diff options
81 files changed, 3397 insertions, 1096 deletions
diff --git a/Documentation/devicetree/bindings/batterydata/batterydata.txt b/Documentation/devicetree/bindings/batterydata/batterydata.txt new file mode 100644 index 000000000000..39f9375a6c48 --- /dev/null +++ b/Documentation/devicetree/bindings/batterydata/batterydata.txt @@ -0,0 +1,221 @@ +Battery Profile Data + +Battery Data is a collection of battery profile data made available to +the QPNP Charger and BMS drivers via device tree. + +qcom,battery-data node required properties: +- qcom,rpull-up-kohm : The vadc pullup resistor's resistance value in kOhms. +- qcom,vref-batt-therm-uv : The vadc voltage used to make readings. + For Qualcomm Technologies, Inc. VADCs, this should be + 1800000uV. + +qcom,battery-data node optional properties: +- qcom,batt-id-range-pct : The area of variation between upper and lower bound + for which a given battery ID resistance is valid. This + value is expressed as a percentage of the specified kohm + resistance provided by qcom,batt-id-kohm. + +qcom,battery-data can also include any number of children nodes. These children +nodes will be treated as battery profile data nodes. + +Profile data node required properties: +- qcom,fcc-mah : Full charge count of the battery in milliamp-hours +- qcom,default-rbatt-mohm : The nominal battery resistance value +- qcom,rbatt-capacitive-mohm : The capacitive resistance of the battery. +- qcom,flat-ocv-threshold-uv : The threshold under which the battery can be + considered to be in the flat portion of the discharge + curve. +- qcom,max-voltage-uv : The maximum rated voltage of the battery +- qcom,v-cutoff-uv : The cutoff voltage of the battery at which the device + should shutdown gracefully. +- qcom,chg-term-ua : The termination charging current of the battery. +- qcom,batt-id-kohm : The battery id resistance of the battery. It can be + used as an array which could support multiple IDs for one battery + module when the ID resistance of some battery modules goes across + several ranges. +- qcom,battery-type : A string indicating the type of battery. +- qcom,fg-profile-data : An array of hexadecimal values used to configure more + complex fuel gauge peripherals which have a large amount + of coefficients used in hardware state machines and thus + influencing the final output of the state of charge read + by software. + +Profile data node optional properties: +- qcom,chg-rslow-comp-c1 : A constant for rslow compensation in the fuel gauge. + This will be provided by the profiling tool for + additional fuel gauge accuracy during charging. +- qcom,chg-rslow-comp-c2 : A constant for rslow compensation in the fuel gauge. + This will be provided by the profiling tool for + additional fuel gauge accuracy during charging. +- qcom,chg-rslow-comp-thr : A constant for rslow compensation in the fuel gauge. + This will be provided by the profiling tool for + additional fuel gauge accuracy during charging. +- qcom,chg-rs-to-rslow: A constant for rslow compensation in the fuel gauge. + This will be provided by the profiling tool for + additional fuel gauge accuracy during charging. +- qcom,fastchg-current-ma: Specifies the maximum fastcharge current. +- qcom,fg-cc-cv-threshold-mv: Voltage threshold in mV for transition from constant + charge (CC) to constant voltage (CV). This value should + be 10 mV less than the float voltage. + This property should only be specified if + "qcom,autoadjust-vfloat" property is specified in the + charger driver to ensure a proper operation. +- qcom,thermal-coefficients: Byte array of thermal coefficients for reading + battery thermistor. This should be exactly 6 bytes + in length. + Example: [01 02 03 04 05 06] + +Profile data node required subnodes: +- qcom,fcc-temp-lut : An 1-dimensional lookup table node that encodes + temperature to fcc lookup. The units for this lookup + table should be degrees celsius to milliamp-hours. +- qcom,pc-temp-ocv-lut : A 2-dimensional lookup table node that encodes + temperature and percent charge to open circuit voltage + lookup. The units for this lookup table should be + degrees celsius and percent to millivolts. +- qcom,rbatt-sf-lut : A 2-dimentional lookup table node that encodes + temperature and percent charge to battery internal + resistance lookup. The units for this lookup table + should be degrees celsius and percent to milliohms. + +Profile data node optional subnodes: +- qcom,ibat-acc-luit: A 2-dimentional lookup table that encodes temperature + and battery current to battery ACC (apparent charge + capacity). The units for this lookup table should be + temperature in degrees celsius, ibat in milli-amps + and ACC in milli-ampere-hour. + +Lookup table required properties: +- qcom,lut-col-legend : An array that encodes the legend of the lookup table's + columns. The length of this array will determine the + lookup table's width. +- qcom,lut-data : An array that encodes the lookup table's data. The size of this + array should be equal to the size of qcom,lut-col-legend + multiplied by 1 if it's a 1-dimensional table, or + the size of qcom,lut-row-legend if it's a 2-dimensional + table. The data should be in a flattened row-major + representation. + +Lookup table optional properties: +- qcom,lut-row-legend : An array that encodes the legend of the lookup table's rows. + If this property exists, then it is assumed that the + lookup table is a 2-dimensional table. + +Example: + +In msm8974-mtp.dtsi: + +mtp_batterydata: qcom,battery-data { + qcom,rpull-up-kohm = <100>; + qcom,vref-batt-therm-uv = <1800000>; + + /include/ "batterydata-palladium.dtsi" + /include/ "batterydata-mtp-3000mah.dtsi" +}; + +&pm8941_bms { + qcom,battery-data = <&mtp_batterydata>; +}; + +In batterydata-palladium.dtsi: + +qcom,palladium-batterydata { + qcom,fcc-mah = <1500>; + qcom,default-rbatt-mohm = <236>; + qcom,rbatt-capacitive-mohm = <50>; + qcom,flat-ocv-threshold-uv = <3800000>; + qcom,max-voltage-uv = <4200000>; + qcom,v-cutoff-uv = <3400000>; + qcom,chg-term-ua = <100000>; + qcom,batt-id-kohm = <75>; + qcom,battery-type = "palladium_1500mah"; + + qcom,fcc-temp-lut { + qcom,lut-col-legend = <(-20) 0 25 40 65>; + qcom,lut-data = <1492 1492 1493 1483 1502>; + }; + + qcom,pc-temp-ocv-lut { + qcom,lut-col-legend = <(-20) 0 25 40 65>; + qcom,lut-row-legend = <100 95 90 85 80 75 70>, + <65 60 55 50 45 40 35>, + <30 25 20 15 10 9 8>, + <7 6 5 4 3 2 1 0>; + qcom,lut-data = <4173 4167 4163 4156 4154>, + <4104 4107 4108 4102 4104>, + <4057 4072 4069 4061 4060>, + <3973 4009 4019 4016 4020>, + <3932 3959 3981 3982 3983>, + <3899 3928 3954 3950 3950>, + <3868 3895 3925 3921 3920>, + <3837 3866 3898 3894 3892>, + <3812 3841 3853 3856 3862>, + <3794 3818 3825 3823 3822>, + <3780 3799 3804 3804 3803>, + <3768 3787 3790 3788 3788>, + <3757 3779 3778 3775 3776>, + <3747 3772 3771 3766 3765>, + <3736 3763 3766 3760 3746>, + <3725 3749 3756 3747 3729>, + <3714 3718 3734 3724 3706>, + <3701 3703 3696 3689 3668>, + <3675 3695 3682 3675 3662>, + <3670 3691 3680 3673 3661>, + <3661 3686 3679 3672 3656>, + <3649 3680 3676 3669 3641>, + <3633 3669 3667 3655 3606>, + <3610 3647 3640 3620 3560>, + <3580 3607 3596 3572 3501>, + <3533 3548 3537 3512 3425>, + <3457 3468 3459 3429 3324>, + <3328 3348 3340 3297 3172>, + <3000 3000 3000 3000 3000>; + }; + + qcom,rbatt-sf-lut { + qcom,lut-col-legend = <(-20) 0 25 40 65>; + qcom,lut-row-legend = <100 95 90 85 80 75 70>, + <65 60 55 50 45 40 35>, + <30 25 20 15 10 9 8>, + <7 6 5 4 3 2 1 0>; + qcom,lut-data = <357 187 100 91 91>, + <400 208 105 94 94>, + <390 204 106 95 96>, + <391 201 108 98 98>, + <391 202 110 98 100>, + <390 200 110 99 102>, + <389 200 110 99 102>, + <393 202 101 93 100>, + <407 205 99 89 94>, + <428 208 100 91 96>, + <455 212 102 92 98>, + <495 220 104 93 101>, + <561 232 107 95 102>, + <634 245 112 98 98>, + <714 258 114 98 98>, + <791 266 114 97 100>, + <871 289 108 95 97>, + <973 340 124 108 105>, + <489 241 109 96 99>, + <511 246 110 96 99>, + <534 252 111 95 98>, + <579 263 112 96 96>, + <636 276 111 95 97>, + <730 294 109 96 99>, + <868 328 112 98 104>, + <1089 374 119 101 115>, + <1559 457 128 105 213>, + <12886 1026 637 422 3269>, + <170899 127211 98968 88907 77102>; + }; + + qcom,ibat-acc-lut { + qcom,lut-col-legend = <(-20) 0 25>; + qcom,lut-row-legend = <0 250 500 1000>; + qcom,lut-data = <1470 1470 1473>, + <1406 1406 1430>, + <1247 1247 1414>, + <764 764 1338>; + }; +}; + 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 199b5c0857f8..bd358593fcb3 100644 --- a/Documentation/devicetree/bindings/power/qcom-charger/qpnp-fg-gen3.txt +++ b/Documentation/devicetree/bindings/power/qcom-charger/qpnp-fg-gen3.txt @@ -140,6 +140,11 @@ First Level Node - FG Gen3 device asleep and the battery is discharging. This option requires qcom,fg-esr-timer-awake to be defined. +- qcom,cycle-counter-en + Usage: optional + Value type: <bool> + Definition: Enables the cycle counter feature. + ========================================================== Second Level Nodes - Peripherals managed by FG Gen3 driver ========================================================== diff --git a/Documentation/devicetree/bindings/power/qcom-charger/qpnp-smb2.txt b/Documentation/devicetree/bindings/power/qcom-charger/qpnp-smb2.txt index 1c5dd91891dc..21404dfc4b7b 100644 --- a/Documentation/devicetree/bindings/power/qcom-charger/qpnp-smb2.txt +++ b/Documentation/devicetree/bindings/power/qcom-charger/qpnp-smb2.txt @@ -21,6 +21,12 @@ Charger specific properties: Value type: <string> Definition: "qcom,qpnp-smb2". +- qcom,pmic-revid + Usage: required + Value type: phandle + Definition: Should specify the phandle of PMI's revid module. This is used to + identify the PMI subtype. + - qcom,batteryless-platform Usage: optional Value type: <empty> diff --git a/Documentation/devicetree/bindings/scheduler/sched_hmp.txt b/Documentation/devicetree/bindings/scheduler/sched_hmp.txt new file mode 100644 index 000000000000..ba1d4db9e407 --- /dev/null +++ b/Documentation/devicetree/bindings/scheduler/sched_hmp.txt @@ -0,0 +1,35 @@ +* HMP scheduler + +This file describes the bindings for an optional HMP scheduler +node (/sched-hmp). + +Required properties: + +Optional properties: + +- boost-policy: The HMP scheduler has two types of task placement boost +policies. + +(1) boost-on-big policy make use of all big CPUs up to their full capacity +before using the little CPUs. This improves performance on true b.L systems +where the big CPUs have higher efficiency compared to the little CPUs. + +(2) boost-on-all policy place the tasks on the CPU having the highest +spare capacity. This policy is optimal for SMP like systems. + +The scheduler sets the boost policy to boost-on-big on systems which has +CPUs of different efficiencies. However it is possible that CPUs of the +same micro architecture to have slight difference in efficiency due to +other factors like cache size. Selecting the boost-on-big policy based +on relative difference in efficiency is not optimal on such systems. +The boost-policy device tree property is introduced to specify the +required boost type and it overrides the default selection of boost +type in the scheduler. + +The possible values for this property are "boost-on-big" and "boost-on-all". + +Example: + +sched-hmp { + boost-policy = "boost-on-all" +} diff --git a/Documentation/devicetree/bindings/thermal/tsens.txt b/Documentation/devicetree/bindings/thermal/tsens.txt index 684bea131405..7189edbf8c5c 100644 --- a/Documentation/devicetree/bindings/thermal/tsens.txt +++ b/Documentation/devicetree/bindings/thermal/tsens.txt @@ -32,6 +32,7 @@ Required properties: should be "qcom,msmcobalt-tsens" for cobalt TSENS driver. should be "qcom,msmhamster-tsens" for hamster TSENS driver. should be "qcom,msmfalcon-tsens" for falcon TSENS driver. + should be "qcom,msmtriton-tsens" for triton TSENS driver. The compatible property is used to identify the respective fusemap to use for the corresponding SoC. - reg : offset and length of the TSENS registers with associated property in reg-names diff --git a/Documentation/scheduler/sched-hmp.txt b/Documentation/scheduler/sched-hmp.txt index b400e053e55d..298064bc44d7 100644 --- a/Documentation/scheduler/sched-hmp.txt +++ b/Documentation/scheduler/sched-hmp.txt @@ -43,6 +43,7 @@ CONTENTS 8.8 sched_get_busy 8.9 sched_freq_alert 8.10 sched_set_boost +9. Device Tree bindings =============== 1. INTRODUCTION @@ -1447,3 +1448,10 @@ Logged when boost settings are being changed <task>-0 [004] d.h4 12700.711489: sched_set_boost: ref_count=1 - ref_count: A non-zero value indicates boost is in effect + +======================== +9. Device Tree bindings +======================== + +The device tree bindings for the HMP scheduler are defined in +Documentation/devicetree/bindings/sched/sched_hmp.txt diff --git a/arch/arm/boot/dts/qcom/msm-pmicobalt.dtsi b/arch/arm/boot/dts/qcom/msm-pmicobalt.dtsi index ec3e9e89b0ae..8a8782f5f8b3 100644 --- a/arch/arm/boot/dts/qcom/msm-pmicobalt.dtsi +++ b/arch/arm/boot/dts/qcom/msm-pmicobalt.dtsi @@ -321,6 +321,7 @@ io-channel-names = "rradc_batt_id"; qcom,fg-esr-timer-awake = <64>; qcom,fg-esr-timer-asleep = <256>; + qcom,cycle-counter-en; status = "okay"; qcom,fg-batt-soc@4000 { diff --git a/arch/arm/boot/dts/qcom/msmcobalt-camera.dtsi b/arch/arm/boot/dts/qcom/msmcobalt-camera.dtsi index 467b2cbfcd7d..190feb5000fc 100644 --- a/arch/arm/boot/dts/qcom/msmcobalt-camera.dtsi +++ b/arch/arm/boot/dts/qcom/msmcobalt-camera.dtsi @@ -333,7 +333,7 @@ camss-vdd-supply = <&gdsc_camss_top>; vdd-supply = <&gdsc_cpp>; qcom,vdd-names = "smmu-vdd", "camss-vdd", "vdd"; - clocks = <&clock_mmss clk_mmss_mnoc_maxi_clk>, + clocks = <&clock_gcc clk_mmssnoc_axi_clk>, <&clock_mmss clk_mmss_mnoc_ahb_clk>, <&clock_mmss clk_mmss_bimc_smmu_ahb_clk>, <&clock_mmss clk_mmss_bimc_smmu_axi_clk>, @@ -344,7 +344,7 @@ <&clock_mmss clk_mmss_fd_ahb_clk>, <&clock_mmss clk_mmss_camss_cpp_axi_clk>, <&clock_mmss clk_mmss_camss_cpp_vbif_ahb_clk>; - clock-names = "mmss_mnoc_maxi_clk", + clock-names = "mmssnoc_axi", "mmss_mnoc_ahb_clk", "mmss_bimc_smmu_ahb_clk", "mmss_bimc_smmu_axi_clk", @@ -387,7 +387,6 @@ vdd-supply = <&gdsc_cpp>; qcom,vdd-names = "smmu-vdd", "camss-vdd", "vdd"; clocks = <&clock_gcc clk_mmssnoc_axi_clk>, - <&clock_mmss clk_mmss_mnoc_maxi_clk>, <&clock_mmss clk_mmss_mnoc_ahb_clk>, <&clock_mmss clk_mmss_camss_ahb_clk>, <&clock_mmss clk_mmss_camss_top_ahb_clk>, @@ -398,12 +397,12 @@ <&clock_mmss clk_mmss_bimc_smmu_axi_clk>, <&clock_mmss clk_mmss_camss_cpp_vbif_ahb_clk>; clock-names = "mmssnoc_axi_clk", - "mnoc_maxi_clk", "mnoc_ahb_clk", + "mnoc_ahb_clk", "camss_ahb_clk", "camss_top_ahb_clk", "cpp_core_clk", "camss_cpp_ahb_clk", "camss_cpp_axi_clk", "micro_iface_clk", "mmss_smmu_axi_clk", "cpp_vbif_ahb_clk"; - qcom,clock-rates = <0 0 0 0 0 200000000 0 0 0 0 0>; + qcom,clock-rates = <0 0 0 0 200000000 0 0 0 0 0>; qcom,min-clock-rate = <200000000>; qcom,bus-master = <1>; qcom,vbif-qos-setting = <0x20 0x10000000>, @@ -455,7 +454,7 @@ qcom,vdd-names = "camss-vdd", "vfe0-vdd", "vfe1-vdd"; qcom,clock-cntl-support; - clocks = <&clock_mmss clk_mmss_mnoc_maxi_clk>, + clocks = <&clock_gcc clk_mmssnoc_axi_clk>, <&clock_mmss clk_mmss_mnoc_ahb_clk>, <&clock_mmss clk_mmss_camss_ahb_clk>, <&clock_mmss clk_mmss_camss_top_ahb_clk>, @@ -482,7 +481,7 @@ <&clock_mmss clk_mmss_camss_vfe1_clk>, <&clock_mmss clk_vfe1_clk_src>, <&clock_mmss clk_mmss_camss_csi_vfe1_clk>; - clock-names = "mnoc_maxi_clk", "mnoc_ahb_clk", + clock-names = "mmssnoc_axi", "mnoc_ahb_clk", "camss_ahb_clk", "camss_top_ahb_clk", "ispif_ahb_clk", "csi0_src_clk", "csi1_src_clk", @@ -534,7 +533,7 @@ camss-vdd-supply = <&gdsc_camss_top>; smmu-vdd-supply = <&gdsc_bimc_smmu>; qcom,vdd-names = "vdd", "camss-vdd", "smmu-vdd"; - clocks = <&clock_mmss clk_mmss_mnoc_maxi_clk>, + clocks = <&clock_gcc clk_mmssnoc_axi_clk>, <&clock_mmss clk_mmss_mnoc_ahb_clk>, <&clock_mmss clk_mmss_bimc_smmu_ahb_clk>, <&clock_mmss clk_mmss_bimc_smmu_axi_clk>, @@ -547,7 +546,7 @@ <&clock_mmss clk_mmss_camss_vfe_vbif_axi_clk>, <&clock_mmss clk_vfe0_clk_src>, <&clock_mmss clk_mmss_camss_csi_vfe0_clk>; - clock-names = "mnoc_maxi_clk", "mnoc_ahb_clk", + clock-names = "mmssnoc_axi", "mnoc_ahb_clk", "bimc_smmu_ahb_clk", "bimc_smmu_axi_clk", "camss_ahb_clk", "camss_top_ahb_clk", "camss_vfe_clk", "camss_vfe_stream_clk", @@ -614,7 +613,7 @@ camss-vdd-supply = <&gdsc_camss_top>; smmu-vdd-supply = <&gdsc_bimc_smmu>; qcom,vdd-names = "vdd", "camss-vdd", "smmu-vdd"; - clocks = <&clock_mmss clk_mmss_mnoc_maxi_clk>, + clocks = <&clock_gcc clk_mmssnoc_axi_clk>, <&clock_mmss clk_mmss_mnoc_ahb_clk>, <&clock_mmss clk_mmss_bimc_smmu_ahb_clk>, <&clock_mmss clk_mmss_bimc_smmu_axi_clk>, @@ -627,7 +626,7 @@ <&clock_mmss clk_mmss_camss_vfe_vbif_axi_clk>, <&clock_mmss clk_vfe1_clk_src>, <&clock_mmss clk_mmss_camss_csi_vfe1_clk>; - clock-names = "mnoc_maxi_clk", "mnoc_ahb_clk", + clock-names = "mmssnoc_axi", "mnoc_ahb_clk", "bimc_smmu_ahb_clk", "bimc_smmu_axi_clk", "camss_ahb_clk", "camss_top_ahb_clk", "camss_vfe_clk", "camss_vfe_stream_clk", @@ -752,7 +751,7 @@ smmu-vdd-supply = <&gdsc_bimc_smmu>; camss-vdd-supply = <&gdsc_camss_top>; qcom,vdd-names = "smmu-vdd", "camss-vdd"; - clock-names = "mmss_mnoc_maxi_clk", + clock-names = "mmssnoc_axi", "mmss_mnoc_ahb_clk", "mmss_bimc_smmu_ahb_clk", "mmss_bimc_smmu_axi_clk", @@ -761,7 +760,7 @@ "core_clk", "mmss_camss_jpeg_ahb_clk", "mmss_camss_jpeg_axi_clk"; - clocks = <&clock_mmss clk_mmss_mnoc_maxi_clk>, + clocks = <&clock_gcc clk_mmssnoc_axi_clk>, <&clock_mmss clk_mmss_mnoc_ahb_clk>, <&clock_mmss clk_mmss_bimc_smmu_ahb_clk>, <&clock_mmss clk_mmss_bimc_smmu_axi_clk>, @@ -796,7 +795,7 @@ smmu-vdd-supply = <&gdsc_bimc_smmu>; camss-vdd-supply = <&gdsc_camss_top>; qcom,vdd-names = "smmu-vdd", "camss-vdd"; - clock-names = "mmss_mnoc_maxi_clk", + clock-names = "mmssnoc_axi", "mmss_mnoc_ahb_clk", "mmss_bimc_smmu_ahb_clk", "mmss_bimc_smmu_axi_clk", @@ -805,7 +804,7 @@ "core_clk", "mmss_camss_jpeg_ahb_clk", "mmss_camss_jpeg_axi_clk"; - clocks = <&clock_mmss clk_mmss_mnoc_maxi_clk>, + clocks = <&clock_gcc clk_mmssnoc_axi_clk>, <&clock_mmss clk_mmss_mnoc_ahb_clk>, <&clock_mmss clk_mmss_bimc_smmu_ahb_clk>, <&clock_mmss clk_mmss_bimc_smmu_axi_clk>, diff --git a/arch/arm/boot/dts/qcom/msmcobalt-mdss-pll.dtsi b/arch/arm/boot/dts/qcom/msmcobalt-mdss-pll.dtsi index 11571415c02e..af0eb60818fb 100644 --- a/arch/arm/boot/dts/qcom/msmcobalt-mdss-pll.dtsi +++ b/arch/arm/boot/dts/qcom/msmcobalt-mdss-pll.dtsi @@ -96,8 +96,10 @@ gdsc-supply = <&gdsc_mdss>; - clocks = <&clock_mmss clk_mmss_mdss_ahb_clk>; - clock-names = "iface_clk"; + clocks = <&clock_mmss clk_mmss_mdss_ahb_clk>, + <&clock_gcc clk_ln_bb_clk1>, + <&clock_gcc clk_gcc_usb3_clkref_clk>; + clock-names = "iface_clk", "ref_clk_src", "ref_clk"; clock-rate = <0>; qcom,platform-supply-entries { diff --git a/arch/arm/boot/dts/qcom/msmcobalt-mdss.dtsi b/arch/arm/boot/dts/qcom/msmcobalt-mdss.dtsi index 4fe694069011..ec38e46b1d89 100644 --- a/arch/arm/boot/dts/qcom/msmcobalt-mdss.dtsi +++ b/arch/arm/boot/dts/qcom/msmcobalt-mdss.dtsi @@ -477,15 +477,17 @@ <&clock_mmss clk_mmss_mdss_mdp_clk>, <&clock_mmss clk_mmss_mdss_hdmi_dp_ahb_clk>, <&clock_mmss clk_mmss_mdss_dp_aux_clk>, + <&clock_gcc clk_ln_bb_clk1>, + <&clock_gcc clk_gcc_usb3_clkref_clk>, <&clock_mmss clk_mmss_mdss_dp_link_clk>, <&clock_mmss clk_mmss_mdss_dp_link_intf_clk>, <&clock_mmss clk_mmss_mdss_dp_crypto_clk>, <&clock_mmss clk_mmss_mdss_dp_pixel_clk>; clock-names = "core_mnoc_clk", "core_iface_clk", "core_bus_clk", "core_mdp_core_clk", "core_alt_iface_clk", - "core_aux_clk", "ctrl_link_clk", - "ctrl_link_iface_clk", "ctrl_crypto_clk", - "ctrl_pixel_clk"; + "core_aux_clk", "core_ref_clk_src", "core_ref_clk", + "ctrl_link_clk", "ctrl_link_iface_clk", + "ctrl_crypto_clk", "ctrl_pixel_clk"; qcom,dp-usbpd-detection = <&pmicobalt_pdphy>; diff --git a/arch/arm/boot/dts/qcom/msmcobalt-v2.dtsi b/arch/arm/boot/dts/qcom/msmcobalt-v2.dtsi index e19fb5f7c233..25fd35c1bd10 100644 --- a/arch/arm/boot/dts/qcom/msmcobalt-v2.dtsi +++ b/arch/arm/boot/dts/qcom/msmcobalt-v2.dtsi @@ -664,11 +664,11 @@ /* SVS2 */ qcom,imem-ab-tbl = - <200000000 1752000>, - <269330000 1752000>, - <355200000 2500000>, - <444000000 6000000>, - <533000000 6000000>; + <200000000 1560000>,/* imem @ svs2 freq 75 Mhz */ + <269330000 3570000>,/* imem @ svs freq 171 Mhz */ + <355200000 3570000>,/* imem @ svs freq 171 Mhz */ + <444000000 6750000>,/* imem @ nom freq 323 Mhz */ + <533000000 8490000>;/* imem @ turbo freq 406 Mhz */ }; /* GPU overrides */ diff --git a/arch/arm/boot/dts/qcom/msmcobalt-vidc.dtsi b/arch/arm/boot/dts/qcom/msmcobalt-vidc.dtsi index c44e8f976710..f17be7570742 100644 --- a/arch/arm/boot/dts/qcom/msmcobalt-vidc.dtsi +++ b/arch/arm/boot/dts/qcom/msmcobalt-vidc.dtsi @@ -66,10 +66,10 @@ * corresponding video core frequency. */ qcom,imem-ab-tbl = - <100000000 1752000>, /* imem @ svs2 freq 75 Mhz */ - <186000000 1752000>, /* imem @ svs2 freq 75 Mhz */ - <360000000 2500000>, /* imem @ svs freq 171 Mhz */ - <465000000 6000000>; /* imem @ noimal freq 320 Mhz */ + <100000000 1560000>, /* imem @ svs2 freq 75 Mhz */ + <186000000 3570000>, /* imem @ svs freq 171 Mhz */ + <360000000 6750000>, /* imem @ nom freq 323 Mhz */ + <465000000 8490000>; /* imem @ turbo freq 406 Mhz */ /* Regulators */ smmu-vdd-supply = <&gdsc_bimc_smmu>; @@ -124,7 +124,7 @@ qcom,bus-master = <MSM_BUS_MASTER_VIDEO_P0_OCMEM>; qcom,bus-slave = <MSM_BUS_SLAVE_VMEM>; qcom,bus-governor = "msm-vidc-vmem+"; - qcom,bus-range-kbps = <1000 6776000>; + qcom,bus-range-kbps = <1000 8490000>; }; arm9_bus_ddr { diff --git a/arch/arm/boot/dts/qcom/msmfalcon-coresight.dtsi b/arch/arm/boot/dts/qcom/msmfalcon-coresight.dtsi index 352856965373..b60d4013dad8 100644 --- a/arch/arm/boot/dts/qcom/msmfalcon-coresight.dtsi +++ b/arch/arm/boot/dts/qcom/msmfalcon-coresight.dtsi @@ -26,6 +26,8 @@ arm,buffer-size = <0x400000>; arm,sg-enable; + coresight-ctis = <&cti0 &cti8>; + coresight-name = "coresight-tmc-etr"; clocks = <&clock_gcc RPM_QDSS_CLK>, @@ -76,6 +78,8 @@ coresight-name = "coresight-tmc-etf"; + coresight-ctis = <&cti0 &cti8>; + clocks = <&clock_gcc RPM_QDSS_CLK>, <&clock_gcc RPM_QDSS_A_CLK>; clock-names = "apb_pclk", "core_a_clk"; @@ -161,6 +165,14 @@ <&funnel_merg_in_funnel_in0>; }; }; + port@3 { + reg = <6>; + funnel_in0_in_funnel_qatb: endpoint { + slave-mode; + remote-endpoint = + <&funnel_qatb_out_funnel_in0>; + }; + }; port@4 { reg = <7>; funnel_in0_in_stm: endpoint { @@ -191,4 +203,294 @@ }; }; }; + + cti0: cti@6010000 { + compatible = "arm,coresight-cti"; + reg = <0x6010000 0x1000>; + reg-names = "cti-base"; + + coresight-name = "coresight-cti0"; + + clocks = <&clock_gcc RPM_QDSS_CLK>, + <&clock_gcc RPM_QDSS_A_CLK>; + clock-names = "core_clk", "core_a_clk"; + }; + + cti1: cti@6011000 { + compatible = "arm,coresight-cti"; + reg = <0x6011000 0x1000>; + reg-names = "cti-base"; + + coresight-name = "coresight-cti1"; + + clocks = <&clock_gcc RPM_QDSS_CLK>, + <&clock_gcc RPM_QDSS_A_CLK>; + clock-names = "core_clk", "core_a_clk"; + }; + + cti2: cti@6012000 { + compatible = "arm,coresight-cti"; + reg = <0x6012000 0x1000>; + reg-names = "cti-base"; + + coresight-name = "coresight-cti2"; + + clocks = <&clock_gcc RPM_QDSS_CLK>, + <&clock_gcc RPM_QDSS_A_CLK>; + clock-names = "core_clk", "core_a_clk"; + }; + + cti3: cti@6013000 { + compatible = "arm,coresight-cti"; + reg = <0x6013000 0x1000>; + reg-names = "cti-base"; + + coresight-name = "coresight-cti3"; + + clocks = <&clock_gcc RPM_QDSS_CLK>, + <&clock_gcc RPM_QDSS_A_CLK>; + clock-names = "core_clk", "core_a_clk"; + }; + + cti4: cti@6014000 { + compatible = "arm,coresight-cti"; + reg = <0x6014000 0x1000>; + reg-names = "cti-base"; + + coresight-name = "coresight-cti4"; + + clocks = <&clock_gcc RPM_QDSS_CLK>, + <&clock_gcc RPM_QDSS_A_CLK>; + clock-names = "core_clk", "core_a_clk"; + }; + + cti5: cti@6015000 { + compatible = "arm,coresight-cti"; + reg = <0x6015000 0x1000>; + reg-names = "cti-base"; + + coresight-name = "coresight-cti5"; + + clocks = <&clock_gcc RPM_QDSS_CLK>, + <&clock_gcc RPM_QDSS_A_CLK>; + clock-names = "core_clk", "core_a_clk"; + }; + + cti6: cti@6016000 { + compatible = "arm,coresight-cti"; + reg = <0x6016000 0x1000>; + reg-names = "cti-base"; + + coresight-name = "coresight-cti6"; + + clocks = <&clock_gcc RPM_QDSS_CLK>, + <&clock_gcc RPM_QDSS_A_CLK>; + clock-names = "core_clk", "core_a_clk"; + }; + + cti7: cti@6017000 { + compatible = "arm,coresight-cti"; + reg = <0x6017000 0x1000>; + reg-names = "cti-base"; + + coresight-name = "coresight-cti7"; + + clocks = <&clock_gcc RPM_QDSS_CLK>, + <&clock_gcc RPM_QDSS_A_CLK>; + clock-names = "core_clk", "core_a_clk"; + }; + + cti8: cti@6018000 { + compatible = "arm,coresight-cti"; + reg = <0x6018000 0x1000>; + reg-names = "cti-base"; + + coresight-name = "coresight-cti8"; + + clocks = <&clock_gcc RPM_QDSS_CLK>, + <&clock_gcc RPM_QDSS_A_CLK>; + clock-names = "core_clk", "core_a_clk"; + }; + + cti9: cti@6019000 { + compatible = "arm,coresight-cti"; + reg = <0x6019000 0x1000>; + reg-names = "cti-base"; + + coresight-name = "coresight-cti9"; + + clocks = <&clock_gcc RPM_QDSS_CLK>, + <&clock_gcc RPM_QDSS_A_CLK>; + clock-names = "core_clk", "core_a_clk"; + }; + + cti10: cti@601a000 { + compatible = "arm,coresight-cti"; + reg = <0x601a000 0x1000>; + reg-names = "cti-base"; + + coresight-name = "coresight-cti10"; + + clocks = <&clock_gcc RPM_QDSS_CLK>, + <&clock_gcc RPM_QDSS_A_CLK>; + clock-names = "core_clk", "core_a_clk"; + }; + + cti11: cti@601b000 { + compatible = "arm,coresight-cti"; + reg = <0x601b000 0x1000>; + reg-names = "cti-base"; + + coresight-name = "coresight-cti11"; + + clocks = <&clock_gcc RPM_QDSS_CLK>, + <&clock_gcc RPM_QDSS_A_CLK>; + clock-names = "core_clk", "core_a_clk"; + }; + + cti12: cti@601c000 { + compatible = "arm,coresight-cti"; + reg = <0x601c000 0x1000>; + reg-names = "cti-base"; + + coresight-name = "coresight-cti12"; + + clocks = <&clock_gcc RPM_QDSS_CLK>, + <&clock_gcc RPM_QDSS_A_CLK>; + clock-names = "core_clk", "core_a_clk"; + }; + + cti13: cti@601d000 { + compatible = "arm,coresight-cti"; + reg = <0x601d000 0x1000>; + reg-names = "cti-base"; + + coresight-name = "coresight-cti13"; + + clocks = <&clock_gcc RPM_QDSS_CLK>, + <&clock_gcc RPM_QDSS_A_CLK>; + clock-names = "core_clk", "core_a_clk"; + }; + + cti14: cti@601e000 { + compatible = "arm,coresight-cti"; + reg = <0x601e000 0x1000>; + reg-names = "cti-base"; + + coresight-name = "coresight-cti14"; + + clocks = <&clock_gcc RPM_QDSS_CLK>, + <&clock_gcc RPM_QDSS_A_CLK>; + clock-names = "core_clk", "core_a_clk"; + }; + + cti15: cti@601f000 { + compatible = "arm,coresight-cti"; + reg = <0x601f000 0x1000>; + reg-names = "cti-base"; + + coresight-name = "coresight-cti15"; + + clocks = <&clock_gcc RPM_QDSS_CLK>, + <&clock_gcc RPM_QDSS_A_CLK>; + clock-names = "core_clk", "core_a_clk"; + }; + + funnel_qatb: funnel@6005000 { + compatible = "arm,primecell"; + arm,primecell-periphid = <0x0003b908>; + + reg = <0x6005000 0x1000>; + reg-names = "funnel-base"; + + coresight-name = "coresight-funnel-qatb"; + + clocks = <&clock_gcc RPM_QDSS_CLK>, + <&clock_gcc RPM_QDSS_A_CLK>; + clock-names = "apb_pclk", "core_a_clk"; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + funnel_qatb_out_funnel_in0: endpoint { + remote-endpoint = + <&funnel_in0_in_funnel_qatb>; + }; + }; + port@1 { + reg = <0>; + funnel_qatb_in_tpda: endpoint { + slave-mode; + remote-endpoint = + <&tpda_out_funnel_qatb>; + }; + }; + }; + }; + + tpda: tpda@6004000 { + compatible = "qcom,coresight-tpda"; + reg = <0x6004000 0x1000>; + reg-names = "tpda-base"; + + coresight-name = "coresight-tpda"; + + qcom,tpda-atid = <65>; + qcom,bc-elem-size = <7 32>, + <9 32>; + qcom,tc-elem-size = <3 32>, + <6 32>, + <9 32>; + qcom,dsb-elem-size = <7 32>, + <9 32>; + qcom,cmb-elem-size = <3 32>, + <4 32>, + <5 32>, + <9 64>; + + clocks = <&clock_gcc RPM_QDSS_CLK>, + <&clock_gcc RPM_QDSS_A_CLK>; + clock-names = "core_clk", "core_a_clk"; + + ports { + #address-cells = <1>; + #size-cells = <0>; + port@0 { + reg = <0>; + tpda_out_funnel_qatb: endpoint { + remote-endpoint = + <&funnel_qatb_in_tpda>; + }; + }; + port@2 { + reg = <5>; + tpda_in_tpdm_dcc: endpoint { + slave-mode; + remote-endpoint = + <&tpdm_dcc_out_tpda>; + }; + }; + }; + }; + + tpdm_dcc: tpdm@7054000 { + compatible = "qcom,coresight-tpdm"; + reg = <0x7054000 0x1000>; + reg-names = "tpdm-base"; + + coresight-name = "coresight-tpdm-dcc"; + + clocks = <&clock_gcc RPM_QDSS_CLK>, + <&clock_gcc RPM_QDSS_A_CLK>; + clock-names = "core_clk", "core_a_clk"; + + port{ + tpdm_dcc_out_tpda: endpoint { + remote-endpoint = <&tpda_in_tpdm_dcc>; + }; + }; + }; }; diff --git a/arch/arm/boot/dts/qcom/msmfalcon.dtsi b/arch/arm/boot/dts/qcom/msmfalcon.dtsi index 4765a8cb7c79..67748d6683c0 100644 --- a/arch/arm/boot/dts/qcom/msmfalcon.dtsi +++ b/arch/arm/boot/dts/qcom/msmfalcon.dtsi @@ -252,6 +252,18 @@ status = "ok"; }; + wdog: qcom,wdt@17817000 { + status = "disabled"; + compatible = "qcom,msm-watchdog"; + reg = <0x17817000 0x1000>; + reg-names = "wdt-base"; + interrupts = <0 3 0>, <0 4 0>; + qcom,bark-time = <11000>; + qcom,pet-time = <10000>; + qcom,ipi-ping; + qcom,wakeup-enable; + }; + qcom,sps { compatible = "qcom,msm_sps_4k"; qcom,pipe-attr-ee; @@ -380,6 +392,16 @@ qcom,mpu-enabled; }; + dcc: dcc@10b3000 { + compatible = "qcom,dcc"; + reg = <0x10b3000 0x1000>, + <0x10b4000 0x800>; + reg-names = "dcc-base", "dcc-ram-base"; + + clocks = <&clock_gcc RPM_QDSS_CLK>; + clock-names = "dcc_clk"; + }; + qcom,glink-smem-native-xprt-modem@86000000 { compatible = "qcom,glink-smem-native-xprt"; reg = <0x86000000 0x200000>, diff --git a/arch/arm/boot/dts/qcom/msmtriton.dtsi b/arch/arm/boot/dts/qcom/msmtriton.dtsi index 17551d94896b..3f0d4cc48696 100644 --- a/arch/arm/boot/dts/qcom/msmtriton.dtsi +++ b/arch/arm/boot/dts/qcom/msmtriton.dtsi @@ -220,6 +220,15 @@ qcom,pipe-attr-ee; }; + tsens: tsens@10ad000 { + compatible = "qcom,msmtriton-tsens"; + reg = <0x10ad000 0x2000>; + reg-names = "tsens_physical"; + interrupts = <0 184 0>, <0 430 0>; + interrupt-names = "tsens-upper-lower", "tsens-critical"; + qcom,sensors = <12>; + }; + uartblsp1dm1: serial@0c170000 { compatible = "qcom,msm-uartdm-v1.4", "qcom,msm-uartdm"; reg = <0xc170000 0x1000>; diff --git a/arch/arm/configs/msmcortex_defconfig b/arch/arm/configs/msmcortex_defconfig index 2b35b0f12787..48507eebe9f3 100644 --- a/arch/arm/configs/msmcortex_defconfig +++ b/arch/arm/configs/msmcortex_defconfig @@ -261,8 +261,8 @@ CONFIG_KEYBOARD_GPIO=y CONFIG_INPUT_JOYSTICK=y CONFIG_INPUT_TOUCHSCREEN=y CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_CORE_v21=y -CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_RMI_DEV_v21=y CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_FW_UPDATE_v21=y +CONFIG_SECURE_TOUCH=y CONFIG_INPUT_MISC=y CONFIG_INPUT_HBTP_INPUT=y CONFIG_INPUT_KEYCHORD=y diff --git a/arch/arm/configs/msmfalcon_defconfig b/arch/arm/configs/msmfalcon_defconfig index 2b35b0f12787..db50dce9f9a4 100644 --- a/arch/arm/configs/msmfalcon_defconfig +++ b/arch/arm/configs/msmfalcon_defconfig @@ -261,7 +261,6 @@ CONFIG_KEYBOARD_GPIO=y CONFIG_INPUT_JOYSTICK=y CONFIG_INPUT_TOUCHSCREEN=y CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_CORE_v21=y -CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_RMI_DEV_v21=y CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_FW_UPDATE_v21=y CONFIG_INPUT_MISC=y CONFIG_INPUT_HBTP_INPUT=y @@ -416,6 +415,9 @@ CONFIG_GPIO_USB_DETECT=y CONFIG_USB_BAM=y CONFIG_REMOTE_SPINLOCK_MSM=y CONFIG_ARM_SMMU=y +CONFIG_IOMMU_DEBUG=y +CONFIG_IOMMU_DEBUG_TRACKING=y +CONFIG_IOMMU_TESTS=y CONFIG_QCOM_COMMON_LOG=y CONFIG_MSM_SMEM=y CONFIG_QPNP_HAPTIC=y diff --git a/arch/arm/kernel/topology.c b/arch/arm/kernel/topology.c index 598323a1842e..e683d147816c 100644 --- a/arch/arm/kernel/topology.c +++ b/arch/arm/kernel/topology.c @@ -190,6 +190,13 @@ static int __init parse_cluster(struct device_node *cluster, int depth) return 0; } +static DEFINE_PER_CPU(unsigned long, cpu_efficiency) = SCHED_CAPACITY_SCALE; + +unsigned long arch_get_cpu_efficiency(int cpu) +{ + return per_cpu(cpu_efficiency, cpu); +} + #ifdef CONFIG_OF struct cpu_efficiency { const char *compatible; @@ -266,6 +273,7 @@ static int __init parse_dt_topology(void) for_each_possible_cpu(cpu) { const u32 *rate; int len; + u32 efficiency; /* too early to use cpu->of_node */ cn = of_get_cpu_node(cpu, NULL); @@ -274,12 +282,26 @@ static int __init parse_dt_topology(void) continue; } - for (cpu_eff = table_efficiency; cpu_eff->compatible; cpu_eff++) - if (of_device_is_compatible(cn, cpu_eff->compatible)) - break; + /* + * The CPU efficiency value passed from the device tree + * overrides the value defined in the table_efficiency[] + */ + if (of_property_read_u32(cn, "efficiency", &efficiency) < 0) { + + for (cpu_eff = table_efficiency; + cpu_eff->compatible; cpu_eff++) - if (cpu_eff->compatible == NULL) - continue; + if (of_device_is_compatible(cn, + cpu_eff->compatible)) + break; + + if (cpu_eff->compatible == NULL) + continue; + + efficiency = cpu_eff->efficiency; + } + + per_cpu(cpu_efficiency, cpu) = efficiency; rate = of_get_property(cn, "clock-frequency", &len); if (!rate || len != 4) { @@ -288,7 +310,7 @@ static int __init parse_dt_topology(void) continue; } - capacity = ((be32_to_cpup(rate)) >> 20) * cpu_eff->efficiency; + capacity = ((be32_to_cpup(rate)) >> 20) * efficiency; /* Save min capacity of the system */ if (capacity < min_capacity) diff --git a/arch/arm64/configs/msm-perf_defconfig b/arch/arm64/configs/msm-perf_defconfig index 7f31331933bb..6ba13806cf16 100644 --- a/arch/arm64/configs/msm-perf_defconfig +++ b/arch/arm64/configs/msm-perf_defconfig @@ -282,7 +282,6 @@ CONFIG_KEYBOARD_GPIO=y # CONFIG_INPUT_MOUSE is not set CONFIG_INPUT_TOUCHSCREEN=y CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_CORE_v21=y -CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_RMI_DEV_v21=y CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_FW_UPDATE_v21=y CONFIG_TOUCHSCREEN_ATMEL_MXT=y CONFIG_TOUCHSCREEN_ATMEL_MAXTOUCH_TS=y diff --git a/arch/arm64/configs/msm_defconfig b/arch/arm64/configs/msm_defconfig index 89bee1463421..bc5fc905188c 100644 --- a/arch/arm64/configs/msm_defconfig +++ b/arch/arm64/configs/msm_defconfig @@ -269,7 +269,6 @@ CONFIG_KEYBOARD_GPIO=y CONFIG_INPUT_JOYSTICK=y CONFIG_INPUT_TOUCHSCREEN=y CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_CORE_v21=y -CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_RMI_DEV_v21=y CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_FW_UPDATE_v21=y CONFIG_TOUCHSCREEN_ATMEL_MXT=y CONFIG_TOUCHSCREEN_ATMEL_MAXTOUCH_TS=y diff --git a/arch/arm64/configs/msmcortex-perf_defconfig b/arch/arm64/configs/msmcortex-perf_defconfig index 50c8848bc6f9..c28d0d4ef2df 100644 --- a/arch/arm64/configs/msmcortex-perf_defconfig +++ b/arch/arm64/configs/msmcortex-perf_defconfig @@ -274,6 +274,7 @@ CONFIG_KEYBOARD_GPIO=y CONFIG_INPUT_TOUCHSCREEN=y CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_CORE_v21=y CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_FW_UPDATE_v21=y +CONFIG_SECURE_TOUCH=y CONFIG_INPUT_MISC=y CONFIG_INPUT_HBTP_INPUT=y CONFIG_INPUT_UINPUT=y diff --git a/arch/arm64/configs/msmcortex_defconfig b/arch/arm64/configs/msmcortex_defconfig index 3d1a01491c0c..85cac221d42d 100644 --- a/arch/arm64/configs/msmcortex_defconfig +++ b/arch/arm64/configs/msmcortex_defconfig @@ -275,6 +275,7 @@ CONFIG_INPUT_JOYSTICK=y CONFIG_INPUT_TOUCHSCREEN=y CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_CORE_v21=y CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_FW_UPDATE_v21=y +CONFIG_SECURE_TOUCH=y CONFIG_INPUT_MISC=y CONFIG_INPUT_HBTP_INPUT=y CONFIG_INPUT_KEYCHORD=y diff --git a/arch/arm64/configs/msmfalcon-perf_defconfig b/arch/arm64/configs/msmfalcon-perf_defconfig index 6ebd60b43c71..79da74733e36 100644 --- a/arch/arm64/configs/msmfalcon-perf_defconfig +++ b/arch/arm64/configs/msmfalcon-perf_defconfig @@ -272,7 +272,6 @@ CONFIG_KEYBOARD_GPIO=y # CONFIG_INPUT_MOUSE is not set CONFIG_INPUT_TOUCHSCREEN=y CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_CORE_v21=y -CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_RMI_DEV_v21=y CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_FW_UPDATE_v21=y CONFIG_INPUT_MISC=y CONFIG_INPUT_HBTP_INPUT=y diff --git a/arch/arm64/configs/msmfalcon_defconfig b/arch/arm64/configs/msmfalcon_defconfig index 01324d89e79e..62e929b998b9 100644 --- a/arch/arm64/configs/msmfalcon_defconfig +++ b/arch/arm64/configs/msmfalcon_defconfig @@ -273,7 +273,6 @@ CONFIG_KEYBOARD_GPIO=y CONFIG_INPUT_JOYSTICK=y CONFIG_INPUT_TOUCHSCREEN=y CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_CORE_v21=y -CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_RMI_DEV_v21=y CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_FW_UPDATE_v21=y CONFIG_INPUT_MISC=y CONFIG_INPUT_HBTP_INPUT=y diff --git a/drivers/clk/msm/clock-osm.c b/drivers/clk/msm/clock-osm.c index 68274765a6c1..0d733f49f184 100644 --- a/drivers/clk/msm/clock-osm.c +++ b/drivers/clk/msm/clock-osm.c @@ -460,7 +460,7 @@ static int clk_osm_set_rate(struct clk *c, unsigned long rate) } pr_debug("rate: %lu --> index %d\n", rate, index); - if (cpuclk->llm_sw_overr) { + if (cpuclk->llm_sw_overr[0]) { clk_osm_write_reg(cpuclk, cpuclk->llm_sw_overr[0], LLM_SW_OVERRIDE_REG); clk_osm_write_reg(cpuclk, cpuclk->llm_sw_overr[1], @@ -471,7 +471,7 @@ static int clk_osm_set_rate(struct clk *c, unsigned long rate) /* Choose index and send request to OSM hardware */ clk_osm_write_reg(cpuclk, index, DCVS_PERF_STATE_DESIRED_REG); - if (cpuclk->llm_sw_overr) { + if (cpuclk->llm_sw_overr[0]) { udelay(1); clk_osm_write_reg(cpuclk, cpuclk->llm_sw_overr[2], LLM_SW_OVERRIDE_REG); @@ -1882,8 +1882,8 @@ static void clk_osm_apm_vc_setup(struct clk_osm *c) } scm_io_write(c->pbases[OSM_BASE] + SEQ_REG(72), c->apm_crossover_vc); - clk_osm_write_reg(c, c->apm_threshold_vc, - SEQ_REG(15)); + scm_io_write(c->pbases[OSM_BASE] + SEQ_REG(15), + c->apm_threshold_vc); scm_io_write(c->pbases[OSM_BASE] + SEQ_REG(31), c->apm_threshold_vc != 0 ? c->apm_threshold_vc - 1 : 0xff); diff --git a/drivers/gpu/msm/kgsl_pwrctrl.c b/drivers/gpu/msm/kgsl_pwrctrl.c index 1f2178848664..0fcc0c3b0d49 100644 --- a/drivers/gpu/msm/kgsl_pwrctrl.c +++ b/drivers/gpu/msm/kgsl_pwrctrl.c @@ -1913,20 +1913,38 @@ void kgsl_idle_check(struct work_struct *work) { struct kgsl_device *device = container_of(work, struct kgsl_device, idle_check_ws); + int ret = 0; + unsigned int requested_state; + WARN_ON(device == NULL); if (device == NULL) return; mutex_lock(&device->mutex); + requested_state = device->requested_state; + if (device->state == KGSL_STATE_ACTIVE || device->state == KGSL_STATE_NAP) { - if (!atomic_read(&device->active_cnt)) - kgsl_pwrctrl_change_state(device, + if (!atomic_read(&device->active_cnt)) { + ret = kgsl_pwrctrl_change_state(device, device->requested_state); + if (ret == -EBUSY) { + /* + * If the GPU is currently busy, restore + * the requested state and reschedule + * idle work. + */ + kgsl_pwrctrl_request_state(device, + requested_state); + kgsl_schedule_work(&device->idle_check_ws); + } + } + + if (!ret) + kgsl_pwrctrl_request_state(device, KGSL_STATE_NONE); - kgsl_pwrctrl_request_state(device, KGSL_STATE_NONE); if (device->state == KGSL_STATE_ACTIVE) mod_timer(&device->idle_timer, jiffies + diff --git a/drivers/hwtracing/coresight/coresight-csr.c b/drivers/hwtracing/coresight/coresight-csr.c index dfb2922b6f33..3c18d686091a 100644 --- a/drivers/hwtracing/coresight/coresight-csr.c +++ b/drivers/hwtracing/coresight/coresight-csr.c @@ -191,8 +191,6 @@ static int csr_probe(struct platform_device *pdev) drvdata = devm_kzalloc(dev, sizeof(*drvdata), GFP_KERNEL); if (!drvdata) return -ENOMEM; - /* Store the driver data pointer for use in exported functions */ - csrdrvdata = drvdata; drvdata->dev = &pdev->dev; platform_set_drvdata(pdev, drvdata); @@ -220,6 +218,8 @@ static int csr_probe(struct platform_device *pdev) if (IS_ERR(drvdata->csdev)) return PTR_ERR(drvdata->csdev); + /* Store the driver data pointer for use in exported functions */ + csrdrvdata = drvdata; dev_info(dev, "CSR initialized\n"); return 0; } diff --git a/drivers/hwtracing/coresight/coresight-stm.c b/drivers/hwtracing/coresight/coresight-stm.c index a234d61802ce..fb2f27299417 100644 --- a/drivers/hwtracing/coresight/coresight-stm.c +++ b/drivers/hwtracing/coresight/coresight-stm.c @@ -774,8 +774,6 @@ static int stm_probe(struct amba_device *adev, const struct amba_id *id) drvdata = devm_kzalloc(dev, sizeof(*drvdata), GFP_KERNEL); if (!drvdata) return -ENOMEM; - /* Store the driver data pointer for use in exported functions */ - stmdrvdata = drvdata; drvdata->dev = &adev->dev; dev_set_drvdata(dev, drvdata); @@ -846,6 +844,8 @@ static int stm_probe(struct amba_device *adev, const struct amba_id *id) if (boot_enable) coresight_enable(drvdata->csdev); + /* Store the driver data pointer for use in exported functions */ + stmdrvdata = drvdata; return 0; err: coresight_unregister(drvdata->csdev); diff --git a/drivers/input/touchscreen/synaptics_dsx/synaptics_dsx_rmi_dev.c b/drivers/input/touchscreen/synaptics_dsx/synaptics_dsx_rmi_dev.c index 4c341ffb6094..9d61eb110e2f 100644 --- a/drivers/input/touchscreen/synaptics_dsx/synaptics_dsx_rmi_dev.c +++ b/drivers/input/touchscreen/synaptics_dsx/synaptics_dsx_rmi_dev.c @@ -347,7 +347,7 @@ static ssize_t rmidev_read(struct file *filp, char __user *buf, size_t count, loff_t *f_pos) { ssize_t retval; - unsigned char tmpbuf[count + 1]; + unsigned char *tmpbuf; struct rmidev_data *dev_data = filp->private_data; if (IS_ERR(dev_data)) { @@ -361,6 +361,10 @@ static ssize_t rmidev_read(struct file *filp, char __user *buf, if (count > (REG_ADDR_LIMIT - *f_pos)) count = REG_ADDR_LIMIT - *f_pos; + tmpbuf = kzalloc(count + 1, GFP_KERNEL); + if (!tmpbuf) + return -ENOMEM; + mutex_lock(&(dev_data->file_mutex)); retval = synaptics_rmi4_reg_read(rmidev->rmi4_data, @@ -377,7 +381,7 @@ static ssize_t rmidev_read(struct file *filp, char __user *buf, clean_up: mutex_unlock(&(dev_data->file_mutex)); - + kfree(tmpbuf); return retval; } @@ -393,7 +397,7 @@ static ssize_t rmidev_write(struct file *filp, const char __user *buf, size_t count, loff_t *f_pos) { ssize_t retval; - unsigned char tmpbuf[count + 1]; + unsigned char *tmpbuf; struct rmidev_data *dev_data = filp->private_data; if (IS_ERR(dev_data)) { @@ -407,9 +411,14 @@ static ssize_t rmidev_write(struct file *filp, const char __user *buf, if (count > (REG_ADDR_LIMIT - *f_pos)) count = REG_ADDR_LIMIT - *f_pos; - if (copy_from_user(tmpbuf, buf, count)) - return -EFAULT; + tmpbuf = kzalloc(count + 1, GFP_KERNEL); + if (!tmpbuf) + return -ENOMEM; + if (copy_from_user(tmpbuf, buf, count)) { + kfree(tmpbuf); + return -EFAULT; + } mutex_lock(&(dev_data->file_mutex)); retval = synaptics_rmi4_reg_write(rmidev->rmi4_data, @@ -420,7 +429,7 @@ static ssize_t rmidev_write(struct file *filp, const char __user *buf, *f_pos += retval; mutex_unlock(&(dev_data->file_mutex)); - + kfree(tmpbuf); return retval; } diff --git a/drivers/media/platform/msm/camera_v2/isp/msm_isp47.c b/drivers/media/platform/msm/camera_v2/isp/msm_isp47.c index b434161f5599..9a469abc56ca 100644 --- a/drivers/media/platform/msm/camera_v2/isp/msm_isp47.c +++ b/drivers/media/platform/msm/camera_v2/isp/msm_isp47.c @@ -248,13 +248,31 @@ static enum cam_ahb_clk_vote msm_isp47_get_cam_clk_vote( return 0; } -static int msm_isp47_ahb_clk_cfg(struct vfe_device *vfe_dev, +int msm_isp47_ahb_clk_cfg(struct vfe_device *vfe_dev, struct msm_isp_ahb_clk_cfg *ahb_cfg) { int rc = 0; enum cam_ahb_clk_vote vote; + enum cam_ahb_clk_vote src_clk_vote; + struct msm_isp_clk_rates clk_rates; - vote = msm_isp47_get_cam_clk_vote(ahb_cfg->vote); + if (ahb_cfg) + vote = msm_isp47_get_cam_clk_vote(ahb_cfg->vote); + else + vote = CAM_AHB_SVS_VOTE; + + vfe_dev->hw_info->vfe_ops.platform_ops.get_clk_rates(vfe_dev, + &clk_rates); + if (vfe_dev->msm_isp_vfe_clk_rate <= clk_rates.svs_rate) + src_clk_vote = CAM_AHB_SVS_VOTE; + else if (vfe_dev->msm_isp_vfe_clk_rate <= clk_rates.nominal_rate) + src_clk_vote = CAM_AHB_NOMINAL_VOTE; + else + src_clk_vote = CAM_AHB_TURBO_VOTE; + + /* vote for higher of the user requested or src clock matched vote */ + if (vote < src_clk_vote) + vote = src_clk_vote; if (vote && vfe_dev->ahb_vote != vote) { rc = cam_config_ahb_clk(NULL, 0, @@ -320,6 +338,7 @@ enable_regulators_failed: void msm_vfe47_release_hardware(struct vfe_device *vfe_dev) { enum cam_ahb_clk_client id; + unsigned long rate = 0; /* when closing node, disable all irq */ vfe_dev->irq0_mask = 0; @@ -345,6 +364,8 @@ void msm_vfe47_release_hardware(struct vfe_device *vfe_dev) vfe_dev->ahb_vote = CAM_AHB_SUSPEND_VOTE; + vfe_dev->hw_info->vfe_ops.platform_ops.set_clk_rate(vfe_dev, &rate); + vfe_dev->hw_info->vfe_ops.platform_ops.enable_clks( vfe_dev, 0); vfe_dev->hw_info->vfe_ops.platform_ops.enable_regulators(vfe_dev, 0); @@ -378,8 +399,8 @@ void msm_vfe47_init_hardware_reg(struct vfe_device *vfe_dev) /* BUS_CFG */ msm_camera_io_w(0x00000101, vfe_dev->vfe_base + 0x84); /* IRQ_MASK/CLEAR */ - msm_vfe47_config_irq(vfe_dev, 0x810000E0, 0xFFFFFF7E, - MSM_ISP_IRQ_ENABLE); + vfe_dev->hw_info->vfe_ops.irq_ops.config_irq(vfe_dev, + 0x810000E0, 0xFFFFFF7E, MSM_ISP_IRQ_ENABLE); msm_camera_io_w(0xFFFFFFFF, vfe_dev->vfe_base + 0x64); msm_camera_io_w_mb(0xFFFFFFFF, vfe_dev->vfe_base + 0x68); msm_camera_io_w_mb(0x1, vfe_dev->vfe_base + 0x58); @@ -387,8 +408,8 @@ void msm_vfe47_init_hardware_reg(struct vfe_device *vfe_dev) void msm_vfe47_clear_status_reg(struct vfe_device *vfe_dev) { - msm_vfe47_config_irq(vfe_dev, 0x80000000, 0x0, - MSM_ISP_IRQ_SET); + vfe_dev->hw_info->vfe_ops.irq_ops.config_irq(vfe_dev, + 0x80000000, 0x0, MSM_ISP_IRQ_SET); msm_camera_io_w(0xFFFFFFFF, vfe_dev->vfe_base + 0x64); msm_camera_io_w_mb(0xFFFFFFFF, vfe_dev->vfe_base + 0x68); msm_camera_io_w_mb(0x1, vfe_dev->vfe_base + 0x58); @@ -536,7 +557,8 @@ void msm_vfe47_read_irq_status(struct vfe_device *vfe_dev, vfe_dev->error_info.camif_status = msm_camera_io_r(vfe_dev->vfe_base + 0x4A4); /* mask off camif error after first occurrance */ - msm_vfe47_config_irq(vfe_dev, 0, (1 << 0), MSM_ISP_IRQ_DISABLE); + vfe_dev->hw_info->vfe_ops.irq_ops.config_irq(vfe_dev, 0, + (1 << 0), MSM_ISP_IRQ_DISABLE); } if (*irq_status1 & (1 << 7)) @@ -775,7 +797,8 @@ void msm_vfe47_axi_cfg_comp_mask(struct vfe_device *vfe_dev, stream_composite_mask << (comp_mask_index * 8)); msm_camera_io_w(comp_mask, vfe_dev->vfe_base + 0x74); - msm_vfe47_config_irq(vfe_dev, 1 << (comp_mask_index + 25), 0, + vfe_dev->hw_info->vfe_ops.irq_ops.config_irq(vfe_dev, + 1 << (comp_mask_index + 25), 0, MSM_ISP_IRQ_ENABLE); } @@ -790,7 +813,8 @@ void msm_vfe47_axi_clear_comp_mask(struct vfe_device *vfe_dev, comp_mask &= ~(0x7F << (comp_mask_index * 8)); msm_camera_io_w(comp_mask, vfe_dev->vfe_base + 0x74); - msm_vfe47_config_irq(vfe_dev, (1 << (comp_mask_index + 25)), 0, + vfe_dev->hw_info->vfe_ops.irq_ops.config_irq(vfe_dev, + (1 << (comp_mask_index + 25)), 0, MSM_ISP_IRQ_DISABLE); } @@ -799,7 +823,8 @@ void msm_vfe47_axi_cfg_wm_irq_mask(struct vfe_device *vfe_dev, { int vfe_idx = msm_isp_get_vfe_idx_for_stream(vfe_dev, stream_info); - msm_vfe47_config_irq(vfe_dev, 1 << (stream_info->wm[vfe_idx][0] + 8), 0, + vfe_dev->hw_info->vfe_ops.irq_ops.config_irq(vfe_dev, + 1 << (stream_info->wm[vfe_idx][0] + 8), 0, MSM_ISP_IRQ_ENABLE); } @@ -808,7 +833,8 @@ void msm_vfe47_axi_clear_wm_irq_mask(struct vfe_device *vfe_dev, { int vfe_idx = msm_isp_get_vfe_idx_for_stream(vfe_dev, stream_info); - msm_vfe47_config_irq(vfe_dev, (1 << (stream_info->wm[vfe_idx][0] + 8)), + vfe_dev->hw_info->vfe_ops.irq_ops.config_irq(vfe_dev, + (1 << (stream_info->wm[vfe_idx][0] + 8)), 0, MSM_ISP_IRQ_DISABLE); } @@ -1060,7 +1086,8 @@ void msm_vfe47_cfg_fetch_engine(struct vfe_device *vfe_dev, temp |= (1 << 1); msm_camera_io_w(temp, vfe_dev->vfe_base + 0x84); - msm_vfe47_config_irq(vfe_dev, (1 << 24), 0, + vfe_dev->hw_info->vfe_ops.irq_ops.config_irq(vfe_dev, + (1 << 24), 0, MSM_ISP_IRQ_ENABLE); temp = fe_cfg->fetch_height - 1; @@ -1390,7 +1417,8 @@ void msm_vfe47_update_camif_state(struct vfe_device *vfe_dev, msm_camera_io_w(0x0, vfe_dev->vfe_base + 0x64); msm_camera_io_w(0x81, vfe_dev->vfe_base + 0x68); msm_camera_io_w(0x1, vfe_dev->vfe_base + 0x58); - msm_vfe47_config_irq(vfe_dev, 0x15, 0x81, + vfe_dev->hw_info->vfe_ops.irq_ops.config_irq(vfe_dev, + 0x15, 0x81, MSM_ISP_IRQ_ENABLE); if ((vfe_dev->hvx_cmd > HVX_DISABLE) && @@ -1422,7 +1450,7 @@ void msm_vfe47_update_camif_state(struct vfe_device *vfe_dev, if (vfe_dev->axi_data.src_info[VFE_PIX_0].input_mux == TESTGEN) update_state = DISABLE_CAMIF; /* turn off camif violation and error irqs */ - msm_vfe47_config_irq(vfe_dev, 0, 0x81, + vfe_dev->hw_info->vfe_ops.irq_ops.config_irq(vfe_dev, 0, 0x81, MSM_ISP_IRQ_DISABLE); val = msm_camera_io_r(vfe_dev->vfe_base + 0x464); /* disable danger signal */ @@ -1447,7 +1475,8 @@ void msm_vfe47_update_camif_state(struct vfe_device *vfe_dev, msm_camera_io_w(0, vfe_dev->vfe_base + 0x64); msm_camera_io_w(1 << 0, vfe_dev->vfe_base + 0x68); msm_camera_io_w_mb(1, vfe_dev->vfe_base + 0x58); - msm_vfe47_config_irq(vfe_dev, vfe_dev->irq0_mask, + vfe_dev->hw_info->vfe_ops.irq_ops.config_irq(vfe_dev, + vfe_dev->irq0_mask, vfe_dev->irq1_mask, MSM_ISP_IRQ_SET); } @@ -1728,7 +1757,8 @@ int msm_vfe47_axi_halt(struct vfe_device *vfe_dev, msm_camera_io_w(val, vfe_dev->vfe_vbif_base + VFE47_VBIF_CLK_OFFSET); /* Keep only halt and reset mask */ - msm_vfe47_config_irq(vfe_dev, (1 << 31), (1 << 8), + vfe_dev->hw_info->vfe_ops.irq_ops.config_irq(vfe_dev, + (1 << 31), (1 << 8), MSM_ISP_IRQ_SET); /*Clear IRQ Status0, only leave reset irq mask*/ @@ -1777,7 +1807,8 @@ int msm_vfe47_axi_halt(struct vfe_device *vfe_dev, void msm_vfe47_axi_restart(struct vfe_device *vfe_dev, uint32_t blocking, uint32_t enable_camif) { - msm_vfe47_config_irq(vfe_dev, vfe_dev->irq0_mask, vfe_dev->irq1_mask, + vfe_dev->hw_info->vfe_ops.irq_ops.config_irq(vfe_dev, + vfe_dev->irq0_mask, vfe_dev->irq1_mask, MSM_ISP_IRQ_SET); msm_camera_io_w(0x7FFFFFFF, vfe_dev->vfe_base + 0x64); msm_camera_io_w(0xFFFFFEFF, vfe_dev->vfe_base + 0x68); @@ -1884,7 +1915,8 @@ void msm_vfe47_stats_cfg_comp_mask( comp_mask_reg |= stats_mask << (request_comp_index * 16); atomic_set(stats_comp_mask, stats_mask | atomic_read(stats_comp_mask)); - msm_vfe47_config_irq(vfe_dev, 1 << (29 + request_comp_index), + vfe_dev->hw_info->vfe_ops.irq_ops.config_irq(vfe_dev, + 1 << (29 + request_comp_index), 0, MSM_ISP_IRQ_ENABLE); } else { if (!(atomic_read(stats_comp_mask) & stats_mask)) @@ -1893,7 +1925,8 @@ void msm_vfe47_stats_cfg_comp_mask( atomic_set(stats_comp_mask, ~stats_mask & atomic_read(stats_comp_mask)); comp_mask_reg &= ~(stats_mask << (request_comp_index * 16)); - msm_vfe47_config_irq(vfe_dev, 1 << (29 + request_comp_index), + vfe_dev->hw_info->vfe_ops.irq_ops.config_irq(vfe_dev, + 1 << (29 + request_comp_index), 0, MSM_ISP_IRQ_DISABLE); } @@ -1916,32 +1949,41 @@ void msm_vfe47_stats_cfg_wm_irq_mask( switch (STATS_IDX(stream_info->stream_handle[vfe_idx])) { case STATS_COMP_IDX_AEC_BG: - msm_vfe47_config_irq(vfe_dev, 1 << 15, 0, MSM_ISP_IRQ_ENABLE); + vfe_dev->hw_info->vfe_ops.irq_ops.config_irq(vfe_dev, + 1 << 15, 0, MSM_ISP_IRQ_ENABLE); break; case STATS_COMP_IDX_HDR_BE: - msm_vfe47_config_irq(vfe_dev, 1 << 16, 0, MSM_ISP_IRQ_ENABLE); + vfe_dev->hw_info->vfe_ops.irq_ops.config_irq(vfe_dev, + 1 << 16, 0, MSM_ISP_IRQ_ENABLE); break; case STATS_COMP_IDX_BG: - msm_vfe47_config_irq(vfe_dev, 1 << 17, 0, MSM_ISP_IRQ_ENABLE); + vfe_dev->hw_info->vfe_ops.irq_ops.config_irq(vfe_dev, + 1 << 17, 0, MSM_ISP_IRQ_ENABLE); break; case STATS_COMP_IDX_BF: - msm_vfe47_config_irq(vfe_dev, 1 << 18, 1 << 26, + vfe_dev->hw_info->vfe_ops.irq_ops.config_irq(vfe_dev, + 1 << 18, 1 << 26, MSM_ISP_IRQ_ENABLE); break; case STATS_COMP_IDX_HDR_BHIST: - msm_vfe47_config_irq(vfe_dev, 1 << 19, 0, MSM_ISP_IRQ_ENABLE); + vfe_dev->hw_info->vfe_ops.irq_ops.config_irq(vfe_dev, + 1 << 19, 0, MSM_ISP_IRQ_ENABLE); break; case STATS_COMP_IDX_RS: - msm_vfe47_config_irq(vfe_dev, 1 << 20, 0, MSM_ISP_IRQ_ENABLE); + vfe_dev->hw_info->vfe_ops.irq_ops.config_irq(vfe_dev, + 1 << 20, 0, MSM_ISP_IRQ_ENABLE); break; case STATS_COMP_IDX_CS: - msm_vfe47_config_irq(vfe_dev, 1 << 21, 0, MSM_ISP_IRQ_ENABLE); + vfe_dev->hw_info->vfe_ops.irq_ops.config_irq(vfe_dev, + 1 << 21, 0, MSM_ISP_IRQ_ENABLE); break; case STATS_COMP_IDX_IHIST: - msm_vfe47_config_irq(vfe_dev, 1 << 22, 0, MSM_ISP_IRQ_ENABLE); + vfe_dev->hw_info->vfe_ops.irq_ops.config_irq(vfe_dev, + 1 << 22, 0, MSM_ISP_IRQ_ENABLE); break; case STATS_COMP_IDX_BHIST: - msm_vfe47_config_irq(vfe_dev, 1 << 23, 0, MSM_ISP_IRQ_ENABLE); + vfe_dev->hw_info->vfe_ops.irq_ops.config_irq(vfe_dev, + 1 << 23, 0, MSM_ISP_IRQ_ENABLE); break; default: pr_err("%s: Invalid stats idx %d\n", __func__, @@ -1958,32 +2000,41 @@ void msm_vfe47_stats_clear_wm_irq_mask( switch (STATS_IDX(stream_info->stream_handle[vfe_idx])) { case STATS_COMP_IDX_AEC_BG: - msm_vfe47_config_irq(vfe_dev, 1 << 15, 0, MSM_ISP_IRQ_DISABLE); + vfe_dev->hw_info->vfe_ops.irq_ops.config_irq(vfe_dev, + 1 << 15, 0, MSM_ISP_IRQ_DISABLE); break; case STATS_COMP_IDX_HDR_BE: - msm_vfe47_config_irq(vfe_dev, 1 << 16, 0, MSM_ISP_IRQ_DISABLE); + vfe_dev->hw_info->vfe_ops.irq_ops.config_irq(vfe_dev, + 1 << 16, 0, MSM_ISP_IRQ_DISABLE); break; case STATS_COMP_IDX_BG: - msm_vfe47_config_irq(vfe_dev, 1 << 17, 0, MSM_ISP_IRQ_DISABLE); + vfe_dev->hw_info->vfe_ops.irq_ops.config_irq(vfe_dev, + 1 << 17, 0, MSM_ISP_IRQ_DISABLE); break; case STATS_COMP_IDX_BF: - msm_vfe47_config_irq(vfe_dev, 1 << 18, 1 << 26, + vfe_dev->hw_info->vfe_ops.irq_ops.config_irq(vfe_dev, + 1 << 18, 1 << 26, MSM_ISP_IRQ_DISABLE); break; case STATS_COMP_IDX_HDR_BHIST: - msm_vfe47_config_irq(vfe_dev, 1 << 19, 0, MSM_ISP_IRQ_DISABLE); + vfe_dev->hw_info->vfe_ops.irq_ops.config_irq(vfe_dev, + 1 << 19, 0, MSM_ISP_IRQ_DISABLE); break; case STATS_COMP_IDX_RS: - msm_vfe47_config_irq(vfe_dev, 1 << 20, 0, MSM_ISP_IRQ_DISABLE); + vfe_dev->hw_info->vfe_ops.irq_ops.config_irq(vfe_dev, + 1 << 20, 0, MSM_ISP_IRQ_DISABLE); break; case STATS_COMP_IDX_CS: - msm_vfe47_config_irq(vfe_dev, 1 << 21, 0, MSM_ISP_IRQ_DISABLE); + vfe_dev->hw_info->vfe_ops.irq_ops.config_irq(vfe_dev, + 1 << 21, 0, MSM_ISP_IRQ_DISABLE); break; case STATS_COMP_IDX_IHIST: - msm_vfe47_config_irq(vfe_dev, 1 << 22, 0, MSM_ISP_IRQ_DISABLE); + vfe_dev->hw_info->vfe_ops.irq_ops.config_irq(vfe_dev, + 1 << 22, 0, MSM_ISP_IRQ_DISABLE); break; case STATS_COMP_IDX_BHIST: - msm_vfe47_config_irq(vfe_dev, 1 << 23, 0, MSM_ISP_IRQ_DISABLE); + vfe_dev->hw_info->vfe_ops.irq_ops.config_irq(vfe_dev, + 1 << 23, 0, MSM_ISP_IRQ_DISABLE); break; default: pr_err("%s: Invalid stats idx %d\n", __func__, @@ -2336,6 +2387,9 @@ int msm_vfe47_set_clk_rate(struct vfe_device *vfe_dev, long *rate) return rc; *rate = clk_round_rate(vfe_dev->vfe_clk[clk_idx], *rate); vfe_dev->msm_isp_vfe_clk_rate = *rate; + + if (vfe_dev->hw_info->vfe_ops.core_ops.ahb_clk_cfg) + vfe_dev->hw_info->vfe_ops.core_ops.ahb_clk_cfg(vfe_dev, NULL); return 0; } diff --git a/drivers/media/platform/msm/camera_v2/isp/msm_isp47.h b/drivers/media/platform/msm/camera_v2/isp/msm_isp47.h index 8581373b3b71..3955196d1deb 100644 --- a/drivers/media/platform/msm/camera_v2/isp/msm_isp47.h +++ b/drivers/media/platform/msm/camera_v2/isp/msm_isp47.h @@ -193,4 +193,6 @@ int msm_vfe47_update_bandwidth( void msm_vfe47_config_irq(struct vfe_device *vfe_dev, uint32_t irq0_mask, uint32_t irq1_mask, enum msm_isp_irq_operation oper); +int msm_isp47_ahb_clk_cfg(struct vfe_device *vfe_dev, + struct msm_isp_ahb_clk_cfg *ahb_cfg); #endif /* __MSM_ISP47_H__ */ diff --git a/drivers/media/platform/msm/camera_v2/isp/msm_isp48.c b/drivers/media/platform/msm/camera_v2/isp/msm_isp48.c index a792404c243c..49520bb44ad8 100644 --- a/drivers/media/platform/msm/camera_v2/isp/msm_isp48.c +++ b/drivers/media/platform/msm/camera_v2/isp/msm_isp48.c @@ -246,6 +246,8 @@ struct msm_vfe_hardware_info vfe48_hw_info = { .num_iommu_secure_ctx = 0, .vfe_clk_idx = VFE48_SRC_CLK_DTSI_IDX, .runtime_axi_update = 1, + .min_ib = 100000000, + .min_ab = 100000000, .vfe_ops = { .irq_ops = { .read_irq_status = msm_vfe47_read_irq_status, @@ -306,6 +308,7 @@ struct msm_vfe_hardware_info vfe48_hw_info = { .process_error_status = msm_vfe47_process_error_status, .is_module_cfg_lock_needed = msm_vfe47_is_module_cfg_lock_needed, + .ahb_clk_cfg = msm_isp47_ahb_clk_cfg, }, .stats_ops = { .get_stats_idx = msm_vfe47_get_stats_idx, diff --git a/drivers/media/platform/msm/camera_v2/isp/msm_isp_axi_util.c b/drivers/media/platform/msm/camera_v2/isp/msm_isp_axi_util.c index 39a0845a886f..4a32de1a85bf 100644 --- a/drivers/media/platform/msm/camera_v2/isp/msm_isp_axi_util.c +++ b/drivers/media/platform/msm/camera_v2/isp/msm_isp_axi_util.c @@ -1023,31 +1023,32 @@ static void msm_isp_calculate_bandwidth( struct msm_vfe_axi_stream *stream_info) { int bpp = 0; + struct vfe_device *vfe_dev; struct msm_vfe_axi_shared_data *axi_data; int i; - if (stream_info->stream_src < RDI_INTF_0) { - for (i = 0; i < stream_info->num_isp; i++) { - axi_data = &stream_info->vfe_dev[i]->axi_data; + for (i = 0; i < stream_info->num_isp; i++) { + vfe_dev = stream_info->vfe_dev[i]; + axi_data = &vfe_dev->axi_data; + if (stream_info->stream_src < RDI_INTF_0) { stream_info->bandwidth[i] = - (axi_data->src_info[VFE_PIX_0].pixel_clock / + (vfe_dev->msm_isp_vfe_clk_rate / axi_data->src_info[VFE_PIX_0].width) * stream_info->max_width[i]; stream_info->bandwidth[i] = (unsigned long)stream_info->bandwidth[i] * stream_info->format_factor / ISP_Q2; - } - } else { - int rdi = SRC_TO_INTF(stream_info->stream_src); - bpp = msm_isp_get_bit_per_pixel(stream_info->output_format); - if (rdi < VFE_SRC_MAX) { - for (i = 0; i < stream_info->num_isp; i++) { - axi_data = &stream_info->vfe_dev[i]->axi_data; + } else { + int rdi = SRC_TO_INTF(stream_info->stream_src); + + bpp = msm_isp_get_bit_per_pixel( + stream_info->output_format); + if (rdi < VFE_SRC_MAX) { stream_info->bandwidth[i] = - (axi_data->src_info[rdi].pixel_clock / 8) * bpp; + (vfe_dev->msm_isp_vfe_clk_rate / 8) * bpp; + } else { + pr_err("%s: Invalid rdi interface\n", __func__); } - } else { - pr_err("%s: Invalid rdi interface\n", __func__); } } } @@ -1411,6 +1412,7 @@ static void __msm_isp_axi_stream_update( switch (stream_info->state) { case UPDATING: stream_info->state = ACTIVE; + complete_all(&stream_info->active_comp); break; case STOP_PENDING: msm_isp_axi_stream_enable_cfg(stream_info); @@ -2268,13 +2270,14 @@ int msm_isp_axi_halt(struct vfe_device *vfe_dev, int msm_isp_axi_reset(struct vfe_device *vfe_dev, struct msm_vfe_axi_reset_cmd *reset_cmd) { - int rc = 0, i, k; + int rc = 0, i, k, j; struct msm_vfe_axi_stream *stream_info; struct msm_vfe_axi_shared_data *axi_data = &vfe_dev->axi_data; uint32_t bufq_handle = 0, bufq_id = 0; struct msm_isp_timestamp timestamp; unsigned long flags; struct vfe_device *update_vfes[MAX_VFE] = {0, 0}; + int vfe_idx; if (!reset_cmd) { pr_err("%s: NULL pointer reset cmd %pK\n", __func__, reset_cmd); @@ -2345,6 +2348,20 @@ int msm_isp_axi_reset(struct vfe_device *vfe_dev, ISP_EVENT_BUF_FATAL_ERROR); return rc; } + if (stream_info->num_planes > 1) { + vfe_dev->hw_info->vfe_ops.axi_ops. + cfg_comp_mask(vfe_dev, stream_info); + } else { + vfe_dev->hw_info->vfe_ops.axi_ops. + cfg_wm_irq_mask(vfe_dev, stream_info); + } + vfe_idx = msm_isp_get_vfe_idx_for_stream( + vfe_dev, stream_info); + for (j = 0; j < stream_info->num_planes; j++) + vfe_dev->hw_info->vfe_ops.axi_ops. + enable_wm( + vfe_dev->vfe_base, + stream_info->wm[vfe_idx][j], 1); axi_data->src_info[SRC_TO_INTF(stream_info-> stream_src)].frame_id = @@ -2731,6 +2748,7 @@ static void __msm_isp_stop_axi_streams(struct msm_vfe_axi_stream **streams, ×tamp); msm_isp_cfg_stream_scratch(stream_info, VFE_PING_FLAG); msm_isp_cfg_stream_scratch(stream_info, VFE_PONG_FLAG); + stream_info->undelivered_request_cnt = 0; for (k = 0; k < stream_info->num_isp; k++) { vfe_dev = stream_info->vfe_dev[k]; if (stream_info->num_planes > 1) @@ -2879,6 +2897,8 @@ static int msm_isp_start_axi_stream(struct vfe_device *vfe_dev_ioctl, msm_isp_get_timestamp(×tamp); for (i = 0; i < stream_cfg_cmd->num_streams; i++) { + if (stream_cfg_cmd->stream_handle[i] == 0) + continue; stream_info = msm_isp_get_stream_common_data(vfe_dev_ioctl, HANDLE_TO_IDX(stream_cfg_cmd->stream_handle[i])); if (SRC_TO_INTF(stream_info->stream_src) < VFE_SRC_MAX) @@ -3018,6 +3038,8 @@ static int msm_isp_stop_axi_stream(struct vfe_device *vfe_dev_ioctl, return -EINVAL; for (i = 0; i < stream_cfg_cmd->num_streams; i++) { + if (stream_cfg_cmd->stream_handle[i] == 0) + continue; stream_info = msm_isp_get_stream_common_data(vfe_dev_ioctl, HANDLE_TO_IDX(stream_cfg_cmd->stream_handle[i])); @@ -3044,12 +3066,37 @@ int msm_isp_cfg_axi_stream(struct vfe_device *vfe_dev, void *arg) { int rc = 0, ret; struct msm_vfe_axi_stream_cfg_cmd *stream_cfg_cmd = arg; + uint32_t stream_idx[MAX_NUM_STREAM]; int i; + int vfe_idx; + struct msm_vfe_axi_stream *stream_info; + + memset(stream_idx, 0, sizeof(stream_idx)); for (i = 0; i < stream_cfg_cmd->num_streams; i++) { if (HANDLE_TO_IDX(stream_cfg_cmd->stream_handle[i]) >= VFE_AXI_SRC_MAX) return -EINVAL; + stream_info = msm_isp_get_stream_common_data(vfe_dev, + HANDLE_TO_IDX(stream_cfg_cmd->stream_handle[i])); + vfe_idx = msm_isp_get_vfe_idx_for_stream_user(vfe_dev, + stream_info); + if (vfe_idx == -ENOTTY || stream_info->stream_handle[vfe_idx] != + stream_cfg_cmd->stream_handle[i]) { + pr_err("%s: Invalid stream handle %x vfe_idx %d expected %x\n", + __func__, stream_cfg_cmd->stream_handle[i], + vfe_idx, + (vfe_idx != -ENOTTY) ? + stream_info->stream_handle[vfe_idx] : 0); + return -EINVAL; + } + /* check for duplicate stream handle */ + if (stream_idx[stream_info->stream_src] == + stream_cfg_cmd->stream_handle[i]) + stream_cfg_cmd->stream_handle[i] = 0; + else + stream_idx[stream_info->stream_src] = + stream_cfg_cmd->stream_handle[i]; } if (stream_cfg_cmd->cmd == START_STREAM) { msm_isp_axi_update_cgc_override(vfe_dev, stream_cfg_cmd, 1); @@ -3387,8 +3434,10 @@ static void msm_isp_remove_buf_queue(struct vfe_device *vfe_dev, if (stream_info->bufq_handle[bufq_id]) { stream_info->bufq_handle[bufq_id] = 0; - if (stream_info->state == ACTIVE) + if (stream_info->state == ACTIVE) { + init_completion(&stream_info->active_comp); stream_info->state = UPDATING; + } } spin_unlock_irqrestore(&stream_info->lock, flags); if (stream_info->state == UPDATING) diff --git a/drivers/media/platform/msm/camera_v2/isp/msm_isp_stats_util.c b/drivers/media/platform/msm/camera_v2/isp/msm_isp_stats_util.c index f851e8c9289e..923cd9aa5954 100644 --- a/drivers/media/platform/msm/camera_v2/isp/msm_isp_stats_util.c +++ b/drivers/media/platform/msm/camera_v2/isp/msm_isp_stats_util.c @@ -904,6 +904,10 @@ int msm_isp_stats_reset(struct vfe_device *vfe_dev) ISP_EVENT_BUF_FATAL_ERROR); return rc; } + vfe_dev->hw_info->vfe_ops.stats_ops.cfg_wm_irq_mask( + vfe_dev, stream_info); + vfe_dev->hw_info->vfe_ops.stats_ops.enable_module( + vfe_dev, BIT(i), 1); } } @@ -960,7 +964,9 @@ static int msm_isp_check_stream_cfg_cmd(struct vfe_device *vfe_dev, struct msm_vfe_stats_stream *stream_info; uint32_t idx; int vfe_idx; + uint32_t stats_idx[MSM_ISP_STATS_MAX]; + memset(stats_idx, 0, sizeof(stats_idx)); for (i = 0; i < stream_cfg_cmd->num_streams; i++) { idx = STATS_IDX(stream_cfg_cmd->stream_handle[i]); @@ -980,6 +986,11 @@ static int msm_isp_check_stream_cfg_cmd(struct vfe_device *vfe_dev, stream_info->stream_handle[vfe_idx]); return -EINVAL; } + /* remove duplicate handles */ + if (stats_idx[idx] == stream_cfg_cmd->stream_handle[i]) + stream_cfg_cmd->stream_handle[i] = 0; + else + stats_idx[idx] = stream_cfg_cmd->stream_handle[i]; } return 0; } @@ -1083,6 +1094,8 @@ static int msm_isp_start_stats_stream(struct vfe_device *vfe_dev_ioctl, num_stats_comp_mask = vfe_dev_ioctl->hw_info->stats_hw_info->num_stats_comp_mask; for (i = 0; i < stream_cfg_cmd->num_streams; i++) { + if (stream_cfg_cmd->stream_handle[i] == 0) + continue; idx = STATS_IDX(stream_cfg_cmd->stream_handle[i]); stream_info = msm_isp_get_stats_stream_common_data( vfe_dev_ioctl, idx); @@ -1169,7 +1182,8 @@ static int msm_isp_stop_stats_stream(struct vfe_device *vfe_dev, vfe_dev->hw_info->stats_hw_info->num_stats_comp_mask; for (i = 0; i < stream_cfg_cmd->num_streams; i++) { - + if (stream_cfg_cmd->stream_handle[i] == 0) + continue; idx = STATS_IDX(stream_cfg_cmd->stream_handle[i]); stream_info = msm_isp_get_stats_stream_common_data( diff --git a/drivers/media/platform/msm/camera_v2/isp/msm_isp_util.c b/drivers/media/platform/msm/camera_v2/isp/msm_isp_util.c index a4eb80f31984..71c907f2b381 100644 --- a/drivers/media/platform/msm/camera_v2/isp/msm_isp_util.c +++ b/drivers/media/platform/msm/camera_v2/isp/msm_isp_util.c @@ -20,6 +20,7 @@ #include "msm_isp_stats_util.h" #include "msm_camera_io_util.h" #include "cam_smmu_api.h" +#include "msm_isp48.h" #define MAX_ISP_V4l2_EVENTS 100 static DEFINE_MUTEX(bandwidth_mgr_mutex); @@ -482,11 +483,18 @@ int msm_isp_cfg_input(struct vfe_device *vfe_dev, void *arg) } pixel_clock = input_cfg->input_pix_clk; - rc = vfe_dev->hw_info->vfe_ops.platform_ops.set_clk_rate(vfe_dev, - &pixel_clock); - if (rc < 0) { - pr_err("%s: clock set rate failed\n", __func__); - return rc; + /* + * Only set rate to higher, do not lower higher + * rate needed by another input + */ + if (pixel_clock > vfe_dev->msm_isp_vfe_clk_rate) { + rc = vfe_dev->hw_info->vfe_ops.platform_ops.set_clk_rate( + vfe_dev, + &pixel_clock); + if (rc < 0) { + pr_err("%s: clock set rate failed\n", __func__); + return rc; + } } return rc; } @@ -1739,6 +1747,9 @@ static void msm_isp_process_overflow_irq( if (overflow_mask) { struct msm_isp_event_data error_event; struct msm_vfe_axi_halt_cmd halt_cmd; + uint32_t val = 0; + int i; + struct msm_vfe_axi_shared_data *axi_data = &vfe_dev->axi_data; if (vfe_dev->reset_pending == 1) { pr_err("%s:%d failed: overflow %x during reset\n", @@ -1747,6 +1758,16 @@ static void msm_isp_process_overflow_irq( *irq_status1 &= ~overflow_mask; return; } + if (msm_vfe_is_vfe48(vfe_dev)) + val = msm_camera_io_r(vfe_dev->vfe_base + 0xC94); + pr_err("%s: vfe %d overflow mask %x, bus_error %x\n", + __func__, vfe_dev->pdev->id, overflow_mask, val); + for (i = 0; i < axi_data->hw_info->num_wm; i++) { + if (!axi_data->free_wm[i]) + continue; + pr_err("%s: wm %d assigned to stream handle %x\n", + __func__, i, axi_data->free_wm[i]); + } halt_cmd.overflow_detected = 1; halt_cmd.stop_camif = 1; diff --git a/drivers/media/platform/msm/camera_v2/sensor/actuator/msm_actuator.c b/drivers/media/platform/msm/camera_v2/sensor/actuator/msm_actuator.c index 0b3e4e1fcf04..bf3973888573 100644 --- a/drivers/media/platform/msm/camera_v2/sensor/actuator/msm_actuator.c +++ b/drivers/media/platform/msm/camera_v2/sensor/actuator/msm_actuator.c @@ -101,11 +101,6 @@ static void msm_actuator_parse_i2c_params(struct msm_actuator_ctrl_t *a_ctrl, i2c_tbl = a_ctrl->i2c_reg_tbl; for (i = 0; i < size; i++) { - /* check that the index into i2c_tbl cannot grow larger that - the allocated size of i2c_tbl */ - if ((a_ctrl->total_steps + 1) < (a_ctrl->i2c_tbl_index)) - break; - if (write_arr[i].reg_write_type == MSM_ACTUATOR_WRITE_DAC) { value = (next_lens_position << write_arr[i].data_shift) | @@ -119,6 +114,11 @@ static void msm_actuator_parse_i2c_params(struct msm_actuator_ctrl_t *a_ctrl, i2c_byte2 = value & 0xFF; CDBG("byte1:0x%x, byte2:0x%x\n", i2c_byte1, i2c_byte2); + if (a_ctrl->i2c_tbl_index > + a_ctrl->total_steps) { + pr_err("failed:i2c table index out of bound\n"); + break; + } i2c_tbl[a_ctrl->i2c_tbl_index]. reg_addr = i2c_byte1; i2c_tbl[a_ctrl->i2c_tbl_index]. @@ -139,6 +139,10 @@ static void msm_actuator_parse_i2c_params(struct msm_actuator_ctrl_t *a_ctrl, i2c_byte2 = (hw_dword & write_arr[i].hw_mask) >> write_arr[i].hw_shift; } + if (a_ctrl->i2c_tbl_index > a_ctrl->total_steps) { + pr_err("failed: i2c table index out of bound\n"); + break; + } CDBG("i2c_byte1:0x%x, i2c_byte2:0x%x\n", i2c_byte1, i2c_byte2); i2c_tbl[a_ctrl->i2c_tbl_index].reg_addr = i2c_byte1; i2c_tbl[a_ctrl->i2c_tbl_index].reg_data = i2c_byte2; diff --git a/drivers/net/wireless/ath/wil6210/debugfs.c b/drivers/net/wireless/ath/wil6210/debugfs.c index ce115c98a72f..d3e420f1b26b 100644 --- a/drivers/net/wireless/ath/wil6210/debugfs.c +++ b/drivers/net/wireless/ath/wil6210/debugfs.c @@ -1583,6 +1583,32 @@ static const struct file_operations fops_fw_capabilities = { .llseek = seq_lseek, }; +/*---------FW version------------*/ +static int wil_fw_version_debugfs_show(struct seq_file *s, void *data) +{ + struct wil6210_priv *wil = s->private; + + if (wil->fw_version[0]) + seq_printf(s, "%s\n", wil->fw_version); + else + seq_puts(s, "N/A\n"); + + return 0; +} + +static int wil_fw_version_seq_open(struct inode *inode, struct file *file) +{ + return single_open(file, wil_fw_version_debugfs_show, + inode->i_private); +} + +static const struct file_operations fops_fw_version = { + .open = wil_fw_version_seq_open, + .release = single_release, + .read = seq_read, + .llseek = seq_lseek, +}; + /*----------------*/ static void wil6210_debugfs_init_blobs(struct wil6210_priv *wil, struct dentry *dbg) @@ -1634,6 +1660,7 @@ static const struct { {"led_cfg", S_IRUGO | S_IWUSR, &fops_led_cfg}, {"led_blink_time", S_IRUGO | S_IWUSR, &fops_led_blink_time}, {"fw_capabilities", S_IRUGO, &fops_fw_capabilities}, + {"fw_version", S_IRUGO, &fops_fw_version}, }; static void wil6210_debugfs_init_files(struct wil6210_priv *wil, @@ -1674,7 +1701,6 @@ static void wil6210_debugfs_init_isr(struct wil6210_priv *wil, static const struct dbg_off dbg_wil_off[] = { WIL_FIELD(privacy, S_IRUGO, doff_u32), WIL_FIELD(status[0], S_IRUGO | S_IWUSR, doff_ulong), - WIL_FIELD(fw_version, S_IRUGO, doff_u32), WIL_FIELD(hw_version, S_IRUGO, doff_x32), WIL_FIELD(recovery_count, S_IRUGO, doff_u32), WIL_FIELD(ap_isolate, S_IRUGO, doff_u32), diff --git a/drivers/net/wireless/ath/wil6210/fw.h b/drivers/net/wireless/ath/wil6210/fw.h index c3191c61832c..2f2b910501ba 100644 --- a/drivers/net/wireless/ath/wil6210/fw.h +++ b/drivers/net/wireless/ath/wil6210/fw.h @@ -102,6 +102,9 @@ struct wil_fw_record_verify { /* type == wil_fw_verify */ /* file header * First record of every file */ +/* the FW version prefix in the comment */ +#define WIL_FW_VERSION_PREFIX "FW version: " +#define WIL_FW_VERSION_PREFIX_LEN (sizeof(WIL_FW_VERSION_PREFIX) - 1) struct wil_fw_record_file_header { __le32 signature ; /* Wilocity signature */ __le32 reserved; diff --git a/drivers/net/wireless/ath/wil6210/fw_inc.c b/drivers/net/wireless/ath/wil6210/fw_inc.c index 3860238840ba..8f40eb301924 100644 --- a/drivers/net/wireless/ath/wil6210/fw_inc.c +++ b/drivers/net/wireless/ath/wil6210/fw_inc.c @@ -223,6 +223,13 @@ static int fw_handle_file_header(struct wil6210_priv *wil, const void *data, wil_hex_dump_fw("", DUMP_PREFIX_OFFSET, 16, 1, d->comment, sizeof(d->comment), true); + if (!memcmp(d->comment, WIL_FW_VERSION_PREFIX, + WIL_FW_VERSION_PREFIX_LEN)) + memcpy(wil->fw_version, + d->comment + WIL_FW_VERSION_PREFIX_LEN, + min(sizeof(d->comment) - WIL_FW_VERSION_PREFIX_LEN, + sizeof(wil->fw_version) - 1)); + return 0; } diff --git a/drivers/net/wireless/ath/wil6210/main.c b/drivers/net/wireless/ath/wil6210/main.c index 88dfb93d0f3d..a509841c3187 100644 --- a/drivers/net/wireless/ath/wil6210/main.c +++ b/drivers/net/wireless/ath/wil6210/main.c @@ -892,6 +892,7 @@ int wil_reset(struct wil6210_priv *wil, bool load_fw) WIL_FW2_NAME); wil_halt_cpu(wil); + memset(wil->fw_version, 0, sizeof(wil->fw_version)); /* Loading f/w from the file */ rc = wil_request_firmware(wil, WIL_FW_NAME, true); if (rc) diff --git a/drivers/net/wireless/ath/wil6210/netdev.c b/drivers/net/wireless/ath/wil6210/netdev.c index 81a4c6a684f5..f4fca9d4eedf 100644 --- a/drivers/net/wireless/ath/wil6210/netdev.c +++ b/drivers/net/wireless/ath/wil6210/netdev.c @@ -222,6 +222,8 @@ int wil_if_add(struct wil6210_priv *wil) wil_dbg_misc(wil, "entered"); + strlcpy(wiphy->fw_version, wil->fw_version, sizeof(wiphy->fw_version)); + rc = wiphy_register(wiphy); if (rc < 0) { wil_err(wil, "failed to register wiphy, err %d\n", rc); diff --git a/drivers/net/wireless/ath/wil6210/wil6210.h b/drivers/net/wireless/ath/wil6210/wil6210.h index 1e0536adb6e7..ce33e919d321 100644 --- a/drivers/net/wireless/ath/wil6210/wil6210.h +++ b/drivers/net/wireless/ath/wil6210/wil6210.h @@ -17,6 +17,7 @@ #ifndef __WIL6210_H__ #define __WIL6210_H__ +#include <linux/etherdevice.h> #include <linux/netdevice.h> #include <linux/wireless.h> #include <net/cfg80211.h> @@ -579,7 +580,7 @@ struct wil6210_priv { struct wireless_dev *wdev; void __iomem *csr; DECLARE_BITMAP(status, wil_status_last); - u32 fw_version; + u8 fw_version[ETHTOOL_FWVERS_LEN]; u32 hw_version; const char *hw_name; DECLARE_BITMAP(hw_capabilities, hw_capability_last); diff --git a/drivers/net/wireless/ath/wil6210/wmi.c b/drivers/net/wireless/ath/wil6210/wmi.c index 0c5db9584159..6ec3ddc5b6f1 100644 --- a/drivers/net/wireless/ath/wil6210/wmi.c +++ b/drivers/net/wireless/ath/wil6210/wmi.c @@ -312,14 +312,14 @@ static void wmi_evt_ready(struct wil6210_priv *wil, int id, void *d, int len) struct wireless_dev *wdev = wil->wdev; struct wmi_ready_event *evt = d; - wil->fw_version = le32_to_cpu(evt->sw_version); wil->n_mids = evt->numof_additional_mids; - wil_info(wil, "FW ver. %d; MAC %pM; %d MID's\n", wil->fw_version, + wil_info(wil, "FW ver. %s(SW %d); MAC %pM; %d MID's\n", + wil->fw_version, le32_to_cpu(evt->sw_version), evt->mac, wil->n_mids); /* ignore MAC address, we already have it from the boot loader */ - snprintf(wdev->wiphy->fw_version, sizeof(wdev->wiphy->fw_version), - "%d", wil->fw_version); + strlcpy(wdev->wiphy->fw_version, wil->fw_version, + sizeof(wdev->wiphy->fw_version)); wil_set_recovery_state(wil, fw_recovery_idle); set_bit(wil_status_fwready, wil->status); diff --git a/drivers/platform/msm/sps/bam.c b/drivers/platform/msm/sps/bam.c index 3aef2060ab52..c94536398dac 100644 --- a/drivers/platform/msm/sps/bam.c +++ b/drivers/platform/msm/sps/bam.c @@ -1162,7 +1162,7 @@ void bam_output_register_content(void *base, u32 ee) print_bam_test_bus_reg(base, 0); - print_bam_selected_reg(dev->base, BAM_MAX_EES); + print_bam_selected_reg(base, BAM_MAX_EES); num_pipes = bam_read_reg_field(base, NUM_PIPES, 0, BAM_NUM_PIPES); @@ -1174,11 +1174,11 @@ void bam_output_register_content(void *base, u32 ee) if (!enhd_pipe || !pipe_attr) for (i = 0; i < num_pipes; i++) - print_bam_pipe_selected_reg(dev->base, i); + print_bam_pipe_selected_reg(base, i); else { for (i = 0; i < num_pipes; i++) { if (pipe_attr & (1UL << i)) - print_bam_pipe_selected_reg(dev->base, i); + print_bam_pipe_selected_reg(base, i); } } } diff --git a/drivers/power/qcom-charger/Makefile b/drivers/power/qcom-charger/Makefile index aae6084c3c10..0126d2d0a18e 100644 --- a/drivers/power/qcom-charger/Makefile +++ b/drivers/power/qcom-charger/Makefile @@ -6,6 +6,6 @@ obj-$(CONFIG_SMB1351_USB_CHARGER) += smb1351-charger.o pmic-voter.o obj-$(CONFIG_MSM_BCL_CTL) += msm_bcl.o obj-$(CONFIG_MSM_BCL_PERIPHERAL_CTL) += bcl_peripheral.o obj-$(CONFIG_BATTERY_BCL) += battery_current_limit.o -obj-$(CONFIG_QPNP_SMB2) += qpnp-smb2.o smb-lib.o pmic-voter.o -obj-$(CONFIG_SMB138X_CHARGER) += smb138x-charger.o smb-lib.o pmic-voter.o +obj-$(CONFIG_QPNP_SMB2) += qpnp-smb2.o smb-lib.o pmic-voter.o storm-watch.o +obj-$(CONFIG_SMB138X_CHARGER) += smb138x-charger.o smb-lib.o pmic-voter.o storm-watch.o obj-$(CONFIG_QPNP_QNOVO) += qpnp-qnovo.o diff --git a/drivers/power/qcom-charger/battery_current_limit.c b/drivers/power/qcom-charger/battery_current_limit.c index 2bda5ce1a8c4..951d0544efa0 100644 --- a/drivers/power/qcom-charger/battery_current_limit.c +++ b/drivers/power/qcom-charger/battery_current_limit.c @@ -174,6 +174,9 @@ struct bcl_context { struct qpnp_adc_tm_btm_param btm_vph_adc_param; /* Low temp min freq limit requested by thermal */ uint32_t thermal_freq_limit; + /* state of charge notifier */ + struct notifier_block psy_nb; + struct work_struct soc_mitig_work; /* BCL Peripheral monitor parameters */ struct bcl_threshold ibat_high_thresh; @@ -204,8 +207,6 @@ static DEFINE_MUTEX(bcl_hotplug_mutex); static bool bcl_hotplug_enabled; static uint32_t battery_soc_val = 100; static uint32_t soc_low_threshold; -static struct power_supply *bcl_psy; -static struct power_supply_desc bcl_psy_des; static const char bcl_psy_name[] = "bcl"; static void bcl_handle_hotplug(struct work_struct *work) @@ -277,22 +278,34 @@ static void update_cpu_freq(void) trace_bcl_sw_mitigation_event("End Frequency Mitigation"); } -static void power_supply_callback(struct power_supply *psy) +static void soc_mitigate(struct work_struct *work) { - static struct power_supply *bms_psy; + if (bcl_hotplug_enabled) + queue_work(gbcl->bcl_hotplug_wq, &bcl_hotplug_work); + update_cpu_freq(); +} + +static int power_supply_callback(struct notifier_block *nb, + unsigned long event, void *data) +{ + struct power_supply *psy = data; + static struct power_supply *batt_psy; union power_supply_propval ret = {0,}; int battery_percentage; enum bcl_threshold_state prev_soc_state; if (gbcl->bcl_mode != BCL_DEVICE_ENABLED) { pr_debug("BCL is not enabled\n"); - return; + return NOTIFY_OK; } - if (!bms_psy) - bms_psy = power_supply_get_by_name("bms"); - if (bms_psy) { - battery_percentage = power_supply_get_property(bms_psy, + if (strcmp(psy->desc->name, "battery")) + return NOTIFY_OK; + + if (!batt_psy) + batt_psy = power_supply_get_by_name("battery"); + if (batt_psy) { + battery_percentage = power_supply_get_property(batt_psy, POWER_SUPPLY_PROP_CAPACITY, &ret); battery_percentage = ret.intval; battery_soc_val = battery_percentage; @@ -302,15 +315,14 @@ static void power_supply_callback(struct power_supply *psy) bcl_soc_state = (battery_soc_val <= soc_low_threshold) ? BCL_LOW_THRESHOLD : BCL_HIGH_THRESHOLD; if (bcl_soc_state == prev_soc_state) - return; + return NOTIFY_OK; trace_bcl_sw_mitigation_event( (bcl_soc_state == BCL_LOW_THRESHOLD) ? "trigger SoC mitigation" : "clear SoC mitigation"); - if (bcl_hotplug_enabled) - queue_work(gbcl->bcl_hotplug_wq, &bcl_hotplug_work); - update_cpu_freq(); + schedule_work(&gbcl->soc_mitig_work); } + return NOTIFY_OK; } static int bcl_get_battery_voltage(int *vbatt_mv) @@ -624,7 +636,6 @@ static void bcl_periph_vbat_notify(enum bcl_trip_type type, int trip_temp, static void bcl_periph_mode_set(enum bcl_device_mode mode) { int ret = 0; - struct power_supply_config bcl_psy_cfg = {}; if (mode == BCL_DEVICE_ENABLED) { /* @@ -632,15 +643,11 @@ static void bcl_periph_mode_set(enum bcl_device_mode mode) * power state changes. Make sure we read the current SoC * and mitigate. */ - power_supply_callback(bcl_psy); - bcl_psy_cfg.num_supplicants = 0; - bcl_psy_cfg.drv_data = gbcl; - - bcl_psy = power_supply_register(gbcl->dev, &bcl_psy_des, - &bcl_psy_cfg); - if (IS_ERR(bcl_psy)) { - pr_err("Unable to register bcl_psy rc = %ld\n", - PTR_ERR(bcl_psy)); + power_supply_callback(&gbcl->psy_nb, 1, gbcl); + ret = power_supply_reg_notifier(&gbcl->psy_nb); + if (ret < 0) { + pr_err("Unable to register soc notifier rc = %d\n", + ret); return; } ret = msm_bcl_set_threshold(BCL_PARAM_CURRENT, BCL_HIGH_TRIP, @@ -678,7 +685,7 @@ static void bcl_periph_mode_set(enum bcl_device_mode mode) } gbcl->btm_mode = BCL_VPH_MONITOR_MODE; } else { - power_supply_unregister(bcl_psy); + power_supply_unreg_notifier(&gbcl->psy_nb); ret = msm_bcl_disable(); if (ret) { pr_err("Error disabling BCL\n"); @@ -1627,19 +1634,6 @@ btm_probe_exit: return ret; } -static int bcl_battery_get_property(struct power_supply *psy, - enum power_supply_property prop, - union power_supply_propval *val) -{ - return 0; -} -static int bcl_battery_set_property(struct power_supply *psy, - enum power_supply_property prop, - const union power_supply_propval *val) -{ - return 0; -} - static uint32_t get_mask_from_core_handle(struct platform_device *pdev, const char *key) { @@ -1725,12 +1719,8 @@ static int bcl_probe(struct platform_device *pdev) pr_err("Cannot create bcl sysfs\n"); return ret; } - bcl_psy_des.name = bcl_psy_name; - bcl_psy_des.type = POWER_SUPPLY_TYPE_BMS; - bcl_psy_des.get_property = bcl_battery_get_property; - bcl_psy_des.set_property = bcl_battery_set_property; - bcl_psy_des.num_properties = 0; - bcl_psy_des.external_power_changed = power_supply_callback; + INIT_WORK(&bcl->soc_mitig_work, soc_mitigate); + bcl->psy_nb.notifier_call = power_supply_callback; bcl->bcl_hotplug_wq = alloc_workqueue("bcl_hotplug_wq", WQ_HIGHPRI, 0); if (!bcl->bcl_hotplug_wq) { pr_err("Workqueue alloc failed\n"); @@ -1773,6 +1763,7 @@ static int bcl_remove(struct platform_device *pdev) int cpu; /* De-register KTM handle */ + power_supply_unreg_notifier(&gbcl->psy_nb); if (gbcl->hotplug_handle) devmgr_unregister_mitigation_client(&pdev->dev, gbcl->hotplug_handle); diff --git a/drivers/power/qcom-charger/fg-core.h b/drivers/power/qcom-charger/fg-core.h index 4feaaa0e0c4e..515f31a44ce7 100644 --- a/drivers/power/qcom-charger/fg-core.h +++ b/drivers/power/qcom-charger/fg-core.h @@ -54,6 +54,8 @@ CHARS_PER_ITEM) + 1) \ #define FG_SRAM_ADDRESS_MAX 255 +#define BUCKET_COUNT 8 +#define BUCKET_SOC_PCT (256 / BUCKET_COUNT) /* Debug flag definitions */ enum fg_debug_flag { @@ -186,6 +188,15 @@ struct fg_batt_props { int batt_id_kohm; }; +struct fg_cyc_ctr_data { + bool en; + bool started[BUCKET_COUNT]; + u16 count[BUCKET_COUNT]; + u8 last_soc[BUCKET_COUNT]; + int id; + struct mutex lock; +}; + struct fg_irq_info { const char *name; const irq_handler_t handler; @@ -209,6 +220,7 @@ struct fg_chip { char *batt_profile; struct fg_dt_props dt; struct fg_batt_props bp; + struct fg_cyc_ctr_data cyc_ctr; struct notifier_block nb; struct mutex bus_lock; struct mutex sram_rw_lock; @@ -216,6 +228,8 @@ struct fg_chip { u32 batt_info_base; u32 mem_if_base; int nom_cap_uah; + int status; + int prev_status; bool batt_id_avail; bool profile_loaded; bool battery_missing; @@ -223,6 +237,7 @@ struct fg_chip { struct completion soc_ready; struct delayed_work profile_load_work; struct work_struct status_change_work; + struct work_struct cycle_count_work; struct fg_alg_flag *alg_flags; }; diff --git a/drivers/power/qcom-charger/qpnp-fg-gen3.c b/drivers/power/qcom-charger/qpnp-fg-gen3.c index a163061156ba..7739952f3254 100644 --- a/drivers/power/qcom-charger/qpnp-fg-gen3.c +++ b/drivers/power/qcom-charger/qpnp-fg-gen3.c @@ -57,6 +57,8 @@ #define PROFILE_LOAD_OFFSET 0 #define NOM_CAP_WORD 58 #define NOM_CAP_OFFSET 0 +#define CYCLE_COUNT_WORD 75 +#define CYCLE_COUNT_OFFSET 0 #define PROFILE_INTEGRITY_WORD 79 #define PROFILE_INTEGRITY_OFFSET 3 #define BATT_SOC_WORD 91 @@ -92,6 +94,8 @@ 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 void fg_encode_voltage(struct fg_sram_param *sp, enum fg_sram_param_id id, int val, u8 *buf); static void fg_encode_current(struct fg_sram_param *sp, @@ -114,7 +118,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_default), + fg_decode_batt_soc), PARAM(VOLTAGE_PRED, VOLTAGE_PRED_WORD, VOLTAGE_PRED_OFFSET, 2, 244141, 1000, 0, NULL, fg_decode_value_16b), PARAM(OCV, OCV_WORD, OCV_OFFSET, 2, 244141, 1000, 0, NULL, @@ -152,7 +156,7 @@ static struct fg_sram_param pmicobalt_v1_sram_params[] = { 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_default), + fg_decode_batt_soc), PARAM(VOLTAGE_PRED, VOLTAGE_PRED_WORD, VOLTAGE_PRED_OFFSET, 2, 244141, 1000, 0, NULL, fg_decode_value_16b), PARAM(OCV, OCV_WORD, OCV_OFFSET, 2, 244141, 1000, 0, NULL, @@ -260,171 +264,6 @@ module_param_named( sram_update_period_ms, fg_sram_update_period_ms, int, S_IRUSR | S_IWUSR ); -/* Other functions HERE */ - -static int fg_awake_cb(struct votable *votable, void *data, int awake, - const char *client) -{ - struct fg_chip *chip = data; - - if (awake) - pm_stay_awake(chip->dev); - else - pm_relax(chip->dev); - - pr_debug("client: %s awake: %d\n", client, awake); - return 0; -} - -static bool is_charger_available(struct fg_chip *chip) -{ - if (!chip->batt_psy) - chip->batt_psy = power_supply_get_by_name("battery"); - - if (!chip->batt_psy) - return false; - - return true; -} - -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, }; - - if (!is_charger_available(chip)) { - fg_dbg(chip, FG_STATUS, "Charger not available?!\n"); - return; - } - - power_supply_get_property(chip->batt_psy, POWER_SUPPLY_PROP_STATUS, - &prop); - switch (prop.intval) { - case POWER_SUPPLY_STATUS_CHARGING: - fg_dbg(chip, FG_POWER_SUPPLY, "Charging\n"); - break; - case POWER_SUPPLY_STATUS_DISCHARGING: - fg_dbg(chip, FG_POWER_SUPPLY, "Discharging\n"); - break; - case POWER_SUPPLY_STATUS_FULL: - fg_dbg(chip, FG_POWER_SUPPLY, "Full\n"); - break; - default: - break; - } -} - -#define PROFILE_LEN 224 -#define PROFILE_COMP_LEN 32 -#define SOC_READY_WAIT_MS 2000 -static void profile_load_work(struct work_struct *work) -{ - struct fg_chip *chip = container_of(work, - struct fg_chip, - profile_load_work.work); - int rc; - u8 buf[PROFILE_COMP_LEN], val; - bool tried_again = false, profiles_same = false; - - if (!chip->batt_id_avail) { - pr_err("batt_id not available\n"); - return; - } - - rc = fg_sram_read(chip, PROFILE_INTEGRITY_WORD, - PROFILE_INTEGRITY_OFFSET, &val, 1, FG_IMA_DEFAULT); - if (rc < 0) { - pr_err("failed to read profile integrity rc=%d\n", rc); - return; - } - - vote(chip->awake_votable, PROFILE_LOAD, true, 0); - if (val == 0x01) { - fg_dbg(chip, FG_STATUS, "Battery profile integrity bit is set\n"); - rc = fg_sram_read(chip, PROFILE_LOAD_WORD, PROFILE_LOAD_OFFSET, - buf, PROFILE_COMP_LEN, FG_IMA_DEFAULT); - if (rc < 0) { - pr_err("Error in reading battery profile, rc:%d\n", rc); - goto out; - } - profiles_same = memcmp(chip->batt_profile, buf, - PROFILE_COMP_LEN) == 0; - if (profiles_same) { - fg_dbg(chip, FG_STATUS, "Battery profile is same\n"); - goto done; - } - fg_dbg(chip, FG_STATUS, "profiles are different?\n"); - } - - fg_dbg(chip, FG_STATUS, "profile loading started\n"); - rc = fg_masked_write(chip, BATT_SOC_RESTART(chip), RESTART_GO_BIT, 0); - if (rc < 0) { - pr_err("Error in writing to %04x, rc=%d\n", - BATT_SOC_RESTART(chip), rc); - goto out; - } - - /* load battery profile */ - rc = fg_sram_write(chip, PROFILE_LOAD_WORD, PROFILE_LOAD_OFFSET, - chip->batt_profile, PROFILE_LEN, FG_IMA_ATOMIC); - if (rc < 0) { - pr_err("Error in writing battery profile, rc:%d\n", rc); - goto out; - } - - rc = fg_masked_write(chip, BATT_SOC_RESTART(chip), RESTART_GO_BIT, - RESTART_GO_BIT); - if (rc < 0) { - pr_err("Error in writing to %04x, rc=%d\n", - BATT_SOC_RESTART(chip), rc); - goto out; - } - -wait: - rc = wait_for_completion_interruptible_timeout(&chip->soc_ready, - msecs_to_jiffies(SOC_READY_WAIT_MS)); - - /* If we were interrupted wait again one more time. */ - if (rc == -ERESTARTSYS && !tried_again) { - tried_again = true; - goto wait; - } else if (rc <= 0) { - pr_err("wait for soc_ready timed out rc=%d\n", rc); - goto out; - } - - fg_dbg(chip, FG_STATUS, "SOC is ready\n"); - - /* Set the profile integrity bit */ - val = 0x1; - rc = fg_sram_write(chip, PROFILE_INTEGRITY_WORD, - PROFILE_INTEGRITY_OFFSET, &val, 1, FG_IMA_DEFAULT); - if (rc < 0) { - pr_err("failed to write profile integrity rc=%d\n", rc); - goto out; - } - - fg_dbg(chip, FG_STATUS, "profile loaded successfully"); -done: - rc = fg_sram_read(chip, NOM_CAP_WORD, NOM_CAP_OFFSET, buf, 2, - FG_IMA_DEFAULT); - if (rc < 0) { - pr_err("Error in reading %04x[%d] rc=%d\n", NOM_CAP_WORD, - NOM_CAP_OFFSET, rc); - goto out; - } - - chip->nom_cap_uah = (int)(buf[0] | buf[1] << 8) * 1000; - chip->profile_loaded = true; -out: - vote(chip->awake_votable, PROFILE_LOAD, false, 0); - rc = fg_masked_write(chip, BATT_SOC_RESTART(chip), RESTART_GO_BIT, 0); - if (rc < 0) - pr_err("Error in writing to %04x, rc=%d\n", - BATT_SOC_RESTART(chip), rc); -} - /* All getters HERE */ static int fg_decode_value_16b(struct fg_sram_param *sp, @@ -436,9 +275,18 @@ static int fg_decode_value_16b(struct fg_sram_param *sp, return sp[id].value; } -static int fg_decode_default(struct fg_sram_param *sp, +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; } @@ -752,6 +600,7 @@ static int fg_get_batt_id(struct fg_chip *chip, int *val) return 0; } +#define PROFILE_LEN 224 static int fg_get_batt_profile(struct fg_chip *chip) { struct device_node *node = chip->dev->of_node; @@ -863,6 +712,316 @@ static int fg_set_esr_timer(struct fg_chip *chip, int cycles, bool charging, return 0; } +/* Other functions HERE */ + +static int fg_awake_cb(struct votable *votable, void *data, int awake, + const char *client) +{ + struct fg_chip *chip = data; + + if (awake) + pm_stay_awake(chip->dev); + else + pm_relax(chip->dev); + + pr_debug("client: %s awake: %d\n", client, awake); + return 0; +} + +static bool is_charger_available(struct fg_chip *chip) +{ + if (!chip->batt_psy) + chip->batt_psy = power_supply_get_by_name("battery"); + + if (!chip->batt_psy) + return false; + + return true; +} + +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, }; + + if (!is_charger_available(chip)) { + fg_dbg(chip, FG_STATUS, "Charger not available?!\n"); + return; + } + + power_supply_get_property(chip->batt_psy, POWER_SUPPLY_PROP_STATUS, + &prop); + chip->prev_status = chip->status; + chip->status = prop.intval; + + if (chip->cyc_ctr.en && chip->prev_status != chip->status) + schedule_work(&chip->cycle_count_work); + + switch (prop.intval) { + case POWER_SUPPLY_STATUS_CHARGING: + fg_dbg(chip, FG_POWER_SUPPLY, "Charging\n"); + break; + case POWER_SUPPLY_STATUS_DISCHARGING: + fg_dbg(chip, FG_POWER_SUPPLY, "Discharging\n"); + break; + case POWER_SUPPLY_STATUS_FULL: + fg_dbg(chip, FG_POWER_SUPPLY, "Full\n"); + break; + default: + break; + } +} + +static void restore_cycle_counter(struct fg_chip *chip) +{ + int rc = 0, i; + u8 data[2]; + + mutex_lock(&chip->cyc_ctr.lock); + for (i = 0; i < BUCKET_COUNT; i++) { + rc = fg_sram_read(chip, CYCLE_COUNT_WORD + (i / 2), + CYCLE_COUNT_OFFSET + (i % 2) * 2, data, 2, + FG_IMA_DEFAULT); + if (rc < 0) + pr_err("failed to read bucket %d rc=%d\n", i, rc); + else + chip->cyc_ctr.count[i] = data[0] | data[1] << 8; + } + mutex_unlock(&chip->cyc_ctr.lock); +} + +static void clear_cycle_counter(struct fg_chip *chip) +{ + int rc = 0, i; + + if (!chip->cyc_ctr.en) + return; + + mutex_lock(&chip->cyc_ctr.lock); + memset(chip->cyc_ctr.count, 0, sizeof(chip->cyc_ctr.count)); + for (i = 0; i < BUCKET_COUNT; i++) { + chip->cyc_ctr.started[i] = false; + chip->cyc_ctr.last_soc[i] = 0; + } + rc = fg_sram_write(chip, CYCLE_COUNT_WORD, CYCLE_COUNT_OFFSET, + (u8 *)&chip->cyc_ctr.count, + sizeof(chip->cyc_ctr.count) / sizeof(u8 *), + FG_IMA_DEFAULT); + if (rc < 0) + pr_err("failed to clear cycle counter rc=%d\n", rc); + + mutex_unlock(&chip->cyc_ctr.lock); +} + +static int fg_inc_store_cycle_ctr(struct fg_chip *chip, int bucket) +{ + int rc = 0; + u16 cyc_count; + u8 data[2]; + + if (bucket < 0 || (bucket > BUCKET_COUNT - 1)) + return 0; + + cyc_count = chip->cyc_ctr.count[bucket]; + cyc_count++; + data[0] = cyc_count & 0xFF; + data[1] = cyc_count >> 8; + + rc = fg_sram_write(chip, CYCLE_COUNT_WORD + (bucket / 2), + CYCLE_COUNT_OFFSET + (bucket % 2) * 2, data, 2, + FG_IMA_DEFAULT); + if (rc < 0) + pr_err("failed to write BATT_CYCLE[%d] rc=%d\n", + bucket, rc); + else + chip->cyc_ctr.count[bucket] = cyc_count; + return rc; +} + +static void cycle_count_work(struct work_struct *work) +{ + int rc = 0, bucket, i, batt_soc; + struct fg_chip *chip = container_of(work, + struct fg_chip, + cycle_count_work); + + mutex_lock(&chip->cyc_ctr.lock); + rc = fg_get_sram_prop(chip, FG_SRAM_BATT_SOC, &batt_soc); + if (rc < 0) { + pr_err("Failed to read battery soc rc: %d\n", rc); + goto out; + } + + if (chip->status == POWER_SUPPLY_STATUS_CHARGING) { + /* Find out which bucket the SOC falls in */ + bucket = batt_soc / BUCKET_SOC_PCT; + pr_debug("batt_soc: %d bucket: %d\n", batt_soc, bucket); + + /* + * If we've started counting for the previous bucket, + * then store the counter for that bucket if the + * counter for current bucket is getting started. + */ + if (bucket > 0 && chip->cyc_ctr.started[bucket - 1] && + !chip->cyc_ctr.started[bucket]) { + rc = fg_inc_store_cycle_ctr(chip, bucket - 1); + if (rc < 0) { + pr_err("Error in storing cycle_ctr rc: %d\n", + rc); + goto out; + } else { + chip->cyc_ctr.started[bucket - 1] = false; + chip->cyc_ctr.last_soc[bucket - 1] = 0; + } + } + if (!chip->cyc_ctr.started[bucket]) { + chip->cyc_ctr.started[bucket] = true; + chip->cyc_ctr.last_soc[bucket] = batt_soc; + } + } else { + for (i = 0; i < BUCKET_COUNT; i++) { + if (chip->cyc_ctr.started[i] && + batt_soc > chip->cyc_ctr.last_soc[i]) { + rc = fg_inc_store_cycle_ctr(chip, i); + if (rc < 0) + pr_err("Error in storing cycle_ctr rc: %d\n", + rc); + chip->cyc_ctr.last_soc[i] = 0; + } + chip->cyc_ctr.started[i] = false; + } + } +out: + mutex_unlock(&chip->cyc_ctr.lock); +} + +static int fg_get_cycle_count(struct fg_chip *chip) +{ + int count; + + if (!chip->cyc_ctr.en) + return 0; + + if ((chip->cyc_ctr.id <= 0) || (chip->cyc_ctr.id > BUCKET_COUNT)) + return -EINVAL; + + mutex_lock(&chip->cyc_ctr.lock); + count = chip->cyc_ctr.count[chip->cyc_ctr.id - 1]; + mutex_unlock(&chip->cyc_ctr.lock); + return count; +} + +#define PROFILE_COMP_LEN 32 +#define SOC_READY_WAIT_MS 2000 +static void profile_load_work(struct work_struct *work) +{ + struct fg_chip *chip = container_of(work, + struct fg_chip, + profile_load_work.work); + int rc; + u8 buf[PROFILE_COMP_LEN], val; + bool tried_again = false, profiles_same = false; + + if (!chip->batt_id_avail) { + pr_err("batt_id not available\n"); + return; + } + + rc = fg_sram_read(chip, PROFILE_INTEGRITY_WORD, + PROFILE_INTEGRITY_OFFSET, &val, 1, FG_IMA_DEFAULT); + if (rc < 0) { + pr_err("failed to read profile integrity rc=%d\n", rc); + return; + } + + vote(chip->awake_votable, PROFILE_LOAD, true, 0); + if (val == 0x01) { + fg_dbg(chip, FG_STATUS, "Battery profile integrity bit is set\n"); + rc = fg_sram_read(chip, PROFILE_LOAD_WORD, PROFILE_LOAD_OFFSET, + buf, PROFILE_COMP_LEN, FG_IMA_DEFAULT); + if (rc < 0) { + pr_err("Error in reading battery profile, rc:%d\n", rc); + goto out; + } + profiles_same = memcmp(chip->batt_profile, buf, + PROFILE_COMP_LEN) == 0; + if (profiles_same) { + fg_dbg(chip, FG_STATUS, "Battery profile is same\n"); + goto done; + } + fg_dbg(chip, FG_STATUS, "profiles are different?\n"); + } + + clear_cycle_counter(chip); + fg_dbg(chip, FG_STATUS, "profile loading started\n"); + rc = fg_masked_write(chip, BATT_SOC_RESTART(chip), RESTART_GO_BIT, 0); + if (rc < 0) { + pr_err("Error in writing to %04x, rc=%d\n", + BATT_SOC_RESTART(chip), rc); + goto out; + } + + /* load battery profile */ + rc = fg_sram_write(chip, PROFILE_LOAD_WORD, PROFILE_LOAD_OFFSET, + chip->batt_profile, PROFILE_LEN, FG_IMA_ATOMIC); + if (rc < 0) { + pr_err("Error in writing battery profile, rc:%d\n", rc); + goto out; + } + + rc = fg_masked_write(chip, BATT_SOC_RESTART(chip), RESTART_GO_BIT, + RESTART_GO_BIT); + if (rc < 0) { + pr_err("Error in writing to %04x, rc=%d\n", + BATT_SOC_RESTART(chip), rc); + goto out; + } + +wait: + rc = wait_for_completion_interruptible_timeout(&chip->soc_ready, + msecs_to_jiffies(SOC_READY_WAIT_MS)); + + /* If we were interrupted wait again one more time. */ + if (rc == -ERESTARTSYS && !tried_again) { + tried_again = true; + goto wait; + } else if (rc <= 0) { + pr_err("wait for soc_ready timed out rc=%d\n", rc); + goto out; + } + + fg_dbg(chip, FG_STATUS, "SOC is ready\n"); + + /* Set the profile integrity bit */ + val = 0x1; + rc = fg_sram_write(chip, PROFILE_INTEGRITY_WORD, + PROFILE_INTEGRITY_OFFSET, &val, 1, FG_IMA_DEFAULT); + if (rc < 0) { + pr_err("failed to write profile integrity rc=%d\n", rc); + goto out; + } + + fg_dbg(chip, FG_STATUS, "profile loaded successfully"); +done: + rc = fg_sram_read(chip, NOM_CAP_WORD, NOM_CAP_OFFSET, buf, 2, + FG_IMA_DEFAULT); + if (rc < 0) { + pr_err("Error in reading %04x[%d] rc=%d\n", NOM_CAP_WORD, + NOM_CAP_OFFSET, rc); + goto out; + } + + chip->nom_cap_uah = (int)(buf[0] | buf[1] << 8) * 1000; + chip->profile_loaded = true; +out: + vote(chip->awake_votable, PROFILE_LOAD, false, 0); + rc = fg_masked_write(chip, BATT_SOC_RESTART(chip), RESTART_GO_BIT, 0); + if (rc < 0) + pr_err("Error in writing to %04x, rc=%d\n", + BATT_SOC_RESTART(chip), rc); +} + /* PSY CALLBACKS STAY HERE */ static int fg_psy_get_property(struct power_supply *psy, @@ -902,6 +1061,11 @@ static int fg_psy_get_property(struct power_supply *psy, break; case POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN: pval->intval = chip->bp.float_volt_uv; + case POWER_SUPPLY_PROP_CYCLE_COUNT: + pval->intval = fg_get_cycle_count(chip); + break; + case POWER_SUPPLY_PROP_CYCLE_COUNT_ID: + pval->intval = chip->cyc_ctr.id; break; default: break; @@ -914,7 +1078,18 @@ static int fg_psy_set_property(struct power_supply *psy, enum power_supply_property psp, const union power_supply_propval *pval) { + struct fg_chip *chip = power_supply_get_drvdata(psy); + switch (psp) { + case POWER_SUPPLY_PROP_CYCLE_COUNT_ID: + if ((pval->intval > 0) && (pval->intval <= BUCKET_COUNT)) { + chip->cyc_ctr.id = pval->intval; + } else { + pr_err("rejecting invalid cycle_count_id = %d\n", + pval->intval); + return -EINVAL; + } + break; default: break; } @@ -926,6 +1101,8 @@ static int fg_property_is_writeable(struct power_supply *psy, enum power_supply_property psp) { switch (psp) { + case POWER_SUPPLY_PROP_CYCLE_COUNT_ID: + return 1; default: break; } @@ -965,6 +1142,8 @@ static enum power_supply_property fg_psy_props[] = { POWER_SUPPLY_PROP_BATTERY_TYPE, POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN, POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN, + POWER_SUPPLY_PROP_CYCLE_COUNT, + POWER_SUPPLY_PROP_CYCLE_COUNT_ID, }; static const struct power_supply_desc fg_psy_desc = { @@ -1121,6 +1300,9 @@ static int fg_hw_init(struct fg_chip *chip) } } + if (chip->cyc_ctr.en) + restore_cycle_counter(chip); + return 0; } @@ -1180,6 +1362,7 @@ static irqreturn_t fg_batt_missing_irq_handler(int irq, void *data) if (chip->battery_missing) { chip->batt_id_avail = false; chip->profile_loaded = false; + clear_cycle_counter(chip); } else { rc = fg_batt_profile_init(chip); if (rc < 0) { @@ -1222,6 +1405,12 @@ static irqreturn_t fg_delta_soc_irq_handler(int irq, void *data) { struct fg_chip *chip = data; + if (chip->cyc_ctr.en) + schedule_work(&chip->cycle_count_work); + + if (is_charger_available(chip)) + power_supply_changed(chip->batt_psy); + fg_dbg(chip, FG_IRQ, "irq %d triggered\n", irq); return IRQ_HANDLED; } @@ -1230,6 +1419,9 @@ static irqreturn_t fg_empty_soc_irq_handler(int irq, void *data) { struct fg_chip *chip = data; + if (is_charger_available(chip)) + power_supply_changed(chip->batt_psy); + fg_dbg(chip, FG_IRQ, "irq %d triggered\n", irq); return IRQ_HANDLED; } @@ -1528,6 +1720,9 @@ static int fg_parse_dt(struct fg_chip *chip) else chip->dt.esr_timer_asleep = temp; + chip->cyc_ctr.en = of_property_read_bool(node, "qcom,cycle-counter-en"); + if (chip->cyc_ctr.en) + chip->cyc_ctr.id = 1; return 0; } @@ -1580,10 +1775,12 @@ static int fg_gen3_probe(struct platform_device *pdev) mutex_init(&chip->bus_lock); mutex_init(&chip->sram_rw_lock); + mutex_init(&chip->cyc_ctr.lock); init_completion(&chip->soc_update); init_completion(&chip->soc_ready); INIT_DELAYED_WORK(&chip->profile_load_work, profile_load_work); INIT_WORK(&chip->status_change_work, status_change_work); + INIT_WORK(&chip->cycle_count_work, cycle_count_work); rc = fg_memif_init(chip); if (rc < 0) { diff --git a/drivers/power/qcom-charger/qpnp-smb2.c b/drivers/power/qcom-charger/qpnp-smb2.c index d19e7827ed83..57f31d8c58e7 100644 --- a/drivers/power/qcom-charger/qpnp-smb2.c +++ b/drivers/power/qcom-charger/qpnp-smb2.c @@ -18,11 +18,13 @@ #include <linux/interrupt.h> #include <linux/of.h> #include <linux/of_irq.h> +#include <linux/qpnp/qpnp-revid.h> #include <linux/regulator/driver.h> #include <linux/regulator/of_regulator.h> #include <linux/regulator/machine.h> #include "smb-reg.h" #include "smb-lib.h" +#include "storm-watch.h" #include "pmic-voter.h" #define SMB2_DEFAULT_WPWR_UW 8000000 @@ -205,6 +207,7 @@ struct smb_dt_props { int wipower_max_uw; u32 step_soc_threshold[STEP_CHARGING_MAX_STEPS - 1]; s32 step_cc_delta[STEP_CHARGING_MAX_STEPS]; + struct device_node *revid_dev_node; }; struct smb2 { @@ -600,6 +603,8 @@ static enum power_supply_property smb2_batt_props[] = { POWER_SUPPLY_PROP_CURRENT_NOW, POWER_SUPPLY_PROP_TEMP, POWER_SUPPLY_PROP_TECHNOLOGY, + POWER_SUPPLY_PROP_STEP_CHARGING_ENABLED, + POWER_SUPPLY_PROP_STEP_CHARGING_STEP, }; static int smb2_batt_get_prop(struct power_supply *psy, @@ -639,6 +644,11 @@ static int smb2_batt_get_prop(struct power_supply *psy, break; case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMITED: rc = smblib_get_prop_input_current_limited(chg, val); + case POWER_SUPPLY_PROP_STEP_CHARGING_ENABLED: + val->intval = chg->step_chg_enabled; + break; + case POWER_SUPPLY_PROP_STEP_CHARGING_STEP: + rc = smblib_get_prop_step_chg_step(chg, val); break; case POWER_SUPPLY_PROP_VOLTAGE_NOW: rc = smblib_get_prop_batt_voltage_now(chg, val); @@ -1085,6 +1095,40 @@ static int smb2_init_hw(struct smb2 *chip) return rc; } +static int smb2_setup_wa_flags(struct smb2 *chip) +{ + struct pmic_revid_data *pmic_rev_id; + struct device_node *revid_dev_node; + + revid_dev_node = of_parse_phandle(chip->chg.dev->of_node, + "qcom,pmic-revid", 0); + if (!revid_dev_node) { + pr_err("Missing qcom,pmic-revid property\n"); + return -EINVAL; + } + + pmic_rev_id = get_revid_data(revid_dev_node); + if (IS_ERR_OR_NULL(pmic_rev_id)) { + /* + * the revid peripheral must be registered, any failure + * here only indicates that the rev-id module has not + * probed yet. + */ + return -EPROBE_DEFER; + } + + switch (pmic_rev_id->pmic_subtype) { + case PMICOBALT_SUBTYPE: + break; + default: + pr_err("PMIC subtype %d not supported\n", + pmic_rev_id->pmic_subtype); + return -EINVAL; + } + + return 0; +} + /**************************** * DETERMINE INITIAL STATUS * ****************************/ @@ -1109,60 +1153,181 @@ static int smb2_determine_initial_status(struct smb2 *chip) **************************/ struct smb2_irq_info { - const char *name; - const irq_handler_t handler; - const bool wake; - int irq; + const char *name; + const irq_handler_t handler; + const bool wake; + const struct storm_watch storm_data; + int irq; }; static struct smb2_irq_info smb2_irqs[] = { /* CHARGER IRQs */ - { "chg-error", smblib_handle_debug }, - { "chg-state-change", smblib_handle_chg_state_change, true }, - { "step-chg-state-change", smblib_handle_step_chg_state_change, - true }, - { "step-chg-soc-update-fail", smblib_handle_step_chg_soc_update_fail, - true }, - { "step-chg-soc-update-request", - smblib_handle_step_chg_soc_update_request, true }, + { + .name = "chg-error", + .handler = smblib_handle_debug, + }, + { + .name = "chg-state-change", + .handler = smblib_handle_chg_state_change, + .wake = true, + }, + { + .name = "step-chg-state-change", + .handler = smblib_handle_step_chg_state_change, + .wake = true, + }, + { + .name = "step-chg-soc-update-fail", + .handler = smblib_handle_step_chg_soc_update_fail, + .wake = true, + }, + { + .name = "step-chg-soc-update-request", + .handler = smblib_handle_step_chg_soc_update_request, + .wake = true, + }, /* OTG IRQs */ - { "otg-fail", smblib_handle_debug }, - { "otg-overcurrent", smblib_handle_debug }, - { "otg-oc-dis-sw-sts", smblib_handle_debug }, - { "testmode-change-detect", smblib_handle_debug }, + { + .name = "otg-fail", + .handler = smblib_handle_debug, + }, + { + .name = "otg-overcurrent", + .handler = smblib_handle_debug, + }, + { + .name = "otg-oc-dis-sw-sts", + .handler = smblib_handle_debug, + }, + { + .name = "testmode-change-detect", + .handler = smblib_handle_debug, + }, /* BATTERY IRQs */ - { "bat-temp", smblib_handle_batt_temp_changed }, - { "bat-ocp", smblib_handle_batt_psy_changed }, - { "bat-ov", smblib_handle_batt_psy_changed }, - { "bat-low", smblib_handle_batt_psy_changed }, - { "bat-therm-or-id-missing", smblib_handle_batt_psy_changed }, - { "bat-terminal-missing", smblib_handle_batt_psy_changed }, + { + .name = "bat-temp", + .handler = smblib_handle_batt_temp_changed, + }, + { + .name = "bat-ocp", + .handler = smblib_handle_batt_psy_changed, + }, + { + .name = "bat-ov", + .handler = smblib_handle_batt_psy_changed, + }, + { + .name = "bat-low", + .handler = smblib_handle_batt_psy_changed, + }, + { + .name = "bat-therm-or-id-missing", + .handler = smblib_handle_batt_psy_changed, + }, + { + .name = "bat-terminal-missing", + .handler = smblib_handle_batt_psy_changed, + }, /* USB INPUT IRQs */ - { "usbin-collapse", smblib_handle_debug }, - { "usbin-lt-3p6v", smblib_handle_debug }, - { "usbin-uv", smblib_handle_debug }, - { "usbin-ov", smblib_handle_debug }, - { "usbin-plugin", smblib_handle_usb_plugin, true }, - { "usbin-src-change", smblib_handle_usb_source_change, true }, - { "usbin-icl-change", smblib_handle_icl_change, true }, - { "type-c-change", smblib_handle_usb_typec_change, true }, + { + .name = "usbin-collapse", + .handler = smblib_handle_debug, + }, + { + .name = "usbin-lt-3p6v", + .handler = smblib_handle_debug, + }, + { + .name = "usbin-uv", + .handler = smblib_handle_debug, + }, + { + .name = "usbin-ov", + .handler = smblib_handle_debug, + }, + { + .name = "usbin-plugin", + .handler = smblib_handle_usb_plugin, + .wake = true, + }, + { + .name = "usbin-src-change", + .handler = smblib_handle_usb_source_change, + .wake = true, + }, + { + .name = "usbin-icl-change", + .handler = smblib_handle_icl_change, + .wake = true, + }, + { + .name = "type-c-change", + .handler = smblib_handle_usb_typec_change, + .wake = true, + }, /* DC INPUT IRQs */ - { "dcin-collapse", smblib_handle_debug }, - { "dcin-lt-3p6v", smblib_handle_debug }, - { "dcin-uv", smblib_handle_debug }, - { "dcin-ov", smblib_handle_debug }, - { "dcin-plugin", smblib_handle_debug }, - { "div2-en-dg", smblib_handle_debug }, - { "dcin-icl-change", smblib_handle_debug }, + { + .name = "dcin-collapse", + .handler = smblib_handle_debug, + }, + { + .name = "dcin-lt-3p6v", + .handler = smblib_handle_debug, + }, + { + .name = "dcin-uv", + .handler = smblib_handle_debug, + }, + { + .name = "dcin-ov", + .handler = smblib_handle_debug, + }, + { + .name = "dcin-plugin", + .handler = smblib_handle_debug, + }, + { + .name = "div2-en-dg", + .handler = smblib_handle_debug, + }, + { + .name = "dcin-icl-change", + .handler = smblib_handle_debug, + }, /* MISCELLANEOUS IRQs */ - { "wdog-snarl", NULL }, - { "wdog-bark", NULL }, - { "aicl-fail", smblib_handle_debug }, - { "aicl-done", smblib_handle_debug }, - { "high-duty-cycle", smblib_handle_high_duty_cycle, true }, - { "input-current-limiting", smblib_handle_debug }, - { "temperature-change", smblib_handle_debug }, - { "switcher-power-ok", smblib_handle_debug }, + { + .name = "wdog-snarl", + .handler = NULL, + }, + { + .name = "wdog-bark", + .handler = NULL, + }, + { + .name = "aicl-fail", + .handler = smblib_handle_debug, + }, + { + .name = "aicl-done", + .handler = smblib_handle_debug, + }, + { + .name = "high-duty-cycle", + .handler = smblib_handle_high_duty_cycle, + .wake = true, + }, + { + .name = "input-current-limiting", + .handler = smblib_handle_debug, + }, + { + .name = "temperature-change", + .handler = smblib_handle_debug, + }, + { + .name = "switcher-power-ok", + .handler = smblib_handle_debug, + }, }; static int smb2_get_irq_index_byname(const char *irq_name) @@ -1205,6 +1370,7 @@ static int smb2_request_interrupt(struct smb2 *chip, irq_data->parent_data = chip; irq_data->name = irq_name; + irq_data->storm_data = smb2_irqs[irq_index].storm_data; rc = devm_request_threaded_irq(chg->dev, irq, NULL, smb2_irqs[irq_index].handler, @@ -1270,6 +1436,13 @@ static int smb2_probe(struct platform_device *pdev) return -EINVAL; } + rc = smb2_setup_wa_flags(chip); + if (rc < 0) { + if (rc != -EPROBE_DEFER) + pr_err("Couldn't setup wa flags rc=%d\n", rc); + return rc; + } + rc = smblib_init(chg); if (rc < 0) { pr_err("Smblib_init failed rc=%d\n", rc); diff --git a/drivers/power/qcom-charger/smb-lib.c b/drivers/power/qcom-charger/smb-lib.c index ed24e12e313d..e93d03788f11 100644 --- a/drivers/power/qcom-charger/smb-lib.c +++ b/drivers/power/qcom-charger/smb-lib.c @@ -18,6 +18,7 @@ #include <linux/irq.h> #include "smb-lib.h" #include "smb-reg.h" +#include "storm-watch.h" #include "pmic-voter.h" #define smblib_dbg(chg, reason, fmt, ...) \ @@ -102,7 +103,8 @@ static int smblib_get_step_charging_adjustment(struct smb_charger *chg, return rc; } - step_state = (stat & STEP_CHARGING_STATUS_MASK) >> 3; + step_state = (stat & STEP_CHARGING_STATUS_MASK) >> + STEP_CHARGING_STATUS_SHIFT; rc = smblib_get_charge_param(chg, &chg->param.step_cc_delta[step_state], cc_offset); @@ -779,13 +781,24 @@ int smblib_vbus_regulator_is_enabled(struct regulator_dev *rdev) int smblib_vconn_regulator_enable(struct regulator_dev *rdev) { struct smb_charger *chg = rdev_get_drvdata(rdev); + u8 stat; int rc = 0; + /* + * VCONN_EN_ORIENTATION is overloaded with overriding the CC pin used + * for Vconn, and it should be set with reverse polarity of CC_OUT. + */ + rc = smblib_read(chg, TYPE_C_STATUS_4_REG, &stat); + if (rc < 0) { + dev_err(chg->dev, "Couldn't read TYPE_C_STATUS_4 rc=%d\n", rc); + return rc; + } + stat = stat & CC_ORIENTATION_BIT ? 0 : VCONN_EN_ORIENTATION_BIT; rc = smblib_masked_write(chg, TYPE_C_INTRPT_ENB_SOFTWARE_CTRL_REG, - VCONN_EN_VALUE_BIT, VCONN_EN_VALUE_BIT); + VCONN_EN_VALUE_BIT | VCONN_EN_ORIENTATION_BIT, + VCONN_EN_VALUE_BIT | stat); if (rc < 0) - dev_err(chg->dev, "Couldn't enable vconn regulator rc=%d\n", - rc); + dev_err(chg->dev, "Couldn't enable vconn setting rc=%d\n", rc); return rc; } @@ -1042,6 +1055,30 @@ int smblib_get_prop_batt_temp(struct smb_charger *chg, return rc; } +int smblib_get_prop_step_chg_step(struct smb_charger *chg, + union power_supply_propval *val) +{ + int rc; + u8 stat; + + if (!chg->step_chg_enabled) { + val->intval = -1; + return 0; + } + + rc = smblib_read(chg, BATTERY_CHARGER_STATUS_1_REG, &stat); + if (rc < 0) { + dev_err(chg->dev, "Couldn't read BATTERY_CHARGER_STATUS_1 rc=%d\n", + rc); + return rc; + } + + val->intval = (stat & STEP_CHARGING_STATUS_MASK) >> + STEP_CHARGING_STATUS_SHIFT; + + return rc; +} + /*********************** * BATTERY PSY SETTERS * ***********************/ @@ -1565,6 +1602,7 @@ int smblib_set_prop_pd_active(struct smb_charger *chg, const union power_supply_propval *val) { int rc; + u8 stat; if (!get_effective_result(chg->pd_allowed_votable)) { dev_err(chg->dev, "PD is not allowed\n"); @@ -1582,6 +1620,40 @@ int smblib_set_prop_pd_active(struct smb_charger *chg, vote(chg->pd_allowed_votable, PD_VOTER, val->intval, 0); + /* + * VCONN_EN_ORIENTATION_BIT controls whether to use CC1 or CC2 line + * when TYPEC_SPARE_CFG_BIT (CC pin selection s/w override) is set + * or when VCONN_EN_VALUE_BIT is set. + */ + if (val->intval) { + rc = smblib_read(chg, TYPE_C_STATUS_4_REG, &stat); + if (rc < 0) { + dev_err(chg->dev, + "Couldn't read TYPE_C_STATUS_4 rc=%d\n", + rc); + return rc; + } + + stat &= CC_ORIENTATION_BIT; + rc = smblib_masked_write(chg, + TYPE_C_INTRPT_ENB_SOFTWARE_CTRL_REG, + VCONN_EN_ORIENTATION_BIT, + stat ? 0 : VCONN_EN_ORIENTATION_BIT); + if (rc < 0) + dev_err(chg->dev, + "Couldn't enable vconn on CC line rc=%d\n", rc); + } + + /* CC pin selection s/w override in PD session; h/w otherwise. */ + rc = smblib_masked_write(chg, TAPER_TIMER_SEL_CFG_REG, + TYPEC_SPARE_CFG_BIT, + val->intval ? TYPEC_SPARE_CFG_BIT : 0); + if (rc < 0) { + dev_err(chg->dev, "Couldn't change cc_out ctrl to %s rc=%d\n", + val->intval ? "SW" : "HW", rc); + return rc; + } + chg->pd_active = (bool)val->intval; smblib_update_usb_type(chg); return rc; @@ -1597,7 +1669,6 @@ irqreturn_t smblib_handle_debug(int irq, void *data) struct smb_charger *chg = irq_data->parent_data; smblib_dbg(chg, PR_INTERRUPT, "IRQ: %s\n", irq_data->name); - return IRQ_HANDLED; } @@ -1633,8 +1704,10 @@ irqreturn_t smblib_handle_chg_state_change(int irq, void *data) dev_err(chg->dev, "Couldn't get batt status type rc=%d\n", rc); return IRQ_HANDLED; } - if (pval.intval == POWER_SUPPLY_STATUS_FULL) + if (pval.intval == POWER_SUPPLY_STATUS_FULL) { + power_supply_changed(chg->batt_psy); vote(chg->pl_disable_votable, TAPER_END_VOTER, false, 0); + } return IRQ_HANDLED; } @@ -1705,7 +1778,7 @@ irqreturn_t smblib_handle_batt_psy_changed(int irq, void *data) struct smb_irq_data *irq_data = data; struct smb_charger *chg = irq_data->parent_data; - smblib_handle_debug(irq, data); + smblib_dbg(chg, PR_INTERRUPT, "IRQ: %s\n", irq_data->name); power_supply_changed(chg->batt_psy); return IRQ_HANDLED; } @@ -1715,7 +1788,7 @@ irqreturn_t smblib_handle_usb_psy_changed(int irq, void *data) struct smb_irq_data *irq_data = data; struct smb_charger *chg = irq_data->parent_data; - smblib_handle_debug(irq, data); + smblib_dbg(chg, PR_INTERRUPT, "IRQ: %s\n", irq_data->name); power_supply_changed(chg->usb_psy); return IRQ_HANDLED; } @@ -1768,6 +1841,7 @@ irqreturn_t smblib_handle_usb_plugin(int irq, void *data) } skip_dpdm_float: + power_supply_changed(chg->usb_psy); smblib_dbg(chg, PR_INTERRUPT, "IRQ: %s %s\n", irq_data->name, chg->vbus_present ? "attached" : "detached"); return IRQ_HANDLED; diff --git a/drivers/power/qcom-charger/smb-lib.h b/drivers/power/qcom-charger/smb-lib.h index 289fb1706e97..f5d9dda8330a 100644 --- a/drivers/power/qcom-charger/smb-lib.h +++ b/drivers/power/qcom-charger/smb-lib.h @@ -16,6 +16,7 @@ #include <linux/irqreturn.h> #include <linux/regulator/driver.h> #include <linux/regulator/consumer.h> +#include "storm-watch.h" enum print_reason { PR_INTERRUPT = BIT(0), @@ -46,8 +47,9 @@ struct smb_regulator { }; struct smb_irq_data { - void *parent_data; - const char *name; + void *parent_data; + const char *name; + struct storm_watch storm_data; }; struct smb_chg_param { @@ -161,6 +163,9 @@ struct smb_charger { bool step_chg_enabled; bool is_hdc; + + /* workaround flag */ + u32 wa_flags; }; int smblib_read(struct smb_charger *chg, u16 addr, u8 *val); @@ -228,6 +233,8 @@ int smblib_get_prop_batt_current_now(struct smb_charger *chg, union power_supply_propval *val); int smblib_get_prop_batt_temp(struct smb_charger *chg, union power_supply_propval *val); +int smblib_get_prop_step_chg_step(struct smb_charger *chg, + union power_supply_propval *val); int smblib_set_prop_input_suspend(struct smb_charger *chg, const union power_supply_propval *val); diff --git a/drivers/power/qcom-charger/smb-reg.h b/drivers/power/qcom-charger/smb-reg.h index 0d5222ec08f8..c88d132fbf70 100644 --- a/drivers/power/qcom-charger/smb-reg.h +++ b/drivers/power/qcom-charger/smb-reg.h @@ -32,6 +32,7 @@ #define BATTERY_CHARGER_STATUS_1_REG (CHGR_BASE + 0x06) #define BVR_INITIAL_RAMP_BIT BIT(7) #define CC_SOFT_TERMINATE_BIT BIT(6) +#define STEP_CHARGING_STATUS_SHIFT 3 #define STEP_CHARGING_STATUS_MASK GENMASK(5, 3) #define BATTERY_CHARGER_STATUS_MASK GENMASK(2, 0) enum { @@ -585,6 +586,7 @@ enum { #define FORCE_FLOAT_SDP_CFG_BIT BIT(0) #define TAPER_TIMER_SEL_CFG_REG (USBIN_BASE + 0x64) +#define TYPEC_SPARE_CFG_BIT BIT(7) #define TAPER_TIMER_SEL_MASK GENMASK(1, 0) #define USBIN_LOAD_CFG_REG (USBIN_BASE + 0x65) @@ -606,6 +608,8 @@ enum { #define TYPEC_VBUS_ASSERT_INT_EN_BIT BIT(0) #define TYPE_C_INTRPT_ENB_SOFTWARE_CTRL_REG (USBIN_BASE + 0x68) +#define EXIT_SNK_BASED_ON_CC BIT(7) +#define VCONN_EN_ORIENTATION_BIT BIT(6) #define TYPEC_VCONN_OVERCURR_INT_EN_BIT BIT(5) #define VCONN_EN_SRC_BIT BIT(4) #define VCONN_EN_VALUE_BIT BIT(3) diff --git a/drivers/power/qcom-charger/smb138x-charger.c b/drivers/power/qcom-charger/smb138x-charger.c index 5b4e7bcccdce..33d759be9aeb 100644 --- a/drivers/power/qcom-charger/smb138x-charger.c +++ b/drivers/power/qcom-charger/smb138x-charger.c @@ -25,6 +25,7 @@ #include <linux/qpnp/qpnp-revid.h> #include "smb-reg.h" #include "smb-lib.h" +#include "storm-watch.h" #include "pmic-voter.h" #define SMB138X_DEFAULT_FCC_UA 1000000 @@ -748,55 +749,170 @@ static int smb138x_determine_initial_status(struct smb138x *chip) **************************/ struct smb138x_irq_info { - const char *name; - const irq_handler_t handler; + const char *name; + const irq_handler_t handler; + const struct storm_watch storm_data; }; static const struct smb138x_irq_info smb138x_irqs[] = { /* CHARGER IRQs */ - { "chg-error", smblib_handle_debug }, - { "chg-state-change", smblib_handle_debug }, - { "step-chg-state-change", smblib_handle_debug }, - { "step-chg-soc-update-fail", smblib_handle_debug }, - { "step-chg-soc-update-request", smblib_handle_debug }, + { + .name = "chg-error", + .handler = smblib_handle_debug, + }, + { + .name = "chg-state-change", + .handler = smblib_handle_debug, + }, + { + .name = "step-chg-state-change", + .handler = smblib_handle_debug, + }, + { + .name = "step-chg-soc-update-fail", + .handler = smblib_handle_debug, + }, + { + .name = "step-chg-soc-update-request", + .handler = smblib_handle_debug, + }, /* OTG IRQs */ - { "otg-fail", smblib_handle_debug }, - { "otg-overcurrent", smblib_handle_debug }, - { "otg-oc-dis-sw-sts", smblib_handle_debug }, - { "testmode-change-detect", smblib_handle_debug }, + { + .name = "otg-fail", + .handler = smblib_handle_debug, + }, + { + .name = "otg-overcurrent", + .handler = smblib_handle_debug, + }, + { + .name = "otg-oc-dis-sw-sts", + .handler = smblib_handle_debug, + }, + { + .name = "testmode-change-detect", + .handler = smblib_handle_debug, + }, /* BATTERY IRQs */ - { "bat-temp", smblib_handle_batt_psy_changed }, - { "bat-ocp", smblib_handle_batt_psy_changed }, - { "bat-ov", smblib_handle_batt_psy_changed }, - { "bat-low", smblib_handle_batt_psy_changed }, - { "bat-therm-or-id-missing", smblib_handle_batt_psy_changed }, - { "bat-terminal-missing", smblib_handle_batt_psy_changed }, + { + .name = "bat-temp", + .handler = smblib_handle_batt_psy_changed, + }, + { + .name = "bat-ocp", + .handler = smblib_handle_batt_psy_changed, + }, + { + .name = "bat-ov", + .handler = smblib_handle_batt_psy_changed, + }, + { + .name = "bat-low", + .handler = smblib_handle_batt_psy_changed, + }, + { + .name = "bat-therm-or-id-missing", + .handler = smblib_handle_batt_psy_changed, + }, + { + .name = "bat-terminal-missing", + .handler = smblib_handle_batt_psy_changed, + }, /* USB INPUT IRQs */ - { "usbin-collapse", smblib_handle_debug }, - { "usbin-lt-3p6v", smblib_handle_debug }, - { "usbin-uv", smblib_handle_debug }, - { "usbin-ov", smblib_handle_debug }, - { "usbin-plugin", smblib_handle_usb_plugin }, - { "usbin-src-change", smblib_handle_usb_source_change }, - { "usbin-icl-change", smblib_handle_debug }, - { "type-c-change", smblib_handle_usb_typec_change }, + { + .name = "usbin-collapse", + .handler = smblib_handle_debug, + }, + { + .name = "usbin-lt-3p6v", + .handler = smblib_handle_debug, + }, + { + .name = "usbin-uv", + .handler = smblib_handle_debug, + }, + { + .name = "usbin-ov", + .handler = smblib_handle_debug, + }, + { + .name = "usbin-plugin", + .handler = smblib_handle_usb_plugin, + }, + { + .name = "usbin-src-change", + .handler = smblib_handle_usb_source_change, + }, + { + .name = "usbin-icl-change", + .handler = smblib_handle_debug, + }, + { + .name = "type-c-change", + .handler = smblib_handle_usb_typec_change, + }, /* DC INPUT IRQs */ - { "dcin-collapse", smblib_handle_debug }, - { "dcin-lt-3p6v", smblib_handle_debug }, - { "dcin-uv", smblib_handle_debug }, - { "dcin-ov", smblib_handle_debug }, - { "dcin-plugin", smblib_handle_debug }, - { "div2-en-dg", smblib_handle_debug }, - { "dcin-icl-change", smblib_handle_debug }, + { + .name = "dcin-collapse", + .handler = smblib_handle_debug, + }, + { + .name = "dcin-lt-3p6v", + .handler = smblib_handle_debug, + }, + { + .name = "dcin-uv", + .handler = smblib_handle_debug, + }, + { + .name = "dcin-ov", + .handler = smblib_handle_debug, + }, + { + .name = "dcin-plugin", + .handler = smblib_handle_debug, + }, + { + .name = "div2-en-dg", + .handler = smblib_handle_debug, + }, + { + .name = "dcin-icl-change", + .handler = smblib_handle_debug, + }, /* MISCELLANEOUS IRQs */ - { "wdog-snarl", smblib_handle_debug }, - { "wdog-bark", smblib_handle_debug }, - { "aicl-fail", smblib_handle_debug }, - { "aicl-done", smblib_handle_debug }, - { "high-duty-cycle", smblib_handle_debug }, - { "input-current-limiting", smblib_handle_debug }, - { "temperature-change", smblib_handle_debug }, - { "switcher-power-ok", smblib_handle_debug }, + { + .name = "wdog-snarl", + .handler = smblib_handle_debug, + }, + { + .name = "wdog-bark", + .handler = smblib_handle_debug, + }, + { + .name = "aicl-fail", + .handler = smblib_handle_debug, + }, + { + .name = "aicl-done", + .handler = smblib_handle_debug, + }, + { + .name = "high-duty-cycle", + .handler = smblib_handle_debug, + }, + { + .name = "input-current-limiting", + .handler = smblib_handle_debug, + }, + { + .name = "temperature-change", + .handler = smblib_handle_debug, + }, + { + .name = "switcher-power-ok", + .handler = smblib_handle_debug, + }, }; static int smb138x_get_irq_index_byname(const char *irq_name) @@ -837,6 +953,7 @@ static int smb138x_request_interrupt(struct smb138x *chip, irq_data->parent_data = chip; irq_data->name = irq_name; + irq_data->storm_data = smb138x_irqs[irq_index].storm_data; rc = devm_request_threaded_irq(chg->dev, irq, NULL, smb138x_irqs[irq_index].handler, diff --git a/drivers/power/qcom-charger/storm-watch.c b/drivers/power/qcom-charger/storm-watch.c new file mode 100644 index 000000000000..90fec12bd742 --- /dev/null +++ b/drivers/power/qcom-charger/storm-watch.c @@ -0,0 +1,57 @@ +/* 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 "storm-watch.h" + +/** + * is_storming(): Check if an event is storming + * + * @data: Data for tracking an event storm + * + * The return value will be true if a storm has been detected and + * false if a storm was not detected. + */ +bool is_storming(struct storm_watch *data) +{ + ktime_t curr_kt, delta_kt; + bool is_storming = false; + + if (!data) + return false; + + if (!data->enabled) + return false; + + /* max storm count must be greater than 0 */ + if (data->max_storm_count <= 0) + return false; + + /* the period threshold must be greater than 0ms */ + if (data->storm_period_ms <= 0) + return false; + + curr_kt = ktime_get_boottime(); + delta_kt = ktime_sub(curr_kt, data->last_kt); + + if (ktime_to_ms(delta_kt) < data->storm_period_ms) + data->storm_count++; + else + data->storm_count = 0; + + if (data->storm_count > data->max_storm_count) { + is_storming = true; + data->storm_count = 0; + } + + data->last_kt = curr_kt; + return is_storming; +} diff --git a/drivers/power/qcom-charger/storm-watch.h b/drivers/power/qcom-charger/storm-watch.h new file mode 100644 index 000000000000..44b9d64d8a87 --- /dev/null +++ b/drivers/power/qcom-charger/storm-watch.h @@ -0,0 +1,36 @@ +/* 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 __STORM_WATCH_H +#define __STORM_WATCH_H +#include <linux/ktime.h> + +/** + * Data used to track an event storm. + * + * @storm_period_ms: The maximum time interval between two events. If this limit + * is exceeded then the event chain will be broken and removed + * from consideration for a storm. + * @max_storm_count: The number of chained events required to trigger a storm. + * @storm_count: The current number of chained events. + * @last_kt: Kernel time of the last event seen. + */ +struct storm_watch { + bool enabled; + int storm_period_ms; + int max_storm_count; + int storm_count; + ktime_t last_kt; +}; + +bool is_storming(struct storm_watch *data); +#endif diff --git a/drivers/scsi/ufs/ufs-qcom.c b/drivers/scsi/ufs/ufs-qcom.c index 1a1dd804ffb3..ad4b6ffef36e 100644 --- a/drivers/scsi/ufs/ufs-qcom.c +++ b/drivers/scsi/ufs/ufs-qcom.c @@ -1498,14 +1498,10 @@ 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 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); + 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); /* disable device ref_clk */ ufs_qcom_dev_ref_clk_ctrl(host, false); } @@ -1960,6 +1956,13 @@ 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) { /* diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c index 311a4ea24973..d478767ad3dd 100644 --- a/drivers/scsi/ufs/ufshcd.c +++ b/drivers/scsi/ufs/ufshcd.c @@ -1346,12 +1346,7 @@ static void ufshcd_gate_work(struct work_struct *work) ufshcd_set_link_hibern8(hba); } - /* - * 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) + if (!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 */ diff --git a/drivers/soc/qcom/icnss.c b/drivers/soc/qcom/icnss.c index edaaa0b2d3c8..999e6f93e873 100644 --- a/drivers/soc/qcom/icnss.c +++ b/drivers/soc/qcom/icnss.c @@ -34,6 +34,7 @@ #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> #include <soc/qcom/memory_dump.h> @@ -41,6 +42,7 @@ #include <soc/qcom/msm_qmi_interface.h> #include <soc/qcom/secure_buffer.h> #include <soc/qcom/subsystem_notif.h> +#include <soc/qcom/subsystem_restart.h> #include <soc/qcom/service-locator.h> #include <soc/qcom/service-notifier.h> #include <soc/qcom/socinfo.h> @@ -270,6 +272,10 @@ enum icnss_driver_event_type { ICNSS_DRIVER_EVENT_MAX, }; +struct icnss_event_pd_service_down_data { + bool crashed; +}; + struct icnss_driver_event { struct list_head list; enum icnss_driver_event_type type; @@ -292,6 +298,7 @@ enum icnss_driver_state { ICNSS_PDR_ENABLED, ICNSS_PD_RESTART, ICNSS_MSA0_ASSIGNED, + ICNSS_WLFW_EXISTS, }; struct ce_irq_list { @@ -535,9 +542,9 @@ static int icnss_driver_event_post(enum icnss_driver_event_type type, int gfp = GFP_KERNEL; int ret = 0; - icnss_pr_dbg("Posting event: %s(%d)%s, state: 0x%lx\n", - icnss_driver_event_to_str(type), type, - sync ? "-sync" : "", penv->state); + icnss_pr_dbg("Posting event: %s: %s%s(%d), state: 0x%lx\n", + current->comm, icnss_driver_event_to_str(type), + sync ? "-sync" : "", type, penv->state); if (type >= ICNSS_DRIVER_EVENT_MAX) { icnss_pr_err("Invalid Event type: %d, can't post", type); @@ -2212,6 +2219,8 @@ static int icnss_driver_event_server_arrive(void *data) if (!penv) return -ENODEV; + set_bit(ICNSS_WLFW_EXISTS, &penv->state); + penv->wlfw_clnt = qmi_handle_create(icnss_qmi_wlfw_clnt_notify, penv); if (!penv->wlfw_clnt) { icnss_pr_err("QMI client handle create failed\n"); @@ -2311,6 +2320,8 @@ static int icnss_call_driver_probe(struct icnss_priv *priv) if (!priv->ops || !priv->ops->probe) return 0; + icnss_pr_dbg("Calling driver probe state: 0x%lx\n", priv->state); + icnss_hw_power_on(priv); ret = priv->ops->probe(&priv->pdev->dev); @@ -2339,6 +2350,8 @@ static int icnss_call_driver_reinit(struct icnss_priv *priv) if (!test_bit(ICNSS_DRIVER_PROBED, &penv->state)) goto out; + icnss_pr_dbg("Calling driver reinit state: 0x%lx\n", priv->state); + icnss_hw_power_on(priv); ret = priv->ops->reinit(&priv->pdev->dev); @@ -2463,36 +2476,70 @@ out: return 0; } -static int icnss_driver_event_pd_service_down(struct icnss_priv *priv, - void *data) +static int icnss_call_driver_remove(struct icnss_priv *priv) { - int ret = 0; + icnss_pr_dbg("Calling driver remove state: 0x%lx\n", priv->state); - if (test_bit(ICNSS_PD_RESTART, &priv->state)) { - icnss_pr_err("PD Down while recovery inprogress, state: 0x%lx\n", - priv->state); - ICNSS_ASSERT(0); - goto out; - } + clear_bit(ICNSS_FW_READY, &priv->state); + + if (!test_bit(ICNSS_DRIVER_PROBED, &penv->state)) + return 0; + + if (!priv->ops || !priv->ops->remove) + return 0; + + penv->ops->remove(&priv->pdev->dev); + + clear_bit(ICNSS_DRIVER_PROBED, &priv->state); + + return 0; +} + +static int icnss_call_driver_shutdown(struct icnss_priv *priv) +{ + icnss_pr_dbg("Calling driver shutdown state: 0x%lx\n", priv->state); set_bit(ICNSS_PD_RESTART, &priv->state); clear_bit(ICNSS_FW_READY, &priv->state); + if (!test_bit(ICNSS_DRIVER_PROBED, &penv->state)) + return 0; + if (!priv->ops || !priv->ops->shutdown) - goto out; + return 0; - if (!test_bit(ICNSS_DRIVER_PROBED, &penv->state)) + priv->ops->shutdown(&priv->pdev->dev); + + return 0; +} + +static int icnss_driver_event_pd_service_down(struct icnss_priv *priv, + void *data) +{ + int ret = 0; + struct icnss_event_pd_service_down_data *event_data = data; + + if (!test_bit(ICNSS_WLFW_EXISTS, &priv->state)) + return 0; + + if (test_bit(ICNSS_PD_RESTART, &priv->state)) { + icnss_pr_err("PD Down while recovery inprogress, crashed: %d, state: 0x%lx\n", + event_data->crashed, priv->state); + ICNSS_ASSERT(0); goto out; + } - priv->ops->shutdown(&priv->pdev->dev); + if (event_data->crashed) + icnss_call_driver_shutdown(priv); + else + icnss_call_driver_remove(priv); out: icnss_remove_msa_permissions(priv); ret = icnss_hw_power_off(priv); - icnss_pr_dbg("PD down completed: %d, state: 0x%lx\n", - ret, priv->state); + kfree(data); return ret; } @@ -2533,7 +2580,8 @@ static void icnss_driver_event_work(struct work_struct *work) ret = icnss_driver_event_unregister_driver(event->data); break; case ICNSS_DRIVER_EVENT_PD_SERVICE_DOWN: - icnss_driver_event_pd_service_down(penv, event->data); + ret = icnss_driver_event_pd_service_down(penv, + event->data); break; default: icnss_pr_err("Invalid Event type: %d", event->type); @@ -2543,6 +2591,11 @@ static void icnss_driver_event_work(struct work_struct *work) penv->stats.events[event->type].processed++; + icnss_pr_dbg("Event Processed: %s%s(%d), ret: %d, state: 0x%lx\n", + icnss_driver_event_to_str(event->type), + event->sync ? "-sync" : "", event->type, ret, + penv->state); + spin_lock_irqsave(&penv->event_lock, flags); if (event->sync) { event->ret = ret; @@ -2590,23 +2643,31 @@ static struct notifier_block wlfw_clnt_nb = { .notifier_call = icnss_qmi_wlfw_clnt_svc_event_notify, }; -static int icnss_modem_notifier_nb(struct notifier_block *this, +static int icnss_modem_notifier_nb(struct notifier_block *nb, unsigned long code, - void *ss_handle) + void *data) { + struct icnss_event_pd_service_down_data *event_data; + struct notif_data *notif = data; + struct icnss_priv *priv = container_of(nb, struct icnss_priv, + modem_ssr_nb); + icnss_pr_dbg("Modem-Notify: event %lu\n", code); - if (code == SUBSYS_AFTER_POWERUP) { - icnss_pr_dbg("Modem-Notify: Powerup\n"); - } else if (code == SUBSYS_BEFORE_SHUTDOWN) { - icnss_pr_info("Modem-Notify: Before shutdown\n"); - icnss_driver_event_post(ICNSS_DRIVER_EVENT_PD_SERVICE_DOWN, - true, NULL); - } else if (code == SUBSYS_AFTER_SHUTDOWN) { - icnss_pr_info("Modem-Notify: After Shutdown\n"); - } else { - return NOTIFY_DONE; - } + if (code != SUBSYS_BEFORE_SHUTDOWN) + return NOTIFY_OK; + + icnss_pr_info("Modem went down, state: %lx\n", priv->state); + + event_data = kzalloc(sizeof(*data), GFP_KERNEL); + + if (event_data == NULL) + return notifier_from_errno(-ENOMEM); + + event_data->crashed = notif->crashed; + + icnss_driver_event_post(ICNSS_DRIVER_EVENT_PD_SERVICE_DOWN, + true, event_data); return NOTIFY_OK; } @@ -2665,14 +2726,23 @@ static int icnss_service_notifier_notify(struct notifier_block *nb, { struct icnss_priv *priv = container_of(nb, struct icnss_priv, service_notifier_nb); + enum pd_subsys_state *state = data; + struct icnss_event_pd_service_down_data *event_data; switch (notification) { case SERVREG_NOTIF_SERVICE_STATE_DOWN_V01: - icnss_pr_info("Service down, state: 0x%lx\n", priv->state); + icnss_pr_info("Service down, data: 0x%p, state: 0x%lx\n", data, + priv->state); + event_data = kzalloc(sizeof(*data), GFP_KERNEL); + + if (event_data == NULL) + return notifier_from_errno(-ENOMEM); + + if (state == NULL || *state != SHUTDOWN) + event_data->crashed = true; + icnss_driver_event_post(ICNSS_DRIVER_EVENT_PD_SERVICE_DOWN, - true, NULL); - icnss_pr_dbg("Service down completed, state: 0x%lx\n", - priv->state); + true, event_data); break; case SERVREG_NOTIF_SERVICE_STATE_UP_V01: icnss_pr_dbg("Service up, state: 0x%lx\n", priv->state); @@ -2818,8 +2888,6 @@ enable_pdr: if (ret) return ret; - icnss_modem_ssr_unregister_notifier(priv); - return 0; } @@ -3774,6 +3842,8 @@ static int icnss_stats_show_state(struct seq_file *s, struct icnss_priv *priv) case ICNSS_MSA0_ASSIGNED: seq_puts(s, "MSA0 ASSIGNED"); continue; + case ICNSS_WLFW_EXISTS: + continue; } seq_printf(s, "UNKNOWN-%d", i); diff --git a/drivers/soc/qcom/jtag-fuse.c b/drivers/soc/qcom/jtag-fuse.c index 0b05ce9a22bb..0f347723e378 100644 --- a/drivers/soc/qcom/jtag-fuse.c +++ b/drivers/soc/qcom/jtag-fuse.c @@ -152,8 +152,6 @@ static int jtag_fuse_probe(struct platform_device *pdev) drvdata = devm_kzalloc(dev, sizeof(*drvdata), GFP_KERNEL); if (!drvdata) return -ENOMEM; - /* Store the driver data pointer for use in exported functions */ - fusedrvdata = drvdata; drvdata->dev = &pdev->dev; platform_set_drvdata(pdev, drvdata); @@ -174,6 +172,8 @@ static int jtag_fuse_probe(struct platform_device *pdev) if (!drvdata->base) return -ENOMEM; + /* Store the driver data pointer for use in exported functions */ + fusedrvdata = drvdata; dev_info(dev, "JTag Fuse initialized\n"); return 0; } diff --git a/drivers/soc/qcom/service-notifier.c b/drivers/soc/qcom/service-notifier.c index 28b47ca6112a..504a3263253c 100644 --- a/drivers/soc/qcom/service-notifier.c +++ b/drivers/soc/qcom/service-notifier.c @@ -114,6 +114,7 @@ struct qmi_client_info { }; static LIST_HEAD(qmi_client_list); static DEFINE_MUTEX(qmi_list_lock); +static DEFINE_MUTEX(qmi_client_release_lock); static DEFINE_MUTEX(notif_add_lock); @@ -417,9 +418,11 @@ static void root_service_service_exit(struct qmi_client_info *data, * Destroy client handle and try connecting when * service comes up again. */ + mutex_lock(&qmi_client_release_lock); data->service_connected = false; qmi_handle_destroy(data->clnt_handle); data->clnt_handle = NULL; + mutex_unlock(&qmi_client_release_lock); } static void root_service_exit_work(struct work_struct *work) diff --git a/drivers/thermal/msm-tsens.c b/drivers/thermal/msm-tsens.c index b6d76402b726..df3a638510c2 100644 --- a/drivers/thermal/msm-tsens.c +++ b/drivers/thermal/msm-tsens.c @@ -933,6 +933,9 @@ static struct of_device_id tsens_match[] = { { .compatible = "qcom,msmfalcon-tsens", .data = (void *)TSENS_CALIB_FUSE_MAP_NONE, }, + { .compatible = "qcom,msmtriton-tsens", + .data = (void *)TSENS_CALIB_FUSE_MAP_NONE, + }, {} }; @@ -5435,7 +5438,8 @@ static int get_device_tree_data(struct platform_device *pdev, tmdev->tsens_type = TSENS_TYPE3; else if (!strcmp(id->compatible, "qcom,msmtitanium-tsens") || (!strcmp(id->compatible, "qcom,msmfalcon-tsens") || - (!strcmp(id->compatible, "qcom,msmhamster-tsens")))) { + (!strcmp(id->compatible, "qcom,msmtriton-tsens") || + (!strcmp(id->compatible, "qcom,msmhamster-tsens"))))) { tmdev->tsens_type = TSENS_TYPE3; tsens_poll_check = 0; } else if (!strcmp(id->compatible, "qcom,msm8952-tsens") || @@ -5457,7 +5461,8 @@ static int get_device_tree_data(struct platform_device *pdev, (!strcmp(id->compatible, "qcom,msmtitanium-tsens")) || (!strcmp(id->compatible, "qcom,msmcobalt-tsens")) || (!strcmp(id->compatible, "qcom,msmfalcon-tsens") || - (!strcmp(id->compatible, "qcom,msmhamster-tsens")))) + (!strcmp(id->compatible, "qcom,msmtriton-tsens") || + (!strcmp(id->compatible, "qcom,msmhamster-tsens"))))) tmdev->tsens_valid_status_check = true; } @@ -5473,7 +5478,8 @@ static int get_device_tree_data(struct platform_device *pdev, (!strcmp(id->compatible, "qcom,msmcobalt-tsens")) || (!strcmp(id->compatible, "qcom,msmhamster-tsens")) || (!strcmp(id->compatible, "qcom,msmfalcon-tsens") || - (!strcmp(id->compatible, "qcom,msmtitanium-tsens")))) { + (!strcmp(id->compatible, "qcom,msmtriton-tsens") || + (!strcmp(id->compatible, "qcom,msmtitanium-tsens"))))) { tmdev->tsens_critical_irq = platform_get_irq_byname(pdev, "tsens-critical"); diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig index c89ae79763c6..9ef57e5d7d64 100644 --- a/drivers/usb/gadget/Kconfig +++ b/drivers/usb/gadget/Kconfig @@ -175,6 +175,9 @@ config USB_F_SUBSET config USB_F_RNDIS tristate +config USB_F_QCRNDIS + tristate + config USB_F_MASS_STORAGE tristate @@ -318,6 +321,14 @@ config USB_CONFIGFS_ECM_SUBSET On hardware that can't implement the full protocol, a simple CDC subset is used, placing fewer demands on USB. +config USB_CONFIGFS_QCRNDIS + bool "RNDIS" + depends on USB_CONFIGFS + depends on RNDIS_IPA + depends on NET + select USB_U_ETHER + select USB_F_QCRNDIS + config USB_CONFIGFS_RNDIS bool "RNDIS" depends on USB_CONFIGFS diff --git a/drivers/usb/gadget/function/Makefile b/drivers/usb/gadget/function/Makefile index 9a27deac7978..a213cd4c8377 100644 --- a/drivers/usb/gadget/function/Makefile +++ b/drivers/usb/gadget/function/Makefile @@ -60,3 +60,5 @@ usb_f_cdev-y := f_cdev.o obj-$(CONFIG_USB_F_CDEV) += usb_f_cdev.o usb_f_qdss-y := f_qdss.o u_qdss.o obj-$(CONFIG_USB_F_QDSS) += usb_f_qdss.o +usb_f_qcrndis-y := f_qc_rndis.o u_data_ipa.o +obj-$(CONFIG_USB_F_QCRNDIS) += usb_f_qcrndis.o diff --git a/drivers/usb/gadget/function/f_qc_rndis.c b/drivers/usb/gadget/function/f_qc_rndis.c index bc319d4fe16c..316967415aa9 100644 --- a/drivers/usb/gadget/function/f_qc_rndis.c +++ b/drivers/usb/gadget/function/f_qc_rndis.c @@ -25,16 +25,17 @@ #include <linux/slab.h> #include <linux/kernel.h> +#include <linux/module.h> #include <linux/device.h> #include <linux/etherdevice.h> #include <linux/atomic.h> #include "u_ether.h" -#include "u_qc_ether.h" #include "rndis.h" -#include "u_bam_data.h" +#include "u_data_ipa.h" #include <linux/rndis_ipa.h> +#include "configfs.h" unsigned int rndis_dl_max_xfer_size = 9216; module_param(rndis_dl_max_xfer_size, uint, S_IRUGO | S_IWUSR); @@ -86,7 +87,7 @@ MODULE_PARM_DESC(rndis_dl_max_xfer_size, */ struct f_rndis_qc { - struct qc_gether port; + struct usb_function func; u8 ctrl_id, data_id; u8 ethaddr[ETH_ALEN]; u32 vendorID; @@ -94,27 +95,27 @@ struct f_rndis_qc { u8 pkt_alignment_factor; u32 max_pkt_size; const char *manufacturer; - int config; + struct rndis_params *params; atomic_t ioctl_excl; atomic_t open_excl; struct usb_ep *notify; struct usb_request *notify_req; atomic_t notify_count; - struct data_port bam_port; - enum transport_type xport; + struct gadget_ipa_port bam_port; u8 port_num; + u16 cdc_filter; bool net_ready_trigger; }; static struct ipa_usb_init_params rndis_ipa_params; static spinlock_t rndis_lock; static bool rndis_ipa_supported; -static void rndis_qc_open(struct qc_gether *geth); +static void rndis_qc_open(struct f_rndis_qc *rndis); static inline struct f_rndis_qc *func_to_rndis_qc(struct usb_function *f) { - return container_of(f, struct f_rndis_qc, port.func); + return container_of(f, struct f_rndis_qc, func); } /* peak (theoretical) bulk transfer rate in bits-per-second */ @@ -322,10 +323,20 @@ static struct usb_endpoint_descriptor rndis_qc_ss_notify_desc = { .bEndpointAddress = USB_DIR_IN, .bmAttributes = USB_ENDPOINT_XFER_INT, - .wMaxPacketSize = cpu_to_le16(STATUS_BYTECOUNT), + .wMaxPacketSize = cpu_to_le16(RNDIS_QC_STATUS_BYTECOUNT), .bInterval = RNDIS_QC_LOG2_STATUS_INTERVAL_MSEC + 4, }; +static struct usb_ss_ep_comp_descriptor ss_intr_comp_desc = { + .bLength = sizeof(ss_intr_comp_desc), + .bDescriptorType = USB_DT_SS_ENDPOINT_COMP, + + /* the following 3 values can be tweaked if necessary */ + /* .bMaxBurst = 0, */ + /* .bmAttributes = 0, */ + .wBytesPerInterval = cpu_to_le16(RNDIS_QC_STATUS_BYTECOUNT), +}; + static struct usb_ss_ep_comp_descriptor rndis_qc_ss_intr_comp_desc = { .bLength = sizeof(ss_intr_comp_desc), .bDescriptorType = USB_DT_SS_ENDPOINT_COMP, @@ -333,7 +344,16 @@ static struct usb_ss_ep_comp_descriptor rndis_qc_ss_intr_comp_desc = { /* the following 3 values can be tweaked if necessary */ /* .bMaxBurst = 0, */ /* .bmAttributes = 0, */ - .wBytesPerInterval = cpu_to_le16(STATUS_BYTECOUNT), + .wBytesPerInterval = cpu_to_le16(RNDIS_QC_STATUS_BYTECOUNT), +}; + +static struct usb_ss_ep_comp_descriptor ss_bulk_comp_desc = { + .bLength = sizeof(ss_bulk_comp_desc), + .bDescriptorType = USB_DT_SS_ENDPOINT_COMP, + + /* the following 2 values can be tweaked if necessary */ + /* .bMaxBurst = 0, */ + /* .bmAttributes = 0, */ }; static struct usb_endpoint_descriptor rndis_qc_ss_in_desc = { @@ -407,7 +427,7 @@ struct f_rndis_qc *_rndis_qc; static inline int rndis_qc_lock(atomic_t *excl) { - if (atomic_inc_return(excl) == 1) { + if (atomic_inc_return(excl) == 1) return 0; atomic_dec(excl); @@ -421,46 +441,6 @@ static inline void rndis_qc_unlock(atomic_t *excl) /*-------------------------------------------------------------------------*/ -static struct sk_buff *rndis_qc_add_header(struct qc_gether *port, - struct sk_buff *skb) -{ - struct sk_buff *skb2; - - skb2 = skb_realloc_headroom(skb, sizeof(struct rndis_packet_msg_type)); - if (skb2) - rndis_add_hdr(skb2); - - dev_kfree_skb_any(skb); - return skb2; -} - -int rndis_qc_rm_hdr(struct qc_gether *port, - struct sk_buff *skb, - struct sk_buff_head *list) -{ - /* tmp points to a struct rndis_packet_msg_type */ - __le32 *tmp = (void *)skb->data; - - /* MessageType, MessageLength */ - if (cpu_to_le32(RNDIS_MSG_PACKET) - != get_unaligned(tmp++)) { - dev_kfree_skb_any(skb); - return -EINVAL; - } - tmp++; - - /* DataOffset, DataLength */ - if (!skb_pull(skb, get_unaligned_le32(tmp++) + 8)) { - dev_kfree_skb_any(skb); - return -EOVERFLOW; - } - skb_trim(skb, get_unaligned_le32(tmp++)); - - skb_queue_tail(list, skb); - return 0; -} - - static void rndis_qc_response_available(void *_rndis) { struct f_rndis_qc *rndis = _rndis; @@ -496,12 +476,12 @@ static void rndis_qc_response_complete(struct usb_ep *ep, int status = req->status; struct usb_composite_dev *cdev; - if (!rndis->port.func.config || !rndis->port.func.config->cdev) { + if (!rndis->func.config || !rndis->func.config->cdev) { pr_err("%s(): cdev or config is NULL.\n", __func__); return; } - cdev = rndis->port.func.config->cdev; + cdev = rndis->func.config->cdev; /* after TX: * - USB_CDC_GET_ENCAPSULATED_RESPONSE (ep0/control) * - RNDIS_RESPONSE_AVAILABLE (status/irq) @@ -544,7 +524,7 @@ static void rndis_qc_command_complete(struct usb_ep *ep, u32 ul_max_xfer_size, dl_max_xfer_size; /* received RNDIS command from USB_CDC_SEND_ENCAPSULATED_COMMAND */ - status = rndis_msg_parser(rndis->config, (u8 *) req->buf); + status = rndis_msg_parser(rndis->params, (u8 *) req->buf); if (status < 0) pr_err("RNDIS command error %d, %d/%d\n", status, req->actual, req->length); @@ -552,8 +532,8 @@ static void rndis_qc_command_complete(struct usb_ep *ep, buf = (rndis_init_msg_type *)req->buf; if (buf->MessageType == RNDIS_MSG_INIT) { - ul_max_xfer_size = rndis_get_ul_max_xfer_size(rndis->config); - u_bam_data_set_ul_max_xfer_size(ul_max_xfer_size); + ul_max_xfer_size = rndis_get_ul_max_xfer_size(rndis->params); + ipa_data_set_ul_max_xfer_size(ul_max_xfer_size); /* * For consistent data throughput from IPA, it is required to * fine tune aggregation byte limit as 7KB. RNDIS IPA driver @@ -565,11 +545,11 @@ static void rndis_qc_command_complete(struct usb_ep *ep, */ if (rndis_dl_max_xfer_size) dl_max_xfer_size = min_t(u32, rndis_dl_max_xfer_size, - rndis_get_dl_max_xfer_size(rndis->config)); + rndis_get_dl_max_xfer_size(rndis->params)); else dl_max_xfer_size = - rndis_get_dl_max_xfer_size(rndis->config); - u_bam_data_set_dl_max_xfer_size(dl_max_xfer_size); + rndis_get_dl_max_xfer_size(rndis->params); + ipa_data_set_dl_max_xfer_size(dl_max_xfer_size); } } @@ -612,11 +592,11 @@ rndis_qc_setup(struct usb_function *f, const struct usb_ctrlrequest *ctrl) u32 n; /* return the result */ - buf = rndis_get_next_response(rndis->config, &n); + buf = rndis_get_next_response(rndis->params, &n); if (buf) { memcpy(req->buf, buf, n); req->complete = rndis_qc_response_complete; - rndis_free_response(rndis->config, buf); + rndis_free_response(rndis->params, buf); value = n; } /* else stalls ... spec says to avoid that */ @@ -647,11 +627,31 @@ invalid: return value; } +struct net_device *rndis_qc_get_net(const char *netname) +{ + struct net_device *net_dev; + + net_dev = dev_get_by_name(&init_net, netname); + if (!net_dev) + return ERR_PTR(-EINVAL); + + /* + * Decrement net_dev refcount as it was incremented in + * dev_get_by_name(). + */ + dev_put(net_dev); + return net_dev; +} static int rndis_qc_set_alt(struct usb_function *f, unsigned intf, unsigned alt) { struct f_rndis_qc *rndis = func_to_rndis_qc(f); + struct f_rndis_qc_opts *opts; struct usb_composite_dev *cdev = f->config->cdev; + u8 src_connection_idx; + u8 dst_connection_idx; + enum usb_ctrl usb_bam_type; + int ret; /* we know alt == 0 */ @@ -672,35 +672,28 @@ static int rndis_qc_set_alt(struct usb_function *f, unsigned intf, unsigned alt) struct net_device *net; rndis->net_ready_trigger = false; - if (rndis->port.in_ep->driver_data) { + if (rndis->bam_port.in->driver_data) { DBG(cdev, "reset rndis\n"); - /* rndis->port is needed for disconnecting the BAM data + /* bam_port is needed for disconnecting the BAM data * path. Only after the BAM data path is disconnected, * we can disconnect the port from the network layer. */ - bam_data_disconnect(&rndis->bam_port, USB_FUNC_RNDIS, - rndis->port_num); - - if (rndis->xport != USB_GADGET_XPORT_BAM2BAM_IPA) - gether_qc_disconnect_name(&rndis->port, - "rndis0"); + ipa_data_disconnect(&rndis->bam_port, + USB_IPA_FUNC_RNDIS); } - if (!rndis->port.in_ep->desc || !rndis->port.out_ep->desc) { + if (!rndis->bam_port.in->desc || !rndis->bam_port.out->desc) { DBG(cdev, "init rndis\n"); if (config_ep_by_speed(cdev->gadget, f, - rndis->port.in_ep) || + rndis->bam_port.in) || config_ep_by_speed(cdev->gadget, f, - rndis->port.out_ep)) { - rndis->port.in_ep->desc = NULL; - rndis->port.out_ep->desc = NULL; + rndis->bam_port.out)) { + rndis->bam_port.in->desc = NULL; + rndis->bam_port.out->desc = NULL; goto fail; } } - /* Avoid ZLPs; they can be troublesome. */ - rndis->port.is_zlp_ok = false; - /* RNDIS should be in the "RNDIS uninitialized" state, * either never activated or after rndis_uninit(). * @@ -713,30 +706,37 @@ static int rndis_qc_set_alt(struct usb_function *f, unsigned intf, unsigned alt) * very long time. We need another call to the link layer * code -- gether_updown(...bool) maybe -- to do it right. */ - rndis->port.cdc_filter = 0; + rndis->cdc_filter = 0; rndis->bam_port.cdev = cdev; - rndis->bam_port.func = &rndis->port.func; - rndis->bam_port.in = rndis->port.in_ep; - rndis->bam_port.out = rndis->port.out_ep; - - if (bam_data_connect(&rndis->bam_port, rndis->xport, - rndis->port_num, USB_FUNC_RNDIS)) + rndis->bam_port.func = &rndis->func; + ipa_data_port_select(USB_IPA_FUNC_RNDIS); + usb_bam_type = usb_bam_get_bam_type(cdev->gadget->name); + + src_connection_idx = usb_bam_get_connection_idx(usb_bam_type, + IPA_P_BAM, USB_TO_PEER_PERIPHERAL, USB_BAM_DEVICE, + rndis->port_num); + dst_connection_idx = usb_bam_get_connection_idx(usb_bam_type, + IPA_P_BAM, PEER_PERIPHERAL_TO_USB, USB_BAM_DEVICE, + rndis->port_num); + if (src_connection_idx < 0 || dst_connection_idx < 0) { + pr_err("%s: usb_bam_get_connection_idx failed\n", + __func__); + return ret; + } + if (ipa_data_connect(&rndis->bam_port, USB_IPA_FUNC_RNDIS, + src_connection_idx, dst_connection_idx)) goto fail; DBG(cdev, "RNDIS RX/TX early activation ...\n"); - if (rndis->xport != USB_GADGET_XPORT_BAM2BAM_IPA) { - net = gether_qc_connect_name(&rndis->port, "rndis0", - false); - } else { - rndis_qc_open(&rndis->port); - net = gether_qc_get_net("rndis0"); - } + rndis_qc_open(rndis); + net = rndis_qc_get_net("rndis0"); if (IS_ERR(net)) return PTR_ERR(net); + opts->net = net; - rndis_set_param_dev(rndis->config, net, - &rndis->port.cdc_filter); + rndis_set_param_dev(rndis->params, net, + &rndis->cdc_filter); } else goto fail; @@ -753,18 +753,13 @@ static void rndis_qc_disable(struct usb_function *f) if (!rndis->notify->driver_data) return; - pr_info("rndis deactivated\n"); + DBG(cdev, "rndis deactivated\n"); - rndis_uninit(rndis->config); - bam_data_disconnect(&rndis->bam_port, USB_FUNC_RNDIS, rndis->port_num); - if (rndis->xport != USB_GADGET_XPORT_BAM2BAM_IPA) - gether_qc_disconnect_name(&rndis->port, "rndis0"); + rndis_uninit(rndis->params); + ipa_data_disconnect(&rndis->bam_port, USB_IPA_FUNC_RNDIS); - if (rndis->xport == USB_GADGET_XPORT_BAM2BAM_IPA && - gadget_is_dwc3(cdev->gadget)) { - msm_ep_unconfig(rndis->port.out_ep); - msm_ep_unconfig(rndis->port.in_ep); - } + msm_ep_unconfig(rndis->bam_port.out); + msm_ep_unconfig(rndis->bam_port.in); usb_ep_disable(rndis->notify); rndis->notify->driver_data = NULL; } @@ -789,11 +784,11 @@ static void rndis_qc_suspend(struct usb_function *f) * host case. In case of windows, this RNDIS state machine is * already updated due to receiving of PACKET_FILTER. */ - rndis_flow_control(rndis->config, true); + rndis_flow_control(rndis->params, true); pr_debug("%s(): Disconnecting\n", __func__); } - bam_data_suspend(&rndis->bam_port, rndis->port_num, USB_FUNC_RNDIS, + ipa_data_suspend(&rndis->bam_port, USB_IPA_FUNC_RNDIS, remote_wakeup_allowed); pr_debug("rndis suspended\n"); } @@ -816,12 +811,11 @@ static void rndis_qc_resume(struct usb_function *f) else remote_wakeup_allowed = f->config->cdev->gadget->remote_wakeup; - bam_data_resume(&rndis->bam_port, rndis->port_num, USB_FUNC_RNDIS, - remote_wakeup_allowed); + ipa_data_resume(&rndis->bam_port, USB_IPA_FUNC_RNDIS, + remote_wakeup_allowed); if (!remote_wakeup_allowed) { - if (rndis->xport == USB_GADGET_XPORT_BAM2BAM_IPA) - rndis_qc_open(&rndis->port); + rndis_qc_open(rndis); /* * Linux Host doesn't sends RNDIS_MSG_INIT or non-zero value * set with RNDIS_MESSAGE_PACKET_FILTER after performing bus @@ -829,7 +823,7 @@ static void rndis_qc_resume(struct usb_function *f) * explicitly here. For Windows host case is also being * handle with RNDIS state machine. */ - rndis_flow_control(rndis->config, false); + rndis_flow_control(rndis->params, false); } pr_debug("%s: RNDIS resume completed\n", __func__); @@ -844,26 +838,23 @@ static void rndis_qc_resume(struct usb_function *f) * not used to tell whether the link should send packets or not. */ -static void rndis_qc_open(struct qc_gether *geth) +static void rndis_qc_open(struct f_rndis_qc *rndis) { - struct f_rndis_qc *rndis = func_to_rndis_qc(&geth->func); - struct usb_composite_dev *cdev = geth->func.config->cdev; + struct usb_composite_dev *cdev = rndis->func.config->cdev; DBG(cdev, "%s\n", __func__); - rndis_set_param_medium(rndis->config, RNDIS_MEDIUM_802_3, + rndis_set_param_medium(rndis->params, RNDIS_MEDIUM_802_3, rndis_qc_bitrate(cdev->gadget) / 100); - rndis_signal_connect(rndis->config); + rndis_signal_connect(rndis->params); } -static void rndis_qc_close(struct qc_gether *geth) +void ipa_data_flow_control_enable(bool enable, struct rndis_params *param) { - struct f_rndis_qc *rndis = func_to_rndis_qc(&geth->func); - - DBG(geth->func.config->cdev, "%s\n", __func__); - - rndis_set_param_medium(rndis->config, RNDIS_MEDIUM_802_3, 0); - rndis_signal_disconnect(rndis->config); + if (enable) + ipa_data_stop_rndis_ipa(USB_IPA_FUNC_RNDIS); + else + ipa_data_start_rndis_ipa(USB_IPA_FUNC_RNDIS); } /*-------------------------------------------------------------------------*/ @@ -875,9 +866,35 @@ rndis_qc_bind(struct usb_configuration *c, struct usb_function *f) { struct usb_composite_dev *cdev = c->cdev; struct f_rndis_qc *rndis = func_to_rndis_qc(f); + struct rndis_params *params; int status; struct usb_ep *ep; + /* maybe allocate device-global string IDs */ + if (rndis_qc_string_defs[0].id == 0) { + + /* control interface label */ + status = usb_string_id(c->cdev); + if (status < 0) + return status; + rndis_qc_string_defs[0].id = status; + rndis_qc_control_intf.iInterface = status; + + /* data interface label */ + status = usb_string_id(c->cdev); + if (status < 0) + return status; + rndis_qc_string_defs[1].id = status; + rndis_qc_data_intf.iInterface = status; + + /* IAD iFunction label */ + status = usb_string_id(c->cdev); + if (status < 0) + return status; + rndis_qc_string_defs[2].id = status; + rndis_qc_iad_descriptor.iFunction = status; + } + /* allocate instance-specific interface IDs */ status = usb_interface_id(c, f); if (status < 0) @@ -902,13 +919,13 @@ rndis_qc_bind(struct usb_configuration *c, struct usb_function *f) ep = usb_ep_autoconfig(cdev->gadget, &rndis_qc_fs_in_desc); if (!ep) goto fail; - rndis->port.in_ep = ep; + rndis->bam_port.in = ep; ep->driver_data = cdev; /* claim */ ep = usb_ep_autoconfig(cdev->gadget, &rndis_qc_fs_out_desc); if (!ep) goto fail; - rndis->port.out_ep = ep; + rndis->bam_port.out = ep; ep->driver_data = cdev; /* claim */ /* NOTE: a status/notification endpoint is, strictly speaking, @@ -972,33 +989,30 @@ rndis_qc_bind(struct usb_configuration *c, struct usb_function *f) goto fail; } - rndis->port.open = rndis_qc_open; - rndis->port.close = rndis_qc_close; - - status = rndis_register(rndis_qc_response_available, rndis, - bam_data_flow_control_enable); - if (status < 0) + params = rndis_register(rndis_qc_response_available, rndis, + ipa_data_flow_control_enable); + if (params < 0) goto fail; - rndis->config = status; + rndis->params = params; - rndis_set_param_medium(rndis->config, RNDIS_MEDIUM_802_3, 0); - rndis_set_host_mac(rndis->config, rndis->ethaddr); + rndis_set_param_medium(rndis->params, RNDIS_MEDIUM_802_3, 0); + rndis_set_host_mac(rndis->params, rndis->ethaddr); if (rndis->manufacturer && rndis->vendorID && - rndis_set_param_vendor(rndis->config, rndis->vendorID, + rndis_set_param_vendor(rndis->params, rndis->vendorID, rndis->manufacturer)) goto fail; pr_debug("%s(): max_pkt_per_xfer:%d\n", __func__, rndis->ul_max_pkt_per_xfer); - rndis_set_max_pkt_xfer(rndis->config, rndis->ul_max_pkt_per_xfer); + rndis_set_max_pkt_xfer(rndis->params, rndis->ul_max_pkt_per_xfer); /* In case of aggregated packets QC device will request * aliment to 4 (2^2). */ pr_debug("%s(): pkt_alignment_factor:%d\n", __func__, rndis->pkt_alignment_factor); - rndis_set_pkt_alignment_factor(rndis->config, + rndis_set_pkt_alignment_factor(rndis->params, rndis->pkt_alignment_factor); /* NOTE: all that is done without knowing or caring about @@ -1009,7 +1023,7 @@ rndis_qc_bind(struct usb_configuration *c, struct usb_function *f) DBG(cdev, "RNDIS: %s speed IN/%s OUT/%s NOTIFY/%s\n", gadget_is_superspeed(c->cdev->gadget) ? "super" : gadget_is_dualspeed(c->cdev->gadget) ? "dual" : "full", - rndis->port.in_ep->name, rndis->port.out_ep->name, + rndis->bam_port.in->name, rndis->bam_port.out->name, rndis->notify->name); return 0; @@ -1029,24 +1043,31 @@ fail: /* we might as well release our claims on endpoints */ if (rndis->notify) rndis->notify->driver_data = NULL; - if (rndis->port.out_ep->desc) - rndis->port.out_ep->driver_data = NULL; - if (rndis->port.in_ep->desc) - rndis->port.in_ep->driver_data = NULL; + if (rndis->bam_port.out->desc) + rndis->bam_port.out->driver_data = NULL; + if (rndis->bam_port.in->desc) + rndis->bam_port.in->driver_data = NULL; pr_err("%s: can't bind, err %d\n", f->name, status); return status; } +static void rndis_qc_free(struct usb_function *f) +{ + struct f_rndis_qc_opts *opts; + + opts = container_of(f->fi, struct f_rndis_qc_opts, func_inst); + opts->refcnt--; +} + static void rndis_qc_unbind(struct usb_configuration *c, struct usb_function *f) { struct f_rndis_qc *rndis = func_to_rndis_qc(f); - unsigned long flags; pr_debug("rndis_qc_unbind: free\n"); - rndis_deregister(rndis->config); + rndis_deregister(rndis->params); if (gadget_is_dualspeed(c->cdev->gadget)) usb_free_descriptors(f->hs_descriptors); @@ -1055,23 +1076,17 @@ rndis_qc_unbind(struct usb_configuration *c, struct usb_function *f) kfree(rndis->notify_req->buf); usb_ep_free_request(rndis->notify, rndis->notify_req); - if (rndis->xport == USB_GADGET_XPORT_BAM2BAM_IPA) { - /* - * call flush_workqueue to make sure that any pending - * disconnect_work() from u_bam_data.c file is being - * flushed before calling this rndis_ipa_cleanup API - * as rndis ipa disconnect API is required to be - * called before this. - */ - bam_data_flush_workqueue(); - rndis_ipa_cleanup(rndis_ipa_params.private); - rndis_ipa_supported = false; - } + /* + * call flush_workqueue to make sure that any pending + * disconnect_work() from u_bam_data.c file is being + * flushed before calling this rndis_ipa_cleanup API + * as rndis ipa disconnect API is required to be + * called before this. + */ + ipa_data_flush_workqueue(); + rndis_ipa_cleanup(rndis_ipa_params.private); + rndis_ipa_supported = false; - spin_lock_irqsave(&rndis_lock, flags); - kfree(rndis); - _rndis_qc = NULL; - spin_unlock_irqrestore(&rndis_lock, flags); } void rndis_ipa_reset_trigger(void) @@ -1099,7 +1114,6 @@ void rndis_net_ready_notify(void) { struct f_rndis_qc *rndis; unsigned long flags; - int port_num; spin_lock_irqsave(&rndis_lock, flags); rndis = _rndis_qc; @@ -1117,19 +1131,7 @@ void rndis_net_ready_notify(void) pr_debug("%s: Set net_ready_trigger", __func__); rndis->net_ready_trigger = true; spin_unlock_irqrestore(&rndis_lock, flags); - port_num = (u_bam_data_func_to_port(USB_FUNC_RNDIS, - RNDIS_QC_ACTIVE_PORT)); - if (port_num < 0) - return; - bam_data_start_rx_tx(port_num); -} - - -/* Some controllers can't support RNDIS ... */ -static inline bool can_support_rndis_qc(struct usb_configuration *c) -{ - /* everything else is *presumably* fine */ - return true; + ipa_data_start_rx_tx(USB_IPA_FUNC_RNDIS); } /** @@ -1144,84 +1146,42 @@ static inline bool can_support_rndis_qc(struct usb_configuration *c) * Caller must have called @gether_setup(). Caller is also responsible * for calling @gether_cleanup() before module unload. */ -int -rndis_qc_bind_config(struct usb_configuration *c, u8 ethaddr[ETH_ALEN]) -{ - return rndis_qc_bind_config_vendor(c, ethaddr, 0, NULL, 1, 0, NULL); -} -int -rndis_qc_bind_config_vendor(struct usb_configuration *c, u8 ethaddr[ETH_ALEN], - u32 vendorID, const char *manufacturer, - u8 max_pkt_per_xfer, - u8 pkt_alignment_factor, - char *xport_name) +static struct +usb_function *rndis_qc_bind_config_vendor(struct usb_function_instance *fi, + u32 vendorID, const char *manufacturer, + u8 max_pkt_per_xfer, u8 pkt_alignment_factor) { + struct f_rndis_qc_opts *opts = container_of(fi, + struct f_rndis_qc_opts, func_inst); struct f_rndis_qc *rndis; int status; - if (!can_support_rndis_qc(c) || !ethaddr) { - pr_debug("%s: invalid argument\n", __func__); - return -EINVAL; - } - - /* maybe allocate device-global string IDs */ - if (rndis_qc_string_defs[0].id == 0) { - - /* control interface label */ - status = usb_string_id(c->cdev); - if (status < 0) - return status; - rndis_qc_string_defs[0].id = status; - rndis_qc_control_intf.iInterface = status; - - /* data interface label */ - status = usb_string_id(c->cdev); - if (status < 0) - return status; - rndis_qc_string_defs[1].id = status; - rndis_qc_data_intf.iInterface = status; - - /* IAD iFunction label */ - status = usb_string_id(c->cdev); - if (status < 0) - return status; - rndis_qc_string_defs[2].id = status; - rndis_qc_iad_descriptor.iFunction = status; - } - /* allocate and initialize one new instance */ status = -ENOMEM; - rndis = kzalloc(sizeof(*rndis), GFP_KERNEL); - if (!rndis) { - pr_err("%s: fail allocate and initialize new instance\n", - __func__); - goto fail; - } - rndis->xport = str_to_xport(xport_name); + opts = container_of(fi, struct f_rndis_qc_opts, func_inst); - /* export host's Ethernet address in CDC format */ - if (rndis->xport == USB_GADGET_XPORT_BAM2BAM_IPA) { - gether_qc_get_macs(rndis_ipa_params.device_ethaddr, - rndis_ipa_params.host_ethaddr); - pr_debug("setting host_ethaddr=%pM, device_ethaddr=%pM\n", - rndis_ipa_params.host_ethaddr, - rndis_ipa_params.device_ethaddr); - rndis_ipa_supported = true; - ether_addr_copy(rndis->ethaddr, &rndis_ipa_params.host_ethaddr); - rndis_ipa_params.device_ready_notify = rndis_net_ready_notify; - } else - ether_addr_copy(rndis->ethaddr, ethaddr); + opts->refcnt++; + rndis = opts->rndis; - rndis->vendorID = vendorID; - rndis->manufacturer = manufacturer; + rndis->vendorID = opts->vendor_id; + rndis->manufacturer = opts->manufacturer; + /* export host's Ethernet address in CDC format */ + random_ether_addr(rndis_ipa_params.host_ethaddr); + random_ether_addr(rndis_ipa_params.device_ethaddr); + pr_debug("setting host_ethaddr=%pM, device_ethaddr=%pM\n", + rndis_ipa_params.host_ethaddr, + rndis_ipa_params.device_ethaddr); + rndis_ipa_supported = true; + ether_addr_copy(rndis->ethaddr, rndis_ipa_params.host_ethaddr); + rndis_ipa_params.device_ready_notify = rndis_net_ready_notify; /* if max_pkt_per_xfer was not configured set to default value */ rndis->ul_max_pkt_per_xfer = max_pkt_per_xfer ? max_pkt_per_xfer : DEFAULT_MAX_PKT_PER_XFER; - u_bam_data_set_ul_max_pkt_num(rndis->ul_max_pkt_per_xfer); + ipa_data_set_ul_max_pkt_num(rndis->ul_max_pkt_per_xfer); /* * Check no RNDIS aggregation, and alignment if not mentioned, @@ -1241,47 +1201,35 @@ rndis_qc_bind_config_vendor(struct usb_configuration *c, u8 ethaddr[ETH_ALEN], DEFAULT_PKT_ALIGNMENT_FACTOR; /* RNDIS activates when the host changes this filter */ - rndis->port.cdc_filter = 0; + rndis->cdc_filter = 0; - /* RNDIS has special (and complex) framing */ - rndis->port.header_len = sizeof(struct rndis_packet_msg_type); - rndis->port.wrap = rndis_qc_add_header; - rndis->port.unwrap = rndis_qc_rm_hdr; - - rndis->port.func.name = "rndis"; - rndis->port.func.strings = rndis_qc_strings; + rndis->func.name = "rndis"; + rndis->func.strings = rndis_qc_strings; /* descriptors are per-instance copies */ - rndis->port.func.bind = rndis_qc_bind; - rndis->port.func.unbind = rndis_qc_unbind; - rndis->port.func.set_alt = rndis_qc_set_alt; - rndis->port.func.setup = rndis_qc_setup; - rndis->port.func.disable = rndis_qc_disable; - rndis->port.func.suspend = rndis_qc_suspend; - rndis->port.func.resume = rndis_qc_resume; + rndis->func.bind = rndis_qc_bind; + rndis->func.unbind = rndis_qc_unbind; + rndis->func.set_alt = rndis_qc_set_alt; + rndis->func.setup = rndis_qc_setup; + rndis->func.disable = rndis_qc_disable; + rndis->func.suspend = rndis_qc_suspend; + rndis->func.resume = rndis_qc_resume; + rndis->func.free_func = rndis_qc_free; _rndis_qc = rndis; - if (rndis->xport == USB_GADGET_XPORT_BAM2BAM_IPA) { - status = rndis_ipa_init(&rndis_ipa_params); - if (status) { - pr_err("%s: failed to init rndis_ipa\n", __func__); - goto fail; - } - } - - status = usb_add_function(c, &rndis->port.func); + status = rndis_ipa_init(&rndis_ipa_params); if (status) { - if (rndis->xport == USB_GADGET_XPORT_BAM2BAM_IPA) - rndis_ipa_cleanup(rndis_ipa_params.private); - goto fail; + pr_err("%s: failed to init rndis_ipa\n", __func__); + kfree(rndis); + return ERR_PTR(status); } - return 0; + return &rndis->func; +} -fail: - kfree(rndis); - _rndis_qc = NULL; - return status; +static struct usb_function *qcrndis_alloc(struct usb_function_instance *fi) +{ + return rndis_qc_bind_config_vendor(fi, 0, NULL, 1, 0); } static int rndis_qc_open_dev(struct inode *ip, struct file *fp) @@ -1370,24 +1318,100 @@ static struct miscdevice rndis_qc_device = { .fops = &rndis_qc_fops, }; -static int rndis_qc_init(void) +static void qcrndis_free_inst(struct usb_function_instance *f) { + struct f_rndis_qc *rndis; + struct f_rndis_qc_opts *opts = container_of(f, + struct f_rndis_qc_opts, func_inst); + unsigned long flags; + + rndis = opts->rndis; + misc_deregister(&rndis_qc_device); + + ipa_data_free(USB_IPA_FUNC_RNDIS); + spin_lock_irqsave(&rndis_lock, flags); + kfree(rndis); + _rndis_qc = NULL; + kfree(opts->rndis); + kfree(opts); + spin_unlock_irqrestore(&rndis_lock, flags); +} + +static int qcrndis_set_inst_name(struct usb_function_instance *fi, + const char *name) +{ + struct f_rndis_qc_opts *opts = container_of(fi, + struct f_rndis_qc_opts, func_inst); + struct f_rndis_qc *rndis; + int name_len; int ret; - pr_info("initialize rndis QC instance\n"); + name_len = strlen(name) + 1; + if (name_len > MAX_INST_NAME_LEN) + return -ENAMETOOLONG; + pr_debug("initialize rndis QC instance\n"); + rndis = kzalloc(sizeof(*rndis), GFP_KERNEL); + if (!rndis) { + pr_err("%s: fail allocate and initialize new instance\n", + __func__); + return -ENOMEM; + } + + opts->rndis = rndis; ret = misc_register(&rndis_qc_device); if (ret) pr_err("rndis QC driver failed to register\n"); spin_lock_init(&rndis_lock); - ret = bam_data_setup(USB_FUNC_RNDIS, RNDIS_QC_NO_PORTS); + ret = ipa_data_setup(USB_IPA_FUNC_RNDIS); if (ret) { pr_err("bam_data_setup failed err: %d\n", ret); + kfree(rndis); return ret; } - return ret; + return 0; +} + +static inline +struct f_rndis_qc_opts *to_f_qc_rndis_opts(struct config_item *item) +{ + return container_of(to_config_group(item), struct f_rndis_qc_opts, + func_inst.group); +} + +static void qcrndis_attr_release(struct config_item *item) +{ + struct f_rndis_qc_opts *opts = to_f_qc_rndis_opts(item); + + usb_put_function_instance(&opts->func_inst); +} + +static struct configfs_item_operations qcrndis_item_ops = { + .release = qcrndis_attr_release, +}; + +static struct config_item_type qcrndis_func_type = { + .ct_item_ops = &qcrndis_item_ops, + .ct_owner = THIS_MODULE, +}; + +static struct usb_function_instance *qcrndis_alloc_inst(void) +{ + struct f_rndis_qc_opts *opts; + + opts = kzalloc(sizeof(*opts), GFP_KERNEL); + if (!opts) + return ERR_PTR(-ENOMEM); + + opts->func_inst.set_inst_name = qcrndis_set_inst_name; + opts->func_inst.free_func_inst = qcrndis_free_inst; + + config_group_init_type_name(&opts->func_inst.group, "", + &qcrndis_func_type); + + return &opts->func_inst; } static void rndis_qc_cleanup(void) @@ -1416,3 +1440,27 @@ bool rndis_qc_get_skip_ep_config(void) { return rndis_ipa_params.skip_ep_cfg; } + +DECLARE_USB_FUNCTION_INIT(qcrndis, qcrndis_alloc_inst, qcrndis_alloc); + +static int __init usb_qcrndis_init(void) +{ + int ret; + + ret = usb_function_register(&qcrndisusb_func); + if (ret) { + pr_err("%s: failed to register diag %d\n", __func__, ret); + return ret; + } + return ret; +} + +static void __exit usb_qcrndis_exit(void) +{ + usb_function_unregister(&qcrndisusb_func); + rndis_qc_cleanup(); +} + +module_init(usb_qcrndis_init); +module_exit(usb_qcrndis_exit); +MODULE_DESCRIPTION("USB RMNET Function Driver"); diff --git a/drivers/usb/gadget/function/rndis.c b/drivers/usb/gadget/function/rndis.c index b0e7b65b84bd..98ac1ff58323 100644 --- a/drivers/usb/gadget/function/rndis.c +++ b/drivers/usb/gadget/function/rndis.c @@ -596,6 +596,7 @@ static int rndis_init_response(struct rndis_params *params, resp->AFListOffset = cpu_to_le32(0); resp->AFListSize = cpu_to_le32(0); + params->ul_max_xfer_size = le32_to_cpu(resp->MaxTransferSize); params->resp_avail(params->v); return 0; } @@ -799,7 +800,7 @@ EXPORT_SYMBOL_GPL(rndis_set_host_mac); */ int rndis_msg_parser(struct rndis_params *params, u8 *buf) { - u32 MsgType, MsgLength; + u32 MsgType, MsgLength, major, minor, max_transfer_size; __le32 *tmp; if (!buf) @@ -822,6 +823,19 @@ int rndis_msg_parser(struct rndis_params *params, u8 *buf) case RNDIS_MSG_INIT: pr_debug("%s: RNDIS_MSG_INIT\n", __func__); + major = get_unaligned_le32(tmp++); + minor = get_unaligned_le32(tmp++); + max_transfer_size = get_unaligned_le32(tmp++); + + params->host_rndis_major_ver = major; + params->host_rndis_minor_ver = minor; + params->dl_max_xfer_size = max_transfer_size; + + pr_debug("%s(): RNDIS Host Major:%d Minor:%d version\n", + __func__, major, minor); + pr_debug("%s(): UL Max Transfer size:%x\n", __func__, + max_transfer_size); + params->state = RNDIS_INITIALIZED; return rndis_init_response(params, (rndis_init_msg_type *)buf); @@ -1013,6 +1027,18 @@ int rndis_set_param_medium(struct rndis_params *params, u32 medium, u32 speed) } EXPORT_SYMBOL_GPL(rndis_set_param_medium); +u32 rndis_get_dl_max_xfer_size(struct rndis_params *params) +{ + pr_debug("%s:\n", __func__); + return params->dl_max_xfer_size; +} + +u32 rndis_get_ul_max_xfer_size(struct rndis_params *params) +{ + pr_debug("%s:\n", __func__); + return params->ul_max_xfer_size; +} + void rndis_set_max_pkt_xfer(struct rndis_params *params, u8 max_pkt_per_xfer) { pr_debug("%s:\n", __func__); diff --git a/drivers/usb/gadget/function/rndis.h b/drivers/usb/gadget/function/rndis.h index 939c3bebe015..3d130b0576fc 100644 --- a/drivers/usb/gadget/function/rndis.h +++ b/drivers/usb/gadget/function/rndis.h @@ -199,6 +199,10 @@ typedef struct rndis_params void *v; struct list_head resp_queue; + u32 host_rndis_major_ver; + u32 host_rndis_minor_ver; + u32 ul_max_xfer_size; + u32 dl_max_xfer_size; } rndis_params; /* RNDIS Message parser and other useless functions */ @@ -213,6 +217,8 @@ int rndis_set_param_vendor(struct rndis_params *params, u32 vendorID, int rndis_set_param_medium(struct rndis_params *params, u32 medium, u32 speed); void rndis_set_max_pkt_xfer(struct rndis_params *params, u8 max_pkt_per_xfer); +u32 rndis_get_ul_max_xfer_size(struct rndis_params *params); +u32 rndis_get_dl_max_xfer_size(struct rndis_params *params); void rndis_add_hdr(struct sk_buff *skb); int rndis_rm_hdr(struct gether *port, struct sk_buff *skb, struct sk_buff_head *list); diff --git a/drivers/usb/gadget/function/u_data_ipa.c b/drivers/usb/gadget/function/u_data_ipa.c index 3a5b1e2da2e6..56e7dea427ec 100644 --- a/drivers/usb/gadget/function/u_data_ipa.c +++ b/drivers/usb/gadget/function/u_data_ipa.c @@ -22,37 +22,49 @@ #include <linux/termios.h> #include <linux/usb_bam.h> -#include "usb_gadget_xport.h" +#include "u_data_ipa.h" -#define IPA_N_PORTS 4 struct ipa_data_ch_info { - struct usb_request *rx_req; - struct usb_request *tx_req; - unsigned long flags; - unsigned id; - enum transport_type trans; - enum gadget_type gtype; - bool is_connected; - unsigned port_num; - spinlock_t port_lock; - - struct work_struct connect_w; - struct work_struct disconnect_w; - struct work_struct suspend_w; - struct work_struct resume_w; - - u32 src_pipe_idx; - u32 dst_pipe_idx; - u8 src_connection_idx; - u8 dst_connection_idx; - enum usb_ctrl usb_bam_type; - struct gadget_ipa_port *port_usb; + struct usb_request *rx_req; + struct usb_request *tx_req; + unsigned long flags; + unsigned id; + enum ipa_func_type func_type; + bool is_connected; + unsigned port_num; + spinlock_t port_lock; + + struct work_struct connect_w; + struct work_struct disconnect_w; + struct work_struct suspend_w; + struct work_struct resume_w; + + u32 src_pipe_idx; + u32 dst_pipe_idx; + u8 src_connection_idx; + u8 dst_connection_idx; + enum usb_ctrl usb_bam_type; + struct gadget_ipa_port *port_usb; + struct usb_gadget *gadget; + atomic_t pipe_connect_notified; struct usb_bam_connect_ipa_params ipa_params; }; -static int n_ipa_ports; +struct rndis_data_ch_info { + /* this provides downlink (device->host i.e host) side configuration*/ + u32 dl_max_transfer_size; + /* this provides uplink (host->device i.e device) side configuration */ + u32 ul_max_transfer_size; + u32 ul_max_packets_number; + bool ul_aggregation_enable; + u32 prod_clnt_hdl; + u32 cons_clnt_hdl; + void *priv; +}; + static struct workqueue_struct *ipa_data_wq; struct ipa_data_ch_info *ipa_data_ports[IPA_N_PORTS]; +static struct rndis_data_ch_info *rndis_data; /** * ipa_data_endless_complete() - completion callback for endless TX/RX request * @ep: USB endpoint for which this completion happen @@ -132,6 +144,56 @@ static void ipa_data_stop_endless_xfer(struct ipa_data_ch_info *port, bool in) } } +/* + * Called when IPA triggers us that the network interface is up. + * Starts the transfers on bulk endpoints. + * (optimization reasons, the pipes and bam with IPA are already connected) + */ +void ipa_data_start_rx_tx(enum ipa_func_type func) +{ + struct ipa_data_ch_info *port; + unsigned long flags; + + pr_debug("%s: Triggered: starting tx, rx", __func__); + /* queue in & out requests */ + port = ipa_data_ports[func]; + if (!port) { + pr_err("%s: port is NULL, can't start tx, rx", __func__); + return; + } + + spin_lock_irqsave(&port->port_lock, flags); + + if (!port->port_usb || !port->port_usb->in || + !port->port_usb->out) { + pr_err("%s: Can't start tx, rx, ep not enabled", __func__); + spin_unlock_irqrestore(&port->port_lock, flags); + return; + } + + if (!port->rx_req || !port->tx_req) { + pr_err("%s: No request d->rx_req=%p, d->tx_req=%p", __func__, + port->rx_req, port->tx_req); + spin_unlock_irqrestore(&port->port_lock, flags); + return; + } + if (!port->is_connected) { + pr_debug("%s: pipes are disconnected", __func__); + spin_unlock_irqrestore(&port->port_lock, flags); + return; + } + + spin_unlock_irqrestore(&port->port_lock, flags); + + /* queue in & out requests */ + pr_debug("%s: Starting rx", __func__); + if (port->port_usb->out) + ipa_data_start_endless_xfer(port, false); + + pr_debug("%s: Starting tx", __func__); + if (port->port_usb->in) + ipa_data_start_endless_xfer(port, true); +} /** * ipa_data_disconnect_work() - Perform USB IPA BAM disconnect * @w: disconnect work @@ -166,6 +228,23 @@ static void ipa_data_disconnect_work(struct work_struct *w) if (ret) pr_err("usb_bam_disconnect_ipa failed: err:%d\n", ret); + if (port->func_type == USB_IPA_FUNC_RNDIS) { + /* + * NOTE: it is required to disconnect USB and IPA BAM related + * pipes before calling IPA tethered function related disconnect + * API. IPA tethered function related disconnect API delete + * depedency graph with IPA RM which would results into IPA not + * pulling data although there is pending data on USB BAM + * producer pipe. + */ + if (atomic_xchg(&port->pipe_connect_notified, 0) == 1) { + void *priv; + + priv = rndis_qc_get_ipa_priv(); + rndis_ipa_pipe_disconnect_notify(priv); + } + } + if (port->ipa_params.prod_clnt_hdl) usb_bam_free_fifos(port->usb_bam_type, port->dst_connection_idx); @@ -173,6 +252,12 @@ static void ipa_data_disconnect_work(struct work_struct *w) usb_bam_free_fifos(port->usb_bam_type, port->src_connection_idx); + /* + * Decrement usage count which was incremented + * upon cable connect or cable disconnect in suspended state. + */ + usb_gadget_autopm_put_async(port->gadget); + pr_debug("%s(): disconnect work completed.\n", __func__); } @@ -186,15 +271,15 @@ static void ipa_data_disconnect_work(struct work_struct *w) * switch is being trigger. This API performs restoring USB endpoint operation * and disable USB endpoint used for accelerated path. */ -void ipa_data_disconnect(struct gadget_ipa_port *gp, u8 port_num) +void ipa_data_disconnect(struct gadget_ipa_port *gp, enum ipa_func_type func) { struct ipa_data_ch_info *port; unsigned long flags; struct usb_gadget *gadget = NULL; - pr_debug("dev:%p port number:%d\n", gp, port_num); - if (port_num >= n_ipa_ports) { - pr_err("invalid ipa portno#%d\n", port_num); + pr_debug("dev:%p port number:%d\n", gp, func); + if (func >= USB_IPA_NUM_FUNCS) { + pr_err("invalid ipa portno#%d\n", func); return; } @@ -203,9 +288,9 @@ void ipa_data_disconnect(struct gadget_ipa_port *gp, u8 port_num) return; } - port = ipa_data_ports[port_num]; + port = ipa_data_ports[func]; if (!port) { - pr_err("port %u is NULL", port_num); + pr_err("port %u is NULL", func); return; } @@ -223,8 +308,7 @@ void ipa_data_disconnect(struct gadget_ipa_port *gp, u8 port_num) * complete function will be called, where we try * to obtain the spinlock as well. */ - if (gadget_is_dwc3(gadget)) - msm_ep_unconfig(port->port_usb->in); + msm_ep_unconfig(port->port_usb->in); spin_unlock_irqrestore(&port->port_lock, flags); usb_ep_disable(port->port_usb->in); spin_lock_irqsave(&port->port_lock, flags); @@ -232,8 +316,7 @@ void ipa_data_disconnect(struct gadget_ipa_port *gp, u8 port_num) } if (port->port_usb->out) { - if (gadget_is_dwc3(gadget)) - msm_ep_unconfig(port->port_usb->out); + msm_ep_unconfig(port->port_usb->out); spin_unlock_irqrestore(&port->port_lock, flags); usb_ep_disable(port->port_usb->out); spin_lock_irqsave(&port->port_lock, flags); @@ -257,14 +340,14 @@ void ipa_data_disconnect(struct gadget_ipa_port *gp, u8 port_num) */ static void configure_fifo(enum usb_ctrl bam_type, u8 idx, struct usb_ep *ep) { - struct u_bam_data_connect_info bam_info; struct sps_mem_buffer data_fifo = {0}; + u32 usb_bam_pipe_idx; get_bam2bam_connection_info(bam_type, idx, - &bam_info.usb_bam_pipe_idx, + &usb_bam_pipe_idx, NULL, &data_fifo, NULL); msm_data_fifo_config(ep, data_fifo.phys_base, data_fifo.size, - bam_info.usb_bam_pipe_idx); + usb_bam_pipe_idx); } /** @@ -308,8 +391,21 @@ static void ipa_data_connect_work(struct work_struct *w) return; } + /* + * check if connect_w got called two times during RNDIS resume as + * explicit flow control is called to start data transfers after + * ipa_data_connect() + */ + if (port->is_connected) { + pr_debug("IPA connect is already done & Transfers started\n"); + spin_unlock_irqrestore(&port->port_lock, flags); + usb_gadget_autopm_put_async(port->gadget); + return; + } + gport->ipa_consumer_ep = -1; gport->ipa_producer_ep = -1; + if (gport->out) { port->rx_req = usb_ep_alloc_request(gport->out, GFP_ATOMIC); if (!port->rx_req) { @@ -341,8 +437,7 @@ static void ipa_data_connect_work(struct work_struct *w) /* update IPA Parameteres here. */ port->ipa_params.usb_connection_speed = gadget->speed; - if (gadget_is_dwc3(gadget)) - port->ipa_params.reset_pipe_after_lpm = + port->ipa_params.reset_pipe_after_lpm = msm_dwc3_reset_ep_after_lpm(gadget); port->ipa_params.skip_ep_cfg = true; port->ipa_params.keep_ipa_awake = true; @@ -354,49 +449,35 @@ static void ipa_data_connect_work(struct work_struct *w) usb_bam_alloc_fifos(port->usb_bam_type, port->src_connection_idx); - if (gadget_is_dwc3(gadget)) { - sps_params = MSM_SPS_MODE | MSM_DISABLE_WB - | MSM_PRODUCER | port->src_pipe_idx; - port->rx_req->length = 32*1024; - port->rx_req->udc_priv = sps_params; - configure_fifo(port->usb_bam_type, - port->src_connection_idx, - port->port_usb->out); - ret = msm_ep_config(gport->out, port->rx_req, - GFP_ATOMIC); - if (ret) { - pr_err("msm_ep_config() failed for OUT EP\n"); - usb_bam_free_fifos(port->usb_bam_type, - port->src_connection_idx); - goto free_rx_tx_req; - } - } else { - sps_params = (MSM_SPS_MODE | port->src_pipe_idx | - MSM_VENDOR_ID) & ~MSM_IS_FINITE_TRANSFER; - port->rx_req->udc_priv = sps_params; + sps_params = MSM_SPS_MODE | MSM_DISABLE_WB + | MSM_PRODUCER | port->src_pipe_idx; + port->rx_req->length = 32*1024; + port->rx_req->udc_priv = sps_params; + configure_fifo(port->usb_bam_type, + port->src_connection_idx, + port->port_usb->out); + ret = msm_ep_config(gport->out); + if (ret) { + pr_err("msm_ep_config() failed for OUT EP\n"); + usb_bam_free_fifos(port->usb_bam_type, + port->src_connection_idx); + goto free_rx_tx_req; } } if (gport->in) { usb_bam_alloc_fifos(port->usb_bam_type, port->dst_connection_idx); - if (gadget_is_dwc3(gadget)) { - sps_params = MSM_SPS_MODE | MSM_DISABLE_WB | - port->dst_pipe_idx; - port->tx_req->length = 32*1024; - port->tx_req->udc_priv = sps_params; - configure_fifo(port->usb_bam_type, - port->dst_connection_idx, gport->in); - ret = msm_ep_config(gport->in, port->tx_req, - GFP_ATOMIC); - if (ret) { - pr_err("msm_ep_config() failed for IN EP\n"); - goto unconfig_msm_ep_out; - } - } else { - sps_params = (MSM_SPS_MODE | port->dst_pipe_idx | - MSM_VENDOR_ID) & ~MSM_IS_FINITE_TRANSFER; - port->tx_req->udc_priv = sps_params; + sps_params = MSM_SPS_MODE | MSM_DISABLE_WB | + port->dst_pipe_idx; + port->tx_req->length = 32*1024; + port->tx_req->udc_priv = sps_params; + configure_fifo(port->usb_bam_type, + port->dst_connection_idx, gport->in); + ret = msm_ep_config(gport->in); + if (ret) { + pr_err("msm_ep_config() failed for IN EP\n"); + goto unconfig_msm_ep_out; } } @@ -410,13 +491,20 @@ static void ipa_data_connect_work(struct work_struct *w) if (gport->out) { pr_debug("configure bam ipa connect for USB OUT\n"); port->ipa_params.dir = USB_TO_PEER_PERIPHERAL; + + if (port->func_type == USB_IPA_FUNC_RNDIS) { + port->ipa_params.notify = rndis_qc_get_ipa_rx_cb(); + port->ipa_params.priv = rndis_qc_get_ipa_priv(); + port->ipa_params.skip_ep_cfg = + rndis_qc_get_skip_ep_config(); + } + ret = usb_bam_connect_ipa(port->usb_bam_type, &port->ipa_params); if (ret) { pr_err("usb_bam_connect_ipa out failed err:%d\n", ret); goto unconfig_msm_ep_in; } - gadget->bam2bam_func_enabled = true; gport->ipa_consumer_ep = port->ipa_params.ipa_cons_ep_idx; is_ipa_disconnected = false; @@ -425,30 +513,71 @@ static void ipa_data_connect_work(struct work_struct *w) if (gport->in) { pr_debug("configure bam ipa connect for USB IN\n"); port->ipa_params.dir = PEER_PERIPHERAL_TO_USB; - port->ipa_params.dst_client = IPA_CLIENT_USB_DPL_CONS; + + if (port->func_type == USB_IPA_FUNC_RNDIS) { + port->ipa_params.notify = rndis_qc_get_ipa_tx_cb(); + port->ipa_params.priv = rndis_qc_get_ipa_priv(); + port->ipa_params.skip_ep_cfg = + rndis_qc_get_skip_ep_config(); + } + + if (port->func_type == USB_IPA_FUNC_DPL) + port->ipa_params.dst_client = IPA_CLIENT_USB_DPL_CONS; ret = usb_bam_connect_ipa(port->usb_bam_type, &port->ipa_params); if (ret) { pr_err("usb_bam_connect_ipa IN failed err:%d\n", ret); goto disconnect_usb_bam_ipa_out; } - gadget->bam2bam_func_enabled = true; gport->ipa_producer_ep = port->ipa_params.ipa_prod_ep_idx; is_ipa_disconnected = false; } - pr_debug("ipa_producer_ep:%d ipa_consumer_ep:%d\n", - gport->ipa_producer_ep, - gport->ipa_consumer_ep); + /* For DPL need to update_ipa_pipes to qti */ + if (port->func_type == USB_IPA_FUNC_RNDIS) { + rndis_data->prod_clnt_hdl = + port->ipa_params.prod_clnt_hdl; + rndis_data->cons_clnt_hdl = + port->ipa_params.cons_clnt_hdl; + rndis_data->priv = port->ipa_params.priv; + + pr_debug("ul_max_transfer_size:%d\n", + rndis_data->ul_max_transfer_size); + pr_debug("ul_max_packets_number:%d\n", + rndis_data->ul_max_packets_number); + pr_debug("dl_max_transfer_size:%d\n", + rndis_data->dl_max_transfer_size); + + ret = rndis_ipa_pipe_connect_notify( + rndis_data->cons_clnt_hdl, + rndis_data->prod_clnt_hdl, + rndis_data->ul_max_transfer_size, + rndis_data->ul_max_packets_number, + rndis_data->dl_max_transfer_size, + rndis_data->priv); + if (ret) { + pr_err("%s: failed to connect IPA: err:%d\n", + __func__, ret); + return; + } + atomic_set(&port->pipe_connect_notified, 1); + } - gqti_ctrl_update_ipa_pipes(NULL, DPL_QTI_CTRL_PORT_NO, + pr_debug("ipa_producer_ep:%d ipa_consumer_ep:%d\n", gport->ipa_producer_ep, gport->ipa_consumer_ep); pr_debug("src_bam_idx:%d dst_bam_idx:%d\n", port->src_connection_idx, port->dst_connection_idx); + /* Don't queue the transfers yet, only after network stack is up */ + if (port->func_type == USB_IPA_FUNC_RNDIS) { + pr_debug("%s: Not starting now, waiting for network notify", + __func__); + return; + } + if (gport->out) ipa_data_start_endless_xfer(port, false); if (gport->in) @@ -496,7 +625,7 @@ free_rx_req: * initiate USB BAM IPA connection. This API is enabling accelerated endpoints * and schedule connect_work() which establishes USB IPA BAM communication. */ -int ipa_data_connect(struct gadget_ipa_port *gp, u8 port_num, +int ipa_data_connect(struct gadget_ipa_port *gp, enum ipa_func_type func, u8 src_connection_idx, u8 dst_connection_idx) { struct ipa_data_ch_info *port; @@ -504,10 +633,10 @@ int ipa_data_connect(struct gadget_ipa_port *gp, u8 port_num, int ret; pr_debug("dev:%p port#%d src_connection_idx:%d dst_connection_idx:%d\n", - gp, port_num, src_connection_idx, dst_connection_idx); + gp, func, src_connection_idx, dst_connection_idx); - if (port_num >= n_ipa_ports) { - pr_err("invalid portno#%d\n", port_num); + if (func >= USB_IPA_NUM_FUNCS) { + pr_err("invalid portno#%d\n", func); ret = -ENODEV; goto err; } @@ -518,10 +647,11 @@ int ipa_data_connect(struct gadget_ipa_port *gp, u8 port_num, goto err; } - port = ipa_data_ports[port_num]; + port = ipa_data_ports[func]; spin_lock_irqsave(&port->port_lock, flags); port->port_usb = gp; + port->gadget = gp->cdev->gadget; port->src_connection_idx = src_connection_idx; port->dst_connection_idx = dst_connection_idx; port->usb_bam_type = usb_bam_get_bam_type(gp->cdev->gadget->name); @@ -565,6 +695,19 @@ int ipa_data_connect(struct gadget_ipa_port *gp, u8 port_num, goto err_usb_in; } + /* Wait for host to enable flow_control */ + if (port->func_type == USB_IPA_FUNC_RNDIS) { + ret = 0; + goto err_usb_in; + } + + /* + * Increment usage count upon cable connect. Decrement after IPA + * handshake is done in disconnect work (due to cable disconnect) + * or in suspend work. + */ + usb_gadget_autopm_get_noresume(port->gadget); + queue_work(ipa_data_wq, &port->connect_w); spin_unlock_irqrestore(&port->port_lock, flags); @@ -642,6 +785,12 @@ static void ipa_data_stop(void *param, enum usb_bam_pipe_dir dir) } } +void ipa_data_flush_workqueue(void) +{ + pr_debug("%s(): Flushing workqueue\n", __func__); + flush_workqueue(ipa_data_wq); +} + /** * ipa_data_suspend() - Initiate USB BAM IPA suspend functionality * @gp: Gadget IPA port @@ -650,15 +799,14 @@ static void ipa_data_stop(void *param, enum usb_bam_pipe_dir dir) * It is being used to initiate USB BAM IPA suspend functionality * for USB bus suspend functionality. */ -void ipa_data_suspend(struct gadget_ipa_port *gp, u8 port_num) +void ipa_data_suspend(struct gadget_ipa_port *gp, enum ipa_func_type func, + bool remote_wakeup_enabled) { struct ipa_data_ch_info *port; - int ret; - - pr_debug("dev:%p port number:%d\n", gp, port_num); + unsigned long flags; - if (port_num >= n_ipa_ports) { - pr_err("invalid ipa portno#%d\n", port_num); + if (func >= USB_IPA_NUM_FUNCS) { + pr_err("invalid ipa portno#%d\n", func); return; } @@ -666,14 +814,61 @@ void ipa_data_suspend(struct gadget_ipa_port *gp, u8 port_num) pr_err("data port is null\n"); return; } + pr_debug("%s: suspended port %d\n", __func__, func); - port = ipa_data_ports[port_num]; + port = ipa_data_ports[func]; if (!port) { - pr_err("port %u is NULL", port_num); + pr_err("%s(): Port is NULL.\n", __func__); + return; + } + + /* suspend with remote wakeup disabled */ + if (!remote_wakeup_enabled) { + /* + * When remote wakeup is disabled, IPA BAM is disconnected + * because it cannot send new data until the USB bus is resumed. + * Endpoint descriptors info is saved before it gets reset by + * the BAM disconnect API. This lets us restore this info when + * the USB bus is resumed. + */ + gp->in_ep_desc_backup = gp->in->desc; + gp->out_ep_desc_backup = gp->out->desc; + + pr_debug("in_ep_desc_backup = %p, out_ep_desc_backup = %p", + gp->in_ep_desc_backup, + gp->out_ep_desc_backup); + + ipa_data_disconnect(gp, func); return; } + spin_lock_irqsave(&port->port_lock, flags); + queue_work(ipa_data_wq, &port->suspend_w); + spin_unlock_irqrestore(&port->port_lock, flags); +} +static void bam2bam_data_suspend_work(struct work_struct *w) +{ + struct ipa_data_ch_info *port = container_of(w, struct ipa_data_ch_info, + connect_w); + unsigned long flags; + int ret; + pr_debug("%s: suspend started\n", __func__); + spin_lock_irqsave(&port->port_lock, flags); + + /* In case of RNDIS, host enables flow_control invoking connect_w. If it + * is delayed then we may end up having suspend_w run before connect_w. + * In this scenario, connect_w may or may not at all start if cable gets + * disconnected or if host changes configuration e.g. RNDIS --> MBIM + * For these cases don't do runtime_put as there was no _get yet, and + * detect this condition on disconnect to not do extra pm_runtme_get + * for SUSPEND --> DISCONNECT scenario. + */ + if (!port->is_connected) { + pr_err("%s: Not yet connected. SUSPEND pending.\n", __func__); + spin_unlock_irqrestore(&port->port_lock, flags); + return; + } ret = usb_bam_register_wake_cb(port->usb_bam_type, port->dst_connection_idx, NULL, port); if (ret) { @@ -685,7 +880,23 @@ void ipa_data_suspend(struct gadget_ipa_port *gp, u8 port_num) usb_bam_register_start_stop_cbs(port->usb_bam_type, port->dst_connection_idx, ipa_data_start, ipa_data_stop, port); + /* + * release lock here because bam_data_start() or + * bam_data_stop() called from usb_bam_suspend() + * re-acquires port lock. + */ + spin_unlock_irqrestore(&port->port_lock, flags); usb_bam_suspend(port->usb_bam_type, &port->ipa_params); + spin_lock_irqsave(&port->port_lock, flags); + + /* + * Decrement usage count after IPA handshake is done + * to allow gadget parent to go to lpm. This counter was + * incremented upon cable connect. + */ + usb_gadget_autopm_put_async(port->gadget); + + spin_unlock_irqrestore(&port->port_lock, flags); } /** @@ -696,17 +907,20 @@ void ipa_data_suspend(struct gadget_ipa_port *gp, u8 port_num) * It is being used to initiate USB resume functionality * for USB bus resume case. */ -void ipa_data_resume(struct gadget_ipa_port *gp, u8 port_num) +void ipa_data_resume(struct gadget_ipa_port *gp, enum ipa_func_type func, + bool remote_wakeup_enabled) { struct ipa_data_ch_info *port; unsigned long flags; struct usb_gadget *gadget = NULL; - int ret; + u8 src_connection_idx; + u8 dst_connection_idx; + enum usb_ctrl usb_bam_type; - pr_debug("dev:%p port number:%d\n", gp, port_num); + pr_debug("dev:%p port number:%d\n", gp, func); - if (port_num >= n_ipa_ports) { - pr_err("invalid ipa portno#%d\n", port_num); + if (func >= USB_IPA_NUM_FUNCS) { + pr_err("invalid ipa portno#%d\n", func); return; } @@ -715,12 +929,66 @@ void ipa_data_resume(struct gadget_ipa_port *gp, u8 port_num) return; } - port = ipa_data_ports[port_num]; + port = ipa_data_ports[func]; if (!port) { - pr_err("port %u is NULL", port_num); + pr_err("port %u is NULL", func); + return; + } + + gadget = gp->cdev->gadget; + /* resume with remote wakeup disabled */ + if (!remote_wakeup_enabled) { + /* Restore endpoint descriptors info. */ + gp->in->desc = gp->in_ep_desc_backup; + gp->out->desc = gp->out_ep_desc_backup; + + pr_debug("in_ep_desc_backup = %p, out_ep_desc_backup = %p", + gp->in_ep_desc_backup, + gp->out_ep_desc_backup); + usb_bam_type = usb_bam_get_bam_type(gadget->name); + src_connection_idx = usb_bam_get_connection_idx(usb_bam_type, + IPA_P_BAM, USB_TO_PEER_PERIPHERAL, USB_BAM_DEVICE, + 0); + dst_connection_idx = usb_bam_get_connection_idx(usb_bam_type, + IPA_P_BAM, PEER_PERIPHERAL_TO_USB, USB_BAM_DEVICE, + 0); + ipa_data_connect(gp, func, + src_connection_idx, dst_connection_idx); return; } + spin_lock_irqsave(&port->port_lock, flags); + + /* + * Increment usage count here to disallow gadget + * parent suspend. This counter will decrement + * after IPA handshake is done in disconnect work + * (due to cable disconnect) or in bam_data_disconnect + * in suspended state. + */ + usb_gadget_autopm_get_noresume(port->gadget); + queue_work(ipa_data_wq, &port->resume_w); + spin_unlock_irqrestore(&port->port_lock, flags); +} + +static void bam2bam_data_resume_work(struct work_struct *w) +{ + struct ipa_data_ch_info *port = container_of(w, struct ipa_data_ch_info, + connect_w); + struct usb_gadget *gadget; + unsigned long flags; + int ret; + + if (!port->port_usb->cdev) { + pr_err("!port->port_usb->cdev is NULL"); + goto exit; + } + + if (!port->port_usb->cdev->gadget) { + pr_err("!port->port_usb->cdev->gadget is NULL"); + goto exit; + } + pr_debug("%s: resume started\n", __func__); spin_lock_irqsave(&port->port_lock, flags); gadget = port->port_usb->cdev->gadget; @@ -750,6 +1018,7 @@ void ipa_data_resume(struct gadget_ipa_port *gp, u8 port_num) usb_bam_resume(port->usb_bam_type, &port->ipa_params); } +exit: spin_unlock_irqrestore(&port->port_lock, flags); } @@ -762,12 +1031,12 @@ void ipa_data_resume(struct gadget_ipa_port *gp, u8 port_num) * * Retrun: 0 in case of success, otherwise errno. */ -static int ipa_data_port_alloc(int portno) +static int ipa_data_port_alloc(enum ipa_func_type func) { struct ipa_data_ch_info *port = NULL; - if (ipa_data_ports[portno] != NULL) { - pr_debug("port %d already allocated.\n", portno); + if (ipa_data_ports[func] != NULL) { + pr_debug("port %d already allocated.\n", func); return 0; } @@ -775,29 +1044,29 @@ static int ipa_data_port_alloc(int portno) if (!port) return -ENOMEM; - ipa_data_ports[portno] = port; + ipa_data_ports[func] = port; - pr_debug("port:%p with portno:%d allocated\n", port, portno); + pr_debug("port:%p with portno:%d allocated\n", port, func); return 0; } /** * ipa_data_port_select() - Select particular port for BAM2BAM IPA mode * @portno: port number to be used by particular USB function - * @gtype: USB gadget function type + * @func_type: USB gadget function type * * It is being used by USB function driver to select which BAM2BAM IPA * port particular USB function wants to use. * */ -void ipa_data_port_select(int portno, enum gadget_type gtype) +void ipa_data_port_select(enum ipa_func_type func) { struct ipa_data_ch_info *port = NULL; - pr_debug("portno:%d\n", portno); + pr_debug("portno:%d\n", func); - port = ipa_data_ports[portno]; - port->port_num = portno; + port = ipa_data_ports[func]; + port->port_num = func; port->is_connected = false; spin_lock_init(&port->port_lock); @@ -808,14 +1077,30 @@ void ipa_data_port_select(int portno, enum gadget_type gtype) if (!work_pending(&port->disconnect_w)) INIT_WORK(&port->disconnect_w, ipa_data_disconnect_work); + INIT_WORK(&port->suspend_w, bam2bam_data_suspend_work); + INIT_WORK(&port->resume_w, bam2bam_data_resume_work); + port->ipa_params.src_client = IPA_CLIENT_USB_PROD; port->ipa_params.dst_client = IPA_CLIENT_USB_CONS; - port->gtype = gtype; + port->func_type = func; }; +void ipa_data_free(enum ipa_func_type func) +{ + pr_debug("freeing %d IPA BAM port", func); + + kfree(ipa_data_ports[func]); + ipa_data_ports[func] = NULL; + if (func == USB_IPA_FUNC_RNDIS) + kfree(rndis_data); + if (ipa_data_wq) { + destroy_workqueue(ipa_data_wq); + ipa_data_wq = NULL; + } +} + /** * ipa_data_setup() - setup BAM2BAM IPA port - * @no_ipa_port: total number of BAM2BAM IPA port to support * * Each USB function who wants to use BAM2BAM IPA port would * be counting number of IPA port to use and initialize those @@ -823,32 +1108,34 @@ void ipa_data_port_select(int portno, enum gadget_type gtype) * * Retrun: 0 in case of success, otherwise errno. */ -int ipa_data_setup(unsigned int no_ipa_port) +int ipa_data_setup(enum ipa_func_type func) { - int i, ret; + int ret; - pr_debug("requested %d IPA BAM ports", no_ipa_port); + pr_debug("requested %d IPA BAM port", func); - if (!no_ipa_port || no_ipa_port > IPA_N_PORTS) { - pr_err("Invalid num of ports count:%d\n", no_ipa_port); + if (func >= USB_IPA_NUM_FUNCS) { + pr_err("Invalid num of ports count:%d\n", func); return -EINVAL; } - for (i = 0; i < no_ipa_port; i++) { - n_ipa_ports++; - ret = ipa_data_port_alloc(i); - if (ret) { - n_ipa_ports--; - pr_err("Failed to alloc port:%d\n", i); + ret = ipa_data_port_alloc(func); + if (ret) { + pr_err("Failed to alloc port:%d\n", func); + return ret; + } + + if (func == USB_IPA_FUNC_RNDIS) { + rndis_data = kzalloc(sizeof(*rndis_data), GFP_KERNEL); + if (!rndis_data) { + pr_err("%s: fail allocate and initialize new instance\n", + __func__); goto free_ipa_ports; } } - - pr_debug("n_ipa_ports:%d\n", n_ipa_ports); - if (ipa_data_wq) { pr_debug("ipa_data_wq is already setup."); - return 0; + goto free_rndis_data; } ipa_data_wq = alloc_workqueue("k_usb_ipa_data", @@ -856,20 +1143,111 @@ int ipa_data_setup(unsigned int no_ipa_port) if (!ipa_data_wq) { pr_err("Failed to create workqueue\n"); ret = -ENOMEM; - goto free_ipa_ports; + goto free_rndis_data; } return 0; +free_rndis_data: + if (func == USB_IPA_FUNC_RNDIS) + kfree(rndis_data); free_ipa_ports: - for (i = 0; i < n_ipa_ports; i++) { - kfree(ipa_data_ports[i]); - ipa_data_ports[i] = NULL; - if (ipa_data_wq) { - destroy_workqueue(ipa_data_wq); - ipa_data_wq = NULL; - } - } + kfree(ipa_data_ports[func]); + ipa_data_ports[func] = NULL; return ret; } + +void ipa_data_set_ul_max_xfer_size(u32 max_transfer_size) +{ + if (!max_transfer_size) { + pr_err("%s: invalid parameters\n", __func__); + return; + } + rndis_data->ul_max_transfer_size = max_transfer_size; + pr_debug("%s(): ul_max_xfer_size:%d\n", __func__, max_transfer_size); +} + +void ipa_data_set_dl_max_xfer_size(u32 max_transfer_size) +{ + + if (!max_transfer_size) { + pr_err("%s: invalid parameters\n", __func__); + return; + } + rndis_data->dl_max_transfer_size = max_transfer_size; + pr_debug("%s(): dl_max_xfer_size:%d\n", __func__, max_transfer_size); +} + +void ipa_data_set_ul_max_pkt_num(u8 max_packets_number) +{ + if (!max_packets_number) { + pr_err("%s: invalid parameters\n", __func__); + return; + } + + rndis_data->ul_max_packets_number = max_packets_number; + + if (max_packets_number > 1) + rndis_data->ul_aggregation_enable = true; + else + rndis_data->ul_aggregation_enable = false; + + pr_debug("%s(): ul_aggregation enable:%d ul_max_packets_number:%d\n", + __func__, rndis_data->ul_aggregation_enable, + max_packets_number); +} + +void ipa_data_start_rndis_ipa(enum ipa_func_type func) +{ + struct ipa_data_ch_info *port; + + pr_debug("%s\n", __func__); + + port = ipa_data_ports[func]; + if (!port) { + pr_err("%s: port is NULL", __func__); + return; + } + + if (atomic_read(&port->pipe_connect_notified)) { + pr_debug("%s: Transfers already started?\n", __func__); + return; + } + /* + * Increment usage count upon cable connect. Decrement after IPA + * handshake is done in disconnect work due to cable disconnect + * or in suspend work. + */ + usb_gadget_autopm_get_noresume(port->gadget); + queue_work(ipa_data_wq, &port->connect_w); +} + +void ipa_data_stop_rndis_ipa(enum ipa_func_type func) +{ + struct ipa_data_ch_info *port; + unsigned long flags; + + pr_debug("%s\n", __func__); + + port = ipa_data_ports[func]; + if (!port) { + pr_err("%s: port is NULL", __func__); + return; + } + + if (!atomic_read(&port->pipe_connect_notified)) + return; + + rndis_ipa_reset_trigger(); + ipa_data_stop_endless_xfer(port, true); + ipa_data_stop_endless_xfer(port, false); + spin_lock_irqsave(&port->port_lock, flags); + /* check if USB cable is disconnected or not */ + if (port->port_usb) { + msm_ep_unconfig(port->port_usb->in); + msm_ep_unconfig(port->port_usb->out); + } + spin_unlock_irqrestore(&port->port_lock, flags); + queue_work(ipa_data_wq, &port->disconnect_w); +} diff --git a/drivers/usb/gadget/function/u_data_ipa.h b/drivers/usb/gadget/function/u_data_ipa.h index b7d47ab1bb04..a1c1055bd8ef 100644 --- a/drivers/usb/gadget/function/u_data_ipa.h +++ b/drivers/usb/gadget/function/u_data_ipa.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2014, The Linux Foundation. All rights reserved. +/* Copyright (c) 2014,2016 The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -13,23 +13,78 @@ #ifndef __U_DATA_IPA_H #define __U_DATA_IPA_H -#include "usb_gadget_xport.h" +#include <linux/usb/composite.h> +#include <linux/rndis_ipa.h> +#include <linux/usb/msm_hsusb.h> +#include <linux/miscdevice.h> +#include <linux/ipa_usb.h> +#include <linux/usb_bam.h> + +enum ipa_func_type { + USB_IPA_FUNC_ECM, + USB_IPA_FUNC_MBIM, + USB_IPA_FUNC_RMNET, + USB_IPA_FUNC_RNDIS, + USB_IPA_FUNC_DPL, + USB_IPA_NUM_FUNCS, +}; + +/* Max Number of IPA data ports supported */ +#define IPA_N_PORTS USB_IPA_NUM_FUNCS struct gadget_ipa_port { struct usb_composite_dev *cdev; struct usb_function *func; + int rx_buffer_size; struct usb_ep *in; struct usb_ep *out; int ipa_consumer_ep; int ipa_producer_ep; + const struct usb_endpoint_descriptor *in_ep_desc_backup; + const struct usb_endpoint_descriptor *out_ep_desc_backup; + +}; + +/* for configfs support */ +#define MAX_INST_NAME_LEN 40 + +struct f_rndis_qc_opts { + struct usb_function_instance func_inst; + struct f_rndis_qc *rndis; + u32 vendor_id; + const char *manufacturer; + struct net_device *net; + int refcnt; }; -void ipa_data_port_select(int portno, enum gadget_type gtype); -void ipa_data_disconnect(struct gadget_ipa_port *gp, u8 port_num); -int ipa_data_connect(struct gadget_ipa_port *gp, u8 port_num, +void ipa_data_port_select(enum ipa_func_type func); +void ipa_data_disconnect(struct gadget_ipa_port *gp, enum ipa_func_type func); +int ipa_data_connect(struct gadget_ipa_port *gp, enum ipa_func_type func, u8 src_connection_idx, u8 dst_connection_idx); -int ipa_data_setup(unsigned int no_ipa_port); -void ipa_data_resume(struct gadget_ipa_port *gp, u8 port_num); -void ipa_data_suspend(struct gadget_ipa_port *gp, u8 port_num); +int ipa_data_setup(enum ipa_func_type func); +void ipa_data_free(enum ipa_func_type func); + +void ipa_data_flush_workqueue(void); +void ipa_data_resume(struct gadget_ipa_port *gp, enum ipa_func_type func, + bool remote_wakeup_enabled); +void ipa_data_suspend(struct gadget_ipa_port *gp, enum ipa_func_type func, + bool remote_wakeup_enabled); + +void ipa_data_set_ul_max_xfer_size(u32 ul_max_xfer_size); + +void ipa_data_set_dl_max_xfer_size(u32 dl_max_transfer_size); + +void ipa_data_set_ul_max_pkt_num(u8 ul_max_packets_number); + +void ipa_data_start_rx_tx(enum ipa_func_type func); + +void ipa_data_start_rndis_ipa(enum ipa_func_type func); + +void ipa_data_stop_rndis_ipa(enum ipa_func_type func); +void *rndis_qc_get_ipa_priv(void); +void *rndis_qc_get_ipa_rx_cb(void); +bool rndis_qc_get_skip_ep_config(void); +void *rndis_qc_get_ipa_tx_cb(void); +void rndis_ipa_reset_trigger(void); #endif diff --git a/drivers/video/fbdev/msm/mdss_smmu.c b/drivers/video/fbdev/msm/mdss_smmu.c index 9a00eff9ade9..b5da4ad1a86b 100644 --- a/drivers/video/fbdev/msm/mdss_smmu.c +++ b/drivers/video/fbdev/msm/mdss_smmu.c @@ -36,6 +36,8 @@ #include "mdss_smmu.h" #include "mdss_debug.h" +#define SZ_4G 0xF0000000 + static DEFINE_MUTEX(mdp_iommu_lock); void mdss_iommu_lock(void) @@ -536,13 +538,13 @@ int mdss_smmu_init(struct mdss_data_type *mdata, struct device *dev) } static struct mdss_smmu_domain mdss_mdp_unsec = { - "mdp_0", MDSS_IOMMU_DOMAIN_UNSECURE, SZ_128K, (SZ_1G - SZ_128K)}; + "mdp_0", MDSS_IOMMU_DOMAIN_UNSECURE, SZ_128K, (SZ_4G - SZ_128K)}; static struct mdss_smmu_domain mdss_rot_unsec = { - NULL, MDSS_IOMMU_DOMAIN_ROT_UNSECURE, SZ_128K, (SZ_1G - SZ_128K)}; + NULL, MDSS_IOMMU_DOMAIN_ROT_UNSECURE, SZ_128K, (SZ_4G - SZ_128K)}; static struct mdss_smmu_domain mdss_mdp_sec = { - "mdp_1", MDSS_IOMMU_DOMAIN_SECURE, SZ_1G, SZ_2G}; + "mdp_1", MDSS_IOMMU_DOMAIN_SECURE, SZ_128K, (SZ_4G - SZ_128K)}; static struct mdss_smmu_domain mdss_rot_sec = { - NULL, MDSS_IOMMU_DOMAIN_ROT_SECURE, SZ_1G, SZ_2G}; + NULL, MDSS_IOMMU_DOMAIN_ROT_SECURE, SZ_128K, (SZ_4G - SZ_128K)}; static const struct of_device_id mdss_smmu_dt_match[] = { { .compatible = "qcom,smmu_mdp_unsec", .data = &mdss_mdp_unsec}, diff --git a/include/linux/percpu-rwsem.h b/include/linux/percpu-rwsem.h index c2fa3ecb0dce..146efefde2a1 100644 --- a/include/linux/percpu-rwsem.h +++ b/include/linux/percpu-rwsem.h @@ -10,30 +10,96 @@ struct percpu_rw_semaphore { struct rcu_sync rss; - unsigned int __percpu *fast_read_ctr; + unsigned int __percpu *read_count; struct rw_semaphore rw_sem; - atomic_t slow_read_ctr; - wait_queue_head_t write_waitq; + wait_queue_head_t writer; + int readers_block; }; -extern void percpu_down_read(struct percpu_rw_semaphore *); -extern int percpu_down_read_trylock(struct percpu_rw_semaphore *); -extern void percpu_up_read(struct percpu_rw_semaphore *); +extern int __percpu_down_read(struct percpu_rw_semaphore *, int); +extern void __percpu_up_read(struct percpu_rw_semaphore *); + +static inline void percpu_down_read(struct percpu_rw_semaphore *sem) +{ + might_sleep(); + + rwsem_acquire_read(&sem->rw_sem.dep_map, 0, 0, _RET_IP_); + + preempt_disable(); + /* + * We are in an RCU-sched read-side critical section, so the writer + * cannot both change sem->state from readers_fast and start checking + * counters while we are here. So if we see !sem->state, we know that + * the writer won't be checking until we're past the preempt_enable() + * and that one the synchronize_sched() is done, the writer will see + * anything we did within this RCU-sched read-size critical section. + */ + __this_cpu_inc(*sem->read_count); + if (unlikely(!rcu_sync_is_idle(&sem->rss))) + __percpu_down_read(sem, false); /* Unconditional memory barrier */ + preempt_enable(); + /* + * The barrier() from preempt_enable() prevents the compiler from + * bleeding the critical section out. + */ +} + +static inline int percpu_down_read_trylock(struct percpu_rw_semaphore *sem) +{ + int ret = 1; + + preempt_disable(); + /* + * Same as in percpu_down_read(). + */ + __this_cpu_inc(*sem->read_count); + if (unlikely(!rcu_sync_is_idle(&sem->rss))) + ret = __percpu_down_read(sem, true); /* Unconditional memory barrier */ + preempt_enable(); + /* + * The barrier() from preempt_enable() prevents the compiler from + * bleeding the critical section out. + */ + + if (ret) + rwsem_acquire_read(&sem->rw_sem.dep_map, 0, 1, _RET_IP_); + + return ret; +} + +static inline void percpu_up_read(struct percpu_rw_semaphore *sem) +{ + /* + * The barrier() in preempt_disable() prevents the compiler from + * bleeding the critical section out. + */ + preempt_disable(); + /* + * Same as in percpu_down_read(). + */ + if (likely(rcu_sync_is_idle(&sem->rss))) + __this_cpu_dec(*sem->read_count); + else + __percpu_up_read(sem); /* Unconditional memory barrier */ + preempt_enable(); + + rwsem_release(&sem->rw_sem.dep_map, 1, _RET_IP_); +} extern void percpu_down_write(struct percpu_rw_semaphore *); extern void percpu_up_write(struct percpu_rw_semaphore *); extern int __percpu_init_rwsem(struct percpu_rw_semaphore *, const char *, struct lock_class_key *); + extern void percpu_free_rwsem(struct percpu_rw_semaphore *); -#define percpu_init_rwsem(brw) \ +#define percpu_init_rwsem(sem) \ ({ \ static struct lock_class_key rwsem_key; \ - __percpu_init_rwsem(brw, #brw, &rwsem_key); \ + __percpu_init_rwsem(sem, #sem, &rwsem_key); \ }) - #define percpu_rwsem_is_held(sem) lockdep_is_held(&(sem)->rw_sem) static inline void percpu_rwsem_release(struct percpu_rw_semaphore *sem, diff --git a/include/linux/rcu_sync.h b/include/linux/rcu_sync.h index a63a33e6196e..ece7ed9a4a70 100644 --- a/include/linux/rcu_sync.h +++ b/include/linux/rcu_sync.h @@ -59,6 +59,7 @@ static inline bool rcu_sync_is_idle(struct rcu_sync *rsp) } extern void rcu_sync_init(struct rcu_sync *, enum rcu_sync_type); +extern void rcu_sync_enter_start(struct rcu_sync *); extern void rcu_sync_enter(struct rcu_sync *); extern void rcu_sync_exit(struct rcu_sync *); extern void rcu_sync_dtor(struct rcu_sync *); diff --git a/kernel/cgroup.c b/kernel/cgroup.c index ad4a12371069..cc6c7d0a6758 100644 --- a/kernel/cgroup.c +++ b/kernel/cgroup.c @@ -5376,6 +5376,12 @@ int __init cgroup_init(void) BUG_ON(cgroup_init_cftypes(NULL, cgroup_dfl_base_files)); BUG_ON(cgroup_init_cftypes(NULL, cgroup_legacy_base_files)); + /* + * The latency of the synchronize_sched() is too high for cgroups, + * avoid it at the cost of forcing all readers into the slow path. + */ + rcu_sync_enter_start(&cgroup_threadgroup_rwsem.rss); + mutex_lock(&cgroup_mutex); /* Add init_css_set to the hash table */ diff --git a/kernel/fork.c b/kernel/fork.c index c9eb86b646ab..e89d0bae6f20 100644 --- a/kernel/fork.c +++ b/kernel/fork.c @@ -1375,7 +1375,6 @@ static struct task_struct *copy_process(unsigned long clone_flags, p->real_start_time = ktime_get_boot_ns(); p->io_context = NULL; p->audit_context = NULL; - threadgroup_change_begin(current); cgroup_fork(p); #ifdef CONFIG_NUMA p->mempolicy = mpol_dup(p->mempolicy); @@ -1527,6 +1526,7 @@ static struct task_struct *copy_process(unsigned long clone_flags, INIT_LIST_HEAD(&p->thread_group); p->task_works = NULL; + threadgroup_change_begin(current); /* * Ensure that the cgroup subsystem policies allow the new process to be * forked. It should be noted the the new process's css_set can be changed @@ -1627,6 +1627,7 @@ static struct task_struct *copy_process(unsigned long clone_flags, bad_fork_cancel_cgroup: cgroup_cancel_fork(p, cgrp_ss_priv); bad_fork_free_pid: + threadgroup_change_end(current); if (pid != &init_struct_pid) free_pid(pid); bad_fork_cleanup_io: @@ -1657,7 +1658,6 @@ bad_fork_cleanup_policy: mpol_put(p->mempolicy); bad_fork_cleanup_threadgroup_lock: #endif - threadgroup_change_end(current); delayacct_tsk_free(p); bad_fork_cleanup_count: atomic_dec(&p->cred->user->processes); diff --git a/kernel/locking/percpu-rwsem.c b/kernel/locking/percpu-rwsem.c index f231e0bb311c..ce182599cf2e 100644 --- a/kernel/locking/percpu-rwsem.c +++ b/kernel/locking/percpu-rwsem.c @@ -8,151 +8,186 @@ #include <linux/sched.h> #include <linux/errno.h> -int __percpu_init_rwsem(struct percpu_rw_semaphore *brw, +int __percpu_init_rwsem(struct percpu_rw_semaphore *sem, const char *name, struct lock_class_key *rwsem_key) { - brw->fast_read_ctr = alloc_percpu(int); - if (unlikely(!brw->fast_read_ctr)) + sem->read_count = alloc_percpu(int); + if (unlikely(!sem->read_count)) return -ENOMEM; /* ->rw_sem represents the whole percpu_rw_semaphore for lockdep */ - __init_rwsem(&brw->rw_sem, name, rwsem_key); - rcu_sync_init(&brw->rss, RCU_SCHED_SYNC); - atomic_set(&brw->slow_read_ctr, 0); - init_waitqueue_head(&brw->write_waitq); + rcu_sync_init(&sem->rss, RCU_SCHED_SYNC); + __init_rwsem(&sem->rw_sem, name, rwsem_key); + init_waitqueue_head(&sem->writer); + sem->readers_block = 0; return 0; } EXPORT_SYMBOL_GPL(__percpu_init_rwsem); -void percpu_free_rwsem(struct percpu_rw_semaphore *brw) +void percpu_free_rwsem(struct percpu_rw_semaphore *sem) { /* * XXX: temporary kludge. The error path in alloc_super() * assumes that percpu_free_rwsem() is safe after kzalloc(). */ - if (!brw->fast_read_ctr) + if (!sem->read_count) return; - rcu_sync_dtor(&brw->rss); - free_percpu(brw->fast_read_ctr); - brw->fast_read_ctr = NULL; /* catch use after free bugs */ + rcu_sync_dtor(&sem->rss); + free_percpu(sem->read_count); + sem->read_count = NULL; /* catch use after free bugs */ } +EXPORT_SYMBOL_GPL(percpu_free_rwsem); -/* - * This is the fast-path for down_read/up_read. If it succeeds we rely - * on the barriers provided by rcu_sync_enter/exit; see the comments in - * percpu_down_write() and percpu_up_write(). - * - * If this helper fails the callers rely on the normal rw_semaphore and - * atomic_dec_and_test(), so in this case we have the necessary barriers. - */ -static bool update_fast_ctr(struct percpu_rw_semaphore *brw, unsigned int val) +int __percpu_down_read(struct percpu_rw_semaphore *sem, int try) { - bool success; + /* + * Due to having preemption disabled the decrement happens on + * the same CPU as the increment, avoiding the + * increment-on-one-CPU-and-decrement-on-another problem. + * + * If the reader misses the writer's assignment of readers_block, then + * the writer is guaranteed to see the reader's increment. + * + * Conversely, any readers that increment their sem->read_count after + * the writer looks are guaranteed to see the readers_block value, + * which in turn means that they are guaranteed to immediately + * decrement their sem->read_count, so that it doesn't matter that the + * writer missed them. + */ - preempt_disable(); - success = rcu_sync_is_idle(&brw->rss); - if (likely(success)) - __this_cpu_add(*brw->fast_read_ctr, val); - preempt_enable(); + smp_mb(); /* A matches D */ - return success; -} + /* + * If !readers_block the critical section starts here, matched by the + * release in percpu_up_write(). + */ + if (likely(!smp_load_acquire(&sem->readers_block))) + return 1; -/* - * Like the normal down_read() this is not recursive, the writer can - * come after the first percpu_down_read() and create the deadlock. - * - * Note: returns with lock_is_held(brw->rw_sem) == T for lockdep, - * percpu_up_read() does rwsem_release(). This pairs with the usage - * of ->rw_sem in percpu_down/up_write(). - */ -void percpu_down_read(struct percpu_rw_semaphore *brw) -{ - might_sleep(); - rwsem_acquire_read(&brw->rw_sem.dep_map, 0, 0, _RET_IP_); + /* + * Per the above comment; we still have preemption disabled and + * will thus decrement on the same CPU as we incremented. + */ + __percpu_up_read(sem); - if (likely(update_fast_ctr(brw, +1))) - return; + if (try) + return 0; - /* Avoid rwsem_acquire_read() and rwsem_release() */ - __down_read(&brw->rw_sem); - atomic_inc(&brw->slow_read_ctr); - __up_read(&brw->rw_sem); -} -EXPORT_SYMBOL_GPL(percpu_down_read); + /* + * We either call schedule() in the wait, or we'll fall through + * and reschedule on the preempt_enable() in percpu_down_read(). + */ + preempt_enable_no_resched(); -int percpu_down_read_trylock(struct percpu_rw_semaphore *brw) -{ - if (unlikely(!update_fast_ctr(brw, +1))) { - if (!__down_read_trylock(&brw->rw_sem)) - return 0; - atomic_inc(&brw->slow_read_ctr); - __up_read(&brw->rw_sem); - } - - rwsem_acquire_read(&brw->rw_sem.dep_map, 0, 1, _RET_IP_); + /* + * Avoid lockdep for the down/up_read() we already have them. + */ + __down_read(&sem->rw_sem); + this_cpu_inc(*sem->read_count); + __up_read(&sem->rw_sem); + + preempt_disable(); return 1; } +EXPORT_SYMBOL_GPL(__percpu_down_read); -void percpu_up_read(struct percpu_rw_semaphore *brw) +void __percpu_up_read(struct percpu_rw_semaphore *sem) { - rwsem_release(&brw->rw_sem.dep_map, 1, _RET_IP_); - - if (likely(update_fast_ctr(brw, -1))) - return; + smp_mb(); /* B matches C */ + /* + * In other words, if they see our decrement (presumably to aggregate + * zero, as that is the only time it matters) they will also see our + * critical section. + */ + __this_cpu_dec(*sem->read_count); - /* false-positive is possible but harmless */ - if (atomic_dec_and_test(&brw->slow_read_ctr)) - wake_up_all(&brw->write_waitq); + /* Prod writer to recheck readers_active */ + wake_up(&sem->writer); } -EXPORT_SYMBOL_GPL(percpu_up_read); +EXPORT_SYMBOL_GPL(__percpu_up_read); + +#define per_cpu_sum(var) \ +({ \ + typeof(var) __sum = 0; \ + int cpu; \ + compiletime_assert_atomic_type(__sum); \ + for_each_possible_cpu(cpu) \ + __sum += per_cpu(var, cpu); \ + __sum; \ +}) -static int clear_fast_ctr(struct percpu_rw_semaphore *brw) +/* + * Return true if the modular sum of the sem->read_count per-CPU variable is + * zero. If this sum is zero, then it is stable due to the fact that if any + * newly arriving readers increment a given counter, they will immediately + * decrement that same counter. + */ +static bool readers_active_check(struct percpu_rw_semaphore *sem) { - unsigned int sum = 0; - int cpu; + if (per_cpu_sum(*sem->read_count) != 0) + return false; + + /* + * If we observed the decrement; ensure we see the entire critical + * section. + */ - for_each_possible_cpu(cpu) { - sum += per_cpu(*brw->fast_read_ctr, cpu); - per_cpu(*brw->fast_read_ctr, cpu) = 0; - } + smp_mb(); /* C matches B */ - return sum; + return true; } -void percpu_down_write(struct percpu_rw_semaphore *brw) +void percpu_down_write(struct percpu_rw_semaphore *sem) { + /* Notify readers to take the slow path. */ + rcu_sync_enter(&sem->rss); + + down_write(&sem->rw_sem); + /* - * Make rcu_sync_is_idle() == F and thus disable the fast-path in - * percpu_down_read() and percpu_up_read(), and wait for gp pass. - * - * The latter synchronises us with the preceding readers which used - * the fast-past, so we can not miss the result of __this_cpu_add() - * or anything else inside their criticial sections. + * Notify new readers to block; up until now, and thus throughout the + * longish rcu_sync_enter() above, new readers could still come in. */ - rcu_sync_enter(&brw->rss); + WRITE_ONCE(sem->readers_block, 1); - /* exclude other writers, and block the new readers completely */ - down_write(&brw->rw_sem); + smp_mb(); /* D matches A */ - /* nobody can use fast_read_ctr, move its sum into slow_read_ctr */ - atomic_add(clear_fast_ctr(brw), &brw->slow_read_ctr); + /* + * If they don't see our writer of readers_block, then we are + * guaranteed to see their sem->read_count increment, and therefore + * will wait for them. + */ - /* wait for all readers to complete their percpu_up_read() */ - wait_event(brw->write_waitq, !atomic_read(&brw->slow_read_ctr)); + /* Wait for all now active readers to complete. */ + wait_event(sem->writer, readers_active_check(sem)); } EXPORT_SYMBOL_GPL(percpu_down_write); -void percpu_up_write(struct percpu_rw_semaphore *brw) +void percpu_up_write(struct percpu_rw_semaphore *sem) { - /* release the lock, but the readers can't use the fast-path */ - up_write(&brw->rw_sem); /* - * Enable the fast-path in percpu_down_read() and percpu_up_read() - * but only after another gp pass; this adds the necessary barrier - * to ensure the reader can't miss the changes done by us. + * Signal the writer is done, no fast path yet. + * + * One reason that we cannot just immediately flip to readers_fast is + * that new readers might fail to see the results of this writer's + * critical section. + * + * Therefore we force it through the slow path which guarantees an + * acquire and thereby guarantees the critical section's consistency. + */ + smp_store_release(&sem->readers_block, 0); + + /* + * Release the write lock, this will allow readers back in the game. + */ + up_write(&sem->rw_sem); + + /* + * Once this completes (at least one RCU-sched grace period hence) the + * reader fast path will be available again. Safe to use outside the + * exclusive write lock because its counting. */ - rcu_sync_exit(&brw->rss); + rcu_sync_exit(&sem->rss); } EXPORT_SYMBOL_GPL(percpu_up_write); diff --git a/kernel/rcu/sync.c b/kernel/rcu/sync.c index be922c9f3d37..e358313a0d6c 100644 --- a/kernel/rcu/sync.c +++ b/kernel/rcu/sync.c @@ -83,6 +83,18 @@ void rcu_sync_init(struct rcu_sync *rsp, enum rcu_sync_type type) } /** + * Must be called after rcu_sync_init() and before first use. + * + * Ensures rcu_sync_is_idle() returns false and rcu_sync_{enter,exit}() + * pairs turn into NO-OPs. + */ +void rcu_sync_enter_start(struct rcu_sync *rsp) +{ + rsp->gp_count++; + rsp->gp_state = GP_PASSED; +} + +/** * rcu_sync_enter() - Force readers onto slowpath * @rsp: Pointer to rcu_sync structure to use for synchronization * diff --git a/kernel/sched/core.c b/kernel/sched/core.c index 4b5b0c3cef7a..4cdf967b67c1 100644 --- a/kernel/sched/core.c +++ b/kernel/sched/core.c @@ -7812,6 +7812,7 @@ void __init sched_init_smp(void) hotcpu_notifier(cpuset_cpu_inactive, CPU_PRI_CPUSET_INACTIVE); update_cluster_topology(); + init_sched_hmp_boost_policy(); init_hrtick(); @@ -7860,6 +7861,7 @@ void __init sched_init(void) BUG_ON(num_possible_cpus() > BITS_PER_LONG); + sched_hmp_parse_dt(); init_clusters(); #ifdef CONFIG_FAIR_GROUP_SCHED diff --git a/kernel/sched/hmp.c b/kernel/sched/hmp.c index 61e352aeec00..1e7b4cd4e64c 100644 --- a/kernel/sched/hmp.c +++ b/kernel/sched/hmp.c @@ -17,6 +17,7 @@ #include <linux/cpufreq.h> #include <linux/list_sort.h> #include <linux/syscore_ops.h> +#include <linux/of.h> #include "sched.h" #include "core_ctl.h" @@ -225,6 +226,52 @@ fail: return ret; } +/* + * It is possible that CPUs of the same micro architecture can have slight + * difference in the efficiency due to other factors like cache size. The + * BOOST_ON_BIG policy may not be optimial for such systems. The required + * boost policy can be specified via device tree to handle this. + */ +static int __read_mostly sched_boost_policy = SCHED_BOOST_NONE; + +/* + * This should be called after clusters are populated and + * the respective efficiency values are initialized. + */ +void init_sched_hmp_boost_policy(void) +{ + /* + * Initialize the boost type here if it is not passed from + * device tree. + */ + if (sched_boost_policy == SCHED_BOOST_NONE) { + if (max_possible_efficiency != min_possible_efficiency) + sched_boost_policy = SCHED_BOOST_ON_BIG; + else + sched_boost_policy = SCHED_BOOST_ON_ALL; + } +} + +void sched_hmp_parse_dt(void) +{ + struct device_node *sn; + const char *boost_policy; + + if (!sched_enable_hmp) + return; + + sn = of_find_node_by_path("/sched-hmp"); + if (!sn) + return; + + if (!of_property_read_string(sn, "boost-policy", &boost_policy)) { + if (!strcmp(boost_policy, "boost-on-big")) + sched_boost_policy = SCHED_BOOST_ON_BIG; + else if (!strcmp(boost_policy, "boost-on-all")) + sched_boost_policy = SCHED_BOOST_ON_ALL; + } +} + unsigned int max_possible_efficiency = 1; unsigned int min_possible_efficiency = UINT_MAX; @@ -1169,12 +1216,9 @@ int task_load_will_fit(struct task_struct *p, u64 task_load, int cpu, enum sched_boost_type sched_boost_type(void) { - if (sched_boost()) { - if (min_possible_efficiency != max_possible_efficiency) - return SCHED_BOOST_ON_BIG; - else - return SCHED_BOOST_ON_ALL; - } + if (sched_boost()) + return sched_boost_policy; + return SCHED_BOOST_NONE; } diff --git a/kernel/sched/sched.h b/kernel/sched/sched.h index 1b641e60233e..ada5e580e968 100644 --- a/kernel/sched/sched.h +++ b/kernel/sched/sched.h @@ -1399,6 +1399,8 @@ extern u64 cpu_upmigrate_discourage_read_u64(struct cgroup_subsys_state *css, struct cftype *cft); extern int cpu_upmigrate_discourage_write_u64(struct cgroup_subsys_state *css, struct cftype *cft, u64 upmigrate_discourage); +extern void sched_hmp_parse_dt(void); +extern void init_sched_hmp_boost_policy(void); #else /* CONFIG_SCHED_HMP */ @@ -1589,6 +1591,8 @@ static inline void post_big_task_count_change(void) { } static inline void set_hmp_defaults(void) { } static inline void clear_reserved(int cpu) { } +static inline void sched_hmp_parse_dt(void) {} +static inline void init_sched_hmp_boost_policy(void) {} #define trace_sched_cpu_load(...) #define trace_sched_cpu_load_lb(...) diff --git a/kernel/sched/sched_avg.c b/kernel/sched/sched_avg.c index c70e0466c36c..29d8a26a78ed 100644 --- a/kernel/sched/sched_avg.c +++ b/kernel/sched/sched_avg.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2012, 2015, The Linux Foundation. All rights reserved. +/* Copyright (c) 2012, 2015-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 @@ -60,17 +60,17 @@ void sched_get_nr_running_avg(int *avg, int *iowait_avg, int *big_avg) spin_lock_irqsave(&per_cpu(nr_lock, cpu), flags); curr_time = sched_clock(); + diff = curr_time - per_cpu(last_time, cpu); + BUG_ON((s64)diff < 0); + tmp_avg += per_cpu(nr_prod_sum, cpu); - tmp_avg += per_cpu(nr, cpu) * - (curr_time - per_cpu(last_time, cpu)); + tmp_avg += per_cpu(nr, cpu) * diff; tmp_big_avg += per_cpu(nr_big_prod_sum, cpu); - tmp_big_avg += nr_eligible_big_tasks(cpu) * - (curr_time - per_cpu(last_time, cpu)); + tmp_big_avg += nr_eligible_big_tasks(cpu) * diff; tmp_iowait += per_cpu(iowait_prod_sum, cpu); - tmp_iowait += nr_iowait_cpu(cpu) * - (curr_time - per_cpu(last_time, cpu)); + tmp_iowait += nr_iowait_cpu(cpu) * diff; per_cpu(last_time, cpu) = curr_time; @@ -107,14 +107,15 @@ EXPORT_SYMBOL(sched_get_nr_running_avg); */ void sched_update_nr_prod(int cpu, long delta, bool inc) { - int diff; - s64 curr_time; + u64 diff; + u64 curr_time; unsigned long flags, nr_running; spin_lock_irqsave(&per_cpu(nr_lock, cpu), flags); nr_running = per_cpu(nr, cpu); curr_time = sched_clock(); diff = curr_time - per_cpu(last_time, cpu); + BUG_ON((s64)diff < 0); per_cpu(last_time, cpu) = curr_time; per_cpu(nr, cpu) = nr_running + (inc ? delta : -delta); diff --git a/net/rmnet_data/rmnet_data_vnd.c b/net/rmnet_data/rmnet_data_vnd.c index 6d6893c7d99d..4e3a205551e0 100644 --- a/net/rmnet_data/rmnet_data_vnd.c +++ b/net/rmnet_data/rmnet_data_vnd.c @@ -928,7 +928,7 @@ int rmnet_vnd_add_tc_flow(uint32_t id, uint32_t map_flow, uint32_t tc_flow) list_add(&(itm->list), &(dev_conf->flow_head)); write_unlock_irqrestore(&dev_conf->flow_map_lock, flags); - LOGD("Created flow mapping [%s][0x%08X][0x%08X]@%p", + LOGD("Created flow mapping [%s][0x%08X][0x%08X]@%pK", dev->name, itm->map_flow_id, itm->tc_flow_id[0], itm); return RMNET_CONFIG_OK; diff --git a/sound/soc/msm/qdsp6v2/q6adm.c b/sound/soc/msm/qdsp6v2/q6adm.c index ffa78af72544..e30a4efa6e60 100644 --- a/sound/soc/msm/qdsp6v2/q6adm.c +++ b/sound/soc/msm/qdsp6v2/q6adm.c @@ -2173,69 +2173,74 @@ int adm_arrange_mch_map(struct adm_cmd_device_open_v5 *open, int path, int channel_mode) { int rc = 0, idx; - - memset(open->dev_channel_mapping, 0, - PCM_FORMAT_MAX_NUM_CHANNEL); - - if (channel_mode == 1) { - open->dev_channel_mapping[0] = PCM_CHANNEL_FC; - } else if (channel_mode == 2) { - open->dev_channel_mapping[0] = PCM_CHANNEL_FL; - open->dev_channel_mapping[1] = PCM_CHANNEL_FR; - } else if (channel_mode == 3) { - open->dev_channel_mapping[0] = PCM_CHANNEL_FL; - open->dev_channel_mapping[1] = PCM_CHANNEL_FR; - open->dev_channel_mapping[2] = PCM_CHANNEL_FC; - } else if (channel_mode == 4) { - open->dev_channel_mapping[0] = PCM_CHANNEL_FL; - open->dev_channel_mapping[1] = PCM_CHANNEL_FR; - open->dev_channel_mapping[2] = PCM_CHANNEL_LS; - open->dev_channel_mapping[3] = PCM_CHANNEL_RS; - } else if (channel_mode == 5) { - open->dev_channel_mapping[0] = PCM_CHANNEL_FL; - open->dev_channel_mapping[1] = PCM_CHANNEL_FR; - open->dev_channel_mapping[2] = PCM_CHANNEL_FC; - open->dev_channel_mapping[3] = PCM_CHANNEL_LS; - open->dev_channel_mapping[4] = PCM_CHANNEL_RS; - } else if (channel_mode == 6) { - open->dev_channel_mapping[0] = PCM_CHANNEL_FL; - open->dev_channel_mapping[1] = PCM_CHANNEL_FR; - open->dev_channel_mapping[2] = PCM_CHANNEL_LFE; - open->dev_channel_mapping[3] = PCM_CHANNEL_FC; - open->dev_channel_mapping[4] = PCM_CHANNEL_LS; - open->dev_channel_mapping[5] = PCM_CHANNEL_RS; - } else if (channel_mode == 8) { - open->dev_channel_mapping[0] = PCM_CHANNEL_FL; - open->dev_channel_mapping[1] = PCM_CHANNEL_FR; - open->dev_channel_mapping[2] = PCM_CHANNEL_LFE; - open->dev_channel_mapping[3] = PCM_CHANNEL_FC; - open->dev_channel_mapping[4] = PCM_CHANNEL_LS; - open->dev_channel_mapping[5] = PCM_CHANNEL_RS; - open->dev_channel_mapping[6] = PCM_CHANNEL_LB; - open->dev_channel_mapping[7] = PCM_CHANNEL_RB; - } else { - pr_err("%s: invalid num_chan %d\n", __func__, - channel_mode); - rc = -EINVAL; - goto inval_ch_mod; - } - + memset(open->dev_channel_mapping, 0, PCM_FORMAT_MAX_NUM_CHANNEL); switch (path) { case ADM_PATH_PLAYBACK: idx = ADM_MCH_MAP_IDX_PLAYBACK; break; case ADM_PATH_LIVE_REC: + case ADM_PATH_NONLIVE_REC: idx = ADM_MCH_MAP_IDX_REC; break; default: goto non_mch_path; - break; }; - - if ((open->dev_num_channel > 2) && multi_ch_maps[idx].set_channel_map) + if ((open->dev_num_channel > 2) && multi_ch_maps[idx].set_channel_map) { memcpy(open->dev_channel_mapping, - multi_ch_maps[idx].channel_mapping, - PCM_FORMAT_MAX_NUM_CHANNEL); + multi_ch_maps[idx].channel_mapping, + PCM_FORMAT_MAX_NUM_CHANNEL); + } else { + if (channel_mode == 1) { + open->dev_channel_mapping[0] = PCM_CHANNEL_FC; + } else if (channel_mode == 2) { + open->dev_channel_mapping[0] = PCM_CHANNEL_FL; + open->dev_channel_mapping[1] = PCM_CHANNEL_FR; + } else if (channel_mode == 3) { + open->dev_channel_mapping[0] = PCM_CHANNEL_FL; + open->dev_channel_mapping[1] = PCM_CHANNEL_FR; + open->dev_channel_mapping[2] = PCM_CHANNEL_FC; + } else if (channel_mode == 4) { + open->dev_channel_mapping[0] = PCM_CHANNEL_FL; + open->dev_channel_mapping[1] = PCM_CHANNEL_FR; + open->dev_channel_mapping[2] = PCM_CHANNEL_LS; + open->dev_channel_mapping[3] = PCM_CHANNEL_RS; + } else if (channel_mode == 5) { + open->dev_channel_mapping[0] = PCM_CHANNEL_FL; + open->dev_channel_mapping[1] = PCM_CHANNEL_FR; + open->dev_channel_mapping[2] = PCM_CHANNEL_FC; + open->dev_channel_mapping[3] = PCM_CHANNEL_LS; + open->dev_channel_mapping[4] = PCM_CHANNEL_RS; + } else if (channel_mode == 6) { + open->dev_channel_mapping[0] = PCM_CHANNEL_FL; + open->dev_channel_mapping[1] = PCM_CHANNEL_FR; + open->dev_channel_mapping[2] = PCM_CHANNEL_LFE; + open->dev_channel_mapping[3] = PCM_CHANNEL_FC; + open->dev_channel_mapping[4] = PCM_CHANNEL_LS; + open->dev_channel_mapping[5] = PCM_CHANNEL_RS; + } else if (channel_mode == 7) { + open->dev_channel_mapping[0] = PCM_CHANNEL_FL; + open->dev_channel_mapping[1] = PCM_CHANNEL_FR; + open->dev_channel_mapping[2] = PCM_CHANNEL_FC; + open->dev_channel_mapping[3] = PCM_CHANNEL_LFE; + open->dev_channel_mapping[4] = PCM_CHANNEL_LB; + open->dev_channel_mapping[5] = PCM_CHANNEL_RB; + open->dev_channel_mapping[6] = PCM_CHANNEL_CS; + } else if (channel_mode == 8) { + open->dev_channel_mapping[0] = PCM_CHANNEL_FL; + open->dev_channel_mapping[1] = PCM_CHANNEL_FR; + open->dev_channel_mapping[2] = PCM_CHANNEL_LFE; + open->dev_channel_mapping[3] = PCM_CHANNEL_FC; + open->dev_channel_mapping[4] = PCM_CHANNEL_LS; + open->dev_channel_mapping[5] = PCM_CHANNEL_RS; + open->dev_channel_mapping[6] = PCM_CHANNEL_LB; + open->dev_channel_mapping[7] = PCM_CHANNEL_RB; + } else { + pr_err("%s: invalid num_chan %d\n", __func__, + channel_mode); + rc = -EINVAL; + goto inval_ch_mod; + } + } non_mch_path: inval_ch_mod: |
