summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Documentation/devicetree/bindings/batterydata/batterydata.txt221
-rw-r--r--Documentation/devicetree/bindings/power/qcom-charger/qpnp-fg-gen3.txt5
-rw-r--r--Documentation/devicetree/bindings/power/qcom-charger/qpnp-smb2.txt6
-rw-r--r--Documentation/devicetree/bindings/scheduler/sched_hmp.txt35
-rw-r--r--Documentation/devicetree/bindings/thermal/tsens.txt1
-rw-r--r--Documentation/scheduler/sched-hmp.txt8
-rw-r--r--arch/arm/boot/dts/qcom/msm-pmicobalt.dtsi1
-rw-r--r--arch/arm/boot/dts/qcom/msmcobalt-camera.dtsi29
-rw-r--r--arch/arm/boot/dts/qcom/msmcobalt-mdss-pll.dtsi6
-rw-r--r--arch/arm/boot/dts/qcom/msmcobalt-mdss.dtsi8
-rw-r--r--arch/arm/boot/dts/qcom/msmcobalt-v2.dtsi10
-rw-r--r--arch/arm/boot/dts/qcom/msmcobalt-vidc.dtsi10
-rw-r--r--arch/arm/boot/dts/qcom/msmfalcon-coresight.dtsi302
-rw-r--r--arch/arm/boot/dts/qcom/msmfalcon.dtsi22
-rw-r--r--arch/arm/boot/dts/qcom/msmtriton.dtsi9
-rw-r--r--arch/arm/configs/msmcortex_defconfig2
-rw-r--r--arch/arm/configs/msmfalcon_defconfig4
-rw-r--r--arch/arm/kernel/topology.c34
-rw-r--r--arch/arm64/configs/msm-perf_defconfig1
-rw-r--r--arch/arm64/configs/msm_defconfig1
-rw-r--r--arch/arm64/configs/msmcortex-perf_defconfig1
-rw-r--r--arch/arm64/configs/msmcortex_defconfig1
-rw-r--r--arch/arm64/configs/msmfalcon-perf_defconfig1
-rw-r--r--arch/arm64/configs/msmfalcon_defconfig1
-rw-r--r--drivers/clk/msm/clock-osm.c8
-rw-r--r--drivers/gpu/msm/kgsl_pwrctrl.c24
-rw-r--r--drivers/hwtracing/coresight/coresight-csr.c4
-rw-r--r--drivers/hwtracing/coresight/coresight-stm.c4
-rw-r--r--drivers/input/touchscreen/synaptics_dsx/synaptics_dsx_rmi_dev.c21
-rw-r--r--drivers/media/platform/msm/camera_v2/isp/msm_isp47.c128
-rw-r--r--drivers/media/platform/msm/camera_v2/isp/msm_isp47.h2
-rw-r--r--drivers/media/platform/msm/camera_v2/isp/msm_isp48.c3
-rw-r--r--drivers/media/platform/msm/camera_v2/isp/msm_isp_axi_util.c81
-rw-r--r--drivers/media/platform/msm/camera_v2/isp/msm_isp_stats_util.c16
-rw-r--r--drivers/media/platform/msm/camera_v2/isp/msm_isp_util.c31
-rw-r--r--drivers/media/platform/msm/camera_v2/sensor/actuator/msm_actuator.c14
-rw-r--r--drivers/net/wireless/ath/wil6210/debugfs.c28
-rw-r--r--drivers/net/wireless/ath/wil6210/fw.h3
-rw-r--r--drivers/net/wireless/ath/wil6210/fw_inc.c7
-rw-r--r--drivers/net/wireless/ath/wil6210/main.c1
-rw-r--r--drivers/net/wireless/ath/wil6210/netdev.c2
-rw-r--r--drivers/net/wireless/ath/wil6210/wil6210.h3
-rw-r--r--drivers/net/wireless/ath/wil6210/wmi.c8
-rw-r--r--drivers/platform/msm/sps/bam.c6
-rw-r--r--drivers/power/qcom-charger/Makefile4
-rw-r--r--drivers/power/qcom-charger/battery_current_limit.c77
-rw-r--r--drivers/power/qcom-charger/fg-core.h15
-rw-r--r--drivers/power/qcom-charger/qpnp-fg-gen3.c533
-rw-r--r--drivers/power/qcom-charger/qpnp-smb2.c263
-rw-r--r--drivers/power/qcom-charger/smb-lib.c90
-rw-r--r--drivers/power/qcom-charger/smb-lib.h11
-rw-r--r--drivers/power/qcom-charger/smb-reg.h4
-rw-r--r--drivers/power/qcom-charger/smb138x-charger.c197
-rw-r--r--drivers/power/qcom-charger/storm-watch.c57
-rw-r--r--drivers/power/qcom-charger/storm-watch.h36
-rw-r--r--drivers/scsi/ufs/ufs-qcom.c19
-rw-r--r--drivers/scsi/ufs/ufshcd.c7
-rw-r--r--drivers/soc/qcom/icnss.c144
-rw-r--r--drivers/soc/qcom/jtag-fuse.c4
-rw-r--r--drivers/soc/qcom/service-notifier.c3
-rw-r--r--drivers/thermal/msm-tsens.c12
-rw-r--r--drivers/usb/gadget/Kconfig11
-rw-r--r--drivers/usb/gadget/function/Makefile2
-rw-r--r--drivers/usb/gadget/function/f_qc_rndis.c592
-rw-r--r--drivers/usb/gadget/function/rndis.c28
-rw-r--r--drivers/usb/gadget/function/rndis.h6
-rw-r--r--drivers/usb/gadget/function/u_data_ipa.c656
-rw-r--r--drivers/usb/gadget/function/u_data_ipa.h71
-rw-r--r--drivers/video/fbdev/msm/mdss_smmu.c10
-rw-r--r--include/linux/percpu-rwsem.h84
-rw-r--r--include/linux/rcu_sync.h1
-rw-r--r--kernel/cgroup.c6
-rw-r--r--kernel/fork.c4
-rw-r--r--kernel/locking/percpu-rwsem.c229
-rw-r--r--kernel/rcu/sync.c12
-rw-r--r--kernel/sched/core.c2
-rw-r--r--kernel/sched/hmp.c56
-rw-r--r--kernel/sched/sched.h4
-rw-r--r--kernel/sched/sched_avg.c19
-rw-r--r--net/rmnet_data/rmnet_data_vnd.c2
-rw-r--r--sound/soc/msm/qdsp6v2/q6adm.c109
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,
&timestamp);
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(&timestamp);
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: