Merge "ARM: dts: msm: Update GPU PM QoS Vote for SDM845"
diff --git a/Documentation/devicetree/bindings/arm/msm/imem.txt b/Documentation/devicetree/bindings/arm/msm/imem.txt
index eaa7146b..654f26e 100644
--- a/Documentation/devicetree/bindings/arm/msm/imem.txt
+++ b/Documentation/devicetree/bindings/arm/msm/imem.txt
@@ -67,6 +67,11 @@
Memory region used to store USB PID and serial numbers to be used by
bootloader in download mode.
+SSR Minidump Offset
+-------------------
+-Compatible: "qcom,msm-imem-minidump"
+-reg: start address and size of ssr imem region
+
Required properties:
-compatible: "qcom,msm-imem-diag-dload"
-reg: start address and size of USB Diag download mode region in imem
@@ -115,4 +120,9 @@
compatible = "qcom,msm-imem-emergency_download_mode";
reg = <0xfe0 12>;
};
+
+ ss_mdump@b88 {
+ compatible = "qcom,msm-imem-minidump";
+ reg = <0xb88 28>;
+ };
};
diff --git a/Documentation/devicetree/bindings/arm/msm/jtag-mm.txt b/Documentation/devicetree/bindings/arm/msm/jtag-mm.txt
new file mode 100644
index 0000000..8f57d0a
--- /dev/null
+++ b/Documentation/devicetree/bindings/arm/msm/jtag-mm.txt
@@ -0,0 +1,32 @@
+* JTAG-MM
+
+The jtag-mm entry specifies the memory mapped addresses for the debug and ETM
+registers. The jtag-mm driver uses these to save and restore the registers
+using memory mapped access during power collapse so as to retain their state
+across power collapse. This is necessary in case cp14 access to the registers
+is not permitted.
+
+Required Properties:
+compatible: component name used for driver matching, should be:
+ "qcom,jtag-mm" - for jtag-mm device
+ "qcom,jtagv8-mm" - for jtagv8-mm device supporting ARMv8 targets
+
+ reg: physical base address and length of the register set
+ reg-names: should be "etm-base" for etm register set and "debug-base"
+ for debug register set.
+ qcom,coresight-jtagmm-cpu: specifies phandle for the cpu associated
+ with the jtag-mm device
+ qcom,si-enable : boolean, indicating etm save and restore is
+ supported via system instructions
+ qcom,save-restore-disable : boolean, to disable etm save and restore
+ functionality
+
+Example:
+jtag_mm: jtagmm@fc332000 {
+ compatible = "qcom,jtag-mm";
+ reg = <0xfc332000 0x1000>,
+ <0xfc333000 0x1000>;
+ reg-names = "etm-base","debug-base";
+
+ qcom,coresight-jtagmm-cpu = <&CPU0>;
+};
diff --git a/Documentation/devicetree/bindings/clock/qcom,camcc.txt b/Documentation/devicetree/bindings/clock/qcom,camcc.txt
index 313e50f..daf8a539 100644
--- a/Documentation/devicetree/bindings/clock/qcom,camcc.txt
+++ b/Documentation/devicetree/bindings/clock/qcom,camcc.txt
@@ -2,7 +2,8 @@
----------------------------------------------------
Required properties :
-- compatible : shall contain "qcom,cam_cc-sdm845" or "qcom,cam_cc-sdm845-v2"
+- compatible : shall contain "qcom,cam_cc-sdm845", "qcom,cam_cc-sdm845-v2" or
+ "qcom,cam_cc-sdm670"
- reg : shall contain base register location and length
- reg-names: names of registers listed in the same order as in
the reg property.
diff --git a/Documentation/devicetree/bindings/clock/qcom,dispcc.txt b/Documentation/devicetree/bindings/clock/qcom,dispcc.txt
index 87af0f6..d169c31 100644
--- a/Documentation/devicetree/bindings/clock/qcom,dispcc.txt
+++ b/Documentation/devicetree/bindings/clock/qcom,dispcc.txt
@@ -2,7 +2,8 @@
----------------------------------------------------
Required properties :
-- compatible : shall contain "qcom,dispcc-sdm845" or "qcom,dispcc-sdm845-v2".
+- compatible : shall contain "qcom,dispcc-sdm845", "qcom,dispcc-sdm845-v2" or
+ "qcom,dispcc-sdm670".
- reg : shall contain base register location and length.
- reg-names: names of registers listed in the same order as in
the reg property.
diff --git a/Documentation/devicetree/bindings/clock/qcom,gcc.txt b/Documentation/devicetree/bindings/clock/qcom,gcc.txt
index c280b92..538fb6d 100644
--- a/Documentation/devicetree/bindings/clock/qcom,gcc.txt
+++ b/Documentation/devicetree/bindings/clock/qcom,gcc.txt
@@ -18,6 +18,7 @@
"qcom,gcc-mdm9615"
"qcom,gcc-sdm845"
"qcom,gcc-sdm845-v2"
+ "qcom,gcc-sdm670"
"qcom,debugcc-sdm845"
- reg : shall contain base register location and length
diff --git a/Documentation/devicetree/bindings/clock/qcom,gpucc.txt b/Documentation/devicetree/bindings/clock/qcom,gpucc.txt
index 12676b7..aa90bc4 100644
--- a/Documentation/devicetree/bindings/clock/qcom,gpucc.txt
+++ b/Documentation/devicetree/bindings/clock/qcom,gpucc.txt
@@ -7,6 +7,8 @@
"qcom,gpucc-sdm845-v2",
"qcom,gfxcc-sdm845",
"qcom,gfxcc-sdm845-v2"
+ "qcom,gpucc-sdm670",
+ "qcom,gfxcc-sdm670"
- reg : shall contain base register offset and size.
- #clock-cells : shall contain 1.
diff --git a/Documentation/devicetree/bindings/clock/qcom,rpmh.txt b/Documentation/devicetree/bindings/clock/qcom,rpmh.txt
index c81a454..9ad7263 100644
--- a/Documentation/devicetree/bindings/clock/qcom,rpmh.txt
+++ b/Documentation/devicetree/bindings/clock/qcom,rpmh.txt
@@ -2,7 +2,8 @@
-------------------------------------------------------
Required properties :
-- compatible : must be "qcom,rpmh-clk-sdm845"
+- compatible : shall contain "qcom,rpmh-clk-sdm845" or "qcom,rpmh-clk-sdm670"
+
- #clock-cells : must contain 1
- mboxes : list of RPMh mailbox phandle and channel identifier tuples.
- mbox-names : list of names to identify the RPMh mailboxes used.
diff --git a/Documentation/devicetree/bindings/clock/qcom,videocc.txt b/Documentation/devicetree/bindings/clock/qcom,videocc.txt
index 6bd0f0b..9b53c65 100644
--- a/Documentation/devicetree/bindings/clock/qcom,videocc.txt
+++ b/Documentation/devicetree/bindings/clock/qcom,videocc.txt
@@ -2,8 +2,8 @@
----------------------------------------------------
Required properties :
-- compatible : shall contain "qcom,video_cc-sdm845" or
- "qcom,video_cc-sdm845-v2".
+- compatible : shall contain "qcom,video_cc-sdm845", "qcom,video_cc-sdm845-v2"
+ or "qcom,video_cc-sdm670".
- reg : shall contain base register location and length.
- reg-names: names of registers listed in the same order as in
the reg property.
diff --git a/Documentation/devicetree/bindings/input/tps65218-pwrbutton.txt b/Documentation/devicetree/bindings/input/tps65218-pwrbutton.txt
index 3e5b979..8682ab6 100644
--- a/Documentation/devicetree/bindings/input/tps65218-pwrbutton.txt
+++ b/Documentation/devicetree/bindings/input/tps65218-pwrbutton.txt
@@ -8,8 +8,9 @@
Required properties:
- compatible: should be "ti,tps65217-pwrbutton" or "ti,tps65218-pwrbutton"
-Required properties for TPS65218:
+Required properties:
- interrupts: should be one of the following
+ - <2>: For controllers compatible with tps65217
- <3 IRQ_TYPE_EDGE_BOTH>: For controllers compatible with tps65218
Examples:
@@ -17,6 +18,7 @@
&tps {
tps65217-pwrbutton {
compatible = "ti,tps65217-pwrbutton";
+ interrupts = <2>;
};
};
diff --git a/Documentation/devicetree/bindings/iommu/arm,smmu.txt b/Documentation/devicetree/bindings/iommu/arm,smmu.txt
index a014dac..0f8dc27 100644
--- a/Documentation/devicetree/bindings/iommu/arm,smmu.txt
+++ b/Documentation/devicetree/bindings/iommu/arm,smmu.txt
@@ -96,11 +96,6 @@
retention. No cache invalidation operations involving asid
may be used.
-- qcom,actlr:
- An array of <sid mask actlr-setting>.
- Any sid X for which X&~mask==sid will be programmed with the
- given actlr-setting.
-
- qcom,deferred-regulator-disable-delay : The time delay for deferred regulator
disable in ms. In case of unmap call, regulator is
enabled/disabled. This may introduce additional delay. For
diff --git a/Documentation/devicetree/bindings/media/video/msm-cam-fd.txt b/Documentation/devicetree/bindings/media/video/msm-cam-fd.txt
new file mode 100644
index 0000000..cf551f6
--- /dev/null
+++ b/Documentation/devicetree/bindings/media/video/msm-cam-fd.txt
@@ -0,0 +1,149 @@
+* Qualcomm Technologies, Inc. MSM Camera FD
+
+The MSM camera Face Detection device provides dependency definitions
+for enabling Camera FD HW. MSM camera FD is implemented in multiple
+device nodes. The root FD device node has properties defined to hint
+the driver about the FD HW nodes available during the probe sequence.
+Each node has multiple properties defined for interrupts, clocks and
+regulators.
+
+=======================
+Required Node Structure
+=======================
+FD root interface node takes care of the handling Face Detection high level
+driver handling and controls underlying FD hardware present.
+
+- compatible
+ Usage: required
+ Value type: <string>
+ Definition: Should be "qcom,cam-fd".
+
+- compat-hw-name
+ Usage: required
+ Value type: <string>
+ Definition: Should be "qcom,fd".
+
+- num-fd
+ Usage: required
+ Value type: <u32>
+ Definition: Number of supported FD HW blocks.
+
+Example:
+ qcom,cam-fd {
+ compatible = "qcom,cam-fd";
+ compat-hw-name = "qcom,fd";
+ num-fd = <1>;
+ };
+
+=======================
+Required Node Structure
+=======================
+FD Node provides interface for Face Detection hardware driver
+about the device register map, interrupt map, clocks, regulators.
+
+- cell-index
+ Usage: required
+ Value type: <u32>
+ Definition: Node instance number.
+
+- compatible
+ Usage: required
+ Value type: <string>
+ Definition: Should be "qcom,fd41".
+
+- reg-names
+ Usage: optional
+ Value type: <string>
+ Definition: Name of the register resources.
+
+- reg
+ Usage: optional
+ Value type: <u32>
+ Definition: Register values.
+
+- reg-cam-base
+ Usage: optional
+ Value type: <u32>
+ Definition: Offset of the register space compared to
+ to Camera base register space.
+
+- interrupt-names
+ Usage: optional
+ Value type: <string>
+ Definition: Name of the interrupt.
+
+- interrupts
+ Usage: optional
+ Value type: <u32>
+ Definition: Interrupt line associated with FD HW.
+
+- regulator-names
+ Usage: required
+ Value type: <string>
+ Definition: Name of the regulator resources for FD HW.
+
+- camss-vdd-supply
+ Usage: required
+ Value type: <phandle>
+ Definition: Regulator reference corresponding to the names listed
+ in "regulator-names".
+
+- clock-names
+ Usage: required
+ Value type: <string>
+ Definition: List of clock names required for FD HW.
+
+- clocks
+ Usage: required
+ Value type: <phandle>
+ Definition: List of clocks required for FD HW.
+
+- clock-rates
+ Usage: required
+ Value type: <u32>
+ Definition: List of clocks rates.
+
+- src-clock-name
+ Usage: required
+ Value type: <string>
+ Definition: Source clock name.
+
+- clock-cntl-level
+ Usage: required
+ Value type: <string>
+ Definition: List of strings corresponds clock-rates levels.
+ Supported strings: minsvs, lowsvs, svs, svs_l1, nominal, turbo.
+
+Examples:
+ cam_fd: qcom,fd@ac5a000 {
+ cell-index = <0>;
+ compatible = "qcom,fd41";
+ reg-names = "fd_core", "fd_wrapper";
+ reg = <0xac5a000 0x1000>,
+ <0xac5b000 0x400>;
+ reg-cam-base = <0x5a000 0x5b000>;
+ interrupt-names = "fd";
+ interrupts = <0 462 0>;
+ regulator-names = "camss-vdd";
+ camss-vdd-supply = <&titan_top_gdsc>;
+ clock-names = "gcc_ahb_clk",
+ "gcc_axi_clk",
+ "soc_ahb_clk",
+ "cpas_ahb_clk",
+ "camnoc_axi_clk",
+ "fd_core_clk_src",
+ "fd_core_clk",
+ "fd_core_uar_clk";
+ clocks = <&clock_gcc GCC_CAMERA_AHB_CLK>,
+ <&clock_gcc GCC_CAMERA_AXI_CLK>,
+ <&clock_camcc CAM_CC_SOC_AHB_CLK>,
+ <&clock_camcc CAM_CC_CPAS_AHB_CLK>,
+ <&clock_camcc CAM_CC_CAMNOC_AXI_CLK>,
+ <&clock_camcc CAM_CC_FD_CORE_CLK_SRC>,
+ <&clock_camcc CAM_CC_FD_CORE_CLK>,
+ <&clock_camcc CAM_CC_FD_CORE_UAR_CLK>;
+ src-clock-name = "fd_core_clk_src";
+ clock-cntl-level = "svs";
+ clock-rates = <0 0 0 0 0 400000000 0 0>;
+ };
+
diff --git a/Documentation/devicetree/bindings/media/video/msm-cam-icp.txt b/Documentation/devicetree/bindings/media/video/msm-cam-icp.txt
index 0c63c7e..36dad1a 100644
--- a/Documentation/devicetree/bindings/media/video/msm-cam-icp.txt
+++ b/Documentation/devicetree/bindings/media/video/msm-cam-icp.txt
@@ -105,6 +105,11 @@
Value type: <string>
Definition: List of clock names required for CDM HW.
+- src-clock-name
+ Usage: required
+ Value type: <string>
+ Definition: Source clock name.
+
- clocks
Usage: required
Value type: <phandle>
@@ -114,7 +119,7 @@
Usage: required
Value type: <string>
Definition: List of strings corresponds clock-rates levels.
- Supported strings: minsvs, lowsvs, svs, svs_l1, nominal, turbo.
+ Supported strings: lowsvs, svs, svs_l1, nominal, turbo.
- clock-rates
Usage: required
@@ -176,14 +181,20 @@
"ipe_0_axi_clk",
"ipe_0_clk",
"ipe_0_clk_src";
+ src-clock-name = "ipe_0_clk_src";
clocks = <&clock_camcc CAM_CC_IPE_0_AHB_CLK>,
<&clock_camcc CAM_CC_IPE_0_AREG_CLK>,
<&clock_camcc CAM_CC_IPE_0_AXI_CLK>,
<&clock_camcc CAM_CC_IPE_0_CLK>,
<&clock_camcc CAM_CC_IPE_0_CLK_SRC>;
- clock-rates = <80000000 400000000 0 0 600000000>;
- clock-cntl-level = "turbo";
+ clock-rates = <0 0 0 0 240000000>,
+ <0 0 0 0 404000000>,
+ <0 0 0 0 480000000>,
+ <0 0 0 0 538000000>,
+ <0 0 0 0 600000000>;
+ clock-cntl-level = "lowsvs", "svs",
+ "svs_l1", "nominal", "turbo";
};
qcom,ipe1 {
@@ -196,14 +207,20 @@
"ipe_1_axi_clk",
"ipe_1_clk",
"ipe_1_clk_src";
+ src-clock-name = "ipe_1_clk_src";
clocks = <&clock_camcc CAM_CC_IPE_1_AHB_CLK>,
<&clock_camcc CAM_CC_IPE_1_AREG_CLK>,
<&clock_camcc CAM_CC_IPE_1_AXI_CLK>,
<&clock_camcc CAM_CC_IPE_1_CLK>,
<&clock_camcc CAM_CC_IPE_1_CLK_SRC>;
- clock-rates = <80000000 400000000 0 0 600000000>;
- clock-cntl-level = "turbo";
+ clock-rates = <0 0 0 0 240000000>,
+ <0 0 0 0 404000000>,
+ <0 0 0 0 480000000>,
+ <0 0 0 0 538000000>,
+ <0 0 0 0 600000000>;
+ clock-cntl-level = "lowsvs", "svs",
+ "svs_l1", "nominal", "turbo";
};
bps: qcom,bps {
@@ -216,13 +233,19 @@
"bps_axi_clk",
"bps_clk",
"bps_clk_src";
+ src-clock-name = "bps_clk_src";
clocks = <&clock_camcc CAM_CC_BPS_AHB_CLK>,
<&clock_camcc CAM_CC_BPS_AREG_CLK>,
<&clock_camcc CAM_CC_BPS_AXI_CLK>,
<&clock_camcc CAM_CC_BPS_CLK>,
<&clock_camcc CAM_CC_BPS_CLK_SRC>;
- clock-rates = <80000000 400000000 0 0 600000000>;
- clock-cntl-level = "turbo";
+ clock-rates = <0 0 0 0 200000000>,
+ <0 0 0 0 404000000>,
+ <0 0 0 0 480000000>,
+ <0 0 0 0 600000000>,
+ <0 0 0 0 600000000>;
+ clock-cntl-level = "lowsvs", "svs",
+ "svs_l1", "nominal", "turbo";
};
diff --git a/Documentation/devicetree/bindings/pil/pil-q6v5-mss.txt b/Documentation/devicetree/bindings/pil/pil-q6v5-mss.txt
index bc844de..271703f 100644
--- a/Documentation/devicetree/bindings/pil/pil-q6v5-mss.txt
+++ b/Documentation/devicetree/bindings/pil/pil-q6v5-mss.txt
@@ -90,6 +90,7 @@
wordline clamp, and compiler memory clamp during MSS restart.
- qcom,qdsp6v56-1-10: Boolean- Present if the qdsp version is v56 1.10
- qcom,override-acc-1: Override the default ACC settings with this value if present.
+- qcom,minidump-id: Unique id for each subsystem
One child node to represent the MBA image may be specified, when the MBA image
needs to be loaded in a specifically carved out memory region.
diff --git a/Documentation/devicetree/bindings/power/supply/qcom/smb138x-charger.txt b/Documentation/devicetree/bindings/power/supply/qcom/smb138x-charger.txt
index 92ef23c..fd2729f 100644
--- a/Documentation/devicetree/bindings/power/supply/qcom/smb138x-charger.txt
+++ b/Documentation/devicetree/bindings/power/supply/qcom/smb138x-charger.txt
@@ -43,6 +43,13 @@
Definition: Boolean flag which indicates that the charger should not draw
current from any of its input sources (USB, DC).
+- qcom,use-extcon
+ Usage: optional
+ Value type: <empty>
+ Definition: Boolean flag which specify that smb138x will act as main charger
+ to do extcon USB calls. If not defined, other charger driver can
+ act as main charger to do extcon USB calls.
+
- qcom,fcc-max-ua
Usage: optional
Value type: <u32>
diff --git a/Documentation/devicetree/bindings/power/supply/tps65217_charger.txt b/Documentation/devicetree/bindings/power/supply/tps65217_charger.txt
index 98d131a..a11072c 100644
--- a/Documentation/devicetree/bindings/power/supply/tps65217_charger.txt
+++ b/Documentation/devicetree/bindings/power/supply/tps65217_charger.txt
@@ -2,11 +2,16 @@
Required Properties:
-compatible: "ti,tps65217-charger"
+-interrupts: TPS65217 interrupt numbers for the AC and USB charger input change.
+ Should be <0> for the USB charger and <1> for the AC adapter.
+-interrupt-names: Should be "USB" and "AC"
This node is a subnode of the tps65217 PMIC.
Example:
tps65217-charger {
- compatible = "ti,tps65090-charger";
+ compatible = "ti,tps65217-charger";
+ interrupts = <0>, <1>;
+ interrupt-names = "USB", "AC";
};
diff --git a/Makefile b/Makefile
index 5894331..3849d63 100644
--- a/Makefile
+++ b/Makefile
@@ -1,6 +1,6 @@
VERSION = 4
PATCHLEVEL = 9
-SUBLEVEL = 40
+SUBLEVEL = 41
EXTRAVERSION =
NAME = Roaring Lionus
diff --git a/arch/arc/kernel/mcip.c b/arch/arc/kernel/mcip.c
index f39142a..be131b2 100644
--- a/arch/arc/kernel/mcip.c
+++ b/arch/arc/kernel/mcip.c
@@ -10,6 +10,7 @@
#include <linux/smp.h>
#include <linux/irq.h>
+#include <linux/irqchip/chained_irq.h>
#include <linux/spinlock.h>
#include <asm/irqflags-arcv2.h>
#include <asm/mcip.h>
@@ -221,10 +222,13 @@
static void idu_cascade_isr(struct irq_desc *desc)
{
struct irq_domain *idu_domain = irq_desc_get_handler_data(desc);
+ struct irq_chip *core_chip = irq_desc_get_chip(desc);
irq_hw_number_t core_hwirq = irqd_to_hwirq(irq_desc_get_irq_data(desc));
irq_hw_number_t idu_hwirq = core_hwirq - idu_first_hwirq;
+ chained_irq_enter(core_chip, desc);
generic_handle_irq(irq_find_mapping(idu_domain, idu_hwirq));
+ chained_irq_exit(core_chip, desc);
}
static int idu_irq_map(struct irq_domain *d, unsigned int virq, irq_hw_number_t hwirq)
diff --git a/arch/arm/boot/dts/am57xx-idk-common.dtsi b/arch/arm/boot/dts/am57xx-idk-common.dtsi
index 03cec62..db858ff 100644
--- a/arch/arm/boot/dts/am57xx-idk-common.dtsi
+++ b/arch/arm/boot/dts/am57xx-idk-common.dtsi
@@ -294,7 +294,7 @@
};
&usb2 {
- dr_mode = "otg";
+ dr_mode = "peripheral";
};
&mmc2 {
diff --git a/arch/arm/boot/dts/omap3-n900.dts b/arch/arm/boot/dts/omap3-n900.dts
index 87ca50b..4d448f1 100644
--- a/arch/arm/boot/dts/omap3-n900.dts
+++ b/arch/arm/boot/dts/omap3-n900.dts
@@ -734,6 +734,8 @@
vmmc_aux-supply = <&vsim>;
bus-width = <8>;
non-removable;
+ no-sdio;
+ no-sd;
};
&mmc3 {
diff --git a/arch/arm/configs/s3c2410_defconfig b/arch/arm/configs/s3c2410_defconfig
index bc4bfe0..60d3fec 100644
--- a/arch/arm/configs/s3c2410_defconfig
+++ b/arch/arm/configs/s3c2410_defconfig
@@ -86,9 +86,9 @@
CONFIG_NETFILTER=y
CONFIG_NF_CONNTRACK=m
CONFIG_NF_CONNTRACK_EVENTS=y
-CONFIG_NF_CT_PROTO_DCCP=m
-CONFIG_NF_CT_PROTO_SCTP=m
-CONFIG_NF_CT_PROTO_UDPLITE=m
+CONFIG_NF_CT_PROTO_DCCP=y
+CONFIG_NF_CT_PROTO_SCTP=y
+CONFIG_NF_CT_PROTO_UDPLITE=y
CONFIG_NF_CONNTRACK_AMANDA=m
CONFIG_NF_CONNTRACK_FTP=m
CONFIG_NF_CONNTRACK_H323=m
diff --git a/arch/arm/mach-omap2/omap_hwmod.c b/arch/arm/mach-omap2/omap_hwmod.c
index 1052b29..b5c1714 100644
--- a/arch/arm/mach-omap2/omap_hwmod.c
+++ b/arch/arm/mach-omap2/omap_hwmod.c
@@ -790,14 +790,14 @@
int ret = 0;
char name[MOD_CLK_MAX_NAME_LEN];
struct clk *clk;
+ static const char modck[] = "_mod_ck";
- /* +7 magic comes from '_mod_ck' suffix */
- if (strlen(oh->name) + 7 > MOD_CLK_MAX_NAME_LEN)
+ if (strlen(oh->name) >= MOD_CLK_MAX_NAME_LEN - strlen(modck))
pr_warn("%s: warning: cropping name for %s\n", __func__,
oh->name);
- strncpy(name, oh->name, MOD_CLK_MAX_NAME_LEN - 7);
- strcat(name, "_mod_ck");
+ strlcpy(name, oh->name, MOD_CLK_MAX_NAME_LEN - strlen(modck));
+ strlcat(name, modck, MOD_CLK_MAX_NAME_LEN);
clk = clk_get(NULL, name);
if (!IS_ERR(clk)) {
diff --git a/arch/arm64/boot/dts/qcom/Makefile b/arch/arm64/boot/dts/qcom/Makefile
index 52af31c..f2cf941 100644
--- a/arch/arm64/boot/dts/qcom/Makefile
+++ b/arch/arm64/boot/dts/qcom/Makefile
@@ -68,14 +68,22 @@
ifeq ($(CONFIG_BUILD_ARM64_DT_OVERLAY),y)
dtbo-$(CONFIG_ARCH_SDM670) += \
sdm670-cdp-overlay.dtbo \
- sdm670-mtp-overlay.dtbo
+ sdm670-mtp-overlay.dtbo \
+ sdm670-rumi-overlay.dtbo \
+ sdm670-pm660a-cdp-overlay.dtbo \
+ sdm670-pm660a-mtp-overlay.dtbo
sdm670-cdp-overlay.dtbo-base := sdm670.dtb
sdm670-mtp-overlay.dtbo-base := sdm670.dtb
+sdm670-rumi-overlay.dtbo-base := sdm670.dtb
+sdm670-pm660a-cdp-overlay.dtbo-base := sdm670.dtb
+sdm670-pm660a-mtp-overlay.dtbo-base := sdm670.dtb
else
dtb-$(CONFIG_ARCH_SDM670) += sdm670-rumi.dtb \
sdm670-mtp.dtb \
- sdm670-cdp.dtb
+ sdm670-cdp.dtb \
+ sdm670-pm660a-mtp.dtb \
+ sdm670-pm660a-cdp.dtb
endif
always := $(dtb-y)
diff --git a/arch/arm64/boot/dts/qcom/pm660.dtsi b/arch/arm64/boot/dts/qcom/pm660.dtsi
index 8075b9e..48d68a7 100644
--- a/arch/arm64/boot/dts/qcom/pm660.dtsi
+++ b/arch/arm64/boot/dts/qcom/pm660.dtsi
@@ -14,7 +14,7 @@
#include <dt-bindings/interrupt-controller/irq.h>
&spmi_bus {
- qcom,pm660@0 {
+ pm660_0: qcom,pm660@0 {
compatible ="qcom,spmi-pmic";
reg = <0x0 SPMI_USID>;
#address-cells = <2>;
@@ -245,149 +245,10 @@
};
};
- pm660_charger: qcom,qpnp-smb2 {
- compatible = "qcom,qpnp-smb2";
- #address-cells = <1>;
- #size-cells = <1>;
-
- qcom,pmic-revid = <&pm660_revid>;
-
- io-channels = <&pm660_rradc 8>,
- <&pm660_rradc 10>,
- <&pm660_rradc 3>,
- <&pm660_rradc 4>;
- io-channel-names = "charger_temp",
- "charger_temp_max",
- "usbin_i",
- "usbin_v";
-
- qcom,wipower-max-uw = <5000000>;
-
- /* Enable after the qusb_phy0 device node is added */
- /* dpdm-supply = <&qusb_phy0>; */
-
- qcom,thermal-mitigation
- = <3000000 2500000 2000000 1500000
- 1000000 500000>;
-
- qcom,chgr@1000 {
- reg = <0x1000 0x100>;
- interrupts =
- <0x0 0x10 0x0 IRQ_TYPE_EDGE_RISING>,
- <0x0 0x10 0x1 IRQ_TYPE_EDGE_RISING>,
- <0x0 0x10 0x2 IRQ_TYPE_EDGE_RISING>,
- <0x0 0x10 0x3 IRQ_TYPE_EDGE_RISING>,
- <0x0 0x10 0x4 IRQ_TYPE_EDGE_RISING>;
-
- interrupt-names = "chg-error",
- "chg-state-change",
- "step-chg-state-change",
- "step-chg-soc-update-fail",
- "step-chg-soc-update-request";
- };
-
- qcom,otg@1100 {
- reg = <0x1100 0x100>;
- interrupts = <0x0 0x11 0x0 IRQ_TYPE_EDGE_BOTH>,
- <0x0 0x11 0x1 IRQ_TYPE_EDGE_BOTH>,
- <0x0 0x11 0x2 IRQ_TYPE_EDGE_BOTH>,
- <0x0 0x11 0x3 IRQ_TYPE_EDGE_BOTH>;
-
- interrupt-names = "otg-fail",
- "otg-overcurrent",
- "otg-oc-dis-sw-sts",
- "testmode-change-detect";
- };
-
- qcom,bat-if@1200 {
- reg = <0x1200 0x100>;
- interrupts =
- <0x0 0x12 0x0 IRQ_TYPE_EDGE_RISING>,
- <0x0 0x12 0x1 IRQ_TYPE_EDGE_BOTH>,
- <0x0 0x12 0x2 IRQ_TYPE_EDGE_BOTH>,
- <0x0 0x12 0x3 IRQ_TYPE_EDGE_BOTH>,
- <0x0 0x12 0x4 IRQ_TYPE_EDGE_BOTH>,
- <0x0 0x12 0x5 IRQ_TYPE_EDGE_BOTH>;
-
- interrupt-names = "bat-temp",
- "bat-ocp",
- "bat-ov",
- "bat-low",
- "bat-therm-or-id-missing",
- "bat-terminal-missing";
- };
-
- qcom,usb-chgpth@1300 {
- reg = <0x1300 0x100>;
- interrupts =
- <0x0 0x13 0x0 IRQ_TYPE_EDGE_BOTH>,
- <0x0 0x13 0x1 IRQ_TYPE_EDGE_BOTH>,
- <0x0 0x13 0x2 IRQ_TYPE_EDGE_BOTH>,
- <0x0 0x13 0x3 IRQ_TYPE_EDGE_BOTH>,
- <0x0 0x13 0x4 IRQ_TYPE_EDGE_BOTH>,
- <0x0 0x13 0x5 IRQ_TYPE_EDGE_RISING>,
- <0x0 0x13 0x6 IRQ_TYPE_EDGE_RISING>,
- <0x0 0x13 0x7 IRQ_TYPE_EDGE_RISING>;
-
- interrupt-names = "usbin-collapse",
- "usbin-lt-3p6v",
- "usbin-uv",
- "usbin-ov",
- "usbin-plugin",
- "usbin-src-change",
- "usbin-icl-change",
- "type-c-change";
- };
-
- qcom,dc-chgpth@1400 {
- reg = <0x1400 0x100>;
- interrupts =
- <0x0 0x14 0x0 IRQ_TYPE_EDGE_BOTH>,
- <0x0 0x14 0x1 IRQ_TYPE_EDGE_BOTH>,
- <0x0 0x14 0x2 IRQ_TYPE_EDGE_BOTH>,
- <0x0 0x14 0x3 IRQ_TYPE_EDGE_BOTH>,
- <0x0 0x14 0x4 IRQ_TYPE_EDGE_BOTH>,
- <0x0 0x14 0x5 IRQ_TYPE_EDGE_BOTH>,
- <0x0 0x14 0x6 IRQ_TYPE_EDGE_RISING>;
-
- interrupt-names = "dcin-collapse",
- "dcin-lt-3p6v",
- "dcin-uv",
- "dcin-ov",
- "dcin-plugin",
- "div2-en-dg",
- "dcin-icl-change";
- };
-
- qcom,chgr-misc@1600 {
- reg = <0x1600 0x100>;
- interrupts =
- <0x0 0x16 0x0 IRQ_TYPE_EDGE_RISING>,
- <0x0 0x16 0x1 IRQ_TYPE_EDGE_RISING>,
- <0x0 0x16 0x2 IRQ_TYPE_EDGE_BOTH>,
- <0x0 0x16 0x3 IRQ_TYPE_EDGE_BOTH>,
- <0x0 0x16 0x4 IRQ_TYPE_EDGE_BOTH>,
- <0x0 0x16 0x5 IRQ_TYPE_EDGE_BOTH>,
- <0x0 0x16 0x6 IRQ_TYPE_EDGE_FALLING>,
- <0x0 0x16 0x7 IRQ_TYPE_EDGE_BOTH>;
-
- interrupt-names = "wdog-snarl",
- "wdog-bark",
- "aicl-fail",
- "aicl-done",
- "high-duty-cycle",
- "input-current-limiting",
- "temperature-change",
- "switcher-power-ok";
- };
- };
-
pm660_pdphy: qcom,usb-pdphy@1700 {
compatible = "qcom,qpnp-pdphy";
reg = <0x1700 0x100>;
vdd-pdphy-supply = <&pm660l_l7>;
- vbus-supply = <&smb2_vbus>;
- vconn-supply = <&smb2_vconn>;
interrupts = <0x0 0x17 0x0 IRQ_TYPE_EDGE_RISING>,
<0x0 0x17 0x1 IRQ_TYPE_EDGE_RISING>,
<0x0 0x17 0x2 IRQ_TYPE_EDGE_RISING>,
@@ -497,73 +358,6 @@
qcom,pmic-revid = <&pm660_revid>;
};
- pm660_fg: qpnp,fg {
- compatible = "qcom,fg-gen3";
- #address-cells = <1>;
- #size-cells = <1>;
- qcom,pmic-revid = <&pm660_revid>;
- io-channels = <&pm660_rradc 0>,
- <&pm660_rradc 7>;
- io-channel-names = "rradc_batt_id",
- "rradc_die_temp";
- qcom,rradc-base = <0x4500>;
- qcom,fg-esr-timer-awake = <96 96>;
- qcom,fg-esr-timer-asleep = <256 256>;
- qcom,fg-esr-timer-charging = <0 96>;
- qcom,cycle-counter-en;
- status = "okay";
-
- qcom,fg-batt-soc@4000 {
- status = "okay";
- reg = <0x4000 0x100>;
- interrupts = <0x0 0x40 0x0 IRQ_TYPE_EDGE_BOTH>,
- <0x0 0x40 0x1 IRQ_TYPE_EDGE_BOTH>,
- <0x0 0x40 0x2
- IRQ_TYPE_EDGE_RISING>,
- <0x0 0x40 0x3
- IRQ_TYPE_EDGE_RISING>,
- <0x0 0x40 0x4 IRQ_TYPE_EDGE_BOTH>,
- <0x0 0x40 0x5
- IRQ_TYPE_EDGE_RISING>,
- <0x0 0x40 0x6 IRQ_TYPE_EDGE_BOTH>,
- <0x0 0x40 0x7 IRQ_TYPE_EDGE_BOTH>;
- interrupt-names = "soc-update",
- "soc-ready",
- "bsoc-delta",
- "msoc-delta",
- "msoc-low",
- "msoc-empty",
- "msoc-high",
- "msoc-full";
- };
-
- qcom,fg-batt-info@4100 {
- status = "okay";
- reg = <0x4100 0x100>;
- interrupts = <0x0 0x41 0x0 IRQ_TYPE_EDGE_BOTH>,
- <0x0 0x41 0x1 IRQ_TYPE_EDGE_BOTH>,
- <0x0 0x41 0x2 IRQ_TYPE_EDGE_BOTH>,
- <0x0 0x41 0x3 IRQ_TYPE_EDGE_BOTH>,
- <0x0 0x41 0x6 IRQ_TYPE_EDGE_BOTH>;
- interrupt-names = "vbatt-pred-delta",
- "vbatt-low",
- "esr-delta",
- "batt-missing",
- "batt-temp-delta";
- };
-
- qcom,fg-memif@4400 {
- status = "okay";
- reg = <0x4400 0x100>;
- interrupts = <0x0 0x44 0x0 IRQ_TYPE_EDGE_BOTH>,
- <0x0 0x44 0x1 IRQ_TYPE_EDGE_BOTH>,
- <0x0 0x44 0x2 IRQ_TYPE_EDGE_BOTH>;
- interrupt-names = "ima-rdy",
- "mem-xcp",
- "dma-grant";
- };
- };
-
bcl@4200 {
compatible = "qcom,msm-bcl-lmh";
reg = <0x4200 0xff>,
@@ -579,32 +373,11 @@
};
};
- qcom,pm660@1 {
+ pm660_1: qcom,pm660@1 {
compatible ="qcom,spmi-pmic";
reg = <0x1 SPMI_USID>;
#address-cells = <2>;
#size-cells = <0>;
-
- pm660_haptics: qcom,haptics@c000 {
- compatible = "qcom,qpnp-haptics";
- reg = <0xc000 0x100>;
- interrupts = <0x1 0xc0 0x0 IRQ_TYPE_EDGE_BOTH>,
- <0x1 0xc0 0x1 IRQ_TYPE_EDGE_BOTH>;
- interrupt-names = "hap-sc-irq", "hap-play-irq";
- qcom,pmic-revid = <&pm660_revid>;
- qcom,pmic-misc = <&pm660_misc>;
- qcom,misc-clk-trim-error-reg = <0xf3>;
- qcom,actuator-type = <0>;
- qcom,play-mode = "direct";
- qcom,vmax-mv = <3200>;
- qcom,ilim-ma = <800>;
- qcom,sc-dbc-cycles = <8>;
- qcom,wave-play-rate-us = <6667>;
- qcom,en-brake;
- qcom,lra-high-z = "opt0";
- qcom,lra-auto-res-mode = "qwd";
- qcom,lra-res-cal-period = <4>;
- };
};
};
diff --git a/arch/arm64/boot/dts/qcom/pm660l.dtsi b/arch/arm64/boot/dts/qcom/pm660l.dtsi
index 11101ffe..771154a 100644
--- a/arch/arm64/boot/dts/qcom/pm660l.dtsi
+++ b/arch/arm64/boot/dts/qcom/pm660l.dtsi
@@ -185,35 +185,6 @@
};
};
- pm660l_wled: qcom,leds@d800 {
- compatible = "qcom,qpnp-wled";
- reg = <0xd800 0x100>,
- <0xd900 0x100>;
- reg-names = "qpnp-wled-ctrl-base",
- "qpnp-wled-sink-base";
- interrupts = <0x3 0xd8 0x1 IRQ_TYPE_EDGE_RISING>;
- interrupt-names = "ovp-irq";
- linux,name = "wled";
- linux,default-trigger = "bkl-trigger";
- qcom,fdbk-output = "auto";
- qcom,vref-uv = <127500>;
- qcom,switch-freq-khz = <800>;
- qcom,ovp-mv = <29600>;
- qcom,ilim-ma = <970>;
- qcom,boost-duty-ns = <26>;
- qcom,mod-freq-khz = <9600>;
- qcom,dim-mode = "hybrid";
- qcom,hyb-thres = <625>;
- qcom,sync-dly-us = <800>;
- qcom,fs-curr-ua = <25000>;
- qcom,cons-sync-write-delay-us = <1000>;
- qcom,led-strings-list = [00 01 02];
- qcom,loop-auto-gm-en;
- qcom,pmic-revid = <&pm660l_revid>;
- qcom,auto-calibration-enable;
- status = "ok";
- };
-
flash_led: qcom,leds@d300 {
compatible = "qcom,qpnp-flash-led-v2";
reg = <0xd300 0x100>;
@@ -322,89 +293,5 @@
qcom,default-led-trigger = "switch1_trigger";
};
};
-
- pm660l_lcdb: qpnp-lcdb@ec00 {
- compatible = "qcom,qpnp-lcdb-regulator";
- #address-cells = <1>;
- #size-cells = <1>;
- reg = <0xec00 0x100>;
- interrupts = <0x3 0xec 0x1 IRQ_TYPE_EDGE_RISING>;
- interrupt-names = "sc-irq";
-
- qcom,pmic-revid = <&pm660l_revid>;
-
- lcdb_ldo_vreg: ldo {
- label = "ldo";
- regulator-name = "lcdb_ldo";
- regulator-min-microvolt = <4000000>;
- regulator-max-microvolt = <6000000>;
- };
-
- lcdb_ncp_vreg: ncp {
- label = "ncp";
- regulator-name = "lcdb_ncp";
- regulator-min-microvolt = <4000000>;
- regulator-max-microvolt = <6000000>;
- };
- };
-
- pm660a_oledb: qpnp-oledb@e000 {
- compatible = "qcom,qpnp-oledb-regulator";
- #address-cells = <1>;
- #size-cells = <1>;
- qcom,pmic-revid = <&pm660l_revid>;
- reg = <0xe000 0x100>;
- qcom,pbs-client = <&pm660l_pbs>;
-
- label = "oledb";
- regulator-name = "regulator-oledb";
- regulator-min-microvolt = <5000000>;
- regulator-max-microvolt = <8100000>;
-
- qcom,swire-control;
- qcom,ext-pin-control;
- status = "disabled";
- };
-
- pm660a_labibb: qpnp-labibb-regulator {
- compatible = "qcom,qpnp-labibb-regulator";
- #address-cells = <1>;
- #size-cells = <1>;
- qcom,pmic-revid = <&pm660l_revid>;
- qcom,swire-control;
- status = "disabled";
-
- ibb_regulator: qcom,ibb@dc00 {
- reg = <0xdc00 0x100>;
- reg-names = "ibb_reg";
- regulator-name = "ibb_reg";
-
- regulator-min-microvolt = <4000000>;
- regulator-max-microvolt = <6300000>;
-
- qcom,qpnp-ibb-min-voltage = <1400000>;
- qcom,qpnp-ibb-step-size = <100000>;
- qcom,qpnp-ibb-slew-rate = <2000000>;
- qcom,qpnp-ibb-init-voltage = <4000000>;
- qcom,qpnp-ibb-init-amoled-voltage = <4000000>;
- };
-
- lab_regulator: qcom,lab@de00 {
- reg = <0xde00 0x100>;
- reg-names = "lab";
- regulator-name = "lab_reg";
-
- regulator-min-microvolt = <4600000>;
- regulator-max-microvolt = <6100000>;
-
- qcom,qpnp-lab-min-voltage = <4600000>;
- qcom,qpnp-lab-step-size = <100000>;
- qcom,qpnp-lab-slew-rate = <5000>;
- qcom,qpnp-lab-init-voltage = <4600000>;
- qcom,qpnp-lab-init-amoled-voltage = <4600000>;
-
- qcom,notify-lab-vreg-ok-sts;
- };
- };
};
};
diff --git a/arch/arm64/boot/dts/qcom/sdm670-cdp-overlay.dts b/arch/arm64/boot/dts/qcom/sdm670-cdp-overlay.dts
index f0c820f..9feb5b4 100644
--- a/arch/arm64/boot/dts/qcom/sdm670-cdp-overlay.dts
+++ b/arch/arm64/boot/dts/qcom/sdm670-cdp-overlay.dts
@@ -22,9 +22,12 @@
#include "sdm670-cdp.dtsi"
/ {
- model = "Qualcomm Technologies, Inc. SDM670 CDP";
+ model = "Qualcomm Technologies, Inc. SDM670 PM660 + PM660L CDP";
compatible = "qcom,sdm670-cdp", "qcom,sdm670", "qcom,cdp";
qcom,msm-id = <336 0x0>;
qcom,board-id = <1 0>;
+ qcom,pmic-id = <0x0001001b 0x0101011a 0x0 0x0>,
+ <0x0001001b 0x0102001a 0x0 0x0>,
+ <0x0001001b 0x0201011a 0x0 0x0>;
};
diff --git a/arch/arm64/boot/dts/qcom/sdm670-cdp.dts b/arch/arm64/boot/dts/qcom/sdm670-cdp.dts
index 7e5947b..27882dd 100644
--- a/arch/arm64/boot/dts/qcom/sdm670-cdp.dts
+++ b/arch/arm64/boot/dts/qcom/sdm670-cdp.dts
@@ -17,7 +17,10 @@
#include "sdm670-cdp.dtsi"
/ {
- model = "Qualcomm Technologies, Inc. SDM670 CDP";
+ model = "Qualcomm Technologies, Inc. SDM670 PM660 + PM660L CDP";
compatible = "qcom,sdm670-cdp", "qcom,sdm670", "qcom,cdp";
qcom,board-id = <1 0>;
+ qcom,pmic-id = <0x0001001b 0x0101011a 0x0 0x0>,
+ <0x0001001b 0x0102001a 0x0 0x0>,
+ <0x0001001b 0x0201011a 0x0 0x0>;
};
diff --git a/arch/arm64/boot/dts/qcom/sdm670-cdp.dtsi b/arch/arm64/boot/dts/qcom/sdm670-cdp.dtsi
index 0cf48a3..4f40678 100644
--- a/arch/arm64/boot/dts/qcom/sdm670-cdp.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm670-cdp.dtsi
@@ -10,6 +10,8 @@
* GNU General Public License for more details.
*/
+#include "sdm670-pmic-overlay.dtsi"
+
&qupv3_se9_2uart {
status = "disabled";
};
diff --git a/arch/arm64/boot/dts/qcom/sdm670-gpu.dtsi b/arch/arm64/boot/dts/qcom/sdm670-gpu.dtsi
new file mode 100644
index 0000000..d13aa15
--- /dev/null
+++ b/arch/arm64/boot/dts/qcom/sdm670-gpu.dtsi
@@ -0,0 +1,310 @@
+/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+&soc {
+
+ pil_gpu: qcom,kgsl-hyp {
+ compatible = "qcom,pil-tz-generic";
+ qcom,pas-id = <13>;
+ qcom,firmware-name = "a615_zap";
+ };
+
+ msm_bus: qcom,kgsl-busmon{
+ label = "kgsl-busmon";
+ compatible = "qcom,kgsl-busmon";
+ };
+
+ gpubw: qcom,gpubw {
+ compatible = "qcom,devbw";
+ governor = "bw_vbif";
+ qcom,src-dst-ports = <26 512>;
+ qcom,bw-tbl =
+ < 0 /* off */ >,
+ < 381 /* 100 MHz */ >,
+ < 762 /* 200 MHz */ >,
+ < 1144 /* 300 MHz */ >,
+ < 1720 /* 451 MHz */ >,
+ < 2086 /* 547 MHz */ >,
+ < 2597 /* 681 MHz */ >,
+ < 3147 /* 825 MHz */ >,
+ < 3879 /* 1017 MHz */ >,
+ < 5161 /* 1353 MHz */ >,
+ < 5931 /* 1555 MHz */ >,
+ < 6881 /* 1804 MHz */ >;
+ };
+
+ msm_gpu: qcom,kgsl-3d0@5000000 {
+ label = "kgsl-3d0";
+ compatible = "qcom,kgsl-3d0", "qcom,kgsl-3d";
+ status = "ok";
+ reg = <0x5000000 0x40000>;
+ reg-names = "kgsl_3d0_reg_memory";
+ interrupts = <0 300 0>;
+ interrupt-names = "kgsl_3d0_irq";
+ qcom,id = <0>;
+
+ qcom,chipid = <0x06010500>;
+
+ qcom,initial-pwrlevel = <3>;
+
+ qcom,gpu-quirk-hfi-use-reg;
+
+ /* <HZ/12> */
+ qcom,idle-timeout = <80>;
+ qcom,no-nap;
+
+ qcom,highest-bank-bit = <14>;
+
+ qcom,min-access-length = <64>;
+
+ qcom,ubwc-mode = <2>;
+
+ /* size in bytes */
+ qcom,snapshot-size = <1048576>;
+
+ /* base addr, size */
+ qcom,gpu-qdss-stm = <0x161c0000 0x40000>;
+
+ clocks = <&clock_gfx GPU_CC_GX_GFX3D_CLK>,
+ <&clock_gpucc GPU_CC_CXO_CLK>,
+ <&clock_gcc GCC_DDRSS_GPU_AXI_CLK>,
+ <&clock_gcc GCC_GPU_MEMNOC_GFX_CLK>,
+ <&clock_gpucc GPU_CC_CX_GMU_CLK>,
+ <&clock_gpucc GPU_CC_AHB_CLK>;
+
+ clock-names = "core_clk", "rbbmtimer_clk", "mem_clk",
+ "mem_iface_clk", "gmu_clk", "ahb_clk";
+
+ /* Bus Scale Settings */
+ qcom,gpubw-dev = <&gpubw>;
+ qcom,bus-control;
+ qcom,msm-bus,name = "grp3d";
+ qcom,bus-width = <32>;
+ qcom,msm-bus,num-cases = <12>;
+ qcom,msm-bus,num-paths = <1>;
+ qcom,msm-bus,vectors-KBps =
+ <26 512 0 0>,
+ <26 512 0 400000>, /* 1 bus=100 */
+ <26 512 0 800000>, /* 2 bus=200 */
+ <26 512 0 1200000>, /* 3 bus=300 */
+ <26 512 0 1804000>, /* 4 bus=451 */
+ <26 512 0 2188000>, /* 5 bus=547 */
+ <26 512 0 2724000>, /* 6 bus=681 */
+ <26 512 0 3300000>, /* 7 bus=825 */
+ <26 512 0 4068000>, /* 8 bus=1017 */
+ <26 512 0 5412000>, /* 9 bus=1353 */
+ <26 512 0 6220000>, /* 10 bus=1555 */
+ <26 512 0 7216000>; /* 11 bus=1804 */
+
+ /* GDSC regulator names */
+ regulator-names = "vddcx", "vdd";
+ /* GDSC oxili regulators */
+ vddcx-supply = <&gpu_cx_gdsc>;
+ vdd-supply = <&gpu_gx_gdsc>;
+
+ /* GPU related llc slices */
+ cache-slice-names = "gpu", "gpuhtw";
+ cache-slices = <&llcc 12>, <&llcc 11>;
+
+ /* CPU latency parameter */
+ qcom,pm-qos-active-latency = <660>;
+ qcom,pm-qos-wakeup-latency = <460>;
+
+ /* Enable context aware freq. scaling */
+ qcom,enable-ca-jump;
+ /* Context aware jump busy penalty in us */
+ qcom,ca-busy-penalty = <12000>;
+ /* Context aware jump target power level */
+ qcom,ca-target-pwrlevel = <1>;
+
+ /* GPU Mempools */
+ qcom,gpu-mempools {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ compatible = "qcom,gpu-mempools";
+
+ /* 4K Page Pool configuration */
+ qcom,gpu-mempool@0 {
+ reg = <0>;
+ qcom,mempool-page-size = <4096>;
+ qcom,mempool-allocate;
+ };
+ /* 8K Page Pool configuration */
+ qcom,gpu-mempool@1 {
+ reg = <1>;
+ qcom,mempool-page-size = <8192>;
+ qcom,mempool-allocate;
+ };
+ /* 64K Page Pool configuration */
+ qcom,gpu-mempool@2 {
+ reg = <2>;
+ qcom,mempool-page-size = <65536>;
+ qcom,mempool-reserved = <256>;
+ };
+ /* 1M Page Pool configuration */
+ qcom,gpu-mempool@3 {
+ reg = <3>;
+ qcom,mempool-page-size = <1048576>;
+ qcom,mempool-reserved = <32>;
+ };
+ };
+
+ /* Power levels */
+ qcom,gpu-pwrlevels {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ compatible = "qcom,gpu-pwrlevels";
+
+ /* SVS_L1 */
+ qcom,gpu-pwrlevel@0 {
+ reg = <0>;
+ qcom,gpu-freq = <430000000>;
+ qcom,bus-freq = <11>;
+ qcom,bus-min = <10>;
+ qcom,bus-max = <11>;
+ };
+
+ /* SVS */
+ qcom,gpu-pwrlevel@1 {
+ reg = <1>;
+ qcom,gpu-freq = <355000000>;
+ qcom,bus-freq = <9>;
+ qcom,bus-min = <8>;
+ qcom,bus-max = <10>;
+ };
+
+ /* LOW SVS */
+ qcom,gpu-pwrlevel@2 {
+ reg = <2>;
+ qcom,gpu-freq = <267000000>;
+ qcom,bus-freq = <6>;
+ qcom,bus-min = <4>;
+ qcom,bus-max = <8>;
+ };
+
+ /* MIN SVS */
+ qcom,gpu-pwrlevel@3 {
+ reg = <3>;
+ qcom,gpu-freq = <180000000>;
+ qcom,bus-freq = <4>;
+ qcom,bus-min = <3>;
+ qcom,bus-max = <5>;
+ };
+
+ /* XO */
+ qcom,gpu-pwrlevel@4 {
+ reg = <4>;
+ qcom,gpu-freq = <0>;
+ qcom,bus-freq = <0>;
+ qcom,bus-min = <0>;
+ qcom,bus-max = <0>;
+ };
+ };
+
+ };
+
+ kgsl_msm_iommu: qcom,kgsl-iommu {
+ compatible = "qcom,kgsl-smmu-v2";
+
+ reg = <0x05040000 0x10000>;
+ qcom,protect = <0x40000 0x10000>;
+ qcom,micro-mmu-control = <0x6000>;
+
+ clocks =<&clock_gcc GCC_GPU_CFG_AHB_CLK>,
+ <&clock_gcc GCC_DDRSS_GPU_AXI_CLK>,
+ <&clock_gcc GCC_GPU_MEMNOC_GFX_CLK>;
+
+ clock-names = "iface_clk", "mem_clk", "mem_iface_clk";
+
+ qcom,secure_align_mask = <0xfff>;
+ qcom,retention;
+ qcom,hyp_secure_alloc;
+
+ gfx3d_user: gfx3d_user {
+ compatible = "qcom,smmu-kgsl-cb";
+ label = "gfx3d_user";
+ iommus = <&kgsl_smmu 0>;
+ qcom,gpu-offset = <0x48000>;
+ };
+
+ gfx3d_secure: gfx3d_secure {
+ compatible = "qcom,smmu-kgsl-cb";
+ iommus = <&kgsl_smmu 2>;
+ };
+ };
+
+ gmu: qcom,gmu {
+ label = "kgsl-gmu";
+ compatible = "qcom,gpu-gmu";
+
+ reg =
+ <0x506a000 0x31000>,
+ <0xb200000 0x300000>,
+ <0xc200000 0x10000>;
+ reg-names =
+ "kgsl_gmu_reg",
+ "kgsl_gmu_pdc_reg",
+ "kgsl_gmu_cpr_reg";
+
+ interrupts = <0 304 0>, <0 305 0>;
+ interrupt-names = "kgsl_hfi_irq", "kgsl_gmu_irq";
+
+ qcom,msm-bus,name = "cnoc";
+ qcom,msm-bus,num-cases = <2>;
+ qcom,msm-bus,num-paths = <1>;
+ qcom,msm-bus,vectors-KBps =
+ <26 10036 0 0>, /* CNOC off */
+ <26 10036 0 100>; /* CNOC on */
+
+ regulator-names = "vddcx", "vdd";
+ vddcx-supply = <&gpu_cx_gdsc>;
+ vdd-supply = <&gpu_gx_gdsc>;
+
+
+ clocks = <&clock_gpucc GPU_CC_CX_GMU_CLK>,
+ <&clock_gpucc GPU_CC_CXO_CLK>,
+ <&clock_gcc GCC_DDRSS_GPU_AXI_CLK>,
+ <&clock_gcc GCC_GPU_MEMNOC_GFX_CLK>,
+ <&clock_gpucc GPU_CC_AHB_CLK>;
+
+ clock-names = "gmu_clk", "cxo_clk", "axi_clk",
+ "memnoc_clk", "ahb_clk";
+
+ qcom,gmu-pwrlevels {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ compatible = "qcom,gmu-pwrlevels";
+
+ qcom,gmu-pwrlevel@0 {
+ reg = <0>;
+ qcom,gmu-freq = <200000000>;
+ };
+
+ qcom,gmu-pwrlevel@1 {
+ reg = <1>;
+ qcom,gmu-freq = <0>;
+ };
+ };
+
+ gmu_user: gmu_user {
+ compatible = "qcom,smmu-gmu-user-cb";
+ iommus = <&kgsl_smmu 4>;
+ };
+
+ gmu_kernel: gmu_kernel {
+ compatible = "qcom,smmu-gmu-kernel-cb";
+ iommus = <&kgsl_smmu 5>;
+ };
+ };
+};
diff --git a/arch/arm64/boot/dts/qcom/sdm670-mtp-overlay.dts b/arch/arm64/boot/dts/qcom/sdm670-mtp-overlay.dts
index c8537bc..65c16c1 100644
--- a/arch/arm64/boot/dts/qcom/sdm670-mtp-overlay.dts
+++ b/arch/arm64/boot/dts/qcom/sdm670-mtp-overlay.dts
@@ -22,8 +22,11 @@
#include "sdm670-mtp.dtsi"
/ {
- model = "Qualcomm Technologies, Inc. SDM670 MTP";
+ model = "Qualcomm Technologies, Inc. SDM670 PM660 + PM660L MTP";
compatible = "qcom,sdm670-mtp", "qcom,sdm670", "qcom,mtp";
qcom,msm-id = <336 0x0>;
qcom,board-id = <8 0>;
+ qcom,pmic-id = <0x0001001b 0x0101011a 0x0 0x0>,
+ <0x0001001b 0x0102001a 0x0 0x0>,
+ <0x0001001b 0x0201011a 0x0 0x0>;
};
diff --git a/arch/arm64/boot/dts/qcom/sdm670-mtp.dts b/arch/arm64/boot/dts/qcom/sdm670-mtp.dts
index 1de40b7..38a9fae 100644
--- a/arch/arm64/boot/dts/qcom/sdm670-mtp.dts
+++ b/arch/arm64/boot/dts/qcom/sdm670-mtp.dts
@@ -17,7 +17,10 @@
#include "sdm670-mtp.dtsi"
/ {
- model = "Qualcomm Technologies, Inc. SDM670 MTP";
+ model = "Qualcomm Technologies, Inc. SDM670 PM660 + PM660L MTP";
compatible = "qcom,sdm670-mtp", "qcom,sdm670", "qcom,mtp";
qcom,board-id = <8 0>;
+ qcom,pmic-id = <0x0001001b 0x0101011a 0x0 0x0>,
+ <0x0001001b 0x0102001a 0x0 0x0>,
+ <0x0001001b 0x0201011a 0x0 0x0>;
};
diff --git a/arch/arm64/boot/dts/qcom/sdm670-mtp.dtsi b/arch/arm64/boot/dts/qcom/sdm670-mtp.dtsi
index 0cf48a3..4f40678 100644
--- a/arch/arm64/boot/dts/qcom/sdm670-mtp.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm670-mtp.dtsi
@@ -10,6 +10,8 @@
* GNU General Public License for more details.
*/
+#include "sdm670-pmic-overlay.dtsi"
+
&qupv3_se9_2uart {
status = "disabled";
};
diff --git a/arch/arm64/boot/dts/qcom/sdm670-pm660a-cdp-overlay.dts b/arch/arm64/boot/dts/qcom/sdm670-pm660a-cdp-overlay.dts
new file mode 100644
index 0000000..b3d2357
--- /dev/null
+++ b/arch/arm64/boot/dts/qcom/sdm670-pm660a-cdp-overlay.dts
@@ -0,0 +1,34 @@
+/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+/dts-v1/;
+/plugin/;
+
+#include <dt-bindings/clock/qcom,gcc-sdm845.h>
+#include <dt-bindings/clock/qcom,camcc-sdm845.h>
+#include <dt-bindings/clock/qcom,dispcc-sdm845.h>
+#include <dt-bindings/clock/qcom,rpmh.h>
+#include <dt-bindings/interrupt-controller/arm-gic.h>
+
+#include "sdm670-cdp.dtsi"
+#include "pm660a.dtsi"
+
+/ {
+ model = "Qualcomm Technologies, Inc. SDM670 PM660 + PM660A CDP";
+ compatible = "qcom,sdm670-cdp", "qcom,sdm670", "qcom,cdp";
+ qcom,msm-id = <336 0x0>;
+ qcom,board-id = <1 0>;
+ qcom,pmic-id = <0x0001001b 0x0001011a 0x0 0x0>,
+ <0x0001001b 0x0002001a 0x0 0x0>,
+ <0x0001001b 0x0202001a 0x0 0x0>;
+};
+
diff --git a/arch/arm64/boot/dts/qcom/sdm670-pm660a-cdp.dts b/arch/arm64/boot/dts/qcom/sdm670-pm660a-cdp.dts
new file mode 100644
index 0000000..5cf3513
--- /dev/null
+++ b/arch/arm64/boot/dts/qcom/sdm670-pm660a-cdp.dts
@@ -0,0 +1,27 @@
+/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+
+/dts-v1/;
+
+#include "sdm670.dtsi"
+#include "sdm670-cdp.dtsi"
+#include "pm660a.dtsi"
+
+/ {
+ model = "Qualcomm Technologies, Inc. SDM670 PM660 + PM660A CDP";
+ compatible = "qcom,sdm670-cdp", "qcom,sdm670", "qcom,cdp";
+ qcom,board-id = <1 0>;
+ qcom,pmic-id = <0x0001001b 0x0001011a 0x0 0x0>,
+ <0x0001001b 0x0002001a 0x0 0x0>,
+ <0x0001001b 0x0202001a 0x0 0x0>;
+};
diff --git a/arch/arm64/boot/dts/qcom/sdm670-pm660a-mtp-overlay.dts b/arch/arm64/boot/dts/qcom/sdm670-pm660a-mtp-overlay.dts
new file mode 100644
index 0000000..ff3270d
--- /dev/null
+++ b/arch/arm64/boot/dts/qcom/sdm670-pm660a-mtp-overlay.dts
@@ -0,0 +1,33 @@
+/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+/dts-v1/;
+/plugin/;
+
+#include <dt-bindings/clock/qcom,gcc-sdm845.h>
+#include <dt-bindings/clock/qcom,camcc-sdm845.h>
+#include <dt-bindings/clock/qcom,dispcc-sdm845.h>
+#include <dt-bindings/clock/qcom,rpmh.h>
+#include <dt-bindings/interrupt-controller/arm-gic.h>
+
+#include "sdm670-mtp.dtsi"
+#include "pm660a.dtsi"
+
+/ {
+ model = "Qualcomm Technologies, Inc. SDM670 PM660 + PM660A MTP";
+ compatible = "qcom,sdm670-mtp", "qcom,sdm670", "qcom,mtp";
+ qcom,msm-id = <336 0x0>;
+ qcom,board-id = <8 0>;
+ qcom,pmic-id = <0x0001001b 0x0001011a 0x0 0x0>,
+ <0x0001001b 0x0002001a 0x0 0x0>,
+ <0x0001001b 0x0202001a 0x0 0x0>;
+};
diff --git a/arch/arm64/boot/dts/qcom/sdm670-pm660a-mtp.dts b/arch/arm64/boot/dts/qcom/sdm670-pm660a-mtp.dts
new file mode 100644
index 0000000..febd5d9
--- /dev/null
+++ b/arch/arm64/boot/dts/qcom/sdm670-pm660a-mtp.dts
@@ -0,0 +1,27 @@
+/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+
+/dts-v1/;
+
+#include "sdm670.dtsi"
+#include "sdm670-mtp.dtsi"
+#include "pm660a.dtsi"
+
+/ {
+ model = "Qualcomm Technologies, Inc. SDM670 PM660 + PM660A MTP";
+ compatible = "qcom,sdm670-mtp", "qcom,sdm670", "qcom,mtp";
+ qcom,board-id = <8 0>;
+ qcom,pmic-id = <0x0001001b 0x0001011a 0x0 0x0>,
+ <0x0001001b 0x0002001a 0x0 0x0>,
+ <0x0001001b 0x0202001a 0x0 0x0>;
+};
diff --git a/arch/arm64/boot/dts/qcom/sdm670-pmic-overlay.dtsi b/arch/arm64/boot/dts/qcom/sdm670-pmic-overlay.dtsi
new file mode 100644
index 0000000..cd8bfba
--- /dev/null
+++ b/arch/arm64/boot/dts/qcom/sdm670-pmic-overlay.dtsi
@@ -0,0 +1,367 @@
+/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+&pm660_0{
+ pm660_charger: qcom,qpnp-smb2 {
+ compatible = "qcom,qpnp-smb2";
+ #address-cells = <1>;
+ #size-cells = <1>;
+
+ qcom,pmic-revid = <&pm660_revid>;
+
+ io-channels = <&pm660_rradc 8>,
+ <&pm660_rradc 10>,
+ <&pm660_rradc 3>,
+ <&pm660_rradc 4>;
+ io-channel-names = "charger_temp",
+ "charger_temp_max",
+ "usbin_i",
+ "usbin_v";
+
+ qcom,wipower-max-uw = <5000000>;
+
+ /* Enable after the qusb_phy0 device node is added */
+ /* dpdm-supply = <&qusb_phy0>; */
+
+ qcom,thermal-mitigation
+ = <3000000 2500000 2000000 1500000
+ 1000000 500000>;
+
+ qcom,chgr@1000 {
+ reg = <0x1000 0x100>;
+ interrupts =
+ <0x0 0x10 0x0 IRQ_TYPE_EDGE_RISING>,
+ <0x0 0x10 0x1 IRQ_TYPE_EDGE_RISING>,
+ <0x0 0x10 0x2 IRQ_TYPE_EDGE_RISING>,
+ <0x0 0x10 0x3 IRQ_TYPE_EDGE_RISING>,
+ <0x0 0x10 0x4 IRQ_TYPE_EDGE_RISING>;
+
+ interrupt-names = "chg-error",
+ "chg-state-change",
+ "step-chg-state-change",
+ "step-chg-soc-update-fail",
+ "step-chg-soc-update-request";
+ };
+
+ qcom,otg@1100 {
+ reg = <0x1100 0x100>;
+ interrupts = <0x0 0x11 0x0 IRQ_TYPE_EDGE_BOTH>,
+ <0x0 0x11 0x1 IRQ_TYPE_EDGE_BOTH>,
+ <0x0 0x11 0x2 IRQ_TYPE_EDGE_BOTH>,
+ <0x0 0x11 0x3 IRQ_TYPE_EDGE_BOTH>;
+
+ interrupt-names = "otg-fail",
+ "otg-overcurrent",
+ "otg-oc-dis-sw-sts",
+ "testmode-change-detect";
+ };
+
+ qcom,bat-if@1200 {
+ reg = <0x1200 0x100>;
+ interrupts =
+ <0x0 0x12 0x0 IRQ_TYPE_EDGE_RISING>,
+ <0x0 0x12 0x1 IRQ_TYPE_EDGE_BOTH>,
+ <0x0 0x12 0x2 IRQ_TYPE_EDGE_BOTH>,
+ <0x0 0x12 0x3 IRQ_TYPE_EDGE_BOTH>,
+ <0x0 0x12 0x4 IRQ_TYPE_EDGE_BOTH>,
+ <0x0 0x12 0x5 IRQ_TYPE_EDGE_BOTH>;
+
+ interrupt-names = "bat-temp",
+ "bat-ocp",
+ "bat-ov",
+ "bat-low",
+ "bat-therm-or-id-missing",
+ "bat-terminal-missing";
+ };
+
+ qcom,usb-chgpth@1300 {
+ reg = <0x1300 0x100>;
+ interrupts =
+ <0x0 0x13 0x0 IRQ_TYPE_EDGE_BOTH>,
+ <0x0 0x13 0x1 IRQ_TYPE_EDGE_BOTH>,
+ <0x0 0x13 0x2 IRQ_TYPE_EDGE_BOTH>,
+ <0x0 0x13 0x3 IRQ_TYPE_EDGE_BOTH>,
+ <0x0 0x13 0x4 IRQ_TYPE_EDGE_BOTH>,
+ <0x0 0x13 0x5 IRQ_TYPE_EDGE_RISING>,
+ <0x0 0x13 0x6 IRQ_TYPE_EDGE_RISING>,
+ <0x0 0x13 0x7 IRQ_TYPE_EDGE_RISING>;
+
+ interrupt-names = "usbin-collapse",
+ "usbin-lt-3p6v",
+ "usbin-uv",
+ "usbin-ov",
+ "usbin-plugin",
+ "usbin-src-change",
+ "usbin-icl-change",
+ "type-c-change";
+ };
+
+ qcom,dc-chgpth@1400 {
+ reg = <0x1400 0x100>;
+ interrupts =
+ <0x0 0x14 0x0 IRQ_TYPE_EDGE_BOTH>,
+ <0x0 0x14 0x1 IRQ_TYPE_EDGE_BOTH>,
+ <0x0 0x14 0x2 IRQ_TYPE_EDGE_BOTH>,
+ <0x0 0x14 0x3 IRQ_TYPE_EDGE_BOTH>,
+ <0x0 0x14 0x4 IRQ_TYPE_EDGE_BOTH>,
+ <0x0 0x14 0x5 IRQ_TYPE_EDGE_BOTH>,
+ <0x0 0x14 0x6 IRQ_TYPE_EDGE_RISING>;
+
+ interrupt-names = "dcin-collapse",
+ "dcin-lt-3p6v",
+ "dcin-uv",
+ "dcin-ov",
+ "dcin-plugin",
+ "div2-en-dg",
+ "dcin-icl-change";
+ };
+
+ qcom,chgr-misc@1600 {
+ reg = <0x1600 0x100>;
+ interrupts =
+ <0x0 0x16 0x0 IRQ_TYPE_EDGE_RISING>,
+ <0x0 0x16 0x1 IRQ_TYPE_EDGE_RISING>,
+ <0x0 0x16 0x2 IRQ_TYPE_EDGE_BOTH>,
+ <0x0 0x16 0x3 IRQ_TYPE_EDGE_BOTH>,
+ <0x0 0x16 0x4 IRQ_TYPE_EDGE_BOTH>,
+ <0x0 0x16 0x5 IRQ_TYPE_EDGE_BOTH>,
+ <0x0 0x16 0x6 IRQ_TYPE_EDGE_FALLING>,
+ <0x0 0x16 0x7 IRQ_TYPE_EDGE_BOTH>;
+
+ interrupt-names = "wdog-snarl",
+ "wdog-bark",
+ "aicl-fail",
+ "aicl-done",
+ "high-duty-cycle",
+ "input-current-limiting",
+ "temperature-change",
+ "switcher-power-ok";
+ };
+ smb2_vbus: qcom,smb2-vbus {
+ regulator-name = "smb2-vbus";
+ };
+
+ smb2_vconn: qcom,smb2-vconn {
+ regulator-name = "smb2-vconn";
+ };
+ };
+
+ pm660_fg: qpnp,fg {
+ compatible = "qcom,fg-gen3";
+ #address-cells = <1>;
+ #size-cells = <1>;
+ qcom,pmic-revid = <&pm660_revid>;
+ io-channels = <&pm660_rradc 0>,
+ <&pm660_rradc 7>;
+ io-channel-names = "rradc_batt_id",
+ "rradc_die_temp";
+ qcom,rradc-base = <0x4500>;
+ qcom,fg-esr-timer-awake = <96 96>;
+ qcom,fg-esr-timer-asleep = <256 256>;
+ qcom,fg-esr-timer-charging = <0 96>;
+ qcom,cycle-counter-en;
+ status = "okay";
+
+ qcom,fg-batt-soc@4000 {
+ status = "okay";
+ reg = <0x4000 0x100>;
+ interrupts = <0x0 0x40 0x0 IRQ_TYPE_EDGE_BOTH>,
+ <0x0 0x40 0x1 IRQ_TYPE_EDGE_BOTH>,
+ <0x0 0x40 0x2
+ IRQ_TYPE_EDGE_RISING>,
+ <0x0 0x40 0x3
+ IRQ_TYPE_EDGE_RISING>,
+ <0x0 0x40 0x4 IRQ_TYPE_EDGE_BOTH>,
+ <0x0 0x40 0x5
+ IRQ_TYPE_EDGE_RISING>,
+ <0x0 0x40 0x6 IRQ_TYPE_EDGE_BOTH>,
+ <0x0 0x40 0x7 IRQ_TYPE_EDGE_BOTH>;
+ interrupt-names = "soc-update",
+ "soc-ready",
+ "bsoc-delta",
+ "msoc-delta",
+ "msoc-low",
+ "msoc-empty",
+ "msoc-high",
+ "msoc-full";
+ };
+
+ qcom,fg-batt-info@4100 {
+ status = "okay";
+ reg = <0x4100 0x100>;
+ interrupts = <0x0 0x41 0x0 IRQ_TYPE_EDGE_BOTH>,
+ <0x0 0x41 0x1 IRQ_TYPE_EDGE_BOTH>,
+ <0x0 0x41 0x2 IRQ_TYPE_EDGE_BOTH>,
+ <0x0 0x41 0x3 IRQ_TYPE_EDGE_BOTH>,
+ <0x0 0x41 0x6 IRQ_TYPE_EDGE_BOTH>;
+ interrupt-names = "vbatt-pred-delta",
+ "vbatt-low",
+ "esr-delta",
+ "batt-missing",
+ "batt-temp-delta";
+ };
+
+ qcom,fg-memif@4400 {
+ status = "okay";
+ reg = <0x4400 0x100>;
+ interrupts = <0x0 0x44 0x0 IRQ_TYPE_EDGE_BOTH>,
+ <0x0 0x44 0x1 IRQ_TYPE_EDGE_BOTH>,
+ <0x0 0x44 0x2 IRQ_TYPE_EDGE_BOTH>;
+ interrupt-names = "ima-rdy",
+ "mem-xcp",
+ "dma-grant";
+ };
+ };
+};
+
+&pm660_1 {
+ pm660_haptics: qcom,haptics@c000 {
+ compatible = "qcom,qpnp-haptics";
+ reg = <0xc000 0x100>;
+ interrupts = <0x1 0xc0 0x0 IRQ_TYPE_EDGE_BOTH>,
+ <0x1 0xc0 0x1 IRQ_TYPE_EDGE_BOTH>;
+ interrupt-names = "hap-sc-irq", "hap-play-irq";
+ qcom,pmic-revid = <&pm660_revid>;
+ qcom,pmic-misc = <&pm660_misc>;
+ qcom,misc-clk-trim-error-reg = <0xf3>;
+ qcom,actuator-type = <0>;
+ qcom,play-mode = "direct";
+ qcom,vmax-mv = <3200>;
+ qcom,ilim-ma = <800>;
+ qcom,sc-dbc-cycles = <8>;
+ qcom,wave-play-rate-us = <6667>;
+ qcom,en-brake;
+ qcom,lra-high-z = "opt0";
+ qcom,lra-auto-res-mode = "qwd";
+ qcom,lra-res-cal-period = <4>;
+ };
+};
+
+&pm660l_3 {
+ pm660l_wled: qcom,leds@d800 {
+ compatible = "qcom,qpnp-wled";
+ reg = <0xd800 0x100>,
+ <0xd900 0x100>;
+ reg-names = "qpnp-wled-ctrl-base",
+ "qpnp-wled-sink-base";
+ interrupts = <0x3 0xd8 0x1 IRQ_TYPE_EDGE_RISING>;
+ interrupt-names = "ovp-irq";
+ linux,name = "wled";
+ linux,default-trigger = "bkl-trigger";
+ qcom,fdbk-output = "auto";
+ qcom,vref-uv = <127500>;
+ qcom,switch-freq-khz = <800>;
+ qcom,ovp-mv = <29600>;
+ qcom,ilim-ma = <970>;
+ qcom,boost-duty-ns = <26>;
+ qcom,mod-freq-khz = <9600>;
+ qcom,dim-mode = "hybrid";
+ qcom,hyb-thres = <625>;
+ qcom,sync-dly-us = <800>;
+ qcom,fs-curr-ua = <25000>;
+ qcom,cons-sync-write-delay-us = <1000>;
+ qcom,led-strings-list = [00 01 02];
+ qcom,loop-auto-gm-en;
+ qcom,pmic-revid = <&pm660l_revid>;
+ qcom,auto-calibration-enable;
+ status = "ok";
+ };
+
+ pm660l_lcdb: qpnp-lcdb@ec00 {
+ compatible = "qcom,qpnp-lcdb-regulator";
+ #address-cells = <1>;
+ #size-cells = <1>;
+ reg = <0xec00 0x100>;
+ interrupts = <0x3 0xec 0x1 IRQ_TYPE_EDGE_RISING>;
+ interrupt-names = "sc-irq";
+
+ qcom,pmic-revid = <&pm660l_revid>;
+
+ lcdb_ldo_vreg: ldo {
+ label = "ldo";
+ regulator-name = "lcdb_ldo";
+ regulator-min-microvolt = <4000000>;
+ regulator-max-microvolt = <6000000>;
+ };
+
+ lcdb_ncp_vreg: ncp {
+ label = "ncp";
+ regulator-name = "lcdb_ncp";
+ regulator-min-microvolt = <4000000>;
+ regulator-max-microvolt = <6000000>;
+ };
+ };
+
+ pm660a_oledb: qpnp-oledb@e000 {
+ compatible = "qcom,qpnp-oledb-regulator";
+ #address-cells = <1>;
+ #size-cells = <1>;
+ qcom,pmic-revid = <&pm660l_revid>;
+ reg = <0xe000 0x100>;
+ qcom,pbs-client = <&pm660l_pbs>;
+
+ label = "oledb";
+ regulator-name = "regulator-oledb";
+ regulator-min-microvolt = <5000000>;
+ regulator-max-microvolt = <8100000>;
+
+ qcom,swire-control;
+ qcom,ext-pin-control;
+ status = "disabled";
+ };
+
+ pm660a_labibb: qpnp-labibb-regulator {
+ compatible = "qcom,qpnp-labibb-regulator";
+ #address-cells = <1>;
+ #size-cells = <1>;
+ qcom,pmic-revid = <&pm660l_revid>;
+ qcom,swire-control;
+ status = "disabled";
+
+ ibb_regulator: qcom,ibb@dc00 {
+ reg = <0xdc00 0x100>;
+ reg-names = "ibb_reg";
+ regulator-name = "ibb_reg";
+
+ regulator-min-microvolt = <4000000>;
+ regulator-max-microvolt = <6300000>;
+
+ qcom,qpnp-ibb-min-voltage = <1400000>;
+ qcom,qpnp-ibb-step-size = <100000>;
+ qcom,qpnp-ibb-slew-rate = <2000000>;
+ qcom,qpnp-ibb-init-voltage = <4000000>;
+ qcom,qpnp-ibb-init-amoled-voltage = <4000000>;
+ };
+
+ lab_regulator: qcom,lab@de00 {
+ reg = <0xde00 0x100>;
+ reg-names = "lab";
+ regulator-name = "lab_reg";
+
+ regulator-min-microvolt = <4600000>;
+ regulator-max-microvolt = <6100000>;
+
+ qcom,qpnp-lab-min-voltage = <4600000>;
+ qcom,qpnp-lab-step-size = <100000>;
+ qcom,qpnp-lab-slew-rate = <5000>;
+ qcom,qpnp-lab-init-voltage = <4600000>;
+ qcom,qpnp-lab-init-amoled-voltage = <4600000>;
+
+ qcom,notify-lab-vreg-ok-sts;
+ };
+ };
+};
+
+&pm660_pdphy {
+ vbus-supply = <&smb2_vbus>;
+ vconn-supply = <&smb2_vconn>;
+};
diff --git a/arch/arm64/boot/dts/qcom/sdm670-regulator.dtsi b/arch/arm64/boot/dts/qcom/sdm670-regulator.dtsi
index 0a8c49f..1f76288 100644
--- a/arch/arm64/boot/dts/qcom/sdm670-regulator.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm670-regulator.dtsi
@@ -628,13 +628,3 @@
};
};
};
-
-&pm660_charger {
- smb2_vbus: qcom,smb2-vbus {
- regulator-name = "smb2-vbus";
- };
-
- smb2_vconn: qcom,smb2-vconn {
- regulator-name = "smb2-vconn";
- };
-};
diff --git a/arch/arm64/boot/dts/qcom/sdm670-rumi-overlay.dts b/arch/arm64/boot/dts/qcom/sdm670-rumi-overlay.dts
new file mode 100644
index 0000000..4a24d87
--- /dev/null
+++ b/arch/arm64/boot/dts/qcom/sdm670-rumi-overlay.dts
@@ -0,0 +1,30 @@
+/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+/dts-v1/;
+/plugin/;
+
+#include <dt-bindings/interrupt-controller/arm-gic.h>
+#include "sdm670-rumi.dtsi"
+
+/ {
+ model = "Qualcomm Technologies, Inc. SDM670 RUMI";
+ compatible = "qcom,sdm670-rumi", "qcom,sdm670", "qcom,rumi";
+ qcom,msm-id = <336 0x0>;
+ qcom,board-id = <15 0>;
+};
+
+&soc {
+ wdog: qcom,wdt@17980000{
+ status = "disabled";
+ };
+};
diff --git a/arch/arm64/boot/dts/qcom/sdm670-rumi.dtsi b/arch/arm64/boot/dts/qcom/sdm670-rumi.dtsi
index 4eb6dca..ca9d8c7 100644
--- a/arch/arm64/boot/dts/qcom/sdm670-rumi.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm670-rumi.dtsi
@@ -10,17 +10,7 @@
* GNU General Public License for more details.
*/
-/{
- aliases {
- serial0 = &qupv3_se10_2uart;
- serial1 = &qupv3_se9_2uart;
- spi0 = &qupv3_se8_spi;
- i2c0 = &qupv3_se10_i2c;
- i2c1 = &qupv3_se3_i2c;
- hsuart0 = &qupv3_se6_4uart;
- };
-
-};
+#include "sdm670-pmic-overlay.dtsi"
&soc {
/* Delete all regulators */
@@ -158,3 +148,38 @@
status = "ok";
};
+
+&usb0 {
+ /delete-property/ qcom,usb-dbm;
+ /delete-property/ extcon;
+ qcom,charging-disabled;
+ dwc3@a600000 {
+ maximum-speed = "high-speed";
+ usb-phy = <&qusb_phy0>, <&usb_nop_phy>;
+ };
+};
+
+&qusb_phy0 {
+ reg = <0x088e2000 0x4>,
+ <0x0a720000 0x9500>;
+ reg-names = "qusb_phy_base",
+ "emu_phy_base";
+ qcom,emulation;
+ qcom,emu-init-seq = <0x19 0x1404
+ 0x20 0x1414
+ 0x79 0x1410
+ 0x00 0x1418
+ 0x99 0x1404
+ 0x04 0x1408
+ 0xd9 0x1404>;
+
+ qcom,emu-dcm-reset-seq = <0x5 0x14 /* 0x1 0x14 for E1.2 */
+ 0x100000 0x20
+ 0x0 0x20
+ 0x1a0 0x20 /* 0x220 0x20 for E1.2 */
+ 0x80 0x28>;
+};
+
+&usb_qmp_dp_phy {
+ status = "disabled";
+};
diff --git a/arch/arm64/boot/dts/qcom/sdm670-usb.dtsi b/arch/arm64/boot/dts/qcom/sdm670-usb.dtsi
new file mode 100644
index 0000000..bd35cf2
--- /dev/null
+++ b/arch/arm64/boot/dts/qcom/sdm670-usb.dtsi
@@ -0,0 +1,35 @@
+/*
+ * Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include "sdm845-usb.dtsi"
+
+&soc {
+ /delete-node/ ssusb@a800000;
+ /delete-node/ qusb@88e3000;
+ /delete-node/ ssphy@88eb000;
+};
+
+&usb0 {
+ extcon = <&pm660_pdphy>, <&pm660_pdphy>, <0> /* <&eud> */;
+};
+
+&qusb_phy0 {
+ vdd-supply = <&pm660l_l1>;
+ vdda18-supply = <&pm660_l10>;
+ vdda33-supply = <&pm660l_l7>;
+};
+
+&usb_qmp_dp_phy {
+ vdd-supply = <&pm660_l1>;
+ core-supply = <&pm660l_l1>;
+};
diff --git a/arch/arm64/boot/dts/qcom/sdm670.dtsi b/arch/arm64/boot/dts/qcom/sdm670.dtsi
index 42c2aed..6e987f1 100644
--- a/arch/arm64/boot/dts/qcom/sdm670.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm670.dtsi
@@ -33,9 +33,6 @@
ufshc1 = &ufshc_mem; /* Embedded UFS slot */
sdhc1 = &sdhc_1; /* SDC1 eMMC slot */
sdhc2 = &sdhc_2; /* SDC2 SD Card slot */
- };
-
- aliases {
serial0 = &qupv3_se12_2uart;
spi0 = &qupv3_se8_spi;
i2c0 = &qupv3_se10_i2c;
@@ -75,6 +72,9 @@
compatible = "arm,arch-cache";
qcom,dump-size = <0x9000>;
};
+ L1_TLB_0: l1-tlb {
+ qcom,dump-size = <0x3000>;
+ };
};
CPU1: cpu@100 {
@@ -100,6 +100,9 @@
compatible = "arm,arch-cache";
qcom,dump-size = <0x9000>;
};
+ L1_TLB_100: l1-tlb {
+ qcom,dump-size = <0x3000>;
+ };
};
CPU2: cpu@200 {
@@ -125,6 +128,9 @@
compatible = "arm,arch-cache";
qcom,dump-size = <0x9000>;
};
+ L1_TLB_200: l1-tlb {
+ qcom,dump-size = <0x3000>;
+ };
};
CPU3: cpu@300 {
@@ -150,6 +156,9 @@
compatible = "arm,arch-cache";
qcom,dump-size = <0x9000>;
};
+ L1_TLB_300: l1-tlb {
+ qcom,dump-size = <0x3000>;
+ };
};
CPU4: cpu@400 {
@@ -175,6 +184,9 @@
compatible = "arm,arch-cache";
qcom,dump-size = <0x9000>;
};
+ L1_TLB_400: l1-tlb {
+ qcom,dump-size = <0x3000>;
+ };
};
CPU5: cpu@500 {
@@ -200,6 +212,9 @@
compatible = "arm,arch-cache";
qcom,dump-size = <0x9000>;
};
+ L1_TLB_500: l1-tlb {
+ qcom,dump-size = <0x3000>;
+ };
};
CPU6: cpu@600 {
@@ -225,6 +240,9 @@
compatible = "arm,arch-cache";
qcom,dump-size = <0x12000>;
};
+ L1_TLB_600: l1-tlb {
+ qcom,dump-size = <0x3c000>;
+ };
};
CPU7: cpu@700 {
@@ -250,6 +268,9 @@
compatible = "arm,arch-cache";
qcom,dump-size = <0x12000>;
};
+ L1_TLB_700: l1-tlb {
+ qcom,dump-size = <0x3c000>;
+ };
};
cpu-map {
@@ -1027,31 +1048,31 @@
qcom,dump-node = <&L1_I_0>;
qcom,dump-id = <0x60>;
};
- qcom,l1_i_cache1 {
+ qcom,l1_i_cache100 {
qcom,dump-node = <&L1_I_100>;
qcom,dump-id = <0x61>;
};
- qcom,l1_i_cache2 {
+ qcom,l1_i_cache200 {
qcom,dump-node = <&L1_I_200>;
qcom,dump-id = <0x62>;
};
- qcom,l1_i_cache3 {
+ qcom,l1_i_cache300 {
qcom,dump-node = <&L1_I_300>;
qcom,dump-id = <0x63>;
};
- qcom,l1_i_cache100 {
+ qcom,l1_i_cache400 {
qcom,dump-node = <&L1_I_400>;
qcom,dump-id = <0x64>;
};
- qcom,l1_i_cache101 {
+ qcom,l1_i_cache500 {
qcom,dump-node = <&L1_I_500>;
qcom,dump-id = <0x65>;
};
- qcom,l1_i_cache102 {
+ qcom,l1_i_cache600 {
qcom,dump-node = <&L1_I_600>;
qcom,dump-id = <0x66>;
};
- qcom,l1_i_cache103 {
+ qcom,l1_i_cache700 {
qcom,dump-node = <&L1_I_700>;
qcom,dump-id = <0x67>;
};
@@ -1059,31 +1080,31 @@
qcom,dump-node = <&L1_D_0>;
qcom,dump-id = <0x80>;
};
- qcom,l1_d_cache1 {
+ qcom,l1_d_cache100 {
qcom,dump-node = <&L1_D_100>;
qcom,dump-id = <0x81>;
};
- qcom,l1_d_cache2 {
+ qcom,l1_d_cache200 {
qcom,dump-node = <&L1_D_200>;
qcom,dump-id = <0x82>;
};
- qcom,l1_d_cache3 {
+ qcom,l1_d_cache300 {
qcom,dump-node = <&L1_D_300>;
qcom,dump-id = <0x83>;
};
- qcom,l1_d_cache100 {
+ qcom,l1_d_cache400 {
qcom,dump-node = <&L1_D_400>;
qcom,dump-id = <0x84>;
};
- qcom,l1_d_cache101 {
+ qcom,l1_d_cache500 {
qcom,dump-node = <&L1_D_500>;
qcom,dump-id = <0x85>;
};
- qcom,l1_d_cache102 {
+ qcom,l1_d_cache600 {
qcom,dump-node = <&L1_D_600>;
qcom,dump-id = <0x86>;
};
- qcom,l1_d_cache103 {
+ qcom,l1_d_cache700 {
qcom,dump-node = <&L1_D_700>;
qcom,dump-id = <0x87>;
};
@@ -1095,6 +1116,38 @@
qcom,dump-node = <&LLCC_2>;
qcom,dump-id = <0x141>;
};
+ qcom,l1_tlb_dump0 {
+ qcom,dump-node = <&L1_TLB_0>;
+ qcom,dump-id = <0x20>;
+ };
+ qcom,l1_tlb_dump100 {
+ qcom,dump-node = <&L1_TLB_100>;
+ qcom,dump-id = <0x21>;
+ };
+ qcom,l1_tlb_dump200 {
+ qcom,dump-node = <&L1_TLB_200>;
+ qcom,dump-id = <0x22>;
+ };
+ qcom,l1_tlb_dump300 {
+ qcom,dump-node = <&L1_TLB_300>;
+ qcom,dump-id = <0x23>;
+ };
+ qcom,l1_tlb_dump400 {
+ qcom,dump-node = <&L1_TLB_400>;
+ qcom,dump-id = <0x24>;
+ };
+ qcom,l1_tlb_dump500 {
+ qcom,dump-node = <&L1_TLB_500>;
+ qcom,dump-id = <0x25>;
+ };
+ qcom,l1_tlb_dump600 {
+ qcom,dump-node = <&L1_TLB_600>;
+ qcom,dump-id = <0x26>;
+ };
+ qcom,l1_tlb_dump700 {
+ qcom,dump-node = <&L1_TLB_700>;
+ qcom,dump-id = <0x27>;
+ };
};
kryo3xx-erp {
@@ -2049,3 +2102,5 @@
#include "pm660l.dtsi"
#include "sdm670-regulator.dtsi"
#include "sdm670-audio.dtsi"
+#include "sdm670-usb.dtsi"
+#include "sdm670-gpu.dtsi"
diff --git a/arch/arm64/boot/dts/qcom/sdm845-camera.dtsi b/arch/arm64/boot/dts/qcom/sdm845-camera.dtsi
index f54cbdc..8df879a 100644
--- a/arch/arm64/boot/dts/qcom/sdm845-camera.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm845-camera.dtsi
@@ -338,6 +338,22 @@
};
};
};
+
+ msm_cam_smmu_fd {
+ compatible = "qcom,msm-cam-smmu-cb";
+ iommus = <&apps_smmu 0x1070 0x0>;
+ label = "fd";
+ fd_iova_mem_map: iova-mem-map {
+ iova-mem-region-io {
+ /* IO region is approximately 3.4 GB */
+ iova-region-name = "io";
+ iova-region-start = <0x7400000>;
+ iova-region-len = <0xd8c00000>;
+ iova-region-id = <0x3>;
+ status = "ok";
+ };
+ };
+ };
};
qcom,cam-cpas@ac40000 {
@@ -840,14 +856,20 @@
"ipe_0_axi_clk",
"ipe_0_clk",
"ipe_0_clk_src";
+ src-clock-name = "ipe_0_clk_src";
clocks = <&clock_camcc CAM_CC_IPE_0_AHB_CLK>,
<&clock_camcc CAM_CC_IPE_0_AREG_CLK>,
<&clock_camcc CAM_CC_IPE_0_AXI_CLK>,
<&clock_camcc CAM_CC_IPE_0_CLK>,
<&clock_camcc CAM_CC_IPE_0_CLK_SRC>;
- clock-rates = <0 0 0 0 600000000>;
- clock-cntl-level = "turbo";
+ clock-rates = <0 0 0 0 240000000>,
+ <0 0 0 0 404000000>,
+ <0 0 0 0 480000000>,
+ <0 0 0 0 538000000>,
+ <0 0 0 0 600000000>;
+ clock-cntl-level = "lowsvs", "svs",
+ "svs_l1", "nominal", "turbo";
status = "ok";
};
@@ -861,14 +883,20 @@
"ipe_1_axi_clk",
"ipe_1_clk",
"ipe_1_clk_src";
+ src-clock-name = "ipe_1_clk_src";
clocks = <&clock_camcc CAM_CC_IPE_1_AHB_CLK>,
<&clock_camcc CAM_CC_IPE_1_AREG_CLK>,
<&clock_camcc CAM_CC_IPE_1_AXI_CLK>,
<&clock_camcc CAM_CC_IPE_1_CLK>,
<&clock_camcc CAM_CC_IPE_1_CLK_SRC>;
- clock-rates = <0 0 0 0 600000000>;
- clock-cntl-level = "turbo";
+ clock-rates = <0 0 0 0 240000000>,
+ <0 0 0 0 404000000>,
+ <0 0 0 0 480000000>,
+ <0 0 0 0 538000000>,
+ <0 0 0 0 600000000>;
+ clock-cntl-level = "lowsvs", "svs",
+ "svs_l1", "nominal", "turbo";
status = "ok";
};
@@ -882,14 +910,20 @@
"bps_axi_clk",
"bps_clk",
"bps_clk_src";
+ src-clock-name = "bps_clk_src";
clocks = <&clock_camcc CAM_CC_BPS_AHB_CLK>,
<&clock_camcc CAM_CC_BPS_AREG_CLK>,
<&clock_camcc CAM_CC_BPS_AXI_CLK>,
<&clock_camcc CAM_CC_BPS_CLK>,
<&clock_camcc CAM_CC_BPS_CLK_SRC>;
- clock-rates = <0 0 0 0 600000000>;
- clock-cntl-level = "turbo";
+ clock-rates = <0 0 0 0 200000000>,
+ <0 0 0 0 404000000>,
+ <0 0 0 0 480000000>,
+ <0 0 0 0 600000000>,
+ <0 0 0 0 600000000>;
+ clock-cntl-level = "lowsvs", "svs",
+ "svs_l1", "nominal", "turbo";
status = "ok";
};
@@ -964,4 +998,43 @@
status = "ok";
};
+ qcom,cam-fd {
+ compatible = "qcom,cam-fd";
+ compat-hw-name = "qcom,fd";
+ num-fd = <1>;
+ status = "ok";
+ };
+
+ cam_fd: qcom,fd@ac5a000 {
+ cell-index = <0>;
+ compatible = "qcom,fd41";
+ reg-names = "fd_core", "fd_wrapper";
+ reg = <0xac5a000 0x1000>,
+ <0xac5b000 0x400>;
+ reg-cam-base = <0x5a000 0x5b000>;
+ interrupt-names = "fd";
+ interrupts = <0 462 0>;
+ regulator-names = "camss-vdd";
+ camss-vdd-supply = <&titan_top_gdsc>;
+ clock-names = "gcc_ahb_clk",
+ "gcc_axi_clk",
+ "soc_ahb_clk",
+ "cpas_ahb_clk",
+ "camnoc_axi_clk",
+ "fd_core_clk_src",
+ "fd_core_clk",
+ "fd_core_uar_clk";
+ clocks = <&clock_gcc GCC_CAMERA_AHB_CLK>,
+ <&clock_gcc GCC_CAMERA_AXI_CLK>,
+ <&clock_camcc CAM_CC_SOC_AHB_CLK>,
+ <&clock_camcc CAM_CC_CPAS_AHB_CLK>,
+ <&clock_camcc CAM_CC_CAMNOC_AXI_CLK>,
+ <&clock_camcc CAM_CC_FD_CORE_CLK_SRC>,
+ <&clock_camcc CAM_CC_FD_CORE_CLK>,
+ <&clock_camcc CAM_CC_FD_CORE_UAR_CLK>;
+ src-clock-name = "fd_core_clk_src";
+ clock-cntl-level = "svs";
+ clock-rates = <0 0 0 0 0 400000000 0 0>;
+ status = "ok";
+ };
};
diff --git a/arch/arm64/boot/dts/qcom/sdm845-coresight.dtsi b/arch/arm64/boot/dts/qcom/sdm845-coresight.dtsi
index c1fcb62..3cfcddb 100644
--- a/arch/arm64/boot/dts/qcom/sdm845-coresight.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm845-coresight.dtsi
@@ -1430,6 +1430,15 @@
<&tpda_spss_out_funnel_spss>;
};
};
+
+ port@2 {
+ reg = <1>;
+ funnel_spss_in_spss_etm0: endpoint {
+ slave-mode;
+ remote-endpoint =
+ <&spss_etm0_out_funnel_spss>;
+ };
+ };
};
};
@@ -1931,6 +1940,20 @@
};
};
+ spss_etm0 {
+ compatible = "qcom,coresight-dummy";
+
+ coresight-name = "coresight-spss-etm0";
+
+ qcom,dummy-source;
+ port {
+ spss_etm0_out_funnel_spss: endpoint {
+ remote-endpoint =
+ <&funnel_spss_in_spss_etm0>;
+ };
+ };
+ };
+
funnel_apss_merg: funnel@7810000 {
compatible = "arm,primecell";
arm,primecell-periphid = <0x0003b908>;
diff --git a/arch/arm64/boot/dts/qcom/sdm845-interposer-pm660.dtsi b/arch/arm64/boot/dts/qcom/sdm845-interposer-pm660.dtsi
index 299f01b..e36a759e 100644
--- a/arch/arm64/boot/dts/qcom/sdm845-interposer-pm660.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm845-interposer-pm660.dtsi
@@ -389,6 +389,7 @@
#include "pm660.dtsi"
#include "pm660l.dtsi"
#include "sdm670-regulator.dtsi"
+#include "sdm670-pmic-overlay.dtsi"
&soc {
/delete-node/ thermal-zones;
diff --git a/arch/arm64/boot/dts/qcom/sdm845-sde.dtsi b/arch/arm64/boot/dts/qcom/sdm845-sde.dtsi
index 121565e..0618f92 100644
--- a/arch/arm64/boot/dts/qcom/sdm845-sde.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm845-sde.dtsi
@@ -14,9 +14,11 @@
mdss_mdp: qcom,mdss_mdp@ae00000 {
compatible = "qcom,sde-kms";
reg = <0x0ae00000 0x81d40>,
- <0x0aeb0000 0x2008>;
+ <0x0aeb0000 0x2008>,
+ <0x0aeac000 0xf0>;
reg-names = "mdp_phys",
- "vbif_phys";
+ "vbif_phys",
+ "regdma_phys";
clocks =
<&clock_gcc GCC_DISP_AHB_CLK>,
@@ -179,6 +181,9 @@
/* offsets are relative to "mdp_phys + qcom,sde-off */
qcom,sde-inline-rot-clk-ctrl = <0x2bc 0x8>, <0x2bc 0xc>;
+ qcom,sde-reg-dma-off = <0>;
+ qcom,sde-reg-dma-version = <0x1>;
+ qcom,sde-reg-dma-trigger-off = <0x119c>;
qcom,sde-sspp-vig-blocks {
qcom,sde-vig-csc-off = <0x1a00>;
diff --git a/arch/arm64/boot/dts/qcom/sdm845.dtsi b/arch/arm64/boot/dts/qcom/sdm845.dtsi
index 70a20c1..d72c91c 100644
--- a/arch/arm64/boot/dts/qcom/sdm845.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm845.dtsi
@@ -26,6 +26,8 @@
#include <dt-bindings/thermal/thermal.h>
#include <dt-bindings/msm/msm-bus-ids.h>
+#define MHZ_TO_MBPS(mhz, w) ((mhz * 1000000 * w) / (1024 * 1024))
+
/ {
model = "Qualcomm Technologies, Inc. SDM845";
compatible = "qcom,sdm845";
@@ -545,6 +547,7 @@
wlan_fw_region: wlan_fw_region@8cb00000 {
compatible = "shared-dma-pool";
+ no-map;
reg = <0 0x8cb00000 0 0x100000>;
};
@@ -3006,6 +3009,7 @@
<0 424 0 /* CE10 */ >,
<0 425 0 /* CE11 */ >;
qcom,wlan-msa-memory = <0x100000>;
+ qcom,wlan-msa-fixed-region = <&wlan_fw_region>;
vdd-0.8-cx-mx-supply = <&pm8998_l5>;
vdd-1.8-xo-supply = <&pm8998_l7>;
diff --git a/arch/arm64/boot/dts/xilinx/zynqmp-ep108.dts b/arch/arm64/boot/dts/xilinx/zynqmp-ep108.dts
index 3580896..ef1b9e5 100644
--- a/arch/arm64/boot/dts/xilinx/zynqmp-ep108.dts
+++ b/arch/arm64/boot/dts/xilinx/zynqmp-ep108.dts
@@ -27,7 +27,7 @@
stdout-path = "serial0:115200n8";
};
- memory {
+ memory@0 {
device_type = "memory";
reg = <0x0 0x0 0x0 0x40000000>;
};
diff --git a/arch/arm64/boot/dts/xilinx/zynqmp.dtsi b/arch/arm64/boot/dts/xilinx/zynqmp.dtsi
index 68a90833..54dc283 100644
--- a/arch/arm64/boot/dts/xilinx/zynqmp.dtsi
+++ b/arch/arm64/boot/dts/xilinx/zynqmp.dtsi
@@ -72,7 +72,7 @@
<1 10 0xf08>;
};
- amba_apu {
+ amba_apu: amba_apu@0 {
compatible = "simple-bus";
#address-cells = <2>;
#size-cells = <1>;
@@ -175,7 +175,7 @@
};
i2c0: i2c@ff020000 {
- compatible = "cdns,i2c-r1p10";
+ compatible = "cdns,i2c-r1p14", "cdns,i2c-r1p10";
status = "disabled";
interrupt-parent = <&gic>;
interrupts = <0 17 4>;
@@ -185,7 +185,7 @@
};
i2c1: i2c@ff030000 {
- compatible = "cdns,i2c-r1p10";
+ compatible = "cdns,i2c-r1p14", "cdns,i2c-r1p10";
status = "disabled";
interrupt-parent = <&gic>;
interrupts = <0 18 4>;
diff --git a/arch/arm64/include/asm/debugv8.h b/arch/arm64/include/asm/debugv8.h
new file mode 100644
index 0000000..e32de80
--- /dev/null
+++ b/arch/arm64/include/asm/debugv8.h
@@ -0,0 +1,229 @@
+/* Copyright (c) 2014, 2016-2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * 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 __ASM_DEBUGV8_H
+#define __ASM_DEBUGV8_H
+
+#include <linux/types.h>
+
+/* 32 bit register reads for aarch 64 bit */
+#define dbg_readl(reg) RSYSL_##reg()
+/* 64 bit register reads for aarch 64 bit */
+#define dbg_readq(reg) RSYSQ_##reg()
+/* 32 and 64 bit register writes for aarch 64 bit */
+#define dbg_write(val, reg) WSYS_##reg(val)
+
+#define MRSL(reg) \
+({ \
+uint32_t val; \
+asm volatile("mrs %0, "#reg : "=r" (val)); \
+val; \
+})
+
+#define MRSQ(reg) \
+({ \
+uint64_t val; \
+asm volatile("mrs %0, "#reg : "=r" (val)); \
+val; \
+})
+
+#define MSR(val, reg) \
+({ \
+asm volatile("msr "#reg", %0" : : "r" (val)); \
+})
+
+/*
+ * Debug Feature Register
+ *
+ * Read only
+ */
+#define RSYSQ_ID_AA64DFR0_EL1() MRSQ(ID_AA64DFR0_EL1)
+
+/*
+ * Debug Registers
+ *
+ * Available only in DBGv8
+ *
+ * Read only
+ * MDCCSR_EL0, MDRAR_EL1, OSLSR_EL1, DBGDTRRX_EL0, DBGAUTHSTATUS_EL1
+ *
+ * Write only
+ * DBGDTRTX_EL0, OSLAR_EL1
+ */
+/* 32 bit registers */
+#define RSYSL_DBGDTRRX_EL0() MRSL(DBGDTRRX_EL0)
+#define RSYSL_MDCCSR_EL0() MRSL(MDCCSR_EL0)
+#define RSYSL_MDSCR_EL1() MRSL(MDSCR_EL1)
+#define RSYSL_OSDTRRX_EL1() MRSL(OSDTRRX_EL1)
+#define RSYSL_OSDTRTX_EL1() MRSL(OSDTRTX_EL1)
+#define RSYSL_OSDLR_EL1() MRSL(OSDLR_EL1)
+#define RSYSL_OSLSR_EL1() MRSL(OSLSR_EL1)
+#define RSYSL_MDCCINT_EL1() MRSL(MDCCINT_EL1)
+#define RSYSL_OSECCR_EL1() MRSL(OSECCR_EL1)
+#define RSYSL_DBGPRCR_EL1() MRSL(DBGPRCR_EL1)
+#define RSYSL_DBGBCR0_EL1() MRSL(DBGBCR0_EL1)
+#define RSYSL_DBGBCR1_EL1() MRSL(DBGBCR1_EL1)
+#define RSYSL_DBGBCR2_EL1() MRSL(DBGBCR2_EL1)
+#define RSYSL_DBGBCR3_EL1() MRSL(DBGBCR3_EL1)
+#define RSYSL_DBGBCR4_EL1() MRSL(DBGBCR4_EL1)
+#define RSYSL_DBGBCR5_EL1() MRSL(DBGBCR5_EL1)
+#define RSYSL_DBGBCR6_EL1() MRSL(DBGBCR6_EL1)
+#define RSYSL_DBGBCR7_EL1() MRSL(DBGBCR7_EL1)
+#define RSYSL_DBGBCR8_EL1() MRSL(DBGBCR8_EL1)
+#define RSYSL_DBGBCR9_EL1() MRSL(DBGBCR9_EL1)
+#define RSYSL_DBGBCR10_EL1() MRSL(DBGBCR10_EL1)
+#define RSYSL_DBGBCR11_EL1() MRSL(DBGBCR11_EL1)
+#define RSYSL_DBGBCR12_EL1() MRSL(DBGBCR12_EL1)
+#define RSYSL_DBGBCR13_EL1() MRSL(DBGBCR13_EL1)
+#define RSYSL_DBGBCR14_EL1() MRSL(DBGBCR14_EL1)
+#define RSYSL_DBGBCR15_EL1() MRSL(DBGBCR15_EL1)
+#define RSYSL_DBGWCR0_EL1() MRSL(DBGWCR0_EL1)
+#define RSYSL_DBGWCR1_EL1() MRSL(DBGWCR1_EL1)
+#define RSYSL_DBGWCR2_EL1() MRSL(DBGWCR2_EL1)
+#define RSYSL_DBGWCR3_EL1() MRSL(DBGWCR3_EL1)
+#define RSYSL_DBGWCR4_EL1() MRSL(DBGWCR4_EL1)
+#define RSYSL_DBGWCR5_EL1() MRSL(DBGWCR5_EL1)
+#define RSYSL_DBGWCR6_EL1() MRSL(DBGWCR6_EL1)
+#define RSYSL_DBGWCR7_EL1() MRSL(DBGWCR7_EL1)
+#define RSYSL_DBGWCR8_EL1() MRSL(DBGWCR8_EL1)
+#define RSYSL_DBGWCR9_EL1() MRSL(DBGWCR9_EL1)
+#define RSYSL_DBGWCR10_EL1() MRSL(DBGWCR10_EL1)
+#define RSYSL_DBGWCR11_EL1() MRSL(DBGWCR11_EL1)
+#define RSYSL_DBGWCR12_EL1() MRSL(DBGWCR12_EL1)
+#define RSYSL_DBGWCR13_EL1() MRSL(DBGWCR13_EL1)
+#define RSYSL_DBGWCR14_EL1() MRSL(DBGWCR14_EL1)
+#define RSYSL_DBGWCR15_EL1() MRSL(DBGWCR15_EL1)
+#define RSYSL_DBGCLAIMSET_EL1() MRSL(DBGCLAIMSET_EL1)
+#define RSYSL_DBGCLAIMCLR_EL1() MRSL(DBGCLAIMCLR_EL1)
+#define RSYSL_DBGAUTHSTATUS_EL1() MRSL(DBGAUTHSTATUS_EL1)
+#define RSYSL_DBGVCR32_EL2() MRSL(DBGVCR32_EL2)
+#define RSYSL_MDCR_EL2() MRSL(MDCR_EL2)
+#define RSYSL_MDCR_EL3() MRSL(MDCR_EL3)
+/* 64 bit registers */
+#define RSYSQ_DBGDTR_EL0() MRSQ(DBGDTR_EL0)
+#define RSYSQ_MDRAR_EL1() MRSQ(MDRAR_EL1)
+#define RSYSQ_DBGBVR0_EL1() MRSQ(DBGBVR0_EL1)
+#define RSYSQ_DBGBVR1_EL1() MRSQ(DBGBVR1_EL1)
+#define RSYSQ_DBGBVR2_EL1() MRSQ(DBGBVR2_EL1)
+#define RSYSQ_DBGBVR3_EL1() MRSQ(DBGBVR3_EL1)
+#define RSYSQ_DBGBVR4_EL1() MRSQ(DBGBVR4_EL1)
+#define RSYSQ_DBGBVR5_EL1() MRSQ(DBGBVR5_EL1)
+#define RSYSQ_DBGBVR6_EL1() MRSQ(DBGBVR6_EL1)
+#define RSYSQ_DBGBVR7_EL1() MRSQ(DBGBVR7_EL1)
+#define RSYSQ_DBGBVR8_EL1() MRSQ(DBGBVR8_EL1)
+#define RSYSQ_DBGBVR9_EL1() MRSQ(DBGBVR9_EL1)
+#define RSYSQ_DBGBVR10_EL1() MRSQ(DBGBVR10_EL1)
+#define RSYSQ_DBGBVR11_EL1() MRSQ(DBGBVR11_EL1)
+#define RSYSQ_DBGBVR12_EL1() MRSQ(DBGBVR12_EL1)
+#define RSYSQ_DBGBVR13_EL1() MRSQ(DBGBVR13_EL1)
+#define RSYSQ_DBGBVR14_EL1() MRSQ(DBGBVR14_EL1)
+#define RSYSQ_DBGBVR15_EL1() MRSQ(DBGBVR15_EL1)
+#define RSYSQ_DBGWVR0_EL1() MRSQ(DBGWVR0_EL1)
+#define RSYSQ_DBGWVR1_EL1() MRSQ(DBGWVR1_EL1)
+#define RSYSQ_DBGWVR2_EL1() MRSQ(DBGWVR2_EL1)
+#define RSYSQ_DBGWVR3_EL1() MRSQ(DBGWVR3_EL1)
+#define RSYSQ_DBGWVR4_EL1() MRSQ(DBGWVR4_EL1)
+#define RSYSQ_DBGWVR5_EL1() MRSQ(DBGWVR5_EL1)
+#define RSYSQ_DBGWVR6_EL1() MRSQ(DBGWVR6_EL1)
+#define RSYSQ_DBGWVR7_EL1() MRSQ(DBGWVR7_EL1)
+#define RSYSQ_DBGWVR8_EL1() MRSQ(DBGWVR8_EL1)
+#define RSYSQ_DBGWVR9_EL1() MRSQ(DBGWVR9_EL1)
+#define RSYSQ_DBGWVR10_EL1() MRSQ(DBGWVR10_EL1)
+#define RSYSQ_DBGWVR11_EL1() MRSQ(DBGWVR11_EL1)
+#define RSYSQ_DBGWVR12_EL1() MRSQ(DBGWVR12_EL1)
+#define RSYSQ_DBGWVR13_EL1() MRSQ(DBGWVR13_EL1)
+#define RSYSQ_DBGWVR14_EL1() MRSQ(DBGWVR14_EL1)
+#define RSYSQ_DBGWVR15_EL1() MRSQ(DBGWVR15_EL1)
+
+/* 32 bit registers */
+#define WSYS_DBGDTRTX_EL0(val) MSR(val, DBGDTRTX_EL0)
+#define WSYS_MDCCINT_EL1(val) MSR(val, MDCCINT_EL1)
+#define WSYS_MDSCR_EL1(val) MSR(val, MDSCR_EL1)
+#define WSYS_OSDTRRX_EL1(val) MSR(val, OSDTRRX_EL1)
+#define WSYS_OSDTRTX_EL1(val) MSR(val, OSDTRTX_EL1)
+#define WSYS_OSDLR_EL1(val) MSR(val, OSDLR_EL1)
+#define WSYS_OSECCR_EL1(val) MSR(val, OSECCR_EL1)
+#define WSYS_DBGPRCR_EL1(val) MSR(val, DBGPRCR_EL1)
+#define WSYS_DBGBCR0_EL1(val) MSR(val, DBGBCR0_EL1)
+#define WSYS_DBGBCR1_EL1(val) MSR(val, DBGBCR1_EL1)
+#define WSYS_DBGBCR2_EL1(val) MSR(val, DBGBCR2_EL1)
+#define WSYS_DBGBCR3_EL1(val) MSR(val, DBGBCR3_EL1)
+#define WSYS_DBGBCR4_EL1(val) MSR(val, DBGBCR4_EL1)
+#define WSYS_DBGBCR5_EL1(val) MSR(val, DBGBCR5_EL1)
+#define WSYS_DBGBCR6_EL1(val) MSR(val, DBGBCR6_EL1)
+#define WSYS_DBGBCR7_EL1(val) MSR(val, DBGBCR7_EL1)
+#define WSYS_DBGBCR8_EL1(val) MSR(val, DBGBCR8_EL1)
+#define WSYS_DBGBCR9_EL1(val) MSR(val, DBGBCR9_EL1)
+#define WSYS_DBGBCR10_EL1(val) MSR(val, DBGBCR10_EL1)
+#define WSYS_DBGBCR11_EL1(val) MSR(val, DBGBCR11_EL1)
+#define WSYS_DBGBCR12_EL1(val) MSR(val, DBGBCR12_EL1)
+#define WSYS_DBGBCR13_EL1(val) MSR(val, DBGBCR13_EL1)
+#define WSYS_DBGBCR14_EL1(val) MSR(val, DBGBCR14_EL1)
+#define WSYS_DBGBCR15_EL1(val) MSR(val, DBGBCR15_EL1)
+#define WSYS_DBGWCR0_EL1(val) MSR(val, DBGWCR0_EL1)
+#define WSYS_DBGWCR1_EL1(val) MSR(val, DBGWCR1_EL1)
+#define WSYS_DBGWCR2_EL1(val) MSR(val, DBGWCR2_EL1)
+#define WSYS_DBGWCR3_EL1(val) MSR(val, DBGWCR3_EL1)
+#define WSYS_DBGWCR4_EL1(val) MSR(val, DBGWCR4_EL1)
+#define WSYS_DBGWCR5_EL1(val) MSR(val, DBGWCR5_EL1)
+#define WSYS_DBGWCR6_EL1(val) MSR(val, DBGWCR6_EL1)
+#define WSYS_DBGWCR7_EL1(val) MSR(val, DBGWCR7_EL1)
+#define WSYS_DBGWCR8_EL1(val) MSR(val, DBGWCR8_EL1)
+#define WSYS_DBGWCR9_EL1(val) MSR(val, DBGWCR9_EL1)
+#define WSYS_DBGWCR10_EL1(val) MSR(val, DBGWCR10_EL1)
+#define WSYS_DBGWCR11_EL1(val) MSR(val, DBGWCR11_EL1)
+#define WSYS_DBGWCR12_EL1(val) MSR(val, DBGWCR12_EL1)
+#define WSYS_DBGWCR13_EL1(val) MSR(val, DBGWCR13_EL1)
+#define WSYS_DBGWCR14_EL1(val) MSR(val, DBGWCR14_EL1)
+#define WSYS_DBGWCR15_EL1(val) MSR(val, DBGWCR15_EL1)
+#define WSYS_DBGCLAIMSET_EL1(val) MSR(val, DBGCLAIMSET_EL1)
+#define WSYS_DBGCLAIMCLR_EL1(val) MSR(val, DBGCLAIMCLR_EL1)
+#define WSYS_OSLAR_EL1(val) MSR(val, OSLAR_EL1)
+#define WSYS_DBGVCR32_EL2(val) MSR(val, DBGVCR32_EL2)
+#define WSYS_MDCR_EL2(val) MSR(val, MDCR_EL2)
+#define WSYS_MDCR_EL3(val) MSR(val, MDCR_EL3)
+/* 64 bit registers */
+#define WSYS_DBGDTR_EL0(val) MSR(val, DBGDTR_EL0)
+#define WSYS_DBGBVR0_EL1(val) MSR(val, DBGBVR0_EL1)
+#define WSYS_DBGBVR1_EL1(val) MSR(val, DBGBVR1_EL1)
+#define WSYS_DBGBVR2_EL1(val) MSR(val, DBGBVR2_EL1)
+#define WSYS_DBGBVR3_EL1(val) MSR(val, DBGBVR3_EL1)
+#define WSYS_DBGBVR4_EL1(val) MSR(val, DBGBVR4_EL1)
+#define WSYS_DBGBVR5_EL1(val) MSR(val, DBGBVR5_EL1)
+#define WSYS_DBGBVR6_EL1(val) MSR(val, DBGBVR6_EL1)
+#define WSYS_DBGBVR7_EL1(val) MSR(val, DBGBVR7_EL1)
+#define WSYS_DBGBVR8_EL1(val) MSR(val, DBGBVR8_EL1)
+#define WSYS_DBGBVR9_EL1(val) MSR(val, DBGBVR9_EL1)
+#define WSYS_DBGBVR10_EL1(val) MSR(val, DBGBVR10_EL1)
+#define WSYS_DBGBVR11_EL1(val) MSR(val, DBGBVR11_EL1)
+#define WSYS_DBGBVR12_EL1(val) MSR(val, DBGBVR12_EL1)
+#define WSYS_DBGBVR13_EL1(val) MSR(val, DBGBVR13_EL1)
+#define WSYS_DBGBVR14_EL1(val) MSR(val, DBGBVR14_EL1)
+#define WSYS_DBGBVR15_EL1(val) MSR(val, DBGBVR15_EL1)
+#define WSYS_DBGWVR0_EL1(val) MSR(val, DBGWVR0_EL1)
+#define WSYS_DBGWVR1_EL1(val) MSR(val, DBGWVR1_EL1)
+#define WSYS_DBGWVR2_EL1(val) MSR(val, DBGWVR2_EL1)
+#define WSYS_DBGWVR3_EL1(val) MSR(val, DBGWVR3_EL1)
+#define WSYS_DBGWVR4_EL1(val) MSR(val, DBGWVR4_EL1)
+#define WSYS_DBGWVR5_EL1(val) MSR(val, DBGWVR5_EL1)
+#define WSYS_DBGWVR6_EL1(val) MSR(val, DBGWVR6_EL1)
+#define WSYS_DBGWVR7_EL1(val) MSR(val, DBGWVR7_EL1)
+#define WSYS_DBGWVR8_EL1(val) MSR(val, DBGWVR8_EL1)
+#define WSYS_DBGWVR9_EL1(val) MSR(val, DBGWVR9_EL1)
+#define WSYS_DBGWVR10_EL1(val) MSR(val, DBGWVR10_EL1)
+#define WSYS_DBGWVR11_EL1(val) MSR(val, DBGWVR11_EL1)
+#define WSYS_DBGWVR12_EL1(val) MSR(val, DBGWVR12_EL1)
+#define WSYS_DBGWVR13_EL1(val) MSR(val, DBGWVR13_EL1)
+#define WSYS_DBGWVR14_EL1(val) MSR(val, DBGWVR14_EL1)
+#define WSYS_DBGWVR15_EL1(val) MSR(val, DBGWVR15_EL1)
+
+#endif
diff --git a/arch/arm64/include/asm/etmv4x.h b/arch/arm64/include/asm/etmv4x.h
new file mode 100644
index 0000000..4fb91ca
--- /dev/null
+++ b/arch/arm64/include/asm/etmv4x.h
@@ -0,0 +1,385 @@
+/* Copyright (c) 2015-2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * 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 __ASM_ETMV4X_H
+#define __ASM_ETMV4X_H
+
+#include <linux/types.h>
+
+/* 32 bit register reads for AArch64 */
+#define trc_readl(reg) RSYSL_##reg()
+/* 64 bit register reads for AArch64 */
+#define trc_readq(reg) RSYSQ_##reg()
+/* 32 and 64 bit register writes for AArch64 */
+#define trc_write(val, reg) WSYS_##reg(val)
+
+#define MRSL(op0, op1, crn, crm, op2) \
+({ \
+uint32_t val; \
+asm volatile("mrs %0, S"#op0"_"#op1"_"#crn"_"#crm"_"#op2 : "=r" (val)); \
+val; \
+})
+
+#define MRSQ(op0, op1, crn, crm, op2) \
+({ \
+uint64_t val; \
+asm volatile("mrs %0, S"#op0"_"#op1"_"#crn"_"#crm"_"#op2 : "=r" (val)); \
+val; \
+})
+
+#define MSR(val, op0, op1, crn, crm, op2) \
+({ \
+asm volatile("msr S"#op0"_"#op1"_"#crn"_"#crm"_"#op2", %0" : : "r" (val)); \
+})
+
+/* Clock and Power Management Register */
+#define RSYSL_CPMR_EL1() MRSL(3, 7, c15, c0, 5)
+#define WSYS_CPMR_EL1(val) MSR(val, 3, 7, c15, c0, 5)
+
+/*
+ * ETMv4 Registers
+ *
+ * Read only
+ * ETMAUTHSTATUS, ETMDEVARCH, ETMDEVID, ETMIDRn[0-13], ETMOSLSR, ETMSTATR
+ *
+ * Write only
+ * ETMOSLAR
+ */
+/* 32 bit registers */
+#define RSYSL_ETMAUTHSTATUS() MRSL(2, 1, c7, c14, 6)
+#define RSYSL_ETMAUXCTLR() MRSL(2, 1, c0, c6, 0)
+#define RSYSL_ETMCCCTLR() MRSL(2, 1, c0, c14, 0)
+#define RSYSL_ETMCIDCCTLR0() MRSL(2, 1, c3, c0, 2)
+#define RSYSL_ETMCNTCTLR0() MRSL(2, 1, c0, c4, 5)
+#define RSYSL_ETMCNTCTLR1() MRSL(2, 1, c0, c5, 5)
+#define RSYSL_ETMCNTCTLR2() MRSL(2, 1, c0, c6, 5)
+#define RSYSL_ETMCNTCTLR3() MRSL(2, 1, c0, c7, 5)
+#define RSYSL_ETMCNTRLDVR0() MRSL(2, 1, c0, c0, 5)
+#define RSYSL_ETMCNTRLDVR1() MRSL(2, 1, c0, c1, 5)
+#define RSYSL_ETMCNTRLDVR2() MRSL(2, 1, c0, c2, 5)
+#define RSYSL_ETMCNTRLDVR3() MRSL(2, 1, c0, c3, 5)
+#define RSYSL_ETMCNTVR0() MRSL(2, 1, c0, c8, 5)
+#define RSYSL_ETMCNTVR1() MRSL(2, 1, c0, c9, 5)
+#define RSYSL_ETMCNTVR2() MRSL(2, 1, c0, c10, 5)
+#define RSYSL_ETMCNTVR3() MRSL(2, 1, c0, c11, 5)
+#define RSYSL_ETMCONFIGR() MRSL(2, 1, c0, c4, 0)
+#define RSYSL_ETMDEVARCH() MRSL(2, 1, c7, c15, 6)
+#define RSYSL_ETMDEVID() MRSL(2, 1, c7, c2, 7)
+#define RSYSL_ETMEVENTCTL0R() MRSL(2, 1, c0, c8, 0)
+#define RSYSL_ETMEVENTCTL1R() MRSL(2, 1, c0, c9, 0)
+#define RSYSL_ETMEXTINSELR() MRSL(2, 1, c0, c8, 4)
+#define RSYSL_ETMIDR0() MRSL(2, 1, c0, c8, 7)
+#define RSYSL_ETMIDR1() MRSL(2, 1, c0, c9, 7)
+#define RSYSL_ETMIDR10() MRSL(2, 1, c0, c2, 6)
+#define RSYSL_ETMIDR11() MRSL(2, 1, c0, c3, 6)
+#define RSYSL_ETMIDR12() MRSL(2, 1, c0, c4, 6)
+#define RSYSL_ETMIDR13() MRSL(2, 1, c0, c5, 6)
+#define RSYSL_ETMIDR2() MRSL(2, 1, c0, c10, 7)
+#define RSYSL_ETMIDR3() MRSL(2, 1, c0, c11, 7)
+#define RSYSL_ETMIDR4() MRSL(2, 1, c0, c12, 7)
+#define RSYSL_ETMIDR5() MRSL(2, 1, c0, c13, 7)
+#define RSYSL_ETMIDR6() MRSL(2, 1, c0, c14, 7)
+#define RSYSL_ETMIDR7() MRSL(2, 1, c0, c15, 7)
+#define RSYSL_ETMIDR8() MRSL(2, 1, c0, c0, 6)
+#define RSYSL_ETMIDR9() MRSL(2, 1, c0, c1, 6)
+#define RSYSL_ETMIMSPEC0() MRSL(2, 1, c0, c0, 7)
+#define RSYSL_ETMOSLSR() MRSL(2, 1, c1, c1, 4)
+#define RSYSL_ETMPRGCTLR() MRSL(2, 1, c0, c1, 0)
+#define RSYSL_ETMRSCTLR10() MRSL(2, 1, c1, c10, 0)
+#define RSYSL_ETMRSCTLR11() MRSL(2, 1, c1, c11, 0)
+#define RSYSL_ETMRSCTLR12() MRSL(2, 1, c1, c12, 0)
+#define RSYSL_ETMRSCTLR13() MRSL(2, 1, c1, c13, 0)
+#define RSYSL_ETMRSCTLR14() MRSL(2, 1, c1, c14, 0)
+#define RSYSL_ETMRSCTLR15() MRSL(2, 1, c1, c15, 0)
+#define RSYSL_ETMRSCTLR2() MRSL(2, 1, c1, c2, 0)
+#define RSYSL_ETMRSCTLR3() MRSL(2, 1, c1, c3, 0)
+#define RSYSL_ETMRSCTLR4() MRSL(2, 1, c1, c4, 0)
+#define RSYSL_ETMRSCTLR5() MRSL(2, 1, c1, c5, 0)
+#define RSYSL_ETMRSCTLR6() MRSL(2, 1, c1, c6, 0)
+#define RSYSL_ETMRSCTLR7() MRSL(2, 1, c1, c7, 0)
+#define RSYSL_ETMRSCTLR8() MRSL(2, 1, c1, c8, 0)
+#define RSYSL_ETMRSCTLR9() MRSL(2, 1, c1, c9, 0)
+#define RSYSL_ETMRSCTLR16() MRSL(2, 1, c1, c0, 1)
+#define RSYSL_ETMRSCTLR17() MRSL(2, 1, c1, c1, 1)
+#define RSYSL_ETMRSCTLR18() MRSL(2, 1, c1, c2, 1)
+#define RSYSL_ETMRSCTLR19() MRSL(2, 1, c1, c3, 1)
+#define RSYSL_ETMRSCTLR20() MRSL(2, 1, c1, c4, 1)
+#define RSYSL_ETMRSCTLR21() MRSL(2, 1, c1, c5, 1)
+#define RSYSL_ETMRSCTLR22() MRSL(2, 1, c1, c6, 1)
+#define RSYSL_ETMRSCTLR23() MRSL(2, 1, c1, c7, 1)
+#define RSYSL_ETMRSCTLR24() MRSL(2, 1, c1, c8, 1)
+#define RSYSL_ETMRSCTLR25() MRSL(2, 1, c1, c9, 1)
+#define RSYSL_ETMRSCTLR26() MRSL(2, 1, c1, c10, 1)
+#define RSYSL_ETMRSCTLR27() MRSL(2, 1, c1, c11, 1)
+#define RSYSL_ETMRSCTLR28() MRSL(2, 1, c1, c12, 1)
+#define RSYSL_ETMRSCTLR29() MRSL(2, 1, c1, c13, 1)
+#define RSYSL_ETMRSCTLR30() MRSL(2, 1, c1, c14, 1)
+#define RSYSL_ETMRSCTLR31() MRSL(2, 1, c1, c15, 1)
+#define RSYSL_ETMSEQEVR0() MRSL(2, 1, c0, c0, 4)
+#define RSYSL_ETMSEQEVR1() MRSL(2, 1, c0, c1, 4)
+#define RSYSL_ETMSEQEVR2() MRSL(2, 1, c0, c2, 4)
+#define RSYSL_ETMSEQRSTEVR() MRSL(2, 1, c0, c6, 4)
+#define RSYSL_ETMSEQSTR() MRSL(2, 1, c0, c7, 4)
+#define RSYSL_ETMSTALLCTLR() MRSL(2, 1, c0, c11, 0)
+#define RSYSL_ETMSTATR() MRSL(2, 1, c0, c3, 0)
+#define RSYSL_ETMSYNCPR() MRSL(2, 1, c0, c13, 0)
+#define RSYSL_ETMTRACEIDR() MRSL(2, 1, c0, c0, 1)
+#define RSYSL_ETMTSCTLR() MRSL(2, 1, c0, c12, 0)
+#define RSYSL_ETMVICTLR() MRSL(2, 1, c0, c0, 2)
+#define RSYSL_ETMVIIECTLR() MRSL(2, 1, c0, c1, 2)
+#define RSYSL_ETMVISSCTLR() MRSL(2, 1, c0, c2, 2)
+#define RSYSL_ETMSSCCR0() MRSL(2, 1, c1, c0, 2)
+#define RSYSL_ETMSSCCR1() MRSL(2, 1, c1, c1, 2)
+#define RSYSL_ETMSSCCR2() MRSL(2, 1, c1, c2, 2)
+#define RSYSL_ETMSSCCR3() MRSL(2, 1, c1, c3, 2)
+#define RSYSL_ETMSSCCR4() MRSL(2, 1, c1, c4, 2)
+#define RSYSL_ETMSSCCR5() MRSL(2, 1, c1, c5, 2)
+#define RSYSL_ETMSSCCR6() MRSL(2, 1, c1, c6, 2)
+#define RSYSL_ETMSSCCR7() MRSL(2, 1, c1, c7, 2)
+#define RSYSL_ETMSSCSR0() MRSL(2, 1, c1, c8, 2)
+#define RSYSL_ETMSSCSR1() MRSL(2, 1, c1, c9, 2)
+#define RSYSL_ETMSSCSR2() MRSL(2, 1, c1, c10, 2)
+#define RSYSL_ETMSSCSR3() MRSL(2, 1, c1, c11, 2)
+#define RSYSL_ETMSSCSR4() MRSL(2, 1, c1, c12, 2)
+#define RSYSL_ETMSSCSR5() MRSL(2, 1, c1, c13, 2)
+#define RSYSL_ETMSSCSR6() MRSL(2, 1, c1, c14, 2)
+#define RSYSL_ETMSSCSR7() MRSL(2, 1, c1, c15, 2)
+#define RSYSL_ETMSSPCICR0() MRSL(2, 1, c1, c0, 3)
+#define RSYSL_ETMSSPCICR1() MRSL(2, 1, c1, c1, 3)
+#define RSYSL_ETMSSPCICR2() MRSL(2, 1, c1, c2, 3)
+#define RSYSL_ETMSSPCICR3() MRSL(2, 1, c1, c3, 3)
+#define RSYSL_ETMSSPCICR4() MRSL(2, 1, c1, c4, 3)
+#define RSYSL_ETMSSPCICR5() MRSL(2, 1, c1, c5, 3)
+#define RSYSL_ETMSSPCICR6() MRSL(2, 1, c1, c6, 3)
+#define RSYSL_ETMSSPCICR7() MRSL(2, 1, c1, c7, 3)
+
+/* 64 bit registers */
+#define RSYSQ_ETMACATR0() MRSQ(2, 1, c2, c0, 2)
+#define RSYSQ_ETMACATR1() MRSQ(2, 1, c2, c2, 2)
+#define RSYSQ_ETMACATR2() MRSQ(2, 1, c2, c4, 2)
+#define RSYSQ_ETMACATR3() MRSQ(2, 1, c2, c6, 2)
+#define RSYSQ_ETMACATR4() MRSQ(2, 1, c2, c8, 2)
+#define RSYSQ_ETMACATR5() MRSQ(2, 1, c2, c10, 2)
+#define RSYSQ_ETMACATR6() MRSQ(2, 1, c2, c12, 2)
+#define RSYSQ_ETMACATR7() MRSQ(2, 1, c2, c14, 2)
+#define RSYSQ_ETMACATR8() MRSQ(2, 1, c2, c0, 3)
+#define RSYSQ_ETMACATR9() MRSQ(2, 1, c2, c2, 3)
+#define RSYSQ_ETMACATR10() MRSQ(2, 1, c2, c4, 3)
+#define RSYSQ_ETMACATR11() MRSQ(2, 1, c2, c6, 3)
+#define RSYSQ_ETMACATR12() MRSQ(2, 1, c2, c8, 3)
+#define RSYSQ_ETMACATR13() MRSQ(2, 1, c2, c10, 3)
+#define RSYSQ_ETMACATR14() MRSQ(2, 1, c2, c12, 3)
+#define RSYSQ_ETMACATR15() MRSQ(2, 1, c2, c14, 3)
+#define RSYSQ_ETMCIDCVR0() MRSQ(2, 1, c3, c0, 0)
+#define RSYSQ_ETMCIDCVR1() MRSQ(2, 1, c3, c2, 0)
+#define RSYSQ_ETMCIDCVR2() MRSQ(2, 1, c3, c4, 0)
+#define RSYSQ_ETMCIDCVR3() MRSQ(2, 1, c3, c6, 0)
+#define RSYSQ_ETMCIDCVR4() MRSQ(2, 1, c3, c8, 0)
+#define RSYSQ_ETMCIDCVR5() MRSQ(2, 1, c3, c10, 0)
+#define RSYSQ_ETMCIDCVR6() MRSQ(2, 1, c3, c12, 0)
+#define RSYSQ_ETMCIDCVR7() MRSQ(2, 1, c3, c14, 0)
+#define RSYSQ_ETMACVR0() MRSQ(2, 1, c2, c0, 0)
+#define RSYSQ_ETMACVR1() MRSQ(2, 1, c2, c2, 0)
+#define RSYSQ_ETMACVR2() MRSQ(2, 1, c2, c4, 0)
+#define RSYSQ_ETMACVR3() MRSQ(2, 1, c2, c6, 0)
+#define RSYSQ_ETMACVR4() MRSQ(2, 1, c2, c8, 0)
+#define RSYSQ_ETMACVR5() MRSQ(2, 1, c2, c10, 0)
+#define RSYSQ_ETMACVR6() MRSQ(2, 1, c2, c12, 0)
+#define RSYSQ_ETMACVR7() MRSQ(2, 1, c2, c14, 0)
+#define RSYSQ_ETMACVR8() MRSQ(2, 1, c2, c0, 1)
+#define RSYSQ_ETMACVR9() MRSQ(2, 1, c2, c2, 1)
+#define RSYSQ_ETMACVR10() MRSQ(2, 1, c2, c4, 1)
+#define RSYSQ_ETMACVR11() MRSQ(2, 1, c2, c6, 1)
+#define RSYSQ_ETMACVR12() MRSQ(2, 1, c2, c8, 1)
+#define RSYSQ_ETMACVR13() MRSQ(2, 1, c2, c10, 1)
+#define RSYSQ_ETMACVR14() MRSQ(2, 1, c2, c12, 1)
+#define RSYSQ_ETMACVR15() MRSQ(2, 1, c2, c14, 1)
+#define RSYSQ_ETMVMIDCVR0() MRSQ(2, 1, c3, c0, 1)
+#define RSYSQ_ETMVMIDCVR1() MRSQ(2, 1, c3, c2, 1)
+#define RSYSQ_ETMVMIDCVR2() MRSQ(2, 1, c3, c4, 1)
+#define RSYSQ_ETMVMIDCVR3() MRSQ(2, 1, c3, c6, 1)
+#define RSYSQ_ETMVMIDCVR4() MRSQ(2, 1, c3, c8, 1)
+#define RSYSQ_ETMVMIDCVR5() MRSQ(2, 1, c3, c10, 1)
+#define RSYSQ_ETMVMIDCVR6() MRSQ(2, 1, c3, c12, 1)
+#define RSYSQ_ETMVMIDCVR7() MRSQ(2, 1, c3, c14, 1)
+#define RSYSQ_ETMDVCVR0() MRSQ(2, 1, c2, c0, 4)
+#define RSYSQ_ETMDVCVR1() MRSQ(2, 1, c2, c4, 4)
+#define RSYSQ_ETMDVCVR2() MRSQ(2, 1, c2, c8, 4)
+#define RSYSQ_ETMDVCVR3() MRSQ(2, 1, c2, c12, 4)
+#define RSYSQ_ETMDVCVR4() MRSQ(2, 1, c2, c0, 5)
+#define RSYSQ_ETMDVCVR5() MRSQ(2, 1, c2, c4, 5)
+#define RSYSQ_ETMDVCVR6() MRSQ(2, 1, c2, c8, 5)
+#define RSYSQ_ETMDVCVR7() MRSQ(2, 1, c2, c12, 5)
+#define RSYSQ_ETMDVCMR0() MRSQ(2, 1, c2, c0, 6)
+#define RSYSQ_ETMDVCMR1() MRSQ(2, 1, c2, c4, 6)
+#define RSYSQ_ETMDVCMR2() MRSQ(2, 1, c2, c8, 6)
+#define RSYSQ_ETMDVCMR3() MRSQ(2, 1, c2, c12, 6)
+#define RSYSQ_ETMDVCMR4() MRSQ(2, 1, c2, c0, 7)
+#define RSYSQ_ETMDVCMR5() MRSQ(2, 1, c2, c4, 7)
+#define RSYSQ_ETMDVCMR6() MRSQ(2, 1, c2, c8, 7)
+#define RSYSQ_ETMDVCMR7() MRSQ(2, 1, c2, c12, 7)
+
+/* 32 and 64 bit registers */
+#define WSYS_ETMAUXCTLR(val) MSR(val, 2, 1, c0, c6, 0)
+#define WSYS_ETMACATR0(val) MSR(val, 2, 1, c2, c0, 2)
+#define WSYS_ETMACATR1(val) MSR(val, 2, 1, c2, c2, 2)
+#define WSYS_ETMACATR2(val) MSR(val, 2, 1, c2, c4, 2)
+#define WSYS_ETMACATR3(val) MSR(val, 2, 1, c2, c6, 2)
+#define WSYS_ETMACATR4(val) MSR(val, 2, 1, c2, c8, 2)
+#define WSYS_ETMACATR5(val) MSR(val, 2, 1, c2, c10, 2)
+#define WSYS_ETMACATR6(val) MSR(val, 2, 1, c2, c12, 2)
+#define WSYS_ETMACATR7(val) MSR(val, 2, 1, c2, c14, 2)
+#define WSYS_ETMACATR8(val) MSR(val, 2, 1, c2, c0, 3)
+#define WSYS_ETMACATR9(val) MSR(val, 2, 1, c2, c2, 3)
+#define WSYS_ETMACATR10(val) MSR(val, 2, 1, c2, c4, 3)
+#define WSYS_ETMACATR11(val) MSR(val, 2, 1, c2, c6, 3)
+#define WSYS_ETMACATR12(val) MSR(val, 2, 1, c2, c8, 3)
+#define WSYS_ETMACATR13(val) MSR(val, 2, 1, c2, c10, 3)
+#define WSYS_ETMACATR14(val) MSR(val, 2, 1, c2, c12, 3)
+#define WSYS_ETMACATR15(val) MSR(val, 2, 1, c2, c14, 3)
+#define WSYS_ETMACVR0(val) MSR(val, 2, 1, c2, c0, 0)
+#define WSYS_ETMACVR1(val) MSR(val, 2, 1, c2, c2, 0)
+#define WSYS_ETMACVR2(val) MSR(val, 2, 1, c2, c4, 0)
+#define WSYS_ETMACVR3(val) MSR(val, 2, 1, c2, c6, 0)
+#define WSYS_ETMACVR4(val) MSR(val, 2, 1, c2, c8, 0)
+#define WSYS_ETMACVR5(val) MSR(val, 2, 1, c2, c10, 0)
+#define WSYS_ETMACVR6(val) MSR(val, 2, 1, c2, c12, 0)
+#define WSYS_ETMACVR7(val) MSR(val, 2, 1, c2, c14, 0)
+#define WSYS_ETMACVR8(val) MSR(val, 2, 1, c2, c0, 1)
+#define WSYS_ETMACVR9(val) MSR(val, 2, 1, c2, c2, 1)
+#define WSYS_ETMACVR10(val) MSR(val, 2, 1, c2, c4, 1)
+#define WSYS_ETMACVR11(val) MSR(val, 2, 1, c2, c6, 1)
+#define WSYS_ETMACVR12(val) MSR(val, 2, 1, c2, c8, 1)
+#define WSYS_ETMACVR13(val) MSR(val, 2, 1, c2, c10, 1)
+#define WSYS_ETMACVR14(val) MSR(val, 2, 1, c2, c12, 1)
+#define WSYS_ETMACVR15(val) MSR(val, 2, 1, c2, c14, 1)
+#define WSYS_ETMCCCTLR(val) MSR(val, 2, 1, c0, c14, 0)
+#define WSYS_ETMCIDCCTLR0(val) MSR(val, 2, 1, c3, c0, 2)
+#define WSYS_ETMCIDCVR0(val) MSR(val, 2, 1, c3, c0, 0)
+#define WSYS_ETMCIDCVR1(val) MSR(val, 2, 1, c3, c2, 0)
+#define WSYS_ETMCIDCVR2(val) MSR(val, 2, 1, c3, c4, 0)
+#define WSYS_ETMCIDCVR3(val) MSR(val, 2, 1, c3, c6, 0)
+#define WSYS_ETMCIDCVR4(val) MSR(val, 2, 1, c3, c8, 0)
+#define WSYS_ETMCIDCVR5(val) MSR(val, 2, 1, c3, c10, 0)
+#define WSYS_ETMCIDCVR6(val) MSR(val, 2, 1, c3, c12, 0)
+#define WSYS_ETMCIDCVR7(val) MSR(val, 2, 1, c3, c14, 0)
+#define WSYS_ETMCNTCTLR0(val) MSR(val, 2, 1, c0, c4, 5)
+#define WSYS_ETMCNTCTLR1(val) MSR(val, 2, 1, c0, c5, 5)
+#define WSYS_ETMCNTCTLR2(val) MSR(val, 2, 1, c0, c6, 5)
+#define WSYS_ETMCNTCTLR3(val) MSR(val, 2, 1, c0, c7, 5)
+#define WSYS_ETMCNTRLDVR0(val) MSR(val, 2, 1, c0, c0, 5)
+#define WSYS_ETMCNTRLDVR1(val) MSR(val, 2, 1, c0, c1, 5)
+#define WSYS_ETMCNTRLDVR2(val) MSR(val, 2, 1, c0, c2, 5)
+#define WSYS_ETMCNTRLDVR3(val) MSR(val, 2, 1, c0, c3, 5)
+#define WSYS_ETMCNTVR0(val) MSR(val, 2, 1, c0, c8, 5)
+#define WSYS_ETMCNTVR1(val) MSR(val, 2, 1, c0, c9, 5)
+#define WSYS_ETMCNTVR2(val) MSR(val, 2, 1, c0, c10, 5)
+#define WSYS_ETMCNTVR3(val) MSR(val, 2, 1, c0, c11, 5)
+#define WSYS_ETMCONFIGR(val) MSR(val, 2, 1, c0, c4, 0)
+#define WSYS_ETMEVENTCTL0R(val) MSR(val, 2, 1, c0, c8, 0)
+#define WSYS_ETMEVENTCTL1R(val) MSR(val, 2, 1, c0, c9, 0)
+#define WSYS_ETMEXTINSELR(val) MSR(val, 2, 1, c0, c8, 4)
+#define WSYS_ETMIMSPEC0(val) MSR(val, 2, 1, c0, c0, 7)
+#define WSYS_ETMOSLAR(val) MSR(val, 2, 1, c1, c0, 4)
+#define WSYS_ETMPRGCTLR(val) MSR(val, 2, 1, c0, c1, 0)
+#define WSYS_ETMRSCTLR10(val) MSR(val, 2, 1, c1, c10, 0)
+#define WSYS_ETMRSCTLR11(val) MSR(val, 2, 1, c1, c11, 0)
+#define WSYS_ETMRSCTLR12(val) MSR(val, 2, 1, c1, c12, 0)
+#define WSYS_ETMRSCTLR13(val) MSR(val, 2, 1, c1, c13, 0)
+#define WSYS_ETMRSCTLR14(val) MSR(val, 2, 1, c1, c14, 0)
+#define WSYS_ETMRSCTLR15(val) MSR(val, 2, 1, c1, c15, 0)
+#define WSYS_ETMRSCTLR2(val) MSR(val, 2, 1, c1, c2, 0)
+#define WSYS_ETMRSCTLR3(val) MSR(val, 2, 1, c1, c3, 0)
+#define WSYS_ETMRSCTLR4(val) MSR(val, 2, 1, c1, c4, 0)
+#define WSYS_ETMRSCTLR5(val) MSR(val, 2, 1, c1, c5, 0)
+#define WSYS_ETMRSCTLR6(val) MSR(val, 2, 1, c1, c6, 0)
+#define WSYS_ETMRSCTLR7(val) MSR(val, 2, 1, c1, c7, 0)
+#define WSYS_ETMRSCTLR8(val) MSR(val, 2, 1, c1, c8, 0)
+#define WSYS_ETMRSCTLR9(val) MSR(val, 2, 1, c1, c9, 0)
+#define WSYS_ETMRSCTLR16(val) MSR(val, 2, 1, c1, c0, 1)
+#define WSYS_ETMRSCTLR17(val) MSR(val, 2, 1, c1, c1, 1)
+#define WSYS_ETMRSCTLR18(val) MSR(val, 2, 1, c1, c2, 1)
+#define WSYS_ETMRSCTLR19(val) MSR(val, 2, 1, c1, c3, 1)
+#define WSYS_ETMRSCTLR20(val) MSR(val, 2, 1, c1, c4, 1)
+#define WSYS_ETMRSCTLR21(val) MSR(val, 2, 1, c1, c5, 1)
+#define WSYS_ETMRSCTLR22(val) MSR(val, 2, 1, c1, c6, 1)
+#define WSYS_ETMRSCTLR23(val) MSR(val, 2, 1, c1, c7, 1)
+#define WSYS_ETMRSCTLR24(val) MSR(val, 2, 1, c1, c8, 1)
+#define WSYS_ETMRSCTLR25(val) MSR(val, 2, 1, c1, c9, 1)
+#define WSYS_ETMRSCTLR26(val) MSR(val, 2, 1, c1, c10, 1)
+#define WSYS_ETMRSCTLR27(val) MSR(val, 2, 1, c1, c11, 1)
+#define WSYS_ETMRSCTLR28(val) MSR(val, 2, 1, c1, c12, 1)
+#define WSYS_ETMRSCTLR29(val) MSR(val, 2, 1, c1, c13, 1)
+#define WSYS_ETMRSCTLR30(val) MSR(val, 2, 1, c1, c14, 1)
+#define WSYS_ETMRSCTLR31(val) MSR(val, 2, 1, c1, c15, 1)
+#define WSYS_ETMSEQEVR0(val) MSR(val, 2, 1, c0, c0, 4)
+#define WSYS_ETMSEQEVR1(val) MSR(val, 2, 1, c0, c1, 4)
+#define WSYS_ETMSEQEVR2(val) MSR(val, 2, 1, c0, c2, 4)
+#define WSYS_ETMSEQRSTEVR(val) MSR(val, 2, 1, c0, c6, 4)
+#define WSYS_ETMSEQSTR(val) MSR(val, 2, 1, c0, c7, 4)
+#define WSYS_ETMSTALLCTLR(val) MSR(val, 2, 1, c0, c11, 0)
+#define WSYS_ETMSYNCPR(val) MSR(val, 2, 1, c0, c13, 0)
+#define WSYS_ETMTRACEIDR(val) MSR(val, 2, 1, c0, c0, 1)
+#define WSYS_ETMTSCTLR(val) MSR(val, 2, 1, c0, c12, 0)
+#define WSYS_ETMVICTLR(val) MSR(val, 2, 1, c0, c0, 2)
+#define WSYS_ETMVIIECTLR(val) MSR(val, 2, 1, c0, c1, 2)
+#define WSYS_ETMVISSCTLR(val) MSR(val, 2, 1, c0, c2, 2)
+#define WSYS_ETMVMIDCVR0(val) MSR(val, 2, 1, c3, c0, 1)
+#define WSYS_ETMVMIDCVR1(val) MSR(val, 2, 1, c3, c2, 1)
+#define WSYS_ETMVMIDCVR2(val) MSR(val, 2, 1, c3, c4, 1)
+#define WSYS_ETMVMIDCVR3(val) MSR(val, 2, 1, c3, c6, 1)
+#define WSYS_ETMVMIDCVR4(val) MSR(val, 2, 1, c3, c8, 1)
+#define WSYS_ETMVMIDCVR5(val) MSR(val, 2, 1, c3, c10, 1)
+#define WSYS_ETMVMIDCVR6(val) MSR(val, 2, 1, c3, c12, 1)
+#define WSYS_ETMVMIDCVR7(val) MSR(val, 2, 1, c3, c14, 1)
+#define WSYS_ETMDVCVR0(val) MSR(val, 2, 1, c2, c0, 4)
+#define WSYS_ETMDVCVR1(val) MSR(val, 2, 1, c2, c4, 4)
+#define WSYS_ETMDVCVR2(val) MSR(val, 2, 1, c2, c8, 4)
+#define WSYS_ETMDVCVR3(val) MSR(val, 2, 1, c2, c12, 4)
+#define WSYS_ETMDVCVR4(val) MSR(val, 2, 1, c2, c0, 5)
+#define WSYS_ETMDVCVR5(val) MSR(val, 2, 1, c2, c4, 5)
+#define WSYS_ETMDVCVR6(val) MSR(val, 2, 1, c2, c8, 5)
+#define WSYS_ETMDVCVR7(val) MSR(val, 2, 1, c2, c12, 5)
+#define WSYS_ETMDVCMR0(val) MSR(val, 2, 1, c2, c0, 6)
+#define WSYS_ETMDVCMR1(val) MSR(val, 2, 1, c2, c4, 6)
+#define WSYS_ETMDVCMR2(val) MSR(val, 2, 1, c2, c8, 6)
+#define WSYS_ETMDVCMR3(val) MSR(val, 2, 1, c2, c12, 6)
+#define WSYS_ETMDVCMR4(val) MSR(val, 2, 1, c2, c0, 7)
+#define WSYS_ETMDVCMR5(val) MSR(val, 2, 1, c2, c4, 7)
+#define WSYS_ETMDVCMR6(val) MSR(val, 2, 1, c2, c8, 7)
+#define WSYS_ETMDVCMR7(val) MSR(val, 2, 1, c2, c12, 7)
+#define WSYS_ETMSSCCR0(val) MSR(val, 2, 1, c1, c0, 2)
+#define WSYS_ETMSSCCR1(val) MSR(val, 2, 1, c1, c1, 2)
+#define WSYS_ETMSSCCR2(val) MSR(val, 2, 1, c1, c2, 2)
+#define WSYS_ETMSSCCR3(val) MSR(val, 2, 1, c1, c3, 2)
+#define WSYS_ETMSSCCR4(val) MSR(val, 2, 1, c1, c4, 2)
+#define WSYS_ETMSSCCR5(val) MSR(val, 2, 1, c1, c5, 2)
+#define WSYS_ETMSSCCR6(val) MSR(val, 2, 1, c1, c6, 2)
+#define WSYS_ETMSSCCR7(val) MSR(val, 2, 1, c1, c7, 2)
+#define WSYS_ETMSSCSR0(val) MSR(val, 2, 1, c1, c8, 2)
+#define WSYS_ETMSSCSR1(val) MSR(val, 2, 1, c1, c9, 2)
+#define WSYS_ETMSSCSR2(val) MSR(val, 2, 1, c1, c10, 2)
+#define WSYS_ETMSSCSR3(val) MSR(val, 2, 1, c1, c11, 2)
+#define WSYS_ETMSSCSR4(val) MSR(val, 2, 1, c1, c12, 2)
+#define WSYS_ETMSSCSR5(val) MSR(val, 2, 1, c1, c13, 2)
+#define WSYS_ETMSSCSR6(val) MSR(val, 2, 1, c1, c14, 2)
+#define WSYS_ETMSSCSR7(val) MSR(val, 2, 1, c1, c15, 2)
+#define WSYS_ETMSSPCICR0(val) MSR(val, 2, 1, c1, c0, 3)
+#define WSYS_ETMSSPCICR1(val) MSR(val, 2, 1, c1, c1, 3)
+#define WSYS_ETMSSPCICR2(val) MSR(val, 2, 1, c1, c2, 3)
+#define WSYS_ETMSSPCICR3(val) MSR(val, 2, 1, c1, c3, 3)
+#define WSYS_ETMSSPCICR4(val) MSR(val, 2, 1, c1, c4, 3)
+#define WSYS_ETMSSPCICR5(val) MSR(val, 2, 1, c1, c5, 3)
+#define WSYS_ETMSSPCICR6(val) MSR(val, 2, 1, c1, c6, 3)
+#define WSYS_ETMSSPCICR7(val) MSR(val, 2, 1, c1, c7, 3)
+
+#endif
diff --git a/arch/arm64/kernel/topology.c b/arch/arm64/kernel/topology.c
index 7b670f1..f2fe96f 100644
--- a/arch/arm64/kernel/topology.c
+++ b/arch/arm64/kernel/topology.c
@@ -420,6 +420,11 @@
return &cpu_topology[cpu].core_sibling;
}
+static int cpu_cpu_flags(void)
+{
+ return SD_ASYM_CPUCAPACITY;
+}
+
static inline int cpu_corepower_flags(void)
{
return SD_SHARE_PKG_RESOURCES | SD_SHARE_POWERDOMAIN | \
@@ -430,7 +435,7 @@
#ifdef CONFIG_SCHED_MC
{ cpu_coregroup_mask, cpu_corepower_flags, cpu_core_energy, SD_INIT_NAME(MC) },
#endif
- { cpu_cpu_mask, NULL, cpu_cluster_energy, SD_INIT_NAME(DIE) },
+ { cpu_cpu_mask, cpu_cpu_flags, cpu_cluster_energy, SD_INIT_NAME(DIE) },
{ NULL, },
};
diff --git a/arch/arm64/mm/fault.c b/arch/arm64/mm/fault.c
index b6b81f8..7f9501a 100644
--- a/arch/arm64/mm/fault.c
+++ b/arch/arm64/mm/fault.c
@@ -103,21 +103,21 @@
break;
pud = pud_offset(pgd, addr);
- printk(", *pud=%016llx", pud_val(*pud));
+ pr_cont(", *pud=%016llx", pud_val(*pud));
if (pud_none(*pud) || pud_bad(*pud))
break;
pmd = pmd_offset(pud, addr);
- printk(", *pmd=%016llx", pmd_val(*pmd));
+ pr_cont(", *pmd=%016llx", pmd_val(*pmd));
if (pmd_none(*pmd) || pmd_bad(*pmd))
break;
pte = pte_offset_map(pmd, addr);
- printk(", *pte=%016llx", pte_val(*pte));
+ pr_cont(", *pte=%016llx", pte_val(*pte));
pte_unmap(pte);
} while(0);
- printk("\n");
+ pr_cont("\n");
}
#ifdef CONFIG_ARM64_HW_AFDBM
diff --git a/arch/openrisc/kernel/vmlinux.lds.S b/arch/openrisc/kernel/vmlinux.lds.S
index d68b9ed..c50609a 100644
--- a/arch/openrisc/kernel/vmlinux.lds.S
+++ b/arch/openrisc/kernel/vmlinux.lds.S
@@ -38,6 +38,8 @@
/* Read-only sections, merged into text segment: */
. = LOAD_BASE ;
+ _text = .;
+
/* _s_kernel_ro must be page aligned */
. = ALIGN(PAGE_SIZE);
_s_kernel_ro = .;
diff --git a/arch/parisc/kernel/cache.c b/arch/parisc/kernel/cache.c
index 53ec75f..c721ea2 100644
--- a/arch/parisc/kernel/cache.c
+++ b/arch/parisc/kernel/cache.c
@@ -452,8 +452,8 @@
before it can be accessed through the kernel mapping. */
preempt_disable();
flush_dcache_page_asm(__pa(vfrom), vaddr);
- preempt_enable();
copy_page_asm(vto, vfrom);
+ preempt_enable();
}
EXPORT_SYMBOL(copy_user_page);
@@ -538,6 +538,10 @@
struct vm_area_struct *vma;
pgd_t *pgd;
+ /* Flush the TLB to avoid speculation if coherency is required. */
+ if (parisc_requires_coherency())
+ flush_tlb_all();
+
/* Flushing the whole cache on each cpu takes forever on
rp3440, etc. So, avoid it if the mm isn't too big. */
if (mm_total_size(mm) >= parisc_cache_flush_threshold) {
@@ -594,33 +598,22 @@
void flush_cache_range(struct vm_area_struct *vma,
unsigned long start, unsigned long end)
{
- unsigned long addr;
- pgd_t *pgd;
-
BUG_ON(!vma->vm_mm->context);
+ /* Flush the TLB to avoid speculation if coherency is required. */
+ if (parisc_requires_coherency())
+ flush_tlb_range(vma, start, end);
+
if ((end - start) >= parisc_cache_flush_threshold) {
flush_cache_all();
return;
}
- if (vma->vm_mm->context == mfsp(3)) {
- flush_user_dcache_range_asm(start, end);
- if (vma->vm_flags & VM_EXEC)
- flush_user_icache_range_asm(start, end);
- return;
- }
+ BUG_ON(vma->vm_mm->context != mfsp(3));
- pgd = vma->vm_mm->pgd;
- for (addr = start & PAGE_MASK; addr < end; addr += PAGE_SIZE) {
- unsigned long pfn;
- pte_t *ptep = get_ptep(pgd, addr);
- if (!ptep)
- continue;
- pfn = pte_pfn(*ptep);
- if (pfn_valid(pfn))
- __flush_cache_page(vma, addr, PFN_PHYS(pfn));
- }
+ flush_user_dcache_range_asm(start, end);
+ if (vma->vm_flags & VM_EXEC)
+ flush_user_icache_range_asm(start, end);
}
void
@@ -629,7 +622,8 @@
BUG_ON(!vma->vm_mm->context);
if (pfn_valid(pfn)) {
- flush_tlb_page(vma, vmaddr);
+ if (parisc_requires_coherency())
+ flush_tlb_page(vma, vmaddr);
__flush_cache_page(vma, vmaddr, PFN_PHYS(pfn));
}
}
diff --git a/arch/parisc/kernel/process.c b/arch/parisc/kernel/process.c
index e7ffde2..7593787 100644
--- a/arch/parisc/kernel/process.c
+++ b/arch/parisc/kernel/process.c
@@ -50,6 +50,7 @@
#include <linux/uaccess.h>
#include <linux/rcupdate.h>
#include <linux/random.h>
+#include <linux/nmi.h>
#include <asm/io.h>
#include <asm/asm-offsets.h>
@@ -142,6 +143,7 @@
/* prevent soft lockup/stalled CPU messages for endless loop. */
rcu_sysrq_start();
+ lockup_detector_suspend();
for (;;);
}
diff --git a/arch/powerpc/include/asm/topology.h b/arch/powerpc/include/asm/topology.h
index 3297715..8b3b46b 100644
--- a/arch/powerpc/include/asm/topology.h
+++ b/arch/powerpc/include/asm/topology.h
@@ -44,22 +44,8 @@
extern int sysfs_add_device_to_node(struct device *dev, int nid);
extern void sysfs_remove_device_from_node(struct device *dev, int nid);
-static inline int early_cpu_to_node(int cpu)
-{
- int nid;
-
- nid = numa_cpu_lookup_table[cpu];
-
- /*
- * Fall back to node 0 if nid is unset (it should be, except bugs).
- * This allows callers to safely do NODE_DATA(early_cpu_to_node(cpu)).
- */
- return (nid < 0) ? 0 : nid;
-}
#else
-static inline int early_cpu_to_node(int cpu) { return 0; }
-
static inline void dump_numa_cpu_topology(void) {}
static inline int sysfs_add_device_to_node(struct device *dev, int nid)
diff --git a/arch/powerpc/kernel/setup_64.c b/arch/powerpc/kernel/setup_64.c
index ada71be..a12be60 100644
--- a/arch/powerpc/kernel/setup_64.c
+++ b/arch/powerpc/kernel/setup_64.c
@@ -595,7 +595,7 @@
static void * __init pcpu_fc_alloc(unsigned int cpu, size_t size, size_t align)
{
- return __alloc_bootmem_node(NODE_DATA(early_cpu_to_node(cpu)), size, align,
+ return __alloc_bootmem_node(NODE_DATA(cpu_to_node(cpu)), size, align,
__pa(MAX_DMA_ADDRESS));
}
@@ -606,7 +606,7 @@
static int pcpu_cpu_distance(unsigned int from, unsigned int to)
{
- if (early_cpu_to_node(from) == early_cpu_to_node(to))
+ if (cpu_to_node(from) == cpu_to_node(to))
return LOCAL_DISTANCE;
else
return REMOTE_DISTANCE;
diff --git a/arch/powerpc/kvm/book3s_hv.c b/arch/powerpc/kvm/book3s_hv.c
index 5c02984..218cba2 100644
--- a/arch/powerpc/kvm/book3s_hv.c
+++ b/arch/powerpc/kvm/book3s_hv.c
@@ -2808,6 +2808,8 @@
int r;
int srcu_idx;
unsigned long ebb_regs[3] = {}; /* shut up GCC */
+ unsigned long user_tar = 0;
+ unsigned int user_vrsave;
if (!vcpu->arch.sane) {
run->exit_reason = KVM_EXIT_INTERNAL_ERROR;
@@ -2828,6 +2830,8 @@
run->fail_entry.hardware_entry_failure_reason = 0;
return -EINVAL;
}
+ /* Enable TM so we can read the TM SPRs */
+ mtmsr(mfmsr() | MSR_TM);
current->thread.tm_tfhar = mfspr(SPRN_TFHAR);
current->thread.tm_tfiar = mfspr(SPRN_TFIAR);
current->thread.tm_texasr = mfspr(SPRN_TEXASR);
@@ -2856,12 +2860,14 @@
flush_all_to_thread(current);
- /* Save userspace EBB register values */
+ /* Save userspace EBB and other register values */
if (cpu_has_feature(CPU_FTR_ARCH_207S)) {
ebb_regs[0] = mfspr(SPRN_EBBHR);
ebb_regs[1] = mfspr(SPRN_EBBRR);
ebb_regs[2] = mfspr(SPRN_BESCR);
+ user_tar = mfspr(SPRN_TAR);
}
+ user_vrsave = mfspr(SPRN_VRSAVE);
vcpu->arch.wqp = &vcpu->arch.vcore->wq;
vcpu->arch.pgdir = current->mm->pgd;
@@ -2885,12 +2891,15 @@
r = kvmppc_xics_rm_complete(vcpu, 0);
} while (is_kvmppc_resume_guest(r));
- /* Restore userspace EBB register values */
+ /* Restore userspace EBB and other register values */
if (cpu_has_feature(CPU_FTR_ARCH_207S)) {
mtspr(SPRN_EBBHR, ebb_regs[0]);
mtspr(SPRN_EBBRR, ebb_regs[1]);
mtspr(SPRN_BESCR, ebb_regs[2]);
+ mtspr(SPRN_TAR, user_tar);
+ mtspr(SPRN_FSCR, current->thread.fscr);
}
+ mtspr(SPRN_VRSAVE, user_vrsave);
out:
vcpu->arch.state = KVMPPC_VCPU_NOTREADY;
diff --git a/arch/powerpc/kvm/book3s_hv_rmhandlers.S b/arch/powerpc/kvm/book3s_hv_rmhandlers.S
index 6f81adb..0447a22 100644
--- a/arch/powerpc/kvm/book3s_hv_rmhandlers.S
+++ b/arch/powerpc/kvm/book3s_hv_rmhandlers.S
@@ -37,6 +37,13 @@
#define NAPPING_CEDE 1
#define NAPPING_NOVCPU 2
+/* Stack frame offsets for kvmppc_hv_entry */
+#define SFS 112
+#define STACK_SLOT_TRAP (SFS-4)
+#define STACK_SLOT_CIABR (SFS-16)
+#define STACK_SLOT_DAWR (SFS-24)
+#define STACK_SLOT_DAWRX (SFS-32)
+
/*
* Call kvmppc_hv_entry in real mode.
* Must be called with interrupts hard-disabled.
@@ -289,10 +296,10 @@
bl kvmhv_accumulate_time
#endif
13: mr r3, r12
- stw r12, 112-4(r1)
+ stw r12, STACK_SLOT_TRAP(r1)
bl kvmhv_commence_exit
nop
- lwz r12, 112-4(r1)
+ lwz r12, STACK_SLOT_TRAP(r1)
b kvmhv_switch_to_host
/*
@@ -537,7 +544,7 @@
*/
mflr r0
std r0, PPC_LR_STKOFF(r1)
- stdu r1, -112(r1)
+ stdu r1, -SFS(r1)
/* Save R1 in the PACA */
std r1, HSTATE_HOST_R1(r13)
@@ -698,6 +705,16 @@
mtspr SPRN_PURR,r7
mtspr SPRN_SPURR,r8
+ /* Save host values of some registers */
+BEGIN_FTR_SECTION
+ mfspr r5, SPRN_CIABR
+ mfspr r6, SPRN_DAWR
+ mfspr r7, SPRN_DAWRX
+ std r5, STACK_SLOT_CIABR(r1)
+ std r6, STACK_SLOT_DAWR(r1)
+ std r7, STACK_SLOT_DAWRX(r1)
+END_FTR_SECTION_IFSET(CPU_FTR_ARCH_207S)
+
BEGIN_FTR_SECTION
/* Set partition DABR */
/* Do this before re-enabling PMU to avoid P7 DABR corruption bug */
@@ -1361,8 +1378,7 @@
*/
li r0, 0
mtspr SPRN_IAMR, r0
- mtspr SPRN_CIABR, r0
- mtspr SPRN_DAWRX, r0
+ mtspr SPRN_PSPB, r0
mtspr SPRN_TCSCR, r0
mtspr SPRN_WORT, r0
/* Set MMCRS to 1<<31 to freeze and disable the SPMC counters */
@@ -1378,6 +1394,7 @@
std r6,VCPU_UAMOR(r9)
li r6,0
mtspr SPRN_AMR,r6
+ mtspr SPRN_UAMOR, r6
/* Switch DSCR back to host value */
mfspr r8, SPRN_DSCR
@@ -1519,6 +1536,16 @@
slbia
ptesync
+ /* Restore host values of some registers */
+BEGIN_FTR_SECTION
+ ld r5, STACK_SLOT_CIABR(r1)
+ ld r6, STACK_SLOT_DAWR(r1)
+ ld r7, STACK_SLOT_DAWRX(r1)
+ mtspr SPRN_CIABR, r5
+ mtspr SPRN_DAWR, r6
+ mtspr SPRN_DAWRX, r7
+END_FTR_SECTION_IFSET(CPU_FTR_ARCH_207S)
+
/*
* POWER7/POWER8 guest -> host partition switch code.
* We don't have to lock against tlbies but we do
@@ -1652,8 +1679,8 @@
li r0, KVM_GUEST_MODE_NONE
stb r0, HSTATE_IN_GUEST(r13)
- ld r0, 112+PPC_LR_STKOFF(r1)
- addi r1, r1, 112
+ ld r0, SFS+PPC_LR_STKOFF(r1)
+ addi r1, r1, SFS
mtlr r0
blr
diff --git a/arch/powerpc/platforms/pseries/reconfig.c b/arch/powerpc/platforms/pseries/reconfig.c
index cc66c49..666ad06 100644
--- a/arch/powerpc/platforms/pseries/reconfig.c
+++ b/arch/powerpc/platforms/pseries/reconfig.c
@@ -82,7 +82,6 @@
of_detach_node(np);
of_node_put(parent);
- of_node_put(np); /* Must decrement the refcount */
return 0;
}
diff --git a/arch/x86/events/intel/cstate.c b/arch/x86/events/intel/cstate.c
index fec8a46..1076c9a 100644
--- a/arch/x86/events/intel/cstate.c
+++ b/arch/x86/events/intel/cstate.c
@@ -434,6 +434,7 @@
.stop = cstate_pmu_event_stop,
.read = cstate_pmu_event_update,
.capabilities = PERF_PMU_CAP_NO_INTERRUPT,
+ .module = THIS_MODULE,
};
static struct pmu cstate_pkg_pmu = {
@@ -447,6 +448,7 @@
.stop = cstate_pmu_event_stop,
.read = cstate_pmu_event_update,
.capabilities = PERF_PMU_CAP_NO_INTERRUPT,
+ .module = THIS_MODULE,
};
static const struct cstate_model nhm_cstates __initconst = {
diff --git a/arch/x86/events/intel/rapl.c b/arch/x86/events/intel/rapl.c
index 8b902b6..970c1de 100644
--- a/arch/x86/events/intel/rapl.c
+++ b/arch/x86/events/intel/rapl.c
@@ -697,6 +697,7 @@
rapl_pmus->pmu.start = rapl_pmu_event_start;
rapl_pmus->pmu.stop = rapl_pmu_event_stop;
rapl_pmus->pmu.read = rapl_pmu_event_read;
+ rapl_pmus->pmu.module = THIS_MODULE;
return 0;
}
diff --git a/arch/x86/events/intel/uncore.c b/arch/x86/events/intel/uncore.c
index 19d646a..aec6cc9 100644
--- a/arch/x86/events/intel/uncore.c
+++ b/arch/x86/events/intel/uncore.c
@@ -733,6 +733,7 @@
.start = uncore_pmu_event_start,
.stop = uncore_pmu_event_stop,
.read = uncore_pmu_event_read,
+ .module = THIS_MODULE,
};
} else {
pmu->pmu = *pmu->type->pmu;
diff --git a/arch/x86/kernel/cpu/mcheck/mce_amd.c b/arch/x86/kernel/cpu/mcheck/mce_amd.c
index 3dfca7b..a5b47c1 100644
--- a/arch/x86/kernel/cpu/mcheck/mce_amd.c
+++ b/arch/x86/kernel/cpu/mcheck/mce_amd.c
@@ -955,6 +955,9 @@
const char *name = get_name(bank, NULL);
int err = 0;
+ if (!dev)
+ return -ENODEV;
+
if (is_shared_bank(bank)) {
nb = node_to_amd_nb(amd_get_nb_id(cpu));
diff --git a/arch/x86/platform/intel-mid/device_libs/Makefile b/arch/x86/platform/intel-mid/device_libs/Makefile
index dd6cfa4..75029d0 100644
--- a/arch/x86/platform/intel-mid/device_libs/Makefile
+++ b/arch/x86/platform/intel-mid/device_libs/Makefile
@@ -15,7 +15,7 @@
obj-$(subst m,y,$(CONFIG_GPIO_INTEL_PMIC)) += platform_pmic_gpio.o
obj-$(subst m,y,$(CONFIG_INTEL_MFLD_THERMAL)) += platform_msic_thermal.o
# SPI Devices
-obj-$(subst m,y,$(CONFIG_SPI_SPIDEV)) += platform_spidev.o
+obj-$(subst m,y,$(CONFIG_SPI_SPIDEV)) += platform_mrfld_spidev.o
# I2C Devices
obj-$(subst m,y,$(CONFIG_SENSORS_EMC1403)) += platform_emc1403.o
obj-$(subst m,y,$(CONFIG_SENSORS_LIS3LV02D)) += platform_lis331.o
diff --git a/arch/x86/platform/intel-mid/device_libs/platform_spidev.c b/arch/x86/platform/intel-mid/device_libs/platform_mrfld_spidev.c
similarity index 91%
rename from arch/x86/platform/intel-mid/device_libs/platform_spidev.c
rename to arch/x86/platform/intel-mid/device_libs/platform_mrfld_spidev.c
index 30c601b..27186ad 100644
--- a/arch/x86/platform/intel-mid/device_libs/platform_spidev.c
+++ b/arch/x86/platform/intel-mid/device_libs/platform_mrfld_spidev.c
@@ -11,6 +11,7 @@
* of the License.
*/
+#include <linux/err.h>
#include <linux/init.h>
#include <linux/sfi.h>
#include <linux/spi/pxa2xx_spi.h>
@@ -34,6 +35,9 @@
{
struct spi_board_info *spi_info = info;
+ if (intel_mid_identify_cpu() != INTEL_MID_CPU_CHIP_TANGIER)
+ return ERR_PTR(-ENODEV);
+
spi_info->mode = SPI_MODE_0;
spi_info->controller_data = &spidev_spi_chip;
diff --git a/crypto/authencesn.c b/crypto/authencesn.c
index 121010a..18c94e1 100644
--- a/crypto/authencesn.c
+++ b/crypto/authencesn.c
@@ -248,6 +248,9 @@
u8 *ihash = ohash + crypto_ahash_digestsize(auth);
u32 tmp[2];
+ if (!authsize)
+ goto decrypt;
+
/* Move high-order bits of sequence number back. */
scatterwalk_map_and_copy(tmp, dst, 4, 4, 0);
scatterwalk_map_and_copy(tmp + 1, dst, assoclen + cryptlen, 4, 0);
@@ -256,6 +259,8 @@
if (crypto_memneq(ihash, ohash, authsize))
return -EBADMSG;
+decrypt:
+
sg_init_table(areq_ctx->dst, 2);
dst = scatterwalk_ffwd(areq_ctx->dst, dst, assoclen);
diff --git a/drivers/acpi/glue.c b/drivers/acpi/glue.c
index 5ea5dc2..73c9c7f 100644
--- a/drivers/acpi/glue.c
+++ b/drivers/acpi/glue.c
@@ -98,7 +98,15 @@
if (check_children && list_empty(&adev->children))
return -ENODEV;
- return sta_present ? FIND_CHILD_MAX_SCORE : FIND_CHILD_MIN_SCORE;
+ /*
+ * If the device has a _HID (or _CID) returning a valid ACPI/PNP
+ * device ID, it is better to make it look less attractive here, so that
+ * the other device with the same _ADR value (that may not have a valid
+ * device ID) can be matched going forward. [This means a second spec
+ * violation in a row, so whatever we do here is best effort anyway.]
+ */
+ return sta_present && list_empty(&adev->pnp.ids) ?
+ FIND_CHILD_MAX_SCORE : FIND_CHILD_MIN_SCORE;
}
struct acpi_device *acpi_find_child_device(struct acpi_device *parent,
diff --git a/drivers/android/binder.c b/drivers/android/binder.c
index 632c814..88edacd 100644
--- a/drivers/android/binder.c
+++ b/drivers/android/binder.c
@@ -1013,7 +1013,7 @@
{
struct binder_thread *thread;
- BUG_ON(!spin_is_locked(&proc->inner_lock));
+ assert_spin_locked(&proc->inner_lock);
thread = list_first_entry_or_null(&proc->waiting_threads,
struct binder_thread,
waiting_thread_node);
@@ -1044,7 +1044,7 @@
struct binder_thread *thread,
bool sync)
{
- BUG_ON(!spin_is_locked(&proc->inner_lock));
+ assert_spin_locked(&proc->inner_lock);
if (thread) {
if (sync)
@@ -1223,7 +1223,7 @@
struct rb_node *n = proc->nodes.rb_node;
struct binder_node *node;
- BUG_ON(!spin_is_locked(&proc->inner_lock));
+ assert_spin_locked(&proc->inner_lock);
while (n) {
node = rb_entry(n, struct binder_node, rb_node);
@@ -1269,7 +1269,8 @@
__u32 flags = fp ? fp->flags : 0;
s8 priority;
- BUG_ON(!spin_is_locked(&proc->inner_lock));
+ assert_spin_locked(&proc->inner_lock);
+
while (*p) {
parent = *p;
@@ -1348,9 +1349,9 @@
{
struct binder_proc *proc = node->proc;
- BUG_ON(!spin_is_locked(&node->lock));
+ assert_spin_locked(&node->lock);
if (proc)
- BUG_ON(!spin_is_locked(&proc->inner_lock));
+ assert_spin_locked(&proc->inner_lock);
if (strong) {
if (internal) {
if (target_list == NULL &&
@@ -1402,9 +1403,9 @@
{
struct binder_proc *proc = node->proc;
- BUG_ON(!spin_is_locked(&node->lock));
+ assert_spin_locked(&node->lock);
if (proc)
- BUG_ON(!spin_is_locked(&proc->inner_lock));
+ assert_spin_locked(&proc->inner_lock);
if (strong) {
if (internal)
node->internal_strong_refs--;
@@ -1928,7 +1929,7 @@
struct binder_transaction *t)
{
BUG_ON(!target_thread);
- BUG_ON(!spin_is_locked(&target_thread->proc->inner_lock));
+ assert_spin_locked(&target_thread->proc->inner_lock);
BUG_ON(target_thread->transaction_stack != t);
BUG_ON(target_thread->transaction_stack->from != target_thread);
target_thread->transaction_stack =
@@ -5071,7 +5072,6 @@
struct binder_proc *to_proc;
struct binder_buffer *buffer = t->buffer;
- WARN_ON(!spin_is_locked(&proc->inner_lock));
spin_lock(&t->lock);
to_proc = t->to_proc;
seq_printf(m,
@@ -5160,7 +5160,6 @@
size_t start_pos = m->count;
size_t header_pos;
- WARN_ON(!spin_is_locked(&thread->proc->inner_lock));
seq_printf(m, " thread %d: l %02x need_return %d tr %d\n",
thread->pid, thread->looper,
thread->looper_need_return,
@@ -5197,10 +5196,6 @@
struct binder_work *w;
int count;
- WARN_ON(!spin_is_locked(&node->lock));
- if (node->proc)
- WARN_ON(!spin_is_locked(&node->proc->inner_lock));
-
count = 0;
hlist_for_each_entry(ref, &node->refs, node_entry)
count++;
@@ -5227,7 +5222,6 @@
static void print_binder_ref_olocked(struct seq_file *m,
struct binder_ref *ref)
{
- WARN_ON(!spin_is_locked(&ref->proc->outer_lock));
binder_node_lock(ref->node);
seq_printf(m, " ref %d: desc %d %snode %d s %d w %d d %pK\n",
ref->data.debug_id, ref->data.desc,
diff --git a/drivers/char/ipmi/ipmi_watchdog.c b/drivers/char/ipmi/ipmi_watchdog.c
index 4facc75..9093110 100644
--- a/drivers/char/ipmi/ipmi_watchdog.c
+++ b/drivers/char/ipmi/ipmi_watchdog.c
@@ -1162,10 +1162,11 @@
ipmi_watchdog_state = WDOG_TIMEOUT_NONE;
ipmi_set_timeout(IPMI_SET_TIMEOUT_NO_HB);
} else if (ipmi_watchdog_state != WDOG_TIMEOUT_NONE) {
- /* Set a long timer to let the reboot happens, but
- reboot if it hangs, but only if the watchdog
+ /* Set a long timer to let the reboot happen or
+ reset if it hangs, but only if the watchdog
timer was already running. */
- timeout = 120;
+ if (timeout < 120)
+ timeout = 120;
pretimeout = 0;
ipmi_watchdog_state = WDOG_TIMEOUT_RESET;
ipmi_set_timeout(IPMI_SET_TIMEOUT_NO_HB);
diff --git a/drivers/clk/qcom/camcc-sdm845.c b/drivers/clk/qcom/camcc-sdm845.c
index 1984d4a..3819959 100644
--- a/drivers/clk/qcom/camcc-sdm845.c
+++ b/drivers/clk/qcom/camcc-sdm845.c
@@ -1959,6 +1959,7 @@
static const struct of_device_id cam_cc_sdm845_match_table[] = {
{ .compatible = "qcom,cam_cc-sdm845" },
{ .compatible = "qcom,cam_cc-sdm845-v2" },
+ { .compatible = "qcom,cam_cc-sdm670" },
{ }
};
MODULE_DEVICE_TABLE(of, cam_cc_sdm845_match_table);
@@ -1986,6 +1987,11 @@
cam_cc_slow_ahb_clk_src.clkr.hw.init->rate_max[VDD_CX_LOWER] = 80000000;
}
+static void cam_cc_sdm845_fixup_sdm670(void)
+{
+ cam_cc_sdm845_fixup_sdm845v2();
+}
+
static int cam_cc_sdm845_fixup(struct platform_device *pdev)
{
const char *compat = NULL;
@@ -1997,6 +2003,8 @@
if (!strcmp(compat, "qcom,cam_cc-sdm845-v2"))
cam_cc_sdm845_fixup_sdm845v2();
+ else if (!strcmp(compat, "qcom,cam_cc-sdm670"))
+ cam_cc_sdm845_fixup_sdm670();
return 0;
}
diff --git a/drivers/clk/qcom/clk-rpmh.c b/drivers/clk/qcom/clk-rpmh.c
index e1cda90..2109132 100644
--- a/drivers/clk/qcom/clk-rpmh.c
+++ b/drivers/clk/qcom/clk-rpmh.c
@@ -317,10 +317,32 @@
static const struct of_device_id clk_rpmh_match_table[] = {
{ .compatible = "qcom,rpmh-clk-sdm845", .data = &clk_rpmh_sdm845},
+ { .compatible = "qcom,rpmh-clk-sdm670", .data = &clk_rpmh_sdm845},
{ }
};
MODULE_DEVICE_TABLE(of, clk_rpmh_match_table);
+static void clk_rpmh_sdm670_fixup_sdm670(void)
+{
+ sdm845_rpmh_clocks[RPMH_RF_CLK3] = NULL;
+ sdm845_rpmh_clocks[RPMH_RF_CLK3_A] = NULL;
+}
+
+static int clk_rpmh_sdm670_fixup(struct platform_device *pdev)
+{
+ const char *compat = NULL;
+ int compatlen = 0;
+
+ compat = of_get_property(pdev->dev.of_node, "compatible", &compatlen);
+ if (!compat || (compatlen <= 0))
+ return -EINVAL;
+
+ if (!strcmp(compat, "qcom,rpmh-clk-sdm670"))
+ clk_rpmh_sdm670_fixup_sdm670();
+
+ return 0;
+}
+
static int clk_rpmh_probe(struct platform_device *pdev)
{
struct clk **clks;
@@ -388,6 +410,10 @@
goto err2;
}
+ ret = clk_rpmh_sdm670_fixup(pdev);
+ if (ret)
+ return ret;
+
hw_clks = desc->clks;
num_clks = desc->num_clks;
@@ -404,6 +430,11 @@
data->clk_num = num_clks;
for (i = 0; i < num_clks; i++) {
+ if (!hw_clks[i]) {
+ clks[i] = ERR_PTR(-ENOENT);
+ continue;
+ }
+
rpmh_clk = to_clk_rpmh(hw_clks[i]);
rpmh_clk->res_addr = cmd_db_get_addr(rpmh_clk->res_name);
if (!rpmh_clk->res_addr) {
diff --git a/drivers/clk/qcom/debugcc-sdm845.c b/drivers/clk/qcom/debugcc-sdm845.c
index cb0cadd..ef1da5c 100644
--- a/drivers/clk/qcom/debugcc-sdm845.c
+++ b/drivers/clk/qcom/debugcc-sdm845.c
@@ -235,6 +235,9 @@
"gcc_video_ahb_clk",
"gcc_video_axi_clk",
"gcc_video_xo_clk",
+ "gcc_sdcc1_ahb_clk",
+ "gcc_sdcc1_apps_clk",
+ "gcc_sdcc1_ice_core_clk",
"gpu_cc_acd_cxo_clk",
"gpu_cc_ahb_clk",
"gpu_cc_crc_ahb_clk",
@@ -685,6 +688,12 @@
0x3F, 0x3FF, 0, 0xF, 0, 4, 0x62008, 0x62000, 0x62004 },
{ "gcc_video_xo_clk", 0x42, 4, GCC,
0x42, 0x3FF, 0, 0xF, 0, 4, 0x62008, 0x62000, 0x62004 },
+ { "gcc_sdcc1_ahb_clk", 0x15C, 4, GCC,
+ 0x42, 0x3FF, 0, 0xF, 0, 4, 0x62008, 0x62000, 0x62004 },
+ { "gcc_sdcc1_apps_clk", 0x15B, 4, GCC,
+ 0x42, 0x3FF, 0, 0xF, 0, 4, 0x62008, 0x62000, 0x62004 },
+ { "gcc_sdcc1_ice_core_clk", 0x15D, 4, GCC,
+ 0x42, 0x3FF, 0, 0xF, 0, 4, 0x62008, 0x62000, 0x62004 },
{ "gpu_cc_acd_cxo_clk", 0x144, 4, GPU_CC,
0x1F, 0xFF, 0, 0x3, 0, 1, 0x1568, 0x10FC, 0x1100 },
{ "gpu_cc_ahb_clk", 0x144, 4, GPU_CC,
diff --git a/drivers/clk/qcom/dispcc-sdm845.c b/drivers/clk/qcom/dispcc-sdm845.c
index 53bfe77..d57bf5f 100644
--- a/drivers/clk/qcom/dispcc-sdm845.c
+++ b/drivers/clk/qcom/dispcc-sdm845.c
@@ -1014,6 +1014,7 @@
static const struct of_device_id disp_cc_sdm845_match_table[] = {
{ .compatible = "qcom,dispcc-sdm845" },
{ .compatible = "qcom,dispcc-sdm845-v2" },
+ { .compatible = "qcom,dispcc-sdm670" },
{ }
};
MODULE_DEVICE_TABLE(of, disp_cc_sdm845_match_table);
@@ -1064,6 +1065,11 @@
430000000;
}
+static void disp_cc_sdm845_fixup_sdm670(struct regmap *regmap)
+{
+ disp_cc_sdm845_fixup_sdm845v2(regmap);
+}
+
static int disp_cc_sdm845_fixup(struct platform_device *pdev,
struct regmap *regmap)
{
@@ -1076,6 +1082,8 @@
if (!strcmp(compat, "qcom,dispcc-sdm845-v2"))
disp_cc_sdm845_fixup_sdm845v2(regmap);
+ else if (!strcmp(compat, "qcom,dispcc-sdm670"))
+ disp_cc_sdm845_fixup_sdm670(regmap);
return 0;
}
diff --git a/drivers/clk/qcom/gcc-sdm845.c b/drivers/clk/qcom/gcc-sdm845.c
index 17b2403..a363235 100644
--- a/drivers/clk/qcom/gcc-sdm845.c
+++ b/drivers/clk/qcom/gcc-sdm845.c
@@ -198,6 +198,22 @@
"core_bi_pll_test_se",
};
+static const struct parent_map gcc_parent_map_7[] = {
+ { P_BI_TCXO, 0 },
+ { P_GPLL0_OUT_MAIN, 1 },
+ { P_GPLL6_OUT_MAIN, 2 },
+ { P_GPLL0_OUT_EVEN, 6 },
+ { P_CORE_BI_PLL_TEST_SE, 7 },
+};
+
+static const char * const gcc_parent_names_11[] = {
+ "bi_tcxo",
+ "gpll0",
+ "gpll6",
+ "gpll0_out_even",
+ "core_bi_pll_test_se",
+};
+
static struct clk_dummy measure_only_snoc_clk = {
.rrate = 1000,
.hw.init = &(struct clk_init_data){
@@ -301,6 +317,28 @@
},
};
+static struct clk_alpha_pll gpll6 = {
+ .offset = 0x13000,
+ .vco_table = fabia_vco,
+ .num_vco = ARRAY_SIZE(fabia_vco),
+ .type = FABIA_PLL,
+ .clkr = {
+ .enable_reg = 0x52000,
+ .enable_mask = BIT(6),
+ .hw.init = &(struct clk_init_data){
+ .name = "gpll6",
+ .parent_names = (const char *[]){ "bi_tcxo" },
+ .num_parents = 1,
+ .ops = &clk_fabia_fixed_pll_ops,
+ VDD_CX_FMAX_MAP4(
+ MIN, 615000000,
+ LOW, 1066000000,
+ LOW_L1, 1600000000,
+ NOMINAL, 2000000000),
+ },
+ },
+};
+
static const struct freq_tbl ftbl_gcc_cpuss_ahb_clk_src[] = {
F(19200000, P_BI_TCXO, 1, 0, 0),
{ }
@@ -330,6 +368,12 @@
{ }
};
+static const struct freq_tbl ftbl_gcc_cpuss_rbcpr_clk_src_sdm670[] = {
+ F(19200000, P_BI_TCXO, 1, 0, 0),
+ F(50000000, P_GPLL0_OUT_MAIN, 12, 0, 0),
+ { }
+};
+
static struct clk_rcg2 gcc_cpuss_rbcpr_clk_src = {
.cmd_rcgr = 0x4815c,
.mnd_width = 0,
@@ -862,6 +906,67 @@
},
};
+static const struct freq_tbl ftbl_gcc_sdcc1_ice_core_clk_src[] = {
+ F(75000000, P_GPLL0_OUT_EVEN, 4, 0, 0),
+ F(150000000, P_GPLL0_OUT_MAIN, 4, 0, 0),
+ F(200000000, P_GPLL0_OUT_MAIN, 3, 0, 0),
+ F(300000000, P_GPLL0_OUT_MAIN, 2, 0, 0),
+ { }
+};
+
+static struct clk_rcg2 gcc_sdcc1_ice_core_clk_src = {
+ .cmd_rcgr = 0x26010,
+ .mnd_width = 8,
+ .hid_width = 5,
+ .parent_map = gcc_parent_map_0,
+ .freq_tbl = ftbl_gcc_sdcc1_ice_core_clk_src,
+ .enable_safe_config = true,
+ .clkr.hw.init = &(struct clk_init_data){
+ .name = "gcc_sdcc1_ice_core_clk_src",
+ .parent_names = gcc_parent_names_0,
+ .num_parents = 4,
+ .flags = CLK_SET_RATE_PARENT,
+ .ops = &clk_rcg2_ops,
+ VDD_CX_FMAX_MAP3(
+ MIN, 75000000,
+ LOW, 150000000,
+ NOMINAL, 300000000),
+ },
+};
+
+static const struct freq_tbl ftbl_gcc_sdcc1_apps_clk_src[] = {
+ F(144000, P_BI_TCXO, 16, 3, 25),
+ F(400000, P_BI_TCXO, 12, 1, 4),
+ F(20000000, P_GPLL0_OUT_EVEN, 5, 1, 3),
+ F(25000000, P_GPLL0_OUT_EVEN, 6, 1, 2),
+ F(50000000, P_GPLL0_OUT_EVEN, 6, 0, 0),
+ F(100000000, P_GPLL0_OUT_MAIN, 6, 0, 0),
+ F(192000000, P_GPLL6_OUT_MAIN, 2, 0, 0),
+ F(384000000, P_GPLL6_OUT_MAIN, 1, 0, 0),
+ { }
+};
+
+static struct clk_rcg2 gcc_sdcc1_apps_clk_src = {
+ .cmd_rcgr = 0x26028,
+ .mnd_width = 8,
+ .hid_width = 5,
+ .parent_map = gcc_parent_map_7,
+ .freq_tbl = ftbl_gcc_sdcc1_apps_clk_src,
+ .enable_safe_config = true,
+ .clkr.hw.init = &(struct clk_init_data){
+ .name = "gcc_sdcc1_apps_clk_src",
+ .parent_names = gcc_parent_names_11,
+ .num_parents = 5,
+ .flags = CLK_SET_RATE_PARENT,
+ .ops = &clk_rcg2_ops,
+ VDD_CX_FMAX_MAP4(
+ MIN, 19200000,
+ LOWER, 50000000,
+ LOW, 100000000,
+ NOMINAL, 384000000),
+ },
+};
+
static const struct freq_tbl ftbl_gcc_sdcc2_apps_clk_src[] = {
F(400000, P_BI_TCXO, 12, 1, 4),
F(9600000, P_BI_TCXO, 2, 0, 0),
@@ -904,17 +1009,28 @@
{ }
};
+static const struct freq_tbl ftbl_gcc_sdcc4_apps_clk_src_sdm670[] = {
+ F(400000, P_BI_TCXO, 12, 1, 4),
+ F(9600000, P_BI_TCXO, 2, 0, 0),
+ F(19200000, P_BI_TCXO, 1, 0, 0),
+ F(25000000, P_GPLL0_OUT_EVEN, 12, 0, 0),
+ F(33333333, P_GPLL0_OUT_EVEN, 9, 0, 0),
+ F(50000000, P_GPLL0_OUT_MAIN, 12, 0, 0),
+ F(100000000, P_GPLL0_OUT_MAIN, 6, 0, 0),
+ { }
+};
+
static struct clk_rcg2 gcc_sdcc4_apps_clk_src = {
.cmd_rcgr = 0x1600c,
.mnd_width = 8,
.hid_width = 5,
- .parent_map = gcc_parent_map_3,
+ .parent_map = gcc_parent_map_0,
.freq_tbl = ftbl_gcc_sdcc4_apps_clk_src,
.enable_safe_config = true,
.clkr.hw.init = &(struct clk_init_data){
.name = "gcc_sdcc4_apps_clk_src",
- .parent_names = gcc_parent_names_3,
- .num_parents = 3,
+ .parent_names = gcc_parent_names_0,
+ .num_parents = 4,
.flags = CLK_SET_RATE_PARENT,
.ops = &clk_rcg2_ops,
VDD_CX_FMAX_MAP4(
@@ -2700,6 +2816,55 @@
},
};
+static struct clk_branch gcc_sdcc1_ice_core_clk = {
+ .halt_reg = 0x2600c,
+ .halt_check = BRANCH_HALT,
+ .clkr = {
+ .enable_reg = 0x2600c,
+ .enable_mask = BIT(0),
+ .hw.init = &(struct clk_init_data){
+ .name = "gcc_sdcc1_ice_core_clk",
+ .parent_names = (const char *[]){
+ "gcc_sdcc1_ice_core_clk_src",
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ .ops = &clk_branch2_ops,
+ },
+ },
+};
+
+static struct clk_branch gcc_sdcc1_ahb_clk = {
+ .halt_reg = 0x26008,
+ .halt_check = BRANCH_HALT,
+ .clkr = {
+ .enable_reg = 0x26008,
+ .enable_mask = BIT(0),
+ .hw.init = &(struct clk_init_data){
+ .name = "gcc_sdcc1_ahb_clk",
+ .ops = &clk_branch2_ops,
+ },
+ },
+};
+
+static struct clk_branch gcc_sdcc1_apps_clk = {
+ .halt_reg = 0x26004,
+ .halt_check = BRANCH_HALT,
+ .clkr = {
+ .enable_reg = 0x26004,
+ .enable_mask = BIT(0),
+ .hw.init = &(struct clk_init_data){
+ .name = "gcc_sdcc1_apps_clk",
+ .parent_names = (const char *[]){
+ "gcc_sdcc1_apps_clk_src",
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ .ops = &clk_branch2_ops,
+ },
+ },
+};
+
static struct clk_branch gcc_sdcc2_ahb_clk = {
.halt_reg = 0x14008,
.halt_check = BRANCH_HALT,
@@ -3824,6 +3989,12 @@
[GPLL0] = &gpll0.clkr,
[GPLL0_OUT_EVEN] = &gpll0_out_even.clkr,
[GPLL4] = &gpll4.clkr,
+ [GCC_SDCC1_AHB_CLK] = NULL,
+ [GCC_SDCC1_APPS_CLK] = NULL,
+ [GCC_SDCC1_ICE_CORE_CLK] = NULL,
+ [GCC_SDCC1_APPS_CLK_SRC] = NULL,
+ [GCC_SDCC1_ICE_CORE_CLK_SRC] = NULL,
+ [GPLL6] = NULL,
};
static const struct qcom_reset_map gcc_sdm845_resets[] = {
@@ -3853,6 +4024,7 @@
[GCC_USB_PHY_CFG_AHB2PHY_BCR] = { 0x6a000 },
[GCC_PCIE_0_PHY_BCR] = { 0x6c01c },
[GCC_PCIE_1_PHY_BCR] = { 0x8e01c },
+ [GCC_SDCC1_BCR] = { 0x26000 },
};
/* List of RCG clocks and corresponding flags requested for DFS Mode */
@@ -3899,6 +4071,7 @@
static const struct of_device_id gcc_sdm845_match_table[] = {
{ .compatible = "qcom,gcc-sdm845" },
{ .compatible = "qcom,gcc-sdm845-v2" },
+ { .compatible = "qcom,gcc-sdm670" },
{ }
};
MODULE_DEVICE_TABLE(of, gcc_sdm845_match_table);
@@ -4009,6 +4182,86 @@
ftbl_gcc_ufs_card_axi_clk_src_sdm845_v2;
}
+static void gcc_sdm845_fixup_sdm670(void)
+{
+ gcc_sdm845_fixup_sdm845v2();
+
+ gcc_sdm845_clocks[GCC_SDCC1_AHB_CLK] = &gcc_sdcc1_ahb_clk.clkr;
+ gcc_sdm845_clocks[GCC_SDCC1_APPS_CLK] = &gcc_sdcc1_apps_clk.clkr;
+ gcc_sdm845_clocks[GCC_SDCC1_ICE_CORE_CLK] =
+ &gcc_sdcc1_ice_core_clk.clkr;
+ gcc_sdm845_clocks[GCC_SDCC1_APPS_CLK_SRC] =
+ &gcc_sdcc1_apps_clk_src.clkr;
+ gcc_sdm845_clocks[GCC_SDCC1_ICE_CORE_CLK_SRC] =
+ &gcc_sdcc1_ice_core_clk_src.clkr;
+ gcc_sdm845_clocks[GPLL6] = &gpll6.clkr;
+ gcc_sdm845_clocks[GCC_AGGRE_UFS_CARD_AXI_CLK] = NULL;
+ gcc_sdm845_clocks[GCC_AGGRE_UFS_CARD_AXI_HW_CTL_CLK] = NULL;
+ gcc_sdm845_clocks[GCC_AGGRE_USB3_SEC_AXI_CLK] = NULL;
+ gcc_sdm845_clocks[GCC_AGGRE_NOC_PCIE_TBU_CLK] = NULL;
+ gcc_sdm845_clocks[GCC_CFG_NOC_USB3_SEC_AXI_CLK] = NULL;
+ gcc_sdm845_clocks[GCC_PCIE_0_AUX_CLK] = NULL;
+ gcc_sdm845_clocks[GCC_PCIE_0_AUX_CLK_SRC] = NULL;
+ gcc_sdm845_clocks[GCC_PCIE_0_CFG_AHB_CLK] = NULL;
+ gcc_sdm845_clocks[GCC_PCIE_0_CLKREF_CLK] = NULL;
+ gcc_sdm845_clocks[GCC_PCIE_0_MSTR_AXI_CLK] = NULL;
+ gcc_sdm845_clocks[GCC_PCIE_0_PIPE_CLK] = NULL;
+ gcc_sdm845_clocks[GCC_PCIE_0_SLV_AXI_CLK] = NULL;
+ gcc_sdm845_clocks[GCC_PCIE_0_SLV_Q2A_AXI_CLK] = NULL;
+ gcc_sdm845_clocks[GCC_PCIE_1_AUX_CLK] = NULL;
+ gcc_sdm845_clocks[GCC_PCIE_1_AUX_CLK_SRC] = NULL;
+ gcc_sdm845_clocks[GCC_PCIE_1_CFG_AHB_CLK] = NULL;
+ gcc_sdm845_clocks[GCC_PCIE_1_CLKREF_CLK] = NULL;
+ gcc_sdm845_clocks[GCC_PCIE_1_MSTR_AXI_CLK] = NULL;
+ gcc_sdm845_clocks[GCC_PCIE_1_PIPE_CLK] = NULL;
+ gcc_sdm845_clocks[GCC_PCIE_1_SLV_AXI_CLK] = NULL;
+ gcc_sdm845_clocks[GCC_PCIE_1_SLV_Q2A_AXI_CLK] = NULL;
+ gcc_sdm845_clocks[GCC_PCIE_PHY_AUX_CLK] = NULL;
+ gcc_sdm845_clocks[GCC_PCIE_PHY_REFGEN_CLK] = NULL;
+ gcc_sdm845_clocks[GCC_PCIE_PHY_REFGEN_CLK_SRC] = NULL;
+ gcc_sdm845_clocks[GCC_UFS_CARD_AHB_CLK] = NULL;
+ gcc_sdm845_clocks[GCC_UFS_CARD_AXI_CLK] = NULL;
+ gcc_sdm845_clocks[GCC_UFS_CARD_AXI_HW_CTL_CLK] = NULL;
+ gcc_sdm845_clocks[GCC_UFS_CARD_AXI_CLK_SRC] = NULL;
+ gcc_sdm845_clocks[GCC_UFS_CARD_CLKREF_CLK] = NULL;
+ gcc_sdm845_clocks[GCC_UFS_CARD_ICE_CORE_CLK] = NULL;
+ gcc_sdm845_clocks[GCC_UFS_CARD_ICE_CORE_HW_CTL_CLK] = NULL;
+ gcc_sdm845_clocks[GCC_UFS_CARD_ICE_CORE_CLK_SRC] = NULL;
+ gcc_sdm845_clocks[GCC_UFS_CARD_PHY_AUX_CLK] = NULL;
+ gcc_sdm845_clocks[GCC_UFS_CARD_PHY_AUX_HW_CTL_CLK] = NULL;
+ gcc_sdm845_clocks[GCC_UFS_CARD_PHY_AUX_CLK_SRC] = NULL;
+ gcc_sdm845_clocks[GCC_UFS_CARD_RX_SYMBOL_0_CLK] = NULL;
+ gcc_sdm845_clocks[GCC_UFS_CARD_RX_SYMBOL_1_CLK] = NULL;
+ gcc_sdm845_clocks[GCC_UFS_CARD_TX_SYMBOL_0_CLK] = NULL;
+ gcc_sdm845_clocks[GCC_UFS_CARD_UNIPRO_CORE_CLK] = NULL;
+ gcc_sdm845_clocks[GCC_UFS_CARD_UNIPRO_CORE_HW_CTL_CLK] = NULL;
+ gcc_sdm845_clocks[GCC_UFS_CARD_UNIPRO_CORE_CLK_SRC] = NULL;
+ gcc_sdm845_clocks[GCC_UFS_PHY_RX_SYMBOL_1_CLK] = NULL;
+ gcc_sdm845_clocks[GCC_USB30_SEC_MASTER_CLK] = NULL;
+ gcc_sdm845_clocks[GCC_USB30_SEC_MASTER_CLK_SRC] = NULL;
+ gcc_sdm845_clocks[GCC_USB30_SEC_MOCK_UTMI_CLK] = NULL;
+ gcc_sdm845_clocks[GCC_USB30_SEC_MOCK_UTMI_CLK_SRC] = NULL;
+ gcc_sdm845_clocks[GCC_USB30_SEC_SLEEP_CLK] = NULL;
+ gcc_sdm845_clocks[GCC_USB3_SEC_CLKREF_CLK] = NULL;
+ gcc_sdm845_clocks[GCC_USB3_SEC_PHY_AUX_CLK] = NULL;
+ gcc_sdm845_clocks[GCC_USB3_SEC_PHY_AUX_CLK_SRC] = NULL;
+ gcc_sdm845_clocks[GCC_USB3_SEC_PHY_COM_AUX_CLK] = NULL;
+ gcc_sdm845_clocks[GCC_USB3_SEC_PHY_PIPE_CLK] = NULL;
+
+ gcc_cpuss_rbcpr_clk_src.freq_tbl = ftbl_gcc_cpuss_rbcpr_clk_src_sdm670;
+ gcc_cpuss_rbcpr_clk_src.clkr.hw.init->rate_max[VDD_CX_NOMINAL] =
+ 50000000;
+ gcc_sdcc2_apps_clk_src.clkr.hw.init->rate_max[VDD_CX_LOWER] =
+ 50000000;
+ gcc_sdcc2_apps_clk_src.clkr.hw.init->rate_max[VDD_CX_LOW_L1] =
+ 100000000;
+ gcc_sdcc2_apps_clk_src.clkr.hw.init->rate_max[VDD_CX_NOMINAL] =
+ 201500000;
+ gcc_sdcc4_apps_clk_src.freq_tbl = ftbl_gcc_sdcc4_apps_clk_src_sdm670;
+ gcc_sdcc4_apps_clk_src.clkr.hw.init->rate_max[VDD_CX_LOWER] =
+ 33333333;
+}
+
static int gcc_sdm845_fixup(struct platform_device *pdev)
{
const char *compat = NULL;
@@ -4020,6 +4273,8 @@
if (!strcmp(compat, "qcom,gcc-sdm845-v2"))
gcc_sdm845_fixup_sdm845v2();
+ else if (!strcmp(compat, "qcom,gcc-sdm670"))
+ gcc_sdm845_fixup_sdm670();
return 0;
}
diff --git a/drivers/clk/qcom/gpucc-sdm845.c b/drivers/clk/qcom/gpucc-sdm845.c
index cf4a8a5..db0dad1 100644
--- a/drivers/clk/qcom/gpucc-sdm845.c
+++ b/drivers/clk/qcom/gpucc-sdm845.c
@@ -224,6 +224,12 @@
{ }
};
+static const struct freq_tbl ftbl_gpu_cc_gmu_clk_src_sdm670[] = {
+ F(19200000, P_BI_TCXO, 1, 0, 0),
+ F(200000000, P_GPLL0_OUT_MAIN_DIV, 1.5, 0, 0),
+ { }
+};
+
static struct clk_rcg2 gpu_cc_gmu_clk_src = {
.cmd_rcgr = 0x1120,
.mnd_width = 0,
@@ -279,6 +285,18 @@
{ }
};
+static const struct freq_tbl ftbl_gpu_cc_gx_gfx3d_clk_src_sdm670[] = {
+ F(180000000, P_CRC_DIV, 1, 0, 0),
+ F(267000000, P_CRC_DIV, 1, 0, 0),
+ F(355000000, P_CRC_DIV, 1, 0, 0),
+ F(430000000, P_CRC_DIV, 1, 0, 0),
+ F(565000000, P_CRC_DIV, 1, 0, 0),
+ F(650000000, P_CRC_DIV, 1, 0, 0),
+ F(750000000, P_CRC_DIV, 1, 0, 0),
+ F(780000000, P_CRC_DIV, 1, 0, 0),
+ { }
+};
+
static struct clk_rcg2 gpu_cc_gx_gfx3d_clk_src = {
.cmd_rcgr = 0x101c,
.mnd_width = 0,
@@ -585,6 +603,7 @@
static const struct of_device_id gpu_cc_sdm845_match_table[] = {
{ .compatible = "qcom,gpucc-sdm845" },
{ .compatible = "qcom,gpucc-sdm845-v2" },
+ { .compatible = "qcom,gpucc-sdm670" },
{ }
};
MODULE_DEVICE_TABLE(of, gpu_cc_sdm845_match_table);
@@ -592,6 +611,7 @@
static const struct of_device_id gpu_cc_gfx_sdm845_match_table[] = {
{ .compatible = "qcom,gfxcc-sdm845" },
{ .compatible = "qcom,gfxcc-sdm845-v2" },
+ { .compatible = "qcom,gfxcc-sdm670" },
{},
};
MODULE_DEVICE_TABLE(of, gpu_cc_gfx_sdm845_match_table);
@@ -605,6 +625,15 @@
gpu_cc_gmu_clk_src.clkr.hw.init->rate_max[VDD_CX_LOW] = 500000000;
}
+static void gpu_cc_sdm845_fixup_sdm670(struct regmap *regmap)
+{
+ gpu_cc_sdm845_clocks[GPU_CC_PLL1] = &gpu_cc_pll1.clkr;
+ clk_fabia_pll_configure(&gpu_cc_pll1, regmap, &gpu_cc_pll1_config);
+
+ gpu_cc_gmu_clk_src.freq_tbl = ftbl_gpu_cc_gmu_clk_src_sdm670;
+ gpu_cc_gmu_clk_src.clkr.hw.init->rate_max[VDD_CX_LOW] = 0;
+}
+
static void gpu_cc_gfx_sdm845_fixup_sdm845v2(void)
{
gpu_cc_gx_gfx3d_clk_src.freq_tbl =
@@ -624,6 +653,28 @@
710000000;
}
+static void gpu_cc_gfx_sdm845_fixup_sdm670(void)
+{
+ gpu_cc_gx_gfx3d_clk_src.freq_tbl =
+ ftbl_gpu_cc_gx_gfx3d_clk_src_sdm670;
+ gpu_cc_gx_gfx3d_clk_src.clkr.hw.init->rate_max[VDD_GX_MIN] =
+ 180000000;
+ gpu_cc_gx_gfx3d_clk_src.clkr.hw.init->rate_max[VDD_GX_LOWER] =
+ 267000000;
+ gpu_cc_gx_gfx3d_clk_src.clkr.hw.init->rate_max[VDD_GX_LOW] =
+ 355000000;
+ gpu_cc_gx_gfx3d_clk_src.clkr.hw.init->rate_max[VDD_GX_LOW_L1] =
+ 430000000;
+ gpu_cc_gx_gfx3d_clk_src.clkr.hw.init->rate_max[VDD_GX_NOMINAL] =
+ 565000000;
+ gpu_cc_gx_gfx3d_clk_src.clkr.hw.init->rate_max[VDD_GX_NOMINAL_L1] =
+ 650000000;
+ gpu_cc_gx_gfx3d_clk_src.clkr.hw.init->rate_max[VDD_GX_HIGH] =
+ 750000000;
+ gpu_cc_gx_gfx3d_clk_src.clkr.hw.init->rate_max[VDD_GX_HIGH_L1] =
+ 780000000;
+}
+
static int gpu_cc_gfx_sdm845_fixup(struct platform_device *pdev)
{
const char *compat = NULL;
@@ -635,6 +686,8 @@
if (!strcmp(compat, "qcom,gfxcc-sdm845-v2"))
gpu_cc_gfx_sdm845_fixup_sdm845v2();
+ else if (!strcmp(compat, "qcom,gfxcc-sdm670"))
+ gpu_cc_gfx_sdm845_fixup_sdm670();
return 0;
}
@@ -651,6 +704,8 @@
if (!strcmp(compat, "qcom,gpucc-sdm845-v2"))
gpu_cc_sdm845_fixup_sdm845v2(regmap);
+ else if (!strcmp(compat, "qcom,gpucc-sdm670"))
+ gpu_cc_sdm845_fixup_sdm670(regmap);
return 0;
}
diff --git a/drivers/clk/qcom/videocc-sdm845.c b/drivers/clk/qcom/videocc-sdm845.c
index ba4e591..3311e9f 100644
--- a/drivers/clk/qcom/videocc-sdm845.c
+++ b/drivers/clk/qcom/videocc-sdm845.c
@@ -328,6 +328,7 @@
static const struct of_device_id video_cc_sdm845_match_table[] = {
{ .compatible = "qcom,video_cc-sdm845" },
{ .compatible = "qcom,video_cc-sdm845-v2" },
+ { .compatible = "qcom,video_cc-sdm670" },
{ }
};
MODULE_DEVICE_TABLE(of, video_cc_sdm845_match_table);
@@ -340,6 +341,12 @@
404000000;
}
+static void video_cc_sdm845_fixup_sdm670(void)
+{
+ video_cc_sdm845_fixup_sdm845v2();
+
+}
+
static int video_cc_sdm845_fixup(struct platform_device *pdev)
{
const char *compat = NULL;
@@ -351,6 +358,8 @@
if (!strcmp(compat, "qcom,video_cc-sdm845-v2"))
video_cc_sdm845_fixup_sdm845v2();
+ else if (!strcmp(compat, "qcom,video_cc-sdm670"))
+ video_cc_sdm845_fixup_sdm670();
return 0;
}
diff --git a/drivers/cpufreq/cpufreq_interactive.c b/drivers/cpufreq/cpufreq_interactive.c
index a6edf2f..c59e980 100644
--- a/drivers/cpufreq/cpufreq_interactive.c
+++ b/drivers/cpufreq/cpufreq_interactive.c
@@ -455,15 +455,11 @@
struct cpufreq_interactive_cpuinfo *pcpu = &per_cpu(cpuinfo, cpu);
struct cpufreq_interactive_tunables *tunables =
ppol->policy->governor_data;
- u64 now;
- u64 now_idle;
- unsigned int delta_idle;
- unsigned int delta_time;
- u64 active_time;
+ u64 now_idle, now, active_time, delta_idle, delta_time;
now_idle = get_cpu_idle_time(cpu, &now, tunables->io_is_busy);
- delta_idle = (unsigned int)(now_idle - pcpu->time_in_idle);
- delta_time = (unsigned int)(now - pcpu->time_in_idle_timestamp);
+ delta_idle = (now_idle - pcpu->time_in_idle);
+ delta_time = (now - pcpu->time_in_idle_timestamp);
if (delta_time <= delta_idle)
active_time = 0;
diff --git a/drivers/cpuidle/lpm-levels.c b/drivers/cpuidle/lpm-levels.c
index 4e3ca4f..5d2e918 100644
--- a/drivers/cpuidle/lpm-levels.c
+++ b/drivers/cpuidle/lpm-levels.c
@@ -1346,6 +1346,8 @@
struct power_params *pwr_params;
pwr_params = &cpu->levels[idx].pwr;
+ sched_set_cpu_cstate(dev->cpu, idx + 1,
+ pwr_params->energy_overhead, pwr_params->latency_us);
cpu_prepare(cpu, idx, true);
cluster_prepare(cpu->parent, cpumask, idx, true, start_time);
diff --git a/drivers/dax/dax.c b/drivers/dax/dax.c
index 586f954..40be374 100644
--- a/drivers/dax/dax.c
+++ b/drivers/dax/dax.c
@@ -546,7 +546,8 @@
struct dax_dev *dax_dev = to_dax_dev(dev);
struct dax_region *dax_region = dax_dev->region;
- ida_simple_remove(&dax_region->ida, dax_dev->id);
+ if (dax_dev->id >= 0)
+ ida_simple_remove(&dax_region->ida, dax_dev->id);
ida_simple_remove(&dax_minor_ida, MINOR(dev->devt));
dax_region_put(dax_region);
iput(dax_dev->inode);
@@ -581,7 +582,7 @@
}
struct dax_dev *devm_create_dax_dev(struct dax_region *dax_region,
- struct resource *res, int count)
+ int id, struct resource *res, int count)
{
struct device *parent = dax_region->dev;
struct dax_dev *dax_dev;
@@ -608,10 +609,16 @@
if (i < count)
goto err_id;
- dax_dev->id = ida_simple_get(&dax_region->ida, 0, 0, GFP_KERNEL);
- if (dax_dev->id < 0) {
- rc = dax_dev->id;
- goto err_id;
+ if (id < 0) {
+ id = ida_simple_get(&dax_region->ida, 0, 0, GFP_KERNEL);
+ dax_dev->id = id;
+ if (id < 0) {
+ rc = id;
+ goto err_id;
+ }
+ } else {
+ /* region provider owns @id lifetime */
+ dax_dev->id = -1;
}
minor = ida_simple_get(&dax_minor_ida, 0, 0, GFP_KERNEL);
@@ -650,7 +657,7 @@
dev->parent = parent;
dev->groups = dax_attribute_groups;
dev->release = dax_dev_release;
- dev_set_name(dev, "dax%d.%d", dax_region->id, dax_dev->id);
+ dev_set_name(dev, "dax%d.%d", dax_region->id, id);
rc = device_add(dev);
if (rc) {
kill_dax_dev(dax_dev);
@@ -669,7 +676,8 @@
err_inode:
ida_simple_remove(&dax_minor_ida, minor);
err_minor:
- ida_simple_remove(&dax_region->ida, dax_dev->id);
+ if (dax_dev->id >= 0)
+ ida_simple_remove(&dax_region->ida, dax_dev->id);
err_id:
kfree(dax_dev);
diff --git a/drivers/dax/dax.h b/drivers/dax/dax.h
index ddd829a..b5ed850 100644
--- a/drivers/dax/dax.h
+++ b/drivers/dax/dax.h
@@ -21,5 +21,5 @@
int region_id, struct resource *res, unsigned int align,
void *addr, unsigned long flags);
struct dax_dev *devm_create_dax_dev(struct dax_region *dax_region,
- struct resource *res, int count);
+ int id, struct resource *res, int count);
#endif /* __DAX_H__ */
diff --git a/drivers/dax/pmem.c b/drivers/dax/pmem.c
index 73c6ce9..eebb357 100644
--- a/drivers/dax/pmem.c
+++ b/drivers/dax/pmem.c
@@ -58,13 +58,12 @@
static int dax_pmem_probe(struct device *dev)
{
- int rc;
void *addr;
struct resource res;
struct dax_dev *dax_dev;
+ int rc, id, region_id;
struct nd_pfn_sb *pfn_sb;
struct dax_pmem *dax_pmem;
- struct nd_region *nd_region;
struct nd_namespace_io *nsio;
struct dax_region *dax_region;
struct nd_namespace_common *ndns;
@@ -122,14 +121,17 @@
/* adjust the dax_region resource to the start of data */
res.start += le64_to_cpu(pfn_sb->dataoff);
- nd_region = to_nd_region(dev->parent);
- dax_region = alloc_dax_region(dev, nd_region->id, &res,
+ rc = sscanf(dev_name(&ndns->dev), "namespace%d.%d", ®ion_id, &id);
+ if (rc != 2)
+ return -EINVAL;
+
+ dax_region = alloc_dax_region(dev, region_id, &res,
le32_to_cpu(pfn_sb->align), addr, PFN_DEV|PFN_MAP);
if (!dax_region)
return -ENOMEM;
/* TODO: support for subdividing a dax region... */
- dax_dev = devm_create_dax_dev(dax_region, &res, 1);
+ dax_dev = devm_create_dax_dev(dax_region, id, &res, 1);
/* child dax_dev instances now own the lifetime of the dax_region */
dax_region_put(dax_region);
diff --git a/drivers/devfreq/governor_memlat.c b/drivers/devfreq/governor_memlat.c
index e1afa60..81d98d1 100644
--- a/drivers/devfreq/governor_memlat.c
+++ b/drivers/devfreq/governor_memlat.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2015-2016, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2015-2017, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -244,7 +244,11 @@
hw->core_stats[i].mem_count,
hw->core_stats[i].freq, ratio);
- if (ratio && ratio <= node->ratio_ceil
+ if (!hw->core_stats[i].inst_count
+ || !hw->core_stats[i].freq)
+ continue;
+
+ if (ratio <= node->ratio_ceil
&& hw->core_stats[i].freq > max_freq) {
lat_dev = i;
max_freq = hw->core_stats[i].freq;
diff --git a/drivers/dma/ioat/hw.h b/drivers/dma/ioat/hw.h
index 8e67895..abcc51b 100644
--- a/drivers/dma/ioat/hw.h
+++ b/drivers/dma/ioat/hw.h
@@ -64,6 +64,8 @@
#define PCI_DEVICE_ID_INTEL_IOAT_BDX8 0x6f2e
#define PCI_DEVICE_ID_INTEL_IOAT_BDX9 0x6f2f
+#define PCI_DEVICE_ID_INTEL_IOAT_SKX 0x2021
+
#define IOAT_VER_1_2 0x12 /* Version 1.2 */
#define IOAT_VER_2_0 0x20 /* Version 2.0 */
#define IOAT_VER_3_0 0x30 /* Version 3.0 */
diff --git a/drivers/dma/ioat/init.c b/drivers/dma/ioat/init.c
index d235fbe..0dea6d55 100644
--- a/drivers/dma/ioat/init.c
+++ b/drivers/dma/ioat/init.c
@@ -106,6 +106,8 @@
{ PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_IOAT_BDX8) },
{ PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_IOAT_BDX9) },
+ { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_IOAT_SKX) },
+
/* I/OAT v3.3 platforms */
{ PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_IOAT_BWD0) },
{ PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_IOAT_BWD1) },
@@ -243,10 +245,15 @@
}
}
+static inline bool is_skx_ioat(struct pci_dev *pdev)
+{
+ return (pdev->device == PCI_DEVICE_ID_INTEL_IOAT_SKX) ? true : false;
+}
+
static bool is_xeon_cb32(struct pci_dev *pdev)
{
return is_jf_ioat(pdev) || is_snb_ioat(pdev) || is_ivb_ioat(pdev) ||
- is_hsw_ioat(pdev) || is_bdx_ioat(pdev);
+ is_hsw_ioat(pdev) || is_bdx_ioat(pdev) || is_skx_ioat(pdev);
}
bool is_bwd_ioat(struct pci_dev *pdev)
@@ -1350,6 +1357,8 @@
device->version = readb(device->reg_base + IOAT_VER_OFFSET);
if (device->version >= IOAT_VER_3_0) {
+ if (is_skx_ioat(pdev))
+ device->version = IOAT_VER_3_2;
err = ioat3_dma_probe(device, ioat_dca_enabled);
if (device->version >= IOAT_VER_3_3)
diff --git a/drivers/dma/ti-dma-crossbar.c b/drivers/dma/ti-dma-crossbar.c
index 3f24aeb..2403475 100644
--- a/drivers/dma/ti-dma-crossbar.c
+++ b/drivers/dma/ti-dma-crossbar.c
@@ -149,6 +149,7 @@
match = of_match_node(ti_am335x_master_match, dma_node);
if (!match) {
dev_err(&pdev->dev, "DMA master is not supported\n");
+ of_node_put(dma_node);
return -EINVAL;
}
@@ -339,6 +340,7 @@
match = of_match_node(ti_dra7_master_match, dma_node);
if (!match) {
dev_err(&pdev->dev, "DMA master is not supported\n");
+ of_node_put(dma_node);
return -EINVAL;
}
diff --git a/drivers/gpu/drm/msm/adreno/adreno_gpu.c b/drivers/gpu/drm/msm/adreno/adreno_gpu.c
index 961d47f..8214127 100644
--- a/drivers/gpu/drm/msm/adreno/adreno_gpu.c
+++ b/drivers/gpu/drm/msm/adreno/adreno_gpu.c
@@ -210,7 +210,14 @@
void adreno_flush(struct msm_gpu *gpu)
{
struct adreno_gpu *adreno_gpu = to_adreno_gpu(gpu);
- uint32_t wptr = get_wptr(gpu->rb);
+ uint32_t wptr;
+
+ /*
+ * Mask wptr value that we calculate to fit in the HW range. This is
+ * to account for the possibility that the last command fit exactly into
+ * the ringbuffer and rb->next hasn't wrapped to zero yet
+ */
+ wptr = get_wptr(gpu->rb) & ((gpu->rb->size / 4) - 1);
/* ensure writes to ringbuffer have hit system memory: */
mb();
diff --git a/drivers/gpu/drm/msm/msm_gem_submit.c b/drivers/gpu/drm/msm/msm_gem_submit.c
index c695dda..cc75fb5 100644
--- a/drivers/gpu/drm/msm/msm_gem_submit.c
+++ b/drivers/gpu/drm/msm/msm_gem_submit.c
@@ -109,7 +109,8 @@
pagefault_disable();
}
- if (submit_bo.flags & ~MSM_SUBMIT_BO_FLAGS) {
+ if ((submit_bo.flags & ~MSM_SUBMIT_BO_FLAGS) ||
+ !(submit_bo.flags & MSM_SUBMIT_BO_FLAGS)) {
DRM_ERROR("invalid flags: %x\n", submit_bo.flags);
ret = -EINVAL;
goto out_unlock;
@@ -293,7 +294,7 @@
{
uint32_t i, last_offset = 0;
uint32_t *ptr;
- int ret;
+ int ret = 0;
if (offset % 4) {
DRM_ERROR("non-aligned cmdstream buffer: %u\n", offset);
@@ -320,12 +321,13 @@
ret = copy_from_user(&submit_reloc, userptr, sizeof(submit_reloc));
if (ret)
- return -EFAULT;
+ goto out;
if (submit_reloc.submit_offset % 4) {
DRM_ERROR("non-aligned reloc offset: %u\n",
submit_reloc.submit_offset);
- return -EINVAL;
+ ret = -EINVAL;
+ goto out;
}
/* offset in dwords: */
@@ -334,12 +336,13 @@
if ((off >= (obj->base.size / 4)) ||
(off < last_offset)) {
DRM_ERROR("invalid offset %u at reloc %u\n", off, i);
- return -EINVAL;
+ ret = -EINVAL;
+ goto out;
}
ret = submit_bo(submit, submit_reloc.reloc_idx, NULL, &iova, &valid);
if (ret)
- return ret;
+ goto out;
if (valid)
continue;
@@ -356,9 +359,10 @@
last_offset = off;
}
+out:
msm_gem_put_vaddr_locked(&obj->base);
- return 0;
+ return ret;
}
static void submit_cleanup(struct msm_gem_submit *submit)
diff --git a/drivers/gpu/drm/msm/msm_ringbuffer.c b/drivers/gpu/drm/msm/msm_ringbuffer.c
index f326cf6..67b34e0 100644
--- a/drivers/gpu/drm/msm/msm_ringbuffer.c
+++ b/drivers/gpu/drm/msm/msm_ringbuffer.c
@@ -23,7 +23,8 @@
struct msm_ringbuffer *ring;
int ret;
- size = ALIGN(size, 4); /* size should be dword aligned */
+ if (WARN_ON(!is_power_of_2(size)))
+ return ERR_PTR(-EINVAL);
ring = kzalloc(sizeof(*ring), GFP_KERNEL);
if (!ring) {
diff --git a/drivers/gpu/drm/msm/sde/sde_encoder.c b/drivers/gpu/drm/msm/sde/sde_encoder.c
index 0071352..8a46d66 100644
--- a/drivers/gpu/drm/msm/sde/sde_encoder.c
+++ b/drivers/gpu/drm/msm/sde/sde_encoder.c
@@ -2520,7 +2520,8 @@
phys->split_role == ENC_ROLE_SLAVE) &&
phys->split_role != ENC_ROLE_SKIP)
set_bit(i, sde_enc->frame_busy_mask);
-
+ if (phys->hw_ctl->ops.reg_dma_flush)
+ phys->hw_ctl->ops.reg_dma_flush(phys->hw_ctl);
if (!phys->ops.needs_single_flush ||
!phys->ops.needs_single_flush(phys))
_sde_encoder_trigger_flush(&sde_enc->base, phys, 0x0);
diff --git a/drivers/gpu/drm/msm/sde/sde_hw_catalog.c b/drivers/gpu/drm/msm/sde/sde_hw_catalog.c
index 0e1ab51..ccc4443 100644
--- a/drivers/gpu/drm/msm/sde/sde_hw_catalog.c
+++ b/drivers/gpu/drm/msm/sde/sde_hw_catalog.c
@@ -2944,33 +2944,25 @@
rc = sde_hardware_format_caps(sde_cfg, hw_rev);
- switch (hw_rev) {
- case SDE_HW_VER_170:
- case SDE_HW_VER_171:
- case SDE_HW_VER_172:
+ if (IS_MSM8996_TARGET(hw_rev)) {
/* update msm8996 target here */
sde_cfg->perf.min_prefill_lines = 21;
- break;
- case SDE_HW_VER_300:
- case SDE_HW_VER_301:
+ } else if (IS_MSM8998_TARGET(hw_rev)) {
/* update msm8998 target here */
sde_cfg->has_wb_ubwc = true;
sde_cfg->perf.min_prefill_lines = 25;
sde_cfg->vbif_qos_nlvl = 4;
sde_cfg->ts_prefill_rev = 1;
- sde_cfg->perf.min_prefill_lines = 25;
- break;
- case SDE_HW_VER_400:
+ } else if (IS_SDM845_TARGET(hw_rev)) {
/* update sdm845 target here */
sde_cfg->has_wb_ubwc = true;
sde_cfg->perf.min_prefill_lines = 24;
sde_cfg->vbif_qos_nlvl = 8;
sde_cfg->ts_prefill_rev = 2;
- sde_cfg->perf.min_prefill_lines = 24;
- break;
- default:
+ } else {
+ SDE_ERROR("unsupported chipset id:%X\n", hw_rev);
sde_cfg->perf.min_prefill_lines = 0xffff;
- break;
+ rc = -ENODEV;
}
return rc;
diff --git a/drivers/gpu/drm/msm/sde/sde_hw_catalog.h b/drivers/gpu/drm/msm/sde/sde_hw_catalog.h
index fa10a88..48c3db7 100644
--- a/drivers/gpu/drm/msm/sde/sde_hw_catalog.h
+++ b/drivers/gpu/drm/msm/sde/sde_hw_catalog.h
@@ -45,7 +45,10 @@
#define SDE_HW_VER_300 SDE_HW_VER(3, 0, 0) /* 8998 v1.0 */
#define SDE_HW_VER_301 SDE_HW_VER(3, 0, 1) /* 8998 v1.1 */
#define SDE_HW_VER_400 SDE_HW_VER(4, 0, 0) /* sdm845 v1.0 */
+#define SDE_HW_VER_401 SDE_HW_VER(4, 0, 1) /* sdm845 v2.0 */
+#define IS_MSM8996_TARGET(rev) IS_SDE_MAJOR_MINOR_SAME((rev), SDE_HW_VER_170)
+#define IS_MSM8998_TARGET(rev) IS_SDE_MAJOR_MINOR_SAME((rev), SDE_HW_VER_300)
#define IS_SDM845_TARGET(rev) IS_SDE_MAJOR_MINOR_SAME((rev), SDE_HW_VER_400)
#define SDE_HW_BLK_NAME_LEN 16
diff --git a/drivers/gpu/drm/msm/sde/sde_hw_ctl.c b/drivers/gpu/drm/msm/sde/sde_hw_ctl.c
index 606fdeb..621a172 100644
--- a/drivers/gpu/drm/msm/sde/sde_hw_ctl.c
+++ b/drivers/gpu/drm/msm/sde/sde_hw_ctl.c
@@ -39,7 +39,7 @@
#define CTL_FLUSH_MASK_ROT BIT(27)
#define CTL_FLUSH_MASK_CTL BIT(17)
-#define SDE_REG_RESET_TIMEOUT_COUNT 20
+#define SDE_REG_RESET_TIMEOUT_US 2000
static struct sde_ctl_cfg *_ctl_offset(enum sde_ctl ctl,
struct sde_mdss_cfg *m,
@@ -113,10 +113,6 @@
static inline void sde_hw_ctl_trigger_flush(struct sde_hw_ctl *ctx)
{
- struct sde_hw_reg_dma_ops *ops = sde_reg_dma_get_ops();
-
- if (ops && ops->last_command)
- ops->last_command(ctx, DMA_CTL_QUEUE0);
SDE_REG_WRITE(&ctx->hw, CTL_FLUSH, ctx->pending_flush_mask);
}
@@ -302,14 +298,13 @@
return 0;
}
-static u32 sde_hw_ctl_poll_reset_status(struct sde_hw_ctl *ctx, u32 count)
+static u32 sde_hw_ctl_poll_reset_status(struct sde_hw_ctl *ctx, u32 timeout_us)
{
struct sde_hw_blk_reg_map *c = &ctx->hw;
+ ktime_t timeout;
u32 status;
- /* protect to do at least one iteration */
- if (!count)
- count = 1;
+ timeout = ktime_add_us(ktime_get(), timeout_us);
/*
* it takes around 30us to have mdp finish resetting its ctl path
@@ -317,10 +312,10 @@
*/
do {
status = SDE_REG_READ(c, CTL_SW_RESET);
- status &= 0x01;
+ status &= 0x1;
if (status)
usleep_range(20, 50);
- } while (status && --count > 0);
+ } while (status && ktime_compare_safe(ktime_get(), timeout) < 0);
return status;
}
@@ -331,7 +326,7 @@
pr_debug("issuing hw ctl reset for ctl:%d\n", ctx->idx);
SDE_REG_WRITE(c, CTL_SW_RESET, 0x1);
- if (sde_hw_ctl_poll_reset_status(ctx, SDE_REG_RESET_TIMEOUT_COUNT))
+ if (sde_hw_ctl_poll_reset_status(ctx, SDE_REG_RESET_TIMEOUT_US))
return -EINVAL;
return 0;
@@ -348,7 +343,7 @@
return 0;
pr_debug("hw ctl reset is set for ctl:%d\n", ctx->idx);
- if (sde_hw_ctl_poll_reset_status(ctx, SDE_REG_RESET_TIMEOUT_COUNT)) {
+ if (sde_hw_ctl_poll_reset_status(ctx, SDE_REG_RESET_TIMEOUT_US)) {
pr_err("hw recovery is not complete for ctl:%d\n", ctx->idx);
return -EINVAL;
}
@@ -547,6 +542,14 @@
SDE_REG_WRITE(c, CTL_ROT_TOP, val);
}
+static void sde_hw_reg_dma_flush(struct sde_hw_ctl *ctx)
+{
+ struct sde_hw_reg_dma_ops *ops = sde_reg_dma_get_ops();
+
+ if (ops && ops->last_command)
+ ops->last_command(ctx, DMA_CTL_QUEUE0);
+}
+
static void _setup_ctl_ops(struct sde_hw_ctl_ops *ops,
unsigned long cap)
{
@@ -568,6 +571,8 @@
ops->get_bitmask_intf = sde_hw_ctl_get_bitmask_intf;
ops->get_bitmask_cdm = sde_hw_ctl_get_bitmask_cdm;
ops->get_bitmask_wb = sde_hw_ctl_get_bitmask_wb;
+ ops->reg_dma_flush = sde_hw_reg_dma_flush;
+
if (cap & BIT(SDE_CTL_SBUF)) {
ops->get_bitmask_rot = sde_hw_ctl_get_bitmask_rot;
ops->setup_sbuf_cfg = sde_hw_ctl_setup_sbuf_cfg;
diff --git a/drivers/gpu/drm/msm/sde/sde_hw_ctl.h b/drivers/gpu/drm/msm/sde/sde_hw_ctl.h
index a111916..5d3ced3 100644
--- a/drivers/gpu/drm/msm/sde/sde_hw_ctl.h
+++ b/drivers/gpu/drm/msm/sde/sde_hw_ctl.h
@@ -206,6 +206,13 @@
void (*setup_sbuf_cfg)(struct sde_hw_ctl *ctx,
struct sde_ctl_sbuf_cfg *cfg);
+
+ /**
+ * Flush the reg dma by sending last command.
+ * @ctx : ctl path ctx pointer
+ */
+ void (*reg_dma_flush)(struct sde_hw_ctl *ctx);
+
};
/**
diff --git a/drivers/gpu/drm/msm/sde/sde_hw_reg_dma_v1.c b/drivers/gpu/drm/msm/sde/sde_hw_reg_dma_v1.c
index 9199048..4487d78 100644
--- a/drivers/gpu/drm/msm/sde/sde_hw_reg_dma_v1.c
+++ b/drivers/gpu/drm/msm/sde/sde_hw_reg_dma_v1.c
@@ -50,7 +50,6 @@
(cfg)->dma_buf->index)
#define REG_DMA_DECODE_SEL 0x180AC060
-#define REG_DMA_LAST_CMD 0x180AC004
#define SINGLE_REG_WRITE_OPCODE (BIT(28))
#define REL_ADDR_OPCODE (BIT(27))
#define HW_INDEX_REG_WRITE_OPCODE (BIT(28) | BIT(29))
@@ -471,7 +470,8 @@
cfg->dma_buf->iova);
SDE_REG_WRITE(&hw, reg_dma_ctl_queue_off[cfg->ctl->idx] + 0x4,
cmd1);
- SDE_REG_WRITE(&cfg->ctl->hw, REG_DMA_CTL_TRIGGER_OFF,
+ if (cfg->last_command)
+ SDE_REG_WRITE(&cfg->ctl->hw, REG_DMA_CTL_TRIGGER_OFF,
queue_sel[cfg->queue_select]);
return 0;
@@ -754,8 +754,8 @@
loc = (u32 *)((u8 *)cfg->dma_buf->vaddr +
cfg->dma_buf->index);
- loc[0] = REG_DMA_LAST_CMD;
- loc[1] = BIT(0);
+ loc[0] = REG_DMA_DECODE_SEL;
+ loc[1] = 0;
cfg->dma_buf->index = sizeof(u32) * 2;
cfg->dma_buf->ops_completed = REG_WRITE_OP | DECODE_SEL_OP;
cfg->dma_buf->next_op_allowed = REG_WRITE_OP;
diff --git a/drivers/gpu/drm/msm/sde/sde_kms.c b/drivers/gpu/drm/msm/sde/sde_kms.c
index 3dbfdfc..1a6585a 100644
--- a/drivers/gpu/drm/msm/sde/sde_kms.c
+++ b/drivers/gpu/drm/msm/sde/sde_kms.c
@@ -2047,7 +2047,7 @@
} else {
sde_kms->reg_dma_len = msm_iomap_size(dev->platformdev,
"regdma_phys");
- rc = sde_dbg_reg_register_base("vbif_nrt",
+ rc = sde_dbg_reg_register_base("reg_dma",
sde_kms->reg_dma,
sde_kms->reg_dma_len);
if (rc)
diff --git a/drivers/gpu/drm/msm/sde/sde_reg_dma.c b/drivers/gpu/drm/msm/sde/sde_reg_dma.c
index cc87aeb..e38524f 100644
--- a/drivers/gpu/drm/msm/sde/sde_reg_dma.c
+++ b/drivers/gpu/drm/msm/sde/sde_reg_dma.c
@@ -12,6 +12,7 @@
#include "sde_reg_dma.h"
#include "sde_hw_reg_dma_v1.h"
+#include "sde_dbg.h"
static int default_check_support(enum sde_reg_dma_features feature,
enum sde_reg_dma_blk blk,
@@ -98,6 +99,9 @@
rc = init_v1(®_dma);
if (rc)
DRM_DEBUG("init v1 dma ops failed\n");
+ else
+ sde_dbg_reg_register_base("reg_dma", addr,
+ reg_dma.caps->len);
break;
default:
break;
diff --git a/drivers/gpu/drm/msm/sde_dbg.c b/drivers/gpu/drm/msm/sde_dbg.c
index 7e58c2f..768dfbd 100644
--- a/drivers/gpu/drm/msm/sde_dbg.c
+++ b/drivers/gpu/drm/msm/sde_dbg.c
@@ -3067,9 +3067,7 @@
memset(&dbg->dbgbus_sde, 0, sizeof(dbg->dbgbus_sde));
memset(&dbg->dbgbus_vbif_rt, 0, sizeof(dbg->dbgbus_vbif_rt));
- switch (hwversion) {
- case SDE_HW_VER_300:
- case SDE_HW_VER_301:
+ if (IS_MSM8998_TARGET(hwversion)) {
dbg->dbgbus_sde.entries = dbg_bus_sde_8998;
dbg->dbgbus_sde.cmn.entries_size = ARRAY_SIZE(dbg_bus_sde_8998);
dbg->dbgbus_sde.cmn.flags = DBGBUS_FLAGS_DSPP;
@@ -3077,9 +3075,7 @@
dbg->dbgbus_vbif_rt.entries = vbif_dbg_bus_msm8998;
dbg->dbgbus_vbif_rt.cmn.entries_size =
ARRAY_SIZE(vbif_dbg_bus_msm8998);
- break;
-
- case SDE_HW_VER_400:
+ } else if (IS_SDM845_TARGET(hwversion)) {
dbg->dbgbus_sde.entries = dbg_bus_sde_sdm845;
dbg->dbgbus_sde.cmn.entries_size =
ARRAY_SIZE(dbg_bus_sde_sdm845);
@@ -3089,10 +3085,8 @@
dbg->dbgbus_vbif_rt.entries = vbif_dbg_bus_msm8998;
dbg->dbgbus_vbif_rt.cmn.entries_size =
ARRAY_SIZE(vbif_dbg_bus_msm8998);
- break;
- default:
- pr_err("unsupported chipset id %u\n", hwversion);
- break;
+ } else {
+ pr_err("unsupported chipset id %X\n", hwversion);
}
}
diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/nv50.h b/drivers/gpu/drm/nouveau/nvkm/engine/disp/nv50.h
index 1e1de6b..5893be9 100644
--- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/nv50.h
+++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/nv50.h
@@ -27,7 +27,7 @@
u8 type[3];
} pior;
- struct nv50_disp_chan *chan[17];
+ struct nv50_disp_chan *chan[21];
};
int nv50_disp_root_scanoutpos(NV50_DISP_MTHD_V0);
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/bar/gf100.c b/drivers/gpu/drm/nouveau/nvkm/subdev/bar/gf100.c
index c794b2c..6d8f212 100644
--- a/drivers/gpu/drm/nouveau/nvkm/subdev/bar/gf100.c
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/bar/gf100.c
@@ -129,7 +129,7 @@
if (bar->bar[0].mem) {
addr = nvkm_memory_addr(bar->bar[0].mem) >> 12;
- nvkm_wr32(device, 0x001714, 0xc0000000 | addr);
+ nvkm_wr32(device, 0x001714, 0x80000000 | addr);
}
return 0;
diff --git a/drivers/gpu/drm/rcar-du/rcar_du_drv.c b/drivers/gpu/drm/rcar-du/rcar_du_drv.c
index 73c971e..ae125d0 100644
--- a/drivers/gpu/drm/rcar-du/rcar_du_drv.c
+++ b/drivers/gpu/drm/rcar-du/rcar_du_drv.c
@@ -285,7 +285,6 @@
drm_kms_helper_poll_fini(ddev);
drm_mode_config_cleanup(ddev);
- drm_vblank_cleanup(ddev);
drm_dev_unref(ddev);
@@ -305,7 +304,7 @@
return -ENODEV;
}
- /* Allocate and initialize the DRM and R-Car device structures. */
+ /* Allocate and initialize the R-Car device structure. */
rcdu = devm_kzalloc(&pdev->dev, sizeof(*rcdu), GFP_KERNEL);
if (rcdu == NULL)
return -ENOMEM;
@@ -315,6 +314,15 @@
rcdu->dev = &pdev->dev;
rcdu->info = of_match_device(rcar_du_of_table, rcdu->dev)->data;
+ platform_set_drvdata(pdev, rcdu);
+
+ /* I/O resources */
+ mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ rcdu->mmio = devm_ioremap_resource(&pdev->dev, mem);
+ if (IS_ERR(rcdu->mmio))
+ return PTR_ERR(rcdu->mmio);
+
+ /* DRM/KMS objects */
ddev = drm_dev_alloc(&rcar_du_driver, &pdev->dev);
if (IS_ERR(ddev))
return PTR_ERR(ddev);
@@ -322,24 +330,6 @@
rcdu->ddev = ddev;
ddev->dev_private = rcdu;
- platform_set_drvdata(pdev, rcdu);
-
- /* I/O resources */
- mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- rcdu->mmio = devm_ioremap_resource(&pdev->dev, mem);
- if (IS_ERR(rcdu->mmio)) {
- ret = PTR_ERR(rcdu->mmio);
- goto error;
- }
-
- /* Initialize vertical blanking interrupts handling. Start with vblank
- * disabled for all CRTCs.
- */
- ret = drm_vblank_init(ddev, (1 << rcdu->info->num_crtcs) - 1);
- if (ret < 0)
- goto error;
-
- /* DRM/KMS objects */
ret = rcar_du_modeset_init(rcdu);
if (ret < 0) {
if (ret != -EPROBE_DEFER)
diff --git a/drivers/gpu/drm/rcar-du/rcar_du_kms.c b/drivers/gpu/drm/rcar-du/rcar_du_kms.c
index 392c7e6..cfc302c 100644
--- a/drivers/gpu/drm/rcar-du/rcar_du_kms.c
+++ b/drivers/gpu/drm/rcar-du/rcar_du_kms.c
@@ -567,6 +567,13 @@
if (ret < 0)
return ret;
+ /* Initialize vertical blanking interrupts handling. Start with vblank
+ * disabled for all CRTCs.
+ */
+ ret = drm_vblank_init(dev, (1 << rcdu->info->num_crtcs) - 1);
+ if (ret < 0)
+ return ret;
+
/* Initialize the groups. */
num_groups = DIV_ROUND_UP(rcdu->num_crtcs, 2);
diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c b/drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c
index c7b53d9..fefb9d9 100644
--- a/drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c
+++ b/drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c
@@ -519,7 +519,7 @@
struct vmw_sw_context *sw_context,
SVGA3dCmdHeader *header)
{
- return capable(CAP_SYS_ADMIN) ? : -EINVAL;
+ return -EINVAL;
}
static int vmw_cmd_ok(struct vmw_private *dev_priv,
diff --git a/drivers/gpu/msm/adreno.c b/drivers/gpu/msm/adreno.c
index 6a0dbce..d16e42d 100644
--- a/drivers/gpu/msm/adreno.c
+++ b/drivers/gpu/msm/adreno.c
@@ -1333,8 +1333,7 @@
}
- if (nopreempt == false &&
- ADRENO_FEATURE(adreno_dev, ADRENO_PREEMPTION)) {
+ if (nopreempt == false) {
int r = 0;
if (gpudev->preemption_init)
diff --git a/drivers/gpu/msm/adreno.h b/drivers/gpu/msm/adreno.h
index 45ea99a..36bd656 100644
--- a/drivers/gpu/msm/adreno.h
+++ b/drivers/gpu/msm/adreno.h
@@ -528,6 +528,7 @@
ADRENO_DEVICE_ISDB_ENABLED = 12,
ADRENO_DEVICE_CACHE_FLUSH_TS_SUSPENDED = 13,
ADRENO_DEVICE_HARD_RESET = 14,
+ ADRENO_DEVICE_PREEMPTION_EXECUTION = 15,
};
/**
@@ -1550,11 +1551,23 @@
smp_wmb();
}
-static inline bool adreno_is_preemption_enabled(
+static inline bool adreno_is_preemption_execution_enabled(
+ struct adreno_device *adreno_dev)
+{
+ return test_bit(ADRENO_DEVICE_PREEMPTION_EXECUTION, &adreno_dev->priv);
+}
+
+static inline bool adreno_is_preemption_setup_enabled(
struct adreno_device *adreno_dev)
{
return test_bit(ADRENO_DEVICE_PREEMPTION, &adreno_dev->priv);
}
+
+static inline bool adreno_is_preemption_enabled(
+ struct adreno_device *adreno_dev)
+{
+ return 0;
+}
/**
* adreno_ctx_get_rb() - Return the ringbuffer that a context should
* use based on priority
@@ -1578,7 +1591,7 @@
* ringbuffer
*/
- if (!adreno_is_preemption_enabled(adreno_dev))
+ if (!adreno_is_preemption_execution_enabled(adreno_dev))
return &(adreno_dev->ringbuffers[0]);
/*
diff --git a/drivers/gpu/msm/adreno_a6xx.c b/drivers/gpu/msm/adreno_a6xx.c
index 5f070bc..12824b0 100644
--- a/drivers/gpu/msm/adreno_a6xx.c
+++ b/drivers/gpu/msm/adreno_a6xx.c
@@ -567,7 +567,7 @@
kgsl_regrmw(device, A6XX_PC_DBG_ECO_CNTL, 0, (1 << 8));
/* Enable the GMEM save/restore feature for preemption */
- if (adreno_is_preemption_enabled(adreno_dev))
+ if (adreno_is_preemption_setup_enabled(adreno_dev))
kgsl_regwrite(device, A6XX_RB_CONTEXT_SWITCH_GMEM_SAVE_RESTORE,
0x1);
@@ -773,7 +773,7 @@
struct adreno_ringbuffer *rb = adreno_dev->cur_rb;
struct kgsl_device *device = KGSL_DEVICE(adreno_dev);
- if (!adreno_is_preemption_enabled(adreno_dev))
+ if (!adreno_is_preemption_execution_enabled(adreno_dev))
return 0;
cmds = adreno_ringbuffer_allocspace(rb, 42);
@@ -2295,7 +2295,7 @@
{
struct kgsl_device *device = KGSL_DEVICE(adreno_dev);
- if (adreno_is_preemption_enabled(adreno_dev))
+ if (adreno_is_preemption_execution_enabled(adreno_dev))
a6xx_preemption_trigger(adreno_dev);
adreno_dispatcher_schedule(device);
diff --git a/drivers/gpu/msm/adreno_a6xx_preempt.c b/drivers/gpu/msm/adreno_a6xx_preempt.c
index a982436..1d5f4a5 100644
--- a/drivers/gpu/msm/adreno_a6xx_preempt.c
+++ b/drivers/gpu/msm/adreno_a6xx_preempt.c
@@ -360,7 +360,7 @@
{
struct kgsl_device *device = KGSL_DEVICE(adreno_dev);
- if (!adreno_is_preemption_enabled(adreno_dev))
+ if (!adreno_is_preemption_execution_enabled(adreno_dev))
return;
mutex_lock(&device->mutex);
@@ -453,7 +453,7 @@
struct adreno_ringbuffer *rb;
unsigned int i;
- if (!adreno_is_preemption_enabled(adreno_dev))
+ if (!adreno_is_preemption_execution_enabled(adreno_dev))
return;
/* Force the state to be clear */
@@ -631,7 +631,7 @@
struct kgsl_device *device = context->device;
struct adreno_device *adreno_dev = ADRENO_DEVICE(device);
- if (!adreno_is_preemption_enabled(adreno_dev))
+ if (!adreno_is_preemption_setup_enabled(adreno_dev))
return;
gpumem_free_entry(context->user_ctxt_record);
@@ -642,7 +642,7 @@
struct kgsl_device *device = context->device;
struct adreno_device *adreno_dev = ADRENO_DEVICE(device);
- if (!adreno_is_preemption_enabled(adreno_dev))
+ if (!adreno_is_preemption_setup_enabled(adreno_dev))
return 0;
context->user_ctxt_record = gpumem_alloc_entry(context->dev_priv,
diff --git a/drivers/gpu/msm/adreno_dispatch.c b/drivers/gpu/msm/adreno_dispatch.c
index 8fd8b88..25aeaef 100644
--- a/drivers/gpu/msm/adreno_dispatch.c
+++ b/drivers/gpu/msm/adreno_dispatch.c
@@ -2139,7 +2139,7 @@
* Deleting uninitialized timer will block for ever on kernel debug
* disable build. Hence skip del timer if it is not initialized.
*/
- if (adreno_is_preemption_enabled(adreno_dev))
+ if (adreno_is_preemption_execution_enabled(adreno_dev))
del_timer_sync(&adreno_dev->preempt.timer);
mutex_lock(&device->mutex);
diff --git a/drivers/gpu/msm/adreno_ioctl.c b/drivers/gpu/msm/adreno_ioctl.c
index 7c7bfa5..8b283ae 100644
--- a/drivers/gpu/msm/adreno_ioctl.c
+++ b/drivers/gpu/msm/adreno_ioctl.c
@@ -96,7 +96,7 @@
int levels_to_copy;
if (!adreno_is_a5xx(adreno_dev) ||
- !adreno_is_preemption_enabled(adreno_dev))
+ !adreno_is_preemption_execution_enabled(adreno_dev))
return -EOPNOTSUPP;
if (read->size_user < size_level)
diff --git a/drivers/gpu/msm/adreno_ringbuffer.c b/drivers/gpu/msm/adreno_ringbuffer.c
index a9d4413..8b87ee2 100644
--- a/drivers/gpu/msm/adreno_ringbuffer.c
+++ b/drivers/gpu/msm/adreno_ringbuffer.c
@@ -284,7 +284,7 @@
struct adreno_gpudev *gpudev = ADRENO_GPU_DEVICE(adreno_dev);
int i;
- if (nopreempt == false && ADRENO_FEATURE(adreno_dev, ADRENO_PREEMPTION))
+ if (nopreempt == false)
adreno_dev->num_ringbuffers = gpudev->num_prio_levels;
else
adreno_dev->num_ringbuffers = 1;
@@ -473,11 +473,11 @@
total_sizedwords += 4;
if (gpudev->preemption_pre_ibsubmit &&
- adreno_is_preemption_enabled(adreno_dev))
+ adreno_is_preemption_execution_enabled(adreno_dev))
total_sizedwords += 22;
if (gpudev->preemption_post_ibsubmit &&
- adreno_is_preemption_enabled(adreno_dev))
+ adreno_is_preemption_execution_enabled(adreno_dev))
total_sizedwords += 5;
/*
@@ -523,7 +523,7 @@
*ringcmds++ = cp_packet(adreno_dev, CP_NOP, 1);
*ringcmds++ = KGSL_CMD_IDENTIFIER;
- if (adreno_is_preemption_enabled(adreno_dev) &&
+ if (adreno_is_preemption_execution_enabled(adreno_dev) &&
gpudev->preemption_pre_ibsubmit)
ringcmds += gpudev->preemption_pre_ibsubmit(
adreno_dev, rb, ringcmds, context);
@@ -660,7 +660,7 @@
ringcmds += cp_secure_mode(adreno_dev, ringcmds, 0);
if (gpudev->preemption_post_ibsubmit &&
- adreno_is_preemption_enabled(adreno_dev))
+ adreno_is_preemption_execution_enabled(adreno_dev))
ringcmds += gpudev->preemption_post_ibsubmit(adreno_dev,
ringcmds);
@@ -864,9 +864,10 @@
dwords += 2;
}
- if (adreno_is_preemption_enabled(adreno_dev))
+ if (adreno_is_preemption_execution_enabled(adreno_dev)) {
if (gpudev->preemption_yield_enable)
dwords += 8;
+ }
if (gpudev->set_marker)
dwords += 4;
@@ -927,9 +928,10 @@
if (gpudev->set_marker)
cmds += gpudev->set_marker(cmds, 0);
- if (adreno_is_preemption_enabled(adreno_dev))
+ if (adreno_is_preemption_execution_enabled(adreno_dev)) {
if (gpudev->preemption_yield_enable)
cmds += gpudev->preemption_yield_enable(cmds);
+ }
if (kernel_profiling) {
cmds += _get_alwayson_counter(adreno_dev, cmds,
diff --git a/drivers/gpu/msm/adreno_snapshot.c b/drivers/gpu/msm/adreno_snapshot.c
index 0840aba..f608927 100644
--- a/drivers/gpu/msm/adreno_snapshot.c
+++ b/drivers/gpu/msm/adreno_snapshot.c
@@ -837,7 +837,8 @@
snapshot_frozen_objsize = 0;
- setup_fault_process(device, snapshot,
+ if (!IS_ERR(context))
+ setup_fault_process(device, snapshot,
context ? context->proc_priv : NULL);
/* Add GPU specific sections - registers mainly, but other stuff too */
diff --git a/drivers/gpu/msm/adreno_sysfs.c b/drivers/gpu/msm/adreno_sysfs.c
index 3977302..b06aa98 100644
--- a/drivers/gpu/msm/adreno_sysfs.c
+++ b/drivers/gpu/msm/adreno_sysfs.c
@@ -205,13 +205,14 @@
{
struct kgsl_device *device = KGSL_DEVICE(adreno_dev);
- if (test_bit(ADRENO_DEVICE_PREEMPTION, &adreno_dev->priv) == val)
+ if (test_bit(ADRENO_DEVICE_PREEMPTION_EXECUTION,
+ &adreno_dev->priv) == val)
return 0;
mutex_lock(&device->mutex);
kgsl_pwrctrl_change_state(device, KGSL_STATE_SUSPEND);
- change_bit(ADRENO_DEVICE_PREEMPTION, &adreno_dev->priv);
+ change_bit(ADRENO_DEVICE_PREEMPTION_EXECUTION, &adreno_dev->priv);
adreno_dev->cur_rb = &(adreno_dev->ringbuffers[0]);
kgsl_pwrctrl_change_state(device, KGSL_STATE_SLUMBER);
@@ -222,7 +223,7 @@
static unsigned int _preemption_show(struct adreno_device *adreno_dev)
{
- return adreno_is_preemption_enabled(adreno_dev);
+ return adreno_is_preemption_execution_enabled(adreno_dev);
}
static int _hwcg_store(struct adreno_device *adreno_dev,
diff --git a/drivers/gpu/msm/kgsl_gmu.c b/drivers/gpu/msm/kgsl_gmu.c
index 2ddb082..9b04543 100644
--- a/drivers/gpu/msm/kgsl_gmu.c
+++ b/drivers/gpu/msm/kgsl_gmu.c
@@ -1364,7 +1364,7 @@
/* Wait for the NMI to be handled */
wmb();
udelay(100);
- kgsl_device_snapshot(device, NULL);
+ kgsl_device_snapshot(device, ERR_PTR(-EINVAL));
adreno_write_gmureg(adreno_dev,
ADRENO_REG_GMU_GMU2HOST_INTR_CLR, 0xFFFFFFFF);
diff --git a/drivers/gpu/msm/kgsl_snapshot.c b/drivers/gpu/msm/kgsl_snapshot.c
index 7cbda72..e704db7 100644
--- a/drivers/gpu/msm/kgsl_snapshot.c
+++ b/drivers/gpu/msm/kgsl_snapshot.c
@@ -182,7 +182,8 @@
context = kgsl_context_get(device, header->current_context);
/* Get the current PT base */
- header->ptbase = kgsl_mmu_get_current_ttbr0(&device->mmu);
+ if (!IS_ERR(priv))
+ header->ptbase = kgsl_mmu_get_current_ttbr0(&device->mmu);
/* And the PID for the task leader */
if (context) {
@@ -633,8 +634,10 @@
snapshot->size += sizeof(*header);
/* Build the Linux specific header */
+ /* Context err is implied a GMU fault, so limit dump */
kgsl_snapshot_add_section(device, KGSL_SNAPSHOT_SECTION_OS,
- snapshot, snapshot_os, NULL);
+ snapshot, snapshot_os,
+ IS_ERR(context) ? context : NULL);
/* Get the device specific sections */
if (device->ftbl->snapshot)
diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c
index d7f6cf0..d42ace8 100644
--- a/drivers/hid/hid-core.c
+++ b/drivers/hid/hid-core.c
@@ -2485,6 +2485,7 @@
{ HID_USB_DEVICE(USB_VENDOR_ID_PANJIT, 0x0002) },
{ HID_USB_DEVICE(USB_VENDOR_ID_PANJIT, 0x0003) },
{ HID_USB_DEVICE(USB_VENDOR_ID_PANJIT, 0x0004) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_PETZL, USB_DEVICE_ID_PETZL_HEADLAMP) },
{ HID_USB_DEVICE(USB_VENDOR_ID_PHILIPS, USB_DEVICE_ID_PHILIPS_IEEE802154_DONGLE) },
{ HID_USB_DEVICE(USB_VENDOR_ID_POWERCOM, USB_DEVICE_ID_POWERCOM_UPS) },
#if IS_ENABLED(CONFIG_MOUSE_SYNAPTICS_USB)
diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h
index cfca43f..08fd3f8 100644
--- a/drivers/hid/hid-ids.h
+++ b/drivers/hid/hid-ids.h
@@ -819,6 +819,9 @@
#define USB_VENDOR_ID_PETALYNX 0x18b1
#define USB_DEVICE_ID_PETALYNX_MAXTER_REMOTE 0x0037
+#define USB_VENDOR_ID_PETZL 0x2122
+#define USB_DEVICE_ID_PETZL_HEADLAMP 0x1234
+
#define USB_VENDOR_ID_PHILIPS 0x0471
#define USB_DEVICE_ID_PHILIPS_IEEE802154_DONGLE 0x0617
diff --git a/drivers/infiniband/core/uverbs_cmd.c b/drivers/infiniband/core/uverbs_cmd.c
index 01e3a37..d118ffe 100644
--- a/drivers/infiniband/core/uverbs_cmd.c
+++ b/drivers/infiniband/core/uverbs_cmd.c
@@ -2342,8 +2342,9 @@
if (copy_from_user(&cmd, buf, sizeof cmd))
return -EFAULT;
- if (cmd.port_num < rdma_start_port(ib_dev) ||
- cmd.port_num > rdma_end_port(ib_dev))
+ if ((cmd.attr_mask & IB_QP_PORT) &&
+ (cmd.port_num < rdma_start_port(ib_dev) ||
+ cmd.port_num > rdma_end_port(ib_dev)))
return -EINVAL;
INIT_UDATA(&udata, buf + sizeof cmd, NULL, in_len - sizeof cmd,
diff --git a/drivers/infiniband/hw/mlx4/main.c b/drivers/infiniband/hw/mlx4/main.c
index f2a885e..8059b7e 100644
--- a/drivers/infiniband/hw/mlx4/main.c
+++ b/drivers/infiniband/hw/mlx4/main.c
@@ -1680,9 +1680,19 @@
size += ret;
}
+ if (mlx4_is_master(mdev->dev) && flow_type == MLX4_FS_REGULAR &&
+ flow_attr->num_of_specs == 1) {
+ struct _rule_hw *rule_header = (struct _rule_hw *)(ctrl + 1);
+ enum ib_flow_spec_type header_spec =
+ ((union ib_flow_spec *)(flow_attr + 1))->type;
+
+ if (header_spec == IB_FLOW_SPEC_ETH)
+ mlx4_handle_eth_header_mcast_prio(ctrl, rule_header);
+ }
+
ret = mlx4_cmd_imm(mdev->dev, mailbox->dma, reg_id, size >> 2, 0,
MLX4_QP_FLOW_STEERING_ATTACH, MLX4_CMD_TIME_CLASS_A,
- MLX4_CMD_WRAPPED);
+ MLX4_CMD_NATIVE);
if (ret == -ENOMEM)
pr_err("mcg table is full. Fail to register network rule.\n");
else if (ret == -ENXIO)
@@ -1699,7 +1709,7 @@
int err;
err = mlx4_cmd(dev, reg_id, 0, 0,
MLX4_QP_FLOW_STEERING_DETACH, MLX4_CMD_TIME_CLASS_A,
- MLX4_CMD_WRAPPED);
+ MLX4_CMD_NATIVE);
if (err)
pr_err("Fail to detach network rule. registration id = 0x%llx\n",
reg_id);
diff --git a/drivers/iommu/arm-smmu.c b/drivers/iommu/arm-smmu.c
index cdd638c..a2c5579 100644
--- a/drivers/iommu/arm-smmu.c
+++ b/drivers/iommu/arm-smmu.c
@@ -319,6 +319,14 @@
QCOM_SMMUV500,
};
+struct arm_smmu_device;
+struct arm_smmu_arch_ops {
+ int (*init)(struct arm_smmu_device *smmu);
+ void (*device_reset)(struct arm_smmu_device *smmu);
+ phys_addr_t (*iova_to_phys_hard)(struct iommu_domain *domain,
+ dma_addr_t iova);
+};
+
struct arm_smmu_impl_def_reg {
u32 offset;
u32 value;
@@ -392,7 +400,6 @@
int regulator_defer;
};
-struct arm_smmu_arch_ops;
struct arm_smmu_device {
struct device *dev;
@@ -554,6 +561,9 @@
static int arm_smmu_assign_table(struct arm_smmu_domain *smmu_domain);
static void arm_smmu_unassign_table(struct arm_smmu_domain *smmu_domain);
+static int arm_smmu_arch_init(struct arm_smmu_device *smmu);
+static void arm_smmu_arch_device_reset(struct arm_smmu_device *smmu);
+
static uint64_t arm_smmu_iova_to_pte(struct iommu_domain *domain,
dma_addr_t iova);
@@ -617,76 +627,6 @@
mutex_unlock(&smmu_domain->assign_lock);
}
-/*
- * init()
- * Hook for additional device tree parsing at probe time.
- *
- * device_reset()
- * Hook for one-time architecture-specific register settings.
- *
- * iova_to_phys_hard()
- * Provides debug information. May be called from the context fault irq handler.
- *
- * init_context_bank()
- * Hook for architecture-specific settings which require knowledge of the
- * dynamically allocated context bank number.
- *
- * device_group()
- * Hook for checking whether a device is compatible with a said group.
- */
-struct arm_smmu_arch_ops {
- int (*init)(struct arm_smmu_device *smmu);
- void (*device_reset)(struct arm_smmu_device *smmu);
- phys_addr_t (*iova_to_phys_hard)(struct iommu_domain *domain,
- dma_addr_t iova);
- void (*init_context_bank)(struct arm_smmu_domain *smmu_domain,
- struct device *dev);
- int (*device_group)(struct device *dev, struct iommu_group *group);
-};
-
-static int arm_smmu_arch_init(struct arm_smmu_device *smmu)
-{
- if (!smmu->arch_ops)
- return 0;
- if (!smmu->arch_ops->init)
- return 0;
- return smmu->arch_ops->init(smmu);
-}
-
-static void arm_smmu_arch_device_reset(struct arm_smmu_device *smmu)
-{
- if (!smmu->arch_ops)
- return;
- if (!smmu->arch_ops->device_reset)
- return;
- return smmu->arch_ops->device_reset(smmu);
-}
-
-static void arm_smmu_arch_init_context_bank(
- struct arm_smmu_domain *smmu_domain, struct device *dev)
-{
- struct arm_smmu_device *smmu = smmu_domain->smmu;
-
- if (!smmu->arch_ops)
- return;
- if (!smmu->arch_ops->init_context_bank)
- return;
- return smmu->arch_ops->init_context_bank(smmu_domain, dev);
-}
-
-static int arm_smmu_arch_device_group(struct device *dev,
- struct iommu_group *group)
-{
- struct iommu_fwspec *fwspec = dev->iommu_fwspec;
- struct arm_smmu_device *smmu = fwspec_smmu(fwspec);
-
- if (!smmu->arch_ops)
- return 0;
- if (!smmu->arch_ops->device_group)
- return 0;
- return smmu->arch_ops->device_group(dev, group);
-}
-
static struct device_node *dev_get_dev_node(struct device *dev)
{
if (dev_is_pci(dev)) {
@@ -1814,8 +1754,6 @@
arm_smmu_init_context_bank(smmu_domain,
&smmu_domain->pgtbl_cfg);
- arm_smmu_arch_init_context_bank(smmu_domain, dev);
-
/*
* Request context fault interrupt. Do this last to avoid the
* handler seeing a half-initialised domain state.
@@ -2721,20 +2659,13 @@
group = smmu->s2crs[idx].group;
}
- if (!group) {
- if (dev_is_pci(dev))
- group = pci_device_group(dev);
- else
- group = generic_device_group(dev);
+ if (group)
+ return group;
- if (IS_ERR(group))
- return NULL;
- }
-
- if (arm_smmu_arch_device_group(dev, group)) {
- iommu_group_put(group);
- return ERR_PTR(-EINVAL);
- }
+ if (dev_is_pci(dev))
+ group = pci_device_group(dev);
+ else
+ group = generic_device_group(dev);
return group;
}
@@ -4011,6 +3942,24 @@
return 0;
}
+static int arm_smmu_arch_init(struct arm_smmu_device *smmu)
+{
+ if (!smmu->arch_ops)
+ return 0;
+ if (!smmu->arch_ops->init)
+ return 0;
+ return smmu->arch_ops->init(smmu);
+}
+
+static void arm_smmu_arch_device_reset(struct arm_smmu_device *smmu)
+{
+ if (!smmu->arch_ops)
+ return;
+ if (!smmu->arch_ops->device_reset)
+ return;
+ return smmu->arch_ops->device_reset(smmu);
+}
+
struct arm_smmu_match_data {
enum arm_smmu_arch_version version;
enum arm_smmu_implementation model;
@@ -4335,19 +4284,10 @@
#define TBU_DBG_TIMEOUT_US 30000
-
-struct actlr_setting {
- struct arm_smmu_smr smr;
- u32 actlr;
-};
-
struct qsmmuv500_archdata {
struct list_head tbus;
void __iomem *tcu_base;
u32 version;
-
- struct actlr_setting *actlrs;
- u32 actlr_tbl_size;
};
#define get_qsmmuv500_archdata(smmu) \
((struct qsmmuv500_archdata *)(smmu->archdata))
@@ -4368,14 +4308,6 @@
u32 halt_count;
};
-struct qsmmuv500_group_iommudata {
- bool has_actlr;
- u32 actlr;
-};
-#define to_qsmmuv500_group_iommudata(group) \
- ((struct qsmmuv500_group_iommudata *) \
- (iommu_group_get_iommudata(group)))
-
static int qsmmuv500_tbu_halt(struct qsmmuv500_tbu_device *tbu)
{
unsigned long flags;
@@ -4629,79 +4561,6 @@
return qsmmuv500_iova_to_phys(domain, iova, sid);
}
-static void qsmmuv500_release_group_iommudata(void *data)
-{
- kfree(data);
-}
-
-/* If a device has a valid actlr, it must match */
-static int qsmmuv500_device_group(struct device *dev,
- struct iommu_group *group)
-{
- struct iommu_fwspec *fwspec = dev->iommu_fwspec;
- struct arm_smmu_device *smmu = fwspec_smmu(fwspec);
- struct qsmmuv500_archdata *data = get_qsmmuv500_archdata(smmu);
- struct qsmmuv500_group_iommudata *iommudata;
- u32 actlr, i, j, idx;
- struct arm_smmu_smr *smr, *smr2;
-
- iommudata = to_qsmmuv500_group_iommudata(group);
- if (!iommudata) {
- iommudata = kzalloc(sizeof(*iommudata), GFP_KERNEL);
- if (!iommudata)
- return -ENOMEM;
-
- iommu_group_set_iommudata(group, iommudata,
- qsmmuv500_release_group_iommudata);
- }
-
- for_each_cfg_sme(fwspec, i, idx) {
- smr = &smmu->smrs[idx];
- for (j = 0; j < data->actlr_tbl_size; j++) {
- smr2 = &data->actlrs[j].smr;
- actlr = data->actlrs[j].actlr;
-
- /* Continue if table entry does not match */
- if ((smr->id ^ smr2->id) & ~(smr->mask | smr2->mask))
- continue;
-
- if (!iommudata->has_actlr) {
- iommudata->actlr = actlr;
- iommudata->has_actlr = true;
- } else if (iommudata->actlr != actlr) {
- return -EINVAL;
- }
- }
- }
-
- return 0;
-}
-
-static void qsmmuv500_init_cb(struct arm_smmu_domain *smmu_domain,
- struct device *dev)
-{
- struct arm_smmu_device *smmu = smmu_domain->smmu;
- struct qsmmuv500_group_iommudata *iommudata =
- to_qsmmuv500_group_iommudata(dev->iommu_group);
- void __iomem *cb_base;
- const struct iommu_gather_ops *tlb;
-
- if (!iommudata->has_actlr)
- return;
-
- tlb = smmu_domain->pgtbl_cfg.tlb;
- cb_base = ARM_SMMU_CB_BASE(smmu) +
- ARM_SMMU_CB(smmu, smmu_domain->cfg.cbndx);
-
- writel_relaxed(iommudata->actlr, cb_base + ARM_SMMU_CB_ACTLR);
-
- /*
- * Flush the context bank after modifying ACTLR to ensure there
- * are no cache entries with stale state
- */
- tlb->tlb_flush_all(smmu_domain);
-}
-
static int qsmmuv500_tbu_register(struct device *dev, void *cookie)
{
struct arm_smmu_device *smmu = cookie;
@@ -4721,38 +4580,6 @@
return 0;
}
-static int qsmmuv500_read_actlr_tbl(struct arm_smmu_device *smmu)
-{
- int len, i;
- struct device *dev = smmu->dev;
- struct qsmmuv500_archdata *data = get_qsmmuv500_archdata(smmu);
- struct actlr_setting *actlrs;
- const __be32 *cell;
-
- cell = of_get_property(dev->of_node, "qcom,actlr", NULL);
- if (!cell)
- return 0;
-
- len = of_property_count_elems_of_size(dev->of_node, "qcom,actlr",
- sizeof(u32) * 3);
- if (len < 0)
- return 0;
-
- actlrs = devm_kzalloc(dev, sizeof(*actlrs) * len, GFP_KERNEL);
- if (!actlrs)
- return -ENOMEM;
-
- for (i = 0; i < len; i++) {
- actlrs[i].smr.id = of_read_number(cell++, 1);
- actlrs[i].smr.mask = of_read_number(cell++, 1);
- actlrs[i].actlr = of_read_number(cell++, 1);
- }
-
- data->actlrs = actlrs;
- data->actlr_tbl_size = len;
- return 0;
-}
-
static int qsmmuv500_arch_init(struct arm_smmu_device *smmu)
{
struct resource *res;
@@ -4760,8 +4587,6 @@
struct qsmmuv500_archdata *data;
struct platform_device *pdev;
int ret;
- u32 val;
- void __iomem *reg;
data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
if (!data)
@@ -4778,23 +4603,6 @@
data->version = readl_relaxed(data->tcu_base + TCU_HW_VERSION_HLOS1);
smmu->archdata = data;
- ret = qsmmuv500_read_actlr_tbl(smmu);
- if (ret)
- return ret;
-
- reg = ARM_SMMU_GR0(smmu);
- val = readl_relaxed(reg + ARM_SMMU_GR0_sACR);
- val &= ~ARM_MMU500_ACR_CACHE_LOCK;
- writel_relaxed(val, reg + ARM_SMMU_GR0_sACR);
- val = readl_relaxed(reg + ARM_SMMU_GR0_sACR);
- /*
- * Modifiying the nonsecure copy of the sACR register is only
- * allowed if permission is given in the secure sACR register.
- * Attempt to detect if we were able to update the value.
- */
- WARN_ON(val & ARM_MMU500_ACR_CACHE_LOCK);
-
-
ret = of_platform_populate(dev->of_node, NULL, NULL, dev);
if (ret)
return ret;
@@ -4810,8 +4618,6 @@
struct arm_smmu_arch_ops qsmmuv500_arch_ops = {
.init = qsmmuv500_arch_init,
.iova_to_phys_hard = qsmmuv500_iova_to_phys_hard,
- .init_context_bank = qsmmuv500_init_cb,
- .device_group = qsmmuv500_device_group,
};
static const struct of_device_id qsmmuv500_tbu_of_match[] = {
diff --git a/drivers/iommu/iova.c b/drivers/iommu/iova.c
index e23001b..5c88ba7 100644
--- a/drivers/iommu/iova.c
+++ b/drivers/iommu/iova.c
@@ -56,13 +56,13 @@
static struct rb_node *
__get_cached_rbnode(struct iova_domain *iovad, unsigned long *limit_pfn)
{
- if ((*limit_pfn != iovad->dma_32bit_pfn) ||
+ if ((*limit_pfn > iovad->dma_32bit_pfn) ||
(iovad->cached32_node == NULL))
return rb_last(&iovad->rbroot);
else {
struct rb_node *prev_node = rb_prev(iovad->cached32_node);
struct iova *curr_iova =
- container_of(iovad->cached32_node, struct iova, node);
+ rb_entry(iovad->cached32_node, struct iova, node);
*limit_pfn = curr_iova->pfn_lo - 1;
return prev_node;
}
@@ -86,11 +86,11 @@
if (!iovad->cached32_node)
return;
curr = iovad->cached32_node;
- cached_iova = container_of(curr, struct iova, node);
+ cached_iova = rb_entry(curr, struct iova, node);
if (free->pfn_lo >= cached_iova->pfn_lo) {
struct rb_node *node = rb_next(&free->node);
- struct iova *iova = container_of(node, struct iova, node);
+ struct iova *iova = rb_entry(node, struct iova, node);
/* only cache if it's below 32bit pfn */
if (node && iova->pfn_lo < iovad->dma_32bit_pfn)
@@ -100,6 +100,34 @@
}
}
+/* Insert the iova into domain rbtree by holding writer lock */
+static void
+iova_insert_rbtree(struct rb_root *root, struct iova *iova,
+ struct rb_node *start)
+{
+ struct rb_node **new, *parent = NULL;
+
+ new = (start) ? &start : &(root->rb_node);
+ /* Figure out where to put new node */
+ while (*new) {
+ struct iova *this = rb_entry(*new, struct iova, node);
+
+ parent = *new;
+
+ if (iova->pfn_lo < this->pfn_lo)
+ new = &((*new)->rb_left);
+ else if (iova->pfn_lo > this->pfn_lo)
+ new = &((*new)->rb_right);
+ else {
+ WARN_ON(1); /* this should not happen */
+ return;
+ }
+ }
+ /* Add new node and rebalance tree. */
+ rb_link_node(&iova->node, parent, new);
+ rb_insert_color(&iova->node, root);
+}
+
/*
* Computes the padding size required, to make the start address
* naturally aligned on the power-of-two order of its size
@@ -125,7 +153,7 @@
curr = __get_cached_rbnode(iovad, &limit_pfn);
prev = curr;
while (curr) {
- struct iova *curr_iova = container_of(curr, struct iova, node);
+ struct iova *curr_iova = rb_entry(curr, struct iova, node);
if (limit_pfn < curr_iova->pfn_lo)
goto move_left;
@@ -138,7 +166,7 @@
break; /* found a free slot */
}
adjust_limit_pfn:
- limit_pfn = curr_iova->pfn_lo - 1;
+ limit_pfn = curr_iova->pfn_lo ? (curr_iova->pfn_lo - 1) : 0;
move_left:
prev = curr;
curr = rb_prev(curr);
@@ -157,36 +185,8 @@
new->pfn_lo = limit_pfn - (size + pad_size) + 1;
new->pfn_hi = new->pfn_lo + size - 1;
- /* Insert the new_iova into domain rbtree by holding writer lock */
- /* Add new node and rebalance tree. */
- {
- struct rb_node **entry, *parent = NULL;
-
- /* If we have 'prev', it's a valid place to start the
- insertion. Otherwise, start from the root. */
- if (prev)
- entry = &prev;
- else
- entry = &iovad->rbroot.rb_node;
-
- /* Figure out where to put new node */
- while (*entry) {
- struct iova *this = container_of(*entry,
- struct iova, node);
- parent = *entry;
-
- if (new->pfn_lo < this->pfn_lo)
- entry = &((*entry)->rb_left);
- else if (new->pfn_lo > this->pfn_lo)
- entry = &((*entry)->rb_right);
- else
- BUG(); /* this should not happen */
- }
-
- /* Add new node and rebalance tree. */
- rb_link_node(&new->node, parent, entry);
- rb_insert_color(&new->node, &iovad->rbroot);
- }
+ /* If we have 'prev', it's a valid place to start the insertion. */
+ iova_insert_rbtree(&iovad->rbroot, new, prev);
__cached_rbnode_insert_update(iovad, saved_pfn, new);
spin_unlock_irqrestore(&iovad->iova_rbtree_lock, flags);
@@ -195,28 +195,6 @@
return 0;
}
-static void
-iova_insert_rbtree(struct rb_root *root, struct iova *iova)
-{
- struct rb_node **new = &(root->rb_node), *parent = NULL;
- /* Figure out where to put new node */
- while (*new) {
- struct iova *this = container_of(*new, struct iova, node);
-
- parent = *new;
-
- if (iova->pfn_lo < this->pfn_lo)
- new = &((*new)->rb_left);
- else if (iova->pfn_lo > this->pfn_lo)
- new = &((*new)->rb_right);
- else
- BUG(); /* this should not happen */
- }
- /* Add new node and rebalance tree. */
- rb_link_node(&iova->node, parent, new);
- rb_insert_color(&iova->node, root);
-}
-
static struct kmem_cache *iova_cache;
static unsigned int iova_cache_users;
static DEFINE_MUTEX(iova_cache_mutex);
@@ -311,7 +289,7 @@
assert_spin_locked(&iovad->iova_rbtree_lock);
while (node) {
- struct iova *iova = container_of(node, struct iova, node);
+ struct iova *iova = rb_entry(node, struct iova, node);
/* If pfn falls within iova's range, return iova */
if ((pfn >= iova->pfn_lo) && (pfn <= iova->pfn_hi)) {
@@ -463,7 +441,7 @@
spin_lock_irqsave(&iovad->iova_rbtree_lock, flags);
node = rb_first(&iovad->rbroot);
while (node) {
- struct iova *iova = container_of(node, struct iova, node);
+ struct iova *iova = rb_entry(node, struct iova, node);
rb_erase(node, &iovad->rbroot);
free_iova_mem(iova);
@@ -477,7 +455,7 @@
__is_range_overlap(struct rb_node *node,
unsigned long pfn_lo, unsigned long pfn_hi)
{
- struct iova *iova = container_of(node, struct iova, node);
+ struct iova *iova = rb_entry(node, struct iova, node);
if ((pfn_lo <= iova->pfn_hi) && (pfn_hi >= iova->pfn_lo))
return 1;
@@ -506,7 +484,7 @@
iova = alloc_and_init_iova(pfn_lo, pfn_hi);
if (iova)
- iova_insert_rbtree(&iovad->rbroot, iova);
+ iova_insert_rbtree(&iovad->rbroot, iova, NULL);
return iova;
}
@@ -541,7 +519,7 @@
spin_lock_irqsave(&iovad->iova_rbtree_lock, flags);
for (node = rb_first(&iovad->rbroot); node; node = rb_next(node)) {
if (__is_range_overlap(node, pfn_lo, pfn_hi)) {
- iova = container_of(node, struct iova, node);
+ iova = rb_entry(node, struct iova, node);
__adjust_overlap_range(iova, &pfn_lo, &pfn_hi);
if ((pfn_lo >= iova->pfn_lo) &&
(pfn_hi <= iova->pfn_hi))
@@ -578,7 +556,7 @@
spin_lock_irqsave(&from->iova_rbtree_lock, flags);
for (node = rb_first(&from->rbroot); node; node = rb_next(node)) {
- struct iova *iova = container_of(node, struct iova, node);
+ struct iova *iova = rb_entry(node, struct iova, node);
struct iova *new_iova;
new_iova = reserve_iova(to, iova->pfn_lo, iova->pfn_hi);
@@ -613,11 +591,11 @@
rb_erase(&iova->node, &iovad->rbroot);
if (prev) {
- iova_insert_rbtree(&iovad->rbroot, prev);
+ iova_insert_rbtree(&iovad->rbroot, prev, NULL);
iova->pfn_lo = pfn_lo;
}
if (next) {
- iova_insert_rbtree(&iovad->rbroot, next);
+ iova_insert_rbtree(&iovad->rbroot, next, NULL);
iova->pfn_hi = pfn_hi;
}
spin_unlock_irqrestore(&iovad->iova_rbtree_lock, flags);
diff --git a/drivers/irqchip/irq-keystone.c b/drivers/irqchip/irq-keystone.c
index 54a5e87..efbcf84 100644
--- a/drivers/irqchip/irq-keystone.c
+++ b/drivers/irqchip/irq-keystone.c
@@ -19,9 +19,9 @@
#include <linux/bitops.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
+#include <linux/interrupt.h>
#include <linux/irqdomain.h>
#include <linux/irqchip.h>
-#include <linux/irqchip/chained_irq.h>
#include <linux/of.h>
#include <linux/of_platform.h>
#include <linux/mfd/syscon.h>
@@ -39,6 +39,7 @@
struct irq_domain *irqd;
struct regmap *devctrl_regs;
u32 devctrl_offset;
+ raw_spinlock_t wa_lock;
};
static inline u32 keystone_irq_readl(struct keystone_irq_device *kirq)
@@ -83,17 +84,15 @@
/* nothing to do here */
}
-static void keystone_irq_handler(struct irq_desc *desc)
+static irqreturn_t keystone_irq_handler(int irq, void *keystone_irq)
{
- unsigned int irq = irq_desc_get_irq(desc);
- struct keystone_irq_device *kirq = irq_desc_get_handler_data(desc);
+ struct keystone_irq_device *kirq = keystone_irq;
+ unsigned long wa_lock_flags;
unsigned long pending;
int src, virq;
dev_dbg(kirq->dev, "start irq %d\n", irq);
- chained_irq_enter(irq_desc_get_chip(desc), desc);
-
pending = keystone_irq_readl(kirq);
keystone_irq_writel(kirq, pending);
@@ -111,13 +110,15 @@
if (!virq)
dev_warn(kirq->dev, "spurious irq detected hwirq %d, virq %d\n",
src, virq);
+ raw_spin_lock_irqsave(&kirq->wa_lock, wa_lock_flags);
generic_handle_irq(virq);
+ raw_spin_unlock_irqrestore(&kirq->wa_lock,
+ wa_lock_flags);
}
}
- chained_irq_exit(irq_desc_get_chip(desc), desc);
-
dev_dbg(kirq->dev, "end irq %d\n", irq);
+ return IRQ_HANDLED;
}
static int keystone_irq_map(struct irq_domain *h, unsigned int virq,
@@ -182,9 +183,16 @@
return -ENODEV;
}
+ raw_spin_lock_init(&kirq->wa_lock);
+
platform_set_drvdata(pdev, kirq);
- irq_set_chained_handler_and_data(kirq->irq, keystone_irq_handler, kirq);
+ ret = request_irq(kirq->irq, keystone_irq_handler,
+ 0, dev_name(dev), kirq);
+ if (ret) {
+ irq_domain_remove(kirq->irqd);
+ return ret;
+ }
/* clear all source bits */
keystone_irq_writel(kirq, ~0x0);
@@ -199,6 +207,8 @@
struct keystone_irq_device *kirq = platform_get_drvdata(pdev);
int hwirq;
+ free_irq(kirq->irq, kirq);
+
for (hwirq = 0; hwirq < KEYSTONE_N_IRQ; hwirq++)
irq_dispose_mapping(irq_find_mapping(kirq->irqd, hwirq));
diff --git a/drivers/irqchip/irq-mxs.c b/drivers/irqchip/irq-mxs.c
index 1730470..05fa9f7 100644
--- a/drivers/irqchip/irq-mxs.c
+++ b/drivers/irqchip/irq-mxs.c
@@ -131,12 +131,16 @@
.irq_ack = icoll_ack_irq,
.irq_mask = icoll_mask_irq,
.irq_unmask = icoll_unmask_irq,
+ .flags = IRQCHIP_MASK_ON_SUSPEND |
+ IRQCHIP_SKIP_SET_WAKE,
};
static struct irq_chip asm9260_icoll_chip = {
.irq_ack = icoll_ack_irq,
.irq_mask = asm9260_mask_irq,
.irq_unmask = asm9260_unmask_irq,
+ .flags = IRQCHIP_MASK_ON_SUSPEND |
+ IRQCHIP_SKIP_SET_WAKE,
};
asmlinkage void __exception_irq_entry icoll_handle_irq(struct pt_regs *regs)
diff --git a/drivers/isdn/i4l/isdn_common.c b/drivers/isdn/i4l/isdn_common.c
index 9b856e1..e4c43a1 100644
--- a/drivers/isdn/i4l/isdn_common.c
+++ b/drivers/isdn/i4l/isdn_common.c
@@ -1379,6 +1379,7 @@
if (arg) {
if (copy_from_user(bname, argp, sizeof(bname) - 1))
return -EFAULT;
+ bname[sizeof(bname)-1] = 0;
} else
return -EINVAL;
ret = mutex_lock_interruptible(&dev->mtx);
diff --git a/drivers/isdn/i4l/isdn_net.c b/drivers/isdn/i4l/isdn_net.c
index c151c6d..f63a110 100644
--- a/drivers/isdn/i4l/isdn_net.c
+++ b/drivers/isdn/i4l/isdn_net.c
@@ -2611,10 +2611,9 @@
char newname[10];
if (p) {
- /* Slave-Name MUST not be empty */
- if (!strlen(p + 1))
+ /* Slave-Name MUST not be empty or overflow 'newname' */
+ if (strscpy(newname, p + 1, sizeof(newname)) <= 0)
return NULL;
- strcpy(newname, p + 1);
*p = 0;
/* Master must already exist */
if (!(n = isdn_net_findif(parm)))
diff --git a/drivers/isdn/i4l/isdn_ppp.c b/drivers/isdn/i4l/isdn_ppp.c
index 9c1e8ad..bf3fbd0 100644
--- a/drivers/isdn/i4l/isdn_ppp.c
+++ b/drivers/isdn/i4l/isdn_ppp.c
@@ -2364,7 +2364,7 @@
id);
return NULL;
} else {
- rs = kzalloc(sizeof(struct ippp_ccp_reset_state), GFP_KERNEL);
+ rs = kzalloc(sizeof(struct ippp_ccp_reset_state), GFP_ATOMIC);
if (!rs)
return NULL;
rs->state = CCPResetIdle;
diff --git a/drivers/mailbox/mailbox.c b/drivers/mailbox/mailbox.c
index 3989bc6..9f340bf 100644
--- a/drivers/mailbox/mailbox.c
+++ b/drivers/mailbox/mailbox.c
@@ -115,11 +115,14 @@
/* Submit next message */
msg_submit(chan);
+ if (!mssg)
+ return;
+
/* Notify the client */
- if (mssg && chan->cl->tx_done)
+ if (chan->cl->tx_done)
chan->cl->tx_done(chan->cl, mssg, r);
- if (chan->cl->tx_block)
+ if (r != -ETIME && chan->cl->tx_block)
complete(&chan->tx_complete);
}
@@ -272,7 +275,7 @@
msg_submit(chan);
- if (chan->cl->tx_block && chan->active_req) {
+ if (chan->cl->tx_block) {
unsigned long wait;
int ret;
@@ -283,8 +286,8 @@
ret = wait_for_completion_timeout(&chan->tx_complete, wait);
if (ret == 0) {
- t = -EIO;
- tx_tick(chan, -EIO);
+ t = -ETIME;
+ tx_tick(chan, t);
}
}
@@ -293,8 +296,9 @@
EXPORT_SYMBOL_GPL(mbox_send_message);
/**
- * mbox_send_controller_data- For client to submit a message to be
- * sent only to the controller.
+ * mbox_write_controller_data - For client to submit a message to be
+ * written to the controller but not sent to
+ * the remote processor.
* @chan: Mailbox channel assigned to this client.
* @mssg: Client specific message typecasted.
*
@@ -305,7 +309,7 @@
* or transmission over chan (blocking mode).
* Negative value denotes failure.
*/
-int mbox_send_controller_data(struct mbox_chan *chan, void *mssg)
+int mbox_write_controller_data(struct mbox_chan *chan, void *mssg)
{
unsigned long flags;
int err;
@@ -314,12 +318,12 @@
return -EINVAL;
spin_lock_irqsave(&chan->lock, flags);
- err = chan->mbox->ops->send_controller_data(chan, mssg);
+ err = chan->mbox->ops->write_controller_data(chan, mssg);
spin_unlock_irqrestore(&chan->lock, flags);
return err;
}
-EXPORT_SYMBOL(mbox_send_controller_data);
+EXPORT_SYMBOL(mbox_write_controller_data);
bool mbox_controller_is_idle(struct mbox_chan *chan)
{
diff --git a/drivers/mailbox/qcom-rpmh-mailbox.c b/drivers/mailbox/qcom-rpmh-mailbox.c
index ab75d67..7bf8a18 100644
--- a/drivers/mailbox/qcom-rpmh-mailbox.c
+++ b/drivers/mailbox/qcom-rpmh-mailbox.c
@@ -1075,7 +1075,7 @@
static const struct mbox_chan_ops mbox_ops = {
.send_data = chan_tcs_write,
- .send_controller_data = chan_tcs_ctrl_write,
+ .write_controller_data = chan_tcs_ctrl_write,
.startup = chan_init,
.shutdown = chan_shutdown,
};
@@ -1178,11 +1178,10 @@
if (tcs->num_tcs <= 0 || tcs->type == CONTROL_TCS)
continue;
- if (tcs->num_tcs > MAX_TCS_PER_TYPE)
- return -EINVAL;
-
- if (st + tcs->num_tcs > max_tcs &&
- st + tcs->num_tcs >= sizeof(tcs->tcs_mask))
+ if (tcs->num_tcs > MAX_TCS_PER_TYPE ||
+ st + tcs->num_tcs > max_tcs ||
+ st + tcs->num_tcs >=
+ BITS_PER_BYTE * sizeof(tcs->tcs_mask))
return -EINVAL;
tcs->tcs_mask = ((1 << tcs->num_tcs) - 1) << st;
diff --git a/drivers/md/raid5.c b/drivers/md/raid5.c
index 8f117d6..383f19c 100644
--- a/drivers/md/raid5.c
+++ b/drivers/md/raid5.c
@@ -5843,6 +5843,8 @@
pr_debug("%d stripes handled\n", handled);
spin_unlock_irq(&conf->device_lock);
+
+ async_tx_issue_pending_all();
blk_finish_plug(&plug);
pr_debug("--- raid5worker inactive\n");
diff --git a/drivers/media/i2c/s5c73m3/s5c73m3-ctrls.c b/drivers/media/i2c/s5c73m3/s5c73m3-ctrls.c
index 0a06033..2e71850 100644
--- a/drivers/media/i2c/s5c73m3/s5c73m3-ctrls.c
+++ b/drivers/media/i2c/s5c73m3/s5c73m3-ctrls.c
@@ -211,7 +211,7 @@
}
if ((ctrl->val ^ ctrl->cur.val) & V4L2_LOCK_FOCUS)
- ret = s5c73m3_af_run(state, ~af_lock);
+ ret = s5c73m3_af_run(state, !af_lock);
return ret;
}
diff --git a/drivers/media/platform/msm/camera/Makefile b/drivers/media/platform/msm/camera/Makefile
index 800c9ea..48fa1c0 100644
--- a/drivers/media/platform/msm/camera/Makefile
+++ b/drivers/media/platform/msm/camera/Makefile
@@ -9,3 +9,4 @@
obj-$(CONFIG_SPECTRA_CAMERA) += cam_sensor_module/
obj-$(CONFIG_SPECTRA_CAMERA) += cam_icp/
obj-$(CONFIG_SPECTRA_CAMERA) += cam_jpeg/
+obj-$(CONFIG_SPECTRA_CAMERA) += cam_fd/
diff --git a/drivers/media/platform/msm/camera/cam_fd/Makefile b/drivers/media/platform/msm/camera/cam_fd/Makefile
new file mode 100644
index 0000000..f8177e8
--- /dev/null
+++ b/drivers/media/platform/msm/camera/cam_fd/Makefile
@@ -0,0 +1,14 @@
+ccflags-y += -Idrivers/media/platform/msm/camera/cam_utils
+ccflags-y += -Idrivers/media/platform/msm/camera/cam_req_mgr
+ccflags-y += -Idrivers/media/platform/msm/camera/cam_core
+ccflags-y += -Idrivers/media/platform/msm/camera/cam_sync
+ccflags-y += -Idrivers/media/platform/msm/camera/cam_smmu
+ccflags-y += -Idrivers/media/platform/msm/camera/cam_cdm
+ccflags-y += -Idrivers/media/platform/msm/camera/cam_fd
+ccflags-y += -Idrivers/media/platform/msm/camera/cam_fd/fd_hw_mgr
+ccflags-y += -Idrivers/media/platform/msm/camera/cam_fd/fd_hw_mgr/fd_hw
+ccflags-y += -Idrivers/media/platform/msm/camera
+ccflags-y += -Idrivers/media/platform/msm/camera/cam_cpas/include
+
+obj-$(CONFIG_SPECTRA_CAMERA) += fd_hw_mgr/
+obj-$(CONFIG_SPECTRA_CAMERA) += cam_fd_dev.o cam_fd_context.o
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
new file mode 100644
index 0000000..4c29ffd
--- /dev/null
+++ b/drivers/media/platform/msm/camera/cam_fd/cam_fd_context.c
@@ -0,0 +1,236 @@
+/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+
+#include "cam_debug_util.h"
+#include "cam_fd_context.h"
+
+/* Functions in Available state */
+static int __cam_fd_ctx_acquire_dev_in_available(struct cam_context *ctx,
+ struct cam_acquire_dev_cmd *cmd)
+{
+ int rc;
+
+ rc = cam_context_acquire_dev_to_hw(ctx, cmd);
+ if (rc) {
+ CAM_ERR(CAM_FD, "Failed in Acquire dev, rc=%d", rc);
+ return rc;
+ }
+
+ ctx->state = CAM_CTX_ACQUIRED;
+
+ return rc;
+}
+
+/* Functions in Acquired state */
+static int __cam_fd_ctx_release_dev_in_acquired(struct cam_context *ctx,
+ struct cam_release_dev_cmd *cmd)
+{
+ int rc;
+
+ rc = cam_context_release_dev_to_hw(ctx, cmd);
+ if (rc) {
+ CAM_ERR(CAM_FD, "Failed in Release dev, rc=%d", rc);
+ return rc;
+ }
+
+ ctx->state = CAM_CTX_AVAILABLE;
+
+ return rc;
+}
+
+static int __cam_fd_ctx_config_dev_in_acquired(struct cam_context *ctx,
+ struct cam_config_dev_cmd *cmd)
+{
+ int rc;
+
+ rc = cam_context_prepare_dev_to_hw(ctx, cmd);
+ if (rc) {
+ CAM_ERR(CAM_FD, "Failed in Prepare dev, rc=%d", rc);
+ return rc;
+ }
+
+ return rc;
+}
+
+static int __cam_fd_ctx_start_dev_in_acquired(struct cam_context *ctx,
+ struct cam_start_stop_dev_cmd *cmd)
+{
+ int rc;
+
+ rc = cam_context_start_dev_to_hw(ctx, cmd);
+ if (rc) {
+ CAM_ERR(CAM_FD, "Failed in Start dev, rc=%d", rc);
+ return rc;
+ }
+
+ ctx->state = CAM_CTX_ACTIVATED;
+
+ return rc;
+}
+
+/* Functions in Activated state */
+static int __cam_fd_ctx_stop_dev_in_activated(struct cam_context *ctx,
+ struct cam_start_stop_dev_cmd *cmd)
+{
+ int rc;
+
+ rc = cam_context_stop_dev_to_hw(ctx);
+ if (rc) {
+ CAM_ERR(CAM_FD, "Failed in Stop dev, rc=%d", rc);
+ return rc;
+ }
+
+ ctx->state = CAM_CTX_ACQUIRED;
+
+ return rc;
+}
+
+static int __cam_fd_ctx_release_dev_in_activated(struct cam_context *ctx,
+ struct cam_release_dev_cmd *cmd)
+{
+ int rc;
+
+ rc = __cam_fd_ctx_stop_dev_in_activated(ctx, NULL);
+ if (rc) {
+ CAM_ERR(CAM_FD, "Failed in Stop dev, rc=%d", rc);
+ return rc;
+ }
+
+ rc = __cam_fd_ctx_release_dev_in_acquired(ctx, cmd);
+ if (rc) {
+ CAM_ERR(CAM_FD, "Failed in Release dev, rc=%d", rc);
+ return rc;
+ }
+
+ return rc;
+}
+
+static int __cam_fd_ctx_config_dev_in_activated(
+ struct cam_context *ctx, struct cam_config_dev_cmd *cmd)
+{
+ int rc;
+
+ rc = cam_context_prepare_dev_to_hw(ctx, cmd);
+ if (rc) {
+ CAM_ERR(CAM_FD, "Failed in Prepare dev, rc=%d", rc);
+ return rc;
+ }
+
+ return rc;
+}
+
+static int __cam_fd_ctx_handle_irq_in_activated(void *context,
+ uint32_t evt_id, void *evt_data)
+{
+ int rc;
+
+ rc = cam_context_buf_done_from_hw(context, evt_data, evt_id);
+ if (rc) {
+ CAM_ERR(CAM_FD, "Failed in buf done, rc=%d", rc);
+ return rc;
+ }
+
+ return rc;
+}
+
+/* top state machine */
+static struct cam_ctx_ops
+ cam_fd_ctx_state_machine[CAM_CTX_STATE_MAX] = {
+ /* Uninit */
+ {
+ .ioctl_ops = {},
+ .crm_ops = {},
+ .irq_ops = NULL,
+ },
+ /* Available */
+ {
+ .ioctl_ops = {
+ .acquire_dev = __cam_fd_ctx_acquire_dev_in_available,
+ },
+ .crm_ops = {},
+ .irq_ops = NULL,
+ },
+ /* Acquired */
+ {
+ .ioctl_ops = {
+ .release_dev = __cam_fd_ctx_release_dev_in_acquired,
+ .config_dev = __cam_fd_ctx_config_dev_in_acquired,
+ .start_dev = __cam_fd_ctx_start_dev_in_acquired,
+ },
+ .crm_ops = {},
+ .irq_ops = NULL,
+ },
+ /* Ready */
+ {
+ .ioctl_ops = { },
+ .crm_ops = {},
+ .irq_ops = NULL,
+ },
+ /* Activated */
+ {
+ .ioctl_ops = {
+ .stop_dev = __cam_fd_ctx_stop_dev_in_activated,
+ .release_dev = __cam_fd_ctx_release_dev_in_activated,
+ .config_dev = __cam_fd_ctx_config_dev_in_activated,
+ },
+ .crm_ops = {},
+ .irq_ops = __cam_fd_ctx_handle_irq_in_activated,
+ },
+};
+
+
+int cam_fd_context_init(struct cam_fd_context *fd_ctx,
+ struct cam_context *base_ctx, struct cam_hw_mgr_intf *hw_intf)
+{
+ int rc;
+
+ if (!base_ctx || !fd_ctx) {
+ CAM_ERR(CAM_FD, "Invalid Context %pK %pK", base_ctx, fd_ctx);
+ return -EINVAL;
+ }
+
+ memset(fd_ctx, 0, sizeof(*fd_ctx));
+
+ rc = cam_context_init(base_ctx, 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;
+ }
+
+ fd_ctx->base = base_ctx;
+ base_ctx->ctx_priv = fd_ctx;
+ base_ctx->state_machine = cam_fd_ctx_state_machine;
+
+ return rc;
+}
+
+int cam_fd_context_deinit(struct cam_fd_context *fd_ctx)
+{
+ int rc = 0;
+
+ if (!fd_ctx || !fd_ctx->base) {
+ CAM_ERR(CAM_FD, "Invalid inputs %pK", fd_ctx);
+ return -EINVAL;
+ }
+
+ rc = cam_context_deinit(fd_ctx->base);
+ if (rc)
+ CAM_ERR(CAM_FD, "Error in base deinit, rc=%d", rc);
+
+ memset(fd_ctx, 0, sizeof(*fd_ctx));
+
+ 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
new file mode 100644
index 0000000..6aa5edb
--- /dev/null
+++ b/drivers/media/platform/msm/camera/cam_fd/cam_fd_context.h
@@ -0,0 +1,36 @@
+/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _CAM_FD_CONTEXT_H_
+#define _CAM_FD_CONTEXT_H_
+
+#include "cam_context.h"
+#include "cam_context_utils.h"
+#include "cam_hw_mgr_intf.h"
+#include "cam_req_mgr_interface.h"
+
+/**
+ * struct cam_fd_context - Face Detection context information
+ *
+ * @base : Base context pointer for this FD context
+ * @req_base : List of base requests for this FD context
+ */
+struct cam_fd_context {
+ struct cam_context *base;
+ struct cam_ctx_request req_base[CAM_CTX_REQ_MAX];
+};
+
+int cam_fd_context_init(struct cam_fd_context *fd_ctx,
+ struct cam_context *base_ctx, struct cam_hw_mgr_intf *hw_intf);
+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
new file mode 100644
index 0000000..27f5518
--- /dev/null
+++ b/drivers/media/platform/msm/camera/cam_fd/cam_fd_dev.c
@@ -0,0 +1,205 @@
+/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/device.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+
+#include "cam_subdev.h"
+#include "cam_node.h"
+#include "cam_fd_context.h"
+#include "cam_fd_hw_mgr.h"
+#include "cam_fd_hw_mgr_intf.h"
+
+#define CAM_FD_DEV_NAME "cam-fd"
+
+/**
+ * struct cam_fd_dev - FD device information
+ *
+ * @sd: Subdev information
+ * @base_ctx: List of base contexts
+ * @fd_ctx: List of FD contexts
+ * @lock: Mutex handle
+ * @open_cnt: FD subdev open count
+ * @probe_done: Whether FD probe is completed
+ */
+struct cam_fd_dev {
+ struct cam_subdev sd;
+ struct cam_context base_ctx[CAM_CTX_MAX];
+ struct cam_fd_context fd_ctx[CAM_CTX_MAX];
+ struct mutex lock;
+ uint32_t open_cnt;
+ bool probe_done;
+};
+
+static struct cam_fd_dev g_fd_dev;
+
+static int cam_fd_dev_open(struct v4l2_subdev *sd,
+ struct v4l2_subdev_fh *fh)
+{
+ struct cam_fd_dev *fd_dev = &g_fd_dev;
+
+ if (!fd_dev->probe_done) {
+ CAM_ERR(CAM_FD, "FD Dev not initialized, fd_dev=%pK", fd_dev);
+ return -ENODEV;
+ }
+
+ mutex_lock(&fd_dev->lock);
+ fd_dev->open_cnt++;
+ CAM_DBG(CAM_FD, "FD Subdev open count %d", fd_dev->open_cnt);
+ mutex_unlock(&fd_dev->lock);
+
+ return 0;
+}
+
+static int cam_fd_dev_close(struct v4l2_subdev *sd,
+ struct v4l2_subdev_fh *fh)
+{
+ struct cam_fd_dev *fd_dev = &g_fd_dev;
+
+ if (!fd_dev->probe_done) {
+ CAM_ERR(CAM_FD, "FD Dev not initialized, fd_dev=%pK", fd_dev);
+ return -ENODEV;
+ }
+
+ mutex_lock(&fd_dev->lock);
+ fd_dev->open_cnt--;
+ CAM_DBG(CAM_FD, "FD Subdev open count %d", fd_dev->open_cnt);
+ mutex_unlock(&fd_dev->lock);
+
+ return 0;
+}
+
+static const struct v4l2_subdev_internal_ops cam_fd_subdev_internal_ops = {
+ .open = cam_fd_dev_open,
+ .close = cam_fd_dev_close,
+};
+
+static int cam_fd_dev_probe(struct platform_device *pdev)
+{
+ int rc;
+ int i;
+ struct cam_hw_mgr_intf hw_mgr_intf;
+ struct cam_node *node;
+
+ g_fd_dev.sd.internal_ops = &cam_fd_subdev_internal_ops;
+
+ /* Initialze the v4l2 subdevice first. (create cam_node) */
+ rc = cam_subdev_probe(&g_fd_dev.sd, pdev, CAM_FD_DEV_NAME,
+ CAM_FD_DEVICE_TYPE);
+ if (rc) {
+ CAM_ERR(CAM_FD, "FD cam_subdev_probe failed, rc=%d", rc);
+ return rc;
+ }
+ node = (struct cam_node *) g_fd_dev.sd.token;
+
+ rc = cam_fd_hw_mgr_init(pdev->dev.of_node, &hw_mgr_intf);
+ if (rc) {
+ CAM_ERR(CAM_FD, "Failed in initializing FD HW manager, rc=%d",
+ rc);
+ goto unregister_subdev;
+ }
+
+ 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);
+ if (rc) {
+ CAM_ERR(CAM_FD, "FD context init failed i=%d, rc=%d",
+ i, rc);
+ goto deinit_ctx;
+ }
+ }
+
+ rc = cam_node_init(node, &hw_mgr_intf, g_fd_dev.base_ctx, CAM_CTX_MAX,
+ CAM_FD_DEV_NAME);
+ if (rc) {
+ CAM_ERR(CAM_FD, "FD node init failed, rc=%d", rc);
+ goto deinit_ctx;
+ }
+
+ mutex_init(&g_fd_dev.lock);
+ g_fd_dev.probe_done = true;
+
+ CAM_DBG(CAM_FD, "Camera FD probe complete");
+
+ return 0;
+
+deinit_ctx:
+ for (--i; i >= 0; i--) {
+ if (cam_fd_context_deinit(&g_fd_dev.fd_ctx[i]))
+ CAM_ERR(CAM_FD, "FD context %d deinit failed", i);
+ }
+unregister_subdev:
+ if (cam_subdev_remove(&g_fd_dev.sd))
+ CAM_ERR(CAM_FD, "Failed in subdev remove");
+
+ return rc;
+}
+
+static int cam_fd_dev_remove(struct platform_device *pdev)
+{
+ int i, rc;
+
+ for (i = 0; i < CAM_CTX_MAX; i++) {
+ rc = cam_fd_context_deinit(&g_fd_dev.fd_ctx[i]);
+ if (rc)
+ CAM_ERR(CAM_FD, "FD context %d deinit failed, rc=%d",
+ i, rc);
+ }
+
+ rc = cam_fd_hw_mgr_deinit(pdev->dev.of_node);
+ if (rc)
+ CAM_ERR(CAM_FD, "Failed in hw mgr deinit, rc=%d", rc);
+
+ rc = cam_subdev_remove(&g_fd_dev.sd);
+ if (rc)
+ CAM_ERR(CAM_FD, "Unregister failed, rc=%d", rc);
+
+ mutex_destroy(&g_fd_dev.lock);
+ g_fd_dev.probe_done = false;
+
+ return rc;
+}
+
+static const struct of_device_id cam_fd_dt_match[] = {
+ {
+ .compatible = "qcom,cam-fd"
+ },
+ {}
+};
+
+static struct platform_driver cam_fd_driver = {
+ .probe = cam_fd_dev_probe,
+ .remove = cam_fd_dev_remove,
+ .driver = {
+ .name = "cam_fd",
+ .owner = THIS_MODULE,
+ .of_match_table = cam_fd_dt_match,
+ },
+};
+
+static int __init cam_fd_dev_init_module(void)
+{
+ return platform_driver_register(&cam_fd_driver);
+}
+
+static void __exit cam_fd_dev_exit_module(void)
+{
+ platform_driver_unregister(&cam_fd_driver);
+}
+
+module_init(cam_fd_dev_init_module);
+module_exit(cam_fd_dev_exit_module);
+MODULE_DESCRIPTION("MSM FD driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/media/platform/msm/camera/cam_fd/fd_hw_mgr/Makefile b/drivers/media/platform/msm/camera/cam_fd/fd_hw_mgr/Makefile
new file mode 100644
index 0000000..e7478cb
--- /dev/null
+++ b/drivers/media/platform/msm/camera/cam_fd/fd_hw_mgr/Makefile
@@ -0,0 +1,14 @@
+ccflags-y += -Idrivers/media/platform/msm/camera/cam_utils
+ccflags-y += -Idrivers/media/platform/msm/camera/cam_req_mgr
+ccflags-y += -Idrivers/media/platform/msm/camera/cam_core
+ccflags-y += -Idrivers/media/platform/msm/camera/cam_sync
+ccflags-y += -Idrivers/media/platform/msm/camera/cam_smmu
+ccflags-y += -Idrivers/media/platform/msm/camera/cam_cdm
+ccflags-y += -Idrivers/media/platform/msm/camera/cam_fd
+ccflags-y += -Idrivers/media/platform/msm/camera/cam_fd/fd_hw_mgr
+ccflags-y += -Idrivers/media/platform/msm/camera/cam_fd/fd_hw_mgr/fd_hw
+ccflags-y += -Idrivers/media/platform/msm/camera
+ccflags-y += -Idrivers/media/platform/msm/camera/cam_cpas/include
+
+obj-$(CONFIG_SPECTRA_CAMERA) += fd_hw/
+obj-$(CONFIG_SPECTRA_CAMERA) += cam_fd_hw_mgr.o
diff --git a/drivers/media/platform/msm/camera/cam_fd/fd_hw_mgr/cam_fd_hw_mgr.c b/drivers/media/platform/msm/camera/cam_fd/fd_hw_mgr/cam_fd_hw_mgr.c
new file mode 100644
index 0000000..d226e17
--- /dev/null
+++ b/drivers/media/platform/msm/camera/cam_fd/fd_hw_mgr/cam_fd_hw_mgr.c
@@ -0,0 +1,1663 @@
+/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <media/cam_cpas.h>
+#include <media/cam_req_mgr.h>
+
+#include "cam_io_util.h"
+#include "cam_soc_util.h"
+#include "cam_mem_mgr_api.h"
+#include "cam_smmu_api.h"
+#include "cam_packet_util.h"
+#include "cam_fd_context.h"
+#include "cam_fd_hw_intf.h"
+#include "cam_fd_hw_core.h"
+#include "cam_fd_hw_soc.h"
+#include "cam_fd_hw_mgr_intf.h"
+#include "cam_fd_hw_mgr.h"
+
+static struct cam_fd_hw_mgr g_fd_hw_mgr;
+
+static int cam_fd_mgr_util_packet_validate(struct cam_packet *packet)
+{
+ struct cam_cmd_buf_desc *cmd_desc = NULL;
+ int i, rc;
+
+ if (!packet)
+ return -EINVAL;
+
+ CAM_DBG(CAM_FD, "Packet request=%d, op_code=0x%x, size=%d, flags=%d",
+ packet->header.request_id, packet->header.op_code,
+ packet->header.size, packet->header.flags);
+ CAM_DBG(CAM_FD,
+ "Packet cmdbuf(offset=%d, num=%d) io(offset=%d, num=%d)",
+ packet->cmd_buf_offset, packet->num_cmd_buf,
+ packet->io_configs_offset, packet->num_io_configs);
+ CAM_DBG(CAM_FD,
+ "Packet Patch(offset=%d, num=%d) kmd(offset=%d, num=%d)",
+ packet->patch_offset, packet->num_patches,
+ packet->kmd_cmd_buf_offset, packet->kmd_cmd_buf_index);
+
+ if (cam_packet_util_validate_packet(packet)) {
+ CAM_ERR(CAM_FD, "invalid packet:%d %d %d %d %d",
+ packet->kmd_cmd_buf_index,
+ packet->num_cmd_buf, packet->cmd_buf_offset,
+ packet->io_configs_offset, packet->header.size);
+ return -EINVAL;
+ }
+
+ /* All buffers must come through io config, do not support patching */
+ if (packet->num_patches || !packet->num_io_configs) {
+ CAM_ERR(CAM_FD, "wrong number of cmd/patch info: %u %u",
+ packet->num_cmd_buf, packet->num_patches);
+ return -EINVAL;
+ }
+
+ /* KMD Buf index can never be greater than or equal to num cmd bufs */
+ if (packet->kmd_cmd_buf_index >= packet->num_cmd_buf) {
+ CAM_ERR(CAM_FD, "Invalid kmd index %d (%d)",
+ packet->kmd_cmd_buf_index, packet->num_cmd_buf);
+ return -EINVAL;
+ }
+
+ if ((packet->header.op_code & 0xff) !=
+ CAM_PACKET_OPCODES_FD_FRAME_UPDATE) {
+ CAM_ERR(CAM_FD, "Invalid op_code %u",
+ packet->header.op_code & 0xff);
+ return -EINVAL;
+ }
+
+ cmd_desc = (struct cam_cmd_buf_desc *) ((uint8_t *)&packet->payload +
+ packet->cmd_buf_offset);
+
+ for (i = 0; i < packet->num_cmd_buf; i++) {
+ /*
+ * We can allow 0 length cmd buffer. This can happen in case
+ * umd gives an empty cmd buffer as kmd buffer
+ */
+ if (!cmd_desc[i].length)
+ continue;
+
+ if ((cmd_desc[i].meta_data != CAM_FD_CMD_BUFFER_ID_GENERIC) &&
+ (cmd_desc[i].meta_data != CAM_FD_CMD_BUFFER_ID_CDM)) {
+ CAM_ERR(CAM_FD, "Invalid meta_data [%d] %u", i,
+ cmd_desc[i].meta_data);
+ return -EINVAL;
+ }
+
+ CAM_DBG(CAM_FD,
+ "CmdBuf[%d] hdl=%d, offset=%d, size=%d, len=%d, type=%d, meta_data=%d",
+ i,
+ cmd_desc[i].mem_handle, cmd_desc[i].offset,
+ cmd_desc[i].size, cmd_desc[i].length, cmd_desc[i].type,
+ cmd_desc[i].meta_data);
+
+ rc = cam_packet_util_validate_cmd_desc(&cmd_desc[i]);
+ if (rc) {
+ CAM_ERR(CAM_FD, "Invalid cmd buffer %d", i);
+ return rc;
+ }
+ }
+
+ return 0;
+}
+
+static int cam_fd_mgr_util_put_ctx(
+ struct list_head *src_list,
+ struct cam_fd_hw_mgr_ctx **fd_ctx)
+{
+ int rc = 0;
+ struct cam_fd_hw_mgr_ctx *ctx_ptr = NULL;
+
+ mutex_lock(&g_fd_hw_mgr.ctx_mutex);
+ ctx_ptr = *fd_ctx;
+ if (ctx_ptr)
+ list_add_tail(&ctx_ptr->list, src_list);
+ *fd_ctx = NULL;
+ mutex_unlock(&g_fd_hw_mgr.ctx_mutex);
+
+ return rc;
+}
+
+static int cam_fd_mgr_util_get_ctx(
+ struct list_head *src_list,
+ struct cam_fd_hw_mgr_ctx **fd_ctx)
+{
+ int rc = 0;
+ struct cam_fd_hw_mgr_ctx *ctx_ptr = NULL;
+
+ mutex_lock(&g_fd_hw_mgr.ctx_mutex);
+ if (!list_empty(src_list)) {
+ ctx_ptr = list_first_entry(src_list,
+ struct cam_fd_hw_mgr_ctx, list);
+ list_del_init(&ctx_ptr->list);
+ } else {
+ CAM_ERR(CAM_FD, "No more free fd hw mgr ctx");
+ rc = -1;
+ }
+ *fd_ctx = ctx_ptr;
+ mutex_unlock(&g_fd_hw_mgr.ctx_mutex);
+
+ return rc;
+}
+
+static int cam_fd_mgr_util_put_frame_req(
+ struct list_head *src_list,
+ struct cam_fd_mgr_frame_request **frame_req)
+{
+ int rc = 0;
+ struct cam_fd_mgr_frame_request *req_ptr = NULL;
+
+ mutex_lock(&g_fd_hw_mgr.frame_req_mutex);
+ req_ptr = *frame_req;
+ if (req_ptr)
+ list_add_tail(&req_ptr->list, src_list);
+ *frame_req = NULL;
+ mutex_unlock(&g_fd_hw_mgr.frame_req_mutex);
+
+ return rc;
+}
+
+static int cam_fd_mgr_util_get_frame_req(
+ struct list_head *src_list,
+ struct cam_fd_mgr_frame_request **frame_req)
+{
+ int rc = 0;
+ struct cam_fd_mgr_frame_request *req_ptr = NULL;
+
+ mutex_lock(&g_fd_hw_mgr.frame_req_mutex);
+ if (!list_empty(src_list)) {
+ req_ptr = list_first_entry(src_list,
+ struct cam_fd_mgr_frame_request, list);
+ list_del_init(&req_ptr->list);
+ } else {
+ CAM_DBG(CAM_FD, "Frame req not available");
+ rc = -EPERM;
+ }
+ *frame_req = req_ptr;
+ mutex_unlock(&g_fd_hw_mgr.frame_req_mutex);
+
+ return rc;
+}
+
+static int cam_fd_mgr_util_get_device(struct cam_fd_hw_mgr *hw_mgr,
+ struct cam_fd_hw_mgr_ctx *hw_ctx, struct cam_fd_device **hw_device)
+{
+ if (!hw_mgr || !hw_ctx || !hw_device) {
+ CAM_ERR(CAM_FD, "Invalid input %pK %pK %pK", hw_mgr, hw_ctx,
+ hw_device);
+ return -EINVAL;
+ }
+
+ if ((hw_ctx->device_index < 0) ||
+ (hw_ctx->device_index >= CAM_FD_HW_MAX)) {
+ CAM_ERR(CAM_FD, "Invalid device indx %d", hw_ctx->device_index);
+ return -EINVAL;
+ }
+
+ CAM_DBG(CAM_FD, "ctx index=%d, hw_ctx=%d", hw_ctx->ctx_index,
+ hw_ctx->device_index);
+
+ *hw_device = &hw_mgr->hw_device[hw_ctx->device_index];
+
+ return 0;
+}
+
+static int cam_fd_mgr_util_release_device(struct cam_fd_hw_mgr *hw_mgr,
+ struct cam_fd_hw_mgr_ctx *hw_ctx)
+{
+ struct cam_fd_device *hw_device;
+ struct cam_fd_hw_release_args hw_release_args;
+ int rc;
+
+ rc = cam_fd_mgr_util_get_device(hw_mgr, hw_ctx, &hw_device);
+ if (rc) {
+ CAM_ERR(CAM_FD, "Error in getting device %d", rc);
+ return rc;
+ }
+
+ if (hw_device->hw_intf->hw_ops.release) {
+ hw_release_args.hw_ctx = hw_ctx;
+ hw_release_args.ctx_hw_private = hw_ctx->ctx_hw_private;
+ rc = hw_device->hw_intf->hw_ops.release(
+ hw_device->hw_intf->hw_priv, &hw_release_args,
+ sizeof(hw_release_args));
+ if (rc) {
+ CAM_ERR(CAM_FD, "Failed in HW release %d", rc);
+ return rc;
+ }
+ } else {
+ CAM_ERR(CAM_FD, "Invalid release function");
+ }
+
+ mutex_lock(&hw_mgr->hw_mgr_mutex);
+ hw_device->num_ctxts--;
+ mutex_unlock(&hw_mgr->hw_mgr_mutex);
+
+ hw_ctx->device_index = -1;
+
+ return rc;
+}
+
+static int cam_fd_mgr_util_select_device(struct cam_fd_hw_mgr *hw_mgr,
+ struct cam_fd_hw_mgr_ctx *hw_ctx,
+ struct cam_fd_acquire_dev_info *fd_acquire_args)
+{
+ int i, rc;
+ struct cam_fd_hw_reserve_args hw_reserve_args;
+ struct cam_fd_device *hw_device = NULL;
+
+ if (!hw_mgr || !hw_ctx || !fd_acquire_args) {
+ CAM_ERR(CAM_FD, "Invalid input %pK %pK %pK", hw_mgr, hw_ctx,
+ fd_acquire_args);
+ return -EINVAL;
+ }
+
+ mutex_lock(&hw_mgr->hw_mgr_mutex);
+
+ /* Check if a device is free which can satisfy the requirements */
+ for (i = 0; i < hw_mgr->num_devices; i++) {
+ hw_device = &hw_mgr->hw_device[i];
+ CAM_DBG(CAM_FD,
+ "[%d] : num_ctxts=%d, modes=%d, raw_results=%d",
+ i, hw_device->num_ctxts,
+ hw_device->hw_caps.supported_modes,
+ hw_device->hw_caps.raw_results_available);
+ if ((hw_device->num_ctxts == 0) &&
+ (fd_acquire_args->mode &
+ hw_device->hw_caps.supported_modes) &&
+ (!fd_acquire_args->get_raw_results ||
+ hw_device->hw_caps.raw_results_available)) {
+ CAM_DBG(CAM_FD, "Found dedicated HW Index=%d", i);
+ hw_device->num_ctxts++;
+ break;
+ }
+ }
+
+ /*
+ * We couldn't find a free HW which meets requirement, now check if
+ * there is a HW which meets acquire requirements
+ */
+ if (i == hw_mgr->num_devices) {
+ for (i = 0; i < hw_mgr->num_devices; i++) {
+ hw_device = &hw_mgr->hw_device[i];
+ if ((fd_acquire_args->mode &
+ hw_device->hw_caps.supported_modes) &&
+ (!fd_acquire_args->get_raw_results ||
+ hw_device->hw_caps.raw_results_available)) {
+ hw_device->num_ctxts++;
+ CAM_DBG(CAM_FD, "Found sharing HW Index=%d", i);
+ break;
+ }
+ }
+ }
+
+ mutex_unlock(&hw_mgr->hw_mgr_mutex);
+
+ if ((i == hw_mgr->num_devices) || !hw_device) {
+ CAM_ERR(CAM_FD, "Couldn't acquire HW %d %d",
+ fd_acquire_args->mode,
+ fd_acquire_args->get_raw_results);
+ return -EBUSY;
+ }
+
+ CAM_DBG(CAM_FD, "Device index %d selected for this acquire", i);
+
+ /* Check if we can reserve this HW */
+ if (hw_device->hw_intf->hw_ops.reserve) {
+ hw_reserve_args.hw_ctx = hw_ctx;
+ hw_reserve_args.mode = fd_acquire_args->mode;
+ rc = hw_device->hw_intf->hw_ops.reserve(
+ hw_device->hw_intf->hw_priv, &hw_reserve_args,
+ sizeof(hw_reserve_args));
+ if (rc) {
+ CAM_ERR(CAM_FD, "Failed in HW reserve %d", rc);
+ return rc;
+ }
+ hw_ctx->ctx_hw_private = hw_reserve_args.ctx_hw_private;
+ } else {
+ CAM_ERR(CAM_FD, "Invalid reserve function");
+ return -EPERM;
+ }
+
+ /* Update required info in hw context */
+ hw_ctx->device_index = i;
+
+ CAM_DBG(CAM_FD, "ctx index=%d, hw_ctx=%d", hw_ctx->ctx_index,
+ hw_ctx->device_index);
+
+ return 0;
+}
+
+static int cam_fd_mgr_util_pdev_get_hw_intf(struct device_node *of_node,
+ int i, struct cam_hw_intf **device_hw_intf)
+{
+ struct device_node *device_node = NULL;
+ struct platform_device *child_pdev = NULL;
+ struct cam_hw_intf *hw_intf = NULL;
+ const char *name = NULL;
+ int rc;
+
+ rc = of_property_read_string_index(of_node, "compat-hw-name", i, &name);
+ if (rc) {
+ CAM_ERR(CAM_FD, "Getting dev object name failed %d %d", i, rc);
+ goto put_node;
+ }
+
+ device_node = of_find_node_by_name(NULL, name);
+ if (!device_node) {
+ CAM_ERR(CAM_FD, "Cannot find node in dtsi %s", name);
+ return -ENODEV;
+ }
+
+ child_pdev = of_find_device_by_node(device_node);
+ if (!child_pdev) {
+ CAM_ERR(CAM_FD, "Failed to find device on bus %s",
+ device_node->name);
+ rc = -ENODEV;
+ goto put_node;
+ }
+
+ hw_intf = (struct cam_hw_intf *)platform_get_drvdata(child_pdev);
+ if (!hw_intf) {
+ CAM_ERR(CAM_FD, "No driver data for child device");
+ rc = -ENODEV;
+ goto put_node;
+ }
+
+ CAM_DBG(CAM_FD, "child type %d index %d child_intf %pK",
+ hw_intf->hw_type, hw_intf->hw_idx, hw_intf);
+
+ if (hw_intf->hw_idx >= CAM_FD_HW_MAX) {
+ CAM_ERR(CAM_FD, "hw_idx invalid %d", hw_intf->hw_idx);
+ rc = -ENODEV;
+ goto put_node;
+ }
+
+ rc = 0;
+ *device_hw_intf = hw_intf;
+
+put_node:
+ of_node_put(device_node);
+
+ return rc;
+}
+
+static int cam_fd_packet_generic_blob_handler(void *user_data,
+ uint32_t blob_type, uint32_t blob_size, uint8_t *blob_data)
+{
+ struct cam_fd_hw_cmd_prestart_args *prestart_args =
+ (struct cam_fd_hw_cmd_prestart_args *)user_data;
+
+ if (!blob_data || (blob_size == 0)) {
+ CAM_ERR(CAM_FD, "Invalid blob info %pK %d", blob_data,
+ blob_size);
+ return -EINVAL;
+ }
+
+ if (!prestart_args) {
+ CAM_ERR(CAM_FD, "Invalid user data");
+ return -EINVAL;
+ }
+
+ switch (blob_type) {
+ case CAM_FD_BLOB_TYPE_RAW_RESULTS_REQUIRED: {
+ uint32_t *get_raw_results = (uint32_t *)blob_data;
+
+ if (sizeof(uint32_t) != blob_size) {
+ CAM_ERR(CAM_FD, "Invalid blob size %d %d",
+ sizeof(uint32_t), blob_size);
+ return -EINVAL;
+ }
+
+ prestart_args->get_raw_results = *get_raw_results;
+ break;
+ }
+ case CAM_FD_BLOB_TYPE_SOC_CLOCK_BW_REQUEST: {
+ struct cam_fd_soc_clock_bw_request *clk_req =
+ (struct cam_fd_soc_clock_bw_request *)blob_data;
+
+ if (sizeof(struct cam_fd_soc_clock_bw_request) != blob_size) {
+ CAM_ERR(CAM_FD, "Invalid blob size %d %d",
+ sizeof(struct cam_fd_soc_clock_bw_request),
+ blob_size);
+ return -EINVAL;
+ }
+
+ CAM_DBG(CAM_FD, "SOC Clk Request clock=%lld, bw=%lld",
+ clk_req->clock_rate, clk_req->bandwidth);
+
+ break;
+ }
+ default:
+ CAM_WARN(CAM_FD, "Unknown blob type %d", blob_type);
+ break;
+ }
+
+ return 0;
+}
+
+static int cam_fd_mgr_util_parse_generic_cmd_buffer(
+ struct cam_fd_hw_mgr_ctx *hw_ctx, struct cam_packet *packet,
+ struct cam_fd_hw_cmd_prestart_args *prestart_args)
+{
+ struct cam_cmd_buf_desc *cmd_desc = NULL;
+ int i, rc = 0;
+
+ cmd_desc = (struct cam_cmd_buf_desc *) ((uint8_t *)&packet->payload +
+ packet->cmd_buf_offset);
+
+ for (i = 0; i < packet->num_cmd_buf; i++) {
+ if (!cmd_desc[i].length)
+ continue;
+
+ if (cmd_desc[i].meta_data == CAM_FD_CMD_BUFFER_ID_CDM)
+ continue;
+
+ rc = cam_packet_util_validate_cmd_desc(&cmd_desc[i]);
+ if (rc)
+ return rc;
+
+ rc = cam_packet_util_process_generic_cmd_buffer(&cmd_desc[i],
+ cam_fd_packet_generic_blob_handler, prestart_args);
+ if (rc)
+ CAM_ERR(CAM_FD, "Failed in processing blobs %d", rc);
+
+ break;
+ }
+
+ return rc;
+}
+
+static int cam_fd_mgr_util_prepare_io_buf_info(int32_t iommu_hdl,
+ struct cam_hw_prepare_update_args *prepare,
+ struct cam_fd_hw_io_buffer *input_buf,
+ struct cam_fd_hw_io_buffer *output_buf, uint32_t io_buf_size)
+{
+ int rc = -EINVAL;
+ uint32_t i, j, plane, num_out_buf, num_in_buf;
+ struct cam_buf_io_cfg *io_cfg;
+ uint64_t io_addr[CAM_PACKET_MAX_PLANES];
+ uint64_t cpu_addr[CAM_PACKET_MAX_PLANES];
+ size_t size;
+
+ /* Get IO Buf information */
+ num_out_buf = 0;
+ num_in_buf = 0;
+ io_cfg = (struct cam_buf_io_cfg *) ((uint8_t *)
+ &prepare->packet->payload + prepare->packet->io_configs_offset);
+
+ for (i = 0; i < prepare->packet->num_io_configs; i++) {
+ CAM_DBG(CAM_FD,
+ "IOConfig[%d] : handle[%d] Dir[%d] Res[%d] Fence[%d], Format[%d]",
+ i, io_cfg[i].mem_handle[0], io_cfg[i].direction,
+ io_cfg[i].resource_type,
+ io_cfg[i].fence, io_cfg[i].format);
+
+ if ((num_in_buf >= io_buf_size) ||
+ (num_out_buf >= io_buf_size)) {
+ CAM_ERR(CAM_FD, "Invalid number of buffers %d %d %d",
+ num_in_buf, num_out_buf, io_buf_size);
+ return -EINVAL;
+ }
+
+ memset(io_addr, 0x0, sizeof(io_addr));
+ for (plane = 0; plane < CAM_PACKET_MAX_PLANES; plane++) {
+ if (!io_cfg[i].mem_handle[plane])
+ break;
+
+ rc = cam_mem_get_io_buf(io_cfg[i].mem_handle[plane],
+ iommu_hdl, &io_addr[plane], &size);
+ if ((rc) || (io_addr[plane] >> 32)) {
+ CAM_ERR(CAM_FD, "Invalid io addr for %d %d",
+ plane, rc);
+ return -ENOMEM;
+ }
+
+ /*
+ * Buffers may be accessed by CPU as well, we do not
+ * know at this point, so get both and send to HW layer
+ */
+ rc = cam_mem_get_cpu_buf(io_cfg[i].mem_handle[plane],
+ &cpu_addr[plane], &size);
+ if (rc) {
+ CAM_ERR(CAM_FD, "unable to get buf address");
+ return rc;
+ }
+
+ io_addr[plane] += io_cfg[i].offsets[plane];
+ cpu_addr[plane] += io_cfg[i].offsets[plane];
+
+ CAM_DBG(CAM_FD, "IO Address[%d][%d] : %pK, %pK",
+ io_cfg[i].direction, plane, io_addr[plane],
+ cpu_addr[plane]);
+ }
+
+ switch (io_cfg[i].direction) {
+ case CAM_BUF_INPUT: {
+ prepare->in_map_entries[num_in_buf].resource_handle =
+ io_cfg[i].resource_type;
+ prepare->in_map_entries[num_in_buf].sync_id =
+ io_cfg[i].fence;
+
+ input_buf[num_in_buf].valid = true;
+ for (j = 0; j < plane; j++) {
+ input_buf[num_in_buf].io_addr[j] = io_addr[j];
+ input_buf[num_in_buf].cpu_addr[j] = cpu_addr[j];
+ }
+ input_buf[num_in_buf].num_buf = plane;
+ input_buf[num_in_buf].io_cfg = &io_cfg[i];
+
+ num_in_buf++;
+ break;
+ }
+ case CAM_BUF_OUTPUT: {
+ prepare->out_map_entries[num_out_buf].resource_handle =
+ io_cfg[i].resource_type;
+ prepare->out_map_entries[num_out_buf].sync_id =
+ io_cfg[i].fence;
+
+ output_buf[num_out_buf].valid = true;
+ for (j = 0; j < plane; j++) {
+ output_buf[num_out_buf].io_addr[j] = io_addr[j];
+ output_buf[num_out_buf].cpu_addr[j] =
+ cpu_addr[j];
+ }
+ output_buf[num_out_buf].num_buf = plane;
+ output_buf[num_out_buf].io_cfg = &io_cfg[i];
+
+ num_out_buf++;
+ break;
+ }
+ default:
+ CAM_ERR(CAM_FD, "Unsupported io direction %d",
+ io_cfg[i].direction);
+ return -EINVAL;
+ }
+ }
+
+ prepare->num_in_map_entries = num_in_buf;
+ prepare->num_out_map_entries = num_out_buf;
+
+ return 0;
+}
+
+static int cam_fd_mgr_util_prepare_hw_update_entries(
+ struct cam_fd_hw_mgr *hw_mgr,
+ struct cam_hw_prepare_update_args *prepare,
+ struct cam_fd_hw_cmd_prestart_args *prestart_args,
+ struct cam_kmd_buf_info *kmd_buf_info)
+{
+ int i, rc;
+ struct cam_hw_update_entry *hw_entry;
+ uint32_t num_ent;
+ struct cam_fd_hw_mgr_ctx *hw_ctx =
+ (struct cam_fd_hw_mgr_ctx *)prepare->ctxt_to_hw_map;
+ struct cam_fd_device *hw_device;
+ uint32_t kmd_buf_max_size, kmd_buf_used_bytes = 0;
+ uint32_t *kmd_buf_addr;
+ struct cam_cmd_buf_desc *cmd_desc = NULL;
+
+ rc = cam_fd_mgr_util_get_device(hw_mgr, hw_ctx, &hw_device);
+ if (rc) {
+ CAM_ERR(CAM_FD, "Error in getting device %d", rc);
+ return rc;
+ }
+
+ kmd_buf_addr = (uint32_t *)((uint8_t *)kmd_buf_info->cpu_addr +
+ kmd_buf_info->used_bytes);
+ kmd_buf_max_size = kmd_buf_info->size - kmd_buf_info->used_bytes;
+
+ prestart_args->cmd_buf_addr = kmd_buf_addr;
+ prestart_args->size = kmd_buf_max_size;
+ prestart_args->pre_config_buf_size = 0;
+ prestart_args->post_config_buf_size = 0;
+
+ if (hw_device->hw_intf->hw_ops.process_cmd) {
+ rc = hw_device->hw_intf->hw_ops.process_cmd(
+ hw_device->hw_intf->hw_priv, CAM_FD_HW_CMD_PRESTART,
+ prestart_args,
+ sizeof(struct cam_fd_hw_cmd_prestart_args));
+ if (rc) {
+ CAM_ERR(CAM_FD, "Failed in CMD_PRESTART %d", rc);
+ return rc;
+ }
+ }
+
+ kmd_buf_used_bytes += prestart_args->pre_config_buf_size;
+ kmd_buf_used_bytes += prestart_args->post_config_buf_size;
+
+ /* HW layer is expected to add commands */
+ if (!kmd_buf_used_bytes || (kmd_buf_used_bytes > kmd_buf_max_size)) {
+ CAM_ERR(CAM_FD, "Invalid kmd used bytes %d (%d)",
+ kmd_buf_used_bytes, kmd_buf_max_size);
+ return -ENOMEM;
+ }
+
+ hw_entry = prepare->hw_update_entries;
+ num_ent = 0;
+
+ if (prestart_args->pre_config_buf_size) {
+ if ((num_ent + 1) >= prepare->max_hw_update_entries) {
+ CAM_ERR(CAM_FD, "Insufficient HW entries :%d %d",
+ num_ent, prepare->max_hw_update_entries);
+ return -EINVAL;
+ }
+
+ hw_entry[num_ent].handle = kmd_buf_info->handle;
+ hw_entry[num_ent].len = prestart_args->pre_config_buf_size;
+ hw_entry[num_ent].offset = kmd_buf_info->offset;
+
+ kmd_buf_info->used_bytes += prestart_args->pre_config_buf_size;
+ kmd_buf_info->offset += prestart_args->pre_config_buf_size;
+ num_ent++;
+ }
+
+ /*
+ * set the cmd_desc to point the first command descriptor in the
+ * packet and update hw entries with CDM command buffers
+ */
+ cmd_desc = (struct cam_cmd_buf_desc *)((uint8_t *)
+ &prepare->packet->payload + prepare->packet->cmd_buf_offset);
+
+ for (i = 0; i < prepare->packet->num_cmd_buf; i++) {
+ if (!cmd_desc[i].length)
+ continue;
+
+ if (cmd_desc[i].meta_data != CAM_FD_CMD_BUFFER_ID_CDM)
+ continue;
+
+ if (num_ent + 1 >= prepare->max_hw_update_entries) {
+ CAM_ERR(CAM_FD, "Insufficient HW entries :%d %d",
+ num_ent, prepare->max_hw_update_entries);
+ return -EINVAL;
+ }
+
+ hw_entry[num_ent].handle = cmd_desc[i].mem_handle;
+ hw_entry[num_ent].len = cmd_desc[i].length;
+ hw_entry[num_ent].offset = cmd_desc[i].offset;
+ num_ent++;
+ }
+
+ if (prestart_args->post_config_buf_size) {
+ if (num_ent + 1 >= prepare->max_hw_update_entries) {
+ CAM_ERR(CAM_FD, "Insufficient HW entries :%d %d",
+ num_ent, prepare->max_hw_update_entries);
+ return -EINVAL;
+ }
+
+ hw_entry[num_ent].handle = kmd_buf_info->handle;
+ hw_entry[num_ent].len = prestart_args->post_config_buf_size;
+ hw_entry[num_ent].offset = kmd_buf_info->offset;
+
+ kmd_buf_info->used_bytes += prestart_args->post_config_buf_size;
+ kmd_buf_info->offset += prestart_args->post_config_buf_size;
+
+ num_ent++;
+ }
+
+ prepare->num_hw_update_entries = num_ent;
+
+ CAM_DBG(CAM_FD, "FinalConfig : hw_entries=%d, Sync(in=%d, out=%d)",
+ prepare->num_hw_update_entries, prepare->num_in_map_entries,
+ prepare->num_out_map_entries);
+
+ return rc;
+}
+
+static int cam_fd_mgr_util_submit_frame(void *priv, void *data)
+{
+ struct cam_fd_device *hw_device;
+ struct cam_fd_hw_mgr *hw_mgr;
+ struct cam_fd_mgr_frame_request *frame_req;
+ struct cam_fd_hw_mgr_ctx *hw_ctx;
+ struct cam_fd_hw_cmd_start_args start_args;
+ int rc;
+
+ if (!priv) {
+ CAM_ERR(CAM_FD, "Invalid data");
+ return -EINVAL;
+ }
+
+ hw_mgr = (struct cam_fd_hw_mgr *)priv;
+ mutex_lock(&hw_mgr->frame_req_mutex);
+
+ /* Check if we have any frames pending in high priority list */
+ if (!list_empty(&hw_mgr->frame_pending_list_high)) {
+ CAM_DBG(CAM_FD, "Pending frames in high priority list");
+ frame_req = list_first_entry(&hw_mgr->frame_pending_list_high,
+ struct cam_fd_mgr_frame_request, list);
+ } else if (!list_empty(&hw_mgr->frame_pending_list_normal)) {
+ CAM_DBG(CAM_FD, "Pending frames in normal priority list");
+ frame_req = list_first_entry(&hw_mgr->frame_pending_list_normal,
+ struct cam_fd_mgr_frame_request, list);
+ } else {
+ mutex_unlock(&hw_mgr->frame_req_mutex);
+ CAM_DBG(CAM_FD, "No pending frames");
+ return 0;
+ }
+
+ CAM_DBG(CAM_FD, "FrameSubmit : Frame[%lld]", frame_req->request_id);
+ hw_ctx = frame_req->hw_ctx;
+ rc = cam_fd_mgr_util_get_device(hw_mgr, hw_ctx, &hw_device);
+ if (rc) {
+ mutex_unlock(&hw_mgr->frame_req_mutex);
+ CAM_ERR(CAM_FD, "Error in getting device %d", rc);
+ return rc;
+ }
+
+ mutex_lock(&hw_device->lock);
+ if (hw_device->ready_to_process == false) {
+ mutex_unlock(&hw_device->lock);
+ mutex_unlock(&hw_mgr->frame_req_mutex);
+ return -EBUSY;
+ }
+
+ list_del_init(&frame_req->list);
+ mutex_unlock(&hw_mgr->frame_req_mutex);
+
+ if (hw_device->hw_intf->hw_ops.start) {
+ start_args.hw_ctx = hw_ctx;
+ start_args.ctx_hw_private = hw_ctx->ctx_hw_private;
+ start_args.hw_req_private = &frame_req->hw_req_private;
+ start_args.hw_update_entries = frame_req->hw_update_entries;
+ start_args.num_hw_update_entries =
+ frame_req->num_hw_update_entries;
+
+ rc = hw_device->hw_intf->hw_ops.start(
+ hw_device->hw_intf->hw_priv, &start_args,
+ sizeof(start_args));
+ if (rc) {
+ CAM_ERR(CAM_FD, "Failed in HW Start %d", rc);
+ mutex_unlock(&hw_device->lock);
+ goto put_req_into_free_list;
+ }
+ } else {
+ CAM_ERR(CAM_FD, "Invalid hw_ops.start");
+ mutex_unlock(&hw_device->lock);
+ rc = -EPERM;
+ goto put_req_into_free_list;
+ }
+
+ hw_device->ready_to_process = false;
+ mutex_unlock(&hw_device->lock);
+
+ rc = cam_fd_mgr_util_put_frame_req(
+ &hw_mgr->frame_processing_list, &frame_req);
+ if (rc) {
+ CAM_ERR(CAM_FD,
+ "Failed in putting frame req in processing list");
+ goto stop_unlock;
+ }
+
+ return rc;
+
+stop_unlock:
+ if (hw_device->hw_intf->hw_ops.stop) {
+ struct cam_fd_hw_stop_args stop_args;
+
+ stop_args.hw_ctx = hw_ctx;
+ stop_args.ctx_hw_private = hw_ctx->ctx_hw_private;
+ stop_args.hw_req_private = &frame_req->hw_req_private;
+ if (hw_device->hw_intf->hw_ops.stop(
+ hw_device->hw_intf->hw_priv, &stop_args,
+ sizeof(stop_args)))
+ CAM_ERR(CAM_FD, "Failed in HW Stop %d", rc);
+ }
+put_req_into_free_list:
+ cam_fd_mgr_util_put_frame_req(&hw_mgr->frame_free_list, &frame_req);
+
+ return rc;
+}
+
+static int cam_fd_mgr_util_schedule_frame_worker_task(
+ struct cam_fd_hw_mgr *hw_mgr)
+{
+ int32_t rc = 0;
+ struct crm_workq_task *task;
+ struct cam_fd_mgr_work_data *work_data;
+
+ task = cam_req_mgr_workq_get_task(hw_mgr->work);
+ if (!task) {
+ CAM_ERR(CAM_FD, "no empty task available");
+ return -ENOMEM;
+ }
+
+ work_data = (struct cam_fd_mgr_work_data *)task->payload;
+ work_data->type = CAM_FD_WORK_FRAME;
+
+ task->process_cb = cam_fd_mgr_util_submit_frame;
+ rc = cam_req_mgr_workq_enqueue_task(task, hw_mgr, CRM_TASK_PRIORITY_0);
+
+ return rc;
+}
+
+static int32_t cam_fd_mgr_workq_irq_cb(void *priv, void *data)
+{
+ struct cam_fd_device *hw_device = NULL;
+ struct cam_fd_hw_mgr *hw_mgr;
+ struct cam_fd_mgr_work_data *work_data;
+ struct cam_fd_mgr_frame_request *frame_req = NULL;
+ enum cam_fd_hw_irq_type irq_type;
+ bool frame_abort = true;
+ int rc;
+
+ if (!data || !priv) {
+ CAM_ERR(CAM_FD, "Invalid data %pK %pK", data, priv);
+ return -EINVAL;
+ }
+
+ hw_mgr = (struct cam_fd_hw_mgr *)priv;
+ work_data = (struct cam_fd_mgr_work_data *)data;
+ irq_type = work_data->irq_type;
+
+ CAM_DBG(CAM_FD, "FD IRQ type=%d", irq_type);
+
+ if (irq_type == CAM_FD_IRQ_HALT_DONE) {
+ /* HALT would be followed by a RESET, ignore this */
+ CAM_DBG(CAM_FD, "HALT IRQ callback");
+ return 0;
+ }
+
+ /* Get the frame from processing list */
+ rc = cam_fd_mgr_util_get_frame_req(&hw_mgr->frame_processing_list,
+ &frame_req);
+ if (rc || !frame_req) {
+ /*
+ * This can happen if reset is triggered while no frames
+ * were pending, so not an error, just continue to check if
+ * there are any pending frames and submit
+ */
+ CAM_DBG(CAM_FD, "No Frame in processing list, rc=%d", rc);
+ goto submit_next_frame;
+ }
+
+ if (!frame_req->hw_ctx) {
+ CAM_ERR(CAM_FD, "Invalid Frame request %lld",
+ frame_req->request_id);
+ goto put_req_in_free_list;
+ }
+
+ rc = cam_fd_mgr_util_get_device(hw_mgr, frame_req->hw_ctx, &hw_device);
+ if (rc) {
+ CAM_ERR(CAM_FD, "Error in getting device %d", rc);
+ goto put_req_in_free_list;
+ }
+
+ /* Read frame results first */
+ if (irq_type == CAM_FD_IRQ_FRAME_DONE) {
+ struct cam_fd_hw_frame_done_args frame_done_args;
+
+ CAM_DBG(CAM_FD, "FrameDone : Frame[%lld]",
+ frame_req->request_id);
+
+ frame_done_args.hw_ctx = frame_req->hw_ctx;
+ frame_done_args.ctx_hw_private =
+ frame_req->hw_ctx->ctx_hw_private;
+ frame_done_args.request_id = frame_req->request_id;
+ frame_done_args.hw_req_private = &frame_req->hw_req_private;
+
+ if (hw_device->hw_intf->hw_ops.process_cmd) {
+ rc = hw_device->hw_intf->hw_ops.process_cmd(
+ hw_device->hw_intf->hw_priv,
+ CAM_FD_HW_CMD_FRAME_DONE,
+ &frame_done_args, sizeof(frame_done_args));
+ if (rc) {
+ CAM_ERR(CAM_FD, "Failed in CMD_PRESTART %d",
+ rc);
+ frame_abort = true;
+ goto notify_context;
+ }
+ }
+
+ frame_abort = false;
+ }
+
+notify_context:
+ /* Do a callback to inform frame done or stop done */
+ if (frame_req->hw_ctx->event_cb) {
+ struct cam_hw_done_event_data buf_data;
+
+ CAM_DBG(CAM_FD, "FrameHALT : Frame[%lld]",
+ frame_req->request_id);
+
+ buf_data.num_handles = frame_req->num_hw_update_entries;
+ buf_data.request_id = frame_req->request_id;
+
+ rc = frame_req->hw_ctx->event_cb(frame_req->hw_ctx->cb_priv,
+ frame_abort, &buf_data);
+ if (rc)
+ CAM_ERR(CAM_FD, "Error in event cb handling %d", rc);
+ }
+
+ /*
+ * Now we can set hw device is free to process further frames.
+ * Note - Do not change state to IDLE until we read the frame results,
+ * Otherwise, other thread may schedule frame processing before
+ * reading current frame's results. Also, we need to set to IDLE state
+ * in case some error happens after getting this irq callback
+ */
+ mutex_lock(&hw_device->lock);
+ hw_device->ready_to_process = true;
+ CAM_DBG(CAM_FD, "ready_to_process=%d", hw_device->ready_to_process);
+ mutex_unlock(&hw_device->lock);
+
+put_req_in_free_list:
+ rc = cam_fd_mgr_util_put_frame_req(&hw_mgr->frame_free_list,
+ &frame_req);
+ if (rc) {
+ CAM_ERR(CAM_FD, "Failed in putting frame req in free list");
+ /* continue */
+ }
+
+submit_next_frame:
+ /* Check if there are any frames pending for processing and submit */
+ rc = cam_fd_mgr_util_submit_frame(hw_mgr, NULL);
+ if (rc) {
+ CAM_ERR(CAM_FD, "Error while submit frame, rc=%d", rc);
+ return rc;
+ }
+
+ return rc;
+}
+
+static int cam_fd_mgr_irq_cb(void *data, enum cam_fd_hw_irq_type irq_type)
+{
+ struct cam_fd_hw_mgr *hw_mgr = &g_fd_hw_mgr;
+ int rc = 0;
+ unsigned long flags;
+ struct crm_workq_task *task;
+ struct cam_fd_mgr_work_data *work_data;
+
+ spin_lock_irqsave(&hw_mgr->hw_mgr_slock, flags);
+ task = cam_req_mgr_workq_get_task(hw_mgr->work);
+ if (!task) {
+ CAM_ERR(CAM_FD, "no empty task available");
+ spin_unlock_irqrestore(&hw_mgr->hw_mgr_slock, flags);
+ return -ENOMEM;
+ }
+
+ work_data = (struct cam_fd_mgr_work_data *)task->payload;
+ work_data->type = CAM_FD_WORK_IRQ;
+ work_data->irq_type = irq_type;
+
+ task->process_cb = cam_fd_mgr_workq_irq_cb;
+ rc = cam_req_mgr_workq_enqueue_task(task, hw_mgr, CRM_TASK_PRIORITY_0);
+ if (rc)
+ CAM_ERR(CAM_FD, "Failed in enqueue work task, rc=%d", rc);
+
+ spin_unlock_irqrestore(&hw_mgr->hw_mgr_slock, flags);
+
+ return rc;
+}
+
+static int cam_fd_mgr_hw_get_caps(void *hw_mgr_priv, void *hw_get_caps_args)
+{
+ int rc = 0;
+ struct cam_fd_hw_mgr *hw_mgr = hw_mgr_priv;
+ struct cam_query_cap_cmd *query = hw_get_caps_args;
+ struct cam_fd_query_cap_cmd query_fd;
+
+ if (copy_from_user(&query_fd, (void __user *)query->caps_handle,
+ sizeof(struct cam_fd_query_cap_cmd))) {
+ CAM_ERR(CAM_FD, "Failed in copy from user, rc=%d", rc);
+ return -EFAULT;
+ }
+
+ query_fd = hw_mgr->fd_caps;
+
+ CAM_DBG(CAM_FD,
+ "IOMMU device(%d, %d), CDM(%d, %d), versions %d.%d, %d.%d",
+ query_fd.device_iommu.secure, query_fd.device_iommu.non_secure,
+ query_fd.cdm_iommu.secure, query_fd.cdm_iommu.non_secure,
+ query_fd.hw_caps.core_version.major,
+ query_fd.hw_caps.core_version.minor,
+ query_fd.hw_caps.wrapper_version.major,
+ query_fd.hw_caps.wrapper_version.minor);
+
+ if (copy_to_user((void __user *)query->caps_handle, &query_fd,
+ sizeof(struct cam_fd_query_cap_cmd)))
+ rc = -EFAULT;
+
+ return rc;
+}
+
+static int cam_fd_mgr_hw_acquire(void *hw_mgr_priv, void *hw_acquire_args)
+{
+ struct cam_fd_hw_mgr *hw_mgr = (struct cam_fd_hw_mgr *)hw_mgr_priv;
+ struct cam_hw_acquire_args *acquire_args =
+ (struct cam_hw_acquire_args *)hw_acquire_args;
+ struct cam_fd_hw_mgr_ctx *hw_ctx;
+ struct cam_fd_acquire_dev_info fd_acquire_args;
+ int rc;
+
+ if (!acquire_args || acquire_args->num_acq <= 0) {
+ CAM_ERR(CAM_FD, "Invalid acquire args %pK", acquire_args);
+ return -EINVAL;
+ }
+
+ if (copy_from_user(&fd_acquire_args,
+ (void __user *)acquire_args->acquire_info,
+ sizeof(struct cam_fd_acquire_dev_info))) {
+ CAM_ERR(CAM_FD, "Copy from user failed");
+ return -EFAULT;
+ }
+
+ CAM_DBG(CAM_FD, "Acquire : mode=%d, get_raw_results=%d, priority=%d",
+ fd_acquire_args.mode, fd_acquire_args.get_raw_results,
+ fd_acquire_args.priority);
+
+ /* get a free fd hw mgr ctx */
+ rc = cam_fd_mgr_util_get_ctx(&hw_mgr->free_ctx_list, &hw_ctx);
+ if (rc || !hw_ctx) {
+ CAM_ERR(CAM_FD, "Get hw context failed, rc=%d, hw_ctx=%pK",
+ rc, hw_ctx);
+ return -EINVAL;
+ }
+
+ if (fd_acquire_args.get_raw_results && !hw_mgr->raw_results_available) {
+ CAM_ERR(CAM_FD, "HW cannot support raw results %d (%d)",
+ fd_acquire_args.get_raw_results,
+ hw_mgr->raw_results_available);
+ goto put_ctx;
+ }
+
+ if (!(fd_acquire_args.mode & hw_mgr->supported_modes)) {
+ CAM_ERR(CAM_FD, "HW cannot support requested mode 0x%x (0x%x)",
+ fd_acquire_args.mode, hw_mgr->supported_modes);
+ rc = -EPERM;
+ goto put_ctx;
+ }
+
+ rc = cam_fd_mgr_util_select_device(hw_mgr, hw_ctx, &fd_acquire_args);
+ if (rc) {
+ CAM_ERR(CAM_FD, "Failed in selecting device, rc=%d", rc);
+ goto put_ctx;
+ }
+
+ hw_ctx->ctx_in_use = true;
+ hw_ctx->hw_mgr = hw_mgr;
+ hw_ctx->get_raw_results = fd_acquire_args.get_raw_results;
+ hw_ctx->mode = fd_acquire_args.mode;
+
+ /* Save incoming cam core info into hw ctx*/
+ hw_ctx->cb_priv = acquire_args->context_data;
+ hw_ctx->event_cb = acquire_args->event_cb;
+
+ /* Update out args */
+ acquire_args->ctxt_to_hw_map = hw_ctx;
+
+ cam_fd_mgr_util_put_ctx(&hw_mgr->used_ctx_list, &hw_ctx);
+
+ return 0;
+put_ctx:
+ list_del_init(&hw_ctx->list);
+ cam_fd_mgr_util_put_ctx(&hw_mgr->free_ctx_list, &hw_ctx);
+ return rc;
+}
+
+static int cam_fd_mgr_hw_release(void *hw_mgr_priv, void *hw_release_args)
+{
+ struct cam_fd_hw_mgr *hw_mgr = (struct cam_fd_hw_mgr *)hw_mgr_priv;
+ struct cam_hw_release_args *release_args = hw_release_args;
+ struct cam_fd_hw_mgr_ctx *hw_ctx;
+ int rc;
+
+ if (!hw_mgr_priv || !hw_release_args) {
+ CAM_ERR(CAM_FD, "Invalid arguments %pK, %pK",
+ hw_mgr_priv, hw_release_args);
+ return -EINVAL;
+ }
+
+ hw_ctx = (struct cam_fd_hw_mgr_ctx *)release_args->ctxt_to_hw_map;
+ if (!hw_ctx || !hw_ctx->ctx_in_use) {
+ CAM_ERR(CAM_FD, "Invalid context is used, hw_ctx=%pK", hw_ctx);
+ return -EPERM;
+ }
+
+ rc = cam_fd_mgr_util_release_device(hw_mgr, hw_ctx);
+ if (rc)
+ CAM_ERR(CAM_FD, "Failed in release device, rc=%d", rc);
+
+ list_del_init(&hw_ctx->list);
+ cam_fd_mgr_util_put_ctx(&hw_mgr->free_ctx_list, &hw_ctx);
+
+ return 0;
+}
+
+static int cam_fd_mgr_hw_start(void *hw_mgr_priv, void *mgr_start_args)
+{
+ int rc = 0;
+ struct cam_fd_hw_mgr *hw_mgr = (struct cam_fd_hw_mgr *)hw_mgr_priv;
+ struct cam_hw_start_args *hw_mgr_start_args =
+ (struct cam_hw_start_args *)mgr_start_args;
+ struct cam_fd_hw_mgr_ctx *hw_ctx;
+ struct cam_fd_device *hw_device;
+ struct cam_fd_hw_init_args hw_init_args;
+
+ if (!hw_mgr_priv || !hw_mgr_start_args) {
+ CAM_ERR(CAM_FD, "Invalid arguments %pK %pK",
+ hw_mgr_priv, hw_mgr_start_args);
+ return -EINVAL;
+ }
+
+ hw_ctx = (struct cam_fd_hw_mgr_ctx *)hw_mgr_start_args->ctxt_to_hw_map;
+ if (!hw_ctx || !hw_ctx->ctx_in_use) {
+ CAM_ERR(CAM_FD, "Invalid context is used, hw_ctx=%pK", hw_ctx);
+ return -EPERM;
+ }
+
+ CAM_DBG(CAM_FD, "ctx index=%d, hw_ctx=%d", hw_ctx->ctx_index,
+ hw_ctx->device_index);
+
+ rc = cam_fd_mgr_util_get_device(hw_mgr, hw_ctx, &hw_device);
+ if (rc) {
+ CAM_ERR(CAM_FD, "Error in getting device %d", rc);
+ return rc;
+ }
+
+ if (hw_device->hw_intf->hw_ops.init) {
+ hw_init_args.hw_ctx = hw_ctx;
+ hw_init_args.ctx_hw_private = hw_ctx->ctx_hw_private;
+ rc = hw_device->hw_intf->hw_ops.init(
+ hw_device->hw_intf->hw_priv, &hw_init_args,
+ sizeof(hw_init_args));
+ if (rc) {
+ CAM_ERR(CAM_FD, "Failed in HW Init %d", rc);
+ return rc;
+ }
+ } else {
+ CAM_ERR(CAM_FD, "Invalid init function");
+ return -EINVAL;
+ }
+
+ return rc;
+}
+
+static int cam_fd_mgr_hw_stop(void *hw_mgr_priv, void *mgr_stop_args)
+{
+ struct cam_fd_hw_mgr *hw_mgr = (struct cam_fd_hw_mgr *)hw_mgr_priv;
+ struct cam_hw_stop_args *hw_mgr_stop_args =
+ (struct cam_hw_stop_args *)mgr_stop_args;
+ struct cam_fd_hw_mgr_ctx *hw_ctx;
+ struct cam_fd_device *hw_device;
+ struct cam_fd_hw_stop_args hw_stop_args;
+ struct cam_fd_hw_deinit_args hw_deinit_args;
+ int rc = 0;
+
+ if (!hw_mgr_priv || !hw_mgr_stop_args) {
+ CAM_ERR(CAM_FD, "Invalid arguments %pK %pK",
+ hw_mgr_priv, hw_mgr_stop_args);
+ return -EINVAL;
+ }
+
+ hw_ctx = (struct cam_fd_hw_mgr_ctx *)hw_mgr_stop_args->ctxt_to_hw_map;
+ if (!hw_ctx || !hw_ctx->ctx_in_use) {
+ CAM_ERR(CAM_FD, "Invalid context is used, hw_ctx=%pK", hw_ctx);
+ return -EPERM;
+ }
+ CAM_DBG(CAM_FD, "ctx index=%d, hw_ctx=%d", hw_ctx->ctx_index,
+ hw_ctx->device_index);
+
+ rc = cam_fd_mgr_util_get_device(hw_mgr, hw_ctx, &hw_device);
+ if (rc) {
+ CAM_ERR(CAM_FD, "Error in getting device %d", rc);
+ return rc;
+ }
+
+ CAM_DBG(CAM_FD, "FD Device ready_to_process = %d",
+ hw_device->ready_to_process);
+
+ if ((hw_device->hw_intf->hw_ops.stop) &&
+ (hw_device->ready_to_process == false)) {
+ /*
+ * Even if device is in processing state, we should submit
+ * stop command only if this ctx is running on hw
+ */
+ hw_stop_args.hw_ctx = hw_ctx;
+ rc = hw_device->hw_intf->hw_ops.stop(
+ hw_device->hw_intf->hw_priv, &hw_stop_args,
+ sizeof(hw_stop_args));
+ if (rc) {
+ CAM_ERR(CAM_FD, "Failed in HW Stop %d", rc);
+ return rc;
+ }
+ }
+
+ if (hw_device->hw_intf->hw_ops.deinit) {
+ hw_deinit_args.hw_ctx = hw_ctx;
+ hw_deinit_args.ctx_hw_private = hw_ctx->ctx_hw_private;
+ rc = hw_device->hw_intf->hw_ops.deinit(
+ hw_device->hw_intf->hw_priv, &hw_deinit_args,
+ sizeof(hw_deinit_args));
+ if (rc) {
+ CAM_ERR(CAM_FD, "Failed in HW DeInit %d", rc);
+ return rc;
+ }
+ }
+
+ return rc;
+}
+
+static int cam_fd_mgr_hw_prepare_update(void *hw_mgr_priv,
+ void *hw_prepare_update_args)
+{
+ struct cam_fd_hw_mgr *hw_mgr = (struct cam_fd_hw_mgr *)hw_mgr_priv;
+ struct cam_hw_prepare_update_args *prepare =
+ (struct cam_hw_prepare_update_args *) hw_prepare_update_args;
+ struct cam_fd_hw_mgr_ctx *hw_ctx;
+ struct cam_fd_device *hw_device;
+ struct cam_kmd_buf_info kmd_buf;
+ int rc;
+ struct cam_fd_hw_cmd_prestart_args prestart_args;
+ struct cam_fd_mgr_frame_request *frame_req;
+
+ if (!hw_mgr_priv || !hw_prepare_update_args) {
+ CAM_ERR(CAM_FD, "Invalid args %pK %pK",
+ hw_mgr_priv, hw_prepare_update_args);
+ return -EINVAL;
+ }
+
+ hw_ctx = (struct cam_fd_hw_mgr_ctx *)prepare->ctxt_to_hw_map;
+ if (!hw_ctx || !hw_ctx->ctx_in_use) {
+ CAM_ERR(CAM_FD, "Invalid context is used, hw_ctx=%pK", hw_ctx);
+ return -EPERM;
+ }
+
+ rc = cam_fd_mgr_util_get_device(hw_mgr, hw_ctx, &hw_device);
+ if (rc) {
+ CAM_ERR(CAM_FD, "Error in getting device %d", rc);
+ goto error;
+ }
+
+ rc = cam_fd_mgr_util_packet_validate(prepare->packet);
+ if (rc) {
+ CAM_ERR(CAM_FD, "Error in packet validation %d", rc);
+ goto error;
+ }
+
+ rc = cam_packet_util_get_kmd_buffer(prepare->packet, &kmd_buf);
+ if (rc) {
+ CAM_ERR(CAM_FD, "Error in get kmd buf buffer %d", rc);
+ goto error;
+ }
+
+ CAM_DBG(CAM_FD,
+ "KMD Buf : hdl=%d, cpu_addr=%pK, offset=%d, size=%d, used=%d",
+ kmd_buf.handle, kmd_buf.cpu_addr, kmd_buf.offset,
+ kmd_buf.size, kmd_buf.used_bytes);
+
+ /* We do not expect any patching, but just do it anyway */
+ rc = cam_packet_util_process_patches(prepare->packet,
+ hw_mgr->device_iommu.non_secure);
+ if (rc) {
+ CAM_ERR(CAM_FD, "Patch FD packet failed, rc=%d", rc);
+ return rc;
+ }
+
+ memset(&prestart_args, 0x0, sizeof(prestart_args));
+ prestart_args.ctx_hw_private = hw_ctx->ctx_hw_private;
+ prestart_args.hw_ctx = hw_ctx;
+ prestart_args.request_id = prepare->packet->header.request_id;
+
+ rc = cam_fd_mgr_util_parse_generic_cmd_buffer(hw_ctx, prepare->packet,
+ &prestart_args);
+ if (rc) {
+ CAM_ERR(CAM_FD, "Error in parsing gerneric cmd buffer %d", rc);
+ goto error;
+ }
+
+ rc = cam_fd_mgr_util_prepare_io_buf_info(
+ hw_mgr->device_iommu.non_secure, prepare,
+ prestart_args.input_buf, prestart_args.output_buf,
+ CAM_FD_MAX_IO_BUFFERS);
+ if (rc) {
+ CAM_ERR(CAM_FD, "Error in prepare IO Buf %d", rc);
+ goto error;
+ }
+
+ rc = cam_fd_mgr_util_prepare_hw_update_entries(hw_mgr, prepare,
+ &prestart_args, &kmd_buf);
+ if (rc) {
+ CAM_ERR(CAM_FD, "Error in hw update entries %d", rc);
+ goto error;
+ }
+
+ /* get a free frame req from free list */
+ rc = cam_fd_mgr_util_get_frame_req(&hw_mgr->frame_free_list,
+ &frame_req);
+ if (rc || !frame_req) {
+ CAM_ERR(CAM_FD, "Get frame_req failed, rc=%d, hw_ctx=%pK",
+ rc, hw_ctx);
+ return -ENOMEM;
+ }
+
+ /* Setup frame request info and queue to pending list */
+ frame_req->hw_ctx = hw_ctx;
+ frame_req->request_id = prepare->packet->header.request_id;
+ /* This has to be passed to HW while calling hw_ops->start */
+ frame_req->hw_req_private = prestart_args.hw_req_private;
+
+ /*
+ * Save the current frame_req into priv,
+ * this will come as priv while hw_config
+ */
+ prepare->priv = frame_req;
+
+ CAM_DBG(CAM_FD, "FramePrepare : Frame[%lld]", frame_req->request_id);
+
+ return 0;
+error:
+ return rc;
+}
+
+static int cam_fd_mgr_hw_config(void *hw_mgr_priv, void *hw_config_args)
+{
+ struct cam_fd_hw_mgr *hw_mgr = (struct cam_fd_hw_mgr *)hw_mgr_priv;
+ struct cam_hw_config_args *config =
+ (struct cam_hw_config_args *) hw_config_args;
+ struct cam_fd_hw_mgr_ctx *hw_ctx;
+ struct cam_fd_mgr_frame_request *frame_req;
+ int rc;
+ int i;
+
+ if (!hw_mgr || !config) {
+ CAM_ERR(CAM_FD, "Invalid arguments %pK %pK", hw_mgr, config);
+ return -EINVAL;
+ }
+
+ if (!config->num_hw_update_entries) {
+ CAM_ERR(CAM_FD, "No hw update enteries are available");
+ return -EINVAL;
+ }
+
+ hw_ctx = (struct cam_fd_hw_mgr_ctx *)config->ctxt_to_hw_map;
+ if (!hw_ctx || !hw_ctx->ctx_in_use) {
+ CAM_ERR(CAM_FD, "Invalid context is used, hw_ctx=%pK", hw_ctx);
+ return -EPERM;
+ }
+
+ frame_req = config->priv;
+ CAM_DBG(CAM_FD, "FrameHWConfig : Frame[%lld]", frame_req->request_id);
+
+ frame_req->num_hw_update_entries = config->num_hw_update_entries;
+ for (i = 0; i < config->num_hw_update_entries; i++) {
+ frame_req->hw_update_entries[i] = config->hw_update_entries[i];
+ CAM_DBG(CAM_FD, "PreStart HWEntry[%d] : %d %d %d %d %pK",
+ frame_req->hw_update_entries[i].handle,
+ frame_req->hw_update_entries[i].offset,
+ frame_req->hw_update_entries[i].len,
+ frame_req->hw_update_entries[i].flags,
+ frame_req->hw_update_entries[i].addr);
+ }
+
+ if (hw_ctx->priority == CAM_FD_PRIORITY_HIGH) {
+ CAM_DBG(CAM_FD, "Insert frame into prio0 queue");
+ rc = cam_fd_mgr_util_put_frame_req(
+ &hw_mgr->frame_pending_list_high, &frame_req);
+ } else {
+ CAM_DBG(CAM_FD, "Insert frame into prio1 queue");
+ rc = cam_fd_mgr_util_put_frame_req(
+ &hw_mgr->frame_pending_list_normal, &frame_req);
+ }
+ if (rc) {
+ CAM_ERR(CAM_FD, "Failed in queuing frame req, rc=%d", rc);
+ goto put_free_list;
+ }
+
+ rc = cam_fd_mgr_util_schedule_frame_worker_task(hw_mgr);
+ if (rc) {
+ CAM_ERR(CAM_FD, "Worker task scheduling failed %d", rc);
+ goto remove_and_put_free_list;
+ }
+
+ return 0;
+
+remove_and_put_free_list:
+
+ if (hw_ctx->priority == CAM_FD_PRIORITY_HIGH) {
+ CAM_DBG(CAM_FD, "Removing frame into prio0 queue");
+ cam_fd_mgr_util_get_frame_req(
+ &hw_mgr->frame_pending_list_high, &frame_req);
+ } else {
+ CAM_DBG(CAM_FD, "Removing frame into prio1 queue");
+ cam_fd_mgr_util_get_frame_req(
+ &hw_mgr->frame_pending_list_normal, &frame_req);
+ }
+put_free_list:
+ cam_fd_mgr_util_put_frame_req(&hw_mgr->frame_free_list,
+ &frame_req);
+
+ return rc;
+}
+
+int cam_fd_hw_mgr_deinit(struct device_node *of_node)
+{
+ CAM_DBG(CAM_FD, "HW Mgr Deinit");
+
+ cam_req_mgr_workq_destroy(&g_fd_hw_mgr.work);
+
+ cam_smmu_ops(g_fd_hw_mgr.device_iommu.non_secure, CAM_SMMU_DETACH);
+ cam_smmu_destroy_handle(g_fd_hw_mgr.device_iommu.non_secure);
+ g_fd_hw_mgr.device_iommu.non_secure = -1;
+
+ mutex_destroy(&g_fd_hw_mgr.ctx_mutex);
+ mutex_destroy(&g_fd_hw_mgr.frame_req_mutex);
+ mutex_destroy(&g_fd_hw_mgr.hw_mgr_mutex);
+
+ return 0;
+}
+
+int cam_fd_hw_mgr_init(struct device_node *of_node,
+ struct cam_hw_mgr_intf *hw_mgr_intf)
+{
+ int count, i, rc = 0;
+ struct cam_hw_intf *hw_intf = NULL;
+ struct cam_fd_hw_mgr_ctx *hw_mgr_ctx;
+ struct cam_fd_device *hw_device;
+ struct cam_fd_mgr_frame_request *frame_req;
+
+ if (!of_node || !hw_mgr_intf) {
+ CAM_ERR(CAM_FD, "Invalid args of_node %pK hw_mgr_intf %pK",
+ of_node, hw_mgr_intf);
+ return -EINVAL;
+ }
+
+ memset(&g_fd_hw_mgr, 0x0, sizeof(g_fd_hw_mgr));
+ memset(hw_mgr_intf, 0x0, sizeof(*hw_mgr_intf));
+
+ mutex_init(&g_fd_hw_mgr.ctx_mutex);
+ mutex_init(&g_fd_hw_mgr.frame_req_mutex);
+ mutex_init(&g_fd_hw_mgr.hw_mgr_mutex);
+ spin_lock_init(&g_fd_hw_mgr.hw_mgr_slock);
+
+ count = of_property_count_strings(of_node, "compat-hw-name");
+ if (!count || (count > CAM_FD_HW_MAX)) {
+ CAM_ERR(CAM_FD, "Invalid compat names in dev tree %d", count);
+ return -EINVAL;
+ }
+ g_fd_hw_mgr.num_devices = count;
+
+ g_fd_hw_mgr.raw_results_available = false;
+ g_fd_hw_mgr.supported_modes = 0;
+
+ for (i = 0; i < count; i++) {
+ hw_device = &g_fd_hw_mgr.hw_device[i];
+
+ rc = cam_fd_mgr_util_pdev_get_hw_intf(of_node, i, &hw_intf);
+ if (rc) {
+ CAM_ERR(CAM_FD, "hw intf from pdev failed, rc=%d", rc);
+ return rc;
+ }
+
+ mutex_init(&hw_device->lock);
+
+ hw_device->valid = true;
+ hw_device->hw_intf = hw_intf;
+ hw_device->ready_to_process = true;
+
+ if (hw_device->hw_intf->hw_ops.process_cmd) {
+ struct cam_fd_hw_cmd_set_irq_cb irq_cb_args;
+
+ irq_cb_args.cam_fd_hw_mgr_cb = cam_fd_mgr_irq_cb;
+ irq_cb_args.data = hw_device;
+
+ rc = hw_device->hw_intf->hw_ops.process_cmd(
+ hw_device->hw_intf->hw_priv,
+ CAM_FD_HW_CMD_REGISTER_CALLBACK,
+ &irq_cb_args, sizeof(irq_cb_args));
+ if (rc) {
+ CAM_ERR(CAM_FD,
+ "Failed in REGISTER_CALLBACK %d", rc);
+ return rc;
+ }
+ }
+
+ if (hw_device->hw_intf->hw_ops.get_hw_caps) {
+ rc = hw_device->hw_intf->hw_ops.get_hw_caps(
+ hw_intf->hw_priv, &hw_device->hw_caps,
+ sizeof(hw_device->hw_caps));
+ if (rc) {
+ CAM_ERR(CAM_FD, "Failed in get_hw_caps %d", rc);
+ return rc;
+ }
+
+ g_fd_hw_mgr.raw_results_available |=
+ hw_device->hw_caps.raw_results_available;
+ g_fd_hw_mgr.supported_modes |=
+ hw_device->hw_caps.supported_modes;
+
+ CAM_DBG(CAM_FD,
+ "Device[mode=%d, raw=%d], Mgr[mode=%d, raw=%d]",
+ hw_device->hw_caps.supported_modes,
+ hw_device->hw_caps.raw_results_available,
+ g_fd_hw_mgr.supported_modes,
+ g_fd_hw_mgr.raw_results_available);
+ }
+ }
+
+ INIT_LIST_HEAD(&g_fd_hw_mgr.free_ctx_list);
+ INIT_LIST_HEAD(&g_fd_hw_mgr.used_ctx_list);
+ INIT_LIST_HEAD(&g_fd_hw_mgr.frame_free_list);
+ INIT_LIST_HEAD(&g_fd_hw_mgr.frame_pending_list_high);
+ INIT_LIST_HEAD(&g_fd_hw_mgr.frame_pending_list_normal);
+ INIT_LIST_HEAD(&g_fd_hw_mgr.frame_processing_list);
+
+ g_fd_hw_mgr.device_iommu.non_secure = -1;
+ g_fd_hw_mgr.device_iommu.secure = -1;
+ g_fd_hw_mgr.cdm_iommu.non_secure = -1;
+ g_fd_hw_mgr.cdm_iommu.secure = -1;
+
+ rc = cam_smmu_get_handle("fd",
+ &g_fd_hw_mgr.device_iommu.non_secure);
+ if (rc) {
+ CAM_ERR(CAM_FD, "Get iommu handle failed, rc=%d", rc);
+ goto destroy_mutex;
+ }
+
+ rc = cam_smmu_ops(g_fd_hw_mgr.device_iommu.non_secure, CAM_SMMU_ATTACH);
+ if (rc) {
+ CAM_ERR(CAM_FD, "FD attach iommu handle failed, rc=%d", rc);
+ goto destroy_smmu;
+ }
+
+ rc = cam_cdm_get_iommu_handle("fd", &g_fd_hw_mgr.cdm_iommu);
+ if (rc)
+ CAM_DBG(CAM_FD, "Failed to acquire the CDM iommu handles");
+
+ CAM_DBG(CAM_FD, "iommu handles : device(%d, %d), cdm(%d, %d)",
+ g_fd_hw_mgr.device_iommu.non_secure,
+ g_fd_hw_mgr.device_iommu.secure,
+ g_fd_hw_mgr.cdm_iommu.non_secure,
+ g_fd_hw_mgr.cdm_iommu.secure);
+
+ /* Init hw mgr contexts and add to free list */
+ for (i = 0; i < CAM_CTX_MAX; i++) {
+ hw_mgr_ctx = &g_fd_hw_mgr.ctx_pool[i];
+
+ memset(hw_mgr_ctx, 0x0, sizeof(*hw_mgr_ctx));
+ INIT_LIST_HEAD(&hw_mgr_ctx->list);
+
+ hw_mgr_ctx->ctx_index = i;
+ hw_mgr_ctx->device_index = -1;
+ hw_mgr_ctx->hw_mgr = &g_fd_hw_mgr;
+
+ list_add_tail(&hw_mgr_ctx->list, &g_fd_hw_mgr.free_ctx_list);
+ }
+
+ /* Init hw mgr frame requests and add to free list */
+ for (i = 0; i < CAM_CTX_REQ_MAX; i++) {
+ frame_req = &g_fd_hw_mgr.frame_req[i];
+
+ memset(frame_req, 0x0, sizeof(*frame_req));
+ INIT_LIST_HEAD(&frame_req->list);
+
+ list_add_tail(&frame_req->list, &g_fd_hw_mgr.frame_free_list);
+ }
+
+ rc = cam_req_mgr_workq_create("cam_fd_worker", CAM_FD_WORKQ_NUM_TASK,
+ &g_fd_hw_mgr.work, CRM_WORKQ_USAGE_IRQ);
+ if (rc) {
+ CAM_ERR(CAM_FD, "Unable to create a worker, rc=%d", rc);
+ goto detach_smmu;
+ }
+
+ for (i = 0; i < CAM_FD_WORKQ_NUM_TASK; i++)
+ g_fd_hw_mgr.work->task.pool[i].payload =
+ &g_fd_hw_mgr.work_data[i];
+
+ /* Setup hw cap so that we can just return the info when requested */
+ memset(&g_fd_hw_mgr.fd_caps, 0, sizeof(g_fd_hw_mgr.fd_caps));
+ g_fd_hw_mgr.fd_caps.device_iommu = g_fd_hw_mgr.device_iommu;
+ g_fd_hw_mgr.fd_caps.cdm_iommu = g_fd_hw_mgr.cdm_iommu;
+ g_fd_hw_mgr.fd_caps.hw_caps = g_fd_hw_mgr.hw_device[0].hw_caps;
+
+ CAM_DBG(CAM_FD,
+ "IOMMU device(%d, %d), CDM(%d, %d) versions core[%d.%d], wrapper[%d.%d]",
+ g_fd_hw_mgr.fd_caps.device_iommu.secure,
+ g_fd_hw_mgr.fd_caps.device_iommu.non_secure,
+ g_fd_hw_mgr.fd_caps.cdm_iommu.secure,
+ g_fd_hw_mgr.fd_caps.cdm_iommu.non_secure,
+ g_fd_hw_mgr.fd_caps.hw_caps.core_version.major,
+ g_fd_hw_mgr.fd_caps.hw_caps.core_version.minor,
+ g_fd_hw_mgr.fd_caps.hw_caps.wrapper_version.major,
+ g_fd_hw_mgr.fd_caps.hw_caps.wrapper_version.minor);
+
+ hw_mgr_intf->hw_mgr_priv = &g_fd_hw_mgr;
+ hw_mgr_intf->hw_get_caps = cam_fd_mgr_hw_get_caps;
+ hw_mgr_intf->hw_acquire = cam_fd_mgr_hw_acquire;
+ hw_mgr_intf->hw_release = cam_fd_mgr_hw_release;
+ hw_mgr_intf->hw_start = cam_fd_mgr_hw_start;
+ hw_mgr_intf->hw_stop = cam_fd_mgr_hw_stop;
+ hw_mgr_intf->hw_prepare_update = cam_fd_mgr_hw_prepare_update;
+ hw_mgr_intf->hw_config = cam_fd_mgr_hw_config;
+ hw_mgr_intf->hw_read = NULL;
+ hw_mgr_intf->hw_write = NULL;
+ hw_mgr_intf->hw_close = NULL;
+
+ return rc;
+
+detach_smmu:
+ cam_smmu_ops(g_fd_hw_mgr.device_iommu.non_secure, CAM_SMMU_DETACH);
+destroy_smmu:
+ cam_smmu_destroy_handle(g_fd_hw_mgr.device_iommu.non_secure);
+ g_fd_hw_mgr.device_iommu.non_secure = -1;
+destroy_mutex:
+ mutex_destroy(&g_fd_hw_mgr.ctx_mutex);
+ mutex_destroy(&g_fd_hw_mgr.frame_req_mutex);
+ mutex_destroy(&g_fd_hw_mgr.hw_mgr_mutex);
+
+ return rc;
+}
+
diff --git a/drivers/media/platform/msm/camera/cam_fd/fd_hw_mgr/cam_fd_hw_mgr.h b/drivers/media/platform/msm/camera/cam_fd/fd_hw_mgr/cam_fd_hw_mgr.h
new file mode 100644
index 0000000..135e006
--- /dev/null
+++ b/drivers/media/platform/msm/camera/cam_fd/fd_hw_mgr/cam_fd_hw_mgr.h
@@ -0,0 +1,182 @@
+/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _CAM_FD_HW_MGR_H_
+#define _CAM_FD_HW_MGR_H_
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+
+#include <media/cam_fd.h>
+#include "cam_hw.h"
+#include "cam_hw_intf.h"
+#include "cam_cpas_api.h"
+#include "cam_debug_util.h"
+#include "cam_hw_mgr_intf.h"
+#include "cam_req_mgr_workq.h"
+#include "cam_fd_hw_intf.h"
+
+#define CAM_FD_HW_MAX 1
+#define CAM_FD_WORKQ_NUM_TASK 10
+
+struct cam_fd_hw_mgr;
+
+/**
+ * enum cam_fd_mgr_work_type - Type of worker task
+ *
+ * @CAM_FD_WORK_FRAME : Work type indicating frame task
+ * @CAM_FD_WORK_IRQ : Work type indicating irq task
+ */
+enum cam_fd_mgr_work_type {
+ CAM_FD_WORK_FRAME,
+ CAM_FD_WORK_IRQ,
+};
+
+/**
+ * struct cam_fd_hw_mgr_ctx : FD HW Mgr context
+ *
+ * @list : List pointer used to maintain this context
+ * in free, used list
+ * @ctx_index : Index of this context
+ * @ctx_in_use : Whether this context is in use
+ * @event_cb : Event callback pointer to notify cam core context
+ * @cb_priv : Event callback private pointer
+ * @hw_mgr : Pointer to hw manager
+ * @get_raw_results : Whether this context needs raw results
+ * @mode : Mode in which this context runs
+ * @device_index : HW Device used by this context
+ * @ctx_hw_private : HW layer's private context pointer for this context
+ * @priority : Priority of this context
+ */
+struct cam_fd_hw_mgr_ctx {
+ struct list_head list;
+ uint32_t ctx_index;
+ bool ctx_in_use;
+ cam_hw_event_cb_func event_cb;
+ void *cb_priv;
+ struct cam_fd_hw_mgr *hw_mgr;
+ bool get_raw_results;
+ enum cam_fd_hw_mode mode;
+ int32_t device_index;
+ void *ctx_hw_private;
+ uint32_t priority;
+};
+
+/**
+ * struct cam_fd_device : FD HW Device
+ *
+ * @hw_caps : This FD device's capabilities
+ * @hw_intf : FD device's interface information
+ * @ready_to_process : Whether this device is ready to process next frame
+ * @num_ctxts : Number of context currently running on this device
+ * @valid : Whether this device is valid
+ * @lock : Lock used for protectin
+ */
+struct cam_fd_device {
+ struct cam_fd_hw_caps hw_caps;
+ struct cam_hw_intf *hw_intf;
+ bool ready_to_process;
+ uint32_t num_ctxts;
+ bool valid;
+ struct mutex lock;
+};
+
+/**
+ * struct cam_fd_mgr_frame_request : Frame request information maintained
+ * in HW Mgr layer
+ *
+ * @list : List pointer used to maintain this request in
+ * free, pending, processing request lists
+ * @request_id : Request ID corresponding to this request
+ * @hw_ctx : HW context from which this request is coming
+ * @hw_req_private : HW layer's private information specific to
+ * this request
+ * @hw_update_entries : HW update entries corresponding to this request
+ * which needs to be submitted to HW through CDM
+ * @num_hw_update_entries : Number of HW update entries
+ */
+struct cam_fd_mgr_frame_request {
+ struct list_head list;
+ uint64_t request_id;
+ struct cam_fd_hw_mgr_ctx *hw_ctx;
+ struct cam_fd_hw_req_private hw_req_private;
+ struct cam_hw_update_entry hw_update_entries[CAM_FD_MAX_HW_ENTRIES];
+ uint32_t num_hw_update_entries;
+};
+
+/**
+ * struct cam_fd_mgr_work_data : HW Mgr work data information
+ *
+ * @type : Type of work
+ * @irq_type : IRQ type when this work is queued because of irq callback
+ */
+struct cam_fd_mgr_work_data {
+ enum cam_fd_mgr_work_type type;
+ enum cam_fd_hw_irq_type irq_type;
+};
+
+/**
+ * struct cam_fd_hw_mgr : FD HW Mgr information
+ *
+ * @free_ctx_list : List of free contexts available for acquire
+ * @used_ctx_list : List of contexts that are acquired
+ * @frame_free_list : List of free frame requests available
+ * @frame_pending_list_high : List of high priority frame requests pending
+ * for processing
+ * @frame_pending_list_normal : List of normal priority frame requests pending
+ * for processing
+ * @frame_processing_list : List of frame requests currently being
+ * processed currently. Generally maximum one
+ * request would be present in this list
+ * @hw_mgr_mutex : Mutex to protect hw mgr data when accessed
+ * from multiple threads
+ * @hw_mgr_slock : Spin lock to protect hw mgr data when accessed
+ * from multiple threads
+ * @ctx_mutex : Mutex to protect context list
+ * @frame_req_mutex : Mutex to protect frame request list
+ * @device_iommu : Device IOMMU information
+ * @cdm_iommu : CDM IOMMU information
+ * @hw_device : Underlying HW device information
+ * @num_devices : Number of HW devices available
+ * @raw_results_available : Whether raw results available in this driver
+ * @supported_modes : Supported modes by this driver
+ * @ctx_pool : List of context
+ * @frame_req : List of frame requests
+ * @work : Worker handle
+ * @work_data : Worker data
+ * @fd_caps : FD driver capabilities
+ */
+struct cam_fd_hw_mgr {
+ struct list_head free_ctx_list;
+ struct list_head used_ctx_list;
+ struct list_head frame_free_list;
+ struct list_head frame_pending_list_high;
+ struct list_head frame_pending_list_normal;
+ struct list_head frame_processing_list;
+ struct mutex hw_mgr_mutex;
+ spinlock_t hw_mgr_slock;
+ struct mutex ctx_mutex;
+ struct mutex frame_req_mutex;
+ struct cam_iommu_handle device_iommu;
+ struct cam_iommu_handle cdm_iommu;
+ struct cam_fd_device hw_device[CAM_FD_HW_MAX];
+ uint32_t num_devices;
+ bool raw_results_available;
+ uint32_t supported_modes;
+ struct cam_fd_hw_mgr_ctx ctx_pool[CAM_CTX_MAX];
+ struct cam_fd_mgr_frame_request frame_req[CAM_CTX_REQ_MAX];
+ struct cam_req_mgr_core_workq *work;
+ struct cam_fd_mgr_work_data work_data[CAM_FD_WORKQ_NUM_TASK];
+ struct cam_fd_query_cap_cmd fd_caps;
+};
+
+#endif /* _CAM_FD_HW_MGR_H_ */
diff --git a/drivers/media/platform/msm/camera/cam_fd/fd_hw_mgr/cam_fd_hw_mgr_intf.h b/drivers/media/platform/msm/camera/cam_fd/fd_hw_mgr/cam_fd_hw_mgr_intf.h
new file mode 100644
index 0000000..58cba4f
--- /dev/null
+++ b/drivers/media/platform/msm/camera/cam_fd/fd_hw_mgr/cam_fd_hw_mgr_intf.h
@@ -0,0 +1,25 @@
+/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _CAM_FD_HW_MGR_INTF_H_
+#define _CAM_FD_HW_MGR_INTF_H_
+
+#include <linux/of.h>
+
+#include "cam_debug_util.h"
+#include "cam_hw_mgr_intf.h"
+
+int cam_fd_hw_mgr_init(struct device_node *of_node,
+ struct cam_hw_mgr_intf *hw_mgr_intf);
+int cam_fd_hw_mgr_deinit(struct device_node *of_node);
+
+#endif /* _CAM_FD_HW_MGR_INTF_H_ */
diff --git a/drivers/media/platform/msm/camera/cam_fd/fd_hw_mgr/fd_hw/Makefile b/drivers/media/platform/msm/camera/cam_fd/fd_hw_mgr/fd_hw/Makefile
new file mode 100644
index 0000000..c3e706d
--- /dev/null
+++ b/drivers/media/platform/msm/camera/cam_fd/fd_hw_mgr/fd_hw/Makefile
@@ -0,0 +1,13 @@
+ccflags-y += -Idrivers/media/platform/msm/camera/cam_utils
+ccflags-y += -Idrivers/media/platform/msm/camera/cam_req_mgr
+ccflags-y += -Idrivers/media/platform/msm/camera/cam_core
+ccflags-y += -Idrivers/media/platform/msm/camera/cam_sync
+ccflags-y += -Idrivers/media/platform/msm/camera/cam_smmu
+ccflags-y += -Idrivers/media/platform/msm/camera/cam_cdm
+ccflags-y += -Idrivers/media/platform/msm/camera/cam_fd
+ccflags-y += -Idrivers/media/platform/msm/camera/cam_fd/fd_hw_mgr
+ccflags-y += -Idrivers/media/platform/msm/camera/cam_fd/fd_hw_mgr/fd_hw
+ccflags-y += -Idrivers/media/platform/msm/camera
+ccflags-y += -Idrivers/media/platform/msm/camera/cam_cpas/include
+
+obj-$(CONFIG_SPECTRA_CAMERA) += cam_fd_hw_dev.o cam_fd_hw_core.o cam_fd_hw_soc.o
diff --git a/drivers/media/platform/msm/camera/cam_fd/fd_hw_mgr/fd_hw/cam_fd_hw_core.c b/drivers/media/platform/msm/camera/cam_fd/fd_hw_mgr/fd_hw/cam_fd_hw_core.c
new file mode 100644
index 0000000..51fcdcaa
--- /dev/null
+++ b/drivers/media/platform/msm/camera/cam_fd/fd_hw_mgr/fd_hw/cam_fd_hw_core.c
@@ -0,0 +1,1125 @@
+/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include "cam_fd_hw_core.h"
+#include "cam_fd_hw_soc.h"
+
+#define CAM_FD_REG_VAL_PAIR_SIZE 256
+
+static uint32_t cam_fd_cdm_write_reg_val_pair(uint32_t *buffer,
+ uint32_t index, uint32_t reg_offset, uint32_t reg_value)
+{
+ buffer[index++] = reg_offset;
+ buffer[index++] = reg_value;
+
+ CAM_DBG(CAM_FD, "FD_CDM_CMD: Base[FD_CORE] Offset[0x%8x] Value[0x%8x]",
+ reg_offset, reg_value);
+
+ return index;
+}
+
+static void cam_fd_hw_util_cdm_callback(uint32_t handle, void *userdata,
+ enum cam_cdm_cb_status status, uint32_t cookie)
+{
+ CAM_DBG(CAM_FD, "CDM hdl=%x, udata=%pK, status=%d, cookie=%d",
+ handle, userdata, status, cookie);
+}
+
+static void cam_fd_hw_util_enable_power_on_settings(struct cam_hw_info *fd_hw)
+{
+ struct cam_hw_soc_info *soc_info = &fd_hw->soc_info;
+ struct cam_fd_hw_static_info *hw_static_info =
+ ((struct cam_fd_core *)fd_hw->core_info)->hw_static_info;
+
+ if (hw_static_info->enable_errata_wa.single_irq_only == false) {
+ /* Enable IRQs here */
+ cam_fd_soc_register_write(soc_info, CAM_FD_REG_WRAPPER,
+ hw_static_info->wrapper_regs.irq_mask,
+ hw_static_info->irq_mask);
+ }
+
+ /* QoS settings */
+ cam_fd_soc_register_write(soc_info, CAM_FD_REG_WRAPPER,
+ hw_static_info->wrapper_regs.vbif_req_priority,
+ hw_static_info->qos_priority);
+ cam_fd_soc_register_write(soc_info, CAM_FD_REG_WRAPPER,
+ hw_static_info->wrapper_regs.vbif_priority_level,
+ hw_static_info->qos_priority_level);
+}
+
+int cam_fd_hw_util_get_hw_caps(struct cam_hw_info *fd_hw,
+ struct cam_fd_hw_caps *hw_caps)
+{
+ struct cam_hw_soc_info *soc_info = &fd_hw->soc_info;
+ struct cam_fd_hw_static_info *hw_static_info =
+ ((struct cam_fd_core *)fd_hw->core_info)->hw_static_info;
+ uint32_t reg_value;
+
+ if (!hw_static_info) {
+ CAM_ERR(CAM_FD, "Invalid hw info data");
+ return -EINVAL;
+ }
+
+ reg_value = cam_fd_soc_register_read(soc_info, CAM_FD_REG_CORE,
+ hw_static_info->core_regs.version);
+ hw_caps->core_version.major =
+ CAM_BITS_MASK_SHIFT(reg_value, 0xf00, 0x8);
+ hw_caps->core_version.minor =
+ CAM_BITS_MASK_SHIFT(reg_value, 0xf0, 0x4);
+ hw_caps->core_version.incr =
+ CAM_BITS_MASK_SHIFT(reg_value, 0xf, 0x0);
+
+ reg_value = cam_fd_soc_register_read(soc_info, CAM_FD_REG_WRAPPER,
+ hw_static_info->wrapper_regs.wrapper_version);
+ hw_caps->wrapper_version.major =
+ CAM_BITS_MASK_SHIFT(reg_value, 0xf0000000, 0x1c);
+ hw_caps->wrapper_version.minor =
+ CAM_BITS_MASK_SHIFT(reg_value, 0xfff0000, 0x10);
+ hw_caps->wrapper_version.incr =
+ CAM_BITS_MASK_SHIFT(reg_value, 0xffff, 0x0);
+
+ hw_caps->raw_results_available =
+ hw_static_info->results.raw_results_available;
+ hw_caps->supported_modes = hw_static_info->supported_modes;
+
+ CAM_DBG(CAM_FD, "core:%d.%d.%d wrapper:%d.%d.%d intermediate:%d",
+ hw_caps->core_version.major, hw_caps->core_version.minor,
+ hw_caps->core_version.incr, hw_caps->wrapper_version.major,
+ hw_caps->wrapper_version.minor, hw_caps->wrapper_version.incr,
+ hw_caps->raw_results_available);
+
+ return 0;
+}
+
+static int cam_fd_hw_util_fdwrapper_sync_reset(struct cam_hw_info *fd_hw)
+{
+ struct cam_fd_core *fd_core = (struct cam_fd_core *)fd_hw->core_info;
+ struct cam_fd_hw_static_info *hw_static_info = fd_core->hw_static_info;
+ struct cam_hw_soc_info *soc_info = &fd_hw->soc_info;
+ long time_left;
+
+ /* Before triggering reset to HW, clear the reset complete */
+ reinit_completion(&fd_core->reset_complete);
+
+ cam_fd_soc_register_write(soc_info, CAM_FD_REG_CORE,
+ hw_static_info->core_regs.control, 0x1);
+
+ if (hw_static_info->enable_errata_wa.single_irq_only) {
+ cam_fd_soc_register_write(soc_info, CAM_FD_REG_WRAPPER,
+ hw_static_info->wrapper_regs.irq_mask,
+ CAM_FD_IRQ_TO_MASK(CAM_FD_IRQ_RESET_DONE));
+ }
+
+ cam_fd_soc_register_write(soc_info, CAM_FD_REG_WRAPPER,
+ hw_static_info->wrapper_regs.sw_reset, 0x1);
+
+ time_left = wait_for_completion_timeout(&fd_core->reset_complete,
+ msecs_to_jiffies(CAM_FD_HW_HALT_RESET_TIMEOUT));
+ if (time_left <= 0) {
+ CAM_ERR(CAM_FD, "HW reset wait failed time_left=%d", time_left);
+ return -EPERM;
+ }
+
+ cam_fd_soc_register_write(soc_info, CAM_FD_REG_CORE,
+ hw_static_info->core_regs.control, 0x0);
+
+ CAM_DBG(CAM_FD, "FD Wrapper SW Sync Reset complete");
+
+ return 0;
+}
+
+
+static int cam_fd_hw_util_fdwrapper_halt(struct cam_hw_info *fd_hw)
+{
+ struct cam_fd_core *fd_core = (struct cam_fd_core *)fd_hw->core_info;
+ struct cam_fd_hw_static_info *hw_static_info = fd_core->hw_static_info;
+ struct cam_hw_soc_info *soc_info = &fd_hw->soc_info;
+ long time_left;
+
+ /* Before triggering halt to HW, clear halt complete */
+ reinit_completion(&fd_core->halt_complete);
+
+ cam_fd_soc_register_write(soc_info, CAM_FD_REG_CORE,
+ hw_static_info->core_regs.control, 0x1);
+
+ if (hw_static_info->enable_errata_wa.single_irq_only) {
+ cam_fd_soc_register_write(soc_info, CAM_FD_REG_WRAPPER,
+ hw_static_info->wrapper_regs.irq_mask,
+ CAM_FD_IRQ_TO_MASK(CAM_FD_IRQ_HALT_DONE));
+ }
+
+ cam_fd_soc_register_write(soc_info, CAM_FD_REG_WRAPPER,
+ hw_static_info->wrapper_regs.hw_stop, 0x1);
+
+ time_left = wait_for_completion_timeout(&fd_core->halt_complete,
+ msecs_to_jiffies(CAM_FD_HW_HALT_RESET_TIMEOUT));
+ if (time_left <= 0) {
+ CAM_ERR(CAM_FD, "HW halt wait failed time_left=%d", time_left);
+ return -EPERM;
+ }
+
+ cam_fd_soc_register_write(soc_info, CAM_FD_REG_CORE,
+ hw_static_info->core_regs.control, 0x0);
+
+ CAM_DBG(CAM_FD, "FD Wrapper Halt complete");
+
+ return 0;
+}
+
+static int cam_fd_hw_util_processcmd_prestart(struct cam_hw_info *fd_hw,
+ struct cam_fd_hw_cmd_prestart_args *prestart_args)
+{
+ struct cam_hw_soc_info *soc_info = &fd_hw->soc_info;
+ struct cam_fd_hw_static_info *hw_static_info =
+ ((struct cam_fd_core *)fd_hw->core_info)->hw_static_info;
+ struct cam_fd_ctx_hw_private *ctx_hw_private =
+ prestart_args->ctx_hw_private;
+ uint32_t size, size_required = 0;
+ uint32_t mem_base;
+ uint32_t *cmd_buf_addr = prestart_args->cmd_buf_addr;
+ uint32_t reg_val_pair[CAM_FD_REG_VAL_PAIR_SIZE];
+ uint32_t num_cmds = 0;
+ int i;
+ struct cam_fd_hw_io_buffer *io_buf;
+ struct cam_fd_hw_req_private *req_private;
+ uint32_t available_size = prestart_args->size;
+ bool work_buffer_configured = false;
+
+ if (!ctx_hw_private || !cmd_buf_addr) {
+ CAM_ERR(CAM_FD, "Invalid input prestart args %pK %pK",
+ ctx_hw_private, cmd_buf_addr);
+ return -EINVAL;
+ }
+
+ if (prestart_args->get_raw_results &&
+ !hw_static_info->results.raw_results_available) {
+ CAM_ERR(CAM_FD, "Raw results not supported %d %d",
+ prestart_args->get_raw_results,
+ hw_static_info->results.raw_results_available);
+ return -EINVAL;
+ }
+
+ req_private = &prestart_args->hw_req_private;
+ req_private->ctx_hw_private = prestart_args->ctx_hw_private;
+ req_private->request_id = prestart_args->request_id;
+ req_private->get_raw_results = prestart_args->get_raw_results;
+ req_private->fd_results = NULL;
+ req_private->raw_results = NULL;
+
+ /* Start preparing CDM register values that KMD has to insert */
+ num_cmds = cam_fd_cdm_write_reg_val_pair(reg_val_pair, num_cmds,
+ hw_static_info->core_regs.control, 0x1);
+ num_cmds = cam_fd_cdm_write_reg_val_pair(reg_val_pair, num_cmds,
+ hw_static_info->core_regs.control, 0x0);
+
+ for (i = 0; i < CAM_FD_MAX_IO_BUFFERS; i++) {
+ io_buf = &prestart_args->input_buf[i];
+
+ if (io_buf->valid == false)
+ break;
+
+ if (io_buf->io_cfg->direction != CAM_BUF_INPUT) {
+ CAM_ERR(CAM_FD, "Incorrect direction %d %d",
+ io_buf->io_cfg->direction, CAM_BUF_INPUT);
+ return -EINVAL;
+ }
+
+ switch (io_buf->io_cfg->resource_type) {
+ case CAM_FD_INPUT_PORT_ID_IMAGE: {
+ if ((num_cmds + 2) > CAM_FD_REG_VAL_PAIR_SIZE) {
+ CAM_ERR(CAM_FD,
+ "Invalid reg_val pair size %d, %d",
+ num_cmds, CAM_FD_REG_VAL_PAIR_SIZE);
+ return -EINVAL;
+ }
+
+ num_cmds = cam_fd_cdm_write_reg_val_pair(
+ reg_val_pair, num_cmds,
+ hw_static_info->core_regs.image_addr,
+ io_buf->io_addr[0]);
+ break;
+ }
+ default:
+ CAM_ERR(CAM_FD, "Invalid resource type %d",
+ io_buf->io_cfg->resource_type);
+ return -EINVAL;
+ }
+ }
+
+ for (i = 0; i < CAM_FD_MAX_IO_BUFFERS; i++) {
+ io_buf = &prestart_args->output_buf[i];
+
+ if (io_buf->valid == false)
+ break;
+
+ if (io_buf->io_cfg->direction != CAM_BUF_OUTPUT) {
+ CAM_ERR(CAM_FD, "Incorrect direction %d %d",
+ io_buf->io_cfg->direction, CAM_BUF_INPUT);
+ return -EINVAL;
+ }
+
+ switch (io_buf->io_cfg->resource_type) {
+ case CAM_FD_OUTPUT_PORT_ID_RESULTS: {
+ uint32_t face_results_offset;
+
+ size_required = hw_static_info->results.max_faces *
+ hw_static_info->results.per_face_entries * 4;
+
+ if (io_buf->io_cfg->planes[0].plane_stride <
+ size_required) {
+ CAM_ERR(CAM_FD, "Invalid results size %d %d",
+ io_buf->io_cfg->planes[0].plane_stride,
+ size_required);
+ return -EINVAL;
+ }
+
+ req_private->fd_results =
+ (struct cam_fd_results *)io_buf->cpu_addr[0];
+
+ face_results_offset =
+ (uint8_t *)&req_private->fd_results->faces[0] -
+ (uint8_t *)req_private->fd_results;
+
+ if (hw_static_info->ro_mode_supported) {
+ if ((num_cmds + 4) > CAM_FD_REG_VAL_PAIR_SIZE) {
+ CAM_ERR(CAM_FD,
+ "Invalid reg_val size %d, %d",
+ num_cmds,
+ CAM_FD_REG_VAL_PAIR_SIZE);
+ return -EINVAL;
+ }
+ /*
+ * Face data actually starts 16bytes later in
+ * the io buffer Check cam_fd_results.
+ */
+ num_cmds = cam_fd_cdm_write_reg_val_pair(
+ reg_val_pair, num_cmds,
+ hw_static_info->core_regs.result_addr,
+ io_buf->io_addr[0] +
+ face_results_offset);
+ num_cmds = cam_fd_cdm_write_reg_val_pair(
+ reg_val_pair, num_cmds,
+ hw_static_info->core_regs.ro_mode,
+ 0x1);
+
+ req_private->ro_mode_enabled = true;
+ } else {
+ req_private->ro_mode_enabled = false;
+ }
+ break;
+ }
+ case CAM_FD_OUTPUT_PORT_ID_RAW_RESULTS: {
+ size_required =
+ hw_static_info->results.raw_results_entries *
+ sizeof(uint32_t);
+
+ if (io_buf->io_cfg->planes[0].plane_stride <
+ size_required) {
+ CAM_ERR(CAM_FD, "Invalid results size %d %d",
+ io_buf->io_cfg->planes[0].plane_stride,
+ size_required);
+ return -EINVAL;
+ }
+
+ req_private->raw_results =
+ (uint32_t *)io_buf->cpu_addr[0];
+ break;
+ }
+ case CAM_FD_OUTPUT_PORT_ID_WORK_BUFFER: {
+ if ((num_cmds + 2) > CAM_FD_REG_VAL_PAIR_SIZE) {
+ CAM_ERR(CAM_FD,
+ "Invalid reg_val pair size %d, %d",
+ num_cmds, CAM_FD_REG_VAL_PAIR_SIZE);
+ return -EINVAL;
+ }
+
+ num_cmds = cam_fd_cdm_write_reg_val_pair(
+ reg_val_pair, num_cmds,
+ hw_static_info->core_regs.work_addr,
+ io_buf->io_addr[0]);
+
+ work_buffer_configured = true;
+ break;
+ }
+ default:
+ CAM_ERR(CAM_FD, "Invalid resource type %d",
+ io_buf->io_cfg->resource_type);
+ return -EINVAL;
+ }
+ }
+
+ if (!req_private->fd_results || !work_buffer_configured) {
+ CAM_ERR(CAM_FD, "Invalid IO Buffers results=%pK work=%d",
+ req_private->fd_results, work_buffer_configured);
+ return -EINVAL;
+ }
+
+ /* First insert CHANGE_BASE command */
+ size = ctx_hw_private->cdm_ops->cdm_required_size_changebase();
+ /* since cdm returns dwords, we need to convert it into bytes */
+ if ((size * 4) > available_size) {
+ CAM_ERR(CAM_FD, "buf size:%d is not sufficient, expected: %d",
+ prestart_args->size, size);
+ return -EINVAL;
+ }
+
+ mem_base = CAM_SOC_GET_REG_MAP_CAM_BASE(soc_info,
+ ((struct cam_fd_soc_private *)soc_info->soc_private)->
+ regbase_index[CAM_FD_REG_CORE]);
+
+ ctx_hw_private->cdm_ops->cdm_write_changebase(cmd_buf_addr, mem_base);
+ cmd_buf_addr += size;
+ available_size -= (size * 4);
+
+ size = ctx_hw_private->cdm_ops->cdm_required_size_reg_random(
+ num_cmds/2);
+ /* cdm util returns dwords, need to convert to bytes */
+ if ((size * 4) > available_size) {
+ CAM_ERR(CAM_FD, "Insufficient size:%d , expected size:%d",
+ available_size, size);
+ return -ENOMEM;
+ }
+ ctx_hw_private->cdm_ops->cdm_write_regrandom(cmd_buf_addr, num_cmds/2,
+ reg_val_pair);
+ cmd_buf_addr += size;
+ available_size -= (size * 4);
+
+ /* Update pre_config_buf_size in bytes */
+ prestart_args->pre_config_buf_size =
+ prestart_args->size - available_size;
+
+ /*
+ * Currently, no post config commands, we trigger HW start directly
+ * from start(). Start trigger command can be inserted into CDM
+ * as post config commands.
+ */
+ prestart_args->post_config_buf_size = 0;
+
+ CAM_DBG(CAM_FD, "PreConfig [%pK %d], PostConfig[%pK %d]",
+ prestart_args->cmd_buf_addr, prestart_args->pre_config_buf_size,
+ cmd_buf_addr, prestart_args->post_config_buf_size);
+
+ for (i = 0; i < (prestart_args->pre_config_buf_size +
+ prestart_args->post_config_buf_size) / 4; i++)
+ CAM_DBG(CAM_FD, "CDM KMD Commands [%d] : [%pK] [0x%x]", i,
+ &prestart_args->cmd_buf_addr[i],
+ prestart_args->cmd_buf_addr[i]);
+
+ return 0;
+}
+
+static int cam_fd_hw_util_processcmd_frame_done(struct cam_hw_info *fd_hw,
+ struct cam_fd_hw_frame_done_args *frame_done_args)
+{
+ struct cam_fd_core *fd_core = (struct cam_fd_core *)fd_hw->core_info;
+ struct cam_fd_hw_static_info *hw_static_info = fd_core->hw_static_info;
+ struct cam_fd_hw_req_private *req_private;
+ uint32_t base, face_cnt;
+ uint32_t *buffer;
+ int i;
+
+ spin_lock(&fd_core->spin_lock);
+ if ((fd_core->core_state != CAM_FD_CORE_STATE_IDLE) ||
+ (fd_core->results_valid == false) ||
+ !fd_core->hw_req_private) {
+ CAM_ERR(CAM_FD,
+ "Invalid state for results state=%d, results=%d %pK",
+ fd_core->core_state, fd_core->results_valid,
+ fd_core->hw_req_private);
+ spin_unlock(&fd_core->spin_lock);
+ return -EINVAL;
+ }
+ fd_core->core_state = CAM_FD_CORE_STATE_READING_RESULTS;
+ req_private = fd_core->hw_req_private;
+ spin_unlock(&fd_core->spin_lock);
+
+ /*
+ * Copy the register value as is into output buffers.
+ * Wehter we are copying the output data by reading registers or
+ * programming output buffer directly to HW must be transparent to UMD.
+ * In case HW supports writing face count value directly into
+ * DDR memory in future, these values should match.
+ */
+ req_private->fd_results->face_count =
+ cam_fd_soc_register_read(&fd_hw->soc_info, CAM_FD_REG_CORE,
+ hw_static_info->core_regs.result_cnt);
+
+ face_cnt = req_private->fd_results->face_count & 0x3F;
+
+ if (face_cnt > hw_static_info->results.max_faces) {
+ CAM_WARN(CAM_FD, "Face count greater than max %d %d",
+ face_cnt, hw_static_info->results.max_faces);
+ face_cnt = hw_static_info->results.max_faces;
+ }
+
+ CAM_DBG(CAM_FD, "ReqID[%lld] Faces Detected = %d",
+ req_private->request_id, face_cnt);
+
+ /*
+ * We need to read the face data information from registers only
+ * if one of below is true
+ * 1. RO mode is not set. i.e FD HW doesn't write face data into
+ * DDR memory
+ * 2. On the current chipset, results written into DDR memory by FD HW
+ * are not gauranteed to be correct
+ */
+ if (!req_private->ro_mode_enabled ||
+ hw_static_info->enable_errata_wa.ro_mode_results_invalid) {
+ buffer = (uint32_t *)&req_private->fd_results->faces[0];
+ base = hw_static_info->core_regs.results_reg_base;
+
+ /*
+ * Write register values as is into face data buffer. Its UMD
+ * driver responsibility to interpret the data and extract face
+ * properties from output buffer. Think in case output buffer
+ * is directly programmed to HW, then KMD has no control to
+ * extract the face properties and UMD anyway has to extract
+ * face properties. So we follow the same approach and keep
+ * this transparent to UMD.
+ */
+ for (i = 0;
+ i < (face_cnt *
+ hw_static_info->results.per_face_entries); i++) {
+ *buffer = cam_fd_soc_register_read(&fd_hw->soc_info,
+ CAM_FD_REG_CORE, base + (i * 0x4));
+ CAM_DBG(CAM_FD, "FaceData[%d] : 0x%x", i / 4, *buffer);
+ buffer++;
+ }
+ }
+
+ if (req_private->get_raw_results &&
+ req_private->raw_results &&
+ hw_static_info->results.raw_results_available) {
+ buffer = req_private->raw_results;
+ base = hw_static_info->core_regs.raw_results_reg_base;
+
+ for (i = 0;
+ i < hw_static_info->results.raw_results_entries;
+ i++) {
+ *buffer = cam_fd_soc_register_read(&fd_hw->soc_info,
+ CAM_FD_REG_CORE, base + (i * 0x4));
+ CAM_DBG(CAM_FD, "RawData[%d] : 0x%x", i, *buffer);
+ buffer++;
+ }
+ }
+
+ spin_lock(&fd_core->spin_lock);
+ fd_core->hw_req_private = NULL;
+ fd_core->core_state = CAM_FD_CORE_STATE_IDLE;
+ spin_unlock(&fd_core->spin_lock);
+
+ return 0;
+}
+
+irqreturn_t cam_fd_hw_irq(int irq_num, void *data)
+{
+ struct cam_hw_info *fd_hw = (struct cam_hw_info *)data;
+ struct cam_fd_core *fd_core;
+ struct cam_hw_soc_info *soc_info;
+ struct cam_fd_hw_static_info *hw_static_info;
+ uint32_t reg_value;
+ enum cam_fd_hw_irq_type irq_type = CAM_FD_IRQ_FRAME_DONE;
+ uint32_t num_irqs = 0;
+
+ if (!fd_hw) {
+ CAM_ERR(CAM_FD, "Invalid data in IRQ callback");
+ return -EINVAL;
+ }
+
+ fd_core = (struct cam_fd_core *) fd_hw->core_info;
+ soc_info = &fd_hw->soc_info;
+ hw_static_info = fd_core->hw_static_info;
+
+ reg_value = cam_fd_soc_register_read(soc_info, CAM_FD_REG_WRAPPER,
+ hw_static_info->wrapper_regs.irq_status);
+
+ CAM_DBG(CAM_FD, "FD IRQ status 0x%x", reg_value);
+
+ if (reg_value & CAM_FD_IRQ_TO_MASK(CAM_FD_IRQ_HALT_DONE)) {
+ complete_all(&fd_core->halt_complete);
+ irq_type = CAM_FD_IRQ_HALT_DONE;
+ num_irqs++;
+ }
+
+ if (reg_value & CAM_FD_IRQ_TO_MASK(CAM_FD_IRQ_RESET_DONE)) {
+ complete_all(&fd_core->reset_complete);
+ irq_type = CAM_FD_IRQ_RESET_DONE;
+ num_irqs++;
+ }
+
+ if (reg_value & CAM_FD_IRQ_TO_MASK(CAM_FD_IRQ_FRAME_DONE)) {
+ complete_all(&fd_core->processing_complete);
+ irq_type = CAM_FD_IRQ_FRAME_DONE;
+ num_irqs++;
+ }
+
+ /*
+ * We should never get an IRQ callback with no or more than one mask.
+ * Validate first to make sure nothing going wrong.
+ */
+ if (num_irqs != 1) {
+ CAM_ERR(CAM_FD,
+ "Invalid number of IRQs, value=0x%x, num_irqs=%d",
+ reg_value, num_irqs);
+ return -EINVAL;
+ }
+
+ cam_fd_soc_register_write(soc_info, CAM_FD_REG_WRAPPER,
+ hw_static_info->wrapper_regs.irq_clear,
+ hw_static_info->irq_mask);
+
+ if (irq_type == CAM_FD_IRQ_HALT_DONE) {
+ /*
+ * Do not send HALT IRQ callback to Hw Mgr,
+ * a reset would always follow
+ */
+ return IRQ_HANDLED;
+ }
+
+ spin_lock(&fd_core->spin_lock);
+ /* Do not change state to IDLE on HALT IRQ. Reset must follow halt */
+ if ((irq_type == CAM_FD_IRQ_RESET_DONE) ||
+ (irq_type == CAM_FD_IRQ_FRAME_DONE)) {
+
+ fd_core->core_state = CAM_FD_CORE_STATE_IDLE;
+ if (irq_type == CAM_FD_IRQ_FRAME_DONE)
+ fd_core->results_valid = true;
+
+ CAM_DBG(CAM_FD, "FD IRQ type %d, state=%d",
+ irq_type, fd_core->core_state);
+ }
+ spin_unlock(&fd_core->spin_lock);
+
+ if (fd_core->irq_cb.cam_fd_hw_mgr_cb)
+ fd_core->irq_cb.cam_fd_hw_mgr_cb(fd_core->irq_cb.data,
+ irq_type);
+
+ return IRQ_HANDLED;
+}
+
+int cam_fd_hw_get_hw_caps(void *hw_priv, void *get_hw_cap_args,
+ uint32_t arg_size)
+{
+ struct cam_hw_info *fd_hw = (struct cam_hw_info *)hw_priv;
+ struct cam_fd_core *fd_core;
+ struct cam_fd_hw_caps *fd_hw_caps =
+ (struct cam_fd_hw_caps *)get_hw_cap_args;
+
+ if (!hw_priv || !get_hw_cap_args) {
+ CAM_ERR(CAM_FD, "Invalid input pointers %pK %pK",
+ hw_priv, get_hw_cap_args);
+ return -EINVAL;
+ }
+
+ fd_core = (struct cam_fd_core *)fd_hw->core_info;
+ *fd_hw_caps = fd_core->hw_caps;
+
+ CAM_DBG(CAM_FD, "core:%d.%d wrapper:%d.%d mode:%d, raw:%d",
+ fd_hw_caps->core_version.major,
+ fd_hw_caps->core_version.minor,
+ fd_hw_caps->wrapper_version.major,
+ fd_hw_caps->wrapper_version.minor,
+ fd_hw_caps->supported_modes,
+ fd_hw_caps->raw_results_available);
+
+ return 0;
+}
+
+int cam_fd_hw_init(void *hw_priv, void *init_hw_args, uint32_t arg_size)
+{
+ struct cam_hw_info *fd_hw = (struct cam_hw_info *)hw_priv;
+ struct cam_fd_core *fd_core;
+ struct cam_fd_hw_init_args *init_args =
+ (struct cam_fd_hw_init_args *)init_hw_args;
+ int rc = 0;
+
+ if (!fd_hw || !init_args) {
+ CAM_ERR(CAM_FD, "Invalid argument %pK %pK", fd_hw, init_args);
+ return -EINVAL;
+ }
+
+ if (arg_size != sizeof(struct cam_fd_hw_init_args)) {
+ CAM_ERR(CAM_FD, "Invalid arg size %d, %d", arg_size,
+ sizeof(struct cam_fd_hw_init_args));
+ return -EINVAL;
+ }
+
+ fd_core = (struct cam_fd_core *)fd_hw->core_info;
+
+ mutex_lock(&fd_hw->hw_mutex);
+ CAM_DBG(CAM_FD, "FD HW Init ref count before %d", fd_hw->open_count);
+
+ if (fd_hw->open_count > 0) {
+ rc = 0;
+ mutex_unlock(&fd_hw->hw_mutex);
+ goto cdm_streamon;
+ }
+
+ rc = cam_fd_soc_enable_resources(&fd_hw->soc_info);
+ if (rc) {
+ CAM_ERR(CAM_FD, "Enable SOC failed, rc=%d", rc);
+ goto unlock_return;
+ }
+
+ rc = cam_fd_hw_reset(hw_priv, NULL, 0);
+ if (rc) {
+ CAM_ERR(CAM_FD, "Reset Failed, rc=%d", rc);
+ goto disable_soc;
+ }
+
+ cam_fd_hw_util_enable_power_on_settings(fd_hw);
+
+ fd_hw->hw_state = CAM_HW_STATE_POWER_UP;
+ fd_core->core_state = CAM_FD_CORE_STATE_IDLE;
+ fd_hw->open_count++;
+ CAM_DBG(CAM_FD, "FD HW Init ref count after %d", fd_hw->open_count);
+
+ mutex_unlock(&fd_hw->hw_mutex);
+
+cdm_streamon:
+ if (init_args->ctx_hw_private) {
+ struct cam_fd_ctx_hw_private *ctx_hw_private =
+ init_args->ctx_hw_private;
+
+ rc = cam_cdm_stream_on(ctx_hw_private->cdm_handle);
+ if (rc) {
+ CAM_ERR(CAM_FD, "CDM StreamOn fail :handle=0x%x, rc=%d",
+ ctx_hw_private->cdm_handle, rc);
+ return rc;
+ }
+ }
+
+ return rc;
+
+disable_soc:
+ if (cam_fd_soc_disable_resources(&fd_hw->soc_info))
+ CAM_ERR(CAM_FD, "Error in disable soc resources");
+unlock_return:
+ mutex_unlock(&fd_hw->hw_mutex);
+ return rc;
+}
+
+int cam_fd_hw_deinit(void *hw_priv, void *deinit_hw_args, uint32_t arg_size)
+{
+ struct cam_hw_info *fd_hw = hw_priv;
+ struct cam_fd_core *fd_core;
+ struct cam_fd_hw_deinit_args *deinit_args =
+ (struct cam_fd_hw_deinit_args *)deinit_hw_args;
+ int rc = 0;
+
+ if (!fd_hw || !deinit_hw_args) {
+ CAM_ERR(CAM_FD, "Invalid argument");
+ return -EINVAL;
+ }
+
+ if (arg_size != sizeof(struct cam_fd_hw_deinit_args)) {
+ CAM_ERR(CAM_FD, "Invalid arg size %d, %d", arg_size,
+ sizeof(struct cam_fd_hw_deinit_args));
+ return -EINVAL;
+ }
+
+ fd_core = (struct cam_fd_core *)fd_hw->core_info;
+
+ if (deinit_args->ctx_hw_private) {
+ struct cam_fd_ctx_hw_private *ctx_hw_private =
+ deinit_args->ctx_hw_private;
+
+ rc = cam_cdm_stream_off(ctx_hw_private->cdm_handle);
+ if (rc) {
+ CAM_ERR(CAM_FD,
+ "Failed in CDM StreamOff, handle=0x%x, rc=%d",
+ ctx_hw_private->cdm_handle, rc);
+ return rc;
+ }
+ }
+
+ mutex_lock(&fd_hw->hw_mutex);
+
+ if (fd_hw->open_count == 0) {
+ mutex_unlock(&fd_hw->hw_mutex);
+ CAM_ERR(CAM_FD, "Error Unbalanced deinit");
+ return -EFAULT;
+ }
+
+ fd_hw->open_count--;
+ CAM_DBG(CAM_FD, "FD HW ref count=%d", fd_hw->open_count);
+
+ if (fd_hw->open_count) {
+ rc = 0;
+ goto unlock_return;
+ }
+
+ rc = cam_fd_soc_disable_resources(&fd_hw->soc_info);
+ if (rc)
+ CAM_ERR(CAM_FD, "Failed in Disable SOC, rc=%d", rc);
+
+ fd_hw->hw_state = CAM_HW_STATE_POWER_DOWN;
+ fd_core->core_state = CAM_FD_CORE_STATE_POWERDOWN;
+
+unlock_return:
+ mutex_unlock(&fd_hw->hw_mutex);
+ return rc;
+}
+
+int cam_fd_hw_reset(void *hw_priv, void *reset_core_args, uint32_t arg_size)
+{
+ struct cam_hw_info *fd_hw = (struct cam_hw_info *)hw_priv;
+ struct cam_fd_core *fd_core;
+ int rc;
+
+ if (!fd_hw) {
+ CAM_ERR(CAM_FD, "Invalid input handle");
+ return -EINVAL;
+ }
+
+ fd_core = (struct cam_fd_core *)fd_hw->core_info;
+
+ spin_lock(&fd_core->spin_lock);
+ if (fd_core->core_state == CAM_FD_CORE_STATE_RESET_PROGRESS) {
+ CAM_ERR(CAM_FD, "Reset not allowed in %d state",
+ fd_core->core_state);
+ spin_unlock(&fd_core->spin_lock);
+ return -EINVAL;
+ }
+
+ fd_core->results_valid = false;
+ fd_core->core_state = CAM_FD_CORE_STATE_RESET_PROGRESS;
+ spin_unlock(&fd_core->spin_lock);
+
+ rc = cam_fd_hw_util_fdwrapper_sync_reset(fd_hw);
+ if (rc) {
+ CAM_ERR(CAM_FD, "Failed in RESET rc=%d", rc);
+ return rc;
+ }
+
+ spin_lock(&fd_core->spin_lock);
+ fd_core->core_state = CAM_FD_CORE_STATE_IDLE;
+ spin_unlock(&fd_core->spin_lock);
+
+ return rc;
+}
+
+int cam_fd_hw_start(void *hw_priv, void *hw_start_args, uint32_t arg_size)
+{
+ struct cam_hw_info *fd_hw = (struct cam_hw_info *)hw_priv;
+ struct cam_fd_core *fd_core;
+ struct cam_fd_hw_static_info *hw_static_info;
+ struct cam_fd_hw_cmd_start_args *start_args =
+ (struct cam_fd_hw_cmd_start_args *)hw_start_args;
+ struct cam_fd_ctx_hw_private *ctx_hw_private;
+ int rc;
+
+ if (!hw_priv || !start_args) {
+ CAM_ERR(CAM_FD, "Invalid input args %pK %pK", hw_priv,
+ start_args);
+ return -EINVAL;
+ }
+
+ if (arg_size != sizeof(struct cam_fd_hw_cmd_start_args)) {
+ CAM_ERR(CAM_FD, "Invalid arg size %d, %d", arg_size,
+ sizeof(struct cam_fd_hw_cmd_start_args));
+ return -EINVAL;
+ }
+
+ fd_core = (struct cam_fd_core *)fd_hw->core_info;
+ hw_static_info = fd_core->hw_static_info;
+
+ spin_lock(&fd_core->spin_lock);
+ if (fd_core->core_state != CAM_FD_CORE_STATE_IDLE) {
+ CAM_ERR(CAM_FD, "Cannot start in %d state",
+ fd_core->core_state);
+ spin_unlock(&fd_core->spin_lock);
+ return -EINVAL;
+ }
+
+ /*
+ * We are about to start FD HW processing, save the request
+ * private data which is being processed by HW. Once the frame
+ * processing is finished, process_cmd(FRAME_DONE) should be called
+ * with same hw_req_private as input.
+ */
+ fd_core->hw_req_private = start_args->hw_req_private;
+ fd_core->core_state = CAM_FD_CORE_STATE_PROCESSING;
+ fd_core->results_valid = false;
+ spin_unlock(&fd_core->spin_lock);
+
+ ctx_hw_private = start_args->ctx_hw_private;
+
+ /* Before starting HW process, clear processing complete */
+ reinit_completion(&fd_core->processing_complete);
+
+ if (hw_static_info->enable_errata_wa.single_irq_only) {
+ cam_fd_soc_register_write(&fd_hw->soc_info, CAM_FD_REG_WRAPPER,
+ hw_static_info->wrapper_regs.irq_mask,
+ CAM_FD_IRQ_TO_MASK(CAM_FD_IRQ_FRAME_DONE));
+ }
+
+ if (start_args->num_hw_update_entries > 0) {
+ struct cam_cdm_bl_request *cdm_cmd = ctx_hw_private->cdm_cmd;
+ struct cam_hw_update_entry *cmd;
+ int i;
+
+ cdm_cmd->cmd_arrary_count = start_args->num_hw_update_entries;
+ cdm_cmd->type = CAM_CDM_BL_CMD_TYPE_MEM_HANDLE;
+ cdm_cmd->flag = false;
+ cdm_cmd->userdata = NULL;
+ cdm_cmd->cookie = 0;
+
+ for (i = 0 ; i <= start_args->num_hw_update_entries; i++) {
+ cmd = (start_args->hw_update_entries + i);
+ cdm_cmd->cmd[i].bl_addr.mem_handle = cmd->handle;
+ cdm_cmd->cmd[i].offset = cmd->offset;
+ cdm_cmd->cmd[i].len = cmd->len;
+ }
+
+ rc = cam_cdm_submit_bls(ctx_hw_private->cdm_handle, cdm_cmd);
+ if (rc) {
+ CAM_ERR(CAM_FD,
+ "Failed to submit cdm commands, rc=%d", rc);
+ goto error;
+ }
+ } else {
+ CAM_ERR(CAM_FD, "Invalid number of hw update entries");
+ rc = -EINVAL;
+ goto error;
+ }
+
+ cam_fd_soc_register_write(&fd_hw->soc_info, CAM_FD_REG_CORE,
+ hw_static_info->core_regs.control, 0x2);
+
+ return 0;
+error:
+ spin_lock(&fd_core->spin_lock);
+ fd_core->core_state = CAM_FD_CORE_STATE_IDLE;
+ spin_unlock(&fd_core->spin_lock);
+
+ return rc;
+}
+
+int cam_fd_hw_halt_reset(void *hw_priv, void *stop_args, uint32_t arg_size)
+{
+ struct cam_hw_info *fd_hw = (struct cam_hw_info *)hw_priv;
+ struct cam_fd_core *fd_core;
+ int rc;
+
+ if (!fd_hw) {
+ CAM_ERR(CAM_FD, "Invalid input handle");
+ return -EINVAL;
+ }
+
+ fd_core = (struct cam_fd_core *)fd_hw->core_info;
+
+ spin_lock(&fd_core->spin_lock);
+ if ((fd_core->core_state == CAM_FD_CORE_STATE_POWERDOWN) ||
+ (fd_core->core_state == CAM_FD_CORE_STATE_RESET_PROGRESS)) {
+ CAM_ERR(CAM_FD, "Reset not allowed in %d state",
+ fd_core->core_state);
+ spin_unlock(&fd_core->spin_lock);
+ return -EINVAL;
+ }
+
+ fd_core->results_valid = false;
+ fd_core->core_state = CAM_FD_CORE_STATE_RESET_PROGRESS;
+ spin_unlock(&fd_core->spin_lock);
+
+ rc = cam_fd_hw_util_fdwrapper_halt(fd_hw);
+ if (rc) {
+ CAM_ERR(CAM_FD, "Failed in HALT rc=%d", rc);
+ return rc;
+ }
+
+ /* HALT must be followed by RESET */
+ rc = cam_fd_hw_util_fdwrapper_sync_reset(fd_hw);
+ if (rc) {
+ CAM_ERR(CAM_FD, "Failed in RESET rc=%d", rc);
+ return rc;
+ }
+
+ spin_lock(&fd_core->spin_lock);
+ fd_core->core_state = CAM_FD_CORE_STATE_IDLE;
+ spin_unlock(&fd_core->spin_lock);
+
+ return rc;
+}
+
+int cam_fd_hw_reserve(void *hw_priv, void *hw_reserve_args, uint32_t arg_size)
+{
+ struct cam_hw_info *fd_hw = (struct cam_hw_info *)hw_priv;
+ int rc = -EINVAL;
+ struct cam_fd_ctx_hw_private *ctx_hw_private;
+ struct cam_fd_hw_reserve_args *reserve_args =
+ (struct cam_fd_hw_reserve_args *)hw_reserve_args;
+ struct cam_cdm_acquire_data cdm_acquire;
+ struct cam_cdm_bl_request *cdm_cmd;
+ int i;
+
+ if (!fd_hw || !reserve_args) {
+ CAM_ERR(CAM_FD, "Invalid input %pK, %pK", fd_hw, reserve_args);
+ return -EINVAL;
+ }
+
+ if (arg_size != sizeof(struct cam_fd_hw_reserve_args)) {
+ CAM_ERR(CAM_FD, "Invalid arg size %d, %d", arg_size,
+ sizeof(struct cam_fd_hw_reserve_args));
+ return -EINVAL;
+ }
+
+ cdm_cmd = kzalloc(((sizeof(struct cam_cdm_bl_request)) +
+ ((CAM_FD_MAX_HW_ENTRIES - 1) *
+ sizeof(struct cam_cdm_bl_cmd))), GFP_KERNEL);
+ if (!cdm_cmd)
+ return -ENOMEM;
+
+ ctx_hw_private = kzalloc(sizeof(struct cam_fd_ctx_hw_private),
+ GFP_KERNEL);
+ if (!ctx_hw_private) {
+ kfree(cdm_cmd);
+ return -ENOMEM;
+ }
+
+ memset(&cdm_acquire, 0, sizeof(cdm_acquire));
+ strlcpy(cdm_acquire.identifier, "fd", sizeof("fd"));
+ cdm_acquire.cell_index = fd_hw->soc_info.index;
+ cdm_acquire.handle = 0;
+ cdm_acquire.userdata = ctx_hw_private;
+ cdm_acquire.cam_cdm_callback = cam_fd_hw_util_cdm_callback;
+ cdm_acquire.id = CAM_CDM_VIRTUAL;
+ cdm_acquire.base_array_cnt = fd_hw->soc_info.num_reg_map;
+ for (i = 0; i < fd_hw->soc_info.num_reg_map; i++)
+ cdm_acquire.base_array[i] = &fd_hw->soc_info.reg_map[i];
+
+ rc = cam_cdm_acquire(&cdm_acquire);
+ if (rc) {
+ CAM_ERR(CAM_FD, "Failed to acquire the CDM HW");
+ goto error;
+ }
+
+ ctx_hw_private->hw_ctx = reserve_args->hw_ctx;
+ ctx_hw_private->fd_hw = fd_hw;
+ ctx_hw_private->mode = reserve_args->mode;
+ ctx_hw_private->cdm_handle = cdm_acquire.handle;
+ ctx_hw_private->cdm_ops = cdm_acquire.ops;
+ ctx_hw_private->cdm_cmd = cdm_cmd;
+
+ reserve_args->ctx_hw_private = ctx_hw_private;
+
+ CAM_DBG(CAM_FD, "private=%pK, hw_ctx=%pK, mode=%d, cdm_handle=0x%x",
+ ctx_hw_private, ctx_hw_private->hw_ctx, ctx_hw_private->mode,
+ ctx_hw_private->cdm_handle);
+
+ return 0;
+error:
+ kfree(ctx_hw_private);
+ kfree(cdm_cmd);
+ return rc;
+}
+
+int cam_fd_hw_release(void *hw_priv, void *hw_release_args, uint32_t arg_size)
+{
+ struct cam_hw_info *fd_hw = (struct cam_hw_info *)hw_priv;
+ int rc = -EINVAL;
+ struct cam_fd_ctx_hw_private *ctx_hw_private;
+ struct cam_fd_hw_release_args *release_args =
+ (struct cam_fd_hw_release_args *)hw_release_args;
+
+ if (!fd_hw || !release_args) {
+ CAM_ERR(CAM_FD, "Invalid input %pK, %pK", fd_hw, release_args);
+ return -EINVAL;
+ }
+
+ if (arg_size != sizeof(struct cam_fd_hw_release_args)) {
+ CAM_ERR(CAM_FD, "Invalid arg size %d, %d", arg_size,
+ sizeof(struct cam_fd_hw_release_args));
+ return -EINVAL;
+ }
+
+ ctx_hw_private =
+ (struct cam_fd_ctx_hw_private *)release_args->ctx_hw_private;
+
+ rc = cam_cdm_release(ctx_hw_private->cdm_handle);
+ if (rc)
+ CAM_ERR(CAM_FD, "Release cdm handle failed, handle=0x%x, rc=%d",
+ ctx_hw_private->cdm_handle, rc);
+
+ kfree(ctx_hw_private);
+ release_args->ctx_hw_private = NULL;
+
+ return 0;
+}
+
+int cam_fd_hw_process_cmd(void *hw_priv, uint32_t cmd_type,
+ void *cmd_args, uint32_t arg_size)
+{
+ struct cam_hw_info *fd_hw = (struct cam_hw_info *)hw_priv;
+ int rc = -EINVAL;
+
+ if (!hw_priv || !cmd_args ||
+ (cmd_type >= CAM_FD_HW_CMD_MAX)) {
+ CAM_ERR(CAM_FD, "Invalid arguments %pK %pK %d", hw_priv,
+ cmd_args, cmd_type);
+ return -EINVAL;
+ }
+
+ switch (cmd_type) {
+ case CAM_FD_HW_CMD_REGISTER_CALLBACK: {
+ struct cam_fd_hw_cmd_set_irq_cb *irq_cb_args;
+ struct cam_fd_core *fd_core =
+ (struct cam_fd_core *)fd_hw->core_info;
+
+ if (sizeof(struct cam_fd_hw_cmd_set_irq_cb) != arg_size) {
+ CAM_ERR(CAM_FD, "cmd_type %d, size mismatch %d",
+ cmd_type, arg_size);
+ break;
+ }
+
+ irq_cb_args = (struct cam_fd_hw_cmd_set_irq_cb *)cmd_args;
+ fd_core->irq_cb.cam_fd_hw_mgr_cb =
+ irq_cb_args->cam_fd_hw_mgr_cb;
+ fd_core->irq_cb.data = irq_cb_args->data;
+ rc = 0;
+ break;
+ }
+ case CAM_FD_HW_CMD_PRESTART: {
+ struct cam_fd_hw_cmd_prestart_args *prestart_args;
+
+ if (sizeof(struct cam_fd_hw_cmd_prestart_args) != arg_size) {
+ CAM_ERR(CAM_FD, "cmd_type %d, size mismatch %d",
+ cmd_type, arg_size);
+ break;
+ }
+
+ prestart_args = (struct cam_fd_hw_cmd_prestart_args *)cmd_args;
+ rc = cam_fd_hw_util_processcmd_prestart(fd_hw, prestart_args);
+ break;
+ }
+ case CAM_FD_HW_CMD_FRAME_DONE: {
+ struct cam_fd_hw_frame_done_args *cmd_frame_results;
+
+ if (sizeof(struct cam_fd_hw_frame_done_args) !=
+ arg_size) {
+ CAM_ERR(CAM_FD, "cmd_type %d, size mismatch %d",
+ cmd_type, arg_size);
+ break;
+ }
+
+ cmd_frame_results =
+ (struct cam_fd_hw_frame_done_args *)cmd_args;
+ rc = cam_fd_hw_util_processcmd_frame_done(fd_hw,
+ cmd_frame_results);
+ break;
+ }
+ default:
+ break;
+ }
+
+ return rc;
+}
diff --git a/drivers/media/platform/msm/camera/cam_fd/fd_hw_mgr/fd_hw/cam_fd_hw_core.h b/drivers/media/platform/msm/camera/cam_fd/fd_hw_mgr/fd_hw/cam_fd_hw_core.h
new file mode 100644
index 0000000..35bf6b6
--- /dev/null
+++ b/drivers/media/platform/msm/camera/cam_fd/fd_hw_mgr/fd_hw/cam_fd_hw_core.h
@@ -0,0 +1,244 @@
+/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _CAM_FD_HW_CORE_H_
+#define _CAM_FD_HW_CORE_H_
+
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <media/cam_defs.h>
+#include <media/cam_fd.h>
+
+#include "cam_common_util.h"
+#include "cam_debug_util.h"
+#include "cam_io_util.h"
+#include "cam_cpas_api.h"
+#include "cam_cdm_intf_api.h"
+#include "cam_fd_hw_intf.h"
+#include "cam_fd_hw_soc.h"
+
+#define CAM_FD_IRQ_TO_MASK(irq) (1 << (irq))
+#define CAM_FD_MASK_TO_IRQ(mask, irq) ((mask) >> (irq))
+
+#define CAM_FD_HW_HALT_RESET_TIMEOUT 3000
+
+/**
+ * enum cam_fd_core_state - FD Core internal states
+ *
+ * @CAM_FD_CORE_STATE_POWERDOWN : Indicates FD core is powered down
+ * @CAM_FD_CORE_STATE_IDLE : Indicates FD HW is in idle state.
+ * Core can be in this state when it is
+ * ready to process frames or when
+ * processing is finished and results are
+ * available
+ * @CAM_FD_CORE_STATE_PROCESSING : Indicates FD core is processing frame
+ * @CAM_FD_CORE_STATE_READING_RESULTS : Indicates results are being read from
+ * FD core
+ * @CAM_FD_CORE_STATE_RESET_PROGRESS : Indicates FD Core is in reset state
+ */
+enum cam_fd_core_state {
+ CAM_FD_CORE_STATE_POWERDOWN,
+ CAM_FD_CORE_STATE_IDLE,
+ CAM_FD_CORE_STATE_PROCESSING,
+ CAM_FD_CORE_STATE_READING_RESULTS,
+ CAM_FD_CORE_STATE_RESET_PROGRESS,
+};
+
+/**
+ * struct cam_fd_ctx_hw_private : HW private information for a specific hw ctx.
+ * This information is populated by HW layer on
+ * reserve() and given back to HW Mgr as private
+ * data for the hw context. This private_data
+ * has to be passed by HW Mgr layer while
+ * further HW layer calls
+ *
+ * @hw_ctx : Corresponding hw_ctx pointer
+ * @fd_hw : FD HW info pointer
+ * @cdm_handle : CDM Handle for this context
+ * @cdm_ops : CDM Ops
+ * @cdm_cmd : CDM command pointer
+ * @mode : Mode this context is running
+ * @curr_req_private : Current Request information
+ *
+ */
+struct cam_fd_ctx_hw_private {
+ void *hw_ctx;
+ struct cam_hw_info *fd_hw;
+ uint32_t cdm_handle;
+ struct cam_cdm_utils_ops *cdm_ops;
+ struct cam_cdm_bl_request *cdm_cmd;
+ enum cam_fd_hw_mode mode;
+ struct cam_fd_hw_req_private *curr_req_private;
+};
+
+/**
+ * struct cam_fd_core_regs : FD HW Core register offsets info
+ *
+ * @version : Offset of version register
+ * @control : Offset of control register
+ * @result_cnt : Offset of result count register
+ * @result_addr : Offset of results address register
+ * @image_addr : Offset of image address register
+ * @work_addr : Offset of work address register
+ * @ro_mode : Offset of ro_mode register
+ * @results_reg_base : Offset of results_reg_base register
+ * @raw_results_reg_base : Offset of raw_results_reg_base register
+ *
+ */
+struct cam_fd_core_regs {
+ uint32_t version;
+ uint32_t control;
+ uint32_t result_cnt;
+ uint32_t result_addr;
+ uint32_t image_addr;
+ uint32_t work_addr;
+ uint32_t ro_mode;
+ uint32_t results_reg_base;
+ uint32_t raw_results_reg_base;
+};
+
+/**
+ * struct cam_fd_core_regs : FD HW Wrapper register offsets info
+ *
+ * @wrapper_version : Offset of wrapper_version register
+ * @cgc_disable : Offset of cgc_disable register
+ * @hw_stop : Offset of hw_stop register
+ * @sw_reset : Offset of sw_reset register
+ * @vbif_req_priority : Offset of vbif_req_priority register
+ * @vbif_priority_level : Offset of vbif_priority_level register
+ * @vbif_done_status : Offset of vbif_done_status register
+ * @irq_mask : Offset of irq mask register
+ * @irq_status : Offset of irq status register
+ * @irq_clear : Offset of irq clear register
+ *
+ */
+struct cam_fd_wrapper_regs {
+ uint32_t wrapper_version;
+ uint32_t cgc_disable;
+ uint32_t hw_stop;
+ uint32_t sw_reset;
+ uint32_t vbif_req_priority;
+ uint32_t vbif_priority_level;
+ uint32_t vbif_done_status;
+ uint32_t irq_mask;
+ uint32_t irq_status;
+ uint32_t irq_clear;
+};
+
+/**
+ * struct cam_fd_hw_errata_wa : FD HW Errata workaround enable/dsiable info
+ *
+ * @single_irq_only : Whether to enable only one irq at any time
+ * @ro_mode_enable_always : Whether to enable ro mode always
+ * @ro_mode_results_invalid : Whether results written directly into output
+ * memory by HW are valid or not
+ */
+struct cam_fd_hw_errata_wa {
+ bool single_irq_only;
+ bool ro_mode_enable_always;
+ bool ro_mode_results_invalid;
+};
+
+/**
+ * struct cam_fd_hw_results_prop : FD HW Results properties
+ *
+ * @max_faces : Maximum number of faces supported
+ * @per_face_entries : Number of register with properties for each face
+ * @raw_results_entries : Number of raw results entries for the full search
+ * @raw_results_available : Whether raw results available on this HW
+ *
+ */
+struct cam_fd_hw_results_prop {
+ uint32_t max_faces;
+ uint32_t per_face_entries;
+ uint32_t raw_results_entries;
+ bool raw_results_available;
+};
+
+/**
+ * struct cam_fd_hw_static_info : FD HW information based on HW version
+ *
+ * @core_version : Core version of FD HW
+ * @wrapper_version : Wrapper version of FD HW
+ * @core_regs : Register offset information for core registers
+ * @wrapper_regs : Register offset information for wrapper registers
+ * @results : Information about results available on this HW
+ * @enable_errata_wa : Errata workaround information
+ * @irq_mask : IRQ mask to enable
+ * @qos_priority : QoS priority setting for this chipset
+ * @qos_priority_level : QoS priority level setting for this chipset
+ * @supported_modes : Supported HW modes on this HW version
+ * @ro_mode_supported : Whether RO mode is supported on this HW
+ *
+ */
+struct cam_fd_hw_static_info {
+ struct cam_hw_version core_version;
+ struct cam_hw_version wrapper_version;
+ struct cam_fd_core_regs core_regs;
+ struct cam_fd_wrapper_regs wrapper_regs;
+ struct cam_fd_hw_results_prop results;
+ struct cam_fd_hw_errata_wa enable_errata_wa;
+ uint32_t irq_mask;
+ uint32_t qos_priority;
+ uint32_t qos_priority_level;
+ uint32_t supported_modes;
+ bool ro_mode_supported;
+};
+
+/**
+ * struct cam_fd_core : FD HW core data structure
+ *
+ * @hw_static_info : HW information specific to version
+ * @hw_caps : HW capabilities
+ * @core_state : Current HW state
+ * @processing_complete : Whether processing is complete
+ * @reset_complete : Whether reset is complete
+ * @halt_complete : Whether halt is complete
+ * @hw_req_private : Request that is being currently processed by HW
+ * @results_valid : Whether HW frame results are available to get
+ * @spin_lock : Mutex to protect shared data in hw layer
+ * @irq_cb : HW Manager callback information
+ *
+ */
+struct cam_fd_core {
+ struct cam_fd_hw_static_info *hw_static_info;
+ struct cam_fd_hw_caps hw_caps;
+ enum cam_fd_core_state core_state;
+ struct completion processing_complete;
+ struct completion reset_complete;
+ struct completion halt_complete;
+ struct cam_fd_hw_req_private *hw_req_private;
+ bool results_valid;
+ spinlock_t spin_lock;
+ struct cam_fd_hw_cmd_set_irq_cb irq_cb;
+};
+
+int cam_fd_hw_util_get_hw_caps(struct cam_hw_info *fd_hw,
+ struct cam_fd_hw_caps *hw_caps);
+irqreturn_t cam_fd_hw_irq(int irq_num, void *data);
+
+int cam_fd_hw_get_hw_caps(void *hw_priv, void *get_hw_cap_args,
+ uint32_t arg_size);
+int cam_fd_hw_init(void *hw_priv, void *init_hw_args, uint32_t arg_size);
+int cam_fd_hw_deinit(void *hw_priv, void *deinit_hw_args, uint32_t arg_size);
+int cam_fd_hw_reset(void *hw_priv, void *reset_core_args, uint32_t arg_size);
+int cam_fd_hw_reserve(void *hw_priv, void *hw_reserve_args, uint32_t arg_size);
+int cam_fd_hw_release(void *hw_priv, void *hw_release_args, uint32_t arg_size);
+int cam_fd_hw_start(void *hw_priv, void *hw_start_args, uint32_t arg_size);
+int cam_fd_hw_halt_reset(void *hw_priv, void *stop_args, uint32_t arg_size);
+int cam_fd_hw_read(void *hw_priv, void *read_args, uint32_t arg_size);
+int cam_fd_hw_write(void *hw_priv, void *write_args, uint32_t arg_size);
+int cam_fd_hw_process_cmd(void *hw_priv, uint32_t cmd_type,
+ void *cmd_args, uint32_t arg_size);
+
+#endif /* _CAM_FD_HW_CORE_H_ */
diff --git a/drivers/media/platform/msm/camera/cam_fd/fd_hw_mgr/fd_hw/cam_fd_hw_dev.c b/drivers/media/platform/msm/camera/cam_fd/fd_hw_mgr/fd_hw/cam_fd_hw_dev.c
new file mode 100644
index 0000000..803da76
--- /dev/null
+++ b/drivers/media/platform/msm/camera/cam_fd/fd_hw_mgr/fd_hw/cam_fd_hw_dev.c
@@ -0,0 +1,223 @@
+/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/platform_device.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+
+#include "cam_subdev.h"
+#include "cam_fd_hw_intf.h"
+#include "cam_fd_hw_core.h"
+#include "cam_fd_hw_soc.h"
+#include "cam_fd_hw_v41.h"
+
+static int cam_fd_hw_dev_probe(struct platform_device *pdev)
+{
+ struct cam_hw_info *fd_hw;
+ struct cam_hw_intf *fd_hw_intf;
+ struct cam_fd_core *fd_core;
+ const struct of_device_id *match_dev = NULL;
+ struct cam_fd_hw_static_info *hw_static_info = NULL;
+ int rc = 0;
+ struct cam_fd_hw_init_args init_args;
+ struct cam_fd_hw_deinit_args deinit_args;
+
+ fd_hw_intf = kzalloc(sizeof(struct cam_hw_intf), GFP_KERNEL);
+ if (!fd_hw_intf)
+ return -ENOMEM;
+
+ fd_hw = kzalloc(sizeof(struct cam_hw_info), GFP_KERNEL);
+ if (!fd_hw) {
+ kfree(fd_hw_intf);
+ return -ENOMEM;
+ }
+
+ fd_core = kzalloc(sizeof(struct cam_fd_core), GFP_KERNEL);
+ if (!fd_core) {
+ kfree(fd_hw);
+ kfree(fd_hw_intf);
+ return -ENOMEM;
+ }
+
+ fd_hw_intf->hw_priv = fd_hw;
+ fd_hw->core_info = fd_core;
+
+ fd_hw->hw_state = CAM_HW_STATE_POWER_DOWN;
+ fd_hw->soc_info.pdev = pdev;
+ fd_hw->soc_info.dev = &pdev->dev;
+ fd_hw->soc_info.dev_name = pdev->name;
+ fd_hw->open_count = 0;
+ mutex_init(&fd_hw->hw_mutex);
+ spin_lock_init(&fd_hw->hw_lock);
+ init_completion(&fd_hw->hw_complete);
+
+ spin_lock_init(&fd_core->spin_lock);
+ init_completion(&fd_core->processing_complete);
+ init_completion(&fd_core->halt_complete);
+ init_completion(&fd_core->reset_complete);
+
+ fd_hw_intf->hw_ops.get_hw_caps = cam_fd_hw_get_hw_caps;
+ fd_hw_intf->hw_ops.init = cam_fd_hw_init;
+ fd_hw_intf->hw_ops.deinit = cam_fd_hw_deinit;
+ fd_hw_intf->hw_ops.reset = cam_fd_hw_reset;
+ fd_hw_intf->hw_ops.reserve = cam_fd_hw_reserve;
+ fd_hw_intf->hw_ops.release = cam_fd_hw_release;
+ fd_hw_intf->hw_ops.start = cam_fd_hw_start;
+ fd_hw_intf->hw_ops.stop = cam_fd_hw_halt_reset;
+ fd_hw_intf->hw_ops.read = NULL;
+ fd_hw_intf->hw_ops.write = NULL;
+ fd_hw_intf->hw_ops.process_cmd = cam_fd_hw_process_cmd;
+ fd_hw_intf->hw_type = CAM_HW_FD;
+
+ match_dev = of_match_device(pdev->dev.driver->of_match_table,
+ &pdev->dev);
+ if (!match_dev || !match_dev->data) {
+ CAM_ERR(CAM_FD, "No Of_match data, %pK", match_dev);
+ rc = -EINVAL;
+ goto free_memory;
+ }
+ hw_static_info = (struct cam_fd_hw_static_info *)match_dev->data;
+ fd_core->hw_static_info = hw_static_info;
+
+ CAM_DBG(CAM_FD, "HW Static Info : version core[%d.%d] wrapper[%d.%d]",
+ hw_static_info->core_version.major,
+ hw_static_info->core_version.minor,
+ hw_static_info->wrapper_version.major,
+ hw_static_info->wrapper_version.minor);
+
+ rc = cam_fd_soc_init_resources(&fd_hw->soc_info, cam_fd_hw_irq, fd_hw);
+ if (rc) {
+ CAM_ERR(CAM_FD, "Failed to init soc, rc=%d", rc);
+ goto free_memory;
+ }
+
+ fd_hw_intf->hw_idx = fd_hw->soc_info.index;
+
+ memset(&init_args, 0x0, sizeof(init_args));
+ memset(&deinit_args, 0x0, sizeof(deinit_args));
+ rc = cam_fd_hw_init(fd_hw, &init_args, sizeof(init_args));
+ if (rc) {
+ CAM_ERR(CAM_FD, "Failed to hw init, rc=%d", rc);
+ goto deinit_platform_res;
+ }
+
+ rc = cam_fd_hw_util_get_hw_caps(fd_hw, &fd_core->hw_caps);
+ if (rc) {
+ CAM_ERR(CAM_FD, "Failed to get hw caps, rc=%d", rc);
+ goto deinit_hw;
+ }
+
+ rc = cam_fd_hw_deinit(fd_hw, &deinit_args, sizeof(deinit_args));
+ if (rc) {
+ CAM_ERR(CAM_FD, "Failed to deinit hw, rc=%d", rc);
+ goto deinit_platform_res;
+ }
+
+ platform_set_drvdata(pdev, fd_hw_intf);
+ CAM_DBG(CAM_FD, "FD-%d probe successful", fd_hw_intf->hw_idx);
+
+ return rc;
+
+deinit_hw:
+ if (cam_fd_hw_deinit(fd_hw, &deinit_args, sizeof(deinit_args)))
+ CAM_ERR(CAM_FD, "Failed in hw deinit");
+deinit_platform_res:
+ if (cam_fd_soc_deinit_resources(&fd_hw->soc_info))
+ CAM_ERR(CAM_FD, "Failed in soc deinit");
+ mutex_destroy(&fd_hw->hw_mutex);
+free_memory:
+ kfree(fd_hw);
+ kfree(fd_hw_intf);
+ kfree(fd_core);
+
+ return rc;
+}
+
+static int cam_fd_hw_dev_remove(struct platform_device *pdev)
+{
+ int rc = 0;
+ struct cam_hw_intf *fd_hw_intf;
+ struct cam_hw_info *fd_hw;
+ struct cam_fd_core *fd_core;
+
+ fd_hw_intf = platform_get_drvdata(pdev);
+ if (!fd_hw_intf) {
+ CAM_ERR(CAM_FD, "Invalid fd_hw_intf from pdev");
+ return -EINVAL;
+ }
+
+ fd_hw = fd_hw_intf->hw_priv;
+ if (!fd_hw) {
+ CAM_ERR(CAM_FD, "Invalid fd_hw from fd_hw_intf");
+ rc = -ENODEV;
+ goto free_fd_hw_intf;
+ }
+
+ fd_core = (struct cam_fd_core *)fd_hw->core_info;
+ if (!fd_core) {
+ CAM_ERR(CAM_FD, "Invalid fd_core from fd_hw");
+ rc = -EINVAL;
+ goto deinit_platform_res;
+ }
+
+ kfree(fd_core);
+
+deinit_platform_res:
+ rc = cam_fd_soc_deinit_resources(&fd_hw->soc_info);
+ if (rc)
+ CAM_ERR(CAM_FD, "Error in FD soc deinit, rc=%d", rc);
+
+ mutex_destroy(&fd_hw->hw_mutex);
+ kfree(fd_hw);
+
+free_fd_hw_intf:
+ kfree(fd_hw_intf);
+
+ return rc;
+}
+
+static const struct of_device_id cam_fd_hw_dt_match[] = {
+ {
+ .compatible = "qcom,fd41",
+ .data = &cam_fd_wrapper120_core410_info,
+ },
+ {}
+};
+MODULE_DEVICE_TABLE(of, cam_fd_hw_dt_match);
+
+static struct platform_driver cam_fd_hw_driver = {
+ .probe = cam_fd_hw_dev_probe,
+ .remove = cam_fd_hw_dev_remove,
+ .driver = {
+ .name = "cam_fd_hw",
+ .owner = THIS_MODULE,
+ .of_match_table = cam_fd_hw_dt_match,
+ },
+};
+
+static int __init cam_fd_hw_init_module(void)
+{
+ return platform_driver_register(&cam_fd_hw_driver);
+}
+
+static void __exit cam_fd_hw_exit_module(void)
+{
+ platform_driver_unregister(&cam_fd_hw_driver);
+}
+
+module_init(cam_fd_hw_init_module);
+module_exit(cam_fd_hw_exit_module);
+MODULE_DESCRIPTION("CAM FD HW driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/media/platform/msm/camera/cam_fd/fd_hw_mgr/fd_hw/cam_fd_hw_intf.h b/drivers/media/platform/msm/camera/cam_fd/fd_hw_mgr/fd_hw/cam_fd_hw_intf.h
new file mode 100644
index 0000000..aae7648
--- /dev/null
+++ b/drivers/media/platform/msm/camera/cam_fd/fd_hw_mgr/fd_hw/cam_fd_hw_intf.h
@@ -0,0 +1,289 @@
+/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _CAM_FD_HW_INTF_H_
+#define _CAM_FD_HW_INTF_H_
+
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <media/cam_cpas.h>
+#include <media/cam_req_mgr.h>
+#include <media/cam_fd.h>
+
+#include "cam_io_util.h"
+#include "cam_soc_util.h"
+#include "cam_hw.h"
+#include "cam_hw_intf.h"
+#include "cam_subdev.h"
+#include "cam_cpas_api.h"
+#include "cam_hw_mgr_intf.h"
+#include "cam_debug_util.h"
+
+#define CAM_FD_MAX_IO_BUFFERS 5
+#define CAM_FD_MAX_HW_ENTRIES 5
+
+/**
+ * enum cam_fd_hw_type - Enum for FD HW type
+ *
+ * @CAM_HW_FD : FaceDetection HW type
+ */
+enum cam_fd_hw_type {
+ CAM_HW_FD,
+};
+
+/**
+ * enum cam_fd_hw_mode - Mode in which HW can run
+ *
+ * @CAM_FD_MODE_FACEDETECTION : Face Detection mode in which face search
+ * is done on the given frame
+ * @CAM_FD_MODE_PYRAMID : Pyramid mode where a pyramid image is generated
+ * from an input image
+ */
+enum cam_fd_hw_mode {
+ CAM_FD_MODE_FACEDETECTION = 0x1,
+ CAM_FD_MODE_PYRAMID = 0x2,
+};
+
+/**
+ * enum cam_fd_priority - FD priority levels
+ *
+ * @CAM_FD_PRIORITY_HIGH : Indicates high priority client, driver prioritizes
+ * frame requests coming from contexts with HIGH
+ * priority compared to context with normal priority
+ * @CAM_FD_PRIORITY_NORMAL : Indicates normal priority client
+ */
+enum cam_fd_priority {
+ CAM_FD_PRIORITY_HIGH = 0x0,
+ CAM_FD_PRIORITY_NORMAL,
+};
+
+/**
+ * enum cam_fd_hw_irq_type - FD HW IRQ types
+ *
+ * @CAM_FD_IRQ_FRAME_DONE : Indicates frame processing is finished
+ * @CAM_FD_IRQ_HALT_DONE : Indicates HW halt is finished
+ * @CAM_FD_IRQ_RESET_DONE : Indicates HW reset is finished
+ */
+enum cam_fd_hw_irq_type {
+ CAM_FD_IRQ_FRAME_DONE,
+ CAM_FD_IRQ_HALT_DONE,
+ CAM_FD_IRQ_RESET_DONE,
+};
+
+/**
+ * enum cam_fd_hw_cmd_type - FD HW layer custom commands
+ *
+ * @CAM_FD_HW_CMD_PRESTART : Command to process pre-start settings
+ * @CAM_FD_HW_CMD_FRAME_DONE : Command to process frame done settings
+ * @CAM_FD_HW_CMD_UPDATE_SOC : Command to process soc update
+ * @CAM_FD_HW_CMD_REGISTER_CALLBACK : Command to set hw mgr callback
+ * @CAM_FD_HW_CMD_MAX : Indicates max cmd
+ */
+enum cam_fd_hw_cmd_type {
+ CAM_FD_HW_CMD_PRESTART,
+ CAM_FD_HW_CMD_FRAME_DONE,
+ CAM_FD_HW_CMD_UPDATE_SOC,
+ CAM_FD_HW_CMD_REGISTER_CALLBACK,
+ CAM_FD_HW_CMD_MAX,
+};
+
+/**
+ * struct cam_fd_hw_io_buffer : FD HW IO Buffer information
+ *
+ * @valid : Whether this IO Buf configuration is valid
+ * @io_cfg : IO Configuration information
+ * @num_buf : Number planes in io_addr, cpu_addr array
+ * @io_addr : Array of IO address information for planes
+ * @cpu_addr : Array of CPU address information for planes
+ */
+struct cam_fd_hw_io_buffer {
+ bool valid;
+ struct cam_buf_io_cfg *io_cfg;
+ uint32_t num_buf;
+ uint64_t io_addr[CAM_PACKET_MAX_PLANES];
+ uint64_t cpu_addr[CAM_PACKET_MAX_PLANES];
+};
+
+/**
+ * struct cam_fd_hw_req_private : FD HW layer's private information
+ * specific to a request
+ *
+ * @ctx_hw_private : FD HW layer's ctx specific private data
+ * @request_id : Request ID corresponding to this private information
+ * @get_raw_results : Whether to get raw results for this request
+ * @ro_mode_enabled : Whether RO mode is enabled for this request
+ * @fd_results : Pointer to save face detection results
+ * @raw_results : Pointer to save face detection raw results
+ */
+struct cam_fd_hw_req_private {
+ void *ctx_hw_private;
+ uint64_t request_id;
+ bool get_raw_results;
+ bool ro_mode_enabled;
+ struct cam_fd_results *fd_results;
+ uint32_t *raw_results;
+};
+
+/**
+ * struct cam_fd_hw_reserve_args : Reserve args for this HW context
+ *
+ * @hw_ctx : HW context for which reserve is requested
+ * @mode : Mode for which this reserve is requested
+ * @ctx_hw_private : Pointer to save HW layer's private information specific
+ * to this hw context. This has to be passed while calling
+ * further HW layer calls
+ */
+struct cam_fd_hw_reserve_args {
+ void *hw_ctx;
+ enum cam_fd_hw_mode mode;
+ void *ctx_hw_private;
+};
+
+/**
+ * struct cam_fd_hw_release_args : Release args for this HW context
+ *
+ * @hw_ctx : HW context for which release is requested
+ * @ctx_hw_private : HW layer's private information specific to this hw context
+ */
+struct cam_fd_hw_release_args {
+ void *hw_ctx;
+ void *ctx_hw_private;
+};
+
+/**
+ * struct cam_fd_hw_init_args : Init args for this HW context
+ *
+ * @hw_ctx : HW context for which init is requested
+ * @ctx_hw_private : HW layer's private information specific to this hw context
+ */
+struct cam_fd_hw_init_args {
+ void *hw_ctx;
+ void *ctx_hw_private;
+};
+
+/**
+ * struct cam_fd_hw_deinit_args : Deinit args for this HW context
+ *
+ * @hw_ctx : HW context for which deinit is requested
+ * @ctx_hw_private : HW layer's private information specific to this hw context
+ */
+struct cam_fd_hw_deinit_args {
+ void *hw_ctx;
+ void *ctx_hw_private;
+};
+
+/**
+ * struct cam_fd_hw_cmd_prestart_args : Prestart command args
+ *
+ * @hw_ctx : HW context which submitted this prestart
+ * @ctx_hw_private : HW layer's private information specific to
+ * this hw context
+ * @request_id : Request ID corresponds to this pre-start command
+ * @get_raw_results : Whether to get raw results for this request
+ * @input_buf : Input IO Buffer information for this request
+ * @output_buf : Output IO Buffer information for this request
+ * @cmd_buf_addr : Command buffer address to fill kmd commands
+ * @size : Size available in command buffer
+ * @pre_config_buf_size : Buffer size filled with commands by KMD that has
+ * to be inserted before umd commands
+ * @post_config_buf_size : Buffer size filled with commands by KMD that has
+ * to be inserted after umd commands
+ * @hw_req_private : HW layer's private information specific to
+ * this request
+ */
+struct cam_fd_hw_cmd_prestart_args {
+ void *hw_ctx;
+ void *ctx_hw_private;
+ uint64_t request_id;
+ bool get_raw_results;
+ struct cam_fd_hw_io_buffer input_buf[CAM_FD_MAX_IO_BUFFERS];
+ struct cam_fd_hw_io_buffer output_buf[CAM_FD_MAX_IO_BUFFERS];
+ uint32_t *cmd_buf_addr;
+ uint32_t size;
+ uint32_t pre_config_buf_size;
+ uint32_t post_config_buf_size;
+ struct cam_fd_hw_req_private hw_req_private;
+};
+
+/**
+ * struct cam_fd_hw_cmd_start_args : Start command args
+ *
+ * @hw_ctx : HW context which submitting start command
+ * @ctx_hw_private : HW layer's private information specific to
+ * this hw context
+ * @hw_req_private : HW layer's private information specific to
+ * this request
+ * @hw_update_entries : HW update entries corresponds to this request
+ * @num_hw_update_entries : Number of hw update entries
+ */
+struct cam_fd_hw_cmd_start_args {
+ void *hw_ctx;
+ void *ctx_hw_private;
+ struct cam_fd_hw_req_private *hw_req_private;
+ struct cam_hw_update_entry *hw_update_entries;
+ uint32_t num_hw_update_entries;
+};
+
+/**
+ * struct cam_fd_hw_stop_args : Stop command args
+ *
+ * @hw_ctx : HW context which submitting stop command
+ * @ctx_hw_private : HW layer's private information specific to this hw context
+ * @request_id : Request ID that need to be stopped
+ * @hw_req_private : HW layer's private information specific to this request
+ */
+struct cam_fd_hw_stop_args {
+ void *hw_ctx;
+ void *ctx_hw_private;
+ uint64_t request_id;
+ struct cam_fd_hw_req_private *hw_req_private;
+};
+
+/**
+ * struct cam_fd_hw_frame_done_args : Frame done command args
+ *
+ * @hw_ctx : HW context which submitting frame done request
+ * @ctx_hw_private : HW layer's private information specific to this hw context
+ * @request_id : Request ID that need to be stopped
+ * @hw_req_private : HW layer's private information specific to this request
+ */
+struct cam_fd_hw_frame_done_args {
+ void *hw_ctx;
+ void *ctx_hw_private;
+ uint64_t request_id;
+ struct cam_fd_hw_req_private *hw_req_private;
+};
+
+/**
+ * struct cam_fd_hw_reset_args : Reset command args
+ *
+ * @hw_ctx : HW context which submitting reset command
+ * @ctx_hw_private : HW layer's private information specific to this hw context
+ */
+struct cam_fd_hw_reset_args {
+ void *hw_ctx;
+ void *ctx_hw_private;
+};
+
+/**
+ * struct cam_fd_hw_cmd_set_irq_cb : Set IRQ callback command args
+ *
+ * @cam_fd_hw_mgr_cb : HW Mgr's callback pointer
+ * @data : HW Mgr's private data
+ */
+struct cam_fd_hw_cmd_set_irq_cb {
+ int (*cam_fd_hw_mgr_cb)(void *data, enum cam_fd_hw_irq_type irq_type);
+ void *data;
+};
+
+#endif /* _CAM_FD_HW_INTF_H_ */
diff --git a/drivers/media/platform/msm/camera/cam_fd/fd_hw_mgr/fd_hw/cam_fd_hw_soc.c b/drivers/media/platform/msm/camera/cam_fd/fd_hw_mgr/fd_hw/cam_fd_hw_soc.c
new file mode 100644
index 0000000..9045dc1
--- /dev/null
+++ b/drivers/media/platform/msm/camera/cam_fd/fd_hw_mgr/fd_hw/cam_fd_hw_soc.c
@@ -0,0 +1,285 @@
+/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/device.h>
+#include <linux/platform_device.h>
+#include <linux/of.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+
+#include "cam_fd_hw_core.h"
+#include "cam_fd_hw_soc.h"
+
+static void cam_fd_hw_util_cpas_callback(uint32_t handle, void *userdata,
+ enum cam_camnoc_irq_type event_type, uint32_t event_data)
+{
+ CAM_DBG(CAM_FD, "CPAS hdl=%d, udata=%pK, event=%d, event_data=%d",
+ handle, userdata, event_type, event_data);
+}
+
+static int cam_fd_hw_soc_util_setup_regbase_indices(
+ struct cam_hw_soc_info *soc_info)
+{
+ struct cam_fd_soc_private *soc_private =
+ (struct cam_fd_soc_private *)soc_info->soc_private;
+ uint32_t index;
+ int rc, i;
+
+ for (i = 0; i < CAM_FD_REG_MAX; i++)
+ soc_private->regbase_index[i] = -1;
+
+ if ((soc_info->num_mem_block > CAM_SOC_MAX_BLOCK) ||
+ (soc_info->num_mem_block != CAM_FD_REG_MAX)) {
+ CAM_ERR(CAM_FD, "Invalid num_mem_block=%d",
+ soc_info->num_mem_block);
+ return -EINVAL;
+ }
+
+ rc = cam_common_util_get_string_index(soc_info->mem_block_name,
+ soc_info->num_mem_block, "fd_core", &index);
+ if ((rc == 0) && (index < CAM_FD_REG_MAX)) {
+ soc_private->regbase_index[CAM_FD_REG_CORE] = index;
+ } else {
+ CAM_ERR(CAM_FD, "regbase not found for FD_CORE, rc=%d, %d %d",
+ rc, index, CAM_FD_REG_MAX);
+ return -EINVAL;
+ }
+
+ rc = cam_common_util_get_string_index(soc_info->mem_block_name,
+ soc_info->num_mem_block, "fd_wrapper", &index);
+ if ((rc == 0) && (index < CAM_FD_REG_MAX)) {
+ soc_private->regbase_index[CAM_FD_REG_WRAPPER] = index;
+ } else {
+ CAM_ERR(CAM_FD, "regbase not found FD_WRAPPER, rc=%d, %d %d",
+ rc, index, CAM_FD_REG_MAX);
+ return -EINVAL;
+ }
+
+ CAM_DBG(CAM_FD, "Reg indices : CORE=%d, WRAPPER=%d",
+ soc_private->regbase_index[CAM_FD_REG_CORE],
+ soc_private->regbase_index[CAM_FD_REG_WRAPPER]);
+
+ return 0;
+}
+
+static int cam_fd_soc_set_clk_flags(struct cam_hw_soc_info *soc_info)
+{
+ int i, rc = 0;
+
+ if (soc_info->num_clk > CAM_SOC_MAX_CLK) {
+ CAM_ERR(CAM_FD, "Invalid num clk %d", soc_info->num_clk);
+ return -EINVAL;
+ }
+
+ /* set memcore and mem periphery logic flags to 0 */
+ for (i = 0; i < soc_info->num_clk; i++) {
+ if ((strcmp(soc_info->clk_name[i], "fd_core_clk") == 0) ||
+ (strcmp(soc_info->clk_name[i], "fd_core_uar_clk") ==
+ 0)) {
+ rc = cam_soc_util_set_clk_flags(soc_info, i,
+ CLKFLAG_NORETAIN_MEM);
+ if (rc)
+ CAM_ERR(CAM_FD,
+ "Failed in NORETAIN_MEM i=%d, rc=%d",
+ i, rc);
+
+ cam_soc_util_set_clk_flags(soc_info, i,
+ CLKFLAG_NORETAIN_PERIPH);
+ if (rc)
+ CAM_ERR(CAM_FD,
+ "Failed in NORETAIN_PERIPH i=%d, rc=%d",
+ i, rc);
+ }
+ }
+
+ return rc;
+}
+
+void cam_fd_soc_register_write(struct cam_hw_soc_info *soc_info,
+ enum cam_fd_reg_base reg_base, uint32_t reg_offset, uint32_t reg_value)
+{
+ struct cam_fd_soc_private *soc_private =
+ (struct cam_fd_soc_private *)soc_info->soc_private;
+ int32_t reg_index = soc_private->regbase_index[reg_base];
+
+ CAM_DBG(CAM_FD, "FD_REG_WRITE: Base[%d] Offset[0x%8x] Value[0x%8x]",
+ reg_base, reg_offset, reg_value);
+
+ cam_io_w_mb(reg_value,
+ soc_info->reg_map[reg_index].mem_base + reg_offset);
+}
+
+uint32_t cam_fd_soc_register_read(struct cam_hw_soc_info *soc_info,
+ enum cam_fd_reg_base reg_base, uint32_t reg_offset)
+{
+ struct cam_fd_soc_private *soc_private =
+ (struct cam_fd_soc_private *)soc_info->soc_private;
+ int32_t reg_index = soc_private->regbase_index[reg_base];
+ uint32_t reg_value;
+
+ reg_value = cam_io_r_mb(
+ soc_info->reg_map[reg_index].mem_base + reg_offset);
+
+ CAM_DBG(CAM_FD, "FD_REG_READ: Base[%d] Offset[0x%8x] Value[0x%8x]",
+ reg_base, reg_offset, reg_value);
+
+ return reg_value;
+}
+
+int cam_fd_soc_enable_resources(struct cam_hw_soc_info *soc_info)
+{
+ struct cam_fd_soc_private *soc_private = soc_info->soc_private;
+ struct cam_ahb_vote ahb_vote;
+ struct cam_axi_vote axi_vote;
+ int rc;
+
+ ahb_vote.type = CAM_VOTE_ABSOLUTE;
+ ahb_vote.vote.level = CAM_SVS_VOTE;
+ axi_vote.compressed_bw = 7200000;
+ axi_vote.uncompressed_bw = 7200000;
+ rc = cam_cpas_start(soc_private->cpas_handle, &ahb_vote, &axi_vote);
+ if (rc) {
+ CAM_ERR(CAM_FD, "Error in CPAS START, rc=%d", rc);
+ return -EFAULT;
+ }
+
+ rc = cam_soc_util_enable_platform_resource(soc_info, true, CAM_SVS_VOTE,
+ true);
+ if (rc) {
+ CAM_ERR(CAM_FD, "Error enable platform failed, rc=%d", rc);
+ goto stop_cpas;
+ }
+
+ return rc;
+
+stop_cpas:
+ if (cam_cpas_stop(soc_private->cpas_handle))
+ CAM_ERR(CAM_FD, "Error in CPAS STOP");
+
+ return rc;
+}
+
+
+int cam_fd_soc_disable_resources(struct cam_hw_soc_info *soc_info)
+{
+ struct cam_fd_soc_private *soc_private;
+ int rc = 0;
+
+ if (!soc_info) {
+ CAM_ERR(CAM_FD, "Invalid soc_info param");
+ return -EINVAL;
+ }
+ soc_private = soc_info->soc_private;
+
+ rc = cam_soc_util_disable_platform_resource(soc_info, true, true);
+ if (rc) {
+ CAM_ERR(CAM_FD, "disable platform resources failed, rc=%d", rc);
+ return rc;
+ }
+
+ rc = cam_cpas_stop(soc_private->cpas_handle);
+ if (rc) {
+ CAM_ERR(CAM_FD, "Error in CPAS STOP, handle=0x%x, rc=%d",
+ soc_private->cpas_handle, rc);
+ return rc;
+ }
+
+ return rc;
+}
+
+int cam_fd_soc_init_resources(struct cam_hw_soc_info *soc_info,
+ irq_handler_t irq_handler, void *private_data)
+{
+ struct cam_fd_soc_private *soc_private;
+ struct cam_cpas_register_params cpas_register_param;
+ int rc;
+
+ rc = cam_soc_util_get_dt_properties(soc_info);
+ if (rc) {
+ CAM_ERR(CAM_FD, "Failed in get_dt_properties, rc=%d", rc);
+ return rc;
+ }
+
+ rc = cam_soc_util_request_platform_resource(soc_info, irq_handler,
+ private_data);
+ if (rc) {
+ CAM_ERR(CAM_FD, "Failed in request_platform_resource rc=%d",
+ rc);
+ return rc;
+ }
+
+ rc = cam_fd_soc_set_clk_flags(soc_info);
+ if (rc) {
+ CAM_ERR(CAM_FD, "failed in set_clk_flags rc=%d", rc);
+ goto release_res;
+ }
+
+ soc_private = kzalloc(sizeof(struct cam_fd_soc_private), GFP_KERNEL);
+ if (!soc_private) {
+ rc = -ENOMEM;
+ goto release_res;
+ }
+ soc_info->soc_private = soc_private;
+
+ rc = cam_fd_hw_soc_util_setup_regbase_indices(soc_info);
+ if (rc) {
+ CAM_ERR(CAM_FD, "Failed in setup regbase, rc=%d", rc);
+ goto free_soc_private;
+ }
+
+ memset(&cpas_register_param, 0, sizeof(cpas_register_param));
+ strlcpy(cpas_register_param.identifier, "fd", CAM_HW_IDENTIFIER_LENGTH);
+ cpas_register_param.cell_index = soc_info->index;
+ cpas_register_param.dev = &soc_info->pdev->dev;
+ cpas_register_param.userdata = private_data;
+ cpas_register_param.cam_cpas_client_cb = cam_fd_hw_util_cpas_callback;
+
+ rc = cam_cpas_register_client(&cpas_register_param);
+ if (rc) {
+ CAM_ERR(CAM_FD, "CPAS registration failed");
+ goto free_soc_private;
+ }
+ soc_private->cpas_handle = cpas_register_param.client_handle;
+ CAM_DBG(CAM_FD, "CPAS handle=%d", soc_private->cpas_handle);
+
+ return rc;
+
+free_soc_private:
+ kfree(soc_info->soc_private);
+ soc_info->soc_private = NULL;
+release_res:
+ cam_soc_util_release_platform_resource(soc_info);
+
+ return rc;
+}
+
+int cam_fd_soc_deinit_resources(struct cam_hw_soc_info *soc_info)
+{
+ struct cam_fd_soc_private *soc_private =
+ (struct cam_fd_soc_private *)soc_info->soc_private;
+ int rc;
+
+ rc = cam_cpas_unregister_client(soc_private->cpas_handle);
+ if (rc)
+ CAM_ERR(CAM_FD, "Unregister cpas failed, handle=%d, rc=%d",
+ soc_private->cpas_handle, rc);
+
+ rc = cam_soc_util_release_platform_resource(soc_info);
+ if (rc)
+ CAM_ERR(CAM_FD, "release platform failed, rc=%d", rc);
+
+ kfree(soc_info->soc_private);
+ soc_info->soc_private = NULL;
+
+ return rc;
+}
diff --git a/drivers/media/platform/msm/camera/cam_fd/fd_hw_mgr/fd_hw/cam_fd_hw_soc.h b/drivers/media/platform/msm/camera/cam_fd/fd_hw_mgr/fd_hw/cam_fd_hw_soc.h
new file mode 100644
index 0000000..4a22293
--- /dev/null
+++ b/drivers/media/platform/msm/camera/cam_fd/fd_hw_mgr/fd_hw/cam_fd_hw_soc.h
@@ -0,0 +1,53 @@
+/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _CAM_FD_HW_SOC_H_
+#define _CAM_FD_HW_SOC_H_
+
+#include "cam_soc_util.h"
+
+/**
+ * enum cam_fd_reg_base - Enum for FD register sets
+ *
+ * @CAM_FD_REG_CORE : Indicates FD Core register space
+ * @CAM_FD_REG_WRAPPER : Indicates FD Wrapper register space
+ * @CAM_FD_REG_MAX : Max number of register sets supported
+ */
+enum cam_fd_reg_base {
+ CAM_FD_REG_CORE,
+ CAM_FD_REG_WRAPPER,
+ CAM_FD_REG_MAX
+};
+
+/**
+ * struct cam_fd_soc_private : FD private SOC information
+ *
+ * @regbase_index : Mapping between Register base enum to register index in SOC
+ * @cpas_handle : CPAS handle
+ *
+ */
+struct cam_fd_soc_private {
+ int32_t regbase_index[CAM_FD_REG_MAX];
+ uint32_t cpas_handle;
+};
+
+int cam_fd_soc_init_resources(struct cam_hw_soc_info *soc_info,
+ irq_handler_t irq_handler, void *private_data);
+int cam_fd_soc_deinit_resources(struct cam_hw_soc_info *soc_info);
+int cam_fd_soc_enable_resources(struct cam_hw_soc_info *soc_info);
+int cam_fd_soc_disable_resources(struct cam_hw_soc_info *soc_info);
+uint32_t cam_fd_soc_register_read(struct cam_hw_soc_info *soc_info,
+ enum cam_fd_reg_base reg_base, uint32_t reg_offset);
+void cam_fd_soc_register_write(struct cam_hw_soc_info *soc_info,
+ enum cam_fd_reg_base reg_base, uint32_t reg_offset, uint32_t reg_value);
+
+#endif /* _CAM_FD_HW_SOC_H_ */
diff --git a/drivers/media/platform/msm/camera/cam_fd/fd_hw_mgr/fd_hw/cam_fd_hw_v41.h b/drivers/media/platform/msm/camera/cam_fd/fd_hw_mgr/fd_hw/cam_fd_hw_v41.h
new file mode 100644
index 0000000..70448bb
--- /dev/null
+++ b/drivers/media/platform/msm/camera/cam_fd/fd_hw_mgr/fd_hw/cam_fd_hw_v41.h
@@ -0,0 +1,70 @@
+/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _CAM_FD_HW_V41_H_
+#define _CAM_FD_HW_V41_H_
+
+static struct cam_fd_hw_static_info cam_fd_wrapper120_core410_info = {
+ .core_version = {
+ .major = 4,
+ .minor = 1,
+ .incr = 0,
+ },
+ .wrapper_version = {
+ .major = 1,
+ .minor = 2,
+ .incr = 0,
+ },
+ .core_regs = {
+ .version = 0x38,
+ .control = 0x0,
+ .result_cnt = 0x4,
+ .result_addr = 0x20,
+ .image_addr = 0x24,
+ .work_addr = 0x28,
+ .ro_mode = 0x34,
+ .results_reg_base = 0x400,
+ .raw_results_reg_base = 0x800,
+ },
+ .wrapper_regs = {
+ .wrapper_version = 0x0,
+ .cgc_disable = 0x4,
+ .hw_stop = 0x8,
+ .sw_reset = 0x10,
+ .vbif_req_priority = 0x20,
+ .vbif_priority_level = 0x24,
+ .vbif_done_status = 0x34,
+ .irq_mask = 0x50,
+ .irq_status = 0x54,
+ .irq_clear = 0x58,
+ },
+ .results = {
+ .max_faces = 35,
+ .per_face_entries = 4,
+ .raw_results_available = true,
+ .raw_results_entries = 512,
+ },
+ .enable_errata_wa = {
+ .single_irq_only = true,
+ .ro_mode_enable_always = true,
+ .ro_mode_results_invalid = true,
+ },
+ .irq_mask = CAM_FD_IRQ_TO_MASK(CAM_FD_IRQ_FRAME_DONE) |
+ CAM_FD_IRQ_TO_MASK(CAM_FD_IRQ_HALT_DONE) |
+ CAM_FD_IRQ_TO_MASK(CAM_FD_IRQ_RESET_DONE),
+ .qos_priority = 4,
+ .qos_priority_level = 4,
+ .supported_modes = CAM_FD_MODE_FACEDETECTION | CAM_FD_MODE_PYRAMID,
+ .ro_mode_supported = true,
+};
+
+#endif /* _CAM_FD_HW_V41_H_ */
diff --git a/drivers/media/platform/msm/camera/cam_icp/icp_hw/bps_hw/bps_core.c b/drivers/media/platform/msm/camera/cam_icp/icp_hw/bps_hw/bps_core.c
index 55a2e1b..3473d08 100644
--- a/drivers/media/platform/msm/camera/cam_icp/icp_hw/bps_hw/bps_core.c
+++ b/drivers/media/platform/msm/camera/cam_icp/icp_hw/bps_hw/bps_core.c
@@ -75,8 +75,8 @@
cpas_vote.ahb_vote.type = CAM_VOTE_ABSOLUTE;
cpas_vote.ahb_vote.vote.level = CAM_SVS_VOTE;
- cpas_vote.axi_vote.compressed_bw = ICP_TURBO_VOTE;
- cpas_vote.axi_vote.uncompressed_bw = ICP_TURBO_VOTE;
+ cpas_vote.axi_vote.compressed_bw = CAM_CPAS_DEFAULT_AXI_BW;
+ cpas_vote.axi_vote.uncompressed_bw = CAM_CPAS_DEFAULT_AXI_BW;
rc = cam_cpas_start(core_info->cpas_handle,
&cpas_vote.ahb_vote, &cpas_vote.axi_vote);
@@ -269,6 +269,13 @@
case CAM_ICP_BPS_CMD_POWER_RESUME:
rc = cam_bps_handle_resume(bps_dev);
break;
+ case CAM_ICP_BPS_CMD_UPDATE_CLK: {
+ uint32_t clk_rate = *(uint32_t *)cmd_args;
+
+ CAM_DBG(CAM_ICP, "bps_src_clk rate = %d", (int)clk_rate);
+ rc = cam_bps_update_clk_rate(soc_info, clk_rate);
+ }
+ break;
default:
break;
}
diff --git a/drivers/media/platform/msm/camera/cam_icp/icp_hw/bps_hw/bps_soc.c b/drivers/media/platform/msm/camera/cam_icp/icp_hw/bps_hw/bps_soc.c
index 07dacb2..2477e7d 100644
--- a/drivers/media/platform/msm/camera/cam_icp/icp_hw/bps_hw/bps_soc.c
+++ b/drivers/media/platform/msm/camera/cam_icp/icp_hw/bps_hw/bps_soc.c
@@ -132,3 +132,13 @@
return rc;
}
+
+int cam_bps_update_clk_rate(struct cam_hw_soc_info *soc_info,
+ uint32_t clk_rate)
+{
+ if (!soc_info)
+ return -EINVAL;
+
+ return cam_soc_util_set_clk_rate(soc_info->clk[soc_info->src_clk_idx],
+ soc_info->clk_name[soc_info->src_clk_idx], clk_rate);
+}
diff --git a/drivers/media/platform/msm/camera/cam_icp/icp_hw/bps_hw/bps_soc.h b/drivers/media/platform/msm/camera/cam_icp/icp_hw/bps_hw/bps_soc.h
index 3cffb09..2dd2c08 100644
--- a/drivers/media/platform/msm/camera/cam_icp/icp_hw/bps_hw/bps_soc.h
+++ b/drivers/media/platform/msm/camera/cam_icp/icp_hw/bps_hw/bps_soc.h
@@ -26,4 +26,6 @@
int cam_bps_transfer_gdsc_control(struct cam_hw_soc_info *soc_info);
+int cam_bps_update_clk_rate(struct cam_hw_soc_info *soc_info,
+ uint32_t clk_rate);
#endif /* _CAM_BPS_SOC_H_*/
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 171d8bd..7c5b405 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
@@ -45,6 +45,7 @@
#include "a5_core.h"
#include "hfi_sys_defs.h"
#include "cam_debug_util.h"
+#include "cam_soc_util.h"
#define ICP_WORKQ_NUM_TASK 30
#define ICP_WORKQ_TASK_CMD_TYPE 1
@@ -52,6 +53,644 @@
static struct cam_icp_hw_mgr icp_hw_mgr;
+static void cam_icp_hw_mgr_clk_info_update(struct cam_icp_hw_mgr *hw_mgr,
+ struct cam_icp_hw_ctx_data *ctx_data)
+{
+ struct cam_icp_clk_info *hw_mgr_clk_info;
+
+ if (ctx_data->icp_dev_acquire_info->dev_type == CAM_ICP_RES_TYPE_BPS)
+ hw_mgr_clk_info = &hw_mgr->clk_info[ICP_CLK_HW_BPS];
+ else
+ hw_mgr_clk_info = &hw_mgr->clk_info[ICP_CLK_HW_IPE];
+
+ if (hw_mgr_clk_info->base_clk >= ctx_data->clk_info.base_clk)
+ hw_mgr_clk_info->base_clk -= ctx_data->clk_info.base_clk;
+}
+
+static void cam_icp_hw_mgr_reset_clk_info(struct cam_icp_hw_mgr *hw_mgr)
+{
+ int i;
+
+ for (i = 0; i < ICP_CLK_HW_MAX; i++) {
+ hw_mgr->clk_info[i].base_clk = 0;
+ hw_mgr->clk_info[i].curr_clk = ICP_TURBO_VOTE;
+ hw_mgr->clk_info[i].threshold = ICP_OVER_CLK_THRESHOLD;
+ hw_mgr->clk_info[i].over_clked = 0;
+ hw_mgr->clk_info[i].uncompressed_bw = CAM_CPAS_DEFAULT_AXI_BW;
+ hw_mgr->clk_info[i].compressed_bw = CAM_CPAS_DEFAULT_AXI_BW;
+ }
+ hw_mgr->icp_default_clk = ICP_SVS_VOTE;
+}
+
+static int cam_icp_get_actual_clk_rate_idx(
+ struct cam_icp_hw_ctx_data *ctx_data, uint32_t base_clk)
+{
+ int i;
+
+ for (i = 0; i < CAM_MAX_VOTE; i++)
+ if (ctx_data->clk_info.clk_rate[i] >= base_clk)
+ return i;
+
+ return 0;
+}
+
+static bool cam_icp_is_over_clk(struct cam_icp_hw_mgr *hw_mgr,
+ struct cam_icp_hw_ctx_data *ctx_data,
+ struct cam_icp_clk_info *hw_mgr_clk_info)
+{
+ int base_clk_idx;
+ int curr_clk_idx;
+
+ base_clk_idx = cam_icp_get_actual_clk_rate_idx(ctx_data,
+ hw_mgr_clk_info->base_clk);
+
+ curr_clk_idx = cam_icp_get_actual_clk_rate_idx(ctx_data,
+ hw_mgr_clk_info->curr_clk);
+
+ CAM_DBG(CAM_ICP, "bc_idx = %d cc_idx = %d %lld %lld",
+ base_clk_idx, curr_clk_idx, hw_mgr_clk_info->base_clk,
+ hw_mgr_clk_info->curr_clk);
+
+ if (curr_clk_idx > base_clk_idx)
+ return true;
+
+ return false;
+}
+
+static int cam_icp_get_lower_clk_rate(struct cam_icp_hw_mgr *hw_mgr,
+ struct cam_icp_hw_ctx_data *ctx_data, uint32_t base_clk)
+{
+ int i;
+
+ i = cam_icp_get_actual_clk_rate_idx(ctx_data, base_clk);
+
+ if (i > 0)
+ return ctx_data->clk_info.clk_rate[i - 1];
+
+ CAM_DBG(CAM_ICP, "Already clk at lower level");
+ return base_clk;
+}
+
+static int cam_icp_get_next_clk_rate(struct cam_icp_hw_mgr *hw_mgr,
+ struct cam_icp_hw_ctx_data *ctx_data, uint32_t base_clk)
+{
+ int i;
+
+ i = cam_icp_get_actual_clk_rate_idx(ctx_data, base_clk);
+
+ if (i < CAM_MAX_VOTE - 1)
+ return ctx_data->clk_info.clk_rate[i + 1];
+
+ CAM_DBG(CAM_ICP, "Already clk at higher level");
+
+ return base_clk;
+}
+
+static int cam_icp_get_actual_clk_rate(struct cam_icp_hw_mgr *hw_mgr,
+ struct cam_icp_hw_ctx_data *ctx_data, uint32_t base_clk)
+{
+ int i;
+
+ for (i = 0; i < CAM_MAX_VOTE; i++)
+ if (ctx_data->clk_info.clk_rate[i] >= base_clk)
+ return ctx_data->clk_info.clk_rate[i];
+
+ return base_clk;
+}
+
+static int cam_icp_supported_clk_rates(struct cam_icp_hw_mgr *hw_mgr,
+ struct cam_icp_hw_ctx_data *ctx_data)
+{
+ int i;
+ struct cam_hw_soc_info *soc_info;
+ struct cam_hw_intf *dev_intf = NULL;
+ struct cam_hw_info *dev = NULL;
+
+ if (ctx_data->icp_dev_acquire_info->dev_type == CAM_ICP_RES_TYPE_BPS)
+ dev_intf = hw_mgr->devices[CAM_ICP_DEV_BPS][0];
+ else
+ dev_intf = hw_mgr->devices[CAM_ICP_DEV_IPE][0];
+
+ if (!dev_intf) {
+ CAM_ERR(CAM_ICP, "dev_intf is invalid");
+ return -EINVAL;
+ }
+ dev = (struct cam_hw_info *)dev_intf->hw_priv;
+ soc_info = &dev->soc_info;
+
+ for (i = 0; i < CAM_MAX_VOTE; i++) {
+ ctx_data->clk_info.clk_rate[i] =
+ soc_info->clk_rate[i][soc_info->num_clk - 1];
+ CAM_DBG(CAM_ICP, "clk_info = %d",
+ ctx_data->clk_info.clk_rate[i]);
+ }
+
+ return 0;
+}
+
+static int cam_icp_clk_idx_from_req_id(struct cam_icp_hw_ctx_data *ctx_data,
+ uint64_t req_id)
+{
+ struct hfi_frame_process_info *frame_process;
+ int i;
+
+ frame_process = &ctx_data->hfi_frame_process;
+
+ for (i = 0; i < CAM_FRAME_CMD_MAX; i++)
+ if (frame_process->request_id[i] == req_id)
+ return i;
+
+ return 0;
+}
+
+static int cam_icp_clk_info_init(struct cam_icp_hw_mgr *hw_mgr,
+ struct cam_icp_hw_ctx_data *ctx_data)
+{
+ int i;
+
+ for (i = 0; i < ICP_CLK_HW_MAX; i++) {
+ hw_mgr->clk_info[i].base_clk = ICP_TURBO_VOTE;
+ hw_mgr->clk_info[i].curr_clk = ICP_TURBO_VOTE;
+ hw_mgr->clk_info[i].threshold = ICP_OVER_CLK_THRESHOLD;
+ hw_mgr->clk_info[i].over_clked = 0;
+ hw_mgr->clk_info[i].uncompressed_bw = CAM_CPAS_DEFAULT_AXI_BW;
+ hw_mgr->clk_info[i].compressed_bw = CAM_CPAS_DEFAULT_AXI_BW;
+ }
+ hw_mgr->icp_default_clk = ICP_SVS_VOTE;
+
+ return 0;
+}
+
+static int cam_icp_ctx_clk_info_init(struct cam_icp_hw_ctx_data *ctx_data)
+{
+ ctx_data->clk_info.curr_fc = 0;
+ ctx_data->clk_info.base_clk = 0;
+ ctx_data->clk_info.uncompressed_bw = 0;
+ ctx_data->clk_info.compressed_bw = 0;
+ cam_icp_supported_clk_rates(&icp_hw_mgr, ctx_data);
+
+ return 0;
+}
+
+static uint32_t cam_icp_mgr_calc_base_clk(uint32_t frame_cycles,
+ uint64_t budget)
+{
+ uint64_t base_clk;
+ uint64_t mul = 1000000000;
+
+ base_clk = (frame_cycles * mul) / budget;
+
+ CAM_DBG(CAM_ICP, "budget = %lld fc = %d ib = %lld base_clk = %lld",
+ budget, frame_cycles,
+ (long long int)(frame_cycles * mul), base_clk);
+
+ return base_clk;
+}
+
+static bool cam_icp_busy_prev_reqs(struct hfi_frame_process_info *frm_process,
+ uint64_t req_id)
+{
+ int i;
+ int cnt;
+
+ for (i = 0, cnt = 0; i < CAM_FRAME_CMD_MAX; i++) {
+ if (frm_process->request_id[i]) {
+ if (frm_process->fw_process_flag[i]) {
+ CAM_DBG(CAM_ICP, "r id = %lld busy = %d",
+ frm_process->request_id[i],
+ frm_process->fw_process_flag[i]);
+ cnt++;
+ }
+ }
+ }
+ if (cnt > 1)
+ return true;
+
+ return false;
+}
+
+static int cam_icp_calc_total_clk(struct cam_icp_hw_mgr *hw_mgr,
+ struct cam_icp_clk_info *hw_mgr_clk_info, uint32_t dev_type)
+{
+ int i;
+ struct cam_icp_hw_ctx_data *ctx_data;
+
+ hw_mgr_clk_info->base_clk = 0;
+ for (i = 0; i < CAM_ICP_CTX_MAX; i++) {
+ ctx_data = &hw_mgr->ctx_data[i];
+ if (ctx_data->in_use &&
+ ctx_data->icp_dev_acquire_info->dev_type == dev_type)
+ hw_mgr_clk_info->base_clk +=
+ ctx_data->clk_info.base_clk;
+ }
+
+ return 0;
+}
+
+static bool cam_icp_update_clk_busy(struct cam_icp_hw_mgr *hw_mgr,
+ struct cam_icp_hw_ctx_data *ctx_data,
+ struct cam_icp_clk_info *hw_mgr_clk_info,
+ struct cam_icp_clk_bw_request *clk_info,
+ uint32_t base_clk)
+{
+ uint32_t next_clk_level;
+ uint32_t actual_clk;
+
+ /* 1. if current request frame cycles(fc) are more than previous
+ * frame fc
+ * Calculate the new base clock.
+ * if sum of base clocks are more than next available clk level
+ * Update clock rate, change curr_clk_rate to sum of base clock
+ * rates and make over_clked to zero
+ * else
+ * Update clock rate to next level, update curr_clk_rate and make
+ * overclked cnt to zero
+ * 2. if current fc is less than or equal to previous frame fc
+ * Still Bump up the clock to next available level
+ * if it is available, then update clock, make overclk cnt to
+ * zero
+ */
+ mutex_lock(&hw_mgr->hw_mgr_mutex);
+ ctx_data->clk_info.curr_fc = clk_info->frame_cycles;
+ ctx_data->clk_info.base_clk = base_clk;
+ hw_mgr_clk_info->over_clked = 0;
+ if (clk_info->frame_cycles > ctx_data->clk_info.curr_fc) {
+ cam_icp_calc_total_clk(hw_mgr, hw_mgr_clk_info,
+ ctx_data->icp_dev_acquire_info->dev_type);
+ actual_clk = cam_icp_get_actual_clk_rate(hw_mgr,
+ ctx_data, base_clk);
+ if (hw_mgr_clk_info->base_clk > actual_clk) {
+ hw_mgr_clk_info->curr_clk = hw_mgr_clk_info->base_clk;
+ } else {
+ next_clk_level = cam_icp_get_next_clk_rate(hw_mgr,
+ ctx_data, hw_mgr_clk_info->curr_clk);
+ hw_mgr_clk_info->curr_clk = next_clk_level;
+ }
+ } else {
+ hw_mgr_clk_info->curr_clk =
+ cam_icp_get_next_clk_rate(hw_mgr, ctx_data,
+ hw_mgr_clk_info->curr_clk);
+ }
+ mutex_unlock(&hw_mgr->hw_mgr_mutex);
+
+ return true;
+}
+
+static bool cam_icp_update_clk_overclk_free(struct cam_icp_hw_mgr *hw_mgr,
+ struct cam_icp_hw_ctx_data *ctx_data,
+ struct cam_icp_clk_info *hw_mgr_clk_info,
+ struct cam_icp_clk_bw_request *clk_info,
+ uint32_t base_clk)
+{
+ int rc = false;
+
+ /*
+ * In caseof no pending packets case
+ * 1. In caseof overclk cnt is less than threshold, increase
+ * overclk count and no update in the clock rate
+ * 2. In caseof overclk cnt is greater than or equal to threshold
+ * then lower clock rate by one level and update hw_mgr current
+ * clock value.
+ * a. In case of new clock rate greater than sum of clock
+ * rates, reset overclk count value to zero if it is
+ * overclock
+ * b. if it is less than sum of base clocks then go to next
+ * level of clock and make overclk count to zero
+ * c. if it is same as sum of base clock rates update overclock
+ * cnt to 0
+ */
+ if (hw_mgr_clk_info->over_clked < hw_mgr_clk_info->threshold) {
+ hw_mgr_clk_info->over_clked++;
+ rc = false;
+ } else {
+ hw_mgr_clk_info->curr_clk =
+ cam_icp_get_lower_clk_rate(hw_mgr, ctx_data,
+ hw_mgr_clk_info->curr_clk);
+ if (hw_mgr_clk_info->curr_clk > hw_mgr_clk_info->base_clk) {
+ if (cam_icp_is_over_clk(hw_mgr, ctx_data,
+ hw_mgr_clk_info))
+ hw_mgr_clk_info->over_clked = 0;
+ } else if (hw_mgr_clk_info->curr_clk <
+ hw_mgr_clk_info->base_clk) {
+ hw_mgr_clk_info->curr_clk =
+ cam_icp_get_next_clk_rate(hw_mgr, ctx_data,
+ hw_mgr_clk_info->curr_clk);
+ hw_mgr_clk_info->over_clked = 0;
+ } else if (hw_mgr_clk_info->curr_clk ==
+ hw_mgr_clk_info->base_clk) {
+ hw_mgr_clk_info->over_clked = 0;
+ }
+ rc = true;
+ }
+
+ return rc;
+}
+
+static bool cam_icp_update_clk_free(struct cam_icp_hw_mgr *hw_mgr,
+ struct cam_icp_hw_ctx_data *ctx_data,
+ struct cam_icp_clk_info *hw_mgr_clk_info,
+ struct cam_icp_clk_bw_request *clk_info,
+ uint32_t base_clk)
+{
+ int rc = false;
+ bool over_clocked = false;
+
+ ctx_data->clk_info.curr_fc = clk_info->frame_cycles;
+ ctx_data->clk_info.base_clk = base_clk;
+ mutex_lock(&hw_mgr->hw_mgr_mutex);
+ cam_icp_calc_total_clk(hw_mgr, hw_mgr_clk_info,
+ ctx_data->icp_dev_acquire_info->dev_type);
+ mutex_unlock(&hw_mgr->hw_mgr_mutex);
+
+ /*
+ * Current clock is not always sum of base clocks, due to
+ * clock scales update to next higher or lower levels, it
+ * equals to one of discrete clock values supported by hardware.
+ * So even current clock is higher than sum of base clocks, we
+ * can not consider it is over clocked. if it is greater than
+ * discrete clock level then only it is considered as over clock.
+ * 1. Handle over clock case
+ * 2. If current clock is less than sum of base clocks
+ * update current clock
+ * 3. If current clock is same as sum of base clocks no action
+ */
+
+ over_clocked = cam_icp_is_over_clk(hw_mgr, ctx_data,
+ hw_mgr_clk_info);
+
+ mutex_lock(&hw_mgr->hw_mgr_mutex);
+ if (hw_mgr_clk_info->curr_clk > hw_mgr_clk_info->base_clk &&
+ over_clocked) {
+ rc = cam_icp_update_clk_overclk_free(hw_mgr, ctx_data,
+ hw_mgr_clk_info, clk_info, base_clk);
+ } else if (hw_mgr_clk_info->curr_clk > hw_mgr_clk_info->base_clk) {
+ hw_mgr_clk_info->over_clked = 0;
+ rc = false;
+ } else if (hw_mgr_clk_info->curr_clk < hw_mgr_clk_info->base_clk) {
+ hw_mgr_clk_info->curr_clk = hw_mgr_clk_info->base_clk;
+ rc = true;
+ }
+ mutex_unlock(&hw_mgr->hw_mgr_mutex);
+
+ return rc;
+}
+
+static bool cam_icp_debug_clk_update(struct cam_icp_clk_info *hw_mgr_clk_info)
+{
+ if (icp_hw_mgr.icp_debug_clk < ICP_TURBO_VOTE &&
+ icp_hw_mgr.icp_debug_clk &&
+ icp_hw_mgr.icp_debug_clk != hw_mgr_clk_info->curr_clk) {
+ mutex_lock(&icp_hw_mgr.hw_mgr_mutex);
+ hw_mgr_clk_info->base_clk = icp_hw_mgr.icp_debug_clk;
+ hw_mgr_clk_info->curr_clk = icp_hw_mgr.icp_debug_clk;
+ hw_mgr_clk_info->uncompressed_bw = icp_hw_mgr.icp_debug_clk;
+ hw_mgr_clk_info->compressed_bw = icp_hw_mgr.icp_debug_clk;
+ mutex_unlock(&icp_hw_mgr.hw_mgr_mutex);
+ CAM_DBG(CAM_ICP, "bc = %d cc = %d",
+ hw_mgr_clk_info->base_clk, hw_mgr_clk_info->curr_clk);
+ return true;
+ }
+
+ return false;
+}
+
+static bool cam_icp_default_clk_update(struct cam_icp_clk_info *hw_mgr_clk_info)
+{
+ if (icp_hw_mgr.icp_default_clk != hw_mgr_clk_info->curr_clk) {
+ mutex_lock(&icp_hw_mgr.hw_mgr_mutex);
+ hw_mgr_clk_info->base_clk = icp_hw_mgr.icp_default_clk;
+ hw_mgr_clk_info->curr_clk = icp_hw_mgr.icp_default_clk;
+ hw_mgr_clk_info->uncompressed_bw = icp_hw_mgr.icp_default_clk;
+ hw_mgr_clk_info->compressed_bw = icp_hw_mgr.icp_default_clk;
+ mutex_unlock(&icp_hw_mgr.hw_mgr_mutex);
+ CAM_DBG(CAM_ICP, "bc = %d cc = %d",
+ hw_mgr_clk_info->base_clk, hw_mgr_clk_info->curr_clk);
+ return true;
+ }
+
+ return false;
+}
+
+static bool cam_icp_update_bw(struct cam_icp_hw_mgr *hw_mgr,
+ struct cam_icp_hw_ctx_data *ctx_data,
+ struct cam_icp_clk_info *hw_mgr_clk_info,
+ struct cam_icp_clk_bw_request *clk_info,
+ bool busy)
+{
+ int i;
+ struct cam_icp_hw_ctx_data *ctx;
+
+ /*
+ * If current request bandwidth is different from previous frames, then
+ * recalculate bandwidth of all contexts of same hardware and update
+ * voting of bandwidth
+ */
+ if (clk_info->uncompressed_bw == ctx_data->clk_info.uncompressed_bw)
+ return false;
+
+ if (busy &&
+ ctx_data->clk_info.uncompressed_bw > clk_info->uncompressed_bw)
+ return false;
+
+ ctx_data->clk_info.uncompressed_bw = clk_info->uncompressed_bw;
+ ctx_data->clk_info.compressed_bw = clk_info->compressed_bw;
+ hw_mgr_clk_info->uncompressed_bw = 0;
+ hw_mgr_clk_info->compressed_bw = 0;
+ for (i = 0; i < CAM_ICP_CTX_MAX; i++) {
+ ctx = &hw_mgr->ctx_data[i];
+ if (ctx->in_use &&
+ ctx->icp_dev_acquire_info->dev_type ==
+ ctx_data->icp_dev_acquire_info->dev_type) {
+ mutex_lock(&hw_mgr->hw_mgr_mutex);
+ hw_mgr_clk_info->uncompressed_bw +=
+ ctx->clk_info.uncompressed_bw;
+ hw_mgr_clk_info->compressed_bw +=
+ ctx->clk_info.compressed_bw;
+ mutex_unlock(&hw_mgr->hw_mgr_mutex);
+ }
+ }
+
+ return true;
+}
+
+static bool cam_icp_check_clk_update(struct cam_icp_hw_mgr *hw_mgr,
+ struct cam_icp_hw_ctx_data *ctx_data, int idx)
+{
+ bool busy, rc = false;
+ uint32_t base_clk;
+ struct cam_icp_clk_bw_request *clk_info;
+ struct hfi_frame_process_info *frame_info;
+ uint64_t req_id;
+ struct cam_icp_clk_info *hw_mgr_clk_info;
+
+ if (ctx_data->icp_dev_acquire_info->dev_type == CAM_ICP_RES_TYPE_BPS)
+ hw_mgr_clk_info = &hw_mgr->clk_info[ICP_CLK_HW_BPS];
+ else
+ hw_mgr_clk_info = &hw_mgr->clk_info[ICP_CLK_HW_IPE];
+
+ if (icp_hw_mgr.icp_debug_clk)
+ return cam_icp_debug_clk_update(hw_mgr_clk_info);
+
+ /* Check is there any pending frames in this context */
+ frame_info = &ctx_data->hfi_frame_process;
+ req_id = frame_info->request_id[idx];
+ busy = cam_icp_busy_prev_reqs(frame_info, req_id);
+ CAM_DBG(CAM_ICP, "busy = %d req_id = %lld", busy, req_id);
+
+ clk_info = &ctx_data->hfi_frame_process.clk_info[idx];
+ if (!clk_info->frame_cycles)
+ return cam_icp_default_clk_update(hw_mgr_clk_info);
+
+ /* Calculate base clk rate */
+ base_clk = cam_icp_mgr_calc_base_clk(
+ clk_info->frame_cycles, clk_info->budget_ns);
+ ctx_data->clk_info.rt_flag = clk_info->rt_flag;
+
+ if (busy)
+ rc = cam_icp_update_clk_busy(hw_mgr, ctx_data,
+ hw_mgr_clk_info, clk_info, base_clk);
+ else
+ rc = cam_icp_update_clk_free(hw_mgr, ctx_data,
+ hw_mgr_clk_info, clk_info, base_clk);
+
+ CAM_DBG(CAM_ICP, "bc = %d cc = %d busy = %d overclk = %d uc = %d",
+ hw_mgr_clk_info->base_clk, hw_mgr_clk_info->curr_clk,
+ busy, hw_mgr_clk_info->over_clked, rc);
+
+ return rc;
+}
+
+static bool cam_icp_check_bw_update(struct cam_icp_hw_mgr *hw_mgr,
+ struct cam_icp_hw_ctx_data *ctx_data, int idx)
+{
+ bool busy, rc = false;
+ struct cam_icp_clk_bw_request *clk_info;
+ struct cam_icp_clk_info *hw_mgr_clk_info;
+ struct hfi_frame_process_info *frame_info;
+ uint64_t req_id;
+
+ if (ctx_data->icp_dev_acquire_info->dev_type == CAM_ICP_RES_TYPE_BPS)
+ hw_mgr_clk_info = &hw_mgr->clk_info[ICP_CLK_HW_BPS];
+ else
+ hw_mgr_clk_info = &hw_mgr->clk_info[ICP_CLK_HW_IPE];
+
+ clk_info = &ctx_data->hfi_frame_process.clk_info[idx];
+ frame_info = &ctx_data->hfi_frame_process;
+ req_id = frame_info->request_id[idx];
+ busy = cam_icp_busy_prev_reqs(frame_info, req_id);
+ rc = cam_icp_update_bw(hw_mgr, ctx_data, hw_mgr_clk_info,
+ clk_info, busy);
+
+ CAM_DBG(CAM_ICP, "bw = %d update_bw = %d",
+ hw_mgr_clk_info->uncompressed_bw, rc);
+
+ return rc;
+}
+
+static int cam_icp_update_clk_rate(struct cam_icp_hw_mgr *hw_mgr,
+ struct cam_icp_hw_ctx_data *ctx_data)
+{
+ uint32_t id;
+ uint32_t curr_clk_rate;
+ struct cam_hw_intf *ipe0_dev_intf = NULL;
+ struct cam_hw_intf *ipe1_dev_intf = NULL;
+ struct cam_hw_intf *bps_dev_intf = NULL;
+ struct cam_hw_intf *dev_intf = NULL;
+
+ ipe0_dev_intf = hw_mgr->devices[CAM_ICP_DEV_IPE][0];
+ ipe1_dev_intf = hw_mgr->devices[CAM_ICP_DEV_IPE][1];
+ bps_dev_intf = hw_mgr->devices[CAM_ICP_DEV_BPS][0];
+
+
+ if ((!ipe0_dev_intf) || (!bps_dev_intf)) {
+ CAM_ERR(CAM_ICP, "dev intfs are wrong, failed to update clk");
+ return -EINVAL;
+ }
+
+ if (ctx_data->icp_dev_acquire_info->dev_type == CAM_ICP_RES_TYPE_BPS) {
+ dev_intf = bps_dev_intf;
+ curr_clk_rate = hw_mgr->clk_info[ICP_CLK_HW_BPS].curr_clk;
+ id = CAM_ICP_BPS_CMD_UPDATE_CLK;
+ } else {
+ dev_intf = ipe0_dev_intf;
+ curr_clk_rate = hw_mgr->clk_info[ICP_CLK_HW_IPE].curr_clk;
+ id = CAM_ICP_IPE_CMD_UPDATE_CLK;
+ }
+
+ dev_intf->hw_ops.process_cmd(dev_intf->hw_priv, id,
+ &curr_clk_rate, sizeof(curr_clk_rate));
+
+ if (ctx_data->icp_dev_acquire_info->dev_type != CAM_ICP_RES_TYPE_BPS)
+ if (ipe1_dev_intf)
+ ipe1_dev_intf->hw_ops.process_cmd(
+ ipe1_dev_intf->hw_priv, id,
+ &curr_clk_rate, sizeof(curr_clk_rate));
+
+ return 0;
+}
+
+static int cam_icp_update_cpas_vote(struct cam_icp_hw_mgr *hw_mgr,
+ struct cam_icp_hw_ctx_data *ctx_data)
+{
+ uint32_t id;
+ struct cam_hw_intf *ipe0_dev_intf = NULL;
+ struct cam_hw_intf *ipe1_dev_intf = NULL;
+ struct cam_hw_intf *bps_dev_intf = NULL;
+ struct cam_hw_intf *dev_intf = NULL;
+ struct cam_icp_clk_info *clk_info;
+ struct cam_icp_cpas_vote clk_update;
+
+ ipe0_dev_intf = hw_mgr->devices[CAM_ICP_DEV_IPE][0];
+ ipe1_dev_intf = hw_mgr->devices[CAM_ICP_DEV_IPE][1];
+ bps_dev_intf = hw_mgr->devices[CAM_ICP_DEV_BPS][0];
+
+ if ((!ipe0_dev_intf) || (!bps_dev_intf)) {
+ CAM_ERR(CAM_ICP, "dev intfs are wrong, failed to update clk");
+ return -EINVAL;
+ }
+
+ if (ctx_data->icp_dev_acquire_info->dev_type == CAM_ICP_RES_TYPE_BPS) {
+ dev_intf = bps_dev_intf;
+ clk_info = &hw_mgr->clk_info[ICP_CLK_HW_BPS];
+ id = CAM_ICP_BPS_CMD_VOTE_CPAS;
+ } else {
+ dev_intf = ipe0_dev_intf;
+ clk_info = &hw_mgr->clk_info[ICP_CLK_HW_IPE];
+ id = CAM_ICP_IPE_CMD_VOTE_CPAS;
+ }
+
+ clk_update.ahb_vote.type = CAM_VOTE_DYNAMIC;
+ clk_update.ahb_vote.vote.freq = clk_info->curr_clk;
+ clk_update.ahb_vote_valid = true;
+ clk_update.axi_vote.compressed_bw = clk_info->compressed_bw;
+ clk_update.axi_vote.uncompressed_bw = clk_info->uncompressed_bw;
+ clk_update.axi_vote_valid = true;
+ dev_intf->hw_ops.process_cmd(dev_intf->hw_priv, id,
+ &clk_update, sizeof(clk_update));
+
+ if (ctx_data->icp_dev_acquire_info->dev_type != CAM_ICP_RES_TYPE_BPS)
+ if (ipe1_dev_intf)
+ ipe1_dev_intf->hw_ops.process_cmd(
+ ipe1_dev_intf->hw_priv, id,
+ &clk_update, sizeof(clk_update));
+
+ return 0;
+}
+
+static int cam_icp_mgr_ipe_bps_clk_update(struct cam_icp_hw_mgr *hw_mgr,
+ struct cam_icp_hw_ctx_data *ctx_data, int idx)
+{
+ int rc = 0;
+
+ if (cam_icp_check_clk_update(hw_mgr, ctx_data, idx))
+ rc = cam_icp_update_clk_rate(hw_mgr, ctx_data);
+
+ if (cam_icp_check_bw_update(hw_mgr, ctx_data, idx))
+ rc |= cam_icp_update_cpas_vote(hw_mgr, ctx_data);
+
+ return rc;
+}
+
static int cam_icp_mgr_ipe_bps_resume(struct cam_icp_hw_mgr *hw_mgr,
struct cam_icp_hw_ctx_data *ctx_data)
{
@@ -128,8 +767,26 @@
return rc;
}
+static int cam_icp_set_dbg_default_clk(void *data, u64 val)
+{
+ icp_hw_mgr.icp_debug_clk = val;
+ return 0;
+}
+
+static int cam_icp_get_dbg_default_clk(void *data, u64 *val)
+{
+ *val = icp_hw_mgr.icp_debug_clk;
+ return 0;
+}
+
+DEFINE_SIMPLE_ATTRIBUTE(cam_icp_debug_default_clk,
+ cam_icp_get_dbg_default_clk,
+ cam_icp_set_dbg_default_clk, "%16llu");
+
static int cam_icp_hw_mgr_create_debugfs_entry(void)
{
+ int rc = 0;
+
icp_hw_mgr.dentry = debugfs_create_dir("camera_icp", NULL);
if (!icp_hw_mgr.dentry)
return -ENOMEM;
@@ -139,7 +796,8 @@
icp_hw_mgr.dentry,
&icp_hw_mgr.a5_debug)) {
debugfs_remove_recursive(icp_hw_mgr.dentry);
- return -ENOMEM;
+ rc = -ENOMEM;
+ goto err;
}
if (!debugfs_create_bool("icp_pc",
@@ -147,10 +805,23 @@
icp_hw_mgr.dentry,
&icp_hw_mgr.icp_pc_flag)) {
CAM_ERR(CAM_ICP, "failed to create icp_pc entry");
- return -ENOMEM;
+ rc = -ENOMEM;
+ goto err;
}
- return 0;
+ if (!debugfs_create_file("icp_debug_clk",
+ 0644,
+ icp_hw_mgr.dentry, NULL,
+ &cam_icp_debug_default_clk)) {
+ CAM_ERR(CAM_ICP, "failed to create icp_debug_clk entry");
+ rc = -ENOMEM;
+ goto err;
+ }
+
+ return rc;
+err:
+ debugfs_remove_recursive(icp_hw_mgr.dentry);
+ return rc;
}
static int cam_icp_mgr_process_cmd(void *priv, void *data)
@@ -210,6 +881,7 @@
ctx_data->ctxt_event_cb(ctx_data->context_priv, flag, &buf_data);
hfi_frame_process->request_id[idx] = 0;
clear_bit(idx, ctx_data->hfi_frame_process.bitmap);
+ hfi_frame_process->fw_process_flag[idx] = false;
mutex_unlock(&ctx_data->ctx_mutex);
return 0;
@@ -228,7 +900,7 @@
ioconfig_ack = (struct hfi_msg_ipebps_async_ack *)msg_ptr;
if (ioconfig_ack->err_type != HFI_ERR_SYS_NONE) {
CAM_ERR(CAM_ICP, "failed with error : %u",
- ioconfig_ack->err_type);
+ ioconfig_ack->err_type);
cam_icp_mgr_handle_frame_process(msg_ptr,
ICP_FRAME_PROCESS_FAILURE);
return -EIO;
@@ -791,6 +1463,9 @@
for (i = 0; i < CAM_FRAME_CMD_MAX; i++)
clear_bit(i, hw_mgr->ctx_data[ctx_id].hfi_frame_process.bitmap);
kfree(hw_mgr->ctx_data[ctx_id].hfi_frame_process.bitmap);
+ cam_icp_hw_mgr_clk_info_update(hw_mgr, &hw_mgr->ctx_data[ctx_id]);
+ hw_mgr->ctx_data[ctx_id].clk_info.curr_fc = 0;
+ hw_mgr->ctx_data[ctx_id].clk_info.base_clk = 0;
hw_mgr->ctxt_cnt--;
kfree(hw_mgr->ctx_data[ctx_id].icp_dev_acquire_info);
hw_mgr->ctx_data[ctx_id].icp_dev_acquire_info = NULL;
@@ -1135,13 +1810,18 @@
static int cam_icp_mgr_handle_config_err(
struct cam_hw_config_args *config_args,
- struct cam_icp_hw_ctx_data *ctx_data)
+ struct cam_icp_hw_ctx_data *ctx_data,
+ int idx)
{
struct cam_hw_done_event_data buf_data;
buf_data.request_id = *(uint64_t *)config_args->priv;
ctx_data->ctxt_event_cb(ctx_data->context_priv, true, &buf_data);
+ ctx_data->hfi_frame_process.request_id[idx] = 0;
+ ctx_data->hfi_frame_process.fw_process_flag[idx] = false;
+ clear_bit(idx, ctx_data->hfi_frame_process.bitmap);
+
return 0;
}
@@ -1180,6 +1860,8 @@
static int cam_icp_mgr_config_hw(void *hw_mgr_priv, void *config_hw_args)
{
int rc = 0;
+ int idx;
+ uint64_t req_id;
struct cam_icp_hw_mgr *hw_mgr = hw_mgr_priv;
struct cam_hw_config_args *config_args = config_hw_args;
struct cam_icp_hw_ctx_data *ctx_data = NULL;
@@ -1199,10 +1881,14 @@
mutex_lock(&ctx_data->ctx_mutex);
if (!ctx_data->in_use) {
CAM_ERR(CAM_ICP, "ctx is not in use");
- rc = -EINVAL;
- goto config_err;
+ return -EINVAL;
}
+ req_id = *(uint64_t *)config_args->priv;
+ idx = cam_icp_clk_idx_from_req_id(ctx_data, req_id);
+ ctx_data->hfi_frame_process.fw_process_flag[idx] = true;
+ cam_icp_mgr_ipe_bps_clk_update(hw_mgr, ctx_data, idx);
+
rc = cam_icp_mgr_enqueue_config(hw_mgr, config_args);
if (rc)
goto config_err;
@@ -1210,8 +1896,8 @@
return 0;
config_err:
+ cam_icp_mgr_handle_config_err(config_args, ctx_data, idx);
mutex_unlock(&ctx_data->ctx_mutex);
- cam_icp_mgr_handle_config_err(config_args, ctx_data);
return rc;
}
@@ -1250,14 +1936,8 @@
packet->header.op_code & 0xff);
return -EINVAL;
}
-
- if ((packet->num_cmd_buf > 1) || (!packet->num_patches) ||
- (!packet->num_io_configs)) {
- CAM_ERR(CAM_ICP, "wrong number of cmd/patch info: %u %u",
+ CAM_DBG(CAM_ICP, "number of cmd/patch info: %u %u",
packet->num_cmd_buf, packet->num_patches);
- return -EINVAL;
- }
-
return 0;
}
@@ -1266,25 +1946,29 @@
uint32_t *fw_cmd_buf_iova_addr)
{
int rc = 0;
- uint64_t iova_addr;
- size_t fw_cmd_buf_len;
+ int i;
+ uint64_t addr;
+ size_t len;
struct cam_cmd_buf_desc *cmd_desc = NULL;
cmd_desc = (struct cam_cmd_buf_desc *)
((uint32_t *) &packet->payload + packet->cmd_buf_offset/4);
- rc = cam_mem_get_io_buf(cmd_desc->mem_handle,
- hw_mgr->iommu_hdl, &iova_addr, &fw_cmd_buf_len);
- if (rc) {
- CAM_ERR(CAM_ICP, "unable to get src buf info for cmd buf: %x",
- hw_mgr->iommu_hdl);
- return rc;
+ *fw_cmd_buf_iova_addr = 0;
+ for (i = 0; i < packet->num_cmd_buf; i++) {
+ if (cmd_desc[i].type == CAM_CMD_BUF_FW) {
+ rc = cam_mem_get_io_buf(cmd_desc[i].mem_handle,
+ hw_mgr->iommu_hdl, &addr, &len);
+ if (rc) {
+ CAM_ERR(CAM_ICP, "get cmd buf failed %x",
+ hw_mgr->iommu_hdl);
+ return rc;
+ }
+ *fw_cmd_buf_iova_addr = addr;
+ *fw_cmd_buf_iova_addr =
+ (*fw_cmd_buf_iova_addr + cmd_desc[i].offset);
+ }
}
- CAM_DBG(CAM_ICP, "cmd_buf desc cpu and iova address: %pK %zu",
- (void *)iova_addr, fw_cmd_buf_len);
-
- *fw_cmd_buf_iova_addr = iova_addr;
- *fw_cmd_buf_iova_addr = (*fw_cmd_buf_iova_addr + cmd_desc->offset);
return rc;
}
@@ -1317,13 +2001,87 @@
}
}
+static int cam_icp_packet_generic_blob_handler(void *user_data,
+ uint32_t blob_type, uint32_t blob_size, uint8_t *blob_data)
+{
+ struct cam_icp_clk_bw_request *soc_req;
+ struct cam_icp_clk_bw_request *clk_info;
+ struct icp_cmd_generic_blob *blob;
+ struct cam_icp_hw_ctx_data *ctx_data;
+ uint32_t index;
+ int rc = 0;
+
+ if (!blob_data || (blob_size == 0)) {
+ CAM_ERR(CAM_ICP, "Invalid blob info %pK %d", blob_data,
+ blob_size);
+ return -EINVAL;
+ }
+
+ blob = (struct icp_cmd_generic_blob *)user_data;
+ ctx_data = blob->ctx;
+ index = blob->frame_info_idx;
+
+ switch (blob_type) {
+ case CAM_ICP_CMD_GENERIC_BLOB_CLK:
+ if (blob_size != sizeof(struct cam_icp_clk_bw_request)) {
+ rc = -EINVAL;
+ break;
+ }
+ clk_info = &ctx_data->hfi_frame_process.clk_info[index];
+ memset(clk_info, 0, sizeof(struct cam_icp_clk_bw_request));
+
+ soc_req = (struct cam_icp_clk_bw_request *)blob_data;
+ *clk_info = *soc_req;
+ CAM_DBG(CAM_ICP, "%llu %llu %d %d %d",
+ clk_info->budget_ns, clk_info->frame_cycles,
+ clk_info->rt_flag, clk_info->uncompressed_bw,
+ clk_info->compressed_bw);
+ break;
+
+ default:
+ CAM_WARN(CAM_ICP, "Invalid blob type %d", blob_type);
+ break;
+ }
+ return rc;
+}
+
+static int cam_icp_process_generic_cmd_buffer(
+ struct cam_packet *packet,
+ struct cam_icp_hw_ctx_data *ctx_data,
+ int32_t index)
+{
+ int i, rc = 0;
+ struct cam_cmd_buf_desc *cmd_desc = NULL;
+ struct icp_cmd_generic_blob cmd_generic_blob;
+
+ cmd_generic_blob.ctx = ctx_data;
+ cmd_generic_blob.frame_info_idx = index;
+
+ cmd_desc = (struct cam_cmd_buf_desc *)
+ ((uint32_t *) &packet->payload + packet->cmd_buf_offset/4);
+ for (i = 0; i < packet->num_cmd_buf; i++) {
+ if (!cmd_desc[i].length)
+ continue;
+
+ if (cmd_desc[i].meta_data != CAM_ICP_CMD_META_GENERIC_BLOB)
+ continue;
+
+ rc = cam_packet_util_process_generic_cmd_buffer(&cmd_desc[i],
+ cam_icp_packet_generic_blob_handler, &cmd_generic_blob);
+ if (rc)
+ CAM_ERR(CAM_ICP, "Failed in processing blobs %d", rc);
+ }
+
+ return rc;
+}
+
static int cam_icp_mgr_update_hfi_frame_process(
struct cam_icp_hw_ctx_data *ctx_data,
struct cam_packet *packet,
struct cam_hw_prepare_update_args *prepare_args,
int32_t *idx)
{
- int32_t index;
+ int32_t index, rc;
index = find_first_zero_bit(ctx_data->hfi_frame_process.bitmap,
ctx_data->hfi_frame_process.bits);
@@ -1335,7 +2093,12 @@
ctx_data->hfi_frame_process.request_id[index] =
packet->header.request_id;
-
+ rc = cam_icp_process_generic_cmd_buffer(packet, ctx_data, index);
+ if (rc) {
+ clear_bit(index, ctx_data->hfi_frame_process.bitmap);
+ ctx_data->hfi_frame_process.request_id[index] = -1;
+ return rc;
+ }
*idx = index;
return 0;
@@ -1475,6 +2238,7 @@
cam_icp_mgr_ipe_bps_power_collapse(hw_mgr,
NULL, 0);
cam_icp_mgr_hw_close(hw_mgr, NULL);
+ cam_icp_hw_mgr_reset_clk_info(hw_mgr);
}
return rc;
@@ -1730,6 +2494,9 @@
mutex_lock(&hw_mgr->hw_mgr_mutex);
if (!hw_mgr->ctxt_cnt) {
mutex_unlock(&hw_mgr->hw_mgr_mutex);
+ rc = cam_icp_clk_info_init(hw_mgr, ctx_data);
+ if (rc)
+ goto get_io_buf_failed;
rc = cam_icp_mgr_download_fw(hw_mgr, ctx_data);
if (rc)
goto get_io_buf_failed;
@@ -1775,6 +2542,7 @@
icp_dev_acquire_info, sizeof(struct cam_icp_acquire_dev_info)))
goto copy_to_user_failed;
+ cam_icp_ctx_clk_info_init(ctx_data);
mutex_unlock(&ctx_data->ctx_mutex);
CAM_DBG(CAM_ICP, "scratch size = %x fw_handle = %x",
(unsigned int)icp_dev_acquire_info->scratch_mem_size,
diff --git a/drivers/media/platform/msm/camera/cam_icp/icp_hw/icp_hw_mgr/cam_icp_hw_mgr.h b/drivers/media/platform/msm/camera/cam_icp/icp_hw/icp_hw_mgr/cam_icp_hw_mgr.h
index 27a86b2..d4f5482 100644
--- a/drivers/media/platform/msm/camera/cam_icp/icp_hw/icp_hw_mgr/cam_icp_hw_mgr.h
+++ b/drivers/media/platform/msm/camera/cam_icp/icp_hw/icp_hw_mgr/cam_icp_hw_mgr.h
@@ -24,7 +24,7 @@
#include "cam_req_mgr_workq.h"
#include "cam_mem_mgr.h"
#include "cam_smmu_api.h"
-
+#include "cam_soc_util.h"
#define CAM_ICP_ROLE_PARENT 1
#define CAM_ICP_ROLE_CHILD 2
@@ -45,6 +45,12 @@
#define ICP_FRAME_PROCESS_SUCCESS 0
#define ICP_FRAME_PROCESS_FAILURE 1
+#define ICP_CLK_HW_IPE 0x0
+#define ICP_CLK_HW_BPS 0x1
+#define ICP_CLK_HW_MAX 0x2
+
+#define ICP_OVER_CLK_THRESHOLD 15
+
/**
* struct icp_hfi_mem_info
* @qtbl: Memory info of queue table
@@ -97,6 +103,8 @@
* @request_id: Request id list
* @num_out_resources: Number of out syncs
* @out_resource: Out sync info
+ * @fw_process_flag: Frame process flag
+ * @clk_info: Clock information for a request
*/
struct hfi_frame_process_info {
struct hfi_cmd_ipebps_async hfi_frame_cmd[CAM_FRAME_CMD_MAX];
@@ -106,9 +114,28 @@
uint64_t request_id[CAM_FRAME_CMD_MAX];
uint32_t num_out_resources[CAM_FRAME_CMD_MAX];
uint32_t out_resource[CAM_FRAME_CMD_MAX][CAM_MAX_OUT_RES];
+ uint32_t fw_process_flag[CAM_FRAME_CMD_MAX];
+ struct cam_icp_clk_bw_request clk_info[CAM_FRAME_CMD_MAX];
};
/**
+ * struct cam_ctx_clk_info
+ * @curr_fc: Context latest request frame cycles
+ * @rt_flag: Flag to indicate real time request
+ * @base_clk: Base clock to process the request
+ * #uncompressed_bw: Current bandwidth voting
+ * @compressed_bw: Current compressed bandwidth voting
+ * @clk_rate: Supported clock rates for the context
+ */
+struct cam_ctx_clk_info {
+ uint32_t curr_fc;
+ uint32_t rt_flag;
+ uint32_t base_clk;
+ uint32_t uncompressed_bw;
+ uint32_t compressed_bw;
+ int32_t clk_rate[CAM_MAX_VOTE];
+};
+/**
* struct cam_icp_hw_ctx_data
* @context_priv: Context private data
* @ctx_mutex: Mutex for context
@@ -122,8 +149,9 @@
* @chain_ctx: Peer context
* @hfi_frame_process: Frame process command
* @wait_complete: Completion info
- * @ctx_id: Context Id
* @temp_payload: Payload for destroy handle data
+ * @ctx_id: Context Id
+ * @clk_info: Current clock info of a context
*/
struct cam_icp_hw_ctx_data {
void *context_priv;
@@ -140,6 +168,35 @@
struct completion wait_complete;
struct ipe_bps_destroy temp_payload;
uint32_t ctx_id;
+ struct cam_ctx_clk_info clk_info;
+};
+
+/**
+ * struct icp_cmd_generic_blob
+ * @ctx: Current context info
+ * @frame_info_idx: Index used for frame process info
+ */
+struct icp_cmd_generic_blob {
+ struct cam_icp_hw_ctx_data *ctx;
+ uint32_t frame_info_idx;
+};
+
+/**
+ * struct cam_icp_clk_info
+ * @base_clk: Base clock to process request
+ * @curr_clk: Current clock of hadrware
+ * @threshold: Threshold for overclk count
+ * @over_clked: Over clock count
+ * #uncompressed_bw: Current bandwidth voting
+ * @compressed_bw: Current compressed bandwidth voting
+ */
+struct cam_icp_clk_info {
+ uint32_t base_clk;
+ uint32_t curr_clk;
+ uint32_t threshold;
+ uint32_t over_clked;
+ uint32_t uncompressed_bw;
+ uint32_t compressed_bw;
};
/**
@@ -166,6 +223,9 @@
* @dentry: Debugfs entry
* @a5_debug: A5 debug flag
* @icp_pc_flag: Flag to enable/disable power collapse
+ * @icp_debug_clk: Set clock based on debug value
+ * @icp_default_clk: Set this clok if user doesn't supply
+ * @clk_info: Clock info of hardware
*/
struct cam_icp_hw_mgr {
struct mutex hw_mgr_mutex;
@@ -192,6 +252,9 @@
struct dentry *dentry;
bool a5_debug;
bool icp_pc_flag;
+ uint64_t icp_debug_clk;
+ uint64_t icp_default_clk;
+ struct cam_icp_clk_info clk_info[ICP_CLK_HW_MAX];
};
static int cam_icp_mgr_hw_close(void *hw_priv, void *hw_close_args);
diff --git a/drivers/media/platform/msm/camera/cam_icp/icp_hw/icp_hw_mgr/include/cam_bps_hw_intf.h b/drivers/media/platform/msm/camera/cam_icp/icp_hw/icp_hw_mgr/include/cam_bps_hw_intf.h
index 4427a30..d79187f 100644
--- a/drivers/media/platform/msm/camera/cam_icp/icp_hw/icp_hw_mgr/include/cam_bps_hw_intf.h
+++ b/drivers/media/platform/msm/camera/cam_icp/icp_hw/icp_hw_mgr/include/cam_bps_hw_intf.h
@@ -26,6 +26,7 @@
CAM_ICP_BPS_CMD_VOTE_CPAS,
CAM_ICP_BPS_CMD_CPAS_START,
CAM_ICP_BPS_CMD_CPAS_STOP,
+ CAM_ICP_BPS_CMD_UPDATE_CLK,
CAM_ICP_BPS_CMD_MAX,
};
diff --git a/drivers/media/platform/msm/camera/cam_icp/icp_hw/icp_hw_mgr/include/cam_ipe_hw_intf.h b/drivers/media/platform/msm/camera/cam_icp/icp_hw/icp_hw_mgr/include/cam_ipe_hw_intf.h
index 0db66c0..697757e 100644
--- a/drivers/media/platform/msm/camera/cam_icp/icp_hw/icp_hw_mgr/include/cam_ipe_hw_intf.h
+++ b/drivers/media/platform/msm/camera/cam_icp/icp_hw/icp_hw_mgr/include/cam_ipe_hw_intf.h
@@ -26,6 +26,7 @@
CAM_ICP_IPE_CMD_VOTE_CPAS,
CAM_ICP_IPE_CMD_CPAS_START,
CAM_ICP_IPE_CMD_CPAS_STOP,
+ CAM_ICP_IPE_CMD_UPDATE_CLK,
CAM_ICP_IPE_CMD_MAX,
};
diff --git a/drivers/media/platform/msm/camera/cam_icp/icp_hw/include/cam_icp_hw_mgr_intf.h b/drivers/media/platform/msm/camera/cam_icp/icp_hw/include/cam_icp_hw_mgr_intf.h
index 2f100ca..d99a878 100644
--- a/drivers/media/platform/msm/camera/cam_icp/icp_hw/include/cam_icp_hw_mgr_intf.h
+++ b/drivers/media/platform/msm/camera/cam_icp/icp_hw/include/cam_icp_hw_mgr_intf.h
@@ -18,7 +18,8 @@
#include <linux/of.h>
#include "cam_cpas_api.h"
-#define ICP_TURBO_VOTE 640000000
+#define ICP_TURBO_VOTE 600000000
+#define ICP_SVS_VOTE 400000000
int cam_icp_hw_mgr_init(struct device_node *of_node,
uint64_t *hw_mgr_hdl);
diff --git a/drivers/media/platform/msm/camera/cam_icp/icp_hw/ipe_hw/ipe_core.c b/drivers/media/platform/msm/camera/cam_icp/icp_hw/ipe_hw/ipe_core.c
index 79c3388..8630e34 100644
--- a/drivers/media/platform/msm/camera/cam_icp/icp_hw/ipe_hw/ipe_core.c
+++ b/drivers/media/platform/msm/camera/cam_icp/icp_hw/ipe_hw/ipe_core.c
@@ -73,8 +73,8 @@
cpas_vote.ahb_vote.type = CAM_VOTE_ABSOLUTE;
cpas_vote.ahb_vote.vote.level = CAM_SVS_VOTE;
- cpas_vote.axi_vote.compressed_bw = ICP_TURBO_VOTE;
- cpas_vote.axi_vote.uncompressed_bw = ICP_TURBO_VOTE;
+ cpas_vote.axi_vote.compressed_bw = CAM_CPAS_DEFAULT_AXI_BW;
+ cpas_vote.axi_vote.uncompressed_bw = CAM_CPAS_DEFAULT_AXI_BW;
rc = cam_cpas_start(core_info->cpas_handle,
&cpas_vote.ahb_vote, &cpas_vote.axi_vote);
@@ -263,6 +263,13 @@
case CAM_ICP_IPE_CMD_POWER_RESUME:
rc = cam_ipe_handle_resume(ipe_dev);
break;
+ case CAM_ICP_IPE_CMD_UPDATE_CLK: {
+ uint32_t clk_rate = *(uint32_t *)cmd_args;
+
+ CAM_DBG(CAM_ICP, "ipe_src_clk rate = %d", (int)clk_rate);
+ rc = cam_ipe_update_clk_rate(soc_info, clk_rate);
+ }
+ break;
default:
break;
}
diff --git a/drivers/media/platform/msm/camera/cam_icp/icp_hw/ipe_hw/ipe_soc.c b/drivers/media/platform/msm/camera/cam_icp/icp_hw/ipe_hw/ipe_soc.c
index e7b2733f..49176b5 100644
--- a/drivers/media/platform/msm/camera/cam_icp/icp_hw/ipe_hw/ipe_soc.c
+++ b/drivers/media/platform/msm/camera/cam_icp/icp_hw/ipe_hw/ipe_soc.c
@@ -135,3 +135,13 @@
return rc;
}
+
+int cam_ipe_update_clk_rate(struct cam_hw_soc_info *soc_info,
+ uint32_t clk_rate)
+{
+ if (!soc_info)
+ return -EINVAL;
+
+ return cam_soc_util_set_clk_rate(soc_info->clk[soc_info->src_clk_idx],
+ soc_info->clk_name[soc_info->src_clk_idx], clk_rate);
+}
diff --git a/drivers/media/platform/msm/camera/cam_icp/icp_hw/ipe_hw/ipe_soc.h b/drivers/media/platform/msm/camera/cam_icp/icp_hw/ipe_hw/ipe_soc.h
index ed8bb6e..8e5a38a 100644
--- a/drivers/media/platform/msm/camera/cam_icp/icp_hw/ipe_hw/ipe_soc.h
+++ b/drivers/media/platform/msm/camera/cam_icp/icp_hw/ipe_hw/ipe_soc.h
@@ -25,4 +25,7 @@
int cam_ipe_get_gdsc_control(struct cam_hw_soc_info *soc_info);
int cam_ipe_transfer_gdsc_control(struct cam_hw_soc_info *soc_info);
+
+int cam_ipe_update_clk_rate(struct cam_hw_soc_info *soc_info,
+ uint32_t clk_rate);
#endif /* CAM_IPE_SOC_H */
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 1fbc105..03c5eb3 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
@@ -656,8 +656,20 @@
case CAM_FORMAT_UBWC_NV12:
case CAM_FORMAT_UBWC_NV12_4R:
return PACKER_FMT_PLAIN_8_LSB_MSB_10;
+ case CAM_FORMAT_PLAIN16_16:
+ return PACKER_FMT_PLAIN_16_16BPP;
case CAM_FORMAT_PLAIN64:
return PACKER_FMT_PLAIN_64;
+ case CAM_FORMAT_PLAIN8:
+ return PACKER_FMT_PLAIN_8;
+ case CAM_FORMAT_PLAIN16_10:
+ return PACKER_FMT_PLAIN_16_10BPP;
+ case CAM_FORMAT_PLAIN16_12:
+ return PACKER_FMT_PLAIN_16_12BPP;
+ case CAM_FORMAT_PLAIN16_14:
+ return PACKER_FMT_PLAIN_16_14BPP;
+ case CAM_FORMAT_PLAIN32_20:
+ return PACKER_FMT_PLAIN_32_20BPP;
case CAM_FORMAT_MIPI_RAW_6:
case CAM_FORMAT_MIPI_RAW_8:
case CAM_FORMAT_MIPI_RAW_10:
@@ -665,14 +677,8 @@
case CAM_FORMAT_MIPI_RAW_14:
case CAM_FORMAT_MIPI_RAW_16:
case CAM_FORMAT_MIPI_RAW_20:
- case CAM_FORMAT_PLAIN128:
- case CAM_FORMAT_PLAIN8:
case CAM_FORMAT_PLAIN16_8:
- case CAM_FORMAT_PLAIN16_10:
- case CAM_FORMAT_PLAIN16_12:
- case CAM_FORMAT_PLAIN16_14:
- case CAM_FORMAT_PLAIN16_16:
- case CAM_FORMAT_PLAIN32_20:
+ case CAM_FORMAT_PLAIN128:
case CAM_FORMAT_PD8:
case CAM_FORMAT_PD10:
return PACKER_FMT_PLAIN_128;
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 6a148ca..96d5b6e 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
@@ -575,7 +575,6 @@
slot->status = CRM_SLOT_STATUS_REQ_APPLIED;
link->trigger_mask = 0;
CAM_DBG(CAM_CRM, "req is applied\n");
- __cam_req_mgr_check_next_req_slot(in_q);
idx = in_q->rd_idx;
__cam_req_mgr_dec_idx(
@@ -1370,7 +1369,11 @@
/*
* Do NOT reset req q slot data here, it can not be done
* here because we need to preserve the data to handle bubble.
+ *
+ * Check if any new req is pending in slot, if not finish the
+ * lower pipeline delay device with available req ids.
*/
+ __cam_req_mgr_check_next_req_slot(in_q);
__cam_req_mgr_inc_idx(&in_q->rd_idx, 1, in_q->num_slots);
}
rc = __cam_req_mgr_process_req(link, trigger_data->trigger);
diff --git a/drivers/media/platform/msm/camera/cam_sensor_module/cam_actuator/cam_actuator_core.c b/drivers/media/platform/msm/camera/cam_sensor_module/cam_actuator/cam_actuator_core.c
index b36fc68..bcf4133 100644
--- a/drivers/media/platform/msm/camera/cam_sensor_module/cam_actuator/cam_actuator_core.c
+++ b/drivers/media/platform/msm/camera/cam_sensor_module/cam_actuator/cam_actuator_core.c
@@ -16,6 +16,70 @@
#include "cam_sensor_util.h"
#include "cam_trace.h"
+static int32_t cam_actuator_i2c_modes_util(
+ struct camera_io_master *io_master_info,
+ struct i2c_settings_list *i2c_list)
+{
+ int32_t rc = 0;
+ uint32_t i, size;
+
+ if (i2c_list->op_code == CAM_SENSOR_I2C_WRITE_RANDOM) {
+ rc = camera_io_dev_write(io_master_info,
+ &(i2c_list->i2c_settings));
+ if (rc < 0) {
+ CAM_ERR(CAM_ACTUATOR,
+ "Failed to random write I2C settings: %d",
+ rc);
+ return rc;
+ }
+ } else if (i2c_list->op_code == CAM_SENSOR_I2C_WRITE_SEQ) {
+ rc = camera_io_dev_write_continuous(
+ io_master_info,
+ &(i2c_list->i2c_settings),
+ 0);
+ if (rc < 0) {
+ CAM_ERR(CAM_ACTUATOR,
+ "Failed to seq write I2C settings: %d",
+ rc);
+ return rc;
+ }
+ } else if (i2c_list->op_code == CAM_SENSOR_I2C_WRITE_BURST) {
+ rc = camera_io_dev_write_continuous(
+ io_master_info,
+ &(i2c_list->i2c_settings),
+ 1);
+ if (rc < 0) {
+ CAM_ERR(CAM_ACTUATOR,
+ "Failed to burst write I2C settings: %d",
+ rc);
+ return rc;
+ }
+ } else if (i2c_list->op_code == CAM_SENSOR_I2C_POLL) {
+ size = i2c_list->i2c_settings.size;
+ for (i = 0; i < size; i++) {
+ rc = camera_io_dev_poll(
+ io_master_info,
+ i2c_list->i2c_settings.
+ reg_setting[i].reg_addr,
+ i2c_list->i2c_settings.
+ reg_setting[i].reg_data,
+ i2c_list->i2c_settings.
+ reg_setting[i].data_mask,
+ i2c_list->i2c_settings.addr_type,
+ i2c_list->i2c_settings.data_type,
+ i2c_list->i2c_settings.
+ reg_setting[i].delay);
+ if (rc < 0) {
+ CAM_ERR(CAM_ACTUATOR,
+ "i2c poll apply setting Fail: %d", rc);
+ return rc;
+ }
+ }
+ }
+
+ return rc;
+}
+
int32_t cam_actuator_slaveInfo_pkt_parser(struct cam_actuator_ctrl_t *a_ctrl,
uint32_t *cmd_buf)
{
@@ -52,7 +116,6 @@
{
struct i2c_settings_list *i2c_list;
int32_t rc = 0;
- uint32_t i, size;
if (a_ctrl == NULL || i2c_set == NULL) {
CAM_ERR(CAM_ACTUATOR, "Invalid Args");
@@ -66,35 +129,14 @@
list_for_each_entry(i2c_list,
&(i2c_set->list_head), list) {
- if (i2c_list->op_code == CAM_SENSOR_I2C_WRITE_RANDOM) {
- rc = camera_io_dev_write(&(a_ctrl->io_master_info),
- &(i2c_list->i2c_settings));
- if (rc < 0) {
- CAM_ERR(CAM_ACTUATOR,
- "Failed in Applying i2c wrt settings");
- return rc;
- }
- } else if (i2c_list->op_code == CAM_SENSOR_I2C_POLL) {
- size = i2c_list->i2c_settings.size;
- for (i = 0; i < size; i++) {
- rc = camera_io_dev_poll(
- &(a_ctrl->io_master_info),
- i2c_list->i2c_settings.
- reg_setting[i].reg_addr,
- i2c_list->i2c_settings.
- reg_setting[i].reg_data,
- i2c_list->i2c_settings.
- reg_setting[i].data_mask,
- i2c_list->i2c_settings.addr_type,
- i2c_list->i2c_settings.data_type,
- i2c_list->i2c_settings.
- reg_setting[i].delay);
- if (rc < 0) {
- CAM_ERR(CAM_ACTUATOR,
- "i2c poll apply setting Fail");
- return rc;
- }
- }
+ rc = cam_actuator_i2c_modes_util(
+ &(a_ctrl->io_master_info),
+ i2c_list);
+ if (rc < 0) {
+ CAM_ERR(CAM_ACTUATOR,
+ "Failed to apply settings: %d",
+ rc);
+ return rc;
}
}
diff --git a/drivers/media/platform/msm/camera/cam_sensor_module/cam_cci/cam_cci_core.c b/drivers/media/platform/msm/camera/cam_sensor_module/cam_cci/cam_cci_core.c
index 975b301..c62b251 100644
--- a/drivers/media/platform/msm/camera/cam_sensor_module/cam_cci/cam_cci_core.c
+++ b/drivers/media/platform/msm/camera/cam_sensor_module/cam_cci/cam_cci_core.c
@@ -424,7 +424,8 @@
msg = &c_ctrl->cfg.cci_i2c_write_cfg;
*pack = 0;
- if (c_ctrl->cmd == MSM_CCI_I2C_WRITE_SEQ) {
+ if (c_ctrl->cmd == MSM_CCI_I2C_WRITE_SEQ ||
+ c_ctrl->cmd == MSM_CCI_I2C_WRITE_BURST) {
addr_len = cam_cci_convert_type_to_num_bytes(msg->addr_type);
len = (size + addr_len) <= (cci_dev->payload_size) ?
(size + addr_len):cci_dev->payload_size;
@@ -725,24 +726,29 @@
do {
if (i2c_msg->data_type == CAMERA_SENSOR_I2C_TYPE_BYTE) {
data[i++] = i2c_cmd->reg_data;
- reg_addr++;
+ if (c_ctrl->cmd == MSM_CCI_I2C_WRITE_SEQ)
+ reg_addr++;
} else {
if ((i + 1) <= cci_dev->payload_size) {
data[i++] = (i2c_cmd->reg_data &
0xFF00) >> 8; /* MSB */
data[i++] = i2c_cmd->reg_data &
0x00FF; /* LSB */
- reg_addr++;
+ if (c_ctrl->cmd ==
+ MSM_CCI_I2C_WRITE_SEQ)
+ reg_addr++;
} else
break;
}
i2c_cmd++;
--cmd_size;
- } while (((c_ctrl->cmd == MSM_CCI_I2C_WRITE_SEQ) || pack--) &&
+ } while (((c_ctrl->cmd == MSM_CCI_I2C_WRITE_SEQ ||
+ c_ctrl->cmd == MSM_CCI_I2C_WRITE_BURST) || pack--) &&
(cmd_size > 0) && (i <= cci_dev->payload_size));
free_size = cam_cci_get_queue_free_size(cci_dev, master,
queue);
- if ((c_ctrl->cmd == MSM_CCI_I2C_WRITE_SEQ) &&
+ if ((c_ctrl->cmd == MSM_CCI_I2C_WRITE_SEQ ||
+ c_ctrl->cmd == MSM_CCI_I2C_WRITE_BURST) &&
((i-1) == MSM_CCI_WRITE_DATA_PAYLOAD_SIZE_11) &&
cci_dev->support_seq_write && cmd_size > 0 &&
free_size > BURST_MIN_FREE_SIZE) {
@@ -1205,10 +1211,8 @@
rc = cam_cci_soc_release(cci_dev);
if (rc < 0) {
CAM_ERR(CAM_CCI, "Failed in releasing the cci: %d", rc);
- cam_cpas_stop(cci_dev->cpas_handle);
return rc;
}
- cam_cpas_stop(cci_dev->cpas_handle);
return rc;
}
@@ -1253,6 +1257,7 @@
break;
case MSM_CCI_I2C_WRITE:
case MSM_CCI_I2C_WRITE_SEQ:
+ case MSM_CCI_I2C_WRITE_BURST:
for (i = 0; i < NUM_QUEUES; i++) {
if (mutex_trylock(&cci_master_info->mutex_q[i])) {
rc = cam_cci_i2c_write(sd, c_ctrl, i,
@@ -1295,6 +1300,7 @@
break;
case MSM_CCI_I2C_WRITE:
case MSM_CCI_I2C_WRITE_SEQ:
+ case MSM_CCI_I2C_WRITE_BURST:
case MSM_CCI_I2C_WRITE_SYNC:
case MSM_CCI_I2C_WRITE_ASYNC:
case MSM_CCI_I2C_WRITE_SYNC_BLOCK:
diff --git a/drivers/media/platform/msm/camera/cam_sensor_module/cam_cci/cam_cci_dev.h b/drivers/media/platform/msm/camera/cam_sensor_module/cam_cci/cam_cci_dev.h
index cb01c6c..4c996e08 100644
--- a/drivers/media/platform/msm/camera/cam_sensor_module/cam_cci/cam_cci_dev.h
+++ b/drivers/media/platform/msm/camera/cam_sensor_module/cam_cci/cam_cci_dev.h
@@ -95,6 +95,7 @@
MSM_CCI_I2C_READ,
MSM_CCI_I2C_WRITE,
MSM_CCI_I2C_WRITE_SEQ,
+ MSM_CCI_I2C_WRITE_BURST,
MSM_CCI_I2C_WRITE_ASYNC,
MSM_CCI_GPIO_WRITE,
MSM_CCI_I2C_WRITE_SYNC,
diff --git a/drivers/media/platform/msm/camera/cam_sensor_module/cam_cci/cam_cci_soc.c b/drivers/media/platform/msm/camera/cam_sensor_module/cam_cci/cam_cci_soc.c
index 83cb49e3..8de4472 100644
--- a/drivers/media/platform/msm/camera/cam_sensor_module/cam_cci/cam_cci_soc.c
+++ b/drivers/media/platform/msm/camera/cam_sensor_module/cam_cci/cam_cci_soc.c
@@ -392,5 +392,7 @@
cci_dev->cycles_per_us = 0;
soc_info->src_clk_idx = 0;
+ cam_cpas_stop(cci_dev->cpas_handle);
+
return rc;
}
diff --git a/drivers/media/platform/msm/camera/cam_sensor_module/cam_flash/cam_flash_core.c b/drivers/media/platform/msm/camera/cam_sensor_module/cam_flash/cam_flash_core.c
index 5c64451..0aaf8b0c 100644
--- a/drivers/media/platform/msm/camera/cam_sensor_module/cam_flash/cam_flash_core.c
+++ b/drivers/media/platform/msm/camera/cam_sensor_module/cam_flash/cam_flash_core.c
@@ -67,7 +67,7 @@
}
soc_private = (struct cam_flash_private_soc *)
- &flash_ctrl->soc_info.soc_private;
+ flash_ctrl->soc_info.soc_private;
if (op == CAMERA_SENSOR_FLASH_OP_FIRELOW) {
for (i = 0; i < flash_ctrl->torch_num_sources; i++) {
@@ -349,7 +349,7 @@
} else if ((flash_data->opcode ==
CAMERA_SENSOR_FLASH_OP_FIRELOW) &&
(flash_data->cmn_attr.is_settings_valid)) {
- /* Turn Off Flash */
+ /* Turn On Torch */
if (fctrl->flash_state == CAM_FLASH_STATE_INIT) {
rc = cam_flash_low(fctrl, flash_data);
if (rc) {
@@ -427,7 +427,15 @@
return rc;
}
- csl_packet = (struct cam_packet *)generic_ptr;
+ if (config.offset > len_of_buffer) {
+ CAM_ERR(CAM_FLASH,
+ "offset is out of bounds: offset: %lld len: %zu",
+ config.offset, len_of_buffer);
+ return -EINVAL;
+ }
+
+ /* Add offset to the flash csl header */
+ csl_packet = (struct cam_packet *)(generic_ptr + config.offset);
switch (csl_packet->header.op_code & 0xFFFFFF) {
case CAM_FLASH_PACKET_OPCODE_INIT: {
diff --git a/drivers/media/platform/msm/camera/cam_sensor_module/cam_sensor/cam_sensor_core.c b/drivers/media/platform/msm/camera/cam_sensor_module/cam_sensor/cam_sensor_core.c
index 75205f9..edbb335 100644
--- a/drivers/media/platform/msm/camera/cam_sensor_module/cam_sensor/cam_sensor_core.c
+++ b/drivers/media/platform/msm/camera/cam_sensor_module/cam_sensor/cam_sensor_core.c
@@ -127,6 +127,70 @@
return rc;
}
+static int32_t cam_sensor_i2c_modes_util(
+ struct camera_io_master *io_master_info,
+ struct i2c_settings_list *i2c_list)
+{
+ int32_t rc = 0;
+ uint32_t i, size;
+
+ if (i2c_list->op_code == CAM_SENSOR_I2C_WRITE_RANDOM) {
+ rc = camera_io_dev_write(io_master_info,
+ &(i2c_list->i2c_settings));
+ if (rc < 0) {
+ CAM_ERR(CAM_SENSOR,
+ "Failed to random write I2C settings: %d",
+ rc);
+ return rc;
+ }
+ } else if (i2c_list->op_code == CAM_SENSOR_I2C_WRITE_SEQ) {
+ rc = camera_io_dev_write_continuous(
+ io_master_info,
+ &(i2c_list->i2c_settings),
+ 0);
+ if (rc < 0) {
+ CAM_ERR(CAM_SENSOR,
+ "Failed to seq write I2C settings: %d",
+ rc);
+ return rc;
+ }
+ } else if (i2c_list->op_code == CAM_SENSOR_I2C_WRITE_BURST) {
+ rc = camera_io_dev_write_continuous(
+ io_master_info,
+ &(i2c_list->i2c_settings),
+ 1);
+ if (rc < 0) {
+ CAM_ERR(CAM_SENSOR,
+ "Failed to burst write I2C settings: %d",
+ rc);
+ return rc;
+ }
+ } else if (i2c_list->op_code == CAM_SENSOR_I2C_POLL) {
+ size = i2c_list->i2c_settings.size;
+ for (i = 0; i < size; i++) {
+ rc = camera_io_dev_poll(
+ io_master_info,
+ i2c_list->i2c_settings.
+ reg_setting[i].reg_addr,
+ i2c_list->i2c_settings.
+ reg_setting[i].reg_data,
+ i2c_list->i2c_settings.
+ reg_setting[i].data_mask,
+ i2c_list->i2c_settings.addr_type,
+ i2c_list->i2c_settings.data_type,
+ i2c_list->i2c_settings.
+ reg_setting[i].delay);
+ if (rc < 0) {
+ CAM_ERR(CAM_SENSOR,
+ "i2c poll apply setting Fail: %d", rc);
+ return rc;
+ }
+ }
+ }
+
+ return rc;
+}
+
int32_t cam_sensor_update_i2c_info(struct cam_cmd_i2c_info *i2c_info,
struct cam_sensor_ctrl_t *s_ctrl)
{
@@ -715,12 +779,13 @@
if (i2c_set->is_settings_valid == 1) {
list_for_each_entry(i2c_list,
&(i2c_set->list_head), list) {
- rc = camera_io_dev_write(
+ rc = cam_sensor_i2c_modes_util(
&(s_ctrl->io_master_info),
- &(i2c_list->i2c_settings));
+ i2c_list);
if (rc < 0) {
CAM_ERR(CAM_SENSOR,
- "Failed to write the I2C settings");
+ "Failed to apply settings: %d",
+ rc);
return rc;
}
}
@@ -738,13 +803,13 @@
i2c_set->request_id == req_id) {
list_for_each_entry(i2c_list,
&(i2c_set->list_head), list) {
- rc = camera_io_dev_write(
+ rc = cam_sensor_i2c_modes_util(
&(s_ctrl->io_master_info),
- &(i2c_list->i2c_settings));
+ i2c_list);
if (rc < 0) {
CAM_ERR(CAM_SENSOR,
- "Fail to write the I2C settings: %d",
- rc);
+ "Failed to apply settings: %d",
+ rc);
return rc;
}
}
diff --git a/drivers/media/platform/msm/camera/cam_sensor_module/cam_sensor_io/cam_sensor_cci_i2c.c b/drivers/media/platform/msm/camera/cam_sensor_module/cam_sensor_io/cam_sensor_cci_i2c.c
index ca648f01..3257703 100644
--- a/drivers/media/platform/msm/camera/cam_sensor_module/cam_sensor_io/cam_sensor_cci_i2c.c
+++ b/drivers/media/platform/msm/camera/cam_sensor_module/cam_sensor_io/cam_sensor_cci_i2c.c
@@ -145,6 +145,23 @@
MSM_CCI_I2C_WRITE);
}
+int32_t cam_cci_i2c_write_continuous_table(
+ struct camera_io_master *client,
+ struct cam_sensor_i2c_reg_setting *write_setting,
+ uint8_t cam_sensor_i2c_write_flag)
+{
+ int32_t rc = 0;
+
+ if (cam_sensor_i2c_write_flag == 1)
+ rc = cam_cci_i2c_write_table_cmd(client, write_setting,
+ MSM_CCI_I2C_WRITE_BURST);
+ else if (cam_sensor_i2c_write_flag == 0)
+ rc = cam_cci_i2c_write_table_cmd(client, write_setting,
+ MSM_CCI_I2C_WRITE_SEQ);
+
+ return rc;
+}
+
static int32_t cam_cci_i2c_compare(struct cam_sensor_cci_client *client,
uint32_t addr, uint16_t data, uint16_t data_mask,
enum camera_sensor_i2c_type data_type,
diff --git a/drivers/media/platform/msm/camera/cam_sensor_module/cam_sensor_io/cam_sensor_i2c.h b/drivers/media/platform/msm/camera/cam_sensor_module/cam_sensor_io/cam_sensor_i2c.h
index 6207a8a..10b07ec 100644
--- a/drivers/media/platform/msm/camera/cam_sensor_module/cam_sensor_io/cam_sensor_i2c.h
+++ b/drivers/media/platform/msm/camera/cam_sensor_module/cam_sensor_io/cam_sensor_i2c.h
@@ -57,7 +57,7 @@
/**
* @client: CCI client structure
- * @cci_cmd: CCI command type
+ * @write_setting: I2C register setting
*
* This API handles CCI random write
*/
@@ -66,6 +66,18 @@
struct cam_sensor_i2c_reg_setting *write_setting);
/**
+ * @client: CCI client structure
+ * @write_setting: I2C register setting
+ * @cam_sensor_i2c_write_flag: burst or seq write
+ *
+ * This API handles CCI continuous write
+ */
+int32_t cam_cci_i2c_write_continuous_table(
+ struct camera_io_master *client,
+ struct cam_sensor_i2c_reg_setting *write_setting,
+ uint8_t cam_sensor_i2c_write_flag);
+
+/**
* @cci_client: CCI client structure
* @cci_cmd: CCI command type
*
diff --git a/drivers/media/platform/msm/camera/cam_sensor_module/cam_sensor_io/cam_sensor_io.c b/drivers/media/platform/msm/camera/cam_sensor_module/cam_sensor_io/cam_sensor_io.c
index f0142e7..c9cf088 100644
--- a/drivers/media/platform/msm/camera/cam_sensor_module/cam_sensor_io/cam_sensor_io.c
+++ b/drivers/media/platform/msm/camera/cam_sensor_module/cam_sensor_io/cam_sensor_io.c
@@ -114,6 +114,33 @@
}
}
+int32_t camera_io_dev_write_continuous(struct camera_io_master *io_master_info,
+ struct cam_sensor_i2c_reg_setting *write_setting,
+ uint8_t cam_sensor_i2c_write_flag)
+{
+ if (!write_setting || !io_master_info) {
+ CAM_ERR(CAM_SENSOR,
+ "Input parameters not valid ws: %pK ioinfo: %pK",
+ write_setting, io_master_info);
+ return -EINVAL;
+ }
+
+ if (io_master_info->master_type == CCI_MASTER) {
+ return cam_cci_i2c_write_continuous_table(io_master_info,
+ write_setting, cam_sensor_i2c_write_flag);
+ } else if (io_master_info->master_type == I2C_MASTER) {
+ return cam_qup_i2c_write_table(io_master_info,
+ write_setting);
+ } else if (io_master_info->master_type == SPI_MASTER) {
+ return cam_spi_write_table(io_master_info,
+ write_setting);
+ } else {
+ CAM_ERR(CAM_SENSOR, "Invalid Comm. Master:%d",
+ io_master_info->master_type);
+ return -EINVAL;
+ }
+}
+
int32_t camera_io_init(struct camera_io_master *io_master_info)
{
if (!io_master_info) {
diff --git a/drivers/media/platform/msm/camera/cam_sensor_module/cam_sensor_io/cam_sensor_io.h b/drivers/media/platform/msm/camera/cam_sensor_module/cam_sensor_io/cam_sensor_io.h
index 9a60fd0e..f6620c0 100644
--- a/drivers/media/platform/msm/camera/cam_sensor_module/cam_sensor_io/cam_sensor_io.h
+++ b/drivers/media/platform/msm/camera/cam_sensor_module/cam_sensor_io/cam_sensor_io.h
@@ -87,6 +87,18 @@
/**
* @io_master_info: I2C/SPI master information
+ * @write_setting: write settings information
+ * @cam_sensor_i2c_write_flag: differentiate between burst & seq
+ *
+ * This API abstracts write functionality based on master type and
+ * write flag for continuous write
+ */
+int32_t camera_io_dev_write_continuous(struct camera_io_master *io_master_info,
+ struct cam_sensor_i2c_reg_setting *write_setting,
+ uint8_t cam_sensor_i2c_write_flag);
+
+/**
+ * @io_master_info: I2C/SPI master information
* @addr: I2C address
* @data: I2C data
* @data_mask: I2C data mask
diff --git a/drivers/media/platform/msm/camera/cam_sensor_module/cam_sensor_utils/cam_sensor_cmn_header.h b/drivers/media/platform/msm/camera/cam_sensor_module/cam_sensor_utils/cam_sensor_cmn_header.h
index 6520042b..6ebd58b 100644
--- a/drivers/media/platform/msm/camera/cam_sensor_module/cam_sensor_utils/cam_sensor_cmn_header.h
+++ b/drivers/media/platform/msm/camera/cam_sensor_module/cam_sensor_utils/cam_sensor_cmn_header.h
@@ -204,6 +204,8 @@
enum cam_sensor_i2c_cmd_type {
CAM_SENSOR_I2C_WRITE_RANDOM,
+ CAM_SENSOR_I2C_WRITE_BURST,
+ CAM_SENSOR_I2C_WRITE_SEQ,
CAM_SENSOR_I2C_READ,
CAM_SENSOR_I2C_POLL
};
@@ -224,14 +226,6 @@
enum camera_vreg_type type;
};
-struct cam_sensor_module_power_setting {
- enum msm_camera_power_seq_type seq_type;
- unsigned short seq_val;
- uint32_t config_val_low;
- uint32_t config_val_high;
- unsigned short delay;
-};
-
struct msm_camera_gpio_num_info {
uint16_t gpio_num[SENSOR_SEQ_TYPE_MAX];
uint8_t valid[SENSOR_SEQ_TYPE_MAX];
@@ -333,26 +327,6 @@
void *data[10];
};
-struct cam_sensor_power_setting_array {
- struct cam_sensor_power_setting power_setting_a[MAX_POWER_CONFIG];
- struct cam_sensor_power_setting *power_setting;
- unsigned short size;
- struct cam_sensor_power_setting power_down_setting_a[MAX_POWER_CONFIG];
- struct cam_sensor_power_setting *power_down_setting;
- unsigned short size_down;
-};
-
-struct msm_camera_sensor_slave_info {
- enum msm_sensor_camera_id_t camera_id;
- unsigned short slave_addr;
- enum i2c_freq_mode i2c_freq_mode;
- enum camera_sensor_i2c_type addr_type;
- struct msm_sensor_id_info_t sensor_id_info;
- struct cam_sensor_power_setting_array power_setting_array;
- unsigned char is_init_params_valid;
- enum msm_sensor_output_format_t output_format;
-};
-
struct cam_sensor_board_info {
struct cam_camera_slave_info slave_info;
int32_t sensor_mount_angle;
diff --git a/drivers/media/platform/msm/camera/cam_sensor_module/cam_sensor_utils/cam_sensor_util.c b/drivers/media/platform/msm/camera/cam_sensor_module/cam_sensor_utils/cam_sensor_util.c
index 878ce73..f5df775 100644
--- a/drivers/media/platform/msm/camera/cam_sensor_module/cam_sensor_utils/cam_sensor_util.c
+++ b/drivers/media/platform/msm/camera/cam_sensor_module/cam_sensor_utils/cam_sensor_util.c
@@ -82,7 +82,7 @@
(struct cam_cmd_unconditional_wait *) *cmd_buf;
struct i2c_settings_list *i2c_list = NULL;
- if (i2c_list == NULL) {
+ if (list_ptr == NULL) {
CAM_ERR(CAM_SENSOR, "Invalid list ptr");
return -EINVAL;
}
@@ -196,6 +196,49 @@
return rc;
}
+static int32_t cam_sensor_handle_continuous_write(
+ struct cam_cmd_i2c_continuous_wr *cam_cmd_i2c_continuous_wr,
+ struct i2c_settings_array *i2c_reg_settings,
+ uint16_t *cmd_length_in_bytes, int32_t *offset,
+ struct list_head **list)
+{
+ struct i2c_settings_list *i2c_list;
+ int32_t rc = 0, cnt;
+
+ i2c_list = cam_sensor_get_i2c_ptr(i2c_reg_settings,
+ cam_cmd_i2c_continuous_wr->header.count);
+ if (i2c_list == NULL ||
+ i2c_list->i2c_settings.reg_setting == NULL) {
+ CAM_ERR(CAM_SENSOR, "Failed in allocating i2c_list");
+ return -ENOMEM;
+ }
+
+ *cmd_length_in_bytes = (sizeof(struct i2c_rdwr_header) +
+ sizeof(cam_cmd_i2c_continuous_wr->reg_addr) +
+ sizeof(struct cam_cmd_read) *
+ (cam_cmd_i2c_continuous_wr->header.count));
+ i2c_list->op_code = cam_cmd_i2c_continuous_wr->header.op_code;
+ i2c_list->i2c_settings.addr_type =
+ cam_cmd_i2c_continuous_wr->header.addr_type;
+
+ for (cnt = 0; cnt < (cam_cmd_i2c_continuous_wr->header.count);
+ cnt++) {
+ i2c_list->i2c_settings.reg_setting[cnt].reg_addr =
+ cam_cmd_i2c_continuous_wr->
+ reg_addr;
+ i2c_list->i2c_settings.
+ reg_setting[cnt].reg_data =
+ cam_cmd_i2c_continuous_wr->
+ data_read[cnt].reg_data;
+ i2c_list->i2c_settings.
+ reg_setting[cnt].data_mask = 0;
+ }
+ (*offset) += cnt;
+ *list = &(i2c_list->list);
+
+ return rc;
+}
+
/**
* Name : cam_sensor_i2c_pkt_parser
* Description : Parse CSL CCI packet and apply register settings
@@ -260,7 +303,29 @@
&cmd_length_in_bytes, &j, &list);
if (rc < 0) {
CAM_ERR(CAM_SENSOR,
- "Failed in random read %d", rc);
+ "Failed in random write %d", rc);
+ return rc;
+ }
+
+ cmd_buf += cmd_length_in_bytes /
+ sizeof(uint32_t);
+ byte_cnt += cmd_length_in_bytes;
+ break;
+ }
+ case CAMERA_SENSOR_CMD_TYPE_I2C_CONT_WR: {
+ uint16_t cmd_length_in_bytes = 0;
+ struct cam_cmd_i2c_continuous_wr
+ *cam_cmd_i2c_continuous_wr =
+ (struct cam_cmd_i2c_continuous_wr *)
+ cmd_buf;
+
+ rc = cam_sensor_handle_continuous_write(
+ cam_cmd_i2c_continuous_wr,
+ i2c_reg_settings,
+ &cmd_length_in_bytes, &j, &list);
+ if (rc < 0) {
+ CAM_ERR(CAM_SENSOR,
+ "Failed in continuous write %d", rc);
return rc;
}
@@ -1242,16 +1307,13 @@
CAM_ERR(CAM_SENSOR, "request gpio failed");
return no_gpio;
}
- if (power_setting->seq_val >= CAM_VREG_MAX ||
- !gpio_num_info) {
- CAM_ERR(CAM_SENSOR, "gpio index %d >= max %d",
- power_setting->seq_val,
- CAM_VREG_MAX);
+ if (!gpio_num_info) {
+ CAM_ERR(CAM_SENSOR, "Invalid gpio_num_info");
goto power_up_failed;
}
CAM_DBG(CAM_SENSOR, "gpio set val %d",
gpio_num_info->gpio_num
- [power_setting->seq_val]);
+ [power_setting->seq_type]);
rc = msm_cam_sensor_handle_reg_gpio(
power_setting->seq_type,
@@ -1349,11 +1411,11 @@
if (!gpio_num_info)
continue;
if (!gpio_num_info->valid
- [power_setting->seq_val])
+ [power_setting->seq_type])
continue;
gpio_set_value_cansleep(
gpio_num_info->gpio_num
- [power_setting->seq_val], GPIOF_OUT_INIT_LOW);
+ [power_setting->seq_type], GPIOF_OUT_INIT_LOW);
break;
case SENSOR_VANA:
case SENSOR_VDIG:
@@ -1519,12 +1581,12 @@
case SENSOR_CUSTOM_GPIO1:
case SENSOR_CUSTOM_GPIO2:
- if (!gpio_num_info->valid[pd->seq_val])
+ if (!gpio_num_info->valid[pd->seq_type])
continue;
gpio_set_value_cansleep(
gpio_num_info->gpio_num
- [pd->seq_val],
+ [pd->seq_type],
(int) pd->config_val);
break;
diff --git a/drivers/media/platform/msm/vidc/msm_vdec.c b/drivers/media/platform/msm/vidc/msm_vdec.c
index 56e7a99..cb03576 100644
--- a/drivers/media/platform/msm/vidc/msm_vdec.c
+++ b/drivers/media/platform/msm/vidc/msm_vdec.c
@@ -581,6 +581,13 @@
inst->prop.width[CAPTURE_PORT] = f->fmt.pix_mp.width;
inst->prop.height[CAPTURE_PORT] = f->fmt.pix_mp.height;
+ rc = msm_vidc_check_session_supported(inst);
+ if (rc) {
+ dprintk(VIDC_ERR,
+ "%s: session not supported\n", __func__);
+ goto err_invalid_fmt;
+ }
+
msm_comm_set_color_format(inst,
msm_comm_get_hal_output_buffer(inst),
f->fmt.pix_mp.pixelformat);
@@ -648,6 +655,12 @@
}
inst->prop.width[OUTPUT_PORT] = f->fmt.pix_mp.width;
inst->prop.height[OUTPUT_PORT] = f->fmt.pix_mp.height;
+ rc = msm_vidc_check_session_supported(inst);
+ if (rc) {
+ dprintk(VIDC_ERR,
+ "%s: session not supported\n", __func__);
+ goto err_invalid_fmt;
+ }
frame_sz.buffer_type = HAL_BUFFER_INPUT;
frame_sz.width = inst->prop.width[OUTPUT_PORT];
diff --git a/drivers/media/platform/msm/vidc/msm_venc.c b/drivers/media/platform/msm/vidc/msm_venc.c
index dfb2ad5..0d8cb76 100644
--- a/drivers/media/platform/msm/vidc/msm_venc.c
+++ b/drivers/media/platform/msm/vidc/msm_venc.c
@@ -2385,6 +2385,12 @@
inst->prop.width[CAPTURE_PORT] = f->fmt.pix_mp.width;
inst->prop.height[CAPTURE_PORT] = f->fmt.pix_mp.height;
+ rc = msm_vidc_check_session_supported(inst);
+ if (rc) {
+ dprintk(VIDC_ERR,
+ "%s: session not supported\n", __func__);
+ goto exit;
+ }
frame_sz.buffer_type = HAL_BUFFER_OUTPUT;
frame_sz.width = inst->prop.width[CAPTURE_PORT];
@@ -2443,6 +2449,12 @@
inst->prop.width[OUTPUT_PORT] = f->fmt.pix_mp.width;
inst->prop.height[OUTPUT_PORT] = f->fmt.pix_mp.height;
+ rc = msm_vidc_check_session_supported(inst);
+ if (rc) {
+ dprintk(VIDC_ERR,
+ "%s: session not supported\n", __func__);
+ goto exit;
+ }
frame_sz.buffer_type = HAL_BUFFER_INPUT;
frame_sz.width = inst->prop.width[OUTPUT_PORT];
diff --git a/drivers/media/platform/msm/vidc/msm_vidc.c b/drivers/media/platform/msm/vidc/msm_vidc.c
index 6a7588c..5149086 100644
--- a/drivers/media/platform/msm/vidc/msm_vidc.c
+++ b/drivers/media/platform/msm/vidc/msm_vidc.c
@@ -883,6 +883,13 @@
goto fail_start;
}
+ rc = msm_vidc_check_scaling_supported(inst);
+ if (rc) {
+ dprintk(VIDC_ERR,
+ "This session scaling is not supported %pK\n", inst);
+ goto fail_start;
+ }
+
/* Decide work mode for current session */
rc = msm_vidc_decide_work_mode(inst);
if (rc) {
diff --git a/drivers/media/platform/msm/vidc/msm_vidc_common.c b/drivers/media/platform/msm/vidc/msm_vidc_common.c
index 428bf71..6e29c53 100644
--- a/drivers/media/platform/msm/vidc/msm_vidc_common.c
+++ b/drivers/media/platform/msm/vidc/msm_vidc_common.c
@@ -2161,6 +2161,7 @@
"Got SYS_ERR but unable to identify core\n");
return;
}
+ hdev = core->device;
mutex_lock(&core->lock);
if (core->state == VIDC_CORE_UNINIT) {
@@ -2181,7 +2182,6 @@
msm_vidc_queue_v4l2_event(inst, V4L2_EVENT_MSM_VIDC_SYS_ERROR);
msm_comm_print_inst_info(inst);
}
- hdev = core->device;
dprintk(VIDC_DBG, "Calling core_release\n");
rc = call_hfi_op(hdev, core_release, hdev->hfi_device_data);
if (rc) {
@@ -3011,6 +3011,23 @@
return msm_vidc_deinit_core(inst);
}
+static int msm_comm_session_init_done(int flipped_state,
+ struct msm_vidc_inst *inst)
+{
+ int rc;
+
+ dprintk(VIDC_DBG, "inst %pK: waiting for session init done\n", inst);
+ rc = wait_for_state(inst, flipped_state, MSM_VIDC_OPEN_DONE,
+ HAL_SESSION_INIT_DONE);
+ if (rc) {
+ dprintk(VIDC_ERR, "Session init failed for inst %pK\n", inst);
+ msm_comm_generate_sys_error(inst);
+ return rc;
+ }
+
+ return rc;
+}
+
static int msm_comm_session_init(int flipped_state,
struct msm_vidc_inst *inst)
{
@@ -3693,8 +3710,7 @@
if (rc || state <= get_flipped_state(inst->state, state))
break;
case MSM_VIDC_OPEN_DONE:
- rc = wait_for_state(inst, flipped_state, MSM_VIDC_OPEN_DONE,
- HAL_SESSION_INIT_DONE);
+ rc = msm_comm_session_init_done(flipped_state, inst);
if (rc || state <= get_flipped_state(inst->state, state))
break;
case MSM_VIDC_LOAD_RESOURCES:
@@ -5472,10 +5488,6 @@
capability->width.max, capability->height.max);
rc = -ENOTSUPP;
}
-
- if (!rc && msm_vidc_check_scaling_supported(inst)) {
- rc = -ENOTSUPP;
- }
}
if (rc) {
dprintk(VIDC_ERR,
diff --git a/drivers/media/platform/msm/vidc/venus_hfi.c b/drivers/media/platform/msm/vidc/venus_hfi.c
index c94da61..dde6029 100644
--- a/drivers/media/platform/msm/vidc/venus_hfi.c
+++ b/drivers/media/platform/msm/vidc/venus_hfi.c
@@ -1047,9 +1047,9 @@
mutex_lock(&device->lock);
- if (device->power_enabled) {
- dprintk(VIDC_DBG, "Venus is busy\n");
- rc = -EBUSY;
+ if (!device->power_enabled) {
+ dprintk(VIDC_WARN, "%s: venus power off\n", __func__);
+ rc = -EINVAL;
goto exit;
}
__flush_debug_queue(device, NULL);
diff --git a/drivers/net/ethernet/emulex/benet/be_main.c b/drivers/net/ethernet/emulex/benet/be_main.c
index b3c9cbe..5626908 100644
--- a/drivers/net/ethernet/emulex/benet/be_main.c
+++ b/drivers/net/ethernet/emulex/benet/be_main.c
@@ -5186,7 +5186,9 @@
skb->inner_protocol_type != ENCAP_TYPE_ETHER ||
skb->inner_protocol != htons(ETH_P_TEB) ||
skb_inner_mac_header(skb) - skb_transport_header(skb) !=
- sizeof(struct udphdr) + sizeof(struct vxlanhdr))
+ sizeof(struct udphdr) + sizeof(struct vxlanhdr) ||
+ !adapter->vxlan_port ||
+ udp_hdr(skb)->dest != adapter->vxlan_port)
return features & ~(NETIF_F_CSUM_MASK | NETIF_F_GSO_MASK);
return features;
diff --git a/drivers/net/ethernet/mellanox/mlx4/icm.c b/drivers/net/ethernet/mellanox/mlx4/icm.c
index 2a9dd46..e1f9e7c 100644
--- a/drivers/net/ethernet/mellanox/mlx4/icm.c
+++ b/drivers/net/ethernet/mellanox/mlx4/icm.c
@@ -118,8 +118,13 @@
if (!buf)
return -ENOMEM;
+ if (offset_in_page(buf)) {
+ dma_free_coherent(dev, PAGE_SIZE << order,
+ buf, sg_dma_address(mem));
+ return -ENOMEM;
+ }
+
sg_set_buf(mem, buf, PAGE_SIZE << order);
- BUG_ON(mem->offset);
sg_dma_len(mem) = PAGE_SIZE << order;
return 0;
}
diff --git a/drivers/net/ethernet/mellanox/mlx4/main.c b/drivers/net/ethernet/mellanox/mlx4/main.c
index b2ca8a6..551786f 100644
--- a/drivers/net/ethernet/mellanox/mlx4/main.c
+++ b/drivers/net/ethernet/mellanox/mlx4/main.c
@@ -42,6 +42,7 @@
#include <linux/io-mapping.h>
#include <linux/delay.h>
#include <linux/kmod.h>
+#include <linux/etherdevice.h>
#include <net/devlink.h>
#include <linux/mlx4/device.h>
@@ -782,6 +783,23 @@
}
EXPORT_SYMBOL(mlx4_is_slave_active);
+void mlx4_handle_eth_header_mcast_prio(struct mlx4_net_trans_rule_hw_ctrl *ctrl,
+ struct _rule_hw *eth_header)
+{
+ if (is_multicast_ether_addr(eth_header->eth.dst_mac) ||
+ is_broadcast_ether_addr(eth_header->eth.dst_mac)) {
+ struct mlx4_net_trans_rule_hw_eth *eth =
+ (struct mlx4_net_trans_rule_hw_eth *)eth_header;
+ struct _rule_hw *next_rule = (struct _rule_hw *)(eth + 1);
+ bool last_rule = next_rule->size == 0 && next_rule->id == 0 &&
+ next_rule->rsvd == 0;
+
+ if (last_rule)
+ ctrl->prio = cpu_to_be16(MLX4_DOMAIN_NIC);
+ }
+}
+EXPORT_SYMBOL(mlx4_handle_eth_header_mcast_prio);
+
static void slave_adjust_steering_mode(struct mlx4_dev *dev,
struct mlx4_dev_cap *dev_cap,
struct mlx4_init_hca_param *hca_param)
diff --git a/drivers/net/ethernet/mellanox/mlx4/resource_tracker.c b/drivers/net/ethernet/mellanox/mlx4/resource_tracker.c
index 32f76bf..1822382 100644
--- a/drivers/net/ethernet/mellanox/mlx4/resource_tracker.c
+++ b/drivers/net/ethernet/mellanox/mlx4/resource_tracker.c
@@ -4165,22 +4165,6 @@
return 0;
}
-static void handle_eth_header_mcast_prio(struct mlx4_net_trans_rule_hw_ctrl *ctrl,
- struct _rule_hw *eth_header)
-{
- if (is_multicast_ether_addr(eth_header->eth.dst_mac) ||
- is_broadcast_ether_addr(eth_header->eth.dst_mac)) {
- struct mlx4_net_trans_rule_hw_eth *eth =
- (struct mlx4_net_trans_rule_hw_eth *)eth_header;
- struct _rule_hw *next_rule = (struct _rule_hw *)(eth + 1);
- bool last_rule = next_rule->size == 0 && next_rule->id == 0 &&
- next_rule->rsvd == 0;
-
- if (last_rule)
- ctrl->prio = cpu_to_be16(MLX4_DOMAIN_NIC);
- }
-}
-
/*
* In case of missing eth header, append eth header with a MAC address
* assigned to the VF.
@@ -4364,10 +4348,7 @@
header_id = map_hw_to_sw_id(be16_to_cpu(rule_header->id));
if (header_id == MLX4_NET_TRANS_RULE_ID_ETH)
- handle_eth_header_mcast_prio(ctrl, rule_header);
-
- if (slave == dev->caps.function)
- goto execute;
+ mlx4_handle_eth_header_mcast_prio(ctrl, rule_header);
switch (header_id) {
case MLX4_NET_TRANS_RULE_ID_ETH:
@@ -4395,7 +4376,6 @@
goto err_put_qp;
}
-execute:
err = mlx4_cmd_imm(dev, inbox->dma, &vhcr->out_param,
vhcr->in_modifier, 0,
MLX4_QP_FLOW_STEERING_ATTACH, MLX4_CMD_TIME_CLASS_A,
@@ -4474,6 +4454,7 @@
struct res_qp *rqp;
struct res_fs_rule *rrule;
u64 mirr_reg_id;
+ int qpn;
if (dev->caps.steering_mode !=
MLX4_STEERING_MODE_DEVICE_MANAGED)
@@ -4490,10 +4471,11 @@
}
mirr_reg_id = rrule->mirr_rule_id;
kfree(rrule->mirr_mbox);
+ qpn = rrule->qpn;
/* Release the rule form busy state before removal */
put_res(dev, slave, vhcr->in_param, RES_FS_RULE);
- err = get_res(dev, slave, rrule->qpn, RES_QP, &rqp);
+ err = get_res(dev, slave, qpn, RES_QP, &rqp);
if (err)
return err;
@@ -4518,7 +4500,7 @@
if (!err)
atomic_dec(&rqp->ref_count);
out:
- put_res(dev, slave, rrule->qpn, RES_QP);
+ put_res(dev, slave, qpn, RES_QP);
return err;
}
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/eswitch_offloads.c b/drivers/net/ethernet/mellanox/mlx5/core/eswitch_offloads.c
index b08b9e2..6ffd5d2 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/eswitch_offloads.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/eswitch_offloads.c
@@ -672,6 +672,12 @@
if (err)
goto err_reps;
}
+
+ /* disable PF RoCE so missed packets don't go through RoCE steering */
+ mlx5_dev_list_lock();
+ mlx5_remove_dev_by_protocol(esw->dev, MLX5_INTERFACE_PROTOCOL_IB);
+ mlx5_dev_list_unlock();
+
return 0;
err_reps:
@@ -695,6 +701,11 @@
{
int err, err1, num_vfs = esw->dev->priv.sriov.num_vfs;
+ /* enable back PF RoCE */
+ mlx5_dev_list_lock();
+ mlx5_add_dev_by_protocol(esw->dev, MLX5_INTERFACE_PROTOCOL_IB);
+ mlx5_dev_list_unlock();
+
mlx5_eswitch_disable_sriov(esw);
err = mlx5_eswitch_enable_sriov(esw, num_vfs, SRIOV_LEGACY);
if (err) {
diff --git a/drivers/net/ethernet/realtek/r8169.c b/drivers/net/ethernet/realtek/r8169.c
index bf000d8..2c4350a 100644
--- a/drivers/net/ethernet/realtek/r8169.c
+++ b/drivers/net/ethernet/realtek/r8169.c
@@ -326,6 +326,7 @@
static const struct pci_device_id rtl8169_pci_tbl[] = {
{ PCI_DEVICE(PCI_VENDOR_ID_REALTEK, 0x8129), 0, 0, RTL_CFG_0 },
{ PCI_DEVICE(PCI_VENDOR_ID_REALTEK, 0x8136), 0, 0, RTL_CFG_2 },
+ { PCI_DEVICE(PCI_VENDOR_ID_REALTEK, 0x8161), 0, 0, RTL_CFG_1 },
{ PCI_DEVICE(PCI_VENDOR_ID_REALTEK, 0x8167), 0, 0, RTL_CFG_0 },
{ PCI_DEVICE(PCI_VENDOR_ID_REALTEK, 0x8168), 0, 0, RTL_CFG_1 },
{ PCI_DEVICE(PCI_VENDOR_ID_REALTEK, 0x8169), 0, 0, RTL_CFG_0 },
diff --git a/drivers/net/ethernet/renesas/sh_eth.c b/drivers/net/ethernet/renesas/sh_eth.c
index a2d218b..12be259 100644
--- a/drivers/net/ethernet/renesas/sh_eth.c
+++ b/drivers/net/ethernet/renesas/sh_eth.c
@@ -819,6 +819,7 @@
.tsu = 1,
.hw_crc = 1,
.select_mii = 1,
+ .shift_rd0 = 1,
};
/* SH7763 */
diff --git a/drivers/net/phy/phy_device.c b/drivers/net/phy/phy_device.c
index 32b555a..9e7b783 100644
--- a/drivers/net/phy/phy_device.c
+++ b/drivers/net/phy/phy_device.c
@@ -1792,7 +1792,7 @@
.phy_id = 0xffffffff,
.phy_id_mask = 0xffffffff,
.name = "Generic PHY",
- .soft_reset = genphy_soft_reset,
+ .soft_reset = genphy_no_soft_reset,
.config_init = genphy_config_init,
.features = PHY_GBIT_FEATURES | SUPPORTED_MII |
SUPPORTED_AUI | SUPPORTED_FIBRE |
diff --git a/drivers/net/usb/asix_devices.c b/drivers/net/usb/asix_devices.c
index dc7b639..50737de 100644
--- a/drivers/net/usb/asix_devices.c
+++ b/drivers/net/usb/asix_devices.c
@@ -1369,6 +1369,7 @@
.probe = usbnet_probe,
.suspend = asix_suspend,
.resume = asix_resume,
+ .reset_resume = asix_resume,
.disconnect = usbnet_disconnect,
.supports_autosuspend = 1,
.disable_hub_initiated_lpm = 1,
diff --git a/drivers/net/wireless/ath/ath10k/wmi-ops.h b/drivers/net/wireless/ath/ath10k/wmi-ops.h
index c9a8bb1..c7956e1 100644
--- a/drivers/net/wireless/ath/ath10k/wmi-ops.h
+++ b/drivers/net/wireless/ath/ath10k/wmi-ops.h
@@ -660,6 +660,9 @@
struct sk_buff *skb;
u32 cmd_id;
+ if (!ar->wmi.ops->gen_vdev_spectral_conf)
+ return -EOPNOTSUPP;
+
skb = ar->wmi.ops->gen_vdev_spectral_conf(ar, arg);
if (IS_ERR(skb))
return PTR_ERR(skb);
@@ -675,6 +678,9 @@
struct sk_buff *skb;
u32 cmd_id;
+ if (!ar->wmi.ops->gen_vdev_spectral_enable)
+ return -EOPNOTSUPP;
+
skb = ar->wmi.ops->gen_vdev_spectral_enable(ar, vdev_id, trigger,
enable);
if (IS_ERR(skb))
diff --git a/drivers/net/wireless/ath/wil6210/debugfs.c b/drivers/net/wireless/ath/wil6210/debugfs.c
index 60ea0ec..45a8081 100644
--- a/drivers/net/wireless/ath/wil6210/debugfs.c
+++ b/drivers/net/wireless/ath/wil6210/debugfs.c
@@ -20,7 +20,6 @@
#include <linux/pci.h>
#include <linux/rtnetlink.h>
#include <linux/power_supply.h>
-
#include "wil6210.h"
#include "wmi.h"
#include "txrx.h"
@@ -30,7 +29,6 @@
static u32 mem_addr;
static u32 dbg_txdesc_index;
static u32 dbg_vring_index; /* 24+ for Rx, 0..23 for Tx */
-u32 vring_idle_trsh = 16; /* HW fetches up to 16 descriptors at once */
enum dbg_off_type {
doff_u32 = 0,
@@ -1768,6 +1766,7 @@
WIL_FIELD(chip_revision, 0444, doff_u8),
WIL_FIELD(abft_len, 0644, doff_u8),
WIL_FIELD(wakeup_trigger, 0644, doff_u8),
+ WIL_FIELD(vring_idle_trsh, 0644, doff_u32),
{},
};
@@ -1783,8 +1782,6 @@
{"desc_index", 0644, (ulong)&dbg_txdesc_index, doff_u32},
{"vring_index", 0644, (ulong)&dbg_vring_index, doff_u32},
{"mem_addr", 0644, (ulong)&mem_addr, doff_u32},
- {"vring_idle_trsh", 0644, (ulong)&vring_idle_trsh,
- doff_u32},
{"led_polarity", 0644, (ulong)&led_polarity, doff_u8},
{},
};
diff --git a/drivers/net/wireless/ath/wil6210/main.c b/drivers/net/wireless/ath/wil6210/main.c
index 409b8cb..d90fe1c 100644
--- a/drivers/net/wireless/ath/wil6210/main.c
+++ b/drivers/net/wireless/ath/wil6210/main.c
@@ -584,6 +584,7 @@
WMI_WAKEUP_TRIGGER_BCAST;
memset(&wil->suspend_stats, 0, sizeof(wil->suspend_stats));
wil->suspend_stats.min_suspend_time = ULONG_MAX;
+ wil->vring_idle_trsh = 16;
return 0;
@@ -1042,6 +1043,12 @@
wil_s(wil, RGF_CAF_ICR + offsetof(struct RGF_ICR, ICR), 0);
wil_w(wil, RGF_CAF_ICR + offsetof(struct RGF_ICR, IMV), ~0);
+ if (wil->fw_calib_result > 0) {
+ __le32 val = cpu_to_le32(wil->fw_calib_result |
+ (CALIB_RESULT_SIGNATURE << 8));
+ wil_w(wil, RGF_USER_FW_CALIB_RESULT, (u32 __force)val);
+ }
+
wil_release_cpu(wil);
}
diff --git a/drivers/net/wireless/ath/wil6210/txrx.c b/drivers/net/wireless/ath/wil6210/txrx.c
index 8f1e79b4..8fe2239 100644
--- a/drivers/net/wireless/ath/wil6210/txrx.c
+++ b/drivers/net/wireless/ath/wil6210/txrx.c
@@ -1666,7 +1666,7 @@
/* performance monitoring */
used = wil_vring_used_tx(vring);
- if (wil_val_in_range(vring_idle_trsh,
+ if (wil_val_in_range(wil->vring_idle_trsh,
used, used + descs_used)) {
txdata->idle += get_cycles() - txdata->last_idle;
wil_dbg_txrx(wil, "Ring[%2d] not idle %d -> %d\n",
@@ -1813,7 +1813,7 @@
/* performance monitoring */
used = wil_vring_used_tx(vring);
- if (wil_val_in_range(vring_idle_trsh,
+ if (wil_val_in_range(wil->vring_idle_trsh,
used, used + nr_frags + 1)) {
txdata->idle += get_cycles() - txdata->last_idle;
wil_dbg_txrx(wil, "Ring[%2d] not idle %d -> %d\n",
@@ -2175,7 +2175,7 @@
/* performance monitoring */
used_new = wil_vring_used_tx(vring);
- if (wil_val_in_range(vring_idle_trsh,
+ if (wil_val_in_range(wil->vring_idle_trsh,
used_new, used_before_complete)) {
wil_dbg_txrx(wil, "Ring[%2d] idle %d -> %d\n",
ringid, used_before_complete, used_new);
diff --git a/drivers/net/wireless/ath/wil6210/wil6210.h b/drivers/net/wireless/ath/wil6210/wil6210.h
index 17d7f19..b4ca4e3 100644
--- a/drivers/net/wireless/ath/wil6210/wil6210.h
+++ b/drivers/net/wireless/ath/wil6210/wil6210.h
@@ -31,7 +31,6 @@
extern unsigned int mtu_max;
extern unsigned short rx_ring_overflow_thrsh;
extern int agg_wsize;
-extern u32 vring_idle_trsh;
extern bool rx_align_2;
extern bool rx_large_buf;
extern bool debug_fw;
@@ -175,6 +174,10 @@
#define RGF_USER_USER_SCRATCH_PAD (0x8802bc)
#define RGF_USER_BL (0x880A3C) /* Boot Loader */
#define RGF_USER_FW_REV_ID (0x880a8c) /* chip revision */
+#define RGF_USER_FW_CALIB_RESULT (0x880a90) /* b0-7:result
+ * b8-15:signature
+ */
+ #define CALIB_RESULT_SIGNATURE (0x11)
#define RGF_USER_CLKS_CTL_0 (0x880abc)
#define BIT_USER_CLKS_CAR_AHB_SW_SEL BIT(1) /* ref clk/PLL */
#define BIT_USER_CLKS_RST_PWGD BIT(11) /* reset on "power good" */
@@ -694,6 +697,7 @@
u8 vring2cid_tid[WIL6210_MAX_TX_RINGS][2]; /* [0] - CID, [1] - TID */
struct wil_sta_info sta[WIL6210_MAX_CID];
int bcast_vring;
+ u32 vring_idle_trsh; /* HW fetches up to 16 descriptors at once */
bool use_extended_dma_addr; /* indicates whether we are using 48 bits */
/* scan */
struct cfg80211_scan_request *scan_request;
@@ -733,6 +737,8 @@
bool tt_data_set;
struct wmi_tt_data tt_data;
+ int fw_calib_result;
+
#ifdef CONFIG_PM
#ifdef CONFIG_PM_SLEEP
struct notifier_block pm_notify;
diff --git a/drivers/net/wireless/ath/wil6210/wmi.c b/drivers/net/wireless/ath/wil6210/wmi.c
index dc1b30c..43cdaef 100644
--- a/drivers/net/wireless/ath/wil6210/wmi.c
+++ b/drivers/net/wireless/ath/wil6210/wmi.c
@@ -345,6 +345,11 @@
strlcpy(wdev->wiphy->fw_version, wil->fw_version,
sizeof(wdev->wiphy->fw_version));
+ if (len > offsetof(struct wmi_ready_event, rfc_read_calib_result)) {
+ wil_dbg_wmi(wil, "rfc calibration result %d\n",
+ evt->rfc_read_calib_result);
+ wil->fw_calib_result = evt->rfc_read_calib_result;
+ }
wil_set_recovery_state(wil, fw_recovery_idle);
set_bit(wil_status_fwready, wil->status);
/* let the reset sequence continue */
diff --git a/drivers/net/wireless/ath/wil6210/wmi.h b/drivers/net/wireless/ath/wil6210/wmi.h
index 4e31c2f..1b426d7 100644
--- a/drivers/net/wireless/ath/wil6210/wmi.h
+++ b/drivers/net/wireless/ath/wil6210/wmi.h
@@ -1300,6 +1300,8 @@
/* enum wmi_phy_capability */
u8 phy_capability;
u8 numof_additional_mids;
+ u8 rfc_read_calib_result;
+ u8 reserved[3];
} __packed;
/* WMI_NOTIFY_REQ_DONE_EVENTID */
diff --git a/drivers/nfc/fdp/i2c.c b/drivers/nfc/fdp/i2c.c
index 5e797d5..712936f 100644
--- a/drivers/nfc/fdp/i2c.c
+++ b/drivers/nfc/fdp/i2c.c
@@ -210,14 +210,14 @@
struct sk_buff *skb;
int r;
- client = phy->i2c_dev;
- dev_dbg(&client->dev, "%s\n", __func__);
-
if (!phy || irq != phy->i2c_dev->irq) {
WARN_ON_ONCE(1);
return IRQ_NONE;
}
+ client = phy->i2c_dev;
+ dev_dbg(&client->dev, "%s\n", __func__);
+
r = fdp_nci_i2c_read(phy, &skb);
if (r == -EREMOTEIO)
diff --git a/drivers/nfc/port100.c b/drivers/nfc/port100.c
index 2b2330b..073e4a4 100644
--- a/drivers/nfc/port100.c
+++ b/drivers/nfc/port100.c
@@ -725,23 +725,33 @@
static int port100_send_ack(struct port100 *dev)
{
- int rc;
+ int rc = 0;
mutex_lock(&dev->out_urb_lock);
- init_completion(&dev->cmd_cancel_done);
-
- usb_kill_urb(dev->out_urb);
-
- dev->out_urb->transfer_buffer = ack_frame;
- dev->out_urb->transfer_buffer_length = sizeof(ack_frame);
- rc = usb_submit_urb(dev->out_urb, GFP_KERNEL);
-
- /* Set the cmd_cancel flag only if the URB has been successfully
- * submitted. It will be reset by the out URB completion callback
- * port100_send_complete().
+ /*
+ * If prior cancel is in-flight (dev->cmd_cancel == true), we
+ * can skip to send cancel. Then this will wait the prior
+ * cancel, or merged into the next cancel rarely if next
+ * cancel was started before waiting done. In any case, this
+ * will be waked up soon or later.
*/
- dev->cmd_cancel = !rc;
+ if (!dev->cmd_cancel) {
+ reinit_completion(&dev->cmd_cancel_done);
+
+ usb_kill_urb(dev->out_urb);
+
+ dev->out_urb->transfer_buffer = ack_frame;
+ dev->out_urb->transfer_buffer_length = sizeof(ack_frame);
+ rc = usb_submit_urb(dev->out_urb, GFP_KERNEL);
+
+ /*
+ * Set the cmd_cancel flag only if the URB has been
+ * successfully submitted. It will be reset by the out
+ * URB completion callback port100_send_complete().
+ */
+ dev->cmd_cancel = !rc;
+ }
mutex_unlock(&dev->out_urb_lock);
@@ -928,8 +938,8 @@
struct port100 *dev = urb->context;
if (dev->cmd_cancel) {
+ complete_all(&dev->cmd_cancel_done);
dev->cmd_cancel = false;
- complete(&dev->cmd_cancel_done);
}
switch (urb->status) {
@@ -1543,6 +1553,7 @@
PORT100_COMM_RF_HEAD_MAX_LEN;
dev->skb_tailroom = PORT100_FRAME_TAIL_LEN;
+ init_completion(&dev->cmd_cancel_done);
INIT_WORK(&dev->cmd_complete_work, port100_wq_cmd_complete);
/* The first thing to do with the Port-100 is to set the command type
diff --git a/drivers/nvmem/imx-ocotp.c b/drivers/nvmem/imx-ocotp.c
index ac27b9b..8e7b120 100644
--- a/drivers/nvmem/imx-ocotp.c
+++ b/drivers/nvmem/imx-ocotp.c
@@ -71,7 +71,7 @@
static const struct of_device_id imx_ocotp_dt_ids[] = {
{ .compatible = "fsl,imx6q-ocotp", (void *)128 },
- { .compatible = "fsl,imx6sl-ocotp", (void *)32 },
+ { .compatible = "fsl,imx6sl-ocotp", (void *)64 },
{ .compatible = "fsl,imx6sx-ocotp", (void *)128 },
{ },
};
diff --git a/drivers/pinctrl/qcom/pinctrl-spmi-gpio.c b/drivers/pinctrl/qcom/pinctrl-spmi-gpio.c
index f06fb1f..2ecbd22 100644
--- a/drivers/pinctrl/qcom/pinctrl-spmi-gpio.c
+++ b/drivers/pinctrl/qcom/pinctrl-spmi-gpio.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2012-2014, 2016 The Linux Foundation. All rights reserved.
+ * Copyright (c) 2012-2014, 2016-2017 The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -471,6 +471,7 @@
pad = pctldev->desc->pins[pin].drv_data;
+ pad->is_enabled = true;
for (i = 0; i < nconfs; i++) {
param = pinconf_to_config_param(configs[i]);
arg = pinconf_to_config_argument(configs[i]);
@@ -619,6 +620,10 @@
return ret;
}
+ val = pad->is_enabled << PMIC_GPIO_REG_MASTER_EN_SHIFT;
+
+ ret = pmic_gpio_write(state, pad, PMIC_GPIO_REG_EN_CTL, val);
+
return ret;
}
diff --git a/drivers/platform/msm/ipa/ipa_clients/ipa_usb.c b/drivers/platform/msm/ipa/ipa_clients/ipa_usb.c
index 205a756..f5d8d61 100644
--- a/drivers/platform/msm/ipa/ipa_clients/ipa_usb.c
+++ b/drivers/platform/msm/ipa/ipa_clients/ipa_usb.c
@@ -784,7 +784,7 @@
mutex_lock(&ipa3_usb_ctx->general_mutex);
IPA_USB_DBG_LOW("entry\n");
- if (teth_prot > IPA_USB_MAX_TETH_PROT_SIZE ||
+ if (teth_prot < 0 || teth_prot >= IPA_USB_MAX_TETH_PROT_SIZE ||
((teth_prot == IPA_USB_RNDIS || teth_prot == IPA_USB_ECM) &&
teth_params == NULL) || ipa_usb_notify_cb == NULL ||
user_data == NULL) {
@@ -987,7 +987,8 @@
params->xfer_scratch.depcmd_hi_addr);
if (params->client >= IPA_CLIENT_MAX ||
- params->teth_prot > IPA_USB_MAX_TETH_PROT_SIZE ||
+ params->teth_prot < 0 ||
+ params->teth_prot >= IPA_USB_MAX_TETH_PROT_SIZE ||
params->xfer_ring_len % GSI_CHAN_RE_SIZE_16B ||
params->xfer_scratch.const_buffer_size < 1 ||
params->xfer_scratch.const_buffer_size > 31) {
@@ -1251,7 +1252,7 @@
int result = 0;
IPA_USB_DBG_LOW("entry\n");
- if (ttype > IPA_USB_TRANSPORT_MAX) {
+ if (ttype < 0 || ttype >= IPA_USB_TRANSPORT_MAX) {
IPA_USB_ERR("bad parameter.\n");
return -EINVAL;
}
@@ -1355,7 +1356,8 @@
(params->teth_prot != IPA_USB_DIAG &&
(params->usb_to_ipa_xferrscidx < 0 ||
params->usb_to_ipa_xferrscidx > 127)) ||
- params->teth_prot > IPA_USB_MAX_TETH_PROT_SIZE) {
+ params->teth_prot < 0 ||
+ params->teth_prot >= IPA_USB_MAX_TETH_PROT_SIZE) {
IPA_USB_ERR("Invalid params\n");
return false;
}
@@ -2059,7 +2061,7 @@
static int ipa3_usb_check_disconnect_prot(enum ipa_usb_teth_prot teth_prot)
{
- if (teth_prot > IPA_USB_MAX_TETH_PROT_SIZE) {
+ if (teth_prot < 0 || teth_prot >= IPA_USB_MAX_TETH_PROT_SIZE) {
IPA_USB_ERR("bad parameter.\n");
return -EFAULT;
}
@@ -2247,7 +2249,7 @@
mutex_lock(&ipa3_usb_ctx->general_mutex);
IPA_USB_DBG_LOW("entry\n");
- if (teth_prot > IPA_USB_MAX_TETH_PROT_SIZE) {
+ if (teth_prot < 0 || teth_prot >= IPA_USB_MAX_TETH_PROT_SIZE) {
IPA_USB_ERR("bad parameters.\n");
result = -EINVAL;
goto bad_params;
@@ -2432,7 +2434,7 @@
mutex_lock(&ipa3_usb_ctx->general_mutex);
IPA_USB_DBG_LOW("entry\n");
- if (teth_prot > IPA_USB_MAX_TETH_PROT_SIZE) {
+ if (teth_prot < 0 || teth_prot >= IPA_USB_MAX_TETH_PROT_SIZE) {
IPA_USB_ERR("bad parameters.\n");
result = -EINVAL;
goto bad_params;
@@ -2604,7 +2606,7 @@
mutex_lock(&ipa3_usb_ctx->general_mutex);
IPA_USB_DBG_LOW("entry\n");
- if (teth_prot > IPA_USB_MAX_TETH_PROT_SIZE) {
+ if (teth_prot < 0 || teth_prot >= IPA_USB_MAX_TETH_PROT_SIZE) {
IPA_USB_ERR("bad parameters.\n");
result = -EINVAL;
goto bad_params;
diff --git a/drivers/power/supply/qcom/smb-lib.c b/drivers/power/supply/qcom/smb-lib.c
index 48e689f..aad7924 100644
--- a/drivers/power/supply/qcom/smb-lib.c
+++ b/drivers/power/supply/qcom/smb-lib.c
@@ -3642,6 +3642,37 @@
}
}
+static void smblib_notify_extcon_props(struct smb_charger *chg, int id)
+{
+ union extcon_property_value val;
+ union power_supply_propval prop_val;
+
+ smblib_get_prop_typec_cc_orientation(chg, &prop_val);
+ val.intval = ((prop_val.intval == 2) ? 1 : 0);
+ extcon_set_property(chg->extcon, id,
+ EXTCON_PROP_USB_TYPEC_POLARITY, val);
+
+ val.intval = true;
+ extcon_set_property(chg->extcon, id,
+ EXTCON_PROP_USB_SS, val);
+}
+
+static void smblib_notify_device_mode(struct smb_charger *chg, bool enable)
+{
+ if (enable)
+ smblib_notify_extcon_props(chg, EXTCON_USB);
+
+ extcon_set_state_sync(chg->extcon, EXTCON_USB, enable);
+}
+
+static void smblib_notify_usb_host(struct smb_charger *chg, bool enable)
+{
+ if (enable)
+ smblib_notify_extcon_props(chg, EXTCON_USB_HOST);
+
+ extcon_set_state_sync(chg->extcon, EXTCON_USB_HOST, enable);
+}
+
#define HVDCP_DET_MS 2500
static void smblib_handle_apsd_done(struct smb_charger *chg, bool rising)
{
@@ -3664,6 +3695,8 @@
/* if not DCP then no hvdcp timeout happens. Enable pd here */
vote(chg->pd_disallowed_votable_indirect, HVDCP_TIMEOUT_VOTER,
false, 0);
+ if (chg->use_extcon)
+ smblib_notify_device_mode(chg, true);
break;
case OCP_CHARGER_BIT:
case FLOAT_CHARGER_BIT:
@@ -3749,6 +3782,10 @@
*/
vote(chg->pd_disallowed_votable_indirect, HVDCP_TIMEOUT_VOTER,
false, 0);
+ if (chg->use_extcon) {
+ smblib_notify_usb_host(chg, true);
+ chg->otg_present = true;
+ }
}
static void typec_sink_removal(struct smb_charger *chg)
@@ -3899,6 +3936,14 @@
typec_sink_removal(chg);
smblib_update_usb_type(chg);
+
+ if (chg->use_extcon) {
+ if (chg->otg_present)
+ smblib_notify_usb_host(chg, false);
+ else
+ smblib_notify_device_mode(chg, false);
+ }
+ chg->otg_present = false;
}
static void smblib_handle_typec_insertion(struct smb_charger *chg)
diff --git a/drivers/power/supply/qcom/smb-lib.h b/drivers/power/supply/qcom/smb-lib.h
index 704a8db..5251b6f 100644
--- a/drivers/power/supply/qcom/smb-lib.h
+++ b/drivers/power/supply/qcom/smb-lib.h
@@ -144,6 +144,9 @@
EXTCON_NONE,
};
+/* EXTCON_USB and EXTCON_USB_HOST are mutually exclusive */
+static const u32 smblib_extcon_exclusive[] = {0x3, 0};
+
struct smb_regulator {
struct regulator_dev *rdev;
struct regulator_desc rdesc;
@@ -331,6 +334,8 @@
int usb_icl_change_irq_enabled;
u32 jeita_status;
u8 float_cfg;
+ bool use_extcon;
+ bool otg_present;
/* workaround flag */
u32 wa_flags;
diff --git a/drivers/power/supply/qcom/smb138x-charger.c b/drivers/power/supply/qcom/smb138x-charger.c
index 911dd69..e0c92c6 100644
--- a/drivers/power/supply/qcom/smb138x-charger.c
+++ b/drivers/power/supply/qcom/smb138x-charger.c
@@ -170,6 +170,9 @@
chip->dt.suspend_input = of_property_read_bool(node,
"qcom,suspend-input");
+ chg->use_extcon = of_property_read_bool(node,
+ "qcom,use-extcon");
+
rc = of_property_read_u32(node,
"qcom,fcc-max-ua", &chip->dt.fcc_ua);
if (rc < 0)
@@ -1405,55 +1408,95 @@
rc = smb138x_parse_dt(chip);
if (rc < 0) {
pr_err("Couldn't parse device tree rc=%d\n", rc);
- return rc;
+ goto cleanup;
}
rc = smb138x_init_vbus_regulator(chip);
if (rc < 0) {
pr_err("Couldn't initialize vbus regulator rc=%d\n",
rc);
- return rc;
+ goto cleanup;
}
rc = smb138x_init_vconn_regulator(chip);
if (rc < 0) {
pr_err("Couldn't initialize vconn regulator rc=%d\n",
rc);
- return rc;
+ goto cleanup;
+ }
+
+ if (chg->use_extcon) {
+ /* extcon registration */
+ chg->extcon = devm_extcon_dev_allocate(chg->dev,
+ smblib_extcon_cable);
+ if (IS_ERR(chg->extcon)) {
+ rc = PTR_ERR(chg->extcon);
+ dev_err(chg->dev, "failed to allocate extcon device rc=%d\n",
+ rc);
+ goto cleanup;
+ }
+
+ chg->extcon->mutually_exclusive = smblib_extcon_exclusive;
+ rc = devm_extcon_dev_register(chg->dev, chg->extcon);
+ if (rc < 0) {
+ dev_err(chg->dev, "failed to register extcon device rc=%d\n",
+ rc);
+ goto cleanup;
+ }
+
+ /* Support reporting polarity and speed via properties */
+ rc = extcon_set_property_capability(chg->extcon,
+ EXTCON_USB, EXTCON_PROP_USB_TYPEC_POLARITY);
+ rc |= extcon_set_property_capability(chg->extcon,
+ EXTCON_USB, EXTCON_PROP_USB_SS);
+ rc |= extcon_set_property_capability(chg->extcon,
+ EXTCON_USB_HOST,
+ EXTCON_PROP_USB_TYPEC_POLARITY);
+ rc |= extcon_set_property_capability(chg->extcon,
+ EXTCON_USB_HOST, EXTCON_PROP_USB_SS);
+ if (rc < 0) {
+ dev_err(chg->dev,
+ "failed to configure extcon capabilities\n");
+ goto cleanup;
+ }
}
rc = smb138x_init_usb_psy(chip);
if (rc < 0) {
pr_err("Couldn't initialize usb psy rc=%d\n", rc);
- return rc;
+ goto cleanup;
}
rc = smb138x_init_batt_psy(chip);
if (rc < 0) {
pr_err("Couldn't initialize batt psy rc=%d\n", rc);
- return rc;
+ goto cleanup;
}
rc = smb138x_init_hw(chip);
if (rc < 0) {
pr_err("Couldn't initialize hardware rc=%d\n", rc);
- return rc;
+ goto cleanup;
}
rc = smb138x_determine_initial_status(chip);
if (rc < 0) {
pr_err("Couldn't determine initial status rc=%d\n",
rc);
- return rc;
+ goto cleanup;
}
rc = smb138x_request_interrupts(chip);
if (rc < 0) {
pr_err("Couldn't request interrupts rc=%d\n", rc);
- return rc;
+ goto cleanup;
}
return rc;
+
+cleanup:
+ smblib_deinit(chg);
+ return rc;
}
static int smb138x_slave_probe(struct smb138x *chip)
diff --git a/drivers/scsi/bfa/bfad.c b/drivers/scsi/bfa/bfad.c
index 9d253cb..e70410b 100644
--- a/drivers/scsi/bfa/bfad.c
+++ b/drivers/scsi/bfa/bfad.c
@@ -64,9 +64,9 @@
u32 bfi_image_cb_size, bfi_image_ct_size, bfi_image_ct2_size;
u32 *bfi_image_cb, *bfi_image_ct, *bfi_image_ct2;
-#define BFAD_FW_FILE_CB "cbfw-3.2.3.0.bin"
-#define BFAD_FW_FILE_CT "ctfw-3.2.3.0.bin"
-#define BFAD_FW_FILE_CT2 "ct2fw-3.2.3.0.bin"
+#define BFAD_FW_FILE_CB "cbfw-3.2.5.1.bin"
+#define BFAD_FW_FILE_CT "ctfw-3.2.5.1.bin"
+#define BFAD_FW_FILE_CT2 "ct2fw-3.2.5.1.bin"
static u32 *bfad_load_fwimg(struct pci_dev *pdev);
static void bfad_free_fwimg(void);
diff --git a/drivers/scsi/bfa/bfad_drv.h b/drivers/scsi/bfa/bfad_drv.h
index f9e8620..cfcfff4 100644
--- a/drivers/scsi/bfa/bfad_drv.h
+++ b/drivers/scsi/bfa/bfad_drv.h
@@ -58,7 +58,7 @@
#ifdef BFA_DRIVER_VERSION
#define BFAD_DRIVER_VERSION BFA_DRIVER_VERSION
#else
-#define BFAD_DRIVER_VERSION "3.2.25.0"
+#define BFAD_DRIVER_VERSION "3.2.25.1"
#endif
#define BFAD_PROTO_NAME FCPI_NAME
diff --git a/drivers/scsi/fnic/fnic.h b/drivers/scsi/fnic/fnic.h
index 9ddc920..9e4b770 100644
--- a/drivers/scsi/fnic/fnic.h
+++ b/drivers/scsi/fnic/fnic.h
@@ -248,6 +248,7 @@
struct completion *remove_wait; /* device remove thread blocks */
atomic_t in_flight; /* io counter */
+ bool internal_reset_inprogress;
u32 _reserved; /* fill hole */
unsigned long state_flags; /* protected by host lock */
enum fnic_state state;
diff --git a/drivers/scsi/fnic/fnic_scsi.c b/drivers/scsi/fnic/fnic_scsi.c
index d9fd2f8..44dd372 100644
--- a/drivers/scsi/fnic/fnic_scsi.c
+++ b/drivers/scsi/fnic/fnic_scsi.c
@@ -2573,6 +2573,19 @@
unsigned long wait_host_tmo;
struct Scsi_Host *shost = sc->device->host;
struct fc_lport *lp = shost_priv(shost);
+ struct fnic *fnic = lport_priv(lp);
+ unsigned long flags;
+
+ spin_lock_irqsave(&fnic->fnic_lock, flags);
+ if (fnic->internal_reset_inprogress == 0) {
+ fnic->internal_reset_inprogress = 1;
+ } else {
+ spin_unlock_irqrestore(&fnic->fnic_lock, flags);
+ FNIC_SCSI_DBG(KERN_DEBUG, fnic->lport->host,
+ "host reset in progress skipping another host reset\n");
+ return SUCCESS;
+ }
+ spin_unlock_irqrestore(&fnic->fnic_lock, flags);
/*
* If fnic_reset is successful, wait for fabric login to complete
@@ -2593,6 +2606,9 @@
}
}
+ spin_lock_irqsave(&fnic->fnic_lock, flags);
+ fnic->internal_reset_inprogress = 0;
+ spin_unlock_irqrestore(&fnic->fnic_lock, flags);
return ret;
}
diff --git a/drivers/scsi/snic/snic_main.c b/drivers/scsi/snic/snic_main.c
index 396b32d..7cf70aa 100644
--- a/drivers/scsi/snic/snic_main.c
+++ b/drivers/scsi/snic/snic_main.c
@@ -591,6 +591,7 @@
if (!pool) {
SNIC_HOST_ERR(shost, "dflt sgl pool creation failed\n");
+ ret = -ENOMEM;
goto err_free_res;
}
@@ -601,6 +602,7 @@
if (!pool) {
SNIC_HOST_ERR(shost, "max sgl pool creation failed\n");
+ ret = -ENOMEM;
goto err_free_dflt_sgl_pool;
}
@@ -611,6 +613,7 @@
if (!pool) {
SNIC_HOST_ERR(shost, "snic tmreq info pool creation failed.\n");
+ ret = -ENOMEM;
goto err_free_max_sgl_pool;
}
diff --git a/drivers/soc/qcom/Kconfig b/drivers/soc/qcom/Kconfig
index 04c53751..a8fb8b6 100644
--- a/drivers/soc/qcom/Kconfig
+++ b/drivers/soc/qcom/Kconfig
@@ -692,3 +692,14 @@
This module enables bridging an Inter-Processor Communication(IPC)
interrupt from a remote subsystem directed towards Qualcomm
Technologies, Inc. Secure Execution Environment(QSEE).
+
+config MSM_JTAGV8
+ bool "Debug and ETM trace support across power collapse for ARMv8"
+ default y if CORESIGHT_SOURCE_ETM4X
+ help
+ Enables support for debugging (specifically breakpoints) and ETM
+ processor tracing across power collapse both for JTag and OS hosted
+ software running on ARMv8 target. Enabling this will ensure debug
+ and ETM registers are saved and restored across power collapse.
+ If unsure, say 'N' here to avoid potential power, performance and
+ memory penalty.
diff --git a/drivers/soc/qcom/Makefile b/drivers/soc/qcom/Makefile
index ca3fd0b..6eef58f 100644
--- a/drivers/soc/qcom/Makefile
+++ b/drivers/soc/qcom/Makefile
@@ -68,6 +68,7 @@
obj-y += subsystem_restart.o
obj-y += ramdump.o
endif
+obj-$(CONFIG_MSM_JTAGV8) += jtagv8.o jtagv8-etm.o
obj-$(CONFIG_QCOM_COMMAND_DB) += cmd-db.o
obj-$(CONFIG_MSM_QBT1000) += qbt1000.o
obj-$(CONFIG_MSM_EVENT_TIMER) += event_timer.o
diff --git a/drivers/soc/qcom/dcc_v2.c b/drivers/soc/qcom/dcc_v2.c
index 21b2034..2a23ba7 100644
--- a/drivers/soc/qcom/dcc_v2.c
+++ b/drivers/soc/qcom/dcc_v2.c
@@ -598,7 +598,7 @@
drvdata->ram_offset/4, DCC_LL_BASE(list));
dcc_writel(drvdata, drvdata->ram_start +
drvdata->ram_offset/4, DCC_FD_BASE(list));
- dcc_writel(drvdata, 0, DCC_LL_TIMEOUT(list));
+ dcc_writel(drvdata, 0xFFF, DCC_LL_TIMEOUT(list));
}
/* 4. Configure trigger, data sink and function type */
diff --git a/drivers/soc/qcom/gladiator_erp.c b/drivers/soc/qcom/gladiator_erp.c
index 835d4b0..3b70ca4 100644
--- a/drivers/soc/qcom/gladiator_erp.c
+++ b/drivers/soc/qcom/gladiator_erp.c
@@ -163,6 +163,7 @@
DISCONNECT_ERROR,
DIRECTORY_ERROR,
PARITY_ERROR,
+ PHYSICAL_ADDRESS_ERROR,
};
static void clear_gladiator_error(void __iomem *gladiator_virt_base,
@@ -200,12 +201,14 @@
static inline void print_gld_errtype(unsigned int errtype)
{
+ char *errors = "Disconnect, Directory, Parity, Physical address";
+
if (errtype == 0)
pr_alert("Error type: Snoop data transfer\n");
else if (errtype == 1)
pr_alert("Error type: DVM error\n");
else if (errtype == 3)
- pr_alert("Error type: Disconnect, directory, or parity error\n");
+ pr_alert("Error type: %s\n", errors);
else
pr_alert("Error type: Unknown; value:%u\n", errtype);
}
@@ -288,7 +291,7 @@
log_err_type = (err_reg5 & mask_shifts->gld_errlog5_error_type_mask)
>> mask_shifts->gld_errlog5_error_type_shift;
- for (i = 0 ; i <= 6 ; i++) {
+ for (i = 0 ; i <= 7 ; i++) {
value = log_err_type & 0x1;
switch (i) {
case DATA_TRANSFER_ERROR:
@@ -337,7 +340,14 @@
mask_shifts);
decode_index_parity(err_reg5, mask_shifts);
break;
+ case PHYSICAL_ADDRESS_ERROR:
+ if (value == 0)
+ continue;
+ pr_alert("Error type: Physical address error\n");
+ pr_alert("Address is greater than SoC address range\n");
+ break;
}
+
log_err_type = log_err_type >> 1;
}
}
diff --git a/drivers/soc/qcom/glink_spi_xprt.c b/drivers/soc/qcom/glink_spi_xprt.c
index c44aa93..a08c4bf 100644
--- a/drivers/soc/qcom/glink_spi_xprt.c
+++ b/drivers/soc/qcom/glink_spi_xprt.c
@@ -121,6 +121,8 @@
* @tx_fifo_write_reg_addr: Address of the TX FIFO Write Index Register.
* @rx_fifo_read_reg_addr: Address of the RX FIFO Read Index Register.
* @rx_fifo_write_reg_addr: Address of the RX FIFO Write Index Register.
+ * @tx_fifo_write: Internal write index for TX FIFO.
+ * @rx_fifo_read: Internal read index for RX FIFO.
* @kwork: Work to be executed when receiving data.
* @kworker: Handle to the entity processing @kwork.
* @task: Handle to the task context that runs @kworker.
@@ -158,6 +160,8 @@
unsigned int tx_fifo_write_reg_addr;
unsigned int rx_fifo_read_reg_addr;
unsigned int rx_fifo_write_reg_addr;
+ uint32_t tx_fifo_write;
+ uint32_t rx_fifo_read;
struct kthread_work kwork;
struct kthread_worker kworker;
@@ -374,6 +378,19 @@
int write_avail;
int ret;
+ if (unlikely(!einfo->tx_fifo_start)) {
+ ret = glink_spi_xprt_reg_read(einfo,
+ einfo->tx_fifo_write_reg_addr, &einfo->tx_fifo_write);
+ if (ret < 0) {
+ pr_err("%s: Error %d reading %s tx_fifo_write_reg_addr %d\n",
+ __func__, ret, einfo->xprt_cfg.edge,
+ einfo->tx_fifo_write_reg_addr);
+ return 0;
+ }
+ einfo->tx_fifo_start = einfo->tx_fifo_write;
+ }
+ write_id = einfo->tx_fifo_write;
+
ret = glink_spi_xprt_reg_read(einfo, einfo->tx_fifo_read_reg_addr,
&read_id);
if (ret < 0) {
@@ -383,21 +400,9 @@
return 0;
}
- ret = glink_spi_xprt_reg_read(einfo, einfo->tx_fifo_write_reg_addr,
- &write_id);
- if (ret < 0) {
- pr_err("%s: Error %d reading %s tx_fifo_write_reg_addr %d\n",
- __func__, ret, einfo->xprt_cfg.edge,
- einfo->tx_fifo_write_reg_addr);
- return 0;
- }
-
if (!read_id || !write_id)
return 0;
- if (unlikely(!einfo->tx_fifo_start))
- einfo->tx_fifo_start = write_id;
-
if (read_id > write_id)
write_avail = read_id - write_id;
else
@@ -427,14 +432,18 @@
int read_avail;
int ret;
- ret = glink_spi_xprt_reg_read(einfo, einfo->rx_fifo_read_reg_addr,
- &read_id);
- if (ret < 0) {
- pr_err("%s: Error %d reading %s rx_fifo_read_reg_addr %d\n",
- __func__, ret, einfo->xprt_cfg.edge,
- einfo->rx_fifo_read_reg_addr);
- return 0;
+ if (unlikely(!einfo->rx_fifo_start)) {
+ ret = glink_spi_xprt_reg_read(einfo,
+ einfo->rx_fifo_read_reg_addr, &einfo->rx_fifo_read);
+ if (ret < 0) {
+ pr_err("%s: Error %d reading %s rx_fifo_read_reg_addr %d\n",
+ __func__, ret, einfo->xprt_cfg.edge,
+ einfo->rx_fifo_read_reg_addr);
+ return 0;
+ }
+ einfo->rx_fifo_start = einfo->rx_fifo_read;
}
+ read_id = einfo->rx_fifo_read;
ret = glink_spi_xprt_reg_read(einfo, einfo->rx_fifo_write_reg_addr,
&write_id);
@@ -448,9 +457,6 @@
if (!read_id || !write_id)
return 0;
- if (unlikely(!einfo->rx_fifo_start))
- einfo->rx_fifo_start = read_id;
-
if (read_id <= write_id)
read_avail = write_id - read_id;
else
@@ -477,15 +483,7 @@
uint32_t offset = 0;
int ret;
- ret = glink_spi_xprt_reg_read(einfo, einfo->rx_fifo_read_reg_addr,
- &read_id);
- if (ret < 0) {
- pr_err("%s: Error %d reading %s rx_fifo_read_reg_addr %d\n",
- __func__, ret, einfo->xprt_cfg.edge,
- einfo->rx_fifo_read_reg_addr);
- return ret;
- }
-
+ read_id = einfo->rx_fifo_read;
do {
if ((read_id + size_to_read) >=
(einfo->rx_fifo_start + einfo->fifo_size))
@@ -510,6 +508,9 @@
pr_err("%s: Error %d writing %s rx_fifo_read_reg_addr %d\n",
__func__, ret, einfo->xprt_cfg.edge,
einfo->rx_fifo_read_reg_addr);
+ else
+ einfo->rx_fifo_read = read_id;
+
return ret;
}
@@ -532,15 +533,7 @@
uint32_t offset = 0;
int ret;
- ret = glink_spi_xprt_reg_read(einfo, einfo->tx_fifo_write_reg_addr,
- &write_id);
- if (ret < 0) {
- pr_err("%s: Error %d reading %s tx_fifo_write_reg_addr %d\n",
- __func__, ret, einfo->xprt_cfg.edge,
- einfo->tx_fifo_write_reg_addr);
- return ret;
- }
-
+ write_id = einfo->tx_fifo_write;
do {
if ((write_id + size_to_write) >=
(einfo->tx_fifo_start + einfo->fifo_size))
@@ -565,6 +558,9 @@
pr_err("%s: Error %d writing %s tx_fifo_write_reg_addr %d\n",
__func__, ret, einfo->xprt_cfg.edge,
einfo->tx_fifo_write_reg_addr);
+ else
+ einfo->tx_fifo_write = write_id;
+
return ret;
}
@@ -1242,6 +1238,8 @@
einfo->tx_blocked_signal_sent = false;
einfo->tx_fifo_start = 0;
einfo->rx_fifo_start = 0;
+ einfo->tx_fifo_write = 0;
+ einfo->rx_fifo_read = 0;
einfo->fifo_size = DEFAULT_FIFO_SIZE;
einfo->xprt_if.glink_core_if_ptr->link_down(&einfo->xprt_if);
diff --git a/drivers/soc/qcom/icnss.c b/drivers/soc/qcom/icnss.c
index 38099e9..209209b 100644
--- a/drivers/soc/qcom/icnss.c
+++ b/drivers/soc/qcom/icnss.c
@@ -558,6 +558,12 @@
int i;
enum icnss_msa_perm old_perm;
+ if (priv->nr_mem_region > QMI_WLFW_MAX_NUM_MEMORY_REGIONS_V01) {
+ icnss_pr_err("Invalid memory region len %d\n",
+ priv->nr_mem_region);
+ return -EINVAL;
+ }
+
for (i = 0; i < priv->nr_mem_region; i++) {
old_perm = priv->mem_region[i].perm;
ret = icnss_assign_msa_perm(&priv->mem_region[i], new_perm);
diff --git a/drivers/soc/qcom/jtagv8-etm.c b/drivers/soc/qcom/jtagv8-etm.c
new file mode 100644
index 0000000..ff8cc99
--- /dev/null
+++ b/drivers/soc/qcom/jtagv8-etm.c
@@ -0,0 +1,1722 @@
+/* Copyright (c) 2014-2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/types.h>
+#include <linux/err.h>
+#include <linux/slab.h>
+#include <linux/smp.h>
+#include <linux/export.h>
+#include <linux/printk.h>
+#include <linux/ratelimit.h>
+#include <linux/coresight.h>
+#include <linux/io.h>
+#include <linux/platform_device.h>
+#include <linux/bitops.h>
+#include <linux/of.h>
+#include <linux/notifier.h>
+#include <linux/cpu.h>
+#include <linux/spinlock.h>
+#include <linux/mutex.h>
+#include <soc/qcom/scm.h>
+#include <soc/qcom/jtag.h>
+#include <asm/smp_plat.h>
+#include <asm/etmv4x.h>
+#include <soc/qcom/socinfo.h>
+
+#define CORESIGHT_LAR (0xFB0)
+
+#define TIMEOUT_US (100)
+
+#define BM(lsb, msb) ((BIT(msb) - BIT(lsb)) + BIT(msb))
+#define BMVAL(val, lsb, msb) ((val & BM(lsb, msb)) >> lsb)
+#define BVAL(val, n) ((val & BIT(n)) >> n)
+
+/*
+ * ETMv4 registers:
+ * 0x000 - 0x2FC: Trace registers
+ * 0x300 - 0x314: Management registers
+ * 0x318 - 0xEFC: Trace registers
+ * 0xF00: Management registers
+ * 0xFA0 - 0xFA4: Trace registers
+ * 0xFA8 - 0xFFC: Management registers
+ */
+
+/* Trace registers (0x000-0x2FC) */
+/* Main control and configuration registers */
+#define TRCPRGCTLR (0x004)
+#define TRCPROCSELR (0x008)
+#define TRCSTATR (0x00C)
+#define TRCCONFIGR (0x010)
+#define TRCAUXCTLR (0x018)
+#define TRCEVENTCTL0R (0x020)
+#define TRCEVENTCTL1R (0x024)
+#define TRCSTALLCTLR (0x02C)
+#define TRCTSCTLR (0x030)
+#define TRCSYNCPR (0x034)
+#define TRCCCCTLR (0x038)
+#define TRCBBCTLR (0x03C)
+#define TRCTRACEIDR (0x040)
+#define TRCQCTLR (0x044)
+/* Filtering control registers */
+#define TRCVICTLR (0x080)
+#define TRCVIIECTLR (0x084)
+#define TRCVISSCTLR (0x088)
+#define TRCVIPCSSCTLR (0x08C)
+#define TRCVDCTLR (0x0A0)
+#define TRCVDSACCTLR (0x0A4)
+#define TRCVDARCCTLR (0x0A8)
+/* Derived resources registers */
+#define TRCSEQEVRn(n) (0x100 + (n * 4))
+#define TRCSEQRSTEVR (0x118)
+#define TRCSEQSTR (0x11C)
+#define TRCEXTINSELR (0x120)
+#define TRCCNTRLDVRn(n) (0x140 + (n * 4))
+#define TRCCNTCTLRn(n) (0x150 + (n * 4))
+#define TRCCNTVRn(n) (0x160 + (n * 4))
+/* ID registers */
+#define TRCIDR8 (0x180)
+#define TRCIDR9 (0x184)
+#define TRCIDR10 (0x188)
+#define TRCIDR11 (0x18C)
+#define TRCIDR12 (0x190)
+#define TRCIDR13 (0x194)
+#define TRCIMSPEC0 (0x1C0)
+#define TRCIMSPECn(n) (0x1C0 + (n * 4))
+#define TRCIDR0 (0x1E0)
+#define TRCIDR1 (0x1E4)
+#define TRCIDR2 (0x1E8)
+#define TRCIDR3 (0x1EC)
+#define TRCIDR4 (0x1F0)
+#define TRCIDR5 (0x1F4)
+#define TRCIDR6 (0x1F8)
+#define TRCIDR7 (0x1FC)
+/* Resource selection registers */
+#define TRCRSCTLRn(n) (0x200 + (n * 4))
+/* Single-shot comparator registers */
+#define TRCSSCCRn(n) (0x280 + (n * 4))
+#define TRCSSCSRn(n) (0x2A0 + (n * 4))
+#define TRCSSPCICRn(n) (0x2C0 + (n * 4))
+/* Management registers (0x300-0x314) */
+#define TRCOSLAR (0x300)
+#define TRCOSLSR (0x304)
+#define TRCPDCR (0x310)
+#define TRCPDSR (0x314)
+/* Trace registers (0x318-0xEFC) */
+/* Comparator registers */
+#define TRCACVRn(n) (0x400 + (n * 8))
+#define TRCACATRn(n) (0x480 + (n * 8))
+#define TRCDVCVRn(n) (0x500 + (n * 16))
+#define TRCDVCMRn(n) (0x580 + (n * 16))
+#define TRCCIDCVRn(n) (0x600 + (n * 8))
+#define TRCVMIDCVRn(n) (0x640 + (n * 8))
+#define TRCCIDCCTLR0 (0x680)
+#define TRCCIDCCTLR1 (0x684)
+#define TRCVMIDCCTLR0 (0x688)
+#define TRCVMIDCCTLR1 (0x68C)
+/* Management register (0xF00) */
+/* Integration control registers */
+#define TRCITCTRL (0xF00)
+/* Trace registers (0xFA0-0xFA4) */
+/* Claim tag registers */
+#define TRCCLAIMSET (0xFA0)
+#define TRCCLAIMCLR (0xFA4)
+/* Management registers (0xFA8-0xFFC) */
+#define TRCDEVAFF0 (0xFA8)
+#define TRCDEVAFF1 (0xFAC)
+#define TRCLAR (0xFB0)
+#define TRCLSR (0xFB4)
+#define TRCAUTHSTATUS (0xFB8)
+#define TRCDEVARCH (0xFBC)
+#define TRCDEVID (0xFC8)
+#define TRCDEVTYPE (0xFCC)
+#define TRCPIDR4 (0xFD0)
+#define TRCPIDR5 (0xFD4)
+#define TRCPIDR6 (0xFD8)
+#define TRCPIDR7 (0xFDC)
+#define TRCPIDR0 (0xFE0)
+#define TRCPIDR1 (0xFE4)
+#define TRCPIDR2 (0xFE8)
+#define TRCPIDR3 (0xFEC)
+#define TRCCIDR0 (0xFF0)
+#define TRCCIDR1 (0xFF4)
+#define TRCCIDR2 (0xFF8)
+#define TRCCIDR3 (0xFFC)
+
+/* ETMv4 resources */
+#define ETM_MAX_NR_PE (8)
+#define ETM_MAX_CNTR (4)
+#define ETM_MAX_SEQ_STATES (4)
+#define ETM_MAX_EXT_INP_SEL (4)
+#define ETM_MAX_EXT_INP (256)
+#define ETM_MAX_EXT_OUT (4)
+#define ETM_MAX_SINGLE_ADDR_CMP (16)
+#define ETM_MAX_ADDR_RANGE_CMP (ETM_MAX_SINGLE_ADDR_CMP / 2)
+#define ETM_MAX_DATA_VAL_CMP (8)
+#define ETM_MAX_CTXID_CMP (8)
+#define ETM_MAX_VMID_CMP (8)
+#define ETM_MAX_PE_CMP (8)
+#define ETM_MAX_RES_SEL (32)
+#define ETM_MAX_SS_CMP (8)
+
+#define ETM_CPMR_CLKEN (0x4)
+#define ETM_ARCH_V4 (0x40)
+#define ETM_ARCH_V4_2 (0x42)
+
+#define MAX_ETM_STATE_SIZE (165)
+
+#define TZ_DBG_ETM_FEAT_ID (0x8)
+#define TZ_DBG_ETM_VER (0x400000)
+#define HW_SOC_ID_M8953 (293)
+
+#define etm_writel(etm, val, off) \
+ __raw_writel(val, etm->base + off)
+#define etm_readl(etm, off) \
+ __raw_readl(etm->base + off)
+
+#define etm_writeq(etm, val, off) \
+ __raw_writeq(val, etm->base + off)
+#define etm_readq(etm, off) \
+ __raw_readq(etm->base + off)
+
+#define ETM_LOCK(base) \
+do { \
+ mb(); /* ensure configuration take effect before we lock it */ \
+ etm_writel(base, 0x0, CORESIGHT_LAR); \
+} while (0)
+
+#define ETM_UNLOCK(base) \
+do { \
+ etm_writel(base, CORESIGHT_UNLOCK, CORESIGHT_LAR); \
+ mb(); /* ensure unlock take effect before we configure */ \
+} while (0)
+
+struct etm_ctx {
+ uint8_t arch;
+ uint8_t nr_pe;
+ uint8_t nr_pe_cmp;
+ uint8_t nr_addr_cmp;
+ uint8_t nr_data_cmp;
+ uint8_t nr_cntr;
+ uint8_t nr_ext_inp;
+ uint8_t nr_ext_inp_sel;
+ uint8_t nr_ext_out;
+ uint8_t nr_ctxid_cmp;
+ uint8_t nr_vmid_cmp;
+ uint8_t nr_seq_state;
+ uint8_t nr_event;
+ uint8_t nr_resource;
+ uint8_t nr_ss_cmp;
+ bool si_enable;
+ bool save_restore_disabled;
+ bool save_restore_enabled;
+ bool os_lock_present;
+ bool init;
+ bool enable;
+ void __iomem *base;
+ struct device *dev;
+ uint64_t *state;
+ spinlock_t spinlock;
+ struct mutex mutex;
+};
+
+static struct etm_ctx *etm[NR_CPUS];
+static int cnt;
+
+static struct clk *clock[NR_CPUS];
+
+static ATOMIC_NOTIFIER_HEAD(etm_save_notifier_list);
+static ATOMIC_NOTIFIER_HEAD(etm_restore_notifier_list);
+
+int msm_jtag_save_register(struct notifier_block *nb)
+{
+ return atomic_notifier_chain_register(&etm_save_notifier_list, nb);
+}
+EXPORT_SYMBOL(msm_jtag_save_register);
+
+int msm_jtag_save_unregister(struct notifier_block *nb)
+{
+ return atomic_notifier_chain_unregister(&etm_save_notifier_list, nb);
+}
+EXPORT_SYMBOL(msm_jtag_save_unregister);
+
+int msm_jtag_restore_register(struct notifier_block *nb)
+{
+ return atomic_notifier_chain_register(&etm_restore_notifier_list, nb);
+}
+EXPORT_SYMBOL(msm_jtag_restore_register);
+
+int msm_jtag_restore_unregister(struct notifier_block *nb)
+{
+ return atomic_notifier_chain_unregister(&etm_restore_notifier_list, nb);
+}
+EXPORT_SYMBOL(msm_jtag_restore_unregister);
+
+static void etm_os_lock(struct etm_ctx *etmdata)
+{
+ if (etmdata->os_lock_present) {
+ etm_writel(etmdata, 0x1, TRCOSLAR);
+ /* Ensure OS lock is set before proceeding */
+ mb();
+ }
+}
+
+static void etm_os_unlock(struct etm_ctx *etmdata)
+{
+ if (etmdata->os_lock_present) {
+ /* Ensure all writes are complete before clearing OS lock */
+ mb();
+ etm_writel(etmdata, 0x0, TRCOSLAR);
+ }
+}
+
+static inline void etm_mm_save_state(struct etm_ctx *etmdata)
+{
+ int i, j, count;
+
+ i = 0;
+ mb(); /* ensure all register writes complete before saving them */
+ isb();
+ ETM_UNLOCK(etmdata);
+
+ switch (etmdata->arch) {
+ case ETM_ARCH_V4_2:
+ case ETM_ARCH_V4:
+ etm_os_lock(etmdata);
+
+ /* poll until programmers' model becomes stable */
+ for (count = TIMEOUT_US; (BVAL(etm_readl(etmdata, TRCSTATR), 1)
+ != 1) && count > 0; count--)
+ udelay(1);
+ if (count == 0)
+ pr_err_ratelimited("programmers model is not stable\n"
+ );
+
+ /* main control and configuration registers */
+ etmdata->state[i++] = etm_readl(etmdata, TRCPROCSELR);
+ etmdata->state[i++] = etm_readl(etmdata, TRCCONFIGR);
+ etmdata->state[i++] = etm_readl(etmdata, TRCAUXCTLR);
+ etmdata->state[i++] = etm_readl(etmdata, TRCEVENTCTL0R);
+ etmdata->state[i++] = etm_readl(etmdata, TRCEVENTCTL1R);
+ etmdata->state[i++] = etm_readl(etmdata, TRCSTALLCTLR);
+ etmdata->state[i++] = etm_readl(etmdata, TRCTSCTLR);
+ etmdata->state[i++] = etm_readl(etmdata, TRCSYNCPR);
+ etmdata->state[i++] = etm_readl(etmdata, TRCCCCTLR);
+ etmdata->state[i++] = etm_readl(etmdata, TRCBBCTLR);
+ etmdata->state[i++] = etm_readl(etmdata, TRCTRACEIDR);
+ etmdata->state[i++] = etm_readl(etmdata, TRCQCTLR);
+ /* filtering control registers */
+ etmdata->state[i++] = etm_readl(etmdata, TRCVICTLR);
+ etmdata->state[i++] = etm_readl(etmdata, TRCVIIECTLR);
+ etmdata->state[i++] = etm_readl(etmdata, TRCVISSCTLR);
+ etmdata->state[i++] = etm_readl(etmdata, TRCVIPCSSCTLR);
+ etmdata->state[i++] = etm_readl(etmdata, TRCVDCTLR);
+ etmdata->state[i++] = etm_readl(etmdata, TRCVDSACCTLR);
+ etmdata->state[i++] = etm_readl(etmdata, TRCVDARCCTLR);
+ /* derived resource registers */
+ for (j = 0; j < etmdata->nr_seq_state-1; j++)
+ etmdata->state[i++] = etm_readl(etmdata, TRCSEQEVRn(j));
+ etmdata->state[i++] = etm_readl(etmdata, TRCSEQRSTEVR);
+ etmdata->state[i++] = etm_readl(etmdata, TRCSEQSTR);
+ etmdata->state[i++] = etm_readl(etmdata, TRCEXTINSELR);
+ for (j = 0; j < etmdata->nr_cntr; j++) {
+ etmdata->state[i++] = etm_readl(etmdata,
+ TRCCNTRLDVRn(j));
+ etmdata->state[i++] = etm_readl(etmdata,
+ TRCCNTCTLRn(j));
+ etmdata->state[i++] = etm_readl(etmdata,
+ TRCCNTVRn(j));
+ }
+ /* resource selection registers */
+ for (j = 0; j < etmdata->nr_resource; j++)
+ etmdata->state[i++] = etm_readl(etmdata, TRCRSCTLRn(j));
+ /* comparator registers */
+ for (j = 0; j < etmdata->nr_addr_cmp * 2; j++) {
+ etmdata->state[i++] = etm_readq(etmdata, TRCACVRn(j));
+ etmdata->state[i++] = etm_readq(etmdata, TRCACATRn(j));
+ }
+ for (j = 0; j < etmdata->nr_data_cmp; j++) {
+ etmdata->state[i++] = etm_readq(etmdata, TRCDVCVRn(j));
+ etmdata->state[i++] = etm_readq(etmdata, TRCDVCMRn(i));
+ }
+ for (j = 0; j < etmdata->nr_ctxid_cmp; j++)
+ etmdata->state[i++] = etm_readq(etmdata, TRCCIDCVRn(j));
+ etmdata->state[i++] = etm_readl(etmdata, TRCCIDCCTLR0);
+ etmdata->state[i++] = etm_readl(etmdata, TRCCIDCCTLR1);
+ for (j = 0; j < etmdata->nr_vmid_cmp; j++)
+ etmdata->state[i++] = etm_readq(etmdata,
+ TRCVMIDCVRn(j));
+ etmdata->state[i++] = etm_readl(etmdata, TRCVMIDCCTLR0);
+ etmdata->state[i++] = etm_readl(etmdata, TRCVMIDCCTLR1);
+ /* single-shot comparator registers */
+ for (j = 0; j < etmdata->nr_ss_cmp; j++) {
+ etmdata->state[i++] = etm_readl(etmdata, TRCSSCCRn(j));
+ etmdata->state[i++] = etm_readl(etmdata, TRCSSCSRn(j));
+ etmdata->state[i++] = etm_readl(etmdata,
+ TRCSSPCICRn(j));
+ }
+ /* claim tag registers */
+ etmdata->state[i++] = etm_readl(etmdata, TRCCLAIMCLR);
+ /* program ctrl register */
+ etmdata->state[i++] = etm_readl(etmdata, TRCPRGCTLR);
+
+ /* ensure trace unit is idle to be powered down */
+ for (count = TIMEOUT_US; (BVAL(etm_readl(etmdata, TRCSTATR), 0)
+ != 1) && count > 0; count--)
+ udelay(1);
+ if (count == 0)
+ pr_err_ratelimited("timeout waiting for idle state\n");
+
+ atomic_notifier_call_chain(&etm_save_notifier_list, 0, NULL);
+
+ break;
+ default:
+ pr_err_ratelimited("unsupported etm arch %d in %s\n",
+ etmdata->arch, __func__);
+ }
+
+ ETM_LOCK(etmdata);
+}
+
+static inline void etm_mm_restore_state(struct etm_ctx *etmdata)
+{
+ int i, j;
+
+ i = 0;
+ ETM_UNLOCK(etmdata);
+
+ switch (etmdata->arch) {
+ case ETM_ARCH_V4_2:
+ case ETM_ARCH_V4:
+ atomic_notifier_call_chain(&etm_restore_notifier_list, 0, NULL);
+
+ /* check OS lock is locked */
+ if (BVAL(etm_readl(etmdata, TRCOSLSR), 1) != 1) {
+ pr_err_ratelimited("OS lock is unlocked\n");
+ etm_os_lock(etmdata);
+ }
+
+ /* main control and configuration registers */
+ etm_writel(etmdata, etmdata->state[i++], TRCPROCSELR);
+ etm_writel(etmdata, etmdata->state[i++], TRCCONFIGR);
+ etm_writel(etmdata, etmdata->state[i++], TRCAUXCTLR);
+ etm_writel(etmdata, etmdata->state[i++], TRCEVENTCTL0R);
+ etm_writel(etmdata, etmdata->state[i++], TRCEVENTCTL1R);
+ etm_writel(etmdata, etmdata->state[i++], TRCSTALLCTLR);
+ etm_writel(etmdata, etmdata->state[i++], TRCTSCTLR);
+ etm_writel(etmdata, etmdata->state[i++], TRCSYNCPR);
+ etm_writel(etmdata, etmdata->state[i++], TRCCCCTLR);
+ etm_writel(etmdata, etmdata->state[i++], TRCBBCTLR);
+ etm_writel(etmdata, etmdata->state[i++], TRCTRACEIDR);
+ etm_writel(etmdata, etmdata->state[i++], TRCQCTLR);
+ /* filtering control registers */
+ etm_writel(etmdata, etmdata->state[i++], TRCVICTLR);
+ etm_writel(etmdata, etmdata->state[i++], TRCVIIECTLR);
+ etm_writel(etmdata, etmdata->state[i++], TRCVISSCTLR);
+ etm_writel(etmdata, etmdata->state[i++], TRCVIPCSSCTLR);
+ etm_writel(etmdata, etmdata->state[i++], TRCVDCTLR);
+ etm_writel(etmdata, etmdata->state[i++], TRCVDSACCTLR);
+ etm_writel(etmdata, etmdata->state[i++], TRCVDARCCTLR);
+ /* derived resources registers */
+ for (j = 0; j < etmdata->nr_seq_state-1; j++)
+ etm_writel(etmdata, etmdata->state[i++], TRCSEQEVRn(j));
+ etm_writel(etmdata, etmdata->state[i++], TRCSEQRSTEVR);
+ etm_writel(etmdata, etmdata->state[i++], TRCSEQSTR);
+ etm_writel(etmdata, etmdata->state[i++], TRCEXTINSELR);
+ for (j = 0; j < etmdata->nr_cntr; j++) {
+ etm_writel(etmdata, etmdata->state[i++],
+ TRCCNTRLDVRn(j));
+ etm_writel(etmdata, etmdata->state[i++],
+ TRCCNTCTLRn(j));
+ etm_writel(etmdata, etmdata->state[i++], TRCCNTVRn(j));
+ }
+ /* resource selection registers */
+ for (j = 0; j < etmdata->nr_resource; j++)
+ etm_writel(etmdata, etmdata->state[i++], TRCRSCTLRn(j));
+ /* comparator registers */
+ for (j = 0; j < etmdata->nr_addr_cmp * 2; j++) {
+ etm_writeq(etmdata, etmdata->state[i++], TRCACVRn(j));
+ etm_writeq(etmdata, etmdata->state[i++], TRCACATRn(j));
+ }
+ for (j = 0; j < etmdata->nr_data_cmp; j++) {
+ etm_writeq(etmdata, etmdata->state[i++], TRCDVCVRn(j));
+ etm_writeq(etmdata, etmdata->state[i++], TRCDVCMRn(j));
+ }
+ for (j = 0; j < etmdata->nr_ctxid_cmp; j++)
+ etm_writeq(etmdata, etmdata->state[i++], TRCCIDCVRn(j));
+ etm_writel(etmdata, etmdata->state[i++], TRCCIDCCTLR0);
+ etm_writel(etmdata, etmdata->state[i++], TRCCIDCCTLR1);
+ for (j = 0; j < etmdata->nr_vmid_cmp; j++)
+ etm_writeq(etmdata, etmdata->state[i++],
+ TRCVMIDCVRn(j));
+ etm_writel(etmdata, etmdata->state[i++], TRCVMIDCCTLR0);
+ etm_writel(etmdata, etmdata->state[i++], TRCVMIDCCTLR1);
+ /* e-shot comparator registers */
+ for (j = 0; j < etmdata->nr_ss_cmp; j++) {
+ etm_writel(etmdata, etmdata->state[i++], TRCSSCCRn(j));
+ etm_writel(etmdata, etmdata->state[i++], TRCSSCSRn(j));
+ etm_writel(etmdata, etmdata->state[i++],
+ TRCSSPCICRn(j));
+ }
+ /* claim tag registers */
+ etm_writel(etmdata, etmdata->state[i++], TRCCLAIMSET);
+ /* program ctrl register */
+ etm_writel(etmdata, etmdata->state[i++], TRCPRGCTLR);
+
+ etm_os_unlock(etmdata);
+ break;
+ default:
+ pr_err_ratelimited("unsupported etm arch %d in %s\n",
+ etmdata->arch, __func__);
+ }
+
+ ETM_LOCK(etmdata);
+}
+
+static inline void etm_clk_disable(void)
+{
+ uint32_t cpmr;
+
+ isb();
+ cpmr = trc_readl(CPMR_EL1);
+ cpmr &= ~ETM_CPMR_CLKEN;
+ trc_write(cpmr, CPMR_EL1);
+}
+
+static inline void etm_clk_enable(void)
+{
+ uint32_t cpmr;
+
+ cpmr = trc_readl(CPMR_EL1);
+ cpmr |= ETM_CPMR_CLKEN;
+ trc_write(cpmr, CPMR_EL1);
+ isb();
+}
+
+static int etm_read_ssxr(uint64_t *state, int i, int j)
+{
+ switch (j) {
+ case 0:
+ state[i++] = trc_readl(ETMSEQEVR0);
+ break;
+ case 1:
+ state[i++] = trc_readl(ETMSEQEVR1);
+ break;
+ case 2:
+ state[i++] = trc_readl(ETMSEQEVR2);
+ break;
+ default:
+ pr_err_ratelimited("idx %d out of bounds in %s\n", j, __func__);
+ }
+ return i;
+}
+
+static int etm_read_crxr(uint64_t *state, int i, int j)
+{
+ switch (j) {
+ case 0:
+ state[i++] = trc_readl(ETMCNTRLDVR0);
+ state[i++] = trc_readl(ETMCNTCTLR0);
+ state[i++] = trc_readl(ETMCNTVR0);
+ break;
+ case 1:
+ state[i++] = trc_readl(ETMCNTRLDVR1);
+ state[i++] = trc_readl(ETMCNTCTLR1);
+ state[i++] = trc_readl(ETMCNTVR1);
+ break;
+ case 2:
+ state[i++] = trc_readl(ETMCNTRLDVR2);
+ state[i++] = trc_readl(ETMCNTCTLR2);
+ state[i++] = trc_readl(ETMCNTVR2);
+ break;
+ case 3:
+ state[i++] = trc_readl(ETMCNTRLDVR3);
+ state[i++] = trc_readl(ETMCNTCTLR3);
+ state[i++] = trc_readl(ETMCNTVR3);
+ break;
+ default:
+ pr_err_ratelimited("idx %d out of bounds in %s\n", j, __func__);
+ }
+ return i;
+}
+
+static int etm_read_rsxr(uint64_t *state, int i, int j)
+{
+ switch (j) {
+ case 2:
+ state[i++] = trc_readl(ETMRSCTLR2);
+ break;
+ case 3:
+ state[i++] = trc_readl(ETMRSCTLR3);
+ break;
+ case 4:
+ state[i++] = trc_readl(ETMRSCTLR4);
+ break;
+ case 5:
+ state[i++] = trc_readl(ETMRSCTLR5);
+ break;
+ case 6:
+ state[i++] = trc_readl(ETMRSCTLR6);
+ break;
+ case 7:
+ state[i++] = trc_readl(ETMRSCTLR7);
+ break;
+ case 8:
+ state[i++] = trc_readl(ETMRSCTLR8);
+ break;
+ case 9:
+ state[i++] = trc_readl(ETMRSCTLR9);
+ break;
+ case 10:
+ state[i++] = trc_readl(ETMRSCTLR10);
+ break;
+ case 11:
+ state[i++] = trc_readl(ETMRSCTLR11);
+ break;
+ case 12:
+ state[i++] = trc_readl(ETMRSCTLR12);
+ break;
+ case 13:
+ state[i++] = trc_readl(ETMRSCTLR13);
+ break;
+ case 14:
+ state[i++] = trc_readl(ETMRSCTLR14);
+ break;
+ case 15:
+ state[i++] = trc_readl(ETMRSCTLR15);
+ break;
+ case 16:
+ state[i++] = trc_readl(ETMRSCTLR16);
+ break;
+ case 17:
+ state[i++] = trc_readl(ETMRSCTLR17);
+ break;
+ case 18:
+ state[i++] = trc_readl(ETMRSCTLR18);
+ break;
+ case 19:
+ state[i++] = trc_readl(ETMRSCTLR19);
+ break;
+ case 20:
+ state[i++] = trc_readl(ETMRSCTLR20);
+ break;
+ case 21:
+ state[i++] = trc_readl(ETMRSCTLR21);
+ break;
+ case 22:
+ state[i++] = trc_readl(ETMRSCTLR22);
+ break;
+ case 23:
+ state[i++] = trc_readl(ETMRSCTLR23);
+ break;
+ case 24:
+ state[i++] = trc_readl(ETMRSCTLR24);
+ break;
+ case 25:
+ state[i++] = trc_readl(ETMRSCTLR25);
+ break;
+ case 26:
+ state[i++] = trc_readl(ETMRSCTLR26);
+ break;
+ case 27:
+ state[i++] = trc_readl(ETMRSCTLR27);
+ break;
+ case 28:
+ state[i++] = trc_readl(ETMRSCTLR28);
+ break;
+ case 29:
+ state[i++] = trc_readl(ETMRSCTLR29);
+ break;
+ case 30:
+ state[i++] = trc_readl(ETMRSCTLR30);
+ break;
+ case 31:
+ state[i++] = trc_readl(ETMRSCTLR31);
+ break;
+ default:
+ pr_err_ratelimited("idx %d out of bounds in %s\n", j, __func__);
+ }
+ return i;
+}
+
+static int etm_read_acr(uint64_t *state, int i, int j)
+{
+ switch (j) {
+ case 0:
+ state[i++] = trc_readq(ETMACVR0);
+ state[i++] = trc_readq(ETMACATR0);
+ break;
+ case 1:
+ state[i++] = trc_readq(ETMACVR1);
+ state[i++] = trc_readq(ETMACATR1);
+ break;
+ case 2:
+ state[i++] = trc_readq(ETMACVR2);
+ state[i++] = trc_readq(ETMACATR2);
+ break;
+ case 3:
+ state[i++] = trc_readq(ETMACVR3);
+ state[i++] = trc_readq(ETMACATR3);
+ break;
+ case 4:
+ state[i++] = trc_readq(ETMACVR4);
+ state[i++] = trc_readq(ETMACATR4);
+ break;
+ case 5:
+ state[i++] = trc_readq(ETMACVR5);
+ state[i++] = trc_readq(ETMACATR5);
+ break;
+ case 6:
+ state[i++] = trc_readq(ETMACVR6);
+ state[i++] = trc_readq(ETMACATR6);
+ break;
+ case 7:
+ state[i++] = trc_readq(ETMACVR7);
+ state[i++] = trc_readq(ETMACATR7);
+ break;
+ case 8:
+ state[i++] = trc_readq(ETMACVR8);
+ state[i++] = trc_readq(ETMACATR8);
+ break;
+ case 9:
+ state[i++] = trc_readq(ETMACVR9);
+ state[i++] = trc_readq(ETMACATR9);
+ break;
+ case 10:
+ state[i++] = trc_readq(ETMACVR10);
+ state[i++] = trc_readq(ETMACATR10);
+ break;
+ case 11:
+ state[i++] = trc_readq(ETMACVR11);
+ state[i++] = trc_readq(ETMACATR11);
+ break;
+ case 12:
+ state[i++] = trc_readq(ETMACVR12);
+ state[i++] = trc_readq(ETMACATR12);
+ break;
+ case 13:
+ state[i++] = trc_readq(ETMACVR13);
+ state[i++] = trc_readq(ETMACATR13);
+ break;
+ case 14:
+ state[i++] = trc_readq(ETMACVR14);
+ state[i++] = trc_readq(ETMACATR14);
+ break;
+ case 15:
+ state[i++] = trc_readq(ETMACVR15);
+ state[i++] = trc_readq(ETMACATR15);
+ break;
+ default:
+ pr_err_ratelimited("idx %d out of bounds in %s\n", j, __func__);
+ }
+ return i;
+}
+
+static int etm_read_dvcr(uint64_t *state, int i, int j)
+{
+ switch (j) {
+ case 0:
+ state[i++] = trc_readq(ETMDVCVR0);
+ state[i++] = trc_readq(ETMDVCMR0);
+ break;
+ case 1:
+ state[i++] = trc_readq(ETMDVCVR1);
+ state[i++] = trc_readq(ETMDVCMR1);
+ break;
+ case 2:
+ state[i++] = trc_readq(ETMDVCVR2);
+ state[i++] = trc_readq(ETMDVCMR2);
+ break;
+ case 3:
+ state[i++] = trc_readq(ETMDVCVR3);
+ state[i++] = trc_readq(ETMDVCMR3);
+ break;
+ case 4:
+ state[i++] = trc_readq(ETMDVCVR4);
+ state[i++] = trc_readq(ETMDVCMR4);
+ break;
+ case 5:
+ state[i++] = trc_readq(ETMDVCVR5);
+ state[i++] = trc_readq(ETMDVCMR5);
+ break;
+ case 6:
+ state[i++] = trc_readq(ETMDVCVR6);
+ state[i++] = trc_readq(ETMDVCMR6);
+ break;
+ case 7:
+ state[i++] = trc_readq(ETMDVCVR7);
+ state[i++] = trc_readq(ETMDVCMR7);
+ break;
+ default:
+ pr_err_ratelimited("idx %d out of bounds in %s\n", j, __func__);
+ }
+ return i;
+}
+
+static int etm_read_ccvr(uint64_t *state, int i, int j)
+{
+ switch (j) {
+ case 0:
+ state[i++] = trc_readq(ETMCIDCVR0);
+ break;
+ case 1:
+ state[i++] = trc_readq(ETMCIDCVR1);
+ break;
+ case 2:
+ state[i++] = trc_readq(ETMCIDCVR2);
+ break;
+ case 3:
+ state[i++] = trc_readq(ETMCIDCVR3);
+ break;
+ case 4:
+ state[i++] = trc_readq(ETMCIDCVR4);
+ break;
+ case 5:
+ state[i++] = trc_readq(ETMCIDCVR5);
+ break;
+ case 6:
+ state[i++] = trc_readq(ETMCIDCVR6);
+ break;
+ case 7:
+ state[i++] = trc_readq(ETMCIDCVR7);
+ break;
+ default:
+ pr_err_ratelimited("idx %d out of bounds in %s\n", j, __func__);
+ }
+ return i;
+}
+
+static int etm_read_vcvr(uint64_t *state, int i, int j)
+{
+ switch (j) {
+ case 0:
+ state[i++] = trc_readq(ETMVMIDCVR0);
+ break;
+ case 1:
+ state[i++] = trc_readq(ETMVMIDCVR1);
+ break;
+ case 2:
+ state[i++] = trc_readq(ETMVMIDCVR2);
+ break;
+ case 3:
+ state[i++] = trc_readq(ETMVMIDCVR3);
+ break;
+ case 4:
+ state[i++] = trc_readq(ETMVMIDCVR4);
+ break;
+ case 5:
+ state[i++] = trc_readq(ETMVMIDCVR5);
+ break;
+ case 6:
+ state[i++] = trc_readq(ETMVMIDCVR6);
+ break;
+ case 7:
+ state[i++] = trc_readq(ETMVMIDCVR7);
+ break;
+ default:
+ pr_err_ratelimited("idx %d out of bounds in %s\n", j, __func__);
+ }
+ return i;
+}
+
+static int etm_read_sscr(uint64_t *state, int i, int j)
+{
+ switch (j) {
+ case 0:
+ state[i++] = trc_readl(ETMSSCCR0);
+ state[i++] = trc_readl(ETMSSCSR0);
+ state[i++] = trc_readl(ETMSSPCICR0);
+ break;
+ case 1:
+ state[i++] = trc_readl(ETMSSCCR1);
+ state[i++] = trc_readl(ETMSSCSR1);
+ state[i++] = trc_readl(ETMSSPCICR1);
+ break;
+ case 2:
+ state[i++] = trc_readl(ETMSSCCR2);
+ state[i++] = trc_readl(ETMSSCSR2);
+ state[i++] = trc_readl(ETMSSPCICR2);
+ break;
+ case 3:
+ state[i++] = trc_readl(ETMSSCCR3);
+ state[i++] = trc_readl(ETMSSCSR3);
+ state[i++] = trc_readl(ETMSSPCICR3);
+ break;
+ case 4:
+ state[i++] = trc_readl(ETMSSCCR4);
+ state[i++] = trc_readl(ETMSSCSR4);
+ state[i++] = trc_readl(ETMSSPCICR4);
+ break;
+ case 5:
+ state[i++] = trc_readl(ETMSSCCR5);
+ state[i++] = trc_readl(ETMSSCSR5);
+ state[i++] = trc_readl(ETMSSPCICR5);
+ break;
+ case 6:
+ state[i++] = trc_readl(ETMSSCCR6);
+ state[i++] = trc_readl(ETMSSCSR6);
+ state[i++] = trc_readl(ETMSSPCICR6);
+ break;
+ case 7:
+ state[i++] = trc_readl(ETMSSCCR7);
+ state[i++] = trc_readl(ETMSSCSR7);
+ state[i++] = trc_readl(ETMSSPCICR7);
+ break;
+ default:
+ pr_err_ratelimited("idx %d out of bounds in %s\n", j, __func__);
+ }
+ return i;
+}
+
+static inline void etm_si_save_state(struct etm_ctx *etmdata)
+{
+ int i, j, count;
+
+ i = 0;
+ /* Ensure all writes are complete before saving ETM registers */
+ mb();
+ isb();
+
+ /* Vote for ETM power/clock enable */
+ etm_clk_enable();
+
+ switch (etmdata->arch) {
+ case ETM_ARCH_V4_2:
+ case ETM_ARCH_V4:
+ trc_write(0x1, ETMOSLAR);
+ isb();
+
+ /* poll until programmers' model becomes stable */
+ for (count = TIMEOUT_US; (BVAL(trc_readl(ETMSTATR), 1)
+ != 1) && count > 0; count--)
+ udelay(1);
+ if (count == 0)
+ pr_err_ratelimited("programmers model is not stable\n");
+
+ /* main control and configuration registers */
+ etmdata->state[i++] = trc_readl(ETMCONFIGR);
+ etmdata->state[i++] = trc_readl(ETMEVENTCTL0R);
+ etmdata->state[i++] = trc_readl(ETMEVENTCTL1R);
+ etmdata->state[i++] = trc_readl(ETMSTALLCTLR);
+ etmdata->state[i++] = trc_readl(ETMTSCTLR);
+ etmdata->state[i++] = trc_readl(ETMSYNCPR);
+ etmdata->state[i++] = trc_readl(ETMCCCTLR);
+ etmdata->state[i++] = trc_readl(ETMTRACEIDR);
+ /* filtering control registers */
+ etmdata->state[i++] = trc_readl(ETMVICTLR);
+ etmdata->state[i++] = trc_readl(ETMVIIECTLR);
+ etmdata->state[i++] = trc_readl(ETMVISSCTLR);
+ /* derived resource registers */
+ for (j = 0; j < etmdata->nr_seq_state-1; j++)
+ i = etm_read_ssxr(etmdata->state, i, j);
+ etmdata->state[i++] = trc_readl(ETMSEQRSTEVR);
+ etmdata->state[i++] = trc_readl(ETMSEQSTR);
+ etmdata->state[i++] = trc_readl(ETMEXTINSELR);
+ for (j = 0; j < etmdata->nr_cntr; j++)
+ i = etm_read_crxr(etmdata->state, i, j);
+ /* resource selection registers */
+ for (j = 0; j < etmdata->nr_resource; j++)
+ i = etm_read_rsxr(etmdata->state, i, j + 2);
+ /* comparator registers */
+ for (j = 0; j < etmdata->nr_addr_cmp * 2; j++)
+ i = etm_read_acr(etmdata->state, i, j);
+ for (j = 0; j < etmdata->nr_data_cmp; j++)
+ i = etm_read_dvcr(etmdata->state, i, j);
+ for (j = 0; j < etmdata->nr_ctxid_cmp; j++)
+ i = etm_read_ccvr(etmdata->state, i, j);
+ etmdata->state[i++] = trc_readl(ETMCIDCCTLR0);
+ for (j = 0; j < etmdata->nr_vmid_cmp; j++)
+ i = etm_read_vcvr(etmdata->state, i, j);
+ /* single-shot comparator registers */
+ for (j = 0; j < etmdata->nr_ss_cmp; j++)
+ i = etm_read_sscr(etmdata->state, i, j);
+ /* program ctrl register */
+ etmdata->state[i++] = trc_readl(ETMPRGCTLR);
+
+ /* ensure trace unit is idle to be powered down */
+ for (count = TIMEOUT_US; (BVAL(trc_readl(ETMSTATR), 0)
+ != 1) && count > 0; count--)
+ udelay(1);
+ if (count == 0)
+ pr_err_ratelimited("timeout waiting for idle state\n");
+
+ atomic_notifier_call_chain(&etm_save_notifier_list, 0, NULL);
+
+ break;
+ default:
+ pr_err_ratelimited("unsupported etm arch %d in %s\n",
+ etmdata->arch, __func__);
+ }
+
+ /* Vote for ETM power/clock disable */
+ etm_clk_disable();
+}
+
+static int etm_write_ssxr(uint64_t *state, int i, int j)
+{
+ switch (j) {
+ case 0:
+ trc_write(state[i++], ETMSEQEVR0);
+ break;
+ case 1:
+ trc_write(state[i++], ETMSEQEVR1);
+ break;
+ case 2:
+ trc_write(state[i++], ETMSEQEVR2);
+ break;
+ default:
+ pr_err_ratelimited("idx %d out of bounds in %s\n", j, __func__);
+ }
+ return i;
+}
+
+static int etm_write_crxr(uint64_t *state, int i, int j)
+{
+ switch (j) {
+ case 0:
+ trc_write(state[i++], ETMCNTRLDVR0);
+ trc_write(state[i++], ETMCNTCTLR0);
+ trc_write(state[i++], ETMCNTVR0);
+ break;
+ case 1:
+ trc_write(state[i++], ETMCNTRLDVR1);
+ trc_write(state[i++], ETMCNTCTLR1);
+ trc_write(state[i++], ETMCNTVR1);
+ break;
+ case 2:
+ trc_write(state[i++], ETMCNTRLDVR2);
+ trc_write(state[i++], ETMCNTCTLR2);
+ trc_write(state[i++], ETMCNTVR2);
+ break;
+ case 3:
+ trc_write(state[i++], ETMCNTRLDVR3);
+ trc_write(state[i++], ETMCNTCTLR3);
+ trc_write(state[i++], ETMCNTVR3);
+ break;
+ default:
+ pr_err_ratelimited("idx %d out of bounds in %s\n", j, __func__);
+ }
+ return i;
+}
+
+static int etm_write_rsxr(uint64_t *state, int i, int j)
+{
+ switch (j) {
+ case 2:
+ trc_write(state[i++], ETMRSCTLR2);
+ break;
+ case 3:
+ trc_write(state[i++], ETMRSCTLR3);
+ break;
+ case 4:
+ trc_write(state[i++], ETMRSCTLR4);
+ break;
+ case 5:
+ trc_write(state[i++], ETMRSCTLR5);
+ break;
+ case 6:
+ trc_write(state[i++], ETMRSCTLR6);
+ break;
+ case 7:
+ trc_write(state[i++], ETMRSCTLR7);
+ break;
+ case 8:
+ trc_write(state[i++], ETMRSCTLR8);
+ break;
+ case 9:
+ trc_write(state[i++], ETMRSCTLR9);
+ break;
+ case 10:
+ trc_write(state[i++], ETMRSCTLR10);
+ break;
+ case 11:
+ trc_write(state[i++], ETMRSCTLR11);
+ break;
+ case 12:
+ trc_write(state[i++], ETMRSCTLR12);
+ break;
+ case 13:
+ trc_write(state[i++], ETMRSCTLR13);
+ break;
+ case 14:
+ trc_write(state[i++], ETMRSCTLR14);
+ break;
+ case 15:
+ trc_write(state[i++], ETMRSCTLR15);
+ break;
+ case 16:
+ trc_write(state[i++], ETMRSCTLR16);
+ break;
+ case 17:
+ trc_write(state[i++], ETMRSCTLR17);
+ break;
+ case 18:
+ trc_write(state[i++], ETMRSCTLR18);
+ break;
+ case 19:
+ trc_write(state[i++], ETMRSCTLR19);
+ break;
+ case 20:
+ trc_write(state[i++], ETMRSCTLR20);
+ break;
+ case 21:
+ trc_write(state[i++], ETMRSCTLR21);
+ break;
+ case 22:
+ trc_write(state[i++], ETMRSCTLR22);
+ break;
+ case 23:
+ trc_write(state[i++], ETMRSCTLR23);
+ break;
+ case 24:
+ trc_write(state[i++], ETMRSCTLR24);
+ break;
+ case 25:
+ trc_write(state[i++], ETMRSCTLR25);
+ break;
+ case 26:
+ trc_write(state[i++], ETMRSCTLR26);
+ break;
+ case 27:
+ trc_write(state[i++], ETMRSCTLR27);
+ break;
+ case 28:
+ trc_write(state[i++], ETMRSCTLR28);
+ break;
+ case 29:
+ trc_write(state[i++], ETMRSCTLR29);
+ break;
+ case 30:
+ trc_write(state[i++], ETMRSCTLR30);
+ break;
+ case 31:
+ trc_write(state[i++], ETMRSCTLR31);
+ break;
+ default:
+ pr_err_ratelimited("idx %d out of bounds in %s\n", j, __func__);
+ }
+ return i;
+}
+
+static int etm_write_acr(uint64_t *state, int i, int j)
+{
+ switch (j) {
+ case 0:
+ trc_write(state[i++], ETMACVR0);
+ trc_write(state[i++], ETMACATR0);
+ break;
+ case 1:
+ trc_write(state[i++], ETMACVR1);
+ trc_write(state[i++], ETMACATR1);
+ break;
+ case 2:
+ trc_write(state[i++], ETMACVR2);
+ trc_write(state[i++], ETMACATR2);
+ break;
+ case 3:
+ trc_write(state[i++], ETMACVR3);
+ trc_write(state[i++], ETMACATR3);
+ break;
+ case 4:
+ trc_write(state[i++], ETMACVR4);
+ trc_write(state[i++], ETMACATR4);
+ break;
+ case 5:
+ trc_write(state[i++], ETMACVR5);
+ trc_write(state[i++], ETMACATR5);
+ break;
+ case 6:
+ trc_write(state[i++], ETMACVR6);
+ trc_write(state[i++], ETMACATR6);
+ break;
+ case 7:
+ trc_write(state[i++], ETMACVR7);
+ trc_write(state[i++], ETMACATR7);
+ break;
+ case 8:
+ trc_write(state[i++], ETMACVR8);
+ trc_write(state[i++], ETMACATR8);
+ break;
+ case 9:
+ trc_write(state[i++], ETMACVR9);
+ trc_write(state[i++], ETMACATR9);
+ break;
+ case 10:
+ trc_write(state[i++], ETMACVR10);
+ trc_write(state[i++], ETMACATR10);
+ break;
+ case 11:
+ trc_write(state[i++], ETMACVR11);
+ trc_write(state[i++], ETMACATR11);
+ break;
+ case 12:
+ trc_write(state[i++], ETMACVR12);
+ trc_write(state[i++], ETMACATR12);
+ break;
+ case 13:
+ trc_write(state[i++], ETMACVR13);
+ trc_write(state[i++], ETMACATR13);
+ break;
+ case 14:
+ trc_write(state[i++], ETMACVR14);
+ trc_write(state[i++], ETMACATR14);
+ break;
+ case 15:
+ trc_write(state[i++], ETMACVR15);
+ trc_write(state[i++], ETMACATR15);
+ break;
+ default:
+ pr_err_ratelimited("idx %d out of bounds in %s\n", j, __func__);
+ }
+ return i;
+}
+
+static int etm_write_dvcr(uint64_t *state, int i, int j)
+{
+ switch (j) {
+ case 0:
+ trc_write(state[i++], ETMDVCVR0);
+ trc_write(state[i++], ETMDVCMR0);
+ break;
+ case 1:
+ trc_write(state[i++], ETMDVCVR1);
+ trc_write(state[i++], ETMDVCMR1);
+ break;
+ case 2:
+ trc_write(state[i++], ETMDVCVR2);
+ trc_write(state[i++], ETMDVCMR2);
+ break;
+ case 3:
+ trc_write(state[i++], ETMDVCVR3);
+ trc_write(state[i++], ETMDVCMR3);
+ break;
+ case 4:
+ trc_write(state[i++], ETMDVCVR4);
+ trc_write(state[i++], ETMDVCMR4);
+ break;
+ case 5:
+ trc_write(state[i++], ETMDVCVR5);
+ trc_write(state[i++], ETMDVCMR5);
+ break;
+ case 6:
+ trc_write(state[i++], ETMDVCVR6);
+ trc_write(state[i++], ETMDVCMR6);
+ break;
+ case 7:
+ trc_write(state[i++], ETMDVCVR7);
+ trc_write(state[i++], ETMDVCMR7);
+ break;
+ default:
+ pr_err_ratelimited("idx %d out of bounds in %s\n", j, __func__);
+ }
+ return i;
+}
+
+static int etm_write_ccvr(uint64_t *state, int i, int j)
+{
+ switch (j) {
+ case 0:
+ trc_write(state[i++], ETMCIDCVR0);
+ break;
+ case 1:
+ trc_write(state[i++], ETMCIDCVR1);
+ break;
+ case 2:
+ trc_write(state[i++], ETMCIDCVR2);
+ break;
+ case 3:
+ trc_write(state[i++], ETMCIDCVR3);
+ break;
+ case 4:
+ trc_write(state[i++], ETMCIDCVR4);
+ break;
+ case 5:
+ trc_write(state[i++], ETMCIDCVR5);
+ break;
+ case 6:
+ trc_write(state[i++], ETMCIDCVR6);
+ break;
+ case 7:
+ trc_write(state[i++], ETMCIDCVR7);
+ break;
+ default:
+ pr_err_ratelimited("idx %d out of bounds in %s\n", j, __func__);
+ }
+ return i;
+}
+
+static int etm_write_vcvr(uint64_t *state, int i, int j)
+{
+ switch (j) {
+ case 0:
+ trc_write(state[i++], ETMVMIDCVR0);
+ break;
+ case 1:
+ trc_write(state[i++], ETMVMIDCVR1);
+ break;
+ case 2:
+ trc_write(state[i++], ETMVMIDCVR2);
+ break;
+ case 3:
+ trc_write(state[i++], ETMVMIDCVR3);
+ break;
+ case 4:
+ trc_write(state[i++], ETMVMIDCVR4);
+ break;
+ case 5:
+ trc_write(state[i++], ETMVMIDCVR5);
+ break;
+ case 6:
+ trc_write(state[i++], ETMVMIDCVR6);
+ break;
+ case 7:
+ trc_write(state[i++], ETMVMIDCVR7);
+ break;
+ default:
+ pr_err_ratelimited("idx %d out of bounds in %s\n", j, __func__);
+ }
+ return i;
+}
+
+static int etm_write_sscr(uint64_t *state, int i, int j)
+{
+ switch (j) {
+ case 0:
+ trc_write(state[i++], ETMSSCCR0);
+ trc_write(state[i++], ETMSSCSR0);
+ trc_write(state[i++], ETMSSPCICR0);
+ break;
+ case 1:
+ trc_write(state[i++], ETMSSCCR1);
+ trc_write(state[i++], ETMSSCSR1);
+ trc_write(state[i++], ETMSSPCICR1);
+ break;
+ case 2:
+ trc_write(state[i++], ETMSSCCR2);
+ trc_write(state[i++], ETMSSCSR2);
+ trc_write(state[i++], ETMSSPCICR2);
+ break;
+ case 3:
+ trc_write(state[i++], ETMSSCCR3);
+ trc_write(state[i++], ETMSSCSR3);
+ trc_write(state[i++], ETMSSPCICR3);
+ break;
+ case 4:
+ trc_write(state[i++], ETMSSCCR4);
+ trc_write(state[i++], ETMSSCSR4);
+ trc_write(state[i++], ETMSSPCICR4);
+ break;
+ case 5:
+ trc_write(state[i++], ETMSSCCR5);
+ trc_write(state[i++], ETMSSCSR5);
+ trc_write(state[i++], ETMSSPCICR5);
+ break;
+ case 6:
+ trc_write(state[i++], ETMSSCCR6);
+ trc_write(state[i++], ETMSSCSR6);
+ trc_write(state[i++], ETMSSPCICR6);
+ break;
+ case 7:
+ trc_write(state[i++], ETMSSCCR7);
+ trc_write(state[i++], ETMSSCSR7);
+ trc_write(state[i++], ETMSSPCICR7);
+ break;
+ default:
+ pr_err_ratelimited("idx %d out of bounds in %s\n", j, __func__);
+ }
+ return i;
+}
+
+static inline void etm_si_restore_state(struct etm_ctx *etmdata)
+{
+ int i, j;
+
+ i = 0;
+
+ /* Vote for ETM power/clock enable */
+ etm_clk_enable();
+
+ switch (etmdata->arch) {
+ case ETM_ARCH_V4_2:
+ case ETM_ARCH_V4:
+ atomic_notifier_call_chain(&etm_restore_notifier_list, 0, NULL);
+
+ /* check OS lock is locked */
+ if (BVAL(trc_readl(ETMOSLSR), 1) != 1) {
+ pr_err_ratelimited("OS lock is unlocked\n");
+ trc_write(0x1, ETMOSLAR);
+ isb();
+ }
+
+ /* main control and configuration registers */
+ trc_write(etmdata->state[i++], ETMCONFIGR);
+ trc_write(etmdata->state[i++], ETMEVENTCTL0R);
+ trc_write(etmdata->state[i++], ETMEVENTCTL1R);
+ trc_write(etmdata->state[i++], ETMSTALLCTLR);
+ trc_write(etmdata->state[i++], ETMTSCTLR);
+ trc_write(etmdata->state[i++], ETMSYNCPR);
+ trc_write(etmdata->state[i++], ETMCCCTLR);
+ trc_write(etmdata->state[i++], ETMTRACEIDR);
+ /* filtering control registers */
+ trc_write(etmdata->state[i++], ETMVICTLR);
+ trc_write(etmdata->state[i++], ETMVIIECTLR);
+ trc_write(etmdata->state[i++], ETMVISSCTLR);
+ /* derived resources registers */
+ for (j = 0; j < etmdata->nr_seq_state-1; j++)
+ i = etm_write_ssxr(etmdata->state, i, j);
+ trc_write(etmdata->state[i++], ETMSEQRSTEVR);
+ trc_write(etmdata->state[i++], ETMSEQSTR);
+ trc_write(etmdata->state[i++], ETMEXTINSELR);
+ for (j = 0; j < etmdata->nr_cntr; j++)
+ i = etm_write_crxr(etmdata->state, i, j);
+ /* resource selection registers */
+ for (j = 0; j < etmdata->nr_resource; j++)
+ i = etm_write_rsxr(etmdata->state, i, j + 2);
+ /* comparator registers */
+ for (j = 0; j < etmdata->nr_addr_cmp * 2; j++)
+ i = etm_write_acr(etmdata->state, i, j);
+ for (j = 0; j < etmdata->nr_data_cmp; j++)
+ i = etm_write_dvcr(etmdata->state, i, j);
+ for (j = 0; j < etmdata->nr_ctxid_cmp; j++)
+ i = etm_write_ccvr(etmdata->state, i, j);
+ trc_write(etmdata->state[i++], ETMCIDCCTLR0);
+ for (j = 0; j < etmdata->nr_vmid_cmp; j++)
+ i = etm_write_vcvr(etmdata->state, i, j);
+ /* single-shot comparator registers */
+ for (j = 0; j < etmdata->nr_ss_cmp; j++)
+ i = etm_write_sscr(etmdata->state, i, j);
+ /* program ctrl register */
+ trc_write(etmdata->state[i++], ETMPRGCTLR);
+
+ isb();
+ trc_write(0x0, ETMOSLAR);
+ break;
+ default:
+ pr_err_ratelimited("unsupported etm arch %d in %s\n",
+ etmdata->arch, __func__);
+ }
+
+ /* Vote for ETM power/clock disable */
+ etm_clk_disable();
+}
+
+void msm_jtag_etm_save_state(void)
+{
+ int cpu;
+
+ cpu = raw_smp_processor_id();
+
+ if (!etm[cpu] || etm[cpu]->save_restore_disabled)
+ return;
+
+ if (etm[cpu]->save_restore_enabled) {
+ if (etm[cpu]->si_enable)
+ etm_si_save_state(etm[cpu]);
+ else
+ etm_mm_save_state(etm[cpu]);
+ }
+}
+EXPORT_SYMBOL(msm_jtag_etm_save_state);
+
+void msm_jtag_etm_restore_state(void)
+{
+ int cpu;
+
+ cpu = raw_smp_processor_id();
+
+ if (!etm[cpu] || etm[cpu]->save_restore_disabled)
+ return;
+
+ /*
+ * Check to ensure we attempt to restore only when save
+ * has been done is accomplished by callee function.
+ */
+ if (etm[cpu]->save_restore_enabled) {
+ if (etm[cpu]->si_enable)
+ etm_si_restore_state(etm[cpu]);
+ else
+ etm_mm_restore_state(etm[cpu]);
+ }
+}
+EXPORT_SYMBOL(msm_jtag_etm_restore_state);
+
+static inline bool etm_arch_supported(uint8_t arch)
+{
+ switch (arch) {
+ case ETM_ARCH_V4_2:
+ case ETM_ARCH_V4:
+ break;
+ default:
+ return false;
+ }
+ return true;
+}
+
+static void etm_os_lock_init(struct etm_ctx *etmdata)
+{
+ uint32_t etmoslsr;
+
+ etmoslsr = etm_readl(etmdata, TRCOSLSR);
+ if ((BVAL(etmoslsr, 0) == 0) && BVAL(etmoslsr, 3))
+ etmdata->os_lock_present = true;
+ else
+ etmdata->os_lock_present = false;
+}
+
+static void etm_init_arch_data(void *info)
+{
+ uint32_t val;
+ struct etm_ctx *etmdata = info;
+
+ ETM_UNLOCK(etmdata);
+
+ etm_os_lock_init(etmdata);
+
+ val = etm_readl(etmdata, TRCIDR1);
+ etmdata->arch = BMVAL(val, 4, 11);
+
+ /* number of resources trace unit supports */
+ val = etm_readl(etmdata, TRCIDR4);
+ etmdata->nr_addr_cmp = BMVAL(val, 0, 3);
+ etmdata->nr_data_cmp = BMVAL(val, 4, 7);
+ etmdata->nr_resource = BMVAL(val, 16, 19);
+ etmdata->nr_ss_cmp = BMVAL(val, 20, 23);
+ etmdata->nr_ctxid_cmp = BMVAL(val, 24, 27);
+ etmdata->nr_vmid_cmp = BMVAL(val, 28, 31);
+
+ val = etm_readl(etmdata, TRCIDR5);
+ etmdata->nr_seq_state = BMVAL(val, 25, 27);
+ etmdata->nr_cntr = BMVAL(val, 28, 30);
+
+ ETM_LOCK(etmdata);
+}
+
+static int jtag_mm_etm_starting(unsigned int cpu)
+{
+ if (!etm[cpu])
+ return 0;
+
+ spin_lock(&etm[cpu]->spinlock);
+ if (!etm[cpu]->init) {
+ etm_init_arch_data(etm[cpu]);
+ etm[cpu]->init = true;
+ }
+ spin_unlock(&etm[cpu]->spinlock);
+
+ return 0;
+}
+
+static int jtag_mm_etm_online(unsigned int cpu)
+{
+ if (!etm[cpu])
+ return 0;
+
+ mutex_lock(&etm[cpu]->mutex);
+ if (etm[cpu]->enable) {
+ mutex_unlock(&etm[cpu]->mutex);
+ return 0;
+ }
+ if (etm_arch_supported(etm[cpu]->arch)) {
+ if (scm_get_feat_version(TZ_DBG_ETM_FEAT_ID) <
+ TZ_DBG_ETM_VER)
+ etm[cpu]->save_restore_enabled = true;
+ else
+ pr_info("etm save-restore supported by TZ\n");
+ } else
+ pr_info("etm arch %u not supported\n", etm[cpu]->arch);
+ etm[cpu]->enable = true;
+ mutex_unlock(&etm[cpu]->mutex);
+
+ return 0;
+}
+
+static bool skip_etm_save_restore(void)
+{
+ uint32_t id;
+ uint32_t version;
+
+ id = socinfo_get_id();
+ version = socinfo_get_version();
+
+ if (id == HW_SOC_ID_M8953 && SOCINFO_VERSION_MAJOR(version) == 1 &&
+ SOCINFO_VERSION_MINOR(version) == 0)
+ return true;
+
+ return false;
+}
+
+static int jtag_mm_etm_probe(struct platform_device *pdev, uint32_t cpu)
+{
+ struct etm_ctx *etmdata;
+ struct resource *res;
+ struct device *dev = &pdev->dev;
+ int ret;
+
+ /* Allocate memory per cpu */
+ etmdata = devm_kzalloc(dev, sizeof(struct etm_ctx), GFP_KERNEL);
+ if (!etmdata)
+ return -ENOMEM;
+
+
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "etm-base");
+ if (!res)
+ return -ENODEV;
+
+ etmdata->base = devm_ioremap(dev, res->start, resource_size(res));
+ if (!etmdata->base)
+ return -EINVAL;
+
+ etmdata->si_enable = of_property_read_bool(pdev->dev.of_node,
+ "qcom,si-enable");
+ etmdata->save_restore_disabled = of_property_read_bool(
+ pdev->dev.of_node,
+ "qcom,save-restore-disable");
+
+ if (skip_etm_save_restore())
+ etmdata->save_restore_disabled = 1;
+
+ /* Allocate etm state save space per core */
+ etmdata->state = devm_kzalloc(dev,
+ MAX_ETM_STATE_SIZE * sizeof(uint64_t),
+ GFP_KERNEL);
+ if (!etmdata->state)
+ return -ENOMEM;
+
+ spin_lock_init(&etmdata->spinlock);
+ mutex_init(&etmdata->mutex);
+
+ if (cnt++ == 0) {
+ cpuhp_setup_state_nocalls(CPUHP_AP_ARM_MM_CORESIGHT4_STARTING,
+ "AP_ARM_CORESIGHT4_STARTING",
+ jtag_mm_etm_starting, NULL);
+ ret = cpuhp_setup_state_nocalls(CPUHP_AP_ONLINE_DYN,
+ "AP_ARM_CORESIGHT4_ONLINE",
+ jtag_mm_etm_online, NULL);
+ }
+
+ get_online_cpus();
+
+ if (!smp_call_function_single(cpu, etm_init_arch_data, etmdata,
+ 1))
+ etmdata->init = true;
+
+ etm[cpu] = etmdata;
+
+ put_online_cpus();
+
+ mutex_lock(&etmdata->mutex);
+ if (etmdata->init && !etmdata->enable) {
+ if (etm_arch_supported(etmdata->arch)) {
+ if (scm_get_feat_version(TZ_DBG_ETM_FEAT_ID) <
+ TZ_DBG_ETM_VER)
+ etmdata->save_restore_enabled = true;
+ else
+ pr_info("etm save-restore supported by TZ\n");
+ } else
+ pr_info("etm arch %u not supported\n", etmdata->arch);
+ etmdata->enable = true;
+ }
+ mutex_unlock(&etmdata->mutex);
+ return 0;
+}
+
+static int jtag_mm_probe(struct platform_device *pdev)
+{
+ int ret, i, cpu = -1;
+ struct device *dev = &pdev->dev;
+ struct device_node *cpu_node;
+
+ cpu_node = of_parse_phandle(pdev->dev.of_node,
+ "qcom,coresight-jtagmm-cpu", 0);
+ if (!cpu_node) {
+ dev_err(dev, "Jtag-mm cpu handle not specified\n");
+ return -ENODEV;
+ }
+ for_each_possible_cpu(i) {
+ if (cpu_node == of_get_cpu_node(i, NULL)) {
+ cpu = i;
+ break;
+ }
+ }
+
+ if (cpu == -1) {
+ dev_err(dev, "invalid Jtag-mm cpu handle\n");
+ return -EINVAL;
+ }
+
+ clock[cpu] = devm_clk_get(dev, "core_clk");
+ if (IS_ERR(clock[cpu])) {
+ ret = PTR_ERR(clock[cpu]);
+ return ret;
+ }
+
+ ret = clk_prepare_enable(clock[cpu]);
+ if (ret)
+ return ret;
+
+ platform_set_drvdata(pdev, clock[cpu]);
+
+ ret = jtag_mm_etm_probe(pdev, cpu);
+ if (ret)
+ clk_disable_unprepare(clock[cpu]);
+
+ return ret;
+}
+
+static void jtag_mm_etm_remove(void)
+{
+ cpuhp_remove_state_nocalls(CPUHP_AP_ARM_MM_CORESIGHT4_STARTING);
+}
+
+static int jtag_mm_remove(struct platform_device *pdev)
+{
+ struct clk *clock = platform_get_drvdata(pdev);
+
+ if (--cnt == 0)
+ jtag_mm_etm_remove();
+ clk_disable_unprepare(clock);
+ return 0;
+}
+
+static const struct of_device_id msm_qdss_mm_match[] = {
+ { .compatible = "qcom,jtagv8-mm"},
+ {}
+};
+
+static struct platform_driver jtag_mm_driver = {
+ .probe = jtag_mm_probe,
+ .remove = jtag_mm_remove,
+ .driver = {
+ .name = "msm-jtagv8-mm",
+ .owner = THIS_MODULE,
+ .of_match_table = msm_qdss_mm_match,
+ },
+};
+
+static int __init jtag_mm_init(void)
+{
+ return platform_driver_register(&jtag_mm_driver);
+}
+module_init(jtag_mm_init);
+
+static void __exit jtag_mm_exit(void)
+{
+ platform_driver_unregister(&jtag_mm_driver);
+}
+module_exit(jtag_mm_exit);
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("CoreSight DEBUGv8 and ETMv4 save-restore driver");
diff --git a/drivers/soc/qcom/jtagv8.c b/drivers/soc/qcom/jtagv8.c
new file mode 100644
index 0000000..94dffd1
--- /dev/null
+++ b/drivers/soc/qcom/jtagv8.c
@@ -0,0 +1,1000 @@
+/* Copyright (c) 2014-2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/types.h>
+#include <linux/err.h>
+#include <linux/slab.h>
+#include <linux/smp.h>
+#include <linux/cpu.h>
+#include <linux/cpu_pm.h>
+#include <linux/export.h>
+#include <linux/printk.h>
+#include <linux/ratelimit.h>
+#include <linux/coresight.h>
+#include <linux/io.h>
+#include <linux/platform_device.h>
+#include <linux/bitops.h>
+#include <soc/qcom/scm.h>
+#include <soc/qcom/jtag.h>
+#ifdef CONFIG_ARM64
+#include <asm/debugv8.h>
+#else
+#include <asm/hardware/debugv8.h>
+#endif
+
+#define TIMEOUT_US (100)
+
+#define BM(lsb, msb) ((BIT(msb) - BIT(lsb)) + BIT(msb))
+#define BMVAL(val, lsb, msb) ((val & BM(lsb, msb)) >> lsb)
+#define BVAL(val, n) ((val & BIT(n)) >> n)
+
+#ifdef CONFIG_ARM64
+#define ARM_DEBUG_ARCH_V8_8 (0x8)
+#define ARM_DEBUG_ARCH_V8 (0x6)
+#endif
+
+#define MAX_DBG_REGS (66)
+#define MAX_DBG_STATE_SIZE (MAX_DBG_REGS * num_possible_cpus())
+
+#define OSLOCK_MAGIC (0xC5ACCE55)
+#define TZ_DBG_ETM_FEAT_ID (0x8)
+#define TZ_DBG_ETM_VER (0x400000)
+
+static uint32_t msm_jtag_save_cntr[NR_CPUS];
+static uint32_t msm_jtag_restore_cntr[NR_CPUS];
+
+/* access debug registers using system instructions */
+struct dbg_cpu_ctx {
+ uint32_t *state;
+};
+
+struct dbg_ctx {
+ uint8_t arch;
+ bool save_restore_enabled;
+ uint8_t nr_wp;
+ uint8_t nr_bp;
+ uint8_t nr_ctx_cmp;
+#ifdef CONFIG_ARM64
+ uint64_t *state;
+#else
+ uint32_t *state;
+#endif
+};
+
+static struct dbg_ctx dbg;
+static struct notifier_block jtag_cpu_pm_notifier;
+
+#ifdef CONFIG_ARM64
+static int dbg_read_arch64_bxr(uint64_t *state, int i, int j)
+{
+ switch (j) {
+ case 0:
+ state[i++] = dbg_readq(DBGBVR0_EL1);
+ state[i++] = (uint64_t)dbg_readl(DBGBCR0_EL1);
+ break;
+ case 1:
+ state[i++] = dbg_readq(DBGBVR1_EL1);
+ state[i++] = (uint64_t)dbg_readl(DBGBCR1_EL1);
+ break;
+ case 2:
+ state[i++] = dbg_readq(DBGBVR2_EL1);
+ state[i++] = (uint64_t)dbg_readl(DBGBCR2_EL1);
+ break;
+ case 3:
+ state[i++] = dbg_readq(DBGBVR3_EL1);
+ state[i++] = (uint64_t)dbg_readl(DBGBCR3_EL1);
+ break;
+ case 4:
+ state[i++] = dbg_readq(DBGBVR4_EL1);
+ state[i++] = (uint64_t)dbg_readl(DBGBCR4_EL1);
+ break;
+ case 5:
+ state[i++] = dbg_readq(DBGBVR5_EL1);
+ state[i++] = (uint64_t)dbg_readl(DBGBCR5_EL1);
+ break;
+ case 6:
+ state[i++] = dbg_readq(DBGBVR6_EL1);
+ state[i++] = (uint64_t)dbg_readl(DBGBCR6_EL1);
+ break;
+ case 7:
+ state[i++] = dbg_readq(DBGBVR7_EL1);
+ state[i++] = (uint64_t)dbg_readl(DBGBCR7_EL1);
+ break;
+ case 8:
+ state[i++] = dbg_readq(DBGBVR8_EL1);
+ state[i++] = (uint64_t)dbg_readl(DBGBCR8_EL1);
+ break;
+ case 9:
+ state[i++] = dbg_readq(DBGBVR9_EL1);
+ state[i++] = (uint64_t)dbg_readl(DBGBCR9_EL1);
+ break;
+ case 10:
+ state[i++] = dbg_readq(DBGBVR10_EL1);
+ state[i++] = (uint64_t)dbg_readl(DBGBCR10_EL1);
+ break;
+ case 11:
+ state[i++] = dbg_readq(DBGBVR11_EL1);
+ state[i++] = (uint64_t)dbg_readl(DBGBCR11_EL1);
+ break;
+ case 12:
+ state[i++] = dbg_readq(DBGBVR12_EL1);
+ state[i++] = (uint64_t)dbg_readl(DBGBCR12_EL1);
+ break;
+ case 13:
+ state[i++] = dbg_readq(DBGBVR13_EL1);
+ state[i++] = (uint64_t)dbg_readl(DBGBCR13_EL1);
+ break;
+ case 14:
+ state[i++] = dbg_readq(DBGBVR14_EL1);
+ state[i++] = (uint64_t)dbg_readl(DBGBCR14_EL1);
+ break;
+ case 15:
+ state[i++] = dbg_readq(DBGBVR15_EL1);
+ state[i++] = (uint64_t)dbg_readl(DBGBCR15_EL1);
+ break;
+ default:
+ pr_err_ratelimited("idx %d out of bounds in %s\n", j, __func__);
+ }
+ return i;
+}
+
+static int dbg_write_arch64_bxr(uint64_t *state, int i, int j)
+{
+ switch (j) {
+ case 0:
+ dbg_write(state[i++], DBGBVR0_EL1);
+ dbg_write(state[i++], DBGBCR0_EL1);
+ break;
+ case 1:
+ dbg_write(state[i++], DBGBVR1_EL1);
+ dbg_write(state[i++], DBGBCR1_EL1);
+ break;
+ case 2:
+ dbg_write(state[i++], DBGBVR2_EL1);
+ dbg_write(state[i++], DBGBCR2_EL1);
+ break;
+ case 3:
+ dbg_write(state[i++], DBGBVR3_EL1);
+ dbg_write(state[i++], DBGBCR3_EL1);
+ break;
+ case 4:
+ dbg_write(state[i++], DBGBVR4_EL1);
+ dbg_write(state[i++], DBGBCR4_EL1);
+ break;
+ case 5:
+ dbg_write(state[i++], DBGBVR5_EL1);
+ dbg_write(state[i++], DBGBCR5_EL1);
+ break;
+ case 6:
+ dbg_write(state[i++], DBGBVR6_EL1);
+ dbg_write(state[i++], DBGBCR6_EL1);
+ break;
+ case 7:
+ dbg_write(state[i++], DBGBVR7_EL1);
+ dbg_write(state[i++], DBGBCR7_EL1);
+ break;
+ case 8:
+ dbg_write(state[i++], DBGBVR8_EL1);
+ dbg_write(state[i++], DBGBCR8_EL1);
+ break;
+ case 9:
+ dbg_write(state[i++], DBGBVR9_EL1);
+ dbg_write(state[i++], DBGBCR9_EL1);
+ break;
+ case 10:
+ dbg_write(state[i++], DBGBVR10_EL1);
+ dbg_write(state[i++], DBGBCR10_EL1);
+ break;
+ case 11:
+ dbg_write(state[i++], DBGBVR11_EL1);
+ dbg_write(state[i++], DBGBCR11_EL1);
+ break;
+ case 12:
+ dbg_write(state[i++], DBGBVR12_EL1);
+ dbg_write(state[i++], DBGBCR12_EL1);
+ break;
+ case 13:
+ dbg_write(state[i++], DBGBVR13_EL1);
+ dbg_write(state[i++], DBGBCR13_EL1);
+ break;
+ case 14:
+ dbg_write(state[i++], DBGBVR14_EL1);
+ dbg_write(state[i++], DBGBCR14_EL1);
+ break;
+ case 15:
+ dbg_write(state[i++], DBGBVR15_EL1);
+ dbg_write(state[i++], DBGBCR15_EL1);
+ break;
+ default:
+ pr_err_ratelimited("idx %d out of bounds in %s\n", j, __func__);
+ }
+ return i;
+}
+
+static int dbg_read_arch64_wxr(uint64_t *state, int i, int j)
+{
+ switch (j) {
+ case 0:
+ state[i++] = dbg_readq(DBGWVR0_EL1);
+ state[i++] = (uint64_t)dbg_readl(DBGWCR0_EL1);
+ break;
+ case 1:
+ state[i++] = dbg_readq(DBGWVR1_EL1);
+ state[i++] = (uint64_t)dbg_readl(DBGWCR1_EL1);
+ break;
+ case 2:
+ state[i++] = dbg_readq(DBGWVR2_EL1);
+ state[i++] = (uint64_t)dbg_readl(DBGWCR2_EL1);
+ break;
+ case 3:
+ state[i++] = dbg_readq(DBGWVR3_EL1);
+ state[i++] = (uint64_t)dbg_readl(DBGWCR3_EL1);
+ break;
+ case 4:
+ state[i++] = dbg_readq(DBGWVR4_EL1);
+ state[i++] = (uint64_t)dbg_readl(DBGWCR4_EL1);
+ break;
+ case 5:
+ state[i++] = dbg_readq(DBGWVR5_EL1);
+ state[i++] = (uint64_t)dbg_readl(DBGWCR5_EL1);
+ break;
+ case 6:
+ state[i++] = dbg_readq(DBGWVR6_EL1);
+ state[i++] = (uint64_t)dbg_readl(DBGWCR6_EL1);
+ break;
+ case 7:
+ state[i++] = dbg_readq(DBGWVR7_EL1);
+ state[i++] = (uint64_t)dbg_readl(DBGWCR7_EL1);
+ break;
+ case 8:
+ state[i++] = dbg_readq(DBGWVR8_EL1);
+ state[i++] = (uint64_t)dbg_readl(DBGWCR8_EL1);
+ break;
+ case 9:
+ state[i++] = dbg_readq(DBGWVR9_EL1);
+ state[i++] = (uint64_t)dbg_readl(DBGWCR9_EL1);
+ break;
+ case 10:
+ state[i++] = dbg_readq(DBGWVR10_EL1);
+ state[i++] = (uint64_t)dbg_readl(DBGWCR10_EL1);
+ break;
+ case 11:
+ state[i++] = dbg_readq(DBGWVR11_EL1);
+ state[i++] = (uint64_t)dbg_readl(DBGWCR11_EL1);
+ break;
+ case 12:
+ state[i++] = dbg_readq(DBGWVR12_EL1);
+ state[i++] = (uint64_t)dbg_readl(DBGWCR12_EL1);
+ break;
+ case 13:
+ state[i++] = dbg_readq(DBGWVR13_EL1);
+ state[i++] = (uint64_t)dbg_readl(DBGWCR13_EL1);
+ break;
+ case 14:
+ state[i++] = dbg_readq(DBGWVR14_EL1);
+ state[i++] = (uint64_t)dbg_readl(DBGWCR14_EL1);
+ break;
+ case 15:
+ state[i++] = dbg_readq(DBGWVR15_EL1);
+ state[i++] = (uint64_t)dbg_readl(DBGWCR15_EL1);
+ break;
+ default:
+ pr_err_ratelimited("idx %d out of bounds in %s\n", j, __func__);
+ }
+ return i;
+}
+
+static int dbg_write_arch64_wxr(uint64_t *state, int i, int j)
+{
+ switch (j) {
+ case 0:
+ dbg_write(state[i++], DBGWVR0_EL1);
+ dbg_write(state[i++], DBGWCR0_EL1);
+ break;
+ case 1:
+ dbg_write(state[i++], DBGWVR1_EL1);
+ dbg_write(state[i++], DBGWCR1_EL1);
+ break;
+ case 2:
+ dbg_write(state[i++], DBGWVR2_EL1);
+ dbg_write(state[i++], DBGWCR2_EL1);
+ break;
+ case 3:
+ dbg_write(state[i++], DBGWVR3_EL1);
+ dbg_write(state[i++], DBGWCR3_EL1);
+ break;
+ case 4:
+ dbg_write(state[i++], DBGWVR4_EL1);
+ dbg_write(state[i++], DBGWCR4_EL1);
+ break;
+ case 5:
+ dbg_write(state[i++], DBGWVR5_EL1);
+ dbg_write(state[i++], DBGWCR5_EL1);
+ break;
+ case 6:
+ dbg_write(state[i++], DBGWVR0_EL1);
+ dbg_write(state[i++], DBGWCR6_EL1);
+ break;
+ case 7:
+ dbg_write(state[i++], DBGWVR7_EL1);
+ dbg_write(state[i++], DBGWCR7_EL1);
+ break;
+ case 8:
+ dbg_write(state[i++], DBGWVR8_EL1);
+ dbg_write(state[i++], DBGWCR8_EL1);
+ break;
+ case 9:
+ dbg_write(state[i++], DBGWVR9_EL1);
+ dbg_write(state[i++], DBGWCR9_EL1);
+ break;
+ case 10:
+ dbg_write(state[i++], DBGWVR10_EL1);
+ dbg_write(state[i++], DBGWCR10_EL1);
+ break;
+ case 11:
+ dbg_write(state[i++], DBGWVR11_EL1);
+ dbg_write(state[i++], DBGWCR11_EL1);
+ break;
+ case 12:
+ dbg_write(state[i++], DBGWVR12_EL1);
+ dbg_write(state[i++], DBGWCR12_EL1);
+ break;
+ case 13:
+ dbg_write(state[i++], DBGWVR13_EL1);
+ dbg_write(state[i++], DBGWCR13_EL1);
+ break;
+ case 14:
+ dbg_write(state[i++], DBGWVR14_EL1);
+ dbg_write(state[i++], DBGWCR14_EL1);
+ break;
+ case 15:
+ dbg_write(state[i++], DBGWVR15_EL1);
+ dbg_write(state[i++], DBGWCR15_EL1);
+ break;
+ default:
+ pr_err_ratelimited("idx %d out of bounds in %s\n", j, __func__);
+ }
+ return i;
+}
+
+static inline void dbg_save_state(int cpu)
+{
+ int i, j;
+
+ i = cpu * MAX_DBG_REGS;
+
+ switch (dbg.arch) {
+ case ARM_DEBUG_ARCH_V8_8:
+ case ARM_DEBUG_ARCH_V8:
+ /* Set OS Lock to inform the debugger that the OS is in the
+ * process of saving debug registers. It prevents accidental
+ * modification of the debug regs by the external debugger.
+ */
+ dbg_write(0x1, OSLAR_EL1);
+ /* Ensure OS lock is set before proceeding */
+ isb();
+
+ dbg.state[i++] = (uint32_t)dbg_readl(MDSCR_EL1);
+ for (j = 0; j < dbg.nr_bp; j++)
+ i = dbg_read_arch64_bxr((uint64_t *)dbg.state, i, j);
+ for (j = 0; j < dbg.nr_wp; j++)
+ i = dbg_read_arch64_wxr((uint64_t *)dbg.state, i, j);
+ dbg.state[i++] = (uint32_t)dbg_readl(MDCCINT_EL1);
+ dbg.state[i++] = (uint32_t)dbg_readl(DBGCLAIMCLR_EL1);
+ dbg.state[i++] = (uint32_t)dbg_readl(OSECCR_EL1);
+ dbg.state[i++] = (uint32_t)dbg_readl(OSDTRRX_EL1);
+ dbg.state[i++] = (uint32_t)dbg_readl(OSDTRTX_EL1);
+
+ /* Set the OS double lock */
+ isb();
+ dbg_write(0x1, OSDLR_EL1);
+ isb();
+ break;
+ default:
+ pr_err_ratelimited("unsupported dbg arch %d in %s\n", dbg.arch,
+ __func__);
+ }
+}
+
+static inline void dbg_restore_state(int cpu)
+{
+ int i, j;
+
+ i = cpu * MAX_DBG_REGS;
+
+ switch (dbg.arch) {
+ case ARM_DEBUG_ARCH_V8_8:
+ case ARM_DEBUG_ARCH_V8:
+ /* Clear the OS double lock */
+ isb();
+ dbg_write(0x0, OSDLR_EL1);
+ isb();
+
+ /* Set OS lock. Lock will already be set after power collapse
+ * but this write is included to ensure it is set.
+ */
+ dbg_write(0x1, OSLAR_EL1);
+ isb();
+
+ dbg_write(dbg.state[i++], MDSCR_EL1);
+ for (j = 0; j < dbg.nr_bp; j++)
+ i = dbg_write_arch64_bxr((uint64_t *)dbg.state, i, j);
+ for (j = 0; j < dbg.nr_wp; j++)
+ i = dbg_write_arch64_wxr((uint64_t *)dbg.state, i, j);
+ dbg_write(dbg.state[i++], MDCCINT_EL1);
+ dbg_write(dbg.state[i++], DBGCLAIMSET_EL1);
+ dbg_write(dbg.state[i++], OSECCR_EL1);
+ dbg_write(dbg.state[i++], OSDTRRX_EL1);
+ dbg_write(dbg.state[i++], OSDTRTX_EL1);
+
+ isb();
+ dbg_write(0x0, OSLAR_EL1);
+ isb();
+ break;
+ default:
+ pr_err_ratelimited("unsupported dbg arch %d in %s\n", dbg.arch,
+ __func__);
+ }
+}
+
+static void dbg_init_arch_data(void)
+{
+ uint64_t dbgfr;
+
+ /* This will run on core0 so use it to populate parameters */
+ dbgfr = dbg_readq(ID_AA64DFR0_EL1);
+ dbg.arch = BMVAL(dbgfr, 0, 3);
+ dbg.nr_bp = BMVAL(dbgfr, 12, 15) + 1;
+ dbg.nr_wp = BMVAL(dbgfr, 20, 23) + 1;
+ dbg.nr_ctx_cmp = BMVAL(dbgfr, 28, 31) + 1;
+}
+#else
+
+static int dbg_read_arch32_bxr(uint32_t *state, int i, int j)
+{
+ switch (j) {
+ case 0:
+ state[i++] = dbg_read(DBGBVR0);
+ state[i++] = dbg_read(DBGBCR0);
+ break;
+ case 1:
+ state[i++] = dbg_read(DBGBVR1);
+ state[i++] = dbg_read(DBGBCR1);
+ break;
+ case 2:
+ state[i++] = dbg_read(DBGBVR2);
+ state[i++] = dbg_read(DBGBCR2);
+ break;
+ case 3:
+ state[i++] = dbg_read(DBGBVR3);
+ state[i++] = dbg_read(DBGBCR3);
+ break;
+ case 4:
+ state[i++] = dbg_read(DBGBVR4);
+ state[i++] = dbg_read(DBGBCR4);
+ break;
+ case 5:
+ state[i++] = dbg_read(DBGBVR5);
+ state[i++] = dbg_read(DBGBCR5);
+ break;
+ case 6:
+ state[i++] = dbg_read(DBGBVR6);
+ state[i++] = dbg_read(DBGBCR6);
+ break;
+ case 7:
+ state[i++] = dbg_read(DBGBVR7);
+ state[i++] = dbg_read(DBGBCR7);
+ break;
+ case 8:
+ state[i++] = dbg_read(DBGBVR8);
+ state[i++] = dbg_read(DBGBCR8);
+ break;
+ case 9:
+ state[i++] = dbg_read(DBGBVR9);
+ state[i++] = dbg_read(DBGBCR9);
+ break;
+ case 10:
+ state[i++] = dbg_read(DBGBVR10);
+ state[i++] = dbg_read(DBGBCR10);
+ break;
+ case 11:
+ state[i++] = dbg_read(DBGBVR11);
+ state[i++] = dbg_read(DBGBCR11);
+ break;
+ case 12:
+ state[i++] = dbg_read(DBGBVR12);
+ state[i++] = dbg_read(DBGBCR12);
+ break;
+ case 13:
+ state[i++] = dbg_read(DBGBVR13);
+ state[i++] = dbg_read(DBGBCR13);
+ break;
+ case 14:
+ state[i++] = dbg_read(DBGBVR14);
+ state[i++] = dbg_read(DBGBCR14);
+ break;
+ case 15:
+ state[i++] = dbg_read(DBGBVR15);
+ state[i++] = dbg_read(DBGBCR15);
+ break;
+ default:
+ pr_err_ratelimited("idx %d out of bounds in %s\n", j, __func__);
+ }
+ return i;
+}
+
+static int dbg_write_arch32_bxr(uint32_t *state, int i, int j)
+{
+ switch (j) {
+ case 0:
+ dbg_write(state[i++], DBGBVR0);
+ dbg_write(state[i++], DBGBCR0);
+ break;
+ case 1:
+ dbg_write(state[i++], DBGBVR1);
+ dbg_write(state[i++], DBGBCR1);
+ break;
+ case 2:
+ dbg_write(state[i++], DBGBVR2);
+ dbg_write(state[i++], DBGBCR2);
+ break;
+ case 3:
+ dbg_write(state[i++], DBGBVR3);
+ dbg_write(state[i++], DBGBCR3);
+ break;
+ case 4:
+ dbg_write(state[i++], DBGBVR4);
+ dbg_write(state[i++], DBGBCR4);
+ break;
+ case 5:
+ dbg_write(state[i++], DBGBVR5);
+ dbg_write(state[i++], DBGBCR5);
+ break;
+ case 6:
+ dbg_write(state[i++], DBGBVR6);
+ dbg_write(state[i++], DBGBCR6);
+ break;
+ case 7:
+ dbg_write(state[i++], DBGBVR7);
+ dbg_write(state[i++], DBGBCR7);
+ break;
+ case 8:
+ dbg_write(state[i++], DBGBVR8);
+ dbg_write(state[i++], DBGBCR8);
+ break;
+ case 9:
+ dbg_write(state[i++], DBGBVR9);
+ dbg_write(state[i++], DBGBCR9);
+ break;
+ case 10:
+ dbg_write(state[i++], DBGBVR10);
+ dbg_write(state[i++], DBGBCR10);
+ break;
+ case 11:
+ dbg_write(state[i++], DBGBVR11);
+ dbg_write(state[i++], DBGBCR11);
+ break;
+ case 12:
+ dbg_write(state[i++], DBGBVR12);
+ dbg_write(state[i++], DBGBCR12);
+ break;
+ case 13:
+ dbg_write(state[i++], DBGBVR13);
+ dbg_write(state[i++], DBGBCR13);
+ break;
+ case 14:
+ dbg_write(state[i++], DBGBVR14);
+ dbg_write(state[i++], DBGBCR14);
+ break;
+ case 15:
+ dbg_write(state[i++], DBGBVR15);
+ dbg_write(state[i++], DBGBCR15);
+ break;
+ default:
+ pr_err_ratelimited("idx %d out of bounds in %s\n", j, __func__);
+ }
+ return i;
+}
+
+static int dbg_read_arch32_wxr(uint32_t *state, int i, int j)
+{
+ switch (j) {
+ case 0:
+ state[i++] = dbg_read(DBGWVR0);
+ state[i++] = dbg_read(DBGWCR0);
+ break;
+ case 1:
+ state[i++] = dbg_read(DBGWVR1);
+ state[i++] = dbg_read(DBGWCR1);
+ break;
+ case 2:
+ state[i++] = dbg_read(DBGWVR2);
+ state[i++] = dbg_read(DBGWCR2);
+ break;
+ case 3:
+ state[i++] = dbg_read(DBGWVR3);
+ state[i++] = dbg_read(DBGWCR3);
+ break;
+ case 4:
+ state[i++] = dbg_read(DBGWVR4);
+ state[i++] = dbg_read(DBGWCR4);
+ break;
+ case 5:
+ state[i++] = dbg_read(DBGWVR5);
+ state[i++] = dbg_read(DBGWCR5);
+ break;
+ case 6:
+ state[i++] = dbg_read(DBGWVR6);
+ state[i++] = dbg_read(DBGWCR6);
+ break;
+ case 7:
+ state[i++] = dbg_read(DBGWVR7);
+ state[i++] = dbg_read(DBGWCR7);
+ break;
+ case 8:
+ state[i++] = dbg_read(DBGWVR8);
+ state[i++] = dbg_read(DBGWCR8);
+ break;
+ case 9:
+ state[i++] = dbg_read(DBGWVR9);
+ state[i++] = dbg_read(DBGWCR9);
+ break;
+ case 10:
+ state[i++] = dbg_read(DBGWVR10);
+ state[i++] = dbg_read(DBGWCR10);
+ break;
+ case 11:
+ state[i++] = dbg_read(DBGWVR11);
+ state[i++] = dbg_read(DBGWCR11);
+ break;
+ case 12:
+ state[i++] = dbg_read(DBGWVR12);
+ state[i++] = dbg_read(DBGWCR12);
+ break;
+ case 13:
+ state[i++] = dbg_read(DBGWVR13);
+ state[i++] = dbg_read(DBGWCR13);
+ break;
+ case 14:
+ state[i++] = dbg_read(DBGWVR14);
+ state[i++] = dbg_read(DBGWCR14);
+ break;
+ case 15:
+ state[i++] = dbg_read(DBGWVR15);
+ state[i++] = dbg_read(DBGWCR15);
+ break;
+ default:
+ pr_err_ratelimited("idx %d out of bounds in %s\n", j, __func__);
+ }
+ return i;
+}
+
+static int dbg_write_arch32_wxr(uint32_t *state, int i, int j)
+{
+ switch (j) {
+ case 0:
+ dbg_write(state[i++], DBGWVR0);
+ dbg_write(state[i++], DBGWCR0);
+ break;
+ case 1:
+ dbg_write(state[i++], DBGWVR1);
+ dbg_write(state[i++], DBGWCR1);
+ break;
+ case 2:
+ dbg_write(state[i++], DBGWVR2);
+ dbg_write(state[i++], DBGWCR2);
+ break;
+ case 3:
+ dbg_write(state[i++], DBGWVR3);
+ dbg_write(state[i++], DBGWCR3);
+ break;
+ case 4:
+ dbg_write(state[i++], DBGWVR4);
+ dbg_write(state[i++], DBGWCR4);
+ break;
+ case 5:
+ dbg_write(state[i++], DBGWVR5);
+ dbg_write(state[i++], DBGWCR5);
+ break;
+ case 6:
+ dbg_write(state[i++], DBGWVR6);
+ dbg_write(state[i++], DBGWCR6);
+ break;
+ case 7:
+ dbg_write(state[i++], DBGWVR7);
+ dbg_write(state[i++], DBGWCR7);
+ break;
+ case 8:
+ dbg_write(state[i++], DBGWVR8);
+ dbg_write(state[i++], DBGWCR8);
+ break;
+ case 9:
+ dbg_write(state[i++], DBGWVR9);
+ dbg_write(state[i++], DBGWCR9);
+ break;
+ case 10:
+ dbg_write(state[i++], DBGWVR10);
+ dbg_write(state[i++], DBGWCR10);
+ break;
+ case 11:
+ dbg_write(state[i++], DBGWVR11);
+ dbg_write(state[i++], DBGWCR11);
+ break;
+ case 12:
+ dbg_write(state[i++], DBGWVR12);
+ dbg_write(state[i++], DBGWCR12);
+ break;
+ case 13:
+ dbg_write(state[i++], DBGWVR13);
+ dbg_write(state[i++], DBGWCR13);
+ break;
+ case 14:
+ dbg_write(state[i++], DBGWVR14);
+ dbg_write(state[i++], DBGWCR14);
+ break;
+ case 15:
+ dbg_write(state[i++], DBGWVR15);
+ dbg_write(state[i++], DBGWCR15);
+ break;
+ default:
+ pr_err_ratelimited("idx %d out of bounds in %s\n", j, __func__);
+ }
+ return i;
+}
+
+static inline void dbg_save_state(int cpu)
+{
+ int i, j;
+
+ i = cpu * MAX_DBG_REGS;
+
+ switch (dbg.arch) {
+ case ARM_DEBUG_ARCH_V8_8:
+ case ARM_DEBUG_ARCH_V8:
+ /* Set OS Lock to inform the debugger that the OS is in the
+ * process of saving debug registers. It prevents accidental
+ * modification of the debug regs by the external debugger.
+ */
+ dbg_write(OSLOCK_MAGIC, DBGOSLAR);
+ /* Ensure OS lock is set before proceeding */
+ isb();
+
+ dbg.state[i++] = dbg_read(DBGDSCRext);
+ for (j = 0; j < dbg.nr_bp; j++)
+ i = dbg_read_arch32_bxr(dbg.state, i, j);
+ for (j = 0; j < dbg.nr_wp; j++)
+ i = dbg_read_arch32_wxr(dbg.state, i, j);
+ dbg.state[i++] = dbg_read(DBGDCCINT);
+ dbg.state[i++] = dbg_read(DBGCLAIMCLR);
+ dbg.state[i++] = dbg_read(DBGOSECCR);
+ dbg.state[i++] = dbg_read(DBGDTRRXext);
+ dbg.state[i++] = dbg_read(DBGDTRTXext);
+
+ /* Set the OS double lock */
+ isb();
+ dbg_write(0x1, DBGOSDLR);
+ isb();
+ break;
+ default:
+ pr_err_ratelimited("unsupported dbg arch %d in %s\n", dbg.arch,
+ __func__);
+ }
+}
+
+static inline void dbg_restore_state(int cpu)
+{
+ int i, j;
+
+ i = cpu * MAX_DBG_REGS;
+
+ switch (dbg.arch) {
+ case ARM_DEBUG_ARCH_V8_8:
+ case ARM_DEBUG_ARCH_V8:
+ /* Clear the OS double lock */
+ isb();
+ dbg_write(0x0, DBGOSDLR);
+ isb();
+
+ /* Set OS lock. Lock will already be set after power collapse
+ * but this write is included to ensure it is set.
+ */
+ dbg_write(OSLOCK_MAGIC, DBGOSLAR);
+ isb();
+
+ dbg_write(dbg.state[i++], DBGDSCRext);
+ for (j = 0; j < dbg.nr_bp; j++)
+ i = dbg_write_arch32_bxr((uint32_t *)dbg.state, i, j);
+ for (j = 0; j < dbg.nr_wp; j++)
+ i = dbg_write_arch32_wxr((uint32_t *)dbg.state, i, j);
+ dbg_write(dbg.state[i++], DBGDCCINT);
+ dbg_write(dbg.state[i++], DBGCLAIMSET);
+ dbg_write(dbg.state[i++], DBGOSECCR);
+ dbg_write(dbg.state[i++], DBGDTRRXext);
+ dbg_write(dbg.state[i++], DBGDTRTXext);
+
+ isb();
+ dbg_write(0x0, DBGOSLAR);
+ isb();
+ break;
+ default:
+ pr_err_ratelimited("unsupported dbg arch %d in %s\n", dbg.arch,
+ __func__);
+ }
+}
+
+static void dbg_init_arch_data(void)
+{
+ uint32_t dbgdidr;
+
+ /* This will run on core0 so use it to populate parameters */
+ dbgdidr = dbg_read(DBGDIDR);
+ dbg.arch = BMVAL(dbgdidr, 16, 19);
+ dbg.nr_ctx_cmp = BMVAL(dbgdidr, 20, 23) + 1;
+ dbg.nr_bp = BMVAL(dbgdidr, 24, 27) + 1;
+ dbg.nr_wp = BMVAL(dbgdidr, 28, 31) + 1;
+}
+#endif
+
+/*
+ * msm_jtag_save_state - save debug registers
+ *
+ * Debug registers are saved before power collapse if debug
+ * architecture is supported respectively and TZ isn't supporting
+ * the save and restore of debug registers.
+ *
+ * CONTEXT:
+ * Called with preemption off and interrupts locked from:
+ * 1. per_cpu idle thread context for idle power collapses
+ * or
+ * 2. per_cpu idle thread context for hotplug/suspend power collapse
+ * for nonboot cpus
+ * or
+ * 3. suspend thread context for suspend power collapse for core0
+ *
+ * In all cases we will run on the same cpu for the entire duration.
+ */
+void msm_jtag_save_state(void)
+{
+ int cpu;
+
+ cpu = raw_smp_processor_id();
+
+ msm_jtag_save_cntr[cpu]++;
+ /* ensure counter is updated before moving forward */
+ mb();
+
+ msm_jtag_etm_save_state();
+ if (dbg.save_restore_enabled)
+ dbg_save_state(cpu);
+}
+EXPORT_SYMBOL(msm_jtag_save_state);
+
+void msm_jtag_restore_state(void)
+{
+ int cpu;
+
+ cpu = raw_smp_processor_id();
+
+ /* Attempt restore only if save has been done. If power collapse
+ * is disabled, hotplug off of non-boot core will result in WFI
+ * and hence msm_jtag_save_state will not occur. Subsequently,
+ * during hotplug on of non-boot core when msm_jtag_restore_state
+ * is called via msm_platform_secondary_init, this check will help
+ * bail us out without restoring.
+ */
+ if (msm_jtag_save_cntr[cpu] == msm_jtag_restore_cntr[cpu])
+ return;
+ else if (msm_jtag_save_cntr[cpu] != msm_jtag_restore_cntr[cpu] + 1)
+ pr_err_ratelimited("jtag imbalance, save:%lu, restore:%lu\n",
+ (unsigned long)msm_jtag_save_cntr[cpu],
+ (unsigned long)msm_jtag_restore_cntr[cpu]);
+
+ msm_jtag_restore_cntr[cpu]++;
+ /* ensure counter is updated before moving forward */
+ mb();
+
+ if (dbg.save_restore_enabled)
+ dbg_restore_state(cpu);
+ msm_jtag_etm_restore_state();
+}
+EXPORT_SYMBOL(msm_jtag_restore_state);
+
+static inline bool dbg_arch_supported(uint8_t arch)
+{
+ switch (arch) {
+ case ARM_DEBUG_ARCH_V8_8:
+ case ARM_DEBUG_ARCH_V8:
+ break;
+ default:
+ return false;
+ }
+ return true;
+}
+
+static int jtag_hotcpu_save_callback(unsigned int cpu)
+{
+ msm_jtag_save_state();
+ return 0;
+}
+
+static int jtag_hotcpu_restore_callback(unsigned int cpu)
+{
+ msm_jtag_restore_state();
+ return 0;
+}
+
+static int jtag_cpu_pm_callback(struct notifier_block *nfb,
+ unsigned long action, void *hcpu)
+{
+ switch (action) {
+ case CPU_PM_ENTER:
+ msm_jtag_save_state();
+ break;
+ case CPU_PM_ENTER_FAILED:
+ case CPU_PM_EXIT:
+ msm_jtag_restore_state();
+ break;
+ }
+ return NOTIFY_OK;
+}
+
+static struct notifier_block jtag_cpu_pm_notifier = {
+ .notifier_call = jtag_cpu_pm_callback,
+};
+
+static int __init msm_jtag_dbg_init(void)
+{
+ int ret;
+
+ /* This will run on core0 so use it to populate parameters */
+ dbg_init_arch_data();
+
+ if (dbg_arch_supported(dbg.arch)) {
+ if (scm_get_feat_version(TZ_DBG_ETM_FEAT_ID) < TZ_DBG_ETM_VER) {
+ dbg.save_restore_enabled = true;
+ } else {
+ pr_info("dbg save-restore supported by TZ\n");
+ goto dbg_out;
+ }
+ } else {
+ pr_info("dbg arch %u not supported\n", dbg.arch);
+ goto dbg_out;
+ }
+
+ /* Allocate dbg state save space */
+#ifdef CONFIG_ARM64
+ dbg.state = kcalloc(MAX_DBG_STATE_SIZE, sizeof(uint64_t), GFP_KERNEL);
+#else
+ dbg.state = kcalloc(MAX_DBG_STATE_SIZE, sizeof(uint32_t), GFP_KERNEL);
+#endif
+ if (!dbg.state) {
+ ret = -ENOMEM;
+ goto dbg_err;
+ }
+
+ cpuhp_setup_state_nocalls(CPUHP_AP_ARM_SAVE_RESTORE_CORESIGHT4_STARTING,
+ "AP_ARM_SAVE_RESTORE_CORESIGHT4_STARTING",
+ jtag_hotcpu_restore_callback,
+ jtag_hotcpu_save_callback);
+
+ cpu_pm_register_notifier(&jtag_cpu_pm_notifier);
+dbg_out:
+ return 0;
+dbg_err:
+ return ret;
+}
+arch_initcall(msm_jtag_dbg_init);
diff --git a/drivers/soc/qcom/peripheral-loader.c b/drivers/soc/qcom/peripheral-loader.c
index 91c9441..a443239 100644
--- a/drivers/soc/qcom/peripheral-loader.c
+++ b/drivers/soc/qcom/peripheral-loader.c
@@ -55,7 +55,9 @@
#endif
#define PIL_NUM_DESC 10
+#define NUM_OF_ENCRYPTED_KEY 3
static void __iomem *pil_info_base;
+static void __iomem *pil_minidump_base;
/**
* proxy_timeout - Override for proxy vote timeouts
@@ -78,6 +80,18 @@
};
/**
+ * struct boot_minidump_smem_region - Representation of SMEM TOC
+ * @region_name: Name of modem segment to be dumped
+ * @region_base_address: Where segment start from
+ * @region_size: Size of segment to be dumped
+ */
+struct boot_minidump_smem_region {
+ char region_name[16];
+ u64 region_base_address;
+ u64 region_size;
+};
+
+/**
* struct pil_seg - memory map representing one segment
* @next: points to next seg mentor NULL if last segment
* @paddr: physical start address of segment
@@ -131,11 +145,67 @@
phys_addr_t region_end;
void *region;
struct pil_image_info __iomem *info;
+ struct md_ssr_ss_info __iomem *minidump;
+ int minidump_id;
int id;
int unvoted_flag;
size_t region_size;
};
+static int pil_do_minidump(struct pil_desc *desc, void *ramdump_dev)
+{
+ struct boot_minidump_smem_region __iomem *region_info;
+ struct ramdump_segment *ramdump_segs, *s;
+ struct pil_priv *priv = desc->priv;
+ void __iomem *subsys_smem_base;
+ void __iomem *offset;
+ int ss_mdump_seg_cnt;
+ int ret, i;
+
+ memcpy(&offset, &priv->minidump, sizeof(priv->minidump));
+ offset = offset + sizeof(priv->minidump->md_ss_smem_regions_baseptr);
+ /* There are 3 encryption keys which also need to be dumped */
+ ss_mdump_seg_cnt = readb_relaxed(offset) +
+ NUM_OF_ENCRYPTED_KEY;
+
+ subsys_smem_base = ioremap(__raw_readl(priv->minidump),
+ ss_mdump_seg_cnt * sizeof(*region_info));
+ region_info =
+ (struct boot_minidump_smem_region __iomem *)subsys_smem_base;
+ ramdump_segs = kcalloc(ss_mdump_seg_cnt,
+ sizeof(*ramdump_segs), GFP_KERNEL);
+ if (!ramdump_segs)
+ return -ENOMEM;
+
+ if (desc->subsys_vmid > 0)
+ ret = pil_assign_mem_to_linux(desc, priv->region_start,
+ (priv->region_end - priv->region_start));
+
+ s = ramdump_segs;
+ for (i = 0; i < ss_mdump_seg_cnt; i++) {
+ memcpy(&offset, ®ion_info, sizeof(region_info));
+ memcpy(&s->name, ®ion_info, sizeof(region_info));
+ offset = offset + sizeof(region_info->region_name);
+ s->address = __raw_readl(offset);
+ offset = offset + sizeof(region_info->region_base_address);
+ s->size = __raw_readl(offset);
+ s++;
+ region_info++;
+ }
+ ret = do_minidump(ramdump_dev, ramdump_segs, ss_mdump_seg_cnt);
+ kfree(ramdump_segs);
+ if (ret)
+ pil_err(desc, "%s: Ramdump collection failed for subsys %s rc:%d\n",
+ __func__, desc->name, ret);
+ writel_relaxed(0, &priv->minidump->md_ss_smem_regions_baseptr);
+ writeb_relaxed(1, &priv->minidump->md_ss_ssr_cause);
+
+ if (desc->subsys_vmid > 0)
+ ret = pil_assign_mem_to_subsys(desc, priv->region_start,
+ (priv->region_end - priv->region_start));
+ return ret;
+}
+
/**
* pil_do_ramdump() - Ramdump an image
* @desc: descriptor from pil_desc_init()
@@ -151,6 +221,9 @@
int count = 0, ret;
struct ramdump_segment *ramdump_segs, *s;
+ if (priv->minidump && (__raw_readl(priv->minidump) > 0))
+ return pil_do_minidump(desc, ramdump_dev);
+
list_for_each_entry(seg, &priv->segs, list)
count++;
@@ -1011,9 +1084,10 @@
int pil_desc_init(struct pil_desc *desc)
{
struct pil_priv *priv;
- int ret;
void __iomem *addr;
+ int ret, ss_imem_offset_mdump;
char buf[sizeof(priv->info->name)];
+ struct device_node *ofnode = desc->dev->of_node;
if (WARN(desc->ops->proxy_unvote && !desc->ops->proxy_vote,
"Invalid proxy voting. Ignoring\n"))
@@ -1036,6 +1110,22 @@
strlcpy(buf, desc->name, sizeof(buf));
__iowrite32_copy(priv->info->name, buf, sizeof(buf) / 4);
}
+ if (of_property_read_u32(ofnode, "qcom,minidump-id",
+ &priv->minidump_id))
+ pr_debug("minidump-id not found for %s\n", desc->name);
+ else {
+ ss_imem_offset_mdump =
+ sizeof(struct md_ssr_ss_info) * priv->minidump_id;
+ if (pil_minidump_base) {
+ /* Add 0x4 to get start of struct md_ssr_ss_info base
+ * from struct md_ssr_toc for any subsystem,
+ * struct md_ssr_ss_info is actually the pointer
+ * of ToC in smem for any subsystem.
+ */
+ addr = pil_minidump_base + ss_imem_offset_mdump + 0x4;
+ priv->minidump = (struct md_ssr_ss_info __iomem *)addr;
+ }
+ }
ret = pil_parse_devicetree(desc);
if (ret)
@@ -1144,6 +1234,20 @@
for (i = 0; i < resource_size(&res)/sizeof(u32); i++)
writel_relaxed(0, pil_info_base + (i * sizeof(u32)));
+ np = of_find_compatible_node(NULL, NULL, "qcom,msm-imem-minidump");
+ if (!np) {
+ pr_warn("pil: failed to find qcom,msm-imem-minidump node\n");
+ goto out;
+ } else {
+ pil_minidump_base = of_iomap(np, 0);
+ if (!pil_minidump_base) {
+ pr_err("unable to map pil minidump imem offset\n");
+ goto out;
+ }
+ }
+ for (i = 0; i < sizeof(struct md_ssr_toc)/sizeof(u32); i++)
+ writel_relaxed(0, pil_minidump_base + (i * sizeof(u32)));
+ writel_relaxed(1, pil_minidump_base);
out:
return register_pm_notifier(&pil_pm_notifier);
}
@@ -1154,6 +1258,8 @@
unregister_pm_notifier(&pil_pm_notifier);
if (pil_info_base)
iounmap(pil_info_base);
+ if (pil_minidump_base)
+ iounmap(pil_minidump_base);
}
module_exit(msm_pil_exit);
diff --git a/drivers/soc/qcom/peripheral-loader.h b/drivers/soc/qcom/peripheral-loader.h
index af7249b..daa4533 100644
--- a/drivers/soc/qcom/peripheral-loader.h
+++ b/drivers/soc/qcom/peripheral-loader.h
@@ -71,6 +71,34 @@
__le32 size;
} __attribute__((__packed__));
+#define MAX_NUM_OF_SS 3
+
+/**
+ * struct md_ssr_ss_info - Info in imem about smem ToC
+ * @md_ss_smem_regions_baseptr: Start physical address of SMEM TOC
+ * @md_ss_num_of_regions: number of segments that need to be dumped
+ * @md_ss_encryption_status: status of encryption of segments
+ * @md_ss_ssr_cause: ssr cause enum
+ */
+struct md_ssr_ss_info {
+ u32 md_ss_smem_regions_baseptr;
+ u8 md_ss_num_of_regions;
+ u8 md_ss_encryption_status;
+ u8 md_ss_ssr_cause;
+ u8 reserved;
+};
+
+/**
+ * struct md_ssr_toc - Wrapper of struct md_ssr_ss_info
+ * @md_ssr_toc_init: flag to indicate to MSS SW about imem init done
+ * @md_ssr_ss: Instance of struct md_ssr_ss_info for a subsystem
+ */
+struct md_ssr_toc /* Shared IMEM ToC struct */
+{
+ u32 md_ssr_toc_init;
+ struct md_ssr_ss_info md_ssr_ss[MAX_NUM_OF_SS];
+};
+
/**
* struct pil_reset_ops - PIL operations
* @init_image: prepare an image for authentication
diff --git a/drivers/soc/qcom/pil-msa.c b/drivers/soc/qcom/pil-msa.c
index 20b9769..a3cf11d 100644
--- a/drivers/soc/qcom/pil-msa.c
+++ b/drivers/soc/qcom/pil-msa.c
@@ -80,6 +80,8 @@
#define MSS_PDC_OFFSET 8
#define MSS_PDC_MASK BIT(MSS_PDC_OFFSET)
+/* Timeout value for MBA boot when minidump is enabled */
+#define MBA_ENCRYPTION_TIMEOUT 3000
enum scm_cmd {
PAS_MEM_SETUP_CMD = 2,
};
@@ -298,7 +300,12 @@
struct device *dev = drv->desc.dev;
int ret;
u32 status;
- u64 val = is_timeout_disabled() ? 0 : pbl_mba_boot_timeout_ms * 1000;
+ u64 val;
+
+ if (of_property_read_bool(dev->of_node, "qcom,minidump-id"))
+ pbl_mba_boot_timeout_ms = MBA_ENCRYPTION_TIMEOUT;
+
+ val = is_timeout_disabled() ? 0 : pbl_mba_boot_timeout_ms * 1000;
/* Wait for PBL completion. */
ret = readl_poll_timeout(drv->rmb_base + RMB_PBL_STATUS, status,
diff --git a/drivers/soc/qcom/ramdump.c b/drivers/soc/qcom/ramdump.c
index dd77062..e4c1bb8 100644
--- a/drivers/soc/qcom/ramdump.c
+++ b/drivers/soc/qcom/ramdump.c
@@ -38,6 +38,8 @@
static DEFINE_IDA(rd_minor_id);
static bool ramdump_devnode_inited;
#define RAMDUMP_WAIT_MSECS 120000
+#define MAX_STRTBL_SIZE 512
+#define MAX_NAME_LENGTH 16
struct ramdump_device {
char name[256];
@@ -452,12 +454,143 @@
}
+static inline struct elf_shdr *elf_sheader(struct elfhdr *hdr)
+{
+ return (struct elf_shdr *)((size_t)hdr + (size_t)hdr->e_shoff);
+}
+
+static inline struct elf_shdr *elf_section(struct elfhdr *hdr, int idx)
+{
+ return &elf_sheader(hdr)[idx];
+}
+
+static inline char *elf_str_table(struct elfhdr *hdr)
+{
+ if (hdr->e_shstrndx == SHN_UNDEF)
+ return NULL;
+ return (char *)hdr + elf_section(hdr, hdr->e_shstrndx)->sh_offset;
+}
+
+static inline unsigned int set_section_name(const char *name,
+ struct elfhdr *ehdr)
+{
+ char *strtab = elf_str_table(ehdr);
+ static int strtable_idx = 1;
+ int idx, ret = 0;
+
+ idx = strtable_idx;
+ if ((strtab == NULL) || (name == NULL))
+ return 0;
+
+ ret = idx;
+ idx += strlcpy((strtab + idx), name, MAX_NAME_LENGTH);
+ strtable_idx = idx + 1;
+
+ return ret;
+}
+
+static int _do_minidump(void *handle, struct ramdump_segment *segments,
+ int nsegments)
+{
+ int ret, i;
+ struct ramdump_device *rd_dev = (struct ramdump_device *)handle;
+ struct elfhdr *ehdr;
+ struct elf_shdr *shdr;
+ unsigned long offset, strtbl_off;
+
+ if (!rd_dev->consumer_present) {
+ pr_err("Ramdump(%s): No consumers. Aborting..\n", rd_dev->name);
+ return -EPIPE;
+ }
+
+ rd_dev->segments = segments;
+ rd_dev->nsegments = nsegments;
+
+ rd_dev->elfcore_size = sizeof(*ehdr) +
+ (sizeof(*shdr) * (nsegments + 2)) + MAX_STRTBL_SIZE;
+ ehdr = kzalloc(rd_dev->elfcore_size, GFP_KERNEL);
+ rd_dev->elfcore_buf = (char *)ehdr;
+ if (!rd_dev->elfcore_buf)
+ return -ENOMEM;
+
+ memcpy(ehdr->e_ident, ELFMAG, SELFMAG);
+ ehdr->e_ident[EI_CLASS] = ELF_CLASS;
+ ehdr->e_ident[EI_DATA] = ELF_DATA;
+ ehdr->e_ident[EI_VERSION] = EV_CURRENT;
+ ehdr->e_ident[EI_OSABI] = ELF_OSABI;
+ ehdr->e_type = ET_CORE;
+ ehdr->e_machine = ELF_ARCH;
+ ehdr->e_version = EV_CURRENT;
+ ehdr->e_ehsize = sizeof(*ehdr);
+ ehdr->e_shoff = sizeof(*ehdr);
+ ehdr->e_shentsize = sizeof(*shdr);
+ ehdr->e_shstrndx = 1;
+
+
+ offset = rd_dev->elfcore_size;
+ shdr = (struct elf_shdr *)(ehdr + 1);
+ strtbl_off = sizeof(*ehdr) + sizeof(*shdr) * (nsegments + 2);
+ shdr++;
+ shdr->sh_type = SHT_STRTAB;
+ shdr->sh_offset = (elf_addr_t)strtbl_off;
+ shdr->sh_size = MAX_STRTBL_SIZE;
+ shdr->sh_entsize = 0;
+ shdr->sh_flags = 0;
+ shdr->sh_name = set_section_name("STR_TBL", ehdr);
+ shdr++;
+
+ for (i = 0; i < nsegments; i++, shdr++) {
+ /* Update elf header */
+ shdr->sh_type = SHT_PROGBITS;
+ shdr->sh_name = set_section_name(segments[i].name, ehdr);
+ shdr->sh_addr = (elf_addr_t)segments[i].address;
+ shdr->sh_size = segments[i].size;
+ shdr->sh_flags = SHF_WRITE;
+ shdr->sh_offset = offset;
+ shdr->sh_entsize = 0;
+ offset += shdr->sh_size;
+ }
+ ehdr->e_shnum = nsegments + 2;
+
+ rd_dev->data_ready = 1;
+ rd_dev->ramdump_status = -1;
+
+ reinit_completion(&rd_dev->ramdump_complete);
+
+ /* Tell userspace that the data is ready */
+ wake_up(&rd_dev->dump_wait_q);
+
+ /* Wait (with a timeout) to let the ramdump complete */
+ ret = wait_for_completion_timeout(&rd_dev->ramdump_complete,
+ msecs_to_jiffies(RAMDUMP_WAIT_MSECS));
+
+ if (!ret) {
+ pr_err("Ramdump(%s): Timed out waiting for userspace.\n",
+ rd_dev->name);
+ ret = -EPIPE;
+ } else {
+ ret = (rd_dev->ramdump_status == 0) ? 0 : -EPIPE;
+ }
+
+ rd_dev->data_ready = 0;
+ rd_dev->elfcore_size = 0;
+ kfree(rd_dev->elfcore_buf);
+ rd_dev->elfcore_buf = NULL;
+ return ret;
+}
+
int do_ramdump(void *handle, struct ramdump_segment *segments, int nsegments)
{
return _do_ramdump(handle, segments, nsegments, false);
}
EXPORT_SYMBOL(do_ramdump);
+int do_minidump(void *handle, struct ramdump_segment *segments, int nsegments)
+{
+ return _do_minidump(handle, segments, nsegments);
+}
+EXPORT_SYMBOL(do_minidump);
+
int
do_elf_ramdump(void *handle, struct ramdump_segment *segments, int nsegments)
{
diff --git a/drivers/soc/qcom/rpmh.c b/drivers/soc/qcom/rpmh.c
index f4c35ab..f7902e1 100644
--- a/drivers/soc/qcom/rpmh.c
+++ b/drivers/soc/qcom/rpmh.c
@@ -563,7 +563,7 @@
spin_lock_irqsave(&rpm->lock, flags);
for (i = 0; rpm->passthru_cache[i]; i++) {
rpm_msg = rpm->passthru_cache[i];
- ret = mbox_send_controller_data(rc->chan, &rpm_msg->msg);
+ ret = mbox_write_controller_data(rc->chan, &rpm_msg->msg);
if (ret)
goto fail;
}
@@ -762,7 +762,7 @@
rpm_msg.msg.is_control = true;
rpm_msg.msg.is_complete = false;
- return mbox_send_controller_data(rc->chan, &rpm_msg.msg);
+ return mbox_write_controller_data(rc->chan, &rpm_msg.msg);
}
EXPORT_SYMBOL(rpmh_write_control);
@@ -797,7 +797,7 @@
rpm->dirty = true;
spin_unlock_irqrestore(&rpm->lock, flags);
- return mbox_send_controller_data(rc->chan, &rpm_msg.msg);
+ return mbox_write_controller_data(rc->chan, &rpm_msg.msg);
}
EXPORT_SYMBOL(rpmh_invalidate);
@@ -886,7 +886,7 @@
rpm_msg.msg.num_payload = 1;
rpm_msg.msg.is_complete = false;
- return mbox_send_controller_data(rc->chan, &rpm_msg.msg);
+ return mbox_write_controller_data(rc->chan, &rpm_msg.msg);
}
/**
diff --git a/drivers/spi/spi-dw.c b/drivers/spi/spi-dw.c
index 27960e4..c4226c0 100644
--- a/drivers/spi/spi-dw.c
+++ b/drivers/spi/spi-dw.c
@@ -107,7 +107,10 @@
static int dw_spi_debugfs_init(struct dw_spi *dws)
{
- dws->debugfs = debugfs_create_dir("dw_spi", NULL);
+ char name[128];
+
+ snprintf(name, 128, "dw_spi-%s", dev_name(&dws->master->dev));
+ dws->debugfs = debugfs_create_dir(name, NULL);
if (!dws->debugfs)
return -ENOMEM;
diff --git a/drivers/staging/comedi/comedi_fops.c b/drivers/staging/comedi/comedi_fops.c
index a34fd5a..ec99790 100644
--- a/drivers/staging/comedi/comedi_fops.c
+++ b/drivers/staging/comedi/comedi_fops.c
@@ -2898,9 +2898,6 @@
comedi_class->dev_groups = comedi_dev_groups;
- /* XXX requires /proc interface */
- comedi_proc_init();
-
/* create devices files for legacy/manual use */
for (i = 0; i < comedi_num_legacy_minors; i++) {
struct comedi_device *dev;
@@ -2918,6 +2915,9 @@
mutex_unlock(&dev->mutex);
}
+ /* XXX requires /proc interface */
+ comedi_proc_init();
+
return 0;
}
module_init(comedi_init);
diff --git a/drivers/usb/dwc3/dwc3-omap.c b/drivers/usb/dwc3/dwc3-omap.c
index 5dd1832..35b6351 100644
--- a/drivers/usb/dwc3/dwc3-omap.c
+++ b/drivers/usb/dwc3/dwc3-omap.c
@@ -19,6 +19,7 @@
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/slab.h>
+#include <linux/irq.h>
#include <linux/interrupt.h>
#include <linux/platform_device.h>
#include <linux/platform_data/dwc3-omap.h>
@@ -511,7 +512,7 @@
/* check the DMA Status */
reg = dwc3_omap_readl(omap->base, USBOTGSS_SYSCONFIG);
-
+ irq_set_status_flags(omap->irq, IRQ_NOAUTOEN);
ret = devm_request_threaded_irq(dev, omap->irq, dwc3_omap_interrupt,
dwc3_omap_interrupt_thread, IRQF_SHARED,
"dwc3-omap", omap);
@@ -532,7 +533,7 @@
}
dwc3_omap_enable_irqs(omap);
-
+ enable_irq(omap->irq);
return 0;
err2:
@@ -553,6 +554,7 @@
extcon_unregister_notifier(omap->edev, EXTCON_USB, &omap->vbus_nb);
extcon_unregister_notifier(omap->edev, EXTCON_USB_HOST, &omap->id_nb);
dwc3_omap_disable_irqs(omap);
+ disable_irq(omap->irq);
of_platform_depopulate(omap->dev);
pm_runtime_put_sync(&pdev->dev);
pm_runtime_disable(&pdev->dev);
diff --git a/drivers/usb/gadget/function/f_hid.c b/drivers/usb/gadget/function/f_hid.c
index b0f7195..b6d4b48 100644
--- a/drivers/usb/gadget/function/f_hid.c
+++ b/drivers/usb/gadget/function/f_hid.c
@@ -582,7 +582,7 @@
}
status = usb_ep_enable(hidg->out_ep);
if (status < 0) {
- ERROR(cdev, "Enable IN endpoint FAILED!\n");
+ ERROR(cdev, "Enable OUT endpoint FAILED!\n");
goto fail;
}
hidg->out_ep->driver_data = hidg;
diff --git a/drivers/usb/phy/phy-msm-qusb-v2.c b/drivers/usb/phy/phy-msm-qusb-v2.c
index 37d904f..6103172 100644
--- a/drivers/usb/phy/phy-msm-qusb-v2.c
+++ b/drivers/usb/phy/phy-msm-qusb-v2.c
@@ -572,11 +572,15 @@
qphy->base +
qphy->phy_reg[PLL_CORE_INPUT_OVERRIDE]);
- /* enable phy auto-resume */
- writel_relaxed(0x91, qphy->base + qphy->phy_reg[TEST1]);
- /* flush the previous write before next write */
- wmb();
- writel_relaxed(0x90, qphy->base + qphy->phy_reg[TEST1]);
+ if (linestate & (LINESTATE_DP | LINESTATE_DM)) {
+ /* enable phy auto-resume */
+ writel_relaxed(0x91,
+ qphy->base + qphy->phy_reg[TEST1]);
+ /* flush the previous write before next write */
+ wmb();
+ writel_relaxed(0x90,
+ qphy->base + qphy->phy_reg[TEST1]);
+ }
dev_dbg(phy->dev, "%s: intr_mask = %x\n",
__func__, intr_mask);
diff --git a/drivers/vfio/pci/vfio_pci.c b/drivers/vfio/pci/vfio_pci.c
index 031bc08..43559be 100644
--- a/drivers/vfio/pci/vfio_pci.c
+++ b/drivers/vfio/pci/vfio_pci.c
@@ -1173,6 +1173,10 @@
return ret;
vdev->barmap[index] = pci_iomap(pdev, index, 0);
+ if (!vdev->barmap[index]) {
+ pci_release_selected_regions(pdev, 1 << index);
+ return -ENOMEM;
+ }
}
vma->vm_private_data = vdev;
diff --git a/drivers/vfio/pci/vfio_pci_rdwr.c b/drivers/vfio/pci/vfio_pci_rdwr.c
index 5ffd1d9..357243d 100644
--- a/drivers/vfio/pci/vfio_pci_rdwr.c
+++ b/drivers/vfio/pci/vfio_pci_rdwr.c
@@ -193,7 +193,10 @@
if (!vdev->has_vga)
return -EINVAL;
- switch (pos) {
+ if (pos > 0xbfffful)
+ return -EINVAL;
+
+ switch ((u32)pos) {
case 0xa0000 ... 0xbffff:
count = min(count, (size_t)(0xc0000 - pos));
iomem = ioremap_nocache(0xa0000, 0xbffff - 0xa0000 + 1);
diff --git a/drivers/video/fbdev/cobalt_lcdfb.c b/drivers/video/fbdev/cobalt_lcdfb.c
index 2d3b691..038ac69 100644
--- a/drivers/video/fbdev/cobalt_lcdfb.c
+++ b/drivers/video/fbdev/cobalt_lcdfb.c
@@ -308,6 +308,11 @@
info->screen_size = resource_size(res);
info->screen_base = devm_ioremap(&dev->dev, res->start,
info->screen_size);
+ if (!info->screen_base) {
+ framebuffer_release(info);
+ return -ENOMEM;
+ }
+
info->fbops = &cobalt_lcd_fbops;
info->fix = cobalt_lcdfb_fix;
info->fix.smem_start = res->start;
diff --git a/drivers/xen/arm-device.c b/drivers/xen/arm-device.c
index 778acf8..85dd20e 100644
--- a/drivers/xen/arm-device.c
+++ b/drivers/xen/arm-device.c
@@ -58,9 +58,13 @@
xen_pfn_t *gpfns;
xen_ulong_t *idxs;
int *errs;
- struct xen_add_to_physmap_range xatp;
for (i = 0; i < count; i++) {
+ struct xen_add_to_physmap_range xatp = {
+ .domid = DOMID_SELF,
+ .space = XENMAPSPACE_dev_mmio
+ };
+
r = &resources[i];
nr = DIV_ROUND_UP(resource_size(r), XEN_PAGE_SIZE);
if ((resource_type(r) != IORESOURCE_MEM) || (nr == 0))
@@ -87,9 +91,7 @@
idxs[j] = XEN_PFN_DOWN(r->start) + j;
}
- xatp.domid = DOMID_SELF;
xatp.size = nr;
- xatp.space = XENMAPSPACE_dev_mmio;
set_xen_guest_handle(xatp.gpfns, gpfns);
set_xen_guest_handle(xatp.idxs, idxs);
diff --git a/fs/btrfs/extent-tree.c b/fs/btrfs/extent-tree.c
index e46e7fb..14a37ff 100644
--- a/fs/btrfs/extent-tree.c
+++ b/fs/btrfs/extent-tree.c
@@ -7401,7 +7401,8 @@
spin_unlock(&cluster->refill_lock);
- down_read(&used_bg->data_rwsem);
+ /* We should only have one-level nested. */
+ down_read_nested(&used_bg->data_rwsem, SINGLE_DEPTH_NESTING);
spin_lock(&cluster->refill_lock);
if (used_bg == cluster->block_group)
diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c
index a2a014b..8a05fa7 100644
--- a/fs/btrfs/inode.c
+++ b/fs/btrfs/inode.c
@@ -7648,11 +7648,18 @@
* within our reservation, otherwise we need to adjust our inode
* counter appropriately.
*/
- if (dio_data->outstanding_extents) {
+ if (dio_data->outstanding_extents >= num_extents) {
dio_data->outstanding_extents -= num_extents;
} else {
+ /*
+ * If dio write length has been split due to no large enough
+ * contiguous space, we need to compensate our inode counter
+ * appropriately.
+ */
+ u64 num_needed = num_extents - dio_data->outstanding_extents;
+
spin_lock(&BTRFS_I(inode)->lock);
- BTRFS_I(inode)->outstanding_extents += num_extents;
+ BTRFS_I(inode)->outstanding_extents += num_needed;
spin_unlock(&BTRFS_I(inode)->lock);
}
}
diff --git a/fs/btrfs/tree-log.c b/fs/btrfs/tree-log.c
index b890045..309313b 100644
--- a/fs/btrfs/tree-log.c
+++ b/fs/btrfs/tree-log.c
@@ -37,6 +37,7 @@
*/
#define LOG_INODE_ALL 0
#define LOG_INODE_EXISTS 1
+#define LOG_OTHER_INODE 2
/*
* directory trouble cases
@@ -4623,7 +4624,7 @@
if (S_ISDIR(inode->i_mode) ||
(!test_bit(BTRFS_INODE_NEEDS_FULL_SYNC,
&BTRFS_I(inode)->runtime_flags) &&
- inode_only == LOG_INODE_EXISTS))
+ inode_only >= LOG_INODE_EXISTS))
max_key.type = BTRFS_XATTR_ITEM_KEY;
else
max_key.type = (u8)-1;
@@ -4647,7 +4648,13 @@
return ret;
}
- mutex_lock(&BTRFS_I(inode)->log_mutex);
+ if (inode_only == LOG_OTHER_INODE) {
+ inode_only = LOG_INODE_EXISTS;
+ mutex_lock_nested(&BTRFS_I(inode)->log_mutex,
+ SINGLE_DEPTH_NESTING);
+ } else {
+ mutex_lock(&BTRFS_I(inode)->log_mutex);
+ }
/*
* a brute force approach to making sure we get the most uptodate
@@ -4799,7 +4806,7 @@
* unpin it.
*/
err = btrfs_log_inode(trans, root, other_inode,
- LOG_INODE_EXISTS,
+ LOG_OTHER_INODE,
0, LLONG_MAX, ctx);
iput(other_inode);
if (err)
diff --git a/fs/dcache.c b/fs/dcache.c
index 7171f0d..227a4f9 100644
--- a/fs/dcache.c
+++ b/fs/dcache.c
@@ -277,6 +277,33 @@
return dentry->d_name.name != dentry->d_iname;
}
+void take_dentry_name_snapshot(struct name_snapshot *name, struct dentry *dentry)
+{
+ spin_lock(&dentry->d_lock);
+ if (unlikely(dname_external(dentry))) {
+ struct external_name *p = external_name(dentry);
+ atomic_inc(&p->u.count);
+ spin_unlock(&dentry->d_lock);
+ name->name = p->name;
+ } else {
+ memcpy(name->inline_name, dentry->d_iname, DNAME_INLINE_LEN);
+ spin_unlock(&dentry->d_lock);
+ name->name = name->inline_name;
+ }
+}
+EXPORT_SYMBOL(take_dentry_name_snapshot);
+
+void release_dentry_name_snapshot(struct name_snapshot *name)
+{
+ if (unlikely(name->name != name->inline_name)) {
+ struct external_name *p;
+ p = container_of(name->name, struct external_name, name[0]);
+ if (unlikely(atomic_dec_and_test(&p->u.count)))
+ kfree_rcu(p, u.head);
+ }
+}
+EXPORT_SYMBOL(release_dentry_name_snapshot);
+
static inline void __d_set_inode_and_type(struct dentry *dentry,
struct inode *inode,
unsigned type_flags)
diff --git a/fs/debugfs/inode.c b/fs/debugfs/inode.c
index 1e30f74..3d7de9f 100644
--- a/fs/debugfs/inode.c
+++ b/fs/debugfs/inode.c
@@ -730,7 +730,7 @@
{
int error;
struct dentry *dentry = NULL, *trap;
- const char *old_name;
+ struct name_snapshot old_name;
trap = lock_rename(new_dir, old_dir);
/* Source or destination directories don't exist? */
@@ -745,19 +745,19 @@
if (IS_ERR(dentry) || dentry == trap || d_really_is_positive(dentry))
goto exit;
- old_name = fsnotify_oldname_init(old_dentry->d_name.name);
+ take_dentry_name_snapshot(&old_name, old_dentry);
error = simple_rename(d_inode(old_dir), old_dentry, d_inode(new_dir),
dentry, 0);
if (error) {
- fsnotify_oldname_free(old_name);
+ release_dentry_name_snapshot(&old_name);
goto exit;
}
d_move(old_dentry, dentry);
- fsnotify_move(d_inode(old_dir), d_inode(new_dir), old_name,
+ fsnotify_move(d_inode(old_dir), d_inode(new_dir), old_name.name,
d_is_dir(old_dentry),
NULL, old_dentry);
- fsnotify_oldname_free(old_name);
+ release_dentry_name_snapshot(&old_name);
unlock_rename(new_dir, old_dir);
dput(dentry);
return old_dentry;
diff --git a/fs/jfs/acl.c b/fs/jfs/acl.c
index 7bc186f..1be45c8 100644
--- a/fs/jfs/acl.c
+++ b/fs/jfs/acl.c
@@ -77,13 +77,6 @@
switch (type) {
case ACL_TYPE_ACCESS:
ea_name = XATTR_NAME_POSIX_ACL_ACCESS;
- if (acl) {
- rc = posix_acl_update_mode(inode, &inode->i_mode, &acl);
- if (rc)
- return rc;
- inode->i_ctime = current_time(inode);
- mark_inode_dirty(inode);
- }
break;
case ACL_TYPE_DEFAULT:
ea_name = XATTR_NAME_POSIX_ACL_DEFAULT;
@@ -118,9 +111,17 @@
tid = txBegin(inode->i_sb, 0);
mutex_lock(&JFS_IP(inode)->commit_mutex);
+ if (type == ACL_TYPE_ACCESS && acl) {
+ rc = posix_acl_update_mode(inode, &inode->i_mode, &acl);
+ if (rc)
+ goto end_tx;
+ inode->i_ctime = current_time(inode);
+ mark_inode_dirty(inode);
+ }
rc = __jfs_set_acl(tid, inode, type, acl);
if (!rc)
rc = txCommit(tid, 1, &inode, 0);
+end_tx:
txEnd(tid);
mutex_unlock(&JFS_IP(inode)->commit_mutex);
return rc;
diff --git a/fs/namei.c b/fs/namei.c
index 79177c9..e10895c 100644
--- a/fs/namei.c
+++ b/fs/namei.c
@@ -4404,11 +4404,11 @@
{
int error;
bool is_dir = d_is_dir(old_dentry);
- const unsigned char *old_name;
struct inode *source = old_dentry->d_inode;
struct inode *target = new_dentry->d_inode;
bool new_is_dir = false;
unsigned max_links = new_dir->i_sb->s_max_links;
+ struct name_snapshot old_name;
/*
* Check source == target.
@@ -4459,7 +4459,7 @@
if (error)
return error;
- old_name = fsnotify_oldname_init(old_dentry->d_name.name);
+ take_dentry_name_snapshot(&old_name, old_dentry);
dget(new_dentry);
if (!is_dir || (flags & RENAME_EXCHANGE))
lock_two_nondirectories(source, target);
@@ -4514,14 +4514,14 @@
inode_unlock(target);
dput(new_dentry);
if (!error) {
- fsnotify_move(old_dir, new_dir, old_name, is_dir,
+ fsnotify_move(old_dir, new_dir, old_name.name, is_dir,
!(flags & RENAME_EXCHANGE) ? target : NULL, old_dentry);
if (flags & RENAME_EXCHANGE) {
fsnotify_move(new_dir, old_dir, old_dentry->d_name.name,
new_is_dir, NULL, new_dentry);
}
}
- fsnotify_oldname_free(old_name);
+ release_dentry_name_snapshot(&old_name);
return error;
}
diff --git a/fs/nfs/file.c b/fs/nfs/file.c
index a1de8ef..84c1cb9 100644
--- a/fs/nfs/file.c
+++ b/fs/nfs/file.c
@@ -757,7 +757,7 @@
*/
nfs_sync_mapping(filp->f_mapping);
if (!NFS_PROTO(inode)->have_delegation(inode, FMODE_READ))
- nfs_zap_mapping(inode, filp->f_mapping);
+ nfs_zap_caches(inode);
out:
return status;
}
diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c
index 401ea6e..46ca788 100644
--- a/fs/nfs/nfs4proc.c
+++ b/fs/nfs/nfs4proc.c
@@ -6419,7 +6419,7 @@
set_current_state(TASK_INTERRUPTIBLE);
spin_unlock_irqrestore(&q->lock, flags);
- freezable_schedule_timeout_interruptible(NFS4_LOCK_MAXTIMEOUT);
+ freezable_schedule_timeout(NFS4_LOCK_MAXTIMEOUT);
}
finish_wait(q, &wait);
diff --git a/fs/notify/fsnotify.c b/fs/notify/fsnotify.c
index db39de2..a64adc2 100644
--- a/fs/notify/fsnotify.c
+++ b/fs/notify/fsnotify.c
@@ -104,16 +104,20 @@
if (unlikely(!fsnotify_inode_watches_children(p_inode)))
__fsnotify_update_child_dentry_flags(p_inode);
else if (p_inode->i_fsnotify_mask & mask) {
+ struct name_snapshot name;
+
/* we are notifying a parent so come up with the new mask which
* specifies these are events which came from a child. */
mask |= FS_EVENT_ON_CHILD;
+ take_dentry_name_snapshot(&name, dentry);
if (path)
ret = fsnotify(p_inode, mask, path, FSNOTIFY_EVENT_PATH,
- dentry->d_name.name, 0);
+ name.name, 0);
else
ret = fsnotify(p_inode, mask, dentry->d_inode, FSNOTIFY_EVENT_INODE,
- dentry->d_name.name, 0);
+ name.name, 0);
+ release_dentry_name_snapshot(&name);
}
dput(parent);
diff --git a/fs/pstore/ram.c b/fs/pstore/ram.c
index bd2dc34..8e151fb 100644
--- a/fs/pstore/ram.c
+++ b/fs/pstore/ram.c
@@ -434,7 +434,7 @@
for (i = 0; i < cxt->max_dump_cnt; i++) {
cxt->przs[i] = persistent_ram_new(*paddr, cxt->record_size, 0,
&cxt->ecc_info,
- cxt->memtype);
+ cxt->memtype, 0);
if (IS_ERR(cxt->przs[i])) {
err = PTR_ERR(cxt->przs[i]);
dev_err(dev, "failed to request mem region (0x%zx@0x%llx): %d\n",
@@ -471,7 +471,8 @@
return -ENOMEM;
}
- *prz = persistent_ram_new(*paddr, sz, sig, &cxt->ecc_info, cxt->memtype);
+ *prz = persistent_ram_new(*paddr, sz, sig, &cxt->ecc_info,
+ cxt->memtype, 0);
if (IS_ERR(*prz)) {
int err = PTR_ERR(*prz);
diff --git a/fs/pstore/ram_core.c b/fs/pstore/ram_core.c
index 3975dee..e11672a 100644
--- a/fs/pstore/ram_core.c
+++ b/fs/pstore/ram_core.c
@@ -48,16 +48,15 @@
return atomic_read(&prz->buffer->start);
}
-static DEFINE_RAW_SPINLOCK(buffer_lock);
-
/* increase and wrap the start pointer, returning the old value */
static size_t buffer_start_add(struct persistent_ram_zone *prz, size_t a)
{
int old;
int new;
- unsigned long flags;
+ unsigned long flags = 0;
- raw_spin_lock_irqsave(&buffer_lock, flags);
+ if (!(prz->flags & PRZ_FLAG_NO_LOCK))
+ raw_spin_lock_irqsave(&prz->buffer_lock, flags);
old = atomic_read(&prz->buffer->start);
new = old + a;
@@ -65,7 +64,8 @@
new -= prz->buffer_size;
atomic_set(&prz->buffer->start, new);
- raw_spin_unlock_irqrestore(&buffer_lock, flags);
+ if (!(prz->flags & PRZ_FLAG_NO_LOCK))
+ raw_spin_unlock_irqrestore(&prz->buffer_lock, flags);
return old;
}
@@ -75,9 +75,10 @@
{
size_t old;
size_t new;
- unsigned long flags;
+ unsigned long flags = 0;
- raw_spin_lock_irqsave(&buffer_lock, flags);
+ if (!(prz->flags & PRZ_FLAG_NO_LOCK))
+ raw_spin_lock_irqsave(&prz->buffer_lock, flags);
old = atomic_read(&prz->buffer->size);
if (old == prz->buffer_size)
@@ -89,7 +90,8 @@
atomic_set(&prz->buffer->size, new);
exit:
- raw_spin_unlock_irqrestore(&buffer_lock, flags);
+ if (!(prz->flags & PRZ_FLAG_NO_LOCK))
+ raw_spin_unlock_irqrestore(&prz->buffer_lock, flags);
}
static void notrace persistent_ram_encode_rs8(struct persistent_ram_zone *prz,
@@ -491,6 +493,7 @@
prz->buffer->sig);
}
+ /* Rewind missing or invalid memory area. */
prz->buffer->sig = sig;
persistent_ram_zap(prz);
@@ -517,7 +520,7 @@
struct persistent_ram_zone *persistent_ram_new(phys_addr_t start, size_t size,
u32 sig, struct persistent_ram_ecc_info *ecc_info,
- unsigned int memtype)
+ unsigned int memtype, u32 flags)
{
struct persistent_ram_zone *prz;
int ret = -ENOMEM;
@@ -528,6 +531,10 @@
goto err;
}
+ /* Initialize general buffer state. */
+ raw_spin_lock_init(&prz->buffer_lock);
+ prz->flags = flags;
+
ret = persistent_ram_buffer_map(start, size, prz, memtype);
if (ret)
goto err;
diff --git a/include/dt-bindings/clock/qcom,gcc-sdm845.h b/include/dt-bindings/clock/qcom,gcc-sdm845.h
index 339d470..c8696df 100644
--- a/include/dt-bindings/clock/qcom,gcc-sdm845.h
+++ b/include/dt-bindings/clock/qcom,gcc-sdm845.h
@@ -212,6 +212,7 @@
#define GCC_VS_CTRL_CLK_SRC 194
#define GCC_VSENSOR_CLK_SRC 195
#define GPLL4 196
+#define GPLL6 197
/* GCC reset clocks */
#define GCC_MMSS_BCR 0
diff --git a/include/linux/cpuhotplug.h b/include/linux/cpuhotplug.h
index edf88bd..9ddaf05 100644
--- a/include/linux/cpuhotplug.h
+++ b/include/linux/cpuhotplug.h
@@ -102,6 +102,8 @@
CPUHP_AP_DUMMY_TIMER_STARTING,
CPUHP_AP_ARM_XEN_STARTING,
CPUHP_AP_ARM_CORESIGHT_STARTING,
+ CPUHP_AP_ARM_SAVE_RESTORE_CORESIGHT4_STARTING,
+ CPUHP_AP_ARM_MM_CORESIGHT4_STARTING,
CPUHP_AP_ARM_CORESIGHT4_STARTING,
CPUHP_AP_ARM64_ISNDEP_STARTING,
CPUHP_AP_SMPCFD_DYING,
diff --git a/include/linux/dcache.h b/include/linux/dcache.h
index 9b0477e..3d4a198 100644
--- a/include/linux/dcache.h
+++ b/include/linux/dcache.h
@@ -591,5 +591,11 @@
return d_backing_inode(d_real((struct dentry *) dentry, NULL, 0));
}
+struct name_snapshot {
+ const char *name;
+ char inline_name[DNAME_INLINE_LEN];
+};
+void take_dentry_name_snapshot(struct name_snapshot *, struct dentry *);
+void release_dentry_name_snapshot(struct name_snapshot *);
#endif /* __LINUX_DCACHE_H */
diff --git a/include/linux/fsnotify.h b/include/linux/fsnotify.h
index b8bcc05..e5f03a4d 100644
--- a/include/linux/fsnotify.h
+++ b/include/linux/fsnotify.h
@@ -293,35 +293,4 @@
}
}
-#if defined(CONFIG_FSNOTIFY) /* notify helpers */
-
-/*
- * fsnotify_oldname_init - save off the old filename before we change it
- */
-static inline const unsigned char *fsnotify_oldname_init(const unsigned char *name)
-{
- return kstrdup(name, GFP_KERNEL);
-}
-
-/*
- * fsnotify_oldname_free - free the name we got from fsnotify_oldname_init
- */
-static inline void fsnotify_oldname_free(const unsigned char *old_name)
-{
- kfree(old_name);
-}
-
-#else /* CONFIG_FSNOTIFY */
-
-static inline const char *fsnotify_oldname_init(const unsigned char *name)
-{
- return NULL;
-}
-
-static inline void fsnotify_oldname_free(const unsigned char *old_name)
-{
-}
-
-#endif /* CONFIG_FSNOTIFY */
-
#endif /* _LINUX_FS_NOTIFY_H */
diff --git a/include/linux/mailbox_client.h b/include/linux/mailbox_client.h
index 86a2dc6..073391b 100644
--- a/include/linux/mailbox_client.h
+++ b/include/linux/mailbox_client.h
@@ -44,7 +44,7 @@
const char *name);
struct mbox_chan *mbox_request_channel(struct mbox_client *cl, int index);
int mbox_send_message(struct mbox_chan *chan, void *mssg);
-int mbox_send_controller_data(struct mbox_chan *chan, void *mssg);
+int mbox_write_controller_data(struct mbox_chan *chan, void *mssg);
void mbox_client_txdone(struct mbox_chan *chan, int r); /* atomic */
bool mbox_client_peek_data(struct mbox_chan *chan); /* atomic */
void mbox_free_channel(struct mbox_chan *chan); /* may sleep */
diff --git a/include/linux/mailbox_controller.h b/include/linux/mailbox_controller.h
index 7827c68..751b354 100644
--- a/include/linux/mailbox_controller.h
+++ b/include/linux/mailbox_controller.h
@@ -24,8 +24,8 @@
* transmission of data is reported by the controller via
* mbox_chan_txdone (if it has some TX ACK irq). It must not
* sleep.
- * @send_controller_data:
- * Send data for the controller driver. This could be data to
+ * @write_controller_data:
+ * Write data for the controller driver. This could be data to
* configure the controller or data that may be cached in the
* controller and not transmitted immediately. There is no ACK
* for this request and the request is not buffered in the
@@ -54,7 +54,7 @@
*/
struct mbox_chan_ops {
int (*send_data)(struct mbox_chan *chan, void *data);
- int (*send_controller_data)(struct mbox_chan *chan, void *data);
+ int (*write_controller_data)(struct mbox_chan *chan, void *data);
int (*startup)(struct mbox_chan *chan);
void (*shutdown)(struct mbox_chan *chan);
bool (*last_tx_done)(struct mbox_chan *chan);
diff --git a/include/linux/mlx4/device.h b/include/linux/mlx4/device.h
index c9f3796..80faf44 100644
--- a/include/linux/mlx4/device.h
+++ b/include/linux/mlx4/device.h
@@ -1384,6 +1384,8 @@
int get_phv_bit(struct mlx4_dev *dev, u8 port, int *phv);
int mlx4_get_is_vlan_offload_disabled(struct mlx4_dev *dev, u8 port,
bool *vlan_offload_disabled);
+void mlx4_handle_eth_header_mcast_prio(struct mlx4_net_trans_rule_hw_ctrl *ctrl,
+ struct _rule_hw *eth_header);
int mlx4_find_cached_mac(struct mlx4_dev *dev, u8 port, u64 mac, int *idx);
int mlx4_find_cached_vlan(struct mlx4_dev *dev, u8 port, u16 vid, int *idx);
int mlx4_register_vlan(struct mlx4_dev *dev, u8 port, u16 vlan, int *index);
diff --git a/include/linux/mmzone.h b/include/linux/mmzone.h
index ed0099c9..07e1acb 100644
--- a/include/linux/mmzone.h
+++ b/include/linux/mmzone.h
@@ -158,7 +158,6 @@
NR_UNEVICTABLE, /* " " " " " */
NR_ISOLATED_ANON, /* Temporary isolated pages from anon lru */
NR_ISOLATED_FILE, /* Temporary isolated pages from file lru */
- NR_PAGES_SCANNED, /* pages scanned since last reclaim */
WORKINGSET_REFAULT,
WORKINGSET_ACTIVATE,
WORKINGSET_NODERECLAIM,
@@ -645,6 +644,8 @@
int kswapd_order;
enum zone_type kswapd_classzone_idx;
+ int kswapd_failures; /* Number of 'reclaimed == 0' runs */
+
#ifdef CONFIG_COMPACTION
int kcompactd_max_order;
enum zone_type kcompactd_classzone_idx;
diff --git a/include/linux/phy.h b/include/linux/phy.h
index 6c9b1e0..8431c8c 100644
--- a/include/linux/phy.h
+++ b/include/linux/phy.h
@@ -799,6 +799,10 @@
int genphy_suspend(struct phy_device *phydev);
int genphy_resume(struct phy_device *phydev);
int genphy_soft_reset(struct phy_device *phydev);
+static inline int genphy_no_soft_reset(struct phy_device *phydev)
+{
+ return 0;
+}
void phy_driver_unregister(struct phy_driver *drv);
void phy_drivers_unregister(struct phy_driver *drv, int n);
int phy_driver_register(struct phy_driver *new_driver, struct module *owner);
diff --git a/include/linux/pstore_ram.h b/include/linux/pstore_ram.h
index 485cc8e..cb5edd6 100644
--- a/include/linux/pstore_ram.h
+++ b/include/linux/pstore_ram.h
@@ -24,6 +24,13 @@
#include <linux/list.h>
#include <linux/types.h>
+/*
+ * Choose whether access to the RAM zone requires locking or not. If a zone
+ * can be written to from different CPUs like with ftrace for example, then
+ * PRZ_FLAG_NO_LOCK is used. For all other cases, locking is required.
+ */
+#define PRZ_FLAG_NO_LOCK BIT(0)
+
struct persistent_ram_buffer;
struct rs_control;
@@ -40,6 +47,8 @@
void *vaddr;
struct persistent_ram_buffer *buffer;
size_t buffer_size;
+ u32 flags;
+ raw_spinlock_t buffer_lock;
/* ECC correction */
char *par_buffer;
@@ -55,7 +64,7 @@
struct persistent_ram_zone *persistent_ram_new(phys_addr_t start, size_t size,
u32 sig, struct persistent_ram_ecc_info *ecc_info,
- unsigned int memtype);
+ unsigned int memtype, u32 flags);
void persistent_ram_free(struct persistent_ram_zone *prz);
void persistent_ram_zap(struct persistent_ram_zone *prz);
diff --git a/include/linux/sched.h b/include/linux/sched.h
index 0737cb6..e1345ec 100644
--- a/include/linux/sched.h
+++ b/include/linux/sched.h
@@ -1197,6 +1197,37 @@
struct sched_group;
+struct eas_stats {
+ /* select_idle_sibling() stats */
+ u64 sis_attempts;
+ u64 sis_idle;
+ u64 sis_cache_affine;
+ u64 sis_suff_cap;
+ u64 sis_idle_cpu;
+ u64 sis_count;
+
+ /* select_energy_cpu_brute() stats */
+ u64 secb_attempts;
+ u64 secb_sync;
+ u64 secb_idle_bt;
+ u64 secb_insuff_cap;
+ u64 secb_no_nrg_sav;
+ u64 secb_nrg_sav;
+ u64 secb_count;
+
+ /* find_best_target() stats */
+ u64 fbt_attempts;
+ u64 fbt_no_cpu;
+ u64 fbt_no_sd;
+ u64 fbt_pref_idle;
+ u64 fbt_count;
+
+ /* cas */
+ /* select_task_rq_fair() stats */
+ u64 cas_attempts;
+ u64 cas_count;
+};
+
struct sched_domain_shared {
atomic_t ref;
atomic_t nr_busy_cpus;
@@ -1265,6 +1296,8 @@
unsigned int ttwu_wake_remote;
unsigned int ttwu_move_affine;
unsigned int ttwu_move_balance;
+
+ struct eas_stats eas_stats;
#endif
#ifdef CONFIG_SCHED_DEBUG
char *name;
@@ -1463,6 +1496,35 @@
u64 nr_wakeups_affine_attempts;
u64 nr_wakeups_passive;
u64 nr_wakeups_idle;
+
+ /* select_idle_sibling() */
+ u64 nr_wakeups_sis_attempts;
+ u64 nr_wakeups_sis_idle;
+ u64 nr_wakeups_sis_cache_affine;
+ u64 nr_wakeups_sis_suff_cap;
+ u64 nr_wakeups_sis_idle_cpu;
+ u64 nr_wakeups_sis_count;
+
+ /* energy_aware_wake_cpu() */
+ u64 nr_wakeups_secb_attempts;
+ u64 nr_wakeups_secb_sync;
+ u64 nr_wakeups_secb_idle_bt;
+ u64 nr_wakeups_secb_insuff_cap;
+ u64 nr_wakeups_secb_no_nrg_sav;
+ u64 nr_wakeups_secb_nrg_sav;
+ u64 nr_wakeups_secb_count;
+
+ /* find_best_target() */
+ u64 nr_wakeups_fbt_attempts;
+ u64 nr_wakeups_fbt_no_cpu;
+ u64 nr_wakeups_fbt_no_sd;
+ u64 nr_wakeups_fbt_pref_idle;
+ u64 nr_wakeups_fbt_count;
+
+ /* cas */
+ /* select_task_rq_fair() */
+ u64 nr_wakeups_cas_attempts;
+ u64 nr_wakeups_cas_count;
};
#endif
diff --git a/include/linux/tick.h b/include/linux/tick.h
index 513cd4fd..fc72465 100644
--- a/include/linux/tick.h
+++ b/include/linux/tick.h
@@ -119,6 +119,7 @@
extern void tick_nohz_idle_exit(void);
extern void tick_nohz_irq_exit(void);
extern ktime_t tick_nohz_get_sleep_length(void);
+extern unsigned long tick_nohz_get_idle_calls(void);
extern u64 get_cpu_idle_time_us(int cpu, u64 *last_update_time);
extern u64 get_cpu_iowait_time_us(int cpu, u64 *last_update_time);
#else /* !CONFIG_NO_HZ_COMMON */
diff --git a/include/net/fib_rules.h b/include/net/fib_rules.h
index 261202f..8dbfdf7 100644
--- a/include/net/fib_rules.h
+++ b/include/net/fib_rules.h
@@ -95,8 +95,6 @@
[FRA_FWMARK] = { .type = NLA_U32 }, \
[FRA_FWMASK] = { .type = NLA_U32 }, \
[FRA_TABLE] = { .type = NLA_U32 }, \
- [FRA_UID_START] = { .type = NLA_U32 }, \
- [FRA_UID_END] = { .type = NLA_U32 }, \
[FRA_SUPPRESS_PREFIXLEN] = { .type = NLA_U32 }, \
[FRA_SUPPRESS_IFGROUP] = { .type = NLA_U32 }, \
[FRA_GOTO] = { .type = NLA_U32 }, \
diff --git a/include/soc/qcom/jtag.h b/include/soc/qcom/jtag.h
index 5719e05..dff601d 100644
--- a/include/soc/qcom/jtag.h
+++ b/include/soc/qcom/jtag.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012-2015, 2017 The Linux Foundation. All rights reserved.
+/* Copyright (c) 2012-2015, 2017. The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
diff --git a/include/soc/qcom/ramdump.h b/include/soc/qcom/ramdump.h
index 50a17c8..4e23ccf 100644
--- a/include/soc/qcom/ramdump.h
+++ b/include/soc/qcom/ramdump.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2011-2014, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2011-2014, 2017 The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -16,6 +16,7 @@
struct device;
struct ramdump_segment {
+ char *name;
unsigned long address;
void *v_address;
unsigned long size;
@@ -28,6 +29,8 @@
int nsegments);
extern int do_elf_ramdump(void *handle, struct ramdump_segment *segments,
int nsegments);
+extern int do_minidump(void *handle, struct ramdump_segment *segments,
+ int nsegments);
#else
static inline void *create_ramdump_device(const char *dev_name,
diff --git a/include/trace/events/vmscan.h b/include/trace/events/vmscan.h
index c88fd09..2f8536b 100644
--- a/include/trace/events/vmscan.h
+++ b/include/trace/events/vmscan.h
@@ -269,23 +269,24 @@
__entry->retval)
);
-DECLARE_EVENT_CLASS(mm_vmscan_lru_isolate_template,
-
+TRACE_EVENT(mm_vmscan_lru_isolate,
TP_PROTO(int classzone_idx,
int order,
unsigned long nr_requested,
unsigned long nr_scanned,
+ unsigned long nr_skipped,
unsigned long nr_taken,
isolate_mode_t isolate_mode,
int file),
- TP_ARGS(classzone_idx, order, nr_requested, nr_scanned, nr_taken, isolate_mode, file),
+ TP_ARGS(classzone_idx, order, nr_requested, nr_scanned, nr_skipped, nr_taken, isolate_mode, file),
TP_STRUCT__entry(
__field(int, classzone_idx)
__field(int, order)
__field(unsigned long, nr_requested)
__field(unsigned long, nr_scanned)
+ __field(unsigned long, nr_skipped)
__field(unsigned long, nr_taken)
__field(isolate_mode_t, isolate_mode)
__field(int, file)
@@ -296,49 +297,23 @@
__entry->order = order;
__entry->nr_requested = nr_requested;
__entry->nr_scanned = nr_scanned;
+ __entry->nr_skipped = nr_skipped;
__entry->nr_taken = nr_taken;
__entry->isolate_mode = isolate_mode;
__entry->file = file;
),
- TP_printk("isolate_mode=%d classzone=%d order=%d nr_requested=%lu nr_scanned=%lu nr_taken=%lu file=%d",
+ TP_printk("isolate_mode=%d classzone=%d order=%d nr_requested=%lu nr_scanned=%lu nr_skipped=%lu nr_taken=%lu file=%d",
__entry->isolate_mode,
__entry->classzone_idx,
__entry->order,
__entry->nr_requested,
__entry->nr_scanned,
+ __entry->nr_skipped,
__entry->nr_taken,
__entry->file)
);
-DEFINE_EVENT(mm_vmscan_lru_isolate_template, mm_vmscan_lru_isolate,
-
- TP_PROTO(int classzone_idx,
- int order,
- unsigned long nr_requested,
- unsigned long nr_scanned,
- unsigned long nr_taken,
- isolate_mode_t isolate_mode,
- int file),
-
- TP_ARGS(classzone_idx, order, nr_requested, nr_scanned, nr_taken, isolate_mode, file)
-
-);
-
-DEFINE_EVENT(mm_vmscan_lru_isolate_template, mm_vmscan_memcg_isolate,
-
- TP_PROTO(int classzone_idx,
- int order,
- unsigned long nr_requested,
- unsigned long nr_scanned,
- unsigned long nr_taken,
- isolate_mode_t isolate_mode,
- int file),
-
- TP_ARGS(classzone_idx, order, nr_requested, nr_scanned, nr_taken, isolate_mode, file)
-
-);
-
TRACE_EVENT(mm_vmscan_writepage,
TP_PROTO(struct page *page),
diff --git a/include/uapi/linux/fib_rules.h b/include/uapi/linux/fib_rules.h
index a66c4ba..bbf02a6 100644
--- a/include/uapi/linux/fib_rules.h
+++ b/include/uapi/linux/fib_rules.h
@@ -54,8 +54,6 @@
FRA_TABLE, /* Extended table id */
FRA_FWMASK, /* mask for netfilter mark */
FRA_OIFNAME,
- FRA_UID_START, /* UID range */
- FRA_UID_END,
FRA_PAD,
FRA_L3MDEV, /* iif or oif is l3mdev goto its table */
FRA_UID_RANGE, /* UID range */
diff --git a/include/uapi/media/cam_defs.h b/include/uapi/media/cam_defs.h
index fdc56ee..9a767dd 100644
--- a/include/uapi/media/cam_defs.h
+++ b/include/uapi/media/cam_defs.h
@@ -27,6 +27,18 @@
#define CAM_GENERIC_BLOB_CMDBUFFER_TYPE_MASK 0xFF
#define CAM_GENERIC_BLOB_CMDBUFFER_TYPE_SHIFT 0
+/* Command Buffer Types */
+#define CAM_CMD_BUF_DMI 0x1
+#define CAM_CMD_BUF_DMI16 0x2
+#define CAM_CMD_BUF_DMI32 0x3
+#define CAM_CMD_BUF_DMI64 0x4
+#define CAM_CMD_BUF_DIRECT 0x5
+#define CAM_CMD_BUF_INDIRECT 0x6
+#define CAM_CMD_BUF_I2C 0x7
+#define CAM_CMD_BUF_FW 0x8
+#define CAM_CMD_BUF_GENERIC 0x9
+#define CAM_CMD_BUF_LEGACY 0xA
+
/**
* struct cam_control - Structure used by ioctl control for camera
*
diff --git a/include/uapi/media/cam_icp.h b/include/uapi/media/cam_icp.h
index 9351d2d..cd2d2d2 100644
--- a/include/uapi/media/cam_icp.h
+++ b/include/uapi/media/cam_icp.h
@@ -56,6 +56,29 @@
#define CAM_ICP_BPS_IO_IMAGES_MAX 0x9
+/* Command meta types */
+#define CAM_ICP_CMD_META_GENERIC_BLOB 0x1
+
+/* Generic blon types */
+#define CAM_ICP_CMD_GENERIC_BLOB_CLK 0x1
+
+/**
+ * struct cam_icp_clk_bw_request
+ *
+ * @budget_ns: Time required to process frame
+ * @frame_cycles: Frame cycles needed to process the frame
+ * @rt_flag: Flag to indicate real time stream
+ * @uncompressed_bw: Bandwidth required to process frame
+ * @compressed_bw: Compressed bandwidth to process frame
+ */
+struct cam_icp_clk_bw_request {
+ uint64_t budget_ns;
+ uint32_t frame_cycles;
+ uint32_t rt_flag;
+ uint64_t uncompressed_bw;
+ uint64_t compressed_bw;
+};
+
/**
* struct cam_icp_dev_ver - Device information for particular hw type
*
diff --git a/kernel/cpu.c b/kernel/cpu.c
index 7e3dfa6..69dc428 100644
--- a/kernel/cpu.c
+++ b/kernel/cpu.c
@@ -411,13 +411,16 @@
}
static void __cpuhp_kick_ap_work(struct cpuhp_cpu_state *st);
+static void __cpuhp_kick_ap_work(struct cpuhp_cpu_state *st);
+
static int bringup_wait_for_ap(unsigned int cpu)
{
struct cpuhp_cpu_state *st = per_cpu_ptr(&cpuhp_state, cpu);
/* Wait for the CPU to reach CPUHP_AP_ONLINE_IDLE */
wait_for_completion(&st->done);
- BUG_ON(!cpu_online(cpu));
+ if (WARN_ON_ONCE((!cpu_online(cpu))))
+ return -ECANCELED;
/* Unpark the stopper thread and the hotplug thread of the target cpu */
stop_machine_unpark(cpu);
diff --git a/kernel/sched/core.c b/kernel/sched/core.c
index a836ad6..4573d45 100644
--- a/kernel/sched/core.c
+++ b/kernel/sched/core.c
@@ -3238,9 +3238,10 @@
return total += scr->dl;
}
+unsigned long boosted_cpu_util(int cpu);
static void sched_freq_tick_pelt(int cpu)
{
- unsigned long cpu_utilization = capacity_max;
+ unsigned long cpu_utilization = boosted_cpu_util(cpu);
unsigned long capacity_curr = capacity_curr_of(cpu);
struct sched_capacity_reqs *scr;
@@ -6222,9 +6223,6 @@
if (!(sd->flags & SD_LOAD_BALANCE)) {
printk("does not load-balance\n");
- if (sd->parent)
- printk(KERN_ERR "ERROR: !SD_LOAD_BALANCE domain"
- " has parent");
return -1;
}
@@ -6319,8 +6317,12 @@
static int sd_degenerate(struct sched_domain *sd)
{
- if (cpumask_weight(sched_domain_span(sd)) == 1)
- return 1;
+ if (cpumask_weight(sched_domain_span(sd)) == 1) {
+ if (sd->groups->sge)
+ sd->flags &= ~SD_LOAD_BALANCE;
+ else
+ return 1;
+ }
/* Following flags need at least 2 groups */
if (sd->flags & (SD_LOAD_BALANCE |
@@ -6366,6 +6368,10 @@
SD_PREFER_SIBLING |
SD_SHARE_POWERDOMAIN |
SD_SHARE_CAP_STATES);
+ if (parent->groups->sge) {
+ parent->flags &= ~SD_LOAD_BALANCE;
+ return 0;
+ }
if (nr_node_ids == 1)
pflags &= ~SD_SERIALIZE;
}
@@ -6446,6 +6452,9 @@
goto free_rto_mask;
init_max_cpu_capacity(&rd->max_cpu_capacity);
+
+ rd->max_cap_orig_cpu = rd->min_cap_orig_cpu = -1;
+
return 0;
free_rto_mask:
@@ -6790,6 +6799,7 @@
*/
sg->sgc->capacity = SCHED_CAPACITY_SCALE * cpumask_weight(sg_span);
sg->sgc->max_capacity = SCHED_CAPACITY_SCALE;
+ sg->sgc->min_capacity = SCHED_CAPACITY_SCALE;
/*
* Make sure the first group of this domain contains the
@@ -7668,7 +7678,6 @@
enum s_alloc alloc_state;
struct sched_domain *sd;
struct s_data d;
- struct rq *rq = NULL;
int i, ret = -ENOMEM;
alloc_state = __visit_domain_allocation_hell(&d, cpu_map);
@@ -7686,8 +7695,6 @@
*per_cpu_ptr(d.sd, i) = sd;
if (tl->flags & SDTL_OVERLAP || sched_feat(FORCE_SD_OVERLAP))
sd->flags |= SD_OVERLAP;
- if (cpumask_equal(cpu_map, sched_domain_span(sd)))
- break;
}
}
@@ -7722,8 +7729,19 @@
/* Attach the domains */
rcu_read_lock();
for_each_cpu(i, cpu_map) {
- rq = cpu_rq(i);
+ int max_cpu = READ_ONCE(d.rd->max_cap_orig_cpu);
+ int min_cpu = READ_ONCE(d.rd->min_cap_orig_cpu);
+
+ if ((max_cpu < 0) || (cpu_rq(i)->cpu_capacity_orig >
+ cpu_rq(max_cpu)->cpu_capacity_orig))
+ WRITE_ONCE(d.rd->max_cap_orig_cpu, i);
+
+ if ((min_cpu < 0) || (cpu_rq(i)->cpu_capacity_orig <
+ cpu_rq(min_cpu)->cpu_capacity_orig))
+ WRITE_ONCE(d.rd->min_cap_orig_cpu, i);
+
sd = *per_cpu_ptr(d.sd, i);
+
cpu_attach_domain(sd, d.rd, i);
}
rcu_read_unlock();
@@ -8260,6 +8278,7 @@
#ifdef CONFIG_FAIR_GROUP_SCHED
root_task_group.shares = ROOT_TASK_GROUP_LOAD;
INIT_LIST_HEAD(&rq->leaf_cfs_rq_list);
+ rq->tmp_alone_branch = &rq->leaf_cfs_rq_list;
/*
* How much cpu bandwidth does root_task_group get?
*
@@ -9081,11 +9100,20 @@
if (IS_ERR(tg))
return ERR_PTR(-ENOMEM);
- sched_online_group(tg, parent);
-
return &tg->css;
}
+/* Expose task group only after completing cgroup initialization */
+static int cpu_cgroup_css_online(struct cgroup_subsys_state *css)
+{
+ struct task_group *tg = css_tg(css);
+ struct task_group *parent = css_tg(css->parent);
+
+ if (parent)
+ sched_online_group(tg, parent);
+ return 0;
+}
+
static void cpu_cgroup_css_released(struct cgroup_subsys_state *css)
{
struct task_group *tg = css_tg(css);
@@ -9488,6 +9516,7 @@
struct cgroup_subsys cpu_cgrp_subsys = {
.css_alloc = cpu_cgroup_css_alloc,
+ .css_online = cpu_cgroup_css_online,
.css_released = cpu_cgroup_css_released,
.css_free = cpu_cgroup_css_free,
.fork = cpu_cgroup_fork,
diff --git a/kernel/sched/cpufreq_sched.c b/kernel/sched/cpufreq_sched.c
index 11b75e3..843eaa7 100644
--- a/kernel/sched/cpufreq_sched.c
+++ b/kernel/sched/cpufreq_sched.c
@@ -32,6 +32,12 @@
static DEFINE_PER_CPU(unsigned long, enabled);
DEFINE_PER_CPU(struct sched_capacity_reqs, cpu_sched_capacity_reqs);
+struct gov_tunables {
+ struct gov_attr_set attr_set;
+ unsigned int up_throttle_nsec;
+ unsigned int down_throttle_nsec;
+};
+
/**
* gov_data - per-policy data internal to the governor
* @up_throttle: next throttling period expiry if increasing OPP
@@ -53,8 +59,8 @@
struct gov_data {
ktime_t up_throttle;
ktime_t down_throttle;
- unsigned int up_throttle_nsec;
- unsigned int down_throttle_nsec;
+ struct gov_tunables *tunables;
+ struct list_head tunables_hook;
struct task_struct *task;
struct irq_work irq_work;
unsigned int requested_freq;
@@ -71,8 +77,10 @@
__cpufreq_driver_target(policy, freq, CPUFREQ_RELATION_L);
- gd->up_throttle = ktime_add_ns(ktime_get(), gd->up_throttle_nsec);
- gd->down_throttle = ktime_add_ns(ktime_get(), gd->down_throttle_nsec);
+ gd->up_throttle = ktime_add_ns(ktime_get(),
+ gd->tunables->up_throttle_nsec);
+ gd->down_throttle = ktime_add_ns(ktime_get(),
+ gd->tunables->down_throttle_nsec);
up_write(&policy->rwsem);
}
@@ -263,12 +271,70 @@
static_key_slow_dec(&__sched_freq);
}
-static struct attribute_group sched_attr_group_gov_pol;
-static struct attribute_group *get_sysfs_attr(void)
+/* Tunables */
+static struct gov_tunables *global_tunables;
+
+static inline struct gov_tunables *to_tunables(struct gov_attr_set *attr_set)
{
- return &sched_attr_group_gov_pol;
+ return container_of(attr_set, struct gov_tunables, attr_set);
}
+static ssize_t up_throttle_nsec_show(struct gov_attr_set *attr_set, char *buf)
+{
+ struct gov_tunables *tunables = to_tunables(attr_set);
+
+ return sprintf(buf, "%u\n", tunables->up_throttle_nsec);
+}
+
+static ssize_t up_throttle_nsec_store(struct gov_attr_set *attr_set,
+ const char *buf, size_t count)
+{
+ struct gov_tunables *tunables = to_tunables(attr_set);
+ int ret;
+ long unsigned int val;
+
+ ret = kstrtoul(buf, 0, &val);
+ if (ret < 0)
+ return ret;
+ tunables->up_throttle_nsec = val;
+ return count;
+}
+
+static ssize_t down_throttle_nsec_show(struct gov_attr_set *attr_set, char *buf)
+{
+ struct gov_tunables *tunables = to_tunables(attr_set);
+
+ return sprintf(buf, "%u\n", tunables->down_throttle_nsec);
+}
+
+static ssize_t down_throttle_nsec_store(struct gov_attr_set *attr_set,
+ const char *buf, size_t count)
+{
+ struct gov_tunables *tunables = to_tunables(attr_set);
+ int ret;
+ long unsigned int val;
+
+ ret = kstrtoul(buf, 0, &val);
+ if (ret < 0)
+ return ret;
+ tunables->down_throttle_nsec = val;
+ return count;
+}
+
+static struct governor_attr up_throttle_nsec = __ATTR_RW(up_throttle_nsec);
+static struct governor_attr down_throttle_nsec = __ATTR_RW(down_throttle_nsec);
+
+static struct attribute *schedfreq_attributes[] = {
+ &up_throttle_nsec.attr,
+ &down_throttle_nsec.attr,
+ NULL
+};
+
+static struct kobj_type tunables_ktype = {
+ .default_attrs = schedfreq_attributes,
+ .sysfs_ops = &governor_sysfs_ops,
+};
+
static int cpufreq_sched_policy_init(struct cpufreq_policy *policy)
{
struct gov_data *gd;
@@ -283,17 +349,39 @@
if (!gd)
return -ENOMEM;
- gd->up_throttle_nsec = policy->cpuinfo.transition_latency ?
- policy->cpuinfo.transition_latency :
- THROTTLE_UP_NSEC;
- gd->down_throttle_nsec = THROTTLE_DOWN_NSEC;
- pr_debug("%s: throttle threshold = %u [ns]\n",
- __func__, gd->up_throttle_nsec);
+ policy->governor_data = gd;
- rc = sysfs_create_group(&policy->kobj, get_sysfs_attr());
- if (rc) {
- pr_err("%s: couldn't create sysfs attributes: %d\n", __func__, rc);
- goto err;
+ if (!global_tunables) {
+ gd->tunables = kzalloc(sizeof(*gd->tunables), GFP_KERNEL);
+ if (!gd->tunables)
+ goto free_gd;
+
+ gd->tunables->up_throttle_nsec =
+ policy->cpuinfo.transition_latency ?
+ policy->cpuinfo.transition_latency :
+ THROTTLE_UP_NSEC;
+ gd->tunables->down_throttle_nsec =
+ THROTTLE_DOWN_NSEC;
+
+ rc = kobject_init_and_add(&gd->tunables->attr_set.kobj,
+ &tunables_ktype,
+ get_governor_parent_kobj(policy),
+ "%s", cpufreq_gov_sched.name);
+ if (rc)
+ goto free_tunables;
+
+ gov_attr_set_init(&gd->tunables->attr_set,
+ &gd->tunables_hook);
+
+ pr_debug("%s: throttle_threshold = %u [ns]\n",
+ __func__, gd->tunables->up_throttle_nsec);
+
+ if (!have_governor_per_policy())
+ global_tunables = gd->tunables;
+ } else {
+ gd->tunables = global_tunables;
+ gov_attr_set_get(&global_tunables->attr_set,
+ &gd->tunables_hook);
}
policy->governor_data = gd;
@@ -305,7 +393,7 @@
if (IS_ERR_OR_NULL(gd->task)) {
pr_err("%s: failed to create kschedfreq thread\n",
__func__);
- goto err;
+ goto free_tunables;
}
get_task_struct(gd->task);
kthread_bind_mask(gd->task, policy->related_cpus);
@@ -317,7 +405,9 @@
return 0;
-err:
+free_tunables:
+ kfree(gd->tunables);
+free_gd:
policy->governor_data = NULL;
kfree(gd);
return -ENOMEM;
@@ -325,6 +415,7 @@
static void cpufreq_sched_policy_exit(struct cpufreq_policy *policy)
{
+ unsigned int count;
struct gov_data *gd = policy->governor_data;
clear_sched_freq();
@@ -333,7 +424,12 @@
put_task_struct(gd->task);
}
- sysfs_remove_group(&policy->kobj, get_sysfs_attr());
+ count = gov_attr_set_put(&gd->tunables->attr_set, &gd->tunables_hook);
+ if (!count) {
+ if (!have_governor_per_policy())
+ global_tunables = NULL;
+ kfree(gd->tunables);
+ }
policy->governor_data = NULL;
@@ -373,88 +469,6 @@
per_cpu(enabled, cpu) = 0;
}
-/* Tunables */
-static ssize_t show_up_throttle_nsec(struct gov_data *gd, char *buf)
-{
- return sprintf(buf, "%u\n", gd->up_throttle_nsec);
-}
-
-static ssize_t store_up_throttle_nsec(struct gov_data *gd,
- const char *buf, size_t count)
-{
- int ret;
- long unsigned int val;
-
- ret = kstrtoul(buf, 0, &val);
- if (ret < 0)
- return ret;
- gd->up_throttle_nsec = val;
- return count;
-}
-
-static ssize_t show_down_throttle_nsec(struct gov_data *gd, char *buf)
-{
- return sprintf(buf, "%u\n", gd->down_throttle_nsec);
-}
-
-static ssize_t store_down_throttle_nsec(struct gov_data *gd,
- const char *buf, size_t count)
-{
- int ret;
- long unsigned int val;
-
- ret = kstrtoul(buf, 0, &val);
- if (ret < 0)
- return ret;
- gd->down_throttle_nsec = val;
- return count;
-}
-
-/*
- * Create show/store routines
- * - sys: One governor instance for complete SYSTEM
- * - pol: One governor instance per struct cpufreq_policy
- */
-#define show_gov_pol_sys(file_name) \
-static ssize_t show_##file_name##_gov_pol \
-(struct cpufreq_policy *policy, char *buf) \
-{ \
- return show_##file_name(policy->governor_data, buf); \
-}
-
-#define store_gov_pol_sys(file_name) \
-static ssize_t store_##file_name##_gov_pol \
-(struct cpufreq_policy *policy, const char *buf, size_t count) \
-{ \
- return store_##file_name(policy->governor_data, buf, count); \
-}
-
-#define gov_pol_attr_rw(_name) \
- static struct freq_attr _name##_gov_pol = \
- __ATTR(_name, 0644, show_##_name##_gov_pol, store_##_name##_gov_pol)
-
-#define show_store_gov_pol_sys(file_name) \
- show_gov_pol_sys(file_name); \
- store_gov_pol_sys(file_name)
-#define tunable_handlers(file_name) \
- show_gov_pol_sys(file_name); \
- store_gov_pol_sys(file_name); \
- gov_pol_attr_rw(file_name)
-
-tunable_handlers(down_throttle_nsec);
-tunable_handlers(up_throttle_nsec);
-
-/* Per policy governor instance */
-static struct attribute *sched_attributes_gov_pol[] = {
- &up_throttle_nsec_gov_pol.attr,
- &down_throttle_nsec_gov_pol.attr,
- NULL,
-};
-
-static struct attribute_group sched_attr_group_gov_pol = {
- .attrs = sched_attributes_gov_pol,
- .name = "sched",
-};
#ifndef CONFIG_CPU_FREQ_DEFAULT_GOV_SCHED
static
diff --git a/kernel/sched/cpufreq_schedutil.c b/kernel/sched/cpufreq_schedutil.c
index 8d327c3..116ffe4 100644
--- a/kernel/sched/cpufreq_schedutil.c
+++ b/kernel/sched/cpufreq_schedutil.c
@@ -17,6 +17,11 @@
#include <trace/events/power.h>
#include <linux/sched/sysctl.h>
#include "sched.h"
+#include "tune.h"
+
+#ifdef CONFIG_SCHED_WALT
+unsigned long boosted_cpu_util(int cpu);
+#endif
#define SUGOV_KTHREAD_PRIORITY 50
@@ -71,6 +76,11 @@
unsigned long max;
unsigned int flags;
unsigned int cpu;
+
+ /* The field below is for single-CPU policies only. */
+#ifdef CONFIG_NO_HZ_COMMON
+ unsigned long saved_idle_calls;
+#endif
};
static DEFINE_PER_CPU(struct sugov_cpu, sugov_cpu);
@@ -100,22 +110,20 @@
{
struct cpufreq_policy *policy = sg_policy->policy;
+ if (sg_policy->next_freq == next_freq)
+ return;
+
+ sg_policy->next_freq = next_freq;
sg_policy->last_freq_update_time = time;
if (policy->fast_switch_enabled) {
- if (sg_policy->next_freq == next_freq) {
- trace_cpu_frequency(policy->cur, smp_processor_id());
- return;
- }
- sg_policy->next_freq = next_freq;
next_freq = cpufreq_driver_fast_switch(policy, next_freq);
if (next_freq == CPUFREQ_ENTRY_INVALID)
return;
policy->cur = next_freq;
trace_cpu_frequency(next_freq, smp_processor_id());
- } else if (sg_policy->next_freq != next_freq) {
- sg_policy->next_freq = next_freq;
+ } else {
sg_policy->work_in_progress = true;
irq_work_queue(&sg_policy->irq_work);
}
@@ -171,6 +179,7 @@
*max = cfs_max;
*util = cpu_util_freq(cpu, &loadcpu->walt_load);
+ *util = boosted_cpu_util(cpu);
}
static void sugov_set_iowait_boost(struct sugov_cpu *sg_cpu, u64 time,
@@ -283,6 +292,19 @@
*util = max(*util, sg_cpu->walt_load.pl);
}
+#ifdef CONFIG_NO_HZ_COMMON
+static bool sugov_cpu_is_busy(struct sugov_cpu *sg_cpu)
+{
+ unsigned long idle_calls = tick_nohz_get_idle_calls();
+ bool ret = idle_calls == sg_cpu->saved_idle_calls;
+
+ sg_cpu->saved_idle_calls = idle_calls;
+ return ret;
+}
+#else
+static inline bool sugov_cpu_is_busy(struct sugov_cpu *sg_cpu) { return false; }
+#endif /* CONFIG_NO_HZ_COMMON */
+
static void sugov_update_single(struct update_util_data *hook, u64 time,
unsigned int flags)
{
@@ -291,6 +313,7 @@
struct cpufreq_policy *policy = sg_policy->policy;
unsigned long util, max;
unsigned int next_f;
+ bool busy;
sugov_set_iowait_boost(sg_cpu, time, flags);
sg_cpu->last_update = time;
@@ -298,6 +321,7 @@
if (!sugov_should_update_freq(sg_policy, time))
return;
+ busy = sugov_cpu_is_busy(sg_cpu);
flags &= ~SCHED_CPUFREQ_RT_DL;
if (flags & SCHED_CPUFREQ_RT_DL) {
@@ -309,35 +333,29 @@
sg_policy->policy->cur);
sugov_walt_adjust(sg_cpu, &util, &max);
next_f = get_next_freq(sg_policy, util, max);
+ /*
+ * Do not reduce the frequency if the CPU has not been idle
+ * recently, as the reduction is likely to be premature then.
+ */
+ if (busy && next_f < sg_policy->next_freq)
+ next_f = sg_policy->next_freq;
}
sugov_update_commit(sg_policy, time, next_f);
}
-static unsigned int sugov_next_freq_shared(struct sugov_cpu *sg_cpu,
- unsigned long util, unsigned long max,
- unsigned int flags)
+static unsigned int sugov_next_freq_shared(struct sugov_cpu *sg_cpu)
{
struct sugov_policy *sg_policy = sg_cpu->sg_policy;
struct cpufreq_policy *policy = sg_policy->policy;
- unsigned int max_f = policy->cpuinfo.max_freq;
u64 last_freq_update_time = sg_policy->last_freq_update_time;
+ unsigned long util = 0, max = 1;
unsigned int j;
- if (flags & SCHED_CPUFREQ_RT_DL)
- return max_f;
-
- sugov_iowait_boost(sg_cpu, &util, &max);
- sugov_walt_adjust(sg_cpu, &util, &max);
-
for_each_cpu(j, policy->cpus) {
- struct sugov_cpu *j_sg_cpu;
+ struct sugov_cpu *j_sg_cpu = &per_cpu(sugov_cpu, j);
unsigned long j_util, j_max;
s64 delta_ns;
- if (j == sg_cpu->cpu)
- continue;
-
- j_sg_cpu = &per_cpu(sugov_cpu, j);
/*
* If the CPU utilization was last updated before the previous
* frequency update and the time elapsed between the last update
@@ -351,7 +369,7 @@
continue;
}
if (j_sg_cpu->flags & SCHED_CPUFREQ_RT_DL)
- return max_f;
+ return policy->cpuinfo.max_freq;
j_util = j_sg_cpu->util;
j_max = j_sg_cpu->max;
@@ -404,7 +422,11 @@
sg_cpu->walt_load.pl, flags);
if (sugov_should_update_freq(sg_policy, time)) {
- next_f = sugov_next_freq_shared(sg_cpu, util, max, flags);
+ if (flags & SCHED_CPUFREQ_RT_DL)
+ next_f = sg_policy->policy->cpuinfo.max_freq;
+ else
+ next_f = sugov_next_freq_shared(sg_cpu);
+
sugov_update_commit(sg_policy, time, next_f);
}
diff --git a/kernel/sched/debug.c b/kernel/sched/debug.c
index 0f8c0b2..c091ca4 100644
--- a/kernel/sched/debug.c
+++ b/kernel/sched/debug.c
@@ -1008,6 +1008,33 @@
P_SCHEDSTAT(se.statistics.nr_wakeups_affine_attempts);
P_SCHEDSTAT(se.statistics.nr_wakeups_passive);
P_SCHEDSTAT(se.statistics.nr_wakeups_idle);
+ /* eas */
+ /* select_idle_sibling() */
+ P_SCHEDSTAT(se.statistics.nr_wakeups_sis_attempts);
+ P_SCHEDSTAT(se.statistics.nr_wakeups_sis_idle);
+ P_SCHEDSTAT(se.statistics.nr_wakeups_sis_cache_affine);
+ P_SCHEDSTAT(se.statistics.nr_wakeups_sis_suff_cap);
+ P_SCHEDSTAT(se.statistics.nr_wakeups_sis_idle_cpu);
+ P_SCHEDSTAT(se.statistics.nr_wakeups_sis_count);
+ /* select_energy_cpu_brute() */
+ P_SCHEDSTAT(se.statistics.nr_wakeups_secb_attempts);
+ P_SCHEDSTAT(se.statistics.nr_wakeups_secb_sync);
+ P_SCHEDSTAT(se.statistics.nr_wakeups_secb_idle_bt);
+ P_SCHEDSTAT(se.statistics.nr_wakeups_secb_insuff_cap);
+ P_SCHEDSTAT(se.statistics.nr_wakeups_secb_no_nrg_sav);
+ P_SCHEDSTAT(se.statistics.nr_wakeups_secb_nrg_sav);
+ P_SCHEDSTAT(se.statistics.nr_wakeups_secb_count);
+ /* find_best_target() */
+ P_SCHEDSTAT(se.statistics.nr_wakeups_fbt_attempts);
+ P_SCHEDSTAT(se.statistics.nr_wakeups_fbt_no_cpu);
+ P_SCHEDSTAT(se.statistics.nr_wakeups_fbt_no_sd);
+ P_SCHEDSTAT(se.statistics.nr_wakeups_fbt_pref_idle);
+ P_SCHEDSTAT(se.statistics.nr_wakeups_fbt_count);
+ /* cas */
+ /* select_task_rq_fair() */
+ P_SCHEDSTAT(se.statistics.nr_wakeups_cas_attempts);
+ P_SCHEDSTAT(se.statistics.nr_wakeups_cas_count);
+
#ifdef CONFIG_SCHED_WALT
P(ravg.demand);
#endif
diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c
index 62a29ed..fde0639 100755
--- a/kernel/sched/fair.c
+++ b/kernel/sched/fair.c
@@ -160,7 +160,7 @@
/*
* The margin used when comparing utilization with CPU capacity:
- * util * 1024 < capacity * margin
+ * util * margin < capacity * 1024
*/
unsigned int sysctl_sched_capacity_margin = 1078; /* ~5% margin */
unsigned int sysctl_sched_capacity_margin_down = 1205; /* ~15% margin */
@@ -338,19 +338,59 @@
static inline void list_add_leaf_cfs_rq(struct cfs_rq *cfs_rq)
{
if (!cfs_rq->on_list) {
+ struct rq *rq = rq_of(cfs_rq);
+ int cpu = cpu_of(rq);
/*
* Ensure we either appear before our parent (if already
* enqueued) or force our parent to appear after us when it is
- * enqueued. The fact that we always enqueue bottom-up
- * reduces this to two cases.
+ * enqueued. The fact that we always enqueue bottom-up
+ * reduces this to two cases and a special case for the root
+ * cfs_rq. Furthermore, it also means that we will always reset
+ * tmp_alone_branch either when the branch is connected
+ * to a tree or when we reach the beg of the tree
*/
if (cfs_rq->tg->parent &&
- cfs_rq->tg->parent->cfs_rq[cpu_of(rq_of(cfs_rq))]->on_list) {
- list_add_rcu(&cfs_rq->leaf_cfs_rq_list,
- &rq_of(cfs_rq)->leaf_cfs_rq_list);
- } else {
+ cfs_rq->tg->parent->cfs_rq[cpu]->on_list) {
+ /*
+ * If parent is already on the list, we add the child
+ * just before. Thanks to circular linked property of
+ * the list, this means to put the child at the tail
+ * of the list that starts by parent.
+ */
list_add_tail_rcu(&cfs_rq->leaf_cfs_rq_list,
- &rq_of(cfs_rq)->leaf_cfs_rq_list);
+ &(cfs_rq->tg->parent->cfs_rq[cpu]->leaf_cfs_rq_list));
+ /*
+ * The branch is now connected to its tree so we can
+ * reset tmp_alone_branch to the beginning of the
+ * list.
+ */
+ rq->tmp_alone_branch = &rq->leaf_cfs_rq_list;
+ } else if (!cfs_rq->tg->parent) {
+ /*
+ * cfs rq without parent should be put
+ * at the tail of the list.
+ */
+ list_add_tail_rcu(&cfs_rq->leaf_cfs_rq_list,
+ &rq->leaf_cfs_rq_list);
+ /*
+ * We have reach the beg of a tree so we can reset
+ * tmp_alone_branch to the beginning of the list.
+ */
+ rq->tmp_alone_branch = &rq->leaf_cfs_rq_list;
+ } else {
+ /*
+ * The parent has not already been added so we want to
+ * make sure that it will be put after us.
+ * tmp_alone_branch points to the beg of the branch
+ * where we will add parent.
+ */
+ list_add_rcu(&cfs_rq->leaf_cfs_rq_list,
+ rq->tmp_alone_branch);
+ /*
+ * update tmp_alone_branch to points to the new beg
+ * of the branch
+ */
+ rq->tmp_alone_branch = &cfs_rq->leaf_cfs_rq_list;
}
cfs_rq->on_list = 1;
@@ -759,9 +799,7 @@
}
static inline u64 cfs_rq_clock_task(struct cfs_rq *cfs_rq);
-static int update_cfs_rq_load_avg(u64 now, struct cfs_rq *cfs_rq, bool update_freq);
-static void update_tg_load_avg(struct cfs_rq *cfs_rq, int force);
-static void attach_entity_load_avg(struct cfs_rq *cfs_rq, struct sched_entity *se);
+static void attach_entity_cfs_rq(struct sched_entity *se);
/*
* With new tasks being created, their initial util_avgs are extrapolated
@@ -793,7 +831,6 @@
struct cfs_rq *cfs_rq = cfs_rq_of(se);
struct sched_avg *sa = &se->avg;
long cap = (long)(SCHED_CAPACITY_SCALE - cfs_rq->avg.util_avg) / 2;
- u64 now = cfs_rq_clock_task(cfs_rq);
if (cap > 0) {
if (cfs_rq->avg.util_avg != 0) {
@@ -821,14 +858,12 @@
* such that the next switched_to_fair() has the
* expected state.
*/
- se->avg.last_update_time = now;
+ se->avg.last_update_time = cfs_rq_clock_task(cfs_rq);
return;
}
}
- update_cfs_rq_load_avg(now, cfs_rq, false);
- attach_entity_load_avg(cfs_rq, se);
- update_tg_load_avg(cfs_rq, false);
+ attach_entity_cfs_rq(se);
}
#else /* !CONFIG_SMP */
@@ -2697,16 +2732,20 @@
static inline int throttled_hierarchy(struct cfs_rq *cfs_rq);
-static void update_cfs_shares(struct cfs_rq *cfs_rq)
+static void update_cfs_shares(struct sched_entity *se)
{
+ struct cfs_rq *cfs_rq = group_cfs_rq(se);
struct task_group *tg;
- struct sched_entity *se;
long shares;
- tg = cfs_rq->tg;
- se = tg->se[cpu_of(rq_of(cfs_rq))];
- if (!se || throttled_hierarchy(cfs_rq))
+ if (!cfs_rq)
return;
+
+ if (throttled_hierarchy(cfs_rq))
+ return;
+
+ tg = cfs_rq->tg;
+
#ifndef CONFIG_SMP
if (likely(se->load.weight == tg->shares))
return;
@@ -2715,8 +2754,9 @@
reweight_entity(cfs_rq_of(se), se, shares);
}
+
#else /* CONFIG_FAIR_GROUP_SCHED */
-static inline void update_cfs_shares(struct cfs_rq *cfs_rq)
+static inline void update_cfs_shares(struct sched_entity *se)
{
}
#endif /* CONFIG_FAIR_GROUP_SCHED */
@@ -2962,6 +3002,26 @@
return decayed;
}
+/*
+ * Signed add and clamp on underflow.
+ *
+ * Explicitly do a load-store to ensure the intermediate value never hits
+ * memory. This allows lockless observations without ever seeing the negative
+ * values.
+ */
+#define add_positive(_ptr, _val) do { \
+ typeof(_ptr) ptr = (_ptr); \
+ typeof(_val) val = (_val); \
+ typeof(*ptr) res, var = READ_ONCE(*ptr); \
+ \
+ res = var + val; \
+ \
+ if (val < 0 && res > var) \
+ res = 0; \
+ \
+ WRITE_ONCE(*ptr, res); \
+} while (0)
+
#ifdef CONFIG_FAIR_GROUP_SCHED
/**
* update_tg_load_avg - update the tg's load avg
@@ -3041,8 +3101,138 @@
se->avg.last_update_time = n_last_update_time;
}
}
+
+/* Take into account change of utilization of a child task group */
+static inline void
+update_tg_cfs_util(struct cfs_rq *cfs_rq, struct sched_entity *se)
+{
+ struct cfs_rq *gcfs_rq = group_cfs_rq(se);
+ long delta = gcfs_rq->avg.util_avg - se->avg.util_avg;
+
+ /* Nothing to update */
+ if (!delta)
+ return;
+
+ /* Set new sched_entity's utilization */
+ se->avg.util_avg = gcfs_rq->avg.util_avg;
+ se->avg.util_sum = se->avg.util_avg * LOAD_AVG_MAX;
+
+ /* Update parent cfs_rq utilization */
+ add_positive(&cfs_rq->avg.util_avg, delta);
+ cfs_rq->avg.util_sum = cfs_rq->avg.util_avg * LOAD_AVG_MAX;
+}
+
+/* Take into account change of load of a child task group */
+static inline void
+update_tg_cfs_load(struct cfs_rq *cfs_rq, struct sched_entity *se)
+{
+ struct cfs_rq *gcfs_rq = group_cfs_rq(se);
+ long delta, load = gcfs_rq->avg.load_avg;
+
+ /*
+ * If the load of group cfs_rq is null, the load of the
+ * sched_entity will also be null so we can skip the formula
+ */
+ if (load) {
+ long tg_load;
+
+ /* Get tg's load and ensure tg_load > 0 */
+ tg_load = atomic_long_read(&gcfs_rq->tg->load_avg) + 1;
+
+ /* Ensure tg_load >= load and updated with current load*/
+ tg_load -= gcfs_rq->tg_load_avg_contrib;
+ tg_load += load;
+
+ /*
+ * We need to compute a correction term in the case that the
+ * task group is consuming more CPU than a task of equal
+ * weight. A task with a weight equals to tg->shares will have
+ * a load less or equal to scale_load_down(tg->shares).
+ * Similarly, the sched_entities that represent the task group
+ * at parent level, can't have a load higher than
+ * scale_load_down(tg->shares). And the Sum of sched_entities'
+ * load must be <= scale_load_down(tg->shares).
+ */
+ if (tg_load > scale_load_down(gcfs_rq->tg->shares)) {
+ /* scale gcfs_rq's load into tg's shares*/
+ load *= scale_load_down(gcfs_rq->tg->shares);
+ load /= tg_load;
+ }
+ }
+
+ delta = load - se->avg.load_avg;
+
+ /* Nothing to update */
+ if (!delta)
+ return;
+
+ /* Set new sched_entity's load */
+ se->avg.load_avg = load;
+ se->avg.load_sum = se->avg.load_avg * LOAD_AVG_MAX;
+
+ /* Update parent cfs_rq load */
+ add_positive(&cfs_rq->avg.load_avg, delta);
+ cfs_rq->avg.load_sum = cfs_rq->avg.load_avg * LOAD_AVG_MAX;
+
+ /*
+ * If the sched_entity is already enqueued, we also have to update the
+ * runnable load avg.
+ */
+ if (se->on_rq) {
+ /* Update parent cfs_rq runnable_load_avg */
+ add_positive(&cfs_rq->runnable_load_avg, delta);
+ cfs_rq->runnable_load_sum = cfs_rq->runnable_load_avg * LOAD_AVG_MAX;
+ }
+}
+
+static inline void set_tg_cfs_propagate(struct cfs_rq *cfs_rq)
+{
+ cfs_rq->propagate_avg = 1;
+}
+
+static inline int test_and_clear_tg_cfs_propagate(struct sched_entity *se)
+{
+ struct cfs_rq *cfs_rq = group_cfs_rq(se);
+
+ if (!cfs_rq->propagate_avg)
+ return 0;
+
+ cfs_rq->propagate_avg = 0;
+ return 1;
+}
+
+/* Update task and its cfs_rq load average */
+static inline int propagate_entity_load_avg(struct sched_entity *se)
+{
+ struct cfs_rq *cfs_rq;
+
+ if (entity_is_task(se))
+ return 0;
+
+ if (!test_and_clear_tg_cfs_propagate(se))
+ return 0;
+
+ cfs_rq = cfs_rq_of(se);
+
+ set_tg_cfs_propagate(cfs_rq);
+
+ update_tg_cfs_util(cfs_rq, se);
+ update_tg_cfs_load(cfs_rq, se);
+
+ return 1;
+}
+
#else /* CONFIG_FAIR_GROUP_SCHED */
+
static inline void update_tg_load_avg(struct cfs_rq *cfs_rq, int force) {}
+
+static inline int propagate_entity_load_avg(struct sched_entity *se)
+{
+ return 0;
+}
+
+static inline void set_tg_cfs_propagate(struct cfs_rq *cfs_rq) {}
+
#endif /* CONFIG_FAIR_GROUP_SCHED */
static inline void cfs_rq_util_change(struct cfs_rq *cfs_rq)
@@ -3113,6 +3303,7 @@
sub_positive(&sa->load_avg, r);
sub_positive(&sa->load_sum, r * LOAD_AVG_MAX);
removed_load = 1;
+ set_tg_cfs_propagate(cfs_rq);
}
if (atomic_long_read(&cfs_rq->removed_util_avg)) {
@@ -3120,6 +3311,7 @@
sub_positive(&sa->util_avg, r);
sub_positive(&sa->util_sum, r * LOAD_AVG_MAX);
removed_util = 1;
+ set_tg_cfs_propagate(cfs_rq);
}
decayed = __update_load_avg(now, cpu_of(rq_of(cfs_rq)), sa,
@@ -3133,31 +3325,46 @@
if (update_freq && (decayed || removed_util))
cfs_rq_util_change(cfs_rq);
+ /* Trace CPU load, unless cfs_rq belongs to a non-root task_group */
+ if (cfs_rq == &rq_of(cfs_rq)->cfs)
+ trace_sched_load_avg_cpu(cpu_of(rq_of(cfs_rq)), cfs_rq);
+
return decayed || removed_load;
}
+/*
+ * Optional action to be done while updating the load average
+ */
+#define UPDATE_TG 0x1
+#define SKIP_AGE_LOAD 0x2
+
/* Update task and its cfs_rq load average */
-static inline void update_load_avg(struct sched_entity *se, int update_tg)
+static inline void update_load_avg(struct sched_entity *se, int flags)
{
struct cfs_rq *cfs_rq = cfs_rq_of(se);
u64 now = cfs_rq_clock_task(cfs_rq);
struct rq *rq = rq_of(cfs_rq);
int cpu = cpu_of(rq);
+ int decayed;
/*
* Track task load average for carrying it to new CPU after migrated, and
* track group sched_entity load average for task_h_load calc in migration
*/
- __update_load_avg(now, cpu, &se->avg,
+ if (se->avg.last_update_time && !(flags & SKIP_AGE_LOAD)) {
+ __update_load_avg(now, cpu, &se->avg,
se->on_rq * scale_load_down(se->load.weight),
cfs_rq->curr == se, NULL);
+ }
- if (update_cfs_rq_load_avg(now, cfs_rq, true) && update_tg)
+ decayed = update_cfs_rq_load_avg(now, cfs_rq, true);
+ decayed |= propagate_entity_load_avg(se);
+
+ if (decayed && (flags & UPDATE_TG))
update_tg_load_avg(cfs_rq, 0);
if (entity_is_task(se))
trace_sched_load_avg_task(task_of(se), &se->avg);
- trace_sched_load_avg_cpu(cpu, cfs_rq);
}
/**
@@ -3170,31 +3377,12 @@
*/
static void attach_entity_load_avg(struct cfs_rq *cfs_rq, struct sched_entity *se)
{
- if (!sched_feat(ATTACH_AGE_LOAD))
- goto skip_aging;
-
- /*
- * If we got migrated (either between CPUs or between cgroups) we'll
- * have aged the average right before clearing @last_update_time.
- *
- * Or we're fresh through post_init_entity_util_avg().
- */
- if (se->avg.last_update_time) {
- __update_load_avg(cfs_rq->avg.last_update_time, cpu_of(rq_of(cfs_rq)),
- &se->avg, 0, 0, NULL);
-
- /*
- * XXX: we could have just aged the entire load away if we've been
- * absent from the fair class for too long.
- */
- }
-
-skip_aging:
se->avg.last_update_time = cfs_rq->avg.last_update_time;
cfs_rq->avg.load_avg += se->avg.load_avg;
cfs_rq->avg.load_sum += se->avg.load_sum;
cfs_rq->avg.util_avg += se->avg.util_avg;
cfs_rq->avg.util_sum += se->avg.util_sum;
+ set_tg_cfs_propagate(cfs_rq);
cfs_rq_util_change(cfs_rq);
}
@@ -3209,14 +3397,12 @@
*/
static void detach_entity_load_avg(struct cfs_rq *cfs_rq, struct sched_entity *se)
{
- __update_load_avg(cfs_rq->avg.last_update_time, cpu_of(rq_of(cfs_rq)),
- &se->avg, se->on_rq * scale_load_down(se->load.weight),
- cfs_rq->curr == se, NULL);
sub_positive(&cfs_rq->avg.load_avg, se->avg.load_avg);
sub_positive(&cfs_rq->avg.load_sum, se->avg.load_sum);
sub_positive(&cfs_rq->avg.util_avg, se->avg.util_avg);
sub_positive(&cfs_rq->avg.util_sum, se->avg.util_sum);
+ set_tg_cfs_propagate(cfs_rq);
cfs_rq_util_change(cfs_rq);
}
@@ -3226,34 +3412,20 @@
enqueue_entity_load_avg(struct cfs_rq *cfs_rq, struct sched_entity *se)
{
struct sched_avg *sa = &se->avg;
- u64 now = cfs_rq_clock_task(cfs_rq);
- int migrated, decayed;
-
- migrated = !sa->last_update_time;
- if (!migrated) {
- __update_load_avg(now, cpu_of(rq_of(cfs_rq)), sa,
- se->on_rq * scale_load_down(se->load.weight),
- cfs_rq->curr == se, NULL);
- }
-
- decayed = update_cfs_rq_load_avg(now, cfs_rq, !migrated);
cfs_rq->runnable_load_avg += sa->load_avg;
cfs_rq->runnable_load_sum += sa->load_sum;
- if (migrated)
+ if (!sa->last_update_time) {
attach_entity_load_avg(cfs_rq, se);
-
- if (decayed || migrated)
update_tg_load_avg(cfs_rq, 0);
+ }
}
/* Remove the runnable load generated by se from cfs_rq's runnable load average */
static inline void
dequeue_entity_load_avg(struct cfs_rq *cfs_rq, struct sched_entity *se)
{
- update_load_avg(se, 1);
-
cfs_rq->runnable_load_avg =
max_t(long, cfs_rq->runnable_load_avg - se->avg.load_avg, 0);
cfs_rq->runnable_load_sum =
@@ -3282,13 +3454,25 @@
#endif
/*
+ * Synchronize entity load avg of dequeued entity without locking
+ * the previous rq.
+ */
+void sync_entity_load_avg(struct sched_entity *se)
+{
+ struct cfs_rq *cfs_rq = cfs_rq_of(se);
+ u64 last_update_time;
+
+ last_update_time = cfs_rq_last_update_time(cfs_rq);
+ __update_load_avg(last_update_time, cpu_of(rq_of(cfs_rq)), &se->avg, 0, 0, NULL);
+}
+
+/*
* Task first catches up with cfs_rq, and then subtract
* itself from the cfs_rq (task must be off the queue now).
*/
void remove_entity_load_avg(struct sched_entity *se)
{
struct cfs_rq *cfs_rq = cfs_rq_of(se);
- u64 last_update_time;
/*
* tasks cannot exit without having gone through wake_up_new_task() ->
@@ -3300,9 +3484,7 @@
* calls this.
*/
- last_update_time = cfs_rq_last_update_time(cfs_rq);
-
- __update_load_avg(last_update_time, cpu_of(rq_of(cfs_rq)), &se->avg, 0, 0, NULL);
+ sync_entity_load_avg(se);
atomic_long_add(se->avg.load_avg, &cfs_rq->removed_load_avg);
atomic_long_add(se->avg.util_avg, &cfs_rq->removed_util_avg);
}
@@ -3327,7 +3509,10 @@
return 0;
}
-static inline void update_load_avg(struct sched_entity *se, int not_used)
+#define UPDATE_TG 0x0
+#define SKIP_AGE_LOAD 0x0
+
+static inline void update_load_avg(struct sched_entity *se, int not_used1)
{
cpufreq_update_util(rq_of(cfs_rq_of(se)), 0);
}
@@ -3472,9 +3657,18 @@
if (renorm && !curr)
se->vruntime += cfs_rq->min_vruntime;
+ /*
+ * When enqueuing a sched_entity, we must:
+ * - Update loads to have both entity and cfs_rq synced with now.
+ * - Add its load to cfs_rq->runnable_avg
+ * - For group_entity, update its weight to reflect the new share of
+ * its group cfs_rq
+ * - Add its new weight to cfs_rq->load.weight
+ */
+ update_load_avg(se, UPDATE_TG);
enqueue_entity_load_avg(cfs_rq, se);
+ update_cfs_shares(se);
account_entity_enqueue(cfs_rq, se);
- update_cfs_shares(cfs_rq);
if (flags & ENQUEUE_WAKEUP)
place_entity(cfs_rq, se, 0);
@@ -3546,6 +3740,16 @@
* Update run-time statistics of the 'current'.
*/
update_curr(cfs_rq);
+
+ /*
+ * When dequeuing a sched_entity, we must:
+ * - Update loads to have both entity and cfs_rq synced with now.
+ * - Substract its load from the cfs_rq->runnable_avg.
+ * - Substract its previous weight from cfs_rq->load.weight.
+ * - For group entity, update its weight to reflect the new share
+ * of its group cfs_rq.
+ */
+ update_load_avg(se, UPDATE_TG);
dequeue_entity_load_avg(cfs_rq, se);
update_stats_dequeue(cfs_rq, se, flags);
@@ -3569,7 +3773,7 @@
/* return excess runtime on last dequeue */
return_cfs_rq_runtime(cfs_rq);
- update_cfs_shares(cfs_rq);
+ update_cfs_shares(se);
/*
* Now advance min_vruntime if @se was the entity holding it back,
@@ -3633,7 +3837,7 @@
*/
update_stats_wait_end(cfs_rq, se);
__dequeue_entity(cfs_rq, se);
- update_load_avg(se, 1);
+ update_load_avg(se, UPDATE_TG);
}
update_stats_curr_start(cfs_rq, se);
@@ -3751,8 +3955,8 @@
/*
* Ensure that runnable average is periodically updated.
*/
- update_load_avg(curr, 1);
- update_cfs_shares(cfs_rq);
+ update_load_avg(curr, UPDATE_TG);
+ update_cfs_shares(curr);
#ifdef CONFIG_SCHED_HRTICK
/*
@@ -4614,9 +4818,9 @@
#ifdef CONFIG_SMP
static unsigned long capacity_orig_of(int cpu);
static unsigned long cpu_util(int cpu);
-static inline unsigned long boosted_cpu_util(int cpu);
+unsigned long boosted_cpu_util(int cpu);
#else
-#define boosted_cpu_util(cpu) cpu_util(cpu)
+#define boosted_cpu_util(cpu) cpu_util_freq(cpu)
#endif
#ifdef CONFIG_SMP
@@ -4688,8 +4892,8 @@
if (cfs_rq_throttled(cfs_rq))
break;
- update_load_avg(se, 1);
- update_cfs_shares(cfs_rq);
+ update_load_avg(se, UPDATE_TG);
+ update_cfs_shares(se);
}
if (!se) {
@@ -4699,6 +4903,25 @@
#ifdef CONFIG_SMP
+ /*
+ * Update SchedTune accounting.
+ *
+ * We do it before updating the CPU capacity to ensure the
+ * boost value of the current task is accounted for in the
+ * selection of the OPP.
+ *
+ * We do it also in the case where we enqueue a throttled task;
+ * we could argue that a throttled task should not boost a CPU,
+ * however:
+ * a) properly implementing CPU boosting considering throttled
+ * tasks will increase a lot the complexity of the solution
+ * b) it's not easy to quantify the benefits introduced by
+ * such a more complex solution.
+ * Thus, for the time being we go for the simple solution and boost
+ * also for throttled RQs.
+ */
+ schedtune_enqueue_task(p, cpu_of(rq));
+
if (!se) {
if (!task_new && !rq->rd->overutilized &&
cpu_overutilized(rq->cpu)) {
@@ -4717,9 +4940,6 @@
update_capacity_of(cpu_of(rq));
}
- /* Update SchedTune accouting */
- schedtune_enqueue_task(p, cpu_of(rq));
-
#endif /* CONFIG_SMP */
hrtick_update(rq);
}
@@ -4775,8 +4995,8 @@
if (cfs_rq_throttled(cfs_rq))
break;
- update_load_avg(se, 1);
- update_cfs_shares(cfs_rq);
+ update_load_avg(se, UPDATE_TG);
+ update_cfs_shares(se);
}
if (!se) {
@@ -4786,6 +5006,15 @@
#ifdef CONFIG_SMP
+ /*
+ * Update SchedTune accounting
+ *
+ * We do it before updating the CPU capacity to ensure the
+ * boost value of the current task is accounted for in the
+ * selection of the OPP.
+ */
+ schedtune_dequeue_task(p, cpu_of(rq));
+
if (!se) {
if (rq->cfs.nr_running)
update_capacity_of(cpu_of(rq));
@@ -4793,9 +5022,6 @@
set_cfs_cpu_capacity(cpu_of(rq), false, 0);
}
- /* Update SchedTune accouting */
- schedtune_dequeue_task(p, cpu_of(rq));
-
#endif /* CONFIG_SMP */
hrtick_update(rq);
@@ -5449,9 +5675,11 @@
return idx;
}
-static int group_idle_state(struct sched_group *sg)
+static int group_idle_state(struct energy_env *eenv, struct sched_group *sg)
{
int i, state = INT_MAX;
+ int src_in_grp, dst_in_grp;
+ long grp_util = 0;
/* Find the shallowest idle state in the sched group. */
for_each_cpu(i, sched_group_cpus(sg))
@@ -5463,6 +5691,54 @@
/* Take non-cpuidle idling into account (active idle/arch_cpu_idle()) */
state++;
+ /*
+ * Try to estimate if a deeper idle state is
+ * achievable when we move the task.
+ */
+ for_each_cpu(i, sched_group_cpus(sg))
+ grp_util += cpu_util(i);
+
+ src_in_grp = cpumask_test_cpu(eenv->src_cpu, sched_group_cpus(sg));
+ dst_in_grp = cpumask_test_cpu(eenv->dst_cpu, sched_group_cpus(sg));
+ if (src_in_grp == dst_in_grp) {
+ /* both CPUs under consideration are in the same group or not in
+ * either group, migration should leave idle state the same.
+ */
+ goto end;
+ }
+ /* add or remove util as appropriate to indicate what group util
+ * will be (worst case - no concurrent execution) after moving the task
+ */
+ grp_util += src_in_grp ? -eenv->util_delta : eenv->util_delta;
+
+ if (grp_util <=
+ ((long)sg->sgc->max_capacity * (int)sg->group_weight)) {
+ /* after moving, this group is at most partly
+ * occupied, so it should have some idle time.
+ */
+ int max_idle_state_idx = sg->sge->nr_idle_states - 2;
+ int new_state = grp_util * max_idle_state_idx;
+ if (grp_util <= 0)
+ /* group will have no util, use lowest state */
+ new_state = max_idle_state_idx + 1;
+ else {
+ /* for partially idle, linearly map util to idle
+ * states, excluding the lowest one. This does not
+ * correspond to the state we expect to enter in
+ * reality, but an indication of what might happen.
+ */
+ new_state = min(max_idle_state_idx, (int)
+ (new_state / sg->sgc->max_capacity));
+ new_state = max_idle_state_idx - new_state;
+ }
+ state = new_state;
+ } else {
+ /* After moving, the group will be fully occupied
+ * so assume it will not be idle at all.
+ */
+ state = 0;
+ }
+end:
return state;
}
@@ -5499,15 +5775,7 @@
*/
sd = rcu_dereference(per_cpu(sd_scs, cpu));
- if (!sd)
- /*
- * We most probably raced with hotplug; returning a
- * wrong energy estimation is better than entering an
- * infinite loop.
- */
- return -EINVAL;
-
- if (sd->parent)
+ if (sd && sd->parent)
sg_shared_cap = sd->parent->groups;
for_each_domain(cpu, sd) {
@@ -5544,7 +5812,7 @@
}
}
- idle_idx = group_idle_state(sg);
+ idle_idx = group_idle_state(eenv, sg);
if (unlikely(idle_idx < 0))
return idle_idx;
@@ -5577,6 +5845,14 @@
} while (sg = sg->next, sg != sd->groups);
}
+
+ /*
+ * If we raced with hotplug and got an sd NULL-pointer;
+ * returning a wrong energy estimation is better than
+ * entering an infinite loop.
+ */
+ if (cpumask_test_cpu(cpu, &visit_cpus))
+ return -EINVAL;
next_cpu:
cpumask_clear_cpu(cpu, &visit_cpus);
continue;
@@ -5603,6 +5879,7 @@
struct sched_domain *sd;
struct sched_group *sg;
int sd_cpu = -1, energy_before = 0, energy_after = 0;
+ int diff, margin;
struct energy_env eenv_before = {
.util_delta = 0,
@@ -5647,12 +5924,22 @@
eenv->nrg.after = energy_after;
eenv->nrg.diff = eenv->nrg.after - eenv->nrg.before;
eenv->payoff = 0;
-
+#ifndef CONFIG_SCHED_TUNE
trace_sched_energy_diff(eenv->task,
eenv->src_cpu, eenv->dst_cpu, eenv->util_delta,
eenv->nrg.before, eenv->nrg.after, eenv->nrg.diff,
eenv->cap.before, eenv->cap.after, eenv->cap.delta,
eenv->nrg.delta, eenv->payoff);
+#endif
+ /*
+ * Dead-zone margin preventing too many migrations.
+ */
+
+ margin = eenv->nrg.before >> 6; /* ~1.56% */
+
+ diff = eenv->nrg.after - eenv->nrg.before;
+
+ eenv->nrg.diff = (abs(diff) < margin) ? 0 : eenv->nrg.diff;
return eenv->nrg.diff;
}
@@ -5660,23 +5947,30 @@
#ifdef CONFIG_SCHED_TUNE
struct target_nrg schedtune_target_nrg;
-
+extern bool schedtune_initialized;
/*
* System energy normalization
- * Returns the normalized value, in the range [0..SCHED_LOAD_SCALE],
+ * Returns the normalized value, in the range [0..SCHED_CAPACITY_SCALE],
* corresponding to the specified energy variation.
*/
static inline int
normalize_energy(int energy_diff)
{
u32 normalized_nrg;
+
+ /* during early setup, we don't know the extents */
+ if (unlikely(!schedtune_initialized))
+ return energy_diff < 0 ? -1 : 1 ;
+
#ifdef CONFIG_SCHED_DEBUG
+ {
int max_delta;
/* Check for boundaries */
max_delta = schedtune_target_nrg.max_power;
max_delta -= schedtune_target_nrg.min_power;
WARN_ON(abs(energy_diff) >= max_delta);
+ }
#endif
/* Do scaling using positive numbers to increase the range */
@@ -5695,18 +5989,13 @@
static inline int
energy_diff(struct energy_env *eenv)
{
- unsigned int boost;
+ int boost = schedtune_task_boost(eenv->task);
int nrg_delta;
/* Conpute "absolute" energy diff */
__energy_diff(eenv);
/* Return energy diff when boost margin is 0 */
-#ifdef CONFIG_CGROUP_SCHEDTUNE
- boost = schedtune_task_boost(eenv->task);
-#else
- boost = get_sysctl_sched_cfs_boost();
-#endif
if (boost == 0)
return eenv->nrg.diff;
@@ -5719,6 +6008,12 @@
eenv->cap.delta,
eenv->task);
+ trace_sched_energy_diff(eenv->task,
+ eenv->src_cpu, eenv->dst_cpu, eenv->util_delta,
+ eenv->nrg.before, eenv->nrg.after, eenv->nrg.diff,
+ eenv->cap.before, eenv->cap.after, eenv->cap.delta,
+ eenv->nrg.delta, eenv->payoff);
+
/*
* When SchedTune is enabled, the energy_diff() function will return
* the computed energy payoff value. Since the energy_diff() return
@@ -5878,6 +6173,8 @@
#ifdef CONFIG_SCHED_TUNE
+struct reciprocal_value schedtune_spc_rdiv;
+
static long
schedtune_margin(unsigned long signal, long boost)
{
@@ -5888,30 +6185,16 @@
*
* The Boost (B) value is used to compute a Margin (M) which is
* proportional to the complement of the original Signal (S):
- * M = B * (SCHED_LOAD_SCALE - S), if B is positive
- * M = B * S, if B is negative
+ * M = B * (SCHED_CAPACITY_SCALE - S)
* The obtained M could be used by the caller to "boost" S.
*/
-
if (boost >= 0) {
margin = SCHED_CAPACITY_SCALE - signal;
margin *= boost;
} else
margin = -signal * boost;
- /*
- * Fast integer division by constant:
- * Constant : (C) = 100
- * Precision : 0.1% (P) = 0.1
- * Reference : C * 100 / P (R) = 100000
- *
- * Thus:
- * Shift bits : ceil(log(R,2)) (S) = 17
- * Mult const : round(2^S/C) (M) = 1311
- *
- *
- */
- margin *= 1311;
- margin >>= 17;
+
+ margin = reciprocal_divide(margin, schedtune_spc_rdiv);
if (boost < 0)
margin *= -1;
@@ -5921,13 +6204,8 @@
static inline int
schedtune_cpu_margin(unsigned long util, int cpu)
{
- int boost;
+ int boost = schedtune_cpu_boost(cpu);
-#ifdef CONFIG_CGROUP_SCHEDTUNE
- boost = schedtune_cpu_boost(cpu);
-#else
- boost = get_sysctl_sched_cfs_boost();
-#endif
if (boost == 0)
return 0;
@@ -5937,15 +6215,10 @@
static inline long
schedtune_task_margin(struct task_struct *task)
{
- int boost;
+ int boost = schedtune_task_boost(task);
unsigned long util;
long margin;
-#ifdef CONFIG_CGROUP_SCHEDTUNE
- boost = schedtune_task_boost(task);
-#else
- boost = get_sysctl_sched_cfs_boost();
-#endif
if (boost == 0)
return 0;
@@ -5971,7 +6244,7 @@
#endif /* CONFIG_SCHED_TUNE */
-static inline unsigned long
+unsigned long
boosted_cpu_util(int cpu)
{
unsigned long util = cpu_util_freq(cpu, NULL);
@@ -6317,20 +6590,29 @@
struct sched_domain *sd;
struct sched_group *sg;
int i = task_cpu(p);
- int best_idle = -1;
- int best_idle_cstate = -1;
- int best_idle_capacity = INT_MAX;
+ int best_idle_cpu = -1;
+ int best_idle_cstate = INT_MAX;
+ unsigned long best_idle_capacity = ULONG_MAX;
+
+ schedstat_inc(p->se.statistics.nr_wakeups_sis_attempts);
+ schedstat_inc(this_rq()->eas_stats.sis_attempts);
if (!sysctl_sched_cstate_aware) {
- if (idle_cpu(target) && !cpu_isolated(target))
+ if (idle_cpu(target) && !cpu_isolated(target)) {
+ schedstat_inc(p->se.statistics.nr_wakeups_sis_idle);
+ schedstat_inc(this_rq()->eas_stats.sis_idle);
return target;
+ }
/*
* If the prevous cpu is cache affine and idle, don't be stupid.
*/
if (i != target && cpus_share_cache(i, target) &&
- idle_cpu(i) && !cpu_isolated(i))
+ idle_cpu(i) && !cpu_isolated(i)) {
+ schedstat_inc(p->se.statistics.nr_wakeups_sis_cache_affine);
+ schedstat_inc(this_rq()->eas_stats.sis_cache_affine);
return i;
+ }
sd = rcu_dereference(per_cpu(sd_llc, target));
if (!sd)
@@ -6363,8 +6645,7 @@
if (sysctl_sched_cstate_aware) {
for_each_cpu_and(i, tsk_cpus_allowed(p), sched_group_cpus(sg)) {
- struct rq *rq = cpu_rq(i);
- int idle_idx = idle_get_state_idx(rq);
+ int idle_idx = idle_get_state_idx(cpu_rq(i));
unsigned long new_usage = boosted_task_util(p);
unsigned long capacity_orig = capacity_orig_of(i);
@@ -6374,11 +6655,16 @@
if (new_usage > capacity_orig || !idle_cpu(i))
goto next;
- if (i == target && new_usage <= capacity_curr_of(target))
+ if (i == target && new_usage <= capacity_curr_of(target)) {
+ schedstat_inc(p->se.statistics.nr_wakeups_sis_suff_cap);
+ schedstat_inc(this_rq()->eas_stats.sis_suff_cap);
+ schedstat_inc(sd->eas_stats.sis_suff_cap);
return target;
+ }
- if (best_idle < 0 || (idle_idx < best_idle_cstate && capacity_orig <= best_idle_capacity)) {
- best_idle = i;
+ if (idle_idx < best_idle_cstate &&
+ capacity_orig <= best_idle_capacity) {
+ best_idle_cpu = i;
best_idle_cstate = idle_idx;
best_idle_capacity = capacity_orig;
}
@@ -6394,19 +6680,26 @@
target = cpumask_first_and(sched_group_cpus(sg),
tsk_cpus_allowed(p));
+ schedstat_inc(p->se.statistics.nr_wakeups_sis_idle_cpu);
+ schedstat_inc(this_rq()->eas_stats.sis_idle_cpu);
+ schedstat_inc(sd->eas_stats.sis_idle_cpu);
goto done;
}
next:
sg = sg->next;
} while (sg != sd->groups);
}
- if (best_idle > 0)
- target = best_idle;
+
+ if (best_idle_cpu >= 0)
+ target = best_idle_cpu;
done:
+ schedstat_inc(p->se.statistics.nr_wakeups_sis_count);
+ schedstat_inc(this_rq()->eas_stats.sis_count);
+
return target;
}
-
+
static inline int find_best_target(struct task_struct *p, bool boosted, bool prefer_idle)
{
int iter_cpu;
@@ -8046,6 +8339,10 @@
if (update_cfs_rq_load_avg(cfs_rq_clock_task(cfs_rq), cfs_rq, true))
update_tg_load_avg(cfs_rq, 0);
+
+ /* Propagate pending load changes to the parent */
+ if (cfs_rq->tg->se[cpu])
+ update_load_avg(cfs_rq->tg->se[cpu], 0);
}
raw_spin_unlock_irqrestore(&rq->lock, flags);
}
@@ -8270,7 +8567,6 @@
raw_spin_unlock_irqrestore(&mcc->lock, flags);
skip_unlock: __attribute__ ((unused));
- sdg->sgc->max_capacity = capacity;
capacity *= scale_rt_capacity(cpu);
capacity >>= SCHED_CAPACITY_SHIFT;
@@ -8280,13 +8576,15 @@
cpu_rq(cpu)->cpu_capacity = capacity;
sdg->sgc->capacity = capacity;
+ sdg->sgc->max_capacity = capacity;
+ sdg->sgc->min_capacity = capacity;
}
void update_group_capacity(struct sched_domain *sd, int cpu)
{
struct sched_domain *child = sd->child;
struct sched_group *group, *sdg = sd->groups;
- unsigned long capacity, max_capacity;
+ unsigned long capacity, max_capacity, min_capacity;
unsigned long interval;
interval = msecs_to_jiffies(sd->balance_interval);
@@ -8300,6 +8598,7 @@
capacity = 0;
max_capacity = 0;
+ min_capacity = ULONG_MAX;
if (child->flags & SD_OVERLAP) {
/*
@@ -8332,6 +8631,7 @@
}
max_capacity = max(capacity, max_capacity);
+ min_capacity = min(capacity, min_capacity);
}
} else {
/*
@@ -8348,6 +8648,7 @@
if (!cpu_isolated(cpumask_first(cpus))) {
capacity += sgc->capacity;
max_capacity = max(sgc->max_capacity, max_capacity);
+ min_capacity = min(sgc->min_capacity, min_capacity);
}
group = group->next;
} while (group != child->groups);
@@ -8355,6 +8656,7 @@
sdg->sgc->capacity = capacity;
sdg->sgc->max_capacity = max_capacity;
+ sdg->sgc->min_capacity = min_capacity;
}
/*
@@ -8598,6 +8900,16 @@
if (sgs->avg_load <= busiest->avg_load)
return false;
+ if (!(env->sd->flags & SD_ASYM_CPUCAPACITY))
+ goto asym_packing;
+
+ /*
+ * Candidate sg has no more than one task per CPU and
+ * has higher per-CPU capacity. Migrating tasks to less
+ * capable CPUs may harm throughput. Maximize throughput,
+ * power/energy consequences are not considered.
+ */
+
/*
* Candiate sg has no more than one task per cpu and has higher
* per-cpu capacity. No reason to pull tasks to less capable cpus.
@@ -8606,6 +8918,7 @@
group_smaller_cpu_capacity(sds->local, sg))
return false;
+asym_packing:
/* This is the busiest node in its class. */
if (!(env->sd->flags & SD_ASYM_PACKING))
return true;
@@ -8660,6 +8973,9 @@
}
#endif /* CONFIG_NUMA_BALANCING */
+#define lb_sd_parent(sd) \
+ (sd->parent && sd->parent->groups != sd->parent->groups->next)
+
/**
* update_sd_lb_stats - Update sched_domain's statistics for load balancing.
* @env: The load balancing environment.
@@ -8744,7 +9060,7 @@
env->src_grp_nr_running = sds->busiest_stat.sum_nr_running;
- if (!env->sd->parent) {
+ if (!lb_sd_parent(env->sd)) {
/* update overload indicator if we are at root domain */
if (env->dst_rq->rd->overload != overload)
env->dst_rq->rd->overload = overload;
@@ -10246,8 +10562,13 @@
if (time_before(now, nohz.next_balance))
return false;
+ if (rq->nr_running >= 2 &&
+ (!energy_aware() || cpu_overutilized(cpu)))
+ return true;
+
+ /* Do idle load balance if there have misfit task */
if (energy_aware())
- return rq->nr_running >= 2 && cpu_overutilized(cpu);
+ return rq->misfit_task;
rcu_read_lock();
sds = rcu_dereference(per_cpu(sd_llc_shared, cpu));
@@ -10475,11 +10796,65 @@
return false;
}
+#ifdef CONFIG_FAIR_GROUP_SCHED
+/*
+ * Propagate the changes of the sched_entity across the tg tree to make it
+ * visible to the root
+ */
+static void propagate_entity_cfs_rq(struct sched_entity *se)
+{
+ struct cfs_rq *cfs_rq;
+
+ /* Start to propagate at parent */
+ se = se->parent;
+
+ for_each_sched_entity(se) {
+ cfs_rq = cfs_rq_of(se);
+
+ if (cfs_rq_throttled(cfs_rq))
+ break;
+
+ update_load_avg(se, UPDATE_TG);
+ }
+}
+#else
+static void propagate_entity_cfs_rq(struct sched_entity *se) { }
+#endif
+
+static void detach_entity_cfs_rq(struct sched_entity *se)
+{
+ struct cfs_rq *cfs_rq = cfs_rq_of(se);
+
+ /* Catch up with the cfs_rq and remove our load when we leave */
+ update_load_avg(se, 0);
+ detach_entity_load_avg(cfs_rq, se);
+ update_tg_load_avg(cfs_rq, false);
+ propagate_entity_cfs_rq(se);
+}
+
+static void attach_entity_cfs_rq(struct sched_entity *se)
+{
+ struct cfs_rq *cfs_rq = cfs_rq_of(se);
+
+#ifdef CONFIG_FAIR_GROUP_SCHED
+ /*
+ * Since the real-depth could have been changed (only FAIR
+ * class maintain depth value), reset depth properly.
+ */
+ se->depth = se->parent ? se->parent->depth + 1 : 0;
+#endif
+
+ /* Synchronize entity with its cfs_rq */
+ update_load_avg(se, sched_feat(ATTACH_AGE_LOAD) ? 0 : SKIP_AGE_LOAD);
+ attach_entity_load_avg(cfs_rq, se);
+ update_tg_load_avg(cfs_rq, false);
+ propagate_entity_cfs_rq(se);
+}
+
static void detach_task_cfs_rq(struct task_struct *p)
{
struct sched_entity *se = &p->se;
struct cfs_rq *cfs_rq = cfs_rq_of(se);
- u64 now = cfs_rq_clock_task(cfs_rq);
if (!vruntime_normalized(p)) {
/*
@@ -10490,30 +10865,15 @@
se->vruntime -= cfs_rq->min_vruntime;
}
- /* Catch up with the cfs_rq and remove our load when we leave */
- update_cfs_rq_load_avg(now, cfs_rq, false);
- detach_entity_load_avg(cfs_rq, se);
- update_tg_load_avg(cfs_rq, false);
+ detach_entity_cfs_rq(se);
}
static void attach_task_cfs_rq(struct task_struct *p)
{
struct sched_entity *se = &p->se;
struct cfs_rq *cfs_rq = cfs_rq_of(se);
- u64 now = cfs_rq_clock_task(cfs_rq);
-#ifdef CONFIG_FAIR_GROUP_SCHED
- /*
- * Since the real-depth could have been changed (only FAIR
- * class maintain depth value), reset depth properly.
- */
- se->depth = se->parent ? se->parent->depth + 1 : 0;
-#endif
-
- /* Synchronize task with its cfs_rq */
- update_cfs_rq_load_avg(now, cfs_rq, false);
- attach_entity_load_avg(cfs_rq, se);
- update_tg_load_avg(cfs_rq, false);
+ attach_entity_cfs_rq(se);
if (!vruntime_normalized(p))
se->vruntime += cfs_rq->min_vruntime;
@@ -10567,6 +10927,9 @@
cfs_rq->min_vruntime_copy = cfs_rq->min_vruntime;
#endif
#ifdef CONFIG_SMP
+#ifdef CONFIG_FAIR_GROUP_SCHED
+ cfs_rq->propagate_avg = 0;
+#endif
atomic_long_set(&cfs_rq->removed_load_avg, 0);
atomic_long_set(&cfs_rq->removed_util_avg, 0);
#endif
@@ -10767,8 +11130,10 @@
/* Possible calls to update_curr() need rq clock */
update_rq_clock(rq);
- for_each_sched_entity(se)
- update_cfs_shares(group_cfs_rq(se));
+ for_each_sched_entity(se) {
+ update_load_avg(se, UPDATE_TG);
+ update_cfs_shares(se);
+ }
raw_spin_unlock_irqrestore(&rq->lock, flags);
}
diff --git a/kernel/sched/sched.h b/kernel/sched/sched.h
index 3df55ac..72b79bb 100644
--- a/kernel/sched/sched.h
+++ b/kernel/sched/sched.h
@@ -477,6 +477,7 @@
unsigned long runnable_load_avg;
#ifdef CONFIG_FAIR_GROUP_SCHED
unsigned long tg_load_avg_contrib;
+ unsigned long propagate_avg;
#endif
atomic_long_t removed_load_avg, removed_util_avg;
#ifndef CONFIG_64BIT
@@ -662,6 +663,9 @@
/* Maximum cpu capacity in the system. */
struct max_cpu_capacity max_cpu_capacity;
+
+ /* First cpu with maximum and minimum original capacity */
+ int max_cap_orig_cpu, min_cap_orig_cpu;
};
extern struct root_domain def_root_domain;
@@ -720,6 +724,7 @@
#ifdef CONFIG_FAIR_GROUP_SCHED
/* list of leaf cfs_rq on this cpu: */
struct list_head leaf_cfs_rq_list;
+ struct list_head *tmp_alone_branch;
#endif /* CONFIG_FAIR_GROUP_SCHED */
/*
@@ -842,6 +847,9 @@
/* try_to_wake_up() stats */
unsigned int ttwu_count;
unsigned int ttwu_local;
+#ifdef CONFIG_SMP
+ struct eas_stats eas_stats;
+#endif
#endif
#ifdef CONFIG_SMP
@@ -1012,6 +1020,7 @@
*/
unsigned long capacity;
unsigned long max_capacity; /* Max per-cpu capacity in group */
+ unsigned long min_capacity; /* Min per-CPU capacity in group */
unsigned long next_update;
int imbalance; /* XXX unrelated to capacity but shared group state */
diff --git a/kernel/sched/stats.c b/kernel/sched/stats.c
index 87e2c9f..6d74a7c 100644
--- a/kernel/sched/stats.c
+++ b/kernel/sched/stats.c
@@ -12,6 +12,28 @@
*/
#define SCHEDSTAT_VERSION 15
+#ifdef CONFIG_SMP
+static inline void show_easstat(struct seq_file *seq, struct eas_stats *stats)
+{
+ /* eas-specific runqueue stats */
+ seq_printf(seq, "eas %llu %llu %llu %llu %llu %llu ",
+ stats->sis_attempts, stats->sis_idle, stats->sis_cache_affine,
+ stats->sis_suff_cap, stats->sis_idle_cpu, stats->sis_count);
+
+ seq_printf(seq, "%llu %llu %llu %llu %llu %llu %llu ",
+ stats->secb_attempts, stats->secb_sync, stats->secb_idle_bt,
+ stats->secb_insuff_cap, stats->secb_no_nrg_sav,
+ stats->secb_nrg_sav, stats->secb_count);
+
+ seq_printf(seq, "%llu %llu %llu %llu %llu ",
+ stats->fbt_attempts, stats->fbt_no_cpu, stats->fbt_no_sd,
+ stats->fbt_pref_idle, stats->fbt_count);
+
+ seq_printf(seq, "%llu %llu\n",
+ stats->cas_attempts, stats->cas_count);
+}
+#endif
+
static int show_schedstat(struct seq_file *seq, void *v)
{
int cpu;
@@ -40,6 +62,8 @@
seq_printf(seq, "\n");
#ifdef CONFIG_SMP
+ show_easstat(seq, &rq->eas_stats);
+
/* domain-specific stats */
rcu_read_lock();
for_each_domain(cpu, sd) {
@@ -66,6 +90,8 @@
sd->sbf_count, sd->sbf_balanced, sd->sbf_pushed,
sd->ttwu_wake_remote, sd->ttwu_move_affine,
sd->ttwu_move_balance);
+
+ show_easstat(seq, &sd->eas_stats);
}
rcu_read_unlock();
#endif
diff --git a/kernel/sched/tune.c b/kernel/sched/tune.c
index 86a167b..217bafe 100644
--- a/kernel/sched/tune.c
+++ b/kernel/sched/tune.c
@@ -14,11 +14,12 @@
unsigned int sysctl_sched_cfs_boost __read_mostly;
#ifdef CONFIG_CGROUP_SCHEDTUNE
-static bool schedtune_initialized = false;
-#endif /* CONFIG_CGROUP_SCHEDTUNE */
+bool schedtune_initialized = false;
+#endif
unsigned int sysctl_sched_cfs_boost __read_mostly;
+extern struct reciprocal_value schedtune_spc_rdiv;
extern struct target_nrg schedtune_target_nrg;
/* Performance Boost region (B) threshold params */
@@ -680,6 +681,9 @@
struct schedtune *st;
int task_boost;
+ if (!unlikely(schedtune_initialized))
+ return 0;
+
/* Get task boost value */
rcu_read_lock();
st = task_schedtune(p);
@@ -694,6 +698,9 @@
struct schedtune *st;
int prefer_idle;
+ if (!unlikely(schedtune_initialized))
+ return 0;
+
/* Get prefer_idle value */
rcu_read_lock();
st = task_schedtune(p);
@@ -831,6 +838,7 @@
bg = &per_cpu(cpu_boost_groups, cpu);
bg->group[st->idx].boost = 0;
bg->group[st->idx].tasks = 0;
+ raw_spin_lock_init(&bg->lock);
}
return 0;
@@ -1126,9 +1134,12 @@
pr_info("schedtune: configured to support global boosting only\n");
#endif /* CONFIG_CGROUP_SCHEDTUNE */
+ schedtune_spc_rdiv = reciprocal_value(100);
+
return 0;
nodata:
+ pr_warning("schedtune: disabled!\n");
rcu_read_unlock();
return -EINVAL;
}
diff --git a/kernel/sched/walt.c b/kernel/sched/walt.c
index cbc75c0..60249e4 100644
--- a/kernel/sched/walt.c
+++ b/kernel/sched/walt.c
@@ -69,6 +69,20 @@
local_irq_restore(*flags);
}
+#ifdef CONFIG_HZ_300
+/*
+ * Tick interval becomes to 3333333 due to
+ * rounding error when HZ=300.
+ */
+#define MIN_SCHED_RAVG_WINDOW (3333333 * 6)
+#else
+/* Min window size (in ns) = 20ms */
+#define MIN_SCHED_RAVG_WINDOW 20000000
+#endif
+
+/* Max window size (in ns) = 1s */
+#define MAX_SCHED_RAVG_WINDOW 1000000000
+
/* 1 -> use PELT based load stats, 0 -> use window-based load stats */
unsigned int __read_mostly walt_disabled = 0;
@@ -132,7 +146,6 @@
*/
__read_mostly unsigned int sched_load_granule =
MIN_SCHED_RAVG_WINDOW / NUM_LOAD_INDICES;
-
/* Size of bitmaps maintained to track top tasks */
static const unsigned int top_tasks_bitmap_size =
BITS_TO_LONGS(NUM_LOAD_INDICES + 1) * sizeof(unsigned long);
diff --git a/kernel/sched/walt.h b/kernel/sched/walt.h
index 535f14b..86d5bfd 100644
--- a/kernel/sched/walt.h
+++ b/kernel/sched/walt.h
@@ -24,12 +24,6 @@
#define WINDOW_STATS_AVG 3
#define WINDOW_STATS_INVALID_POLICY 4
-/* Min window size (in ns) = 20ms */
-#define MIN_SCHED_RAVG_WINDOW 20000000
-
-/* Max window size (in ns) = 1s */
-#define MAX_SCHED_RAVG_WINDOW 1000000000
-
#define EXITING_TASK_MARKER 0xdeaddead
#define FREQ_REPORT_MAX_CPU_LOAD_TOP_TASK 0
diff --git a/kernel/sysctl.c b/kernel/sysctl.c
index 1d894fc..2c4cd17 100644
--- a/kernel/sysctl.c
+++ b/kernel/sysctl.c
@@ -335,8 +335,8 @@
.extra2 = &max_sched_granularity_ns,
},
{
- .procname = "sched_is_big_little",
- .data = &sysctl_sched_is_big_little,
+ .procname = "sched_sync_hint_enable",
+ .data = &sysctl_sched_sync_hint_enable,
.maxlen = sizeof(unsigned int),
.mode = 0644,
.proc_handler = proc_dointvec,
@@ -367,13 +367,6 @@
},
#endif
{
- .procname = "sched_sync_hint_enable",
- .data = &sysctl_sched_sync_hint_enable,
- .maxlen = sizeof(unsigned int),
- .mode = 0644,
- .proc_handler = proc_dointvec,
- },
- {
.procname = "sched_initial_task_util",
.data = &sysctl_sched_initial_task_util,
.maxlen = sizeof(unsigned int),
diff --git a/kernel/time/tick-sched.c b/kernel/time/tick-sched.c
index 44cc350..df3191e 100644
--- a/kernel/time/tick-sched.c
+++ b/kernel/time/tick-sched.c
@@ -1020,6 +1020,18 @@
return ts->sleep_length;
}
+/**
+ * tick_nohz_get_idle_calls - return the current idle calls counter value
+ *
+ * Called from the schedutil frequency scaling governor in scheduler context.
+ */
+unsigned long tick_nohz_get_idle_calls(void)
+{
+ struct tick_sched *ts = this_cpu_ptr(&tick_cpu_sched);
+
+ return ts->idle_calls;
+}
+
static void tick_nohz_account_idle_ticks(struct tick_sched *ts)
{
#ifndef CONFIG_VIRT_CPU_ACCOUNTING_NATIVE
diff --git a/kernel/trace/trace.c b/kernel/trace/trace.c
index 4a848f7..66b0714 100644
--- a/kernel/trace/trace.c
+++ b/kernel/trace/trace.c
@@ -1599,11 +1599,11 @@
#define SAVED_CMDLINES_DEFAULT 128
#define NO_CMDLINE_MAP UINT_MAX
-static unsigned saved_tgids[SAVED_CMDLINES_DEFAULT];
static arch_spinlock_t trace_cmdline_lock = __ARCH_SPIN_LOCK_UNLOCKED;
struct saved_cmdlines_buffer {
unsigned map_pid_to_cmdline[PID_MAX_DEFAULT+1];
unsigned *map_cmdline_to_pid;
+ unsigned *map_cmdline_to_tgid;
unsigned cmdline_num;
int cmdline_idx;
char *saved_cmdlines;
@@ -1637,12 +1637,23 @@
return -ENOMEM;
}
+ s->map_cmdline_to_tgid = kmalloc_array(val,
+ sizeof(*s->map_cmdline_to_tgid),
+ GFP_KERNEL);
+ if (!s->map_cmdline_to_tgid) {
+ kfree(s->map_cmdline_to_pid);
+ kfree(s->saved_cmdlines);
+ return -ENOMEM;
+ }
+
s->cmdline_idx = 0;
s->cmdline_num = val;
memset(&s->map_pid_to_cmdline, NO_CMDLINE_MAP,
sizeof(s->map_pid_to_cmdline));
memset(s->map_cmdline_to_pid, NO_CMDLINE_MAP,
val * sizeof(*s->map_cmdline_to_pid));
+ memset(s->map_cmdline_to_tgid, NO_CMDLINE_MAP,
+ val * sizeof(*s->map_cmdline_to_tgid));
return 0;
}
@@ -1808,14 +1819,17 @@
if (!tsk->pid || unlikely(tsk->pid > PID_MAX_DEFAULT))
return 0;
+ preempt_disable();
/*
* It's not the end of the world if we don't get
* the lock, but we also don't want to spin
* nor do we want to disable interrupts,
* so if we miss here, then better luck next time.
*/
- if (!arch_spin_trylock(&trace_cmdline_lock))
+ if (!arch_spin_trylock(&trace_cmdline_lock)) {
+ preempt_enable();
return 0;
+ }
idx = savedcmd->map_pid_to_cmdline[tsk->pid];
if (idx == NO_CMDLINE_MAP) {
@@ -1838,8 +1852,9 @@
}
set_cmdline(idx, tsk->comm);
- saved_tgids[idx] = tsk->tgid;
+ savedcmd->map_cmdline_to_tgid[idx] = tsk->tgid;
arch_spin_unlock(&trace_cmdline_lock);
+ preempt_enable();
return 1;
}
@@ -1881,19 +1896,29 @@
preempt_enable();
}
-int trace_find_tgid(int pid)
+static int __find_tgid_locked(int pid)
{
unsigned map;
int tgid;
- preempt_disable();
- arch_spin_lock(&trace_cmdline_lock);
map = savedcmd->map_pid_to_cmdline[pid];
if (map != NO_CMDLINE_MAP)
- tgid = saved_tgids[map];
+ tgid = savedcmd->map_cmdline_to_tgid[map];
else
tgid = -1;
+ return tgid;
+}
+
+int trace_find_tgid(int pid)
+{
+ int tgid;
+
+ preempt_disable();
+ arch_spin_lock(&trace_cmdline_lock);
+
+ tgid = __find_tgid_locked(pid);
+
arch_spin_unlock(&trace_cmdline_lock);
preempt_enable();
@@ -4386,10 +4411,15 @@
{
char buf[64];
int r;
+ unsigned int n;
+ preempt_disable();
arch_spin_lock(&trace_cmdline_lock);
- r = scnprintf(buf, sizeof(buf), "%u\n", savedcmd->cmdline_num);
+ n = savedcmd->cmdline_num;
arch_spin_unlock(&trace_cmdline_lock);
+ preempt_enable();
+
+ r = scnprintf(buf, sizeof(buf), "%u\n", n);
return simple_read_from_buffer(ubuf, cnt, ppos, buf, r);
}
@@ -4398,6 +4428,7 @@
{
kfree(s->saved_cmdlines);
kfree(s->map_cmdline_to_pid);
+ kfree(s->map_cmdline_to_tgid);
kfree(s);
}
@@ -4414,10 +4445,12 @@
return -ENOMEM;
}
+ preempt_disable();
arch_spin_lock(&trace_cmdline_lock);
savedcmd_temp = savedcmd;
savedcmd = s;
arch_spin_unlock(&trace_cmdline_lock);
+ preempt_enable();
free_saved_cmdlines_buffer(savedcmd_temp);
return 0;
@@ -4636,33 +4669,61 @@
char *file_buf;
char *buf;
int len = 0;
- int pid;
int i;
+ int *pids;
+ int n = 0;
- file_buf = kmalloc(SAVED_CMDLINES_DEFAULT*(16+1+16), GFP_KERNEL);
- if (!file_buf)
+ preempt_disable();
+ arch_spin_lock(&trace_cmdline_lock);
+
+ pids = kmalloc_array(savedcmd->cmdline_num, 2*sizeof(int), GFP_KERNEL);
+ if (!pids) {
+ arch_spin_unlock(&trace_cmdline_lock);
+ preempt_enable();
return -ENOMEM;
+ }
- buf = file_buf;
-
- for (i = 0; i < SAVED_CMDLINES_DEFAULT; i++) {
- int tgid;
- int r;
+ for (i = 0; i < savedcmd->cmdline_num; i++) {
+ int pid;
pid = savedcmd->map_cmdline_to_pid[i];
if (pid == -1 || pid == NO_CMDLINE_MAP)
continue;
- tgid = trace_find_tgid(pid);
- r = sprintf(buf, "%d %d\n", pid, tgid);
+ pids[n] = pid;
+ pids[n+1] = __find_tgid_locked(pid);
+ n += 2;
+ }
+ arch_spin_unlock(&trace_cmdline_lock);
+ preempt_enable();
+
+ if (n == 0) {
+ kfree(pids);
+ return 0;
+ }
+
+ /* enough to hold max pair of pids + space, lr and nul */
+ len = n * 12;
+ file_buf = kmalloc(len, GFP_KERNEL);
+ if (!file_buf) {
+ kfree(pids);
+ return -ENOMEM;
+ }
+
+ buf = file_buf;
+ for (i = 0; i < n && len > 0; i += 2) {
+ int r;
+
+ r = snprintf(buf, len, "%d %d\n", pids[i], pids[i+1]);
buf += r;
- len += r;
+ len -= r;
}
len = simple_read_from_buffer(ubuf, cnt, ppos,
- file_buf, len);
+ file_buf, buf - file_buf);
kfree(file_buf);
+ kfree(pids);
return len;
}
diff --git a/mm/internal.h b/mm/internal.h
index 537ac99..df6319f 100644
--- a/mm/internal.h
+++ b/mm/internal.h
@@ -74,11 +74,16 @@
extern unsigned long highest_memmap_pfn;
/*
+ * Maximum number of reclaim retries without progress before the OOM
+ * killer is consider the only way forward.
+ */
+#define MAX_RECLAIM_RETRIES 16
+
+/*
* in mm/vmscan.c:
*/
extern int isolate_lru_page(struct page *page);
extern void putback_lru_page(struct page *page);
-extern bool pgdat_reclaimable(struct pglist_data *pgdat);
/*
* in mm/rmap.c:
diff --git a/mm/migrate.c b/mm/migrate.c
index f0b786d..4213d27 100644
--- a/mm/migrate.c
+++ b/mm/migrate.c
@@ -1738,9 +1738,6 @@
{
int z;
- if (!pgdat_reclaimable(pgdat))
- return false;
-
for (z = pgdat->nr_zones - 1; z >= 0; z--) {
struct zone *zone = pgdat->node_zones + z;
diff --git a/mm/page_alloc.c b/mm/page_alloc.c
index 44085b2..3eb5f68 100644
--- a/mm/page_alloc.c
+++ b/mm/page_alloc.c
@@ -1099,14 +1099,10 @@
{
int migratetype = 0;
int batch_free = 0;
- unsigned long nr_scanned;
bool isolated_pageblocks;
spin_lock(&zone->lock);
isolated_pageblocks = has_isolate_pageblock(zone);
- nr_scanned = node_page_state(zone->zone_pgdat, NR_PAGES_SCANNED);
- if (nr_scanned)
- __mod_node_page_state(zone->zone_pgdat, NR_PAGES_SCANNED, -nr_scanned);
while (count) {
struct page *page;
@@ -1159,12 +1155,7 @@
unsigned int order,
int migratetype)
{
- unsigned long nr_scanned;
spin_lock(&zone->lock);
- nr_scanned = node_page_state(zone->zone_pgdat, NR_PAGES_SCANNED);
- if (nr_scanned)
- __mod_node_page_state(zone->zone_pgdat, NR_PAGES_SCANNED, -nr_scanned);
-
if (unlikely(has_isolate_pageblock(zone) ||
is_migrate_isolate(migratetype))) {
migratetype = get_pfnblock_migratetype(page, pfn);
@@ -3501,19 +3492,12 @@
}
/*
- * Maximum number of reclaim retries without any progress before OOM killer
- * is consider as the only way to move forward.
- */
-#define MAX_RECLAIM_RETRIES 16
-
-/*
* Checks whether it makes sense to retry the reclaim to make a forward progress
* for the given allocation request.
- * The reclaim feedback represented by did_some_progress (any progress during
- * the last reclaim round) and no_progress_loops (number of reclaim rounds without
- * any progress in a row) is considered as well as the reclaimable pages on the
- * applicable zone list (with a backoff mechanism which is a function of
- * no_progress_loops).
+ *
+ * We give up when we either have tried MAX_RECLAIM_RETRIES in a row
+ * without success, or when we couldn't even meet the watermark if we
+ * reclaimed all remaining pages on the LRU lists.
*
* Returns true if a retry is viable or false to enter the oom path.
*/
@@ -3556,13 +3540,11 @@
unsigned long reclaimable;
available = reclaimable = zone_reclaimable_pages(zone);
- available -= DIV_ROUND_UP((*no_progress_loops) * available,
- MAX_RECLAIM_RETRIES);
available += zone_page_state_snapshot(zone, NR_FREE_PAGES);
/*
- * Would the allocation succeed if we reclaimed the whole
- * available?
+ * Would the allocation succeed if we reclaimed all
+ * reclaimable pages?
*/
if (__zone_watermark_ok(zone, order, min_wmark_pages(zone),
ac_classzone_idx(ac), alloc_flags, available)) {
@@ -4442,7 +4424,6 @@
#endif
" writeback_tmp:%lukB"
" unstable:%lukB"
- " pages_scanned:%lu"
" all_unreclaimable? %s"
"\n",
pgdat->node_id,
@@ -4465,8 +4446,8 @@
#endif
K(node_page_state(pgdat, NR_WRITEBACK_TEMP)),
K(node_page_state(pgdat, NR_UNSTABLE_NFS)),
- node_page_state(pgdat, NR_PAGES_SCANNED),
- !pgdat_reclaimable(pgdat) ? "yes" : "no");
+ pgdat->kswapd_failures >= MAX_RECLAIM_RETRIES ?
+ "yes" : "no");
}
for_each_populated_zone(zone) {
diff --git a/mm/vmscan.c b/mm/vmscan.c
index 7b5848cf..ecd6c359 100644
--- a/mm/vmscan.c
+++ b/mm/vmscan.c
@@ -235,12 +235,6 @@
return nr;
}
-bool pgdat_reclaimable(struct pglist_data *pgdat)
-{
- return node_page_state_snapshot(pgdat, NR_PAGES_SCANNED) <
- pgdat_reclaimable_pages(pgdat) * 6;
-}
-
/**
* lruvec_lru_size - Returns the number of pages on the given LRU list.
* @lruvec: lru vector
@@ -1514,11 +1508,12 @@
unsigned long nr_taken = 0;
unsigned long nr_zone_taken[MAX_NR_ZONES] = { 0 };
unsigned long nr_skipped[MAX_NR_ZONES] = { 0, };
+ unsigned long skipped = 0;
unsigned long scan, nr_pages;
LIST_HEAD(pages_skipped);
for (scan = 0; scan < nr_to_scan && nr_taken < nr_to_scan &&
- !list_empty(src);) {
+ !list_empty(src); scan++) {
struct page *page;
page = lru_to_page(src);
@@ -1532,12 +1527,6 @@
continue;
}
- /*
- * Account for scanned and skipped separetly to avoid the pgdat
- * being prematurely marked unreclaimable by pgdat_reclaimable.
- */
- scan++;
-
switch (__isolate_lru_page(page, mode)) {
case 0:
nr_pages = hpage_nr_pages(page);
@@ -1565,28 +1554,20 @@
*/
if (!list_empty(&pages_skipped)) {
int zid;
- unsigned long total_skipped = 0;
+ list_splice(&pages_skipped, src);
for (zid = 0; zid < MAX_NR_ZONES; zid++) {
if (!nr_skipped[zid])
continue;
__count_zid_vm_events(PGSCAN_SKIP, zid, nr_skipped[zid]);
- total_skipped += nr_skipped[zid];
+ skipped += nr_skipped[zid];
}
-
- /*
- * Account skipped pages as a partial scan as the pgdat may be
- * close to unreclaimable. If the LRU list is empty, account
- * skipped pages as a full scan.
- */
- scan += list_empty(src) ? total_skipped : total_skipped >> 2;
-
- list_splice(&pages_skipped, src);
}
*nr_scanned = scan;
- trace_mm_vmscan_lru_isolate(sc->reclaim_idx, sc->order, nr_to_scan, scan,
- nr_taken, mode, is_file_lru(lru));
+ trace_mm_vmscan_lru_isolate(sc->reclaim_idx, sc->order, nr_to_scan,
+ scan, skipped, nr_taken, mode,
+ is_file_lru(lru));
update_lru_sizes(lruvec, lru, nr_zone_taken);
return nr_taken;
}
@@ -1849,7 +1830,6 @@
reclaim_stat->recent_scanned[file] += nr_taken;
if (global_reclaim(sc)) {
- __mod_node_page_state(pgdat, NR_PAGES_SCANNED, nr_scanned);
if (current_is_kswapd())
__count_vm_events(PGSCAN_KSWAPD, nr_scanned);
else
@@ -2038,8 +2018,6 @@
__mod_node_page_state(pgdat, NR_ISOLATED_ANON + file, nr_taken);
reclaim_stat->recent_scanned[file] += nr_taken;
- if (global_reclaim(sc))
- __mod_node_page_state(pgdat, NR_PAGES_SCANNED, nr_scanned);
__count_vm_events(PGREFILL, nr_scanned);
spin_unlock_irq(&pgdat->lru_lock);
@@ -2199,30 +2177,8 @@
unsigned long anon_prio, file_prio;
enum scan_balance scan_balance;
unsigned long anon, file;
- bool force_scan = false;
unsigned long ap, fp;
enum lru_list lru;
- bool some_scanned;
- int pass;
-
- /*
- * If the zone or memcg is small, nr[l] can be 0. This
- * results in no scanning on this priority and a potential
- * priority drop. Global direct reclaim can go to the next
- * zone and tends to have no problems. Global kswapd is for
- * zone balancing and it needs to scan a minimum amount. When
- * reclaiming for a memcg, a priority drop can cause high
- * latencies, so it's better to scan a minimum amount there as
- * well.
- */
- if (current_is_kswapd()) {
- if (!pgdat_reclaimable(pgdat))
- force_scan = true;
- if (!mem_cgroup_online(memcg))
- force_scan = true;
- }
- if (!global_reclaim(sc))
- force_scan = true;
/* If we have no swap space, do not bother scanning anon pages. */
if (!sc->may_swap || mem_cgroup_get_nr_swap_pages(memcg) <= 0) {
@@ -2354,55 +2310,48 @@
fraction[1] = fp;
denominator = ap + fp + 1;
out:
- some_scanned = false;
- /* Only use force_scan on second pass. */
- for (pass = 0; !some_scanned && pass < 2; pass++) {
- *lru_pages = 0;
- for_each_evictable_lru(lru) {
- int file = is_file_lru(lru);
- unsigned long size;
- unsigned long scan;
+ *lru_pages = 0;
+ for_each_evictable_lru(lru) {
+ int file = is_file_lru(lru);
+ unsigned long size;
+ unsigned long scan;
- size = lruvec_lru_size(lruvec, lru, sc->reclaim_idx);
- scan = size >> sc->priority;
+ size = lruvec_lru_size(lruvec, lru, sc->reclaim_idx);
+ scan = size >> sc->priority;
+ /*
+ * If the cgroup's already been deleted, make sure to
+ * scrape out the remaining cache.
+ */
+ if (!scan && !mem_cgroup_online(memcg))
+ scan = min(size, SWAP_CLUSTER_MAX);
- if (!scan && pass && force_scan)
- scan = min(size, SWAP_CLUSTER_MAX);
-
- switch (scan_balance) {
- case SCAN_EQUAL:
- /* Scan lists relative to size */
- break;
- case SCAN_FRACT:
- /*
- * Scan types proportional to swappiness and
- * their relative recent reclaim efficiency.
- */
- scan = div64_u64(scan * fraction[file],
- denominator);
- break;
- case SCAN_FILE:
- case SCAN_ANON:
- /* Scan one type exclusively */
- if ((scan_balance == SCAN_FILE) != file) {
- size = 0;
- scan = 0;
- }
- break;
- default:
- /* Look ma, no brain */
- BUG();
- }
-
- *lru_pages += size;
- nr[lru] = scan;
-
+ switch (scan_balance) {
+ case SCAN_EQUAL:
+ /* Scan lists relative to size */
+ break;
+ case SCAN_FRACT:
/*
- * Skip the second pass and don't force_scan,
- * if we found something to scan.
+ * Scan types proportional to swappiness and
+ * their relative recent reclaim efficiency.
*/
- some_scanned |= !!scan;
+ scan = div64_u64(scan * fraction[file],
+ denominator);
+ break;
+ case SCAN_FILE:
+ case SCAN_ANON:
+ /* Scan one type exclusively */
+ if ((scan_balance == SCAN_FILE) != file) {
+ size = 0;
+ scan = 0;
+ }
+ break;
+ default:
+ /* Look ma, no brain */
+ BUG();
}
+
+ *lru_pages += size;
+ nr[lru] = scan;
}
}
@@ -2704,6 +2653,15 @@
} while (should_continue_reclaim(pgdat, sc->nr_reclaimed - nr_reclaimed,
sc->nr_scanned - nr_scanned, sc));
+ /*
+ * Kswapd gives up on balancing particular nodes after too
+ * many failures to reclaim anything from them and goes to
+ * sleep. On reclaim progress, reset the failure counter. A
+ * successful direct reclaim run will revive a dormant kswapd.
+ */
+ if (reclaimable)
+ pgdat->kswapd_failures = 0;
+
return reclaimable;
}
@@ -2778,10 +2736,6 @@
GFP_KERNEL | __GFP_HARDWALL))
continue;
- if (sc->priority != DEF_PRIORITY &&
- !pgdat_reclaimable(zone->zone_pgdat))
- continue; /* Let kswapd poll it */
-
/*
* If we already have plenty of memory free for
* compaction in this zone, don't free any more.
@@ -2918,7 +2872,7 @@
return 0;
}
-static bool pfmemalloc_watermark_ok(pg_data_t *pgdat)
+static bool allow_direct_reclaim(pg_data_t *pgdat)
{
struct zone *zone;
unsigned long pfmemalloc_reserve = 0;
@@ -2926,10 +2880,15 @@
int i;
bool wmark_ok;
+ if (pgdat->kswapd_failures >= MAX_RECLAIM_RETRIES)
+ return true;
+
for (i = 0; i <= ZONE_NORMAL; i++) {
zone = &pgdat->node_zones[i];
- if (!managed_zone(zone) ||
- pgdat_reclaimable_pages(pgdat) == 0)
+ if (!managed_zone(zone))
+ continue;
+
+ if (!zone_reclaimable_pages(zone))
continue;
pfmemalloc_reserve += min_wmark_pages(zone);
@@ -3006,7 +2965,7 @@
/* Throttle based on the first usable node */
pgdat = zone->zone_pgdat;
- if (pfmemalloc_watermark_ok(pgdat))
+ if (allow_direct_reclaim(pgdat))
goto out;
break;
}
@@ -3028,14 +2987,14 @@
*/
if (!(gfp_mask & __GFP_FS)) {
wait_event_interruptible_timeout(pgdat->pfmemalloc_wait,
- pfmemalloc_watermark_ok(pgdat), HZ);
+ allow_direct_reclaim(pgdat), HZ);
goto check_pending;
}
/* Throttle until kswapd wakes the process */
wait_event_killable(zone->zone_pgdat->pfmemalloc_wait,
- pfmemalloc_watermark_ok(pgdat));
+ allow_direct_reclaim(pgdat));
check_pending:
if (fatal_signal_pending(current))
@@ -3198,6 +3157,7 @@
*/
clear_bit(PGDAT_CONGESTED, &zone->zone_pgdat->flags);
clear_bit(PGDAT_DIRTY, &zone->zone_pgdat->flags);
+ clear_bit(PGDAT_WRITEBACK, &zone->zone_pgdat->flags);
return true;
}
@@ -3214,7 +3174,7 @@
/*
* The throttled processes are normally woken up in balance_pgdat() as
- * soon as pfmemalloc_watermark_ok() is true. But there is a potential
+ * soon as allow_direct_reclaim() is true. But there is a potential
* race between when kswapd checks the watermarks and a process gets
* throttled. There is also a potential race if processes get
* throttled, kswapd wakes, a large process exits thereby balancing the
@@ -3228,17 +3188,21 @@
if (waitqueue_active(&pgdat->pfmemalloc_wait))
wake_up_all(&pgdat->pfmemalloc_wait);
+ /* Hopeless node, leave it to direct reclaim */
+ if (pgdat->kswapd_failures >= MAX_RECLAIM_RETRIES)
+ return true;
+
for (i = 0; i <= classzone_idx; i++) {
struct zone *zone = pgdat->node_zones + i;
if (!managed_zone(zone))
continue;
- if (!zone_balanced(zone, order, classzone_idx))
- return false;
+ if (zone_balanced(zone, order, classzone_idx))
+ return true;
}
- return true;
+ return false;
}
/*
@@ -3314,9 +3278,9 @@
count_vm_event(PAGEOUTRUN);
do {
+ unsigned long nr_reclaimed = sc.nr_reclaimed;
bool raise_priority = true;
- sc.nr_reclaimed = 0;
sc.reclaim_idx = classzone_idx;
/*
@@ -3371,7 +3335,7 @@
* If we're getting trouble reclaiming, start doing writepage
* even in laptop mode.
*/
- if (sc.priority < DEF_PRIORITY - 2 || !pgdat_reclaimable(pgdat))
+ if (sc.priority < DEF_PRIORITY - 2)
sc.may_writepage = 1;
/* Call soft limit reclaim before calling shrink_node. */
@@ -3395,7 +3359,7 @@
* able to safely make forward progress. Wake them
*/
if (waitqueue_active(&pgdat->pfmemalloc_wait) &&
- pfmemalloc_watermark_ok(pgdat))
+ allow_direct_reclaim(pgdat))
wake_up_all(&pgdat->pfmemalloc_wait);
/* Check if kswapd should be suspending */
@@ -3406,10 +3370,14 @@
* Raise priority if scanning rate is too low or there was no
* progress in reclaiming pages
*/
- if (raise_priority || !sc.nr_reclaimed)
+ nr_reclaimed = sc.nr_reclaimed - nr_reclaimed;
+ if (raise_priority || !nr_reclaimed)
sc.priority--;
} while (sc.priority >= 1);
+ if (!sc.nr_reclaimed)
+ pgdat->kswapd_failures++;
+
out:
/*
* Return the order kswapd stopped reclaiming at as
@@ -3431,7 +3399,13 @@
prepare_to_wait(&pgdat->kswapd_wait, &wait, TASK_INTERRUPTIBLE);
- /* Try to sleep for a short interval */
+ /*
+ * Try to sleep for a short interval. Note that kcompactd will only be
+ * woken if it is possible to sleep for a short interval. This is
+ * deliberate on the assumption that if reclaim cannot keep an
+ * eligible zone balanced that it's also unlikely that compaction will
+ * succeed.
+ */
if (prepare_kswapd_sleep(pgdat, reclaim_order, classzone_idx)) {
/*
* Compaction records what page blocks it recently failed to
@@ -3609,6 +3583,10 @@
if (!waitqueue_active(&pgdat->kswapd_wait))
return;
+ /* Hopeless node, leave it to direct reclaim */
+ if (pgdat->kswapd_failures >= MAX_RECLAIM_RETRIES)
+ return;
+
/* Only wake kswapd if all zones are unbalanced */
for (z = 0; z <= classzone_idx; z++) {
zone = pgdat->node_zones + z;
@@ -3879,9 +3857,6 @@
sum_zone_node_page_state(pgdat->node_id, NR_SLAB_RECLAIMABLE) <= pgdat->min_slab_pages)
return NODE_RECLAIM_FULL;
- if (!pgdat_reclaimable(pgdat))
- return NODE_RECLAIM_FULL;
-
/*
* Do not scan if the allocation should not be delayed.
*/
diff --git a/mm/vmstat.c b/mm/vmstat.c
index 513c37a..2ab7973 100644
--- a/mm/vmstat.c
+++ b/mm/vmstat.c
@@ -954,7 +954,6 @@
"nr_unevictable",
"nr_isolated_anon",
"nr_isolated_file",
- "nr_pages_scanned",
"workingset_refault",
"workingset_activate",
"workingset_nodereclaim",
@@ -1379,7 +1378,6 @@
"\n min %lu"
"\n low %lu"
"\n high %lu"
- "\n node_scanned %lu"
"\n spanned %lu"
"\n present %lu"
"\n managed %lu",
@@ -1387,7 +1385,6 @@
min_wmark_pages(zone),
low_wmark_pages(zone),
high_wmark_pages(zone),
- node_page_state(zone->zone_pgdat, NR_PAGES_SCANNED),
zone->spanned_pages,
zone->present_pages,
zone->managed_pages);
@@ -1426,7 +1423,7 @@
"\n node_unreclaimable: %u"
"\n start_pfn: %lu"
"\n node_inactive_ratio: %u",
- !pgdat_reclaimable(zone->zone_pgdat),
+ pgdat->kswapd_failures >= MAX_RECLAIM_RETRIES,
zone->zone_start_pfn,
zone->zone_pgdat->inactive_ratio);
seq_putc(m, '\n');
@@ -1588,22 +1585,9 @@
for (i = 0; i < NR_VM_ZONE_STAT_ITEMS; i++) {
val = atomic_long_read(&vm_zone_stat[i]);
if (val < 0) {
- switch (i) {
- case NR_PAGES_SCANNED:
- /*
- * This is often seen to go negative in
- * recent kernels, but not to go permanently
- * negative. Whilst it would be nicer not to
- * have exceptions, rooting them out would be
- * another task, of rather low priority.
- */
- break;
- default:
- pr_warn("%s: %s %ld\n",
- __func__, vmstat_text[i], val);
- err = -EINVAL;
- break;
- }
+ pr_warn("%s: %s %ld\n",
+ __func__, vmstat_text[i], val);
+ err = -EINVAL;
}
}
if (err)
diff --git a/net/core/dev.c b/net/core/dev.c
index 7e168d0..610e5f8 100644
--- a/net/core/dev.c
+++ b/net/core/dev.c
@@ -2704,9 +2704,10 @@
static inline bool skb_needs_check(struct sk_buff *skb, bool tx_path)
{
if (tx_path)
- return skb->ip_summed != CHECKSUM_PARTIAL;
- else
- return skb->ip_summed == CHECKSUM_NONE;
+ return skb->ip_summed != CHECKSUM_PARTIAL &&
+ skb->ip_summed != CHECKSUM_NONE;
+
+ return skb->ip_summed == CHECKSUM_NONE;
}
/**
@@ -2725,11 +2726,12 @@
struct sk_buff *__skb_gso_segment(struct sk_buff *skb,
netdev_features_t features, bool tx_path)
{
+ struct sk_buff *segs;
+
if (unlikely(skb_needs_check(skb, tx_path))) {
int err;
- skb_warn_bad_offload(skb);
-
+ /* We're going to init ->check field in TCP or UDP header */
err = skb_cow_head(skb, 0);
if (err < 0)
return ERR_PTR(err);
@@ -2757,7 +2759,12 @@
skb_reset_mac_header(skb);
skb_reset_mac_len(skb);
- return skb_mac_gso_segment(skb, features);
+ segs = skb_mac_gso_segment(skb, features);
+
+ if (unlikely(skb_needs_check(skb, tx_path)))
+ skb_warn_bad_offload(skb);
+
+ return segs;
}
EXPORT_SYMBOL(__skb_gso_segment);
diff --git a/net/core/dst.c b/net/core/dst.c
index 39cc119..b5de366 100644
--- a/net/core/dst.c
+++ b/net/core/dst.c
@@ -349,8 +349,15 @@
new = ((unsigned long) &dst_default_metrics) | DST_METRICS_READ_ONLY;
prev = cmpxchg(&dst->_metrics, old, new);
- if (prev == old)
- kfree(__DST_METRICS_PTR(old));
+ if (prev == old) {
+ struct dst_metrics *old_p = (struct dst_metrics *)
+ __DST_METRICS_PTR(old);
+
+ if (prev & DST_METRICS_REFCOUNTED) {
+ if (atomic_dec_and_test(&old_p->refcnt))
+ kfree(old_p);
+ }
+ }
}
EXPORT_SYMBOL(__dst_destroy_metrics_generic);
diff --git a/net/ipv6/ip6_output.c b/net/ipv6/ip6_output.c
index 3e42221..4403260 100644
--- a/net/ipv6/ip6_output.c
+++ b/net/ipv6/ip6_output.c
@@ -1384,7 +1384,7 @@
*/
cork->length += length;
- if (((length > mtu) ||
+ if ((((length + fragheaderlen) > mtu) ||
(skb && skb_is_gso(skb))) &&
(sk->sk_protocol == IPPROTO_UDP) &&
(rt->dst.dev->features & NETIF_F_UFO) && !rt->dst.header_len &&
diff --git a/net/key/af_key.c b/net/key/af_key.c
index d8d95b6..2e1050e 100644
--- a/net/key/af_key.c
+++ b/net/key/af_key.c
@@ -63,6 +63,7 @@
} u;
struct sk_buff *skb;
} dump;
+ struct mutex dump_lock;
};
static int parse_sockaddr_pair(struct sockaddr *sa, int ext_len,
@@ -143,6 +144,7 @@
{
struct netns_pfkey *net_pfkey = net_generic(net, pfkey_net_id);
struct sock *sk;
+ struct pfkey_sock *pfk;
int err;
if (!ns_capable(net->user_ns, CAP_NET_ADMIN))
@@ -157,6 +159,9 @@
if (sk == NULL)
goto out;
+ pfk = pfkey_sk(sk);
+ mutex_init(&pfk->dump_lock);
+
sock->ops = &pfkey_ops;
sock_init_data(sock, sk);
@@ -285,13 +290,23 @@
struct sadb_msg *hdr;
int rc;
+ mutex_lock(&pfk->dump_lock);
+ if (!pfk->dump.dump) {
+ rc = 0;
+ goto out;
+ }
+
rc = pfk->dump.dump(pfk);
- if (rc == -ENOBUFS)
- return 0;
+ if (rc == -ENOBUFS) {
+ rc = 0;
+ goto out;
+ }
if (pfk->dump.skb) {
- if (!pfkey_can_dump(&pfk->sk))
- return 0;
+ if (!pfkey_can_dump(&pfk->sk)) {
+ rc = 0;
+ goto out;
+ }
hdr = (struct sadb_msg *) pfk->dump.skb->data;
hdr->sadb_msg_seq = 0;
@@ -302,6 +317,9 @@
}
pfkey_terminate_dump(pfk);
+
+out:
+ mutex_unlock(&pfk->dump_lock);
return rc;
}
@@ -1806,19 +1824,26 @@
struct xfrm_address_filter *filter = NULL;
struct pfkey_sock *pfk = pfkey_sk(sk);
- if (pfk->dump.dump != NULL)
+ mutex_lock(&pfk->dump_lock);
+ if (pfk->dump.dump != NULL) {
+ mutex_unlock(&pfk->dump_lock);
return -EBUSY;
+ }
proto = pfkey_satype2proto(hdr->sadb_msg_satype);
- if (proto == 0)
+ if (proto == 0) {
+ mutex_unlock(&pfk->dump_lock);
return -EINVAL;
+ }
if (ext_hdrs[SADB_X_EXT_FILTER - 1]) {
struct sadb_x_filter *xfilter = ext_hdrs[SADB_X_EXT_FILTER - 1];
filter = kmalloc(sizeof(*filter), GFP_KERNEL);
- if (filter == NULL)
+ if (filter == NULL) {
+ mutex_unlock(&pfk->dump_lock);
return -ENOMEM;
+ }
memcpy(&filter->saddr, &xfilter->sadb_x_filter_saddr,
sizeof(xfrm_address_t));
@@ -1834,6 +1859,7 @@
pfk->dump.dump = pfkey_dump_sa;
pfk->dump.done = pfkey_dump_sa_done;
xfrm_state_walk_init(&pfk->dump.u.state, proto, filter);
+ mutex_unlock(&pfk->dump_lock);
return pfkey_do_dump(pfk);
}
@@ -2693,14 +2719,18 @@
{
struct pfkey_sock *pfk = pfkey_sk(sk);
- if (pfk->dump.dump != NULL)
+ mutex_lock(&pfk->dump_lock);
+ if (pfk->dump.dump != NULL) {
+ mutex_unlock(&pfk->dump_lock);
return -EBUSY;
+ }
pfk->dump.msg_version = hdr->sadb_msg_version;
pfk->dump.msg_portid = hdr->sadb_msg_pid;
pfk->dump.dump = pfkey_dump_sp;
pfk->dump.done = pfkey_dump_sp_done;
xfrm_policy_walk_init(&pfk->dump.u.policy, XFRM_POLICY_TYPE_MAIN);
+ mutex_unlock(&pfk->dump_lock);
return pfkey_do_dump(pfk);
}
diff --git a/net/l2tp/l2tp_ip6.c b/net/l2tp/l2tp_ip6.c
index 74d0d33..96efe47 100644
--- a/net/l2tp/l2tp_ip6.c
+++ b/net/l2tp/l2tp_ip6.c
@@ -64,7 +64,7 @@
struct sock *sk;
sk_for_each_bound(sk, &l2tp_ip6_bind_table) {
- const struct in6_addr *addr = inet6_rcv_saddr(sk);
+ const struct in6_addr *sk_laddr = inet6_rcv_saddr(sk);
struct l2tp_ip6_sock *l2tp = l2tp_ip6_sk(sk);
if (l2tp == NULL)
@@ -72,7 +72,7 @@
if ((l2tp->conn_id == tunnel_id) &&
net_eq(sock_net(sk), net) &&
- (!addr || ipv6_addr_equal(addr, laddr)) &&
+ (!sk_laddr || ipv6_addr_any(sk_laddr) || ipv6_addr_equal(sk_laddr, laddr)) &&
(!sk->sk_bound_dev_if || !dif ||
sk->sk_bound_dev_if == dif))
goto found;
diff --git a/net/xfrm/xfrm_policy.c b/net/xfrm/xfrm_policy.c
index 8da67f7..e26b515 100644
--- a/net/xfrm/xfrm_policy.c
+++ b/net/xfrm/xfrm_policy.c
@@ -1248,7 +1248,7 @@
}
static struct xfrm_policy *xfrm_sk_policy_lookup(const struct sock *sk, int dir,
- const struct flowi *fl)
+ const struct flowi *fl, u16 family)
{
struct xfrm_policy *pol;
@@ -1256,8 +1256,7 @@
again:
pol = rcu_dereference(sk->sk_policy[dir]);
if (pol != NULL) {
- bool match = xfrm_selector_match(&pol->selector, fl,
- sk->sk_family);
+ bool match = xfrm_selector_match(&pol->selector, fl, family);
int err = 0;
if (match) {
@@ -2206,7 +2205,7 @@
sk = sk_const_to_full_sk(sk);
if (sk && sk->sk_policy[XFRM_POLICY_OUT]) {
num_pols = 1;
- pols[0] = xfrm_sk_policy_lookup(sk, XFRM_POLICY_OUT, fl);
+ pols[0] = xfrm_sk_policy_lookup(sk, XFRM_POLICY_OUT, fl, family);
err = xfrm_expand_policies(fl, family, pols,
&num_pols, &num_xfrms);
if (err < 0)
@@ -2485,7 +2484,7 @@
pol = NULL;
sk = sk_to_full_sk(sk);
if (sk && sk->sk_policy[dir]) {
- pol = xfrm_sk_policy_lookup(sk, dir, &fl);
+ pol = xfrm_sk_policy_lookup(sk, dir, &fl, family);
if (IS_ERR(pol)) {
XFRM_INC_STATS(net, LINUX_MIB_XFRMINPOLERROR);
return 0;
diff --git a/sound/pci/fm801.c b/sound/pci/fm801.c
index c47287d..a178e0d 100644
--- a/sound/pci/fm801.c
+++ b/sound/pci/fm801.c
@@ -1235,8 +1235,6 @@
}
}
- snd_fm801_chip_init(chip);
-
if ((chip->tea575x_tuner & TUNER_ONLY) == 0) {
if (devm_request_irq(&pci->dev, pci->irq, snd_fm801_interrupt,
IRQF_SHARED, KBUILD_MODNAME, chip)) {
@@ -1248,6 +1246,8 @@
pci_set_master(pci);
}
+ snd_fm801_chip_init(chip);
+
if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops)) < 0) {
snd_fm801_free(chip);
return err;
diff --git a/sound/pci/hda/patch_hdmi.c b/sound/pci/hda/patch_hdmi.c
index 4bf4833..775c678 100644
--- a/sound/pci/hda/patch_hdmi.c
+++ b/sound/pci/hda/patch_hdmi.c
@@ -3600,11 +3600,15 @@
HDA_CODEC_ENTRY(0x10951390, "SiI1390 HDMI", patch_generic_hdmi),
HDA_CODEC_ENTRY(0x10951392, "SiI1392 HDMI", patch_generic_hdmi),
HDA_CODEC_ENTRY(0x17e80047, "Chrontel HDMI", patch_generic_hdmi),
+HDA_CODEC_ENTRY(0x10de0001, "MCP73 HDMI", patch_nvhdmi_2ch),
HDA_CODEC_ENTRY(0x10de0002, "MCP77/78 HDMI", patch_nvhdmi_8ch_7x),
HDA_CODEC_ENTRY(0x10de0003, "MCP77/78 HDMI", patch_nvhdmi_8ch_7x),
+HDA_CODEC_ENTRY(0x10de0004, "GPU 04 HDMI", patch_nvhdmi_8ch_7x),
HDA_CODEC_ENTRY(0x10de0005, "MCP77/78 HDMI", patch_nvhdmi_8ch_7x),
HDA_CODEC_ENTRY(0x10de0006, "MCP77/78 HDMI", patch_nvhdmi_8ch_7x),
HDA_CODEC_ENTRY(0x10de0007, "MCP79/7A HDMI", patch_nvhdmi_8ch_7x),
+HDA_CODEC_ENTRY(0x10de0008, "GPU 08 HDMI/DP", patch_nvhdmi),
+HDA_CODEC_ENTRY(0x10de0009, "GPU 09 HDMI/DP", patch_nvhdmi),
HDA_CODEC_ENTRY(0x10de000a, "GPU 0a HDMI/DP", patch_nvhdmi),
HDA_CODEC_ENTRY(0x10de000b, "GPU 0b HDMI/DP", patch_nvhdmi),
HDA_CODEC_ENTRY(0x10de000c, "MCP89 HDMI", patch_nvhdmi),
@@ -3631,17 +3635,40 @@
HDA_CODEC_ENTRY(0x10de0042, "GPU 42 HDMI/DP", patch_nvhdmi),
HDA_CODEC_ENTRY(0x10de0043, "GPU 43 HDMI/DP", patch_nvhdmi),
HDA_CODEC_ENTRY(0x10de0044, "GPU 44 HDMI/DP", patch_nvhdmi),
+HDA_CODEC_ENTRY(0x10de0045, "GPU 45 HDMI/DP", patch_nvhdmi),
+HDA_CODEC_ENTRY(0x10de0050, "GPU 50 HDMI/DP", patch_nvhdmi),
HDA_CODEC_ENTRY(0x10de0051, "GPU 51 HDMI/DP", patch_nvhdmi),
+HDA_CODEC_ENTRY(0x10de0052, "GPU 52 HDMI/DP", patch_nvhdmi),
HDA_CODEC_ENTRY(0x10de0060, "GPU 60 HDMI/DP", patch_nvhdmi),
+HDA_CODEC_ENTRY(0x10de0061, "GPU 61 HDMI/DP", patch_nvhdmi),
+HDA_CODEC_ENTRY(0x10de0062, "GPU 62 HDMI/DP", patch_nvhdmi),
HDA_CODEC_ENTRY(0x10de0067, "MCP67 HDMI", patch_nvhdmi_2ch),
HDA_CODEC_ENTRY(0x10de0070, "GPU 70 HDMI/DP", patch_nvhdmi),
HDA_CODEC_ENTRY(0x10de0071, "GPU 71 HDMI/DP", patch_nvhdmi),
HDA_CODEC_ENTRY(0x10de0072, "GPU 72 HDMI/DP", patch_nvhdmi),
+HDA_CODEC_ENTRY(0x10de0073, "GPU 73 HDMI/DP", patch_nvhdmi),
+HDA_CODEC_ENTRY(0x10de0074, "GPU 74 HDMI/DP", patch_nvhdmi),
+HDA_CODEC_ENTRY(0x10de0076, "GPU 76 HDMI/DP", patch_nvhdmi),
+HDA_CODEC_ENTRY(0x10de007b, "GPU 7b HDMI/DP", patch_nvhdmi),
+HDA_CODEC_ENTRY(0x10de007c, "GPU 7c HDMI/DP", patch_nvhdmi),
HDA_CODEC_ENTRY(0x10de007d, "GPU 7d HDMI/DP", patch_nvhdmi),
+HDA_CODEC_ENTRY(0x10de007e, "GPU 7e HDMI/DP", patch_nvhdmi),
HDA_CODEC_ENTRY(0x10de0080, "GPU 80 HDMI/DP", patch_nvhdmi),
+HDA_CODEC_ENTRY(0x10de0081, "GPU 81 HDMI/DP", patch_nvhdmi),
HDA_CODEC_ENTRY(0x10de0082, "GPU 82 HDMI/DP", patch_nvhdmi),
HDA_CODEC_ENTRY(0x10de0083, "GPU 83 HDMI/DP", patch_nvhdmi),
+HDA_CODEC_ENTRY(0x10de0084, "GPU 84 HDMI/DP", patch_nvhdmi),
+HDA_CODEC_ENTRY(0x10de0090, "GPU 90 HDMI/DP", patch_nvhdmi),
+HDA_CODEC_ENTRY(0x10de0091, "GPU 91 HDMI/DP", patch_nvhdmi),
+HDA_CODEC_ENTRY(0x10de0092, "GPU 92 HDMI/DP", patch_nvhdmi),
+HDA_CODEC_ENTRY(0x10de0093, "GPU 93 HDMI/DP", patch_nvhdmi),
+HDA_CODEC_ENTRY(0x10de0094, "GPU 94 HDMI/DP", patch_nvhdmi),
+HDA_CODEC_ENTRY(0x10de0095, "GPU 95 HDMI/DP", patch_nvhdmi),
+HDA_CODEC_ENTRY(0x10de0097, "GPU 97 HDMI/DP", patch_nvhdmi),
+HDA_CODEC_ENTRY(0x10de0098, "GPU 98 HDMI/DP", patch_nvhdmi),
+HDA_CODEC_ENTRY(0x10de0099, "GPU 99 HDMI/DP", patch_nvhdmi),
HDA_CODEC_ENTRY(0x10de8001, "MCP73 HDMI", patch_nvhdmi_2ch),
+HDA_CODEC_ENTRY(0x10de8067, "MCP67/68 HDMI", patch_nvhdmi_2ch),
HDA_CODEC_ENTRY(0x11069f80, "VX900 HDMI/DP", patch_via_hdmi),
HDA_CODEC_ENTRY(0x11069f81, "VX900 HDMI/DP", patch_via_hdmi),
HDA_CODEC_ENTRY(0x11069f84, "VX11 HDMI/DP", patch_generic_hdmi),
diff --git a/sound/soc/codecs/nau8825.c b/sound/soc/codecs/nau8825.c
index e643be9..f9f2737 100644
--- a/sound/soc/codecs/nau8825.c
+++ b/sound/soc/codecs/nau8825.c
@@ -1928,7 +1928,8 @@
NAU8825_FLL_INTEGER_MASK, fll_param->fll_int);
/* FLL pre-scaler */
regmap_update_bits(nau8825->regmap, NAU8825_REG_FLL4,
- NAU8825_FLL_REF_DIV_MASK, fll_param->clk_ref_div);
+ NAU8825_FLL_REF_DIV_MASK,
+ fll_param->clk_ref_div << NAU8825_FLL_REF_DIV_SFT);
/* select divided VCO input */
regmap_update_bits(nau8825->regmap, NAU8825_REG_FLL5,
NAU8825_FLL_CLK_SW_MASK, NAU8825_FLL_CLK_SW_REF);
diff --git a/sound/soc/codecs/nau8825.h b/sound/soc/codecs/nau8825.h
index 1c63e2a..574d6f9 100644
--- a/sound/soc/codecs/nau8825.h
+++ b/sound/soc/codecs/nau8825.h
@@ -129,7 +129,8 @@
#define NAU8825_FLL_CLK_SRC_FS (0x3 << NAU8825_FLL_CLK_SRC_SFT)
/* FLL4 (0x07) */
-#define NAU8825_FLL_REF_DIV_MASK (0x3 << 10)
+#define NAU8825_FLL_REF_DIV_SFT 10
+#define NAU8825_FLL_REF_DIV_MASK (0x3 << NAU8825_FLL_REF_DIV_SFT)
/* FLL5 (0x08) */
#define NAU8825_FLL_PDB_DAC_EN (0x1 << 15)
diff --git a/sound/soc/codecs/tlv320aic3x.c b/sound/soc/codecs/tlv320aic3x.c
index 5a8d96e..fe45a16 100644
--- a/sound/soc/codecs/tlv320aic3x.c
+++ b/sound/soc/codecs/tlv320aic3x.c
@@ -126,6 +126,16 @@
{ 108, 0x00 }, { 109, 0x00 },
};
+static bool aic3x_volatile_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case AIC3X_RESET:
+ return true;
+ default:
+ return false;
+ }
+}
+
static const struct regmap_config aic3x_regmap = {
.reg_bits = 8,
.val_bits = 8,
@@ -133,6 +143,9 @@
.max_register = DAC_ICC_ADJ,
.reg_defaults = aic3x_reg,
.num_reg_defaults = ARRAY_SIZE(aic3x_reg),
+
+ .volatile_reg = aic3x_volatile_reg,
+
.cache_type = REGCACHE_RBTREE,
};
diff --git a/sound/soc/fsl/fsl_ssi.c b/sound/soc/fsl/fsl_ssi.c
index 5034943..fde08660 100644
--- a/sound/soc/fsl/fsl_ssi.c
+++ b/sound/soc/fsl/fsl_ssi.c
@@ -224,6 +224,12 @@
* @dbg_stats: Debugging statistics
*
* @soc: SoC specific data
+ *
+ * @fifo_watermark: the FIFO watermark setting. Notifies DMA when
+ * there are @fifo_watermark or fewer words in TX fifo or
+ * @fifo_watermark or more empty words in RX fifo.
+ * @dma_maxburst: max number of words to transfer in one go. So far,
+ * this is always the same as fifo_watermark.
*/
struct fsl_ssi_private {
struct regmap *regs;
@@ -263,6 +269,9 @@
const struct fsl_ssi_soc_data *soc;
struct device *dev;
+
+ u32 fifo_watermark;
+ u32 dma_maxburst;
};
/*
@@ -1051,21 +1060,7 @@
regmap_write(regs, CCSR_SSI_SRCR, srcr);
regmap_write(regs, CCSR_SSI_SCR, scr);
- /*
- * Set the watermark for transmit FIFI 0 and receive FIFO 0. We don't
- * use FIFO 1. We program the transmit water to signal a DMA transfer
- * if there are only two (or fewer) elements left in the FIFO. Two
- * elements equals one frame (left channel, right channel). This value,
- * however, depends on the depth of the transmit buffer.
- *
- * We set the watermark on the same level as the DMA burstsize. For
- * fiq it is probably better to use the biggest possible watermark
- * size.
- */
- if (ssi_private->use_dma)
- wm = ssi_private->fifo_depth - 2;
- else
- wm = ssi_private->fifo_depth;
+ wm = ssi_private->fifo_watermark;
regmap_write(regs, CCSR_SSI_SFCSR,
CCSR_SSI_SFCSR_TFWM0(wm) | CCSR_SSI_SFCSR_RFWM0(wm) |
@@ -1373,12 +1368,8 @@
dev_dbg(&pdev->dev, "could not get baud clock: %ld\n",
PTR_ERR(ssi_private->baudclk));
- /*
- * We have burstsize be "fifo_depth - 2" to match the SSI
- * watermark setting in fsl_ssi_startup().
- */
- ssi_private->dma_params_tx.maxburst = ssi_private->fifo_depth - 2;
- ssi_private->dma_params_rx.maxburst = ssi_private->fifo_depth - 2;
+ ssi_private->dma_params_tx.maxburst = ssi_private->dma_maxburst;
+ ssi_private->dma_params_rx.maxburst = ssi_private->dma_maxburst;
ssi_private->dma_params_tx.addr = ssi_private->ssi_phys + CCSR_SSI_STX0;
ssi_private->dma_params_rx.addr = ssi_private->ssi_phys + CCSR_SSI_SRX0;
@@ -1543,6 +1534,47 @@
/* Older 8610 DTs didn't have the fifo-depth property */
ssi_private->fifo_depth = 8;
+ /*
+ * Set the watermark for transmit FIFO 0 and receive FIFO 0. We don't
+ * use FIFO 1 but set the watermark appropriately nontheless.
+ * We program the transmit water to signal a DMA transfer
+ * if there are N elements left in the FIFO. For chips with 15-deep
+ * FIFOs, set watermark to 8. This allows the SSI to operate at a
+ * high data rate without channel slipping. Behavior is unchanged
+ * for the older chips with a fifo depth of only 8. A value of 4
+ * might be appropriate for the older chips, but is left at
+ * fifo_depth-2 until sombody has a chance to test.
+ *
+ * We set the watermark on the same level as the DMA burstsize. For
+ * fiq it is probably better to use the biggest possible watermark
+ * size.
+ */
+ switch (ssi_private->fifo_depth) {
+ case 15:
+ /*
+ * 2 samples is not enough when running at high data
+ * rates (like 48kHz @ 16 bits/channel, 16 channels)
+ * 8 seems to split things evenly and leave enough time
+ * for the DMA to fill the FIFO before it's over/under
+ * run.
+ */
+ ssi_private->fifo_watermark = 8;
+ ssi_private->dma_maxburst = 8;
+ break;
+ case 8:
+ default:
+ /*
+ * maintain old behavior for older chips.
+ * Keeping it the same because I don't have an older
+ * board to test with.
+ * I suspect this could be changed to be something to
+ * leave some more space in the fifo.
+ */
+ ssi_private->fifo_watermark = ssi_private->fifo_depth - 2;
+ ssi_private->dma_maxburst = ssi_private->fifo_depth - 2;
+ break;
+ }
+
dev_set_drvdata(&pdev->dev, ssi_private);
if (ssi_private->soc->imx) {
diff --git a/sound/soc/intel/boards/bytcr_rt5640.c b/sound/soc/intel/boards/bytcr_rt5640.c
index d5873ee..bd19fad 100644
--- a/sound/soc/intel/boards/bytcr_rt5640.c
+++ b/sound/soc/intel/boards/bytcr_rt5640.c
@@ -142,7 +142,7 @@
* for Jack detection and button press
*/
ret = snd_soc_dai_set_sysclk(codec_dai, RT5640_SCLK_S_RCCLK,
- 0,
+ 48000 * 512,
SND_SOC_CLOCK_IN);
if (!ret) {
if ((byt_rt5640_quirk & BYT_RT5640_MCLK_EN) && priv->mclk)
diff --git a/sound/soc/intel/skylake/skl-sst.c b/sound/soc/intel/skylake/skl-sst.c
index 8fc3178..b30bd38 100644
--- a/sound/soc/intel/skylake/skl-sst.c
+++ b/sound/soc/intel/skylake/skl-sst.c
@@ -515,6 +515,9 @@
void skl_sst_dsp_cleanup(struct device *dev, struct skl_sst *ctx)
{
+
+ if (ctx->dsp->fw)
+ release_firmware(ctx->dsp->fw);
skl_clear_module_table(ctx->dsp);
skl_freeup_uuid_list(ctx);
skl_ipc_free(&ctx->ipc);
diff --git a/sound/usb/endpoint.c b/sound/usb/endpoint.c
index 70e1477..6bd424b 100644
--- a/sound/usb/endpoint.c
+++ b/sound/usb/endpoint.c
@@ -384,6 +384,9 @@
if (unlikely(atomic_read(&ep->chip->shutdown)))
goto exit_clear;
+ if (unlikely(!test_bit(EP_FLAG_RUNNING, &ep->flags)))
+ goto exit_clear;
+
if (usb_pipeout(ep->pipe)) {
retire_outbound_urb(ep, ctx);
/* can be stopped during retire callback */
diff --git a/tools/lib/traceevent/plugin_sched_switch.c b/tools/lib/traceevent/plugin_sched_switch.c
index f1ce600..ec30c2f 100644
--- a/tools/lib/traceevent/plugin_sched_switch.c
+++ b/tools/lib/traceevent/plugin_sched_switch.c
@@ -111,7 +111,7 @@
trace_seq_printf(s, "%lld ", val);
if (pevent_get_field_val(s, event, "prev_prio", record, &val, 0) == 0)
- trace_seq_printf(s, "[%lld] ", val);
+ trace_seq_printf(s, "[%d] ", (int) val);
if (pevent_get_field_val(s, event, "prev_state", record, &val, 0) == 0)
write_state(s, val);
@@ -129,7 +129,7 @@
trace_seq_printf(s, "%lld", val);
if (pevent_get_field_val(s, event, "next_prio", record, &val, 0) == 0)
- trace_seq_printf(s, " [%lld]", val);
+ trace_seq_printf(s, " [%d]", (int) val);
return 0;
}
diff --git a/tools/perf/Makefile.perf b/tools/perf/Makefile.perf
index 982d643..ef52d1e 100644
--- a/tools/perf/Makefile.perf
+++ b/tools/perf/Makefile.perf
@@ -729,9 +729,9 @@
$(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/tests/attr'; \
$(INSTALL) tests/attr/* '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/tests/attr'
-install-bin: install-tools install-tests
+install-bin: install-tools install-tests install-traceevent-plugins
-install: install-bin try-install-man install-traceevent-plugins
+install: install-bin try-install-man
install-python_ext:
$(PYTHON_WORD) util/setup.py --quiet install --root='/$(DESTDIR_SQ)'
diff --git a/tools/perf/util/probe-event.c b/tools/perf/util/probe-event.c
index 7ea13f4..6c50d9f 100644
--- a/tools/perf/util/probe-event.c
+++ b/tools/perf/util/probe-event.c
@@ -268,21 +268,6 @@
}
/*
- * NOTE:
- * '.gnu.linkonce.this_module' section of kernel module elf directly
- * maps to 'struct module' from linux/module.h. This section contains
- * actual module name which will be used by kernel after loading it.
- * But, we cannot use 'struct module' here since linux/module.h is not
- * exposed to user-space. Offset of 'name' has remained same from long
- * time, so hardcoding it here.
- */
-#ifdef __LP64__
-#define MOD_NAME_OFFSET 24
-#else
-#define MOD_NAME_OFFSET 12
-#endif
-
-/*
* @module can be module name of module file path. In case of path,
* inspect elf and find out what is actual module name.
* Caller has to free mod_name after using it.
@@ -296,6 +281,7 @@
Elf_Data *data;
Elf_Scn *sec;
char *mod_name = NULL;
+ int name_offset;
fd = open(module, O_RDONLY);
if (fd < 0)
@@ -317,7 +303,21 @@
if (!data || !data->d_buf)
goto ret_err;
- mod_name = strdup((char *)data->d_buf + MOD_NAME_OFFSET);
+ /*
+ * NOTE:
+ * '.gnu.linkonce.this_module' section of kernel module elf directly
+ * maps to 'struct module' from linux/module.h. This section contains
+ * actual module name which will be used by kernel after loading it.
+ * But, we cannot use 'struct module' here since linux/module.h is not
+ * exposed to user-space. Offset of 'name' has remained same from long
+ * time, so hardcoding it here.
+ */
+ if (ehdr.e_ident[EI_CLASS] == ELFCLASS32)
+ name_offset = 12;
+ else /* expect ELFCLASS64 by default */
+ name_offset = 24;
+
+ mod_name = strdup((char *)data->d_buf + name_offset);
ret_err:
elf_end(elf);
diff --git a/tools/perf/util/symbol-elf.c b/tools/perf/util/symbol-elf.c
index 99400b0..adbc6c0 100644
--- a/tools/perf/util/symbol-elf.c
+++ b/tools/perf/util/symbol-elf.c
@@ -537,6 +537,12 @@
break;
} else {
int n = namesz + descsz;
+
+ if (n > (int)sizeof(bf)) {
+ n = sizeof(bf);
+ pr_debug("%s: truncating reading of build id in sysfs file %s: n_namesz=%u, n_descsz=%u.\n",
+ __func__, filename, nhdr.n_namesz, nhdr.n_descsz);
+ }
if (read(fd, bf, n) != n)
break;
}