Merge "usb: f_rmnet: Add support for BAM DMUX data path"
diff --git a/Documentation/devicetree/bindings/arm/msm/bam_dmux.txt b/Documentation/devicetree/bindings/arm/msm/bam_dmux.txt
new file mode 100644
index 0000000..ef40b72
--- /dev/null
+++ b/Documentation/devicetree/bindings/arm/msm/bam_dmux.txt
@@ -0,0 +1,28 @@
+Qualcomm Technologies, Inc. BAM Data Multiplexer Driver
+
+Required properties:
+- compatible : should be "qcom,bam_dmux"
+- reg : the location and size of the BAM hardware
+- interrupts : the BAM hardware to apps processor interrupt line
+
+Optional properties:
+-qcom,satellite-mode: the hardware needs to be configured in satellite mode
+-qcom,rx-ring-size: the size of the receive ring buffer pool, default is 32
+-qcom,max-rx-mtu: the maximum receive MTU that can be negotiated, in bytes.
+ Default is 2048. Other possible values are 4096, 8192, and 16384.
+-qcom,no-cpu-affinity: boolean value indicating that workqueue CPU affinity
+ is not required.
+-qcom,fast-shutdown: boolean value to support fast shutdown time.
+
+Example:
+
+ qcom,bam_dmux@fc834000 {
+ compatible = "qcom,bam_dmux";
+ reg = <0xfc834000 0x7000>;
+ interrupts = <0 29 1>;
+ qcom,satellite-mode;
+ qcom,rx-ring-size = <64>;
+ qcom,max-rx-mtu = <8192>;
+ qcom,no-cpu-affinity;
+ qcom,fast-shutdown;
+ };
diff --git a/Documentation/devicetree/bindings/power/supply/qcom/qpnp-smb5.txt b/Documentation/devicetree/bindings/power/supply/qcom/qpnp-smb5.txt
index 65c3cb8..8f8c87e 100644
--- a/Documentation/devicetree/bindings/power/supply/qcom/qpnp-smb5.txt
+++ b/Documentation/devicetree/bindings/power/supply/qcom/qpnp-smb5.txt
@@ -164,6 +164,32 @@
to be get from these properties defined in battery profile:
qcom,step-chg-ranges, qcom,jeita-fcc-ranges, qcom,jeita-fv-ranges.
+- qcom,flash-derating-soc
+ Usage: optional
+ Value type: <u32>
+ Definition: SOC threshold in percentage below which hardware will start
+ derating flash. This is only applicable to certain PMICs like
+ PMI632 which has SCHGM_FLASH peripheral.
+
+- qcom,flash-disable-soc
+ Usage: optional
+ Value type: <u32>
+ Definition: SOC threshold in percentage below which hardware will disable
+ flash. This is only applicable to certain PMICs like PMI632
+ which has SCHGM_FLASH peripheral.
+
+- qcom,headroom-mode
+ Usage: optional
+ Value type: <u32>
+ Definition: Specifies flash hardware headroom management policy. The
+ possible values are:
+ <0>: Fixed mode, constant 5V at flash input.
+ <1>: Adaptive mode allows charger output voltage to be
+ dynamically controlled by the flash module based on the
+ required flash headroom.
+ This is only applicable to certain PMICs like PMI632 which
+ has SCHGM_FLASH peripheral.
+
=============================================
Second Level Nodes - SMB5 Charger Peripherals
=============================================
@@ -301,4 +327,25 @@
"temperature-change",
"switcher-power-ok";
};
+
+ qcom,schgm-flash@a600 {
+ reg = <0xa600 0x100>;
+ interrupts = <0x2 0xa6 0x0 IRQ_TYPE_NONE>,
+ <0x2 0xa6 0x1 IRQ_TYPE_NONE>,
+ <0x2 0xa6 0x2 IRQ_TYPE_NONE>,
+ <0x2 0xa6 0x3 IRQ_TYPE_NONE>,
+ <0x2 0xa6 0x4 IRQ_TYPE_NONE>,
+ <0x2 0xa6 0x5 IRQ_TYPE_NONE>,
+ <0x2 0xa6 0x6 IRQ_TYPE_NONE>,
+ <0x2 0xa6 0x7 IRQ_TYPE_NONE>;
+
+ interrupt-names = "flash-en",
+ "torch-req",
+ "flash-state-change",
+ "vout-up",
+ "vout-down",
+ "ilim1-s1",
+ "ilim2-s2",
+ "vreg-ok";
+ };
};
diff --git a/Documentation/kernel-parameters.txt b/Documentation/kernel-parameters.txt
index 9f5bfd6..a226070 100644
--- a/Documentation/kernel-parameters.txt
+++ b/Documentation/kernel-parameters.txt
@@ -2821,8 +2821,6 @@
norandmaps Don't use address space randomization. Equivalent to
echo 0 > /proc/sys/kernel/randomize_va_space
- noreplace-paravirt [X86,IA-64,PV_OPS] Don't patch paravirt_ops
-
noreplace-smp [X86-32,SMP] Don't replace SMP instructions
with UP alternatives
diff --git a/Documentation/speculation.txt b/Documentation/speculation.txt
new file mode 100644
index 0000000..e9e6cba
--- /dev/null
+++ b/Documentation/speculation.txt
@@ -0,0 +1,90 @@
+This document explains potential effects of speculation, and how undesirable
+effects can be mitigated portably using common APIs.
+
+===========
+Speculation
+===========
+
+To improve performance and minimize average latencies, many contemporary CPUs
+employ speculative execution techniques such as branch prediction, performing
+work which may be discarded at a later stage.
+
+Typically speculative execution cannot be observed from architectural state,
+such as the contents of registers. However, in some cases it is possible to
+observe its impact on microarchitectural state, such as the presence or
+absence of data in caches. Such state may form side-channels which can be
+observed to extract secret information.
+
+For example, in the presence of branch prediction, it is possible for bounds
+checks to be ignored by code which is speculatively executed. Consider the
+following code:
+
+ int load_array(int *array, unsigned int index)
+ {
+ if (index >= MAX_ARRAY_ELEMS)
+ return 0;
+ else
+ return array[index];
+ }
+
+Which, on arm64, may be compiled to an assembly sequence such as:
+
+ CMP <index>, #MAX_ARRAY_ELEMS
+ B.LT less
+ MOV <returnval>, #0
+ RET
+ less:
+ LDR <returnval>, [<array>, <index>]
+ RET
+
+It is possible that a CPU mis-predicts the conditional branch, and
+speculatively loads array[index], even if index >= MAX_ARRAY_ELEMS. This
+value will subsequently be discarded, but the speculated load may affect
+microarchitectural state which can be subsequently measured.
+
+More complex sequences involving multiple dependent memory accesses may
+result in sensitive information being leaked. Consider the following
+code, building on the prior example:
+
+ int load_dependent_arrays(int *arr1, int *arr2, int index)
+ {
+ int val1, val2,
+
+ val1 = load_array(arr1, index);
+ val2 = load_array(arr2, val1);
+
+ return val2;
+ }
+
+Under speculation, the first call to load_array() may return the value
+of an out-of-bounds address, while the second call will influence
+microarchitectural state dependent on this value. This may provide an
+arbitrary read primitive.
+
+====================================
+Mitigating speculation side-channels
+====================================
+
+The kernel provides a generic API to ensure that bounds checks are
+respected even under speculation. Architectures which are affected by
+speculation-based side-channels are expected to implement these
+primitives.
+
+The array_index_nospec() helper in <linux/nospec.h> can be used to
+prevent information from being leaked via side-channels.
+
+A call to array_index_nospec(index, size) returns a sanitized index
+value that is bounded to [0, size) even under cpu speculation
+conditions.
+
+This can be used to protect the earlier load_array() example:
+
+ int load_array(int *array, unsigned int index)
+ {
+ if (index >= MAX_ARRAY_ELEMS)
+ return 0;
+ else {
+ index = array_index_nospec(index, MAX_ARRAY_ELEMS);
+ return array[index];
+ }
+ }
diff --git a/Makefile b/Makefile
index b075470..1de0efc 100644
--- a/Makefile
+++ b/Makefile
@@ -1,6 +1,6 @@
VERSION = 4
PATCHLEVEL = 9
-SUBLEVEL = 80
+SUBLEVEL = 81
EXTRAVERSION =
NAME = Roaring Lionus
diff --git a/arch/arm/boot/dts/qcom/Makefile b/arch/arm/boot/dts/qcom/Makefile
index 8f6af4c..28b2ec2 100644
--- a/arch/arm/boot/dts/qcom/Makefile
+++ b/arch/arm/boot/dts/qcom/Makefile
@@ -2,6 +2,8 @@
dtb-$(CONFIG_ARCH_SDXPOORWILLS) += sdxpoorwills-rumi.dtb \
sdxpoorwills-cdp.dtb \
sdxpoorwills-mtp.dtb \
+ sdxpoorwills-cdp-256.dtb \
+ sdxpoorwills-mtp-256.dtb \
sdxpoorwills-pcie-ep-cdp.dtb \
sdxpoorwills-pcie-ep-mtp.dtb
diff --git a/arch/arm/boot/dts/qcom/sdxpoorwills-cdp-256.dts b/arch/arm/boot/dts/qcom/sdxpoorwills-cdp-256.dts
new file mode 100644
index 0000000..04f11ce
--- /dev/null
+++ b/arch/arm/boot/dts/qcom/sdxpoorwills-cdp-256.dts
@@ -0,0 +1,22 @@
+/* Copyright (c) 2018, 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 "sdxpoorwills-cdp-256.dtsi"
+
+/ {
+ model = "Qualcomm Technologies, Inc. SDXPOORWILLS CDP (256MB)";
+ compatible = "qcom,sdxpoorwills-cdp",
+ "qcom,sdxpoorwills", "qcom,cdp";
+ qcom,board-id = <1 0x0>;
+};
diff --git a/arch/arm/boot/dts/qcom/sdxpoorwills-cdp-256.dtsi b/arch/arm/boot/dts/qcom/sdxpoorwills-cdp-256.dtsi
new file mode 100644
index 0000000..8a7b771
--- /dev/null
+++ b/arch/arm/boot/dts/qcom/sdxpoorwills-cdp-256.dtsi
@@ -0,0 +1,13 @@
+/* Copyright (c) 2018, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include "sdxpoorwills-cdp.dtsi"
diff --git a/arch/arm/boot/dts/qcom/sdxpoorwills-cdp.dts b/arch/arm/boot/dts/qcom/sdxpoorwills-cdp.dts
index 38137a2..3007f5b 100644
--- a/arch/arm/boot/dts/qcom/sdxpoorwills-cdp.dts
+++ b/arch/arm/boot/dts/qcom/sdxpoorwills-cdp.dts
@@ -18,5 +18,5 @@
model = "Qualcomm Technologies, Inc. SDXPOORWILLS CDP";
compatible = "qcom,sdxpoorwills-cdp",
"qcom,sdxpoorwills", "qcom,cdp";
- qcom,board-id = <1 0x0>, <1 0x100>, <1 0x2>, <1 0x102>;
+ qcom,board-id = <1 0x102>;
};
diff --git a/arch/arm/boot/dts/qcom/sdxpoorwills-mtp-256.dts b/arch/arm/boot/dts/qcom/sdxpoorwills-mtp-256.dts
new file mode 100644
index 0000000..2377d79c
--- /dev/null
+++ b/arch/arm/boot/dts/qcom/sdxpoorwills-mtp-256.dts
@@ -0,0 +1,22 @@
+/* Copyright (c) 2018, 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 "sdxpoorwills-mtp-256.dtsi"
+
+/ {
+ model = "Qualcomm Technologies, Inc. SDXPOORWILLS MTP (256MB)";
+ compatible = "qcom,sdxpoorwills-mtp",
+ "qcom,sdxpoorwills", "qcom,mtp";
+ qcom,board-id = <8 0x0>;
+};
diff --git a/arch/arm/boot/dts/qcom/sdxpoorwills-mtp-256.dtsi b/arch/arm/boot/dts/qcom/sdxpoorwills-mtp-256.dtsi
new file mode 100644
index 0000000..4a2ece8c
--- /dev/null
+++ b/arch/arm/boot/dts/qcom/sdxpoorwills-mtp-256.dtsi
@@ -0,0 +1,13 @@
+/* Copyright (c) 2018, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include "sdxpoorwills-mtp.dtsi"
diff --git a/arch/arm/boot/dts/qcom/sdxpoorwills-mtp.dts b/arch/arm/boot/dts/qcom/sdxpoorwills-mtp.dts
index a0bcdc9..ae3de38 100644
--- a/arch/arm/boot/dts/qcom/sdxpoorwills-mtp.dts
+++ b/arch/arm/boot/dts/qcom/sdxpoorwills-mtp.dts
@@ -18,5 +18,5 @@
model = "Qualcomm Technologies, Inc. SDXPOORWILLS MTP";
compatible = "qcom,sdxpoorwills-mtp",
"qcom,sdxpoorwills", "qcom,mtp";
- qcom,board-id = <8 0x0>, <8 0x100>, <8 0x2>, <8 0x102>;
+ qcom,board-id = <8 0x102>;
};
diff --git a/arch/arm/boot/dts/qcom/sdxpoorwills-regulator.dtsi b/arch/arm/boot/dts/qcom/sdxpoorwills-regulator.dtsi
index 37903b9..162f0d9 100644
--- a/arch/arm/boot/dts/qcom/sdxpoorwills-regulator.dtsi
+++ b/arch/arm/boot/dts/qcom/sdxpoorwills-regulator.dtsi
@@ -24,7 +24,8 @@
pmxpoorwills_s1_level: regualtor-pmxpoorwills-s1 {
regulator-name = "pmxpoorwills_s1_level";
qcom,set = <RPMH_REGULATOR_SET_ALL>;
- regulator-min-microvolt = <RPMH_REGULATOR_LEVEL_OFF>;
+ regulator-min-microvolt =
+ <RPMH_REGULATOR_LEVEL_MIN_SVS>;
regulator-max-microvolt = <RPMH_REGULATOR_LEVEL_MAX>;
};
};
@@ -53,7 +54,8 @@
pmxpoorwills_s5_level: regualtor-pmxpoorwills-s5-level {
regulator-name = "pmxpoorwills_s5_level";
qcom,set = <RPMH_REGULATOR_SET_ALL>;
- regulator-min-microvolt = <RPMH_REGULATOR_LEVEL_OFF>;
+ regulator-min-microvolt =
+ <RPMH_REGULATOR_LEVEL_LOW_SVS>;
regulator-max-microvolt = <RPMH_REGULATOR_LEVEL_MAX>;
qcom,min-dropout-voltage-level = <(-1)>;
};
@@ -61,7 +63,8 @@
pmxpoorwills_s5_level_ao: regualtor-pmxpoorwills-s5-level-ao {
regulator-name = "pmxpoorwills_s5_level_ao";
qcom,set = <RPMH_REGULATOR_SET_ACTIVE>;
- regulator-min-microvolt = <RPMH_REGULATOR_LEVEL_OFF>;
+ regulator-min-microvolt =
+ <RPMH_REGULATOR_LEVEL_LOW_SVS>;
regulator-max-microvolt = <RPMH_REGULATOR_LEVEL_MAX>;
qcom,min-dropout-voltage-level = <(-1)>;
};
diff --git a/arch/arm/boot/dts/qcom/sdxpoorwills.dtsi b/arch/arm/boot/dts/qcom/sdxpoorwills.dtsi
index c23d48b..8f59515 100644
--- a/arch/arm/boot/dts/qcom/sdxpoorwills.dtsi
+++ b/arch/arm/boot/dts/qcom/sdxpoorwills.dtsi
@@ -912,6 +912,87 @@
qcom,pet-time = <10000>;
};
+ qcom_rng: qrng@793000{
+ compatible = "qcom,msm-rng";
+ reg = <0x793000 0x1000>;
+ qcom,msm-rng-iface-clk;
+ qcom,no-qrng-config;
+ qcom,msm-bus,name = "msm-rng-noc";
+ qcom,msm-bus,num-cases = <2>;
+ qcom,msm-bus,num-paths = <1>;
+ qcom,msm-bus,vectors-KBps =
+ <1 618 0 0>, /* No vote */
+ <1 618 0 800>; /* 100 KHz */
+ clocks = <&clock_gcc GCC_PRNG_AHB_CLK>;
+ clock-names = "iface_clk";
+ };
+
+ qcom_cedev: qcedev@1de0000 {
+ compatible = "qcom,qcedev";
+ reg = <0x1de0000 0x20000>,
+ <0x1dc4000 0x24000>;
+ reg-names = "crypto-base","crypto-bam-base";
+ interrupts = <0 252 0>;
+ qcom,bam-pipe-pair = <3>;
+ qcom,ce-hw-instance = <0>;
+ qcom,ce-device = <0>;
+ qcom,bam-ee = <0>;
+ qcom,ce-hw-shared;
+ qcom,clk-mgmt-sus-res;
+ qcom,msm-bus,name = "qcedev-noc";
+ qcom,msm-bus,num-cases = <2>;
+ qcom,msm-bus,num-paths = <1>;
+ qcom,msm-bus,vectors-KBps =
+ <125 512 0 0>,
+ <125 512 393600 393600>;
+ clock-names = "core_clk_src", "core_clk",
+ "iface_clk", "bus_clk";
+ clocks = <&clock_gcc GCC_CE1_CLK>,
+ <&clock_gcc GCC_CE1_CLK>,
+ <&clock_gcc GCC_CE1_AHB_CLK>,
+ <&clock_gcc GCC_CE1_AXI_CLK>;
+ qcom,ce-opp-freq = <171430000>;
+ qcom,request-bw-before-clk;
+ iommus = <&apps_smmu 0x66 0x1>,
+ <&apps_smmu 0x76 0x1>;
+ };
+
+ qcom_crypto: qcrypto@1de0000 {
+ compatible = "qcom,qcrypto";
+ reg = <0x1de0000 0x20000>,
+ <0x1dc4000 0x24000>;
+ reg-names = "crypto-base","crypto-bam-base";
+ interrupts = <0 252 0>;
+ qcom,bam-pipe-pair = <2>;
+ qcom,ce-hw-instance = <0>;
+ qcom,ce-device = <0>;
+ qcom,bam-ee = <0>;
+ qcom,ce-hw-shared;
+ qcom,clk-mgmt-sus-res;
+ qcom,msm-bus,name = "qcrypto-noc";
+ qcom,msm-bus,num-cases = <2>;
+ qcom,msm-bus,num-paths = <1>;
+ qcom,msm-bus,vectors-KBps =
+ <125 512 0 0>,
+ <125 512 393600 393600>;
+ clock-names = "core_clk_src", "core_clk",
+ "iface_clk", "bus_clk";
+ clocks = <&clock_gcc GCC_CE1_CLK>,
+ <&clock_gcc GCC_CE1_CLK>,
+ <&clock_gcc GCC_CE1_AHB_CLK>,
+ <&clock_gcc GCC_CE1_AXI_CLK>;
+ qcom,ce-opp-freq = <171430000>;
+ qcom,request-bw-before-clk;
+ qcom,use-sw-aes-cbc-ecb-ctr-algo;
+ qcom,use-sw-aes-xts-algo;
+ qcom,use-sw-aes-ccm-algo;
+ qcom,use-sw-aead-algo;
+ qcom,use-sw-ahash-algo;
+ qcom,use-sw-hmac-algo;
+ iommus = <&apps_smmu 0x64 0x1>,
+ <&apps_smmu 0x74 0x1>;
+ };
+
qcom,msm-rtb {
compatible = "qcom,msm-rtb";
qcom,rtb-size = <0x100000>;
diff --git a/arch/arm/configs/msm8953-perf_defconfig b/arch/arm/configs/msm8953-perf_defconfig
index d539904..7f4120f 100644
--- a/arch/arm/configs/msm8953-perf_defconfig
+++ b/arch/arm/configs/msm8953-perf_defconfig
@@ -288,8 +288,6 @@
# CONFIG_SERIO_SERPORT is not set
# CONFIG_VT is not set
# CONFIG_LEGACY_PTYS is not set
-CONFIG_SERIAL_MSM=y
-CONFIG_SERIAL_MSM_CONSOLE=y
CONFIG_HW_RANDOM=y
CONFIG_HW_RANDOM_MSM_LEGACY=y
CONFIG_MSM_ADSPRPC=y
@@ -366,6 +364,7 @@
CONFIG_USB_MON=y
CONFIG_USB_XHCI_HCD=y
CONFIG_USB_EHCI_HCD=y
+CONFIG_USB_EHCI_MSM=y
CONFIG_USB_EHCI_HCD_PLATFORM=y
CONFIG_USB_OHCI_HCD=y
CONFIG_USB_OHCI_HCD_PLATFORM=y
@@ -395,10 +394,12 @@
CONFIG_USB_GADGET_DEBUG_FILES=y
CONFIG_USB_GADGET_DEBUG_FS=y
CONFIG_USB_GADGET_VBUS_DRAW=500
+CONFIG_USB_CI13XXX_MSM=y
CONFIG_USB_CONFIGFS=y
CONFIG_USB_CONFIGFS_SERIAL=y
CONFIG_USB_CONFIGFS_NCM=y
CONFIG_USB_CONFIGFS_QCRNDIS=y
+CONFIG_USB_CONFIGFS_RNDIS=y
CONFIG_USB_CONFIGFS_RMNET_BAM=y
CONFIG_USB_CONFIGFS_MASS_STORAGE=y
CONFIG_USB_CONFIGFS_F_FS=y
@@ -427,6 +428,7 @@
CONFIG_LEDS_CLASS=y
CONFIG_LEDS_QPNP=y
CONFIG_LEDS_QPNP_FLASH=y
+CONFIG_LEDS_QPNP_FLASH_V2=y
CONFIG_LEDS_QPNP_WLED=y
CONFIG_LEDS_QPNP_HAPTICS=y
CONFIG_LEDS_QPNP_VIBRATOR_LDO=y
@@ -487,6 +489,7 @@
CONFIG_QCOM_DEVFREQ_DEVBW=y
CONFIG_PWM=y
CONFIG_PWM_QPNP=y
+CONFIG_QTI_MPM=y
CONFIG_ANDROID=y
CONFIG_ANDROID_BINDER_IPC=y
CONFIG_SENSORS_SSC=y
diff --git a/arch/arm/configs/msm8953_defconfig b/arch/arm/configs/msm8953_defconfig
index 288856d..2f76154 100644
--- a/arch/arm/configs/msm8953_defconfig
+++ b/arch/arm/configs/msm8953_defconfig
@@ -377,6 +377,7 @@
CONFIG_USB_MON=y
CONFIG_USB_XHCI_HCD=y
CONFIG_USB_EHCI_HCD=y
+CONFIG_USB_EHCI_MSM=y
CONFIG_USB_EHCI_HCD_PLATFORM=y
CONFIG_USB_OHCI_HCD=y
CONFIG_USB_OHCI_HCD_PLATFORM=y
@@ -406,10 +407,12 @@
CONFIG_USB_GADGET_DEBUG_FILES=y
CONFIG_USB_GADGET_DEBUG_FS=y
CONFIG_USB_GADGET_VBUS_DRAW=500
+CONFIG_USB_CI13XXX_MSM=y
CONFIG_USB_CONFIGFS=y
CONFIG_USB_CONFIGFS_SERIAL=y
CONFIG_USB_CONFIGFS_NCM=y
CONFIG_USB_CONFIGFS_QCRNDIS=y
+CONFIG_USB_CONFIGFS_RNDIS=y
CONFIG_USB_CONFIGFS_RMNET_BAM=y
CONFIG_USB_CONFIGFS_MASS_STORAGE=y
CONFIG_USB_CONFIGFS_F_FS=y
@@ -439,6 +442,7 @@
CONFIG_LEDS_CLASS=y
CONFIG_LEDS_QPNP=y
CONFIG_LEDS_QPNP_FLASH=y
+CONFIG_LEDS_QPNP_FLASH_V2=y
CONFIG_LEDS_QPNP_WLED=y
CONFIG_LEDS_QPNP_HAPTICS=y
CONFIG_LEDS_QPNP_VIBRATOR_LDO=y
@@ -504,6 +508,7 @@
CONFIG_QCOM_DEVFREQ_DEVBW=y
CONFIG_PWM=y
CONFIG_PWM_QPNP=y
+CONFIG_QTI_MPM=y
CONFIG_ANDROID=y
CONFIG_ANDROID_BINDER_IPC=y
CONFIG_SENSORS_SSC=y
diff --git a/arch/arm/configs/sdxpoorwills-perf_defconfig b/arch/arm/configs/sdxpoorwills-perf_defconfig
index 02b4c58..1f04446 100644
--- a/arch/arm/configs/sdxpoorwills-perf_defconfig
+++ b/arch/arm/configs/sdxpoorwills-perf_defconfig
@@ -213,6 +213,7 @@
CONFIG_SERIAL_MSM_HS=y
CONFIG_DIAG_CHAR=y
CONFIG_HW_RANDOM=y
+CONFIG_HW_RANDOM_MSM_LEGACY=y
CONFIG_I2C=y
CONFIG_I2C_CHARDEV=y
CONFIG_I2C_MSM_V2=y
@@ -357,6 +358,7 @@
CONFIG_QCOM_COMMAND_DB=y
CONFIG_MSM_PM=y
CONFIG_QTI_RPM_STATS_LOG=y
+CONFIG_QCOM_FORCE_WDOG_BITE_ON_PANIC=y
CONFIG_IIO=y
CONFIG_PWM=y
CONFIG_PWM_QPNP=y
@@ -389,4 +391,7 @@
CONFIG_CORESIGHT_CTI=y
CONFIG_CORESIGHT_EVENT=y
CONFIG_CORESIGHT_HWEVENT=y
+CONFIG_CRYPTO_DEV_QCOM_MSM_QCE=y
+CONFIG_CRYPTO_DEV_QCRYPTO=y
+CONFIG_CRYPTO_DEV_QCEDEV=y
CONFIG_QMI_ENCDEC=y
diff --git a/arch/arm/configs/sdxpoorwills_defconfig b/arch/arm/configs/sdxpoorwills_defconfig
index 855f606..0b5a266 100644
--- a/arch/arm/configs/sdxpoorwills_defconfig
+++ b/arch/arm/configs/sdxpoorwills_defconfig
@@ -207,6 +207,7 @@
CONFIG_SERIAL_MSM_HS=y
CONFIG_DIAG_CHAR=y
CONFIG_HW_RANDOM=y
+CONFIG_HW_RANDOM_MSM_LEGACY=y
CONFIG_I2C=y
CONFIG_I2C_CHARDEV=y
CONFIG_I2C_MSM_V2=y
@@ -356,6 +357,7 @@
CONFIG_QCOM_COMMAND_DB=y
CONFIG_MSM_PM=y
CONFIG_QTI_RPM_STATS_LOG=y
+CONFIG_QCOM_FORCE_WDOG_BITE_ON_PANIC=y
CONFIG_QCOM_DEVFREQ_DEVBW=y
CONFIG_IIO=y
CONFIG_PWM=y
@@ -407,8 +409,10 @@
CONFIG_CORESIGHT_TGU=y
CONFIG_CORESIGHT_HWEVENT=y
CONFIG_CORESIGHT_DUMMY=y
-CONFIG_CRYPTO_ECB=y
CONFIG_CRYPTO_CMAC=y
CONFIG_CRYPTO_SHA256=y
+CONFIG_CRYPTO_DEV_QCOM_MSM_QCE=y
+CONFIG_CRYPTO_DEV_QCRYPTO=y
+CONFIG_CRYPTO_DEV_QCEDEV=y
CONFIG_XZ_DEC=y
CONFIG_QMI_ENCDEC=y
diff --git a/arch/arm/mach-qcom/board-msm8953.c b/arch/arm/mach-qcom/board-msm8953.c
index cae3bf7..04b0bcc 100644
--- a/arch/arm/mach-qcom/board-msm8953.c
+++ b/arch/arm/mach-qcom/board-msm8953.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2017-2018, 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
@@ -17,6 +17,7 @@
static const char *msm8953_dt_match[] __initconst = {
"qcom,msm8953",
+ "qcom,apq8053",
NULL
};
diff --git a/arch/arm64/boot/dts/qcom/dsi-panel-nt36850-truly-dualmipi-wqhd-cmd.dtsi b/arch/arm64/boot/dts/qcom/dsi-panel-nt36850-truly-dualmipi-wqhd-cmd.dtsi
index 1b38d06..ca28261 100644
--- a/arch/arm64/boot/dts/qcom/dsi-panel-nt36850-truly-dualmipi-wqhd-cmd.dtsi
+++ b/arch/arm64/boot/dts/qcom/dsi-panel-nt36850-truly-dualmipi-wqhd-cmd.dtsi
@@ -27,7 +27,6 @@
qcom,mdss-dsi-bllp-eof-power-mode;
qcom,mdss-dsi-bllp-power-mode;
qcom,mdss-dsi-tx-eot-append;
- qcom,cmd-sync-wait-broadcast;
qcom,mdss-dsi-lane-0-state;
qcom,mdss-dsi-lane-1-state;
qcom,mdss-dsi-lane-2-state;
diff --git a/arch/arm64/boot/dts/qcom/msm8937-bus.dtsi b/arch/arm64/boot/dts/qcom/msm8937-bus.dtsi
new file mode 100644
index 0000000..89bfc73
--- /dev/null
+++ b/arch/arm64/boot/dts/qcom/msm8937-bus.dtsi
@@ -0,0 +1,986 @@
+/* Copyright (c) 2015-2016, 2018, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <dt-bindings/msm/msm-bus-ids.h>
+
+&soc {
+ /*Version = 11 */
+ ad_hoc_bus: ad-hoc-bus@580000 {
+ compatible = "qcom,msm-bus-device";
+ reg = <0x580000 0x16080>,
+ <0x580000 0x16080>,
+ <0x400000 0x5A000>,
+ <0x500000 0x13080>;
+ reg-names = "snoc-base", "snoc-mm-base",
+ "bimc-base", "pcnoc-base";
+
+ /*Buses*/
+ fab_bimc: fab-bimc {
+ cell-id = <MSM_BUS_FAB_BIMC>;
+ label = "fab-bimc";
+ qcom,fab-dev;
+ qcom,base-name = "bimc-base";
+ qcom,bus-type = <2>;
+ qcom,util-fact = <154>;
+ clock-names = "bus_clk", "bus_a_clk";
+ clocks = <&clock_gcc clk_bimc_msmbus_clk>,
+ <&clock_gcc clk_bimc_msmbus_a_clk>;
+ };
+
+ fab_pcnoc: fab-pcnoc {
+ cell-id = <MSM_BUS_FAB_PERIPH_NOC>;
+ label = "fab-pcnoc";
+ qcom,fab-dev;
+ qcom,base-name = "pcnoc-base";
+ qcom,base-offset = <0x7000>;
+ qcom,qos-off = <0x1000>;
+ qcom,bus-type = <1>;
+ clock-names = "bus_clk", "bus_a_clk";
+ clocks = <&clock_gcc clk_pnoc_msmbus_clk>,
+ <&clock_gcc clk_pnoc_msmbus_a_clk>;
+ };
+
+ fab_snoc: fab-snoc {
+ cell-id = <MSM_BUS_FAB_SYS_NOC>;
+ label = "fab-snoc";
+ qcom,fab-dev;
+ qcom,base-name = "snoc-base";
+ qcom,base-offset = <0x7000>;
+ qcom,qos-off = <0x1000>;
+ qcom,bus-type = <1>;
+ clock-names = "bus_clk", "bus_a_clk";
+ clocks = <&clock_gcc clk_snoc_msmbus_clk>,
+ <&clock_gcc clk_snoc_msmbus_a_clk>;
+ };
+
+ fab_snoc_mm: fab-snoc-mm {
+ cell-id = <MSM_BUS_FAB_MMSS_NOC>;
+ label = "fab-snoc-mm";
+ qcom,fab-dev;
+ qcom,base-name = "snoc-mm-base";
+ qcom,base-offset = <0x7000>;
+ qcom,qos-off = <0x1000>;
+ qcom,bus-type = <1>;
+ qcom,util-fact = <154>;
+ clock-names = "bus_clk", "bus_a_clk";
+ clocks = <&clock_gcc clk_sysmmnoc_msmbus_clk>,
+ <&clock_gcc clk_sysmmnoc_msmbus_a_clk>;
+ };
+
+ /*BIMC Masters*/
+ mas_apps_proc: mas-apps-proc {
+ cell-id = <MSM_BUS_MASTER_AMPSS_M0>;
+ label = "mas-apps-proc";
+ qcom,buswidth = <8>;
+ qcom,agg-ports = <1>;
+ qcom,ap-owned;
+ qcom,qport = <0>;
+ qcom,qos-mode = "fixed";
+ qcom,connections = <&slv_ebi &slv_bimc_snoc>;
+ qcom,prio-lvl = <0>;
+ qcom,prio-rd = <0>;
+ qcom,prio-wr = <0>;
+ qcom,bus-dev = <&fab_bimc>;
+ qcom,mas-rpm-id = <ICBID_MASTER_APPSS_PROC>;
+ };
+
+ mas_oxili: mas-oxili {
+ cell-id = <MSM_BUS_MASTER_GRAPHICS_3D>;
+ label = "mas-oxili";
+ qcom,buswidth = <8>;
+ qcom,agg-ports = <1>;
+ qcom,ap-owned;
+ qcom,qport = <2>;
+ qcom,qos-mode = "fixed";
+ qcom,connections = <&slv_ebi &slv_bimc_snoc>;
+ qcom,prio-lvl = <0>;
+ qcom,prio-rd = <0>;
+ qcom,prio-wr = <0>;
+ qcom,bus-dev = <&fab_bimc>;
+ qcom,mas-rpm-id = <ICBID_MASTER_GFX3D>;
+ };
+
+ mas_snoc_bimc_0: mas-snoc-bimc-0 {
+ cell-id = <MSM_BUS_SNOC_BIMC_0_MAS>;
+ label = "mas-snoc-bimc-0";
+ qcom,buswidth = <8>;
+ qcom,agg-ports = <1>;
+ qcom,ap-owned;
+ qcom,qport = <3>;
+ qcom,qos-mode = "bypass";
+ qcom,connections = <&slv_ebi &slv_bimc_snoc>;
+ qcom,bus-dev = <&fab_bimc>;
+ qcom,mas-rpm-id = <ICBID_MASTER_SNOC_BIMC_0>;
+ };
+
+ mas_snoc_bimc_2: mas-snoc-bimc-2 {
+ cell-id = <MSM_BUS_SNOC_BIMC_2_MAS>;
+ label = "mas-snoc-bimc-2";
+ qcom,buswidth = <8>;
+ qcom,agg-ports = <1>;
+ qcom,ap-owned;
+ qcom,qport = <4>;
+ qcom,qos-mode = "bypass";
+ qcom,connections = <&slv_ebi &slv_bimc_snoc>;
+ qcom,bus-dev = <&fab_bimc>;
+ qcom,mas-rpm-id = <ICBID_MASTER_SNOC_BIMC_2>;
+ };
+
+ mas_snoc_bimc_1: mas-snoc-bimc-1 {
+ cell-id = <MSM_BUS_SNOC_BIMC_1_MAS>;
+ label = "mas-snoc-bimc-1";
+ qcom,buswidth = <8>;
+ qcom,agg-ports = <1>;
+ qcom,qport = <5>;
+ qcom,qos-mode = "bypass";
+ qcom,connections = <&slv_ebi>;
+ qcom,bus-dev = <&fab_bimc>;
+ qcom,mas-rpm-id = <ICBID_MASTER_SNOC_BIMC_1>;
+ };
+
+ mas_tcu_0: mas-tcu-0 {
+ cell-id = <MSM_BUS_MASTER_TCU_0>;
+ label = "mas-tcu-0";
+ qcom,buswidth = <8>;
+ qcom,agg-ports = <1>;
+ qcom,ap-owned;
+ qcom,qport = <6>;
+ qcom,qos-mode = "fixed";
+ qcom,connections = <&slv_ebi &slv_bimc_snoc>;
+ qcom,prio-lvl = <2>;
+ qcom,prio-rd = <2>;
+ qcom,bus-dev = <&fab_bimc>;
+ qcom,mas-rpm-id = <ICBID_MASTER_TCU_0>;
+ };
+
+ /*PCNOC Masters*/
+ mas_spdm: mas-spdm {
+ cell-id = <MSM_BUS_MASTER_SPDM>;
+ label = "mas-spdm";
+ qcom,buswidth = <4>;
+ qcom,agg-ports = <1>;
+ qcom,ap-owned;
+ qcom,connections = <&pcnoc_m_0>;
+ qcom,bus-dev = <&fab_pcnoc>;
+ qcom,mas-rpm-id = <ICBID_MASTER_SPDM>;
+ };
+
+ mas_blsp_1: mas-blsp-1 {
+ cell-id = <MSM_BUS_MASTER_BLSP_1>;
+ label = "mas-blsp-1";
+ qcom,buswidth = <4>;
+ qcom,agg-ports = <1>;
+ qcom,connections = <&pcnoc_m_1>;
+ qcom,bus-dev = <&fab_pcnoc>;
+ qcom,mas-rpm-id = <ICBID_MASTER_BLSP_1>;
+ };
+
+ mas_blsp_2: mas-blsp-2 {
+ cell-id = <MSM_BUS_MASTER_BLSP_2>;
+ label = "mas-blsp-2";
+ qcom,buswidth = <4>;
+ qcom,agg-ports = <1>;
+ qcom,connections = <&pcnoc_m_1>;
+ qcom,bus-dev = <&fab_pcnoc>;
+ qcom,mas-rpm-id = <ICBID_MASTER_BLSP_2>;
+ };
+
+ mas_usb_hs1: mas-usb-hs1 {
+ cell-id = <MSM_BUS_MASTER_USB_HS>;
+ label = "mas-usb-hs1";
+ qcom,buswidth = <4>;
+ qcom,agg-ports = <1>;
+ qcom,ap-owned;
+ qcom,qport = <12>;
+ qcom,qos-mode = "fixed";
+ qcom,prio1 = <1>;
+ qcom,prio0 = <1>;
+ qcom,connections = <&pcnoc_int_0>;
+ qcom,bus-dev = <&fab_pcnoc>;
+ qcom,mas-rpm-id = <ICBID_MASTER_USB_HS1>;
+ };
+
+ mas_xi_usb_hs1: mas-xi-usb-hs1 {
+ cell-id = <MSM_BUS_MASTER_XM_USB_HS1>;
+ label = "mas-xi-usb-hs1";
+ qcom,buswidth = <8>;
+ qcom,agg-ports = <1>;
+ qcom,qport = <11>;
+ qcom,qos-mode = "fixed";
+ qcom,connections = <&pcnoc_int_0>;
+ qcom,bus-dev = <&fab_pcnoc>;
+ qcom,mas-rpm-id = <ICBID_MASTER_XI_USB_HS1>;
+ };
+
+ mas_crypto: mas-crypto {
+ cell-id = <MSM_BUS_MASTER_CRYPTO_CORE0>;
+ label = "mas-crypto";
+ qcom,buswidth = <8>;
+ qcom,agg-ports = <1>;
+ qcom,ap-owned;
+ qcom,qport = <0>;
+ qcom,qos-mode = "fixed";
+ qcom,connections = <&pcnoc_int_0>;
+ qcom,prio1 = <1>;
+ qcom,prio0 = <1>;
+ qcom,bus-dev = <&fab_pcnoc>;
+ qcom,mas-rpm-id = <ICBID_MASTER_CRYPTO>;
+ };
+
+ mas_sdcc_1: mas-sdcc-1 {
+ cell-id = <MSM_BUS_MASTER_SDCC_1>;
+ label = "mas-sdcc-1";
+ qcom,buswidth = <8>;
+ qcom,agg-ports = <1>;
+ qcom,qport = <7>;
+ qcom,qos-mode = "fixed";
+ qcom,connections = <&pcnoc_int_0>;
+ qcom,bus-dev = <&fab_pcnoc>;
+ qcom,mas-rpm-id = <ICBID_MASTER_SDCC_1>;
+ };
+
+ mas_sdcc_2: mas-sdcc-2 {
+ cell-id = <MSM_BUS_MASTER_SDCC_2>;
+ label = "mas-sdcc-2";
+ qcom,buswidth = <8>;
+ qcom,agg-ports = <1>;
+ qcom,qport = <8>;
+ qcom,qos-mode = "fixed";
+ qcom,connections = <&pcnoc_int_0>;
+ qcom,bus-dev = <&fab_pcnoc>;
+ qcom,mas-rpm-id = <ICBID_MASTER_SDCC_2>;
+ };
+
+ mas_snoc_pcnoc: mas-snoc-pcnoc {
+ cell-id = <MSM_BUS_SNOC_PNOC_MAS>;
+ label = "mas-snoc-pcnoc";
+ qcom,buswidth = <8>;
+ qcom,agg-ports = <1>;
+ qcom,qport = <9>;
+ qcom,qos-mode = "fixed";
+ qcom,connections = <&pcnoc_s_7
+ &pcnoc_int_2 &pcnoc_int_3>;
+ qcom,bus-dev = <&fab_pcnoc>;
+ qcom,mas-rpm-id = <ICBID_MASTER_SNOC_PCNOC>;
+ };
+
+ /*SNOC Masters*/
+ mas_qdss_bam: mas-qdss-bam {
+ cell-id = <MSM_BUS_MASTER_QDSS_BAM>;
+ label = "mas-qdss-bam";
+ qcom,buswidth = <4>;
+ qcom,agg-ports = <1>;
+ qcom,ap-owned;
+ qcom,qport = <11>;
+ qcom,qos-mode = "fixed";
+ qcom,connections = <&qdss_int>;
+ qcom,prio1 = <1>;
+ qcom,prio0 = <1>;
+ qcom,bus-dev = <&fab_snoc>;
+ qcom,mas-rpm-id = <ICBID_MASTER_QDSS_BAM>;
+ };
+
+ mas_bimc_snoc: mas-bimc-snoc {
+ cell-id = <MSM_BUS_BIMC_SNOC_MAS>;
+ label = "mas-bimc-snoc";
+ qcom,buswidth = <8>;
+ qcom,agg-ports = <1>;
+ qcom,connections = <&snoc_int_0
+ &snoc_int_1 &snoc_int_2>;
+ qcom,bus-dev = <&fab_snoc>;
+ qcom,mas-rpm-id = <ICBID_MASTER_BIMC_SNOC>;
+ };
+
+ mas_jpeg: mas-jpeg {
+ cell-id = <MSM_BUS_MASTER_JPEG>;
+ label = "mas-jpeg";
+ qcom,buswidth = <16>;
+ qcom,agg-ports = <1>;
+ qcom,ap-owned;
+ qcom,qport = <6>;
+ qcom,qos-mode = "bypass";
+ qcom,connections = <&slv_snoc_bimc_2>;
+ qcom,bus-dev = <&fab_snoc_mm>;
+ qcom,mas-rpm-id = <ICBID_MASTER_JPEG>;
+ };
+
+ mas_mdp: mas-mdp {
+ cell-id = <MSM_BUS_MASTER_MDP_PORT0>;
+ label = "mas-mdp";
+ qcom,buswidth = <16>;
+ qcom,agg-ports = <1>;
+ qcom,ap-owned;
+ qcom,qport = <7>;
+ qcom,qos-mode = "bypass";
+ qcom,connections = <&slv_snoc_bimc_0>;
+ qcom,bus-dev = <&fab_snoc_mm>;
+ qcom,mas-rpm-id = <ICBID_MASTER_MDP>;
+ };
+
+ mas_pcnoc_snoc: mas-pcnoc-snoc {
+ cell-id = <MSM_BUS_PNOC_SNOC_MAS>;
+ label = "mas-pcnoc-snoc";
+ qcom,buswidth = <8>;
+ qcom,agg-ports = <1>;
+ qcom,qport = <5>;
+ qcom,qos-mode = "fixed";
+ qcom,connections = <&snoc_int_0
+ &snoc_int_1 &slv_snoc_bimc_1>;
+ qcom,bus-dev = <&fab_snoc>;
+ qcom,mas-rpm-id = <ICBID_MASTER_PNOC_SNOC>;
+ qcom,blacklist = <&slv_snoc_pcnoc>;
+ };
+
+ mas_venus: mas-venus {
+ cell-id = <MSM_BUS_MASTER_VIDEO_P0>;
+ label = "mas-venus";
+ qcom,buswidth = <16>;
+ qcom,agg-ports = <1>;
+ qcom,ap-owned;
+ qcom,qport = <8>;
+ qcom,qos-mode = "bypass";
+ qcom,connections = <&slv_snoc_bimc_2>;
+ qcom,bus-dev = <&fab_snoc_mm>;
+ qcom,mas-rpm-id = <ICBID_MASTER_VIDEO>;
+ };
+
+ mas_vfe0: mas-vfe0 {
+ cell-id = <MSM_BUS_MASTER_VFE>;
+ label = "mas-vfe0";
+ qcom,buswidth = <16>;
+ qcom,agg-ports = <1>;
+ qcom,ap-owned;
+ qcom,qport = <9>;
+ qcom,qos-mode = "bypass";
+ qcom,connections = <&slv_snoc_bimc_0>;
+ qcom,bus-dev = <&fab_snoc_mm>;
+ qcom,mas-rpm-id = <ICBID_MASTER_VFE>;
+ };
+
+ mas_vfe1: mas-vfe1 {
+ cell-id = <MSM_BUS_MASTER_VFE1>;
+ label = "mas-vfe1";
+ qcom,buswidth = <16>;
+ qcom,agg-ports = <1>;
+ qcom,ap-owned;
+ qcom,qport = <13>;
+ qcom,qos-mode = "bypass";
+ qcom,connections = <&slv_snoc_bimc_0>;
+ qcom,bus-dev = <&fab_snoc_mm>;
+ qcom,mas-rpm-id = <ICBID_MASTER_VFE1>;
+ };
+
+ mas_cpp: mas-cpp {
+ cell-id = <MSM_BUS_MASTER_CPP>;
+ label = "mas-cpp";
+ qcom,buswidth = <16>;
+ qcom,agg-ports = <1>;
+ qcom,ap-owned;
+ qcom,qport = <12>;
+ qcom,qos-mode = "bypass";
+ qcom,connections = <&slv_snoc_bimc_2>;
+ qcom,bus-dev = <&fab_snoc_mm>;
+ qcom,mas-rpm-id = <ICBID_MASTER_CPP>;
+ };
+
+ mas_qdss_etr: mas-qdss-etr {
+ cell-id = <MSM_BUS_MASTER_QDSS_ETR>;
+ label = "mas-qdss-etr";
+ qcom,buswidth = <8>;
+ qcom,agg-ports = <1>;
+ qcom,ap-owned;
+ qcom,qport = <10>;
+ qcom,qos-mode = "fixed";
+ qcom,connections = <&qdss_int>;
+ qcom,prio1 = <1>;
+ qcom,prio0 = <1>;
+ qcom,bus-dev = <&fab_snoc>;
+ qcom,mas-rpm-id = <ICBID_MASTER_QDSS_ETR>;
+ };
+
+ /*Internal nodes*/
+ pcnoc_m_0: pcnoc-m-0 {
+ cell-id = <MSM_BUS_PNOC_M_0>;
+ label = "pcnoc-m-0";
+ qcom,buswidth = <4>;
+ qcom,agg-ports = <1>;
+ qcom,ap-owned;
+ qcom,qport = <5>;
+ qcom,qos-mode = "fixed";
+ qcom,connections = <&pcnoc_int_0>;
+ qcom,prio1 = <1>;
+ qcom,prio0 = <1>;
+ qcom,bus-dev = <&fab_pcnoc>;
+ qcom,mas-rpm-id = <ICBID_MASTER_PCNOC_M_0>;
+ qcom,slv-rpm-id = <ICBID_SLAVE_PCNOC_M_0>;
+ };
+
+ pcnoc_m_1: pcnoc-m-1 {
+ cell-id = <MSM_BUS_PNOC_M_1>;
+ label = "pcnoc-m-1";
+ qcom,buswidth = <4>;
+ qcom,agg-ports = <1>;
+ qcom,connections = <&pcnoc_int_0>;
+ qcom,bus-dev = <&fab_pcnoc>;
+ qcom,mas-rpm-id = <ICBID_MASTER_PCNOC_M_1>;
+ qcom,slv-rpm-id = <ICBID_SLAVE_PCNOC_M_1>;
+ };
+
+ pcnoc_int_0: pcnoc-int-0 {
+ cell-id = <MSM_BUS_PNOC_INT_0>;
+ label = "pcnoc-int-0";
+ qcom,buswidth = <8>;
+ qcom,agg-ports = <1>;
+ qcom,connections = <&slv_pcnoc_snoc
+ &pcnoc_s_7 &pcnoc_int_3 &pcnoc_int_2>;
+ qcom,bus-dev = <&fab_pcnoc>;
+ qcom,mas-rpm-id = <ICBID_MASTER_PCNOC_INT_0>;
+ qcom,slv-rpm-id = <ICBID_SLAVE_PCNOC_INT_0>;
+ };
+
+ pcnoc_int_1: pcnoc-int-1 {
+ cell-id = <MSM_BUS_PNOC_INT_1>;
+ label = "pcnoc-int-1";
+ qcom,buswidth = <8>;
+ qcom,agg-ports = <1>;
+ qcom,connections = <&slv_pcnoc_snoc
+ &pcnoc_s_7 &pcnoc_int_3 &pcnoc_int_2>;
+ qcom,bus-dev = <&fab_pcnoc>;
+ qcom,mas-rpm-id = <ICBID_MASTER_PCNOC_INT_1>;
+ qcom,slv-rpm-id = <ICBID_SLAVE_PCNOC_INT_1>;
+ };
+
+ pcnoc_int_2: pcnoc-int-2 {
+ cell-id = <MSM_BUS_PNOC_INT_2>;
+ label = "pcnoc-int-2";
+ qcom,buswidth = <8>;
+ qcom,agg-ports = <1>;
+ qcom,connections = <&pcnoc_s_2
+ &pcnoc_s_3 &pcnoc_s_6 &pcnoc_s_8>;
+ qcom,bus-dev = <&fab_pcnoc>;
+ qcom,mas-rpm-id = <ICBID_MASTER_PCNOC_INT_2>;
+ qcom,slv-rpm-id = <ICBID_SLAVE_PCNOC_INT_2>;
+ };
+
+ pcnoc_int_3: pcnoc-int-3 {
+ cell-id = <MSM_BUS_PNOC_INT_3>;
+ label = "pcnoc-int-3";
+ qcom,buswidth = <8>;
+ qcom,agg-ports = <1>;
+ qcom,connections = < &pcnoc_s_1 &pcnoc_s_0 &pcnoc_s_4
+ &slv_gpu_cfg &slv_tcu >;
+ qcom,bus-dev = <&fab_pcnoc>;
+ qcom,mas-rpm-id = <ICBID_MASTER_PCNOC_INT_3>;
+ qcom,slv-rpm-id = <ICBID_SLAVE_PCNOC_INT_3>;
+ };
+
+ pcnoc_s_0: pcnoc-s-0 {
+ cell-id = <MSM_BUS_PNOC_SLV_0>;
+ label = "pcnoc-s-0";
+ qcom,buswidth = <4>;
+ qcom,agg-ports = <1>;
+ qcom,connections = <&slv_spdm &slv_pdm &slv_prng
+ &slv_sdcc_2>;
+ qcom,bus-dev = <&fab_pcnoc>;
+ qcom,mas-rpm-id = <ICBID_MASTER_PCNOC_S_0>;
+ qcom,slv-rpm-id = <ICBID_SLAVE_PCNOC_S_0>;
+ };
+
+ pcnoc_s_1: pcnoc-s-1 {
+ cell-id = <MSM_BUS_PNOC_SLV_1>;
+ label = "pcnoc-s-1";
+ qcom,buswidth = <4>;
+ qcom,agg-ports = <1>;
+ qcom,connections = <&slv_tcsr>;
+ qcom,bus-dev = <&fab_pcnoc>;
+ qcom,mas-rpm-id = <ICBID_MASTER_PCNOC_S_1>;
+ qcom,slv-rpm-id = <ICBID_SLAVE_PCNOC_S_1>;
+ };
+
+ pcnoc_s_2: pcnoc-s-2 {
+ cell-id = <MSM_BUS_PNOC_SLV_2>;
+ label = "pcnoc-s-2";
+ qcom,buswidth = <4>;
+ qcom,agg-ports = <1>;
+ qcom,connections = <&slv_snoc_cfg>;
+ qcom,bus-dev = <&fab_pcnoc>;
+ qcom,mas-rpm-id = <ICBID_MASTER_PCNOC_S_2>;
+ qcom,slv-rpm-id = <ICBID_SLAVE_PCNOC_S_2>;
+ };
+
+ pcnoc_s_3: pcnoc-s-3 {
+ cell-id = <MSM_BUS_PNOC_SLV_3>;
+ label = "pcnoc-s-3";
+ qcom,buswidth = <4>;
+ qcom,agg-ports = <1>;
+ qcom,connections = <&slv_message_ram>;
+ qcom,bus-dev = <&fab_pcnoc>;
+ qcom,mas-rpm-id = <ICBID_MASTER_PCNOC_S_3>;
+ qcom,slv-rpm-id = <ICBID_SLAVE_PCNOC_S_3>;
+ };
+
+ pcnoc_s_4: pcnoc-s-4 {
+ cell-id = <MSM_BUS_PNOC_SLV_4>;
+ label = "pcnoc-s-4";
+ qcom,buswidth = <4>;
+ qcom,agg-ports = <1>;
+ qcom,ap-owned;
+ qcom,connections = <&slv_camera_ss_cfg
+ &slv_disp_ss_cfg &slv_venus_cfg>;
+ qcom,bus-dev = <&fab_pcnoc>;
+ qcom,mas-rpm-id = <ICBID_MASTER_PCNOC_S_4>;
+ qcom,slv-rpm-id = <ICBID_SLAVE_PCNOC_S_4>;
+ };
+
+ pcnoc_s_6: pcnoc-s-6 {
+ cell-id = <MSM_BUS_PNOC_SLV_6>;
+ label = "pcnoc-s-6";
+ qcom,buswidth = <4>;
+ qcom,agg-ports = <1>;
+ qcom,connections = <&slv_tlmm &slv_blsp_1 &slv_blsp_2>;
+ qcom,bus-dev = <&fab_pcnoc>;
+ qcom,mas-rpm-id = <ICBID_MASTER_PCNOC_S_6>;
+ qcom,slv-rpm-id = <ICBID_SLAVE_PCNOC_S_6>;
+ };
+
+ pcnoc_s_7: pcnoc-s-7 {
+ cell-id = <MSM_BUS_PNOC_SLV_7>;
+ label = "pcnoc-s-7";
+ qcom,buswidth = <4>;
+ qcom,agg-ports = <1>;
+ qcom,connections = < &slv_sdcc_1 &slv_pmic_arb>;
+ qcom,bus-dev = <&fab_pcnoc>;
+ qcom,mas-rpm-id = <ICBID_MASTER_PCNOC_S_7>;
+ qcom,slv-rpm-id = <ICBID_SLAVE_PCNOC_S_7>;
+ };
+
+ pcnoc_s_8: pcnoc-s-8 {
+ cell-id = <MSM_BUS_PNOC_SLV_8>;
+ label = "pcnoc-s-8";
+ qcom,buswidth = <4>;
+ qcom,agg-ports = <1>;
+ qcom,connections = <&slv_usb_hs &slv_crypto_0_cfg>;
+ qcom,bus-dev = <&fab_pcnoc>;
+ qcom,mas-rpm-id = <ICBID_MASTER_PCNOC_S_8>;
+ qcom,slv-rpm-id = <ICBID_SLAVE_PCNOC_S_8>;
+ };
+
+ qdss_int: qdss-int {
+ cell-id = <MSM_BUS_SNOC_QDSS_INT>;
+ label = "qdss-int";
+ qcom,buswidth = <8>;
+ qcom,agg-ports = <1>;
+ qcom,ap-owned;
+ qcom,connections = <&snoc_int_1 &slv_snoc_bimc_1>;
+ qcom,bus-dev = <&fab_snoc>;
+ qcom,mas-rpm-id = <ICBID_MASTER_QDSS_INT>;
+ qcom,slv-rpm-id = <ICBID_SLAVE_QDSS_INT>;
+ };
+
+ snoc_int_0: snoc-int-0 {
+ cell-id = <MSM_BUS_SNOC_INT_0>;
+ label = "snoc-int-0";
+ qcom,buswidth = <8>;
+ qcom,agg-ports = <1>;
+ qcom,ap-owned;
+ qcom,connections = <&slv_lpass
+ &slv_wcss &slv_kpss_ahb>;
+ qcom,bus-dev = <&fab_snoc>;
+ qcom,mas-rpm-id = <ICBID_MASTER_SNOC_INT_0>;
+ qcom,slv-rpm-id = <ICBID_SLAVE_SNOC_INT_0>;
+ };
+
+ snoc_int_1: snoc-int-1 {
+ cell-id = <MSM_BUS_SNOC_INT_1>;
+ label = "snoc-int-1";
+ qcom,buswidth = <8>;
+ qcom,agg-ports = <1>;
+ qcom,connections = <&slv_qdss_stm
+ &slv_imem &slv_snoc_pcnoc>;
+ qcom,bus-dev = <&fab_snoc>;
+ qcom,mas-rpm-id = <ICBID_MASTER_SNOC_INT_1>;
+ qcom,slv-rpm-id = <ICBID_SLAVE_SNOC_INT_1>;
+ };
+
+ snoc_int_2: snoc-int-2 {
+ cell-id = <MSM_BUS_SNOC_INT_2>;
+ label = "snoc-int-2";
+ qcom,buswidth = <8>;
+ qcom,agg-ports = <1>;
+ qcom,ap-owned;
+ qcom,connections = <&slv_cats_0 &slv_cats_1>;
+ qcom,bus-dev = <&fab_snoc>;
+ qcom,mas-rpm-id = <ICBID_MASTER_SNOC_INT_2>;
+ qcom,slv-rpm-id = <ICBID_SLAVE_SNOC_INT_2>;
+ };
+ /*Slaves*/
+
+ slv_ebi:slv-ebi {
+ cell-id = <MSM_BUS_SLAVE_EBI_CH0>;
+ label = "slv-ebi";
+ qcom,buswidth = <8>;
+ qcom,agg-ports = <1>;
+ qcom,bus-dev = <&fab_bimc>;
+ qcom,slv-rpm-id = <ICBID_SLAVE_EBI1>;
+ };
+
+ slv_bimc_snoc:slv-bimc-snoc {
+ cell-id = <MSM_BUS_BIMC_SNOC_SLV>;
+ label = "slv-bimc-snoc";
+ qcom,buswidth = <8>;
+ qcom,agg-ports = <1>;
+ qcom,bus-dev = <&fab_bimc>;
+ qcom,connections = <&mas_bimc_snoc>;
+ qcom,slv-rpm-id = <ICBID_SLAVE_BIMC_SNOC>;
+ };
+
+ slv_sdcc_2:slv-sdcc-2 {
+ cell-id = <MSM_BUS_SLAVE_SDCC_2>;
+ label = "slv-sdcc-2";
+ qcom,buswidth = <4>;
+ qcom,agg-ports = <1>;
+ qcom,bus-dev = <&fab_pcnoc>;
+ qcom,slv-rpm-id = <ICBID_SLAVE_SDCC_2>;
+ };
+
+ slv_spdm:slv-spdm {
+ cell-id = <MSM_BUS_SLAVE_SPDM_WRAPPER>;
+ label = "slv-spdm";
+ qcom,buswidth = <4>;
+ qcom,agg-ports = <1>;
+ qcom,ap-owned;
+ qcom,bus-dev = <&fab_pcnoc>;
+ qcom,slv-rpm-id = <ICBID_SLAVE_SPDM_WRAPPER>;
+ };
+
+ slv_pdm:slv-pdm {
+ cell-id = <MSM_BUS_SLAVE_PDM>;
+ label = "slv-pdm";
+ qcom,buswidth = <4>;
+ qcom,agg-ports = <1>;
+ qcom,bus-dev = <&fab_pcnoc>;
+ qcom,slv-rpm-id = <ICBID_SLAVE_PDM>;
+ };
+
+ slv_prng:slv-prng {
+ cell-id = <MSM_BUS_SLAVE_PRNG>;
+ label = "slv-prng";
+ qcom,buswidth = <4>;
+ qcom,agg-ports = <1>;
+ qcom,bus-dev = <&fab_pcnoc>;
+ qcom,slv-rpm-id = <ICBID_SLAVE_PRNG>;
+ };
+
+ slv_tcsr:slv-tcsr {
+ cell-id = <MSM_BUS_SLAVE_TCSR>;
+ label = "slv-tcsr";
+ qcom,buswidth = <4>;
+ qcom,agg-ports = <1>;
+ qcom,bus-dev = <&fab_pcnoc>;
+ qcom,slv-rpm-id = <ICBID_SLAVE_TCSR>;
+ };
+
+ slv_snoc_cfg:slv-snoc-cfg {
+ cell-id = <MSM_BUS_SLAVE_SNOC_CFG>;
+ label = "slv-snoc-cfg";
+ qcom,buswidth = <4>;
+ qcom,agg-ports = <1>;
+ qcom,bus-dev = <&fab_pcnoc>;
+ qcom,slv-rpm-id = <ICBID_SLAVE_SNOC_CFG>;
+ };
+
+ slv_message_ram:slv-message-ram {
+ cell-id = <MSM_BUS_SLAVE_MESSAGE_RAM>;
+ label = "slv-message-ram";
+ qcom,buswidth = <4>;
+ qcom,agg-ports = <1>;
+ qcom,bus-dev = <&fab_pcnoc>;
+ qcom,slv-rpm-id = <ICBID_SLAVE_MESSAGE_RAM>;
+ };
+
+ slv_camera_ss_cfg:slv-camera-ss-cfg {
+ cell-id = <MSM_BUS_SLAVE_CAMERA_CFG>;
+ label = "slv-camera-ss-cfg";
+ qcom,buswidth = <4>;
+ qcom,agg-ports = <1>;
+ qcom,ap-owned;
+ qcom,bus-dev = <&fab_pcnoc>;
+ qcom,slv-rpm-id = <ICBID_SLAVE_CAMERA_CFG>;
+ };
+
+ slv_disp_ss_cfg:slv-disp-ss-cfg {
+ cell-id = <MSM_BUS_SLAVE_DISPLAY_CFG>;
+ label = "slv-disp-ss-cfg";
+ qcom,buswidth = <4>;
+ qcom,agg-ports = <1>;
+ qcom,ap-owned;
+ qcom,bus-dev = <&fab_pcnoc>;
+ qcom,slv-rpm-id = <ICBID_SLAVE_DISPLAY_CFG>;
+ };
+
+ slv_venus_cfg:slv-venus-cfg {
+ cell-id = <MSM_BUS_SLAVE_VENUS_CFG>;
+ label = "slv-venus-cfg";
+ qcom,buswidth = <4>;
+ qcom,agg-ports = <1>;
+ qcom,ap-owned;
+ qcom,bus-dev = <&fab_pcnoc>;
+ qcom,slv-rpm-id = <ICBID_SLAVE_VENUS_CFG>;
+ };
+
+ slv_gpu_cfg:slv-gpu-cfg {
+ cell-id = <MSM_BUS_SLAVE_GRAPHICS_3D_CFG>;
+ label = "slv-gpu-cfg";
+ qcom,buswidth = <8>;
+ qcom,agg-ports = <1>;
+ qcom,ap-owned;
+ qcom,bus-dev = <&fab_pcnoc>;
+ qcom,slv-rpm-id = <ICBID_SLAVE_GFX3D_CFG>;
+ };
+
+ slv_tlmm:slv-tlmm {
+ cell-id = <MSM_BUS_SLAVE_TLMM>;
+ label = "slv-tlmm";
+ qcom,buswidth = <4>;
+ qcom,agg-ports = <1>;
+ qcom,bus-dev = <&fab_pcnoc>;
+ qcom,slv-rpm-id = <ICBID_SLAVE_TLMM>;
+ };
+
+ slv_blsp_1:slv-blsp-1 {
+ cell-id = <MSM_BUS_SLAVE_BLSP_1>;
+ label = "slv-blsp-1";
+ qcom,buswidth = <4>;
+ qcom,agg-ports = <1>;
+ qcom,bus-dev = <&fab_pcnoc>;
+ qcom,slv-rpm-id = <ICBID_SLAVE_BLSP_1>;
+ };
+
+ slv_blsp_2:slv-blsp-2 {
+ cell-id = <MSM_BUS_SLAVE_BLSP_2>;
+ label = "slv-blsp-2";
+ qcom,buswidth = <4>;
+ qcom,agg-ports = <1>;
+ qcom,bus-dev = <&fab_pcnoc>;
+ qcom,slv-rpm-id = <ICBID_SLAVE_BLSP_2>;
+ };
+
+ slv_pmic_arb:slv-pmic-arb {
+ cell-id = <MSM_BUS_SLAVE_PMIC_ARB>;
+ label = "slv-pmic-arb";
+ qcom,buswidth = <4>;
+ qcom,agg-ports = <1>;
+ qcom,bus-dev = <&fab_pcnoc>;
+ qcom,slv-rpm-id = <ICBID_SLAVE_PMIC_ARB>;
+ };
+
+ slv_sdcc_1:slv-sdcc-1 {
+ cell-id = <MSM_BUS_SLAVE_SDCC_1>;
+ label = "slv-sdcc-1";
+ qcom,buswidth = <4>;
+ qcom,agg-ports = <1>;
+ qcom,bus-dev = <&fab_pcnoc>;
+ qcom,slv-rpm-id = <ICBID_SLAVE_SDCC_1>;
+ };
+
+ slv_crypto_0_cfg:slv-crypto-0-cfg {
+ cell-id = <MSM_BUS_SLAVE_CRYPTO_0_CFG>;
+ label = "slv-crypto-0-cfg";
+ qcom,buswidth = <4>;
+ qcom,agg-ports = <1>;
+ qcom,bus-dev = <&fab_pcnoc>;
+ qcom,slv-rpm-id = <ICBID_SLAVE_CRYPTO_0_CFG>;
+ };
+
+ slv_usb_hs:slv-usb-hs {
+ cell-id = <MSM_BUS_SLAVE_USB_HS>;
+ label = "slv-usb-hs";
+ qcom,buswidth = <4>;
+ qcom,agg-ports = <1>;
+ qcom,bus-dev = <&fab_pcnoc>;
+ qcom,slv-rpm-id = <ICBID_SLAVE_USB_HS>;
+ };
+
+ slv_tcu:slv-tcu {
+ cell-id = <MSM_BUS_SLAVE_TCU>;
+ label = "slv-tcu";
+ qcom,buswidth = <8>;
+ qcom,agg-ports = <1>;
+ qcom,ap-owned;
+ qcom,bus-dev = <&fab_pcnoc>;
+ qcom,slv-rpm-id = <ICBID_SLAVE_TCU>;
+ };
+
+ slv_pcnoc_snoc:slv-pcnoc-snoc {
+ cell-id = <MSM_BUS_PNOC_SNOC_SLV>;
+ label = "slv-pcnoc-snoc";
+ qcom,buswidth = <8>;
+ qcom,agg-ports = <1>;
+ qcom,bus-dev = <&fab_pcnoc>;
+ qcom,connections = <&mas_pcnoc_snoc>;
+ qcom,slv-rpm-id = <ICBID_SLAVE_PCNOC_SNOC>;
+ };
+
+ slv_kpss_ahb:slv-kpss-ahb {
+ cell-id = <MSM_BUS_SLAVE_APPSS>;
+ label = "slv-kpss-ahb";
+ qcom,buswidth = <4>;
+ qcom,agg-ports = <1>;
+ qcom,ap-owned;
+ qcom,bus-dev = <&fab_snoc>;
+ qcom,slv-rpm-id = <ICBID_SLAVE_APPSS>;
+ };
+
+ slv_wcss:slv-wcss {
+ cell-id = <MSM_BUS_SLAVE_WCSS>;
+ label = "slv-wcss";
+ qcom,buswidth = <4>;
+ qcom,agg-ports = <1>;
+ qcom,ap-owned;
+ qcom,bus-dev = <&fab_snoc>;
+ qcom,slv-rpm-id = <ICBID_SLAVE_WCSS>;
+ };
+
+ slv_snoc_bimc_0:slv-snoc-bimc-0 {
+ cell-id = <MSM_BUS_SNOC_BIMC_0_SLV>;
+ label = "slv-snoc-bimc-0";
+ qcom,buswidth = <16>;
+ qcom,agg-ports = <1>;
+ qcom,ap-owned;
+ qcom,bus-dev = <&fab_snoc_mm>;
+ qcom,connections = <&mas_snoc_bimc_0>;
+ qcom,slv-rpm-id = <ICBID_SLAVE_SNOC_BIMC_0>;
+ };
+
+ slv_snoc_bimc_1:slv-snoc-bimc-1 {
+ cell-id = <MSM_BUS_SNOC_BIMC_1_SLV>;
+ label = "slv-snoc-bimc-1";
+ qcom,buswidth = <8>;
+ qcom,agg-ports = <1>;
+ qcom,bus-dev = <&fab_snoc>;
+ qcom,connections = <&mas_snoc_bimc_1>;
+ qcom,slv-rpm-id = <ICBID_SLAVE_SNOC_BIMC_1>;
+ };
+
+ slv_snoc_bimc_2:slv-snoc-bimc-2 {
+ cell-id = <MSM_BUS_SNOC_BIMC_2_SLV>;
+ label = "slv-snoc-bimc-2";
+ qcom,buswidth = <16>;
+ qcom,agg-ports = <1>;
+ qcom,ap-owned;
+ qcom,bus-dev = <&fab_snoc_mm>;
+ qcom,connections = <&mas_snoc_bimc_2>;
+ qcom,slv-rpm-id = <ICBID_SLAVE_SNOC_BIMC_2>;
+ };
+
+ slv_imem:slv-imem {
+ cell-id = <MSM_BUS_SLAVE_OCIMEM>;
+ label = "slv-imem";
+ qcom,buswidth = <8>;
+ qcom,agg-ports = <1>;
+ qcom,bus-dev = <&fab_snoc>;
+ qcom,slv-rpm-id = <ICBID_SLAVE_IMEM>;
+ };
+
+ slv_snoc_pcnoc:slv-snoc-pcnoc {
+ cell-id = <MSM_BUS_SNOC_PNOC_SLV>;
+ label = "slv-snoc-pcnoc";
+ qcom,buswidth = <8>;
+ qcom,agg-ports = <1>;
+ qcom,bus-dev = <&fab_snoc>;
+ qcom,connections = <&mas_snoc_pcnoc>;
+ qcom,slv-rpm-id = <ICBID_SLAVE_SNOC_PCNOC>;
+ };
+
+ slv_qdss_stm:slv-qdss-stm {
+ cell-id = <MSM_BUS_SLAVE_QDSS_STM>;
+ label = "slv-qdss-stm";
+ qcom,buswidth = <4>;
+ qcom,agg-ports = <1>;
+ qcom,bus-dev = <&fab_snoc>;
+ qcom,slv-rpm-id = <ICBID_SLAVE_QDSS_STM>;
+ };
+
+ slv_cats_0:slv-cats-0 {
+ cell-id = <MSM_BUS_SLAVE_CATS_128>;
+ label = "slv-cats-0";
+ qcom,buswidth = <16>;
+ qcom,agg-ports = <1>;
+ qcom,ap-owned;
+ qcom,bus-dev = <&fab_snoc_mm>;
+ qcom,slv-rpm-id = <ICBID_SLAVE_CATS_0>;
+ };
+
+ slv_cats_1:slv-cats-1 {
+ cell-id = <MSM_BUS_SLAVE_OCMEM_64>;
+ label = "slv-cats-1";
+ qcom,buswidth = <8>;
+ qcom,agg-ports = <1>;
+ qcom,ap-owned;
+ qcom,bus-dev = <&fab_snoc>;
+ qcom,slv-rpm-id = <ICBID_SLAVE_CATS_1>;
+ };
+
+ slv_lpass:slv-lpass {
+ cell-id = <MSM_BUS_SLAVE_LPASS>;
+ label = "slv-lpass";
+ qcom,buswidth = <8>;
+ qcom,agg-ports = <1>;
+ qcom,ap-owned;
+ qcom,bus-dev = <&fab_snoc>;
+ qcom,slv-rpm-id = <ICBID_SLAVE_LPASS>;
+ };
+ };
+
+ devfreq_spdm_cpu {
+ compatible = "qcom,devfreq_spdm";
+ qcom,msm-bus,name = "devfreq_spdm";
+ qcom,msm-bus,num-cases = <2>;
+ qcom,msm-bus,num-paths = <1>;
+ qcom,msm-bus,vectors-KBps =
+ <1 512 0 0>,
+ <1 512 0 0>;
+ qcom,msm-bus,active-only;
+ qcom,spdm-client = <0>;
+
+ clock-names = "cci_clk";
+ clocks = <&clock_cpu clk_cci_clk>;
+
+ qcom,bw-upstep = <400>;
+ qcom,bw-dwnstep = <3640>;
+ qcom,max-vote = <3640>;
+ qcom,up-step-multp = <1>;
+ qcom,spdm-interval = <50>;
+
+ qcom,ports = <11>;
+ qcom,alpha-up = <8>;
+ qcom,alpha-down = <15>;
+ qcom,bucket-size = <8>;
+
+ /*max pl1 freq, max pl2 freq*/
+ qcom,pl-freqs = <520000 720000>;
+
+ /* pl1 low, pl1 high, pl2 low, pl2 high, pl3 low, pl3 high */
+ qcom,reject-rate = <5000 5000 5000 5000 5000 5000>;
+ /* pl1 low, pl1 high, pl2 low, pl2 high, pl3 low, pl3 high */
+ qcom,response-time-us = <5000 5000 5000 5000 5000 5000>;
+ /* pl1 low, pl1 high, pl2 low, pl2 high, pl3 low, pl3 high */
+ qcom,cci-response-time-us = <5000 5000 5000 5000 5000 5000>;
+ qcom,max-cci-freq = <500000>;
+ };
+
+ devfreq_spdm_gov {
+ compatible = "qcom,gov_spdm_hyp";
+ interrupt-names = "spdm-irq";
+ interrupts = <0 192 0>;
+ };
+};
diff --git a/arch/arm64/boot/dts/qcom/msm8937.dtsi b/arch/arm64/boot/dts/qcom/msm8937.dtsi
index 14a1e9a..61688b7 100644
--- a/arch/arm64/boot/dts/qcom/msm8937.dtsi
+++ b/arch/arm64/boot/dts/qcom/msm8937.dtsi
@@ -105,6 +105,10 @@
smd11 = &smdtty_data11;
smd21 = &smdtty_data21;
smd36 = &smdtty_loopback;
+ i2c2 = &i2c_2;
+ i2c5 = &i2c_5;
+ spi3 = &spi_3;
+ i2c3 = &i2c_3;
};
soc: soc { };
@@ -123,6 +127,7 @@
#include "msm8937-cpu.dtsi"
#include "msm8937-ion.dtsi"
#include "msm8937-smp2p.dtsi"
+#include "msm8937-bus.dtsi"
&soc {
#address-cells = <1>;
@@ -555,6 +560,110 @@
#clock-cells = <1>;
};
+ i2c_2: i2c@78b6000 { /* BLSP1 QUP2 */
+ compatible = "qcom,i2c-msm-v2";
+ #address-cells = <1>;
+ #size-cells = <0>;
+ reg-names = "qup_phys_addr";
+ reg = <0x78b6000 0x600>;
+ interrupt-names = "qup_irq";
+ interrupts = <0 96 0>;
+ qcom,clk-freq-out = <400000>;
+ qcom,clk-freq-in = <19200000>;
+ clock-names = "iface_clk", "core_clk";
+ clocks = <&clock_gcc clk_gcc_blsp1_ahb_clk>,
+ <&clock_gcc clk_gcc_blsp1_qup2_i2c_apps_clk>;
+
+ pinctrl-names = "i2c_active", "i2c_sleep";
+ pinctrl-0 = <&i2c_2_active>;
+ pinctrl-1 = <&i2c_2_sleep>;
+ qcom,noise-rjct-scl = <0>;
+ qcom,noise-rjct-sda = <0>;
+ qcom,master-id = <86>;
+ dmas = <&dma_blsp1 6 64 0x20000020 0x20>,
+ <&dma_blsp1 7 32 0x20000020 0x20>;
+ dma-names = "tx", "rx";
+ status = "disabled";
+ };
+
+ i2c_3: i2c@78b7000 { /* BLSP1 QUP3 */
+ compatible = "qcom,i2c-msm-v2";
+ #address-cells = <1>;
+ #size-cells = <0>;
+ reg-names = "qup_phys_addr";
+ reg = <0x78b7000 0x600>;
+ interrupt-names = "qup_irq";
+ interrupts = <0 97 0>;
+ qcom,clk-freq-out = <400000>;
+ qcom,clk-freq-in = <19200000>;
+ clock-names = "iface_clk", "core_clk";
+ clocks = <&clock_gcc clk_gcc_blsp1_ahb_clk>,
+ <&clock_gcc clk_gcc_blsp1_qup3_i2c_apps_clk>;
+
+ pinctrl-names = "i2c_active", "i2c_sleep";
+ pinctrl-0 = <&i2c_3_active>;
+ pinctrl-1 = <&i2c_3_sleep>;
+ qcom,noise-rjct-scl = <0>;
+ qcom,noise-rjct-sda = <0>;
+ qcom,master-id = <86>;
+ dmas = <&dma_blsp1 8 64 0x20000020 0x20>,
+ <&dma_blsp1 9 32 0x20000020 0x20>;
+ dma-names = "tx", "rx";
+ status = "disabled";
+ };
+
+ i2c_5: i2c@7af5000 { /* BLSP2 QUP1 */
+ compatible = "qcom,i2c-msm-v2";
+ #address-cells = <1>;
+ #size-cells = <0>;
+ reg-names = "qup_phys_addr";
+ reg = <0x7af5000 0x600>;
+ interrupt-names = "qup_irq";
+ interrupts = <0 299 0>;
+ qcom,clk-freq-out = <400000>;
+ qcom,clk-freq-in = <19200000>;
+ clock-names = "iface_clk", "core_clk";
+ clocks = <&clock_gcc clk_gcc_blsp2_ahb_clk>,
+ <&clock_gcc clk_gcc_blsp2_qup1_i2c_apps_clk>;
+
+ pinctrl-names = "i2c_active", "i2c_sleep";
+ pinctrl-0 = <&i2c_5_active>;
+ pinctrl-1 = <&i2c_5_sleep>;
+ qcom,noise-rjct-scl = <0>;
+ qcom,noise-rjct-sda = <0>;
+ qcom,master-id = <84>;
+ dmas = <&dma_blsp2 4 64 0x20000020 0x20>,
+ <&dma_blsp2 5 32 0x20000020 0x20>;
+ dma-names = "tx", "rx";
+ status = "disabled";
+ };
+
+ spi_3: spi@78b7000 { /* BLSP1 QUP3 */
+ compatible = "qcom,spi-qup-v2";
+ #address-cells = <1>;
+ #size-cells = <0>;
+ reg-names = "spi_physical", "spi_bam_physical";
+ reg = <0x78b7000 0x600>,
+ <0x7884000 0x1f000>;
+ interrupt-names = "spi_irq", "spi_bam_irq";
+ interrupts = <0 97 0>, <0 238 0>;
+ spi-max-frequency = <19200000>;
+ pinctrl-names = "spi_default", "spi_sleep";
+ pinctrl-0 = <&spi3_default &spi3_cs0_active>;
+ pinctrl-1 = <&spi3_sleep &spi3_cs0_sleep>;
+ clocks = <&clock_gcc clk_gcc_blsp1_ahb_clk>,
+ <&clock_gcc clk_gcc_blsp1_qup3_spi_apps_clk>;
+ clock-names = "iface_clk", "core_clk";
+ qcom,infinite-mode = <0>;
+ qcom,use-bam;
+ qcom,use-pinctrl;
+ qcom,ver-reg-exists;
+ qcom,bam-consumer-pipe-index = <8>;
+ qcom,bam-producer-pipe-index = <9>;
+ qcom,master-id = <86>;
+ status = "disabled";
+ };
+
cpubw: qcom,cpubw {
compatible = "qcom,devbw";
governor = "cpufreq";
@@ -600,6 +709,42 @@
< 7031 /* 921.6 MHz */ >; /* TURBO */
};
+ blsp2_uart1: uart@7aef000 {
+ compatible = "qcom,msm-hsuart-v14";
+ reg = <0x7aef000 0x200>,
+ <0x7ac4000 0x1f000>;
+ reg-names = "core_mem", "bam_mem";
+ interrupt-names = "core_irq", "bam_irq", "wakeup_irq";
+ #address-cells = <0>;
+ interrupt-parent = <&blsp2_uart1>;
+ interrupts = <0 1 2>;
+ #interrupt-cells = <1>;
+ interrupt-map-mask = <0xffffffff>;
+ interrupt-map = <0 &intc 0 306 0
+ 1 &intc 0 239 0
+ 2 &tlmm 17 0>;
+
+ qcom,inject-rx-on-wakeup;
+ qcom,rx-char-to-inject = <0xfd>;
+
+ qcom,bam-tx-ep-pipe-index = <0>;
+ qcom,bam-rx-ep-pipe-index = <1>;
+ qcom,master-id = <84>;
+ clock-names = "core_clk", "iface_clk";
+ clocks = <&clock_gcc clk_gcc_blsp2_uart1_apps_clk>,
+ <&clock_gcc clk_gcc_blsp2_ahb_clk>;
+ pinctrl-names = "sleep", "default";
+ pinctrl-0 = <&blsp2_uart1_sleep>;
+ pinctrl-1 = <&blsp2_uart1_active>;
+ qcom,msm-bus,name = "blsp2_uart1";
+ qcom,msm-bus,num-cases = <2>;
+ qcom,msm-bus,num-paths = <1>;
+ qcom,msm-bus,vectors-KBps =
+ <84 512 0 0>,
+ <84 512 500 800>;
+ status = "disabled";
+ };
+
qcom,ipc-spinlock@1905000 {
compatible = "qcom,ipc-spinlock-sfpb";
reg = <0x1905000 0x8000>;
@@ -686,6 +831,92 @@
rpm-channel-type = <15>; /* SMD_APPS_RPM */
};
+ usb_otg: usb@78db000 {
+ compatible = "qcom,hsusb-otg";
+ reg = <0x78db000 0x400>, <0x6c000 0x200>;
+ reg-names = "core", "phy_csr";
+ #address-cells = <1>;
+ #size-cells = <1>;
+ ranges;
+
+ interrupts = <0 134 0>,<0 140 0>;
+ interrupt-names = "core_irq", "async_irq";
+
+ hsusb_vdd_dig-supply = <&pm8937_l2>;
+ HSUSB_1p8-supply = <&pm8937_l7>;
+ HSUSB_3p3-supply = <&pm8937_l13>;
+ qcom,vdd-voltage-level = <0 1200000 1200000>;
+ vbus_otg-supply = <&smbcharger_charger_otg>;
+
+ qcom,hsusb-otg-phy-type = <3>; /* SNPS Femto PHY */
+ qcom,hsusb-otg-mode = <3>; /* OTG mode */
+ qcom,hsusb-otg-otg-control = <2>; /* PMIC */
+ qcom,dp-manual-pullup;
+ qcom,phy-dvdd-always-on;
+ qcom,boost-sysclk-with-streaming;
+ qcom,axi-prefetch-enable;
+ qcom,hsusb-otg-delay-lpm;
+
+ qcom,msm-bus,name = "usb2";
+ qcom,msm-bus,num-cases = <3>;
+ qcom,msm-bus,num-paths = <1>;
+ qcom,msm-bus,vectors-KBps =
+ <87 512 0 0>,
+ <87 512 80000 0>,
+ <87 512 6000 6000>;
+ clocks = <&clock_gcc clk_gcc_usb_hs_ahb_clk>,
+ <&clock_gcc clk_gcc_usb_hs_system_clk>,
+ <&clock_gcc clk_gcc_usb2a_phy_sleep_clk>,
+ <&clock_gcc clk_bimc_usb_a_clk>,
+ <&clock_gcc clk_snoc_usb_a_clk>,
+ <&clock_gcc clk_pnoc_usb_a_clk>,
+ <&clock_gcc clk_gcc_qusb2_phy_clk>,
+ <&clock_gcc clk_gcc_usb2_hs_phy_only_clk>,
+ <&clock_gcc clk_gcc_usb_hs_phy_cfg_ahb_clk>,
+ <&clock_gcc clk_xo_otg_clk>;
+ clock-names = "iface_clk", "core_clk", "sleep_clk",
+ "bimc_clk", "snoc_clk", "pcnoc_clk",
+ "phy_reset_clk", "phy_por_clk", "phy_csr_clk",
+ "xo";
+ qcom,bus-clk-rate = <748800000 200000000 100000000>;
+ qcom,max-nominal-sysclk-rate = <133330000>;
+
+ resets = <&clock_gcc GCC_USB_HS_BCR>,
+ <&clock_gcc GCC_QUSB2_PHY_BCR>,
+ <&clock_gcc GCC_USB2_HS_PHY_ONLY_BCR>;
+ reset-names = "core_reset", "phy_reset", "phy_por_reset";
+
+ qcom,usbbam@78c4000 {
+ compatible = "qcom,usb-bam-msm";
+ reg = <0x78c4000 0x17000>;
+ interrupt-parent = <&intc>;
+ interrupts = <0 135 0>;
+
+ qcom,bam-type = <1>;
+ qcom,usb-bam-num-pipes = <4>;
+ qcom,usb-bam-fifo-baseaddr = <0x08605000>;
+ qcom,ignore-core-reset-ack;
+ qcom,disable-clk-gating;
+ qcom,usb-bam-max-mbps-highspeed = <400>;
+ qcom,reset-bam-on-disconnect;
+
+ qcom,pipe0 {
+ label = "hsusb-qdss-in-0";
+ qcom,usb-bam-mem-type = <2>;
+ qcom,dir = <1>;
+ qcom,pipe-num = <0>;
+ qcom,peer-bam = <0>;
+ qcom,peer-bam-physical-address = <0x6044000>;
+ qcom,src-bam-pipe-index = <0>;
+ qcom,dst-bam-pipe-index = <0>;
+ qcom,data-fifo-offset = <0x0>;
+ qcom,data-fifo-size = <0xe00>;
+ qcom,descriptor-fifo-offset = <0xe00>;
+ qcom,descriptor-fifo-size = <0x200>;
+ };
+ };
+ };
+
qcom,wdt@b017000 {
compatible = "qcom,msm-watchdog";
reg = <0xb017000 0x1000>;
diff --git a/arch/arm64/boot/dts/qcom/msm8953-audio-cdp.dtsi b/arch/arm64/boot/dts/qcom/msm8953-audio-cdp.dtsi
new file mode 100644
index 0000000..493e002
--- /dev/null
+++ b/arch/arm64/boot/dts/qcom/msm8953-audio-cdp.dtsi
@@ -0,0 +1,33 @@
+/*
+ * Copyright (c) 2015-2016, 2018, 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.
+ */
+
+&int_codec {
+ status = "okay";
+ qcom,msm-hs-micbias-type = "external";
+};
+
+&msm_digital_codec {
+ status = "okay";
+};
+
+&pmic_analog_codec {
+ status = "okay";
+};
+
+&wsa881x_i2c_f {
+ status = "okay";
+};
+
+&wsa881x_i2c_45 {
+ status = "okay";
+};
diff --git a/arch/arm64/boot/dts/qcom/msm8953-audio.dtsi b/arch/arm64/boot/dts/qcom/msm8953-audio.dtsi
new file mode 100644
index 0000000..c19f267
--- /dev/null
+++ b/arch/arm64/boot/dts/qcom/msm8953-audio.dtsi
@@ -0,0 +1,443 @@
+/*
+ * Copyright (c) 2015-2016, 2018, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include "msm-audio-lpass.dtsi"
+#include "msm8953-wsa881x.dtsi"
+
+&msm_audio_ion {
+ iommus = <&apps_iommu 0x2401 0x0>;
+ qcom,smmu-sid-mask = /bits/ 64 <0xf>;
+};
+
+&soc {
+ qcom,msm-audio-apr {
+ compatible = "qcom,msm-audio-apr";
+ msm_audio_apr_dummy {
+ compatible = "qcom,msm-audio-apr-dummy";
+ };
+ };
+
+ qcom,avtimer@c0a300c {
+ compatible = "qcom,avtimer";
+ reg = <0x0c0a300c 0x4>,
+ <0x0c0a3010 0x4>;
+ reg-names = "avtimer_lsb_addr", "avtimer_msb_addr";
+ qcom,clk-div = <27>;
+ };
+
+ int_codec: sound {
+ status = "okay";
+ compatible = "qcom,msm8952-audio-codec";
+ qcom,model = "msm8953-snd-card-mtp";
+ reg = <0xc051000 0x4>,
+ <0xc051004 0x4>,
+ <0xc055000 0x4>,
+ <0xc052000 0x4>;
+ reg-names = "csr_gp_io_mux_mic_ctl",
+ "csr_gp_io_mux_spkr_ctl",
+ "csr_gp_io_lpaif_pri_pcm_pri_mode_muxsel",
+ "csr_gp_io_mux_quin_ctl";
+
+ qcom,msm-ext-pa = "primary";
+ qcom,msm-mclk-freq = <9600000>;
+ qcom,msm-mbhc-hphl-swh = <0>;
+ qcom,msm-mbhc-gnd-swh = <0>;
+ qcom,msm-hs-micbias-type = "internal";
+ qcom,msm-micbias1-ext-cap;
+
+ qcom,audio-routing =
+ "RX_BIAS", "MCLK",
+ "SPK_RX_BIAS", "MCLK",
+ "INT_LDO_H", "MCLK",
+ "RX_I2S_CLK", "MCLK",
+ "TX_I2S_CLK", "MCLK",
+ "MIC BIAS External", "Handset Mic",
+ "MIC BIAS External2", "Headset Mic",
+ "MIC BIAS External", "Secondary Mic",
+ "AMIC1", "MIC BIAS External",
+ "AMIC2", "MIC BIAS External2",
+ "AMIC3", "MIC BIAS External",
+ "ADC1_IN", "ADC1_OUT",
+ "ADC2_IN", "ADC2_OUT",
+ "ADC3_IN", "ADC3_OUT",
+ "PDM_IN_RX1", "PDM_OUT_RX1",
+ "PDM_IN_RX2", "PDM_OUT_RX2",
+ "PDM_IN_RX3", "PDM_OUT_RX3",
+ "WSA_SPK OUT", "VDD_WSA_SWITCH",
+ "SpkrMono WSA_IN", "WSA_SPK OUT";
+
+ qcom,cdc-us-euro-gpios = <&tlmm 63 0>;
+ qcom,cdc-us-eu-gpios = <&cdc_us_euro_sw>;
+ qcom,cdc-comp-gpios = <&cdc_comp_gpios>;
+ qcom,pri-mi2s-gpios = <&cdc_pri_mi2s_gpios>;
+ qcom,quin-mi2s-gpios = <&cdc_quin_mi2s_gpios>;
+
+ asoc-platform = <&pcm0>, <&pcm1>, <&pcm2>, <&voip>, <&voice>,
+ <&loopback>, <&compress>, <&hostless>,
+ <&afe>, <&lsm>, <&routing>, <&pcm_noirq>;
+ asoc-platform-names = "msm-pcm-dsp.0", "msm-pcm-dsp.1",
+ "msm-pcm-dsp.2", "msm-voip-dsp",
+ "msm-pcm-voice", "msm-pcm-loopback",
+ "msm-compress-dsp", "msm-pcm-hostless",
+ "msm-pcm-afe", "msm-lsm-client",
+ "msm-pcm-routing", "msm-pcm-dsp-noirq";
+ asoc-cpu = <&dai_pri_auxpcm>,
+ <&dai_mi2s0>, <&dai_mi2s1>,
+ <&dai_mi2s2>, <&dai_mi2s3>,
+ <&dai_mi2s4>, <&dai_mi2s5>,
+ <&sb_0_rx>, <&sb_0_tx>, <&sb_1_rx>, <&sb_1_tx>,
+ <&sb_3_rx>, <&sb_3_tx>, <&sb_4_rx>, <&sb_4_tx>,
+ <&bt_sco_rx>, <&bt_sco_tx>,
+ <&int_fm_rx>, <&int_fm_tx>,
+ <&afe_pcm_rx>, <&afe_pcm_tx>,
+ <&afe_proxy_rx>, <&afe_proxy_tx>,
+ <&incall_record_rx>, <&incall_record_tx>,
+ <&incall_music_rx>, <&incall_music_2_rx>;
+
+ asoc-cpu-names = "msm-dai-q6-auxpcm.1",
+ "msm-dai-q6-mi2s.0", "msm-dai-q6-mi2s.1",
+ "msm-dai-q6-mi2s.2", "msm-dai-q6-mi2s.3",
+ "msm-dai-q6-mi2s.4", "msm-dai-q6-mi2s.6",
+ "msm-dai-q6-dev.16384", "msmdai-q6-dev.16385",
+ "msm-dai-q6-dev.16386", "msm-dai-q6-dev.16387",
+ "msm-dai-q6-dev.16390", "msm-dai-q6-dev.16391",
+ "msm-dai-q6-dev.16392", "msm-dai-q6-dev.16393",
+ "msm-dai-q6-dev.12288", "msm-dai-q6-dev.12289",
+ "msm-dai-q6-dev.12292", "msm-dai-q6-dev.12293",
+ "msm-dai-q6-dev.224", "msm-dai-q6-dev.225",
+ "msm-dai-q6-dev.241", "msm-dai-q6-dev.240",
+ "msm-dai-q6-dev.32771", "msm-dai-q6-dev.32772",
+ "msm-dai-q6-dev.32773", "msm-dai-q6-dev.32770";
+
+ asoc-codec = <&stub_codec>, <&msm_digital_codec>,
+ <&pmic_analog_codec>;
+ asoc-codec-names = "msm-stub-codec.1", "msm-dig-codec",
+ "analog-codec";
+ asoc-wsa-codec-names = "wsa881x-i2c-codec.2-000f";
+ asoc-wsa-codec-prefixes = "SpkrMono";
+ msm-vdd-wsa-switch-supply = <&pm8953_l5>;
+ qcom,msm-vdd-wsa-switch-voltage = <1800000>;
+ qcom,msm-vdd-wsa-switch-current = <10000>;
+ };
+
+ cdc_us_euro_sw: msm_cdc_pinctrl_us_euro_sw {
+ compatible = "qcom,msm-cdc-pinctrl";
+ pinctrl-names = "aud_active", "aud_sleep";
+ pinctrl-0 = <&cross_conn_det_act>;
+ pinctrl-1 = <&cross_conn_det_sus>;
+ };
+
+ cdc_comp_gpios: cdc_comp_pinctrl {
+ compatible = "qcom,msm-cdc-pinctrl";
+ pinctrl-names = "aud_active", "aud_sleep";
+ pinctrl-0 = <&cdc_pdm_comp_lines_act>;
+ pinctrl-1 = <&cdc_pdm_comp_lines_sus>;
+ };
+
+ cdc_pri_mi2s_gpios: msm_cdc_pinctrl_pri {
+ compatible = "qcom,msm-cdc-pinctrl";
+ pinctrl-names = "aud_active", "aud_sleep";
+ pinctrl-0 = <&cdc_pdm_lines_act &cdc_pdm_lines_2_act>;
+ pinctrl-1 = <&cdc_pdm_lines_sus &cdc_pdm_lines_2_sus>;
+ };
+
+ cdc_quin_mi2s_gpios: msm_cdc_pinctrl_quin {
+ compatible = "qcom,msm-cdc-pinctrl";
+ pinctrl-names = "aud_active", "aud_sleep";
+ pinctrl-0 = <&pri_tlmm_lines_act &pri_tlmm_ws_act>;
+ pinctrl-1 = <&pri_tlmm_lines_sus &pri_tlmm_ws_sus>;
+ };
+
+
+ i2c@78b6000 {
+ status = "okay";
+ #address-cells = <1>;
+ #size-cells = <0>;
+ wsa881x_i2c_f: wsa881x-i2c-codec@f {
+ status = "okay";
+ compatible = "qcom,wsa881x-i2c-codec";
+ reg = <0x0f>;
+ qcom,wsa-analog-vi-gpio = <&wsa881x_analog_vi_gpio>;
+ qcom,wsa-analog-clk-gpio = <&wsa881x_analog_clk_gpio>;
+ qcom,wsa-analog-reset-gpio =
+ <&wsa881x_analog_reset_gpio>;
+ };
+ wsa881x_i2c_45: wsa881x-i2c-codec@45 {
+ status = "okay";
+ compatible = "qcom,wsa881x-i2c-codec";
+ reg = <0x45>;
+ };
+ };
+
+ wsa881x_analog_vi_gpio: wsa881x_analog_vi_pctrl {
+ compatible = "qcom,msm-cdc-pinctrl";
+ pinctrl-names = "aud_active", "aud_sleep";
+ pinctrl-0 = <&wsa_vi_on>;
+ pinctrl-1 = <&wsa_vi_off>;
+ };
+ wsa881x_analog_clk_gpio: wsa881x_analog_clk_pctrl {
+ compatible = "qcom,msm-cdc-pinctrl";
+ pinctrl-names = "aud_active", "aud_sleep";
+ pinctrl-0 = <&wsa_clk_on>;
+ pinctrl-1 = <&wsa_clk_off>;
+ };
+ wsa881x_analog_reset_gpio: wsa881x_analog_reset_pctrl {
+ compatible = "qcom,msm-cdc-pinctrl";
+ pinctrl-names = "aud_active", "aud_sleep";
+ pinctrl-0 = <&wsa_reset_on>;
+ pinctrl-1 = <&wsa_reset_off>;
+ };
+
+ ext_codec: sound-9335 {
+ status = "disabled";
+ compatible = "qcom,msm8952-audio-slim-codec";
+ qcom,model = "msm8953-tasha-snd-card";
+
+ reg = <0xc051000 0x4>,
+ <0xc051004 0x4>,
+ <0xc055000 0x4>,
+ <0xc052000 0x4>;
+ reg-names = "csr_gp_io_mux_mic_ctl",
+ "csr_gp_io_mux_spkr_ctl",
+ "csr_gp_io_lpaif_pri_pcm_pri_mode_muxsel",
+ "csr_gp_io_mux_quin_ctl";
+
+ qcom,audio-routing =
+ "AIF4 VI", "MCLK",
+ "AIF4 VI", "MICBIAS_REGULATOR",
+ "RX_BIAS", "MCLK",
+ "MADINPUT", "MCLK",
+ "AIF4 MAD", "MICBIAS_REGULATOR",
+ "AMIC2", "MIC BIAS2",
+ "MIC BIAS2", "Headset Mic",
+ "AMIC3", "MIC BIAS2",
+ "MIC BIAS2", "ANCRight Headset Mic",
+ "AMIC4", "MIC BIAS2",
+ "MIC BIAS2", "ANCLeft Headset Mic",
+ "AMIC5", "MIC BIAS3",
+ "MIC BIAS3", "Handset Mic",
+ "AMIC6", "MIC BIAS4",
+ "MIC BIAS4", "Analog Mic6",
+ "DMIC0", "MIC BIAS1",
+ "MIC BIAS1", "Digital Mic0",
+ "DMIC1", "MIC BIAS1",
+ "MIC BIAS1", "Digital Mic1",
+ "DMIC2", "MIC BIAS3",
+ "MIC BIAS3", "Digital Mic2",
+ "DMIC3", "MIC BIAS3",
+ "MIC BIAS3", "Digital Mic3",
+ "DMIC4", "MIC BIAS4",
+ "MIC BIAS4", "Digital Mic4",
+ "DMIC5", "MIC BIAS4",
+ "MIC BIAS4", "Digital Mic5",
+ "MIC BIAS1", "MICBIAS_REGULATOR",
+ "MIC BIAS2", "MICBIAS_REGULATOR",
+ "MIC BIAS3", "MICBIAS_REGULATOR",
+ "MIC BIAS4", "MICBIAS_REGULATOR",
+ "SpkrLeft IN", "SPK1 OUT",
+ "SpkrRight IN", "SPK2 OUT";
+
+ qcom,tasha-mclk-clk-freq = <9600000>;
+
+ asoc-platform = <&pcm0>, <&pcm1>, <&pcm2>, <&voip>, <&voice>,
+ <&loopback>, <&compress>, <&hostless>,
+ <&afe>, <&lsm>, <&routing>;
+ asoc-platform-names = "msm-pcm-dsp.0", "msm-pcm-dsp.1",
+ "msm-pcm-dsp.2", "msm-voip-dsp",
+ "msm-pcm-voice", "msm-pcm-loopback",
+ "msm-compress-dsp", "msm-pcm-hostless",
+ "msm-pcm-afe", "msm-lsm-client",
+ "msm-pcm-routing";
+
+ asoc-cpu = <&dai_pri_auxpcm>,
+ <&dai_mi2s2>, <&dai_mi2s3>, <&dai_mi2s5>,
+ <&sb_0_rx>, <&sb_0_tx>, <&sb_1_rx>, <&sb_1_tx>,
+ <&sb_2_rx>, <&sb_2_tx>, <&sb_3_rx>, <&sb_3_tx>,
+ <&sb_4_rx>, <&sb_4_tx>, <&sb_5_tx>,
+ <&afe_pcm_rx>, <&afe_pcm_tx>,
+ <&afe_proxy_rx>, <&afe_proxy_tx>,
+ <&incall_record_rx>, <&incall_record_tx>,
+ <&incall_music_rx>, <&incall_music_2_rx>,
+ <&sb_5_rx>, <&bt_sco_rx>, <&bt_sco_tx>,
+ <&int_fm_rx>, <&int_fm_tx>, <&sb_6_rx>;
+
+ asoc-cpu-names = "msm-dai-q6-auxpcm.1",
+ "msm-dai-q6-mi2s.2", "msm-dai-q6-mi2s.3",
+ "msm-dai-q6-mi2s.5", "msm-dai-q6-dev.16384",
+ "msm-dai-q6-dev.16385", "msm-dai-q6-dev.16386",
+ "msm-dai-q6-dev.16387", "msm-dai-q6-dev.16388",
+ "msm-dai-q6-dev.16389", "msm-dai-q6-dev.16390",
+ "msm-dai-q6-dev.16391", "msm-dai-q6-dev.16392",
+ "msm-dai-q6-dev.16393", "msm-dai-q6-dev.16395",
+ "msm-dai-q6-dev.224", "msm-dai-q6-dev.225",
+ "msm-dai-q6-dev.241", "msm-dai-q6-dev.240",
+ "msm-dai-q6-dev.32771", "msm-dai-q6-dev.32772",
+ "msm-dai-q6-dev.32773", "msm-dai-q6-dev.32770",
+ "msm-dai-q6-dev.16394", "msm-dai-q6-dev.12288",
+ "msm-dai-q6-dev.12289", "msm-dai-q6-dev.12292",
+ "msm-dai-q6-dev.12293", "msm-dai-q6-dev.16396";
+
+ asoc-codec = <&stub_codec>, <&hdmi_dba>;
+ asoc-codec-names = "msm-stub-codec.1", "msm-hdmi-dba-codec-rx";
+ qcom,cdc-us-euro-gpios = <&tlmm 63 0>;
+ qcom,msm-mbhc-hphl-swh = <0>;
+ qcom,msm-mbhc-gnd-swh = <0>;
+
+ qcom,wsa-max-devs = <2>;
+ qcom,wsa-devs = <&wsa881x_211>, <&wsa881x_212>,
+ <&wsa881x_213>, <&wsa881x_214>;
+ qcom,wsa-aux-dev-prefix = "SpkrLeft", "SpkrRight",
+ "SpkrLeft", "SpkrRight";
+ };
+
+ wcd9xxx_intc: wcd9xxx-irq {
+ status = "disabled";
+ interrupt-parent = <&tlmm>;
+ interrupts = <73 0>;
+ qcom,gpio-connect = <&tlmm 73 0>;
+ };
+
+ clock_audio: audio_ext_clk {
+ status = "disabled";
+ compatible = "qcom,audio-ref-clk";
+ clock-names = "osr_clk";
+ qcom,node_has_rpm_clock;
+ #clock-cells = <1>;
+ qcom,audio-ref-clk-gpio = <&pm8953_gpios 1 0>;
+ qcom,lpass-mclk-id = "pri_mclk";
+ clocks = <&clock_gcc clk_div_clk2>;
+ pinctrl-0 = <&cdc_mclk2_sleep>;
+ pinctrl-1 = <&cdc_mclk2_active>;
+ };
+
+ wcd_rst_gpio: wcd_gpio_ctrl {
+ status = "disabled";
+ qcom,cdc-rst-n-gpio = <&tlmm 67 0>;
+ };
+};
+
+&slim_msm {
+ status = "disabled";
+ wcd9335: tasha_codec {
+ status = "disabled";
+ compatible = "qcom,tasha-slim-pgd";
+ clock-names = "wcd_clk", "wcd_native_clk";
+ clocks = <&clock_audio clk_audio_pmi_clk>,
+ <&clock_audio clk_audio_ap_clk2>;
+
+ qcom,cdc-reset-gpio = <&tlmm 67 0>;
+
+ cdc-vdd-buck-supply = <&eldo2_8953>;
+ qcom,cdc-vdd-buck-voltage = <1800000 1800000>;
+ qcom,cdc-vdd-buck-current = <650000>;
+
+ cdc-buck-sido-supply = <&eldo2_8953>;
+ qcom,cdc-buck-sido-voltage = <1800000 1800000>;
+ qcom,cdc-buck-sido-current = <150000>;
+
+ cdc-vdd-tx-h-supply = <&eldo2_8953>;
+ qcom,cdc-vdd-tx-h-voltage = <1800000 1800000>;
+ qcom,cdc-vdd-tx-h-current = <25000>;
+
+ cdc-vdd-rx-h-supply = <&eldo2_8953>;
+ qcom,cdc-vdd-rx-h-voltage = <1800000 1800000>;
+ qcom,cdc-vdd-rx-h-current = <25000>;
+
+ cdc-vdd-px-supply = <&eldo2_8953>;
+ qcom,cdc-vdd-px-voltage = <1800000 1800000>;
+ qcom,cdc-vdd-px-current = <10000>;
+
+ cdc-vdd-mic-bias-supply = <&pm8953_l13>;
+ qcom,cdc-vdd-mic-bias-voltage = <3125000 3125000>;
+ qcom,cdc-vdd-mic-bias-current = <15000>;
+ };
+};
+&pm8953_gpios {
+ gpio@c000 {
+ status = "ok";
+ qcom,mode = <1>;
+ qcom,pull = <5>;
+ qcom,vin-sel = <0>;
+ qcom,src-sel = <2>;
+ qcom,master-en = <1>;
+ qcom,out-strength = <2>;
+ };
+};
+
+&pm8953_1 {
+ pmic_analog_codec: analog-codec@f000 {
+ status = "okay";
+ compatible = "qcom,pmic-analog-codec";
+ reg = <0xf000 0x200>;
+ #address-cells = <2>;
+ #size-cells = <0>;
+ interrupt-parent = <&spmi_bus>;
+ interrupts = <0x1 0xf0 0x0 IRQ_TYPE_NONE>,
+ <0x1 0xf0 0x1 IRQ_TYPE_NONE>,
+ <0x1 0xf0 0x2 IRQ_TYPE_NONE>,
+ <0x1 0xf0 0x3 IRQ_TYPE_NONE>,
+ <0x1 0xf0 0x4 IRQ_TYPE_NONE>,
+ <0x1 0xf0 0x5 IRQ_TYPE_NONE>,
+ <0x1 0xf0 0x6 IRQ_TYPE_NONE>,
+ <0x1 0xf0 0x7 IRQ_TYPE_NONE>,
+ <0x1 0xf1 0x0 IRQ_TYPE_NONE>,
+ <0x1 0xf1 0x1 IRQ_TYPE_NONE>,
+ <0x1 0xf1 0x2 IRQ_TYPE_NONE>,
+ <0x1 0xf1 0x3 IRQ_TYPE_NONE>,
+ <0x1 0xf1 0x4 IRQ_TYPE_NONE>,
+ <0x1 0xf1 0x5 IRQ_TYPE_NONE>;
+ interrupt-names = "spk_cnp_int",
+ "spk_clip_int",
+ "spk_ocp_int",
+ "ins_rem_det1",
+ "but_rel_det",
+ "but_press_det",
+ "ins_rem_det",
+ "mbhc_int",
+ "ear_ocp_int",
+ "hphr_ocp_int",
+ "hphl_ocp_det",
+ "ear_cnp_int",
+ "hphr_cnp_int",
+ "hphl_cnp_int";
+
+ cdc-vdda-cp-supply = <&pm8953_s4>;
+ qcom,cdc-vdda-cp-voltage = <1900000 2050000>;
+ qcom,cdc-vdda-cp-current = <500000>;
+
+ cdc-vdd-io-supply = <&pm8953_l5>;
+ qcom,cdc-vdd-io-voltage = <1800000 1800000>;
+ qcom,cdc-vdd-io-current = <5000>;
+
+ cdc-vdd-pa-supply = <&pm8953_s4>;
+ qcom,cdc-vdd-pa-voltage = <1900000 2050000>;
+ qcom,cdc-vdd-pa-current = <260000>;
+
+ cdc-vdd-mic-bias-supply = <&pm8953_l13>;
+ qcom,cdc-vdd-mic-bias-voltage = <3125000 3125000>;
+ qcom,cdc-vdd-mic-bias-current = <5000>;
+
+ qcom,cdc-mclk-clk-rate = <9600000>;
+
+ qcom,cdc-static-supplies = "cdc-vdd-io",
+ "cdc-vdd-pa",
+ "cdc-vdda-cp";
+
+ qcom,cdc-on-demand-supplies = "cdc-vdd-mic-bias";
+
+ msm_digital_codec: msm-dig-codec {
+ compatible = "qcom,msm-digital-codec";
+ reg = <0xc0f0000 0x0>;
+ };
+ };
+};
diff --git a/arch/arm64/boot/dts/qcom/msm8953-camera-sensor-cdp.dtsi b/arch/arm64/boot/dts/qcom/msm8953-camera-sensor-cdp.dtsi
new file mode 100644
index 0000000..a46bce3
--- /dev/null
+++ b/arch/arm64/boot/dts/qcom/msm8953-camera-sensor-cdp.dtsi
@@ -0,0 +1,325 @@
+/*
+ * Copyright (c) 2015-2018, 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.
+ */
+
+&cci {
+ actuator0: qcom,actuator@0 {
+ cell-index = <0>;
+ reg = <0x0>;
+ compatible = "qcom,actuator";
+ qcom,cci-master = <0>;
+ cam_vaf-supply = <&pm8953_l17>;
+ qcom,cam-vreg-name = "cam_vaf";
+ qcom,cam-vreg-min-voltage = <2850000>;
+ qcom,cam-vreg-max-voltage = <2850000>;
+ qcom,cam-vreg-op-mode = <80000>;
+ };
+
+ actuator1: qcom,actuator@1 {
+ cell-index = <1>;
+ reg = <0x1>;
+ compatible = "qcom,actuator";
+ qcom,cci-master = <1>;
+ cam_vaf-supply = <&pm8953_l17>;
+ qcom,cam-vreg-name = "cam_vaf";
+ qcom,cam-vreg-min-voltage = <2850000>;
+ qcom,cam-vreg-max-voltage = <2850000>;
+ qcom,cam-vreg-op-mode = <80000>;
+ };
+
+ eeprom0: qcom,eeprom@0 {
+ cell-index = <0>;
+ compatible = "qcom,eeprom";
+ qcom,cci-master = <0>;
+ reg = <0x0>;
+ cam_vio-supply = <&pm8953_l6>;
+ cam_vdig-supply = <&pm8953_l2>;
+ cam_vaf-supply = <&pm8953_l17>;
+ qcom,cam-vreg-name = "cam_vio", "cam_vdig", "cam_vaf";
+ qcom,cam-vreg-min-voltage = <0 1100000 2850000>;
+ qcom,cam-vreg-max-voltage = <0 1100000 2850000>;
+ qcom,cam-vreg-op-mode = <0 105000 100000>;
+ pinctrl-names = "cam_default", "cam_suspend";
+ pinctrl-0 = <&cam_sensor_mclk0_default
+ &cam_sensor_rear_default
+ &cam_sensor_rear_vana>;
+ pinctrl-1 = <&cam_sensor_mclk0_sleep &cam_sensor_rear_sleep
+ &cam_sensor_rear_vana_sleep>;
+ gpios = <&tlmm 26 0>,
+ <&tlmm 40 0>,
+ <&tlmm 39 0>,
+ <&tlmm 134 0>;
+ qcom,gpio-reset = <1>;
+ qcom,gpio-standby = <2>;
+ qcom,gpio-vana = <3>;
+ qcom,gpio-req-tbl-num = <0 1 2 3>;
+ qcom,gpio-req-tbl-flags = <1 0 0 0>;
+ qcom,gpio-req-tbl-label = "CAMIF_MCLK0",
+ "CAM_RESET0",
+ "CAM_STANDBY0",
+ "CAM_VANA";
+ status = "ok";
+ clocks = <&clock_gcc clk_mclk0_clk_src>,
+ <&clock_gcc clk_gcc_camss_mclk0_clk>;
+ clock-names = "cam_src_clk", "cam_clk";
+ qcom,clock-rates = <19200000 0>;
+ };
+
+ eeprom1: qcom,eeprom@1 {
+ cell-index = <1>;
+ reg = <0x1>;
+ qcom,eeprom-name = "sunny_8865";
+ compatible = "qcom,eeprom";
+ qcom,slave-addr = <0x6c>;
+ qcom,cci-master = <0>;
+ qcom,num-blocks = <8>;
+
+ qcom,page0 = <1 0x0100 2 0x01 1 1>;
+ qcom,poll0 = <0 0x0 2 0x0 1 0>;
+ qcom,mem0 = <0 0x0 2 0x0 1 0>;
+
+ qcom,page1 = <1 0x5002 2 0x00 1 0>;
+ qcom,poll1 = <0 0x0 2 0x0 1 0>;
+ qcom,mem1 = <0 0x0 2 0x0 1 0>;
+
+ qcom,page2 = <1 0x3d84 2 0xc0 1 0>;
+ qcom,poll2 = <0 0x0 2 0x0 1 0>;
+ qcom,mem2 = <0 0x0 2 0x0 1 0>;
+
+ qcom,page3 = <1 0x3d88 2 0x70 1 0>;
+ qcom,poll3 = <0 0x0 2 0x0 1 0>;
+ qcom,mem3 = <0 0x0 2 0x0 1 0>;
+
+ qcom,page4 = <1 0x3d89 2 0x10 1 0>;
+ qcom,poll4 = <0 0x0 2 0x0 1 0>;
+ qcom,mem4 = <0 0x0 2 0x0 1 0>;
+
+ qcom,page5 = <1 0x3d8a 2 0x70 1 0>;
+ qcom,poll5 = <0 0x0 2 0x0 1 0>;
+ qcom,mem5 = <0 0x0 2 0x0 1 0>;
+
+ qcom,page6 = <1 0x3d8b 2 0xf4 1 0>;
+ qcom,poll6 = <0 0x0 2 0x0 1 0>;
+ qcom,mem6 = <0 0x0 2 0x0 1 0>;
+
+ qcom,page7 = <1 0x3d81 2 0x01 1 10>;
+ qcom,poll7 = <0 0x0 2 0x0 1 1>;
+ qcom,mem7 = <1536 0x7010 2 0 1 0>;
+
+ cam_vdig-supply = <&pm8953_l23>;
+ cam_vana-supply = <&pm8953_l22>;
+ cam_vio-supply = <&pm8953_l6>;
+ cam_vaf-supply = <&pm8953_l17>;
+ qcom,cam-vreg-name = "cam_vdig", "cam_vio", "cam_vana",
+ "cam_vaf";
+ qcom,cam-vreg-min-voltage = <1200000 0 2800000 2850000>;
+ qcom,cam-vreg-max-voltage = <1200000 0 2800000 2850000>;
+ qcom,cam-vreg-op-mode = <105000 0 80000 100000>;
+ qcom,gpio-no-mux = <0>;
+ pinctrl-names = "cam_default", "cam_suspend";
+ pinctrl-0 = <&cam_sensor_mclk1_default
+ &cam_sensor_front1_default>;
+ pinctrl-1 = <&cam_sensor_mclk1_sleep &cam_sensor_front1_sleep>;
+ gpios = <&tlmm 27 0>,
+ <&tlmm 129 0>,
+ <&tlmm 130 0>;
+ qcom,gpio-reset = <1>;
+ qcom,gpio-standby = <2>;
+ qcom,gpio-req-tbl-num = <0 1 2>;
+ qcom,gpio-req-tbl-flags = <1 0 0>;
+ qcom,gpio-req-tbl-label = "CAMIF_MCLK2",
+ "CAM_RESET2",
+ "CAM_STANDBY2";
+ qcom,cam-power-seq-type = "sensor_vreg", "sensor_vreg",
+ "sensor_vreg",
+ "sensor_gpio", "sensor_gpio" , "sensor_clk";
+ qcom,cam-power-seq-val = "cam_vdig", "cam_vana", "cam_vio",
+ "sensor_gpio_reset", "sensor_gpio_standby",
+ "sensor_cam_mclk";
+ qcom,cam-power-seq-cfg-val = <1 1 1 1 1 24000000>;
+ qcom,cam-power-seq-delay = <1 1 1 30 30 5>;
+ status = "ok";
+ clocks = <&clock_gcc clk_mclk1_clk_src>,
+ <&clock_gcc clk_gcc_camss_mclk1_clk>;
+ clock-names = "cam_src_clk", "cam_clk";
+ qcom,clock-rates = <19200000 0>;
+ };
+
+ eeprom2: qcom,eeprom@2 {
+ cell-index = <2>;
+ compatible = "qcom,eeprom";
+ qcom,cci-master = <1>;
+ reg = <0x2>;
+ cam_vdig-supply = <&pm8953_l23>;
+ cam_vana-supply = <&pm8953_l22>;
+ cam_vio-supply = <&pm8953_l6>;
+ cam_vaf-supply = <&pm8953_l17>;
+ qcom,cam-vreg-name = "cam_vdig", "cam_vio", "cam_vana",
+ "cam_vaf";
+ qcom,cam-vreg-min-voltage = <1200000 0 2800000 2850000>;
+ qcom,cam-vreg-max-voltage = <1200000 0 2800000 2850000>;
+ qcom,cam-vreg-op-mode = <200000 0 80000 100000>;
+ pinctrl-names = "cam_default", "cam_suspend";
+ pinctrl-0 = <&cam_sensor_mclk2_default
+ &cam_sensor_front_default>;
+ pinctrl-1 = <&cam_sensor_mclk2_sleep
+ &cam_sensor_front_sleep>;
+ gpios = <&tlmm 28 0>,
+ <&tlmm 131 0>,
+ <&tlmm 132 0>;
+ qcom,gpio-reset = <1>;
+ qcom,gpio-standby = <2>;
+ qcom,gpio-req-tbl-num = <0 1 2>;
+ qcom,gpio-req-tbl-flags = <1 0 0>;
+ qcom,gpio-req-tbl-label = "CAMIF_MCLK1",
+ "CAM_RESET1",
+ "CAM_STANDBY1";
+ status = "ok";
+ clocks = <&clock_gcc clk_mclk2_clk_src>,
+ <&clock_gcc clk_gcc_camss_mclk2_clk>;
+ clock-names = "cam_src_clk", "cam_clk";
+ qcom,clock-rates = <19200000 0>;
+ };
+
+ qcom,camera@0 {
+ cell-index = <0>;
+ compatible = "qcom,camera";
+ reg = <0x0>;
+ qcom,csiphy-sd-index = <0>;
+ qcom,csid-sd-index = <0>;
+ qcom,mount-angle = <270>;
+ qcom,led-flash-src = <&led_flash0>;
+ qcom,eeprom-src = <&eeprom0>;
+ qcom,actuator-src = <&actuator0>;
+ cam_vio-supply = <&pm8953_l6>;
+ cam_vdig-supply = <&pm8953_l2>;
+ cam_vaf-supply = <&pm8953_l17>;
+ qcom,cam-vreg-name = "cam_vio", "cam_vdig", "cam_vaf";
+ qcom,cam-vreg-min-voltage = <0 1100000 2850000>;
+ qcom,cam-vreg-max-voltage = <0 1100000 2850000>;
+ qcom,cam-vreg-op-mode = <0 105000 100000>;
+ pinctrl-names = "cam_default", "cam_suspend";
+ pinctrl-0 = <&cam_sensor_mclk0_default
+ &cam_sensor_rear_default
+ &cam_sensor_rear_vana>;
+ pinctrl-1 = <&cam_sensor_mclk0_sleep &cam_sensor_rear_sleep
+ &cam_sensor_rear_vana_sleep>;
+ gpios = <&tlmm 26 0>,
+ <&tlmm 40 0>,
+ <&tlmm 39 0>,
+ <&tlmm 134 0>;
+ qcom,gpio-reset = <1>;
+ qcom,gpio-standby = <2>;
+ qcom,gpio-vana = <3>;
+ qcom,gpio-req-tbl-num = <0 1 2 3>;
+ qcom,gpio-req-tbl-flags = <1 0 0 0>;
+ qcom,gpio-req-tbl-label = "CAMIF_MCLK0",
+ "CAM_RESET0",
+ "CAM_STANDBY0",
+ "CAM_VANA";
+ qcom,sensor-position = <0>;
+ qcom,sensor-mode = <0>;
+ qcom,cci-master = <0>;
+ status = "ok";
+ clocks = <&clock_gcc clk_mclk0_clk_src>,
+ <&clock_gcc clk_gcc_camss_mclk0_clk>;
+ clock-names = "cam_src_clk", "cam_clk";
+ qcom,clock-rates = <24000000 0>;
+ };
+
+ qcom,camera@1 {
+ cell-index = <1>;
+ compatible = "qcom,camera";
+ reg = <0x1>;
+ qcom,csiphy-sd-index = <1>;
+ qcom,csid-sd-index = <1>;
+ qcom,mount-angle = <90>;
+ qcom,eeprom-src = <&eeprom2>;
+ qcom,actuator-src = <&actuator1>;
+ cam_vdig-supply = <&pm8953_l23>;
+ cam_vana-supply = <&pm8953_l22>;
+ cam_vio-supply = <&pm8953_l6>;
+ cam_vaf-supply = <&pm8953_l17>;
+ qcom,cam-vreg-name = "cam_vdig", "cam_vio", "cam_vana",
+ "cam_vaf";
+ qcom,cam-vreg-min-voltage = <1200000 0 2800000 2850000>;
+ qcom,cam-vreg-max-voltage = <1200000 0 2800000 2850000>;
+ qcom,cam-vreg-op-mode = <200000 0 80000 100000>;
+ pinctrl-names = "cam_default", "cam_suspend";
+ pinctrl-0 = <&cam_sensor_mclk2_default
+ &cam_sensor_front_default>;
+ pinctrl-1 = <&cam_sensor_mclk2_sleep
+ &cam_sensor_front_sleep>;
+ gpios = <&tlmm 28 0>,
+ <&tlmm 131 0>,
+ <&tlmm 132 0>;
+ qcom,gpio-reset = <1>;
+ qcom,gpio-standby = <2>;
+ qcom,gpio-req-tbl-num = <0 1 2>;
+ qcom,gpio-req-tbl-flags = <1 0 0>;
+ qcom,gpio-req-tbl-label = "CAMIF_MCLK1",
+ "CAM_RESET1",
+ "CAM_STANDBY1";
+ qcom,sensor-position = <0x100>;
+ qcom,sensor-mode = <1>;
+ qcom,cci-master = <1>;
+ status = "ok";
+ clocks = <&clock_gcc clk_mclk2_clk_src>,
+ <&clock_gcc clk_gcc_camss_mclk2_clk>;
+ clock-names = "cam_src_clk", "cam_clk";
+ qcom,clock-rates = <24000000 0>;
+ };
+
+ qcom,camera@2 {
+ cell-index = <2>;
+ compatible = "qcom,camera";
+ reg = <0x02>;
+ qcom,csiphy-sd-index = <2>;
+ qcom,csid-sd-index = <2>;
+ qcom,mount-angle = <90>;
+ qcom,eeprom-src = <&eeprom1>;
+ qcom,actuator-src = <&actuator1>;
+ cam_vdig-supply = <&pm8953_l23>;
+ cam_vio-supply = <&pm8953_l6>;
+ cam_vana-supply = <&pm8953_l22>;
+ cam_vaf-supply = <&pm8953_l17>;
+ qcom,cam-vreg-name = "cam_vdig", "cam_vio", "cam_vana",
+ "cam_vaf";
+ qcom,cam-vreg-min-voltage = <1175000 0 2800000 2850000>;
+ qcom,cam-vreg-max-voltage = <1175000 0 2800000 2850000>;
+ qcom,cam-vreg-op-mode = <105000 0 80000 100000>;
+ qcom,gpio-no-mux = <0>;
+ pinctrl-names = "cam_default", "cam_suspend";
+ pinctrl-0 = <&cam_sensor_mclk1_default
+ &cam_sensor_front1_default>;
+ pinctrl-1 = <&cam_sensor_mclk1_sleep
+ &cam_sensor_front1_sleep>;
+ gpios = <&tlmm 27 0>,
+ <&tlmm 129 0>,
+ <&tlmm 130 0>;
+ qcom,gpio-reset = <1>;
+ qcom,gpio-standby = <2>;
+ qcom,gpio-req-tbl-num = <0 1 2>;
+ qcom,gpio-req-tbl-flags = <1 0 0>;
+ qcom,gpio-req-tbl-label = "CAMIF_MCLK2",
+ "CAM_RESET2",
+ "CAM_STANDBY2";
+ qcom,sensor-position = <1>;
+ qcom,sensor-mode = <0>;
+ qcom,cci-master = <1>;
+ status = "ok";
+ clocks = <&clock_gcc clk_mclk1_clk_src>,
+ <&clock_gcc clk_gcc_camss_mclk1_clk>;
+ clock-names = "cam_src_clk", "cam_clk";
+ qcom,clock-rates = <24000000 0>;
+ };
+};
diff --git a/arch/arm64/boot/dts/qcom/msm8953-camera-sensor-mtp.dtsi b/arch/arm64/boot/dts/qcom/msm8953-camera-sensor-mtp.dtsi
new file mode 100644
index 0000000..a7688f0
--- /dev/null
+++ b/arch/arm64/boot/dts/qcom/msm8953-camera-sensor-mtp.dtsi
@@ -0,0 +1,327 @@
+/*
+ * Copyright (c) 2015-2018, 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.
+ */
+
+&cci {
+ actuator0: qcom,actuator@0 {
+ cell-index = <0>;
+ reg = <0x0>;
+ compatible = "qcom,actuator";
+ qcom,cci-master = <0>;
+ cam_vaf-supply = <&pm8953_l17>;
+ qcom,cam-vreg-name = "cam_vaf";
+ qcom,cam-vreg-min-voltage = <2850000>;
+ qcom,cam-vreg-max-voltage = <2850000>;
+ qcom,cam-vreg-op-mode = <80000>;
+ };
+
+ actuator1: qcom,actuator@1 {
+ cell-index = <1>;
+ reg = <0x1>;
+ compatible = "qcom,actuator";
+ qcom,cci-master = <1>;
+ cam_vaf-supply = <&pm8953_l17>;
+ qcom,cam-vreg-name = "cam_vaf";
+ qcom,cam-vreg-min-voltage = <2850000>;
+ qcom,cam-vreg-max-voltage = <2850000>;
+ qcom,cam-vreg-op-mode = <80000>;
+ };
+
+ eeprom0: qcom,eeprom@0 {
+ cell-index = <0>;
+ compatible = "qcom,eeprom";
+ qcom,cci-master = <0>;
+ reg = <0x0>;
+ cam_vio-supply = <&pm8953_l6>;
+ cam_vdig-supply = <&pm8953_l2>;
+ cam_vaf-supply = <&pm8953_l17>;
+ qcom,cam-vreg-name = "cam_vio", "cam_vdig", "cam_vaf";
+ qcom,cam-vreg-min-voltage = <0 1100000 2850000>;
+ qcom,cam-vreg-max-voltage = <0 1100000 2850000>;
+ qcom,cam-vreg-op-mode = <0 105000 100000>;
+ pinctrl-names = "cam_default", "cam_suspend";
+ pinctrl-0 = <&cam_sensor_mclk0_default
+ &cam_sensor_rear_default
+ &cam_sensor_rear_vana>;
+ pinctrl-1 = <&cam_sensor_mclk0_sleep &cam_sensor_rear_sleep
+ &cam_sensor_rear_vana_sleep>;
+ gpios = <&tlmm 26 0>,
+ <&tlmm 40 0>,
+ <&tlmm 39 0>,
+ <&tlmm 134 0>;
+ qcom,gpio-reset = <1>;
+ qcom,gpio-standby = <2>;
+ qcom,gpio-vana = <3>;
+ qcom,gpio-req-tbl-num = <0 1 2 3>;
+ qcom,gpio-req-tbl-flags = <1 0 0 0>;
+ qcom,gpio-req-tbl-label = "CAMIF_MCLK0",
+ "CAM_RESET0",
+ "CAM_STANDBY0",
+ "CAM_VANA";
+ status = "ok";
+ clocks = <&clock_gcc clk_mclk0_clk_src>,
+ <&clock_gcc clk_gcc_camss_mclk0_clk>;
+ clock-names = "cam_src_clk", "cam_clk";
+ qcom,clock-rates = <19200000 0>;
+ };
+
+ eeprom1: qcom,eeprom@1 {
+ cell-index = <1>;
+ reg = <0x1>;
+ qcom,eeprom-name = "sunny_8865";
+ compatible = "qcom,eeprom";
+ qcom,slave-addr = <0x6c>;
+ qcom,cci-master = <0>;
+ qcom,num-blocks = <8>;
+
+ qcom,page0 = <1 0x0100 2 0x01 1 1>;
+ qcom,poll0 = <0 0x0 2 0x0 1 0>;
+ qcom,mem0 = <0 0x0 2 0x0 1 0>;
+
+ qcom,page1 = <1 0x5002 2 0x00 1 0>;
+ qcom,poll1 = <0 0x0 2 0x0 1 0>;
+ qcom,mem1 = <0 0x0 2 0x0 1 0>;
+
+ qcom,page2 = <1 0x3d84 2 0xc0 1 0>;
+ qcom,poll2 = <0 0x0 2 0x0 1 0>;
+ qcom,mem2 = <0 0x0 2 0x0 1 0>;
+
+ qcom,page3 = <1 0x3d88 2 0x70 1 0>;
+ qcom,poll3 = <0 0x0 2 0x0 1 0>;
+ qcom,mem3 = <0 0x0 2 0x0 1 0>;
+
+ qcom,page4 = <1 0x3d89 2 0x10 1 0>;
+ qcom,poll4 = <0 0x0 2 0x0 1 0>;
+ qcom,mem4 = <0 0x0 2 0x0 1 0>;
+
+ qcom,page5 = <1 0x3d8a 2 0x70 1 0>;
+ qcom,poll5 = <0 0x0 2 0x0 1 0>;
+ qcom,mem5 = <0 0x0 2 0x0 1 0>;
+
+ qcom,page6 = <1 0x3d8b 2 0xf4 1 0>;
+ qcom,poll6 = <0 0x0 2 0x0 1 0>;
+ qcom,mem6 = <0 0x0 2 0x0 1 0>;
+
+ qcom,page7 = <1 0x3d81 2 0x01 1 10>;
+ qcom,poll7 = <0 0x0 2 0x0 1 1>;
+ qcom,mem7 = <1536 0x7010 2 0 1 0>;
+
+ cam_vdig-supply = <&pm8953_l23>;
+ cam_vana-supply = <&pm8953_l22>;
+ cam_vio-supply = <&pm8953_l6>;
+ cam_vaf-supply = <&pm8953_l17>;
+ qcom,cam-vreg-name = "cam_vdig", "cam_vio", "cam_vana",
+ "cam_vaf";
+ qcom,cam-vreg-min-voltage = <1200000 0 2800000 2850000>;
+ qcom,cam-vreg-max-voltage = <1200000 0 2800000 2850000>;
+ qcom,cam-vreg-op-mode = <105000 0 80000 100000>;
+ qcom,gpio-no-mux = <0>;
+ pinctrl-names = "cam_default", "cam_suspend";
+ pinctrl-0 = <&cam_sensor_mclk1_default
+ &cam_sensor_front1_default>;
+ pinctrl-1 = <&cam_sensor_mclk1_sleep &cam_sensor_front1_sleep>;
+ gpios = <&tlmm 27 0>,
+ <&tlmm 129 0>,
+ <&tlmm 130 0>;
+ qcom,gpio-reset = <1>;
+ qcom,gpio-standby = <2>;
+ qcom,gpio-req-tbl-num = <0 1 2>;
+ qcom,gpio-req-tbl-flags = <1 0 0>;
+ qcom,gpio-req-tbl-label = "CAMIF_MCLK2",
+ "CAM_RESET2",
+ "CAM_STANDBY2";
+ qcom,cam-power-seq-type = "sensor_vreg", "sensor_vreg",
+ "sensor_vreg",
+ "sensor_gpio", "sensor_gpio" , "sensor_clk";
+ qcom,cam-power-seq-val = "cam_vdig", "cam_vana", "cam_vio",
+ "sensor_gpio_reset", "sensor_gpio_standby",
+ "sensor_cam_mclk";
+ qcom,cam-power-seq-cfg-val = <1 1 1 1 1 24000000>;
+ qcom,cam-power-seq-delay = <1 1 1 30 30 5>;
+ status = "ok";
+ clocks = <&clock_gcc clk_mclk1_clk_src>,
+ <&clock_gcc clk_gcc_camss_mclk1_clk>;
+ clock-names = "cam_src_clk", "cam_clk";
+ qcom,clock-rates = <19200000 0>;
+ };
+
+ eeprom2: qcom,eeprom@2 {
+ cell-index = <2>;
+ compatible = "qcom,eeprom";
+ qcom,cci-master = <1>;
+ reg = <0x2>;
+ cam_vdig-supply = <&pm8953_l23>;
+ cam_vana-supply = <&pm8953_l22>;
+ cam_vio-supply = <&pm8953_l6>;
+ cam_vaf-supply = <&pm8953_l17>;
+ qcom,cam-vreg-name = "cam_vdig", "cam_vio", "cam_vana",
+ "cam_vaf";
+ qcom,cam-vreg-min-voltage = <1200000 0 2800000 2850000>;
+ qcom,cam-vreg-max-voltage = <1200000 0 2800000 2850000>;
+ qcom,cam-vreg-op-mode = <200000 0 80000 100000>;
+ pinctrl-names = "cam_default", "cam_suspend";
+ pinctrl-0 = <&cam_sensor_mclk2_default
+ &cam_sensor_front_default>;
+ pinctrl-1 = <&cam_sensor_mclk2_sleep
+ &cam_sensor_front_sleep>;
+ gpios = <&tlmm 28 0>,
+ <&tlmm 131 0>,
+ <&tlmm 132 0>;
+ qcom,gpio-reset = <1>;
+ qcom,gpio-standby = <2>;
+ qcom,gpio-req-tbl-num = <0 1 2>;
+ qcom,gpio-req-tbl-flags = <1 0 0>;
+ qcom,gpio-req-tbl-label = "CAMIF_MCLK1",
+ "CAM_RESET1",
+ "CAM_STANDBY1";
+ status = "ok";
+ clocks = <&clock_gcc clk_mclk2_clk_src>,
+ <&clock_gcc clk_gcc_camss_mclk2_clk>;
+ clock-names = "cam_src_clk", "cam_clk";
+ qcom,clock-rates = <19200000 0>;
+ };
+
+ qcom,camera@0 {
+ cell-index = <0>;
+ compatible = "qcom,camera";
+ reg = <0x0>;
+ qcom,csiphy-sd-index = <0>;
+ qcom,csid-sd-index = <0>;
+ qcom,mount-angle = <270>;
+ qcom,led-flash-src = <&led_flash0>;
+ qcom,eeprom-src = <&eeprom0>;
+ qcom,actuator-src = <&actuator0>;
+ cam_vio-supply = <&pm8953_l6>;
+ cam_vdig-supply = <&pm8953_l2>;
+ cam_vaf-supply = <&pm8953_l17>;
+ cam_vana-supply = <&pm8953_l22>;
+ qcom,cam-vreg-name = "cam_vio", "cam_vdig", "cam_vaf",
+ "cam_vana";
+ qcom,cam-vreg-min-voltage = <0 1100000 2850000 2800000>;
+ qcom,cam-vreg-max-voltage = <0 1100000 2850000 2800000>;
+ qcom,cam-vreg-op-mode = <0 105000 100000 80000>;
+ pinctrl-names = "cam_default", "cam_suspend";
+ pinctrl-0 = <&cam_sensor_mclk0_default
+ &cam_sensor_rear_default
+ &cam_sensor_rear_vana>;
+ pinctrl-1 = <&cam_sensor_mclk0_sleep &cam_sensor_rear_sleep
+ &cam_sensor_rear_vana_sleep>;
+ gpios = <&tlmm 26 0>,
+ <&tlmm 40 0>,
+ <&tlmm 39 0>,
+ <&tlmm 134 0>;
+ qcom,gpio-reset = <1>;
+ qcom,gpio-standby = <2>;
+ qcom,gpio-vana = <3>;
+ qcom,gpio-req-tbl-num = <0 1 2 3>;
+ qcom,gpio-req-tbl-flags = <1 0 0 0>;
+ qcom,gpio-req-tbl-label = "CAMIF_MCLK0",
+ "CAM_RESET0",
+ "CAM_STANDBY0",
+ "CAM_VANA";
+ qcom,sensor-position = <0>;
+ qcom,sensor-mode = <0>;
+ qcom,cci-master = <0>;
+ status = "ok";
+ clocks = <&clock_gcc clk_mclk0_clk_src>,
+ <&clock_gcc clk_gcc_camss_mclk0_clk>;
+ clock-names = "cam_src_clk", "cam_clk";
+ qcom,clock-rates = <24000000 0>;
+ };
+
+ qcom,camera@1 {
+ cell-index = <1>;
+ compatible = "qcom,camera";
+ reg = <0x1>;
+ qcom,csiphy-sd-index = <1>;
+ qcom,csid-sd-index = <1>;
+ qcom,mount-angle = <90>;
+ qcom,eeprom-src = <&eeprom2>;
+ qcom,actuator-src = <&actuator1>;
+ cam_vdig-supply = <&pm8953_l23>;
+ cam_vana-supply = <&pm8953_l22>;
+ cam_vio-supply = <&pm8953_l6>;
+ cam_vaf-supply = <&pm8953_l17>;
+ qcom,cam-vreg-name = "cam_vdig", "cam_vio", "cam_vana",
+ "cam_vaf";
+ qcom,cam-vreg-min-voltage = <1200000 0 2800000 2850000>;
+ qcom,cam-vreg-max-voltage = <1200000 0 2800000 2850000>;
+ qcom,cam-vreg-op-mode = <200000 0 80000 100000>;
+ pinctrl-names = "cam_default", "cam_suspend";
+ pinctrl-0 = <&cam_sensor_mclk2_default
+ &cam_sensor_front_default>;
+ pinctrl-1 = <&cam_sensor_mclk2_sleep
+ &cam_sensor_front_sleep>;
+ gpios = <&tlmm 28 0>,
+ <&tlmm 131 0>,
+ <&tlmm 132 0>;
+ qcom,gpio-reset = <1>;
+ qcom,gpio-standby = <2>;
+ qcom,gpio-req-tbl-num = <0 1 2>;
+ qcom,gpio-req-tbl-flags = <1 0 0>;
+ qcom,gpio-req-tbl-label = "CAMIF_MCLK1",
+ "CAM_RESET1",
+ "CAM_STANDBY1";
+ qcom,sensor-position = <0x100>;
+ qcom,sensor-mode = <1>;
+ qcom,cci-master = <1>;
+ status = "ok";
+ clocks = <&clock_gcc clk_mclk2_clk_src>,
+ <&clock_gcc clk_gcc_camss_mclk2_clk>;
+ clock-names = "cam_src_clk", "cam_clk";
+ qcom,clock-rates = <24000000 0>;
+ };
+
+ qcom,camera@2 {
+ cell-index = <2>;
+ compatible = "qcom,camera";
+ reg = <0x02>;
+ qcom,csiphy-sd-index = <2>;
+ qcom,csid-sd-index = <2>;
+ qcom,mount-angle = <90>;
+ qcom,eeprom-src = <&eeprom1>;
+ qcom,actuator-src = <&actuator1>;
+ cam_vdig-supply = <&pm8953_l23>;
+ cam_vio-supply = <&pm8953_l6>;
+ cam_vana-supply = <&pm8953_l22>;
+ cam_vaf-supply = <&pm8953_l17>;
+ qcom,cam-vreg-name = "cam_vdig", "cam_vio", "cam_vana",
+ "cam_vaf";
+ qcom,cam-vreg-min-voltage = <1175000 0 2800000 2850000>;
+ qcom,cam-vreg-max-voltage = <1175000 0 2800000 2850000>;
+ qcom,cam-vreg-op-mode = <105000 0 80000 100000>;
+ qcom,gpio-no-mux = <0>;
+ pinctrl-names = "cam_default", "cam_suspend";
+ pinctrl-0 = <&cam_sensor_mclk1_default
+ &cam_sensor_front1_default>;
+ pinctrl-1 = <&cam_sensor_mclk1_sleep
+ &cam_sensor_front1_sleep>;
+ gpios = <&tlmm 27 0>,
+ <&tlmm 129 0>,
+ <&tlmm 130 0>;
+ qcom,gpio-reset = <1>;
+ qcom,gpio-standby = <2>;
+ qcom,gpio-req-tbl-num = <0 1 2>;
+ qcom,gpio-req-tbl-flags = <1 0 0>;
+ qcom,gpio-req-tbl-label = "CAMIF_MCLK2",
+ "CAM_RESET2",
+ "CAM_STANDBY2";
+ qcom,sensor-position = <1>;
+ qcom,sensor-mode = <0>;
+ qcom,cci-master = <1>;
+ status = "ok";
+ clocks = <&clock_gcc clk_mclk1_clk_src>,
+ <&clock_gcc clk_gcc_camss_mclk1_clk>;
+ clock-names = "cam_src_clk", "cam_clk";
+ qcom,clock-rates = <24000000 0>;
+ };
+};
diff --git a/arch/arm64/boot/dts/qcom/msm8953-camera-sensor-qrd.dtsi b/arch/arm64/boot/dts/qcom/msm8953-camera-sensor-qrd.dtsi
new file mode 100644
index 0000000..8098efb
--- /dev/null
+++ b/arch/arm64/boot/dts/qcom/msm8953-camera-sensor-qrd.dtsi
@@ -0,0 +1,229 @@
+/*
+ * Copyright (c) 2015-2018, 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.
+ */
+
+&cci {
+ actuator0: qcom,actuator@0 {
+ cell-index = <0>;
+ reg = <0x0>;
+ compatible = "qcom,actuator";
+ qcom,cci-master = <0>;
+ cam_vaf-supply = <&pm8953_l17>;
+ qcom,cam-vreg-name = "cam_vaf";
+ qcom,cam-vreg-min-voltage = <2850000>;
+ qcom,cam-vreg-max-voltage = <2850000>;
+ qcom,cam-vreg-op-mode = <80000>;
+ };
+
+ eeprom0: qcom,eeprom@0 {
+ cell-index = <0>;
+ compatible = "qcom,eeprom";
+ qcom,cci-master = <0>;
+ reg = <0x0>;
+ cam_vio-supply = <&pm8953_l6>;
+ cam_vdig-supply = <&pm8953_l23>;
+ cam_vaf-supply = <&pm8953_l17>;
+ qcom,cam-vreg-name = "cam_vio", "cam_vdig", "cam_vaf";
+ qcom,cam-vreg-min-voltage = <0 1100000 2850000>;
+ qcom,cam-vreg-max-voltage = <0 1100000 2850000>;
+ qcom,cam-vreg-op-mode = <0 105000 100000>;
+ pinctrl-names = "cam_default", "cam_suspend";
+ pinctrl-0 = <&cam_sensor_mclk0_default
+ &cam_sensor_rear_default
+ &cam_sensor_rear_vana>;
+ pinctrl-1 = <&cam_sensor_mclk0_sleep &cam_sensor_rear_sleep
+ &cam_sensor_rear_vana_sleep>;
+ gpios = <&tlmm 26 0>,
+ <&tlmm 40 0>,
+ <&tlmm 39 0>,
+ <&tlmm 134 0>;
+ qcom,gpio-reset = <1>;
+ qcom,gpio-standby = <2>;
+ qcom,gpio-vana = <3>;
+ qcom,gpio-req-tbl-num = <0 1 2 3>;
+ qcom,gpio-req-tbl-flags = <1 0 0 0>;
+ qcom,gpio-req-tbl-label = "CAMIF_MCLK0",
+ "CAM_RESET0",
+ "CAM_STANDBY0",
+ "CAM_VANA";
+ status = "ok";
+ clocks = <&clock_gcc clk_mclk0_clk_src>,
+ <&clock_gcc clk_gcc_camss_mclk0_clk>;
+ clock-names = "cam_src_clk", "cam_clk";
+ qcom,clock-rates = <19200000 0>;
+ };
+
+ eeprom2: qcom,eeprom@2 {
+ cell-index = <2>;
+ reg = <0x2>;
+ compatible = "qcom,eeprom";
+ qcom,cci-master = <1>;
+ cam_vdig-supply = <&pm8953_l23>;
+ cam_vana-supply = <&pm8953_l22>;
+ cam_vio-supply = <&pm8953_l6>;
+ qcom,cam-vreg-name = "cam_vdig", "cam_vio", "cam_vana";
+ qcom,cam-vreg-min-voltage = <1200000 0 2800000>;
+ qcom,cam-vreg-max-voltage = <1200000 0 2800000>;
+ qcom,cam-vreg-op-mode = <105000 0 80000>;
+ qcom,gpio-no-mux = <0>;
+ pinctrl-names = "cam_default", "cam_suspend";
+ pinctrl-0 = <&cam_sensor_mclk1_default
+ &cam_sensor_front1_default>;
+ pinctrl-1 = <&cam_sensor_mclk1_sleep &cam_sensor_front1_sleep>;
+ gpios = <&tlmm 27 0>,
+ <&tlmm 129 0>,
+ <&tlmm 130 0>;
+ qcom,gpio-reset = <1>;
+ qcom,gpio-standby = <2>;
+ qcom,gpio-req-tbl-num = <0 1 2>;
+ qcom,gpio-req-tbl-flags = <1 0 0>;
+ qcom,gpio-req-tbl-label = "CAMIF_MCLK2",
+ "CAM_RESET2",
+ "CAM_STANDBY2";
+ qcom,sensor-mode = <0>;
+ status = "ok";
+ clocks = <&clock_gcc clk_mclk1_clk_src>,
+ <&clock_gcc clk_gcc_camss_mclk1_clk>;
+ clock-names = "cam_src_clk", "cam_clk";
+ qcom,clock-rates = <19200000 0>;
+ };
+
+ qcom,camera@0 {
+ cell-index = <0>;
+ compatible = "qcom,camera";
+ reg = <0x0>;
+ qcom,csiphy-sd-index = <0>;
+ qcom,csid-sd-index = <0>;
+ qcom,mount-angle = <90>;
+ qcom,led-flash-src = <&led_flash0>;
+ qcom,eeprom-src = <&eeprom0>;
+ qcom,actuator-src = <&actuator0>;
+ cam_vio-supply = <&pm8953_l6>;
+ cam_vdig-supply = <&pm8953_l23>;
+ cam_vaf-supply = <&pm8953_l17>;
+ cam_vana-supply = <&pm8953_l22>;
+ qcom,cam-vreg-name = "cam_vio", "cam_vdig", "cam_vaf",
+ "cam_vana";
+ qcom,cam-vreg-min-voltage = <0 1100000 2850000 2800000>;
+ qcom,cam-vreg-max-voltage = <0 1100000 2850000 2800000>;
+ qcom,cam-vreg-op-mode = <0 105000 100000 80000>;
+ pinctrl-names = "cam_default", "cam_suspend";
+ pinctrl-0 = <&cam_sensor_mclk0_default
+ &cam_sensor_rear_default
+ &cam_sensor_rear_vana>;
+ pinctrl-1 = <&cam_sensor_mclk0_sleep &cam_sensor_rear_sleep
+ &cam_sensor_rear_vana_sleep>;
+ gpios = <&tlmm 26 0>,
+ <&tlmm 40 0>,
+ <&tlmm 39 0>,
+ <&tlmm 134 0>;
+ qcom,gpio-reset = <1>;
+ qcom,gpio-standby = <2>;
+ qcom,gpio-vana = <3>;
+ qcom,gpio-req-tbl-num = <0 1 2 3>;
+ qcom,gpio-req-tbl-flags = <1 0 0 0>;
+ qcom,gpio-req-tbl-label = "CAMIF_MCLK0",
+ "CAM_RESET0",
+ "CAM_STANDBY0",
+ "CAM_VANA";
+ qcom,sensor-position = <0>;
+ qcom,sensor-mode = <0>;
+ qcom,cci-master = <0>;
+ status = "ok";
+ clocks = <&clock_gcc clk_mclk0_clk_src>,
+ <&clock_gcc clk_gcc_camss_mclk0_clk>;
+ clock-names = "cam_src_clk", "cam_clk";
+ qcom,clock-rates = <24000000 0>;
+ };
+
+ qcom,camera@1 {
+ cell-index = <1>;
+ compatible = "qcom,camera";
+ reg = <0x1>;
+ qcom,csiphy-sd-index = <1>;
+ qcom,csid-sd-index = <1>;
+ qcom,mount-angle = <90>;
+ cam_vdig-supply = <&pm8953_l23>;
+ cam_vana-supply = <&pm8953_l22>;
+ cam_vio-supply = <&pm8953_l6>;
+ cam_vaf-supply = <&pm8953_l17>;
+ qcom,cam-vreg-name = "cam_vdig", "cam_vio", "cam_vana",
+ "cam_vaf";
+ qcom,cam-vreg-min-voltage = <1200000 0 2800000 2850000>;
+ qcom,cam-vreg-max-voltage = <1200000 0 2800000 2850000>;
+ qcom,cam-vreg-op-mode = <200000 0 80000 100000>;
+ pinctrl-names = "cam_default", "cam_suspend";
+ pinctrl-0 = <&cam_sensor_mclk2_default
+ &cam_sensor_front_default>;
+ pinctrl-1 = <&cam_sensor_mclk2_sleep
+ &cam_sensor_front_sleep>;
+ gpios = <&tlmm 28 0>,
+ <&tlmm 131 0>,
+ <&tlmm 132 0>;
+ qcom,gpio-reset = <1>;
+ qcom,gpio-standby = <2>;
+ qcom,gpio-req-tbl-num = <0 1 2>;
+ qcom,gpio-req-tbl-flags = <1 0 0>;
+ qcom,gpio-req-tbl-label = "CAMIF_MCLK1",
+ "CAM_RESET1",
+ "CAM_STANDBY1";
+ qcom,sensor-position = <1>;
+ qcom,sensor-mode = <0>;
+ qcom,cci-master = <0>;
+ status = "disabled";
+ clocks = <&clock_gcc clk_mclk2_clk_src>,
+ <&clock_gcc clk_gcc_camss_mclk2_clk>;
+ clock-names = "cam_src_clk", "cam_clk";
+ qcom,clock-rates = <24000000 0>;
+ };
+
+ qcom,camera@2 {
+ cell-index = <2>;
+ compatible = "qcom,camera";
+ reg = <0x02>;
+ qcom,csiphy-sd-index = <2>;
+ qcom,csid-sd-index = <2>;
+ qcom,mount-angle = <270>;
+ qcom,eeprom-src = <&eeprom2>;
+ cam_vdig-supply = <&pm8953_l23>;
+ cam_vana-supply = <&pm8953_l22>;
+ cam_vio-supply = <&pm8953_l6>;
+ qcom,cam-vreg-name = "cam_vdig", "cam_vio", "cam_vana";
+ qcom,cam-vreg-min-voltage = <1200000 0 2800000>;
+ qcom,cam-vreg-max-voltage = <1200000 0 2800000>;
+ qcom,cam-vreg-op-mode = <105000 0 80000>;
+ qcom,gpio-no-mux = <0>;
+ pinctrl-names = "cam_default", "cam_suspend";
+ pinctrl-0 = <&cam_sensor_mclk1_default
+ &cam_sensor_front1_default>;
+ pinctrl-1 = <&cam_sensor_mclk1_sleep
+ &cam_sensor_front1_sleep>;
+ gpios = <&tlmm 27 0>,
+ <&tlmm 129 0>,
+ <&tlmm 130 0>;
+ qcom,gpio-reset = <1>;
+ qcom,gpio-standby = <2>;
+ qcom,gpio-req-tbl-num = <0 1 2>;
+ qcom,gpio-req-tbl-flags = <1 0 0>;
+ qcom,gpio-req-tbl-label = "CAMIF_MCLK2",
+ "CAM_RESET2",
+ "CAM_STANDBY2";
+ qcom,sensor-position = <1>;
+ qcom,sensor-mode = <0>;
+ qcom,cci-master = <1>;
+ status = "ok";
+ clocks = <&clock_gcc clk_mclk1_clk_src>,
+ <&clock_gcc clk_gcc_camss_mclk1_clk>;
+ clock-names = "cam_src_clk", "cam_clk";
+ qcom,clock-rates = <24000000 0>;
+ };
+};
diff --git a/arch/arm64/boot/dts/qcom/msm8953-camera.dtsi b/arch/arm64/boot/dts/qcom/msm8953-camera.dtsi
index 39d67ab..2685f5a 100644
--- a/arch/arm64/boot/dts/qcom/msm8953-camera.dtsi
+++ b/arch/arm64/boot/dts/qcom/msm8953-camera.dtsi
@@ -332,32 +332,31 @@
compatible = "qcom,vfe";
num_child = <2>;
};
-/*
- * qcom,cam_smmu {
- * status = "ok";
- * compatible = "qcom,msm-cam-smmu";
- * msm_cam_smmu_cb1: msm_cam_smmu_cb1 {
- * compatible = "qcom,qsmmu-cam-cb";
- * iommus = <&apps_iommu 0x400>,
- * <&apps_iommu 0x2800>;
- * label = "vfe";
- * qcom,scratch-buf-support;
- * };
- *
- *
- * msm_cam_smmu_cb3: msm_cam_smmu_cb3 {
- * compatible = "qcom,qsmmu-cam-cb";
- * iommus = <&apps_iommu 0x1c00>;
- * label = "cpp";
- * };
- *
- * msm_cam_smmu_cb4: msm_cam_smmu_cb4 {
- * compatible = "qcom,qsmmu-cam-cb";
- * iommus = <&apps_iommu 0x1800>;
- * label = "jpeg_enc0";
- * };
- * };
- */
+
+ qcom,cam_smmu {
+ status = "ok";
+ compatible = "qcom,msm-cam-smmu";
+ msm_cam_smmu_cb1: msm_cam_smmu_cb1 {
+ compatible = "qcom,msm-cam-smmu-cb";
+ iommus = <&apps_iommu 0x400 0x00>,
+ <&apps_iommu 0x2800 0x00>;
+ label = "vfe";
+ qcom,scratch-buf-support;
+ };
+
+ msm_cam_smmu_cb3: msm_cam_smmu_cb3 {
+ compatible = "qcom,msm-cam-smmu-cb";
+ iommus = <&apps_iommu 0x1c00 0x00>;
+ label = "cpp";
+ };
+
+ msm_cam_smmu_cb4: msm_cam_smmu_cb4 {
+ compatible = "qcom,msm-cam-smmu-cb";
+ iommus = <&apps_iommu 0x1800 0x00>;
+ label = "jpeg_enc0";
+ };
+ };
+
qcom,jpeg@1b1c000 {
status = "ok";
cell-index = <0>;
@@ -422,6 +421,8 @@
qcom,clock-rates = <0 180000000 0 0 180000000 0 0>;
qcom,min-clock-rate = <100000000>;
qcom,bus-master = <1>;
+ resets = <&clock_gcc GCC_CAMSS_MICRO_BCR>;
+ reset-names = "micro_iface_reset";
qcom,msm-bus,name = "msm_camera_cpp";
qcom,msm-bus,num-cases = <2>;
qcom,msm-bus,num-paths = <1>;
@@ -429,6 +430,7 @@
<106 512 0 0>,
<106 512 0 0>;
qcom,msm-bus-vector-dyn-vote;
+ qcom,micro-reset;
qcom,cpp-fw-payload-info {
qcom,stripe-base = <156>;
qcom,plane-base = <141>;
diff --git a/arch/arm64/boot/dts/qcom/msm8953-cdp.dts b/arch/arm64/boot/dts/qcom/msm8953-cdp.dts
index 34c5f8f..6105b52 100644
--- a/arch/arm64/boot/dts/qcom/msm8953-cdp.dts
+++ b/arch/arm64/boot/dts/qcom/msm8953-cdp.dts
@@ -17,6 +17,7 @@
#include "pmi8950.dtsi"
#include "msm8953-cdp.dtsi"
#include "msm8953-pmi8950.dtsi"
+#include "msm8953-camera-sensor-cdp.dtsi"
/ {
model = "Qualcomm Technologies, Inc. MSM8953 + PMI8950 CDP";
diff --git a/arch/arm64/boot/dts/qcom/msm8953-cdp.dtsi b/arch/arm64/boot/dts/qcom/msm8953-cdp.dtsi
index 8a0f492..9aa5260 100644
--- a/arch/arm64/boot/dts/qcom/msm8953-cdp.dtsi
+++ b/arch/arm64/boot/dts/qcom/msm8953-cdp.dtsi
@@ -11,6 +11,8 @@
* GNU General Public License for more details.
*/
+#include "msm8953-audio-cdp.dtsi"
+
&blsp1_uart0 {
status = "ok";
pinctrl-names = "default";
@@ -148,6 +150,12 @@
qcom,mdss-dsi-pan-fps-update = "dfps_immediate_porch_mode_vfp";
};
+&dsi_hx8399c_truly_vid {
+ qcom,panel-supply-entries = <&dsi_panel_pwr_supply>;
+ qcom,mdss-dsi-pan-enable-dynamic-fps;
+ qcom,mdss-dsi-pan-fps-update = "dfps_immediate_porch_mode_vfp";
+};
+
&dsi_truly_1080_cmd {
qcom,panel-supply-entries = <&dsi_panel_pwr_supply>;
qcom,ulps-enabled;
@@ -155,3 +163,71 @@
qcom,panel-roi-alignment = <2 2 4 2 1080 2>;
};
+&soc {
+ gpio_keys {
+ compatible = "gpio-keys";
+ input-name = "gpio-keys";
+ pinctrl-names = "tlmm_gpio_key_active","tlmm_gpio_key_suspend";
+ pinctrl-0 = <&gpio_key_active>;
+ pinctrl-1 = <&gpio_key_suspend>;
+
+ camera_focus {
+ label = "camera_focus";
+ gpios = <&tlmm 87 0x1>;
+ linux,input-type = <1>;
+ linux,code = <0x210>;
+ debounce-interval = <15>;
+ };
+
+ camera_snapshot {
+ label = "camera_snapshot";
+ gpios = <&tlmm 86 0x1>;
+ linux,input-type = <1>;
+ linux,code = <0x2fe>;
+ debounce-interval = <15>;
+ };
+
+ vol_up {
+ label = "volume_up";
+ gpios = <&tlmm 85 0x1>;
+ linux,input-type = <1>;
+ linux,code = <115>;
+ debounce-interval = <15>;
+ };
+
+ home {
+ label = "home";
+ gpios = <&tlmm 88 0x1>;
+ linux,input-type = <1>;
+ linux,code = <102>;
+ debounce-interval = <15>;
+ };
+ };
+};
+
+&tlmm {
+ tlmm_gpio_key {
+ gpio_key_active: gpio_key_active {
+ mux {
+ pins = "gpio85", "gpio86", "gpio87", "gpio88";
+ function = "gpio";
+ };
+
+ config {
+ pins = "gpio85", "gpio86", "gpio87", "gpio88";
+ };
+ };
+
+ gpio_key_suspend: gpio_key_suspend {
+ mux {
+ pins = "gpio85", "gpio86", "gpio87", "gpio88";
+ function = "gpio";
+ };
+
+ config {
+ pins = "gpio85", "gpio86", "gpio87", "gpio88";
+ };
+ };
+ };
+};
+
diff --git a/arch/arm64/boot/dts/qcom/msm8953-ext-codec-mtp.dts b/arch/arm64/boot/dts/qcom/msm8953-ext-codec-mtp.dts
index b80583e..59a3136 100644
--- a/arch/arm64/boot/dts/qcom/msm8953-ext-codec-mtp.dts
+++ b/arch/arm64/boot/dts/qcom/msm8953-ext-codec-mtp.dts
@@ -17,6 +17,7 @@
#include "pmi8950.dtsi"
#include "msm8953-mtp.dtsi"
#include "msm8953-pmi8950.dtsi"
+#include "msm8953-camera-sensor-mtp.dtsi"
/ {
model = "Qualcomm Technologies, Inc. MSM8953 + PMI8950 Ext Codec MTP";
diff --git a/arch/arm64/boot/dts/qcom/msm8953-mtp.dts b/arch/arm64/boot/dts/qcom/msm8953-mtp.dts
index 97c6db3..82f6315 100644
--- a/arch/arm64/boot/dts/qcom/msm8953-mtp.dts
+++ b/arch/arm64/boot/dts/qcom/msm8953-mtp.dts
@@ -17,6 +17,7 @@
#include "pmi8950.dtsi"
#include "msm8953-mtp.dtsi"
#include "msm8953-pmi8950.dtsi"
+#include "msm8953-camera-sensor-mtp.dtsi"
/ {
model = "Qualcomm Technologies, Inc. MSM8953 + PMI8950 MTP";
diff --git a/arch/arm64/boot/dts/qcom/msm8953-mtp.dtsi b/arch/arm64/boot/dts/qcom/msm8953-mtp.dtsi
index 8a0f492..65fba16 100644
--- a/arch/arm64/boot/dts/qcom/msm8953-mtp.dtsi
+++ b/arch/arm64/boot/dts/qcom/msm8953-mtp.dtsi
@@ -148,6 +148,12 @@
qcom,mdss-dsi-pan-fps-update = "dfps_immediate_porch_mode_vfp";
};
+&dsi_hx8399c_truly_vid {
+ qcom,panel-supply-entries = <&dsi_panel_pwr_supply>;
+ qcom,mdss-dsi-pan-enable-dynamic-fps;
+ qcom,mdss-dsi-pan-fps-update = "dfps_immediate_porch_mode_vfp";
+};
+
&dsi_truly_1080_cmd {
qcom,panel-supply-entries = <&dsi_panel_pwr_supply>;
qcom,ulps-enabled;
@@ -155,3 +161,36 @@
qcom,panel-roi-alignment = <2 2 4 2 1080 2>;
};
+&soc {
+ gpio_keys {
+ compatible = "gpio-keys";
+ input-name = "gpio-keys";
+ pinctrl-names = "tlmm_gpio_key_active","tlmm_gpio_key_suspend";
+ pinctrl-0 = <&gpio_key_active>;
+ pinctrl-1 = <&gpio_key_suspend>;
+
+ camera_focus {
+ label = "camera_focus";
+ gpios = <&tlmm 87 0x1>;
+ linux,input-type = <1>;
+ linux,code = <0x210>;
+ debounce-interval = <15>;
+ };
+
+ camera_snapshot {
+ label = "camera_snapshot";
+ gpios = <&tlmm 86 0x1>;
+ linux,input-type = <1>;
+ linux,code = <0x2fe>;
+ debounce-interval = <15>;
+ };
+
+ vol_up {
+ label = "volume_up";
+ gpios = <&tlmm 85 0x1>;
+ linux,input-type = <1>;
+ linux,code = <115>;
+ debounce-interval = <15>;
+ };
+ };
+};
diff --git a/arch/arm64/boot/dts/qcom/msm8953-pmi632-cdp-s2.dts b/arch/arm64/boot/dts/qcom/msm8953-pmi632-cdp-s2.dts
index 78ff97f..4639f02 100644
--- a/arch/arm64/boot/dts/qcom/msm8953-pmi632-cdp-s2.dts
+++ b/arch/arm64/boot/dts/qcom/msm8953-pmi632-cdp-s2.dts
@@ -16,6 +16,7 @@
#include "msm8953.dtsi"
#include "sdm450-pmi632-cdp-s2.dtsi"
#include "sdm450-pmi632.dtsi"
+#include "sdm632-camera-sensor-cdp.dtsi"
/ {
model = "Qualcomm Technologies, Inc. msm8953 + PMI632 CDP S2";
diff --git a/arch/arm64/boot/dts/qcom/msm8953-pmi8950.dtsi b/arch/arm64/boot/dts/qcom/msm8953-pmi8950.dtsi
index d81a0a5..e8bdb7a 100644
--- a/arch/arm64/boot/dts/qcom/msm8953-pmi8950.dtsi
+++ b/arch/arm64/boot/dts/qcom/msm8953-pmi8950.dtsi
@@ -11,17 +11,6 @@
* GNU General Public License for more details.
*/
-&soc {
- led_flash0: qcom,camera-flash {
- cell-index = <0>;
- compatible = "qcom,camera-flash";
- qcom,flash-type = <1>;
- qcom,flash-source = <&pmi8950_flash0 &pmi8950_flash1>;
- qcom,torch-source = <&pmi8950_torch0 &pmi8950_torch1>;
- qcom,switch-source = <&pmi8950_switch>;
- };
-};
-
&labibb {
status = "ok";
qpnp,qpnp-labibb-mode = "lcd";
diff --git a/arch/arm64/boot/dts/qcom/msm8953-qrd.dtsi b/arch/arm64/boot/dts/qcom/msm8953-qrd.dtsi
index 2f9e20e..4e1c030 100644
--- a/arch/arm64/boot/dts/qcom/msm8953-qrd.dtsi
+++ b/arch/arm64/boot/dts/qcom/msm8953-qrd.dtsi
@@ -11,35 +11,9 @@
* GNU General Public License for more details.
*/
-
&soc {
i2c@78b7000 { /* BLSP1 QUP3 */
- status = "okay";
- synaptics@4b {
- compatible = "synaptics,dsx-i2c";
- reg = <0x4b>;
- interrupt-parent = <&tlmm>;
- interrupts = <65 0x2008>;
- vdd_ana-supply = <&vdd_vreg>;
- vcc_i2c-supply = <&pm8953_l6>;
- synaptics,pwr-reg-name = "vdd_ana";
- synaptics,bus-reg-name = "vcc_i2c";
- synaptics,irq-gpio = <&tlmm 65 0x2008>;
- synaptics,irq-on-state = <0>;
- synaptics,irq-flags = <0x2008>;
- synaptics,power-delay-ms = <200>;
- synaptics,reset-delay-ms = <200>;
- synaptics,max-y-for-2d = <1919>;
- synaptics,cap-button-codes = <139 158 172>;
- synaptics,vir-button-codes = <139 180 2000 320 160
- 158 540 2000 320 160
- 172 900 2000 320 160>;
- synaptics,resume-in-workqueue;
- /* Underlying clocks used by secure touch */
- clock-names = "iface_clk", "core_clk";
- clocks = <&clock_gcc clk_gcc_blsp1_ahb_clk>,
- <&clock_gcc clk_gcc_blsp1_qup3_i2c_apps_clk>;
- };
+ /delete-node/ synaptics@4b;
};
vdd_vreg: vdd_vreg {
@@ -48,6 +22,21 @@
regulator-name = "vdd_vreg";
};
+ gpio_keys {
+ compatible = "gpio-keys";
+ input-name = "gpio-keys";
+ pinctrl-names = "tlmm_gpio_key_active","tlmm_gpio_key_suspend";
+ pinctrl-0 = <&gpio_key_active>;
+ pinctrl-1 = <&gpio_key_suspend>;
+
+ vol_up {
+ label = "volume_up";
+ gpios = <&tlmm 85 0x1>;
+ linux,input-type = <1>;
+ linux,code = <115>;
+ debounce-interval = <15>;
+ };
+ };
};
&blsp1_uart0 {
diff --git a/arch/arm64/boot/dts/qcom/msm8953-thermal.dtsi b/arch/arm64/boot/dts/qcom/msm8953-thermal.dtsi
index d5a6f52..d6d4427 100644
--- a/arch/arm64/boot/dts/qcom/msm8953-thermal.dtsi
+++ b/arch/arm64/boot/dts/qcom/msm8953-thermal.dtsi
@@ -1087,4 +1087,19 @@
};
};
};
+
+ pa-therm0 {
+ polling-delay-passive = <0>;
+ polling-delay = <0>;
+ thermal-sensors = <&pm8953_adc_tm 0x36>;
+ thermal-governor = "user_space";
+
+ trips {
+ active-config0 {
+ temperature = <65000>;
+ hysteresis = <5000>;
+ type = "passive";
+ };
+ };
+ };
};
diff --git a/arch/arm64/boot/dts/qcom/msm8953-wsa881x.dtsi b/arch/arm64/boot/dts/qcom/msm8953-wsa881x.dtsi
new file mode 100644
index 0000000..671b736
--- /dev/null
+++ b/arch/arm64/boot/dts/qcom/msm8953-wsa881x.dtsi
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2015-2016, 2018, 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.
+ */
+
+&slim_msm {
+ tasha_codec {
+ swr_master {
+ compatible = "qcom,swr-wcd";
+ #address-cells = <2>;
+ #size-cells = <0>;
+
+ wsa881x_211: wsa881x@20170211 {
+ compatible = "qcom,wsa881x";
+ reg = <0x00 0x20170211>;
+ qcom,spkr-sd-n-gpio = <&tlmm 96 0>;
+ };
+
+ wsa881x_212: wsa881x@20170212 {
+ compatible = "qcom,wsa881x";
+ reg = <0x00 0x20170212>;
+ qcom,spkr-sd-n-gpio = <&tlmm 96 0>;
+ };
+
+ wsa881x_213: wsa881x@21170213 {
+ compatible = "qcom,wsa881x";
+ reg = <0x00 0x21170213>;
+ qcom,spkr-sd-n-gpio = <&tlmm 96 0>;
+ };
+
+ wsa881x_214: wsa881x@21170214 {
+ compatible = "qcom,wsa881x";
+ reg = <0x00 0x21170214>;
+ qcom,spkr-sd-n-gpio = <&tlmm 96 0>;
+ };
+ };
+ };
+};
diff --git a/arch/arm64/boot/dts/qcom/msm8953.dtsi b/arch/arm64/boot/dts/qcom/msm8953.dtsi
index aa96539..b4111b9 100644
--- a/arch/arm64/boot/dts/qcom/msm8953.dtsi
+++ b/arch/arm64/boot/dts/qcom/msm8953.dtsi
@@ -1921,6 +1921,7 @@
#include "msm-gdsc-8916.dtsi"
#include "msm8953-thermal.dtsi"
#include "msm8953-camera.dtsi"
+#include "msm8953-audio.dtsi"
&gdsc_venus {
clock-names = "bus_clk", "core_clk";
diff --git a/arch/arm64/boot/dts/qcom/pm8937.dtsi b/arch/arm64/boot/dts/qcom/pm8937.dtsi
index 6a61445..fff83d5 100644
--- a/arch/arm64/boot/dts/qcom/pm8937.dtsi
+++ b/arch/arm64/boot/dts/qcom/pm8937.dtsi
@@ -92,6 +92,24 @@
"pm8937_mpp3", "pm8937_mpp4";
gpio-controller;
#gpio-cells = <2>;
+
+ case_therm {
+ cas_therm_default: cas_therm_default {
+ pins = "mpp4";
+ function = "analog";
+ input-enable;
+ qcom,amux-route = <3>;
+ };
+ };
+
+ pa_therm1 {
+ pa_therm1_default: pa_therm1_default {
+ pins = "mpp2";
+ function = "analog";
+ input-enable;
+ qcom,amux-route = <1>;
+ };
+ };
};
pm8937_gpios: gpios {
@@ -120,6 +138,9 @@
qcom,adc-bit-resolution = <15>;
qcom,adc-vdd-reference = <1800>;
qcom,vadc-poll-eoc;
+ #thermal-sensor-cells = <1>;
+ pinctrl-names = "default";
+ pinctrl-0 = <&pa_therm1_default &cas_therm_default>;
chan@5 {
label = "vcoin";
@@ -307,3 +328,80 @@
};
};
};
+
+&thermal_zones {
+ pa-therm1-adc {
+ polling-delay-passive = <0>;
+ polling-delay = <0>;
+ thermal-sensors = <&pm8937_vadc 0x11>;
+ thermal-governor = "user_space";
+
+ trips {
+ active-config0 {
+ temperature = <65000>;
+ hysteresis = <1000>;
+ type = "passive";
+ };
+ };
+ };
+
+ xo-therm-adc {
+ polling-delay-passive = <0>;
+ polling-delay = <0>;
+ thermal-sensors = <&pm8937_vadc 0x32>;
+ thermal-governor = "user_space";
+
+ trips {
+ active-config0 {
+ temperature = <65000>;
+ hysteresis = <1000>;
+ type = "passive";
+ };
+ };
+ };
+
+ xo-therm-buf-adc {
+ polling-delay-passive = <0>;
+ polling-delay = <0>;
+ thermal-sensors = <&pm8937_vadc 0x3c>;
+ thermal-governor = "user_space";
+
+ trips {
+ active-config0 {
+ temperature = <65000>;
+ hysteresis = <1000>;
+ type = "passive";
+ };
+ };
+ };
+
+ case-therm-adc {
+ polling-delay-passive = <0>;
+ polling-delay = <0>;
+ thermal-sensors = <&pm8937_vadc 0x13>;
+ thermal-governor = "user_space";
+
+ trips {
+ active-config0 {
+ temperature = <65000>;
+ hysteresis = <1000>;
+ type = "passive";
+ };
+ };
+ };
+
+ pa-therm0-adc {
+ polling-delay-passive = <0>;
+ polling-delay = <0>;
+ thermal-sensors = <&pm8937_adc_tm 0x36>;
+ thermal-governor = "user_space";
+
+ trips {
+ active-config0 {
+ temperature = <65000>;
+ hysteresis = <1000>;
+ type = "passive";
+ };
+ };
+ };
+};
diff --git a/arch/arm64/boot/dts/qcom/pm8953.dtsi b/arch/arm64/boot/dts/qcom/pm8953.dtsi
index b26f993..21fdfd3 100644
--- a/arch/arm64/boot/dts/qcom/pm8953.dtsi
+++ b/arch/arm64/boot/dts/qcom/pm8953.dtsi
@@ -248,6 +248,7 @@
qcom,adc-bit-resolution = <15>;
qcom,adc-vdd-reference = <1800>;
qcom,adc_tm-vadc = <&pm8953_vadc>;
+ #thermal-sensor-cells = <1>;
};
pm8953_rtc: qcom,pm8953_rtc {
@@ -295,13 +296,11 @@
#address-cells = <1>;
#size-cells = <1>;
- pm8953_pwm: pwm@bc00 {
+ pm8953_pwm: qcom,pwms@bc00 {
status = "disabled";
- compatible = "qcom,qpnp-pwm";
+ compatible = "qcom,pwm-lpg";
reg = <0xbc00 0x100>;
- reg-names = "qpnp-lpg-channel-base";
- qcom,channel-id = <0>;
- qcom,supported-sizes = <6>, <9>;
+ reg-names = "lpg-base";
#pwm-cells = <2>;
};
};
diff --git a/arch/arm64/boot/dts/qcom/pmi632.dtsi b/arch/arm64/boot/dts/qcom/pmi632.dtsi
index d5a3c35..e263058 100644
--- a/arch/arm64/boot/dts/qcom/pmi632.dtsi
+++ b/arch/arm64/boot/dts/qcom/pmi632.dtsi
@@ -355,6 +355,20 @@
"temp-change-smb";
};
+ qcom,schgm-flash@a600 {
+ reg = <0xa600 0x100>;
+ interrupts =
+ <0x2 0xa6 0x2 IRQ_TYPE_EDGE_RISING>,
+ <0x2 0xa6 0x5 IRQ_TYPE_EDGE_RISING>,
+ <0x2 0xa6 0x6 IRQ_TYPE_EDGE_RISING>,
+ <0x2 0xa6 0x7 IRQ_TYPE_EDGE_BOTH>;
+
+ interrupt-names = "flash-state-change",
+ "ilim1-s1",
+ "ilim2-s2",
+ "vreg-ok";
+ };
+
smb5_vbus: qcom,smb5-vbus {
regulator-name = "smb5-vbus";
};
@@ -408,54 +422,31 @@
qcom,vib-overdrive-volt-uv = <3544000>;
};
- pmi632_pwm_1: pwm@b300 {
- compatible = "qcom,qpnp-pwm";
- reg = <0xb300 0x100>;
- reg-names = "qpnp-lpg-channel-base";
- qcom,channel-id = <1>;
- qcom,supported-sizes = <6>, <9>;
+ pmi632_pwm: qcom,pwms@b300 {
+ compatible = "qcom,pwm-lpg";
+ reg = <0xb300 0x500>;
+ reg-names = "lpg-base";
#pwm-cells = <2>;
- status = "disabled";
};
- pmi632_pwm_2: pwm@b400 {
- compatible = "qcom,qpnp-pwm";
- reg = <0xb400 0x100>;
- reg-names = "qpnp-lpg-channel-base";
- qcom,channel-id = <2>;
- qcom,supported-sizes = <6>, <9>;
- #pwm-cells = <2>;
- status = "disabled";
- };
-
- pmi632_pwm_3: pwm@b500 {
- compatible = "qcom,qpnp-pwm";
- reg = <0xb500 0x100>;
- reg-names = "qpnp-lpg-channel-base";
- qcom,channel-id = <3>;
- qcom,supported-sizes = <6>, <9>;
- #pwm-cells = <2>;
- status = "disabled";
- };
-
- pmi632_pwm_4: pwm@b600 {
- compatible = "qcom,qpnp-pwm";
- reg = <0xb600 0x100>;
- reg-names = "qpnp-lpg-channel-base";
- qcom,channel-id = <4>;
- qcom,supported-sizes = <6>, <9>;
- #pwm-cells = <2>;
- status = "disabled";
- };
-
- pmi632_pwm_5: pwm@b700 {
- compatible = "qcom,qpnp-pwm";
- reg = <0xb700 0x100>;
- reg-names = "qpnp-lpg-channel-base";
- qcom,channel-id = <5>;
- qcom,supported-sizes = <6>, <9>;
- #pwm-cells = <2>;
- status = "disabled";
+ pmi632_rgb: qcom,leds@d000 {
+ compatible = "qcom,tri-led";
+ reg = <0xd000 0x100>;
+ red {
+ label = "red";
+ pwms = <&pmi632_pwm 0 1000000>;
+ led-sources = <0>;
+ };
+ green {
+ label = "green";
+ pwms = <&pmi632_pwm 1 1000000>;
+ led-sources = <1>;
+ };
+ blue {
+ label = "blue";
+ pwms = <&pmi632_pwm 2 1000000>;
+ led-sources = <2>;
+ };
};
pmi632_lcdb: qpnp-lcdb@ec00 {
@@ -482,5 +473,101 @@
regulator-max-microvolt = <6000000>;
};
};
+
+ flash_led: qcom,leds@d300 {
+ compatible = "qcom,qpnp-flash-led-v2";
+ status = "okay";
+ reg = <0xd300 0x100>;
+ label = "flash";
+ interrupts = <0x3 0xd3 0x0 IRQ_TYPE_EDGE_RISING>,
+ <0x3 0xd3 0x3 IRQ_TYPE_EDGE_RISING>,
+ <0x3 0xd3 0x4 IRQ_TYPE_EDGE_RISING>;
+ interrupt-names = "led-fault-irq",
+ "all-ramp-down-done-irq",
+ "all-ramp-up-done-irq";
+ qcom,short-circuit-det;
+ qcom,open-circuit-det;
+ qcom,vph-droop-det;
+ qcom,thermal-derate-en;
+ qcom,thermal-derate-current = <200 500 1000>;
+ qcom,isc-delay = <192>;
+ qcom,pmic-revid = <&pmi632_revid>;
+
+ pmi632_flash0: qcom,flash_0 {
+ label = "flash";
+ qcom,led-name = "led:flash_0";
+ qcom,max-current = <1500>;
+ qcom,default-led-trigger = "flash0_trigger";
+ qcom,id = <0>;
+ qcom,current-ma = <1000>;
+ qcom,duration-ms = <1280>;
+ qcom,ires-ua = <12500>;
+ qcom,hdrm-voltage-mv = <400>;
+ qcom,hdrm-vol-hi-lo-win-mv = <100>;
+ };
+
+ pmi632_flash1: qcom,flash_1 {
+ label = "flash";
+ qcom,led-name = "led:flash_1";
+ qcom,max-current = <1500>;
+ qcom,default-led-trigger = "flash1_trigger";
+ qcom,id = <1>;
+ qcom,current-ma = <1000>;
+ qcom,duration-ms = <1280>;
+ qcom,ires-ua = <12500>;
+ qcom,hdrm-voltage-mv = <400>;
+ qcom,hdrm-vol-hi-lo-win-mv = <100>;
+ };
+
+ pmi632_torch0: qcom,torch_0 {
+ label = "torch";
+ qcom,led-name = "led:torch_0";
+ qcom,max-current = <500>;
+ qcom,default-led-trigger = "torch0_trigger";
+ qcom,id = <0>;
+ qcom,current-ma = <300>;
+ qcom,ires-ua = <12500>;
+ qcom,hdrm-voltage-mv = <400>;
+ qcom,hdrm-vol-hi-lo-win-mv = <100>;
+ };
+
+ pmi632_torch1: qcom,torch_1 {
+ label = "torch";
+ qcom,led-name = "led:torch_1";
+ qcom,max-current = <500>;
+ qcom,default-led-trigger = "torch1_trigger";
+ qcom,id = <1>;
+ qcom,current-ma = <300>;
+ qcom,ires-ua = <12500>;
+ qcom,hdrm-voltage-mv = <400>;
+ qcom,hdrm-vol-hi-lo-win-mv = <100>;
+ };
+
+ pmi632_switch0: qcom,led_switch_0 {
+ label = "switch";
+ qcom,led-name = "led:switch_0";
+ qcom,led-mask = <3>;
+ qcom,default-led-trigger = "switch0_trigger";
+ };
+
+ pmi632_switch1: qcom,led_switch_1 {
+ label = "switch";
+ qcom,led-name = "led:switch_1";
+ qcom,led-mask = <2>;
+ qcom,default-led-trigger = "switch1_trigger";
+ };
+
+ };
+
+ };
+};
+&soc {
+ led_flash0: qcom,camera-flash {
+ cell-index = <0>;
+ compatible = "qcom,camera-flash";
+ qcom,flash-type = <1>;
+ qcom,flash-source = <&pmi632_flash0 &pmi632_flash1>;
+ qcom,torch-source = <&pmi632_torch0 &pmi632_torch1>;
+ qcom,switch-source = <&pmi632_switch0>;
};
};
diff --git a/arch/arm64/boot/dts/qcom/pmi8950.dtsi b/arch/arm64/boot/dts/qcom/pmi8950.dtsi
index e3388c1..6f1f899 100644
--- a/arch/arm64/boot/dts/qcom/pmi8950.dtsi
+++ b/arch/arm64/boot/dts/qcom/pmi8950.dtsi
@@ -50,6 +50,7 @@
qcom,adc-bit-resolution = <15>;
qcom,adc-vdd-reference = <1800>;
qcom,vadc-poll-eoc;
+ #thermal-sensor-cells = <1>;
chan@0 {
label = "usbin";
@@ -609,3 +610,31 @@
};
};
};
+
+&thermal_zones {
+ chg-temp-adc {
+ polling-delay-passive = <0>;
+ polling-delay = <0>;
+ thermal-sensors = <&pmi8950_vadc 0xd>;
+ thermal-governor = "user_space";
+
+ trips {
+ active-config0 {
+ temperature = <65000>;
+ hysteresis = <1000>;
+ type = "passive";
+ };
+ };
+ };
+};
+
+&soc {
+ led_flash0: qcom,camera-flash {
+ cell-index = <0>;
+ compatible = "qcom,camera-flash";
+ qcom,flash-type = <1>;
+ qcom,flash-source = <&pmi8950_flash0 &pmi8950_flash1>;
+ qcom,torch-source = <&pmi8950_torch0 &pmi8950_torch1>;
+ qcom,switch-source = <&pmi8950_switch>;
+ };
+};
diff --git a/arch/arm64/boot/dts/qcom/qcs605-lc-mtp.dtsi b/arch/arm64/boot/dts/qcom/qcs605-lc-mtp.dtsi
index 02b6821..f592c0e 100644
--- a/arch/arm64/boot/dts/qcom/qcs605-lc-mtp.dtsi
+++ b/arch/arm64/boot/dts/qcom/qcs605-lc-mtp.dtsi
@@ -41,3 +41,46 @@
status = "ok";
};
+
+&tlmm {
+ sdc2_cd_on: cd_on {
+ mux {
+ pins = "gpio116";
+ function = "gpio";
+ };
+
+ config {
+ pins = "gpio116";
+ drive-strength = <2>;
+ bias-pull-up;
+ };
+ };
+
+ sdc2_cd_off: cd_off {
+ mux {
+ pins = "gpio116";
+ function = "gpio";
+ };
+
+ config {
+ pins = "gpio116";
+ drive-strength = <2>;
+ bias-disable;
+ };
+ };
+};
+
+&sdhc_2 {
+ /* VDD external regulator is enabled/disabled by pm660_l18 regulator */
+ vdd-io-supply = <&pm660_l18>;
+ qcom,vdd-io-voltage-level = <1800000 2960000>;
+ qcom,vdd-io-current-level = <0 22000>;
+
+ pinctrl-names = "active", "sleep";
+ pinctrl-0 = <&sdc2_clk_on &sdc2_cmd_on &sdc2_data_on &sdc2_cd_on>;
+ pinctrl-1 = <&sdc2_clk_off &sdc2_cmd_off &sdc2_data_off &sdc2_cd_off>;
+
+ cd-gpios = <&tlmm 116 0x1>;
+
+ status = "ok";
+};
diff --git a/arch/arm64/boot/dts/qcom/qcs605-lc.dtsi b/arch/arm64/boot/dts/qcom/qcs605-lc.dtsi
index 2db6129..2bef956 100644
--- a/arch/arm64/boot/dts/qcom/qcs605-lc.dtsi
+++ b/arch/arm64/boot/dts/qcom/qcs605-lc.dtsi
@@ -132,53 +132,70 @@
&soc {
qcom,turing@8300000 {
/delete-property/ vdd_cx-supply;
+ vdd_cx-supply = <&pm8005_s1_level>;
};
qcom,lpass@62400000 {
/delete-property/ vdd_cx-supply;
+ vdd_cx-supply = <&pm8005_s1_level>;
};
};
&clock_cpucc {
/delete-property/ vdd_l3_mx_ao-supply;
/delete-property/ vdd_pwrcl_mx_ao-supply;
+ vdd_l3_mx_ao-supply = <&pm660_s2_level_ao>;
+ vdd_pwrcl_mx_ao-supply = <&pm660_s2_level_ao>;
};
&clock_gcc {
/delete-property/ vdd_cx-supply;
/delete-property/ vdd_cx_ao-supply;
+ vdd_cx-supply = <&pm8005_s1_level>;
+ vdd_cx_ao-supply = <&pm8005_s1_level_ao>;
};
&clock_videocc {
/delete-property/ vdd_cx-supply;
+ vdd_cx-supply = <&pm8005_s1_level>;
};
&clock_camcc {
/delete-property/ vdd_mx-supply;
/delete-property/ vdd_cx-supply;
+ vdd_cx-supply = <&pm8005_s1_level>;
+ vdd_mx-supply = <&pm660_s2_level>;
};
&clock_dispcc {
/delete-property/ vdd_cx-supply;
+ vdd_cx-supply = <&pm8005_s1_level>;
};
&clock_gpucc {
/delete-property/ vdd_mx-supply;
/delete-property/ vdd_cx-supply;
+ vdd_cx-supply = <&pm8005_s1_level>;
+ vdd_mx-supply = <&pm660_s2_level>;
};
&pil_modem {
/delete-property/ vdd_mx-supply;
/delete-property/ vdd_cx-supply;
/delete-property/ vdd_mss-supply;
+ vdd_cx-supply = <&pm8005_s1_level>;
+ vdd_mx-supply = <&pm660_s2_level>;
+ vdd_mss-supply = <&pm8005_s1_level>;
};
&clock_gfx {
/delete-property/ vdd_gfx-supply;
+ vdd_gfx-supply = <&pm8005_s2>;
};
&gpu_gx_gdsc {
/delete-property/ parent-supply;
+ parent-supply = <&pm8005_s2>;
};
&mdss_dsi_phy0 {
diff --git a/arch/arm64/boot/dts/qcom/qcs605.dtsi b/arch/arm64/boot/dts/qcom/qcs605.dtsi
index 23da195..5adbbb8 100644
--- a/arch/arm64/boot/dts/qcom/qcs605.dtsi
+++ b/arch/arm64/boot/dts/qcom/qcs605.dtsi
@@ -83,3 +83,7 @@
};
};
};
+
+&msm_gpu {
+ /delete-node/qcom,gpu-mempools;
+};
diff --git a/arch/arm64/boot/dts/qcom/sda845-svr.dtsi b/arch/arm64/boot/dts/qcom/sda845-svr.dtsi
index fa82be2..51dbe74 100644
--- a/arch/arm64/boot/dts/qcom/sda845-svr.dtsi
+++ b/arch/arm64/boot/dts/qcom/sda845-svr.dtsi
@@ -324,6 +324,10 @@
};
};
+&adsp_mem {
+ size = <0 0xc800000>;
+};
+
&qupv3_se9_2uart {
status = "ok";
};
diff --git a/arch/arm64/boot/dts/qcom/sdm450-cdp.dts b/arch/arm64/boot/dts/qcom/sdm450-cdp.dts
index c55622a..0458650 100644
--- a/arch/arm64/boot/dts/qcom/sdm450-cdp.dts
+++ b/arch/arm64/boot/dts/qcom/sdm450-cdp.dts
@@ -17,6 +17,7 @@
#include "pmi8950.dtsi"
#include "msm8953-cdp.dtsi"
#include "msm8953-pmi8950.dtsi"
+#include "msm8953-camera-sensor-cdp.dtsi"
/ {
model = "Qualcomm Technologies, Inc. SDM450 + PMI8950 CDP";
diff --git a/arch/arm64/boot/dts/qcom/sdm450-mtp.dts b/arch/arm64/boot/dts/qcom/sdm450-mtp.dts
index 5744390..f097895 100644
--- a/arch/arm64/boot/dts/qcom/sdm450-mtp.dts
+++ b/arch/arm64/boot/dts/qcom/sdm450-mtp.dts
@@ -17,6 +17,7 @@
#include "pmi8950.dtsi"
#include "msm8953-mtp.dtsi"
#include "msm8953-pmi8950.dtsi"
+#include "msm8953-camera-sensor-mtp.dtsi"
/ {
model = "Qualcomm Technologies, Inc. SDM450 + PMI8950 MTP";
diff --git a/arch/arm64/boot/dts/qcom/sdm450-pmi632-cdp-s2.dts b/arch/arm64/boot/dts/qcom/sdm450-pmi632-cdp-s2.dts
index 68f02a8..004186b6be 100644
--- a/arch/arm64/boot/dts/qcom/sdm450-pmi632-cdp-s2.dts
+++ b/arch/arm64/boot/dts/qcom/sdm450-pmi632-cdp-s2.dts
@@ -16,6 +16,7 @@
#include "sdm450.dtsi"
#include "sdm450-pmi632-cdp-s2.dtsi"
#include "sdm450-pmi632.dtsi"
+#include "sdm632-camera-sensor-cdp.dtsi"
/ {
model = "Qualcomm Technologies, Inc. SDM450 + PMI632 CDP S2";
diff --git a/arch/arm64/boot/dts/qcom/sdm450-pmi632-cdp-s2.dtsi b/arch/arm64/boot/dts/qcom/sdm450-pmi632-cdp-s2.dtsi
index 220ec20..c47e323 100644
--- a/arch/arm64/boot/dts/qcom/sdm450-pmi632-cdp-s2.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm450-pmi632-cdp-s2.dtsi
@@ -13,3 +13,49 @@
#include "msm8953-cdp.dtsi"
+&mdss_dsi0 {
+ qcom,dsi-pref-prim-pan = <&dsi_hx8399c_truly_vid>;
+ pinctrl-names = "mdss_default", "mdss_sleep";
+ pinctrl-0 = <&mdss_dsi_active &mdss_te_active &bklt_en_default>;
+ pinctrl-1 = <&mdss_dsi_suspend &mdss_te_suspend>;
+
+ qcom,platform-bklight-en-gpio = <&pm8953_gpios 4 0>;
+ lab-supply = <&lcdb_ldo_vreg>;
+ ibb-supply = <&lcdb_ncp_vreg>;
+};
+
+&dsi_truly_1080_vid {
+ qcom,mdss-dsi-bl-pmic-control-type = "bl_ctrl_pwm";
+ qcom,mdss-dsi-bl-pmic-pwm-frequency = <100>;
+ qcom,mdss-dsi-bl-pmic-bank-select = <0>;
+ qcom,mdss-dsi-pwm-gpio = <&pm8953_gpios 8 0>;
+};
+
+&dsi_hx8399c_truly_vid {
+ qcom,mdss-dsi-bl-pmic-control-type = "bl_ctrl_pwm";
+ qcom,mdss-dsi-bl-pmic-pwm-frequency = <100>;
+ qcom,mdss-dsi-bl-pmic-bank-select = <0>;
+ qcom,mdss-dsi-pwm-gpio = <&pm8953_gpios 8 0>;
+};
+
+
+&dsi_panel_pwr_supply {
+ qcom,panel-supply-entry@2 {
+ reg = <2>;
+ qcom,supply-name = "lab";
+ qcom,supply-min-voltage = <4600000>;
+ qcom,supply-max-voltage = <6000000>;
+ qcom,supply-enable-load = <100000>;
+ qcom,supply-disable-load = <100>;
+ };
+ qcom,panel-supply-entry@3 {
+ reg = <3>;
+ qcom,supply-name = "ibb";
+ qcom,supply-min-voltage = <4600000>;
+ qcom,supply-max-voltage = <6000000>;
+ qcom,supply-enable-load = <100000>;
+ qcom,supply-disable-load = <100>;
+ qcom,supply-post-on-sleep = <10>;
+ };
+};
+
diff --git a/arch/arm64/boot/dts/qcom/sdm450-pmi632-mtp-s3.dts b/arch/arm64/boot/dts/qcom/sdm450-pmi632-mtp-s3.dts
index b9aadc1..1a2309f 100644
--- a/arch/arm64/boot/dts/qcom/sdm450-pmi632-mtp-s3.dts
+++ b/arch/arm64/boot/dts/qcom/sdm450-pmi632-mtp-s3.dts
@@ -16,6 +16,7 @@
#include "sdm450.dtsi"
#include "sdm450-pmi632-mtp-s3.dtsi"
#include "sdm450-pmi632.dtsi"
+#include "sdm632-camera-sensor-mtp.dtsi"
/ {
model = "Qualcomm Technologies, Inc. SDM450 + PMI632 MTP S3";
diff --git a/arch/arm64/boot/dts/qcom/sdm450-pmi632-mtp-s3.dtsi b/arch/arm64/boot/dts/qcom/sdm450-pmi632-mtp-s3.dtsi
index adb7f47..d532434 100644
--- a/arch/arm64/boot/dts/qcom/sdm450-pmi632-mtp-s3.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm450-pmi632-mtp-s3.dtsi
@@ -13,3 +13,49 @@
#include "msm8953-mtp.dtsi"
+&mdss_dsi0 {
+ qcom,dsi-pref-prim-pan = <&dsi_hx8399c_truly_vid>;
+ pinctrl-names = "mdss_default", "mdss_sleep";
+ pinctrl-0 = <&mdss_dsi_active &mdss_te_active &bklt_en_default>;
+ pinctrl-1 = <&mdss_dsi_suspend &mdss_te_suspend>;
+
+ qcom,platform-bklight-en-gpio = <&pm8953_gpios 4 0>;
+ lab-supply = <&lcdb_ldo_vreg>;
+ ibb-supply = <&lcdb_ncp_vreg>;
+
+};
+
+&dsi_truly_1080_vid {
+ qcom,mdss-dsi-bl-pmic-control-type = "bl_ctrl_pwm";
+ qcom,mdss-dsi-bl-pmic-pwm-frequency = <100>;
+ qcom,mdss-dsi-bl-pmic-bank-select = <0>;
+ qcom,mdss-dsi-pwm-gpio = <&pm8953_gpios 8 0>;
+};
+
+&dsi_hx8399c_truly_vid {
+ qcom,mdss-dsi-bl-pmic-control-type = "bl_ctrl_pwm";
+ qcom,mdss-dsi-bl-pmic-pwm-frequency = <100>;
+ qcom,mdss-dsi-bl-pmic-bank-select = <0>;
+ qcom,mdss-dsi-pwm-gpio = <&pm8953_gpios 8 0>;
+};
+
+&dsi_panel_pwr_supply {
+ qcom,panel-supply-entry@2 {
+ reg = <2>;
+ qcom,supply-name = "lab";
+ qcom,supply-min-voltage = <4600000>;
+ qcom,supply-max-voltage = <6000000>;
+ qcom,supply-enable-load = <100000>;
+ qcom,supply-disable-load = <100>;
+ };
+ qcom,panel-supply-entry@3 {
+ reg = <3>;
+ qcom,supply-name = "ibb";
+ qcom,supply-min-voltage = <4600000>;
+ qcom,supply-max-voltage = <6000000>;
+ qcom,supply-enable-load = <100000>;
+ qcom,supply-disable-load = <100>;
+ qcom,supply-post-on-sleep = <10>;
+ };
+};
+
diff --git a/arch/arm64/boot/dts/qcom/sdm450-pmi632.dtsi b/arch/arm64/boot/dts/qcom/sdm450-pmi632.dtsi
index c79f7a7..f0f7ad7 100644
--- a/arch/arm64/boot/dts/qcom/sdm450-pmi632.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm450-pmi632.dtsi
@@ -45,3 +45,18 @@
qcom,battery-data = <&mtp_batterydata>;
qcom,rbat-conn-mohm = <20>;
};
+
+&pm8953_gpios {
+ bklt_en {
+ bklt_en_default: bklt_en_default {
+ pins = "gpio4";
+ function = "normal";
+ power-source = <0>;
+ output-high;
+ };
+ };
+};
+
+&pm8953_pwm {
+ status = "ok";
+};
diff --git a/arch/arm64/boot/dts/qcom/sdm450-qrd-sku4.dts b/arch/arm64/boot/dts/qcom/sdm450-qrd-sku4.dts
index 977a978..1dc8874 100644
--- a/arch/arm64/boot/dts/qcom/sdm450-qrd-sku4.dts
+++ b/arch/arm64/boot/dts/qcom/sdm450-qrd-sku4.dts
@@ -16,6 +16,7 @@
#include "sdm450.dtsi"
#include "sdm450-qrd-sku4.dtsi"
#include "sdm450-pmi632.dtsi"
+#include "msm8953-camera-sensor-qrd.dtsi"
/ {
model = "Qualcomm Technologies, Inc. SDM450 + PMI632 QRD SKU4";
@@ -24,3 +25,8 @@
qcom,pmic-id = <0x010016 0x25 0x0 0x0>;
};
+&pmi632_vadc {
+ chan@4a {
+ qcom,scale-function = <22>;
+ };
+};
diff --git a/arch/arm64/boot/dts/qcom/sdm450-qrd-sku4.dtsi b/arch/arm64/boot/dts/qcom/sdm450-qrd-sku4.dtsi
index 9e2981a..d33c42c 100644
--- a/arch/arm64/boot/dts/qcom/sdm450-qrd-sku4.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm450-qrd-sku4.dtsi
@@ -32,6 +32,23 @@
status = "disabled";
};
+&int_codec {
+ status = "okay";
+ qcom,model = "msm8953-sku4-snd-card";
+ qcom,msm-micbias1-ext-cap;
+ qcom,msm-mbhc-hphl-swh = <1>;
+ qcom,msm-mbhc-gnd-swh = <0>;
+ qcom,msm-hs-micbias-type = "internal";
+};
+
+&wsa881x_i2c_f {
+ status = "okay";
+};
+
+&wsa881x_i2c_45 {
+ status = "okay";
+};
+
&tlmm {
pmx_mdss {
mdss_dsi_active: mdss_dsi_active {
@@ -107,3 +124,23 @@
qcom,mdss-dsi-pwm-gpio = <&pm8953_gpios 8 0>;
qcom,panel-supply-entries = <&dsi_panel_pwr_supply>;
};
+
+&soc {
+ gpio_keys {
+ compatible = "gpio-keys";
+ label = "gpio-keys";
+ pinctrl-names = "default", "sleep";
+ pinctrl-0 = <&gpio_key_active>;
+ pinctrl-1 = <&gpio_key_suspend>;
+
+ vol_up {
+ label = "volume_up";
+ gpios = <&tlmm 85 GPIO_ACTIVE_LOW>;
+ linux,input-type = <1>;
+ linux,code = <115>;
+ gpio-key,wakeup;
+ debounce-interval = <15>;
+ linux,can-disable;
+ };
+ };
+};
diff --git a/arch/arm64/boot/dts/qcom/sdm632-camera-sensor-cdp.dtsi b/arch/arm64/boot/dts/qcom/sdm632-camera-sensor-cdp.dtsi
new file mode 100644
index 0000000..e9295ad
--- /dev/null
+++ b/arch/arm64/boot/dts/qcom/sdm632-camera-sensor-cdp.dtsi
@@ -0,0 +1,325 @@
+/*
+ * Copyright (c) 2015-2018, 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.
+ */
+
+&cci {
+ actuator0: qcom,actuator@0 {
+ cell-index = <0>;
+ reg = <0x0>;
+ compatible = "qcom,actuator";
+ qcom,cci-master = <0>;
+ cam_vaf-supply = <&pm8953_l17>;
+ qcom,cam-vreg-name = "cam_vaf";
+ qcom,cam-vreg-min-voltage = <2850000>;
+ qcom,cam-vreg-max-voltage = <2850000>;
+ qcom,cam-vreg-op-mode = <80000>;
+ };
+
+ actuator1: qcom,actuator@1 {
+ cell-index = <1>;
+ reg = <0x1>;
+ compatible = "qcom,actuator";
+ qcom,cci-master = <1>;
+ cam_vaf-supply = <&pm8953_l17>;
+ qcom,cam-vreg-name = "cam_vaf";
+ qcom,cam-vreg-min-voltage = <2850000>;
+ qcom,cam-vreg-max-voltage = <2850000>;
+ qcom,cam-vreg-op-mode = <80000>;
+ };
+
+ eeprom0: qcom,eeprom@0 {
+ cell-index = <0>;
+ compatible = "qcom,eeprom";
+ qcom,cci-master = <0>;
+ reg = <0x0>;
+ cam_vio-supply = <&pm8953_l6>;
+ cam_vdig-supply = <&pm8953_l23>;
+ cam_vaf-supply = <&pm8953_l17>;
+ qcom,cam-vreg-name = "cam_vio", "cam_vdig", "cam_vaf";
+ qcom,cam-vreg-min-voltage = <0 1100000 2850000>;
+ qcom,cam-vreg-max-voltage = <0 1100000 2850000>;
+ qcom,cam-vreg-op-mode = <0 105000 100000>;
+ pinctrl-names = "cam_default", "cam_suspend";
+ pinctrl-0 = <&cam_sensor_mclk0_default
+ &cam_sensor_rear_default
+ &cam_sensor_rear_vana>;
+ pinctrl-1 = <&cam_sensor_mclk0_sleep &cam_sensor_rear_sleep
+ &cam_sensor_rear_vana_sleep>;
+ gpios = <&tlmm 26 0>,
+ <&tlmm 40 0>,
+ <&tlmm 39 0>,
+ <&tlmm 134 0>;
+ qcom,gpio-reset = <1>;
+ qcom,gpio-standby = <2>;
+ qcom,gpio-vana = <3>;
+ qcom,gpio-req-tbl-num = <0 1 2 3>;
+ qcom,gpio-req-tbl-flags = <1 0 0 0>;
+ qcom,gpio-req-tbl-label = "CAMIF_MCLK0",
+ "CAM_RESET0",
+ "CAM_STANDBY0",
+ "CAM_VANA";
+ status = "ok";
+ clocks = <&clock_gcc clk_mclk0_clk_src>,
+ <&clock_gcc clk_gcc_camss_mclk0_clk>;
+ clock-names = "cam_src_clk", "cam_clk";
+ qcom,clock-rates = <19200000 0>;
+ };
+
+ eeprom1: qcom,eeprom@1 {
+ cell-index = <1>;
+ reg = <0x1>;
+ qcom,eeprom-name = "sunny_8865";
+ compatible = "qcom,eeprom";
+ qcom,slave-addr = <0x6c>;
+ qcom,cci-master = <0>;
+ qcom,num-blocks = <8>;
+
+ qcom,page0 = <1 0x0100 2 0x01 1 1>;
+ qcom,poll0 = <0 0x0 2 0x0 1 0>;
+ qcom,mem0 = <0 0x0 2 0x0 1 0>;
+
+ qcom,page1 = <1 0x5002 2 0x00 1 0>;
+ qcom,poll1 = <0 0x0 2 0x0 1 0>;
+ qcom,mem1 = <0 0x0 2 0x0 1 0>;
+
+ qcom,page2 = <1 0x3d84 2 0xc0 1 0>;
+ qcom,poll2 = <0 0x0 2 0x0 1 0>;
+ qcom,mem2 = <0 0x0 2 0x0 1 0>;
+
+ qcom,page3 = <1 0x3d88 2 0x70 1 0>;
+ qcom,poll3 = <0 0x0 2 0x0 1 0>;
+ qcom,mem3 = <0 0x0 2 0x0 1 0>;
+
+ qcom,page4 = <1 0x3d89 2 0x10 1 0>;
+ qcom,poll4 = <0 0x0 2 0x0 1 0>;
+ qcom,mem4 = <0 0x0 2 0x0 1 0>;
+
+ qcom,page5 = <1 0x3d8a 2 0x70 1 0>;
+ qcom,poll5 = <0 0x0 2 0x0 1 0>;
+ qcom,mem5 = <0 0x0 2 0x0 1 0>;
+
+ qcom,page6 = <1 0x3d8b 2 0xf4 1 0>;
+ qcom,poll6 = <0 0x0 2 0x0 1 0>;
+ qcom,mem6 = <0 0x0 2 0x0 1 0>;
+
+ qcom,page7 = <1 0x3d81 2 0x01 1 10>;
+ qcom,poll7 = <0 0x0 2 0x0 1 1>;
+ qcom,mem7 = <1536 0x7010 2 0 1 0>;
+
+ cam_vdig-supply = <&pm8953_l23>;
+ cam_vana-supply = <&pm8953_l22>;
+ cam_vio-supply = <&pm8953_l6>;
+ cam_vaf-supply = <&pm8953_l17>;
+ qcom,cam-vreg-name = "cam_vdig", "cam_vio", "cam_vana",
+ "cam_vaf";
+ qcom,cam-vreg-min-voltage = <1200000 0 2800000 2850000>;
+ qcom,cam-vreg-max-voltage = <1200000 0 2800000 2850000>;
+ qcom,cam-vreg-op-mode = <105000 0 80000 100000>;
+ qcom,gpio-no-mux = <0>;
+ pinctrl-names = "cam_default", "cam_suspend";
+ pinctrl-0 = <&cam_sensor_mclk1_default
+ &cam_sensor_front1_default>;
+ pinctrl-1 = <&cam_sensor_mclk1_sleep &cam_sensor_front1_sleep>;
+ gpios = <&tlmm 27 0>,
+ <&tlmm 129 0>,
+ <&tlmm 130 0>;
+ qcom,gpio-reset = <1>;
+ qcom,gpio-standby = <2>;
+ qcom,gpio-req-tbl-num = <0 1 2>;
+ qcom,gpio-req-tbl-flags = <1 0 0>;
+ qcom,gpio-req-tbl-label = "CAMIF_MCLK2",
+ "CAM_RESET2",
+ "CAM_STANDBY2";
+ qcom,cam-power-seq-type = "sensor_vreg", "sensor_vreg",
+ "sensor_vreg",
+ "sensor_gpio", "sensor_gpio" , "sensor_clk";
+ qcom,cam-power-seq-val = "cam_vdig", "cam_vana", "cam_vio",
+ "sensor_gpio_reset", "sensor_gpio_standby",
+ "sensor_cam_mclk";
+ qcom,cam-power-seq-cfg-val = <1 1 1 1 1 24000000>;
+ qcom,cam-power-seq-delay = <1 1 1 30 30 5>;
+ status = "ok";
+ clocks = <&clock_gcc clk_mclk1_clk_src>,
+ <&clock_gcc clk_gcc_camss_mclk1_clk>;
+ clock-names = "cam_src_clk", "cam_clk";
+ qcom,clock-rates = <19200000 0>;
+ };
+
+ eeprom2: qcom,eeprom@2 {
+ cell-index = <2>;
+ compatible = "qcom,eeprom";
+ qcom,cci-master = <1>;
+ reg = <0x2>;
+ cam_vdig-supply = <&pm8953_l23>;
+ cam_vana-supply = <&pm8953_l22>;
+ cam_vio-supply = <&pm8953_l6>;
+ cam_vaf-supply = <&pm8953_l17>;
+ qcom,cam-vreg-name = "cam_vdig", "cam_vio", "cam_vana",
+ "cam_vaf";
+ qcom,cam-vreg-min-voltage = <1200000 0 2800000 2850000>;
+ qcom,cam-vreg-max-voltage = <1200000 0 2800000 2850000>;
+ qcom,cam-vreg-op-mode = <200000 0 80000 100000>;
+ pinctrl-names = "cam_default", "cam_suspend";
+ pinctrl-0 = <&cam_sensor_mclk2_default
+ &cam_sensor_front_default>;
+ pinctrl-1 = <&cam_sensor_mclk2_sleep
+ &cam_sensor_front_sleep>;
+ gpios = <&tlmm 28 0>,
+ <&tlmm 131 0>,
+ <&tlmm 132 0>;
+ qcom,gpio-reset = <1>;
+ qcom,gpio-standby = <2>;
+ qcom,gpio-req-tbl-num = <0 1 2>;
+ qcom,gpio-req-tbl-flags = <1 0 0>;
+ qcom,gpio-req-tbl-label = "CAMIF_MCLK1",
+ "CAM_RESET1",
+ "CAM_STANDBY1";
+ status = "ok";
+ clocks = <&clock_gcc clk_mclk2_clk_src>,
+ <&clock_gcc clk_gcc_camss_mclk2_clk>;
+ clock-names = "cam_src_clk", "cam_clk";
+ qcom,clock-rates = <19200000 0>;
+ };
+
+ qcom,camera@0 {
+ cell-index = <0>;
+ compatible = "qcom,camera";
+ reg = <0x0>;
+ qcom,csiphy-sd-index = <0>;
+ qcom,csid-sd-index = <0>;
+ qcom,mount-angle = <270>;
+ qcom,led-flash-src = <&led_flash0>;
+ qcom,eeprom-src = <&eeprom0>;
+ qcom,actuator-src = <&actuator0>;
+ cam_vio-supply = <&pm8953_l6>;
+ cam_vdig-supply = <&pm8953_l23>;
+ cam_vaf-supply = <&pm8953_l17>;
+ qcom,cam-vreg-name = "cam_vio", "cam_vdig", "cam_vaf";
+ qcom,cam-vreg-min-voltage = <0 1100000 2850000>;
+ qcom,cam-vreg-max-voltage = <0 1100000 2850000>;
+ qcom,cam-vreg-op-mode = <0 105000 100000>;
+ pinctrl-names = "cam_default", "cam_suspend";
+ pinctrl-0 = <&cam_sensor_mclk0_default
+ &cam_sensor_rear_default
+ &cam_sensor_rear_vana>;
+ pinctrl-1 = <&cam_sensor_mclk0_sleep &cam_sensor_rear_sleep
+ &cam_sensor_rear_vana_sleep>;
+ gpios = <&tlmm 26 0>,
+ <&tlmm 40 0>,
+ <&tlmm 39 0>,
+ <&tlmm 134 0>;
+ qcom,gpio-reset = <1>;
+ qcom,gpio-standby = <2>;
+ qcom,gpio-vana = <3>;
+ qcom,gpio-req-tbl-num = <0 1 2 3>;
+ qcom,gpio-req-tbl-flags = <1 0 0 0>;
+ qcom,gpio-req-tbl-label = "CAMIF_MCLK0",
+ "CAM_RESET0",
+ "CAM_STANDBY0",
+ "CAM_VANA";
+ qcom,sensor-position = <0>;
+ qcom,sensor-mode = <0>;
+ qcom,cci-master = <0>;
+ status = "ok";
+ clocks = <&clock_gcc clk_mclk0_clk_src>,
+ <&clock_gcc clk_gcc_camss_mclk0_clk>;
+ clock-names = "cam_src_clk", "cam_clk";
+ qcom,clock-rates = <24000000 0>;
+ };
+
+ qcom,camera@1 {
+ cell-index = <1>;
+ compatible = "qcom,camera";
+ reg = <0x1>;
+ qcom,csiphy-sd-index = <1>;
+ qcom,csid-sd-index = <1>;
+ qcom,mount-angle = <90>;
+ qcom,eeprom-src = <&eeprom2>;
+ qcom,actuator-src = <&actuator1>;
+ cam_vdig-supply = <&pm8953_l23>;
+ cam_vana-supply = <&pm8953_l22>;
+ cam_vio-supply = <&pm8953_l6>;
+ cam_vaf-supply = <&pm8953_l17>;
+ qcom,cam-vreg-name = "cam_vdig", "cam_vio", "cam_vana",
+ "cam_vaf";
+ qcom,cam-vreg-min-voltage = <1200000 0 2800000 2850000>;
+ qcom,cam-vreg-max-voltage = <1200000 0 2800000 2850000>;
+ qcom,cam-vreg-op-mode = <200000 0 80000 100000>;
+ pinctrl-names = "cam_default", "cam_suspend";
+ pinctrl-0 = <&cam_sensor_mclk2_default
+ &cam_sensor_front_default>;
+ pinctrl-1 = <&cam_sensor_mclk2_sleep
+ &cam_sensor_front_sleep>;
+ gpios = <&tlmm 28 0>,
+ <&tlmm 131 0>,
+ <&tlmm 132 0>;
+ qcom,gpio-reset = <1>;
+ qcom,gpio-standby = <2>;
+ qcom,gpio-req-tbl-num = <0 1 2>;
+ qcom,gpio-req-tbl-flags = <1 0 0>;
+ qcom,gpio-req-tbl-label = "CAMIF_MCLK1",
+ "CAM_RESET1",
+ "CAM_STANDBY1";
+ qcom,sensor-position = <0x100>;
+ qcom,sensor-mode = <1>;
+ qcom,cci-master = <1>;
+ status = "ok";
+ clocks = <&clock_gcc clk_mclk2_clk_src>,
+ <&clock_gcc clk_gcc_camss_mclk2_clk>;
+ clock-names = "cam_src_clk", "cam_clk";
+ qcom,clock-rates = <24000000 0>;
+ };
+
+ qcom,camera@2 {
+ cell-index = <2>;
+ compatible = "qcom,camera";
+ reg = <0x02>;
+ qcom,csiphy-sd-index = <2>;
+ qcom,csid-sd-index = <2>;
+ qcom,mount-angle = <90>;
+ qcom,eeprom-src = <&eeprom1>;
+ qcom,actuator-src = <&actuator1>;
+ cam_vdig-supply = <&pm8953_l23>;
+ cam_vio-supply = <&pm8953_l6>;
+ cam_vana-supply = <&pm8953_l22>;
+ cam_vaf-supply = <&pm8953_l17>;
+ qcom,cam-vreg-name = "cam_vdig", "cam_vio", "cam_vana",
+ "cam_vaf";
+ qcom,cam-vreg-min-voltage = <1175000 0 2800000 2850000>;
+ qcom,cam-vreg-max-voltage = <1175000 0 2800000 2850000>;
+ qcom,cam-vreg-op-mode = <105000 0 80000 100000>;
+ qcom,gpio-no-mux = <0>;
+ pinctrl-names = "cam_default", "cam_suspend";
+ pinctrl-0 = <&cam_sensor_mclk1_default
+ &cam_sensor_front1_default>;
+ pinctrl-1 = <&cam_sensor_mclk1_sleep
+ &cam_sensor_front1_sleep>;
+ gpios = <&tlmm 27 0>,
+ <&tlmm 129 0>,
+ <&tlmm 130 0>;
+ qcom,gpio-reset = <1>;
+ qcom,gpio-standby = <2>;
+ qcom,gpio-req-tbl-num = <0 1 2>;
+ qcom,gpio-req-tbl-flags = <1 0 0>;
+ qcom,gpio-req-tbl-label = "CAMIF_MCLK2",
+ "CAM_RESET2",
+ "CAM_STANDBY2";
+ qcom,sensor-position = <1>;
+ qcom,sensor-mode = <0>;
+ qcom,cci-master = <1>;
+ status = "ok";
+ clocks = <&clock_gcc clk_mclk1_clk_src>,
+ <&clock_gcc clk_gcc_camss_mclk1_clk>;
+ clock-names = "cam_src_clk", "cam_clk";
+ qcom,clock-rates = <24000000 0>;
+ };
+};
diff --git a/arch/arm64/boot/dts/qcom/sdm632-camera-sensor-mtp.dtsi b/arch/arm64/boot/dts/qcom/sdm632-camera-sensor-mtp.dtsi
new file mode 100644
index 0000000..07b3811
--- /dev/null
+++ b/arch/arm64/boot/dts/qcom/sdm632-camera-sensor-mtp.dtsi
@@ -0,0 +1,327 @@
+/*
+ * Copyright (c) 2015-2018, 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.
+ */
+
+&cci {
+ actuator0: qcom,actuator@0 {
+ cell-index = <0>;
+ reg = <0x0>;
+ compatible = "qcom,actuator";
+ qcom,cci-master = <0>;
+ cam_vaf-supply = <&pm8953_l17>;
+ qcom,cam-vreg-name = "cam_vaf";
+ qcom,cam-vreg-min-voltage = <2850000>;
+ qcom,cam-vreg-max-voltage = <2850000>;
+ qcom,cam-vreg-op-mode = <80000>;
+ };
+
+ actuator1: qcom,actuator@1 {
+ cell-index = <1>;
+ reg = <0x1>;
+ compatible = "qcom,actuator";
+ qcom,cci-master = <1>;
+ cam_vaf-supply = <&pm8953_l17>;
+ qcom,cam-vreg-name = "cam_vaf";
+ qcom,cam-vreg-min-voltage = <2850000>;
+ qcom,cam-vreg-max-voltage = <2850000>;
+ qcom,cam-vreg-op-mode = <80000>;
+ };
+
+ eeprom0: qcom,eeprom@0 {
+ cell-index = <0>;
+ compatible = "qcom,eeprom";
+ qcom,cci-master = <0>;
+ reg = <0x0>;
+ cam_vio-supply = <&pm8953_l6>;
+ cam_vdig-supply = <&pm8953_l23>;
+ cam_vaf-supply = <&pm8953_l17>;
+ qcom,cam-vreg-name = "cam_vio", "cam_vdig", "cam_vaf";
+ qcom,cam-vreg-min-voltage = <0 1100000 2850000>;
+ qcom,cam-vreg-max-voltage = <0 1100000 2850000>;
+ qcom,cam-vreg-op-mode = <0 105000 100000>;
+ pinctrl-names = "cam_default", "cam_suspend";
+ pinctrl-0 = <&cam_sensor_mclk0_default
+ &cam_sensor_rear_default
+ &cam_sensor_rear_vana>;
+ pinctrl-1 = <&cam_sensor_mclk0_sleep &cam_sensor_rear_sleep
+ &cam_sensor_rear_vana_sleep>;
+ gpios = <&tlmm 26 0>,
+ <&tlmm 40 0>,
+ <&tlmm 39 0>,
+ <&tlmm 134 0>;
+ qcom,gpio-reset = <1>;
+ qcom,gpio-standby = <2>;
+ qcom,gpio-vana = <3>;
+ qcom,gpio-req-tbl-num = <0 1 2 3>;
+ qcom,gpio-req-tbl-flags = <1 0 0 0>;
+ qcom,gpio-req-tbl-label = "CAMIF_MCLK0",
+ "CAM_RESET0",
+ "CAM_STANDBY0",
+ "CAM_VANA";
+ status = "ok";
+ clocks = <&clock_gcc clk_mclk0_clk_src>,
+ <&clock_gcc clk_gcc_camss_mclk0_clk>;
+ clock-names = "cam_src_clk", "cam_clk";
+ qcom,clock-rates = <19200000 0>;
+ };
+
+ eeprom1: qcom,eeprom@1 {
+ cell-index = <1>;
+ reg = <0x1>;
+ qcom,eeprom-name = "sunny_8865";
+ compatible = "qcom,eeprom";
+ qcom,slave-addr = <0x6c>;
+ qcom,cci-master = <0>;
+ qcom,num-blocks = <8>;
+
+ qcom,page0 = <1 0x0100 2 0x01 1 1>;
+ qcom,poll0 = <0 0x0 2 0x0 1 0>;
+ qcom,mem0 = <0 0x0 2 0x0 1 0>;
+
+ qcom,page1 = <1 0x5002 2 0x00 1 0>;
+ qcom,poll1 = <0 0x0 2 0x0 1 0>;
+ qcom,mem1 = <0 0x0 2 0x0 1 0>;
+
+ qcom,page2 = <1 0x3d84 2 0xc0 1 0>;
+ qcom,poll2 = <0 0x0 2 0x0 1 0>;
+ qcom,mem2 = <0 0x0 2 0x0 1 0>;
+
+ qcom,page3 = <1 0x3d88 2 0x70 1 0>;
+ qcom,poll3 = <0 0x0 2 0x0 1 0>;
+ qcom,mem3 = <0 0x0 2 0x0 1 0>;
+
+ qcom,page4 = <1 0x3d89 2 0x10 1 0>;
+ qcom,poll4 = <0 0x0 2 0x0 1 0>;
+ qcom,mem4 = <0 0x0 2 0x0 1 0>;
+
+ qcom,page5 = <1 0x3d8a 2 0x70 1 0>;
+ qcom,poll5 = <0 0x0 2 0x0 1 0>;
+ qcom,mem5 = <0 0x0 2 0x0 1 0>;
+
+ qcom,page6 = <1 0x3d8b 2 0xf4 1 0>;
+ qcom,poll6 = <0 0x0 2 0x0 1 0>;
+ qcom,mem6 = <0 0x0 2 0x0 1 0>;
+
+ qcom,page7 = <1 0x3d81 2 0x01 1 10>;
+ qcom,poll7 = <0 0x0 2 0x0 1 1>;
+ qcom,mem7 = <1536 0x7010 2 0 1 0>;
+
+ cam_vdig-supply = <&pm8953_l23>;
+ cam_vana-supply = <&pm8953_l22>;
+ cam_vio-supply = <&pm8953_l6>;
+ cam_vaf-supply = <&pm8953_l17>;
+ qcom,cam-vreg-name = "cam_vdig", "cam_vio", "cam_vana",
+ "cam_vaf";
+ qcom,cam-vreg-min-voltage = <1200000 0 2800000 2850000>;
+ qcom,cam-vreg-max-voltage = <1200000 0 2800000 2850000>;
+ qcom,cam-vreg-op-mode = <105000 0 80000 100000>;
+ qcom,gpio-no-mux = <0>;
+ pinctrl-names = "cam_default", "cam_suspend";
+ pinctrl-0 = <&cam_sensor_mclk1_default
+ &cam_sensor_front1_default>;
+ pinctrl-1 = <&cam_sensor_mclk1_sleep &cam_sensor_front1_sleep>;
+ gpios = <&tlmm 27 0>,
+ <&tlmm 129 0>,
+ <&tlmm 130 0>;
+ qcom,gpio-reset = <1>;
+ qcom,gpio-standby = <2>;
+ qcom,gpio-req-tbl-num = <0 1 2>;
+ qcom,gpio-req-tbl-flags = <1 0 0>;
+ qcom,gpio-req-tbl-label = "CAMIF_MCLK2",
+ "CAM_RESET2",
+ "CAM_STANDBY2";
+ qcom,cam-power-seq-type = "sensor_vreg", "sensor_vreg",
+ "sensor_vreg",
+ "sensor_gpio", "sensor_gpio" , "sensor_clk";
+ qcom,cam-power-seq-val = "cam_vdig", "cam_vana", "cam_vio",
+ "sensor_gpio_reset", "sensor_gpio_standby",
+ "sensor_cam_mclk";
+ qcom,cam-power-seq-cfg-val = <1 1 1 1 1 24000000>;
+ qcom,cam-power-seq-delay = <1 1 1 30 30 5>;
+ status = "ok";
+ clocks = <&clock_gcc clk_mclk1_clk_src>,
+ <&clock_gcc clk_gcc_camss_mclk1_clk>;
+ clock-names = "cam_src_clk", "cam_clk";
+ qcom,clock-rates = <19200000 0>;
+ };
+
+ eeprom2: qcom,eeprom@2 {
+ cell-index = <2>;
+ compatible = "qcom,eeprom";
+ qcom,cci-master = <1>;
+ reg = <0x2>;
+ cam_vdig-supply = <&pm8953_l23>;
+ cam_vana-supply = <&pm8953_l22>;
+ cam_vio-supply = <&pm8953_l6>;
+ cam_vaf-supply = <&pm8953_l17>;
+ qcom,cam-vreg-name = "cam_vdig", "cam_vio", "cam_vana",
+ "cam_vaf";
+ qcom,cam-vreg-min-voltage = <1200000 0 2800000 2850000>;
+ qcom,cam-vreg-max-voltage = <1200000 0 2800000 2850000>;
+ qcom,cam-vreg-op-mode = <200000 0 80000 100000>;
+ pinctrl-names = "cam_default", "cam_suspend";
+ pinctrl-0 = <&cam_sensor_mclk2_default
+ &cam_sensor_front_default>;
+ pinctrl-1 = <&cam_sensor_mclk2_sleep
+ &cam_sensor_front_sleep>;
+ gpios = <&tlmm 28 0>,
+ <&tlmm 131 0>,
+ <&tlmm 132 0>;
+ qcom,gpio-reset = <1>;
+ qcom,gpio-standby = <2>;
+ qcom,gpio-req-tbl-num = <0 1 2>;
+ qcom,gpio-req-tbl-flags = <1 0 0>;
+ qcom,gpio-req-tbl-label = "CAMIF_MCLK1",
+ "CAM_RESET1",
+ "CAM_STANDBY1";
+ status = "ok";
+ clocks = <&clock_gcc clk_mclk2_clk_src>,
+ <&clock_gcc clk_gcc_camss_mclk2_clk>;
+ clock-names = "cam_src_clk", "cam_clk";
+ qcom,clock-rates = <19200000 0>;
+ };
+
+ qcom,camera@0 {
+ cell-index = <0>;
+ compatible = "qcom,camera";
+ reg = <0x0>;
+ qcom,csiphy-sd-index = <0>;
+ qcom,csid-sd-index = <0>;
+ qcom,mount-angle = <270>;
+ qcom,led-flash-src = <&led_flash0>;
+ qcom,eeprom-src = <&eeprom0>;
+ qcom,actuator-src = <&actuator0>;
+ cam_vio-supply = <&pm8953_l6>;
+ cam_vdig-supply = <&pm8953_l23>;
+ cam_vaf-supply = <&pm8953_l17>;
+ cam_vana-supply = <&pm8953_l22>;
+ qcom,cam-vreg-name = "cam_vio", "cam_vdig", "cam_vaf",
+ "cam_vana";
+ qcom,cam-vreg-min-voltage = <0 1100000 2850000 2800000>;
+ qcom,cam-vreg-max-voltage = <0 1100000 2850000 2800000>;
+ qcom,cam-vreg-op-mode = <0 105000 100000 80000>;
+ pinctrl-names = "cam_default", "cam_suspend";
+ pinctrl-0 = <&cam_sensor_mclk0_default
+ &cam_sensor_rear_default
+ &cam_sensor_rear_vana>;
+ pinctrl-1 = <&cam_sensor_mclk0_sleep &cam_sensor_rear_sleep
+ &cam_sensor_rear_vana_sleep>;
+ gpios = <&tlmm 26 0>,
+ <&tlmm 40 0>,
+ <&tlmm 39 0>,
+ <&tlmm 134 0>;
+ qcom,gpio-reset = <1>;
+ qcom,gpio-standby = <2>;
+ qcom,gpio-vana = <3>;
+ qcom,gpio-req-tbl-num = <0 1 2 3>;
+ qcom,gpio-req-tbl-flags = <1 0 0 0>;
+ qcom,gpio-req-tbl-label = "CAMIF_MCLK0",
+ "CAM_RESET0",
+ "CAM_STANDBY0",
+ "CAM_VANA";
+ qcom,sensor-position = <0>;
+ qcom,sensor-mode = <0>;
+ qcom,cci-master = <0>;
+ status = "ok";
+ clocks = <&clock_gcc clk_mclk0_clk_src>,
+ <&clock_gcc clk_gcc_camss_mclk0_clk>;
+ clock-names = "cam_src_clk", "cam_clk";
+ qcom,clock-rates = <24000000 0>;
+ };
+
+ qcom,camera@1 {
+ cell-index = <1>;
+ compatible = "qcom,camera";
+ reg = <0x1>;
+ qcom,csiphy-sd-index = <1>;
+ qcom,csid-sd-index = <1>;
+ qcom,mount-angle = <90>;
+ qcom,eeprom-src = <&eeprom2>;
+ qcom,actuator-src = <&actuator1>;
+ cam_vdig-supply = <&pm8953_l23>;
+ cam_vana-supply = <&pm8953_l22>;
+ cam_vio-supply = <&pm8953_l6>;
+ cam_vaf-supply = <&pm8953_l17>;
+ qcom,cam-vreg-name = "cam_vdig", "cam_vio", "cam_vana",
+ "cam_vaf";
+ qcom,cam-vreg-min-voltage = <1200000 0 2800000 2850000>;
+ qcom,cam-vreg-max-voltage = <1200000 0 2800000 2850000>;
+ qcom,cam-vreg-op-mode = <200000 0 80000 100000>;
+ pinctrl-names = "cam_default", "cam_suspend";
+ pinctrl-0 = <&cam_sensor_mclk2_default
+ &cam_sensor_front_default>;
+ pinctrl-1 = <&cam_sensor_mclk2_sleep
+ &cam_sensor_front_sleep>;
+ gpios = <&tlmm 28 0>,
+ <&tlmm 131 0>,
+ <&tlmm 132 0>;
+ qcom,gpio-reset = <1>;
+ qcom,gpio-standby = <2>;
+ qcom,gpio-req-tbl-num = <0 1 2>;
+ qcom,gpio-req-tbl-flags = <1 0 0>;
+ qcom,gpio-req-tbl-label = "CAMIF_MCLK1",
+ "CAM_RESET1",
+ "CAM_STANDBY1";
+ qcom,sensor-position = <0x100>;
+ qcom,sensor-mode = <1>;
+ qcom,cci-master = <1>;
+ status = "ok";
+ clocks = <&clock_gcc clk_mclk2_clk_src>,
+ <&clock_gcc clk_gcc_camss_mclk2_clk>;
+ clock-names = "cam_src_clk", "cam_clk";
+ qcom,clock-rates = <24000000 0>;
+ };
+
+ qcom,camera@2 {
+ cell-index = <2>;
+ compatible = "qcom,camera";
+ reg = <0x02>;
+ qcom,csiphy-sd-index = <2>;
+ qcom,csid-sd-index = <2>;
+ qcom,mount-angle = <90>;
+ qcom,eeprom-src = <&eeprom1>;
+ qcom,actuator-src = <&actuator1>;
+ cam_vdig-supply = <&pm8953_l23>;
+ cam_vio-supply = <&pm8953_l6>;
+ cam_vana-supply = <&pm8953_l22>;
+ cam_vaf-supply = <&pm8953_l17>;
+ qcom,cam-vreg-name = "cam_vdig", "cam_vio", "cam_vana",
+ "cam_vaf";
+ qcom,cam-vreg-min-voltage = <1175000 0 2800000 2850000>;
+ qcom,cam-vreg-max-voltage = <1175000 0 2800000 2850000>;
+ qcom,cam-vreg-op-mode = <105000 0 80000 100000>;
+ qcom,gpio-no-mux = <0>;
+ pinctrl-names = "cam_default", "cam_suspend";
+ pinctrl-0 = <&cam_sensor_mclk1_default
+ &cam_sensor_front1_default>;
+ pinctrl-1 = <&cam_sensor_mclk1_sleep
+ &cam_sensor_front1_sleep>;
+ gpios = <&tlmm 27 0>,
+ <&tlmm 129 0>,
+ <&tlmm 130 0>;
+ qcom,gpio-reset = <1>;
+ qcom,gpio-standby = <2>;
+ qcom,gpio-req-tbl-num = <0 1 2>;
+ qcom,gpio-req-tbl-flags = <1 0 0>;
+ qcom,gpio-req-tbl-label = "CAMIF_MCLK2",
+ "CAM_RESET2",
+ "CAM_STANDBY2";
+ qcom,sensor-position = <1>;
+ qcom,sensor-mode = <0>;
+ qcom,cci-master = <1>;
+ status = "ok";
+ clocks = <&clock_gcc clk_mclk1_clk_src>,
+ <&clock_gcc clk_gcc_camss_mclk1_clk>;
+ clock-names = "cam_src_clk", "cam_clk";
+ qcom,clock-rates = <24000000 0>;
+ };
+};
diff --git a/arch/arm64/boot/dts/qcom/sdm632-qrd-sku4.dts b/arch/arm64/boot/dts/qcom/sdm632-qrd-sku4.dts
index 9f33721..6b90e1d 100644
--- a/arch/arm64/boot/dts/qcom/sdm632-qrd-sku4.dts
+++ b/arch/arm64/boot/dts/qcom/sdm632-qrd-sku4.dts
@@ -24,3 +24,8 @@
qcom,pmic-id = <0x010016 0x25 0xC 0x0>;
};
+&pmi632_vadc {
+ chan@4a {
+ qcom,scale-function = <22>;
+ };
+};
diff --git a/arch/arm64/boot/dts/qcom/sdm670-camera.dtsi b/arch/arm64/boot/dts/qcom/sdm670-camera.dtsi
index 96c4640..d9068b1 100644
--- a/arch/arm64/boot/dts/qcom/sdm670-camera.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm670-camera.dtsi
@@ -338,13 +338,23 @@
};
iova-mem-region-io {
- /* IO region is approximately 3.3 GB */
+ /* IO region is approximately 3 GB */
iova-region-name = "io";
- iova-region-start = <0xd900000>;
- iova-region-len = <0xd2700000>;
+ iova-region-start = <0xd911000>;
+ iova-region-len = <0xd26ef000>;
iova-region-id = <0x3>;
status = "ok";
};
+
+ iova-mem-qdss-region {
+ /* qdss region is approximately 64K */
+ iova-region-name = "qdss";
+ iova-region-start = <0xd900000>;
+ iova-region-len = <0x10000>;
+ iova-region-id = <0x5>;
+ qdss-phy-addr = <0x16790000>;
+ status = "ok";
+ };
};
};
diff --git a/arch/arm64/boot/dts/qcom/sdm670-coresight.dtsi b/arch/arm64/boot/dts/qcom/sdm670-coresight.dtsi
index 6dc5c2c..dc9e54e 100644
--- a/arch/arm64/boot/dts/qcom/sdm670-coresight.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm670-coresight.dtsi
@@ -11,6 +11,19 @@
*/
&soc {
+ csr: csr@6001000 {
+ compatible = "qcom,coresight-csr";
+ reg = <0x6001000 0x1000>;
+ reg-names = "csr-base";
+
+ coresight-name = "coresight-csr";
+
+ qcom,usb-bam-support;
+ qcom,hwctrl-set-support;
+ qcom,set-byte-cntr-support;
+ qcom,blk-size = <1>;
+ };
+
replicator_qdss: replicator@6046000 {
compatible = "arm,primecell";
arm,primecell-periphid = <0x0003b909>;
@@ -59,6 +72,7 @@
coresight-name = "coresight-tmc-etr";
coresight-ctis = <&cti0 &cti8>;
+ coresight-csr = <&csr>;
clocks = <&clock_aop QDSS_CLK>;
clock-names = "apb_pclk";
@@ -117,6 +131,7 @@
reg-names = "tmc-base";
coresight-name = "coresight-tmc-etf-swao";
+ coresight-csr = <&csr>;
clocks = <&clock_aop QDSS_CLK>;
clock-names = "apb_pclk";
@@ -284,6 +299,7 @@
coresight-name = "coresight-tmc-etf";
coresight-ctis = <&cti0 &cti8>;
arm,default-sink;
+ coresight-csr = <&csr>;
clocks = <&clock_aop QDSS_CLK>;
clock-names = "apb_pclk";
@@ -395,22 +411,13 @@
reg-names = "ddr-ch0-cfg", "ddr-ch23-cfg", "ddr-ch0-ctrl",
"ddr-ch23-ctrl";
+ coresight-csr = <&csr>;
coresight-name = "coresight-hwevent";
clocks = <&clock_aop QDSS_CLK>;
clock-names = "apb_pclk";
};
- csr: csr@6001000 {
- compatible = "qcom,coresight-csr";
- reg = <0x6001000 0x1000>;
- reg-names = "csr-base";
-
- coresight-name = "coresight-csr";
-
- qcom,blk-size = <1>;
- };
-
funnel_in0: funnel@0x6041000 {
compatible = "arm,primecell";
arm,primecell-periphid = <0x0003b908>;
diff --git a/arch/arm64/boot/dts/qcom/sdm670-vidc.dtsi b/arch/arm64/boot/dts/qcom/sdm670-vidc.dtsi
index 01d4057..a0b4a22 100644
--- a/arch/arm64/boot/dts/qcom/sdm670-vidc.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm670-vidc.dtsi
@@ -1,4 +1,4 @@
-/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2017-2018, 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
@@ -42,7 +42,7 @@
"bus_clk", "core0_clk", "core0_bus_clk",
"core1_clk", "core1_bus_clk";
qcom,clock-configs = <0x1 0x0 0x0 0x1 0x0 0x1 0x0>;
- qcom,allowed-clock-rates = <100000000 200000000 320000000
+ qcom,allowed-clock-rates = <100000000 200000000 330000000
380000000 444000000 533000000>;
/* Buses */
@@ -138,7 +138,7 @@
qcom,proxy-clock-names = "core_clk", "iface_clk",
"bus_clk", "core0_clk", "core0_bus_clk";
qcom,clock-configs = <0x1 0x0 0x0 0x1 0x0>;
- qcom,allowed-clock-rates = <100000000 200000000 320000000
+ qcom,allowed-clock-rates = <100000000 200000000 330000000
364700000>;
/* Buses */
diff --git a/arch/arm64/boot/dts/qcom/sdm670.dtsi b/arch/arm64/boot/dts/qcom/sdm670.dtsi
index 8993e1f..e576baf 100644
--- a/arch/arm64/boot/dts/qcom/sdm670.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm670.dtsi
@@ -809,6 +809,10 @@
<&apps_smmu 0x716 0x1>;
};
+ qcom_msmhdcp: qcom,msm_hdcp {
+ compatible = "qcom,msm-hdcp";
+ };
+
qcom_crypto: qcrypto@1de0000 {
compatible = "qcom,qcrypto";
reg = <0x1de0000 0x20000>,
@@ -1037,6 +1041,20 @@
vdd_mx-supply = <&pm660l_s1_level>;
#clock-cells = <1>;
#reset-cells = <1>;
+ qcom,cam_cc_csi0phytimer_clk_src-opp-handle = <&cam_csiphy0>;
+ qcom,cam_cc_csi1phytimer_clk_src-opp-handle = <&cam_csiphy1>;
+ qcom,cam_cc_csi2phytimer_clk_src-opp-handle = <&cam_csiphy2>;
+ qcom,cam_cc_cci_clk_src-opp-handle = <&cam_cci>;
+ qcom,cam_cc_ife_0_csid_clk_src-opp-handle = <&cam_csid0>;
+ qcom,cam_cc_ife_0_clk_src-opp-handle = <&cam_vfe0>;
+ qcom,cam_cc_ife_1_csid_clk_src-opp-handle = <&cam_csid1>;
+ qcom,cam_cc_ife_1_clk_src-opp-handle = <&cam_vfe1>;
+ qcom,cam_cc_ife_lite_csid_clk_src-opp-handle = <&cam_csid_lite>;
+ qcom,cam_cc_ife_lite_clk_src-opp-handle = <&cam_vfe_lite>;
+ qcom,cam_cc_icp_clk_src-opp-handle = <&cam_a5>;
+ qcom,cam_cc_ipe_0_clk_src-opp-handle = <&cam_ipe0>;
+ qcom,cam_cc_ipe_1_clk_src-opp-handle = <&cam_ipe1>;
+ qcom,cam_cc_bps_clk_src-opp-handle = <&cam_bps>;
};
clock_dispcc: qcom,dispcc@af00000 {
diff --git a/arch/arm64/boot/dts/qcom/sdm845-camera.dtsi b/arch/arm64/boot/dts/qcom/sdm845-camera.dtsi
index ec1e9c7..fd6a0c7 100644
--- a/arch/arm64/boot/dts/qcom/sdm845-camera.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm845-camera.dtsi
@@ -309,13 +309,23 @@
};
iova-mem-region-io {
- /* IO region is approximately 3.3 GB */
+ /* IO region is approximately 3 GB */
iova-region-name = "io";
- iova-region-start = <0xd900000>;
- iova-region-len = <0xd2700000>;
+ iova-region-start = <0xd911000>;
+ iova-region-len = <0xd26ef000>;
iova-region-id = <0x3>;
status = "ok";
};
+
+ iova-mem-qdss-region {
+ /* qdss region is approximately 64K */
+ iova-region-name = "qdss";
+ iova-region-start = <0xd900000>;
+ iova-region-len = <0x10000>;
+ iova-region-id = <0x5>;
+ qdss-phy-addr = <0x16790000>;
+ status = "ok";
+ };
};
};
diff --git a/arch/arm64/boot/dts/qcom/sdm845-v2-camera.dtsi b/arch/arm64/boot/dts/qcom/sdm845-v2-camera.dtsi
index 05d77d3..4aa6927 100644
--- a/arch/arm64/boot/dts/qcom/sdm845-v2-camera.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm845-v2-camera.dtsi
@@ -248,13 +248,23 @@
};
iova-mem-region-io {
- /* IO region is approximately 3.3 GB */
+ /* IO region is approximately 3 GB */
iova-region-name = "io";
- iova-region-start = <0xd900000>;
- iova-region-len = <0xd2700000>;
+ iova-region-start = <0xd911000>;
+ iova-region-len = <0xd26ef000>;
iova-region-id = <0x3>;
status = "ok";
};
+
+ iova-mem-qdss-region {
+ /* qdss region is approximately 64K */
+ iova-region-name = "qdss";
+ iova-region-start = <0xd900000>;
+ iova-region-len = <0x10000>;
+ iova-region-id = <0x5>;
+ qdss-phy-addr = <0x16790000>;
+ status = "ok";
+ };
};
};
diff --git a/arch/arm64/configs/msm8953-perf_defconfig b/arch/arm64/configs/msm8953-perf_defconfig
index 2b01389..6e73c48 100644
--- a/arch/arm64/configs/msm8953-perf_defconfig
+++ b/arch/arm64/configs/msm8953-perf_defconfig
@@ -291,8 +291,6 @@
# CONFIG_SERIO_SERPORT is not set
# CONFIG_VT is not set
# CONFIG_LEGACY_PTYS is not set
-CONFIG_SERIAL_MSM=y
-CONFIG_SERIAL_MSM_CONSOLE=y
CONFIG_SERIAL_MSM_HS=y
CONFIG_SERIAL_MSM_SMD=y
CONFIG_DIAG_CHAR=y
@@ -423,6 +421,7 @@
CONFIG_USB_MON=y
CONFIG_USB_XHCI_HCD=y
CONFIG_USB_EHCI_HCD=y
+CONFIG_USB_EHCI_MSM=y
CONFIG_USB_EHCI_HCD_PLATFORM=y
CONFIG_USB_OHCI_HCD=y
CONFIG_USB_OHCI_HCD_PLATFORM=y
@@ -451,10 +450,12 @@
CONFIG_USB_GADGET_DEBUG_FILES=y
CONFIG_USB_GADGET_DEBUG_FS=y
CONFIG_USB_GADGET_VBUS_DRAW=500
+CONFIG_USB_CI13XXX_MSM=y
CONFIG_USB_CONFIGFS=y
CONFIG_USB_CONFIGFS_SERIAL=y
CONFIG_USB_CONFIGFS_NCM=y
CONFIG_USB_CONFIGFS_QCRNDIS=y
+CONFIG_USB_CONFIGFS_RNDIS=y
CONFIG_USB_CONFIGFS_RMNET_BAM=y
CONFIG_USB_CONFIGFS_MASS_STORAGE=y
CONFIG_USB_CONFIGFS_F_FS=y
@@ -479,12 +480,15 @@
CONFIG_MMC_SDHCI_MSM=y
CONFIG_MMC_SDHCI_MSM_ICE=y
CONFIG_MMC_CQ_HCI=y
+CONFIG_LEDS_QTI_TRI_LED=y
CONFIG_LEDS_QPNP=y
CONFIG_LEDS_QPNP_FLASH=y
+CONFIG_LEDS_QPNP_FLASH_V2=y
CONFIG_LEDS_QPNP_WLED=y
CONFIG_LEDS_QPNP_HAPTICS=y
CONFIG_LEDS_QPNP_VIBRATOR_LDO=y
CONFIG_LEDS_TRIGGERS=y
+CONFIG_LEDS_TRIGGER_TIMER=y
CONFIG_EDAC=y
CONFIG_EDAC_MM_EDAC=y
CONFIG_RTC_CLASS=y
@@ -547,11 +551,17 @@
CONFIG_DEVFREQ_SPDM=y
CONFIG_PWM=y
CONFIG_PWM_QPNP=y
+CONFIG_PWM_QTI_LPG=y
CONFIG_ARM_GIC_V3_ACL=y
+CONFIG_QTI_MPM=y
CONFIG_ANDROID=y
CONFIG_ANDROID_BINDER_IPC=y
CONFIG_SENSORS_SSC=y
CONFIG_MSM_TZ_LOG=y
+CONFIG_EXT2_FS=y
+CONFIG_EXT2_FS_XATTR=y
+CONFIG_EXT3_FS=y
+CONFIG_EXT4_FS_SECURITY=y
CONFIG_QUOTA=y
CONFIG_QUOTA_NETLINK_INTERFACE=y
CONFIG_QFMT_V2=y
diff --git a/arch/arm64/configs/msm8953_defconfig b/arch/arm64/configs/msm8953_defconfig
index f95beaa..2e924d4 100644
--- a/arch/arm64/configs/msm8953_defconfig
+++ b/arch/arm64/configs/msm8953_defconfig
@@ -434,6 +434,7 @@
CONFIG_USB_MON=y
CONFIG_USB_XHCI_HCD=y
CONFIG_USB_EHCI_HCD=y
+CONFIG_USB_EHCI_MSM=y
CONFIG_USB_EHCI_HCD_PLATFORM=y
CONFIG_USB_OHCI_HCD=y
CONFIG_USB_OHCI_HCD_PLATFORM=y
@@ -462,10 +463,12 @@
CONFIG_USB_GADGET_DEBUG_FILES=y
CONFIG_USB_GADGET_DEBUG_FS=y
CONFIG_USB_GADGET_VBUS_DRAW=500
+CONFIG_USB_CI13XXX_MSM=y
CONFIG_USB_CONFIGFS=y
CONFIG_USB_CONFIGFS_SERIAL=y
CONFIG_USB_CONFIGFS_NCM=y
CONFIG_USB_CONFIGFS_QCRNDIS=y
+CONFIG_USB_CONFIGFS_RNDIS=y
CONFIG_USB_CONFIGFS_RMNET_BAM=y
CONFIG_USB_CONFIGFS_MASS_STORAGE=y
CONFIG_USB_CONFIGFS_F_FS=y
@@ -491,12 +494,15 @@
CONFIG_MMC_SDHCI_MSM=y
CONFIG_MMC_SDHCI_MSM_ICE=y
CONFIG_MMC_CQ_HCI=y
+CONFIG_LEDS_QTI_TRI_LED=y
CONFIG_LEDS_QPNP=y
CONFIG_LEDS_QPNP_FLASH=y
+CONFIG_LEDS_QPNP_FLASH_V2=y
CONFIG_LEDS_QPNP_WLED=y
CONFIG_LEDS_QPNP_HAPTICS=y
CONFIG_LEDS_QPNP_VIBRATOR_LDO=y
CONFIG_LEDS_TRIGGERS=y
+CONFIG_LEDS_TRIGGER_TIMER=y
CONFIG_EDAC=y
CONFIG_EDAC_MM_EDAC=y
CONFIG_RTC_CLASS=y
@@ -566,7 +572,9 @@
CONFIG_DEVFREQ_SPDM=y
CONFIG_PWM=y
CONFIG_PWM_QPNP=y
+CONFIG_PWM_QTI_LPG=y
CONFIG_ARM_GIC_V3_ACL=y
+CONFIG_QTI_MPM=y
CONFIG_ANDROID=y
CONFIG_ANDROID_BINDER_IPC=y
CONFIG_SENSORS_SSC=y
diff --git a/arch/arm64/configs/sdm670-perf_defconfig b/arch/arm64/configs/sdm670-perf_defconfig
index 83b0d66..560c510 100644
--- a/arch/arm64/configs/sdm670-perf_defconfig
+++ b/arch/arm64/configs/sdm670-perf_defconfig
@@ -378,6 +378,7 @@
CONFIG_DRM=y
CONFIG_DRM_SDE_EVTLOG_DEBUG=y
CONFIG_DRM_SDE_RSC=y
+CONFIG_DRM_LT_LT9611=y
CONFIG_FB_VIRTUAL=y
CONFIG_BACKLIGHT_LCD_SUPPORT=y
CONFIG_BACKLIGHT_CLASS_DEVICE=y
diff --git a/arch/arm64/configs/sdm845-perf_defconfig b/arch/arm64/configs/sdm845-perf_defconfig
index c154576..b9b22856 100644
--- a/arch/arm64/configs/sdm845-perf_defconfig
+++ b/arch/arm64/configs/sdm845-perf_defconfig
@@ -614,6 +614,7 @@
CONFIG_FORTIFY_SOURCE=y
CONFIG_SECURITY_SELINUX=y
CONFIG_SECURITY_SMACK=y
+CONFIG_CRYPTO_GCM=y
CONFIG_CRYPTO_XCBC=y
CONFIG_CRYPTO_MD4=y
CONFIG_CRYPTO_TWOFISH=y
diff --git a/arch/arm64/configs/sdm845_defconfig b/arch/arm64/configs/sdm845_defconfig
index 0b0d26d..a3da988 100644
--- a/arch/arm64/configs/sdm845_defconfig
+++ b/arch/arm64/configs/sdm845_defconfig
@@ -680,6 +680,7 @@
CONFIG_FORTIFY_SOURCE=y
CONFIG_SECURITY_SELINUX=y
CONFIG_SECURITY_SMACK=y
+CONFIG_CRYPTO_GCM=y
CONFIG_CRYPTO_XCBC=y
CONFIG_CRYPTO_MD4=y
CONFIG_CRYPTO_TWOFISH=y
diff --git a/arch/powerpc/Kconfig b/arch/powerpc/Kconfig
index acb6026..bc11d709f 100644
--- a/arch/powerpc/Kconfig
+++ b/arch/powerpc/Kconfig
@@ -129,6 +129,7 @@
select ARCH_HAS_GCOV_PROFILE_ALL
select GENERIC_SMP_IDLE_THREAD
select GENERIC_CMOS_UPDATE
+ select GENERIC_CPU_VULNERABILITIES if PPC_BOOK3S_64
select GENERIC_TIME_VSYSCALL_OLD
select GENERIC_CLOCKEVENTS
select GENERIC_CLOCKEVENTS_BROADCAST if SMP
diff --git a/arch/powerpc/include/asm/exception-64e.h b/arch/powerpc/include/asm/exception-64e.h
index a703452..555e22d 100644
--- a/arch/powerpc/include/asm/exception-64e.h
+++ b/arch/powerpc/include/asm/exception-64e.h
@@ -209,5 +209,11 @@
ori r3,r3,vector_offset@l; \
mtspr SPRN_IVOR##vector_number,r3;
+#define RFI_TO_KERNEL \
+ rfi
+
+#define RFI_TO_USER \
+ rfi
+
#endif /* _ASM_POWERPC_EXCEPTION_64E_H */
diff --git a/arch/powerpc/include/asm/exception-64s.h b/arch/powerpc/include/asm/exception-64s.h
index 9a3eee6..cab6d2a 100644
--- a/arch/powerpc/include/asm/exception-64s.h
+++ b/arch/powerpc/include/asm/exception-64s.h
@@ -51,6 +51,59 @@
#define EX_PPR 88 /* SMT thread status register (priority) */
#define EX_CTR 96
+/*
+ * Macros for annotating the expected destination of (h)rfid
+ *
+ * The nop instructions allow us to insert one or more instructions to flush the
+ * L1-D cache when returning to userspace or a guest.
+ */
+#define RFI_FLUSH_SLOT \
+ RFI_FLUSH_FIXUP_SECTION; \
+ nop; \
+ nop; \
+ nop
+
+#define RFI_TO_KERNEL \
+ rfid
+
+#define RFI_TO_USER \
+ RFI_FLUSH_SLOT; \
+ rfid; \
+ b rfi_flush_fallback
+
+#define RFI_TO_USER_OR_KERNEL \
+ RFI_FLUSH_SLOT; \
+ rfid; \
+ b rfi_flush_fallback
+
+#define RFI_TO_GUEST \
+ RFI_FLUSH_SLOT; \
+ rfid; \
+ b rfi_flush_fallback
+
+#define HRFI_TO_KERNEL \
+ hrfid
+
+#define HRFI_TO_USER \
+ RFI_FLUSH_SLOT; \
+ hrfid; \
+ b hrfi_flush_fallback
+
+#define HRFI_TO_USER_OR_KERNEL \
+ RFI_FLUSH_SLOT; \
+ hrfid; \
+ b hrfi_flush_fallback
+
+#define HRFI_TO_GUEST \
+ RFI_FLUSH_SLOT; \
+ hrfid; \
+ b hrfi_flush_fallback
+
+#define HRFI_TO_UNKNOWN \
+ RFI_FLUSH_SLOT; \
+ hrfid; \
+ b hrfi_flush_fallback
+
#ifdef CONFIG_RELOCATABLE
#define __EXCEPTION_RELON_PROLOG_PSERIES_1(label, h) \
mfspr r11,SPRN_##h##SRR0; /* save SRR0 */ \
diff --git a/arch/powerpc/include/asm/feature-fixups.h b/arch/powerpc/include/asm/feature-fixups.h
index ddf54f5..7b33234 100644
--- a/arch/powerpc/include/asm/feature-fixups.h
+++ b/arch/powerpc/include/asm/feature-fixups.h
@@ -189,4 +189,19 @@
void setup_feature_keys(void);
#endif
+#define RFI_FLUSH_FIXUP_SECTION \
+951: \
+ .pushsection __rfi_flush_fixup,"a"; \
+ .align 2; \
+952: \
+ FTR_ENTRY_OFFSET 951b-952b; \
+ .popsection;
+
+
+#ifndef __ASSEMBLY__
+
+extern long __start___rfi_flush_fixup, __stop___rfi_flush_fixup;
+
+#endif
+
#endif /* __ASM_POWERPC_FEATURE_FIXUPS_H */
diff --git a/arch/powerpc/include/asm/hvcall.h b/arch/powerpc/include/asm/hvcall.h
index 708edeb..0e12cb2 100644
--- a/arch/powerpc/include/asm/hvcall.h
+++ b/arch/powerpc/include/asm/hvcall.h
@@ -240,6 +240,7 @@
#define H_GET_HCA_INFO 0x1B8
#define H_GET_PERF_COUNT 0x1BC
#define H_MANAGE_TRACE 0x1C0
+#define H_GET_CPU_CHARACTERISTICS 0x1C8
#define H_FREE_LOGICAL_LAN_BUFFER 0x1D4
#define H_QUERY_INT_STATE 0x1E4
#define H_POLL_PENDING 0x1D8
@@ -306,6 +307,17 @@
#define H_SET_MODE_RESOURCE_ADDR_TRANS_MODE 3
#define H_SET_MODE_RESOURCE_LE 4
+/* H_GET_CPU_CHARACTERISTICS return values */
+#define H_CPU_CHAR_SPEC_BAR_ORI31 (1ull << 63) // IBM bit 0
+#define H_CPU_CHAR_BCCTRL_SERIALISED (1ull << 62) // IBM bit 1
+#define H_CPU_CHAR_L1D_FLUSH_ORI30 (1ull << 61) // IBM bit 2
+#define H_CPU_CHAR_L1D_FLUSH_TRIG2 (1ull << 60) // IBM bit 3
+#define H_CPU_CHAR_L1D_THREAD_PRIV (1ull << 59) // IBM bit 4
+
+#define H_CPU_BEHAV_FAVOUR_SECURITY (1ull << 63) // IBM bit 0
+#define H_CPU_BEHAV_L1D_FLUSH_PR (1ull << 62) // IBM bit 1
+#define H_CPU_BEHAV_BNDS_CHK_SPEC_BAR (1ull << 61) // IBM bit 2
+
#ifndef __ASSEMBLY__
/**
@@ -433,6 +445,11 @@
}
#endif /* CONFIG_PPC_PSERIES */
+struct h_cpu_char_result {
+ u64 character;
+ u64 behaviour;
+};
+
#endif /* __ASSEMBLY__ */
#endif /* __KERNEL__ */
#endif /* _ASM_POWERPC_HVCALL_H */
diff --git a/arch/powerpc/include/asm/paca.h b/arch/powerpc/include/asm/paca.h
index 6a6792b..ea43897 100644
--- a/arch/powerpc/include/asm/paca.h
+++ b/arch/powerpc/include/asm/paca.h
@@ -205,6 +205,16 @@
struct sibling_subcore_state *sibling_subcore_state;
#endif
#endif
+#ifdef CONFIG_PPC_BOOK3S_64
+ /*
+ * rfi fallback flush must be in its own cacheline to prevent
+ * other paca data leaking into the L1d
+ */
+ u64 exrfi[13] __aligned(0x80);
+ void *rfi_flush_fallback_area;
+ u64 l1d_flush_congruence;
+ u64 l1d_flush_sets;
+#endif
};
#ifdef CONFIG_PPC_BOOK3S
diff --git a/arch/powerpc/include/asm/plpar_wrappers.h b/arch/powerpc/include/asm/plpar_wrappers.h
index 1b39424..4e53b85 100644
--- a/arch/powerpc/include/asm/plpar_wrappers.h
+++ b/arch/powerpc/include/asm/plpar_wrappers.h
@@ -340,4 +340,18 @@
return plpar_set_mode(0, H_SET_MODE_RESOURCE_SET_DAWR, dawr0, dawrx0);
}
+static inline long plpar_get_cpu_characteristics(struct h_cpu_char_result *p)
+{
+ unsigned long retbuf[PLPAR_HCALL_BUFSIZE];
+ long rc;
+
+ rc = plpar_hcall(H_GET_CPU_CHARACTERISTICS, retbuf);
+ if (rc == H_SUCCESS) {
+ p->character = retbuf[0];
+ p->behaviour = retbuf[1];
+ }
+
+ return rc;
+}
+
#endif /* _ASM_POWERPC_PLPAR_WRAPPERS_H */
diff --git a/arch/powerpc/include/asm/setup.h b/arch/powerpc/include/asm/setup.h
index 654d64c..6825a67 100644
--- a/arch/powerpc/include/asm/setup.h
+++ b/arch/powerpc/include/asm/setup.h
@@ -38,6 +38,19 @@
static inline void pseries_little_endian_exceptions(void) {}
#endif /* CONFIG_PPC_PSERIES */
+void rfi_flush_enable(bool enable);
+
+/* These are bit flags */
+enum l1d_flush_type {
+ L1D_FLUSH_NONE = 0x1,
+ L1D_FLUSH_FALLBACK = 0x2,
+ L1D_FLUSH_ORI = 0x4,
+ L1D_FLUSH_MTTRIG = 0x8,
+};
+
+void __init setup_rfi_flush(enum l1d_flush_type, bool enable);
+void do_rfi_flush_fixups(enum l1d_flush_type types);
+
#endif /* !__ASSEMBLY__ */
#endif /* _ASM_POWERPC_SETUP_H */
diff --git a/arch/powerpc/kernel/asm-offsets.c b/arch/powerpc/kernel/asm-offsets.c
index c833d88..64bcbd5 100644
--- a/arch/powerpc/kernel/asm-offsets.c
+++ b/arch/powerpc/kernel/asm-offsets.c
@@ -240,6 +240,10 @@
#ifdef CONFIG_PPC_BOOK3S_64
DEFINE(PACAMCEMERGSP, offsetof(struct paca_struct, mc_emergency_sp));
DEFINE(PACA_IN_MCE, offsetof(struct paca_struct, in_mce));
+ DEFINE(PACA_RFI_FLUSH_FALLBACK_AREA, offsetof(struct paca_struct, rfi_flush_fallback_area));
+ DEFINE(PACA_EXRFI, offsetof(struct paca_struct, exrfi));
+ DEFINE(PACA_L1D_FLUSH_CONGRUENCE, offsetof(struct paca_struct, l1d_flush_congruence));
+ DEFINE(PACA_L1D_FLUSH_SETS, offsetof(struct paca_struct, l1d_flush_sets));
#endif
DEFINE(PACAHWCPUID, offsetof(struct paca_struct, hw_cpu_id));
DEFINE(PACAKEXECSTATE, offsetof(struct paca_struct, kexec_state));
diff --git a/arch/powerpc/kernel/entry_64.S b/arch/powerpc/kernel/entry_64.S
index caa6596..c33b69d 100644
--- a/arch/powerpc/kernel/entry_64.S
+++ b/arch/powerpc/kernel/entry_64.S
@@ -251,13 +251,23 @@
END_FTR_SECTION_IFSET(CPU_FTR_HAS_PPR)
ld r13,GPR13(r1) /* only restore r13 if returning to usermode */
+ ld r2,GPR2(r1)
+ ld r1,GPR1(r1)
+ mtlr r4
+ mtcr r5
+ mtspr SPRN_SRR0,r7
+ mtspr SPRN_SRR1,r8
+ RFI_TO_USER
+ b . /* prevent speculative execution */
+
+ /* exit to kernel */
1: ld r2,GPR2(r1)
ld r1,GPR1(r1)
mtlr r4
mtcr r5
mtspr SPRN_SRR0,r7
mtspr SPRN_SRR1,r8
- RFI
+ RFI_TO_KERNEL
b . /* prevent speculative execution */
syscall_error:
@@ -859,7 +869,7 @@
END_FTR_SECTION_IFSET(CPU_FTR_HAS_PPR)
ACCOUNT_CPU_USER_EXIT(r13, r2, r4)
REST_GPR(13, r1)
-1:
+
mtspr SPRN_SRR1,r3
ld r2,_CCR(r1)
@@ -872,8 +882,22 @@
ld r3,GPR3(r1)
ld r4,GPR4(r1)
ld r1,GPR1(r1)
+ RFI_TO_USER
+ b . /* prevent speculative execution */
- rfid
+1: mtspr SPRN_SRR1,r3
+
+ ld r2,_CCR(r1)
+ mtcrf 0xFF,r2
+ ld r2,_NIP(r1)
+ mtspr SPRN_SRR0,r2
+
+ ld r0,GPR0(r1)
+ ld r2,GPR2(r1)
+ ld r3,GPR3(r1)
+ ld r4,GPR4(r1)
+ ld r1,GPR1(r1)
+ RFI_TO_KERNEL
b . /* prevent speculative execution */
#endif /* CONFIG_PPC_BOOK3E */
diff --git a/arch/powerpc/kernel/exceptions-64s.S b/arch/powerpc/kernel/exceptions-64s.S
index fd68e19..96db6c3 100644
--- a/arch/powerpc/kernel/exceptions-64s.S
+++ b/arch/powerpc/kernel/exceptions-64s.S
@@ -655,6 +655,8 @@
andi. r10,r12,MSR_RI /* check for unrecoverable exception */
beq- 2f
+ andi. r10,r12,MSR_PR /* check for user mode (PR != 0) */
+ bne 1f
/* All done -- return from exception. */
@@ -671,7 +673,23 @@
ld r11,PACA_EXSLB+EX_R11(r13)
ld r12,PACA_EXSLB+EX_R12(r13)
ld r13,PACA_EXSLB+EX_R13(r13)
- rfid
+ RFI_TO_KERNEL
+ b . /* prevent speculative execution */
+
+1:
+.machine push
+.machine "power4"
+ mtcrf 0x80,r9
+ mtcrf 0x01,r9 /* slb_allocate uses cr0 and cr7 */
+.machine pop
+
+ RESTORE_PPR_PACA(PACA_EXSLB, r9)
+ ld r9,PACA_EXSLB+EX_R9(r13)
+ ld r10,PACA_EXSLB+EX_R10(r13)
+ ld r11,PACA_EXSLB+EX_R11(r13)
+ ld r12,PACA_EXSLB+EX_R12(r13)
+ ld r13,PACA_EXSLB+EX_R13(r13)
+ RFI_TO_USER
b . /* prevent speculative execution */
2: mfspr r11,SPRN_SRR0
@@ -679,7 +697,7 @@
mtspr SPRN_SRR0,r10
ld r10,PACAKMSR(r13)
mtspr SPRN_SRR1,r10
- rfid
+ RFI_TO_KERNEL
b .
8: mfspr r11,SPRN_SRR0
@@ -1576,6 +1594,92 @@
bl kernel_bad_stack
b 1b
+ .globl rfi_flush_fallback
+rfi_flush_fallback:
+ SET_SCRATCH0(r13);
+ GET_PACA(r13);
+ std r9,PACA_EXRFI+EX_R9(r13)
+ std r10,PACA_EXRFI+EX_R10(r13)
+ std r11,PACA_EXRFI+EX_R11(r13)
+ std r12,PACA_EXRFI+EX_R12(r13)
+ std r8,PACA_EXRFI+EX_R13(r13)
+ mfctr r9
+ ld r10,PACA_RFI_FLUSH_FALLBACK_AREA(r13)
+ ld r11,PACA_L1D_FLUSH_SETS(r13)
+ ld r12,PACA_L1D_FLUSH_CONGRUENCE(r13)
+ /*
+ * The load adresses are at staggered offsets within cachelines,
+ * which suits some pipelines better (on others it should not
+ * hurt).
+ */
+ addi r12,r12,8
+ mtctr r11
+ DCBT_STOP_ALL_STREAM_IDS(r11) /* Stop prefetch streams */
+
+ /* order ld/st prior to dcbt stop all streams with flushing */
+ sync
+1: li r8,0
+ .rept 8 /* 8-way set associative */
+ ldx r11,r10,r8
+ add r8,r8,r12
+ xor r11,r11,r11 // Ensure r11 is 0 even if fallback area is not
+ add r8,r8,r11 // Add 0, this creates a dependency on the ldx
+ .endr
+ addi r10,r10,128 /* 128 byte cache line */
+ bdnz 1b
+
+ mtctr r9
+ ld r9,PACA_EXRFI+EX_R9(r13)
+ ld r10,PACA_EXRFI+EX_R10(r13)
+ ld r11,PACA_EXRFI+EX_R11(r13)
+ ld r12,PACA_EXRFI+EX_R12(r13)
+ ld r8,PACA_EXRFI+EX_R13(r13)
+ GET_SCRATCH0(r13);
+ rfid
+
+ .globl hrfi_flush_fallback
+hrfi_flush_fallback:
+ SET_SCRATCH0(r13);
+ GET_PACA(r13);
+ std r9,PACA_EXRFI+EX_R9(r13)
+ std r10,PACA_EXRFI+EX_R10(r13)
+ std r11,PACA_EXRFI+EX_R11(r13)
+ std r12,PACA_EXRFI+EX_R12(r13)
+ std r8,PACA_EXRFI+EX_R13(r13)
+ mfctr r9
+ ld r10,PACA_RFI_FLUSH_FALLBACK_AREA(r13)
+ ld r11,PACA_L1D_FLUSH_SETS(r13)
+ ld r12,PACA_L1D_FLUSH_CONGRUENCE(r13)
+ /*
+ * The load adresses are at staggered offsets within cachelines,
+ * which suits some pipelines better (on others it should not
+ * hurt).
+ */
+ addi r12,r12,8
+ mtctr r11
+ DCBT_STOP_ALL_STREAM_IDS(r11) /* Stop prefetch streams */
+
+ /* order ld/st prior to dcbt stop all streams with flushing */
+ sync
+1: li r8,0
+ .rept 8 /* 8-way set associative */
+ ldx r11,r10,r8
+ add r8,r8,r12
+ xor r11,r11,r11 // Ensure r11 is 0 even if fallback area is not
+ add r8,r8,r11 // Add 0, this creates a dependency on the ldx
+ .endr
+ addi r10,r10,128 /* 128 byte cache line */
+ bdnz 1b
+
+ mtctr r9
+ ld r9,PACA_EXRFI+EX_R9(r13)
+ ld r10,PACA_EXRFI+EX_R10(r13)
+ ld r11,PACA_EXRFI+EX_R11(r13)
+ ld r12,PACA_EXRFI+EX_R12(r13)
+ ld r8,PACA_EXRFI+EX_R13(r13)
+ GET_SCRATCH0(r13);
+ hrfid
+
/*
* Called from arch_local_irq_enable when an interrupt needs
* to be resent. r3 contains 0x500, 0x900, 0xa00 or 0xe80 to indicate
diff --git a/arch/powerpc/kernel/setup_64.c b/arch/powerpc/kernel/setup_64.c
index a12be60..7c30a91 100644
--- a/arch/powerpc/kernel/setup_64.c
+++ b/arch/powerpc/kernel/setup_64.c
@@ -37,6 +37,7 @@
#include <linux/memblock.h>
#include <linux/memory.h>
#include <linux/nmi.h>
+#include <linux/debugfs.h>
#include <asm/io.h>
#include <asm/kdump.h>
@@ -678,4 +679,142 @@
return 0;
}
early_initcall(disable_hardlockup_detector);
+
+#ifdef CONFIG_PPC_BOOK3S_64
+static enum l1d_flush_type enabled_flush_types;
+static void *l1d_flush_fallback_area;
+static bool no_rfi_flush;
+bool rfi_flush;
+
+static int __init handle_no_rfi_flush(char *p)
+{
+ pr_info("rfi-flush: disabled on command line.");
+ no_rfi_flush = true;
+ return 0;
+}
+early_param("no_rfi_flush", handle_no_rfi_flush);
+
+/*
+ * The RFI flush is not KPTI, but because users will see doco that says to use
+ * nopti we hijack that option here to also disable the RFI flush.
+ */
+static int __init handle_no_pti(char *p)
+{
+ pr_info("rfi-flush: disabling due to 'nopti' on command line.\n");
+ handle_no_rfi_flush(NULL);
+ return 0;
+}
+early_param("nopti", handle_no_pti);
+
+static void do_nothing(void *unused)
+{
+ /*
+ * We don't need to do the flush explicitly, just enter+exit kernel is
+ * sufficient, the RFI exit handlers will do the right thing.
+ */
+}
+
+void rfi_flush_enable(bool enable)
+{
+ if (rfi_flush == enable)
+ return;
+
+ if (enable) {
+ do_rfi_flush_fixups(enabled_flush_types);
+ on_each_cpu(do_nothing, NULL, 1);
+ } else
+ do_rfi_flush_fixups(L1D_FLUSH_NONE);
+
+ rfi_flush = enable;
+}
+
+static void init_fallback_flush(void)
+{
+ u64 l1d_size, limit;
+ int cpu;
+
+ l1d_size = ppc64_caches.dsize;
+ limit = min(safe_stack_limit(), ppc64_rma_size);
+
+ /*
+ * Align to L1d size, and size it at 2x L1d size, to catch possible
+ * hardware prefetch runoff. We don't have a recipe for load patterns to
+ * reliably avoid the prefetcher.
+ */
+ l1d_flush_fallback_area = __va(memblock_alloc_base(l1d_size * 2, l1d_size, limit));
+ memset(l1d_flush_fallback_area, 0, l1d_size * 2);
+
+ for_each_possible_cpu(cpu) {
+ /*
+ * The fallback flush is currently coded for 8-way
+ * associativity. Different associativity is possible, but it
+ * will be treated as 8-way and may not evict the lines as
+ * effectively.
+ *
+ * 128 byte lines are mandatory.
+ */
+ u64 c = l1d_size / 8;
+
+ paca[cpu].rfi_flush_fallback_area = l1d_flush_fallback_area;
+ paca[cpu].l1d_flush_congruence = c;
+ paca[cpu].l1d_flush_sets = c / 128;
+ }
+}
+
+void __init setup_rfi_flush(enum l1d_flush_type types, bool enable)
+{
+ if (types & L1D_FLUSH_FALLBACK) {
+ pr_info("rfi-flush: Using fallback displacement flush\n");
+ init_fallback_flush();
+ }
+
+ if (types & L1D_FLUSH_ORI)
+ pr_info("rfi-flush: Using ori type flush\n");
+
+ if (types & L1D_FLUSH_MTTRIG)
+ pr_info("rfi-flush: Using mttrig type flush\n");
+
+ enabled_flush_types = types;
+
+ if (!no_rfi_flush)
+ rfi_flush_enable(enable);
+}
+
+#ifdef CONFIG_DEBUG_FS
+static int rfi_flush_set(void *data, u64 val)
+{
+ if (val == 1)
+ rfi_flush_enable(true);
+ else if (val == 0)
+ rfi_flush_enable(false);
+ else
+ return -EINVAL;
+
+ return 0;
+}
+
+static int rfi_flush_get(void *data, u64 *val)
+{
+ *val = rfi_flush ? 1 : 0;
+ return 0;
+}
+
+DEFINE_SIMPLE_ATTRIBUTE(fops_rfi_flush, rfi_flush_get, rfi_flush_set, "%llu\n");
+
+static __init int rfi_flush_debugfs_init(void)
+{
+ debugfs_create_file("rfi_flush", 0600, powerpc_debugfs_root, NULL, &fops_rfi_flush);
+ return 0;
+}
+device_initcall(rfi_flush_debugfs_init);
+#endif
+
+ssize_t cpu_show_meltdown(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ if (rfi_flush)
+ return sprintf(buf, "Mitigation: RFI Flush\n");
+
+ return sprintf(buf, "Vulnerable\n");
+}
+#endif /* CONFIG_PPC_BOOK3S_64 */
#endif
diff --git a/arch/powerpc/kernel/vmlinux.lds.S b/arch/powerpc/kernel/vmlinux.lds.S
index 7394b77..b61fb79 100644
--- a/arch/powerpc/kernel/vmlinux.lds.S
+++ b/arch/powerpc/kernel/vmlinux.lds.S
@@ -132,6 +132,15 @@
/* Read-only data */
RODATA
+#ifdef CONFIG_PPC64
+ . = ALIGN(8);
+ __rfi_flush_fixup : AT(ADDR(__rfi_flush_fixup) - LOAD_OFFSET) {
+ __start___rfi_flush_fixup = .;
+ *(__rfi_flush_fixup)
+ __stop___rfi_flush_fixup = .;
+ }
+#endif
+
EXCEPTION_TABLE(0)
NOTES :kernel :notes
diff --git a/arch/powerpc/lib/feature-fixups.c b/arch/powerpc/lib/feature-fixups.c
index 043415f..e86bfa1 100644
--- a/arch/powerpc/lib/feature-fixups.c
+++ b/arch/powerpc/lib/feature-fixups.c
@@ -23,6 +23,7 @@
#include <asm/sections.h>
#include <asm/setup.h>
#include <asm/firmware.h>
+#include <asm/setup.h>
struct fixup_entry {
unsigned long mask;
@@ -115,6 +116,47 @@
}
}
+#ifdef CONFIG_PPC_BOOK3S_64
+void do_rfi_flush_fixups(enum l1d_flush_type types)
+{
+ unsigned int instrs[3], *dest;
+ long *start, *end;
+ int i;
+
+ start = PTRRELOC(&__start___rfi_flush_fixup),
+ end = PTRRELOC(&__stop___rfi_flush_fixup);
+
+ instrs[0] = 0x60000000; /* nop */
+ instrs[1] = 0x60000000; /* nop */
+ instrs[2] = 0x60000000; /* nop */
+
+ if (types & L1D_FLUSH_FALLBACK)
+ /* b .+16 to fallback flush */
+ instrs[0] = 0x48000010;
+
+ i = 0;
+ if (types & L1D_FLUSH_ORI) {
+ instrs[i++] = 0x63ff0000; /* ori 31,31,0 speculation barrier */
+ instrs[i++] = 0x63de0000; /* ori 30,30,0 L1d flush*/
+ }
+
+ if (types & L1D_FLUSH_MTTRIG)
+ instrs[i++] = 0x7c12dba6; /* mtspr TRIG2,r0 (SPR #882) */
+
+ for (i = 0; start < end; start++, i++) {
+ dest = (void *)start + *start;
+
+ pr_devel("patching dest %lx\n", (unsigned long)dest);
+
+ patch_instruction(dest, instrs[0]);
+ patch_instruction(dest + 1, instrs[1]);
+ patch_instruction(dest + 2, instrs[2]);
+ }
+
+ printk(KERN_DEBUG "rfi-flush: patched %d locations\n", i);
+}
+#endif /* CONFIG_PPC_BOOK3S_64 */
+
void do_lwsync_fixups(unsigned long value, void *fixup_start, void *fixup_end)
{
long *start, *end;
diff --git a/arch/powerpc/platforms/powernv/setup.c b/arch/powerpc/platforms/powernv/setup.c
index b33faa0..6f8b4c1 100644
--- a/arch/powerpc/platforms/powernv/setup.c
+++ b/arch/powerpc/platforms/powernv/setup.c
@@ -35,13 +35,63 @@
#include <asm/opal.h>
#include <asm/kexec.h>
#include <asm/smp.h>
+#include <asm/tm.h>
+#include <asm/setup.h>
#include "powernv.h"
+static void pnv_setup_rfi_flush(void)
+{
+ struct device_node *np, *fw_features;
+ enum l1d_flush_type type;
+ int enable;
+
+ /* Default to fallback in case fw-features are not available */
+ type = L1D_FLUSH_FALLBACK;
+ enable = 1;
+
+ np = of_find_node_by_name(NULL, "ibm,opal");
+ fw_features = of_get_child_by_name(np, "fw-features");
+ of_node_put(np);
+
+ if (fw_features) {
+ np = of_get_child_by_name(fw_features, "inst-l1d-flush-trig2");
+ if (np && of_property_read_bool(np, "enabled"))
+ type = L1D_FLUSH_MTTRIG;
+
+ of_node_put(np);
+
+ np = of_get_child_by_name(fw_features, "inst-l1d-flush-ori30,30,0");
+ if (np && of_property_read_bool(np, "enabled"))
+ type = L1D_FLUSH_ORI;
+
+ of_node_put(np);
+
+ /* Enable unless firmware says NOT to */
+ enable = 2;
+ np = of_get_child_by_name(fw_features, "needs-l1d-flush-msr-hv-1-to-0");
+ if (np && of_property_read_bool(np, "disabled"))
+ enable--;
+
+ of_node_put(np);
+
+ np = of_get_child_by_name(fw_features, "needs-l1d-flush-msr-pr-0-to-1");
+ if (np && of_property_read_bool(np, "disabled"))
+ enable--;
+
+ of_node_put(np);
+ of_node_put(fw_features);
+ }
+
+ setup_rfi_flush(type, enable > 0);
+}
+
static void __init pnv_setup_arch(void)
{
set_arch_panic_timeout(10, ARCH_PANIC_TIMEOUT);
+ pnv_setup_rfi_flush();
+
/* Initialize SMP */
pnv_smp_init();
diff --git a/arch/powerpc/platforms/pseries/setup.c b/arch/powerpc/platforms/pseries/setup.c
index 97aa3f3..1845fc6 100644
--- a/arch/powerpc/platforms/pseries/setup.c
+++ b/arch/powerpc/platforms/pseries/setup.c
@@ -450,6 +450,39 @@
of_pci_check_probe_only();
}
+static void pseries_setup_rfi_flush(void)
+{
+ struct h_cpu_char_result result;
+ enum l1d_flush_type types;
+ bool enable;
+ long rc;
+
+ /* Enable by default */
+ enable = true;
+
+ rc = plpar_get_cpu_characteristics(&result);
+ if (rc == H_SUCCESS) {
+ types = L1D_FLUSH_NONE;
+
+ if (result.character & H_CPU_CHAR_L1D_FLUSH_TRIG2)
+ types |= L1D_FLUSH_MTTRIG;
+ if (result.character & H_CPU_CHAR_L1D_FLUSH_ORI30)
+ types |= L1D_FLUSH_ORI;
+
+ /* Use fallback if nothing set in hcall */
+ if (types == L1D_FLUSH_NONE)
+ types = L1D_FLUSH_FALLBACK;
+
+ if (!(result.behaviour & H_CPU_BEHAV_L1D_FLUSH_PR))
+ enable = false;
+ } else {
+ /* Default to fallback if case hcall is not available */
+ types = L1D_FLUSH_FALLBACK;
+ }
+
+ setup_rfi_flush(types, enable);
+}
+
static void __init pSeries_setup_arch(void)
{
set_arch_panic_timeout(10, ARCH_PANIC_TIMEOUT);
@@ -467,6 +500,8 @@
fwnmi_init();
+ pseries_setup_rfi_flush();
+
/* By default, only probe PCI (can be overridden by rtas_pci) */
pci_add_flags(PCI_PROBE_ONLY);
diff --git a/arch/x86/entry/common.c b/arch/x86/entry/common.c
index bdd9cc5..b0cd306 100644
--- a/arch/x86/entry/common.c
+++ b/arch/x86/entry/common.c
@@ -20,6 +20,7 @@
#include <linux/export.h>
#include <linux/context_tracking.h>
#include <linux/user-return-notifier.h>
+#include <linux/nospec.h>
#include <linux/uprobes.h>
#include <asm/desc.h>
@@ -201,7 +202,7 @@
* special case only applies after poking regs and before the
* very next return to user mode.
*/
- current->thread.status &= ~(TS_COMPAT|TS_I386_REGS_POKED);
+ ti->status &= ~(TS_COMPAT|TS_I386_REGS_POKED);
#endif
user_enter_irqoff();
@@ -277,7 +278,8 @@
* regs->orig_ax, which changes the behavior of some syscalls.
*/
if (likely((nr & __SYSCALL_MASK) < NR_syscalls)) {
- regs->ax = sys_call_table[nr & __SYSCALL_MASK](
+ nr = array_index_nospec(nr & __SYSCALL_MASK, NR_syscalls);
+ regs->ax = sys_call_table[nr](
regs->di, regs->si, regs->dx,
regs->r10, regs->r8, regs->r9);
}
@@ -299,7 +301,7 @@
unsigned int nr = (unsigned int)regs->orig_ax;
#ifdef CONFIG_IA32_EMULATION
- current->thread.status |= TS_COMPAT;
+ ti->status |= TS_COMPAT;
#endif
if (READ_ONCE(ti->flags) & _TIF_WORK_SYSCALL_ENTRY) {
@@ -313,6 +315,7 @@
}
if (likely(nr < IA32_NR_syscalls)) {
+ nr = array_index_nospec(nr, IA32_NR_syscalls);
/*
* It's possible that a 32-bit syscall implementation
* takes a 64-bit parameter but nonetheless assumes that
diff --git a/arch/x86/entry/entry_32.S b/arch/x86/entry/entry_32.S
index a76dc73..f5434b4 100644
--- a/arch/x86/entry/entry_32.S
+++ b/arch/x86/entry/entry_32.S
@@ -237,7 +237,8 @@
* exist, overwrite the RSB with entries which capture
* speculative execution to prevent attack.
*/
- FILL_RETURN_BUFFER %ebx, RSB_CLEAR_LOOPS, X86_FEATURE_RSB_CTXSW
+ /* Clobbers %ebx */
+ FILL_RETURN_BUFFER RSB_CLEAR_LOOPS, X86_FEATURE_RSB_CTXSW
#endif
/* restore callee-saved registers */
diff --git a/arch/x86/entry/entry_64.S b/arch/x86/entry/entry_64.S
index e729e15..db5009c 100644
--- a/arch/x86/entry/entry_64.S
+++ b/arch/x86/entry/entry_64.S
@@ -177,96 +177,17 @@
pushq %r9 /* pt_regs->r9 */
pushq %r10 /* pt_regs->r10 */
pushq %r11 /* pt_regs->r11 */
- sub $(6*8), %rsp /* pt_regs->bp, bx, r12-15 not saved */
+ pushq %rbx /* pt_regs->rbx */
+ pushq %rbp /* pt_regs->rbp */
+ pushq %r12 /* pt_regs->r12 */
+ pushq %r13 /* pt_regs->r13 */
+ pushq %r14 /* pt_regs->r14 */
+ pushq %r15 /* pt_regs->r15 */
- /*
- * If we need to do entry work or if we guess we'll need to do
- * exit work, go straight to the slow path.
- */
- movq PER_CPU_VAR(current_task), %r11
- testl $_TIF_WORK_SYSCALL_ENTRY|_TIF_ALLWORK_MASK, TASK_TI_flags(%r11)
- jnz entry_SYSCALL64_slow_path
-
-entry_SYSCALL_64_fastpath:
- /*
- * Easy case: enable interrupts and issue the syscall. If the syscall
- * needs pt_regs, we'll call a stub that disables interrupts again
- * and jumps to the slow path.
- */
- TRACE_IRQS_ON
- ENABLE_INTERRUPTS(CLBR_NONE)
-#if __SYSCALL_MASK == ~0
- cmpq $__NR_syscall_max, %rax
-#else
- andl $__SYSCALL_MASK, %eax
- cmpl $__NR_syscall_max, %eax
-#endif
- ja 1f /* return -ENOSYS (already in pt_regs->ax) */
- movq %r10, %rcx
-
- /*
- * This call instruction is handled specially in stub_ptregs_64.
- * It might end up jumping to the slow path. If it jumps, RAX
- * and all argument registers are clobbered.
- */
-#ifdef CONFIG_RETPOLINE
- movq sys_call_table(, %rax, 8), %rax
- call __x86_indirect_thunk_rax
-#else
- call *sys_call_table(, %rax, 8)
-#endif
-.Lentry_SYSCALL_64_after_fastpath_call:
-
- movq %rax, RAX(%rsp)
-1:
-
- /*
- * If we get here, then we know that pt_regs is clean for SYSRET64.
- * If we see that no exit work is required (which we are required
- * to check with IRQs off), then we can go straight to SYSRET64.
- */
- DISABLE_INTERRUPTS(CLBR_NONE)
- TRACE_IRQS_OFF
- movq PER_CPU_VAR(current_task), %r11
- testl $_TIF_ALLWORK_MASK, TASK_TI_flags(%r11)
- jnz 1f
-
- LOCKDEP_SYS_EXIT
- TRACE_IRQS_ON /* user mode is traced as IRQs on */
- movq RIP(%rsp), %rcx
- movq EFLAGS(%rsp), %r11
- RESTORE_C_REGS_EXCEPT_RCX_R11
- /*
- * This opens a window where we have a user CR3, but are
- * running in the kernel. This makes using the CS
- * register useless for telling whether or not we need to
- * switch CR3 in NMIs. Normal interrupts are OK because
- * they are off here.
- */
- SWITCH_USER_CR3
- movq RSP(%rsp), %rsp
- USERGS_SYSRET64
-
-1:
- /*
- * The fast path looked good when we started, but something changed
- * along the way and we need to switch to the slow path. Calling
- * raise(3) will trigger this, for example. IRQs are off.
- */
- TRACE_IRQS_ON
- ENABLE_INTERRUPTS(CLBR_NONE)
- SAVE_EXTRA_REGS
- movq %rsp, %rdi
- call syscall_return_slowpath /* returns with IRQs disabled */
- jmp return_from_SYSCALL_64
-
-entry_SYSCALL64_slow_path:
/* IRQs are off. */
- SAVE_EXTRA_REGS
movq %rsp, %rdi
call do_syscall_64 /* returns with IRQs disabled */
-return_from_SYSCALL_64:
RESTORE_EXTRA_REGS
TRACE_IRQS_IRETQ /* we're about to change IF */
@@ -339,6 +260,7 @@
syscall_return_via_sysret:
/* rcx and r11 are already restored (see code above) */
RESTORE_C_REGS_EXCEPT_RCX_R11
+
/*
* This opens a window where we have a user CR3, but are
* running in the kernel. This makes using the CS
@@ -363,45 +285,6 @@
jmp restore_c_regs_and_iret
END(entry_SYSCALL_64)
-ENTRY(stub_ptregs_64)
- /*
- * Syscalls marked as needing ptregs land here.
- * If we are on the fast path, we need to save the extra regs,
- * which we achieve by trying again on the slow path. If we are on
- * the slow path, the extra regs are already saved.
- *
- * RAX stores a pointer to the C function implementing the syscall.
- * IRQs are on.
- */
- cmpq $.Lentry_SYSCALL_64_after_fastpath_call, (%rsp)
- jne 1f
-
- /*
- * Called from fast path -- disable IRQs again, pop return address
- * and jump to slow path
- */
- DISABLE_INTERRUPTS(CLBR_NONE)
- TRACE_IRQS_OFF
- popq %rax
- jmp entry_SYSCALL64_slow_path
-
-1:
- JMP_NOSPEC %rax /* Called from C */
-END(stub_ptregs_64)
-
-.macro ptregs_stub func
-ENTRY(ptregs_\func)
- leaq \func(%rip), %rax
- jmp stub_ptregs_64
-END(ptregs_\func)
-.endm
-
-/* Instantiate ptregs_stub for each ptregs-using syscall */
-#define __SYSCALL_64_QUAL_(sym)
-#define __SYSCALL_64_QUAL_ptregs(sym) ptregs_stub sym
-#define __SYSCALL_64(nr, sym, qual) __SYSCALL_64_QUAL_##qual(sym)
-#include <asm/syscalls_64.h>
-
/*
* %rdi: prev task
* %rsi: next task
@@ -435,7 +318,8 @@
* exist, overwrite the RSB with entries which capture
* speculative execution to prevent attack.
*/
- FILL_RETURN_BUFFER %r12, RSB_CLEAR_LOOPS, X86_FEATURE_RSB_CTXSW
+ /* Clobbers %rbx */
+ FILL_RETURN_BUFFER RSB_CLEAR_LOOPS, X86_FEATURE_RSB_CTXSW
#endif
/* restore callee-saved registers */
diff --git a/arch/x86/entry/syscall_64.c b/arch/x86/entry/syscall_64.c
index 9dbc5ab..6705edd 100644
--- a/arch/x86/entry/syscall_64.c
+++ b/arch/x86/entry/syscall_64.c
@@ -6,14 +6,11 @@
#include <asm/asm-offsets.h>
#include <asm/syscall.h>
-#define __SYSCALL_64_QUAL_(sym) sym
-#define __SYSCALL_64_QUAL_ptregs(sym) ptregs_##sym
-
-#define __SYSCALL_64(nr, sym, qual) extern asmlinkage long __SYSCALL_64_QUAL_##qual(sym)(unsigned long, unsigned long, unsigned long, unsigned long, unsigned long, unsigned long);
+#define __SYSCALL_64(nr, sym, qual) extern asmlinkage long sym(unsigned long, unsigned long, unsigned long, unsigned long, unsigned long, unsigned long);
#include <asm/syscalls_64.h>
#undef __SYSCALL_64
-#define __SYSCALL_64(nr, sym, qual) [nr] = __SYSCALL_64_QUAL_##qual(sym),
+#define __SYSCALL_64(nr, sym, qual) [nr] = sym,
extern long sys_ni_syscall(unsigned long, unsigned long, unsigned long, unsigned long, unsigned long, unsigned long);
diff --git a/arch/x86/events/intel/bts.c b/arch/x86/events/intel/bts.c
index 982c9e3..21298c1 100644
--- a/arch/x86/events/intel/bts.c
+++ b/arch/x86/events/intel/bts.c
@@ -22,6 +22,7 @@
#include <linux/debugfs.h>
#include <linux/device.h>
#include <linux/coredump.h>
+#include <linux/kaiser.h>
#include <asm-generic/sizes.h>
#include <asm/perf_event.h>
@@ -77,6 +78,23 @@
return 1 << (PAGE_SHIFT + page_private(page));
}
+static void bts_buffer_free_aux(void *data)
+{
+#ifdef CONFIG_PAGE_TABLE_ISOLATION
+ struct bts_buffer *buf = data;
+ int nbuf;
+
+ for (nbuf = 0; nbuf < buf->nr_bufs; nbuf++) {
+ struct page *page = buf->buf[nbuf].page;
+ void *kaddr = page_address(page);
+ size_t page_size = buf_size(page);
+
+ kaiser_remove_mapping((unsigned long)kaddr, page_size);
+ }
+#endif
+ kfree(data);
+}
+
static void *
bts_buffer_setup_aux(int cpu, void **pages, int nr_pages, bool overwrite)
{
@@ -113,29 +131,33 @@
buf->real_size = size - size % BTS_RECORD_SIZE;
for (pg = 0, nbuf = 0, offset = 0, pad = 0; nbuf < buf->nr_bufs; nbuf++) {
- unsigned int __nr_pages;
+ void *kaddr = pages[pg];
+ size_t page_size;
- page = virt_to_page(pages[pg]);
- __nr_pages = PagePrivate(page) ? 1 << page_private(page) : 1;
+ page = virt_to_page(kaddr);
+ page_size = buf_size(page);
+
+ if (kaiser_add_mapping((unsigned long)kaddr,
+ page_size, __PAGE_KERNEL) < 0) {
+ buf->nr_bufs = nbuf;
+ bts_buffer_free_aux(buf);
+ return NULL;
+ }
+
buf->buf[nbuf].page = page;
buf->buf[nbuf].offset = offset;
buf->buf[nbuf].displacement = (pad ? BTS_RECORD_SIZE - pad : 0);
- buf->buf[nbuf].size = buf_size(page) - buf->buf[nbuf].displacement;
+ buf->buf[nbuf].size = page_size - buf->buf[nbuf].displacement;
pad = buf->buf[nbuf].size % BTS_RECORD_SIZE;
buf->buf[nbuf].size -= pad;
- pg += __nr_pages;
- offset += __nr_pages << PAGE_SHIFT;
+ pg += page_size >> PAGE_SHIFT;
+ offset += page_size;
}
return buf;
}
-static void bts_buffer_free_aux(void *data)
-{
- kfree(data);
-}
-
static unsigned long bts_buffer_offset(struct bts_buffer *buf, unsigned int idx)
{
return buf->buf[idx].offset + buf->buf[idx].displacement;
diff --git a/arch/x86/include/asm/asm-prototypes.h b/arch/x86/include/asm/asm-prototypes.h
index b15aa40..1666542 100644
--- a/arch/x86/include/asm/asm-prototypes.h
+++ b/arch/x86/include/asm/asm-prototypes.h
@@ -37,5 +37,7 @@
INDIRECT_THUNK(si)
INDIRECT_THUNK(di)
INDIRECT_THUNK(bp)
-INDIRECT_THUNK(sp)
+asmlinkage void __fill_rsb(void);
+asmlinkage void __clear_rsb(void);
+
#endif /* CONFIG_RETPOLINE */
diff --git a/arch/x86/include/asm/asm.h b/arch/x86/include/asm/asm.h
index 0052352..7bb29a4 100644
--- a/arch/x86/include/asm/asm.h
+++ b/arch/x86/include/asm/asm.h
@@ -11,10 +11,12 @@
# define __ASM_FORM_COMMA(x) " " #x ","
#endif
-#ifdef CONFIG_X86_32
+#ifndef __x86_64__
+/* 32 bit */
# define __ASM_SEL(a,b) __ASM_FORM(a)
# define __ASM_SEL_RAW(a,b) __ASM_FORM_RAW(a)
#else
+/* 64 bit */
# define __ASM_SEL(a,b) __ASM_FORM(b)
# define __ASM_SEL_RAW(a,b) __ASM_FORM_RAW(b)
#endif
diff --git a/arch/x86/include/asm/barrier.h b/arch/x86/include/asm/barrier.h
index bfb28ca..8575903 100644
--- a/arch/x86/include/asm/barrier.h
+++ b/arch/x86/include/asm/barrier.h
@@ -23,6 +23,34 @@
#define wmb() asm volatile("sfence" ::: "memory")
#endif
+/**
+ * array_index_mask_nospec() - generate a mask that is ~0UL when the
+ * bounds check succeeds and 0 otherwise
+ * @index: array element index
+ * @size: number of elements in array
+ *
+ * Returns:
+ * 0 - (index < size)
+ */
+static inline unsigned long array_index_mask_nospec(unsigned long index,
+ unsigned long size)
+{
+ unsigned long mask;
+
+ asm ("cmp %1,%2; sbb %0,%0;"
+ :"=r" (mask)
+ :"r"(size),"r" (index)
+ :"cc");
+ return mask;
+}
+
+/* Override the default implementation from linux/nospec.h. */
+#define array_index_mask_nospec array_index_mask_nospec
+
+/* Prevent speculative execution past this barrier. */
+#define barrier_nospec() alternative_2("", "mfence", X86_FEATURE_MFENCE_RDTSC, \
+ "lfence", X86_FEATURE_LFENCE_RDTSC)
+
#ifdef CONFIG_X86_PPRO_FENCE
#define dma_rmb() rmb()
#else
diff --git a/arch/x86/include/asm/cpufeature.h b/arch/x86/include/asm/cpufeature.h
index 9ea67a0..8c10157 100644
--- a/arch/x86/include/asm/cpufeature.h
+++ b/arch/x86/include/asm/cpufeature.h
@@ -28,6 +28,7 @@
CPUID_8000_000A_EDX,
CPUID_7_ECX,
CPUID_8000_0007_EBX,
+ CPUID_7_EDX,
};
#ifdef CONFIG_X86_FEATURE_NAMES
@@ -78,8 +79,9 @@
CHECK_BIT_IN_MASK_WORD(REQUIRED_MASK, 15, feature_bit) || \
CHECK_BIT_IN_MASK_WORD(REQUIRED_MASK, 16, feature_bit) || \
CHECK_BIT_IN_MASK_WORD(REQUIRED_MASK, 17, feature_bit) || \
+ CHECK_BIT_IN_MASK_WORD(REQUIRED_MASK, 18, feature_bit) || \
REQUIRED_MASK_CHECK || \
- BUILD_BUG_ON_ZERO(NCAPINTS != 18))
+ BUILD_BUG_ON_ZERO(NCAPINTS != 19))
#define DISABLED_MASK_BIT_SET(feature_bit) \
( CHECK_BIT_IN_MASK_WORD(DISABLED_MASK, 0, feature_bit) || \
@@ -100,8 +102,9 @@
CHECK_BIT_IN_MASK_WORD(DISABLED_MASK, 15, feature_bit) || \
CHECK_BIT_IN_MASK_WORD(DISABLED_MASK, 16, feature_bit) || \
CHECK_BIT_IN_MASK_WORD(DISABLED_MASK, 17, feature_bit) || \
+ CHECK_BIT_IN_MASK_WORD(DISABLED_MASK, 18, feature_bit) || \
DISABLED_MASK_CHECK || \
- BUILD_BUG_ON_ZERO(NCAPINTS != 18))
+ BUILD_BUG_ON_ZERO(NCAPINTS != 19))
#define cpu_has(c, bit) \
(__builtin_constant_p(bit) && REQUIRED_MASK_BIT_SET(bit) ? 1 : \
diff --git a/arch/x86/include/asm/cpufeatures.h b/arch/x86/include/asm/cpufeatures.h
index 8537a21..8eb23f5 100644
--- a/arch/x86/include/asm/cpufeatures.h
+++ b/arch/x86/include/asm/cpufeatures.h
@@ -12,7 +12,7 @@
/*
* Defines x86 CPU feature bits
*/
-#define NCAPINTS 18 /* N 32-bit words worth of info */
+#define NCAPINTS 19 /* N 32-bit words worth of info */
#define NBUGINTS 1 /* N 32-bit bug flags */
/*
@@ -194,16 +194,16 @@
#define X86_FEATURE_HW_PSTATE ( 7*32+ 8) /* AMD HW-PState */
#define X86_FEATURE_PROC_FEEDBACK ( 7*32+ 9) /* AMD ProcFeedbackInterface */
-#define X86_FEATURE_RETPOLINE ( 7*32+12) /* Generic Retpoline mitigation for Spectre variant 2 */
-#define X86_FEATURE_RETPOLINE_AMD ( 7*32+13) /* AMD Retpoline mitigation for Spectre variant 2 */
+#define X86_FEATURE_RETPOLINE ( 7*32+12) /* "" Generic Retpoline mitigation for Spectre variant 2 */
+#define X86_FEATURE_RETPOLINE_AMD ( 7*32+13) /* "" AMD Retpoline mitigation for Spectre variant 2 */
-#define X86_FEATURE_AVX512_4VNNIW (7*32+16) /* AVX-512 Neural Network Instructions */
-#define X86_FEATURE_AVX512_4FMAPS (7*32+17) /* AVX-512 Multiply Accumulation Single precision */
-#define X86_FEATURE_RSB_CTXSW ( 7*32+19) /* Fill RSB on context switches */
+#define X86_FEATURE_RSB_CTXSW ( 7*32+19) /* "" Fill RSB on context switches */
/* Because the ALTERNATIVE scheme is for members of the X86_FEATURE club... */
#define X86_FEATURE_KAISER ( 7*32+31) /* CONFIG_PAGE_TABLE_ISOLATION w/o nokaiser */
+#define X86_FEATURE_USE_IBPB ( 7*32+21) /* "" Indirect Branch Prediction Barrier enabled */
+
/* Virtualization flags: Linux defined, word 8 */
#define X86_FEATURE_TPR_SHADOW ( 8*32+ 0) /* Intel TPR Shadow */
#define X86_FEATURE_VNMI ( 8*32+ 1) /* Intel Virtual NMI */
@@ -260,6 +260,9 @@
/* AMD-defined CPU features, CPUID level 0x80000008 (ebx), word 13 */
#define X86_FEATURE_CLZERO (13*32+0) /* CLZERO instruction */
#define X86_FEATURE_IRPERF (13*32+1) /* Instructions Retired Count */
+#define X86_FEATURE_IBPB (13*32+12) /* Indirect Branch Prediction Barrier */
+#define X86_FEATURE_IBRS (13*32+14) /* Indirect Branch Restricted Speculation */
+#define X86_FEATURE_STIBP (13*32+15) /* Single Thread Indirect Branch Predictors */
/* Thermal and Power Management Leaf, CPUID level 0x00000006 (eax), word 14 */
#define X86_FEATURE_DTHERM (14*32+ 0) /* Digital Thermal Sensor */
@@ -295,6 +298,13 @@
#define X86_FEATURE_SUCCOR (17*32+1) /* Uncorrectable error containment and recovery */
#define X86_FEATURE_SMCA (17*32+3) /* Scalable MCA */
+/* Intel-defined CPU features, CPUID level 0x00000007:0 (EDX), word 18 */
+#define X86_FEATURE_AVX512_4VNNIW (18*32+ 2) /* AVX-512 Neural Network Instructions */
+#define X86_FEATURE_AVX512_4FMAPS (18*32+ 3) /* AVX-512 Multiply Accumulation Single precision */
+#define X86_FEATURE_SPEC_CTRL (18*32+26) /* "" Speculation Control (IBRS + IBPB) */
+#define X86_FEATURE_INTEL_STIBP (18*32+27) /* "" Single Thread Indirect Branch Predictors */
+#define X86_FEATURE_ARCH_CAPABILITIES (18*32+29) /* IA32_ARCH_CAPABILITIES MSR (Intel) */
+
/*
* BUG word(s)
*/
diff --git a/arch/x86/include/asm/disabled-features.h b/arch/x86/include/asm/disabled-features.h
index 21c5ac1..1f8cca4 100644
--- a/arch/x86/include/asm/disabled-features.h
+++ b/arch/x86/include/asm/disabled-features.h
@@ -59,6 +59,7 @@
#define DISABLED_MASK15 0
#define DISABLED_MASK16 (DISABLE_PKU|DISABLE_OSPKE)
#define DISABLED_MASK17 0
-#define DISABLED_MASK_CHECK BUILD_BUG_ON_ZERO(NCAPINTS != 18)
+#define DISABLED_MASK18 0
+#define DISABLED_MASK_CHECK BUILD_BUG_ON_ZERO(NCAPINTS != 19)
#endif /* _ASM_X86_DISABLED_FEATURES_H */
diff --git a/arch/x86/include/asm/intel-family.h b/arch/x86/include/asm/intel-family.h
index 34a46dc..75b748a 100644
--- a/arch/x86/include/asm/intel-family.h
+++ b/arch/x86/include/asm/intel-family.h
@@ -12,6 +12,7 @@
*/
#define INTEL_FAM6_CORE_YONAH 0x0E
+
#define INTEL_FAM6_CORE2_MEROM 0x0F
#define INTEL_FAM6_CORE2_MEROM_L 0x16
#define INTEL_FAM6_CORE2_PENRYN 0x17
@@ -21,6 +22,7 @@
#define INTEL_FAM6_NEHALEM_G 0x1F /* Auburndale / Havendale */
#define INTEL_FAM6_NEHALEM_EP 0x1A
#define INTEL_FAM6_NEHALEM_EX 0x2E
+
#define INTEL_FAM6_WESTMERE 0x25
#define INTEL_FAM6_WESTMERE_EP 0x2C
#define INTEL_FAM6_WESTMERE_EX 0x2F
@@ -36,9 +38,9 @@
#define INTEL_FAM6_HASWELL_GT3E 0x46
#define INTEL_FAM6_BROADWELL_CORE 0x3D
-#define INTEL_FAM6_BROADWELL_XEON_D 0x56
#define INTEL_FAM6_BROADWELL_GT3E 0x47
#define INTEL_FAM6_BROADWELL_X 0x4F
+#define INTEL_FAM6_BROADWELL_XEON_D 0x56
#define INTEL_FAM6_SKYLAKE_MOBILE 0x4E
#define INTEL_FAM6_SKYLAKE_DESKTOP 0x5E
@@ -57,9 +59,10 @@
#define INTEL_FAM6_ATOM_SILVERMONT2 0x4D /* Avaton/Rangely */
#define INTEL_FAM6_ATOM_AIRMONT 0x4C /* CherryTrail / Braswell */
#define INTEL_FAM6_ATOM_MERRIFIELD 0x4A /* Tangier */
-#define INTEL_FAM6_ATOM_MOOREFIELD 0x5A /* Annidale */
+#define INTEL_FAM6_ATOM_MOOREFIELD 0x5A /* Anniedale */
#define INTEL_FAM6_ATOM_GOLDMONT 0x5C
#define INTEL_FAM6_ATOM_DENVERTON 0x5F /* Goldmont Microserver */
+#define INTEL_FAM6_ATOM_GEMINI_LAKE 0x7A
/* Xeon Phi */
diff --git a/arch/x86/include/asm/msr-index.h b/arch/x86/include/asm/msr-index.h
index b11c4c0..c768bc1 100644
--- a/arch/x86/include/asm/msr-index.h
+++ b/arch/x86/include/asm/msr-index.h
@@ -37,6 +37,13 @@
#define EFER_FFXSR (1<<_EFER_FFXSR)
/* Intel MSRs. Some also available on other CPUs */
+#define MSR_IA32_SPEC_CTRL 0x00000048 /* Speculation Control */
+#define SPEC_CTRL_IBRS (1 << 0) /* Indirect Branch Restricted Speculation */
+#define SPEC_CTRL_STIBP (1 << 1) /* Single Thread Indirect Branch Predictors */
+
+#define MSR_IA32_PRED_CMD 0x00000049 /* Prediction Command */
+#define PRED_CMD_IBPB (1 << 0) /* Indirect Branch Prediction Barrier */
+
#define MSR_IA32_PERFCTR0 0x000000c1
#define MSR_IA32_PERFCTR1 0x000000c2
#define MSR_FSB_FREQ 0x000000cd
@@ -50,6 +57,11 @@
#define SNB_C3_AUTO_UNDEMOTE (1UL << 28)
#define MSR_MTRRcap 0x000000fe
+
+#define MSR_IA32_ARCH_CAPABILITIES 0x0000010a
+#define ARCH_CAP_RDCL_NO (1 << 0) /* Not susceptible to Meltdown */
+#define ARCH_CAP_IBRS_ALL (1 << 1) /* Enhanced IBRS support */
+
#define MSR_IA32_BBL_CR_CTL 0x00000119
#define MSR_IA32_BBL_CR_CTL3 0x0000011e
diff --git a/arch/x86/include/asm/msr.h b/arch/x86/include/asm/msr.h
index b5fee97..ed35b91 100644
--- a/arch/x86/include/asm/msr.h
+++ b/arch/x86/include/asm/msr.h
@@ -188,8 +188,7 @@
* that some other imaginary CPU is updating continuously with a
* time stamp.
*/
- alternative_2("", "mfence", X86_FEATURE_MFENCE_RDTSC,
- "lfence", X86_FEATURE_LFENCE_RDTSC);
+ barrier_nospec();
return rdtsc();
}
diff --git a/arch/x86/include/asm/nospec-branch.h b/arch/x86/include/asm/nospec-branch.h
index 4ad4108..300cc15 100644
--- a/arch/x86/include/asm/nospec-branch.h
+++ b/arch/x86/include/asm/nospec-branch.h
@@ -1,56 +1,12 @@
/* SPDX-License-Identifier: GPL-2.0 */
-#ifndef __NOSPEC_BRANCH_H__
-#define __NOSPEC_BRANCH_H__
+#ifndef _ASM_X86_NOSPEC_BRANCH_H_
+#define _ASM_X86_NOSPEC_BRANCH_H_
#include <asm/alternative.h>
#include <asm/alternative-asm.h>
#include <asm/cpufeatures.h>
-/*
- * Fill the CPU return stack buffer.
- *
- * Each entry in the RSB, if used for a speculative 'ret', contains an
- * infinite 'pause; lfence; jmp' loop to capture speculative execution.
- *
- * This is required in various cases for retpoline and IBRS-based
- * mitigations for the Spectre variant 2 vulnerability. Sometimes to
- * eliminate potentially bogus entries from the RSB, and sometimes
- * purely to ensure that it doesn't get empty, which on some CPUs would
- * allow predictions from other (unwanted!) sources to be used.
- *
- * We define a CPP macro such that it can be used from both .S files and
- * inline assembly. It's possible to do a .macro and then include that
- * from C via asm(".include <asm/nospec-branch.h>") but let's not go there.
- */
-
-#define RSB_CLEAR_LOOPS 32 /* To forcibly overwrite all entries */
-#define RSB_FILL_LOOPS 16 /* To avoid underflow */
-
-/*
- * Google experimented with loop-unrolling and this turned out to be
- * the optimal version — two calls, each with their own speculation
- * trap should their return address end up getting used, in a loop.
- */
-#define __FILL_RETURN_BUFFER(reg, nr, sp) \
- mov $(nr/2), reg; \
-771: \
- call 772f; \
-773: /* speculation trap */ \
- pause; \
- lfence; \
- jmp 773b; \
-772: \
- call 774f; \
-775: /* speculation trap */ \
- pause; \
- lfence; \
- jmp 775b; \
-774: \
- dec reg; \
- jnz 771b; \
- add $(BITS_PER_LONG/8) * nr, sp;
-
#ifdef __ASSEMBLY__
/*
@@ -121,17 +77,10 @@
#endif
.endm
- /*
- * A simpler FILL_RETURN_BUFFER macro. Don't make people use the CPP
- * monstrosity above, manually.
- */
-.macro FILL_RETURN_BUFFER reg:req nr:req ftr:req
+/* This clobbers the BX register */
+.macro FILL_RETURN_BUFFER nr:req ftr:req
#ifdef CONFIG_RETPOLINE
- ANNOTATE_NOSPEC_ALTERNATIVE
- ALTERNATIVE "jmp .Lskip_rsb_\@", \
- __stringify(__FILL_RETURN_BUFFER(\reg,\nr,%_ASM_SP)) \
- \ftr
-.Lskip_rsb_\@:
+ ALTERNATIVE "", "call __clear_rsb", \ftr
#endif
.endm
@@ -201,22 +150,30 @@
* On VMEXIT we must ensure that no RSB predictions learned in the guest
* can be followed in the host, by overwriting the RSB completely. Both
* retpoline and IBRS mitigations for Spectre v2 need this; only on future
- * CPUs with IBRS_ATT *might* it be avoided.
+ * CPUs with IBRS_ALL *might* it be avoided.
*/
static inline void vmexit_fill_RSB(void)
{
#ifdef CONFIG_RETPOLINE
- unsigned long loops;
-
- asm volatile (ANNOTATE_NOSPEC_ALTERNATIVE
- ALTERNATIVE("jmp 910f",
- __stringify(__FILL_RETURN_BUFFER(%0, RSB_CLEAR_LOOPS, %1)),
- X86_FEATURE_RETPOLINE)
- "910:"
- : "=r" (loops), ASM_CALL_CONSTRAINT
- : : "memory" );
+ alternative_input("",
+ "call __fill_rsb",
+ X86_FEATURE_RETPOLINE,
+ ASM_NO_INPUT_CLOBBER(_ASM_BX, "memory"));
#endif
}
+static inline void indirect_branch_prediction_barrier(void)
+{
+ asm volatile(ALTERNATIVE("",
+ "movl %[msr], %%ecx\n\t"
+ "movl %[val], %%eax\n\t"
+ "movl $0, %%edx\n\t"
+ "wrmsr",
+ X86_FEATURE_USE_IBPB)
+ : : [msr] "i" (MSR_IA32_PRED_CMD),
+ [val] "i" (PRED_CMD_IBPB)
+ : "eax", "ecx", "edx", "memory");
+}
+
#endif /* __ASSEMBLY__ */
-#endif /* __NOSPEC_BRANCH_H__ */
+#endif /* _ASM_X86_NOSPEC_BRANCH_H_ */
diff --git a/arch/x86/include/asm/pgalloc.h b/arch/x86/include/asm/pgalloc.h
index 1178a51..b6d4259 100644
--- a/arch/x86/include/asm/pgalloc.h
+++ b/arch/x86/include/asm/pgalloc.h
@@ -27,17 +27,6 @@
*/
extern gfp_t __userpte_alloc_gfp;
-#ifdef CONFIG_PAGE_TABLE_ISOLATION
-/*
- * Instead of one PGD, we acquire two PGDs. Being order-1, it is
- * both 8k in size and 8k-aligned. That lets us just flip bit 12
- * in a pointer to swap between the two 4k halves.
- */
-#define PGD_ALLOCATION_ORDER 1
-#else
-#define PGD_ALLOCATION_ORDER 0
-#endif
-
/*
* Allocate and free page tables.
*/
diff --git a/arch/x86/include/asm/pgtable.h b/arch/x86/include/asm/pgtable.h
index 2536f90..5af0401 100644
--- a/arch/x86/include/asm/pgtable.h
+++ b/arch/x86/include/asm/pgtable.h
@@ -20,9 +20,15 @@
#ifdef CONFIG_PAGE_TABLE_ISOLATION
extern int kaiser_enabled;
+/*
+ * Instead of one PGD, we acquire two PGDs. Being order-1, it is
+ * both 8k in size and 8k-aligned. That lets us just flip bit 12
+ * in a pointer to swap between the two 4k halves.
+ */
#else
#define kaiser_enabled 0
#endif
+#define PGD_ALLOCATION_ORDER kaiser_enabled
void ptdump_walk_pgd_level(struct seq_file *m, pgd_t *pgd);
void ptdump_walk_pgd_level_checkwx(void);
diff --git a/arch/x86/include/asm/processor.h b/arch/x86/include/asm/processor.h
index 353f038..cb866ae 100644
--- a/arch/x86/include/asm/processor.h
+++ b/arch/x86/include/asm/processor.h
@@ -391,8 +391,6 @@
unsigned short gsindex;
#endif
- u32 status; /* thread synchronous flags */
-
#ifdef CONFIG_X86_64
unsigned long fsbase;
unsigned long gsbase;
diff --git a/arch/x86/include/asm/required-features.h b/arch/x86/include/asm/required-features.h
index fac9a5c..6847d85 100644
--- a/arch/x86/include/asm/required-features.h
+++ b/arch/x86/include/asm/required-features.h
@@ -100,6 +100,7 @@
#define REQUIRED_MASK15 0
#define REQUIRED_MASK16 0
#define REQUIRED_MASK17 0
-#define REQUIRED_MASK_CHECK BUILD_BUG_ON_ZERO(NCAPINTS != 18)
+#define REQUIRED_MASK18 0
+#define REQUIRED_MASK_CHECK BUILD_BUG_ON_ZERO(NCAPINTS != 19)
#endif /* _ASM_X86_REQUIRED_FEATURES_H */
diff --git a/arch/x86/include/asm/syscall.h b/arch/x86/include/asm/syscall.h
index e3c95e8..03eedc2 100644
--- a/arch/x86/include/asm/syscall.h
+++ b/arch/x86/include/asm/syscall.h
@@ -60,7 +60,7 @@
* TS_COMPAT is set for 32-bit syscall entries and then
* remains set until we return to user mode.
*/
- if (task->thread.status & (TS_COMPAT|TS_I386_REGS_POKED))
+ if (task->thread_info.status & (TS_COMPAT|TS_I386_REGS_POKED))
/*
* Sign-extend the value so (int)-EFOO becomes (long)-EFOO
* and will match correctly in comparisons.
@@ -116,7 +116,7 @@
unsigned long *args)
{
# ifdef CONFIG_IA32_EMULATION
- if (task->thread.status & TS_COMPAT)
+ if (task->thread_info.status & TS_COMPAT)
switch (i) {
case 0:
if (!n--) break;
@@ -177,7 +177,7 @@
const unsigned long *args)
{
# ifdef CONFIG_IA32_EMULATION
- if (task->thread.status & TS_COMPAT)
+ if (task->thread_info.status & TS_COMPAT)
switch (i) {
case 0:
if (!n--) break;
diff --git a/arch/x86/include/asm/thread_info.h b/arch/x86/include/asm/thread_info.h
index bdf9c4c..89978b9 100644
--- a/arch/x86/include/asm/thread_info.h
+++ b/arch/x86/include/asm/thread_info.h
@@ -54,6 +54,7 @@
struct thread_info {
unsigned long flags; /* low level flags */
+ u32 status; /* thread synchronous flags */
};
#define INIT_THREAD_INFO(tsk) \
@@ -213,7 +214,7 @@
#define in_ia32_syscall() true
#else
#define in_ia32_syscall() (IS_ENABLED(CONFIG_IA32_EMULATION) && \
- current->thread.status & TS_COMPAT)
+ current_thread_info()->status & TS_COMPAT)
#endif
/*
diff --git a/arch/x86/include/asm/uaccess.h b/arch/x86/include/asm/uaccess.h
index dead0f3..a8d85a6 100644
--- a/arch/x86/include/asm/uaccess.h
+++ b/arch/x86/include/asm/uaccess.h
@@ -123,6 +123,11 @@
#define __uaccess_begin() stac()
#define __uaccess_end() clac()
+#define __uaccess_begin_nospec() \
+({ \
+ stac(); \
+ barrier_nospec(); \
+})
/*
* This is a type: either unsigned long, if the argument fits into
@@ -432,7 +437,7 @@
({ \
int __gu_err; \
__inttype(*(ptr)) __gu_val; \
- __uaccess_begin(); \
+ __uaccess_begin_nospec(); \
__get_user_size(__gu_val, (ptr), (size), __gu_err, -EFAULT); \
__uaccess_end(); \
(x) = (__force __typeof__(*(ptr)))__gu_val; \
@@ -474,6 +479,10 @@
__uaccess_begin(); \
barrier();
+#define uaccess_try_nospec do { \
+ current->thread.uaccess_err = 0; \
+ __uaccess_begin_nospec(); \
+
#define uaccess_catch(err) \
__uaccess_end(); \
(err) |= (current->thread.uaccess_err ? -EFAULT : 0); \
@@ -538,7 +547,7 @@
* get_user_ex(...);
* } get_user_catch(err)
*/
-#define get_user_try uaccess_try
+#define get_user_try uaccess_try_nospec
#define get_user_catch(err) uaccess_catch(err)
#define get_user_ex(x, ptr) do { \
@@ -573,7 +582,7 @@
__typeof__(ptr) __uval = (uval); \
__typeof__(*(ptr)) __old = (old); \
__typeof__(*(ptr)) __new = (new); \
- __uaccess_begin(); \
+ __uaccess_begin_nospec(); \
switch (size) { \
case 1: \
{ \
diff --git a/arch/x86/include/asm/uaccess_32.h b/arch/x86/include/asm/uaccess_32.h
index 7d3bdd1..d6d2450 100644
--- a/arch/x86/include/asm/uaccess_32.h
+++ b/arch/x86/include/asm/uaccess_32.h
@@ -102,17 +102,17 @@
switch (n) {
case 1:
- __uaccess_begin();
+ __uaccess_begin_nospec();
__get_user_size(*(u8 *)to, from, 1, ret, 1);
__uaccess_end();
return ret;
case 2:
- __uaccess_begin();
+ __uaccess_begin_nospec();
__get_user_size(*(u16 *)to, from, 2, ret, 2);
__uaccess_end();
return ret;
case 4:
- __uaccess_begin();
+ __uaccess_begin_nospec();
__get_user_size(*(u32 *)to, from, 4, ret, 4);
__uaccess_end();
return ret;
@@ -130,17 +130,17 @@
switch (n) {
case 1:
- __uaccess_begin();
+ __uaccess_begin_nospec();
__get_user_size(*(u8 *)to, from, 1, ret, 1);
__uaccess_end();
return ret;
case 2:
- __uaccess_begin();
+ __uaccess_begin_nospec();
__get_user_size(*(u16 *)to, from, 2, ret, 2);
__uaccess_end();
return ret;
case 4:
- __uaccess_begin();
+ __uaccess_begin_nospec();
__get_user_size(*(u32 *)to, from, 4, ret, 4);
__uaccess_end();
return ret;
diff --git a/arch/x86/include/asm/uaccess_64.h b/arch/x86/include/asm/uaccess_64.h
index 673059a..6e5cc08 100644
--- a/arch/x86/include/asm/uaccess_64.h
+++ b/arch/x86/include/asm/uaccess_64.h
@@ -59,31 +59,31 @@
return copy_user_generic(dst, (__force void *)src, size);
switch (size) {
case 1:
- __uaccess_begin();
+ __uaccess_begin_nospec();
__get_user_asm(*(u8 *)dst, (u8 __user *)src,
ret, "b", "b", "=q", 1);
__uaccess_end();
return ret;
case 2:
- __uaccess_begin();
+ __uaccess_begin_nospec();
__get_user_asm(*(u16 *)dst, (u16 __user *)src,
ret, "w", "w", "=r", 2);
__uaccess_end();
return ret;
case 4:
- __uaccess_begin();
+ __uaccess_begin_nospec();
__get_user_asm(*(u32 *)dst, (u32 __user *)src,
ret, "l", "k", "=r", 4);
__uaccess_end();
return ret;
case 8:
- __uaccess_begin();
+ __uaccess_begin_nospec();
__get_user_asm(*(u64 *)dst, (u64 __user *)src,
ret, "q", "", "=r", 8);
__uaccess_end();
return ret;
case 10:
- __uaccess_begin();
+ __uaccess_begin_nospec();
__get_user_asm(*(u64 *)dst, (u64 __user *)src,
ret, "q", "", "=r", 10);
if (likely(!ret))
@@ -93,7 +93,7 @@
__uaccess_end();
return ret;
case 16:
- __uaccess_begin();
+ __uaccess_begin_nospec();
__get_user_asm(*(u64 *)dst, (u64 __user *)src,
ret, "q", "", "=r", 16);
if (likely(!ret))
diff --git a/arch/x86/kernel/alternative.c b/arch/x86/kernel/alternative.c
index 10d5a3d..03b6e5c 100644
--- a/arch/x86/kernel/alternative.c
+++ b/arch/x86/kernel/alternative.c
@@ -46,17 +46,6 @@
}
__setup("noreplace-smp", setup_noreplace_smp);
-#ifdef CONFIG_PARAVIRT
-static int __initdata_or_module noreplace_paravirt = 0;
-
-static int __init setup_noreplace_paravirt(char *str)
-{
- noreplace_paravirt = 1;
- return 1;
-}
-__setup("noreplace-paravirt", setup_noreplace_paravirt);
-#endif
-
#define DPRINTK(fmt, args...) \
do { \
if (debug_alternative) \
@@ -588,9 +577,6 @@
struct paravirt_patch_site *p;
char insnbuf[MAX_PATCH_LEN];
- if (noreplace_paravirt)
- return;
-
for (p = start; p < end; p++) {
unsigned int used;
diff --git a/arch/x86/kernel/cpu/bugs.c b/arch/x86/kernel/cpu/bugs.c
index 8cacf62..957ad44 100644
--- a/arch/x86/kernel/cpu/bugs.c
+++ b/arch/x86/kernel/cpu/bugs.c
@@ -10,6 +10,7 @@
#include <linux/init.h>
#include <linux/utsname.h>
#include <linux/cpu.h>
+#include <linux/module.h>
#include <asm/nospec-branch.h>
#include <asm/cmdline.h>
@@ -89,20 +90,41 @@
};
#undef pr_fmt
-#define pr_fmt(fmt) "Spectre V2 mitigation: " fmt
+#define pr_fmt(fmt) "Spectre V2 : " fmt
static enum spectre_v2_mitigation spectre_v2_enabled = SPECTRE_V2_NONE;
+#ifdef RETPOLINE
+static bool spectre_v2_bad_module;
+
+bool retpoline_module_ok(bool has_retpoline)
+{
+ if (spectre_v2_enabled == SPECTRE_V2_NONE || has_retpoline)
+ return true;
+
+ pr_err("System may be vulnerable to spectre v2\n");
+ spectre_v2_bad_module = true;
+ return false;
+}
+
+static inline const char *spectre_v2_module_string(void)
+{
+ return spectre_v2_bad_module ? " - vulnerable module loaded" : "";
+}
+#else
+static inline const char *spectre_v2_module_string(void) { return ""; }
+#endif
+
static void __init spec2_print_if_insecure(const char *reason)
{
if (boot_cpu_has_bug(X86_BUG_SPECTRE_V2))
- pr_info("%s\n", reason);
+ pr_info("%s selected on command line.\n", reason);
}
static void __init spec2_print_if_secure(const char *reason)
{
if (!boot_cpu_has_bug(X86_BUG_SPECTRE_V2))
- pr_info("%s\n", reason);
+ pr_info("%s selected on command line.\n", reason);
}
static inline bool retp_compiler(void)
@@ -117,42 +139,68 @@
return len == arglen && !strncmp(arg, opt, len);
}
+static const struct {
+ const char *option;
+ enum spectre_v2_mitigation_cmd cmd;
+ bool secure;
+} mitigation_options[] = {
+ { "off", SPECTRE_V2_CMD_NONE, false },
+ { "on", SPECTRE_V2_CMD_FORCE, true },
+ { "retpoline", SPECTRE_V2_CMD_RETPOLINE, false },
+ { "retpoline,amd", SPECTRE_V2_CMD_RETPOLINE_AMD, false },
+ { "retpoline,generic", SPECTRE_V2_CMD_RETPOLINE_GENERIC, false },
+ { "auto", SPECTRE_V2_CMD_AUTO, false },
+};
+
static enum spectre_v2_mitigation_cmd __init spectre_v2_parse_cmdline(void)
{
char arg[20];
- int ret;
+ int ret, i;
+ enum spectre_v2_mitigation_cmd cmd = SPECTRE_V2_CMD_AUTO;
- ret = cmdline_find_option(boot_command_line, "spectre_v2", arg,
- sizeof(arg));
- if (ret > 0) {
- if (match_option(arg, ret, "off")) {
- goto disable;
- } else if (match_option(arg, ret, "on")) {
- spec2_print_if_secure("force enabled on command line.");
- return SPECTRE_V2_CMD_FORCE;
- } else if (match_option(arg, ret, "retpoline")) {
- spec2_print_if_insecure("retpoline selected on command line.");
- return SPECTRE_V2_CMD_RETPOLINE;
- } else if (match_option(arg, ret, "retpoline,amd")) {
- if (boot_cpu_data.x86_vendor != X86_VENDOR_AMD) {
- pr_err("retpoline,amd selected but CPU is not AMD. Switching to AUTO select\n");
- return SPECTRE_V2_CMD_AUTO;
- }
- spec2_print_if_insecure("AMD retpoline selected on command line.");
- return SPECTRE_V2_CMD_RETPOLINE_AMD;
- } else if (match_option(arg, ret, "retpoline,generic")) {
- spec2_print_if_insecure("generic retpoline selected on command line.");
- return SPECTRE_V2_CMD_RETPOLINE_GENERIC;
- } else if (match_option(arg, ret, "auto")) {
+ if (cmdline_find_option_bool(boot_command_line, "nospectre_v2"))
+ return SPECTRE_V2_CMD_NONE;
+ else {
+ ret = cmdline_find_option(boot_command_line, "spectre_v2", arg,
+ sizeof(arg));
+ if (ret < 0)
+ return SPECTRE_V2_CMD_AUTO;
+
+ for (i = 0; i < ARRAY_SIZE(mitigation_options); i++) {
+ if (!match_option(arg, ret, mitigation_options[i].option))
+ continue;
+ cmd = mitigation_options[i].cmd;
+ break;
+ }
+
+ if (i >= ARRAY_SIZE(mitigation_options)) {
+ pr_err("unknown option (%s). Switching to AUTO select\n",
+ mitigation_options[i].option);
return SPECTRE_V2_CMD_AUTO;
}
}
- if (!cmdline_find_option_bool(boot_command_line, "nospectre_v2"))
+ if ((cmd == SPECTRE_V2_CMD_RETPOLINE ||
+ cmd == SPECTRE_V2_CMD_RETPOLINE_AMD ||
+ cmd == SPECTRE_V2_CMD_RETPOLINE_GENERIC) &&
+ !IS_ENABLED(CONFIG_RETPOLINE)) {
+ pr_err("%s selected but not compiled in. Switching to AUTO select\n",
+ mitigation_options[i].option);
return SPECTRE_V2_CMD_AUTO;
-disable:
- spec2_print_if_insecure("disabled on command line.");
- return SPECTRE_V2_CMD_NONE;
+ }
+
+ if (cmd == SPECTRE_V2_CMD_RETPOLINE_AMD &&
+ boot_cpu_data.x86_vendor != X86_VENDOR_AMD) {
+ pr_err("retpoline,amd selected but CPU is not AMD. Switching to AUTO select\n");
+ return SPECTRE_V2_CMD_AUTO;
+ }
+
+ if (mitigation_options[i].secure)
+ spec2_print_if_secure(mitigation_options[i].option);
+ else
+ spec2_print_if_insecure(mitigation_options[i].option);
+
+ return cmd;
}
/* Check for Skylake-like CPUs (for RSB handling) */
@@ -190,10 +238,10 @@
return;
case SPECTRE_V2_CMD_FORCE:
- /* FALLTRHU */
case SPECTRE_V2_CMD_AUTO:
- goto retpoline_auto;
-
+ if (IS_ENABLED(CONFIG_RETPOLINE))
+ goto retpoline_auto;
+ break;
case SPECTRE_V2_CMD_RETPOLINE_AMD:
if (IS_ENABLED(CONFIG_RETPOLINE))
goto retpoline_amd;
@@ -248,6 +296,12 @@
setup_force_cpu_cap(X86_FEATURE_RSB_CTXSW);
pr_info("Filling RSB on context switch\n");
}
+
+ /* Initialize Indirect Branch Prediction Barrier if supported */
+ if (boot_cpu_has(X86_FEATURE_IBPB)) {
+ setup_force_cpu_cap(X86_FEATURE_USE_IBPB);
+ pr_info("Enabling Indirect Branch Prediction Barrier\n");
+ }
}
#undef pr_fmt
@@ -268,7 +322,7 @@
{
if (!boot_cpu_has_bug(X86_BUG_SPECTRE_V1))
return sprintf(buf, "Not affected\n");
- return sprintf(buf, "Vulnerable\n");
+ return sprintf(buf, "Mitigation: __user pointer sanitization\n");
}
ssize_t cpu_show_spectre_v2(struct device *dev,
@@ -277,6 +331,8 @@
if (!boot_cpu_has_bug(X86_BUG_SPECTRE_V2))
return sprintf(buf, "Not affected\n");
- return sprintf(buf, "%s\n", spectre_v2_strings[spectre_v2_enabled]);
+ return sprintf(buf, "%s%s%s\n", spectre_v2_strings[spectre_v2_enabled],
+ boot_cpu_has(X86_FEATURE_USE_IBPB) ? ", IBPB" : "",
+ spectre_v2_module_string());
}
#endif
diff --git a/arch/x86/kernel/cpu/common.c b/arch/x86/kernel/cpu/common.c
index d198ae0..08e89ed 100644
--- a/arch/x86/kernel/cpu/common.c
+++ b/arch/x86/kernel/cpu/common.c
@@ -44,6 +44,8 @@
#include <asm/pat.h>
#include <asm/microcode.h>
#include <asm/microcode_intel.h>
+#include <asm/intel-family.h>
+#include <asm/cpu_device_id.h>
#ifdef CONFIG_X86_LOCAL_APIC
#include <asm/uv/uv.h>
@@ -716,6 +718,26 @@
}
}
+static void init_speculation_control(struct cpuinfo_x86 *c)
+{
+ /*
+ * The Intel SPEC_CTRL CPUID bit implies IBRS and IBPB support,
+ * and they also have a different bit for STIBP support. Also,
+ * a hypervisor might have set the individual AMD bits even on
+ * Intel CPUs, for finer-grained selection of what's available.
+ *
+ * We use the AMD bits in 0x8000_0008 EBX as the generic hardware
+ * features, which are visible in /proc/cpuinfo and used by the
+ * kernel. So set those accordingly from the Intel bits.
+ */
+ if (cpu_has(c, X86_FEATURE_SPEC_CTRL)) {
+ set_cpu_cap(c, X86_FEATURE_IBRS);
+ set_cpu_cap(c, X86_FEATURE_IBPB);
+ }
+ if (cpu_has(c, X86_FEATURE_INTEL_STIBP))
+ set_cpu_cap(c, X86_FEATURE_STIBP);
+}
+
void get_cpu_cap(struct cpuinfo_x86 *c)
{
u32 eax, ebx, ecx, edx;
@@ -737,6 +759,7 @@
cpuid_count(0x00000007, 0, &eax, &ebx, &ecx, &edx);
c->x86_capability[CPUID_7_0_EBX] = ebx;
c->x86_capability[CPUID_7_ECX] = ecx;
+ c->x86_capability[CPUID_7_EDX] = edx;
}
/* Extended state features: level 0x0000000d */
@@ -809,6 +832,7 @@
c->x86_capability[CPUID_8000_000A_EDX] = cpuid_edx(0x8000000a);
init_scattered_cpuid_features(c);
+ init_speculation_control(c);
}
static void identify_cpu_without_cpuid(struct cpuinfo_x86 *c)
@@ -837,6 +861,41 @@
#endif
}
+static const __initconst struct x86_cpu_id cpu_no_speculation[] = {
+ { X86_VENDOR_INTEL, 6, INTEL_FAM6_ATOM_CEDARVIEW, X86_FEATURE_ANY },
+ { X86_VENDOR_INTEL, 6, INTEL_FAM6_ATOM_CLOVERVIEW, X86_FEATURE_ANY },
+ { X86_VENDOR_INTEL, 6, INTEL_FAM6_ATOM_LINCROFT, X86_FEATURE_ANY },
+ { X86_VENDOR_INTEL, 6, INTEL_FAM6_ATOM_PENWELL, X86_FEATURE_ANY },
+ { X86_VENDOR_INTEL, 6, INTEL_FAM6_ATOM_PINEVIEW, X86_FEATURE_ANY },
+ { X86_VENDOR_CENTAUR, 5 },
+ { X86_VENDOR_INTEL, 5 },
+ { X86_VENDOR_NSC, 5 },
+ { X86_VENDOR_ANY, 4 },
+ {}
+};
+
+static const __initconst struct x86_cpu_id cpu_no_meltdown[] = {
+ { X86_VENDOR_AMD },
+ {}
+};
+
+static bool __init cpu_vulnerable_to_meltdown(struct cpuinfo_x86 *c)
+{
+ u64 ia32_cap = 0;
+
+ if (x86_match_cpu(cpu_no_meltdown))
+ return false;
+
+ if (cpu_has(c, X86_FEATURE_ARCH_CAPABILITIES))
+ rdmsrl(MSR_IA32_ARCH_CAPABILITIES, ia32_cap);
+
+ /* Rogue Data Cache Load? No! */
+ if (ia32_cap & ARCH_CAP_RDCL_NO)
+ return false;
+
+ return true;
+}
+
/*
* Do minimum CPU detection early.
* Fields really needed: vendor, cpuid_level, family, model, mask,
@@ -883,11 +942,12 @@
setup_force_cpu_cap(X86_FEATURE_ALWAYS);
- if (c->x86_vendor != X86_VENDOR_AMD)
- setup_force_cpu_bug(X86_BUG_CPU_MELTDOWN);
-
- setup_force_cpu_bug(X86_BUG_SPECTRE_V1);
- setup_force_cpu_bug(X86_BUG_SPECTRE_V2);
+ if (!x86_match_cpu(cpu_no_speculation)) {
+ if (cpu_vulnerable_to_meltdown(c))
+ setup_force_cpu_bug(X86_BUG_CPU_MELTDOWN);
+ setup_force_cpu_bug(X86_BUG_SPECTRE_V1);
+ setup_force_cpu_bug(X86_BUG_SPECTRE_V2);
+ }
fpu__init_system(c);
diff --git a/arch/x86/kernel/cpu/intel.c b/arch/x86/kernel/cpu/intel.c
index fcd484d..4097b43 100644
--- a/arch/x86/kernel/cpu/intel.c
+++ b/arch/x86/kernel/cpu/intel.c
@@ -61,6 +61,59 @@
}
}
+/*
+ * Early microcode releases for the Spectre v2 mitigation were broken.
+ * Information taken from;
+ * - https://newsroom.intel.com/wp-content/uploads/sites/11/2018/01/microcode-update-guidance.pdf
+ * - https://kb.vmware.com/s/article/52345
+ * - Microcode revisions observed in the wild
+ * - Release note from 20180108 microcode release
+ */
+struct sku_microcode {
+ u8 model;
+ u8 stepping;
+ u32 microcode;
+};
+static const struct sku_microcode spectre_bad_microcodes[] = {
+ { INTEL_FAM6_KABYLAKE_DESKTOP, 0x0B, 0x84 },
+ { INTEL_FAM6_KABYLAKE_DESKTOP, 0x0A, 0x84 },
+ { INTEL_FAM6_KABYLAKE_DESKTOP, 0x09, 0x84 },
+ { INTEL_FAM6_KABYLAKE_MOBILE, 0x0A, 0x84 },
+ { INTEL_FAM6_KABYLAKE_MOBILE, 0x09, 0x84 },
+ { INTEL_FAM6_SKYLAKE_X, 0x03, 0x0100013e },
+ { INTEL_FAM6_SKYLAKE_X, 0x04, 0x0200003c },
+ { INTEL_FAM6_SKYLAKE_MOBILE, 0x03, 0xc2 },
+ { INTEL_FAM6_SKYLAKE_DESKTOP, 0x03, 0xc2 },
+ { INTEL_FAM6_BROADWELL_CORE, 0x04, 0x28 },
+ { INTEL_FAM6_BROADWELL_GT3E, 0x01, 0x1b },
+ { INTEL_FAM6_BROADWELL_XEON_D, 0x02, 0x14 },
+ { INTEL_FAM6_BROADWELL_XEON_D, 0x03, 0x07000011 },
+ { INTEL_FAM6_BROADWELL_X, 0x01, 0x0b000025 },
+ { INTEL_FAM6_HASWELL_ULT, 0x01, 0x21 },
+ { INTEL_FAM6_HASWELL_GT3E, 0x01, 0x18 },
+ { INTEL_FAM6_HASWELL_CORE, 0x03, 0x23 },
+ { INTEL_FAM6_HASWELL_X, 0x02, 0x3b },
+ { INTEL_FAM6_HASWELL_X, 0x04, 0x10 },
+ { INTEL_FAM6_IVYBRIDGE_X, 0x04, 0x42a },
+ /* Updated in the 20180108 release; blacklist until we know otherwise */
+ { INTEL_FAM6_ATOM_GEMINI_LAKE, 0x01, 0x22 },
+ /* Observed in the wild */
+ { INTEL_FAM6_SANDYBRIDGE_X, 0x06, 0x61b },
+ { INTEL_FAM6_SANDYBRIDGE_X, 0x07, 0x712 },
+};
+
+static bool bad_spectre_microcode(struct cpuinfo_x86 *c)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(spectre_bad_microcodes); i++) {
+ if (c->x86_model == spectre_bad_microcodes[i].model &&
+ c->x86_mask == spectre_bad_microcodes[i].stepping)
+ return (c->microcode <= spectre_bad_microcodes[i].microcode);
+ }
+ return false;
+}
+
static void early_init_intel(struct cpuinfo_x86 *c)
{
u64 misc_enable;
@@ -87,6 +140,19 @@
rdmsr(MSR_IA32_UCODE_REV, lower_word, c->microcode);
}
+ /* Now if any of them are set, check the blacklist and clear the lot */
+ if ((cpu_has(c, X86_FEATURE_SPEC_CTRL) ||
+ cpu_has(c, X86_FEATURE_INTEL_STIBP) ||
+ cpu_has(c, X86_FEATURE_IBRS) || cpu_has(c, X86_FEATURE_IBPB) ||
+ cpu_has(c, X86_FEATURE_STIBP)) && bad_spectre_microcode(c)) {
+ pr_warn("Intel Spectre v2 broken microcode detected; disabling Speculation Control\n");
+ setup_clear_cpu_cap(X86_FEATURE_IBRS);
+ setup_clear_cpu_cap(X86_FEATURE_IBPB);
+ setup_clear_cpu_cap(X86_FEATURE_STIBP);
+ setup_clear_cpu_cap(X86_FEATURE_SPEC_CTRL);
+ setup_clear_cpu_cap(X86_FEATURE_INTEL_STIBP);
+ }
+
/*
* Atom erratum AAE44/AAF40/AAG38/AAH41:
*
diff --git a/arch/x86/kernel/cpu/microcode/core.c b/arch/x86/kernel/cpu/microcode/core.c
index 5ce5155..0afaf00 100644
--- a/arch/x86/kernel/cpu/microcode/core.c
+++ b/arch/x86/kernel/cpu/microcode/core.c
@@ -43,7 +43,7 @@
#define MICROCODE_VERSION "2.01"
static struct microcode_ops *microcode_ops;
-static bool dis_ucode_ldr;
+static bool dis_ucode_ldr = true;
/*
* Synchronization.
@@ -73,6 +73,7 @@
static bool __init check_loader_disabled_bsp(void)
{
static const char *__dis_opt_str = "dis_ucode_ldr";
+ u32 a, b, c, d;
#ifdef CONFIG_X86_32
const char *cmdline = (const char *)__pa_nodebug(boot_command_line);
@@ -85,8 +86,20 @@
bool *res = &dis_ucode_ldr;
#endif
- if (cmdline_find_option_bool(cmdline, option))
- *res = true;
+ a = 1;
+ c = 0;
+ native_cpuid(&a, &b, &c, &d);
+
+ /*
+ * CPUID(1).ECX[31]: reserved for hypervisor use. This is still not
+ * completely accurate as xen pv guests don't see that CPUID bit set but
+ * that's good enough as they don't land on the BSP path anyway.
+ */
+ if (c & BIT(31))
+ return *res;
+
+ if (cmdline_find_option_bool(cmdline, option) <= 0)
+ *res = false;
return *res;
}
@@ -114,9 +127,7 @@
{
int vendor;
unsigned int family;
-
- if (check_loader_disabled_bsp())
- return;
+ bool intel = true;
if (!have_cpuid_p())
return;
@@ -126,16 +137,27 @@
switch (vendor) {
case X86_VENDOR_INTEL:
- if (family >= 6)
- load_ucode_intel_bsp();
+ if (family < 6)
+ return;
break;
+
case X86_VENDOR_AMD:
- if (family >= 0x10)
- load_ucode_amd_bsp(family);
+ if (family < 0x10)
+ return;
+ intel = false;
break;
+
default:
- break;
+ return;
}
+
+ if (check_loader_disabled_bsp())
+ return;
+
+ if (intel)
+ load_ucode_intel_bsp();
+ else
+ load_ucode_amd_bsp(family);
}
static bool check_loader_disabled_ap(void)
@@ -154,9 +176,6 @@
if (check_loader_disabled_ap())
return;
- if (!have_cpuid_p())
- return;
-
vendor = x86_cpuid_vendor();
family = x86_cpuid_family();
diff --git a/arch/x86/kernel/cpu/scattered.c b/arch/x86/kernel/cpu/scattered.c
index b0dd9ae..afbb525 100644
--- a/arch/x86/kernel/cpu/scattered.c
+++ b/arch/x86/kernel/cpu/scattered.c
@@ -31,8 +31,6 @@
const struct cpuid_bit *cb;
static const struct cpuid_bit cpuid_bits[] = {
- { X86_FEATURE_AVX512_4VNNIW, CR_EDX, 2, 0x00000007, 0 },
- { X86_FEATURE_AVX512_4FMAPS, CR_EDX, 3, 0x00000007, 0 },
{ X86_FEATURE_APERFMPERF, CR_ECX, 0, 0x00000006, 0 },
{ X86_FEATURE_EPB, CR_ECX, 3, 0x00000006, 0 },
{ X86_FEATURE_HW_PSTATE, CR_EDX, 7, 0x80000007, 0 },
diff --git a/arch/x86/kernel/process_64.c b/arch/x86/kernel/process_64.c
index 0887d2a..dffe81d 100644
--- a/arch/x86/kernel/process_64.c
+++ b/arch/x86/kernel/process_64.c
@@ -538,7 +538,7 @@
current->personality &= ~READ_IMPLIES_EXEC;
/* in_compat_syscall() uses the presence of the x32
syscall bit flag to determine compat status */
- current->thread.status &= ~TS_COMPAT;
+ current_thread_info()->status &= ~TS_COMPAT;
} else {
set_thread_flag(TIF_IA32);
clear_thread_flag(TIF_X32);
@@ -546,7 +546,7 @@
current->mm->context.ia32_compat = TIF_IA32;
current->personality |= force_personality32;
/* Prepare the first "return" to user space */
- current->thread.status |= TS_COMPAT;
+ current_thread_info()->status |= TS_COMPAT;
}
}
EXPORT_SYMBOL_GPL(set_personality_ia32);
diff --git a/arch/x86/kernel/ptrace.c b/arch/x86/kernel/ptrace.c
index 0e63c02..e497d37 100644
--- a/arch/x86/kernel/ptrace.c
+++ b/arch/x86/kernel/ptrace.c
@@ -934,7 +934,7 @@
*/
regs->orig_ax = value;
if (syscall_get_nr(child, regs) >= 0)
- child->thread.status |= TS_I386_REGS_POKED;
+ child->thread_info.status |= TS_I386_REGS_POKED;
break;
case offsetof(struct user32, regs.eflags):
diff --git a/arch/x86/kernel/signal.c b/arch/x86/kernel/signal.c
index 763af1d..b1a5d25 100644
--- a/arch/x86/kernel/signal.c
+++ b/arch/x86/kernel/signal.c
@@ -785,7 +785,7 @@
* than the tracee.
*/
#ifdef CONFIG_IA32_EMULATION
- if (current->thread.status & (TS_COMPAT|TS_I386_REGS_POKED))
+ if (current_thread_info()->status & (TS_COMPAT|TS_I386_REGS_POKED))
return __NR_ia32_restart_syscall;
#endif
#ifdef CONFIG_X86_X32_ABI
diff --git a/arch/x86/kernel/tboot.c b/arch/x86/kernel/tboot.c
index 8402907..21454e2 100644
--- a/arch/x86/kernel/tboot.c
+++ b/arch/x86/kernel/tboot.c
@@ -134,6 +134,16 @@
return -1;
set_pte_at(&tboot_mm, vaddr, pte, pfn_pte(pfn, prot));
pte_unmap(pte);
+
+ /*
+ * PTI poisons low addresses in the kernel page tables in the
+ * name of making them unusable for userspace. To execute
+ * code at such a low address, the poison must be cleared.
+ *
+ * Note: 'pgd' actually gets set in pud_alloc().
+ */
+ pgd->pgd &= ~_PAGE_NX;
+
return 0;
}
diff --git a/arch/x86/kvm/cpuid.c b/arch/x86/kvm/cpuid.c
index 91af75e..93f924d 100644
--- a/arch/x86/kvm/cpuid.c
+++ b/arch/x86/kvm/cpuid.c
@@ -355,6 +355,10 @@
F(3DNOWPREFETCH) | F(OSVW) | 0 /* IBS */ | F(XOP) |
0 /* SKINIT, WDT, LWP */ | F(FMA4) | F(TBM);
+ /* cpuid 0x80000008.ebx */
+ const u32 kvm_cpuid_8000_0008_ebx_x86_features =
+ F(IBPB) | F(IBRS);
+
/* cpuid 0xC0000001.edx */
const u32 kvm_cpuid_C000_0001_edx_x86_features =
F(XSTORE) | F(XSTORE_EN) | F(XCRYPT) | F(XCRYPT_EN) |
@@ -376,6 +380,10 @@
/* cpuid 7.0.ecx*/
const u32 kvm_cpuid_7_0_ecx_x86_features = F(PKU) | 0 /*OSPKE*/;
+ /* cpuid 7.0.edx*/
+ const u32 kvm_cpuid_7_0_edx_x86_features =
+ F(SPEC_CTRL) | F(ARCH_CAPABILITIES);
+
/* all calls to cpuid_count() should be made on the same cpu */
get_cpu();
@@ -458,12 +466,14 @@
/* PKU is not yet implemented for shadow paging. */
if (!tdp_enabled || !boot_cpu_has(X86_FEATURE_OSPKE))
entry->ecx &= ~F(PKU);
+ entry->edx &= kvm_cpuid_7_0_edx_x86_features;
+ cpuid_mask(&entry->edx, CPUID_7_EDX);
} else {
entry->ebx = 0;
entry->ecx = 0;
+ entry->edx = 0;
}
entry->eax = 0;
- entry->edx = 0;
break;
}
case 9:
@@ -607,7 +617,14 @@
if (!g_phys_as)
g_phys_as = phys_as;
entry->eax = g_phys_as | (virt_as << 8);
- entry->ebx = entry->edx = 0;
+ entry->edx = 0;
+ /* IBRS and IBPB aren't necessarily present in hardware cpuid */
+ if (boot_cpu_has(X86_FEATURE_IBPB))
+ entry->ebx |= F(IBPB);
+ if (boot_cpu_has(X86_FEATURE_IBRS))
+ entry->ebx |= F(IBRS);
+ entry->ebx &= kvm_cpuid_8000_0008_ebx_x86_features;
+ cpuid_mask(&entry->ebx, CPUID_8000_0008_EBX);
break;
}
case 0x80000019:
diff --git a/arch/x86/kvm/cpuid.h b/arch/x86/kvm/cpuid.h
index 9368fec..d1beb71 100644
--- a/arch/x86/kvm/cpuid.h
+++ b/arch/x86/kvm/cpuid.h
@@ -160,6 +160,37 @@
return best && (best->edx & bit(X86_FEATURE_RDTSCP));
}
+static inline bool guest_cpuid_has_ibpb(struct kvm_vcpu *vcpu)
+{
+ struct kvm_cpuid_entry2 *best;
+
+ best = kvm_find_cpuid_entry(vcpu, 0x80000008, 0);
+ if (best && (best->ebx & bit(X86_FEATURE_IBPB)))
+ return true;
+ best = kvm_find_cpuid_entry(vcpu, 7, 0);
+ return best && (best->edx & bit(X86_FEATURE_SPEC_CTRL));
+}
+
+static inline bool guest_cpuid_has_ibrs(struct kvm_vcpu *vcpu)
+{
+ struct kvm_cpuid_entry2 *best;
+
+ best = kvm_find_cpuid_entry(vcpu, 0x80000008, 0);
+ if (best && (best->ebx & bit(X86_FEATURE_IBRS)))
+ return true;
+ best = kvm_find_cpuid_entry(vcpu, 7, 0);
+ return best && (best->edx & bit(X86_FEATURE_SPEC_CTRL));
+}
+
+static inline bool guest_cpuid_has_arch_capabilities(struct kvm_vcpu *vcpu)
+{
+ struct kvm_cpuid_entry2 *best;
+
+ best = kvm_find_cpuid_entry(vcpu, 7, 0);
+ return best && (best->edx & bit(X86_FEATURE_ARCH_CAPABILITIES));
+}
+
+
/*
* NRIPS is provided through cpuidfn 0x8000000a.edx bit 3
*/
diff --git a/arch/x86/kvm/emulate.c b/arch/x86/kvm/emulate.c
index 6f5a3b0..c8d5738 100644
--- a/arch/x86/kvm/emulate.c
+++ b/arch/x86/kvm/emulate.c
@@ -25,6 +25,7 @@
#include <asm/kvm_emulate.h>
#include <linux/stringify.h>
#include <asm/debugreg.h>
+#include <asm/nospec-branch.h>
#include "x86.h"
#include "tss.h"
@@ -1012,8 +1013,8 @@
void (*fop)(void) = (void *)em_setcc + 4 * (condition & 0xf);
flags = (flags & EFLAGS_MASK) | X86_EFLAGS_IF;
- asm("push %[flags]; popf; call *%[fastop]"
- : "=a"(rc) : [fastop]"r"(fop), [flags]"r"(flags));
+ asm("push %[flags]; popf; " CALL_NOSPEC
+ : "=a"(rc) : [thunk_target]"r"(fop), [flags]"r"(flags));
return rc;
}
@@ -5306,15 +5307,14 @@
static int fastop(struct x86_emulate_ctxt *ctxt, void (*fop)(struct fastop *))
{
- register void *__sp asm(_ASM_SP);
ulong flags = (ctxt->eflags & EFLAGS_MASK) | X86_EFLAGS_IF;
if (!(ctxt->d & ByteOp))
fop += __ffs(ctxt->dst.bytes) * FASTOP_SIZE;
- asm("push %[flags]; popf; call *%[fastop]; pushf; pop %[flags]\n"
+ asm("push %[flags]; popf; " CALL_NOSPEC " ; pushf; pop %[flags]\n"
: "+a"(ctxt->dst.val), "+d"(ctxt->src.val), [flags]"+D"(flags),
- [fastop]"+S"(fop), "+r"(__sp)
+ [thunk_target]"+S"(fop), ASM_CALL_CONSTRAINT
: "c"(ctxt->src2.val));
ctxt->eflags = (ctxt->eflags & ~EFLAGS_MASK) | (flags & EFLAGS_MASK);
diff --git a/arch/x86/kvm/svm.c b/arch/x86/kvm/svm.c
index 24af898..be644af 100644
--- a/arch/x86/kvm/svm.c
+++ b/arch/x86/kvm/svm.c
@@ -183,6 +183,8 @@
u64 gs_base;
} host;
+ u64 spec_ctrl;
+
u32 *msrpm;
ulong nmi_iret_rip;
@@ -248,6 +250,8 @@
{ .index = MSR_CSTAR, .always = true },
{ .index = MSR_SYSCALL_MASK, .always = true },
#endif
+ { .index = MSR_IA32_SPEC_CTRL, .always = false },
+ { .index = MSR_IA32_PRED_CMD, .always = false },
{ .index = MSR_IA32_LASTBRANCHFROMIP, .always = false },
{ .index = MSR_IA32_LASTBRANCHTOIP, .always = false },
{ .index = MSR_IA32_LASTINTFROMIP, .always = false },
@@ -510,6 +514,7 @@
struct kvm_ldttss_desc *tss_desc;
struct page *save_area;
+ struct vmcb *current_vmcb;
};
static DEFINE_PER_CPU(struct svm_cpu_data *, svm_data);
@@ -861,6 +866,25 @@
return false;
}
+static bool msr_write_intercepted(struct kvm_vcpu *vcpu, unsigned msr)
+{
+ u8 bit_write;
+ unsigned long tmp;
+ u32 offset;
+ u32 *msrpm;
+
+ msrpm = is_guest_mode(vcpu) ? to_svm(vcpu)->nested.msrpm:
+ to_svm(vcpu)->msrpm;
+
+ offset = svm_msrpm_offset(msr);
+ bit_write = 2 * (msr & 0x0f) + 1;
+ tmp = msrpm[offset];
+
+ BUG_ON(offset == MSR_INVALID);
+
+ return !!test_bit(bit_write, &tmp);
+}
+
static void set_msr_interception(u32 *msrpm, unsigned msr,
int read, int write)
{
@@ -1535,6 +1559,8 @@
u32 dummy;
u32 eax = 1;
+ svm->spec_ctrl = 0;
+
if (!init_event) {
svm->vcpu.arch.apic_base = APIC_DEFAULT_PHYS_BASE |
MSR_IA32_APICBASE_ENABLE;
@@ -1644,11 +1670,17 @@
__free_pages(virt_to_page(svm->nested.msrpm), MSRPM_ALLOC_ORDER);
kvm_vcpu_uninit(vcpu);
kmem_cache_free(kvm_vcpu_cache, svm);
+ /*
+ * The vmcb page can be recycled, causing a false negative in
+ * svm_vcpu_load(). So do a full IBPB now.
+ */
+ indirect_branch_prediction_barrier();
}
static void svm_vcpu_load(struct kvm_vcpu *vcpu, int cpu)
{
struct vcpu_svm *svm = to_svm(vcpu);
+ struct svm_cpu_data *sd = per_cpu(svm_data, cpu);
int i;
if (unlikely(cpu != vcpu->cpu)) {
@@ -1677,6 +1709,10 @@
if (static_cpu_has(X86_FEATURE_RDTSCP))
wrmsrl(MSR_TSC_AUX, svm->tsc_aux);
+ if (sd->current_vmcb != svm->vmcb) {
+ sd->current_vmcb = svm->vmcb;
+ indirect_branch_prediction_barrier();
+ }
avic_vcpu_load(vcpu, cpu);
}
@@ -3508,6 +3544,13 @@
case MSR_VM_CR:
msr_info->data = svm->nested.vm_cr_msr;
break;
+ case MSR_IA32_SPEC_CTRL:
+ if (!msr_info->host_initiated &&
+ !guest_cpuid_has_ibrs(vcpu))
+ return 1;
+
+ msr_info->data = svm->spec_ctrl;
+ break;
case MSR_IA32_UCODE_REV:
msr_info->data = 0x01000065;
break;
@@ -3599,6 +3642,49 @@
case MSR_IA32_TSC:
kvm_write_tsc(vcpu, msr);
break;
+ case MSR_IA32_SPEC_CTRL:
+ if (!msr->host_initiated &&
+ !guest_cpuid_has_ibrs(vcpu))
+ return 1;
+
+ /* The STIBP bit doesn't fault even if it's not advertised */
+ if (data & ~(SPEC_CTRL_IBRS | SPEC_CTRL_STIBP))
+ return 1;
+
+ svm->spec_ctrl = data;
+
+ if (!data)
+ break;
+
+ /*
+ * For non-nested:
+ * When it's written (to non-zero) for the first time, pass
+ * it through.
+ *
+ * For nested:
+ * The handling of the MSR bitmap for L2 guests is done in
+ * nested_svm_vmrun_msrpm.
+ * We update the L1 MSR bit as well since it will end up
+ * touching the MSR anyway now.
+ */
+ set_msr_interception(svm->msrpm, MSR_IA32_SPEC_CTRL, 1, 1);
+ break;
+ case MSR_IA32_PRED_CMD:
+ if (!msr->host_initiated &&
+ !guest_cpuid_has_ibpb(vcpu))
+ return 1;
+
+ if (data & ~PRED_CMD_IBPB)
+ return 1;
+
+ if (!data)
+ break;
+
+ wrmsrl(MSR_IA32_PRED_CMD, PRED_CMD_IBPB);
+ if (is_guest_mode(vcpu))
+ break;
+ set_msr_interception(svm->msrpm, MSR_IA32_PRED_CMD, 0, 1);
+ break;
case MSR_STAR:
svm->vmcb->save.star = data;
break;
@@ -4826,6 +4912,15 @@
local_irq_enable();
+ /*
+ * If this vCPU has touched SPEC_CTRL, restore the guest's value if
+ * it's non-zero. Since vmentry is serialising on affected CPUs, there
+ * is no need to worry about the conditional branch over the wrmsr
+ * being speculatively taken.
+ */
+ if (svm->spec_ctrl)
+ wrmsrl(MSR_IA32_SPEC_CTRL, svm->spec_ctrl);
+
asm volatile (
"push %%" _ASM_BP "; \n\t"
"mov %c[rbx](%[svm]), %%" _ASM_BX " \n\t"
@@ -4918,6 +5013,27 @@
#endif
);
+ /*
+ * We do not use IBRS in the kernel. If this vCPU has used the
+ * SPEC_CTRL MSR it may have left it on; save the value and
+ * turn it off. This is much more efficient than blindly adding
+ * it to the atomic save/restore list. Especially as the former
+ * (Saving guest MSRs on vmexit) doesn't even exist in KVM.
+ *
+ * For non-nested case:
+ * If the L01 MSR bitmap does not intercept the MSR, then we need to
+ * save it.
+ *
+ * For nested case:
+ * If the L02 MSR bitmap does not intercept the MSR, then we need to
+ * save it.
+ */
+ if (!msr_write_intercepted(vcpu, MSR_IA32_SPEC_CTRL))
+ rdmsrl(MSR_IA32_SPEC_CTRL, svm->spec_ctrl);
+
+ if (svm->spec_ctrl)
+ wrmsrl(MSR_IA32_SPEC_CTRL, 0);
+
/* Eliminate branch target predictions from guest mode */
vmexit_fill_RSB();
diff --git a/arch/x86/kvm/vmx.c b/arch/x86/kvm/vmx.c
index 178a344..d49da86 100644
--- a/arch/x86/kvm/vmx.c
+++ b/arch/x86/kvm/vmx.c
@@ -33,6 +33,7 @@
#include <linux/slab.h>
#include <linux/tboot.h>
#include <linux/hrtimer.h>
+#include <linux/nospec.h>
#include "kvm_cache_regs.h"
#include "x86.h"
@@ -109,6 +110,14 @@
static bool __read_mostly enable_pml = 1;
module_param_named(pml, enable_pml, bool, S_IRUGO);
+#define MSR_TYPE_R 1
+#define MSR_TYPE_W 2
+#define MSR_TYPE_RW 3
+
+#define MSR_BITMAP_MODE_X2APIC 1
+#define MSR_BITMAP_MODE_X2APIC_APICV 2
+#define MSR_BITMAP_MODE_LM 4
+
#define KVM_VMX_TSC_MULTIPLIER_MAX 0xffffffffffffffffULL
/* Guest_tsc -> host_tsc conversion requires 64-bit division. */
@@ -173,7 +182,6 @@
extern const ulong vmx_return;
#define NR_AUTOLOAD_MSRS 8
-#define VMCS02_POOL_SIZE 1
struct vmcs {
u32 revision_id;
@@ -191,6 +199,7 @@
struct vmcs *shadow_vmcs;
int cpu;
int launched;
+ unsigned long *msr_bitmap;
struct list_head loaded_vmcss_on_cpu_link;
};
@@ -207,7 +216,7 @@
* stored in guest memory specified by VMPTRLD, but is opaque to the guest,
* which must access it using VMREAD/VMWRITE/VMCLEAR instructions.
* More than one of these structures may exist, if L1 runs multiple L2 guests.
- * nested_vmx_run() will use the data here to build a vmcs02: a VMCS for the
+ * nested_vmx_run() will use the data here to build the vmcs02: a VMCS for the
* underlying hardware which will be used to run L2.
* This structure is packed to ensure that its layout is identical across
* machines (necessary for live migration).
@@ -386,13 +395,6 @@
*/
#define VMCS12_SIZE 0x1000
-/* Used to remember the last vmcs02 used for some recently used vmcs12s */
-struct vmcs02_list {
- struct list_head list;
- gpa_t vmptr;
- struct loaded_vmcs vmcs02;
-};
-
/*
* The nested_vmx structure is part of vcpu_vmx, and holds information we need
* for correct emulation of VMX (i.e., nested VMX) on this vcpu.
@@ -419,15 +421,15 @@
*/
bool sync_shadow_vmcs;
- /* vmcs02_list cache of VMCSs recently used to run L2 guests */
- struct list_head vmcs02_pool;
- int vmcs02_num;
bool change_vmcs01_virtual_x2apic_mode;
/* L2 must run next, and mustn't decide to exit to L1. */
bool nested_run_pending;
+
+ struct loaded_vmcs vmcs02;
+
/*
- * Guest pages referred to in vmcs02 with host-physical pointers, so
- * we must keep them pinned while L2 runs.
+ * Guest pages referred to in the vmcs02 with host-physical
+ * pointers, so we must keep them pinned while L2 runs.
*/
struct page *apic_access_page;
struct page *virtual_apic_page;
@@ -436,8 +438,6 @@
bool pi_pending;
u16 posted_intr_nv;
- unsigned long *msr_bitmap;
-
struct hrtimer preemption_timer;
bool preemption_timer_expired;
@@ -538,6 +538,7 @@
unsigned long host_rsp;
u8 fail;
bool nmi_known_unmasked;
+ u8 msr_bitmap_mode;
u32 exit_intr_info;
u32 idt_vectoring_info;
ulong rflags;
@@ -549,6 +550,10 @@
u64 msr_host_kernel_gs_base;
u64 msr_guest_kernel_gs_base;
#endif
+
+ u64 arch_capabilities;
+ u64 spec_ctrl;
+
u32 vm_entry_controls_shadow;
u32 vm_exit_controls_shadow;
/*
@@ -856,21 +861,18 @@
static inline short vmcs_field_to_offset(unsigned long field)
{
- BUILD_BUG_ON(ARRAY_SIZE(vmcs_field_to_offset_table) > SHRT_MAX);
+ const size_t size = ARRAY_SIZE(vmcs_field_to_offset_table);
+ unsigned short offset;
- if (field >= ARRAY_SIZE(vmcs_field_to_offset_table))
+ BUILD_BUG_ON(size > SHRT_MAX);
+ if (field >= size)
return -ENOENT;
- /*
- * FIXME: Mitigation for CVE-2017-5753. To be replaced with a
- * generic mechanism.
- */
- asm("lfence");
-
- if (vmcs_field_to_offset_table[field] == 0)
+ field = array_index_nospec(field, size);
+ offset = vmcs_field_to_offset_table[field];
+ if (offset == 0)
return -ENOENT;
-
- return vmcs_field_to_offset_table[field];
+ return offset;
}
static inline struct vmcs12 *get_vmcs12(struct kvm_vcpu *vcpu)
@@ -912,6 +914,9 @@
static void copy_vmcs12_to_shadow(struct vcpu_vmx *vmx);
static void copy_shadow_to_vmcs12(struct vcpu_vmx *vmx);
static int alloc_identity_pagetable(struct kvm *kvm);
+static void vmx_update_msr_bitmap(struct kvm_vcpu *vcpu);
+static void __always_inline vmx_disable_intercept_for_msr(unsigned long *msr_bitmap,
+ u32 msr, int type);
static DEFINE_PER_CPU(struct vmcs *, vmxarea);
static DEFINE_PER_CPU(struct vmcs *, current_vmcs);
@@ -931,12 +936,6 @@
static unsigned long *vmx_io_bitmap_a;
static unsigned long *vmx_io_bitmap_b;
-static unsigned long *vmx_msr_bitmap_legacy;
-static unsigned long *vmx_msr_bitmap_longmode;
-static unsigned long *vmx_msr_bitmap_legacy_x2apic;
-static unsigned long *vmx_msr_bitmap_longmode_x2apic;
-static unsigned long *vmx_msr_bitmap_legacy_x2apic_apicv_inactive;
-static unsigned long *vmx_msr_bitmap_longmode_x2apic_apicv_inactive;
static unsigned long *vmx_vmread_bitmap;
static unsigned long *vmx_vmwrite_bitmap;
@@ -1853,6 +1852,52 @@
vmcs_write32(EXCEPTION_BITMAP, eb);
}
+/*
+ * Check if MSR is intercepted for currently loaded MSR bitmap.
+ */
+static bool msr_write_intercepted(struct kvm_vcpu *vcpu, u32 msr)
+{
+ unsigned long *msr_bitmap;
+ int f = sizeof(unsigned long);
+
+ if (!cpu_has_vmx_msr_bitmap())
+ return true;
+
+ msr_bitmap = to_vmx(vcpu)->loaded_vmcs->msr_bitmap;
+
+ if (msr <= 0x1fff) {
+ return !!test_bit(msr, msr_bitmap + 0x800 / f);
+ } else if ((msr >= 0xc0000000) && (msr <= 0xc0001fff)) {
+ msr &= 0x1fff;
+ return !!test_bit(msr, msr_bitmap + 0xc00 / f);
+ }
+
+ return true;
+}
+
+/*
+ * Check if MSR is intercepted for L01 MSR bitmap.
+ */
+static bool msr_write_intercepted_l01(struct kvm_vcpu *vcpu, u32 msr)
+{
+ unsigned long *msr_bitmap;
+ int f = sizeof(unsigned long);
+
+ if (!cpu_has_vmx_msr_bitmap())
+ return true;
+
+ msr_bitmap = to_vmx(vcpu)->vmcs01.msr_bitmap;
+
+ if (msr <= 0x1fff) {
+ return !!test_bit(msr, msr_bitmap + 0x800 / f);
+ } else if ((msr >= 0xc0000000) && (msr <= 0xc0001fff)) {
+ msr &= 0x1fff;
+ return !!test_bit(msr, msr_bitmap + 0xc00 / f);
+ }
+
+ return true;
+}
+
static void clear_atomic_switch_msr_special(struct vcpu_vmx *vmx,
unsigned long entry, unsigned long exit)
{
@@ -2262,6 +2307,7 @@
if (per_cpu(current_vmcs, cpu) != vmx->loaded_vmcs->vmcs) {
per_cpu(current_vmcs, cpu) = vmx->loaded_vmcs->vmcs;
vmcs_load(vmx->loaded_vmcs->vmcs);
+ indirect_branch_prediction_barrier();
}
if (!already_loaded) {
@@ -2530,36 +2576,6 @@
vmx->guest_msrs[from] = tmp;
}
-static void vmx_set_msr_bitmap(struct kvm_vcpu *vcpu)
-{
- unsigned long *msr_bitmap;
-
- if (is_guest_mode(vcpu))
- msr_bitmap = to_vmx(vcpu)->nested.msr_bitmap;
- else if (cpu_has_secondary_exec_ctrls() &&
- (vmcs_read32(SECONDARY_VM_EXEC_CONTROL) &
- SECONDARY_EXEC_VIRTUALIZE_X2APIC_MODE)) {
- if (enable_apicv && kvm_vcpu_apicv_active(vcpu)) {
- if (is_long_mode(vcpu))
- msr_bitmap = vmx_msr_bitmap_longmode_x2apic;
- else
- msr_bitmap = vmx_msr_bitmap_legacy_x2apic;
- } else {
- if (is_long_mode(vcpu))
- msr_bitmap = vmx_msr_bitmap_longmode_x2apic_apicv_inactive;
- else
- msr_bitmap = vmx_msr_bitmap_legacy_x2apic_apicv_inactive;
- }
- } else {
- if (is_long_mode(vcpu))
- msr_bitmap = vmx_msr_bitmap_longmode;
- else
- msr_bitmap = vmx_msr_bitmap_legacy;
- }
-
- vmcs_write64(MSR_BITMAP, __pa(msr_bitmap));
-}
-
/*
* Set up the vmcs to automatically save and restore system
* msrs. Don't touch the 64-bit msrs if the guest is in legacy
@@ -2600,7 +2616,7 @@
vmx->save_nmsrs = save_nmsrs;
if (cpu_has_vmx_msr_bitmap())
- vmx_set_msr_bitmap(&vmx->vcpu);
+ vmx_update_msr_bitmap(&vmx->vcpu);
}
/*
@@ -2989,6 +3005,19 @@
case MSR_IA32_TSC:
msr_info->data = guest_read_tsc(vcpu);
break;
+ case MSR_IA32_SPEC_CTRL:
+ if (!msr_info->host_initiated &&
+ !guest_cpuid_has_ibrs(vcpu))
+ return 1;
+
+ msr_info->data = to_vmx(vcpu)->spec_ctrl;
+ break;
+ case MSR_IA32_ARCH_CAPABILITIES:
+ if (!msr_info->host_initiated &&
+ !guest_cpuid_has_arch_capabilities(vcpu))
+ return 1;
+ msr_info->data = to_vmx(vcpu)->arch_capabilities;
+ break;
case MSR_IA32_SYSENTER_CS:
msr_info->data = vmcs_read32(GUEST_SYSENTER_CS);
break;
@@ -3093,6 +3122,68 @@
case MSR_IA32_TSC:
kvm_write_tsc(vcpu, msr_info);
break;
+ case MSR_IA32_SPEC_CTRL:
+ if (!msr_info->host_initiated &&
+ !guest_cpuid_has_ibrs(vcpu))
+ return 1;
+
+ /* The STIBP bit doesn't fault even if it's not advertised */
+ if (data & ~(SPEC_CTRL_IBRS | SPEC_CTRL_STIBP))
+ return 1;
+
+ vmx->spec_ctrl = data;
+
+ if (!data)
+ break;
+
+ /*
+ * For non-nested:
+ * When it's written (to non-zero) for the first time, pass
+ * it through.
+ *
+ * For nested:
+ * The handling of the MSR bitmap for L2 guests is done in
+ * nested_vmx_merge_msr_bitmap. We should not touch the
+ * vmcs02.msr_bitmap here since it gets completely overwritten
+ * in the merging. We update the vmcs01 here for L1 as well
+ * since it will end up touching the MSR anyway now.
+ */
+ vmx_disable_intercept_for_msr(vmx->vmcs01.msr_bitmap,
+ MSR_IA32_SPEC_CTRL,
+ MSR_TYPE_RW);
+ break;
+ case MSR_IA32_PRED_CMD:
+ if (!msr_info->host_initiated &&
+ !guest_cpuid_has_ibpb(vcpu))
+ return 1;
+
+ if (data & ~PRED_CMD_IBPB)
+ return 1;
+
+ if (!data)
+ break;
+
+ wrmsrl(MSR_IA32_PRED_CMD, PRED_CMD_IBPB);
+
+ /*
+ * For non-nested:
+ * When it's written (to non-zero) for the first time, pass
+ * it through.
+ *
+ * For nested:
+ * The handling of the MSR bitmap for L2 guests is done in
+ * nested_vmx_merge_msr_bitmap. We should not touch the
+ * vmcs02.msr_bitmap here since it gets completely overwritten
+ * in the merging.
+ */
+ vmx_disable_intercept_for_msr(vmx->vmcs01.msr_bitmap, MSR_IA32_PRED_CMD,
+ MSR_TYPE_W);
+ break;
+ case MSR_IA32_ARCH_CAPABILITIES:
+ if (!msr_info->host_initiated)
+ return 1;
+ vmx->arch_capabilities = data;
+ break;
case MSR_IA32_CR_PAT:
if (vmcs_config.vmentry_ctrl & VM_ENTRY_LOAD_IA32_PAT) {
if (!kvm_mtrr_valid(vcpu, MSR_IA32_CR_PAT, data))
@@ -3532,11 +3623,6 @@
return vmcs;
}
-static struct vmcs *alloc_vmcs(void)
-{
- return alloc_vmcs_cpu(raw_smp_processor_id());
-}
-
static void free_vmcs(struct vmcs *vmcs)
{
free_pages((unsigned long)vmcs, vmcs_config.order);
@@ -3552,9 +3638,38 @@
loaded_vmcs_clear(loaded_vmcs);
free_vmcs(loaded_vmcs->vmcs);
loaded_vmcs->vmcs = NULL;
+ if (loaded_vmcs->msr_bitmap)
+ free_page((unsigned long)loaded_vmcs->msr_bitmap);
WARN_ON(loaded_vmcs->shadow_vmcs != NULL);
}
+static struct vmcs *alloc_vmcs(void)
+{
+ return alloc_vmcs_cpu(raw_smp_processor_id());
+}
+
+static int alloc_loaded_vmcs(struct loaded_vmcs *loaded_vmcs)
+{
+ loaded_vmcs->vmcs = alloc_vmcs();
+ if (!loaded_vmcs->vmcs)
+ return -ENOMEM;
+
+ loaded_vmcs->shadow_vmcs = NULL;
+ loaded_vmcs_init(loaded_vmcs);
+
+ if (cpu_has_vmx_msr_bitmap()) {
+ loaded_vmcs->msr_bitmap = (unsigned long *)__get_free_page(GFP_KERNEL);
+ if (!loaded_vmcs->msr_bitmap)
+ goto out_vmcs;
+ memset(loaded_vmcs->msr_bitmap, 0xff, PAGE_SIZE);
+ }
+ return 0;
+
+out_vmcs:
+ free_loaded_vmcs(loaded_vmcs);
+ return -ENOMEM;
+}
+
static void free_kvm_area(void)
{
int cpu;
@@ -4561,10 +4676,8 @@
spin_unlock(&vmx_vpid_lock);
}
-#define MSR_TYPE_R 1
-#define MSR_TYPE_W 2
-static void __vmx_disable_intercept_for_msr(unsigned long *msr_bitmap,
- u32 msr, int type)
+static void __always_inline vmx_disable_intercept_for_msr(unsigned long *msr_bitmap,
+ u32 msr, int type)
{
int f = sizeof(unsigned long);
@@ -4598,8 +4711,8 @@
}
}
-static void __vmx_enable_intercept_for_msr(unsigned long *msr_bitmap,
- u32 msr, int type)
+static void __always_inline vmx_enable_intercept_for_msr(unsigned long *msr_bitmap,
+ u32 msr, int type)
{
int f = sizeof(unsigned long);
@@ -4633,6 +4746,15 @@
}
}
+static void __always_inline vmx_set_intercept_for_msr(unsigned long *msr_bitmap,
+ u32 msr, int type, bool value)
+{
+ if (value)
+ vmx_enable_intercept_for_msr(msr_bitmap, msr, type);
+ else
+ vmx_disable_intercept_for_msr(msr_bitmap, msr, type);
+}
+
/*
* If a msr is allowed by L0, we should check whether it is allowed by L1.
* The corresponding bit will be cleared unless both of L0 and L1 allow it.
@@ -4679,58 +4801,68 @@
}
}
-static void vmx_disable_intercept_for_msr(u32 msr, bool longmode_only)
+static u8 vmx_msr_bitmap_mode(struct kvm_vcpu *vcpu)
{
- if (!longmode_only)
- __vmx_disable_intercept_for_msr(vmx_msr_bitmap_legacy,
- msr, MSR_TYPE_R | MSR_TYPE_W);
- __vmx_disable_intercept_for_msr(vmx_msr_bitmap_longmode,
- msr, MSR_TYPE_R | MSR_TYPE_W);
+ u8 mode = 0;
+
+ if (cpu_has_secondary_exec_ctrls() &&
+ (vmcs_read32(SECONDARY_VM_EXEC_CONTROL) &
+ SECONDARY_EXEC_VIRTUALIZE_X2APIC_MODE)) {
+ mode |= MSR_BITMAP_MODE_X2APIC;
+ if (enable_apicv && kvm_vcpu_apicv_active(vcpu))
+ mode |= MSR_BITMAP_MODE_X2APIC_APICV;
+ }
+
+ if (is_long_mode(vcpu))
+ mode |= MSR_BITMAP_MODE_LM;
+
+ return mode;
}
-static void vmx_enable_intercept_msr_read_x2apic(u32 msr, bool apicv_active)
+#define X2APIC_MSR(r) (APIC_BASE_MSR + ((r) >> 4))
+
+static void vmx_update_msr_bitmap_x2apic(unsigned long *msr_bitmap,
+ u8 mode)
{
- if (apicv_active) {
- __vmx_enable_intercept_for_msr(vmx_msr_bitmap_legacy_x2apic,
- msr, MSR_TYPE_R);
- __vmx_enable_intercept_for_msr(vmx_msr_bitmap_longmode_x2apic,
- msr, MSR_TYPE_R);
- } else {
- __vmx_enable_intercept_for_msr(vmx_msr_bitmap_legacy_x2apic_apicv_inactive,
- msr, MSR_TYPE_R);
- __vmx_enable_intercept_for_msr(vmx_msr_bitmap_longmode_x2apic_apicv_inactive,
- msr, MSR_TYPE_R);
+ int msr;
+
+ for (msr = 0x800; msr <= 0x8ff; msr += BITS_PER_LONG) {
+ unsigned word = msr / BITS_PER_LONG;
+ msr_bitmap[word] = (mode & MSR_BITMAP_MODE_X2APIC_APICV) ? 0 : ~0;
+ msr_bitmap[word + (0x800 / sizeof(long))] = ~0;
+ }
+
+ if (mode & MSR_BITMAP_MODE_X2APIC) {
+ /*
+ * TPR reads and writes can be virtualized even if virtual interrupt
+ * delivery is not in use.
+ */
+ vmx_disable_intercept_for_msr(msr_bitmap, X2APIC_MSR(APIC_TASKPRI), MSR_TYPE_RW);
+ if (mode & MSR_BITMAP_MODE_X2APIC_APICV) {
+ vmx_enable_intercept_for_msr(msr_bitmap, X2APIC_MSR(APIC_TMCCT), MSR_TYPE_R);
+ vmx_disable_intercept_for_msr(msr_bitmap, X2APIC_MSR(APIC_EOI), MSR_TYPE_W);
+ vmx_disable_intercept_for_msr(msr_bitmap, X2APIC_MSR(APIC_SELF_IPI), MSR_TYPE_W);
+ }
}
}
-static void vmx_disable_intercept_msr_read_x2apic(u32 msr, bool apicv_active)
+static void vmx_update_msr_bitmap(struct kvm_vcpu *vcpu)
{
- if (apicv_active) {
- __vmx_disable_intercept_for_msr(vmx_msr_bitmap_legacy_x2apic,
- msr, MSR_TYPE_R);
- __vmx_disable_intercept_for_msr(vmx_msr_bitmap_longmode_x2apic,
- msr, MSR_TYPE_R);
- } else {
- __vmx_disable_intercept_for_msr(vmx_msr_bitmap_legacy_x2apic_apicv_inactive,
- msr, MSR_TYPE_R);
- __vmx_disable_intercept_for_msr(vmx_msr_bitmap_longmode_x2apic_apicv_inactive,
- msr, MSR_TYPE_R);
- }
-}
+ struct vcpu_vmx *vmx = to_vmx(vcpu);
+ unsigned long *msr_bitmap = vmx->vmcs01.msr_bitmap;
+ u8 mode = vmx_msr_bitmap_mode(vcpu);
+ u8 changed = mode ^ vmx->msr_bitmap_mode;
-static void vmx_disable_intercept_msr_write_x2apic(u32 msr, bool apicv_active)
-{
- if (apicv_active) {
- __vmx_disable_intercept_for_msr(vmx_msr_bitmap_legacy_x2apic,
- msr, MSR_TYPE_W);
- __vmx_disable_intercept_for_msr(vmx_msr_bitmap_longmode_x2apic,
- msr, MSR_TYPE_W);
- } else {
- __vmx_disable_intercept_for_msr(vmx_msr_bitmap_legacy_x2apic_apicv_inactive,
- msr, MSR_TYPE_W);
- __vmx_disable_intercept_for_msr(vmx_msr_bitmap_longmode_x2apic_apicv_inactive,
- msr, MSR_TYPE_W);
- }
+ if (!changed)
+ return;
+
+ vmx_set_intercept_for_msr(msr_bitmap, MSR_KERNEL_GS_BASE, MSR_TYPE_RW,
+ !(mode & MSR_BITMAP_MODE_LM));
+
+ if (changed & (MSR_BITMAP_MODE_X2APIC | MSR_BITMAP_MODE_X2APIC_APICV))
+ vmx_update_msr_bitmap_x2apic(msr_bitmap, mode);
+
+ vmx->msr_bitmap_mode = mode;
}
static bool vmx_get_enable_apicv(void)
@@ -4738,30 +4870,45 @@
return enable_apicv;
}
-static int vmx_complete_nested_posted_interrupt(struct kvm_vcpu *vcpu)
+static void nested_mark_vmcs12_pages_dirty(struct kvm_vcpu *vcpu)
+{
+ struct vmcs12 *vmcs12 = get_vmcs12(vcpu);
+ gfn_t gfn;
+
+ /*
+ * Don't need to mark the APIC access page dirty; it is never
+ * written to by the CPU during APIC virtualization.
+ */
+
+ if (nested_cpu_has(vmcs12, CPU_BASED_TPR_SHADOW)) {
+ gfn = vmcs12->virtual_apic_page_addr >> PAGE_SHIFT;
+ kvm_vcpu_mark_page_dirty(vcpu, gfn);
+ }
+
+ if (nested_cpu_has_posted_intr(vmcs12)) {
+ gfn = vmcs12->posted_intr_desc_addr >> PAGE_SHIFT;
+ kvm_vcpu_mark_page_dirty(vcpu, gfn);
+ }
+}
+
+
+static void vmx_complete_nested_posted_interrupt(struct kvm_vcpu *vcpu)
{
struct vcpu_vmx *vmx = to_vmx(vcpu);
int max_irr;
void *vapic_page;
u16 status;
- if (vmx->nested.pi_desc &&
- vmx->nested.pi_pending) {
- vmx->nested.pi_pending = false;
- if (!pi_test_and_clear_on(vmx->nested.pi_desc))
- return 0;
+ if (!vmx->nested.pi_desc || !vmx->nested.pi_pending)
+ return;
- max_irr = find_last_bit(
- (unsigned long *)vmx->nested.pi_desc->pir, 256);
+ vmx->nested.pi_pending = false;
+ if (!pi_test_and_clear_on(vmx->nested.pi_desc))
+ return;
- if (max_irr == 256)
- return 0;
-
+ max_irr = find_last_bit((unsigned long *)vmx->nested.pi_desc->pir, 256);
+ if (max_irr != 256) {
vapic_page = kmap(vmx->nested.virtual_apic_page);
- if (!vapic_page) {
- WARN_ON(1);
- return -ENOMEM;
- }
__kvm_apic_update_irr(vmx->nested.pi_desc->pir, vapic_page);
kunmap(vmx->nested.virtual_apic_page);
@@ -4772,7 +4919,8 @@
vmcs_write16(GUEST_INTR_STATUS, status);
}
}
- return 0;
+
+ nested_mark_vmcs12_pages_dirty(vcpu);
}
static inline bool kvm_vcpu_trigger_posted_interrupt(struct kvm_vcpu *vcpu)
@@ -4959,7 +5107,7 @@
}
if (cpu_has_vmx_msr_bitmap())
- vmx_set_msr_bitmap(vcpu);
+ vmx_update_msr_bitmap(vcpu);
}
static u32 vmx_exec_control(struct vcpu_vmx *vmx)
@@ -5048,7 +5196,7 @@
vmcs_write64(VMWRITE_BITMAP, __pa(vmx_vmwrite_bitmap));
}
if (cpu_has_vmx_msr_bitmap())
- vmcs_write64(MSR_BITMAP, __pa(vmx_msr_bitmap_legacy));
+ vmcs_write64(MSR_BITMAP, __pa(vmx->vmcs01.msr_bitmap));
vmcs_write64(VMCS_LINK_POINTER, -1ull); /* 22.3.1.5 */
@@ -5122,6 +5270,8 @@
++vmx->nmsrs;
}
+ if (boot_cpu_has(X86_FEATURE_ARCH_CAPABILITIES))
+ rdmsrl(MSR_IA32_ARCH_CAPABILITIES, vmx->arch_capabilities);
vm_exit_controls_init(vmx, vmcs_config.vmexit_ctrl);
@@ -5150,6 +5300,7 @@
u64 cr0;
vmx->rmode.vm86_active = 0;
+ vmx->spec_ctrl = 0;
vmx->soft_vnmi_blocked = 0;
@@ -6379,7 +6530,7 @@
static __init int hardware_setup(void)
{
- int r = -ENOMEM, i, msr;
+ int r = -ENOMEM, i;
rdmsrl_safe(MSR_EFER, &host_efer);
@@ -6394,41 +6545,13 @@
if (!vmx_io_bitmap_b)
goto out;
- vmx_msr_bitmap_legacy = (unsigned long *)__get_free_page(GFP_KERNEL);
- if (!vmx_msr_bitmap_legacy)
- goto out1;
-
- vmx_msr_bitmap_legacy_x2apic =
- (unsigned long *)__get_free_page(GFP_KERNEL);
- if (!vmx_msr_bitmap_legacy_x2apic)
- goto out2;
-
- vmx_msr_bitmap_legacy_x2apic_apicv_inactive =
- (unsigned long *)__get_free_page(GFP_KERNEL);
- if (!vmx_msr_bitmap_legacy_x2apic_apicv_inactive)
- goto out3;
-
- vmx_msr_bitmap_longmode = (unsigned long *)__get_free_page(GFP_KERNEL);
- if (!vmx_msr_bitmap_longmode)
- goto out4;
-
- vmx_msr_bitmap_longmode_x2apic =
- (unsigned long *)__get_free_page(GFP_KERNEL);
- if (!vmx_msr_bitmap_longmode_x2apic)
- goto out5;
-
- vmx_msr_bitmap_longmode_x2apic_apicv_inactive =
- (unsigned long *)__get_free_page(GFP_KERNEL);
- if (!vmx_msr_bitmap_longmode_x2apic_apicv_inactive)
- goto out6;
-
vmx_vmread_bitmap = (unsigned long *)__get_free_page(GFP_KERNEL);
if (!vmx_vmread_bitmap)
- goto out7;
+ goto out1;
vmx_vmwrite_bitmap = (unsigned long *)__get_free_page(GFP_KERNEL);
if (!vmx_vmwrite_bitmap)
- goto out8;
+ goto out2;
memset(vmx_vmread_bitmap, 0xff, PAGE_SIZE);
memset(vmx_vmwrite_bitmap, 0xff, PAGE_SIZE);
@@ -6437,12 +6560,9 @@
memset(vmx_io_bitmap_b, 0xff, PAGE_SIZE);
- memset(vmx_msr_bitmap_legacy, 0xff, PAGE_SIZE);
- memset(vmx_msr_bitmap_longmode, 0xff, PAGE_SIZE);
-
if (setup_vmcs_config(&vmcs_config) < 0) {
r = -EIO;
- goto out9;
+ goto out3;
}
if (boot_cpu_has(X86_FEATURE_NX))
@@ -6499,47 +6619,8 @@
kvm_tsc_scaling_ratio_frac_bits = 48;
}
- vmx_disable_intercept_for_msr(MSR_FS_BASE, false);
- vmx_disable_intercept_for_msr(MSR_GS_BASE, false);
- vmx_disable_intercept_for_msr(MSR_KERNEL_GS_BASE, true);
- vmx_disable_intercept_for_msr(MSR_IA32_SYSENTER_CS, false);
- vmx_disable_intercept_for_msr(MSR_IA32_SYSENTER_ESP, false);
- vmx_disable_intercept_for_msr(MSR_IA32_SYSENTER_EIP, false);
-
- memcpy(vmx_msr_bitmap_legacy_x2apic,
- vmx_msr_bitmap_legacy, PAGE_SIZE);
- memcpy(vmx_msr_bitmap_longmode_x2apic,
- vmx_msr_bitmap_longmode, PAGE_SIZE);
- memcpy(vmx_msr_bitmap_legacy_x2apic_apicv_inactive,
- vmx_msr_bitmap_legacy, PAGE_SIZE);
- memcpy(vmx_msr_bitmap_longmode_x2apic_apicv_inactive,
- vmx_msr_bitmap_longmode, PAGE_SIZE);
-
set_bit(0, vmx_vpid_bitmap); /* 0 is reserved for host */
- /*
- * enable_apicv && kvm_vcpu_apicv_active()
- */
- for (msr = 0x800; msr <= 0x8ff; msr++)
- vmx_disable_intercept_msr_read_x2apic(msr, true);
-
- /* TMCCT */
- vmx_enable_intercept_msr_read_x2apic(0x839, true);
- /* TPR */
- vmx_disable_intercept_msr_write_x2apic(0x808, true);
- /* EOI */
- vmx_disable_intercept_msr_write_x2apic(0x80b, true);
- /* SELF-IPI */
- vmx_disable_intercept_msr_write_x2apic(0x83f, true);
-
- /*
- * (enable_apicv && !kvm_vcpu_apicv_active()) ||
- * !enable_apicv
- */
- /* TPR */
- vmx_disable_intercept_msr_read_x2apic(0x808, false);
- vmx_disable_intercept_msr_write_x2apic(0x808, false);
-
if (enable_ept) {
kvm_mmu_set_mask_ptes(VMX_EPT_READABLE_MASK,
(enable_ept_ad_bits) ? VMX_EPT_ACCESS_BIT : 0ull,
@@ -6585,22 +6666,10 @@
return alloc_kvm_area();
-out9:
- free_page((unsigned long)vmx_vmwrite_bitmap);
-out8:
- free_page((unsigned long)vmx_vmread_bitmap);
-out7:
- free_page((unsigned long)vmx_msr_bitmap_longmode_x2apic_apicv_inactive);
-out6:
- free_page((unsigned long)vmx_msr_bitmap_longmode_x2apic);
-out5:
- free_page((unsigned long)vmx_msr_bitmap_longmode);
-out4:
- free_page((unsigned long)vmx_msr_bitmap_legacy_x2apic_apicv_inactive);
out3:
- free_page((unsigned long)vmx_msr_bitmap_legacy_x2apic);
+ free_page((unsigned long)vmx_vmwrite_bitmap);
out2:
- free_page((unsigned long)vmx_msr_bitmap_legacy);
+ free_page((unsigned long)vmx_vmread_bitmap);
out1:
free_page((unsigned long)vmx_io_bitmap_b);
out:
@@ -6611,12 +6680,6 @@
static __exit void hardware_unsetup(void)
{
- free_page((unsigned long)vmx_msr_bitmap_legacy_x2apic);
- free_page((unsigned long)vmx_msr_bitmap_legacy_x2apic_apicv_inactive);
- free_page((unsigned long)vmx_msr_bitmap_longmode_x2apic);
- free_page((unsigned long)vmx_msr_bitmap_longmode_x2apic_apicv_inactive);
- free_page((unsigned long)vmx_msr_bitmap_legacy);
- free_page((unsigned long)vmx_msr_bitmap_longmode);
free_page((unsigned long)vmx_io_bitmap_b);
free_page((unsigned long)vmx_io_bitmap_a);
free_page((unsigned long)vmx_vmwrite_bitmap);
@@ -6664,94 +6727,6 @@
}
/*
- * To run an L2 guest, we need a vmcs02 based on the L1-specified vmcs12.
- * We could reuse a single VMCS for all the L2 guests, but we also want the
- * option to allocate a separate vmcs02 for each separate loaded vmcs12 - this
- * allows keeping them loaded on the processor, and in the future will allow
- * optimizations where prepare_vmcs02 doesn't need to set all the fields on
- * every entry if they never change.
- * So we keep, in vmx->nested.vmcs02_pool, a cache of size VMCS02_POOL_SIZE
- * (>=0) with a vmcs02 for each recently loaded vmcs12s, most recent first.
- *
- * The following functions allocate and free a vmcs02 in this pool.
- */
-
-/* Get a VMCS from the pool to use as vmcs02 for the current vmcs12. */
-static struct loaded_vmcs *nested_get_current_vmcs02(struct vcpu_vmx *vmx)
-{
- struct vmcs02_list *item;
- list_for_each_entry(item, &vmx->nested.vmcs02_pool, list)
- if (item->vmptr == vmx->nested.current_vmptr) {
- list_move(&item->list, &vmx->nested.vmcs02_pool);
- return &item->vmcs02;
- }
-
- if (vmx->nested.vmcs02_num >= max(VMCS02_POOL_SIZE, 1)) {
- /* Recycle the least recently used VMCS. */
- item = list_last_entry(&vmx->nested.vmcs02_pool,
- struct vmcs02_list, list);
- item->vmptr = vmx->nested.current_vmptr;
- list_move(&item->list, &vmx->nested.vmcs02_pool);
- return &item->vmcs02;
- }
-
- /* Create a new VMCS */
- item = kmalloc(sizeof(struct vmcs02_list), GFP_KERNEL);
- if (!item)
- return NULL;
- item->vmcs02.vmcs = alloc_vmcs();
- item->vmcs02.shadow_vmcs = NULL;
- if (!item->vmcs02.vmcs) {
- kfree(item);
- return NULL;
- }
- loaded_vmcs_init(&item->vmcs02);
- item->vmptr = vmx->nested.current_vmptr;
- list_add(&(item->list), &(vmx->nested.vmcs02_pool));
- vmx->nested.vmcs02_num++;
- return &item->vmcs02;
-}
-
-/* Free and remove from pool a vmcs02 saved for a vmcs12 (if there is one) */
-static void nested_free_vmcs02(struct vcpu_vmx *vmx, gpa_t vmptr)
-{
- struct vmcs02_list *item;
- list_for_each_entry(item, &vmx->nested.vmcs02_pool, list)
- if (item->vmptr == vmptr) {
- free_loaded_vmcs(&item->vmcs02);
- list_del(&item->list);
- kfree(item);
- vmx->nested.vmcs02_num--;
- return;
- }
-}
-
-/*
- * Free all VMCSs saved for this vcpu, except the one pointed by
- * vmx->loaded_vmcs. We must be running L1, so vmx->loaded_vmcs
- * must be &vmx->vmcs01.
- */
-static void nested_free_all_saved_vmcss(struct vcpu_vmx *vmx)
-{
- struct vmcs02_list *item, *n;
-
- WARN_ON(vmx->loaded_vmcs != &vmx->vmcs01);
- list_for_each_entry_safe(item, n, &vmx->nested.vmcs02_pool, list) {
- /*
- * Something will leak if the above WARN triggers. Better than
- * a use-after-free.
- */
- if (vmx->loaded_vmcs == &item->vmcs02)
- continue;
-
- free_loaded_vmcs(&item->vmcs02);
- list_del(&item->list);
- kfree(item);
- vmx->nested.vmcs02_num--;
- }
-}
-
-/*
* The following 3 functions, nested_vmx_succeed()/failValid()/failInvalid(),
* set the success or error code of an emulated VMX instruction, as specified
* by Vol 2B, VMX Instruction Reference, "Conventions".
@@ -7025,6 +7000,7 @@
struct vmcs *shadow_vmcs;
const u64 VMXON_NEEDED_FEATURES = FEATURE_CONTROL_LOCKED
| FEATURE_CONTROL_VMXON_ENABLED_OUTSIDE_SMX;
+ int r;
/* The Intel VMX Instruction Reference lists a bunch of bits that
* are prerequisite to running VMXON, most notably cr4.VMXE must be
@@ -7064,12 +7040,9 @@
return 1;
}
- if (cpu_has_vmx_msr_bitmap()) {
- vmx->nested.msr_bitmap =
- (unsigned long *)__get_free_page(GFP_KERNEL);
- if (!vmx->nested.msr_bitmap)
- goto out_msr_bitmap;
- }
+ r = alloc_loaded_vmcs(&vmx->nested.vmcs02);
+ if (r < 0)
+ goto out_vmcs02;
vmx->nested.cached_vmcs12 = kmalloc(VMCS12_SIZE, GFP_KERNEL);
if (!vmx->nested.cached_vmcs12)
@@ -7086,9 +7059,6 @@
vmx->vmcs01.shadow_vmcs = shadow_vmcs;
}
- INIT_LIST_HEAD(&(vmx->nested.vmcs02_pool));
- vmx->nested.vmcs02_num = 0;
-
hrtimer_init(&vmx->nested.preemption_timer, CLOCK_MONOTONIC,
HRTIMER_MODE_REL_PINNED);
vmx->nested.preemption_timer.function = vmx_preemption_timer_fn;
@@ -7103,9 +7073,9 @@
kfree(vmx->nested.cached_vmcs12);
out_cached_vmcs12:
- free_page((unsigned long)vmx->nested.msr_bitmap);
+ free_loaded_vmcs(&vmx->nested.vmcs02);
-out_msr_bitmap:
+out_vmcs02:
return -ENOMEM;
}
@@ -7181,17 +7151,13 @@
vmx->nested.vmxon = false;
free_vpid(vmx->nested.vpid02);
nested_release_vmcs12(vmx);
- if (vmx->nested.msr_bitmap) {
- free_page((unsigned long)vmx->nested.msr_bitmap);
- vmx->nested.msr_bitmap = NULL;
- }
if (enable_shadow_vmcs) {
vmcs_clear(vmx->vmcs01.shadow_vmcs);
free_vmcs(vmx->vmcs01.shadow_vmcs);
vmx->vmcs01.shadow_vmcs = NULL;
}
kfree(vmx->nested.cached_vmcs12);
- /* Unpin physical memory we referred to in current vmcs02 */
+ /* Unpin physical memory we referred to in the vmcs02 */
if (vmx->nested.apic_access_page) {
nested_release_page(vmx->nested.apic_access_page);
vmx->nested.apic_access_page = NULL;
@@ -7207,7 +7173,7 @@
vmx->nested.pi_desc = NULL;
}
- nested_free_all_saved_vmcss(vmx);
+ free_loaded_vmcs(&vmx->nested.vmcs02);
}
/* Emulate the VMXOFF instruction */
@@ -7241,8 +7207,6 @@
vmptr + offsetof(struct vmcs12, launch_state),
&zero, sizeof(zero));
- nested_free_vmcs02(vmx, vmptr);
-
skip_emulated_instruction(vcpu);
nested_vmx_succeed(vcpu);
return 1;
@@ -8029,6 +7993,19 @@
vmcs_read32(VM_EXIT_INTR_ERROR_CODE),
KVM_ISA_VMX);
+ /*
+ * The host physical addresses of some pages of guest memory
+ * are loaded into the vmcs02 (e.g. vmcs12's Virtual APIC
+ * Page). The CPU may write to these pages via their host
+ * physical address while L2 is running, bypassing any
+ * address-translation-based dirty tracking (e.g. EPT write
+ * protection).
+ *
+ * Mark them dirty on every exit from L2 to prevent them from
+ * getting out of sync with dirty tracking.
+ */
+ nested_mark_vmcs12_pages_dirty(vcpu);
+
if (vmx->nested.nested_run_pending)
return false;
@@ -8520,7 +8497,7 @@
}
vmcs_write32(SECONDARY_VM_EXEC_CONTROL, sec_exec_control);
- vmx_set_msr_bitmap(vcpu);
+ vmx_update_msr_bitmap(vcpu);
}
static void vmx_set_apic_access_page_addr(struct kvm_vcpu *vcpu, hpa_t hpa)
@@ -8676,14 +8653,14 @@
#endif
"pushf\n\t"
__ASM_SIZE(push) " $%c[cs]\n\t"
- "call *%[entry]\n\t"
+ CALL_NOSPEC
:
#ifdef CONFIG_X86_64
[sp]"=&r"(tmp),
#endif
"+r"(__sp)
:
- [entry]"r"(entry),
+ THUNK_TARGET(entry),
[ss]"i"(__KERNEL_DS),
[cs]"i"(__KERNEL_CS)
);
@@ -8909,6 +8886,15 @@
vmx_arm_hv_timer(vcpu);
+ /*
+ * If this vCPU has touched SPEC_CTRL, restore the guest's value if
+ * it's non-zero. Since vmentry is serialising on affected CPUs, there
+ * is no need to worry about the conditional branch over the wrmsr
+ * being speculatively taken.
+ */
+ if (vmx->spec_ctrl)
+ wrmsrl(MSR_IA32_SPEC_CTRL, vmx->spec_ctrl);
+
vmx->__launched = vmx->loaded_vmcs->launched;
asm(
/* Store host registers */
@@ -9027,6 +9013,27 @@
#endif
);
+ /*
+ * We do not use IBRS in the kernel. If this vCPU has used the
+ * SPEC_CTRL MSR it may have left it on; save the value and
+ * turn it off. This is much more efficient than blindly adding
+ * it to the atomic save/restore list. Especially as the former
+ * (Saving guest MSRs on vmexit) doesn't even exist in KVM.
+ *
+ * For non-nested case:
+ * If the L01 MSR bitmap does not intercept the MSR, then we need to
+ * save it.
+ *
+ * For nested case:
+ * If the L02 MSR bitmap does not intercept the MSR, then we need to
+ * save it.
+ */
+ if (!msr_write_intercepted(vcpu, MSR_IA32_SPEC_CTRL))
+ rdmsrl(MSR_IA32_SPEC_CTRL, vmx->spec_ctrl);
+
+ if (vmx->spec_ctrl)
+ wrmsrl(MSR_IA32_SPEC_CTRL, 0);
+
/* Eliminate branch target predictions from guest mode */
vmexit_fill_RSB();
@@ -9140,6 +9147,7 @@
{
int err;
struct vcpu_vmx *vmx = kmem_cache_zalloc(kvm_vcpu_cache, GFP_KERNEL);
+ unsigned long *msr_bitmap;
int cpu;
if (!vmx)
@@ -9172,17 +9180,24 @@
if (!vmx->guest_msrs)
goto free_pml;
- vmx->loaded_vmcs = &vmx->vmcs01;
- vmx->loaded_vmcs->vmcs = alloc_vmcs();
- vmx->loaded_vmcs->shadow_vmcs = NULL;
- if (!vmx->loaded_vmcs->vmcs)
- goto free_msrs;
if (!vmm_exclusive)
kvm_cpu_vmxon(__pa(per_cpu(vmxarea, raw_smp_processor_id())));
- loaded_vmcs_init(vmx->loaded_vmcs);
+ err = alloc_loaded_vmcs(&vmx->vmcs01);
if (!vmm_exclusive)
kvm_cpu_vmxoff();
+ if (err < 0)
+ goto free_msrs;
+ msr_bitmap = vmx->vmcs01.msr_bitmap;
+ vmx_disable_intercept_for_msr(msr_bitmap, MSR_FS_BASE, MSR_TYPE_RW);
+ vmx_disable_intercept_for_msr(msr_bitmap, MSR_GS_BASE, MSR_TYPE_RW);
+ vmx_disable_intercept_for_msr(msr_bitmap, MSR_KERNEL_GS_BASE, MSR_TYPE_RW);
+ vmx_disable_intercept_for_msr(msr_bitmap, MSR_IA32_SYSENTER_CS, MSR_TYPE_RW);
+ vmx_disable_intercept_for_msr(msr_bitmap, MSR_IA32_SYSENTER_ESP, MSR_TYPE_RW);
+ vmx_disable_intercept_for_msr(msr_bitmap, MSR_IA32_SYSENTER_EIP, MSR_TYPE_RW);
+ vmx->msr_bitmap_mode = 0;
+
+ vmx->loaded_vmcs = &vmx->vmcs01;
cpu = get_cpu();
vmx_vcpu_load(&vmx->vcpu, cpu);
vmx->vcpu.cpu = cpu;
@@ -9576,21 +9591,31 @@
int msr;
struct page *page;
unsigned long *msr_bitmap_l1;
- unsigned long *msr_bitmap_l0 = to_vmx(vcpu)->nested.msr_bitmap;
+ unsigned long *msr_bitmap_l0 = to_vmx(vcpu)->nested.vmcs02.msr_bitmap;
+ /*
+ * pred_cmd & spec_ctrl are trying to verify two things:
+ *
+ * 1. L0 gave a permission to L1 to actually passthrough the MSR. This
+ * ensures that we do not accidentally generate an L02 MSR bitmap
+ * from the L12 MSR bitmap that is too permissive.
+ * 2. That L1 or L2s have actually used the MSR. This avoids
+ * unnecessarily merging of the bitmap if the MSR is unused. This
+ * works properly because we only update the L01 MSR bitmap lazily.
+ * So even if L0 should pass L1 these MSRs, the L01 bitmap is only
+ * updated to reflect this when L1 (or its L2s) actually write to
+ * the MSR.
+ */
+ bool pred_cmd = msr_write_intercepted_l01(vcpu, MSR_IA32_PRED_CMD);
+ bool spec_ctrl = msr_write_intercepted_l01(vcpu, MSR_IA32_SPEC_CTRL);
- /* This shortcut is ok because we support only x2APIC MSRs so far. */
- if (!nested_cpu_has_virt_x2apic_mode(vmcs12))
+ if (!nested_cpu_has_virt_x2apic_mode(vmcs12) &&
+ !pred_cmd && !spec_ctrl)
return false;
page = nested_get_page(vcpu, vmcs12->msr_bitmap);
if (!page)
return false;
msr_bitmap_l1 = (unsigned long *)kmap(page);
- if (!msr_bitmap_l1) {
- nested_release_page_clean(page);
- WARN_ON(1);
- return false;
- }
memset(msr_bitmap_l0, 0xff, PAGE_SIZE);
@@ -9617,6 +9642,19 @@
MSR_TYPE_W);
}
}
+
+ if (spec_ctrl)
+ nested_vmx_disable_intercept_for_msr(
+ msr_bitmap_l1, msr_bitmap_l0,
+ MSR_IA32_SPEC_CTRL,
+ MSR_TYPE_R | MSR_TYPE_W);
+
+ if (pred_cmd)
+ nested_vmx_disable_intercept_for_msr(
+ msr_bitmap_l1, msr_bitmap_l0,
+ MSR_IA32_PRED_CMD,
+ MSR_TYPE_W);
+
kunmap(page);
nested_release_page_clean(page);
@@ -10096,6 +10134,9 @@
if (kvm_has_tsc_control)
decache_tsc_multiplier(vmx);
+ if (cpu_has_vmx_msr_bitmap())
+ vmcs_write64(MSR_BITMAP, __pa(vmx->nested.vmcs02.msr_bitmap));
+
if (enable_vpid) {
/*
* There is no direct mapping between vpid02 and vpid12, the
@@ -10191,7 +10232,6 @@
struct vmcs12 *vmcs12;
struct vcpu_vmx *vmx = to_vmx(vcpu);
int cpu;
- struct loaded_vmcs *vmcs02;
bool ia32e;
u32 msr_entry_idx;
@@ -10331,17 +10371,13 @@
* the nested entry.
*/
- vmcs02 = nested_get_current_vmcs02(vmx);
- if (!vmcs02)
- return -ENOMEM;
-
enter_guest_mode(vcpu);
if (!(vmcs12->vm_entry_controls & VM_ENTRY_LOAD_DEBUG_CONTROLS))
vmx->nested.vmcs01_debugctl = vmcs_read64(GUEST_IA32_DEBUGCTL);
cpu = get_cpu();
- vmx->loaded_vmcs = vmcs02;
+ vmx->loaded_vmcs = &vmx->nested.vmcs02;
vmx_vcpu_put(vcpu);
vmx_vcpu_load(vcpu, cpu);
vcpu->cpu = cpu;
@@ -10493,7 +10529,8 @@
return 0;
}
- return vmx_complete_nested_posted_interrupt(vcpu);
+ vmx_complete_nested_posted_interrupt(vcpu);
+ return 0;
}
static u32 vmx_get_preemption_timer_value(struct kvm_vcpu *vcpu)
@@ -10804,7 +10841,7 @@
vmcs_write64(GUEST_IA32_DEBUGCTL, 0);
if (cpu_has_vmx_msr_bitmap())
- vmx_set_msr_bitmap(vcpu);
+ vmx_update_msr_bitmap(vcpu);
if (nested_vmx_load_msr(vcpu, vmcs12->vm_exit_msr_load_addr,
vmcs12->vm_exit_msr_load_count))
@@ -10855,10 +10892,6 @@
vm_exit_controls_reset_shadow(vmx);
vmx_segment_cache_clear(vmx);
- /* if no vmcs02 cache requested, remove the one we used */
- if (VMCS02_POOL_SIZE == 0)
- nested_free_vmcs02(vmx, vmx->nested.current_vmptr);
-
load_vmcs12_host_state(vcpu, vmcs12);
/* Update any VMCS fields that might have changed while L2 ran */
diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c
index e023ef9..75f756e 100644
--- a/arch/x86/kvm/x86.c
+++ b/arch/x86/kvm/x86.c
@@ -975,6 +975,7 @@
#endif
MSR_IA32_TSC, MSR_IA32_CR_PAT, MSR_VM_HSAVE_PA,
MSR_IA32_FEATURE_CONTROL, MSR_IA32_BNDCFGS, MSR_TSC_AUX,
+ MSR_IA32_SPEC_CTRL, MSR_IA32_ARCH_CAPABILITIES
};
static unsigned num_msrs_to_save;
diff --git a/arch/x86/lib/Makefile b/arch/x86/lib/Makefile
index 6bf1898..4ad7c4d 100644
--- a/arch/x86/lib/Makefile
+++ b/arch/x86/lib/Makefile
@@ -26,6 +26,7 @@
lib-$(CONFIG_INSTRUCTION_DECODER) += insn.o inat.o
lib-$(CONFIG_RANDOMIZE_BASE) += kaslr.o
lib-$(CONFIG_RETPOLINE) += retpoline.o
+OBJECT_FILES_NON_STANDARD_retpoline.o :=y
obj-y += msr.o msr-reg.o msr-reg-export.o hweight.o
diff --git a/arch/x86/lib/getuser.S b/arch/x86/lib/getuser.S
index 37b62d4..b12b214 100644
--- a/arch/x86/lib/getuser.S
+++ b/arch/x86/lib/getuser.S
@@ -39,6 +39,8 @@
mov PER_CPU_VAR(current_task), %_ASM_DX
cmp TASK_addr_limit(%_ASM_DX),%_ASM_AX
jae bad_get_user
+ sbb %_ASM_DX, %_ASM_DX /* array_index_mask_nospec() */
+ and %_ASM_DX, %_ASM_AX
ASM_STAC
1: movzbl (%_ASM_AX),%edx
xor %eax,%eax
@@ -53,6 +55,8 @@
mov PER_CPU_VAR(current_task), %_ASM_DX
cmp TASK_addr_limit(%_ASM_DX),%_ASM_AX
jae bad_get_user
+ sbb %_ASM_DX, %_ASM_DX /* array_index_mask_nospec() */
+ and %_ASM_DX, %_ASM_AX
ASM_STAC
2: movzwl -1(%_ASM_AX),%edx
xor %eax,%eax
@@ -67,6 +71,8 @@
mov PER_CPU_VAR(current_task), %_ASM_DX
cmp TASK_addr_limit(%_ASM_DX),%_ASM_AX
jae bad_get_user
+ sbb %_ASM_DX, %_ASM_DX /* array_index_mask_nospec() */
+ and %_ASM_DX, %_ASM_AX
ASM_STAC
3: movl -3(%_ASM_AX),%edx
xor %eax,%eax
@@ -82,6 +88,8 @@
mov PER_CPU_VAR(current_task), %_ASM_DX
cmp TASK_addr_limit(%_ASM_DX),%_ASM_AX
jae bad_get_user
+ sbb %_ASM_DX, %_ASM_DX /* array_index_mask_nospec() */
+ and %_ASM_DX, %_ASM_AX
ASM_STAC
4: movq -7(%_ASM_AX),%rdx
xor %eax,%eax
@@ -93,6 +101,8 @@
mov PER_CPU_VAR(current_task), %_ASM_DX
cmp TASK_addr_limit(%_ASM_DX),%_ASM_AX
jae bad_get_user_8
+ sbb %_ASM_DX, %_ASM_DX /* array_index_mask_nospec() */
+ and %_ASM_DX, %_ASM_AX
ASM_STAC
4: movl -7(%_ASM_AX),%edx
5: movl -3(%_ASM_AX),%ecx
diff --git a/arch/x86/lib/retpoline.S b/arch/x86/lib/retpoline.S
index dfb2ba9..480edc3 100644
--- a/arch/x86/lib/retpoline.S
+++ b/arch/x86/lib/retpoline.S
@@ -7,6 +7,7 @@
#include <asm/alternative-asm.h>
#include <asm/export.h>
#include <asm/nospec-branch.h>
+#include <asm/bitsperlong.h>
.macro THUNK reg
.section .text.__x86.indirect_thunk
@@ -36,7 +37,6 @@
GENERATE_THUNK(_ASM_SI)
GENERATE_THUNK(_ASM_DI)
GENERATE_THUNK(_ASM_BP)
-GENERATE_THUNK(_ASM_SP)
#ifdef CONFIG_64BIT
GENERATE_THUNK(r8)
GENERATE_THUNK(r9)
@@ -47,3 +47,58 @@
GENERATE_THUNK(r14)
GENERATE_THUNK(r15)
#endif
+
+/*
+ * Fill the CPU return stack buffer.
+ *
+ * Each entry in the RSB, if used for a speculative 'ret', contains an
+ * infinite 'pause; lfence; jmp' loop to capture speculative execution.
+ *
+ * This is required in various cases for retpoline and IBRS-based
+ * mitigations for the Spectre variant 2 vulnerability. Sometimes to
+ * eliminate potentially bogus entries from the RSB, and sometimes
+ * purely to ensure that it doesn't get empty, which on some CPUs would
+ * allow predictions from other (unwanted!) sources to be used.
+ *
+ * Google experimented with loop-unrolling and this turned out to be
+ * the optimal version - two calls, each with their own speculation
+ * trap should their return address end up getting used, in a loop.
+ */
+.macro STUFF_RSB nr:req sp:req
+ mov $(\nr / 2), %_ASM_BX
+ .align 16
+771:
+ call 772f
+773: /* speculation trap */
+ pause
+ lfence
+ jmp 773b
+ .align 16
+772:
+ call 774f
+775: /* speculation trap */
+ pause
+ lfence
+ jmp 775b
+ .align 16
+774:
+ dec %_ASM_BX
+ jnz 771b
+ add $((BITS_PER_LONG/8) * \nr), \sp
+.endm
+
+#define RSB_FILL_LOOPS 16 /* To avoid underflow */
+
+ENTRY(__fill_rsb)
+ STUFF_RSB RSB_FILL_LOOPS, %_ASM_SP
+ ret
+END(__fill_rsb)
+EXPORT_SYMBOL_GPL(__fill_rsb)
+
+#define RSB_CLEAR_LOOPS 32 /* To forcibly overwrite all entries */
+
+ENTRY(__clear_rsb)
+ STUFF_RSB RSB_CLEAR_LOOPS, %_ASM_SP
+ ret
+END(__clear_rsb)
+EXPORT_SYMBOL_GPL(__clear_rsb)
diff --git a/arch/x86/lib/usercopy_32.c b/arch/x86/lib/usercopy_32.c
index 3bc7baf..5c06dbf 100644
--- a/arch/x86/lib/usercopy_32.c
+++ b/arch/x86/lib/usercopy_32.c
@@ -570,12 +570,12 @@
unsigned long __copy_to_user_ll(void __user *to, const void *from,
unsigned long n)
{
- stac();
+ __uaccess_begin_nospec();
if (movsl_is_ok(to, from, n))
__copy_user(to, from, n);
else
n = __copy_user_intel(to, from, n);
- clac();
+ __uaccess_end();
return n;
}
EXPORT_SYMBOL(__copy_to_user_ll);
@@ -627,7 +627,7 @@
unsigned long __copy_from_user_ll_nocache_nozero(void *to, const void __user *from,
unsigned long n)
{
- stac();
+ __uaccess_begin_nospec();
#ifdef CONFIG_X86_INTEL_USERCOPY
if (n > 64 && static_cpu_has(X86_FEATURE_XMM2))
n = __copy_user_intel_nocache(to, from, n);
@@ -636,7 +636,7 @@
#else
__copy_user(to, from, n);
#endif
- clac();
+ __uaccess_end();
return n;
}
EXPORT_SYMBOL(__copy_from_user_ll_nocache_nozero);
diff --git a/crypto/tcrypt.c b/crypto/tcrypt.c
index e3af318..2a07341 100644
--- a/crypto/tcrypt.c
+++ b/crypto/tcrypt.c
@@ -223,11 +223,13 @@
}
sg_init_table(sg, np + 1);
- np--;
+ if (rem)
+ np--;
for (k = 0; k < np; k++)
sg_set_buf(&sg[k + 1], xbuf[k], PAGE_SIZE);
- sg_set_buf(&sg[k + 1], xbuf[k], rem);
+ if (rem)
+ sg_set_buf(&sg[k + 1], xbuf[k], rem);
}
static void test_aead_speed(const char *algo, int enc, unsigned int secs,
diff --git a/drivers/auxdisplay/img-ascii-lcd.c b/drivers/auxdisplay/img-ascii-lcd.c
index 83f1439..6e8eaa7 100644
--- a/drivers/auxdisplay/img-ascii-lcd.c
+++ b/drivers/auxdisplay/img-ascii-lcd.c
@@ -442,3 +442,7 @@
.remove = img_ascii_lcd_remove,
};
module_platform_driver(img_ascii_lcd_driver);
+
+MODULE_DESCRIPTION("Imagination Technologies ASCII LCD Display");
+MODULE_AUTHOR("Paul Burton <paul.burton@mips.com>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/gpu/drm/msm/dp/dp_display.c b/drivers/gpu/drm/msm/dp/dp_display.c
index b1d03d6..731a554 100644
--- a/drivers/gpu/drm/msm/dp/dp_display.c
+++ b/drivers/gpu/drm/msm/dp/dp_display.c
@@ -48,11 +48,6 @@
void *hdcp1;
void *hdcp2;
- int enc_lvl;
-
- bool auth_state;
- bool hdcp1_present;
- bool hdcp2_present;
bool feature_enabled;
};
@@ -65,6 +60,8 @@
bool power_on;
bool audio_supported;
+ atomic_t aborted;
+
struct platform_device *pdev;
struct dentry *root;
struct completion notification_comp;
@@ -93,7 +90,6 @@
struct work_struct attention_work;
struct mutex hdcp_mutex;
struct mutex session_lock;
- int hdcp_status;
unsigned long audio_status;
};
@@ -109,9 +105,8 @@
static inline bool dp_display_is_hdcp_enabled(struct dp_display_private *dp)
{
- return dp->hdcp.feature_enabled &&
- (dp->hdcp.hdcp1_present || dp->hdcp.hdcp2_present) &&
- dp->hdcp.ops;
+ return dp->hdcp.feature_enabled && dp->link->hdcp_status.hdcp_version
+ && dp->hdcp.ops;
}
static irqreturn_t dp_display_irq(int irq, void *dev_id)
@@ -156,32 +151,25 @@
ops = dp->hdcp.ops;
- switch (dp->hdcp_status) {
- case HDCP_STATE_AUTHENTICATING:
- pr_debug("start authenticaton\n");
+ pr_debug("%s: %s\n",
+ sde_hdcp_version(dp->link->hdcp_status.hdcp_version),
+ sde_hdcp_state_name(dp->link->hdcp_status.hdcp_state));
+ switch (dp->link->hdcp_status.hdcp_state) {
+ case HDCP_STATE_AUTHENTICATING:
if (dp->hdcp.ops && dp->hdcp.ops->authenticate)
rc = dp->hdcp.ops->authenticate(dp->hdcp.data);
-
- break;
- case HDCP_STATE_AUTHENTICATED:
- pr_debug("hdcp authenticated\n");
- dp->hdcp.auth_state = true;
break;
case HDCP_STATE_AUTH_FAIL:
- dp->hdcp.auth_state = false;
-
if (dp->power_on) {
- pr_debug("Reauthenticating\n");
if (ops && ops->reauthenticate) {
rc = ops->reauthenticate(dp->hdcp.data);
if (rc)
- pr_err("reauth failed rc=%d\n", rc);
+ pr_err("failed rc=%d\n", rc);
}
} else {
pr_debug("not reauthenticating, cable disconnected\n");
}
-
break;
default:
break;
@@ -189,7 +177,7 @@
}
static void dp_display_notify_hdcp_status_cb(void *ptr,
- enum sde_hdcp_states status)
+ enum sde_hdcp_state state)
{
struct dp_display_private *dp = ptr;
@@ -198,7 +186,7 @@
return;
}
- dp->hdcp_status = status;
+ dp->link->hdcp_status.hdcp_state = state;
if (dp->dp_display.is_connected)
queue_delayed_work(dp->wq, &dp->hdcp_cb_work, HZ/4);
@@ -214,12 +202,16 @@
{
void *fd = NULL;
struct sde_hdcp_ops *ops = NULL;
+ bool hdcp2_present = false, hdcp1_present = false;
if (!dp) {
pr_err("invalid input\n");
return;
}
+ dp->link->hdcp_status.hdcp_state = HDCP_STATE_INACTIVE;
+ dp->link->hdcp_status.hdcp_version = HDCP_VERSION_NONE;
+
if (!dp->hdcp.feature_enabled) {
pr_debug("feature not enabled\n");
return;
@@ -230,26 +222,29 @@
ops = sde_dp_hdcp2p2_start(fd);
if (ops && ops->feature_supported)
- dp->hdcp.hdcp2_present = ops->feature_supported(fd);
+ hdcp2_present = ops->feature_supported(fd);
else
- dp->hdcp.hdcp2_present = false;
+ hdcp2_present = false;
pr_debug("hdcp2p2: %s\n",
- dp->hdcp.hdcp2_present ? "supported" : "not supported");
+ hdcp2_present ? "supported" : "not supported");
- if (!dp->hdcp.hdcp2_present) {
- dp->hdcp.hdcp1_present = hdcp1_check_if_supported_load_app();
+ if (!hdcp2_present) {
+ hdcp1_present = hdcp1_check_if_supported_load_app();
- if (dp->hdcp.hdcp1_present) {
+ if (hdcp1_present) {
fd = dp->hdcp.hdcp1;
ops = sde_hdcp_1x_start(fd);
+ dp->link->hdcp_status.hdcp_version = HDCP_VERSION_1X;
}
+ } else {
+ dp->link->hdcp_status.hdcp_version = HDCP_VERSION_2P2;
}
pr_debug("hdcp1x: %s\n",
- dp->hdcp.hdcp1_present ? "supported" : "not supported");
+ hdcp1_present ? "supported" : "not supported");
- if (dp->hdcp.hdcp2_present || dp->hdcp.hdcp1_present) {
+ if (hdcp2_present || hdcp1_present) {
dp->hdcp.data = fd;
dp->hdcp.ops = ops;
} else {
@@ -290,7 +285,6 @@
hdcp_init_data.drm_aux = dp->aux->drm_aux;
hdcp_init_data.cb_data = (void *)dp;
hdcp_init_data.workq = dp->wq;
- hdcp_init_data.mutex = &dp->hdcp_mutex;
hdcp_init_data.sec_access = true;
hdcp_init_data.notify_status = dp_display_notify_hdcp_status_cb;
hdcp_init_data.dp_ahb = &parser->get_io(parser, "dp_ahb")->io;
@@ -302,6 +296,7 @@
hdcp_init_data.hdcp_io = &parser->get_io(parser,
"hdcp_physical")->io;
hdcp_init_data.revision = &dp->panel->link_info.revision;
+ hdcp_init_data.msm_hdcp_dev = dp->parser->msm_hdcp_dev;
dp->hdcp.hdcp1 = sde_hdcp_1x_init(&hdcp_init_data);
if (IS_ERR_OR_NULL(dp->hdcp.hdcp1)) {
@@ -627,7 +622,7 @@
static void dp_display_clean(struct dp_display_private *dp)
{
if (dp_display_is_hdcp_enabled(dp)) {
- dp->hdcp_status = HDCP_STATE_INACTIVE;
+ dp->link->hdcp_status.hdcp_state = HDCP_STATE_INACTIVE;
cancel_delayed_work_sync(&dp->hdcp_cb_work);
if (dp->hdcp.ops->off)
@@ -646,6 +641,11 @@
int rc;
rc = dp_display_process_hpd_low(dp);
+ if (rc) {
+ /* cancel any pending request */
+ dp->ctrl->abort(dp->ctrl);
+ dp->aux->abort(dp->aux);
+ }
mutex_lock(&dp->session_lock);
if (rc && dp->power_on)
@@ -688,6 +688,7 @@
dp->link->psm_config(dp->link, &dp->panel->link_info, true);
/* cancel any pending request */
+ atomic_set(&dp->aborted, 1);
dp->ctrl->abort(dp->ctrl);
dp->aux->abort(dp->aux);
@@ -696,6 +697,7 @@
flush_workqueue(dp->wq);
dp_display_handle_disconnect(dp);
+ atomic_set(&dp->aborted, 0);
end:
return rc;
}
@@ -779,6 +781,12 @@
return -ENODEV;
}
+ /* check if framework is ready */
+ if (!dp_display_framework_ready(dp)) {
+ pr_err("framework not ready\n");
+ return -ENODEV;
+ }
+
if (dp->usbpd->hpd_irq && dp->usbpd->hpd_high &&
dp->power_on) {
dp->link->process_request(dp->link);
@@ -787,6 +795,7 @@
queue_delayed_work(dp->wq, &dp->connect_work, 0);
} else {
/* cancel any pending request */
+ atomic_set(&dp->aborted, 1);
dp->ctrl->abort(dp->ctrl);
dp->aux->abort(dp->aux);
@@ -795,6 +804,7 @@
flush_workqueue(dp->wq);
dp_display_handle_disconnect(dp);
+ atomic_set(&dp->aborted, 0);
}
return 0;
@@ -811,6 +821,11 @@
return;
}
+ if (atomic_read(&dp->aborted)) {
+ pr_err("aborted\n");
+ return;
+ }
+
dp_display_process_hpd_high(dp);
}
@@ -1063,6 +1078,11 @@
goto end;
}
+ if (atomic_read(&dp->aborted)) {
+ pr_err("aborted\n");
+ goto end;
+ }
+
dp->aux->init(dp->aux, dp->parser->aux_cfg);
rc = dp->ctrl->on(dp->ctrl);
@@ -1095,6 +1115,11 @@
goto end;
}
+ if (atomic_read(&dp->aborted)) {
+ pr_err("aborted\n");
+ goto end;
+ }
+
dp->panel->spd_config(dp->panel);
if (dp->audio_supported) {
@@ -1108,7 +1133,7 @@
if (dp_display_is_hdcp_enabled(dp)) {
cancel_delayed_work_sync(&dp->hdcp_cb_work);
- dp->hdcp_status = HDCP_STATE_AUTHENTICATING;
+ dp->link->hdcp_status.hdcp_state = HDCP_STATE_AUTHENTICATING;
queue_delayed_work(dp->wq, &dp->hdcp_cb_work, HZ / 2);
}
@@ -1142,7 +1167,7 @@
}
if (dp_display_is_hdcp_enabled(dp)) {
- dp->hdcp_status = HDCP_STATE_INACTIVE;
+ dp->link->hdcp_status.hdcp_state = HDCP_STATE_INACTIVE;
cancel_delayed_work_sync(&dp->hdcp_cb_work);
if (dp->hdcp.ops->off)
@@ -1370,6 +1395,7 @@
dp->pdev = pdev;
dp->name = "drm_dp";
dp->audio_status = -ENODEV;
+ atomic_set(&dp->aborted, 0);
rc = dp_display_create_workqueue(dp);
if (rc) {
diff --git a/drivers/gpu/drm/msm/dp/dp_hdcp2p2.c b/drivers/gpu/drm/msm/dp/dp_hdcp2p2.c
index 0e1490f..2b7b217 100644
--- a/drivers/gpu/drm/msm/dp/dp_hdcp2p2.c
+++ b/drivers/gpu/drm/msm/dp/dp_hdcp2p2.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2016-2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2016-2018, 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
@@ -19,6 +19,7 @@
#include <linux/types.h>
#include <linux/kthread.h>
#include <linux/hdcp_qseecom.h>
+#include <linux/msm_hdcp.h>
#include <drm/drm_dp_helper.h>
#include "sde_hdcp.h"
@@ -320,35 +321,16 @@
struct dp_hdcp2p2_ctrl *ctrl = (struct dp_hdcp2p2_ctrl *)client_ctx;
struct hdcp_lib_wakeup_data cdata = {
HDCP_LIB_WKUP_CMD_QUERY_STREAM_TYPE};
- bool enc_notify = true;
- int enc_lvl;
if (!ctrl) {
pr_err("invalid input\n");
return;
}
- switch (min_enc_level) {
- case 0:
- enc_lvl = HDCP_STATE_AUTH_ENC_NONE;
- break;
- case 1:
- enc_lvl = HDCP_STATE_AUTH_ENC_1X;
- break;
- case 2:
- enc_lvl = HDCP_STATE_AUTH_ENC_2P2;
- break;
- default:
- enc_notify = false;
- }
-
pr_debug("enc level changed %d\n", min_enc_level);
cdata.context = ctrl->lib_ctx;
dp_hdcp2p2_wakeup_lib(ctrl, &cdata);
-
- if (enc_notify && ctrl->init_data.notify_status)
- ctrl->init_data.notify_status(ctrl->init_data.cb_data, enc_lvl);
}
static void dp_hdcp2p2_auth_failed(struct dp_hdcp2p2_ctrl *ctrl)
@@ -812,7 +794,6 @@
static struct hdcp_client_ops client_ops = {
.wakeup = dp_hdcp2p2_wakeup,
- .notify_lvl_change = dp_hdcp2p2_min_level_change,
};
static struct dp_hdcp2p2_int_set int_set1[] = {
{BIT(17), "authentication successful", NULL},
@@ -867,6 +848,9 @@
goto error;
}
+ msm_hdcp_register_cb(init_data->msm_hdcp_dev, ctrl,
+ dp_hdcp2p2_min_level_change);
+
kthread_init_worker(&ctrl->worker);
kthread_init_work(&ctrl->auth, dp_hdcp2p2_auth_work);
diff --git a/drivers/gpu/drm/msm/dp/dp_link.h b/drivers/gpu/drm/msm/dp/dp_link.h
index 46d30a7..d14b881 100644
--- a/drivers/gpu/drm/msm/dp/dp_link.h
+++ b/drivers/gpu/drm/msm/dp/dp_link.h
@@ -72,6 +72,11 @@
u32 test_audio_period_ch_8;
};
+struct dp_link_hdcp_status {
+ int hdcp_state;
+ int hdcp_version;
+};
+
struct dp_link_phy_params {
u32 phy_test_pattern_sel;
u8 v_level;
@@ -110,6 +115,7 @@
struct dp_link_test_audio test_audio;
struct dp_link_phy_params phy_params;
struct dp_link_params link_params;
+ struct dp_link_hdcp_status hdcp_status;
u32 (*get_test_bits_depth)(struct dp_link *dp_link, u32 bpp);
int (*process_request)(struct dp_link *dp_link);
diff --git a/drivers/gpu/drm/msm/dp/dp_parser.c b/drivers/gpu/drm/msm/dp/dp_parser.c
index adcc762..0174ea10 100644
--- a/drivers/gpu/drm/msm/dp/dp_parser.c
+++ b/drivers/gpu/drm/msm/dp/dp_parser.c
@@ -15,6 +15,7 @@
#define pr_fmt(fmt) "[drm-dp] %s: " fmt, __func__
#include <linux/of_gpio.h>
+#include <linux/of_platform.h>
#include "dp_parser.h"
@@ -159,6 +160,30 @@
return 0;
}
+static int dp_parser_msm_hdcp_dev(struct dp_parser *parser)
+{
+ struct device_node *node;
+ struct platform_device *pdev;
+
+ node = of_find_compatible_node(NULL, NULL, "qcom,msm-hdcp");
+ if (!node) {
+ // This is a non-fatal error, module initialization can proceed
+ pr_warn("couldn't find msm-hdcp node\n");
+ return 0;
+ }
+
+ pdev = of_find_device_by_node(node);
+ if (!pdev) {
+ // This is a non-fatal error, module initialization can proceed
+ pr_warn("couldn't find msm-hdcp pdev\n");
+ return 0;
+ }
+
+ parser->msm_hdcp_dev = &pdev->dev;
+
+ return 0;
+}
+
static int dp_parser_pinctrl(struct dp_parser *parser)
{
int rc = 0;
@@ -587,6 +612,10 @@
goto err;
rc = dp_parser_pinctrl(parser);
+ if (rc)
+ goto err;
+
+ rc = dp_parser_msm_hdcp_dev(parser);
err:
return rc;
}
diff --git a/drivers/gpu/drm/msm/dp/dp_parser.h b/drivers/gpu/drm/msm/dp/dp_parser.h
index 6e78db2..a768ca3 100644
--- a/drivers/gpu/drm/msm/dp/dp_parser.h
+++ b/drivers/gpu/drm/msm/dp/dp_parser.h
@@ -156,6 +156,7 @@
* struct dp_parser - DP parser's data exposed to clients
*
* @pdev: platform data of the client
+ * @msm_hdcp_dev: device pointer for the HDCP driver
* @mp: gpio, regulator and clock related data
* @pinctrl: pin-control related data
* @ctrl_resouce: controller's register address realated data
@@ -167,6 +168,7 @@
*/
struct dp_parser {
struct platform_device *pdev;
+ struct device *msm_hdcp_dev;
struct dss_module_power mp[DP_MAX_PM];
struct dp_pinctrl pinctrl;
struct dp_io io;
diff --git a/drivers/gpu/drm/msm/dsi-staging/dsi_ctrl.c b/drivers/gpu/drm/msm/dsi-staging/dsi_ctrl.c
index 0113c83..5b8c910 100644
--- a/drivers/gpu/drm/msm/dsi-staging/dsi_ctrl.c
+++ b/drivers/gpu/drm/msm/dsi-staging/dsi_ctrl.c
@@ -1416,8 +1416,7 @@
u32 lanes = 0;
u32 ulps_lanes;
- if (dsi_ctrl->host_config.panel_mode == DSI_OP_CMD_MODE)
- lanes = dsi_ctrl->host_config.common_config.data_lanes;
+ lanes = dsi_ctrl->host_config.common_config.data_lanes;
rc = dsi_ctrl->hw.ops.wait_for_lane_idle(&dsi_ctrl->hw, lanes);
if (rc) {
@@ -1458,9 +1457,7 @@
return 0;
}
- if (dsi_ctrl->host_config.panel_mode == DSI_OP_CMD_MODE)
- lanes = dsi_ctrl->host_config.common_config.data_lanes;
-
+ lanes = dsi_ctrl->host_config.common_config.data_lanes;
lanes |= DSI_CLOCK_LANE;
ulps_lanes = dsi_ctrl->hw.ops.ulps_ops.get_lanes_in_ulps(&dsi_ctrl->hw);
@@ -2439,6 +2436,31 @@
}
/**
+ * dsi_ctrl_update_host_init_state() - Update the host initialization state.
+ * @dsi_ctrl: DSI controller handle.
+ * @enable: boolean signifying host state.
+ *
+ * Update the host initialization status only while exiting from ulps during
+ * suspend state.
+ *
+ * Return: error code.
+ */
+int dsi_ctrl_update_host_init_state(struct dsi_ctrl *dsi_ctrl, bool enable)
+{
+ int rc = 0;
+ u32 state = enable ? 0x1 : 0x0;
+
+ rc = dsi_ctrl_check_state(dsi_ctrl, DSI_CTRL_OP_HOST_INIT, state);
+ if (rc) {
+ pr_err("[DSI_%d] Controller state check failed, rc=%d\n",
+ dsi_ctrl->cell_index, rc);
+ return rc;
+ }
+ dsi_ctrl_update_state(dsi_ctrl, DSI_CTRL_OP_HOST_INIT, state);
+ return rc;
+}
+
+/**
* dsi_ctrl_host_init() - Initialize DSI host hardware.
* @dsi_ctrl: DSI controller handle.
* @is_splash_enabled: boolean signifying splash status.
diff --git a/drivers/gpu/drm/msm/dsi-staging/dsi_ctrl.h b/drivers/gpu/drm/msm/dsi-staging/dsi_ctrl.h
index 9636dbe..ed3d0eb 100644
--- a/drivers/gpu/drm/msm/dsi-staging/dsi_ctrl.h
+++ b/drivers/gpu/drm/msm/dsi-staging/dsi_ctrl.h
@@ -743,4 +743,9 @@
int dsi_ctrl_get_host_engine_init_state(struct dsi_ctrl *dsi_ctrl,
bool *state);
+/**
+ * dsi_ctrl_update_host_init_state() - Set the host initialization state
+ */
+int dsi_ctrl_update_host_init_state(struct dsi_ctrl *dsi_ctrl, bool en);
+
#endif /* _DSI_CTRL_H_ */
diff --git a/drivers/gpu/drm/msm/dsi-staging/dsi_display.c b/drivers/gpu/drm/msm/dsi-staging/dsi_display.c
index fd18905..c5f8092 100644
--- a/drivers/gpu/drm/msm/dsi-staging/dsi_display.c
+++ b/drivers/gpu/drm/msm/dsi-staging/dsi_display.c
@@ -17,6 +17,7 @@
#include <linux/list.h>
#include <linux/of.h>
#include <linux/of_graph.h>
+#include <linux/of_gpio.h>
#include <linux/err.h>
#include "msm_drv.h"
@@ -309,6 +310,90 @@
dsi_panel_release_panel_lock(display->panel);
}
+static irqreturn_t dsi_display_panel_te_irq_handler(int irq, void *data)
+{
+ struct dsi_display *display = (struct dsi_display *)data;
+
+ /*
+ * This irq handler is used for sole purpose of identifying
+ * ESD attacks on panel and we can safely assume IRQ_HANDLED
+ * in case of display not being initalized yet
+ */
+ if (!display)
+ return IRQ_HANDLED;
+
+ atomic_set(&display->te_irq_triggered, 1);
+
+ return IRQ_HANDLED;
+}
+
+static void dsi_display_change_te_irq_status(struct dsi_display *display,
+ bool enable)
+{
+ if (!display) {
+ pr_err("Invalid params\n");
+ return;
+ }
+
+ /* Handle unbalanced irq enable/disbale calls */
+ if (enable && !display->is_te_irq_enabled) {
+ atomic_set(&display->te_irq_triggered, 1);
+ enable_irq(gpio_to_irq(display->disp_te_gpio));
+ display->is_te_irq_enabled = true;
+ } else if (!enable && display->is_te_irq_enabled) {
+ disable_irq(gpio_to_irq(display->disp_te_gpio));
+ atomic_set(&display->te_irq_triggered, 0);
+ display->is_te_irq_enabled = false;
+ }
+}
+
+static void dsi_display_register_te_irq(struct dsi_display *display)
+{
+ int rc;
+ struct platform_device *pdev;
+ struct device *dev;
+
+ pdev = display->pdev;
+ if (!pdev) {
+ pr_err("invalid platform device\n");
+ return;
+ }
+
+ dev = &pdev->dev;
+ if (!dev) {
+ pr_err("invalid device\n");
+ return;
+ }
+
+ rc = devm_request_irq(dev, gpio_to_irq(display->disp_te_gpio),
+ dsi_display_panel_te_irq_handler, IRQF_TRIGGER_FALLING,
+ "TE_GPIO", display);
+ if (rc) {
+ pr_err("TE request_irq failed for ESD rc:%d\n", rc);
+ return;
+ }
+
+ disable_irq(gpio_to_irq(display->disp_te_gpio));
+ display->is_te_irq_enabled = false;
+}
+
+static bool dsi_display_is_te_based_esd(struct dsi_display *display)
+{
+ u32 status_mode = 0;
+
+ if (!display->panel) {
+ pr_err("Invalid panel data\n");
+ return false;
+ }
+
+ status_mode = display->panel->esd_config.status_mode;
+
+ if (status_mode == ESD_MODE_PANEL_TE &&
+ gpio_is_valid(display->disp_te_gpio))
+ return true;
+ return false;
+}
+
/* Allocate memory for cmd dma tx buffer */
static int dsi_host_alloc_cmd_tx_buffer(struct dsi_display *display)
{
@@ -421,6 +506,27 @@
return false;
}
+static void dsi_display_parse_te_gpio(struct dsi_display *display)
+{
+ struct platform_device *pdev;
+ struct device *dev;
+
+ pdev = display->pdev;
+ if (!pdev) {
+ pr_err("Inavlid platform device\n");
+ return;
+ }
+
+ dev = &pdev->dev;
+ if (!dev) {
+ pr_err("Inavlid platform device\n");
+ return;
+ }
+
+ display->disp_te_gpio = of_get_named_gpio(dev->of_node,
+ "qcom,platform-te-gpio", 0);
+}
+
static int dsi_display_read_status(struct dsi_display_ctrl *ctrl,
struct dsi_panel *panel)
{
@@ -566,12 +672,16 @@
static int dsi_display_status_check_te(struct dsi_display *display)
{
- int rc = 0;
+ int const esd_check_success = 1;
- pr_debug(" ++\n");
- /* TODO: wait for TE interrupt from panel */
+ if (atomic_read(&display->te_irq_triggered)) {
+ atomic_set(&display->te_irq_triggered, 0);
+ } else {
+ pr_err("ESD check failed\n");
+ return -EINVAL;
+ }
- return rc;
+ return esd_check_success;
}
int dsi_display_check_status(void *display)
@@ -1933,18 +2043,34 @@
int i;
struct dsi_display_ctrl *ctrl;
- for (i = 0 ; i < display->ctrl_count; i++) {
- ctrl = &display->ctrl[i];
- rc = dsi_ctrl_host_init(ctrl->ctrl,
- display->is_cont_splash_enabled);
- if (rc) {
- pr_err("[%s] failed to init host_%d, rc=%d\n",
- display->name, i, rc);
- goto error_host_deinit;
+ /* when ULPS suspend feature is enabled, we will keep the lanes in
+ * ULPS during suspend state and clamp DSI phy. Hence while resuming
+ * we will programe DSI controller as part of core clock enable.
+ * After that we should not re-configure DSI controller again here for
+ * usecases where we are resuming from ulps suspend as it might put
+ * the HW in bad state.
+ */
+ if (!display->panel->ulps_suspend_enabled || !display->ulps_enabled) {
+ for (i = 0 ; i < display->ctrl_count; i++) {
+ ctrl = &display->ctrl[i];
+ rc = dsi_ctrl_host_init(ctrl->ctrl,
+ display->is_cont_splash_enabled);
+ if (rc) {
+ pr_err("[%s] failed to init host_%d, rc=%d\n",
+ display->name, i, rc);
+ goto error_host_deinit;
+ }
+ }
+ } else {
+ for (i = 0 ; i < display->ctrl_count; i++) {
+ ctrl = &display->ctrl[i];
+ rc = dsi_ctrl_update_host_init_state(ctrl->ctrl, true);
+ if (rc)
+ pr_debug("host init update failed rc=%d\n", rc);
}
}
- return 0;
+ return rc;
error_host_deinit:
for (i = i - 1; i >= 0; i--) {
ctrl = &display->ctrl[i];
@@ -3078,6 +3204,9 @@
display->type = DSI_DISPLAY_SINGLE;
}
+ /* Parse TE gpio */
+ dsi_display_parse_te_gpio(display);
+
error:
return rc;
}
@@ -3934,6 +4063,9 @@
}
}
+ /* register te irq handler */
+ dsi_display_register_te_irq(display);
+
/* Initialize resources for continuous splash */
rc = dsi_display_splash_res_init(display);
if (rc)
@@ -5174,6 +5306,9 @@
mode = display->panel->cur_mode;
+ if (dsi_display_is_te_based_esd(display))
+ dsi_display_change_te_irq_status(display, true);
+
if (mode->dsi_mode_flags & DSI_MODE_FLAG_DMS) {
if (display->is_cont_splash_enabled) {
pr_err("DMS is not supposed to be set on first frame\n");
@@ -5701,6 +5836,10 @@
pr_err("[%s] display wake up failed, rc=%d\n",
display->name, rc);
+ /* Disable panel TE irq */
+ if (dsi_display_is_te_based_esd(display))
+ dsi_display_change_te_irq_status(display, false);
+
rc = dsi_panel_unprepare(display->panel);
if (rc)
pr_err("[%s] panel unprepare failed, rc=%d\n",
diff --git a/drivers/gpu/drm/msm/dsi-staging/dsi_display.h b/drivers/gpu/drm/msm/dsi-staging/dsi_display.h
index 53004e4..e243a92 100644
--- a/drivers/gpu/drm/msm/dsi-staging/dsi_display.h
+++ b/drivers/gpu/drm/msm/dsi-staging/dsi_display.h
@@ -132,6 +132,9 @@
* @is_active: Is display active.
* @is_cont_splash_enabled: Is continuous splash enabled
* @display_lock: Mutex for dsi_display interface.
+ * @disp_te_gpio: GPIO for panel TE interrupt.
+ * @is_te_irq_enabled:bool to specify whether TE interrupt is enabled.
+ * @te_irq_triggered: atomic state notifying panel TE interrupt.
* @ctrl_count: Number of DSI interfaces required by panel.
* @ctrl: Controller information for DSI display.
* @panel: Handle to DSI panel.
@@ -173,6 +176,9 @@
bool is_active;
bool is_cont_splash_enabled;
struct mutex display_lock;
+ int disp_te_gpio;
+ bool is_te_irq_enabled;
+ atomic_t te_irq_triggered;
u32 ctrl_count;
struct dsi_display_ctrl ctrl[MAX_DSI_CTRLS_PER_DISPLAY];
diff --git a/drivers/gpu/drm/msm/dsi-staging/dsi_phy.c b/drivers/gpu/drm/msm/dsi-staging/dsi_phy.c
index 07b2305..cc74de2 100644
--- a/drivers/gpu/drm/msm/dsi-staging/dsi_phy.c
+++ b/drivers/gpu/drm/msm/dsi-staging/dsi_phy.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2016-2017, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2016-2018, 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
@@ -694,8 +694,7 @@
u32 lanes = 0;
u32 ulps_lanes;
- if (config->panel_mode == DSI_OP_CMD_MODE)
- lanes = config->common_config.data_lanes;
+ lanes = config->common_config.data_lanes;
lanes |= DSI_CLOCK_LANE;
/*
@@ -730,8 +729,7 @@
{
u32 ulps_lanes, lanes = 0;
- if (config->panel_mode == DSI_OP_CMD_MODE)
- lanes = config->common_config.data_lanes;
+ lanes = config->common_config.data_lanes;
lanes |= DSI_CLOCK_LANE;
ulps_lanes = phy->hw.ops.ulps_ops.get_lanes_in_ulps(&phy->hw);
diff --git a/drivers/gpu/drm/msm/sde/sde_connector.c b/drivers/gpu/drm/msm/sde/sde_connector.c
index 21b67d9..2adae3d 100644
--- a/drivers/gpu/drm/msm/sde/sde_connector.c
+++ b/drivers/gpu/drm/msm/sde/sde_connector.c
@@ -1736,6 +1736,7 @@
event.length = sizeof(bool);
msm_mode_object_event_notify(&conn->base.base,
conn->base.dev, &event, (u8 *)&panel_dead);
+ sde_encoder_display_failure_notification(conn->encoder);
}
static const struct drm_connector_helper_funcs sde_connector_helper_ops = {
diff --git a/drivers/gpu/drm/msm/sde/sde_encoder.c b/drivers/gpu/drm/msm/sde/sde_encoder.c
index e0023d0..e35a46b 100644
--- a/drivers/gpu/drm/msm/sde/sde_encoder.c
+++ b/drivers/gpu/drm/msm/sde/sde_encoder.c
@@ -1513,6 +1513,29 @@
return ret;
}
+static int _sde_encoder_switch_to_watchdog_vsync(struct drm_encoder *drm_enc)
+{
+ struct sde_encoder_virt *sde_enc;
+ struct msm_display_info disp_info;
+
+ if (!drm_enc) {
+ pr_err("invalid drm encoder\n");
+ return -EINVAL;
+ }
+
+ sde_enc = to_sde_encoder_virt(drm_enc);
+
+ sde_encoder_control_te(drm_enc, false);
+
+ memcpy(&disp_info, &sde_enc->disp_info, sizeof(disp_info));
+ disp_info.is_te_using_watchdog_timer = true;
+ _sde_encoder_update_vsync_source(sde_enc, &disp_info, false);
+
+ sde_encoder_control_te(drm_enc, true);
+
+ return 0;
+}
+
static int _sde_encoder_update_rsc_client(
struct drm_encoder *drm_enc,
struct sde_encoder_rsc_config *config, bool enable)
@@ -1652,7 +1675,12 @@
if (ret) {
SDE_ERROR_ENC(sde_enc,
"wait for vblank failed ret:%d\n", ret);
- break;
+ /**
+ * rsc hardware may hang without vsync. avoid rsc hang
+ * by generating the vsync from watchdog timer.
+ */
+ if (crtc->base.id == wait_vblank_crtc_id)
+ _sde_encoder_switch_to_watchdog_vsync(drm_enc);
}
}
@@ -2546,6 +2574,9 @@
return;
}
+ if (drm_enc->crtc && !sde_enc->crtc)
+ sde_enc->crtc = drm_enc->crtc;
+
comp_info = &mode_info.comp_info;
cur_mode = &sde_enc->base.crtc->state->adjusted_mode;
@@ -4755,3 +4786,17 @@
return ret;
}
+
+int sde_encoder_display_failure_notification(struct drm_encoder *enc)
+{
+ /**
+ * panel may stop generating te signal (vsync) during esd failure. rsc
+ * hardware may hang without vsync. Avoid rsc hang by generating the
+ * vsync from watchdog timer instead of panel.
+ */
+ _sde_encoder_switch_to_watchdog_vsync(enc);
+
+ sde_encoder_wait_for_event(enc, MSM_ENC_TX_COMPLETE);
+
+ return 0;
+}
diff --git a/drivers/gpu/drm/msm/sde/sde_encoder.h b/drivers/gpu/drm/msm/sde/sde_encoder.h
index 2c84e20..4696736 100644
--- a/drivers/gpu/drm/msm/sde/sde_encoder.h
+++ b/drivers/gpu/drm/msm/sde/sde_encoder.h
@@ -237,4 +237,14 @@
*/
int sde_encoder_update_caps_for_cont_splash(struct drm_encoder *encoder);
+/**
+ * sde_encoder_display_failure_notification - update sde encoder state for
+ * esd timeout or other display failure notification. This event flows from
+ * dsi, sde_connector to sde_encoder.
+ * TODO: manage the event at sde_kms level for forward processing.
+ * @drm_enc: Pointer to drm encoder structure
+ * @Return: true if successful in updating the encoder structure
+ */
+int sde_encoder_display_failure_notification(struct drm_encoder *enc);
+
#endif /* __SDE_ENCODER_H__ */
diff --git a/drivers/gpu/drm/msm/sde/sde_encoder_phys_cmd.c b/drivers/gpu/drm/msm/sde/sde_encoder_phys_cmd.c
index 53c8dfb..792654d 100644
--- a/drivers/gpu/drm/msm/sde/sde_encoder_phys_cmd.c
+++ b/drivers/gpu/drm/msm/sde/sde_encoder_phys_cmd.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2015-2017 The Linux Foundation. All rights reserved.
+ * Copyright (c) 2015-2018 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
@@ -450,6 +450,17 @@
cmd_enc->pp_timeout_report_cnt++;
+ if (sde_encoder_phys_cmd_is_master(phys_enc)) {
+ /* trigger the retire fence if it was missed */
+ if (atomic_add_unless(&phys_enc->pending_retire_fence_cnt,
+ -1, 0))
+ phys_enc->parent_ops.handle_frame_done(
+ phys_enc->parent,
+ phys_enc,
+ SDE_ENCODER_FRAME_EVENT_SIGNAL_RETIRE_FENCE);
+ atomic_add_unless(&phys_enc->pending_ctlstart_cnt, -1, 0);
+ }
+
SDE_EVT32(DRMID(phys_enc->parent), phys_enc->hw_pp->idx - PINGPONG_0,
cmd_enc->pp_timeout_report_cnt,
atomic_read(&phys_enc->pending_kickoff_cnt),
diff --git a/drivers/gpu/drm/msm/sde_hdcp.h b/drivers/gpu/drm/msm/sde_hdcp.h
index 6c44260..c8e84d3cc 100644
--- a/drivers/gpu/drm/msm/sde_hdcp.h
+++ b/drivers/gpu/drm/msm/sde_hdcp.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012, 2014-2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2012, 2014-2018, 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
@@ -30,17 +30,21 @@
HDCP_CLIENT_DP,
};
-enum sde_hdcp_states {
+enum sde_hdcp_state {
HDCP_STATE_INACTIVE,
HDCP_STATE_AUTHENTICATING,
HDCP_STATE_AUTHENTICATED,
HDCP_STATE_AUTH_FAIL,
- HDCP_STATE_AUTH_ENC_NONE,
- HDCP_STATE_AUTH_ENC_1X,
- HDCP_STATE_AUTH_ENC_2P2
+};
+
+enum sde_hdcp_version {
+ HDCP_VERSION_NONE,
+ HDCP_VERSION_1X,
+ HDCP_VERSION_2P2
};
struct sde_hdcp_init_data {
+ struct device *msm_hdcp_dev;
struct dss_io_data *core_io;
struct dss_io_data *dp_ahb;
struct dss_io_data *dp_aux;
@@ -52,7 +56,7 @@
struct mutex *mutex;
struct workqueue_struct *workq;
void *cb_data;
- void (*notify_status)(void *cb_data, enum sde_hdcp_states status);
+ void (*notify_status)(void *cb_data, enum sde_hdcp_state state);
u8 sink_rx_status;
unsigned char *revision;
u32 phy_addr;
@@ -69,11 +73,32 @@
void (*off)(void *hdcp_ctrl);
};
+static inline const char *sde_hdcp_state_name(enum sde_hdcp_state hdcp_state)
+{
+ switch (hdcp_state) {
+ case HDCP_STATE_INACTIVE: return "HDCP_STATE_INACTIVE";
+ case HDCP_STATE_AUTHENTICATING: return "HDCP_STATE_AUTHENTICATING";
+ case HDCP_STATE_AUTHENTICATED: return "HDCP_STATE_AUTHENTICATED";
+ case HDCP_STATE_AUTH_FAIL: return "HDCP_STATE_AUTH_FAIL";
+ default: return "???";
+ }
+}
+
+static inline const char *sde_hdcp_version(enum sde_hdcp_version hdcp_version)
+{
+ switch (hdcp_version) {
+ case HDCP_VERSION_NONE: return "HDCP_VERSION_NONE";
+ case HDCP_VERSION_1X: return "HDCP_VERSION_1X";
+ case HDCP_VERSION_2P2: return "HDCP_VERSION_2P2";
+ default: return "???";
+ }
+}
+
void *sde_hdcp_1x_init(struct sde_hdcp_init_data *init_data);
void sde_hdcp_1x_deinit(void *input);
struct sde_hdcp_ops *sde_hdcp_1x_start(void *input);
void *sde_dp_hdcp2p2_init(struct sde_hdcp_init_data *init_data);
void sde_dp_hdcp2p2_deinit(void *input);
-const char *sde_hdcp_state_name(enum sde_hdcp_states hdcp_state);
+const char *sde_hdcp_state_name(enum sde_hdcp_state hdcp_state);
struct sde_hdcp_ops *sde_dp_hdcp2p2_start(void *input);
#endif /* __SDE_HDCP_H__ */
diff --git a/drivers/gpu/drm/msm/sde_hdcp_1x.c b/drivers/gpu/drm/msm/sde_hdcp_1x.c
index 2f900ece..2c7f52c 100644
--- a/drivers/gpu/drm/msm/sde_hdcp_1x.c
+++ b/drivers/gpu/drm/msm/sde_hdcp_1x.c
@@ -18,6 +18,7 @@
#include <linux/slab.h>
#include <linux/stat.h>
#include <linux/iopoll.h>
+#include <linux/msm_hdcp.h>
#include <linux/hdcp_qseecom.h>
#include <drm/drm_dp_helper.h>
#include "sde_hdcp.h"
@@ -213,8 +214,7 @@
bool sink_r0_ready;
bool reauth;
bool ksv_ready;
- enum sde_hdcp_states hdcp_state;
- struct HDCP_V2V1_MSG_TOPOLOGY cached_tp;
+ enum sde_hdcp_state hdcp_state;
struct HDCP_V2V1_MSG_TOPOLOGY current_tp;
struct delayed_work hdcp_auth_work;
struct completion r0_checked;
@@ -227,17 +227,6 @@
struct workqueue_struct *workq;
};
-const char *sde_hdcp_state_name(enum sde_hdcp_states hdcp_state)
-{
- switch (hdcp_state) {
- case HDCP_STATE_INACTIVE: return "HDCP_STATE_INACTIVE";
- case HDCP_STATE_AUTHENTICATING: return "HDCP_STATE_AUTHENTICATING";
- case HDCP_STATE_AUTHENTICATED: return "HDCP_STATE_AUTHENTICATED";
- case HDCP_STATE_AUTH_FAIL: return "HDCP_STATE_AUTH_FAIL";
- default: return "???";
- }
-}
-
static int sde_hdcp_1x_count_one(u8 *array, u8 len)
{
int i, j, count = 0;
@@ -1062,30 +1051,12 @@
return rc;
}
-static void sde_hdcp_1x_cache_topology(struct sde_hdcp_1x *hdcp)
-{
- if (!hdcp || !hdcp->init_data.dp_ahb || !hdcp->init_data.dp_aux ||
- !hdcp->init_data.dp_link || !hdcp->init_data.dp_p0) {
- pr_err("invalid input\n");
- return;
- }
-
- memcpy((void *)&hdcp->cached_tp,
- (void *) &hdcp->current_tp,
- sizeof(hdcp->cached_tp));
- hdcp1_cache_repeater_topology((void *)&hdcp->cached_tp);
-}
-
-static void sde_hdcp_1x_notify_topology(void)
-{
- hdcp1_notify_topology();
-}
-
static void sde_hdcp_1x_update_auth_status(struct sde_hdcp_1x *hdcp)
{
if (sde_hdcp_1x_state(HDCP_STATE_AUTHENTICATED)) {
- sde_hdcp_1x_cache_topology(hdcp);
- sde_hdcp_1x_notify_topology();
+ msm_hdcp_cache_repeater_topology(hdcp->init_data.msm_hdcp_dev,
+ &hdcp->current_tp);
+ msm_hdcp_notify_topology(hdcp->init_data.msm_hdcp_dev);
}
if (hdcp->init_data.notify_status &&
@@ -1262,11 +1233,9 @@
* Also, need to set the state to inactive here so that any ongoing
* reauth works will know that the HDCP session has been turned off.
*/
- mutex_lock(hdcp->init_data.mutex);
DSS_REG_W(io, isr->int_reg,
DSS_REG_R(io, isr->int_reg) & ~HDCP_INT_EN);
hdcp->hdcp_state = HDCP_STATE_INACTIVE;
- mutex_unlock(hdcp->init_data.mutex);
/* complete any wait pending */
complete_all(&hdcp->sink_r0_available);
@@ -1510,7 +1479,7 @@
.off = sde_hdcp_1x_off
};
- if (!init_data || !init_data->mutex || !init_data->notify_status ||
+ if (!init_data || !init_data->notify_status ||
!init_data->workq || !init_data->cb_data) {
pr_err("invalid input\n");
goto error;
diff --git a/drivers/gpu/drm/rcar-du/rcar_du_crtc.c b/drivers/gpu/drm/rcar-du/rcar_du_crtc.c
index a2ec6d8..3322b15 100644
--- a/drivers/gpu/drm/rcar-du/rcar_du_crtc.c
+++ b/drivers/gpu/drm/rcar-du/rcar_du_crtc.c
@@ -392,6 +392,31 @@
rcrtc->started = true;
}
+static void rcar_du_crtc_disable_planes(struct rcar_du_crtc *rcrtc)
+{
+ struct rcar_du_device *rcdu = rcrtc->group->dev;
+ struct drm_crtc *crtc = &rcrtc->crtc;
+ u32 status;
+ /* Make sure vblank interrupts are enabled. */
+ drm_crtc_vblank_get(crtc);
+ /*
+ * Disable planes and calculate how many vertical blanking interrupts we
+ * have to wait for. If a vertical blanking interrupt has been triggered
+ * but not processed yet, we don't know whether it occurred before or
+ * after the planes got disabled. We thus have to wait for two vblank
+ * interrupts in that case.
+ */
+ spin_lock_irq(&rcrtc->vblank_lock);
+ rcar_du_group_write(rcrtc->group, rcrtc->index % 2 ? DS2PR : DS1PR, 0);
+ status = rcar_du_crtc_read(rcrtc, DSSR);
+ rcrtc->vblank_count = status & DSSR_VBK ? 2 : 1;
+ spin_unlock_irq(&rcrtc->vblank_lock);
+ if (!wait_event_timeout(rcrtc->vblank_wait, rcrtc->vblank_count == 0,
+ msecs_to_jiffies(100)))
+ dev_warn(rcdu->dev, "vertical blanking timeout\n");
+ drm_crtc_vblank_put(crtc);
+}
+
static void rcar_du_crtc_stop(struct rcar_du_crtc *rcrtc)
{
struct drm_crtc *crtc = &rcrtc->crtc;
@@ -400,17 +425,16 @@
return;
/* Disable all planes and wait for the change to take effect. This is
- * required as the DSnPR registers are updated on vblank, and no vblank
- * will occur once the CRTC is stopped. Disabling planes when starting
- * the CRTC thus wouldn't be enough as it would start scanning out
- * immediately from old frame buffers until the next vblank.
+ * required as the plane enable registers are updated on vblank, and no
+ * vblank will occur once the CRTC is stopped. Disabling planes when
+ * starting the CRTC thus wouldn't be enough as it would start scanning
+ * out immediately from old frame buffers until the next vblank.
*
* This increases the CRTC stop delay, especially when multiple CRTCs
* are stopped in one operation as we now wait for one vblank per CRTC.
* Whether this can be improved needs to be researched.
*/
- rcar_du_group_write(rcrtc->group, rcrtc->index % 2 ? DS2PR : DS1PR, 0);
- drm_crtc_wait_one_vblank(crtc);
+ rcar_du_crtc_disable_planes(rcrtc);
/* Disable vertical blanking interrupt reporting. We first need to wait
* for page flip completion before stopping the CRTC as userspace
@@ -548,10 +572,25 @@
irqreturn_t ret = IRQ_NONE;
u32 status;
+ spin_lock(&rcrtc->vblank_lock);
+
status = rcar_du_crtc_read(rcrtc, DSSR);
rcar_du_crtc_write(rcrtc, DSRCR, status & DSRCR_MASK);
- if (status & DSSR_FRM) {
+ if (status & DSSR_VBK) {
+ /*
+ * Wake up the vblank wait if the counter reaches 0. This must
+ * be protected by the vblank_lock to avoid races in
+ * rcar_du_crtc_disable_planes().
+ */
+ if (rcrtc->vblank_count) {
+ if (--rcrtc->vblank_count == 0)
+ wake_up(&rcrtc->vblank_wait);
+ }
+ }
+ spin_unlock(&rcrtc->vblank_lock);
+
+ if (status & DSSR_VBK) {
drm_crtc_handle_vblank(&rcrtc->crtc);
rcar_du_crtc_finish_page_flip(rcrtc);
ret = IRQ_HANDLED;
@@ -606,6 +645,8 @@
}
init_waitqueue_head(&rcrtc->flip_wait);
+ init_waitqueue_head(&rcrtc->vblank_wait);
+ spin_lock_init(&rcrtc->vblank_lock);
rcrtc->group = rgrp;
rcrtc->mmio_offset = mmio_offsets[index];
diff --git a/drivers/gpu/drm/rcar-du/rcar_du_crtc.h b/drivers/gpu/drm/rcar-du/rcar_du_crtc.h
index 6f08b7e..48bef05 100644
--- a/drivers/gpu/drm/rcar-du/rcar_du_crtc.h
+++ b/drivers/gpu/drm/rcar-du/rcar_du_crtc.h
@@ -15,6 +15,7 @@
#define __RCAR_DU_CRTC_H__
#include <linux/mutex.h>
+#include <linux/spinlock.h>
#include <linux/wait.h>
#include <drm/drmP.h>
@@ -33,6 +34,9 @@
* @started: whether the CRTC has been started and is running
* @event: event to post when the pending page flip completes
* @flip_wait: wait queue used to signal page flip completion
+ * @vblank_lock: protects vblank_wait and vblank_count
+ * @vblank_wait: wait queue used to signal vertical blanking
+ * @vblank_count: number of vertical blanking interrupts to wait for
* @outputs: bitmask of the outputs (enum rcar_du_output) driven by this CRTC
* @group: CRTC group this CRTC belongs to
*/
@@ -48,6 +52,10 @@
struct drm_pending_vblank_event *event;
wait_queue_head_t flip_wait;
+ spinlock_t vblank_lock;
+ wait_queue_head_t vblank_wait;
+ unsigned int vblank_count;
+
unsigned int outputs;
struct rcar_du_group *group;
diff --git a/drivers/gpu/msm/adreno_a5xx.c b/drivers/gpu/msm/adreno_a5xx.c
index a615dca..4775c3e 100644
--- a/drivers/gpu/msm/adreno_a5xx.c
+++ b/drivers/gpu/msm/adreno_a5xx.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2014-2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2014-2018, 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
@@ -1353,31 +1353,27 @@
/* todo double check the reg writes */
while ((cur - opcode) < length) {
- switch (cur[0]) {
- /* Write a 32 bit value to a 64 bit reg */
- case 1:
+ if (cur[0] == 1 && ((cur + 4) - opcode) <= length) {
+ /* Write a 32 bit value to a 64 bit reg */
reg = cur[2];
reg = (reg << 32) | cur[1];
kgsl_regwrite(KGSL_DEVICE(adreno_dev), reg, cur[3]);
cur += 4;
- break;
- /* Write a 64 bit value to a 64 bit reg */
- case 2:
+ } else if (cur[0] == 2 && ((cur + 5) - opcode) <= length) {
+ /* Write a 64 bit value to a 64 bit reg */
reg = cur[2];
reg = (reg << 32) | cur[1];
val = cur[4];
val = (val << 32) | cur[3];
kgsl_regwrite(KGSL_DEVICE(adreno_dev), reg, val);
cur += 5;
- break;
- /* Delay for X usec */
- case 3:
+ } else if (cur[0] == 3 && ((cur + 2) - opcode) <= length) {
+ /* Delay for X usec */
udelay(cur[1]);
cur += 2;
- break;
- default:
+ } else
return -EINVAL;
- } }
+ }
return 0;
}
diff --git a/drivers/gpu/msm/adreno_a6xx.c b/drivers/gpu/msm/adreno_a6xx.c
index 7b783a9..1250437 100644
--- a/drivers/gpu/msm/adreno_a6xx.c
+++ b/drivers/gpu/msm/adreno_a6xx.c
@@ -791,13 +791,14 @@
kgsl_regwrite(device, A6XX_RB_CONTEXT_SWITCH_GMEM_SAVE_RESTORE,
0x1);
+ a6xx_protect_init(adreno_dev);
+
if (!patch_reglist && (adreno_dev->pwrup_reglist.gpuaddr != 0)) {
a6xx_patch_pwrup_reglist(adreno_dev);
patch_reglist = true;
}
a6xx_preemption_start(adreno_dev);
- a6xx_protect_init(adreno_dev);
/*
* We start LM here because we want all the following to be up
diff --git a/drivers/hwmon/qpnp-adc-common.c b/drivers/hwmon/qpnp-adc-common.c
index f3e16b3..b35605d 100644
--- a/drivers/hwmon/qpnp-adc-common.c
+++ b/drivers/hwmon/qpnp-adc-common.c
@@ -677,6 +677,80 @@
{124, 980}
};
+/* Voltage to temperature */
+static const struct qpnp_vadc_map_pt adcmap_batt_therm_qrd[] = {
+ {1840, -400},
+ {1835, -380},
+ {1828, -360},
+ {1821, -340},
+ {1813, -320},
+ {1803, -300},
+ {1793, -280},
+ {1781, -260},
+ {1768, -240},
+ {1753, -220},
+ {1737, -200},
+ {1719, -180},
+ {1700, -160},
+ {1679, -140},
+ {1655, -120},
+ {1630, -100},
+ {1603, -80},
+ {1574, -60},
+ {1543, -40},
+ {1510, -20},
+ {1475, 00},
+ {1438, 20},
+ {1400, 40},
+ {1360, 60},
+ {1318, 80},
+ {1276, 100},
+ {1232, 120},
+ {1187, 140},
+ {1142, 160},
+ {1097, 180},
+ {1051, 200},
+ {1005, 220},
+ {960, 240},
+ {915, 260},
+ {871, 280},
+ {828, 300},
+ {786, 320},
+ {745, 340},
+ {705, 360},
+ {666, 380},
+ {629, 400},
+ {594, 420},
+ {560, 440},
+ {527, 460},
+ {497, 480},
+ {467, 500},
+ {439, 520},
+ {413, 540},
+ {388, 560},
+ {365, 580},
+ {343, 600},
+ {322, 620},
+ {302, 640},
+ {284, 660},
+ {267, 680},
+ {251, 700},
+ {235, 720},
+ {221, 740},
+ {208, 760},
+ {195, 780},
+ {184, 800},
+ {173, 820},
+ {163, 840},
+ {153, 860},
+ {144, 880},
+ {136, 900},
+ {128, 920},
+ {120, 940},
+ {114, 960},
+ {107, 980}
+};
+
/*
* Voltage to temperature table for 100k pull up for NTCG104EF104 with
* 1.875V reference.
@@ -1016,6 +1090,36 @@
}
EXPORT_SYMBOL(qpnp_adc_batt_therm);
+int32_t qpnp_adc_batt_therm_qrd(struct qpnp_vadc_chip *chip,
+ int32_t adc_code,
+ const struct qpnp_adc_properties *adc_properties,
+ const struct qpnp_vadc_chan_properties *chan_properties,
+ struct qpnp_vadc_result *adc_chan_result)
+{
+ int64_t batt_thm_voltage = 0;
+
+ if (!chan_properties || !chan_properties->offset_gain_numerator ||
+ !chan_properties->offset_gain_denominator || !adc_properties
+ || !adc_chan_result)
+ return -EINVAL;
+
+ if (adc_properties->adc_hc) {
+ /* (code * vref_vadc (1.875V) * 1000) / (scale_code * 1000) */
+ if (adc_code > QPNP_VADC_HC_MAX_CODE)
+ adc_code = 0;
+ batt_thm_voltage = (int64_t) adc_code;
+ batt_thm_voltage *= (adc_properties->adc_vdd_reference
+ * 1000);
+ batt_thm_voltage = div64_s64(batt_thm_voltage,
+ adc_properties->full_scale_code * 1000);
+ qpnp_adc_map_voltage_temp(adcmap_batt_therm_qrd,
+ ARRAY_SIZE(adcmap_batt_therm_qrd),
+ batt_thm_voltage, &adc_chan_result->physical);
+ }
+ return 0;
+}
+EXPORT_SYMBOL(qpnp_adc_batt_therm_qrd);
+
int32_t qpnp_adc_scale_batt_therm(struct qpnp_vadc_chip *chip,
int32_t adc_code,
const struct qpnp_adc_properties *adc_properties,
diff --git a/drivers/hwmon/qpnp-adc-voltage.c b/drivers/hwmon/qpnp-adc-voltage.c
index 8b44c0f..f5f914fc6 100644
--- a/drivers/hwmon/qpnp-adc-voltage.c
+++ b/drivers/hwmon/qpnp-adc-voltage.c
@@ -107,7 +107,7 @@
#define QPNP_VADC_CONV_TIME_MIN 1000
#define QPNP_VADC_CONV_TIME_MAX 1100
#define QPNP_ADC_COMPLETION_TIMEOUT HZ
-#define QPNP_VADC_ERR_COUNT 20
+#define QPNP_VADC_ERR_COUNT 50
#define QPNP_OP_MODE_SHIFT 3
#define QPNP_VADC_THR_LSB_MASK(val) (val & 0xff)
@@ -224,6 +224,7 @@
[SCALE_DIE_TEMP] = {qpnp_adc_scale_die_temp},
[SCALE_I_DEFAULT] = {qpnp_iadc_scale_default},
[SCALE_USBIN_I] = {qpnp_adc_scale_usbin_curr},
+ [SCALE_BATT_THERM_TEMP_QRD] = {qpnp_adc_batt_therm_qrd},
};
static struct qpnp_vadc_rscale_fn adc_vadc_rscale_fn[] = {
diff --git a/drivers/hwtracing/coresight/coresight-dbgui.c b/drivers/hwtracing/coresight/coresight-dbgui.c
index e4feea2..eb01fca 100644
--- a/drivers/hwtracing/coresight/coresight-dbgui.c
+++ b/drivers/hwtracing/coresight/coresight-dbgui.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2014-2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2014-2018, 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
@@ -375,10 +375,10 @@
const char *buf,
size_t size)
{
- uint32_t val;
+ unsigned long val;
struct dbgui_drvdata *drvdata = dev_get_drvdata(dev->parent);
- if (kstrtoul(buf, 16, (unsigned long *)&val))
+ if (kstrtoul(buf, 16, &val))
return -EINVAL;
mutex_lock(&drvdata->mutex);
@@ -403,10 +403,10 @@
const char *buf,
size_t size)
{
- uint32_t val;
+ unsigned long val;
struct dbgui_drvdata *drvdata = dev_get_drvdata(dev->parent);
- if (kstrtoul(buf, 16, (unsigned long *)&val))
+ if (kstrtoul(buf, 16, &val))
return -EINVAL;
mutex_lock(&drvdata->mutex);
@@ -434,10 +434,10 @@
const char *buf,
size_t size)
{
- uint32_t val;
+ unsigned long val;
struct dbgui_drvdata *drvdata = dev_get_drvdata(dev->parent);
- if (kstrtoul(buf, 16, (unsigned long *)&val))
+ if (kstrtoul(buf, 16, &val))
return -EINVAL;
mutex_lock(&drvdata->mutex);
@@ -463,10 +463,10 @@
const char *buf,
size_t size)
{
- uint32_t val;
+ unsigned long val;
struct dbgui_drvdata *drvdata = dev_get_drvdata(dev->parent);
- if (kstrtoul(buf, 16, (unsigned long *)&val))
+ if (kstrtoul(buf, 16, &val))
return -EINVAL;
mutex_lock(&drvdata->mutex);
@@ -492,10 +492,10 @@
const char *buf,
size_t size)
{
- uint32_t val;
+ unsigned long val;
struct dbgui_drvdata *drvdata = dev_get_drvdata(dev->parent);
- if (kstrtoul(buf, 16, (unsigned long *)&val))
+ if (kstrtoul(buf, 16, &val))
return -EINVAL;
if (!val)
@@ -528,10 +528,10 @@
const char *buf,
size_t size)
{
- uint32_t val;
+ unsigned long val;
struct dbgui_drvdata *drvdata = dev_get_drvdata(dev->parent);
- if (kstrtoul(buf, 16, (unsigned long *)&val))
+ if (kstrtoul(buf, 16, &val))
return -EINVAL;
if (val > drvdata->size)
@@ -569,10 +569,10 @@
const char *buf,
size_t size)
{
- uint32_t val;
+ unsigned long val;
struct dbgui_drvdata *drvdata = dev_get_drvdata(dev->parent);
- if (kstrtoul(buf, 16, (unsigned long *)&val))
+ if (kstrtoul(buf, 16, &val))
return -EINVAL;
if (val > drvdata->size)
@@ -615,10 +615,10 @@
const char *buf,
size_t size)
{
- uint32_t val;
+ unsigned long val;
struct dbgui_drvdata *drvdata = dev_get_drvdata(dev->parent);
- if (kstrtoul(buf, 16, (unsigned long *)&val))
+ if (kstrtoul(buf, 16, &val))
return -EINVAL;
mutex_lock(&drvdata->mutex);
@@ -643,10 +643,10 @@
const char *buf,
size_t size)
{
- uint32_t val;
+ unsigned long val;
struct dbgui_drvdata *drvdata = dev_get_drvdata(dev->parent);
- if (kstrtoul(buf, 16, (unsigned long *)&val))
+ if (kstrtoul(buf, 16, &val))
return -EINVAL;
if (val >= drvdata->size)
@@ -674,10 +674,10 @@
const char *buf,
size_t size)
{
- uint32_t val;
+ unsigned long val;
struct dbgui_drvdata *drvdata = dev_get_drvdata(dev->parent);
- if (kstrtoul(buf, 16, (unsigned long *)&val))
+ if (kstrtoul(buf, 16, &val))
return -EINVAL;
mutex_lock(&drvdata->mutex);
@@ -755,9 +755,10 @@
size_t size)
{
struct dbgui_drvdata *drvdata = dev_get_drvdata(dev->parent);
- uint32_t val, ret;
+ uint32_t ret;
+ unsigned long val;
- if (kstrtoul(buf, 16, (unsigned long *)&val))
+ if (kstrtoul(buf, 16, &val))
return -EINVAL;
if (val)
diff --git a/drivers/i2c/busses/i2c-msm-v2.c b/drivers/i2c/busses/i2c-msm-v2.c
index 4daed7f..ad77697 100644
--- a/drivers/i2c/busses/i2c-msm-v2.c
+++ b/drivers/i2c/busses/i2c-msm-v2.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2014-2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2014-2018, 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
@@ -1274,10 +1274,10 @@
tx->dir,
(SPS_IOVEC_FLAG_EOT |
SPS_IOVEC_FLAG_NWD));
- if (dma_desc_tx < 0) {
+ if (IS_ERR_OR_NULL(dma_desc_tx)) {
dev_err(ctrl->dev, "error dmaengine_prep_slave_sg tx:%ld\n",
PTR_ERR(dma_desc_tx));
- ret = PTR_ERR(dma_desc_tx);
+ ret = dma_desc_tx ? PTR_ERR(dma_desc_tx) : -ENOMEM;
goto dma_xfer_end;
}
@@ -1292,11 +1292,11 @@
sg_rx_itr - sg_rx, rx->dir,
(SPS_IOVEC_FLAG_EOT |
SPS_IOVEC_FLAG_NWD));
- if (dma_desc_rx < 0) {
+ if (IS_ERR_OR_NULL(dma_desc_rx)) {
dev_err(ctrl->dev,
"error dmaengine_prep_slave_sg rx:%ld\n",
PTR_ERR(dma_desc_rx));
- ret = PTR_ERR(dma_desc_rx);
+ ret = dma_desc_rx ? PTR_ERR(dma_desc_rx) : -ENOMEM;
goto dma_xfer_end;
}
diff --git a/drivers/input/misc/hbtp_input.c b/drivers/input/misc/hbtp_input.c
index 90493b1..09fa146 100644
--- a/drivers/input/misc/hbtp_input.c
+++ b/drivers/input/misc/hbtp_input.c
@@ -1,5 +1,5 @@
-/* Copyright (c) 2014-2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2014-2018, 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
@@ -86,6 +86,7 @@
u32 power_on_delay;
u32 power_off_delay;
bool manage_pin_ctrl;
+ bool init_completion_done_once;
struct kobject *sysfs_kobject;
s16 ROI[MAX_ROI_SIZE];
s16 accelBuffer[MAX_ACCEL_SIZE];
@@ -783,8 +784,14 @@
return -EFAULT;
}
mutex_lock(&hbtp->mutex);
- init_completion(&hbtp->power_resume_sig);
- init_completion(&hbtp->power_suspend_sig);
+ if (hbtp->init_completion_done_once) {
+ reinit_completion(&hbtp->power_resume_sig);
+ reinit_completion(&hbtp->power_suspend_sig);
+ } else {
+ init_completion(&hbtp->power_resume_sig);
+ init_completion(&hbtp->power_suspend_sig);
+ hbtp->init_completion_done_once = true;
+ }
hbtp->power_sig_enabled = true;
mutex_unlock(&hbtp->mutex);
pr_err("%s: sync_signal option is enabled\n", __func__);
@@ -1455,6 +1462,7 @@
mutex_init(&hbtp->mutex);
mutex_init(&hbtp->sensormutex);
hbtp->display_status = 1;
+ hbtp->init_completion_done_once = false;
error = misc_register(&hbtp_input_misc);
if (error) {
diff --git a/drivers/iommu/arm-smmu.c b/drivers/iommu/arm-smmu.c
index 868be8b..bc01a41 100644
--- a/drivers/iommu/arm-smmu.c
+++ b/drivers/iommu/arm-smmu.c
@@ -535,6 +535,8 @@
struct list_head unassign_list;
struct mutex assign_lock;
struct list_head secure_pool_list;
+ /* nonsecure pool protected by pgtbl_lock */
+ struct list_head nonsecure_pool;
struct iommu_domain domain;
bool qsmmuv500_errata1_init;
@@ -1313,8 +1315,19 @@
void *page;
struct arm_smmu_domain *smmu_domain = cookie;
- if (!arm_smmu_is_master_side_secure(smmu_domain))
+ if (!arm_smmu_is_master_side_secure(smmu_domain)) {
+ struct page *pg;
+ /* size is expected to be 4K with current configuration */
+ if (size == PAGE_SIZE) {
+ pg = list_first_entry_or_null(
+ &smmu_domain->nonsecure_pool, struct page, lru);
+ if (pg) {
+ list_del_init(&pg->lru);
+ return page_address(pg);
+ }
+ }
return alloc_pages_exact(size, gfp_mask);
+ }
page = arm_smmu_secure_pool_remove(smmu_domain, size);
if (page)
@@ -2080,6 +2093,7 @@
INIT_LIST_HEAD(&smmu_domain->unassign_list);
mutex_init(&smmu_domain->assign_lock);
INIT_LIST_HEAD(&smmu_domain->secure_pool_list);
+ INIT_LIST_HEAD(&smmu_domain->nonsecure_pool);
arm_smmu_domain_reinit(smmu_domain);
return &smmu_domain->domain;
@@ -2432,6 +2446,50 @@
return 0;
}
+static void arm_smmu_prealloc_memory(struct arm_smmu_domain *smmu_domain,
+ struct scatterlist *sgl, int nents,
+ struct list_head *pool)
+{
+ u32 nr = 0;
+ int i;
+ size_t size = 0;
+ struct scatterlist *sg;
+ struct page *page;
+
+ if ((smmu_domain->attributes & (1 << DOMAIN_ATTR_ATOMIC)) ||
+ arm_smmu_has_secure_vmid(smmu_domain))
+ return;
+
+ for_each_sg(sgl, sg, nents, i)
+ size += sg->length;
+
+ /* number of 2nd level pagetable entries */
+ nr += round_up(size, SZ_1G) >> 30;
+ /* number of 3rd level pagetabel entries */
+ nr += round_up(size, SZ_2M) >> 21;
+
+ /* Retry later with atomic allocation on error */
+ for (i = 0; i < nr; i++) {
+ page = alloc_pages(GFP_KERNEL | __GFP_ZERO, 0);
+ if (!page)
+ break;
+ list_add(&page->lru, pool);
+ }
+}
+
+static void arm_smmu_release_prealloc_memory(
+ struct arm_smmu_domain *smmu_domain, struct list_head *list)
+{
+ struct page *page, *tmp;
+ u32 remaining = 0;
+
+ list_for_each_entry_safe(page, tmp, list, lru) {
+ list_del(&page->lru);
+ __free_pages(page, 0);
+ remaining++;
+ }
+}
+
static int arm_smmu_attach_dev(struct iommu_domain *domain, struct device *dev)
{
int ret;
@@ -2589,10 +2647,12 @@
unsigned int idx_start, idx_end;
struct scatterlist *sg_start, *sg_end;
unsigned long __saved_iova_start;
+ LIST_HEAD(nonsecure_pool);
if (!ops)
return -ENODEV;
+ arm_smmu_prealloc_memory(smmu_domain, sg, nents, &nonsecure_pool);
arm_smmu_secure_domain_lock(smmu_domain);
__saved_iova_start = iova;
@@ -2611,8 +2671,10 @@
}
spin_lock_irqsave(&smmu_domain->pgtbl_lock, flags);
+ list_splice_init(&nonsecure_pool, &smmu_domain->nonsecure_pool);
ret = ops->map_sg(ops, iova, sg_start, idx_end - idx_start,
prot, &size);
+ list_splice_init(&smmu_domain->nonsecure_pool, &nonsecure_pool);
spin_unlock_irqrestore(&smmu_domain->pgtbl_lock, flags);
/* Returns 0 on error */
if (!ret) {
@@ -2633,6 +2695,7 @@
iova = __saved_iova_start;
}
arm_smmu_secure_domain_unlock(smmu_domain);
+ arm_smmu_release_prealloc_memory(smmu_domain, &nonsecure_pool);
return iova - __saved_iova_start;
}
@@ -2780,6 +2843,9 @@
if (!smmu)
return true;
+ if (!arm_smmu_is_static_cb(smmu))
+ return false;
+
/* Do not write to global space */
if (((unsigned long)addr & (smmu->size - 1)) < (smmu->size >> 1))
return true;
@@ -4454,6 +4520,11 @@
goto out_exit_power_resources;
smmu->sec_id = msm_dev_to_device_id(dev);
+ INIT_LIST_HEAD(&smmu->list);
+ spin_lock(&arm_smmu_devices_lock);
+ list_add(&smmu->list, &arm_smmu_devices);
+ spin_unlock(&arm_smmu_devices_lock);
+
err = arm_smmu_device_cfg_probe(smmu);
if (err)
goto out_power_off;
@@ -4496,11 +4567,6 @@
arm_smmu_device_reset(smmu);
arm_smmu_power_off(smmu->pwr);
- INIT_LIST_HEAD(&smmu->list);
- spin_lock(&arm_smmu_devices_lock);
- list_add(&smmu->list, &arm_smmu_devices);
- spin_unlock(&arm_smmu_devices_lock);
-
/* bus_set_iommu depends on this. */
bus_for_each_dev(&platform_bus_type, NULL, NULL,
arm_smmu_of_iommu_configure_fixup);
@@ -4530,6 +4596,9 @@
out_power_off:
arm_smmu_power_off(smmu->pwr);
+ spin_lock(&arm_smmu_devices_lock);
+ list_del(&smmu->list);
+ spin_unlock(&arm_smmu_devices_lock);
out_exit_power_resources:
arm_smmu_exit_power_resources(smmu->pwr);
diff --git a/drivers/iommu/msm_dma_iommu_mapping.c b/drivers/iommu/msm_dma_iommu_mapping.c
index 3f739a2..180edf3 100644
--- a/drivers/iommu/msm_dma_iommu_mapping.c
+++ b/drivers/iommu/msm_dma_iommu_mapping.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2015-2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2015-2018, 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
@@ -183,7 +183,7 @@
mutex_lock(&iommu_meta->lock);
iommu_map = msm_iommu_lookup(iommu_meta, dev);
if (!iommu_map) {
- iommu_map = kmalloc(sizeof(*iommu_map), GFP_ATOMIC);
+ iommu_map = kmalloc(sizeof(*iommu_map), GFP_KERNEL);
if (!iommu_map) {
ret = -ENOMEM;
diff --git a/drivers/media/platform/msm/camera/cam_core/cam_context.c b/drivers/media/platform/msm/camera/cam_core/cam_context.c
index d1222aa..98fff48 100644
--- a/drivers/media/platform/msm/camera/cam_core/cam_context.c
+++ b/drivers/media/platform/msm/camera/cam_core/cam_context.c
@@ -224,6 +224,7 @@
struct cam_acquire_dev_cmd *cmd)
{
int rc;
+ int i;
if (!ctx->state_machine) {
CAM_ERR(CAM_CORE, "Context is not ready");
@@ -244,6 +245,17 @@
cmd->dev_handle, ctx->state);
rc = -EPROTO;
}
+
+ INIT_LIST_HEAD(&ctx->active_req_list);
+ INIT_LIST_HEAD(&ctx->wait_req_list);
+ INIT_LIST_HEAD(&ctx->pending_req_list);
+ INIT_LIST_HEAD(&ctx->free_req_list);
+
+ for (i = 0; i < ctx->req_size; i++) {
+ INIT_LIST_HEAD(&ctx->req_list[i].list);
+ list_add_tail(&ctx->req_list[i].list, &ctx->free_req_list);
+ }
+
mutex_unlock(&ctx->ctx_mutex);
return rc;
@@ -394,6 +406,8 @@
int cam_context_init(struct cam_context *ctx,
const char *dev_name,
+ uint64_t dev_id,
+ uint32_t ctx_id,
struct cam_req_mgr_kmd_ops *crm_node_intf,
struct cam_hw_mgr_intf *hw_mgr_intf,
struct cam_ctx_request *req_list,
@@ -417,6 +431,8 @@
spin_lock_init(&ctx->lock);
ctx->dev_name = dev_name;
+ ctx->dev_id = dev_id;
+ ctx->ctx_id = ctx_id;
ctx->ctx_crm_intf = NULL;
ctx->crm_ctx_intf = crm_node_intf;
ctx->hw_mgr_intf = hw_mgr_intf;
diff --git a/drivers/media/platform/msm/camera/cam_core/cam_context.h b/drivers/media/platform/msm/camera/cam_core/cam_context.h
index af92b7e..8324e78 100644
--- a/drivers/media/platform/msm/camera/cam_core/cam_context.h
+++ b/drivers/media/platform/msm/camera/cam_core/cam_context.h
@@ -147,6 +147,8 @@
* struct cam_context - camera context object for the subdevice node
*
* @dev_name: String giving name of device associated
+ * @dev_id: ID of device associated
+ * @ctx_id: ID for this context
* @list: Link list entry
* @sessoin_hdl: Session handle
* @dev_hdl: Device handle
@@ -174,6 +176,8 @@
*/
struct cam_context {
const char *dev_name;
+ uint64_t dev_id;
+ uint32_t ctx_id;
struct list_head list;
int32_t session_hdl;
int32_t dev_hdl;
@@ -376,6 +380,8 @@
*
* @ctx: Object pointer for cam_context
* @dev_name: String giving name of device associated
+ * @dev_id: ID of the device associated
+ * @ctx_id: ID for this context
* @crm_node_intf: Function table for crm to context interface
* @hw_mgr_intf: Function table for context to hw interface
* @req_list: Requests storage
@@ -384,6 +390,8 @@
*/
int cam_context_init(struct cam_context *ctx,
const char *dev_name,
+ uint64_t dev_id,
+ uint32_t ctx_id,
struct cam_req_mgr_kmd_ops *crm_node_intf,
struct cam_hw_mgr_intf *hw_mgr_intf,
struct cam_ctx_request *req_list,
diff --git a/drivers/media/platform/msm/camera/cam_core/cam_context_utils.c b/drivers/media/platform/msm/camera/cam_core/cam_context_utils.c
index 8ea920d..b85f00b 100644
--- a/drivers/media/platform/msm/camera/cam_core/cam_context_utils.c
+++ b/drivers/media/platform/msm/camera/cam_core/cam_context_utils.c
@@ -26,6 +26,9 @@
#include "cam_trace.h"
#include "cam_debug_util.h"
+static uint cam_debug_ctx_req_list;
+module_param(cam_debug_ctx_req_list, uint, 0644);
+
static inline int cam_context_validate_thread(void)
{
if (in_interrupt()) {
@@ -56,7 +59,8 @@
spin_lock(&ctx->lock);
if (list_empty(&ctx->active_req_list)) {
- CAM_ERR(CAM_CTXT, "no active request");
+ CAM_ERR(CAM_CTXT, "[%s][%d] no active request",
+ ctx->dev_name, ctx->ctx_id);
spin_unlock(&ctx->lock);
return -EIO;
}
@@ -66,14 +70,17 @@
trace_cam_buf_done("UTILS", ctx, req);
if (done->request_id != req->request_id) {
- CAM_ERR(CAM_CTXT, "mismatch: done req[%lld], active req[%lld]",
+ CAM_ERR(CAM_CTXT,
+ "[%s][%d] mismatch: done req[%lld], active req[%lld]",
+ ctx->dev_name, ctx->ctx_id,
done->request_id, req->request_id);
spin_unlock(&ctx->lock);
return -EIO;
}
if (!req->num_out_map_entries) {
- CAM_ERR(CAM_CTXT, "no output fence to signal");
+ CAM_ERR(CAM_CTXT, "[%s][%d] no output fence to signal",
+ ctx->dev_name, ctx->ctx_id);
spin_unlock(&ctx->lock);
return -EIO;
}
@@ -94,6 +101,11 @@
req->out_map_entries[j].sync_id = -1;
}
+ if (cam_debug_ctx_req_list & ctx->dev_id)
+ CAM_INFO(CAM_CTXT,
+ "[%s][%d] : Moving req[%llu] from active_list to free_list",
+ ctx->dev_name, ctx->ctx_id, req->request_id);
+
/*
* another thread may be adding/removing from free list,
* so hold the lock
@@ -114,7 +126,8 @@
struct cam_hw_config_args cfg;
if (!ctx->hw_mgr_intf) {
- CAM_ERR(CAM_CTXT, "HW interface is not ready");
+ CAM_ERR(CAM_CTXT, "[%s][%d] HW interface is not ready",
+ ctx->dev_name, ctx->ctx_id);
rc = -EFAULT;
goto end;
}
@@ -124,6 +137,11 @@
list_add_tail(&req->list, &ctx->active_req_list);
spin_unlock(&ctx->lock);
+ if (cam_debug_ctx_req_list & ctx->dev_id)
+ CAM_INFO(CAM_CTXT,
+ "[%s][%d] : Moving req[%llu] from pending_list to active_list",
+ ctx->dev_name, ctx->ctx_id, req->request_id);
+
cfg.ctxt_to_hw_map = ctx->ctxt_to_hw_map;
cfg.hw_update_entries = req->hw_update_entries;
cfg.num_hw_update_entries = req->num_hw_update_entries;
@@ -135,7 +153,13 @@
if (rc) {
spin_lock(&ctx->lock);
list_del_init(&req->list);
+ list_add_tail(&req->list, &ctx->free_req_list);
spin_unlock(&ctx->lock);
+
+ if (cam_debug_ctx_req_list & ctx->dev_id)
+ CAM_INFO(CAM_CTXT,
+ "[%s][%d] : Moving req[%llu] from active_list to free_list",
+ ctx->dev_name, ctx->ctx_id, req->request_id);
}
end:
@@ -159,6 +183,11 @@
return;
ctx = req->ctx;
+ if (!ctx) {
+ CAM_ERR(CAM_CTXT, "Invalid ctx for req %llu", req->request_id);
+ return;
+ }
+
req->num_in_acked++;
if (req->num_in_acked == req->num_in_map_entries) {
apply.request_id = req->request_id;
@@ -174,8 +203,6 @@
CAM_DBG(CAM_CTXT, "fence error: %d", sync_obj);
flush_cmd.req_id = req->request_id;
cam_context_flush_req_to_hw(ctx, &flush_cmd);
- cam_context_putref(ctx);
- return;
}
mutex_lock(&ctx->sync_mutex);
@@ -190,6 +217,12 @@
list_del_init(&req->list);
list_add_tail(&req->list, &ctx->free_req_list);
spin_unlock(&ctx->lock);
+
+ if (cam_debug_ctx_req_list & ctx->dev_id)
+ CAM_INFO(CAM_CTXT,
+ "[%s][%d] : Moving req[%llu] from pending_list to free_list",
+ ctx->dev_name, ctx->ctx_id,
+ req->request_id);
}
}
cam_context_putref(ctx);
@@ -206,7 +239,8 @@
}
if ((!ctx->hw_mgr_intf) || (!ctx->hw_mgr_intf->hw_release)) {
- CAM_ERR(CAM_CTXT, "HW interface is not ready");
+ CAM_ERR(CAM_CTXT, "[%s][%d] HW interface is not ready",
+ ctx->dev_name, ctx->ctx_id);
return -EINVAL;
}
@@ -241,7 +275,8 @@
}
if (!ctx->hw_mgr_intf) {
- CAM_ERR(CAM_CTXT, "HW interface is not ready");
+ CAM_ERR(CAM_CTXT, "[%s][%d] HW interface is not ready",
+ ctx->dev_name, ctx->ctx_id);
rc = -EFAULT;
goto end;
}
@@ -258,7 +293,8 @@
spin_unlock(&ctx->lock);
if (!req) {
- CAM_ERR(CAM_CTXT, "No more request obj free");
+ CAM_ERR(CAM_CTXT, "[%s][%d] No more request obj free",
+ ctx->dev_name, ctx->ctx_id);
rc = -ENOMEM;
goto end;
}
@@ -273,7 +309,8 @@
(uint64_t *) &packet_addr,
&len);
if (rc != 0) {
- CAM_ERR(CAM_CTXT, "Can not get packet address");
+ CAM_ERR(CAM_CTXT, "[%s][%d] Can not get packet address",
+ ctx->dev_name, ctx->ctx_id);
rc = -EINVAL;
goto free_req;
}
@@ -295,7 +332,9 @@
rc = ctx->hw_mgr_intf->hw_prepare_update(
ctx->hw_mgr_intf->hw_mgr_priv, &cfg);
if (rc != 0) {
- CAM_ERR(CAM_CTXT, "Prepare config packet failed in HW layer");
+ CAM_ERR(CAM_CTXT,
+ "[%s][%d] Prepare config packet failed in HW layer",
+ ctx->dev_name, ctx->ctx_id);
rc = -EFAULT;
goto free_req;
}
@@ -310,6 +349,12 @@
spin_lock(&ctx->lock);
list_add_tail(&req->list, &ctx->pending_req_list);
spin_unlock(&ctx->lock);
+
+ if (cam_debug_ctx_req_list & ctx->dev_id)
+ CAM_INFO(CAM_CTXT,
+ "[%s][%d] : Moving req[%llu] from free_list to pending_list",
+ ctx->dev_name, ctx->ctx_id, req->request_id);
+
for (i = 0; i < req->num_in_map_entries; i++) {
cam_context_getref(ctx);
rc = cam_sync_register_callback(
@@ -318,9 +363,21 @@
req->in_map_entries[i].sync_id);
if (rc) {
CAM_ERR(CAM_CTXT,
- "Failed register fence cb: %d ret = %d",
+ "[%s][%d] Failed register fence cb: %d ret = %d",
+ ctx->dev_name, ctx->ctx_id,
req->in_map_entries[i].sync_id, rc);
+ spin_lock(&ctx->lock);
+ list_del_init(&req->list);
+ spin_unlock(&ctx->lock);
+
+ if (cam_debug_ctx_req_list & ctx->dev_id)
+ CAM_INFO(CAM_CTXT,
+ "[%s][%d] : Moving req[%llu] from pending_list to free_list",
+ ctx->dev_name, ctx->ctx_id,
+ req->request_id);
+
cam_context_putref(ctx);
+
goto free_req;
}
CAM_DBG(CAM_CTXT, "register in fence cb: %d ret = %d",
@@ -355,7 +412,8 @@
}
if (!ctx->hw_mgr_intf) {
- CAM_ERR(CAM_CTXT, "HW interface is not ready");
+ CAM_ERR(CAM_CTXT, "[%s][%d] HW interface is not ready",
+ ctx->dev_name, ctx->ctx_id);
rc = -EFAULT;
goto end;
}
@@ -365,14 +423,16 @@
cmd->resource_hdl);
if (cmd->num_resources > CAM_CTX_RES_MAX) {
- CAM_ERR(CAM_CTXT, "resource limit exceeded");
+ CAM_ERR(CAM_CTXT, "[%s][%d] resource limit exceeded",
+ ctx->dev_name, ctx->ctx_id);
rc = -ENOMEM;
goto end;
}
/* for now we only support user pointer */
if (cmd->handle_type != 1) {
- CAM_ERR(CAM_CTXT, "Only user pointer is supported");
+ CAM_ERR(CAM_CTXT, "[%s][%d] Only user pointer is supported",
+ ctx->dev_name, ctx->ctx_id);
rc = -EINVAL;
goto end;
}
@@ -387,7 +447,8 @@
rc = ctx->hw_mgr_intf->hw_acquire(ctx->hw_mgr_intf->hw_mgr_priv,
¶m);
if (rc != 0) {
- CAM_ERR(CAM_CTXT, "Acquire device failed");
+ CAM_ERR(CAM_CTXT, "[%s][%d] Acquire device failed",
+ ctx->dev_name, ctx->ctx_id);
goto end;
}
@@ -404,7 +465,8 @@
ctx->dev_hdl = cam_create_device_hdl(&req_hdl_param);
if (ctx->dev_hdl <= 0) {
rc = -EFAULT;
- CAM_ERR(CAM_CTXT, "Can not create device handle");
+ CAM_ERR(CAM_CTXT, "[%s][%d] Can not create device handle",
+ ctx->dev_name, ctx->ctx_id);
goto free_hw;
}
cmd->dev_handle = ctx->dev_hdl;
@@ -430,7 +492,7 @@
uint32_t i;
int rc = 0;
- CAM_DBG(CAM_CTXT, "E: NRT flush ctx");
+ CAM_DBG(CAM_CTXT, "[%s] E: NRT flush ctx", ctx->dev_name);
/*
* flush pending requests, take the sync lock to synchronize with the
@@ -442,17 +504,30 @@
spin_lock(&ctx->lock);
list_splice_init(&ctx->pending_req_list, &temp_list);
spin_unlock(&ctx->lock);
+
+ if (cam_debug_ctx_req_list & ctx->dev_id)
+ CAM_INFO(CAM_CTXT,
+ "[%s][%d] : Moving all pending requests from pending_list to temp_list",
+ ctx->dev_name, ctx->ctx_id);
+
flush_args.num_req_pending = 0;
- while (!list_empty(&temp_list)) {
+ while (true) {
+ spin_lock(&ctx->lock);
+ if (list_empty(&temp_list)) {
+ spin_unlock(&ctx->lock);
+ break;
+ }
+
req = list_first_entry(&temp_list,
struct cam_ctx_request, list);
list_del_init(&req->list);
+ spin_unlock(&ctx->lock);
req->flushed = 1;
flush_args.flush_req_pending[flush_args.num_req_pending++] =
req->req_priv;
- for (i = 0; i < req->num_out_map_entries; i++)
+ for (i = 0; i < req->num_out_map_entries; i++) {
if (req->out_map_entries[i].sync_id != -1) {
rc = cam_sync_signal(
req->out_map_entries[i].sync_id,
@@ -466,6 +541,12 @@
break;
}
}
+ }
+
+ if (cam_debug_ctx_req_list & ctx->dev_id)
+ CAM_INFO(CAM_CTXT,
+ "[%s][%d] : Deleting req[%llu] from temp_list",
+ ctx->dev_name, ctx->ctx_id, req->request_id);
}
mutex_unlock(&ctx->sync_mutex);
@@ -492,10 +573,22 @@
INIT_LIST_HEAD(&ctx->active_req_list);
spin_unlock(&ctx->lock);
- while (!list_empty(&temp_list)) {
+ if (cam_debug_ctx_req_list & ctx->dev_id)
+ CAM_INFO(CAM_CTXT,
+ "[%s][%d] : Moving all requests from active_list to temp_list",
+ ctx->dev_name, ctx->ctx_id);
+
+ while (true) {
+ spin_lock(&ctx->lock);
+ if (list_empty(&temp_list)) {
+ spin_unlock(&ctx->lock);
+ break;
+ }
req = list_first_entry(&temp_list,
struct cam_ctx_request, list);
list_del_init(&req->list);
+ spin_unlock(&ctx->lock);
+
for (i = 0; i < req->num_out_map_entries; i++) {
if (req->out_map_entries[i].sync_id != -1) {
rc = cam_sync_signal(
@@ -517,9 +610,14 @@
list_add_tail(&req->list, &ctx->free_req_list);
spin_unlock(&ctx->lock);
req->ctx = NULL;
+
+ if (cam_debug_ctx_req_list & ctx->dev_id)
+ CAM_INFO(CAM_CTXT,
+ "[%s][%d] : Moving req[%llu] from temp_list to free_list",
+ ctx->dev_name, ctx->ctx_id, req->request_id);
}
- CAM_DBG(CAM_CTXT, "X: NRT flush ctx");
+ CAM_DBG(CAM_CTXT, "[%s] X: NRT flush ctx", ctx->dev_name);
return 0;
}
@@ -532,7 +630,7 @@
uint32_t i;
int rc = 0;
- CAM_DBG(CAM_CTXT, "E: NRT flush req");
+ CAM_DBG(CAM_CTXT, "[%s] E: NRT flush req", ctx->dev_name);
flush_args.num_req_pending = 0;
flush_args.num_req_active = 0;
@@ -542,6 +640,11 @@
if (req->request_id != cmd->req_id)
continue;
+ if (cam_debug_ctx_req_list & ctx->dev_id)
+ CAM_INFO(CAM_CTXT,
+ "[%s][%d] : Deleting req[%llu] from pending_list",
+ ctx->dev_name, ctx->ctx_id, req->request_id);
+
list_del_init(&req->list);
req->flushed = 1;
@@ -598,10 +701,16 @@
list_add_tail(&req->list, &ctx->free_req_list);
spin_unlock(&ctx->lock);
req->ctx = NULL;
+
+ if (cam_debug_ctx_req_list & ctx->dev_id)
+ CAM_INFO(CAM_CTXT,
+ "[%s][%d] : Moving req[%llu] from active_list to free_list",
+ ctx->dev_name, ctx->ctx_id,
+ req->request_id);
}
}
}
- CAM_DBG(CAM_CTXT, "X: NRT flush req");
+ CAM_DBG(CAM_CTXT, "[%s] X: NRT flush req", ctx->dev_name);
return 0;
}
@@ -619,7 +728,8 @@
}
if (!ctx->hw_mgr_intf) {
- CAM_ERR(CAM_CTXT, "HW interface is not ready");
+ CAM_ERR(CAM_CTXT, "[%s][%d] HW interface is not ready",
+ ctx->dev_name, ctx->ctx_id);
rc = -EFAULT;
goto end;
}
@@ -630,7 +740,8 @@
rc = cam_context_flush_req_to_hw(ctx, cmd);
else {
rc = -EINVAL;
- CAM_ERR(CAM_CORE, "Invalid flush type %d", cmd->flush_type);
+ CAM_ERR(CAM_CORE, "[%s][%d] Invalid flush type %d",
+ ctx->dev_name, ctx->ctx_id, cmd->flush_type);
}
end:
@@ -650,14 +761,17 @@
}
if (!ctx->hw_mgr_intf) {
- CAM_ERR(CAM_CTXT, "HW interface is not ready");
+ CAM_ERR(CAM_CTXT, "[%s][%d] HW interface is not ready",
+ ctx->dev_name, ctx->ctx_id);
rc = -EFAULT;
goto end;
}
if ((cmd->session_handle != ctx->session_hdl) ||
(cmd->dev_handle != ctx->dev_hdl)) {
- CAM_ERR(CAM_CTXT, "Invalid session hdl[%d], dev_handle[%d]",
+ CAM_ERR(CAM_CTXT,
+ "[%s][%d] Invalid session hdl[%d], dev_handle[%d]",
+ ctx->dev_name, ctx->ctx_id,
cmd->session_handle, cmd->dev_handle);
rc = -EPERM;
goto end;
@@ -669,7 +783,8 @@
&arg);
if (rc) {
/* HW failure. user need to clean up the resource */
- CAM_ERR(CAM_CTXT, "Start HW failed");
+ CAM_ERR(CAM_CTXT, "[%s][%d] Start HW failed",
+ ctx->dev_name, ctx->ctx_id);
goto end;
}
}
@@ -690,7 +805,8 @@
}
if (!ctx->hw_mgr_intf) {
- CAM_ERR(CAM_CTXT, "HW interface is not ready");
+ CAM_ERR(CAM_CTXT, "[%s][%d] HW interface is not ready",
+ ctx->dev_name, ctx->ctx_id);
rc = -EFAULT;
goto end;
}
@@ -699,11 +815,9 @@
if (rc)
goto end;
- if (ctx->ctxt_to_hw_map) {
- rc = cam_context_flush_ctx_to_hw(ctx);
- if (rc)
- goto end;
- }
+ rc = cam_context_flush_ctx_to_hw(ctx);
+ if (rc)
+ goto end;
/* stop hw first */
if (ctx->hw_mgr_intf->hw_stop) {
diff --git a/drivers/media/platform/msm/camera/cam_fd/cam_fd_context.c b/drivers/media/platform/msm/camera/cam_fd/cam_fd_context.c
index 04d65dd..99c509c 100644
--- a/drivers/media/platform/msm/camera/cam_fd/cam_fd_context.c
+++ b/drivers/media/platform/msm/camera/cam_fd/cam_fd_context.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2017-2018, 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
@@ -211,7 +211,8 @@
int cam_fd_context_init(struct cam_fd_context *fd_ctx,
- struct cam_context *base_ctx, struct cam_hw_mgr_intf *hw_intf)
+ struct cam_context *base_ctx, struct cam_hw_mgr_intf *hw_intf,
+ uint32_t ctx_id)
{
int rc;
@@ -222,8 +223,8 @@
memset(fd_ctx, 0, sizeof(*fd_ctx));
- rc = cam_context_init(base_ctx, fd_dev_name, NULL, hw_intf,
- fd_ctx->req_base, CAM_CTX_REQ_MAX);
+ rc = cam_context_init(base_ctx, fd_dev_name, CAM_FD, ctx_id,
+ NULL, hw_intf, fd_ctx->req_base, CAM_CTX_REQ_MAX);
if (rc) {
CAM_ERR(CAM_FD, "Camera Context Base init failed, rc=%d", rc);
return rc;
diff --git a/drivers/media/platform/msm/camera/cam_fd/cam_fd_context.h b/drivers/media/platform/msm/camera/cam_fd/cam_fd_context.h
index 6aa5edb..a8b5d15 100644
--- a/drivers/media/platform/msm/camera/cam_fd/cam_fd_context.h
+++ b/drivers/media/platform/msm/camera/cam_fd/cam_fd_context.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2017-2018, 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
@@ -30,7 +30,8 @@
};
int cam_fd_context_init(struct cam_fd_context *fd_ctx,
- struct cam_context *base_ctx, struct cam_hw_mgr_intf *hw_intf);
+ struct cam_context *base_ctx, struct cam_hw_mgr_intf *hw_intf,
+ uint32_t ctx_id);
int cam_fd_context_deinit(struct cam_fd_context *ctx);
#endif /* _CAM_FD_CONTEXT_H_ */
diff --git a/drivers/media/platform/msm/camera/cam_fd/cam_fd_dev.c b/drivers/media/platform/msm/camera/cam_fd/cam_fd_dev.c
index 260dcd8..3f01244 100644
--- a/drivers/media/platform/msm/camera/cam_fd/cam_fd_dev.c
+++ b/drivers/media/platform/msm/camera/cam_fd/cam_fd_dev.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2017-2018, 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
@@ -121,7 +121,7 @@
for (i = 0; i < CAM_CTX_MAX; i++) {
rc = cam_fd_context_init(&g_fd_dev.fd_ctx[i],
- &g_fd_dev.base_ctx[i], &node->hw_mgr_intf);
+ &g_fd_dev.base_ctx[i], &node->hw_mgr_intf, i);
if (rc) {
CAM_ERR(CAM_FD, "FD context init failed i=%d, rc=%d",
i, rc);
diff --git a/drivers/media/platform/msm/camera/cam_icp/cam_icp_context.c b/drivers/media/platform/msm/camera/cam_icp/cam_icp_context.c
index d47350c..502c95d 100644
--- a/drivers/media/platform/msm/camera/cam_icp/cam_icp_context.c
+++ b/drivers/media/platform/msm/camera/cam_icp/cam_icp_context.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2017-2018, 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
@@ -177,7 +177,7 @@
};
int cam_icp_context_init(struct cam_icp_context *ctx,
- struct cam_hw_mgr_intf *hw_intf)
+ struct cam_hw_mgr_intf *hw_intf, uint32_t ctx_id)
{
int rc;
@@ -187,8 +187,8 @@
goto err;
}
- rc = cam_context_init(ctx->base, icp_dev_name, NULL, hw_intf,
- ctx->req_base, CAM_CTX_REQ_MAX);
+ rc = cam_context_init(ctx->base, icp_dev_name, CAM_ICP, ctx_id,
+ NULL, hw_intf, ctx->req_base, CAM_CTX_REQ_MAX);
if (rc) {
CAM_ERR(CAM_ICP, "Camera Context Base init failed");
goto err;
diff --git a/drivers/media/platform/msm/camera/cam_icp/cam_icp_context.h b/drivers/media/platform/msm/camera/cam_icp/cam_icp_context.h
index 709fc56..0c3a360 100644
--- a/drivers/media/platform/msm/camera/cam_icp/cam_icp_context.h
+++ b/drivers/media/platform/msm/camera/cam_icp/cam_icp_context.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2017-2018, 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
@@ -35,9 +35,10 @@
* cam_icp_context_init() - ICP context init
* @ctx: Pointer to context
* @hw_intf: Pointer to ICP hardware interface
+ * @ctx_id: ID for this context
*/
int cam_icp_context_init(struct cam_icp_context *ctx,
- struct cam_hw_mgr_intf *hw_intf);
+ struct cam_hw_mgr_intf *hw_intf, uint32_t ctx_id);
/**
* cam_icp_context_deinit() - ICP context deinit
diff --git a/drivers/media/platform/msm/camera/cam_icp/cam_icp_subdev.c b/drivers/media/platform/msm/camera/cam_icp/cam_icp_subdev.c
index 51499de..4f91f73 100644
--- a/drivers/media/platform/msm/camera/cam_icp/cam_icp_subdev.c
+++ b/drivers/media/platform/msm/camera/cam_icp/cam_icp_subdev.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2017-2018, 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
@@ -167,7 +167,7 @@
for (i = 0; i < CAM_ICP_CTX_MAX; i++) {
g_icp_dev.ctx_icp[i].base = &g_icp_dev.ctx[i];
rc = cam_icp_context_init(&g_icp_dev.ctx_icp[i],
- hw_mgr_intf);
+ hw_mgr_intf, i);
if (rc) {
CAM_ERR(CAM_ICP, "ICP context init failed");
goto ctx_fail;
diff --git a/drivers/media/platform/msm/camera/cam_icp/icp_hw/icp_hw_mgr/cam_icp_hw_mgr.c b/drivers/media/platform/msm/camera/cam_icp/icp_hw/icp_hw_mgr/cam_icp_hw_mgr.c
index 5dfb1bc..1550a48 100644
--- a/drivers/media/platform/msm/camera/cam_icp/icp_hw/icp_hw_mgr/cam_icp_hw_mgr.c
+++ b/drivers/media/platform/msm/camera/cam_icp/icp_hw/icp_hw_mgr/cam_icp_hw_mgr.c
@@ -1314,6 +1314,32 @@
return rc;
}
+static int cam_icp_mgr_process_cmd_and_free_mem(void *priv, void *data)
+{
+ int rc;
+ struct hfi_cmd_work_data *task_data = NULL;
+ struct cam_icp_hw_mgr *hw_mgr;
+
+ if (!data || !priv) {
+ CAM_ERR(CAM_ICP, "Invalid params%pK %pK", data, priv);
+ return -EINVAL;
+ }
+
+ hw_mgr = priv;
+ task_data = (struct hfi_cmd_work_data *)data;
+
+ if (!task_data->data) {
+ CAM_ERR(CAM_ICP, "Invalid data");
+ return -EINVAL;
+ }
+
+ rc = hfi_write_cmd(task_data->data);
+
+ kfree(task_data->data);
+ task_data->data = NULL;
+ return rc;
+}
+
static int cam_icp_mgr_process_cmd(void *priv, void *data)
{
int rc;
@@ -2188,14 +2214,14 @@
task_data->data = (void *)abort_cmd;
task_data->request_id = 0;
task_data->type = ICP_WORKQ_TASK_CMD_TYPE;
- task->process_cb = cam_icp_mgr_process_cmd;
+ task->process_cb = cam_icp_mgr_process_cmd_and_free_mem;
rc = cam_req_mgr_workq_enqueue_task(task, &icp_hw_mgr,
CRM_TASK_PRIORITY_0);
if (rc) {
kfree(abort_cmd);
return rc;
}
-
+ CAM_DBG(CAM_ICP, "payload data = %pK", task_data->data);
CAM_DBG(CAM_ICP, "fw_handle = %x ctx_data = %pK",
ctx_data->fw_handle, ctx_data);
rem_jiffies = wait_for_completion_timeout(&ctx_data->wait_complete,
@@ -2205,7 +2231,6 @@
CAM_ERR(CAM_ICP, "FW timeout/err in abort handle command");
}
- kfree(abort_cmd);
return rc;
}
@@ -2253,14 +2278,14 @@
task_data->data = (void *)destroy_cmd;
task_data->request_id = 0;
task_data->type = ICP_WORKQ_TASK_CMD_TYPE;
- task->process_cb = cam_icp_mgr_process_cmd;
+ task->process_cb = cam_icp_mgr_process_cmd_and_free_mem;
rc = cam_req_mgr_workq_enqueue_task(task, &icp_hw_mgr,
CRM_TASK_PRIORITY_0);
if (rc) {
kfree(destroy_cmd);
return rc;
}
-
+ CAM_DBG(CAM_ICP, "payload data = %pK", task_data->data);
CAM_DBG(CAM_ICP, "fw_handle = %x ctx_data = %pK",
ctx_data->fw_handle, ctx_data);
rem_jiffies = wait_for_completion_timeout(&ctx_data->wait_complete,
@@ -2272,8 +2297,6 @@
if (icp_hw_mgr.a5_debug_q)
cam_icp_mgr_process_dbg_buf();
}
-
- kfree(destroy_cmd);
return rc;
}
@@ -3537,7 +3560,7 @@
static int cam_icp_mgr_send_ping(struct cam_icp_hw_ctx_data *ctx_data)
{
- struct hfi_cmd_ping_pkt ping_pkt;
+ struct hfi_cmd_ping_pkt *ping_pkt;
struct hfi_cmd_work_data *task_data;
unsigned long rem_jiffies;
int timeout = 5000;
@@ -3550,20 +3573,28 @@
return -ENOMEM;
}
- ping_pkt.size = sizeof(struct hfi_cmd_ping_pkt);
- ping_pkt.pkt_type = HFI_CMD_SYS_PING;
- ping_pkt.user_data = (uint64_t)ctx_data;
+ ping_pkt = kzalloc(sizeof(struct hfi_cmd_ping_pkt), GFP_KERNEL);
+ if (!ping_pkt) {
+ rc = -ENOMEM;
+ return rc;
+ }
+ ping_pkt->size = sizeof(struct hfi_cmd_ping_pkt);
+ ping_pkt->pkt_type = HFI_CMD_SYS_PING;
+ ping_pkt->user_data = (uint64_t)ctx_data;
init_completion(&ctx_data->wait_complete);
task_data = (struct hfi_cmd_work_data *)task->payload;
- task_data->data = (void *)&ping_pkt;
+ task_data->data = (void *)ping_pkt;
task_data->request_id = 0;
task_data->type = ICP_WORKQ_TASK_CMD_TYPE;
- task->process_cb = cam_icp_mgr_process_cmd;
+ task->process_cb = cam_icp_mgr_process_cmd_and_free_mem;
rc = cam_req_mgr_workq_enqueue_task(task, &icp_hw_mgr,
CRM_TASK_PRIORITY_0);
- if (rc)
+ if (rc) {
+ kfree(ping_pkt);
return rc;
+ }
+ CAM_DBG(CAM_ICP, "payload data = %pK", task_data->data);
rem_jiffies = wait_for_completion_timeout(&ctx_data->wait_complete,
msecs_to_jiffies((timeout)));
diff --git a/drivers/media/platform/msm/camera/cam_isp/cam_isp_context.c b/drivers/media/platform/msm/camera/cam_isp/cam_isp_context.c
index d62344d..6a294b2 100644
--- a/drivers/media/platform/msm/camera/cam_isp/cam_isp_context.c
+++ b/drivers/media/platform/msm/camera/cam_isp/cam_isp_context.c
@@ -1145,7 +1145,9 @@
struct cam_ctx_request *req;
struct cam_ctx_request *req_temp;
struct cam_isp_ctx_req *req_isp;
+ struct list_head flush_list;
+ INIT_LIST_HEAD(&flush_list);
spin_lock_bh(&ctx->lock);
if (list_empty(req_list)) {
spin_unlock_bh(&ctx->lock);
@@ -1154,11 +1156,22 @@
}
list_for_each_entry_safe(req, req_temp, req_list, list) {
- if ((flush_req->type == CAM_REQ_MGR_FLUSH_TYPE_CANCEL_REQ)
- && (req->request_id != flush_req->req_id))
- continue;
-
+ if (flush_req->type == CAM_REQ_MGR_FLUSH_TYPE_CANCEL_REQ) {
+ if (req->request_id != flush_req->req_id) {
+ continue;
+ } else {
+ list_del_init(&req->list);
+ list_add_tail(&req->list, &flush_list);
+ cancel_req_id_found = 1;
+ break;
+ }
+ }
list_del_init(&req->list);
+ list_add_tail(&req->list, &flush_list);
+ }
+ spin_unlock_bh(&ctx->lock);
+
+ list_for_each_entry_safe(req, req_temp, &flush_list, list) {
req_isp = (struct cam_isp_ctx_req *) req->req_priv;
for (i = 0; i < req_isp->num_fence_map_out; i++) {
if (req_isp->fence_map_out[i].sync_id != -1) {
@@ -1175,14 +1188,7 @@
}
}
list_add_tail(&req->list, &ctx->free_req_list);
-
- /* If flush request id found, exit the loop */
- if (flush_req->type == CAM_REQ_MGR_FLUSH_TYPE_CANCEL_REQ) {
- cancel_req_id_found = 1;
- break;
- }
}
- spin_unlock_bh(&ctx->lock);
if (flush_req->type == CAM_REQ_MGR_FLUSH_TYPE_CANCEL_REQ &&
!cancel_req_id_found)
@@ -2498,7 +2504,8 @@
int cam_isp_context_init(struct cam_isp_context *ctx,
struct cam_context *ctx_base,
struct cam_req_mgr_kmd_ops *crm_node_intf,
- struct cam_hw_mgr_intf *hw_intf)
+ struct cam_hw_mgr_intf *hw_intf,
+ uint32_t ctx_id)
{
int rc = -1;
@@ -2527,8 +2534,8 @@
}
/* camera context setup */
- rc = cam_context_init(ctx_base, isp_dev_name, crm_node_intf, hw_intf,
- ctx->req_base, CAM_CTX_REQ_MAX);
+ rc = cam_context_init(ctx_base, isp_dev_name, CAM_ISP, ctx_id,
+ crm_node_intf, hw_intf, ctx->req_base, CAM_CTX_REQ_MAX);
if (rc) {
CAM_ERR(CAM_ISP, "Camera Context Base init failed");
goto err;
diff --git a/drivers/media/platform/msm/camera/cam_isp/cam_isp_context.h b/drivers/media/platform/msm/camera/cam_isp/cam_isp_context.h
index f1f3137d..38ea58d 100644
--- a/drivers/media/platform/msm/camera/cam_isp/cam_isp_context.h
+++ b/drivers/media/platform/msm/camera/cam_isp/cam_isp_context.h
@@ -148,12 +148,14 @@
* @ctx: ISP context obj to be initialized
* @bridge_ops: Bridge call back funciton
* @hw_intf: ISP hw manager interface
+ * @ctx_id: ID for this context
*
*/
int cam_isp_context_init(struct cam_isp_context *ctx,
struct cam_context *ctx_base,
struct cam_req_mgr_kmd_ops *bridge_ops,
- struct cam_hw_mgr_intf *hw_intf);
+ struct cam_hw_mgr_intf *hw_intf,
+ uint32_t ctx_id);
/**
* cam_isp_context_deinit()
diff --git a/drivers/media/platform/msm/camera/cam_isp/cam_isp_dev.c b/drivers/media/platform/msm/camera/cam_isp/cam_isp_dev.c
index bcdba05..e775daa 100644
--- a/drivers/media/platform/msm/camera/cam_isp/cam_isp_dev.c
+++ b/drivers/media/platform/msm/camera/cam_isp/cam_isp_dev.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2017-2018, 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
@@ -104,7 +104,8 @@
rc = cam_isp_context_init(&g_isp_dev.ctx_isp[i],
&g_isp_dev.ctx[i],
&node->crm_node_intf,
- &node->hw_mgr_intf);
+ &node->hw_mgr_intf,
+ i);
if (rc) {
CAM_ERR(CAM_ISP, "ISP context init failed!");
goto unregister;
diff --git a/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/cam_ife_hw_mgr.c b/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/cam_ife_hw_mgr.c
index a6f1cf5..8c0c6d3 100644
--- a/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/cam_ife_hw_mgr.c
+++ b/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/cam_ife_hw_mgr.c
@@ -3187,7 +3187,7 @@
(event_cnt[core_idx1] &&
(event_cnt[core_idx1] - event_cnt[core_idx0] > 1))) {
- CAM_WARN(CAM_ISP,
+ CAM_ERR_RATE_LIMIT(CAM_ISP,
"One of the VFE cound not generate hw event %d",
hw_event_type);
rc = -1;
diff --git a/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/vfe_bus/cam_vfe_bus_ver2.c b/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/vfe_bus/cam_vfe_bus_ver2.c
index 36ce652..cc897a3 100644
--- a/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/vfe_bus/cam_vfe_bus_ver2.c
+++ b/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/vfe_bus/cam_vfe_bus_ver2.c
@@ -1000,7 +1000,7 @@
rsrc_data->height = 0;
rsrc_data->stride = 1;
rsrc_data->en_cfg = 0x3;
- } else if (rsrc_data->index == 9) {
+ } else if (rsrc_data->index == 9 || rsrc_data->index == 10) {
/* Write master 9 - Raw dump */
rsrc_data->width = rsrc_data->width * 2;
rsrc_data->stride = rsrc_data->width;
diff --git a/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/vfe_top/cam_vfe_camif_ver2.c b/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/vfe_top/cam_vfe_camif_ver2.c
index f427ab9..b748bc8 100644
--- a/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/vfe_top/cam_vfe_camif_ver2.c
+++ b/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/vfe_top/cam_vfe_camif_ver2.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2017-2018, 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
@@ -234,13 +234,6 @@
CAM_DBG(CAM_ISP, "hw id:%d core_cfg val:%d", camif_res->hw_intf->hw_idx,
val);
- cam_io_w_mb(0x00400040, rsrc_data->mem_base +
- rsrc_data->camif_reg->camif_config);
- cam_io_w_mb(0x1, rsrc_data->mem_base +
- rsrc_data->camif_reg->line_skip_pattern);
- cam_io_w_mb(0x1, rsrc_data->mem_base +
- rsrc_data->camif_reg->pixel_skip_pattern);
-
/* epoch config with 20 line */
cam_io_w_mb(rsrc_data->reg_data->epoch_line_cfg,
rsrc_data->mem_base + rsrc_data->camif_reg->epoch_irq);
diff --git a/drivers/media/platform/msm/camera/cam_jpeg/cam_jpeg_context.c b/drivers/media/platform/msm/camera/cam_jpeg/cam_jpeg_context.c
index 1ccef0d..02334a4 100644
--- a/drivers/media/platform/msm/camera/cam_jpeg/cam_jpeg_context.c
+++ b/drivers/media/platform/msm/camera/cam_jpeg/cam_jpeg_context.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2017-2018, 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
@@ -121,7 +121,8 @@
int cam_jpeg_context_init(struct cam_jpeg_context *ctx,
struct cam_context *ctx_base,
- struct cam_hw_mgr_intf *hw_intf)
+ struct cam_hw_mgr_intf *hw_intf,
+ uint32_t ctx_id)
{
int rc;
int i;
@@ -139,8 +140,8 @@
for (i = 0; i < CAM_CTX_REQ_MAX; i++)
ctx->req_base[i].req_priv = ctx;
- rc = cam_context_init(ctx_base, jpeg_dev_name, NULL, hw_intf,
- ctx->req_base, CAM_CTX_REQ_MAX);
+ rc = cam_context_init(ctx_base, jpeg_dev_name, CAM_JPEG, ctx_id,
+ NULL, hw_intf, ctx->req_base, CAM_CTX_REQ_MAX);
if (rc) {
CAM_ERR(CAM_JPEG, "Camera Context Base init failed");
goto err;
diff --git a/drivers/media/platform/msm/camera/cam_jpeg/cam_jpeg_context.h b/drivers/media/platform/msm/camera/cam_jpeg/cam_jpeg_context.h
index 90ac5cf..1a40679 100644
--- a/drivers/media/platform/msm/camera/cam_jpeg/cam_jpeg_context.h
+++ b/drivers/media/platform/msm/camera/cam_jpeg/cam_jpeg_context.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2017-2018, 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
@@ -53,11 +53,13 @@
* @ctx: JPEG context obj to be initialized
* @ctx_base: Context base from cam_context
* @hw_intf: JPEG hw manager interface
+ * @ctx_id: ID for this context
*
*/
int cam_jpeg_context_init(struct cam_jpeg_context *ctx,
struct cam_context *ctx_base,
- struct cam_hw_mgr_intf *hw_intf);
+ struct cam_hw_mgr_intf *hw_intf,
+ uint32_t ctx_id);
/**
* cam_jpeg_context_deinit()
diff --git a/drivers/media/platform/msm/camera/cam_jpeg/cam_jpeg_dev.c b/drivers/media/platform/msm/camera/cam_jpeg/cam_jpeg_dev.c
index a400388..60feeac 100644
--- a/drivers/media/platform/msm/camera/cam_jpeg/cam_jpeg_dev.c
+++ b/drivers/media/platform/msm/camera/cam_jpeg/cam_jpeg_dev.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2017-2018, 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
@@ -98,7 +98,8 @@
for (i = 0; i < CAM_CTX_MAX; i++) {
rc = cam_jpeg_context_init(&g_jpeg_dev.ctx_jpeg[i],
&g_jpeg_dev.ctx[i],
- &node->hw_mgr_intf);
+ &node->hw_mgr_intf,
+ i);
if (rc) {
CAM_ERR(CAM_JPEG, "JPEG context init failed %d %d",
i, rc);
diff --git a/drivers/media/platform/msm/camera/cam_lrme/cam_lrme_context.c b/drivers/media/platform/msm/camera/cam_lrme/cam_lrme_context.c
index 1ab3143..3d0266d 100644
--- a/drivers/media/platform/msm/camera/cam_lrme/cam_lrme_context.c
+++ b/drivers/media/platform/msm/camera/cam_lrme/cam_lrme_context.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2017-2018, 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
@@ -16,6 +16,8 @@
#include "cam_debug_util.h"
#include "cam_lrme_context.h"
+static const char lrme_dev_name[] = "lrme";
+
static int __cam_lrme_ctx_acquire_dev_in_available(struct cam_context *ctx,
struct cam_acquire_dev_cmd *cmd)
{
@@ -208,7 +210,7 @@
int cam_lrme_context_init(struct cam_lrme_context *lrme_ctx,
struct cam_context *base_ctx,
struct cam_hw_mgr_intf *hw_intf,
- uint64_t index)
+ uint32_t index)
{
int rc = 0;
@@ -221,8 +223,8 @@
memset(lrme_ctx, 0, sizeof(*lrme_ctx));
- rc = cam_context_init(base_ctx, "lrme", NULL, hw_intf,
- lrme_ctx->req_base, CAM_CTX_REQ_MAX);
+ rc = cam_context_init(base_ctx, lrme_dev_name, CAM_LRME, index,
+ NULL, hw_intf, lrme_ctx->req_base, CAM_CTX_REQ_MAX);
if (rc) {
CAM_ERR(CAM_LRME, "Failed to init context");
return rc;
diff --git a/drivers/media/platform/msm/camera/cam_lrme/cam_lrme_context.h b/drivers/media/platform/msm/camera/cam_lrme/cam_lrme_context.h
index 882f7ac..4c705c1 100644
--- a/drivers/media/platform/msm/camera/cam_lrme/cam_lrme_context.h
+++ b/drivers/media/platform/msm/camera/cam_lrme/cam_lrme_context.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2017-2018, 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
@@ -35,7 +35,7 @@
int cam_lrme_context_init(struct cam_lrme_context *lrme_ctx,
struct cam_context *base_ctx, struct cam_hw_mgr_intf *hw_intf,
- uint64_t index);
+ uint32_t index);
int cam_lrme_context_deinit(struct cam_lrme_context *lrme_ctx);
#endif /* _CAM_LRME_CONTEXT_H_ */
diff --git a/drivers/media/platform/msm/camera/cam_req_mgr/Makefile b/drivers/media/platform/msm/camera/cam_req_mgr/Makefile
index f514139..86c2039 100644
--- a/drivers/media/platform/msm/camera/cam_req_mgr/Makefile
+++ b/drivers/media/platform/msm/camera/cam_req_mgr/Makefile
@@ -2,9 +2,9 @@
ccflags-y += -Idrivers/media/platform/msm/camera/cam_smmu/
ccflags-y += -Idrivers/media/platform/msm/camera/cam_utils
-obj-$(CONFIG_SPECTRA_CAMERA) += cam_req_mgr_dev.o \
+obj-$(CONFIG_SPECTRA_CAMERA) += cam_req_mgr_core.o\
+ cam_req_mgr_dev.o \
cam_req_mgr_util.o \
- cam_req_mgr_core.o \
cam_req_mgr_workq.o \
cam_mem_mgr.o \
cam_req_mgr_timer.o \
diff --git a/drivers/media/platform/msm/camera/cam_req_mgr/cam_mem_mgr.c b/drivers/media/platform/msm/camera/cam_req_mgr/cam_mem_mgr.c
index 02f03ea..643b0afc 100644
--- a/drivers/media/platform/msm/camera/cam_req_mgr/cam_mem_mgr.c
+++ b/drivers/media/platform/msm/camera/cam_req_mgr/cam_mem_mgr.c
@@ -377,7 +377,8 @@
heap_id = ION_HEAP(ION_SECURE_DISPLAY_HEAP_ID);
ion_flag |= ION_FLAG_SECURE | ION_FLAG_CP_CAMERA;
} else {
- heap_id = ION_HEAP(ION_SYSTEM_HEAP_ID);
+ heap_id = ION_HEAP(ION_SYSTEM_HEAP_ID) |
+ ION_HEAP(ION_CAMERA_HEAP_ID);
}
if (cmd->flags & CAM_MEM_FLAG_CACHE)
@@ -947,7 +948,8 @@
else
ion_flag &= ~ION_FLAG_CACHED;
- heap_id = ION_HEAP(ION_SYSTEM_HEAP_ID);
+ heap_id = ION_HEAP(ION_SYSTEM_HEAP_ID) |
+ ION_HEAP(ION_CAMERA_HEAP_ID);
rc = cam_mem_util_get_dma_buf(inp->size,
inp->align,
@@ -1111,7 +1113,8 @@
return -EINVAL;
}
- heap_id = ION_HEAP(ION_SYSTEM_HEAP_ID);
+ heap_id = ION_HEAP(ION_SYSTEM_HEAP_ID) |
+ ION_HEAP(ION_CAMERA_HEAP_ID);
rc = cam_mem_util_get_dma_buf(inp->size,
inp->align,
heap_id,
diff --git a/drivers/media/platform/msm/camera/cam_req_mgr/cam_req_mgr_core.c b/drivers/media/platform/msm/camera/cam_req_mgr/cam_req_mgr_core.c
index 3100f91..3e6b856 100644
--- a/drivers/media/platform/msm/camera/cam_req_mgr/cam_req_mgr_core.c
+++ b/drivers/media/platform/msm/camera/cam_req_mgr/cam_req_mgr_core.c
@@ -25,6 +25,22 @@
static struct cam_req_mgr_core_device *g_crm_core_dev;
+void cam_req_mgr_handle_core_shutdown(void)
+{
+ struct cam_req_mgr_core_session *session;
+ struct cam_req_mgr_core_session *tsession;
+ struct cam_req_mgr_session_info ses_info;
+
+ if (!list_empty(&g_crm_core_dev->session_head)) {
+ list_for_each_entry_safe(session, tsession,
+ &g_crm_core_dev->session_head, entry) {
+ ses_info.session_hdl =
+ session->session_hdl;
+ cam_req_mgr_destroy_session(&ses_info);
+ }
+ }
+}
+
static int __cam_req_mgr_setup_payload(struct cam_req_mgr_core_workq *workq)
{
int32_t i = 0;
@@ -183,20 +199,14 @@
tbl->skip_traverse, traverse_data->in_q->slot[curr_idx].status,
traverse_data->in_q->slot[curr_idx].skip_idx);
- if (tbl->inject_delay > 0 && (traverse_data->validate_only == false)) {
+ if ((tbl->inject_delay > 0) &&
+ (traverse_data->self_link == true)) {
CAM_DBG(CAM_CRM, "Injecting Delay of one frame");
+ apply_data[tbl->pd].req_id = -1;
tbl->inject_delay--;
/* This pd table is not ready to proceed with asked idx */
SET_FAILURE_BIT(traverse_data->result, tbl->pd);
- apply_data[tbl->pd].req_id = -1;
- if (tbl->next) {
- __cam_req_mgr_dec_idx(&next_idx, tbl->pd_delta,
- tbl->num_slots);
- traverse_data->idx = next_idx;
- traverse_data->tbl = tbl->next;
- rc = __cam_req_mgr_traverse(traverse_data);
- }
- return rc;
+ return -EAGAIN;
}
/* Check if req is ready or in skip mode or pd tbl is in skip mode */
@@ -212,14 +222,21 @@
}
if (rc >= 0) {
SET_SUCCESS_BIT(traverse_data->result, tbl->pd);
+
if (traverse_data->validate_only == false) {
apply_data[tbl->pd].pd = tbl->pd;
apply_data[tbl->pd].req_id =
- CRM_GET_REQ_ID(traverse_data->in_q,
- curr_idx);
+ CRM_GET_REQ_ID(
+ traverse_data->in_q, curr_idx);
apply_data[tbl->pd].idx = curr_idx;
- /* If traverse is success dec traverse skip */
+ CAM_DBG(CAM_CRM, "req_id: %d with pd of %d",
+ apply_data[tbl->pd].req_id,
+ apply_data[tbl->pd].pd);
+ /*
+ * If traverse is successful decrement
+ * traverse skip
+ */
if (tbl->skip_traverse > 0) {
apply_data[tbl->pd].req_id = -1;
tbl->skip_traverse--;
@@ -231,20 +248,8 @@
}
} else {
/* This pd table is not ready to proceed with asked idx */
- if (tbl->slot[curr_idx].state == CRM_REQ_STATE_APPLIED)
- SET_SUCCESS_BIT(traverse_data->result, tbl->pd);
- else
- SET_FAILURE_BIT(traverse_data->result, tbl->pd);
-
- apply_data[tbl->pd].req_id = -1;
- if (tbl->next) {
- __cam_req_mgr_dec_idx(&next_idx, tbl->pd_delta,
- tbl->num_slots);
- traverse_data->idx = next_idx;
- traverse_data->tbl = tbl->next;
- rc = __cam_req_mgr_traverse(traverse_data);
- }
- return rc;
+ SET_FAILURE_BIT(traverse_data->result, tbl->pd);
+ return -EAGAIN;
}
return 0;
}
@@ -439,8 +444,8 @@
trace_cam_req_mgr_apply_request(link, &apply_req, dev);
apply_req.trigger_point = trigger;
- CAM_DBG(CAM_CRM, "SEND: pd %d req_id %lld",
- pd, apply_req.request_id);
+ CAM_DBG(CAM_CRM, "SEND: link_hdl: %x pd %d req_id %lld",
+ link->link_hdl, pd, apply_req.request_id);
if (dev->ops && dev->ops->apply_req) {
rc = dev->ops->apply_req(&apply_req);
if (rc < 0)
@@ -468,20 +473,20 @@
/**
* __cam_req_mgr_check_link_is_ready()
*
- * @brief : traverse through all request tables and see if
- * all devices are ready to apply request settings
- * @link : pointer to link whose input queue and req tbl are
- * traversed through
- * @idx : index within input request queue
+ * @brief : traverse through all request tables and see if all devices are
+ * ready to apply request settings.
+ * @link : pointer to link whose input queue and req tbl are
+ * traversed through
+ * @idx : index within input request queue
* @validate_only : Whether to validate only and/or update settings
- * @result : Holds the value that indicates which of the pd
- * tables have a req that is ready to be applied
+ * @self_link : To indicate whether the validation is for the given link or
+ * other sync link
*
* @return : 0 for success, negative for failure
*
*/
static int __cam_req_mgr_check_link_is_ready(struct cam_req_mgr_core_link *link,
- int32_t idx, bool validate_only, int *result)
+ int32_t idx, bool validate_only, bool self_link)
{
int rc;
struct cam_req_mgr_traverse traverse_data;
@@ -491,10 +496,10 @@
in_q = link->req.in_q;
apply_data = link->req.apply_data;
+
if (validate_only == false) {
memset(apply_data, 0,
- sizeof(struct cam_req_mgr_apply) *
- CAM_PIPELINE_DELAY_MAX);
+ sizeof(struct cam_req_mgr_apply) * CAM_PIPELINE_DELAY_MAX);
}
traverse_data.apply_data = apply_data;
@@ -503,27 +508,28 @@
traverse_data.in_q = in_q;
traverse_data.result = 0;
traverse_data.validate_only = validate_only;
+ traverse_data.self_link = self_link;
/*
* Traverse through all pd tables, if result is success,
* apply the settings
*/
rc = __cam_req_mgr_traverse(&traverse_data);
- CAM_DBG(CAM_CRM, "SOF: idx %d result %x pd_mask %x rc %d",
- idx, traverse_data.result, link->pd_mask, rc);
+ CAM_DBG(CAM_CRM,
+ "SOF: idx %d self_link %d validate %d result %x pd_mask %x rc %d",
+ idx, traverse_data.self_link, traverse_data.validate_only,
+ traverse_data.result, link->pd_mask, rc);
- if (!traverse_data.result)
- return -EAGAIN;
-
- if (!rc) {
+ if (!rc && traverse_data.result == link->pd_mask) {
CAM_DBG(CAM_CRM,
"APPLY: link_hdl= %x idx= %d, req_id= %lld :%lld :%lld",
link->link_hdl, idx,
- apply_data[2].req_id, apply_data[1].req_id,
+ apply_data[2].req_id,
+ apply_data[1].req_id,
apply_data[0].req_id);
- }
+ } else
+ rc = -EAGAIN;
- *result = traverse_data.result;
return rc;
}
@@ -574,6 +580,11 @@
link->sof_counter = -1;
link->sync_link->sof_counter = -1;
link->frame_skip_flag = false;
+
+ CAM_DBG(CAM_CRM,
+ "link_hdl %x self_counter %lld other_counter %lld frame_skip_lag %d",
+ link->link_hdl, link->sof_counter,
+ link->sync_link->sof_counter, link->frame_skip_flag);
}
/**
@@ -592,6 +603,11 @@
link->sof_counter++;
link->sync_self_ref = link->sof_counter -
link->sync_link->sof_counter;
+
+ CAM_DBG(CAM_CRM,
+ "link_hdl %x self_counter %lld other_counter %lld",
+ link->link_hdl, link->sof_counter,
+ link->sync_link->sof_counter);
}
/**
@@ -609,6 +625,11 @@
link->sof_counter = (MAX_SYNC_COUNT -
(link->sync_link->sof_counter));
link->sync_link->sof_counter = 0;
+
+ CAM_DBG(CAM_CRM,
+ "link_hdl %x self_counter %lld sync_link_hdl %x other_counter %lld",
+ link->link_hdl, link->sof_counter,
+ link->sync_link->link_hdl, link->sync_link->sof_counter);
}
/**
@@ -632,12 +653,18 @@
__cam_req_mgr_wrap_sof_cnt(link);
sync_diff = link->sof_counter - sync_link->sof_counter;
+
+ CAM_DBG(CAM_CRM,
+ "link[%x] self_counter=%lld other_counter=%lld diff=%lld sync_self_ref=%lld",
+ link->link_hdl, link->sof_counter,
+ sync_link->sof_counter, sync_diff, link->sync_self_ref);
+
if (sync_diff != link->sync_self_ref) {
link->sync_link->frame_skip_flag = true;
CAM_WARN(CAM_CRM,
- "Detected anomaly, skip link:%d, self=%lld, other=%lld",
+ "Detected anomaly, skip link_hdl %x self_counter=%lld other_counter=%lld sync_self_ref=%lld",
link->link_hdl, link->sof_counter,
- sync_link->sof_counter);
+ sync_link->sof_counter, link->sync_self_ref);
rc = -EPERM;
}
@@ -652,16 +679,12 @@
* @link : pointer to link whose input queue and req tbl are
* traversed through
* @slot : pointer to the current slot being processed
- * @result : Holds the value that indicates which of the pd
- * tables have a req that is ready to be applied
- *
* @return : 0 for success, negative for failure
*
*/
static int __cam_req_mgr_process_sync_req(
struct cam_req_mgr_core_link *link,
- struct cam_req_mgr_slot *slot,
- int *result)
+ struct cam_req_mgr_slot *slot)
{
struct cam_req_mgr_core_link *sync_link = NULL;
int64_t req_id = 0;
@@ -674,11 +697,17 @@
sync_link = link->sync_link;
req_id = slot->req_id;
+
+ CAM_DBG(CAM_CRM,
+ "link_hdl %x req %lld sync_self_ref %lld sof_counter %lld frame_skip_flag %d sync_link_self_ref %lld",
+ link->link_hdl, req_id, link->sync_self_ref, link->sof_counter,
+ link->frame_skip_flag, link->sync_link->sync_self_ref);
+
if (link->sof_counter == -1) {
__cam_req_mgr_sof_cnt_initialize(link);
- } else if (link->frame_skip_flag &&
+ } else if ((link->frame_skip_flag) &&
(sync_link->sync_self_ref != -1)) {
- CAM_DBG(CAM_CRM, "Link[%x] Req[%lld] Resetting values",
+ CAM_DBG(CAM_CRM, "Link[%x] Req[%lld] Resetting values ",
link->link_hdl, req_id);
__cam_req_mgr_reset_sof_cnt(link);
__cam_req_mgr_sof_cnt_initialize(link);
@@ -686,7 +715,7 @@
link->sof_counter++;
}
- rc = __cam_req_mgr_check_link_is_ready(link, slot->idx, true, result);
+ rc = __cam_req_mgr_check_link_is_ready(link, slot->idx, true, true);
if (rc) {
CAM_DBG(CAM_CRM,
"Req: %lld [My link]not available link: %x, rc=%d",
@@ -696,9 +725,10 @@
sync_slot_idx = __cam_req_mgr_find_slot_for_req(
sync_link->req.in_q, req_id);
+
if (sync_slot_idx != -1) {
rc = __cam_req_mgr_check_link_is_ready(
- sync_link, sync_slot_idx, true, result);
+ sync_link, sync_slot_idx, true, false);
CAM_DBG(CAM_CRM, "sync_slot_idx=%d, status=%d, rc=%d",
sync_slot_idx,
sync_link->req.in_q->slot[sync_slot_idx].status,
@@ -709,8 +739,8 @@
}
if ((sync_slot_idx != -1) &&
- ((sync_link->req.in_q->slot[sync_slot_idx].status ==
- CRM_SLOT_STATUS_REQ_APPLIED) || (rc == 0))) {
+ ((sync_link->req.in_q->slot[sync_slot_idx].status ==
+ CRM_SLOT_STATUS_REQ_APPLIED) || (rc == 0))) {
rc = __cam_req_mgr_validate_sof_cnt(link, sync_link);
if (rc) {
CAM_DBG(CAM_CRM,
@@ -718,8 +748,21 @@
req_id, sync_link->link_hdl);
goto failure;
}
- __cam_req_mgr_check_link_is_ready(link, slot->idx, false,
- result);
+
+ CAM_DBG(CAM_CRM,
+ "Req: %lld ready to apply on link: %x [validation successful]",
+ req_id, link->link_hdl);
+ /*
+ * At this point all validation is successfully done
+ * and we can proceed to apply the given request.
+ * Ideally the next call should return success.
+ */
+ rc = __cam_req_mgr_check_link_is_ready(link,
+ slot->idx, false, true);
+
+ if (rc) {
+ CAM_WARN(CAM_CRM, "Unexpected return value rc: %d", rc);
+ }
} else {
CAM_DBG(CAM_CRM,
"Req: %lld [Other link] not ready to apply on link: %x",
@@ -736,41 +779,6 @@
}
/**
- * __cam_req_mgr_reset_pd_tables()
- *
- * @brief : resets pd tables based on req getting applied on
- * from a particular pd table
- * @link : pointer to link whose input queue and req tbl are
- * traversed through
- * @slot : Pointer to the current slot
- * @result : indicates request of which pd table was successfully
- * processed
- *
- */
-static void __cam_req_mgr_reset_pd_tables(
- struct cam_req_mgr_core_link *link,
- struct cam_req_mgr_slot *slot,
- int result)
-{
- int pd_set_bit = 0;
- int curr_idx = slot->idx;
- int no_tables = link->req.num_tbl;
- int max_pd_delay = link->max_delay;
- struct cam_req_mgr_req_tbl *tbl = link->req.l_tbl;
- struct cam_req_mgr_req_queue *in_q = link->req.in_q;
-
- while (no_tables) {
- pd_set_bit = (result & (1 << max_pd_delay));
- if (pd_set_bit)
- tbl->slot[curr_idx].state = CRM_REQ_STATE_APPLIED;
- max_pd_delay--;
- no_tables--;
- tbl = tbl->next;
- __cam_req_mgr_dec_idx(&curr_idx, 1, in_q->num_slots);
- }
-}
-
-/**
* __cam_req_mgr_process_req()
*
* @brief : processes read index in request queue and traverse through table
@@ -783,7 +791,7 @@
static int __cam_req_mgr_process_req(struct cam_req_mgr_core_link *link,
uint32_t trigger)
{
- int rc = 0, idx, result = 0;
+ int rc = 0, idx;
struct cam_req_mgr_slot *slot = NULL;
struct cam_req_mgr_req_queue *in_q;
struct cam_req_mgr_core_session *session;
@@ -798,9 +806,9 @@
* - if in applied_state, somthign wrong.
* - if in no_req state, no new req
*/
- CAM_DBG(CAM_CRM, "SOF Req[%lld] idx %d req_status %d",
+ CAM_DBG(CAM_CRM, "SOF Req[%lld] idx %d req_status %d link_hdl %x",
in_q->slot[in_q->rd_idx].req_id, in_q->rd_idx,
- in_q->slot[in_q->rd_idx].status);
+ in_q->slot[in_q->rd_idx].status, link->link_hdl);
slot = &in_q->slot[in_q->rd_idx];
if (slot->status == CRM_SLOT_STATUS_NO_REQ) {
@@ -809,8 +817,8 @@
goto error;
}
- if (trigger != CAM_TRIGGER_POINT_SOF &&
- trigger != CAM_TRIGGER_POINT_EOF)
+ if ((trigger != CAM_TRIGGER_POINT_SOF) &&
+ (trigger != CAM_TRIGGER_POINT_EOF))
goto error;
if ((trigger == CAM_TRIGGER_POINT_EOF) &&
@@ -829,11 +837,10 @@
}
if (slot->sync_mode == CAM_REQ_MGR_SYNC_MODE_SYNC)
- rc = __cam_req_mgr_process_sync_req(link, slot,
- &result);
+ rc = __cam_req_mgr_process_sync_req(link, slot);
else
rc = __cam_req_mgr_check_link_is_ready(link,
- slot->idx, false, &result);
+ slot->idx, false, true);
if (rc < 0) {
/*
@@ -865,10 +872,10 @@
/* Apply req failed retry at next sof */
slot->status = CRM_SLOT_STATUS_REQ_PENDING;
} else {
- CAM_DBG(CAM_CRM, "Applied req[%lld] on link[%x] success",
- slot->req_id, link->link_hdl);
link->trigger_mask |= trigger;
+ CAM_DBG(CAM_CRM, "Applied req[%lld] on link[%x] success",
+ slot->req_id, link->link_hdl);
spin_lock_bh(&link->link_state_spin_lock);
if (link->state == CAM_CRM_LINK_STATE_ERR) {
CAM_WARN(CAM_CRM, "Err recovery done idx %d",
@@ -878,30 +885,21 @@
spin_unlock_bh(&link->link_state_spin_lock);
if (link->trigger_mask == link->subscribe_event) {
- if (result == link->pd_mask) {
- slot->status = CRM_SLOT_STATUS_REQ_APPLIED;
- CAM_DBG(CAM_CRM, "req %d is applied on link %d",
- slot->req_id, link->link_hdl);
- idx = in_q->rd_idx;
- __cam_req_mgr_dec_idx(
- &idx, link->max_delay + 1,
- in_q->num_slots);
- __cam_req_mgr_reset_req_slot(link, idx);
- } else {
- CAM_DBG(CAM_CRM,
- "Req:%lld not applied on all devices",
- slot->req_id);
- __cam_req_mgr_reset_pd_tables(link, slot,
- result);
- slot->status = CRM_SLOT_STATUS_REQ_PENDING;
- }
-
+ slot->status = CRM_SLOT_STATUS_REQ_APPLIED;
link->trigger_mask = 0;
+ CAM_DBG(CAM_CRM, "req %d is applied on link %x",
+ slot->req_id,
+ link->link_hdl);
+ idx = in_q->rd_idx;
+ __cam_req_mgr_dec_idx(
+ &idx, link->max_delay + 1,
+ in_q->num_slots);
+ __cam_req_mgr_reset_req_slot(link, idx);
}
}
+
mutex_unlock(&session->lock);
return rc;
-
error:
mutex_unlock(&session->lock);
return rc;
@@ -1138,8 +1136,9 @@
* @brief : Cleans up the mem allocated while linking
* @link : pointer to link, mem associated with this link is freed
*
+ * @return : returns if unlink for any device was success or failure
*/
-static void __cam_req_mgr_destroy_link_info(struct cam_req_mgr_core_link *link)
+static int __cam_req_mgr_destroy_link_info(struct cam_req_mgr_core_link *link)
{
int32_t i = 0;
struct cam_req_mgr_connected_device *dev;
@@ -1160,8 +1159,8 @@
rc = dev->ops->link_setup(&link_data);
if (rc)
CAM_ERR(CAM_CRM,
- "Unlink failed dev_hdl 0x%x rc=%d",
- dev->dev_hdl, rc);
+ "Unlink failed dev_hdl %d",
+ dev->dev_hdl);
}
dev->dev_hdl = 0;
dev->parent = NULL;
@@ -1176,6 +1175,8 @@
link->pd_mask = 0;
link->num_devs = 0;
link->max_delay = 0;
+
+ return rc;
}
/**
@@ -1238,6 +1239,9 @@
/* Loop through and find a free index */
for (i = 0; i < MAX_LINKS_PER_SESSION; i++) {
if (!session->links[i]) {
+ CAM_DBG(CAM_CRM,
+ "Free link index %d found, num_links=%d",
+ i, session->num_links);
session->links[i] = link;
break;
}
@@ -1442,9 +1446,9 @@
if (device->ops && device->ops->flush_req)
rc = device->ops->flush_req(&flush_req);
}
+ complete(&link->workq_comp);
mutex_unlock(&link->req.lock);
- complete(&link->workq_comp);
end:
return rc;
}
@@ -1476,13 +1480,12 @@
link = (struct cam_req_mgr_core_link *)priv;
task_data = (struct crm_task_payload *)data;
sched_req = (struct cam_req_mgr_sched_request *)&task_data->u;
- CAM_DBG(CAM_CRM, "link_hdl %x req_id %lld sync_mode %d",
- sched_req->link_hdl,
- sched_req->req_id,
- sched_req->sync_mode);
-
in_q = link->req.in_q;
+ CAM_DBG(CAM_CRM, "link_hdl %x req_id %lld at slot %d sync_mode %d",
+ sched_req->link_hdl, sched_req->req_id,
+ in_q->wr_idx, sched_req->sync_mode);
+
mutex_lock(&link->req.lock);
slot = &in_q->slot[in_q->wr_idx];
@@ -1490,9 +1493,6 @@
slot->status != CRM_SLOT_STATUS_REQ_APPLIED)
CAM_WARN(CAM_CRM, "in_q overwrite %d", slot->status);
- CAM_DBG(CAM_CRM, "sched_req %lld at slot %d sync_mode %d",
- sched_req->req_id, in_q->wr_idx, sched_req->sync_mode);
-
slot->status = CRM_SLOT_STATUS_REQ_ADDED;
slot->req_id = sched_req->req_id;
slot->sync_mode = sched_req->sync_mode;
@@ -1875,9 +1875,9 @@
rc = -EPERM;
goto end;
}
+ crm_timer_reset(link->watchdog);
spin_unlock_bh(&link->link_state_spin_lock);
- crm_timer_reset(link->watchdog);
task = cam_req_mgr_workq_get_task(link->workq);
if (!task) {
CAM_ERR(CAM_CRM, "no empty task req_id %lld", err_info->req_id);
@@ -1938,9 +1938,9 @@
rc = -EPERM;
goto end;
}
+ crm_timer_reset(link->watchdog);
spin_unlock_bh(&link->link_state_spin_lock);
- crm_timer_reset(link->watchdog);
task = cam_req_mgr_workq_get_task(link->workq);
if (!task) {
CAM_ERR(CAM_CRM, "no empty task frame %lld",
@@ -2109,22 +2109,6 @@
return rc;
}
-void cam_req_mgr_handle_core_shutdown(void)
-{
- struct cam_req_mgr_core_session *session;
- struct cam_req_mgr_core_session *tsession;
- struct cam_req_mgr_session_info ses_info;
-
- if (!list_empty(&g_crm_core_dev->session_head)) {
- list_for_each_entry_safe(session, tsession,
- &g_crm_core_dev->session_head, entry) {
- ses_info.session_hdl =
- session->session_hdl;
- cam_req_mgr_destroy_session(&ses_info);
- }
- }
-}
-
/* IOCTLs handling section */
int cam_req_mgr_create_session(
struct cam_req_mgr_session_info *ses_info)
@@ -2185,6 +2169,9 @@
mutex_lock(&link->lock);
spin_lock_bh(&link->link_state_spin_lock);
link->state = CAM_CRM_LINK_STATE_IDLE;
+
+ /* Destroy timer of link */
+ crm_timer_exit(&link->watchdog);
spin_unlock_bh(&link->link_state_spin_lock);
__cam_req_mgr_print_req_tbl(&link->req);
@@ -2192,13 +2179,14 @@
kfree(link->workq->task.pool[0].payload);
link->workq->task.pool[0].payload = NULL;
- /* Destroy workq and timer of link */
- crm_timer_exit(&link->watchdog);
-
+ /* Destroy workq of link */
cam_req_mgr_workq_destroy(&link->workq);
/* Cleanup request tables and unlink devices */
- __cam_req_mgr_destroy_link_info(link);
+ rc = __cam_req_mgr_destroy_link_info(link);
+ if (rc)
+ CAM_ERR(CAM_CORE,
+ "Unlink for all devices was not successful");
/* Free memory holding data of linked devs */
__cam_req_mgr_destroy_subdev(link->l_dev);
@@ -2504,6 +2492,7 @@
}
mutex_lock(&cam_session->lock);
+
CAM_DBG(CAM_CRM, "link handles %x %x",
sync_info->link_hdls[0], sync_info->link_hdls[1]);
@@ -2660,7 +2649,9 @@
}
} else if (control->ops == CAM_REQ_MGR_LINK_DEACTIVATE) {
/* Destroy SOF watchdog timer */
+ spin_lock_bh(&link->link_state_spin_lock);
crm_timer_exit(&link->watchdog);
+ spin_unlock_bh(&link->link_state_spin_lock);
/* notify nodes */
for (j = 0; j < link->num_devs; j++) {
dev = &link->l_dev[j];
diff --git a/drivers/media/platform/msm/camera/cam_req_mgr/cam_req_mgr_core.h b/drivers/media/platform/msm/camera/cam_req_mgr/cam_req_mgr_core.h
index 4511a5d..e4865b3 100644
--- a/drivers/media/platform/msm/camera/cam_req_mgr/cam_req_mgr_core.h
+++ b/drivers/media/platform/msm/camera/cam_req_mgr/cam_req_mgr_core.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2016-2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2016-2018, 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
@@ -79,14 +79,12 @@
* EMPTY : indicates req slot is empty
* PENDING : indicates req slot is waiting for reqs from all devs
* READY : indicates req slot is ready to be sent to devs
- * APPLIED : indicates req slot is already sent to devs
* INVALID : indicates req slot is not in valid state
*/
enum crm_req_state {
CRM_REQ_STATE_EMPTY,
CRM_REQ_STATE_PENDING,
CRM_REQ_STATE_READY,
- CRM_REQ_STATE_APPLIED,
CRM_REQ_STATE_INVALID,
};
@@ -132,6 +130,8 @@
* @apply_data : pointer which various tables will update during traverse
* @in_q : input request queue pointer
* @validate_only : Whether to validate only and/or update settings
+ * @self_link : To indicate whether the check is for the given link or the
+ * other sync link
*/
struct cam_req_mgr_traverse {
int32_t idx;
@@ -140,6 +140,7 @@
struct cam_req_mgr_apply *apply_data;
struct cam_req_mgr_req_queue *in_q;
bool validate_only;
+ bool self_link;
};
/**
diff --git a/drivers/media/platform/msm/camera/cam_req_mgr/cam_req_mgr_dev.c b/drivers/media/platform/msm/camera/cam_req_mgr/cam_req_mgr_dev.c
index e4944d0..9a93feb 100644
--- a/drivers/media/platform/msm/camera/cam_req_mgr/cam_req_mgr_dev.c
+++ b/drivers/media/platform/msm/camera/cam_req_mgr/cam_req_mgr_dev.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2016-2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2016-2018, 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
@@ -25,10 +25,12 @@
#include "cam_subdev.h"
#include "cam_mem_mgr.h"
#include "cam_debug_util.h"
+#include <linux/slub_def.h>
#define CAM_REQ_MGR_EVENT_MAX 30
static struct cam_req_mgr_device g_dev;
+struct kmem_cache *g_cam_req_mgr_timer_cachep;
static int cam_media_device_setup(struct device *dev)
{
@@ -635,6 +637,19 @@
g_dev.state = true;
+ if (g_cam_req_mgr_timer_cachep == NULL) {
+ g_cam_req_mgr_timer_cachep = kmem_cache_create("crm_timer",
+ sizeof(struct cam_req_mgr_timer), 64,
+ SLAB_CONSISTENCY_CHECKS | SLAB_RED_ZONE |
+ SLAB_POISON | SLAB_STORE_USER, NULL);
+ if (!g_cam_req_mgr_timer_cachep)
+ CAM_ERR(CAM_CRM,
+ "Failed to create kmem_cache for crm_timer");
+ else
+ CAM_DBG(CAM_CRM, "Name : %s",
+ g_cam_req_mgr_timer_cachep->name);
+ }
+
return rc;
req_mgr_core_fail:
diff --git a/drivers/media/platform/msm/camera/cam_req_mgr/cam_req_mgr_interface.h b/drivers/media/platform/msm/camera/cam_req_mgr/cam_req_mgr_interface.h
index 45ebc69..1ca6cc5 100644
--- a/drivers/media/platform/msm/camera/cam_req_mgr/cam_req_mgr_interface.h
+++ b/drivers/media/platform/msm/camera/cam_req_mgr/cam_req_mgr_interface.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2016-2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2016-2018, 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
diff --git a/drivers/media/platform/msm/camera/cam_req_mgr/cam_req_mgr_timer.c b/drivers/media/platform/msm/camera/cam_req_mgr/cam_req_mgr_timer.c
index 2189202..124b336 100644
--- a/drivers/media/platform/msm/camera/cam_req_mgr/cam_req_mgr_timer.c
+++ b/drivers/media/platform/msm/camera/cam_req_mgr/cam_req_mgr_timer.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2016-2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2016-2018, 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
@@ -53,9 +53,18 @@
CAM_DBG(CAM_CRM, "init timer %d %pK", expires, *timer);
if (*timer == NULL) {
- crm_timer = (struct cam_req_mgr_timer *)
- kzalloc(sizeof(struct cam_req_mgr_timer), GFP_KERNEL);
- if (!crm_timer) {
+ if (g_cam_req_mgr_timer_cachep) {
+ crm_timer = (struct cam_req_mgr_timer *)
+ kmem_cache_alloc(
+ g_cam_req_mgr_timer_cachep,
+ __GFP_ZERO | GFP_KERNEL);
+ if (!crm_timer) {
+ ret = -ENOMEM;
+ goto end;
+ }
+ }
+
+ else {
ret = -ENOMEM;
goto end;
}
@@ -80,10 +89,11 @@
}
void crm_timer_exit(struct cam_req_mgr_timer **crm_timer)
{
- CAM_DBG(CAM_CRM, "destroy timer %pK", *crm_timer);
+ CAM_DBG(CAM_CRM, "destroy timer %pK @ %pK", *crm_timer, crm_timer);
if (*crm_timer) {
del_timer_sync(&(*crm_timer)->sys_timer);
- kfree(*crm_timer);
+ if (g_cam_req_mgr_timer_cachep)
+ kmem_cache_free(g_cam_req_mgr_timer_cachep, *crm_timer);
*crm_timer = NULL;
}
}
diff --git a/drivers/media/platform/msm/camera/cam_req_mgr/cam_req_mgr_timer.h b/drivers/media/platform/msm/camera/cam_req_mgr/cam_req_mgr_timer.h
index 4d600ee..b3e473a 100644
--- a/drivers/media/platform/msm/camera/cam_req_mgr/cam_req_mgr_timer.h
+++ b/drivers/media/platform/msm/camera/cam_req_mgr/cam_req_mgr_timer.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2016-2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2016-2018, 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
@@ -66,4 +66,6 @@
* @timer : timer pointer which will be freed
*/
void crm_timer_exit(struct cam_req_mgr_timer **timer);
+
+extern struct kmem_cache *g_cam_req_mgr_timer_cachep;
#endif
diff --git a/drivers/media/platform/msm/camera/cam_req_mgr/cam_req_mgr_util.c b/drivers/media/platform/msm/camera/cam_req_mgr/cam_req_mgr_util.c
index f357941..dda04f8 100644
--- a/drivers/media/platform/msm/camera/cam_req_mgr/cam_req_mgr_util.c
+++ b/drivers/media/platform/msm/camera/cam_req_mgr/cam_req_mgr_util.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2017-2018, 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
@@ -296,7 +296,7 @@
idx = CAM_REQ_MGR_GET_HDL_IDX(dev_hdl);
if (idx >= CAM_REQ_MGR_MAX_HANDLES) {
- CAM_ERR(CAM_CRM, "Invalid idx");
+ CAM_ERR(CAM_CRM, "Invalid idx %d", idx);
goto destroy_hdl_fail;
}
diff --git a/drivers/media/platform/msm/camera/cam_sync/cam_sync.c b/drivers/media/platform/msm/camera/cam_sync/cam_sync.c
index 3d230af..d625a20 100644
--- a/drivers/media/platform/msm/camera/cam_sync/cam_sync.c
+++ b/drivers/media/platform/msm/camera/cam_sync/cam_sync.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2017-2018, 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
@@ -90,8 +90,9 @@
}
/* Trigger callback if sync object is already in SIGNALED state */
- if (row->state == CAM_SYNC_STATE_SIGNALED_SUCCESS ||
- row->state == CAM_SYNC_STATE_SIGNALED_ERROR) {
+ if ((row->state == CAM_SYNC_STATE_SIGNALED_SUCCESS ||
+ row->state == CAM_SYNC_STATE_SIGNALED_ERROR) &&
+ (!row->remaining)) {
sync_cb->callback_func = cb_func;
sync_cb->cb_data = userdata;
sync_cb->sync_obj = sync_obj;
diff --git a/drivers/media/platform/msm/camera_v2/Makefile b/drivers/media/platform/msm/camera_v2/Makefile
index cdb0468..4348d44 100644
--- a/drivers/media/platform/msm/camera_v2/Makefile
+++ b/drivers/media/platform/msm/camera_v2/Makefile
@@ -1,7 +1,7 @@
ccflags-y += -Idrivers/media/platform/msm/camera_v2
ccflags-y += -Idrivers/media/platform/msm/camera_v2/sensor
ccflags-y += -Idrivers/media/platform/msm/camera_v2/codecs
-ccflags-y += -Idrivers/media/platform/msm/camera_v2/isps
+ccflags-y += -Idrivers/media/platform/msm/camera_v2/isp
ccflags-y += -Idrivers/media/platform/msm/camera_v2/pproc
ccflags-y += -Idrivers/media/platform/msm/camera_v2/msm_vb2
ccflags-y += -Idrivers/media/platform/msm/camera_v2/camera
diff --git a/drivers/media/platform/msm/camera_v2/camera/camera.c b/drivers/media/platform/msm/camera_v2/camera/camera.c
index 0efbd1b..01ee5d4 100644
--- a/drivers/media/platform/msm/camera_v2/camera/camera.c
+++ b/drivers/media/platform/msm/camera_v2/camera/camera.c
@@ -917,9 +917,7 @@
rc = media_entity_pads_init(&pvdev->vdev->entity, 0, NULL);
if (WARN_ON(rc < 0))
goto entity_fail;
- pvdev->vdev->entity.function = MEDIA_ENT_F_IO_V4L;
- //TODO: Use entity.name in from userspcae to find device.
- //pvdev->vdev->entity.group_id = QCAMERA_VNODE_GROUP_ID;
+ pvdev->vdev->entity.function = QCAMERA_VNODE_GROUP_ID;
#endif
v4l2_dev->notify = NULL;
diff --git a/drivers/media/platform/msm/camera_v2/common/cam_smmu_api.c b/drivers/media/platform/msm/camera_v2/common/cam_smmu_api.c
index 36e3752..08c8575 100644
--- a/drivers/media/platform/msm/camera_v2/common/cam_smmu_api.c
+++ b/drivers/media/platform/msm/camera_v2/common/cam_smmu_api.c
@@ -1390,7 +1390,7 @@
virt_len, &iova);
if (rc < 0) {
- pr_err("Could not find valid iova for scratch buffer");
+ pr_err("Could not find valid iova for scratch buffer\n");
goto err_iommu_map;
}
@@ -2118,7 +2118,7 @@
/* set the name of the context bank */
property = of_get_property(dev->of_node, "iommus", &cnt);
cnt /= 4;
- for (i = 0, j = 0; i < cnt; i = i + 2, j++) {
+ for (i = 0, j = 0; i < cnt; i = i + 3, j++) {
rc = of_property_read_u32_index(dev->of_node,
"iommus", i + 1, &cb->sids[j]);
if (rc < 0)
diff --git a/drivers/media/platform/msm/camera_v2/common/cam_soc_api.c b/drivers/media/platform/msm/camera_v2/common/cam_soc_api.c
index 3c38f12..2eb00eb 100644
--- a/drivers/media/platform/msm/camera_v2/common/cam_soc_api.c
+++ b/drivers/media/platform/msm/camera_v2/common/cam_soc_api.c
@@ -710,7 +710,7 @@
CDBG("name : %s, enable : %d\n", tmp->name, mode);
if (mode) {
rc = regulator_set_mode(tmp->vdd,
- REGULATOR_MODE_FAST);
+ REGULATOR_MODE_NORMAL);
if (rc < 0) {
pr_err("regulator enable failed %d\n",
i);
diff --git a/drivers/media/platform/msm/camera_v2/common/msm_camera_io_util.c b/drivers/media/platform/msm/camera_v2/common/msm_camera_io_util.c
index 009b4cd..9a212825 100644
--- a/drivers/media/platform/msm/camera_v2/common/msm_camera_io_util.c
+++ b/drivers/media/platform/msm/camera_v2/common/msm_camera_io_util.c
@@ -408,14 +408,14 @@
return -EINVAL;
}
- if (cam_vreg == NULL) {
- pr_err("%s:%d cam_vreg sequence invalid\n", __func__, __LINE__);
- return -EINVAL;
- }
-
if (!num_vreg_seq)
num_vreg_seq = num_vreg;
+ if ((cam_vreg == NULL) && num_vreg_seq) {
+ pr_err("%s:%d cam_vreg NULL\n", __func__, __LINE__);
+ return -EINVAL;
+ }
+
if (config) {
for (i = 0; i < num_vreg_seq; i++) {
if (vreg_seq) {
diff --git a/drivers/media/platform/msm/camera_v2/isp/msm_isp.c b/drivers/media/platform/msm/camera_v2/isp/msm_isp.c
index 7af9a3e..cfe8054 100644
--- a/drivers/media/platform/msm/camera_v2/isp/msm_isp.c
+++ b/drivers/media/platform/msm/camera_v2/isp/msm_isp.c
@@ -693,7 +693,7 @@
spin_lock_init(&req_history_lock);
spin_lock_init(&vfe_dev->completion_lock);
media_entity_pads_init(&vfe_dev->subdev.sd.entity, 0, NULL);
- vfe_dev->subdev.sd.entity.function = MEDIA_ENT_F_IO_V4L;
+ vfe_dev->subdev.sd.entity.function = MSM_CAMERA_SUBDEV_VFE;
//vfe_dev->subdev.sd.entity.group_id = MSM_CAMERA_SUBDEV_VFE;
vfe_dev->subdev.sd.entity.name = pdev->name;
vfe_dev->subdev.close_seq = MSM_SD_CLOSE_1ST_CATEGORY | 0x2;
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 0b7bb8d..fab2623f 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
@@ -935,8 +935,12 @@
}
if (frame_src == VFE_PIX_0) {
- vfe_dev->isp_page->kernel_sofid =
- vfe_dev->axi_data.src_info[frame_src].frame_id;
+ if (vfe_dev->isp_page == NULL)
+ pr_err("Invalid ISP PAGE");
+ else
+ vfe_dev->isp_page->kernel_sofid =
+ vfe_dev->axi_data.src_info[frame_src].frame_id;
+
if (!src_info->frame_id &&
!src_info->reg_update_frame_id &&
((src_info->frame_id -
diff --git a/drivers/media/platform/msm/camera_v2/msm.c b/drivers/media/platform/msm/camera_v2/msm.c
index 121e42a5..d52d8bc 100644
--- a/drivers/media/platform/msm/camera_v2/msm.c
+++ b/drivers/media/platform/msm/camera_v2/msm.c
@@ -350,8 +350,11 @@
return -EINVAL;
rc = v4l2_device_register_subdev(msm_v4l2_dev, sd);
- if (rc < 0)
+ if (rc < 0) {
+ pr_err("v4l2_device_register_subdev: failed for %s", sd->name);
+ WARN_ON(1);
return rc;
+ }
/* Register a device node for every subdev marked with the
* V4L2_SUBDEV_FL_HAS_DEVNODE flag.
@@ -1303,7 +1306,7 @@
uint64_t seq_num = 0;
int ret;
- if (copy_from_user(lbuf, buf, sizeof(lbuf)))
+ if (copy_from_user(lbuf, buf, sizeof(lbuf) - 1))
return -EFAULT;
ret = kstrtoull(lbuf, 0, &seq_num);
@@ -1366,8 +1369,7 @@
0, NULL)) < 0))
goto entity_fail;
- pvdev->vdev->entity.function = MEDIA_ENT_F_IO_V4L;
- //pvdev->vdev->entity.group_id = QCAMERA_VNODE_GROUP_ID;
+ pvdev->vdev->entity.function = QCAMERA_VNODE_GROUP_ID;
#endif
msm_v4l2_dev->notify = msm_sd_notify;
diff --git a/drivers/media/platform/msm/camera_v2/msm_buf_mgr/msm_generic_buf_mgr.c b/drivers/media/platform/msm/camera_v2/msm_buf_mgr/msm_generic_buf_mgr.c
index 8a91c3e..e350096 100644
--- a/drivers/media/platform/msm/camera_v2/msm_buf_mgr/msm_generic_buf_mgr.c
+++ b/drivers/media/platform/msm/camera_v2/msm_buf_mgr/msm_generic_buf_mgr.c
@@ -857,9 +857,8 @@
v4l2_set_subdevdata(&msm_buf_mngr_dev->subdev.sd, msm_buf_mngr_dev);
media_entity_pads_init(&msm_buf_mngr_dev->subdev.sd.entity, 0, NULL);
- msm_buf_mngr_dev->subdev.sd.entity.function = MEDIA_ENT_F_IO_V4L;
- //msm_buf_mngr_dev->subdev.sd.entity.group_id =
- // MSM_CAMERA_SUBDEV_BUF_MNGR;
+ msm_buf_mngr_dev->subdev.sd.entity.function =
+ MSM_CAMERA_SUBDEV_BUF_MNGR;
msm_buf_mngr_dev->subdev.sd.internal_ops =
&msm_generic_buf_mngr_subdev_internal_ops;
msm_buf_mngr_dev->subdev.close_seq = MSM_SD_CLOSE_4TH_CATEGORY;
diff --git a/drivers/media/platform/msm/camera_v2/msm_vb2/msm_vb2.c b/drivers/media/platform/msm/camera_v2/msm_vb2/msm_vb2.c
index 1322707..5c13de5 100644
--- a/drivers/media/platform/msm/camera_v2/msm_vb2/msm_vb2.c
+++ b/drivers/media/platform/msm/camera_v2/msm_vb2/msm_vb2.c
@@ -399,7 +399,7 @@
static int msm_vb2_buf_done(struct vb2_v4l2_buffer *vb, int session_id,
unsigned int stream_id, uint32_t sequence,
- struct timeval *ts, uint32_t reserved)
+ struct timeval *ts, uint32_t buf_type)
{
unsigned long flags, rl_flags;
struct msm_vb2_buffer *msm_vb2;
@@ -441,7 +441,9 @@
/* put buf before buf done */
if (msm_vb2->in_freeq) {
vb2_v4l2_buf->sequence = sequence;
- //vb2_v4l2_buf->timestamp = *ts;
+ vb2_v4l2_buf->timecode.type = buf_type;
+ vb2_v4l2_buf->vb2_buf.timestamp =
+ (ts->tv_sec * 1000000 + ts->tv_usec) * 1000;
vb2_buffer_done(&vb2_v4l2_buf->vb2_buf,
VB2_BUF_STATE_DONE);
msm_vb2->in_freeq = 0;
diff --git a/drivers/media/platform/msm/camera_v2/pproc/cpp/msm_cpp.c b/drivers/media/platform/msm/camera_v2/pproc/cpp/msm_cpp.c
index 6fd9c4d..70bb3f2 100644
--- a/drivers/media/platform/msm/camera_v2/pproc/cpp/msm_cpp.c
+++ b/drivers/media/platform/msm/camera_v2/pproc/cpp/msm_cpp.c
@@ -4579,15 +4579,6 @@
}
}
- if (of_find_property(pdev->dev.of_node, "qcom,cpp-cx-ipeak", NULL)) {
- cpp_dev->cpp_cx_ipeak = cx_ipeak_register(
- pdev->dev.of_node, "qcom,cpp-cx-ipeak");
- if (cpp_dev->cpp_cx_ipeak)
- CPP_DBG("Cx ipeak Registration Successful ");
- else
- pr_err("Cx ipeak Registration Unsuccessful");
- }
-
rc = msm_camera_get_reset_info(pdev,
&cpp_dev->micro_iface_reset);
if (rc < 0) {
@@ -4596,7 +4587,6 @@
__func__);
goto get_reg_err;
}
-
rc = msm_camera_get_regulator_info(pdev, &cpp_dev->cpp_vdd,
&cpp_dev->num_reg);
if (rc < 0) {
@@ -4625,7 +4615,7 @@
goto bus_de_init;
media_entity_pads_init(&cpp_dev->msm_sd.sd.entity, 0, NULL);
- cpp_dev->msm_sd.sd.entity.function = MEDIA_ENT_F_IO_V4L;
+ cpp_dev->msm_sd.sd.entity.function = MSM_CAMERA_SUBDEV_CPP;
cpp_dev->msm_sd.sd.entity.name = pdev->name;
cpp_dev->msm_sd.close_seq = MSM_SD_CLOSE_3RD_CATEGORY;
msm_sd_register(&cpp_dev->msm_sd);
diff --git a/drivers/media/platform/msm/camera_v2/pproc/cpp/msm_cpp_soc.c b/drivers/media/platform/msm/camera_v2/pproc/cpp/msm_cpp_soc.c
index d733d2a..64f3104 100644
--- a/drivers/media/platform/msm/camera_v2/pproc/cpp/msm_cpp_soc.c
+++ b/drivers/media/platform/msm/camera_v2/pproc/cpp/msm_cpp_soc.c
@@ -83,67 +83,37 @@
return -EINVAL;
}
-static int cpp_get_clk_freq_tbl_dt(struct cpp_device *cpp_dev)
+static int cpp_get_clk_freq_tbl(struct clk *clk, struct cpp_hw_info *hw_info,
+ uint32_t min_clk_rate)
{
- uint32_t i, count, min_clk_rate;
+ uint32_t i;
uint32_t idx = 0;
- struct device_node *of_node;
- uint32_t *rates;
- int32_t rc = 0;
- struct cpp_hw_info *hw_info;
+ signed long freq_tbl_entry = 0;
- if (cpp_dev == NULL) {
+ if ((clk == NULL) || (hw_info == NULL) || (clk->ops == NULL) ||
+ (clk->ops->list_rate == NULL)) {
pr_err("Bad parameter\n");
- rc = -EINVAL;
- goto err;
+ return -EINVAL;
}
- of_node = cpp_dev->pdev->dev.of_node;
- min_clk_rate = cpp_dev->min_clk_rate;
- hw_info = &cpp_dev->hw_info;
-
- if ((hw_info == NULL) || (of_node == NULL)) {
- pr_err("Invalid hw_info %pK or ofnode %pK\n", hw_info, of_node);
- rc = -EINVAL;
- goto err;
-
- }
- count = of_property_count_u32_elems(of_node, "qcom,src-clock-rates");
- if ((count == 0) || (count > MAX_FREQ_TBL)) {
- pr_err("Clock count is invalid\n");
- rc = -EINVAL;
- goto err;
- }
-
- rates = devm_kcalloc(&cpp_dev->pdev->dev, count, sizeof(uint32_t),
- GFP_KERNEL);
- if (!rates) {
- rc = -ENOMEM;
- goto err;
- }
-
- rc = of_property_read_u32_array(of_node, "qcom,src-clock-rates",
- rates, count);
- if (rc) {
- rc = -EINVAL;
- goto mem_free;
- }
-
- for (i = 0; i < count; i++) {
- pr_debug("entry=%d\n", rates[i]);
- if (rates[i] >= min_clk_rate) {
- hw_info->freq_tbl[idx++] = rates[i];
- pr_debug("tbl[%d]=%d\n", idx-1, rates[i]);
+ for (i = 0; i < MAX_FREQ_TBL; i++) {
+ freq_tbl_entry = clk->ops->list_rate(clk, i);
+ pr_debug("entry=%ld\n", freq_tbl_entry);
+ if (freq_tbl_entry >= 0) {
+ if (freq_tbl_entry >= min_clk_rate) {
+ hw_info->freq_tbl[idx++] = freq_tbl_entry;
+ pr_debug("tbl[%d]=%ld\n", idx-1,
+ freq_tbl_entry);
+ }
+ } else {
+ pr_debug("freq table returned invalid entry/end %ld\n",
+ freq_tbl_entry);
+ break;
}
}
-
- pr_debug("%s: idx %d\n", __func__, idx);
+ pr_debug("%s: idx %d", __func__, idx);
hw_info->freq_tbl_count = idx;
-
-mem_free:
- devm_kfree(&cpp_dev->pdev->dev, rates);
-err:
- return rc;
+ return 0;
}
int msm_cpp_set_micro_clk(struct cpp_device *cpp_dev)
@@ -192,7 +162,8 @@
rc = msm_cpp_core_clk_idx;
return rc;
}
- rc = cpp_get_clk_freq_tbl_dt(cpp_dev);
+ rc = cpp_get_clk_freq_tbl(cpp_dev->cpp_clk[msm_cpp_core_clk_idx],
+ &cpp_dev->hw_info, cpp_dev->min_clk_rate);
if (rc < 0) {
pr_err("%s: fail to get frequency table\n", __func__);
return rc;
diff --git a/drivers/media/platform/msm/camera_v2/sensor/actuator/msm_actuator.c b/drivers/media/platform/msm/camera_v2/sensor/actuator/msm_actuator.c
index 295e203..769a9a5 100644
--- a/drivers/media/platform/msm/camera_v2/sensor/actuator/msm_actuator.c
+++ b/drivers/media/platform/msm/camera_v2/sensor/actuator/msm_actuator.c
@@ -1895,7 +1895,7 @@
act_ctrl_t->msm_sd.sd.internal_ops = &msm_actuator_internal_ops;
act_ctrl_t->msm_sd.sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
media_entity_pads_init(&act_ctrl_t->msm_sd.sd.entity, 0, NULL);
- act_ctrl_t->msm_sd.sd.entity.function = MEDIA_ENT_F_IO_V4L;
+ act_ctrl_t->msm_sd.sd.entity.function = MSM_CAMERA_SUBDEV_ACTUATOR;
act_ctrl_t->msm_sd.close_seq = MSM_SD_CLOSE_2ND_CATEGORY | 0x2;
msm_sd_register(&act_ctrl_t->msm_sd);
msm_cam_copy_v4l2_subdev_fops(&msm_actuator_v4l2_subdev_fops);
@@ -2007,7 +2007,7 @@
snprintf(msm_actuator_t->msm_sd.sd.name,
ARRAY_SIZE(msm_actuator_t->msm_sd.sd.name), "msm_actuator");
media_entity_pads_init(&msm_actuator_t->msm_sd.sd.entity, 0, NULL);
- msm_actuator_t->msm_sd.sd.entity.function = MEDIA_ENT_F_IO_V4L;
+ msm_actuator_t->msm_sd.sd.entity.function = MSM_CAMERA_SUBDEV_ACTUATOR;
msm_actuator_t->msm_sd.close_seq = MSM_SD_CLOSE_2ND_CATEGORY | 0x2;
msm_sd_register(&msm_actuator_t->msm_sd);
msm_actuator_t->actuator_state = ACT_DISABLE_STATE;
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 0f4bd88..b67af9c 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
@@ -2095,11 +2095,11 @@
return -ENOMEM;
}
v4l2_subdev_init(&new_cci_dev->msm_sd.sd, &msm_cci_subdev_ops);
- new_cci_dev->msm_sd.sd.internal_ops = &msm_cci_internal_ops;
snprintf(new_cci_dev->msm_sd.sd.name,
ARRAY_SIZE(new_cci_dev->msm_sd.sd.name), "msm_cci");
v4l2_set_subdevdata(&new_cci_dev->msm_sd.sd, new_cci_dev);
platform_set_drvdata(pdev, &new_cci_dev->msm_sd.sd);
+
CDBG("%s sd %pK\n", __func__, &new_cci_dev->msm_sd.sd);
if (pdev->dev.of_node)
of_property_read_u32((&pdev->dev)->of_node,
@@ -2141,6 +2141,13 @@
}
msm_camera_enable_irq(new_cci_dev->irq, false);
+
+ new_cci_dev->pdev = pdev;
+ new_cci_dev->msm_sd.sd.internal_ops = &msm_cci_internal_ops;
+ new_cci_dev->msm_sd.sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
+ media_entity_pads_init(&new_cci_dev->msm_sd.sd.entity, 0, NULL);
+ new_cci_dev->msm_sd.sd.entity.function = MSM_CAMERA_SUBDEV_CCI;
+ new_cci_dev->msm_sd.sd.entity.name = new_cci_dev->msm_sd.sd.name;
new_cci_dev->msm_sd.close_seq = MSM_SD_CLOSE_2ND_CATEGORY | 0x6;
msm_sd_register(&new_cci_dev->msm_sd);
new_cci_dev->pdev = pdev;
diff --git a/drivers/media/platform/msm/camera_v2/sensor/csid/msm_csid.c b/drivers/media/platform/msm/camera_v2/sensor/csid/msm_csid.c
index 057472a..ba32526 100644
--- a/drivers/media/platform/msm/camera_v2/sensor/csid/msm_csid.c
+++ b/drivers/media/platform/msm/camera_v2/sensor/csid/msm_csid.c
@@ -1008,6 +1008,8 @@
if (!new_csid_dev)
return -ENOMEM;
+ CDBG("%s: csid_probe entry\n", __func__);
+
new_csid_dev->csid_3p_enabled = 0;
new_csid_dev->ctrl_reg = NULL;
new_csid_dev->ctrl_reg = kzalloc(sizeof(struct csid_ctrl_t),
@@ -1088,7 +1090,7 @@
snprintf(new_csid_dev->msm_sd.sd.name,
ARRAY_SIZE(new_csid_dev->msm_sd.sd.name), "msm_csid");
media_entity_pads_init(&new_csid_dev->msm_sd.sd.entity, 0, NULL);
- new_csid_dev->msm_sd.sd.entity.function = MEDIA_ENT_F_IO_V4L;
+ new_csid_dev->msm_sd.sd.entity.function = MSM_CAMERA_SUBDEV_CSID;
new_csid_dev->msm_sd.close_seq = MSM_SD_CLOSE_2ND_CATEGORY | 0x5;
msm_sd_register(&new_csid_dev->msm_sd);
diff --git a/drivers/media/platform/msm/camera_v2/sensor/csiphy/msm_csiphy.c b/drivers/media/platform/msm/camera_v2/sensor/csiphy/msm_csiphy.c
index 04f7732..edf022e 100644
--- a/drivers/media/platform/msm/camera_v2/sensor/csiphy/msm_csiphy.c
+++ b/drivers/media/platform/msm/camera_v2/sensor/csiphy/msm_csiphy.c
@@ -1743,7 +1743,7 @@
snprintf(new_csiphy_dev->msm_sd.sd.name,
ARRAY_SIZE(new_csiphy_dev->msm_sd.sd.name), "msm_csiphy");
media_entity_pads_init(&new_csiphy_dev->msm_sd.sd.entity, 0, NULL);
- new_csiphy_dev->msm_sd.sd.entity.function = MEDIA_ENT_F_IO_V4L;
+ new_csiphy_dev->msm_sd.sd.entity.function = MSM_CAMERA_SUBDEV_CSIPHY;
new_csiphy_dev->msm_sd.close_seq = MSM_SD_CLOSE_2ND_CATEGORY | 0x4;
msm_sd_register(&new_csiphy_dev->msm_sd);
diff --git a/drivers/media/platform/msm/camera_v2/sensor/eeprom/msm_eeprom.c b/drivers/media/platform/msm/camera_v2/sensor/eeprom/msm_eeprom.c
index 30a3113..cd16236 100644
--- a/drivers/media/platform/msm/camera_v2/sensor/eeprom/msm_eeprom.c
+++ b/drivers/media/platform/msm/camera_v2/sensor/eeprom/msm_eeprom.c
@@ -847,7 +847,7 @@
e_ctrl->msm_sd.sd.internal_ops = &msm_eeprom_internal_ops;
e_ctrl->msm_sd.sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
media_entity_pads_init(&e_ctrl->msm_sd.sd.entity, 0, NULL);
- e_ctrl->msm_sd.sd.entity.function = MEDIA_ENT_F_IO_V4L;
+ e_ctrl->msm_sd.sd.entity.function = MSM_CAMERA_SUBDEV_EEPROM;
msm_sd_register(&e_ctrl->msm_sd);
CDBG("%s success result=%d X\n", __func__, rc);
return rc;
@@ -1215,7 +1215,7 @@
e_ctrl->msm_sd.sd.internal_ops = &msm_eeprom_internal_ops;
e_ctrl->msm_sd.sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
media_entity_pads_init(&e_ctrl->msm_sd.sd.entity, 0, NULL);
- e_ctrl->msm_sd.sd.entity.function = MEDIA_ENT_F_IO_V4L;
+ e_ctrl->msm_sd.sd.entity.function = MSM_CAMERA_SUBDEV_EEPROM;
msm_sd_register(&e_ctrl->msm_sd);
e_ctrl->is_supported = (e_ctrl->is_supported << 1) | 1;
CDBG("%s success result=%d supported=%x X\n", __func__, rc,
@@ -1755,7 +1755,7 @@
snprintf(e_ctrl->msm_sd.sd.name,
ARRAY_SIZE(e_ctrl->msm_sd.sd.name), "msm_eeprom");
media_entity_pads_init(&e_ctrl->msm_sd.sd.entity, 0, NULL);
- e_ctrl->msm_sd.sd.entity.function = MEDIA_ENT_F_IO_V4L;
+ e_ctrl->msm_sd.sd.entity.function = MSM_CAMERA_SUBDEV_EEPROM;
msm_sd_register(&e_ctrl->msm_sd);
#ifdef CONFIG_COMPAT
diff --git a/drivers/media/platform/msm/camera_v2/sensor/flash/msm_flash.c b/drivers/media/platform/msm/camera_v2/sensor/flash/msm_flash.c
index b26fee4..656b1ca 100644
--- a/drivers/media/platform/msm/camera_v2/sensor/flash/msm_flash.c
+++ b/drivers/media/platform/msm/camera_v2/sensor/flash/msm_flash.c
@@ -1314,7 +1314,7 @@
ARRAY_SIZE(flash_ctrl->msm_sd.sd.name),
"msm_camera_flash");
media_entity_pads_init(&flash_ctrl->msm_sd.sd.entity, 0, NULL);
- flash_ctrl->msm_sd.sd.entity.function = MEDIA_ENT_F_IO_V4L;
+ flash_ctrl->msm_sd.sd.entity.function = MSM_CAMERA_SUBDEV_FLASH;
flash_ctrl->msm_sd.close_seq = MSM_SD_CLOSE_2ND_CATEGORY | 0x1;
msm_sd_register(&flash_ctrl->msm_sd);
diff --git a/drivers/media/platform/msm/camera_v2/sensor/ir_cut/msm_ir_cut.c b/drivers/media/platform/msm/camera_v2/sensor/ir_cut/msm_ir_cut.c
index 165f0c3..002a9b9 100644
--- a/drivers/media/platform/msm/camera_v2/sensor/ir_cut/msm_ir_cut.c
+++ b/drivers/media/platform/msm/camera_v2/sensor/ir_cut/msm_ir_cut.c
@@ -601,7 +601,7 @@
ARRAY_SIZE(ir_cut_ctrl->msm_sd.sd.name),
"msm_camera_ir_cut");
media_entity_pads_init(&ir_cut_ctrl->msm_sd.sd.entity, 0, NULL);
- ir_cut_ctrl->msm_sd.sd.entity.function = MEDIA_ENT_F_IO_V4L;
+ ir_cut_ctrl->msm_sd.sd.entity.function = MSM_CAMERA_SUBDEV_IR_CUT;
ir_cut_ctrl->msm_sd.close_seq = MSM_SD_CLOSE_2ND_CATEGORY | 0x1;
msm_sd_register(&ir_cut_ctrl->msm_sd);
diff --git a/drivers/media/platform/msm/camera_v2/sensor/ir_led/msm_ir_led.c b/drivers/media/platform/msm/camera_v2/sensor/ir_led/msm_ir_led.c
index 00ce821..2beca58 100644
--- a/drivers/media/platform/msm/camera_v2/sensor/ir_led/msm_ir_led.c
+++ b/drivers/media/platform/msm/camera_v2/sensor/ir_led/msm_ir_led.c
@@ -398,7 +398,7 @@
ARRAY_SIZE(ir_led_ctrl->msm_sd.sd.name),
"msm_camera_ir_led");
media_entity_pads_init(&ir_led_ctrl->msm_sd.sd.entity, 0, NULL);
- ir_led_ctrl->msm_sd.sd.entity.function = MEDIA_ENT_F_IO_V4L;
+ ir_led_ctrl->msm_sd.sd.entity.function = MSM_CAMERA_SUBDEV_IR_LED;
ir_led_ctrl->msm_sd.close_seq = MSM_SD_CLOSE_2ND_CATEGORY | 0x1;
msm_sd_register(&ir_led_ctrl->msm_sd);
diff --git a/drivers/media/platform/msm/camera_v2/sensor/laser_led/msm_laser_led.c b/drivers/media/platform/msm/camera_v2/sensor/laser_led/msm_laser_led.c
index d30fc95..ee695d8 100644
--- a/drivers/media/platform/msm/camera_v2/sensor/laser_led/msm_laser_led.c
+++ b/drivers/media/platform/msm/camera_v2/sensor/laser_led/msm_laser_led.c
@@ -515,11 +515,7 @@
ARRAY_SIZE(laser_led_ctrl->msm_sd.sd.name),
"msm_camera_laser_led");
media_entity_pads_init(&laser_led_ctrl->msm_sd.sd.entity, 0, NULL);
- laser_led_ctrl->msm_sd.sd.entity.function = MEDIA_ENT_F_IO_V4L;
- /*
- * laser_led_ctrl->msm_sd.sd.entity.group_id
- * = MSM_CAMERA_SUBDEV_LASER_LED;
- */
+ laser_led_ctrl->msm_sd.sd.entity.function = MSM_CAMERA_SUBDEV_LASER_LED;
laser_led_ctrl->msm_sd.close_seq = MSM_SD_CLOSE_2ND_CATEGORY | 0x1;
msm_sd_register(&laser_led_ctrl->msm_sd);
diff --git a/drivers/media/platform/msm/camera_v2/sensor/msm_sensor_driver.c b/drivers/media/platform/msm/camera_v2/sensor/msm_sensor_driver.c
index 80149a9..0b0f98a 100644
--- a/drivers/media/platform/msm/camera_v2/sensor/msm_sensor_driver.c
+++ b/drivers/media/platform/msm/camera_v2/sensor/msm_sensor_driver.c
@@ -104,7 +104,7 @@
v4l2_set_subdevdata(&s_ctrl->msm_sd.sd, client);
s_ctrl->msm_sd.sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
media_entity_pads_init(&s_ctrl->msm_sd.sd.entity, 0, NULL);
- s_ctrl->msm_sd.sd.entity.function = MEDIA_ENT_F_IO_V4L;
+ s_ctrl->msm_sd.sd.entity.function = MSM_CAMERA_SUBDEV_SENSOR;
s_ctrl->msm_sd.sd.entity.name = s_ctrl->msm_sd.sd.name;
s_ctrl->sensordata->sensor_info->session_id = session_id;
s_ctrl->msm_sd.close_seq = MSM_SD_CLOSE_2ND_CATEGORY | 0x3;
@@ -148,7 +148,7 @@
v4l2_set_subdevdata(&s_ctrl->msm_sd.sd, s_ctrl->pdev);
s_ctrl->msm_sd.sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
media_entity_pads_init(&s_ctrl->msm_sd.sd.entity, 0, NULL);
- s_ctrl->msm_sd.sd.entity.function = MEDIA_ENT_F_IO_V4L;
+ s_ctrl->msm_sd.sd.entity.function = MSM_CAMERA_SUBDEV_SENSOR;
s_ctrl->msm_sd.sd.entity.name = s_ctrl->msm_sd.sd.name;
s_ctrl->msm_sd.close_seq = MSM_SD_CLOSE_2ND_CATEGORY | 0x3;
rc = msm_sd_register(&s_ctrl->msm_sd);
diff --git a/drivers/media/platform/msm/camera_v2/sensor/msm_sensor_init.c b/drivers/media/platform/msm/camera_v2/sensor/msm_sensor_init.c
index b9fafc8..acab45e 100644
--- a/drivers/media/platform/msm/camera_v2/sensor/msm_sensor_init.c
+++ b/drivers/media/platform/msm/camera_v2/sensor/msm_sensor_init.c
@@ -187,7 +187,7 @@
s_init->msm_sd.sd.internal_ops = &msm_sensor_init_internal_ops;
s_init->msm_sd.sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
media_entity_pads_init(&s_init->msm_sd.sd.entity, 0, NULL);
- s_init->msm_sd.sd.entity.function = MEDIA_ENT_F_IO_V4L;
+ s_init->msm_sd.sd.entity.function = MSM_CAMERA_SUBDEV_SENSOR_INIT;
s_init->msm_sd.sd.entity.name = s_init->msm_sd.sd.name;
s_init->msm_sd.close_seq = MSM_SD_CLOSE_2ND_CATEGORY | 0x6;
ret = msm_sd_register(&s_init->msm_sd);
diff --git a/drivers/media/platform/msm/camera_v2/sensor/ois/msm_ois.c b/drivers/media/platform/msm/camera_v2/sensor/ois/msm_ois.c
index 2253eee..0e17b7d 100644
--- a/drivers/media/platform/msm/camera_v2/sensor/ois/msm_ois.c
+++ b/drivers/media/platform/msm/camera_v2/sensor/ois/msm_ois.c
@@ -830,7 +830,7 @@
ois_ctrl_t->msm_sd.sd.internal_ops = &msm_ois_internal_ops;
ois_ctrl_t->msm_sd.sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
media_entity_pads_init(&ois_ctrl_t->msm_sd.sd.entity, 0, NULL);
- ois_ctrl_t->msm_sd.sd.entity.function = MEDIA_ENT_F_IO_V4L;
+ ois_ctrl_t->msm_sd.sd.entity.function = MSM_CAMERA_SUBDEV_OIS;
ois_ctrl_t->msm_sd.close_seq = MSM_SD_CLOSE_2ND_CATEGORY | 0x2;
msm_sd_register(&ois_ctrl_t->msm_sd);
ois_ctrl_t->ois_state = OIS_DISABLE_STATE;
@@ -1032,7 +1032,7 @@
snprintf(msm_ois_t->msm_sd.sd.name,
ARRAY_SIZE(msm_ois_t->msm_sd.sd.name), "msm_ois");
media_entity_pads_init(&msm_ois_t->msm_sd.sd.entity, 0, NULL);
- msm_ois_t->msm_sd.sd.entity.function = MEDIA_ENT_F_IO_V4L;
+ msm_ois_t->msm_sd.sd.entity.function = MSM_CAMERA_SUBDEV_OIS;
msm_ois_t->msm_sd.close_seq = MSM_SD_CLOSE_2ND_CATEGORY | 0x2;
msm_sd_register(&msm_ois_t->msm_sd);
msm_ois_t->ois_state = OIS_DISABLE_STATE;
diff --git a/drivers/media/platform/msm/vidc_3x/msm_vidc.c b/drivers/media/platform/msm/vidc_3x/msm_vidc.c
index 7b22511..16db81c 100644
--- a/drivers/media/platform/msm/vidc_3x/msm_vidc.c
+++ b/drivers/media/platform/msm/vidc_3x/msm_vidc.c
@@ -81,16 +81,24 @@
int msm_vidc_querycap(void *instance, struct v4l2_capability *cap)
{
+ int rc = -EINVAL;
struct msm_vidc_inst *inst = instance;
if (!inst || !cap)
return -EINVAL;
if (inst->session_type == MSM_VIDC_DECODER)
- return msm_vdec_querycap(instance, cap);
+ rc = msm_vdec_querycap(instance, cap);
else if (inst->session_type == MSM_VIDC_ENCODER)
- return msm_venc_querycap(instance, cap);
- return -EINVAL;
+ rc = msm_venc_querycap(instance, cap);
+ else
+ goto exit;
+ if (!rc) {
+ cap->device_caps = cap->capabilities;
+ cap->capabilities |= V4L2_CAP_DEVICE_CAPS;
+ }
+exit:
+ return rc;
}
EXPORT_SYMBOL(msm_vidc_querycap);
@@ -1067,6 +1075,7 @@
q->ops = msm_venc_get_vb2q_ops();
q->mem_ops = &msm_vidc_vb2_mem_ops;
q->drv_priv = inst;
+ q->allow_zero_bytesused = 1;
return vb2_queue_init(q);
}
diff --git a/drivers/media/platform/soc_camera/soc_scale_crop.c b/drivers/media/platform/soc_camera/soc_scale_crop.c
index f77252d..d29c248 100644
--- a/drivers/media/platform/soc_camera/soc_scale_crop.c
+++ b/drivers/media/platform/soc_camera/soc_scale_crop.c
@@ -418,3 +418,7 @@
mf->height = soc_camera_shift_scale(rect->height, shift, scale_v);
}
EXPORT_SYMBOL(soc_camera_calc_client_output);
+
+MODULE_DESCRIPTION("soc-camera scaling-cropping functions");
+MODULE_AUTHOR("Guennadi Liakhovetski <kernel@pengutronix.de>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/v4l2-core/v4l2-ctrls.c b/drivers/media/v4l2-core/v4l2-ctrls.c
index 98e5e3b..fa14101 100644
--- a/drivers/media/v4l2-core/v4l2-ctrls.c
+++ b/drivers/media/v4l2-core/v4l2-ctrls.c
@@ -307,6 +307,7 @@
static const char * const header_mode[] = {
"Separate Buffer",
"Joined With 1st Frame",
+ "Joined with I Frame",
NULL,
};
static const char * const multi_slice[] = {
diff --git a/drivers/media/v4l2-core/v4l2-ioctl.c b/drivers/media/v4l2-core/v4l2-ioctl.c
index 0e48938..5a12b41 100644
--- a/drivers/media/v4l2-core/v4l2-ioctl.c
+++ b/drivers/media/v4l2-core/v4l2-ioctl.c
@@ -1371,6 +1371,12 @@
descr = "VP9"; break;
case V4L2_PIX_FMT_TME:
descr = "TME"; break;
+ case V4L2_PIX_FMT_HEVC_HYBRID:
+ descr = "HEVC Hybrid"; break;
+ case V4L2_PIX_FMT_DIVX_311:
+ descr = "DIVX311"; break;
+ case V4L2_PIX_FMT_DIVX:
+ descr = "DIVX"; break;
default:
WARN(1, "Unknown pixelformat 0x%08x\n", fmt->pixelformat);
if (fmt->description[0])
diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile
index 744001a..fb07ce4 100644
--- a/drivers/misc/Makefile
+++ b/drivers/misc/Makefile
@@ -49,7 +49,8 @@
obj-$(CONFIG_SRAM) += sram.o
obj-y += mic/
obj-$(CONFIG_GENWQE) += genwqe/
-obj-$(CONFIG_HDCP_QSEECOM) += hdcp.o
+obj-$(CONFIG_HDCP_QSEECOM) += hdcp_qseecom.o
+obj-$(CONFIG_HDCP_QSEECOM) += msm_hdcp.o
obj-$(CONFIG_QSEECOM) += qseecom.o
obj-$(CONFIG_ECHO) += echo/
obj-$(CONFIG_VEXPRESS_SYSCFG) += vexpress-syscfg.o
diff --git a/drivers/misc/hdcp.c b/drivers/misc/hdcp_qseecom.c
similarity index 87%
rename from drivers/misc/hdcp.c
rename to drivers/misc/hdcp_qseecom.c
index a86d9f1..6ce3776 100644
--- a/drivers/misc/hdcp.c
+++ b/drivers/misc/hdcp_qseecom.c
@@ -38,8 +38,6 @@
#include "qseecom_kernel.h"
-#define CLASS_NAME "hdcp"
-#define DRIVER_NAME "msm_hdcp"
#define TZAPP_NAME "hdcp2p2"
#define HDCP1_APP_NAME "hdcp1"
#define QSEECOM_SBUFF_SIZE 0x1000
@@ -509,21 +507,6 @@
const char *msg_name;
};
-struct msm_hdcp_mgr {
- struct platform_device *pdev;
- dev_t dev_num;
- struct cdev cdev;
- struct class *class;
- struct device *device;
- struct HDCP_V2V1_MSG_TOPOLOGY cached_tp;
- u32 tp_msgid;
- void *client_ctx;
- struct hdcp_lib_handle *handle;
-};
-
-static struct msm_hdcp_mgr *hdcp_drv_mgr;
-static struct hdcp_lib_handle *drv_client_handle;
-
static void hdcp_lib_clean(struct hdcp_lib_handle *handle);
static void hdcp_lib_init(struct hdcp_lib_handle *handle);
static void hdcp_lib_msg_sent(struct hdcp_lib_handle *handle);
@@ -751,7 +734,7 @@
QSEECOM_ALIGN(sizeof
(struct hdcp_set_hw_key_rsp)));
- if ((rc < 0) || (rsp_buf->status < 0)) {
+ if ((rc < 0) || (rsp_buf->status != HDCP_SUCCESS)) {
pr_err("qseecom cmd failed with err = %d status = %d\n",
rc, rsp_buf->status);
rc = -EINVAL;
@@ -1125,7 +1108,7 @@
(struct
hdcp_lib_session_deinit_rsp)));
- if ((rc < 0) || (rsp_buf->status < 0) ||
+ if ((rc < 0) || (rsp_buf->status != HDCP_SUCCESS) ||
(rsp_buf->commandid != HDCP_SESSION_DEINIT)) {
pr_err("qseecom cmd failed with err = %d status = %d\n",
rc, rsp_buf->status);
@@ -1231,7 +1214,7 @@
QSEECOM_ALIGN(sizeof
(struct hdcp_deinit_rsp)));
- if ((rc < 0) || (rsp_buf->status < 0) ||
+ if ((rc < 0) || (rsp_buf->status != HDCP_SUCCESS) ||
(rsp_buf->commandid != HDCP_TXMTR_DEINIT)) {
pr_err("qseecom cmd failed with err = %d status = %d\n",
rc, rsp_buf->status);
@@ -1285,7 +1268,7 @@
if ((rc < 0) || (rsp_buf->status != HDCP_SUCCESS) ||
(rsp_buf->commandid != HDCP_TXMTR_START_AUTHENTICATE) ||
- (rsp_buf->msglen <= 0) || (rsp_buf->message == NULL)) {
+ (rsp_buf->msglen == 0) || (rsp_buf->message == NULL)) {
pr_err("qseecom cmd failed with err = %d, status = %d\n",
rc, rsp_buf->status);
rc = -EINVAL;
@@ -1352,9 +1335,10 @@
(struct
hdcp_query_stream_type_rsp)));
- if ((rc < 0) || (rsp_buf->status < 0) || (rsp_buf->msglen <= 0) ||
- (rsp_buf->commandid != HDCP_TXMTR_QUERY_STREAM_TYPE) ||
- (rsp_buf->msg == NULL)) {
+ if ((rc < 0) || (rsp_buf->status != HDCP_SUCCESS) ||
+ (rsp_buf->msglen == 0) ||
+ (rsp_buf->commandid != HDCP_TXMTR_QUERY_STREAM_TYPE) ||
+ (rsp_buf->msg == NULL)) {
pr_err("qseecom cmd failed with err=%d status=%d\n",
rc, rsp_buf->status);
rc = -EINVAL;
@@ -1851,7 +1835,7 @@
mutex_lock(&handle->msg_lock);
msglen = handle->last_msg_recvd_len;
- if (msglen <= 0) {
+ if (msglen == 0) {
pr_err("invalid msg len\n");
mutex_unlock(&handle->msg_lock);
rc = -EINVAL;
@@ -1937,9 +1921,10 @@
goto exit;
}
- if ((rc < 0) || (rsp_buf->status != 0) || (rsp_buf->msglen <= 0) ||
- (rsp_buf->commandid != HDCP_TXMTR_PROCESS_RECEIVED_MESSAGE) ||
- (rsp_buf->msg == NULL)) {
+ if ((rc < 0) || (rsp_buf->status != HDCP_SUCCESS) ||
+ (rsp_buf->msglen == 0) ||
+ (rsp_buf->commandid != HDCP_TXMTR_PROCESS_RECEIVED_MESSAGE) ||
+ (rsp_buf->msg == NULL)) {
pr_err("qseecom cmd failed with err=%d status=%d\n",
rc, rsp_buf->status);
rc = -EINVAL;
@@ -2089,14 +2074,10 @@
}
/* copy bytes into msb and lsb */
- *aksv_msb = key_set_rsp->ksv[0] << 24;
- *aksv_msb |= key_set_rsp->ksv[1] << 16;
- *aksv_msb |= key_set_rsp->ksv[2] << 8;
- *aksv_msb |= key_set_rsp->ksv[3];
- *aksv_lsb = key_set_rsp->ksv[4] << 24;
- *aksv_lsb |= key_set_rsp->ksv[5] << 16;
- *aksv_lsb |= key_set_rsp->ksv[6] << 8;
- *aksv_lsb |= key_set_rsp->ksv[7];
+ *aksv_msb = key_set_rsp->ksv[0] << 24 | key_set_rsp->ksv[1] << 16 |
+ key_set_rsp->ksv[2] << 8 | key_set_rsp->ksv[3];
+ *aksv_lsb = key_set_rsp->ksv[4] << 24 | key_set_rsp->ksv[5] << 16 |
+ key_set_rsp->ksv[6] << 8 | key_set_rsp->ksv[7];
return 0;
}
@@ -2217,23 +2198,6 @@
*data->hdcp_ctx = handle;
- /* Cache the client ctx to be used later if
- * Misc HDCP driver probe happens later than
- * SDE driver probe hence caching it to
- * be used later.
- */
- drv_client_handle = handle;
-
- /* if misc HDCP driver probe happens earlier
- * than SDE driver probe store the client
- * handle to be used to sysfs notifications.
- */
-
- if (hdcp_drv_mgr && !hdcp_drv_mgr->handle) {
- if (drv_client_handle)
- hdcp_drv_mgr->handle = drv_client_handle;
- }
-
handle->thread = kthread_run(kthread_worker_fn,
&handle->worker, "hdcp_tz_lib");
@@ -2274,287 +2238,3 @@
}
EXPORT_SYMBOL(hdcp_library_deregister);
-void hdcp1_notify_topology(void)
-{
- char *envp[4];
- char *a;
- char *b;
-
- if (!hdcp_drv_mgr) {
- pr_err("invalid input\n");
- return;
- }
-
- a = kzalloc(SZ_16, GFP_KERNEL);
-
- if (!a)
- return;
-
- b = kzalloc(SZ_16, GFP_KERNEL);
-
- if (!b) {
- kfree(a);
- return;
- }
-
- envp[0] = "HDCP_MGR_EVENT=MSG_READY";
- envp[1] = a;
- envp[2] = b;
- envp[3] = NULL;
-
- snprintf(envp[1], 16, "%d", (int)DOWN_CHECK_TOPOLOGY);
- snprintf(envp[2], 16, "%d", (int)HDCP_V1_TX);
-
- kobject_uevent_env(&hdcp_drv_mgr->device->kobj, KOBJ_CHANGE, envp);
- kfree(a);
- kfree(b);
-}
-
-static ssize_t msm_hdcp_1x_sysfs_rda_tp(struct device *dev,
-struct device_attribute *attr, char *buf)
-{
- ssize_t ret = 0;
-
- if (!hdcp_drv_mgr) {
- pr_err("invalid input\n");
- return -EINVAL;
- }
-
- switch (hdcp_drv_mgr->tp_msgid) {
- case DOWN_CHECK_TOPOLOGY:
- case DOWN_REQUEST_TOPOLOGY:
- buf[MSG_ID_IDX] = hdcp_drv_mgr->tp_msgid;
- buf[RET_CODE_IDX] = HDCP_AUTHED;
- ret = HEADER_LEN;
-
- memcpy(buf + HEADER_LEN, &hdcp_drv_mgr->cached_tp,
- sizeof(struct HDCP_V2V1_MSG_TOPOLOGY));
-
- ret += sizeof(struct HDCP_V2V1_MSG_TOPOLOGY);
-
- /* clear the flag once data is read back to user space*/
- hdcp_drv_mgr->tp_msgid = -1;
- break;
- default:
- ret = -EINVAL;
- }
-
- return ret;
-}
-
-static ssize_t msm_hdcp_1x_sysfs_wta_tp(struct device *dev,
- struct device_attribute *attr, const char *buf, size_t count)
-{
- int msgid = 0;
- ssize_t ret = count;
-
- if (!hdcp_drv_mgr || !buf) {
- pr_err("invalid input\n");
- return -EINVAL;
- }
-
- msgid = buf[0];
-
- switch (msgid) {
- case DOWN_CHECK_TOPOLOGY:
- case DOWN_REQUEST_TOPOLOGY:
- hdcp_drv_mgr->tp_msgid = msgid;
- break;
- /* more cases added here */
- default:
- ret = -EINVAL;
- }
-
- return ret;
-}
-
-static ssize_t hdcp2p2_sysfs_wta_min_level_change(struct device *dev,
- struct device_attribute *attr, const char *buf, size_t count)
-{
- int rc;
- int min_enc_lvl;
- struct hdcp_lib_handle *handle;
- ssize_t ret = count;
-
- if (!hdcp_drv_mgr) {
- pr_err("invalid input\n");
- return -EINVAL;
- }
-
- handle = hdcp_drv_mgr->handle;
-
- rc = kstrtoint(buf, 10, &min_enc_lvl);
- if (rc) {
- pr_err("%s: kstrtoint failed. rc=%d\n", __func__, rc);
- return -EINVAL;
- }
-
- if (handle && handle->client_ops->notify_lvl_change) {
- handle->client_ops->notify_lvl_change(handle->client_ctx,
- min_enc_lvl);
- }
-
- return ret;
-}
-
-static DEVICE_ATTR(tp, 0644, msm_hdcp_1x_sysfs_rda_tp,
- msm_hdcp_1x_sysfs_wta_tp);
-
-static DEVICE_ATTR(min_level_change, 0200, NULL,
- hdcp2p2_sysfs_wta_min_level_change);
-
-void hdcp1_cache_repeater_topology(void *hdcp1_cached_tp)
-{
- if (!hdcp_drv_mgr) {
- pr_err("invalid input\n");
- return;
- }
-
- memcpy((void *)&hdcp_drv_mgr->cached_tp,
- hdcp1_cached_tp,
- sizeof(struct HDCP_V2V1_MSG_TOPOLOGY));
-}
-
-static struct attribute *msm_hdcp_fs_attrs[] = {
- &dev_attr_tp.attr,
- &dev_attr_min_level_change.attr,
- NULL
-};
-
-static struct attribute_group msm_hdcp_fs_attr_group = {
- .attrs = msm_hdcp_fs_attrs
-};
-
-static int msm_hdcp_open(struct inode *inode, struct file *file)
-{
- return 0;
-}
-
-static int msm_hdcp_close(struct inode *inode, struct file *file)
-{
- return 0;
-}
-
-static const struct file_operations msm_hdcp_fops = {
- .owner = THIS_MODULE,
- .open = msm_hdcp_open,
- .release = msm_hdcp_close,
-};
-
-static const struct of_device_id msm_hdcp_dt_match[] = {
- { .compatible = "qcom,msm-hdcp",},
- {}
-};
-
-MODULE_DEVICE_TABLE(of, msm_hdcp_dt_match);
-
-static int msm_hdcp_probe(struct platform_device *pdev)
-{
- int ret;
-
- hdcp_drv_mgr = devm_kzalloc(&pdev->dev, sizeof(struct msm_hdcp_mgr),
- GFP_KERNEL);
- if (!hdcp_drv_mgr)
- return -ENOMEM;
-
- hdcp_drv_mgr->pdev = pdev;
-
- platform_set_drvdata(pdev, hdcp_drv_mgr);
-
- ret = alloc_chrdev_region(&hdcp_drv_mgr->dev_num, 0, 1, DRIVER_NAME);
- if (ret < 0) {
- pr_err("alloc_chrdev_region failed ret = %d\n", ret);
- goto error_get_dev_num;
- }
-
- hdcp_drv_mgr->class = class_create(THIS_MODULE, CLASS_NAME);
- if (IS_ERR(hdcp_drv_mgr->class)) {
- ret = PTR_ERR(hdcp_drv_mgr->class);
- pr_err("couldn't create class rc = %d\n", ret);
- goto error_class_create;
- }
-
- hdcp_drv_mgr->device = device_create(hdcp_drv_mgr->class, NULL,
- hdcp_drv_mgr->dev_num, NULL, DRIVER_NAME);
- if (IS_ERR(hdcp_drv_mgr->device)) {
- ret = PTR_ERR(hdcp_drv_mgr->device);
- pr_err("device_create failed %d\n", ret);
- goto error_class_device_create;
- }
-
- cdev_init(&hdcp_drv_mgr->cdev, &msm_hdcp_fops);
- ret = cdev_add(&hdcp_drv_mgr->cdev,
- MKDEV(MAJOR(hdcp_drv_mgr->dev_num), 0), 1);
- if (ret < 0) {
- pr_err("cdev_add failed %d\n", ret);
- goto error_cdev_add;
- }
-
- ret = sysfs_create_group(&hdcp_drv_mgr->device->kobj,
- &msm_hdcp_fs_attr_group);
- if (ret)
- pr_err("unable to register rotator sysfs nodes\n");
-
- /* Store the handle in the hdcp drv mgr
- * to be used for the sysfs notifications
- */
- hdcp_drv_mgr->handle = drv_client_handle;
-
- return 0;
-error_cdev_add:
- device_destroy(hdcp_drv_mgr->class, hdcp_drv_mgr->dev_num);
-error_class_device_create:
- class_destroy(hdcp_drv_mgr->class);
-error_class_create:
- unregister_chrdev_region(hdcp_drv_mgr->dev_num, 1);
-error_get_dev_num:
- devm_kfree(&pdev->dev, hdcp_drv_mgr);
- hdcp_drv_mgr = NULL;
- return ret;
-}
-
-static int msm_hdcp_remove(struct platform_device *pdev)
-{
- struct msm_hdcp_mgr *mgr;
-
- mgr = (struct msm_hdcp_mgr *)platform_get_drvdata(pdev);
- if (!mgr)
- return -ENODEV;
-
- sysfs_remove_group(&hdcp_drv_mgr->device->kobj,
- &msm_hdcp_fs_attr_group);
- cdev_del(&hdcp_drv_mgr->cdev);
- device_destroy(hdcp_drv_mgr->class, hdcp_drv_mgr->dev_num);
- class_destroy(hdcp_drv_mgr->class);
- unregister_chrdev_region(hdcp_drv_mgr->dev_num, 1);
-
- devm_kfree(&pdev->dev, hdcp_drv_mgr);
- hdcp_drv_mgr = NULL;
- return 0;
-}
-
-static struct platform_driver msm_hdcp_driver = {
- .probe = msm_hdcp_probe,
- .remove = msm_hdcp_remove,
- .driver = {
- .name = "msm_hdcp",
- .of_match_table = msm_hdcp_dt_match,
- .pm = NULL,
- }
-};
-
-static int __init msm_hdcp_init(void)
-{
- return platform_driver_register(&msm_hdcp_driver);
-}
-
-static void __exit msm_hdcp_exit(void)
-{
- return platform_driver_unregister(&msm_hdcp_driver);
-}
-
-module_init(msm_hdcp_init);
-module_exit(msm_hdcp_exit);
-
-MODULE_DESCRIPTION("MSM HDCP driver");
-MODULE_LICENSE("GPL v2");
diff --git a/drivers/misc/msm_hdcp.c b/drivers/misc/msm_hdcp.c
new file mode 100644
index 0000000..ea8e84d
--- /dev/null
+++ b/drivers/misc/msm_hdcp.c
@@ -0,0 +1,356 @@
+/* Copyright (c) 2018, 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) "[msm-hdcp] %s: " fmt, __func__
+
+#include <linux/platform_device.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/fs.h>
+#include <linux/file.h>
+#include <linux/uaccess.h>
+#include <linux/cdev.h>
+#include <linux/list.h>
+#include <linux/device.h>
+#include <linux/errno.h>
+#include <linux/msm_hdcp.h>
+#include <linux/of.h>
+
+#define CLASS_NAME "hdcp"
+#define DRIVER_NAME "msm_hdcp"
+
+struct msm_hdcp {
+ struct platform_device *pdev;
+ dev_t dev_num;
+ struct cdev cdev;
+ struct class *class;
+ struct device *device;
+ struct HDCP_V2V1_MSG_TOPOLOGY cached_tp;
+ u32 tp_msgid;
+ void *client_ctx;
+ void (*cb)(void *ctx, int data);
+};
+
+void msm_hdcp_register_cb(struct device *dev, void *ctx,
+ void (*cb)(void *ctx, int data))
+{
+ struct msm_hdcp *hdcp = NULL;
+
+ if (!dev) {
+ pr_err("invalid device pointer\n");
+ return;
+ }
+
+ hdcp = dev_get_drvdata(dev);
+ if (!hdcp) {
+ pr_err("invalid driver pointer\n");
+ return;
+ }
+
+ hdcp->cb = cb;
+ hdcp->client_ctx = ctx;
+}
+
+void msm_hdcp_notify_topology(struct device *dev)
+{
+ char *envp[4];
+ char tp[SZ_16];
+ char ver[SZ_16];
+ struct msm_hdcp *hdcp = NULL;
+
+ if (!dev) {
+ pr_err("invalid device pointer\n");
+ return;
+ }
+
+ hdcp = dev_get_drvdata(dev);
+ if (!hdcp) {
+ pr_err("invalid driver pointer\n");
+ return;
+ }
+
+ snprintf(tp, SZ_16, "%d", DOWN_CHECK_TOPOLOGY);
+ snprintf(ver, SZ_16, "%d", HDCP_V1_TX);
+
+ envp[0] = "HDCP_MGR_EVENT=MSG_READY";
+ envp[1] = tp;
+ envp[2] = ver;
+ envp[3] = NULL;
+
+ kobject_uevent_env(&hdcp->device->kobj, KOBJ_CHANGE, envp);
+}
+
+void msm_hdcp_cache_repeater_topology(struct device *dev,
+ struct HDCP_V2V1_MSG_TOPOLOGY *tp)
+{
+ struct msm_hdcp *hdcp = NULL;
+
+ if (!dev || !tp) {
+ pr_err("invalid input\n");
+ return;
+ }
+
+ hdcp = dev_get_drvdata(dev);
+ if (!hdcp) {
+ pr_err("invalid driver pointer\n");
+ return;
+ }
+
+ memcpy(&hdcp->cached_tp, tp,
+ sizeof(struct HDCP_V2V1_MSG_TOPOLOGY));
+}
+
+static ssize_t msm_hdcp_1x_sysfs_rda_tp(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ ssize_t ret = 0;
+ struct msm_hdcp *hdcp = NULL;
+
+ if (!dev) {
+ pr_err("invalid device pointer\n");
+ return -ENODEV;
+ }
+
+ hdcp = dev_get_drvdata(dev);
+ if (!hdcp) {
+ pr_err("invalid driver pointer\n");
+ return -ENODEV;
+ }
+
+ switch (hdcp->tp_msgid) {
+ case DOWN_CHECK_TOPOLOGY:
+ case DOWN_REQUEST_TOPOLOGY:
+ buf[MSG_ID_IDX] = hdcp->tp_msgid;
+ buf[RET_CODE_IDX] = HDCP_AUTHED;
+ ret = HEADER_LEN;
+
+ memcpy(buf + HEADER_LEN, &hdcp->cached_tp,
+ sizeof(struct HDCP_V2V1_MSG_TOPOLOGY));
+
+ ret += sizeof(struct HDCP_V2V1_MSG_TOPOLOGY);
+
+ /* clear the flag once data is read back to user space*/
+ hdcp->tp_msgid = -1;
+ break;
+ default:
+ ret = -EINVAL;
+ }
+
+ return ret;
+}
+
+static ssize_t msm_hdcp_1x_sysfs_wta_tp(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ int msgid = 0;
+ ssize_t ret = count;
+ struct msm_hdcp *hdcp = NULL;
+
+ if (!dev) {
+ pr_err("invalid device pointer\n");
+ return -ENODEV;
+ }
+
+ hdcp = dev_get_drvdata(dev);
+ if (!hdcp) {
+ pr_err("invalid driver pointer\n");
+ return -ENODEV;
+ }
+
+ msgid = buf[0];
+
+ switch (msgid) {
+ case DOWN_CHECK_TOPOLOGY:
+ case DOWN_REQUEST_TOPOLOGY:
+ hdcp->tp_msgid = msgid;
+ break;
+ default:
+ ret = -EINVAL;
+ }
+
+ return ret;
+}
+
+static ssize_t msm_hdcp_2x_sysfs_wta_min_level_change(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ int rc;
+ int min_enc_lvl;
+ ssize_t ret = count;
+ struct msm_hdcp *hdcp = NULL;
+
+ if (!dev) {
+ pr_err("invalid device pointer\n");
+ return -ENODEV;
+ }
+
+ hdcp = dev_get_drvdata(dev);
+ if (!hdcp) {
+ pr_err("invalid driver pointer\n");
+ return -ENODEV;
+ }
+
+ rc = kstrtoint(buf, 10, &min_enc_lvl);
+ if (rc) {
+ pr_err("kstrtoint failed. rc=%d\n", rc);
+ return -EINVAL;
+ }
+
+ if (hdcp->cb && hdcp->client_ctx)
+ hdcp->cb(hdcp->client_ctx, min_enc_lvl);
+
+ return ret;
+}
+
+static DEVICE_ATTR(tp, 0644, msm_hdcp_1x_sysfs_rda_tp,
+ msm_hdcp_1x_sysfs_wta_tp);
+
+static DEVICE_ATTR(min_level_change, 0200, NULL,
+ msm_hdcp_2x_sysfs_wta_min_level_change);
+
+static struct attribute *msm_hdcp_fs_attrs[] = {
+ &dev_attr_tp.attr,
+ &dev_attr_min_level_change.attr,
+ NULL
+};
+
+static struct attribute_group msm_hdcp_fs_attr_group = {
+ .attrs = msm_hdcp_fs_attrs
+};
+
+static int msm_hdcp_open(struct inode *inode, struct file *file)
+{
+ return 0;
+}
+
+static int msm_hdcp_close(struct inode *inode, struct file *file)
+{
+ return 0;
+}
+
+static const struct file_operations msm_hdcp_fops = {
+ .owner = THIS_MODULE,
+ .open = msm_hdcp_open,
+ .release = msm_hdcp_close,
+};
+
+static const struct of_device_id msm_hdcp_dt_match[] = {
+ { .compatible = "qcom,msm-hdcp",},
+ {}
+};
+
+MODULE_DEVICE_TABLE(of, msm_hdcp_dt_match);
+
+static int msm_hdcp_probe(struct platform_device *pdev)
+{
+ int ret;
+ struct msm_hdcp *hdcp;
+
+ hdcp = devm_kzalloc(&pdev->dev, sizeof(struct msm_hdcp), GFP_KERNEL);
+ if (!hdcp)
+ return -ENOMEM;
+
+ hdcp->pdev = pdev;
+
+ platform_set_drvdata(pdev, hdcp);
+
+ ret = alloc_chrdev_region(&hdcp->dev_num, 0, 1, DRIVER_NAME);
+ if (ret < 0) {
+ pr_err("alloc_chrdev_region failed ret = %d\n", ret);
+ goto error_get_dev_num;
+ }
+
+ hdcp->class = class_create(THIS_MODULE, CLASS_NAME);
+ if (IS_ERR(hdcp->class)) {
+ ret = PTR_ERR(hdcp->class);
+ pr_err("couldn't create class rc = %d\n", ret);
+ goto error_class_create;
+ }
+
+ hdcp->device = device_create(hdcp->class, NULL,
+ hdcp->dev_num, NULL, DRIVER_NAME);
+ if (IS_ERR(hdcp->device)) {
+ ret = PTR_ERR(hdcp->device);
+ pr_err("device_create failed %d\n", ret);
+ goto error_class_device_create;
+ }
+
+ cdev_init(&hdcp->cdev, &msm_hdcp_fops);
+ ret = cdev_add(&hdcp->cdev, MKDEV(MAJOR(hdcp->dev_num), 0), 1);
+ if (ret < 0) {
+ pr_err("cdev_add failed %d\n", ret);
+ goto error_cdev_add;
+ }
+
+ ret = sysfs_create_group(&hdcp->device->kobj, &msm_hdcp_fs_attr_group);
+ if (ret)
+ pr_err("unable to register msm_hdcp sysfs nodes\n");
+
+ return 0;
+error_cdev_add:
+ device_destroy(hdcp->class, hdcp->dev_num);
+error_class_device_create:
+ class_destroy(hdcp->class);
+error_class_create:
+ unregister_chrdev_region(hdcp->dev_num, 1);
+error_get_dev_num:
+ devm_kfree(&pdev->dev, hdcp);
+ hdcp = NULL;
+ return ret;
+}
+
+static int msm_hdcp_remove(struct platform_device *pdev)
+{
+ struct msm_hdcp *hdcp;
+
+ hdcp = platform_get_drvdata(pdev);
+ if (!hdcp)
+ return -ENODEV;
+
+ sysfs_remove_group(&hdcp->device->kobj,
+ &msm_hdcp_fs_attr_group);
+ cdev_del(&hdcp->cdev);
+ device_destroy(hdcp->class, hdcp->dev_num);
+ class_destroy(hdcp->class);
+ unregister_chrdev_region(hdcp->dev_num, 1);
+
+ devm_kfree(&pdev->dev, hdcp);
+ hdcp = NULL;
+ return 0;
+}
+
+static struct platform_driver msm_hdcp_driver = {
+ .probe = msm_hdcp_probe,
+ .remove = msm_hdcp_remove,
+ .driver = {
+ .name = "msm_hdcp",
+ .of_match_table = msm_hdcp_dt_match,
+ .pm = NULL,
+ }
+};
+
+static int __init msm_hdcp_init(void)
+{
+ return platform_driver_register(&msm_hdcp_driver);
+}
+
+static void __exit msm_hdcp_exit(void)
+{
+ return platform_driver_unregister(&msm_hdcp_driver);
+}
+
+module_init(msm_hdcp_init);
+module_exit(msm_hdcp_exit);
+
+MODULE_DESCRIPTION("MSM HDCP driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c
index 89edc6d..289f3bc 100644
--- a/drivers/mmc/core/core.c
+++ b/drivers/mmc/core/core.c
@@ -470,6 +470,9 @@
mmc_host_clear_sdr104(host);
err = mmc_hw_reset(host);
host->card->sdr104_blocked = true;
+ } else {
+ /* If sdr104_wa is not present, just return status */
+ err = host->bus_ops->alive(host);
}
if (err)
pr_err("%s: %s: Fallback to lower speed mode failed with err=%d\n",
diff --git a/drivers/mmc/host/sdhci-msm.c b/drivers/mmc/host/sdhci-msm.c
index 0abc7a3..0bea2cb 100644
--- a/drivers/mmc/host/sdhci-msm.c
+++ b/drivers/mmc/host/sdhci-msm.c
@@ -2692,6 +2692,52 @@
msm_host_offset->CORE_PWRCTL_CTL), irq_flags);
}
+static int sdhci_msm_clear_pwrctl_status(struct sdhci_host *host, u8 value)
+{
+ struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+ struct sdhci_msm_host *msm_host = pltfm_host->priv;
+ const struct sdhci_msm_offset *msm_host_offset = msm_host->offset;
+ int ret = 0, retry = 10;
+
+ /*
+ * There is a rare HW scenario where the first clear pulse could be
+ * lost when actual reset and clear/read of status register is
+ * happening at a time. Hence, retry for at least 10 times to make
+ * sure status register is cleared. Otherwise, this will result in
+ * a spurious power IRQ resulting in system instability.
+ */
+ do {
+ if (retry == 0) {
+ pr_err("%s: Timedout clearing (0x%x) pwrctl status register\n",
+ mmc_hostname(host->mmc), value);
+ sdhci_msm_dump_pwr_ctrl_regs(host);
+ WARN_ON(1);
+ ret = -EBUSY;
+ break;
+ }
+
+ /*
+ * Clear the PWRCTL_STATUS interrupt bits by writing to the
+ * corresponding bits in the PWRCTL_CLEAR register.
+ */
+ sdhci_msm_writeb_relaxed(value, host,
+ msm_host_offset->CORE_PWRCTL_CLEAR);
+ /*
+ * SDHC has core_mem and hc_mem device memory and these memory
+ * addresses do not fall within 1KB region. Hence, any update
+ * to core_mem address space would require an mb() to ensure
+ * this gets completed before its next update to registers
+ * within hc_mem.
+ */
+ mb();
+ retry--;
+ udelay(10);
+ } while (value & sdhci_msm_readb_relaxed(host,
+ msm_host_offset->CORE_PWRCTL_STATUS));
+
+ return ret;
+}
+
static irqreturn_t sdhci_msm_pwr_irq(int irq, void *data)
{
struct sdhci_host *host = (struct sdhci_host *)data;
@@ -2704,7 +2750,6 @@
int ret = 0;
int pwr_state = 0, io_level = 0;
unsigned long flags;
- int retry = 10;
irq_status = sdhci_msm_readb_relaxed(host,
msm_host_offset->CORE_PWRCTL_STATUS);
@@ -2712,40 +2757,7 @@
pr_debug("%s: Received IRQ(%d), status=0x%x\n",
mmc_hostname(msm_host->mmc), irq, irq_status);
- /* Clear the interrupt */
- sdhci_msm_writeb_relaxed(irq_status, host,
- msm_host_offset->CORE_PWRCTL_CLEAR);
-
- /*
- * SDHC has core_mem and hc_mem device memory and these memory
- * addresses do not fall within 1KB region. Hence, any update to
- * core_mem address space would require an mb() to ensure this gets
- * completed before its next update to registers within hc_mem.
- */
- mb();
- /*
- * There is a rare HW scenario where the first clear pulse could be
- * lost when actual reset and clear/read of status register is
- * happening at a time. Hence, retry for at least 10 times to make
- * sure status register is cleared. Otherwise, this will result in
- * a spurious power IRQ resulting in system instability.
- */
- while (irq_status & sdhci_msm_readb_relaxed(host,
- msm_host_offset->CORE_PWRCTL_STATUS)) {
- if (retry == 0) {
- pr_err("%s: Timedout clearing (0x%x) pwrctl status register\n",
- mmc_hostname(host->mmc), irq_status);
- sdhci_msm_dump_pwr_ctrl_regs(host);
- BUG_ON(1);
- }
- sdhci_msm_writeb_relaxed(irq_status, host,
- msm_host_offset->CORE_PWRCTL_CLEAR);
- retry--;
- udelay(10);
- }
- if (likely(retry < 10))
- pr_debug("%s: success clearing (0x%x) pwrctl status register, retries left %d\n",
- mmc_hostname(host->mmc), irq_status, retry);
+ sdhci_msm_clear_pwrctl_status(host, irq_status);
/* Handle BUS ON/OFF*/
if (irq_status & CORE_PWRCTL_BUS_ON) {
@@ -3124,6 +3136,7 @@
{
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
struct sdhci_msm_host *msm_host = pltfm_host->priv;
+ u8 irq_status;
const struct sdhci_msm_offset *msm_host_offset =
msm_host->offset;
@@ -3131,10 +3144,9 @@
!msm_host->regs_restore.is_valid)
return;
+ writel_relaxed(0, host->ioaddr + msm_host_offset->CORE_PWRCTL_MASK);
writel_relaxed(msm_host->regs_restore.vendor_func, host->ioaddr +
msm_host_offset->CORE_VENDOR_SPEC);
- writel_relaxed(msm_host->regs_restore.vendor_pwrctl_mask,
- host->ioaddr + msm_host_offset->CORE_PWRCTL_MASK);
writel_relaxed(msm_host->regs_restore.vendor_func2,
host->ioaddr +
msm_host_offset->CORE_VENDOR_SPEC_FUNC2);
@@ -3145,8 +3157,6 @@
SDHCI_CLOCK_CONTROL);
sdhci_writel(host, msm_host->regs_restore.hc_3c_3e,
SDHCI_AUTO_CMD_ERR);
- writel_relaxed(msm_host->regs_restore.vendor_pwrctl_ctl,
- host->ioaddr + msm_host_offset->CORE_PWRCTL_CTL);
sdhci_writel(host, msm_host->regs_restore.hc_38_3a,
SDHCI_SIGNAL_ENABLE);
sdhci_writel(host, msm_host->regs_restore.hc_34_36,
@@ -3162,6 +3172,24 @@
msm_host_offset->CORE_TESTBUS_CONFIG);
msm_host->regs_restore.is_valid = false;
+ /*
+ * Clear the PWRCTL_STATUS register.
+ * There is a rare HW scenario where the first clear pulse could be
+ * lost when actual reset and clear/read of status register is
+ * happening at a time. Hence, retry for at least 10 times to make
+ * sure status register is cleared. Otherwise, this will result in
+ * a spurious power IRQ resulting in system instability.
+ */
+ irq_status = sdhci_msm_readb_relaxed(host,
+ msm_host_offset->CORE_PWRCTL_STATUS);
+
+ sdhci_msm_clear_pwrctl_status(host, irq_status);
+
+ writel_relaxed(msm_host->regs_restore.vendor_pwrctl_ctl,
+ host->ioaddr + msm_host_offset->CORE_PWRCTL_CTL);
+ writel_relaxed(msm_host->regs_restore.vendor_pwrctl_mask,
+ host->ioaddr + msm_host_offset->CORE_PWRCTL_MASK);
+
pr_debug("%s: %s: registers restored. PWRCTL_MASK = 0x%x\n",
mmc_hostname(host->mmc), __func__,
readl_relaxed(host->ioaddr +
diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_83xx_hw.c b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_83xx_hw.c
index bdbcd2b..c3c28f0 100644
--- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_83xx_hw.c
+++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_83xx_hw.c
@@ -3849,7 +3849,7 @@
struct list_head *head = &mbx->cmd_q;
struct qlcnic_cmd_args *cmd = NULL;
- spin_lock(&mbx->queue_lock);
+ spin_lock_bh(&mbx->queue_lock);
while (!list_empty(head)) {
cmd = list_entry(head->next, struct qlcnic_cmd_args, list);
@@ -3860,7 +3860,7 @@
qlcnic_83xx_notify_cmd_completion(adapter, cmd);
}
- spin_unlock(&mbx->queue_lock);
+ spin_unlock_bh(&mbx->queue_lock);
}
static int qlcnic_83xx_check_mbx_status(struct qlcnic_adapter *adapter)
@@ -3896,12 +3896,12 @@
{
struct qlcnic_mailbox *mbx = adapter->ahw->mailbox;
- spin_lock(&mbx->queue_lock);
+ spin_lock_bh(&mbx->queue_lock);
list_del(&cmd->list);
mbx->num_cmds--;
- spin_unlock(&mbx->queue_lock);
+ spin_unlock_bh(&mbx->queue_lock);
qlcnic_83xx_notify_cmd_completion(adapter, cmd);
}
@@ -3966,7 +3966,7 @@
init_completion(&cmd->completion);
cmd->rsp_opcode = QLC_83XX_MBX_RESPONSE_UNKNOWN;
- spin_lock(&mbx->queue_lock);
+ spin_lock_bh(&mbx->queue_lock);
list_add_tail(&cmd->list, &mbx->cmd_q);
mbx->num_cmds++;
@@ -3974,7 +3974,7 @@
*timeout = cmd->total_cmds * QLC_83XX_MBX_TIMEOUT;
queue_work(mbx->work_q, &mbx->work);
- spin_unlock(&mbx->queue_lock);
+ spin_unlock_bh(&mbx->queue_lock);
return 0;
}
@@ -4070,15 +4070,15 @@
mbx->rsp_status = QLC_83XX_MBX_RESPONSE_WAIT;
spin_unlock_irqrestore(&mbx->aen_lock, flags);
- spin_lock(&mbx->queue_lock);
+ spin_lock_bh(&mbx->queue_lock);
if (list_empty(head)) {
- spin_unlock(&mbx->queue_lock);
+ spin_unlock_bh(&mbx->queue_lock);
return;
}
cmd = list_entry(head->next, struct qlcnic_cmd_args, list);
- spin_unlock(&mbx->queue_lock);
+ spin_unlock_bh(&mbx->queue_lock);
mbx_ops->encode_cmd(adapter, cmd);
mbx_ops->nofity_fw(adapter, QLC_83XX_MBX_REQUEST);
diff --git a/drivers/net/ethernet/realtek/r8169.c b/drivers/net/ethernet/realtek/r8169.c
index 298b74e..18e68c9 100644
--- a/drivers/net/ethernet/realtek/r8169.c
+++ b/drivers/net/ethernet/realtek/r8169.c
@@ -1387,7 +1387,7 @@
{
void __iomem *ioaddr = tp->mmio_addr;
- return RTL_R8(IBISR0) & 0x02;
+ return RTL_R8(IBISR0) & 0x20;
}
static void rtl8168ep_stop_cmac(struct rtl8169_private *tp)
@@ -1395,7 +1395,7 @@
void __iomem *ioaddr = tp->mmio_addr;
RTL_W8(IBCR2, RTL_R8(IBCR2) & ~0x01);
- rtl_msleep_loop_wait_low(tp, &rtl_ocp_tx_cond, 50, 2000);
+ rtl_msleep_loop_wait_high(tp, &rtl_ocp_tx_cond, 50, 2000);
RTL_W8(IBISR0, RTL_R8(IBISR0) | 0x20);
RTL_W8(IBCR0, RTL_R8(IBCR0) & ~0x01);
}
diff --git a/drivers/net/usb/qmi_wwan.c b/drivers/net/usb/qmi_wwan.c
index db65d9a..e1e5e84 100644
--- a/drivers/net/usb/qmi_wwan.c
+++ b/drivers/net/usb/qmi_wwan.c
@@ -944,6 +944,7 @@
{QMI_QUIRK_SET_DTR(0x2c7c, 0x0125, 4)}, /* Quectel EC25, EC20 R2.0 Mini PCIe */
{QMI_QUIRK_SET_DTR(0x2c7c, 0x0121, 4)}, /* Quectel EC21 Mini PCIe */
{QMI_FIXED_INTF(0x2c7c, 0x0296, 4)}, /* Quectel BG96 */
+ {QMI_QUIRK_SET_DTR(0x2c7c, 0x0306, 4)}, /* Quectel EP06 Mini PCIe */
/* 4. Gobi 1000 devices */
{QMI_GOBI1K_DEVICE(0x05c6, 0x9212)}, /* Acer Gobi Modem Device */
diff --git a/drivers/net/wireless/broadcom/b43/main.c b/drivers/net/wireless/broadcom/b43/main.c
index 6e5d909..a635fc6 100644
--- a/drivers/net/wireless/broadcom/b43/main.c
+++ b/drivers/net/wireless/broadcom/b43/main.c
@@ -71,8 +71,18 @@
MODULE_FIRMWARE("b43/ucode13.fw");
MODULE_FIRMWARE("b43/ucode14.fw");
MODULE_FIRMWARE("b43/ucode15.fw");
+MODULE_FIRMWARE("b43/ucode16_lp.fw");
MODULE_FIRMWARE("b43/ucode16_mimo.fw");
+MODULE_FIRMWARE("b43/ucode24_lcn.fw");
+MODULE_FIRMWARE("b43/ucode25_lcn.fw");
+MODULE_FIRMWARE("b43/ucode25_mimo.fw");
+MODULE_FIRMWARE("b43/ucode26_mimo.fw");
+MODULE_FIRMWARE("b43/ucode29_mimo.fw");
+MODULE_FIRMWARE("b43/ucode33_lcn40.fw");
+MODULE_FIRMWARE("b43/ucode30_mimo.fw");
MODULE_FIRMWARE("b43/ucode5.fw");
+MODULE_FIRMWARE("b43/ucode40.fw");
+MODULE_FIRMWARE("b43/ucode42.fw");
MODULE_FIRMWARE("b43/ucode9.fw");
static int modparam_bad_frames_preempt;
diff --git a/drivers/pinctrl/pxa/pinctrl-pxa2xx.c b/drivers/pinctrl/pxa/pinctrl-pxa2xx.c
index 866aa3c..6cf0006 100644
--- a/drivers/pinctrl/pxa/pinctrl-pxa2xx.c
+++ b/drivers/pinctrl/pxa/pinctrl-pxa2xx.c
@@ -436,3 +436,7 @@
return 0;
}
EXPORT_SYMBOL_GPL(pxa2xx_pinctrl_exit);
+
+MODULE_AUTHOR("Robert Jarzmik <robert.jarzmik@free.fr>");
+MODULE_DESCRIPTION("Marvell PXA2xx pinctrl driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/pinctrl/qcom/pinctrl-sdm670.c b/drivers/pinctrl/qcom/pinctrl-sdm670.c
index f7af6da..37a6199 100644
--- a/drivers/pinctrl/qcom/pinctrl-sdm670.c
+++ b/drivers/pinctrl/qcom/pinctrl-sdm670.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2017-2018, 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
@@ -1588,7 +1588,7 @@
[156] = SDC_QDSD_PINGROUP(sdc2_data, 0x9a000, 9, 0),
[157] = UFS_RESET(ufs_reset, 0x9f000),
};
-static const struct msm_dir_conn sdm670_dir_conn[] = {
+static struct msm_dir_conn sdm670_dir_conn[] = {
{1, 510},
{3, 511},
{5, 512},
diff --git a/drivers/pinctrl/qcom/pinctrl-sdm845-v2.c b/drivers/pinctrl/qcom/pinctrl-sdm845-v2.c
index e77dcd9..72b730d 100644
--- a/drivers/pinctrl/qcom/pinctrl-sdm845-v2.c
+++ b/drivers/pinctrl/qcom/pinctrl-sdm845-v2.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2017-2018, 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
@@ -1681,7 +1681,7 @@
[153] = UFS_RESET(ufs_reset, 0x99f000),
};
-static const struct msm_dir_conn sdm845_dir_conn[] = {
+static struct msm_dir_conn sdm845_dir_conn[] = {
{1, 510},
{3, 511},
{5, 512},
diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_pm.h b/drivers/platform/msm/ipa/ipa_v3/ipa_pm.h
index 205e7a5..ce9c684 100644
--- a/drivers/platform/msm/ipa/ipa_v3/ipa_pm.h
+++ b/drivers/platform/msm/ipa/ipa_v3/ipa_pm.h
@@ -90,6 +90,8 @@
bool skip_clk_vote;
};
+#ifdef CONFIG_IPA3
+
int ipa_pm_register(struct ipa_pm_register_params *params, u32 *hdl);
int ipa_pm_associate_ipa_cons_to_client(u32 hdl, enum ipa_client_type consumer);
int ipa_pm_activate(u32 hdl);
@@ -107,4 +109,80 @@
int ipa_pm_stat(char *buf, int size);
int ipa_pm_exceptions_stat(char *buf, int size);
+#else
+
+static inline int ipa_pm_register(
+ struct ipa_pm_register_params *params, u32 *hdl)
+{
+ return -EPERM;
+}
+
+static inline int ipa_pm_associate_ipa_cons_to_client(
+ u32 hdl, enum ipa_client_type consumer)
+{
+ return -EPERM;
+}
+
+static inline int ipa_pm_activate(u32 hdl)
+{
+ return -EPERM;
+}
+
+static inline int ipa_pm_activate_sync(u32 hdl)
+{
+ return -EPERM;
+}
+
+static inline int ipa_pm_deferred_deactivate(u32 hdl)
+{
+ return -EPERM;
+}
+
+static inline int ipa_pm_deactivate_sync(u32 hdl)
+{
+ return -EPERM;
+}
+
+static inline int ipa_pm_set_perf_profile(u32 hdl, int throughput)
+{
+ return -EPERM;
+}
+
+static inline int ipa_pm_deregister(u32 hdl)
+{
+ return -EPERM;
+}
+
+/* IPA Internal Functions */
+static inline int ipa_pm_init(struct ipa_pm_init_params *params)
+{
+ return -EPERM;
+}
+
+static inline int ipa_pm_destroy(void)
+{
+ return -EPERM;
+}
+
+static inline int ipa_pm_handle_suspend(u32 pipe_bitmask)
+{
+ return -EPERM;
+}
+
+static inline int ipa_pm_deactivate_all_deferred(void)
+{
+ return -EPERM;
+}
+
+static inline int ipa_pm_stat(char *buf, int size)
+{
+ return -EPERM;
+}
+
+static inline int ipa_pm_exceptions_stat(char *buf, int size)
+{
+ return -EPERM;
+}
+#endif
+
#endif /* _IPA_PM_H_ */
diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_qmi_service.c b/drivers/platform/msm/ipa/ipa_v3/ipa_qmi_service.c
index c158c94..88de06e 100644
--- a/drivers/platform/msm/ipa/ipa_v3/ipa_qmi_service.c
+++ b/drivers/platform/msm/ipa/ipa_v3/ipa_qmi_service.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2013-2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2013-2018, 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
@@ -708,7 +708,7 @@
return -EINVAL;
}
- for (i = 0; i < req->filter_spec_ex_list_len-1; i++) {
+ for (i = 0; i < req->filter_spec_ex_list_len; i++) {
if ((req->filter_spec_ex_list[i].ip_type !=
QMI_IPA_IP_TYPE_V4_V01) &&
(req->filter_spec_ex_list[i].ip_type !=
diff --git a/drivers/power/qcom/apm.c b/drivers/power/qcom/apm.c
index 3dfb439..ed3df00 100644
--- a/drivers/power/qcom/apm.c
+++ b/drivers/power/qcom/apm.c
@@ -638,9 +638,15 @@
#define MSM8953_APCC_APM_MODE 0x000002a8
#define MSM8953_APCC_APM_CTL_STS 0x000002b0
+/* 8953 constants */
+#define MSM8953_APM_SWITCH_TIMEOUT_US 500
+
+/* Register bit mask definitions */
+#define MSM8953_APM_CTL_STS_MASK 0x1f
+
static int msm8953_apm_switch_to_mx(struct msm_apm_ctrl_dev *ctrl_dev)
{
- int timeout = MSM_APM_SWITCH_TIMEOUT_US;
+ int timeout = MSM8953_APM_SWITCH_TIMEOUT_US;
u32 regval;
int ret = 0;
unsigned long flags;
@@ -657,7 +663,7 @@
while (timeout > 0) {
regval = readl_relaxed(ctrl_dev->reg_base +
MSM8953_APCC_APM_CTL_STS);
- if ((regval & MSM_APM_CTL_STS_MASK) ==
+ if ((regval & MSM8953_APM_CTL_STS_MASK) ==
MSM8953_APM_MX_DONE_VAL)
break;
@@ -681,7 +687,7 @@
static int msm8953_apm_switch_to_apcc(struct msm_apm_ctrl_dev *ctrl_dev)
{
- int timeout = MSM_APM_SWITCH_TIMEOUT_US;
+ int timeout = MSM8953_APM_SWITCH_TIMEOUT_US;
u32 regval;
int ret = 0;
unsigned long flags;
@@ -698,7 +704,7 @@
while (timeout > 0) {
regval = readl_relaxed(ctrl_dev->reg_base +
MSM8953_APCC_APM_CTL_STS);
- if ((regval & MSM_APM_CTL_STS_MASK) ==
+ if ((regval & MSM8953_APM_CTL_STS_MASK) ==
MSM8953_APM_APCC_DONE_VAL)
break;
diff --git a/drivers/power/supply/qcom/Makefile b/drivers/power/supply/qcom/Makefile
index 6c7fc78..662f18d 100644
--- a/drivers/power/supply/qcom/Makefile
+++ b/drivers/power/supply/qcom/Makefile
@@ -9,4 +9,4 @@
obj-$(CONFIG_QPNP_QG) += qpnp-qg.o pmic-voter.o qg-util.o qg-soc.o qg-sdam.o qg-battery-profile.o qg-profile-lib.o
obj-$(CONFIG_QPNP_QNOVO) += qpnp-qnovo.o battery.o
obj-$(CONFIG_QPNP_TYPEC) += qpnp-typec.o
-obj-$(CONFIG_QPNP_SMB5) += step-chg-jeita.o battery.o qpnp-smb5.o smb5-lib.o pmic-voter.o storm-watch.o
+obj-$(CONFIG_QPNP_SMB5) += step-chg-jeita.o battery.o qpnp-smb5.o smb5-lib.o pmic-voter.o storm-watch.o schgm-flash.o
diff --git a/drivers/power/supply/qcom/qpnp-smb5.c b/drivers/power/supply/qcom/qpnp-smb5.c
index b07efdf..b3f66d5 100644
--- a/drivers/power/supply/qcom/qpnp-smb5.c
+++ b/drivers/power/supply/qcom/qpnp-smb5.c
@@ -27,6 +27,7 @@
#include <linux/pmic-voter.h>
#include "smb5-reg.h"
#include "smb5-lib.h"
+#include "schgm-flash.h"
static struct smb_params smb5_pmi632_params = {
.fcc = {
@@ -371,6 +372,7 @@
POWER_SUPPLY_PROP_SDP_CURRENT_MAX,
POWER_SUPPLY_PROP_CONNECTOR_TYPE,
POWER_SUPPLY_PROP_VOLTAGE_MAX,
+ POWER_SUPPLY_PROP_SCOPE,
};
static int smb5_usb_get_prop(struct power_supply *psy,
@@ -379,6 +381,7 @@
{
struct smb5 *chip = power_supply_get_drvdata(psy);
struct smb_charger *chg = &chip->chg;
+ union power_supply_propval pval;
int rc = 0;
switch (psp) {
@@ -476,6 +479,15 @@
case POWER_SUPPLY_PROP_CONNECTOR_TYPE:
val->intval = chg->connector_type;
break;
+ case POWER_SUPPLY_PROP_SCOPE:
+ val->intval = POWER_SUPPLY_SCOPE_UNKNOWN;
+ rc = smblib_get_prop_usb_present(chg, &pval);
+ if (rc < 0)
+ break;
+ val->intval = pval.intval ? POWER_SUPPLY_SCOPE_DEVICE
+ : chg->otg_present ? POWER_SUPPLY_SCOPE_SYSTEM
+ : POWER_SUPPLY_SCOPE_UNKNOWN;
+ break;
default:
pr_err("get prop %d is not supported in usb\n", psp);
rc = -EINVAL;
@@ -701,6 +713,8 @@
POWER_SUPPLY_PROP_INPUT_VOLTAGE_SETTLED,
POWER_SUPPLY_PROP_FCC_DELTA,
POWER_SUPPLY_PROP_CURRENT_MAX,
+ POWER_SUPPLY_PROP_FLASH_ACTIVE,
+ POWER_SUPPLY_PROP_FLASH_TRIGGER,
};
static int smb5_usb_main_get_prop(struct power_supply *psy,
@@ -734,6 +748,12 @@
case POWER_SUPPLY_PROP_CURRENT_MAX:
rc = smblib_get_icl_current(chg, &val->intval);
break;
+ case POWER_SUPPLY_PROP_FLASH_ACTIVE:
+ val->intval = chg->flash_active;
+ break;
+ case POWER_SUPPLY_PROP_FLASH_TRIGGER:
+ rc = schgm_flash_get_vreg_ok(chg, &val->intval);
+ break;
default:
pr_debug("get prop %d is not supported in usb-main\n", psp);
rc = -EINVAL;
@@ -765,6 +785,9 @@
case POWER_SUPPLY_PROP_CURRENT_MAX:
rc = smblib_set_icl_current(chg, val->intval);
break;
+ case POWER_SUPPLY_PROP_FLASH_ACTIVE:
+ chg->flash_active = val->intval;
+ break;
default:
pr_err("set prop %d is not supported\n", psp);
rc = -EINVAL;
@@ -1367,6 +1390,13 @@
: POWER_SUPPLY_CONNECTOR_TYPEC;
pr_debug("Connector type=%s\n", type ? "Micro USB" : "TypeC");
+ /*
+ * PMI632 based hw init:
+ * - Initialize flash module for PMI632
+ */
+ if (chg->smb_version == PMI632_SUBTYPE)
+ schgm_flash_init(chg);
+
smblib_rerun_apsd_if_required(chg);
/* vote 0mA on usb_icl for non battery platforms */
@@ -1829,6 +1859,39 @@
.name = "temp-change-smb",
.handler = default_irq_handler,
},
+ /* FLASH */
+ [VREG_OK_IRQ] = {
+ .name = "vreg-ok",
+ .handler = schgm_flash_default_irq_handler,
+ },
+ [ILIM_S2_IRQ] = {
+ .name = "ilim2-s2",
+ .handler = schgm_flash_ilim2_irq_handler,
+ },
+ [ILIM_S1_IRQ] = {
+ .name = "ilim1-s1",
+ .handler = schgm_flash_default_irq_handler,
+ },
+ [VOUT_DOWN_IRQ] = {
+ .name = "vout-down",
+ .handler = schgm_flash_default_irq_handler,
+ },
+ [VOUT_UP_IRQ] = {
+ .name = "vout-up",
+ .handler = schgm_flash_default_irq_handler,
+ },
+ [FLASH_STATE_CHANGE_IRQ] = {
+ .name = "flash-state-change",
+ .handler = schgm_flash_state_change_irq_handler,
+ },
+ [TORCH_REQ_IRQ] = {
+ .name = "torch-req",
+ .handler = schgm_flash_default_irq_handler,
+ },
+ [FLASH_EN_IRQ] = {
+ .name = "flash-en",
+ .handler = schgm_flash_default_irq_handler,
+ },
};
static int smb5_get_irq_index_byname(const char *irq_name)
diff --git a/drivers/power/supply/qcom/schgm-flash.c b/drivers/power/supply/qcom/schgm-flash.c
new file mode 100644
index 0000000..eed70d3
--- /dev/null
+++ b/drivers/power/supply/qcom/schgm-flash.c
@@ -0,0 +1,216 @@
+/* Copyright (c) 2018 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) "SCHG-FLASH: %s: " fmt, __func__
+
+#include <linux/device.h>
+#include <linux/delay.h>
+#include <linux/module.h>
+#include <linux/regmap.h>
+#include <linux/power_supply.h>
+#include <linux/interrupt.h>
+#include <linux/of.h>
+#include <linux/of_irq.h>
+#include <linux/printk.h>
+#include <linux/pmic-voter.h>
+#include "smb5-lib.h"
+#include "schgm-flash.h"
+
+#define IS_BETWEEN(left, right, value) \
+ (((left) >= (right) && (left) >= (value) \
+ && (value) >= (right)) \
+ || ((left) <= (right) && (left) <= (value) \
+ && (value) <= (right)))
+
+irqreturn_t schgm_flash_default_irq_handler(int irq, void *data)
+{
+ struct smb_irq_data *irq_data = data;
+
+ pr_debug("IRQ: %s\n", irq_data->name);
+
+ return IRQ_HANDLED;
+}
+
+irqreturn_t schgm_flash_ilim2_irq_handler(int irq, void *data)
+{
+ struct smb_irq_data *irq_data = data;
+ struct smb_charger *chg = irq_data->parent_data;
+ int rc;
+
+ rc = smblib_write(chg, SCHGM_FLASH_S2_LATCH_RESET_CMD_REG,
+ FLASH_S2_LATCH_RESET_BIT);
+ if (rc < 0)
+ pr_err("Couldn't reset S2_LATCH reset rc=%d\n", rc);
+
+ return IRQ_HANDLED;
+}
+
+irqreturn_t schgm_flash_state_change_irq_handler(int irq, void *data)
+{
+ struct smb_irq_data *irq_data = data;
+ struct smb_charger *chg = irq_data->parent_data;
+ int rc;
+ u8 reg;
+
+ rc = smblib_read(chg, SCHGM_FLASH_STATUS_3_REG, ®);
+ if (rc < 0)
+ pr_err("Couldn't read flash status_3 rc=%d\n", rc);
+ else
+ pr_debug("Flash status changed state=[%x]\n",
+ (reg && FLASH_STATE_MASK));
+
+ return IRQ_HANDLED;
+}
+
+#define FIXED_MODE 0
+#define ADAPTIVE_MODE 1
+static void schgm_flash_parse_dt(struct smb_charger *chg)
+{
+ struct device_node *node = chg->dev->of_node;
+ u32 val;
+ int rc;
+
+ chg->flash_derating_soc = -EINVAL;
+ rc = of_property_read_u32(node, "qcom,flash-derating-soc", &val);
+ if (!rc) {
+ if (IS_BETWEEN(0, 100, val))
+ chg->flash_derating_soc = (val * 255) / 100;
+ }
+
+ chg->flash_disable_soc = -EINVAL;
+ rc = of_property_read_u32(node, "qcom,flash-disable-soc", &val);
+ if (!rc) {
+ if (IS_BETWEEN(0, 100, val))
+ chg->flash_disable_soc = (val * 255) / 100;
+ }
+
+ chg->headroom_mode = -EINVAL;
+ rc = of_property_read_u32(node, "qcom,headroom-mode", &val);
+ if (!rc) {
+ if (IS_BETWEEN(FIXED_MODE, ADAPTIVE_MODE, val))
+ chg->headroom_mode = val;
+ }
+}
+
+int schgm_flash_get_vreg_ok(struct smb_charger *chg, int *val)
+{
+ int rc, vreg_state;
+ u8 stat = 0;
+
+ if (!chg->flash_init_done)
+ return -EPERM;
+
+ rc = smblib_read(chg, SCHGM_FLASH_STATUS_2_REG, &stat);
+ if (rc < 0) {
+ pr_err("Couldn't read FLASH STATUS_2 rc=%d\n", rc);
+ return rc;
+ }
+ vreg_state = !!(stat & VREG_OK_BIT);
+
+ /* If VREG_OK is not set check for flash error */
+ if (!vreg_state) {
+ rc = smblib_read(chg, SCHGM_FLASH_STATUS_3_REG, &stat);
+ if (rc < 0) {
+ pr_err("Couldn't read FLASH_STATUS_3 rc=%d\n", rc);
+ return rc;
+ }
+ if ((stat & FLASH_STATE_MASK) == FLASH_ERROR_VAL) {
+ vreg_state = -EFAULT;
+ rc = smblib_read(chg, SCHGM_FLASH_STATUS_5_REG,
+ &stat);
+ if (rc < 0) {
+ pr_err("Couldn't read FLASH_STATUS_5 rc=%d\n",
+ rc);
+ return rc;
+ }
+ pr_debug("Flash error: status=%x\n", stat);
+ }
+ }
+
+ /*
+ * val can be one of the following:
+ * 1 - VREG_OK is set.
+ * 0 - VREG_OK is 0 but no Flash error.
+ * -EFAULT - Flash Error is set.
+ */
+ *val = vreg_state;
+
+ return 0;
+}
+
+int schgm_flash_init(struct smb_charger *chg)
+{
+ int rc;
+ u8 reg;
+
+ schgm_flash_parse_dt(chg);
+
+ if (chg->flash_derating_soc != -EINVAL) {
+ rc = smblib_write(chg, SCHGM_SOC_BASED_FLASH_DERATE_TH_CFG_REG,
+ chg->flash_derating_soc);
+ if (rc < 0) {
+ pr_err("Couldn't configure SOC for flash derating rc=%d\n",
+ rc);
+ return rc;
+ }
+ }
+
+ if (chg->flash_disable_soc != -EINVAL) {
+ rc = smblib_write(chg, SCHGM_SOC_BASED_FLASH_DISABLE_TH_CFG_REG,
+ chg->flash_disable_soc);
+ if (rc < 0) {
+ pr_err("Couldn't configure SOC for flash disable rc=%d\n",
+ rc);
+ return rc;
+ }
+ }
+
+ if (chg->headroom_mode != -EINVAL) {
+ /*
+ * configure headroom management policy for
+ * flash and torch mode.
+ */
+ reg = (chg->headroom_mode == FIXED_MODE)
+ ? FORCE_FLASH_BOOST_5V_BIT : 0;
+ rc = smblib_write(chg, SCHGM_FORCE_BOOST_CONTROL, reg);
+ if (rc < 0) {
+ pr_err("Couldn't write force boost control reg rc=%d\n",
+ rc);
+ return rc;
+ }
+
+ reg = (chg->headroom_mode == FIXED_MODE)
+ ? TORCH_PRIORITY_CONTROL_BIT : 0;
+ rc = smblib_write(chg, SCHGM_TORCH_PRIORITY_CONTROL, reg);
+ if (rc < 0) {
+ pr_err("Couldn't force 5V boost in torch mode rc=%d\n",
+ rc);
+ return rc;
+ }
+ }
+
+ if ((chg->flash_derating_soc != -EINVAL)
+ || (chg->flash_disable_soc != -EINVAL)) {
+ /* Check if SOC based derating/disable is enabled */
+ rc = smblib_read(chg, SCHGM_FLASH_CONTROL_REG, ®);
+ if (rc < 0) {
+ pr_err("Couldn't read flash control reg rc=%d\n", rc);
+ return rc;
+ }
+ if (!(reg & SOC_LOW_FOR_FLASH_EN_BIT))
+ pr_warn("Soc based flash derating not enabled\n");
+ }
+
+ chg->flash_init_done = true;
+
+ return 0;
+}
diff --git a/drivers/power/supply/qcom/schgm-flash.h b/drivers/power/supply/qcom/schgm-flash.h
new file mode 100644
index 0000000..b6fff6c
--- /dev/null
+++ b/drivers/power/supply/qcom/schgm-flash.h
@@ -0,0 +1,54 @@
+/* Copyright (c) 2018 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 __SCHGM_FLASH_H__
+#define __SCHGM_FLASH_H__
+
+#include <linux/bitops.h>
+
+#define SCHGM_FLASH_BASE 0xA600
+
+#define SCHGM_FLASH_STATUS_2_REG (SCHGM_FLASH_BASE + 0x07)
+#define VREG_OK_BIT BIT(4)
+
+#define SCHGM_FLASH_STATUS_3_REG (SCHGM_FLASH_BASE + 0x08)
+#define FLASH_STATE_MASK GENMASK(2, 0)
+#define FLASH_ERROR_VAL 0x7
+
+#define SCHGM_FLASH_INT_RT_STS_REG (SCHGM_FLASH_BASE + 0x10)
+
+#define SCHGM_FLASH_STATUS_5_REG (SCHGM_FLASH_BASE + 0x0B)
+
+#define SCHGM_FORCE_BOOST_CONTROL (SCHGM_FLASH_BASE + 0x41)
+#define FORCE_FLASH_BOOST_5V_BIT BIT(0)
+
+#define SCHGM_FLASH_S2_LATCH_RESET_CMD_REG (SCHGM_FLASH_BASE + 0x44)
+#define FLASH_S2_LATCH_RESET_BIT BIT(0)
+
+#define SCHGM_FLASH_CONTROL_REG (SCHGM_FLASH_BASE + 0x60)
+#define SOC_LOW_FOR_FLASH_EN_BIT BIT(7)
+
+#define SCHGM_TORCH_PRIORITY_CONTROL (SCHGM_FLASH_BASE + 0x63)
+#define TORCH_PRIORITY_CONTROL_BIT BIT(0)
+
+#define SCHGM_SOC_BASED_FLASH_DERATE_TH_CFG_REG (SCHGM_FLASH_BASE + 0x67)
+
+#define SCHGM_SOC_BASED_FLASH_DISABLE_TH_CFG_REG \
+ (SCHGM_FLASH_BASE + 0x68)
+
+int schgm_flash_get_vreg_ok(struct smb_charger *chg, int *val);
+int schgm_flash_init(struct smb_charger *chg);
+
+irqreturn_t schgm_flash_default_irq_handler(int irq, void *data);
+irqreturn_t schgm_flash_ilim2_irq_handler(int irq, void *data);
+irqreturn_t schgm_flash_state_change_irq_handler(int irq, void *data);
+#endif /* __SCHGM_FLASH_H__ */
diff --git a/drivers/power/supply/qcom/smb5-lib.h b/drivers/power/supply/qcom/smb5-lib.h
index fa7381c..d7b9a17 100644
--- a/drivers/power/supply/qcom/smb5-lib.h
+++ b/drivers/power/supply/qcom/smb5-lib.h
@@ -143,6 +143,15 @@
IMP_TRIGGER_IRQ,
TEMP_CHANGE_IRQ,
TEMP_CHANGE_SMB_IRQ,
+ /* FLASH */
+ VREG_OK_IRQ,
+ ILIM_S2_IRQ,
+ ILIM_S1_IRQ,
+ VOUT_DOWN_IRQ,
+ VOUT_UP_IRQ,
+ FLASH_STATE_CHANGE_IRQ,
+ TORCH_REQ_IRQ,
+ FLASH_EN_IRQ,
/* END */
SMB_IRQ_MAX,
};
@@ -351,6 +360,13 @@
int pulse_cnt;
int die_health;
+
+ /* flash */
+ u32 flash_derating_soc;
+ u32 flash_disable_soc;
+ u32 headroom_mode;
+ bool flash_init_done;
+ bool flash_active;
};
int smblib_read(struct smb_charger *chg, u16 addr, u8 *val);
diff --git a/drivers/slimbus/slim-msm.c b/drivers/slimbus/slim-msm.c
index 01779f9..24a3ccf 100644
--- a/drivers/slimbus/slim-msm.c
+++ b/drivers/slimbus/slim-msm.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2011-2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2011-2018, 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
@@ -490,32 +490,26 @@
return SLIM_P_INPROGRESS;
}
-static int msm_slim_iommu_map(struct msm_slim_ctrl *dev, phys_addr_t iobuf,
+static dma_addr_t msm_slim_iommu_map(struct msm_slim_ctrl *dev, void *buf_addr,
u32 len)
{
- int ret;
+ dma_addr_t ret;
+ struct device *devp = dev->iommu_desc.cb_dev ? dev->iommu_desc.cb_dev :
+ dev->dev;
+ ret = dma_map_single(devp, buf_addr, len, DMA_BIDIRECTIONAL);
- if (!dev->iommu_desc.cb_dev)
- return 0;
+ if (dma_mapping_error(devp, ret))
+ return DMA_ERROR_CODE;
- ret = iommu_map(dev->iommu_desc.iommu_map->domain,
- rounddown(iobuf, PAGE_SIZE),
- rounddown(iobuf, PAGE_SIZE),
- roundup((len + (iobuf - rounddown(iobuf, PAGE_SIZE))),
- PAGE_SIZE), IOMMU_READ | IOMMU_WRITE);
return ret;
}
-static void msm_slim_iommu_unmap(struct msm_slim_ctrl *dev, phys_addr_t iobuf,
+static void msm_slim_iommu_unmap(struct msm_slim_ctrl *dev, dma_addr_t buf_addr,
u32 len)
{
- if (!dev->iommu_desc.cb_dev)
- return;
-
- iommu_unmap(dev->iommu_desc.iommu_map->domain,
- rounddown(iobuf, PAGE_SIZE),
- roundup((len + (iobuf - rounddown(iobuf, PAGE_SIZE))),
- PAGE_SIZE));
+ struct device *devp = dev->iommu_desc.cb_dev ? dev->iommu_desc.cb_dev :
+ dev->dev;
+ dma_unmap_single(devp, buf_addr, len, DMA_BIDIRECTIONAL);
}
static void msm_slim_port_cb(struct sps_event_notify *ev)
@@ -539,11 +533,12 @@
complete(comp);
}
-int msm_slim_port_xfer(struct slim_controller *ctrl, u8 pn, phys_addr_t iobuf,
+int msm_slim_port_xfer(struct slim_controller *ctrl, u8 pn, void *buf,
u32 len, struct completion *comp)
{
struct sps_register_event sreg;
int ret;
+ dma_addr_t dma_buf;
struct msm_slim_ctrl *dev = slim_get_ctrldata(ctrl);
if (pn >= dev->port_nums)
@@ -552,9 +547,11 @@
if (!dev->pipes[pn].connected)
return -ENOTCONN;
- ret = msm_slim_iommu_map(dev, iobuf, len);
- if (ret)
- return ret;
+ dma_buf = msm_slim_iommu_map(dev, buf, len);
+ if (dma_buf == DMA_ERROR_CODE) {
+ dev_err(dev->dev, "error DMA mapping buffers\n");
+ return -ENOMEM;
+ }
sreg.options = (SPS_EVENT_DESC_DONE|SPS_EVENT_ERROR);
sreg.mode = SPS_TRIGGER_WAIT;
@@ -564,10 +561,10 @@
ret = sps_register_event(dev->pipes[pn].sps, &sreg);
if (ret) {
dev_dbg(dev->dev, "sps register event error:%x\n", ret);
- msm_slim_iommu_unmap(dev, iobuf, len);
+ msm_slim_iommu_unmap(dev, dma_buf, len);
return ret;
}
- ret = sps_transfer_one(dev->pipes[pn].sps, iobuf, len, comp,
+ ret = sps_transfer_one(dev->pipes[pn].sps, dma_buf, len, comp,
SPS_IOVEC_FLAG_INT);
dev_dbg(dev->dev, "sps submit xfer error code:%x\n", ret);
if (!ret) {
@@ -581,7 +578,7 @@
/* Make sure that port registers are updated before returning */
mb();
} else {
- msm_slim_iommu_unmap(dev, iobuf, len);
+ msm_slim_iommu_unmap(dev, dma_buf, len);
}
return ret;
diff --git a/drivers/slimbus/slim-msm.h b/drivers/slimbus/slim-msm.h
index 5859c5f..edebfd9 100644
--- a/drivers/slimbus/slim-msm.h
+++ b/drivers/slimbus/slim-msm.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2011-2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2011-2018, 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
@@ -423,7 +423,7 @@
int msm_slim_connect_pipe_port(struct msm_slim_ctrl *dev, u8 pn);
enum slim_port_err msm_slim_port_xfer_status(struct slim_controller *ctr,
u8 pn, phys_addr_t *done_buf, u32 *done_len);
-int msm_slim_port_xfer(struct slim_controller *ctrl, u8 pn, phys_addr_t iobuf,
+int msm_slim_port_xfer(struct slim_controller *ctrl, u8 pn, void *buf,
u32 len, struct completion *comp);
int msm_send_msg_buf(struct msm_slim_ctrl *dev, u32 *buf, u8 len, u32 tx_reg);
u32 *msm_get_msg_buf(struct msm_slim_ctrl *dev, int len,
diff --git a/drivers/slimbus/slimbus.c b/drivers/slimbus/slimbus.c
index f34467b..1140b33 100644
--- a/drivers/slimbus/slimbus.c
+++ b/drivers/slimbus/slimbus.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2011-2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2011-2018, 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
@@ -1600,14 +1600,14 @@
* Client will call slim_port_get_xfer_status to get error and/or number of
* bytes transferred if used asynchronously.
*/
-int slim_port_xfer(struct slim_device *sb, u32 ph, phys_addr_t iobuf, u32 len,
+int slim_port_xfer(struct slim_device *sb, u32 ph, void *buf, u32 len,
struct completion *comp)
{
struct slim_controller *ctrl = sb->ctrl;
u8 pn = SLIM_HDL_TO_PORT(ph);
dev_dbg(&ctrl->dev, "port xfer: num:%d", pn);
- return ctrl->port_xfer(ctrl, pn, iobuf, len, comp);
+ return ctrl->port_xfer(ctrl, pn, buf, len, comp);
}
EXPORT_SYMBOL(slim_port_xfer);
diff --git a/drivers/soc/qcom/Kconfig b/drivers/soc/qcom/Kconfig
index 770f056..e5e3147 100644
--- a/drivers/soc/qcom/Kconfig
+++ b/drivers/soc/qcom/Kconfig
@@ -786,4 +786,13 @@
sub-system to USB on APSS side. The driver acts as a bridge between the
MHI and USB interface. If unsure, say N.
+config MSM_BAM_DMUX
+ bool "BAM Data Mux Driver"
+ depends on SPS
+ help
+ Support Muxed Data Channels over BAM interface.
+ BAM has a limited number of pipes. This driver
+ provides a means to support more logical channels
+ via muxing than BAM could without muxing.
+
source "drivers/soc/qcom/wcnss/Kconfig"
diff --git a/drivers/soc/qcom/Makefile b/drivers/soc/qcom/Makefile
index c882403..c7c7f62 100644
--- a/drivers/soc/qcom/Makefile
+++ b/drivers/soc/qcom/Makefile
@@ -96,4 +96,5 @@
obj-$(CONFIG_MSM_REMOTEQDSS) += remoteqdss.o
obj-$(CONFIG_QSEE_IPC_IRQ_BRIDGE) += qsee_ipc_irq_bridge.o
obj-$(CONFIG_QCOM_QDSS_BRIDGE) += qdss_bridge.o
+obj-$(CONFIG_MSM_BAM_DMUX) += bam_dmux.o
obj-$(CONFIG_WCNSS_CORE) += wcnss/
diff --git a/drivers/soc/qcom/bam_dmux.c b/drivers/soc/qcom/bam_dmux.c
new file mode 100644
index 0000000..3b75e74
--- /dev/null
+++ b/drivers/soc/qcom/bam_dmux.c
@@ -0,0 +1,2853 @@
+/* Copyright (c) 2011-2016, 2018, 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.
+ *
+ */
+
+/*
+ * BAM DMUX module.
+ */
+
+#define DEBUG
+
+#include <linux/delay.h>
+#include <linux/module.h>
+#include <linux/netdevice.h>
+#include <linux/platform_device.h>
+#include <linux/sched.h>
+#include <linux/skbuff.h>
+#include <linux/debugfs.h>
+#include <linux/clk.h>
+#include <linux/pm.h>
+#include <linux/of.h>
+#include <linux/ipc_logging.h>
+#include <linux/srcu.h>
+#include <linux/msm-sps.h>
+#include <linux/sizes.h>
+#include <soc/qcom/bam_dmux.h>
+#include <soc/qcom/smsm.h>
+#include <soc/qcom/subsystem_restart.h>
+#include <soc/qcom/subsystem_notif.h>
+
+#include "bam_dmux_private.h"
+
+#define BAM_CH_LOCAL_OPEN 0x1
+#define BAM_CH_REMOTE_OPEN 0x2
+#define BAM_CH_IN_RESET 0x4
+
+#define LOW_WATERMARK 2
+#define HIGH_WATERMARK 4
+#define DEFAULT_POLLING_MIN_SLEEP (950)
+#define MAX_POLLING_SLEEP (6050)
+#define MIN_POLLING_SLEEP (950)
+
+static int msm_bam_dmux_debug_enable;
+module_param_named(debug_enable, msm_bam_dmux_debug_enable, int, 0664);
+static int POLLING_MIN_SLEEP = 2950;
+module_param_named(min_sleep, POLLING_MIN_SLEEP, int, 0664);
+static int POLLING_MAX_SLEEP = 3050;
+module_param_named(max_sleep, POLLING_MAX_SLEEP, int, 0664);
+static int POLLING_INACTIVITY = 1;
+module_param_named(inactivity, POLLING_INACTIVITY, int, 0664);
+static int bam_adaptive_timer_enabled;
+module_param_named(adaptive_timer_enabled,
+ bam_adaptive_timer_enabled, int, 0664);
+
+static struct bam_ops_if bam_default_ops = {
+ /* smsm */
+ .smsm_change_state_ptr = &smsm_change_state,
+ .smsm_get_state_ptr = &smsm_get_state,
+ .smsm_state_cb_register_ptr = &smsm_state_cb_register,
+ .smsm_state_cb_deregister_ptr = &smsm_state_cb_deregister,
+
+ /* sps */
+ .sps_connect_ptr = &sps_connect,
+ .sps_disconnect_ptr = &sps_disconnect,
+ .sps_register_bam_device_ptr = &sps_register_bam_device,
+ .sps_deregister_bam_device_ptr = &sps_deregister_bam_device,
+ .sps_alloc_endpoint_ptr = &sps_alloc_endpoint,
+ .sps_free_endpoint_ptr = &sps_free_endpoint,
+ .sps_set_config_ptr = &sps_set_config,
+ .sps_get_config_ptr = &sps_get_config,
+ .sps_device_reset_ptr = &sps_device_reset,
+ .sps_register_event_ptr = &sps_register_event,
+ .sps_transfer_one_ptr = &sps_transfer_one,
+ .sps_get_iovec_ptr = &sps_get_iovec,
+ .sps_get_unused_desc_num_ptr = &sps_get_unused_desc_num,
+
+ .dma_to = DMA_TO_DEVICE,
+ .dma_from = DMA_FROM_DEVICE,
+};
+static struct bam_ops_if *bam_ops = &bam_default_ops;
+
+#if defined(DEBUG)
+static uint32_t bam_dmux_read_cnt;
+static uint32_t bam_dmux_write_cnt;
+static uint32_t bam_dmux_write_cpy_cnt;
+static uint32_t bam_dmux_write_cpy_bytes;
+static uint32_t bam_dmux_tx_sps_failure_cnt;
+static uint32_t bam_dmux_tx_stall_cnt;
+static atomic_t bam_dmux_ack_out_cnt = ATOMIC_INIT(0);
+static atomic_t bam_dmux_ack_in_cnt = ATOMIC_INIT(0);
+static atomic_t bam_dmux_a2_pwr_cntl_in_cnt = ATOMIC_INIT(0);
+
+#define DBG(x...) do { \
+ if (msm_bam_dmux_debug_enable) \
+ pr_debug(x); \
+ } while (0)
+
+#define DBG_INC_READ_CNT(x) do { \
+ bam_dmux_read_cnt += (x); \
+ if (msm_bam_dmux_debug_enable) \
+ pr_debug("%s: total read bytes %u\n", \
+ __func__, bam_dmux_read_cnt); \
+ } while (0)
+
+#define DBG_INC_WRITE_CNT(x) do { \
+ bam_dmux_write_cnt += (x); \
+ if (msm_bam_dmux_debug_enable) \
+ pr_debug("%s: total written bytes %u\n", \
+ __func__, bam_dmux_write_cnt); \
+ } while (0)
+
+#define DBG_INC_WRITE_CPY(x) do { \
+ bam_dmux_write_cpy_bytes += (x); \
+ bam_dmux_write_cpy_cnt++; \
+ if (msm_bam_dmux_debug_enable) \
+ pr_debug("%s: total write copy cnt %u, bytes %u\n", \
+ __func__, bam_dmux_write_cpy_cnt, \
+ bam_dmux_write_cpy_bytes); \
+ } while (0)
+
+#define DBG_INC_TX_SPS_FAILURE_CNT() (bam_dmux_tx_sps_failure_cnt++)
+
+#define DBG_INC_TX_STALL_CNT() (bam_dmux_tx_stall_cnt++)
+
+#define DBG_INC_ACK_OUT_CNT() \
+ atomic_inc(&bam_dmux_ack_out_cnt)
+
+#define DBG_INC_A2_POWER_CONTROL_IN_CNT() \
+ atomic_inc(&bam_dmux_a2_pwr_cntl_in_cnt)
+
+#define DBG_INC_ACK_IN_CNT() \
+ atomic_inc(&bam_dmux_ack_in_cnt)
+#else
+#define DBG(x...) do { } while (0)
+#define DBG_INC_READ_CNT(x...) do { } while (0)
+#define DBG_INC_WRITE_CNT(x...) do { } while (0)
+#define DBG_INC_WRITE_CPY(x...) do { } while (0)
+#define DBG_INC_TX_SPS_FAILURE_CNT() do { } while (0)
+#define DBG_INC_TX_STALL_CNT() do { } while (0)
+#define DBG_INC_ACK_OUT_CNT() do { } while (0)
+#define DBG_INC_A2_POWER_CONTROL_IN_CNT() \
+ do { } while (0)
+#define DBG_INC_ACK_IN_CNT() do { } while (0)
+#endif
+
+struct bam_ch_info {
+ uint32_t status;
+ void (*notify)(void *, int, unsigned long);
+ void *priv;
+ spinlock_t lock;
+ struct platform_device *pdev;
+ char name[BAM_DMUX_CH_NAME_MAX_LEN];
+ int num_tx_pkts;
+ int use_wm;
+};
+
+#define A2_NUM_PIPES 6
+#define A2_SUMMING_THRESHOLD 4096
+#define A2_PHYS_BASE 0x124C2000
+#define A2_PHYS_SIZE 0x2000
+#define DEFAULT_NUM_BUFFERS 32
+
+#ifndef A2_BAM_IRQ
+#define A2_BAM_IRQ -1
+#endif
+
+static phys_addr_t a2_phys_base;
+static uint32_t a2_phys_size;
+static int a2_bam_irq;
+static struct sps_bam_props a2_props;
+static unsigned long a2_device_handle;
+static struct sps_pipe *bam_tx_pipe;
+static struct sps_pipe *bam_rx_pipe;
+static struct sps_connect tx_connection;
+static struct sps_connect rx_connection;
+static struct sps_mem_buffer tx_desc_mem_buf;
+static struct sps_mem_buffer rx_desc_mem_buf;
+static struct sps_register_event tx_register_event;
+static struct sps_register_event rx_register_event;
+static bool satellite_mode;
+static uint32_t num_buffers;
+static unsigned long long last_rx_pkt_timestamp;
+static struct device *dma_dev;
+static bool dynamic_mtu_enabled;
+static uint16_t ul_mtu = DEFAULT_BUFFER_SIZE;
+static uint16_t dl_mtu = DEFAULT_BUFFER_SIZE;
+static uint16_t buffer_size = DEFAULT_BUFFER_SIZE;
+static bool no_cpu_affinity;
+
+static struct bam_ch_info bam_ch[BAM_DMUX_NUM_CHANNELS];
+static int bam_mux_initialized;
+
+static int polling_mode;
+static unsigned long rx_timer_interval;
+
+static LIST_HEAD(bam_rx_pool);
+static DEFINE_MUTEX(bam_rx_pool_mutexlock);
+static int bam_rx_pool_len;
+static LIST_HEAD(bam_tx_pool);
+static DEFINE_SPINLOCK(bam_tx_pool_spinlock);
+static DEFINE_MUTEX(bam_pdev_mutexlock);
+
+static void notify_all(int event, unsigned long data);
+static void bam_mux_write_done(struct work_struct *work);
+static void handle_bam_mux_cmd(struct work_struct *work);
+static void rx_timer_work_func(struct work_struct *work);
+static void queue_rx_work_func(struct work_struct *work);
+static int ssrestart_check(void);
+
+static DECLARE_WORK(rx_timer_work, rx_timer_work_func);
+static DECLARE_WORK(queue_rx_work, queue_rx_work_func);
+
+static struct workqueue_struct *bam_mux_rx_workqueue;
+static struct workqueue_struct *bam_mux_tx_workqueue;
+
+static struct srcu_struct bam_dmux_srcu;
+
+/* A2 power collaspe */
+#define UL_TIMEOUT_DELAY 1000 /* in ms */
+#define UL_FAST_TIMEOUT_DELAY 100 /* in ms */
+#define SHUTDOWN_TIMEOUT_MS 500
+#define UL_WAKEUP_TIMEOUT_MS 2000
+static uint32_t ul_timeout_delay = UL_TIMEOUT_DELAY;
+static void toggle_apps_ack(void);
+static void reconnect_to_bam(void);
+static void disconnect_to_bam(void);
+static void ul_wakeup(void);
+static void ul_timeout(struct work_struct *work);
+static void vote_dfab(void);
+static void unvote_dfab(void);
+static void kickoff_ul_wakeup_func(struct work_struct *work);
+static void grab_wakelock(void);
+static void release_wakelock(void);
+
+static int bam_is_connected;
+static DEFINE_MUTEX(wakeup_lock);
+static struct completion ul_wakeup_ack_completion;
+static struct completion bam_connection_completion;
+static struct delayed_work ul_timeout_work;
+static int ul_packet_written;
+static atomic_t ul_ondemand_vote = ATOMIC_INIT(0);
+static struct clk *dfab_clk, *xo_clk;
+static DEFINE_RWLOCK(ul_wakeup_lock);
+static DECLARE_WORK(kickoff_ul_wakeup, kickoff_ul_wakeup_func);
+static int bam_connection_is_active;
+static int wait_for_ack;
+static struct wakeup_source bam_wakelock;
+static int a2_pc_disabled;
+static DEFINE_MUTEX(dfab_status_lock);
+static int dfab_is_on;
+static int wait_for_dfab;
+static struct completion dfab_unvote_completion;
+static DEFINE_SPINLOCK(wakelock_reference_lock);
+static int wakelock_reference_count;
+static int a2_pc_disabled_wakelock_skipped;
+static LIST_HEAD(bam_other_notify_funcs);
+static DEFINE_MUTEX(smsm_cb_lock);
+static DEFINE_MUTEX(delayed_ul_vote_lock);
+static int need_delayed_ul_vote;
+static int ssr_skipped_disconnect;
+static struct completion shutdown_completion;
+
+struct outside_notify_func {
+ void (*notify)(void *, int, unsigned long);
+ void *priv;
+ struct list_head list_node;
+};
+/* End A2 power collaspe */
+
+/* subsystem restart */
+static int restart_notifier_cb(struct notifier_block *this,
+ unsigned long code,
+ void *data);
+
+static struct notifier_block restart_notifier = {
+ .notifier_call = restart_notifier_cb,
+};
+static int in_global_reset;
+/* end subsystem restart */
+
+#define bam_ch_is_open(x) \
+ (bam_ch[(x)].status == (BAM_CH_LOCAL_OPEN | BAM_CH_REMOTE_OPEN))
+
+#define bam_ch_is_local_open(x) \
+ (bam_ch[(x)].status & BAM_CH_LOCAL_OPEN)
+
+#define bam_ch_is_remote_open(x) \
+ (bam_ch[(x)].status & BAM_CH_REMOTE_OPEN)
+
+#define bam_ch_is_in_reset(x) \
+ (bam_ch[(x)].status & BAM_CH_IN_RESET)
+
+static int bam_dmux_uplink_vote;
+static int bam_dmux_power_state;
+
+static void *bam_ipc_log_txt;
+
+#define BAM_IPC_LOG_PAGES 5
+
+/**
+ * Log a state change along with a small message.
+ * Complete size of message is limited to @todo.
+ * Logging is done using IPC Logging infrastructure.
+ *
+ * States
+ * D: 1 = Power collapse disabled
+ * R: 1 = in global reset
+ * P: 1 = BAM is powered up
+ * A: 1 = BAM initialized and ready for data
+ * V: 1 = Uplink vote for power
+ * U: 1 = Uplink active
+ * W: 1 = Uplink Wait-for-ack
+ * A: 1 = Uplink ACK received
+ * #: >=1 On-demand uplink vote
+ * D: 1 = Disconnect ACK active
+ */
+
+#define BAM_DMUX_LOG(fmt, args...) \
+do { \
+ if (bam_ipc_log_txt) { \
+ ipc_log_string(bam_ipc_log_txt, \
+ "<DMUX> %c%c%c%c %c%c%c%c%d " fmt, \
+ a2_pc_disabled ? 'D' : 'd', \
+ in_global_reset ? 'R' : 'r', \
+ bam_dmux_power_state ? 'P' : 'p', \
+ bam_connection_is_active ? 'A' : 'a', \
+ bam_dmux_uplink_vote ? 'V' : 'v', \
+ bam_is_connected ? 'U' : 'u', \
+ wait_for_ack ? 'W' : 'w', \
+ ul_wakeup_ack_completion.done ? 'A' : 'a', \
+ atomic_read(&ul_ondemand_vote), \
+ args); \
+ } \
+} while (0)
+
+#define DMUX_LOG_KERR(fmt, args...) \
+do { \
+ BAM_DMUX_LOG(fmt, args); \
+ pr_err(fmt, args); \
+} while (0)
+
+static inline void set_tx_timestamp(struct tx_pkt_info *pkt)
+{
+ unsigned long long t_now;
+
+ t_now = sched_clock();
+ pkt->ts_nsec = do_div(t_now, 1000000000U);
+ pkt->ts_sec = (unsigned int)t_now;
+}
+
+static inline void verify_tx_queue_is_empty(const char *func)
+{
+ unsigned long flags;
+ struct tx_pkt_info *info;
+ int reported = 0;
+
+ spin_lock_irqsave(&bam_tx_pool_spinlock, flags);
+ list_for_each_entry(info, &bam_tx_pool, list_node) {
+ if (!reported) {
+ BAM_DMUX_LOG("%s: tx pool not empty\n", func);
+ if (!in_global_reset)
+ pr_err("%s: tx pool not empty\n", func);
+ reported = 1;
+ }
+ BAM_DMUX_LOG("%s: node=%pK ts=%u.%09lu\n", __func__,
+ &info->list_node, info->ts_sec, info->ts_nsec);
+ if (!in_global_reset)
+ pr_err("%s: node=%pK ts=%u.%09lu\n", __func__,
+ &info->list_node, info->ts_sec, info->ts_nsec);
+ }
+ spin_unlock_irqrestore(&bam_tx_pool_spinlock, flags);
+}
+
+static void __queue_rx(gfp_t alloc_flags)
+{
+ void *ptr;
+ struct rx_pkt_info *info;
+ int ret;
+ int rx_len_cached;
+ uint16_t current_buffer_size;
+
+ mutex_lock(&bam_rx_pool_mutexlock);
+ rx_len_cached = bam_rx_pool_len;
+ current_buffer_size = buffer_size;
+ mutex_unlock(&bam_rx_pool_mutexlock);
+
+ while (bam_connection_is_active && rx_len_cached < num_buffers) {
+ if (in_global_reset)
+ goto fail;
+
+ info = kmalloc(sizeof(*info), alloc_flags);
+ if (!info)
+ goto fail;
+
+ info->len = current_buffer_size;
+
+ INIT_WORK(&info->work, handle_bam_mux_cmd);
+
+ info->skb = __dev_alloc_skb(info->len, alloc_flags);
+ if (info->skb == NULL)
+ goto fail_info;
+
+ ptr = skb_put(info->skb, info->len);
+
+ info->dma_address = dma_map_single(dma_dev, ptr, info->len,
+ bam_ops->dma_from);
+ if (info->dma_address == 0 || info->dma_address == ~0) {
+ DMUX_LOG_KERR("%s:dma_map_single failure %pK for %pK\n",
+ __func__, (void *)info->dma_address, ptr);
+ goto fail_skb;
+ }
+
+ mutex_lock(&bam_rx_pool_mutexlock);
+ list_add_tail(&info->list_node, &bam_rx_pool);
+ rx_len_cached = ++bam_rx_pool_len;
+ current_buffer_size = buffer_size;
+ ret = bam_ops->sps_transfer_one_ptr(bam_rx_pipe,
+ info->dma_address, info->len, info, 0);
+ if (ret) {
+ list_del(&info->list_node);
+ rx_len_cached = --bam_rx_pool_len;
+ mutex_unlock(&bam_rx_pool_mutexlock);
+ DMUX_LOG_KERR("%s: sps_transfer_one failed %d\n",
+ __func__, ret);
+
+ dma_unmap_single(dma_dev, info->dma_address,
+ info->len,
+ bam_ops->dma_from);
+
+ goto fail_skb;
+ }
+ mutex_unlock(&bam_rx_pool_mutexlock);
+
+ }
+ return;
+
+fail_skb:
+ dev_kfree_skb_any(info->skb);
+
+fail_info:
+ kfree(info);
+
+fail:
+ if (!in_global_reset) {
+ DMUX_LOG_KERR("%s: rescheduling\n", __func__);
+ schedule_work(&queue_rx_work);
+ }
+}
+
+static void queue_rx(void)
+{
+ /*
+ * Hot path. Delays waiting for the allocation to find memory if its
+ * not immediately available, and delays from logging allocation
+ * failures which cannot be tolerated at this time.
+ */
+ __queue_rx(GFP_NOWAIT | __GFP_NOWARN);
+}
+
+static void queue_rx_work_func(struct work_struct *work)
+{
+ /*
+ * Cold path. Delays can be tolerated. Use of GFP_KERNEL should
+ * guarantee the requested memory will be found, after some ammount of
+ * delay.
+ */
+ __queue_rx(GFP_KERNEL);
+}
+
+/**
+ * process_dynamic_mtu() - Process the dynamic MTU signal bit from data cmds
+ * @current_state: State of the dynamic MTU signal bit for the current
+ * data command packet.
+ */
+static void process_dynamic_mtu(bool current_state)
+{
+ static bool old_state;
+
+ if (!dynamic_mtu_enabled)
+ return;
+
+ if (old_state == current_state)
+ return;
+
+ mutex_lock(&bam_rx_pool_mutexlock);
+ if (current_state) {
+ buffer_size = dl_mtu;
+ BAM_DMUX_LOG("%s: switching to large mtu %x\n", __func__,
+ dl_mtu);
+ } else {
+ buffer_size = DEFAULT_BUFFER_SIZE;
+ BAM_DMUX_LOG("%s: switching to reg mtu %x\n", __func__,
+ DEFAULT_BUFFER_SIZE);
+ }
+ mutex_unlock(&bam_rx_pool_mutexlock);
+
+ old_state = current_state;
+}
+
+static void bam_mux_process_data(struct sk_buff *rx_skb)
+{
+ unsigned long flags;
+ struct bam_mux_hdr *rx_hdr;
+ unsigned long event_data;
+ uint8_t ch_id;
+ void (*notify)(void *, int, unsigned long);
+ void *priv;
+
+ rx_hdr = (struct bam_mux_hdr *)rx_skb->data;
+ ch_id = rx_hdr->ch_id;
+
+ process_dynamic_mtu(rx_hdr->signal & DYNAMIC_MTU_MASK);
+
+ rx_skb->data = (unsigned char *)(rx_hdr + 1);
+ skb_set_tail_pointer(rx_skb, rx_hdr->pkt_len);
+ rx_skb->len = rx_hdr->pkt_len;
+ rx_skb->truesize = rx_hdr->pkt_len + sizeof(struct sk_buff);
+
+ event_data = (unsigned long)(rx_skb);
+ notify = NULL;
+ priv = NULL;
+
+ spin_lock_irqsave(&bam_ch[ch_id].lock, flags);
+ if (bam_ch[ch_id].notify) {
+ notify = bam_ch[ch_id].notify;
+ priv = bam_ch[ch_id].priv;
+ }
+ spin_unlock_irqrestore(&bam_ch[ch_id].lock, flags);
+ if (notify)
+ notify(priv, BAM_DMUX_RECEIVE, event_data);
+ else
+ dev_kfree_skb_any(rx_skb);
+
+ queue_rx();
+}
+
+/**
+ * set_ul_mtu() - Converts the MTU code received from the remote side in the
+ * open cmd into a byte value.
+ * @mtu_code: MTU size code to translate.
+ * @reset: Reset the MTU.
+ */
+static void set_ul_mtu(int mtu_code, bool reset)
+{
+ static bool first = true;
+
+ if (reset) {
+ first = true;
+ ul_mtu = DEFAULT_BUFFER_SIZE;
+ return;
+ }
+
+ switch (mtu_code) {
+ case 0:
+ if (ul_mtu != SZ_2K && !first) {
+ BAM_DMUX_LOG("%s: bad request for 2k, ul_mtu is %d\n",
+ __func__, ul_mtu);
+ ssrestart_check();
+ }
+ ul_mtu = SZ_2K;
+ break;
+ case 1:
+ if (ul_mtu != SZ_4K && !first) {
+ BAM_DMUX_LOG("%s: bad request for 4k, ul_mtu is %d\n",
+ __func__, ul_mtu);
+ ssrestart_check();
+ }
+ ul_mtu = SZ_4K;
+ break;
+ case 2:
+ if (ul_mtu != SZ_8K && !first) {
+ BAM_DMUX_LOG("%s: bad request for 8k, ul_mtu is %d\n",
+ __func__, ul_mtu);
+ ssrestart_check();
+ }
+ ul_mtu = SZ_8K;
+ break;
+ case 3:
+ if (ul_mtu != SZ_16K && !first) {
+ BAM_DMUX_LOG("%s: bad request for 16k, ul_mtu is %d\n",
+ __func__, ul_mtu);
+ ssrestart_check();
+ }
+ ul_mtu = SZ_16K;
+ break;
+ default:
+ BAM_DMUX_LOG("%s: bad request %d\n", __func__, mtu_code);
+ ssrestart_check();
+ break;
+ }
+
+ first = false;
+}
+
+static inline void handle_bam_mux_cmd_open(struct bam_mux_hdr *rx_hdr)
+{
+ unsigned long flags;
+ int ret;
+
+ mutex_lock(&bam_pdev_mutexlock);
+ if (in_global_reset) {
+ BAM_DMUX_LOG("%s: open cid %d aborted due to ssr\n",
+ __func__, rx_hdr->ch_id);
+ mutex_unlock(&bam_pdev_mutexlock);
+ queue_rx();
+ return;
+ }
+ if (rx_hdr->signal & DYNAMIC_MTU_MASK) {
+ dynamic_mtu_enabled = true;
+ set_ul_mtu((rx_hdr->signal & MTU_SIZE_MASK) >> MTU_SIZE_SHIFT,
+ false);
+ } else {
+ set_ul_mtu(0, false);
+ }
+ spin_lock_irqsave(&bam_ch[rx_hdr->ch_id].lock, flags);
+ if (bam_ch_is_remote_open(rx_hdr->ch_id)) {
+ /*
+ * Receiving an open command for a channel that is already open
+ * is an invalid operation and likely signifies a significant
+ * issue within the A2 which should be caught immediately
+ * before it snowballs and the root cause is lost.
+ */
+ panic("A2 sent invalid duplicate open for channel %d\n",
+ rx_hdr->ch_id);
+ }
+ bam_ch[rx_hdr->ch_id].status |= BAM_CH_REMOTE_OPEN;
+ bam_ch[rx_hdr->ch_id].num_tx_pkts = 0;
+ spin_unlock_irqrestore(&bam_ch[rx_hdr->ch_id].lock, flags);
+ ret = platform_device_add(bam_ch[rx_hdr->ch_id].pdev);
+ if (ret)
+ pr_err("%s: platform_device_add() error: %d\n",
+ __func__, ret);
+ mutex_unlock(&bam_pdev_mutexlock);
+ queue_rx();
+}
+
+static void handle_bam_mux_cmd(struct work_struct *work)
+{
+ unsigned long flags;
+ struct bam_mux_hdr *rx_hdr;
+ struct rx_pkt_info *info;
+ struct sk_buff *rx_skb;
+ uint16_t sps_size;
+
+ info = container_of(work, struct rx_pkt_info, work);
+ rx_skb = info->skb;
+ dma_unmap_single(dma_dev, info->dma_address, info->len,
+ bam_ops->dma_from);
+ sps_size = info->sps_size;
+ kfree(info);
+
+ rx_hdr = (struct bam_mux_hdr *)rx_skb->data;
+
+ DBG_INC_READ_CNT(sizeof(struct bam_mux_hdr));
+ DBG("%s: magic %x signal %x cmd %d pad %d ch %d len %d\n", __func__,
+ rx_hdr->magic_num, rx_hdr->signal, rx_hdr->cmd,
+ rx_hdr->pad_len, rx_hdr->ch_id, rx_hdr->pkt_len);
+ if (rx_hdr->magic_num != BAM_MUX_HDR_MAGIC_NO) {
+ DMUX_LOG_KERR(
+ "%s: dropping invalid hdr. magic %x signal %x cmd %d pad %d ch %d len %d\n",
+ __func__, rx_hdr->magic_num, rx_hdr->signal,
+ rx_hdr->cmd, rx_hdr->pad_len, rx_hdr->ch_id,
+ rx_hdr->pkt_len);
+ dev_kfree_skb_any(rx_skb);
+ queue_rx();
+ return;
+ }
+
+ if (rx_hdr->ch_id >= BAM_DMUX_NUM_CHANNELS) {
+ DMUX_LOG_KERR(
+ "%s: dropping invalid LCID %d signal %x cmd %d pad %d ch %d len %d\n",
+ __func__, rx_hdr->ch_id, rx_hdr->signal, rx_hdr->cmd,
+ rx_hdr->pad_len, rx_hdr->ch_id, rx_hdr->pkt_len);
+ dev_kfree_skb_any(rx_skb);
+ queue_rx();
+ return;
+ }
+
+ switch (rx_hdr->cmd) {
+ case BAM_MUX_HDR_CMD_DATA:
+ if (rx_hdr->pkt_len == 0xffff)
+ /* SPS includes the header bytes, need just payload */
+ rx_hdr->pkt_len = sps_size - sizeof(*rx_hdr);
+ DBG_INC_READ_CNT(rx_hdr->pkt_len);
+ bam_mux_process_data(rx_skb);
+ break;
+ case BAM_MUX_HDR_CMD_OPEN:
+ BAM_DMUX_LOG("%s: opening cid %d PC enabled\n", __func__,
+ rx_hdr->ch_id);
+ handle_bam_mux_cmd_open(rx_hdr);
+ dev_kfree_skb_any(rx_skb);
+ break;
+ case BAM_MUX_HDR_CMD_OPEN_NO_A2_PC:
+ BAM_DMUX_LOG("%s: opening cid %d PC disabled\n", __func__,
+ rx_hdr->ch_id);
+
+ if (!a2_pc_disabled) {
+ a2_pc_disabled = 1;
+ ul_wakeup();
+ }
+
+ handle_bam_mux_cmd_open(rx_hdr);
+ dev_kfree_skb_any(rx_skb);
+ break;
+ case BAM_MUX_HDR_CMD_CLOSE:
+ /* probably should drop pending write */
+ BAM_DMUX_LOG("%s: closing cid %d\n", __func__,
+ rx_hdr->ch_id);
+ mutex_lock(&bam_pdev_mutexlock);
+ if (in_global_reset) {
+ BAM_DMUX_LOG("%s: close cid %d aborted due to ssr\n",
+ __func__, rx_hdr->ch_id);
+ mutex_unlock(&bam_pdev_mutexlock);
+ break;
+ }
+ spin_lock_irqsave(&bam_ch[rx_hdr->ch_id].lock, flags);
+ bam_ch[rx_hdr->ch_id].status &= ~BAM_CH_REMOTE_OPEN;
+ spin_unlock_irqrestore(&bam_ch[rx_hdr->ch_id].lock, flags);
+ platform_device_unregister(bam_ch[rx_hdr->ch_id].pdev);
+ bam_ch[rx_hdr->ch_id].pdev =
+ platform_device_alloc(bam_ch[rx_hdr->ch_id].name, 2);
+ if (!bam_ch[rx_hdr->ch_id].pdev)
+ pr_err("%s: platform_device_alloc failed\n", __func__);
+ mutex_unlock(&bam_pdev_mutexlock);
+ dev_kfree_skb_any(rx_skb);
+ queue_rx();
+ break;
+ default:
+ DMUX_LOG_KERR(
+ "%s: dropping invalid hdr. magic %x signal %x cmd %d pad %d ch %d len %d\n",
+ __func__, rx_hdr->magic_num, rx_hdr->signal,
+ rx_hdr->cmd, rx_hdr->pad_len, rx_hdr->ch_id,
+ rx_hdr->pkt_len);
+ dev_kfree_skb_any(rx_skb);
+ queue_rx();
+ return;
+ }
+}
+
+static int bam_mux_write_cmd(void *data, uint32_t len)
+{
+ int rc;
+ struct tx_pkt_info *pkt;
+ dma_addr_t dma_address;
+ unsigned long flags;
+
+ pkt = kmalloc(sizeof(struct tx_pkt_info), GFP_ATOMIC);
+ if (pkt == NULL) {
+ rc = -ENOMEM;
+ return rc;
+ }
+
+ dma_address = dma_map_single(dma_dev, data, len,
+ bam_ops->dma_to);
+ if (!dma_address) {
+ pr_err("%s: dma_map_single() failed\n", __func__);
+ kfree(pkt);
+ rc = -ENOMEM;
+ return rc;
+ }
+ pkt->skb = (struct sk_buff *)(data);
+ pkt->len = len;
+ pkt->dma_address = dma_address;
+ pkt->is_cmd = 1;
+ set_tx_timestamp(pkt);
+ INIT_WORK(&pkt->work, bam_mux_write_done);
+ spin_lock_irqsave(&bam_tx_pool_spinlock, flags);
+ list_add_tail(&pkt->list_node, &bam_tx_pool);
+ rc = bam_ops->sps_transfer_one_ptr(bam_tx_pipe, dma_address, len,
+ pkt, SPS_IOVEC_FLAG_EOT);
+ if (rc) {
+ DMUX_LOG_KERR("%s sps_transfer_one failed rc=%d\n",
+ __func__, rc);
+ list_del(&pkt->list_node);
+ DBG_INC_TX_SPS_FAILURE_CNT();
+ spin_unlock_irqrestore(&bam_tx_pool_spinlock, flags);
+ dma_unmap_single(dma_dev, pkt->dma_address,
+ pkt->len,
+ bam_ops->dma_to);
+ kfree(pkt);
+ } else {
+ spin_unlock_irqrestore(&bam_tx_pool_spinlock, flags);
+ }
+
+ ul_packet_written = 1;
+ return rc;
+}
+
+static void bam_mux_write_done(struct work_struct *work)
+{
+ struct sk_buff *skb;
+ struct bam_mux_hdr *hdr;
+ struct tx_pkt_info *info;
+ struct tx_pkt_info *info_expected;
+ unsigned long event_data;
+ unsigned long flags;
+
+ if (in_global_reset)
+ return;
+
+ info = container_of(work, struct tx_pkt_info, work);
+
+ spin_lock_irqsave(&bam_tx_pool_spinlock, flags);
+ info_expected = list_first_entry(&bam_tx_pool,
+ struct tx_pkt_info, list_node);
+ if (unlikely(info != info_expected)) {
+ struct tx_pkt_info *errant_pkt;
+
+ BAM_DMUX_LOG(
+ "%s: bam_tx_pool mismatch .next=%pK, list_node=%pK, ts=%u.%09lu\n",
+ __func__, bam_tx_pool.next, &info->list_node,
+ info->ts_sec, info->ts_nsec
+ );
+
+ list_for_each_entry(errant_pkt, &bam_tx_pool, list_node) {
+ BAM_DMUX_LOG("%s: node=%pK ts=%u.%09lu\n", __func__,
+ &errant_pkt->list_node, errant_pkt->ts_sec,
+ errant_pkt->ts_nsec);
+
+ }
+ spin_unlock_irqrestore(&bam_tx_pool_spinlock, flags);
+ WARN_ON(1);
+ }
+ list_del(&info->list_node);
+ spin_unlock_irqrestore(&bam_tx_pool_spinlock, flags);
+
+ if (info->is_cmd) {
+ kfree(info->skb);
+ kfree(info);
+ return;
+ }
+ skb = info->skb;
+ kfree(info);
+ hdr = (struct bam_mux_hdr *)skb->data;
+ DBG_INC_WRITE_CNT(skb->len);
+ /* Restore skb for client */
+ skb_pull(skb, sizeof(*hdr));
+ if (hdr->pad_len)
+ skb_trim(skb, skb->len - hdr->pad_len);
+
+ event_data = (unsigned long)(skb);
+ spin_lock_irqsave(&bam_ch[hdr->ch_id].lock, flags);
+ bam_ch[hdr->ch_id].num_tx_pkts--;
+ spin_unlock_irqrestore(&bam_ch[hdr->ch_id].lock, flags);
+ if (bam_ch[hdr->ch_id].notify)
+ bam_ch[hdr->ch_id].notify(
+ bam_ch[hdr->ch_id].priv, BAM_DMUX_WRITE_DONE,
+ event_data);
+ else
+ dev_kfree_skb_any(skb);
+}
+
+int msm_bam_dmux_write(uint32_t id, struct sk_buff *skb)
+{
+ int rc = 0;
+ struct bam_mux_hdr *hdr;
+ unsigned long flags;
+ struct sk_buff *new_skb = NULL;
+ dma_addr_t dma_address;
+ struct tx_pkt_info *pkt;
+ int rcu_id;
+
+ if (id >= BAM_DMUX_NUM_CHANNELS)
+ return -EINVAL;
+ if (!skb)
+ return -EINVAL;
+ if (!bam_mux_initialized)
+ return -ENODEV;
+
+ rcu_id = srcu_read_lock(&bam_dmux_srcu);
+ if (in_global_reset) {
+ BAM_DMUX_LOG("%s: In SSR... ch_id[%d]\n", __func__, id);
+ srcu_read_unlock(&bam_dmux_srcu, rcu_id);
+ return -EFAULT;
+ }
+
+ DBG("%s: writing to ch %d len %d\n", __func__, id, skb->len);
+ spin_lock_irqsave(&bam_ch[id].lock, flags);
+ if (!bam_ch_is_open(id)) {
+ spin_unlock_irqrestore(&bam_ch[id].lock, flags);
+ pr_err("%s: port not open: %d\n", __func__, bam_ch[id].status);
+ srcu_read_unlock(&bam_dmux_srcu, rcu_id);
+ return -ENODEV;
+ }
+
+ if (bam_ch[id].use_wm &&
+ (bam_ch[id].num_tx_pkts >= HIGH_WATERMARK)) {
+ spin_unlock_irqrestore(&bam_ch[id].lock, flags);
+ pr_err("%s: watermark exceeded: %d\n", __func__, id);
+ srcu_read_unlock(&bam_dmux_srcu, rcu_id);
+ return -EAGAIN;
+ }
+ spin_unlock_irqrestore(&bam_ch[id].lock, flags);
+
+ read_lock(&ul_wakeup_lock);
+ if (!bam_is_connected) {
+ atomic_inc(&ul_ondemand_vote);
+ read_unlock(&ul_wakeup_lock);
+ ul_wakeup();
+ if (unlikely(in_global_reset == 1)) {
+ srcu_read_unlock(&bam_dmux_srcu, rcu_id);
+ atomic_dec(&ul_ondemand_vote);
+ return -EFAULT;
+ }
+ read_lock(&ul_wakeup_lock);
+ notify_all(BAM_DMUX_UL_CONNECTED, (unsigned long)(NULL));
+ atomic_dec(&ul_ondemand_vote);
+ }
+
+ /* if skb do not have any tailroom for padding,
+ * copy the skb into a new expanded skb
+ */
+ if ((skb->len & 0x3) && (skb_tailroom(skb) < (4 - (skb->len & 0x3)))) {
+ /* revisit, probably dev_alloc_skb and memcpy is effecient */
+ new_skb = skb_copy_expand(skb, skb_headroom(skb),
+ 4 - (skb->len & 0x3), GFP_ATOMIC);
+ if (new_skb == NULL) {
+ pr_err("%s: cannot allocate skb\n", __func__);
+ goto write_fail;
+ }
+ dev_kfree_skb_any(skb);
+ skb = new_skb;
+ DBG_INC_WRITE_CPY(skb->len);
+ }
+
+ hdr = (struct bam_mux_hdr *)skb_push(skb, sizeof(struct bam_mux_hdr));
+
+ /* caller should allocate for hdr and padding
+ * hdr is fine, padding is tricky
+ */
+ hdr->magic_num = BAM_MUX_HDR_MAGIC_NO;
+ hdr->cmd = BAM_MUX_HDR_CMD_DATA;
+ hdr->signal = 0;
+ hdr->ch_id = id;
+ hdr->pkt_len = skb->len - sizeof(struct bam_mux_hdr);
+ if (skb->len & 0x3)
+ skb_put(skb, 4 - (skb->len & 0x3));
+
+ hdr->pad_len = skb->len - (sizeof(struct bam_mux_hdr) + hdr->pkt_len);
+
+ DBG("%s: data %pK, tail %pK skb len %d pkt len %d pad len %d\n",
+ __func__, skb->data, skb_tail_pointer(skb), skb->len,
+ hdr->pkt_len, hdr->pad_len);
+
+ pkt = kmalloc(sizeof(struct tx_pkt_info), GFP_ATOMIC);
+ if (pkt == NULL)
+ goto write_fail2;
+
+ dma_address = dma_map_single(dma_dev, skb->data, skb->len,
+ bam_ops->dma_to);
+ if (!dma_address) {
+ pr_err("%s: dma_map_single() failed\n", __func__);
+ goto write_fail3;
+ }
+ pkt->skb = skb;
+ pkt->dma_address = dma_address;
+ pkt->is_cmd = 0;
+ set_tx_timestamp(pkt);
+ INIT_WORK(&pkt->work, bam_mux_write_done);
+ spin_lock_irqsave(&bam_tx_pool_spinlock, flags);
+ list_add_tail(&pkt->list_node, &bam_tx_pool);
+ rc = bam_ops->sps_transfer_one_ptr(bam_tx_pipe, dma_address, skb->len,
+ pkt, SPS_IOVEC_FLAG_EOT);
+ if (rc) {
+ DMUX_LOG_KERR("%s sps_transfer_one failed rc=%d\n",
+ __func__, rc);
+ list_del(&pkt->list_node);
+ DBG_INC_TX_SPS_FAILURE_CNT();
+ spin_unlock_irqrestore(&bam_tx_pool_spinlock, flags);
+ dma_unmap_single(dma_dev, pkt->dma_address,
+ pkt->skb->len, bam_ops->dma_to);
+ kfree(pkt);
+ if (new_skb)
+ dev_kfree_skb_any(new_skb);
+ } else {
+ spin_unlock_irqrestore(&bam_tx_pool_spinlock, flags);
+ spin_lock_irqsave(&bam_ch[id].lock, flags);
+ bam_ch[id].num_tx_pkts++;
+ spin_unlock_irqrestore(&bam_ch[id].lock, flags);
+ }
+ ul_packet_written = 1;
+ read_unlock(&ul_wakeup_lock);
+ srcu_read_unlock(&bam_dmux_srcu, rcu_id);
+ return rc;
+
+write_fail3:
+ kfree(pkt);
+write_fail2:
+ skb_pull(skb, sizeof(struct bam_mux_hdr));
+ if (new_skb)
+ dev_kfree_skb_any(new_skb);
+write_fail:
+ read_unlock(&ul_wakeup_lock);
+ srcu_read_unlock(&bam_dmux_srcu, rcu_id);
+ return -ENOMEM;
+}
+
+/**
+ * create_open_signal() - Generate a proper signal field for outgoing open cmds
+ *
+ * A properly constructed signal field of the mux header for opem commands semt
+ * to the remote side depend on what has been locally configured, and what has
+ * been received from the remote side. The byte value to code translations
+ * must match the valid values in set_rx_buffer_ring_pool() and set_dl_mtu().
+ *
+ * Return: A properly constructed signal field for an outgoing mux open command.
+ */
+static uint8_t create_open_signal(void)
+{
+ uint8_t signal = 0;
+ uint8_t buff_count = 0;
+ uint8_t dl_size = 0;
+
+ if (!dynamic_mtu_enabled)
+ return signal;
+
+ signal = DYNAMIC_MTU_MASK;
+
+ switch (num_buffers) {
+ case SZ_256:
+ buff_count = 3;
+ break;
+ case SZ_128:
+ buff_count = 2;
+ break;
+ case SZ_64:
+ buff_count = 1;
+ break;
+ case SZ_32:
+ buff_count = 0;
+ break;
+ }
+
+ signal |= buff_count << DL_POOL_SIZE_SHIFT;
+
+ switch (dl_mtu) {
+ case SZ_16K:
+ dl_size = 3;
+ break;
+ case SZ_8K:
+ dl_size = 2;
+ break;
+ case SZ_4K:
+ dl_size = 1;
+ break;
+ case SZ_2K:
+ dl_size = 0;
+ break;
+ }
+
+ signal |= dl_size << MTU_SIZE_SHIFT;
+
+ return signal;
+}
+
+int msm_bam_dmux_open(uint32_t id, void *priv,
+ void (*notify)(void *, int, unsigned long))
+{
+ struct bam_mux_hdr *hdr;
+ unsigned long flags;
+ int rc = 0;
+
+ DBG("%s: opening ch %d\n", __func__, id);
+ if (!bam_mux_initialized) {
+ DBG("%s: not inititialized\n", __func__);
+ return -ENODEV;
+ }
+ if (id >= BAM_DMUX_NUM_CHANNELS) {
+ pr_err("%s: invalid channel id %d\n", __func__, id);
+ return -EINVAL;
+ }
+ if (notify == NULL) {
+ pr_err("%s: notify function is NULL\n", __func__);
+ return -EINVAL;
+ }
+
+ hdr = kmalloc(sizeof(struct bam_mux_hdr), GFP_KERNEL);
+ if (hdr == NULL)
+ return -ENOMEM;
+
+ spin_lock_irqsave(&bam_ch[id].lock, flags);
+ if (bam_ch_is_open(id)) {
+ DBG("%s: Already opened %d\n", __func__, id);
+ spin_unlock_irqrestore(&bam_ch[id].lock, flags);
+ kfree(hdr);
+ goto open_done;
+ }
+ if (!bam_ch_is_remote_open(id)) {
+ DBG("%s: Remote not open; ch: %d\n", __func__, id);
+ spin_unlock_irqrestore(&bam_ch[id].lock, flags);
+ kfree(hdr);
+ return -ENODEV;
+ }
+
+ bam_ch[id].notify = notify;
+ bam_ch[id].priv = priv;
+ bam_ch[id].status |= BAM_CH_LOCAL_OPEN;
+ bam_ch[id].num_tx_pkts = 0;
+ bam_ch[id].use_wm = 0;
+ spin_unlock_irqrestore(&bam_ch[id].lock, flags);
+
+ notify(priv, BAM_DMUX_TRANSMIT_SIZE, ul_mtu);
+
+ read_lock(&ul_wakeup_lock);
+ if (!bam_is_connected) {
+ atomic_inc(&ul_ondemand_vote);
+ read_unlock(&ul_wakeup_lock);
+ ul_wakeup();
+ if (unlikely(in_global_reset == 1)) {
+ atomic_dec(&ul_ondemand_vote);
+ kfree(hdr);
+ return -EFAULT;
+ }
+ read_lock(&ul_wakeup_lock);
+ notify_all(BAM_DMUX_UL_CONNECTED, (unsigned long)(NULL));
+ atomic_dec(&ul_ondemand_vote);
+ }
+
+ hdr->magic_num = BAM_MUX_HDR_MAGIC_NO;
+ hdr->cmd = BAM_MUX_HDR_CMD_OPEN;
+ hdr->signal = create_open_signal();
+ hdr->ch_id = id;
+ hdr->pkt_len = 0;
+ hdr->pad_len = 0;
+
+ rc = bam_mux_write_cmd((void *)hdr, sizeof(struct bam_mux_hdr));
+ read_unlock(&ul_wakeup_lock);
+
+open_done:
+ DBG("%s: opened ch %d\n", __func__, id);
+ return rc;
+}
+
+int msm_bam_dmux_close(uint32_t id)
+{
+ struct bam_mux_hdr *hdr;
+ unsigned long flags;
+ int rc;
+
+ if (id >= BAM_DMUX_NUM_CHANNELS)
+ return -EINVAL;
+ DBG("%s: closing ch %d\n", __func__, id);
+ if (!bam_mux_initialized || !bam_ch_is_local_open(id))
+ return -ENODEV;
+
+ read_lock(&ul_wakeup_lock);
+ if (!bam_is_connected && !bam_ch_is_in_reset(id)) {
+ atomic_inc(&ul_ondemand_vote);
+ read_unlock(&ul_wakeup_lock);
+ ul_wakeup();
+ if (unlikely(in_global_reset == 1)) {
+ atomic_dec(&ul_ondemand_vote);
+ return -EFAULT;
+ }
+ read_lock(&ul_wakeup_lock);
+ notify_all(BAM_DMUX_UL_CONNECTED, (unsigned long)(NULL));
+ atomic_dec(&ul_ondemand_vote);
+ }
+
+ spin_lock_irqsave(&bam_ch[id].lock, flags);
+ bam_ch[id].notify = NULL;
+ bam_ch[id].priv = NULL;
+ bam_ch[id].status &= ~BAM_CH_LOCAL_OPEN;
+ spin_unlock_irqrestore(&bam_ch[id].lock, flags);
+
+ if (bam_ch_is_in_reset(id)) {
+ read_unlock(&ul_wakeup_lock);
+ bam_ch[id].status &= ~BAM_CH_IN_RESET;
+ return 0;
+ }
+
+ hdr = kmalloc(sizeof(struct bam_mux_hdr), GFP_ATOMIC);
+ if (hdr == NULL) {
+ read_unlock(&ul_wakeup_lock);
+ return -ENOMEM;
+ }
+ hdr->magic_num = BAM_MUX_HDR_MAGIC_NO;
+ hdr->cmd = BAM_MUX_HDR_CMD_CLOSE;
+ hdr->signal = 0;
+ hdr->ch_id = id;
+ hdr->pkt_len = 0;
+ hdr->pad_len = 0;
+
+ rc = bam_mux_write_cmd((void *)hdr, sizeof(struct bam_mux_hdr));
+ read_unlock(&ul_wakeup_lock);
+
+ DBG("%s: closed ch %d\n", __func__, id);
+ return rc;
+}
+
+int msm_bam_dmux_is_ch_full(uint32_t id)
+{
+ unsigned long flags;
+ int ret;
+
+ if (id >= BAM_DMUX_NUM_CHANNELS)
+ return -EINVAL;
+
+ spin_lock_irqsave(&bam_ch[id].lock, flags);
+ bam_ch[id].use_wm = 1;
+ ret = bam_ch[id].num_tx_pkts >= HIGH_WATERMARK;
+ DBG("%s: ch %d num tx pkts=%d, HWM=%d\n", __func__,
+ id, bam_ch[id].num_tx_pkts, ret);
+ if (!bam_ch_is_local_open(id)) {
+ ret = -ENODEV;
+ pr_err("%s: port not open: %d\n", __func__, bam_ch[id].status);
+ }
+ spin_unlock_irqrestore(&bam_ch[id].lock, flags);
+
+ return ret;
+}
+
+int msm_bam_dmux_is_ch_low(uint32_t id)
+{
+ unsigned long flags;
+ int ret;
+
+ if (id >= BAM_DMUX_NUM_CHANNELS)
+ return -EINVAL;
+
+ spin_lock_irqsave(&bam_ch[id].lock, flags);
+ bam_ch[id].use_wm = 1;
+ ret = bam_ch[id].num_tx_pkts <= LOW_WATERMARK;
+ DBG("%s: ch %d num tx pkts=%d, LWM=%d\n", __func__,
+ id, bam_ch[id].num_tx_pkts, ret);
+ if (!bam_ch_is_local_open(id)) {
+ ret = -ENODEV;
+ pr_err("%s: port not open: %d\n", __func__, bam_ch[id].status);
+ }
+ spin_unlock_irqrestore(&bam_ch[id].lock, flags);
+
+ return ret;
+}
+
+static void rx_switch_to_interrupt_mode(void)
+{
+ struct sps_connect cur_rx_conn;
+ struct sps_iovec iov;
+ struct rx_pkt_info *info;
+ int ret;
+
+ /*
+ * Attempt to enable interrupts - if this fails,
+ * continue polling and we will retry later.
+ */
+ ret = bam_ops->sps_get_config_ptr(bam_rx_pipe, &cur_rx_conn);
+ if (ret) {
+ pr_err("%s: sps_get_config() failed %d\n", __func__, ret);
+ goto fail;
+ }
+
+ rx_register_event.options = SPS_O_EOT;
+ ret = bam_ops->sps_register_event_ptr(bam_rx_pipe, &rx_register_event);
+ if (ret) {
+ pr_err("%s: sps_register_event() failed %d\n", __func__, ret);
+ goto fail;
+ }
+
+ cur_rx_conn.options = SPS_O_AUTO_ENABLE |
+ SPS_O_EOT | SPS_O_ACK_TRANSFERS;
+ ret = bam_ops->sps_set_config_ptr(bam_rx_pipe, &cur_rx_conn);
+ if (ret) {
+ pr_err("%s: sps_set_config() failed %d\n", __func__, ret);
+ goto fail;
+ }
+ polling_mode = 0;
+ complete_all(&shutdown_completion);
+ release_wakelock();
+
+ /* handle any rx packets before interrupt was enabled */
+ while (bam_connection_is_active && !polling_mode) {
+ ret = bam_ops->sps_get_iovec_ptr(bam_rx_pipe, &iov);
+ if (ret) {
+ pr_err("%s: sps_get_iovec failed %d\n",
+ __func__, ret);
+ break;
+ }
+ if (iov.addr == 0)
+ break;
+
+ mutex_lock(&bam_rx_pool_mutexlock);
+ if (unlikely(list_empty(&bam_rx_pool))) {
+ DMUX_LOG_KERR("%s: have iovec %pK but rx pool empty\n",
+ __func__, (void *)(uintptr_t)iov.addr);
+ mutex_unlock(&bam_rx_pool_mutexlock);
+ continue;
+ }
+ info = list_first_entry(&bam_rx_pool, struct rx_pkt_info,
+ list_node);
+ if (info->dma_address != iov.addr) {
+ DMUX_LOG_KERR("%s: iovec %pK != dma %pK\n",
+ __func__,
+ (void *)(uintptr_t)iov.addr,
+ (void *)(uintptr_t)info->dma_address);
+ list_for_each_entry(info, &bam_rx_pool, list_node) {
+ DMUX_LOG_KERR("%s: dma %pK\n", __func__,
+ (void *)(uintptr_t)info->dma_address);
+ if (iov.addr == info->dma_address)
+ break;
+ }
+ }
+ WARN_ON(info->dma_address != iov.addr);
+ list_del(&info->list_node);
+ --bam_rx_pool_len;
+ mutex_unlock(&bam_rx_pool_mutexlock);
+ info->sps_size = iov.size;
+ handle_bam_mux_cmd(&info->work);
+ }
+ return;
+
+fail:
+ pr_err("%s: reverting to polling\n", __func__);
+ if (no_cpu_affinity)
+ queue_work(bam_mux_rx_workqueue, &rx_timer_work);
+ else
+ queue_work_on(0, bam_mux_rx_workqueue, &rx_timer_work);
+}
+
+/**
+ * store_rx_timestamp() - store the current raw time as as a timestamp for when
+ * the last rx packet was processed
+ */
+static void store_rx_timestamp(void)
+{
+ last_rx_pkt_timestamp = sched_clock();
+}
+
+/**
+ * log_rx_timestamp() - Log the stored rx pkt timestamp in a human readable
+ * format
+ */
+static void log_rx_timestamp(void)
+{
+ unsigned long long t = last_rx_pkt_timestamp;
+ unsigned long nanosec_rem;
+
+ nanosec_rem = do_div(t, 1000000000U);
+ BAM_DMUX_LOG("Last rx pkt processed at [%6u.%09lu]\n", (unsigned int)t,
+ nanosec_rem);
+}
+
+static void rx_timer_work_func(struct work_struct *work)
+{
+ struct sps_iovec iov;
+ struct rx_pkt_info *info;
+ int inactive_cycles = 0;
+ int ret;
+ u32 buffs_unused, buffs_used;
+
+ BAM_DMUX_LOG("%s: polling start\n", __func__);
+ while (bam_connection_is_active) { /* timer loop */
+ ++inactive_cycles;
+ while (bam_connection_is_active) { /* deplete queue loop */
+ if (in_global_reset) {
+ BAM_DMUX_LOG(
+ "%s: polling exit, global reset detected\n",
+ __func__);
+ return;
+ }
+
+ ret = bam_ops->sps_get_iovec_ptr(bam_rx_pipe, &iov);
+ if (ret) {
+ DMUX_LOG_KERR("%s: sps_get_iovec failed %d\n",
+ __func__, ret);
+ break;
+ }
+ if (iov.addr == 0)
+ break;
+ store_rx_timestamp();
+ inactive_cycles = 0;
+ mutex_lock(&bam_rx_pool_mutexlock);
+ if (unlikely(list_empty(&bam_rx_pool))) {
+ DMUX_LOG_KERR(
+ "%s:have iovec %pK but rx pool empty\n",
+ __func__, (void *)(uintptr_t)iov.addr);
+ mutex_unlock(&bam_rx_pool_mutexlock);
+ continue;
+ }
+ info = list_first_entry(&bam_rx_pool,
+ struct rx_pkt_info, list_node);
+ if (info->dma_address != iov.addr) {
+ DMUX_LOG_KERR("%s: iovec %pK != dma %pK\n",
+ __func__,
+ (void *)(uintptr_t)iov.addr,
+ (void *)(uintptr_t)info->dma_address);
+ list_for_each_entry(info, &bam_rx_pool,
+ list_node) {
+ DMUX_LOG_KERR("%s: dma %pK\n", __func__,
+ (void *)(uintptr_t)
+ info->dma_address);
+ if (iov.addr == info->dma_address)
+ break;
+ }
+ }
+ WARN_ON(info->dma_address != iov.addr);
+ list_del(&info->list_node);
+ --bam_rx_pool_len;
+ mutex_unlock(&bam_rx_pool_mutexlock);
+ info->sps_size = iov.size;
+ handle_bam_mux_cmd(&info->work);
+ }
+
+ if (inactive_cycles >= POLLING_INACTIVITY) {
+ BAM_DMUX_LOG("%s: polling exit, no data\n", __func__);
+ rx_switch_to_interrupt_mode();
+ break;
+ }
+
+ if (bam_adaptive_timer_enabled) {
+ usleep_range(rx_timer_interval, rx_timer_interval + 50);
+
+ ret = bam_ops->sps_get_unused_desc_num_ptr(bam_rx_pipe,
+ &buffs_unused);
+
+ if (ret) {
+ DMUX_LOG_KERR(
+ "%s: error getting num buffers unused after sleep\n",
+ __func__);
+
+ break;
+ }
+
+ buffs_used = num_buffers - buffs_unused;
+
+ if (buffs_unused == 0) {
+ rx_timer_interval = MIN_POLLING_SLEEP;
+ } else {
+ if (buffs_used > 0) {
+ rx_timer_interval =
+ (2 * num_buffers *
+ rx_timer_interval)/
+ (3 * buffs_used);
+ } else {
+ rx_timer_interval =
+ MAX_POLLING_SLEEP;
+ }
+ }
+
+ if (rx_timer_interval > MAX_POLLING_SLEEP)
+ rx_timer_interval = MAX_POLLING_SLEEP;
+ else if (rx_timer_interval < MIN_POLLING_SLEEP)
+ rx_timer_interval = MIN_POLLING_SLEEP;
+ } else {
+ usleep_range(POLLING_MIN_SLEEP, POLLING_MAX_SLEEP);
+ }
+ }
+}
+
+static void bam_mux_tx_notify(struct sps_event_notify *notify)
+{
+ struct tx_pkt_info *pkt;
+
+ DBG("%s: event %d notified\n", __func__, notify->event_id);
+
+ if (in_global_reset)
+ return;
+
+ switch (notify->event_id) {
+ case SPS_EVENT_EOT:
+ pkt = notify->data.transfer.user;
+ if (!pkt->is_cmd)
+ dma_unmap_single(dma_dev, pkt->dma_address,
+ pkt->skb->len,
+ bam_ops->dma_to);
+ else
+ dma_unmap_single(dma_dev, pkt->dma_address,
+ pkt->len,
+ bam_ops->dma_to);
+ queue_work(bam_mux_tx_workqueue, &pkt->work);
+ break;
+ default:
+ pr_err("%s: received unexpected event id %d\n", __func__,
+ notify->event_id);
+ }
+}
+
+static void bam_mux_rx_notify(struct sps_event_notify *notify)
+{
+ int ret;
+ struct sps_connect cur_rx_conn;
+
+ DBG("%s: event %d notified\n", __func__, notify->event_id);
+
+ if (in_global_reset)
+ return;
+
+ switch (notify->event_id) {
+ case SPS_EVENT_EOT:
+ /* attempt to disable interrupts in this pipe */
+ if (!polling_mode) {
+ ret = bam_ops->sps_get_config_ptr(bam_rx_pipe,
+ &cur_rx_conn);
+ if (ret) {
+ pr_err("%s: sps_get_config() failed %d, interrupts not disabled\n",
+ __func__, ret);
+ break;
+ }
+ cur_rx_conn.options = SPS_O_AUTO_ENABLE |
+ SPS_O_ACK_TRANSFERS | SPS_O_POLL;
+ ret = bam_ops->sps_set_config_ptr(bam_rx_pipe,
+ &cur_rx_conn);
+ if (ret) {
+ pr_err("%s: sps_set_config() failed %d, interrupts not disabled\n",
+ __func__, ret);
+ break;
+ }
+ reinit_completion(&shutdown_completion);
+ grab_wakelock();
+ polling_mode = 1;
+ /*
+ * run on core 0 so that netif_rx() in rmnet uses only
+ * one queue if RPS enable use no_cpu_affinity
+ */
+ if (no_cpu_affinity)
+ queue_work(bam_mux_rx_workqueue,
+ &rx_timer_work);
+ else
+ queue_work_on(0, bam_mux_rx_workqueue,
+ &rx_timer_work);
+ }
+ break;
+ default:
+ pr_err("%s: received unexpected event id %d\n", __func__,
+ notify->event_id);
+ }
+}
+
+#ifdef CONFIG_DEBUG_FS
+
+static int debug_tbl(char *buf, int max)
+{
+ int i = 0;
+ int j;
+
+ for (j = 0; j < BAM_DMUX_NUM_CHANNELS; ++j) {
+ i += scnprintf(buf + i, max - i,
+ "ch%02d local open=%s remote open=%s\n",
+ j, bam_ch_is_local_open(j) ? "Y" : "N",
+ bam_ch_is_remote_open(j) ? "Y" : "N");
+ }
+
+ return i;
+}
+
+static int debug_ul_pkt_cnt(char *buf, int max)
+{
+ struct list_head *p;
+ unsigned long flags;
+ int n = 0;
+
+ spin_lock_irqsave(&bam_tx_pool_spinlock, flags);
+ list_for_each(p, &bam_tx_pool) {
+ ++n;
+ }
+ spin_unlock_irqrestore(&bam_tx_pool_spinlock, flags);
+
+ return scnprintf(buf, max, "Number of UL packets in flight: %d\n", n);
+}
+
+static int debug_stats(char *buf, int max)
+{
+ int i = 0;
+
+ i += scnprintf(buf + i, max - i,
+ "skb read cnt: %u\n"
+ "skb write cnt: %u\n"
+ "skb copy cnt: %u\n"
+ "skb copy bytes: %u\n"
+ "sps tx failures: %u\n"
+ "sps tx stalls: %u\n"
+ "rx queue len: %d\n"
+ "a2 ack out cnt: %d\n"
+ "a2 ack in cnt: %d\n"
+ "a2 pwr cntl in: %d\n",
+ bam_dmux_read_cnt,
+ bam_dmux_write_cnt,
+ bam_dmux_write_cpy_cnt,
+ bam_dmux_write_cpy_bytes,
+ bam_dmux_tx_sps_failure_cnt,
+ bam_dmux_tx_stall_cnt,
+ bam_rx_pool_len,
+ atomic_read(&bam_dmux_ack_out_cnt),
+ atomic_read(&bam_dmux_ack_in_cnt),
+ atomic_read(&bam_dmux_a2_pwr_cntl_in_cnt)
+ );
+
+ return i;
+}
+
+#define DEBUG_BUFMAX 4096
+static char debug_buffer[DEBUG_BUFMAX];
+
+static ssize_t debug_read(struct file *file, char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ int (*fill)(char *buf, int max) = file->private_data;
+ int bsize = fill(debug_buffer, DEBUG_BUFMAX);
+
+ return simple_read_from_buffer(buf, count, ppos, debug_buffer, bsize);
+}
+
+static int debug_open(struct inode *inode, struct file *file)
+{
+ file->private_data = inode->i_private;
+ return 0;
+}
+
+
+static const struct file_operations debug_ops = {
+ .read = debug_read,
+ .open = debug_open,
+};
+
+static void debug_create(const char *name, mode_t mode,
+ struct dentry *dent,
+ int (*fill)(char *buf, int max))
+{
+ struct dentry *file;
+
+ file = debugfs_create_file(name, mode, dent, fill, &debug_ops);
+ if (IS_ERR(file))
+ pr_err("%s: debugfs create failed %d\n", __func__,
+ (int)PTR_ERR(file));
+}
+
+#endif
+
+static void notify_all(int event, unsigned long data)
+{
+ int i;
+ unsigned long flags;
+ struct list_head *temp;
+ struct outside_notify_func *func;
+ void (*notify)(void *, int, unsigned long);
+ void *priv;
+
+ BAM_DMUX_LOG("%s: event=%d, data=%lu\n", __func__, event, data);
+
+ for (i = 0; i < BAM_DMUX_NUM_CHANNELS; ++i) {
+ notify = NULL;
+ priv = NULL;
+ spin_lock_irqsave(&bam_ch[i].lock, flags);
+ if (bam_ch_is_open(i)) {
+ notify = bam_ch[i].notify;
+ priv = bam_ch[i].priv;
+ }
+ spin_unlock_irqrestore(&bam_ch[i].lock, flags);
+ if (notify)
+ notify(priv, event, data);
+ }
+
+ list_for_each(temp, &bam_other_notify_funcs) {
+ func = container_of(temp, struct outside_notify_func,
+ list_node);
+ func->notify(func->priv, event, data);
+ }
+}
+
+static void kickoff_ul_wakeup_func(struct work_struct *work)
+{
+ read_lock(&ul_wakeup_lock);
+ if (!bam_is_connected) {
+ atomic_inc(&ul_ondemand_vote);
+ read_unlock(&ul_wakeup_lock);
+ ul_wakeup();
+ if (unlikely(in_global_reset == 1)) {
+ atomic_dec(&ul_ondemand_vote);
+ return;
+ }
+ read_lock(&ul_wakeup_lock);
+ ul_packet_written = 1;
+ notify_all(BAM_DMUX_UL_CONNECTED, (unsigned long)(NULL));
+ atomic_dec(&ul_ondemand_vote);
+ }
+ read_unlock(&ul_wakeup_lock);
+}
+
+int msm_bam_dmux_kickoff_ul_wakeup(void)
+{
+ int is_connected;
+
+ read_lock(&ul_wakeup_lock);
+ ul_packet_written = 1;
+ is_connected = bam_is_connected;
+ if (!is_connected)
+ queue_work(bam_mux_tx_workqueue, &kickoff_ul_wakeup);
+ read_unlock(&ul_wakeup_lock);
+
+ return is_connected;
+}
+
+static void power_vote(int vote)
+{
+ BAM_DMUX_LOG("%s: curr=%d, vote=%d\n", __func__,
+ bam_dmux_uplink_vote, vote);
+
+ if (bam_dmux_uplink_vote == vote)
+ BAM_DMUX_LOG("%s: warning - duplicate power vote\n", __func__);
+
+ bam_dmux_uplink_vote = vote;
+ if (vote)
+ bam_ops->smsm_change_state_ptr(SMSM_APPS_STATE,
+ 0, SMSM_A2_POWER_CONTROL);
+ else
+ bam_ops->smsm_change_state_ptr(SMSM_APPS_STATE,
+ SMSM_A2_POWER_CONTROL, 0);
+}
+
+/*
+ * @note: Must be called with ul_wakeup_lock locked.
+ */
+static inline void ul_powerdown(void)
+{
+ BAM_DMUX_LOG("%s: powerdown\n", __func__);
+ verify_tx_queue_is_empty(__func__);
+
+ if (a2_pc_disabled) {
+ wait_for_dfab = 1;
+ reinit_completion(&dfab_unvote_completion);
+ release_wakelock();
+ } else {
+ wait_for_ack = 1;
+ reinit_completion(&ul_wakeup_ack_completion);
+ power_vote(0);
+ }
+ bam_is_connected = 0;
+ notify_all(BAM_DMUX_UL_DISCONNECTED, (unsigned long)(NULL));
+}
+
+static inline void ul_powerdown_finish(void)
+{
+ if (a2_pc_disabled && wait_for_dfab) {
+ unvote_dfab();
+ complete_all(&dfab_unvote_completion);
+ wait_for_dfab = 0;
+ }
+}
+
+/*
+ * Votes for UL power and returns current power state.
+ *
+ * @returns true if currently connected
+ */
+int msm_bam_dmux_ul_power_vote(void)
+{
+ int is_connected;
+
+ read_lock(&ul_wakeup_lock);
+ atomic_inc(&ul_ondemand_vote);
+ is_connected = bam_is_connected;
+ if (!is_connected)
+ queue_work(bam_mux_tx_workqueue, &kickoff_ul_wakeup);
+ read_unlock(&ul_wakeup_lock);
+
+ return is_connected;
+}
+
+/*
+ * Unvotes for UL power.
+ *
+ * @returns true if vote count is 0 (UL shutdown possible)
+ */
+int msm_bam_dmux_ul_power_unvote(void)
+{
+ int vote;
+
+ read_lock(&ul_wakeup_lock);
+ vote = atomic_dec_return(&ul_ondemand_vote);
+ if (unlikely(vote < 0))
+ DMUX_LOG_KERR("%s: invalid power vote %d\n", __func__, vote);
+ read_unlock(&ul_wakeup_lock);
+
+ return vote == 0;
+}
+
+int msm_bam_dmux_reg_notify(void *priv,
+ void (*notify)(void *priv, int event_type,
+ unsigned long data))
+{
+ struct outside_notify_func *func;
+
+ if (!notify)
+ return -EINVAL;
+
+ func = kmalloc(sizeof(struct outside_notify_func), GFP_KERNEL);
+ if (!func)
+ return -ENOMEM;
+
+ func->notify = notify;
+ func->priv = priv;
+ list_add(&func->list_node, &bam_other_notify_funcs);
+
+ return 0;
+}
+
+static void ul_timeout(struct work_struct *work)
+{
+ unsigned long flags;
+ int ret;
+
+ if (in_global_reset)
+ return;
+ ret = write_trylock_irqsave(&ul_wakeup_lock, flags);
+ if (!ret) { /* failed to grab lock, reschedule and bail */
+ schedule_delayed_work(&ul_timeout_work,
+ msecs_to_jiffies(ul_timeout_delay));
+ return;
+ }
+ if (bam_is_connected) {
+ if (!ul_packet_written) {
+ spin_lock(&bam_tx_pool_spinlock);
+ if (!list_empty(&bam_tx_pool)) {
+ struct tx_pkt_info *info;
+
+ info = list_first_entry(&bam_tx_pool,
+ struct tx_pkt_info, list_node);
+ DMUX_LOG_KERR("%s: UL delayed ts=%u.%09lu\n",
+ __func__, info->ts_sec, info->ts_nsec);
+ DBG_INC_TX_STALL_CNT();
+ ul_packet_written = 1;
+ }
+ spin_unlock(&bam_tx_pool_spinlock);
+ }
+
+ if (ul_packet_written || atomic_read(&ul_ondemand_vote)) {
+ BAM_DMUX_LOG("%s: pkt written %d\n",
+ __func__, ul_packet_written);
+ ul_packet_written = 0;
+ schedule_delayed_work(&ul_timeout_work,
+ msecs_to_jiffies(ul_timeout_delay));
+ } else {
+ ul_powerdown();
+ }
+ }
+ write_unlock_irqrestore(&ul_wakeup_lock, flags);
+ ul_powerdown_finish();
+}
+
+static int ssrestart_check(void)
+{
+ int ret = 0;
+
+ if (in_global_reset) {
+ DMUX_LOG_KERR("%s: already in SSR\n",
+ __func__);
+ return 1;
+ }
+
+ DMUX_LOG_KERR(
+ "%s: fatal modem interaction: BAM DMUX disabled for SSR\n",
+ __func__);
+ in_global_reset = 1;
+ ret = subsystem_restart("modem");
+ if (ret == -ENODEV)
+ panic("modem subsystem restart failed\n");
+ return 1;
+}
+
+static void ul_wakeup(void)
+{
+ int ret;
+ int do_vote_dfab = 0;
+
+ mutex_lock(&wakeup_lock);
+ if (bam_is_connected) { /* bam got connected before lock grabbed */
+ BAM_DMUX_LOG("%s Already awake\n", __func__);
+ mutex_unlock(&wakeup_lock);
+ return;
+ }
+
+ /*
+ * if this gets hit, that means restart_notifier_cb() has started
+ * but probably not finished, thus we know SSR has happened, but
+ * haven't been able to send that info to our clients yet.
+ * in that case, abort the ul_wakeup() so that we don't undo any
+ * work restart_notifier_cb() has done. The clients will be notified
+ * shortly. No cleanup necessary (reschedule the wakeup) as our and
+ * their SSR handling will cover it
+ */
+ if (unlikely(in_global_reset == 1)) {
+ mutex_unlock(&wakeup_lock);
+ return;
+ }
+
+ /*
+ * if someone is voting for UL before bam is inited (modem up first
+ * time), set flag for init to kickoff ul wakeup once bam is inited
+ */
+ mutex_lock(&delayed_ul_vote_lock);
+ if (unlikely(!bam_mux_initialized)) {
+ need_delayed_ul_vote = 1;
+ mutex_unlock(&delayed_ul_vote_lock);
+ mutex_unlock(&wakeup_lock);
+ return;
+ }
+ mutex_unlock(&delayed_ul_vote_lock);
+
+ if (a2_pc_disabled) {
+ /*
+ * don't grab the wakelock the first time because it is
+ * already grabbed when a2 powers on
+ */
+ if (likely(a2_pc_disabled_wakelock_skipped)) {
+ grab_wakelock();
+ do_vote_dfab = 1; /* vote must occur after wait */
+ } else {
+ a2_pc_disabled_wakelock_skipped = 1;
+ }
+ if (wait_for_dfab) {
+ ret = wait_for_completion_timeout(
+ &dfab_unvote_completion, HZ);
+ WARN_ON(ret == 0);
+ }
+ if (likely(do_vote_dfab))
+ vote_dfab();
+ schedule_delayed_work(&ul_timeout_work,
+ msecs_to_jiffies(ul_timeout_delay));
+ bam_is_connected = 1;
+ mutex_unlock(&wakeup_lock);
+ return;
+ }
+
+ /*
+ * must wait for the previous power down request to have been acked
+ * chances are it already came in and this will just fall through
+ * instead of waiting
+ */
+ if (wait_for_ack) {
+ BAM_DMUX_LOG("%s waiting for previous ack\n", __func__);
+ ret = wait_for_completion_timeout(
+ &ul_wakeup_ack_completion,
+ msecs_to_jiffies(UL_WAKEUP_TIMEOUT_MS));
+ wait_for_ack = 0;
+ if (unlikely(in_global_reset == 1)
+ || (unlikely(ret == 0) && ssrestart_check())) {
+ mutex_unlock(&wakeup_lock);
+ BAM_DMUX_LOG("%s timeout previous ack\n", __func__);
+ return;
+ }
+ }
+ reinit_completion(&ul_wakeup_ack_completion);
+ power_vote(1);
+ BAM_DMUX_LOG("%s waiting for wakeup ack\n", __func__);
+ ret = wait_for_completion_timeout(&ul_wakeup_ack_completion,
+ msecs_to_jiffies(UL_WAKEUP_TIMEOUT_MS));
+ if (unlikely(in_global_reset == 1)
+ || (unlikely(ret == 0) && ssrestart_check())) {
+ mutex_unlock(&wakeup_lock);
+ BAM_DMUX_LOG("%s timeout wakeup ack\n", __func__);
+ return;
+ }
+ BAM_DMUX_LOG("%s waiting completion\n", __func__);
+ ret = wait_for_completion_timeout(&bam_connection_completion,
+ msecs_to_jiffies(UL_WAKEUP_TIMEOUT_MS));
+ if (unlikely(in_global_reset == 1)
+ || (unlikely(ret == 0) && ssrestart_check())) {
+ mutex_unlock(&wakeup_lock);
+ BAM_DMUX_LOG("%s timeout power on\n", __func__);
+ return;
+ }
+
+ bam_is_connected = 1;
+ BAM_DMUX_LOG("%s complete\n", __func__);
+ schedule_delayed_work(&ul_timeout_work,
+ msecs_to_jiffies(ul_timeout_delay));
+ mutex_unlock(&wakeup_lock);
+}
+
+static void reconnect_to_bam(void)
+{
+ int i;
+
+ if (in_global_reset) {
+ BAM_DMUX_LOG("%s: skipping due to SSR\n", __func__);
+ return;
+ }
+
+ vote_dfab();
+
+ if (ssr_skipped_disconnect) {
+ /* delayed to here to prevent bus stall */
+ bam_ops->sps_disconnect_ptr(bam_tx_pipe);
+ bam_ops->sps_disconnect_ptr(bam_rx_pipe);
+ memset(rx_desc_mem_buf.base, 0, rx_desc_mem_buf.size);
+ memset(tx_desc_mem_buf.base, 0, tx_desc_mem_buf.size);
+ }
+ ssr_skipped_disconnect = 0;
+ i = bam_ops->sps_device_reset_ptr(a2_device_handle);
+ if (i)
+ pr_err("%s: device reset failed rc = %d\n", __func__,
+ i);
+ i = bam_ops->sps_connect_ptr(bam_tx_pipe, &tx_connection);
+ if (i)
+ pr_err("%s: tx connection failed rc = %d\n", __func__,
+ i);
+ i = bam_ops->sps_connect_ptr(bam_rx_pipe, &rx_connection);
+ if (i)
+ pr_err("%s: rx connection failed rc = %d\n", __func__,
+ i);
+ i = bam_ops->sps_register_event_ptr(bam_tx_pipe,
+ &tx_register_event);
+ if (i)
+ pr_err("%s: tx event reg failed rc = %d\n", __func__,
+ i);
+ i = bam_ops->sps_register_event_ptr(bam_rx_pipe,
+ &rx_register_event);
+ if (i)
+ pr_err("%s: rx event reg failed rc = %d\n", __func__,
+ i);
+ bam_connection_is_active = 1;
+
+ if (polling_mode)
+ rx_switch_to_interrupt_mode();
+
+ toggle_apps_ack();
+ complete_all(&bam_connection_completion);
+ queue_rx();
+}
+
+static void disconnect_to_bam(void)
+{
+ struct list_head *node;
+ struct rx_pkt_info *info;
+ unsigned long flags;
+ unsigned long time_remaining;
+
+ if (!in_global_reset) {
+ time_remaining = wait_for_completion_timeout(
+ &shutdown_completion,
+ msecs_to_jiffies(SHUTDOWN_TIMEOUT_MS));
+ if (time_remaining == 0) {
+ DMUX_LOG_KERR("%s: shutdown completion timed out\n",
+ __func__);
+ log_rx_timestamp();
+ ssrestart_check();
+ }
+ }
+
+ bam_connection_is_active = 0;
+
+ /* handle disconnect during active UL */
+ write_lock_irqsave(&ul_wakeup_lock, flags);
+ if (bam_is_connected) {
+ BAM_DMUX_LOG("%s: UL active - forcing powerdown\n", __func__);
+ ul_powerdown();
+ }
+ write_unlock_irqrestore(&ul_wakeup_lock, flags);
+ ul_powerdown_finish();
+
+ /* tear down BAM connection */
+ reinit_completion(&bam_connection_completion);
+
+ /* documentation/assumptions found in restart_notifier_cb */
+ if (likely(!in_global_reset)) {
+ BAM_DMUX_LOG("%s: disconnect tx\n", __func__);
+ bam_ops->sps_disconnect_ptr(bam_tx_pipe);
+ BAM_DMUX_LOG("%s: disconnect rx\n", __func__);
+ bam_ops->sps_disconnect_ptr(bam_rx_pipe);
+ memset(rx_desc_mem_buf.base, 0, rx_desc_mem_buf.size);
+ memset(tx_desc_mem_buf.base, 0, tx_desc_mem_buf.size);
+ BAM_DMUX_LOG("%s: device reset\n", __func__);
+ bam_ops->sps_device_reset_ptr(a2_device_handle);
+ } else {
+ ssr_skipped_disconnect = 1;
+ }
+ unvote_dfab();
+
+ mutex_lock(&bam_rx_pool_mutexlock);
+ while (!list_empty(&bam_rx_pool)) {
+ node = bam_rx_pool.next;
+ list_del(node);
+ info = container_of(node, struct rx_pkt_info, list_node);
+ dma_unmap_single(dma_dev, info->dma_address, info->len,
+ bam_ops->dma_from);
+ dev_kfree_skb_any(info->skb);
+ kfree(info);
+ }
+ bam_rx_pool_len = 0;
+ mutex_unlock(&bam_rx_pool_mutexlock);
+ toggle_apps_ack();
+ verify_tx_queue_is_empty(__func__);
+}
+
+static void vote_dfab(void)
+{
+ int rc;
+
+ BAM_DMUX_LOG("%s\n", __func__);
+ mutex_lock(&dfab_status_lock);
+ if (dfab_is_on) {
+ BAM_DMUX_LOG("%s: dfab is already on\n", __func__);
+ mutex_unlock(&dfab_status_lock);
+ return;
+ }
+ if (dfab_clk) {
+ rc = clk_prepare_enable(dfab_clk);
+ if (rc)
+ DMUX_LOG_KERR("bam_dmux vote for dfab failed rc = %d\n",
+ rc);
+ }
+ if (xo_clk) {
+ rc = clk_prepare_enable(xo_clk);
+ if (rc)
+ DMUX_LOG_KERR("bam_dmux vote for xo failed rc = %d\n",
+ rc);
+ }
+ dfab_is_on = 1;
+ mutex_unlock(&dfab_status_lock);
+}
+
+static void unvote_dfab(void)
+{
+ BAM_DMUX_LOG("%s\n", __func__);
+ mutex_lock(&dfab_status_lock);
+ if (!dfab_is_on) {
+ DMUX_LOG_KERR("%s: dfab is already off\n", __func__);
+ dump_stack();
+ mutex_unlock(&dfab_status_lock);
+ return;
+ }
+ if (dfab_clk)
+ clk_disable_unprepare(dfab_clk);
+ if (xo_clk)
+ clk_disable_unprepare(xo_clk);
+ dfab_is_on = 0;
+ mutex_unlock(&dfab_status_lock);
+}
+
+/* reference counting wrapper around wakelock */
+static void grab_wakelock(void)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&wakelock_reference_lock, flags);
+ BAM_DMUX_LOG("%s: ref count = %d\n", __func__,
+ wakelock_reference_count);
+ if (wakelock_reference_count == 0)
+ __pm_stay_awake(&bam_wakelock);
+ ++wakelock_reference_count;
+ spin_unlock_irqrestore(&wakelock_reference_lock, flags);
+}
+
+static void release_wakelock(void)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&wakelock_reference_lock, flags);
+ if (wakelock_reference_count == 0) {
+ DMUX_LOG_KERR("%s: bam_dmux wakelock not locked\n", __func__);
+ dump_stack();
+ spin_unlock_irqrestore(&wakelock_reference_lock, flags);
+ return;
+ }
+ BAM_DMUX_LOG("%s: ref count = %d\n", __func__,
+ wakelock_reference_count);
+ --wakelock_reference_count;
+ if (wakelock_reference_count == 0)
+ __pm_relax(&bam_wakelock);
+ spin_unlock_irqrestore(&wakelock_reference_lock, flags);
+}
+
+static int restart_notifier_cb(struct notifier_block *this,
+ unsigned long code,
+ void *data)
+{
+ int i;
+ struct list_head *node;
+ struct tx_pkt_info *info;
+ int temp_remote_status;
+ unsigned long flags;
+
+ /*
+ * Bam_dmux counts on the fact that the BEFORE_SHUTDOWN level of
+ * notifications are guaranteed to execute before the AFTER_SHUTDOWN
+ * level of notifications, and that BEFORE_SHUTDOWN always occurs in
+ * all SSR events, no matter what triggered the SSR. Also, bam_dmux
+ * assumes that SMD does its SSR processing in the AFTER_SHUTDOWN level
+ * thus bam_dmux is guaranteed to detect SSR before SMD, since the
+ * callbacks for all the drivers within the AFTER_SHUTDOWN level could
+ * occur in any order. Bam_dmux uses this knowledge to skip accessing
+ * the bam hardware when disconnect_to_bam() is triggered by SMD's SSR
+ * processing. We do not wat to access the bam hardware during SSR
+ * because a watchdog crash from a bus stall would likely occur.
+ */
+ if (code == SUBSYS_BEFORE_SHUTDOWN) {
+ BAM_DMUX_LOG("%s: begin\n", __func__);
+ in_global_reset = 1;
+ /* wakeup ul_wakeup() thread*/
+ complete_all(&ul_wakeup_ack_completion);
+ complete_all(&bam_connection_completion);
+ /* sync to ensure the driver sees SSR */
+ synchronize_srcu(&bam_dmux_srcu);
+ BAM_DMUX_LOG("%s: ssr signaling complete\n", __func__);
+ flush_workqueue(bam_mux_rx_workqueue);
+ }
+ if (code == SUBSYS_BEFORE_POWERUP)
+ in_global_reset = 0;
+ if (code != SUBSYS_AFTER_SHUTDOWN)
+ return NOTIFY_DONE;
+
+ /* Handle uplink Powerdown */
+ write_lock_irqsave(&ul_wakeup_lock, flags);
+ if (bam_is_connected) {
+ ul_powerdown();
+ wait_for_ack = 0;
+ }
+ /*
+ * if modem crash during ul_wakeup(), power_vote is 1, needs to be
+ * reset to 0. harmless if bam_is_connected check above passes
+ */
+ power_vote(0);
+ write_unlock_irqrestore(&ul_wakeup_lock, flags);
+ ul_powerdown_finish();
+ a2_pc_disabled = 0;
+ a2_pc_disabled_wakelock_skipped = 0;
+ process_dynamic_mtu(false);
+ set_ul_mtu(0, true);
+ dynamic_mtu_enabled = false;
+
+ /* Cleanup Channel States */
+ mutex_lock(&bam_pdev_mutexlock);
+ for (i = 0; i < BAM_DMUX_NUM_CHANNELS; ++i) {
+ temp_remote_status = bam_ch_is_remote_open(i);
+ bam_ch[i].status &= ~BAM_CH_REMOTE_OPEN;
+ bam_ch[i].num_tx_pkts = 0;
+ if (bam_ch_is_local_open(i))
+ bam_ch[i].status |= BAM_CH_IN_RESET;
+ if (temp_remote_status) {
+ platform_device_unregister(bam_ch[i].pdev);
+ bam_ch[i].pdev = platform_device_alloc(
+ bam_ch[i].name, 2);
+ }
+ }
+ mutex_unlock(&bam_pdev_mutexlock);
+
+ /* Cleanup pending UL data */
+ spin_lock_irqsave(&bam_tx_pool_spinlock, flags);
+ while (!list_empty(&bam_tx_pool)) {
+ node = bam_tx_pool.next;
+ list_del(node);
+ info = container_of(node, struct tx_pkt_info,
+ list_node);
+ if (!info->is_cmd) {
+ dma_unmap_single(dma_dev, info->dma_address,
+ info->skb->len,
+ bam_ops->dma_to);
+ dev_kfree_skb_any(info->skb);
+ } else {
+ dma_unmap_single(dma_dev, info->dma_address,
+ info->len,
+ bam_ops->dma_to);
+ kfree(info->skb);
+ }
+ kfree(info);
+ }
+ spin_unlock_irqrestore(&bam_tx_pool_spinlock, flags);
+
+ BAM_DMUX_LOG("%s: complete\n", __func__);
+ return NOTIFY_DONE;
+}
+
+static int bam_init(void)
+{
+ unsigned long h;
+ dma_addr_t dma_addr;
+ int ret;
+ void *a2_virt_addr;
+ int skip_iounmap = 0;
+
+ in_global_reset = 0;
+ vote_dfab();
+ /* init BAM */
+ a2_virt_addr = ioremap_nocache(a2_phys_base, a2_phys_size);
+ if (!a2_virt_addr) {
+ pr_err("%s: ioremap failed\n", __func__);
+ ret = -ENOMEM;
+ goto ioremap_failed;
+ }
+ a2_props.phys_addr = a2_phys_base;
+ a2_props.virt_addr = a2_virt_addr;
+ a2_props.virt_size = a2_phys_size;
+ a2_props.irq = a2_bam_irq;
+ a2_props.options = SPS_BAM_OPT_IRQ_WAKEUP | SPS_BAM_HOLD_MEM;
+ a2_props.num_pipes = A2_NUM_PIPES;
+ a2_props.summing_threshold = A2_SUMMING_THRESHOLD;
+ a2_props.constrained_logging = true;
+ a2_props.logging_number = 1;
+ a2_props.ipc_loglevel = 3;
+ if (satellite_mode)
+ a2_props.manage = SPS_BAM_MGR_DEVICE_REMOTE;
+ /* need to free on tear down */
+ ret = bam_ops->sps_register_bam_device_ptr(&a2_props, &h);
+ if (ret < 0) {
+ pr_err("%s: register bam error %d\n", __func__, ret);
+ goto register_bam_failed;
+ }
+ a2_device_handle = h;
+
+ bam_tx_pipe = bam_ops->sps_alloc_endpoint_ptr();
+ if (bam_tx_pipe == NULL) {
+ pr_err("%s: tx alloc endpoint failed\n", __func__);
+ ret = -ENOMEM;
+ goto tx_alloc_endpoint_failed;
+ }
+ ret = bam_ops->sps_get_config_ptr(bam_tx_pipe, &tx_connection);
+ if (ret) {
+ pr_err("%s: tx get config failed %d\n", __func__, ret);
+ goto tx_get_config_failed;
+ }
+
+ tx_connection.source = SPS_DEV_HANDLE_MEM;
+ tx_connection.src_pipe_index = 0;
+ tx_connection.destination = h;
+ tx_connection.dest_pipe_index = 4;
+ tx_connection.mode = SPS_MODE_DEST;
+ tx_connection.options = SPS_O_AUTO_ENABLE | SPS_O_EOT;
+ tx_desc_mem_buf.size = 0x800; /* 2k */
+ tx_desc_mem_buf.base = dma_alloc_coherent(dma_dev, tx_desc_mem_buf.size,
+ &dma_addr, 0);
+ if (tx_desc_mem_buf.base == NULL) {
+ ret = -ENOMEM;
+ goto tx_get_config_failed;
+ }
+ tx_desc_mem_buf.phys_base = dma_addr;
+ memset(tx_desc_mem_buf.base, 0x0, tx_desc_mem_buf.size);
+ tx_connection.desc = tx_desc_mem_buf;
+ tx_connection.event_thresh = 0x10;
+
+ ret = bam_ops->sps_connect_ptr(bam_tx_pipe, &tx_connection);
+ if (ret < 0) {
+ pr_err("%s: tx connect error %d\n", __func__, ret);
+ goto tx_connect_failed;
+ }
+
+ bam_rx_pipe = bam_ops->sps_alloc_endpoint_ptr();
+ if (bam_rx_pipe == NULL) {
+ pr_err("%s: rx alloc endpoint failed\n", __func__);
+ ret = -ENOMEM;
+ goto rx_alloc_endpoint_failed;
+ }
+ ret = bam_ops->sps_get_config_ptr(bam_rx_pipe, &rx_connection);
+ if (ret) {
+ pr_err("%s: rx get config failed %d\n", __func__, ret);
+ goto rx_get_config_failed;
+ }
+
+ rx_connection.source = h;
+ rx_connection.src_pipe_index = 5;
+ rx_connection.destination = SPS_DEV_HANDLE_MEM;
+ rx_connection.dest_pipe_index = 1;
+ rx_connection.mode = SPS_MODE_SRC;
+ rx_connection.options = SPS_O_AUTO_ENABLE | SPS_O_EOT |
+ SPS_O_ACK_TRANSFERS;
+ rx_desc_mem_buf.size = 0x800; /* 2k */
+ rx_desc_mem_buf.base = dma_alloc_coherent(dma_dev, rx_desc_mem_buf.size,
+ &dma_addr, 0);
+ if (rx_desc_mem_buf.base == NULL) {
+ ret = -ENOMEM;
+ goto rx_mem_failed;
+ }
+ rx_desc_mem_buf.phys_base = dma_addr;
+ memset(rx_desc_mem_buf.base, 0x0, rx_desc_mem_buf.size);
+ rx_connection.desc = rx_desc_mem_buf;
+ rx_connection.event_thresh = 0x10;
+
+ ret = bam_ops->sps_connect_ptr(bam_rx_pipe, &rx_connection);
+ if (ret < 0) {
+ pr_err("%s: rx connect error %d\n", __func__, ret);
+ goto rx_connect_failed;
+ }
+
+ tx_register_event.options = SPS_O_EOT;
+ tx_register_event.mode = SPS_TRIGGER_CALLBACK;
+ tx_register_event.xfer_done = NULL;
+ tx_register_event.callback = bam_mux_tx_notify;
+ tx_register_event.user = NULL;
+ ret = bam_ops->sps_register_event_ptr(bam_tx_pipe, &tx_register_event);
+ if (ret < 0) {
+ pr_err("%s: tx register event error %d\n", __func__, ret);
+ goto rx_event_reg_failed;
+ }
+
+ rx_register_event.options = SPS_O_EOT;
+ rx_register_event.mode = SPS_TRIGGER_CALLBACK;
+ rx_register_event.xfer_done = NULL;
+ rx_register_event.callback = bam_mux_rx_notify;
+ rx_register_event.user = NULL;
+ ret = bam_ops->sps_register_event_ptr(bam_rx_pipe, &rx_register_event);
+ if (ret < 0) {
+ pr_err("%s: tx register event error %d\n", __func__, ret);
+ goto rx_event_reg_failed;
+ }
+
+ mutex_lock(&delayed_ul_vote_lock);
+ bam_mux_initialized = 1;
+ if (need_delayed_ul_vote) {
+ need_delayed_ul_vote = 0;
+ msm_bam_dmux_kickoff_ul_wakeup();
+ }
+ mutex_unlock(&delayed_ul_vote_lock);
+ toggle_apps_ack();
+ bam_connection_is_active = 1;
+ complete_all(&bam_connection_completion);
+ queue_rx();
+ return 0;
+
+rx_event_reg_failed:
+ bam_ops->sps_disconnect_ptr(bam_rx_pipe);
+rx_connect_failed:
+ dma_free_coherent(dma_dev, rx_desc_mem_buf.size, rx_desc_mem_buf.base,
+ rx_desc_mem_buf.phys_base);
+rx_mem_failed:
+rx_get_config_failed:
+ bam_ops->sps_free_endpoint_ptr(bam_rx_pipe);
+rx_alloc_endpoint_failed:
+ bam_ops->sps_disconnect_ptr(bam_tx_pipe);
+tx_connect_failed:
+ dma_free_coherent(dma_dev, tx_desc_mem_buf.size, tx_desc_mem_buf.base,
+ tx_desc_mem_buf.phys_base);
+tx_get_config_failed:
+ bam_ops->sps_free_endpoint_ptr(bam_tx_pipe);
+tx_alloc_endpoint_failed:
+ bam_ops->sps_deregister_bam_device_ptr(h);
+ /*
+ * sps_deregister_bam_device() calls iounmap. calling iounmap on the
+ * same handle below will cause a crash, so skip it if we've freed
+ * the handle here.
+ */
+ skip_iounmap = 1;
+register_bam_failed:
+ if (!skip_iounmap)
+ iounmap(a2_virt_addr);
+ioremap_failed:
+ /*destroy_workqueue(bam_mux_workqueue);*/
+ return ret;
+}
+
+static void toggle_apps_ack(void)
+{
+ static unsigned int clear_bit; /* 0 = set the bit, else clear bit */
+
+ if (in_global_reset) {
+ BAM_DMUX_LOG("%s: skipped due to SSR\n", __func__);
+ return;
+ }
+
+ BAM_DMUX_LOG("%s: apps ack %d->%d\n", __func__,
+ clear_bit & 0x1, ~clear_bit & 0x1);
+ bam_ops->smsm_change_state_ptr(SMSM_APPS_STATE,
+ clear_bit & SMSM_A2_POWER_CONTROL_ACK,
+ ~clear_bit & SMSM_A2_POWER_CONTROL_ACK);
+ clear_bit = ~clear_bit;
+ DBG_INC_ACK_OUT_CNT();
+}
+
+static void bam_dmux_smsm_cb(void *priv, uint32_t old_state, uint32_t new_state)
+{
+ static int last_processed_state;
+ int rcu_id;
+
+ rcu_id = srcu_read_lock(&bam_dmux_srcu);
+ mutex_lock(&smsm_cb_lock);
+ bam_dmux_power_state = new_state & SMSM_A2_POWER_CONTROL ? 1 : 0;
+ DBG_INC_A2_POWER_CONTROL_IN_CNT();
+ BAM_DMUX_LOG("%s: 0x%08x -> 0x%08x\n", __func__, old_state,
+ new_state);
+ if (last_processed_state == (new_state & SMSM_A2_POWER_CONTROL)) {
+ BAM_DMUX_LOG("%s: already processed this state\n", __func__);
+ mutex_unlock(&smsm_cb_lock);
+ srcu_read_unlock(&bam_dmux_srcu, rcu_id);
+ return;
+ }
+
+ last_processed_state = new_state & SMSM_A2_POWER_CONTROL;
+
+ if (bam_mux_initialized && new_state & SMSM_A2_POWER_CONTROL) {
+ BAM_DMUX_LOG("%s: reconnect\n", __func__);
+ grab_wakelock();
+ reconnect_to_bam();
+ } else if (bam_mux_initialized &&
+ !(new_state & SMSM_A2_POWER_CONTROL)) {
+ BAM_DMUX_LOG("%s: disconnect\n", __func__);
+ disconnect_to_bam();
+ release_wakelock();
+ } else if (new_state & SMSM_A2_POWER_CONTROL) {
+ BAM_DMUX_LOG("%s: init\n", __func__);
+ grab_wakelock();
+ bam_init();
+ } else {
+ BAM_DMUX_LOG("%s: bad state change\n", __func__);
+ pr_err("%s: unsupported state change\n", __func__);
+ }
+ mutex_unlock(&smsm_cb_lock);
+ srcu_read_unlock(&bam_dmux_srcu, rcu_id);
+}
+
+static void bam_dmux_smsm_ack_cb(void *priv, uint32_t old_state,
+ uint32_t new_state)
+{
+ int rcu_id;
+
+ rcu_id = srcu_read_lock(&bam_dmux_srcu);
+ DBG_INC_ACK_IN_CNT();
+ BAM_DMUX_LOG("%s: 0x%08x -> 0x%08x\n", __func__, old_state,
+ new_state);
+ complete_all(&ul_wakeup_ack_completion);
+ srcu_read_unlock(&bam_dmux_srcu, rcu_id);
+}
+
+/**
+ * msm_bam_dmux_set_bam_ops() - sets the bam_ops
+ * @ops: bam_ops_if to set
+ *
+ * Sets bam_ops to allow switching of runtime behavior. Preconditon, bam dmux
+ * must be in an idle state. If input ops is NULL, then bam_ops will be
+ * restored to their default state.
+ */
+void msm_bam_dmux_set_bam_ops(struct bam_ops_if *ops)
+{
+ if (ops != NULL)
+ bam_ops = ops;
+ else
+ bam_ops = &bam_default_ops;
+}
+EXPORT_SYMBOL(msm_bam_dmux_set_bam_ops);
+
+/**
+ * msm_bam_dmux_deinit() - puts bam dmux into a deinited state
+ *
+ * Puts bam dmux into a deinitialized state by simulating an ssr.
+ */
+void msm_bam_dmux_deinit(void)
+{
+ restart_notifier_cb(NULL, SUBSYS_BEFORE_SHUTDOWN, NULL);
+ restart_notifier_cb(NULL, SUBSYS_AFTER_SHUTDOWN, NULL);
+ restart_notifier_cb(NULL, SUBSYS_BEFORE_POWERUP, NULL);
+ restart_notifier_cb(NULL, SUBSYS_AFTER_POWERUP, NULL);
+ in_global_reset = 0;
+}
+EXPORT_SYMBOL(msm_bam_dmux_deinit);
+
+/**
+ * msm_bam_dmux_reinit() - reinitializes bam dmux
+ */
+void msm_bam_dmux_reinit(void)
+{
+ bam_mux_initialized = 0;
+ bam_ops->smsm_state_cb_register_ptr(SMSM_MODEM_STATE,
+ SMSM_A2_POWER_CONTROL,
+ bam_dmux_smsm_cb, NULL);
+ bam_ops->smsm_state_cb_register_ptr(SMSM_MODEM_STATE,
+ SMSM_A2_POWER_CONTROL_ACK,
+ bam_dmux_smsm_ack_cb, NULL);
+}
+EXPORT_SYMBOL(msm_bam_dmux_reinit);
+
+/**
+ * set_rx_buffer_ring_pool() - Configure the size of the rx ring pool to a
+ * supported value.
+ * @requested_buffs: Desired pool size.
+ *
+ * The requested size will be reduced to the largest supported size. The
+ * supported sizes must match the values in create_open_signal() for proper
+ * signal field construction in that function.
+ */
+static void set_rx_buffer_ring_pool(int requested_buffs)
+{
+ if (requested_buffs >= SZ_256) {
+ num_buffers = SZ_256;
+ return;
+ }
+
+ if (requested_buffs >= SZ_128) {
+ num_buffers = SZ_128;
+ return;
+ }
+
+ if (requested_buffs >= SZ_64) {
+ num_buffers = SZ_64;
+ return;
+ }
+
+ num_buffers = SZ_32;
+}
+
+/**
+ * set_dl_mtu() - Configure the non-default MTU to a supported value.
+ * @requested_mtu: Desired MTU size.
+ *
+ * Sets the dynamic receive MTU which can be enabled via negotiation with the
+ * remote side. Until the dynamic MTU is enabled, the default MTU will be used.
+ * The requested size will be reduced to the largest supported size. The
+ * supported sizes must match the values in create_open_signal() for proper
+ * signal field construction in that function.
+ */
+static void set_dl_mtu(int requested_mtu)
+{
+ if (requested_mtu >= SZ_16K) {
+ dl_mtu = SZ_16K;
+ return;
+ }
+
+ if (requested_mtu >= SZ_8K) {
+ dl_mtu = SZ_8K;
+ return;
+ }
+
+ if (requested_mtu >= SZ_4K) {
+ dl_mtu = SZ_4K;
+ return;
+ }
+
+ dl_mtu = SZ_2K;
+}
+
+static int bam_dmux_probe(struct platform_device *pdev)
+{
+ int rc;
+ struct resource *r;
+ void *subsys_h;
+ uint32_t requested_dl_mtu;
+
+ DBG("%s probe called\n", __func__);
+ if (bam_mux_initialized)
+ return 0;
+
+ if (pdev->dev.of_node) {
+ r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!r) {
+ pr_err("%s: reg field missing\n", __func__);
+ return -ENODEV;
+ }
+ a2_phys_base = r->start;
+ a2_phys_size = (uint32_t)(resource_size(r));
+ a2_bam_irq = platform_get_irq(pdev, 0);
+ if (a2_bam_irq == -ENXIO) {
+ pr_err("%s: irq field missing\n", __func__);
+ return -ENODEV;
+ }
+ satellite_mode = of_property_read_bool(pdev->dev.of_node,
+ "qcom,satellite-mode");
+
+ rc = of_property_read_u32(pdev->dev.of_node,
+ "qcom,rx-ring-size",
+ &num_buffers);
+ if (rc) {
+ DBG("%s: falling back to num_buffs default, rc:%d\n",
+ __func__, rc);
+ num_buffers = DEFAULT_NUM_BUFFERS;
+ }
+
+ set_rx_buffer_ring_pool(num_buffers);
+
+ rc = of_property_read_u32(pdev->dev.of_node,
+ "qcom,max-rx-mtu",
+ &requested_dl_mtu);
+ if (rc) {
+ DBG("%s: falling back to dl_mtu default, rc:%d\n",
+ __func__, rc);
+ requested_dl_mtu = 0;
+ }
+
+ set_dl_mtu(requested_dl_mtu);
+
+ no_cpu_affinity = of_property_read_bool(pdev->dev.of_node,
+ "qcom,no-cpu-affinity");
+
+ rc = of_property_read_bool(pdev->dev.of_node,
+ "qcom,fast-shutdown");
+ if (rc)
+ ul_timeout_delay = UL_FAST_TIMEOUT_DELAY;
+
+ BAM_DMUX_LOG(
+ "%s: base:%pK size:%x irq:%d satellite:%d num_buffs:%d dl_mtu:%x cpu-affinity:%d ul_timeout_delay:%d\n",
+ __func__,
+ (void *)(uintptr_t)a2_phys_base,
+ a2_phys_size,
+ a2_bam_irq,
+ satellite_mode,
+ num_buffers,
+ dl_mtu,
+ no_cpu_affinity,
+ ul_timeout_delay);
+ } else { /* fallback to default init data */
+ a2_phys_base = A2_PHYS_BASE;
+ a2_phys_size = A2_PHYS_SIZE;
+ a2_bam_irq = A2_BAM_IRQ;
+ num_buffers = DEFAULT_NUM_BUFFERS;
+ set_rx_buffer_ring_pool(num_buffers);
+ }
+
+ dma_dev = &pdev->dev;
+ /* The BAM only suports 32 bits of address */
+ dma_dev->dma_mask = kmalloc(sizeof(*dma_dev->dma_mask), GFP_KERNEL);
+ if (!dma_dev->dma_mask)
+ return -ENOMEM;
+
+ *dma_dev->dma_mask = DMA_BIT_MASK(32);
+ dma_dev->coherent_dma_mask = DMA_BIT_MASK(32);
+
+ xo_clk = clk_get(&pdev->dev, "xo");
+ if (IS_ERR(xo_clk)) {
+ BAM_DMUX_LOG("%s: did not get xo clock\n", __func__);
+ xo_clk = NULL;
+ }
+ dfab_clk = clk_get(&pdev->dev, "bus_clk");
+ if (IS_ERR(dfab_clk)) {
+ BAM_DMUX_LOG("%s: did not get dfab clock\n", __func__);
+ dfab_clk = NULL;
+ } else {
+ rc = clk_set_rate(dfab_clk, 64000000);
+ if (rc)
+ pr_err("%s: unable to set dfab clock rate\n", __func__);
+ }
+
+ /*
+ * setup the workqueue so that it can be pinned to core 0 and not
+ * block the watchdog pet function, so that netif_rx() in rmnet
+ * only uses one queue.
+ */
+ if (no_cpu_affinity)
+ bam_mux_rx_workqueue =
+ create_singlethread_workqueue("bam_dmux_rx");
+ else
+ bam_mux_rx_workqueue = alloc_workqueue("bam_dmux_rx",
+ WQ_MEM_RECLAIM | WQ_CPU_INTENSIVE, 1);
+ if (!bam_mux_rx_workqueue)
+ return -ENOMEM;
+
+ bam_mux_tx_workqueue = create_singlethread_workqueue("bam_dmux_tx");
+ if (!bam_mux_tx_workqueue) {
+ destroy_workqueue(bam_mux_rx_workqueue);
+ return -ENOMEM;
+ }
+
+ for (rc = 0; rc < BAM_DMUX_NUM_CHANNELS; ++rc) {
+ spin_lock_init(&bam_ch[rc].lock);
+ scnprintf(bam_ch[rc].name, BAM_DMUX_CH_NAME_MAX_LEN,
+ "bam_dmux_ch_%d", rc);
+ /* bus 2, ie a2 stream 2 */
+ bam_ch[rc].pdev = platform_device_alloc(bam_ch[rc].name, 2);
+ if (!bam_ch[rc].pdev) {
+ pr_err("%s: platform device alloc failed\n", __func__);
+ destroy_workqueue(bam_mux_rx_workqueue);
+ destroy_workqueue(bam_mux_tx_workqueue);
+ return -ENOMEM;
+ }
+ }
+
+ init_completion(&ul_wakeup_ack_completion);
+ init_completion(&bam_connection_completion);
+ init_completion(&dfab_unvote_completion);
+ init_completion(&shutdown_completion);
+ complete_all(&shutdown_completion);
+ INIT_DELAYED_WORK(&ul_timeout_work, ul_timeout);
+ wakeup_source_init(&bam_wakelock, "bam_dmux_wakelock");
+ init_srcu_struct(&bam_dmux_srcu);
+
+ subsys_h = subsys_notif_register_notifier("modem", &restart_notifier);
+ if (IS_ERR(subsys_h)) {
+ destroy_workqueue(bam_mux_rx_workqueue);
+ destroy_workqueue(bam_mux_tx_workqueue);
+ rc = (int)PTR_ERR(subsys_h);
+ pr_err("%s: failed to register for ssr rc: %d\n", __func__, rc);
+ return rc;
+ }
+
+ rc = bam_ops->smsm_state_cb_register_ptr(SMSM_MODEM_STATE,
+ SMSM_A2_POWER_CONTROL,
+ bam_dmux_smsm_cb, NULL);
+
+ if (rc) {
+ subsys_notif_unregister_notifier(subsys_h, &restart_notifier);
+ destroy_workqueue(bam_mux_rx_workqueue);
+ destroy_workqueue(bam_mux_tx_workqueue);
+ pr_err("%s: smsm cb register failed, rc: %d\n", __func__, rc);
+ return -ENOMEM;
+ }
+
+ rc = bam_ops->smsm_state_cb_register_ptr(SMSM_MODEM_STATE,
+ SMSM_A2_POWER_CONTROL_ACK,
+ bam_dmux_smsm_ack_cb, NULL);
+
+ if (rc) {
+ subsys_notif_unregister_notifier(subsys_h, &restart_notifier);
+ destroy_workqueue(bam_mux_rx_workqueue);
+ destroy_workqueue(bam_mux_tx_workqueue);
+ bam_ops->smsm_state_cb_deregister_ptr(SMSM_MODEM_STATE,
+ SMSM_A2_POWER_CONTROL,
+ bam_dmux_smsm_cb, NULL);
+ pr_err("%s: smsm ack cb register failed, rc: %d\n", __func__,
+ rc);
+ for (rc = 0; rc < BAM_DMUX_NUM_CHANNELS; ++rc)
+ platform_device_put(bam_ch[rc].pdev);
+ return -ENOMEM;
+ }
+
+ if (bam_ops->smsm_get_state_ptr(SMSM_MODEM_STATE) &
+ SMSM_A2_POWER_CONTROL)
+ bam_dmux_smsm_cb(NULL, 0,
+ bam_ops->smsm_get_state_ptr(SMSM_MODEM_STATE));
+
+ return 0;
+}
+
+static const struct of_device_id msm_match_table[] = {
+ {.compatible = "qcom,bam_dmux"},
+ {},
+};
+
+static struct platform_driver bam_dmux_driver = {
+ .probe = bam_dmux_probe,
+ .driver = {
+ .name = "BAM_RMNT",
+ .owner = THIS_MODULE,
+ .of_match_table = msm_match_table,
+ },
+};
+
+static int __init bam_dmux_init(void)
+{
+#ifdef CONFIG_DEBUG_FS
+ struct dentry *dent;
+
+ dent = debugfs_create_dir("bam_dmux", 0);
+ if (!IS_ERR(dent)) {
+ debug_create("tbl", 0444, dent, debug_tbl);
+ debug_create("ul_pkt_cnt", 0444, dent, debug_ul_pkt_cnt);
+ debug_create("stats", 0444, dent, debug_stats);
+ }
+#endif
+
+ bam_ipc_log_txt = ipc_log_context_create(BAM_IPC_LOG_PAGES, "bam_dmux",
+ 0);
+ if (!bam_ipc_log_txt)
+ pr_err("%s : unable to create IPC Logging Context", __func__);
+
+ rx_timer_interval = DEFAULT_POLLING_MIN_SLEEP;
+
+ return platform_driver_register(&bam_dmux_driver);
+}
+
+late_initcall(bam_dmux_init); /* needs to init after SMD */
+MODULE_DESCRIPTION("MSM BAM DMUX");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/soc/qcom/bam_dmux_private.h b/drivers/soc/qcom/bam_dmux_private.h
new file mode 100644
index 0000000..4f39076
--- /dev/null
+++ b/drivers/soc/qcom/bam_dmux_private.h
@@ -0,0 +1,182 @@
+/* Copyright (c) 2013-2014, 2018, 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 _BAM_DMUX_PRIVATE_H
+#define _BAM_DMUX_PRIVATE_H
+
+#include <linux/types.h>
+#include <linux/dma-mapping.h>
+
+#include <linux/msm-sps.h>
+
+#define BAM_MUX_HDR_MAGIC_NO 0x33fc
+#define BAM_MUX_HDR_CMD_DATA 0
+#define BAM_MUX_HDR_CMD_OPEN 1
+#define BAM_MUX_HDR_CMD_CLOSE 2
+#define BAM_MUX_HDR_CMD_STATUS 3 /* unused */
+#define BAM_MUX_HDR_CMD_OPEN_NO_A2_PC 4
+#define DEFAULT_BUFFER_SIZE SZ_2K
+
+#define DYNAMIC_MTU_MASK 0x2
+#define MTU_SIZE_MASK 0xc0
+#define MTU_SIZE_SHIFT 0x6
+#define DL_POOL_SIZE_SHIFT 0x4
+
+/**
+ * struct bam_ops_if - collection of function pointers to allow swappable
+ * runtime functionality
+ * @smsm_change_state_ptr: pointer to smsm_change_state function
+ * @smsm_get_state_ptr: pointer to smsm_get_state function
+ * @smsm_state_cb_register_ptr: pointer to smsm_state_cb_register function
+ * @smsm_state_cb_deregister_ptr: pointer to smsm_state_cb_deregister function
+ * @sps_connect_ptr: pointer to sps_connect function
+ * @sps_disconnect_ptr: pointer to sps_disconnect function
+ * @sps_register_bam_device_ptr: pointer to sps_register_bam_device
+ * @sps_deregister_bam_device_ptr: pointer to sps_deregister_bam_device
+ * function
+ * @sps_alloc_endpoint_ptr: pointer to sps_alloc_endpoint function
+ * @sps_free_endpoint_ptr: pointer to sps_free_endpoint function
+ * @sps_set_config_ptr: pointer to sps_set_config function
+ * @sps_get_config_ptr: pointer to sps_get_config function
+ * @sps_device_reset_ptr: pointer to sps_device_reset function
+ * @sps_register_event_ptr: pointer to sps_register_event function
+ * @sps_transfer_one_ptr: pointer to sps_transfer_one function
+ * @sps_get_iovec_ptr: pointer to sps_get_iovec function
+ * @sps_get_unused_desc_num_ptr: pointer to sps_get_unused_desc_num function
+ * @dma_to: enum for the direction of dma operations to device
+ * @dma_from: enum for the direction of dma operations from device
+ *
+ * This struct contains the interface from bam_dmux to smsm and sps. The
+ * pointers can be swapped out at run time to provide different functionality.
+ */
+struct bam_ops_if {
+ /* smsm */
+ int (*smsm_change_state_ptr)(uint32_t smsm_entry,
+ uint32_t clear_mask, uint32_t set_mask);
+
+ uint32_t (*smsm_get_state_ptr)(uint32_t smsm_entry);
+
+ int (*smsm_state_cb_register_ptr)(uint32_t smsm_entry, uint32_t mask,
+ void (*notify)(void *, uint32_t old_state, uint32_t new_state),
+ void *data);
+
+ int (*smsm_state_cb_deregister_ptr)(uint32_t smsm_entry, uint32_t mask,
+ void (*notify)(void *, uint32_t, uint32_t), void *data);
+
+ /* sps */
+ int (*sps_connect_ptr)(struct sps_pipe *h, struct sps_connect *connect);
+
+ int (*sps_disconnect_ptr)(struct sps_pipe *h);
+
+ int (*sps_register_bam_device_ptr)(
+ const struct sps_bam_props *bam_props,
+ unsigned long *dev_handle);
+
+ int (*sps_deregister_bam_device_ptr)(unsigned long dev_handle);
+
+ struct sps_pipe *(*sps_alloc_endpoint_ptr)(void);
+
+ int (*sps_free_endpoint_ptr)(struct sps_pipe *h);
+
+ int (*sps_set_config_ptr)(struct sps_pipe *h,
+ struct sps_connect *config);
+
+ int (*sps_get_config_ptr)(struct sps_pipe *h,
+ struct sps_connect *config);
+
+ int (*sps_device_reset_ptr)(unsigned long dev);
+
+ int (*sps_register_event_ptr)(struct sps_pipe *h,
+ struct sps_register_event *reg);
+
+ int (*sps_transfer_one_ptr)(struct sps_pipe *h,
+ phys_addr_t addr, u32 size,
+ void *user, u32 flags);
+
+ int (*sps_get_iovec_ptr)(struct sps_pipe *h,
+ struct sps_iovec *iovec);
+
+ int (*sps_get_unused_desc_num_ptr)(struct sps_pipe *h,
+ u32 *desc_num);
+
+ enum dma_data_direction dma_to;
+
+ enum dma_data_direction dma_from;
+};
+
+/**
+ * struct bam_mux_hdr - struct which contains bam dmux header info
+ * @magic_num: magic number placed at start to ensure that it is actually a
+ * valid bam dmux header
+ * @signal: optional signaling bits with commmand type specific definitions
+ * @cmd: the command
+ * @pad_len: the length of padding
+ * @ch_id: the id of the bam dmux channel that this is sent on
+ * @pkt_len: the length of the packet that this is the header of
+ */
+struct bam_mux_hdr {
+ uint16_t magic_num;
+ uint8_t signal;
+ uint8_t cmd;
+ uint8_t pad_len;
+ uint8_t ch_id;
+ uint16_t pkt_len;
+};
+
+/**
+ * struct rx_pkt_info - struct describing an rx packet
+ * @skb: socket buffer containing the packet
+ * @dma_address: dma mapped address of the packet
+ * @work: work_struct for processing the packet
+ * @list_node: list_head for placing this on a list
+ * @sps_size: size of the sps_iovec for this packet
+ * @len: total length of the buffer containing this packet
+ */
+struct rx_pkt_info {
+ struct sk_buff *skb;
+ dma_addr_t dma_address;
+ struct work_struct work;
+ struct list_head list_node;
+ uint16_t sps_size;
+ uint16_t len;
+};
+
+/**
+ * struct tx_pkt_info - struct describing a tx packet
+ * @skb: socket buffer containing the packet
+ * @dma_address: dma mapped address of the packet
+ * @is_cmd: signifies whether this is a command or data packet
+ * @len: length og the packet
+ * @work: work_struct for processing this packet
+ * @list_node: list_head for placing this on a list
+ * @ts_sec: seconds portion of the timestamp
+ * @ts_nsec: nanoseconds portion of the timestamp
+ *
+ */
+struct tx_pkt_info {
+ struct sk_buff *skb;
+ dma_addr_t dma_address;
+ char is_cmd;
+ uint32_t len;
+ struct work_struct work;
+ struct list_head list_node;
+ unsigned int ts_sec;
+ unsigned long ts_nsec;
+};
+
+void msm_bam_dmux_set_bam_ops(struct bam_ops_if *ops);
+
+void msm_bam_dmux_deinit(void);
+
+void msm_bam_dmux_reinit(void);
+
+#endif /* _BAM_DMUX_PRIVATE_H */
diff --git a/drivers/soc/qcom/dcc.c b/drivers/soc/qcom/dcc.c
index 43dddb4..a687286 100644
--- a/drivers/soc/qcom/dcc.c
+++ b/drivers/soc/qcom/dcc.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2015-2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2015-2018, 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
@@ -554,7 +554,8 @@
if (strlen(buf) >= 10)
return -EINVAL;
- strlcpy(str, buf, sizeof(str));
+ if (sscanf(buf, "%9s", str) != 1)
+ return -EINVAL;
mutex_lock(&drvdata->mutex);
if (drvdata->enable) {
@@ -599,7 +600,8 @@
if (strlen(buf) >= 10)
return -EINVAL;
- strlcpy(str, buf, sizeof(str));
+ if (sscanf(buf, "%9s", str) != 1)
+ return -EINVAL;
mutex_lock(&drvdata->mutex);
if (drvdata->enable) {
diff --git a/drivers/thermal/qpnp-adc-tm.c b/drivers/thermal/qpnp-adc-tm.c
index bec3dea..f8fdc13 100644
--- a/drivers/thermal/qpnp-adc-tm.c
+++ b/drivers/thermal/qpnp-adc-tm.c
@@ -42,15 +42,128 @@
#define QPNP_PERPH_TYPE2 0x2
#define QPNP_REVISION_EIGHT_CHANNEL_SUPPORT 2
#define QPNP_PERPH_SUBTYPE_TWO_CHANNEL_SUPPORT 0x22
+#define QPNP_STATUS1 0x8
+#define QPNP_STATUS1_OP_MODE 4
+#define QPNP_STATUS1_MEAS_INTERVAL_EN_STS BIT(2)
+#define QPNP_STATUS1_REQ_STS BIT(1)
+#define QPNP_STATUS1_EOC BIT(0)
+#define QPNP_STATUS2 0x9
+#define QPNP_STATUS2_CONV_SEQ_STATE 6
+#define QPNP_STATUS2_FIFO_NOT_EMPTY_FLAG BIT(1)
+#define QPNP_STATUS2_CONV_SEQ_TIMEOUT_STS BIT(0)
+#define QPNP_CONV_TIMEOUT_ERR 2
+
+#define QPNP_MODE_CTL 0x40
+#define QPNP_OP_MODE_SHIFT 3
+#define QPNP_VREF_XO_THM_FORCE BIT(2)
+#define QPNP_AMUX_TRIM_EN BIT(1)
+#define QPNP_ADC_TRIM_EN BIT(0)
#define QPNP_EN_CTL1 0x46
#define QPNP_ADC_TM_EN BIT(7)
#define QPNP_BTM_CONV_REQ 0x47
#define QPNP_ADC_CONV_REQ_EN BIT(7)
+#define QPNP_ADC_DIG_PARAM 0x50
+#define QPNP_ADC_DIG_DEC_RATIO_SEL_SHIFT 3
+#define QPNP_HW_SETTLE_DELAY 0x51
+#define QPNP_CONV_SEQ_CTL 0x54
+#define QPNP_CONV_SEQ_HOLDOFF_SHIFT 4
+#define QPNP_CONV_SEQ_TRIG_CTL 0x55
+#define QPNP_ADC_TM_MEAS_INTERVAL_CTL 0x57
+#define QPNP_ADC_TM_MEAS_INTERVAL_TIME_SHIFT 0x3
+#define QPNP_ADC_TM_MEAS_INTERVAL_CTL2 0x58
+#define QPNP_ADC_TM_MEAS_INTERVAL_CTL2_SHIFT 0x4
+#define QPNP_ADC_TM_MEAS_INTERVAL_CTL2_MASK 0xf0
+#define QPNP_ADC_TM_MEAS_INTERVAL_CTL3_MASK 0xf
+
+#define QPNP_ADC_MEAS_INTERVAL_OP_CTL 0x59
+#define QPNP_ADC_MEAS_INTERVAL_OP BIT(7)
+
#define QPNP_OP_MODE_SHIFT 3
#define QPNP_CONV_REQ 0x52
#define QPNP_CONV_REQ_SET BIT(7)
+#define QPNP_FAST_AVG_CTL 0x5a
+#define QPNP_FAST_AVG_EN 0x5b
+#define QPNP_FAST_AVG_ENABLED BIT(7)
+
+#define QPNP_M0_LOW_THR_LSB 0x5c
+#define QPNP_M0_LOW_THR_MSB 0x5d
+#define QPNP_M0_HIGH_THR_LSB 0x5e
+#define QPNP_M0_HIGH_THR_MSB 0x5f
+#define QPNP_M1_ADC_CH_SEL_CTL 0x68
+#define QPNP_M1_LOW_THR_LSB 0x69
+#define QPNP_M1_LOW_THR_MSB 0x6a
+#define QPNP_M1_HIGH_THR_LSB 0x6b
+#define QPNP_M1_HIGH_THR_MSB 0x6c
+#define QPNP_M2_ADC_CH_SEL_CTL 0x70
+#define QPNP_M2_LOW_THR_LSB 0x71
+#define QPNP_M2_LOW_THR_MSB 0x72
+#define QPNP_M2_HIGH_THR_LSB 0x73
+#define QPNP_M2_HIGH_THR_MSB 0x74
+#define QPNP_M3_ADC_CH_SEL_CTL 0x78
+#define QPNP_M3_LOW_THR_LSB 0x79
+#define QPNP_M3_LOW_THR_MSB 0x7a
+#define QPNP_M3_HIGH_THR_LSB 0x7b
+#define QPNP_M3_HIGH_THR_MSB 0x7c
+#define QPNP_M4_ADC_CH_SEL_CTL 0x80
+#define QPNP_M4_LOW_THR_LSB 0x81
+#define QPNP_M4_LOW_THR_MSB 0x82
+#define QPNP_M4_HIGH_THR_LSB 0x83
+#define QPNP_M4_HIGH_THR_MSB 0x84
+#define QPNP_M5_ADC_CH_SEL_CTL 0x88
+#define QPNP_M5_LOW_THR_LSB 0x89
+#define QPNP_M5_LOW_THR_MSB 0x8a
+#define QPNP_M5_HIGH_THR_LSB 0x8b
+#define QPNP_M5_HIGH_THR_MSB 0x8c
+#define QPNP_M6_ADC_CH_SEL_CTL 0x90
+#define QPNP_M6_LOW_THR_LSB 0x91
+#define QPNP_M6_LOW_THR_MSB 0x92
+#define QPNP_M6_HIGH_THR_LSB 0x93
+#define QPNP_M6_HIGH_THR_MSB 0x94
+#define QPNP_M7_ADC_CH_SEL_CTL 0x98
+#define QPNP_M7_LOW_THR_LSB 0x99
+#define QPNP_M7_LOW_THR_MSB 0x9a
+#define QPNP_M7_HIGH_THR_LSB 0x9b
+#define QPNP_M7_HIGH_THR_MSB 0x9c
+
+#define QPNP_ADC_TM_MULTI_MEAS_EN 0x41
+#define QPNP_ADC_TM_MULTI_MEAS_EN_M0 BIT(0)
+#define QPNP_ADC_TM_MULTI_MEAS_EN_M1 BIT(1)
+#define QPNP_ADC_TM_MULTI_MEAS_EN_M2 BIT(2)
+#define QPNP_ADC_TM_MULTI_MEAS_EN_M3 BIT(3)
+#define QPNP_ADC_TM_MULTI_MEAS_EN_M4 BIT(4)
+#define QPNP_ADC_TM_MULTI_MEAS_EN_M5 BIT(5)
+#define QPNP_ADC_TM_MULTI_MEAS_EN_M6 BIT(6)
+#define QPNP_ADC_TM_MULTI_MEAS_EN_M7 BIT(7)
+#define QPNP_ADC_TM_LOW_THR_INT_EN 0x42
+#define QPNP_ADC_TM_LOW_THR_INT_EN_M0 BIT(0)
+#define QPNP_ADC_TM_LOW_THR_INT_EN_M1 BIT(1)
+#define QPNP_ADC_TM_LOW_THR_INT_EN_M2 BIT(2)
+#define QPNP_ADC_TM_LOW_THR_INT_EN_M3 BIT(3)
+#define QPNP_ADC_TM_LOW_THR_INT_EN_M4 BIT(4)
+#define QPNP_ADC_TM_LOW_THR_INT_EN_M5 BIT(5)
+#define QPNP_ADC_TM_LOW_THR_INT_EN_M6 BIT(6)
+#define QPNP_ADC_TM_LOW_THR_INT_EN_M7 BIT(7)
+#define QPNP_ADC_TM_HIGH_THR_INT_EN 0x43
+#define QPNP_ADC_TM_HIGH_THR_INT_EN_M0 BIT(0)
+#define QPNP_ADC_TM_HIGH_THR_INT_EN_M1 BIT(1)
+#define QPNP_ADC_TM_HIGH_THR_INT_EN_M2 BIT(2)
+#define QPNP_ADC_TM_HIGH_THR_INT_EN_M3 BIT(3)
+#define QPNP_ADC_TM_HIGH_THR_INT_EN_M4 BIT(4)
+#define QPNP_ADC_TM_HIGH_THR_INT_EN_M5 BIT(5)
+#define QPNP_ADC_TM_HIGH_THR_INT_EN_M6 BIT(6)
+#define QPNP_ADC_TM_HIGH_THR_INT_EN_M7 BIT(7)
+
+#define QPNP_ADC_TM_M0_MEAS_INTERVAL_CTL 0x59
+#define QPNP_ADC_TM_M1_MEAS_INTERVAL_CTL 0x6d
+#define QPNP_ADC_TM_M2_MEAS_INTERVAL_CTL 0x75
+#define QPNP_ADC_TM_M3_MEAS_INTERVAL_CTL 0x7d
+#define QPNP_ADC_TM_M4_MEAS_INTERVAL_CTL 0x85
+#define QPNP_ADC_TM_M5_MEAS_INTERVAL_CTL 0x8d
+#define QPNP_ADC_TM_M6_MEAS_INTERVAL_CTL 0x95
+#define QPNP_ADC_TM_M7_MEAS_INTERVAL_CTL 0x9d
+
#define QPNP_ADC_TM_STATUS1 0x8
#define QPNP_ADC_TM_STATUS_LOW 0xa
#define QPNP_ADC_TM_STATUS_HIGH 0xb
@@ -62,6 +175,10 @@
#define QPNP_ADC_TM_THR_LSB_MASK(val) (val & 0xff)
#define QPNP_ADC_TM_THR_MSB_MASK(val) ((val & 0xff00) >> 8)
+#define QPNP_MIN_TIME 2000
+#define QPNP_MAX_TIME 2100
+#define QPNP_RETRY 1000
+
/* QPNP ADC TM HC start */
#define QPNP_BTM_HC_STATUS1 0x08
#define QPNP_BTM_HC_STATUS_LOW 0x0a
@@ -110,6 +227,8 @@
u8 adc_tm_high_enable;
u8 adc_tm_low_thr_set;
u8 adc_tm_high_thr_set;
+ spinlock_t adc_tm_low_lock;
+ spinlock_t adc_tm_high_lock;
};
struct qpnp_adc_thr_client_info {
@@ -168,6 +287,69 @@
LIST_HEAD(qpnp_adc_tm_device_list);
+struct qpnp_adc_tm_trip_reg_type {
+ enum qpnp_adc_tm_channel_select btm_amux_chan;
+ uint16_t low_thr_lsb_addr;
+ uint16_t low_thr_msb_addr;
+ uint16_t high_thr_lsb_addr;
+ uint16_t high_thr_msb_addr;
+ u8 multi_meas_en;
+ u8 low_thr_int_chan_en;
+ u8 high_thr_int_chan_en;
+ u8 meas_interval_ctl;
+};
+
+static struct qpnp_adc_tm_trip_reg_type adc_tm_data[] = {
+ [QPNP_ADC_TM_CHAN0] = {QPNP_ADC_TM_M0_ADC_CH_SEL_CTL,
+ QPNP_M0_LOW_THR_LSB,
+ QPNP_M0_LOW_THR_MSB, QPNP_M0_HIGH_THR_LSB,
+ QPNP_M0_HIGH_THR_MSB, QPNP_ADC_TM_MULTI_MEAS_EN_M0,
+ QPNP_ADC_TM_LOW_THR_INT_EN_M0, QPNP_ADC_TM_HIGH_THR_INT_EN_M0,
+ QPNP_ADC_TM_M0_MEAS_INTERVAL_CTL},
+ [QPNP_ADC_TM_CHAN1] = {QPNP_ADC_TM_M1_ADC_CH_SEL_CTL,
+ QPNP_M1_LOW_THR_LSB,
+ QPNP_M1_LOW_THR_MSB, QPNP_M1_HIGH_THR_LSB,
+ QPNP_M1_HIGH_THR_MSB, QPNP_ADC_TM_MULTI_MEAS_EN_M1,
+ QPNP_ADC_TM_LOW_THR_INT_EN_M1, QPNP_ADC_TM_HIGH_THR_INT_EN_M1,
+ QPNP_ADC_TM_M1_MEAS_INTERVAL_CTL},
+ [QPNP_ADC_TM_CHAN2] = {QPNP_ADC_TM_M2_ADC_CH_SEL_CTL,
+ QPNP_M2_LOW_THR_LSB,
+ QPNP_M2_LOW_THR_MSB, QPNP_M2_HIGH_THR_LSB,
+ QPNP_M2_HIGH_THR_MSB, QPNP_ADC_TM_MULTI_MEAS_EN_M2,
+ QPNP_ADC_TM_LOW_THR_INT_EN_M2, QPNP_ADC_TM_HIGH_THR_INT_EN_M2,
+ QPNP_ADC_TM_M2_MEAS_INTERVAL_CTL},
+ [QPNP_ADC_TM_CHAN3] = {QPNP_ADC_TM_M3_ADC_CH_SEL_CTL,
+ QPNP_M3_LOW_THR_LSB,
+ QPNP_M3_LOW_THR_MSB, QPNP_M3_HIGH_THR_LSB,
+ QPNP_M3_HIGH_THR_MSB, QPNP_ADC_TM_MULTI_MEAS_EN_M3,
+ QPNP_ADC_TM_LOW_THR_INT_EN_M3, QPNP_ADC_TM_HIGH_THR_INT_EN_M3,
+ QPNP_ADC_TM_M3_MEAS_INTERVAL_CTL},
+ [QPNP_ADC_TM_CHAN4] = {QPNP_ADC_TM_M4_ADC_CH_SEL_CTL,
+ QPNP_M4_LOW_THR_LSB,
+ QPNP_M4_LOW_THR_MSB, QPNP_M4_HIGH_THR_LSB,
+ QPNP_M4_HIGH_THR_MSB, QPNP_ADC_TM_MULTI_MEAS_EN_M4,
+ QPNP_ADC_TM_LOW_THR_INT_EN_M4, QPNP_ADC_TM_HIGH_THR_INT_EN_M4,
+ QPNP_ADC_TM_M4_MEAS_INTERVAL_CTL},
+ [QPNP_ADC_TM_CHAN5] = {QPNP_ADC_TM_M5_ADC_CH_SEL_CTL,
+ QPNP_M5_LOW_THR_LSB,
+ QPNP_M5_LOW_THR_MSB, QPNP_M5_HIGH_THR_LSB,
+ QPNP_M5_HIGH_THR_MSB, QPNP_ADC_TM_MULTI_MEAS_EN_M5,
+ QPNP_ADC_TM_LOW_THR_INT_EN_M5, QPNP_ADC_TM_HIGH_THR_INT_EN_M5,
+ QPNP_ADC_TM_M5_MEAS_INTERVAL_CTL},
+ [QPNP_ADC_TM_CHAN6] = {QPNP_ADC_TM_M6_ADC_CH_SEL_CTL,
+ QPNP_M6_LOW_THR_LSB,
+ QPNP_M6_LOW_THR_MSB, QPNP_M6_HIGH_THR_LSB,
+ QPNP_M6_HIGH_THR_MSB, QPNP_ADC_TM_MULTI_MEAS_EN_M6,
+ QPNP_ADC_TM_LOW_THR_INT_EN_M6, QPNP_ADC_TM_HIGH_THR_INT_EN_M6,
+ QPNP_ADC_TM_M6_MEAS_INTERVAL_CTL},
+ [QPNP_ADC_TM_CHAN7] = {QPNP_ADC_TM_M7_ADC_CH_SEL_CTL,
+ QPNP_M7_LOW_THR_LSB,
+ QPNP_M7_LOW_THR_MSB, QPNP_M7_HIGH_THR_LSB,
+ QPNP_M7_HIGH_THR_MSB, QPNP_ADC_TM_MULTI_MEAS_EN_M7,
+ QPNP_ADC_TM_LOW_THR_INT_EN_M7, QPNP_ADC_TM_HIGH_THR_INT_EN_M7,
+ QPNP_ADC_TM_M7_MEAS_INTERVAL_CTL},
+};
+
static struct qpnp_adc_tm_reverse_scale_fn adc_tm_rscale_fn[] = {
[SCALE_R_VBATT] = {qpnp_adc_vbatt_rscaler},
[SCALE_RBATT_THERM] = {qpnp_adc_btm_scaler},
@@ -208,6 +390,33 @@
return rc;
}
+static int32_t qpnp_adc_tm_fast_avg_en(struct qpnp_adc_tm_chip *chip,
+ uint32_t *fast_avg_sample)
+{
+ int rc = 0, version = 0;
+ u8 fast_avg_en = 0;
+
+ version = qpnp_adc_get_revid_version(chip->dev);
+ if (!((version == QPNP_REV_ID_8916_1_0) ||
+ (version == QPNP_REV_ID_8916_1_1) ||
+ (version == QPNP_REV_ID_8916_2_0))) {
+ pr_debug("fast-avg-en not required for this version\n");
+ return rc;
+ }
+
+ fast_avg_en = QPNP_FAST_AVG_ENABLED;
+ rc = qpnp_adc_tm_write_reg(chip, QPNP_FAST_AVG_EN, fast_avg_en, 1);
+ if (rc < 0) {
+ pr_err("adc-tm fast-avg enable err\n");
+ return rc;
+ }
+
+ if (*fast_avg_sample >= 3)
+ *fast_avg_sample = 2;
+
+ return rc;
+}
+
static int qpnp_adc_tm_check_vreg_vote(struct qpnp_adc_tm_chip *chip)
{
int rc = 0;
@@ -244,19 +453,37 @@
return rc;
}
- data = QPNP_ADC_CONV_REQ_EN;
- rc = qpnp_adc_tm_write_reg(chip, QPNP_BTM_CONV_REQ, data, 1);
- if (rc < 0) {
- pr_err("adc-tm enable failed\n");
- return rc;
+ if (chip->adc_tm_hc) {
+ data = QPNP_ADC_CONV_REQ_EN;
+ rc = qpnp_adc_tm_write_reg(chip, QPNP_BTM_CONV_REQ, data, 1);
+ if (rc < 0) {
+ pr_err("adc-tm enable failed\n");
+ return rc;
+ }
}
-
return rc;
}
static int32_t qpnp_adc_tm_disable(struct qpnp_adc_tm_chip *chip)
{
- return 0;
+ u8 data = 0;
+ int rc = 0;
+
+ if (chip->adc_tm_hc) {
+ rc = qpnp_adc_tm_write_reg(chip, QPNP_BTM_CONV_REQ, data, 1);
+ if (rc < 0) {
+ pr_err("adc-tm enable failed\n");
+ return rc;
+ }
+ }
+
+ rc = qpnp_adc_tm_write_reg(chip, QPNP_EN_CTL1, data, 1);
+ if (rc < 0) {
+ pr_err("adc-tm disable failed\n");
+ return rc;
+ }
+
+ return rc;
}
static int qpnp_adc_tm_is_valid(struct qpnp_adc_tm_chip *chip)
@@ -320,11 +547,129 @@
static int32_t qpnp_adc_tm_enable_if_channel_meas(
struct qpnp_adc_tm_chip *chip)
{
+ u8 adc_tm_meas_en = 0, status_low = 0, status_high = 0;
int rc = 0;
- rc = qpnp_adc_tm_rc_check_channel_en(chip);
+ if (chip->adc_tm_hc) {
+ rc = qpnp_adc_tm_rc_check_channel_en(chip);
+ if (rc) {
+ pr_err("adc_tm channel check failed\n");
+ return rc;
+ }
+ } else {
+ /* Check if a measurement request is still required */
+ rc = qpnp_adc_tm_read_reg(chip, QPNP_ADC_TM_MULTI_MEAS_EN,
+ &adc_tm_meas_en, 1);
+ if (rc) {
+ pr_err("read status high failed with %d\n", rc);
+ return rc;
+ }
+ rc = qpnp_adc_tm_read_reg(chip, QPNP_ADC_TM_LOW_THR_INT_EN,
+ &status_low, 1);
+ if (rc) {
+ pr_err("read status low failed with %d\n", rc);
+ return rc;
+ }
+
+ rc = qpnp_adc_tm_read_reg(chip, QPNP_ADC_TM_HIGH_THR_INT_EN,
+ &status_high, 1);
+ if (rc) {
+ pr_err("read status high failed with %d\n", rc);
+ return rc;
+ }
+
+ /* Enable only if there are pending measurement requests */
+ if ((adc_tm_meas_en && status_high) ||
+ (adc_tm_meas_en && status_low)) {
+ qpnp_adc_tm_enable(chip);
+ /* Request conversion */
+ rc = qpnp_adc_tm_write_reg(chip, QPNP_CONV_REQ,
+ QPNP_CONV_REQ_SET, 1);
+ if (rc < 0) {
+ pr_err("adc-tm request conversion failed\n");
+ return rc;
+ }
+ } else {
+ /* disable the vote if applicable */
+ if (chip->adc_vote_enable && chip->adc->hkadc_ldo &&
+ chip->adc->hkadc_ldo_ok) {
+ qpnp_adc_disable_voltage(chip->adc);
+ chip->adc_vote_enable = false;
+ }
+ }
+ }
+ return rc;
+}
+
+static int32_t qpnp_adc_tm_mode_select(struct qpnp_adc_tm_chip *chip,
+ u8 mode_ctl)
+{
+ int rc;
+
+ mode_ctl |= (QPNP_ADC_TRIM_EN | QPNP_AMUX_TRIM_EN);
+
+ /* VADC_BTM current sets mode to recurring measurements */
+ rc = qpnp_adc_tm_write_reg(chip, QPNP_MODE_CTL, mode_ctl, 1);
+ if (rc < 0)
+ pr_err("adc-tm write mode selection err\n");
+
+ return rc;
+}
+
+static int32_t qpnp_adc_tm_req_sts_check(struct qpnp_adc_tm_chip *chip)
+{
+ u8 status1 = 0, mode_ctl = 0;
+ int rc, count = 0;
+
+ /* Re-enable the peripheral */
+ rc = qpnp_adc_tm_enable(chip);
if (rc) {
- pr_err("adc_tm channel check failed\n");
+ pr_err("adc-tm re-enable peripheral failed\n");
+ return rc;
+ }
+
+ /* The VADC_TM bank needs to be disabled for new conversion request */
+ rc = qpnp_adc_tm_read_reg(chip, QPNP_ADC_TM_STATUS1, &status1, 1);
+ if (rc) {
+ pr_err("adc-tm read status1 failed\n");
+ return rc;
+ }
+
+ /* Disable the bank if a conversion is occurring */
+ while (status1 & QPNP_STATUS1_REQ_STS) {
+ if (count > QPNP_RETRY) {
+ pr_err("retry error=%d with 0x%x\n", count, status1);
+ break;
+ }
+ /*
+ * Wait time is based on the optimum sampling rate
+ * and adding enough time buffer to account for ADC conversions
+ * occurring on different peripheral banks
+ */
+ usleep_range(QPNP_MIN_TIME, QPNP_MAX_TIME);
+ rc = qpnp_adc_tm_read_reg(chip, QPNP_ADC_TM_STATUS1,
+ &status1, 1);
+ if (rc < 0) {
+ pr_err("adc-tm disable failed\n");
+ return rc;
+ }
+ count++;
+ }
+
+ if (!chip->adc_tm_hc) {
+ /* Change the mode back to recurring measurement mode */
+ mode_ctl = ADC_OP_MEASUREMENT_INTERVAL << QPNP_OP_MODE_SHIFT;
+ rc = qpnp_adc_tm_mode_select(chip, mode_ctl);
+ if (rc < 0) {
+ pr_err("adc-tm mode change to recurring failed\n");
+ return rc;
+ }
+ }
+
+ /* Disable the peripheral */
+ rc = qpnp_adc_tm_disable(chip);
+ if (rc < 0) {
+ pr_err("adc-tm peripheral disable failed\n");
return rc;
}
@@ -337,17 +682,25 @@
int rc = 0, i;
bool chan_found = false;
- for (i = 0; i < chip->max_channels_available; i++) {
- if (chip->sensor[i].btm_channel_num == btm_chan) {
- *btm_chan_idx = i;
- chan_found = true;
- break;
+ if (!chip->adc_tm_hc) {
+ for (i = 0; i < QPNP_ADC_TM_CHAN_NONE; i++) {
+ if (adc_tm_data[i].btm_amux_chan == btm_chan) {
+ *btm_chan_idx = i;
+ chan_found = true;
+ }
+ }
+ } else {
+ for (i = 0; i < chip->max_channels_available; i++) {
+ if (chip->sensor[i].btm_channel_num == btm_chan) {
+ *btm_chan_idx = i;
+ chan_found = true;
+ break;
+ }
}
}
if (!chan_found)
return -EINVAL;
-
return rc;
}
@@ -412,19 +765,29 @@
switch (chip->sensor[chan_idx].timer_select) {
case ADC_MEAS_TIMER_SELECT1:
- rc = qpnp_adc_tm_write_reg(chip,
+ if (!chip->adc_tm_hc)
+ rc = qpnp_adc_tm_write_reg(chip,
+ QPNP_ADC_TM_MEAS_INTERVAL_CTL,
+ chip->sensor[chan_idx].meas_interval, 1);
+ else
+ rc = qpnp_adc_tm_write_reg(chip,
QPNP_BTM_MEAS_INTERVAL_CTL,
chip->sensor[chan_idx].meas_interval, 1);
if (rc < 0) {
pr_err("timer1 configure failed\n");
return rc;
}
- break;
+ break;
case ADC_MEAS_TIMER_SELECT2:
/* Thermal channels uses timer2, default to 1 second */
- rc = qpnp_adc_tm_read_reg(chip,
- QPNP_BTM_MEAS_INTERVAL_CTL2,
+ if (!chip->adc_tm_hc)
+ rc = qpnp_adc_tm_read_reg(chip,
+ QPNP_ADC_TM_MEAS_INTERVAL_CTL2,
&meas_interval_timer2, 1);
+ else
+ rc = qpnp_adc_tm_read_reg(chip,
+ QPNP_BTM_MEAS_INTERVAL_CTL2,
+ &meas_interval_timer2, 1);
if (rc < 0) {
pr_err("timer2 configure read failed\n");
return rc;
@@ -433,7 +796,12 @@
timer_interval_store <<= QPNP_ADC_TM_MEAS_INTERVAL_CTL2_SHIFT;
timer_interval_store &= QPNP_ADC_TM_MEAS_INTERVAL_CTL2_MASK;
meas_interval_timer2 |= timer_interval_store;
- rc = qpnp_adc_tm_write_reg(chip,
+ if (!chip->adc_tm_hc)
+ rc = qpnp_adc_tm_write_reg(chip,
+ QPNP_ADC_TM_MEAS_INTERVAL_CTL2,
+ meas_interval_timer2, 1);
+ else
+ rc = qpnp_adc_tm_write_reg(chip,
QPNP_BTM_MEAS_INTERVAL_CTL2,
meas_interval_timer2, 1);
if (rc < 0) {
@@ -442,7 +810,12 @@
}
break;
case ADC_MEAS_TIMER_SELECT3:
- rc = qpnp_adc_tm_read_reg(chip,
+ if (!chip->adc_tm_hc)
+ rc = qpnp_adc_tm_read_reg(chip,
+ QPNP_ADC_TM_MEAS_INTERVAL_CTL2,
+ &meas_interval_timer2, 1);
+ else
+ rc = qpnp_adc_tm_read_reg(chip,
QPNP_BTM_MEAS_INTERVAL_CTL2,
&meas_interval_timer2, 1);
if (rc < 0) {
@@ -452,6 +825,11 @@
timer_interval_store = chip->sensor[chan_idx].meas_interval;
timer_interval_store &= QPNP_ADC_TM_MEAS_INTERVAL_CTL3_MASK;
meas_interval_timer2 |= timer_interval_store;
+ if (!chip->adc_tm_hc)
+ rc = qpnp_adc_tm_write_reg(chip,
+ QPNP_ADC_TM_MEAS_INTERVAL_CTL2,
+ meas_interval_timer2, 1);
+ else
rc = qpnp_adc_tm_write_reg(chip,
QPNP_BTM_MEAS_INTERVAL_CTL2,
meas_interval_timer2, 1);
@@ -471,7 +849,12 @@
pr_err("Invalid btm channel idx\n");
return rc;
}
- rc = qpnp_adc_tm_write_reg(chip,
+ if (!chip->adc_tm_hc)
+ rc = qpnp_adc_tm_write_reg(chip,
+ adc_tm_data[btm_chan_idx].meas_interval_ctl,
+ chip->sensor[chan_idx].timer_select, 1);
+ else
+ rc = qpnp_adc_tm_write_reg(chip,
QPNP_BTM_Mn_MEAS_INTERVAL_CTL(btm_chan_idx),
chip->sensor[chan_idx].timer_select, 1);
if (rc < 0) {
@@ -556,6 +939,69 @@
return rc;
}
+static int32_t qpnp_adc_tm_read_thr_value(struct qpnp_adc_tm_chip *chip,
+ uint32_t btm_chan)
+{
+ int rc = 0;
+ u8 data_lsb = 0, data_msb = 0;
+ uint32_t btm_chan_idx = 0;
+ int32_t low_thr = 0, high_thr = 0;
+
+ if (!chip->adc_tm_hc) {
+ pr_err("Not applicable for VADC HC peripheral\n");
+ return -EINVAL;
+ }
+
+ rc = qpnp_adc_tm_get_btm_idx(chip, btm_chan, &btm_chan_idx);
+ if (rc < 0) {
+ pr_err("Invalid btm channel idx\n");
+ return rc;
+ }
+
+ rc = qpnp_adc_tm_read_reg(chip,
+ adc_tm_data[btm_chan_idx].low_thr_lsb_addr,
+ &data_lsb, 1);
+ if (rc < 0) {
+ pr_err("low threshold lsb setting failed\n");
+ return rc;
+ }
+
+ rc = qpnp_adc_tm_read_reg(chip,
+ adc_tm_data[btm_chan_idx].low_thr_msb_addr,
+ &data_msb, 1);
+ if (rc < 0) {
+ pr_err("low threshold msb setting failed\n");
+ return rc;
+ }
+
+ low_thr = (data_msb << 8) | data_lsb;
+
+ rc = qpnp_adc_tm_read_reg(chip,
+ adc_tm_data[btm_chan_idx].high_thr_lsb_addr,
+ &data_lsb, 1);
+ if (rc < 0) {
+ pr_err("high threshold lsb setting failed\n");
+ return rc;
+ }
+
+ rc = qpnp_adc_tm_read_reg(chip,
+ adc_tm_data[btm_chan_idx].high_thr_msb_addr,
+ &data_msb, 1);
+ if (rc < 0) {
+ pr_err("high threshold msb setting failed\n");
+ return rc;
+ }
+
+ high_thr = (data_msb << 8) | data_lsb;
+
+ pr_debug("configured thresholds high:0x%x and low:0x%x\n",
+ high_thr, low_thr);
+
+ return rc;
+}
+
+
+
static int32_t qpnp_adc_tm_thr_update(struct qpnp_adc_tm_chip *chip,
uint32_t btm_chan, int32_t high_thr, int32_t low_thr)
{
@@ -568,38 +1014,71 @@
return rc;
}
- rc = qpnp_adc_tm_write_reg(chip,
- QPNP_BTM_Mn_LOW_THR0(btm_chan_idx),
- QPNP_ADC_TM_THR_LSB_MASK(low_thr), 1);
- if (rc < 0) {
- pr_err("low threshold lsb setting failed\n");
- return rc;
- }
+ if (!chip->adc_tm_hc) {
+ rc = qpnp_adc_tm_write_reg(chip,
+ adc_tm_data[btm_chan_idx].low_thr_lsb_addr,
+ QPNP_ADC_TM_THR_LSB_MASK(low_thr), 1);
+ if (rc < 0) {
+ pr_err("low threshold lsb setting failed\n");
+ return rc;
+ }
- rc = qpnp_adc_tm_write_reg(chip,
- QPNP_BTM_Mn_LOW_THR1(btm_chan_idx),
- QPNP_ADC_TM_THR_MSB_MASK(low_thr), 1);
- if (rc < 0) {
- pr_err("low threshold msb setting failed\n");
- return rc;
- }
+ rc = qpnp_adc_tm_write_reg(chip,
+ adc_tm_data[btm_chan_idx].low_thr_msb_addr,
+ QPNP_ADC_TM_THR_MSB_MASK(low_thr), 1);
+ if (rc < 0) {
+ pr_err("low threshold msb setting failed\n");
+ return rc;
+ }
- rc = qpnp_adc_tm_write_reg(chip,
- QPNP_BTM_Mn_HIGH_THR0(btm_chan_idx),
- QPNP_ADC_TM_THR_LSB_MASK(high_thr), 1);
- if (rc < 0) {
- pr_err("high threshold lsb setting failed\n");
- return rc;
- }
+ rc = qpnp_adc_tm_write_reg(chip,
+ adc_tm_data[btm_chan_idx].high_thr_lsb_addr,
+ QPNP_ADC_TM_THR_LSB_MASK(high_thr), 1);
+ if (rc < 0) {
+ pr_err("high threshold lsb setting failed\n");
+ return rc;
+ }
- rc = qpnp_adc_tm_write_reg(chip,
- QPNP_BTM_Mn_HIGH_THR1(btm_chan_idx),
- QPNP_ADC_TM_THR_MSB_MASK(high_thr), 1);
- if (rc < 0)
- pr_err("high threshold msb setting failed\n");
+ rc = qpnp_adc_tm_write_reg(chip,
+ adc_tm_data[btm_chan_idx].high_thr_msb_addr,
+ QPNP_ADC_TM_THR_MSB_MASK(high_thr), 1);
+ if (rc < 0)
+ pr_err("high threshold msb setting failed\n");
+ } else {
+ rc = qpnp_adc_tm_write_reg(chip,
+ QPNP_BTM_Mn_LOW_THR0(btm_chan_idx),
+ QPNP_ADC_TM_THR_LSB_MASK(low_thr), 1);
+ if (rc < 0) {
+ pr_err("low threshold lsb setting failed\n");
+ return rc;
+ }
+
+ rc = qpnp_adc_tm_write_reg(chip,
+ QPNP_BTM_Mn_LOW_THR1(btm_chan_idx),
+ QPNP_ADC_TM_THR_MSB_MASK(low_thr), 1);
+ if (rc < 0) {
+ pr_err("low threshold msb setting failed\n");
+ return rc;
+ }
+
+ rc = qpnp_adc_tm_write_reg(chip,
+ QPNP_BTM_Mn_HIGH_THR0(btm_chan_idx),
+ QPNP_ADC_TM_THR_LSB_MASK(high_thr), 1);
+ if (rc < 0) {
+ pr_err("high threshold lsb setting failed\n");
+ return rc;
+ }
+
+ rc = qpnp_adc_tm_write_reg(chip,
+ QPNP_BTM_Mn_HIGH_THR1(btm_chan_idx),
+ QPNP_ADC_TM_THR_MSB_MASK(high_thr), 1);
+ if (rc < 0)
+ pr_err("high threshold msb setting failed\n");
+
+ }
pr_debug("client requested high:%d and low:%d\n",
- high_thr, low_thr);
+ high_thr, low_thr);
return rc;
}
@@ -734,9 +1213,14 @@
pr_debug("low sensor mask:%x with state:%d\n",
sensor_mask, chan_prop->state_request);
/* Enable low threshold's interrupt */
- rc = qpnp_adc_tm_reg_update(chip,
- QPNP_BTM_Mn_EN(btm_chan_idx),
- QPNP_BTM_Mn_LOW_THR_INT_EN, true);
+ if (!chip->adc_tm_hc)
+ rc = qpnp_adc_tm_reg_update(chip,
+ QPNP_ADC_TM_LOW_THR_INT_EN,
+ sensor_mask, true);
+ else
+ rc = qpnp_adc_tm_reg_update(chip,
+ QPNP_BTM_Mn_EN(btm_chan_idx),
+ QPNP_BTM_Mn_LOW_THR_INT_EN, true);
if (rc < 0) {
pr_err("low thr enable err:%d\n", btm_chan);
return rc;
@@ -746,9 +1230,14 @@
if (high_thr_set) {
/* Enable high threshold's interrupt */
pr_debug("high sensor mask:%x\n", sensor_mask);
- rc = qpnp_adc_tm_reg_update(chip,
- QPNP_BTM_Mn_EN(btm_chan_idx),
- QPNP_BTM_Mn_HIGH_THR_INT_EN, true);
+ if (!chip->adc_tm_hc)
+ rc = qpnp_adc_tm_reg_update(chip,
+ QPNP_ADC_TM_HIGH_THR_INT_EN,
+ sensor_mask, true);
+ else
+ rc = qpnp_adc_tm_reg_update(chip,
+ QPNP_BTM_Mn_EN(btm_chan_idx),
+ QPNP_BTM_Mn_HIGH_THR_INT_EN, true);
if (rc < 0) {
pr_err("high thr enable err:%d\n", btm_chan);
return rc;
@@ -757,13 +1246,16 @@
}
/* Enable corresponding BTM channel measurement */
- rc = qpnp_adc_tm_reg_update(chip, QPNP_BTM_Mn_EN(btm_chan_idx),
+ if (!chip->adc_tm_hc)
+ rc = qpnp_adc_tm_reg_update(chip,
+ QPNP_ADC_TM_MULTI_MEAS_EN, sensor_mask, true);
+ else
+ rc = qpnp_adc_tm_reg_update(chip, QPNP_BTM_Mn_EN(btm_chan_idx),
QPNP_BTM_Mn_MEAS_EN, true);
if (rc < 0) {
pr_err("multi measurement en failed\n");
return rc;
}
-
return rc;
}
@@ -872,12 +1364,120 @@
return 0;
}
+static int32_t qpnp_adc_tm_configure(struct qpnp_adc_tm_chip *chip,
+ struct qpnp_adc_amux_properties *chan_prop)
+{
+ u8 decimation = 0, op_cntrl = 0, mode_ctl = 0;
+ int rc = 0;
+ uint32_t btm_chan = 0;
+
+ /* Set measurement in single measurement mode */
+ mode_ctl = ADC_OP_NORMAL_MODE << QPNP_OP_MODE_SHIFT;
+ rc = qpnp_adc_tm_mode_select(chip, mode_ctl);
+ if (rc < 0) {
+ pr_err("adc-tm single mode select failed\n");
+ return rc;
+ }
+
+ /* Disable bank */
+ rc = qpnp_adc_tm_disable(chip);
+ if (rc)
+ return rc;
+
+ /* Check if a conversion is in progress */
+ rc = qpnp_adc_tm_req_sts_check(chip);
+ if (rc < 0) {
+ pr_err("adc-tm req_sts check failed\n");
+ return rc;
+ }
+
+ /* Configure AMUX channel select for the corresponding BTM channel*/
+ btm_chan = chan_prop->chan_prop->tm_channel_select;
+ rc = qpnp_adc_tm_write_reg(chip, btm_chan, chan_prop->amux_channel, 1);
+ if (rc < 0) {
+ pr_err("adc-tm channel selection err\n");
+ return rc;
+ }
+
+ /* Digital parameter setup */
+ decimation |= chan_prop->decimation <<
+ QPNP_ADC_DIG_DEC_RATIO_SEL_SHIFT;
+ rc = qpnp_adc_tm_write_reg(chip, QPNP_ADC_DIG_PARAM, decimation, 1);
+ if (rc < 0) {
+ pr_err("adc-tm digital parameter setup err\n");
+ return rc;
+ }
+
+ /* Hardware setting time */
+ rc = qpnp_adc_tm_write_reg(chip, QPNP_HW_SETTLE_DELAY,
+ chan_prop->hw_settle_time, 1);
+ if (rc < 0) {
+ pr_err("adc-tm hw settling time setup err\n");
+ return rc;
+ }
+
+ /* Fast averaging setup/enable */
+ rc = qpnp_adc_tm_fast_avg_en(chip, &chan_prop->fast_avg_setup);
+ if (rc < 0) {
+ pr_err("adc-tm fast-avg enable err\n");
+ return rc;
+ }
+
+ rc = qpnp_adc_tm_write_reg(chip, QPNP_FAST_AVG_CTL,
+ chan_prop->fast_avg_setup, 1);
+ if (rc < 0) {
+ pr_err("adc-tm fast-avg setup err\n");
+ return rc;
+ }
+
+ /* Measurement interval setup */
+ rc = qpnp_adc_tm_timer_interval_select(chip, btm_chan,
+ chan_prop->chan_prop);
+ if (rc < 0) {
+ pr_err("adc-tm timer select failed\n");
+ return rc;
+ }
+
+ /* Channel configuration setup */
+ rc = qpnp_adc_tm_channel_configure(chip, btm_chan,
+ chan_prop->chan_prop, chan_prop->amux_channel);
+ if (rc < 0) {
+ pr_err("adc-tm channel configure failed\n");
+ return rc;
+ }
+
+ /* Recurring interval measurement enable */
+ rc = qpnp_adc_tm_read_reg(chip, QPNP_ADC_MEAS_INTERVAL_OP_CTL,
+ &op_cntrl, 1);
+ op_cntrl |= QPNP_ADC_MEAS_INTERVAL_OP;
+ rc = qpnp_adc_tm_reg_update(chip, QPNP_ADC_MEAS_INTERVAL_OP_CTL,
+ op_cntrl, true);
+ if (rc < 0) {
+ pr_err("adc-tm meas interval op configure failed\n");
+ return rc;
+ }
+
+ /* Enable bank */
+ rc = qpnp_adc_tm_enable(chip);
+ if (rc)
+ return rc;
+
+ /* Request conversion */
+ rc = qpnp_adc_tm_write_reg(chip, QPNP_CONV_REQ, QPNP_CONV_REQ_SET, 1);
+ if (rc < 0) {
+ pr_err("adc-tm request conversion failed\n");
+ return rc;
+ }
+
+ return 0;
+}
+
static int qpnp_adc_tm_set_mode(struct qpnp_adc_tm_sensor *adc_tm,
enum thermal_device_mode mode)
{
struct qpnp_adc_tm_chip *chip = adc_tm->chip;
int rc = 0, channel;
- u8 sensor_mask = 0;
+ u8 sensor_mask = 0, mode_ctl = 0;
uint32_t btm_chan_idx = 0, btm_chan = 0;
if (qpnp_adc_tm_is_valid(chip)) {
@@ -916,13 +1516,30 @@
chip->adc->amux_prop->calib_type =
chip->adc->adc_channels[channel].calib_type;
- rc = qpnp_adc_tm_hc_configure(chip, chip->adc->amux_prop);
- if (rc) {
- pr_err("hc configure failed with %d\n", rc);
- goto fail;
+ if (!chip->adc_tm_hc) {
+ rc = qpnp_adc_tm_configure(chip, chip->adc->amux_prop);
+ if (rc) {
+ pr_err("adc-tm configure failed with %d\n", rc);
+ goto fail;
+ }
+ } else {
+ rc = qpnp_adc_tm_hc_configure(chip,
+ chip->adc->amux_prop);
+ if (rc) {
+ pr_err("hc configure failed with %d\n", rc);
+ goto fail;
+ }
}
} else if (mode == THERMAL_DEVICE_DISABLED) {
sensor_mask = 1 << adc_tm->sensor_num;
+ if (!chip->adc_tm_hc) {
+ mode_ctl = ADC_OP_NORMAL_MODE << QPNP_OP_MODE_SHIFT;
+ rc = qpnp_adc_tm_mode_select(chip, mode_ctl);
+ if (rc < 0) {
+ pr_err("adc-tm single mode select failed\n");
+ goto fail;
+ }
+ }
/* Disable bank */
rc = qpnp_adc_tm_disable(chip);
@@ -931,12 +1548,28 @@
goto fail;
}
- rc = qpnp_adc_tm_reg_update(chip,
- QPNP_BTM_Mn_EN(btm_chan_idx),
- QPNP_BTM_Mn_MEAS_EN, false);
- if (rc < 0) {
- pr_err("multi measurement disable failed\n");
- goto fail;
+ if (!chip->adc_tm_hc) {
+ /* Check if a conversion is in progress */
+ rc = qpnp_adc_tm_req_sts_check(chip);
+ if (rc < 0) {
+ pr_err("adc-tm req_sts check failed\n");
+ goto fail;
+ }
+ rc = qpnp_adc_tm_reg_update(chip,
+ QPNP_ADC_TM_MULTI_MEAS_EN,
+ sensor_mask, false);
+ if (rc < 0) {
+ pr_err("multi measurement update failed\n");
+ goto fail;
+ }
+ } else {
+ rc = qpnp_adc_tm_reg_update(chip,
+ QPNP_BTM_Mn_EN(btm_chan_idx),
+ QPNP_BTM_Mn_MEAS_EN, false);
+ if (rc < 0) {
+ pr_err("multi measurement disable failed\n");
+ goto fail;
+ }
}
rc = qpnp_adc_tm_enable_if_channel_meas(chip);
@@ -959,6 +1592,7 @@
{
struct qpnp_adc_tm_chip *chip = adc_tm->chip;
int rc = 0, sensor_mask = 0;
+ u8 thr_int_en = 0;
bool state = false;
uint32_t btm_chan_idx = 0, btm_chan = 0;
@@ -986,17 +1620,29 @@
switch (trip) {
case ADC_TM_TRIP_HIGH_WARM:
/* low_thr (lower voltage) for higher temp */
- rc = qpnp_adc_tm_reg_update(chip,
- QPNP_BTM_Mn_EN(btm_chan_idx),
- QPNP_BTM_Mn_LOW_THR_INT_EN, state);
+ thr_int_en = adc_tm_data[btm_chan_idx].low_thr_int_chan_en;
+ if (!chip->adc_tm_hc)
+ rc = qpnp_adc_tm_reg_update(chip,
+ QPNP_ADC_TM_LOW_THR_INT_EN,
+ sensor_mask, state);
+ else
+ rc = qpnp_adc_tm_reg_update(chip,
+ QPNP_BTM_Mn_EN(btm_chan_idx),
+ QPNP_BTM_Mn_LOW_THR_INT_EN, state);
if (rc)
pr_err("channel:%x failed\n", btm_chan);
break;
case ADC_TM_TRIP_LOW_COOL:
/* high_thr (higher voltage) for cooler temp */
- rc = qpnp_adc_tm_reg_update(chip,
- QPNP_BTM_Mn_EN(btm_chan_idx),
- QPNP_BTM_Mn_HIGH_THR_INT_EN, state);
+ thr_int_en = adc_tm_data[btm_chan_idx].high_thr_int_chan_en;
+ if (!chip->adc_tm_hc)
+ rc = qpnp_adc_tm_reg_update(chip,
+ QPNP_ADC_TM_HIGH_THR_INT_EN,
+ sensor_mask, state);
+ else
+ rc = qpnp_adc_tm_reg_update(chip,
+ QPNP_BTM_Mn_EN(btm_chan_idx),
+ QPNP_BTM_Mn_HIGH_THR_INT_EN, state);
if (rc)
pr_err("channel:%x failed\n", btm_chan);
break;
@@ -1060,10 +1706,17 @@
return rc;
}
- reg_low_thr_lsb = QPNP_BTM_Mn_LOW_THR0(btm_chan_idx);
- reg_low_thr_msb = QPNP_BTM_Mn_LOW_THR1(btm_chan_idx);
- reg_high_thr_lsb = QPNP_BTM_Mn_HIGH_THR0(btm_chan_idx);
- reg_high_thr_msb = QPNP_BTM_Mn_HIGH_THR1(btm_chan_idx);
+ if (!chip->adc_tm_hc) {
+ reg_low_thr_lsb = adc_tm_data[btm_chan_idx].low_thr_lsb_addr;
+ reg_low_thr_msb = adc_tm_data[btm_chan_idx].low_thr_msb_addr;
+ reg_high_thr_lsb = adc_tm_data[btm_chan_idx].high_thr_lsb_addr;
+ reg_high_thr_msb = adc_tm_data[btm_chan_idx].high_thr_msb_addr;
+ } else {
+ reg_low_thr_lsb = QPNP_BTM_Mn_LOW_THR0(btm_chan_idx);
+ reg_low_thr_msb = QPNP_BTM_Mn_LOW_THR1(btm_chan_idx);
+ reg_high_thr_lsb = QPNP_BTM_Mn_HIGH_THR0(btm_chan_idx);
+ reg_high_thr_msb = QPNP_BTM_Mn_HIGH_THR1(btm_chan_idx);
+ }
if (high_temp != INT_MAX) {
rc = qpnp_adc_tm_write_reg(chip, reg_low_thr_lsb,
@@ -1222,6 +1875,144 @@
atomic_dec(&chip->wq_cnt);
}
+static int qpnp_adc_tm_recalib_request_check(struct qpnp_adc_tm_chip *chip,
+ int sensor_num, u8 status_high, u8 *notify_check)
+{
+ int rc = 0;
+ u8 sensor_mask = 0, mode_ctl = 0;
+ int32_t old_thr = 0, new_thr = 0;
+ uint32_t channel, btm_chan_num, scale_type;
+ struct qpnp_vadc_result result;
+ struct qpnp_adc_thr_client_info *client_info = NULL;
+ struct list_head *thr_list;
+ bool status = false;
+
+ if (!chip->adc_tm_recalib_check) {
+ *notify_check = 1;
+ return rc;
+ }
+
+ list_for_each(thr_list, &chip->sensor[sensor_num].thr_list) {
+ client_info = list_entry(thr_list,
+ struct qpnp_adc_thr_client_info, list);
+ channel = client_info->btm_param->channel;
+ btm_chan_num = chip->sensor[sensor_num].btm_channel_num;
+ sensor_mask = 1 << sensor_num;
+
+ rc = qpnp_vadc_read(chip->vadc_dev, channel, &result);
+ if (rc < 0) {
+ pr_err("failure to read vadc channel=%d\n",
+ client_info->btm_param->channel);
+ goto fail;
+ }
+ new_thr = result.physical;
+
+ if (status_high)
+ old_thr = client_info->btm_param->high_thr;
+ else
+ old_thr = client_info->btm_param->low_thr;
+
+ if (new_thr > old_thr)
+ status = (status_high) ? true : false;
+ else
+ status = (status_high) ? false : true;
+
+ pr_debug(
+ "recalib:sen=%d, new_thr=%d, new_thr_adc_code=0x%x, old_thr=%d status=%d valid_status=%d\n",
+ sensor_num, new_thr, result.adc_code,
+ old_thr, status_high, status);
+
+ rc = qpnp_adc_tm_read_thr_value(chip, btm_chan_num);
+ if (rc < 0) {
+ pr_err("adc-tm thresholds read failed\n");
+ goto fail;
+ }
+
+ if (status) {
+ *notify_check = 1;
+ pr_debug("Client can be notify\n");
+ return rc;
+ }
+
+ pr_debug("Client can not be notify, restart measurement\n");
+ /* Set measurement in single measurement mode */
+ mode_ctl = ADC_OP_NORMAL_MODE << QPNP_OP_MODE_SHIFT;
+ rc = qpnp_adc_tm_mode_select(chip, mode_ctl);
+ if (rc < 0) {
+ pr_err("adc-tm single mode select failed\n");
+ goto fail;
+ }
+
+ /* Disable bank */
+ rc = qpnp_adc_tm_disable(chip);
+ if (rc < 0) {
+ pr_err("adc-tm disable failed\n");
+ goto fail;
+ }
+
+ /* Check if a conversion is in progress */
+ rc = qpnp_adc_tm_req_sts_check(chip);
+ if (rc < 0) {
+ pr_err("adc-tm req_sts check failed\n");
+ goto fail;
+ }
+
+ rc = qpnp_adc_tm_reg_update(chip, QPNP_ADC_TM_LOW_THR_INT_EN,
+ sensor_mask, false);
+ if (rc < 0) {
+ pr_err("low threshold int write failed\n");
+ goto fail;
+ }
+
+ rc = qpnp_adc_tm_reg_update(chip, QPNP_ADC_TM_HIGH_THR_INT_EN,
+ sensor_mask, false);
+ if (rc < 0) {
+ pr_err("high threshold int enable failed\n");
+ goto fail;
+ }
+
+ rc = qpnp_adc_tm_reg_update(chip, QPNP_ADC_TM_MULTI_MEAS_EN,
+ sensor_mask, false);
+ if (rc < 0) {
+ pr_err("multi measurement en failed\n");
+ goto fail;
+ }
+
+ /* restart measurement */
+ scale_type = chip->sensor[sensor_num].scale_type;
+ chip->adc->amux_prop->amux_channel = channel;
+ chip->adc->amux_prop->decimation =
+ chip->adc->adc_channels[sensor_num].adc_decimation;
+ chip->adc->amux_prop->hw_settle_time =
+ chip->adc->adc_channels[sensor_num].hw_settle_time;
+ chip->adc->amux_prop->fast_avg_setup =
+ chip->adc->adc_channels[sensor_num].fast_avg_setup;
+ chip->adc->amux_prop->mode_sel =
+ ADC_OP_MEASUREMENT_INTERVAL << QPNP_OP_MODE_SHIFT;
+ adc_tm_rscale_fn[scale_type].chan(chip->vadc_dev,
+ client_info->btm_param,
+ &chip->adc->amux_prop->chan_prop->low_thr,
+ &chip->adc->amux_prop->chan_prop->high_thr);
+ qpnp_adc_tm_add_to_list(chip, sensor_num,
+ client_info->btm_param,
+ chip->adc->amux_prop->chan_prop);
+ chip->adc->amux_prop->chan_prop->tm_channel_select =
+ chip->sensor[sensor_num].btm_channel_num;
+ chip->adc->amux_prop->chan_prop->state_request =
+ client_info->btm_param->state_request;
+
+ rc = qpnp_adc_tm_configure(chip, chip->adc->amux_prop);
+ if (rc) {
+ pr_err("adc-tm configure failed with %d\n", rc);
+ goto fail;
+ }
+ *notify_check = 0;
+ pr_debug("BTM channel reconfigured for measuremnt\n");
+ }
+fail:
+ return rc;
+}
+
static int qpnp_adc_tm_disable_rearm_high_thresholds(
struct qpnp_adc_tm_chip *chip, int sensor_num)
{
@@ -1229,7 +2020,7 @@
struct qpnp_adc_thr_client_info *client_info = NULL;
struct list_head *thr_list;
uint32_t btm_chan_num = 0, btm_chan_idx = 0;
- u8 sensor_mask = 0;
+ u8 sensor_mask = 0, notify_check = 0;
int rc = 0;
btm_chan_num = chip->sensor[sensor_num].btm_channel_num;
@@ -1250,14 +2041,24 @@
*/
sensor_mask = 1 << sensor_num;
pr_debug("non thermal node - mask:%x\n", sensor_mask);
- rc = qpnp_adc_tm_reg_update(chip,
- QPNP_BTM_Mn_EN(btm_chan_idx),
- QPNP_BTM_Mn_HIGH_THR_INT_EN, false);
- if (rc < 0) {
- pr_err("high threshold int update failed\n");
- return rc;
+ if (!chip->adc_tm_hc) {
+ rc = qpnp_adc_tm_recalib_request_check(chip,
+ sensor_num, true, ¬ify_check);
+ if (rc < 0 || !notify_check) {
+ pr_debug("Calib recheck re-armed rc=%d\n", rc);
+ chip->th_info.adc_tm_high_enable = 0;
+ return rc;
+ }
+ } else {
+ rc = qpnp_adc_tm_reg_update(chip,
+ QPNP_BTM_Mn_EN(btm_chan_idx),
+ QPNP_BTM_Mn_HIGH_THR_INT_EN, false);
+ if (rc < 0) {
+ pr_err("high threshold int update failed\n");
+ return rc;
+ }
}
- } else {
+ } else {
/*
* Uses the thermal sysfs registered device to disable
* the corresponding high voltage threshold which
@@ -1291,12 +2092,22 @@
}
qpnp_adc_tm_manage_thresholds(chip, sensor_num, btm_chan_num);
- rc = qpnp_adc_tm_reg_update(chip,
- QPNP_BTM_Mn_EN(sensor_num),
- QPNP_BTM_Mn_MEAS_EN, false);
- if (rc < 0) {
- pr_err("multi meas disable failed\n");
- return rc;
+ if (!chip->adc_tm_hc) {
+ rc = qpnp_adc_tm_reg_update(chip,
+ QPNP_ADC_TM_MULTI_MEAS_EN,
+ sensor_mask, false);
+ if (rc < 0) {
+ pr_err("multi meas disable failed\n");
+ return rc;
+ }
+ } else {
+ rc = qpnp_adc_tm_reg_update(chip,
+ QPNP_BTM_Mn_EN(sensor_num),
+ QPNP_BTM_Mn_MEAS_EN, false);
+ if (rc < 0) {
+ pr_err("multi meas disable failed\n");
+ return rc;
+ }
}
rc = qpnp_adc_tm_enable_if_channel_meas(chip);
@@ -1320,7 +2131,7 @@
struct qpnp_adc_thr_client_info *client_info = NULL;
struct list_head *thr_list;
uint32_t btm_chan_num = 0, btm_chan_idx = 0;
- u8 sensor_mask = 0;
+ u8 sensor_mask = 0, notify_check = 0;
int rc = 0;
btm_chan_num = chip->sensor[sensor_num].btm_channel_num;
@@ -1341,12 +2152,22 @@
*/
sensor_mask = 1 << sensor_num;
pr_debug("non thermal node - mask:%x\n", sensor_mask);
- rc = qpnp_adc_tm_reg_update(chip,
- QPNP_BTM_Mn_EN(btm_chan_idx),
- QPNP_BTM_Mn_LOW_THR_INT_EN, false);
- if (rc < 0) {
- pr_err("low threshold int update failed\n");
- return rc;
+ if (!chip->adc_tm_hc) {
+ rc = qpnp_adc_tm_recalib_request_check(chip,
+ sensor_num, false, ¬ify_check);
+ if (rc < 0 || !notify_check) {
+ pr_debug("Calib recheck re-armed rc=%d\n", rc);
+ chip->th_info.adc_tm_low_enable = 0;
+ return rc;
+ }
+ } else {
+ rc = qpnp_adc_tm_reg_update(chip,
+ QPNP_BTM_Mn_EN(btm_chan_idx),
+ QPNP_BTM_Mn_LOW_THR_INT_EN, false);
+ if (rc < 0) {
+ pr_err("low threshold int update failed\n");
+ return rc;
+ }
}
} else {
/*
@@ -1382,12 +2203,22 @@
}
qpnp_adc_tm_manage_thresholds(chip, sensor_num, btm_chan_num);
- rc = qpnp_adc_tm_reg_update(chip,
- QPNP_BTM_Mn_EN(sensor_num),
- QPNP_BTM_Mn_MEAS_EN, false);
- if (rc < 0) {
- pr_err("multi meas disable failed\n");
- return rc;
+ if (!chip->adc_tm_hc) {
+ rc = qpnp_adc_tm_reg_update(chip,
+ QPNP_ADC_TM_MULTI_MEAS_EN,
+ sensor_mask, false);
+ if (rc < 0) {
+ pr_err("multi meas disable failed\n");
+ return rc;
+ }
+ } else {
+ rc = qpnp_adc_tm_reg_update(chip,
+ QPNP_BTM_Mn_EN(sensor_num),
+ QPNP_BTM_Mn_MEAS_EN, false);
+ if (rc < 0) {
+ pr_err("multi meas disable failed\n");
+ return rc;
+ }
}
rc = qpnp_adc_tm_enable_if_channel_meas(chip);
@@ -1416,6 +2247,13 @@
mutex_lock(&chip->adc->adc_lock);
+ if (!chip->adc_tm_hc) {
+ rc = qpnp_adc_tm_req_sts_check(chip);
+ if (rc) {
+ pr_err("adc-tm-tm req sts check failed with %d\n", rc);
+ goto fail;
+ }
+ }
while (sensor_num < chip->max_channels_available) {
if (chip->sensor[sensor_num].high_thr_triggered) {
rc = qpnp_adc_tm_disable_rearm_high_thresholds(
@@ -1473,6 +2311,92 @@
pr_err("adc-tm high thr work failed\n");
}
+static irqreturn_t qpnp_adc_tm_high_thr_isr(int irq, void *data)
+{
+ struct qpnp_adc_tm_chip *chip = data;
+ u8 mode_ctl = 0, status1 = 0, sensor_mask = 0;
+ int rc = 0, sensor_notify_num = 0, i = 0, sensor_num = 0;
+
+ mode_ctl = ADC_OP_NORMAL_MODE << QPNP_OP_MODE_SHIFT;
+ /* Set measurement in single measurement mode */
+ qpnp_adc_tm_mode_select(chip, mode_ctl);
+
+ qpnp_adc_tm_disable(chip);
+
+ rc = qpnp_adc_tm_read_reg(chip, QPNP_ADC_TM_STATUS1, &status1, 1);
+ if (rc) {
+ pr_err("adc-tm read status1 failed\n");
+ return IRQ_HANDLED;
+ }
+
+ rc = qpnp_adc_tm_read_reg(chip, QPNP_ADC_TM_STATUS_HIGH,
+ &chip->th_info.status_high, 1);
+ if (rc) {
+ pr_err("adc-tm-tm read status high failed with %d\n", rc);
+ return IRQ_HANDLED;
+ }
+
+ rc = qpnp_adc_tm_read_reg(chip, QPNP_ADC_TM_HIGH_THR_INT_EN,
+ &chip->th_info.adc_tm_high_thr_set, 1);
+ if (rc) {
+ pr_err("adc-tm-tm read high thr failed with %d\n", rc);
+ return IRQ_HANDLED;
+ }
+
+ /* Check which interrupt threshold is lower and measure against the
+ * enabled channel
+ */
+ rc = qpnp_adc_tm_read_reg(chip, QPNP_ADC_TM_MULTI_MEAS_EN,
+ &chip->th_info.qpnp_adc_tm_meas_en, 1);
+ if (rc) {
+ pr_err("adc-tm-tm read status high failed with %d\n", rc);
+ return IRQ_HANDLED;
+ }
+
+ chip->th_info.adc_tm_high_enable = chip->th_info.qpnp_adc_tm_meas_en &
+ chip->th_info.status_high;
+ chip->th_info.adc_tm_high_enable &= chip->th_info.adc_tm_high_thr_set;
+
+ sensor_notify_num = chip->th_info.adc_tm_high_enable;
+ while (i < chip->max_channels_available) {
+ if ((sensor_notify_num & 0x1) == 1)
+ sensor_num = i;
+ sensor_notify_num >>= 1;
+ i++;
+ }
+
+ if (!chip->sensor[sensor_num].thermal_node) {
+ sensor_mask = 1 << sensor_num;
+ rc = qpnp_adc_tm_reg_update(chip,
+ QPNP_ADC_TM_HIGH_THR_INT_EN,
+ sensor_mask, false);
+ if (rc < 0) {
+ pr_err("high threshold int read failed\n");
+ return IRQ_HANDLED;
+ }
+ } else {
+ /*
+ * Uses the thermal sysfs registered device to disable
+ * the corresponding high voltage threshold which
+ * is triggered by low temp
+ */
+ pr_debug("thermal node with mask:%x\n", sensor_mask);
+ rc = qpnp_adc_tm_activate_trip_type(
+ &chip->sensor[sensor_num],
+ ADC_TM_TRIP_LOW_COOL,
+ THERMAL_TRIP_ACTIVATION_DISABLED);
+ if (rc < 0) {
+ pr_err("notify error:%d\n", sensor_num);
+ return IRQ_HANDLED;
+ }
+ }
+
+ atomic_inc(&chip->wq_cnt);
+ queue_work(chip->high_thr_wq, &chip->trigger_high_thr_work);
+
+ return IRQ_HANDLED;
+}
+
static void qpnp_adc_tm_low_thr_work(struct work_struct *work)
{
struct qpnp_adc_tm_chip *chip = container_of(work,
@@ -1493,6 +2417,89 @@
pr_err("adc-tm low thr work failed\n");
}
+static irqreturn_t qpnp_adc_tm_low_thr_isr(int irq, void *data)
+{
+ struct qpnp_adc_tm_chip *chip = data;
+ u8 mode_ctl = 0, status1 = 0, sensor_mask = 0;
+ int rc = 0, sensor_notify_num = 0, i = 0, sensor_num = 0;
+
+ mode_ctl = ADC_OP_NORMAL_MODE << QPNP_OP_MODE_SHIFT;
+ /* Set measurement in single measurement mode */
+ qpnp_adc_tm_mode_select(chip, mode_ctl);
+
+ qpnp_adc_tm_disable(chip);
+
+ rc = qpnp_adc_tm_read_reg(chip, QPNP_ADC_TM_STATUS1, &status1, 1);
+ if (rc) {
+ pr_err("adc-tm read status1 failed\n");
+ return IRQ_HANDLED;
+ }
+
+ rc = qpnp_adc_tm_read_reg(chip, QPNP_ADC_TM_STATUS_LOW,
+ &chip->th_info.status_low, 1);
+ if (rc) {
+ pr_err("adc-tm-tm read status low failed with %d\n", rc);
+ return IRQ_HANDLED;
+ }
+
+ rc = qpnp_adc_tm_read_reg(chip, QPNP_ADC_TM_LOW_THR_INT_EN,
+ &chip->th_info.adc_tm_low_thr_set, 1);
+ if (rc) {
+ pr_err("adc-tm-tm read low thr failed with %d\n", rc);
+ return IRQ_HANDLED;
+ }
+
+ rc = qpnp_adc_tm_read_reg(chip, QPNP_ADC_TM_MULTI_MEAS_EN,
+ &chip->th_info.qpnp_adc_tm_meas_en, 1);
+ if (rc) {
+ pr_err("adc-tm-tm read status high failed with %d\n", rc);
+ return IRQ_HANDLED;
+ }
+
+ chip->th_info.adc_tm_low_enable = chip->th_info.qpnp_adc_tm_meas_en &
+ chip->th_info.status_low;
+ chip->th_info.adc_tm_low_enable &= chip->th_info.adc_tm_low_thr_set;
+
+ sensor_notify_num = chip->th_info.adc_tm_low_enable;
+ while (i < chip->max_channels_available) {
+ if ((sensor_notify_num & 0x1) == 1)
+ sensor_num = i;
+ sensor_notify_num >>= 1;
+ i++;
+ }
+
+ if (!chip->sensor[sensor_num].thermal_node) {
+ sensor_mask = 1 << sensor_num;
+ rc = qpnp_adc_tm_reg_update(chip,
+ QPNP_ADC_TM_LOW_THR_INT_EN,
+ sensor_mask, false);
+ if (rc < 0) {
+ pr_err("low threshold int read failed\n");
+ return IRQ_HANDLED;
+ }
+ } else {
+ /*
+ * Uses the thermal sysfs registered device to disable
+ * the corresponding low voltage threshold which
+ * is triggered by high temp
+ */
+ pr_debug("thermal node with mask:%x\n", sensor_mask);
+ rc = qpnp_adc_tm_activate_trip_type(
+ &chip->sensor[sensor_num],
+ ADC_TM_TRIP_HIGH_WARM,
+ THERMAL_TRIP_ACTIVATION_DISABLED);
+ if (rc < 0) {
+ pr_err("notify error:%d\n", sensor_num);
+ return IRQ_HANDLED;
+ }
+ }
+
+ atomic_inc(&chip->wq_cnt);
+ queue_work(chip->low_thr_wq, &chip->trigger_low_thr_work);
+
+ return IRQ_HANDLED;
+}
+
static int qpnp_adc_tm_rc_check_sensor_trip(struct qpnp_adc_tm_chip *chip,
u8 status_low, u8 status_high, int i,
int *sensor_low_notify_num, int *sensor_high_notify_num)
@@ -1743,11 +2750,18 @@
param->state_request;
chip->adc->amux_prop->calib_type =
chip->adc->adc_channels[dt_index].calib_type;
-
- rc = qpnp_adc_tm_hc_configure(chip, chip->adc->amux_prop);
- if (rc) {
- pr_err("adc-tm hc configure failed with %d\n", rc);
- goto fail_unlock;
+ if (!chip->adc_tm_hc) {
+ rc = qpnp_adc_tm_configure(chip, chip->adc->amux_prop);
+ if (rc) {
+ pr_err("adc-tm configure failed with %d\n", rc);
+ goto fail_unlock;
+ }
+ } else {
+ rc = qpnp_adc_tm_hc_configure(chip, chip->adc->amux_prop);
+ if (rc) {
+ pr_err("adc-tm hc configure failed with %d\n", rc);
+ goto fail_unlock;
+ }
}
chip->sensor[dt_index].scale_type = scale_type;
@@ -1763,6 +2777,7 @@
struct qpnp_adc_tm_btm_param *param)
{
uint32_t channel, dt_index = 0, btm_chan_num;
+ u8 sensor_mask = 0, mode_ctl = 0;
int rc = 0;
if (qpnp_adc_tm_is_valid(chip))
@@ -1770,6 +2785,16 @@
mutex_lock(&chip->adc->adc_lock);
+ if (!chip->adc_tm_hc) {
+ /* Set measurement in single measurement mode */
+ mode_ctl = ADC_OP_NORMAL_MODE << QPNP_OP_MODE_SHIFT;
+ rc = qpnp_adc_tm_mode_select(chip, mode_ctl);
+ if (rc < 0) {
+ pr_err("adc-tm single mode select failed\n");
+ goto fail;
+ }
+ }
+
/* Disable bank */
rc = qpnp_adc_tm_disable(chip);
if (rc < 0) {
@@ -1777,6 +2802,15 @@
goto fail;
}
+ if (!chip->adc_tm_hc) {
+ /* Check if a conversion is in progress */
+ rc = qpnp_adc_tm_req_sts_check(chip);
+ if (rc < 0) {
+ pr_err("adc-tm req_sts check failed\n");
+ goto fail;
+ }
+ }
+
channel = param->channel;
while ((chip->adc->adc_channels[dt_index].channel_num
!= channel) && (dt_index < chip->max_channels_available))
@@ -1790,25 +2824,43 @@
btm_chan_num = chip->sensor[dt_index].btm_channel_num;
- rc = qpnp_adc_tm_reg_update(chip, QPNP_BTM_Mn_EN(btm_chan_num),
- QPNP_BTM_Mn_HIGH_THR_INT_EN, false);
- if (rc < 0) {
- pr_err("high thr disable err:%d\n", btm_chan_num);
- return rc;
- }
+ if (!chip->adc_tm_hc) {
+ sensor_mask = 1 << chip->sensor[dt_index].sensor_num;
- rc = qpnp_adc_tm_reg_update(chip, QPNP_BTM_Mn_EN(btm_chan_num),
+ rc = qpnp_adc_tm_reg_update(chip, QPNP_ADC_TM_LOW_THR_INT_EN,
+ sensor_mask, false);
+ if (rc < 0) {
+ pr_err("high threshold int enable failed\n");
+ goto fail;
+ }
+
+ rc = qpnp_adc_tm_reg_update(chip, QPNP_ADC_TM_MULTI_MEAS_EN,
+ sensor_mask, false);
+ if (rc < 0) {
+ pr_err("multi measurement en failed\n");
+ goto fail;
+ }
+ } else {
+ rc = qpnp_adc_tm_reg_update(chip, QPNP_BTM_Mn_EN(btm_chan_num),
+ QPNP_BTM_Mn_HIGH_THR_INT_EN, false);
+ if (rc < 0) {
+ pr_err("high thr disable err:%d\n", btm_chan_num);
+ return rc;
+ }
+
+ rc = qpnp_adc_tm_reg_update(chip, QPNP_BTM_Mn_EN(btm_chan_num),
QPNP_BTM_Mn_LOW_THR_INT_EN, false);
- if (rc < 0) {
- pr_err("low thr disable err:%d\n", btm_chan_num);
- return rc;
- }
+ if (rc < 0) {
+ pr_err("low thr disable err:%d\n", btm_chan_num);
+ return rc;
+ }
- rc = qpnp_adc_tm_reg_update(chip, QPNP_BTM_Mn_EN(btm_chan_num),
+ rc = qpnp_adc_tm_reg_update(chip, QPNP_BTM_Mn_EN(btm_chan_num),
QPNP_BTM_Mn_MEAS_EN, false);
- if (rc < 0) {
- pr_err("multi measurement disable failed\n");
- return rc;
+ if (rc < 0) {
+ pr_err("multi measurement disable failed\n");
+ return rc;
+ }
}
rc = qpnp_adc_tm_enable_if_channel_meas(chip);
@@ -1842,6 +2894,35 @@
}
EXPORT_SYMBOL(qpnp_get_adc_tm);
+static int qpnp_adc_tm_initial_setup(struct qpnp_adc_tm_chip *chip)
+{
+ u8 thr_init = 0;
+ int rc = 0;
+
+ rc = qpnp_adc_tm_write_reg(chip, QPNP_ADC_TM_HIGH_THR_INT_EN,
+ thr_init, 1);
+ if (rc < 0) {
+ pr_err("high thr init failed\n");
+ return rc;
+ }
+
+ rc = qpnp_adc_tm_write_reg(chip, QPNP_ADC_TM_LOW_THR_INT_EN,
+ thr_init, 1);
+ if (rc < 0) {
+ pr_err("low thr init failed\n");
+ return rc;
+ }
+
+ rc = qpnp_adc_tm_write_reg(chip, QPNP_ADC_TM_MULTI_MEAS_EN,
+ thr_init, 1);
+ if (rc < 0) {
+ pr_err("multi meas en failed\n");
+ return rc;
+ }
+
+ return rc;
+}
+
static const struct of_device_id qpnp_adc_tm_match_table[] = {
{ .compatible = "qcom,qpnp-adc-tm" },
{ .compatible = "qcom,qpnp-adc-tm-hc" },
@@ -1894,8 +2975,10 @@
goto fail;
}
- chip->adc_tm_hc = true;
- chip->adc->adc_hc = true;
+ if (of_device_is_compatible(node, "qcom,qpnp-adc-tm-hc")) {
+ chip->adc_tm_hc = true;
+ chip->adc->adc_hc = true;
+ }
rc = qpnp_adc_get_devicetree_data(pdev, chip->adc);
if (rc) {
@@ -1904,6 +2987,24 @@
}
mutex_init(&chip->adc->adc_lock);
+ /* Register the ADC peripheral interrupt */
+ if (!chip->adc_tm_hc) {
+ chip->adc->adc_high_thr_irq = platform_get_irq_byname(pdev,
+ "high-thr-en-set");
+ if (chip->adc->adc_high_thr_irq < 0) {
+ pr_err("Invalid irq\n");
+ rc = -ENXIO;
+ goto fail;
+ }
+
+ chip->adc->adc_low_thr_irq = platform_get_irq_byname(pdev,
+ "low-thr-en-set");
+ if (chip->adc->adc_low_thr_irq < 0) {
+ pr_err("Invalid irq\n");
+ rc = -ENXIO;
+ goto fail;
+ }
+ }
chip->vadc_dev = qpnp_get_vadc(&pdev->dev, "adc_tm");
if (IS_ERR(chip->vadc_dev)) {
rc = PTR_ERR(chip->vadc_dev);
@@ -2016,17 +3117,45 @@
INIT_WORK(&chip->trigger_low_thr_work, qpnp_adc_tm_low_thr_work);
atomic_set(&chip->wq_cnt, 0);
- rc = devm_request_irq(&pdev->dev, chip->adc->adc_irq_eoc,
- qpnp_adc_tm_rc_thr_isr,
- IRQF_TRIGGER_HIGH, "qpnp_adc_tm_interrupt", chip);
- if (rc)
- dev_err(&pdev->dev, "failed to request adc irq\n");
- else
- enable_irq_wake(chip->adc->adc_irq_eoc);
+ if (!chip->adc_tm_hc) {
+ rc = qpnp_adc_tm_initial_setup(chip);
+ if (rc)
+ goto fail;
+ rc = devm_request_irq(&pdev->dev, chip->adc->adc_high_thr_irq,
+ qpnp_adc_tm_high_thr_isr,
+ IRQF_TRIGGER_RISING, "qpnp_adc_tm_high_interrupt", chip);
+ if (rc) {
+ dev_err(&pdev->dev, "failed to request adc irq\n");
+ goto fail;
+ } else {
+ enable_irq_wake(chip->adc->adc_high_thr_irq);
+ }
+
+ rc = devm_request_irq(&pdev->dev, chip->adc->adc_low_thr_irq,
+ qpnp_adc_tm_low_thr_isr,
+ IRQF_TRIGGER_RISING,
+ "qpnp_adc_tm_low_interrupt", chip);
+ if (rc) {
+ dev_err(&pdev->dev, "failed to request adc irq\n");
+ goto fail;
+ } else {
+ enable_irq_wake(chip->adc->adc_low_thr_irq);
+ }
+ } else {
+ rc = devm_request_irq(&pdev->dev, chip->adc->adc_irq_eoc,
+ qpnp_adc_tm_rc_thr_isr,
+ IRQF_TRIGGER_HIGH, "qpnp_adc_tm_interrupt", chip);
+ if (rc)
+ dev_err(&pdev->dev, "failed to request adc irq\n");
+ else
+ enable_irq_wake(chip->adc->adc_irq_eoc);
+ }
chip->adc_vote_enable = false;
dev_set_drvdata(&pdev->dev, chip);
list_add(&chip->list, &qpnp_adc_tm_device_list);
+ spin_lock_init(&chip->th_info.adc_tm_low_lock);
+ spin_lock_init(&chip->th_info.adc_tm_high_lock);
pr_debug("OK\n");
return 0;
diff --git a/drivers/thermal/tsens1xxx.c b/drivers/thermal/tsens1xxx.c
index e2fad32..0ea4a46 100644
--- a/drivers/thermal/tsens1xxx.c
+++ b/drivers/thermal/tsens1xxx.c
@@ -53,6 +53,7 @@
#define TSENS_TRDY_RDY_MAX_TIME 2100
#define TSENS_THRESHOLD_MAX_CODE 0x3ff
#define TSENS_THRESHOLD_MIN_CODE 0x0
+#define TSENS_SCALE_MILLIDEG 1000
/* eeprom layout data for 8937 */
#define BASE0_MASK 0x000000ff
@@ -303,6 +304,7 @@
}
*temp = code_to_degc(last_temp, sensor);
+ *temp = *temp * TSENS_SCALE_MILLIDEG;
return 0;
}
@@ -386,6 +388,7 @@
spin_lock_irqsave(&tmdev->tsens_upp_low_lock, flags);
if (high_temp != INT_MAX) {
+ high_temp /= TSENS_SCALE_MILLIDEG;
high_code = degc_to_code(high_temp, tm_sensor);
tmdev->sensor[tm_sensor->hw_id].thr_state.high_adc_code =
high_code;
@@ -407,6 +410,7 @@
}
if (low_temp != INT_MIN) {
+ low_temp /= TSENS_SCALE_MILLIDEG;
low_code = degc_to_code(low_temp, tm_sensor);
tmdev->sensor[tm_sensor->hw_id].thr_state.low_adc_code =
low_code;
diff --git a/drivers/tty/serial/serial_core.c b/drivers/tty/serial/serial_core.c
index 2e2b88a..dce39de 100644
--- a/drivers/tty/serial/serial_core.c
+++ b/drivers/tty/serial/serial_core.c
@@ -968,6 +968,8 @@
}
} else {
retval = uart_startup(tty, state, 1);
+ if (retval == 0)
+ tty_port_set_initialized(port, true);
if (retval > 0)
retval = 0;
}
diff --git a/drivers/usb/gadget/composite.c b/drivers/usb/gadget/composite.c
index 0390c72..e85f24d 100644
--- a/drivers/usb/gadget/composite.c
+++ b/drivers/usb/gadget/composite.c
@@ -1748,7 +1748,9 @@
if (!gadget_is_dualspeed(gadget) ||
gadget->speed >= USB_SPEED_SUPER)
break;
+ spin_lock(&cdev->lock);
device_qual(cdev);
+ spin_unlock(&cdev->lock);
value = min_t(int, w_length,
sizeof(struct usb_qualifier_descriptor));
break;
@@ -1758,7 +1760,9 @@
break;
/* FALLTHROUGH */
case USB_DT_CONFIG:
+ spin_lock(&cdev->lock);
value = config_desc(cdev, w_value);
+ spin_unlock(&cdev->lock);
if (value >= 0)
value = min(w_length, (u16) value);
break;
diff --git a/drivers/usb/gadget/function/Makefile b/drivers/usb/gadget/function/Makefile
index 845c177..a9344a5 100644
--- a/drivers/usb/gadget/function/Makefile
+++ b/drivers/usb/gadget/function/Makefile
@@ -64,7 +64,7 @@
obj-$(CONFIG_USB_F_GSI) += usb_f_gsi.o
usb_f_qdss-y := f_qdss.o u_qdss.o
obj-$(CONFIG_USB_F_QDSS) += usb_f_qdss.o
-usb_f_qcrndis-y := f_qc_rndis.o rndis.o u_data_ipa.o
+usb_f_qcrndis-y := f_qc_rndis.o u_data_ipa.o
obj-$(CONFIG_USB_F_QCRNDIS) += usb_f_qcrndis.o
usb_f_rmnet_bam-y := f_rmnet.o u_ctrl_qti.o u_bam_dmux.o
obj-$(CONFIG_USB_F_RMNET_BAM) += usb_f_rmnet_bam.o
diff --git a/drivers/usb/gadget/function/f_gsi.c b/drivers/usb/gadget/function/f_gsi.c
index 41aa62d..e7f072a 100644
--- a/drivers/usb/gadget/function/f_gsi.c
+++ b/drivers/usb/gadget/function/f_gsi.c
@@ -532,6 +532,9 @@
if (!usb_gsi_ep_op(gsi->d_port.in_ep, (void *) &f_suspend,
GSI_EP_OP_CHECK_FOR_SUSPEND)) {
ret = -EFAULT;
+ block_db = false;
+ usb_gsi_ep_op(d_port->in_ep, (void *)&block_db,
+ GSI_EP_OP_SET_CLR_BLOCK_DBL);
goto done;
}
diff --git a/drivers/usb/gadget/function/f_rmnet.c b/drivers/usb/gadget/function/f_rmnet.c
index a86679e..47b87b0a 100644
--- a/drivers/usb/gadget/function/f_rmnet.c
+++ b/drivers/usb/gadget/function/f_rmnet.c
@@ -220,6 +220,14 @@
.bInterfaceProtocol = 0xff,
};
+static struct usb_endpoint_descriptor dpl_fs_data_desc = {
+ .bLength = USB_DT_ENDPOINT_SIZE,
+ .bDescriptorType = USB_DT_ENDPOINT,
+ .bEndpointAddress = USB_DIR_IN,
+ .bmAttributes = USB_ENDPOINT_XFER_BULK,
+ .wMaxPacketSize = cpu_to_le16(64),
+};
+
static struct usb_endpoint_descriptor dpl_hs_data_desc = {
.bLength = USB_DT_ENDPOINT_SIZE,
.bDescriptorType = USB_DT_ENDPOINT,
@@ -244,6 +252,12 @@
.wBytesPerInterval = 0,
};
+static struct usb_descriptor_header *dpl_fs_data_only_desc[] = {
+ (struct usb_descriptor_header *) &dpl_data_intf_desc,
+ (struct usb_descriptor_header *) &dpl_fs_data_desc,
+ NULL,
+};
+
static struct usb_descriptor_header *dpl_hs_data_only_desc[] = {
(struct usb_descriptor_header *) &dpl_data_intf_desc,
(struct usb_descriptor_header *) &dpl_hs_data_desc,
@@ -1119,10 +1133,10 @@
} else {
info.string_defs = dpl_string_defs;
info.data_desc = &dpl_data_intf_desc;
- info.fs_in_desc = &dpl_hs_data_desc;
+ info.fs_in_desc = &dpl_fs_data_desc;
info.hs_in_desc = &dpl_hs_data_desc;
info.ss_in_desc = &dpl_ss_data_desc;
- info.fs_desc_hdr = dpl_hs_data_only_desc;
+ info.fs_desc_hdr = dpl_fs_data_only_desc;
info.hs_desc_hdr = dpl_hs_data_only_desc;
info.ss_desc_hdr = dpl_ss_data_only_desc;
}
diff --git a/drivers/vhost/net.c b/drivers/vhost/net.c
index 96a0661..e5b7652 100644
--- a/drivers/vhost/net.c
+++ b/drivers/vhost/net.c
@@ -1078,6 +1078,7 @@
}
vhost_net_stop(n, &tx_sock, &rx_sock);
vhost_net_flush(n);
+ vhost_dev_stop(&n->dev);
vhost_dev_reset_owner(&n->dev, umem);
vhost_net_vq_reset(n);
done:
diff --git a/drivers/video/fbdev/msm/mdss_fb.c b/drivers/video/fbdev/msm/mdss_fb.c
index 65b689f..1ce9be0 100644
--- a/drivers/video/fbdev/msm/mdss_fb.c
+++ b/drivers/video/fbdev/msm/mdss_fb.c
@@ -3014,7 +3014,8 @@
if (sync_pt_data->timeline) {
val = sync_pt_data->threshold +
atomic_read(&sync_pt_data->commit_cnt);
- mdss_inc_timeline(sync_pt_data->timeline, val);
+ mdss_resync_timeline(sync_pt_data->timeline);
+ mdss_resync_timeline(sync_pt_data->timeline_retire);
sync_pt_data->timeline_value += val;
atomic_set(&sync_pt_data->commit_cnt, 0);
}
diff --git a/drivers/video/fbdev/msm/mdss_sync.c b/drivers/video/fbdev/msm/mdss_sync.c
index ed611e7..7b1028ab 100644
--- a/drivers/video/fbdev/msm/mdss_sync.c
+++ b/drivers/video/fbdev/msm/mdss_sync.c
@@ -226,8 +226,12 @@
int increment)
{
struct mdss_fence *f, *next;
+ s32 val;
- tl->value += increment;
+ val = tl->next_value - tl->value;
+ if (val >= increment)
+ tl->value += increment;
+
list_for_each_entry_safe(f, next, &tl->fence_list_head, fence_list) {
if (fence_is_signaled_locked(&f->base)) {
pr_debug("%s signaled\n", f->name);
@@ -255,7 +259,8 @@
spin_lock_irqsave(&tl->lock, flags);
val = tl->next_value - tl->value;
if (val > 0) {
- pr_warn("flush %s:%d\n", tl->name, val);
+ pr_warn("flush %s:%d TL(Nxt %d , Crnt %d)\n", tl->name, val,
+ tl->next_value, tl->value);
mdss_inc_timeline_locked(tl, val);
}
spin_unlock_irqrestore(&tl->lock, flags);
@@ -388,7 +393,19 @@
pr_debug("fence signaled\n");
rc = 0;
} else if (rc == 0) {
- pr_debug("fence timeout\n");
+ struct fence *input_fence = (struct fence *) fence;
+ char timeline_str[MDSS_SYNC_NAME_SIZE];
+
+ if (input_fence->ops->timeline_value_str)
+ input_fence->ops->timeline_value_str(input_fence,
+ timeline_str, MDSS_SYNC_NAME_SIZE);
+ pr_err(
+ "drv:%s timeline:%s seqno:%d timeline:%s status:0x%x\n",
+ input_fence->ops->get_driver_name(input_fence),
+ input_fence->ops->get_timeline_name(input_fence),
+ input_fence->seqno, timeline_str,
+ input_fence->ops->signaled ?
+ input_fence->ops->signaled(input_fence) : 0xffffffff);
rc = -ETIMEDOUT;
}
diff --git a/include/linux/fdtable.h b/include/linux/fdtable.h
index 6e84b2cae..442b54a 100644
--- a/include/linux/fdtable.h
+++ b/include/linux/fdtable.h
@@ -9,6 +9,7 @@
#include <linux/compiler.h>
#include <linux/spinlock.h>
#include <linux/rcupdate.h>
+#include <linux/nospec.h>
#include <linux/types.h>
#include <linux/init.h>
#include <linux/fs.h>
@@ -81,8 +82,10 @@
{
struct fdtable *fdt = rcu_dereference_raw(files->fdt);
- if (fd < fdt->max_fds)
+ if (fd < fdt->max_fds) {
+ fd = array_index_nospec(fd, fdt->max_fds);
return rcu_dereference_raw(fdt->fd[fd]);
+ }
return NULL;
}
diff --git a/include/linux/hdcp_qseecom.h b/include/linux/hdcp_qseecom.h
index 20f5cba..08f30a46 100644
--- a/include/linux/hdcp_qseecom.h
+++ b/include/linux/hdcp_qseecom.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2015-2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2015-2018, 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
@@ -181,11 +181,9 @@
/**
* struct hdcp_client_ops - call back functions to display transport layer
* @wakeup: wake up display transport layer with a new command
- * @notify_lvl_change notify of encryption level changes
*/
struct hdcp_client_ops {
int (*wakeup)(struct hdcp_wakeup_data *data);
- void (*notify_lvl_change)(void *client_ctx, int min_lvl);
};
/**
@@ -219,6 +217,4 @@
bool hdcp1_check_if_supported_load_app(void);
int hdcp1_set_keys(uint32_t *aksv_msb, uint32_t *aksv_lsb);
int hdcp1_set_enc(bool enable);
-void hdcp1_cache_repeater_topology(void *hdcp1_cached_tp);
-void hdcp1_notify_topology(void);
#endif /* __HDCP_QSEECOM_H */
diff --git a/include/linux/init.h b/include/linux/init.h
index e30104c..8e346d1 100644
--- a/include/linux/init.h
+++ b/include/linux/init.h
@@ -4,6 +4,13 @@
#include <linux/compiler.h>
#include <linux/types.h>
+/* Built-in __init functions needn't be compiled with retpoline */
+#if defined(RETPOLINE) && !defined(MODULE)
+#define __noretpoline __attribute__((indirect_branch("keep")))
+#else
+#define __noretpoline
+#endif
+
/* These macros are used to mark some functions or
* initialized data (doesn't apply to uninitialized data)
* as `initialization' functions. The kernel can take this
@@ -39,7 +46,7 @@
/* These are for everybody (although not all archs will actually
discard it in modules) */
-#define __init __section(.init.text) __cold notrace __latent_entropy
+#define __init __section(.init.text) __cold notrace __latent_entropy __noretpoline
#define __initdata __section(.init.data)
#define __initconst __section(.init.rodata)
#define __exitdata __section(.exit.data)
diff --git a/include/linux/module.h b/include/linux/module.h
index 0c3207d..d2224a0 100644
--- a/include/linux/module.h
+++ b/include/linux/module.h
@@ -791,6 +791,15 @@
static inline void module_bug_cleanup(struct module *mod) {}
#endif /* CONFIG_GENERIC_BUG */
+#ifdef RETPOLINE
+extern bool retpoline_module_ok(bool has_retpoline);
+#else
+static inline bool retpoline_module_ok(bool has_retpoline)
+{
+ return true;
+}
+#endif
+
#ifdef CONFIG_MODULE_SIG
static inline bool module_sig_ok(struct module *module)
{
diff --git a/include/linux/msm_hdcp.h b/include/linux/msm_hdcp.h
new file mode 100644
index 0000000..e25d242
--- /dev/null
+++ b/include/linux/msm_hdcp.h
@@ -0,0 +1,26 @@
+/* Copyright (c) 2018, 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 __MSM_HDCP_H
+#define __MSM_HDCP_H
+#include <linux/types.h>
+#include "video/msm_hdmi_hdcp_mgr.h"
+
+void msm_hdcp_notify_topology(struct device *dev);
+void msm_hdcp_cache_repeater_topology(struct device *dev,
+ struct HDCP_V2V1_MSG_TOPOLOGY *tp);
+void msm_hdcp_register_cb(struct device *dev, void *ctx,
+ void (*cb)(void *ctx, int data));
+
+#endif /* __MSM_HDCP_H */
+
+
diff --git a/include/linux/nospec.h b/include/linux/nospec.h
new file mode 100644
index 0000000..b99bced
--- /dev/null
+++ b/include/linux/nospec.h
@@ -0,0 +1,72 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright(c) 2018 Linus Torvalds. All rights reserved.
+// Copyright(c) 2018 Alexei Starovoitov. All rights reserved.
+// Copyright(c) 2018 Intel Corporation. All rights reserved.
+
+#ifndef _LINUX_NOSPEC_H
+#define _LINUX_NOSPEC_H
+
+/**
+ * array_index_mask_nospec() - generate a ~0 mask when index < size, 0 otherwise
+ * @index: array element index
+ * @size: number of elements in array
+ *
+ * When @index is out of bounds (@index >= @size), the sign bit will be
+ * set. Extend the sign bit to all bits and invert, giving a result of
+ * zero for an out of bounds index, or ~0 if within bounds [0, @size).
+ */
+#ifndef array_index_mask_nospec
+static inline unsigned long array_index_mask_nospec(unsigned long index,
+ unsigned long size)
+{
+ /*
+ * Warn developers about inappropriate array_index_nospec() usage.
+ *
+ * Even if the CPU speculates past the WARN_ONCE branch, the
+ * sign bit of @index is taken into account when generating the
+ * mask.
+ *
+ * This warning is compiled out when the compiler can infer that
+ * @index and @size are less than LONG_MAX.
+ */
+ if (WARN_ONCE(index > LONG_MAX || size > LONG_MAX,
+ "array_index_nospec() limited to range of [0, LONG_MAX]\n"))
+ return 0;
+
+ /*
+ * Always calculate and emit the mask even if the compiler
+ * thinks the mask is not needed. The compiler does not take
+ * into account the value of @index under speculation.
+ */
+ OPTIMIZER_HIDE_VAR(index);
+ return ~(long)(index | (size - 1UL - index)) >> (BITS_PER_LONG - 1);
+}
+#endif
+
+/*
+ * array_index_nospec - sanitize an array index after a bounds check
+ *
+ * For a code sequence like:
+ *
+ * if (index < size) {
+ * index = array_index_nospec(index, size);
+ * val = array[index];
+ * }
+ *
+ * ...if the CPU speculates past the bounds check then
+ * array_index_nospec() will clamp the index within the range of [0,
+ * size).
+ */
+#define array_index_nospec(index, size) \
+({ \
+ typeof(index) _i = (index); \
+ typeof(size) _s = (size); \
+ unsigned long _mask = array_index_mask_nospec(_i, _s); \
+ \
+ BUILD_BUG_ON(sizeof(_i) > sizeof(long)); \
+ BUILD_BUG_ON(sizeof(_s) > sizeof(long)); \
+ \
+ _i &= _mask; \
+ _i; \
+})
+#endif /* _LINUX_NOSPEC_H */
diff --git a/include/linux/qpnp/qpnp-adc.h b/include/linux/qpnp/qpnp-adc.h
index 9a079a6..a2a0152 100644
--- a/include/linux/qpnp/qpnp-adc.h
+++ b/include/linux/qpnp/qpnp-adc.h
@@ -388,6 +388,8 @@
* %SCALE_DIE_TEMP: Conversion for die temp.
* %SCALE_I_DEFAULT: Default scaling to convert raw adc code to current (uA).
* %SCALE_USBIN_I: Conversion for USB input current.
+ * %SCALE_BATT_THERM_TEMP_QRD: Conversion to temperature(decidegC) based on btm
+ * parameters for QRD.
* %SCALE_NONE: Do not use this scaling type.
*/
enum qpnp_adc_scale_fn_type {
@@ -410,6 +412,7 @@
SCALE_DIE_TEMP,
SCALE_I_DEFAULT,
SCALE_USBIN_I,
+ SCALE_BATT_THERM_TEMP_QRD,
SCALE_NONE,
};
@@ -1432,6 +1435,23 @@
const struct qpnp_vadc_chan_properties *chan_prop,
struct qpnp_vadc_result *chan_rslt);
/**
+ * qpnp_adc_batt_therm_qrd() - Scales the pre-calibrated digital output
+ * of an ADC to the ADC reference and compensates for the
+ * gain and offset. Returns the temperature in decidegC for QRD.
+ * @dev: Structure device for qpnp vadc
+ * @adc_code: pre-calibrated digital output of the ADC.
+ * @adc_prop: adc properties of the pm8xxx adc such as bit resolution,
+ * reference voltage.
+ * @chan_prop: individual channel properties to compensate the i/p scaling,
+ * slope and offset.
+ * @chan_rslt: physical result to be stored.
+ */
+int32_t qpnp_adc_batt_therm_qrd(struct qpnp_vadc_chip *dev,
+ int32_t adc_code,
+ const struct qpnp_adc_properties *adc_prop,
+ const struct qpnp_vadc_chan_properties *chan_prop,
+ struct qpnp_vadc_result *chan_rslt);
+/**
* qpnp_adc_scale_batt_therm() - Scales the pre-calibrated digital output
* of an ADC to the ADC reference and compensates for the
* gain and offset. Returns the temperature in decidegC.
@@ -2033,6 +2053,12 @@
const struct qpnp_vadc_chan_properties *chan_prop,
struct qpnp_vadc_result *chan_rslt)
{ return -ENXIO; }
+static inline int32_t qpnp_adc_batt_therm_qrd(struct qpnp_vadc_chip *vadc,
+ int32_t adc_code,
+ const struct qpnp_adc_properties *adc_prop,
+ const struct qpnp_vadc_chan_properties *chan_prop,
+ struct qpnp_vadc_result *chan_rslt)
+{ return -ENXIO; }
static inline int32_t qpnp_adc_scale_batt_therm(struct qpnp_vadc_chip *vadc,
int32_t adc_code,
const struct qpnp_adc_properties *adc_prop,
diff --git a/include/linux/slimbus/slimbus.h b/include/linux/slimbus/slimbus.h
index 53af941..4b5dc54 100644
--- a/include/linux/slimbus/slimbus.h
+++ b/include/linux/slimbus/slimbus.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2011-2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2011-2018, 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
@@ -601,7 +601,7 @@
int (*framer_handover)(struct slim_controller *ctrl,
struct slim_framer *new_framer);
int (*port_xfer)(struct slim_controller *ctrl,
- u8 pn, phys_addr_t iobuf, u32 len,
+ u8 pn, void *buf, u32 len,
struct completion *comp);
enum slim_port_err (*port_xfer_status)(struct slim_controller *ctr,
u8 pn, phys_addr_t *done_buf, u32 *done_len);
@@ -869,7 +869,7 @@
* Client will call slim_port_get_xfer_status to get error and/or number of
* bytes transferred if used asynchronously.
*/
-extern int slim_port_xfer(struct slim_device *sb, u32 ph, phys_addr_t iobuf,
+extern int slim_port_xfer(struct slim_device *sb, u32 ph, void *buf,
u32 len, struct completion *comp);
/*
diff --git a/include/sound/rawmidi.h b/include/sound/rawmidi.h
index f730b91..9e1aa1a 100644
--- a/include/sound/rawmidi.h
+++ b/include/sound/rawmidi.h
@@ -78,6 +78,7 @@
size_t xruns; /* over/underruns counter */
/* misc */
spinlock_t lock;
+ struct mutex realloc_mutex;
wait_queue_head_t sleep;
/* event handler (new bytes, input only) */
void (*event)(struct snd_rawmidi_substream *substream);
diff --git a/include/uapi/media/msm_camera.h b/include/uapi/media/msm_camera.h
index d282586..693fd21 100644
--- a/include/uapi/media/msm_camera.h
+++ b/include/uapi/media/msm_camera.h
@@ -14,9 +14,12 @@
#ifndef __UAPI_MSM_CAMERA_H
#define __UAPI_MSM_CAMERA_H
+#define CAM_API_V1
+
#include <linux/videodev2.h>
#include <linux/types.h>
#include <linux/ioctl.h>
+#include <linux/media.h>
#include <linux/msm_ion.h>
@@ -1986,8 +1989,7 @@
#define QCAMERA_NAME "qcamera"
#define QCAMERA_SERVER_NAME "qcamera_server"
-#define QCAMERA_DEVICE_GROUP_ID 1
-#define QCAMERA_VNODE_GROUP_ID 2
+#define QCAMERA_VNODE_GROUP_ID MEDIA_ENT_F_IO_V4L
enum msm_cam_subdev_type {
CSIPHY_DEV,
diff --git a/include/uapi/media/msmb_camera.h b/include/uapi/media/msmb_camera.h
index 58aff97..8bb2f8c 100644
--- a/include/uapi/media/msmb_camera.h
+++ b/include/uapi/media/msmb_camera.h
@@ -4,6 +4,7 @@
#include <linux/videodev2.h>
#include <linux/types.h>
#include <linux/ioctl.h>
+#include <linux/media.h>
#define MSM_CAM_LOGSYNC_FILE_NAME "logsync"
#define MSM_CAM_LOGSYNC_FILE_BASEDIR "camera"
@@ -26,33 +27,36 @@
#define MSM_CAM_V4L2_IOCTL_DAEMON_DISABLED \
_IOW('V', BASE_VIDIOC_PRIVATE + 35, struct msm_v4l2_event_data)
-#define QCAMERA_DEVICE_GROUP_ID 1
-#define QCAMERA_VNODE_GROUP_ID 2
+#define QCAMERA_VNODE_GROUP_ID MEDIA_ENT_F_IO_V4L
#define MSM_CAMERA_NAME "msm_camera"
#define MSM_CONFIGURATION_NAME "msm_config"
-#define MSM_CAMERA_SUBDEV_CSIPHY 0
-#define MSM_CAMERA_SUBDEV_CSID 1
-#define MSM_CAMERA_SUBDEV_ISPIF 2
-#define MSM_CAMERA_SUBDEV_VFE 3
-#define MSM_CAMERA_SUBDEV_AXI 4
-#define MSM_CAMERA_SUBDEV_VPE 5
-#define MSM_CAMERA_SUBDEV_SENSOR 6
-#define MSM_CAMERA_SUBDEV_ACTUATOR 7
-#define MSM_CAMERA_SUBDEV_EEPROM 8
-#define MSM_CAMERA_SUBDEV_CPP 9
-#define MSM_CAMERA_SUBDEV_CCI 10
-#define MSM_CAMERA_SUBDEV_LED_FLASH 11
-#define MSM_CAMERA_SUBDEV_STROBE_FLASH 12
-#define MSM_CAMERA_SUBDEV_BUF_MNGR 13
-#define MSM_CAMERA_SUBDEV_SENSOR_INIT 14
-#define MSM_CAMERA_SUBDEV_OIS 15
-#define MSM_CAMERA_SUBDEV_FLASH 16
-#define MSM_CAMERA_SUBDEV_IR_LED 17
-#define MSM_CAMERA_SUBDEV_IR_CUT 18
-#define MSM_CAMERA_SUBDEV_EXT 19
-#define MSM_CAMERA_SUBDEV_TOF 20
-#define MSM_CAMERA_SUBDEV_LASER_LED 21
+//#define MSM_CAMERA_SUBDEV_BASE (MEDIA_ENT_F_OLD_SUBDEV_BASE + 1)
+#define MSM_CAMERA_SUBDEV_BASE (MEDIA_ENT_F_OLD_BASE + 0xF00)
+#define MSM_CAMERA_SUBDEV_CSIPHY (MSM_CAMERA_SUBDEV_BASE + 0)
+//#define MSM_CAMERA_SUBDEV_CSID (MSM_CAMERA_SUBDEV_BASE + 1)
+#define MSM_CAMERA_SUBDEV_CSID (MSM_CAMERA_SUBDEV_BASE + 13)
+#define MSM_CAMERA_SUBDEV_ISPIF (MSM_CAMERA_SUBDEV_BASE + 2)
+#define MSM_CAMERA_SUBDEV_VFE (MSM_CAMERA_SUBDEV_BASE + 3)
+#define MSM_CAMERA_SUBDEV_AXI (MSM_CAMERA_SUBDEV_BASE + 4)
+#define MSM_CAMERA_SUBDEV_VPE (MSM_CAMERA_SUBDEV_BASE + 5)
+#define MSM_CAMERA_SUBDEV_SENSOR (MSM_CAMERA_SUBDEV_BASE + 6)
+#define MSM_CAMERA_SUBDEV_ACTUATOR (MSM_CAMERA_SUBDEV_BASE + 7)
+#define MSM_CAMERA_SUBDEV_EEPROM (MSM_CAMERA_SUBDEV_BASE + 8)
+#define MSM_CAMERA_SUBDEV_CPP (MSM_CAMERA_SUBDEV_BASE + 9)
+#define MSM_CAMERA_SUBDEV_CCI (MSM_CAMERA_SUBDEV_BASE + 10)
+#define MSM_CAMERA_SUBDEV_LED_FLASH (MSM_CAMERA_SUBDEV_BASE + 11)
+#define MSM_CAMERA_SUBDEV_STROBE_FLASH (MSM_CAMERA_SUBDEV_BASE + 12)
+#define MSM_CAMERA_SUBDEV_BUF_MNGR (MSM_CAMERA_SUBDEV_BASE + 1)
+//#define MSM_CAMERA_SUBDEV_BUF_MNGR (MSM_CAMERA_SUBDEV_BASE + 13)
+#define MSM_CAMERA_SUBDEV_SENSOR_INIT (MSM_CAMERA_SUBDEV_BASE + 14)
+#define MSM_CAMERA_SUBDEV_OIS (MSM_CAMERA_SUBDEV_BASE + 15)
+#define MSM_CAMERA_SUBDEV_FLASH (MSM_CAMERA_SUBDEV_BASE + 16)
+#define MSM_CAMERA_SUBDEV_IR_LED (MSM_CAMERA_SUBDEV_BASE + 17)
+#define MSM_CAMERA_SUBDEV_IR_CUT (MSM_CAMERA_SUBDEV_BASE + 18)
+#define MSM_CAMERA_SUBDEV_EXT (MSM_CAMERA_SUBDEV_BASE + 19)
+#define MSM_CAMERA_SUBDEV_TOF (MSM_CAMERA_SUBDEV_BASE + 20)
+#define MSM_CAMERA_SUBDEV_LASER_LED (MSM_CAMERA_SUBDEV_BASE + 21)
#define MSM_MAX_CAMERA_SENSORS 5
/* The below macro is defined to put an upper limit on maximum
diff --git a/include/uapi/video/msm_hdmi_hdcp_mgr.h b/include/uapi/video/msm_hdmi_hdcp_mgr.h
index 85fa918..1c19d4a 100644
--- a/include/uapi/video/msm_hdmi_hdcp_mgr.h
+++ b/include/uapi/video/msm_hdmi_hdcp_mgr.h
@@ -1,4 +1,4 @@
-#ifndef _UAPI__HDMI_HDCP_MGR_H
+#ifndef _UAPI__MSM_HDMI_HDCP_MGR_H
#define _UAPI__MSM_HDMI_HDCP_MGR_H
enum DS_TYPE { /* type of downstream device */
diff --git a/kernel/module.c b/kernel/module.c
index 0e54d5b..07bfb99 100644
--- a/kernel/module.c
+++ b/kernel/module.c
@@ -2817,6 +2817,15 @@
}
#endif /* CONFIG_LIVEPATCH */
+static void check_modinfo_retpoline(struct module *mod, struct load_info *info)
+{
+ if (retpoline_module_ok(get_modinfo(info, "retpoline")))
+ return;
+
+ pr_warn("%s: loading module not compiled with retpoline compiler.\n",
+ mod->name);
+}
+
/* Sets info->hdr and info->len. */
static int copy_module_from_user(const void __user *umod, unsigned long len,
struct load_info *info)
@@ -2969,6 +2978,8 @@
add_taint_module(mod, TAINT_OOT_MODULE, LOCKDEP_STILL_OK);
}
+ check_modinfo_retpoline(mod, info);
+
if (get_modinfo(info, "staging")) {
add_taint_module(mod, TAINT_CRAP, LOCKDEP_STILL_OK);
pr_warn("%s: module is from the staging directory, the quality "
diff --git a/kernel/sched/energy.c b/kernel/sched/energy.c
index 420cb52..77d8361 100644
--- a/kernel/sched/energy.c
+++ b/kernel/sched/energy.c
@@ -150,6 +150,7 @@
int cpu;
unsigned long *max_frequencies = NULL;
int ret;
+ bool is_sge_valid = false;
if (!sched_is_energy_aware())
return 0;
@@ -248,6 +249,7 @@
sge_l0->cap_states[i].power);
}
+ is_sge_valid = true;
dev_info(&pdev->dev,
"cpu=%d eff=%d [freq=%ld cap=%ld power_d0=%ld] -> [freq=%ld cap=%ld power_d0=%ld]\n",
cpu, efficiency,
@@ -271,7 +273,8 @@
kfree(max_frequencies);
- walt_sched_energy_populated_callback();
+ if (is_sge_valid)
+ walt_sched_energy_populated_callback();
dev_info(&pdev->dev, "Sched-energy-costs capacity updated\n");
return 0;
diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c
index 55c3957..4cd730e 100755
--- a/kernel/sched/fair.c
+++ b/kernel/sched/fair.c
@@ -5805,10 +5805,22 @@
u64 total_energy = 0;
struct cpumask visit_cpus;
struct sched_group *sg;
+ int cpu_count;
WARN_ON(!eenv->sg_top->sge);
cpumask_copy(&visit_cpus, sched_group_cpus(eenv->sg_top));
+ /* If a cpu is hotplugged in while we are in this function,
+ * it does not appear in the existing visit_cpus mask
+ * which came from the sched_group pointer of the
+ * sched_domain pointed at by sd_ea for either the prev
+ * or next cpu and was dereferenced in __energy_diff.
+ * Since we will dereference sd_scs later as we iterate
+ * through the CPUs we expect to visit, new CPUs can
+ * be present which are not in the visit_cpus mask.
+ * Guard this with cpu_count.
+ */
+ cpu_count = cpumask_weight(&visit_cpus);
while (!cpumask_empty(&visit_cpus)) {
struct sched_group *sg_shared_cap = NULL;
@@ -5818,6 +5830,8 @@
/*
* Is the group utilization affected by cpus outside this
* sched_group?
+ * This sd may have groups with cpus which were not present
+ * when we took visit_cpus.
*/
sd = rcu_dereference(per_cpu(sd_scs, cpu));
@@ -5883,8 +5897,24 @@
idle_idx,
sg->sge->cap_states[eenv->cap_idx].cap);
- if (!sd->child)
+ if (!sd->child) {
+ /*
+ * cpu_count here is the number of
+ * cpus we expect to visit in this
+ * calculation. If we race against
+ * hotplug, we can have extra cpus
+ * added to the groups we are
+ * iterating which do not appear in
+ * the visit_cpus mask. In that case
+ * we are not able to calculate energy
+ * without restarting so we will bail
+ * out and use prev_cpu this time.
+ */
+ if (!cpu_count)
+ return -EINVAL;
cpumask_xor(&visit_cpus, &visit_cpus, sched_group_cpus(sg));
+ cpu_count--;
+ }
if (cpumask_equal(sched_group_cpus(sg), sched_group_cpus(eenv->sg_top)))
goto next_cpu;
@@ -5896,6 +5926,9 @@
* If we raced with hotplug and got an sd NULL-pointer;
* returning a wrong energy estimation is better than
* entering an infinite loop.
+ * Specifically: If a cpu is unplugged after we took
+ * the visit_cpus mask, it no longer has an sd_scs
+ * pointer, so when we dereference it, we get NULL.
*/
if (cpumask_test_cpu(cpu, &visit_cpus))
return -EINVAL;
diff --git a/net/core/sock_reuseport.c b/net/core/sock_reuseport.c
index 77f396b..5dce429 100644
--- a/net/core/sock_reuseport.c
+++ b/net/core/sock_reuseport.c
@@ -93,6 +93,16 @@
return more_reuse;
}
+static void reuseport_free_rcu(struct rcu_head *head)
+{
+ struct sock_reuseport *reuse;
+
+ reuse = container_of(head, struct sock_reuseport, rcu);
+ if (reuse->prog)
+ bpf_prog_destroy(reuse->prog);
+ kfree(reuse);
+}
+
/**
* reuseport_add_sock - Add a socket to the reuseport group of another.
* @sk: New socket to add to the group.
@@ -101,7 +111,7 @@
*/
int reuseport_add_sock(struct sock *sk, struct sock *sk2)
{
- struct sock_reuseport *reuse;
+ struct sock_reuseport *old_reuse, *reuse;
if (!rcu_access_pointer(sk2->sk_reuseport_cb)) {
int err = reuseport_alloc(sk2);
@@ -112,10 +122,13 @@
spin_lock_bh(&reuseport_lock);
reuse = rcu_dereference_protected(sk2->sk_reuseport_cb,
- lockdep_is_held(&reuseport_lock)),
- WARN_ONCE(rcu_dereference_protected(sk->sk_reuseport_cb,
- lockdep_is_held(&reuseport_lock)),
- "socket already in reuseport group");
+ lockdep_is_held(&reuseport_lock));
+ old_reuse = rcu_dereference_protected(sk->sk_reuseport_cb,
+ lockdep_is_held(&reuseport_lock));
+ if (old_reuse && old_reuse->num_socks != 1) {
+ spin_unlock_bh(&reuseport_lock);
+ return -EBUSY;
+ }
if (reuse->num_socks == reuse->max_socks) {
reuse = reuseport_grow(reuse);
@@ -133,19 +146,11 @@
spin_unlock_bh(&reuseport_lock);
+ if (old_reuse)
+ call_rcu(&old_reuse->rcu, reuseport_free_rcu);
return 0;
}
-static void reuseport_free_rcu(struct rcu_head *head)
-{
- struct sock_reuseport *reuse;
-
- reuse = container_of(head, struct sock_reuseport, rcu);
- if (reuse->prog)
- bpf_prog_destroy(reuse->prog);
- kfree(reuse);
-}
-
void reuseport_detach_sock(struct sock *sk)
{
struct sock_reuseport *reuse;
diff --git a/net/ipv4/igmp.c b/net/ipv4/igmp.c
index 9c7a4ce..7f5fe07 100644
--- a/net/ipv4/igmp.c
+++ b/net/ipv4/igmp.c
@@ -386,7 +386,11 @@
pip->frag_off = htons(IP_DF);
pip->ttl = 1;
pip->daddr = fl4.daddr;
+
+ rcu_read_lock();
pip->saddr = igmpv3_get_srcaddr(dev, &fl4);
+ rcu_read_unlock();
+
pip->protocol = IPPROTO_IGMP;
pip->tot_len = 0; /* filled in later */
ip_select_ident(net, skb, NULL);
diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c
index cb3f08c..6ba9bdc 100644
--- a/net/ipv4/tcp.c
+++ b/net/ipv4/tcp.c
@@ -2325,6 +2325,12 @@
WARN_ON(inet->inet_num && !icsk->icsk_bind_hash);
+ if (sk->sk_frag.page) {
+ put_page(sk->sk_frag.page);
+ sk->sk_frag.page = NULL;
+ sk->sk_frag.offset = 0;
+ }
+
sk->sk_error_report(sk);
return err;
}
diff --git a/net/ipv4/tcp_bbr.c b/net/ipv4/tcp_bbr.c
index e86a34f..8ec6053 100644
--- a/net/ipv4/tcp_bbr.c
+++ b/net/ipv4/tcp_bbr.c
@@ -452,7 +452,8 @@
bbr->cycle_idx = (bbr->cycle_idx + 1) & (CYCLE_LEN - 1);
bbr->cycle_mstamp = tp->delivered_mstamp;
- bbr->pacing_gain = bbr_pacing_gain[bbr->cycle_idx];
+ bbr->pacing_gain = bbr->lt_use_bw ? BBR_UNIT :
+ bbr_pacing_gain[bbr->cycle_idx];
}
/* Gain cycling: cycle pacing gain to converge to fair share of available bw. */
@@ -461,8 +462,7 @@
{
struct bbr *bbr = inet_csk_ca(sk);
- if ((bbr->mode == BBR_PROBE_BW) && !bbr->lt_use_bw &&
- bbr_is_next_cycle_phase(sk, rs))
+ if (bbr->mode == BBR_PROBE_BW && bbr_is_next_cycle_phase(sk, rs))
bbr_advance_cycle_phase(sk);
}
diff --git a/net/ipv6/af_inet6.c b/net/ipv6/af_inet6.c
index 55a8f68..1111684 100644
--- a/net/ipv6/af_inet6.c
+++ b/net/ipv6/af_inet6.c
@@ -290,6 +290,7 @@
struct net *net = sock_net(sk);
__be32 v4addr = 0;
unsigned short snum;
+ bool saved_ipv6only;
int addr_type = 0;
int err = 0;
@@ -394,19 +395,21 @@
if (!(addr_type & IPV6_ADDR_MULTICAST))
np->saddr = addr->sin6_addr;
+ saved_ipv6only = sk->sk_ipv6only;
+ if (addr_type != IPV6_ADDR_ANY && addr_type != IPV6_ADDR_MAPPED)
+ sk->sk_ipv6only = 1;
+
/* Make sure we are allowed to bind here. */
if ((snum || !inet->bind_address_no_port) &&
sk->sk_prot->get_port(sk, snum)) {
+ sk->sk_ipv6only = saved_ipv6only;
inet_reset_saddr(sk);
err = -EADDRINUSE;
goto out;
}
- if (addr_type != IPV6_ADDR_ANY) {
+ if (addr_type != IPV6_ADDR_ANY)
sk->sk_userlocks |= SOCK_BINDADDR_LOCK;
- if (addr_type != IPV6_ADDR_MAPPED)
- sk->sk_ipv6only = 1;
- }
if (snum)
sk->sk_userlocks |= SOCK_BINDPORT_LOCK;
inet->inet_sport = htons(inet->inet_num);
diff --git a/net/ipv6/ip6mr.c b/net/ipv6/ip6mr.c
index 117405d..a30e7e9 100644
--- a/net/ipv6/ip6mr.c
+++ b/net/ipv6/ip6mr.c
@@ -495,6 +495,7 @@
return ERR_PTR(-ENOENT);
it->mrt = mrt;
+ it->cache = NULL;
return *pos ? ipmr_mfc_seq_idx(net, seq->private, *pos - 1)
: SEQ_START_TOKEN;
}
diff --git a/net/netfilter/xt_qtaguid.c b/net/netfilter/xt_qtaguid.c
index 822be06..d2141a6 100644
--- a/net/netfilter/xt_qtaguid.c
+++ b/net/netfilter/xt_qtaguid.c
@@ -519,13 +519,11 @@
DR_DEBUG("qtaguid: get_tag_ref(0x%llx)\n",
full_tag);
- spin_lock_bh(&uid_tag_data_tree_lock);
tr_entry = lookup_tag_ref(full_tag, &utd_entry);
BUG_ON(IS_ERR_OR_NULL(utd_entry));
if (!tr_entry)
tr_entry = new_tag_ref(full_tag, utd_entry);
- spin_unlock_bh(&uid_tag_data_tree_lock);
if (utd_res)
*utd_res = utd_entry;
DR_DEBUG("qtaguid: get_tag_ref(0x%llx) utd=%p tr=%p\n",
@@ -2027,6 +2025,7 @@
/* Delete socket tags */
spin_lock_bh(&sock_tag_list_lock);
+ spin_lock_bh(&uid_tag_data_tree_lock);
node = rb_first(&sock_tag_tree);
while (node) {
st_entry = rb_entry(node, struct sock_tag, sock_node);
@@ -2056,6 +2055,7 @@
list_del(&st_entry->list);
}
}
+ spin_unlock_bh(&uid_tag_data_tree_lock);
spin_unlock_bh(&sock_tag_list_lock);
sock_tag_tree_erase(&st_to_free_tree);
@@ -2265,10 +2265,12 @@
full_tag = combine_atag_with_uid(acct_tag, uid_int);
spin_lock_bh(&sock_tag_list_lock);
+ spin_lock_bh(&uid_tag_data_tree_lock);
sock_tag_entry = get_sock_stat_nl(el_socket->sk);
tag_ref_entry = get_tag_ref(full_tag, &uid_tag_data_entry);
if (IS_ERR(tag_ref_entry)) {
res = PTR_ERR(tag_ref_entry);
+ spin_unlock_bh(&uid_tag_data_tree_lock);
spin_unlock_bh(&sock_tag_list_lock);
goto err_put;
}
@@ -2295,9 +2297,14 @@
pr_err("qtaguid: ctrl_tag(%s): "
"socket tag alloc failed\n",
input);
+ BUG_ON(tag_ref_entry->num_sock_tags <= 0);
+ tag_ref_entry->num_sock_tags--;
+ free_tag_ref_from_utd_entry(tag_ref_entry,
+ uid_tag_data_entry);
+ spin_unlock_bh(&uid_tag_data_tree_lock);
spin_unlock_bh(&sock_tag_list_lock);
res = -ENOMEM;
- goto err_tag_unref_put;
+ goto err_put;
}
/*
* Hold the sk refcount here to make sure the sk pointer cannot
@@ -2307,7 +2314,6 @@
sock_tag_entry->sk = el_socket->sk;
sock_tag_entry->pid = current->tgid;
sock_tag_entry->tag = combine_atag_with_uid(acct_tag, uid_int);
- spin_lock_bh(&uid_tag_data_tree_lock);
pqd_entry = proc_qtu_data_tree_search(
&proc_qtu_data_tree, current->tgid);
/*
@@ -2325,11 +2331,11 @@
else
list_add(&sock_tag_entry->list,
&pqd_entry->sock_tag_list);
- spin_unlock_bh(&uid_tag_data_tree_lock);
sock_tag_tree_insert(sock_tag_entry, &sock_tag_tree);
atomic64_inc(&qtu_events.sockets_tagged);
}
+ spin_unlock_bh(&uid_tag_data_tree_lock);
spin_unlock_bh(&sock_tag_list_lock);
/* We keep the ref to the sk until it is untagged */
CT_DEBUG("qtaguid: ctrl_tag(%s): done st@%p ...->sk_refcnt=%d\n",
@@ -2338,10 +2344,6 @@
sockfd_put(el_socket);
return 0;
-err_tag_unref_put:
- BUG_ON(tag_ref_entry->num_sock_tags <= 0);
- tag_ref_entry->num_sock_tags--;
- free_tag_ref_from_utd_entry(tag_ref_entry, uid_tag_data_entry);
err_put:
CT_DEBUG("qtaguid: ctrl_tag(%s): done. ...->sk_refcnt=%d\n",
input, atomic_read(&el_socket->sk->sk_refcnt) - 1);
diff --git a/net/sched/cls_u32.c b/net/sched/cls_u32.c
index ae83c3ae..da574a1 100644
--- a/net/sched/cls_u32.c
+++ b/net/sched/cls_u32.c
@@ -496,6 +496,7 @@
static int u32_replace_hw_knode(struct tcf_proto *tp, struct tc_u_knode *n,
u32 flags)
{
+ struct tc_u_hnode *ht = rtnl_dereference(n->ht_down);
struct net_device *dev = tp->q->dev_queue->dev;
struct tc_cls_u32_offload u32_offload = {0};
struct tc_to_netdev offload;
@@ -520,7 +521,7 @@
offload.cls_u32->knode.sel = &n->sel;
offload.cls_u32->knode.exts = &n->exts;
if (n->ht_down)
- offload.cls_u32->knode.link_handle = n->ht_down->handle;
+ offload.cls_u32->knode.link_handle = ht->handle;
err = dev->netdev_ops->ndo_setup_tc(dev, tp->q->handle,
tp->protocol, &offload);
@@ -788,8 +789,9 @@
static struct tc_u_knode *u32_init_knode(struct tcf_proto *tp,
struct tc_u_knode *n)
{
- struct tc_u_knode *new;
+ struct tc_u_hnode *ht = rtnl_dereference(n->ht_down);
struct tc_u32_sel *s = &n->sel;
+ struct tc_u_knode *new;
new = kzalloc(sizeof(*n) + s->nkeys*sizeof(struct tc_u32_key),
GFP_KERNEL);
@@ -807,11 +809,11 @@
new->fshift = n->fshift;
new->res = n->res;
new->flags = n->flags;
- RCU_INIT_POINTER(new->ht_down, n->ht_down);
+ RCU_INIT_POINTER(new->ht_down, ht);
/* bump reference count as long as we hold pointer to structure */
- if (new->ht_down)
- new->ht_down->refcnt++;
+ if (ht)
+ ht->refcnt++;
#ifdef CONFIG_CLS_U32_PERF
/* Statistics may be incremented by readers during update
diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c
index 8f9bd38..466aa7e 100644
--- a/net/wireless/nl80211.c
+++ b/net/wireless/nl80211.c
@@ -16,6 +16,7 @@
#include <linux/nl80211.h>
#include <linux/rtnetlink.h>
#include <linux/netlink.h>
+#include <linux/nospec.h>
#include <linux/etherdevice.h>
#include <net/net_namespace.h>
#include <net/genetlink.h>
@@ -2039,20 +2040,22 @@
static int parse_txq_params(struct nlattr *tb[],
struct ieee80211_txq_params *txq_params)
{
+ u8 ac;
+
if (!tb[NL80211_TXQ_ATTR_AC] || !tb[NL80211_TXQ_ATTR_TXOP] ||
!tb[NL80211_TXQ_ATTR_CWMIN] || !tb[NL80211_TXQ_ATTR_CWMAX] ||
!tb[NL80211_TXQ_ATTR_AIFS])
return -EINVAL;
- txq_params->ac = nla_get_u8(tb[NL80211_TXQ_ATTR_AC]);
+ ac = nla_get_u8(tb[NL80211_TXQ_ATTR_AC]);
txq_params->txop = nla_get_u16(tb[NL80211_TXQ_ATTR_TXOP]);
txq_params->cwmin = nla_get_u16(tb[NL80211_TXQ_ATTR_CWMIN]);
txq_params->cwmax = nla_get_u16(tb[NL80211_TXQ_ATTR_CWMAX]);
txq_params->aifs = nla_get_u8(tb[NL80211_TXQ_ATTR_AIFS]);
- if (txq_params->ac >= NL80211_NUM_ACS)
+ if (ac >= NL80211_NUM_ACS)
return -EINVAL;
-
+ txq_params->ac = array_index_nospec(ac, NL80211_NUM_ACS);
return 0;
}
diff --git a/scripts/mod/modpost.c b/scripts/mod/modpost.c
index 845eb9b..238db4f 100644
--- a/scripts/mod/modpost.c
+++ b/scripts/mod/modpost.c
@@ -2130,6 +2130,14 @@
buf_printf(b, "\nMODULE_INFO(intree, \"Y\");\n");
}
+/* Cannot check for assembler */
+static void add_retpoline(struct buffer *b)
+{
+ buf_printf(b, "\n#ifdef RETPOLINE\n");
+ buf_printf(b, "MODULE_INFO(retpoline, \"Y\");\n");
+ buf_printf(b, "#endif\n");
+}
+
static void add_staging_flag(struct buffer *b, const char *name)
{
static const char *staging_dir = "drivers/staging";
@@ -2474,6 +2482,7 @@
add_header(&buf, mod);
add_intree_flag(&buf, !external_module);
+ add_retpoline(&buf);
add_staging_flag(&buf, mod->name);
err |= add_versions(&buf, mod);
add_depends(&buf, mod, modules);
diff --git a/security/keys/encrypted-keys/encrypted.c b/security/keys/encrypted-keys/encrypted.c
index a871159..ead2fd6 100644
--- a/security/keys/encrypted-keys/encrypted.c
+++ b/security/keys/encrypted-keys/encrypted.c
@@ -141,23 +141,22 @@
*/
static int valid_master_desc(const char *new_desc, const char *orig_desc)
{
- if (!memcmp(new_desc, KEY_TRUSTED_PREFIX, KEY_TRUSTED_PREFIX_LEN)) {
- if (strlen(new_desc) == KEY_TRUSTED_PREFIX_LEN)
- goto out;
- if (orig_desc)
- if (memcmp(new_desc, orig_desc, KEY_TRUSTED_PREFIX_LEN))
- goto out;
- } else if (!memcmp(new_desc, KEY_USER_PREFIX, KEY_USER_PREFIX_LEN)) {
- if (strlen(new_desc) == KEY_USER_PREFIX_LEN)
- goto out;
- if (orig_desc)
- if (memcmp(new_desc, orig_desc, KEY_USER_PREFIX_LEN))
- goto out;
- } else
- goto out;
+ int prefix_len;
+
+ if (!strncmp(new_desc, KEY_TRUSTED_PREFIX, KEY_TRUSTED_PREFIX_LEN))
+ prefix_len = KEY_TRUSTED_PREFIX_LEN;
+ else if (!strncmp(new_desc, KEY_USER_PREFIX, KEY_USER_PREFIX_LEN))
+ prefix_len = KEY_USER_PREFIX_LEN;
+ else
+ return -EINVAL;
+
+ if (!new_desc[prefix_len])
+ return -EINVAL;
+
+ if (orig_desc && strncmp(new_desc, orig_desc, prefix_len))
+ return -EINVAL;
+
return 0;
-out:
- return -EINVAL;
}
/*
diff --git a/sound/core/rawmidi.c b/sound/core/rawmidi.c
index 16f8124..5143801 100644
--- a/sound/core/rawmidi.c
+++ b/sound/core/rawmidi.c
@@ -115,6 +115,7 @@
return -ENOMEM;
runtime->substream = substream;
spin_lock_init(&runtime->lock);
+ mutex_init(&runtime->realloc_mutex);
init_waitqueue_head(&runtime->sleep);
INIT_WORK(&runtime->event_work, snd_rawmidi_input_event_work);
runtime->event = NULL;
@@ -636,8 +637,10 @@
struct snd_rawmidi_params * params)
{
char *newbuf;
+ char *oldbuf;
struct snd_rawmidi_runtime *runtime = substream->runtime;
-
+ unsigned long flags;
+
if (substream->append && substream->use_count > 1)
return -EBUSY;
snd_rawmidi_drain_output(substream);
@@ -648,13 +651,22 @@
return -EINVAL;
}
if (params->buffer_size != runtime->buffer_size) {
- newbuf = krealloc(runtime->buffer, params->buffer_size,
+ mutex_lock(&runtime->realloc_mutex);
+ newbuf = __krealloc(runtime->buffer, params->buffer_size,
GFP_KERNEL);
- if (!newbuf)
+ if (!newbuf) {
+ mutex_unlock(&runtime->realloc_mutex);
return -ENOMEM;
+ }
+ spin_lock_irqsave(&runtime->lock, flags);
+ oldbuf = runtime->buffer;
runtime->buffer = newbuf;
runtime->buffer_size = params->buffer_size;
runtime->avail = runtime->buffer_size;
+ spin_unlock_irqrestore(&runtime->lock, flags);
+ if (oldbuf != newbuf)
+ kfree(oldbuf);
+ mutex_unlock(&runtime->realloc_mutex);
}
runtime->avail_min = params->avail_min;
substream->active_sensing = !params->no_active_sensing;
@@ -666,7 +678,9 @@
struct snd_rawmidi_params * params)
{
char *newbuf;
+ char *oldbuf;
struct snd_rawmidi_runtime *runtime = substream->runtime;
+ unsigned long flags;
snd_rawmidi_drain_input(substream);
if (params->buffer_size < 32 || params->buffer_size > 1024L * 1024L) {
@@ -676,12 +690,21 @@
return -EINVAL;
}
if (params->buffer_size != runtime->buffer_size) {
- newbuf = krealloc(runtime->buffer, params->buffer_size,
+ mutex_lock(&runtime->realloc_mutex);
+ newbuf = __krealloc(runtime->buffer, params->buffer_size,
GFP_KERNEL);
- if (!newbuf)
+ if (!newbuf) {
+ mutex_unlock(&runtime->realloc_mutex);
return -ENOMEM;
+ }
+ spin_lock_irqsave(&runtime->lock, flags);
+ oldbuf = runtime->buffer;
runtime->buffer = newbuf;
runtime->buffer_size = params->buffer_size;
+ spin_unlock_irqrestore(&runtime->lock, flags);
+ if (oldbuf != newbuf)
+ kfree(oldbuf);
+ mutex_unlock(&runtime->realloc_mutex);
}
runtime->avail_min = params->avail_min;
return 0;
@@ -954,6 +977,8 @@
unsigned long appl_ptr;
spin_lock_irqsave(&runtime->lock, flags);
+ if (userbuf)
+ mutex_lock(&runtime->realloc_mutex);
while (count > 0 && runtime->avail) {
count1 = runtime->buffer_size - runtime->appl_ptr;
if (count1 > count)
@@ -973,6 +998,7 @@
spin_unlock_irqrestore(&runtime->lock, flags);
if (copy_to_user(userbuf + result,
runtime->buffer + appl_ptr, count1)) {
+ mutex_unlock(&runtime->realloc_mutex);
return result > 0 ? result : -EFAULT;
}
spin_lock_irqsave(&runtime->lock, flags);
@@ -981,6 +1007,8 @@
count -= count1;
}
spin_unlock_irqrestore(&runtime->lock, flags);
+ if (userbuf)
+ mutex_unlock(&runtime->realloc_mutex);
return result;
}
@@ -1245,10 +1273,14 @@
return -EINVAL;
result = 0;
+ if (userbuf)
+ mutex_lock(&runtime->realloc_mutex);
spin_lock_irqsave(&runtime->lock, flags);
if (substream->append) {
if ((long)runtime->avail < count) {
spin_unlock_irqrestore(&runtime->lock, flags);
+ if (userbuf)
+ mutex_unlock(&runtime->realloc_mutex);
return -EAGAIN;
}
}
@@ -1284,6 +1316,8 @@
__end:
count1 = runtime->avail < runtime->buffer_size;
spin_unlock_irqrestore(&runtime->lock, flags);
+ if (userbuf)
+ mutex_unlock(&runtime->realloc_mutex);
if (count1)
snd_rawmidi_output_trigger(substream, 1);
return result;
diff --git a/sound/soc/codecs/pcm512x-spi.c b/sound/soc/codecs/pcm512x-spi.c
index 712ed65..ebdf9bd 100644
--- a/sound/soc/codecs/pcm512x-spi.c
+++ b/sound/soc/codecs/pcm512x-spi.c
@@ -70,3 +70,7 @@
};
module_spi_driver(pcm512x_spi_driver);
+
+MODULE_DESCRIPTION("ASoC PCM512x codec driver - SPI");
+MODULE_AUTHOR("Mark Brown <broonie@kernel.org>");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/generic/simple-card.c b/sound/soc/generic/simple-card.c
index f608f8d2..dd88c2c 100644
--- a/sound/soc/generic/simple-card.c
+++ b/sound/soc/generic/simple-card.c
@@ -232,13 +232,19 @@
snprintf(prop, sizeof(prop), "%scpu", prefix);
cpu = of_get_child_by_name(node, prop);
+ if (!cpu) {
+ ret = -EINVAL;
+ dev_err(dev, "%s: Can't find %s DT node\n", __func__, prop);
+ goto dai_link_of_err;
+ }
+
snprintf(prop, sizeof(prop), "%splat", prefix);
plat = of_get_child_by_name(node, prop);
snprintf(prop, sizeof(prop), "%scodec", prefix);
codec = of_get_child_by_name(node, prop);
- if (!cpu || !codec) {
+ if (!codec) {
ret = -EINVAL;
dev_err(dev, "%s: Can't find %s DT node\n", __func__, prop);
goto dai_link_of_err;
diff --git a/sound/soc/sh/rcar/ssi.c b/sound/soc/sh/rcar/ssi.c
index 560cf4b..a9a43ac 100644
--- a/sound/soc/sh/rcar/ssi.c
+++ b/sound/soc/sh/rcar/ssi.c
@@ -699,9 +699,14 @@
struct rsnd_priv *priv)
{
struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod);
+ struct rsnd_mod *pure_ssi_mod = rsnd_io_to_mod_ssi(io);
struct device *dev = rsnd_priv_to_dev(priv);
int irq = ssi->irq;
+ /* Do nothing if non SSI (= SSI parent, multi SSI) mod */
+ if (pure_ssi_mod != mod)
+ return 0;
+
/* PIO will request IRQ again */
devm_free_irq(dev, irq, mod);