summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Documentation/devicetree/bindings/fb/mdss-dp.txt52
-rw-r--r--Documentation/devicetree/bindings/regulator/qpnp-labibb-regulator.txt4
-rw-r--r--Documentation/devicetree/bindings/regulator/qpnp-oledb-regulator.txt13
-rw-r--r--Documentation/devicetree/bindings/spmi/qcom,spmi-pmic-arb.txt4
-rw-r--r--arch/arm/boot/dts/qcom/Makefile9
-rw-r--r--arch/arm/boot/dts/qcom/msm-pm660l.dtsi4
-rw-r--r--arch/arm/boot/dts/qcom/msm8996-gpu.dtsi9
-rw-r--r--arch/arm/boot/dts/qcom/msm8996-sde.dtsi2
-rw-r--r--arch/arm/boot/dts/qcom/msm8998-mdss-panels.dtsi2
-rw-r--r--arch/arm/boot/dts/qcom/msm8998-mdss.dtsi11
-rw-r--r--arch/arm/boot/dts/qcom/msm8998-qrd-overlay.dts27
-rw-r--r--arch/arm/boot/dts/qcom/msm8998-qrd-skuk-overlay.dts27
-rw-r--r--arch/arm/boot/dts/qcom/msm8998-qrd-vr1-overlay.dts27
-rw-r--r--arch/arm/boot/dts/qcom/msm8998-v2.1-mtp-4k-display.dts51
-rw-r--r--arch/arm/boot/dts/qcom/msm8998-v2.dtsi112
-rw-r--r--arch/arm/boot/dts/qcom/msm8998.dtsi1
-rw-r--r--arch/arm/boot/dts/qcom/sdm630.dtsi1
-rw-r--r--arch/arm/boot/dts/qcom/sdm660-common.dtsi2
-rw-r--r--arch/arm/boot/dts/qcom/sdm660-mdss-panels.dtsi16
-rw-r--r--arch/arm/boot/dts/qcom/sdm660-mdss.dtsi11
-rw-r--r--arch/arm/boot/dts/qcom/sdm660.dtsi1
-rw-r--r--arch/arm/mm/dma-mapping.c5
-rw-r--r--arch/arm64/configs/msmcortex-perf_defconfig4
-rw-r--r--arch/arm64/configs/msmcortex_defconfig4
-rw-r--r--arch/arm64/configs/msmcortex_mediabox_defconfig1
-rw-r--r--arch/arm64/configs/sdm660-perf_defconfig1
-rw-r--r--arch/arm64/configs/sdm660_defconfig1
-rw-r--r--arch/arm64/include/asm/cache.h12
-rw-r--r--arch/arm64/kernel/cpufeature.c6
-rw-r--r--drivers/char/Kconfig10
-rw-r--r--drivers/char/Makefile1
-rw-r--r--drivers/char/diag/diag_masks.c4
-rw-r--r--drivers/char/diag/diag_memorydevice.c94
-rw-r--r--drivers/char/diag/diag_mux.c10
-rw-r--r--drivers/char/diag/diagchar.h19
-rw-r--r--drivers/char/diag/diagchar_core.c136
-rw-r--r--drivers/char/diag/diagfwd.c1
-rw-r--r--drivers/char/diag/diagfwd_cntl.c44
-rw-r--r--drivers/char/diag/diagfwd_peripheral.c374
-rw-r--r--drivers/char/diag/diagfwd_peripheral.h2
-rw-r--r--drivers/char/msm_smd_pkt.c1397
-rw-r--r--drivers/clk/msm/clock-local2.c5
-rw-r--r--drivers/clk/msm/clock-mmss-8998.c1
-rw-r--r--drivers/clk/qcom/clk-cpu-osm.c2
-rw-r--r--drivers/clk/qcom/gcc-sdm660.c4
-rw-r--r--drivers/cpuidle/lpm-levels.c12
-rw-r--r--drivers/crypto/msm/qcedev.c51
-rw-r--r--drivers/hwtracing/coresight/coresight-etm4x.c4
-rw-r--r--drivers/hwtracing/coresight/coresight-tmc.c7
-rw-r--r--drivers/iommu/arm-smmu.c37
-rw-r--r--drivers/iommu/dma-mapping-fast.c38
-rw-r--r--drivers/iommu/io-pgtable-fast.c19
-rw-r--r--drivers/iommu/iommu-debug.c6
-rw-r--r--drivers/leds/leds-qpnp-flash-v2.c53
-rw-r--r--drivers/media/platform/msm/camera_v2/isp/msm_isp_axi_util.c76
-rw-r--r--drivers/media/platform/msm/camera_v2/isp/msm_isp_axi_util.h5
-rw-r--r--drivers/media/platform/msm/camera_v2/isp/msm_isp_stats_util.c5
-rw-r--r--drivers/media/platform/msm/camera_v2/isp/msm_isp_util.c3
-rw-r--r--drivers/media/platform/msm/camera_v2/sensor/cci/msm_cci.c59
-rw-r--r--drivers/media/platform/msm/camera_v2/sensor/cci/msm_cci.h3
-rw-r--r--drivers/net/wireless/ath/wil6210/cfg80211.c2
-rw-r--r--drivers/net/wireless/ath/wil6210/pcie_bus.c16
-rw-r--r--drivers/net/wireless/ath/wil6210/wil6210.h9
-rw-r--r--drivers/net/wireless/ath/wil6210/wmi.c11
-rw-r--r--drivers/net/wireless/wcnss/wcnss_wlan.c112
-rw-r--r--drivers/pci/host/pci-msm.c36
-rw-r--r--drivers/perf/arm_pmu.c84
-rw-r--r--drivers/platform/msm/ipa/ipa_v2/ipa.c26
-rw-r--r--drivers/platform/msm/ipa/ipa_v2/ipa_dp.c32
-rw-r--r--drivers/platform/msm/ipa/ipa_v2/ipa_hdr.c21
-rw-r--r--drivers/platform/msm/ipa/ipa_v2/ipa_rt.c7
-rw-r--r--drivers/platform/msm/ipa/ipa_v3/ipa.c40
-rw-r--r--drivers/platform/msm/ipa/ipa_v3/ipa_i.h8
-rw-r--r--drivers/platform/msm/ipa/ipa_v3/ipa_utils.c78
-rw-r--r--drivers/platform/msm/msm_11ad/msm_11ad.c16
-rw-r--r--drivers/power/reset/msm-poweroff.c50
-rw-r--r--drivers/power/supply/qcom/battery.c51
-rw-r--r--drivers/power/supply/qcom/qpnp-qnovo.c37
-rw-r--r--drivers/power/supply/qcom/qpnp-smb2.c18
-rw-r--r--drivers/power/supply/qcom/smb-lib.c798
-rw-r--r--drivers/power/supply/qcom/smb-lib.h18
-rw-r--r--drivers/regulator/qpnp-labibb-regulator.c51
-rw-r--r--drivers/regulator/qpnp-oledb-regulator.c78
-rw-r--r--drivers/scsi/ufs/ufshcd.c7
-rw-r--r--drivers/soc/qcom/Kconfig17
-rw-r--r--drivers/soc/qcom/Makefile1
-rw-r--r--drivers/soc/qcom/common_log.c41
-rw-r--r--drivers/soc/qcom/cpuss_dump.c4
-rw-r--r--drivers/soc/qcom/dcc.c6
-rw-r--r--drivers/soc/qcom/glink_smem_native_xprt.c3
-rw-r--r--drivers/soc/qcom/memory_dump_v2.c30
-rw-r--r--drivers/soc/qcom/msm_minidump.c371
-rw-r--r--drivers/soc/qcom/pil-msa.c10
-rw-r--r--drivers/soc/qcom/qpnp-haptic.c92
-rw-r--r--drivers/soc/qcom/spcom.c14
-rw-r--r--drivers/soc/qcom/watchdog_v2.c18
-rw-r--r--drivers/spmi/spmi-pmic-arb.c13
-rw-r--r--drivers/staging/android/ion/ion.c85
-rw-r--r--drivers/staging/android/lowmemorykiller.c34
-rw-r--r--drivers/staging/android/sync.c5
-rw-r--r--drivers/usb/gadget/configfs.c2
-rw-r--r--drivers/usb/phy/phy-msm-ssusb-qmp.c15
-rw-r--r--drivers/video/fbdev/msm/mdss_compat_utils.c1
-rw-r--r--drivers/video/fbdev/msm/mdss_dba_utils.c54
-rw-r--r--drivers/video/fbdev/msm/mdss_debug.c58
-rw-r--r--drivers/video/fbdev/msm/mdss_debug.h3
-rw-r--r--drivers/video/fbdev/msm/mdss_debug_xlog.c23
-rw-r--r--drivers/video/fbdev/msm/mdss_dp.c457
-rw-r--r--drivers/video/fbdev/msm/mdss_dp.h117
-rw-r--r--drivers/video/fbdev/msm/mdss_dp_aux.c443
-rw-r--r--drivers/video/fbdev/msm/mdss_dp_util.c72
-rw-r--r--drivers/video/fbdev/msm/mdss_dp_util.h21
-rw-r--r--drivers/video/fbdev/msm/mdss_dsi.c99
-rw-r--r--drivers/video/fbdev/msm/mdss_dsi.h5
-rw-r--r--drivers/video/fbdev/msm/mdss_dsi_host.c21
-rw-r--r--drivers/video/fbdev/msm/mdss_dsi_status.c31
-rw-r--r--drivers/video/fbdev/msm/mdss_fb.c24
-rw-r--r--drivers/video/fbdev/msm/mdss_fb.h1
-rw-r--r--drivers/video/fbdev/msm/mdss_hdcp_1x.c4
-rw-r--r--drivers/video/fbdev/msm/mdss_hdmi_edid.c5
-rw-r--r--drivers/video/fbdev/msm/mdss_mdp.c3
-rw-r--r--drivers/video/fbdev/msm/mdss_mdp.h29
-rw-r--r--drivers/video/fbdev/msm/mdss_mdp_cdm.c13
-rw-r--r--drivers/video/fbdev/msm/mdss_mdp_ctl.c22
-rw-r--r--drivers/video/fbdev/msm/mdss_mdp_intf_cmd.c6
-rw-r--r--drivers/video/fbdev/msm/mdss_mdp_intf_video.c17
-rw-r--r--drivers/video/fbdev/msm/mdss_mdp_intf_writeback.c4
-rw-r--r--drivers/video/fbdev/msm/mdss_mdp_overlay.c27
-rw-r--r--drivers/video/fbdev/msm/mdss_mdp_pp.c164
-rw-r--r--drivers/video/fbdev/msm/msm_mdss_io_8974.c9
-rw-r--r--fs/9p/acl.c2
-rw-r--r--fs/ext4/inline.c14
-rw-r--r--fs/ext4/inode.c54
-rw-r--r--fs/ext4/readpage.c47
-rw-r--r--fs/f2fs/data.c41
-rw-r--r--fs/f2fs/inline.c18
-rw-r--r--fs/gfs2/acl.c5
-rw-r--r--fs/mpage.c36
-rw-r--r--fs/xfs/xfs_acl.c4
-rw-r--r--include/linux/io-pgtable-fast.h3
-rw-r--r--include/linux/leds-qpnp-flash.h3
-rw-r--r--include/linux/msm_smd_pkt.h23
-rw-r--r--include/linux/perf/arm_pmu.h8
-rw-r--r--include/linux/qpnp/qpnp-revid.h5
-rw-r--r--include/linux/regulator/qpnp-labibb-regulator.h1
-rw-r--r--include/linux/shrinker.h1
-rw-r--r--include/soc/qcom/minidump.h48
-rw-r--r--include/trace/events/android_fs.h65
-rw-r--r--include/trace/events/android_fs_template.h57
-rw-r--r--kernel/trace/msm_rtb.c10
-rw-r--r--mm/vmscan.c54
-rw-r--r--net/ipv6/netfilter/Kconfig12
-rw-r--r--net/ipv6/netfilter/ip6_tables.c30
-rw-r--r--sound/soc/msm/msm8998.c22
-rw-r--r--sound/soc/msm/qdsp6v2/q6afe.c2
155 files changed, 6240 insertions, 1327 deletions
diff --git a/Documentation/devicetree/bindings/fb/mdss-dp.txt b/Documentation/devicetree/bindings/fb/mdss-dp.txt
index aa227c2628da..707e6edb26ea 100644
--- a/Documentation/devicetree/bindings/fb/mdss-dp.txt
+++ b/Documentation/devicetree/bindings/fb/mdss-dp.txt
@@ -27,7 +27,46 @@ Required properties
- qcom,aux-en-gpio: Specifies the aux-channel enable gpio.
- qcom,aux-sel-gpio: Specifies the aux-channel select gpio.
- qcom,usbplug-cc-gpio: Specifies the usbplug orientation gpio.
-- qcom,aux-cfg-settings: An array that specifies the DP AUX configuration settings.
+- qcom,aux-cfg0-settings: Specifies the DP AUX configuration 0 settings. The first
+ entry in this array corresponds to the register offset
+ within DP AUX, while the remaining entries indicate the
+ programmable values.
+- qcom,aux-cfg1-settings: Specifies the DP AUX configuration 1 settings. The first
+ entry in this array corresponds to the register offset
+ within DP AUX, while the remaining entries indicate the
+ programmable values.
+- qcom,aux-cfg2-settings: Specifies the DP AUX configuration 2 settings. The first
+ entry in this array corresponds to the register offset
+ within DP AUX, while the remaining entries indicate the
+ programmable values.
+- qcom,aux-cfg3-settings: Specifies the DP AUX configuration 3 settings. The first
+ entry in this array corresponds to the register offset
+ within DP AUX, while the remaining entries indicate the
+ programmable values.
+- qcom,aux-cfg4-settings: Specifies the DP AUX configuration 4 settings. The first
+ entry in this array corresponds to the register offset
+ within DP AUX, while the remaining entries indicate the
+ programmable values.
+- qcom,aux-cfg5-settings: Specifies the DP AUX configuration 5 settings. The first
+ entry in this array corresponds to the register offset
+ within DP AUX, while the remaining entries indicate the
+ programmable values.
+- qcom,aux-cfg6-settings: Specifies the DP AUX configuration 6 settings. The first
+ entry in this array corresponds to the register offset
+ within DP AUX, while the remaining entries indicate the
+ programmable values.
+- qcom,aux-cfg7-settings: Specifies the DP AUX configuration 7 settings. The first
+ entry in this array corresponds to the register offset
+ within DP AUX, while the remaining entries indicate the
+ programmable values.
+- qcom,aux-cfg8-settings: Specifies the DP AUX configuration 8 settings. The first
+ entry in this array corresponds to the register offset
+ within DP AUX, while the remaining entries indicate the
+ programmable values.
+- qcom,aux-cfg9-settings: Specifies the DP AUX configuration 9 settings. The first
+ entry in this array corresponds to the register offset
+ within DP AUX, while the remaining entries indicate the
+ programmable values.
Optional properties:
- qcom,<type>-supply-entries: A node that lists the elements of the supply used by the
@@ -87,7 +126,16 @@ Example:
"core_aux_clk", "core_cfg_ahb_clk", "ctrl_link_clk",
"ctrl_link_iface_clk", "ctrl_crypto_clk", "ctrl_pixel_clk";
- qcom,aux-cfg-settings = [00 13 00 10 0a 26 0a 03 8b 03];
+ qcom,aux-cfg0-settings = [1c 00];
+ qcom,aux-cfg1-settings = [20 13 23 1d];
+ qcom,aux-cfg2-settings = [24 00];
+ qcom,aux-cfg3-settings = [28 00];
+ qcom,aux-cfg4-settings = [2c 0a];
+ qcom,aux-cfg5-settings = [30 26];
+ qcom,aux-cfg6-settings = [34 0a];
+ qcom,aux-cfg7-settings = [38 03];
+ qcom,aux-cfg8-settings = [3c bb];
+ qcom,aux-cfg9-settings = [40 03];
qcom,logical2physical-lane-map = [02 03 01 00];
qcom,phy-register-offset = <0x4>;
qcom,max-pclk-frequency-khz = <593470>;
diff --git a/Documentation/devicetree/bindings/regulator/qpnp-labibb-regulator.txt b/Documentation/devicetree/bindings/regulator/qpnp-labibb-regulator.txt
index c9cfc889faba..0d53b9fa4378 100644
--- a/Documentation/devicetree/bindings/regulator/qpnp-labibb-regulator.txt
+++ b/Documentation/devicetree/bindings/regulator/qpnp-labibb-regulator.txt
@@ -151,6 +151,10 @@ LAB subnode optional properties:
any value in the allowed limit.
- qcom,notify-lab-vreg-ok-sts: A boolean property which upon set will
poll and notify the lab_vreg_ok status.
+- qcom,qpnp-lab-sc-wait-time-ms: This property is used to specify the time
+ (in ms) to poll for the short circuit
+ detection. If not specified the default time
+ is 5 sec.
Following properties are available only for PM660A:
diff --git a/Documentation/devicetree/bindings/regulator/qpnp-oledb-regulator.txt b/Documentation/devicetree/bindings/regulator/qpnp-oledb-regulator.txt
index 38f599ba5321..55fde0d4feb6 100644
--- a/Documentation/devicetree/bindings/regulator/qpnp-oledb-regulator.txt
+++ b/Documentation/devicetree/bindings/regulator/qpnp-oledb-regulator.txt
@@ -14,6 +14,11 @@ Required Node Structure
Value type: <string>
Definition: should be "qcom,qpnp-oledb-regulator".
+- qcom,pmic-revid
+ Usage: required
+ Value type: <phandle>
+ Definition: Used to identify the PMIC subtype.
+
- reg
Usage: required
Value type: <prop-encoded-array>
@@ -57,13 +62,6 @@ Required Node Structure
rail. This property is applicable only if qcom,ext-pin-ctl
property is specified and it is specific to PM660A.
-- qcom,force-pd-control
- Usage: optional
- Value type: <bool>
- Definition: Used to enable the pull down control forcibly via SPMI by
- disabling the pull down configuration done by hardware
- automatically through SWIRE pulses.
-
- qcom,pbs-client
Usage: optional
Value type: <phandle>
@@ -224,6 +222,7 @@ pm660a_oledb: qpnp-oledb@e000 {
compatible = "qcom,qpnp-oledb-regulator";
#address-cells = <1>;
#size-cells = <1>;
+ qcom,pmic-revid = <&pm660l_revid>;
reg = <0xe000 0x100>;
label = "oledb";
diff --git a/Documentation/devicetree/bindings/spmi/qcom,spmi-pmic-arb.txt b/Documentation/devicetree/bindings/spmi/qcom,spmi-pmic-arb.txt
index bef919334574..633abd2b8d08 100644
--- a/Documentation/devicetree/bindings/spmi/qcom,spmi-pmic-arb.txt
+++ b/Documentation/devicetree/bindings/spmi/qcom,spmi-pmic-arb.txt
@@ -42,6 +42,9 @@ Required properties:
cell 4: interrupt flags indicating level-sense information, as defined in
dt-bindings/interrupt-controller/irq.h
+Optional properties:
+- qcom,reserved-chan : Reserved channel for debug purpose
+
Example V1 PMIC-Arbiter:
spmi {
@@ -56,6 +59,7 @@ Example V1 PMIC-Arbiter:
qcom,ee = <0>;
qcom,channel = <0>;
+ qcom,reserved-chan = <511>;
#address-cells = <2>;
#size-cells = <0>;
diff --git a/arch/arm/boot/dts/qcom/Makefile b/arch/arm/boot/dts/qcom/Makefile
index 0b60560281c7..f5c0da0cd8e7 100644
--- a/arch/arm/boot/dts/qcom/Makefile
+++ b/arch/arm/boot/dts/qcom/Makefile
@@ -109,7 +109,10 @@ dtbo-$(CONFIG_ARCH_MSM8998) += \
msm8998-v2-cdp-overlay.dtbo \
msm8998-v2-mtp-overlay.dtbo \
msm8998-v2.1-cdp-overlay.dtbo \
- msm8998-v2.1-mtp-overlay.dtbo
+ msm8998-v2.1-mtp-overlay.dtbo \
+ msm8998-qrd-overlay.dtbo \
+ msm8998-qrd-vr1-overlay.dtbo \
+ msm8998-qrd-skuk-overlay.dtbo
msm8998-cdp-overlay.dtbo-base := msm8998.dtb
msm8998-mtp-overlay.dtbo-base := msm8998.dtb
@@ -117,6 +120,9 @@ msm8998-v2-cdp-overlay.dtbo-base := msm8998-v2.dtb
msm8998-v2-mtp-overlay.dtbo-base := msm8998-v2.dtb
msm8998-v2.1-cdp-overlay.dtbo-base := msm8998-v2.1.dtb
msm8998-v2.1-mtp-overlay.dtbo-base := msm8998-v2.1.dtb
+msm8998-qrd-overlay.dtbo-base := msm8998-qrd.dtb
+msm8998-qrd-vr1-overlay.dtbo-base := msm8998-qrd-vr1.dtb
+msm8998-qrd-skuk-overlay.dtbo-base := msm8998-qrd-skuk.dtb
else
dtb-$(CONFIG_ARCH_MSM8998) += msm8998-sim.dtb \
msm8998-rumi.dtb \
@@ -141,6 +147,7 @@ dtb-$(CONFIG_ARCH_MSM8998) += msm8998-sim.dtb \
apq8098-v2-qrd.dtb \
apq8098-v2-qrd-skuk-hdk.dtb \
msm8998-v2.1-mtp.dtb \
+ msm8998-v2.1-mtp-4k-display.dtb \
msm8998-v2.1-cdp.dtb \
msm8998-v2.1-qrd.dtb \
apq8098-v2.1-mtp.dtb \
diff --git a/arch/arm/boot/dts/qcom/msm-pm660l.dtsi b/arch/arm/boot/dts/qcom/msm-pm660l.dtsi
index fdc04b9726b4..679149a78833 100644
--- a/arch/arm/boot/dts/qcom/msm-pm660l.dtsi
+++ b/arch/arm/boot/dts/qcom/msm-pm660l.dtsi
@@ -415,7 +415,9 @@
compatible = "qcom,qpnp-oledb-regulator";
#address-cells = <1>;
#size-cells = <1>;
+ qcom,pmic-revid = <&pm660l_revid>;
reg = <0xe000 0x100>;
+ qcom,pbs-client = <&pm660l_pbs>;
label = "oledb";
regulator-name = "regulator-oledb";
@@ -463,6 +465,8 @@
qcom,qpnp-lab-slew-rate = <5000>;
qcom,qpnp-lab-init-voltage = <4600000>;
qcom,qpnp-lab-init-amoled-voltage = <4600000>;
+
+ qcom,notify-lab-vreg-ok-sts;
};
};
};
diff --git a/arch/arm/boot/dts/qcom/msm8996-gpu.dtsi b/arch/arm/boot/dts/qcom/msm8996-gpu.dtsi
index 215608959dc5..27f692bb14af 100644
--- a/arch/arm/boot/dts/qcom/msm8996-gpu.dtsi
+++ b/arch/arm/boot/dts/qcom/msm8996-gpu.dtsi
@@ -1,4 +1,4 @@
-/* Copyright (c) 2015-2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2015-2017, 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
@@ -89,6 +89,13 @@
coresight-child-list = <&funnel_in0>;
coresight-child-ports = <4>;
+ /* DRM settings */
+ qcom,gpmu-tsens = <0x00060007>;
+ qcom,lm-max-power = <5448>;
+ qcom,gpmu-firmware = "a530v3_gpmu.fw2";
+ qcom,gpmu-version = <1 0>;
+ qcom,zap-shader = "a530_zap";
+
clocks = <&clock_gpu clk_gpu_gx_gfx3d_clk>,
<&clock_gpu clk_gpu_ahb_clk>,
<&clock_gpu clk_gpu_gx_rbbmtimer_clk>,
diff --git a/arch/arm/boot/dts/qcom/msm8996-sde.dtsi b/arch/arm/boot/dts/qcom/msm8996-sde.dtsi
index 8aebac3b0e22..cb33df82da0d 100644
--- a/arch/arm/boot/dts/qcom/msm8996-sde.dtsi
+++ b/arch/arm/boot/dts/qcom/msm8996-sde.dtsi
@@ -51,6 +51,8 @@
#interrupt-cells = <1>;
iommus = <&mdp_smmu 0>;
+ gpus = <&msm_gpu>;
+
/* hw blocks */
qcom,sde-off = <0x1000>;
qcom,sde-ctl-off = <0x2000 0x2200 0x2400
diff --git a/arch/arm/boot/dts/qcom/msm8998-mdss-panels.dtsi b/arch/arm/boot/dts/qcom/msm8998-mdss-panels.dtsi
index d0d13332595a..64f377f1a576 100644
--- a/arch/arm/boot/dts/qcom/msm8998-mdss-panels.dtsi
+++ b/arch/arm/boot/dts/qcom/msm8998-mdss-panels.dtsi
@@ -10,8 +10,6 @@
* GNU General Public License for more details.
*/
-#include "dsi-panel-sim-video.dtsi"
-#include "dsi-panel-sim-dualmipi-video.dtsi"
#include "dsi-panel-nt35597-dualmipi-wqxga-video.dtsi"
#include "dsi-panel-nt35597-dualmipi-wqxga-cmd.dtsi"
#include "dsi-panel-nt35597-truly-dualmipi-wqxga-video.dtsi"
diff --git a/arch/arm/boot/dts/qcom/msm8998-mdss.dtsi b/arch/arm/boot/dts/qcom/msm8998-mdss.dtsi
index 24186aca22be..2b9e13ea24f2 100644
--- a/arch/arm/boot/dts/qcom/msm8998-mdss.dtsi
+++ b/arch/arm/boot/dts/qcom/msm8998-mdss.dtsi
@@ -502,7 +502,16 @@
qcom,msm_ext_disp = <&msm_ext_disp>;
- qcom,aux-cfg-settings = [00 13 00 10 0a 26 0a 03 8b 03];
+ qcom,aux-cfg0-settings = [1c 00];
+ qcom,aux-cfg1-settings = [20 13 23 1d];
+ qcom,aux-cfg2-settings = [24 00];
+ qcom,aux-cfg3-settings = [28 00];
+ qcom,aux-cfg4-settings = [2c 0a];
+ qcom,aux-cfg5-settings = [30 26];
+ qcom,aux-cfg6-settings = [34 0a];
+ qcom,aux-cfg7-settings = [38 03];
+ qcom,aux-cfg8-settings = [3c bb];
+ qcom,aux-cfg9-settings = [40 03];
qcom,logical2physical-lane-map = [02 03 01 00];
qcom,core-supply-entries {
diff --git a/arch/arm/boot/dts/qcom/msm8998-qrd-overlay.dts b/arch/arm/boot/dts/qcom/msm8998-qrd-overlay.dts
new file mode 100644
index 000000000000..55255261a827
--- /dev/null
+++ b/arch/arm/boot/dts/qcom/msm8998-qrd-overlay.dts
@@ -0,0 +1,27 @@
+/* Copyright (c) 2017, 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.
+ */
+
+
+/dts-v1/;
+/plugin/;
+
+#include <dt-bindings/clock/msm-clocks-8998.h>
+#include <dt-bindings/interrupt-controller/irq.h>
+
+#include "msm8998-qrd.dtsi"
+
+/ {
+ model = "Qualcomm Technologies, Inc. MSM 8998 QRD";
+ compatible = "qcom,msm8998-qrd", "qcom,msm8998", "qcom,qrd";
+ qcom,msm-id = <292 0x0>;
+ qcom,board-id = <11 0>;
+};
diff --git a/arch/arm/boot/dts/qcom/msm8998-qrd-skuk-overlay.dts b/arch/arm/boot/dts/qcom/msm8998-qrd-skuk-overlay.dts
new file mode 100644
index 000000000000..408a067dbeee
--- /dev/null
+++ b/arch/arm/boot/dts/qcom/msm8998-qrd-skuk-overlay.dts
@@ -0,0 +1,27 @@
+/* Copyright (c) 2017, 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.
+ */
+
+
+/dts-v1/;
+/plugin/;
+
+#include <dt-bindings/clock/msm-clocks-8998.h>
+#include <dt-bindings/interrupt-controller/irq.h>
+
+#include "msm8998-qrd-skuk.dtsi"
+
+/ {
+ model = "Qualcomm Technologies, Inc. MSM 8998 QRD SKUK";
+ compatible = "qcom,msm8998-qrd", "qcom,msm8998", "qcom,qrd";
+ qcom,msm-id = <292 0x0>;
+ qcom,board-id = <0x01000b 0x80>;
+};
diff --git a/arch/arm/boot/dts/qcom/msm8998-qrd-vr1-overlay.dts b/arch/arm/boot/dts/qcom/msm8998-qrd-vr1-overlay.dts
new file mode 100644
index 000000000000..ff0e24dd0371
--- /dev/null
+++ b/arch/arm/boot/dts/qcom/msm8998-qrd-vr1-overlay.dts
@@ -0,0 +1,27 @@
+/* Copyright (c) 2017, 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.
+ */
+
+
+/dts-v1/;
+/plugin/;
+
+#include <dt-bindings/clock/msm-clocks-8998.h>
+#include <dt-bindings/interrupt-controller/irq.h>
+
+#include "msm8998-qrd-vr1.dtsi"
+
+/ {
+ model = "Qualcomm Technologies, Inc. MSM 8998 QRD VR1 Board";
+ compatible = "qcom,msm8998-qrd", "qcom,msm8998", "qcom,qrd";
+ qcom,msm-id = <292 0x0>;
+ qcom,board-id = <0x02000b 0x80>;
+};
diff --git a/arch/arm/boot/dts/qcom/msm8998-v2.1-mtp-4k-display.dts b/arch/arm/boot/dts/qcom/msm8998-v2.1-mtp-4k-display.dts
new file mode 100644
index 000000000000..7d537aa35533
--- /dev/null
+++ b/arch/arm/boot/dts/qcom/msm8998-v2.1-mtp-4k-display.dts
@@ -0,0 +1,51 @@
+/* Copyright (c) 2017, 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.
+ */
+
+
+/dts-v1/;
+
+#include "msm8998-v2.1.dtsi"
+#include "msm8998-mtp.dtsi"
+
+/ {
+ model = "Qualcomm Technologies, Inc. MSM 8998 v2.1 MTP, 4k display";
+ compatible = "qcom,msm8998-mtp", "qcom,msm8998", "qcom,mtp";
+ qcom,board-id = <8 4>;
+};
+
+&mdss_mdp {
+ qcom,mdss-pref-prim-intf = "dsi";
+};
+
+&mdss_dsi {
+ hw-config = "split_dsi";
+};
+
+&mdss_dsi0 {
+ qcom,dsi-pref-prim-pan = <&dsi_sharp_4k_dsc_cmd>;
+ pinctrl-names = "mdss_default", "mdss_sleep";
+ pinctrl-0 = <&mdss_dsi_active &mdss_te_active>;
+ pinctrl-1 = <&mdss_dsi_suspend &mdss_te_suspend>;
+ qcom,platform-reset-gpio = <&tlmm 94 0>;
+ qcom,platform-te-gpio = <&tlmm 10 0>;
+ qcom,panel-mode-gpio = <&tlmm 91 0>;
+};
+
+&mdss_dsi1 {
+ qcom,dsi-pref-prim-pan = <&dsi_sharp_4k_dsc_cmd>;
+ pinctrl-names = "mdss_default", "mdss_sleep";
+ pinctrl-0 = <&mdss_dsi_active &mdss_te_active>;
+ pinctrl-1 = <&mdss_dsi_suspend &mdss_te_suspend>;
+ qcom,platform-reset-gpio = <&tlmm 94 0>;
+ qcom,platform-te-gpio = <&tlmm 10 0>;
+ qcom,panel-mode-gpio = <&tlmm 91 0>;
+};
diff --git a/arch/arm/boot/dts/qcom/msm8998-v2.dtsi b/arch/arm/boot/dts/qcom/msm8998-v2.dtsi
index 348faf9b69c3..b2f30de94bbc 100644
--- a/arch/arm/boot/dts/qcom/msm8998-v2.dtsi
+++ b/arch/arm/boot/dts/qcom/msm8998-v2.dtsi
@@ -591,53 +591,53 @@
qcom,cpr-open-loop-voltage-adjustment =
/* Speed bin 0 */
- <(-4000) (-4000) (-4000) (-4000) (-4000)
- (-4000) (-4000) (-4000) (-8000) (-8000)
- (-8000) (-12000) (-12000) (-12000) (-12000)
+ < 0 0 0 0 0
+ 0 0 0 0 0
+ 0 (-12000) (-12000) (-12000) (-12000)
(-12000) (-16000) (-16000) (-20000) (-24000)
(-28000) (-28000)>,
/* Speed bin 1 */
- <(-4000) (-4000) (-4000) (-4000) (-4000)
- (-4000) (-4000) (-4000) (-8000) (-8000)
- (-8000) (-12000) (-12000) (-12000) (-12000)
+ < 0 0 0 0 0
+ 0 0 0 0 0
+ 0 (-12000) (-12000) (-12000) (-12000)
(-12000) (-16000) (-16000) (-20000) (-24000)
(-28000) (-28000)>,
/* Speed bin 2 */
- <(-4000) (-4000) (-4000) (-4000) (-4000)
- (-4000) (-4000) (-4000) (-8000) (-8000)
- (-8000) (-12000) (-12000) (-12000) (-12000)
+ < 0 0 0 0 0
+ 0 0 0 0 0
+ 0 (-12000) (-12000) (-12000) (-12000)
(-12000) (-16000) (-16000) (-20000) (-24000)
(-28000) (-28000)>,
/* Speed bin 3 */
- <(-4000) (-4000) (-4000) (-4000) (-4000)
- (-4000) (-4000) (-4000) (-8000) (-8000)
- (-8000) (-12000) (-12000) (-12000) (-12000)
+ < 0 0 0 0 0
+ 0 0 0 0 0
+ 0 (-12000) (-12000) (-12000) (-12000)
(-12000) (-16000) (-16000) (-20000) (-24000)
(-28000) (-28000)>;
qcom,cpr-closed-loop-voltage-adjustment =
/* Speed bin 0 */
- <(-5000) (-5000) (-5000) (-5000) (-5000)
- (-5000) (-5000) (-5000) (-7000) (-8000)
- (-10000) (-10000) (-11000) (-12000) (-13000)
+ < 0 0 0 0 0
+ 0 0 0 0 0
+ 0 (-10000) (-11000) (-12000) (-13000)
(-14000) (-14000) (-15000) (-21000) (-24000)
(-26000) (-28000)>,
/* Speed bin 1 */
- <(-5000) (-5000) (-5000) (-5000) (-5000)
- (-5000) (-5000) (-5000) (-7000) (-8000)
- (-10000) (-10000) (-11000) (-12000) (-13000)
+ < 0 0 0 0 0
+ 0 0 0 0 0
+ 0 (-10000) (-11000) (-12000) (-13000)
(-14000) (-14000) (-15000) (-21000) (-24000)
(-26000) (-28000)>,
/* Speed bin 2 */
- <(-5000) (-5000) (-5000) (-5000) (-5000)
- (-5000) (-5000) (-5000) (-7000) (-8000)
- (-10000) (-10000) (-11000) (-12000) (-13000)
+ < 0 0 0 0 0
+ 0 0 0 0 0
+ 0 (-10000) (-11000) (-12000) (-13000)
(-14000) (-14000) (-15000) (-21000) (-24000)
(-26000) (-28000)>,
/* Speed bin 3 */
- <(-5000) (-5000) (-5000) (-5000) (-5000)
- (-5000) (-5000) (-5000) (-7000) (-8000)
- (-10000) (-10000) (-11000) (-12000) (-13000)
+ < 0 0 0 0 0
+ 0 0 0 0 0
+ 0 (-10000) (-11000) (-12000) (-13000)
(-14000) (-14000) (-15000) (-21000) (-24000)
(-26000) (-28000)>;
@@ -926,65 +926,65 @@
qcom,cpr-open-loop-voltage-adjustment =
/* Speed bin 0 */
- <(-4000) (-4000) (-4000) (-4000) (-4000)
- (-4000) (-4000) (-4000) (-8000) (-8000)
- (-8000) (-8000) (-12000) (-12000) (-12000)
+ < 0 0 0 0 0
+ 0 0 0 0 0
+ 0 (-8000) (-12000) (-12000) (-12000)
(-12000) (-12000) (-12000) (-16000) (-16000)
- (-20000) (-20000) (-24000) (-24000) (-24000)
+ (-20000) (-16000) (-16000) (-16000) (-12000)
(-28000) (-28000) (-28000) (-28000) (-28000)
(-28000) (-28000)>,
/* Speed bin 1 */
- <(-4000) (-4000) (-4000) (-4000) (-4000)
- (-4000) (-4000) (-4000) (-8000) (-8000)
- (-8000) (-8000) (-12000) (-12000) (-12000)
+ < 0 0 0 0 0
+ 0 0 0 0 0
+ 0 (-8000) (-12000) (-12000) (-12000)
(-12000) (-12000) (-12000) (-16000) (-16000)
- (-20000) (-20000) (-24000) (-24000) (-28000)
+ (-20000) (-16000) (-16000) (-16000) (-16000)
(-28000)>,
/* Speed bin 2 */
- <(-4000) (-4000) (-4000) (-4000) (-4000)
- (-4000) (-4000) (-4000) (-8000) (-8000)
- (-8000) (-8000) (-12000) (-12000) (-12000)
+ < 0 0 0 0 0
+ 0 0 0 0 0
+ 0 (-8000) (-12000) (-12000) (-12000)
(-12000) (-12000) (-12000) (-16000) (-16000)
- (-20000) (-20000) (-24000) (-24000) (-24000)
+ (-20000) (-16000) (-16000) (-16000) (-12000)
(-28000) (-28000) (-28000) (-28000) (-28000)>,
/* Speed bin 3 */
- <(-4000) (-4000) (-4000) (-4000) (-4000)
- (-4000) (-4000) (-4000) (-8000) (-8000)
- (-8000) (-8000) (-12000) (-12000) (-12000)
+ < 0 0 0 0 0
+ 0 0 0 0 0
+ 0 (-8000) (-12000) (-12000) (-12000)
(-12000) (-12000) (-12000) (-16000) (-16000)
- (-20000) (-20000) (-24000) (-24000) (-24000)
+ (-20000) (-16000) (-16000) (-16000) (-12000)
(-28000) (-28000) (-28000) (-28000) (-28000)
(-28000)>;
qcom,cpr-closed-loop-voltage-adjustment =
/* Speed bin 0 */
- <(-5000) (-5000) (-5000) (-5000) (-5000)
- (-5000) (-5000) (-5000) (-6000) (-7000)
- (-9000) (-10000) (-10000) (-11000) (-12000)
+ < 0 0 0 0 0
+ 0 0 0 0 0
+ 0 (-10000) (-10000) (-11000) (-12000)
(-12000) (-13000) (-14000) (-14000) (-15000)
- (-18000) (-21000) (-24000) (-25000) (-25000)
+ (-16000) (-16000) (-17000) (-15000) (-13000)
(-26000) (-26000) (-27000) (-27000) (-28000)
(-28000) (-28000)>,
/* Speed bin 1 */
- <(-5000) (-5000) (-5000) (-5000) (-5000)
- (-5000) (-5000) (-5000) (-6000) (-7000)
- (-9000) (-10000) (-10000) (-11000) (-12000)
+ < 0 0 0 0 0
+ 0 0 0 0 0
+ 0 (-10000) (-10000) (-11000) (-12000)
(-12000) (-13000) (-14000) (-14000) (-15000)
- (-18000) (-21000) (-24000) (-26000) (-27000)
+ (-16000) (-16000) (-17000) (-16000) (-15000)
(-28000)>,
/* Speed bin 2 */
- <(-5000) (-5000) (-5000) (-5000) (-5000)
- (-5000) (-5000) (-5000) (-6000) (-7000)
- (-9000) (-10000) (-10000) (-11000) (-12000)
+ < 0 0 0 0 0
+ 0 0 0 0 0
+ 0 (-10000) (-10000) (-11000) (-12000)
(-12000) (-13000) (-14000) (-14000) (-15000)
- (-18000) (-21000) (-24000) (-25000) (-26000)
+ (-16000) (-16000) (-17000) (-15000) (-14000)
(-27000) (-27000) (-28000) (-28000) (-28000)>,
/* Speed bin 3 */
- <(-5000) (-5000) (-5000) (-5000) (-5000)
- (-5000) (-5000) (-5000) (-6000) (-7000)
- (-9000) (-10000) (-10000) (-11000) (-12000)
+ < 0 0 0 0 0
+ 0 0 0 0 0
+ 0 (-10000) (-10000) (-11000) (-12000)
(-12000) (-13000) (-14000) (-14000) (-15000)
- (-18000) (-21000) (-24000) (-25000) (-26000)
+ (-16000) (-16000) (-17000) (-15000) (-14000)
(-26000) (-27000) (-27000) (-28000) (-28000)
(-28000)>;
diff --git a/arch/arm/boot/dts/qcom/msm8998.dtsi b/arch/arm/boot/dts/qcom/msm8998.dtsi
index 85142c6c755e..9b5092cf7f14 100644
--- a/arch/arm/boot/dts/qcom/msm8998.dtsi
+++ b/arch/arm/boot/dts/qcom/msm8998.dtsi
@@ -441,6 +441,7 @@
interrupts = <GIC_SPI 326 IRQ_TYPE_NONE>;
qcom,ee = <0>;
qcom,channel = <0>;
+ qcom,reserved-chan = <511>;
#address-cells = <2>;
#size-cells = <0>;
interrupt-controller;
diff --git a/arch/arm/boot/dts/qcom/sdm630.dtsi b/arch/arm/boot/dts/qcom/sdm630.dtsi
index 0011e1d75321..24a935ffebec 100644
--- a/arch/arm/boot/dts/qcom/sdm630.dtsi
+++ b/arch/arm/boot/dts/qcom/sdm630.dtsi
@@ -1988,6 +1988,7 @@
interrupts = <GIC_SPI 326 IRQ_TYPE_NONE>;
qcom,ee = <0>;
qcom,channel = <0>;
+ qcom,reserved-chan = <511>;
#address-cells = <2>;
#size-cells = <0>;
interrupt-controller;
diff --git a/arch/arm/boot/dts/qcom/sdm660-common.dtsi b/arch/arm/boot/dts/qcom/sdm660-common.dtsi
index 76130f177fad..f933586183ec 100644
--- a/arch/arm/boot/dts/qcom/sdm660-common.dtsi
+++ b/arch/arm/boot/dts/qcom/sdm660-common.dtsi
@@ -377,6 +377,8 @@
0x8f8 0x77 0x00
0x4fc 0x80 0x00
0x8fc 0x80 0x00
+ 0x564 0x00 0x00
+ 0x964 0x00 0x00
0x4c0 0x0a 0x00
0x8c0 0x0a 0x00
0x504 0x03 0x00
diff --git a/arch/arm/boot/dts/qcom/sdm660-mdss-panels.dtsi b/arch/arm/boot/dts/qcom/sdm660-mdss-panels.dtsi
index 4cd8bf4407ac..19862f02aa84 100644
--- a/arch/arm/boot/dts/qcom/sdm660-mdss-panels.dtsi
+++ b/arch/arm/boot/dts/qcom/sdm660-mdss-panels.dtsi
@@ -147,7 +147,13 @@
23 1e 07 08 05 03 04 a0
23 18 07 08 04 03 04 a0];
qcom,esd-check-enabled;
- qcom,mdss-dsi-panel-status-check-mode = "bta_check";
+ qcom,mdss-dsi-panel-status-check-mode = "reg_read";
+ qcom,mdss-dsi-panel-status-command = [06 01 00 01 00 00 01 0a];
+ qcom,mdss-dsi-panel-status-command-state = "dsi_hs_mode";
+ qcom,mdss-dsi-panel-status-value = <0x9c>;
+ qcom,mdss-dsi-panel-on-check-value = <0x9c>;
+ qcom,mdss-dsi-panel-status-read-length = <1>;
+ qcom,mdss-dsi-panel-max-error-count = <3>;
};
&dsi_dual_nt36850_truly_cmd {
@@ -195,7 +201,13 @@
20 12 05 06 03 13 04 a0];
qcom,config-select = <&dsi_nt35597_truly_dsc_cmd_config2>;
qcom,esd-check-enabled;
- qcom,mdss-dsi-panel-status-check-mode = "bta_check";
+ qcom,mdss-dsi-panel-status-check-mode = "reg_read";
+ qcom,mdss-dsi-panel-status-command = [06 01 00 01 00 00 01 0a];
+ qcom,mdss-dsi-panel-status-command-state = "dsi_hs_mode";
+ qcom,mdss-dsi-panel-status-value = <0x9c>;
+ qcom,mdss-dsi-panel-on-check-value = <0x9c>;
+ qcom,mdss-dsi-panel-status-read-length = <1>;
+ qcom,mdss-dsi-panel-max-error-count = <3>;
};
&dsi_dual_nt35597_video {
diff --git a/arch/arm/boot/dts/qcom/sdm660-mdss.dtsi b/arch/arm/boot/dts/qcom/sdm660-mdss.dtsi
index b263d2a68792..787c4f1e2fb6 100644
--- a/arch/arm/boot/dts/qcom/sdm660-mdss.dtsi
+++ b/arch/arm/boot/dts/qcom/sdm660-mdss.dtsi
@@ -505,7 +505,16 @@
qcom,msm_ext_disp = <&msm_ext_disp>;
- qcom,aux-cfg-settings = [00 13 00 00 0a 28 0a 03 b7 03];
+ qcom,aux-cfg0-settings = [20 00];
+ qcom,aux-cfg1-settings = [24 13 23 1d];
+ qcom,aux-cfg2-settings = [28 00];
+ qcom,aux-cfg3-settings = [2c 00];
+ qcom,aux-cfg4-settings = [30 0a];
+ qcom,aux-cfg5-settings = [34 28];
+ qcom,aux-cfg6-settings = [38 0a];
+ qcom,aux-cfg7-settings = [3c 03];
+ qcom,aux-cfg8-settings = [40 b7];
+ qcom,aux-cfg9-settings = [44 03];
qcom,logical2physical-lane-map = [00 01 02 03];
qcom,phy-register-offset = <0x4>;
qcom,max-pclk-frequency-khz = <300000>;
diff --git a/arch/arm/boot/dts/qcom/sdm660.dtsi b/arch/arm/boot/dts/qcom/sdm660.dtsi
index be200f8dd531..2e576a51677f 100644
--- a/arch/arm/boot/dts/qcom/sdm660.dtsi
+++ b/arch/arm/boot/dts/qcom/sdm660.dtsi
@@ -466,6 +466,7 @@
interrupts = <GIC_SPI 326 IRQ_TYPE_NONE>;
qcom,ee = <0>;
qcom,channel = <0>;
+ qcom,reserved-chan = <511>;
#address-cells = <2>;
#size-cells = <0>;
interrupt-controller;
diff --git a/arch/arm/mm/dma-mapping.c b/arch/arm/mm/dma-mapping.c
index 8bda55f00b7b..a25d6b0e22a4 100644
--- a/arch/arm/mm/dma-mapping.c
+++ b/arch/arm/mm/dma-mapping.c
@@ -2169,6 +2169,9 @@ arm_iommu_create_mapping(struct bus_type *bus, dma_addr_t base, u64 size)
if (!bitmap_size)
return ERR_PTR(-EINVAL);
+ WARN(!IS_ALIGNED(size, SZ_128M),
+ "size is not aligned to 128M, alignment enforced");
+
if (bitmap_size > PAGE_SIZE) {
extensions = bitmap_size / PAGE_SIZE;
bitmap_size = PAGE_SIZE;
@@ -2191,7 +2194,7 @@ arm_iommu_create_mapping(struct bus_type *bus, dma_addr_t base, u64 size)
mapping->nr_bitmaps = 1;
mapping->extensions = extensions;
mapping->base = base;
- mapping->bits = bits;
+ mapping->bits = BITS_PER_BYTE * bitmap_size;
spin_lock_init(&mapping->lock);
diff --git a/arch/arm64/configs/msmcortex-perf_defconfig b/arch/arm64/configs/msmcortex-perf_defconfig
index 4f55414454fc..105c4017314e 100644
--- a/arch/arm64/configs/msmcortex-perf_defconfig
+++ b/arch/arm64/configs/msmcortex-perf_defconfig
@@ -7,6 +7,8 @@ CONFIG_HIGH_RES_TIMERS=y
CONFIG_IRQ_TIME_ACCOUNTING=y
CONFIG_RCU_EXPERT=y
CONFIG_RCU_FAST_NO_HZ=y
+CONFIG_RCU_NOCB_CPU=y
+CONFIG_RCU_NOCB_CPU_ALL=y
CONFIG_IKCONFIG=y
CONFIG_IKCONFIG_PROC=y
CONFIG_LOG_CPU_MAX_BUF_SHIFT=17
@@ -186,6 +188,7 @@ CONFIG_IP_NF_ARPFILTER=y
CONFIG_IP_NF_ARP_MANGLE=y
CONFIG_NF_CONNTRACK_IPV6=y
CONFIG_IP6_NF_IPTABLES=y
+CONFIG_IP6_NF_IPTABLES_128=y
CONFIG_IP6_NF_MATCH_RPFILTER=y
CONFIG_IP6_NF_FILTER=y
CONFIG_IP6_NF_TARGET_REJECT=y
@@ -588,7 +591,6 @@ CONFIG_PWM_QPNP=y
CONFIG_ARM_GIC_V3_ACL=y
CONFIG_ANDROID=y
CONFIG_ANDROID_BINDER_IPC=y
-CONFIG_ANDROID_BINDER_DEVICES="binder,hwbinder"
CONFIG_MSM_TZ_LOG=y
CONFIG_SENSORS_SSC=y
CONFIG_EXT2_FS=y
diff --git a/arch/arm64/configs/msmcortex_defconfig b/arch/arm64/configs/msmcortex_defconfig
index 9814d1826d93..af78c47df33d 100644
--- a/arch/arm64/configs/msmcortex_defconfig
+++ b/arch/arm64/configs/msmcortex_defconfig
@@ -6,6 +6,8 @@ CONFIG_HIGH_RES_TIMERS=y
CONFIG_IRQ_TIME_ACCOUNTING=y
CONFIG_RCU_EXPERT=y
CONFIG_RCU_FAST_NO_HZ=y
+CONFIG_RCU_NOCB_CPU=y
+CONFIG_RCU_NOCB_CPU_ALL=y
CONFIG_IKCONFIG=y
CONFIG_IKCONFIG_PROC=y
CONFIG_LOG_CPU_MAX_BUF_SHIFT=17
@@ -186,6 +188,7 @@ CONFIG_IP_NF_ARPFILTER=y
CONFIG_IP_NF_ARP_MANGLE=y
CONFIG_NF_CONNTRACK_IPV6=y
CONFIG_IP6_NF_IPTABLES=y
+CONFIG_IP6_NF_IPTABLES_128=y
CONFIG_IP6_NF_MATCH_RPFILTER=y
CONFIG_IP6_NF_FILTER=y
CONFIG_IP6_NF_TARGET_REJECT=y
@@ -610,7 +613,6 @@ CONFIG_ARM_GIC_V3_ACL=y
CONFIG_PHY_XGENE=y
CONFIG_ANDROID=y
CONFIG_ANDROID_BINDER_IPC=y
-CONFIG_ANDROID_BINDER_DEVICES="binder,hwbinder"
CONFIG_MSM_TZ_LOG=y
CONFIG_SENSORS_SSC=y
CONFIG_EXT2_FS=y
diff --git a/arch/arm64/configs/msmcortex_mediabox_defconfig b/arch/arm64/configs/msmcortex_mediabox_defconfig
index 29a511d3eec5..3322f8fa11fc 100644
--- a/arch/arm64/configs/msmcortex_mediabox_defconfig
+++ b/arch/arm64/configs/msmcortex_mediabox_defconfig
@@ -194,6 +194,7 @@ CONFIG_L2TP_V3=y
CONFIG_L2TP_IP=y
CONFIG_L2TP_ETH=y
CONFIG_BRIDGE=y
+CONFIG_VLAN_8021Q=y
CONFIG_NET_SCHED=y
CONFIG_NET_SCH_HTB=y
CONFIG_NET_SCH_PRIO=y
diff --git a/arch/arm64/configs/sdm660-perf_defconfig b/arch/arm64/configs/sdm660-perf_defconfig
index b7f47de09ecd..ffb983587c31 100644
--- a/arch/arm64/configs/sdm660-perf_defconfig
+++ b/arch/arm64/configs/sdm660-perf_defconfig
@@ -594,7 +594,6 @@ CONFIG_PWM_QPNP=y
CONFIG_ARM_GIC_V3_ACL=y
CONFIG_ANDROID=y
CONFIG_ANDROID_BINDER_IPC=y
-CONFIG_ANDROID_BINDER_DEVICES="binder,hwbinder"
CONFIG_MSM_TZ_LOG=y
CONFIG_SENSORS_SSC=y
CONFIG_EXT2_FS=y
diff --git a/arch/arm64/configs/sdm660_defconfig b/arch/arm64/configs/sdm660_defconfig
index cf2395e5e86c..13ae21bdd562 100644
--- a/arch/arm64/configs/sdm660_defconfig
+++ b/arch/arm64/configs/sdm660_defconfig
@@ -617,7 +617,6 @@ CONFIG_ARM_GIC_V3_ACL=y
CONFIG_PHY_XGENE=y
CONFIG_ANDROID=y
CONFIG_ANDROID_BINDER_IPC=y
-CONFIG_ANDROID_BINDER_DEVICES="binder,hwbinder"
CONFIG_MSM_TZ_LOG=y
CONFIG_SENSORS_SSC=y
CONFIG_EXT2_FS=y
diff --git a/arch/arm64/include/asm/cache.h b/arch/arm64/include/asm/cache.h
index 5082b30bc2c0..f9359d32fae5 100644
--- a/arch/arm64/include/asm/cache.h
+++ b/arch/arm64/include/asm/cache.h
@@ -18,17 +18,17 @@
#include <asm/cachetype.h>
-#define L1_CACHE_SHIFT 7
+#define L1_CACHE_SHIFT 6
#define L1_CACHE_BYTES (1 << L1_CACHE_SHIFT)
/*
* Memory returned by kmalloc() may be used for DMA, so we must make
- * sure that all such allocations are cache aligned. Otherwise,
- * unrelated code may cause parts of the buffer to be read into the
- * cache before the transfer is done, causing old data to be seen by
- * the CPU.
+ * sure that all such allocations are aligned to the maximum *known*
+ * cache line size on ARMv8 systems. Otherwise, unrelated code may
+ * cause parts of the buffer to be read into the cache before the
+ * transfer is done, causing old data to be seen by the CPU.
*/
-#define ARCH_DMA_MINALIGN L1_CACHE_BYTES
+#define ARCH_DMA_MINALIGN (128)
#ifndef __ASSEMBLY__
diff --git a/arch/arm64/kernel/cpufeature.c b/arch/arm64/kernel/cpufeature.c
index cdf1dca64133..f75000996e4c 100644
--- a/arch/arm64/kernel/cpufeature.c
+++ b/arch/arm64/kernel/cpufeature.c
@@ -992,9 +992,9 @@ void __init setup_cpu_features(void)
if (!cwg)
pr_warn("No Cache Writeback Granule information, assuming cache line size %d\n",
cls);
- if (L1_CACHE_BYTES < cls)
- pr_warn("L1_CACHE_BYTES smaller than the Cache Writeback Granule (%d < %d)\n",
- L1_CACHE_BYTES, cls);
+ if (ARCH_DMA_MINALIGN < cls)
+ pr_warn("ARCH_DMA_MINALIGN smaller than the Cache Writeback Granule (%d < %d)\n",
+ ARCH_DMA_MINALIGN, cls);
}
static bool __maybe_unused
diff --git a/drivers/char/Kconfig b/drivers/char/Kconfig
index 8e3bff9c7fe9..4bbe4e5f9a6d 100644
--- a/drivers/char/Kconfig
+++ b/drivers/char/Kconfig
@@ -592,6 +592,16 @@ config DEVPORT
source "drivers/s390/char/Kconfig"
+config MSM_SMD_PKT
+ bool "Enable device interface for some SMD packet ports"
+ default n
+ depends on MSM_SMD
+ help
+ smd_pkt driver provides the interface for the userspace clients
+ to communicate over smd via device nodes. This enable the
+ usersapce clients to read and write to some smd packets channel
+ for MSM chipset.
+
config TILE_SROM
bool "Character-device access via hypervisor to the Tilera SPI ROM"
depends on TILE
diff --git a/drivers/char/Makefile b/drivers/char/Makefile
index 7b0bd5408324..77697b8c42c0 100644
--- a/drivers/char/Makefile
+++ b/drivers/char/Makefile
@@ -9,6 +9,7 @@ obj-$(CONFIG_ATARI_DSP56K) += dsp56k.o
obj-$(CONFIG_VIRTIO_CONSOLE) += virtio_console.o
obj-$(CONFIG_RAW_DRIVER) += raw.o
obj-$(CONFIG_SGI_SNSC) += snsc.o snsc_event.o
+obj-$(CONFIG_MSM_SMD_PKT) += msm_smd_pkt.o
obj-$(CONFIG_MSPEC) += mspec.o
obj-$(CONFIG_MMTIMER) += mmtimer.o
obj-$(CONFIG_UV_MMTIMER) += uv_mmtimer.o
diff --git a/drivers/char/diag/diag_masks.c b/drivers/char/diag/diag_masks.c
index 437077c4d44d..3c10462c2274 100644
--- a/drivers/char/diag/diag_masks.c
+++ b/drivers/char/diag/diag_masks.c
@@ -457,7 +457,9 @@ static void diag_send_feature_mask_update(uint8_t peripheral)
if (driver->supports_apps_hdlc_encoding)
DIAG_SET_FEATURE_MASK(F_DIAG_APPS_HDLC_ENCODE);
if (driver->supports_apps_header_untagging) {
- if (peripheral == PERIPHERAL_MODEM) {
+ if (peripheral == PERIPHERAL_MODEM ||
+ peripheral == PERIPHERAL_LPASS ||
+ peripheral == PERIPHERAL_CDSP) {
DIAG_SET_FEATURE_MASK(F_DIAG_PKT_HEADER_UNTAG);
driver->peripheral_untag[peripheral] =
ENABLE_PKT_HEADER_UNTAGGING;
diff --git a/drivers/char/diag/diag_memorydevice.c b/drivers/char/diag/diag_memorydevice.c
index dc3029cc459d..bd34e6cceec0 100644
--- a/drivers/char/diag/diag_memorydevice.c
+++ b/drivers/char/diag/diag_memorydevice.c
@@ -129,6 +129,37 @@ void diag_md_close_all()
diag_ws_reset(DIAG_WS_MUX);
}
+static int diag_md_get_peripheral(int ctxt)
+{
+ int peripheral;
+
+ if (driver->num_pd_session) {
+ peripheral = GET_PD_CTXT(ctxt);
+ switch (peripheral) {
+ case UPD_WLAN:
+ case UPD_AUDIO:
+ case UPD_SENSORS:
+ break;
+ case DIAG_ID_MPSS:
+ case DIAG_ID_LPASS:
+ case DIAG_ID_CDSP:
+ default:
+ peripheral =
+ GET_BUF_PERIPHERAL(ctxt);
+ if (peripheral > NUM_PERIPHERALS)
+ peripheral = -EINVAL;
+ break;
+ }
+ } else {
+ /* Account for Apps data as well */
+ peripheral = GET_BUF_PERIPHERAL(ctxt);
+ if (peripheral > NUM_PERIPHERALS)
+ peripheral = -EINVAL;
+ }
+
+ return peripheral;
+}
+
int diag_md_write(int id, unsigned char *buf, int len, int ctx)
{
int i;
@@ -144,26 +175,13 @@ int diag_md_write(int id, unsigned char *buf, int len, int ctx)
if (!buf || len < 0)
return -EINVAL;
- if (driver->pd_logging_mode) {
- peripheral = GET_PD_CTXT(ctx);
- switch (peripheral) {
- case UPD_WLAN:
- break;
- case DIAG_ID_MPSS:
- default:
- peripheral = GET_BUF_PERIPHERAL(ctx);
- if (peripheral > NUM_PERIPHERALS)
- return -EINVAL;
- break;
- }
- } else {
- /* Account for Apps data as well */
- peripheral = GET_BUF_PERIPHERAL(ctx);
- if (peripheral > NUM_PERIPHERALS)
- return -EINVAL;
- }
+ peripheral =
+ diag_md_get_peripheral(ctx);
+ if (peripheral < 0)
+ return -EINVAL;
- session_info = diag_md_session_get_peripheral(peripheral);
+ session_info =
+ diag_md_session_get_peripheral(peripheral);
if (!session_info)
return -EIO;
@@ -243,31 +261,15 @@ int diag_md_copy_to_user(char __user *buf, int *pret, size_t buf_size,
entry = &ch->tbl[j];
if (entry->len <= 0)
continue;
- if (driver->pd_logging_mode) {
- peripheral = GET_PD_CTXT(entry->ctx);
- switch (peripheral) {
- case UPD_WLAN:
- break;
- case DIAG_ID_MPSS:
- default:
- peripheral =
- GET_BUF_PERIPHERAL(entry->ctx);
- if (peripheral > NUM_PERIPHERALS)
- goto drop_data;
- break;
- }
- } else {
- /* Account for Apps data as well */
- peripheral = GET_BUF_PERIPHERAL(entry->ctx);
- if (peripheral > NUM_PERIPHERALS)
- goto drop_data;
- }
+
+ peripheral = diag_md_get_peripheral(entry->ctx);
+ if (peripheral < 0)
+ goto drop_data;
session_info =
diag_md_session_get_peripheral(peripheral);
if (!session_info) {
- mutex_unlock(&driver->diagfwd_untag_mutex);
- return -EIO;
+ goto drop_data;
}
if (session_info && info &&
@@ -363,9 +365,15 @@ int diag_md_close_peripheral(int id, uint8_t peripheral)
spin_lock_irqsave(&ch->lock, flags);
for (i = 0; i < ch->num_tbl_entries && !found; i++) {
entry = &ch->tbl[i];
- if ((GET_BUF_PERIPHERAL(entry->ctx) != peripheral) ||
- (GET_PD_CTXT(entry->ctx) != peripheral))
- continue;
+
+ if (peripheral > NUM_PERIPHERALS) {
+ if (GET_PD_CTXT(entry->ctx) != peripheral)
+ continue;
+ } else {
+ if (GET_BUF_PERIPHERAL(entry->ctx) !=
+ peripheral)
+ continue;
+ }
found = 1;
if (ch->ops && ch->ops->write_done) {
ch->ops->write_done(entry->buf, entry->len,
diff --git a/drivers/char/diag/diag_mux.c b/drivers/char/diag/diag_mux.c
index 55c5de1ea9fc..39f4b08d9b0a 100644
--- a/drivers/char/diag/diag_mux.c
+++ b/drivers/char/diag/diag_mux.c
@@ -27,7 +27,7 @@
#include "diag_mux.h"
#include "diag_usb.h"
#include "diag_memorydevice.h"
-
+#include "diag_ipc_logging.h"
struct diag_mux_state_t *diag_mux;
static struct diag_logger_t usb_logger;
@@ -146,7 +146,15 @@ int diag_mux_write(int proc, unsigned char *buf, int len, int ctx)
case DIAG_ID_MPSS:
upd = PERIPHERAL_MODEM;
break;
+ case DIAG_ID_LPASS:
+ upd = PERIPHERAL_LPASS;
+ break;
+ case DIAG_ID_CDSP:
+ upd = PERIPHERAL_CDSP;
+ break;
case UPD_WLAN:
+ case UPD_AUDIO:
+ case UPD_SENSORS:
break;
default:
pr_err("diag: invalid pd ctxt= %d\n", upd);
diff --git a/drivers/char/diag/diagchar.h b/drivers/char/diag/diagchar.h
index b68a47219132..b17538a10ea9 100644
--- a/drivers/char/diag/diagchar.h
+++ b/drivers/char/diag/diagchar.h
@@ -76,7 +76,9 @@
| DIAG_CON_LPASS | DIAG_CON_WCNSS \
| DIAG_CON_SENSORS | DIAG_CON_WDSP \
| DIAG_CON_CDSP)
-#define DIAG_CON_UPD_ALL (DIAG_CON_UPD_WLAN)
+#define DIAG_CON_UPD_ALL (DIAG_CON_UPD_WLAN \
+ | DIAG_CON_UPD_AUDIO \
+ | DIAG_CON_UPD_SENSORS)
#define DIAG_STM_MODEM 0x01
#define DIAG_STM_LPASS 0x02
@@ -222,6 +224,10 @@
#define DIAG_ID_APPS 1
#define DIAG_ID_MPSS 2
#define DIAG_ID_WLAN 3
+#define DIAG_ID_LPASS 4
+#define DIAG_ID_CDSP 5
+#define DIAG_ID_AUDIO 6
+#define DIAG_ID_SENSORS 7
/* Number of sessions possible in Memory Device Mode. +1 for Apps data */
#define NUM_MD_SESSIONS (NUM_PERIPHERALS \
@@ -598,10 +604,15 @@ struct diagchar_dev {
int in_busy_dcipktdata;
int logging_mode;
int logging_mask;
- int pd_logging_mode;
+ int pd_logging_mode[NUM_UPD];
+ int pd_session_clear[NUM_UPD];
int num_pd_session;
- int cpd_len_1;
- int cpd_len_2;
+ int cpd_len_1[NUM_PERIPHERALS];
+ int cpd_len_2[NUM_PERIPHERALS];
+ int upd_len_1_a[NUM_PERIPHERALS];
+ int upd_len_1_b[NUM_PERIPHERALS];
+ int upd_len_2_a;
+ int upd_len_2_b;
int mask_check;
uint32_t md_session_mask;
uint8_t md_session_mode;
diff --git a/drivers/char/diag/diagchar_core.c b/drivers/char/diag/diagchar_core.c
index 4f56696f52e9..574a13de6a0d 100644
--- a/drivers/char/diag/diagchar_core.c
+++ b/drivers/char/diag/diagchar_core.c
@@ -397,6 +397,10 @@ static uint32_t diag_translate_kernel_to_user_mask(uint32_t peripheral_mask)
ret |= DIAG_CON_CDSP;
if (peripheral_mask & MD_PERIPHERAL_MASK(UPD_WLAN))
ret |= DIAG_CON_UPD_WLAN;
+ if (peripheral_mask & MD_PERIPHERAL_MASK(UPD_AUDIO))
+ ret |= DIAG_CON_UPD_AUDIO;
+ if (peripheral_mask & MD_PERIPHERAL_MASK(UPD_SENSORS))
+ ret |= DIAG_CON_UPD_SENSORS;
return ret;
}
int diag_mask_param(void)
@@ -426,8 +430,8 @@ void diag_clear_masks(struct diag_md_session_t *info)
static void diag_close_logging_process(const int pid)
{
- int i;
- int session_peripheral_mask;
+ int i, j;
+ int session_mask;
struct diag_md_session_t *session_info = NULL;
struct diag_logging_mode_param_t params;
@@ -443,27 +447,34 @@ static void diag_close_logging_process(const int pid)
mutex_unlock(&driver->diag_maskclear_mutex);
mutex_lock(&driver->diagchar_mutex);
- session_peripheral_mask = session_info->peripheral_mask;
+
+ session_mask = session_info->peripheral_mask;
diag_md_session_close(session_info);
- mutex_unlock(&driver->diagchar_mutex);
+
for (i = 0; i < NUM_MD_SESSIONS; i++)
- if (MD_PERIPHERAL_MASK(i) & session_peripheral_mask)
+ if (MD_PERIPHERAL_MASK(i) & session_mask)
diag_mux_close_peripheral(DIAG_LOCAL_PROC, i);
params.req_mode = USB_MODE;
params.mode_param = 0;
params.peripheral_mask =
- diag_translate_kernel_to_user_mask(session_peripheral_mask);
- if (driver->pd_logging_mode)
- params.pd_mask =
- diag_translate_kernel_to_user_mask(session_peripheral_mask);
-
- if (session_peripheral_mask & MD_PERIPHERAL_MASK(UPD_WLAN)) {
- driver->pd_logging_mode--;
- driver->num_pd_session--;
+ diag_translate_kernel_to_user_mask(session_mask);
+
+ for (i = UPD_WLAN; i < NUM_MD_SESSIONS; i++) {
+ if (session_mask &
+ MD_PERIPHERAL_MASK(i)) {
+ j = i - UPD_WLAN;
+ driver->pd_session_clear[j] = 1;
+ driver->pd_logging_mode[j] = 0;
+ driver->num_pd_session -= 1;
+ params.pd_mask =
+ diag_translate_kernel_to_user_mask(session_mask);
+ } else
+ params.pd_mask = 0;
}
- mutex_lock(&driver->diagchar_mutex);
+
diag_switch_logging(&params);
+
mutex_unlock(&driver->diagchar_mutex);
}
@@ -1562,17 +1573,22 @@ static uint32_t diag_translate_mask(uint32_t peripheral_mask)
ret |= (1 << PERIPHERAL_CDSP);
if (peripheral_mask & DIAG_CON_UPD_WLAN)
ret |= (1 << UPD_WLAN);
+ if (peripheral_mask & DIAG_CON_UPD_AUDIO)
+ ret |= (1 << UPD_AUDIO);
+ if (peripheral_mask & DIAG_CON_UPD_SENSORS)
+ ret |= (1 << UPD_SENSORS);
return ret;
}
static int diag_switch_logging(struct diag_logging_mode_param_t *param)
{
- int new_mode;
+ int new_mode, i;
int curr_mode;
int err = 0;
uint8_t do_switch = 1;
uint32_t peripheral_mask = 0;
+ uint8_t peripheral, upd;
if (!param)
return -EINVAL;
@@ -1583,10 +1599,28 @@ static int diag_switch_logging(struct diag_logging_mode_param_t *param)
return -EINVAL;
}
- switch (param->pd_mask) {
- case DIAG_CON_UPD_WLAN:
- if (driver->md_session_map[PERIPHERAL_MODEM] &&
- (MD_PERIPHERAL_MASK(PERIPHERAL_MODEM) &
+ if (param->pd_mask) {
+ switch (param->pd_mask) {
+ case DIAG_CON_UPD_WLAN:
+ peripheral = PERIPHERAL_MODEM;
+ upd = UPD_WLAN;
+ break;
+ case DIAG_CON_UPD_AUDIO:
+ peripheral = PERIPHERAL_LPASS;
+ upd = UPD_AUDIO;
+ break;
+ case DIAG_CON_UPD_SENSORS:
+ peripheral = PERIPHERAL_LPASS;
+ upd = UPD_SENSORS;
+ break;
+ default:
+ DIAG_LOG(DIAG_DEBUG_USERSPACE,
+ "asking for mode switch with no pd mask set\n");
+ return -EINVAL;
+ }
+
+ if (driver->md_session_map[peripheral] &&
+ (MD_PERIPHERAL_MASK(peripheral) &
diag_mux->mux_mask)) {
DIAG_LOG(DIAG_DEBUG_USERSPACE,
"diag_fr: User PD is already logging onto active peripheral logging\n");
@@ -1595,15 +1629,16 @@ static int diag_switch_logging(struct diag_logging_mode_param_t *param)
peripheral_mask =
diag_translate_mask(param->pd_mask);
param->peripheral_mask = peripheral_mask;
- driver->pd_logging_mode++;
- driver->num_pd_session++;
- break;
-
- default:
+ i = upd - UPD_WLAN;
+ if (!driver->pd_session_clear[i]) {
+ driver->pd_logging_mode[i] = 1;
+ driver->num_pd_session += 1;
+ driver->pd_session_clear[i] = 0;
+ }
+ } else {
peripheral_mask =
diag_translate_mask(param->peripheral_mask);
param->peripheral_mask = peripheral_mask;
- break;
}
switch (param->req_mode) {
@@ -1945,9 +1980,36 @@ static int diag_ioctl_hdlc_toggle(unsigned long ioarg)
return 0;
}
-static int diag_ioctl_query_pd_logging(unsigned long ioarg)
+static int diag_ioctl_query_pd_logging(struct diag_logging_mode_param_t *param)
{
int ret = -EINVAL;
+ int peripheral;
+ char *p_str = NULL;
+
+ if (!param)
+ return -EINVAL;
+
+ if (!param->pd_mask) {
+ DIAG_LOG(DIAG_DEBUG_USERSPACE,
+ "query with no pd mask set, returning error\n");
+ return -EINVAL;
+ }
+
+ switch (param->pd_mask) {
+ case DIAG_CON_UPD_WLAN:
+ peripheral = PERIPHERAL_MODEM;
+ p_str = "MODEM";
+ break;
+ case DIAG_CON_UPD_AUDIO:
+ case DIAG_CON_UPD_SENSORS:
+ peripheral = PERIPHERAL_LPASS;
+ p_str = "LPASS";
+ break;
+ default:
+ DIAG_LOG(DIAG_DEBUG_USERSPACE,
+ "Invalid pd mask, returning EINVAL\n");
+ return -EINVAL;
+ }
DIAG_LOG(DIAG_DEBUG_USERSPACE,
"diag: %s: Untagging support on APPS is %s\n", __func__,
@@ -1955,12 +2017,13 @@ static int diag_ioctl_query_pd_logging(unsigned long ioarg)
"present" : "absent"));
DIAG_LOG(DIAG_DEBUG_USERSPACE,
- "diag: %s: Tagging support on MODEM is %s\n", __func__,
- (driver->feature[PERIPHERAL_MODEM].untag_header ?
+ "diag: %s: Tagging support on %s is %s\n",
+ __func__, p_str,
+ (driver->feature[peripheral].untag_header ?
"present" : "absent"));
if (driver->supports_apps_header_untagging &&
- driver->feature[PERIPHERAL_MODEM].untag_header)
+ driver->feature[peripheral].untag_header)
ret = 0;
return ret;
@@ -2206,7 +2269,10 @@ long diagchar_compat_ioctl(struct file *filp,
result = diag_ioctl_hdlc_toggle(ioarg);
break;
case DIAG_IOCTL_QUERY_PD_LOGGING:
- result = diag_ioctl_query_pd_logging(ioarg);
+ if (copy_from_user((void *)&mode_param, (void __user *)ioarg,
+ sizeof(mode_param)))
+ return -EFAULT;
+ result = diag_ioctl_query_pd_logging(&mode_param);
break;
}
return result;
@@ -2332,7 +2398,10 @@ long diagchar_ioctl(struct file *filp,
result = diag_ioctl_hdlc_toggle(ioarg);
break;
case DIAG_IOCTL_QUERY_PD_LOGGING:
- result = diag_ioctl_query_pd_logging(ioarg);
+ if (copy_from_user((void *)&mode_param, (void __user *)ioarg,
+ sizeof(mode_param)))
+ return -EFAULT;
+ result = diag_ioctl_query_pd_logging(&mode_param);
break;
}
return result;
@@ -3474,7 +3543,10 @@ static int __init diagchar_init(void)
poolsize_usb_apps + 1 + (NUM_PERIPHERALS * 6));
driver->num_clients = max_clients;
driver->logging_mode = DIAG_USB_MODE;
- driver->pd_logging_mode = 0;
+ for (i = 0; i < NUM_UPD; i++) {
+ driver->pd_logging_mode[i] = 0;
+ driver->pd_session_clear[i] = 0;
+ }
driver->num_pd_session = 0;
driver->mask_check = 0;
driver->in_busy_pktdata = 0;
diff --git a/drivers/char/diag/diagfwd.c b/drivers/char/diag/diagfwd.c
index 07c90b741fa0..8fb724305c03 100644
--- a/drivers/char/diag/diagfwd.c
+++ b/drivers/char/diag/diagfwd.c
@@ -38,6 +38,7 @@
#include "diag_masks.h"
#include "diag_usb.h"
#include "diag_mux.h"
+#include "diag_ipc_logging.h"
#define STM_CMD_VERSION_OFFSET 4
#define STM_CMD_MASK_OFFSET 5
diff --git a/drivers/char/diag/diagfwd_cntl.c b/drivers/char/diag/diagfwd_cntl.c
index ae749725f6db..82a67f1f6f47 100644
--- a/drivers/char/diag/diagfwd_cntl.c
+++ b/drivers/char/diag/diagfwd_cntl.c
@@ -110,6 +110,8 @@ void diag_notify_md_client(uint8_t peripheral, int data)
{
int stat = 0;
struct siginfo info;
+ struct pid *pid_struct;
+ struct task_struct *result;
if (peripheral > NUM_PERIPHERALS)
return;
@@ -122,20 +124,38 @@ void diag_notify_md_client(uint8_t peripheral, int data)
info.si_code = SI_QUEUE;
info.si_int = (PERIPHERAL_MASK(peripheral) | data);
info.si_signo = SIGCONT;
- if (driver->md_session_map[peripheral] &&
- driver->md_session_map[peripheral]->task) {
- if (driver->md_session_map[peripheral]->
- md_client_thread_info->task != NULL
- && driver->md_session_map[peripheral]->pid ==
- driver->md_session_map[peripheral]->task->tgid) {
+
+ if (!driver->md_session_map[peripheral] ||
+ driver->md_session_map[peripheral]->pid <= 0) {
+ pr_err("diag: md_session_map[%d] is invalid\n", peripheral);
+ mutex_unlock(&driver->md_session_lock);
+ return;
+ }
+
+ pid_struct = find_get_pid(
+ driver->md_session_map[peripheral]->pid);
+ DIAG_LOG(DIAG_DEBUG_PERIPHERALS,
+ "md_session_map[%d] pid = %d task = %pK\n",
+ peripheral,
+ driver->md_session_map[peripheral]->pid,
+ driver->md_session_map[peripheral]->task);
+
+ if (pid_struct) {
+ result = get_pid_task(pid_struct, PIDTYPE_PID);
+
+ if (!result) {
DIAG_LOG(DIAG_DEBUG_PERIPHERALS,
- "md_session %d pid = %d, md_session %d task tgid = %d\n",
- peripheral,
- driver->md_session_map[peripheral]->pid,
+ "diag: md_session_map[%d] with pid = %d Exited..\n",
peripheral,
- driver->md_session_map[peripheral]->task->tgid);
- stat = send_sig_info(info.si_signo, &info,
- driver->md_session_map[peripheral]->task);
+ driver->md_session_map[peripheral]->pid);
+ mutex_unlock(&driver->md_session_lock);
+ return;
+ }
+
+ if (driver->md_session_map[peripheral] &&
+ driver->md_session_map[peripheral]->task == result) {
+ stat = send_sig_info(info.si_signo,
+ &info, result);
if (stat)
pr_err("diag: Err sending signal to memory device client, signal data: 0x%x, stat: %d\n",
info.si_int, stat);
diff --git a/drivers/char/diag/diagfwd_peripheral.c b/drivers/char/diag/diagfwd_peripheral.c
index aaa587975469..e86dc8292bf0 100644
--- a/drivers/char/diag/diagfwd_peripheral.c
+++ b/drivers/char/diag/diagfwd_peripheral.c
@@ -244,9 +244,14 @@ static void diagfwd_data_process_done(struct diagfwd_info *fwd_info,
mutex_lock(&driver->hdlc_disable_mutex);
mutex_lock(&fwd_info->data_mutex);
+
peripheral = GET_PD_CTXT(buf->ctxt);
if (peripheral == DIAG_ID_MPSS)
peripheral = PERIPHERAL_MODEM;
+ if (peripheral == DIAG_ID_LPASS)
+ peripheral = PERIPHERAL_LPASS;
+ if (peripheral == DIAG_ID_CDSP)
+ peripheral = PERIPHERAL_CDSP;
session_info =
diag_md_session_get_peripheral(peripheral);
@@ -323,15 +328,19 @@ end:
static void diagfwd_data_read_untag_done(struct diagfwd_info *fwd_info,
unsigned char *buf, int len)
{
- int len_cpd = 0, len_upd_1 = 0;
- int ctxt_cpd = 0, ctxt_upd_1 = 0;
+ int len_cpd = 0;
+ int len_upd_1 = 0, len_upd_2 = 0;
+ int ctxt_cpd = 0;
+ int ctxt_upd_1 = 0, ctxt_upd_2 = 0;
int buf_len = 0, processed = 0;
unsigned char *temp_buf_main = NULL;
unsigned char *temp_buf_cpd = NULL;
unsigned char *temp_buf_upd_1 = NULL;
+ unsigned char *temp_buf_upd_2 = NULL;
struct diagfwd_buf_t *temp_ptr_upd = NULL;
struct diagfwd_buf_t *temp_ptr_cpd = NULL;
int flag_buf_1 = 0, flag_buf_2 = 0;
+ uint8_t peripheral;
if (!fwd_info || !buf || len <= 0) {
diag_ws_release();
@@ -349,24 +358,42 @@ static void diagfwd_data_read_untag_done(struct diagfwd_info *fwd_info,
diag_ws_release();
return;
}
+ peripheral = fwd_info->peripheral;
- if (driver->feature[fwd_info->peripheral].encode_hdlc &&
- driver->feature[fwd_info->peripheral].untag_header &&
- driver->peripheral_untag[fwd_info->peripheral]) {
+ if (driver->feature[peripheral].encode_hdlc &&
+ driver->feature[peripheral].untag_header &&
+ driver->peripheral_untag[peripheral]) {
mutex_lock(&driver->diagfwd_untag_mutex);
temp_buf_cpd = buf;
temp_buf_main = buf;
if (fwd_info->buf_1 &&
fwd_info->buf_1->data_raw == buf) {
flag_buf_1 = 1;
- if (fwd_info->type == TYPE_DATA)
+ temp_ptr_cpd = fwd_info->buf_1;
+ if (fwd_info->type == TYPE_DATA) {
temp_buf_upd_1 =
fwd_info->buf_upd_1_a->data_raw;
- } else {
+ if (peripheral ==
+ PERIPHERAL_LPASS)
+ temp_buf_upd_2 =
+ fwd_info->buf_upd_2_a->data_raw;
+ }
+ } else if (fwd_info->buf_2 &&
+ fwd_info->buf_2->data_raw == buf) {
flag_buf_2 = 1;
+ temp_ptr_cpd = fwd_info->buf_2;
if (fwd_info->type == TYPE_DATA)
temp_buf_upd_1 =
fwd_info->buf_upd_1_b->data_raw;
+ if (peripheral ==
+ PERIPHERAL_LPASS)
+ temp_buf_upd_2 =
+ fwd_info->buf_upd_2_b->data_raw;
+ } else {
+ pr_err("diag: In %s, no match for buffer %pK, peripheral %d, type: %d\n",
+ __func__, buf, peripheral,
+ fwd_info->type);
+ goto end;
}
while (processed < len) {
buf_len =
@@ -390,31 +417,97 @@ static void diagfwd_data_read_untag_done(struct diagfwd_info *fwd_info,
temp_buf_upd_1 += buf_len;
}
break;
+ case DIAG_ID_LPASS:
+ ctxt_cpd = DIAG_ID_LPASS;
+ len_cpd += buf_len;
+ if (temp_buf_cpd) {
+ memcpy(temp_buf_cpd,
+ (temp_buf_main + 4), buf_len);
+ temp_buf_cpd += buf_len;
+ }
+ break;
+ case DIAG_ID_AUDIO:
+ ctxt_upd_1 = UPD_AUDIO;
+ len_upd_1 += buf_len;
+ if (temp_buf_upd_1) {
+ memcpy(temp_buf_upd_1,
+ (temp_buf_main + 4), buf_len);
+ temp_buf_upd_1 += buf_len;
+ }
+ break;
+ case DIAG_ID_SENSORS:
+ ctxt_upd_2 = UPD_SENSORS;
+ len_upd_2 += buf_len;
+ if (temp_buf_upd_2) {
+ memcpy(temp_buf_upd_2,
+ (temp_buf_main + 4), buf_len);
+ temp_buf_upd_2 += buf_len;
+ }
+ break;
+ case DIAG_ID_CDSP:
+ ctxt_cpd = DIAG_ID_CDSP;
+ len_cpd += buf_len;
+ if (temp_buf_cpd) {
+ memcpy(temp_buf_cpd,
+ (temp_buf_main + 4), buf_len);
+ temp_buf_cpd += buf_len;
+ }
+ break;
+ default:
+ goto end;
}
len = len - 4;
temp_buf_main += (buf_len + 4);
processed += buf_len;
}
- if (fwd_info->type == TYPE_DATA && len_upd_1) {
+ if (peripheral == PERIPHERAL_LPASS &&
+ fwd_info->type == TYPE_DATA && len_upd_2) {
+ if (flag_buf_1) {
+ driver->upd_len_2_a = len_upd_2;
+ temp_ptr_upd = fwd_info->buf_upd_2_a;
+ } else {
+ driver->upd_len_2_b = len_upd_2;
+ temp_ptr_upd = fwd_info->buf_upd_2_b;
+ }
+ temp_ptr_upd->ctxt &= 0x00FFFFFF;
+ temp_ptr_upd->ctxt |=
+ (SET_PD_CTXT(ctxt_upd_2));
+ atomic_set(&temp_ptr_upd->in_busy, 1);
+ diagfwd_data_process_done(fwd_info,
+ temp_ptr_upd, len_upd_2);
+ } else {
if (flag_buf_1)
+ driver->upd_len_2_a = 0;
+ if (flag_buf_2)
+ driver->upd_len_2_b = 0;
+ }
+ if (fwd_info->type == TYPE_DATA && len_upd_1) {
+ if (flag_buf_1) {
+ driver->upd_len_1_a[peripheral] =
+ len_upd_1;
temp_ptr_upd = fwd_info->buf_upd_1_a;
- else
+ } else {
+ driver->upd_len_1_b[peripheral] =
+ len_upd_1;
temp_ptr_upd = fwd_info->buf_upd_1_b;
+ }
temp_ptr_upd->ctxt &= 0x00FFFFFF;
temp_ptr_upd->ctxt |=
(SET_PD_CTXT(ctxt_upd_1));
atomic_set(&temp_ptr_upd->in_busy, 1);
diagfwd_data_process_done(fwd_info,
temp_ptr_upd, len_upd_1);
+ } else {
+ if (flag_buf_1)
+ driver->upd_len_1_a[peripheral] = 0;
+ if (flag_buf_2)
+ driver->upd_len_1_b[peripheral] = 0;
}
if (len_cpd) {
- if (flag_buf_1) {
- driver->cpd_len_1 = len_cpd;
- temp_ptr_cpd = fwd_info->buf_1;
- } else {
- driver->cpd_len_2 = len_cpd;
- temp_ptr_cpd = fwd_info->buf_2;
- }
+ if (flag_buf_1)
+ driver->cpd_len_1[peripheral] = len_cpd;
+ else
+ driver->cpd_len_2[peripheral] = len_cpd;
temp_ptr_cpd->ctxt &= 0x00FFFFFF;
temp_ptr_cpd->ctxt |=
(SET_PD_CTXT(ctxt_cpd));
@@ -422,14 +515,24 @@ static void diagfwd_data_read_untag_done(struct diagfwd_info *fwd_info,
temp_ptr_cpd, len_cpd);
} else {
if (flag_buf_1)
- driver->cpd_len_1 = 0;
+ driver->cpd_len_1[peripheral] = 0;
if (flag_buf_2)
- driver->cpd_len_2 = 0;
+ driver->cpd_len_2[peripheral] = 0;
}
mutex_unlock(&driver->diagfwd_untag_mutex);
+ return;
} else {
diagfwd_data_read_done(fwd_info, buf, len);
+ return;
}
+end:
+ diag_ws_release();
+ mutex_unlock(&driver->diagfwd_untag_mutex);
+ if (temp_ptr_cpd) {
+ diagfwd_write_done(fwd_info->peripheral, fwd_info->type,
+ GET_BUF_NUM(temp_ptr_cpd->ctxt));
+ }
+ diagfwd_queue_read(fwd_info);
}
static void diagfwd_data_read_done(struct diagfwd_info *fwd_info,
@@ -1166,20 +1269,78 @@ void diagfwd_write_done(uint8_t peripheral, uint8_t type, int ctxt)
return;
fwd_info = &peripheral_info[type][peripheral];
+
if (ctxt == 1 && fwd_info->buf_1) {
+ /* Buffer 1 for core PD is freed */
atomic_set(&fwd_info->buf_1->in_busy, 0);
- driver->cpd_len_1 = 0;
+ driver->cpd_len_1[peripheral] = 0;
} else if (ctxt == 2 && fwd_info->buf_2) {
+ /* Buffer 2 for core PD is freed */
atomic_set(&fwd_info->buf_2->in_busy, 0);
- driver->cpd_len_2 = 0;
+ driver->cpd_len_2[peripheral] = 0;
} else if (ctxt == 3 && fwd_info->buf_upd_1_a) {
+ /* Buffer 1 for user pd 1 is freed */
atomic_set(&fwd_info->buf_upd_1_a->in_busy, 0);
- if (driver->cpd_len_1 == 0)
- atomic_set(&fwd_info->buf_1->in_busy, 0);
+
+ if (peripheral == PERIPHERAL_LPASS) {
+ /* if not data in cpd and other user pd
+ * free the core pd buffer for LPASS
+ */
+ if (!driver->cpd_len_1[PERIPHERAL_LPASS] &&
+ !driver->upd_len_2_a)
+ atomic_set(&fwd_info->buf_1->in_busy, 0);
+ } else {
+ /* if not data in cpd
+ * free the core pd buffer for MPSS
+ */
+ if (!driver->cpd_len_1[PERIPHERAL_MODEM])
+ atomic_set(&fwd_info->buf_1->in_busy, 0);
+ }
+ driver->upd_len_1_a[peripheral] = 0;
+
} else if (ctxt == 4 && fwd_info->buf_upd_1_b) {
+ /* Buffer 2 for user pd 1 is freed */
atomic_set(&fwd_info->buf_upd_1_b->in_busy, 0);
- if (driver->cpd_len_2 == 0)
+ if (peripheral == PERIPHERAL_LPASS) {
+ /* if not data in cpd and other user pd
+ * free the core pd buffer for LPASS
+ */
+ if (!driver->cpd_len_2[peripheral] &&
+ !driver->upd_len_2_b)
+ atomic_set(&fwd_info->buf_2->in_busy, 0);
+ } else {
+ /* if not data in cpd
+ * free the core pd buffer for MPSS
+ */
+ if (!driver->cpd_len_2[PERIPHERAL_MODEM])
+ atomic_set(&fwd_info->buf_2->in_busy, 0);
+ }
+ driver->upd_len_1_b[peripheral] = 0;
+
+ } else if (ctxt == 5 && fwd_info->buf_upd_2_a) {
+ /* Buffer 1 for user pd 2 is freed */
+ atomic_set(&fwd_info->buf_upd_2_a->in_busy, 0);
+ /* if not data in cpd and other user pd
+ * free the core pd buffer for LPASS
+ */
+ if (!driver->cpd_len_1[PERIPHERAL_LPASS] &&
+ !driver->upd_len_1_a[PERIPHERAL_LPASS])
+ atomic_set(&fwd_info->buf_1->in_busy, 0);
+
+ driver->upd_len_2_a = 0;
+
+ } else if (ctxt == 6 && fwd_info->buf_upd_2_b) {
+ /* Buffer 2 for user pd 2 is freed */
+ atomic_set(&fwd_info->buf_upd_2_b->in_busy, 0);
+ /* if not data in cpd and other user pd
+ * free the core pd buffer for LPASS
+ */
+ if (!driver->cpd_len_2[PERIPHERAL_LPASS] &&
+ !driver->upd_len_1_b[PERIPHERAL_LPASS])
atomic_set(&fwd_info->buf_2->in_busy, 0);
+
+ driver->upd_len_2_b = 0;
+
} else
pr_err("diag: In %s, invalid ctxt %d\n", __func__, ctxt);
@@ -1312,7 +1473,8 @@ static void diagfwd_queue_read(struct diagfwd_info *fwd_info)
void diagfwd_buffers_init(struct diagfwd_info *fwd_info)
{
- unsigned char *temp_buf = NULL;
+ struct diagfwd_buf_t *temp_fwd_buf;
+ unsigned char *temp_char_buf;
if (!fwd_info)
return;
@@ -1383,11 +1545,11 @@ void diagfwd_buffers_init(struct diagfwd_info *fwd_info)
kzalloc(PERIPHERAL_BUF_SZ +
APF_DIAG_PADDING,
GFP_KERNEL);
- temp_buf = fwd_info->buf_upd_1_a->data;
- if (ZERO_OR_NULL_PTR(temp_buf))
+ temp_char_buf = fwd_info->buf_upd_1_a->data;
+ if (ZERO_OR_NULL_PTR(temp_char_buf))
goto err;
fwd_info->buf_upd_1_a->len = PERIPHERAL_BUF_SZ;
- kmemleak_not_leak(temp_buf);
+ kmemleak_not_leak(temp_char_buf);
fwd_info->buf_upd_1_a->ctxt = SET_BUF_CTXT(
fwd_info->peripheral,
fwd_info->type, 3);
@@ -1408,16 +1570,76 @@ void diagfwd_buffers_init(struct diagfwd_info *fwd_info)
kzalloc(PERIPHERAL_BUF_SZ +
APF_DIAG_PADDING,
GFP_KERNEL);
- temp_buf = fwd_info->buf_upd_1_b->data;
- if (ZERO_OR_NULL_PTR(temp_buf))
+ temp_char_buf =
+ fwd_info->buf_upd_1_b->data;
+ if (ZERO_OR_NULL_PTR(temp_char_buf))
goto err;
fwd_info->buf_upd_1_b->len =
PERIPHERAL_BUF_SZ;
- kmemleak_not_leak(temp_buf);
+ kmemleak_not_leak(temp_char_buf);
fwd_info->buf_upd_1_b->ctxt = SET_BUF_CTXT(
fwd_info->peripheral,
fwd_info->type, 4);
}
+ if (fwd_info->peripheral ==
+ PERIPHERAL_LPASS) {
+ if (!fwd_info->buf_upd_2_a) {
+ fwd_info->buf_upd_2_a =
+ kzalloc(sizeof(struct diagfwd_buf_t),
+ GFP_KERNEL);
+ temp_fwd_buf =
+ fwd_info->buf_upd_2_a;
+ if (ZERO_OR_NULL_PTR(temp_fwd_buf))
+ goto err;
+ kmemleak_not_leak(temp_fwd_buf);
+ }
+
+ if (!fwd_info->buf_upd_2_a->data) {
+ fwd_info->buf_upd_2_a->data =
+ kzalloc(PERIPHERAL_BUF_SZ +
+ APF_DIAG_PADDING,
+ GFP_KERNEL);
+ temp_char_buf =
+ fwd_info->buf_upd_2_a->data;
+ if (ZERO_OR_NULL_PTR(temp_char_buf))
+ goto err;
+ fwd_info->buf_upd_2_a->len =
+ PERIPHERAL_BUF_SZ;
+ kmemleak_not_leak(temp_char_buf);
+ fwd_info->buf_upd_2_a->ctxt =
+ SET_BUF_CTXT(
+ fwd_info->peripheral,
+ fwd_info->type, 5);
+ }
+ if (!fwd_info->buf_upd_2_b) {
+ fwd_info->buf_upd_2_b =
+ kzalloc(sizeof(struct diagfwd_buf_t),
+ GFP_KERNEL);
+ temp_fwd_buf =
+ fwd_info->buf_upd_2_b;
+ if (ZERO_OR_NULL_PTR(temp_fwd_buf))
+ goto err;
+ kmemleak_not_leak(temp_fwd_buf);
+ }
+
+ if (!fwd_info->buf_upd_2_b->data) {
+ fwd_info->buf_upd_2_b->data =
+ kzalloc(PERIPHERAL_BUF_SZ +
+ APF_DIAG_PADDING,
+ GFP_KERNEL);
+ temp_char_buf =
+ fwd_info->buf_upd_2_b->data;
+ if (ZERO_OR_NULL_PTR(temp_char_buf))
+ goto err;
+ fwd_info->buf_upd_2_b->len =
+ PERIPHERAL_BUF_SZ;
+ kmemleak_not_leak(temp_char_buf);
+ fwd_info->buf_upd_2_b->ctxt =
+ SET_BUF_CTXT(
+ fwd_info->peripheral,
+ fwd_info->type, 6);
+ }
+ }
}
if (driver->supports_apps_hdlc_encoding) {
@@ -1427,12 +1649,13 @@ void diagfwd_buffers_init(struct diagfwd_info *fwd_info)
kzalloc(PERIPHERAL_BUF_SZ +
APF_DIAG_PADDING,
GFP_KERNEL);
- temp_buf = fwd_info->buf_1->data_raw;
- if (ZERO_OR_NULL_PTR(temp_buf))
+ temp_char_buf =
+ fwd_info->buf_1->data_raw;
+ if (ZERO_OR_NULL_PTR(temp_char_buf))
goto err;
fwd_info->buf_1->len_raw =
PERIPHERAL_BUF_SZ;
- kmemleak_not_leak(temp_buf);
+ kmemleak_not_leak(temp_char_buf);
}
if (!fwd_info->buf_2->data_raw) {
@@ -1440,12 +1663,13 @@ void diagfwd_buffers_init(struct diagfwd_info *fwd_info)
kzalloc(PERIPHERAL_BUF_SZ +
APF_DIAG_PADDING,
GFP_KERNEL);
- temp_buf = fwd_info->buf_2->data_raw;
- if (ZERO_OR_NULL_PTR(temp_buf))
+ temp_char_buf =
+ fwd_info->buf_2->data_raw;
+ if (ZERO_OR_NULL_PTR(temp_char_buf))
goto err;
fwd_info->buf_2->len_raw =
PERIPHERAL_BUF_SZ;
- kmemleak_not_leak(temp_buf);
+ kmemleak_not_leak(temp_char_buf);
}
if (driver->feature[fwd_info->peripheral].
@@ -1456,13 +1680,13 @@ void diagfwd_buffers_init(struct diagfwd_info *fwd_info)
kzalloc(PERIPHERAL_BUF_SZ +
APF_DIAG_PADDING,
GFP_KERNEL);
- temp_buf =
+ temp_char_buf =
fwd_info->buf_upd_1_a->data_raw;
- if (ZERO_OR_NULL_PTR(temp_buf))
+ if (ZERO_OR_NULL_PTR(temp_char_buf))
goto err;
fwd_info->buf_upd_1_a->len_raw =
PERIPHERAL_BUF_SZ;
- kmemleak_not_leak(temp_buf);
+ kmemleak_not_leak(temp_char_buf);
}
if (fwd_info->buf_upd_1_b &&
@@ -1471,13 +1695,41 @@ void diagfwd_buffers_init(struct diagfwd_info *fwd_info)
kzalloc(PERIPHERAL_BUF_SZ +
APF_DIAG_PADDING,
GFP_KERNEL);
- temp_buf =
+ temp_char_buf =
fwd_info->buf_upd_1_b->data_raw;
- if (ZERO_OR_NULL_PTR(temp_buf))
+ if (ZERO_OR_NULL_PTR(temp_char_buf))
goto err;
fwd_info->buf_upd_1_b->len_raw =
PERIPHERAL_BUF_SZ;
- kmemleak_not_leak(temp_buf);
+ kmemleak_not_leak(temp_char_buf);
+ }
+ if (fwd_info->peripheral == PERIPHERAL_LPASS
+ && !fwd_info->buf_upd_2_a->data_raw) {
+ fwd_info->buf_upd_2_a->data_raw =
+ kzalloc(PERIPHERAL_BUF_SZ +
+ APF_DIAG_PADDING,
+ GFP_KERNEL);
+ temp_char_buf =
+ fwd_info->buf_upd_2_a->data_raw;
+ if (ZERO_OR_NULL_PTR(temp_char_buf))
+ goto err;
+ fwd_info->buf_upd_2_a->len_raw =
+ PERIPHERAL_BUF_SZ;
+ kmemleak_not_leak(temp_char_buf);
+ }
+ if (fwd_info->peripheral == PERIPHERAL_LPASS
+ && !fwd_info->buf_upd_2_b->data_raw) {
+ fwd_info->buf_upd_2_b->data_raw =
+ kzalloc(PERIPHERAL_BUF_SZ +
+ APF_DIAG_PADDING,
+ GFP_KERNEL);
+ temp_char_buf =
+ fwd_info->buf_upd_2_b->data_raw;
+ if (ZERO_OR_NULL_PTR(temp_char_buf))
+ goto err;
+ fwd_info->buf_upd_2_b->len_raw =
+ PERIPHERAL_BUF_SZ;
+ kmemleak_not_leak(temp_char_buf);
}
}
}
@@ -1490,10 +1742,12 @@ void diagfwd_buffers_init(struct diagfwd_info *fwd_info)
fwd_info->buf_1->data_raw = kzalloc(PERIPHERAL_BUF_SZ +
APF_DIAG_PADDING,
GFP_KERNEL);
- if (!fwd_info->buf_1->data_raw)
+ temp_char_buf =
+ fwd_info->buf_1->data_raw;
+ if (ZERO_OR_NULL_PTR(temp_char_buf))
goto err;
fwd_info->buf_1->len_raw = PERIPHERAL_BUF_SZ;
- kmemleak_not_leak(fwd_info->buf_1->data_raw);
+ kmemleak_not_leak(temp_char_buf);
}
}
@@ -1530,6 +1784,38 @@ static void diagfwd_buffers_exit(struct diagfwd_info *fwd_info)
kfree(fwd_info->buf_2);
fwd_info->buf_2 = NULL;
}
+ if (fwd_info->buf_upd_1_a) {
+ kfree(fwd_info->buf_upd_1_a->data);
+ fwd_info->buf_upd_1_a->data = NULL;
+ kfree(fwd_info->buf_upd_1_a->data_raw);
+ fwd_info->buf_upd_1_a->data_raw = NULL;
+ kfree(fwd_info->buf_upd_1_a);
+ fwd_info->buf_upd_1_a = NULL;
+ }
+ if (fwd_info->buf_upd_1_b) {
+ kfree(fwd_info->buf_upd_1_b->data);
+ fwd_info->buf_upd_1_b->data = NULL;
+ kfree(fwd_info->buf_upd_1_b->data_raw);
+ fwd_info->buf_upd_1_b->data_raw = NULL;
+ kfree(fwd_info->buf_upd_1_b);
+ fwd_info->buf_upd_1_b = NULL;
+ }
+ if (fwd_info->buf_upd_2_a) {
+ kfree(fwd_info->buf_upd_2_a->data);
+ fwd_info->buf_upd_2_a->data = NULL;
+ kfree(fwd_info->buf_upd_2_a->data_raw);
+ fwd_info->buf_upd_2_a->data_raw = NULL;
+ kfree(fwd_info->buf_upd_2_a);
+ fwd_info->buf_upd_2_a = NULL;
+ }
+ if (fwd_info->buf_upd_2_b) {
+ kfree(fwd_info->buf_upd_2_b->data);
+ fwd_info->buf_upd_2_b->data = NULL;
+ kfree(fwd_info->buf_upd_2_b->data_raw);
+ fwd_info->buf_upd_2_b->data_raw = NULL;
+ kfree(fwd_info->buf_upd_2_b);
+ fwd_info->buf_upd_2_b = NULL;
+ }
mutex_unlock(&fwd_info->buf_mutex);
}
diff --git a/drivers/char/diag/diagfwd_peripheral.h b/drivers/char/diag/diagfwd_peripheral.h
index f483da81cc96..760f139ff428 100644
--- a/drivers/char/diag/diagfwd_peripheral.h
+++ b/drivers/char/diag/diagfwd_peripheral.h
@@ -80,6 +80,8 @@ struct diagfwd_info {
struct diagfwd_buf_t *buf_2;
struct diagfwd_buf_t *buf_upd_1_a;
struct diagfwd_buf_t *buf_upd_1_b;
+ struct diagfwd_buf_t *buf_upd_2_a;
+ struct diagfwd_buf_t *buf_upd_2_b;
struct diagfwd_buf_t *buf_ptr[NUM_WRITE_BUFFERS];
struct diag_peripheral_ops *p_ops;
struct diag_channel_ops *c_ops;
diff --git a/drivers/char/msm_smd_pkt.c b/drivers/char/msm_smd_pkt.c
new file mode 100644
index 000000000000..a61d273bfb65
--- /dev/null
+++ b/drivers/char/msm_smd_pkt.c
@@ -0,0 +1,1397 @@
+/* Copyright (c) 2008-2017, 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.
+ *
+ */
+/*
+ * SMD Packet Driver -- Provides a binary SMD non-muxed packet port
+ * interface.
+ */
+
+#include <linux/slab.h>
+#include <linux/cdev.h>
+#include <linux/module.h>
+#include <linux/fs.h>
+#include <linux/device.h>
+#include <linux/sched.h>
+#include <linux/spinlock.h>
+#include <linux/mutex.h>
+#include <linux/delay.h>
+#include <linux/uaccess.h>
+#include <linux/workqueue.h>
+#include <linux/platform_device.h>
+#include <linux/completion.h>
+#include <linux/msm_smd_pkt.h>
+#include <linux/poll.h>
+#include <soc/qcom/smd.h>
+#include <soc/qcom/smsm.h>
+#include <soc/qcom/subsystem_restart.h>
+#include <asm/ioctls.h>
+#include <linux/pm.h>
+#include <linux/of.h>
+#include <linux/ipc_logging.h>
+
+#define MODULE_NAME "msm_smdpkt"
+#define DEVICE_NAME "smdpkt"
+#define WAKEUPSOURCE_TIMEOUT (2000) /* two seconds */
+
+struct smd_pkt_dev {
+ struct list_head dev_list;
+ char dev_name[SMD_MAX_CH_NAME_LEN];
+ char ch_name[SMD_MAX_CH_NAME_LEN];
+ uint32_t edge;
+
+ struct cdev cdev;
+ struct device *devicep;
+ void *pil;
+
+ struct smd_channel *ch;
+ struct mutex ch_lock;
+ struct mutex rx_lock;
+ struct mutex tx_lock;
+ wait_queue_head_t ch_read_wait_queue;
+ wait_queue_head_t ch_write_wait_queue;
+ wait_queue_head_t ch_opened_wait_queue;
+
+ int i;
+ int ref_cnt;
+
+ int blocking_write;
+ int is_open;
+ int poll_mode;
+ unsigned ch_size;
+ uint open_modem_wait;
+
+ int has_reset;
+ int do_reset_notification;
+ struct completion ch_allocated;
+ struct wakeup_source pa_ws; /* Packet Arrival Wakeup Source */
+ struct work_struct packet_arrival_work;
+ spinlock_t pa_spinlock;
+ int ws_locked;
+};
+
+
+struct smd_pkt_driver {
+ struct list_head list;
+ int ref_cnt;
+ char pdriver_name[SMD_MAX_CH_NAME_LEN];
+ struct platform_driver driver;
+};
+
+static DEFINE_MUTEX(smd_pkt_driver_lock_lha1);
+static LIST_HEAD(smd_pkt_driver_list);
+
+struct class *smd_pkt_classp;
+static dev_t smd_pkt_number;
+static struct delayed_work loopback_work;
+static void check_and_wakeup_reader(struct smd_pkt_dev *smd_pkt_devp);
+static void check_and_wakeup_writer(struct smd_pkt_dev *smd_pkt_devp);
+static uint32_t is_modem_smsm_inited(void);
+
+static DEFINE_MUTEX(smd_pkt_dev_lock_lha1);
+static LIST_HEAD(smd_pkt_dev_list);
+static int num_smd_pkt_ports;
+
+#define SMD_PKT_IPC_LOG_PAGE_CNT 2
+static void *smd_pkt_ilctxt;
+
+static int msm_smd_pkt_debug_mask;
+module_param_named(debug_mask, msm_smd_pkt_debug_mask,
+ int, S_IRUGO | S_IWUSR | S_IWGRP);
+
+enum {
+ SMD_PKT_STATUS = 1U << 0,
+ SMD_PKT_READ = 1U << 1,
+ SMD_PKT_WRITE = 1U << 2,
+ SMD_PKT_POLL = 1U << 5,
+};
+
+#define DEBUG
+
+#ifdef DEBUG
+
+#define SMD_PKT_LOG_STRING(x...) \
+do { \
+ if (smd_pkt_ilctxt) \
+ ipc_log_string(smd_pkt_ilctxt, "<SMD_PKT>: "x); \
+} while (0)
+
+#define D_STATUS(x...) \
+do { \
+ if (msm_smd_pkt_debug_mask & SMD_PKT_STATUS) \
+ pr_info("Status: "x); \
+ SMD_PKT_LOG_STRING(x); \
+} while (0)
+
+#define D_READ(x...) \
+do { \
+ if (msm_smd_pkt_debug_mask & SMD_PKT_READ) \
+ pr_info("Read: "x); \
+ SMD_PKT_LOG_STRING(x); \
+} while (0)
+
+#define D_WRITE(x...) \
+do { \
+ if (msm_smd_pkt_debug_mask & SMD_PKT_WRITE) \
+ pr_info("Write: "x); \
+ SMD_PKT_LOG_STRING(x); \
+} while (0)
+
+#define D_POLL(x...) \
+do { \
+ if (msm_smd_pkt_debug_mask & SMD_PKT_POLL) \
+ pr_info("Poll: "x); \
+ SMD_PKT_LOG_STRING(x); \
+} while (0)
+
+#define E_SMD_PKT_SSR(x) \
+do { \
+ if (x->do_reset_notification) \
+ pr_err("%s notifying reset for smd_pkt_dev id:%d\n", \
+ __func__, x->i); \
+} while (0)
+#else
+#define D_STATUS(x...) do {} while (0)
+#define D_READ(x...) do {} while (0)
+#define D_WRITE(x...) do {} while (0)
+#define D_POLL(x...) do {} while (0)
+#define E_SMD_PKT_SSR(x) do {} while (0)
+#endif
+
+static ssize_t open_timeout_store(struct device *d,
+ struct device_attribute *attr,
+ const char *buf,
+ size_t n)
+{
+ struct smd_pkt_dev *smd_pkt_devp;
+ unsigned long tmp;
+
+ mutex_lock(&smd_pkt_dev_lock_lha1);
+ list_for_each_entry(smd_pkt_devp, &smd_pkt_dev_list, dev_list) {
+ if (smd_pkt_devp->devicep == d) {
+ if (!kstrtoul(buf, 10, &tmp)) {
+ smd_pkt_devp->open_modem_wait = tmp;
+ mutex_unlock(&smd_pkt_dev_lock_lha1);
+ return n;
+ }
+ mutex_unlock(&smd_pkt_dev_lock_lha1);
+ pr_err("%s: unable to convert: %s to an int\n",
+ __func__, buf);
+ return -EINVAL;
+ }
+ }
+ mutex_unlock(&smd_pkt_dev_lock_lha1);
+
+ pr_err("%s: unable to match device to valid smd_pkt port\n", __func__);
+ return -EINVAL;
+}
+
+static ssize_t open_timeout_show(struct device *d,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct smd_pkt_dev *smd_pkt_devp;
+
+ mutex_lock(&smd_pkt_dev_lock_lha1);
+ list_for_each_entry(smd_pkt_devp, &smd_pkt_dev_list, dev_list) {
+ if (smd_pkt_devp->devicep == d) {
+ mutex_unlock(&smd_pkt_dev_lock_lha1);
+ return snprintf(buf, PAGE_SIZE, "%d\n",
+ smd_pkt_devp->open_modem_wait);
+ }
+ }
+ mutex_unlock(&smd_pkt_dev_lock_lha1);
+ pr_err("%s: unable to match device to valid smd_pkt port\n", __func__);
+ return -EINVAL;
+
+}
+
+static DEVICE_ATTR(open_timeout, 0664, open_timeout_show, open_timeout_store);
+
+/**
+ * loopback_edge_store() - Set the edge type for loopback device
+ * @d: Linux device structure
+ * @attr: Device attribute structure
+ * @buf: Input string
+ * @n: Length of the input string
+ *
+ * This function is used to set the loopback device edge runtime
+ * by writing to the loopback_edge node.
+ */
+static ssize_t loopback_edge_store(struct device *d,
+ struct device_attribute *attr,
+ const char *buf,
+ size_t n)
+{
+ struct smd_pkt_dev *smd_pkt_devp;
+ unsigned long tmp;
+
+ mutex_lock(&smd_pkt_dev_lock_lha1);
+ list_for_each_entry(smd_pkt_devp, &smd_pkt_dev_list, dev_list) {
+ if (smd_pkt_devp->devicep == d) {
+ if (!kstrtoul(buf, 10, &tmp)) {
+ smd_pkt_devp->edge = tmp;
+ mutex_unlock(&smd_pkt_dev_lock_lha1);
+ return n;
+ }
+ mutex_unlock(&smd_pkt_dev_lock_lha1);
+ pr_err("%s: unable to convert: %s to an int\n",
+ __func__, buf);
+ return -EINVAL;
+ }
+ }
+ mutex_unlock(&smd_pkt_dev_lock_lha1);
+ pr_err("%s: unable to match device to valid smd_pkt port\n", __func__);
+ return -EINVAL;
+}
+
+/**
+ * loopback_edge_show() - Get the edge type for loopback device
+ * @d: Linux device structure
+ * @attr: Device attribute structure
+ * @buf: Output buffer
+ *
+ * This function is used to get the loopback device edge runtime
+ * by reading the loopback_edge node.
+ */
+static ssize_t loopback_edge_show(struct device *d,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct smd_pkt_dev *smd_pkt_devp;
+
+ mutex_lock(&smd_pkt_dev_lock_lha1);
+ list_for_each_entry(smd_pkt_devp, &smd_pkt_dev_list, dev_list) {
+ if (smd_pkt_devp->devicep == d) {
+ mutex_unlock(&smd_pkt_dev_lock_lha1);
+ return snprintf(buf, PAGE_SIZE, "%d\n",
+ smd_pkt_devp->edge);
+ }
+ }
+ mutex_unlock(&smd_pkt_dev_lock_lha1);
+ pr_err("%s: unable to match device to valid smd_pkt port\n", __func__);
+ return -EINVAL;
+
+}
+
+static DEVICE_ATTR(loopback_edge, 0664, loopback_edge_show,
+ loopback_edge_store);
+
+static int notify_reset(struct smd_pkt_dev *smd_pkt_devp)
+{
+ smd_pkt_devp->do_reset_notification = 0;
+
+ return -ENETRESET;
+}
+
+static void clean_and_signal(struct smd_pkt_dev *smd_pkt_devp)
+{
+ smd_pkt_devp->do_reset_notification = 1;
+ smd_pkt_devp->has_reset = 1;
+
+ smd_pkt_devp->is_open = 0;
+
+ wake_up(&smd_pkt_devp->ch_read_wait_queue);
+ wake_up(&smd_pkt_devp->ch_write_wait_queue);
+ wake_up_interruptible(&smd_pkt_devp->ch_opened_wait_queue);
+ D_STATUS("%s smd_pkt_dev id:%d\n", __func__, smd_pkt_devp->i);
+}
+
+static void loopback_probe_worker(struct work_struct *work)
+{
+
+ /* Wait for the modem SMSM to be inited for the SMD
+ ** Loopback channel to be allocated at the modem. Since
+ ** the wait need to be done atmost once, using msleep
+ ** doesn't degrade the performance.
+ */
+ if (!is_modem_smsm_inited())
+ schedule_delayed_work(&loopback_work, msecs_to_jiffies(1000));
+ else
+ smsm_change_state(SMSM_APPS_STATE,
+ 0, SMSM_SMD_LOOPBACK);
+
+}
+
+static void packet_arrival_worker(struct work_struct *work)
+{
+ struct smd_pkt_dev *smd_pkt_devp;
+ unsigned long flags;
+
+ smd_pkt_devp = container_of(work, struct smd_pkt_dev,
+ packet_arrival_work);
+ mutex_lock(&smd_pkt_devp->ch_lock);
+ spin_lock_irqsave(&smd_pkt_devp->pa_spinlock, flags);
+ if (smd_pkt_devp->ch && smd_pkt_devp->ws_locked) {
+ D_READ("%s locking smd_pkt_dev id:%d wakeup source\n",
+ __func__, smd_pkt_devp->i);
+ /*
+ * Keep system awake long enough to allow userspace client
+ * to process the packet.
+ */
+ __pm_wakeup_event(&smd_pkt_devp->pa_ws, WAKEUPSOURCE_TIMEOUT);
+ }
+ spin_unlock_irqrestore(&smd_pkt_devp->pa_spinlock, flags);
+ mutex_unlock(&smd_pkt_devp->ch_lock);
+}
+
+static long smd_pkt_ioctl(struct file *file, unsigned int cmd,
+ unsigned long arg)
+{
+ int ret;
+ struct smd_pkt_dev *smd_pkt_devp;
+ uint32_t val;
+
+ smd_pkt_devp = file->private_data;
+ if (!smd_pkt_devp)
+ return -EINVAL;
+
+ mutex_lock(&smd_pkt_devp->ch_lock);
+ switch (cmd) {
+ case TIOCMGET:
+ D_STATUS("%s TIOCMGET command on smd_pkt_dev id:%d\n",
+ __func__, smd_pkt_devp->i);
+ ret = smd_tiocmget(smd_pkt_devp->ch);
+ break;
+ case TIOCMSET:
+ ret = get_user(val, (uint32_t *)arg);
+ if (ret) {
+ pr_err("Error getting TIOCMSET value\n");
+ mutex_unlock(&smd_pkt_devp->ch_lock);
+ return ret;
+ }
+ D_STATUS("%s TIOCSET command on smd_pkt_dev id:%d arg[0x%x]\n",
+ __func__, smd_pkt_devp->i, val);
+ ret = smd_tiocmset(smd_pkt_devp->ch, val, ~val);
+ break;
+ case SMD_PKT_IOCTL_BLOCKING_WRITE:
+ ret = get_user(smd_pkt_devp->blocking_write, (int *)arg);
+ break;
+ default:
+ pr_err_ratelimited("%s: Unrecognized ioctl command %d\n",
+ __func__, cmd);
+ ret = -ENOIOCTLCMD;
+ }
+ mutex_unlock(&smd_pkt_devp->ch_lock);
+
+ return ret;
+}
+
+ssize_t smd_pkt_read(struct file *file,
+ char __user *_buf,
+ size_t count,
+ loff_t *ppos)
+{
+ int r;
+ int bytes_read;
+ int pkt_size;
+ struct smd_pkt_dev *smd_pkt_devp;
+ unsigned long flags;
+ void *buf;
+
+ smd_pkt_devp = file->private_data;
+
+ if (!smd_pkt_devp) {
+ pr_err_ratelimited("%s on NULL smd_pkt_dev\n", __func__);
+ return -EINVAL;
+ }
+
+ if (!smd_pkt_devp->ch) {
+ pr_err_ratelimited("%s on a closed smd_pkt_dev id:%d\n",
+ __func__, smd_pkt_devp->i);
+ return -EINVAL;
+ }
+
+ if (smd_pkt_devp->do_reset_notification) {
+ /* notify client that a reset occurred */
+ E_SMD_PKT_SSR(smd_pkt_devp);
+ return notify_reset(smd_pkt_devp);
+ }
+ D_READ("Begin %s on smd_pkt_dev id:%d buffer_size %zu\n",
+ __func__, smd_pkt_devp->i, count);
+
+ buf = kmalloc(count, GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
+
+wait_for_packet:
+ r = wait_event_interruptible(smd_pkt_devp->ch_read_wait_queue,
+ !smd_pkt_devp->ch ||
+ (smd_cur_packet_size(smd_pkt_devp->ch) > 0
+ && smd_read_avail(smd_pkt_devp->ch)) ||
+ smd_pkt_devp->has_reset);
+
+ mutex_lock(&smd_pkt_devp->rx_lock);
+ if (smd_pkt_devp->has_reset) {
+ mutex_unlock(&smd_pkt_devp->rx_lock);
+ E_SMD_PKT_SSR(smd_pkt_devp);
+ kfree(buf);
+ return notify_reset(smd_pkt_devp);
+ }
+
+ if (!smd_pkt_devp->ch) {
+ mutex_unlock(&smd_pkt_devp->rx_lock);
+ pr_err_ratelimited("%s on a closed smd_pkt_dev id:%d\n",
+ __func__, smd_pkt_devp->i);
+ kfree(buf);
+ return -EINVAL;
+ }
+
+ if (r < 0) {
+ mutex_unlock(&smd_pkt_devp->rx_lock);
+ /* qualify error message */
+ if (r != -ERESTARTSYS) {
+ /* we get this anytime a signal comes in */
+ pr_err_ratelimited("%s: wait_event_interruptible on smd_pkt_dev id:%d ret %i\n",
+ __func__, smd_pkt_devp->i, r);
+ }
+ kfree(buf);
+ return r;
+ }
+
+ /* Here we have a whole packet waiting for us */
+ pkt_size = smd_cur_packet_size(smd_pkt_devp->ch);
+
+ if (!pkt_size) {
+ pr_err_ratelimited("%s: No data on smd_pkt_dev id:%d, False wakeup\n",
+ __func__, smd_pkt_devp->i);
+ mutex_unlock(&smd_pkt_devp->rx_lock);
+ goto wait_for_packet;
+ }
+
+ if (pkt_size < 0) {
+ pr_err_ratelimited("%s: Error %d obtaining packet size for Channel %s",
+ __func__, pkt_size, smd_pkt_devp->ch_name);
+ kfree(buf);
+ return pkt_size;
+ }
+
+ if ((uint32_t)pkt_size > count) {
+ pr_err_ratelimited("%s: failure on smd_pkt_dev id: %d - packet size %d > buffer size %zu,",
+ __func__, smd_pkt_devp->i,
+ pkt_size, count);
+ mutex_unlock(&smd_pkt_devp->rx_lock);
+ kfree(buf);
+ return -ETOOSMALL;
+ }
+
+ bytes_read = 0;
+ do {
+ r = smd_read(smd_pkt_devp->ch,
+ (buf + bytes_read),
+ (pkt_size - bytes_read));
+ if (r < 0) {
+ mutex_unlock(&smd_pkt_devp->rx_lock);
+ if (smd_pkt_devp->has_reset) {
+ E_SMD_PKT_SSR(smd_pkt_devp);
+ return notify_reset(smd_pkt_devp);
+ }
+ pr_err_ratelimited("%s Error while reading %d\n",
+ __func__, r);
+ kfree(buf);
+ return r;
+ }
+ bytes_read += r;
+ if (pkt_size != bytes_read)
+ wait_event(smd_pkt_devp->ch_read_wait_queue,
+ smd_read_avail(smd_pkt_devp->ch) ||
+ smd_pkt_devp->has_reset);
+ if (smd_pkt_devp->has_reset) {
+ mutex_unlock(&smd_pkt_devp->rx_lock);
+ E_SMD_PKT_SSR(smd_pkt_devp);
+ kfree(buf);
+ return notify_reset(smd_pkt_devp);
+ }
+ } while (pkt_size != bytes_read);
+ mutex_unlock(&smd_pkt_devp->rx_lock);
+
+ mutex_lock(&smd_pkt_devp->ch_lock);
+ spin_lock_irqsave(&smd_pkt_devp->pa_spinlock, flags);
+ if (smd_pkt_devp->poll_mode &&
+ !smd_cur_packet_size(smd_pkt_devp->ch)) {
+ __pm_relax(&smd_pkt_devp->pa_ws);
+ smd_pkt_devp->ws_locked = 0;
+ smd_pkt_devp->poll_mode = 0;
+ D_READ("%s unlocked smd_pkt_dev id:%d wakeup_source\n",
+ __func__, smd_pkt_devp->i);
+ }
+ spin_unlock_irqrestore(&smd_pkt_devp->pa_spinlock, flags);
+ mutex_unlock(&smd_pkt_devp->ch_lock);
+
+ r = copy_to_user(_buf, buf, bytes_read);
+ if (r) {
+ kfree(buf);
+ return -EFAULT;
+ }
+ D_READ("Finished %s on smd_pkt_dev id:%d %d bytes\n",
+ __func__, smd_pkt_devp->i, bytes_read);
+ kfree(buf);
+
+ /* check and wakeup read threads waiting on this device */
+ check_and_wakeup_reader(smd_pkt_devp);
+
+ return bytes_read;
+}
+
+ssize_t smd_pkt_write(struct file *file,
+ const char __user *_buf,
+ size_t count,
+ loff_t *ppos)
+{
+ int r = 0, bytes_written;
+ struct smd_pkt_dev *smd_pkt_devp;
+ DEFINE_WAIT(write_wait);
+ void *buf;
+
+ smd_pkt_devp = file->private_data;
+
+ if (!smd_pkt_devp) {
+ pr_err_ratelimited("%s on NULL smd_pkt_dev\n", __func__);
+ return -EINVAL;
+ }
+
+ if (!smd_pkt_devp->ch) {
+ pr_err_ratelimited("%s on a closed smd_pkt_dev id:%d\n",
+ __func__, smd_pkt_devp->i);
+ return -EINVAL;
+ }
+
+ if (smd_pkt_devp->do_reset_notification || smd_pkt_devp->has_reset) {
+ E_SMD_PKT_SSR(smd_pkt_devp);
+ /* notify client that a reset occurred */
+ return notify_reset(smd_pkt_devp);
+ }
+ D_WRITE("Begin %s on smd_pkt_dev id:%d data_size %zu\n",
+ __func__, smd_pkt_devp->i, count);
+
+ buf = kmalloc(count, GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
+
+ r = copy_from_user(buf, _buf, count);
+ if (r) {
+ kfree(buf);
+ return -EFAULT;
+ }
+
+ mutex_lock(&smd_pkt_devp->tx_lock);
+ if (!smd_pkt_devp->blocking_write) {
+ if (smd_write_avail(smd_pkt_devp->ch) < count) {
+ pr_err_ratelimited("%s: Not enough space in smd_pkt_dev id:%d\n",
+ __func__, smd_pkt_devp->i);
+ mutex_unlock(&smd_pkt_devp->tx_lock);
+ kfree(buf);
+ return -ENOMEM;
+ }
+ }
+
+ r = smd_write_start(smd_pkt_devp->ch, count);
+ if (r < 0) {
+ mutex_unlock(&smd_pkt_devp->tx_lock);
+ pr_err_ratelimited("%s: Error:%d in smd_pkt_dev id:%d @ smd_write_start\n",
+ __func__, r, smd_pkt_devp->i);
+ kfree(buf);
+ return r;
+ }
+
+ bytes_written = 0;
+ do {
+ prepare_to_wait(&smd_pkt_devp->ch_write_wait_queue,
+ &write_wait, TASK_UNINTERRUPTIBLE);
+ if (!smd_write_segment_avail(smd_pkt_devp->ch) &&
+ !smd_pkt_devp->has_reset) {
+ smd_enable_read_intr(smd_pkt_devp->ch);
+ schedule();
+ }
+ finish_wait(&smd_pkt_devp->ch_write_wait_queue, &write_wait);
+ smd_disable_read_intr(smd_pkt_devp->ch);
+
+ if (smd_pkt_devp->has_reset) {
+ mutex_unlock(&smd_pkt_devp->tx_lock);
+ E_SMD_PKT_SSR(smd_pkt_devp);
+ kfree(buf);
+ return notify_reset(smd_pkt_devp);
+ }
+ r = smd_write_segment(smd_pkt_devp->ch,
+ (void *)(buf + bytes_written),
+ (count - bytes_written));
+ if (r < 0) {
+ mutex_unlock(&smd_pkt_devp->tx_lock);
+ if (smd_pkt_devp->has_reset) {
+ E_SMD_PKT_SSR(smd_pkt_devp);
+ return notify_reset(smd_pkt_devp);
+ }
+ pr_err_ratelimited("%s on smd_pkt_dev id:%d failed r:%d\n",
+ __func__, smd_pkt_devp->i, r);
+ kfree(buf);
+ return r;
+ }
+ bytes_written += r;
+ } while (bytes_written != count);
+ smd_write_end(smd_pkt_devp->ch);
+ mutex_unlock(&smd_pkt_devp->tx_lock);
+ D_WRITE("Finished %s on smd_pkt_dev id:%d %zu bytes\n",
+ __func__, smd_pkt_devp->i, count);
+
+ kfree(buf);
+ return count;
+}
+
+static unsigned int smd_pkt_poll(struct file *file, poll_table *wait)
+{
+ struct smd_pkt_dev *smd_pkt_devp;
+ unsigned int mask = 0;
+
+ smd_pkt_devp = file->private_data;
+ if (!smd_pkt_devp) {
+ pr_err_ratelimited("%s on a NULL device\n", __func__);
+ return POLLERR;
+ }
+
+ smd_pkt_devp->poll_mode = 1;
+ poll_wait(file, &smd_pkt_devp->ch_read_wait_queue, wait);
+ mutex_lock(&smd_pkt_devp->ch_lock);
+ if (smd_pkt_devp->has_reset || !smd_pkt_devp->ch) {
+ mutex_unlock(&smd_pkt_devp->ch_lock);
+ return POLLERR;
+ }
+
+ if (smd_read_avail(smd_pkt_devp->ch)) {
+ mask |= POLLIN | POLLRDNORM;
+ D_POLL("%s sets POLLIN for smd_pkt_dev id: %d\n",
+ __func__, smd_pkt_devp->i);
+ }
+ mutex_unlock(&smd_pkt_devp->ch_lock);
+
+ return mask;
+}
+
+static void check_and_wakeup_reader(struct smd_pkt_dev *smd_pkt_devp)
+{
+ int sz;
+ unsigned long flags;
+
+ if (!smd_pkt_devp) {
+ pr_err("%s on a NULL device\n", __func__);
+ return;
+ }
+
+ if (!smd_pkt_devp->ch) {
+ pr_err("%s on a closed smd_pkt_dev id:%d\n",
+ __func__, smd_pkt_devp->i);
+ return;
+ }
+
+ sz = smd_cur_packet_size(smd_pkt_devp->ch);
+ if (sz == 0) {
+ D_READ("%s: No packet in smd_pkt_dev id:%d\n",
+ __func__, smd_pkt_devp->i);
+ return;
+ }
+ if (!smd_read_avail(smd_pkt_devp->ch)) {
+ D_READ(
+ "%s: packet size is %d in smd_pkt_dev id:%d - but the data isn't here\n",
+ __func__, sz, smd_pkt_devp->i);
+ return;
+ }
+
+ /* here we have a packet of size sz ready */
+ spin_lock_irqsave(&smd_pkt_devp->pa_spinlock, flags);
+ __pm_stay_awake(&smd_pkt_devp->pa_ws);
+ smd_pkt_devp->ws_locked = 1;
+ spin_unlock_irqrestore(&smd_pkt_devp->pa_spinlock, flags);
+ wake_up(&smd_pkt_devp->ch_read_wait_queue);
+ schedule_work(&smd_pkt_devp->packet_arrival_work);
+ D_READ("%s: wake_up smd_pkt_dev id:%d\n", __func__, smd_pkt_devp->i);
+}
+
+static void check_and_wakeup_writer(struct smd_pkt_dev *smd_pkt_devp)
+{
+ int sz;
+
+ if (!smd_pkt_devp) {
+ pr_err("%s on a NULL device\n", __func__);
+ return;
+ }
+
+ if (!smd_pkt_devp->ch) {
+ pr_err("%s on a closed smd_pkt_dev id:%d\n",
+ __func__, smd_pkt_devp->i);
+ return;
+ }
+
+ sz = smd_write_segment_avail(smd_pkt_devp->ch);
+ if (sz) {
+ D_WRITE("%s: %d bytes write space in smd_pkt_dev id:%d\n",
+ __func__, sz, smd_pkt_devp->i);
+ smd_disable_read_intr(smd_pkt_devp->ch);
+ wake_up(&smd_pkt_devp->ch_write_wait_queue);
+ }
+}
+
+static void ch_notify(void *priv, unsigned event)
+{
+ struct smd_pkt_dev *smd_pkt_devp = priv;
+
+ if (smd_pkt_devp->ch == 0) {
+ if (event != SMD_EVENT_CLOSE)
+ pr_err("%s on a closed smd_pkt_dev id:%d\n",
+ __func__, smd_pkt_devp->i);
+ return;
+ }
+
+ switch (event) {
+ case SMD_EVENT_DATA: {
+ D_STATUS("%s: DATA event in smd_pkt_dev id:%d\n",
+ __func__, smd_pkt_devp->i);
+ check_and_wakeup_reader(smd_pkt_devp);
+ if (smd_pkt_devp->blocking_write)
+ check_and_wakeup_writer(smd_pkt_devp);
+ break;
+ }
+ case SMD_EVENT_OPEN:
+ D_STATUS("%s: OPEN event in smd_pkt_dev id:%d\n",
+ __func__, smd_pkt_devp->i);
+ smd_pkt_devp->has_reset = 0;
+ smd_pkt_devp->is_open = 1;
+ wake_up_interruptible(&smd_pkt_devp->ch_opened_wait_queue);
+ break;
+ case SMD_EVENT_CLOSE:
+ D_STATUS("%s: CLOSE event in smd_pkt_dev id:%d\n",
+ __func__, smd_pkt_devp->i);
+ smd_pkt_devp->is_open = 0;
+ /* put port into reset state */
+ clean_and_signal(smd_pkt_devp);
+ if (!strcmp(smd_pkt_devp->ch_name, "LOOPBACK"))
+ schedule_delayed_work(&loopback_work,
+ msecs_to_jiffies(1000));
+ break;
+ }
+}
+
+static int smd_pkt_dummy_probe(struct platform_device *pdev)
+{
+ struct smd_pkt_dev *smd_pkt_devp;
+
+ mutex_lock(&smd_pkt_dev_lock_lha1);
+ list_for_each_entry(smd_pkt_devp, &smd_pkt_dev_list, dev_list) {
+ if (smd_pkt_devp->edge == pdev->id
+ && !strcmp(pdev->name, smd_pkt_devp->ch_name)) {
+ complete_all(&smd_pkt_devp->ch_allocated);
+ D_STATUS("%s allocated SMD ch for smd_pkt_dev id:%d\n",
+ __func__, smd_pkt_devp->i);
+ break;
+ }
+ }
+ mutex_unlock(&smd_pkt_dev_lock_lha1);
+ return 0;
+}
+
+static uint32_t is_modem_smsm_inited(void)
+{
+ uint32_t modem_state;
+ uint32_t ready_state = (SMSM_INIT | SMSM_SMDINIT);
+
+ modem_state = smsm_get_state(SMSM_MODEM_STATE);
+ return (modem_state & ready_state) == ready_state;
+}
+
+/**
+ * smd_pkt_add_driver() - Add platform drivers for smd pkt device
+ *
+ * @smd_pkt_devp: pointer to the smd pkt device structure
+ *
+ * @returns: 0 for success, standard Linux error code otherwise
+ *
+ * This function is used to register platform driver once for all
+ * smd pkt devices which have same names and increment the reference
+ * count for 2nd to nth devices.
+ */
+static int smd_pkt_add_driver(struct smd_pkt_dev *smd_pkt_devp)
+{
+ int r = 0;
+ struct smd_pkt_driver *smd_pkt_driverp;
+ struct smd_pkt_driver *item;
+
+ if (!smd_pkt_devp) {
+ pr_err("%s on a NULL device\n", __func__);
+ return -EINVAL;
+ }
+ D_STATUS("Begin %s on smd_pkt_ch[%s]\n", __func__,
+ smd_pkt_devp->ch_name);
+
+ mutex_lock(&smd_pkt_driver_lock_lha1);
+ list_for_each_entry(item, &smd_pkt_driver_list, list) {
+ if (!strcmp(item->pdriver_name, smd_pkt_devp->ch_name)) {
+ D_STATUS("%s:%s Already Platform driver reg. cnt:%d\n",
+ __func__, smd_pkt_devp->ch_name, item->ref_cnt);
+ ++item->ref_cnt;
+ goto exit;
+ }
+ }
+
+ smd_pkt_driverp = kzalloc(sizeof(*smd_pkt_driverp), GFP_KERNEL);
+ if (IS_ERR_OR_NULL(smd_pkt_driverp)) {
+ pr_err("%s: kzalloc() failed for smd_pkt_driver[%s]\n",
+ __func__, smd_pkt_devp->ch_name);
+ r = -ENOMEM;
+ goto exit;
+ }
+
+ smd_pkt_driverp->driver.probe = smd_pkt_dummy_probe;
+ scnprintf(smd_pkt_driverp->pdriver_name, SMD_MAX_CH_NAME_LEN,
+ "%s", smd_pkt_devp->ch_name);
+ smd_pkt_driverp->driver.driver.name = smd_pkt_driverp->pdriver_name;
+ smd_pkt_driverp->driver.driver.owner = THIS_MODULE;
+ r = platform_driver_register(&smd_pkt_driverp->driver);
+ if (r) {
+ pr_err("%s: %s Platform driver reg. failed\n",
+ __func__, smd_pkt_devp->ch_name);
+ kfree(smd_pkt_driverp);
+ goto exit;
+ }
+ ++smd_pkt_driverp->ref_cnt;
+ list_add(&smd_pkt_driverp->list, &smd_pkt_driver_list);
+
+exit:
+ D_STATUS("End %s on smd_pkt_ch[%s]\n", __func__, smd_pkt_devp->ch_name);
+ mutex_unlock(&smd_pkt_driver_lock_lha1);
+ return r;
+}
+
+/**
+ * smd_pkt_remove_driver() - Remove the platform drivers for smd pkt device
+ *
+ * @smd_pkt_devp: pointer to the smd pkt device structure
+ *
+ * This function is used to decrement the reference count on
+ * platform drivers for smd pkt devices and removes the drivers
+ * when the reference count becomes zero.
+ */
+static void smd_pkt_remove_driver(struct smd_pkt_dev *smd_pkt_devp)
+{
+ struct smd_pkt_driver *smd_pkt_driverp;
+ bool found_item = false;
+
+ if (!smd_pkt_devp) {
+ pr_err("%s on a NULL device\n", __func__);
+ return;
+ }
+
+ D_STATUS("Begin %s on smd_pkt_ch[%s]\n", __func__,
+ smd_pkt_devp->ch_name);
+ mutex_lock(&smd_pkt_driver_lock_lha1);
+ list_for_each_entry(smd_pkt_driverp, &smd_pkt_driver_list, list) {
+ if (!strcmp(smd_pkt_driverp->pdriver_name,
+ smd_pkt_devp->ch_name)) {
+ found_item = true;
+ D_STATUS("%s:%s Platform driver cnt:%d\n",
+ __func__, smd_pkt_devp->ch_name,
+ smd_pkt_driverp->ref_cnt);
+ if (smd_pkt_driverp->ref_cnt > 0)
+ --smd_pkt_driverp->ref_cnt;
+ else
+ pr_warn("%s reference count <= 0\n", __func__);
+ break;
+ }
+ }
+ if (!found_item)
+ pr_err("%s:%s No item found in list.\n",
+ __func__, smd_pkt_devp->ch_name);
+
+ if (found_item && smd_pkt_driverp->ref_cnt == 0) {
+ platform_driver_unregister(&smd_pkt_driverp->driver);
+ smd_pkt_driverp->driver.probe = NULL;
+ list_del(&smd_pkt_driverp->list);
+ kfree(smd_pkt_driverp);
+ }
+ mutex_unlock(&smd_pkt_driver_lock_lha1);
+ D_STATUS("End %s on smd_pkt_ch[%s]\n", __func__, smd_pkt_devp->ch_name);
+}
+
+int smd_pkt_open(struct inode *inode, struct file *file)
+{
+ int r = 0;
+ struct smd_pkt_dev *smd_pkt_devp;
+ const char *peripheral = NULL;
+
+ smd_pkt_devp = container_of(inode->i_cdev, struct smd_pkt_dev, cdev);
+
+ if (!smd_pkt_devp) {
+ pr_err_ratelimited("%s on a NULL device\n", __func__);
+ return -EINVAL;
+ }
+ D_STATUS("Begin %s on smd_pkt_dev id:%d\n", __func__, smd_pkt_devp->i);
+
+ file->private_data = smd_pkt_devp;
+
+ mutex_lock(&smd_pkt_devp->ch_lock);
+ if (smd_pkt_devp->ch == 0) {
+ unsigned open_wait_rem = smd_pkt_devp->open_modem_wait * 1000;
+
+ reinit_completion(&smd_pkt_devp->ch_allocated);
+
+ r = smd_pkt_add_driver(smd_pkt_devp);
+ if (r) {
+ pr_err_ratelimited("%s: %s Platform driver reg. failed\n",
+ __func__, smd_pkt_devp->ch_name);
+ goto out;
+ }
+
+ peripheral = smd_edge_to_pil_str(smd_pkt_devp->edge);
+ if (!IS_ERR_OR_NULL(peripheral)) {
+ smd_pkt_devp->pil = subsystem_get(peripheral);
+ if (IS_ERR(smd_pkt_devp->pil)) {
+ r = PTR_ERR(smd_pkt_devp->pil);
+ pr_err_ratelimited("%s failed on smd_pkt_dev id:%d - subsystem_get failed for %s\n",
+ __func__, smd_pkt_devp->i, peripheral);
+ /*
+ * Sleep inorder to reduce the frequency of
+ * retry by user-space modules and to avoid
+ * possible watchdog bite.
+ */
+ msleep(open_wait_rem);
+ goto release_pd;
+ }
+ }
+
+ /* Wait for the modem SMSM to be inited for the SMD
+ ** Loopback channel to be allocated at the modem. Since
+ ** the wait need to be done atmost once, using msleep
+ ** doesn't degrade the performance.
+ */
+ if (!strcmp(smd_pkt_devp->ch_name, "LOOPBACK")) {
+ if (!is_modem_smsm_inited())
+ msleep(5000);
+ smsm_change_state(SMSM_APPS_STATE,
+ 0, SMSM_SMD_LOOPBACK);
+ msleep(100);
+ }
+
+ /*
+ * Wait for a packet channel to be allocated so we know
+ * the modem is ready enough.
+ */
+ if (open_wait_rem) {
+ r = wait_for_completion_interruptible_timeout(
+ &smd_pkt_devp->ch_allocated,
+ msecs_to_jiffies(open_wait_rem));
+ if (r >= 0)
+ open_wait_rem = jiffies_to_msecs(r);
+ if (r == 0)
+ r = -ETIMEDOUT;
+ if (r == -ERESTARTSYS) {
+ pr_info_ratelimited("%s: wait on smd_pkt_dev id:%d allocation interrupted\n",
+ __func__, smd_pkt_devp->i);
+ goto release_pil;
+ }
+ if (r < 0) {
+ pr_err_ratelimited("%s: wait on smd_pkt_dev id:%d allocation failed rc:%d\n",
+ __func__, smd_pkt_devp->i, r);
+ goto release_pil;
+ }
+ }
+
+ r = smd_named_open_on_edge(smd_pkt_devp->ch_name,
+ smd_pkt_devp->edge,
+ &smd_pkt_devp->ch,
+ smd_pkt_devp,
+ ch_notify);
+ if (r < 0) {
+ pr_err_ratelimited("%s: %s open failed %d\n", __func__,
+ smd_pkt_devp->ch_name, r);
+ goto release_pil;
+ }
+
+ open_wait_rem = max_t(unsigned, 2000, open_wait_rem);
+ r = wait_event_interruptible_timeout(
+ smd_pkt_devp->ch_opened_wait_queue,
+ smd_pkt_devp->is_open,
+ msecs_to_jiffies(open_wait_rem));
+ if (r == 0)
+ r = -ETIMEDOUT;
+
+ if (r < 0) {
+ /* close the ch to sync smd's state with smd_pkt */
+ smd_close(smd_pkt_devp->ch);
+ smd_pkt_devp->ch = NULL;
+ }
+
+ if (r == -ERESTARTSYS) {
+ pr_info_ratelimited("%s: wait on smd_pkt_dev id:%d OPEN interrupted\n",
+ __func__, smd_pkt_devp->i);
+ } else if (r < 0) {
+ pr_err_ratelimited("%s: wait on smd_pkt_dev id:%d OPEN event failed rc:%d\n",
+ __func__, smd_pkt_devp->i, r);
+ } else if (!smd_pkt_devp->is_open) {
+ pr_err_ratelimited("%s: Invalid OPEN event on smd_pkt_dev id:%d\n",
+ __func__, smd_pkt_devp->i);
+ r = -ENODEV;
+ } else {
+ smd_disable_read_intr(smd_pkt_devp->ch);
+ smd_pkt_devp->ch_size =
+ smd_write_avail(smd_pkt_devp->ch);
+ r = 0;
+ smd_pkt_devp->ref_cnt++;
+ D_STATUS("Finished %s on smd_pkt_dev id:%d\n",
+ __func__, smd_pkt_devp->i);
+ }
+ } else {
+ smd_pkt_devp->ref_cnt++;
+ }
+release_pil:
+ if (peripheral && (r < 0)) {
+ subsystem_put(smd_pkt_devp->pil);
+ smd_pkt_devp->pil = NULL;
+ }
+
+release_pd:
+ if (r < 0)
+ smd_pkt_remove_driver(smd_pkt_devp);
+out:
+ mutex_unlock(&smd_pkt_devp->ch_lock);
+
+
+ return r;
+}
+
+int smd_pkt_release(struct inode *inode, struct file *file)
+{
+ int r = 0;
+ struct smd_pkt_dev *smd_pkt_devp = file->private_data;
+ unsigned long flags;
+
+ if (!smd_pkt_devp) {
+ pr_err_ratelimited("%s on a NULL device\n", __func__);
+ return -EINVAL;
+ }
+ D_STATUS("Begin %s on smd_pkt_dev id:%d\n",
+ __func__, smd_pkt_devp->i);
+
+ mutex_lock(&smd_pkt_devp->ch_lock);
+ mutex_lock(&smd_pkt_devp->rx_lock);
+ mutex_lock(&smd_pkt_devp->tx_lock);
+ if (smd_pkt_devp->ref_cnt > 0)
+ smd_pkt_devp->ref_cnt--;
+
+ if (smd_pkt_devp->ch != 0 && smd_pkt_devp->ref_cnt == 0) {
+ clean_and_signal(smd_pkt_devp);
+ r = smd_close(smd_pkt_devp->ch);
+ smd_pkt_devp->ch = 0;
+ smd_pkt_devp->blocking_write = 0;
+ smd_pkt_devp->poll_mode = 0;
+ smd_pkt_remove_driver(smd_pkt_devp);
+ if (smd_pkt_devp->pil)
+ subsystem_put(smd_pkt_devp->pil);
+ smd_pkt_devp->has_reset = 0;
+ smd_pkt_devp->do_reset_notification = 0;
+ spin_lock_irqsave(&smd_pkt_devp->pa_spinlock, flags);
+ if (smd_pkt_devp->ws_locked) {
+ __pm_relax(&smd_pkt_devp->pa_ws);
+ smd_pkt_devp->ws_locked = 0;
+ }
+ spin_unlock_irqrestore(&smd_pkt_devp->pa_spinlock, flags);
+ }
+ mutex_unlock(&smd_pkt_devp->tx_lock);
+ mutex_unlock(&smd_pkt_devp->rx_lock);
+ mutex_unlock(&smd_pkt_devp->ch_lock);
+
+ if (flush_work(&smd_pkt_devp->packet_arrival_work))
+ D_STATUS("%s: Flushed work for smd_pkt_dev id:%d\n", __func__,
+ smd_pkt_devp->i);
+
+ D_STATUS("Finished %s on smd_pkt_dev id:%d\n",
+ __func__, smd_pkt_devp->i);
+
+ return r;
+}
+
+static const struct file_operations smd_pkt_fops = {
+ .owner = THIS_MODULE,
+ .open = smd_pkt_open,
+ .release = smd_pkt_release,
+ .read = smd_pkt_read,
+ .write = smd_pkt_write,
+ .poll = smd_pkt_poll,
+ .unlocked_ioctl = smd_pkt_ioctl,
+ .compat_ioctl = smd_pkt_ioctl,
+};
+
+static int smd_pkt_init_add_device(struct smd_pkt_dev *smd_pkt_devp, int i)
+{
+ int r = 0;
+
+ smd_pkt_devp->i = i;
+
+ init_waitqueue_head(&smd_pkt_devp->ch_read_wait_queue);
+ init_waitqueue_head(&smd_pkt_devp->ch_write_wait_queue);
+ smd_pkt_devp->is_open = 0;
+ smd_pkt_devp->poll_mode = 0;
+ smd_pkt_devp->ws_locked = 0;
+ init_waitqueue_head(&smd_pkt_devp->ch_opened_wait_queue);
+
+ spin_lock_init(&smd_pkt_devp->pa_spinlock);
+ mutex_init(&smd_pkt_devp->ch_lock);
+ mutex_init(&smd_pkt_devp->rx_lock);
+ mutex_init(&smd_pkt_devp->tx_lock);
+ wakeup_source_init(&smd_pkt_devp->pa_ws, smd_pkt_devp->dev_name);
+ INIT_WORK(&smd_pkt_devp->packet_arrival_work, packet_arrival_worker);
+ init_completion(&smd_pkt_devp->ch_allocated);
+
+ cdev_init(&smd_pkt_devp->cdev, &smd_pkt_fops);
+ smd_pkt_devp->cdev.owner = THIS_MODULE;
+
+ r = cdev_add(&smd_pkt_devp->cdev, (smd_pkt_number + i), 1);
+ if (IS_ERR_VALUE(r)) {
+ pr_err("%s: cdev_add() failed for smd_pkt_dev id:%d ret:%i\n",
+ __func__, i, r);
+ return r;
+ }
+
+ smd_pkt_devp->devicep =
+ device_create(smd_pkt_classp,
+ NULL,
+ (smd_pkt_number + i),
+ NULL,
+ smd_pkt_devp->dev_name);
+
+ if (IS_ERR_OR_NULL(smd_pkt_devp->devicep)) {
+ pr_err("%s: device_create() failed for smd_pkt_dev id:%d\n",
+ __func__, i);
+ r = -ENOMEM;
+ cdev_del(&smd_pkt_devp->cdev);
+ wakeup_source_trash(&smd_pkt_devp->pa_ws);
+ return r;
+ }
+ if (device_create_file(smd_pkt_devp->devicep,
+ &dev_attr_open_timeout))
+ pr_err("%s: unable to create device attr for smd_pkt_dev id:%d\n",
+ __func__, i);
+
+ if (!strcmp(smd_pkt_devp->ch_name, "LOOPBACK")) {
+ if (device_create_file(smd_pkt_devp->devicep,
+ &dev_attr_loopback_edge))
+ pr_err("%s: unable to create device attr for smd_pkt_dev id:%d\n",
+ __func__, i);
+ }
+ mutex_lock(&smd_pkt_dev_lock_lha1);
+ list_add(&smd_pkt_devp->dev_list, &smd_pkt_dev_list);
+ mutex_unlock(&smd_pkt_dev_lock_lha1);
+
+ return r;
+}
+
+static void smd_pkt_core_deinit(void)
+{
+ struct smd_pkt_dev *smd_pkt_devp;
+ struct smd_pkt_dev *index;
+
+ mutex_lock(&smd_pkt_dev_lock_lha1);
+ list_for_each_entry_safe(smd_pkt_devp, index, &smd_pkt_dev_list,
+ dev_list) {
+ cdev_del(&smd_pkt_devp->cdev);
+ list_del(&smd_pkt_devp->dev_list);
+ device_destroy(smd_pkt_classp,
+ MKDEV(MAJOR(smd_pkt_number), smd_pkt_devp->i));
+ kfree(smd_pkt_devp);
+ }
+ mutex_unlock(&smd_pkt_dev_lock_lha1);
+
+ if (!IS_ERR_OR_NULL(smd_pkt_classp))
+ class_destroy(smd_pkt_classp);
+
+ unregister_chrdev_region(MAJOR(smd_pkt_number), num_smd_pkt_ports);
+}
+
+static int smd_pkt_alloc_chrdev_region(void)
+{
+ int r = alloc_chrdev_region(&smd_pkt_number,
+ 0,
+ num_smd_pkt_ports,
+ DEVICE_NAME);
+
+ if (IS_ERR_VALUE(r)) {
+ pr_err("%s: alloc_chrdev_region() failed ret:%i\n",
+ __func__, r);
+ return r;
+ }
+
+ smd_pkt_classp = class_create(THIS_MODULE, DEVICE_NAME);
+ if (IS_ERR(smd_pkt_classp)) {
+ pr_err("%s: class_create() failed ENOMEM\n", __func__);
+ r = -ENOMEM;
+ unregister_chrdev_region(MAJOR(smd_pkt_number),
+ num_smd_pkt_ports);
+ return r;
+ }
+
+ return 0;
+}
+
+static int parse_smdpkt_devicetree(struct device_node *node,
+ struct smd_pkt_dev *smd_pkt_devp)
+{
+ int edge;
+ char *key;
+ const char *ch_name;
+ const char *dev_name;
+ const char *remote_ss;
+
+ key = "qcom,smdpkt-remote";
+ remote_ss = of_get_property(node, key, NULL);
+ if (!remote_ss)
+ goto error;
+
+ edge = smd_remote_ss_to_edge(remote_ss);
+ if (edge < 0)
+ goto error;
+
+ smd_pkt_devp->edge = edge;
+ D_STATUS("%s: %s = %d", __func__, key, edge);
+
+ key = "qcom,smdpkt-port-name";
+ ch_name = of_get_property(node, key, NULL);
+ if (!ch_name)
+ goto error;
+
+ strlcpy(smd_pkt_devp->ch_name, ch_name, SMD_MAX_CH_NAME_LEN);
+ D_STATUS("%s ch_name = %s\n", __func__, ch_name);
+
+ key = "qcom,smdpkt-dev-name";
+ dev_name = of_get_property(node, key, NULL);
+ if (!dev_name)
+ goto error;
+
+ strlcpy(smd_pkt_devp->dev_name, dev_name, SMD_MAX_CH_NAME_LEN);
+ D_STATUS("%s dev_name = %s\n", __func__, dev_name);
+
+ return 0;
+
+error:
+ pr_err("%s: missing key: %s\n", __func__, key);
+ return -ENODEV;
+
+}
+
+static int smd_pkt_devicetree_init(struct platform_device *pdev)
+{
+ int ret;
+ int i = 0;
+ struct device_node *node;
+ struct smd_pkt_dev *smd_pkt_devp;
+ int subnode_num = 0;
+
+ for_each_child_of_node(pdev->dev.of_node, node)
+ ++subnode_num;
+
+ num_smd_pkt_ports = subnode_num;
+
+ ret = smd_pkt_alloc_chrdev_region();
+ if (ret) {
+ pr_err("%s: smd_pkt_alloc_chrdev_region() failed ret:%i\n",
+ __func__, ret);
+ return ret;
+ }
+
+ for_each_child_of_node(pdev->dev.of_node, node) {
+ smd_pkt_devp = kzalloc(sizeof(struct smd_pkt_dev), GFP_KERNEL);
+ if (IS_ERR_OR_NULL(smd_pkt_devp)) {
+ pr_err("%s: kzalloc() failed for smd_pkt_dev id:%d\n",
+ __func__, i);
+ ret = -ENOMEM;
+ goto error_destroy;
+ }
+
+ ret = parse_smdpkt_devicetree(node, smd_pkt_devp);
+ if (ret) {
+ pr_err(" failed to parse_smdpkt_devicetree %d\n", i);
+ kfree(smd_pkt_devp);
+ goto error_destroy;
+ }
+
+ ret = smd_pkt_init_add_device(smd_pkt_devp, i);
+ if (ret < 0) {
+ pr_err("add device failed for idx:%d ret=%d\n", i, ret);
+ kfree(smd_pkt_devp);
+ goto error_destroy;
+ }
+ i++;
+ }
+
+ INIT_DELAYED_WORK(&loopback_work, loopback_probe_worker);
+
+ D_STATUS("SMD Packet Port Driver Initialized.\n");
+ return 0;
+
+error_destroy:
+ smd_pkt_core_deinit();
+ return ret;
+}
+
+static int msm_smd_pkt_probe(struct platform_device *pdev)
+{
+ int ret;
+
+ if (pdev) {
+ if (pdev->dev.of_node) {
+ D_STATUS("%s device tree implementation\n", __func__);
+ ret = smd_pkt_devicetree_init(pdev);
+ if (ret)
+ pr_err("%s: device tree init failed\n",
+ __func__);
+ }
+ }
+
+ return 0;
+}
+
+static const struct of_device_id msm_smd_pkt_match_table[] = {
+ { .compatible = "qcom,smdpkt" },
+ {},
+};
+
+static struct platform_driver msm_smd_pkt_driver = {
+ .probe = msm_smd_pkt_probe,
+ .driver = {
+ .name = MODULE_NAME,
+ .owner = THIS_MODULE,
+ .of_match_table = msm_smd_pkt_match_table,
+ },
+};
+
+static int __init smd_pkt_init(void)
+{
+ int rc;
+
+ INIT_LIST_HEAD(&smd_pkt_dev_list);
+ INIT_LIST_HEAD(&smd_pkt_driver_list);
+ rc = platform_driver_register(&msm_smd_pkt_driver);
+ if (rc) {
+ pr_err("%s: msm_smd_driver register failed %d\n",
+ __func__, rc);
+ return rc;
+ }
+
+ smd_pkt_ilctxt = ipc_log_context_create(SMD_PKT_IPC_LOG_PAGE_CNT,
+ "smd_pkt", 0);
+ return 0;
+}
+
+static void __exit smd_pkt_cleanup(void)
+{
+ smd_pkt_core_deinit();
+}
+
+module_init(smd_pkt_init);
+module_exit(smd_pkt_cleanup);
+
+MODULE_DESCRIPTION("MSM Shared Memory Packet Port");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/clk/msm/clock-local2.c b/drivers/clk/msm/clock-local2.c
index 19956f030ae9..adb07cdb7e8d 100644
--- a/drivers/clk/msm/clock-local2.c
+++ b/drivers/clk/msm/clock-local2.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012-2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2012-2017, 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
@@ -928,7 +928,8 @@ static unsigned long branch_clk_get_rate(struct clk *c)
{
struct branch_clk *branch = to_branch_clk(c);
- if (branch->max_div)
+ if (branch->max_div ||
+ (branch->aggr_sibling_rates && !branch->is_prepared))
return branch->c.rate;
return clk_get_rate(c->parent);
diff --git a/drivers/clk/msm/clock-mmss-8998.c b/drivers/clk/msm/clock-mmss-8998.c
index 6ebb3ed6ed91..fdaaa723accd 100644
--- a/drivers/clk/msm/clock-mmss-8998.c
+++ b/drivers/clk/msm/clock-mmss-8998.c
@@ -359,6 +359,7 @@ static struct rcg_clk mdp_clk_src = {
.set_rate = set_rate_hid,
.freq_tbl = ftbl_mdp_clk_src,
.current_freq = &rcg_dummy_freq,
+ .non_local_children = true,
.base = &virt_base,
.c = {
.dbg_name = "mdp_clk_src",
diff --git a/drivers/clk/qcom/clk-cpu-osm.c b/drivers/clk/qcom/clk-cpu-osm.c
index f82ddc3b008b..d3914ab5f47c 100644
--- a/drivers/clk/qcom/clk-cpu-osm.c
+++ b/drivers/clk/qcom/clk-cpu-osm.c
@@ -772,7 +772,7 @@ static const char * const gcc_parent_names_1[] = {
};
static struct freq_tbl ftbl_osm_clk_src[] = {
- F(200000000, LMH_LITE_CLK_SRC, 3, 0, 0),
+ F(200000000, LMH_LITE_CLK_SRC, 1.5, 0, 0),
{ }
};
diff --git a/drivers/clk/qcom/gcc-sdm660.c b/drivers/clk/qcom/gcc-sdm660.c
index b55310e091af..b10f9ca9fe1a 100644
--- a/drivers/clk/qcom/gcc-sdm660.c
+++ b/drivers/clk/qcom/gcc-sdm660.c
@@ -732,6 +732,7 @@ static struct clk_rcg2 gp3_clk_src = {
};
static const struct freq_tbl ftbl_hmss_gpll0_clk_src[] = {
+ F(300000000, P_GPLL0_OUT_MAIN, 2, 0, 0),
F(600000000, P_GPLL0_OUT_MAIN, 1, 0, 0),
{ }
};
@@ -2755,6 +2756,9 @@ static int gcc_660_probe(struct platform_device *pdev)
/* Keep bimc gfx clock port on all the time */
clk_prepare_enable(gcc_bimc_gfx_clk.clkr.hw.clk);
+ /* Set the HMSS_GPLL0_SRC for 300MHz to CPU subsystem */
+ clk_set_rate(hmss_gpll0_clk_src.clkr.hw.clk, 300000000);
+
dev_info(&pdev->dev, "Registered GCC clocks\n");
return ret;
diff --git a/drivers/cpuidle/lpm-levels.c b/drivers/cpuidle/lpm-levels.c
index 37535a72e066..4224b594f1b8 100644
--- a/drivers/cpuidle/lpm-levels.c
+++ b/drivers/cpuidle/lpm-levels.c
@@ -43,6 +43,7 @@
#include <soc/qcom/event_timer.h>
#include <soc/qcom/lpm-stats.h>
#include <soc/qcom/jtag.h>
+#include <soc/qcom/minidump.h>
#include <asm/cputype.h>
#include <asm/arch_timer.h>
#include <asm/cacheflush.h>
@@ -690,7 +691,7 @@ static int cpu_power_select(struct cpuidle_device *dev,
if (!cpu)
return -EINVAL;
- if (sleep_disabled)
+ if (sleep_disabled && !cpu_isolated(dev->cpu))
return 0;
idx_restrict = cpu->nlevels + 1;
@@ -1854,6 +1855,7 @@ static int lpm_probe(struct platform_device *pdev)
int ret;
int size;
struct kobject *module_kobj = NULL;
+ struct md_region md_entry;
get_online_cpus();
lpm_root_node = lpm_of_parse_cluster(pdev);
@@ -1914,6 +1916,14 @@ static int lpm_probe(struct platform_device *pdev)
goto failed;
}
+ /* Add lpm_debug to Minidump*/
+ strlcpy(md_entry.name, "KLPMDEBUG", sizeof(md_entry.name));
+ md_entry.virt_addr = (uintptr_t)lpm_debug;
+ md_entry.phys_addr = lpm_debug_phys;
+ md_entry.size = size;
+ if (msm_minidump_add_region(&md_entry))
+ pr_info("Failed to add lpm_debug in Minidump\n");
+
return 0;
failed:
free_cluster_node(lpm_root_node);
diff --git a/drivers/crypto/msm/qcedev.c b/drivers/crypto/msm/qcedev.c
index 7459401979fe..d04ca6f28f90 100644
--- a/drivers/crypto/msm/qcedev.c
+++ b/drivers/crypto/msm/qcedev.c
@@ -56,6 +56,7 @@ static uint8_t _std_init_vector_sha256_uint8[] = {
static DEFINE_MUTEX(send_cmd_lock);
static DEFINE_MUTEX(qcedev_sent_bw_req);
+static DEFINE_MUTEX(hash_access_lock);
static void qcedev_ce_high_bw_req(struct qcedev_control *podev,
bool high_bw_req)
@@ -1648,12 +1649,18 @@ long qcedev_ioctl(struct file *file, unsigned cmd, unsigned long arg)
(void __user *)arg,
sizeof(struct qcedev_sha_op_req)))
return -EFAULT;
- if (qcedev_check_sha_params(&qcedev_areq.sha_op_req, podev))
+ mutex_lock(&hash_access_lock);
+ if (qcedev_check_sha_params(&qcedev_areq.sha_op_req, podev)) {
+ mutex_unlock(&hash_access_lock);
return -EINVAL;
+ }
qcedev_areq.op_type = QCEDEV_CRYPTO_OPER_SHA;
err = qcedev_hash_init(&qcedev_areq, handle, &sg_src);
- if (err)
+ if (err) {
+ mutex_unlock(&hash_access_lock);
return err;
+ }
+ mutex_unlock(&hash_access_lock);
if (copy_to_user((void __user *)arg, &qcedev_areq.sha_op_req,
sizeof(struct qcedev_sha_op_req)))
return -EFAULT;
@@ -1671,32 +1678,42 @@ long qcedev_ioctl(struct file *file, unsigned cmd, unsigned long arg)
(void __user *)arg,
sizeof(struct qcedev_sha_op_req)))
return -EFAULT;
- if (qcedev_check_sha_params(&qcedev_areq.sha_op_req, podev))
+ mutex_lock(&hash_access_lock);
+ if (qcedev_check_sha_params(&qcedev_areq.sha_op_req, podev)) {
+ mutex_unlock(&hash_access_lock);
return -EINVAL;
+ }
qcedev_areq.op_type = QCEDEV_CRYPTO_OPER_SHA;
if (qcedev_areq.sha_op_req.alg == QCEDEV_ALG_AES_CMAC) {
err = qcedev_hash_cmac(&qcedev_areq, handle, &sg_src);
- if (err)
+ if (err) {
+ mutex_unlock(&hash_access_lock);
return err;
+ }
} else {
if (handle->sha_ctxt.init_done == false) {
pr_err("%s Init was not called\n", __func__);
+ mutex_unlock(&hash_access_lock);
return -EINVAL;
}
err = qcedev_hash_update(&qcedev_areq, handle, &sg_src);
- if (err)
+ if (err) {
+ mutex_unlock(&hash_access_lock);
return err;
+ }
}
if (handle->sha_ctxt.diglen > QCEDEV_MAX_SHA_DIGEST) {
pr_err("Invalid sha_ctxt.diglen %d\n",
handle->sha_ctxt.diglen);
+ mutex_unlock(&hash_access_lock);
return -EINVAL;
}
memcpy(&qcedev_areq.sha_op_req.digest[0],
&handle->sha_ctxt.digest[0],
handle->sha_ctxt.diglen);
+ mutex_unlock(&hash_access_lock);
if (copy_to_user((void __user *)arg, &qcedev_areq.sha_op_req,
sizeof(struct qcedev_sha_op_req)))
return -EFAULT;
@@ -1713,16 +1730,22 @@ long qcedev_ioctl(struct file *file, unsigned cmd, unsigned long arg)
(void __user *)arg,
sizeof(struct qcedev_sha_op_req)))
return -EFAULT;
- if (qcedev_check_sha_params(&qcedev_areq.sha_op_req, podev))
+ mutex_lock(&hash_access_lock);
+ if (qcedev_check_sha_params(&qcedev_areq.sha_op_req, podev)) {
+ mutex_unlock(&hash_access_lock);
return -EINVAL;
+ }
qcedev_areq.op_type = QCEDEV_CRYPTO_OPER_SHA;
err = qcedev_hash_final(&qcedev_areq, handle);
- if (err)
+ if (err) {
+ mutex_unlock(&hash_access_lock);
return err;
+ }
qcedev_areq.sha_op_req.diglen = handle->sha_ctxt.diglen;
memcpy(&qcedev_areq.sha_op_req.digest[0],
&handle->sha_ctxt.digest[0],
handle->sha_ctxt.diglen);
+ mutex_unlock(&hash_access_lock);
if (copy_to_user((void __user *)arg, &qcedev_areq.sha_op_req,
sizeof(struct qcedev_sha_op_req)))
return -EFAULT;
@@ -1737,20 +1760,28 @@ long qcedev_ioctl(struct file *file, unsigned cmd, unsigned long arg)
(void __user *)arg,
sizeof(struct qcedev_sha_op_req)))
return -EFAULT;
- if (qcedev_check_sha_params(&qcedev_areq.sha_op_req, podev))
+ mutex_lock(&hash_access_lock);
+ if (qcedev_check_sha_params(&qcedev_areq.sha_op_req, podev)) {
+ mutex_unlock(&hash_access_lock);
return -EINVAL;
+ }
qcedev_areq.op_type = QCEDEV_CRYPTO_OPER_SHA;
qcedev_hash_init(&qcedev_areq, handle, &sg_src);
err = qcedev_hash_update(&qcedev_areq, handle, &sg_src);
- if (err)
+ if (err) {
+ mutex_unlock(&hash_access_lock);
return err;
+ }
err = qcedev_hash_final(&qcedev_areq, handle);
- if (err)
+ if (err) {
+ mutex_unlock(&hash_access_lock);
return err;
+ }
qcedev_areq.sha_op_req.diglen = handle->sha_ctxt.diglen;
memcpy(&qcedev_areq.sha_op_req.digest[0],
&handle->sha_ctxt.digest[0],
handle->sha_ctxt.diglen);
+ mutex_unlock(&hash_access_lock);
if (copy_to_user((void __user *)arg, &qcedev_areq.sha_op_req,
sizeof(struct qcedev_sha_op_req)))
return -EFAULT;
diff --git a/drivers/hwtracing/coresight/coresight-etm4x.c b/drivers/hwtracing/coresight/coresight-etm4x.c
index 287d839f98d0..9f9dd574c8d0 100644
--- a/drivers/hwtracing/coresight/coresight-etm4x.c
+++ b/drivers/hwtracing/coresight/coresight-etm4x.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2014, 2016 The Linux Foundation. All rights reserved.
+/* Copyright (c) 2014, 2016-2017 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
@@ -2557,6 +2557,8 @@ static int etm4_set_reg_dump(struct etmv4_drvdata *drvdata)
drvdata->reg_data.addr = virt_to_phys(baddr);
drvdata->reg_data.len = size;
+ scnprintf(drvdata->reg_data.name, sizeof(drvdata->reg_data.name),
+ "KETM_REG%d", drvdata->cpu);
dump_entry.id = MSM_DUMP_DATA_ETM_REG + drvdata->cpu;
dump_entry.addr = virt_to_phys(&drvdata->reg_data);
diff --git a/drivers/hwtracing/coresight/coresight-tmc.c b/drivers/hwtracing/coresight/coresight-tmc.c
index 34b12e015768..c5998bd5ce02 100644
--- a/drivers/hwtracing/coresight/coresight-tmc.c
+++ b/drivers/hwtracing/coresight/coresight-tmc.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012, 2016 The Linux Foundation. All rights reserved.
+/* Copyright (c) 2012, 2016-2017 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
@@ -1726,6 +1726,9 @@ static int tmc_etf_set_buf_dump(struct tmc_drvdata *drvdata)
drvdata->buf_data.addr = virt_to_phys(drvdata->buf);
drvdata->buf_data.len = drvdata->size;
+ scnprintf(drvdata->buf_data.name, sizeof(drvdata->buf_data.name),
+ "KTMC_ETF%d", count);
+
dump_entry.id = MSM_DUMP_DATA_TMC_ETF + count;
dump_entry.addr = virt_to_phys(&drvdata->buf_data);
@@ -1817,6 +1820,8 @@ static int tmc_set_reg_dump(struct tmc_drvdata *drvdata)
drvdata->reg_data.addr = virt_to_phys(drvdata->reg_buf);
drvdata->reg_data.len = size;
+ scnprintf(drvdata->reg_data.name, sizeof(drvdata->reg_data.name),
+ "KTMC_REG%d", count);
dump_entry.id = MSM_DUMP_DATA_TMC_REG + count;
dump_entry.addr = virt_to_phys(&drvdata->reg_data);
diff --git a/drivers/iommu/arm-smmu.c b/drivers/iommu/arm-smmu.c
index 51159711b1d8..25fe6c85a34e 100644
--- a/drivers/iommu/arm-smmu.c
+++ b/drivers/iommu/arm-smmu.c
@@ -3288,6 +3288,43 @@ static int arm_smmu_domain_set_attr(struct iommu_domain *domain,
1 << DOMAIN_ATTR_ENABLE_TTBR1;
ret = 0;
break;
+ case DOMAIN_ATTR_GEOMETRY: {
+ struct iommu_domain_geometry *geometry =
+ (struct iommu_domain_geometry *)data;
+
+ if (smmu_domain->smmu != NULL) {
+ dev_err(smmu_domain->smmu->dev,
+ "cannot set geometry attribute while attached\n");
+ ret = -EBUSY;
+ break;
+ }
+
+ if (geometry->aperture_start >= SZ_1G * 4ULL ||
+ geometry->aperture_end >= SZ_1G * 4ULL) {
+ pr_err("fastmap does not support IOVAs >= 4GB\n");
+ ret = -EINVAL;
+ break;
+ }
+ if (smmu_domain->attributes
+ & (1 << DOMAIN_ATTR_GEOMETRY)) {
+ if (geometry->aperture_start
+ < domain->geometry.aperture_start)
+ domain->geometry.aperture_start =
+ geometry->aperture_start;
+
+ if (geometry->aperture_end
+ > domain->geometry.aperture_end)
+ domain->geometry.aperture_end =
+ geometry->aperture_end;
+ } else {
+ smmu_domain->attributes |= 1 << DOMAIN_ATTR_GEOMETRY;
+ domain->geometry.aperture_start =
+ geometry->aperture_start;
+ domain->geometry.aperture_end = geometry->aperture_end;
+ }
+ ret = 0;
+ break;
+ }
default:
ret = -ENODEV;
break;
diff --git a/drivers/iommu/dma-mapping-fast.c b/drivers/iommu/dma-mapping-fast.c
index 8c6364f03eac..0881d68f34d8 100644
--- a/drivers/iommu/dma-mapping-fast.c
+++ b/drivers/iommu/dma-mapping-fast.c
@@ -188,7 +188,9 @@ static dma_addr_t __fast_smmu_alloc_iova(struct dma_fast_smmu_mapping *mapping,
iommu_tlbiall(mapping->domain);
mapping->have_stale_tlbs = false;
- av8l_fast_clear_stale_ptes(mapping->pgtbl_pmds, mapping->base,
+ av8l_fast_clear_stale_ptes(mapping->pgtbl_pmds,
+ mapping->domain->geometry.aperture_start,
+ mapping->base,
mapping->base + mapping->size - 1,
skip_sync);
}
@@ -367,7 +369,8 @@ static dma_addr_t fast_smmu_map_page(struct device *dev, struct page *page,
if (unlikely(iova == DMA_ERROR_CODE))
goto fail;
- pmd = iopte_pmd_offset(mapping->pgtbl_pmds, mapping->base, iova);
+ pmd = iopte_pmd_offset(mapping->pgtbl_pmds,
+ mapping->domain->geometry.aperture_start, iova);
if (unlikely(av8l_fast_map_public(pmd, phys_to_map, len, prot)))
goto fail_free_iova;
@@ -391,7 +394,8 @@ static void fast_smmu_unmap_page(struct device *dev, dma_addr_t iova,
struct dma_fast_smmu_mapping *mapping = dev->archdata.mapping->fast;
unsigned long flags;
av8l_fast_iopte *pmd = iopte_pmd_offset(mapping->pgtbl_pmds,
- mapping->base, iova);
+ mapping->domain->geometry.aperture_start,
+ iova);
unsigned long offset = iova & ~FAST_PAGE_MASK;
size_t len = ALIGN(size + offset, FAST_PAGE_SIZE);
int nptes = len >> FAST_PAGE_SHIFT;
@@ -414,7 +418,8 @@ static void fast_smmu_sync_single_for_cpu(struct device *dev,
{
struct dma_fast_smmu_mapping *mapping = dev->archdata.mapping->fast;
av8l_fast_iopte *pmd = iopte_pmd_offset(mapping->pgtbl_pmds,
- mapping->base, iova);
+ mapping->domain->geometry.aperture_start,
+ iova);
unsigned long offset = iova & ~FAST_PAGE_MASK;
struct page *page = phys_to_page((*pmd & FAST_PTE_ADDR_MASK));
@@ -427,7 +432,8 @@ static void fast_smmu_sync_single_for_device(struct device *dev,
{
struct dma_fast_smmu_mapping *mapping = dev->archdata.mapping->fast;
av8l_fast_iopte *pmd = iopte_pmd_offset(mapping->pgtbl_pmds,
- mapping->base, iova);
+ mapping->domain->geometry.aperture_start,
+ iova);
unsigned long offset = iova & ~FAST_PAGE_MASK;
struct page *page = phys_to_page((*pmd & FAST_PTE_ADDR_MASK));
@@ -555,8 +561,9 @@ static void *fast_smmu_alloc(struct device *dev, size_t size,
while (sg_miter_next(&miter)) {
int nptes = miter.length >> FAST_PAGE_SHIFT;
- ptep = iopte_pmd_offset(mapping->pgtbl_pmds, mapping->base,
- iova_iter);
+ ptep = iopte_pmd_offset(mapping->pgtbl_pmds,
+ mapping->domain->geometry.aperture_start,
+ iova_iter);
if (unlikely(av8l_fast_map_public(
ptep, page_to_phys(miter.page),
miter.length, prot))) {
@@ -584,7 +591,9 @@ static void *fast_smmu_alloc(struct device *dev, size_t size,
out_unmap:
/* need to take the lock again for page tables and iova */
spin_lock_irqsave(&mapping->lock, flags);
- ptep = iopte_pmd_offset(mapping->pgtbl_pmds, mapping->base, dma_addr);
+ ptep = iopte_pmd_offset(mapping->pgtbl_pmds,
+ mapping->domain->geometry.aperture_start,
+ dma_addr);
av8l_fast_unmap_public(ptep, size);
fast_dmac_clean_range(mapping, ptep, ptep + count);
out_free_iova:
@@ -616,7 +625,8 @@ static void fast_smmu_free(struct device *dev, size_t size,
pages = area->pages;
dma_common_free_remap(vaddr, size, VM_USERMAP, false);
- ptep = iopte_pmd_offset(mapping->pgtbl_pmds, mapping->base, dma_handle);
+ ptep = iopte_pmd_offset(mapping->pgtbl_pmds,
+ mapping->domain->geometry.aperture_start, dma_handle);
spin_lock_irqsave(&mapping->lock, flags);
av8l_fast_unmap_public(ptep, size);
fast_dmac_clean_range(mapping, ptep, ptep + count);
@@ -720,7 +730,7 @@ static const struct dma_map_ops fast_smmu_dma_ops = {
*
* Creates a mapping structure which holds information about used/unused IO
* address ranges, which is required to perform mapping with IOMMU aware
- * functions. The only VA range supported is [0, 4GB).
+ * functions. The only VA range supported is [0, 4GB].
*
* The client device need to be attached to the mapping with
* fast_smmu_attach_device function.
@@ -774,6 +784,7 @@ int fast_smmu_attach_device(struct device *dev,
struct iommu_domain *domain = mapping->domain;
struct iommu_pgtbl_info info;
u64 size = (u64)mapping->bits << PAGE_SHIFT;
+ struct iommu_domain_geometry geometry;
if (mapping->base + size > (SZ_1G * 4ULL))
return -EINVAL;
@@ -788,8 +799,11 @@ int fast_smmu_attach_device(struct device *dev,
mapping->fast->domain = domain;
mapping->fast->dev = dev;
- domain->geometry.aperture_start = mapping->base;
- domain->geometry.aperture_end = mapping->base + size - 1;
+ geometry.aperture_start = mapping->base;
+ geometry.aperture_end = mapping->base + size - 1;
+ if (iommu_domain_set_attr(domain, DOMAIN_ATTR_GEOMETRY,
+ &geometry))
+ return -EINVAL;
if (iommu_attach_device(domain, dev))
return -EINVAL;
diff --git a/drivers/iommu/io-pgtable-fast.c b/drivers/iommu/io-pgtable-fast.c
index 3582e206db68..5378e95c4627 100644
--- a/drivers/iommu/io-pgtable-fast.c
+++ b/drivers/iommu/io-pgtable-fast.c
@@ -133,6 +133,9 @@ struct av8l_fast_io_pgtable {
#define AV8L_FAST_TCR_EPD1_SHIFT 23
#define AV8L_FAST_TCR_EPD1_FAULT 1
+#define AV8L_FAST_TCR_SEP_SHIFT (15 + 32)
+#define AV8L_FAST_TCR_SEP_UPSTREAM 7ULL
+
#define AV8L_FAST_MAIR_ATTR_SHIFT(n) ((n) << 3)
#define AV8L_FAST_MAIR_ATTR_MASK 0xff
#define AV8L_FAST_MAIR_ATTR_DEVICE 0x04
@@ -173,12 +176,12 @@ static void __av8l_check_for_stale_tlb(av8l_fast_iopte *ptep)
}
void av8l_fast_clear_stale_ptes(av8l_fast_iopte *pmds, u64 base,
- u64 end, bool skip_sync)
+ u64 start, u64 end, bool skip_sync)
{
int i;
- av8l_fast_iopte *pmdp = pmds;
+ av8l_fast_iopte *pmdp = iopte_pmd_offset(pmds, base, start);
- for (i = base >> AV8L_FAST_PAGE_SHIFT;
+ for (i = start >> AV8L_FAST_PAGE_SHIFT;
i <= (end >> AV8L_FAST_PAGE_SHIFT); ++i) {
if (!(*pmdp & AV8L_FAST_PTE_VALID)) {
*pmdp = 0;
@@ -256,16 +259,17 @@ void av8l_fast_unmap_public(av8l_fast_iopte *ptep, size_t size)
__av8l_fast_unmap(ptep, size, true);
}
-/* upper layer must take care of TLB invalidation */
static size_t av8l_fast_unmap(struct io_pgtable_ops *ops, unsigned long iova,
size_t size)
{
struct av8l_fast_io_pgtable *data = iof_pgtable_ops_to_data(ops);
+ struct io_pgtable *iop = &data->iop;
av8l_fast_iopte *ptep = iopte_pmd_offset(data->pmds, data->base, iova);
unsigned long nptes = size >> AV8L_FAST_PAGE_SHIFT;
__av8l_fast_unmap(ptep, size, false);
dmac_clean_range(ptep, ptep + nptes);
+ iop->cfg.tlb->tlb_flush_all(iop->cookie);
return size;
}
@@ -522,6 +526,7 @@ av8l_fast_alloc_pgtable(struct io_pgtable_cfg *cfg, void *cookie)
#if defined(CONFIG_ARM)
reg |= ARM_32_LPAE_TCR_EAE;
#endif
+ reg |= AV8L_FAST_TCR_SEP_UPSTREAM << AV8L_FAST_TCR_SEP_SHIFT;
cfg->av8l_fast_cfg.tcr = reg;
/* MAIRs */
@@ -668,7 +673,7 @@ static int __init av8l_fast_positive_testing(void)
}
/* sweep up TLB proving PTEs */
- av8l_fast_clear_stale_ptes(pmds, base, max, false);
+ av8l_fast_clear_stale_ptes(pmds, base, base, max, false);
/* map the entire 4GB VA space with 8K map calls */
for (iova = base; iova < max; iova += SZ_8K) {
@@ -689,7 +694,7 @@ static int __init av8l_fast_positive_testing(void)
}
/* sweep up TLB proving PTEs */
- av8l_fast_clear_stale_ptes(pmds, base, max, false);
+ av8l_fast_clear_stale_ptes(pmds, base, base, max, false);
/* map the entire 4GB VA space with 16K map calls */
for (iova = base; iova < max; iova += SZ_16K) {
@@ -710,7 +715,7 @@ static int __init av8l_fast_positive_testing(void)
}
/* sweep up TLB proving PTEs */
- av8l_fast_clear_stale_ptes(pmds, base, max, false);
+ av8l_fast_clear_stale_ptes(pmds, base, base, max, false);
/* map the entire 4GB VA space with 64K map calls */
for (iova = base; iova < max; iova += SZ_64K) {
diff --git a/drivers/iommu/iommu-debug.c b/drivers/iommu/iommu-debug.c
index 75fcde6e2c20..22b4934df6df 100644
--- a/drivers/iommu/iommu-debug.c
+++ b/drivers/iommu/iommu-debug.c
@@ -787,9 +787,13 @@ static int iommu_debug_profiling_fast_show(struct seq_file *s, void *ignored)
enum iommu_attr attrs[] = {
DOMAIN_ATTR_FAST,
DOMAIN_ATTR_ATOMIC,
+ DOMAIN_ATTR_GEOMETRY,
};
int one = 1;
- void *attr_values[] = { &one, &one, &one };
+ struct iommu_domain_geometry geometry = {0, 0, 0};
+ void *attr_values[] = { &one, &one, &geometry};
+
+ geometry.aperture_end = (dma_addr_t)(SZ_1G * 4ULL - 1);
iommu_debug_device_profiling(s, ddev->dev, attrs, attr_values,
ARRAY_SIZE(attrs), sizes);
diff --git a/drivers/leds/leds-qpnp-flash-v2.c b/drivers/leds/leds-qpnp-flash-v2.c
index de5c61418d07..1a6cad946a25 100644
--- a/drivers/leds/leds-qpnp-flash-v2.c
+++ b/drivers/leds/leds-qpnp-flash-v2.c
@@ -127,11 +127,11 @@
#define FLASH_LED_LMH_MITIGATION_DISABLE 0
#define FLASH_LED_CHGR_MITIGATION_ENABLE BIT(4)
#define FLASH_LED_CHGR_MITIGATION_DISABLE 0
-#define FLASH_LED_MITIGATION_SEL_DEFAULT 2
+#define FLASH_LED_LMH_MITIGATION_SEL_DEFAULT 2
#define FLASH_LED_MITIGATION_SEL_MAX 2
#define FLASH_LED_CHGR_MITIGATION_SEL_SHIFT 4
-#define FLASH_LED_MITIGATION_THRSH_DEFAULT 0xA
-#define FLASH_LED_MITIGATION_THRSH_MAX 0x1F
+#define FLASH_LED_CHGR_MITIGATION_THRSH_DEFAULT 0xA
+#define FLASH_LED_CHGR_MITIGATION_THRSH_MAX 0x1F
#define FLASH_LED_LMH_OCV_THRESH_DEFAULT_UV 3700000
#define FLASH_LED_LMH_RBATT_THRESH_DEFAULT_UOHM 400000
#define FLASH_LED_IRES_BASE 3
@@ -157,6 +157,12 @@
/* notifier call chain for flash-led irqs */
static ATOMIC_NOTIFIER_HEAD(irq_notifier_list);
+enum flash_charger_mitigation {
+ FLASH_DISABLE_CHARGER_MITIGATION,
+ FLASH_HW_CHARGER_MITIGATION_BY_ILED_THRSHLD,
+ FLASH_SW_CHARGER_MITIGATION,
+};
+
enum flash_led_type {
FLASH_LED_TYPE_FLASH,
FLASH_LED_TYPE_TORCH,
@@ -181,6 +187,7 @@ struct flash_node_data {
int ires_ua;
int max_current;
int current_ma;
+ int prev_current_ma;
u8 duration;
u8 id;
u8 type;
@@ -260,6 +267,7 @@ struct qpnp_flash_led {
int num_fnodes;
int num_snodes;
int enable;
+ int total_current_ma;
u16 base;
bool trigger_lmh;
bool trigger_chgr;
@@ -486,10 +494,12 @@ static int qpnp_flash_led_init_settings(struct qpnp_flash_led *led)
if (rc < 0)
return rc;
+ val = led->pdata->chgr_mitigation_sel
+ << FLASH_LED_CHGR_MITIGATION_SEL_SHIFT;
rc = qpnp_flash_led_masked_write(led,
FLASH_LED_REG_MITIGATION_SEL(led->base),
FLASH_LED_CHGR_MITIGATION_SEL_MASK,
- led->pdata->chgr_mitigation_sel);
+ val);
if (rc < 0)
return rc;
@@ -875,10 +885,24 @@ static int qpnp_flash_led_get_max_avail_current(struct qpnp_flash_led *led)
return max_avail_current;
}
+static void qpnp_flash_led_aggregate_max_current(struct flash_node_data *fnode)
+{
+ struct qpnp_flash_led *led = dev_get_drvdata(&fnode->pdev->dev);
+
+ if (fnode->current_ma)
+ led->total_current_ma += fnode->current_ma
+ - fnode->prev_current_ma;
+ else
+ led->total_current_ma -= fnode->prev_current_ma;
+
+ fnode->prev_current_ma = fnode->current_ma;
+}
+
static void qpnp_flash_led_node_set(struct flash_node_data *fnode, int value)
{
int prgm_current_ma = value;
int min_ma = fnode->ires_ua / 1000;
+ struct qpnp_flash_led *led = dev_get_drvdata(&fnode->pdev->dev);
if (value <= 0)
prgm_current_ma = 0;
@@ -891,6 +915,13 @@ static void qpnp_flash_led_node_set(struct flash_node_data *fnode, int value)
fnode->current_reg_val = CURRENT_MA_TO_REG_VAL(prgm_current_ma,
fnode->ires_ua);
fnode->led_on = prgm_current_ma != 0;
+
+ if (led->pdata->chgr_mitigation_sel == FLASH_SW_CHARGER_MITIGATION) {
+ qpnp_flash_led_aggregate_max_current(fnode);
+ led->trigger_chgr = false;
+ if (led->total_current_ma >= 1000)
+ led->trigger_chgr = true;
+ }
}
static int qpnp_flash_led_switch_disable(struct flash_switch_data *snode)
@@ -1158,10 +1189,6 @@ int qpnp_flash_led_prepare(struct led_trigger *trig, int options,
*max_current = rc;
}
- led->trigger_chgr = false;
- if (options & PRE_FLASH)
- led->trigger_chgr = true;
-
return 0;
}
@@ -1956,7 +1983,7 @@ static int qpnp_flash_led_parse_common_dt(struct qpnp_flash_led *led,
return rc;
}
- led->pdata->lmh_mitigation_sel = FLASH_LED_MITIGATION_SEL_DEFAULT;
+ led->pdata->lmh_mitigation_sel = FLASH_LED_LMH_MITIGATION_SEL_DEFAULT;
rc = of_property_read_u32(node, "qcom,lmh-mitigation-sel", &val);
if (!rc) {
led->pdata->lmh_mitigation_sel = val;
@@ -1970,7 +1997,7 @@ static int qpnp_flash_led_parse_common_dt(struct qpnp_flash_led *led,
return -EINVAL;
}
- led->pdata->chgr_mitigation_sel = FLASH_LED_MITIGATION_SEL_DEFAULT;
+ led->pdata->chgr_mitigation_sel = FLASH_SW_CHARGER_MITIGATION;
rc = of_property_read_u32(node, "qcom,chgr-mitigation-sel", &val);
if (!rc) {
led->pdata->chgr_mitigation_sel = val;
@@ -1984,9 +2011,7 @@ static int qpnp_flash_led_parse_common_dt(struct qpnp_flash_led *led,
return -EINVAL;
}
- led->pdata->chgr_mitigation_sel <<= FLASH_LED_CHGR_MITIGATION_SEL_SHIFT;
-
- led->pdata->iled_thrsh_val = FLASH_LED_MITIGATION_THRSH_DEFAULT;
+ led->pdata->iled_thrsh_val = FLASH_LED_CHGR_MITIGATION_THRSH_DEFAULT;
rc = of_property_read_u32(node, "qcom,iled-thrsh-ma", &val);
if (!rc) {
led->pdata->iled_thrsh_val = MITIGATION_THRSH_MA_TO_VAL(val);
@@ -1995,7 +2020,7 @@ static int qpnp_flash_led_parse_common_dt(struct qpnp_flash_led *led,
return rc;
}
- if (led->pdata->iled_thrsh_val > FLASH_LED_MITIGATION_THRSH_MAX) {
+ if (led->pdata->iled_thrsh_val > FLASH_LED_CHGR_MITIGATION_THRSH_MAX) {
pr_err("Invalid iled_thrsh_val specified\n");
return -EINVAL;
}
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 dce474e40470..63e46125c292 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
@@ -773,6 +773,40 @@ void msm_isp_check_for_output_error(struct vfe_device *vfe_dev,
}
}
+static int msm_isp_check_sync_time(struct msm_vfe_src_info *src_info,
+ struct msm_isp_timestamp *ts,
+ struct master_slave_resource_info *ms_res)
+{
+ int i;
+ struct msm_vfe_src_info *master_src_info = NULL;
+ uint32_t master_time = 0, current_time;
+
+ if (!ms_res->src_sof_mask)
+ return 0;
+
+ for (i = 0; i < MAX_VFE * VFE_SRC_MAX; i++) {
+ if (ms_res->src_info[i] == NULL)
+ continue;
+ if (src_info == ms_res->src_info[i] ||
+ ms_res->src_info[i]->active == 0)
+ continue;
+ if (ms_res->src_sof_mask &
+ (1 << ms_res->src_info[i]->dual_hw_ms_info.index)) {
+ master_src_info = ms_res->src_info[i];
+ break;
+ }
+ }
+ if (!master_src_info)
+ return 0;
+ master_time = master_src_info->
+ dual_hw_ms_info.sof_info.mono_timestamp_ms;
+ current_time = ts->buf_time.tv_sec * 1000 +
+ ts->buf_time.tv_usec / 1000;
+ if ((current_time - master_time) > ms_res->sof_delta_threshold)
+ return 1;
+ return 0;
+}
+
static void msm_isp_sync_dual_cam_frame_id(
struct vfe_device *vfe_dev,
struct master_slave_resource_info *ms_res,
@@ -787,11 +821,24 @@ static void msm_isp_sync_dual_cam_frame_id(
if (src_info->dual_hw_ms_info.sync_state ==
ms_res->dual_sync_mode) {
- (frame_src == VFE_PIX_0) ? src_info->frame_id +=
+ if (msm_isp_check_sync_time(src_info, ts, ms_res) == 0) {
+ (frame_src == VFE_PIX_0) ? src_info->frame_id +=
vfe_dev->axi_data.src_info[frame_src].
sof_counter_step :
src_info->frame_id++;
- return;
+ return;
+ }
+ ms_res->src_sof_mask = 0;
+ ms_res->active_src_mask = 0;
+ for (i = 0; i < MAX_VFE * VFE_SRC_MAX; i++) {
+ if (ms_res->src_info[i] == NULL)
+ continue;
+ if (ms_res->src_info[i]->active == 0)
+ continue;
+ ms_res->src_info[i]->dual_hw_ms_info.
+ sync_state =
+ MSM_ISP_DUAL_CAM_ASYNC;
+ }
}
WARN_ON(ms_res->dual_sync_mode == MSM_ISP_DUAL_CAM_ASYNC);
@@ -948,8 +995,6 @@ static void msm_isp_update_pd_stats_idx(struct vfe_device *vfe_dev,
uint32_t pingpong_status = 0, pingpong_bit = 0;
struct msm_isp_buffer *done_buf = NULL;
int vfe_idx = -1;
- /* initialize pd_buf_idx with an invalid index 0xF */
- vfe_dev->pd_buf_idx = 0xF;
if (frame_src < VFE_RAW_0 || frame_src > VFE_RAW_2)
return;
@@ -3387,22 +3432,21 @@ static int msm_isp_request_frame(struct vfe_device *vfe_dev,
/*
* If frame_id = 1 then no eof check is needed
*/
- if (vfe_dev->axi_data.src_info[VFE_PIX_0].active &&
- vfe_dev->axi_data.src_info[VFE_PIX_0].accept_frame == false) {
+ if (vfe_dev->axi_data.src_info[frame_src].active &&
+ frame_src == VFE_PIX_0 &&
+ vfe_dev->axi_data.src_info[frame_src].accept_frame == false) {
pr_debug("%s:%d invalid time to request frame %d\n",
__func__, __LINE__, frame_id);
goto error;
}
- if ((vfe_dev->axi_data.src_info[VFE_PIX_0].active && (frame_id !=
- vfe_dev->axi_data.src_info[VFE_PIX_0].frame_id + vfe_dev->
- axi_data.src_info[VFE_PIX_0].sof_counter_step)) ||
- ((!vfe_dev->axi_data.src_info[VFE_PIX_0].active) && (frame_id !=
+ if ((vfe_dev->axi_data.src_info[frame_src].active && (frame_id !=
vfe_dev->axi_data.src_info[frame_src].frame_id + vfe_dev->
- axi_data.src_info[frame_src].sof_counter_step))) {
+ axi_data.src_info[VFE_PIX_0].sof_counter_step)) ||
+ ((!vfe_dev->axi_data.src_info[frame_src].active))) {
pr_debug("%s:%d invalid frame id %d cur frame id %d pix %d\n",
__func__, __LINE__, frame_id,
- vfe_dev->axi_data.src_info[VFE_PIX_0].frame_id,
- vfe_dev->axi_data.src_info[VFE_PIX_0].active);
+ vfe_dev->axi_data.src_info[frame_src].frame_id,
+ vfe_dev->axi_data.src_info[frame_src].active);
goto error;
}
if (stream_info->undelivered_request_cnt >= MAX_BUFFERS_IN_HW) {
@@ -3909,6 +3953,12 @@ int msm_isp_update_axi_stream(struct vfe_device *vfe_dev, void *arg)
&update_cmd->req_frm_ver2;
stream_info = msm_isp_get_stream_common_data(vfe_dev,
HANDLE_TO_IDX(req_frm->stream_handle));
+ if (stream_info == NULL) {
+ pr_err_ratelimited("%s: stream_info is NULL\n",
+ __func__);
+ rc = -EINVAL;
+ break;
+ }
rc = msm_isp_request_frame(vfe_dev, stream_info,
req_frm->user_stream_id,
req_frm->frame_id,
diff --git a/drivers/media/platform/msm/camera_v2/isp/msm_isp_axi_util.h b/drivers/media/platform/msm/camera_v2/isp/msm_isp_axi_util.h
index 65009cb22286..a8d4cfb43927 100644
--- a/drivers/media/platform/msm/camera_v2/isp/msm_isp_axi_util.h
+++ b/drivers/media/platform/msm/camera_v2/isp/msm_isp_axi_util.h
@@ -141,6 +141,11 @@ static inline struct msm_vfe_axi_stream *msm_isp_get_stream_common_data(
struct msm_vfe_common_dev_data *common_data = vfe_dev->common_data;
struct msm_vfe_axi_stream *stream_info;
+ if (stream_idx >= VFE_AXI_SRC_MAX) {
+ pr_err("invalid stream_idx %d\n", stream_idx);
+ return NULL;
+ }
+
if (vfe_dev->is_split && stream_idx < RDI_INTF_0)
stream_info = &common_data->streams[stream_idx];
else
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 f2cf4d477b3f..f92c67150215 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
@@ -228,9 +228,10 @@ static int32_t msm_isp_stats_buf_divert(struct vfe_device *vfe_dev,
done_buf->buf_idx;
stats_event->pd_stats_idx = 0xF;
- if (stream_info->stats_type == MSM_ISP_STATS_BF)
+ if (stream_info->stats_type == MSM_ISP_STATS_BF) {
stats_event->pd_stats_idx = vfe_dev->pd_buf_idx;
-
+ vfe_dev->pd_buf_idx = 0xF;
+ }
if (comp_stats_type_mask == NULL) {
stats_event->stats_mask =
1 << stream_info->stats_type;
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 e7be914ca267..2a9bb6e8e505 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
@@ -2315,6 +2315,9 @@ int msm_isp_open_node(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
vfe_dev->reg_update_requested = 0;
/* Register page fault handler */
vfe_dev->buf_mgr->pagefault_debug_disable = 0;
+ /* initialize pd_buf_idx with an invalid index 0xF */
+ vfe_dev->pd_buf_idx = 0xF;
+
cam_smmu_reg_client_page_fault_handler(
vfe_dev->buf_mgr->iommu_hdl,
msm_vfe_iommu_fault_handler, vfe_dev);
diff --git a/drivers/media/platform/msm/camera_v2/sensor/cci/msm_cci.c b/drivers/media/platform/msm/camera_v2/sensor/cci/msm_cci.c
index b067c4916341..0ff270bb8410 100644
--- a/drivers/media/platform/msm/camera_v2/sensor/cci/msm_cci.c
+++ b/drivers/media/platform/msm/camera_v2/sensor/cci/msm_cci.c
@@ -201,6 +201,7 @@ static int32_t msm_cci_validate_queue(struct cci_device *cci_dev,
enum cci_i2c_queue_t queue)
{
int32_t rc = 0;
+ unsigned long flags;
uint32_t read_val = 0;
uint32_t reg_offset = master * 0x200 + queue * 0x100;
read_val = msm_camera_io_r_mb(cci_dev->base +
@@ -223,6 +224,8 @@ static int32_t msm_cci_validate_queue(struct cci_device *cci_dev,
CCI_I2C_M0_Q0_EXEC_WORD_CNT_ADDR + reg_offset);
reg_val = 1 << ((master * 2) + queue);
CDBG("%s:%d CCI_QUEUE_START_ADDR\n", __func__, __LINE__);
+ spin_lock_irqsave(&cci_dev->cci_master_info[master].
+ lock_q[queue], flags);
atomic_set(&cci_dev->cci_master_info[master].
done_pending[queue], 1);
msm_camera_io_w_mb(reg_val, cci_dev->base +
@@ -230,6 +233,8 @@ static int32_t msm_cci_validate_queue(struct cci_device *cci_dev,
CDBG("%s line %d wait_for_completion_timeout\n",
__func__, __LINE__);
atomic_set(&cci_dev->cci_master_info[master].q_free[queue], 1);
+ spin_unlock_irqrestore(&cci_dev->cci_master_info[master].
+ lock_q[queue], flags);
rc = wait_for_completion_timeout(&cci_dev->
cci_master_info[master].report_q[queue], CCI_TIMEOUT);
if (rc <= 0) {
@@ -438,10 +443,17 @@ static int32_t msm_cci_wait_report_cmd(struct cci_device *cci_dev,
enum cci_i2c_master_t master,
enum cci_i2c_queue_t queue)
{
+ unsigned long flags;
uint32_t reg_val = 1 << ((master * 2) + queue);
msm_cci_load_report_cmd(cci_dev, master, queue);
+
+ spin_lock_irqsave(&cci_dev->cci_master_info[master].
+ lock_q[queue], flags);
atomic_set(&cci_dev->cci_master_info[master].q_free[queue], 1);
atomic_set(&cci_dev->cci_master_info[master].done_pending[queue], 1);
+ spin_unlock_irqrestore(&cci_dev->cci_master_info[master].
+ lock_q[queue], flags);
+
msm_camera_io_w_mb(reg_val, cci_dev->base +
CCI_QUEUE_START_ADDR);
return msm_cci_wait(cci_dev, master, queue);
@@ -451,13 +463,19 @@ static void msm_cci_process_half_q(struct cci_device *cci_dev,
enum cci_i2c_master_t master,
enum cci_i2c_queue_t queue)
{
+ unsigned long flags;
uint32_t reg_val = 1 << ((master * 2) + queue);
+
+ spin_lock_irqsave(&cci_dev->cci_master_info[master].
+ lock_q[queue], flags);
if (0 == atomic_read(&cci_dev->cci_master_info[master].q_free[queue])) {
msm_cci_load_report_cmd(cci_dev, master, queue);
atomic_set(&cci_dev->cci_master_info[master].q_free[queue], 1);
msm_camera_io_w_mb(reg_val, cci_dev->base +
CCI_QUEUE_START_ADDR);
}
+ spin_unlock_irqrestore(&cci_dev->cci_master_info[master].
+ lock_q[queue], flags);
}
static int32_t msm_cci_process_full_q(struct cci_device *cci_dev,
@@ -465,15 +483,23 @@ static int32_t msm_cci_process_full_q(struct cci_device *cci_dev,
enum cci_i2c_queue_t queue)
{
int32_t rc = 0;
+ unsigned long flags;
+
+ spin_lock_irqsave(&cci_dev->cci_master_info[master].
+ lock_q[queue], flags);
if (1 == atomic_read(&cci_dev->cci_master_info[master].q_free[queue])) {
atomic_set(&cci_dev->cci_master_info[master].
done_pending[queue], 1);
+ spin_unlock_irqrestore(&cci_dev->cci_master_info[master].
+ lock_q[queue], flags);
rc = msm_cci_wait(cci_dev, master, queue);
if (rc < 0) {
pr_err("%s: %d failed rc %d\n", __func__, __LINE__, rc);
return rc;
}
} else {
+ spin_unlock_irqrestore(&cci_dev->cci_master_info[master].
+ lock_q[queue], flags);
rc = msm_cci_wait_report_cmd(cci_dev, master, queue);
if (rc < 0) {
pr_err("%s: %d failed rc %d\n", __func__, __LINE__, rc);
@@ -501,8 +527,13 @@ static int32_t msm_cci_transfer_end(struct cci_device *cci_dev,
enum cci_i2c_queue_t queue)
{
int32_t rc = 0;
+ unsigned long flags;
+ spin_lock_irqsave(&cci_dev->cci_master_info[master].
+ lock_q[queue], flags);
if (0 == atomic_read(&cci_dev->cci_master_info[master].q_free[queue])) {
+ spin_unlock_irqrestore(&cci_dev->cci_master_info[master].
+ lock_q[queue], flags);
rc = msm_cci_lock_queue(cci_dev, master, queue, 0);
if (rc < 0) {
pr_err("%s failed line %d\n", __func__, __LINE__);
@@ -516,6 +547,8 @@ static int32_t msm_cci_transfer_end(struct cci_device *cci_dev,
} else {
atomic_set(&cci_dev->cci_master_info[master].
done_pending[queue], 1);
+ spin_unlock_irqrestore(&cci_dev->cci_master_info[master].
+ lock_q[queue], flags);
rc = msm_cci_wait(cci_dev, master, queue);
if (rc < 0) {
pr_err("%s: %d failed rc %d\n", __func__, __LINE__, rc);
@@ -570,6 +603,7 @@ static int32_t msm_cci_data_queue(struct cci_device *cci_dev,
uint32_t reg_offset;
uint32_t val = 0;
uint32_t max_queue_size;
+ unsigned long flags;
if (i2c_cmd == NULL) {
pr_err("%s:%d Failed line\n", __func__,
@@ -613,7 +647,11 @@ static int32_t msm_cci_data_queue(struct cci_device *cci_dev,
msm_camera_io_w_mb(val, cci_dev->base + CCI_I2C_M0_Q0_LOAD_DATA_ADDR +
reg_offset);
+ spin_lock_irqsave(&cci_dev->cci_master_info[master].
+ lock_q[queue], flags);
atomic_set(&cci_dev->cci_master_info[master].q_free[queue], 0);
+ spin_unlock_irqrestore(&cci_dev->cci_master_info[master].
+ lock_q[queue], flags);
max_queue_size = cci_dev->cci_i2c_queue_info[master][queue].
max_queue_size;
@@ -1641,6 +1679,7 @@ static int32_t msm_cci_config(struct v4l2_subdev *sd,
static irqreturn_t msm_cci_irq(int irq_num, void *data)
{
uint32_t irq;
+ unsigned long flags;
struct cci_device *cci_dev = data;
irq = msm_camera_io_r_mb(cci_dev->base + CCI_IRQ_STATUS_0_ADDR);
msm_camera_io_w_mb(irq, cci_dev->base + CCI_IRQ_CLEAR_0_ADDR);
@@ -1667,22 +1706,30 @@ static irqreturn_t msm_cci_irq(int irq_num, void *data)
if (irq & CCI_IRQ_STATUS_0_I2C_M0_Q0_REPORT_BMSK) {
struct msm_camera_cci_master_info *cci_master_info;
cci_master_info = &cci_dev->cci_master_info[MASTER_0];
+ spin_lock_irqsave(&cci_dev->cci_master_info[MASTER_0].
+ lock_q[QUEUE_0], flags);
atomic_set(&cci_master_info->q_free[QUEUE_0], 0);
cci_master_info->status = 0;
if (atomic_read(&cci_master_info->done_pending[QUEUE_0]) == 1) {
complete(&cci_master_info->report_q[QUEUE_0]);
atomic_set(&cci_master_info->done_pending[QUEUE_0], 0);
}
+ spin_unlock_irqrestore(&cci_dev->cci_master_info[MASTER_0].
+ lock_q[QUEUE_0], flags);
}
if (irq & CCI_IRQ_STATUS_0_I2C_M0_Q1_REPORT_BMSK) {
struct msm_camera_cci_master_info *cci_master_info;
cci_master_info = &cci_dev->cci_master_info[MASTER_0];
+ spin_lock_irqsave(&cci_dev->cci_master_info[MASTER_0].
+ lock_q[QUEUE_1], flags);
atomic_set(&cci_master_info->q_free[QUEUE_1], 0);
cci_master_info->status = 0;
if (atomic_read(&cci_master_info->done_pending[QUEUE_1]) == 1) {
complete(&cci_master_info->report_q[QUEUE_1]);
atomic_set(&cci_master_info->done_pending[QUEUE_1], 0);
}
+ spin_unlock_irqrestore(&cci_dev->cci_master_info[MASTER_0].
+ lock_q[QUEUE_1], flags);
}
if (irq & CCI_IRQ_STATUS_0_I2C_M1_RD_DONE_BMSK) {
cci_dev->cci_master_info[MASTER_1].status = 0;
@@ -1691,22 +1738,30 @@ static irqreturn_t msm_cci_irq(int irq_num, void *data)
if (irq & CCI_IRQ_STATUS_0_I2C_M1_Q0_REPORT_BMSK) {
struct msm_camera_cci_master_info *cci_master_info;
cci_master_info = &cci_dev->cci_master_info[MASTER_1];
+ spin_lock_irqsave(&cci_dev->cci_master_info[MASTER_1].
+ lock_q[QUEUE_0], flags);
atomic_set(&cci_master_info->q_free[QUEUE_0], 0);
cci_master_info->status = 0;
if (atomic_read(&cci_master_info->done_pending[QUEUE_0]) == 1) {
complete(&cci_master_info->report_q[QUEUE_0]);
atomic_set(&cci_master_info->done_pending[QUEUE_0], 0);
}
+ spin_unlock_irqrestore(&cci_dev->cci_master_info[MASTER_1].
+ lock_q[QUEUE_0], flags);
}
if (irq & CCI_IRQ_STATUS_0_I2C_M1_Q1_REPORT_BMSK) {
struct msm_camera_cci_master_info *cci_master_info;
cci_master_info = &cci_dev->cci_master_info[MASTER_1];
+ spin_lock_irqsave(&cci_dev->cci_master_info[MASTER_1].
+ lock_q[QUEUE_1], flags);
atomic_set(&cci_master_info->q_free[QUEUE_1], 0);
cci_master_info->status = 0;
if (atomic_read(&cci_master_info->done_pending[QUEUE_1]) == 1) {
complete(&cci_master_info->report_q[QUEUE_1]);
atomic_set(&cci_master_info->done_pending[QUEUE_1], 0);
}
+ spin_unlock_irqrestore(&cci_dev->cci_master_info[MASTER_1].
+ lock_q[QUEUE_1], flags);
}
if (irq & CCI_IRQ_STATUS_0_I2C_M0_Q0Q1_HALT_ACK_BMSK) {
cci_dev->cci_master_info[MASTER_0].reset_pending = TRUE;
@@ -1795,7 +1850,9 @@ static void msm_cci_init_cci_params(struct cci_device *new_cci_dev)
mutex_init(&new_cci_dev->cci_master_info[i].mutex_q[j]);
init_completion(&new_cci_dev->
cci_master_info[i].report_q[j]);
- }
+ spin_lock_init(&new_cci_dev->
+ cci_master_info[i].lock_q[j]);
+ }
}
return;
}
diff --git a/drivers/media/platform/msm/camera_v2/sensor/cci/msm_cci.h b/drivers/media/platform/msm/camera_v2/sensor/cci/msm_cci.h
index 6e39d814bd73..eb615cc7a62c 100644
--- a/drivers/media/platform/msm/camera_v2/sensor/cci/msm_cci.h
+++ b/drivers/media/platform/msm/camera_v2/sensor/cci/msm_cci.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012-2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2012-2017, 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
@@ -125,6 +125,7 @@ struct msm_camera_cci_master_info {
struct mutex mutex_q[NUM_QUEUES];
struct completion report_q[NUM_QUEUES];
atomic_t done_pending[NUM_QUEUES];
+ spinlock_t lock_q[NUM_QUEUES];
};
struct msm_cci_clk_params_t {
diff --git a/drivers/net/wireless/ath/wil6210/cfg80211.c b/drivers/net/wireless/ath/wil6210/cfg80211.c
index 476521b77008..37898146f01d 100644
--- a/drivers/net/wireless/ath/wil6210/cfg80211.c
+++ b/drivers/net/wireless/ath/wil6210/cfg80211.c
@@ -1414,6 +1414,8 @@ static int wil_cfg80211_stop_ap(struct wiphy *wiphy,
wil6210_bus_request(wil, WIL_DEFAULT_BUS_REQUEST_KBPS);
wil_set_recovery_state(wil, fw_recovery_idle);
+ set_bit(wil_status_resetting, wil->status);
+
mutex_lock(&wil->mutex);
wmi_pcp_stop(wil);
diff --git a/drivers/net/wireless/ath/wil6210/pcie_bus.c b/drivers/net/wireless/ath/wil6210/pcie_bus.c
index d472e13fb9d9..2a515e848820 100644
--- a/drivers/net/wireless/ath/wil6210/pcie_bus.c
+++ b/drivers/net/wireless/ath/wil6210/pcie_bus.c
@@ -26,6 +26,10 @@ static bool use_msi = true;
module_param(use_msi, bool, 0444);
MODULE_PARM_DESC(use_msi, " Use MSI interrupt, default - true");
+static bool ftm_mode;
+module_param(ftm_mode, bool, 0444);
+MODULE_PARM_DESC(ftm_mode, " Set factory test mode, default - false");
+
#ifdef CONFIG_PM
#ifdef CONFIG_PM_SLEEP
static int wil6210_pm_notify(struct notifier_block *notify_block,
@@ -36,13 +40,15 @@ static int wil6210_pm_notify(struct notifier_block *notify_block,
static
void wil_set_capabilities(struct wil6210_priv *wil)
{
+ const char *wil_fw_name;
u32 jtag_id = wil_r(wil, RGF_USER_JTAG_DEV_ID);
u8 chip_revision = (wil_r(wil, RGF_USER_REVISION_ID) &
RGF_USER_REVISION_ID_MASK);
bitmap_zero(wil->hw_capabilities, hw_capability_last);
bitmap_zero(wil->fw_capabilities, WMI_FW_CAPABILITY_MAX);
- wil->wil_fw_name = WIL_FW_NAME_DEFAULT;
+ wil->wil_fw_name = ftm_mode ? WIL_FW_NAME_FTM_DEFAULT :
+ WIL_FW_NAME_DEFAULT;
wil->chip_revision = chip_revision;
switch (jtag_id) {
@@ -51,9 +57,11 @@ void wil_set_capabilities(struct wil6210_priv *wil)
case REVISION_ID_SPARROW_D0:
wil->hw_name = "Sparrow D0";
wil->hw_version = HW_VER_SPARROW_D0;
- if (wil_fw_verify_file_exists(wil,
- WIL_FW_NAME_SPARROW_PLUS))
- wil->wil_fw_name = WIL_FW_NAME_SPARROW_PLUS;
+ wil_fw_name = ftm_mode ? WIL_FW_NAME_FTM_SPARROW_PLUS :
+ WIL_FW_NAME_SPARROW_PLUS;
+
+ if (wil_fw_verify_file_exists(wil, wil_fw_name))
+ wil->wil_fw_name = wil_fw_name;
break;
case REVISION_ID_SPARROW_B0:
wil->hw_name = "Sparrow B0";
diff --git a/drivers/net/wireless/ath/wil6210/wil6210.h b/drivers/net/wireless/ath/wil6210/wil6210.h
index 9d3e7a4f911e..0529d10a8268 100644
--- a/drivers/net/wireless/ath/wil6210/wil6210.h
+++ b/drivers/net/wireless/ath/wil6210/wil6210.h
@@ -37,8 +37,13 @@ extern bool debug_fw;
extern bool disable_ap_sme;
#define WIL_NAME "wil6210"
-#define WIL_FW_NAME_DEFAULT "wil6210.fw" /* code Sparrow B0 */
-#define WIL_FW_NAME_SPARROW_PLUS "wil6210_sparrow_plus.fw" /* code Sparrow D0 */
+
+#define WIL_FW_NAME_DEFAULT "wil6210.fw"
+#define WIL_FW_NAME_FTM_DEFAULT "wil6210_ftm.fw"
+
+#define WIL_FW_NAME_SPARROW_PLUS "wil6210_sparrow_plus.fw"
+#define WIL_FW_NAME_FTM_SPARROW_PLUS "wil6210_sparrow_plus_ftm.fw"
+
#define WIL_BOARD_FILE_NAME "wil6210.brd" /* board & radio parameters */
#define WIL_DEFAULT_BUS_REQUEST_KBPS 128000 /* ~1Gbps */
diff --git a/drivers/net/wireless/ath/wil6210/wmi.c b/drivers/net/wireless/ath/wil6210/wmi.c
index 83ef6eb57e53..41afbdc34c18 100644
--- a/drivers/net/wireless/ath/wil6210/wmi.c
+++ b/drivers/net/wireless/ath/wil6210/wmi.c
@@ -518,16 +518,16 @@ static void wmi_evt_connect(struct wil6210_priv *wil, int id, void *d, int len)
assoc_resp_ielen = 0;
}
- mutex_lock(&wil->mutex);
if (test_bit(wil_status_resetting, wil->status) ||
!test_bit(wil_status_fwready, wil->status)) {
wil_err(wil, "status_resetting, cancel connect event, CID %d\n",
evt->cid);
- mutex_unlock(&wil->mutex);
/* no need for cleanup, wil_reset will do that */
return;
}
+ mutex_lock(&wil->mutex);
+
if ((wdev->iftype == NL80211_IFTYPE_STATION) ||
(wdev->iftype == NL80211_IFTYPE_P2P_CLIENT)) {
if (!test_bit(wil_status_fwconnecting, wil->status)) {
@@ -631,6 +631,13 @@ static void wmi_evt_disconnect(struct wil6210_priv *wil, int id,
wil->sinfo_gen++;
+ if (test_bit(wil_status_resetting, wil->status) ||
+ !test_bit(wil_status_fwready, wil->status)) {
+ wil_err(wil, "status_resetting, cancel disconnect event\n");
+ /* no need for cleanup, wil_reset will do that */
+ return;
+ }
+
mutex_lock(&wil->mutex);
wil6210_disconnect(wil, evt->bssid, reason_code, true);
mutex_unlock(&wil->mutex);
diff --git a/drivers/net/wireless/wcnss/wcnss_wlan.c b/drivers/net/wireless/wcnss/wcnss_wlan.c
index 3f9eeabc5464..450b7ad8bf7f 100644
--- a/drivers/net/wireless/wcnss/wcnss_wlan.c
+++ b/drivers/net/wireless/wcnss/wcnss_wlan.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2011-2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2011-2017, 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
@@ -192,6 +192,8 @@ static DEFINE_SPINLOCK(reg_spinlock);
#define WCNSS_USR_WLAN_MAC_ADDR (WCNSS_USR_CTRL_MSG_START + 3)
#define MAC_ADDRESS_STR "%02x:%02x:%02x:%02x:%02x:%02x"
+#define SHOW_MAC_ADDRESS_STR "%02x:%02x:%02x:%02x:%02x:%02x\n"
+#define WCNSS_USER_MAC_ADDR_LENGTH 18
/* message types */
#define WCNSS_CTRL_MSG_START 0x01000000
@@ -396,7 +398,6 @@ static struct {
int user_cal_available;
u32 user_cal_rcvd;
int user_cal_exp_size;
- int device_opened;
int iris_xo_mode_set;
int fw_vbatt_state;
char wlan_nv_macAddr[WLAN_MAC_ADDR_SIZE];
@@ -427,23 +428,28 @@ static struct {
static ssize_t wcnss_wlan_macaddr_store(struct device *dev,
struct device_attribute *attr, const char *buf, size_t count)
{
- char macAddr[WLAN_MAC_ADDR_SIZE];
+ int index;
+ int macAddr[WLAN_MAC_ADDR_SIZE];
if (!penv)
return -ENODEV;
- pr_debug("%s: Receive MAC Addr From user space: %s\n", __func__, buf);
+ if (strlen(buf) != WCNSS_USER_MAC_ADDR_LENGTH) {
+ dev_err(dev, "%s: Invalid MAC addr length\n", __func__);
+ return -EINVAL;
+ }
if (WLAN_MAC_ADDR_SIZE != sscanf(buf, MAC_ADDRESS_STR,
- (int *)&macAddr[0], (int *)&macAddr[1],
- (int *)&macAddr[2], (int *)&macAddr[3],
- (int *)&macAddr[4], (int *)&macAddr[5])) {
-
+ &macAddr[0], &macAddr[1], &macAddr[2],
+ &macAddr[3], &macAddr[4], &macAddr[5])) {
pr_err("%s: Failed to Copy MAC\n", __func__);
return -EINVAL;
}
- memcpy(penv->wlan_nv_macAddr, macAddr, sizeof(penv->wlan_nv_macAddr));
+ for (index = 0; index < WLAN_MAC_ADDR_SIZE; index++) {
+ memcpy(&penv->wlan_nv_macAddr[index],
+ (char *)&macAddr[index], sizeof(char));
+ }
pr_info("%s: Write MAC Addr:" MAC_ADDRESS_STR "\n", __func__,
penv->wlan_nv_macAddr[0], penv->wlan_nv_macAddr[1],
@@ -459,7 +465,7 @@ static ssize_t wcnss_wlan_macaddr_show(struct device *dev,
if (!penv)
return -ENODEV;
- return scnprintf(buf, PAGE_SIZE, MAC_ADDRESS_STR,
+ return scnprintf(buf, PAGE_SIZE, SHOW_MAC_ADDRESS_STR,
penv->wlan_nv_macAddr[0], penv->wlan_nv_macAddr[1],
penv->wlan_nv_macAddr[2], penv->wlan_nv_macAddr[3],
penv->wlan_nv_macAddr[4], penv->wlan_nv_macAddr[5]);
@@ -3258,14 +3264,6 @@ static int wcnss_node_open(struct inode *inode, struct file *file)
return -EFAULT;
}
- mutex_lock(&penv->dev_lock);
- penv->user_cal_rcvd = 0;
- penv->user_cal_read = 0;
- penv->user_cal_available = false;
- penv->user_cal_data = NULL;
- penv->device_opened = 1;
- mutex_unlock(&penv->dev_lock);
-
return rc;
}
@@ -3274,7 +3272,7 @@ static ssize_t wcnss_wlan_read(struct file *fp, char __user
{
int rc = 0;
- if (!penv || !penv->device_opened)
+ if (!penv)
return -EFAULT;
rc = wait_event_interruptible(penv->read_wait, penv->fw_cal_rcvd
@@ -3311,55 +3309,66 @@ static ssize_t wcnss_wlan_write(struct file *fp, const char __user
*user_buffer, size_t count, loff_t *position)
{
int rc = 0;
- u32 size = 0;
+ char *cal_data = NULL;
- if (!penv || !penv->device_opened || penv->user_cal_available)
+ if (!penv || penv->user_cal_available)
return -EFAULT;
- if (penv->user_cal_rcvd == 0 && count >= 4
- && !penv->user_cal_data) {
- rc = copy_from_user((void *)&size, user_buffer, 4);
- if (!size || size > MAX_CALIBRATED_DATA_SIZE) {
- pr_err(DEVICE " invalid size to write %d\n", size);
+ if (!penv->user_cal_rcvd && count >= 4 && !penv->user_cal_exp_size) {
+ mutex_lock(&penv->dev_lock);
+ rc = copy_from_user((void *)&penv->user_cal_exp_size,
+ user_buffer, 4);
+ if (!penv->user_cal_exp_size ||
+ penv->user_cal_exp_size > MAX_CALIBRATED_DATA_SIZE) {
+ pr_err(DEVICE " invalid size to write %d\n",
+ penv->user_cal_exp_size);
+ penv->user_cal_exp_size = 0;
+ mutex_unlock(&penv->dev_lock);
return -EFAULT;
}
-
- rc += count;
- count -= 4;
- penv->user_cal_exp_size = size;
- penv->user_cal_data = kmalloc(size, GFP_KERNEL);
- if (penv->user_cal_data == NULL) {
- pr_err(DEVICE " no memory to write\n");
- return -ENOMEM;
- }
- if (0 == count)
- goto exit;
-
- } else if (penv->user_cal_rcvd == 0 && count < 4)
+ mutex_unlock(&penv->dev_lock);
+ return count;
+ } else if (!penv->user_cal_rcvd && count < 4) {
return -EFAULT;
+ }
+ mutex_lock(&penv->dev_lock);
if ((UINT32_MAX - count < penv->user_cal_rcvd) ||
(penv->user_cal_exp_size < count + penv->user_cal_rcvd)) {
pr_err(DEVICE " invalid size to write %zu\n", count +
penv->user_cal_rcvd);
- rc = -ENOMEM;
- goto exit;
+ mutex_unlock(&penv->dev_lock);
+ return -ENOMEM;
}
- rc = copy_from_user((void *)penv->user_cal_data +
- penv->user_cal_rcvd, user_buffer, count);
- if (0 == rc) {
+
+ cal_data = kmalloc(count, GFP_KERNEL);
+ if (!cal_data) {
+ mutex_unlock(&penv->dev_lock);
+ return -ENOMEM;
+ }
+
+ rc = copy_from_user(cal_data, user_buffer, count);
+ if (!rc) {
+ memcpy(penv->user_cal_data + penv->user_cal_rcvd,
+ cal_data, count);
penv->user_cal_rcvd += count;
rc += count;
}
+
+ kfree(cal_data);
if (penv->user_cal_rcvd == penv->user_cal_exp_size) {
penv->user_cal_available = true;
pr_info_ratelimited("wcnss: user cal written");
}
+ mutex_unlock(&penv->dev_lock);
-exit:
return rc;
}
+static int wcnss_node_release(struct inode *inode, struct file *file)
+{
+ return 0;
+}
static int wcnss_notif_cb(struct notifier_block *this, unsigned long code,
void *ss_handle)
@@ -3418,6 +3427,7 @@ static const struct file_operations wcnss_node_fops = {
.open = wcnss_node_open,
.read = wcnss_wlan_read,
.write = wcnss_wlan_write,
+ .release = wcnss_node_release,
};
static struct miscdevice wcnss_misc = {
@@ -3445,6 +3455,13 @@ wcnss_wlan_probe(struct platform_device *pdev)
}
penv->pdev = pdev;
+ penv->user_cal_data =
+ devm_kzalloc(&pdev->dev, MAX_CALIBRATED_DATA_SIZE, GFP_KERNEL);
+ if (!penv->user_cal_data) {
+ dev_err(&pdev->dev, "Failed to alloc memory for cal data.\n");
+ return -ENOMEM;
+ }
+
/* register sysfs entries */
ret = wcnss_create_sysfs(&pdev->dev);
if (ret) {
@@ -3465,6 +3482,11 @@ wcnss_wlan_probe(struct platform_device *pdev)
mutex_init(&penv->pm_qos_mutex);
init_waitqueue_head(&penv->read_wait);
+ penv->user_cal_rcvd = 0;
+ penv->user_cal_read = 0;
+ penv->user_cal_exp_size = 0;
+ penv->user_cal_available = false;
+
/* Since we were built into the kernel we'll be called as part
* of kernel initialization. We don't know if userspace
* applications are available to service PIL at this time
diff --git a/drivers/pci/host/pci-msm.c b/drivers/pci/host/pci-msm.c
index 9dc678ce4a48..f364882943e1 100644
--- a/drivers/pci/host/pci-msm.c
+++ b/drivers/pci/host/pci-msm.c
@@ -5811,9 +5811,10 @@ static int msm_pcie_map_qgic_addr(struct msm_pcie_dev_t *dev,
struct msi_msg *msg)
{
struct iommu_domain *domain = iommu_get_domain_for_dev(&pdev->dev);
- int ret, bypass_en = 0;
+ struct iommu_domain_geometry geometry;
+ int ret, fastmap_en = 0, bypass_en = 0;
dma_addr_t iova;
- phys_addr_t pcie_base_addr, gicm_db_offset;
+ phys_addr_t gicm_db_offset;
msg->address_hi = 0;
msg->address_lo = dev->msi_gicm_addr;
@@ -5835,16 +5836,25 @@ static int msm_pcie_map_qgic_addr(struct msm_pcie_dev_t *dev,
if (bypass_en)
return 0;
- gicm_db_offset = dev->msi_gicm_addr -
- rounddown(dev->msi_gicm_addr, PAGE_SIZE);
- /*
- * Use PCIe DBI address as the IOVA since client cannot
- * use this address for their IOMMU mapping. This will
- * prevent any conflicts between PCIe host and
- * client's mapping.
- */
- pcie_base_addr = dev->res[MSM_PCIE_RES_DM_CORE].resource->start;
- iova = rounddown(pcie_base_addr, PAGE_SIZE);
+ iommu_domain_get_attr(domain, DOMAIN_ATTR_FAST, &fastmap_en);
+ if (fastmap_en) {
+ iommu_domain_get_attr(domain, DOMAIN_ATTR_GEOMETRY, &geometry);
+ iova = geometry.aperture_start;
+ PCIE_DBG(dev,
+ "PCIe: RC%d: Use client's IOVA 0x%llx to map QGIC MSI address\n",
+ dev->rc_idx, iova);
+ } else {
+ phys_addr_t pcie_base_addr;
+
+ /*
+ * Use PCIe DBI address as the IOVA since client cannot
+ * use this address for their IOMMU mapping. This will
+ * prevent any conflicts between PCIe host and
+ * client's mapping.
+ */
+ pcie_base_addr = dev->res[MSM_PCIE_RES_DM_CORE].resource->start;
+ iova = rounddown(pcie_base_addr, PAGE_SIZE);
+ }
ret = iommu_map(domain, iova, rounddown(dev->msi_gicm_addr, PAGE_SIZE),
PAGE_SIZE, IOMMU_READ | IOMMU_WRITE);
@@ -5855,6 +5865,8 @@ static int msm_pcie_map_qgic_addr(struct msm_pcie_dev_t *dev,
return -ENOMEM;
}
+ gicm_db_offset = dev->msi_gicm_addr -
+ rounddown(dev->msi_gicm_addr, PAGE_SIZE);
msg->address_lo = iova + gicm_db_offset;
return 0;
diff --git a/drivers/perf/arm_pmu.c b/drivers/perf/arm_pmu.c
index 4c75b4d392c6..39400dda27c2 100644
--- a/drivers/perf/arm_pmu.c
+++ b/drivers/perf/arm_pmu.c
@@ -367,6 +367,8 @@ armpmu_reserve_hardware(struct arm_pmu *armpmu)
return err;
}
+ armpmu->pmu_state = ARM_PMU_STATE_RUNNING;
+
return 0;
}
@@ -601,10 +603,12 @@ static void cpu_pmu_free_irq(struct arm_pmu *cpu_pmu)
struct platform_device *pmu_device = cpu_pmu->plat_device;
struct pmu_hw_events __percpu *hw_events = cpu_pmu->hw_events;
+ cpu_pmu->pmu_state = ARM_PMU_STATE_GOING_DOWN;
+
irqs = min(pmu_device->num_resources, num_possible_cpus());
irq = platform_get_irq(pmu_device, 0);
- if (irq >= 0 && irq_is_percpu(irq)) {
+ if (irq > 0 && irq_is_percpu(irq)) {
on_each_cpu(cpu_pmu_disable_percpu_irq, &irq, 1);
free_percpu_irq(irq, &hw_events->percpu_pmu);
} else {
@@ -617,10 +621,11 @@ static void cpu_pmu_free_irq(struct arm_pmu *cpu_pmu)
if (!cpumask_test_and_clear_cpu(cpu, &cpu_pmu->active_irqs))
continue;
irq = platform_get_irq(pmu_device, i);
- if (irq >= 0)
+ if (irq > 0)
free_irq(irq, per_cpu_ptr(&hw_events->percpu_pmu, cpu));
}
}
+ cpu_pmu->pmu_state = ARM_PMU_STATE_OFF;
}
static int cpu_pmu_request_irq(struct arm_pmu *cpu_pmu, irq_handler_t handler)
@@ -639,7 +644,7 @@ static int cpu_pmu_request_irq(struct arm_pmu *cpu_pmu, irq_handler_t handler)
}
irq = platform_get_irq(pmu_device, 0);
- if (irq >= 0 && irq_is_percpu(irq)) {
+ if (irq > 0 && irq_is_percpu(irq)) {
err = request_percpu_irq(irq, handler, "arm-pmu",
&hw_events->percpu_pmu);
if (err) {
@@ -648,6 +653,7 @@ static int cpu_pmu_request_irq(struct arm_pmu *cpu_pmu, irq_handler_t handler)
return err;
}
on_each_cpu(cpu_pmu_enable_percpu_irq, &irq, 1);
+ cpu_pmu->percpu_irq = irq;
} else {
for (i = 0; i < irqs; ++i) {
int cpu = i;
@@ -754,13 +760,6 @@ static void cpu_pm_pmu_common(void *info)
return;
}
- /*
- * Always reset the PMU registers on power-up even if
- * there are no events running.
- */
- if (cmd == CPU_PM_EXIT && armpmu->reset)
- armpmu->reset(armpmu);
-
if (!enabled) {
data->ret = NOTIFY_OK;
return;
@@ -795,6 +794,13 @@ static int cpu_pm_pmu_notify(struct notifier_block *b, unsigned long cmd,
.cpu = smp_processor_id(),
};
+ /*
+ * Always reset the PMU registers on power-up even if
+ * there are no events running.
+ */
+ if (cmd == CPU_PM_EXIT && data.armpmu->reset)
+ data.armpmu->reset(data.armpmu);
+
cpu_pm_pmu_common(&data);
return data.ret;
}
@@ -824,6 +830,7 @@ static inline void cpu_pm_pmu_common(void *info) { }
static int cpu_pmu_notify(struct notifier_block *b, unsigned long action,
void *hcpu)
{
+ int irq = -1;
unsigned long masked_action = (action & ~CPU_TASKS_FROZEN);
struct cpu_pm_pmu_args data = {
.armpmu = container_of(b, struct arm_pmu, hotplug_nb),
@@ -835,37 +842,37 @@ static int cpu_pmu_notify(struct notifier_block *b, unsigned long action,
switch (masked_action) {
case CPU_STARTING:
- data.cmd = CPU_PM_EXIT;
- break;
- case CPU_DYING:
- data.cmd = CPU_PM_ENTER;
- break;
case CPU_DOWN_FAILED:
- data.cmd = CPU_PM_ENTER_FAILED;
- break;
- case CPU_ONLINE:
- if (data.armpmu->plat_device) {
- struct platform_device *pmu_device =
- data.armpmu->plat_device;
- int irq = platform_get_irq(pmu_device, 0);
-
- if (irq >= 0 && irq_is_percpu(irq)) {
- smp_call_function_single(data.cpu,
- cpu_pmu_enable_percpu_irq, &irq, 1);
- }
+ /*
+ * Always reset the PMU registers on power-up even if
+ * there are no events running.
+ */
+ if (data.armpmu->reset)
+ data.armpmu->reset(data.armpmu);
+ if (data.armpmu->pmu_state == ARM_PMU_STATE_RUNNING) {
+ if (data.armpmu->plat_device)
+ irq = data.armpmu->percpu_irq;
+ /* Arm the PMU IRQ before appearing. */
+ if (irq > 0 && irq_is_percpu(irq))
+ cpu_pmu_enable_percpu_irq(&irq);
+ data.cmd = CPU_PM_EXIT;
+ cpu_pm_pmu_common(&data);
}
- return NOTIFY_DONE;
+ return NOTIFY_OK;
+ case CPU_DYING:
+ if (data.armpmu->pmu_state != ARM_PMU_STATE_OFF) {
+ data.cmd = CPU_PM_ENTER;
+ cpu_pm_pmu_common(&data);
+ /* Disarm the PMU IRQ before disappearing. */
+ if (data.armpmu->plat_device)
+ irq = data.armpmu->percpu_irq;
+ if (irq > 0 && irq_is_percpu(irq))
+ cpu_pmu_disable_percpu_irq(&irq);
+ }
+ return NOTIFY_OK;
default:
return NOTIFY_DONE;
}
-
- if (smp_processor_id() == data.cpu)
- cpu_pm_pmu_common(&data);
- else
- smp_call_function_single(data.cpu,
- cpu_pm_pmu_common, &data, 1);
-
- return data.ret;
}
static int cpu_pmu_init(struct arm_pmu *cpu_pmu)
@@ -966,7 +973,7 @@ static int of_pmu_irq_cfg(struct arm_pmu *pmu)
/* Check the IRQ type and prohibit a mix of PPIs and SPIs */
irq = platform_get_irq(pdev, i);
- if (irq >= 0) {
+ if (irq > 0) {
bool spi = !irq_is_percpu(irq);
if (i > 0 && spi != using_spi) {
@@ -1085,6 +1092,9 @@ int arm_pmu_device_probe(struct platform_device *pdev,
if (ret)
goto out_destroy;
+ pmu->pmu_state = ARM_PMU_STATE_OFF;
+ pmu->percpu_irq = -1;
+
pr_info("enabled with %s PMU driver, %d counters available\n",
pmu->name, pmu->num_events);
diff --git a/drivers/platform/msm/ipa/ipa_v2/ipa.c b/drivers/platform/msm/ipa/ipa_v2/ipa.c
index 100bbd582a5e..f01743d04e84 100644
--- a/drivers/platform/msm/ipa/ipa_v2/ipa.c
+++ b/drivers/platform/msm/ipa/ipa_v2/ipa.c
@@ -1584,6 +1584,7 @@ static int ipa_init_smem_region(int memory_region_size,
struct ipa_hw_imm_cmd_dma_shared_mem *cmd = NULL;
struct ipa_desc desc;
struct ipa_mem_buffer mem;
+ gfp_t flag = GFP_KERNEL | (ipa_ctx->use_dma_zone ? GFP_DMA : 0);
int rc;
if (memory_region_size == 0)
@@ -1603,7 +1604,7 @@ static int ipa_init_smem_region(int memory_region_size,
memset(mem.base, 0, mem.size);
cmd = kzalloc(sizeof(*cmd),
- GFP_KERNEL);
+ flag);
if (cmd == NULL) {
IPAERR("Failed to alloc immediate command object\n");
rc = -ENOMEM;
@@ -2166,6 +2167,7 @@ int _ipa_init_sram_v2(void)
struct ipa_hw_imm_cmd_dma_shared_mem *cmd = NULL;
struct ipa_desc desc = {0};
struct ipa_mem_buffer mem;
+ gfp_t flag = GFP_KERNEL | (ipa_ctx->use_dma_zone ? GFP_DMA : 0);
int rc = 0;
phys_addr = ipa_ctx->ipa_wrapper_base +
@@ -2203,7 +2205,7 @@ int _ipa_init_sram_v2(void)
}
memset(mem.base, 0, mem.size);
- cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
+ cmd = kzalloc(sizeof(*cmd), flag);
if (cmd == NULL) {
IPAERR("Failed to alloc immediate command object\n");
rc = -ENOMEM;
@@ -2314,6 +2316,7 @@ int _ipa_init_hdr_v2(void)
struct ipa_desc desc = { 0 };
struct ipa_mem_buffer mem;
struct ipa_hdr_init_local *cmd = NULL;
+ gfp_t flag = GFP_KERNEL | (ipa_ctx->use_dma_zone ? GFP_DMA : 0);
int rc = 0;
mem.size = IPA_MEM_PART(modem_hdr_size) + IPA_MEM_PART(apps_hdr_size);
@@ -2325,7 +2328,7 @@ int _ipa_init_hdr_v2(void)
}
memset(mem.base, 0, mem.size);
- cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
+ cmd = kzalloc(sizeof(*cmd), flag);
if (cmd == NULL) {
IPAERR("Failed to alloc header init command object\n");
rc = -ENOMEM;
@@ -2360,6 +2363,7 @@ int _ipa_init_hdr_v2_5(void)
struct ipa_mem_buffer mem;
struct ipa_hdr_init_local *cmd = NULL;
struct ipa_hw_imm_cmd_dma_shared_mem *dma_cmd = NULL;
+ gfp_t flag = GFP_KERNEL | (ipa_ctx->use_dma_zone ? GFP_DMA : 0);
mem.size = IPA_MEM_PART(modem_hdr_size) + IPA_MEM_PART(apps_hdr_size);
mem.base = dma_alloc_coherent(ipa_ctx->pdev, mem.size, &mem.phys_base,
@@ -2370,7 +2374,7 @@ int _ipa_init_hdr_v2_5(void)
}
memset(mem.base, 0, mem.size);
- cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
+ cmd = kzalloc(sizeof(*cmd), flag);
if (cmd == NULL) {
IPAERR("Failed to alloc header init command object\n");
dma_free_coherent(ipa_ctx->pdev, mem.size, mem.base,
@@ -2411,7 +2415,7 @@ int _ipa_init_hdr_v2_5(void)
memset(mem.base, 0, mem.size);
memset(&desc, 0, sizeof(desc));
- dma_cmd = kzalloc(sizeof(*dma_cmd), GFP_KERNEL);
+ dma_cmd = kzalloc(sizeof(*dma_cmd), flag);
if (dma_cmd == NULL) {
IPAERR("Failed to alloc immediate command object\n");
dma_free_coherent(ipa_ctx->pdev,
@@ -2462,6 +2466,7 @@ int _ipa_init_rt4_v2(void)
struct ipa_desc desc = { 0 };
struct ipa_mem_buffer mem;
struct ipa_ip_v4_routing_init *v4_cmd = NULL;
+ gfp_t flag = GFP_KERNEL | (ipa_ctx->use_dma_zone ? GFP_DMA : 0);
u32 *entry;
int i;
int rc = 0;
@@ -2486,7 +2491,7 @@ int _ipa_init_rt4_v2(void)
entry++;
}
- v4_cmd = kzalloc(sizeof(*v4_cmd), GFP_KERNEL);
+ v4_cmd = kzalloc(sizeof(*v4_cmd), flag);
if (v4_cmd == NULL) {
IPAERR("Failed to alloc v4 routing init command object\n");
rc = -ENOMEM;
@@ -2522,6 +2527,7 @@ int _ipa_init_rt6_v2(void)
struct ipa_desc desc = { 0 };
struct ipa_mem_buffer mem;
struct ipa_ip_v6_routing_init *v6_cmd = NULL;
+ gfp_t flag = GFP_KERNEL | (ipa_ctx->use_dma_zone ? GFP_DMA : 0);
u32 *entry;
int i;
int rc = 0;
@@ -2546,7 +2552,7 @@ int _ipa_init_rt6_v2(void)
entry++;
}
- v6_cmd = kzalloc(sizeof(*v6_cmd), GFP_KERNEL);
+ v6_cmd = kzalloc(sizeof(*v6_cmd), flag);
if (v6_cmd == NULL) {
IPAERR("Failed to alloc v6 routing init command object\n");
rc = -ENOMEM;
@@ -2582,6 +2588,7 @@ int _ipa_init_flt4_v2(void)
struct ipa_desc desc = { 0 };
struct ipa_mem_buffer mem;
struct ipa_ip_v4_filter_init *v4_cmd = NULL;
+ gfp_t flag = GFP_KERNEL | (ipa_ctx->use_dma_zone ? GFP_DMA : 0);
u32 *entry;
int i;
int rc = 0;
@@ -2604,7 +2611,7 @@ int _ipa_init_flt4_v2(void)
entry++;
}
- v4_cmd = kzalloc(sizeof(*v4_cmd), GFP_KERNEL);
+ v4_cmd = kzalloc(sizeof(*v4_cmd), flag);
if (v4_cmd == NULL) {
IPAERR("Failed to alloc v4 fliter init command object\n");
rc = -ENOMEM;
@@ -2640,6 +2647,7 @@ int _ipa_init_flt6_v2(void)
struct ipa_desc desc = { 0 };
struct ipa_mem_buffer mem;
struct ipa_ip_v6_filter_init *v6_cmd = NULL;
+ gfp_t flag = GFP_KERNEL | (ipa_ctx->use_dma_zone ? GFP_DMA : 0);
u32 *entry;
int i;
int rc = 0;
@@ -2662,7 +2670,7 @@ int _ipa_init_flt6_v2(void)
entry++;
}
- v6_cmd = kzalloc(sizeof(*v6_cmd), GFP_KERNEL);
+ v6_cmd = kzalloc(sizeof(*v6_cmd), flag);
if (v6_cmd == NULL) {
IPAERR("Failed to alloc v6 fliter init command object\n");
rc = -ENOMEM;
diff --git a/drivers/platform/msm/ipa/ipa_v2/ipa_dp.c b/drivers/platform/msm/ipa/ipa_v2/ipa_dp.c
index 25364e8efa38..2fdb20d99ce2 100644
--- a/drivers/platform/msm/ipa/ipa_v2/ipa_dp.c
+++ b/drivers/platform/msm/ipa/ipa_v2/ipa_dp.c
@@ -322,8 +322,8 @@ int ipa_send_one(struct ipa_sys_context *sys, struct ipa_desc *desc,
dma_address = desc->dma_address;
tx_pkt->no_unmap_dma = true;
}
- if (!dma_address) {
- IPAERR("failed to DMA wrap\n");
+ if (dma_mapping_error(ipa_ctx->pdev, dma_address)) {
+ IPAERR("dma_map_single failed\n");
goto fail_dma_map;
}
@@ -445,7 +445,7 @@ int ipa_send(struct ipa_sys_context *sys, u32 num_desc, struct ipa_desc *desc,
}
dma_addr = dma_map_single(ipa_ctx->pdev,
transfer.iovec, size, DMA_TO_DEVICE);
- if (!dma_addr) {
+ if (dma_mapping_error(ipa_ctx->pdev, dma_addr)) {
IPAERR("dma_map_single failed for sps xfr buff\n");
kfree(transfer.iovec);
return -EFAULT;
@@ -493,6 +493,15 @@ int ipa_send(struct ipa_sys_context *sys, u32 num_desc, struct ipa_desc *desc,
tx_pkt->mem.base,
tx_pkt->mem.size,
DMA_TO_DEVICE);
+
+ if (dma_mapping_error(ipa_ctx->pdev,
+ tx_pkt->mem.phys_base)) {
+ IPAERR("dma_map_single ");
+ IPAERR("failed\n");
+ fail_dma_wrap = 1;
+ goto failure;
+ }
+
} else {
tx_pkt->mem.phys_base = desc[i].dma_address;
tx_pkt->no_unmap_dma = true;
@@ -1874,8 +1883,8 @@ begin:
rx_pkt->data.dma_addr = dma_map_single(ipa_ctx->pdev, ptr,
sys->rx_buff_sz,
DMA_FROM_DEVICE);
- if (rx_pkt->data.dma_addr == 0 ||
- rx_pkt->data.dma_addr == ~0) {
+ if (dma_mapping_error(ipa_ctx->pdev,
+ rx_pkt->data.dma_addr)) {
pr_err_ratelimited("%s dma map fail %p for %p sys=%p\n",
__func__, (void *)rx_pkt->data.dma_addr,
ptr, sys);
@@ -2030,8 +2039,8 @@ static void ipa_alloc_wlan_rx_common_cache(u32 size)
ptr = skb_put(rx_pkt->data.skb, IPA_WLAN_RX_BUFF_SZ);
rx_pkt->data.dma_addr = dma_map_single(ipa_ctx->pdev, ptr,
IPA_WLAN_RX_BUFF_SZ, DMA_FROM_DEVICE);
- if (rx_pkt->data.dma_addr == 0 ||
- rx_pkt->data.dma_addr == ~0) {
+ if (dma_mapping_error(ipa_ctx->pdev,
+ rx_pkt->data.dma_addr)) {
IPAERR("dma_map_single failure %p for %p\n",
(void *)rx_pkt->data.dma_addr, ptr);
goto fail_dma_mapping;
@@ -2102,8 +2111,8 @@ static void ipa_replenish_rx_cache(struct ipa_sys_context *sys)
rx_pkt->data.dma_addr = dma_map_single(ipa_ctx->pdev, ptr,
sys->rx_buff_sz,
DMA_FROM_DEVICE);
- if (rx_pkt->data.dma_addr == 0 ||
- rx_pkt->data.dma_addr == ~0) {
+ if (dma_mapping_error(ipa_ctx->pdev,
+ rx_pkt->data.dma_addr)) {
IPAERR("dma_map_single failure %p for %p\n",
(void *)rx_pkt->data.dma_addr, ptr);
goto fail_dma_mapping;
@@ -2160,9 +2169,10 @@ static void ipa_replenish_rx_cache_recycle(struct ipa_sys_context *sys)
ptr = skb_put(rx_pkt->data.skb, sys->rx_buff_sz);
rx_pkt->data.dma_addr = dma_map_single(ipa_ctx->pdev,
ptr, sys->rx_buff_sz, DMA_FROM_DEVICE);
- if (rx_pkt->data.dma_addr == 0 ||
- rx_pkt->data.dma_addr == ~0)
+ if (dma_mapping_error(ipa_ctx->pdev, rx_pkt->data.dma_addr)) {
+ IPAERR("dma_map_single failure for rx_pkt\n");
goto fail_dma_mapping;
+ }
list_add_tail(&rx_pkt->link, &sys->head_desc_list);
rx_len_cached = ++sys->len;
diff --git a/drivers/platform/msm/ipa/ipa_v2/ipa_hdr.c b/drivers/platform/msm/ipa/ipa_v2/ipa_hdr.c
index e23de3f26613..f43981f15c31 100644
--- a/drivers/platform/msm/ipa/ipa_v2/ipa_hdr.c
+++ b/drivers/platform/msm/ipa/ipa_v2/ipa_hdr.c
@@ -268,6 +268,7 @@ int __ipa_commit_hdr_v2(void)
struct ipa_mem_buffer mem;
struct ipa_hdr_init_system *cmd = NULL;
struct ipa_hw_imm_cmd_dma_shared_mem *dma_cmd = NULL;
+ gfp_t flag = GFP_ATOMIC | (ipa_ctx->use_dma_zone ? GFP_DMA : 0);
int rc = -EFAULT;
if (ipa_generate_hdr_hw_tbl(&mem)) {
@@ -281,7 +282,7 @@ int __ipa_commit_hdr_v2(void)
IPA_MEM_PART(apps_hdr_size));
goto fail_send_cmd;
} else {
- dma_cmd = kzalloc(sizeof(*dma_cmd), GFP_ATOMIC);
+ dma_cmd = kzalloc(sizeof(*dma_cmd), flag);
if (dma_cmd == NULL) {
IPAERR("fail to alloc immediate cmd\n");
rc = -ENOMEM;
@@ -303,7 +304,7 @@ int __ipa_commit_hdr_v2(void)
IPA_MEM_PART(apps_hdr_size_ddr));
goto fail_send_cmd;
} else {
- cmd = kzalloc(sizeof(*cmd), GFP_ATOMIC);
+ cmd = kzalloc(sizeof(*cmd), flag);
if (cmd == NULL) {
IPAERR("fail to alloc hdr init cmd\n");
rc = -ENOMEM;
@@ -359,6 +360,7 @@ int __ipa_commit_hdr_v2_5(void)
struct ipa_hw_imm_cmd_dma_shared_mem *dma_cmd_hdr = NULL;
struct ipa_hw_imm_cmd_dma_shared_mem *dma_cmd_ctx = NULL;
struct ipa_register_write *reg_write_cmd = NULL;
+ gfp_t flag = GFP_ATOMIC | (ipa_ctx->use_dma_zone ? GFP_DMA : 0);
int rc = -EFAULT;
u32 proc_ctx_size;
u32 proc_ctx_ofst;
@@ -383,7 +385,7 @@ int __ipa_commit_hdr_v2_5(void)
IPA_MEM_PART(apps_hdr_size));
goto fail_send_cmd1;
} else {
- dma_cmd_hdr = kzalloc(sizeof(*dma_cmd_hdr), GFP_ATOMIC);
+ dma_cmd_hdr = kzalloc(sizeof(*dma_cmd_hdr), flag);
if (dma_cmd_hdr == NULL) {
IPAERR("fail to alloc immediate cmd\n");
rc = -ENOMEM;
@@ -406,7 +408,7 @@ int __ipa_commit_hdr_v2_5(void)
goto fail_send_cmd1;
} else {
hdr_init_cmd = kzalloc(sizeof(*hdr_init_cmd),
- GFP_ATOMIC);
+ flag);
if (hdr_init_cmd == NULL) {
IPAERR("fail to alloc immediate cmd\n");
rc = -ENOMEM;
@@ -431,7 +433,7 @@ int __ipa_commit_hdr_v2_5(void)
goto fail_send_cmd1;
} else {
dma_cmd_ctx = kzalloc(sizeof(*dma_cmd_ctx),
- GFP_ATOMIC);
+ flag);
if (dma_cmd_ctx == NULL) {
IPAERR("fail to alloc immediate cmd\n");
rc = -ENOMEM;
@@ -456,7 +458,7 @@ int __ipa_commit_hdr_v2_5(void)
goto fail_send_cmd1;
} else {
reg_write_cmd = kzalloc(sizeof(*reg_write_cmd),
- GFP_ATOMIC);
+ flag);
if (reg_write_cmd == NULL) {
IPAERR("fail to alloc immediate cmd\n");
rc = -ENOMEM;
@@ -722,6 +724,11 @@ static int __ipa_add_hdr(struct ipa_hdr_add *hdr)
entry->hdr,
entry->hdr_len,
DMA_TO_DEVICE);
+ if (dma_mapping_error(ipa_ctx->pdev,
+ entry->phys_base)) {
+ IPAERR("dma_map_single failure for entry\n");
+ goto fail_dma_mapping;
+ }
}
} else {
entry->is_hdr_proc_ctx = false;
@@ -798,6 +805,8 @@ fail_add_proc_ctx:
list_del(&entry->link);
dma_unmap_single(ipa_ctx->pdev, entry->phys_base,
entry->hdr_len, DMA_TO_DEVICE);
+fail_dma_mapping:
+ entry->is_hdr_proc_ctx = false;
bad_hdr_len:
entry->cookie = 0;
kmem_cache_free(ipa_ctx->hdr_cache, entry);
diff --git a/drivers/platform/msm/ipa/ipa_v2/ipa_rt.c b/drivers/platform/msm/ipa/ipa_v2/ipa_rt.c
index f5dea76764f8..11c77934e04f 100644
--- a/drivers/platform/msm/ipa/ipa_v2/ipa_rt.c
+++ b/drivers/platform/msm/ipa/ipa_v2/ipa_rt.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012-2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2012-2017, 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
@@ -698,6 +698,7 @@ int __ipa_commit_rt_v2(enum ipa_ip_type ip)
struct ipa_mem_buffer head;
struct ipa_hw_imm_cmd_dma_shared_mem *cmd1 = NULL;
struct ipa_hw_imm_cmd_dma_shared_mem *cmd2 = NULL;
+ gfp_t flag = GFP_KERNEL | (ipa_ctx->use_dma_zone ? GFP_DMA : 0);
u16 avail;
u32 num_modem_rt_index;
int rc = 0;
@@ -748,7 +749,7 @@ int __ipa_commit_rt_v2(enum ipa_ip_type ip)
}
cmd1 = kzalloc(sizeof(struct ipa_hw_imm_cmd_dma_shared_mem),
- GFP_KERNEL);
+ flag);
if (cmd1 == NULL) {
IPAERR("Failed to alloc immediate command object\n");
rc = -ENOMEM;
@@ -765,7 +766,7 @@ int __ipa_commit_rt_v2(enum ipa_ip_type ip)
if (lcl) {
cmd2 = kzalloc(sizeof(struct ipa_hw_imm_cmd_dma_shared_mem),
- GFP_KERNEL);
+ flag);
if (cmd2 == NULL) {
IPAERR("Failed to alloc immediate command object\n");
rc = -ENOMEM;
diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa.c b/drivers/platform/msm/ipa/ipa_v3/ipa.c
index ddff50834f03..5ee6e5d2d9e3 100644
--- a/drivers/platform/msm/ipa/ipa_v3/ipa.c
+++ b/drivers/platform/msm/ipa/ipa_v3/ipa.c
@@ -2276,6 +2276,36 @@ static int ipa3_q6_set_ex_path_to_apps(void)
desc[num_descs].len = cmd_pyld->len;
num_descs++;
}
+
+ /* disable statuses for modem producers */
+ if (IPA_CLIENT_IS_Q6_PROD(client_idx)) {
+ ipa_assert_on(num_descs >= ipa3_ctx->ipa_num_pipes);
+
+ reg_write.skip_pipeline_clear = false;
+ reg_write.pipeline_clear_options =
+ IPAHAL_HPS_CLEAR;
+ reg_write.offset =
+ ipahal_get_reg_n_ofst(IPA_ENDP_STATUS_n,
+ ep_idx);
+ reg_write.value = 0;
+ reg_write.value_mask = ~0;
+ cmd_pyld = ipahal_construct_imm_cmd(
+ IPA_IMM_CMD_REGISTER_WRITE, &reg_write, false);
+ if (!cmd_pyld) {
+ IPAERR("fail construct register_write cmd\n");
+ ipa_assert();
+ return -EFAULT;
+ }
+
+ desc[num_descs].opcode = ipahal_imm_cmd_get_opcode(
+ IPA_IMM_CMD_REGISTER_WRITE);
+ desc[num_descs].type = IPA_IMM_CMD_DESC;
+ desc[num_descs].callback = ipa3_destroy_imm;
+ desc[num_descs].user1 = cmd_pyld;
+ desc[num_descs].pyld = cmd_pyld->data;
+ desc[num_descs].len = cmd_pyld->len;
+ num_descs++;
+ }
}
/* Will wait 500msecs for IPA tag process completion */
@@ -4036,6 +4066,7 @@ fail_register_device:
unregister_chrdev_region(ipa3_ctx->dev_num, 1);
if (ipa3_ctx->pipe_mem_pool)
gen_pool_destroy(ipa3_ctx->pipe_mem_pool);
+ ipa3_free_dma_task_for_gsi();
ipa3_destroy_flt_tbl_idrs();
idr_destroy(&ipa3_ctx->ipa_idr);
kmem_cache_destroy(ipa3_ctx->rx_pkt_wrapper_cache);
@@ -4551,6 +4582,13 @@ static int ipa3_pre_init(const struct ipa3_plat_drv_res *resource_p,
goto fail_dma_pool;
}
+ /* allocate memory for DMA_TASK workaround */
+ result = ipa3_allocate_dma_task_for_gsi();
+ if (result) {
+ IPAERR("failed to allocate dma task\n");
+ goto fail_dma_task;
+ }
+
/* init the various list heads */
INIT_LIST_HEAD(&ipa3_ctx->hdr_tbl.head_hdr_entry_list);
for (i = 0; i < IPA_HDR_BIN_MAX; i++) {
@@ -4723,6 +4761,8 @@ fail_cdev_add:
fail_device_create:
unregister_chrdev_region(ipa3_ctx->dev_num, 1);
fail_alloc_chrdev_region:
+ ipa3_free_dma_task_for_gsi();
+fail_dma_task:
if (ipa3_ctx->pipe_mem_pool)
gen_pool_destroy(ipa3_ctx->pipe_mem_pool);
ipa3_destroy_flt_tbl_idrs();
diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_i.h b/drivers/platform/msm/ipa/ipa_v3/ipa_i.h
index 0cf77bbde496..ac7ef6a21952 100644
--- a/drivers/platform/msm/ipa/ipa_v3/ipa_i.h
+++ b/drivers/platform/msm/ipa/ipa_v3/ipa_i.h
@@ -1027,6 +1027,11 @@ struct ipa_tz_unlock_reg_info {
u32 size;
};
+struct ipa_dma_task_info {
+ struct ipa_mem_buffer mem;
+ struct ipahal_imm_cmd_pyld *cmd_pyld;
+};
+
/**
* struct ipa3_context - IPA context
* @class: pointer to the struct class
@@ -1246,6 +1251,7 @@ struct ipa3_context {
struct ipa3_smp2p_info smp2p_info;
u32 ipa_tz_unlock_reg_num;
struct ipa_tz_unlock_reg_info *ipa_tz_unlock_reg;
+ struct ipa_dma_task_info dma_task_info;
};
/**
@@ -2053,4 +2059,6 @@ int ipa3_get_ntn_stats(struct Ipa3HwStatsNTNInfoData_t *stats);
struct dentry *ipa_debugfs_get_root(void);
bool ipa3_is_msm_device(void);
struct device *ipa3_get_pdev(void);
+int ipa3_allocate_dma_task_for_gsi(void);
+void ipa3_free_dma_task_for_gsi(void);
#endif /* _IPA3_I_H_ */
diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_utils.c b/drivers/platform/msm/ipa/ipa_v3/ipa_utils.c
index 14735787cb9c..f4a7319ca290 100644
--- a/drivers/platform/msm/ipa/ipa_v3/ipa_utils.c
+++ b/drivers/platform/msm/ipa/ipa_v3/ipa_utils.c
@@ -3483,6 +3483,51 @@ void ipa3_suspend_apps_pipes(bool suspend)
}
}
+int ipa3_allocate_dma_task_for_gsi(void)
+{
+ struct ipahal_imm_cmd_dma_task_32b_addr cmd = { 0 };
+
+ IPADBG("Allocate mem\n");
+ ipa3_ctx->dma_task_info.mem.size = IPA_GSI_CHANNEL_STOP_PKT_SIZE;
+ ipa3_ctx->dma_task_info.mem.base = dma_alloc_coherent(ipa3_ctx->pdev,
+ ipa3_ctx->dma_task_info.mem.size,
+ &ipa3_ctx->dma_task_info.mem.phys_base,
+ GFP_KERNEL);
+ if (!ipa3_ctx->dma_task_info.mem.base) {
+ IPAERR("no mem\n");
+ return -EFAULT;
+ }
+
+ cmd.flsh = 1;
+ cmd.size1 = ipa3_ctx->dma_task_info.mem.size;
+ cmd.addr1 = ipa3_ctx->dma_task_info.mem.phys_base;
+ cmd.packet_size = ipa3_ctx->dma_task_info.mem.size;
+ ipa3_ctx->dma_task_info.cmd_pyld = ipahal_construct_imm_cmd(
+ IPA_IMM_CMD_DMA_TASK_32B_ADDR, &cmd, false);
+ if (!ipa3_ctx->dma_task_info.cmd_pyld) {
+ IPAERR("failed to construct dma_task_32b_addr cmd\n");
+ dma_free_coherent(ipa3_ctx->pdev,
+ ipa3_ctx->dma_task_info.mem.size,
+ ipa3_ctx->dma_task_info.mem.base,
+ ipa3_ctx->dma_task_info.mem.phys_base);
+ memset(&ipa3_ctx->dma_task_info, 0,
+ sizeof(ipa3_ctx->dma_task_info));
+ return -EFAULT;
+ }
+
+ return 0;
+}
+
+void ipa3_free_dma_task_for_gsi(void)
+{
+ dma_free_coherent(ipa3_ctx->pdev,
+ ipa3_ctx->dma_task_info.mem.size,
+ ipa3_ctx->dma_task_info.mem.base,
+ ipa3_ctx->dma_task_info.mem.phys_base);
+ ipahal_destroy_imm_cmd(ipa3_ctx->dma_task_info.cmd_pyld);
+ memset(&ipa3_ctx->dma_task_info, 0, sizeof(ipa3_ctx->dma_task_info));
+}
+
/**
* ipa3_inject_dma_task_for_gsi()- Send DMA_TASK to IPA for GSI stop channel
*
@@ -3491,41 +3536,12 @@ void ipa3_suspend_apps_pipes(bool suspend)
*/
int ipa3_inject_dma_task_for_gsi(void)
{
- static struct ipa_mem_buffer mem = {0};
- struct ipahal_imm_cmd_dma_task_32b_addr cmd = {0};
- static struct ipahal_imm_cmd_pyld *cmd_pyld;
struct ipa3_desc desc = {0};
- /* allocate the memory only for the very first time */
- if (!mem.base) {
- IPADBG("Allocate mem\n");
- mem.size = IPA_GSI_CHANNEL_STOP_PKT_SIZE;
- mem.base = dma_alloc_coherent(ipa3_ctx->pdev,
- mem.size,
- &mem.phys_base,
- GFP_KERNEL);
- if (!mem.base) {
- IPAERR("no mem\n");
- return -EFAULT;
- }
- }
- if (!cmd_pyld) {
- cmd.flsh = 1;
- cmd.size1 = mem.size;
- cmd.addr1 = mem.phys_base;
- cmd.packet_size = mem.size;
- cmd_pyld = ipahal_construct_imm_cmd(
- IPA_IMM_CMD_DMA_TASK_32B_ADDR, &cmd, false);
- if (!cmd_pyld) {
- IPAERR("failed to construct dma_task_32b_addr cmd\n");
- return -EFAULT;
- }
- }
-
desc.opcode = ipahal_imm_cmd_get_opcode_param(
IPA_IMM_CMD_DMA_TASK_32B_ADDR, 1);
- desc.pyld = cmd_pyld->data;
- desc.len = cmd_pyld->len;
+ desc.pyld = ipa3_ctx->dma_task_info.cmd_pyld->data;
+ desc.len = ipa3_ctx->dma_task_info.cmd_pyld->len;
desc.type = IPA_IMM_CMD_DESC;
IPADBG("sending 1B packet to IPA\n");
diff --git a/drivers/platform/msm/msm_11ad/msm_11ad.c b/drivers/platform/msm/msm_11ad/msm_11ad.c
index 406b5abae5dc..f1e348969c7b 100644
--- a/drivers/platform/msm/msm_11ad/msm_11ad.c
+++ b/drivers/platform/msm/msm_11ad/msm_11ad.c
@@ -643,6 +643,9 @@ static int msm_11ad_smmu_init(struct msm11ad_ctx *ctx)
int rc;
int force_pt_coherent = 1;
int smmu_bypass = !ctx->smmu_s1_en;
+ dma_addr_t iova_base = 0;
+ dma_addr_t iova_end = ctx->smmu_base + ctx->smmu_size - 1;
+ struct iommu_domain_geometry geometry;
if (!ctx->use_smmu)
return 0;
@@ -700,6 +703,17 @@ static int msm_11ad_smmu_init(struct msm11ad_ctx *ctx)
rc);
goto release_mapping;
}
+ memset(&geometry, 0, sizeof(geometry));
+ geometry.aperture_start = iova_base;
+ geometry.aperture_end = iova_end;
+ rc = iommu_domain_set_attr(ctx->mapping->domain,
+ DOMAIN_ATTR_GEOMETRY,
+ &geometry);
+ if (rc) {
+ dev_err(ctx->dev, "Set geometry attribute to SMMU failed (%d)\n",
+ rc);
+ goto release_mapping;
+ }
}
}
@@ -871,6 +885,8 @@ static int msm_11ad_ssr_init(struct msm11ad_ctx *ctx)
ctx->dump_data.addr = virt_to_phys(ctx->ramdump_addr);
ctx->dump_data.len = WIGIG_RAMDUMP_SIZE;
+ strlcpy(ctx->dump_data.name, "KWIGIG",
+ sizeof(ctx->dump_data.name));
dump_entry.id = MSM_DUMP_DATA_WIGIG;
dump_entry.addr = virt_to_phys(&ctx->dump_data);
diff --git a/drivers/power/reset/msm-poweroff.c b/drivers/power/reset/msm-poweroff.c
index a30ed90d6e92..8d038ba0770d 100644
--- a/drivers/power/reset/msm-poweroff.c
+++ b/drivers/power/reset/msm-poweroff.c
@@ -33,6 +33,7 @@
#include <soc/qcom/scm.h>
#include <soc/qcom/restart.h>
#include <soc/qcom/watchdog.h>
+#include <soc/qcom/minidump.h>
#define EMERGENCY_DLOAD_MAGIC1 0x322A4F99
#define EMERGENCY_DLOAD_MAGIC2 0xC67E4350
@@ -42,9 +43,10 @@
#define SCM_IO_DISABLE_PMIC_ARBITER 1
#define SCM_IO_DEASSERT_PS_HOLD 2
#define SCM_WDOG_DEBUG_BOOT_PART 0x9
-#define SCM_DLOAD_MODE 0X10
+#define SCM_DLOAD_FULLDUMP 0X10
#define SCM_EDLOAD_MODE 0X01
#define SCM_DLOAD_CMD 0x10
+#define SCM_DLOAD_MINIDUMP 0X20
static int restart_mode;
@@ -69,6 +71,7 @@ static void scm_disable_sdi(void);
#endif
static int in_panic;
+static int dload_type = SCM_DLOAD_FULLDUMP;
static int download_mode = 1;
static struct kobject dload_kobj;
static void *dload_mode_addr, *dload_type_addr;
@@ -142,7 +145,7 @@ static void set_dload_mode(int on)
mb();
}
- ret = scm_set_dload_mode(on ? SCM_DLOAD_MODE : 0, 0);
+ ret = scm_set_dload_mode(on ? dload_type : 0, 0);
if (ret)
pr_err("Failed to set secure DLOAD mode: %d\n", ret);
@@ -185,7 +188,6 @@ static int dload_set(const char *val, struct kernel_param *kp)
int old_val = download_mode;
ret = param_set_int(val, kp);
-
if (ret)
return ret;
@@ -454,7 +456,7 @@ static ssize_t show_emmc_dload(struct kobject *kobj, struct attribute *attr,
else
show_val = 0;
- return snprintf(buf, sizeof(show_val), "%u\n", show_val);
+ return scnprintf(buf, sizeof(show_val), "%u\n", show_val);
}
static size_t store_emmc_dload(struct kobject *kobj, struct attribute *attr,
@@ -477,10 +479,50 @@ static size_t store_emmc_dload(struct kobject *kobj, struct attribute *attr,
return count;
}
+
+#ifdef CONFIG_QCOM_MINIDUMP
+
+static DEFINE_MUTEX(tcsr_lock);
+
+static ssize_t show_dload_mode(struct kobject *kobj, struct attribute *attr,
+ char *buf)
+{
+ return scnprintf(buf, PAGE_SIZE, "DLOAD dump type: %s\n",
+ (dload_type == SCM_DLOAD_MINIDUMP) ? "mini" : "full");
+}
+
+static size_t store_dload_mode(struct kobject *kobj, struct attribute *attr,
+ const char *buf, size_t count)
+{
+ if (sysfs_streq(buf, "full")) {
+ dload_type = SCM_DLOAD_FULLDUMP;
+ } else if (sysfs_streq(buf, "mini")) {
+ if (!msm_minidump_enabled()) {
+ pr_info("Minidump is not enabled\n");
+ return -ENODEV;
+ }
+ dload_type = SCM_DLOAD_MINIDUMP;
+ } else {
+ pr_info("Invalid value. Use 'full' or 'mini'\n");
+ return -EINVAL;
+ }
+
+ mutex_lock(&tcsr_lock);
+ /*Overwrite TCSR reg*/
+ set_dload_mode(dload_type);
+ mutex_unlock(&tcsr_lock);
+ return count;
+}
+RESET_ATTR(dload_mode, 0644, show_dload_mode, store_dload_mode);
+#endif
+
RESET_ATTR(emmc_dload, 0644, show_emmc_dload, store_emmc_dload);
static struct attribute *reset_attrs[] = {
&reset_attr_emmc_dload.attr,
+#ifdef CONFIG_QCOM_MINIDUMP
+ &reset_attr_dload_mode.attr,
+#endif
NULL
};
diff --git a/drivers/power/supply/qcom/battery.c b/drivers/power/supply/qcom/battery.c
index 67f9d4fafeb8..539e757d3e99 100644
--- a/drivers/power/supply/qcom/battery.c
+++ b/drivers/power/supply/qcom/battery.c
@@ -434,23 +434,28 @@ static int pl_fcc_vote_callback(struct votable *votable, void *data,
return rc;
}
- split_fcc(chip, total_fcc_ua, &master_fcc_ua, &slave_fcc_ua);
- pval.intval = slave_fcc_ua;
- rc = power_supply_set_property(chip->pl_psy,
- POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX, &pval);
- if (rc < 0) {
- pr_err("Couldn't set parallel fcc, rc=%d\n", rc);
- return rc;
- }
+ if (chip->pl_mode != POWER_SUPPLY_PL_NONE) {
+ split_fcc(chip, total_fcc_ua, &master_fcc_ua, &slave_fcc_ua);
+
+ pval.intval = slave_fcc_ua;
+ rc = power_supply_set_property(chip->pl_psy,
+ POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX,
+ &pval);
+ if (rc < 0) {
+ pr_err("Couldn't set parallel fcc, rc=%d\n", rc);
+ return rc;
+ }
- chip->slave_fcc_ua = slave_fcc_ua;
+ chip->slave_fcc_ua = slave_fcc_ua;
- pval.intval = master_fcc_ua;
- rc = power_supply_set_property(chip->main_psy,
- POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX, &pval);
- if (rc < 0) {
- pr_err("Could not set main fcc, rc=%d\n", rc);
- return rc;
+ pval.intval = master_fcc_ua;
+ rc = power_supply_set_property(chip->main_psy,
+ POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX,
+ &pval);
+ if (rc < 0) {
+ pr_err("Could not set main fcc, rc=%d\n", rc);
+ return rc;
+ }
}
pl_dbg(chip, PR_PARALLEL, "master_fcc=%d slave_fcc=%d distribution=(%d/%d)\n",
@@ -685,9 +690,6 @@ static bool is_main_available(struct pl_data *chip)
chip->main_psy = power_supply_get_by_name("main");
- if (chip->main_psy)
- rerun_election(chip->usb_icl_votable);
-
return !!chip->main_psy;
}
@@ -866,7 +868,18 @@ static void status_change_work(struct work_struct *work)
struct pl_data *chip = container_of(work,
struct pl_data, status_change_work);
- if (!is_main_available(chip))
+ if (!chip->main_psy && is_main_available(chip)) {
+ /*
+ * re-run election for FCC/FV/ICL once main_psy
+ * is available to ensure all votes are reflected
+ * on hardware
+ */
+ rerun_election(chip->usb_icl_votable);
+ rerun_election(chip->fcc_votable);
+ rerun_election(chip->fv_votable);
+ }
+
+ if (!chip->main_psy)
return;
if (!is_batt_available(chip))
diff --git a/drivers/power/supply/qcom/qpnp-qnovo.c b/drivers/power/supply/qcom/qpnp-qnovo.c
index c74dc8989821..eb97eb0ff2ac 100644
--- a/drivers/power/supply/qcom/qpnp-qnovo.c
+++ b/drivers/power/supply/qcom/qpnp-qnovo.c
@@ -89,7 +89,16 @@
#define QNOVO_STRM_CTRL 0xA8
#define QNOVO_IADC_OFFSET_OVR_VAL 0xA9
#define QNOVO_IADC_OFFSET_OVR 0xAA
+
#define QNOVO_DISABLE_CHARGING 0xAB
+#define ERR_SWITCHER_DISABLED BIT(7)
+#define ERR_JEITA_SOFT_CONDITION BIT(6)
+#define ERR_BAT_OV BIT(5)
+#define ERR_CV_MODE BIT(4)
+#define ERR_BATTERY_MISSING BIT(3)
+#define ERR_SAFETY_TIMER_EXPIRED BIT(2)
+#define ERR_CHARGING_DISABLED BIT(1)
+#define ERR_JEITA_HARD_CONDITION BIT(0)
#define QNOVO_TR_IADC_OFFSET_0 0xF1
#define QNOVO_TR_IADC_OFFSET_1 0xF2
@@ -1107,24 +1116,28 @@ static int qnovo_update_status(struct qnovo *chip)
{
u8 val = 0;
int rc;
- bool charging;
+ bool ok_to_qnovo;
bool changed = false;
rc = qnovo_read(chip, QNOVO_ERROR_STS2, &val, 1);
if (rc < 0) {
pr_err("Couldn't read error sts rc = %d\n", rc);
- charging = false;
+ ok_to_qnovo = false;
} else {
- charging = !(val & QNOVO_ERROR_CHARGING_DISABLED);
+ /*
+ * For CV mode keep qnovo enabled, userspace is expected to
+ * disable it after few runs
+ */
+ ok_to_qnovo = (val == ERR_CV_MODE || val == 0) ? true : false;
}
- if (chip->ok_to_qnovo ^ charging) {
+ if (chip->ok_to_qnovo ^ ok_to_qnovo) {
- vote(chip->disable_votable, OK_TO_QNOVO_VOTER, !charging, 0);
- if (!charging)
+ vote(chip->disable_votable, OK_TO_QNOVO_VOTER, !ok_to_qnovo, 0);
+ if (!ok_to_qnovo)
vote(chip->disable_votable, USER_VOTER, true, 0);
- chip->ok_to_qnovo = charging;
+ chip->ok_to_qnovo = ok_to_qnovo;
changed = true;
}
@@ -1247,6 +1260,16 @@ static int qnovo_hw_init(struct qnovo *chip)
chip->v_gain_mega = 1000000000 + (s64)(s8)vadc_gain * GAIN_LSB_FACTOR;
chip->v_gain_mega = div_s64(chip->v_gain_mega, 1000);
+ /* allow charger error conditions to disable qnovo, CV mode excluded */
+ val = ERR_SWITCHER_DISABLED | ERR_JEITA_SOFT_CONDITION | ERR_BAT_OV |
+ ERR_BATTERY_MISSING | ERR_SAFETY_TIMER_EXPIRED |
+ ERR_CHARGING_DISABLED | ERR_JEITA_HARD_CONDITION;
+ rc = qnovo_write(chip, QNOVO_DISABLE_CHARGING, &val, 1);
+ if (rc < 0) {
+ pr_err("Couldn't write QNOVO_DISABLE_CHARGING rc = %d\n", rc);
+ return rc;
+ }
+
return 0;
}
diff --git a/drivers/power/supply/qcom/qpnp-smb2.c b/drivers/power/supply/qcom/qpnp-smb2.c
index c7d6937bcc49..8855a1c74e0b 100644
--- a/drivers/power/supply/qcom/qpnp-smb2.c
+++ b/drivers/power/supply/qcom/qpnp-smb2.c
@@ -540,6 +540,12 @@ static int smb2_usb_set_prop(struct power_supply *psy,
struct smb_charger *chg = &chip->chg;
int rc = 0;
+ mutex_lock(&chg->lock);
+ if (!chg->typec_present) {
+ rc = -EINVAL;
+ goto unlock;
+ }
+
switch (psp) {
case POWER_SUPPLY_PROP_VOLTAGE_MIN:
rc = smblib_set_prop_usb_voltage_min(chg, val);
@@ -578,6 +584,8 @@ static int smb2_usb_set_prop(struct power_supply *psy,
break;
}
+unlock:
+ mutex_unlock(&chg->lock);
return rc;
}
@@ -1350,10 +1358,12 @@ static int smb2_configure_typec(struct smb_charger *chg)
return rc;
}
- /* disable try.SINK mode */
- rc = smblib_masked_write(chg, TYPE_C_CFG_3_REG, EN_TRYSINK_MODE_BIT, 0);
+ /* disable try.SINK mode and legacy cable IRQs */
+ rc = smblib_masked_write(chg, TYPE_C_CFG_3_REG, EN_TRYSINK_MODE_BIT |
+ TYPEC_NONCOMPLIANT_LEGACY_CABLE_INT_EN_BIT |
+ TYPEC_LEGACY_CABLE_INT_EN_BIT, 0);
if (rc < 0) {
- dev_err(chg->dev, "Couldn't set TRYSINK_MODE rc=%d\n", rc);
+ dev_err(chg->dev, "Couldn't set Type-C config rc=%d\n", rc);
return rc;
}
@@ -1509,6 +1519,8 @@ static int smb2_init_hw(struct smb2 *chip)
DEFAULT_VOTER, true, chip->dt.dc_icl_ua);
vote(chg->hvdcp_disable_votable_indirect, PD_INACTIVE_VOTER,
true, 0);
+ vote(chg->hvdcp_disable_votable_indirect, VBUS_CC_SHORT_VOTER,
+ true, 0);
vote(chg->hvdcp_disable_votable_indirect, DEFAULT_VOTER,
chip->dt.hvdcp_disable, 0);
vote(chg->pd_disallowed_votable_indirect, CC_DETACHED_VOTER,
diff --git a/drivers/power/supply/qcom/smb-lib.c b/drivers/power/supply/qcom/smb-lib.c
index e4ab41b1f16a..1e417e8aa22d 100644
--- a/drivers/power/supply/qcom/smb-lib.c
+++ b/drivers/power/supply/qcom/smb-lib.c
@@ -543,30 +543,6 @@ static void smblib_rerun_apsd(struct smb_charger *chg)
smblib_err(chg, "Couldn't re-run APSD rc=%d\n", rc);
}
-static int try_rerun_apsd_for_hvdcp(struct smb_charger *chg)
-{
- const struct apsd_result *apsd_result;
-
- /*
- * PD_INACTIVE_VOTER on hvdcp_disable_votable indicates whether
- * apsd rerun was tried earlier
- */
- if (get_client_vote(chg->hvdcp_disable_votable_indirect,
- PD_INACTIVE_VOTER)) {
- vote(chg->hvdcp_disable_votable_indirect,
- PD_INACTIVE_VOTER, false, 0);
- /* ensure hvdcp is enabled */
- if (!get_effective_result(
- chg->hvdcp_disable_votable_indirect)) {
- apsd_result = smblib_get_apsd_result(chg);
- if (apsd_result->bit & (QC_2P0_BIT | QC_3P0_BIT)) {
- smblib_rerun_apsd(chg);
- }
- }
- }
- return 0;
-}
-
static const struct apsd_result *smblib_update_usb_type(struct smb_charger *chg)
{
const struct apsd_result *apsd_result = smblib_get_apsd_result(chg);
@@ -1023,6 +999,7 @@ static int smblib_hvdcp_enable_vote_callback(struct votable *votable,
struct smb_charger *chg = data;
int rc;
u8 val = HVDCP_AUTH_ALG_EN_CFG_BIT | HVDCP_EN_BIT;
+ u8 stat;
/* vote to enable/disable HW autonomous INOV */
vote(chg->hvdcp_hw_inov_dis_votable, client, !hvdcp_enable, 0);
@@ -1044,6 +1021,16 @@ static int smblib_hvdcp_enable_vote_callback(struct votable *votable,
return rc;
}
+ rc = smblib_read(chg, APSD_STATUS_REG, &stat);
+ if (rc < 0) {
+ smblib_err(chg, "Couldn't read APSD status rc=%d\n", rc);
+ return rc;
+ }
+
+ /* re-run APSD if HVDCP was detected */
+ if (stat & QC_CHARGER_BIT)
+ smblib_rerun_apsd(chg);
+
return 0;
}
@@ -1137,6 +1124,22 @@ static int smblib_usb_irq_enable_vote_callback(struct votable *votable,
return 0;
}
+static int smblib_typec_irq_disable_vote_callback(struct votable *votable,
+ void *data, int disable, const char *client)
+{
+ struct smb_charger *chg = data;
+
+ if (!chg->irq_info[TYPE_C_CHANGE_IRQ].irq)
+ return 0;
+
+ if (disable)
+ disable_irq_nosync(chg->irq_info[TYPE_C_CHANGE_IRQ].irq);
+ else
+ enable_irq(chg->irq_info[TYPE_C_CHANGE_IRQ].irq);
+
+ return 0;
+}
+
/*******************
* VCONN REGULATOR *
* *****************/
@@ -1145,7 +1148,7 @@ static int smblib_usb_irq_enable_vote_callback(struct votable *votable,
static int _smblib_vconn_regulator_enable(struct regulator_dev *rdev)
{
struct smb_charger *chg = rdev_get_drvdata(rdev);
- u8 otg_stat, stat4;
+ u8 otg_stat, val;
int rc = 0, i;
if (!chg->external_vconn) {
@@ -1176,17 +1179,12 @@ static int _smblib_vconn_regulator_enable(struct regulator_dev *rdev)
* 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, &stat4);
- if (rc < 0) {
- smblib_err(chg, "Couldn't read TYPE_C_STATUS_4 rc=%d\n", rc);
- return rc;
- }
-
smblib_dbg(chg, PR_OTG, "enabling VCONN\n");
- stat4 = stat4 & CC_ORIENTATION_BIT ? 0 : VCONN_EN_ORIENTATION_BIT;
+ val = chg->typec_status[3] &
+ 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_ORIENTATION_BIT,
- VCONN_EN_VALUE_BIT | stat4);
+ VCONN_EN_VALUE_BIT | val);
if (rc < 0) {
smblib_err(chg, "Couldn't enable vconn setting rc=%d\n", rc);
return rc;
@@ -1537,14 +1535,16 @@ int smblib_get_prop_batt_status(struct smb_charger *chg,
if (val->intval != POWER_SUPPLY_STATUS_CHARGING)
return 0;
- rc = smblib_read(chg, BATTERY_CHARGER_STATUS_2_REG, &stat);
+ rc = smblib_read(chg, BATTERY_CHARGER_STATUS_7_REG, &stat);
if (rc < 0) {
smblib_err(chg, "Couldn't read BATTERY_CHARGER_STATUS_2 rc=%d\n",
rc);
return rc;
}
- if (stat & (BAT_TEMP_STATUS_TOO_HOT_BIT | BAT_TEMP_STATUS_TOO_COLD_BIT))
+ stat &= ENABLE_TRICKLE_BIT | ENABLE_PRE_CHARGING_BIT |
+ ENABLE_FAST_CHARGING_BIT | ENABLE_FULLON_MODE_BIT;
+ if (!stat)
val->intval = POWER_SUPPLY_STATUS_NOT_CHARGING;
return 0;
@@ -2147,23 +2147,13 @@ int smblib_get_prop_charger_temp_max(struct smb_charger *chg,
int smblib_get_prop_typec_cc_orientation(struct smb_charger *chg,
union power_supply_propval *val)
{
- int rc = 0;
- u8 stat;
-
- rc = smblib_read(chg, TYPE_C_STATUS_4_REG, &stat);
- if (rc < 0) {
- smblib_err(chg, "Couldn't read TYPE_C_STATUS_4 rc=%d\n", rc);
- return rc;
- }
- smblib_dbg(chg, PR_REGISTER, "TYPE_C_STATUS_4 = 0x%02x\n",
- stat);
-
- if (stat & CC_ATTACHED_BIT)
- val->intval = (bool)(stat & CC_ORIENTATION_BIT) + 1;
+ if (chg->typec_status[3] & CC_ATTACHED_BIT)
+ val->intval =
+ (bool)(chg->typec_status[3] & CC_ORIENTATION_BIT) + 1;
else
val->intval = 0;
- return rc;
+ return 0;
}
static const char * const smblib_typec_mode_name[] = {
@@ -2181,17 +2171,7 @@ static const char * const smblib_typec_mode_name[] = {
static int smblib_get_prop_ufp_mode(struct smb_charger *chg)
{
- int rc;
- u8 stat;
-
- rc = smblib_read(chg, TYPE_C_STATUS_1_REG, &stat);
- if (rc < 0) {
- smblib_err(chg, "Couldn't read TYPE_C_STATUS_1 rc=%d\n", rc);
- return POWER_SUPPLY_TYPEC_NONE;
- }
- smblib_dbg(chg, PR_REGISTER, "TYPE_C_STATUS_1 = 0x%02x\n", stat);
-
- switch (stat) {
+ switch (chg->typec_status[0]) {
case 0:
return POWER_SUPPLY_TYPEC_NONE;
case UFP_TYPEC_RDSTD_BIT:
@@ -2209,17 +2189,7 @@ static int smblib_get_prop_ufp_mode(struct smb_charger *chg)
static int smblib_get_prop_dfp_mode(struct smb_charger *chg)
{
- int rc;
- u8 stat;
-
- rc = smblib_read(chg, TYPE_C_STATUS_2_REG, &stat);
- if (rc < 0) {
- smblib_err(chg, "Couldn't read TYPE_C_STATUS_2 rc=%d\n", rc);
- return POWER_SUPPLY_TYPEC_NONE;
- }
- smblib_dbg(chg, PR_REGISTER, "TYPE_C_STATUS_2 = 0x%02x\n", stat);
-
- switch (stat & DFP_TYPEC_MASK) {
+ switch (chg->typec_status[1] & DFP_TYPEC_MASK) {
case DFP_RA_RA_BIT:
return POWER_SUPPLY_TYPEC_SINK_AUDIO_ADAPTER;
case DFP_RD_RD_BIT:
@@ -2240,28 +2210,17 @@ static int smblib_get_prop_dfp_mode(struct smb_charger *chg)
int smblib_get_prop_typec_mode(struct smb_charger *chg,
union power_supply_propval *val)
{
- int rc;
- u8 stat;
-
- rc = smblib_read(chg, TYPE_C_STATUS_4_REG, &stat);
- if (rc < 0) {
- smblib_err(chg, "Couldn't read TYPE_C_STATUS_4 rc=%d\n", rc);
+ if (!(chg->typec_status[3] & TYPEC_DEBOUNCE_DONE_STATUS_BIT)) {
val->intval = POWER_SUPPLY_TYPEC_NONE;
- return rc;
- }
- smblib_dbg(chg, PR_REGISTER, "TYPE_C_STATUS_4 = 0x%02x\n", stat);
-
- if (!(stat & TYPEC_DEBOUNCE_DONE_STATUS_BIT)) {
- val->intval = POWER_SUPPLY_TYPEC_NONE;
- return rc;
+ return 0;
}
- if (stat & UFP_DFP_MODE_STATUS_BIT)
+ if (chg->typec_status[3] & UFP_DFP_MODE_STATUS_BIT)
val->intval = smblib_get_prop_dfp_mode(chg);
else
val->intval = smblib_get_prop_ufp_mode(chg);
- return rc;
+ return 0;
}
int smblib_get_prop_typec_power_role(struct smb_charger *chg,
@@ -2353,16 +2312,7 @@ int smblib_get_prop_input_voltage_settled(struct smb_charger *chg,
int smblib_get_prop_pd_in_hard_reset(struct smb_charger *chg,
union power_supply_propval *val)
{
- int rc;
- u8 ctrl;
-
- rc = smblib_read(chg, TYPE_C_INTRPT_ENB_SOFTWARE_CTRL_REG, &ctrl);
- if (rc < 0) {
- smblib_err(chg, "Couldn't read TYPE_C_INTRPT_ENB_SOFTWARE_CTRL_REG rc=%d\n",
- rc);
- return rc;
- }
- val->intval = ctrl & EXIT_SNK_BASED_ON_CC_BIT;
+ val->intval = chg->pd_hard_reset;
return 0;
}
@@ -2558,53 +2508,60 @@ int smblib_set_prop_pd_active(struct smb_charger *chg,
const union power_supply_propval *val)
{
int rc;
- u8 stat = 0;
- bool cc_debounced;
- bool orientation;
- bool pd_active = val->intval;
+ bool orientation, cc_debounced, sink_attached, hvdcp;
+ u8 stat;
- if (!get_effective_result(chg->pd_allowed_votable)) {
- smblib_err(chg, "PD is not allowed\n");
+ if (!get_effective_result(chg->pd_allowed_votable))
return -EINVAL;
- }
- vote(chg->apsd_disable_votable, PD_VOTER, pd_active, 0);
- vote(chg->pd_allowed_votable, PD_VOTER, pd_active, 0);
- vote(chg->usb_irq_enable_votable, PD_VOTER, pd_active, 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.
- */
- rc = smblib_read(chg, TYPE_C_STATUS_4_REG, &stat);
+ rc = smblib_read(chg, APSD_STATUS_REG, &stat);
if (rc < 0) {
- smblib_err(chg, "Couldn't read TYPE_C_STATUS_4 rc=%d\n", rc);
+ smblib_err(chg, "Couldn't read APSD status rc=%d\n", rc);
return rc;
}
- if (pd_active) {
- orientation = stat & CC_ORIENTATION_BIT;
+ cc_debounced = (bool)
+ (chg->typec_status[3] & TYPEC_DEBOUNCE_DONE_STATUS_BIT);
+ sink_attached = (bool)
+ (chg->typec_status[3] & UFP_DFP_MODE_STATUS_BIT);
+ hvdcp = stat & QC_CHARGER_BIT;
+
+ chg->pd_active = val->intval;
+ if (chg->pd_active) {
+ vote(chg->apsd_disable_votable, PD_VOTER, true, 0);
+ vote(chg->pd_allowed_votable, PD_VOTER, true, 0);
+ vote(chg->usb_irq_enable_votable, PD_VOTER, true, 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.
+ */
+ orientation = chg->typec_status[3] & CC_ORIENTATION_BIT;
rc = smblib_masked_write(chg,
TYPE_C_INTRPT_ENB_SOFTWARE_CTRL_REG,
VCONN_EN_ORIENTATION_BIT,
orientation ? 0 : VCONN_EN_ORIENTATION_BIT);
- if (rc < 0) {
+ if (rc < 0)
smblib_err(chg,
"Couldn't enable vconn on CC line rc=%d\n", rc);
- return rc;
- }
+
+ /* SW controlled CC_OUT */
+ rc = smblib_masked_write(chg, TAPER_TIMER_SEL_CFG_REG,
+ TYPEC_SPARE_CFG_BIT, TYPEC_SPARE_CFG_BIT);
+ if (rc < 0)
+ smblib_err(chg, "Couldn't enable SW cc_out rc=%d\n",
+ rc);
+
/*
* Enforce 500mA for PD until the real vote comes in later.
* It is guaranteed that pd_active is set prior to
* pd_current_max
*/
rc = vote(chg->usb_icl_votable, PD_VOTER, true, USBIN_500MA);
- if (rc < 0) {
+ if (rc < 0)
smblib_err(chg, "Couldn't vote for USB ICL rc=%d\n",
- rc);
- return rc;
- }
+ rc);
/* since PD was found the cable must be non-legacy */
vote(chg->usb_icl_votable, LEGACY_UNKNOWN_VOTER, false, 0);
@@ -2612,36 +2569,40 @@ int smblib_set_prop_pd_active(struct smb_charger *chg,
/* clear USB ICL vote for DCP_VOTER */
rc = vote(chg->usb_icl_votable, DCP_VOTER, false, 0);
if (rc < 0)
- smblib_err(chg,
- "Couldn't un-vote DCP from USB ICL rc=%d\n",
- rc);
+ smblib_err(chg, "Couldn't un-vote DCP from USB ICL rc=%d\n",
+ rc);
/* remove USB_PSY_VOTER */
rc = vote(chg->usb_icl_votable, USB_PSY_VOTER, false, 0);
- if (rc < 0) {
+ if (rc < 0)
smblib_err(chg, "Couldn't unvote USB_PSY rc=%d\n", rc);
- return rc;
- }
- }
+ } else {
+ vote(chg->apsd_disable_votable, PD_VOTER, false, 0);
+ vote(chg->pd_allowed_votable, PD_VOTER, true, 0);
+ vote(chg->usb_irq_enable_votable, PD_VOTER, true, 0);
+ vote(chg->hvdcp_disable_votable_indirect, PD_INACTIVE_VOTER,
+ false, 0);
- /* 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,
- pd_active ? TYPEC_SPARE_CFG_BIT : 0);
- if (rc < 0) {
- smblib_err(chg, "Couldn't change cc_out ctrl to %s rc=%d\n",
- pd_active ? "SW" : "HW", rc);
- return rc;
- }
+ /* HW controlled CC_OUT */
+ rc = smblib_masked_write(chg, TAPER_TIMER_SEL_CFG_REG,
+ TYPEC_SPARE_CFG_BIT, 0);
+ if (rc < 0)
+ smblib_err(chg, "Couldn't enable HW cc_out rc=%d\n",
+ rc);
- cc_debounced = (bool)(stat & TYPEC_DEBOUNCE_DONE_STATUS_BIT);
- if (!pd_active && cc_debounced)
- try_rerun_apsd_for_hvdcp(chg);
+ /*
+ * This WA should only run for HVDCP. Non-legacy SDP/CDP could
+ * draw more, but this WA will remove Rd causing VBUS to drop,
+ * and data could be interrupted. Non-legacy DCP could also draw
+ * more, but it may impact compliance.
+ */
+ if (!chg->typec_legacy_valid && cc_debounced &&
+ !sink_attached && hvdcp)
+ schedule_work(&chg->legacy_detection_work);
+ }
- chg->pd_active = pd_active;
smblib_update_usb_type(chg);
power_supply_changed(chg->usb_psy);
-
return rc;
}
@@ -2744,88 +2705,70 @@ static struct reg_info cc2_detach_settings[] = {
static int smblib_cc2_sink_removal_enter(struct smb_charger *chg)
{
- int rc = 0;
- union power_supply_propval cc2_val = {0, };
+ int rc, ccout, ufp_mode;
+ u8 stat;
if ((chg->wa_flags & TYPEC_CC2_REMOVAL_WA_BIT) == 0)
- return rc;
+ return 0;
- if (chg->cc2_sink_detach_flag != CC2_SINK_NONE)
- return rc;
+ if (chg->cc2_detach_wa_active)
+ return 0;
- rc = smblib_get_prop_typec_cc_orientation(chg, &cc2_val);
+ rc = smblib_read(chg, TYPE_C_STATUS_4_REG, &stat);
if (rc < 0) {
- smblib_err(chg, "Couldn't get cc orientation rc=%d\n", rc);
+ smblib_err(chg, "Couldn't read TYPE_C_STATUS_4 rc=%d\n", rc);
return rc;
}
- if (cc2_val.intval == 1)
- return rc;
+ ccout = (stat & CC_ATTACHED_BIT) ?
+ (!!(stat & CC_ORIENTATION_BIT) + 1) : 0;
+ ufp_mode = (stat & TYPEC_DEBOUNCE_DONE_STATUS_BIT) ?
+ !(stat & UFP_DFP_MODE_STATUS_BIT) : 0;
- rc = smblib_get_prop_typec_mode(chg, &cc2_val);
- if (rc < 0) {
- smblib_err(chg, "Couldn't get prop typec mode rc=%d\n", rc);
- return rc;
- }
+ if (ccout != 2)
+ return 0;
- switch (cc2_val.intval) {
- case POWER_SUPPLY_TYPEC_SOURCE_DEFAULT:
- smblib_reg_block_update(chg, cc2_detach_settings);
- chg->cc2_sink_detach_flag = CC2_SINK_STD;
- schedule_work(&chg->rdstd_cc2_detach_work);
- break;
- case POWER_SUPPLY_TYPEC_SOURCE_MEDIUM:
- case POWER_SUPPLY_TYPEC_SOURCE_HIGH:
- chg->cc2_sink_detach_flag = CC2_SINK_MEDIUM_HIGH;
- break;
- default:
- break;
- }
+ if (!ufp_mode)
+ return 0;
+ chg->cc2_detach_wa_active = true;
+ /* The CC2 removal WA will cause a type-c-change IRQ storm */
+ smblib_reg_block_update(chg, cc2_detach_settings);
+ schedule_work(&chg->rdstd_cc2_detach_work);
return rc;
}
static int smblib_cc2_sink_removal_exit(struct smb_charger *chg)
{
- int rc = 0;
-
if ((chg->wa_flags & TYPEC_CC2_REMOVAL_WA_BIT) == 0)
- return rc;
-
- if (chg->cc2_sink_detach_flag == CC2_SINK_STD) {
- cancel_work_sync(&chg->rdstd_cc2_detach_work);
- smblib_reg_block_restore(chg, cc2_detach_settings);
- }
+ return 0;
- chg->cc2_sink_detach_flag = CC2_SINK_NONE;
+ if (!chg->cc2_detach_wa_active)
+ return 0;
- return rc;
+ chg->cc2_detach_wa_active = false;
+ cancel_work_sync(&chg->rdstd_cc2_detach_work);
+ smblib_reg_block_restore(chg, cc2_detach_settings);
+ return 0;
}
int smblib_set_prop_pd_in_hard_reset(struct smb_charger *chg,
const union power_supply_propval *val)
{
- int rc;
+ int rc = 0;
- rc = smblib_masked_write(chg, TYPE_C_INTRPT_ENB_SOFTWARE_CTRL_REG,
- EXIT_SNK_BASED_ON_CC_BIT,
- (val->intval) ? EXIT_SNK_BASED_ON_CC_BIT : 0);
- if (rc < 0) {
- smblib_err(chg, "Could not set EXIT_SNK_BASED_ON_CC rc=%d\n",
- rc);
+ if (chg->pd_hard_reset == val->intval)
return rc;
- }
- vote(chg->apsd_disable_votable, PD_HARD_RESET_VOTER, val->intval, 0);
-
- if (val->intval)
- rc = smblib_cc2_sink_removal_enter(chg);
- else
- rc = smblib_cc2_sink_removal_exit(chg);
+ chg->pd_hard_reset = val->intval;
+ rc = smblib_masked_write(chg, TYPE_C_INTRPT_ENB_SOFTWARE_CTRL_REG,
+ EXIT_SNK_BASED_ON_CC_BIT,
+ (chg->pd_hard_reset) ? EXIT_SNK_BASED_ON_CC_BIT : 0);
+ if (rc < 0)
+ smblib_err(chg, "Couldn't set EXIT_SNK_BASED_ON_CC rc=%d\n",
+ rc);
- if (rc < 0) {
- smblib_err(chg, "Could not detect cc2 removal rc=%d\n", rc);
- return rc;
- }
+ vote(chg->apsd_disable_votable, PD_HARD_RESET_VOTER,
+ chg->pd_hard_reset, 0);
return rc;
}
@@ -3132,25 +3075,43 @@ irqreturn_t smblib_handle_usbin_uv(int irq, void *data)
return IRQ_HANDLED;
}
+static void smblib_micro_usb_plugin(struct smb_charger *chg, bool vbus_rising)
+{
+ if (vbus_rising) {
+ /* use the typec flag even though its not typec */
+ chg->typec_present = 1;
+ } else {
+ chg->typec_present = 0;
+ smblib_update_usb_type(chg);
+ extcon_set_cable_state_(chg->extcon, EXTCON_USB, false);
+ smblib_uusb_removal(chg);
+ }
+}
+
+static void smblib_typec_usb_plugin(struct smb_charger *chg, bool vbus_rising)
+{
+ if (vbus_rising)
+ smblib_cc2_sink_removal_exit(chg);
+ else
+ smblib_cc2_sink_removal_enter(chg);
+}
+
#define PL_DELAY_MS 30000
-irqreturn_t smblib_handle_usb_plugin(int irq, void *data)
+void smblib_usb_plugin_locked(struct smb_charger *chg)
{
- struct smb_irq_data *irq_data = data;
- struct smb_charger *chg = irq_data->parent_data;
int rc;
u8 stat;
bool vbus_rising;
rc = smblib_read(chg, USBIN_BASE + INT_RT_STS_OFFSET, &stat);
if (rc < 0) {
- dev_err(chg->dev, "Couldn't read USB_INT_RT_STS rc=%d\n", rc);
- return IRQ_HANDLED;
+ smblib_err(chg, "Couldn't read USB_INT_RT_STS rc=%d\n", rc);
+ return;
}
vbus_rising = (bool)(stat & USBIN_PLUGIN_RT_STS_BIT);
- smblib_set_opt_freq_buck(chg,
- vbus_rising ? chg->chg_freq.freq_5V :
- chg->chg_freq.freq_removal);
+ smblib_set_opt_freq_buck(chg, vbus_rising ? chg->chg_freq.freq_5V :
+ chg->chg_freq.freq_removal);
/* fetch the DPDM regulator */
if (!chg->dpdm_reg && of_get_property(chg->dev->of_node,
@@ -3187,17 +3148,26 @@ irqreturn_t smblib_handle_usb_plugin(int irq, void *data)
smblib_err(chg, "Couldn't disable dpdm regulator rc=%d\n",
rc);
}
-
- if (chg->micro_usb_mode) {
- smblib_update_usb_type(chg);
- extcon_set_cable_state_(chg->extcon, EXTCON_USB, false);
- smblib_uusb_removal(chg);
- }
}
+ if (chg->micro_usb_mode)
+ smblib_micro_usb_plugin(chg, vbus_rising);
+ else
+ smblib_typec_usb_plugin(chg, vbus_rising);
+
power_supply_changed(chg->usb_psy);
- smblib_dbg(chg, PR_INTERRUPT, "IRQ: %s %s\n",
- irq_data->name, vbus_rising ? "attached" : "detached");
+ smblib_dbg(chg, PR_INTERRUPT, "IRQ: usbin-plugin %s\n",
+ vbus_rising ? "attached" : "detached");
+}
+
+irqreturn_t smblib_handle_usb_plugin(int irq, void *data)
+{
+ struct smb_irq_data *irq_data = data;
+ struct smb_charger *chg = irq_data->parent_data;
+
+ mutex_lock(&chg->lock);
+ smblib_usb_plugin_locked(chg);
+ mutex_unlock(&chg->lock);
return IRQ_HANDLED;
}
@@ -3366,9 +3336,6 @@ static void smblib_handle_hvdcp_check_timeout(struct smb_charger *chg,
if (rising) {
vote(chg->pd_disallowed_votable_indirect, HVDCP_TIMEOUT_VOTER,
false, 0);
- if (get_effective_result(chg->pd_disallowed_votable_indirect))
- /* could be a legacy cable, try doing hvdcp */
- try_rerun_apsd_for_hvdcp(chg);
/* enable HDC and ICL irq for QC2/3 charger */
if (qc_charger)
@@ -3403,6 +3370,10 @@ static void smblib_handle_hvdcp_detect_done(struct smb_charger *chg,
static void smblib_force_legacy_icl(struct smb_charger *chg, int pst)
{
+ /* while PD is active it should have complete ICL control */
+ if (chg->pd_active)
+ return;
+
switch (pst) {
case POWER_SUPPLY_TYPE_USB:
/*
@@ -3442,7 +3413,7 @@ static void smblib_handle_apsd_done(struct smb_charger *chg, bool rising)
apsd_result = smblib_update_usb_type(chg);
- if (!chg->pd_active)
+ if (!chg->typec_legacy_valid)
smblib_force_legacy_icl(chg, apsd_result->pst);
switch (apsd_result->bit) {
@@ -3528,73 +3499,6 @@ irqreturn_t smblib_handle_usb_source_change(int irq, void *data)
return IRQ_HANDLED;
}
-static void typec_source_removal(struct smb_charger *chg)
-{
- int rc;
-
- /* reset legacy unknown vote */
- vote(chg->usb_icl_votable, LEGACY_UNKNOWN_VOTER, false, 0);
-
- /* reset both usbin current and voltage votes */
- vote(chg->pl_enable_votable_indirect, USBIN_I_VOTER, false, 0);
- vote(chg->pl_enable_votable_indirect, USBIN_V_VOTER, false, 0);
-
- cancel_delayed_work_sync(&chg->hvdcp_detect_work);
-
- if (chg->wa_flags & QC_AUTH_INTERRUPT_WA_BIT) {
- /* re-enable AUTH_IRQ_EN_CFG_BIT */
- rc = smblib_masked_write(chg,
- USBIN_SOURCE_CHANGE_INTRPT_ENB_REG,
- AUTH_IRQ_EN_CFG_BIT, AUTH_IRQ_EN_CFG_BIT);
- if (rc < 0)
- smblib_err(chg,
- "Couldn't enable QC auth setting rc=%d\n", rc);
- }
-
- /* reconfigure allowed voltage for HVDCP */
- rc = smblib_set_adapter_allowance(chg,
- USBIN_ADAPTER_ALLOW_5V_OR_9V_TO_12V);
- if (rc < 0)
- smblib_err(chg, "Couldn't set USBIN_ADAPTER_ALLOW_5V_OR_9V_TO_12V rc=%d\n",
- rc);
-
- chg->voltage_min_uv = MICRO_5V;
- chg->voltage_max_uv = MICRO_5V;
-
- /* clear USB ICL vote for PD_VOTER */
- rc = vote(chg->usb_icl_votable, PD_VOTER, false, 0);
- if (rc < 0)
- smblib_err(chg, "Couldn't un-vote PD from USB ICL rc=%d\n", rc);
-
- /* clear USB ICL vote for USB_PSY_VOTER */
- rc = vote(chg->usb_icl_votable, USB_PSY_VOTER, false, 0);
- if (rc < 0)
- smblib_err(chg,
- "Couldn't un-vote USB_PSY from USB ICL rc=%d\n", rc);
-
- /* clear USB ICL vote for DCP_VOTER */
- rc = vote(chg->usb_icl_votable, DCP_VOTER, false, 0);
- if (rc < 0)
- smblib_err(chg,
- "Couldn't un-vote DCP from USB ICL rc=%d\n", rc);
-
-}
-
-static void typec_source_insertion(struct smb_charger *chg)
-{
- /*
- * at any time we want LEGACY_UNKNOWN, PD, or USB_PSY to be voting for
- * ICL, so vote LEGACY_UNKNOWN here if none of the above three have
- * casted their votes
- */
- if (!is_client_vote_enabled(chg->usb_icl_votable, LEGACY_UNKNOWN_VOTER)
- && !is_client_vote_enabled(chg->usb_icl_votable, PD_VOTER)
- && !is_client_vote_enabled(chg->usb_icl_votable, USB_PSY_VOTER))
- vote(chg->usb_icl_votable, LEGACY_UNKNOWN_VOTER, true, 100000);
-
- smblib_set_opt_freq_buck(chg, chg->chg_freq.freq_5V);
-}
-
static void typec_sink_insertion(struct smb_charger *chg)
{
/* when a sink is inserted we should not wait on hvdcp timeout to
@@ -3615,30 +3519,50 @@ static void smblib_handle_typec_removal(struct smb_charger *chg)
{
int rc;
+ chg->cc2_detach_wa_active = false;
+
+ /* reset APSD voters */
+ vote(chg->apsd_disable_votable, PD_HARD_RESET_VOTER, false, 0);
+ vote(chg->apsd_disable_votable, PD_VOTER, false, 0);
+
cancel_delayed_work_sync(&chg->pl_enable_work);
- vote(chg->pl_disable_votable, PL_DELAY_VOTER, true, 0);
- vote(chg->awake_votable, PL_DELAY_VOTER, false, 0);
+ cancel_delayed_work_sync(&chg->hvdcp_detect_work);
+ /* reset input current limit voters */
+ vote(chg->usb_icl_votable, LEGACY_UNKNOWN_VOTER, true, 100000);
+ vote(chg->usb_icl_votable, PD_VOTER, false, 0);
+ vote(chg->usb_icl_votable, USB_PSY_VOTER, false, 0);
+ vote(chg->usb_icl_votable, DCP_VOTER, false, 0);
+ vote(chg->usb_icl_votable, PL_USBIN_USBIN_VOTER, false, 0);
+
+ /* reset hvdcp voters */
+ vote(chg->hvdcp_disable_votable_indirect, VBUS_CC_SHORT_VOTER, true, 0);
+ vote(chg->hvdcp_disable_votable_indirect, PD_INACTIVE_VOTER, true, 0);
+
+ /* reset power delivery voters */
+ vote(chg->pd_allowed_votable, PD_VOTER, false, 0);
vote(chg->pd_disallowed_votable_indirect, CC_DETACHED_VOTER, true, 0);
vote(chg->pd_disallowed_votable_indirect, HVDCP_TIMEOUT_VOTER, true, 0);
+
+ /* reset usb irq voters */
vote(chg->usb_irq_enable_votable, PD_VOTER, false, 0);
vote(chg->usb_irq_enable_votable, QC_VOTER, false, 0);
- /* reset votes from vbus_cc_short */
- vote(chg->hvdcp_disable_votable_indirect, VBUS_CC_SHORT_VOTER,
- true, 0);
- vote(chg->hvdcp_disable_votable_indirect, PD_INACTIVE_VOTER,
- true, 0);
- /*
- * cable could be removed during hard reset, remove its vote to
- * disable apsd
- */
- vote(chg->apsd_disable_votable, PD_HARD_RESET_VOTER, false, 0);
+ /* reset parallel voters */
+ vote(chg->pl_disable_votable, PL_DELAY_VOTER, true, 0);
+ vote(chg->pl_enable_votable_indirect, USBIN_I_VOTER, false, 0);
+ vote(chg->pl_enable_votable_indirect, USBIN_V_VOTER, false, 0);
+ vote(chg->awake_votable, PL_DELAY_VOTER, false, 0);
chg->vconn_attempts = 0;
chg->otg_attempts = 0;
chg->pulse_cnt = 0;
chg->usb_icl_delta_ua = 0;
+ chg->voltage_min_uv = MICRO_5V;
+ chg->voltage_max_uv = MICRO_5V;
+ chg->pd_active = 0;
+ chg->pd_hard_reset = 0;
+ chg->typec_legacy_valid = false;
/* enable APSD CC trigger for next insertion */
rc = smblib_masked_write(chg, TYPE_C_CFG_REG,
@@ -3646,15 +3570,48 @@ static void smblib_handle_typec_removal(struct smb_charger *chg)
if (rc < 0)
smblib_err(chg, "Couldn't enable APSD_START_ON_CC rc=%d\n", rc);
- smblib_update_usb_type(chg);
- typec_source_removal(chg);
+ if (chg->wa_flags & QC_AUTH_INTERRUPT_WA_BIT) {
+ /* re-enable AUTH_IRQ_EN_CFG_BIT */
+ rc = smblib_masked_write(chg,
+ USBIN_SOURCE_CHANGE_INTRPT_ENB_REG,
+ AUTH_IRQ_EN_CFG_BIT, AUTH_IRQ_EN_CFG_BIT);
+ if (rc < 0)
+ smblib_err(chg,
+ "Couldn't enable QC auth setting rc=%d\n", rc);
+ }
+
+ /* reconfigure allowed voltage for HVDCP */
+ rc = smblib_set_adapter_allowance(chg,
+ USBIN_ADAPTER_ALLOW_5V_OR_9V_TO_12V);
+ if (rc < 0)
+ smblib_err(chg, "Couldn't set USBIN_ADAPTER_ALLOW_5V_OR_9V_TO_12V rc=%d\n",
+ rc);
+
+ /* enable DRP */
+ rc = smblib_masked_write(chg, TYPE_C_INTRPT_ENB_SOFTWARE_CTRL_REG,
+ TYPEC_POWER_ROLE_CMD_MASK, 0);
+ if (rc < 0)
+ smblib_err(chg, "Couldn't enable DRP rc=%d\n", rc);
+
+ /* HW controlled CC_OUT */
+ rc = smblib_masked_write(chg, TAPER_TIMER_SEL_CFG_REG,
+ TYPEC_SPARE_CFG_BIT, 0);
+ if (rc < 0)
+ smblib_err(chg, "Couldn't enable HW cc_out rc=%d\n", rc);
+
+ /* restore crude sensor */
+ rc = smblib_write(chg, TM_IO_DTEST4_SEL, 0xA5);
+ if (rc < 0)
+ smblib_err(chg, "Couldn't restore crude sensor rc=%d\n", rc);
+
typec_sink_removal(chg);
+ smblib_update_usb_type(chg);
}
static void smblib_handle_typec_insertion(struct smb_charger *chg,
- bool sink_attached, bool legacy_cable)
+ bool sink_attached)
{
- int rp, rc;
+ int rc;
vote(chg->pd_disallowed_votable_indirect, CC_DETACHED_VOTER, false, 0);
@@ -3664,59 +3621,36 @@ static void smblib_handle_typec_insertion(struct smb_charger *chg,
smblib_err(chg, "Couldn't disable APSD_START_ON_CC rc=%d\n",
rc);
- if (sink_attached) {
- typec_source_removal(chg);
+ if (sink_attached)
typec_sink_insertion(chg);
- } else {
+ else
typec_sink_removal(chg);
- typec_source_insertion(chg);
- }
-
- rp = smblib_get_prop_ufp_mode(chg);
- if (rp == POWER_SUPPLY_TYPEC_SOURCE_HIGH
- || rp == POWER_SUPPLY_TYPEC_NON_COMPLIANT) {
- smblib_dbg(chg, PR_MISC, "VBUS & CC could be shorted; keeping HVDCP disabled\n");
- vote(chg->hvdcp_disable_votable_indirect, VBUS_CC_SHORT_VOTER,
- true, 0);
- } else {
- vote(chg->hvdcp_disable_votable_indirect, VBUS_CC_SHORT_VOTER,
- false, 0);
- }
}
static void smblib_handle_typec_debounce_done(struct smb_charger *chg,
- bool rising, bool sink_attached, bool legacy_cable)
+ bool rising, bool sink_attached)
{
int rc;
union power_supply_propval pval = {0, };
- if (rising)
- smblib_handle_typec_insertion(chg, sink_attached, legacy_cable);
- else
- smblib_handle_typec_removal(chg);
+ if (rising) {
+ if (!chg->typec_present) {
+ chg->typec_present = true;
+ smblib_dbg(chg, PR_MISC, "TypeC insertion\n");
+ smblib_handle_typec_insertion(chg, sink_attached);
+ }
+ } else {
+ if (chg->typec_present) {
+ chg->typec_present = false;
+ smblib_dbg(chg, PR_MISC, "TypeC removal\n");
+ smblib_handle_typec_removal(chg);
+ }
+ }
rc = smblib_get_prop_typec_mode(chg, &pval);
if (rc < 0)
smblib_err(chg, "Couldn't get prop typec mode rc=%d\n", rc);
- /*
- * HW BUG - after cable is removed, medium or high rd reading
- * falls to std. Use it for signal of typec cc detachment in
- * software WA.
- */
- if (chg->cc2_sink_detach_flag == CC2_SINK_MEDIUM_HIGH
- && pval.intval == POWER_SUPPLY_TYPEC_SOURCE_DEFAULT) {
-
- chg->cc2_sink_detach_flag = CC2_SINK_WA_DONE;
-
- rc = smblib_masked_write(chg,
- TYPE_C_INTRPT_ENB_SOFTWARE_CTRL_REG,
- EXIT_SNK_BASED_ON_CC_BIT, 0);
- if (rc < 0)
- smblib_err(chg, "Couldn't get prop typec mode rc=%d\n",
- rc);
- }
-
smblib_dbg(chg, PR_INTERRUPT, "IRQ: debounce-done %s; Type-C %s detected\n",
rising ? "rising" : "falling",
smblib_typec_mode_name[pval.intval]);
@@ -3742,50 +3676,54 @@ irqreturn_t smblib_handle_usb_typec_change_for_uusb(struct smb_charger *chg)
return IRQ_HANDLED;
}
-irqreturn_t smblib_handle_usb_typec_change(int irq, void *data)
+static void smblib_usb_typec_change(struct smb_charger *chg)
{
- struct smb_irq_data *irq_data = data;
- struct smb_charger *chg = irq_data->parent_data;
int rc;
- u8 stat4, stat5;
- bool debounce_done, sink_attached, legacy_cable;
-
- if (chg->micro_usb_mode)
- return smblib_handle_usb_typec_change_for_uusb(chg);
+ bool debounce_done, sink_attached;
- /* WA - not when PD hard_reset WIP on cc2 in sink mode */
- if (chg->cc2_sink_detach_flag == CC2_SINK_STD)
- return IRQ_HANDLED;
-
- rc = smblib_read(chg, TYPE_C_STATUS_4_REG, &stat4);
- if (rc < 0) {
- smblib_err(chg, "Couldn't read TYPE_C_STATUS_4 rc=%d\n", rc);
- return IRQ_HANDLED;
- }
-
- rc = smblib_read(chg, TYPE_C_STATUS_5_REG, &stat5);
+ rc = smblib_multibyte_read(chg, TYPE_C_STATUS_1_REG,
+ chg->typec_status, 5);
if (rc < 0) {
- smblib_err(chg, "Couldn't read TYPE_C_STATUS_5 rc=%d\n", rc);
- return IRQ_HANDLED;
+ smblib_err(chg, "Couldn't cache USB Type-C status rc=%d\n", rc);
+ return;
}
- debounce_done = (bool)(stat4 & TYPEC_DEBOUNCE_DONE_STATUS_BIT);
- sink_attached = (bool)(stat4 & UFP_DFP_MODE_STATUS_BIT);
- legacy_cable = (bool)(stat5 & TYPEC_LEGACY_CABLE_STATUS_BIT);
+ debounce_done =
+ (bool)(chg->typec_status[3] & TYPEC_DEBOUNCE_DONE_STATUS_BIT);
+ sink_attached =
+ (bool)(chg->typec_status[3] & UFP_DFP_MODE_STATUS_BIT);
- smblib_handle_typec_debounce_done(chg,
- debounce_done, sink_attached, legacy_cable);
+ smblib_handle_typec_debounce_done(chg, debounce_done, sink_attached);
- if (stat4 & TYPEC_VBUS_ERROR_STATUS_BIT)
- smblib_dbg(chg, PR_INTERRUPT, "IRQ: %s vbus-error\n",
- irq_data->name);
+ if (chg->typec_status[3] & TYPEC_VBUS_ERROR_STATUS_BIT)
+ smblib_dbg(chg, PR_INTERRUPT, "IRQ: vbus-error\n");
- if (stat4 & TYPEC_VCONN_OVERCURR_STATUS_BIT)
+ if (chg->typec_status[3] & TYPEC_VCONN_OVERCURR_STATUS_BIT)
schedule_work(&chg->vconn_oc_work);
power_supply_changed(chg->usb_psy);
- smblib_dbg(chg, PR_REGISTER, "TYPE_C_STATUS_4 = 0x%02x\n", stat4);
- smblib_dbg(chg, PR_REGISTER, "TYPE_C_STATUS_5 = 0x%02x\n", stat5);
+}
+
+irqreturn_t smblib_handle_usb_typec_change(int irq, void *data)
+{
+ struct smb_irq_data *irq_data = data;
+ struct smb_charger *chg = irq_data->parent_data;
+
+ if (chg->micro_usb_mode) {
+ smblib_handle_usb_typec_change_for_uusb(chg);
+ return IRQ_HANDLED;
+ }
+
+ if (chg->cc2_detach_wa_active || chg->typec_en_dis_active) {
+ smblib_dbg(chg, PR_INTERRUPT, "Ignoring since %s active\n",
+ chg->cc2_detach_wa_active ?
+ "cc2_detach_wa" : "typec_en_dis");
+ return IRQ_HANDLED;
+ }
+
+ mutex_lock(&chg->lock);
+ smblib_usb_typec_change(chg);
+ mutex_unlock(&chg->lock);
return IRQ_HANDLED;
}
@@ -3813,7 +3751,7 @@ irqreturn_t smblib_handle_switcher_power_ok(int irq, void *data)
{
struct smb_irq_data *irq_data = data;
struct smb_charger *chg = irq_data->parent_data;
- int rc;
+ int rc, usb_icl;
u8 stat;
if (!(chg->wa_flags & BOOST_BACK_WA))
@@ -3825,8 +3763,9 @@ irqreturn_t smblib_handle_switcher_power_ok(int irq, void *data)
return IRQ_HANDLED;
}
- if ((stat & USE_USBIN_BIT) &&
- get_effective_result(chg->usb_icl_votable) < USBIN_25MA)
+ /* skip suspending input if its already suspended by some other voter */
+ usb_icl = get_effective_result(chg->usb_icl_votable);
+ if ((stat & USE_USBIN_BIT) && usb_icl >= 0 && usb_icl < USBIN_25MA)
return IRQ_HANDLED;
if (stat & USE_DCIN_BIT)
@@ -3864,12 +3803,7 @@ static void smblib_hvdcp_detect_work(struct work_struct *work)
vote(chg->pd_disallowed_votable_indirect, HVDCP_TIMEOUT_VOTER,
false, 0);
- if (get_effective_result(chg->pd_disallowed_votable_indirect))
- /* pd is still disabled, try hvdcp */
- try_rerun_apsd_for_hvdcp(chg);
- else
- /* notify pd now that pd is allowed */
- power_supply_changed(chg->usb_psy);
+ power_supply_changed(chg->usb_psy);
}
static void bms_update_work(struct work_struct *work)
@@ -3910,11 +3844,13 @@ static void clear_hdc_work(struct work_struct *work)
static void rdstd_cc2_detach_work(struct work_struct *work)
{
int rc;
- u8 stat;
- struct smb_irq_data irq_data = {NULL, "cc2-removal-workaround"};
+ u8 stat4, stat5;
struct smb_charger *chg = container_of(work, struct smb_charger,
rdstd_cc2_detach_work);
+ if (!chg->cc2_detach_wa_active)
+ return;
+
/*
* WA steps -
* 1. Enable both UFP and DFP, wait for 10ms.
@@ -3922,7 +3858,7 @@ static void rdstd_cc2_detach_work(struct work_struct *work)
* 3. Removal detected if both TYPEC_DEBOUNCE_DONE_STATUS
* and TIMER_STAGE bits are gone, otherwise repeat all by
* work rescheduling.
- * Note, work will be cancelled when pd_hard_reset is 0.
+ * Note, work will be cancelled when USB_PLUGIN rises.
*/
rc = smblib_masked_write(chg, TYPE_C_INTRPT_ENB_SOFTWARE_CTRL_REG,
@@ -3945,30 +3881,35 @@ static void rdstd_cc2_detach_work(struct work_struct *work)
usleep_range(30000, 31000);
- rc = smblib_read(chg, TYPE_C_STATUS_4_REG, &stat);
+ rc = smblib_read(chg, TYPE_C_STATUS_4_REG, &stat4);
if (rc < 0) {
- smblib_err(chg, "Couldn't read TYPE_C_STATUS_4 rc=%d\n",
- rc);
+ smblib_err(chg, "Couldn't read TYPE_C_STATUS_4 rc=%d\n", rc);
return;
}
- if (stat & TYPEC_DEBOUNCE_DONE_STATUS_BIT)
- goto rerun;
- rc = smblib_read(chg, TYPE_C_STATUS_5_REG, &stat);
+ rc = smblib_read(chg, TYPE_C_STATUS_5_REG, &stat5);
if (rc < 0) {
smblib_err(chg,
"Couldn't read TYPE_C_STATUS_5_REG rc=%d\n", rc);
return;
}
- if (stat & TIMER_STAGE_2_BIT)
+
+ if ((stat4 & TYPEC_DEBOUNCE_DONE_STATUS_BIT)
+ || (stat5 & TIMER_STAGE_2_BIT)) {
+ smblib_dbg(chg, PR_MISC, "rerunning DD=%d TS2BIT=%d\n",
+ (int)(stat4 & TYPEC_DEBOUNCE_DONE_STATUS_BIT),
+ (int)(stat5 & TIMER_STAGE_2_BIT));
goto rerun;
+ }
- /* Bingo, cc2 removal detected */
+ smblib_dbg(chg, PR_MISC, "Bingo CC2 Removal detected\n");
+ chg->cc2_detach_wa_active = false;
+ rc = smblib_masked_write(chg, TYPE_C_INTRPT_ENB_SOFTWARE_CTRL_REG,
+ EXIT_SNK_BASED_ON_CC_BIT, 0);
smblib_reg_block_restore(chg, cc2_detach_settings);
- chg->cc2_sink_detach_flag = CC2_SINK_WA_DONE;
- irq_data.parent_data = chg;
- smblib_handle_usb_typec_change(0, &irq_data);
-
+ mutex_lock(&chg->lock);
+ smblib_usb_typec_change(chg);
+ mutex_unlock(&chg->lock);
return;
rerun:
@@ -4191,6 +4132,56 @@ static void smblib_pl_enable_work(struct work_struct *work)
vote(chg->awake_votable, PL_DELAY_VOTER, false, 0);
}
+static void smblib_legacy_detection_work(struct work_struct *work)
+{
+ struct smb_charger *chg = container_of(work, struct smb_charger,
+ legacy_detection_work);
+ int rc;
+ u8 stat;
+ bool legacy, rp_high;
+
+ mutex_lock(&chg->lock);
+ chg->typec_en_dis_active = 1;
+ smblib_dbg(chg, PR_MISC, "running legacy unknown workaround\n");
+ rc = smblib_masked_write(chg,
+ TYPE_C_INTRPT_ENB_SOFTWARE_CTRL_REG,
+ TYPEC_DISABLE_CMD_BIT,
+ TYPEC_DISABLE_CMD_BIT);
+ if (rc < 0)
+ smblib_err(chg, "Couldn't disable type-c rc=%d\n", rc);
+
+ /* wait for the adapter to turn off VBUS */
+ msleep(500);
+
+ rc = smblib_masked_write(chg,
+ TYPE_C_INTRPT_ENB_SOFTWARE_CTRL_REG,
+ TYPEC_DISABLE_CMD_BIT, 0);
+ if (rc < 0)
+ smblib_err(chg, "Couldn't enable type-c rc=%d\n", rc);
+
+ /* wait for type-c detection to complete */
+ msleep(100);
+
+ rc = smblib_read(chg, TYPE_C_STATUS_5_REG, &stat);
+ if (rc < 0) {
+ smblib_err(chg, "Couldn't read typec stat5 rc = %d\n", rc);
+ goto unlock;
+ }
+
+ chg->typec_legacy_valid = true;
+ vote(chg->usb_icl_votable, LEGACY_UNKNOWN_VOTER, false, 0);
+ legacy = stat & TYPEC_LEGACY_CABLE_STATUS_BIT;
+ rp_high = smblib_get_prop_ufp_mode(chg) ==
+ POWER_SUPPLY_TYPEC_SOURCE_HIGH;
+ if (!legacy || !rp_high)
+ vote(chg->hvdcp_disable_votable_indirect, VBUS_CC_SHORT_VOTER,
+ false, 0);
+
+unlock:
+ chg->typec_en_dis_active = 0;
+ mutex_unlock(&chg->lock);
+}
+
static int smblib_create_votables(struct smb_charger *chg)
{
int rc = 0;
@@ -4323,6 +4314,15 @@ static int smblib_create_votables(struct smb_charger *chg)
return rc;
}
+ chg->typec_irq_disable_votable = create_votable("TYPEC_IRQ_DISABLE",
+ VOTE_SET_ANY,
+ smblib_typec_irq_disable_vote_callback,
+ chg);
+ if (IS_ERR(chg->typec_irq_disable_votable)) {
+ rc = PTR_ERR(chg->typec_irq_disable_votable);
+ return rc;
+ }
+
return rc;
}
@@ -4348,6 +4348,8 @@ static void smblib_destroy_votables(struct smb_charger *chg)
destroy_votable(chg->apsd_disable_votable);
if (chg->hvdcp_hw_inov_dis_votable)
destroy_votable(chg->hvdcp_hw_inov_dis_votable);
+ if (chg->typec_irq_disable_votable)
+ destroy_votable(chg->typec_irq_disable_votable);
}
static void smblib_iio_deinit(struct smb_charger *chg)
@@ -4368,6 +4370,7 @@ int smblib_init(struct smb_charger *chg)
{
int rc = 0;
+ mutex_init(&chg->lock);
mutex_init(&chg->write_lock);
mutex_init(&chg->otg_oc_lock);
INIT_WORK(&chg->bms_update_work, bms_update_work);
@@ -4380,6 +4383,7 @@ int smblib_init(struct smb_charger *chg)
INIT_DELAYED_WORK(&chg->otg_ss_done_work, smblib_otg_ss_done_work);
INIT_DELAYED_WORK(&chg->icl_change_work, smblib_icl_change_work);
INIT_DELAYED_WORK(&chg->pl_enable_work, smblib_pl_enable_work);
+ INIT_WORK(&chg->legacy_detection_work, smblib_legacy_detection_work);
chg->fake_capacity = -EINVAL;
switch (chg->mode) {
diff --git a/drivers/power/supply/qcom/smb-lib.h b/drivers/power/supply/qcom/smb-lib.h
index 4b277c4282cf..b0d84f014b0d 100644
--- a/drivers/power/supply/qcom/smb-lib.h
+++ b/drivers/power/supply/qcom/smb-lib.h
@@ -61,6 +61,7 @@ enum print_reason {
#define SW_QC3_VOTER "SW_QC3_VOTER"
#define AICL_RERUN_VOTER "AICL_RERUN_VOTER"
#define LEGACY_UNKNOWN_VOTER "LEGACY_UNKNOWN_VOTER"
+#define CC2_WA_VOTER "CC2_WA_VOTER"
#define VCONN_MAX_ATTEMPTS 3
#define OTG_MAX_ATTEMPTS 3
@@ -71,13 +72,6 @@ enum smb_mode {
NUM_MODES,
};
-enum cc2_sink_type {
- CC2_SINK_NONE = 0,
- CC2_SINK_STD,
- CC2_SINK_MEDIUM_HIGH,
- CC2_SINK_WA_DONE,
-};
-
enum {
QC_CHARGER_DETECTION_WA_BIT = BIT(0),
BOOST_BACK_WA = BIT(1),
@@ -236,6 +230,7 @@ struct smb_charger {
int smb_version;
/* locks */
+ struct mutex lock;
struct mutex write_lock;
struct mutex ps_change_lock;
struct mutex otg_oc_lock;
@@ -276,6 +271,7 @@ struct smb_charger {
struct votable *apsd_disable_votable;
struct votable *hvdcp_hw_inov_dis_votable;
struct votable *usb_irq_enable_votable;
+ struct votable *typec_irq_disable_votable;
/* work */
struct work_struct bms_update_work;
@@ -289,6 +285,7 @@ struct smb_charger {
struct delayed_work otg_ss_done_work;
struct delayed_work icl_change_work;
struct delayed_work pl_enable_work;
+ struct work_struct legacy_detection_work;
/* cached status */
int voltage_min_uv;
@@ -313,10 +310,15 @@ struct smb_charger {
int default_icl_ua;
int otg_cl_ua;
bool uusb_apsd_rerun_done;
+ bool pd_hard_reset;
+ bool typec_present;
+ u8 typec_status[5];
+ bool typec_legacy_valid;
/* workaround flag */
u32 wa_flags;
- enum cc2_sink_type cc2_sink_detach_flag;
+ bool cc2_detach_wa_active;
+ bool typec_en_dis_active;
int boost_current_ua;
int temp_speed_reading_count;
diff --git a/drivers/regulator/qpnp-labibb-regulator.c b/drivers/regulator/qpnp-labibb-regulator.c
index dbe2a08f1776..858ddcc228df 100644
--- a/drivers/regulator/qpnp-labibb-regulator.c
+++ b/drivers/regulator/qpnp-labibb-regulator.c
@@ -559,6 +559,7 @@ struct lab_regulator {
int step_size;
int slew_rate;
int soft_start;
+ int sc_wait_time_ms;
int vreg_enabled;
};
@@ -608,6 +609,8 @@ struct qpnp_labibb {
bool skip_2nd_swire_cmd;
bool pfm_enable;
bool notify_lab_vreg_ok_sts;
+ bool detect_lab_sc;
+ bool sc_detected;
u32 swire_2nd_cmd_delay;
u32 swire_ibb_ps_enable_delay;
};
@@ -2138,8 +2141,10 @@ static void qpnp_lab_vreg_notifier_work(struct work_struct *work)
u8 val;
struct qpnp_labibb *labibb = container_of(work, struct qpnp_labibb,
lab_vreg_ok_work);
+ if (labibb->lab_vreg.sc_wait_time_ms != -EINVAL)
+ retries = labibb->lab_vreg.sc_wait_time_ms / 5;
- while (retries--) {
+ while (retries) {
rc = qpnp_labibb_read(labibb, labibb->lab_base +
REG_LAB_STATUS1, &val, 1);
if (rc < 0) {
@@ -2155,10 +2160,30 @@ static void qpnp_lab_vreg_notifier_work(struct work_struct *work)
}
usleep_range(dly, dly + 100);
+ retries--;
}
- if (!retries)
- pr_err("LAB_VREG_OK not set, failed to notify\n");
+ if (!retries) {
+ if (labibb->detect_lab_sc) {
+ pr_crit("short circuit detected on LAB rail.. disabling the LAB/IBB/OLEDB modules\n");
+ /* Disable LAB module */
+ val = 0;
+ rc = qpnp_labibb_write(labibb, labibb->lab_base +
+ REG_LAB_MODULE_RDY, &val, 1);
+ if (rc < 0) {
+ pr_err("write register %x failed rc = %d\n",
+ REG_LAB_MODULE_RDY, rc);
+ return;
+ }
+ raw_notifier_call_chain(&labibb_notifier,
+ LAB_VREG_NOT_OK, NULL);
+ labibb->sc_detected = true;
+ labibb->lab_vreg.vreg_enabled = 0;
+ labibb->ibb_vreg.vreg_enabled = 0;
+ } else {
+ pr_err("LAB_VREG_OK not set, failed to notify\n");
+ }
+ }
}
static int qpnp_labibb_regulator_enable(struct qpnp_labibb *labibb)
@@ -2323,6 +2348,11 @@ static int qpnp_lab_regulator_enable(struct regulator_dev *rdev)
struct qpnp_labibb *labibb = rdev_get_drvdata(rdev);
+ if (labibb->sc_detected) {
+ pr_info("Short circuit detected: disabled LAB/IBB rails\n");
+ return 0;
+ }
+
if (labibb->skip_2nd_swire_cmd) {
rc = qpnp_ibb_ps_config(labibb, false);
if (rc < 0) {
@@ -2363,7 +2393,7 @@ static int qpnp_lab_regulator_enable(struct regulator_dev *rdev)
labibb->lab_vreg.vreg_enabled = 1;
}
- if (labibb->notify_lab_vreg_ok_sts)
+ if (labibb->notify_lab_vreg_ok_sts || labibb->detect_lab_sc)
schedule_work(&labibb->lab_vreg_ok_work);
return 0;
@@ -2621,6 +2651,12 @@ static int register_qpnp_lab_regulator(struct qpnp_labibb *labibb,
labibb->notify_lab_vreg_ok_sts = of_property_read_bool(of_node,
"qcom,notify-lab-vreg-ok-sts");
+ labibb->lab_vreg.sc_wait_time_ms = -EINVAL;
+ if (labibb->pmic_rev_id->pmic_subtype == PM660L_SUBTYPE &&
+ labibb->detect_lab_sc)
+ of_property_read_u32(of_node, "qcom,qpnp-lab-sc-wait-time-ms",
+ &labibb->lab_vreg.sc_wait_time_ms);
+
rc = of_property_read_u32(of_node, "qcom,qpnp-lab-soft-start",
&(labibb->lab_vreg.soft_start));
if (!rc) {
@@ -3255,6 +3291,11 @@ static int qpnp_ibb_regulator_enable(struct regulator_dev *rdev)
u8 val;
struct qpnp_labibb *labibb = rdev_get_drvdata(rdev);
+ if (labibb->sc_detected) {
+ pr_info("Short circuit detected: disabled LAB/IBB rails\n");
+ return 0;
+ }
+
if (!labibb->ibb_vreg.vreg_enabled && !labibb->swire_control) {
if (!labibb->standalone)
@@ -3731,6 +3772,8 @@ static int qpnp_labibb_regulator_probe(struct platform_device *pdev)
if (labibb->pmic_rev_id->pmic_subtype == PM660L_SUBTYPE) {
labibb->mode = QPNP_LABIBB_AMOLED_MODE;
+ /* Enable polling for LAB short circuit detection for PM660A */
+ labibb->detect_lab_sc = true;
} else {
rc = of_property_read_string(labibb->dev->of_node,
"qcom,qpnp-labibb-mode", &mode_name);
diff --git a/drivers/regulator/qpnp-oledb-regulator.c b/drivers/regulator/qpnp-oledb-regulator.c
index c012f373e80e..bee9a3d82eeb 100644
--- a/drivers/regulator/qpnp-oledb-regulator.c
+++ b/drivers/regulator/qpnp-oledb-regulator.c
@@ -27,6 +27,7 @@
#include <linux/regulator/of_regulator.h>
#include <linux/regulator/qpnp-labibb-regulator.h>
#include <linux/qpnp/qpnp-pbs.h>
+#include <linux/qpnp/qpnp-revid.h>
#define QPNP_OLEDB_REGULATOR_DRIVER_NAME "qcom,qpnp-oledb-regulator"
#define OLEDB_VOUT_STEP_MV 100
@@ -162,6 +163,7 @@ struct qpnp_oledb {
struct notifier_block oledb_nb;
struct mutex bus_lock;
struct device_node *pbs_dev_node;
+ struct pmic_revid_data *pmic_rev_id;
u32 base;
u8 mod_enable;
@@ -181,6 +183,8 @@ struct qpnp_oledb {
bool dynamic_ext_pinctl_config;
bool pbs_control;
bool force_pd_control;
+ bool handle_lab_sc_notification;
+ bool lab_sc_detected;
};
static const u16 oledb_warmup_dly_ns[] = {6700, 13300, 26700, 53400};
@@ -275,6 +279,11 @@ static int qpnp_oledb_regulator_enable(struct regulator_dev *rdev)
struct qpnp_oledb *oledb = rdev_get_drvdata(rdev);
+ if (oledb->lab_sc_detected == true) {
+ pr_info("Short circuit detected: Disabled OLEDB rail\n");
+ return 0;
+ }
+
if (oledb->ext_pin_control) {
rc = qpnp_oledb_read(oledb, oledb->base + OLEDB_EXT_PIN_CTL,
&val, 1);
@@ -368,12 +377,19 @@ static int qpnp_oledb_regulator_disable(struct regulator_dev *rdev)
}
if (val & OLEDB_FORCE_PD_CTL_SPARE_BIT) {
- rc = qpnp_pbs_trigger_event(oledb->pbs_dev_node,
- trigger_bitmap);
+ rc = qpnp_oledb_sec_masked_write(oledb, oledb->base +
+ OLEDB_SPARE_CTL,
+ OLEDB_FORCE_PD_CTL_SPARE_BIT, 0);
if (rc < 0) {
- pr_err("Failed to trigger the PBS sequence\n");
+ pr_err("Failed to write SPARE_CTL rc=%d\n", rc);
return rc;
}
+
+ rc = qpnp_pbs_trigger_event(oledb->pbs_dev_node,
+ trigger_bitmap);
+ if (rc < 0)
+ pr_err("Failed to trigger the PBS sequence\n");
+
pr_debug("PBS event triggered\n");
} else {
pr_debug("OLEDB_SPARE_CTL register bit not set\n");
@@ -1085,8 +1101,22 @@ static int qpnp_oledb_parse_fast_precharge(struct qpnp_oledb *oledb)
static int qpnp_oledb_parse_dt(struct qpnp_oledb *oledb)
{
int rc = 0;
+ struct device_node *revid_dev_node;
struct device_node *of_node = oledb->dev->of_node;
+ revid_dev_node = of_parse_phandle(oledb->dev->of_node,
+ "qcom,pmic-revid", 0);
+ if (!revid_dev_node) {
+ pr_err("Missing qcom,pmic-revid property - driver failed\n");
+ return -EINVAL;
+ }
+
+ oledb->pmic_rev_id = get_revid_data(revid_dev_node);
+ if (IS_ERR(oledb->pmic_rev_id)) {
+ pr_debug("Unable to get revid data\n");
+ return -EPROBE_DEFER;
+ }
+
oledb->swire_control =
of_property_read_bool(of_node, "qcom,swire-control");
@@ -1100,8 +1130,14 @@ static int qpnp_oledb_parse_dt(struct qpnp_oledb *oledb)
oledb->pbs_control =
of_property_read_bool(of_node, "qcom,pbs-control");
- oledb->force_pd_control =
- of_property_read_bool(of_node, "qcom,force-pd-control");
+ /* Use the force_pd_control only for PM660A versions <= v2.0 */
+ if (oledb->pmic_rev_id->pmic_subtype == PM660L_SUBTYPE &&
+ oledb->pmic_rev_id->rev4 <= PM660L_V2P0_REV4) {
+ if (!(oledb->pmic_rev_id->rev4 == PM660L_V2P0_REV4 &&
+ oledb->pmic_rev_id->rev2 > PM660L_V2P0_REV2)) {
+ oledb->force_pd_control = true;
+ }
+ }
if (oledb->force_pd_control) {
oledb->pbs_dev_node = of_parse_phandle(of_node,
@@ -1199,13 +1235,6 @@ static int qpnp_oledb_force_pulldown_config(struct qpnp_oledb *oledb)
int rc = 0;
u8 val;
- rc = qpnp_oledb_sec_masked_write(oledb, oledb->base +
- OLEDB_SPARE_CTL, OLEDB_FORCE_PD_CTL_SPARE_BIT, 0);
- if (rc < 0) {
- pr_err("Failed to write SPARE_CTL rc=%d\n", rc);
- return rc;
- }
-
val = 1;
rc = qpnp_oledb_write(oledb, oledb->base + OLEDB_PD_CTL,
&val, 1);
@@ -1227,14 +1256,31 @@ static int qpnp_labibb_notifier_cb(struct notifier_block *nb,
unsigned long action, void *data)
{
int rc = 0;
+ u8 val;
struct qpnp_oledb *oledb = container_of(nb, struct qpnp_oledb,
oledb_nb);
+ if (action == LAB_VREG_NOT_OK) {
+ /* short circuit detected. Disable OLEDB module */
+ val = 0;
+ rc = qpnp_oledb_write(oledb, oledb->base + OLEDB_MODULE_RDY,
+ &val, 1);
+ if (rc < 0) {
+ pr_err("Failed to write MODULE_RDY rc=%d\n", rc);
+ return NOTIFY_STOP;
+ }
+ oledb->lab_sc_detected = true;
+ oledb->mod_enable = false;
+ pr_crit("LAB SC detected, disabling OLEDB forever!\n");
+ }
+
if (action == LAB_VREG_OK) {
/* Disable SWIRE pull down control and enable via spmi mode */
rc = qpnp_oledb_force_pulldown_config(oledb);
- if (rc < 0)
+ if (rc < 0) {
+ pr_err("Failed to config force pull down\n");
return NOTIFY_STOP;
+ }
}
return NOTIFY_OK;
@@ -1281,7 +1327,11 @@ static int qpnp_oledb_regulator_probe(struct platform_device *pdev)
return rc;
}
- if (oledb->force_pd_control) {
+ /* Enable LAB short circuit notification support */
+ if (oledb->pmic_rev_id->pmic_subtype == PM660L_SUBTYPE)
+ oledb->handle_lab_sc_notification = true;
+
+ if (oledb->force_pd_control || oledb->handle_lab_sc_notification) {
oledb->oledb_nb.notifier_call = qpnp_labibb_notifier_cb;
rc = qpnp_labibb_notifier_register(&oledb->oledb_nb);
if (rc < 0) {
diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c
index 79bb3337ba36..c5393d517432 100644
--- a/drivers/scsi/ufs/ufshcd.c
+++ b/drivers/scsi/ufs/ufshcd.c
@@ -9238,10 +9238,11 @@ static int ufshcd_scale_gear(struct ufs_hba *hba, bool scale_up)
/* scale up to G3 now */
new_pwr_info.gear_tx = UFS_HS_G3;
new_pwr_info.gear_rx = UFS_HS_G3;
- ret = ufshcd_change_power_mode(hba, &new_pwr_info);
- if (ret)
- goto out;
+ /* now, fall through to set the HS-G3 */
}
+ ret = ufshcd_change_power_mode(hba, &new_pwr_info);
+ if (ret)
+ goto out;
} else {
memcpy(&new_pwr_info, &hba->pwr_info,
sizeof(struct ufs_pa_layer_attr));
diff --git a/drivers/soc/qcom/Kconfig b/drivers/soc/qcom/Kconfig
index 75bebf66376d..34b0adb108eb 100644
--- a/drivers/soc/qcom/Kconfig
+++ b/drivers/soc/qcom/Kconfig
@@ -441,6 +441,23 @@ config QCOM_MEMORY_DUMP_V2
of deadlocks or cpu hangs these dump regions are captured to
give a snapshot of the system at the time of the crash.
+config QCOM_MINIDUMP
+ bool "QCOM Minidump Support"
+ depends on MSM_SMEM && QCOM_DLOAD_MODE
+ help
+ This enables minidump feature. It allows various clients to
+ register to dump their state at system bad state (panic/WDT,etc.,).
+ This uses SMEM to store all registered client information.
+ This will dump all registered entries, only when DLOAD mode is enabled.
+
+config MINIDUMP_MAX_ENTRIES
+ int "Minidump Maximum num of entries"
+ default 200
+ depends on QCOM_MINIDUMP
+ help
+ This defines maximum number of entries to be allocated for application
+ subsytem in Minidump SMEM table.
+
config ICNSS
tristate "Platform driver for Q6 integrated connectivity"
---help---
diff --git a/drivers/soc/qcom/Makefile b/drivers/soc/qcom/Makefile
index fa350d122384..87698b75d3b8 100644
--- a/drivers/soc/qcom/Makefile
+++ b/drivers/soc/qcom/Makefile
@@ -66,6 +66,7 @@ obj-$(CONFIG_QCOM_SCM_XPU) += scm-xpu.o
obj-$(CONFIG_QCOM_WATCHDOG_V2) += watchdog_v2.o
obj-$(CONFIG_QCOM_MEMORY_DUMP) += memory_dump.o
obj-$(CONFIG_QCOM_MEMORY_DUMP_V2) += memory_dump_v2.o
+obj-$(CONFIG_QCOM_MINIDUMP) += msm_minidump.o
obj-$(CONFIG_QCOM_DCC) += dcc.o
obj-$(CONFIG_QCOM_WATCHDOG_V2) += watchdog_v2.o
obj-$(CONFIG_QCOM_COMMON_LOG) += common_log.o
diff --git a/drivers/soc/qcom/common_log.c b/drivers/soc/qcom/common_log.c
index ecf89b2b3b37..f001e820b797 100644
--- a/drivers/soc/qcom/common_log.c
+++ b/drivers/soc/qcom/common_log.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2014-2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2014-2017, 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
@@ -18,6 +18,8 @@
#include <linux/slab.h>
#include <linux/kmemleak.h>
#include <soc/qcom/memory_dump.h>
+#include <soc/qcom/minidump.h>
+#include <asm/sections.h>
#define MISC_DUMP_DATA_LEN 4096
#define PMIC_DUMP_DATA_LEN (64 * 1024)
@@ -38,6 +40,8 @@ void register_misc_dump(void)
misc_buf = kzalloc(MISC_DUMP_DATA_LEN, GFP_KERNEL);
if (!misc_buf)
goto err0;
+
+ strlcpy(misc_data->name, "KMISC", sizeof(misc_data->name));
misc_data->addr = virt_to_phys(misc_buf);
misc_data->len = MISC_DUMP_DATA_LEN;
dump_entry.id = MSM_DUMP_DATA_MISC;
@@ -70,6 +74,7 @@ static void register_pmic_dump(void)
if (!dump_addr)
goto err0;
+ strlcpy(dump_data->name, "KPMIC", sizeof(dump_data->name));
dump_data->addr = virt_to_phys(dump_addr);
dump_data->len = PMIC_DUMP_DATA_LEN;
dump_entry.id = MSM_DUMP_DATA_PMIC;
@@ -104,6 +109,8 @@ static void register_vsense_dump(void)
if (!dump_addr)
goto err0;
+ strlcpy(dump_data->name, "KVSENSE",
+ sizeof(dump_data->name));
dump_data->addr = virt_to_phys(dump_addr);
dump_data->len = VSENSE_DUMP_DATA_LEN;
dump_entry.id = MSM_DUMP_DATA_VSENSE;
@@ -136,6 +143,7 @@ void register_rpm_dump(void)
if (!dump_addr)
goto err0;
+ strlcpy(dump_data->name, "KRPM", sizeof(dump_data->name));
dump_data->addr = virt_to_phys(dump_addr);
dump_data->len = RPM_DUMP_DATA_LEN;
dump_entry.id = MSM_DUMP_DATA_RPM;
@@ -217,8 +225,39 @@ static void __init common_log_register_log_buf(void)
}
}
+static void __init register_kernel_sections(void)
+{
+ struct md_region ksec_entry;
+ char *data_name = "KDATABSS";
+ const size_t static_size = __per_cpu_end - __per_cpu_start;
+ void __percpu *base = (void __percpu *)__per_cpu_start;
+ unsigned int cpu;
+
+ strlcpy(ksec_entry.name, data_name, sizeof(ksec_entry.name));
+ ksec_entry.virt_addr = (uintptr_t)_sdata;
+ ksec_entry.phys_addr = virt_to_phys(_sdata);
+ ksec_entry.size = roundup((__bss_stop - _sdata), 4);
+ if (msm_minidump_add_region(&ksec_entry))
+ pr_err("Failed to add data section in Minidump\n");
+
+ /* Add percpu static sections */
+ for_each_possible_cpu(cpu) {
+ void *start = per_cpu_ptr(base, cpu);
+
+ memset(&ksec_entry, 0, sizeof(ksec_entry));
+ scnprintf(ksec_entry.name, sizeof(ksec_entry.name),
+ "KSPERCPU%d", cpu);
+ ksec_entry.virt_addr = (uintptr_t)start;
+ ksec_entry.phys_addr = per_cpu_ptr_to_phys(start);
+ ksec_entry.size = static_size;
+ if (msm_minidump_add_region(&ksec_entry))
+ pr_err("Failed to add percpu sections in Minidump\n");
+ }
+}
+
static int __init msm_common_log_init(void)
{
+ register_kernel_sections();
common_log_register_log_buf();
register_misc_dump();
register_pmic_dump();
diff --git a/drivers/soc/qcom/cpuss_dump.c b/drivers/soc/qcom/cpuss_dump.c
index 93c876a7ad73..2e08b78dee94 100644
--- a/drivers/soc/qcom/cpuss_dump.c
+++ b/drivers/soc/qcom/cpuss_dump.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2014-2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2014-2017, 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
@@ -75,6 +75,8 @@ static int cpuss_dump_probe(struct platform_device *pdev)
dump_data->addr = dump_addr;
dump_data->len = size;
+ scnprintf(dump_data->name, sizeof(dump_data->name),
+ "KCPUSS%X", id);
dump_entry.id = id;
dump_entry.addr = virt_to_phys(dump_data);
ret = msm_dump_data_register(MSM_DUMP_TABLE_APPS, &dump_entry);
diff --git a/drivers/soc/qcom/dcc.c b/drivers/soc/qcom/dcc.c
index aced50bf7fda..e5f3f119065b 100644
--- a/drivers/soc/qcom/dcc.c
+++ b/drivers/soc/qcom/dcc.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2015-2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2015-2017, 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
@@ -1173,6 +1173,8 @@ static void dcc_allocate_dump_mem(struct dcc_drvdata *drvdata)
/* Allocate memory for dcc reg dump */
drvdata->reg_buf = devm_kzalloc(dev, drvdata->reg_size, GFP_KERNEL);
if (drvdata->reg_buf) {
+ strlcpy(drvdata->reg_data.name, "KDCC_REG",
+ sizeof(drvdata->reg_data.name));
drvdata->reg_data.addr = virt_to_phys(drvdata->reg_buf);
drvdata->reg_data.len = drvdata->reg_size;
reg_dump_entry.id = MSM_DUMP_DATA_DCC_REG;
@@ -1190,6 +1192,8 @@ static void dcc_allocate_dump_mem(struct dcc_drvdata *drvdata)
/* Allocate memory for dcc sram dump */
drvdata->sram_buf = devm_kzalloc(dev, drvdata->ram_size, GFP_KERNEL);
if (drvdata->sram_buf) {
+ strlcpy(drvdata->sram_data.name, "KDCC_SRAM",
+ sizeof(drvdata->sram_data.name));
drvdata->sram_data.addr = virt_to_phys(drvdata->sram_buf);
drvdata->sram_data.len = drvdata->ram_size;
sram_dump_entry.id = MSM_DUMP_DATA_DCC_SRAM;
diff --git a/drivers/soc/qcom/glink_smem_native_xprt.c b/drivers/soc/qcom/glink_smem_native_xprt.c
index 85d51807077c..3f969234b705 100644
--- a/drivers/soc/qcom/glink_smem_native_xprt.c
+++ b/drivers/soc/qcom/glink_smem_native_xprt.c
@@ -678,7 +678,8 @@ static void process_rx_data(struct edge_info *einfo, uint16_t cmd_id,
err = true;
} else if (intent->data == NULL) {
if (einfo->intentless) {
- intent->data = kmalloc(cmd.frag_size, GFP_ATOMIC);
+ intent->data = kmalloc(cmd.frag_size,
+ __GFP_ATOMIC | __GFP_HIGH);
if (!intent->data) {
err = true;
GLINK_ERR(
diff --git a/drivers/soc/qcom/memory_dump_v2.c b/drivers/soc/qcom/memory_dump_v2.c
index af141c808c81..092b1c1af44b 100644
--- a/drivers/soc/qcom/memory_dump_v2.c
+++ b/drivers/soc/qcom/memory_dump_v2.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2014-2015, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2014-2015, 2017, 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
@@ -20,6 +20,7 @@
#include <linux/kmemleak.h>
#include <soc/qcom/memory_dump.h>
#include <soc/qcom/scm.h>
+#include <soc/qcom/minidump.h>
#define MSM_DUMP_TABLE_VERSION MSM_DUMP_MAKE_VERSION(2, 0)
@@ -87,6 +88,29 @@ static struct msm_dump_table *msm_dump_get_table(enum msm_dump_table_ids id)
return table;
}
+int msm_dump_data_add_minidump(struct msm_dump_entry *entry)
+{
+ struct msm_dump_data *data;
+ struct md_region md_entry;
+
+ data = (struct msm_dump_data *)(phys_to_virt(entry->addr));
+ if (!strcmp(data->name, "")) {
+ pr_info("Entry name is NULL, Use ID %d for minidump\n",
+ entry->id);
+ snprintf(md_entry.name, sizeof(md_entry.name), "KMDT0x%X",
+ entry->id);
+ } else {
+ strlcpy(md_entry.name, data->name, sizeof(md_entry.name));
+ }
+
+ md_entry.phys_addr = data->addr;
+ md_entry.virt_addr = (uintptr_t)phys_to_virt(data->addr);
+ md_entry.size = data->len;
+ md_entry.id = entry->id;
+
+ return msm_minidump_add_region(&md_entry);
+}
+
int msm_dump_data_register(enum msm_dump_table_ids id,
struct msm_dump_entry *entry)
{
@@ -107,6 +131,10 @@ int msm_dump_data_register(enum msm_dump_table_ids id,
table->num_entries++;
dmac_flush_range(table, (void *)table + sizeof(struct msm_dump_table));
+
+ if (msm_dump_data_add_minidump(entry))
+ pr_info("Failed to add entry in Minidump table\n");
+
return 0;
}
EXPORT_SYMBOL(msm_dump_data_register);
diff --git a/drivers/soc/qcom/msm_minidump.c b/drivers/soc/qcom/msm_minidump.c
new file mode 100644
index 000000000000..1cb36bf98555
--- /dev/null
+++ b/drivers/soc/qcom/msm_minidump.c
@@ -0,0 +1,371 @@
+/* Copyright (c) 2017, 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.
+ */
+#define pr_fmt(fmt) "Minidump: " fmt
+
+#include <linux/init.h>
+#include <linux/export.h>
+#include <linux/kernel.h>
+#include <linux/err.h>
+#include <linux/elf.h>
+#include <linux/errno.h>
+#include <linux/string.h>
+#include <linux/slab.h>
+#include <soc/qcom/smem.h>
+#include <soc/qcom/scm.h>
+#include <soc/qcom/minidump.h>
+
+
+#define MAX_NUM_ENTRIES (CONFIG_MINIDUMP_MAX_ENTRIES + 1)
+#define SMEM_ENTRY_SIZE 32
+#define MAX_MEM_LENGTH (SMEM_ENTRY_SIZE * MAX_NUM_ENTRIES)
+#define MAX_STRTBL_SIZE (MAX_NUM_ENTRIES * MAX_NAME_LENGTH)
+#define SMEM_MINIDUMP_TABLE_ID 602
+
+/* Bootloader Minidump table */
+struct md_smem_table {
+ u32 version;
+ u32 smem_length;
+ u64 next_avail_offset;
+ char reserved[MAX_NAME_LENGTH];
+ u64 *region_start;
+};
+
+/* Bootloader Minidump region */
+struct md_smem_region {
+ char name[MAX_NAME_LENGTH];
+ u64 address;
+ u64 size;
+};
+
+/* md_table: APPS minidump table
+ * @num_regions: Number of entries registered
+ * @region_base_offset: APPS region start offset smem table
+ * @md_smem_table: Pointer smem table
+ * @region: Pointer to APPS region in smem table
+ * @entry: All registered client entries.
+ */
+
+struct md_table {
+ u32 num_regions;
+ u32 region_base_offset;
+ struct md_smem_table *md_smem_table;
+ struct md_smem_region *region;
+ struct md_region entry[MAX_NUM_ENTRIES];
+};
+
+/* Protect elfheader and smem table from deferred calls contention */
+static DEFINE_SPINLOCK(mdt_lock);
+static bool minidump_enabled;
+static struct md_table minidump_table;
+static unsigned int pendings;
+static unsigned int region_idx = 1; /* First entry is ELF header*/
+
+/* ELF Header */
+static struct elfhdr *md_ehdr;
+/* ELF Program header */
+static struct elf_phdr *phdr;
+/* ELF Section header */
+static struct elf_shdr *shdr;
+/* Section offset in elf image */
+static u64 elf_offset;
+/* String table index, first byte must be '\0' */
+static unsigned int stringtable_idx = 1;
+
+static inline struct elf_shdr *elf_sheader(struct elfhdr *hdr)
+{
+ return (struct elf_shdr *)((size_t)hdr + (size_t)hdr->e_shoff);
+}
+
+static inline struct elf_shdr *elf_section(struct elfhdr *hdr, int idx)
+{
+ return &elf_sheader(hdr)[idx];
+}
+
+static inline char *elf_str_table(struct elfhdr *hdr)
+{
+ if (hdr->e_shstrndx == SHN_UNDEF)
+ return NULL;
+ return (char *)hdr + elf_section(hdr, hdr->e_shstrndx)->sh_offset;
+}
+
+static inline char *elf_lookup_string(struct elfhdr *hdr, int offset)
+{
+ char *strtab = elf_str_table(hdr);
+
+ if ((strtab == NULL) | (stringtable_idx < offset))
+ return NULL;
+ return strtab + offset;
+}
+
+static inline unsigned int set_section_name(const char *name)
+{
+ char *strtab = elf_str_table(md_ehdr);
+ int ret = 0;
+
+ if ((strtab == NULL) | (name == NULL))
+ return 0;
+
+ ret = stringtable_idx;
+ stringtable_idx += strlcpy((strtab + stringtable_idx),
+ name, MAX_NAME_LENGTH);
+ stringtable_idx += 1;
+ return ret;
+}
+
+/* return 1 if name already exists */
+static inline bool md_check_name(const char *name)
+{
+ struct md_region *mde = minidump_table.entry;
+ int i, regno = minidump_table.num_regions;
+
+ for (i = 0; i < regno; i++, mde++)
+ if (!strcmp(mde->name, name))
+ return true;
+ return false;
+}
+
+/* Update Mini dump table in SMEM */
+static int md_update_smem_table(const struct md_region *entry)
+{
+ struct md_smem_region *mdr;
+
+ if (!minidump_enabled) {
+ pr_info("Table in smem is not setup\n");
+ return -ENODEV;
+ }
+
+ mdr = &minidump_table.region[region_idx++];
+
+ strlcpy(mdr->name, entry->name, sizeof(mdr->name));
+ mdr->address = entry->phys_addr;
+ mdr->size = entry->size;
+
+ /* Update elf header */
+ shdr->sh_type = SHT_PROGBITS;
+ shdr->sh_name = set_section_name(mdr->name);
+ shdr->sh_addr = (elf_addr_t)entry->virt_addr;
+ shdr->sh_size = mdr->size;
+ shdr->sh_flags = SHF_WRITE;
+ shdr->sh_offset = elf_offset;
+ shdr->sh_entsize = 0;
+
+ phdr->p_type = PT_LOAD;
+ phdr->p_offset = elf_offset;
+ phdr->p_vaddr = entry->virt_addr;
+ phdr->p_paddr = entry->phys_addr;
+ phdr->p_filesz = phdr->p_memsz = mdr->size;
+ phdr->p_flags = PF_R | PF_W;
+
+ md_ehdr->e_shnum += 1;
+ md_ehdr->e_phnum += 1;
+ elf_offset += shdr->sh_size;
+ shdr++;
+ phdr++;
+
+ return 0;
+}
+
+bool msm_minidump_enabled(void)
+{
+ bool ret;
+
+ spin_lock(&mdt_lock);
+ ret = minidump_enabled;
+ spin_unlock(&mdt_lock);
+ return ret;
+}
+EXPORT_SYMBOL(msm_minidump_enabled);
+
+int msm_minidump_add_region(const struct md_region *entry)
+{
+ u32 entries;
+ struct md_region *mdr;
+ int ret = 0;
+
+ if (!entry)
+ return -EINVAL;
+
+ if (((strlen(entry->name) > MAX_NAME_LENGTH) ||
+ md_check_name(entry->name)) && !entry->virt_addr) {
+ pr_info("Invalid entry details\n");
+ return -EINVAL;
+ }
+
+ if (!IS_ALIGNED(entry->size, 4)) {
+ pr_info("size should be 4 byte aligned\n");
+ return -EINVAL;
+ }
+
+ spin_lock(&mdt_lock);
+ entries = minidump_table.num_regions;
+ if (entries >= MAX_NUM_ENTRIES) {
+ pr_info("Maximum entries reached.\n");
+ spin_unlock(&mdt_lock);
+ return -ENOMEM;
+ }
+
+ mdr = &minidump_table.entry[entries];
+ strlcpy(mdr->name, entry->name, sizeof(mdr->name));
+ mdr->virt_addr = entry->virt_addr;
+ mdr->phys_addr = entry->phys_addr;
+ mdr->size = entry->size;
+ mdr->id = entry->id;
+
+ minidump_table.num_regions = entries + 1;
+
+ if (minidump_enabled)
+ ret = md_update_smem_table(entry);
+ else
+ pendings++;
+
+ spin_unlock(&mdt_lock);
+
+ pr_debug("Minidump: added %s to %s list\n",
+ mdr->name, minidump_enabled ? "":"pending");
+ return ret;
+}
+EXPORT_SYMBOL(msm_minidump_add_region);
+
+static int msm_minidump_add_header(void)
+{
+ struct md_smem_region *mdreg = &minidump_table.region[0];
+ char *banner;
+ unsigned int strtbl_off, elfh_size, phdr_off;
+
+ elfh_size = sizeof(*md_ehdr) + MAX_STRTBL_SIZE + MAX_MEM_LENGTH +
+ ((sizeof(*shdr) + sizeof(*phdr)) * (MAX_NUM_ENTRIES + 1));
+
+ md_ehdr = kzalloc(elfh_size, GFP_KERNEL);
+ if (!md_ehdr)
+ return -ENOMEM;
+
+ strlcpy(mdreg->name, "KELF_HEADER", sizeof(mdreg->name));
+ mdreg->address = virt_to_phys(md_ehdr);
+ mdreg->size = elfh_size;
+
+ /* Section headers*/
+ shdr = (struct elf_shdr *)(md_ehdr + 1);
+ phdr = (struct elf_phdr *)(shdr + MAX_NUM_ENTRIES);
+ phdr_off = sizeof(*md_ehdr) + (sizeof(*shdr) * MAX_NUM_ENTRIES);
+
+ memcpy(md_ehdr->e_ident, ELFMAG, SELFMAG);
+ md_ehdr->e_ident[EI_CLASS] = ELF_CLASS;
+ md_ehdr->e_ident[EI_DATA] = ELF_DATA;
+ md_ehdr->e_ident[EI_VERSION] = EV_CURRENT;
+ md_ehdr->e_ident[EI_OSABI] = ELF_OSABI;
+ md_ehdr->e_type = ET_CORE;
+ md_ehdr->e_machine = ELF_ARCH;
+ md_ehdr->e_version = EV_CURRENT;
+ md_ehdr->e_ehsize = sizeof(*md_ehdr);
+ md_ehdr->e_phoff = phdr_off;
+ md_ehdr->e_phentsize = sizeof(*phdr);
+ md_ehdr->e_phnum = 1;
+ md_ehdr->e_shoff = sizeof(*md_ehdr);
+ md_ehdr->e_shentsize = sizeof(*shdr);
+ md_ehdr->e_shnum = 3; /* NULL, STR TABLE, Linux banner */
+ md_ehdr->e_shstrndx = 1;
+
+ elf_offset = elfh_size;
+ strtbl_off = sizeof(*md_ehdr) +
+ ((sizeof(*phdr) + sizeof(*shdr)) * MAX_NUM_ENTRIES);
+ /* First section header should be NULL
+ * 2nd entry for string table
+ */
+ shdr++;
+ shdr->sh_type = SHT_STRTAB;
+ shdr->sh_offset = (elf_addr_t)strtbl_off;
+ shdr->sh_size = MAX_STRTBL_SIZE;
+ shdr->sh_entsize = 0;
+ shdr->sh_flags = 0;
+ shdr->sh_name = set_section_name("STR_TBL");
+ shdr++;
+
+ /* 3rd entry for linux banner */
+ banner = (char *)md_ehdr + strtbl_off + MAX_STRTBL_SIZE;
+ strlcpy(banner, linux_banner, MAX_MEM_LENGTH);
+
+ shdr->sh_type = SHT_PROGBITS;
+ shdr->sh_offset = (elf_addr_t)(strtbl_off + MAX_STRTBL_SIZE);
+ shdr->sh_size = strlen(linux_banner) + 1;
+ shdr->sh_addr = (elf_addr_t)linux_banner;
+ shdr->sh_entsize = 0;
+ shdr->sh_flags = SHF_WRITE;
+ shdr->sh_name = set_section_name("linux_banner");
+ shdr++;
+
+ phdr->p_type = PT_LOAD;
+ phdr->p_offset = (elf_addr_t)(strtbl_off + MAX_STRTBL_SIZE);
+ phdr->p_vaddr = (elf_addr_t)linux_banner;
+ phdr->p_paddr = virt_to_phys(linux_banner);
+ phdr->p_filesz = phdr->p_memsz = strlen(linux_banner) + 1;
+ phdr->p_flags = PF_R | PF_W;
+
+ md_ehdr->e_phnum += 1;
+ phdr++;
+
+ return 0;
+}
+
+static int __init msm_minidump_init(void)
+{
+ unsigned int i, size;
+ struct md_region *mdr;
+ struct md_smem_table *smem_table;
+
+ /* Get Minidump table */
+ smem_table = smem_get_entry(SMEM_MINIDUMP_TABLE_ID, &size, 0,
+ SMEM_ANY_HOST_FLAG);
+ if (IS_ERR_OR_NULL(smem_table)) {
+ pr_info("SMEM is not initialized.\n");
+ return -ENODEV;
+ }
+
+ if ((smem_table->next_avail_offset + MAX_MEM_LENGTH) >
+ smem_table->smem_length) {
+ pr_info("SMEM memory not available.\n");
+ return -ENOMEM;
+ }
+
+ /* Get next_avail_offset and update it to reserve memory */
+ minidump_table.region_base_offset = smem_table->next_avail_offset;
+ minidump_table.region = (struct md_smem_region *)((uintptr_t)smem_table
+ + minidump_table.region_base_offset);
+
+ smem_table->next_avail_offset =
+ minidump_table.region_base_offset + MAX_MEM_LENGTH;
+ minidump_table.md_smem_table = smem_table;
+
+ msm_minidump_add_header();
+
+ /* Add pending entries to smem table */
+ spin_lock(&mdt_lock);
+ minidump_enabled = true;
+
+ for (i = 0; i < pendings; i++) {
+ mdr = &minidump_table.entry[i];
+ if (md_update_smem_table(mdr)) {
+ pr_info("Unable to add entry %s to smem table\n",
+ mdr->name);
+ spin_unlock(&mdt_lock);
+ return -ENODEV;
+ }
+ }
+
+ pendings = 0;
+ spin_unlock(&mdt_lock);
+
+ pr_info("Enabled, region base:%d, region 0x%pK\n",
+ minidump_table.region_base_offset, minidump_table.region);
+
+ return 0;
+}
+subsys_initcall(msm_minidump_init)
diff --git a/drivers/soc/qcom/pil-msa.c b/drivers/soc/qcom/pil-msa.c
index 53bddc5987df..988b6e8c9fd9 100644
--- a/drivers/soc/qcom/pil-msa.c
+++ b/drivers/soc/qcom/pil-msa.c
@@ -616,7 +616,15 @@ int pil_mss_reset_load_mba(struct pil_desc *pil)
/* Load the MBA image into memory */
count = fw->size;
- memcpy(mba_dp_virt, data, count);
+ if (count <= SZ_1M) {
+ /* Ensures memcpy is done for max 1MB fw size */
+ memcpy(mba_dp_virt, data, count);
+ } else {
+ dev_err(pil->dev, "%s fw image loading into memory is failed due to fw size overflow\n",
+ __func__);
+ ret = -EINVAL;
+ goto err_mba_data;
+ }
/* Ensure memcpy of the MBA memory is done before loading the DP */
wmb();
diff --git a/drivers/soc/qcom/qpnp-haptic.c b/drivers/soc/qcom/qpnp-haptic.c
index c86eebcd390f..70cf11359e97 100644
--- a/drivers/soc/qcom/qpnp-haptic.c
+++ b/drivers/soc/qcom/qpnp-haptic.c
@@ -138,7 +138,7 @@
#define QPNP_HAP_WAV_SAMP_MAX 0x7E
#define QPNP_HAP_BRAKE_PAT_LEN 4
#define QPNP_HAP_PLAY_EN 0x80
-#define QPNP_HAP_EN 0x80
+#define QPNP_HAP_EN_BIT BIT(7)
#define QPNP_HAP_BRAKE_MASK BIT(0)
#define QPNP_HAP_AUTO_RES_MASK BIT(7)
#define AUTO_RES_ENABLE BIT(7)
@@ -305,7 +305,6 @@ struct qpnp_pwm_info {
* @ wave_samp - array of wave samples
* @ shadow_wave_samp - shadow array of wave samples
* @ brake_pat - pattern for active breaking
- * @ reg_en_ctl - enable control register
* @ reg_play - play register
* @ lra_res_cal_period - period for resonance calibration
* @ sc_duration - counter to determine the duration of short circuit condition
@@ -358,6 +357,7 @@ struct qpnp_hap {
u32 sc_irq;
u32 status_flags;
u16 base;
+ u16 last_rate_cfg;
u16 drive_period_code_max_limit;
u16 drive_period_code_min_limit;
u16 lra_res_cal_period;
@@ -368,7 +368,6 @@ struct qpnp_hap {
u8 wave_samp[QPNP_HAP_WAV_SAMP_LEN];
u8 shadow_wave_samp[QPNP_HAP_WAV_SAMP_LEN];
u8 brake_pat[QPNP_HAP_BRAKE_PAT_LEN];
- u8 reg_en_ctl;
u8 reg_play;
u8 sc_duration;
u8 ext_pwm_dtest_line;
@@ -391,6 +390,18 @@ struct qpnp_hap {
static struct qpnp_hap *ghap;
/* helper to read a pmic register */
+static int qpnp_hap_read_mult_reg(struct qpnp_hap *hap, u16 addr, u8 *val,
+ int len)
+{
+ int rc;
+
+ rc = regmap_bulk_read(hap->regmap, addr, val, len);
+ if (rc < 0)
+ pr_err("Error reading address: %X - ret %X\n", addr, rc);
+
+ return rc;
+}
+
static int qpnp_hap_read_reg(struct qpnp_hap *hap, u16 addr, u8 *val)
{
int rc;
@@ -399,11 +410,28 @@ static int qpnp_hap_read_reg(struct qpnp_hap *hap, u16 addr, u8 *val)
rc = regmap_read(hap->regmap, addr, &tmp);
if (rc < 0)
pr_err("Error reading address: %X - ret %X\n", addr, rc);
- *val = (u8)tmp;
+ else
+ *val = (u8)tmp;
+
return rc;
}
/* helper to write a pmic register */
+static int qpnp_hap_write_mult_reg(struct qpnp_hap *hap, u16 addr, u8 *val,
+ int len)
+{
+ unsigned long flags;
+ int rc;
+
+ spin_lock_irqsave(&hap->bus_lock, flags);
+ rc = regmap_bulk_write(hap->regmap, addr, val, len);
+ if (rc < 0)
+ pr_err("Error writing address: %X - ret %X\n", addr, rc);
+
+ spin_unlock_irqrestore(&hap->bus_lock, flags);
+ return rc;
+}
+
static int qpnp_hap_write_reg(struct qpnp_hap *hap, u16 addr, u8 val)
{
unsigned long flags;
@@ -480,15 +508,12 @@ static void qpnp_handle_sc_irq(struct work_struct *work)
}
}
-static int qpnp_hap_mod_enable(struct qpnp_hap *hap, int on)
+static int qpnp_hap_mod_enable(struct qpnp_hap *hap, bool on)
{
u8 val;
int rc, i;
- val = hap->reg_en_ctl;
- if (on) {
- val |= QPNP_HAP_EN;
- } else {
+ if (!on) {
for (i = 0; i < QPNP_HAP_MAX_RETRIES; i++) {
/* wait for 4 cycles of play rate */
unsigned long sleep_time =
@@ -511,16 +536,13 @@ static int qpnp_hap_mod_enable(struct qpnp_hap *hap, int on)
if (i >= QPNP_HAP_MAX_RETRIES)
pr_debug("Haptics Busy. Force disable\n");
-
- val &= ~QPNP_HAP_EN;
}
+ val = on ? QPNP_HAP_EN_BIT : 0;
rc = qpnp_hap_write_reg(hap, QPNP_HAP_EN_CTL_REG(hap->base), val);
if (rc < 0)
return rc;
- hap->reg_en_ctl = val;
-
return 0;
}
@@ -1517,20 +1539,23 @@ static int qpnp_hap_auto_res_enable(struct qpnp_hap *hap, int enable)
static void update_lra_frequency(struct qpnp_hap *hap)
{
- u8 lra_auto_res_lo = 0, lra_auto_res_hi = 0, val;
+ u8 lra_auto_res[2], val;
u32 play_rate_code;
+ u16 rate_cfg;
int rc;
- qpnp_hap_read_reg(hap, QPNP_HAP_LRA_AUTO_RES_LO(hap->base),
- &lra_auto_res_lo);
- qpnp_hap_read_reg(hap, QPNP_HAP_LRA_AUTO_RES_HI(hap->base),
- &lra_auto_res_hi);
+ rc = qpnp_hap_read_mult_reg(hap, QPNP_HAP_LRA_AUTO_RES_LO(hap->base),
+ lra_auto_res, 2);
+ if (rc < 0) {
+ pr_err("Error in reading LRA_AUTO_RES_LO/HI, rc=%d\n", rc);
+ return;
+ }
play_rate_code =
- (lra_auto_res_hi & 0xF0) << 4 | (lra_auto_res_lo & 0xFF);
+ (lra_auto_res[1] & 0xF0) << 4 | (lra_auto_res[0] & 0xFF);
pr_debug("lra_auto_res_lo = 0x%x lra_auto_res_hi = 0x%x play_rate_code = 0x%x\n",
- lra_auto_res_lo, lra_auto_res_hi, play_rate_code);
+ lra_auto_res[0], lra_auto_res[1], play_rate_code);
rc = qpnp_hap_read_reg(hap, QPNP_HAP_STATUS(hap->base), &val);
if (rc < 0)
@@ -1559,12 +1584,21 @@ static void update_lra_frequency(struct qpnp_hap *hap)
return;
}
- qpnp_hap_write_reg(hap, QPNP_HAP_RATE_CFG1_REG(hap->base),
- lra_auto_res_lo);
+ lra_auto_res[1] >>= 4;
+ rate_cfg = lra_auto_res[1] << 8 | lra_auto_res[0];
+ if (hap->last_rate_cfg == rate_cfg) {
+ pr_debug("Same rate_cfg, skip updating\n");
+ return;
+ }
- lra_auto_res_hi = lra_auto_res_hi >> 4;
- qpnp_hap_write_reg(hap, QPNP_HAP_RATE_CFG2_REG(hap->base),
- lra_auto_res_hi);
+ rc = qpnp_hap_write_mult_reg(hap, QPNP_HAP_RATE_CFG1_REG(hap->base),
+ lra_auto_res, 2);
+ if (rc < 0) {
+ pr_err("Error in writing to RATE_CFG1/2, rc=%d\n", rc);
+ } else {
+ pr_debug("Update RATE_CFG with [0x%x]\n", rate_cfg);
+ hap->last_rate_cfg = rate_cfg;
+ }
}
static enum hrtimer_restart detect_auto_res_error(struct hrtimer *timer)
@@ -1983,6 +2017,8 @@ static int qpnp_hap_config(struct qpnp_hap *hap)
if (rc)
return rc;
+ hap->last_rate_cfg = hap->init_drive_period_code;
+
if (hap->act_type == QPNP_HAP_LRA &&
hap->perform_lra_auto_resonance_search)
calculate_lra_code(hap);
@@ -2019,12 +2055,6 @@ static int qpnp_hap_config(struct qpnp_hap *hap)
return rc;
}
- /* Cache enable control register */
- rc = qpnp_hap_read_reg(hap, QPNP_HAP_EN_CTL_REG(hap->base), &val);
- if (rc < 0)
- return rc;
- hap->reg_en_ctl = val;
-
/* Cache play register */
rc = qpnp_hap_read_reg(hap, QPNP_HAP_PLAY_REG(hap->base), &val);
if (rc < 0)
diff --git a/drivers/soc/qcom/spcom.c b/drivers/soc/qcom/spcom.c
index 0c44d76bc7c7..cab758f695dc 100644
--- a/drivers/soc/qcom/spcom.c
+++ b/drivers/soc/qcom/spcom.c
@@ -898,12 +898,12 @@ static int spcom_rx(struct spcom_channel *ch,
goto exit_err;
}
+copy_buf:
if (!ch->glink_rx_buf) {
pr_err("invalid glink_rx_buf.\n");
goto exit_err;
}
-copy_buf:
/* Copy from glink buffer to spcom buffer */
size = min_t(int, ch->actual_rx_size, size);
memcpy(buf, ch->glink_rx_buf, size);
@@ -1723,12 +1723,16 @@ static int spcom_handle_lock_ion_buf_command(struct spcom_channel *ch,
pr_debug("ion handle ok.\n");
+ /* ION buf lock doesn't involve any rx/tx data to SP. */
+ mutex_lock(&ch->lock);
+
/* Check if this ION buffer is already locked */
for (i = 0 ; i < ARRAY_SIZE(ch->ion_handle_table) ; i++) {
if (ch->ion_handle_table[i] == ion_handle) {
pr_err("fd [%d] ion buf is already locked.\n", fd);
/* decrement back the ref count */
ion_free(spcom_dev->ion_client, ion_handle);
+ mutex_unlock(&ch->lock);
return -EINVAL;
}
}
@@ -1740,6 +1744,7 @@ static int spcom_handle_lock_ion_buf_command(struct spcom_channel *ch,
ch->ion_fd_table[i] = fd;
pr_debug("ch [%s] locked ion buf #%d, fd [%d].\n",
ch->name, i, fd);
+ mutex_unlock(&ch->lock);
return 0;
}
}
@@ -1748,6 +1753,8 @@ static int spcom_handle_lock_ion_buf_command(struct spcom_channel *ch,
/* decrement back the ref count */
ion_free(spcom_dev->ion_client, ion_handle);
+ mutex_unlock(&ch->lock);
+
return -EFAULT;
}
@@ -1826,8 +1833,13 @@ static int spcom_handle_unlock_ion_buf_command(struct spcom_channel *ch,
return -EINVAL;
}
+ /* ION buf unlock doesn't involve any rx/tx data to SP. */
+ mutex_lock(&ch->lock);
+
ret = spcom_unlock_ion_buf(ch, fd);
+ mutex_unlock(&ch->lock);
+
return ret;
}
diff --git a/drivers/soc/qcom/watchdog_v2.c b/drivers/soc/qcom/watchdog_v2.c
index 470ecfdd9f5e..745a069df88a 100644
--- a/drivers/soc/qcom/watchdog_v2.c
+++ b/drivers/soc/qcom/watchdog_v2.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012-2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2012-2017, 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
@@ -29,6 +29,7 @@
#include <linux/wait.h>
#include <soc/qcom/scm.h>
#include <soc/qcom/memory_dump.h>
+#include <soc/qcom/minidump.h>
#include <soc/qcom/watchdog.h>
#define MODULE_NAME "msm_watchdog"
@@ -521,6 +522,8 @@ void register_scan_dump(struct msm_watchdog_data *wdog_dd)
dump_data->addr = virt_to_phys(dump_addr);
dump_data->len = wdog_dd->scandump_size;
+ strlcpy(dump_data->name, "KSCANDUMP", sizeof(dump_data->name));
+
dump_entry.id = MSM_DUMP_DATA_SCANDUMP;
dump_entry.addr = virt_to_phys(dump_data);
ret = msm_dump_data_register(MSM_DUMP_TABLE_APPS, &dump_entry);
@@ -605,6 +608,9 @@ static void configure_bark_dump(struct msm_watchdog_data *wdog_dd)
cpu_data[cpu].addr = virt_to_phys(cpu_buf +
cpu * MAX_CPU_CTX_SIZE);
cpu_data[cpu].len = MAX_CPU_CTX_SIZE;
+ snprintf(cpu_data[cpu].name, sizeof(cpu_data[cpu].name),
+ "KCPU_CTX%d", cpu);
+
dump_entry.id = MSM_DUMP_DATA_CPU_CTX + cpu;
dump_entry.addr = virt_to_phys(&cpu_data[cpu]);
ret = msm_dump_data_register(MSM_DUMP_TABLE_APPS,
@@ -820,6 +826,7 @@ static int msm_watchdog_probe(struct platform_device *pdev)
{
int ret;
struct msm_watchdog_data *wdog_dd;
+ struct md_region md_entry;
if (!pdev->dev.of_node || !enable)
return -ENODEV;
@@ -841,6 +848,15 @@ static int msm_watchdog_probe(struct platform_device *pdev)
goto err;
}
init_watchdog_data(wdog_dd);
+
+ /* Add wdog info to minidump table */
+ strlcpy(md_entry.name, "KWDOGDATA", sizeof(md_entry.name));
+ md_entry.virt_addr = (uintptr_t)wdog_dd;
+ md_entry.phys_addr = virt_to_phys(wdog_dd);
+ md_entry.size = sizeof(*wdog_dd);
+ if (msm_minidump_add_region(&md_entry))
+ pr_info("Failed to add RTB in Minidump\n");
+
return 0;
err:
kzfree(wdog_dd);
diff --git a/drivers/spmi/spmi-pmic-arb.c b/drivers/spmi/spmi-pmic-arb.c
index a5bfeab596ac..d1802bcba0fb 100644
--- a/drivers/spmi/spmi-pmic-arb.c
+++ b/drivers/spmi/spmi-pmic-arb.c
@@ -166,6 +166,7 @@ struct spmi_pmic_arb {
u16 max_apid;
u16 max_periph;
u32 *mapping_table;
+ int reserved_chan;
DECLARE_BITMAP(mapping_table_valid, PMIC_ARB_MAX_PERIPHS);
struct irq_domain *domain;
struct spmi_controller *spmic;
@@ -861,6 +862,10 @@ static u16 pmic_arb_find_apid(struct spmi_pmic_arb *pa, u16 ppid)
* ppid_to_apid is an in-memory invert of that table.
*/
for (apid = pa->last_apid; apid < pa->max_periph; apid++) {
+ /* Do not keep the reserved channel in the mapping table */
+ if (pa->reserved_chan >= 0 && apid == pa->reserved_chan)
+ continue;
+
regval = readl_relaxed(pa->cnfg +
SPMI_OWNERSHIP_TABLE_REG(apid));
pa->apid_data[apid].irq_owner
@@ -920,6 +925,10 @@ static int pmic_arb_read_apid_map_v5(struct spmi_pmic_arb *pa)
* receive interrupts from the PPID.
*/
for (apid = 0; apid < pa->max_periph; apid++) {
+ /* Do not keep the reserved channel in the mapping table */
+ if (pa->reserved_chan >= 0 && apid == pa->reserved_chan)
+ continue;
+
offset = pa->ver_ops->channel_map_offset(apid);
if (offset >= pa->core_size)
break;
@@ -1340,6 +1349,10 @@ static int spmi_pmic_arb_probe(struct platform_device *pdev)
pa->ee = ee;
+ pa->reserved_chan = -EINVAL;
+ of_property_read_u32(pdev->dev.of_node, "qcom,reserved-chan",
+ &pa->reserved_chan);
+
pa->mapping_table = devm_kcalloc(&ctrl->dev, PMIC_ARB_MAX_PERIPHS - 1,
sizeof(*pa->mapping_table), GFP_KERNEL);
if (!pa->mapping_table) {
diff --git a/drivers/staging/android/ion/ion.c b/drivers/staging/android/ion/ion.c
index faa81c28a0d3..58bf3d2f52bd 100644
--- a/drivers/staging/android/ion/ion.c
+++ b/drivers/staging/android/ion/ion.c
@@ -114,6 +114,7 @@ struct ion_client {
*/
struct ion_handle {
struct kref ref;
+ unsigned int user_ref_count;
struct ion_client *client;
struct ion_buffer *buffer;
struct rb_node node;
@@ -433,6 +434,50 @@ int ion_handle_put(struct ion_handle *handle)
return ret;
}
+/* Must hold the client lock */
+static void user_ion_handle_get(struct ion_handle *handle)
+{
+ if (handle->user_ref_count++ == 0)
+ kref_get(&handle->ref);
+}
+
+/* Must hold the client lock */
+static struct ion_handle *user_ion_handle_get_check_overflow(
+ struct ion_handle *handle)
+{
+ if (handle->user_ref_count + 1 == 0)
+ return ERR_PTR(-EOVERFLOW);
+ user_ion_handle_get(handle);
+ return handle;
+}
+
+/* passes a kref to the user ref count.
+ * We know we're holding a kref to the object before and
+ * after this call, so no need to reverify handle.
+ */
+static struct ion_handle *pass_to_user(struct ion_handle *handle)
+{
+ struct ion_client *client = handle->client;
+ struct ion_handle *ret;
+
+ mutex_lock(&client->lock);
+ ret = user_ion_handle_get_check_overflow(handle);
+ ion_handle_put_nolock(handle);
+ mutex_unlock(&client->lock);
+ return ret;
+}
+
+/* Must hold the client lock */
+static int user_ion_handle_put_nolock(struct ion_handle *handle)
+{
+ int ret = 0;
+
+ if (--handle->user_ref_count == 0)
+ ret = ion_handle_put_nolock(handle);
+
+ return ret;
+}
+
static struct ion_handle *ion_handle_lookup(struct ion_client *client,
struct ion_buffer *buffer)
{
@@ -644,6 +689,25 @@ static void ion_free_nolock(struct ion_client *client, struct ion_handle *handle
ion_handle_put_nolock(handle);
}
+static void user_ion_free_nolock(struct ion_client *client,
+ struct ion_handle *handle)
+{
+ bool valid_handle;
+
+ WARN_ON(client != handle->client);
+
+ valid_handle = ion_handle_validate(client, handle);
+ if (!valid_handle) {
+ WARN(1, "%s: invalid handle passed to free.\n", __func__);
+ return;
+ }
+ if (!handle->user_ref_count > 0) {
+ WARN(1, "%s: User does not have access!\n", __func__);
+ return;
+ }
+ user_ion_handle_put_nolock(handle);
+}
+
void ion_free(struct ion_client *client, struct ion_handle *handle)
{
BUG_ON(client != handle->client);
@@ -1518,7 +1582,7 @@ static long ion_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
data.allocation.flags, true);
if (IS_ERR(handle))
return PTR_ERR(handle);
-
+ pass_to_user(handle);
data.allocation.handle = handle->id;
cleanup_handle = handle;
@@ -1534,7 +1598,7 @@ static long ion_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
mutex_unlock(&client->lock);
return PTR_ERR(handle);
}
- ion_free_nolock(client, handle);
+ user_ion_free_nolock(client, handle);
ion_handle_put_nolock(handle);
mutex_unlock(&client->lock);
break;
@@ -1558,10 +1622,15 @@ static long ion_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
struct ion_handle *handle;
handle = ion_import_dma_buf(client, data.fd.fd);
- if (IS_ERR(handle))
+ if (IS_ERR(handle)) {
ret = PTR_ERR(handle);
- else
- data.handle.handle = handle->id;
+ } else {
+ handle = pass_to_user(handle);
+ if (IS_ERR(handle))
+ ret = PTR_ERR(handle);
+ else
+ data.handle.handle = handle->id;
+ }
break;
}
case ION_IOC_SYNC:
@@ -1593,8 +1662,10 @@ static long ion_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
if (dir & _IOC_READ) {
if (copy_to_user((void __user *)arg, &data, _IOC_SIZE(cmd))) {
if (cleanup_handle) {
- ion_free(client, cleanup_handle);
- ion_handle_put(cleanup_handle);
+ mutex_lock(&client->lock);
+ user_ion_free_nolock(client, cleanup_handle);
+ ion_handle_put_nolock(cleanup_handle);
+ mutex_unlock(&client->lock);
}
return -EFAULT;
}
diff --git a/drivers/staging/android/lowmemorykiller.c b/drivers/staging/android/lowmemorykiller.c
index bec687853c5d..205af6627b80 100644
--- a/drivers/staging/android/lowmemorykiller.c
+++ b/drivers/staging/android/lowmemorykiller.c
@@ -217,6 +217,22 @@ static int test_task_flag(struct task_struct *p, int flag)
return 0;
}
+static int test_task_state(struct task_struct *p, int state)
+{
+ struct task_struct *t;
+
+ for_each_thread(p, t) {
+ task_lock(t);
+ if (t->state & state) {
+ task_unlock(t);
+ return 1;
+ }
+ task_unlock(t);
+ }
+
+ return 0;
+}
+
static DEFINE_MUTEX(scan_mutex);
int can_use_cma_pages(gfp_t gfp_mask)
@@ -404,7 +420,7 @@ static unsigned long lowmem_scan(struct shrinker *s, struct shrink_control *sc)
int other_free;
int other_file;
- if (mutex_lock_interruptible(&scan_mutex) < 0)
+ if (!mutex_trylock(&scan_mutex))
return 0;
other_free = global_page_state(NR_FREE_PAGES);
@@ -462,8 +478,6 @@ static unsigned long lowmem_scan(struct shrinker *s, struct shrink_control *sc)
if (time_before_eq(jiffies, lowmem_deathpending_timeout)) {
if (test_task_flag(tsk, TIF_MEMDIE)) {
rcu_read_unlock();
- /* give the system time to free up the memory */
- msleep_interruptible(20);
mutex_unlock(&scan_mutex);
return 0;
}
@@ -497,6 +511,17 @@ static unsigned long lowmem_scan(struct shrinker *s, struct shrink_control *sc)
}
if (selected) {
long cache_size, cache_limit, free;
+
+ if (test_task_flag(selected, TIF_MEMDIE) &&
+ (test_task_state(selected, TASK_UNINTERRUPTIBLE))) {
+ lowmem_print(2, "'%s' (%d) is already killed\n",
+ selected->comm,
+ selected->pid);
+ rcu_read_unlock();
+ mutex_unlock(&scan_mutex);
+ return 0;
+ }
+
task_lock(selected);
send_sig(SIGKILL, selected, 0);
/*
@@ -565,7 +590,8 @@ static unsigned long lowmem_scan(struct shrinker *s, struct shrink_control *sc)
static struct shrinker lowmem_shrinker = {
.scan_objects = lowmem_scan,
.count_objects = lowmem_count,
- .seeks = DEFAULT_SEEKS * 16
+ .seeks = DEFAULT_SEEKS * 16,
+ .flags = SHRINKER_LMK
};
static int __init lowmem_init(void)
diff --git a/drivers/staging/android/sync.c b/drivers/staging/android/sync.c
index 9b8d17ce3a5e..5238d67490ce 100644
--- a/drivers/staging/android/sync.c
+++ b/drivers/staging/android/sync.c
@@ -29,6 +29,7 @@
#include "sync.h"
#define CREATE_TRACE_POINTS
+#define SYNC_DUMP_TIME_LIMIT 7000
#include "trace/sync.h"
static const struct fence_ops android_fence_ops;
@@ -392,7 +393,9 @@ int sync_fence_wait(struct sync_fence *fence, long timeout)
if (timeout) {
pr_info("fence timeout on [%pK] after %dms\n", fence,
jiffies_to_msecs(timeout));
- sync_dump();
+ if (jiffies_to_msecs(timeout) >=
+ SYNC_DUMP_TIME_LIMIT)
+ sync_dump();
}
return -ETIME;
}
diff --git a/drivers/usb/gadget/configfs.c b/drivers/usb/gadget/configfs.c
index c31aaf7a9880..e5d3a0bdf32a 100644
--- a/drivers/usb/gadget/configfs.c
+++ b/drivers/usb/gadget/configfs.c
@@ -160,7 +160,7 @@ static int usb_string_copy(const char *s, char **s_copy)
if (!str)
return -ENOMEM;
}
- strncpy(str, s, MAX_USB_STRING_WITH_NULL_LEN);
+ strlcpy(str, s, MAX_USB_STRING_WITH_NULL_LEN);
if (str[ret - 1] == '\n')
str[ret - 1] = '\0';
*s_copy = str;
diff --git a/drivers/usb/phy/phy-msm-ssusb-qmp.c b/drivers/usb/phy/phy-msm-ssusb-qmp.c
index aa11cf2f7417..6ba742076baa 100644
--- a/drivers/usb/phy/phy-msm-ssusb-qmp.c
+++ b/drivers/usb/phy/phy-msm-ssusb-qmp.c
@@ -222,11 +222,6 @@ static int msm_ssusb_qmp_ldo_enable(struct msm_ssphy_qmp *phy, int on)
"enable phy->fpc_redrive_ldo failed\n");
return rc;
}
-
- dev_dbg(phy->phy.dev,
- "fpc redrive ldo: min_vol:%duV max_vol:%duV\n",
- phy->redrive_voltage_levels[VOLTAGE_LEVEL_MIN],
- phy->redrive_voltage_levels[VOLTAGE_LEVEL_MAX]);
}
rc = msm_ldo_enable(phy, phy->vdd, phy->vdd_levels,
@@ -236,11 +231,6 @@ static int msm_ssusb_qmp_ldo_enable(struct msm_ssphy_qmp *phy, int on)
goto disable_fpc_redrive;
}
- dev_dbg(phy->phy.dev,
- "vdd ldo: min_vol:%duV max_vol:%duV\n",
- phy->vdd_levels[VOLTAGE_LEVEL_MIN],
- phy->vdd_levels[VOLTAGE_LEVEL_MAX]);
-
rc = msm_ldo_enable(phy, phy->core_ldo, phy->core_voltage_levels,
USB_SSPHY_HPM_LOAD);
if (rc < 0) {
@@ -248,11 +238,6 @@ static int msm_ssusb_qmp_ldo_enable(struct msm_ssphy_qmp *phy, int on)
goto disable_vdd;
}
- dev_dbg(phy->phy.dev,
- "core ldo: min_vol:%duV max_vol:%duV\n",
- phy->core_voltage_levels[VOLTAGE_LEVEL_MIN],
- phy->core_voltage_levels[VOLTAGE_LEVEL_MAX]);
-
return 0;
disable_regulators:
diff --git a/drivers/video/fbdev/msm/mdss_compat_utils.c b/drivers/video/fbdev/msm/mdss_compat_utils.c
index 17644e3556b6..e9ba77501b38 100644
--- a/drivers/video/fbdev/msm/mdss_compat_utils.c
+++ b/drivers/video/fbdev/msm/mdss_compat_utils.c
@@ -226,6 +226,7 @@ static struct mdp_input_layer *__create_layer_list(
layer->transp_mask = layer32->transp_mask;
layer->bg_color = layer32->bg_color;
layer->blend_op = layer32->blend_op;
+ layer->alpha = layer32->alpha;
layer->color_space = layer32->color_space;
layer->src_rect = layer32->src_rect;
layer->dst_rect = layer32->dst_rect;
diff --git a/drivers/video/fbdev/msm/mdss_dba_utils.c b/drivers/video/fbdev/msm/mdss_dba_utils.c
index 0808a1a6e14b..3330f8f62b78 100644
--- a/drivers/video/fbdev/msm/mdss_dba_utils.c
+++ b/drivers/video/fbdev/msm/mdss_dba_utils.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2015-2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2015-2017, 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
@@ -46,6 +46,7 @@ struct mdss_dba_utils_data {
struct cec_cbs ccbs;
char disp_switch_name[MAX_SWITCH_NAME_SIZE];
u32 current_vic;
+ bool support_audio;
};
static struct mdss_dba_utils_data *mdss_dba_utils_get_data(
@@ -84,7 +85,7 @@ end:
return udata;
}
-static void mdss_dba_utils_send_display_notification(
+static void mdss_dba_utils_notify_display(
struct mdss_dba_utils_data *udata, int val)
{
int state = 0;
@@ -109,7 +110,7 @@ static void mdss_dba_utils_send_display_notification(
udata->sdev_display.state);
}
-static void mdss_dba_utils_send_audio_notification(
+static void mdss_dba_utils_notify_audio(
struct mdss_dba_utils_data *udata, int val)
{
int state = 0;
@@ -291,6 +292,31 @@ static void mdss_dba_utils_sysfs_remove(struct kobject *kobj)
sysfs_remove_group(kobj, &mdss_dba_utils_fs_attrs_group);
}
+static bool mdss_dba_check_audio_support(struct mdss_dba_utils_data *udata)
+{
+ bool dvi_mode = false;
+ int audio_blk_size = 0;
+ struct msm_ext_disp_audio_edid_blk audio_blk;
+
+ if (!udata) {
+ pr_debug("%s: Invalid input\n", __func__);
+ return false;
+ }
+ memset(&audio_blk, 0, sizeof(audio_blk));
+
+ /* check if sink is in DVI mode */
+ dvi_mode = hdmi_edid_is_dvi_mode(udata->edid_data);
+
+ /* get the audio block size info from EDID */
+ hdmi_edid_get_audio_blk(udata->edid_data, &audio_blk);
+ audio_blk_size = audio_blk.audio_data_blk_size;
+
+ if (dvi_mode || !audio_blk_size)
+ return false;
+ else
+ return true;
+}
+
static void mdss_dba_utils_dba_cb(void *data, enum msm_dba_callback_event event)
{
int ret = -EINVAL;
@@ -322,19 +348,26 @@ static void mdss_dba_utils_dba_cb(void *data, enum msm_dba_callback_event event)
if (!ret) {
hdmi_edid_parser(udata->edid_data);
- hdmi_edid_get_audio_blk(udata->edid_data, &blk);
- if (udata->ops.set_audio_block)
- udata->ops.set_audio_block(
+ /* check whether audio is supported or not */
+ udata->support_audio =
+ mdss_dba_check_audio_support(udata);
+ if (udata->support_audio) {
+ hdmi_edid_get_audio_blk(
+ udata->edid_data, &blk);
+ if (udata->ops.set_audio_block)
+ udata->ops.set_audio_block(
udata->dba_data,
sizeof(blk), &blk);
+ }
} else {
pr_err("failed to get edid%d\n", ret);
}
}
if (pluggable) {
- mdss_dba_utils_send_display_notification(udata, 1);
- mdss_dba_utils_send_audio_notification(udata, 1);
+ mdss_dba_utils_notify_display(udata, 1);
+ if (udata->support_audio)
+ mdss_dba_utils_notify_audio(udata, 1);
} else {
mdss_dba_utils_video_on(udata, udata->pinfo);
}
@@ -346,8 +379,9 @@ static void mdss_dba_utils_dba_cb(void *data, enum msm_dba_callback_event event)
if (!udata->hpd_state)
break;
if (pluggable) {
- mdss_dba_utils_send_audio_notification(udata, 0);
- mdss_dba_utils_send_display_notification(udata, 0);
+ if (udata->support_audio)
+ mdss_dba_utils_notify_audio(udata, 0);
+ mdss_dba_utils_notify_display(udata, 0);
} else {
mdss_dba_utils_video_off(udata);
}
diff --git a/drivers/video/fbdev/msm/mdss_debug.c b/drivers/video/fbdev/msm/mdss_debug.c
index e60869144339..ddb7a4c31f68 100644
--- a/drivers/video/fbdev/msm/mdss_debug.c
+++ b/drivers/video/fbdev/msm/mdss_debug.c
@@ -44,6 +44,42 @@
#define INVALID_XIN_ID 0xFF
+static u32 dsi_dbg_bus_sdm660[] = {
+ 0x0001, 0x1001, 0x0001, 0x0011,
+ 0x1021, 0x0021, 0x0031, 0x0041,
+ 0x0051, 0x0061, 0x3061, 0x0061,
+ 0x2061, 0x2061, 0x1061, 0x1061,
+ 0x1061, 0x0071, 0x0071, 0x0071,
+ 0x0081, 0x0081, 0x00A1, 0x00A1,
+ 0x10A1, 0x20A1, 0x30A1, 0x10A1,
+ 0x10A1, 0x30A1, 0x20A1, 0x00B1,
+ 0x00C1, 0x00C1, 0x10C1, 0x20C1,
+ 0x30C1, 0x00D1, 0x00D1, 0x20D1,
+ 0x30D1, 0x00E1, 0x00E1, 0x00E1,
+ 0x00F1, 0x00F1, 0x0101, 0x0101,
+ 0x1101, 0x2101, 0x3101, 0x0111,
+ 0x0141, 0x1141, 0x0141, 0x1141,
+ 0x1141, 0x0151, 0x0151, 0x1151,
+ 0x2151, 0x3151, 0x0161, 0x0161,
+ 0x1161, 0x0171, 0x0171, 0x0181,
+ 0x0181, 0x0191, 0x0191, 0x01A1,
+ 0x01A1, 0x01B1, 0x01B1, 0x11B1,
+ 0x21B1, 0x01C1, 0x01C1, 0x11C1,
+ 0x21C1, 0x31C1, 0x01D1, 0x01D1,
+ 0x01D1, 0x01D1, 0x11D1, 0x21D1,
+ 0x21D1, 0x01E1, 0x01E1, 0x01F1,
+ 0x01F1, 0x0201, 0x0201, 0x0211,
+ 0x0221, 0x0231, 0x0241, 0x0251,
+ 0x0281, 0x0291, 0x0281, 0x0291,
+ 0x02A1, 0x02B1, 0x02C1, 0x0321,
+ 0x0321, 0x1321, 0x2321, 0x3321,
+ 0x0331, 0x0331, 0x1331, 0x0341,
+ 0x0341, 0x1341, 0x2341, 0x3341,
+ 0x0351, 0x0361, 0x0361, 0x1361,
+ 0x2361, 0x0371, 0x0381, 0x0391,
+ 0x03C1, 0x03D1, 0x03E1, 0x03F1,
+};
+
static DEFINE_MUTEX(mdss_debug_lock);
static char panel_reg[2] = {DEFAULT_READ_PANEL_POWER_MODE_REG, 0x00};
@@ -59,11 +95,13 @@ static int panel_debug_base_open(struct inode *inode, struct file *file)
static int panel_debug_base_release(struct inode *inode, struct file *file)
{
struct mdss_debug_base *dbg = file->private_data;
+ mutex_lock(&mdss_debug_lock);
if (dbg && dbg->buf) {
kfree(dbg->buf);
dbg->buf_len = 0;
dbg->buf = NULL;
}
+ mutex_unlock(&mdss_debug_lock);
return 0;
}
@@ -385,11 +423,13 @@ static int mdss_debug_base_open(struct inode *inode, struct file *file)
static int mdss_debug_base_release(struct inode *inode, struct file *file)
{
struct mdss_debug_base *dbg = file->private_data;
+ mutex_lock(&mdss_debug_lock);
if (dbg && dbg->buf) {
kfree(dbg->buf);
dbg->buf_len = 0;
dbg->buf = NULL;
}
+ mutex_unlock(&mdss_debug_lock);
return 0;
}
@@ -1788,6 +1828,24 @@ void mdss_misr_crc_collect(struct mdss_data_type *mdata, int block_id,
}
+void mdss_dsi_debug_bus_init(struct mdss_dsi_data *sdata)
+{
+ if (!sdata)
+ return;
+
+ sdata->dbg_bus = NULL;
+ sdata->dbg_bus_size = 0;
+
+ switch (sdata->shared_data->hw_rev) {
+ case MDSS_DSI_HW_REV_201:
+ sdata->dbg_bus = dsi_dbg_bus_sdm660;
+ sdata->dbg_bus_size = ARRAY_SIZE(dsi_dbg_bus_sdm660);
+ break;
+ default:
+ break;
+ }
+}
+
int mdss_dump_misr_data(char **buf, u32 size)
{
struct mdss_mdp_misr_map *dsi0_map;
diff --git a/drivers/video/fbdev/msm/mdss_debug.h b/drivers/video/fbdev/msm/mdss_debug.h
index 1a44ab5c44a2..d778fb35ed2d 100644
--- a/drivers/video/fbdev/msm/mdss_debug.h
+++ b/drivers/video/fbdev/msm/mdss_debug.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012-2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2012-2017, 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
@@ -170,6 +170,7 @@ u32 get_dump_range(struct dump_offset *range_node, size_t max_offset);
void mdss_dump_reg(const char *dump_name, u32 reg_dump_flag, char *addr,
int len, u32 **dump_mem, bool from_isr);
void mdss_mdp_debug_mid(u32 mid);
+void mdss_dump_dsi_debug_bus(u32 bus_dump_flag, u32 **dump_mem);
#else
struct mdss_debug_base;
struct dump_offset;
diff --git a/drivers/video/fbdev/msm/mdss_debug_xlog.c b/drivers/video/fbdev/msm/mdss_debug_xlog.c
index 0a45ce036cf6..bf4117650e3c 100644
--- a/drivers/video/fbdev/msm/mdss_debug_xlog.c
+++ b/drivers/video/fbdev/msm/mdss_debug_xlog.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2014-2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2014-2017, 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
@@ -32,6 +32,7 @@
#define XLOG_DEFAULT_REGDUMP 0x2 /* dump in RAM */
#define XLOG_DEFAULT_DBGBUSDUMP 0x2 /* dump in RAM */
#define XLOG_DEFAULT_VBIF_DBGBUSDUMP 0x2 /* dump in RAM */
+#define XLOG_DEFAULT_DSI_DBGBUSDUMP 0x2 /* dump in RAM */
/*
* xlog will print this number of entries when it is called through
@@ -73,14 +74,17 @@ struct mdss_dbg_xlog {
u32 enable_reg_dump;
u32 enable_dbgbus_dump;
u32 enable_vbif_dbgbus_dump;
+ u32 enable_dsi_dbgbus_dump;
struct work_struct xlog_dump_work;
struct mdss_debug_base *blk_arr[MDSS_DEBUG_BASE_MAX];
bool work_panic;
bool work_dbgbus;
bool work_vbif_dbgbus;
+ bool work_dsi_dbgbus;
u32 *dbgbus_dump; /* address for the debug bus dump */
u32 *vbif_dbgbus_dump; /* address for the vbif debug bus dump */
u32 *nrt_vbif_dbgbus_dump; /* address for the nrt vbif debug bus dump */
+ u32 *dsi_dbgbus_dump; /* address for the dsi debug bus dump */
} mdss_dbg_xlog;
static inline bool mdss_xlog_is_enabled(u32 flag)
@@ -579,7 +583,7 @@ struct mdss_debug_base *get_dump_blk_addr(const char *blk_name)
static void mdss_xlog_dump_array(struct mdss_debug_base *blk_arr[],
u32 len, bool dead, const char *name, bool dump_dbgbus,
- bool dump_vbif_dbgbus)
+ bool dump_vbif_dbgbus, bool dump_dsi_dbgbus)
{
int i;
@@ -603,6 +607,10 @@ static void mdss_xlog_dump_array(struct mdss_debug_base *blk_arr[],
&mdss_dbg_xlog.nrt_vbif_dbgbus_dump, false);
}
+ if (dump_dsi_dbgbus)
+ mdss_dump_dsi_debug_bus(mdss_dbg_xlog.enable_dsi_dbgbus_dump,
+ &mdss_dbg_xlog.dsi_dbgbus_dump);
+
if (dead && mdss_dbg_xlog.panic_on_err)
panic(name);
}
@@ -614,7 +622,8 @@ static void xlog_debug_work(struct work_struct *work)
ARRAY_SIZE(mdss_dbg_xlog.blk_arr),
mdss_dbg_xlog.work_panic, "xlog_workitem",
mdss_dbg_xlog.work_dbgbus,
- mdss_dbg_xlog.work_vbif_dbgbus);
+ mdss_dbg_xlog.work_vbif_dbgbus,
+ mdss_dbg_xlog.work_dsi_dbgbus);
}
void mdss_xlog_tout_handler_default(bool queue, const char *name, ...)
@@ -622,6 +631,7 @@ void mdss_xlog_tout_handler_default(bool queue, const char *name, ...)
int i, index = 0;
bool dead = false;
bool dump_dbgbus = false, dump_vbif_dbgbus = false;
+ bool dump_dsi_dbgbus = false;
va_list args;
char *blk_name = NULL;
struct mdss_debug_base *blk_base = NULL;
@@ -657,6 +667,9 @@ void mdss_xlog_tout_handler_default(bool queue, const char *name, ...)
if (!strcmp(blk_name, "vbif_dbg_bus"))
dump_vbif_dbgbus = true;
+ if (!strcmp(blk_name, "dsi_dbg_bus"))
+ dump_dsi_dbgbus = true;
+
if (!strcmp(blk_name, "panic"))
dead = true;
}
@@ -667,10 +680,11 @@ void mdss_xlog_tout_handler_default(bool queue, const char *name, ...)
mdss_dbg_xlog.work_panic = dead;
mdss_dbg_xlog.work_dbgbus = dump_dbgbus;
mdss_dbg_xlog.work_vbif_dbgbus = dump_vbif_dbgbus;
+ mdss_dbg_xlog.work_dsi_dbgbus = dump_dsi_dbgbus;
schedule_work(&mdss_dbg_xlog.xlog_dump_work);
} else {
mdss_xlog_dump_array(blk_arr, blk_len, dead, name, dump_dbgbus,
- dump_vbif_dbgbus);
+ dump_vbif_dbgbus, dump_dsi_dbgbus);
}
}
@@ -754,6 +768,7 @@ int mdss_create_xlog_debug(struct mdss_debug_data *mdd)
mdss_dbg_xlog.enable_reg_dump = XLOG_DEFAULT_REGDUMP;
mdss_dbg_xlog.enable_dbgbus_dump = XLOG_DEFAULT_DBGBUSDUMP;
mdss_dbg_xlog.enable_vbif_dbgbus_dump = XLOG_DEFAULT_VBIF_DBGBUSDUMP;
+ mdss_dbg_xlog.enable_dsi_dbgbus_dump = XLOG_DEFAULT_DSI_DBGBUSDUMP;
pr_info("xlog_status: enable:%d, panic:%d, dump:%d\n",
mdss_dbg_xlog.xlog_enable, mdss_dbg_xlog.panic_on_err,
diff --git a/drivers/video/fbdev/msm/mdss_dp.c b/drivers/video/fbdev/msm/mdss_dp.c
index 7a1b563fbb6c..1e878e9a00cb 100644
--- a/drivers/video/fbdev/msm/mdss_dp.c
+++ b/drivers/video/fbdev/msm/mdss_dp.c
@@ -69,6 +69,11 @@ static int mdss_dp_process_phy_test_pattern_request(
static int mdss_dp_send_audio_notification(
struct mdss_dp_drv_pdata *dp, int val);
+static inline void mdss_dp_reset_sink_count(struct mdss_dp_drv_pdata *dp)
+{
+ memset(&dp->sink_count, 0, sizeof(dp->sink_count));
+}
+
static inline void mdss_dp_reset_test_data(struct mdss_dp_drv_pdata *dp)
{
dp->test_data = (const struct dpcd_test_request){ 0 };
@@ -133,22 +138,77 @@ static int mdss_dp_is_clk_prefix(const char *clk_prefix, const char *clk_name)
return !strncmp(clk_name, clk_prefix, strlen(clk_prefix));
}
+static void mdss_dp_reset_phy_config_indices(struct mdss_dp_drv_pdata *dp)
+{
+ int i = 0;
+
+ for (i = 0; i < PHY_AUX_CFG_MAX; i++)
+ dp->aux_cfg[i].current_index = 0;
+}
+
+static void mdss_dp_phy_aux_cfg_reset(struct mdss_dp_drv_pdata *dp)
+{
+ int i = 0;
+
+ for (i = 0; i < PHY_AUX_CFG_MAX; i++)
+ dp->aux_cfg[i] = (const struct mdss_dp_phy_cfg){ 0 };
+}
+
+static int mdss_dp_parse_aux_cfg(struct platform_device *pdev,
+ struct mdss_dp_drv_pdata *dp)
+{
+ int len = 0, i = 0, j = 0, config_count = 0;
+ const char *data;
+ int const minimum_config_count = 1;
+
+ for (i = 0; i < PHY_AUX_CFG_MAX; i++) {
+ const char *property = mdss_dp_get_phy_aux_config_property(i);
+
+ data = of_get_property(pdev->dev.of_node, property, &len);
+ if (!data) {
+ pr_err("Unable to read %s\n", property);
+ goto error;
+ }
+
+ config_count = len - 1;
+ if ((config_count < minimum_config_count) ||
+ (config_count > MDSS_DP_MAX_PHY_CFG_VALUE_CNT)) {
+ pr_err("Invalid config count (%d) configs for %s\n",
+ config_count, property);
+ goto error;
+ }
+
+ dp->aux_cfg[i].offset = data[0];
+ dp->aux_cfg[i].cfg_cnt = config_count;
+ pr_debug("%s offset=0x%x, cfg_cnt=%d\n",
+ property,
+ dp->aux_cfg[i].offset,
+ dp->aux_cfg[i].cfg_cnt);
+ for (j = 1; j < len; j++) {
+ dp->aux_cfg[i].lut[j - 1] = data[j];
+ pr_debug("%s lut[%d]=0x%x\n",
+ property,
+ i,
+ dp->aux_cfg[i].lut[j - 1]);
+ }
+ }
+
+ return 0;
+
+error:
+ mdss_dp_phy_aux_cfg_reset(dp);
+ return -EINVAL;
+}
+
static int mdss_dp_parse_prop(struct platform_device *pdev,
struct mdss_dp_drv_pdata *dp_drv)
{
int len = 0, i = 0, rc = 0;
const char *data;
- data = of_get_property(pdev->dev.of_node,
- "qcom,aux-cfg-settings", &len);
- if ((!data) || (len != AUX_CFG_LEN)) {
- pr_err("%s:%d, Unable to read DP AUX CFG settings",
- __func__, __LINE__);
- return -EINVAL;
- }
-
- for (i = 0; i < len; i++)
- dp_drv->aux_cfg[i] = data[i];
+ rc = mdss_dp_parse_aux_cfg(pdev, dp_drv);
+ if (rc)
+ return rc;
data = of_get_property(pdev->dev.of_node,
"qcom,logical2physical-lane-map", &len);
@@ -958,6 +1018,12 @@ void mdss_dp_config_ctrl(struct mdss_dp_drv_pdata *dp)
mdss_dp_configuration_ctrl(&dp->ctrl_io, data);
}
+static inline void mdss_dp_ack_state(struct mdss_dp_drv_pdata *dp, int val)
+{
+ if (dp && dp->ext_audio_data.intf_ops.notify)
+ dp->ext_audio_data.intf_ops.notify(dp->ext_pdev, val);
+}
+
static int mdss_dp_wait4video_ready(struct mdss_dp_drv_pdata *dp_drv)
{
int ret = 0;
@@ -989,26 +1055,28 @@ static int mdss_dp_wait4video_ready(struct mdss_dp_drv_pdata *dp_drv)
static void mdss_dp_update_cable_status(struct mdss_dp_drv_pdata *dp,
bool connected)
{
- mutex_lock(&dp->pd_msg_mutex);
+ mutex_lock(&dp->attention_lock);
pr_debug("cable_connected to %d\n", connected);
if (dp->cable_connected != connected)
dp->cable_connected = connected;
else
pr_debug("no change in cable status\n");
- mutex_unlock(&dp->pd_msg_mutex);
+ mutex_unlock(&dp->attention_lock);
}
static int dp_get_cable_status(struct platform_device *pdev, u32 vote)
{
- struct mdss_dp_drv_pdata *dp_ctrl = platform_get_drvdata(pdev);
+ struct mdss_dp_drv_pdata *dp = platform_get_drvdata(pdev);
u32 hpd;
- if (!dp_ctrl) {
+ if (!dp) {
DEV_ERR("%s: invalid input\n", __func__);
return -ENODEV;
}
- hpd = dp_ctrl->cable_connected;
+ mutex_lock(&dp->attention_lock);
+ hpd = dp->cable_connected;
+ mutex_unlock(&dp->attention_lock);
return hpd;
}
@@ -1230,12 +1298,6 @@ static int dp_init_panel_info(struct mdss_dp_drv_pdata *dp_drv, u32 vic)
return 0;
} /* dp_init_panel_info */
-static inline void mdss_dp_ack_state(struct mdss_dp_drv_pdata *dp, int val)
-{
- if (dp && dp->ext_audio_data.intf_ops.notify)
- dp->ext_audio_data.intf_ops.notify(dp->ext_pdev, val);
-}
-
/**
* mdss_dp_get_lane_mapping() - returns lane mapping based on given orientation
* @orientation: usb plug orientation
@@ -1621,15 +1683,19 @@ int mdss_dp_on(struct mdss_panel_data *pdata)
dp_drv = container_of(pdata, struct mdss_dp_drv_pdata,
panel_data);
- if (dp_drv->power_on) {
- /*
- * Acknowledge the connection event if link training has already
- * been done. This will unblock the external display thread and
- * allow the driver to progress. For example, in the case of
- * video test pattern requests, to send the test response and
- * start transmitting the test pattern.
- */
- mdss_dp_ack_state(dp_drv, true);
+ /*
+ * If the link already active, then nothing needs to be done here.
+ * However, it is possible that the the power_on flag could be
+ * set to true but we would still need to initialize the DP host.
+ * An example of this use-case is when a multiport dongle is connected
+ * and subsequently the downstream sink is disconnected. This would
+ * only go through the IRQ HPD path where we tear down the link but
+ * the power_on flag remains set to true. When the downstream sink
+ * is subsequently connected again, we need to re-initialize DP
+ * host
+ */
+ if (dp_drv->power_on &&
+ (dp_drv->new_vic && (dp_drv->new_vic == dp_drv->vic))) {
pr_debug("Link already setup, return\n");
return 0;
}
@@ -1647,6 +1713,23 @@ int mdss_dp_on(struct mdss_panel_data *pdata)
return mdss_dp_on_hpd(dp_drv);
}
+static bool mdss_dp_is_ds_bridge(struct mdss_dp_drv_pdata *dp)
+{
+ return dp->dpcd.downstream_port.dfp_present;
+}
+
+static bool mdss_dp_is_ds_bridge_sink_count_zero(struct mdss_dp_drv_pdata *dp)
+{
+ return (mdss_dp_is_ds_bridge(dp) &&
+ (dp->sink_count.count == 0));
+}
+
+static bool mdss_dp_is_ds_bridge_no_local_edid(struct mdss_dp_drv_pdata *dp)
+{
+ return (mdss_dp_is_ds_bridge_sink_count_zero(dp) &&
+ !(dp->dpcd.flags & DPCD_PORT_0_EDID_PRESENTED));
+}
+
static int mdss_dp_off_irq(struct mdss_dp_drv_pdata *dp_drv)
{
if (!dp_drv->power_on) {
@@ -1664,10 +1747,16 @@ static int mdss_dp_off_irq(struct mdss_dp_drv_pdata *dp_drv)
/* Make sure DP mainlink and audio engines are disabled */
wmb();
- mdss_dp_ack_state(dp_drv, false);
+ /*
+ * If downstream device is a brige which no longer has any
+ * downstream devices connected to it, then we should reset
+ * the current panel info
+ */
+ if (mdss_dp_is_ds_bridge_sink_count_zero(dp_drv))
+ dp_init_panel_info(dp_drv, HDMI_VFRMT_UNKNOWN);
+
mutex_unlock(&dp_drv->train_mutex);
- complete_all(&dp_drv->irq_comp);
pr_debug("end\n");
return 0;
@@ -1694,11 +1783,11 @@ static int mdss_dp_off_hpd(struct mdss_dp_drv_pdata *dp_drv)
mdss_dp_host_deinit(dp_drv);
dp_drv->power_on = false;
- dp_drv->sink_info_read = false;
dp_init_panel_info(dp_drv, HDMI_VFRMT_UNKNOWN);
- mdss_dp_ack_state(dp_drv, false);
mdss_dp_reset_test_data(dp_drv);
+ mdss_dp_reset_sink_count(dp_drv);
+ dp_drv->prev_sink_count = dp_drv->sink_count;
mutex_unlock(&dp_drv->train_mutex);
pr_debug("DP off done\n");
@@ -1737,8 +1826,9 @@ static int mdss_dp_send_audio_notification(
if (mdss_dp_sink_audio_supp(dp) || dp->audio_test_req) {
dp->audio_test_req = false;
- pr_debug("sending audio notification\n");
flags |= MSM_EXT_DISP_HPD_AUDIO;
+ pr_debug("sending audio notification = %d, flags = %d\n", val,
+ flags);
if (dp->ext_audio_data.intf_ops.hpd)
ret = dp->ext_audio_data.intf_ops.hpd(dp->ext_pdev,
@@ -1763,7 +1853,8 @@ static int mdss_dp_send_video_notification(
goto end;
}
- flags |= MSM_EXT_DISP_HPD_VIDEO;
+ flags |= MSM_EXT_DISP_HPD_ASYNC_VIDEO;
+ pr_debug("sending video notification = %d, flags = %d\n", val, flags);
if (dp->ext_audio_data.intf_ops.hpd)
ret = dp->ext_audio_data.intf_ops.hpd(dp->ext_pdev,
@@ -1820,6 +1911,8 @@ static int mdss_dp_edid_init(struct mdss_panel_data *pdata)
dp_drv->edid_buf = edid_init_data.buf;
dp_drv->edid_buf_size = edid_init_data.buf_size;
+ mdss_dp_set_default_resolution(dp_drv);
+
return 0;
}
@@ -1871,14 +1964,16 @@ static int mdss_dp_host_init(struct mdss_panel_data *pdata)
mdss_dp_ctrl_reset(&dp_drv->ctrl_io);
mdss_dp_phy_reset(&dp_drv->ctrl_io);
mdss_dp_aux_reset(&dp_drv->ctrl_io);
+ mdss_dp_aux_set_limits(&dp_drv->ctrl_io);
+
mdss_dp_aux_ctrl(&dp_drv->ctrl_io, true);
pr_debug("Ctrl_hw_rev =0x%x, phy hw_rev =0x%x\n",
mdss_dp_get_ctrl_hw_version(&dp_drv->ctrl_io),
mdss_dp_get_phy_hw_version(&dp_drv->phy_io));
- mdss_dp_phy_aux_setup(&dp_drv->phy_io, dp_drv->aux_cfg,
- dp_drv->phy_reg_offset);
+ mdss_dp_reset_phy_config_indices(dp_drv);
+ mdss_dp_phy_aux_setup(dp_drv);
mdss_dp_irq_enable(dp_drv);
dp_drv->dp_initialized = true;
@@ -1946,10 +2041,11 @@ static int mdss_dp_host_deinit(struct mdss_dp_drv_pdata *dp)
static int mdss_dp_notify_clients(struct mdss_dp_drv_pdata *dp,
enum notification_status status)
{
- const int irq_comp_timeout = HZ * 2;
int ret = 0;
+ bool notify = false;
+ bool connect;
- mutex_lock(&dp->pd_msg_mutex);
+ pr_debug("beginning notification\n");
if (status == dp->hpd_notification_status) {
pr_debug("No change in status %s --> %s\n",
mdss_dp_notification_status_to_string(status),
@@ -1962,39 +2058,40 @@ static int mdss_dp_notify_clients(struct mdss_dp_drv_pdata *dp,
case NOTIFY_CONNECT_IRQ_HPD:
if (dp->hpd_notification_status != NOTIFY_DISCONNECT_IRQ_HPD)
goto invalid_request;
- /* Follow the same programming as for NOTIFY_CONNECT */
- mdss_dp_host_init(&dp->panel_data);
- mdss_dp_send_video_notification(dp, true);
+ notify = true;
+ connect = true;
break;
case NOTIFY_CONNECT:
- if ((dp->hpd_notification_status == NOTIFY_CONNECT_IRQ_HPD) ||
- (dp->hpd_notification_status ==
- NOTIFY_DISCONNECT_IRQ_HPD))
+ if (dp->hpd_notification_status == NOTIFY_CONNECT_IRQ_HPD)
goto invalid_request;
- mdss_dp_host_init(&dp->panel_data);
- mdss_dp_send_video_notification(dp, true);
+ notify = true;
+ connect = true;
break;
case NOTIFY_DISCONNECT:
- mdss_dp_send_audio_notification(dp, false);
- mdss_dp_send_video_notification(dp, false);
+ /*
+ * Userspace triggers a disconnect event on boot up, this must
+ * not be processed as there was no previously connected sink
+ * device.
+ */
+ if (dp->hpd_notification_status == NOTIFY_UNKNOWN)
+ goto invalid_request;
+ if (dp->hpd_notification_status == NOTIFY_DISCONNECT_IRQ_HPD) {
+ /*
+ * user modules already turned off. Need to explicitly
+ * turn off DP core here.
+ */
+ mdss_dp_off_hpd(dp);
+ } else {
+ notify = true;
+ connect = false;
+ }
break;
case NOTIFY_DISCONNECT_IRQ_HPD:
if (dp->hpd_notification_status == NOTIFY_DISCONNECT)
goto invalid_request;
- mdss_dp_send_audio_notification(dp, false);
- mdss_dp_send_video_notification(dp, false);
- if (!IS_ERR_VALUE(ret) && ret) {
- reinit_completion(&dp->irq_comp);
- ret = wait_for_completion_timeout(&dp->irq_comp,
- irq_comp_timeout);
- if (ret <= 0) {
- pr_warn("irq_comp timed out\n");
- ret = -EINVAL;
- } else {
- ret = 0;
- }
- }
+ notify = true;
+ connect = false;
break;
default:
pr_err("Invalid notification status = %d\n", status);
@@ -2002,7 +2099,7 @@ static int mdss_dp_notify_clients(struct mdss_dp_drv_pdata *dp,
break;
}
- goto end;
+ goto notify;
invalid_request:
pr_err("Invalid request %s --> %s\n",
@@ -2011,16 +2108,34 @@ invalid_request:
mdss_dp_notification_status_to_string(status));
ret = -EINVAL;
-end:
+notify:
+ if (ret || !notify) {
+ pr_debug("not sending notification\n");
+ goto end;
+ }
+
+ atomic_set(&dp->notification_pending, 1);
+ if (connect) {
+ mdss_dp_host_init(&dp->panel_data);
+ ret = mdss_dp_send_video_notification(dp, true);
+ } else {
+ mdss_dp_send_audio_notification(dp, false);
+ ret = mdss_dp_send_video_notification(dp, false);
+ }
+
if (!ret) {
pr_debug("Successfully sent notification %s --> %s\n",
mdss_dp_notification_status_to_string(
dp->hpd_notification_status),
mdss_dp_notification_status_to_string(status));
- dp->hpd_notification_status = status;
+ } else {
+ pr_err("%s Notification failed\n",
+ mdss_dp_notification_status_to_string(status));
+ atomic_set(&dp->notification_pending, 0);
}
- mutex_unlock(&dp->pd_msg_mutex);
+end:
+ dp->hpd_notification_status = status;
return ret;
}
@@ -2029,9 +2144,6 @@ static int mdss_dp_process_hpd_high(struct mdss_dp_drv_pdata *dp)
int ret;
u32 max_pclk_khz;
- if (dp->sink_info_read)
- return 0;
-
pr_debug("start\n");
ret = mdss_dp_dpcd_cap_read(dp);
@@ -2044,8 +2156,25 @@ static int mdss_dp_process_hpd_high(struct mdss_dp_drv_pdata *dp)
*/
pr_err("dpcd read failed, set failsafe parameters\n");
mdss_dp_set_default_link_parameters(dp);
+ goto read_edid;
}
+ /*
+ * When connected to a multiport adaptor which does not have a
+ * local EDID present, do not attempt to read the EDID.
+ * When connected to a multiport adaptor with no downstream device
+ * connected to it, do not attempt to read the EDID. It is possible
+ * that the adaptor may advertise the presence of local EDID, but it
+ * is not guaranteed to work.
+ */
+ if (mdss_dp_is_ds_bridge_sink_count_zero(dp)) {
+ if (mdss_dp_is_ds_bridge_no_local_edid(dp))
+ pr_debug("No local EDID present on DS branch device\n");
+ pr_info("no downstream devices, skip client notification\n");
+ goto end;
+ }
+
+read_edid:
ret = mdss_dp_edid_read(dp);
if (ret) {
pr_err("edid read error, setting default resolution\n");
@@ -2056,14 +2185,18 @@ static int mdss_dp_process_hpd_high(struct mdss_dp_drv_pdata *dp)
hdmi_edid_set_max_pclk_rate(dp->panel_data.panel_info.edid_data,
min(dp->max_pclk_khz, max_pclk_khz));
+ if (dp->dpcd_read_required) {
+ pr_debug("reading DPCD with updated AUX config\n");
+ mdss_dp_dpcd_cap_read(dp);
+ dp->dpcd_read_required = false;
+ }
+
ret = hdmi_edid_parser(dp->panel_data.panel_info.edid_data);
if (ret) {
pr_err("edid parse failed, setting default resolution\n");
goto notify;
}
- dp->sink_info_read = true;
-
notify:
if (ret) {
/* set failsafe parameters */
@@ -2090,7 +2223,6 @@ notify:
end:
pr_debug("end\n");
return ret;
-
}
static int mdss_dp_check_params(struct mdss_dp_drv_pdata *dp, void *arg)
@@ -2291,12 +2423,16 @@ static ssize_t mdss_dp_rda_connected(struct device *dev,
{
ssize_t ret;
struct mdss_dp_drv_pdata *dp = mdss_dp_get_drvdata(dev);
+ bool cable_connected;
if (!dp)
return -EINVAL;
- ret = snprintf(buf, PAGE_SIZE, "%d\n", dp->cable_connected);
- pr_debug("%d\n", dp->cable_connected);
+ mutex_lock(&dp->attention_lock);
+ cable_connected = dp->cable_connected;
+ mutex_unlock(&dp->attention_lock);
+ ret = snprintf(buf, PAGE_SIZE, "%d\n", cable_connected);
+ pr_debug("%d\n", cable_connected);
return ret;
}
@@ -2465,6 +2601,7 @@ static ssize_t mdss_dp_wta_hpd(struct device *dev,
int hpd, rc;
ssize_t ret = strnlen(buf, PAGE_SIZE);
struct mdss_dp_drv_pdata *dp = mdss_dp_get_drvdata(dev);
+ bool cable_connected;
if (!dp) {
pr_err("invalid data\n");
@@ -2480,9 +2617,13 @@ static ssize_t mdss_dp_wta_hpd(struct device *dev,
}
dp->hpd = !!hpd;
- pr_debug("hpd=%d\n", dp->hpd);
+ mutex_lock(&dp->attention_lock);
+ cable_connected = dp->cable_connected;
+ mutex_unlock(&dp->attention_lock);
+ pr_debug("hpd=%d cable_connected=%s\n", dp->hpd,
+ cable_connected ? "true" : "false");
- if (dp->hpd && dp->cable_connected) {
+ if (dp->hpd && cable_connected) {
if (dp->alt_mode.current_state & DP_CONFIGURE_DONE) {
mdss_dp_host_init(&dp->panel_data);
mdss_dp_process_hpd_high(dp);
@@ -2812,8 +2953,6 @@ static void mdss_dp_mainlink_push_idle(struct mdss_panel_data *pdata)
/* wait until link training is completed */
mutex_lock(&dp_drv->train_mutex);
- mdss_dp_aux_set_sink_power_state(dp_drv, SINK_POWER_OFF);
-
reinit_completion(&dp_drv->idle_comp);
mdss_dp_state_ctrl(&dp_drv->ctrl_io, ST_PUSH_IDLE);
if (!wait_for_completion_timeout(&dp_drv->idle_comp,
@@ -2903,28 +3042,33 @@ static int mdss_dp_event_handler(struct mdss_panel_data *pdata,
switch (event) {
case MDSS_EVENT_UNBLANK:
- mdss_dp_ack_state(dp, true);
rc = mdss_dp_on(pdata);
break;
case MDSS_EVENT_PANEL_ON:
mdss_dp_update_hdcp_info(dp);
if (dp_is_hdcp_enabled(dp)) {
- cancel_delayed_work(&dp->hdcp_cb_work);
+ cancel_delayed_work_sync(&dp->hdcp_cb_work);
dp->hdcp_status = HDCP_STATE_AUTHENTICATING;
queue_delayed_work(dp->workq,
&dp->hdcp_cb_work, HZ / 2);
}
break;
+ case MDSS_EVENT_POST_PANEL_ON:
+ atomic_set(&dp->notification_pending, 0);
+ complete_all(&dp->notification_comp);
+ break;
case MDSS_EVENT_PANEL_OFF:
rc = mdss_dp_off(pdata);
+ atomic_set(&dp->notification_pending, 0);
+ complete_all(&dp->notification_comp);
break;
case MDSS_EVENT_BLANK:
if (dp_is_hdcp_enabled(dp)) {
dp->hdcp_status = HDCP_STATE_INACTIVE;
- cancel_delayed_work(&dp->hdcp_cb_work);
+ cancel_delayed_work_sync(&dp->hdcp_cb_work);
if (dp->hdcp.ops->off)
dp->hdcp.ops->off(dp->hdcp.data);
}
@@ -3081,6 +3225,7 @@ static int mdss_retrieve_dp_ctrl_resources(struct platform_device *pdev,
static void mdss_dp_video_ready(struct mdss_dp_drv_pdata *dp)
{
pr_debug("dp_video_ready\n");
+ mdss_dp_ack_state(dp, true);
complete(&dp->video_comp);
}
@@ -3112,10 +3257,21 @@ static int mdss_dp_event_thread(void *data)
ev_data = (struct mdss_dp_event_data *)data;
+ pr_debug("starting\n");
while (!kthread_should_stop()) {
wait_event(ev_data->event_q,
(ev_data->pndx != ev_data->gndx) ||
- kthread_should_stop());
+ kthread_should_stop() ||
+ kthread_should_park());
+ if (kthread_should_stop())
+ return 0;
+
+ if (kthread_should_park()) {
+ pr_debug("parking event thread\n");
+ kthread_parkme();
+ continue;
+ }
+
spin_lock_irqsave(&ev_data->event_lock, flag);
ev = &(ev_data->event_list[ev_data->gndx++]);
todo = ev->id;
@@ -3207,6 +3363,7 @@ irqreturn_t dp_isr(int irq, void *ptr)
spin_lock(&dp->lock);
isr1 = dp_read(base + DP_INTR_STATUS);
isr2 = dp_read(base + DP_INTR_STATUS2);
+ pr_debug("isr1=0x%08x, isr2=0x%08x\n", isr1, isr2);
mask1 = isr1 & dp->mask1;
@@ -3290,6 +3447,27 @@ static int mdss_dp_event_setup(struct mdss_dp_drv_pdata *dp)
return 0;
}
+static void mdss_dp_reset_event_list(struct mdss_dp_drv_pdata *dp)
+{
+ struct mdss_dp_event_data *ev_data = &dp->dp_event;
+
+ spin_lock(&ev_data->event_lock);
+ ev_data->pndx = ev_data->gndx = 0;
+ spin_unlock(&ev_data->event_lock);
+
+ mutex_lock(&dp->attention_lock);
+ INIT_LIST_HEAD(&dp->attention_head);
+ mutex_unlock(&dp->attention_lock);
+}
+
+static void mdss_dp_reset_sw_state(struct mdss_dp_drv_pdata *dp)
+{
+ pr_debug("enter\n");
+ mdss_dp_reset_event_list(dp);
+ atomic_set(&dp->notification_pending, 0);
+ complete_all(&dp->notification_comp);
+}
+
static void usbpd_connect_callback(struct usbpd_svid_handler *hdlr)
{
struct mdss_dp_drv_pdata *dp_drv;
@@ -3301,6 +3479,8 @@ static void usbpd_connect_callback(struct usbpd_svid_handler *hdlr)
}
mdss_dp_update_cable_status(dp_drv, true);
+ if (dp_drv->ev_thread)
+ kthread_unpark(dp_drv->ev_thread);
if (dp_drv->hpd)
dp_send_events(dp_drv, EV_USBPD_DISCOVER_MODES);
@@ -3320,6 +3500,9 @@ static void usbpd_disconnect_callback(struct usbpd_svid_handler *hdlr)
mdss_dp_update_cable_status(dp_drv, false);
dp_drv->alt_mode.current_state = UNKNOWN_STATE;
+ mdss_dp_reset_sw_state(dp_drv);
+ kthread_park(dp_drv->ev_thread);
+
/**
* Manually turn off the DP controller if we are in PHY
* testing mode.
@@ -3423,6 +3606,17 @@ static inline void mdss_dp_link_maintenance(struct mdss_dp_drv_pdata *dp,
if (mdss_dp_notify_clients(dp, NOTIFY_DISCONNECT_IRQ_HPD))
return;
+ if (atomic_read(&dp->notification_pending)) {
+ int ret;
+
+ pr_debug("waiting for the disconnect to finish\n");
+ ret = wait_for_completion_timeout(&dp->notification_comp, HZ);
+ if (ret <= 0) {
+ pr_warn("NOTIFY_DISCONNECT_IRQ_HPD timed out\n");
+ return;
+ }
+ }
+
mdss_dp_on_irq(dp, lt_needed);
}
@@ -3574,7 +3768,7 @@ static int mdss_dp_process_audio_pattern_request(struct mdss_dp_drv_pdata *dp)
return -EINVAL;
if (dp_is_hdcp_enabled(dp) && dp->hdcp.ops->off) {
- cancel_delayed_work(&dp->hdcp_cb_work);
+ cancel_delayed_work_sync(&dp->hdcp_cb_work);
dp->hdcp.ops->off(dp->hdcp.data);
}
@@ -3620,10 +3814,46 @@ static int mdss_dp_process_audio_pattern_request(struct mdss_dp_drv_pdata *dp)
static int mdss_dp_process_downstream_port_status_change(
struct mdss_dp_drv_pdata *dp)
{
- if (!mdss_dp_is_downstream_port_status_changed(dp))
+ bool ds_status_changed = false;
+
+ if (mdss_dp_is_downstream_port_status_changed(dp)) {
+ pr_debug("downstream port status changed\n");
+ ds_status_changed = true;
+ }
+
+ /*
+ * Ideally sink should update the downstream port status changed
+ * whenever it updates the downstream sink count. However, it is
+ * possible that only the sink count is updated without setting
+ * the downstream port status changed bit.
+ */
+ if (dp->sink_count.count != dp->prev_sink_count.count) {
+ pr_debug("downstream sink count changed from %d --> %d\n",
+ dp->prev_sink_count.count, dp->sink_count.count);
+ ds_status_changed = true;
+ }
+
+ if (!ds_status_changed)
return -EINVAL;
- return mdss_dp_edid_read(dp);
+ mdss_dp_notify_clients(dp, NOTIFY_DISCONNECT_IRQ_HPD);
+ if (atomic_read(&dp->notification_pending)) {
+ int ret;
+
+ pr_debug("waiting for the disconnect to finish\n");
+ ret = wait_for_completion_timeout(&dp->notification_comp, HZ);
+ if (ret <= 0) {
+ pr_warn("NOTIFY_DISCONNECT_IRQ_HPD timed out\n");
+ return -ETIMEDOUT;
+ }
+ }
+
+ if (mdss_dp_is_ds_bridge_sink_count_zero(dp)) {
+ pr_debug("sink count is zero, nothing to do\n");
+ return 0;
+ }
+
+ return mdss_dp_process_hpd_high(dp);
}
static bool mdss_dp_video_pattern_test_lt_needed(struct mdss_dp_drv_pdata *dp)
@@ -3721,19 +3951,19 @@ static int mdss_dp_process_hpd_irq_high(struct mdss_dp_drv_pdata *dp)
mdss_dp_aux_parse_sink_status_field(dp);
- ret = mdss_dp_process_link_training_request(dp);
+ ret = mdss_dp_process_downstream_port_status_change(dp);
if (!ret)
goto exit;
- ret = mdss_dp_process_phy_test_pattern_request(dp);
+ ret = mdss_dp_process_link_training_request(dp);
if (!ret)
goto exit;
- ret = mdss_dp_process_link_status_update(dp);
+ ret = mdss_dp_process_phy_test_pattern_request(dp);
if (!ret)
goto exit;
- ret = mdss_dp_process_downstream_port_status_change(dp);
+ ret = mdss_dp_process_link_status_update(dp);
if (!ret)
goto exit;
@@ -3746,7 +3976,6 @@ static int mdss_dp_process_hpd_irq_high(struct mdss_dp_drv_pdata *dp)
goto exit;
pr_debug("done\n");
-
exit:
dp->hpd_irq_on = false;
return ret;
@@ -3792,7 +4021,8 @@ static void usbpd_response_callback(struct usbpd_svid_handler *hdlr, u8 cmd,
node->vdo = *vdos;
mutex_lock(&dp_drv->attention_lock);
- list_add_tail(&node->list, &dp_drv->attention_head);
+ if (dp_drv->cable_connected)
+ list_add_tail(&node->list, &dp_drv->attention_head);
mutex_unlock(&dp_drv->attention_lock);
dp_send_events(dp_drv, EV_USBPD_ATTENTION);
@@ -3840,11 +4070,21 @@ static void mdss_dp_process_attention(struct mdss_dp_drv_pdata *dp_drv)
if (!dp_drv->alt_mode.dp_status.hpd_high) {
pr_debug("Attention: HPD low\n");
+ if (!dp_drv->power_on) {
+ pr_debug("HPD already low\n");
+ return;
+ }
+
if (dp_is_hdcp_enabled(dp_drv) && dp_drv->hdcp.ops->off) {
- cancel_delayed_work(&dp_drv->hdcp_cb_work);
+ cancel_delayed_work_sync(&dp_drv->hdcp_cb_work);
dp_drv->hdcp.ops->off(dp_drv->hdcp.data);
}
+ /*
+ * Reset the sink count before nofifying clients since HPD Low
+ * indicates that the downstream device has been disconnected.
+ */
+ mdss_dp_reset_sink_count(dp_drv);
mdss_dp_notify_clients(dp_drv, NOTIFY_DISCONNECT);
pr_debug("Attention: Notified clients\n");
@@ -3872,6 +4112,11 @@ static void mdss_dp_process_attention(struct mdss_dp_drv_pdata *dp_drv)
pr_debug("Attention: HPD high\n");
+ if (dp_drv->power_on) {
+ pr_debug("HPD high processed already\n");
+ return;
+ }
+
dp_drv->alt_mode.current_state |= DP_STATUS_DONE;
if (dp_drv->alt_mode.current_state & DP_CONFIGURE_DONE) {
@@ -3893,7 +4138,13 @@ static void mdss_dp_handle_attention(struct mdss_dp_drv_pdata *dp)
pr_debug("processing item %d in the list\n", ++i);
+ reinit_completion(&dp->notification_comp);
mutex_lock(&dp->attention_lock);
+ if (!dp->cable_connected) {
+ pr_debug("cable disconnected, returning\n");
+ mutex_unlock(&dp->attention_lock);
+ goto exit;
+ }
node = list_first_entry(&dp->attention_head,
struct mdss_dp_attention_node, list);
@@ -3907,9 +4158,25 @@ static void mdss_dp_handle_attention(struct mdss_dp_drv_pdata *dp)
mdss_dp_usbpd_ext_dp_status(&dp->alt_mode.dp_status);
mdss_dp_process_attention(dp);
+ if (atomic_read(&dp->notification_pending)) {
+ pr_debug("waiting for the attention event to finish\n");
+ /*
+ * This wait is intentionally implemented without a
+ * timeout since this is happens only in possible error
+ * conditions e.g. if the display framework does not
+ * power off/on the DisplayPort device in time. Other
+ * events might already be queued from the sink at this
+ * point and they cannot be processed until the power
+ * off/on is complete otherwise we might have problems
+ * with interleaving of these events e.g. un-clocked
+ * register access.
+ */
+ wait_for_completion(&dp->notification_comp);
+ }
pr_debug("done processing item %d in the list\n", i);
};
+exit:
pr_debug("exit\n");
}
@@ -3985,7 +4252,6 @@ static int mdss_dp_probe(struct platform_device *pdev)
dp_drv->mask1 = EDP_INTR_MASK1;
dp_drv->mask2 = EDP_INTR_MASK2;
mutex_init(&dp_drv->emutex);
- mutex_init(&dp_drv->pd_msg_mutex);
mutex_init(&dp_drv->attention_lock);
mutex_init(&dp_drv->hdcp_mutex);
spin_lock_init(&dp_drv->lock);
@@ -4078,8 +4344,9 @@ static int mdss_dp_probe(struct platform_device *pdev)
dp_drv->inited = true;
dp_drv->hpd_irq_on = false;
+ atomic_set(&dp_drv->notification_pending, 0);
mdss_dp_reset_test_data(dp_drv);
- init_completion(&dp_drv->irq_comp);
+ init_completion(&dp_drv->notification_comp);
dp_drv->suspend_vic = HDMI_VFRMT_UNKNOWN;
pr_debug("done\n");
diff --git a/drivers/video/fbdev/msm/mdss_dp.h b/drivers/video/fbdev/msm/mdss_dp.h
index 4decb26ea073..f358aad8a667 100644
--- a/drivers/video/fbdev/msm/mdss_dp.h
+++ b/drivers/video/fbdev/msm/mdss_dp.h
@@ -77,7 +77,7 @@
#define EDP_INTR_I2C_NACK BIT(18)
#define EDP_INTR_I2C_DEFER BIT(21)
#define EDP_INTR_PLL_UNLOCKED BIT(24)
-#define EDP_INTR_AUX_ERROR BIT(27)
+#define EDP_INTR_PHY_AUX_ERR BIT(27)
#define EDP_INTR_STATUS1 \
@@ -85,7 +85,7 @@
EDP_INTR_WRONG_ADDR | EDP_INTR_TIMEOUT | \
EDP_INTR_NACK_DEFER | EDP_INTR_WRONG_DATA_CNT | \
EDP_INTR_I2C_NACK | EDP_INTR_I2C_DEFER | \
- EDP_INTR_PLL_UNLOCKED | EDP_INTR_AUX_ERROR)
+ EDP_INTR_PLL_UNLOCKED | EDP_INTR_PHY_AUX_ERR)
#define EDP_INTR_MASK1 (EDP_INTR_STATUS1 << 2)
@@ -110,6 +110,8 @@ struct edp_buf {
int len; /* dara length */
char trans_num; /* transaction number */
char i2c; /* 1 == i2c cmd, 0 == native cmd */
+ bool no_send_addr;
+ bool no_send_stop;
};
/* USBPD-TypeC specific Macros */
@@ -186,6 +188,7 @@ struct dp_alt_mode {
#define DPCD_MAX_DOWNSPREAD_0_5 BIT(2)
#define DPCD_NO_AUX_HANDSHAKE BIT(3)
#define DPCD_PORT_0_EDID_PRESENTED BIT(4)
+#define DPCD_PORT_1_EDID_PRESENTED BIT(5)
/* event */
#define EV_EDP_AUX_SETUP BIT(0)
@@ -239,6 +242,8 @@ struct downstream_port_config {
bool oui_support;
};
+#define DP_MAX_DS_PORT_COUNT 2
+
struct dpcd_cap {
char major;
char minor;
@@ -249,7 +254,7 @@ struct dpcd_cap {
char enhanced_frame;
u32 max_link_rate; /* 162, 270 and 540 Mb, divided by 10 */
u32 flags;
- u32 rx_port0_buf_size;
+ u32 rx_port_buf_size[DP_MAX_DS_PORT_COUNT];
u32 training_read_interval;/* us */
struct downstream_port_config downstream_port;
};
@@ -426,6 +431,102 @@ struct mdss_dp_crc_data {
u32 b_cb;
};
+#define MDSS_DP_MAX_PHY_CFG_VALUE_CNT 3
+struct mdss_dp_phy_cfg {
+ u32 cfg_cnt;
+ u32 current_index;
+ u32 offset;
+ u32 lut[MDSS_DP_MAX_PHY_CFG_VALUE_CNT];
+};
+
+/* PHY AUX config registers */
+enum dp_phy_aux_config_type {
+ PHY_AUX_CFG0,
+ PHY_AUX_CFG1,
+ PHY_AUX_CFG2,
+ PHY_AUX_CFG3,
+ PHY_AUX_CFG4,
+ PHY_AUX_CFG5,
+ PHY_AUX_CFG6,
+ PHY_AUX_CFG7,
+ PHY_AUX_CFG8,
+ PHY_AUX_CFG9,
+ PHY_AUX_CFG_MAX,
+};
+
+static inline const char *mdss_dp_get_phy_aux_config_property(u32 cfg_type)
+{
+ switch (cfg_type) {
+ case PHY_AUX_CFG0:
+ return "qcom,aux-cfg0-settings";
+ case PHY_AUX_CFG1:
+ return "qcom,aux-cfg1-settings";
+ case PHY_AUX_CFG2:
+ return "qcom,aux-cfg2-settings";
+ case PHY_AUX_CFG3:
+ return "qcom,aux-cfg3-settings";
+ case PHY_AUX_CFG4:
+ return "qcom,aux-cfg4-settings";
+ case PHY_AUX_CFG5:
+ return "qcom,aux-cfg5-settings";
+ case PHY_AUX_CFG6:
+ return "qcom,aux-cfg6-settings";
+ case PHY_AUX_CFG7:
+ return "qcom,aux-cfg7-settings";
+ case PHY_AUX_CFG8:
+ return "qcom,aux-cfg8-settings";
+ case PHY_AUX_CFG9:
+ return "qcom,aux-cfg9-settings";
+ default:
+ return "unknown";
+ }
+}
+
+static inline char *mdss_dp_phy_aux_config_type_to_string(u32 cfg_type)
+{
+ switch (cfg_type) {
+ case PHY_AUX_CFG0:
+ return DP_ENUM_STR(PHY_AUX_CFG0);
+ case PHY_AUX_CFG1:
+ return DP_ENUM_STR(PHY_AUX_CFG1);
+ case PHY_AUX_CFG2:
+ return DP_ENUM_STR(PHY_AUX_CFG2);
+ case PHY_AUX_CFG3:
+ return DP_ENUM_STR(PHY_AUX_CFG3);
+ case PHY_AUX_CFG4:
+ return DP_ENUM_STR(PHY_AUX_CFG4);
+ case PHY_AUX_CFG5:
+ return DP_ENUM_STR(PHY_AUX_CFG5);
+ case PHY_AUX_CFG6:
+ return DP_ENUM_STR(PHY_AUX_CFG6);
+ case PHY_AUX_CFG7:
+ return DP_ENUM_STR(PHY_AUX_CFG7);
+ case PHY_AUX_CFG8:
+ return DP_ENUM_STR(PHY_AUX_CFG8);
+ case PHY_AUX_CFG9:
+ return DP_ENUM_STR(PHY_AUX_CFG9);
+ default:
+ return "unknown";
+ }
+}
+
+enum dp_aux_transaction {
+ DP_AUX_WRITE,
+ DP_AUX_READ
+};
+
+static inline char *mdss_dp_aux_transaction_to_string(u32 transaction)
+{
+ switch (transaction) {
+ case DP_AUX_WRITE:
+ return DP_ENUM_STR(DP_AUX_WRITE);
+ case DP_AUX_READ:
+ return DP_ENUM_STR(DP_AUX_READ);
+ default:
+ return "unknown";
+ }
+}
+
struct mdss_dp_drv_pdata {
/* device driver */
int (*on) (struct mdss_panel_data *pdata);
@@ -449,11 +550,11 @@ struct mdss_dp_drv_pdata {
bool core_clks_on;
bool link_clks_on;
bool power_on;
- bool sink_info_read;
u32 suspend_vic;
bool hpd;
bool psm_enabled;
bool audio_test_req;
+ bool dpcd_read_required;
/* dp specific */
unsigned char *base;
@@ -513,10 +614,9 @@ struct mdss_dp_drv_pdata {
struct completion aux_comp;
struct completion idle_comp;
struct completion video_comp;
- struct completion irq_comp;
+ struct completion notification_comp;
struct mutex aux_mutex;
struct mutex train_mutex;
- struct mutex pd_msg_mutex;
struct mutex attention_lock;
struct mutex hdcp_mutex;
bool cable_connected;
@@ -540,13 +640,14 @@ struct mdss_dp_drv_pdata {
struct dp_statistic dp_stat;
bool hpd_irq_on;
u32 hpd_notification_status;
+ atomic_t notification_pending;
struct mdss_dp_event_data dp_event;
struct task_struct *ev_thread;
/* dt settings */
char l_map[4];
- u32 aux_cfg[AUX_CFG_LEN];
+ struct mdss_dp_phy_cfg aux_cfg[PHY_AUX_CFG_MAX];
struct workqueue_struct *workq;
struct delayed_work hdcp_cb_work;
@@ -562,6 +663,7 @@ struct mdss_dp_drv_pdata {
struct dpcd_test_request test_data;
struct dpcd_sink_count sink_count;
+ struct dpcd_sink_count prev_sink_count;
struct list_head attention_head;
};
@@ -688,6 +790,7 @@ enum dp_aux_error {
EDP_AUX_ERR_NACK = -3,
EDP_AUX_ERR_DEFER = -4,
EDP_AUX_ERR_NACK_DEFER = -5,
+ EDP_AUX_ERR_PHY = -6,
};
static inline char *mdss_dp_get_aux_error(u32 aux_error)
diff --git a/drivers/video/fbdev/msm/mdss_dp_aux.c b/drivers/video/fbdev/msm/mdss_dp_aux.c
index 8566b1d6985a..37209c161366 100644
--- a/drivers/video/fbdev/msm/mdss_dp_aux.c
+++ b/drivers/video/fbdev/msm/mdss_dp_aux.c
@@ -73,6 +73,21 @@ static int dp_buf_trailing(struct edp_buf *eb)
return (int)(eb->end - eb->data);
}
+static void mdss_dp_aux_clear_hw_interrupts(void __iomem *phy_base)
+{
+ u32 data;
+
+ data = dp_read(phy_base + DP_PHY_AUX_INTERRUPT_STATUS);
+ pr_debug("PHY_AUX_INTERRUPT_STATUS=0x%08x\n", data);
+
+ dp_write(phy_base + DP_PHY_AUX_INTERRUPT_CLEAR, 0x1f);
+ dp_write(phy_base + DP_PHY_AUX_INTERRUPT_CLEAR, 0x9f);
+ dp_write(phy_base + DP_PHY_AUX_INTERRUPT_CLEAR, 0);
+
+ /* Ensure that all interrupts are cleared and acked */
+ wmb();
+}
+
/*
* edp aux dp_buf_add_cmd:
* NO native and i2c command mix allowed
@@ -123,35 +138,46 @@ static int dp_buf_add_cmd(struct edp_buf *eb, struct edp_cmd *cmd)
return cmd->len - 1;
}
-static int dp_cmd_fifo_tx(struct edp_buf *tp, unsigned char *base)
+static int dp_cmd_fifo_tx(struct mdss_dp_drv_pdata *dp)
{
u32 data;
- char *dp;
+ char *datap;
int len, cnt;
+ struct edp_buf *tp = &dp->txp;
+ void __iomem *base = dp->base;
+
len = tp->len; /* total byte to cmd fifo */
if (len == 0)
return 0;
cnt = 0;
- dp = tp->start;
+ datap = tp->start;
while (cnt < len) {
- data = *dp; /* data byte */
+ data = *datap; /* data byte */
data <<= 8;
data &= 0x00ff00; /* index = 0, write */
if (cnt == 0)
data |= BIT(31); /* INDEX_WRITE */
dp_write(base + DP_AUX_DATA, data);
cnt++;
- dp++;
+ datap++;
}
+ /* clear the current tx request before queuing a new one */
+ dp_write(base + DP_AUX_TRANS_CTRL, 0);
+
+ /* clear any previous PHY AUX interrupts */
+ mdss_dp_aux_clear_hw_interrupts(dp->phy_io.base);
+
data = (tp->trans_num - 1);
if (tp->i2c) {
data |= BIT(8); /* I2C */
- data |= BIT(10); /* NO SEND ADDR */
- data |= BIT(11); /* NO SEND STOP */
+ if (tp->no_send_addr)
+ data |= BIT(10); /* NO SEND ADDR */
+ if (tp->no_send_stop)
+ data |= BIT(11); /* NO SEND STOP */
}
data |= BIT(9); /* GO */
@@ -164,7 +190,7 @@ static int dp_cmd_fifo_rx(struct edp_buf *rp, int len, unsigned char *base)
{
u32 data;
char *dp;
- int i;
+ int i, actual_i;
data = 0; /* index = 0 */
data |= BIT(31); /* INDEX_WRITE */
@@ -177,7 +203,12 @@ static int dp_cmd_fifo_rx(struct edp_buf *rp, int len, unsigned char *base)
data = dp_read(base + DP_AUX_DATA);
for (i = 0; i < len; i++) {
data = dp_read(base + DP_AUX_DATA);
- *dp++ = (char)((data >> 8) & 0xff);
+ *dp++ = (char)((data >> 8) & 0xFF);
+
+ actual_i = (data >> 16) & 0xFF;
+ if (i != actual_i)
+ pr_warn("Index mismatch: expected %d, found %d\n",
+ i, actual_i);
}
rp->len = len;
@@ -214,9 +245,16 @@ static int dp_aux_write_cmds(struct mdss_dp_drv_pdata *ep,
reinit_completion(&ep->aux_comp);
- len = dp_cmd_fifo_tx(&ep->txp, ep->base);
+ tp->no_send_addr = true;
+ tp->no_send_stop = true;
+ len = dp_cmd_fifo_tx(ep);
- wait_for_completion_timeout(&ep->aux_comp, HZ/4);
+ if (!wait_for_completion_timeout(&ep->aux_comp, HZ/4)) {
+ pr_err("aux write timeout\n");
+ ep->aux_error_num = EDP_AUX_ERR_TOUT;
+ /* Reset the AUX controller state machine */
+ mdss_dp_aux_reset(&ep->ctrl_io);
+ }
if (ep->aux_error_num == EDP_AUX_ERR_NONE)
ret = len;
@@ -228,13 +266,6 @@ static int dp_aux_write_cmds(struct mdss_dp_drv_pdata *ep,
return ret;
}
-int dp_aux_write(void *ep, struct edp_cmd *cmd)
-{
- int rc = dp_aux_write_cmds(ep, cmd);
-
- return rc < 0 ? -EINVAL : 0;
-}
-
static int dp_aux_read_cmds(struct mdss_dp_drv_pdata *ep,
struct edp_cmd *cmds)
{
@@ -242,6 +273,7 @@ static int dp_aux_read_cmds(struct mdss_dp_drv_pdata *ep,
struct edp_buf *tp;
struct edp_buf *rp;
int len, ret;
+ u32 data;
mutex_lock(&ep->aux_mutex);
ep->aux_cmd_busy = 1;
@@ -270,10 +302,23 @@ static int dp_aux_read_cmds(struct mdss_dp_drv_pdata *ep,
reinit_completion(&ep->aux_comp);
- dp_cmd_fifo_tx(tp, ep->base);
+ tp->no_send_addr = true;
+ tp->no_send_stop = false;
+ dp_cmd_fifo_tx(ep);
- wait_for_completion_timeout(&ep->aux_comp, HZ/4);
+ if (!wait_for_completion_timeout(&ep->aux_comp, HZ/4)) {
+ pr_err("aux read timeout\n");
+ ep->aux_error_num = EDP_AUX_ERR_TOUT;
+ /* Reset the AUX controller state machine */
+ mdss_dp_aux_reset(&ep->ctrl_io);
+ ret = ep->aux_error_num;
+ goto end;
+ }
+ /* clear the current rx request before queuing a new one */
+ data = dp_read(ep->base + DP_AUX_TRANS_CTRL);
+ data &= (~BIT(9));
+ dp_write(ep->base + DP_AUX_TRANS_CTRL, data);
if (ep->aux_error_num == EDP_AUX_ERR_NONE) {
ret = dp_cmd_fifo_rx(rp, len, ep->base);
@@ -284,58 +329,128 @@ static int dp_aux_read_cmds(struct mdss_dp_drv_pdata *ep,
ret = ep->aux_error_num;
}
+end:
ep->aux_cmd_busy = 0;
mutex_unlock(&ep->aux_mutex);
return ret;
}
-int dp_aux_read(void *ep, struct edp_cmd *cmds)
-{
- int rc = dp_aux_read_cmds(ep, cmds);
-
- return rc < 0 ? -EINVAL : 0;
-}
-
void dp_aux_native_handler(struct mdss_dp_drv_pdata *ep, u32 isr)
{
- if (isr & EDP_INTR_AUX_I2C_DONE)
+ pr_debug("isr=0x%08x\n", isr);
+ if (isr & EDP_INTR_AUX_I2C_DONE) {
ep->aux_error_num = EDP_AUX_ERR_NONE;
- else if (isr & EDP_INTR_WRONG_ADDR)
+ } else if (isr & EDP_INTR_WRONG_ADDR) {
ep->aux_error_num = EDP_AUX_ERR_ADDR;
- else if (isr & EDP_INTR_TIMEOUT)
+ } else if (isr & EDP_INTR_TIMEOUT) {
ep->aux_error_num = EDP_AUX_ERR_TOUT;
- if (isr & EDP_INTR_NACK_DEFER)
+ } else if (isr & EDP_INTR_NACK_DEFER) {
ep->aux_error_num = EDP_AUX_ERR_NACK;
+ } else if (isr & EDP_INTR_PHY_AUX_ERR) {
+ ep->aux_error_num = EDP_AUX_ERR_PHY;
+ mdss_dp_aux_clear_hw_interrupts(ep->phy_io.base);
+ } else {
+ ep->aux_error_num = EDP_AUX_ERR_NONE;
+ }
complete(&ep->aux_comp);
}
void dp_aux_i2c_handler(struct mdss_dp_drv_pdata *ep, u32 isr)
{
+ pr_debug("isr=0x%08x\n", isr);
if (isr & EDP_INTR_AUX_I2C_DONE) {
if (isr & (EDP_INTR_I2C_NACK | EDP_INTR_I2C_DEFER))
ep->aux_error_num = EDP_AUX_ERR_NACK;
else
ep->aux_error_num = EDP_AUX_ERR_NONE;
} else {
- if (isr & EDP_INTR_WRONG_ADDR)
+ if (isr & EDP_INTR_WRONG_ADDR) {
ep->aux_error_num = EDP_AUX_ERR_ADDR;
- else if (isr & EDP_INTR_TIMEOUT)
+ } else if (isr & EDP_INTR_TIMEOUT) {
ep->aux_error_num = EDP_AUX_ERR_TOUT;
- if (isr & EDP_INTR_NACK_DEFER)
+ } else if (isr & EDP_INTR_NACK_DEFER) {
ep->aux_error_num = EDP_AUX_ERR_NACK_DEFER;
- if (isr & EDP_INTR_I2C_NACK)
+ } else if (isr & EDP_INTR_I2C_NACK) {
ep->aux_error_num = EDP_AUX_ERR_NACK;
- if (isr & EDP_INTR_I2C_DEFER)
+ } else if (isr & EDP_INTR_I2C_DEFER) {
ep->aux_error_num = EDP_AUX_ERR_DEFER;
+ } else if (isr & EDP_INTR_PHY_AUX_ERR) {
+ ep->aux_error_num = EDP_AUX_ERR_PHY;
+ mdss_dp_aux_clear_hw_interrupts(ep->phy_io.base);
+ } else {
+ ep->aux_error_num = EDP_AUX_ERR_NONE;
+ }
}
complete(&ep->aux_comp);
}
-static int dp_aux_write_buf(struct mdss_dp_drv_pdata *ep, u32 addr,
- char *buf, int len, int i2c)
+static int dp_aux_rw_cmds_retry(struct mdss_dp_drv_pdata *dp,
+ struct edp_cmd *cmd, enum dp_aux_transaction transaction)
+{
+ int const retry_count = 5;
+ int adjust_count = 0;
+ int i;
+ u32 aux_cfg1_config_count;
+ int ret;
+
+ aux_cfg1_config_count = mdss_dp_phy_aux_get_config_cnt(dp,
+ PHY_AUX_CFG1);
+retry:
+ i = 0;
+ ret = 0;
+ do {
+ struct edp_cmd cmd1 = *cmd;
+
+ dp->aux_error_num = EDP_AUX_ERR_NONE;
+ pr_debug("Trying %s, iteration count: %d\n",
+ mdss_dp_aux_transaction_to_string(transaction),
+ i + 1);
+ if (transaction == DP_AUX_READ)
+ ret = dp_aux_read_cmds(dp, &cmd1);
+ else if (transaction == DP_AUX_WRITE)
+ ret = dp_aux_write_cmds(dp, &cmd1);
+
+ i++;
+ } while ((i < retry_count) && (ret < 0));
+
+ if (ret >= 0) /* rw success */
+ goto end;
+
+ if (adjust_count >= aux_cfg1_config_count) {
+ pr_err("PHY_AUX_CONFIG1 calibration failed\n");
+ goto end;
+ }
+
+ /* Adjust AUX configuration and retry */
+ pr_debug("AUX failure (%d), adjust AUX settings\n", ret);
+ mdss_dp_phy_aux_update_config(dp, PHY_AUX_CFG1);
+ adjust_count++;
+ goto retry;
+
+end:
+ return ret;
+}
+
+/**
+ * dp_aux_write_buf_retry() - send a AUX write command
+ * @dp: display port driver data
+ * @addr: AUX address (in hex) to write the command to
+ * @buf: the buffer containing the actual payload
+ * @len: the length of the buffer @buf
+ * @i2c: indicates if it is an i2c-over-aux transaction
+ * @retry: specifies if retries should be attempted upon failures
+ *
+ * Send an AUX write command with the specified payload over the AUX
+ * channel. This function can send both native AUX command or an
+ * i2c-over-AUX command. In addition, if specified, it can also retry
+ * when failures are detected. The retry logic would adjust AUX PHY
+ * parameters on the fly.
+ */
+static int dp_aux_write_buf_retry(struct mdss_dp_drv_pdata *dp, u32 addr,
+ char *buf, int len, int i2c, bool retry)
{
struct edp_cmd cmd;
@@ -346,11 +461,42 @@ static int dp_aux_write_buf(struct mdss_dp_drv_pdata *ep, u32 addr,
cmd.len = len & 0x0ff;
cmd.next = 0;
- return dp_aux_write_cmds(ep, &cmd);
+ if (retry)
+ return dp_aux_rw_cmds_retry(dp, &cmd, DP_AUX_WRITE);
+ else
+ return dp_aux_write_cmds(dp, &cmd);
}
-static int dp_aux_read_buf(struct mdss_dp_drv_pdata *ep, u32 addr,
- int len, int i2c)
+static int dp_aux_write_buf(struct mdss_dp_drv_pdata *dp, u32 addr,
+ char *buf, int len, int i2c)
+{
+ return dp_aux_write_buf_retry(dp, addr, buf, len, i2c, true);
+}
+
+int dp_aux_write(void *dp, struct edp_cmd *cmd)
+{
+ int rc = dp_aux_write_cmds(dp, cmd);
+
+ return rc < 0 ? -EINVAL : 0;
+}
+
+/**
+ * dp_aux_read_buf_retry() - send a AUX read command
+ * @dp: display port driver data
+ * @addr: AUX address (in hex) to write the command to
+ * @buf: the buffer containing the actual payload
+ * @len: the length of the buffer @buf
+ * @i2c: indicates if it is an i2c-over-aux transaction
+ * @retry: specifies if retries should be attempted upon failures
+ *
+ * Send an AUX write command with the specified payload over the AUX
+ * channel. This function can send both native AUX command or an
+ * i2c-over-AUX command. In addition, if specified, it can also retry
+ * when failures are detected. The retry logic would adjust AUX PHY
+ * parameters on the fly.
+ */
+static int dp_aux_read_buf_retry(struct mdss_dp_drv_pdata *dp, u32 addr,
+ int len, int i2c, bool retry)
{
struct edp_cmd cmd = {0};
@@ -361,7 +507,23 @@ static int dp_aux_read_buf(struct mdss_dp_drv_pdata *ep, u32 addr,
cmd.len = len & 0x0ff;
cmd.next = 0;
- return dp_aux_read_cmds(ep, &cmd);
+ if (retry)
+ return dp_aux_rw_cmds_retry(dp, &cmd, DP_AUX_READ);
+ else
+ return dp_aux_read_cmds(dp, &cmd);
+}
+
+static int dp_aux_read_buf(struct mdss_dp_drv_pdata *dp, u32 addr,
+ int len, int i2c)
+{
+ return dp_aux_read_buf_retry(dp, addr, len, i2c, true);
+}
+
+int dp_aux_read(void *dp, struct edp_cmd *cmds)
+{
+ int rc = dp_aux_read_cmds(dp, cmds);
+
+ return rc < 0 ? -EINVAL : 0;
}
/*
@@ -733,16 +895,68 @@ static void dp_aux_send_checksum(struct mdss_dp_drv_pdata *dp, u32 checksum)
dp_aux_write_buf(dp, 0x260, data, 1, 0);
}
+int mdss_dp_aux_read_edid(struct mdss_dp_drv_pdata *dp,
+ u8 *buf, int size, int blk_num)
+{
+ int max_size_bytes = 16;
+ int rc, read_size;
+ int ret = 0;
+ u8 offset_lut[] = {0x0, 0x80};
+ u8 offset;
+
+ if (dp->test_data.test_requested == TEST_EDID_READ)
+ max_size_bytes = 128;
+
+ /*
+ * Calculate the offset of the desired EDID block to be read.
+ * For even blocks, offset starts at 0x0
+ * For odd blocks, offset starts at 0x80
+ */
+ if (blk_num % 2)
+ offset = offset_lut[1];
+ else
+ offset = offset_lut[0];
+
+ do {
+ struct edp_cmd cmd = {0};
+
+ read_size = min(size, max_size_bytes);
+ cmd.read = 1;
+ cmd.addr = EDID_START_ADDRESS;
+ cmd.len = read_size;
+ cmd.out_buf = buf;
+ cmd.i2c = 1;
+
+ /* Write the offset first prior to reading the data */
+ pr_debug("offset=0x%x, size=%d\n", offset, size);
+ dp_aux_write_buf_retry(dp, EDID_START_ADDRESS, &offset, 1, 1,
+ false);
+ rc = dp_aux_read(dp, &cmd);
+ if (rc < 0) {
+ pr_err("aux read failed\n");
+ return rc;
+ }
+
+ print_hex_dump(KERN_DEBUG, "DP:EDID: ", DUMP_PREFIX_NONE, 16, 1,
+ buf, read_size, false);
+ buf += read_size;
+ offset += read_size;
+ size -= read_size;
+ ret += read_size;
+ } while (size > 0);
+
+ return ret;
+}
+
int mdss_dp_edid_read(struct mdss_dp_drv_pdata *dp)
{
- struct edp_buf *rp = &dp->rxp;
int rlen, ret = 0;
int edid_blk = 0, blk_num = 0, retries = 10;
bool edid_parsing_done = false;
- const u8 cea_tag = 0x02, start_ext_blk = 0x1;
u32 const segment_addr = 0x30;
u32 checksum = 0;
- char segment = 0x1;
+ bool phy_aux_update_requested = false;
+ bool ext_block_parsing_done = false;
ret = dp_aux_chan_ready(dp);
if (ret) {
@@ -750,72 +964,91 @@ int mdss_dp_edid_read(struct mdss_dp_drv_pdata *dp)
return ret;
}
+ memset(dp->edid_buf, 0, dp->edid_buf_size);
+
/**
* Parse the test request vector to see whether there is a
* TEST_EDID_READ test request.
*/
dp_sink_parse_test_request(dp);
- do {
- rlen = dp_aux_read_buf(dp, EDID_START_ADDRESS +
- (blk_num * EDID_BLOCK_SIZE),
- EDID_BLOCK_SIZE, 1);
+ while (retries) {
+ u8 segment;
+ u8 edid_buf[EDID_BLOCK_SIZE] = {0};
+
+ /*
+ * Write the segment first.
+ * Segment = 0, for blocks 0 and 1
+ * Segment = 1, for blocks 2 and 3
+ * Segment = 2, for blocks 3 and 4
+ * and so on ...
+ */
+ segment = blk_num >> 1;
+ dp_aux_write_buf_retry(dp, segment_addr, &segment, 1, 1, false);
+
+ rlen = mdss_dp_aux_read_edid(dp, edid_buf, EDID_BLOCK_SIZE,
+ blk_num);
if (rlen != EDID_BLOCK_SIZE) {
- pr_err("Read failed. rlen=%d\n", rlen);
+ pr_err("Read failed. rlen=%s\n",
+ mdss_dp_get_aux_error(rlen));
+ mdss_dp_phy_aux_update_config(dp, PHY_AUX_CFG1);
+ phy_aux_update_requested = true;
+ retries--;
continue;
}
-
pr_debug("blk_num=%d, rlen=%d\n", blk_num, rlen);
-
- if (dp_edid_is_valid_header(rp->data)) {
- ret = dp_edid_buf_error(rp->data, rp->len);
+ print_hex_dump(KERN_DEBUG, "DP:EDID: ", DUMP_PREFIX_NONE, 16, 1,
+ edid_buf, EDID_BLOCK_SIZE, false);
+ if (dp_edid_is_valid_header(edid_buf)) {
+ ret = dp_edid_buf_error(edid_buf, rlen);
if (ret) {
pr_err("corrupt edid block detected\n");
+ mdss_dp_phy_aux_update_config(dp, PHY_AUX_CFG1);
+ phy_aux_update_requested = true;
+ retries--;
continue;
}
if (edid_parsing_done) {
+ pr_debug("block 0 parsed already\n");
blk_num++;
+ retries--;
continue;
}
- dp_extract_edid_manufacturer(&dp->edid, rp->data);
- dp_extract_edid_product(&dp->edid, rp->data);
- dp_extract_edid_version(&dp->edid, rp->data);
- dp_extract_edid_ext_block_cnt(&dp->edid, rp->data);
- dp_extract_edid_video_support(&dp->edid, rp->data);
- dp_extract_edid_feature(&dp->edid, rp->data);
+ dp_extract_edid_manufacturer(&dp->edid, edid_buf);
+ dp_extract_edid_product(&dp->edid, edid_buf);
+ dp_extract_edid_version(&dp->edid, edid_buf);
+ dp_extract_edid_ext_block_cnt(&dp->edid, edid_buf);
+ dp_extract_edid_video_support(&dp->edid, edid_buf);
+ dp_extract_edid_feature(&dp->edid, edid_buf);
dp_extract_edid_detailed_timing_description(&dp->edid,
- rp->data);
+ edid_buf);
edid_parsing_done = true;
+ } else if (!edid_parsing_done) {
+ pr_debug("Invalid edid block 0 header\n");
+ /* Retry block 0 with adjusted phy aux settings */
+ mdss_dp_phy_aux_update_config(dp, PHY_AUX_CFG1);
+ phy_aux_update_requested = true;
+ retries--;
+ continue;
} else {
edid_blk++;
blk_num++;
-
- /* fix dongle byte shift issue */
- if (edid_blk == 1 && rp->data[0] != cea_tag) {
- u8 tmp[EDID_BLOCK_SIZE - 1];
-
- memcpy(tmp, rp->data, EDID_BLOCK_SIZE - 1);
- rp->data[0] = cea_tag;
- memcpy(rp->data + 1, tmp, EDID_BLOCK_SIZE - 1);
- }
}
memcpy(dp->edid_buf + (edid_blk * EDID_BLOCK_SIZE),
- rp->data, EDID_BLOCK_SIZE);
+ edid_buf, EDID_BLOCK_SIZE);
- checksum = rp->data[rp->len - 1];
+ checksum = edid_buf[rlen - 1];
/* break if no more extension blocks present */
- if (edid_blk == dp->edid.ext_block_cnt)
+ if (edid_blk >= dp->edid.ext_block_cnt) {
+ ext_block_parsing_done = true;
break;
-
- /* write segment number to read block 3 onwards */
- if (edid_blk == start_ext_blk)
- dp_aux_write_buf(dp, segment_addr, &segment, 1, 1);
- } while (retries--);
+ }
+ }
if (dp->test_data.test_requested == TEST_EDID_READ) {
pr_debug("sending checksum %d\n", checksum);
@@ -823,6 +1056,18 @@ int mdss_dp_edid_read(struct mdss_dp_drv_pdata *dp)
dp->test_data = (const struct dpcd_test_request){ 0 };
}
+ /*
+ * Trigger the reading of DPCD if there was a change in the AUX
+ * configuration caused by a failure while reading the EDID.
+ * This is required to ensure the integrity and validity
+ * of the sink capabilities read that will subsequently be used
+ * to establish the mainlink.
+ */
+ if (edid_parsing_done && ext_block_parsing_done
+ && phy_aux_update_requested) {
+ dp->dpcd_read_required = true;
+ }
+
return ret;
}
@@ -834,6 +1079,10 @@ int mdss_dp_dpcd_cap_read(struct mdss_dp_drv_pdata *ep)
struct dpcd_cap *cap;
struct edp_buf *rp;
int rlen;
+ int i;
+
+ cap = &ep->dpcd;
+ memset(cap, 0, sizeof(*cap));
rlen = dp_aux_read_buf(ep, 0, len, 0);
if (rlen <= 0) {
@@ -848,11 +1097,8 @@ int mdss_dp_dpcd_cap_read(struct mdss_dp_drv_pdata *ep)
}
rp = &ep->rxp;
- cap = &ep->dpcd;
bp = rp->data;
- memset(cap, 0, sizeof(*cap));
-
data = *bp++; /* byte 0 */
cap->major = (data >> 4) & 0x0f;
cap->minor = data & 0x0f;
@@ -909,6 +1155,11 @@ int mdss_dp_dpcd_cap_read(struct mdss_dp_drv_pdata *ep)
data = *bp++; /* Byte 7: DOWN_STREAM_PORT_COUNT */
cap->downstream_port.dfp_count = data & 0x7;
+ if (cap->downstream_port.dfp_count > DP_MAX_DS_PORT_COUNT) {
+ pr_debug("DS port count %d greater that max (%d) supported\n",
+ cap->downstream_port.dfp_count, DP_MAX_DS_PORT_COUNT);
+ cap->downstream_port.dfp_count = DP_MAX_DS_PORT_COUNT;
+ }
cap->downstream_port.msa_timing_par_ignored = data & BIT(6);
cap->downstream_port.oui_support = data & BIT(7);
pr_debug("dfp_count = %d, msa_timing_par_ignored = %d\n",
@@ -916,17 +1167,23 @@ int mdss_dp_dpcd_cap_read(struct mdss_dp_drv_pdata *ep)
cap->downstream_port.msa_timing_par_ignored);
pr_debug("oui_support = %d\n", cap->downstream_port.oui_support);
- data = *bp++; /* byte 8 */
- if (data & BIT(1)) {
- cap->flags |= DPCD_PORT_0_EDID_PRESENTED;
- pr_debug("edid presented\n");
- }
-
- data = *bp++; /* byte 9 */
- cap->rx_port0_buf_size = (data + 1) * 32;
- pr_debug("lane_buf_size=%d\n", cap->rx_port0_buf_size);
+ for (i = 0; i < DP_MAX_DS_PORT_COUNT; i++) {
+ data = *bp++; /* byte 8 + i*2 */
+ pr_debug("parsing capabilities for DS port %d\n", i);
+ if (data & BIT(1)) {
+ if (i == 0)
+ cap->flags |= DPCD_PORT_0_EDID_PRESENTED;
+ else
+ cap->flags |= DPCD_PORT_1_EDID_PRESENTED;
+ pr_debug("local edid present\n");
+ } else {
+ pr_debug("local edid absent\n");
+ }
- bp += 2; /* skip 10, 11 port1 capability */
+ data = *bp++; /* byte 9 + i*2 */
+ cap->rx_port_buf_size[i] = (data + 1) * 32;
+ pr_debug("lane_buf_size=%d\n", cap->rx_port_buf_size[i]);
+ }
data = *bp++; /* byte 12 */
cap->i2c_speed_ctrl = data;
@@ -1258,6 +1515,8 @@ static void dp_sink_parse_sink_count(struct mdss_dp_drv_pdata *ep)
int const param_len = 0x1;
int const sink_count_addr = 0x200;
+ ep->prev_sink_count = ep->sink_count;
+
rlen = dp_aux_read_buf(ep, sink_count_addr, param_len, 0);
if (rlen < param_len) {
pr_err("failed to read sink count\n");
@@ -2363,8 +2622,8 @@ clear:
void mdss_dp_aux_parse_sink_status_field(struct mdss_dp_drv_pdata *ep)
{
dp_sink_parse_sink_count(ep);
- dp_sink_parse_test_request(ep);
mdss_dp_aux_link_status_read(ep, 6);
+ dp_sink_parse_test_request(ep);
}
int mdss_dp_dpcd_status_read(struct mdss_dp_drv_pdata *ep)
diff --git a/drivers/video/fbdev/msm/mdss_dp_util.c b/drivers/video/fbdev/msm/mdss_dp_util.c
index ea492f54054c..0d9cf7b72b4d 100644
--- a/drivers/video/fbdev/msm/mdss_dp_util.c
+++ b/drivers/video/fbdev/msm/mdss_dp_util.c
@@ -858,6 +858,48 @@ void mdss_dp_setup_tr_unit(struct dss_io_data *ctrl_io, u8 link_rate,
pr_debug("dp_tu=0x%x\n", dp_tu);
}
+void mdss_dp_aux_set_limits(struct dss_io_data *ctrl_io)
+{
+ u32 const max_aux_timeout_count = 0xFFFFF;
+ u32 const max_aux_limits = 0xFFFFFFFF;
+
+ pr_debug("timeout=0x%x, limits=0x%x\n",
+ max_aux_timeout_count, max_aux_limits);
+
+ writel_relaxed(max_aux_timeout_count,
+ ctrl_io->base + DP_AUX_TIMEOUT_COUNT);
+ writel_relaxed(max_aux_limits, ctrl_io->base + DP_AUX_LIMITS);
+}
+
+void mdss_dp_phy_aux_update_config(struct mdss_dp_drv_pdata *dp,
+ enum dp_phy_aux_config_type config_type)
+{
+ u32 new_index;
+ struct dss_io_data *phy_io = &dp->phy_io;
+ struct mdss_dp_phy_cfg *cfg = mdss_dp_phy_aux_get_config(dp,
+ config_type);
+
+ if (!cfg) {
+ pr_err("invalid config type %s",
+ mdss_dp_phy_aux_config_type_to_string(config_type));
+ return;
+ }
+
+ new_index = (cfg->current_index + 1) % cfg->cfg_cnt;
+
+ pr_debug("Updating %s from 0x%08x to 0x%08x\n",
+ mdss_dp_phy_aux_config_type_to_string(config_type),
+ cfg->lut[cfg->current_index], cfg->lut[new_index]);
+ writel_relaxed(cfg->lut[new_index], phy_io->base + cfg->offset);
+ cfg->current_index = new_index;
+
+ /* Make sure the new HW configuration takes effect */
+ wmb();
+
+ /* Reset the AUX controller before any subsequent transactions */
+ mdss_dp_aux_reset(&dp->ctrl_io);
+}
+
void mdss_dp_ctrl_lane_mapping(struct dss_io_data *ctrl_io, char *l_map)
{
u8 bits_per_lane = 2;
@@ -870,26 +912,24 @@ void mdss_dp_ctrl_lane_mapping(struct dss_io_data *ctrl_io, char *l_map)
ctrl_io->base + DP_LOGICAL2PHYSCIAL_LANE_MAPPING);
}
-void mdss_dp_phy_aux_setup(struct dss_io_data *phy_io, u32 *aux_cfg,
- u32 phy_reg_offset)
+void mdss_dp_phy_aux_setup(struct mdss_dp_drv_pdata *dp)
{
- void __iomem *adjusted_phy_io_base = phy_io->base + phy_reg_offset;
+ int i;
+ void __iomem *adjusted_phy_io_base = dp->phy_io.base +
+ dp->phy_reg_offset;
writel_relaxed(0x3d, adjusted_phy_io_base + DP_PHY_PD_CTL);
- /* DP AUX CFG register programming */
- writel_relaxed(aux_cfg[0], adjusted_phy_io_base + DP_PHY_AUX_CFG0);
- writel_relaxed(aux_cfg[1], adjusted_phy_io_base + DP_PHY_AUX_CFG1);
- writel_relaxed(aux_cfg[2], adjusted_phy_io_base + DP_PHY_AUX_CFG2);
- writel_relaxed(aux_cfg[3], adjusted_phy_io_base + DP_PHY_AUX_CFG3);
- writel_relaxed(aux_cfg[4], adjusted_phy_io_base + DP_PHY_AUX_CFG4);
- writel_relaxed(aux_cfg[5], adjusted_phy_io_base + DP_PHY_AUX_CFG5);
- writel_relaxed(aux_cfg[6], adjusted_phy_io_base + DP_PHY_AUX_CFG6);
- writel_relaxed(aux_cfg[7], adjusted_phy_io_base + DP_PHY_AUX_CFG7);
- writel_relaxed(aux_cfg[8], adjusted_phy_io_base + DP_PHY_AUX_CFG8);
- writel_relaxed(aux_cfg[9], adjusted_phy_io_base + DP_PHY_AUX_CFG9);
-
- writel_relaxed(0x1f, adjusted_phy_io_base + DP_PHY_AUX_INTERRUPT_MASK);
+ for (i = 0; i < PHY_AUX_CFG_MAX; i++) {
+ struct mdss_dp_phy_cfg *cfg = mdss_dp_phy_aux_get_config(dp, i);
+
+ pr_debug("%s: offset=0x%08x, value=0x%08x\n",
+ mdss_dp_phy_aux_config_type_to_string(i), cfg->offset,
+ cfg->lut[cfg->current_index]);
+ writel_relaxed(cfg->lut[cfg->current_index],
+ dp->phy_io.base + cfg->offset);
+ };
+ writel_relaxed(0x1e, adjusted_phy_io_base + DP_PHY_AUX_INTERRUPT_MASK);
}
int mdss_dp_irq_setup(struct mdss_dp_drv_pdata *dp_drv)
diff --git a/drivers/video/fbdev/msm/mdss_dp_util.h b/drivers/video/fbdev/msm/mdss_dp_util.h
index 8f19e7cdf3cf..4c93e48e97dc 100644
--- a/drivers/video/fbdev/msm/mdss_dp_util.h
+++ b/drivers/video/fbdev/msm/mdss_dp_util.h
@@ -35,6 +35,8 @@
#define DP_AUX_CTRL (0x00000230)
#define DP_AUX_DATA (0x00000234)
#define DP_AUX_TRANS_CTRL (0x00000238)
+#define DP_AUX_TIMEOUT_COUNT (0x0000023C)
+#define DP_AUX_LIMITS (0x00000240)
#define DP_AUX_STATUS (0x00000244)
#define DP_DPCD_CP_IRQ (0x201)
@@ -163,6 +165,7 @@
#define DP_PHY_AUX_CFG9 (0x00000040)
#define DP_PHY_AUX_INTERRUPT_MASK (0x00000044)
#define DP_PHY_AUX_INTERRUPT_CLEAR (0x00000048)
+#define DP_PHY_AUX_INTERRUPT_STATUS (0x000000B8)
#define DP_PHY_SPARE0 0x00A8
@@ -271,6 +274,19 @@ static const struct dp_vc_tu_mapping_table tu_table[] = {
0x21, 0x000c, false, 0x00, 0x00, 0x00, 0x27},
};
+static inline struct mdss_dp_phy_cfg *mdss_dp_phy_aux_get_config(
+ struct mdss_dp_drv_pdata *dp, enum dp_phy_aux_config_type cfg_type)
+{
+ return &dp->aux_cfg[cfg_type];
+}
+
+static inline u32 mdss_dp_phy_aux_get_config_cnt(
+ struct mdss_dp_drv_pdata *dp, enum dp_phy_aux_config_type cfg_type)
+{
+ return dp->aux_cfg[cfg_type].cfg_cnt;
+}
+
+void mdss_dp_aux_set_limits(struct dss_io_data *ctrl_io);
int dp_aux_read(void *ep, struct edp_cmd *cmds);
int dp_aux_write(void *ep, struct edp_cmd *cmd);
void mdss_dp_state_ctrl(struct dss_io_data *ctrl_io, u32 data);
@@ -285,8 +301,9 @@ void mdss_dp_assert_phy_reset(struct dss_io_data *ctrl_io, bool assert);
void mdss_dp_setup_tr_unit(struct dss_io_data *ctrl_io, u8 link_rate,
u8 ln_cnt, u32 res, struct mdss_panel_info *pinfo);
void mdss_dp_config_misc(struct mdss_dp_drv_pdata *dp, u32 bd, u32 cc);
-void mdss_dp_phy_aux_setup(struct dss_io_data *phy_io, u32 *aux_cfg,
- u32 phy_reg_offset);
+void mdss_dp_phy_aux_setup(struct mdss_dp_drv_pdata *dp);
+void mdss_dp_phy_aux_update_config(struct mdss_dp_drv_pdata *dp,
+ enum dp_phy_aux_config_type config_type);
void mdss_dp_hpd_configure(struct dss_io_data *ctrl_io, bool enable);
void mdss_dp_aux_ctrl(struct dss_io_data *ctrl_io, bool enable);
void mdss_dp_mainlink_ctrl(struct dss_io_data *ctrl_io, bool enable);
diff --git a/drivers/video/fbdev/msm/mdss_dsi.c b/drivers/video/fbdev/msm/mdss_dsi.c
index 17722eac3006..889fbe5c4aff 100644
--- a/drivers/video/fbdev/msm/mdss_dsi.c
+++ b/drivers/video/fbdev/msm/mdss_dsi.c
@@ -26,6 +26,7 @@
#include <linux/uaccess.h>
#include <linux/msm-bus.h>
#include <linux/pm_qos.h>
+#include <linux/dma-buf.h>
#include "mdss.h"
#include "mdss_panel.h"
@@ -44,6 +45,97 @@ static struct mdss_dsi_data *mdss_dsi_res;
static struct pm_qos_request mdss_dsi_pm_qos_request;
+void mdss_dump_dsi_debug_bus(u32 bus_dump_flag,
+ u32 **dump_mem)
+{
+ struct mdss_dsi_data *sdata = mdss_dsi_res;
+ struct mdss_dsi_ctrl_pdata *m_ctrl, *s_ctrl;
+ bool in_log, in_mem;
+ u32 *dump_addr = NULL;
+ u32 status0 = 0, status1 = 0;
+ phys_addr_t phys = 0;
+ int list_size = 0;
+ int i;
+ bool dsi0_active = false, dsi1_active = false;
+
+ if (!sdata || !sdata->dbg_bus || !sdata->dbg_bus_size)
+ return;
+
+ m_ctrl = sdata->ctrl_pdata[0];
+ s_ctrl = sdata->ctrl_pdata[1];
+
+ if (!m_ctrl)
+ return;
+
+ if (m_ctrl && m_ctrl->shared_data->dsi0_active)
+ dsi0_active = true;
+ if (s_ctrl && s_ctrl->shared_data->dsi1_active)
+ dsi1_active = true;
+
+ list_size = (sdata->dbg_bus_size * sizeof(sdata->dbg_bus[0]) * 4);
+
+ in_log = (bus_dump_flag & MDSS_DBG_DUMP_IN_LOG);
+ in_mem = (bus_dump_flag & MDSS_DBG_DUMP_IN_MEM);
+
+ if (in_mem) {
+ if (!(*dump_mem))
+ *dump_mem = dma_alloc_coherent(&sdata->pdev->dev,
+ list_size, &phys, GFP_KERNEL);
+
+ if (*dump_mem) {
+ dump_addr = *dump_mem;
+ pr_info("%s: start_addr:0x%pK end_addr:0x%pK\n",
+ __func__, dump_addr, dump_addr + list_size);
+ } else {
+ in_mem = false;
+ pr_err("dump_mem: allocation fails\n");
+ }
+ }
+
+ pr_info("========= Start DSI Debug Bus =========\n");
+
+ mdss_dsi_clk_ctrl(m_ctrl, m_ctrl->dsi_clk_handle,
+ MDSS_DSI_CORE_CLK, MDSS_DSI_CLK_ON);
+
+ for (i = 0; i < sdata->dbg_bus_size; i++) {
+ if (dsi0_active) {
+ writel_relaxed(sdata->dbg_bus[i],
+ m_ctrl->ctrl_base + 0x124);
+ wmb(); /* ensure regsiter is committed */
+ }
+ if (dsi1_active) {
+ writel_relaxed(sdata->dbg_bus[i],
+ s_ctrl->ctrl_base + 0x124);
+ wmb(); /* ensure register is committed */
+ }
+
+ if (dsi0_active) {
+ status0 = readl_relaxed(m_ctrl->ctrl_base + 0x128);
+ if (in_log)
+ pr_err("CTRL:0 bus_ctrl: 0x%x status: 0x%x\n",
+ sdata->dbg_bus[i], status0);
+ }
+ if (dsi1_active) {
+ status1 = readl_relaxed(s_ctrl->ctrl_base + 0x128);
+ if (in_log)
+ pr_err("CTRL:1 bus_ctrl: 0x%x status: 0x%x\n",
+ sdata->dbg_bus[i], status1);
+ }
+
+ if (dump_addr && in_mem) {
+ dump_addr[i*4] = sdata->dbg_bus[i];
+ dump_addr[i*4 + 1] = status0;
+ dump_addr[i*4 + 2] = status1;
+ dump_addr[i*4 + 3] = 0x0;
+ }
+ }
+
+ mdss_dsi_clk_ctrl(m_ctrl, m_ctrl->dsi_clk_handle,
+ MDSS_DSI_CORE_CLK, MDSS_DSI_CLK_OFF);
+
+ pr_info("========End DSI Debug Bus=========\n");
+}
+
static void mdss_dsi_pm_qos_add_request(struct mdss_dsi_ctrl_pdata *ctrl_pdata)
{
struct irq_info *irq_info;
@@ -2713,10 +2805,7 @@ static int mdss_dsi_event_handler(struct mdss_panel_data *pdata,
rc = mdss_dsi_reconfig(pdata, mode);
break;
case MDSS_EVENT_DSI_PANEL_STATUS:
- if (ctrl_pdata->check_status)
- rc = ctrl_pdata->check_status(ctrl_pdata);
- else
- rc = true;
+ rc = mdss_dsi_check_panel_status(ctrl_pdata, arg);
break;
case MDSS_EVENT_PANEL_TIMING_SWITCH:
rc = mdss_dsi_panel_timing_switch(ctrl_pdata, arg);
@@ -3359,6 +3448,8 @@ static int mdss_dsi_ctrl_probe(struct platform_device *pdev)
else
ctrl_pdata->shared_data->dsi1_active = true;
+ mdss_dsi_debug_bus_init(mdss_dsi_res);
+
return 0;
error_shadow_clk_deinit:
diff --git a/drivers/video/fbdev/msm/mdss_dsi.h b/drivers/video/fbdev/msm/mdss_dsi.h
index 2a76466abf3e..335037860ffe 100644
--- a/drivers/video/fbdev/msm/mdss_dsi.h
+++ b/drivers/video/fbdev/msm/mdss_dsi.h
@@ -315,6 +315,8 @@ struct mdss_dsi_data {
* mutex, clocks, regulator information, setup information
*/
struct dsi_shared_data *shared_data;
+ u32 *dbg_bus;
+ int dbg_bus_size;
};
/*
@@ -704,6 +706,9 @@ void mdss_dsi_cfg_lane_ctrl(struct mdss_dsi_ctrl_pdata *ctrl,
void mdss_dsi_set_reg(struct mdss_dsi_ctrl_pdata *ctrl, int off,
u32 mask, u32 val);
int mdss_dsi_phy_pll_reset_status(struct mdss_dsi_ctrl_pdata *ctrl);
+int mdss_dsi_check_panel_status(struct mdss_dsi_ctrl_pdata *ctrl, void *arg);
+
+void mdss_dsi_debug_bus_init(struct mdss_dsi_data *sdata);
static inline const char *__mdss_dsi_pm_name(enum dsi_pm_type module)
{
diff --git a/drivers/video/fbdev/msm/mdss_dsi_host.c b/drivers/video/fbdev/msm/mdss_dsi_host.c
index 37f3929a3a2c..1a471155072b 100644
--- a/drivers/video/fbdev/msm/mdss_dsi_host.c
+++ b/drivers/video/fbdev/msm/mdss_dsi_host.c
@@ -1562,11 +1562,13 @@ wait:
MDSS_XLOG_TOUT_HANDLER("mdp", "dsi0_ctrl",
"dsi0_phy", "dsi1_ctrl", "dsi1_phy",
"vbif", "vbif_nrt", "dbg_bus",
- "vbif_dbg_bus", "panic");
+ "vbif_dbg_bus", "dsi_dbg_bus", "panic");
/* mask out overflow errors */
if (ignore_underflow)
mdss_dsi_set_reg(ctrl_pdata, 0x10c, 0x0f0000, 0x0f0000);
+
+ MDSS_XLOG(ctrl_pdata->ndx, ctrl_pdata->mdp_busy);
MIPI_OUTP(ctrl_pdata->ctrl_base + 0x098, 0x01); /* trigger */
wmb();
@@ -2185,6 +2187,7 @@ static int mdss_dsi_cmd_dma_tx(struct mdss_dsi_ctrl_pdata *ctrl,
MIPI_OUTP((ctrl->ctrl_base) + 0x090, 0x01);
wmb();
+ MDSS_XLOG(ctrl->dma_addr, len);
if (ctrl->do_unicast) {
/* let cmd_trigger to kickoff later */
@@ -2561,7 +2564,7 @@ void mdss_dsi_cmd_mdp_busy(struct mdss_dsi_ctrl_pdata *ctrl)
MDSS_XLOG_TOUT_HANDLER("mdp", "dsi0_ctrl",
"dsi0_phy", "dsi1_ctrl", "dsi1_phy",
"vbif", "vbif_nrt", "dbg_bus",
- "vbif_dbg_bus", "panic");
+ "vbif_dbg_bus", "dsi_dbg_bus", "panic");
}
}
}
@@ -3031,7 +3034,10 @@ bool mdss_dsi_ack_err_status(struct mdss_dsi_ctrl_pdata *ctrl)
* warning message is ignored.
*/
if (ctrl->panel_data.panel_info.esd_check_enabled &&
- (ctrl->status_mode == ESD_BTA) && (status & 0x1008000))
+ ((ctrl->status_mode == ESD_BTA) ||
+ (ctrl->status_mode == ESD_REG) ||
+ (ctrl->status_mode == ESD_REG_NT35596)) &&
+ (status & 0x1008000))
return false;
pr_err("%s: status=%x\n", __func__, status);
@@ -3184,7 +3190,7 @@ static void __dsi_error_counter(struct dsi_err_container *err_container)
pr_err("%s: panic in WQ as dsi error intrs within:%dms\n",
__func__, err_container->err_time_delta);
MDSS_XLOG_TOUT_HANDLER_WQ("mdp", "dsi0_ctrl", "dsi0_phy",
- "dsi1_ctrl", "dsi1_phy", "panic");
+ "dsi1_ctrl", "dsi1_phy", "dsi_dbg_bus", "panic");
}
}
@@ -3262,8 +3268,10 @@ irqreturn_t mdss_dsi_isr(int irq, void *ptr)
* cleared.
*/
if (ctrl->panel_data.panel_info.esd_check_enabled &&
- (ctrl->status_mode == ESD_BTA) &&
- (ctrl->panel_mode == DSI_VIDEO_MODE)) {
+ ((ctrl->status_mode == ESD_BTA) ||
+ (ctrl->status_mode == ESD_REG) ||
+ (ctrl->status_mode == ESD_REG_NT35596)) &&
+ (ctrl->panel_mode == DSI_VIDEO_MODE)) {
isr &= ~DSI_INTR_ERROR;
/* clear only overflow */
mdss_dsi_set_reg(ctrl, 0x0c, 0x44440000, 0x44440000);
@@ -3272,6 +3280,7 @@ irqreturn_t mdss_dsi_isr(int irq, void *ptr)
}
if (isr & DSI_INTR_VIDEO_DONE) {
+ MDSS_XLOG(ctrl->ndx, isr, 0x111);
spin_lock(&ctrl->mdp_lock);
mdss_dsi_disable_irq_nosync(ctrl, DSI_VIDEO_TERM);
complete(&ctrl->video_comp);
diff --git a/drivers/video/fbdev/msm/mdss_dsi_status.c b/drivers/video/fbdev/msm/mdss_dsi_status.c
index 4208c2c43efb..0f24f66dbcc6 100644
--- a/drivers/video/fbdev/msm/mdss_dsi_status.c
+++ b/drivers/video/fbdev/msm/mdss_dsi_status.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2013-2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2013-2017, 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
@@ -39,6 +39,35 @@ static uint32_t interval = STATUS_CHECK_INTERVAL_MS;
static int32_t dsi_status_disable = DSI_STATUS_CHECK_INIT;
struct dsi_status_data *pstatus_data;
+int mdss_dsi_check_panel_status(struct mdss_dsi_ctrl_pdata *ctrl, void *arg)
+{
+ struct mdss_mdp_ctl *ctl = NULL;
+ struct msm_fb_data_type *mfd = arg;
+ int ret = 0;
+
+ if (!mfd)
+ return -EINVAL;
+
+ ctl = mfd_to_ctl(mfd);
+
+ if (!ctl || !ctrl)
+ return -EINVAL;
+
+ mutex_lock(&ctl->offlock);
+ /*
+ * if check_status method is not defined
+ * then no need to fail this function,
+ * instead return a positive value.
+ */
+ if (ctrl->check_status)
+ ret = ctrl->check_status(ctrl);
+ else
+ ret = 1;
+ mutex_unlock(&ctl->offlock);
+
+ return ret;
+}
+
/*
* check_dsi_ctrl_status() - Reads MFD structure and
* calls platform specific DSI ctrl Status function.
diff --git a/drivers/video/fbdev/msm/mdss_fb.c b/drivers/video/fbdev/msm/mdss_fb.c
index 93643246935e..698c5633cf6a 100644
--- a/drivers/video/fbdev/msm/mdss_fb.c
+++ b/drivers/video/fbdev/msm/mdss_fb.c
@@ -653,7 +653,7 @@ static ssize_t mdss_fb_get_panel_status(struct device *dev,
ret = scnprintf(buf, PAGE_SIZE, "panel_status=%s\n", "suspend");
} else {
panel_status = mdss_fb_send_panel_event(mfd,
- MDSS_EVENT_DSI_PANEL_STATUS, NULL);
+ MDSS_EVENT_DSI_PANEL_STATUS, mfd);
ret = scnprintf(buf, PAGE_SIZE, "panel_status=%s\n",
panel_status > 0 ? "alive" : "dead");
}
@@ -1596,13 +1596,30 @@ static int mdss_fb_resume(struct platform_device *pdev)
static int mdss_fb_pm_suspend(struct device *dev)
{
struct msm_fb_data_type *mfd = dev_get_drvdata(dev);
+ int rc = 0;
if (!mfd)
return -ENODEV;
dev_dbg(dev, "display pm suspend\n");
- return mdss_fb_suspend_sub(mfd);
+ rc = mdss_fb_suspend_sub(mfd);
+
+ /*
+ * Call MDSS footswitch control to ensure GDSC is
+ * off after pm suspend call. There are cases when
+ * mdss runtime call doesn't trigger even when clock
+ * ref count is zero after fb pm suspend.
+ */
+ if (!rc) {
+ if (mfd->mdp.footswitch_ctrl)
+ mfd->mdp.footswitch_ctrl(false);
+ } else {
+ pr_err("fb pm suspend failed, rc: %d\n", rc);
+ }
+
+ return rc;
+
}
static int mdss_fb_pm_resume(struct device *dev)
@@ -1622,6 +1639,9 @@ static int mdss_fb_pm_resume(struct device *dev)
pm_runtime_set_suspended(dev);
pm_runtime_enable(dev);
+ if (mfd->mdp.footswitch_ctrl)
+ mfd->mdp.footswitch_ctrl(true);
+
return mdss_fb_resume_sub(mfd);
}
#endif
diff --git a/drivers/video/fbdev/msm/mdss_fb.h b/drivers/video/fbdev/msm/mdss_fb.h
index 8e5fc5949770..518c24810acd 100644
--- a/drivers/video/fbdev/msm/mdss_fb.h
+++ b/drivers/video/fbdev/msm/mdss_fb.h
@@ -232,6 +232,7 @@ struct msm_mdp_interface {
int (*configure_panel)(struct msm_fb_data_type *mfd, int mode,
int dest_ctrl);
int (*input_event_handler)(struct msm_fb_data_type *mfd);
+ void (*footswitch_ctrl)(bool on);
int (*pp_release_fnc)(struct msm_fb_data_type *mfd);
void *private1;
};
diff --git a/drivers/video/fbdev/msm/mdss_hdcp_1x.c b/drivers/video/fbdev/msm/mdss_hdcp_1x.c
index 834726e84bda..2dc9c8f96c5b 100644
--- a/drivers/video/fbdev/msm/mdss_hdcp_1x.c
+++ b/drivers/video/fbdev/msm/mdss_hdcp_1x.c
@@ -1381,7 +1381,8 @@ int hdcp_1x_authenticate(void *input)
flush_delayed_work(&hdcp->hdcp_auth_work);
- if (!hdcp_1x_state(HDCP_STATE_INACTIVE)) {
+ if (!hdcp_1x_state(HDCP_STATE_INACTIVE) &&
+ !hdcp_1x_state(HDCP_STATE_AUTH_FAIL)) {
pr_err("invalid state\n");
return -EINVAL;
}
@@ -1443,7 +1444,6 @@ int hdcp_1x_reauthenticate(void *input)
DSS_REG_W(io, reg_set->reset, reg & ~reg_set->reset_bit);
- hdcp->hdcp_state = HDCP_STATE_INACTIVE;
hdcp_1x_authenticate(hdcp);
return ret;
diff --git a/drivers/video/fbdev/msm/mdss_hdmi_edid.c b/drivers/video/fbdev/msm/mdss_hdmi_edid.c
index 37c4be6135aa..599f6cb44c63 100644
--- a/drivers/video/fbdev/msm/mdss_hdmi_edid.c
+++ b/drivers/video/fbdev/msm/mdss_hdmi_edid.c
@@ -2604,6 +2604,11 @@ void hdmi_edid_set_video_resolution(void *input, u32 resolution, bool reset)
return;
}
+ if (resolution == HDMI_VFRMT_UNKNOWN) {
+ pr_debug("%s: Default video resolution not set\n", __func__);
+ return;
+ }
+
edid_ctrl->video_resolution = resolution;
if (reset) {
diff --git a/drivers/video/fbdev/msm/mdss_mdp.c b/drivers/video/fbdev/msm/mdss_mdp.c
index 171f44815430..a645a3495593 100644
--- a/drivers/video/fbdev/msm/mdss_mdp.c
+++ b/drivers/video/fbdev/msm/mdss_mdp.c
@@ -233,7 +233,6 @@ static struct mdss_mdp_irq mdp_irq_map[] = {
static struct intr_callback *mdp_intr_cb;
-static void mdss_mdp_footswitch_ctrl(struct mdss_data_type *mdata, int on);
static int mdss_mdp_parse_dt(struct platform_device *pdev);
static int mdss_mdp_parse_dt_pipe(struct platform_device *pdev);
static int mdss_mdp_parse_dt_mixer(struct platform_device *pdev);
@@ -5172,7 +5171,7 @@ static void mdss_mdp_notify_idle_pc(struct mdss_data_type *mdata)
* active (but likely in an idle state), the vote for the CX and the batfet
* rails should not be released.
*/
-static void mdss_mdp_footswitch_ctrl(struct mdss_data_type *mdata, int on)
+void mdss_mdp_footswitch_ctrl(struct mdss_data_type *mdata, int on)
{
int ret;
int active_cnt = 0;
diff --git a/drivers/video/fbdev/msm/mdss_mdp.h b/drivers/video/fbdev/msm/mdss_mdp.h
index 56af021e8cfc..a2139f495f52 100644
--- a/drivers/video/fbdev/msm/mdss_mdp.h
+++ b/drivers/video/fbdev/msm/mdss_mdp.h
@@ -142,6 +142,25 @@
#define BITS_TO_BYTES(x) DIV_ROUND_UP(x, BITS_PER_BYTE)
+#define PP_PROGRAM_PA 0x1
+#define PP_PROGRAM_PCC 0x2
+#define PP_PROGRAM_IGC 0x4
+#define PP_PROGRAM_ARGC 0x8
+#define PP_PROGRAM_HIST 0x10
+#define PP_PROGRAM_DITHER 0x20
+#define PP_PROGRAM_GAMUT 0x40
+#define PP_PROGRAM_PGC 0x100
+#define PP_PROGRAM_PA_DITHER 0x400
+#define PP_PROGRAM_AD 0x800
+
+#define PP_NORMAL_PROGRAM_MASK (PP_PROGRAM_AD | PP_PROGRAM_PCC | \
+ PP_PROGRAM_HIST)
+#define PP_DEFER_PROGRAM_MASK (PP_PROGRAM_IGC | PP_PROGRAM_PGC | \
+ PP_PROGRAM_ARGC | PP_PROGRAM_GAMUT | \
+ PP_PROGRAM_PA | PP_PROGRAM_DITHER | \
+ PP_PROGRAM_PA_DITHER)
+#define PP_PROGRAM_ALL (PP_NORMAL_PROGRAM_MASK | PP_DEFER_PROGRAM_MASK)
+
enum mdss_mdp_perf_state_type {
PERF_SW_COMMIT_STATE = 0,
PERF_HW_MDP_STATE,
@@ -773,6 +792,12 @@ struct mdss_pipe_pp_res {
void *hist_lut_cfg_payload;
};
+struct mdss_mdp_pp_program_info {
+ u32 pp_program_mask;
+ u32 pp_opmode_left;
+ u32 pp_opmode_right;
+};
+
struct mdss_mdp_pipe_smp_map {
DECLARE_BITMAP(reserved, MAX_DRV_SUP_MMB_BLKS);
DECLARE_BITMAP(allocated, MAX_DRV_SUP_MMB_BLKS);
@@ -1640,6 +1665,7 @@ int mdss_mdp_set_intr_callback_nosync(u32 intr_type, u32 intf_num,
void (*fnc_ptr)(void *), void *arg);
u32 mdss_mdp_get_irq_mask(u32 intr_type, u32 intf_num);
+void mdss_mdp_footswitch_ctrl(struct mdss_data_type *mdata, int on);
void mdss_mdp_footswitch_ctrl_splash(int on);
void mdss_mdp_batfet_ctrl(struct mdss_data_type *mdata, int enable);
void mdss_mdp_set_clk_rate(unsigned long min_clk_rate, bool locked);
@@ -1802,7 +1828,8 @@ int mdss_mdp_pp_resume(struct msm_fb_data_type *mfd);
void mdss_mdp_pp_dest_scaler_resume(struct mdss_mdp_ctl *ctl);
int mdss_mdp_pp_setup(struct mdss_mdp_ctl *ctl);
-int mdss_mdp_pp_setup_locked(struct mdss_mdp_ctl *ctl);
+int mdss_mdp_pp_setup_locked(struct mdss_mdp_ctl *ctl,
+ struct mdss_mdp_pp_program_info *info);
int mdss_mdp_pipe_pp_setup(struct mdss_mdp_pipe *pipe, u32 *op);
void mdss_mdp_pipe_pp_clear(struct mdss_mdp_pipe *pipe);
int mdss_mdp_pipe_sspp_setup(struct mdss_mdp_pipe *pipe, u32 *op);
diff --git a/drivers/video/fbdev/msm/mdss_mdp_cdm.c b/drivers/video/fbdev/msm/mdss_mdp_cdm.c
index f1d1bdd301e3..10928e6bceaa 100644
--- a/drivers/video/fbdev/msm/mdss_mdp_cdm.c
+++ b/drivers/video/fbdev/msm/mdss_mdp_cdm.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2014-2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2014-2017, 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
@@ -377,6 +377,17 @@ int mdss_mdp_cdm_destroy(struct mdss_mdp_cdm *cdm)
return -EINVAL;
}
+ mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_ON);
+ mutex_lock(&cdm->lock);
+ /* Disable HDMI packer */
+ writel_relaxed(0x0, cdm->base + MDSS_MDP_REG_CDM_HDMI_PACK_OP_MODE);
+
+ /* Put CDM in bypass */
+ writel_relaxed(0x0, cdm->mdata->mdp_base + MDSS_MDP_MDP_OUT_CTL_0);
+
+ mutex_unlock(&cdm->lock);
+ mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_OFF);
+
kref_put(&cdm->kref, mdss_mdp_cdm_free);
return rc;
diff --git a/drivers/video/fbdev/msm/mdss_mdp_ctl.c b/drivers/video/fbdev/msm/mdss_mdp_ctl.c
index a66ecb7a57b7..2968d883c8cb 100644
--- a/drivers/video/fbdev/msm/mdss_mdp_ctl.c
+++ b/drivers/video/fbdev/msm/mdss_mdp_ctl.c
@@ -4585,7 +4585,7 @@ void mdss_mdp_check_ctl_reset_status(struct mdss_mdp_ctl *ctl)
pr_err("hw recovery is not complete for ctl:%d status:0x%x\n",
ctl->num, status);
MDSS_XLOG_TOUT_HANDLER("mdp", "vbif", "vbif_nrt", "dbg_bus",
- "vbif_dbg_bus", "panic");
+ "vbif_dbg_bus", "dsi_dbg_bus", "panic");
}
}
@@ -5752,6 +5752,9 @@ int mdss_mdp_display_commit(struct mdss_mdp_ctl *ctl, void *arg,
bool is_bw_released, split_lm_valid;
struct mdss_data_type *mdata = mdss_mdp_get_mdata();
u32 ctl_flush_bits = 0, sctl_flush_bits = 0;
+ /* Must initialize pp_program_info */
+ struct mdss_mdp_pp_program_info pp_program_info = {
+ PP_PROGRAM_ALL, 0, 0};
if (!ctl) {
pr_err("display function not set\n");
@@ -5864,9 +5867,13 @@ int mdss_mdp_display_commit(struct mdss_mdp_ctl *ctl, void *arg,
mdss_mdp_ctl_split_display_enable(split_lm_valid, ctl, sctl);
ATRACE_BEGIN("postproc_programming");
- if (ctl->is_video_mode && ctl->mfd && ctl->mfd->dcm_state != DTM_ENTER)
+ if (ctl->mfd && ctl->mfd->dcm_state != DTM_ENTER) {
/* postprocessing setup, including dspp */
- mdss_mdp_pp_setup_locked(ctl);
+ if (!ctl->is_video_mode)
+ pp_program_info.pp_program_mask =
+ PP_NORMAL_PROGRAM_MASK;
+ mdss_mdp_pp_setup_locked(ctl, &pp_program_info);
+ }
if (sctl) {
if (ctl->split_flush_en) {
@@ -5922,11 +5929,17 @@ int mdss_mdp_display_commit(struct mdss_mdp_ctl *ctl, void *arg,
}
/* Moved pp programming to post ping pong */
+ ATRACE_BEGIN("postproc_programming_deferred");
if (!ctl->is_video_mode && ctl->mfd &&
ctl->mfd->dcm_state != DTM_ENTER) {
/* postprocessing setup, including dspp */
mutex_lock(&ctl->flush_lock);
- mdss_mdp_pp_setup_locked(ctl);
+ pp_program_info.pp_program_mask = PP_DEFER_PROGRAM_MASK;
+ /*
+ * pp_program_info should not be modified beween normal and
+ * deferred stage calls.
+ */
+ mdss_mdp_pp_setup_locked(ctl, &pp_program_info);
if (sctl) {
if (ctl->split_flush_en) {
ctl->flush_bits |= sctl->flush_bits;
@@ -5939,6 +5952,7 @@ int mdss_mdp_display_commit(struct mdss_mdp_ctl *ctl, void *arg,
ctl_flush_bits |= ctl->flush_bits;
mutex_unlock(&ctl->flush_lock);
}
+ ATRACE_END("postproc_programming_deferred");
/*
* if serialize_wait4pp is false then roi_bkup used in wait4pingpong
* will be of previous frame as expected.
diff --git a/drivers/video/fbdev/msm/mdss_mdp_intf_cmd.c b/drivers/video/fbdev/msm/mdss_mdp_intf_cmd.c
index c9ce56fb96a4..55b41b3b38d1 100644
--- a/drivers/video/fbdev/msm/mdss_mdp_intf_cmd.c
+++ b/drivers/video/fbdev/msm/mdss_mdp_intf_cmd.c
@@ -2148,12 +2148,14 @@ static int mdss_mdp_cmd_wait4pingpong(struct mdss_mdp_ctl *ctl, void *arg)
MDSS_XLOG(0xbad);
MDSS_XLOG_TOUT_HANDLER("mdp", "dsi0_ctrl", "dsi0_phy",
"dsi1_ctrl", "dsi1_phy", "vbif", "vbif_nrt",
- "dbg_bus", "vbif_dbg_bus", "panic");
+ "dbg_bus", "vbif_dbg_bus",
+ "dsi_dbg_bus", "panic");
} else if (ctx->pp_timeout_report_cnt == MAX_RECOVERY_TRIALS) {
MDSS_XLOG(0xbad2);
MDSS_XLOG_TOUT_HANDLER("mdp", "dsi0_ctrl", "dsi0_phy",
"dsi1_ctrl", "dsi1_phy", "vbif", "vbif_nrt",
- "dbg_bus", "vbif_dbg_bus", "panic");
+ "dbg_bus", "vbif_dbg_bus",
+ "dsi_dbg_bus", "panic");
mdss_fb_report_panel_dead(ctl->mfd);
}
ctx->pp_timeout_report_cnt++;
diff --git a/drivers/video/fbdev/msm/mdss_mdp_intf_video.c b/drivers/video/fbdev/msm/mdss_mdp_intf_video.c
index ea55203afc51..13c70822e266 100644
--- a/drivers/video/fbdev/msm/mdss_mdp_intf_video.c
+++ b/drivers/video/fbdev/msm/mdss_mdp_intf_video.c
@@ -1071,6 +1071,13 @@ static int mdss_mdp_video_stop(struct mdss_mdp_ctl *ctl, int panel_power_state)
{
int intfs_num, ret = 0;
+ if (ctl->cdm) {
+ if (!mdss_mdp_cdm_destroy(ctl->cdm))
+ mdss_mdp_ctl_write(ctl,
+ MDSS_MDP_REG_CTL_FLUSH, BIT(26));
+ ctl->cdm = NULL;
+ }
+
intfs_num = ctl->intf_num - MDSS_MDP_INTF0;
ret = mdss_mdp_video_intfs_stop(ctl, ctl->panel_data, intfs_num);
if (IS_ERR_VALUE(ret)) {
@@ -1083,10 +1090,6 @@ static int mdss_mdp_video_stop(struct mdss_mdp_ctl *ctl, int panel_power_state)
mdss_mdp_ctl_reset(ctl, false);
ctl->intf_ctx[MASTER_CTX] = NULL;
- if (ctl->cdm) {
- mdss_mdp_cdm_destroy(ctl->cdm);
- ctl->cdm = NULL;
- }
return 0;
}
@@ -1384,7 +1387,7 @@ static int mdss_mdp_video_dfps_wait4vsync(struct mdss_mdp_ctl *ctl)
pr_err("error polling for vsync\n");
MDSS_XLOG_TOUT_HANDLER("mdp", "dsi0_ctrl", "dsi0_phy",
"dsi1_ctrl", "dsi1_phy", "vbif", "dbg_bus",
- "vbif_dbg_bus", "panic");
+ "vbif_dbg_bus", "dsi_dbg_bus", "panic");
}
} else {
rc = 0;
@@ -1806,7 +1809,9 @@ int mdss_mdp_video_reconfigure_splash_done(struct mdss_mdp_ctl *ctl,
mdss_mdp_video_timegen_flush(ctl, sctx);
/* wait for 1 VSYNC for the pipe to be unstaged */
- msleep(20);
+ mdss_mdp_video_wait4comp(ctl, NULL);
+ if (sctl)
+ mdss_mdp_video_wait4comp(sctl, NULL);
ret = mdss_mdp_ctl_intf_event(ctl,
MDSS_EVENT_CONT_SPLASH_FINISH, NULL,
diff --git a/drivers/video/fbdev/msm/mdss_mdp_intf_writeback.c b/drivers/video/fbdev/msm/mdss_mdp_intf_writeback.c
index 87ed56028edd..9b63499e64b0 100644
--- a/drivers/video/fbdev/msm/mdss_mdp_intf_writeback.c
+++ b/drivers/video/fbdev/msm/mdss_mdp_intf_writeback.c
@@ -792,7 +792,9 @@ static int mdss_mdp_writeback_stop(struct mdss_mdp_ctl *ctl,
}
if (ctl->cdm) {
- mdss_mdp_cdm_destroy(ctl->cdm);
+ if (!mdss_mdp_cdm_destroy(ctl->cdm))
+ mdss_mdp_ctl_write(ctl,
+ MDSS_MDP_REG_CTL_FLUSH, BIT(26));
ctl->cdm = NULL;
}
return 0;
diff --git a/drivers/video/fbdev/msm/mdss_mdp_overlay.c b/drivers/video/fbdev/msm/mdss_mdp_overlay.c
index 8eb12d764be3..8c612e2b83fb 100644
--- a/drivers/video/fbdev/msm/mdss_mdp_overlay.c
+++ b/drivers/video/fbdev/msm/mdss_mdp_overlay.c
@@ -2601,6 +2601,18 @@ int mdss_mdp_overlay_kickoff(struct msm_fb_data_type *mfd,
mdss_mdp_splash_cleanup(mfd, true);
/*
+ * Wait for pingpong done only during resume for
+ * command mode panels. Ensure that one commit is
+ * sent before kickoff completes so that backlight
+ * update happens after it.
+ */
+ if (mdss_fb_is_power_off(mfd) &&
+ mfd->panel_info->type == MIPI_CMD_PANEL) {
+ pr_debug("wait for pp done after resume for cmd mode\n");
+ mdss_mdp_display_wait4pingpong(ctl, true);
+ }
+
+ /*
* Configure Timing Engine, if new fps was set.
* We need to do this after the wait for vsync
* to guarantee that mdp flush bit and dsi flush
@@ -6279,6 +6291,13 @@ int mdss_mdp_input_event_handler(struct msm_fb_data_type *mfd)
return rc;
}
+void mdss_mdp_footswitch_ctrl_handler(bool on)
+{
+ struct mdss_data_type *mdata = mdss_mdp_get_mdata();
+
+ mdss_mdp_footswitch_ctrl(mdata, on);
+}
+
int mdss_mdp_overlay_init(struct msm_fb_data_type *mfd)
{
struct device *dev = mfd->fbi->dev;
@@ -6321,6 +6340,14 @@ int mdss_mdp_overlay_init(struct msm_fb_data_type *mfd)
mdp5_interface->configure_panel = mdss_mdp_update_panel_info;
mdp5_interface->input_event_handler = mdss_mdp_input_event_handler;
+ /*
+ * Register footswitch control only for primary fb pm
+ * suspend/resume calls.
+ */
+ if (mfd->panel_info->is_prim_panel)
+ mdp5_interface->footswitch_ctrl =
+ mdss_mdp_footswitch_ctrl_handler;
+
if (mfd->panel_info->type == WRITEBACK_PANEL) {
mdp5_interface->atomic_validate =
mdss_mdp_layer_atomic_validate_wfd;
diff --git a/drivers/video/fbdev/msm/mdss_mdp_pp.c b/drivers/video/fbdev/msm/mdss_mdp_pp.c
index f10d4fb60f52..f128f82fab04 100644
--- a/drivers/video/fbdev/msm/mdss_mdp_pp.c
+++ b/drivers/video/fbdev/msm/mdss_mdp_pp.c
@@ -2322,7 +2322,9 @@ static void pp_dspp_opmode_config(struct mdss_mdp_ctl *ctl, u32 num,
*opmode |= MDSS_MDP_DSPP_OP_ARGC_LUT_EN;
}
-static int pp_dspp_setup(u32 disp_num, struct mdss_mdp_mixer *mixer)
+static int pp_dspp_setup(u32 disp_num, struct mdss_mdp_mixer *mixer,
+ u32 pp_program_mask, int *op_mode)
+
{
u32 ad_flags, flags, dspp_num, opmode = 0, ad_bypass;
struct mdp_pgc_lut_data *pgc_config;
@@ -2338,6 +2340,8 @@ static int pp_dspp_setup(u32 disp_num, struct mdss_mdp_mixer *mixer)
u32 mixer_id[MDSS_MDP_INTF_MAX_LAYERMIXER];
int side;
+ opmode = *op_mode;
+
if (!mixer || !mixer->ctl || !mixer->ctl->mdata)
return -EINVAL;
ctl = mixer->ctl;
@@ -2358,19 +2362,23 @@ static int pp_dspp_setup(u32 disp_num, struct mdss_mdp_mixer *mixer)
}
mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_ON);
- if ((mdata->pp_block_off.dspp_gamut_off != U32_MAX) &&
- (pp_driver_ops.gamut_clk_gate_en))
- pp_driver_ops.gamut_clk_gate_en(base +
+ if (pp_program_mask & PP_PROGRAM_GAMUT) {
+ if ((mdata->pp_block_off.dspp_gamut_off != U32_MAX) &&
+ (pp_driver_ops.gamut_clk_gate_en))
+ pp_driver_ops.gamut_clk_gate_en(base +
mdata->pp_block_off.dspp_gamut_off);
-
+ }
if (disp_num < MDSS_BLOCK_DISP_NUM) {
pp_sts = &mdss_pp_res->pp_disp_sts[disp_num];
pp_sts->side_sts = side;
- ret = pp_hist_setup(&opmode, MDSS_PP_DSPP_CFG | dspp_num, mixer,
- pp_sts);
- if (ret)
- goto dspp_exit;
+ if (pp_program_mask & PP_PROGRAM_HIST) {
+ ret = pp_hist_setup(&opmode,
+ MDSS_PP_DSPP_CFG | dspp_num, mixer,
+ pp_sts);
+ if (ret)
+ goto dspp_exit;
+ }
flags = mdss_pp_res->pp_disp_flags[disp_num];
} else {
@@ -2391,7 +2399,8 @@ static int pp_dspp_setup(u32 disp_num, struct mdss_mdp_mixer *mixer)
if ((!flags) && (!(opmode)) && (!ad_flags))
goto dspp_exit;
- if (flags & PP_FLAGS_DIRTY_PA) {
+ if ((flags & PP_FLAGS_DIRTY_PA) &&
+ (pp_program_mask & PP_PROGRAM_PA)) {
if (!pp_ops[PA].pp_set_config) {
if (mdata->mdp_rev >= MDSS_MDP_HW_REV_103) {
pa_v2_cfg_data =
@@ -2412,7 +2421,8 @@ static int pp_dspp_setup(u32 disp_num, struct mdss_mdp_mixer *mixer)
DSPP);
}
}
- if (flags & PP_FLAGS_DIRTY_PCC) {
+ if ((flags & PP_FLAGS_DIRTY_PCC) &&
+ (pp_program_mask & PP_PROGRAM_PCC)) {
if (!pp_ops[PCC].pp_set_config)
pp_pcc_config(flags, base + MDSS_MDP_REG_DSPP_PCC_BASE,
pp_sts,
@@ -2429,7 +2439,8 @@ static int pp_dspp_setup(u32 disp_num, struct mdss_mdp_mixer *mixer)
}
}
- if (flags & PP_FLAGS_DIRTY_IGC) {
+ if ((flags & PP_FLAGS_DIRTY_IGC) &&
+ (pp_program_mask & PP_PROGRAM_IGC)) {
if (!pp_ops[IGC].pp_set_config) {
pp_igc_config(flags,
mdata->mdp_base + MDSS_MDP_REG_IGC_DSPP_BASE,
@@ -2449,7 +2460,8 @@ static int pp_dspp_setup(u32 disp_num, struct mdss_mdp_mixer *mixer)
DSPP);
}
- if (flags & PP_FLAGS_DIRTY_ENHIST) {
+ if ((flags & PP_FLAGS_DIRTY_ENHIST) &&
+ (pp_program_mask & PP_PROGRAM_HIST)) {
if (!pp_ops[HIST_LUT].pp_set_config) {
pp_enhist_config(flags,
base + MDSS_MDP_REG_DSPP_HIST_LUT_BASE,
@@ -2473,7 +2485,8 @@ static int pp_dspp_setup(u32 disp_num, struct mdss_mdp_mixer *mixer)
}
}
- if (flags & PP_FLAGS_DIRTY_DITHER) {
+ if ((flags & PP_FLAGS_DIRTY_DITHER) &&
+ (pp_program_mask & PP_PROGRAM_DITHER)) {
if (!pp_ops[DITHER].pp_set_config && addr) {
pp_dither_config(addr, pp_sts,
&mdss_pp_res->dither_disp_cfg[disp_num]);
@@ -2483,7 +2496,8 @@ static int pp_dspp_setup(u32 disp_num, struct mdss_mdp_mixer *mixer)
&mdss_pp_res->dither_disp_cfg[disp_num], DSPP);
}
}
- if (flags & PP_FLAGS_DIRTY_GAMUT) {
+ if ((flags & PP_FLAGS_DIRTY_GAMUT) &&
+ (pp_program_mask & PP_PROGRAM_GAMUT)) {
if (!pp_ops[GAMUT].pp_set_config) {
pp_gamut_config(&mdss_pp_res->gamut_disp_cfg[disp_num],
base, pp_sts);
@@ -2500,7 +2514,8 @@ static int pp_dspp_setup(u32 disp_num, struct mdss_mdp_mixer *mixer)
}
}
- if (flags & PP_FLAGS_DIRTY_PGC) {
+ if ((flags & PP_FLAGS_DIRTY_PGC) &&
+ (pp_program_mask & PP_PROGRAM_PGC)) {
pgc_config = &mdss_pp_res->pgc_disp_cfg[disp_num];
if (pp_ops[GC].pp_set_config) {
if (mdata->pp_block_off.dspp_pgc_off == U32_MAX) {
@@ -2526,6 +2541,7 @@ static int pp_dspp_setup(u32 disp_num, struct mdss_mdp_mixer *mixer)
}
}
if (flags & PP_FLAGS_DIRTY_PA_DITHER &&
+ (pp_program_mask & PP_PROGRAM_PA_DITHER) &&
pp_ops[PA_DITHER].pp_set_config) {
pp_ops[PA_DITHER].pp_set_config(base, pp_sts,
&mdss_pp_res->pa_dither_cfg[disp_num],
@@ -2536,7 +2552,7 @@ static int pp_dspp_setup(u32 disp_num, struct mdss_mdp_mixer *mixer)
pp_dspp_opmode_config(ctl, dspp_num, pp_sts, mdata->mdp_rev,
&opmode);
- if (ad_hw) {
+ if (ad_hw && (pp_program_mask & PP_PROGRAM_AD)) {
mutex_lock(&ad->lock);
ad_flags = ad->reg_sts;
if (ad_flags & PP_AD_STS_DIRTY_DATA)
@@ -2566,6 +2582,9 @@ static int pp_dspp_setup(u32 disp_num, struct mdss_mdp_mixer *mixer)
ctl->flush_bits |= BIT(13 + dspp_num);
wmb();
+
+ *op_mode = opmode;
+
dspp_exit:
mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_OFF);
return ret;
@@ -2684,6 +2703,8 @@ void mdss_mdp_pp_dest_scaler_resume(struct mdss_mdp_ctl *ctl)
int mdss_mdp_pp_setup(struct mdss_mdp_ctl *ctl)
{
int ret = 0;
+ struct mdss_mdp_pp_program_info pp_program_info = {
+ PP_PROGRAM_ALL, 0, 0};
if ((!ctl->mfd) || (!mdss_pp_res))
return -EINVAL;
@@ -2695,14 +2716,15 @@ int mdss_mdp_pp_setup(struct mdss_mdp_ctl *ctl)
ret = -EPERM;
goto error;
}
- ret = mdss_mdp_pp_setup_locked(ctl);
+ ret = mdss_mdp_pp_setup_locked(ctl, &pp_program_info);
error:
mutex_unlock(&ctl->lock);
return ret;
}
-int mdss_mdp_pp_setup_locked(struct mdss_mdp_ctl *ctl)
+int mdss_mdp_pp_setup_locked(struct mdss_mdp_ctl *ctl,
+ struct mdss_mdp_pp_program_info *info)
{
struct mdss_data_type *mdata;
int ret = 0, i;
@@ -2715,6 +2737,16 @@ int mdss_mdp_pp_setup_locked(struct mdss_mdp_ctl *ctl)
bool valid_ad_panel = true;
if ((!ctl) || (!ctl->mfd) || (!mdss_pp_res) || (!ctl->mdata))
return -EINVAL;
+ if (!info) {
+ pr_err("pp_program_info is NULL");
+ return -EINVAL;
+ }
+ if (!(info->pp_program_mask == PP_NORMAL_PROGRAM_MASK ||
+ info->pp_program_mask == PP_DEFER_PROGRAM_MASK ||
+ info->pp_program_mask == PP_PROGRAM_ALL)) {
+ pr_err("Invalid pp program mask : %x ", info->pp_program_mask);
+ return -EINVAL;
+ }
mdata = ctl->mdata;
/* treat fb_num the same as block logical id*/
@@ -2748,7 +2780,11 @@ int mdss_mdp_pp_setup_locked(struct mdss_mdp_ctl *ctl)
mutex_lock(&mdss_pp_mutex);
- flags = mdss_pp_res->pp_disp_flags[disp_num];
+ if (disp_num < MDSS_BLOCK_DISP_NUM)
+ flags = mdss_pp_res->pp_disp_flags[disp_num];
+ else
+ flags = 0;
+
if (pp_ops[PA].pp_set_config)
pa_v2_flags = mdss_pp_res->pa_v2_disp_cfg[disp_num].flags;
else
@@ -2759,50 +2795,72 @@ int mdss_mdp_pp_setup_locked(struct mdss_mdp_ctl *ctl)
* increase the register bus bandwidth to maximum frequency
* in order to speed up the register reprogramming.
*/
- max_bw_needed = (IS_PP_RESUME_COMMIT(flags) &&
- (IS_PP_LUT_DIRTY(flags) ||
- IS_SIX_ZONE_DIRTY(flags, pa_v2_flags)));
- if (mdata->pp_reg_bus_clt && max_bw_needed) {
- ret = mdss_update_reg_bus_vote(mdata->pp_reg_bus_clt,
- VOTE_INDEX_HIGH);
- if (ret)
- pr_err("Updated reg_bus_scale failed, ret = %d", ret);
+ if (info->pp_program_mask & PP_DEFER_PROGRAM_MASK) {
+ max_bw_needed = (IS_PP_RESUME_COMMIT(flags) &&
+ (IS_PP_LUT_DIRTY(flags) ||
+ IS_SIX_ZONE_DIRTY(flags, pa_v2_flags)));
+ if (mdata->pp_reg_bus_clt && max_bw_needed) {
+ ret = mdss_update_reg_bus_vote(mdata->pp_reg_bus_clt,
+ VOTE_INDEX_HIGH);
+ if (ret)
+ pr_err("Updated reg_bus_scale failed, ret = %d",
+ ret);
+ }
}
if (ctl->mixer_left) {
- pp_mixer_setup(ctl->mixer_left);
- pp_dspp_setup(disp_num, ctl->mixer_left);
- pp_ppb_setup(ctl->mixer_left);
+ if (info->pp_program_mask & PP_DEFER_PROGRAM_MASK) {
+ pp_mixer_setup(ctl->mixer_left);
+ pp_dspp_setup(disp_num, ctl->mixer_left,
+ info->pp_program_mask, &info->pp_opmode_left);
+ pp_ppb_setup(ctl->mixer_left);
+ } else {
+ pp_dspp_setup(disp_num, ctl->mixer_left,
+ info->pp_program_mask, &info->pp_opmode_left);
+ }
}
if (ctl->mixer_right) {
- pp_mixer_setup(ctl->mixer_right);
- pp_dspp_setup(disp_num, ctl->mixer_right);
- pp_ppb_setup(ctl->mixer_right);
+ if (info->pp_program_mask & PP_DEFER_PROGRAM_MASK) {
+ pp_mixer_setup(ctl->mixer_right);
+ pp_dspp_setup(disp_num, ctl->mixer_right,
+ info->pp_program_mask, &info->pp_opmode_right);
+ pp_ppb_setup(ctl->mixer_right);
+ } else {
+ pp_dspp_setup(disp_num, ctl->mixer_right,
+ info->pp_program_mask, &info->pp_opmode_right);
+ }
}
- if (valid_mixers && (mixer_cnt <= mdata->nmax_concurrent_ad_hw) &&
- valid_ad_panel) {
- ret = mdss_mdp_ad_ipc_reset(ctl->mfd);
- if (ret < 0)
- pr_warn("ad_setup(disp%d) returns %d\n", disp_num, ret);
+ if (info->pp_program_mask & PP_PROGRAM_AD) {
+ if (valid_mixers &&
+ (mixer_cnt <= mdata->nmax_concurrent_ad_hw) &&
+ valid_ad_panel) {
+ ret = mdss_mdp_ad_ipc_reset(ctl->mfd);
+ if (ret < 0)
+ pr_warn("ad_setup(disp%d) returns %d\n",
+ disp_num, ret);
+ }
}
- /* clear dirty flag */
- if (disp_num < MDSS_BLOCK_DISP_NUM) {
- mdss_pp_res->pp_disp_flags[disp_num] = 0;
- if (disp_num < mdata->nad_cfgs)
- mdata->ad_cfgs[disp_num].reg_sts = 0;
- }
+ if (info->pp_program_mask & PP_DEFER_PROGRAM_MASK) {
+ /* clear dirty flag */
+ if (disp_num < MDSS_BLOCK_DISP_NUM) {
+ mdss_pp_res->pp_disp_flags[disp_num] = 0;
+ if (disp_num < mdata->nad_cfgs)
+ mdata->ad_cfgs[disp_num].reg_sts = 0;
+ }
- if (mdata->pp_reg_bus_clt && max_bw_needed) {
- ret = mdss_update_reg_bus_vote(mdata->pp_reg_bus_clt,
- VOTE_INDEX_DISABLE);
- if (ret)
- pr_err("Updated reg_bus_scale failed, ret = %d", ret);
+ if (mdata->pp_reg_bus_clt && max_bw_needed) {
+ ret = mdss_update_reg_bus_vote(mdata->pp_reg_bus_clt,
+ VOTE_INDEX_DISABLE);
+ if (ret)
+ pr_err("Updated reg_bus_scale failed, ret = %d",
+ ret);
+ }
+ if (IS_PP_RESUME_COMMIT(flags))
+ mdss_pp_res->pp_disp_flags[disp_num] &=
+ ~PP_FLAGS_RESUME_COMMIT;
}
- if (IS_PP_RESUME_COMMIT(flags))
- mdss_pp_res->pp_disp_flags[disp_num] &=
- ~PP_FLAGS_RESUME_COMMIT;
mutex_unlock(&mdss_pp_mutex);
exit:
return ret;
diff --git a/drivers/video/fbdev/msm/msm_mdss_io_8974.c b/drivers/video/fbdev/msm/msm_mdss_io_8974.c
index 9c156af6b63c..87fe6d739acf 100644
--- a/drivers/video/fbdev/msm/msm_mdss_io_8974.c
+++ b/drivers/video/fbdev/msm/msm_mdss_io_8974.c
@@ -1229,10 +1229,11 @@ static void mdss_dsi_8996_phy_config(struct mdss_dsi_ctrl_pdata *ctrl)
off = DSIPHY_LANE_CFG_BASE;
ln_off = cnt * MDSS_DSI_NUM_DATA_LANES;
ip = &pd->lanecfg[ln_off];
- for (j = 0; j < cnt; j++, *ip++) {
+ for (j = 0; j < cnt; j++) {
MIPI_OUTP(base + off, *ip);
if (panel_info->split_link_enabled)
MIPI_OUTP(base + CLKLANE_SIZE_8996 + off, *ip);
+ ip++;
off += DSIPHY_LANE_CFG_OFFSET;
}
@@ -1245,10 +1246,11 @@ static void mdss_dsi_8996_phy_config(struct mdss_dsi_ctrl_pdata *ctrl)
off = DSIPHY_LANE_TIMING_CTRL_BASE;
ln_off = cnt * MDSS_DSI_NUM_DATA_LANES;
ip = &pd->timing_8996[ln_off];
- for (j = 0; j < cnt; j++, *ip++) {
+ for (j = 0; j < cnt; j++) {
MIPI_OUTP(base + off, *ip);
if (panel_info->split_link_enabled)
MIPI_OUTP(base + CLKLANE_SIZE_8996 + off, *ip);
+ ip++;
off += DSIPHY_LANE_TIMING_CTRL_OFFSET;
}
@@ -1261,10 +1263,11 @@ static void mdss_dsi_8996_phy_config(struct mdss_dsi_ctrl_pdata *ctrl)
off = DSIPHY_LANE_STRENGTH_CTRL_BASE;
ln_off = cnt * MDSS_DSI_NUM_DATA_LANES;
ip = &pd->strength[ln_off];
- for (j = 0; j < cnt; j++, *ip++) {
+ for (j = 0; j < cnt; j++) {
MIPI_OUTP(base + off, *ip);
if (panel_info->split_link_enabled)
MIPI_OUTP(base + CLKLANE_SIZE_8996 + off, *ip);
+ ip++;
off += DSIPHY_LANE_STRENGTH_CTRL_OFFSET;
}
diff --git a/fs/9p/acl.c b/fs/9p/acl.c
index 929b618da43b..c30c6ceac2c4 100644
--- a/fs/9p/acl.c
+++ b/fs/9p/acl.c
@@ -283,6 +283,7 @@ static int v9fs_xattr_set_acl(const struct xattr_handler *handler,
case ACL_TYPE_ACCESS:
if (acl) {
struct iattr iattr;
+ struct posix_acl *old_acl = acl;
retval = posix_acl_update_mode(inode, &iattr.ia_mode, &acl);
if (retval)
@@ -293,6 +294,7 @@ static int v9fs_xattr_set_acl(const struct xattr_handler *handler,
* by the mode bits. So don't
* update ACL.
*/
+ posix_acl_release(old_acl);
value = NULL;
size = 0;
}
diff --git a/fs/ext4/inline.c b/fs/ext4/inline.c
index cb3d6f6419cd..db81acb686c0 100644
--- a/fs/ext4/inline.c
+++ b/fs/ext4/inline.c
@@ -18,6 +18,7 @@
#include "ext4.h"
#include "xattr.h"
#include "truncate.h"
+#include <trace/events/android_fs.h>
#define EXT4_XATTR_SYSTEM_DATA "data"
#define EXT4_MIN_INLINE_DATA_SIZE ((sizeof(__le32) * EXT4_N_BLOCKS))
@@ -502,6 +503,17 @@ int ext4_readpage_inline(struct inode *inode, struct page *page)
return -EAGAIN;
}
+ if (trace_android_fs_dataread_start_enabled()) {
+ char *path, pathbuf[MAX_TRACE_PATHBUF_LEN];
+
+ path = android_fstrace_get_pathname(pathbuf,
+ MAX_TRACE_PATHBUF_LEN,
+ inode);
+ trace_android_fs_dataread_start(inode, page_offset(page),
+ PAGE_SIZE, current->pid,
+ path, current->comm);
+ }
+
/*
* Current inline data can only exist in the 1st page,
* So for all the other pages, just set them uptodate.
@@ -513,6 +525,8 @@ int ext4_readpage_inline(struct inode *inode, struct page *page)
SetPageUptodate(page);
}
+ trace_android_fs_dataread_end(inode, page_offset(page), PAGE_SIZE);
+
up_read(&EXT4_I(inode)->xattr_sem);
unlock_page(page);
diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c
index 99fa2fca52b1..72b384931f66 100644
--- a/fs/ext4/inode.c
+++ b/fs/ext4/inode.c
@@ -45,6 +45,7 @@
#include "ext4_ice.h"
#include <trace/events/ext4.h>
+#include <trace/events/android_fs.h>
#define MPAGE_DA_EXTENT_TAIL 0x01
@@ -1018,6 +1019,16 @@ static int ext4_write_begin(struct file *file, struct address_space *mapping,
pgoff_t index;
unsigned from, to;
+ if (trace_android_fs_datawrite_start_enabled()) {
+ char *path, pathbuf[MAX_TRACE_PATHBUF_LEN];
+
+ path = android_fstrace_get_pathname(pathbuf,
+ MAX_TRACE_PATHBUF_LEN,
+ inode);
+ trace_android_fs_datawrite_start(inode, pos, len,
+ current->pid, path,
+ current->comm);
+ }
trace_ext4_write_begin(inode, pos, len, flags);
/*
* Reserve one block more for addition to orphan list in case
@@ -1154,6 +1165,7 @@ static int ext4_write_end(struct file *file,
int ret = 0, ret2;
int i_size_changed = 0;
+ trace_android_fs_datawrite_end(inode, pos, len);
trace_ext4_write_end(inode, pos, len, copied);
if (ext4_test_inode_state(inode, EXT4_STATE_ORDERED_MODE)) {
ret = ext4_jbd2_file_inode(handle, inode);
@@ -1267,6 +1279,7 @@ static int ext4_journalled_write_end(struct file *file,
unsigned from, to;
int size_changed = 0;
+ trace_android_fs_datawrite_end(inode, pos, len);
trace_ext4_journalled_write_end(inode, pos, len, copied);
from = pos & (PAGE_CACHE_SIZE - 1);
to = from + len;
@@ -2742,6 +2755,16 @@ static int ext4_da_write_begin(struct file *file, struct address_space *mapping,
len, flags, pagep, fsdata);
}
*fsdata = (void *)0;
+ if (trace_android_fs_datawrite_start_enabled()) {
+ char *path, pathbuf[MAX_TRACE_PATHBUF_LEN];
+
+ path = android_fstrace_get_pathname(pathbuf,
+ MAX_TRACE_PATHBUF_LEN,
+ inode);
+ trace_android_fs_datawrite_start(inode, pos, len,
+ current->pid,
+ path, current->comm);
+ }
trace_ext4_da_write_begin(inode, pos, len, flags);
if (ext4_test_inode_state(inode, EXT4_STATE_MAY_INLINE_DATA)) {
@@ -2860,6 +2883,7 @@ static int ext4_da_write_end(struct file *file,
return ext4_write_end(file, mapping, pos,
len, copied, page, fsdata);
+ trace_android_fs_datawrite_end(inode, pos, len);
trace_ext4_da_write_end(inode, pos, len, copied);
start = pos & (PAGE_CACHE_SIZE - 1);
end = start + copied - 1;
@@ -3352,12 +3376,42 @@ static ssize_t ext4_direct_IO(struct kiocb *iocb, struct iov_iter *iter,
if (ext4_has_inline_data(inode))
return 0;
+ if (trace_android_fs_dataread_start_enabled() &&
+ (iov_iter_rw(iter) == READ)) {
+ char *path, pathbuf[MAX_TRACE_PATHBUF_LEN];
+
+ path = android_fstrace_get_pathname(pathbuf,
+ MAX_TRACE_PATHBUF_LEN,
+ inode);
+ trace_android_fs_dataread_start(inode, offset, count,
+ current->pid, path,
+ current->comm);
+ }
+ if (trace_android_fs_datawrite_start_enabled() &&
+ (iov_iter_rw(iter) == WRITE)) {
+ char *path, pathbuf[MAX_TRACE_PATHBUF_LEN];
+
+ path = android_fstrace_get_pathname(pathbuf,
+ MAX_TRACE_PATHBUF_LEN,
+ inode);
+ trace_android_fs_datawrite_start(inode, offset, count,
+ current->pid, path,
+ current->comm);
+ }
trace_ext4_direct_IO_enter(inode, offset, count, iov_iter_rw(iter));
if (ext4_test_inode_flag(inode, EXT4_INODE_EXTENTS))
ret = ext4_ext_direct_IO(iocb, iter, offset);
else
ret = ext4_ind_direct_IO(iocb, iter, offset);
trace_ext4_direct_IO_exit(inode, offset, count, iov_iter_rw(iter), ret);
+
+ if (trace_android_fs_dataread_start_enabled() &&
+ (iov_iter_rw(iter) == READ))
+ trace_android_fs_dataread_end(inode, offset, count);
+ if (trace_android_fs_datawrite_start_enabled() &&
+ (iov_iter_rw(iter) == WRITE))
+ trace_android_fs_datawrite_end(inode, offset, count);
+
return ret;
}
diff --git a/fs/ext4/readpage.c b/fs/ext4/readpage.c
index 71a08a294529..99f1bd8c7f05 100644
--- a/fs/ext4/readpage.c
+++ b/fs/ext4/readpage.c
@@ -46,6 +46,7 @@
#include "ext4.h"
#include "ext4_ice.h"
+#include <trace/events/android_fs.h>
/*
* Call ext4_decrypt on every single page, reusing the encryption
@@ -92,6 +93,17 @@ static inline bool ext4_bio_encrypted(struct bio *bio)
#endif
}
+static void
+ext4_trace_read_completion(struct bio *bio)
+{
+ struct page *first_page = bio->bi_io_vec[0].bv_page;
+
+ if (first_page != NULL)
+ trace_android_fs_dataread_end(first_page->mapping->host,
+ page_offset(first_page),
+ bio->bi_iter.bi_size);
+}
+
/*
* I/O completion handler for multipage BIOs.
*
@@ -109,6 +121,9 @@ static void mpage_end_io(struct bio *bio)
struct bio_vec *bv;
int i;
+ if (trace_android_fs_dataread_start_enabled())
+ ext4_trace_read_completion(bio);
+
if (ext4_bio_encrypted(bio)) {
struct ext4_crypto_ctx *ctx = bio->bi_private;
@@ -136,6 +151,30 @@ static void mpage_end_io(struct bio *bio)
bio_put(bio);
}
+static void
+ext4_submit_bio_read(struct bio *bio)
+{
+ if (trace_android_fs_dataread_start_enabled()) {
+ struct page *first_page = bio->bi_io_vec[0].bv_page;
+
+ if (first_page != NULL) {
+ char *path, pathbuf[MAX_TRACE_PATHBUF_LEN];
+
+ path = android_fstrace_get_pathname(pathbuf,
+ MAX_TRACE_PATHBUF_LEN,
+ first_page->mapping->host);
+ trace_android_fs_dataread_start(
+ first_page->mapping->host,
+ page_offset(first_page),
+ bio->bi_iter.bi_size,
+ current->pid,
+ path,
+ current->comm);
+ }
+ }
+ submit_bio(READ, bio);
+}
+
int ext4_mpage_readpages(struct address_space *mapping,
struct list_head *pages, struct page *page,
unsigned nr_pages)
@@ -277,7 +316,7 @@ int ext4_mpage_readpages(struct address_space *mapping,
*/
if (bio && (last_block_in_bio != blocks[0] - 1)) {
submit_and_realloc:
- submit_bio(READ, bio);
+ ext4_submit_bio_read(bio);
bio = NULL;
}
if (bio == NULL) {
@@ -309,14 +348,14 @@ int ext4_mpage_readpages(struct address_space *mapping,
if (((map.m_flags & EXT4_MAP_BOUNDARY) &&
(relative_block == map.m_len)) ||
(first_hole != blocks_per_page)) {
- submit_bio(READ, bio);
+ ext4_submit_bio_read(bio);
bio = NULL;
} else
last_block_in_bio = blocks[blocks_per_page - 1];
goto next_page;
confused:
if (bio) {
- submit_bio(READ, bio);
+ ext4_submit_bio_read(bio);
bio = NULL;
}
if (!PageUptodate(page))
@@ -329,6 +368,6 @@ int ext4_mpage_readpages(struct address_space *mapping,
}
BUG_ON(pages && !list_empty(pages));
if (bio)
- submit_bio(READ, bio);
+ ext4_submit_bio_read(bio);
return 0;
}
diff --git a/fs/f2fs/data.c b/fs/f2fs/data.c
index f53826ec30f3..4fb5709256fd 100644
--- a/fs/f2fs/data.c
+++ b/fs/f2fs/data.c
@@ -26,6 +26,7 @@
#include "segment.h"
#include "trace.h"
#include <trace/events/f2fs.h>
+#include <trace/events/android_fs.h>
static void f2fs_read_end_io(struct bio *bio)
{
@@ -1405,6 +1406,16 @@ static int f2fs_write_begin(struct file *file, struct address_space *mapping,
struct dnode_of_data dn;
int err = 0;
+ if (trace_android_fs_datawrite_start_enabled()) {
+ char *path, pathbuf[MAX_TRACE_PATHBUF_LEN];
+
+ path = android_fstrace_get_pathname(pathbuf,
+ MAX_TRACE_PATHBUF_LEN,
+ inode);
+ trace_android_fs_datawrite_start(inode, pos, len,
+ current->pid, path,
+ current->comm);
+ }
trace_f2fs_write_begin(inode, pos, len, flags);
f2fs_balance_fs(sbi);
@@ -1533,6 +1544,7 @@ static int f2fs_write_end(struct file *file,
{
struct inode *inode = page->mapping->host;
+ trace_android_fs_datawrite_end(inode, pos, len);
trace_f2fs_write_end(inode, pos, len, copied);
set_page_dirty(page);
@@ -1586,6 +1598,28 @@ static ssize_t f2fs_direct_IO(struct kiocb *iocb, struct iov_iter *iter,
trace_f2fs_direct_IO_enter(inode, offset, count, iov_iter_rw(iter));
+ if (trace_android_fs_dataread_start_enabled() &&
+ (iov_iter_rw(iter) == READ)) {
+ char *path, pathbuf[MAX_TRACE_PATHBUF_LEN];
+
+ path = android_fstrace_get_pathname(pathbuf,
+ MAX_TRACE_PATHBUF_LEN,
+ inode);
+ trace_android_fs_dataread_start(inode, offset,
+ count, current->pid, path,
+ current->comm);
+ }
+ if (trace_android_fs_datawrite_start_enabled() &&
+ (iov_iter_rw(iter) == WRITE)) {
+ char *path, pathbuf[MAX_TRACE_PATHBUF_LEN];
+
+ path = android_fstrace_get_pathname(pathbuf,
+ MAX_TRACE_PATHBUF_LEN,
+ inode);
+ trace_android_fs_datawrite_start(inode, offset, count,
+ current->pid, path,
+ current->comm);
+ }
if (iov_iter_rw(iter) == WRITE) {
__allocate_data_blocks(inode, offset, count);
if (unlikely(f2fs_cp_error(F2FS_I_SB(inode)))) {
@@ -1599,6 +1633,13 @@ out:
if (err < 0 && iov_iter_rw(iter) == WRITE)
f2fs_write_failed(mapping, offset + count);
+ if (trace_android_fs_dataread_start_enabled() &&
+ (iov_iter_rw(iter) == READ))
+ trace_android_fs_dataread_end(inode, offset, count);
+ if (trace_android_fs_datawrite_start_enabled() &&
+ (iov_iter_rw(iter) == WRITE))
+ trace_android_fs_datawrite_end(inode, offset, count);
+
trace_f2fs_direct_IO_exit(inode, offset, count, iov_iter_rw(iter), err);
return err;
diff --git a/fs/f2fs/inline.c b/fs/f2fs/inline.c
index bda7126466c0..dbb2cc4df603 100644
--- a/fs/f2fs/inline.c
+++ b/fs/f2fs/inline.c
@@ -13,6 +13,7 @@
#include "f2fs.h"
#include "node.h"
+#include <trace/events/android_fs.h>
bool f2fs_may_inline_data(struct inode *inode)
{
@@ -84,14 +85,29 @@ int f2fs_read_inline_data(struct inode *inode, struct page *page)
{
struct page *ipage;
+ if (trace_android_fs_dataread_start_enabled()) {
+ char *path, pathbuf[MAX_TRACE_PATHBUF_LEN];
+
+ path = android_fstrace_get_pathname(pathbuf,
+ MAX_TRACE_PATHBUF_LEN,
+ inode);
+ trace_android_fs_dataread_start(inode, page_offset(page),
+ PAGE_SIZE, current->pid,
+ path, current->comm);
+ }
+
ipage = get_node_page(F2FS_I_SB(inode), inode->i_ino);
if (IS_ERR(ipage)) {
+ trace_android_fs_dataread_end(inode, page_offset(page),
+ PAGE_SIZE);
unlock_page(page);
return PTR_ERR(ipage);
}
if (!f2fs_has_inline_data(inode)) {
f2fs_put_page(ipage, 1);
+ trace_android_fs_dataread_end(inode, page_offset(page),
+ PAGE_SIZE);
return -EAGAIN;
}
@@ -102,6 +118,8 @@ int f2fs_read_inline_data(struct inode *inode, struct page *page)
SetPageUptodate(page);
f2fs_put_page(ipage, 1);
+ trace_android_fs_dataread_end(inode, page_offset(page),
+ PAGE_SIZE);
unlock_page(page);
return 0;
}
diff --git a/fs/gfs2/acl.c b/fs/gfs2/acl.c
index ff0ac96a8e7b..db4c867369b5 100644
--- a/fs/gfs2/acl.c
+++ b/fs/gfs2/acl.c
@@ -78,8 +78,11 @@ int gfs2_set_acl(struct inode *inode, struct posix_acl *acl, int type)
if (type == ACL_TYPE_ACCESS) {
umode_t mode = inode->i_mode;
-
+ struct posix_acl *old_acl = acl;
error = posix_acl_update_mode(inode, &inode->i_mode, &acl);
+
+ if (!acl)
+ posix_acl_release(old_acl);
if (error)
return error;
if (mode != inode->i_mode)
diff --git a/fs/mpage.c b/fs/mpage.c
index 1480d3a18037..0fd48fdcc1b1 100644
--- a/fs/mpage.c
+++ b/fs/mpage.c
@@ -30,6 +30,14 @@
#include <linux/cleancache.h>
#include "internal.h"
+#define CREATE_TRACE_POINTS
+#include <trace/events/android_fs.h>
+
+EXPORT_TRACEPOINT_SYMBOL(android_fs_datawrite_start);
+EXPORT_TRACEPOINT_SYMBOL(android_fs_datawrite_end);
+EXPORT_TRACEPOINT_SYMBOL(android_fs_dataread_start);
+EXPORT_TRACEPOINT_SYMBOL(android_fs_dataread_end);
+
/*
* I/O completion handler for multipage BIOs.
*
@@ -47,6 +55,16 @@ static void mpage_end_io(struct bio *bio)
struct bio_vec *bv;
int i;
+ if (trace_android_fs_dataread_end_enabled() &&
+ (bio_data_dir(bio) == READ)) {
+ struct page *first_page = bio->bi_io_vec[0].bv_page;
+
+ if (first_page != NULL)
+ trace_android_fs_dataread_end(first_page->mapping->host,
+ page_offset(first_page),
+ bio->bi_iter.bi_size);
+ }
+
bio_for_each_segment_all(bv, bio, i) {
struct page *page = bv->bv_page;
page_endio(page, bio_data_dir(bio), bio->bi_error);
@@ -57,6 +75,24 @@ static void mpage_end_io(struct bio *bio)
static struct bio *mpage_bio_submit(int rw, struct bio *bio)
{
+ if (trace_android_fs_dataread_start_enabled() && (rw == READ)) {
+ struct page *first_page = bio->bi_io_vec[0].bv_page;
+
+ if (first_page != NULL) {
+ char *path, pathbuf[MAX_TRACE_PATHBUF_LEN];
+
+ path = android_fstrace_get_pathname(pathbuf,
+ MAX_TRACE_PATHBUF_LEN,
+ first_page->mapping->host);
+ trace_android_fs_dataread_start(
+ first_page->mapping->host,
+ page_offset(first_page),
+ bio->bi_iter.bi_size,
+ current->pid,
+ path,
+ current->comm);
+ }
+ }
bio->bi_end_io = mpage_end_io;
guard_bio_eod(rw, bio);
submit_bio(rw, bio);
diff --git a/fs/xfs/xfs_acl.c b/fs/xfs/xfs_acl.c
index c5101a3295d8..62ba66e1c598 100644
--- a/fs/xfs/xfs_acl.c
+++ b/fs/xfs/xfs_acl.c
@@ -289,8 +289,10 @@ xfs_set_acl(struct inode *inode, struct posix_acl *acl, int type)
if (type == ACL_TYPE_ACCESS) {
umode_t mode;
-
+ struct posix_acl *old_acl = acl;
error = posix_acl_update_mode(inode, &mode, &acl);
+ if (!acl)
+ posix_acl_release(old_acl);
if (error)
return error;
error = xfs_set_mode(inode, mode);
diff --git a/include/linux/io-pgtable-fast.h b/include/linux/io-pgtable-fast.h
index 029e11f9919b..6a56f0039f15 100644
--- a/include/linux/io-pgtable-fast.h
+++ b/include/linux/io-pgtable-fast.h
@@ -37,7 +37,7 @@ void av8l_fast_unmap_public(av8l_fast_iopte *ptep, size_t size);
#define AV8L_FAST_PTE_UNMAPPED_NEED_TLBI 0xa
void av8l_fast_clear_stale_ptes(av8l_fast_iopte *puds, u64 base,
- u64 end, bool skip_sync);
+ u64 start, u64 end, bool skip_sync);
void av8l_register_notify(struct notifier_block *nb);
#else /* !CONFIG_IOMMU_IO_PGTABLE_FAST_PROVE_TLB */
@@ -46,6 +46,7 @@ void av8l_register_notify(struct notifier_block *nb);
static inline void av8l_fast_clear_stale_ptes(av8l_fast_iopte *puds,
u64 base,
+ u64 start,
u64 end,
bool skip_sync)
{
diff --git a/include/linux/leds-qpnp-flash.h b/include/linux/leds-qpnp-flash.h
index 4b5a339970fa..1fe6e1709fa6 100644
--- a/include/linux/leds-qpnp-flash.h
+++ b/include/linux/leds-qpnp-flash.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2016-2017, 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
@@ -18,7 +18,6 @@
#define ENABLE_REGULATOR BIT(0)
#define DISABLE_REGULATOR BIT(1)
#define QUERY_MAX_CURRENT BIT(2)
-#define PRE_FLASH BIT(3)
#define FLASH_LED_PREPARE_OPTIONS_MASK GENMASK(3, 0)
diff --git a/include/linux/msm_smd_pkt.h b/include/linux/msm_smd_pkt.h
new file mode 100644
index 000000000000..c79933d27d4a
--- /dev/null
+++ b/include/linux/msm_smd_pkt.h
@@ -0,0 +1,23 @@
+/* Copyright (c) 2010,2017 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 __LINUX_MSM_SMD_PKT_H
+#define __LINUX_MSM_SMD_PKT_H
+
+#include <linux/ioctl.h>
+
+#define SMD_PKT_IOCTL_MAGIC (0xC2)
+
+#define SMD_PKT_IOCTL_BLOCKING_WRITE \
+ _IOR(SMD_PKT_IOCTL_MAGIC, 0, unsigned int)
+
+#endif /* __LINUX_MSM_SMD_PKT_H */
diff --git a/include/linux/perf/arm_pmu.h b/include/linux/perf/arm_pmu.h
index 654bb97a3188..5bc4836af286 100644
--- a/include/linux/perf/arm_pmu.h
+++ b/include/linux/perf/arm_pmu.h
@@ -77,6 +77,12 @@ struct pmu_hw_events {
struct arm_pmu *percpu_pmu;
};
+enum armpmu_pmu_states {
+ ARM_PMU_STATE_OFF,
+ ARM_PMU_STATE_RUNNING,
+ ARM_PMU_STATE_GOING_DOWN,
+};
+
struct arm_pmu {
struct pmu pmu;
cpumask_t active_irqs;
@@ -101,6 +107,8 @@ struct arm_pmu {
void (*free_irq)(struct arm_pmu *);
int (*map_event)(struct perf_event *event);
int num_events;
+ int pmu_state;
+ int percpu_irq;
atomic_t active_events;
struct mutex reserve_mutex;
u64 max_period;
diff --git a/include/linux/qpnp/qpnp-revid.h b/include/linux/qpnp/qpnp-revid.h
index 7fca674b6230..b025df568259 100644
--- a/include/linux/qpnp/qpnp-revid.h
+++ b/include/linux/qpnp/qpnp-revid.h
@@ -214,6 +214,11 @@
#define PM660L_V1P1_REV3 0x01
#define PM660L_V1P1_REV4 0x01
+#define PM660L_V2P0_REV1 0x00
+#define PM660L_V2P0_REV2 0x00
+#define PM660L_V2P0_REV3 0x00
+#define PM660L_V2P0_REV4 0x02
+
/* PMI8998 FAB_ID */
#define PMI8998_FAB_ID_SMIC 0x11
#define PMI8998_FAB_ID_GF 0x30
diff --git a/include/linux/regulator/qpnp-labibb-regulator.h b/include/linux/regulator/qpnp-labibb-regulator.h
index 247069507fd9..33985afeb6e9 100644
--- a/include/linux/regulator/qpnp-labibb-regulator.h
+++ b/include/linux/regulator/qpnp-labibb-regulator.h
@@ -15,6 +15,7 @@
enum labibb_notify_event {
LAB_VREG_OK = 1,
+ LAB_VREG_NOT_OK,
};
int qpnp_labibb_notifier_register(struct notifier_block *nb);
diff --git a/include/linux/shrinker.h b/include/linux/shrinker.h
index 4fcacd915d45..e77f648f9662 100644
--- a/include/linux/shrinker.h
+++ b/include/linux/shrinker.h
@@ -66,6 +66,7 @@ struct shrinker {
/* Flags */
#define SHRINKER_NUMA_AWARE (1 << 0)
#define SHRINKER_MEMCG_AWARE (1 << 1)
+#define SHRINKER_LMK (1 << 2)
extern int register_shrinker(struct shrinker *);
extern void unregister_shrinker(struct shrinker *);
diff --git a/include/soc/qcom/minidump.h b/include/soc/qcom/minidump.h
new file mode 100644
index 000000000000..9d993a17fb89
--- /dev/null
+++ b/include/soc/qcom/minidump.h
@@ -0,0 +1,48 @@
+/* Copyright (c) 2017 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 __MINIDUMP_H
+#define __MINIDUMP_H
+
+#define MAX_NAME_LENGTH 16
+/* md_region - Minidump table entry
+ * @name: Entry name, Minidump will dump binary with this name.
+ * @id: Entry ID, used only for SDI dumps.
+ * @virt_addr: Address of the entry.
+ * @phys_addr: Physical address of the entry to dump.
+ * @size: Number of byte to dump from @address location
+ * it should be 4 byte aligned.
+ */
+struct md_region {
+ char name[MAX_NAME_LENGTH];
+ u32 id;
+ u64 virt_addr;
+ u64 phys_addr;
+ u64 size;
+};
+
+/* Register an entry in Minidump table
+ * Returns:
+ * Zero: on successful addition
+ * Negetive error number on failures
+ */
+#ifdef CONFIG_QCOM_MINIDUMP
+extern int msm_minidump_add_region(const struct md_region *entry);
+extern bool msm_minidump_enabled(void);
+#else
+static inline int msm_minidump_add_region(const struct md_region *entry)
+{
+ return -ENODEV;
+}
+static inline bool msm_minidump_enabled(void) { return false; }
+#endif
+#endif
diff --git a/include/trace/events/android_fs.h b/include/trace/events/android_fs.h
new file mode 100644
index 000000000000..49509533d3fa
--- /dev/null
+++ b/include/trace/events/android_fs.h
@@ -0,0 +1,65 @@
+#undef TRACE_SYSTEM
+#define TRACE_SYSTEM android_fs
+
+#if !defined(_TRACE_ANDROID_FS_H) || defined(TRACE_HEADER_MULTI_READ)
+#define _TRACE_ANDROID_FS_H
+
+#include <linux/tracepoint.h>
+#include <trace/events/android_fs_template.h>
+
+DEFINE_EVENT(android_fs_data_start_template, android_fs_dataread_start,
+ TP_PROTO(struct inode *inode, loff_t offset, int bytes,
+ pid_t pid, char *pathname, char *command),
+ TP_ARGS(inode, offset, bytes, pid, pathname, command));
+
+DEFINE_EVENT(android_fs_data_end_template, android_fs_dataread_end,
+ TP_PROTO(struct inode *inode, loff_t offset, int bytes),
+ TP_ARGS(inode, offset, bytes));
+
+DEFINE_EVENT(android_fs_data_start_template, android_fs_datawrite_start,
+ TP_PROTO(struct inode *inode, loff_t offset, int bytes,
+ pid_t pid, char *pathname, char *command),
+ TP_ARGS(inode, offset, bytes, pid, pathname, command));
+
+DEFINE_EVENT(android_fs_data_end_template, android_fs_datawrite_end,
+ TP_PROTO(struct inode *inode, loff_t offset, int bytes),
+ TP_ARGS(inode, offset, bytes));
+
+#endif /* _TRACE_ANDROID_FS_H */
+
+/* This part must be outside protection */
+#include <trace/define_trace.h>
+
+#ifndef ANDROID_FSTRACE_GET_PATHNAME
+#define ANDROID_FSTRACE_GET_PATHNAME
+
+/* Sizes an on-stack array, so careful if sizing this up ! */
+#define MAX_TRACE_PATHBUF_LEN 256
+
+static inline char *
+android_fstrace_get_pathname(char *buf, int buflen, struct inode *inode)
+{
+ char *path;
+ struct dentry *d;
+
+ /*
+ * d_obtain_alias() will either iput() if it locates an existing
+ * dentry or transfer the reference to the new dentry created.
+ * So get an extra reference here.
+ */
+ ihold(inode);
+ d = d_obtain_alias(inode);
+ if (likely(!IS_ERR(d))) {
+ path = dentry_path_raw(d, buf, buflen);
+ if (unlikely(IS_ERR(path))) {
+ strcpy(buf, "ERROR");
+ path = buf;
+ }
+ dput(d);
+ } else {
+ strcpy(buf, "ERROR");
+ path = buf;
+ }
+ return path;
+}
+#endif
diff --git a/include/trace/events/android_fs_template.h b/include/trace/events/android_fs_template.h
new file mode 100644
index 000000000000..4e61ffe7a814
--- /dev/null
+++ b/include/trace/events/android_fs_template.h
@@ -0,0 +1,57 @@
+#if !defined(_TRACE_ANDROID_FS_TEMPLATE_H) || defined(TRACE_HEADER_MULTI_READ)
+#define _TRACE_ANDROID_FS_TEMPLATE_H
+
+#include <linux/tracepoint.h>
+
+DECLARE_EVENT_CLASS(android_fs_data_start_template,
+ TP_PROTO(struct inode *inode, loff_t offset, int bytes,
+ pid_t pid, char *pathname, char *command),
+ TP_ARGS(inode, offset, bytes, pid, pathname, command),
+ TP_STRUCT__entry(
+ __string(pathbuf, pathname);
+ __field(loff_t, offset);
+ __field(int, bytes);
+ __field(loff_t, i_size);
+ __string(cmdline, command);
+ __field(pid_t, pid);
+ __field(ino_t, ino);
+ ),
+ TP_fast_assign(
+ {
+ __assign_str(pathbuf, pathname);
+ __entry->offset = offset;
+ __entry->bytes = bytes;
+ __entry->i_size = i_size_read(inode);
+ __assign_str(cmdline, command);
+ __entry->pid = pid;
+ __entry->ino = inode->i_ino;
+ }
+ ),
+ TP_printk("entry_name %s, offset %llu, bytes %d, cmdline %s,"
+ " pid %d, i_size %llu, ino %lu",
+ __get_str(pathbuf), __entry->offset, __entry->bytes,
+ __get_str(cmdline), __entry->pid, __entry->i_size,
+ (unsigned long) __entry->ino)
+);
+
+DECLARE_EVENT_CLASS(android_fs_data_end_template,
+ TP_PROTO(struct inode *inode, loff_t offset, int bytes),
+ TP_ARGS(inode, offset, bytes),
+ TP_STRUCT__entry(
+ __field(ino_t, ino);
+ __field(loff_t, offset);
+ __field(int, bytes);
+ ),
+ TP_fast_assign(
+ {
+ __entry->ino = inode->i_ino;
+ __entry->offset = offset;
+ __entry->bytes = bytes;
+ }
+ ),
+ TP_printk("ino %lu, offset %llu, bytes %d",
+ (unsigned long) __entry->ino,
+ __entry->offset, __entry->bytes)
+);
+
+#endif /* _TRACE_ANDROID_FS_TEMPLATE_H */
diff --git a/kernel/trace/msm_rtb.c b/kernel/trace/msm_rtb.c
index 80058b544cb5..587082117842 100644
--- a/kernel/trace/msm_rtb.c
+++ b/kernel/trace/msm_rtb.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2013-2016, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2013-2017, 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
@@ -28,6 +28,7 @@
#include <asm-generic/sizes.h>
#include <linux/msm_rtb.h>
#include <asm/timex.h>
+#include <soc/qcom/minidump.h>
#define SENTINEL_BYTE_1 0xFF
#define SENTINEL_BYTE_2 0xAA
@@ -243,6 +244,7 @@ EXPORT_SYMBOL(uncached_logk);
static int msm_rtb_probe(struct platform_device *pdev)
{
struct msm_rtb_platform_data *d = pdev->dev.platform_data;
+ struct md_region md_entry;
#if defined(CONFIG_QCOM_RTB_SEPARATE_CPUS)
unsigned int cpu;
#endif
@@ -294,6 +296,12 @@ static int msm_rtb_probe(struct platform_device *pdev)
memset(msm_rtb.rtb, 0, msm_rtb.size);
+ strlcpy(md_entry.name, "KRTB_BUF", sizeof(md_entry.name));
+ md_entry.virt_addr = (uintptr_t)msm_rtb.rtb;
+ md_entry.phys_addr = msm_rtb.phys;
+ md_entry.size = msm_rtb.size;
+ if (msm_minidump_add_region(&md_entry))
+ pr_info("Failed to add RTB in Minidump\n");
#if defined(CONFIG_QCOM_RTB_SEPARATE_CPUS)
for_each_possible_cpu(cpu) {
diff --git a/mm/vmscan.c b/mm/vmscan.c
index 5e9e74955bd1..94fecacf0ddc 100644
--- a/mm/vmscan.c
+++ b/mm/vmscan.c
@@ -399,6 +399,35 @@ static unsigned long do_shrink_slab(struct shrink_control *shrinkctl,
return freed;
}
+static void shrink_slab_lmk(gfp_t gfp_mask, int nid,
+ struct mem_cgroup *memcg,
+ unsigned long nr_scanned,
+ unsigned long nr_eligible)
+{
+ struct shrinker *shrinker;
+
+ if (nr_scanned == 0)
+ nr_scanned = SWAP_CLUSTER_MAX;
+
+ if (!down_read_trylock(&shrinker_rwsem))
+ goto out;
+
+ list_for_each_entry(shrinker, &shrinker_list, list) {
+ struct shrink_control sc = {
+ .gfp_mask = gfp_mask,
+ };
+
+ if (!(shrinker->flags & SHRINKER_LMK))
+ continue;
+
+ do_shrink_slab(&sc, shrinker, nr_scanned, nr_eligible);
+ }
+
+ up_read(&shrinker_rwsem);
+out:
+ cond_resched();
+}
+
/**
* shrink_slab - shrink slab caches
* @gfp_mask: allocation context
@@ -460,6 +489,9 @@ static unsigned long shrink_slab(gfp_t gfp_mask, int nid,
.memcg = memcg,
};
+ if (shrinker->flags & SHRINKER_LMK)
+ continue;
+
if (memcg && !(shrinker->flags & SHRINKER_MEMCG_AWARE))
continue;
@@ -2626,6 +2658,7 @@ static bool shrink_zones(struct zonelist *zonelist, struct scan_control *sc)
gfp_t orig_mask;
enum zone_type requested_highidx = gfp_zone(sc->gfp_mask);
bool reclaimable = false;
+ unsigned long lru_pages = 0;
/*
* If the number of buffer_heads in the machine exceeds the maximum
@@ -2653,6 +2686,7 @@ static bool shrink_zones(struct zonelist *zonelist, struct scan_control *sc)
* to global LRU.
*/
if (global_reclaim(sc)) {
+ lru_pages += zone_reclaimable_pages(zone);
if (!cpuset_zone_allowed(zone,
GFP_KERNEL | __GFP_HARDWALL))
continue;
@@ -2703,6 +2737,9 @@ static bool shrink_zones(struct zonelist *zonelist, struct scan_control *sc)
reclaimable = true;
}
+ if (global_reclaim(sc))
+ shrink_slab_lmk(sc->gfp_mask, 0, NULL,
+ sc->nr_scanned, lru_pages);
/*
* Restore to original mask to avoid the impact on the caller if we
* promoted it to __GFP_HIGHMEM.
@@ -3181,7 +3218,8 @@ static bool prepare_kswapd_sleep(pg_data_t *pgdat, int order, long remaining,
*/
static bool kswapd_shrink_zone(struct zone *zone,
int classzone_idx,
- struct scan_control *sc)
+ struct scan_control *sc,
+ unsigned long lru_pages)
{
unsigned long balance_gap;
bool lowmem_pressure;
@@ -3208,6 +3246,8 @@ static bool kswapd_shrink_zone(struct zone *zone,
return true;
shrink_zone(zone, sc, zone_idx(zone) == classzone_idx);
+ shrink_slab_lmk(sc->gfp_mask, zone_to_nid(zone), NULL,
+ sc->nr_scanned, lru_pages);
clear_bit(ZONE_WRITEBACK, &zone->flags);
@@ -3265,6 +3305,7 @@ static int balance_pgdat(pg_data_t *pgdat, int order, int classzone_idx)
do {
bool raise_priority = true;
+ unsigned long lru_pages = 0;
sc.nr_reclaimed = 0;
@@ -3322,6 +3363,15 @@ static int balance_pgdat(pg_data_t *pgdat, int order, int classzone_idx)
if (sc.priority < DEF_PRIORITY - 2)
sc.may_writepage = 1;
+ for (i = 0; i <= end_zone; i++) {
+ struct zone *zone = pgdat->node_zones + i;
+
+ if (!populated_zone(zone))
+ continue;
+
+ lru_pages += zone_reclaimable_pages(zone);
+ }
+
/*
* Now scan the zone in the dma->highmem direction, stopping
* at the last zone which needs scanning.
@@ -3358,7 +3408,7 @@ static int balance_pgdat(pg_data_t *pgdat, int order, int classzone_idx)
* that that high watermark would be met at 100%
* efficiency.
*/
- if (kswapd_shrink_zone(zone, end_zone, &sc))
+ if (kswapd_shrink_zone(zone, end_zone, &sc, lru_pages))
raise_priority = false;
}
diff --git a/net/ipv6/netfilter/Kconfig b/net/ipv6/netfilter/Kconfig
index e10a04c9cdc7..cf336d670f8b 100644
--- a/net/ipv6/netfilter/Kconfig
+++ b/net/ipv6/netfilter/Kconfig
@@ -135,6 +135,18 @@ config IP6_NF_IPTABLES
if IP6_NF_IPTABLES
+config IP6_NF_IPTABLES_128
+ tristate "128 bit arithmetic for iptables matching"
+ depends on IP6_NF_IPTABLES
+ help
+ This enables 128 bit matching in ip6tables to help optimize cases
+ where there is no match required. ip6tables matching for ipv6 always
+ has a mask if an address is specified for match. Adding a check for
+ mask prior to that helps to improve performance as it avoids the
+ masked comparison.
+
+ Note that this feature depends on the architecture. If unsure, say N.
+
# The simple matches.
config IP6_NF_MATCH_AH
tristate '"ah" match support'
diff --git a/net/ipv6/netfilter/ip6_tables.c b/net/ipv6/netfilter/ip6_tables.c
index 22f39e00bef3..6fd784643d6e 100644
--- a/net/ipv6/netfilter/ip6_tables.c
+++ b/net/ipv6/netfilter/ip6_tables.c
@@ -94,22 +94,26 @@ ip6_packet_match(const struct sk_buff *skb,
{
unsigned long ret;
const struct ipv6hdr *ipv6 = ipv6_hdr(skb);
+#if IS_ENABLED(IP6_NF_IPTABLES_128)
+ const __uint128_t *ulm1 = (const __uint128_t *)&ip6info->smsk;
+ const __uint128_t *ulm2 = (const __uint128_t *)&ip6info->dmsk;
+#endif
#define FWINV(bool, invflg) ((bool) ^ !!(ip6info->invflags & (invflg)))
- if (FWINV(ipv6_masked_addr_cmp(&ipv6->saddr, &ip6info->smsk,
- &ip6info->src), IP6T_INV_SRCIP) ||
- FWINV(ipv6_masked_addr_cmp(&ipv6->daddr, &ip6info->dmsk,
- &ip6info->dst), IP6T_INV_DSTIP)) {
- dprintf("Source or dest mismatch.\n");
-/*
- dprintf("SRC: %u. Mask: %u. Target: %u.%s\n", ip->saddr,
- ipinfo->smsk.s_addr, ipinfo->src.s_addr,
- ipinfo->invflags & IP6T_INV_SRCIP ? " (INV)" : "");
- dprintf("DST: %u. Mask: %u. Target: %u.%s\n", ip->daddr,
- ipinfo->dmsk.s_addr, ipinfo->dst.s_addr,
- ipinfo->invflags & IP6T_INV_DSTIP ? " (INV)" : "");*/
- return false;
+#if IS_ENABLED(IP6_NF_IPTABLES_128)
+ if (*ulm1 || *ulm2)
+#endif
+ {
+ if (FWINV(ipv6_masked_addr_cmp
+ (&ipv6->saddr, &ip6info->smsk, &ip6info->src),
+ IP6T_INV_SRCIP) ||
+ FWINV(ipv6_masked_addr_cmp(&ipv6->daddr, &ip6info->dmsk,
+ &ip6info->dst),
+ IP6T_INV_DSTIP)) {
+ dprintf("Source or dest mismatch.\n");
+ return false;
+ }
}
ret = ifname_compare_aligned(indev, ip6info->iniface, ip6info->iniface_mask);
diff --git a/sound/soc/msm/msm8998.c b/sound/soc/msm/msm8998.c
index 0f09c68ab344..f6a5d1344568 100644
--- a/sound/soc/msm/msm8998.c
+++ b/sound/soc/msm/msm8998.c
@@ -4203,6 +4203,13 @@ static int msm_set_pinctrl(struct msm_pinctrl_info *pinctrl_info,
ret = -EINVAL;
goto err;
}
+
+ if (pinctrl_info->pinctrl == NULL) {
+ pr_err("%s: pinctrl_info->pinctrl is NULL\n", __func__);
+ ret = -EINVAL;
+ goto err;
+ }
+
curr_state = pinctrl_info->curr_state;
pinctrl_info->curr_state = new_state;
pr_debug("%s: curr_state = %s new_state = %s\n", __func__,
@@ -4471,6 +4478,7 @@ static int msm_mi2s_snd_startup(struct snd_pcm_substream *substream)
struct snd_soc_card *card = rtd->card;
struct msm_asoc_mach_data *pdata = snd_soc_card_get_drvdata(card);
struct msm_pinctrl_info *pinctrl_info = &pdata->pinctrl_info;
+ int ret_pinctrl = 0;
dev_dbg(rtd->card->dev,
"%s: substream = %s stream = %d, dai name %s, dai ID %d\n",
@@ -4485,11 +4493,10 @@ static int msm_mi2s_snd_startup(struct snd_pcm_substream *substream)
goto done;
}
if (index == QUAT_MI2S) {
- ret = msm_set_pinctrl(pinctrl_info, STATE_MI2S_ACTIVE);
- if (ret) {
+ ret_pinctrl = msm_set_pinctrl(pinctrl_info, STATE_MI2S_ACTIVE);
+ if (ret_pinctrl) {
pr_err("%s: MI2S TLMM pinctrl set failed with %d\n",
- __func__, ret);
- goto done;
+ __func__, ret_pinctrl);
}
}
@@ -4548,6 +4555,7 @@ static void msm_mi2s_snd_shutdown(struct snd_pcm_substream *substream)
struct snd_soc_card *card = rtd->card;
struct msm_asoc_mach_data *pdata = snd_soc_card_get_drvdata(card);
struct msm_pinctrl_info *pinctrl_info = &pdata->pinctrl_info;
+ int ret_pinctrl = 0;
pr_debug("%s(): substream = %s stream = %d\n", __func__,
substream->name, substream->stream);
@@ -4568,10 +4576,10 @@ static void msm_mi2s_snd_shutdown(struct snd_pcm_substream *substream)
mutex_unlock(&mi2s_intf_conf[index].lock);
if (index == QUAT_MI2S) {
- ret = msm_set_pinctrl(pinctrl_info, STATE_DISABLE);
- if (ret)
+ ret_pinctrl = msm_set_pinctrl(pinctrl_info, STATE_DISABLE);
+ if (ret_pinctrl)
pr_err("%s: MI2S TLMM pinctrl set failed with %d\n",
- __func__, ret);
+ __func__, ret_pinctrl);
}
}
diff --git a/sound/soc/msm/qdsp6v2/q6afe.c b/sound/soc/msm/qdsp6v2/q6afe.c
index 6616edcd3347..f0a78dc8aee8 100644
--- a/sound/soc/msm/qdsp6v2/q6afe.c
+++ b/sound/soc/msm/qdsp6v2/q6afe.c
@@ -5193,7 +5193,7 @@ static int afe_sidetone(u16 tx_port_id, u16 rx_port_id, bool enable)
AFE_API_VERSION_LOOPBACK_CONFIG;
cmd_sidetone.cfg_data.dst_port_id = rx_port_id;
cmd_sidetone.cfg_data.routing_mode = LB_MODE_SIDETONE;
- cmd_sidetone.cfg_data.enable = ((enable == 1) ? sidetone_enable : 0);
+ cmd_sidetone.cfg_data.enable = enable;
pr_debug("%s rx(0x%x) tx(0x%x) enable(%d) mid(0x%x) gain(%d) sidetone_enable(%d)\n",
__func__, rx_port_id, tx_port_id,