Merge "Merge android-4.9.186 (cd46375) into msm-4.9"
diff --git a/AndroidKernel.mk b/AndroidKernel.mk
index 790e713..1c2f92c 100644
--- a/AndroidKernel.mk
+++ b/AndroidKernel.mk
@@ -210,14 +210,31 @@
echo $(KERNEL_CONFIG_OVERRIDE) >> $(KERNEL_OUT)/.config; \
$(MAKE) -C $(TARGET_KERNEL_SOURCE) O=$(BUILD_ROOT_LOC)$(KERNEL_OUT) $(KERNEL_MAKE_ENV) ARCH=$(KERNEL_ARCH) CROSS_COMPILE=$(KERNEL_CROSS_COMPILE) $(real_cc) oldconfig; fi
-$(TARGET_PREBUILT_INT_KERNEL): $(KERNEL_OUT) $(KERNEL_HEADERS_INSTALL)
+ifeq ($(TARGET_KERNEL_APPEND_DTB), true)
+TARGET_PREBUILT_INT_KERNEL_IMAGE := $(KERNEL_OUT)/arch/$(KERNEL_ARCH)/boot/Image
+$(TARGET_PREBUILT_INT_KERNEL_IMAGE): $(KERNEL_USR)
+$(TARGET_PREBUILT_INT_KERNEL_IMAGE): $(KERNEL_OUT) $(KERNEL_HEADERS_INSTALL)
+ $(hide) echo "Building kernel modules..."
+ $(MAKE) -C $(TARGET_KERNEL_SOURCE) O=$(BUILD_ROOT_LOC)$(KERNEL_OUT) $(KERNEL_MAKE_ENV) ARCH=$(KERNEL_ARCH) CROSS_COMPILE=$(KERNEL_CROSS_COMPILE) $(real_cc) $(KERNEL_CFLAGS) Image
+ $(MAKE) -C $(TARGET_KERNEL_SOURCE) O=$(BUILD_ROOT_LOC)$(KERNEL_OUT) $(KERNEL_MAKE_ENV) ARCH=$(KERNEL_ARCH) CROSS_COMPILE=$(KERNEL_CROSS_COMPILE) $(real_cc) $(KERNEL_CFLAGS) modules
+ $(MAKE) -C $(TARGET_KERNEL_SOURCE) O=$(BUILD_ROOT_LOC)$(KERNEL_OUT) INSTALL_MOD_PATH=$(BUILD_ROOT_LOC)../$(KERNEL_MODULES_INSTALL) INSTALL_MOD_STRIP=1 $(KERNEL_MAKE_ENV) ARCH=$(KERNEL_ARCH) CROSS_COMPILE=$(KERNEL_CROSS_COMPILE) $(real_cc) modules_install
+ $(mv-modules)
+ $(clean-module-folder)
+
+$(TARGET_PREBUILT_INT_KERNEL): $(TARGET_PREBUILT_INT_KERNEL_IMAGE)
$(hide) echo "Building kernel..."
$(hide) rm -rf $(KERNEL_OUT)/arch/$(KERNEL_ARCH)/boot/dts
$(MAKE) -C $(TARGET_KERNEL_SOURCE) O=$(BUILD_ROOT_LOC)$(KERNEL_OUT) $(KERNEL_MAKE_ENV) ARCH=$(KERNEL_ARCH) CROSS_COMPILE=$(KERNEL_CROSS_COMPILE) $(real_cc) $(KERNEL_CFLAGS)
+else
+TARGET_PREBUILT_INT_KERNEL_IMAGE := $(TARGET_PREBUILT_INT_KERNEL)
+$(TARGET_PREBUILT_INT_KERNEL): $(KERNEL_OUT) $(KERNEL_HEADERS_INSTALL)
+ $(hide) echo "Building kernel..."
+ $(MAKE) -C $(TARGET_KERNEL_SOURCE) O=$(BUILD_ROOT_LOC)$(KERNEL_OUT) $(KERNEL_MAKE_ENV) ARCH=$(KERNEL_ARCH) CROSS_COMPILE=$(KERNEL_CROSS_COMPILE) $(real_cc) $(KERNEL_CFLAGS)
$(MAKE) -C $(TARGET_KERNEL_SOURCE) O=$(BUILD_ROOT_LOC)$(KERNEL_OUT) $(KERNEL_MAKE_ENV) ARCH=$(KERNEL_ARCH) CROSS_COMPILE=$(KERNEL_CROSS_COMPILE) $(real_cc) $(KERNEL_CFLAGS) modules
$(MAKE) -C $(TARGET_KERNEL_SOURCE) O=$(BUILD_ROOT_LOC)$(KERNEL_OUT) INSTALL_MOD_PATH=$(BUILD_ROOT_LOC)../$(KERNEL_MODULES_INSTALL) INSTALL_MOD_STRIP=1 $(KERNEL_MAKE_ENV) ARCH=$(KERNEL_ARCH) CROSS_COMPILE=$(KERNEL_CROSS_COMPILE) $(real_cc) modules_install
$(mv-modules)
$(clean-module-folder)
+endif
$(KERNEL_HEADERS_INSTALL): $(KERNEL_OUT)
$(hide) if [ ! -z "$(KERNEL_HEADER_DEFCONFIG)" ]; then \
diff --git a/Documentation/devicetree/bindings/net/can/microchip,mcp25xxfd.txt b/Documentation/devicetree/bindings/net/can/microchip,mcp25xxfd.txt
new file mode 100644
index 0000000..716b0a1
--- /dev/null
+++ b/Documentation/devicetree/bindings/net/can/microchip,mcp25xxfd.txt
@@ -0,0 +1,31 @@
+* Microchip MCP2517 stand-alone CAN controller device tree bindings
+
+Required properties:
+ - compatible: Should be one of the following:
+ - "microchip,mcp2517fd" for MCP2517fd.
+ - reg: SPI chip select.
+ - clocks: The clock feeding the CAN controller.
+ - interrupt-parent: The parent interrupt controller.
+ - interrupts: Should contain IRQ line for the CAN controller.
+ - gpio-controller: Marks the device node as a GPIO controller
+
+Optional properties:
+ - vdd-supply: Regulator that powers the CAN controller.
+ - xceiver-supply: Regulator that powers the CAN transceiver.
+ - microchip,clock_out_div = <0|1|2|4|10>: Clock output pin divider
+ 0 = Start of Frame output
+ default: 10
+ - microchip,clock_div2: bool: divide the internal clock by 2
+ - gpio_opendrain: bool: enable open-drain for all pins (except cantx)
+
+Example:
+ can0: can@1 {
+ compatible = "microchip,mcp2515";
+ reg = <1>;
+ clocks = <&clk24m>;
+ interrupt-parent = <&gpio4>;
+ interrupts = <13 0x8>;
+ vdd-supply = <®5v0>;
+ xceiver-supply = <®5v0>;
+ gpio-controller;
+ };
diff --git a/arch/arm/boot/dts/qcom/sa415m-ccard-pcie-ep.dts b/arch/arm/boot/dts/qcom/sa415m-ccard-pcie-ep.dts
index c2a447b..2eaca02 100644
--- a/arch/arm/boot/dts/qcom/sa415m-ccard-pcie-ep.dts
+++ b/arch/arm/boot/dts/qcom/sa415m-ccard-pcie-ep.dts
@@ -31,6 +31,8 @@
&ipa_hw {
qcom,use-ipa-in-mhi-mode;
+ qcom,ipa-config-is-auto;
+ qcom,mhi-event-ring-id-limits = <7 11>; /* start and end */
};
&cnss_pcie {
diff --git a/arch/arm/boot/dts/qcom/sa415m-ccard-usb-ep.dts b/arch/arm/boot/dts/qcom/sa415m-ccard-usb-ep.dts
index 1cf3a55..70e2849 100644
--- a/arch/arm/boot/dts/qcom/sa415m-ccard-usb-ep.dts
+++ b/arch/arm/boot/dts/qcom/sa415m-ccard-usb-ep.dts
@@ -20,3 +20,8 @@
"qcom,sdxpoorwills", "qcom,ccard";
qcom,board-id = <25 2>, <25 0x102>;
};
+
+&ipa_hw {
+ qcom,ipa-config-is-auto;
+};
+
diff --git a/arch/arm/boot/dts/qcom/sa415m-ttp-usb-ep.dts b/arch/arm/boot/dts/qcom/sa415m-ttp-usb-ep.dts
index 802e8319..ffbc8de 100644
--- a/arch/arm/boot/dts/qcom/sa415m-ttp-usb-ep.dts
+++ b/arch/arm/boot/dts/qcom/sa415m-ttp-usb-ep.dts
@@ -29,3 +29,8 @@
&mss_mem {
reg = <0x86400000 0x9300000>;
};
+
+&ipa_hw {
+ qcom,ipa-config-is-auto;
+};
+
diff --git a/arch/arm/boot/dts/qcom/sa415m-ttp.dtsi b/arch/arm/boot/dts/qcom/sa415m-ttp.dtsi
index 27d4437..b0d746a 100644
--- a/arch/arm/boot/dts/qcom/sa415m-ttp.dtsi
+++ b/arch/arm/boot/dts/qcom/sa415m-ttp.dtsi
@@ -20,10 +20,17 @@
status = "okay";
};
+&sdhc_1 {
+ qcom,nonremovable;
+};
+
&usb {
status = "okay";
qcom,connector-type-uAB;
extcon = <0>, <0>, <0>, <&vbus_detect>;
+ dwc3@a600000 {
+ normal-eps-in-gsi-mode;
+ };
};
&soc {
diff --git a/arch/arm/configs/msm8909_defconfig b/arch/arm/configs/msm8909_defconfig
index b903f85..575cf5e 100755
--- a/arch/arm/configs/msm8909_defconfig
+++ b/arch/arm/configs/msm8909_defconfig
@@ -384,6 +384,8 @@
CONFIG_USB_ACM=y
CONFIG_USB_STORAGE=y
CONFIG_USB_SERIAL=y
+CONFIG_USB_SERIAL_CP210X=y
+CONFIG_USB_SERIAL_FTDI_SIO=y
CONFIG_USB_EHSET_TEST_FIXTURE=y
CONFIG_USB_GADGET=y
CONFIG_USB_GADGET_DEBUG_FILES=y
diff --git a/arch/arm/configs/msm8937-perf_defconfig b/arch/arm/configs/msm8937-perf_defconfig
index 655d4db..2feff14 100755
--- a/arch/arm/configs/msm8937-perf_defconfig
+++ b/arch/arm/configs/msm8937-perf_defconfig
@@ -554,6 +554,7 @@
CONFIG_MSM_RMNET_BAM=y
CONFIG_MSM_MDSS_PLL=y
CONFIG_REMOTE_SPINLOCK_MSM=y
+CONFIG_MSM_TIMER_LEAP=y
CONFIG_MAILBOX=y
CONFIG_ARM_SMMU=y
CONFIG_QCOM_LAZY_MAPPING=y
diff --git a/arch/arm/configs/msm8937_defconfig b/arch/arm/configs/msm8937_defconfig
index 6e9f546..fa4dc50 100755
--- a/arch/arm/configs/msm8937_defconfig
+++ b/arch/arm/configs/msm8937_defconfig
@@ -565,6 +565,7 @@
CONFIG_MSM_RMNET_BAM=y
CONFIG_MSM_MDSS_PLL=y
CONFIG_REMOTE_SPINLOCK_MSM=y
+CONFIG_MSM_TIMER_LEAP=y
CONFIG_MAILBOX=y
CONFIG_ARM_SMMU=y
CONFIG_QCOM_LAZY_MAPPING=y
diff --git a/arch/arm/configs/msm8937go-perf_defconfig b/arch/arm/configs/msm8937go-perf_defconfig
index 6ab6c96..8b9fd07 100755
--- a/arch/arm/configs/msm8937go-perf_defconfig
+++ b/arch/arm/configs/msm8937go-perf_defconfig
@@ -548,6 +548,7 @@
CONFIG_MSM_RMNET_BAM=y
CONFIG_MSM_MDSS_PLL=y
CONFIG_REMOTE_SPINLOCK_MSM=y
+CONFIG_MSM_TIMER_LEAP=y
CONFIG_MAILBOX=y
CONFIG_ARM_SMMU=y
CONFIG_QCOM_LAZY_MAPPING=y
diff --git a/arch/arm/configs/msm8937go_defconfig b/arch/arm/configs/msm8937go_defconfig
index 41f11be..4ebb26b 100755
--- a/arch/arm/configs/msm8937go_defconfig
+++ b/arch/arm/configs/msm8937go_defconfig
@@ -557,6 +557,7 @@
CONFIG_MSM_RMNET_BAM=y
CONFIG_MSM_MDSS_PLL=y
CONFIG_REMOTE_SPINLOCK_MSM=y
+CONFIG_MSM_TIMER_LEAP=y
CONFIG_MAILBOX=y
CONFIG_ARM_SMMU=y
CONFIG_QCOM_LAZY_MAPPING=y
diff --git a/arch/arm/configs/msm8953-perf_defconfig b/arch/arm/configs/msm8953-perf_defconfig
index d072b67..e008aa1 100755
--- a/arch/arm/configs/msm8953-perf_defconfig
+++ b/arch/arm/configs/msm8953-perf_defconfig
@@ -555,6 +555,7 @@
CONFIG_MSM_RMNET_BAM=y
CONFIG_MSM_MDSS_PLL=y
CONFIG_REMOTE_SPINLOCK_MSM=y
+CONFIG_MSM_TIMER_LEAP=y
CONFIG_MAILBOX=y
CONFIG_ARM_SMMU=y
CONFIG_QCOM_LAZY_MAPPING=y
diff --git a/arch/arm/configs/msm8953_defconfig b/arch/arm/configs/msm8953_defconfig
index 8ba9cce..a4ccfb7 100755
--- a/arch/arm/configs/msm8953_defconfig
+++ b/arch/arm/configs/msm8953_defconfig
@@ -565,6 +565,7 @@
CONFIG_MSM_RMNET_BAM=y
CONFIG_MSM_MDSS_PLL=y
CONFIG_REMOTE_SPINLOCK_MSM=y
+CONFIG_MSM_TIMER_LEAP=y
CONFIG_MAILBOX=y
CONFIG_ARM_SMMU=y
CONFIG_QCOM_LAZY_MAPPING=y
diff --git a/arch/arm/configs/sa415m-perf_defconfig b/arch/arm/configs/sa415m-perf_defconfig
index a517020..5a9b777 100644
--- a/arch/arm/configs/sa415m-perf_defconfig
+++ b/arch/arm/configs/sa415m-perf_defconfig
@@ -229,7 +229,7 @@
CONFIG_CLD_LL_CORE=y
CONFIG_CNSS_GENL=y
# CONFIG_INPUT_MOUSEDEV is not set
-CONFIG_INPUT_EVDEV=y
+CONFIG_INPUT_EVDEV=m
# CONFIG_INPUT_KEYBOARD is not set
# CONFIG_INPUT_MOUSE is not set
CONFIG_INPUT_MISC=y
diff --git a/arch/arm/configs/sa415m_defconfig b/arch/arm/configs/sa415m_defconfig
index e10d4ef..c3ec5c5 100644
--- a/arch/arm/configs/sa415m_defconfig
+++ b/arch/arm/configs/sa415m_defconfig
@@ -230,7 +230,7 @@
CONFIG_CLD_LL_CORE=y
CONFIG_CNSS_GENL=y
# CONFIG_INPUT_MOUSEDEV is not set
-CONFIG_INPUT_EVDEV=y
+CONFIG_INPUT_EVDEV=m
# CONFIG_INPUT_KEYBOARD is not set
# CONFIG_INPUT_MOUSE is not set
CONFIG_INPUT_MISC=y
@@ -445,6 +445,7 @@
CONFIG_PWM_QPNP=y
CONFIG_QCOM_SHOW_RESUME_IRQ=y
CONFIG_ANDROID=y
+CONFIG_MSM_TZ_LOG=y
CONFIG_EXT3_FS=y
CONFIG_EXT4_FS_SECURITY=y
CONFIG_VFAT_FS=y
diff --git a/arch/arm/configs/sdm670-perf_defconfig b/arch/arm/configs/sdm670-perf_defconfig
index d597ff0..a4d1631 100755
--- a/arch/arm/configs/sdm670-perf_defconfig
+++ b/arch/arm/configs/sdm670-perf_defconfig
@@ -99,9 +99,11 @@
CONFIG_IP_ROUTE_VERBOSE=y
CONFIG_IP_PNP=y
CONFIG_IP_PNP_DHCP=y
+CONFIG_SYN_COOKIES=y
CONFIG_INET_AH=y
CONFIG_INET_ESP=y
CONFIG_INET_IPCOMP=y
+CONFIG_INET_UDP_DIAG=y
CONFIG_INET_DIAG_DESTROY=y
CONFIG_IPV6_ROUTER_PREF=y
CONFIG_IPV6_ROUTE_INFO=y
@@ -117,7 +119,6 @@
CONFIG_NF_CONNTRACK_SECMARK=y
CONFIG_NF_CONNTRACK_EVENTS=y
CONFIG_NF_CT_PROTO_DCCP=y
-CONFIG_NF_CT_PROTO_SCTP=y
CONFIG_NF_CT_PROTO_UDPLITE=y
CONFIG_NF_CONNTRACK_AMANDA=y
CONFIG_NF_CONNTRACK_FTP=y
@@ -163,6 +164,7 @@
CONFIG_NETFILTER_XT_MATCH_PKTTYPE=y
CONFIG_NETFILTER_XT_MATCH_QUOTA=y
CONFIG_NETFILTER_XT_MATCH_QUOTA2=y
+# CONFIG_NETFILTER_XT_MATCH_SCTP is not set
CONFIG_NETFILTER_XT_MATCH_SOCKET=y
CONFIG_NETFILTER_XT_MATCH_STATE=y
CONFIG_NETFILTER_XT_MATCH_STATISTIC=y
@@ -196,6 +198,7 @@
CONFIG_IP6_NF_RAW=y
CONFIG_BRIDGE_NF_EBTABLES=y
CONFIG_BRIDGE_EBT_BROUTE=y
+CONFIG_IP_SCTP=y
CONFIG_L2TP=y
CONFIG_L2TP_V3=y
CONFIG_L2TP_IP=y
@@ -465,6 +468,7 @@
CONFIG_MSM_CLK_AOP_QMP=y
CONFIG_QCOM_MDSS_PLL=y
CONFIG_REMOTE_SPINLOCK_MSM=y
+CONFIG_MSM_TIMER_LEAP=y
CONFIG_MSM_QMP=y
CONFIG_ARM_SMMU=y
CONFIG_QCOM_LAZY_MAPPING=y
diff --git a/arch/arm/configs/sdm670_defconfig b/arch/arm/configs/sdm670_defconfig
index 83aaf9b..8d9d85b 100755
--- a/arch/arm/configs/sdm670_defconfig
+++ b/arch/arm/configs/sdm670_defconfig
@@ -102,9 +102,11 @@
CONFIG_IP_ROUTE_VERBOSE=y
CONFIG_IP_PNP=y
CONFIG_IP_PNP_DHCP=y
+CONFIG_SYN_COOKIES=y
CONFIG_INET_AH=y
CONFIG_INET_ESP=y
CONFIG_INET_IPCOMP=y
+CONFIG_INET_UDP_DIAG=y
CONFIG_INET_DIAG_DESTROY=y
CONFIG_IPV6_ROUTER_PREF=y
CONFIG_IPV6_ROUTE_INFO=y
@@ -120,7 +122,6 @@
CONFIG_NF_CONNTRACK_SECMARK=y
CONFIG_NF_CONNTRACK_EVENTS=y
CONFIG_NF_CT_PROTO_DCCP=y
-CONFIG_NF_CT_PROTO_SCTP=y
CONFIG_NF_CT_PROTO_UDPLITE=y
CONFIG_NF_CONNTRACK_AMANDA=y
CONFIG_NF_CONNTRACK_FTP=y
@@ -167,6 +168,7 @@
CONFIG_NETFILTER_XT_MATCH_QUOTA=y
CONFIG_NETFILTER_XT_MATCH_QUOTA2=y
CONFIG_NETFILTER_XT_MATCH_QUOTA2_LOG=y
+# CONFIG_NETFILTER_XT_MATCH_SCTP is not set
CONFIG_NETFILTER_XT_MATCH_SOCKET=y
CONFIG_NETFILTER_XT_MATCH_STATE=y
CONFIG_NETFILTER_XT_MATCH_STATISTIC=y
@@ -200,6 +202,7 @@
CONFIG_IP6_NF_RAW=y
CONFIG_BRIDGE_NF_EBTABLES=y
CONFIG_BRIDGE_EBT_BROUTE=y
+CONFIG_IP_SCTP=y
CONFIG_L2TP=y
CONFIG_L2TP_DEBUGFS=y
CONFIG_L2TP_V3=y
@@ -477,6 +480,7 @@
CONFIG_MSM_CLK_AOP_QMP=y
CONFIG_QCOM_MDSS_PLL=y
CONFIG_REMOTE_SPINLOCK_MSM=y
+CONFIG_MSM_TIMER_LEAP=y
CONFIG_MSM_QMP=y
CONFIG_ARM_SMMU=y
CONFIG_QCOM_LAZY_MAPPING=y
diff --git a/arch/arm/include/asm/arch_timer.h b/arch/arm/include/asm/arch_timer.h
index d4ebf56..1d85857 100644
--- a/arch/arm/include/asm/arch_timer.h
+++ b/arch/arm/include/asm/arch_timer.h
@@ -92,7 +92,14 @@
u64 cval;
isb();
+#if IS_ENABLED(CONFIG_MSM_TIMER_LEAP)
+#define L32_BITS 0x00000000FFFFFFFF
+ do {
+ asm volatile("mrrc p15, 1, %Q0, %R0, c14" : "=r" (cval));
+ } while ((cval & L32_BITS) == L32_BITS);
+#else
asm volatile("mrrc p15, 1, %Q0, %R0, c14" : "=r" (cval));
+#endif
return cval;
}
diff --git a/arch/arm64/boot/dts/qcom/dsi-panel-truly-rm69090-qvga-cmd.dtsi b/arch/arm64/boot/dts/qcom/dsi-panel-truly-rm69090-qvga-cmd.dtsi
new file mode 100644
index 0000000..9c0ffc5
--- /dev/null
+++ b/arch/arm64/boot/dts/qcom/dsi-panel-truly-rm69090-qvga-cmd.dtsi
@@ -0,0 +1,87 @@
+/*
+ * Copyright (c) 2019, 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.
+ */
+
+&mdss_mdp {
+ dsi_truly_rm69090_qvga_cmd: qcom,mdss_dsi_truly_rm69090_qvga_cmd {
+ qcom,mdss-dsi-panel-name = "rm69090 qvga cmd mode dsi panel";
+ qcom,mdss-dsi-panel-controller = <&mdss_dsi0>;
+ qcom,mdss-dsi-panel-type = "dsi_cmd_mode";
+ qcom,mdss-dsi-panel-destination = "display_1";
+ qcom,mdss-dsi-panel-framerate = <60>;
+ qcom,mdss-dsi-virtual-channel-id = <0>;
+ qcom,mdss-dsi-stream = <0>;
+ qcom,mdss-dsi-panel-width = <368>;
+ qcom,mdss-dsi-panel-height = <448>;
+ qcom,mdss-dsi-h-front-porch = <40>;
+ qcom,mdss-dsi-h-back-porch = <20>;
+ qcom,mdss-dsi-h-pulse-width = <2>;
+ qcom,mdss-dsi-h-sync-skew = <0>;
+ qcom,mdss-dsi-v-back-porch = <8>;
+ qcom,mdss-dsi-v-front-porch = <6>;
+ qcom,mdss-dsi-v-pulse-width = <2>;
+ qcom,mdss-dsi-h-left-border = <0>;
+ qcom,mdss-dsi-h-right-border = <0>;
+ qcom,mdss-dsi-v-top-border = <0>;
+ qcom,mdss-dsi-v-bottom-border = <0>;
+ qcom,mdss-dsi-bpp = <24>;
+ qcom,mdss-dsi-color-order = "rgb_swap_rgb";
+ qcom,mdss-dsi-underflow-color = <0xff>;
+ qcom,mdss-dsi-border-color = <0>;
+ qcom,mdss-dsi-pixel-packing = "tight";
+ qcom,mdss-dsi-pixel-alignment = <0>;
+ qcom,mdss-dsi-on-command = [
+ 15 01 00 00 00 00 02 FE 00
+ 15 01 00 00 00 00 02 35 00
+ 15 01 00 00 00 00 02 51 80
+ 39 01 00 00 00 00 05 2A 00 10 01 7F
+ 39 01 00 00 00 00 05 2B 00 00 01 BF
+ 05 01 00 00 78 00 02 11 00
+ 05 01 00 00 40 00 02 29 00
+ ];
+ qcom,mdss-dsi-off-command = [
+ 05 01 00 00 28 00 02 28 00
+ 05 01 00 00 78 00 02 10 00
+ 15 01 00 00 00 00 02 4F 01
+ ];
+ qcom,mdss-dsi-on-command-state = "dsi_lp_mode";
+ qcom,mdss-dsi-off-command-state = "dsi_hs_mode";
+ qcom,mdss-dsi-idle-on-command = [
+ 05 01 00 00 00 00 01 39 /* Idle-Mode On */
+ ];
+ qcom,mdss-dsi-idle-off-command = [
+ 05 01 00 00 00 00 01 38 /* Idle-Mode Off */
+ ];
+ qcom,mdss-dsi-idle-on-command-state = "dsi_hs_mode";
+ qcom,mdss-dsi-idle-off-command-state = "dsi_lp_mode";
+ qcom,mdss-dsi-h-sync-pulse = <1>;
+ qcom,mdss-dsi-traffic-mode = "burst_mode";
+ qcom,mdss-dsi-lane-map = "lane_map_0123";
+ qcom,mdss-dsi-bllp-eof-power-mode;
+ qcom,mdss-dsi-bllp-power-mode;
+ qcom,mdss-dsi-lane-0-state;
+ qcom,mdss-dsi-te-pin-select = <1>;
+ qcom,mdss-dsi-te-dcs-command = <1>;
+ qcom,mdss-dsi-te-check-enable;
+ qcom,mdss-dsi-te-using-te-pin;
+ qcom,mdss-dsi-panel-timings = [7d 25 1d 00 37 33
+ 22 27 1e 03 04 00];
+ qcom,mdss-dsi-t-clk-post = <0x09>;
+ qcom,mdss-dsi-t-clk-pre = <0x2c>;
+ qcom,mdss-dsi-bl-min-level = <1>;
+ qcom,mdss-dsi-bl-max-level = <255>;
+ qcom,mdss-dsi-dma-trigger = "trigger_sw";
+ qcom,mdss-dsi-mdp-trigger = "none";
+ qcom,mdss-dsi-bl-pmic-control-type = "bl_ctrl_dcs";
+ qcom,mdss-dsi-reset-sequence = <1 1>, <0 12>, <1 12>;
+ };
+};
diff --git a/arch/arm64/boot/dts/qcom/pm660-rpm-regulator.dtsi b/arch/arm64/boot/dts/qcom/pm660-rpm-regulator.dtsi
index 6d384fc..010694d 100644
--- a/arch/arm64/boot/dts/qcom/pm660-rpm-regulator.dtsi
+++ b/arch/arm64/boot/dts/qcom/pm660-rpm-regulator.dtsi
@@ -19,9 +19,9 @@
qcom,hpm-min-load = <100000>;
status = "disabled";
- regulator-s2 {
+ regulator-s1 {
compatible = "qcom,rpm-smd-regulator";
- regulator-name = "pm660_s2";
+ regulator-name = "pm660_s1";
qcom,set = <3>;
status = "disabled";
};
diff --git a/arch/arm64/boot/dts/qcom/sdm429-qrd-spyro-evt-overlay.dts b/arch/arm64/boot/dts/qcom/sdm429-qrd-spyro-evt-overlay.dts
deleted file mode 100644
index a90c4ba..0000000
--- a/arch/arm64/boot/dts/qcom/sdm429-qrd-spyro-evt-overlay.dts
+++ /dev/null
@@ -1,22 +0,0 @@
-/*
- * Copyright (c) 2019, 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 "sdm429-qrd-spyro-evt.dtsi"
-
-/ {
- model = "Qualcomm Technologies, Inc. SDM429 QRD Spyro Overlay";
- qcom,board-id = <0xb 6>;
-};
diff --git a/arch/arm64/boot/dts/qcom/sdm429-qrd-spyro-evt.dts b/arch/arm64/boot/dts/qcom/sdm429-qrd-spyro-evt.dts
deleted file mode 100644
index c9d952d..0000000
--- a/arch/arm64/boot/dts/qcom/sdm429-qrd-spyro-evt.dts
+++ /dev/null
@@ -1,24 +0,0 @@
-/*
- * Copyright (c) 2019, 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 "sdm429.dtsi"
-#include "sdm429-qrd-spyro-evt.dtsi"
-
-/ {
- model = "Qualcomm Technologies, Inc. SDM429 QRD Spyro";
- compatible = "qcom,sdm429-qrd", "qcom,sdm429", "qcom,qrd";
- qcom,board-id = <0xb 6>;
- qcom,pmic-id = <0x0002001b 0x0 0x0 0x0>;
-};
diff --git a/arch/arm64/boot/dts/qcom/sdm429-qrd-spyro-evt.dtsi b/arch/arm64/boot/dts/qcom/sdm429-qrd-spyro-evt.dtsi
deleted file mode 100644
index eaa844b..0000000
--- a/arch/arm64/boot/dts/qcom/sdm429-qrd-spyro-evt.dtsi
+++ /dev/null
@@ -1,667 +0,0 @@
-/*
- * Copyright (c) 2019, 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 "sdm439-qrd.dtsi"
-#include "sdm429w-pm660.dtsi"
-
-&gpio_key_active {
- mux {
- pins = "gpio91", "gpio127", "gpio128", "gpio35", "gpio126";
- function = "gpio";
- };
-
- config {
- pins = "gpio91", "gpio127", "gpio128", "gpio35", "gpio126";
- drive-strength = <2>;
- bias-pull-up;
- };
-};
-
-&gpio_key_suspend {
- mux {
- pins = "gpio91", "gpio127", "gpio128", "gpio35", "gpio126";
- function = "gpio";
- };
-
- config {
- pins = "gpio91", "gpio127", "gpio128", "gpio35", "gpio126";
- drive-strength = <2>;
- bias-pull-up;
- };
-};
-
-&cdc_pdm_lines_act {
- mux {
- pins = "gpio68", "gpio73", "gpio74";
- function = "cdc_pdm0";
- };
-
- config {
- pins = "gpio68", "gpio73", "gpio74";
- drive-strength = <16>;
- };
-};
-
-&cdc_pdm_lines_sus {
- mux {
- pins = "gpio68", "gpio73", "gpio74";
- function = "cdc_pdm0";
- };
-
- config {
- pins = "gpio68", "gpio73", "gpio74";
- drive-strength = <2>;
- bias-disable;
- };
-};
-
-&cam_sensor_rear_standby {
- /* STANDBY */
- mux {
- /delete-property/ pins;
- pins = "gpio92";
- function = "gpio";
- };
-
- config {
- /delete-property/ pins;
- pins = "gpio92";
- bias-disable; /* No PULL */
- drive-strength = <2>; /* 2 MA */
- };
-};
-
-&cam_sensor_rear_standby_sleep {
- /* STANDBY */
- mux {
- /delete-property/ pins;
- pins = "gpio92";
- function = "gpio";
- };
-
- config {
- /delete-property/ pins;
- pins = "gpio92";
- bias-disable; /* No PULL */
- drive-strength = <2>; /* 2 MA */
- };
-};
-
-&cam_sensor_rear_vana {
- /* VANA */
- mux {
- /delete-property/ pins;
- pins = "gpio58";
- function = "gpio";
- };
-
- config {
- /delete-property/ pins;
- pins = "gpio58";
- bias-disable; /* No PULL */
- drive-strength = <2>; /* 2 MA */
- };
-};
-
-&cam_sensor_rear_vana_sleep {
- /* VANA */
- mux {
- /delete-property/ pins;
- pins = "gpio58";
- function = "gpio";
- };
-
- config {
- /delete-property/ pins;
- pins = "gpio58";
- bias-disable; /* No PULL */
- drive-strength = <2>; /* 2 MA */
- };
-};
-&mdss_dsi0 {
- qcom,dsi-pref-prim-pan = <&dsi_edo_rm67162_qvga_cmd>;
- pinctrl-names = "mdss_default", "mdss_sleep";
- pinctrl-0 = <&mdss_dsi_active &mdss_te_active>;
- pinctrl-1 = <&mdss_dsi_suspend &mdss_te_suspend>;
- qcom,platform-te-gpio = <&tlmm 24 0>;
- qcom,platform-reset-gpio = <&tlmm 60 0>;
- qcom,platform-enable-gpio = <&tlmm 69 0>;
-};
-
-&dsi_edo_rm67162_qvga_cmd {
- /delete-property/ qcom,mdss-dsi-panel-timings;
- qcom,mdss-dsi-panel-timings-phy-12nm = [06 05 01 0A 00 03 01 0F];
- qcom,mdss-dsi-bl-pmic-control-type = "bl_ctrl_dcs";
- qcom,panel-supply-entries = <&dsi_pm660_panel_pwr_supply>;
-};
-&soc {
- /delete-node/ qcom,cci@1b0c000;
- cci: qcom,cci@1b0c000 {
- status = "ok";
- cell-index = <0>;
- compatible = "qcom,cci";
- reg = <0x1b0c000 0x4000>;
- #address-cells = <1>;
- #size-cells = <0>;
- reg-names = "cci";
- interrupts = <0 50 0>;
- interrupt-names = "cci";
- clocks = <&clock_gcc clk_gcc_camss_ispif_ahb_clk>,
- <&clock_gcc clk_cci_clk_src>,
- <&clock_gcc clk_gcc_camss_cci_ahb_clk>,
- <&clock_gcc clk_gcc_camss_cci_clk>,
- <&clock_gcc clk_gcc_camss_ahb_clk>,
- <&clock_gcc clk_gcc_camss_top_ahb_clk>;
- clock-names = "ispif_ahb_clk", "cci_src_clk",
- "cci_ahb_clk", "camss_cci_clk",
- "camss_ahb_clk", "camss_top_ahb_clk";
- qcom,clock-rates = <61540000 19200000 0 0 0 0>,
- <61540000 37500000 0 0 0 0>;
- pinctrl-names = "cci_default", "cci_suspend";
- pinctrl-0 = <&cci0_active &cci1_active>;
- pinctrl-1 = <&cci0_suspend &cci1_suspend>;
- gpios = <&tlmm 29 0>,
- <&tlmm 30 0>,
- <&tlmm 31 0>,
- <&tlmm 32 0>;
- qcom,gpio-tbl-num = <0 1 2 3>;
- qcom,gpio-tbl-flags = <1 1 1 1>;
- qcom,gpio-tbl-label = "CCI_I2C_DATA0",
- "CCI_I2C_CLK0",
- "CCI_I2C_DATA1",
- "CCI_I2C_CLK1";
- i2c_freq_100Khz: qcom,i2c_standard_mode {
- status = "disabled";
- };
- i2c_freq_400Khz: qcom,i2c_fast_mode {
- status = "disabled";
- };
- i2c_freq_custom: qcom,i2c_custom_mode {
- status = "disabled";
- };
-
- i2c_freq_1Mhz: qcom,i2c_fast_plus_mode {
- status = "disabled";
- };
- };
-};
-
-#include "sdm429w-camera-sensor-spyro.dtsi"
-
-&i2c_5 {
- status = "disabled";
-};
-
-&vol_up {
- gpios = <&tlmm 35 0x1>;
-};
-
-&gpio_keys {
- function_1: function_1 {
- label = "function_1";
- gpios = <&tlmm 127 0x1>;
- linux,input-type = <1>;
- linux,code = <116>;
- debounce-interval = <15>;
- linux,can-disable;
- gpio-key,wakeup;
- };
-
- function_2: function_2 {
- label = "function_2";
- gpios = <&tlmm 126 0x1>;
- linux,input-type = <1>;
- linux,code = <117>;
- debounce-interval = <15>;
- linux,can-disable;
- gpio-key,wakeup;
- };
-};
-
-&soc {
- /delete-node/ qcom,spm@b1d2000;
- qcom,spm@b1d2000 {
- compatible = "qcom,spm-v2";
- #address-cells = <1>;
- #size-cells = <1>;
- reg = <0xb1d2000 0x1000>;
- qcom,name = "system-cci";
- qcom,saw2-ver-reg = <0xfd0>;
- qcom,saw2-cfg = <0x14>;
- qcom,saw2-spm-dly= <0x3C102800>;
- qcom,saw2-spm-ctl = <0xe>;
- qcom,cpu-vctl-list = <&CPU0 &CPU1 &CPU2 &CPU3>;
- qcom,vctl-timeout-us = <500>;
- qcom,vctl-port = <0x0>;
- qcom,vctl-port-ub = <0x1>;
- qcom,pfm-port = <0x2>;
- };
-
-};
-
-
-&dsi_hx8399c_hd_vid {
- qcom,mdss-dsi-on-command = [
- 39 01 00 00 00 00 04
- b9 ff 83 99
- 39 01 00 00 00 00 02
- d2 88
- 39 01 00 00 00 00 0c
- b1 02 04 72 92 01
- 32 aa 11 11 52 57
- 39 01 00 00 00 00 10
- b2 00 80 80 cc 05 07 5a
- 11 10 10 00 1e 70 03 d4
- 39 01 00 00 00 00 2d
- b4 00 ff 59 59 01 ab 00
- 00 09 00 03 05 00 28 03
- 0b 0d 21 03 02 00 0c a3
- 80 59 59 02 ab 00 00 09
- 00 03 05 00 28 03 0b 0d
- 02 00 0c a3 01
- 39 01 00 00 05 00 22
- d3 00 0c 03 03 00 00 10
- 10 00 00 03 00 03 00 08
- 78 08 78 00 00 00 00 00
- 24 02 05 05 03 00 00 00
- 05 40
- 39 01 00 00 05 00 21
- d5 20 20 19 19 18 18 02
- 03 00 01 24 24 18 18 18
- 18 24 24 00 00 00 00 00
- 00 00 00 2f 2f 30 30 31
- 31
- 39 01 00 00 05 00 21
- d6 24 24 18 18 19 19 01
- 00 03 02 24 24 18 18 18
- 18 20 20 40 40 40 40 40
- 40 40 40 2f 2f 30 30 31
- 31
- 39 01 00 00 00 00 02
- bd 00
- 39 01 00 00 00 00 11
- d8 aa aa aa aa aa aa aa
- aa aa ba aa aa aa ba aa
- aa
- 39 01 00 00 00 00 02
- bd 01
- 39 01 00 00 00 00 11
- d8 00 00 00 00 00 00 00
- 00 82 ea aa aa 82 ea aa
- aa
- 39 01 00 00 00 00 02
- bd 02
- 39 01 00 00 00 00 09
- d8 ff ff c0 3f ff ff c0
- 3f
- 39 01 00 00 00 00 02
- bd 00
- 39 01 00 00 05 00 37
- e0 01 21 31 2d 66 6f 7b
- 75 7a 81 86 89 8c 90 95
- 97 9a a1 a2 aa 9e ad b0
- 5b 57 63 7a 01 21 31 2d
- 66 6f 7b 75 7a 81 86 89
- 8c 90 95 97 9a a1 a2 aa
- 9e ad b0 5b 57 63 7a
- 39 01 00 00 00 00 03
- b6 7e 7e
- 39 01 00 00 00 00 02
- cc 08
- 39 01 00 00 00 00 02
- 35 00
- 39 01 00 00 00 00 02
- dd 03
- 05 01 00 00 78 00 02 11 00
- 05 01 00 00 05 00 02 29 00];
- qcom,mdss-dsi-reset-sequence = <1 2>, <0 5>, <1 10>;
- };
-
-&firmware {
- android {
- compatible = "android,firmware";
- vbmeta {
- compatible = "android,vbmeta";
- parts = "vbmeta,boot,system,vendor,dtbo,recovery";
- };
- fstab {
- compatible = "android,fstab";
- vendor {
- compatible = "android,vendor";
- dev = "/dev/block/platform/soc/7824900.sdhci/by-name/vendor";
- type = "ext4";
- mnt_flags = "ro,barrier=1,discard";
- fsmgr_flags = "wait,avb";
- status = "ok";
- };
- system {
- compatible = "android,system";
- dev = "/dev/block/platform/soc/7824900.sdhci/by-name/system";
- type = "ext4";
- mnt_flags = "ro,barrier=1,discard";
- fsmgr_flags = "wait,avb";
- status = "ok";
- };
- };
- };
-};
-
-&i2c_4 {
- status = "ok";
-
- tsc@24 {
- compatible = "cy,cyttsp5_i2c_adapter";
- reg = <0x24>;
-
- interrupt-parent = <&tlmm>;
- interrupts = <65 0x2008>;
- cy,adapter_id = "cyttsp5_i2c_adapter";
- vcc_i2c-supply = <&L13A>;
- vdd-supply = <&L15A>;
- pinctrl-names = "pmx_ts_active", "pmx_ts_suspend",
- "pmx_ts_release";
- pinctrl-0 = <&ts_int_active &ts_reset_active>;
- pinctrl-1 = <&ts_int_suspend &ts_reset_suspend>;
- pinctrl-2 = <&ts_release>;
-
- cy,core {
- cy,name = "cyttsp5_core";
- cy,irq_gpio = <65>;
- cy,rst_gpio = <64>;
- cy,hid_desc_register = <1>;
- cy,flags = <4>;
- cy,easy_wakeup_gesture = <1>;
- cy,btn_keys = <172 139 158 217 114 115 212 116>;
- cy,btn_keys-tag = <0>;
-
- cy,mt {
- cy,name = "cyttsp5_mt";
-
- cy,inp_dev_name = "cyttsp5_mt";
- cy,flags = <0x28>;
- cy,abs =
- <0x35 0 320 0 0
- 0x36 0 360 0 0
- 0x3a 0 255 0 0
- 0xffff 0 255 0 0
- 0x39 0 15 0 0
- 0x30 0 255 0 0
- 0x31 0 255 0 0
- 0x34 0xffffff81 127 0 0
- 0x37 0 1 0 0
- 0x3b 0 255 0 0>;
-
- cy,vkeys_x = <320>;
- cy,vkeys_y = <360>;
-
- cy,virtual_keys =
- <158 1360 90 160 180
- 139 1360 270 160 180
- 172 1360 450 160 180
- 217 1360 630 160 180>;
- };
-
- cy,btn {
- cy,name = "cyttsp5_btn";
-
- cy,inp_dev_name = "cyttsp5_btn";
- };
-
- cy,proximity {
- cy,name = "cyttsp5_proximity";
-
- cy,inp_dev_name = "cyttsp5_proximity";
- cy,abs = <0x19 0 1 0 0>;
- };
- };
- };
-
-};
-
-&tlmm {
- pmx_ts_int_active {
- ts_int_active: ts_int_active {
- mux {
- pins = "gpio65";
- function = "gpio";
- };
-
- config {
- pins = "gpio65";
- drive-strength = <8>;
- bias-pull-up;
- };
- };
- };
-
- pmx_ts_int_suspend {
- ts_int_suspend: ts_int_suspend {
- mux {
- pins = "gpio65";
- function = "gpio";
- };
-
- config {
- pins = "gpio65";
- drive-strength = <2>;
- bias-pull-down;
- };
- };
- };
-
- pmx_ts_reset_active {
- ts_reset_active: ts_reset_active {
- mux {
- pins = "gpio64";
- function = "gpio";
- };
-
- config {
- pins = "gpio64";
- drive-strength = <8>;
- bias-pull-up;
- };
- };
- };
-
- pmx_ts_reset_suspend {
- ts_reset_suspend: ts_reset_suspend {
- mux {
- pins = "gpio64";
- function = "gpio";
- };
-
- config {
- pins = "gpio64";
- drive-strength = <2>;
- bias-pull-down;
- };
- };
- };
-
- pmx_ts_release {
- ts_release: ts_release {
- mux {
- pins = "gpio65", "gpio64";
- function = "gpio";
- };
-
- config {
- pins = "gpio65", "gpio64";
- drive-strength = <2>;
- bias-pull-down;
- };
- };
- };
-
- smart_pa_int {
- pa_int_default: pa_int_default {
- mux {
- pins = "gpio73", "gpio73";
- function = "gpio";
- };
-
- config {
- pins = "gpio73", "gpio73";
- drive-strength = <4>;
- bias-disable;
- };
- };
- };
-
- smart_pa_rst {
- pa_rst_default: pa_rst_default {
- mux {
- pins = "gpio68", "gpio68";
- function = "gpio";
- };
-
- config {
- pins = "gpio68", "gpio68";
- drive-strength = <4>;
- bias-disable;
- };
- };
- };
-
-};
-
-&modem_mem {
- reg = <0x0 0x86800000 0x0 0x5000000>;
-};
-
-&adsp_fw_mem {
- reg = <0x0 0x8b800000 0x0 0x1400000>;
-};
-
-&wcnss_fw_mem {
- reg = <0x0 0x8cc00000 0x0 0x700000>;
-};
-
-
-&int_codec {
- compatible = "qcom,msm8952-dig-asoc-snd";
- status = "okay";
- qcom,model = "sdm429-qrd-snd-card";
- qcom,msm-ext-pa = "quaternary";
- /delete-property/ qcom,split-a2dp;
- asoc-wsa-codec-names;
- asoc-wsa-codec-prefixes;
- ext_pa_aw8896;
- qcom,audio-routing =
- "CDC_CONN", "MCLK",
- "QUAT_MI2S_RX", "DIGITAL_REGULATOR",
- "TX_I2S_CLK", "DIGITAL_REGULATOR",
- "DMIC1", "Digital Mic1",
- "DMIC2", "Digital Mic2";
- qcom,cdc-dmic-gpios = <&cdc_dmic_gpios>;
- qcom,quat-mi2s-gpios = <&cdc_quat_mi2s_gpios>;
- qcom,msm-gpios =
- "quat_i2s",
- "dmic";
- qcom,pinctrl-names =
- "all_off",
- "quat_i2s_act",
- "dmic_act",
- "quat_i2s_dmic_act";
- pinctrl-names =
- "all_off",
- "quat_i2s_act",
- "dmic_act",
- "quat_i2s_dmic_act";
- pinctrl-0 = <&quat_mi2s_sleep &quat_mi2s_din_sleep
- &cdc_dmic0_clk_sus &cdc_dmic0_data_sus>;
- pinctrl-1 = <&quat_mi2s_active &quat_mi2s_din_active
- &cdc_dmic0_clk_sus &cdc_dmic0_data_sus>;
- pinctrl-2 = <&quat_mi2s_sleep &quat_mi2s_din_sleep
- &cdc_dmic0_clk_act &cdc_dmic0_data_act>;
- pinctrl-3 = <&quat_mi2s_active &quat_mi2s_din_active
- &cdc_dmic0_clk_act &cdc_dmic0_data_act>;
- /delete-property/qcom,cdc-us-euro-gpios;
- /delete-property/qcom,pri-mi2s-gpios;
- /delete-property/qcom,cdc-us-eu-gpios;
-
-
- asoc-codec = <&stub_codec>, <&msm_dig_codec>, <&ext_smart_pa>;
- asoc-codec-names = "msm-stub-codec.1", "msm-dig-codec", "ext-smart-pa";
-};
-
-&soc {
- msm_dig_codec: msm_dig_codec {
- compatible = "qcom,msm-digital-codec";
- reg = <0xc0f0000 0x0>;
- qcom,no-analog-codec;
- cdc-vdd-digital-supply = <&pm660_l9>;
- qcom,cdc-vdd-digital-voltage = <1800000 1800000>;
- qcom,cdc-vdd-digital-current = <10000>;
- qcom,cdc-on-demand-supplies = "cdc-vdd-digital";
- };
-
- cdc_dmic_gpios: cdc_dmic_pinctrl {
- compatible = "qcom,msm-cdc-pinctrl";
- pinctrl-names = "aud_active", "aud_sleep";
- pinctrl-0 = <&cdc_dmic0_clk_act &cdc_dmic0_data_act>;
- pinctrl-1 = <&cdc_dmic0_clk_sus &cdc_dmic0_data_sus>;
- };
-
- cdc_quat_mi2s_gpios: msm_cdc_pinctrl_quat {
- compatible = "qcom,msm-cdc-pinctrl";
- pinctrl-names = "aud_active", "aud_sleep";
- pinctrl-0 = <&quat_mi2s_active &quat_mi2s_din_active>;
- pinctrl-1 = <&quat_mi2s_sleep &quat_mi2s_din_sleep>;
- };
-};
-
-&wsa881x_i2c_f {
- status = "disabled";
-};
-
-&wsa881x_i2c_45 {
- status = "disabled";
-};
-
-&wsa881x_analog_vi_gpio {
- status = "disabled";
-};
-
-&wsa881x_analog_clk_gpio {
- status = "disabled";
-};
-
-&wsa881x_analog_reset_gpio {
- status = "disabled";
-};
-
-&cdc_us_euro_sw {
- status = "disabled";
-};
-
-&cdc_pri_mi2s_gpios {
- status = "disabled";
-};
-
-&cdc_quin_mi2s_gpios {
- status = "disabled";
-};
-
-&i2c_2 {
- ext_smart_pa: aw8896_smartpa@34 {
- compatible = "awinic,aw8896_smartpa";
- reg = <0x34>;
- reset-gpio = <&tlmm 68 0>;
- irq-gpio = <&tlmm 73 0>;
- pinctrl-names = "default";
- pinctrl-0 = <&pa_int_default &pa_rst_default>;
- status = "okay";
- };
-};
diff --git a/arch/arm64/boot/dts/qcom/sdm429-spyro-qrd-evt-camera.dtsi b/arch/arm64/boot/dts/qcom/sdm429-spyro-qrd-evt-camera.dtsi
index 5c15948..8d047ca 100644
--- a/arch/arm64/boot/dts/qcom/sdm429-spyro-qrd-evt-camera.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm429-spyro-qrd-evt-camera.dtsi
@@ -203,82 +203,40 @@
eeprom_spyro1: qcom,eeprom@1 {
cell-index = <1>;
reg = <0x1>;
- qcom,eeprom-name = "sunny_8865";
compatible = "qcom,eeprom";
- qcom,slave-addr = <0x6c>;
- qcom,cci-master = <0>;
- qcom,num-blocks = <8>;
-
- qcom,page0 = <1 0x0100 2 0x01 1 1>;
- qcom,poll0 = <0 0x0 2 0x0 1 0>;
- qcom,mem0 = <0 0x0 2 0x0 1 0>;
-
- qcom,page1 = <1 0x5002 2 0x00 1 0>;
- qcom,poll1 = <0 0x0 2 0x0 1 0>;
- qcom,mem1 = <0 0x0 2 0x0 1 0>;
-
- qcom,page2 = <1 0x3d84 2 0xc0 1 0>;
- qcom,poll2 = <0 0x0 2 0x0 1 0>;
- qcom,mem2 = <0 0x0 2 0x0 1 0>;
-
- qcom,page3 = <1 0x3d88 2 0x70 1 0>;
- qcom,poll3 = <0 0x0 2 0x0 1 0>;
- qcom,mem3 = <0 0x0 2 0x0 1 0>;
-
- qcom,page4 = <1 0x3d89 2 0x10 1 0>;
- qcom,poll4 = <0 0x0 2 0x0 1 0>;
- qcom,mem4 = <0 0x0 2 0x0 1 0>;
-
- qcom,page5 = <1 0x3d8a 2 0x70 1 0>;
- qcom,poll5 = <0 0x0 2 0x0 1 0>;
- qcom,mem5 = <0 0x0 2 0x0 1 0>;
-
- qcom,page6 = <1 0x3d8b 2 0xf4 1 0>;
- qcom,poll6 = <0 0x0 2 0x0 1 0>;
- qcom,mem6 = <0 0x0 2 0x0 1 0>;
-
- qcom,page7 = <1 0x3d81 2 0x01 1 10>;
- qcom,poll7 = <0 0x0 2 0x0 1 1>;
- qcom,mem7 = <1536 0x7010 2 0 1 0>;
+ qcom,cci-master = <1>;
cam_vdig-supply = <&pm660_l3>;
- cam_vana-supply = <&pm660_l7>;
- cam_vio-supply = <&pm660_l6>;
+ cam_vio-supply = <&pm660_l14>;
+ cam_vana-supply = <&pm660_s5>;
cam_vaf-supply = <&pm660_l17>;
- qcom,cam-vreg-name = "cam_vdig", "cam_vio",
- "cam_vana", "cam_vaf";
- qcom,cam-vreg-min-voltage = <1200000 0 2800000 2850000>;
- qcom,cam-vreg-max-voltage = <1200000 0 2800000 3200000>;
- qcom,cam-vreg-op-mode = <105000 0 80000 100000>;
- qcom,gpio-no-mux = <0>;
+ qcom,cam-vreg-name = "cam_vdig", "cam_vio", "cam_vana";
+ qcom,cam-vreg-min-voltage = <1200000 1800000 1420000>;
+ qcom,cam-vreg-max-voltage = <1200000 1800000 1420000>;
+ qcom,cam-vreg-op-mode = <200000 80000 80000>;
pinctrl-names = "cam_default", "cam_suspend";
- pinctrl-0 = <&cam_sensor_mclk2_default
- &cam_sensor_front1_default>;
- pinctrl-1 = <&cam_sensor_mclk2_sleep
- &cam_sensor_front1_sleep>;
- gpios = <&tlmm 28 0>,
- <&tlmm 40 0>,
- <&tlmm 39 0>;
- qcom,gpio-reset = <1>;
- qcom,gpio-standby = <2>;
- qcom,gpio-req-tbl-num = <0 1 2>;
- qcom,gpio-req-tbl-flags = <1 0 0>;
- qcom,gpio-req-tbl-label = "CAMIF_MCLK2",
- "CAM_RESET2",
- "CAM_STANDBY2";
- qcom,cam-power-seq-type = "sensor_vreg", "sensor_vreg",
- "sensor_vreg",
- "sensor_gpio", "sensor_gpio" , "sensor_clk";
- qcom,cam-power-seq-val = "cam_vdig", "cam_vana", "cam_vio",
- "sensor_gpio_reset", "sensor_gpio_standby",
- "sensor_cam_mclk";
- qcom,cam-power-seq-cfg-val = <1 1 1 1 1 24000000>;
- qcom,cam-power-seq-delay = <1 1 1 30 30 5>;
- status = "disabled";
- clocks = <&clock_gcc clk_mclk2_clk_src>,
- <&clock_gcc clk_gcc_camss_mclk2_clk>;
+ pinctrl-0 = <&cam_sensor_mclk1_default
+ &cam_sensor_front_default>;
+ pinctrl-1 = <&cam_sensor_mclk1_sleep
+ &cam_sensor_front_sleep>;
+ gpios = <&tlmm 27 0>,
+ <&tlmm 33 0>,
+ <&tlmm 66 0>,
+ <&tlmm 38 0>;
+ qcom,gpio-vana= <1>;
+ qcom,gpio-vdig= <2>;
+ qcom,gpio-reset = <3>;
+ qcom,gpio-req-tbl-num = <0 1 2 3>;
+ qcom,gpio-req-tbl-flags = <1 0 0 0>;
+ qcom,gpio-req-tbl-label = "CAMIF_MCLK1",
+ "CAM_AVDD1",
+ "CAM_DVDD1",
+ "CAM_RESET1";
+ status = "ok";
+ clocks = <&clock_gcc clk_mclk1_clk_src>,
+ <&clock_gcc clk_gcc_camss_mclk1_clk>;
clock-names = "cam_src_clk", "cam_clk";
- qcom,clock-rates = <19200000 0>;
+ qcom,clock-rates = <24000000 0>;
};
qcom,camera@0 {
@@ -337,6 +295,8 @@
qcom,csiphy-sd-index = <1>;
qcom,csid-sd-index = <1>;
qcom,mount-angle = <270>;
+ qcom,eeprom-src = <&eeprom_spyro1>;
+
cam_vdig-supply = <&pm660_l3>;
cam_vio-supply = <&pm660_l14>;
cam_vana-supply = <&pm660_s5>;
@@ -378,7 +338,6 @@
qcom,csiphy-sd-index = <1>;
qcom,csid-sd-index = <1>;
qcom,mount-angle = <270>;
- qcom,eeprom-src = <&eeprom_spyro1>;
qcom,actuator-src = <&actuator_spyro1>;
cam_vdig-supply = <&pm660_l3>;
cam_vana-supply = <&pm660_l7>;
diff --git a/arch/arm64/boot/dts/qcom/sdm429-spyro-qrd-evt.dtsi b/arch/arm64/boot/dts/qcom/sdm429-spyro-qrd-evt.dtsi
index 3207827..0f73bd9 100644
--- a/arch/arm64/boot/dts/qcom/sdm429-spyro-qrd-evt.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm429-spyro-qrd-evt.dtsi
@@ -14,15 +14,48 @@
#include "sdm429-spyro-qrd-mdss-panels.dtsi"
#include "sdm429-spyro-qrd-evt-camera.dtsi"
#include "sdm429-spyro-qrd-evt-audio.dtsi"
+#include <dt-bindings/thermal/thermal.h>
+
+&tlmm {
+ sd_eldo_active {
+ sd_eldo_active: sd_eldo_active {
+ mux {
+ pins = "gpio91";
+ function = "gpio";
+ };
+
+ config {
+ pins = "gpio91";
+ drive-strength = <2>;
+ bias-pull-up;
+ };
+ };
+ };
+
+ sd_eldo_suspend {
+ sd_eldo_suspend: sd_eldo_suspend {
+ mux {
+ pins = "gpio91";
+ function = "gpio";
+ };
+
+ config {
+ pins = "gpio91";
+ drive-strength = <2>;
+ bias-disable;
+ };
+ };
+ };
+};
&gpio_key_active {
mux {
- pins = "gpio91", "gpio127", "gpio128", "gpio35", "gpio126";
+ pins = "gpio127", "gpio128", "gpio35", "gpio126";
function = "gpio";
};
config {
- pins = "gpio91", "gpio127", "gpio128", "gpio35", "gpio126";
+ pins = "gpio127", "gpio128", "gpio35", "gpio126";
drive-strength = <2>;
bias-pull-up;
};
@@ -30,18 +63,36 @@
&gpio_key_suspend {
mux {
- pins = "gpio91", "gpio127", "gpio128", "gpio35", "gpio126";
+ pins = "gpio127", "gpio128", "gpio35", "gpio126";
function = "gpio";
};
config {
- pins = "gpio91", "gpio127", "gpio128", "gpio35", "gpio126";
+ pins = "gpio127", "gpio128", "gpio35", "gpio126";
drive-strength = <2>;
bias-pull-up;
};
};
&soc {
+ /delete-node/ qcom,spm@b1d2000;
+ qcom,spm@b1d2000 {
+ compatible = "qcom,spm-v2";
+ #address-cells = <1>;
+ #size-cells = <1>;
+ reg = <0xb1d2000 0x1000>;
+ qcom,name = "system-cci";
+ qcom,saw2-ver-reg = <0xfd0>;
+ qcom,saw2-cfg = <0x14>;
+ qcom,saw2-spm-dly= <0x3C102800>;
+ qcom,saw2-spm-ctl = <0xe>;
+ qcom,cpu-vctl-list = <&CPU0 &CPU1 &CPU2 &CPU3>;
+ qcom,vctl-timeout-us = <500>;
+ qcom,vctl-port = <0x0>;
+ qcom,vctl-port-ub = <0x1>;
+ qcom,pfm-port = <0x2>;
+ };
+
gpio_keys: gpio_keys {
compatible = "gpio-keys";
label = "gpio-keys";
@@ -124,8 +175,10 @@
qcom,vdd-io-current-level = <200 22000>;
pinctrl-names = "active", "sleep";
- pinctrl-0 = <&sdc2_clk_on &sdc2_cmd_on &sdc2_data_on &sdc2_cd_on>;
- pinctrl-1 = <&sdc2_clk_off &sdc2_cmd_off &sdc2_data_off>;
+ pinctrl-0 = <&sdc2_clk_on &sdc2_cmd_on &sdc2_data_on &sdc2_cd_on
+ &sd_eldo_active>;
+ pinctrl-1 = <&sdc2_clk_off &sdc2_cmd_off &sdc2_data_off &sdc2_cd_off
+ &sd_eldo_suspend>;
cd-gpios = <&tlmm 67 0x0>;
@@ -189,11 +242,11 @@
};
&adsp_fw_mem {
- reg = <0x0 0x8b800000 0x0 0x1400000>;
+ reg = <0x0 0x8b800000 0x0 0x1500000>;
};
&wcnss_fw_mem {
- reg = <0x0 0x8cc00000 0x0 0x700000>;
+ reg = <0x0 0x8cd00000 0x0 0x700000>;
};
&i2c_4 {
@@ -382,3 +435,661 @@
};
};
+
+&pm660_vadc {
+ chan@50 {
+ label = "pmic_therm";
+ reg = <0x50>;
+ qcom,decimation = <2>;
+ qcom,pre-div-channel-scaling = <0>;
+ qcom,calibration-type = "ratiometric";
+ qcom,scale-function = <2>;
+ qcom,hw-settle-time = <2>;
+ qcom,fast-avg-setup = <0>;
+ };
+};
+
+&pm660_adc_tm {
+ chan@50 {
+ label = "pmic_therm";
+ reg = <0x50>;
+ qcom,pre-div-channel-scaling = <0>;
+ qcom,calibration-type = "ratiometric";
+ qcom,scale-function = <2>;
+ qcom,hw-settle-time = <2>;
+ qcom,btm-channel-number = <0x48>;
+ qcom,thermal-node;
+ };
+};
+
+&thermal_zones {
+
+ emmc-therm-adc {
+ status = "disabled";
+ };
+
+ aoss0-lowf {
+ status = "disabled";
+ };
+
+ mdm-core-lowf {
+ status = "disabled";
+ };
+
+ lpass-lowf {
+ status = "disabled";
+ };
+
+ camera-lowf {
+ status = "disabled";
+ };
+
+ cpuss1-lowf {
+ status = "disabled";
+ };
+
+ apc1-cpu0-lowf {
+ status = "disabled";
+ };
+
+ apc1-cpu1-lowf {
+ status = "disabled";
+ };
+
+ apc1-cpu2-lowf {
+ status = "disabled";
+ };
+
+ apc1-cpu3-lowf {
+ status = "disabled";
+ };
+
+ cpuss0-lowf {
+ status = "disabled";
+ };
+
+ gpu-lowf {
+ status = "disabled";
+ };
+
+ aoss0-lowfr {
+ polling-delay-passive = <0>;
+ polling-delay = <0>;
+ thermal-governor = "low_limits_floor";
+ thermal-sensors = <&tsens0 0>;
+ tracks-low;
+
+ trips {
+ aoss0_trip: aoss-trip {
+ temperature = <5000>;
+ hysteresis = <5000>;
+ type = "passive";
+ };
+ };
+
+ cooling-maps {
+ cpu0_vdd_cdev {
+ trip = <&aoss0_trip>;
+ cooling-device = <&CPU0 (THERMAL_MAX_LIMIT-2)
+ (THERMAL_MAX_LIMIT-2)>;
+ };
+
+ cx_vdd_cdev {
+ trip = <&aoss0_trip>;
+ cooling-device = <&pm660_cx_cdev 0 0>;
+ };
+
+ modem_vdd_cdev {
+ trip = <&aoss0_trip>;
+ cooling-device = <&modem_vdd 0 0>;
+ };
+ };
+ };
+
+ mdm-core-lowfr {
+ polling-delay-passive = <0>;
+ polling-delay = <0>;
+ thermal-governor = "low_limits_floor";
+ thermal-sensors = <&tsens0 1>;
+ tracks-low;
+
+ trips {
+ mdm_core_trip: mdm-core-trip {
+ temperature = <5000>;
+ hysteresis = <5000>;
+ type = "passive";
+ };
+ };
+
+ cooling-maps {
+ cpu0_vdd_cdev {
+ trip = <&mdm_core_trip>;
+ cooling-device = <&CPU0 (THERMAL_MAX_LIMIT-2)
+ (THERMAL_MAX_LIMIT-2)>;
+ };
+
+ cx_vdd_cdev {
+ trip = <&mdm_core_trip>;
+ cooling-device = <&pm660_cx_cdev 0 0>;
+ };
+
+ modem_vdd_cdev {
+ trip = <&mdm_core_trip>;
+ cooling-device = <&modem_vdd 0 0>;
+ };
+ };
+ };
+
+ lpass-lowfr {
+ polling-delay-passive = <0>;
+ polling-delay = <0>;
+ thermal-governor = "low_limits_floor";
+ thermal-sensors = <&tsens0 2>;
+ tracks-low;
+
+ trips {
+ qdsp_trip: qdsp-trip {
+ temperature = <5000>;
+ hysteresis = <5000>;
+ type = "passive";
+ };
+ };
+
+ cooling-maps {
+ cpu0_vdd_cdev {
+ trip = <&qdsp_trip>;
+ cooling-device = <&CPU0 (THERMAL_MAX_LIMIT-2)
+ (THERMAL_MAX_LIMIT-2)>;
+ };
+
+ cx_vdd_cdev {
+ trip = <&qdsp_trip>;
+ cooling-device = <&pm660_cx_cdev 0 0>;
+ };
+
+ modem_vdd_cdev {
+ trip = <&qdsp_trip>;
+ cooling-device = <&modem_vdd 0 0>;
+ };
+ };
+ };
+
+ camera-lowfr {
+ polling-delay-passive = <0>;
+ polling-delay = <0>;
+ thermal-governor = "low_limits_floor";
+ thermal-sensors = <&tsens0 3>;
+ tracks-low;
+
+ trips {
+ camera_trip: camera-trip {
+ temperature = <5000>;
+ hysteresis = <5000>;
+ type = "passive";
+ };
+ };
+
+ cooling-maps {
+ cpu0_vdd_cdev {
+ trip = <&camera_trip>;
+ cooling-device = <&CPU0 (THERMAL_MAX_LIMIT-2)
+ (THERMAL_MAX_LIMIT-2)>;
+ };
+
+ cx_vdd_cdev {
+ trip = <&camera_trip>;
+ cooling-device = <&pm660_cx_cdev 0 0>;
+ };
+
+ modem_vdd_cdev {
+ trip = <&camera_trip>;
+ cooling-device = <&modem_vdd 0 0>;
+ };
+ };
+ };
+
+ cpuss1-lowfr {
+ polling-delay-passive = <0>;
+ polling-delay = <0>;
+ thermal-governor = "low_limits_floor";
+ thermal-sensors = <&tsens0 4>;
+ tracks-low;
+
+ trips {
+ cpuss1_trip: cpuss1-trip {
+ temperature = <5000>;
+ hysteresis = <5000>;
+ type = "passive";
+ };
+ };
+
+ cooling-maps {
+ cpu0_vdd_cdev {
+ trip = <&cpuss1_trip>;
+ cooling-device = <&CPU0 (THERMAL_MAX_LIMIT-2)
+ (THERMAL_MAX_LIMIT-2)>;
+ };
+
+ cx_vdd_cdev {
+ trip = <&cpuss1_trip>;
+ cooling-device = <&pm660_cx_cdev 0 0>;
+ };
+
+ modem_vdd_cdev {
+ trip = <&cpuss1_trip>;
+ cooling-device = <&modem_vdd 0 0>;
+ };
+ };
+ };
+
+ apc1-cpu0-lowfr {
+ polling-delay-passive = <0>;
+ polling-delay = <0>;
+ thermal-governor = "low_limits_floor";
+ thermal-sensors = <&tsens0 5>;
+ tracks-low;
+
+ trips {
+ cpu0_trip: apc1-cpu0-trip {
+ temperature = <5000>;
+ hysteresis = <5000>;
+ type = "passive";
+ };
+ };
+
+ cooling-maps {
+ cpu0_vdd_cdev {
+ trip = <&cpu0_trip>;
+ cooling-device = <&CPU0 (THERMAL_MAX_LIMIT-2)
+ (THERMAL_MAX_LIMIT-2)>;
+ };
+
+ cx_vdd_cdev {
+ trip = <&cpu0_trip>;
+ cooling-device = <&pm660_cx_cdev 0 0>;
+ };
+
+ modem_vdd_cdev {
+ trip = <&cpu0_trip>;
+ cooling-device = <&modem_vdd 0 0>;
+ };
+ };
+ };
+
+ apc1-cpu1-lowfr {
+ polling-delay-passive = <0>;
+ polling-delay = <0>;
+ thermal-governor = "low_limits_floor";
+ thermal-sensors = <&tsens0 6>;
+ tracks-low;
+
+ trips {
+ cpu1_trip: apc1-cpu1-trip {
+ temperature = <5000>;
+ hysteresis = <5000>;
+ type = "passive";
+ };
+ };
+
+ cooling-maps {
+ cpu0_vdd_cdev {
+ trip = <&cpu1_trip>;
+ cooling-device = <&CPU0 (THERMAL_MAX_LIMIT-2)
+ (THERMAL_MAX_LIMIT-2)>;
+ };
+
+ cx_vdd_cdev {
+ trip = <&cpu1_trip>;
+ cooling-device = <&pm660_cx_cdev 0 0>;
+ };
+
+ modem_vdd_cdev {
+ trip = <&cpu1_trip>;
+ cooling-device = <&modem_vdd 0 0>;
+ };
+ };
+ };
+
+ apc1-cpu2-lowfr {
+ polling-delay-passive = <0>;
+ polling-delay = <0>;
+ thermal-governor = "low_limits_floor";
+ thermal-sensors = <&tsens0 7>;
+ tracks-low;
+
+ trips {
+ cpu2_trip: apc1-cpu2-trip {
+ temperature = <5000>;
+ hysteresis = <5000>;
+ type = "passive";
+ };
+ };
+
+ cooling-maps {
+ cpu0_vdd_cdev {
+ trip = <&cpu2_trip>;
+ cooling-device = <&CPU0 (THERMAL_MAX_LIMIT-2)
+ (THERMAL_MAX_LIMIT-2)>;
+ };
+
+ cx_vdd_cdev {
+ trip = <&cpu2_trip>;
+ cooling-device = <&pm660_cx_cdev 0 0>;
+ };
+
+ modem_vdd_cdev {
+ trip = <&cpu2_trip>;
+ cooling-device = <&modem_vdd 0 0>;
+ };
+ };
+ };
+
+ apc1-cpu3-lowfr {
+ polling-delay-passive = <0>;
+ polling-delay = <0>;
+ thermal-governor = "low_limits_floor";
+ thermal-sensors = <&tsens0 8>;
+ tracks-low;
+
+ trips {
+ cpu3_trip: apc1-cpu3-trip {
+ temperature = <5000>;
+ hysteresis = <5000>;
+ type = "passive";
+ };
+ };
+
+ cooling-maps {
+ cpu0_vdd_cdev {
+ trip = <&cpu3_trip>;
+ cooling-device = <&CPU0 (THERMAL_MAX_LIMIT-2)
+ (THERMAL_MAX_LIMIT-2)>;
+ };
+
+ cx_vdd_cdev {
+ trip = <&cpu3_trip>;
+ cooling-device = <&pm660_cx_cdev 0 0>;
+ };
+
+ modem_vdd_cdev {
+ trip = <&cpu3_trip>;
+ cooling-device = <&modem_vdd 0 0>;
+ };
+ };
+ };
+
+ cpuss0-lowfr {
+ polling-delay-passive = <0>;
+ polling-delay = <0>;
+ thermal-governor = "low_limits_floor";
+ thermal-sensors = <&tsens0 9>;
+ tracks-low;
+
+ trips {
+ cpuss0_lowf_trip: cpuss0-lowf-trip {
+ temperature = <5000>;
+ hysteresis = <5000>;
+ type = "passive";
+ };
+ };
+
+ cooling-maps {
+ cpu0_vdd_cdev {
+ trip = <&cpuss0_lowf_trip>;
+ cooling-device = <&CPU0 (THERMAL_MAX_LIMIT-2)
+ (THERMAL_MAX_LIMIT-2)>;
+ };
+
+ cx_vdd_cdev {
+ trip = <&cpuss0_lowf_trip>;
+ cooling-device = <&pm660_cx_cdev 0 0>;
+ };
+
+ modem_vdd_cdev {
+ trip = <&cpuss0_lowf_trip>;
+ cooling-device = <&modem_vdd 0 0>;
+ };
+ };
+ };
+
+ gpu-lowfr {
+ polling-delay-passive = <0>;
+ polling-delay = <0>;
+ thermal-governor = "low_limits_floor";
+ thermal-sensors = <&tsens0 10>;
+ tracks-low;
+
+ trips {
+ gpu_lowf_trip: gpu-lowf-trip {
+ temperature = <5000>;
+ hysteresis = <5000>;
+ type = "passive";
+ };
+ };
+
+ cooling-maps {
+ cpu0_vdd_cdev {
+ trip = <&gpu_lowf_trip>;
+ cooling-device = <&CPU0 (THERMAL_MAX_LIMIT-2)
+ (THERMAL_MAX_LIMIT-2)>;
+ };
+
+ cx_vdd_cdev {
+ trip = <&gpu_lowf_trip>;
+ cooling-device = <&pm660_cx_cdev 0 0>;
+ };
+
+ modem_vdd_cdev {
+ trip = <&gpu_lowf_trip>;
+ cooling-device = <&modem_vdd 0 0>;
+ };
+ };
+ };
+
+ camera-therm-adc {
+ polling-delay-passive = <0>;
+ polling-delay = <0>;
+ thermal-sensors = <&pm660_adc_tm 0x4e>;
+ wake-capable-sensor;
+ thermal-governor = "user_space";
+
+ trips {
+ active-config0 {
+ temperature = <65000>;
+ hysteresis = <1000>;
+ type = "passive";
+ };
+ };
+ };
+
+ pmic-therm-adc {
+ polling-delay-passive = <0>;
+ polling-delay = <0>;
+ thermal-sensors = <&pm660_adc_tm 0x50>;
+ wake-capable-sensor;
+ thermal-governor = "user_space";
+
+ trips {
+ active-config0 {
+ temperature = <65000>;
+ hysteresis = <1000>;
+ type = "passive";
+ };
+ };
+ };
+
+ quiet-therm-step {
+ polling-delay-passive = <1000>;
+ polling-delay = <0>;
+ thermal-sensors = <&pm660_adc_tm 0x51>;
+ thermal-governor = "step_wise";
+
+ trips {
+ quiet_batt_439_trip1: quiet-batt-trip1 {
+ temperature = <38000>;
+ hysteresis = <2000>;
+ type = "passive";
+ };
+
+ quiet_batt_439_trip2: quiet-batt-trip2 {
+ temperature = <40000>;
+ hysteresis = <2000>;
+ type = "passive";
+ };
+
+ quiet_batt_439_trip3: quiet-batt-trip3 {
+ temperature = <42000>;
+ hysteresis = <2000>;
+ type = "passive";
+ };
+
+ quiet_batt_439_trip4: quiet-batt-trip4 {
+ temperature = <44000>;
+ hysteresis = <2000>;
+ type = "passive";
+ };
+
+ quiet_modem_439_trip1: quiet-modem-trip0 {
+ temperature = <44000>;
+ hysteresis = <4000>;
+ type = "passive";
+ };
+
+ quiet_modem_439_trip2: quiet-modem-trip1 {
+ temperature = <46000>;
+ hysteresis = <4000>;
+ type = "passive";
+ };
+
+ quiet_batt_439_trip5: quiet-batt-trip5 {
+ temperature = <46000>;
+ hysteresis = <2000>;
+ type = "passive";
+ };
+
+ quiet_439_batt_trip6_mdm_trip3: quiet-bt-trp6-mdm-trp3 {
+ temperature = <48000>;
+ hysteresis = <2000>;
+ type = "passive";
+ };
+
+ quiet_cpus_439_trip: quiet-cpus-trip {
+ temperature = <48000>;
+ hysteresis = <0>;
+ type = "passive";
+ };
+
+ quiet_gpu_439_trip: quiet-gpu-trip {
+ temperature = <50000>;
+ hysteresis = <0>;
+ type = "passive";
+ };
+
+ quiet_batt_439_trip7: quiet-batt-trip7 {
+ temperature = <50000>;
+ hysteresis = <2000>;
+ type = "passive";
+ };
+
+ quiet_modem_439_trip4: quiet-modem-trip3 {
+ temperature = <55000>;
+ hysteresis = <5000>;
+ type = "passive";
+ };
+ };
+
+ cooling-maps {
+ skin_cpu0 {
+ trip = <&quiet_cpus_439_trip>;
+ /* throttle from fmax to 1497600KHz */
+ cooling-device = <&CPU0 THERMAL_NO_LIMIT
+ (THERMAL_MAX_LIMIT-3)>;
+ };
+
+ skin_cpu1 {
+ trip = <&quiet_cpus_439_trip>;
+ cooling-device = <&CPU1 THERMAL_NO_LIMIT
+ (THERMAL_MAX_LIMIT-3)>;
+ };
+
+ skin_cpu2 {
+ trip = <&quiet_cpus_439_trip>;
+ cooling-device = <&CPU2 THERMAL_NO_LIMIT
+ (THERMAL_MAX_LIMIT-3)>;
+ };
+
+ skin_cpu3 {
+ trip = <&quiet_cpus_439_trip>;
+ cooling-device = <&CPU3 THERMAL_NO_LIMIT
+ (THERMAL_MAX_LIMIT-3)>;
+ };
+
+ skin_gpu {
+ trip = <&quiet_gpu_439_trip>;
+ /* throttle from fmax to 510000000Hz */
+ cooling-device = <&msm_gpu THERMAL_NO_LIMIT
+ (THERMAL_MAX_LIMIT-2)>;
+ };
+
+ modem_proc_lvl1 {
+ trip = <&quiet_modem_439_trip1>;
+ cooling-device = <&modem_proc 1 1>;
+ };
+
+ modem_proc_lvl2 {
+ trip = <&quiet_modem_439_trip4>;
+ cooling-device = <&modem_proc 3 3>;
+ };
+
+ modem_lvl1 {
+ trip = <&quiet_modem_439_trip2>;
+ cooling-device = <&modem_pa 1 1>;
+ };
+
+ modem_lvl2 {
+ trip = <&quiet_439_batt_trip6_mdm_trip3>;
+ cooling-device = <&modem_pa 2 2>;
+ };
+
+ modem_lvl3 {
+ trip = <&quiet_modem_439_trip4>;
+ cooling-device = <&modem_pa 3 3>;
+ };
+
+ battery_lvl1 {
+ trip = <&quiet_batt_439_trip1>;
+ cooling-device = <&pm660_charger 1 1>;
+ };
+
+ battery_lvl2 {
+ trip = <&quiet_batt_439_trip2>;
+ cooling-device = <&pm660_charger 2 2>;
+ };
+
+ battery_lvl3 {
+ trip = <&quiet_batt_439_trip3>;
+ cooling-device = <&pm660_charger 3 3>;
+ };
+
+ battery_lvl4 {
+ trip = <&quiet_batt_439_trip4>;
+ cooling-device = <&pm660_charger 4 4>;
+ };
+
+ battery_lvl5 {
+ trip = <&quiet_batt_439_trip5>;
+ cooling-device = <&pm660_charger 5 5>;
+ };
+
+ battery_lvl6 {
+ trip = <&quiet_439_batt_trip6_mdm_trip3>;
+ cooling-device = <&pm660_charger 6 6>;
+ };
+
+ battery_lvl7 {
+ trip = <&quiet_batt_439_trip7>;
+ cooling-device = <&pm660_charger 7 7>;
+ };
+ };
+ };
+};
diff --git a/arch/arm64/boot/dts/qcom/sdm429-spyro-qrd-mdss-panels.dtsi b/arch/arm64/boot/dts/qcom/sdm429-spyro-qrd-mdss-panels.dtsi
index 247d312..3fecf16 100644
--- a/arch/arm64/boot/dts/qcom/sdm429-spyro-qrd-mdss-panels.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm429-spyro-qrd-mdss-panels.dtsi
@@ -12,6 +12,7 @@
*/
#include "dsi-panel-edo-rm67162-qvga-cmd.dtsi"
+#include "dsi-panel-truly-rm69090-qvga-cmd.dtsi"
&soc {
dsi_pm660_panel_pwr_supply: dsi_pm660_panel_pwr_supply {
@@ -83,3 +84,11 @@
qcom,partial-update-enabled;
qcom,panel-roi-alignment = <2 2 4 2 320 2>;
};
+
+&dsi_truly_rm69090_qvga_cmd {
+ /delete-property/ qcom,mdss-dsi-panel-timings;
+ qcom,mdss-dsi-panel-timings-phy-12nm = [04 04 01 08 00 03 01 0D];
+ qcom,panel-supply-entries = <&dsi_pm660_panel_pwr_supply>;
+ qcom,esd-check-enabled;
+ qcom,mdss-dsi-panel-status-check-mode = "te_signal_check";
+};
diff --git a/arch/arm64/boot/dts/qcom/sdm429-spyro-qrd-wdp-overlay.dts b/arch/arm64/boot/dts/qcom/sdm429-spyro-qrd-wdp-overlay.dts
index f70c458..26819ac 100644
--- a/arch/arm64/boot/dts/qcom/sdm429-spyro-qrd-wdp-overlay.dts
+++ b/arch/arm64/boot/dts/qcom/sdm429-spyro-qrd-wdp-overlay.dts
@@ -20,7 +20,7 @@
model = "Qualcomm Technologies, Inc. SDM429 QRD Spyro WDP Overlay";
compatible = "qcom,sdm429w-qrd", "qcom,sdm429w", "qcom,qrd";
qcom,msm-id = <416 0x0>;
- qcom,board-id = <0x01000b 6>;
+ qcom,board-id = <0x01000b 7>;
qcom,pmic-id = <0x0002001b 0x0 0x0 0x0>;
};
@@ -39,3 +39,64 @@
&sdhc_2 {
cd-gpios = <&tlmm 67 0x1>;
};
+
+&mdss_dsi {
+ vddio-supply = <&L12A>; /* 1.8v */
+};
+
+&mdss_dsi0_pll {
+ vddio-supply = <&L12A>; /* 1.8V */
+};
+
+&mdss_dsi1_pll {
+ vddio-supply = <&L12A>; /* 1.8V */
+};
+
+&mdss_dsi0 {
+ qcom,platform-enable-gpio = <&pm660_gpios 12 0>;
+ /delete-property/ vdd-supply;
+ qcom,dsi-pref-prim-pan = <&dsi_truly_rm69090_qvga_cmd>;
+};
+&dsi_pm660_panel_pwr_supply {
+ /delete-node/ qcom,panel-supply-entry@0;
+};
+
+&pm660_gpios {
+ gpio@cb00 {
+ status = "ok";
+ qcom,mode = <1>;
+ qcom,vin-sel = <0>;
+ qcom,src-sel = <0>;
+ qcom,master-en = <1>;
+ qcom,out-strength = <2>;
+ };
+};
+
+&i2c_4 {
+ status = "ok";
+
+ tsc@24 {
+ cy,core {
+ cy,mt {
+ cy,name = "cyttsp5_mt";
+
+ cy,inp_dev_name = "cyttsp5_mt";
+ cy,flags = <0x8>;
+ cy,abs =
+ <0x35 0 368 0 0
+ 0x36 0 448 0 0
+ 0x3a 0 255 0 0
+ 0xffff 0 255 0 0
+ 0x39 0 15 0 0
+ 0x30 0 255 0 0
+ 0x31 0 255 0 0
+ 0x34 0xffffff81 127 0 0
+ 0x37 0 1 0 0
+ 0x3b 0 255 0 0>;
+
+ };
+
+ };
+ };
+
+};
diff --git a/arch/arm64/boot/dts/qcom/sdm429-spyro-wdp.dts b/arch/arm64/boot/dts/qcom/sdm429-spyro-wdp.dts
index dd12d18..b6eedaf 100644
--- a/arch/arm64/boot/dts/qcom/sdm429-spyro-wdp.dts
+++ b/arch/arm64/boot/dts/qcom/sdm429-spyro-wdp.dts
@@ -19,6 +19,6 @@
model = "Qualcomm Technologies, Inc. SDM429 QRD Spyro WDP";
compatible = "qcom,sdm429w-qrd", "qcom,sdm429w", "qcom,qrd";
qcom,msm-id = <416 0x0>;
- qcom,board-id = <0x01000b 6>;
+ qcom,board-id = <0x01000b 7>;
qcom,pmic-id = <0x0002001b 0x0 0x0 0x0>;
};
diff --git a/arch/arm64/boot/dts/qcom/sdm429w-camera-sensor-spyro.dtsi b/arch/arm64/boot/dts/qcom/sdm429w-camera-sensor-spyro.dtsi
index 45756f7..c032db5 100644
--- a/arch/arm64/boot/dts/qcom/sdm429w-camera-sensor-spyro.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm429w-camera-sensor-spyro.dtsi
@@ -109,82 +109,40 @@
eeprom_spyro1: qcom,eeprom@1 {
cell-index = <1>;
reg = <0x1>;
- qcom,eeprom-name = "sunny_8865";
compatible = "qcom,eeprom";
- qcom,slave-addr = <0x6c>;
- qcom,cci-master = <0>;
- qcom,num-blocks = <8>;
-
- qcom,page0 = <1 0x0100 2 0x01 1 1>;
- qcom,poll0 = <0 0x0 2 0x0 1 0>;
- qcom,mem0 = <0 0x0 2 0x0 1 0>;
-
- qcom,page1 = <1 0x5002 2 0x00 1 0>;
- qcom,poll1 = <0 0x0 2 0x0 1 0>;
- qcom,mem1 = <0 0x0 2 0x0 1 0>;
-
- qcom,page2 = <1 0x3d84 2 0xc0 1 0>;
- qcom,poll2 = <0 0x0 2 0x0 1 0>;
- qcom,mem2 = <0 0x0 2 0x0 1 0>;
-
- qcom,page3 = <1 0x3d88 2 0x70 1 0>;
- qcom,poll3 = <0 0x0 2 0x0 1 0>;
- qcom,mem3 = <0 0x0 2 0x0 1 0>;
-
- qcom,page4 = <1 0x3d89 2 0x10 1 0>;
- qcom,poll4 = <0 0x0 2 0x0 1 0>;
- qcom,mem4 = <0 0x0 2 0x0 1 0>;
-
- qcom,page5 = <1 0x3d8a 2 0x70 1 0>;
- qcom,poll5 = <0 0x0 2 0x0 1 0>;
- qcom,mem5 = <0 0x0 2 0x0 1 0>;
-
- qcom,page6 = <1 0x3d8b 2 0xf4 1 0>;
- qcom,poll6 = <0 0x0 2 0x0 1 0>;
- qcom,mem6 = <0 0x0 2 0x0 1 0>;
-
- qcom,page7 = <1 0x3d81 2 0x01 1 10>;
- qcom,poll7 = <0 0x0 2 0x0 1 1>;
- qcom,mem7 = <1536 0x7010 2 0 1 0>;
+ qcom,cci-master = <1>;
cam_vdig-supply = <&pm660_l3>;
- cam_vana-supply = <&pm660_l7>;
- cam_vio-supply = <&pm660_l6>;
+ cam_vio-supply = <&pm660_l14>;
+ cam_vana-supply = <&pm660_s5>;
cam_vaf-supply = <&pm660_l17>;
- qcom,cam-vreg-name = "cam_vdig", "cam_vio",
- "cam_vana", "cam_vaf";
- qcom,cam-vreg-min-voltage = <1200000 0 2800000 2850000>;
- qcom,cam-vreg-max-voltage = <1200000 0 2800000 3200000>;
- qcom,cam-vreg-op-mode = <105000 0 80000 100000>;
- qcom,gpio-no-mux = <0>;
+ qcom,cam-vreg-name = "cam_vdig", "cam_vio", "cam_vana";
+ qcom,cam-vreg-min-voltage = <1200000 1800000 1420000>;
+ qcom,cam-vreg-max-voltage = <1200000 1800000 1420000>;
+ qcom,cam-vreg-op-mode = <200000 80000 80000>;
pinctrl-names = "cam_default", "cam_suspend";
- pinctrl-0 = <&cam_sensor_mclk2_default
- &cam_sensor_front1_default>;
- pinctrl-1 = <&cam_sensor_mclk2_sleep
- &cam_sensor_front1_sleep>;
- gpios = <&tlmm 28 0>,
- <&tlmm 40 0>,
- <&tlmm 39 0>;
- qcom,gpio-reset = <1>;
- qcom,gpio-standby = <2>;
- qcom,gpio-req-tbl-num = <0 1 2>;
- qcom,gpio-req-tbl-flags = <1 0 0>;
- qcom,gpio-req-tbl-label = "CAMIF_MCLK2",
- "CAM_RESET2",
- "CAM_STANDBY2";
- qcom,cam-power-seq-type = "sensor_vreg", "sensor_vreg",
- "sensor_vreg",
- "sensor_gpio", "sensor_gpio" , "sensor_clk";
- qcom,cam-power-seq-val = "cam_vdig", "cam_vana", "cam_vio",
- "sensor_gpio_reset", "sensor_gpio_standby",
- "sensor_cam_mclk";
- qcom,cam-power-seq-cfg-val = <1 1 1 1 1 24000000>;
- qcom,cam-power-seq-delay = <1 1 1 30 30 5>;
+ pinctrl-0 = <&cam_sensor_mclk1_default
+ &cam_sensor_front_default>;
+ pinctrl-1 = <&cam_sensor_mclk1_sleep
+ &cam_sensor_front_sleep>;
+ gpios = <&tlmm 27 0>,
+ <&tlmm 33 0>,
+ <&tlmm 66 0>,
+ <&tlmm 38 0>;
+ qcom,gpio-vana= <1>;
+ qcom,gpio-vdig= <2>;
+ qcom,gpio-reset = <3>;
+ qcom,gpio-req-tbl-num = <0 1 2 3>;
+ qcom,gpio-req-tbl-flags = <1 0 0 0>;
+ qcom,gpio-req-tbl-label = "CAMIF_MCLK1",
+ "CAM_AVDD1",
+ "CAM_DVDD1",
+ "CAM_RESET1";
status = "ok";
- clocks = <&clock_gcc clk_mclk2_clk_src>,
- <&clock_gcc clk_gcc_camss_mclk2_clk>;
+ clocks = <&clock_gcc clk_mclk1_clk_src>,
+ <&clock_gcc clk_gcc_camss_mclk1_clk>;
clock-names = "cam_src_clk", "cam_clk";
- qcom,clock-rates = <19200000 0>;
+ qcom,clock-rates = <24000000 0>;
};
qcom,camera@0 {
@@ -244,6 +202,8 @@
qcom,csiphy-sd-index = <1>;
qcom,csid-sd-index = <1>;
qcom,mount-angle = <270>;
+ qcom,eeprom-src = <&eeprom_spyro1>;
+
cam_vdig-supply = <&pm660_l3>;
cam_vio-supply = <&pm660_l14>;
cam_vaf-supply = <&pm660_l19>;
@@ -286,7 +246,6 @@
qcom,csiphy-sd-index = <1>;
qcom,csid-sd-index = <1>;
qcom,mount-angle = <270>;
- qcom,eeprom-src = <&eeprom_spyro1>;
qcom,actuator-src = <&actuator_spyro1>;
cam_vdig-supply = <&pm660_l3>;
cam_vana-supply = <&pm660_l7>;
diff --git a/arch/arm64/boot/dts/qcom/sdm429w-pm660.dtsi b/arch/arm64/boot/dts/qcom/sdm429w-pm660.dtsi
index b432ce4..f6d85f4 100644
--- a/arch/arm64/boot/dts/qcom/sdm429w-pm660.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm429w-pm660.dtsi
@@ -370,9 +370,10 @@
/* over-write the PM660 GPIO mappings for 429w */
&pm660_gpios {
- interrupts = <0x0 0xc3 0 IRQ_TYPE_NONE>;
- interrupt-names = "pm660_gpio4";
- qcom,gpios-disallowed = <1 2 3 5 6 7 8 9 10 11 12 13>;
+ interrupts = <0x0 0xc3 0 IRQ_TYPE_NONE>,
+ <0x0 0xcb 0 IRQ_TYPE_NONE>;
+ interrupt-names = "pm660_gpio4", "pm660_gpio12";
+ qcom,gpios-disallowed = <1 2 3 5 6 7 8 9 10 11 13>;
};
&pm660_vadc {
@@ -403,6 +404,8 @@
qcom,use-extcon;
qcom,pd-not-supported;
+ dpdm-supply = <&usb_otg>;
+
qcom,chgr@1000 {
reg = <0x1000 0x100>;
interrupts =
diff --git a/arch/arm64/boot/dts/qcom/sdm429w-regulator.dtsi b/arch/arm64/boot/dts/qcom/sdm429w-regulator.dtsi
index 4b521b2..db76a24 100644
--- a/arch/arm64/boot/dts/qcom/sdm429w-regulator.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm429w-regulator.dtsi
@@ -55,6 +55,14 @@
<RPM_SMD_REGULATOR_LEVEL_BINNING>;
qcom,use-voltage-level;
};
+
+ pm660_cx_cdev: regulator-cx-cdev {
+ compatible = "qcom,regulator-cooling-device";
+ regulator-cdev-supply = <&pm660_s1_floor_level>;
+ regulator-levels = <RPM_SMD_REGULATOR_LEVEL_NOM_PLUS
+ RPM_SMD_REGULATOR_LEVEL_RETENTION>;
+ #cooling-cells = <2>;
+ };
};
/* PM660 S2 - VDD_MX supply */
diff --git a/arch/arm64/boot/dts/qcom/sdm845-rb3.dtsi b/arch/arm64/boot/dts/qcom/sdm845-rb3.dtsi
index 2955610..2db4b60 100644
--- a/arch/arm64/boot/dts/qcom/sdm845-rb3.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm845-rb3.dtsi
@@ -290,6 +290,11 @@
&pmi8998_fg {
qcom,battery-data = <&mtp_batterydata>;
+ status = "disabled";
+};
+
+&bcl_sensor {
+ status = "disabled";
};
&smb1355_charger_0 {
diff --git a/arch/arm64/boot/dts/qcom/sdm845-v2.1-rb3.dtsi b/arch/arm64/boot/dts/qcom/sdm845-v2.1-rb3.dtsi
index cf84ecf..47638e3 100644
--- a/arch/arm64/boot/dts/qcom/sdm845-v2.1-rb3.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm845-v2.1-rb3.dtsi
@@ -33,6 +33,33 @@
status = "ok";
};
+&pcie1 {
+ status = "disable";
+};
+
+&soc {
+ clk40M: can_clock {
+ compatible = "fixed-clock";
+ #clock-cells = <0>;
+ clock-frequency = <40000000>;
+ };
+};
+
+&qupv3_se0_spi {
+ status = "ok";
+ can@0 {
+ compatible = "microchip,mcp2517fd";
+ reg = <0>;
+ clocks = <&clk40M>;
+ interrupt-parent = <&tlmm>;
+ interrupts = <104 0>;
+ interrupt-names = "can_irq";
+ spi-max-frequency = <10000000>;
+ gpio-controller;
+ status = "okay";
+ };
+};
+
&qupv3_se3_i2c {
status = "disabled";
};
diff --git a/arch/arm64/boot/dts/qcom/sdw3100-apq8009w-wtp.dts b/arch/arm64/boot/dts/qcom/sdw3100-apq8009w-wtp.dts
index 1f91311..e23d1a6 100644
--- a/arch/arm64/boot/dts/qcom/sdw3100-apq8009w-wtp.dts
+++ b/arch/arm64/boot/dts/qcom/sdw3100-apq8009w-wtp.dts
@@ -26,7 +26,8 @@
qcom,msm-id = <265 0>,
<301 0>;
qcom,board-id = <8 0x10f>,
- <8 0x117>;
+ <8 0x117>,
+ <8 0x17>;
qcom,pmic-id = <0x0001001b 0x0 0x0 0x0>,
<0x0001011b 0x0 0x0 0x0>;
};
diff --git a/arch/arm64/configs/sdm670-perf_defconfig b/arch/arm64/configs/sdm670-perf_defconfig
index d75479e..d233c67 100755
--- a/arch/arm64/configs/sdm670-perf_defconfig
+++ b/arch/arm64/configs/sdm670-perf_defconfig
@@ -104,6 +104,7 @@
CONFIG_IP_PNP=y
CONFIG_IP_PNP_DHCP=y
CONFIG_NET_IPGRE_DEMUX=y
+CONFIG_SYN_COOKIES=y
CONFIG_NET_IPVTI=y
CONFIG_INET_AH=y
CONFIG_INET_ESP=y
@@ -125,7 +126,6 @@
CONFIG_NF_CONNTRACK_SECMARK=y
CONFIG_NF_CONNTRACK_EVENTS=y
CONFIG_NF_CT_PROTO_DCCP=y
-CONFIG_NF_CT_PROTO_SCTP=y
CONFIG_NF_CT_PROTO_UDPLITE=y
CONFIG_NF_CONNTRACK_AMANDA=y
CONFIG_NF_CONNTRACK_FTP=y
@@ -172,6 +172,7 @@
CONFIG_NETFILTER_XT_MATCH_PKTTYPE=y
CONFIG_NETFILTER_XT_MATCH_QUOTA=y
CONFIG_NETFILTER_XT_MATCH_QUOTA2=y
+# CONFIG_NETFILTER_XT_MATCH_SCTP is not set
CONFIG_NETFILTER_XT_MATCH_SOCKET=y
CONFIG_NETFILTER_XT_MATCH_STATE=y
CONFIG_NETFILTER_XT_MATCH_STATISTIC=y
@@ -205,6 +206,7 @@
CONFIG_IP6_NF_RAW=y
CONFIG_BRIDGE_NF_EBTABLES=y
CONFIG_BRIDGE_EBT_BROUTE=y
+CONFIG_IP_SCTP=y
CONFIG_L2TP=y
CONFIG_L2TP_V3=y
CONFIG_L2TP_IP=y
@@ -249,6 +251,7 @@
CONFIG_DMA_CMA=y
CONFIG_ZRAM=y
CONFIG_BLK_DEV_LOOP=y
+CONFIG_BLK_DEV_LOOP_MIN_COUNT=16
CONFIG_BLK_DEV_RAM=y
CONFIG_BLK_DEV_RAM_SIZE=8192
CONFIG_QSEECOM=y
diff --git a/arch/arm64/configs/sdm670_defconfig b/arch/arm64/configs/sdm670_defconfig
index 1eea891..25e3245 100755
--- a/arch/arm64/configs/sdm670_defconfig
+++ b/arch/arm64/configs/sdm670_defconfig
@@ -109,6 +109,7 @@
CONFIG_IP_PNP=y
CONFIG_IP_PNP_DHCP=y
CONFIG_NET_IPGRE_DEMUX=y
+CONFIG_SYN_COOKIES=y
CONFIG_NET_IPVTI=y
CONFIG_INET_AH=y
CONFIG_INET_ESP=y
@@ -130,7 +131,6 @@
CONFIG_NF_CONNTRACK_SECMARK=y
CONFIG_NF_CONNTRACK_EVENTS=y
CONFIG_NF_CT_PROTO_DCCP=y
-CONFIG_NF_CT_PROTO_SCTP=y
CONFIG_NF_CT_PROTO_UDPLITE=y
CONFIG_NF_CONNTRACK_AMANDA=y
CONFIG_NF_CONNTRACK_FTP=y
@@ -178,6 +178,7 @@
CONFIG_NETFILTER_XT_MATCH_QUOTA=y
CONFIG_NETFILTER_XT_MATCH_QUOTA2=y
CONFIG_NETFILTER_XT_MATCH_QUOTA2_LOG=y
+# CONFIG_NETFILTER_XT_MATCH_SCTP is not set
CONFIG_NETFILTER_XT_MATCH_SOCKET=y
CONFIG_NETFILTER_XT_MATCH_STATE=y
CONFIG_NETFILTER_XT_MATCH_STATISTIC=y
@@ -211,6 +212,7 @@
CONFIG_IP6_NF_RAW=y
CONFIG_BRIDGE_NF_EBTABLES=y
CONFIG_BRIDGE_EBT_BROUTE=y
+CONFIG_IP_SCTP=y
CONFIG_L2TP=y
CONFIG_L2TP_DEBUGFS=y
CONFIG_L2TP_V3=y
@@ -258,6 +260,7 @@
CONFIG_DMA_CMA=y
CONFIG_ZRAM=y
CONFIG_BLK_DEV_LOOP=y
+CONFIG_BLK_DEV_LOOP_MIN_COUNT=16
CONFIG_BLK_DEV_RAM=y
CONFIG_BLK_DEV_RAM_SIZE=8192
CONFIG_QSEECOM=y
diff --git a/arch/arm64/configs/sdm845-perf_defconfig b/arch/arm64/configs/sdm845-perf_defconfig
index 1ec275b..230ad65 100755
--- a/arch/arm64/configs/sdm845-perf_defconfig
+++ b/arch/arm64/configs/sdm845-perf_defconfig
@@ -235,6 +235,8 @@
CONFIG_RMNET_DATA_FC=y
CONFIG_RMNET_DATA_DEBUG_PKT=y
CONFIG_SOCKEV_NLMCAST=y
+CONFIG_CAN=y
+CONFIG_CAN_MCP25XXFD=y
CONFIG_BT=y
CONFIG_MSM_BT_POWER=y
CONFIG_CFG80211=y
@@ -250,6 +252,7 @@
CONFIG_DMA_CMA=y
CONFIG_ZRAM=y
CONFIG_BLK_DEV_LOOP=y
+CONFIG_BLK_DEV_LOOP_MIN_COUNT=16
CONFIG_BLK_DEV_RAM=y
CONFIG_BLK_DEV_RAM_SIZE=8192
CONFIG_QSEECOM=y
diff --git a/arch/arm64/configs/sdm845_defconfig b/arch/arm64/configs/sdm845_defconfig
index 1a39bca..4225d8b 100644
--- a/arch/arm64/configs/sdm845_defconfig
+++ b/arch/arm64/configs/sdm845_defconfig
@@ -240,6 +240,8 @@
CONFIG_RMNET_DATA_FC=y
CONFIG_RMNET_DATA_DEBUG_PKT=y
CONFIG_SOCKEV_NLMCAST=y
+CONFIG_CAN=y
+CONFIG_CAN_MCP25XXFD=y
CONFIG_BT=y
CONFIG_MSM_BT_POWER=y
CONFIG_CFG80211=y
@@ -256,6 +258,7 @@
CONFIG_DMA_CMA=y
CONFIG_ZRAM=y
CONFIG_BLK_DEV_LOOP=y
+CONFIG_BLK_DEV_LOOP_MIN_COUNT=16
CONFIG_BLK_DEV_RAM=y
CONFIG_BLK_DEV_RAM_SIZE=8192
CONFIG_QSEECOM=y
diff --git a/arch/arm64/include/asm/thread_info.h b/arch/arm64/include/asm/thread_info.h
index 3b79682..af0c14d 100644
--- a/arch/arm64/include/asm/thread_info.h
+++ b/arch/arm64/include/asm/thread_info.h
@@ -40,6 +40,7 @@
*/
struct thread_info {
unsigned long flags; /* low level flags */
+ unsigned long padding[7];
mm_segment_t addr_limit; /* address limit */
#ifdef CONFIG_ARM64_SW_TTBR0_PAN
u64 ttbr0; /* saved TTBR0_EL1 */
diff --git a/drivers/bluetooth/bluetooth-power.c b/drivers/bluetooth/bluetooth-power.c
index b90bbfe..650585c 100644
--- a/drivers/bluetooth/bluetooth-power.c
+++ b/drivers/bluetooth/bluetooth-power.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2009-2010, 2013-2018 The Linux Foundation. All rights reserved.
+/* Copyright (c) 2009-2010, 2013-2019 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
@@ -613,6 +613,23 @@
return 0;
}
+static int get_bt_reset_gpio_value(void)
+{
+ int rc = 0;
+ int bt_reset_gpio = bt_power_pdata->bt_gpio_sys_rst;
+
+ rc = gpio_request(bt_reset_gpio, "bt_sys_rst_n");
+ if (rc) {
+ BT_PWR_ERR("unable to request gpio %d (%d)\n",
+ bt_reset_gpio, rc);
+ return rc;
+ }
+
+ rc = gpio_get_value(bt_reset_gpio);
+ gpio_free(bt_power_pdata->bt_gpio_sys_rst);
+ return rc;
+}
+
static int bt_power_probe(struct platform_device *pdev)
{
int ret = 0;
@@ -659,7 +676,8 @@
btpdev = pdev;
if (of_id) {
- if (strcmp(of_id->compatible, "qca,qca6174") == 0) {
+ if ((get_bt_reset_gpio_value() == BT_RESET_GPIO_HIGH_VAL)
+ && (strcmp(of_id->compatible, "qca,qca6174") == 0)) {
bluetooth_toggle_radio(pdev->dev.platform_data, 0);
bluetooth_toggle_radio(pdev->dev.platform_data, 1);
}
diff --git a/drivers/char/diag/diag_dci.c b/drivers/char/diag/diag_dci.c
index 57d6544..12c8bd9 100644
--- a/drivers/char/diag/diag_dci.c
+++ b/drivers/char/diag/diag_dci.c
@@ -726,29 +726,44 @@
return ((*event_mask_ptr & byte_mask) == byte_mask) ? 1 : 0;
}
-static int diag_dci_filter_commands(struct diag_pkt_header_t *header)
+static int diag_dci_filter_commands(struct diag_pkt_header_t *header,
+ int header_len)
{
if (!header)
return -ENOMEM;
- switch (header->cmd_code) {
- case 0x7d: /* Msg Mask Configuration */
- case 0x73: /* Log Mask Configuration */
- case 0x81: /* Event Mask Configuration */
- case 0x82: /* Event Mask Change */
- case 0x60: /* Event Mask Toggle */
- return 1;
+ if (header_len <= 0)
+ return -EIO;
+
+ if (header_len) {
+ switch (header->cmd_code) {
+ case 0x7d: /* Msg Mask Configuration */
+ case 0x73: /* Log Mask Configuration */
+ case 0x81: /* Event Mask Configuration */
+ case 0x82: /* Event Mask Change */
+ case 0x60: /* Event Mask Toggle */
+ DIAG_LOG(DIAG_DEBUG_DCI,
+ "diag: command not supported: %d\n",
+ header->cmd_code);
+ return 1;
+ }
}
- if (header->cmd_code == 0x4b && header->subsys_id == 0x12) {
- switch (header->subsys_cmd_code) {
- case 0x60: /* Extended Event Mask Config */
- case 0x61: /* Extended Msg Mask Config */
- case 0x62: /* Extended Log Mask Config */
- case 0x20C: /* Set current Preset ID */
- case 0x20D: /* Get current Preset ID */
- case 0x218: /* HDLC Disabled Command */
- return 1;
+ if (header_len >= (3*sizeof(uint8_t))) {
+ if (header->cmd_code == 0x4b && header->subsys_id == 0x12) {
+ switch (header->subsys_cmd_code) {
+ case 0x60: /* Extended Event Mask Config */
+ case 0x61: /* Extended Msg Mask Config */
+ case 0x62: /* Extended Log Mask Config */
+ case 0x20C: /* Set current Preset ID */
+ case 0x20D: /* Get current Preset ID */
+ case 0x218: /* HDLC Disabled Command */
+ DIAG_LOG(DIAG_DEBUG_DCI,
+ "diag: command not supported %d %d %d\n",
+ header->cmd_code, header->subsys_id,
+ header->subsys_cmd_code);
+ return 1;
+ }
}
}
@@ -1800,22 +1815,26 @@
static int diag_dci_process_apps_pkt(struct diag_pkt_header_t *pkt_header,
unsigned char *req_buf, int req_len,
- int tag)
+ int tag, int pkt_header_len)
{
- uint8_t cmd_code, subsys_id, i, goto_download = 0;
+ uint8_t cmd_code = 0, subsys_id = 0, i, goto_download = 0;
uint8_t header_len = sizeof(struct diag_dci_pkt_header_t);
- uint16_t ss_cmd_code;
+ uint16_t ss_cmd_code = 0;
uint32_t write_len = 0;
unsigned char *dest_buf = driver->apps_dci_buf;
unsigned char *payload_ptr = driver->apps_dci_buf + header_len;
struct diag_dci_pkt_header_t dci_header;
- if (!pkt_header || !req_buf || req_len <= 0 || tag < 0)
+ if (!pkt_header || !req_buf || req_len <= 0 || tag < 0 ||
+ pkt_header_len <= 0)
return -EIO;
- cmd_code = pkt_header->cmd_code;
- subsys_id = pkt_header->subsys_id;
- ss_cmd_code = pkt_header->subsys_cmd_code;
+ if (pkt_header_len >= (sizeof(uint8_t)))
+ cmd_code = pkt_header->cmd_code;
+ if (pkt_header_len >= (2 * sizeof(uint8_t)))
+ subsys_id = pkt_header->subsys_id;
+ if (pkt_header_len >= (3 * sizeof(uint8_t)))
+ ss_cmd_code = pkt_header->subsys_cmd_code;
if (cmd_code == DIAG_CMD_DOWNLOAD) {
*payload_ptr = DIAG_CMD_DOWNLOAD;
@@ -1935,7 +1954,7 @@
static int diag_process_dci_pkt_rsp(unsigned char *buf, int len)
{
int ret = DIAG_DCI_TABLE_ERR;
- int common_cmd = 0;
+ int common_cmd = 0, header_len = 0;
struct diag_pkt_header_t *header = NULL;
unsigned char *temp = buf;
unsigned char *req_buf = NULL;
@@ -1951,8 +1970,7 @@
if (!buf)
return -EIO;
- if (len < (sizeof(struct dci_pkt_req_t) +
- sizeof(struct diag_pkt_header_t)) ||
+ if (len < sizeof(struct dci_pkt_req_t) ||
len > DCI_REQ_BUF_SIZE) {
pr_err("diag: dci: Invalid length %d len in %s", len, __func__);
return -EIO;
@@ -1963,13 +1981,6 @@
read_len += sizeof(struct dci_pkt_req_t);
req_len -= sizeof(struct dci_pkt_req_t);
req_buf = temp; /* Start of the Request */
- header = (struct diag_pkt_header_t *)temp;
- read_len += sizeof(struct diag_pkt_header_t);
- if (read_len >= DCI_REQ_BUF_SIZE) {
- pr_err("diag: dci: In %s, invalid read_len: %d\n", __func__,
- read_len);
- return -EIO;
- }
mutex_lock(&driver->dci_mutex);
dci_entry = diag_dci_get_client_entry(req_hdr.client_id);
@@ -1980,11 +1991,40 @@
return DIAG_DCI_NO_REG;
}
+ header = (void *)temp;
+ header_len = len - sizeof(struct dci_pkt_req_t);
+ if (header_len <= 0) {
+ mutex_unlock(&driver->dci_mutex);
+ return -EIO;
+ }
+ if (header_len >= sizeof(uint8_t)) {
+ header->cmd_code = (uint16_t)(*(uint8_t *)temp);
+ read_len += sizeof(uint8_t);
+ }
+ if (header_len >= (2 * sizeof(uint8_t))) {
+ temp += sizeof(uint8_t);
+ header->subsys_id = (uint16_t)(*(uint8_t *)temp);
+ read_len += sizeof(uint8_t);
+ }
+ if (header_len == (3 * sizeof(uint8_t))) {
+ temp += sizeof(uint8_t);
+ header->subsys_cmd_code = (uint16_t)(*(uint8_t *)temp);
+ read_len += sizeof(uint8_t);
+ } else if (header_len >=
+ (2 * sizeof(uint8_t)) + sizeof(uint16_t)) {
+ temp += sizeof(uint8_t);
+ header->subsys_cmd_code = (uint16_t)(*(uint16_t *)temp);
+ read_len += sizeof(uint16_t);
+ }
+ if (read_len > DCI_REQ_BUF_SIZE) {
+ pr_err("diag: dci: In %s, invalid read_len: %d\n", __func__,
+ read_len);
+ mutex_unlock(&driver->dci_mutex);
+ return -EIO;
+ }
+
/* Check if the command is allowed on DCI */
- if (diag_dci_filter_commands(header)) {
- pr_debug("diag: command not supported %d %d %d",
- header->cmd_code, header->subsys_id,
- header->subsys_cmd_code);
+ if (diag_dci_filter_commands(header, header_len)) {
mutex_unlock(&driver->dci_mutex);
return DIAG_DCI_SEND_DATA_FAIL;
}
@@ -2038,14 +2078,23 @@
/* Check if it is a dedicated Apps command */
ret = diag_dci_process_apps_pkt(header, req_buf, req_len,
- req_entry->tag);
+ req_entry->tag, header_len);
if ((ret == DIAG_DCI_NO_ERROR && !common_cmd) || ret < 0)
return ret;
- reg_entry.cmd_code = header->cmd_code;
- reg_entry.subsys_id = header->subsys_id;
- reg_entry.cmd_code_hi = header->subsys_cmd_code;
- reg_entry.cmd_code_lo = header->subsys_cmd_code;
+ reg_entry.cmd_code = 0;
+ reg_entry.subsys_id = 0;
+ reg_entry.cmd_code_hi = 0;
+ reg_entry.cmd_code_lo = 0;
+
+ if (header_len >= (sizeof(uint8_t)))
+ reg_entry.cmd_code = header->cmd_code;
+ if (header_len >= (2 * sizeof(uint8_t)))
+ reg_entry.subsys_id = header->subsys_id;
+ if (header_len >= (3 * sizeof(uint8_t))) {
+ reg_entry.cmd_code_hi = header->subsys_cmd_code;
+ reg_entry.cmd_code_lo = header->subsys_cmd_code;
+ }
mutex_lock(&driver->cmd_reg_mutex);
temp_entry = diag_cmd_search(®_entry, ALL_PROC);
diff --git a/drivers/char/diag/diag_masks.c b/drivers/char/diag/diag_masks.c
index c6ec271..c67f073 100644
--- a/drivers/char/diag/diag_masks.c
+++ b/drivers/char/diag/diag_masks.c
@@ -1195,7 +1195,7 @@
int rsp_header_len = sizeof(struct diag_log_config_rsp_t);
uint32_t mask_size = 0;
struct diag_log_mask_t *log_item = NULL;
- struct diag_log_config_req_t *req;
+ struct diag_log_config_get_req_t *req;
struct diag_log_config_rsp_t rsp;
struct diag_mask_info *mask_info = NULL;
struct diag_md_session_t *info = NULL;
@@ -1205,7 +1205,7 @@
mask_info = (!info) ? &log_mask : info->log_mask;
if (!src_buf || !dest_buf || dest_len <= 0 || !mask_info ||
- src_len < sizeof(struct diag_log_config_req_t)) {
+ src_len < sizeof(struct diag_log_config_get_req_t)) {
pr_err("diag: Invalid input in %s, src_buf: %pK, src_len: %d, dest_buf: %pK, dest_len: %d, mask_info: %pK\n",
__func__, src_buf, src_len, dest_buf, dest_len,
mask_info);
@@ -1224,7 +1224,7 @@
return 0;
}
- req = (struct diag_log_config_req_t *)src_buf;
+ req = (struct diag_log_config_get_req_t *)src_buf;
read_len += req_header_len;
rsp.cmd_code = DIAG_CMD_LOG_CONFIG;
diff --git a/drivers/char/diag/diag_masks.h b/drivers/char/diag/diag_masks.h
index a736ff2..5c76825 100644
--- a/drivers/char/diag/diag_masks.h
+++ b/drivers/char/diag/diag_masks.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2013-2015, 2018 The Linux Foundation. All rights reserved.
+/* Copyright (c) 2013-2015, 2018-2019 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
@@ -40,6 +40,13 @@
uint32_t *ptr;
};
+struct diag_log_config_get_req_t {
+ uint8_t cmd_code;
+ uint8_t padding[3];
+ uint32_t sub_cmd;
+ uint32_t equip_id;
+} __packed;
+
struct diag_log_config_req_t {
uint8_t cmd_code;
uint8_t padding[3];
diff --git a/drivers/clocksource/Kconfig b/drivers/clocksource/Kconfig
index 91cf857..863be43 100644
--- a/drivers/clocksource/Kconfig
+++ b/drivers/clocksource/Kconfig
@@ -334,7 +334,7 @@
config MSM_TIMER_LEAP
bool "ARCH TIMER counter rollover"
default n
- depends on ARM_ARCH_TIMER && ARM64
+ depends on ARM_ARCH_TIMER
help
This option enables a check for least significant 32 bits of
counter rollover. On every counter read if least significant
diff --git a/drivers/input/sensors/smi130/smi130_acc.c b/drivers/input/sensors/smi130/smi130_acc.c
index c36c31d..a63e08e 100644
--- a/drivers/input/sensors/smi130/smi130_acc.c
+++ b/drivers/input/sensors/smi130/smi130_acc.c
@@ -1604,6 +1604,7 @@
bool read_acc_boot_sample;
int acc_bufsample_cnt;
bool acc_buffer_smi130_samples;
+ bool acc_enable;
struct kmem_cache *smi_acc_cachepool;
struct smi_acc_sample *smi130_acc_samplist[SMI_ACC_MAXSAMPLE];
int max_buffer_time;
@@ -4073,12 +4074,25 @@
else
return 0;
}
+static void smi130_check_acc_enable_flag(struct smi130_acc_data *client_data,
+ unsigned long data)
+{
+ if (data == SMI_ACC2X2_MODE_NORMAL)
+ client_data->acc_enable = true;
+ else
+ client_data->acc_enable = false;
+}
#else
static inline int smi130_check_acc_early_buff_enable_flag(
struct smi130_acc_data *client_data)
{
return 0;
}
+static void smi130_check_acc_enable_flag(struct smi130_acc_data *client_data,
+ unsigned long data)
+{
+
+}
#endif
static ssize_t smi130_acc_enable_int_store(struct device *dev,
@@ -5481,13 +5495,17 @@
struct i2c_client *client = to_i2c_client(dev);
struct smi130_acc_data *smi130_acc = i2c_get_clientdata(client);
- error = smi130_check_acc_early_buff_enable_flag(smi130_acc);
- if (error)
- return count;
error = kstrtoul(buf, 10, &data);
if (error)
return error;
+
+ smi130_check_acc_enable_flag(smi130_acc, data);
+
+ error = smi130_check_acc_early_buff_enable_flag(smi130_acc);
+ if (error)
+ return count;
+
if (smi130_acc_set_mode(smi130_acc->smi130_acc_client,
(unsigned char) data, SMI_ACC_ENABLED_BSX) < 0)
return -EINVAL;
@@ -6915,6 +6933,9 @@
PINFO("End of ACC buffering %d\n",
client_data->acc_bufsample_cnt);
client_data->acc_buffer_smi130_samples = false;
+ if (client_data->acc_enable == false)
+ smi130_acc_set_mode(client_data->smi130_acc_client,
+ SMI_ACC2X2_MODE_SUSPEND, 1);
}
}
#else
@@ -6982,6 +7003,7 @@
}
client_data->acc_buffer_smi130_samples = true;
+ client_data->acc_enable = false;
smi130_acc_set_mode(client, SMI_ACC2X2_MODE_NORMAL, 1);
smi130_acc_set_bandwidth(client, SMI_ACC2X2_BW_62_50HZ);
diff --git a/drivers/input/sensors/smi130/smi130_gyro_driver.c b/drivers/input/sensors/smi130/smi130_gyro_driver.c
index 552d39c..fd9e87d 100644
--- a/drivers/input/sensors/smi130/smi130_gyro_driver.c
+++ b/drivers/input/sensors/smi130/smi130_gyro_driver.c
@@ -295,6 +295,7 @@
bool read_gyro_boot_sample;
int gyro_bufsample_cnt;
bool gyro_buffer_smi130_samples;
+ bool gyro_enable;
struct kmem_cache *smi_gyro_cachepool;
struct smi_gyro_sample *smi130_gyro_samplist[SMI_GYRO_MAXSAMPLE];
int max_buffer_time;
@@ -860,12 +861,24 @@
else
return 0;
}
+static void smi130_check_gyro_enable_flag(
+ struct smi_gyro_client_data *client_data, unsigned long data)
+{
+ if (data == SMI130_GYRO_MODE_NORMAL)
+ client_data->gyro_enable = true;
+ else
+ client_data->gyro_enable = false;
+}
#else
static inline int smi130_check_gyro_early_buff_enable_flag(
struct smi_gyro_client_data *client_data)
{
return 0;
}
+static void smi130_check_gyro_enable_flag(
+ struct smi_gyro_client_data *client_data, unsigned long data)
+{
+}
#endif
static ssize_t smi_gyro_show_op_mode(struct device *dev,
@@ -895,13 +908,17 @@
long op_mode;
- err = smi130_check_gyro_early_buff_enable_flag(client_data);
- if (err)
- return count;
err = kstrtoul(buf, 10, &op_mode);
if (err)
return err;
+
+ smi130_check_gyro_enable_flag(client_data, op_mode);
+
+ err = smi130_check_gyro_early_buff_enable_flag(client_data);
+ if (err)
+ return count;
+
mutex_lock(&client_data->mutex_op_mode);
err = SMI_GYRO_CALL_API(set_mode)(op_mode);
@@ -1742,6 +1759,10 @@
PINFO("End of GYRO buffering %d",
client_data->gyro_bufsample_cnt);
client_data->gyro_buffer_smi130_samples = false;
+ if (client_data->gyro_enable == false) {
+ smi130_gyro_set_mode(SMI130_GYRO_MODE_SUSPEND);
+ smi130_gyro_delay(5);
+ }
}
}
#else
@@ -1810,6 +1831,7 @@
}
client_data->gyro_buffer_smi130_samples = true;
+ client_data->gyro_enable = false;
smi130_gyro_set_mode(SMI130_GYRO_MODE_NORMAL);
smi130_gyro_delay(5);
diff --git a/drivers/input/touchscreen/cyttsp5/cyttsp5_core.c b/drivers/input/touchscreen/cyttsp5/cyttsp5_core.c
index 64cc510..44bb9ba 100644
--- a/drivers/input/touchscreen/cyttsp5/cyttsp5_core.c
+++ b/drivers/input/touchscreen/cyttsp5/cyttsp5_core.c
@@ -306,8 +306,12 @@
for (i = 0; i < cd->num_hid_reports; i++) {
report = cd->hid_reports[i];
- for (j = 0; j < report->num_fields; j++)
+ if (!report)
+ continue;
+ for (j = 0; j < report->num_fields; j++) {
kfree(report->fields[j]);
+ report->fields[j] = NULL;
+ }
kfree(report);
cd->hid_reports[i] = NULL;
}
@@ -3698,7 +3702,7 @@
rc = cyttsp5_hid_output_exit_easywake_state_(cd,
cd->easy_wakeup_gesture, &status);
if (rc || status == 0) {
- dev_err(cd->dev, "%s: failed, rc=%d, status=%d\n",
+ dev_dbg(cd->dev, "%s: failed, rc=%d, status=%d\n",
__func__, rc, status);
return -EBUSY;
}
@@ -4395,7 +4399,8 @@
if (!IS_DEEP_SLEEP_CONFIGURED(cd->easy_wakeup_gesture)) {
#ifdef CY_GES_WAKEUP
- return cyttsp5_core_wake_device_from_easy_wakeup_(cd);
+ if (!cyttsp5_core_wake_device_from_easy_wakeup_(cd))
+ return 0;
#endif
}
@@ -4633,7 +4638,7 @@
rc = cyttsp5_check_and_deassert_int(cd);
- if (reset || retry != CY_CORE_STARTUP_RETRY_COUNT) {
+ if (rc || retry != CY_CORE_STARTUP_RETRY_COUNT) {
/* reset hardware */
rc = cyttsp5_reset_and_wait(cd);
if (rc < 0) {
@@ -4773,12 +4778,11 @@
/* attention startup */
call_atten_cb(cd, CY_ATTEN_STARTUP, 0);
+ cyttsp5_start_wd_timer(cd);
exit:
if (!rc)
cd->startup_retry_count = 0;
- cyttsp5_start_wd_timer(cd);
-
if (!detected)
rc = -ENODEV;
@@ -4806,6 +4810,10 @@
rc = cyttsp5_startup_(cd, reset);
+ /* Wake the waiters for end of startup */
+ if (!rc)
+ wake_up(&cd->wait_q);
+
if (release_exclusive(cd, cd->dev) < 0)
/* Don't return fail code, mode is already changed. */
dev_err(cd->dev, "%s: fail to release exclusive\n", __func__);
@@ -4818,9 +4826,6 @@
cd->startup_state = STARTUP_NONE;
mutex_unlock(&cd->system_lock);
- /* Wake the waiters for end of startup */
- wake_up(&cd->wait_q);
-
return rc;
}
@@ -5759,6 +5764,8 @@
struct cyttsp5_core_commands *cyttsp5_get_commands(void)
{
+ if (!is_cyttsp5_probe_success)
+ return NULL;
return &_cyttsp5_core_commands;
}
EXPORT_SYMBOL_GPL(cyttsp5_get_commands);
@@ -6436,12 +6443,12 @@
cyttsp5_btn_release(dev);
error_startup_mt:
cyttsp5_mt_release(dev);
+ cyttsp5_free_si_ptrs(cd);
error_startup:
pm_runtime_disable(dev);
device_init_wakeup(dev, 0);
cancel_work_sync(&cd->startup_work);
cyttsp5_stop_wd_timer(cd);
- cyttsp5_free_si_ptrs(cd);
remove_sysfs_interfaces(dev);
error_attr_create:
free_irq(cd->irq, cd);
@@ -6453,7 +6460,7 @@
cyttsp5_del_core(dev);
dev_set_drvdata(dev, NULL);
error_power:
- kfree(cd);
+ cyttsp5_power_init(cd, false);
error_alloc_data:
error_no_pdata:
dev_err(dev, "%s failed.\n", __func__);
diff --git a/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/cam_ife_hw_mgr.c b/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/cam_ife_hw_mgr.c
index 2695c54..8d454cb 100644
--- a/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/cam_ife_hw_mgr.c
+++ b/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/cam_ife_hw_mgr.c
@@ -2164,6 +2164,7 @@
}
CAM_DBG(CAM_ISP, "START CID SRC ... in ctx id:%d", ctx->ctx_index);
+ ctx->dual_ife_irq_mismatch_cnt = 0;
/* Start IFE root node: do nothing */
CAM_DBG(CAM_ISP, "Exit...(success)");
return 0;
@@ -2419,6 +2420,7 @@
memset(ctx->base, 0, sizeof(ctx->base));
/* release cdm handle */
+ ctx->dual_ife_irq_mismatch_cnt = 0;
cam_cdm_release(ctx->cdm_handle);
/* clean context */
@@ -3004,6 +3006,36 @@
}
}
+static void cam_ife_mgr_ctx_irq_dump(struct cam_ife_hw_mgr_ctx *ctx)
+{
+ struct cam_ife_hw_mgr_res *hw_mgr_res;
+ struct cam_hw_intf *hw_intf;
+ struct cam_isp_hw_get_cmd_update cmd_update;
+ int i = 0;
+
+ list_for_each_entry(hw_mgr_res, &ctx->res_list_ife_src, list) {
+ if (hw_mgr_res->res_type == CAM_IFE_HW_MGR_RES_UNINIT)
+ continue;
+ for (i = 0; i < CAM_ISP_HW_SPLIT_MAX; i++) {
+ if (!hw_mgr_res->hw_res[i])
+ continue;
+ switch (hw_mgr_res->hw_res[i]->res_id) {
+ case CAM_ISP_HW_VFE_IN_CAMIF:
+ hw_intf = hw_mgr_res->hw_res[i]->hw_intf;
+ cmd_update.res = hw_mgr_res->hw_res[i];
+ cmd_update.cmd_type =
+ CAM_ISP_HW_CMD_GET_IRQ_REGISTER_DUMP;
+ hw_intf->hw_ops.process_cmd(hw_intf->hw_priv,
+ CAM_ISP_HW_CMD_GET_IRQ_REGISTER_DUMP,
+ &cmd_update, sizeof(cmd_update));
+ break;
+ default:
+ break;
+ }
+ }
+ }
+}
+
static int cam_ife_mgr_cmd(void *hw_mgr_priv, void *cmd_args)
{
int rc = 0;
@@ -3654,11 +3686,26 @@
(event_cnt[core_idx1] &&
(event_cnt[core_idx1] - event_cnt[core_idx0] > 1))) {
+ if (ife_hw_mgr_ctx->dual_ife_irq_mismatch_cnt > 10) {
+ rc = -1;
+ return rc;
+ }
+
CAM_ERR_RATE_LIMIT(CAM_ISP,
- "One of the VFE cound not generate hw event %d",
+ "One of the VFE could not generate hw event %d",
hw_event_type);
- rc = -1;
- return rc;
+ if (event_cnt[core_idx0] >= 2) {
+ event_cnt[core_idx0]--;
+ ife_hw_mgr_ctx->dual_ife_irq_mismatch_cnt++;
+ }
+ if (event_cnt[core_idx1] >= 2) {
+ event_cnt[core_idx1]--;
+ ife_hw_mgr_ctx->dual_ife_irq_mismatch_cnt++;
+ }
+
+ if (ife_hw_mgr_ctx->dual_ife_irq_mismatch_cnt == 1)
+ cam_ife_mgr_ctx_irq_dump(ife_hw_mgr_ctx);
+ rc = 0;
}
CAM_DBG(CAM_ISP, "Only one core_index has given hw event %d",
diff --git a/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/cam_ife_hw_mgr.h b/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/cam_ife_hw_mgr.h
index 9bfa34f..3a910b4 100644
--- a/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/cam_ife_hw_mgr.h
+++ b/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/cam_ife_hw_mgr.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2017-2018, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2017-2019, 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
@@ -97,35 +97,38 @@
/**
* struct cam_vfe_hw_mgr_ctx - IFE HW manager Context object
*
- * @list: used by the ctx list.
- * @common: common acquired context data
- * @ctx_index: acquired context id.
- * @hw_mgr: IFE hw mgr which owns this context
- * @ctx_in_use: flag to tell whether context is active
- * @res_list_ife_in: Starting resource(TPG,PHY0, PHY1...) Can only be
- * one.
- * @res_list_csid: CSID resource list
- * @res_list_ife_src: IFE input resource list
- * @res_list_ife_out: IFE output resoruces array
- * @free_res_list: Free resources list for the branch node
- * @res_pool: memory storage for the free resource list
- * @irq_status0_mask: irq_status0_mask for the context
- * @irq_status1_mask: irq_status1_mask for the context
- * @base device base index array contain the all IFE HW
- * instance associated with this context.
- * @num_base number of valid base data in the base array
- * @cdm_handle cdm hw acquire handle
- * @cdm_ops cdm util operation pointer for building
- * cdm commands
- * @cdm_cmd cdm base and length request pointer
- * @sof_cnt sof count value per core, used for dual VFE
- * @epoch_cnt epoch count value per core, used for dual VFE
- * @eof_cnt eof count value per core, used for dual VFE
- * @overflow_pending flat to specify the overflow is pending for the
- * context
- * @is_rdi_only_context flag to specify the context has only rdi resource
- * @config_done_complete indicator for configuration complete
- * @init_done indicate whether init hw is done
+ * @list: used by the ctx list.
+ * @common: common acquired context data
+ * @ctx_index: acquired context id.
+ * @hw_mgr: IFE hw mgr which owns this context
+ * @ctx_in_use: flag to tell whether context is active
+ * @res_list_ife_in: Starting resource(TPG,PHY0, PHY1...) Can only be
+ * one.
+ * @res_list_csid: CSID resource list
+ * @res_list_ife_src: IFE input resource list
+ * @res_list_ife_out: IFE output resoruces array
+ * @free_res_list: Free resources list for the branch node
+ * @res_pool: memory storage for the free resource list
+ * @irq_status0_mask: irq_status0_mask for the context
+ * @irq_status1_mask: irq_status1_mask for the context
+ * @base device base index array contain the all IFE HW
+ * instance associated with this context.
+ * @num_base number of valid base data in the base array
+ * @cdm_handle cdm hw acquire handle
+ * @cdm_ops cdm util operation pointer for building
+ * cdm commands
+ * @cdm_cmd cdm base and length request pointer
+ * @sof_cnt sof count value per core, used for dual VFE
+ * @epoch_cnt epoch count value per core, used for dual VFE
+ * @eof_cnt eof count value per core, used for dual VFE
+ * @overflow_pending flag to specify the overflow is pending for the
+ * context
+ * @is_rdi_only_context flag to specify the context has only rdi
+ * resource
+ * @config_done_complete indicator for configuration complete
+ * @init_done indicate whether init hw is done
+ * @dual_ife_irq_mismatch_cnt irq mismatch count value per core, used for
+ * dual VFE
*/
struct cam_ife_hw_mgr_ctx {
struct list_head list;
@@ -160,6 +163,7 @@
uint32_t is_rdi_only_context;
struct completion config_done_complete;
bool init_done;
+ uint32_t dual_ife_irq_mismatch_cnt;
};
/**
diff --git a/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/include/cam_isp_hw.h b/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/include/cam_isp_hw.h
index 54aa4c2..2b814ac 100644
--- a/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/include/cam_isp_hw.h
+++ b/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/include/cam_isp_hw.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2017-2018, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2017-2019, 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
@@ -97,6 +97,7 @@
CAM_ISP_HW_CMD_GET_REG_DUMP,
CAM_ISP_HW_CMD_SOF_IRQ_DEBUG,
CAM_ISP_HW_CMD_SET_CAMIF_DEBUG,
+ CAM_ISP_HW_CMD_GET_IRQ_REGISTER_DUMP,
CAM_ISP_HW_CMD_MAX,
};
diff --git a/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/cam_vfe_core.c b/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/cam_vfe_core.c
index f6becfb..d8af627 100644
--- a/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/cam_vfe_core.c
+++ b/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/cam_vfe_core.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2017-2018, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2017-2019, 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
@@ -696,6 +696,7 @@
case CAM_ISP_HW_CMD_CLOCK_UPDATE:
case CAM_ISP_HW_CMD_BW_UPDATE:
case CAM_ISP_HW_CMD_BW_CONTROL:
+ case CAM_ISP_HW_CMD_GET_IRQ_REGISTER_DUMP:
rc = core_info->vfe_top->hw_ops.process_cmd(
core_info->vfe_top->top_priv, cmd_type, cmd_args,
arg_size);
diff --git a/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/vfe_top/cam_vfe_camif_ver2.c b/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/vfe_top/cam_vfe_camif_ver2.c
index fc257ec..528eb6d 100644
--- a/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/vfe_top/cam_vfe_camif_ver2.c
+++ b/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/vfe_top/cam_vfe_camif_ver2.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2017-2018, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2017-2019, 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
@@ -378,6 +378,40 @@
return rc;
}
+static int cam_vfe_camif_irq_reg_dump(
+ struct cam_isp_resource_node *camif_res)
+{
+ struct cam_vfe_mux_camif_data *camif_priv;
+ struct cam_vfe_soc_private *soc_private;
+ int rc = 0;
+
+ if (!camif_res) {
+ CAM_ERR(CAM_ISP, "Error! Invalid input arguments\n");
+ return -EINVAL;
+ }
+
+ if ((camif_res->res_state == CAM_ISP_RESOURCE_STATE_RESERVED) ||
+ (camif_res->res_state == CAM_ISP_RESOURCE_STATE_AVAILABLE)) {
+ CAM_ERR(CAM_ISP, "Error! Invalid state\n");
+ return 0;
+ }
+
+ camif_priv = (struct cam_vfe_mux_camif_data *)camif_res->res_priv;
+ soc_private = camif_priv->soc_info->soc_private;
+
+ CAM_INFO(CAM_ISP,
+ "Core Id =%d Mask reg: offset 0x%x val 0x%x offset 0x%x val 0x%x",
+ camif_priv->hw_intf->hw_idx,
+ 0x5c, cam_io_r_mb(camif_priv->mem_base + 0x5c),
+ 0x60, cam_io_r_mb(camif_priv->mem_base + 0x60));
+ CAM_INFO(CAM_ISP,
+ "Core Id =%d Status reg: offset 0x%x val 0x%x offset 0x%x val 0x%x",
+ camif_priv->hw_intf->hw_idx,
+ 0x6c, cam_io_r_mb(camif_priv->mem_base + 0x6c),
+ 0x70, cam_io_r_mb(camif_priv->mem_base + 0x70));
+ return rc;
+}
+
static int cam_vfe_camif_resource_stop(
struct cam_isp_resource_node *camif_res)
{
@@ -465,6 +499,9 @@
(struct cam_vfe_mux_camif_data *)rsrc_node->res_priv;
camif_priv->camif_debug = *((uint32_t *)cmd_args);
break;
+ case CAM_ISP_HW_CMD_GET_IRQ_REGISTER_DUMP:
+ rc = cam_vfe_camif_irq_reg_dump(rsrc_node);
+ break;
default:
CAM_ERR(CAM_ISP,
"unsupported process command:%d", cmd_type);
diff --git a/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/vfe_top/cam_vfe_top_ver2.c b/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/vfe_top/cam_vfe_top_ver2.c
index 47a0438..7ec11e8 100644
--- a/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/vfe_top/cam_vfe_top_ver2.c
+++ b/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/vfe_top/cam_vfe_top_ver2.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2017-2018, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2017-2019, 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
@@ -379,6 +379,19 @@
return -EINVAL;
}
+static int cam_vfe_get_irq_register_dump(
+ struct cam_vfe_top_ver2_priv *top_priv,
+ void *cmd_args, uint32_t arg_size)
+{
+ struct cam_isp_hw_get_cmd_update *cmd_update = cmd_args;
+
+ if (cmd_update->res->process_cmd)
+ cmd_update->res->process_cmd(cmd_update->res,
+ CAM_ISP_HW_CMD_GET_IRQ_REGISTER_DUMP, cmd_args, arg_size);
+
+ return 0;
+}
+
int cam_vfe_top_get_hw_caps(void *device_priv,
void *get_hw_cap_args, uint32_t arg_size)
{
@@ -657,6 +670,10 @@
case CAM_ISP_HW_CMD_BW_CONTROL:
rc = cam_vfe_top_bw_control(top_priv, cmd_args, arg_size);
break;
+ case CAM_ISP_HW_CMD_GET_IRQ_REGISTER_DUMP:
+ rc = cam_vfe_get_irq_register_dump(top_priv,
+ cmd_args, arg_size);
+ break;
default:
rc = -EINVAL;
CAM_ERR(CAM_ISP, "Error! Invalid cmd:%d", cmd_type);
diff --git a/drivers/media/platform/msm/camera/cam_lrme/lrme_hw_mgr/lrme_hw/cam_lrme_hw_core.c b/drivers/media/platform/msm/camera/cam_lrme/lrme_hw_mgr/lrme_hw/cam_lrme_hw_core.c
index 9628c81..dce58f3 100644
--- a/drivers/media/platform/msm/camera/cam_lrme/lrme_hw_mgr/lrme_hw/cam_lrme_hw_core.c
+++ b/drivers/media/platform/msm/camera/cam_lrme/lrme_hw_mgr/lrme_hw/cam_lrme_hw_core.c
@@ -899,7 +899,7 @@
lrme_core->state = CAM_LRME_CORE_STATE_INIT;
} else {
CAM_ERR(CAM_LRME, "HW in wrong state %d", lrme_core->state);
- return -EINVAL;
+ rc = -EINVAL;
}
unlock:
@@ -951,7 +951,8 @@
if (lrme_core->req_submit != NULL) {
CAM_ERR(CAM_LRME, "req_submit is not NULL");
- return -EBUSY;
+ rc = -EBUSY;
+ goto error;
}
rc = cam_lrme_hw_util_submit_req(lrme_core, frame_req);
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 8c2853b..927e00b 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
@@ -1,4 +1,4 @@
-/* Copyright (c) 2017-2018, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2017-2019, 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
@@ -51,6 +51,10 @@
CAM_DBG(CAM_CCI, "master %d", master);
if (master < MASTER_MAX && master >= 0) {
mutex_lock(&cci_dev->cci_master_info[master].mutex);
+ mutex_lock(&cci_dev->
+ cci_master_info[master].mutex_q[QUEUE_0]);
+ mutex_lock(&cci_dev->
+ cci_master_info[master].mutex_q[QUEUE_1]);
flush_workqueue(cci_dev->write_wq[master]);
/* Re-initialize the completion */
reinit_completion(
@@ -73,6 +77,10 @@
CCI_TIMEOUT);
if (rc <= 0)
CAM_ERR(CAM_CCI, "wait failed %d", rc);
+ mutex_unlock(&cci_dev->
+ cci_master_info[master].mutex_q[QUEUE_1]);
+ mutex_unlock(&cci_dev->
+ cci_master_info[master].mutex_q[QUEUE_0]);
mutex_unlock(&cci_dev->cci_master_info[master].mutex);
}
return 0;
diff --git a/drivers/media/platform/msm/camera/cam_sensor_module/cam_eeprom/cam_eeprom_core.c b/drivers/media/platform/msm/camera/cam_sensor_module/cam_eeprom/cam_eeprom_core.c
index 6797ba4..20976af 100644
--- a/drivers/media/platform/msm/camera/cam_sensor_module/cam_eeprom/cam_eeprom_core.c
+++ b/drivers/media/platform/msm/camera/cam_sensor_module/cam_eeprom/cam_eeprom_core.c
@@ -974,7 +974,7 @@
&eeprom_cap,
sizeof(struct cam_eeprom_query_cap_t))) {
CAM_ERR(CAM_EEPROM, "Failed Copy to User");
- return -EFAULT;
+ rc = -EFAULT;
goto release_mutex;
}
CAM_DBG(CAM_EEPROM, "eeprom_cap: ID: %d", eeprom_cap.slot_info);
diff --git a/drivers/media/platform/msm/camera_v3/cam_core/cam_context_utils.c b/drivers/media/platform/msm/camera_v3/cam_core/cam_context_utils.c
index 7600cbb..11bad5d 100644
--- a/drivers/media/platform/msm/camera_v3/cam_core/cam_context_utils.c
+++ b/drivers/media/platform/msm/camera_v3/cam_core/cam_context_utils.c
@@ -462,6 +462,17 @@
ctx->dev_name, ctx->ctx_id, req->request_id);
for (j = 0; j < req->num_in_map_entries; j++) {
+ rc = cam_sync_check_valid(
+ req->in_map_entries[j].sync_id);
+ if (rc) {
+ CAM_ERR(CAM_CTXT,
+ "invalid in map sync object %d",
+ req->in_map_entries[j].sync_id);
+ goto put_ref;
+ }
+ }
+
+ for (j = 0; j < req->num_in_map_entries; j++) {
cam_context_getref(ctx);
rc = cam_sync_register_callback(
cam_context_sync_callback,
@@ -482,7 +493,9 @@
ctx->dev_name, ctx->ctx_id,
req->request_id);
- goto put_ctx_ref;
+ cam_context_putref(ctx);
+ goto put_ref;
+
}
CAM_DBG(CAM_CTXT, "register in fence cb: %d ret = %d",
req->in_map_entries[j].sync_id, rc);
@@ -491,9 +504,6 @@
}
return rc;
-put_ctx_ref:
- for (--j; j >= 0; j--)
- cam_context_putref(ctx);
put_ref:
for (--i; i >= 0; i--) {
rc = cam_sync_put_obj_ref(req->out_map_entries[i].sync_id);
diff --git a/drivers/media/platform/msm/camera_v3/cam_cpas/cam_cpas_hw.c b/drivers/media/platform/msm/camera_v3/cam_cpas/cam_cpas_hw.c
index 4276b35..a68e207 100644
--- a/drivers/media/platform/msm/camera_v3/cam_cpas/cam_cpas_hw.c
+++ b/drivers/media/platform/msm/camera_v3/cam_cpas/cam_cpas_hw.c
@@ -980,8 +980,10 @@
return -EINVAL;
}
- if (!CAM_CPAS_CLIENT_VALID(client_indx))
+ if (!CAM_CPAS_CLIENT_VALID(client_indx)) {
+ CAM_ERR(CAM_CPAS, "Client index invalid %d", client_indx);
return -EINVAL;
+ }
mutex_lock(&cpas_hw->hw_mutex);
mutex_lock(&cpas_core->client_mutex[client_indx]);
@@ -1099,8 +1101,10 @@
cmd_hw_stop = (struct cam_cpas_hw_cmd_stop *)stop_args;
client_indx = CAM_CPAS_GET_CLIENT_IDX(cmd_hw_stop->client_handle);
- if (!CAM_CPAS_CLIENT_VALID(client_indx))
+ if (!CAM_CPAS_CLIENT_VALID(client_indx)) {
+ CAM_ERR(CAM_CPAS, "Client index invalid %d", client_indx);
return -EINVAL;
+ }
mutex_lock(&cpas_hw->hw_mutex);
mutex_lock(&cpas_core->client_mutex[client_indx]);
@@ -1162,14 +1166,20 @@
ahb_vote.vote.level = CAM_SUSPEND_VOTE;
rc = cam_cpas_util_apply_client_ahb_vote(cpas_hw, cpas_client,
&ahb_vote, NULL);
- if (rc)
+ if (rc) {
+ CAM_ERR(CAM_CPAS, "ahb vote failed for %s rc %d",
+ cpas_client->data.identifier, rc);
goto done;
+ }
axi_vote.uncompressed_bw = 0;
axi_vote.compressed_bw = 0;
axi_vote.compressed_bw_ab = 0;
rc = cam_cpas_util_apply_client_axi_vote(cpas_hw,
cpas_client, &axi_vote);
+ if (rc)
+ CAM_ERR(CAM_CPAS, "axi vote failed for %s rc %d",
+ cpas_client->data.identifier, rc);
done:
mutex_unlock(&cpas_core->client_mutex[client_indx]);
@@ -1234,6 +1244,13 @@
rc = cam_common_util_get_string_index(soc_private->client_name,
soc_private->num_clients, client_name, &client_indx);
+ if (rc) {
+ CAM_ERR(CAM_CPAS, "No match found for client %s",
+ client_name);
+ mutex_unlock(&cpas_hw->hw_mutex);
+ return rc;
+ }
+
mutex_lock(&cpas_core->client_mutex[client_indx]);
if (rc || !CAM_CPAS_CLIENT_VALID(client_indx) ||
diff --git a/drivers/media/platform/msm/camera_v3/cam_icp/icp_hw/a5_hw/a5_core.c b/drivers/media/platform/msm/camera_v3/cam_icp/icp_hw/a5_hw/a5_core.c
index e13d7f2..4dbc8f1 100644
--- a/drivers/media/platform/msm/camera_v3/cam_icp/icp_hw/a5_hw/a5_core.c
+++ b/drivers/media/platform/msm/camera_v3/cam_icp/icp_hw/a5_hw/a5_core.c
@@ -464,7 +464,11 @@
case CAM_ICP_A5_CMD_CPAS_STOP:
if (core_info->cpas_start) {
- cam_cpas_stop(core_info->cpas_handle);
+ rc = cam_cpas_stop(core_info->cpas_handle);
+ if (rc) {
+ CAM_ERR(CAM_ICP, "cpas stop failed %d", rc);
+ return rc;
+ }
core_info->cpas_start = false;
}
break;
diff --git a/drivers/media/platform/msm/camera_v3/cam_icp/icp_hw/bps_hw/bps_core.c b/drivers/media/platform/msm/camera_v3/cam_icp/icp_hw/bps_hw/bps_core.c
index c94276c..f522f71 100644
--- a/drivers/media/platform/msm/camera_v3/cam_icp/icp_hw/bps_hw/bps_core.c
+++ b/drivers/media/platform/msm/camera_v3/cam_icp/icp_hw/bps_hw/bps_core.c
@@ -347,7 +347,11 @@
case CAM_ICP_BPS_CMD_CPAS_STOP:
if (core_info->cpas_start) {
- cam_cpas_stop(core_info->cpas_handle);
+ rc = cam_cpas_stop(core_info->cpas_handle);
+ if (rc) {
+ CAM_ERR(CAM_ICP, "cpas stop failed %d", rc);
+ return rc;
+ }
core_info->cpas_start = false;
}
break;
diff --git a/drivers/media/platform/msm/camera_v3/cam_icp/icp_hw/icp_hw_mgr/cam_icp_hw_mgr.c b/drivers/media/platform/msm/camera_v3/cam_icp/icp_hw/icp_hw_mgr/cam_icp_hw_mgr.c
index cfc474a..03b93ac 100644
--- a/drivers/media/platform/msm/camera_v3/cam_icp/icp_hw/icp_hw_mgr/cam_icp_hw_mgr.c
+++ b/drivers/media/platform/msm/camera_v3/cam_icp/icp_hw/icp_hw_mgr/cam_icp_hw_mgr.c
@@ -4077,8 +4077,13 @@
for (i = 0; i < packet->num_io_configs; i++) {
for (j = 0; j < CAM_PACKET_MAX_PLANES; j++) {
- if (!io_cfg[i].mem_handle[j])
+ if (!io_cfg[i].mem_handle[j]) {
+ CAM_ERR(CAM_ICP,
+ "Mem Handle %d is NULL for %d io config",
+ j, i);
break;
+ }
+
if (GET_FD_FROM_HANDLE(io_cfg[i].mem_handle[j]) ==
GET_FD_FROM_HANDLE(pf_buf_info)) {
diff --git a/drivers/media/platform/msm/camera_v3/cam_icp/icp_hw/ipe_hw/ipe_core.c b/drivers/media/platform/msm/camera_v3/cam_icp/icp_hw/ipe_hw/ipe_core.c
index ae3d134..ae58b34 100644
--- a/drivers/media/platform/msm/camera_v3/cam_icp/icp_hw/ipe_hw/ipe_core.c
+++ b/drivers/media/platform/msm/camera_v3/cam_icp/icp_hw/ipe_hw/ipe_core.c
@@ -342,7 +342,11 @@
case CAM_ICP_IPE_CMD_CPAS_STOP:
if (core_info->cpas_start) {
- cam_cpas_stop(core_info->cpas_handle);
+ rc = cam_cpas_stop(core_info->cpas_handle);
+ if (rc) {
+ CAM_ERR(CAM_ICP, "CPAS stop failed %d", rc);
+ return rc;
+ }
core_info->cpas_start = false;
}
break;
diff --git a/drivers/media/platform/msm/camera_v3/cam_isp/cam_isp_context.c b/drivers/media/platform/msm/camera_v3/cam_isp/cam_isp_context.c
index 57375c4..94d531d 100644
--- a/drivers/media/platform/msm/camera_v3/cam_isp/cam_isp_context.c
+++ b/drivers/media/platform/msm/camera_v3/cam_isp/cam_isp_context.c
@@ -38,18 +38,26 @@
static void __cam_isp_ctx_update_state_monitor_array(
struct cam_isp_context *ctx_isp,
- enum cam_isp_state_change_trigger trigger_type,
- uint32_t req_id)
+ enum cam_isp_hw_event_type hw_event,
+ enum cam_isp_ctx_activated_substate curr_state,
+ enum cam_isp_ctx_activated_substate next_state)
{
int iterator = 0;
iterator = INC_STATE_MONITOR_HEAD(&ctx_isp->state_monitor_head);
ctx_isp->cam_isp_ctx_state_monitor[iterator].curr_state =
- ctx_isp->substate_activated;
- ctx_isp->cam_isp_ctx_state_monitor[iterator].trigger =
- trigger_type;
- ctx_isp->cam_isp_ctx_state_monitor[iterator].req_id =
- req_id;
+ curr_state;
+ ctx_isp->cam_isp_ctx_state_monitor[iterator].next_state =
+ next_state;
+ ctx_isp->cam_isp_ctx_state_monitor[iterator].hw_event =
+ hw_event;
+ ctx_isp->cam_isp_ctx_state_monitor[iterator].last_reported_id =
+ ctx_isp->req_info.reported_req_id;
+ ctx_isp->cam_isp_ctx_state_monitor[iterator].last_applied_req_id =
+ ctx_isp->req_info.last_applied_req_id;
+ ctx_isp->cam_isp_ctx_state_monitor[iterator].frame_id =
+ ctx_isp->frame_id;
+
ctx_isp->cam_isp_ctx_state_monitor[iterator].evt_time_stamp =
jiffies_to_msecs(jiffies);
}
@@ -79,17 +87,17 @@
uint32_t evt_id)
{
switch (evt_id) {
- case CAM_ISP_STATE_CHANGE_TRIGGER_ERROR:
+ case CAM_ISP_HW_EVENT_ERROR:
return "ERROR";
- case CAM_ISP_STATE_CHANGE_TRIGGER_SOF:
+ case CAM_ISP_HW_EVENT_SOF:
return "SOF";
- case CAM_ISP_STATE_CHANGE_TRIGGER_REG_UPDATE:
+ case CAM_ISP_HW_EVENT_REG_UPDATE:
return "REG_UPDATE";
- case CAM_ISP_STATE_CHANGE_TRIGGER_EPOCH:
+ case CAM_ISP_HW_EVENT_EPOCH:
return "EPOCH";
- case CAM_ISP_STATE_CHANGE_TRIGGER_EOF:
+ case CAM_ISP_HW_EVENT_EOF:
return "EOF";
- case CAM_ISP_STATE_CHANGE_TRIGGER_DONE:
+ case CAM_ISP_HW_EVENT_DONE:
return "DONE";
default:
return "CAM_ISP_EVENT_INVALID";
@@ -97,29 +105,58 @@
}
static void __cam_isp_ctx_dump_state_monitor_array(
- struct cam_isp_context *ctx_isp)
+ struct cam_isp_context *ctx_isp, bool log_rate_limit)
{
int i = 0;
uint64_t state_head = 0;
uint64_t index;
+ struct cam_isp_context_state_monitor *ctx_monitor;
state_head = atomic64_read(&ctx_isp->state_monitor_head);
- CAM_ERR_RATE_LIMIT(CAM_ISP,
- "Dumping state information for preceding requests");
+
+ ctx_monitor = ctx_isp->cam_isp_ctx_state_monitor;
+
+ if (log_rate_limit)
+ CAM_INFO_RATE_LIMIT_CUSTOM(CAM_ISP, 5, 20,
+ "Dumping state information for preceding requests");
+ else
+ CAM_INFO(CAM_ISP,
+ "Dumping state information for preceding requests");
for (i = CAM_ISP_CTX_STATE_MONITOR_MAX_ENTRIES - 1; i >= 0;
i--) {
index = (((state_head - i) +
CAM_ISP_CTX_STATE_MONITOR_MAX_ENTRIES) %
CAM_ISP_CTX_STATE_MONITOR_MAX_ENTRIES);
- CAM_ERR_RATE_LIMIT(CAM_ISP,
- "time[0x%llx] req_id[%u] state[%s] evt_type[%s]",
- ctx_isp->cam_isp_ctx_state_monitor[index].evt_time_stamp,
- ctx_isp->cam_isp_ctx_state_monitor[index].req_id,
- __cam_isp_ctx_substate_val_to_type(
- ctx_isp->cam_isp_ctx_state_monitor[index].curr_state),
- __cam_isp_hw_evt_val_to_type(
- ctx_isp->cam_isp_ctx_state_monitor[index].trigger));
+
+ if (log_rate_limit) {
+ CAM_INFO_RATE_LIMIT_CUSTOM(CAM_ISP, 5, 20,
+ "time[%lld] last reported req_id[%lld] frame id[%lld] applied id[%lld] current state[%s] next state[%s] hw_event[%s]",
+ ctx_monitor[index].evt_time_stamp,
+ ctx_monitor[index].last_reported_id,
+ ctx_monitor[index].frame_id,
+ ctx_monitor[index].last_applied_req_id,
+ __cam_isp_ctx_substate_val_to_type(
+ ctx_monitor[index].curr_state),
+ __cam_isp_ctx_substate_val_to_type(
+ ctx_monitor[index].next_state),
+ __cam_isp_hw_evt_val_to_type(
+ ctx_monitor[index].hw_event));
+
+ } else {
+ CAM_INFO(CAM_ISP,
+ "time[%lld] last reported req_id[%lld] frame id[%lld] applied id[%lld] current state[%s] next state[%s] hw_event[%s]",
+ ctx_monitor[index].evt_time_stamp,
+ ctx_monitor[index].last_reported_id,
+ ctx_monitor[index].frame_id,
+ ctx_monitor[index].last_applied_req_id,
+ __cam_isp_ctx_substate_val_to_type(
+ ctx_monitor[index].curr_state),
+ __cam_isp_ctx_substate_val_to_type(
+ ctx_monitor[index].next_state),
+ __cam_isp_hw_evt_val_to_type(
+ ctx_monitor[index].hw_event));
+ }
}
}
@@ -403,7 +440,7 @@
struct cam_context *ctx = ctx_isp->base;
if (list_empty(&ctx->active_req_list)) {
- CAM_DBG(CAM_ISP, "Buf done with no active request!");
+ CAM_WARN(CAM_ISP, "Buf done with no active request!");
goto end;
}
@@ -508,6 +545,10 @@
CAM_DBG(CAM_REQ,
"Move active request %lld to pending list(cnt = %d) [bubble recovery], ctx %u",
req->request_id, ctx_isp->active_req_cnt, ctx->ctx_id);
+ __cam_isp_ctx_update_state_monitor_array(ctx_isp,
+ CAM_ISP_HW_EVENT_DONE,
+ ctx_isp->substate_activated,
+ ctx_isp->substate_activated);
} else {
list_del_init(&req->list);
list_add_tail(&req->list, &ctx->free_req_list);
@@ -515,32 +556,39 @@
CAM_DBG(CAM_REQ,
"Move active request %lld to free list(cnt = %d) [all fences done], ctx %u",
req->request_id, ctx_isp->active_req_cnt, ctx->ctx_id);
+ ctx_isp->req_info.last_bufdone_req_id = req->request_id;
+ ctx_isp->req_info.last_bufdone_time_stamp =
+ jiffies_to_msecs(jiffies);
+ __cam_isp_ctx_update_state_monitor_array(ctx_isp,
+ CAM_ISP_HW_EVENT_DONE,
+ ctx_isp->substate_activated,
+ ctx_isp->substate_activated);
}
end:
- __cam_isp_ctx_update_state_monitor_array(ctx_isp,
- CAM_ISP_STATE_CHANGE_TRIGGER_DONE,
- ctx_isp->base->req_list->request_id);
return rc;
}
static void __cam_isp_ctx_send_sof_boot_timestamp(
struct cam_isp_context *ctx_isp, uint64_t request_id,
- uint32_t sof_event_status)
+ uint32_t sof_event_status, uint64_t delta_ts)
{
struct cam_req_mgr_message req_msg;
req_msg.session_hdl = ctx_isp->base->session_hdl;
req_msg.u.frame_msg.frame_id = ctx_isp->frame_id;
req_msg.u.frame_msg.request_id = request_id;
- req_msg.u.frame_msg.timestamp = ctx_isp->boot_timestamp;
req_msg.u.frame_msg.link_hdl = ctx_isp->base->link_hdl;
req_msg.u.frame_msg.sof_status = sof_event_status;
+ req_msg.u.frame_msg.timestamp = ctx_isp->prev_boot_timestamp + delta_ts;
+
CAM_DBG(CAM_ISP,
- "request id:%lld frame number:%lld boot time stamp:0x%llx",
- request_id, ctx_isp->frame_id,
- ctx_isp->boot_timestamp);
+ "req id:%lld frame num:%lld bt_ts:0x%llx pre_bt_ts:0x%llx diff:0x%llx",
+ request_id, ctx_isp->frame_id,
+ ctx_isp->boot_timestamp, ctx_isp->prev_boot_timestamp,
+ delta_ts);
+
if (cam_req_mgr_notify_message(&req_msg,
V4L_EVENT_CAM_REQ_MGR_SOF_BOOT_TS,
@@ -548,6 +596,8 @@
CAM_ERR(CAM_ISP,
"Error in notifying the boot time for req id:%lld",
request_id);
+
+ ctx_isp->prev_boot_timestamp = req_msg.u.frame_msg.timestamp;
}
@@ -556,6 +606,7 @@
uint32_t sof_event_status)
{
struct cam_req_mgr_message req_msg;
+ uint64_t delta_ts;
req_msg.session_hdl = ctx_isp->base->session_hdl;
req_msg.u.frame_msg.frame_id = ctx_isp->frame_id;
@@ -565,9 +616,9 @@
req_msg.u.frame_msg.sof_status = sof_event_status;
CAM_DBG(CAM_ISP,
- "request id:%lld frame number:%lld SOF time stamp:0x%llx",
+ "request id:%lld frame number:%lld SOF time stamp:0x%llx, Prev SOF time:0x%llx",
request_id, ctx_isp->frame_id,
- ctx_isp->sof_timestamp_val);
+ ctx_isp->sof_timestamp_val, ctx_isp->prev_sof_timestamp_val);
CAM_DBG(CAM_ISP, "sof status:%d", sof_event_status);
if (cam_req_mgr_notify_message(&req_msg,
@@ -575,9 +626,17 @@
CAM_ERR(CAM_ISP,
"Error in notifying the sof time for req id:%lld",
request_id);
+ delta_ts = ctx_isp->sof_timestamp_val -
+ ctx_isp->prev_sof_timestamp_val;
__cam_isp_ctx_send_sof_boot_timestamp(ctx_isp,
- request_id, sof_event_status);
+ request_id, sof_event_status,
+ (ctx_isp->prev_sof_timestamp_val == 0) ?
+ ctx_isp->boot_timestamp :
+ delta_ts);
+
+ ctx_isp->prev_sof_timestamp_val =
+ ctx_isp->sof_timestamp_val;
}
@@ -658,6 +717,7 @@
notify.dev_hdl = ctx->dev_hdl;
notify.frame_id = ctx_isp->frame_id;
notify.trigger = CAM_TRIGGER_POINT_SOF;
+ notify.sof_timestamp_val = ctx_isp->sof_timestamp_val;
ctx->ctx_crm_intf->notify_trigger(¬ify);
CAM_DBG(CAM_ISP, "Notify CRM SOF frame %lld ctx %u",
@@ -665,9 +725,12 @@
}
list_for_each_entry(req, &ctx->active_req_list, list) {
- if (req->request_id > ctx_isp->reported_req_id) {
+ if (req->request_id >
+ ctx_isp->req_info.reported_req_id) {
request_id = req->request_id;
- ctx_isp->reported_req_id = request_id;
+ ctx_isp->req_info.reported_req_id = request_id;
+ ctx_isp->req_info.last_reported_id_time_stamp =
+ jiffies_to_msecs(jiffies);
break;
}
}
@@ -675,6 +738,17 @@
if (ctx_isp->substate_activated == CAM_ISP_CTX_ACTIVATED_BUBBLE)
request_id = 0;
+ if (request_id && ctx_isp->req_info.reported_req_id &&
+ ((request_id - ctx_isp->req_info.reported_req_id) >
+ 1)){
+ CAM_INFO(CAM_ISP,
+ "ctx:%d curr req id: %lld last reported id:%lld",
+ ctx->ctx_id, request_id,
+ ctx_isp->req_info.reported_req_id);
+
+ __cam_isp_ctx_dump_state_monitor_array(ctx_isp, true);
+ }
+
__cam_isp_ctx_send_sof_timestamp(ctx_isp, request_id,
CAM_REQ_MGR_SOF_EVENT_SUCCESS);
} else {
@@ -742,8 +816,7 @@
ctx_isp->frame_id++;
ctx_isp->sof_timestamp_val = sof_event_data->timestamp;
ctx_isp->boot_timestamp = sof_event_data->boot_time;
- __cam_isp_ctx_update_state_monitor_array(ctx_isp,
- CAM_ISP_STATE_CHANGE_TRIGGER_SOF, req->request_id);
+
CAM_DBG(CAM_ISP, "frame id: %lld time stamp:0x%llx, ctx %u",
ctx_isp->frame_id, ctx_isp->sof_timestamp_val, ctx->ctx_id);
@@ -778,11 +851,7 @@
CAM_ERR(CAM_ISP,
"receive rup in unexpected state");
}
- if (req != NULL) {
- __cam_isp_ctx_update_state_monitor_array(ctx_isp,
- CAM_ISP_STATE_CHANGE_TRIGGER_REG_UPDATE,
- req->request_id);
- }
+
end:
return rc;
}
@@ -800,7 +869,8 @@
* If no wait req in epoch, this is an error case.
* The recovery is to go back to sof state
*/
- CAM_ERR(CAM_ISP, "No wait request");
+ CAM_ERR(CAM_ISP, "Ctx:%d No wait request", ctx->ctx_id);
+ __cam_isp_ctx_dump_state_monitor_array(ctx_isp, true);
ctx_isp->substate_activated = CAM_ISP_CTX_ACTIVATED_SOF;
/* Send SOF event as empty frame*/
@@ -815,7 +885,9 @@
req_isp = (struct cam_isp_ctx_req *)req->req_priv;
req_isp->bubble_detected = true;
- CAM_DBG(CAM_ISP, "Report Bubble flag %d", req_isp->bubble_report);
+ CAM_INFO(CAM_ISP, "ctx:%d Report Bubble flag %d req id:%lld",
+ ctx->ctx_id, req_isp->bubble_report, req->request_id);
+ __cam_isp_ctx_dump_state_monitor_array(ctx_isp, true);
if (req_isp->bubble_report && ctx->ctx_crm_intf &&
ctx->ctx_crm_intf->notify_err) {
struct cam_req_mgr_error_notify notify;
@@ -842,9 +914,11 @@
list_del_init(&req->list);
list_add_tail(&req->list, &ctx->active_req_list);
- if (req->request_id > ctx_isp->reported_req_id) {
+ if (req->request_id > ctx_isp->req_info.reported_req_id) {
request_id = req->request_id;
- ctx_isp->reported_req_id = request_id;
+ ctx_isp->req_info.reported_req_id = request_id;
+ ctx_isp->req_info.last_reported_id_time_stamp =
+ jiffies_to_msecs(jiffies);
}
__cam_isp_ctx_send_sof_timestamp(ctx_isp, request_id,
CAM_REQ_MGR_SOF_EVENT_ERROR);
@@ -853,15 +927,7 @@
CAM_DBG(CAM_ISP, "next substate %d",
ctx_isp->substate_activated);
end:
- if (request_id == 0) {
- req = list_last_entry(&ctx->active_req_list,
- struct cam_ctx_request, list);
- __cam_isp_ctx_update_state_monitor_array(ctx_isp,
- CAM_ISP_STATE_CHANGE_TRIGGER_EPOCH, req->request_id);
- } else {
- __cam_isp_ctx_update_state_monitor_array(ctx_isp,
- CAM_ISP_STATE_CHANGE_TRIGGER_EPOCH, request_id);
- }
+
return 0;
}
@@ -884,7 +950,6 @@
int rc = 0;
struct cam_context *ctx = ctx_isp->base;
struct cam_isp_hw_sof_event_data *sof_event_data = evt_data;
- struct cam_ctx_request *req;
if (!evt_data) {
CAM_ERR(CAM_ISP, "in valid sof event data");
@@ -900,12 +965,6 @@
else
CAM_DBG(CAM_ISP, "Still need to wait for the buf done");
- req = list_last_entry(&ctx->active_req_list,
- struct cam_ctx_request, list);
- if (req)
- __cam_isp_ctx_update_state_monitor_array(ctx_isp,
- CAM_ISP_STATE_CHANGE_TRIGGER_SOF,
- ctx->req_list->request_id);
CAM_DBG(CAM_ISP, "next substate %d",
ctx_isp->substate_activated);
@@ -952,7 +1011,8 @@
* If no pending req in epoch, this is an error case.
* Just go back to the bubble state.
*/
- CAM_ERR(CAM_ISP, "No pending request.");
+ CAM_ERR(CAM_ISP, "ctx:%d No pending request.", ctx->ctx_id);
+ __cam_isp_ctx_dump_state_monitor_array(ctx_isp, true);
__cam_isp_ctx_send_sof_timestamp(ctx_isp, request_id,
CAM_REQ_MGR_SOF_EVENT_SUCCESS);
@@ -964,6 +1024,9 @@
list);
req_isp = (struct cam_isp_ctx_req *)req->req_priv;
req_isp->bubble_detected = true;
+ CAM_INFO(CAM_ISP, "Ctx:%d Report Bubble flag %d req id:%lld",
+ ctx->ctx_id, req_isp->bubble_report, req->request_id);
+ __cam_isp_ctx_dump_state_monitor_array(ctx_isp, true);
if (req_isp->bubble_report && ctx->ctx_crm_intf &&
ctx->ctx_crm_intf->notify_err) {
@@ -992,9 +1055,11 @@
list_add_tail(&req->list, &ctx->active_req_list);
if (!req_isp->bubble_report) {
- if (req->request_id > ctx_isp->reported_req_id) {
+ if (req->request_id > ctx_isp->req_info.reported_req_id) {
request_id = req->request_id;
- ctx_isp->reported_req_id = request_id;
+ ctx_isp->req_info.reported_req_id = request_id;
+ ctx_isp->req_info.last_reported_id_time_stamp =
+ jiffies_to_msecs(jiffies);
__cam_isp_ctx_send_sof_timestamp(ctx_isp, request_id,
CAM_REQ_MGR_SOF_EVENT_ERROR);
} else
@@ -1007,11 +1072,7 @@
ctx_isp->substate_activated = CAM_ISP_CTX_ACTIVATED_BUBBLE;
CAM_DBG(CAM_ISP, "next substate %d", ctx_isp->substate_activated);
end:
- req = list_last_entry(&ctx->active_req_list, struct cam_ctx_request,
- list);
- if (req)
- __cam_isp_ctx_update_state_monitor_array(ctx_isp,
- CAM_ISP_STATE_CHANGE_TRIGGER_EPOCH, req->request_id);
+
return 0;
}
@@ -1023,9 +1084,7 @@
(struct cam_isp_hw_done_event_data *) evt_data;
rc = __cam_isp_ctx_handle_buf_done_in_activated_state(ctx_isp, done, 1);
- __cam_isp_ctx_update_state_monitor_array(ctx_isp,
- CAM_ISP_STATE_CHANGE_TRIGGER_DONE,
- ctx_isp->base->req_list->request_id);
+
return rc;
}
@@ -1083,9 +1142,6 @@
if (error_event_data->enable_reg_dump)
cam_isp_ctx_dump_req(req_isp);
- __cam_isp_ctx_update_state_monitor_array(ctx_isp,
- CAM_ISP_STATE_CHANGE_TRIGGER_ERROR, req_to_dump->request_id);
-
list_for_each_entry_safe(req, req_temp,
&ctx->active_req_list, list) {
req_isp = (struct cam_isp_ctx_req *) req->req_priv;
@@ -1176,14 +1232,15 @@
end:
do {
if (list_empty(&ctx->pending_req_list)) {
- error_request_id = ctx_isp->last_applied_req_id + 1;
+ error_request_id =
+ ctx_isp->req_info.last_applied_req_id + 1;
req_isp = NULL;
break;
}
req = list_first_entry(&ctx->pending_req_list,
struct cam_ctx_request, list);
req_isp = (struct cam_isp_ctx_req *) req->req_priv;
- error_request_id = ctx_isp->last_applied_req_id;
+ error_request_id = ctx_isp->req_info.last_applied_req_id;
if (req_isp->bubble_report) {
req_to_report = req;
@@ -1201,7 +1258,8 @@
list_del_init(&req->list);
list_add_tail(&req->list, &ctx->free_req_list);
- } while (req->request_id < ctx_isp->last_applied_req_id);
+ } while (req->request_id <
+ ctx_isp->req_info.last_applied_req_id);
if (ctx->ctx_crm_intf && ctx->ctx_crm_intf->notify_err) {
notify.link_hdl = ctx->link_hdl;
@@ -1240,8 +1298,8 @@
V4L_EVENT_CAM_REQ_MGR_EVENT))
CAM_ERR(CAM_ISP,
"Error in notifying the error time for req id:%lld ctx %u",
- ctx_isp->last_applied_req_id,
- ctx->ctx_id);
+ ctx_isp->req_info.last_applied_req_id,
+ ctx->ctx_id);
}
ctx_isp->substate_activated = CAM_ISP_CTX_ACTIVATED_HW_ERROR;
} else {
@@ -1278,8 +1336,7 @@
ctx_isp->frame_id++;
ctx_isp->sof_timestamp_val = sof_event_data->timestamp;
ctx_isp->boot_timestamp = sof_event_data->boot_time;
- __cam_isp_ctx_update_state_monitor_array(ctx_isp,
- CAM_ISP_STATE_CHANGE_TRIGGER_SOF, req->request_id);
+
CAM_DBG(CAM_ISP, "frame id: %lld time stamp:0x%llx",
ctx_isp->frame_id, ctx_isp->sof_timestamp_val);
@@ -1293,6 +1350,7 @@
notify.dev_hdl = ctx->dev_hdl;
notify.frame_id = ctx_isp->frame_id;
notify.trigger = CAM_TRIGGER_POINT_SOF;
+ notify.sof_timestamp_val = ctx_isp->sof_timestamp_val;
ctx->ctx_crm_intf->notify_trigger(¬ify);
CAM_DBG(CAM_ISP, "Notify CRM SOF frame %lld",
@@ -1300,9 +1358,12 @@
}
list_for_each_entry(req, &ctx->active_req_list, list) {
- if (req->request_id > ctx_isp->reported_req_id) {
+ if (req->request_id >
+ ctx_isp->req_info.reported_req_id) {
request_id = req->request_id;
- ctx_isp->reported_req_id = request_id;
+ ctx_isp->req_info.reported_req_id = request_id;
+ ctx_isp->req_info.last_reported_id_time_stamp =
+ jiffies_to_msecs(jiffies);
break;
}
}
@@ -1343,8 +1404,10 @@
CAM_DBG(CAM_ISP, "No request, move to SOF");
ctx_isp->substate_activated =
CAM_ISP_CTX_ACTIVATED_SOF;
- if (ctx_isp->reported_req_id < curr_req_id) {
- ctx_isp->reported_req_id = curr_req_id;
+ if (ctx_isp->req_info.reported_req_id < curr_req_id) {
+ ctx_isp->req_info.reported_req_id = curr_req_id;
+ ctx_isp->req_info.last_reported_id_time_stamp =
+ jiffies_to_msecs(jiffies);
__cam_isp_ctx_send_sof_timestamp(ctx_isp,
curr_req_id,
CAM_REQ_MGR_SOF_EVENT_SUCCESS);
@@ -1402,11 +1465,7 @@
CAM_ERR(CAM_ISP,
"receive rup in unexpected state");
}
- if (req != NULL) {
- __cam_isp_ctx_update_state_monitor_array(ctx_isp,
- CAM_ISP_STATE_CHANGE_TRIGGER_REG_UPDATE,
- req->request_id);
- }
+
end:
return rc;
}
@@ -1451,9 +1510,12 @@
if (ctx->ctx_crm_intf && ctx->ctx_crm_intf->notify_trigger &&
ctx_isp->active_req_cnt <= 2) {
list_for_each_entry(req, &ctx->active_req_list, list) {
- if (req->request_id > ctx_isp->reported_req_id) {
+ if (req->request_id >
+ ctx_isp->req_info.reported_req_id) {
request_id = req->request_id;
- ctx_isp->reported_req_id = request_id;
+ ctx_isp->req_info.reported_req_id = request_id;
+ ctx_isp->req_info.last_reported_id_time_stamp =
+ jiffies_to_msecs(jiffies);
break;
}
}
@@ -1466,6 +1528,7 @@
notify.dev_hdl = ctx->dev_hdl;
notify.frame_id = ctx_isp->frame_id;
notify.trigger = CAM_TRIGGER_POINT_SOF;
+ notify.sof_timestamp_val = ctx_isp->sof_timestamp_val;
ctx->ctx_crm_intf->notify_trigger(¬ify);
CAM_DBG(CAM_ISP, "Notify CRM SOF frame %lld",
@@ -1478,11 +1541,7 @@
CAM_DBG(CAM_ISP, "next substate %d", ctx_isp->substate_activated);
end:
- if (req != NULL && !rc) {
- __cam_isp_ctx_update_state_monitor_array(ctx_isp,
- CAM_ISP_STATE_CHANGE_TRIGGER_EPOCH,
- req->request_id);
- }
+
return rc;
}
@@ -1729,19 +1788,19 @@
} else {
spin_lock_bh(&ctx->lock);
ctx_isp->substate_activated = next_state;
- ctx_isp->last_applied_req_id = apply->request_id;
+ ctx_isp->req_info.last_applied_req_id =
+ apply->request_id;
+ ctx_isp->req_info.last_applied_time_stamp =
+ jiffies_to_msecs(jiffies);
list_del_init(&req->list);
list_add_tail(&req->list, &ctx->wait_req_list);
CAM_DBG(CAM_ISP, "new substate state %d, applied req %lld",
- next_state, ctx_isp->last_applied_req_id);
+ next_state,
+ ctx_isp->req_info.last_applied_req_id);
spin_unlock_bh(&ctx->lock);
}
end:
- if (ctx_isp != NULL) {
- __cam_isp_ctx_update_state_monitor_array(ctx_isp,
- CAM_ISP_STATE_CHANGE_TRIGGER_SOF,
- ctx->req_list->request_id);
- }
+
return rc;
}
@@ -1875,6 +1934,23 @@
CAM_DBG(CAM_ISP, "try to flush pending list");
spin_lock_bh(&ctx->lock);
rc = __cam_isp_ctx_flush_req(ctx, &ctx->pending_req_list, flush_req);
+
+ if (!list_empty(&ctx->active_req_list)) {
+ CAM_INFO_RATE_LIMIT_CUSTOM(CAM_ISP, 5, 20,
+ "ctx:%d last applied id:%lld, reported req id:%lld, buf done id:%lld",
+ ctx->ctx_id,
+ ctx_isp->req_info.last_applied_req_id,
+ ctx_isp->req_info.reported_req_id,
+ ctx_isp->req_info.last_bufdone_req_id);
+ CAM_INFO_RATE_LIMIT_CUSTOM(CAM_ISP, 5, 20,
+ "current time:%u last apply time:%lld, reported req time:%lld, buf done time:%lld",
+ jiffies_to_msecs(jiffies),
+ ctx_isp->req_info.last_applied_time_stamp,
+ ctx_isp->req_info.last_reported_id_time_stamp,
+ ctx_isp->req_info.last_bufdone_time_stamp);
+
+ __cam_isp_ctx_dump_state_monitor_array(ctx_isp, true);
+ }
spin_unlock_bh(&ctx->lock);
atomic_set(&ctx_isp->process_bubble, 0);
@@ -2041,6 +2117,7 @@
notify.dev_hdl = ctx->dev_hdl;
notify.frame_id = ctx_isp->frame_id;
notify.trigger = CAM_TRIGGER_POINT_SOF;
+ notify.sof_timestamp_val = ctx_isp->sof_timestamp_val;
ctx->ctx_crm_intf->notify_trigger(¬ify);
CAM_DBG(CAM_ISP, "Notify CRM SOF frame %lld",
@@ -2133,8 +2210,10 @@
list);
req_isp = (struct cam_isp_ctx_req *)req->req_priv;
req_isp->bubble_detected = true;
+ CAM_INFO(CAM_ISP, "Ctx:%d Report Bubble flag %d req id:%lld",
+ ctx->ctx_id, req_isp->bubble_report, req->request_id);
+ __cam_isp_ctx_dump_state_monitor_array(ctx_isp, true);
- CAM_DBG(CAM_ISP, "Report Bubble flag %d", req_isp->bubble_report);
if (req_isp->bubble_report && ctx->ctx_crm_intf &&
ctx->ctx_crm_intf->notify_err) {
struct cam_req_mgr_error_notify notify;
@@ -2161,9 +2240,11 @@
req->request_id, ctx_isp->active_req_cnt);
if (!req_isp->bubble_report) {
- if (req->request_id > ctx_isp->reported_req_id) {
+ if (req->request_id > ctx_isp->req_info.reported_req_id) {
request_id = req->request_id;
- ctx_isp->reported_req_id = request_id;
+ ctx_isp->req_info.reported_req_id = request_id;
+ ctx_isp->req_info.last_reported_id_time_stamp =
+ jiffies_to_msecs(jiffies);
__cam_isp_ctx_send_sof_timestamp(ctx_isp, request_id,
CAM_REQ_MGR_SOF_EVENT_ERROR);
} else
@@ -2228,6 +2309,7 @@
notify.dev_hdl = ctx->dev_hdl;
notify.frame_id = ctx_isp->frame_id;
notify.trigger = CAM_TRIGGER_POINT_SOF;
+ notify.sof_timestamp_val = ctx_isp->sof_timestamp_val;
ctx->ctx_crm_intf->notify_trigger(¬ify);
CAM_DBG(CAM_ISP, "Notify CRM SOF frame %lld",
@@ -2298,6 +2380,7 @@
notify.dev_hdl = ctx->dev_hdl;
notify.frame_id = ctx_isp->frame_id;
notify.trigger = CAM_TRIGGER_POINT_SOF;
+ notify.sof_timestamp_val = ctx_isp->sof_timestamp_val;
ctx->ctx_crm_intf->notify_trigger(¬ify);
CAM_DBG(CAM_ISP, "Notify CRM SOF frame %lld",
@@ -2305,8 +2388,11 @@
} else {
CAM_ERR(CAM_ISP, "Can not notify SOF to CRM");
}
- if (request_id)
- ctx_isp->reported_req_id = request_id;
+ if (request_id) {
+ ctx_isp->req_info.reported_req_id = request_id;
+ ctx_isp->req_info.last_reported_id_time_stamp =
+ jiffies_to_msecs(jiffies);
+ }
__cam_isp_ctx_send_sof_timestamp(ctx_isp, request_id,
CAM_REQ_MGR_SOF_EVENT_SUCCESS);
@@ -2481,9 +2567,14 @@
ctx->last_flush_req = 0;
ctx_isp->frame_id = 0;
ctx_isp->active_req_cnt = 0;
- ctx_isp->reported_req_id = 0;
ctx_isp->hw_acquired = false;
ctx_isp->init_received = false;
+ ctx_isp->req_info.reported_req_id = 0;
+ ctx_isp->req_info.last_applied_req_id = 0;
+ ctx_isp->req_info.last_bufdone_req_id = 0;
+ ctx_isp->req_info.last_applied_time_stamp = 0;
+ ctx_isp->req_info.last_bufdone_time_stamp = 0;
+ ctx_isp->req_info.last_reported_id_time_stamp = 0;
/*
* Ideally, we should never have any active request here.
@@ -2538,11 +2629,16 @@
ctx->last_flush_req = 0;
ctx_isp->frame_id = 0;
ctx_isp->active_req_cnt = 0;
- ctx_isp->reported_req_id = 0;
ctx_isp->hw_acquired = false;
ctx_isp->init_received = false;
ctx_isp->rdi_only_context = false;
ctx_isp->split_acquire = false;
+ ctx_isp->req_info.reported_req_id = 0;
+ ctx_isp->req_info.last_applied_req_id = 0;
+ ctx_isp->req_info.last_bufdone_req_id = 0;
+ ctx_isp->req_info.last_applied_time_stamp = 0;
+ ctx_isp->req_info.last_bufdone_time_stamp = 0;
+ ctx_isp->req_info.last_reported_id_time_stamp = 0;
/*
* Ideally, we should never have any active request here.
@@ -3169,7 +3265,7 @@
atomic_set(&ctx_isp->process_bubble, 0);
ctx_isp->frame_id = 0;
ctx_isp->active_req_cnt = 0;
- ctx_isp->reported_req_id = 0;
+ ctx_isp->req_info.reported_req_id = 0;
ctx_isp->substate_activated = ctx_isp->rdi_only_context ?
CAM_ISP_CTX_ACTIVATED_APPLIED :
(req_isp->num_fence_map_out) ? CAM_ISP_CTX_ACTIVATED_EPOCH :
@@ -3301,7 +3397,15 @@
}
ctx_isp->frame_id = 0;
ctx_isp->active_req_cnt = 0;
- ctx_isp->reported_req_id = 0;
+ ctx_isp->req_info.reported_req_id = 0;
+ ctx_isp->req_info.last_applied_req_id = 0;
+ ctx_isp->req_info.last_bufdone_req_id = 0;
+ ctx_isp->req_info.last_applied_time_stamp = 0;
+ ctx_isp->req_info.last_bufdone_time_stamp = 0;
+ ctx_isp->req_info.last_reported_id_time_stamp = 0;
+ ctx_isp->prev_sof_timestamp_val = 0;
+ ctx_isp->prev_boot_timestamp = 0;
+
atomic_set(&ctx_isp->process_bubble, 0);
CAM_DBG(CAM_ISP, "Stop device success next state %d on ctx %u",
@@ -3478,8 +3582,9 @@
rc = ctx_ops->crm_ops.apply_req(ctx, apply);
} else {
CAM_ERR_RATE_LIMIT(CAM_ISP,
- "No handle function in activated substate %d",
- ctx_isp->substate_activated);
+ "Ctx:%d No handle function in activated substate %d",
+ ctx->ctx_id, ctx_isp->substate_activated);
+ __cam_isp_ctx_dump_state_monitor_array(ctx_isp, true);
rc = -EFAULT;
}
@@ -3500,22 +3605,27 @@
struct cam_context *ctx = (struct cam_context *)context;
struct cam_isp_context *ctx_isp =
(struct cam_isp_context *)ctx->ctx_priv;
+ enum cam_isp_ctx_activated_substate curr_state;
spin_lock(&ctx->lock);
trace_cam_isp_activated_irq(ctx, ctx_isp->substate_activated, evt_id,
__cam_isp_ctx_get_event_ts(evt_id, evt_data));
+ curr_state = ctx_isp->substate_activated;
CAM_DBG(CAM_ISP, "Enter: State %d, Substate %d, evt id %d",
ctx->state, ctx_isp->substate_activated, evt_id);
irq_ops = &ctx_isp->substate_machine_irq[ctx_isp->substate_activated];
if (irq_ops->irq_ops[evt_id]) {
rc = irq_ops->irq_ops[evt_id](ctx_isp, evt_data);
} else {
- CAM_DBG(CAM_ISP, "No handle function for substate %d",
- ctx_isp->substate_activated);
- __cam_isp_ctx_dump_state_monitor_array(ctx_isp);
+ CAM_INFO(CAM_ISP, "Ctx:%d No handle function for substate %d",
+ ctx->ctx_id, ctx_isp->substate_activated);
+ __cam_isp_ctx_dump_state_monitor_array(ctx_isp, true);
}
+ if (evt_id != CAM_ISP_HW_EVENT_DONE)
+ __cam_isp_ctx_update_state_monitor_array(ctx_isp, evt_id,
+ curr_state, ctx_isp->substate_activated);
CAM_DBG(CAM_ISP, "Exit: State %d Substate %d",
ctx->state, ctx_isp->substate_activated);
@@ -3677,7 +3787,13 @@
ctx->base = ctx_base;
ctx->frame_id = 0;
ctx->active_req_cnt = 0;
- ctx->reported_req_id = 0;
+ ctx->req_info.reported_req_id = 0;
+ ctx->req_info.last_applied_req_id = 0;
+ ctx->req_info.last_bufdone_req_id = 0;
+ ctx->req_info.last_applied_time_stamp = 0;
+ ctx->req_info.last_bufdone_time_stamp = 0;
+ ctx->req_info.last_reported_id_time_stamp = 0;
+
ctx->hw_ctx = NULL;
ctx->substate_activated = CAM_ISP_CTX_ACTIVATED_SOF;
ctx->substate_machine = cam_isp_ctx_activated_state_machine;
diff --git a/drivers/media/platform/msm/camera_v3/cam_isp/cam_isp_context.h b/drivers/media/platform/msm/camera_v3/cam_isp/cam_isp_context.h
index 4954f20..a4f4e5a 100644
--- a/drivers/media/platform/msm/camera_v3/cam_isp/cam_isp_context.h
+++ b/drivers/media/platform/msm/camera_v3/cam_isp/cam_isp_context.h
@@ -62,20 +62,6 @@
};
/**
- * enum cam_isp_state_change_trigger - Different types of ISP events
- *
- */
-enum cam_isp_state_change_trigger {
- CAM_ISP_STATE_CHANGE_TRIGGER_ERROR,
- CAM_ISP_STATE_CHANGE_TRIGGER_SOF,
- CAM_ISP_STATE_CHANGE_TRIGGER_REG_UPDATE,
- CAM_ISP_STATE_CHANGE_TRIGGER_EPOCH,
- CAM_ISP_STATE_CHANGE_TRIGGER_EOF,
- CAM_ISP_STATE_CHANGE_TRIGGER_DONE,
- CAM_ISP_STATE_CHANGE_TRIGGER_MAX
-};
-
-/**
* struct cam_isp_ctx_irq_ops - Function table for handling IRQ callbacks
*
* @irq_ops: Array of handle function pointers.
@@ -125,20 +111,47 @@
* debug purposes
*
*@curr_state: Current sub state that received req
- *@req_type: Event type of incoming req
- *@req_id: Request id
- *@evt_time_stamp Current time stamp
+ *@next_state: Next sub state that received req
+ *@hw_event: Hw Event type of incoming req
+ *@last_reported_id: Last_reported_id to userspace
+ *@last_applied_req_id Last applied request id to hardware
+ *@frame_id: Current Frame id
+ *@evt_time_stamp Current time stamp of this event logged
*
*/
struct cam_isp_context_state_monitor {
enum cam_isp_ctx_activated_substate curr_state;
- enum cam_isp_state_change_trigger trigger;
- uint32_t req_id;
+ enum cam_isp_ctx_activated_substate next_state;
+ enum cam_isp_hw_event_type hw_event;
+ int64_t last_reported_id;
+ int64_t last_applied_req_id;
int64_t frame_id;
uint64_t evt_time_stamp;
};
/**
+ * struct cam_isp_context_req_id_info - ISP context request id
+ * information for last applied, reported and bufdone.
+ *
+ *@last_applied_req_id: Last applied request id
+ *@last_bufdone_req_id: Last bufdone request id
+ *@reported_req_id: Last reported request id to userspace
+ *@last_applied_time_stamp: Last applied request time stamp information
+ *@last_bufdone_time_stamp Last bufdone request time stamp information
+ *@last_reported_id_time_stamp: Last reported request time stamp information
+ *
+ */
+
+struct cam_isp_context_req_id_info {
+ int64_t last_applied_req_id;
+ int64_t last_bufdone_req_id;
+ int64_t reported_req_id;
+ int64_t last_applied_time_stamp;
+ int64_t last_bufdone_time_stamp;
+ int64_t last_reported_id_time_stamp;
+
+};
+/**
* struct cam_isp_context - ISP context object
*
* @base: Common context object pointer
@@ -152,13 +165,15 @@
* @req_isp: ISP private request object storage
* @hw_ctx: HW object returned by the acquire device command
* @sof_timestamp_val: Captured time stamp value at sof hw event
+ * @prev_sof_timestamp_val Holds last notified sof time stamp
* @boot_timestamp: Boot time stamp for a given req_id
+ * @prev_boot_timestamp Holds last notified boot time stamp
* @active_req_cnt: Counter for the active request
- * @reported_req_id: Last reported request id
* @subscribe_event: The irq event mask that CRM subscribes to, IFE
* will invoke CRM cb at those event.
- * @last_applied_req_id: Last applied request id
* @state_monitor_head: Write index to the state monitoring array
+ * @req_info Request id information about last applied,
+ * reported and buf done
* @cam_isp_ctx_state_monitor: State monitoring array
* @rdi_only_context: Get context type information.
* true, if context is rdi only context
@@ -181,14 +196,15 @@
void *hw_ctx;
uint64_t sof_timestamp_val;
+ uint64_t prev_sof_timestamp_val;
uint64_t boot_timestamp;
+ uint64_t prev_boot_timestamp;
int32_t active_req_cnt;
- int64_t reported_req_id;
uint32_t subscribe_event;
- int64_t last_applied_req_id;
atomic64_t state_monitor_head;
struct cam_isp_context_state_monitor cam_isp_ctx_state_monitor[
CAM_ISP_CTX_STATE_MONITOR_MAX_ENTRIES];
+ struct cam_isp_context_req_id_info req_info;
bool rdi_only_context;
bool hw_acquired;
bool init_received;
diff --git a/drivers/media/platform/msm/camera_v3/cam_isp/isp_hw_mgr/cam_ife_hw_mgr.c b/drivers/media/platform/msm/camera_v3/cam_isp/isp_hw_mgr/cam_ife_hw_mgr.c
index fe3d2f1..cfc902a 100644
--- a/drivers/media/platform/msm/camera_v3/cam_isp/isp_hw_mgr/cam_ife_hw_mgr.c
+++ b/drivers/media/platform/msm/camera_v3/cam_isp/isp_hw_mgr/cam_ife_hw_mgr.c
@@ -2637,7 +2637,7 @@
/* Stop the master CSID path first */
cam_ife_mgr_csid_stop_hw(ctx, &ctx->res_list_ife_csid,
- master_base_idx, CAM_CSID_HALT_AT_FRAME_BOUNDARY);
+ master_base_idx, csid_halt_type);
/* stop rest of the CSID paths */
for (i = 0; i < ctx->num_base; i++) {
@@ -2647,7 +2647,7 @@
ctx->base[i].idx, i, master_base_idx);
cam_ife_mgr_csid_stop_hw(ctx, &ctx->res_list_ife_csid,
- ctx->base[i].idx, CAM_CSID_HALT_AT_FRAME_BOUNDARY);
+ ctx->base[i].idx, csid_halt_type);
}
CAM_DBG(CAM_ISP, "Stopping master CID idx %d", master_base_idx);
@@ -4097,8 +4097,12 @@
for (i = 0; i < packet->num_io_configs; i++) {
for (j = 0; j < CAM_PACKET_MAX_PLANES; j++) {
- if (!io_cfg[i].mem_handle[j])
+ if (!io_cfg[i].mem_handle[j]) {
+ CAM_ERR(CAM_ISP,
+ "Mem Handle %d is NULL for %d io config",
+ j, i);
break;
+ }
if (pf_buf_info &&
GET_FD_FROM_HANDLE(io_cfg[i].mem_handle[j]) ==
@@ -4261,45 +4265,44 @@
struct cam_hw_intf *hw_intf;
struct cam_csid_get_time_stamp_args csid_get_time;
- list_for_each_entry(hw_mgr_res, &ife_ctx->res_list_ife_csid, list) {
- for (i = 0; i < CAM_ISP_HW_SPLIT_MAX; i++) {
- if (!hw_mgr_res->hw_res[i])
- continue;
+ hw_mgr_res = list_first_entry(&ife_ctx->res_list_ife_csid,
+ struct cam_ife_hw_mgr_res, list);
+ for (i = 0; i < CAM_ISP_HW_SPLIT_MAX; i++) {
+ if (!hw_mgr_res->hw_res[i])
+ continue;
+ /*
+ * Get the SOF time stamp from left resource only.
+ * Left resource is master for dual vfe case and
+ * Rdi only context case left resource only hold
+ * the RDI resource
+ */
+
+ hw_intf = hw_mgr_res->hw_res[i]->hw_intf;
+ if (hw_intf->hw_ops.process_cmd) {
/*
- * Get the SOF time stamp from left resource only.
- * Left resource is master for dual vfe case and
- * Rdi only context case left resource only hold
- * the RDI resource
+ * Single VFE case, Get the time stamp from
+ * available one csid hw in the context
+ * Dual VFE case, get the time stamp from
+ * master(left) would be sufficient
*/
- hw_intf = hw_mgr_res->hw_res[i]->hw_intf;
- if (hw_intf->hw_ops.process_cmd) {
- /*
- * Single VFE case, Get the time stamp from
- * available one csid hw in the context
- * Dual VFE case, get the time stamp from
- * master(left) would be sufficient
- */
-
- csid_get_time.node_res =
- hw_mgr_res->hw_res[i];
- rc = hw_intf->hw_ops.process_cmd(
- hw_intf->hw_priv,
- CAM_IFE_CSID_CMD_GET_TIME_STAMP,
- &csid_get_time,
- sizeof(
- struct cam_csid_get_time_stamp_args));
- if (!rc && (i == CAM_ISP_HW_SPLIT_LEFT)) {
- *time_stamp =
- csid_get_time.time_stamp_val;
- *boot_time_stamp =
- csid_get_time.boot_timestamp;
- }
+ csid_get_time.node_res =
+ hw_mgr_res->hw_res[i];
+ rc = hw_intf->hw_ops.process_cmd(
+ hw_intf->hw_priv,
+ CAM_IFE_CSID_CMD_GET_TIME_STAMP,
+ &csid_get_time,
+ sizeof(
+ struct cam_csid_get_time_stamp_args));
+ if (!rc && (i == CAM_ISP_HW_SPLIT_LEFT)) {
+ *time_stamp =
+ csid_get_time.time_stamp_val;
+ *boot_time_stamp =
+ csid_get_time.boot_timestamp;
}
}
}
-
if (rc)
CAM_ERR(CAM_ISP, "Getting sof time stamp failed");
diff --git a/drivers/media/platform/msm/camera_v3/cam_isp/isp_hw_mgr/hw_utils/cam_isp_packet_parser.c b/drivers/media/platform/msm/camera_v3/cam_isp/isp_hw_mgr/hw_utils/cam_isp_packet_parser.c
index 0b3c387..21f67ba 100644
--- a/drivers/media/platform/msm/camera_v3/cam_isp/isp_hw_mgr/hw_utils/cam_isp_packet_parser.c
+++ b/drivers/media/platform/msm/camera_v3/cam_isp/isp_hw_mgr/hw_utils/cam_isp_packet_parser.c
@@ -467,6 +467,7 @@
int32_t hdl;
int mmu_hdl;
bool mode, is_buf_secure;
+ uint64_t req_id;
io_cfg = (struct cam_buf_io_cfg *) ((uint8_t *)
&prepare->packet->payload +
@@ -475,6 +476,7 @@
num_in_buf = 0;
io_cfg_used_bytes = 0;
prepare->pf_data->packet = prepare->packet;
+ req_id = prepare->packet->header.request_id;
/* Max one hw entries required for each base */
if (prepare->num_hw_update_entries + 1 >=
@@ -489,7 +491,7 @@
CAM_DBG(CAM_ISP, "======= io config idx %d ============", i);
CAM_DBG(CAM_REQ,
"i %d req_id %llu resource_type:%d fence:%d direction %d",
- i, prepare->packet->header.request_id,
+ i, req_id,
io_cfg[i].resource_type, io_cfg[i].fence,
io_cfg[i].direction);
CAM_DBG(CAM_ISP, "format: %d", io_cfg[i].format);
@@ -616,12 +618,37 @@
mmu_hdl, &io_addr[plane_id], &size);
if (rc) {
CAM_ERR(CAM_ISP,
- "no io addr for plane%d",
- plane_id);
+ "no io addr for plane%d Bufhdl:%d, Size =%d",
+ plane_id,
+ io_cfg[i].mem_handle[plane_id],
+ (int)size);
+ CAM_ERR(CAM_ISP,
+ "Port i %d Reqid %llu res_type:%d fence:%d dir %d",
+ i, req_id,
+ io_cfg[i].resource_type,
+ io_cfg[i].fence,
+ io_cfg[i].direction);
rc = -ENOMEM;
return rc;
}
+ if (j == 0) {
+ rc = cam_packet_validate_plane_size(
+ &io_cfg[i],
+ plane_id,
+ size);
+ if (rc) {
+ CAM_ERR(CAM_ISP,
+ "Invalid buffer size, port 0x%x plane %d req_id %llu format %d memh 0x%x",
+ io_cfg[i].resource_type,
+ plane_id,
+ req_id,
+ io_cfg[i].format,
+ io_cfg[i].mem_handle[plane_id]);
+ return -EINVAL;
+ }
+ }
+
/* need to update with offset */
io_addr[plane_id] +=
io_cfg[i].offsets[plane_id];
diff --git a/drivers/media/platform/msm/camera_v3/cam_isp/isp_hw_mgr/isp_hw/ife_csid_hw/cam_ife_csid_core.c b/drivers/media/platform/msm/camera_v3/cam_isp/isp_hw_mgr/isp_hw/ife_csid_hw/cam_ife_csid_core.c
index 5610f6d..3431a64 100644
--- a/drivers/media/platform/msm/camera_v3/cam_isp/isp_hw_mgr/isp_hw/ife_csid_hw/cam_ife_csid_core.c
+++ b/drivers/media/platform/msm/camera_v3/cam_isp/isp_hw_mgr/isp_hw/ife_csid_hw/cam_ife_csid_core.c
@@ -467,6 +467,7 @@
CAM_ERR(CAM_ISP, "CSID:%d IRQ value after reset rc = %d",
csid_hw->hw_intf->hw_idx, val);
csid_hw->error_irq_count = 0;
+ csid_hw->first_sof_ts = 0;
for (i = 0 ; i < CAM_IFE_PIX_PATH_RES_MAX; i++)
csid_hw->res_sof_cnt[i] = 0;
@@ -1167,6 +1168,7 @@
csid_hw->hw_info->hw_state = CAM_HW_STATE_POWER_DOWN;
csid_hw->error_irq_count = 0;
+ csid_hw->first_sof_ts = 0;
return rc;
}
@@ -2454,9 +2456,16 @@
CAM_IFE_CSID_QTIMER_MUL_FACTOR,
CAM_IFE_CSID_QTIMER_DIV_FACTOR);
- get_monotonic_boottime64(&ts);
- time_stamp->boot_timestamp = (uint64_t)((ts.tv_sec * 1000000000) +
- ts.tv_nsec);
+ if (!csid_hw->first_sof_ts) {
+ get_monotonic_boottime64(&ts);
+ time_stamp->boot_timestamp =
+ (uint64_t)((ts.tv_sec * 1000000000) +
+ ts.tv_nsec);
+ CAM_DBG(CAM_ISP, "timestamp:%lld",
+ time_stamp->boot_timestamp);
+ csid_hw->first_sof_ts = 1;
+ } else
+ time_stamp->boot_timestamp = 0;
return 0;
}
@@ -3737,6 +3746,7 @@
ife_csid_hw->csid_debug = 0;
ife_csid_hw->error_irq_count = 0;
+ ife_csid_hw->first_sof_ts = 0;
return 0;
err:
diff --git a/drivers/media/platform/msm/camera_v3/cam_isp/isp_hw_mgr/isp_hw/ife_csid_hw/cam_ife_csid_core.h b/drivers/media/platform/msm/camera_v3/cam_isp/isp_hw_mgr/isp_hw/ife_csid_hw/cam_ife_csid_core.h
index 600deb2..9b4d5c3 100644
--- a/drivers/media/platform/msm/camera_v3/cam_isp/isp_hw_mgr/isp_hw/ife_csid_hw/cam_ife_csid_core.h
+++ b/drivers/media/platform/msm/camera_v3/cam_isp/isp_hw_mgr/isp_hw/ife_csid_hw/cam_ife_csid_core.h
@@ -480,6 +480,7 @@
* @init_frame_drop Initial frame drop number
* @res_sof_cnt path resource sof count value. it used for initial
* frame drop
+ * @first_sof_ts flag to mark the first sof has been registered
*
*/
struct cam_ife_csid_hw {
@@ -511,6 +512,7 @@
uint32_t dual_usage;
uint32_t init_frame_drop;
uint32_t res_sof_cnt[CAM_IFE_PIX_PATH_RES_MAX];
+ uint32_t first_sof_ts;
};
int cam_ife_csid_hw_probe_init(struct cam_hw_intf *csid_hw_intf,
diff --git a/drivers/media/platform/msm/camera_v3/cam_jpeg/jpeg_hw/cam_jpeg_hw_mgr.c b/drivers/media/platform/msm/camera_v3/cam_jpeg/jpeg_hw/cam_jpeg_hw_mgr.c
index f7b42d5..1a3a7cb 100644
--- a/drivers/media/platform/msm/camera_v3/cam_jpeg/jpeg_hw/cam_jpeg_hw_mgr.c
+++ b/drivers/media/platform/msm/camera_v3/cam_jpeg/jpeg_hw/cam_jpeg_hw_mgr.c
@@ -634,8 +634,13 @@
for (i = 0; i < packet->num_io_configs; i++) {
for (j = 0; j < CAM_PACKET_MAX_PLANES; j++) {
- if (!io_cfg[i].mem_handle[j])
+ if (!io_cfg[i].mem_handle[j]) {
+ CAM_ERR(CAM_JPEG,
+ "Mem Handle %d is NULL for %d io config",
+ j, i);
break;
+ }
+
if (GET_FD_FROM_HANDLE(io_cfg[i].mem_handle[j]) ==
GET_FD_FROM_HANDLE(pf_buf_info)) {
diff --git a/drivers/media/platform/msm/camera_v3/cam_jpeg/jpeg_hw/jpeg_enc_hw/jpeg_enc_core.c b/drivers/media/platform/msm/camera_v3/cam_jpeg/jpeg_hw/jpeg_enc_hw/jpeg_enc_core.c
index 52907cd..225f859 100644
--- a/drivers/media/platform/msm/camera_v3/cam_jpeg/jpeg_hw/jpeg_enc_hw/jpeg_enc_core.c
+++ b/drivers/media/platform/msm/camera_v3/cam_jpeg/jpeg_hw/jpeg_enc_hw/jpeg_enc_core.c
@@ -91,6 +91,9 @@
CAM_ERR(CAM_JPEG, "soc enable is failed %d", rc);
goto soc_failed;
}
+ spin_lock(&jpeg_enc_dev->hw_lock);
+ jpeg_enc_dev->hw_state = CAM_HW_STATE_POWER_UP;
+ spin_unlock(&jpeg_enc_dev->hw_lock);
mutex_unlock(&core_info->core_mutex);
@@ -140,6 +143,9 @@
return -EFAULT;
}
+ spin_lock(&jpeg_enc_dev->hw_lock);
+ jpeg_enc_dev->hw_state = CAM_HW_STATE_POWER_DOWN;
+ spin_unlock(&jpeg_enc_dev->hw_lock);
rc = cam_jpeg_enc_disable_soc_resources(soc_info);
if (rc)
CAM_ERR(CAM_JPEG, "soc disable failed %d", rc);
@@ -173,12 +179,19 @@
hw_info = core_info->jpeg_enc_hw_info;
mem_base = soc_info->reg_map[0].mem_base;
+ spin_lock(&jpeg_enc_dev->hw_lock);
+ if (jpeg_enc_dev->hw_state == CAM_HW_STATE_POWER_DOWN) {
+ CAM_ERR(CAM_JPEG, "JPEG HW is in off state");
+ spin_unlock(&jpeg_enc_dev->hw_lock);
+ return -EINVAL;
+ }
irq_status = cam_io_r_mb(mem_base +
core_info->jpeg_enc_hw_info->reg_offset.int_status);
cam_io_w_mb(irq_status,
soc_info->reg_map[0].mem_base +
core_info->jpeg_enc_hw_info->reg_offset.int_clr);
+ spin_unlock(&jpeg_enc_dev->hw_lock);
CAM_DBG(CAM_JPEG, "irq_num %d irq_status = %x , core_state %d",
irq_num, irq_status, core_info->core_state);
@@ -268,6 +281,12 @@
mutex_lock(&core_info->core_mutex);
spin_lock(&jpeg_enc_dev->hw_lock);
+ if (jpeg_enc_dev->hw_state == CAM_HW_STATE_POWER_DOWN) {
+ CAM_ERR(CAM_JPEG, "JPEG HW is in off state");
+ spin_unlock(&jpeg_enc_dev->hw_lock);
+ mutex_unlock(&core_info->core_mutex);
+ return -EINVAL;
+ }
if (core_info->core_state == CAM_JPEG_ENC_CORE_RESETTING) {
CAM_ERR(CAM_JPEG, "alrady resetting");
spin_unlock(&jpeg_enc_dev->hw_lock);
@@ -319,10 +338,18 @@
hw_info = core_info->jpeg_enc_hw_info;
mem_base = soc_info->reg_map[0].mem_base;
- if (core_info->core_state != CAM_JPEG_ENC_CORE_READY) {
- CAM_ERR(CAM_JPEG, "Error not ready");
+ spin_lock(&jpeg_enc_dev->hw_lock);
+ if (jpeg_enc_dev->hw_state == CAM_HW_STATE_POWER_DOWN) {
+ CAM_ERR(CAM_JPEG, "JPEG HW is in off state");
+ spin_unlock(&jpeg_enc_dev->hw_lock);
return -EINVAL;
}
+ if (core_info->core_state != CAM_JPEG_ENC_CORE_READY) {
+ CAM_ERR(CAM_JPEG, "Error not ready");
+ spin_unlock(&jpeg_enc_dev->hw_lock);
+ return -EINVAL;
+ }
+ spin_unlock(&jpeg_enc_dev->hw_lock);
cam_io_w_mb(hw_info->reg_val.hw_cmd_start,
mem_base + hw_info->reg_offset.hw_cmd);
@@ -352,6 +379,12 @@
mutex_lock(&core_info->core_mutex);
spin_lock(&jpeg_enc_dev->hw_lock);
+ if (jpeg_enc_dev->hw_state == CAM_HW_STATE_POWER_DOWN) {
+ CAM_ERR(CAM_JPEG, "JPEG HW is in off state");
+ spin_unlock(&jpeg_enc_dev->hw_lock);
+ mutex_unlock(&core_info->core_mutex);
+ return -EINVAL;
+ }
if (core_info->core_state == CAM_JPEG_ENC_CORE_ABORTING) {
CAM_ERR(CAM_JPEG, "alrady stopping");
spin_unlock(&jpeg_enc_dev->hw_lock);
diff --git a/drivers/media/platform/msm/camera_v3/cam_req_mgr/cam_req_mgr_core.c b/drivers/media/platform/msm/camera_v3/cam_req_mgr/cam_req_mgr_core.c
index c8e2a02..b2994d5 100644
--- a/drivers/media/platform/msm/camera_v3/cam_req_mgr/cam_req_mgr_core.c
+++ b/drivers/media/platform/msm/camera_v3/cam_req_mgr/cam_req_mgr_core.c
@@ -48,6 +48,9 @@
link->last_flush_id = 0;
link->initial_sync_req = -1;
link->in_msync_mode = false;
+ link->initial_skip = true;
+ link->sof_timestamp = 0;
+ link->prev_sof_timestamp = 0;
}
void cam_req_mgr_handle_core_shutdown(void)
@@ -610,6 +613,19 @@
traverse_data.open_req_cnt = link->open_req_cnt;
/*
+ * Some no-sync mode requests are processed after link config,
+ * then process the sync mode requests after no-sync mode requests
+ * are handled, the initial_skip should be false when processing
+ * the sync mode requests.
+ */
+ if (link->initial_skip) {
+ CAM_DBG(CAM_CRM,
+ "Set initial_skip to false for link %x",
+ link->link_hdl);
+ link->initial_skip = false;
+ }
+
+ /*
* Traverse through all pd tables, if result is success,
* apply the settings
*/
@@ -894,9 +910,11 @@
struct cam_req_mgr_slot *slot)
{
struct cam_req_mgr_core_link *sync_link = NULL;
- int64_t req_id = 0;
+ struct cam_req_mgr_slot *sync_rd_slot = NULL;
+ int64_t req_id = 0, sync_req_id = 0;
int sync_slot_idx = 0, sync_rd_idx = 0, rc = 0;
int32_t sync_num_slots = 0;
+ uint64_t sync_frame_duration = 0;
bool ready = true, sync_ready = true;
if (!link->sync_link) {
@@ -907,11 +925,65 @@
sync_link = link->sync_link;
req_id = slot->req_id;
sync_num_slots = sync_link->req.in_q->num_slots;
+ sync_rd_idx = sync_link->req.in_q->rd_idx;
+ sync_rd_slot = &sync_link->req.in_q->slot[sync_rd_idx];
+ sync_req_id = sync_rd_slot->req_id;
CAM_DBG(CAM_REQ,
"link_hdl %x req %lld frame_skip_flag %d ",
link->link_hdl, req_id, link->sync_link_sof_skip);
+ if (sync_link->initial_skip) {
+ link->initial_skip = false;
+ __cam_req_mgr_inject_delay(link->req.l_tbl, slot->idx);
+ CAM_DBG(CAM_CRM,
+ "sync link %x not streamed on",
+ sync_link->link_hdl);
+ return -EAGAIN;
+ }
+
+ if (sync_link->prev_sof_timestamp)
+ sync_frame_duration = sync_link->sof_timestamp
+ - sync_link->prev_sof_timestamp;
+ else
+ sync_frame_duration = DEFAULT_FRAME_DURATION;
+
+ CAM_DBG(CAM_CRM,
+ "sync link %x last frame duration is %d ns",
+ sync_link->link_hdl, sync_frame_duration);
+
+ if (link->initial_skip) {
+ link->initial_skip = false;
+
+ if (link->sof_timestamp > sync_link->sof_timestamp &&
+ sync_link->sof_timestamp > 0 &&
+ link->sof_timestamp - sync_link->sof_timestamp <
+ sync_frame_duration / 2) {
+ /*
+ * If this frame sync with the previous frame of sync
+ * link, then we need to skip this frame, since the
+ * previous frame of sync link is also skipped.
+ */
+ __cam_req_mgr_inject_delay(link->req.l_tbl, slot->idx);
+ CAM_DBG(CAM_CRM,
+ "This frame sync with previous sync_link %x frame",
+ sync_link->link_hdl);
+ return -EAGAIN;
+ } else if (link->sof_timestamp <= sync_link->sof_timestamp) {
+ /*
+ * Sometimes, link receives the SOF event is eariler
+ * than sync link in IFE CSID side, but link's SOF
+ * event is processed later than sync link's, then
+ * we need to skip this SOF event since the sync
+ * link's SOF event is also skipped.
+ */
+ __cam_req_mgr_inject_delay(link->req.l_tbl, slot->idx);
+ CAM_DBG(CAM_CRM,
+ "The previous frame of sync link is skipped");
+ return -EAGAIN;
+ }
+ }
+
if (sync_link->sync_link_sof_skip) {
CAM_DBG(CAM_REQ,
"No req applied on corresponding SOF on sync link: %x",
@@ -937,12 +1009,11 @@
sync_ready = false;
}
- sync_rd_idx = sync_link->req.in_q->rd_idx;
if ((sync_link->req.in_q->slot[sync_slot_idx].status !=
CRM_SLOT_STATUS_REQ_APPLIED) &&
(((sync_slot_idx - sync_rd_idx + sync_num_slots) %
sync_num_slots) >= 1) &&
- (sync_link->req.in_q->slot[sync_rd_idx].status !=
+ (sync_rd_slot->status !=
CRM_SLOT_STATUS_REQ_APPLIED)) {
CAM_DBG(CAM_CRM,
"Req: %lld [other link] not next req to be applied on link: %x",
@@ -975,7 +1046,15 @@
CAM_DBG(CAM_CRM,
"Req: %lld ready %d sync_ready %d, ignore sync link next SOF",
req_id, ready, sync_ready);
- link->sync_link_sof_skip = true;
+
+ /*
+ * Only skip the frames if current frame sync with
+ * next frame of sync link.
+ */
+ if (link->sof_timestamp - sync_link->sof_timestamp >
+ sync_frame_duration / 2)
+ link->sync_link_sof_skip = true;
+
return -EINVAL;
} else if (ready == false) {
CAM_DBG(CAM_CRM,
@@ -984,6 +1063,61 @@
return -EINVAL;
}
+ /*
+ * Do the self-correction when the frames are sync,
+ * we consider that the frames are synced if the
+ * difference of two SOF timestamp less than
+ * (sync_frame_duration / 5).
+ */
+ if ((link->sof_timestamp > sync_link->sof_timestamp) &&
+ (sync_link->sof_timestamp > 0) &&
+ (link->sof_timestamp - sync_link->sof_timestamp <
+ sync_frame_duration / 5) &&
+ (sync_rd_slot->sync_mode == CAM_REQ_MGR_SYNC_MODE_SYNC)) {
+
+ /*
+ * This means current frame should sync with next
+ * frame of sync link, then the request id of in
+ * rd slot of two links should be same.
+ */
+ CAM_DBG(CAM_CRM,
+ "link %x req_id %lld, sync_link %x req_id %lld",
+ link->link_hdl, req_id,
+ sync_link->link_hdl, sync_req_id);
+
+ if (req_id > sync_req_id) {
+ CAM_DBG(CAM_CRM,
+ "link %x too quickly, skip this frame",
+ link->link_hdl);
+ return -EAGAIN;
+ } else if (req_id < sync_req_id) {
+ CAM_DBG(CAM_CRM,
+ "sync link %x too quickly, skip next frame of sync link",
+ sync_link->link_hdl);
+ link->sync_link_sof_skip = true;
+ }
+ } else if ((sync_link->sof_timestamp > 0) &&
+ (link->sof_timestamp < sync_link->sof_timestamp) &&
+ (sync_link->sof_timestamp - link->sof_timestamp <
+ sync_frame_duration / 5) &&
+ (sync_rd_slot->sync_mode == CAM_REQ_MGR_SYNC_MODE_SYNC)) {
+
+ /*
+ * There is a timing issue once enter this condition,
+ * it means link receives the SOF event earlier than
+ * sync link in IFE CSID side, but the process in CRM
+ * is sync_link earlier than link, then previous SOF
+ * event of sync link is skipped, so we also need to
+ * skip this SOF event.
+ */
+ if (req_id >= sync_req_id) {
+ CAM_DBG(CAM_CRM,
+ "Timing issue, the sof event of link %x is delayed",
+ link->link_hdl);
+ return -EAGAIN;
+ }
+ }
+
CAM_DBG(CAM_REQ,
"Req: %lld ready to apply on link: %x [validation successful]",
req_id, link->link_hdl);
@@ -1011,10 +1145,11 @@
*
*/
static int __cam_req_mgr_process_req(struct cam_req_mgr_core_link *link,
- uint32_t trigger)
+ struct cam_req_mgr_trigger_notify *trigger_data)
{
int rc = 0, idx, last_app_idx;
int reset_step = 0;
+ uint32_t trigger = trigger_data->trigger;
struct cam_req_mgr_slot *slot = NULL;
struct cam_req_mgr_req_queue *in_q;
struct cam_req_mgr_core_session *session;
@@ -1052,6 +1187,13 @@
}
if (trigger == CAM_TRIGGER_POINT_SOF) {
+ /*
+ * Update the timestamp in session lock protection
+ * to avoid timing issue.
+ */
+ link->prev_sof_timestamp = link->sof_timestamp;
+ link->sof_timestamp = trigger_data->sof_timestamp_val;
+
if (link->trigger_mask) {
CAM_ERR_RATE_LIMIT(CAM_CRM,
"Applying for last EOF fails");
@@ -2141,7 +2283,7 @@
__cam_req_mgr_inc_idx(&in_q->rd_idx, 1, in_q->num_slots);
}
- rc = __cam_req_mgr_process_req(link, trigger_data->trigger);
+ rc = __cam_req_mgr_process_req(link, trigger_data);
release_lock:
mutex_unlock(&link->req.lock);
@@ -2372,6 +2514,7 @@
notify_trigger->link_hdl = trigger_data->link_hdl;
notify_trigger->dev_hdl = trigger_data->dev_hdl;
notify_trigger->trigger = trigger_data->trigger;
+ notify_trigger->sof_timestamp_val = trigger_data->sof_timestamp_val;
task->process_cb = &cam_req_mgr_process_trigger;
rc = cam_req_mgr_workq_enqueue_task(task, link, CRM_TASK_PRIORITY_0);
@@ -3125,8 +3268,6 @@
link1->is_master = false;
link2->is_master = false;
- link1->initial_skip = false;
- link2->initial_skip = false;
link1->in_msync_mode = false;
link2->in_msync_mode = false;
@@ -3137,6 +3278,16 @@
link1->sync_link = link2;
link2->sync_link = link1;
__cam_req_mgr_set_master_link(link1, link2);
+ } else {
+ /*
+ * Reset below info after the mode is configured
+ * to NO-SYNC mode since they may be overridden
+ * if the sync config is invoked after SOF comes.
+ */
+ link1->initial_skip = true;
+ link2->initial_skip = true;
+ link1->sof_timestamp = 0;
+ link2->sof_timestamp = 0;
}
cam_session->sync_mode = sync_info->sync_mode;
diff --git a/drivers/media/platform/msm/camera_v3/cam_req_mgr/cam_req_mgr_core.h b/drivers/media/platform/msm/camera_v3/cam_req_mgr/cam_req_mgr_core.h
index 05fe086..94f26de 100644
--- a/drivers/media/platform/msm/camera_v3/cam_req_mgr/cam_req_mgr_core.h
+++ b/drivers/media/platform/msm/camera_v3/cam_req_mgr/cam_req_mgr_core.h
@@ -32,6 +32,9 @@
#define MAX_SYNC_COUNT 65535
+/* Default frame rate is 30 */
+#define DEFAULT_FRAME_DURATION 33333333
+
#define SYNC_LINK_SOF_CNT_MAX_LMT 1
#define MAXIMUM_LINKS_PER_SESSION 4
@@ -321,7 +324,10 @@
* master-slave sync
* @in_msync_mode : Flag to determine if a link is in master-slave mode
* @initial_sync_req : The initial req which is required to sync with the
- * other link
+ * other link, it means current hasn't receive any
+ * stream after streamon if it is true
+ * @sof_timestamp_value : SOF timestamp value
+ * @prev_sof_timestamp : Previous SOF timestamp value
*
*/
struct cam_req_mgr_core_link {
@@ -349,6 +355,8 @@
bool initial_skip;
bool in_msync_mode;
int64_t initial_sync_req;
+ uint64_t sof_timestamp;
+ uint64_t prev_sof_timestamp;
};
/**
diff --git a/drivers/media/platform/msm/camera_v3/cam_req_mgr/cam_req_mgr_interface.h b/drivers/media/platform/msm/camera_v3/cam_req_mgr/cam_req_mgr_interface.h
index 1d1df45..934bc76 100644
--- a/drivers/media/platform/msm/camera_v3/cam_req_mgr/cam_req_mgr_interface.h
+++ b/drivers/media/platform/msm/camera_v3/cam_req_mgr/cam_req_mgr_interface.h
@@ -201,12 +201,14 @@
* @frame_id : frame id for internal tracking
* @trigger : trigger point of this notification, CRM will send apply
* only to the devices which subscribe to this point.
+ * @sof_timestamp_val: Captured time stamp value at sof hw event
*/
struct cam_req_mgr_trigger_notify {
int32_t link_hdl;
int32_t dev_hdl;
int64_t frame_id;
uint32_t trigger;
+ uint64_t sof_timestamp_val;
};
/**
diff --git a/drivers/media/platform/msm/camera_v3/cam_sensor_module/cam_cci/cam_cci_soc.c b/drivers/media/platform/msm/camera_v3/cam_sensor_module/cam_cci/cam_cci_soc.c
index 0181a4d..f66d86c 100644
--- a/drivers/media/platform/msm/camera_v3/cam_sensor_module/cam_cci/cam_cci_soc.c
+++ b/drivers/media/platform/msm/camera_v3/cam_sensor_module/cam_cci/cam_cci_soc.c
@@ -410,7 +410,9 @@
cci_dev->cci_state = CCI_STATE_DISABLED;
cci_dev->cycles_per_us = 0;
- cam_cpas_stop(cci_dev->cpas_handle);
+ rc = cam_cpas_stop(cci_dev->cpas_handle);
+ if (rc)
+ CAM_ERR(CAM_CCI, "cpas stop failed %d", rc);
return rc;
}
diff --git a/drivers/media/platform/msm/camera_v3/cam_sensor_module/cam_csiphy/cam_csiphy_core.c b/drivers/media/platform/msm/camera_v3/cam_sensor_module/cam_csiphy/cam_csiphy_core.c
index 8074ecd..b860559 100644
--- a/drivers/media/platform/msm/camera_v3/cam_sensor_module/cam_csiphy/cam_csiphy_core.c
+++ b/drivers/media/platform/msm/camera_v3/cam_sensor_module/cam_csiphy/cam_csiphy_core.c
@@ -551,7 +551,7 @@
void cam_csiphy_shutdown(struct csiphy_device *csiphy_dev)
{
struct cam_hw_soc_info *soc_info;
- int32_t i = 0;
+ int32_t i = 0, rc = 0;
if (csiphy_dev->csiphy_state == CAM_CSIPHY_INIT)
return;
@@ -574,7 +574,10 @@
cam_csiphy_reset(csiphy_dev);
cam_soc_util_disable_platform_resource(soc_info, true, true);
- cam_cpas_stop(csiphy_dev->cpas_handle);
+ rc = cam_cpas_stop(csiphy_dev->cpas_handle);
+ if (rc)
+ CAM_ERR(CAM_CSIPHY, "cpas stop failed %d", rc);
+
csiphy_dev->csiphy_state = CAM_CSIPHY_ACQUIRE;
}
@@ -934,7 +937,10 @@
if (rc < 0) {
csiphy_dev->csiphy_info.secure_mode[offset] =
CAM_SECURE_MODE_NON_SECURE;
- cam_cpas_stop(csiphy_dev->cpas_handle);
+ rc = cam_cpas_stop(csiphy_dev->cpas_handle);
+ if (rc < 0)
+ CAM_ERR(CAM_CSIPHY,
+ "de-voting CPAS: %d", rc);
goto release_mutex;
}
}
@@ -942,7 +948,9 @@
rc = cam_csiphy_enable_hw(csiphy_dev);
if (rc != 0) {
CAM_ERR(CAM_CSIPHY, "cam_csiphy_enable_hw failed");
- cam_cpas_stop(csiphy_dev->cpas_handle);
+ rc = cam_cpas_stop(csiphy_dev->cpas_handle);
+ if (rc < 0)
+ CAM_ERR(CAM_CSIPHY, "de-voting CPAS: %d", rc);
goto release_mutex;
}
rc = cam_csiphy_config_dev(csiphy_dev);
@@ -952,7 +960,9 @@
if (rc < 0) {
CAM_ERR(CAM_CSIPHY, "cam_csiphy_config_dev failed");
cam_csiphy_disable_hw(csiphy_dev);
- cam_cpas_stop(csiphy_dev->cpas_handle);
+ rc = cam_cpas_stop(csiphy_dev->cpas_handle);
+ if (rc < 0)
+ CAM_ERR(CAM_CSIPHY, "de-voting CPAS: %d", rc);
goto release_mutex;
}
csiphy_dev->start_dev_count++;
diff --git a/drivers/media/platform/msm/camera_v3/cam_sensor_module/cam_eeprom/cam_eeprom_core.c b/drivers/media/platform/msm/camera_v3/cam_sensor_module/cam_eeprom/cam_eeprom_core.c
index 06d6bd4..bcd3823 100644
--- a/drivers/media/platform/msm/camera_v3/cam_sensor_module/cam_eeprom/cam_eeprom_core.c
+++ b/drivers/media/platform/msm/camera_v3/cam_sensor_module/cam_eeprom/cam_eeprom_core.c
@@ -1016,7 +1016,7 @@
&eeprom_cap,
sizeof(struct cam_eeprom_query_cap_t))) {
CAM_ERR(CAM_EEPROM, "Failed Copy to User");
- return -EFAULT;
+ rc = -EFAULT;
goto release_mutex;
}
CAM_DBG(CAM_EEPROM, "eeprom_cap: ID: %d", eeprom_cap.slot_info);
diff --git a/drivers/media/platform/msm/camera_v3/cam_sensor_module/cam_sensor/cam_sensor_core.c b/drivers/media/platform/msm/camera_v3/cam_sensor_module/cam_sensor/cam_sensor_core.c
index a2a738d..94bc611 100644
--- a/drivers/media/platform/msm/camera_v3/cam_sensor_module/cam_sensor/cam_sensor_core.c
+++ b/drivers/media/platform/msm/camera_v3/cam_sensor_module/cam_sensor/cam_sensor_core.c
@@ -927,6 +927,16 @@
}
break;
case CAM_CONFIG_DEV: {
+ if (s_ctrl->sensor_state < CAM_SENSOR_ACQUIRE) {
+ rc = -EINVAL;
+ CAM_ERR(CAM_SENSOR,
+ "sensor_id:[0x%x] not acquired to configure [%d] ",
+ s_ctrl->sensordata->slave_info.sensor_id,
+ s_ctrl->sensor_state
+ );
+ goto release_mutex;
+ }
+
rc = cam_sensor_i2c_pkt_parse(s_ctrl, arg);
if (rc < 0) {
CAM_ERR(CAM_SENSOR, "Failed i2c pkt parse: %d", rc);
diff --git a/drivers/media/platform/msm/camera_v3/cam_smmu/cam_smmu_api.c b/drivers/media/platform/msm/camera_v3/cam_smmu/cam_smmu_api.c
index 626473f..77a7204 100644
--- a/drivers/media/platform/msm/camera_v3/cam_smmu/cam_smmu_api.c
+++ b/drivers/media/platform/msm/camera_v3/cam_smmu/cam_smmu_api.c
@@ -191,7 +191,7 @@
static struct dentry *smmu_dentry;
-static bool smmu_fatal_flag;
+static bool smmu_fatal_flag = true;
static enum dma_data_direction cam_smmu_translate_dir(
enum cam_smmu_map_dir dir);
diff --git a/drivers/media/platform/msm/camera_v3/cam_sync/cam_sync.c b/drivers/media/platform/msm/camera_v3/cam_sync/cam_sync.c
index d4487ef..93d39e3 100644
--- a/drivers/media/platform/msm/camera_v3/cam_sync/cam_sync.c
+++ b/drivers/media/platform/msm/camera_v3/cam_sync/cam_sync.c
@@ -288,6 +288,7 @@
int rc;
long idx = 0;
bool bit;
+ int i = 0;
if (!sync_obj || !merged_obj) {
CAM_ERR(CAM_SYNC, "Invalid pointer(s)");
@@ -305,6 +306,14 @@
return -EINVAL;
}
+ for (i = 0; i < num_objs; i++) {
+ rc = cam_sync_check_valid(sync_obj[i]);
+ if (rc) {
+ CAM_ERR(CAM_SYNC, "Sync_obj[%d] %d valid check fail",
+ i, sync_obj[i]);
+ return rc;
+ }
+ }
do {
idx = find_first_zero_bit(sync_dev->bitmap, CAM_SYNC_MAX_OBJS);
if (idx >= CAM_SYNC_MAX_OBJS)
@@ -376,6 +385,29 @@
return cam_sync_deinit_object(sync_dev->sync_table, sync_obj);
}
+int cam_sync_check_valid(int32_t sync_obj)
+{
+ struct sync_table_row *row = NULL;
+
+ if (sync_obj >= CAM_SYNC_MAX_OBJS || sync_obj <= 0)
+ return -EINVAL;
+
+ row = sync_dev->sync_table + sync_obj;
+
+ if (!test_bit(sync_obj, sync_dev->bitmap)) {
+ CAM_ERR(CAM_SYNC, "Error: Released sync obj received %d",
+ sync_obj);
+ return -EINVAL;
+ }
+
+ if (row->state == CAM_SYNC_STATE_INVALID) {
+ CAM_ERR(CAM_SYNC,
+ "Error: accessing an uninitialized sync obj = %d",
+ sync_obj);
+ return -EINVAL;
+ }
+ return 0;
+}
int cam_sync_wait(int32_t sync_obj, uint64_t timeout_ms)
{
unsigned long timeleft;
diff --git a/drivers/media/platform/msm/camera_v3/cam_sync/cam_sync_api.h b/drivers/media/platform/msm/camera_v3/cam_sync/cam_sync_api.h
index c735d51..f2f67cb 100644
--- a/drivers/media/platform/msm/camera_v3/cam_sync/cam_sync_api.h
+++ b/drivers/media/platform/msm/camera_v3/cam_sync/cam_sync_api.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2017-2018, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2017-2019, 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
@@ -147,5 +147,14 @@
*/
int cam_sync_wait(int32_t sync_obj, uint64_t timeout_ms);
+/**
+ * @brief: Check if sync object is valid
+ *
+ * @param sync_obj: int referencing the sync object to be checked
+ *
+ * @return 0 upon success, -EINVAL if sync object is in bad state or arguments
+ * are invalid
+ */
+int cam_sync_check_valid(int32_t sync_obj);
#endif /* __CAM_SYNC_API_H__ */
diff --git a/drivers/media/platform/msm/camera_v3/cam_utils/cam_packet_util.c b/drivers/media/platform/msm/camera_v3/cam_utils/cam_packet_util.c
index 0f910c9..185e060 100644
--- a/drivers/media/platform/msm/camera_v3/cam_utils/cam_packet_util.c
+++ b/drivers/media/platform/msm/camera_v3/cam_utils/cam_packet_util.c
@@ -182,11 +182,15 @@
int i;
int rc = 0;
int32_t hdl;
+ uint64_t requestId;
+ uint32_t num_patches;
/* process patch descriptor */
patch_desc = (struct cam_patch_desc *)
((uint32_t *) &packet->payload +
packet->patch_offset/4);
+ requestId = packet->header.request_id;
+ num_patches = packet->num_patches;
CAM_DBG(CAM_UTIL, "packet = %pK patch_desc = %pK size = %lu",
(void *)packet, (void *)patch_desc,
sizeof(struct cam_patch_desc));
@@ -197,7 +201,16 @@
rc = cam_mem_get_io_buf(patch_desc[i].src_buf_hdl,
hdl, &iova_addr, &src_buf_size);
if (rc < 0) {
- CAM_ERR(CAM_UTIL, "unable to get src buf address");
+ CAM_ERR(CAM_UTIL,
+ "unable to get src buf address ReqId: %llu, num_patches = %d",
+ requestId, num_patches);
+ CAM_ERR(CAM_UTIL,
+ "i = %d patch info = %x %x %x %x src_bfsz:0x%x",
+ i, patch_desc[i].dst_buf_hdl,
+ patch_desc[i].dst_offset,
+ patch_desc[i].src_buf_hdl,
+ patch_desc[i].src_offset,
+ (uint32_t)src_buf_size);
return rc;
}
src_buf_iova_addr = (uint32_t *)iova_addr;
@@ -206,18 +219,37 @@
rc = cam_mem_get_cpu_buf(patch_desc[i].dst_buf_hdl,
&cpu_addr, &dst_buf_len);
if (rc < 0 || !cpu_addr || (dst_buf_len == 0)) {
- CAM_ERR(CAM_UTIL, "unable to get dst buf address");
+ CAM_ERR(CAM_UTIL,
+ "unable to get dst buf address ReqId: %llu, num_patches = %d",
+ requestId, num_patches);
+ CAM_ERR(CAM_UTIL,
+ "i = %d patch info = %x %x %x %x dst_bfsz:0x%x",
+ i, patch_desc[i].dst_buf_hdl,
+ patch_desc[i].dst_offset,
+ patch_desc[i].src_buf_hdl,
+ patch_desc[i].src_offset,
+ (uint32_t)dst_buf_len);
return rc;
}
dst_cpu_addr = (uint32_t *)cpu_addr;
- CAM_DBG(CAM_UTIL, "i = %d patch info = %x %x %x %x", i,
- patch_desc[i].dst_buf_hdl, patch_desc[i].dst_offset,
+ CAM_DBG(CAM_UTIL,
+ "ReqId: %llu, i = %d patch info = %x %x %x %x",
+ requestId, i, patch_desc[i].dst_buf_hdl,
+ patch_desc[i].dst_offset,
patch_desc[i].src_buf_hdl, patch_desc[i].src_offset);
if ((size_t)patch_desc[i].src_offset >= src_buf_size) {
CAM_ERR(CAM_UTIL,
- "Invalid src buf patch offset");
+ "Invalid src buf patch offset ReqId: %llu, num_patches = %d",
+ requestId, num_patches);
+ CAM_ERR(CAM_UTIL,
+ "i = %d patch info = %x %x %x %x src_bfsz:0x%x",
+ i, patch_desc[i].dst_buf_hdl,
+ patch_desc[i].dst_offset,
+ patch_desc[i].src_buf_hdl,
+ patch_desc[i].src_offset,
+ (uint32_t)src_buf_size);
return -EINVAL;
}
@@ -225,7 +257,15 @@
((dst_buf_len - sizeof(void *)) <
(size_t)patch_desc[i].dst_offset)) {
CAM_ERR(CAM_UTIL,
- "Invalid dst buf patch offset");
+ "Invalid dst buf patch offset ReqId: %llu, num_patches = %d",
+ requestId, num_patches);
+ CAM_ERR(CAM_UTIL,
+ "i = %d patch info = %x %x %x %x dst_bfsz:0x%x",
+ i, patch_desc[i].dst_buf_hdl,
+ patch_desc[i].dst_offset,
+ patch_desc[i].src_buf_hdl,
+ patch_desc[i].src_offset,
+ (uint32_t)dst_buf_len);
return -EINVAL;
}
@@ -353,3 +393,115 @@
return rc;
}
+
+int32_t cam_packet_validate_plane_size(
+ struct cam_buf_io_cfg *io_cfg,
+ int plane_index,
+ size_t size)
+{
+ int rc = 0;
+ uint32_t kmd_plane_size = 0;
+ uint32_t plane_stride = 0;
+ uint32_t slice_height = 0;
+ uint32_t metadata_size = 0;
+ uint32_t format = io_cfg->format;
+ uint32_t plane_pixel_size = 0;
+
+ if (plane_index < CAM_PACKET_MAX_PLANES) {
+ plane_stride = io_cfg->planes[plane_index].plane_stride;
+ slice_height = io_cfg->planes[plane_index].slice_height;
+ }
+
+ if (!(plane_stride && slice_height)) {
+ CAM_ERR(CAM_ISP,
+ "Invalid values from UMD stride %d, slice height %d",
+ plane_stride,
+ slice_height);
+ return -EINVAL;
+ }
+
+ switch (format) {
+ case CAM_FORMAT_MIPI_RAW_6:
+ case CAM_FORMAT_MIPI_RAW_8:
+ kmd_plane_size = ((plane_stride * slice_height) + 16 - 1)
+ / 16 * 16;
+ break;
+ case CAM_FORMAT_MIPI_RAW_10:
+ if (plane_stride % 4 == 0)
+ kmd_plane_size = ((plane_stride * slice_height)
+ + 16 - 1) / 16 * 16;
+ break;
+ case CAM_FORMAT_MIPI_RAW_12:
+ if (plane_stride % 2 == 0)
+ kmd_plane_size = ((plane_stride * slice_height)
+ + 16 - 1) / 16 * 16;
+ break;
+ case CAM_FORMAT_MIPI_RAW_14:
+ if (plane_stride % 4 == 0)
+ kmd_plane_size = plane_stride * slice_height * 7 / 4;
+ break;
+ 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_PLAIN64:
+ kmd_plane_size = plane_stride * slice_height;
+ break;
+ case CAM_FORMAT_NV21:
+ case CAM_FORMAT_NV12:
+ if (plane_index < CAM_PACKET_MAX_PLANES)
+ kmd_plane_size = plane_stride * slice_height;
+ break;
+ case CAM_FORMAT_PD10:
+ if (plane_index < CAM_PACKET_MAX_PLANES)
+ kmd_plane_size = plane_stride * slice_height;
+ break;
+ case CAM_FORMAT_UBWC_NV12:
+ case CAM_FORMAT_UBWC_NV12_4R:
+ case CAM_FORMAT_UBWC_TP10:
+ metadata_size = io_cfg->planes[plane_index].meta_size;
+ plane_pixel_size = ((plane_stride * slice_height) +
+ (4096 - 1)) & ~((uint32_t) 4096 - 1);
+ kmd_plane_size = metadata_size + plane_pixel_size;
+ break;
+ case CAM_FORMAT_UBWC_P010:
+ case CAM_FORMAT_PLAIN32_20:
+ case CAM_FORMAT_TP10:
+ case CAM_FORMAT_YUV422:
+ case CAM_FORMAT_PD8:
+ case CAM_FORMAT_PLAIN128:
+ case CAM_FORMAT_ARGB:
+ case CAM_FORMAT_ARGB_10:
+ case CAM_FORMAT_ARGB_12:
+ case CAM_FORMAT_ARGB_14:
+ case CAM_FORMAT_MIPI_RAW_16:
+ case CAM_FORMAT_MIPI_RAW_20:
+ case CAM_FORMAT_QTI_RAW_8:
+ case CAM_FORMAT_QTI_RAW_10:
+ case CAM_FORMAT_QTI_RAW_12:
+ case CAM_FORMAT_QTI_RAW_14:
+ case CAM_FORMAT_PLAIN8:
+ case CAM_FORMAT_PLAIN8_SWAP:
+ case CAM_FORMAT_PLAIN8_10:
+ case CAM_FORMAT_PLAIN8_10_SWAP:
+ kmd_plane_size = plane_stride * slice_height;
+ break;
+ default:
+ kmd_plane_size = plane_stride * slice_height;
+ break;
+ }
+ if (!kmd_plane_size ||
+ kmd_plane_size > (size - io_cfg->offsets[plane_index])) {
+ CAM_ERR(CAM_ISP,
+ "kmd size: %d umd size: %zu width: %d height: %d stride: %d sliceheight: %d ",
+ kmd_plane_size,
+ size,
+ io_cfg->planes[plane_index].width,
+ io_cfg->planes[plane_index].height,
+ plane_stride,
+ slice_height);
+ return -EINVAL;
+ }
+ return rc;
+}
diff --git a/drivers/media/platform/msm/camera_v3/cam_utils/cam_packet_util.h b/drivers/media/platform/msm/camera_v3/cam_utils/cam_packet_util.h
index 33c07ad..e49968e 100644
--- a/drivers/media/platform/msm/camera_v3/cam_utils/cam_packet_util.h
+++ b/drivers/media/platform/msm/camera_v3/cam_utils/cam_packet_util.h
@@ -135,4 +135,20 @@
struct cam_cmd_buf_desc *cmd_buf,
cam_packet_generic_blob_handler blob_handler_cb, void *user_data);
+/**
+ * cam_packet_validate_plane_size()
+ *
+ * @brief: Utility function to calculate and validate size of buffer
+ * required for a format.
+ * @io_cfg: Contains IO config info
+ * @plane_index Plane index for which size is to be calculated
+ *
+ * @return: Size of buffer
+ *
+ */
+int32_t cam_packet_validate_plane_size(
+ struct cam_buf_io_cfg *io_cfg,
+ int plane_index,
+ size_t size);
+
#endif /* _CAM_PACKET_UTIL_H_ */
diff --git a/drivers/media/platform/msm/vidc/hfi_response_handler.c b/drivers/media/platform/msm/vidc/hfi_response_handler.c
index bde3110..6c2eebef 100644
--- a/drivers/media/platform/msm/vidc/hfi_response_handler.c
+++ b/drivers/media/platform/msm/vidc/hfi_response_handler.c
@@ -370,6 +370,12 @@
"hal_process_session_init_done: bad_pkt_size\n");
return -E2BIG;
}
+ if (pkt->size < sizeof(struct hfi_msg_event_notify_packet) - sizeof(u32)
+ + sizeof(struct hfi_msg_release_buffer_ref_event_packet)) {
+ dprintk(VIDC_ERR, "%s: bad_pkt_size: %d\n",
+ __func__, pkt->size);
+ return -E2BIG;
+ }
data = (struct hfi_msg_release_buffer_ref_event_packet *)
pkt->rg_ext_event_data;
@@ -1558,15 +1564,13 @@
struct hfi_msg_session_empty_buffer_done_packet *pkt = _pkt;
struct msm_vidc_cb_data_done data_done = {0};
struct hfi_picture_type *hfi_picture_type = NULL;
+ u32 is_sync_frame;
dprintk(VIDC_DBG, "RECEIVED: SESSION_ETB_DONE[%#x]\n", pkt->session_id);
if (!pkt || pkt->size <
- sizeof(struct hfi_msg_session_empty_buffer_done_packet)) {
- dprintk(VIDC_ERR,
- "hal_process_session_etb_done: bad_pkt_size\n");
- return -E2BIG;
- }
+ sizeof(struct hfi_msg_session_empty_buffer_done_packet))
+ goto bad_packet_size;
data_done.device_id = device_id;
data_done.session_id = (void *)(uintptr_t)pkt->session_id;
@@ -1586,8 +1590,13 @@
data_done.input_done.extra_data_buffer = pkt->extra_data_buffer;
data_done.input_done.status =
hfi_map_err_status(pkt->error_type);
- hfi_picture_type = (struct hfi_picture_type *)&pkt->rgData[0];
- if (hfi_picture_type->is_sync_frame) {
+ is_sync_frame = pkt->rgData[0];
+ if (is_sync_frame == 1) {
+ if (pkt->size <
+ sizeof(struct hfi_msg_session_empty_buffer_done_packet)
+ + sizeof(struct hfi_picture_type))
+ goto bad_packet_size;
+ hfi_picture_type = (struct hfi_picture_type *)&pkt->rgData[1];
if (hfi_picture_type->picture_type)
data_done.input_done.flags =
hfi_picture_type->picture_type;
@@ -1604,6 +1613,10 @@
info->response.data = data_done;
return 0;
+bad_packet_size:
+ dprintk(VIDC_ERR, "%s: bad_pkt_size: %d\n",
+ __func__, pkt ? pkt->size : 0);
+ return -E2BIG;
}
static int hfi_process_session_ftb_done(
@@ -1838,8 +1851,7 @@
cmd_done.size = sizeof(struct msm_vidc_cb_cmd_done);
cmd_done.session_id = (void *)(uintptr_t)pkt->session_id;
cmd_done.status = hfi_map_err_status(pkt->error_type);
- cmd_done.data.buffer_info =
- *(struct hal_buffer_info *)pkt->rg_buffer_info;
+ cmd_done.data.buffer_info.buffer_addr = *pkt->rg_buffer_info;
cmd_done.size = sizeof(struct hal_buffer_info);
info->response_type = HAL_SESSION_RELEASE_BUFFER_DONE;
diff --git a/drivers/media/platform/msm/vidc/vidc_hfi.h b/drivers/media/platform/msm/vidc/vidc_hfi.h
index 0cfe693..949eb0d 100644
--- a/drivers/media/platform/msm/vidc/vidc_hfi.h
+++ b/drivers/media/platform/msm/vidc/vidc_hfi.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012-2018, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2012-2019, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -601,7 +601,7 @@
u32 extra_data_buffer;
u32 flags;
struct hfi_frame_cr_stats_type ubwc_cr_stats;
- u32 rgData[0];
+ u32 rgData[1];
};
struct hfi_msg_session_fill_buffer_done_compressed_packet {
diff --git a/drivers/media/platform/msm/vidc/vidc_hfi_helper.h b/drivers/media/platform/msm/vidc/vidc_hfi_helper.h
index 733cbb9..f66d6c9 100644
--- a/drivers/media/platform/msm/vidc/vidc_hfi_helper.h
+++ b/drivers/media/platform/msm/vidc/vidc_hfi_helper.h
@@ -655,7 +655,6 @@
};
struct hfi_picture_type {
- u32 is_sync_frame;
u32 picture_type;
};
diff --git a/drivers/media/platform/msm/vidc_3x/hfi_response_handler.c b/drivers/media/platform/msm/vidc_3x/hfi_response_handler.c
index abf2ef1..654d9fa 100644
--- a/drivers/media/platform/msm/vidc_3x/hfi_response_handler.c
+++ b/drivers/media/platform/msm/vidc_3x/hfi_response_handler.c
@@ -287,6 +287,12 @@
"hal_process_session_init_done: bad_pkt_size\n");
return -E2BIG;
}
+ if (pkt->size < sizeof(struct hfi_msg_event_notify_packet) - sizeof(u32)
+ + sizeof(struct hfi_msg_release_buffer_ref_event_packet)) {
+ dprintk(VIDC_ERR,
+ "hfi_msg_release_buffer_ref_event: bad_pkt_size\n");
+ return -E2BIG;
+ }
data = (struct hfi_msg_release_buffer_ref_event_packet *)
pkt->rg_ext_event_data;
@@ -1540,15 +1546,13 @@
struct hfi_msg_session_empty_buffer_done_packet *pkt = _pkt;
struct msm_vidc_cb_data_done data_done = {0};
struct hfi_picture_type *hfi_picture_type = NULL;
+ u32 is_sync_frame;
dprintk(VIDC_DBG, "RECEIVED: SESSION_ETB_DONE[%#x]\n", pkt->session_id);
if (!pkt || pkt->size <
- sizeof(struct hfi_msg_session_empty_buffer_done_packet)) {
- dprintk(VIDC_ERR,
- "hal_process_session_etb_done: bad_pkt_size\n");
- return -E2BIG;
- }
+ sizeof(struct hfi_msg_session_empty_buffer_done_packet))
+ goto bad_packet_size;
data_done.device_id = device_id;
data_done.session_id = (void *)(uintptr_t)pkt->session_id;
@@ -1563,8 +1567,13 @@
(ion_phys_addr_t)pkt->extra_data_buffer;
data_done.input_done.status =
hfi_map_err_status(pkt->error_type);
- hfi_picture_type = (struct hfi_picture_type *)&pkt->rgData[0];
- if (hfi_picture_type->is_sync_frame) {
+ is_sync_frame = pkt->rgData[0];
+ if (is_sync_frame == 1) {
+ if (pkt->size <
+ sizeof(struct hfi_msg_session_empty_buffer_done_packet)
+ + sizeof(struct hfi_picture_type))
+ goto bad_packet_size;
+ hfi_picture_type = (struct hfi_picture_type *)&pkt->rgData[1];
if (hfi_picture_type->picture_type)
data_done.input_done.flags =
hfi_picture_type->picture_type;
@@ -1583,6 +1592,10 @@
};
return 0;
+bad_packet_size:
+ dprintk(VIDC_ERR, "%s: bad_pkt_size: %d\n",
+ __func__, pkt ? pkt->size : 0);
+ return -E2BIG;
}
static int hfi_process_session_ftb_done(
@@ -1823,8 +1836,7 @@
cmd_done.session_id = (void *)(uintptr_t)pkt->session_id;
cmd_done.status = hfi_map_err_status(pkt->error_type);
if (pkt->rg_buffer_info) {
- cmd_done.data.buffer_info =
- *(struct hal_buffer_info *)pkt->rg_buffer_info;
+ cmd_done.data.buffer_info.buffer_addr = *pkt->rg_buffer_info;
cmd_done.size = sizeof(struct hal_buffer_info);
} else {
dprintk(VIDC_ERR, "invalid payload in rel_buff_done\n");
diff --git a/drivers/media/platform/msm/vidc_3x/msm_vidc_common.c b/drivers/media/platform/msm/vidc_3x/msm_vidc_common.c
index be3b992..d7e2154 100644
--- a/drivers/media/platform/msm/vidc_3x/msm_vidc_common.c
+++ b/drivers/media/platform/msm/vidc_3x/msm_vidc_common.c
@@ -347,7 +347,7 @@
* ----------------|----------------------|------------------------|
*/
- if (is_realtime_session(inst) &&
+ if (!is_realtime_session(inst) &&
(quirks & LOAD_CALC_IGNORE_NON_REALTIME_LOAD)) {
if (!inst->prop.fps) {
dprintk(VIDC_INFO, "instance:%pK fps = 0\n", inst);
diff --git a/drivers/media/platform/msm/vidc_3x/vidc_hfi.h b/drivers/media/platform/msm/vidc_3x/vidc_hfi.h
index d0fd493..903603b 100644
--- a/drivers/media/platform/msm/vidc_3x/vidc_hfi.h
+++ b/drivers/media/platform/msm/vidc_3x/vidc_hfi.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012-2016, 2018 The Linux Foundation. All rights reserved.
+/* Copyright (c) 2012-2016, 2018-2019 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
@@ -662,7 +662,7 @@
u32 input_tag;
u32 packet_buffer;
u32 extra_data_buffer;
- u32 rgData[0];
+ u32 rgData[1];
};
struct hfi_msg_session_fill_buffer_done_compressed_packet {
diff --git a/drivers/media/platform/msm/vidc_3x/vidc_hfi_helper.h b/drivers/media/platform/msm/vidc_3x/vidc_hfi_helper.h
index c09cf84c..16c390a 100644
--- a/drivers/media/platform/msm/vidc_3x/vidc_hfi_helper.h
+++ b/drivers/media/platform/msm/vidc_3x/vidc_hfi_helper.h
@@ -701,7 +701,6 @@
};
struct hfi_picture_type {
- u32 is_sync_frame;
u32 picture_type;
};
diff --git a/drivers/net/can/spi/Kconfig b/drivers/net/can/spi/Kconfig
index 3ec631fc..5cd8a56 100644
--- a/drivers/net/can/spi/Kconfig
+++ b/drivers/net/can/spi/Kconfig
@@ -14,4 +14,10 @@
depends on SPI
---help---
Unified driver for QTI CAN controllers.
+
+config CAN_MCP25XXFD
+ tristate "Microchip MCP25xxFD SPI CAN controllers"
+ depends on HAS_DMA
+ ---help---
+ Driver for the Microchip MCP25XXFD SPI FD-CAN controller family.
endmenu
diff --git a/drivers/net/can/spi/Makefile b/drivers/net/can/spi/Makefile
index 64ba861..bbb4343 100644
--- a/drivers/net/can/spi/Makefile
+++ b/drivers/net/can/spi/Makefile
@@ -5,3 +5,4 @@
obj-$(CONFIG_CAN_MCP251X) += mcp251x.o
obj-$(CONFIG_QTI_CAN) += qti-can.o
+obj-$(CONFIG_CAN_MCP25XXFD) += mcp25xxfd.o
diff --git a/drivers/net/can/spi/mcp25xxfd.c b/drivers/net/can/spi/mcp25xxfd.c
new file mode 100644
index 0000000..12cdf44
--- /dev/null
+++ b/drivers/net/can/spi/mcp25xxfd.c
@@ -0,0 +1,4513 @@
+/*
+ * CAN bus driver for Microchip 25XXFD CAN Controller with SPI Interface
+ *
+ * Copyright 2017 Martin Sperl <kernel@xxxxxxxxxxxxxxxx>
+ *
+ * SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note
+ *
+ * Based on Microchip MCP251x CAN controller driver written by
+ * David Vrabel, Copyright 2006 Arcom Control Systems Ltd.
+ *
+ */
+
+#include <linux/can/core.h>
+#include <linux/can/dev.h>
+#include <linux/can/led.h>
+#include <linux/clk.h>
+#include <linux/completion.h>
+#include <linux/debugfs.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/dma-mapping.h>
+#include <linux/freezer.h>
+#include <linux/gpio/driver.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/jiffies.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/netdevice.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/sort.h>
+#include <linux/spi/spi.h>
+#include <linux/uaccess.h>
+#include <linux/regulator/consumer.h>
+
+#define DEVICE_NAME "mcp25xxfd"
+
+/* device description and rational:
+ *
+ * the mcp25xxfd is a CanFD controller that also supports can2.0 only
+ * modes.
+ * It is connected via spi to the host and requires at minimum a single
+ * irq line in addition to the SPI lines - it is not mentioned explicitly
+ * in the documentation but in principle SPI 3-wire should be possible.
+ *
+ * The clock connected is typically 4MHz, 20MHz or 40MHz.
+ * For the 4MHz clock the controller contains 10x PLL circuitry.
+ *
+ * The controller itself has 2KB or ECC-SRAM for data.
+ * It also has 32 FIFOs (of up to 32 CAN-frames).
+ * There are 4 Fifo types which can get configured:
+ * * TEF - Transmission Event Fifo - which consumes FIFO 0
+ * even if it is not configured
+ * * Tansmission Queue - for up to 32 Frames.
+ * this queue reorders CAN frames to get transmitted following the
+ * typical CAN dominant/recessive rules on the can bus itself.
+ * This FIFO is optional.
+ * * TX FIFO: generic TX fifos that can contain arbitrary data
+ * and which come with a configurable priority for transmission
+ * It is also possible to have the Controller automatically trigger
+ * a transfer when a Filter Rule for a RTR frame matches.
+ * Each of these fifos in principle can get configured for distinct
+ * dlc sizes (8 thru 64 bytes)
+ * * RX FIFO: generic RX fifo which is filled via filter-rules.
+ * Each of these fifos in principle can get configured for distinct
+ * dlc sizes (8 thru 64 bytes)
+ * Unfortunately there is no filter rule that would allow triggering
+ * on different frame sizes, so for all practical purposes the
+ * RX fifos have to be of the same size (unless one wants to experience
+ * lost data).
+ * When a Can Frame is transmitted fromthe TX Queue or an individual
+ * TX FIFO then a small TEF Frame can get added to the TEF FIFO queue
+ * to log the Transmission of the frame - this includes ID, Flags
+ * (including a custom identifier/index) .
+ *
+ * The controller provides an optional free running counter with a divider
+ * for timestamping of RX frames as well as for TEF entries.
+ *
+ * Driver Implementation details and rational:
+ * * The whole driver has been designed to give best performance
+ * and as little packet loss as possible with 1MHZ Can frames with DLC=0
+ * on small/slow devices like the Raspberry Pi 1
+ * * This means that some optimizations for full duplex communication
+ * have been implemented to avoid CPU introduced latencies
+ * (especially for spi_write_then_read cases) - this only applies to
+ * 4 wire SPI busses.
+ * * Due to the fact that the TXQ does reorder Can-Frames we do not make
+ * use of it to avoid unexpected behaviour (say when running a
+ * firmware upgrade via Can)
+ * * this means we use individual TX-fifos with a given priority and
+ * we have to wait until all the TX fifos have been transmitted before
+ * we can restart the networking queue to avoid reordering the frames on
+ * the Can bus itself.
+ * Still we can transmit a transmit only Duty-cycle of 66% to 90% on the
+ * Can bus (at 1MHz).
+ * The scaling factors here are:
+ * * Can bus speed - lower Speeds increase Duty-cycle
+ * * SPI Clock Rate - higher speeds increase duty-cycle
+ * * CPU speed + SPI implementation - reduces latencies between transfers
+ * * There is a module parameter that allows the modification of the
+ * number of tx_fifos, which is by default 7.
+ * * The driver offers some module parameters that allow to control the use
+ * of some optimizations (prefer reading more data than necessary instead
+ * of multiple SPI transfers - the idea here is that this way we may
+ * allow the SPI-controller to use DMA instead of programmed IO to
+ * limit latencies/number of interrupts)
+ * When we have to read multiple RX frames in CanFD mode:
+ * * we allow reading all 64 bytes of payload even if DLC <=8
+ * this mode is used in Can2.0 only mode by default and can not get
+ * disabled (SRAM Reads have to be a multiple of 4 bytes anyway)
+ * * Releasing/freeing the RX queue requires writing of 1 byte per fifo.
+ * unfortunately these 32-bit registers are not ajacent to each other,
+ * so that for 2 consecutive RX Frames instead of writing 1 byte per
+ * fifo (with protocol overhead of 2 bytes - so a total of 6 bytes in
+ * 2 transfers) we transmit 13 bytes (with a protocol overhead of 2 -
+ * so a total of 15 bytes)
+ * This optimization is only enabled by a module parameter.
+ * * we use TEF + time stamping to record the transmitted frames
+ * including their timestamp - we use this to order TX and RX frames
+ * when submitting them to the network stack.
+ * * due to the inability to "filter" based on DLC sizes we have to use
+ * a common FIFO size. This is 8 bytes for Can2.0 and 64 bytes for CanFD.
+ * * the driver tries to detect the Controller only by reading registers,
+ * but there are circumstances (e.g. after a crashed driver) where we
+ * have to "blindly" configure the clock rate to get the controller to
+ * respond correctly.
+ * * There is one situation where the controller will require a full POR
+ * (total power off) to recover from a bad Clock configuration.
+ * This happens when the wrong clock is configured in the device tree
+ * (say 4MHz are configured, while 20 or 40MHz are used)
+ * in such a situation the driver tries to enable the PLL, which will
+ * never synchronize and the controller becomes unresponsive to further
+ * spi requests until a POR.
+ */
+
+#define MCP25XXFD_OST_DELAY_MS 3
+#define MCP25XXFD_MIN_CLOCK_FREQUENCY 1000000
+#define MCP25XXFD_MAX_CLOCK_FREQUENCY 40000000
+#define MCP25XXFD_PLL_MULTIPLIER 10
+#define MCP25XXFD_AUTO_PLL_MAX_CLOCK_FREQUENCY \
+ (MCP25XXFD_MAX_CLOCK_FREQUENCY / MCP25XXFD_PLL_MULTIPLIER)
+#define MCP25XXFD_SCLK_DIVIDER 2
+
+#define MCP25XXFD_OSC_POLLING_JIFFIES (HZ / 2)
+
+#define TX_ECHO_SKB_MAX 32
+
+#define INSTRUCTION_RESET 0x0000
+#define INSTRUCTION_READ 0x3000
+#define INSTRUCTION_WRITE 0x2000
+#define INSTRUCTION_READ_CRC 0xB000
+#define INSTRUCTION_WRITE_CRC 0xA000
+#define INSTRUCTION_WRITE_SAVE 0xC000
+
+#define ADDRESS_MASK 0x0fff
+
+#define MCP25XXFD_SFR_BASE(x) (0xE00 + (x))
+#define MCP25XXFD_OSC MCP25XXFD_SFR_BASE(0x00)
+# define MCP25XXFD_OSC_PLLEN BIT(0)
+# define MCP25XXFD_OSC_OSCDIS BIT(2)
+# define MCP25XXFD_OSC_SCLKDIV BIT(4)
+# define MCP25XXFD_OSC_CLKODIV_BITS 2
+# define MCP25XXFD_OSC_CLKODIV_SHIFT 5
+# define MCP25XXFD_OSC_CLKODIV_MASK \
+ GENMASK(MCP25XXFD_OSC_CLKODIV_SHIFT \
+ + MCP25XXFD_OSC_CLKODIV_BITS - 1, \
+ MCP25XXFD_OSC_CLKODIV_SHIFT)
+# define MCP25XXFD_OSC_CLKODIV_10 3
+# define MCP25XXFD_OSC_CLKODIV_4 2
+# define MCP25XXFD_OSC_CLKODIV_2 1
+# define MCP25XXFD_OSC_CLKODIV_1 0
+# define MCP25XXFD_OSC_PLLRDY BIT(8)
+# define MCP25XXFD_OSC_OSCRDY BIT(10)
+# define MCP25XXFD_OSC_SCLKRDY BIT(12)
+#define MCP25XXFD_IOCON MCP25XXFD_SFR_BASE(0x04)
+# define MCP25XXFD_IOCON_TRIS0 BIT(0)
+# define MCP25XXFD_IOCON_TRIS1 BIT(1)
+# define MCP25XXFD_IOCON_XSTBYEN BIT(6)
+# define MCP25XXFD_IOCON_LAT0 BIT(8)
+# define MCP25XXFD_IOCON_LAT1 BIT(9)
+# define MCP25XXFD_IOCON_GPIO0 BIT(16)
+# define MCP25XXFD_IOCON_GPIO1 BIT(17)
+# define MCP25XXFD_IOCON_PM0 BIT(24)
+# define MCP25XXFD_IOCON_PM1 BIT(25)
+# define MCP25XXFD_IOCON_TXCANOD BIT(28)
+# define MCP25XXFD_IOCON_SOF BIT(29)
+# define MCP25XXFD_IOCON_INTOD BIT(29)
+#define MCP25XXFD_CRC MCP25XXFD_SFR_BASE(0x08)
+# define MCP25XXFD_CRC_MASK GENMASK(15, 0)
+# define MCP25XXFD_CRC_CRCERRIE BIT(16)
+# define MCP25XXFD_CRC_FERRIE BIT(17)
+# define MCP25XXFD_CRC_CRCERRIF BIT(24)
+# define MCP25XXFD_CRC_FERRIF BIT(25)
+#define MCP25XXFD_ECCCON MCP25XXFD_SFR_BASE(0x0C)
+# define MCP25XXFD_ECCCON_ECCEN BIT(0)
+# define MCP25XXFD_ECCCON_SECIE BIT(1)
+# define MCP25XXFD_ECCCON_DEDIE BIT(2)
+# define MCP25XXFD_ECCCON_PARITY_BITS 6
+# define MCP25XXFD_ECCCON_PARITY_SHIFT 8
+# define MCP25XXFD_ECCCON_PARITY_MASK \
+ GENMASK(MCP25XXFD_ECCCON_PARITY_SHIFT \
+ + MCP25XXFD_ECCCON_PARITY_BITS - 1, \
+ MCP25XXFD_ECCCON_PARITY_SHIFT)
+#define MCP25XXFD_ECCSTAT MCP25XXFD_SFR_BASE(0x10)
+# define MCP25XXFD_ECCSTAT_SECIF BIT(1)
+# define MCP25XXFD_ECCSTAT_DEDIF BIT(2)
+# define MCP25XXFD_ECCSTAT_ERRADDR_SHIFT 8
+# define MCP25XXFD_ECCSTAT_ERRADDR_MASK \
+ GENMASK(MCP25XXFD_ECCSTAT_ERRADDR_SHIFT + 11, \
+ MCP25XXFD_ECCSTAT_ERRADDR_SHIFT)
+
+#define CAN_SFR_BASE(x) (0x000 + (x))
+#define CAN_CON CAN_SFR_BASE(0x00)
+# define CAN_CON_DNCNT_BITS 5
+# define CAN_CON_DNCNT_SHIFT 0
+# define CAN_CON_DNCNT_MASK \
+ GENMASK(CAN_CON_DNCNT_SHIFT + CAN_CON_DNCNT_BITS - 1, \
+ CAN_CON_DNCNT_SHIFT)
+# define CAN_CON_ISOCRCEN BIT(5)
+# define CAN_CON_PXEDIS BIT(6)
+# define CAN_CON_WAKFIL BIT(8)
+# define CAN_CON_WFT_BITS 2
+# define CAN_CON_WFT_SHIFT 9
+# define CAN_CON_WFT_MASK \
+ GENMASK(CAN_CON_WFT_SHIFT + CAN_CON_WFT_BITS - 1, \
+ CAN_CON_WFT_SHIFT)
+# define CAN_CON_BUSY BIT(11)
+# define CAN_CON_BRSDIS BIT(12)
+# define CAN_CON_RTXAT BIT(16)
+# define CAN_CON_ESIGM BIT(17)
+# define CAN_CON_SERR2LOM BIT(18)
+# define CAN_CON_STEF BIT(19)
+# define CAN_CON_TXQEN BIT(20)
+# define CAN_CON_OPMODE_BITS 3
+# define CAN_CON_OPMOD_SHIFT 21
+# define CAN_CON_OPMOD_MASK \
+ GENMASK(CAN_CON_OPMOD_SHIFT + CAN_CON_OPMODE_BITS - 1, \
+ CAN_CON_OPMOD_SHIFT)
+# define CAN_CON_REQOP_BITS 3
+# define CAN_CON_REQOP_SHIFT 24
+# define CAN_CON_REQOP_MASK \
+ GENMASK(CAN_CON_REQOP_SHIFT + CAN_CON_REQOP_BITS - 1, \
+ CAN_CON_REQOP_SHIFT)
+# define CAN_CON_MODE_MIXED 0
+# define CAN_CON_MODE_SLEEP 1
+# define CAN_CON_MODE_INTERNAL_LOOPBACK 2
+# define CAN_CON_MODE_LISTENONLY 3
+# define CAN_CON_MODE_CONFIG 4
+# define CAN_CON_MODE_EXTERNAL_LOOPBACK 5
+# define CAN_CON_MODE_CAN2_0 6
+# define CAN_CON_MODE_RESTRICTED 7
+# define CAN_CON_ABAT BIT(27)
+# define CAN_CON_TXBWS_BITS 3
+# define CAN_CON_TXBWS_SHIFT 28
+# define CAN_CON_TXBWS_MASK \
+ GENMASK(CAN_CON_TXBWS_SHIFT + CAN_CON_TXBWS_BITS - 1, \
+ CAN_CON_TXBWS_SHIFT)
+# define CAN_CON_DEFAULT \
+ (CAN_CON_ISOCRCEN | \
+ CAN_CON_PXEDIS | \
+ CAN_CON_WAKFIL | \
+ (3 << CAN_CON_WFT_SHIFT) | \
+ CAN_CON_STEF | \
+ CAN_CON_TXQEN | \
+ (CAN_CON_MODE_CONFIG << CAN_CON_OPMOD_SHIFT) | \
+ (CAN_CON_MODE_CONFIG << CAN_CON_REQOP_SHIFT))
+# define CAN_CON_DEFAULT_MASK \
+ (CAN_CON_DNCNT_MASK | \
+ CAN_CON_ISOCRCEN | \
+ CAN_CON_PXEDIS | \
+ CAN_CON_WAKFIL | \
+ CAN_CON_WFT_MASK | \
+ CAN_CON_BRSDIS | \
+ CAN_CON_RTXAT | \
+ CAN_CON_ESIGM | \
+ CAN_CON_SERR2LOM | \
+ CAN_CON_STEF | \
+ CAN_CON_TXQEN | \
+ CAN_CON_OPMOD_MASK | \
+ CAN_CON_REQOP_MASK | \
+ CAN_CON_ABAT | \
+ CAN_CON_TXBWS_MASK)
+#define CAN_NBTCFG CAN_SFR_BASE(0x04)
+# define CAN_NBTCFG_SJW_BITS 7
+# define CAN_NBTCFG_SJW_SHIFT 0
+# define CAN_NBTCFG_SJW_MASK \
+ GENMASK(CAN_NBTCFG_SJW_SHIFT + CAN_NBTCFG_SJW_BITS - 1, \
+ CAN_NBTCFG_SJW_SHIFT)
+# define CAN_NBTCFG_TSEG2_BITS 7
+# define CAN_NBTCFG_TSEG2_SHIFT 8
+# define CAN_NBTCFG_TSEG2_MASK \
+ GENMASK(CAN_NBTCFG_TSEG2_SHIFT + CAN_NBTCFG_TSEG2_BITS - 1, \
+ CAN_NBTCFG_TSEG2_SHIFT)
+# define CAN_NBTCFG_TSEG1_BITS 8
+# define CAN_NBTCFG_TSEG1_SHIFT 16
+# define CAN_NBTCFG_TSEG1_MASK \
+ GENMASK(CAN_NBTCFG_TSEG1_SHIFT + CAN_NBTCFG_TSEG1_BITS - 1, \
+ CAN_NBTCFG_TSEG1_SHIFT)
+# define CAN_NBTCFG_BRP_BITS 8
+# define CAN_NBTCFG_BRP_SHIFT 24
+# define CAN_NBTCFG_BRP_MASK \
+ GENMASK(CAN_NBTCFG_BRP_SHIFT + CAN_NBTCFG_BRP_BITS - 1, \
+ CAN_NBTCFG_BRP_SHIFT)
+#define CAN_DBTCFG CAN_SFR_BASE(0x08)
+# define CAN_DBTCFG_SJW_BITS 4
+# define CAN_DBTCFG_SJW_SHIFT 0
+# define CAN_DBTCFG_SJW_MASK \
+ GENMASK(CAN_DBTCFG_SJW_SHIFT + CAN_DBTCFG_SJW_BITS - 1, \
+ CAN_DBTCFG_SJW_SHIFT)
+# define CAN_DBTCFG_TSEG2_BITS 4
+# define CAN_DBTCFG_TSEG2_SHIFT 8
+# define CAN_DBTCFG_TSEG2_MASK \
+ GENMASK(CAN_DBTCFG_TSEG2_SHIFT + CAN_DBTCFG_TSEG2_BITS - 1, \
+ CAN_DBTCFG_TSEG2_SHIFT)
+# define CAN_DBTCFG_TSEG1_BITS 5
+# define CAN_DBTCFG_TSEG1_SHIFT 16
+# define CAN_DBTCFG_TSEG1_MASK \
+ GENMASK(CAN_DBTCFG_TSEG1_SHIFT + CAN_DBTCFG_TSEG1_BITS - 1, \
+ CAN_DBTCFG_TSEG1_SHIFT)
+# define CAN_DBTCFG_BRP_BITS 8
+# define CAN_DBTCFG_BRP_SHIFT 24
+# define CAN_DBTCFG_BRP_MASK \
+ GENMASK(CAN_DBTCFG_BRP_SHIFT + CAN_DBTCFG_BRP_BITS - 1, \
+ CAN_DBTCFG_BRP_SHIFT)
+#define CAN_TDC CAN_SFR_BASE(0x0C)
+# define CAN_TDC_TDCV_BITS 5
+# define CAN_TDC_TDCV_SHIFT 0
+# define CAN_TDC_TDCV_MASK \
+ GENMASK(CAN_TDC_TDCV_SHIFT + CAN_TDC_TDCV_BITS - 1, \
+ CAN_TDC_TDCV_SHIFT)
+# define CAN_TDC_TDCO_BITS 5
+# define CAN_TDC_TDCO_SHIFT 8
+# define CAN_TDC_TDCO_MASK \
+ GENMASK(CAN_TDC_TDCO_SHIFT + CAN_TDC_TDCO_BITS - 1, \
+ CAN_TDC_TDCO_SHIFT)
+# define CAN_TDC_TDCMOD_BITS 2
+# define CAN_TDC_TDCMOD_SHIFT 16
+# define CAN_TDC_TDCMOD_MASK \
+ GENMASK(CAN_TDC_TDCMOD_SHIFT + CAN_TDC_TDCMOD_BITS - 1, \
+ CAN_TDC_TDCMOD_SHIFT)
+# define CAN_TDC_TDCMOD_DISABLED 0
+# define CAN_TDC_TDCMOD_MANUAL 1
+# define CAN_TDC_TDCMOD_AUTO 2
+# define CAN_TDC_SID11EN BIT(24)
+# define CAN_TDC_EDGFLTEN BIT(25)
+#define CAN_TBC CAN_SFR_BASE(0x10)
+#define CAN_TSCON CAN_SFR_BASE(0x14)
+# define CAN_TSCON_TBCPRE_BITS 10
+# define CAN_TSCON_TBCPRE_SHIFT 0
+# define CAN_TSCON_TBCPRE_MASK \
+ GENMASK(CAN_TSCON_TBCPRE_SHIFT + CAN_TSCON_TBCPRE_BITS - 1, \
+ CAN_TSCON_TBCPRE_SHIFT)
+# define CAN_TSCON_TBCEN BIT(16)
+# define CAN_TSCON_TSEOF BIT(17)
+# define CAN_TSCON_TSRES BIT(18)
+#define CAN_VEC CAN_SFR_BASE(0x18)
+# define CAN_VEC_ICODE_BITS 7
+# define CAN_VEC_ICODE_SHIFT 0
+# define CAN_VEC_ICODE_MASK \
+ GENMASK(CAN_VEC_ICODE_SHIFT + CAN_VEC_ICODE_BITS - 1, \
+ CAN_VEC_ICODE_SHIFT)
+# define CAN_VEC_FILHIT_BITS 5
+# define CAN_VEC_FILHIT_SHIFT 8
+# define CAN_VEC_FILHIT_MASK \
+ GENMASK(CAN_VEC_FILHIT_SHIFT + CAN_VEC_FILHIT_BITS - 1, \
+ CAN_VEC_FILHIT_SHIFT)
+# define CAN_VEC_TXCODE_BITS 7
+# define CAN_VEC_TXCODE_SHIFT 16
+# define CAN_VEC_TXCODE_MASK \
+ GENMASK(CAN_VEC_TXCODE_SHIFT + CAN_VEC_TXCODE_BITS - 1, \
+ CAN_VEC_TXCODE_SHIFT)
+# define CAN_VEC_RXCODE_BITS 7
+# define CAN_VEC_RXCODE_SHIFT 24
+# define CAN_VEC_RXCODE_MASK \
+ GENMASK(CAN_VEC_RXCODE_SHIFT + CAN_VEC_RXCODE_BITS - 1, \
+ CAN_VEC_RXCODE_SHIFT)
+#define CAN_INT CAN_SFR_BASE(0x1C)
+# define CAN_INT_IF_SHIFT 0
+# define CAN_INT_TXIF BIT(0)
+# define CAN_INT_RXIF BIT(1)
+# define CAN_INT_TBCIF BIT(2)
+# define CAN_INT_MODIF BIT(3)
+# define CAN_INT_TEFIF BIT(4)
+# define CAN_INT_ECCIF BIT(8)
+# define CAN_INT_SPICRCIF BIT(9)
+# define CAN_INT_TXATIF BIT(10)
+# define CAN_INT_RXOVIF BIT(11)
+# define CAN_INT_SERRIF BIT(12)
+# define CAN_INT_CERRIF BIT(13)
+# define CAN_INT_WAKIF BIT(14)
+# define CAN_INT_IVMIF BIT(15)
+# define CAN_INT_IF_MASK \
+ (CAN_INT_TXIF | \
+ CAN_INT_RXIF | \
+ CAN_INT_TBCIF | \
+ CAN_INT_MODIF | \
+ CAN_INT_TEFIF | \
+ CAN_INT_ECCIF | \
+ CAN_INT_SPICRCIF | \
+ CAN_INT_TXATIF | \
+ CAN_INT_RXOVIF | \
+ CAN_INT_CERRIF | \
+ CAN_INT_SERRIF | \
+ CAN_INT_WAKEIF | \
+ CAN_INT_IVMIF)
+# define CAN_INT_IE_SHIFT 16
+# define CAN_INT_TXIE (CAN_INT_TXIF << CAN_INT_IE_SHIFT)
+# define CAN_INT_RXIE (CAN_INT_RXIF << CAN_INT_IE_SHIFT)
+# define CAN_INT_TBCIE (CAN_INT_TBCIF << CAN_INT_IE_SHIFT)
+# define CAN_INT_MODIE (CAN_INT_MODIF << CAN_INT_IE_SHIFT)
+# define CAN_INT_TEFIE (CAN_INT_TEFIF << CAN_INT_IE_SHIFT)
+# define CAN_INT_ECCIE (CAN_INT_ECCIF << CAN_INT_IE_SHIFT)
+# define CAN_INT_SPICRCIE \
+ (CAN_INT_SPICRCIF << CAN_INT_IE_SHIFT)
+# define CAN_INT_TXATIE (CAN_INT_TXATIF << CAN_INT_IE_SHIFT)
+# define CAN_INT_RXOVIE (CAN_INT_RXOVIF << CAN_INT_IE_SHIFT)
+# define CAN_INT_CERRIE (CAN_INT_CERRIF << CAN_INT_IE_SHIFT)
+# define CAN_INT_SERRIE (CAN_INT_SERRIF << CAN_INT_IE_SHIFT)
+# define CAN_INT_WAKIE (CAN_INT_WAKIF << CAN_INT_IE_SHIFT)
+# define CAN_INT_IVMIE (CAN_INT_IVMIF << CAN_INT_IE_SHIFT)
+# define CAN_INT_IE_MASK \
+ (CAN_INT_TXIE | \
+ CAN_INT_RXIE | \
+ CAN_INT_TBCIE | \
+ CAN_INT_MODIE | \
+ CAN_INT_TEFIE | \
+ CAN_INT_ECCIE | \
+ CAN_INT_SPICRCIE | \
+ CAN_INT_TXATIE | \
+ CAN_INT_RXOVIE | \
+ CAN_INT_CERRIE | \
+ CAN_INT_SERRIE | \
+ CAN_INT_WAKEIE | \
+ CAN_INT_IVMIE)
+#define CAN_RXIF CAN_SFR_BASE(0x20)
+#define CAN_TXIF CAN_SFR_BASE(0x24)
+#define CAN_RXOVIF CAN_SFR_BASE(0x28)
+#define CAN_TXATIF CAN_SFR_BASE(0x2C)
+#define CAN_TXREQ CAN_SFR_BASE(0x30)
+#define CAN_TREC CAN_SFR_BASE(0x34)
+# define CAN_TREC_REC_BITS 8
+# define CAN_TREC_REC_SHIFT 0
+# define CAN_TREC_REC_MASK \
+ GENMASK(CAN_TREC_REC_SHIFT + CAN_TREC_REC_BITS - 1, \
+ CAN_TREC_REC_SHIFT)
+# define CAN_TREC_TEC_BITS 8
+# define CAN_TREC_TEC_SHIFT 8
+# define CAN_TREC_TEC_MASK \
+ GENMASK(CAN_TREC_TEC_SHIFT + CAN_TREC_TEC_BITS - 1, \
+ CAN_TREC_TEC_SHIFT)
+# define CAN_TREC_EWARN BIT(16)
+# define CAN_TREC_RXWARN BIT(17)
+# define CAN_TREC_TXWARN BIT(18)
+# define CAN_TREC_RXBP BIT(19)
+# define CAN_TREC_TXBP BIT(20)
+# define CAN_TREC_TXBO BIT(21)
+#define CAN_BDIAG0 CAN_SFR_BASE(0x38)
+# define CAN_BDIAG0_NRERRCNT_BITS 8
+# define CAN_BDIAG0_NRERRCNT_SHIFT 0
+# define CAN_BDIAG0_NRERRCNT_MASK \
+ GENMASK(CAN_BDIAG0_NRERRCNT_SHIFT + CAN_BDIAG0_NRERRCNT_BITS - 1, \
+ CAN_BDIAG0_NRERRCNT_SHIFT)
+# define CAN_BDIAG0_NTERRCNT_BITS 8
+# define CAN_BDIAG0_NTERRCNT_SHIFT 8
+# define CAN_BDIAG0_NTERRCNT_MASK \
+ GENMASK(CAN_BDIAG0_NTERRCNT_SHIFT + CAN_BDIAG0_NTERRCNT_BITS - 1, \
+ CAN_BDIAG0_NTERRCNT_SHIFT)
+# define CAN_BDIAG0_DRERRCNT_BITS 8
+# define CAN_BDIAG0_DRERRCNT_SHIFT 16
+# define CAN_BDIAG0_DRERRCNT_MASK \
+ GENMASK(CAN_BDIAG0_DRERRCNT_SHIFT + CAN_BDIAG0_DRERRCNT_BITS - 1, \
+ CAN_BDIAG0_DRERRCNT_SHIFT)
+# define CAN_BDIAG0_DTERRCNT_BITS 8
+# define CAN_BDIAG0_DTERRCNT_SHIFT 24
+# define CAN_BDIAG0_DTERRCNT_MASK \
+ GENMASK(CAN_BDIAG0_DTERRCNT_SHIFT + CAN_BDIAG0_DTERRCNT_BITS - 1, \
+ CAN_BDIAG0_DTERRCNT_SHIFT)
+#define CAN_BDIAG1 CAN_SFR_BASE(0x3C)
+# define CAN_BDIAG1_EFMSGCNT_BITS 16
+# define CAN_BDIAG1_EFMSGCNT_SHIFT 0
+# define CAN_BDIAG1_EFMSGCNT_MASK \
+ GENMASK(CAN_BDIAG1_EFMSGCNT_SHIFT + CAN_BDIAG1_EFMSGCNT_BITS - 1, \
+ CAN_BDIAG1_EFMSGCNT_SHIFT)
+# define CAN_BDIAG1_NBIT0ERR BIT(16)
+# define CAN_BDIAG1_NBIT1ERR BIT(17)
+# define CAN_BDIAG1_NACKERR BIT(18)
+# define CAN_BDIAG1_NSTUFERR BIT(19)
+# define CAN_BDIAG1_NFORMERR BIT(20)
+# define CAN_BDIAG1_NCRCERR BIT(21)
+# define CAN_BDIAG1_TXBOERR BIT(23)
+# define CAN_BDIAG1_DBIT0ERR BIT(24)
+# define CAN_BDIAG1_DBIT1ERR BIT(25)
+# define CAN_BDIAG1_DFORMERR BIT(27)
+# define CAN_BDIAG1_DSTUFERR BIT(28)
+# define CAN_BDIAG1_DCRCERR BIT(29)
+# define CAN_BDIAG1_ESI BIT(30)
+# define CAN_BDIAG1_DLCMM BIT(31)
+#define CAN_TEFCON CAN_SFR_BASE(0x40)
+# define CAN_TEFCON_TEFNEIE BIT(0)
+# define CAN_TEFCON_TEFHIE BIT(1)
+# define CAN_TEFCON_TEFFIE BIT(2)
+# define CAN_TEFCON_TEFOVIE BIT(3)
+# define CAN_TEFCON_TEFTSEN BIT(5)
+# define CAN_TEFCON_UINC BIT(8)
+# define CAN_TEFCON_FRESET BIT(10)
+# define CAN_TEFCON_FSIZE_BITS 5
+# define CAN_TEFCON_FSIZE_SHIFT 24
+# define CAN_TEFCON_FSIZE_MASK \
+ GENMASK(CAN_TEFCON_FSIZE_SHIFT + CAN_TEFCON_FSIZE_BITS - 1, \
+ CAN_TEFCON_FSIZE_SHIFT)
+#define CAN_TEFSTA CAN_SFR_BASE(0x44)
+# define CAN_TEFSTA_TEFNEIF BIT(0)
+# define CAN_TEFSTA_TEFHIF BIT(1)
+# define CAN_TEFSTA_TEFFIF BIT(2)
+# define CAN_TEFSTA_TEVOVIF BIT(3)
+#define CAN_TEFUA CAN_SFR_BASE(0x48)
+#define CAN_RESERVED CAN_SFR_BASE(0x4C)
+#define CAN_TXQCON CAN_SFR_BASE(0x50)
+# define CAN_TXQCON_TXQNIE BIT(0)
+# define CAN_TXQCON_TXQEIE BIT(2)
+# define CAN_TXQCON_TXATIE BIT(4)
+# define CAN_TXQCON_TXEN BIT(7)
+# define CAN_TXQCON_UINC BIT(8)
+# define CAN_TXQCON_TXREQ BIT(9)
+# define CAN_TXQCON_FRESET BIT(10)
+# define CAN_TXQCON_TXPRI_BITS 5
+# define CAN_TXQCON_TXPRI_SHIFT 16
+# define CAN_TXQCON_TXPRI_MASK \
+ GENMASK(CAN_TXQCON_TXPRI_SHIFT + CAN_TXQCON_TXPRI_BITS - 1, \
+ CAN_TXQCON_TXPRI_SHIFT)
+# define CAN_TXQCON_TXAT_BITS 2
+# define CAN_TXQCON_TXAT_SHIFT 21
+# define CAN_TXQCON_TXAT_MASK \
+ GENMASK(CAN_TXQCON_TXAT_SHIFT + CAN_TXQCON_TXAT_BITS - 1, \
+ CAN_TXQCON_TXAT_SHIFT)
+# define CAN_TXQCON_FSIZE_BITS 5
+# define CAN_TXQCON_FSIZE_SHIFT 24
+# define CAN_TXQCON_FSIZE_MASK \
+ GENMASK(CAN_TXQCON_FSIZE_SHIFT + CAN_TXQCON_FSIZE_BITS - 1, \
+ CAN_TXQCON_FSIZE_SHIFT)
+# define CAN_TXQCON_PLSIZE_BITS 3
+# define CAN_TXQCON_PLSIZE_SHIFT 29
+# define CAN_TXQCON_PLSIZE_MASK \
+ GENMASK(CAN_TXQCON_PLSIZE_SHIFT + CAN_TXQCON_PLSIZE_BITS - 1, \
+ CAN_TXQCON_PLSIZE_SHIFT)
+# define CAN_TXQCON_PLSIZE_8 0
+# define CAN_TXQCON_PLSIZE_12 1
+# define CAN_TXQCON_PLSIZE_16 2
+# define CAN_TXQCON_PLSIZE_20 3
+# define CAN_TXQCON_PLSIZE_24 4
+# define CAN_TXQCON_PLSIZE_32 5
+# define CAN_TXQCON_PLSIZE_48 6
+# define CAN_TXQCON_PLSIZE_64 7
+
+#define CAN_TXQSTA CAN_SFR_BASE(0x54)
+# define CAN_TXQSTA_TXQNIF BIT(0)
+# define CAN_TXQSTA_TXQEIF BIT(2)
+# define CAN_TXQSTA_TXATIF BIT(4)
+# define CAN_TXQSTA_TXERR BIT(5)
+# define CAN_TXQSTA_TXLARB BIT(6)
+# define CAN_TXQSTA_TXABT BIT(7)
+# define CAN_TXQSTA_TXQCI_BITS 5
+# define CAN_TXQSTA_TXQCI_SHIFT 8
+# define CAN_TXQSTA_TXQCI_MASK \
+ GENMASK(CAN_TXQSTA_TXQCI_SHIFT + CAN_TXQSTA_TXQCI_BITS - 1, \
+ CAN_TXQSTA_TXQCI_SHIFT)
+
+#define CAN_TXQUA CAN_SFR_BASE(0x58)
+#define CAN_FIFOCON(x) CAN_SFR_BASE(0x5C + 12 * ((x) - 1))
+#define CAN_FIFOCON_TFNRFNIE BIT(0)
+#define CAN_FIFOCON_TFHRFHIE BIT(1)
+#define CAN_FIFOCON_TFERFFIE BIT(2)
+#define CAN_FIFOCON_RXOVIE BIT(3)
+#define CAN_FIFOCON_TXATIE BIT(4)
+#define CAN_FIFOCON_RXTSEN BIT(5)
+#define CAN_FIFOCON_RTREN BIT(6)
+#define CAN_FIFOCON_TXEN BIT(7)
+#define CAN_FIFOCON_UINC BIT(8)
+#define CAN_FIFOCON_TXREQ BIT(9)
+#define CAN_FIFOCON_FRESET BIT(10)
+# define CAN_FIFOCON_TXPRI_BITS 5
+# define CAN_FIFOCON_TXPRI_SHIFT 16
+# define CAN_FIFOCON_TXPRI_MASK \
+ GENMASK(CAN_FIFOCON_TXPRI_SHIFT + CAN_FIFOCON_TXPRI_BITS - 1, \
+ CAN_FIFOCON_TXPRI_SHIFT)
+# define CAN_FIFOCON_TXAT_BITS 2
+# define CAN_FIFOCON_TXAT_SHIFT 21
+# define CAN_FIFOCON_TXAT_MASK \
+ GENMASK(CAN_FIFOCON_TXAT_SHIFT + CAN_FIFOCON_TXAT_BITS - 1, \
+ CAN_FIFOCON_TXAT_SHIFT)
+# define CAN_FIFOCON_TXAT_ONE_SHOT 0
+# define CAN_FIFOCON_TXAT_THREE_SHOT 1
+# define CAN_FIFOCON_TXAT_UNLIMITED 2
+# define CAN_FIFOCON_FSIZE_BITS 5
+# define CAN_FIFOCON_FSIZE_SHIFT 24
+# define CAN_FIFOCON_FSIZE_MASK \
+ GENMASK(CAN_FIFOCON_FSIZE_SHIFT + CAN_FIFOCON_FSIZE_BITS - 1, \
+ CAN_FIFOCON_FSIZE_SHIFT)
+# define CAN_FIFOCON_PLSIZE_BITS 3
+# define CAN_FIFOCON_PLSIZE_SHIFT 29
+# define CAN_FIFOCON_PLSIZE_MASK \
+ GENMASK(CAN_FIFOCON_PLSIZE_SHIFT + CAN_FIFOCON_PLSIZE_BITS - 1, \
+ CAN_FIFOCON_PLSIZE_SHIFT)
+#define CAN_FIFOSTA(x) CAN_SFR_BASE(0x60 + 12 * ((x) - 1))
+# define CAN_FIFOSTA_TFNRFNIF BIT(0)
+# define CAN_FIFOSTA_TFHRFHIF BIT(1)
+# define CAN_FIFOSTA_TFERFFIF BIT(2)
+# define CAN_FIFOSTA_RXOVIF BIT(3)
+# define CAN_FIFOSTA_TXATIF BIT(4)
+# define CAN_FIFOSTA_TXERR BIT(5)
+# define CAN_FIFOSTA_TXLARB BIT(6)
+# define CAN_FIFOSTA_TXABT BIT(7)
+# define CAN_FIFOSTA_FIFOCI_BITS 5
+# define CAN_FIFOSTA_FIFOCI_SHIFT 8
+# define CAN_FIFOSTA_FIFOCI_MASK \
+ GENMASK(CAN_FIFOSTA_FIFOCI_SHIFT + CAN_FIFOSTA_FIFOCI_BITS - 1, \
+ CAN_FIFOSTA_FIFOCI_SHIFT)
+#define CAN_FIFOUA(x) CAN_SFR_BASE(0x64 + 12 * ((x) - 1))
+#define CAN_FLTCON(x) CAN_SFR_BASE(0x1D0 + ((x) & 0x1c))
+# define CAN_FILCON_SHIFT(x) (((x) & 3) * 8)
+# define CAN_FILCON_BITS(x) CAN_FILCON_BITS_
+# define CAN_FILCON_BITS_ 4
+ /* avoid macro reuse warning, so do not use GENMASK as above */
+# define CAN_FILCON_MASK(x) \
+ (GENMASK(CAN_FILCON_BITS_ - 1, 0) << CAN_FILCON_SHIFT(x))
+# define CAN_FIFOCON_FLTEN(x) BIT(7 + CAN_FILCON_SHIFT(x))
+#define CAN_FLTOBJ(x) CAN_SFR_BASE(0x1F0 + 8 * (x))
+# define CAN_FILOBJ_SID_BITS 11
+# define CAN_FILOBJ_SID_SHIFT 0
+# define CAN_FILOBJ_SID_MASK \
+ GENMASK(CAN_FILOBJ_SID_SHIFT + CAN_FILOBJ_SID_BITS - 1, \
+ CAN_FILOBJ_SID_SHIFT)
+# define CAN_FILOBJ_EID_BITS 18
+# define CAN_FILOBJ_EID_SHIFT 12
+# define CAN_FILOBJ_EID_MASK \
+ GENMASK(CAN_FILOBJ_EID_SHIFT + CAN_FILOBJ_EID_BITS - 1, \
+ CAN_FILOBJ_EID_SHIFT)
+# define CAN_FILOBJ_SID11 BIT(29)
+# define CAN_FILOBJ_EXIDE BIT(30)
+#define CAN_FLTMASK(x) CAN_SFR_BASE(0x1F4 + 8 * (x))
+# define CAN_FILMASK_MSID_BITS 11
+# define CAN_FILMASK_MSID_SHIFT 0
+# define CAN_FILMASK_MSID_MASK \
+ GENMASK(CAN_FILMASK_MSID_SHIFT + CAN_FILMASK_MSID_BITS - 1, \
+ CAN_FILMASK_MSID_SHIFT)
+# define CAN_FILMASK_MEID_BITS 18
+# define CAN_FILMASK_MEID_SHIFT 12
+# define CAN_FILMASK_MEID_MASK \
+ GENMASK(CAN_FILMASK_MEID_SHIFT + CAN_FILMASK_MEID_BITS - 1, \
+ CAN_FILMASK_MEID_SHIFT)
+# define CAN_FILMASK_MSID11 BIT(29)
+# define CAN_FILMASK_MIDE BIT(30)
+
+#define CAN_OBJ_ID_SID_BITS 11
+#define CAN_OBJ_ID_SID_SHIFT 0
+#define CAN_OBJ_ID_SID_MASK \
+ GENMASK(CAN_OBJ_ID_SID_SHIFT + CAN_OBJ_ID_SID_BITS - 1, \
+ CAN_OBJ_ID_SID_SHIFT)
+#define CAN_OBJ_ID_EID_BITS 18
+#define CAN_OBJ_ID_EID_SHIFT 11
+#define CAN_OBJ_ID_EID_MASK \
+ GENMASK(CAN_OBJ_ID_EID_SHIFT + CAN_OBJ_ID_EID_BITS - 1, \
+ CAN_OBJ_ID_EID_SHIFT)
+#define CAN_OBJ_ID_SID_BIT11 BIT(29)
+
+#define CAN_OBJ_FLAGS_DLC_BITS 4
+#define CAN_OBJ_FLAGS_DLC_SHIFT 0
+#define CAN_OBJ_FLAGS_DLC_MASK \
+ GENMASK(CAN_OBJ_FLAGS_DLC_SHIFT + CAN_OBJ_FLAGS_DLC_BITS - 1, \
+ CAN_OBJ_FLAGS_DLC_SHIFT)
+#define CAN_OBJ_FLAGS_IDE BIT(4)
+#define CAN_OBJ_FLAGS_RTR BIT(5)
+#define CAN_OBJ_FLAGS_BRS BIT(6)
+#define CAN_OBJ_FLAGS_FDF BIT(7)
+#define CAN_OBJ_FLAGS_ESI BIT(8)
+#define CAN_OBJ_FLAGS_SEQ_BITS 7
+#define CAN_OBJ_FLAGS_SEQ_SHIFT 9
+#define CAN_OBJ_FLAGS_SEQ_MASK \
+ GENMASK(CAN_OBJ_FLAGS_SEQ_SHIFT + CAN_OBJ_FLAGS_SEQ_BITS - 1, \
+ CAN_OBJ_FLAGS_SEQ_SHIFT)
+#define CAN_OBJ_FLAGS_FILHIT_BITS 11
+#define CAN_OBJ_FLAGS_FILHIT_SHIFT 5
+#define CAN_OBJ_FLAGS_FILHIT_MASK \
+ GENMASK(CAN_FLAGS_FILHIT_SHIFT + CAN_FLAGS_FILHIT_BITS - 1, \
+ CAN_FLAGS_FILHIT_SHIFT)
+
+#define CAN_OBJ_FLAGS_CUSTOM_ISTEF BIT(31)
+
+#define MCP25XXFD_BUFFER_TXRX_SIZE 2048
+
+static const char * const mcp25xxfd_mode_names[] = {
+ [CAN_CON_MODE_MIXED] = "can2.0+canfd",
+ [CAN_CON_MODE_SLEEP] = "sleep",
+ [CAN_CON_MODE_INTERNAL_LOOPBACK] = "internal loopback",
+ [CAN_CON_MODE_LISTENONLY] = "listen only",
+ [CAN_CON_MODE_CONFIG] = "config",
+ [CAN_CON_MODE_EXTERNAL_LOOPBACK] = "external loopback",
+ [CAN_CON_MODE_CAN2_0] = "can2.0",
+ [CAN_CON_MODE_RESTRICTED] = "restricted"
+};
+
+struct mcp25xxfd_obj {
+ u32 id;
+ u32 flags;
+};
+
+struct mcp25xxfd_obj_tx {
+ struct mcp25xxfd_obj header;
+ u32 data[];
+};
+
+static void mcp25xxfd_obj_to_le(struct mcp25xxfd_obj *obj)
+{
+ obj->id = cpu_to_le32(obj->id);
+ obj->flags = cpu_to_le32(obj->flags);
+}
+
+struct mcp25xxfd_obj_ts {
+ u32 id;
+ u32 flags;
+ u32 ts;
+};
+
+struct mcp25xxfd_obj_tef {
+ struct mcp25xxfd_obj_ts header;
+};
+
+struct mcp25xxfd_obj_rx {
+ struct mcp25xxfd_obj_ts header;
+ u8 data[];
+};
+
+static void mcp25xxfd_obj_ts_from_le(struct mcp25xxfd_obj_ts *obj)
+{
+ obj->id = le32_to_cpu(obj->id);
+ obj->flags = le32_to_cpu(obj->flags);
+ obj->ts = le32_to_cpu(obj->ts);
+}
+
+#define FIFO_DATA(x) (0x400 + (x))
+#define FIFO_DATA_SIZE 0x800
+
+static const struct can_bittiming_const mcp25xxfd_nominal_bittiming_const = {
+ .name = DEVICE_NAME,
+ .tseg1_min = 2,
+ .tseg1_max = BIT(CAN_NBTCFG_TSEG1_BITS),
+ .tseg2_min = 1,
+ .tseg2_max = BIT(CAN_NBTCFG_TSEG2_BITS),
+ .sjw_max = BIT(CAN_NBTCFG_SJW_BITS),
+ .brp_min = 1,
+ .brp_max = BIT(CAN_NBTCFG_BRP_BITS),
+ .brp_inc = 1,
+};
+
+static const struct can_bittiming_const mcp25xxfd_data_bittiming_const = {
+ .name = DEVICE_NAME,
+ .tseg1_min = 1,
+ .tseg1_max = BIT(CAN_DBTCFG_TSEG1_BITS),
+ .tseg2_min = 1,
+ .tseg2_max = BIT(CAN_DBTCFG_TSEG2_BITS),
+ .sjw_max = BIT(CAN_DBTCFG_SJW_BITS),
+ .brp_min = 1,
+ .brp_max = BIT(CAN_DBTCFG_BRP_BITS),
+ .brp_inc = 1,
+};
+
+enum mcp25xxfd_model {
+ CAN_MCP2517FD = 0x2517,
+};
+
+enum mcp25xxfd_gpio_mode {
+ gpio_mode_int = 0,
+ gpio_mode_standby = MCP25XXFD_IOCON_XSTBYEN,
+ gpio_mode_out_low = MCP25XXFD_IOCON_PM0,
+ gpio_mode_out_high = MCP25XXFD_IOCON_PM0 | MCP25XXFD_IOCON_LAT0,
+ gpio_mode_in = MCP25XXFD_IOCON_PM0 | MCP25XXFD_IOCON_TRIS0
+};
+
+struct mcp25xxfd_trigger_tx_message {
+ struct spi_message msg;
+ struct spi_transfer fill_xfer;
+ struct spi_transfer trigger_xfer;
+ int fifo;
+ char fill_cmd[2];
+ char fill_obj[sizeof(struct mcp25xxfd_obj_tx)];
+ char fill_data[64];
+ char trigger_cmd[2];
+ char trigger_data;
+};
+
+struct mcp25xxfd_read_fifo_info {
+ struct mcp25xxfd_obj_ts *rxb[32];
+ int rx_count;
+};
+
+struct mcp25xxfd_priv {
+ struct can_priv can;
+ struct net_device *net;
+ struct spi_device *spi;
+ struct regulator *power;
+ struct regulator *transceiver;
+ struct clk *clk;
+
+ struct mutex clk_user_lock; /* lock for enabling/disabling the clock */
+ int clk_user_mask;
+#define MCP25XXFD_CLK_USER_CAN BIT(0)
+#define MCP25XXFD_CLK_USER_GPIO0 BIT(1)
+#define MCP25XXFD_CLK_USER_GPIO1 BIT(2)
+
+ struct dentry *debugfs_dir;
+
+#ifdef CONFIG_GPIOLIB
+ struct gpio_chip gpio;
+#endif
+
+ /* the actual model of the mcp25xxfd */
+ enum mcp25xxfd_model model;
+
+ struct {
+ /* clock configuration */
+ bool clock_pll;
+ bool clock_div2;
+ int clock_odiv;
+
+ /* GPIO configuration */
+ bool gpio_opendrain;
+ } config;
+
+ /* the distinct spi_speeds to use for spi communication */
+ u32 spi_setup_speed_hz;
+ u32 spi_speed_hz;
+
+ /* fifo info */
+ struct {
+ /* define payload size and mode */
+ int payload_size;
+ u32 payload_mode;
+
+ /* TEF addresses - start, end and current */
+ u32 tef_fifos;
+ u32 tef_address_start;
+ u32 tef_address_end;
+ u32 tef_address;
+
+ /* address in mcp25xxfd-Fifo RAM of each fifo */
+ u32 fifo_address[32];
+
+ /* infos on tx-fifos */
+ u32 tx_fifos;
+ u32 tx_fifo_start;
+ u32 tx_fifo_mask; /* bitmask of which fifo is a tx fifo */
+ u32 tx_submitted_mask;
+ u32 tx_pending_mask;
+ u32 tx_pending_mask_in_irq;
+ u32 tx_processed_mask;
+
+ /* info on rx_fifos */
+ u32 rx_fifos;
+ u32 rx_fifo_depth;
+ u32 rx_fifo_start;
+ u32 rx_fifo_mask; /* bitmask of which fifo is a rx fifo */
+
+ /* memory image of FIFO RAM on mcp25xxfd */
+ u8 fifo_data[MCP25XXFD_BUFFER_TXRX_SIZE];
+
+ } fifos;
+
+ /* structure with active fifos that need to get fed to the system */
+ struct mcp25xxfd_read_fifo_info queued_fifos;
+
+ /* statistics */
+ struct {
+ /* number of calls to the irq handler */
+ u64 irq_calls;
+ /* number of loops inside the irq handler */
+ u64 irq_loops;
+
+ /* interrupt handler state and statistics */
+ u32 irq_state;
+#define IRQ_STATE_NEVER_RUN 0
+#define IRQ_STATE_RUNNING 1
+#define IRQ_STATE_HANDLED 2
+ /* stats on number of rx overflows */
+ u64 rx_overflow;
+ /* statistics of FIFO usage */
+ u64 fifo_usage[32];
+
+ /* message abort counter */
+ u64 rx_mab;
+ u64 tx_mab;
+
+ /* message counter fd */
+ u64 rx_fd_count;
+ u64 tx_fd_count;
+
+ /* message counter fd bit rate switch */
+ u64 rx_brs_count;
+ u64 tx_brs_count;
+
+ /* interrupt counter */
+ u64 int_ivm_count;
+ u64 int_wake_count;
+ u64 int_cerr_count;
+ u64 int_serr_count;
+ u64 int_rxov_count;
+ u64 int_txat_count;
+ u64 int_spicrc_count;
+ u64 int_ecc_count;
+ u64 int_tef_count;
+ u64 int_mod_count;
+ u64 int_tbc_count;
+ u64 int_rx_count;
+ u64 int_tx_count;
+
+ /* dlc statistics */
+ u64 rx_dlc_usage[16];
+ u64 tx_dlc_usage[16];
+ } stats;
+
+ /* the current status of the mcp25xxfd */
+ struct {
+ u32 intf;
+ /* ASSERT(CAN_INT + 4 == CAN_RXIF) */
+ u32 rxif;
+ /* ASSERT(CAN_RXIF + 4 == CAN_TXIF) */
+ u32 txif;
+ /* ASSERT(CAN_TXIF + 4 == CAN_RXOVIF) */
+ u32 rxovif;
+ /* ASSERT(CAN_RXOVIF + 4 == CAN_TXATIF) */
+ u32 txatif;
+ /* ASSERT(CAN_TXATIF + 4 == CAN_TXREQ) */
+ u32 txreq;
+ /* ASSERT(CAN_TXREQ + 4 == CAN_TREC) */
+ u32 trec;
+ /* ASSERT(CAN_TREC + 4 == CAN_BDIAG0) */
+ u32 bdiag0;
+ /* ASSERT(CAN_BDIAG0 + 4 == CAN_BDIAG1) */
+ u32 bdiag1;
+ } status;
+
+ /* configuration registers */
+ struct {
+ u32 osc;
+ u32 ecccon;
+ u32 con;
+ u32 iocon;
+ u32 tdc;
+ u32 tscon;
+ u32 tefcon;
+ u32 nbtcfg;
+ u32 dbtcfg;
+ } regs;
+
+ /* interrupt handler signaling */
+ int force_quit;
+ int after_suspend;
+#define AFTER_SUSPEND_UP 1
+#define AFTER_SUSPEND_DOWN 2
+#define AFTER_SUSPEND_POWER 4
+#define AFTER_SUSPEND_RESTART 8
+ int restart_tx;
+
+ /* interrupt flags during irq handling */
+ u32 bdiag1_clear_mask;
+ u32 bdiag1_clear_value;
+
+ /* composit error id and dataduring irq handling */
+ u32 can_err_id;
+ u32 can_err_data[8];
+
+ /* the current mode */
+ u32 active_can_mode;
+ u32 new_state;
+
+ /* status of the tx_queue enabled/disabled */
+ u32 tx_queue_status;
+#define TX_QUEUE_STATUS_INIT 0
+#define TX_QUEUE_STATUS_RUNNING 1
+#define TX_QUEUE_STATUS_NEEDS_START 2
+#define TX_QUEUE_STATUS_STOPPED 3
+
+ /* spi-tx/rx buffers for efficient transfers
+ * used during setup and irq
+ */
+ struct mutex spi_rxtx_lock;
+ u8 spi_tx[MCP25XXFD_BUFFER_TXRX_SIZE];
+ u8 spi_rx[MCP25XXFD_BUFFER_TXRX_SIZE];
+
+ /* structure for transmit fifo spi_messages */
+ struct mcp25xxfd_trigger_tx_message *spi_transmit_fifos;
+};
+
+/* module parameters */
+bool use_bulk_release_fifos;
+module_param(use_bulk_release_fifos, bool, 0664);
+MODULE_PARM_DESC(use_bulk_release_fifos,
+ "Use code that favours longer spi transfers over multiple transfers");
+bool use_complete_fdfifo_read;
+module_param(use_complete_fdfifo_read, bool, 0664);
+MODULE_PARM_DESC(use_complete_fdfifo_read,
+ "Use code that favours longer spi transfers over multiple transfers for fd can");
+unsigned int tx_fifos;
+module_param(tx_fifos, uint, 0664);
+MODULE_PARM_DESC(tx_fifos,
+ "Number of tx-fifos to configure\n");
+unsigned int bw_sharing_log2bits;
+module_param(bw_sharing_log2bits, uint, 0664);
+MODULE_PARM_DESC(bw_sharing_log2bits,
+ "Delay between 2 transmissions in number of arbitration bit times\n");
+bool three_shot;
+module_param(three_shot, bool, 0664);
+MODULE_PARM_DESC(three_shot,
+ "Use 3 shots when one-shot is requested");
+
+/* spi sync helper */
+
+/* wrapper arround spi_sync, that sets speed_hz */
+static int mcp25xxfd_sync_transfer(struct spi_device *spi,
+ struct spi_transfer *xfer,
+ unsigned int xfers,
+ int speed_hz)
+{
+ int i;
+
+ for (i = 0; i < xfers; i++)
+ xfer[i].speed_hz = speed_hz;
+
+ return spi_sync_transfer(spi, xfer, xfers);
+}
+
+/* an optimization of spi_write_then_read that merges the transfers */
+static int mcp25xxfd_write_then_read(struct spi_device *spi,
+ const void *tx_buf,
+ unsigned int tx_len,
+ void *rx_buf,
+ unsigned int rx_len,
+ int speed_hz)
+{
+ struct mcp25xxfd_priv *priv = spi_get_drvdata(spi);
+ struct spi_transfer xfer[2];
+ u8 single_reg_data_tx[6];
+ u8 single_reg_data_rx[6];
+ int ret;
+
+ memset(xfer, 0, sizeof(xfer));
+
+ /* when using a halfduplex controller or to big for buffer */
+ if ((spi->master->flags & SPI_MASTER_HALF_DUPLEX) ||
+ (tx_len + rx_len > sizeof(priv->spi_tx))) {
+ xfer[0].tx_buf = tx_buf;
+ xfer[0].len = tx_len;
+
+ xfer[1].rx_buf = rx_buf;
+ xfer[1].len = rx_len;
+
+ return mcp25xxfd_sync_transfer(spi, xfer, 2, speed_hz);
+ }
+
+ /* full duplex optimization */
+ xfer[0].len = tx_len + rx_len;
+ if (xfer[0].len > sizeof(single_reg_data_tx)) {
+ mutex_lock(&priv->spi_rxtx_lock);
+ xfer[0].tx_buf = priv->spi_tx;
+ xfer[0].rx_buf = priv->spi_rx;
+ } else {
+ xfer[0].tx_buf = single_reg_data_tx;
+ xfer[0].rx_buf = single_reg_data_rx;
+ }
+
+ /* copy and clean */
+ memcpy((u8 *)xfer[0].tx_buf, tx_buf, tx_len);
+ memset((u8 *)xfer[0].tx_buf + tx_len, 0, rx_len);
+
+ ret = mcp25xxfd_sync_transfer(spi, xfer, 1, speed_hz);
+ if (!ret)
+ memcpy(rx_buf, xfer[0].rx_buf + tx_len, rx_len);
+
+ if (xfer[0].len > sizeof(single_reg_data_tx))
+ mutex_unlock(&priv->spi_rxtx_lock);
+
+ return ret;
+}
+
+/* simple spi_write wrapper with speed_hz */
+static int mcp25xxfd_write(struct spi_device *spi,
+ const void *tx_buf,
+ unsigned int tx_len,
+ int speed_hz)
+{
+ struct spi_transfer xfer;
+
+ memset(&xfer, 0, sizeof(xfer));
+ xfer.tx_buf = tx_buf;
+ xfer.len = tx_len;
+
+ return mcp25xxfd_sync_transfer(spi, &xfer, 1, speed_hz);
+}
+
+/* spi_sync wrapper similar to spi_write_then_read that optimizes transfers */
+static int mcp25xxfd_write_then_write(struct spi_device *spi,
+ const void *tx_buf,
+ unsigned int tx_len,
+ const void *tx2_buf,
+ unsigned int tx2_len,
+ int speed_hz)
+{
+ struct mcp25xxfd_priv *priv = spi_get_drvdata(spi);
+ struct spi_transfer xfer;
+ u8 single_reg_data[6];
+ int ret;
+
+ if (tx_len + tx2_len > MCP25XXFD_BUFFER_TXRX_SIZE)
+ return -EINVAL;
+
+ memset(&xfer, 0, sizeof(xfer));
+
+ xfer.len = tx_len + tx2_len;
+ if (xfer.len > sizeof(single_reg_data)) {
+ mutex_lock(&priv->spi_rxtx_lock);
+ xfer.tx_buf = priv->spi_tx;
+ } else {
+ xfer.tx_buf = single_reg_data;
+ }
+
+ memcpy((u8 *)xfer.tx_buf, tx_buf, tx_len);
+ memcpy((u8 *)xfer.tx_buf + tx_len, tx2_buf, tx2_len);
+
+ ret = mcp25xxfd_sync_transfer(spi, &xfer, 1, speed_hz);
+
+ if (xfer.len > sizeof(single_reg_data))
+ mutex_unlock(&priv->spi_rxtx_lock);
+
+ return ret;
+}
+
+/* mcp25xxfd spi command/protocol helper */
+
+static void mcp25xxfd_calc_cmd_addr(u16 cmd, u16 addr, u8 *data)
+{
+ cmd = cmd | (addr & ADDRESS_MASK);
+
+ data[0] = (cmd >> 8) & 0xff;
+ data[1] = (cmd >> 0) & 0xff;
+}
+
+static int mcp25xxfd_cmd_reset(struct spi_device *spi, u32 speed_hz)
+{
+ u8 cmd[2];
+
+ mcp25xxfd_calc_cmd_addr(INSTRUCTION_RESET, 0, cmd);
+
+ /* write the reset command */
+ return mcp25xxfd_write(spi, cmd, 2, speed_hz);
+}
+
+/* read multiple bytes, transform some registers */
+static int mcp25xxfd_cmd_readn(struct spi_device *spi, u32 reg,
+ void *data, int n, u32 speed_hz)
+{
+ u8 cmd[2];
+ int ret;
+
+ mcp25xxfd_calc_cmd_addr(INSTRUCTION_READ, reg, cmd);
+
+ ret = mcp25xxfd_write_then_read(spi, &cmd, 2, data, n, speed_hz);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+static int mcp25xxfd_convert_to_cpu(u32 *data, int n)
+{
+ int i;
+
+ for (i = 0; i < n; i++)
+ data[i] = le32_to_cpu(data[i]);
+
+ return 0;
+}
+
+static int mcp25xxfd_first_byte(u32 mask)
+{
+ return (mask & 0x0000ffff) ?
+ ((mask & 0x000000ff) ? 0 : 1) :
+ ((mask & 0x00ff0000) ? 2 : 3);
+}
+
+static int mcp25xxfd_last_byte(u32 mask)
+{
+ return (mask & 0xffff0000) ?
+ ((mask & 0xff000000) ? 3 : 2) :
+ ((mask & 0x0000ff00) ? 1 : 0);
+}
+
+/* read a register, but we are only interrested in a few bytes */
+static int mcp25xxfd_cmd_read_mask(struct spi_device *spi, u32 reg,
+ u32 *data, u32 mask, u32 speed_hz)
+{
+ int first_byte, last_byte, len_byte;
+ int ret;
+
+ /* check that at least one bit is set */
+ if (!mask)
+ return -EINVAL;
+
+ /* calculate first and last byte used */
+ first_byte = mcp25xxfd_first_byte(mask);
+ last_byte = mcp25xxfd_last_byte(mask);
+ len_byte = last_byte - first_byte + 1;
+
+ /* do a partial read */
+ *data = 0;
+ ret = mcp25xxfd_cmd_readn(spi, reg + first_byte,
+ ((void *)data + first_byte), len_byte,
+ speed_hz);
+ if (ret)
+ return ret;
+
+ return mcp25xxfd_convert_to_cpu(data, 1);
+}
+
+static int mcp25xxfd_cmd_read(struct spi_device *spi, u32 reg, u32 *data,
+ u32 speed_hz)
+{
+ return mcp25xxfd_cmd_read_mask(spi, reg, data, -1, speed_hz);
+}
+
+/* read a register, but we are only interrested in a few bytes */
+static int mcp25xxfd_cmd_write_mask(struct spi_device *spi, u32 reg,
+ u32 data, u32 mask, u32 speed_hz)
+{
+ int first_byte, last_byte, len_byte;
+ u8 cmd[2];
+
+ /* check that at least one bit is set */
+ if (!mask)
+ return -EINVAL;
+
+ /* calculate first and last byte used */
+ first_byte = mcp25xxfd_first_byte(mask);
+ last_byte = mcp25xxfd_last_byte(mask);
+ len_byte = last_byte - first_byte + 1;
+
+ /* prepare buffer */
+ mcp25xxfd_calc_cmd_addr(INSTRUCTION_WRITE, reg + first_byte, cmd);
+ data = cpu_to_le32(data);
+
+ return mcp25xxfd_write_then_write(spi,
+ cmd, sizeof(cmd),
+ ((void *)&data + first_byte),
+ len_byte,
+ speed_hz);
+}
+
+static int mcp25xxfd_cmd_write(struct spi_device *spi, u32 reg, u32 data,
+ u32 speed_hz)
+{
+ return mcp25xxfd_cmd_write_mask(spi, reg, data, -1, speed_hz);
+}
+
+static int mcp25xxfd_cmd_writen(struct spi_device *spi, u32 reg,
+ void *data, int n, u32 speed_hz)
+{
+ u8 cmd[2];
+ int ret;
+
+ mcp25xxfd_calc_cmd_addr(INSTRUCTION_WRITE, reg, cmd);
+
+ ret = mcp25xxfd_write_then_write(spi, &cmd, 2, data, n, speed_hz);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+static int mcp25xxfd_clean_sram(struct spi_device *spi, u32 speed_hz)
+{
+ u8 buffer[256];
+ int i;
+ int ret;
+
+ memset(buffer, 0, sizeof(buffer));
+
+ for (i = 0; i < FIFO_DATA_SIZE; i += sizeof(buffer)) {
+ ret = mcp25xxfd_cmd_writen(spi, FIFO_DATA(i),
+ buffer, sizeof(buffer),
+ speed_hz);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+/* mcp25xxfd opmode helper functions */
+
+static int mcp25xxfd_get_opmode(struct spi_device *spi,
+ int *mode,
+ int speed_hz)
+{
+ struct mcp25xxfd_priv *priv = spi_get_drvdata(spi);
+ int ret;
+
+ /* read the mode */
+ ret = mcp25xxfd_cmd_read_mask(spi,
+ CAN_CON,
+ &priv->regs.con,
+ CAN_CON_OPMOD_MASK,
+ speed_hz);
+ if (ret)
+ return ret;
+ /* calculate the mode */
+ *mode = (priv->regs.con & CAN_CON_OPMOD_MASK) >>
+ CAN_CON_OPMOD_SHIFT;
+
+ /* and assign to active mode as well */
+ priv->active_can_mode = *mode;
+
+ return 0;
+}
+
+static int mcp25xxfd_set_opmode(struct spi_device *spi, int mode,
+ int speed_hz)
+{
+ struct mcp25xxfd_priv *priv = spi_get_drvdata(spi);
+ u32 val = priv->regs.con & ~CAN_CON_REQOP_MASK;
+
+ /* regs.con also contains the effective register */
+ priv->regs.con = val |
+ (mode << CAN_CON_REQOP_SHIFT) |
+ (mode << CAN_CON_OPMOD_SHIFT);
+ priv->active_can_mode = mode;
+
+ /* if the opmode is sleep then the oscilator will be disabled
+ * and also not ready
+ */
+ if (mode == CAN_CON_MODE_SLEEP) {
+ priv->regs.osc &= ~(MCP25XXFD_OSC_OSCRDY |
+ MCP25XXFD_OSC_PLLRDY |
+ MCP25XXFD_OSC_SCLKRDY);
+ priv->regs.osc |= MCP25XXFD_OSC_OSCDIS;
+ }
+
+ /* but only write the relevant section */
+ return mcp25xxfd_cmd_write_mask(spi, CAN_CON,
+ priv->regs.con,
+ CAN_CON_REQOP_MASK,
+ speed_hz);
+}
+
+static int mcp25xxfd_set_normal_opmode(struct spi_device *spi)
+{
+ struct mcp25xxfd_priv *priv = spi_get_drvdata(spi);
+ int mode;
+
+ if (priv->can.ctrlmode & CAN_CTRLMODE_LOOPBACK)
+ mode = CAN_CON_MODE_EXTERNAL_LOOPBACK;
+ else if (priv->can.ctrlmode & CAN_CTRLMODE_LISTENONLY)
+ mode = CAN_CON_MODE_LISTENONLY;
+ else if (priv->can.ctrlmode & CAN_CTRLMODE_FD)
+ mode = CAN_CON_MODE_MIXED;
+ else
+ mode = CAN_CON_MODE_CAN2_0;
+
+ return mcp25xxfd_set_opmode(spi, mode, priv->spi_setup_speed_hz);
+}
+
+/* clock helper */
+static int mcp25xxfd_wake_from_sleep(struct spi_device *spi)
+{
+ struct mcp25xxfd_priv *priv = spi_get_drvdata(spi);
+ u32 waitfor = MCP25XXFD_OSC_OSCRDY;
+ u32 mask = waitfor | MCP25XXFD_OSC_OSCDIS;
+ unsigned long timeout;
+ int ret;
+
+ /* write clock with OSCDIS cleared*/
+ priv->regs.osc &= ~MCP25XXFD_OSC_OSCDIS;
+ ret = mcp25xxfd_cmd_write(spi, MCP25XXFD_OSC,
+ priv->regs.osc,
+ priv->spi_setup_speed_hz);
+ if (ret)
+ return ret;
+
+ /* wait for synced pll/osc/sclk */
+ timeout = jiffies + MCP25XXFD_OSC_POLLING_JIFFIES;
+ while (time_before_eq(jiffies, timeout)) {
+ ret = mcp25xxfd_cmd_read(spi, MCP25XXFD_OSC,
+ &priv->regs.osc,
+ priv->spi_setup_speed_hz);
+ if (ret)
+ return ret;
+ if ((priv->regs.osc & mask) == waitfor) {
+ priv->active_can_mode = CAN_CON_MODE_CONFIG;
+ return 0;
+ }
+ /* wait some time */
+ msleep(100);
+ }
+
+ dev_err(&spi->dev,
+ "Clock did not enable within the timeout period\n");
+ return -ETIMEDOUT;
+}
+
+static int mcp25xxfd_hw_check_clock(struct spi_device *spi)
+{
+ struct mcp25xxfd_priv *priv = spi_get_drvdata(spi);
+ u32 val;
+ int ret;
+
+ /* read the osc register and check if it matches
+ * what we have on record
+ */
+ ret = mcp25xxfd_cmd_read(spi, MCP25XXFD_OSC,
+ &val,
+ priv->spi_setup_speed_hz);
+ if (ret)
+ return ret;
+
+ if (val == priv->regs.osc)
+ return 0;
+
+ /* ignore all those ready bits on second try */
+ if ((val & 0xff) == (priv->regs.osc & 0xff)) {
+ dev_info(&spi->dev,
+ "The oscillator register value %08x does not match what we expect: %08x - it is still reasonable, but please investigate\n",
+ val, priv->regs.osc);
+ return 0;
+ }
+
+ dev_err(&spi->dev,
+ "The oscillator register value %08x does not match what we expect: %08x\n",
+ val, priv->regs.osc);
+
+ return -ENODEV;
+}
+
+static int mcp25xxfd_start_clock(struct spi_device *spi, int requestor_mask)
+{
+ struct mcp25xxfd_priv *priv = spi_get_drvdata(spi);
+ int ret = 0;
+
+ mutex_lock(&priv->clk_user_lock);
+
+ priv->clk_user_mask |= requestor_mask;
+
+ if (priv->clk_user_mask != requestor_mask)
+ goto out;
+
+ /* check that the controller clock register
+ * is what it is supposed to be
+ */
+ ret = mcp25xxfd_hw_check_clock(spi);
+ if (ret) {
+ dev_err(&spi->dev,
+ "Controller clock register in unexpected state");
+ goto out;
+ }
+
+ /* and we start the clock */
+ if (!IS_ERR(priv->clk))
+ ret = clk_prepare_enable(priv->clk);
+
+ /* we wake from sleep */
+ if (priv->active_can_mode == CAN_CON_MODE_SLEEP)
+ ret = mcp25xxfd_wake_from_sleep(spi);
+
+out:
+ mutex_unlock(&priv->clk_user_lock);
+
+ return ret;
+}
+
+static int mcp25xxfd_stop_clock(struct spi_device *spi, int requestor_mask)
+{
+ struct mcp25xxfd_priv *priv = spi_get_drvdata(spi);
+
+ mutex_lock(&priv->clk_user_lock);
+
+ priv->clk_user_mask &= ~requestor_mask;
+
+ if (!priv->clk_user_mask)
+ goto out;
+
+ /* put us into sleep mode */
+ mcp25xxfd_set_opmode(spi, CAN_CON_MODE_SLEEP,
+ priv->spi_setup_speed_hz);
+
+ /* and we stop the clock */
+ if (!IS_ERR(priv->clk))
+ clk_disable_unprepare(priv->clk);
+
+out:
+ mutex_unlock(&priv->clk_user_lock);
+
+ return 0;
+}
+
+/* mcp25xxfd GPIO helper functions */
+#ifdef CONFIG_GPIOLIB
+
+enum mcp25xxfd_gpio_pins {
+ MCP25XXFD_GPIO_GPIO0 = 0,
+ MCP25XXFD_GPIO_GPIO1 = 1,
+};
+
+static int mcp25xxfd_gpio_request(struct gpio_chip *chip,
+ unsigned int offset)
+{
+ struct mcp25xxfd_priv *priv = gpiochip_get_data(chip);
+ int clock_requestor = offset ?
+ MCP25XXFD_CLK_USER_GPIO1 : MCP25XXFD_CLK_USER_GPIO0;
+
+ /* only handle gpio 0/1 */
+ if (offset > 1)
+ return -EINVAL;
+
+ mcp25xxfd_start_clock(priv->spi, clock_requestor);
+
+ return 0;
+}
+
+static void mcp25xxfd_gpio_free(struct gpio_chip *chip,
+ unsigned int offset)
+{
+ struct mcp25xxfd_priv *priv = gpiochip_get_data(chip);
+ int clock_requestor = offset ?
+ MCP25XXFD_CLK_USER_GPIO1 : MCP25XXFD_CLK_USER_GPIO0;
+
+ /* only handle gpio 0/1 */
+ if (offset > 1)
+ return;
+
+ mcp25xxfd_stop_clock(priv->spi, clock_requestor);
+}
+
+static int mcp25xxfd_gpio_get(struct gpio_chip *chip, unsigned int offset)
+{
+ struct mcp25xxfd_priv *priv = gpiochip_get_data(chip);
+ u32 mask = (offset) ? MCP25XXFD_IOCON_GPIO1 : MCP25XXFD_IOCON_GPIO0;
+ int ret;
+
+ /* only handle gpio 0/1 */
+ if (offset > 1)
+ return -EINVAL;
+
+ /* read the relevant gpio Latch */
+ ret = mcp25xxfd_cmd_read_mask(priv->spi, MCP25XXFD_IOCON,
+ &priv->regs.iocon, mask,
+ priv->spi_setup_speed_hz);
+ if (ret)
+ return ret;
+
+ /* return the match */
+ return priv->regs.iocon & mask;
+}
+
+static void mcp25xxfd_gpio_set(struct gpio_chip *chip, unsigned int offset,
+ int value)
+{
+ struct mcp25xxfd_priv *priv = gpiochip_get_data(chip);
+ u32 mask = (offset) ? MCP25XXFD_IOCON_LAT1 : MCP25XXFD_IOCON_LAT0;
+
+ /* only handle gpio 0/1 */
+ if (offset > 1)
+ return;
+
+ /* update in memory representation with the corresponding value */
+ if (value)
+ priv->regs.iocon |= mask;
+ else
+ priv->regs.iocon &= ~mask;
+
+ mcp25xxfd_cmd_write_mask(priv->spi, MCP25XXFD_IOCON,
+ priv->regs.iocon, mask,
+ priv->spi_setup_speed_hz);
+}
+
+static int mcp25xxfd_gpio_direction_input(struct gpio_chip *chip,
+ unsigned int offset)
+{
+ struct mcp25xxfd_priv *priv = gpiochip_get_data(chip);
+ u32 mask_tri = (offset) ?
+ MCP25XXFD_IOCON_TRIS1 : MCP25XXFD_IOCON_TRIS0;
+ u32 mask_stby = (offset) ?
+ 0 : MCP25XXFD_IOCON_XSTBYEN;
+ u32 mask_pm = (offset) ?
+ MCP25XXFD_IOCON_PM1 : MCP25XXFD_IOCON_PM0;
+
+ /* only handle gpio 0/1 */
+ if (offset > 1)
+ return -EINVAL;
+
+ /* set the mask */
+ priv->regs.iocon |= mask_tri | mask_pm;
+
+ /* clear stby */
+ priv->regs.iocon &= ~mask_stby;
+
+ return mcp25xxfd_cmd_write_mask(priv->spi, MCP25XXFD_IOCON,
+ priv->regs.iocon,
+ mask_tri | mask_stby | mask_pm,
+ priv->spi_setup_speed_hz);
+}
+
+static int mcp25xxfd_gpio_direction_output(struct gpio_chip *chip,
+ unsigned int offset, int value)
+{
+ struct mcp25xxfd_priv *priv = gpiochip_get_data(chip);
+ u32 mask_tri = (offset) ?
+ MCP25XXFD_IOCON_TRIS1 : MCP25XXFD_IOCON_TRIS0;
+ u32 mask_lat = (offset) ?
+ MCP25XXFD_IOCON_LAT1 : MCP25XXFD_IOCON_LAT0;
+ u32 mask_pm = (offset) ?
+ MCP25XXFD_IOCON_PM1 : MCP25XXFD_IOCON_PM0;
+ u32 mask_stby = (offset) ?
+ 0 : MCP25XXFD_IOCON_XSTBYEN;
+
+ /* only handle gpio 0/1 */
+ if (offset > 1)
+ return -EINVAL;
+
+ /* clear the tristate bit and also clear stby */
+ priv->regs.iocon &= ~(mask_tri | mask_stby);
+
+ /* set GPIO mode */
+ priv->regs.iocon |= mask_pm;
+
+ /* set the value */
+ if (value)
+ priv->regs.iocon |= mask_lat;
+ else
+ priv->regs.iocon &= ~mask_lat;
+
+ return mcp25xxfd_cmd_write_mask(priv->spi, MCP25XXFD_IOCON,
+ priv->regs.iocon,
+ mask_tri | mask_lat |
+ mask_pm | mask_stby,
+ priv->spi_setup_speed_hz);
+}
+
+static int mcp25xxfd_gpio_setup(struct spi_device *spi)
+{
+ struct mcp25xxfd_priv *priv = spi_get_drvdata(spi);
+
+ /* gpiochip only handles GPIO0 and GPIO1 */
+ priv->gpio.owner = THIS_MODULE;
+ priv->gpio.parent = &spi->dev;
+ priv->gpio.label = dev_name(&spi->dev);
+ priv->gpio.direction_input = mcp25xxfd_gpio_direction_input;
+ priv->gpio.get = mcp25xxfd_gpio_get;
+ priv->gpio.direction_output = mcp25xxfd_gpio_direction_output;
+ priv->gpio.set = mcp25xxfd_gpio_set;
+ priv->gpio.request = mcp25xxfd_gpio_request;
+ priv->gpio.free = mcp25xxfd_gpio_free;
+ priv->gpio.base = -1;
+ priv->gpio.ngpio = 2;
+ priv->gpio.can_sleep = 1;
+
+ return devm_gpiochip_add_data(&spi->dev, &priv->gpio, priv);
+}
+
+#else
+
+static int mcp25xxfd_gpio_setup(struct spi_device *spi)
+{
+ return 0;
+}
+
+#endif
+
+/* ideally these would be defined in uapi/linux/can.h */
+#define CAN_EFF_SID_SHIFT (CAN_EFF_ID_BITS - CAN_SFF_ID_BITS)
+#define CAN_EFF_SID_BITS CAN_SFF_ID_BITS
+#define CAN_EFF_SID_MASK \
+ GENMASK(CAN_EFF_SID_SHIFT + CAN_EFF_SID_BITS - 1, \
+ CAN_EFF_SID_SHIFT)
+#define CAN_EFF_EID_SHIFT 0
+#define CAN_EFF_EID_BITS CAN_EFF_SID_SHIFT
+#define CAN_EFF_EID_MASK \
+ GENMASK(CAN_EFF_EID_SHIFT + CAN_EFF_EID_BITS - 1, \
+ CAN_EFF_EID_SHIFT)
+
+static void mcp25xxfd_canid_to_mcpid(u32 can_id, u32 *id, u32 *flags)
+{
+ if (can_id & CAN_EFF_FLAG) {
+ int sid = (can_id & CAN_EFF_SID_MASK) >> CAN_EFF_SID_SHIFT;
+ int eid = (can_id & CAN_EFF_EID_MASK) >> CAN_EFF_EID_SHIFT;
+ *id = (eid << CAN_OBJ_ID_EID_SHIFT) |
+ (sid << CAN_OBJ_ID_SID_SHIFT);
+ *flags = CAN_OBJ_FLAGS_IDE;
+ } else {
+ *id = can_id & CAN_SFF_MASK;
+ *flags = 0;
+ }
+
+ *flags |= (can_id & CAN_RTR_FLAG) ? CAN_OBJ_FLAGS_RTR : 0;
+}
+
+static void mcp25xxfd_mcpid_to_canid(u32 mcpid, u32 mcpflags, u32 *id)
+{
+ u32 sid = (mcpid & CAN_OBJ_ID_SID_MASK) >> CAN_OBJ_ID_SID_SHIFT;
+ u32 eid = (mcpid & CAN_OBJ_ID_EID_MASK) >> CAN_OBJ_ID_EID_SHIFT;
+
+ if (mcpflags & CAN_OBJ_FLAGS_IDE) {
+ *id = (eid << CAN_EFF_EID_SHIFT) |
+ (sid << CAN_EFF_SID_SHIFT) |
+ CAN_EFF_FLAG;
+ } else {
+ *id = sid;
+ }
+
+ *id |= (mcpflags & CAN_OBJ_FLAGS_RTR) ? CAN_RTR_FLAG : 0;
+}
+
+static void __mcp25xxfd_stop_queue(struct net_device *net,
+ unsigned int id)
+{
+ struct mcp25xxfd_priv *priv = netdev_priv(net);
+
+ if (priv->tx_queue_status >= TX_QUEUE_STATUS_STOPPED)
+ dev_warn(&priv->spi->dev,
+ "tx-queue is already stopped by: %i\n",
+ priv->tx_queue_status);
+
+ priv->tx_queue_status = id ? id : TX_QUEUE_STATUS_STOPPED;
+ netif_stop_queue(priv->net);
+}
+
+/* helper to identify who is stopping the queue by line number */
+#define mcp25xxfd_stop_queue(spi) \
+ __mcp25xxfd_stop_queue(spi, __LINE__)
+
+static void mcp25xxfd_wake_queue(struct spi_device *spi)
+{
+ struct mcp25xxfd_priv *priv = spi_get_drvdata(spi);
+
+ /* nothing should be left pending /in flight now... */
+ priv->fifos.tx_pending_mask = 0;
+ priv->fifos.tx_submitted_mask = 0;
+ priv->fifos.tx_processed_mask = 0;
+ priv->tx_queue_status = TX_QUEUE_STATUS_RUNNING;
+
+ /* wake queue now */
+ netif_wake_queue(priv->net);
+}
+
+/* CAN transmit related*/
+
+static void mcp25xxfd_mark_tx_pending(void *context)
+{
+ struct mcp25xxfd_trigger_tx_message *txm = context;
+ struct spi_device *spi = txm->msg.spi;
+ struct mcp25xxfd_priv *priv = spi_get_drvdata(spi);
+
+ /* only here or in the irq handler this value is changed,
+ * so there is no race condition and it does not require locking
+ * serialization happens via spi_pump_message
+ */
+ priv->fifos.tx_pending_mask |= BIT(txm->fifo);
+}
+
+static int mcp25xxfd_fill_spi_transmit_fifos(struct mcp25xxfd_priv *priv)
+{
+ struct mcp25xxfd_trigger_tx_message *txm;
+ int i, fifo;
+ const u32 trigger = CAN_FIFOCON_TXREQ | CAN_FIFOCON_UINC;
+ const int first_byte = mcp25xxfd_first_byte(trigger);
+ u32 fifo_address;
+
+ priv->spi_transmit_fifos = kcalloc(priv->fifos.tx_fifos,
+ sizeof(*priv->spi_transmit_fifos),
+ GFP_KERNEL | GFP_DMA);
+ if (!priv->spi_transmit_fifos)
+ return -ENOMEM;
+
+ for (i = 0; i < priv->fifos.tx_fifos; i++) {
+ fifo = priv->fifos.tx_fifo_start + i;
+ txm = &priv->spi_transmit_fifos[i];
+ fifo_address = priv->fifos.fifo_address[fifo];
+ /* prepare the message */
+ spi_message_init(&txm->msg);
+ txm->msg.complete = mcp25xxfd_mark_tx_pending;
+ txm->msg.context = txm;
+ txm->fifo = fifo;
+ /* the payload itself */
+ txm->fill_xfer.speed_hz = priv->spi_speed_hz;
+ txm->fill_xfer.tx_buf = txm->fill_cmd;
+ txm->fill_xfer.len = 2;
+ txm->fill_xfer.cs_change = true;
+ mcp25xxfd_calc_cmd_addr(INSTRUCTION_WRITE,
+ FIFO_DATA(fifo_address),
+ txm->fill_cmd);
+ spi_message_add_tail(&txm->fill_xfer, &txm->msg);
+ /* the trigger command */
+ txm->trigger_xfer.speed_hz = priv->spi_speed_hz;
+ txm->trigger_xfer.tx_buf = txm->trigger_cmd;
+ txm->trigger_xfer.len = 3;
+ mcp25xxfd_calc_cmd_addr(INSTRUCTION_WRITE,
+ CAN_FIFOCON(fifo) + first_byte,
+ txm->trigger_cmd);
+ txm->trigger_data = trigger >> (8 * first_byte);
+ spi_message_add_tail(&txm->trigger_xfer, &txm->msg);
+ }
+
+ return 0;
+}
+
+static int mcp25xxfd_transmit_message_common(struct spi_device *spi,
+ int fifo,
+ struct mcp25xxfd_obj_tx *obj,
+ int len,
+ u8 *data)
+{
+ struct mcp25xxfd_priv *priv = spi_get_drvdata(spi);
+ struct mcp25xxfd_trigger_tx_message *txm =
+ &priv->spi_transmit_fifos[fifo - priv->fifos.tx_fifo_start];
+ int ret;
+
+ /* add fifo as seq */
+ obj->header.flags |= fifo << CAN_OBJ_FLAGS_SEQ_SHIFT;
+
+ /* transform to le32 */
+ mcp25xxfd_obj_to_le(&obj->header);
+
+ /* fill in details */
+ memcpy(txm->fill_obj, obj, sizeof(struct mcp25xxfd_obj_tx));
+ memset(txm->fill_data, 0, priv->fifos.payload_size);
+ memcpy(txm->fill_data, data, len);
+
+ /* transfers to FIFO RAM has to be multiple of 4 */
+ txm->fill_xfer.len =
+ 2 + sizeof(struct mcp25xxfd_obj_tx) + ALIGN(len, 4);
+
+ /* and transmit asyncroniously */
+ ret = spi_async(spi, &txm->msg);
+ if (ret)
+ return NETDEV_TX_BUSY;
+
+ return NETDEV_TX_OK;
+}
+
+static int mcp25xxfd_transmit_fdmessage(struct spi_device *spi, int fifo,
+ struct canfd_frame *frame)
+{
+ struct mcp25xxfd_priv *priv = spi_get_drvdata(spi);
+ struct mcp25xxfd_obj_tx obj;
+ int dlc = can_len2dlc(frame->len);
+ u32 flags;
+
+ frame->len = can_dlc2len(dlc);
+
+ mcp25xxfd_canid_to_mcpid(frame->can_id, &obj.header.id, &flags);
+
+ flags |= dlc << CAN_OBJ_FLAGS_DLC_SHIFT;
+ flags |= (frame->can_id & CAN_EFF_FLAG) ? CAN_OBJ_FLAGS_IDE : 0;
+ flags |= (frame->can_id & CAN_RTR_FLAG) ? CAN_OBJ_FLAGS_RTR : 0;
+ if (frame->flags & CANFD_BRS) {
+ flags |= CAN_OBJ_FLAGS_BRS;
+ priv->stats.tx_brs_count++;
+ }
+ flags |= (frame->flags & CANFD_ESI) ? CAN_OBJ_FLAGS_ESI : 0;
+ flags |= CAN_OBJ_FLAGS_FDF;
+
+ priv->stats.tx_fd_count++;
+ priv->stats.tx_dlc_usage[dlc]++;
+
+ obj.header.flags = flags;
+
+ return mcp25xxfd_transmit_message_common(spi, fifo, &obj,
+ frame->len, frame->data);
+}
+
+static int mcp25xxfd_transmit_message(struct spi_device *spi, int fifo,
+ struct can_frame *frame)
+{
+ struct mcp25xxfd_priv *priv = spi_get_drvdata(spi);
+ struct mcp25xxfd_obj_tx obj;
+ u32 flags;
+
+ if (frame->can_dlc > 8)
+ frame->can_dlc = 8;
+
+ priv->stats.tx_dlc_usage[frame->can_dlc]++;
+
+ mcp25xxfd_canid_to_mcpid(frame->can_id, &obj.header.id, &flags);
+
+ flags |= frame->can_dlc << CAN_OBJ_FLAGS_DLC_SHIFT;
+ flags |= (frame->can_id & CAN_EFF_FLAG) ? CAN_OBJ_FLAGS_IDE : 0;
+ flags |= (frame->can_id & CAN_RTR_FLAG) ? CAN_OBJ_FLAGS_RTR : 0;
+
+ obj.header.flags = flags;
+
+ return mcp25xxfd_transmit_message_common(spi, fifo, &obj,
+ frame->can_dlc, frame->data);
+}
+
+static bool mcp25xxfd_is_last_txfifo(struct spi_device *spi,
+ int fifo)
+{
+ struct mcp25xxfd_priv *priv = spi_get_drvdata(spi);
+
+ return (fifo ==
+ (priv->fifos.tx_fifo_start + priv->fifos.tx_fifos - 1));
+}
+
+static netdev_tx_t mcp25xxfd_start_xmit(struct sk_buff *skb,
+ struct net_device *net)
+{
+ struct mcp25xxfd_priv *priv = netdev_priv(net);
+ struct spi_device *spi = priv->spi;
+ u32 pending_mask;
+ int fifo;
+ int ret;
+
+ if (can_dropped_invalid_skb(net, skb))
+ return NETDEV_TX_OK;
+
+ if (priv->can.state == CAN_STATE_BUS_OFF) {
+ mcp25xxfd_stop_queue(priv->net);
+ return NETDEV_TX_BUSY;
+ }
+
+ /* get effective mask */
+ pending_mask = priv->fifos.tx_pending_mask |
+ priv->fifos.tx_submitted_mask;
+
+ /* decide on fifo to assign */
+ if (pending_mask)
+ fifo = fls(pending_mask);
+ else
+ fifo = priv->fifos.tx_fifo_start;
+
+ /* handle error - this should not happen... */
+ if (fifo >= priv->fifos.tx_fifo_start + priv->fifos.tx_fifos) {
+ dev_err(&spi->dev,
+ "reached tx-fifo %i, which is not valid\n",
+ fifo);
+ return NETDEV_TX_BUSY;
+ }
+
+ /* if we are the last one, then stop the queue */
+ if (mcp25xxfd_is_last_txfifo(spi, fifo))
+ mcp25xxfd_stop_queue(priv->net);
+
+ /* mark as submitted */
+ priv->fifos.tx_submitted_mask |= BIT(fifo);
+ priv->stats.fifo_usage[fifo]++;
+
+ /* now process it for real */
+ if (can_is_canfd_skb(skb))
+ ret = mcp25xxfd_transmit_fdmessage(spi, fifo,
+ (struct canfd_frame *)
+ skb->data);
+ else
+ ret = mcp25xxfd_transmit_message(spi, fifo,
+ (struct can_frame *)
+ skb->data);
+
+ /* keep it for reference until the message really got transmitted */
+ if (ret == NETDEV_TX_OK)
+ can_put_echo_skb(skb, priv->net, fifo);
+
+ return ret;
+}
+
+/* CAN RX Related */
+
+static int mcp25xxfd_can_transform_rx_fd(struct spi_device *spi,
+ struct mcp25xxfd_obj_rx *rx)
+{
+ struct mcp25xxfd_priv *priv = spi_get_drvdata(spi);
+ struct canfd_frame *frame;
+ struct sk_buff *skb;
+ u32 flags = rx->header.flags;
+ int dlc;
+
+ /* allocate the skb buffer */
+ skb = alloc_canfd_skb(priv->net, &frame);
+ if (!skb) {
+ dev_err(&spi->dev, "cannot allocate RX skb\n");
+ priv->net->stats.rx_dropped++;
+ return -ENOMEM;
+ }
+
+ mcp25xxfd_mcpid_to_canid(rx->header.id, flags, &frame->can_id);
+ frame->flags |= (flags & CAN_OBJ_FLAGS_BRS) ? CANFD_BRS : 0;
+ frame->flags |= (flags & CAN_OBJ_FLAGS_ESI) ? CANFD_ESI : 0;
+
+ dlc = (flags & CAN_OBJ_FLAGS_DLC_MASK) >> CAN_OBJ_FLAGS_DLC_SHIFT;
+ frame->len = can_dlc2len(dlc);
+
+ memcpy(frame->data, rx->data, frame->len);
+
+ priv->stats.rx_fd_count++;
+ priv->net->stats.rx_packets++;
+ priv->net->stats.rx_bytes += frame->len;
+ if (rx->header.flags & CAN_OBJ_FLAGS_BRS)
+ priv->stats.rx_brs_count++;
+ priv->stats.rx_dlc_usage[dlc]++;
+
+ can_led_event(priv->net, CAN_LED_EVENT_RX);
+
+ netif_rx_ni(skb);
+
+ return 0;
+}
+
+static int mcp25xxfd_can_transform_rx_normal(struct spi_device *spi,
+ struct mcp25xxfd_obj_rx *rx)
+{
+ struct mcp25xxfd_priv *priv = spi_get_drvdata(spi);
+ struct sk_buff *skb;
+ struct can_frame *frame;
+ u32 flags = rx->header.flags;
+ int len;
+ int dlc;
+
+ /* allocate the skb buffer */
+ skb = alloc_can_skb(priv->net, &frame);
+ if (!skb) {
+ dev_err(&spi->dev, "cannot allocate RX skb\n");
+ priv->net->stats.rx_dropped++;
+ return -ENOMEM;
+ }
+
+ mcp25xxfd_mcpid_to_canid(rx->header.id, flags, &frame->can_id);
+
+ dlc = (flags & CAN_OBJ_FLAGS_DLC_MASK) >> CAN_OBJ_FLAGS_DLC_SHIFT;
+ frame->can_dlc = dlc;
+
+ len = can_dlc2len(frame->can_dlc);
+
+ memcpy(frame->data, rx->data, len);
+
+ priv->net->stats.rx_packets++;
+ priv->net->stats.rx_bytes += len;
+ priv->stats.rx_dlc_usage[dlc]++;
+
+ can_led_event(priv->net, CAN_LED_EVENT_RX);
+
+ netif_rx_ni(skb);
+
+ return 0;
+}
+
+static int mcp25xxfd_process_queued_rx(struct spi_device *spi,
+ struct mcp25xxfd_obj_ts *obj)
+{
+ struct mcp25xxfd_obj_rx *rx = container_of(obj,
+ struct mcp25xxfd_obj_rx,
+ header);
+
+ if (obj->flags & CAN_OBJ_FLAGS_FDF)
+ return mcp25xxfd_can_transform_rx_fd(spi, rx);
+ else
+ return mcp25xxfd_can_transform_rx_normal(spi, rx);
+}
+
+static int mcp25xxfd_normal_release_fifos(struct spi_device *spi,
+ int start, int end)
+{
+ struct mcp25xxfd_priv *priv = spi_get_drvdata(spi);
+ int ret;
+
+ /* release each fifo in a separate transfer */
+ for (; start < end ; start++) {
+ ret = mcp25xxfd_cmd_write_mask(spi, CAN_FIFOCON(start),
+ CAN_FIFOCON_UINC,
+ CAN_FIFOCON_UINC,
+ priv->spi_speed_hz);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+/* unfortunately the CAN_FIFOCON are not directly consecutive
+ * so the optimization of "clearing all in one spi_transfer"
+ * would produce an overhead of 11 unnecessary bytes/fifo
+ * - transferring 14 (2 cmd + 12 data) bytes
+ * instead of just 3 (2 + 1).
+ * On some slower systems this may still be beneficial,
+ * but it is not good enough for the generic case.
+ * On a Raspberry Pi CM the timings for clearing 3 fifos
+ * (at 12.5MHz SPI clock speed) are:
+ * * normal:
+ * * 3 spi transfers
+ * * 9 bytes total
+ * * 36.74us from first CS low to last CS high
+ * * individual CS: 9.14us, 5.74us and 5.16us
+ * * 77.02us from CS up of fifo transfer to last release CS up
+ * * bulk:
+ * * 1 spi transfer
+ * * 27 bytes total
+ * * 29.06us CS Low
+ * * 78.28us from CS up of fifo transfer to last release CS up
+ * this obviously varies with SPI_clock speed
+ * - the slower the clock the less efficient the optimization.
+ * similarly the faster the CPU (and bigger the code cache) the
+ * less effcient the optimization - the above case is border line.
+ */
+
+#define FIFOCON_SPACING (CAN_FIFOCON(1) - CAN_FIFOCON(0))
+#define FIFOCON_SPACINGW (FIFOCON_SPACING / sizeof(u32))
+
+static int mcp25xxfd_bulk_release_fifos(struct spi_device *spi,
+ int start, int end)
+{
+ struct mcp25xxfd_priv *priv = spi_get_drvdata(spi);
+ int i;
+ int ret;
+
+ /* calculate start address and length */
+ int fifos = end - start;
+ int first_byte = mcp25xxfd_first_byte(CAN_FIFOCON_UINC);
+ int addr = CAN_FIFOCON(start);
+ int len = 1 + (fifos - 1) * FIFOCON_SPACING;
+
+ /* the worsted case buffer */
+ u32 buf[32 * FIFOCON_SPACINGW], base;
+
+ base = (priv->fifos.payload_mode << CAN_FIFOCON_PLSIZE_SHIFT) |
+ ((priv->fifos.rx_fifo_depth - 1) << CAN_FIFOCON_FSIZE_SHIFT) |
+ CAN_FIFOCON_RXTSEN | /* RX timestamps */
+ CAN_FIFOCON_UINC |
+ CAN_FIFOCON_TFERFFIE | /* FIFO Full */
+ CAN_FIFOCON_TFHRFHIE | /* FIFO Half Full*/
+ CAN_FIFOCON_TFNRFNIE; /* FIFO not empty */
+
+ memset(buf, 0, sizeof(buf));
+ for (i = 0; i < end - start ; i++) {
+ if (i == priv->fifos.rx_fifos - 1)
+ base |= CAN_FIFOCON_RXOVIE;
+ buf[FIFOCON_SPACINGW * i] = cpu_to_le32(base);
+ }
+
+ ret = mcp25xxfd_cmd_writen(spi, addr + first_byte,
+ (u8 *)buf + first_byte,
+ len,
+ priv->spi_speed_hz);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+/* queued FIFO handling for release to system */
+
+static void mcp25xxfd_clear_queued_fifos(struct spi_device *spi)
+{
+ struct mcp25xxfd_priv *priv = spi_get_drvdata(spi);
+
+ /* prepare rfi - mostly used for sorting */
+ priv->queued_fifos.rx_count = 0;
+}
+
+static void mcp25xxfd_addto_queued_fifos(struct spi_device *spi,
+ struct mcp25xxfd_obj_ts *obj)
+{
+ struct mcp25xxfd_priv *priv = spi_get_drvdata(spi);
+ struct mcp25xxfd_read_fifo_info *rfi = &priv->queued_fifos;
+
+ /* timestamps must ignore the highest byte, so we shift it,
+ * so that it still compares correctly
+ */
+ obj->ts <<= 8;
+
+ /* add pointer to queued array-list */
+ rfi->rxb[rfi->rx_count] = obj;
+ rfi->rx_count++;
+}
+
+static int mcp25xxfd_process_queued_tef(struct spi_device *spi,
+ struct mcp25xxfd_obj_ts *obj)
+{
+ struct mcp25xxfd_priv *priv = spi_get_drvdata(spi);
+ struct mcp25xxfd_obj_tef *tef = container_of(obj,
+ struct mcp25xxfd_obj_tef,
+ header);
+ int dlc = (obj->flags & CAN_OBJ_FLAGS_DLC_MASK)
+ >> CAN_OBJ_FLAGS_DLC_SHIFT;
+ int fifo = (tef->header.flags & CAN_OBJ_FLAGS_SEQ_MASK) >>
+ CAN_OBJ_FLAGS_SEQ_SHIFT;
+
+ /* update counters */
+ priv->net->stats.tx_packets++;
+ priv->net->stats.tx_bytes += can_dlc2len(dlc);
+ if (obj->flags & CAN_OBJ_FLAGS_FDF)
+ priv->stats.tx_fd_count++;
+ if (obj->flags & CAN_OBJ_FLAGS_BRS)
+ priv->stats.tx_brs_count++;
+ priv->stats.tx_dlc_usage[dlc]++;
+
+ /* release it */
+ can_get_echo_skb(priv->net, fifo);
+
+ can_led_event(priv->net, CAN_LED_EVENT_TX);
+
+ return 0;
+}
+
+static int mcp25xxfd_compare_obj_ts(const void *a, const void *b)
+{
+ const struct mcp25xxfd_obj_ts * const *rxa = a;
+ const struct mcp25xxfd_obj_ts * const *rxb = b;
+ /* using signed here to handle rollover correctly */
+ s32 ats = (*rxa)->ts;
+ s32 bts = (*rxb)->ts;
+
+ if (ats < bts)
+ return -EINVAL;
+ if (ats > bts)
+ return 1;
+ return 0;
+}
+
+static int mcp25xxfd_process_queued_fifos(struct spi_device *spi)
+{
+ struct mcp25xxfd_priv *priv = spi_get_drvdata(spi);
+ struct mcp25xxfd_read_fifo_info *rfi = &priv->queued_fifos;
+ int i;
+ int ret;
+
+ /* sort the fifos (rx and TEF) by receive timestamp */
+ sort(rfi->rxb, rfi->rx_count, sizeof(struct mcp25xxfd_obj_ts *),
+ mcp25xxfd_compare_obj_ts, NULL);
+
+ /* process the recived fifos */
+ for (i = 0; i < rfi->rx_count ; i++) {
+ if (rfi->rxb[i]->flags & CAN_OBJ_FLAGS_CUSTOM_ISTEF)
+ ret = mcp25xxfd_process_queued_tef(spi, rfi->rxb[i]);
+ else
+ ret = mcp25xxfd_process_queued_rx(spi, rfi->rxb[i]);
+ if (ret)
+ return ret;
+ }
+
+ /* clear queued fifos */
+ mcp25xxfd_clear_queued_fifos(spi);
+
+ return 0;
+}
+
+static int mcp25xxfd_transform_rx(struct spi_device *spi,
+ struct mcp25xxfd_obj_rx *rx)
+{
+ int dlc;
+
+ /* transform the data to system byte order */
+ mcp25xxfd_obj_ts_from_le(&rx->header);
+
+ /* add the object to the list */
+ mcp25xxfd_addto_queued_fifos(spi, &rx->header);
+
+ /* calc length and return it */
+ dlc = (rx->header.flags & CAN_OBJ_FLAGS_DLC_MASK)
+ >> CAN_OBJ_FLAGS_DLC_SHIFT;
+ return can_dlc2len(dlc);
+}
+
+/* read_fifo implementations
+ *
+ * read_fifos is a simple implementation, that:
+ * * loops all fifos
+ * * read header + some data-bytes (8)
+ * * read rest of data-bytes bytes
+ * * release fifo
+ * for 3 can frames dlc<=8 to read here we have:
+ * * 6 spi transfers
+ * * 75 bytes (= 3 * (2 + 12 + 8) bytes + 3 * 3 bytes)
+ * for 3 canfd frames dlc>8 to read here we have:
+ * * 9 spi transfers
+ * * 81 (= 3 * (2 + 12 + 8 + 2) bytes + 3 * 3 bytes) + 3 * extra payload
+ * this only transfers the required size of bytes on the spi bus.
+ *
+ * bulk_read_fifos is an optimization that is most practical for
+ * Can2.0 busses, but may also be practical for CanFD busses that
+ * have a high average payload data size.
+ *
+ * It will read all of the fifo data in a single spi_transfer:
+ * * read all fifos in one go (as long as these are ajacent to each other)
+ * * loop all fifos
+ * * release fifo
+ * for 3 can2.0 frames to read here we have:
+ * * 4 spi transfers
+ * * 71 bytes (= 2 + 3 * (12 + 8) bytes + 3 * 3 bytes)
+ * for 3 canfd frames to read here we have:
+ * * 4 spi transfers
+ * * 230 bytes (= 2 + 3 * (12 + 64) bytes)
+ * obviously this reads way too many bytes for framesizes <=32 bytes,
+ * but it avoids the overhead on the CPU side and may even trigger
+ * DMA transfers due to the high byte count, which release CPU cycles.
+ *
+ * This optimization will also be efficient for cases where a high
+ * percentage of canFD frames has a dlc-size > 8.
+ * This mode is used for Can2.0 configured busses.
+ *
+ * For now this option can get forced for CanFD via a module parameter.
+ * In the future there may be some heuristics that could trigger a usage
+ * of this mode as well in some circumstances.
+ *
+ * Note: there is a second optimization for release fifo as well,
+ * but it is not as efficient as this optimization for the
+ * non-CanFD case - see mcp25xxfd_bulk_release_fifos
+ */
+
+static int mcp25xxfd_read_fifos(struct spi_device *spi)
+{
+ struct mcp25xxfd_priv *priv = spi_get_drvdata(spi);
+ int fifo_header_size = sizeof(struct mcp25xxfd_obj_rx);
+ int fifo_min_payload_size = 8;
+ int fifo_min_size = fifo_header_size + fifo_min_payload_size;
+ int fifo_max_payload_size =
+ ((priv->can.ctrlmode & CAN_CTRLMODE_FD) ? 64 : 8);
+ u32 mask = priv->status.rxif;
+ struct mcp25xxfd_obj_rx *rx;
+ int i, len;
+ int ret;
+ u32 fifo_address;
+ u8 *data;
+
+ /* read all the "open" segments in big chunks */
+ for (i = priv->fifos.rx_fifo_start + priv->fifos.rx_fifos - 1;
+ i >= priv->fifos.rx_fifo_start;
+ i--) {
+ if (!(mask & BIT(i)))
+ continue;
+ /* the fifo to fill */
+ rx = (struct mcp25xxfd_obj_rx *)
+ (priv->fifos.fifo_data + priv->fifos.fifo_address[i]);
+ /* read the minimal payload */
+ fifo_address = priv->fifos.fifo_address[i];
+ ret = mcp25xxfd_cmd_readn(spi,
+ FIFO_DATA(fifo_address),
+ rx,
+ fifo_min_size,
+ priv->spi_speed_hz);
+ if (ret)
+ return ret;
+ /* process fifo stats and get length */
+ len = min_t(int, mcp25xxfd_transform_rx(spi, rx),
+ fifo_max_payload_size);
+
+ /* read extra payload if needed */
+ if (len > fifo_min_payload_size) {
+ data = &rx->data[fifo_min_payload_size];
+ ret = mcp25xxfd_cmd_readn(spi,
+ FIFO_DATA(fifo_address +
+ fifo_min_size),
+ data,
+ len - fifo_min_payload_size,
+ priv->spi_speed_hz);
+ if (ret)
+ return ret;
+ }
+ /* release fifo */
+ ret = mcp25xxfd_normal_release_fifos(spi, i, i + 1);
+ if (ret)
+ return ret;
+ /* increment fifo_usage */
+ priv->stats.fifo_usage[i]++;
+ }
+
+ return 0;
+}
+
+static int mcp25xxfd_bulk_read_fifo_range(struct spi_device *spi,
+ int start, int end)
+{
+ struct mcp25xxfd_priv *priv = spi_get_drvdata(spi);
+ const int fifo_header_size = sizeof(struct mcp25xxfd_obj_rx);
+ const int fifo_max_payload_size = priv->fifos.payload_size;
+ const int fifo_max_size = fifo_header_size + fifo_max_payload_size;
+ struct mcp25xxfd_obj_rx *rx;
+ int i;
+ int ret;
+
+ /* now we got start and end, so read the range */
+ ret = mcp25xxfd_cmd_readn(spi,
+ FIFO_DATA(priv->fifos.fifo_address[start]),
+ priv->fifos.fifo_data +
+ priv->fifos.fifo_address[start],
+ (end - start) * fifo_max_size,
+ priv->spi_speed_hz);
+ if (ret)
+ return ret;
+
+ /* clear all the fifos in range */
+ if (use_bulk_release_fifos)
+ ret = mcp25xxfd_bulk_release_fifos(spi, start, end);
+ else
+ ret = mcp25xxfd_normal_release_fifos(spi, start, end);
+ if (ret)
+ return ret;
+
+ /* preprocess data */
+ for (i = start; i < end ; i++) {
+ /* store the fifo to process */
+ rx = (struct mcp25xxfd_obj_rx *)
+ (priv->fifos.fifo_data + priv->fifos.fifo_address[i]);
+ /* process fifo stats */
+ mcp25xxfd_transform_rx(spi, rx);
+ /* increment usage */
+ priv->stats.fifo_usage[i]++;
+ }
+
+ return 0;
+}
+
+static int mcp25xxfd_bulk_read_fifos(struct spi_device *spi)
+{
+ struct mcp25xxfd_priv *priv = spi_get_drvdata(spi);
+ u32 mask = priv->status.rxif;
+ int i, start, end;
+ int ret;
+
+ /* find blocks of set bits top down */
+ for (i = priv->fifos.rx_fifo_start + priv->fifos.rx_fifos - 1;
+ mask && (i >= priv->fifos.rx_fifo_start);
+ i--) {
+ /* if the bit is 0 then continue loop to find a 1 */
+ if ((mask & BIT(i)) == 0)
+ continue;
+
+ /* so we found a non-0 bit - this is start and end */
+ start = i;
+ end = i;
+
+ /* find the first bit set */
+ for (; mask & BIT(i); i--) {
+ mask &= ~BIT(i);
+ start = i;
+ }
+
+ /* now process that range */
+ ret = mcp25xxfd_bulk_read_fifo_range(spi, start, end + 1);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+static int mcp25xxfd_can_ist_handle_rxif(struct spi_device *spi)
+{
+ struct mcp25xxfd_priv *priv = spi_get_drvdata(spi);
+ u32 mask = priv->status.rxif;
+ int ret;
+
+ if (!mask)
+ return 0;
+
+ /* read all the fifos - for non-fd case use bulk read optimization */
+ if (((priv->can.ctrlmode & CAN_CTRLMODE_FD) == 0) ||
+ use_complete_fdfifo_read)
+ ret = mcp25xxfd_bulk_read_fifos(spi);
+ else
+ ret = mcp25xxfd_read_fifos(spi);
+
+ return 0;
+}
+
+static void mcp25xxfd_mark_tx_processed(struct spi_device *spi,
+ int fifo)
+{
+ struct mcp25xxfd_priv *priv = spi_get_drvdata(spi);
+
+ /* set mask */
+ priv->fifos.tx_processed_mask |= BIT(fifo);
+
+ /* check if we should reenable the TX-queue */
+ if (mcp25xxfd_is_last_txfifo(spi, fifo))
+ priv->tx_queue_status = TX_QUEUE_STATUS_NEEDS_START;
+}
+
+static int mcp25xxfd_can_ist_handle_tefif_handle_single(struct spi_device *spi)
+{
+ struct mcp25xxfd_priv *priv = spi_get_drvdata(spi);
+ struct mcp25xxfd_obj_tef *tef;
+ int fifo;
+ int ret;
+
+ /* calc address in address space */
+ tef = (struct mcp25xxfd_obj_tef *)(priv->fifos.fifo_data +
+ priv->fifos.tef_address);
+
+ /* read all the object data */
+ ret = mcp25xxfd_cmd_readn(spi,
+ FIFO_DATA(priv->fifos.tef_address),
+ tef,
+ /* we do not read the last byte of the ts
+ * to avoid MAB issiues
+ */
+ sizeof(*tef) - 1,
+ priv->spi_speed_hz);
+ /* increment the counter to read next */
+ ret = mcp25xxfd_cmd_write_mask(spi,
+ CAN_TEFCON,
+ CAN_TEFCON_UINC,
+ CAN_TEFCON_UINC,
+ priv->spi_speed_hz);
+
+ /* transform the data to system byte order */
+ mcp25xxfd_obj_ts_from_le(&tef->header);
+
+ fifo = (tef->header.flags & CAN_OBJ_FLAGS_SEQ_MASK) >>
+ CAN_OBJ_FLAGS_SEQ_SHIFT;
+
+ /* submit to queue */
+ tef->header.flags |= CAN_OBJ_FLAGS_CUSTOM_ISTEF;
+ mcp25xxfd_addto_queued_fifos(spi, &tef->header);
+
+ /* increment tef_address with rollover */
+ priv->fifos.tef_address += sizeof(*tef);
+ if (priv->fifos.tef_address > priv->fifos.tef_address_end)
+ priv->fifos.tef_address =
+ priv->fifos.tef_address_start;
+
+ /* and mark as processed right now */
+ mcp25xxfd_mark_tx_processed(spi, fifo);
+
+ return 0;
+}
+
+static int mcp25xxfd_can_ist_handle_tefif_conservative(struct spi_device *spi)
+{
+ struct mcp25xxfd_priv *priv = spi_get_drvdata(spi);
+ u32 val[2];
+ int ret;
+
+ while (1) {
+ /* get the current TEFSTA and TEFUA */
+ ret = mcp25xxfd_cmd_readn(priv->spi,
+ CAN_TEFSTA,
+ val,
+ 8,
+ priv->spi_speed_hz);
+ if (ret)
+ return ret;
+ mcp25xxfd_convert_to_cpu(val, 2);
+
+ /* check for interrupt flags */
+ if (!(val[0] & CAN_TEFSTA_TEFNEIF))
+ return 0;
+
+ if (priv->fifos.tef_address != val[1]) {
+ dev_err(&spi->dev,
+ "TEF Address mismatch - read: %04x calculated: %04x\n",
+ val[1], priv->fifos.tef_address);
+ priv->fifos.tef_address = val[1];
+ }
+
+ ret = mcp25xxfd_can_ist_handle_tefif_handle_single(spi);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+static int mcp25xxfd_can_ist_handle_tefif_count(struct spi_device *spi,
+ int count)
+{
+ int i;
+ int ret;
+
+ /* now clear TEF for each */
+ /* TODO: optimize for BULK reads, as we (hopefully) know COUNT */
+ for (i = 0; i < count; i++) {
+ /* handle a single TEF */
+ ret = mcp25xxfd_can_ist_handle_tefif_handle_single(spi);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+static int mcp25xxfd_can_ist_handle_tefif(struct spi_device *spi)
+{
+ struct mcp25xxfd_priv *priv = spi_get_drvdata(spi);
+ u32 pending = priv->fifos.tx_pending_mask_in_irq &
+ (~priv->fifos.tx_processed_mask);
+ int count;
+
+ /* calculate the number of fifos that have been processed */
+ count = hweight_long(pending);
+ count -= hweight_long(priv->status.txreq & pending);
+
+ /* in case of unexpected results handle "safely" */
+ if (count <= 0)
+ return mcp25xxfd_can_ist_handle_tefif_conservative(spi);
+
+ return mcp25xxfd_can_ist_handle_tefif_count(spi, count);
+}
+
+static int mcp25xxfd_can_ist_handle_txatif_fifo(struct spi_device *spi,
+ int fifo)
+{
+ struct mcp25xxfd_priv *priv = spi_get_drvdata(spi);
+ u32 val;
+ int ret;
+
+ /* read fifo status */
+ ret = mcp25xxfd_cmd_read(spi,
+ CAN_FIFOSTA(fifo),
+ &val,
+ priv->spi_speed_hz);
+ if (ret)
+ return ret;
+
+ /* clear the relevant interrupt flags */
+ ret = mcp25xxfd_cmd_write_mask(spi,
+ CAN_FIFOSTA(fifo),
+ 0,
+ CAN_FIFOSTA_TXABT |
+ CAN_FIFOSTA_TXLARB |
+ CAN_FIFOSTA_TXERR |
+ CAN_FIFOSTA_TXATIF,
+ priv->spi_speed_hz);
+
+ /* for specific cases we could trigger a retransmit
+ * instead of an abort.
+ */
+
+ /* and we release it from the echo_skb buffer
+ * NOTE: this is one place where packet delivery will not
+ * be ordered, as we do not have any timing information
+ * when this occurred
+ */
+ can_get_echo_skb(priv->net, fifo);
+
+ /* but we need to run a bit of cleanup */
+ priv->status.txif &= ~BIT(fifo);
+ priv->net->stats.tx_aborted_errors++;
+
+ /* mark the fifo as processed */
+ mcp25xxfd_mark_tx_processed(spi, fifo);
+
+ /* handle all the known cases accordingly - ignoring FIFO full */
+ val &= CAN_FIFOSTA_TXABT |
+ CAN_FIFOSTA_TXLARB |
+ CAN_FIFOSTA_TXERR;
+ switch (val) {
+ case CAN_FIFOSTA_TXERR:
+ break;
+ default:
+ dev_warn_ratelimited(&spi->dev,
+ "Unknown TX-Fifo abort condition: %08x - stopping tx-queue\n",
+ val);
+ break;
+ }
+
+ return 0;
+}
+
+static int mcp25xxfd_can_ist_handle_txatif(struct spi_device *spi)
+{
+ struct mcp25xxfd_priv *priv = spi_get_drvdata(spi);
+ int i, fifo;
+ int ret;
+
+ /* process all the fifos with that flag set */
+ for (i = 0, fifo = priv->fifos.tx_fifo_start;
+ i < priv->fifos.tx_fifos; i++, fifo++) {
+ if (priv->status.txatif & BIT(fifo)) {
+ ret = mcp25xxfd_can_ist_handle_txatif_fifo(spi, fifo);
+ if (ret)
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+static void mcp25xxfd_error_skb(struct net_device *net)
+{
+ struct mcp25xxfd_priv *priv = netdev_priv(net);
+ struct sk_buff *skb;
+ struct can_frame *frame;
+
+ skb = alloc_can_err_skb(net, &frame);
+ if (skb) {
+ frame->can_id = priv->can_err_id;
+ memcpy(frame->data, priv->can_err_data, 8);
+ netif_rx_ni(skb);
+ } else {
+ netdev_err(net, "cannot allocate error skb\n");
+ }
+}
+
+static int mcp25xxfd_can_ist_handle_rxovif(struct spi_device *spi)
+{
+ struct mcp25xxfd_priv *priv = spi_get_drvdata(spi);
+ u32 mask = priv->status.rxovif;
+ int i;
+ int ret;
+
+ /* clear all fifos that have an overflow bit set */
+ for (i = 0; i < 32; i++) {
+ if (mask & BIT(i)) {
+ ret = mcp25xxfd_cmd_write_mask(spi,
+ CAN_FIFOSTA(i),
+ 0,
+ CAN_FIFOSTA_RXOVIF,
+ priv->spi_speed_hz);
+ if (ret)
+ return ret;
+ /* update statistics */
+ priv->net->stats.rx_over_errors++;
+ priv->net->stats.rx_errors++;
+ priv->stats.rx_overflow++;
+ priv->can_err_id |= CAN_ERR_CRTL;
+ priv->can_err_data[1] |= CAN_ERR_CRTL_RX_OVERFLOW;
+ }
+ }
+
+ return 0;
+}
+
+static int mcp25xxfd_can_ist_handle_modif(struct spi_device *spi)
+{
+ struct mcp25xxfd_priv *priv = spi_get_drvdata(spi);
+ int omode = priv->active_can_mode;
+ int mode;
+ int ret;
+
+ /* Note that this irq does not get triggered in all situations
+ * for example SERRIF will move to RESTICTED or LISTENONLY
+ * but MODIF will not be raised!
+ */
+
+ /* get the mode */
+ ret = mcp25xxfd_get_opmode(spi, &mode, priv->spi_speed_hz);
+ if (ret)
+ return ret;
+
+ /* if we are restricted, then return to "normal" mode */
+ if (mode == CAN_CON_MODE_RESTRICTED)
+ return mcp25xxfd_set_normal_opmode(spi);
+
+ /* the controller itself will transition to sleep, so we ignore it */
+ if (mode == CAN_CON_MODE_SLEEP)
+ return 0;
+
+ /* switches to the same mode as before are also ignored
+ * - this typically happens if the driver is shortly
+ * switching to a different mode and then returning to the
+ * original mode
+ */
+ if (mode == omode)
+ return 0;
+
+ /* these we need to handle correctly, so warn and give context */
+ dev_warn(&spi->dev,
+ "Controller unexpectedly switched from mode %s(%u) to %s(%u)\n",
+ mcp25xxfd_mode_names[omode], omode,
+ mcp25xxfd_mode_names[mode], mode);
+
+ return 0;
+}
+
+static int mcp25xxfd_can_ist_handle_cerrif(struct spi_device *spi)
+{
+ struct mcp25xxfd_priv *priv = spi_get_drvdata(spi);
+
+ /* in principle we could also delay reading bdiag registers
+ * until we get here - it would add some extra delay in the
+ * error case, but be slightly faster in the "normal" case.
+ * slightly faster would be saving 8 bytes of spi transfer.
+ */
+
+ dev_err_ratelimited(&spi->dev, "CAN Bus error\n");
+ priv->can_err_id |= CAN_ERR_BUSERROR;
+
+ if (priv->status.bdiag1 &
+ (CAN_BDIAG1_DBIT0ERR | CAN_BDIAG1_NBIT0ERR)) {
+ priv->can_err_id |= CAN_ERR_BUSERROR;
+ priv->can_err_data[2] |= CAN_ERR_PROT_BIT0;
+ priv->bdiag1_clear_mask |= CAN_BDIAG1_DBIT0ERR |
+ CAN_BDIAG1_NBIT0ERR;
+ }
+ if (priv->status.bdiag1 &
+ (CAN_BDIAG1_DBIT1ERR | CAN_BDIAG1_NBIT1ERR)) {
+ priv->can_err_id |= CAN_ERR_BUSERROR;
+ priv->can_err_data[2] |= CAN_ERR_PROT_BIT1;
+ priv->bdiag1_clear_mask |= CAN_BDIAG1_DBIT1ERR |
+ CAN_BDIAG1_NBIT1ERR;
+ }
+ if (priv->status.bdiag1 &
+ (CAN_BDIAG1_DSTUFERR | CAN_BDIAG1_NSTUFERR)) {
+ priv->can_err_id |= CAN_ERR_BUSERROR;
+ priv->can_err_data[2] |= CAN_ERR_PROT_STUFF;
+ priv->bdiag1_clear_mask |= CAN_BDIAG1_DSTUFERR |
+ CAN_BDIAG1_NSTUFERR;
+ }
+ if (priv->status.bdiag1 &
+ (CAN_BDIAG1_DFORMERR | CAN_BDIAG1_NFORMERR)) {
+ priv->can_err_id |= CAN_ERR_BUSERROR;
+ priv->can_err_data[2] |= CAN_ERR_PROT_FORM;
+ priv->bdiag1_clear_mask |= CAN_BDIAG1_DFORMERR |
+ CAN_BDIAG1_NFORMERR;
+ }
+ if (priv->status.bdiag1 & CAN_BDIAG1_NACKERR) {
+ priv->can_err_id |= CAN_ERR_ACK;
+ priv->bdiag1_clear_mask |= CAN_BDIAG1_NACKERR;
+ }
+
+ return 0;
+}
+
+static int mcp25xxfd_can_ist_handle_eccif(struct spi_device *spi)
+{
+ struct mcp25xxfd_priv *priv = spi_get_drvdata(spi);
+ int ret;
+ u32 val;
+ u32 addr;
+
+ priv->can_err_id |= CAN_ERR_CRTL;
+ priv->can_err_data[1] |= CAN_ERR_CRTL_UNSPEC;
+
+ /* read ECC status register */
+ ret = mcp25xxfd_cmd_read(spi, MCP25XXFD_ECCSTAT, &val,
+ priv->spi_speed_hz);
+ if (ret)
+ return ret;
+
+ addr = (val & MCP25XXFD_ECCSTAT_ERRADDR_MASK) >>
+ MCP25XXFD_ECCSTAT_ERRADDR_SHIFT;
+
+ dev_err_ratelimited(&spi->dev,
+ "ECC %s bit error at %03x\n",
+ (val & MCP25XXFD_ECCSTAT_DEDIF) ?
+ "double" : "single",
+ addr);
+
+ return mcp25xxfd_cmd_write(spi, MCP25XXFD_ECCSTAT, 0,
+ priv->spi_speed_hz);
+}
+
+static int mcp25xxfd_can_ist_handle_serrif_txmab(struct spi_device *spi)
+{
+ struct mcp25xxfd_priv *priv = spi_get_drvdata(spi);
+
+ priv->net->stats.tx_fifo_errors++;
+ priv->net->stats.tx_errors++;
+ priv->stats.tx_mab++;
+
+ return 0;
+}
+
+static int mcp25xxfd_can_ist_handle_serrif_rxmab(struct spi_device *spi)
+{
+ struct mcp25xxfd_priv *priv = spi_get_drvdata(spi);
+
+ priv->net->stats.rx_dropped++;
+ priv->net->stats.rx_errors++;
+ priv->stats.rx_mab++;
+
+ return 0;
+}
+
+static int mcp25xxfd_can_ist_handle_serrif(struct spi_device *spi)
+{
+ struct mcp25xxfd_priv *priv = spi_get_drvdata(spi);
+ u32 clear;
+ int ret;
+
+ /* clear some interrupts immediately,
+ * so that we get notified if they happen again
+ */
+ clear = CAN_INT_SERRIF | CAN_INT_MODIF | CAN_INT_IVMIF;
+ ret = mcp25xxfd_cmd_write_mask(spi, CAN_INT,
+ priv->status.intf & (~clear),
+ clear,
+ priv->spi_speed_hz);
+ if (ret)
+ return ret;
+
+ /* Errors here are:
+ * * Bus Bandwidth Error: when a RX Message Assembly Buffer
+ * is still full when the next message has already arrived
+ * the recived message shall be ignored
+ * * TX MAB Underflow: when a TX Message is invalid
+ * due to ECC errors or TXMAB underflow
+ * in this situatioon the system will transition to
+ * Restricted or Listen Only mode
+ */
+
+ priv->can_err_id |= CAN_ERR_CRTL;
+ priv->can_err_data[1] |= CAN_ERR_CRTL_UNSPEC;
+
+ /* a mode change + invalid message would indicate
+ * TX MAB Underflow
+ */
+ if ((priv->status.intf & CAN_INT_MODIF) &&
+ (priv->status.intf & CAN_INT_IVMIF)) {
+ return mcp25xxfd_can_ist_handle_serrif_txmab(spi);
+ }
+
+ /* for RX there is only the RXIF an indicator
+ * - surprizingly RX-MAB does not change mode or anything
+ */
+ if (priv->status.intf & CAN_INT_RXIF)
+ return mcp25xxfd_can_ist_handle_serrif_rxmab(spi);
+
+ /* the final case */
+ dev_warn_ratelimited(&spi->dev,
+ "unidentified system error - intf = %08x\n",
+ priv->status.intf);
+
+ return 0;
+}
+
+static int mcp25xxfd_can_ist_handle_ivmif(struct spi_device *spi)
+{
+ struct mcp25xxfd_priv *priv = spi_get_drvdata(spi);
+
+ /* if we have a systemerror as well, then ignore it */
+ if (priv->status.intf & CAN_INT_SERRIF)
+ return 0;
+
+ /* otherwise it is an RX issue, so account for it here */
+ priv->can_err_id |= CAN_ERR_PROT;
+ priv->can_err_data[2] |= CAN_ERR_PROT_FORM;
+ priv->net->stats.rx_frame_errors++;
+ priv->net->stats.rx_errors++;
+
+ return 0;
+}
+
+static int mcp25xxfd_disable_interrupts(struct spi_device *spi,
+ u32 speed_hz)
+{
+ struct mcp25xxfd_priv *priv = spi_get_drvdata(spi);
+
+ priv->status.intf = 0;
+ return mcp25xxfd_cmd_write(spi, CAN_INT, 0, speed_hz);
+}
+
+static int mcp25xxfd_enable_interrupts(struct spi_device *spi,
+ u32 speed_hz)
+{
+ struct mcp25xxfd_priv *priv = spi_get_drvdata(spi);
+
+ priv->status.intf = CAN_INT_TEFIE |
+ CAN_INT_RXIE |
+ CAN_INT_MODIE |
+ CAN_INT_SERRIE |
+ CAN_INT_IVMIE |
+ CAN_INT_CERRIE |
+ CAN_INT_RXOVIE |
+ CAN_INT_ECCIE;
+ return mcp25xxfd_cmd_write(spi, CAN_INT,
+ priv->status.intf,
+ speed_hz);
+}
+
+static int mcp25xxfd_hw_wake(struct spi_device *spi)
+{
+ return mcp25xxfd_start_clock(spi, MCP25XXFD_CLK_USER_CAN);
+}
+
+static void mcp25xxfd_hw_sleep(struct spi_device *spi)
+{
+ struct mcp25xxfd_priv *priv = spi_get_drvdata(spi);
+
+ /* disable interrupts */
+ mcp25xxfd_disable_interrupts(spi, priv->spi_setup_speed_hz);
+
+ /* stop the clocks */
+ mcp25xxfd_stop_clock(spi, MCP25XXFD_CLK_USER_CAN);
+}
+
+static int mcp25xxfd_can_ist_handle_status(struct spi_device *spi)
+{
+ struct mcp25xxfd_priv *priv = spi_get_drvdata(spi);
+ const u32 clear_irq = CAN_INT_TBCIF |
+ CAN_INT_MODIF |
+ CAN_INT_SERRIF |
+ CAN_INT_CERRIF |
+ CAN_INT_WAKIF |
+ CAN_INT_IVMIF;
+ int ret;
+
+ /* clear all the interrupts asap */
+ ret = mcp25xxfd_cmd_write_mask(spi, CAN_INT,
+ priv->status.intf & (~clear_irq),
+ clear_irq,
+ priv->spi_speed_hz);
+ if (ret)
+ return ret;
+
+ /* interrupt clearing info */
+ priv->bdiag1_clear_value = 0;
+ priv->bdiag1_clear_mask = 0;
+ priv->can_err_id = 0;
+ memset(priv->can_err_data, 0, 8);
+
+ /* state changes */
+ priv->new_state = priv->can.state;
+
+ /* clear queued fifos */
+ mcp25xxfd_clear_queued_fifos(spi);
+
+ /* system error interrupt needs to get handled first
+ * to get us out of restricted mode
+ */
+ if (priv->status.intf & CAN_INT_SERRIF) {
+ priv->stats.int_serr_count++;
+ ret = mcp25xxfd_can_ist_handle_serrif(spi);
+ if (ret)
+ return ret;
+ }
+
+ /* mode change interrupt */
+ if (priv->status.intf & CAN_INT_MODIF) {
+ priv->stats.int_mod_count++;
+ ret = mcp25xxfd_can_ist_handle_modif(spi);
+ if (ret)
+ return ret;
+ }
+
+ /* handle the rx */
+ if (priv->status.intf & CAN_INT_RXIF) {
+ priv->stats.int_rx_count++;
+ ret = mcp25xxfd_can_ist_handle_rxif(spi);
+ if (ret)
+ return ret;
+ }
+
+ /* handle aborted TX FIFOs */
+ if (priv->status.txatif) {
+ priv->stats.int_txat_count++;
+ ret = mcp25xxfd_can_ist_handle_txatif(spi);
+ if (ret)
+ return ret;
+ }
+
+ /* handle the tef */
+ if (priv->status.intf & CAN_INT_TEFIF) {
+ priv->stats.int_tef_count++;
+ ret = mcp25xxfd_can_ist_handle_tefif(spi);
+ if (ret)
+ return ret;
+ }
+
+ /* process the queued fifos */
+ ret = mcp25xxfd_process_queued_fifos(spi);
+
+ /* handle error interrupt flags */
+ if (priv->status.rxovif) {
+ priv->stats.int_rxov_count++;
+ ret = mcp25xxfd_can_ist_handle_rxovif(spi);
+ if (ret)
+ return ret;
+ }
+
+ /* sram ECC error interrupt */
+ if (priv->status.intf & CAN_INT_ECCIF) {
+ priv->stats.int_ecc_count++;
+ ret = mcp25xxfd_can_ist_handle_eccif(spi);
+ if (ret)
+ return ret;
+ }
+
+ /* message format interrupt */
+ if (priv->status.intf & CAN_INT_IVMIF) {
+ priv->stats.int_ivm_count++;
+ ret = mcp25xxfd_can_ist_handle_ivmif(spi);
+ if (ret)
+ return ret;
+ }
+
+ /* handle bus errors in more detail */
+ if (priv->status.intf & CAN_INT_CERRIF) {
+ priv->stats.int_cerr_count++;
+ ret = mcp25xxfd_can_ist_handle_cerrif(spi);
+ if (ret)
+ return ret;
+ }
+
+ /* Error counter handling */
+ if (priv->status.trec & CAN_TREC_TXWARN) {
+ priv->new_state = CAN_STATE_ERROR_WARNING;
+ priv->can_err_id |= CAN_ERR_CRTL;
+ priv->can_err_data[1] |= CAN_ERR_CRTL_TX_WARNING;
+ }
+ if (priv->status.trec & CAN_TREC_RXWARN) {
+ priv->new_state = CAN_STATE_ERROR_WARNING;
+ priv->can_err_id |= CAN_ERR_CRTL;
+ priv->can_err_data[1] |= CAN_ERR_CRTL_RX_WARNING;
+ }
+ if (priv->status.trec & CAN_TREC_TXBP) {
+ priv->new_state = CAN_STATE_ERROR_PASSIVE;
+ priv->can_err_id |= CAN_ERR_CRTL;
+ priv->can_err_data[1] |= CAN_ERR_CRTL_TX_PASSIVE;
+ }
+ if (priv->status.trec & CAN_TREC_RXBP) {
+ priv->new_state = CAN_STATE_ERROR_PASSIVE;
+ priv->can_err_id |= CAN_ERR_CRTL;
+ priv->can_err_data[1] |= CAN_ERR_CRTL_RX_PASSIVE;
+ }
+ if (priv->status.trec & CAN_TREC_TXBO) {
+ priv->new_state = CAN_STATE_BUS_OFF;
+ priv->can_err_id |= CAN_ERR_BUSOFF;
+ }
+
+ /* based on the last state state check the new state */
+ switch (priv->can.state) {
+ case CAN_STATE_ERROR_ACTIVE:
+ if (priv->new_state >= CAN_STATE_ERROR_WARNING &&
+ priv->new_state <= CAN_STATE_BUS_OFF)
+ priv->can.can_stats.error_warning++;
+ /* fallthrough */
+ case CAN_STATE_ERROR_WARNING:
+ if (priv->new_state >= CAN_STATE_ERROR_PASSIVE &&
+ priv->new_state <= CAN_STATE_BUS_OFF)
+ priv->can.can_stats.error_passive++;
+ break;
+ default:
+ break;
+ }
+ priv->can.state = priv->new_state;
+
+ /* and send error packet */
+ if (priv->can_err_id)
+ mcp25xxfd_error_skb(priv->net);
+
+ /* handle BUS OFF */
+ if (priv->can.state == CAN_STATE_BUS_OFF) {
+ if (priv->can.restart_ms == 0) {
+ mcp25xxfd_stop_queue(priv->net);
+ priv->force_quit = 1;
+ priv->can.can_stats.bus_off++;
+ can_bus_off(priv->net);
+ mcp25xxfd_hw_sleep(spi);
+ }
+ } else {
+ /* restart the tx queue if needed */
+ if (priv->fifos.tx_processed_mask == priv->fifos.tx_fifo_mask)
+ mcp25xxfd_wake_queue(spi);
+ }
+
+ /* clear bdiag flags */
+ if (priv->bdiag1_clear_mask) {
+ ret = mcp25xxfd_cmd_write_mask(spi,
+ CAN_BDIAG1,
+ priv->bdiag1_clear_value,
+ priv->bdiag1_clear_mask,
+ priv->spi_speed_hz);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+static irqreturn_t mcp25xxfd_can_ist(int irq, void *dev_id)
+{
+ struct mcp25xxfd_priv *priv = dev_id;
+ struct spi_device *spi = priv->spi;
+ int ret;
+
+ priv->stats.irq_calls++;
+ priv->stats.irq_state = IRQ_STATE_RUNNING;
+
+ while (!priv->force_quit) {
+ /* count irq loops */
+ priv->stats.irq_loops++;
+
+ /* copy pending to in_irq - any
+ * updates that happen asyncronously
+ * are not taken into account here
+ */
+ priv->fifos.tx_pending_mask_in_irq =
+ priv->fifos.tx_pending_mask;
+
+ /* read interrupt status flags */
+ ret = mcp25xxfd_cmd_readn(spi, CAN_INT,
+ &priv->status,
+ sizeof(priv->status),
+ priv->spi_speed_hz);
+ if (ret)
+ return ret;
+
+ /* only act if the mask is applied */
+ if ((priv->status.intf &
+ (priv->status.intf >> CAN_INT_IE_SHIFT)) == 0)
+ break;
+
+ /* handle the status */
+ ret = mcp25xxfd_can_ist_handle_status(spi);
+ if (ret)
+ return ret;
+ }
+
+ return IRQ_HANDLED;
+}
+
+static int mcp25xxfd_get_berr_counter(const struct net_device *net,
+ struct can_berr_counter *bec)
+{
+ struct mcp25xxfd_priv *priv = netdev_priv(net);
+
+ bec->txerr = (priv->status.trec & CAN_TREC_TEC_MASK) >>
+ CAN_TREC_TEC_SHIFT;
+ bec->rxerr = (priv->status.trec & CAN_TREC_REC_MASK) >>
+ CAN_TREC_REC_SHIFT;
+
+ return 0;
+}
+
+static int mcp25xxfd_power_enable(struct regulator *reg, int enable)
+{
+ return 0;
+}
+
+static int mcp25xxfd_do_set_mode(struct net_device *net, enum can_mode mode)
+{
+ switch (mode) {
+ case CAN_MODE_START:
+ break;
+ default:
+ return -EOPNOTSUPP;
+ }
+
+ return 0;
+}
+
+static int mcp25xxfd_do_set_nominal_bittiming(struct net_device *net)
+{
+ struct mcp25xxfd_priv *priv = netdev_priv(net);
+ struct can_bittiming *bt = &priv->can.bittiming;
+ struct spi_device *spi = priv->spi;
+
+ int sjw = bt->sjw;
+ int pseg2 = bt->phase_seg2;
+ int pseg1 = bt->phase_seg1;
+ int propseg = bt->prop_seg;
+ int brp = bt->brp;
+
+ int tseg1 = propseg + pseg1;
+ int tseg2 = pseg2;
+
+ /* calculate nominal bit timing */
+ priv->regs.nbtcfg = ((sjw - 1) << CAN_NBTCFG_SJW_SHIFT) |
+ ((tseg2 - 1) << CAN_NBTCFG_TSEG2_SHIFT) |
+ ((tseg1 - 1) << CAN_NBTCFG_TSEG1_SHIFT) |
+ ((brp - 1) << CAN_NBTCFG_BRP_SHIFT);
+
+ return mcp25xxfd_cmd_write(spi, CAN_NBTCFG,
+ priv->regs.nbtcfg,
+ priv->spi_setup_speed_hz);
+}
+
+static int mcp25xxfd_do_set_data_bittiming(struct net_device *net)
+{
+ struct mcp25xxfd_priv *priv = netdev_priv(net);
+ struct can_bittiming *bt = &priv->can.data_bittiming;
+ struct spi_device *spi = priv->spi;
+
+ int sjw = bt->sjw;
+ int pseg2 = bt->phase_seg2;
+ int pseg1 = bt->phase_seg1;
+ int propseg = bt->prop_seg;
+ int brp = bt->brp;
+
+ int tseg1 = propseg + pseg1;
+ int tseg2 = pseg2;
+
+ int ret;
+
+ /* set up Transmitter delay compensation */
+ if (!priv->regs.tdc)
+ priv->regs.tdc = CAN_TDC_EDGFLTEN |
+ (CAN_TDC_TDCMOD_AUTO << CAN_TDC_TDCMOD_SHIFT);
+ ret = mcp25xxfd_cmd_write(spi, CAN_TDC, priv->regs.tdc,
+ priv->spi_setup_speed_hz);
+ if (ret)
+ return ret;
+
+ /* calculate nominal bit timing */
+ priv->regs.dbtcfg = ((sjw - 1) << CAN_DBTCFG_SJW_SHIFT) |
+ ((tseg2 - 1) << CAN_DBTCFG_TSEG2_SHIFT) |
+ ((tseg1 - 1) << CAN_DBTCFG_TSEG1_SHIFT) |
+ ((brp - 1) << CAN_DBTCFG_BRP_SHIFT);
+
+ return mcp25xxfd_cmd_write(spi, CAN_DBTCFG,
+ priv->regs.dbtcfg,
+ priv->spi_setup_speed_hz);
+}
+
+static int mcp25xxfd_hw_probe(struct spi_device *spi)
+{
+ struct mcp25xxfd_priv *priv = spi_get_drvdata(spi);
+ int ret;
+
+ /* Wait for oscillator startup timer after power up */
+ msleep(MCP25XXFD_OST_DELAY_MS);
+
+ /* send a "blind" reset, hoping we are in Config mode */
+ mcp25xxfd_cmd_reset(spi, priv->spi_setup_speed_hz);
+
+ /* Wait for oscillator startup again */
+ msleep(MCP25XXFD_OST_DELAY_MS);
+
+ /* check clock register that the clock is ready or disabled */
+ ret = mcp25xxfd_cmd_read(spi, MCP25XXFD_OSC,
+ &priv->regs.osc,
+ priv->spi_setup_speed_hz);
+ if (ret)
+ return ret;
+
+ /* there can only be one... */
+ switch (priv->regs.osc &
+ (MCP25XXFD_OSC_OSCRDY | MCP25XXFD_OSC_OSCDIS)) {
+ case MCP25XXFD_OSC_OSCRDY: /* either the clock is ready */
+ break;
+ case MCP25XXFD_OSC_OSCDIS: /* or the clock is disabled */
+ /* wakeup sleeping system */
+ ret = mcp25xxfd_wake_from_sleep(spi);
+ if (ret)
+ return ret;
+ /* send a reset, hoping we are now in Config mode */
+ mcp25xxfd_cmd_reset(spi, priv->spi_setup_speed_hz);
+
+ /* Wait for oscillator startup again */
+ msleep(MCP25XXFD_OST_DELAY_MS);
+ break;
+ default:
+ /* otherwise there is no valid device (or in strange state)
+ *
+ * if PLL is enabled but not ready, then there may be
+ * something "fishy"
+ * this happened during driver development
+ * (enabling pll, when when on wrong clock), so best warn
+ * about such a possibility
+ */
+ if ((priv->regs.osc &
+ (MCP25XXFD_OSC_PLLEN | MCP25XXFD_OSC_PLLRDY))
+ == MCP25XXFD_OSC_PLLEN)
+ dev_err(&spi->dev,
+ "mcp25xxfd may be in a strange state - a power disconnect may be required\n");
+
+ return -ENODEV;
+ }
+
+ /* check if we are in config mode already*/
+
+ /* read CON register and match */
+ ret = mcp25xxfd_cmd_read(spi, CAN_CON,
+ &priv->regs.con,
+ priv->spi_setup_speed_hz);
+ if (ret)
+ return ret;
+
+ /* apply mask and check */
+ if ((priv->regs.con & CAN_CON_DEFAULT_MASK) == CAN_CON_DEFAULT) {
+ priv->active_can_mode = CAN_CON_MODE_CONFIG;
+ return 0;
+ }
+
+ /* as per datasheet a reset only works in Config Mode
+ * so as we have in principle no knowledge of the current
+ * mode that the controller is in we have no safe way
+ * to detect the device correctly
+ * hence we need to "blindly" put the controller into
+ * config mode.
+ * on the "save" side, the OSC reg has to be valid already,
+ * so there is a chance we got the controller...
+ */
+
+ /* blindly force it into config mode */
+ priv->regs.con = CAN_CON_DEFAULT;
+ priv->active_can_mode = CAN_CON_MODE_CONFIG;
+ ret = mcp25xxfd_cmd_write(spi, CAN_CON, CAN_CON_DEFAULT,
+ priv->spi_setup_speed_hz);
+ if (ret)
+ return ret;
+
+ /* delay some time */
+ msleep(MCP25XXFD_OST_DELAY_MS);
+
+ /* reset can controller */
+ mcp25xxfd_cmd_reset(spi, priv->spi_setup_speed_hz);
+
+ /* delay some time */
+ msleep(MCP25XXFD_OST_DELAY_MS);
+
+ /* read CON register and match a final time */
+ ret = mcp25xxfd_cmd_read(spi, CAN_CON,
+ &priv->regs.con,
+ priv->spi_setup_speed_hz);
+ if (ret)
+ return ret;
+
+ /* apply mask and check */
+ if ((priv->regs.con & CAN_CON_DEFAULT_MASK) != CAN_CON_DEFAULT)
+ return -ENODEV;
+
+ /* just in case: disable interrupts on controller */
+ return mcp25xxfd_disable_interrupts(spi,
+ priv->spi_setup_speed_hz);
+}
+
+static int mcp25xxfd_setup_osc(struct spi_device *spi)
+{
+ struct mcp25xxfd_priv *priv = spi_get_drvdata(spi);
+ int val = ((priv->config.clock_pll) ? MCP25XXFD_OSC_PLLEN : 0)
+ | ((priv->config.clock_div2) ? MCP25XXFD_OSC_SCLKDIV : 0);
+ int waitfor = ((priv->config.clock_pll) ? MCP25XXFD_OSC_PLLRDY : 0)
+ | ((priv->config.clock_div2) ? MCP25XXFD_OSC_SCLKRDY : 0)
+ | MCP25XXFD_OSC_OSCRDY;
+ int ret;
+ unsigned long timeout;
+
+ /* manage clock_out divider */
+ switch (priv->config.clock_odiv) {
+ case 10:
+ val |= (MCP25XXFD_OSC_CLKODIV_10)
+ << MCP25XXFD_OSC_CLKODIV_SHIFT;
+ break;
+ case 4:
+ val |= (MCP25XXFD_OSC_CLKODIV_4)
+ << MCP25XXFD_OSC_CLKODIV_SHIFT;
+ break;
+ case 2:
+ val |= (MCP25XXFD_OSC_CLKODIV_2)
+ << MCP25XXFD_OSC_CLKODIV_SHIFT;
+ break;
+ case 1:
+ val |= (MCP25XXFD_OSC_CLKODIV_1)
+ << MCP25XXFD_OSC_CLKODIV_SHIFT;
+ break;
+ case 0:
+ /* this means implicitly SOF output */
+ val |= (MCP25XXFD_OSC_CLKODIV_10)
+ << MCP25XXFD_OSC_CLKODIV_SHIFT;
+ break;
+ default:
+ dev_err(&spi->dev,
+ "Unsupported output clock divider %i\n",
+ priv->config.clock_odiv);
+ return -EINVAL;
+ }
+
+ /* write clock */
+ ret = mcp25xxfd_cmd_write(spi, MCP25XXFD_OSC, val,
+ priv->spi_setup_speed_hz);
+ if (ret)
+ return ret;
+
+ /* wait for synced pll/osc/sclk */
+ timeout = jiffies + MCP25XXFD_OSC_POLLING_JIFFIES;
+ while (time_before_eq(jiffies, timeout)) {
+ ret = mcp25xxfd_cmd_read(spi, MCP25XXFD_OSC,
+ &priv->regs.osc,
+ priv->spi_setup_speed_hz);
+ if (ret)
+ return ret;
+ if ((priv->regs.osc & waitfor) == waitfor)
+ return 0;
+ }
+
+ dev_err(&spi->dev,
+ "Clock did not lock within the timeout period\n");
+
+ /* we timed out */
+ return -ENODEV;
+}
+
+static int mcp25xxfd_setup_fifo(struct net_device *net,
+ struct mcp25xxfd_priv *priv,
+ struct spi_device *spi)
+{
+ u32 val, available_memory, tx_memory_used;
+ int ret;
+ int i, fifo;
+
+ /* clear all filter */
+ for (i = 0; i < 32; i++) {
+ ret = mcp25xxfd_cmd_write(spi, CAN_FLTOBJ(i), 0,
+ priv->spi_setup_speed_hz);
+ if (ret)
+ return ret;
+ ret = mcp25xxfd_cmd_write(spi, CAN_FLTMASK(i), 0,
+ priv->spi_setup_speed_hz);
+ if (ret)
+ return ret;
+ ret = mcp25xxfd_cmd_write_mask(spi, CAN_FLTCON(i), 0,
+ CAN_FILCON_MASK(i),
+ priv->spi_setup_speed_hz);
+ if (ret)
+ return ret;
+ }
+
+ /* decide on TEF, tx and rx FIFOS */
+ switch (net->mtu) {
+ case CAN_MTU:
+ /* note: if we have INT1 connected to a GPIO
+ * then we could handle this differently and more
+ * efficiently
+ */
+
+ /* mtu is 8 */
+ priv->fifos.payload_size = 8;
+ priv->fifos.payload_mode = CAN_TXQCON_PLSIZE_8;
+
+ /* 7 tx fifos starting at fifo 1 */
+ priv->fifos.tx_fifos = 7;
+
+ /* 24 rx fifos with 1 buffers/fifo */
+ priv->fifos.rx_fifo_depth = 1;
+
+ break;
+ case CANFD_MTU:
+ /* wish there was a way to have hw filters
+ * that can separate based on length ...
+ */
+ /* MTU is 64 */
+ priv->fifos.payload_size = 64;
+ priv->fifos.payload_mode = CAN_TXQCON_PLSIZE_64;
+
+ /* 7 tx fifos */
+ priv->fifos.tx_fifos = 7;
+
+ /* 19 rx fifos with 1 buffer/fifo */
+ priv->fifos.rx_fifo_depth = 1;
+
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* if defined as a module modify the number of tx_fifos */
+ if (tx_fifos) {
+ dev_info(&spi->dev,
+ "Using %i tx-fifos as per module parameter\n",
+ tx_fifos);
+ priv->fifos.tx_fifos = tx_fifos;
+ }
+
+ /* check range - we need 1 RX-fifo and one tef-fifo, hence 30 */
+ if (priv->fifos.tx_fifos > 30) {
+ dev_err(&spi->dev,
+ "There is an absolute maximum of 30 tx-fifos\n");
+ return -EINVAL;
+ }
+
+ tx_memory_used = priv->fifos.tx_fifos *
+ (sizeof(struct mcp25xxfd_obj_tef) +
+ sizeof(struct mcp25xxfd_obj_tx) +
+ priv->fifos.payload_size);
+ /* check that we are not exceeding memory limits with 1 RX buffer */
+ if (tx_memory_used + (sizeof(struct mcp25xxfd_obj_rx) +
+ priv->fifos.payload_size) > MCP25XXFD_BUFFER_TXRX_SIZE) {
+ dev_err(&spi->dev,
+ "Configured %i tx-fifos exceeds available memory already\n",
+ priv->fifos.tx_fifos);
+ return -EINVAL;
+ }
+
+ /* calculate possible amount of RX fifos */
+ available_memory = MCP25XXFD_BUFFER_TXRX_SIZE - tx_memory_used;
+
+ priv->fifos.rx_fifos = available_memory /
+ (sizeof(struct mcp25xxfd_obj_rx) +
+ priv->fifos.payload_size) /
+ priv->fifos.rx_fifo_depth;
+
+ /* we only support 31 FIFOS in total (TEF = FIFO0),
+ * so modify rx accordingly
+ */
+ if (priv->fifos.tx_fifos + priv->fifos.rx_fifos > 31)
+ priv->fifos.rx_fifos = 31 - priv->fifos.tx_fifos;
+
+ /* calculate effective memory used */
+ available_memory -= priv->fifos.rx_fifos *
+ (sizeof(struct mcp25xxfd_obj_rx) +
+ priv->fifos.payload_size) *
+ priv->fifos.rx_fifo_depth;
+
+ /* calcluate tef size */
+ priv->fifos.tef_fifos = priv->fifos.tx_fifos;
+ fifo = available_memory / sizeof(struct mcp25xxfd_obj_tef);
+ if (fifo > 0) {
+ priv->fifos.tef_fifos += fifo;
+ if (priv->fifos.tef_fifos > 32)
+ priv->fifos.tef_fifos = 32;
+ }
+
+ /* calculate rx/tx fifo start */
+ priv->fifos.rx_fifo_start = 1;
+ priv->fifos.tx_fifo_start =
+ priv->fifos.rx_fifo_start + priv->fifos.rx_fifos;
+
+ /* set up TEF SIZE to the number of tx_fifos and IRQ */
+ priv->regs.tefcon = CAN_TEFCON_FRESET |
+ CAN_TEFCON_TEFNEIE |
+ CAN_TEFCON_TEFTSEN |
+ ((priv->fifos.tef_fifos - 1) << CAN_TEFCON_FSIZE_SHIFT);
+
+ ret = mcp25xxfd_cmd_write(spi, CAN_TEFCON,
+ priv->regs.tefcon,
+ priv->spi_setup_speed_hz);
+ if (ret)
+ return ret;
+
+ /* set up tx fifos */
+ val = CAN_FIFOCON_TXEN |
+ CAN_FIFOCON_TXATIE | /* show up txatie flags in txatif reg */
+ CAN_FIFOCON_FRESET | /* reset FIFO */
+ (priv->fifos.payload_mode << CAN_FIFOCON_PLSIZE_SHIFT) |
+ (0 << CAN_FIFOCON_FSIZE_SHIFT); /* 1 FIFO only */
+
+ if (priv->can.ctrlmode & CAN_CTRLMODE_ONE_SHOT)
+ if (three_shot)
+ val |= CAN_FIFOCON_TXAT_THREE_SHOT <<
+ CAN_FIFOCON_TXAT_SHIFT;
+ else
+ val |= CAN_FIFOCON_TXAT_ONE_SHOT <<
+ CAN_FIFOCON_TXAT_SHIFT;
+ else
+ val |= CAN_FIFOCON_TXAT_UNLIMITED <<
+ CAN_FIFOCON_TXAT_SHIFT;
+
+ for (i = 0; i < priv->fifos.tx_fifos; i++) {
+ fifo = priv->fifos.tx_fifo_start + i;
+ ret = mcp25xxfd_cmd_write(spi, CAN_FIFOCON(fifo),
+ /* the prioriy needs to be inverted
+ * we need to run from lowest to
+ * highest to avoid MAB errors
+ */
+ val | ((31 - fifo) <<
+ CAN_FIFOCON_TXPRI_SHIFT),
+ priv->spi_setup_speed_hz);
+ if (ret)
+ return ret;
+ priv->fifos.tx_fifo_mask |= BIT(fifo);
+ }
+
+ /* now set up RX FIFO */
+ for (i = 0,
+ fifo = priv->fifos.rx_fifo_start + priv->fifos.rx_fifos - 1;
+ i < priv->fifos.rx_fifos; i++, fifo--) {
+ /* prepare the fifo itself */
+ ret = mcp25xxfd_cmd_write(spi, CAN_FIFOCON(fifo),
+ (priv->fifos.payload_mode <<
+ CAN_FIFOCON_PLSIZE_SHIFT) |
+ ((priv->fifos.rx_fifo_depth - 1) <<
+ CAN_FIFOCON_FSIZE_SHIFT) |
+ /* RX timestamps: */
+ CAN_FIFOCON_RXTSEN |
+ /* reset FIFO: */
+ CAN_FIFOCON_FRESET |
+ /* FIFO Full: */
+ CAN_FIFOCON_TFERFFIE |
+ /* FIFO Half Full: */
+ CAN_FIFOCON_TFHRFHIE |
+ /* FIFO not empty: */
+ CAN_FIFOCON_TFNRFNIE |
+ /* on last fifo add overflow flag: */
+ ((i == priv->fifos.rx_fifos - 1) ?
+ CAN_FIFOCON_RXOVIE : 0),
+ priv->spi_setup_speed_hz);
+ if (ret)
+ return ret;
+ /* prepare the rx filter config: filter i directs to fifo
+ * FLTMSK and FLTOBJ are 0 already, so they match everything
+ */
+ ret = mcp25xxfd_cmd_write_mask(spi, CAN_FLTCON(i),
+ CAN_FIFOCON_FLTEN(i) |
+ (fifo << CAN_FILCON_SHIFT(i)),
+ CAN_FIFOCON_FLTEN(i) |
+ CAN_FILCON_MASK(i),
+ priv->spi_setup_speed_hz);
+ if (ret)
+ return ret;
+
+ priv->fifos.rx_fifo_mask |= BIT(fifo);
+ }
+
+ /* we need to move out of CONFIG mode shortly to get the addresses */
+ ret = mcp25xxfd_set_opmode(spi, CAN_CON_MODE_INTERNAL_LOOPBACK,
+ priv->spi_setup_speed_hz);
+ if (ret)
+ return ret;
+
+ /* for the TEF fifo */
+ ret = mcp25xxfd_cmd_read(spi, CAN_TEFUA, &val,
+ priv->spi_setup_speed_hz);
+ if (ret)
+ return ret;
+ priv->fifos.tef_address = val;
+ priv->fifos.tef_address_start = val;
+ priv->fifos.tef_address_end = priv->fifos.tef_address_start +
+ priv->fifos.tef_fifos * sizeof(struct mcp25xxfd_obj_tef) -
+ 1;
+
+ /* get all the relevant addresses for the transmit fifos */
+ for (i = 0; i < priv->fifos.tx_fifos; i++) {
+ fifo = priv->fifos.tx_fifo_start + i;
+ ret = mcp25xxfd_cmd_read(spi, CAN_FIFOUA(fifo),
+ &val, priv->spi_setup_speed_hz);
+ if (ret)
+ return ret;
+ priv->fifos.fifo_address[fifo] = val;
+ }
+
+ /* and prepare the spi_messages */
+ ret = mcp25xxfd_fill_spi_transmit_fifos(priv);
+ if (ret)
+ return ret;
+
+ /* get all the relevant addresses for the rx fifos */
+ for (i = 0; i < priv->fifos.rx_fifos; i++) {
+ fifo = priv->fifos.rx_fifo_start + i;
+ ret = mcp25xxfd_cmd_read(spi, CAN_FIFOUA(fifo),
+ &val, priv->spi_setup_speed_hz);
+ if (ret)
+ return ret;
+ priv->fifos.fifo_address[fifo] = val;
+ }
+
+ /* now get back into config mode */
+ ret = mcp25xxfd_set_opmode(spi, CAN_CON_MODE_CONFIG,
+ priv->spi_setup_speed_hz);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+static int mcp25xxfd_setup(struct net_device *net,
+ struct mcp25xxfd_priv *priv,
+ struct spi_device *spi)
+{
+ int ret;
+
+ /* set up pll/clock if required */
+ ret = mcp25xxfd_setup_osc(spi);
+ if (ret)
+ return ret;
+
+ /* set up RAM ECC */
+ priv->regs.ecccon = MCP25XXFD_ECCCON_ECCEN |
+ MCP25XXFD_ECCCON_SECIE |
+ MCP25XXFD_ECCCON_DEDIE;
+ ret = mcp25xxfd_cmd_write(spi, MCP25XXFD_ECCCON,
+ priv->regs.ecccon,
+ priv->spi_setup_speed_hz);
+ if (ret)
+ return ret;
+
+ /* clean SRAM now that we have ECC enabled
+ * only this case it is clear that all RAM cels have
+ * valid ECC bits
+ */
+ ret = mcp25xxfd_clean_sram(spi, priv->spi_setup_speed_hz);
+ if (ret)
+ return ret;
+
+ /* time stamp control register - 1ns resolution, but disabled */
+ ret = mcp25xxfd_cmd_write(spi, CAN_TBC, 0,
+ priv->spi_setup_speed_hz);
+ if (ret)
+ return ret;
+ priv->regs.tscon = CAN_TSCON_TBCEN |
+ ((priv->can.clock.freq / 1000000)
+ << CAN_TSCON_TBCPRE_SHIFT);
+ ret = mcp25xxfd_cmd_write(spi, CAN_TSCON,
+ priv->regs.tscon,
+ priv->spi_setup_speed_hz);
+ if (ret)
+ return ret;
+
+ /* setup value of con_register */
+ priv->regs.con = CAN_CON_STEF /* enable TEF */;
+
+ /* transmission bandwidth sharing bits */
+ if (bw_sharing_log2bits > 12)
+ bw_sharing_log2bits = 12;
+ priv->regs.con |= bw_sharing_log2bits << CAN_CON_TXBWS_SHIFT;
+ /* non iso FD mode */
+ if (!(priv->can.ctrlmode & CAN_CTRLMODE_FD_NON_ISO))
+ priv->regs.con |= CAN_CON_ISOCRCEN;
+ /* one shot */
+ if (priv->can.ctrlmode & CAN_CTRLMODE_ONE_SHOT)
+ priv->regs.con |= CAN_CON_RTXAT;
+
+ /* and put us into default mode = CONFIG */
+ priv->regs.con |= (CAN_CON_MODE_CONFIG << CAN_CON_REQOP_SHIFT) |
+ (CAN_CON_MODE_CONFIG << CAN_CON_OPMOD_SHIFT);
+ /* apply it now - later we will only switch opsmodes... */
+ ret = mcp25xxfd_cmd_write(spi, CAN_CON,
+ priv->regs.con,
+ priv->spi_setup_speed_hz);
+
+ /* setup fifos - this also puts the system into sleep mode */
+ return mcp25xxfd_setup_fifo(net, priv, spi);
+}
+
+static int mcp25xxfd_open(struct net_device *net)
+{
+ struct mcp25xxfd_priv *priv = netdev_priv(net);
+ struct spi_device *spi = priv->spi;
+ int ret;
+
+ //pr_err("mcp25xxfd_open start\n");
+ ret = open_candev(net);
+ if (ret) {
+ dev_err(&spi->dev, "unable to set initial baudrate!\n");
+ return ret;
+ }
+
+ mcp25xxfd_gpio_direction_output(&priv->gpio, 0, 0);
+
+ mcp25xxfd_power_enable(priv->transceiver, 1);
+
+ priv->force_quit = 0;
+
+ /* clear those statistics */
+ memset(&priv->stats, 0, sizeof(priv->stats));
+
+ ret = request_threaded_irq(spi->irq, NULL,
+ mcp25xxfd_can_ist,
+ IRQF_ONESHOT | IRQF_TRIGGER_LOW,
+ DEVICE_NAME, priv);
+ if (ret) {
+ dev_err(&spi->dev, "failed to acquire irq %d - %i\n",
+ spi->irq, ret);
+ mcp25xxfd_power_enable(priv->transceiver, 0);
+ close_candev(net);
+ return ret;
+ }
+
+ /* wake from sleep if necessary */
+ ret = mcp25xxfd_hw_wake(spi);
+ if (ret)
+ goto open_clean;
+
+ ret = mcp25xxfd_setup(net, priv, spi);
+ if (ret)
+ goto open_clean;
+
+ mcp25xxfd_do_set_nominal_bittiming(net);
+ mcp25xxfd_do_set_data_bittiming(net);
+
+ ret = mcp25xxfd_set_normal_opmode(spi);
+ if (ret)
+ goto open_clean;
+ /* setting up default state */
+ priv->can.state = CAN_STATE_ERROR_ACTIVE;
+
+ /* only now enable the interrupt on the controller */
+ ret = mcp25xxfd_enable_interrupts(spi,
+ priv->spi_setup_speed_hz);
+ if (ret)
+ goto open_clean;
+
+ can_led_event(net, CAN_LED_EVENT_OPEN);
+
+ priv->tx_queue_status = TX_QUEUE_STATUS_RUNNING;
+ netif_wake_queue(net);
+
+ return 0;
+
+open_clean:
+ mcp25xxfd_disable_interrupts(spi, priv->spi_setup_speed_hz);
+ free_irq(spi->irq, priv);
+ mcp25xxfd_hw_sleep(spi);
+ mcp25xxfd_power_enable(priv->transceiver, 0);
+ close_candev(net);
+
+ return ret;
+}
+
+static void mcp25xxfd_clean(struct net_device *net)
+{
+ struct mcp25xxfd_priv *priv = netdev_priv(net);
+ int i;
+
+ for (i = 0; i < priv->fifos.tx_fifos; i++) {
+ if (priv->fifos.tx_pending_mask & BIT(i)) {
+ can_free_echo_skb(priv->net, 0);
+ priv->net->stats.tx_errors++;
+ }
+ }
+
+ priv->fifos.tx_pending_mask = 0;
+}
+
+static int mcp25xxfd_stop(struct net_device *net)
+{
+ struct mcp25xxfd_priv *priv = netdev_priv(net);
+ struct spi_device *spi = priv->spi;
+
+ close_candev(net);
+ mcp25xxfd_gpio_direction_output(&priv->gpio, 0, 1);
+ kfree(priv->spi_transmit_fifos);
+ priv->spi_transmit_fifos = NULL;
+
+ priv->force_quit = 1;
+ free_irq(spi->irq, priv);
+
+ /* Disable and clear pending interrupts */
+ mcp25xxfd_disable_interrupts(spi, priv->spi_setup_speed_hz);
+
+ mcp25xxfd_clean(net);
+
+ mcp25xxfd_hw_sleep(spi);
+
+ mcp25xxfd_power_enable(priv->transceiver, 0);
+
+ priv->can.state = CAN_STATE_STOPPED;
+
+ can_led_event(net, CAN_LED_EVENT_STOP);
+
+ return 0;
+}
+
+static const struct net_device_ops mcp25xxfd_netdev_ops = {
+ .ndo_open = mcp25xxfd_open,
+ .ndo_stop = mcp25xxfd_stop,
+ .ndo_start_xmit = mcp25xxfd_start_xmit,
+ .ndo_change_mtu = can_change_mtu,
+};
+
+static const struct of_device_id mcp25xxfd_of_match[] = {
+ {
+ .compatible = "microchip,mcp2517fd",
+ .data = (void *)CAN_MCP2517FD,
+ },
+ { }
+};
+MODULE_DEVICE_TABLE(of, mcp25xxfd_of_match);
+
+static const struct spi_device_id mcp25xxfd_id_table[] = {
+ {
+ .name = "mcp2517fd",
+ .driver_data = (kernel_ulong_t)CAN_MCP2517FD,
+ },
+ { }
+};
+MODULE_DEVICE_TABLE(spi, mcp25xxfd_id_table);
+
+static int mcp25xxfd_dump_regs(struct seq_file *file, void *offset)
+{
+ struct spi_device *spi = file->private;
+ struct mcp25xxfd_priv *priv = spi_get_drvdata(spi);
+ u32 data[CAN_TXQUA - CAN_CON + 4];
+ int i;
+ int count;
+ int ret;
+
+ count = (CAN_TXQUA - CAN_CON) / 4 + 1;
+ ret = mcp25xxfd_cmd_readn(spi, CAN_CON, data, 4 * count,
+ priv->spi_setup_speed_hz);
+ if (ret)
+ return ret;
+
+ mcp25xxfd_convert_to_cpu((u32 *)data, 4 * count);
+
+ for (i = 0; i < count; i++) {
+ seq_printf(file, "Reg 0x%03x = 0x%08x\n",
+ CAN_CON + 4 * i,
+ ((u32 *)data)[i]);
+ }
+
+ count = (MCP25XXFD_ECCSTAT - MCP25XXFD_OSC) / 4 + 1;
+ ret = mcp25xxfd_cmd_readn(spi, MCP25XXFD_OSC, data, 4 * count,
+ priv->spi_setup_speed_hz);
+ if (ret)
+ return ret;
+ mcp25xxfd_convert_to_cpu((u32 *)data, 4 * count);
+
+ for (i = 0; i < count; i++) {
+ seq_printf(file, "Reg 0x%03x = 0x%08x\n",
+ MCP25XXFD_OSC + 4 * i,
+ ((u32 *)data)[i]);
+ }
+
+ return 0;
+}
+
+#if defined(CONFIG_DEBUG_FS)
+static void mcp25xxfd_debugfs_add(struct mcp25xxfd_priv *priv)
+{
+ struct dentry *root, *fifousage, *fifoaddr, *rx, *tx, *status,
+ *regs, *stats, *rxdlc, *txdlc;
+ char name[32];
+ int i;
+
+ /* create the net device name */
+ snprintf(name, sizeof(name), DEVICE_NAME "-%s", priv->net->name);
+ priv->debugfs_dir = debugfs_create_dir(name, NULL);
+ root = priv->debugfs_dir;
+
+ rx = debugfs_create_dir("rx", root);
+ tx = debugfs_create_dir("tx", root);
+ fifoaddr = debugfs_create_dir("fifo_address", root);
+ status = debugfs_create_dir("status", root);
+ regs = debugfs_create_dir("regs", root);
+ stats = debugfs_create_dir("stats", root);
+ fifousage = debugfs_create_dir("fifo_usage", stats);
+ rxdlc = debugfs_create_dir("rx_dlc_usage", stats);
+ txdlc = debugfs_create_dir("tx_dlc_usage", stats);
+
+ /* add spi speed info */
+ debugfs_create_u32("spi_setup_speed_hz", 0444, root,
+ &priv->spi_setup_speed_hz);
+ debugfs_create_u32("spi_speed_hz", 0444, root,
+ &priv->spi_speed_hz);
+
+ /* add irq state info */
+ debugfs_create_u32("irq_state", 0444, root, &priv->stats.irq_state);
+
+ /* for the clock user mask */
+ debugfs_create_u32("clk_user_mask", 0444, root, &priv->clk_user_mask);
+
+ /* add fd statistics */
+ debugfs_create_u64("rx_fd_frames", 0444, stats,
+ &priv->stats.rx_fd_count);
+ debugfs_create_u64("tx_fd_frames", 0444, stats,
+ &priv->stats.tx_fd_count);
+ debugfs_create_u64("rx_brs_frames", 0444, stats,
+ &priv->stats.rx_brs_count);
+ debugfs_create_u64("tx_brs_frames", 0444, stats,
+ &priv->stats.tx_brs_count);
+
+ /* export the status structure */
+ debugfs_create_x32("intf", 0444, status, &priv->status.intf);
+ debugfs_create_x32("rx_if", 0444, status, &priv->status.rxif);
+ debugfs_create_x32("tx_if", 0444, status, &priv->status.txif);
+ debugfs_create_x32("rx_ovif", 0444, status, &priv->status.rxovif);
+ debugfs_create_x32("tx_atif", 0444, status, &priv->status.txatif);
+ debugfs_create_x32("tx_req", 0444, status, &priv->status.txreq);
+ debugfs_create_x32("trec", 0444, status, &priv->status.trec);
+ debugfs_create_x32("bdiag0", 0444, status, &priv->status.bdiag0);
+ debugfs_create_x32("bdiag1", 0444, status, &priv->status.bdiag1);
+
+ /* some configuration registers */
+ debugfs_create_x32("con", 0444, regs, &priv->regs.con);
+ debugfs_create_x32("ecccon", 0444, regs, &priv->regs.ecccon);
+ debugfs_create_x32("osc", 0444, regs, &priv->regs.osc);
+ debugfs_create_x32("iocon", 0444, regs, &priv->regs.iocon);
+ debugfs_create_x32("tdc", 0774, regs, &priv->regs.tdc);
+ debugfs_create_x32("tscon", 0444, regs, &priv->regs.tscon);
+ debugfs_create_x32("nbtcfg", 0444, regs, &priv->regs.nbtcfg);
+ debugfs_create_x32("dbtcfg", 0444, regs, &priv->regs.dbtcfg);
+
+ /* information on fifos */
+ debugfs_create_u32("fifo_start", 0444, rx,
+ &priv->fifos.rx_fifo_start);
+ debugfs_create_u32("fifo_count", 0444, rx,
+ &priv->fifos.rx_fifos);
+ debugfs_create_x32("fifo_mask", 0444, rx,
+ &priv->fifos.rx_fifo_mask);
+ debugfs_create_u64("rx_overflow", 0444, rx,
+ &priv->stats.rx_overflow);
+ debugfs_create_u64("rx_mab", 0444, stats,
+ &priv->stats.rx_mab);
+
+ debugfs_create_u32("fifo_start", 0444, tx,
+ &priv->fifos.tx_fifo_start);
+ debugfs_create_u32("fifo_count", 0444, tx,
+ &priv->fifos.tx_fifos);
+ debugfs_create_x32("fifo_mask", 0444, tx,
+ &priv->fifos.tx_fifo_mask);
+ debugfs_create_x32("fifo_pending", 0444, tx,
+ &priv->fifos.tx_pending_mask);
+ debugfs_create_x32("fifo_submitted", 0444, tx,
+ &priv->fifos.tx_submitted_mask);
+ debugfs_create_x32("fifo_processed", 0444, tx,
+ &priv->fifos.tx_processed_mask);
+ debugfs_create_u32("queue_status", 0444, tx,
+ &priv->tx_queue_status);
+ debugfs_create_u64("tx_mab", 0444, stats,
+ &priv->stats.tx_mab);
+
+ debugfs_create_u32("tef_count", 0444, tx,
+ &priv->fifos.tef_fifos);
+
+ debugfs_create_u32("fifo_max_payload_size", 0444, root,
+ &priv->fifos.payload_size);
+
+ /* interrupt statistics */
+ debugfs_create_u64("int", 0444, stats,
+ &priv->stats.irq_calls);
+ debugfs_create_u64("int_loops", 0444, stats,
+ &priv->stats.irq_loops);
+ debugfs_create_u64("int_ivm", 0444, stats,
+ &priv->stats.int_ivm_count);
+ debugfs_create_u64("int_wake", 0444, stats,
+ &priv->stats.int_wake_count);
+ debugfs_create_u64("int_cerr", 0444, stats,
+ &priv->stats.int_cerr_count);
+ debugfs_create_u64("int_serr", 0444, stats,
+ &priv->stats.int_serr_count);
+ debugfs_create_u64("int_rxov", 0444, stats,
+ &priv->stats.int_rxov_count);
+ debugfs_create_u64("int_txat", 0444, stats,
+ &priv->stats.int_txat_count);
+ debugfs_create_u64("int_spicrc", 0444, stats,
+ &priv->stats.int_spicrc_count);
+ debugfs_create_u64("int_ecc", 0444, stats,
+ &priv->stats.int_ecc_count);
+ debugfs_create_u64("int_tef", 0444, stats,
+ &priv->stats.int_tef_count);
+ debugfs_create_u64("int_mod", 0444, stats,
+ &priv->stats.int_mod_count);
+ debugfs_create_u64("int_tbc", 0444, stats,
+ &priv->stats.int_tbc_count);
+ debugfs_create_u64("int_rx", 0444, stats,
+ &priv->stats.int_rx_count);
+ debugfs_create_u64("int_tx", 0444, stats,
+ &priv->stats.int_tx_count);
+
+ /* dlc statistics */
+ for (i = 0; i < 16; i++) {
+ snprintf(name, sizeof(name), "%02i", i);
+ debugfs_create_u64(name, 0444, rxdlc,
+ &priv->stats.rx_dlc_usage[i]);
+ debugfs_create_u64(name, 0444, txdlc,
+ &priv->stats.tx_dlc_usage[i]);
+ }
+
+ /* statistics on fifo buffer usage and address */
+ for (i = 1; i < 32; i++) {
+ snprintf(name, sizeof(name), "%02i", i);
+ debugfs_create_u64(name, 0444, fifousage,
+ &priv->stats.fifo_usage[i]);
+ debugfs_create_u32(name, 0444, fifoaddr,
+ &priv->fifos.fifo_address[i]);
+ }
+
+ /* dump the controller registers themselves */
+ debugfs_create_devm_seqfile(&priv->spi->dev, "reg_dump",
+ root, mcp25xxfd_dump_regs);
+}
+
+static void mcp25xxfd_debugfs_remove(struct mcp25xxfd_priv *priv)
+{
+ debugfs_remove_recursive(priv->debugfs_dir);
+}
+
+#else
+static void mcp25xxfd_debugfs_add(struct mcp25xxfd_priv *priv)
+{
+ return 0;
+}
+
+static void mcp25xxfd_debugfs_remove(struct mcp25xxfd_priv *priv)
+{
+}
+#endif
+
+#ifdef CONFIG_OF_DYNAMIC
+int mcp25xxfd_of_parse(struct mcp25xxfd_priv *priv)
+{
+ struct spi_device *spi = priv->spi;
+ const struct device_node *np = spi->dev.of_node;
+ u32 val;
+ int ret;
+
+ priv->config.clock_div2 =
+ of_property_read_bool(np, "microchip,clock-div2");
+
+ ret = of_property_read_u32_index(np, "microchip,clock-out-div",
+ 0, &val);
+ if (!ret) {
+ switch (val) {
+ case 0:
+ case 1:
+ case 2:
+ case 4:
+ case 10:
+ priv->config.clock_odiv = val;
+ break;
+ default:
+ dev_err(&spi->dev,
+ "Invalid value in device tree for microchip,clock_out_div: %u - valid values: 0, 1, 2, 4, 10\n",
+ val);
+ return -EINVAL;
+ }
+ }
+
+ priv->config.gpio_opendrain =
+ of_property_read_bool(np, "gpio-open-drain");
+
+ return 0;
+}
+#else
+int mcp25xxfd_of_parse(struct mcp25xxfd_priv *priv)
+{
+ return 0;
+}
+#endif
+
+static int mcp25xxfd_can_probe(struct spi_device *spi)
+{
+ const struct of_device_id *of_id =
+ of_match_device(mcp25xxfd_of_match, &spi->dev);
+ struct net_device *net;
+ struct mcp25xxfd_priv *priv;
+ struct clk *clk;
+ int ret, freq;
+
+ /* as irq_create_fwspec_mapping() can return 0, check for it */
+ if (spi->irq <= 0) {
+ dev_err(&spi->dev, "no valid irq line defined: irq = %i\n",
+ spi->irq);
+ return -EINVAL;
+ }
+
+ clk = devm_clk_get(&spi->dev, NULL);
+ if (IS_ERR(clk)) {
+ dev_err(&spi->dev,
+ "Can't get Clock source\n");
+ return PTR_ERR(clk);
+ }
+ freq = clk_get_rate(clk);
+ if (freq < MCP25XXFD_MIN_CLOCK_FREQUENCY ||
+ freq > MCP25XXFD_MAX_CLOCK_FREQUENCY) {
+ dev_err(&spi->dev,
+ "Clock frequency %i is not in range [%i:%i]\n",
+ freq,
+ MCP25XXFD_MIN_CLOCK_FREQUENCY,
+ MCP25XXFD_MAX_CLOCK_FREQUENCY);
+ return -ERANGE;
+ }
+
+ /* Allocate can/net device */
+ net = alloc_candev(sizeof(*priv), TX_ECHO_SKB_MAX);
+ if (!net)
+ return -ENOMEM;
+
+ net->netdev_ops = &mcp25xxfd_netdev_ops;
+ net->flags |= IFF_ECHO;
+
+ priv = netdev_priv(net);
+ priv->can.bittiming_const = &mcp25xxfd_nominal_bittiming_const;
+ priv->can.do_set_bittiming = &mcp25xxfd_do_set_nominal_bittiming;
+ priv->can.data_bittiming_const = &mcp25xxfd_data_bittiming_const;
+ priv->can.do_set_data_bittiming = &mcp25xxfd_do_set_data_bittiming;
+ priv->can.do_set_mode = mcp25xxfd_do_set_mode;
+ priv->can.do_get_berr_counter = mcp25xxfd_get_berr_counter;
+
+ priv->can.ctrlmode_supported =
+ CAN_CTRLMODE_FD |
+ CAN_CTRLMODE_LOOPBACK |
+ CAN_CTRLMODE_LISTENONLY |
+ CAN_CTRLMODE_BERR_REPORTING |
+ CAN_CTRLMODE_FD_NON_ISO |
+ CAN_CTRLMODE_ONE_SHOT;
+
+ if (of_id)
+ priv->model = (enum mcp25xxfd_model)of_id->data;
+ else
+ priv->model = spi_get_device_id(spi)->driver_data;
+
+ spi_set_drvdata(spi, priv);
+ priv->spi = spi;
+ priv->net = net;
+ priv->clk = clk;
+
+ priv->clk_user_mask = MCP25XXFD_CLK_USER_CAN;
+
+ mutex_init(&priv->clk_user_lock);
+ mutex_init(&priv->spi_rxtx_lock);
+
+ /* enable the clock and mark as enabled */
+ priv->clk_user_mask = MCP25XXFD_CLK_USER_CAN;
+ ret = clk_prepare_enable(clk);
+ if (ret)
+ goto out_free;
+
+ /* Setup GPIO controller */
+ ret = mcp25xxfd_gpio_setup(spi);
+ if (ret)
+ goto out_clk;
+
+ /* all by default as push/pull */
+ priv->config.gpio_opendrain = false;
+
+ /* do not use the SCK clock divider of 2 */
+ priv->config.clock_div2 = false;
+
+ /* clock output is divided by 10 */
+ priv->config.clock_odiv = 10;
+
+ /* as a first guess we assume we are in CAN_CON_MODE_SLEEP
+ * this is how we leave the controller when removing ourselves
+ */
+ priv->active_can_mode = CAN_CON_MODE_SLEEP;
+
+ /* if we have a clock that is smaller then 4MHz, then enable the pll */
+ priv->config.clock_pll =
+ (freq <= MCP25XXFD_AUTO_PLL_MAX_CLOCK_FREQUENCY);
+
+ /* check in device tree for overrrides */
+ ret = mcp25xxfd_of_parse(priv);
+ if (ret)
+ return ret;
+
+ /* decide on real can clock rate */
+ priv->can.clock.freq = freq;
+ if (priv->config.clock_pll) {
+ priv->can.clock.freq *= MCP25XXFD_PLL_MULTIPLIER;
+ if (priv->can.clock.freq > MCP25XXFD_MAX_CLOCK_FREQUENCY) {
+ dev_err(&spi->dev,
+ "PLL clock frequency %i would exceed limit\n",
+ priv->can.clock.freq
+ );
+ return -EINVAL;
+ }
+ }
+ if (priv->config.clock_div2)
+ priv->can.clock.freq /= MCP25XXFD_SCLK_DIVIDER;
+
+ /* calclculate the clock frequencies to use */
+ priv->spi_setup_speed_hz = freq / 2;
+ priv->spi_speed_hz = priv->can.clock.freq / 2;
+ if (priv->config.clock_div2) {
+ priv->spi_setup_speed_hz /= MCP25XXFD_SCLK_DIVIDER;
+ priv->spi_speed_hz /= MCP25XXFD_SCLK_DIVIDER;
+ }
+
+ if (spi->max_speed_hz) {
+ priv->spi_setup_speed_hz = min_t(int,
+ priv->spi_setup_speed_hz,
+ spi->max_speed_hz);
+ priv->spi_speed_hz = min_t(int,
+ priv->spi_speed_hz,
+ spi->max_speed_hz);
+ }
+ /* Configure the SPI bus */
+ spi->bits_per_word = 8;
+ ret = spi_setup(spi);
+ if (ret)
+ goto out_clk;
+
+ ret = mcp25xxfd_power_enable(priv->power, 1);
+ if (ret)
+ goto out_clk;
+
+ SET_NETDEV_DEV(net, &spi->dev);
+
+ ret = mcp25xxfd_hw_probe(spi);
+ /* on error retry a second time */
+ if (ret == -ENODEV) {
+ ret = mcp25xxfd_hw_probe(spi);
+ if (!ret)
+ dev_info(&spi->dev,
+ "found device only during retry\n");
+ }
+ if (ret) {
+ if (ret == -ENODEV)
+ dev_err(&spi->dev,
+ "Cannot initialize MCP%x. Wrong wiring?\n",
+ priv->model);
+ }
+
+ /* setting up GPIO+INT as PUSHPULL , TXCAN PUSH/PULL, no Standby */
+ priv->regs.iocon = 0;
+
+ /* SOF/CLOCKOUT pin 3 */
+ if (priv->config.clock_odiv < 1)
+ priv->regs.iocon |= MCP25XXFD_IOCON_SOF;
+
+ /* INT/GPIO (probably also clockout) as open drain */
+ if (priv->config.gpio_opendrain)
+ priv->regs.iocon |= MCP25XXFD_IOCON_INTOD;
+
+ ret = mcp25xxfd_cmd_write(spi, MCP25XXFD_IOCON, priv->regs.iocon,
+ priv->spi_setup_speed_hz);
+ if (ret)
+ return ret;
+
+ /* and put controller to sleep */
+ mcp25xxfd_hw_sleep(spi);
+
+ ret = register_candev(net);
+ if (ret)
+ goto error_probe;
+
+ /* register debugfs */
+ mcp25xxfd_debugfs_add(priv);
+
+ devm_can_led_init(net);
+
+ netdev_info(net, "MCP%x successfully initialized.\n", priv->model);
+ return 0;
+
+error_probe:
+ mcp25xxfd_power_enable(priv->power, 0);
+
+out_clk:
+ mcp25xxfd_stop_clock(spi, MCP25XXFD_CLK_USER_CAN);
+
+out_free:
+ free_candev(net);
+ dev_err(&spi->dev, "Probe failed, err=%d\n", -ret);
+ return ret;
+}
+
+static int mcp25xxfd_can_remove(struct spi_device *spi)
+{
+ struct mcp25xxfd_priv *priv = spi_get_drvdata(spi);
+ struct net_device *net = priv->net;
+
+ mcp25xxfd_debugfs_remove(priv);
+
+ unregister_candev(net);
+
+ mcp25xxfd_power_enable(priv->power, 0);
+
+ if (!IS_ERR(priv->clk))
+ clk_disable_unprepare(priv->clk);
+
+ free_candev(net);
+
+ return 0;
+}
+
+static int __maybe_unused mcp25xxfd_can_suspend(struct device *dev)
+{
+ struct spi_device *spi = to_spi_device(dev);
+ struct mcp25xxfd_priv *priv = spi_get_drvdata(spi);
+ struct net_device *net = priv->net;
+
+ priv->force_quit = 1;
+ disable_irq(spi->irq);
+
+ if (netif_running(net)) {
+ netif_device_detach(net);
+
+ mcp25xxfd_hw_sleep(spi);
+ mcp25xxfd_power_enable(priv->transceiver, 0);
+ priv->after_suspend = AFTER_SUSPEND_UP;
+ } else {
+ priv->after_suspend = AFTER_SUSPEND_DOWN;
+ }
+
+ if (!IS_ERR_OR_NULL(priv->power)) {
+ regulator_disable(priv->power);
+ priv->after_suspend |= AFTER_SUSPEND_POWER;
+ }
+
+ return 0;
+}
+
+static int __maybe_unused mcp25xxfd_can_resume(struct device *dev)
+{
+ struct spi_device *spi = to_spi_device(dev);
+ struct mcp25xxfd_priv *priv = spi_get_drvdata(spi);
+
+ if (priv->after_suspend & AFTER_SUSPEND_POWER)
+ mcp25xxfd_power_enable(priv->power, 1);
+
+ if (priv->after_suspend & AFTER_SUSPEND_UP)
+ mcp25xxfd_power_enable(priv->transceiver, 1);
+ else
+ priv->after_suspend = 0;
+
+ priv->force_quit = 0;
+
+ enable_irq(spi->irq);
+
+ return 0;
+}
+
+static SIMPLE_DEV_PM_OPS(mcp25xxfd_can_pm_ops, mcp25xxfd_can_suspend,
+ mcp25xxfd_can_resume);
+
+static struct spi_driver mcp25xxfd_can_driver = {
+ .driver = {
+ .name = DEVICE_NAME,
+ .of_match_table = mcp25xxfd_of_match,
+ .pm = &mcp25xxfd_can_pm_ops,
+ },
+ .probe = mcp25xxfd_can_probe,
+ .remove = mcp25xxfd_can_remove,
+};
+
+static int __init mcp25xxfd_can_driver_init(void)
+{
+ int ret;
+
+ ret = spi_register_driver(&mcp25xxfd_can_driver);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+module_init(mcp25xxfd_can_driver_init);
+
+static void __exit mcp25xxfd_can_driver_exit(void)
+{
+ spi_unregister_driver(&mcp25xxfd_can_driver);
+}
+module_exit(mcp25xxfd_can_driver_exit);
+
+MODULE_DESCRIPTION("Microchip 25XXFD CAN driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/net/can/spi/qti-can.c b/drivers/net/can/spi/qti-can.c
index 3aa9395..0131148 100644
--- a/drivers/net/can/spi/qti-can.c
+++ b/drivers/net/can/spi/qti-can.c
@@ -1049,6 +1049,40 @@
return -EINVAL;
}
+static int qti_can_end_fwupgrade_ioctl(struct net_device *netdev,
+ struct ifreq *ifr, int cmd)
+{
+ int spi_cmd, ret;
+
+ struct qti_can *priv_data;
+ struct qti_can_netdev_privdata *netdev_priv_data;
+ struct spi_device *spi;
+ int len = 0;
+ u8 *data = NULL;
+
+ netdev_priv_data = netdev_priv(netdev);
+ priv_data = netdev_priv_data->qti_can;
+ spi = priv_data->spidev;
+ spi_cmd = qti_can_convert_ioctl_cmd_to_spi_cmd(cmd);
+ LOGDI("%s spi_cmd %x\n", __func__, spi_cmd);
+ if (spi_cmd < 0) {
+ LOGDE("%s wrong command %d\n", __func__, cmd);
+ return spi_cmd;
+ }
+
+ if (!ifr)
+ return -EINVAL;
+
+ mutex_lock(&priv_data->spi_lock);
+ LOGDI("%s len %d\n", __func__, len);
+
+ ret = qti_can_send_spi_locked(priv_data, spi_cmd, len, data);
+
+ mutex_unlock(&priv_data->spi_lock);
+
+ return ret;
+}
+
static int qti_can_do_blocking_ioctl(struct net_device *netdev,
struct ifreq *ifr, int cmd)
{
@@ -1184,10 +1218,12 @@
qti_can_frame_filter(netdev, ifr, cmd);
ret = 0;
break;
+ case IOCTL_END_FIRMWARE_UPGRADE:
+ ret = qti_can_end_fwupgrade_ioctl(netdev, ifr, cmd);
+ break;
case IOCTL_GET_FW_BR_VERSION:
case IOCTL_BEGIN_FIRMWARE_UPGRADE:
case IOCTL_FIRMWARE_UPGRADE_DATA:
- case IOCTL_END_FIRMWARE_UPGRADE:
case IOCTL_BEGIN_BOOT_ROM_UPGRADE:
case IOCTL_BOOT_ROM_UPGRADE_DATA:
case IOCTL_END_BOOT_ROM_UPGRADE:
diff --git a/drivers/platform/msm/ipa/ipa_api.c b/drivers/platform/msm/ipa/ipa_api.c
index 0ab6e90..2c11464 100644
--- a/drivers/platform/msm/ipa/ipa_api.c
+++ b/drivers/platform/msm/ipa/ipa_api.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2015-2018, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2015-2019, 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
@@ -187,7 +187,19 @@
__stringify(RESERVERD_PROD_74),
__stringify(IPA_CLIENT_MHI_DPL_CONS),
__stringify(RESERVERD_PROD_76),
- __stringify(IPA_CLIENT_DUMMY_CONS1)
+ __stringify(IPA_CLIENT_DUMMY_CONS1),
+ __stringify(IPA_CLIENT_WIGIG_PROD),
+ __stringify(IPA_CLIENT_WIGIG1_CONS),
+ __stringify(RESERVERD_PROD_80),
+ __stringify(IPA_CLIENT_WIGIG2_CONS),
+ __stringify(RESERVERD_PROD_82),
+ __stringify(IPA_CLIENT_WIGIG3_CONS),
+ __stringify(RESERVERD_PROD_84),
+ __stringify(IPA_CLIENT_WIGIG4_CONS),
+ __stringify(IPA_CLIENT_MHI2_PROD),
+ __stringify(IPA_CLIENT_MHI2_CONS),
+ __stringify(IPA_CLIENT_Q6_CV2X_PROD),
+ __stringify(IPA_CLIENT_Q6_CV2X_CONS)
};
/**
diff --git a/drivers/platform/msm/ipa/ipa_clients/ipa_mhi_client.c b/drivers/platform/msm/ipa/ipa_clients/ipa_mhi_client.c
index 7c97381..e211473 100644
--- a/drivers/platform/msm/ipa/ipa_clients/ipa_mhi_client.c
+++ b/drivers/platform/msm/ipa/ipa_clients/ipa_mhi_client.c
@@ -66,8 +66,8 @@
#define IPA_MHI_SUSPEND_SLEEP_MIN 900
#define IPA_MHI_SUSPEND_SLEEP_MAX 1100
-#define IPA_MHI_MAX_UL_CHANNELS 1
-#define IPA_MHI_MAX_DL_CHANNELS 2
+#define IPA_MHI_MAX_UL_CHANNELS 2
+#define IPA_MHI_MAX_DL_CHANNELS 3
/* bit #40 in address should be asserted for MHI transfers over pcie */
#define IPA_MHI_CLIENT_HOST_ADDR_COND(addr) \
diff --git a/drivers/platform/msm/ipa/ipa_clients/ipa_usb.c b/drivers/platform/msm/ipa/ipa_clients/ipa_usb.c
index 704308f..eccdeab 100644
--- a/drivers/platform/msm/ipa/ipa_clients/ipa_usb.c
+++ b/drivers/platform/msm/ipa/ipa_clients/ipa_usb.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2015-2018 The Linux Foundation. All rights reserved.
+/* Copyright (c) 2015-2019 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
@@ -144,13 +144,15 @@
enum ipa3_usb_transport_type {
IPA_USB_TRANSPORT_TETH,
IPA_USB_TRANSPORT_DPL,
+ IPA_USB_TRANSPORT_TETH_2,
IPA_USB_TRANSPORT_MAX
};
/* Get transport type from tethering protocol */
#define IPA3_USB_GET_TTYPE(__teth_prot) \
(((__teth_prot) == IPA_USB_DIAG) ? \
- IPA_USB_TRANSPORT_DPL : IPA_USB_TRANSPORT_TETH)
+ IPA_USB_TRANSPORT_DPL : (((__teth_prot) == IPA_USB_RMNET_CV2X) ? \
+ IPA_USB_TRANSPORT_TETH_2 : IPA_USB_TRANSPORT_TETH))
/* Does the given transport type is DPL? */
#define IPA3_USB_IS_TTYPE_DPL(__ttype) \
@@ -186,7 +188,7 @@
struct ipa3_usb_teth_prot_context
teth_prot_ctx[IPA_USB_MAX_TETH_PROT_SIZE];
int num_init_prot; /* without dpl */
- struct teth_bridge_init_params teth_bridge_params;
+ struct teth_bridge_init_params teth_bridge_params[IPA_TETH_BRIDGE_MAX];
struct completion dev_ready_comp;
u32 qmi_req_id;
spinlock_t state_lock;
@@ -198,6 +200,7 @@
struct dentry *dfile_state_info;
struct dentry *dent;
struct ipa3_usb_smmu_reg_map smmu_reg_map;
+ struct ipa3_usb_smmu_reg_map smmu_reg_map_dummy;
};
enum ipa3_usb_op {
@@ -704,6 +707,8 @@
case IPA_USB_RMNET:
case IPA_USB_MBIM:
return "teth_bridge";
+ case IPA_USB_RMNET_CV2X:
+ return "teth_bridge_cv2x";
case IPA_USB_DIAG:
return "dpl";
default:
@@ -719,6 +724,8 @@
switch (teth_prot) {
case IPA_USB_RMNET:
return "rmnet";
+ case IPA_USB_RMNET_CV2X:
+ return "rmnet_cv2x";
case IPA_USB_MBIM:
return "mbim";
default:
@@ -728,11 +735,18 @@
return "unsupported";
}
-static int ipa3_usb_init_teth_bridge(void)
+static int ipa3_usb_init_teth_bridge(enum ipa_usb_teth_prot teth_prot)
{
int result;
- result = teth_bridge_init(&ipa3_usb_ctx->teth_bridge_params);
+ if (teth_prot == IPA_USB_RMNET_CV2X)
+ result =
+ teth_bridge_init(
+ &ipa3_usb_ctx->teth_bridge_params[IPA_TETH_BRIDGE_2]);
+ else
+ result =
+ teth_bridge_init(
+ &ipa3_usb_ctx->teth_bridge_params[IPA_TETH_BRIDGE_1]);
if (result) {
IPA_USB_ERR("Failed to initialize teth_bridge.\n");
return result;
@@ -746,15 +760,26 @@
struct ipa3_usb_transport_type_ctx *ttype_ctx =
&ipa3_usb_ctx->ttype_ctx[ttype];
int result;
+ enum ipa_client_type consumer;
- /* there is one PM resource for teth and one for DPL */
- if (!IPA3_USB_IS_TTYPE_DPL(ttype) && ipa3_usb_ctx->num_init_prot > 0)
+ /*
+ * One PM resource for teth1,
+ * One PM resource for teth2 (CV2X),
+ * One for DPL,
+ */
+
+ if (!IPA3_USB_IS_TTYPE_DPL(ttype) && (ipa3_usb_ctx->num_init_prot > 0)
+ && (ttype != IPA_USB_TRANSPORT_TETH_2))
return 0;
memset(&ttype_ctx->pm_ctx.reg_params, 0,
sizeof(ttype_ctx->pm_ctx.reg_params));
- ttype_ctx->pm_ctx.reg_params.name = (ttype == IPA_USB_TRANSPORT_DPL) ?
- "USB DPL" : "USB";
+ ttype_ctx->pm_ctx.reg_params.name =
+ (ttype == IPA_USB_TRANSPORT_DPL) ?
+ "USB DPL" :
+ (ttype == IPA_USB_TRANSPORT_TETH_2) ?
+ "USB2" : "USB";
+
ttype_ctx->pm_ctx.reg_params.callback = ipa3_usb_pm_cb;
ttype_ctx->pm_ctx.reg_params.user_data = ttype_ctx;
ttype_ctx->pm_ctx.reg_params.group = IPA_PM_GROUP_DEFAULT;
@@ -766,9 +791,12 @@
goto fail_pm_reg;
}
+ consumer = (ttype == IPA_USB_TRANSPORT_DPL) ?
+ IPA_CLIENT_USB_DPL_CONS :
+ (ttype == IPA_USB_TRANSPORT_TETH_2) ?
+ IPA_CLIENT_USB2_CONS : IPA_CLIENT_USB_CONS;
result = ipa_pm_associate_ipa_cons_to_client(ttype_ctx->pm_ctx.hdl,
- (ttype == IPA_USB_TRANSPORT_DPL) ?
- IPA_CLIENT_USB_DPL_CONS : IPA_CLIENT_USB_CONS);
+ consumer);
if (result) {
IPA_USB_ERR("fail to associate cons with PM %d\n", result);
goto fail_pm_cons;
@@ -994,9 +1022,11 @@
goto bad_params;
}
ipa3_usb_ctx->teth_prot_ctx[teth_prot].user_data = user_data;
- result = ipa3_usb_init_teth_bridge();
+
+ result = ipa3_usb_init_teth_bridge(teth_prot);
if (result)
goto teth_prot_init_fail;
+
ipa3_usb_ctx->teth_prot_ctx[teth_prot].state =
IPA_USB_TETH_PROT_INITIALIZED;
ipa3_usb_ctx->num_init_prot++;
@@ -1004,6 +1034,26 @@
ipa3_usb_teth_prot_to_string(teth_prot),
ipa3_usb_teth_bridge_prot_to_string(teth_prot));
break;
+ case IPA_USB_RMNET_CV2X:
+ if (ipa3_usb_ctx->teth_prot_ctx[teth_prot].state !=
+ IPA_USB_TETH_PROT_INVALID) {
+ IPA_USB_DBG("%s already initialized\n",
+ ipa3_usb_teth_prot_to_string(teth_prot));
+ result = -EPERM;
+ goto bad_params;
+ }
+ ipa3_usb_ctx->teth_prot_ctx[teth_prot].user_data = user_data;
+
+ result = ipa3_usb_init_teth_bridge(teth_prot);
+ if (result)
+ goto teth_prot_init_fail;
+
+ ipa3_usb_ctx->teth_prot_ctx[teth_prot].state =
+ IPA_USB_TETH_PROT_INITIALIZED;
+ IPA_USB_DBG("initialized %s %s\n",
+ ipa3_usb_teth_prot_to_string(teth_prot),
+ ipa3_usb_teth_bridge_prot_to_string(teth_prot));
+ break;
case IPA_USB_DIAG:
if (ipa3_usb_ctx->teth_prot_ctx[teth_prot].state !=
IPA_USB_TETH_PROT_INVALID) {
@@ -1031,7 +1081,8 @@
teth_prot_init_fail:
if ((IPA3_USB_IS_TTYPE_DPL(ttype))
- || (ipa3_usb_ctx->num_init_prot == 0)) {
+ || (ipa3_usb_ctx->num_init_prot == 0)
+ || (teth_prot == IPA_USB_RMNET_CV2X)) {
if (ipa_pm_is_used()) {
ipa3_usb_deregister_pm(ttype);
} else {
@@ -1114,6 +1165,7 @@
}
break;
case IPA_USB_RMNET:
+ case IPA_USB_RMNET_CV2X:
case IPA_USB_MBIM:
if (ipa3_usb_ctx->teth_prot_ctx[params->teth_prot].state ==
IPA_USB_TETH_PROT_INVALID) {
@@ -1131,6 +1183,102 @@
return true;
}
+/*
+ * ipa3_usb_smmu_map_dummy: Does the same job of ipa3_usb_smmu_map_xdci_channel.
+ * API to map geventcount dummy addr, which will be provided
+ * to GSI from USB to handle sw path. Where USB driver will take
+ * care of replenishing desc to ipa hw. This dummy gevntcount addr
+ * cannot be in same page as other, since other are actual usb hw addr.
+ * We map this dummy addr to AP smmu context, so that there will
+ * not be any NOC issue when IPA/GSI tries to access it.
+ */
+static int ipa3_usb_smmu_map_dummy(
+ struct ipa_usb_xdci_chan_params *params,
+ bool map
+ )
+{
+ int result = 0;
+ u32 gevntcount_r = rounddown(params->gevntcount_low_addr, PAGE_SIZE);
+ u32 xfer_scratch_r =
+ rounddown(params->xfer_scratch.depcmd_low_addr, PAGE_SIZE);
+
+ if ((ipa3_usb_ctx->smmu_reg_map.addr != xfer_scratch_r) &&
+ (ipa3_usb_ctx->smmu_reg_map.cnt != 0)) {
+ IPA_USB_ERR("No support more than 1 page map for USB regs");
+ WARN_ON(1);
+ return -EINVAL;
+ }
+
+ if (map) {
+ if (ipa3_usb_ctx->smmu_reg_map_dummy.cnt == 0) {
+ ipa3_usb_ctx->smmu_reg_map_dummy.addr = gevntcount_r;
+ result = ipa3_smmu_map_peer_reg(
+ ipa3_usb_ctx->smmu_reg_map_dummy.addr, true,
+ IPA_SMMU_CB_AP);
+ if (result) {
+ IPA_USB_ERR("failed to map USB regs %d\n",
+ result);
+ return result;
+ }
+
+ if (ipa3_usb_ctx->smmu_reg_map.cnt == 0) {
+ ipa3_usb_ctx->smmu_reg_map.addr =
+ xfer_scratch_r;
+ result = ipa3_smmu_map_peer_reg(
+ ipa3_usb_ctx->smmu_reg_map.addr, true,
+ IPA_SMMU_CB_AP);
+ if (result) {
+ IPA_USB_ERR(
+ "failed to map USB regs %d\n",
+ result);
+ return result;
+ }
+ }
+ ipa3_usb_ctx->smmu_reg_map.cnt++;
+ ipa3_usb_ctx->smmu_reg_map_dummy.cnt++;
+ }
+ } else {
+ if (gevntcount_r != ipa3_usb_ctx->smmu_reg_map_dummy.addr) {
+ IPA_USB_ERR(
+ "No support for unmap different reg\n");
+ return -EINVAL;
+ }
+
+ if (ipa3_usb_ctx->smmu_reg_map_dummy.cnt == 1) {
+ result = ipa3_smmu_map_peer_reg(
+ ipa3_usb_ctx->smmu_reg_map_dummy.addr, false,
+ IPA_SMMU_CB_AP);
+ if (result) {
+ IPA_USB_ERR("failed to unmap USB regs %d\n",
+ result);
+ return result;
+ }
+
+ if (ipa3_usb_ctx->smmu_reg_map.cnt == 1) {
+ if (xfer_scratch_r !=
+ ipa3_usb_ctx->smmu_reg_map.addr) {
+ IPA_USB_ERR(
+ "No support for un map different reg\n");
+ return -EINVAL;
+ }
+
+ result = ipa3_smmu_map_peer_reg(
+ ipa3_usb_ctx->smmu_reg_map.addr, false,
+ IPA_SMMU_CB_AP);
+ if (result) {
+ IPA_USB_ERR(
+ "failed to unmap USB regs %d\n",
+ result);
+ return result;
+ }
+ }
+ ipa3_usb_ctx->smmu_reg_map.cnt--;
+ ipa3_usb_ctx->smmu_reg_map_dummy.cnt--;
+ }
+ }
+ return result;
+}
+
static int ipa3_usb_smmu_map_xdci_channel(
struct ipa_usb_xdci_chan_params *params, bool map)
{
@@ -1139,13 +1287,22 @@
u32 xfer_scratch_r =
rounddown(params->xfer_scratch.depcmd_low_addr, PAGE_SIZE);
- if (gevntcount_r != xfer_scratch_r) {
+ if ((gevntcount_r != xfer_scratch_r) &&
+ (params->is_sw_path == false)) {
IPA_USB_ERR("No support more than 1 page map for USB regs\n");
WARN_ON(1);
return -EINVAL;
}
- if (map) {
+ if (params->is_sw_path == true) {
+ result = ipa3_usb_smmu_map_dummy(params, map);
+ if (result) {
+ IPA_USB_ERR("failed to %s USB regs %d\n",
+ (map == true)?"map":"unmap",
+ result);
+ return result;
+ }
+ } else if (map) {
if (ipa3_usb_ctx->smmu_reg_map.cnt == 0) {
ipa3_usb_ctx->smmu_reg_map.addr = gevntcount_r;
result = ipa3_smmu_map_peer_reg(
@@ -1184,7 +1341,6 @@
ipa3_usb_ctx->smmu_reg_map.cnt--;
}
-
result = ipa3_smmu_map_peer_buff(params->xfer_ring_base_addr_iova,
params->xfer_ring_len, map, params->sgt_xfer_rings,
IPA_SMMU_CB_AP);
@@ -1266,11 +1422,25 @@
case IPA_USB_RMNET:
case IPA_USB_MBIM:
chan_params.priv =
- ipa3_usb_ctx->teth_bridge_params.private_data;
+ ipa3_usb_ctx->teth_bridge_params[IPA_TETH_BRIDGE_1].
+ private_data;
chan_params.notify =
- ipa3_usb_ctx->teth_bridge_params.usb_notify_cb;
+ ipa3_usb_ctx->teth_bridge_params[IPA_TETH_BRIDGE_1].
+ usb_notify_cb;
chan_params.skip_ep_cfg =
- ipa3_usb_ctx->teth_bridge_params.skip_ep_cfg;
+ ipa3_usb_ctx->teth_bridge_params[IPA_TETH_BRIDGE_1].
+ skip_ep_cfg;
+ break;
+ case IPA_USB_RMNET_CV2X:
+ chan_params.priv =
+ ipa3_usb_ctx->teth_bridge_params[IPA_TETH_BRIDGE_2].
+ private_data;
+ chan_params.notify =
+ ipa3_usb_ctx->teth_bridge_params[IPA_TETH_BRIDGE_2].
+ usb_notify_cb;
+ chan_params.skip_ep_cfg =
+ ipa3_usb_ctx->teth_bridge_params[IPA_TETH_BRIDGE_2].
+ skip_ep_cfg;
break;
case IPA_USB_DIAG:
chan_params.priv = NULL;
@@ -1571,6 +1741,16 @@
return 0;
}
+static int ipa3_get_tethering_mode(enum ipa_usb_teth_prot teth_prot)
+{
+ if (teth_prot == IPA_USB_RMNET)
+ return TETH_TETHERING_MODE_RMNET;
+ else if (teth_prot == IPA_USB_RMNET_CV2X)
+ return TETH_TETHERING_MODE_RMNET_2;
+ else
+ return TETH_TETHERING_MODE_MBIM;
+}
+
static int ipa3_usb_connect_teth_prot(enum ipa_usb_teth_prot teth_prot)
{
int result;
@@ -1639,6 +1819,7 @@
ipa3_usb_teth_prot_to_string(teth_prot));
break;
case IPA_USB_RMNET:
+ case IPA_USB_RMNET_CV2X:
case IPA_USB_MBIM:
if (ipa3_usb_ctx->teth_prot_ctx[teth_prot].state ==
IPA_USB_TETH_PROT_CONNECTED) {
@@ -1646,7 +1827,8 @@
ipa3_usb_teth_prot_to_string(teth_prot));
break;
}
- result = ipa3_usb_init_teth_bridge();
+
+ result = ipa3_usb_init_teth_bridge(teth_prot);
if (result)
return result;
@@ -1658,14 +1840,19 @@
teth_bridge_params.usb_ipa_pipe_hdl =
teth_conn_params->usb_to_ipa_clnt_hdl;
teth_bridge_params.tethering_mode =
- (teth_prot == IPA_USB_RMNET) ?
- (TETH_TETHERING_MODE_RMNET):(TETH_TETHERING_MODE_MBIM);
- teth_bridge_params.client_type = IPA_CLIENT_USB_PROD;
+ ipa3_get_tethering_mode(teth_prot);
+
+ if (teth_prot == IPA_USB_RMNET_CV2X)
+ teth_bridge_params.client_type = IPA_CLIENT_USB2_PROD;
+ else
+ teth_bridge_params.client_type = IPA_CLIENT_USB_PROD;
+
result = ipa3_usb_connect_teth_bridge(&teth_bridge_params);
if (result) {
ipa3_usb_ctx->ttype_ctx[ttype].user_data = NULL;
return result;
}
+
ipa3_usb_ctx->teth_prot_ctx[teth_prot].state =
IPA_USB_TETH_PROT_CONNECTED;
ipa3_usb_notify_do(ttype, IPA_USB_DEVICE_READY);
@@ -1704,11 +1891,15 @@
return 0;
}
-static int ipa3_usb_disconnect_teth_bridge(void)
+static int ipa3_usb_disconnect_teth_bridge(enum ipa_usb_teth_prot teth_prot)
{
int result;
- result = teth_bridge_disconnect(IPA_CLIENT_USB_PROD);
+ if (teth_prot == IPA_USB_RMNET_CV2X)
+ result = teth_bridge_disconnect(IPA_CLIENT_USB2_PROD);
+ else
+ result = teth_bridge_disconnect(IPA_CLIENT_USB_PROD);
+
if (result) {
IPA_USB_ERR("failed to disconnect teth_bridge.\n");
return result;
@@ -1774,6 +1965,7 @@
ipa3_usb_teth_prot_to_string(teth_prot));
break;
case IPA_USB_RMNET:
+ case IPA_USB_RMNET_CV2X:
case IPA_USB_MBIM:
if (ipa3_usb_ctx->teth_prot_ctx[teth_prot].state !=
IPA_USB_TETH_PROT_CONNECTED) {
@@ -1782,7 +1974,8 @@
ipa3_usb_teth_bridge_prot_to_string(teth_prot));
return -EPERM;
}
- result = ipa3_usb_disconnect_teth_bridge();
+
+ result = ipa3_usb_disconnect_teth_bridge(teth_prot);
if (result)
break;
@@ -1828,8 +2021,7 @@
return -EINVAL;
}
- ttype = (params->teth_prot == IPA_USB_DIAG) ? IPA_USB_TRANSPORT_DPL :
- IPA_USB_TRANSPORT_TETH;
+ ttype = IPA3_USB_GET_TTYPE(params->teth_prot);
if (!ipa3_usb_check_legal_op(IPA_USB_OP_CONNECT, ttype)) {
IPA_USB_ERR("Illegal operation.\n");
@@ -2013,7 +2205,9 @@
for (i = 0 ; i < IPA_USB_MAX_TETH_PROT_SIZE ; i++) {
if (ipa3_usb_ctx->teth_prot_ctx[i].state ==
IPA_USB_TETH_PROT_INITIALIZED) {
- if ((i == IPA_USB_RMNET) || (i == IPA_USB_MBIM))
+ if ((i == IPA_USB_RMNET) ||
+ (i == IPA_USB_MBIM) ||
+ (i == IPA_USB_RMNET_CV2X))
status->inited_prots[status->num_init_prot++] =
ipa3_usb_teth_bridge_prot_to_string(i);
else
@@ -2023,6 +2217,7 @@
IPA_USB_TETH_PROT_CONNECTED) {
switch (i) {
case IPA_USB_RMNET:
+ case IPA_USB_RMNET_CV2X:
case IPA_USB_MBIM:
status->teth_connected_prot =
ipa3_usb_teth_bridge_prot_to_string(i);
@@ -2527,6 +2722,24 @@
ipa3_usb_teth_prot_to_string(teth_prot),
ipa3_usb_teth_bridge_prot_to_string(teth_prot));
break;
+ case IPA_USB_RMNET_CV2X:
+ if (ipa3_usb_ctx->teth_prot_ctx[teth_prot].state !=
+ IPA_USB_TETH_PROT_INITIALIZED) {
+ IPA_USB_ERR("%s (%s) is not initialized\n",
+ ipa3_usb_teth_prot_to_string(teth_prot),
+ ipa3_usb_teth_bridge_prot_to_string(teth_prot));
+ result = -EINVAL;
+ goto bad_params;
+ }
+
+ ipa3_usb_ctx->teth_prot_ctx[teth_prot].user_data =
+ NULL;
+ ipa3_usb_ctx->teth_prot_ctx[teth_prot].state =
+ IPA_USB_TETH_PROT_INVALID;
+ IPA_USB_DBG("deinitialized %s (%s)\n",
+ ipa3_usb_teth_prot_to_string(teth_prot),
+ ipa3_usb_teth_bridge_prot_to_string(teth_prot));
+ break;
case IPA_USB_DIAG:
if (ipa3_usb_ctx->teth_prot_ctx[teth_prot].state !=
IPA_USB_TETH_PROT_INITIALIZED) {
@@ -2549,7 +2762,8 @@
}
if (IPA3_USB_IS_TTYPE_DPL(ttype) ||
- (ipa3_usb_ctx->num_init_prot == 0)) {
+ (ipa3_usb_ctx->num_init_prot == 0) ||
+ (teth_prot == IPA_USB_RMNET_CV2X)) {
if (!ipa3_usb_set_state(IPA_USB_INVALID, false, ttype))
IPA_USB_ERR(
"failed to change state to invalid\n");
@@ -2976,6 +3190,9 @@
pm_ctx = &ipa3_usb_ctx->ttype_ctx[IPA_USB_TRANSPORT_TETH].pm_ctx;
pm_ctx->hdl = ~0;
pm_ctx->remote_wakeup_work = &ipa3_usb_notify_remote_wakeup_work;
+ pm_ctx = &ipa3_usb_ctx->ttype_ctx[IPA_USB_TRANSPORT_TETH_2].pm_ctx;
+ pm_ctx->hdl = ~0;
+ pm_ctx->remote_wakeup_work = &ipa3_usb_notify_remote_wakeup_work;
pm_ctx = &ipa3_usb_ctx->ttype_ctx[IPA_USB_TRANSPORT_DPL].pm_ctx;
pm_ctx->hdl = ~0;
pm_ctx->remote_wakeup_work = &ipa3_usb_dpl_notify_remote_wakeup_work;
diff --git a/drivers/platform/msm/ipa/ipa_v2/ipa_rt.c b/drivers/platform/msm/ipa/ipa_v2/ipa_rt.c
index 095d60b..50bb9bc 100644
--- a/drivers/platform/msm/ipa/ipa_v2/ipa_rt.c
+++ b/drivers/platform/msm/ipa/ipa_v2/ipa_rt.c
@@ -1067,9 +1067,8 @@
* tables
*/
if (!strcmp(tbl->name, IPA_DFLT_RT_TBL_NAME) &&
- (tbl->rule_cnt > 0) && (at_rear != 0)) {
- IPAERR("cannot add rule at end of tbl rule_cnt=%d at_rear=%d\n",
- tbl->rule_cnt, at_rear);
+ (tbl->rule_cnt > 0)) {
+ IPAERR_RL("cannot add rules to default rt table\n");
goto error;
}
@@ -1608,6 +1607,11 @@
goto error;
}
+ if (!strcmp(entry->tbl->name, IPA_DFLT_RT_TBL_NAME)) {
+ IPAERR_RL("Default tbl rule cannot be modified\n");
+ return -EINVAL;
+ }
+
/* Adding check to confirm still
* header entry present in header table or not
*/
diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa.c b/drivers/platform/msm/ipa/ipa_v3/ipa.c
index 6de07dc..80513d2 100644
--- a/drivers/platform/msm/ipa/ipa_v3/ipa.c
+++ b/drivers/platform/msm/ipa/ipa_v3/ipa.c
@@ -624,6 +624,185 @@
kfree(buff);
}
+static void ipa3_get_usb_ep_info(
+ struct ipa_ioc_get_ep_info *ep_info,
+ struct ipa_ep_pair_info *pair_info
+ )
+{
+ int ep_index = -1, i;
+
+ ep_info->num_ep_pairs = 0;
+ for (i = 0; i < ep_info->max_ep_pairs; i++) {
+ pair_info[i].consumer_pipe_num = -1;
+ pair_info[i].producer_pipe_num = -1;
+ pair_info[i].ep_id = -1;
+ }
+
+ ep_index = ipa3_get_ep_mapping(IPA_CLIENT_USB_PROD);
+
+ if ((ep_index != -1) && ipa3_ctx->ep[ep_index].valid) {
+ pair_info[ep_info->num_ep_pairs].consumer_pipe_num = ep_index;
+ ep_index = ipa3_get_ep_mapping(IPA_CLIENT_USB_CONS);
+ if ((ep_index != -1) && (ipa3_ctx->ep[ep_index].valid)) {
+ pair_info[ep_info->num_ep_pairs].producer_pipe_num =
+ ep_index;
+ pair_info[ep_info->num_ep_pairs].ep_id =
+ IPA_USB0_EP_ID;
+
+ IPADBG("ep_pair_info consumer_pipe_num %d",
+ pair_info[ep_info->num_ep_pairs].
+ consumer_pipe_num);
+ IPADBG(" producer_pipe_num %d ep_id %d\n",
+ pair_info[ep_info->num_ep_pairs].
+ producer_pipe_num,
+ pair_info[ep_info->num_ep_pairs].ep_id);
+ ep_info->num_ep_pairs++;
+ } else {
+ pair_info[ep_info->num_ep_pairs].consumer_pipe_num = -1;
+ IPADBG("ep_pair_info consumer_pipe_num %d",
+ pair_info[ep_info->num_ep_pairs].
+ consumer_pipe_num);
+ IPADBG(" producer_pipe_num %d ep_id %d\n",
+ pair_info[ep_info->num_ep_pairs].
+ producer_pipe_num,
+ pair_info[ep_info->num_ep_pairs].ep_id);
+ }
+ }
+
+ ep_index = ipa3_get_ep_mapping(IPA_CLIENT_USB2_PROD);
+
+ if ((ep_index != -1) && ipa3_ctx->ep[ep_index].valid) {
+ pair_info[ep_info->num_ep_pairs].consumer_pipe_num = ep_index;
+ ep_index = ipa3_get_ep_mapping(IPA_CLIENT_USB2_CONS);
+ if ((ep_index != -1) && (ipa3_ctx->ep[ep_index].valid)) {
+ pair_info[ep_info->num_ep_pairs].producer_pipe_num =
+ ep_index;
+ pair_info[ep_info->num_ep_pairs].ep_id =
+ IPA_USB1_EP_ID;
+
+ IPADBG("ep_pair_info consumer_pipe_num %d",
+ pair_info[ep_info->num_ep_pairs].
+ consumer_pipe_num);
+ IPADBG(" producer_pipe_num %d ep_id %d\n",
+ pair_info[ep_info->num_ep_pairs].
+ producer_pipe_num,
+ pair_info[ep_info->num_ep_pairs].ep_id);
+ ep_info->num_ep_pairs++;
+ } else {
+ pair_info[ep_info->num_ep_pairs].consumer_pipe_num = -1;
+ IPADBG("ep_pair_info consumer_pipe_num %d",
+ pair_info[ep_info->num_ep_pairs].
+ consumer_pipe_num);
+ IPADBG(" producer_pipe_num %d ep_id %d\n",
+ pair_info[ep_info->num_ep_pairs].
+ producer_pipe_num,
+ pair_info[ep_info->num_ep_pairs].ep_id);
+ }
+ }
+}
+
+static void ipa3_get_pcie_ep_info(
+ struct ipa_ioc_get_ep_info *ep_info,
+ struct ipa_ep_pair_info *pair_info
+ )
+{
+ int ep_index = -1, i;
+
+ ep_info->num_ep_pairs = 0;
+ for (i = 0; i < ep_info->max_ep_pairs; i++) {
+ pair_info[i].consumer_pipe_num = -1;
+ pair_info[i].producer_pipe_num = -1;
+ pair_info[i].ep_id = -1;
+ }
+
+ ep_index = ipa3_get_ep_mapping(IPA_CLIENT_MHI_PROD);
+
+ if ((ep_index != -1) && ipa3_ctx->ep[ep_index].valid) {
+ pair_info[ep_info->num_ep_pairs].consumer_pipe_num = ep_index;
+ ep_index = ipa3_get_ep_mapping(IPA_CLIENT_MHI_CONS);
+ if ((ep_index != -1) && (ipa3_ctx->ep[ep_index].valid)) {
+ pair_info[ep_info->num_ep_pairs].producer_pipe_num =
+ ep_index;
+ pair_info[ep_info->num_ep_pairs].ep_id =
+ IPA_PCIE0_EP_ID;
+
+ IPADBG("ep_pair_info consumer_pipe_num %d",
+ pair_info[ep_info->num_ep_pairs].
+ consumer_pipe_num);
+ IPADBG(" producer_pipe_num %d ep_id %d\n",
+ pair_info[ep_info->num_ep_pairs].
+ producer_pipe_num,
+ pair_info[ep_info->num_ep_pairs].ep_id);
+ ep_info->num_ep_pairs++;
+ } else {
+ pair_info[ep_info->num_ep_pairs].consumer_pipe_num = -1;
+ IPADBG("ep_pair_info consumer_pipe_num %d",
+ pair_info[ep_info->num_ep_pairs].
+ consumer_pipe_num);
+ IPADBG(" producer_pipe_num %d ep_id %d\n",
+ pair_info[ep_info->num_ep_pairs].
+ producer_pipe_num,
+ pair_info[ep_info->num_ep_pairs].ep_id);
+ }
+ }
+
+ ep_index = ipa3_get_ep_mapping(IPA_CLIENT_MHI2_PROD);
+
+ if ((ep_index != -1) && ipa3_ctx->ep[ep_index].valid) {
+ pair_info[ep_info->num_ep_pairs].consumer_pipe_num = ep_index;
+ ep_index = ipa3_get_ep_mapping(IPA_CLIENT_MHI2_CONS);
+ if ((ep_index != -1) && (ipa3_ctx->ep[ep_index].valid)) {
+ pair_info[ep_info->num_ep_pairs].producer_pipe_num =
+ ep_index;
+ pair_info[ep_info->num_ep_pairs].ep_id =
+ IPA_PCIE1_EP_ID;
+
+ IPADBG("ep_pair_info consumer_pipe_num %d",
+ pair_info[ep_info->num_ep_pairs].
+ consumer_pipe_num);
+ IPADBG(" producer_pipe_num %d ep_id %d\n",
+ pair_info[ep_info->num_ep_pairs].
+ producer_pipe_num,
+ pair_info[ep_info->num_ep_pairs].ep_id);
+ ep_info->num_ep_pairs++;
+ } else {
+ pair_info[ep_info->num_ep_pairs].consumer_pipe_num = -1;
+ IPADBG("ep_pair_info consumer_pipe_num %d",
+ pair_info[ep_info->num_ep_pairs].
+ consumer_pipe_num);
+ IPADBG(" producer_pipe_num %d ep_id %d\n",
+ pair_info[ep_info->num_ep_pairs].
+ producer_pipe_num,
+ pair_info[ep_info->num_ep_pairs].ep_id);
+ }
+ }
+}
+
+
+static int ipa3_get_ep_info(struct ipa_ioc_get_ep_info *ep_info,
+ u8 *param)
+{
+ int ret = 0;
+ struct ipa_ep_pair_info *pair_info = (struct ipa_ep_pair_info *)param;
+
+ switch (ep_info->ep_type) {
+ case IPA_DATA_EP_TYP_HSUSB:
+ ipa3_get_usb_ep_info(ep_info, pair_info);
+ break;
+
+ case IPA_DATA_EP_TYP_PCIE:
+ ipa3_get_pcie_ep_info(ep_info, pair_info);
+ break;
+
+ default:
+ IPAERR_RL("Undefined ep_type %d\n", ep_info->ep_type);
+ ret = -EFAULT;
+ break;
+ }
+
+ return ret;
+}
+
static int ipa3_send_gsb_msg(unsigned long usr_param, uint8_t msg_type)
{
int retval;
@@ -689,8 +868,10 @@
struct ipa_ioc_rm_dependency rm_depend;
struct ipa_ioc_nat_dma_cmd *table_dma_cmd;
struct ipa_ioc_get_vlan_mode vlan_mode;
+ struct ipa_ioc_get_ep_info ep_info;
size_t sz;
int pre_entry;
+ unsigned long uptr = 0;
IPADBG("cmd=%x nr=%d\n", cmd, _IOC_NR(cmd));
@@ -1878,6 +2059,60 @@
}
break;
+ case IPA_IOC_GET_PHERIPHERAL_EP_INFO:
+ IPADBG("Got IPA_IOC_GET_EP_INFO\n");
+ if (copy_from_user(&ep_info, (const void __user *)arg,
+ sizeof(struct ipa_ioc_get_ep_info))) {
+ IPAERR_RL("copy_from_user fails\n");
+ retval = -EFAULT;
+ break;
+ }
+
+ if (ep_info.max_ep_pairs != QUERY_MAX_EP_PAIRS)
+ IPAERR_RL("unexpected max_ep_pairs %d\n",
+ ep_info.max_ep_pairs);
+
+ if (ep_info.ep_pair_size !=
+ (QUERY_MAX_EP_PAIRS * sizeof(struct ipa_ep_pair_info)))
+ IPAERR_RL("unexpected ep_pair_size %d\n",
+ ep_info.max_ep_pairs);
+
+ uptr = ep_info.info;
+ if (unlikely(!uptr)) {
+ IPAERR_RL("unexpected NULL info\n");
+ retval = -EFAULT;
+ break;
+ }
+
+ param = kzalloc(ep_info.ep_pair_size, GFP_KERNEL);
+ if (!param) {
+ IPAERR_RL("kzalloc fails\n");
+ retval = -ENOMEM;
+ break;
+ }
+
+ retval = ipa3_get_ep_info(&ep_info, param);
+ if (retval < 0) {
+ IPAERR("ipa3_get_ep_info failed\n");
+ retval = -EFAULT;
+ break;
+ }
+
+ if (copy_to_user((void __user *)uptr, param,
+ ep_info.ep_pair_size)) {
+ IPAERR_RL("copy_to_user fails\n");
+ retval = -EFAULT;
+ break;
+ }
+
+ if (copy_to_user((void __user *)arg, &ep_info,
+ sizeof(struct ipa_ioc_get_ep_info))) {
+ IPAERR_RL("copy_to_user fails\n");
+ retval = -EFAULT;
+ break;
+ }
+ break;
+
default:
IPA_ACTIVE_CLIENTS_DEC_SIMPLE();
return -ENOTTY;
@@ -2619,9 +2854,14 @@
ipa3_q6_pipe_delay(true);
ipa3_q6_avoid_holb();
- if (ipa3_ctx->ipa_config_is_mhi)
+ if (ipa3_ctx->ipa_config_is_mhi) {
ipa3_set_reset_client_cons_pipe_sus_holb(true,
IPA_CLIENT_MHI_CONS);
+ if (ipa3_ctx->ipa_config_is_auto)
+ ipa3_set_reset_client_cons_pipe_sus_holb(true,
+ IPA_CLIENT_MHI2_CONS);
+ }
+
if (ipa3_q6_clean_q6_tables()) {
IPAERR("Failed to clean Q6 tables\n");
BUG();
@@ -2636,9 +2876,16 @@
ipa3_q6_pipe_delay(false);
ipa3_set_reset_client_prod_pipe_delay(true,
IPA_CLIENT_USB_PROD);
- if (ipa3_ctx->ipa_config_is_mhi)
+ if (ipa3_ctx->ipa_config_is_auto)
+ ipa3_set_reset_client_prod_pipe_delay(true,
+ IPA_CLIENT_USB2_PROD);
+ if (ipa3_ctx->ipa_config_is_mhi) {
ipa3_set_reset_client_prod_pipe_delay(true,
IPA_CLIENT_MHI_PROD);
+ if (ipa3_ctx->ipa_config_is_auto)
+ ipa3_set_reset_client_prod_pipe_delay(true,
+ IPA_CLIENT_MHI2_PROD);
+ }
IPA_ACTIVE_CLIENTS_DEC_SIMPLE();
IPADBG_LOW("Exit with success\n");
@@ -6371,6 +6618,7 @@
/* map SMEM memory for IPA table accesses */
smem_addr = smem_alloc(SMEM_IPA_FILTER_TABLE, IPA_SMEM_SIZE,
SMEM_MODEM, 0);
+ q6_smem_size = IPA_SMEM_SIZE;
} else {
IPADBG("ipa q6 smem size = %d\n", q6_smem_size);
smem_addr = smem_alloc(SMEM_IPA_FILTER_TABLE, q6_smem_size,
@@ -6383,7 +6631,7 @@
phys_addr_t pa_p;
u32 size_p;
- IPA_SMMU_ROUND_TO_PAGE(iova, pa, IPA_SMEM_SIZE,
+ IPA_SMMU_ROUND_TO_PAGE(iova, pa, q6_smem_size,
iova_p, pa_p, size_p);
IPADBG("mapping 0x%lx to 0x%pa size %d\n",
iova_p, &pa_p, size_p);
diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_mhi.c b/drivers/platform/msm/ipa/ipa_v3/ipa_mhi.c
index c4eb731..cc15baa 100644
--- a/drivers/platform/msm/ipa/ipa_v3/ipa_mhi.c
+++ b/drivers/platform/msm/ipa/ipa_v3/ipa_mhi.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2015-2018 The Linux Foundation. All rights reserved.
+/* Copyright (c) 2015-2019 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
@@ -383,6 +383,7 @@
int res;
struct gsi_device_scratch gsi_scratch;
const struct ipa_gsi_ep_config *gsi_ep_info;
+ u32 ipa_mhi_max_ul_channels, ipa_mhi_max_dl_channels;
IPA_MHI_FUNC_ENTRY();
@@ -391,7 +392,16 @@
return -EINVAL;
}
- if ((IPA_MHI_MAX_UL_CHANNELS + IPA_MHI_MAX_DL_CHANNELS) >
+ ipa_mhi_max_ul_channels = IPA_MHI_MAX_UL_CHANNELS;
+ ipa_mhi_max_dl_channels = IPA_MHI_MAX_DL_CHANNELS;
+
+ /* In case of Auto-pcie config, MHI2_PROD and MHI2_CONS is used */
+ if (ipa3_ctx->ipa_config_is_auto == true) {
+ ipa_mhi_max_ul_channels++;
+ ipa_mhi_max_dl_channels++;
+ }
+
+ if ((ipa_mhi_max_ul_channels + ipa_mhi_max_dl_channels) >
((ipa3_ctx->mhi_evid_limits[1] -
ipa3_ctx->mhi_evid_limits[0]) + 1)) {
IPAERR("Not enough event rings for MHI\n");
diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_utils.c b/drivers/platform/msm/ipa/ipa_v3/ipa_utils.c
index 03b0c72..60af28d 100644
--- a/drivers/platform/msm/ipa/ipa_v3/ipa_utils.c
+++ b/drivers/platform/msm/ipa/ipa_v3/ipa_utils.c
@@ -1593,12 +1593,12 @@
IPA_DPS_HPS_SEQ_TYPE_DMA_ONLY,
QMB_MASTER_SELECT_DDR,
{ 5, 7, 20, 24, IPA_EE_AP, GSI_USE_PREFETCH_BUFS } },
- [IPA_4_0_AUTO][IPA_CLIENT_ODU_PROD] = {
- false, IPA_v4_0_GROUP_UL_DL,
+ [IPA_4_0_AUTO][IPA_CLIENT_Q6_CV2X_PROD] = {
+ true, IPA_v4_0_GROUP_CV2X,
true,
IPA_DPS_HPS_REP_SEQ_TYPE_2PKT_PROC_PASS_NO_DEC_UCP_DMAP,
QMB_MASTER_SELECT_DDR,
- { 1, 0, 8, 16, IPA_EE_AP, GSI_ESCAPE_BUF_ONLY } },
+ { 1, 2, 8, 16, IPA_EE_Q6, GSI_ESCAPE_BUF_ONLY } },
[IPA_4_0_AUTO][IPA_CLIENT_ETHERNET_PROD] = {
true, IPA_v4_0_GROUP_UL_DL,
true,
@@ -1606,13 +1606,13 @@
QMB_MASTER_SELECT_DDR,
{ 9, 0, 8, 16, IPA_EE_UC, GSI_USE_PREFETCH_BUFS } },
[IPA_4_0_AUTO][IPA_CLIENT_Q6_WAN_PROD] = {
- false, IPA_v4_0_GROUP_UL_DL,
+ true, IPA_v4_0_GROUP_UL_DL,
true,
IPA_DPS_HPS_SEQ_TYPE_2ND_PKT_PROCESS_PASS_NO_DEC_UCP,
QMB_MASTER_SELECT_DDR,
{ 3, 0, 16, 32, IPA_EE_Q6, GSI_USE_PREFETCH_BUFS } },
[IPA_4_0_AUTO][IPA_CLIENT_Q6_CMD_PROD] = {
- false, IPA_v4_0_GROUP_UL_DL,
+ true, IPA_v4_0_GROUP_UL_DL,
false,
IPA_DPS_HPS_SEQ_TYPE_PKT_PROCESS_NO_DEC_UCP,
QMB_MASTER_SELECT_DDR,
@@ -1711,23 +1711,23 @@
QMB_MASTER_SELECT_DDR,
{ 22, 1, 9, 9, IPA_EE_UC, GSI_USE_PREFETCH_BUFS } },
[IPA_4_0_AUTO][IPA_CLIENT_Q6_LAN_CONS] = {
- false, IPA_v4_0_GROUP_UL_DL,
+ true, IPA_v4_0_GROUP_UL_DL,
false,
IPA_DPS_HPS_SEQ_TYPE_INVALID,
QMB_MASTER_SELECT_DDR,
{ 14, 4, 9, 9, IPA_EE_Q6, GSI_USE_PREFETCH_BUFS } },
[IPA_4_0_AUTO][IPA_CLIENT_Q6_WAN_CONS] = {
- false, IPA_v4_0_GROUP_UL_DL,
+ true, IPA_v4_0_GROUP_UL_DL,
false,
IPA_DPS_HPS_SEQ_TYPE_INVALID,
QMB_MASTER_SELECT_DDR,
{ 13, 3, 9, 9, IPA_EE_Q6, GSI_USE_PREFETCH_BUFS } },
- [IPA_4_0_AUTO][IPA_CLIENT_Q6_LTE_WIFI_AGGR_CONS] = {
- false, IPA_v4_0_GROUP_UL_DL,
+ [IPA_4_0_AUTO][IPA_CLIENT_Q6_CV2X_CONS] = {
+ true, IPA_v4_0_GROUP_CV2X,
false,
IPA_DPS_HPS_SEQ_TYPE_INVALID,
QMB_MASTER_SELECT_DDR,
- { 16, 5, 9, 9, IPA_EE_Q6, GSI_USE_PREFETCH_BUFS } },
+ { 16, 5, 9, 9, IPA_EE_Q6, GSI_ESCAPE_BUF_ONLY } },
/* Only for test purpose */
/* MBIM aggregation test pipes should have the same QMB as USB_CONS */
[IPA_4_0_AUTO][IPA_CLIENT_TEST_CONS] = {
@@ -1799,14 +1799,20 @@
IPA_DPS_HPS_REP_SEQ_TYPE_2PKT_PROC_PASS_NO_DEC_UCP_DMAP,
QMB_MASTER_SELECT_DDR,
{ 9, 0, 8, 16, IPA_EE_UC, GSI_USE_PREFETCH_BUFS } },
+ [IPA_4_0_AUTO_MHI][IPA_CLIENT_Q6_CV2X_PROD] = {
+ true, IPA_v4_0_GROUP_CV2X,
+ true,
+ IPA_DPS_HPS_REP_SEQ_TYPE_2PKT_PROC_PASS_NO_DEC_UCP_DMAP,
+ QMB_MASTER_SELECT_DDR,
+ { 1, 2, 8, 16, IPA_EE_Q6, GSI_USE_PREFETCH_BUFS } },
[IPA_4_0_AUTO_MHI][IPA_CLIENT_Q6_WAN_PROD] = {
- false, IPA_v4_0_GROUP_UL_DL,
+ true, IPA_v4_0_GROUP_UL_DL,
true,
IPA_DPS_HPS_SEQ_TYPE_PKT_PROCESS_NO_DEC_UCP,
QMB_MASTER_SELECT_DDR,
{ 3, 0, 16, 32, IPA_EE_Q6, GSI_USE_PREFETCH_BUFS } },
[IPA_4_0_AUTO_MHI][IPA_CLIENT_Q6_CMD_PROD] = {
- false, IPA_v4_0_MHI_GROUP_PCIE,
+ true, IPA_v4_0_MHI_GROUP_PCIE,
false,
IPA_DPS_HPS_SEQ_TYPE_PKT_PROCESS_NO_DEC_UCP,
QMB_MASTER_SELECT_DDR,
@@ -1887,17 +1893,23 @@
QMB_MASTER_SELECT_DDR,
{ 22, 1, 9, 9, IPA_EE_UC, GSI_USE_PREFETCH_BUFS } },
[IPA_4_0_AUTO_MHI][IPA_CLIENT_Q6_LAN_CONS] = {
- false, IPA_v4_0_MHI_GROUP_DDR,
+ true, IPA_v4_0_MHI_GROUP_DDR,
false,
IPA_DPS_HPS_SEQ_TYPE_INVALID,
QMB_MASTER_SELECT_DDR,
{ 14, 4, 9, 9, IPA_EE_Q6, GSI_USE_PREFETCH_BUFS } },
[IPA_4_0_AUTO_MHI][IPA_CLIENT_Q6_WAN_CONS] = {
- false, IPA_v4_0_MHI_GROUP_DDR,
+ true, IPA_v4_0_MHI_GROUP_DDR,
false,
IPA_DPS_HPS_SEQ_TYPE_INVALID,
QMB_MASTER_SELECT_DDR,
{ 13, 3, 9, 9, IPA_EE_Q6, GSI_USE_PREFETCH_BUFS } },
+ [IPA_4_0_AUTO_MHI][IPA_CLIENT_Q6_CV2X_CONS] = {
+ true, IPA_v4_0_GROUP_CV2X,
+ false,
+ IPA_DPS_HPS_SEQ_TYPE_INVALID,
+ QMB_MASTER_SELECT_DDR,
+ { 16, 5, 9, 9, IPA_EE_Q6, GSI_ESCAPE_BUF_ONLY } },
[IPA_4_0_AUTO_MHI][IPA_CLIENT_MEMCPY_DMA_SYNC_CONS] = {
true, IPA_v4_0_MHI_GROUP_DMA,
false,
@@ -2076,6 +2088,7 @@
return false;
if (client == IPA_CLIENT_USB_CONS ||
+ client == IPA_CLIENT_USB2_CONS ||
client == IPA_CLIENT_USB_DPL_CONS ||
client == IPA_CLIENT_MHI_CONS ||
client == IPA_CLIENT_HSIC1_CONS ||
@@ -3856,6 +3869,7 @@
meta.qmap_id = param_in->qmap_id;
if (param_in->client == IPA_CLIENT_USB_PROD ||
+ param_in->client == IPA_CLIENT_USB2_PROD ||
param_in->client == IPA_CLIENT_HSIC1_PROD ||
param_in->client == IPA_CLIENT_ODU_PROD ||
param_in->client == IPA_CLIENT_ETHERNET_PROD) {
@@ -5412,8 +5426,8 @@
case IPA_4_0_AUTO_MHI:
src_rsrc_type_max = IPA_v4_0_RSRC_GRP_TYPE_SRC_MAX;
dst_rsrc_type_max = IPA_v4_0_RSRC_GRP_TYPE_DST_MAX;
- src_grp_idx_max = IPA_v4_0_GROUP_CV2X;
- dst_grp_idx_max = IPA_v4_0_GROUP_CV2X;
+ src_grp_idx_max = IPA_v4_0_SRC_GROUP_MAX;
+ dst_grp_idx_max = IPA_v4_0_DST_GROUP_MAX;
break;
default:
IPAERR("invalid hw type index\n");
diff --git a/drivers/platform/msm/ipa/ipa_v3/ipahal/ipahal_fltrt.c b/drivers/platform/msm/ipa/ipa_v3/ipahal/ipahal_fltrt.c
index 530adef..35d5efc 100644
--- a/drivers/platform/msm/ipa/ipa_v3/ipahal/ipahal_fltrt.c
+++ b/drivers/platform/msm/ipa/ipa_v3/ipahal/ipahal_fltrt.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012-2018, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2012-2019, 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
@@ -3479,6 +3479,7 @@
int ipahal_fltrt_allocate_hw_sys_tbl(struct ipa_mem_buffer *tbl_mem)
{
struct ipahal_fltrt_obj *obj;
+ gfp_t flag = GFP_KERNEL;
IPAHAL_DBG_LOW("Entry\n");
@@ -3496,10 +3497,14 @@
/* add word for rule-set terminator */
tbl_mem->size += obj->tbl_width;
-
+alloc:
tbl_mem->base = dma_alloc_coherent(ipahal_ctx->ipa_pdev, tbl_mem->size,
- &tbl_mem->phys_base, GFP_KERNEL);
+ &tbl_mem->phys_base, flag);
if (!tbl_mem->base) {
+ if (flag == GFP_KERNEL) {
+ flag = GFP_ATOMIC;
+ goto alloc;
+ }
IPAHAL_ERR("fail to alloc DMA buf of size %d\n",
tbl_mem->size);
return -ENOMEM;
diff --git a/drivers/platform/msm/ipa/ipa_v3/teth_bridge.c b/drivers/platform/msm/ipa/ipa_v3/teth_bridge.c
index 0f5c61e..566e7bf 100644
--- a/drivers/platform/msm/ipa/ipa_v3/teth_bridge.c
+++ b/drivers/platform/msm/ipa/ipa_v3/teth_bridge.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2013-2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2013-2017,2019, 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
@@ -38,6 +38,12 @@
#define TETH_ERR(fmt, args...) \
pr_err(TETH_BRIDGE_DRV_NAME " %s:%d " fmt, __func__, __LINE__, ## args)
+enum ipa_num_teth_iface {
+ IPA_TETH_IFACE_1 = 0,
+ IPA_TETH_IFACE_2 = 1,
+ IPA_TETH_IFACE_MAX
+};
+
/**
* struct ipa3_teth_bridge_ctx - Tethering bridge driver context information
* @class: kernel class pointer
@@ -50,7 +56,7 @@
dev_t dev_num;
struct device *dev;
struct cdev cdev;
- u32 modem_pm_hdl;
+ u32 modem_pm_hdl[IPA_TETH_IFACE_MAX];
};
static struct ipa3_teth_bridge_ctx *ipa3_teth_ctx;
@@ -120,21 +126,32 @@
int ipa3_teth_bridge_disconnect(enum ipa_client_type client)
{
int res = 0;
+ int *pm_hdl = NULL;
TETH_DBG_FUNC_ENTRY();
if (ipa_pm_is_used()) {
- res = ipa_pm_deactivate_sync(ipa3_teth_ctx->modem_pm_hdl);
+
+ if (client == IPA_CLIENT_USB2_PROD)
+ pm_hdl = &ipa3_teth_ctx->modem_pm_hdl[IPA_TETH_IFACE_2];
+ else
+ pm_hdl = &ipa3_teth_ctx->modem_pm_hdl[IPA_TETH_IFACE_1];
+
+ res = ipa_pm_deactivate_sync(*pm_hdl);
if (res) {
TETH_ERR("fail to deactivate modem %d\n", res);
return res;
}
- res = ipa_pm_deregister(ipa3_teth_ctx->modem_pm_hdl);
- ipa3_teth_ctx->modem_pm_hdl = ~0;
+ res = ipa_pm_deregister(*pm_hdl);
+ *pm_hdl = ~0;
} else {
- ipa_rm_delete_dependency(IPA_RM_RESOURCE_USB_PROD,
- IPA_RM_RESOURCE_Q6_CONS);
- ipa_rm_delete_dependency(IPA_RM_RESOURCE_Q6_PROD,
- IPA_RM_RESOURCE_USB_CONS);
+ if (client == IPA_CLIENT_USB2_PROD) {
+ TETH_ERR("No support for rm added/validated.\n");
+ } else {
+ ipa_rm_delete_dependency(IPA_RM_RESOURCE_USB_PROD,
+ IPA_RM_RESOURCE_Q6_CONS);
+ ipa_rm_delete_dependency(IPA_RM_RESOURCE_Q6_PROD,
+ IPA_RM_RESOURCE_USB_CONS);
+ }
}
TETH_DBG_FUNC_EXIT();
@@ -154,23 +171,37 @@
{
int res = 0;
struct ipa_pm_register_params reg_params;
+ u32 *pm = NULL;
memset(®_params, 0, sizeof(reg_params));
TETH_DBG_FUNC_ENTRY();
if (ipa_pm_is_used()) {
- reg_params.name = "MODEM (USB RMNET)";
+ if (connect_params->tethering_mode ==
+ TETH_TETHERING_MODE_RMNET_2) {
+ reg_params.name = "MODEM (USB RMNET_CV2X)";
+ pm = &ipa3_teth_ctx->modem_pm_hdl[IPA_TETH_IFACE_2];
+ } else {
+ reg_params.name = "MODEM (USB RMNET)";
+ pm = &ipa3_teth_ctx->modem_pm_hdl[IPA_TETH_IFACE_1];
+ }
reg_params.group = IPA_PM_GROUP_MODEM;
reg_params.skip_clk_vote = true;
res = ipa_pm_register(®_params,
- &ipa3_teth_ctx->modem_pm_hdl);
+ pm);
if (res) {
TETH_ERR("fail to register with PM %d\n", res);
return res;
}
- res = ipa_pm_activate_sync(ipa3_teth_ctx->modem_pm_hdl);
+ res = ipa_pm_activate_sync(*pm);
+ goto bail;
+ }
+
+ if (connect_params->tethering_mode == TETH_TETHERING_MODE_RMNET_2) {
+ res = -EINVAL;
+ TETH_ERR("No support for rm added/validated.\n");
goto bail;
}
@@ -225,7 +256,7 @@
*/
int ipa3_teth_bridge_driver_init(void)
{
- int res;
+ int res, i;
TETH_DBG("Tethering bridge driver init\n");
ipa3_teth_ctx = kzalloc(sizeof(*ipa3_teth_ctx), GFP_KERNEL);
@@ -266,7 +297,9 @@
goto fail_cdev_add;
}
- ipa3_teth_ctx->modem_pm_hdl = ~0;
+ for (i = 0; i < IPA_TETH_IFACE_MAX; i++)
+ ipa3_teth_ctx->modem_pm_hdl[i] = ~0;
+
TETH_DBG("Tethering bridge driver init OK\n");
return 0;
diff --git a/drivers/soc/qcom/icnss.c b/drivers/soc/qcom/icnss.c
index a2cbfd8..4691257 100644
--- a/drivers/soc/qcom/icnss.c
+++ b/drivers/soc/qcom/icnss.c
@@ -199,6 +199,8 @@
ICNSS_DRIVER_EVENT_UNREGISTER_DRIVER,
ICNSS_DRIVER_EVENT_PD_SERVICE_DOWN,
ICNSS_DRIVER_EVENT_FW_EARLY_CRASH_IND,
+ ICNSS_DRIVER_EVENT_IDLE_SHUTDOWN,
+ ICNSS_DRIVER_EVENT_IDLE_RESTART,
ICNSS_DRIVER_EVENT_MAX,
};
@@ -291,6 +293,7 @@
ICNSS_MODE_ON,
ICNSS_BLOCK_SHUTDOWN,
ICNSS_PDR,
+ ICNSS_MODEM_CRASHED,
};
struct ce_irq_list {
@@ -626,6 +629,10 @@
return "PD_SERVICE_DOWN";
case ICNSS_DRIVER_EVENT_FW_EARLY_CRASH_IND:
return "FW_EARLY_CRASH_IND";
+ case ICNSS_DRIVER_EVENT_IDLE_SHUTDOWN:
+ return "IDLE_SHUTDOWN";
+ case ICNSS_DRIVER_EVENT_IDLE_RESTART:
+ return "IDLE_RESTART";
case ICNSS_DRIVER_EVENT_MAX:
return "EVENT_MAX";
}
@@ -2361,6 +2368,7 @@
icnss_call_driver_shutdown(priv);
clear_bit(ICNSS_PDR, &priv->state);
+ clear_bit(ICNSS_MODEM_CRASHED, &priv->state);
clear_bit(ICNSS_REJUVENATE, &priv->state);
clear_bit(ICNSS_PD_RESTART, &priv->state);
priv->early_crash_ind = false;
@@ -2707,6 +2715,51 @@
return ret;
}
+static int icnss_driver_event_idle_shutdown(void *data)
+{
+ int ret = 0;
+
+ if (!penv->ops || !penv->ops->idle_shutdown)
+ return 0;
+
+ if (test_bit(ICNSS_MODEM_CRASHED, &penv->state) ||
+ test_bit(ICNSS_PDR, &penv->state) ||
+ test_bit(ICNSS_REJUVENATE, &penv->state)) {
+ icnss_pr_err("SSR/PDR is already in-progress during idle shutdown callback\n");
+ ret = -EBUSY;
+ } else {
+ icnss_pr_dbg("Calling driver idle shutdown, state: 0x%lx\n",
+ penv->state);
+ icnss_block_shutdown(true);
+ ret = penv->ops->idle_shutdown(&penv->pdev->dev);
+ icnss_block_shutdown(false);
+ }
+
+ return ret;
+}
+
+static int icnss_driver_event_idle_restart(void *data)
+{
+ int ret = 0;
+
+ if (!penv->ops || !penv->ops->idle_restart)
+ return 0;
+
+ if (test_bit(ICNSS_MODEM_CRASHED, &penv->state) ||
+ test_bit(ICNSS_PDR, &penv->state) ||
+ test_bit(ICNSS_REJUVENATE, &penv->state)) {
+ icnss_pr_err("SSR/PDR is already in-progress during idle restart callback\n");
+ ret = -EBUSY;
+ } else {
+ icnss_pr_dbg("Calling driver idle restart, state: 0x%lx\n",
+ penv->state);
+ icnss_block_shutdown(true);
+ ret = penv->ops->idle_restart(&penv->pdev->dev);
+ icnss_block_shutdown(false);
+ }
+
+ return ret;
+}
static void icnss_driver_event_work(struct work_struct *work)
{
@@ -2753,6 +2806,12 @@
ret = icnss_driver_event_early_crash_ind(penv,
event->data);
break;
+ case ICNSS_DRIVER_EVENT_IDLE_SHUTDOWN:
+ ret = icnss_driver_event_idle_shutdown(event->data);
+ break;
+ case ICNSS_DRIVER_EVENT_IDLE_RESTART:
+ ret = icnss_driver_event_idle_restart(event->data);
+ break;
default:
icnss_pr_err("Invalid Event type: %d", event->type);
kfree(event);
@@ -2861,6 +2920,9 @@
priv->is_ssr = true;
+ if (notif->crashed)
+ set_bit(ICNSS_MODEM_CRASHED, &priv->state);
+
if (code == SUBSYS_BEFORE_SHUTDOWN && !notif->crashed &&
test_bit(ICNSS_BLOCK_SHUTDOWN, &priv->state)) {
if (!wait_for_completion_timeout(&priv->unblock_shutdown,
@@ -3745,6 +3807,48 @@
}
EXPORT_SYMBOL(icnss_trigger_recovery);
+int icnss_idle_shutdown(struct device *dev)
+{
+ struct icnss_priv *priv = dev_get_drvdata(dev);
+
+ if (!priv) {
+ icnss_pr_err("Invalid drvdata: dev %pK", dev);
+ return -EINVAL;
+ }
+
+ if (test_bit(ICNSS_MODEM_CRASHED, &priv->state) ||
+ test_bit(ICNSS_PDR, &priv->state) ||
+ test_bit(ICNSS_REJUVENATE, &penv->state)) {
+ icnss_pr_err("SSR/PDR is already in-progress during idle shutdown\n");
+ return -EBUSY;
+ }
+
+ return icnss_driver_event_post(ICNSS_DRIVER_EVENT_IDLE_SHUTDOWN,
+ ICNSS_EVENT_SYNC_UNINTERRUPTIBLE, NULL);
+}
+EXPORT_SYMBOL(icnss_idle_shutdown);
+
+int icnss_idle_restart(struct device *dev)
+{
+ struct icnss_priv *priv = dev_get_drvdata(dev);
+
+ if (!priv) {
+ icnss_pr_err("Invalid drvdata: dev %pK", dev);
+ return -EINVAL;
+ }
+
+ if (test_bit(ICNSS_MODEM_CRASHED, &priv->state) ||
+ test_bit(ICNSS_PDR, &priv->state) ||
+ test_bit(ICNSS_REJUVENATE, &penv->state)) {
+ icnss_pr_err("SSR/PDR is already in-progress during idle restart\n");
+ return -EBUSY;
+ }
+
+ return icnss_driver_event_post(ICNSS_DRIVER_EVENT_IDLE_RESTART,
+ ICNSS_EVENT_SYNC_UNINTERRUPTIBLE, NULL);
+}
+EXPORT_SYMBOL(icnss_idle_restart);
+
static int icnss_smmu_init(struct icnss_priv *priv)
{
@@ -4241,6 +4345,10 @@
continue;
case ICNSS_PDR:
seq_puts(s, "PDR TRIGGERED");
+ continue;
+ case ICNSS_MODEM_CRASHED:
+ seq_puts(s, "MODEM CRASHED");
+ continue;
}
seq_printf(s, "UNKNOWN-%d", i);
diff --git a/drivers/staging/android/lowmemorykiller.c b/drivers/staging/android/lowmemorykiller.c
index ecae4c9..d54e68b 100644
--- a/drivers/staging/android/lowmemorykiller.c
+++ b/drivers/staging/android/lowmemorykiller.c
@@ -98,6 +98,153 @@
pr_info(x); \
} while (0)
+
+static DECLARE_WAIT_QUEUE_HEAD(event_wait);
+static DEFINE_SPINLOCK(lmk_event_lock);
+static struct circ_buf event_buffer;
+#define MAX_BUFFERED_EVENTS 8
+#define MAX_TASKNAME 128
+
+struct lmk_event {
+ char taskname[MAX_TASKNAME];
+ pid_t pid;
+ uid_t uid;
+ pid_t group_leader_pid;
+ unsigned long min_flt;
+ unsigned long maj_flt;
+ unsigned long rss_in_pages;
+ short oom_score_adj;
+ short min_score_adj;
+ unsigned long long start_time;
+ struct list_head list;
+};
+
+void handle_lmk_event(struct task_struct *selected, int selected_tasksize,
+ short min_score_adj)
+{
+ int head;
+ int tail;
+ struct lmk_event *events;
+ struct lmk_event *event;
+ int res;
+ char taskname[MAX_TASKNAME];
+
+ res = get_cmdline(selected, taskname, MAX_TASKNAME - 1);
+
+ /* No valid process name means this is definitely not associated with a
+ * userspace activity.
+ */
+
+ if (res <= 0 || res >= MAX_TASKNAME)
+ return;
+
+ taskname[res] = '\0';
+
+ spin_lock(&lmk_event_lock);
+
+ head = event_buffer.head;
+ tail = READ_ONCE(event_buffer.tail);
+
+ /* Do not continue to log if no space remains in the buffer. */
+ if (CIRC_SPACE(head, tail, MAX_BUFFERED_EVENTS) < 1) {
+ spin_unlock(&lmk_event_lock);
+ return;
+ }
+
+ events = (struct lmk_event *) event_buffer.buf;
+ event = &events[head];
+
+ memcpy(event->taskname, taskname, res + 1);
+
+ event->pid = selected->pid;
+ event->uid = from_kuid_munged(current_user_ns(), task_uid(selected));
+ if (selected->group_leader)
+ event->group_leader_pid = selected->group_leader->pid;
+ else
+ event->group_leader_pid = -1;
+ event->min_flt = selected->min_flt;
+ event->maj_flt = selected->maj_flt;
+ event->oom_score_adj = selected->signal->oom_score_adj;
+ event->start_time = nsec_to_clock_t(selected->real_start_time);
+ event->rss_in_pages = selected_tasksize;
+ event->min_score_adj = min_score_adj;
+
+ event_buffer.head = (head + 1) & (MAX_BUFFERED_EVENTS - 1);
+
+ spin_unlock(&lmk_event_lock);
+
+ wake_up_interruptible(&event_wait);
+}
+
+static int lmk_event_show(struct seq_file *s, void *unused)
+{
+ struct lmk_event *events = (struct lmk_event *) event_buffer.buf;
+ int head;
+ int tail;
+ struct lmk_event *event;
+
+ spin_lock(&lmk_event_lock);
+
+ head = event_buffer.head;
+ tail = event_buffer.tail;
+
+ if (head == tail) {
+ spin_unlock(&lmk_event_lock);
+ return -EAGAIN;
+ }
+
+ event = &events[tail];
+
+ seq_printf(s, "%lu %lu %lu %lu %lu %lu %hd %hd %llu\n%s\n",
+ (unsigned long) event->pid, (unsigned long) event->uid,
+ (unsigned long) event->group_leader_pid, event->min_flt,
+ event->maj_flt, event->rss_in_pages, event->oom_score_adj,
+ event->min_score_adj, event->start_time, event->taskname);
+
+ event_buffer.tail = (tail + 1) & (MAX_BUFFERED_EVENTS - 1);
+
+ spin_unlock(&lmk_event_lock);
+ return 0;
+}
+
+static unsigned int lmk_event_poll(struct file *file, poll_table *wait)
+{
+ int ret = 0;
+
+ poll_wait(file, &event_wait, wait);
+ spin_lock(&lmk_event_lock);
+ if (event_buffer.head != event_buffer.tail)
+ ret = POLLIN;
+ spin_unlock(&lmk_event_lock);
+ return ret;
+}
+
+static int lmk_event_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, lmk_event_show, inode->i_private);
+}
+
+static const struct file_operations event_file_ops = {
+ .open = lmk_event_open,
+ .poll = lmk_event_poll,
+ .read = seq_read
+};
+
+static void lmk_event_init(void)
+{
+ struct proc_dir_entry *entry;
+
+ event_buffer.head = 0;
+ event_buffer.tail = 0;
+ event_buffer.buf = kmalloc(
+ sizeof(struct lmk_event) * MAX_BUFFERED_EVENTS, GFP_KERNEL);
+ if (!event_buffer.buf)
+ return;
+ entry = proc_create("lowmemorykiller", 0, NULL, &event_file_ops);
+ if (!entry)
+ pr_err("error creating kernel lmk event file\n");
+}
+
static unsigned long lowmem_count(struct shrinker *s,
struct shrink_control *sc)
{
@@ -635,6 +782,7 @@
lowmem_deathpending_timeout = jiffies + HZ;
rem += selected_tasksize;
rcu_read_unlock();
+ get_task_struct(selected);
/* give the system time to free up the memory */
msleep_interruptible(20);
trace_almk_shrink(selected_tasksize, ret,
@@ -649,6 +797,10 @@
sc->nr_to_scan, sc->gfp_mask, rem);
mutex_unlock(&scan_mutex);
+ if (selected) {
+ handle_lmk_event(selected, selected_tasksize, min_score_adj);
+ put_task_struct(selected);
+ }
return rem;
}
@@ -662,6 +814,7 @@
{
register_shrinker(&lowmem_shrinker);
vmpressure_notifier_register(&lmk_vmpr_nb);
+ lmk_event_init();
return 0;
}
device_initcall(lowmem_init);
diff --git a/drivers/usb/dwc3/dwc3-msm.c b/drivers/usb/dwc3/dwc3-msm.c
index a2a4438..83df750 100644
--- a/drivers/usb/dwc3/dwc3-msm.c
+++ b/drivers/usb/dwc3/dwc3-msm.c
@@ -71,6 +71,12 @@
module_param(cpu_to_affin, int, S_IRUGO|S_IWUSR);
MODULE_PARM_DESC(cpu_to_affin, "affin usb irq to this cpu");
+/* Interrupt moderation for normal endpoints */
+static unsigned int dwc3_gadget_imod_val;
+module_param(dwc3_gadget_imod_val, int, 0644);
+MODULE_PARM_DESC(dwc3_gadget_imod_val,
+ "Interrupt moderation in usecs for normal EPs");
+
/* XHCI registers */
#define USB3_HCSPARAMS1 (0x4)
#define USB3_HCCPARAMS2 (0x1c)
@@ -120,6 +126,12 @@
#define GSI_RING_BASE_ADDR_L(n) ((QSCRATCH_REG_OFFSET + 0x130) + (n*4))
#define GSI_RING_BASE_ADDR_H(n) ((QSCRATCH_REG_OFFSET + 0x144) + (n*4))
+#define IMOD(n) ((QSCRATCH_REG_OFFSET + 0x170) + (n*4))
+#define IMOD_EE_EN_MASK BIT(12)
+#define IMOD_EE_CNT_MASK 0x7FF
+
+#define USEC_CNT (QSCRATCH_REG_OFFSET + 0x180)
+
#define GSI_IF_STS (QSCRATCH_REG_OFFSET + 0x1A4)
#define GSI_WR_CTRL_STATE_MASK BIT(15)
@@ -4405,9 +4417,22 @@
msm_dwc3_perf_vote_update(mdwc, true);
schedule_delayed_work(&mdwc->perf_vote_work,
msecs_to_jiffies(1000 * PM_QOS_SAMPLE_SEC));
+ if (dwc3_gadget_imod_val > 0) {
+ dwc3_msm_write_reg(mdwc->base, USEC_CNT, 0x7D);
+ dwc3_msm_write_reg_field(mdwc->base, IMOD(0),
+ IMOD_EE_CNT_MASK,
+ dwc3_gadget_imod_val);
+ dwc3_msm_write_reg_field(mdwc->base, IMOD(0),
+ IMOD_EE_EN_MASK, 0x1);
+ }
} else {
dev_dbg(mdwc->dev, "%s: turn off gadget %s\n",
__func__, dwc->gadget.name);
+ dwc3_msm_write_reg_field(mdwc->base, IMOD(0),
+ IMOD_EE_EN_MASK, 0x0);
+ dwc3_msm_write_reg_field(mdwc->base, IMOD(0),
+ IMOD_EE_CNT_MASK, 0x0);
+ dwc3_msm_write_reg(mdwc->base, USEC_CNT, 0x0);
cancel_delayed_work_sync(&mdwc->perf_vote_work);
msm_dwc3_perf_vote_update(mdwc, false);
pm_qos_remove_request(&mdwc->pm_qos_req_dma);
diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c
index 1235287..8438999 100644
--- a/drivers/usb/dwc3/gadget.c
+++ b/drivers/usb/dwc3/gadget.c
@@ -744,6 +744,21 @@
dwc3_gadget_giveback(dep, req, -ESHUTDOWN);
}
+
+ if (dep->number == 1 && dwc->ep0state != EP0_SETUP_PHASE) {
+ unsigned int dir;
+
+ dbg_log_string("CTRLPEND %d", dwc->ep0state);
+ dir = !!dwc->ep0_expect_in;
+ if (dwc->ep0state == EP0_DATA_PHASE)
+ dwc3_ep0_end_control_data(dwc, dwc->eps[dir]);
+ else
+ dwc3_ep0_end_control_data(dwc, dwc->eps[!dir]);
+
+ dwc->eps[0]->trb_enqueue = 0;
+ dwc->eps[1]->trb_enqueue = 0;
+ }
+
dbg_log_string("DONE for %s(%d)", dep->name, dep->number);
}
diff --git a/drivers/usb/gadget/function/f_gsi.c b/drivers/usb/gadget/function/f_gsi.c
index d58b20a..1cd612a 100644
--- a/drivers/usb/gadget/function/f_gsi.c
+++ b/drivers/usb/gadget/function/f_gsi.c
@@ -535,6 +535,7 @@
{
int ret;
struct f_gsi *gsi = d_port_to_gsi(d_port);
+ struct f_gsi *gsi_rmnet_v2x = __gsi[USB_PROT_RMNET_V2X_IPA];
struct ipa_usb_xdci_chan_params *in_params =
&d_port->ipa_in_channel_params;
struct ipa_usb_xdci_chan_params *out_params =
@@ -663,6 +664,23 @@
gsi_channel_info.depcmd_hi_addr;
}
+ /*
+ * When both RmNet LTE and V2X instances are enabled in a composition,
+ * set 'is_sw_path' flag to true for LTE, so that IPA can ignore the
+ * dummy address for GEVENTCOUNT register.
+ */
+ in_params->is_sw_path = false;
+ if (gsi->prot_id == USB_PROT_RMNET_IPA &&
+ gsi_rmnet_v2x->function.fs_descriptors)
+ in_params->is_sw_path = true;
+
+ if (d_port->out_ep) {
+ out_params->is_sw_path = false;
+ if (gsi->prot_id == USB_PROT_RMNET_IPA &&
+ gsi_rmnet_v2x->function.fs_descriptors)
+ out_params->is_sw_path = true;
+ }
+
/* Populate connection params */
conn_params->max_pkt_size =
(cdev->gadget->speed == USB_SPEED_SUPER) ?
@@ -2206,7 +2224,6 @@
queue_work(gsi->c_port.uevent_wq,
&gsi->c_port.uevent_work);
- gsi_ctrl_send_cpkt_tomodem(gsi, NULL, 0);
value = 0;
break;
case ((USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8)
@@ -2415,6 +2432,7 @@
unsigned int alt)
{
struct f_gsi *gsi = func_to_gsi(f);
+ struct f_gsi *gsi_rmnet_v2x = __gsi[USB_PROT_RMNET_V2X_IPA];
struct usb_composite_dev *cdev = f->config->cdev;
struct net_device *net;
int ret;
@@ -2489,12 +2507,18 @@
goto notify_ep_disable;
}
- /* Configure EPs for GSI */
+ /*
+ * Configure EPs for GSI. Note that when both RmNet LTE
+ * and V2X instances are enabled in a composition,
+ * configure HW accelerated EPs for V2X instance and
+ * normal EPs for LTE.
+ */
if (gsi->d_port.in_ep &&
gsi->prot_id <= USB_PROT_RMNET_V2X_IPA) {
if (gsi->prot_id == USB_PROT_DIAG_IPA)
gsi->d_port.in_ep->ep_intr_num = 3;
- else if (gsi->prot_id == USB_PROT_RMNET_V2X_IPA)
+ else if (gsi->prot_id == USB_PROT_RMNET_IPA &&
+ gsi_rmnet_v2x->function.fs_descriptors)
gsi->d_port.in_ep->ep_intr_num = 0;
else
gsi->d_port.in_ep->ep_intr_num = 2;
@@ -2505,7 +2529,8 @@
if (gsi->d_port.out_ep &&
gsi->prot_id <= USB_PROT_RMNET_V2X_IPA) {
- if (gsi->prot_id == USB_PROT_RMNET_V2X_IPA)
+ if (gsi->prot_id == USB_PROT_RMNET_IPA &&
+ gsi_rmnet_v2x->function.fs_descriptors)
gsi->d_port.out_ep->ep_intr_num = 0;
else
gsi->d_port.out_ep->ep_intr_num = 1;
@@ -2575,7 +2600,10 @@
if (gsi->prot_id == USB_PROT_DIAG_IPA ||
gsi->prot_id == USB_PROT_DPL_ETHER ||
gsi->prot_id == USB_PROT_GPS_CTRL ||
- gsi->prot_id == USB_PROT_MBIM_IPA)
+ gsi->prot_id == USB_PROT_MBIM_IPA ||
+ gsi->prot_id == USB_PROT_RMNET_IPA ||
+ gsi->prot_id == USB_PROT_RMNET_V2X_IPA ||
+ gsi->prot_id == USB_PROT_RMNET_ETHER)
gsi_ctrl_send_cpkt_tomodem(gsi, NULL, 0);
if (gsi->c_port.uevent_wq)
diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c
index 8e710e0..5578aa8 100644
--- a/fs/ext4/inode.c
+++ b/fs/ext4/inode.c
@@ -3624,8 +3624,9 @@
ssize_t ret;
int rw = iov_iter_rw(iter);
-#ifdef CONFIG_FS_ENCRYPTION
- if (IS_ENCRYPTED(inode) && S_ISREG(inode->i_mode))
+#ifdef CONFIG_EXT4_FS_ENCRYPTION
+ if (IS_ENCRYPTED(inode) && S_ISREG(inode->i_mode)
+ && !fscrypt_using_hardware_encryption(inode))
return 0;
#endif
diff --git a/include/linux/bluetooth-power.h b/include/linux/bluetooth-power.h
index 901dae0..b4e2128 100644
--- a/include/linux/bluetooth-power.h
+++ b/include/linux/bluetooth-power.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2013-2018, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2013-2019, 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
@@ -88,4 +88,7 @@
#define BT_CMD_SLIM_TEST 0xbfac
#define BT_CMD_PWR_CTRL 0xbfad
#define BT_CMD_CHIPSET_VERS 0xbfae
+
+#define BT_RESET_GPIO_HIGH_VAL 0x1
+
#endif /* __LINUX_BLUETOOTH_POWER_H */
diff --git a/include/linux/ipa.h b/include/linux/ipa.h
index 2c31666..b0828a0 100644
--- a/include/linux/ipa.h
+++ b/include/linux/ipa.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012-2018, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2012-2019, 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
@@ -754,6 +754,7 @@
enum teth_tethering_mode {
TETH_TETHERING_MODE_RMNET,
TETH_TETHERING_MODE_MBIM,
+ TETH_TETHERING_MODE_RMNET_2,
TETH_TETHERING_MODE_MAX,
};
diff --git a/include/linux/ipa_usb.h b/include/linux/ipa_usb.h
index cceae83..9dbdb9f 100644
--- a/include/linux/ipa_usb.h
+++ b/include/linux/ipa_usb.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012-2018, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2012-2019, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -19,9 +19,16 @@
IPA_USB_RMNET = 2,
IPA_USB_MBIM = 3,
IPA_USB_DIAG = 4,
+ IPA_USB_RMNET_CV2X = 5,
IPA_USB_MAX_TETH_PROT_SIZE
};
+enum teth_bridge_params {
+ IPA_TETH_BRIDGE_1 = 0,
+ IPA_TETH_BRIDGE_2 = 1,
+ IPA_TETH_BRIDGE_MAX
+};
+
/**
* ipa_usb_teth_params - parameters for RDNIS/ECM initialization API
*
@@ -151,6 +158,7 @@
u64 data_buff_base_addr_iova;
struct sg_table *sgt_xfer_rings;
struct sg_table *sgt_data_buff;
+ bool is_sw_path;
};
/**
diff --git a/include/soc/qcom/icnss.h b/include/soc/qcom/icnss.h
index e55353a..bff4d3f 100644
--- a/include/soc/qcom/icnss.h
+++ b/include/soc/qcom/icnss.h
@@ -51,6 +51,8 @@
int (*resume_noirq)(struct device *dev);
int (*uevent)(struct device *dev, struct icnss_uevent_data *uevent);
int (*set_therm_state)(struct device *dev, unsigned long thermal_state);
+ int (*idle_shutdown)(struct device *dev);
+ int (*idle_restart)(struct device *dev);
};
@@ -151,4 +153,6 @@
extern void icnss_thermal_unregister(struct device *dev);
extern int icnss_get_curr_therm_state(struct device *dev,
unsigned long *thermal_state);
+extern int icnss_idle_restart(struct device *dev);
+extern int icnss_idle_shutdown(struct device *dev);
#endif /* _ICNSS_WLAN_H_ */
diff --git a/include/uapi/linux/msm_ipa.h b/include/uapi/linux/msm_ipa.h
index e64942a..73d65a6 100644
--- a/include/uapi/linux/msm_ipa.h
+++ b/include/uapi/linux/msm_ipa.h
@@ -107,7 +107,7 @@
#define IPA_IOCTL_DEL_BRIDGE_VLAN_MAPPING 60
#define IPA_IOCTL_GSB_CONNECT 61
#define IPA_IOCTL_GSB_DISCONNECT 62
-
+#define IPA_IOCTL_GET_PHERIPHERAL_EP_INFO 63
/**
@@ -334,11 +334,14 @@
IPA_CLIENT_MHI2_PROD = 86,
IPA_CLIENT_MHI2_CONS = 87,
+
+ IPA_CLIENT_Q6_CV2X_PROD = 88,
+ IPA_CLIENT_Q6_CV2X_CONS = 89,
};
#define IPA_CLIENT_DUMMY_CONS IPA_CLIENT_DUMMY_CONS1
#define IPA_CLIENT_WIGIG4_CONS IPA_CLIENT_WIGIG4_CONS
-#define IPA_CLIENT_MAX (IPA_CLIENT_MHI2_CONS + 1)
+#define IPA_CLIENT_MAX (IPA_CLIENT_Q6_CV2X_CONS + 1)
#define IPA_CLIENT_IS_APPS_CONS(client) \
((client) == IPA_CLIENT_APPS_LAN_CONS || \
@@ -367,20 +370,23 @@
(client) == IPA_CLIENT_Q6_DUN_CONS || \
(client) == IPA_CLIENT_Q6_DECOMP_CONS || \
(client) == IPA_CLIENT_Q6_DECOMP2_CONS || \
- (client) == IPA_CLIENT_Q6_LTE_WIFI_AGGR_CONS)
+ (client) == IPA_CLIENT_Q6_LTE_WIFI_AGGR_CONS || \
+ (client) == IPA_CLIENT_Q6_CV2X_CONS)
#define IPA_CLIENT_IS_Q6_PROD(client) \
((client) == IPA_CLIENT_Q6_LAN_PROD || \
(client) == IPA_CLIENT_Q6_WAN_PROD || \
(client) == IPA_CLIENT_Q6_CMD_PROD || \
(client) == IPA_CLIENT_Q6_DECOMP_PROD || \
- (client) == IPA_CLIENT_Q6_DECOMP2_PROD)
+ (client) == IPA_CLIENT_Q6_DECOMP2_PROD || \
+ (client) == IPA_CLIENT_Q6_CV2X_PROD)
#define IPA_CLIENT_IS_Q6_NON_ZIP_CONS(client) \
((client) == IPA_CLIENT_Q6_LAN_CONS || \
(client) == IPA_CLIENT_Q6_WAN_CONS || \
(client) == IPA_CLIENT_Q6_DUN_CONS || \
- (client) == IPA_CLIENT_Q6_LTE_WIFI_AGGR_CONS)
+ (client) == IPA_CLIENT_Q6_LTE_WIFI_AGGR_CONS || \
+ (client) == IPA_CLIENT_Q6_CV2X_CONS)
#define IPA_CLIENT_IS_Q6_ZIP_CONS(client) \
((client) == IPA_CLIENT_Q6_DECOMP_CONS || \
@@ -389,7 +395,8 @@
#define IPA_CLIENT_IS_Q6_NON_ZIP_PROD(client) \
((client) == IPA_CLIENT_Q6_LAN_PROD || \
(client) == IPA_CLIENT_Q6_WAN_PROD || \
- (client) == IPA_CLIENT_Q6_CMD_PROD)
+ (client) == IPA_CLIENT_Q6_CMD_PROD || \
+ (client) == IPA_CLIENT_Q6_CV2X_PROD)
#define IPA_CLIENT_IS_Q6_ZIP_PROD(client) \
((client) == IPA_CLIENT_Q6_DECOMP_PROD || \
@@ -411,6 +418,8 @@
#define IPA_CLIENT_IS_MHI(client) \
((client) == IPA_CLIENT_MHI_CONS || \
(client) == IPA_CLIENT_MHI_PROD || \
+ (client) == IPA_CLIENT_MHI2_PROD || \
+ (client) == IPA_CLIENT_MHI2_CONS || \
(client) == IPA_CLIENT_MHI_DPL_CONS)
#define IPA_CLIENT_IS_TEST_PROD(client) \
@@ -1750,6 +1759,46 @@
char name[IPA_RESOURCE_NAME_MAX];
};
+#define QUERY_MAX_EP_PAIRS 2
+
+#define IPA_USB0_EP_ID 11
+#define IPA_USB1_EP_ID 12
+
+#define IPA_PCIE0_EP_ID 21
+#define IPA_PCIE1_EP_ID 22
+
+enum ipa_peripheral_ep_type {
+ IPA_DATA_EP_TYP_RESERVED = 0,
+ IPA_DATA_EP_TYP_HSIC = 1,
+ IPA_DATA_EP_TYP_HSUSB = 2,
+ IPA_DATA_EP_TYP_PCIE = 3,
+ IPA_DATA_EP_TYP_EMBEDDED = 4,
+ IPA_DATA_EP_TYP_BAM_DMUX,
+};
+
+struct ipa_ep_pair_info {
+ uint32_t consumer_pipe_num;
+ uint32_t producer_pipe_num;
+ uint32_t ep_id;
+};
+
+/**
+ * struct ipa_ioc_get_ep_info - flt/rt counter id query
+ * @ep_type: type USB/PCIE - i/p param
+ * @max_ep_pairs: max number of ep_pairs (constant),
+ (QUERY_MAX_EP_PAIRS)
+ * @num_ep_pairs: number of ep_pairs - o/p param
+ * @ep_pair_size: sizeof(ipa_ep_pair_info) * max_ep_pairs
+ * @info: structure contains ep pair info
+ */
+struct ipa_ioc_get_ep_info {
+ enum ipa_peripheral_ep_type ep_type;
+ uint8_t max_ep_pairs;
+ uint8_t num_ep_pairs;
+ uint32_t ep_pair_size;
+ uintptr_t info;
+};
+
/**
* struct ipa_msg_meta - Format of the message meta-data.
* @msg_type: the type of the message
@@ -2191,6 +2240,10 @@
IPA_IOCTL_GSB_DISCONNECT, \
struct ipa_ioc_gsb_info)
+#define IPA_IOC_GET_PHERIPHERAL_EP_INFO _IOWR(IPA_IOC_MAGIC, \
+ IPA_IOCTL_GET_PHERIPHERAL_EP_INFO, \
+ struct ipa_ioc_get_ep_info)
+
/*
* unique magic number of the Tethering bridge ioctls
*/
diff --git a/kernel/time/hrtimer.c b/kernel/time/hrtimer.c
index 11e4af2f..d471752 100644
--- a/kernel/time/hrtimer.c
+++ b/kernel/time/hrtimer.c
@@ -49,6 +49,7 @@
#include <linux/sched/deadline.h>
#include <linux/timer.h>
#include <linux/freezer.h>
+#include <linux/delay.h>
#include <asm/uaccess.h>
@@ -149,6 +150,7 @@
raw_spin_unlock_irqrestore(&base->cpu_base->lock, *flags);
}
cpu_relax();
+ ndelay(TIMER_LOCK_TIGHT_LOOP_DELAY_NS);
}
}
@@ -1043,6 +1045,7 @@
if (ret >= 0)
return ret;
cpu_relax();
+ ndelay(TIMER_LOCK_TIGHT_LOOP_DELAY_NS);
}
}
EXPORT_SYMBOL_GPL(hrtimer_cancel);
diff --git a/kernel/time/tick-internal.h b/kernel/time/tick-internal.h
index f7382510..0059764 100644
--- a/kernel/time/tick-internal.h
+++ b/kernel/time/tick-internal.h
@@ -165,3 +165,4 @@
extern u64 get_next_timer_interrupt(unsigned long basej, u64 basem);
void timer_clear_idle(void);
+#define TIMER_LOCK_TIGHT_LOOP_DELAY_NS 350
diff --git a/kernel/time/timer.c b/kernel/time/timer.c
index af9a29e..018e2f5 100644
--- a/kernel/time/timer.c
+++ b/kernel/time/timer.c
@@ -942,6 +942,7 @@
spin_unlock_irqrestore(&base->lock, *flags);
}
cpu_relax();
+ ndelay(TIMER_LOCK_TIGHT_LOOP_DELAY_NS);
}
}
@@ -1264,6 +1265,7 @@
if (ret >= 0)
return ret;
cpu_relax();
+ ndelay(TIMER_LOCK_TIGHT_LOOP_DELAY_NS);
}
}
EXPORT_SYMBOL(del_timer_sync);