Merge "Merge android-4.9.95 (b4c14c2) into msm-4.9"
diff --git a/Documentation/devicetree/bindings/iio/imu/invn-icm20602.txt b/Documentation/devicetree/bindings/iio/imu/invn-icm20602.txt
new file mode 100644
index 0000000..27c304e
--- /dev/null
+++ b/Documentation/devicetree/bindings/iio/imu/invn-icm20602.txt
@@ -0,0 +1,28 @@
+The ICM20602 sensor is 6-axis gyroscope+accelerometer combo
+device which is made by InvenSense Inc.
+
+Required properties:
+
+ - compatible : Should be "invensense,icm20602".
+ - reg : the I2C address which depends on the AD0 pin.
+ - interrupt-parent : should be the phandle for the interrupt controller
+ - interrupts : interrupt mapping for GPIO IRQ
+
+Optional properties:
+ - invensense,icm20602-gpio: the irq gpio. This is not used if
+ using SMD to trigger. this is needed only if using the
+ device IRQ to trigger. Only using SMD to trigger can
+ support 8K sample rate.
+
+Example:
+ icm20602-i2c@068 {
+ compatible = "invensense,icm20602";
+ reg = <0x68>;
+ interrupt-parent = <&msm_gpio>;
+ interrupts = <13 0>;
+ invensense,icm20602-gpio = <&msm_gpio 13 0x0>;
+ pinctrl-names = "imu_active","imu_suspend";
+ pinctrl-0 = <&imu_int_active>;
+ pinctrl-1 = <&imu_int_suspend>;
+ status = "okay";
+ };
diff --git a/Documentation/devicetree/bindings/power/supply/qcom/qpnp-qg.txt b/Documentation/devicetree/bindings/power/supply/qcom/qpnp-qg.txt
index efa67f5..afeb65d 100644
--- a/Documentation/devicetree/bindings/power/supply/qcom/qpnp-qg.txt
+++ b/Documentation/devicetree/bindings/power/supply/qcom/qpnp-qg.txt
@@ -191,6 +191,77 @@
temperature specific configuration as applied. If not
specified, the default value is 0 degree centigrade.
+- qcom,cl-disable
+ Usage: optional
+ Value type: <empty>
+ Definition: A boolean property to disable the battery capacity
+ learning when charging.
+
+- qcom,cl-feedback-on
+ Usage: optional
+ Value type: <empty>
+ Definition: A boolean property to feedback the learned capacity into
+ the capacity lerning algorithm. This has to be used only if the
+ property "qcom,cl-disable" is not specified.
+
+- qcom,cl-max-start-soc
+ Usage: optional
+ Value type: <u32>
+ Definition: Battery SOC has to be below or equal to this value at the
+ start of a charge cycle to start the capacity learning.
+ If this is not specified, then the default value used
+ will be 15. Unit is in percentage.
+
+- qcom,cl-min-start-soc
+ Usage: optional
+ Value type: <u32>
+ Definition: Battery SOC has to be above or equal to this value at the
+ start of a charge cycle to start the capacity learning.
+ If this is not specified, then the default value used
+ will be 10. Unit is in percentage.
+
+- qcom,cl-min-temp
+ Usage: optional
+ Value type: <u32>
+ Definition: Lower limit of battery temperature to start the capacity
+ learning. If this is not specified, then the default value
+ used will be 150 (15 C). Unit is in decidegC.
+
+- qcom,cl-max-temp
+ Usage: optional
+ Value type: <u32>
+ Definition: Upper limit of battery temperature to start the capacity
+ learning. If this is not specified, then the default value
+ used will be 500 (50 C). Unit is in decidegC.
+
+- qcom,cl-max-increment
+ Usage: optional
+ Value type: <u32>
+ Definition: Maximum capacity increment allowed per capacity learning
+ cycle. If this is not specified, then the default value
+ used will be 5 (0.5%). Unit is in decipercentage.
+
+- qcom,cl-max-decrement
+ Usage: optional
+ Value type: <u32>
+ Definition: Maximum capacity decrement allowed per capacity learning
+ cycle. If this is not specified, then the default value
+ used will be 100 (10%). Unit is in decipercentage.
+
+- qcom,cl-min-limit
+ Usage: optional
+ Value type: <u32>
+ Definition: Minimum limit that the capacity cannot go below in a
+ capacity learning cycle. If this is not specified, then
+ the default value is 0. Unit is in decipercentage.
+
+- qcom,cl-max-limit
+ Usage: optional
+ Value type: <u32>
+ Definition: Maximum limit that the capacity cannot go above in a
+ capacity learning cycle. If this is not specified, then
+ the default value is 0. Unit is in decipercentage.
+
==========================================================
Second Level Nodes - Peripherals managed by QGAUGE driver
==========================================================
diff --git a/arch/arm/boot/dts/qcom/sdxpoorwills.dtsi b/arch/arm/boot/dts/qcom/sdxpoorwills.dtsi
index c6608a8..68a13ee 100644
--- a/arch/arm/boot/dts/qcom/sdxpoorwills.dtsi
+++ b/arch/arm/boot/dts/qcom/sdxpoorwills.dtsi
@@ -1075,6 +1075,12 @@
#mbox-cells = <1>;
};
+ aop-msg-client {
+ compatible = "qcom,debugfs-qmp-client";
+ mboxes = <&qmp_aop 0>;
+ mbox-names = "aop";
+ };
+
vbus_detect: qcom,pmd-vbus-det {
compatible = "qcom,pmd-vbus-det";
interrupt-parent = <&spmi_bus>;
diff --git a/arch/arm/configs/msm8909-perf_defconfig b/arch/arm/configs/msm8909-perf_defconfig
index aee8104..9f5001f 100644
--- a/arch/arm/configs/msm8909-perf_defconfig
+++ b/arch/arm/configs/msm8909-perf_defconfig
@@ -259,7 +259,9 @@
CONFIG_PPP_ASYNC=y
CONFIG_PPP_SYNC_TTY=y
CONFIG_WCNSS_MEM_PRE_ALLOC=y
-CONFIG_CLD_LL_CORE=y
+CONFIG_CNSS=y
+CONFIG_CNSS_SDIO=y
+CONFIG_CLD_HL_SDIO_CORE=y
CONFIG_INPUT_EVDEV=y
CONFIG_KEYBOARD_GPIO=y
CONFIG_INPUT_JOYSTICK=y
@@ -423,6 +425,7 @@
CONFIG_MSM_EVENT_TIMER=y
CONFIG_QTI_RPM_STATS_LOG=y
CONFIG_QCOM_FORCE_WDOG_BITE_ON_PANIC=y
+CONFIG_CNSS_CRYPTO=y
CONFIG_PWM=y
CONFIG_PWM_QPNP=y
CONFIG_QTI_MPM=y
diff --git a/arch/arm/configs/msm8909_defconfig b/arch/arm/configs/msm8909_defconfig
index b82c3fd..c8087ad 100644
--- a/arch/arm/configs/msm8909_defconfig
+++ b/arch/arm/configs/msm8909_defconfig
@@ -253,7 +253,9 @@
CONFIG_PPP_ASYNC=y
CONFIG_PPP_SYNC_TTY=y
CONFIG_WCNSS_MEM_PRE_ALLOC=y
-CONFIG_CLD_LL_CORE=y
+CONFIG_CNSS=y
+CONFIG_CNSS_SDIO=y
+CONFIG_CLD_HL_SDIO_CORE=y
CONFIG_INPUT_EVDEV=y
CONFIG_KEYBOARD_GPIO=y
CONFIG_INPUT_JOYSTICK=y
@@ -418,6 +420,7 @@
CONFIG_MSM_EVENT_TIMER=y
CONFIG_QTI_RPM_STATS_LOG=y
CONFIG_QCOM_FORCE_WDOG_BITE_ON_PANIC=y
+CONFIG_CNSS_CRYPTO=y
CONFIG_PWM=y
CONFIG_PWM_QPNP=y
CONFIG_QTI_MPM=y
diff --git a/arch/arm/configs/msm8909w-perf_defconfig b/arch/arm/configs/msm8909w-perf_defconfig
index 9b55740..fb711f6 100644
--- a/arch/arm/configs/msm8909w-perf_defconfig
+++ b/arch/arm/configs/msm8909w-perf_defconfig
@@ -30,7 +30,7 @@
CONFIG_EMBEDDED=y
CONFIG_PROFILING=y
CONFIG_OPROFILE=y
-CONFIG_CC_STACKPROTECTOR_STRONG=y
+CONFIG_CC_STACKPROTECTOR_REGULAR=y
CONFIG_MODULES=y
CONFIG_MODULE_UNLOAD=y
CONFIG_MODULE_FORCE_UNLOAD=y
@@ -132,6 +132,7 @@
CONFIG_NETFILTER_XT_MATCH_QTAGUID=y
CONFIG_NETFILTER_XT_MATCH_QUOTA=y
CONFIG_NETFILTER_XT_MATCH_QUOTA2=y
+CONFIG_NETFILTER_XT_MATCH_QUOTA2_LOG=y
CONFIG_NETFILTER_XT_MATCH_SOCKET=y
CONFIG_NETFILTER_XT_MATCH_STATE=y
CONFIG_NETFILTER_XT_MATCH_STATISTIC=y
@@ -498,6 +499,7 @@
CONFIG_CRYPTO_ANSI_CPRNG=y
CONFIG_CRYPTO_DEV_QCE=y
CONFIG_CRYPTO_DEV_QCOM_MSM_QCE=y
+CONFIG_CRYPTO_DEV_QCRYPTO=y
CONFIG_CRYPTO_DEV_QCEDEV=y
CONFIG_CRYPTO_DEV_OTA_CRYPTO=y
CONFIG_CRYPTO_DEV_QCOM_ICE=y
diff --git a/arch/arm/configs/msm8909w_defconfig b/arch/arm/configs/msm8909w_defconfig
index bb6bc88..de6fb6b 100644
--- a/arch/arm/configs/msm8909w_defconfig
+++ b/arch/arm/configs/msm8909w_defconfig
@@ -34,7 +34,7 @@
CONFIG_EMBEDDED=y
CONFIG_PROFILING=y
CONFIG_OPROFILE=y
-CONFIG_CC_STACKPROTECTOR_STRONG=y
+CONFIG_CC_STACKPROTECTOR_REGULAR=y
CONFIG_MODULES=y
CONFIG_MODULE_UNLOAD=y
CONFIG_MODULE_FORCE_UNLOAD=y
@@ -141,6 +141,7 @@
CONFIG_NETFILTER_XT_MATCH_QTAGUID=y
CONFIG_NETFILTER_XT_MATCH_QUOTA=y
CONFIG_NETFILTER_XT_MATCH_QUOTA2=y
+CONFIG_NETFILTER_XT_MATCH_QUOTA2_LOG=y
CONFIG_NETFILTER_XT_MATCH_SOCKET=y
CONFIG_NETFILTER_XT_MATCH_STATE=y
CONFIG_NETFILTER_XT_MATCH_STATISTIC=y
diff --git a/arch/arm/configs/sdxpoorwills-perf_defconfig b/arch/arm/configs/sdxpoorwills-perf_defconfig
index 0e971d2..52b3158 100644
--- a/arch/arm/configs/sdxpoorwills-perf_defconfig
+++ b/arch/arm/configs/sdxpoorwills-perf_defconfig
@@ -222,6 +222,7 @@
CONFIG_INPUT_GPIO=m
CONFIG_SERIO_LIBPS2=y
# CONFIG_LEGACY_PTYS is not set
+CONFIG_SERIAL_MSM=y
CONFIG_SERIAL_MSM_HS=y
CONFIG_DIAG_CHAR=y
CONFIG_HW_RANDOM=y
diff --git a/arch/arm/mach-qcom/board-sdm429.c b/arch/arm/mach-qcom/board-sdm429.c
index c648eaf..6bdf0f4 100644
--- a/arch/arm/mach-qcom/board-sdm429.c
+++ b/arch/arm/mach-qcom/board-sdm429.c
@@ -17,6 +17,7 @@
static const char *sdm429_dt_match[] __initconst = {
"qcom,sdm429",
+ "qcom,sda429",
NULL
};
diff --git a/arch/arm/mach-qcom/board-sdm439.c b/arch/arm/mach-qcom/board-sdm439.c
index 312f3a5..7b9aa0f 100644
--- a/arch/arm/mach-qcom/board-sdm439.c
+++ b/arch/arm/mach-qcom/board-sdm439.c
@@ -17,6 +17,7 @@
static const char *sdm439_dt_match[] __initconst = {
"qcom,sdm439",
+ "qcom,sda439",
NULL
};
diff --git a/arch/arm64/boot/dts/qcom/8909w-pm660.dtsi b/arch/arm64/boot/dts/qcom/8909w-pm660.dtsi
index 3947406..c8330bd 100644
--- a/arch/arm64/boot/dts/qcom/8909w-pm660.dtsi
+++ b/arch/arm64/boot/dts/qcom/8909w-pm660.dtsi
@@ -79,6 +79,31 @@
};
};
+&msm_gpio {
+ /delete-node/ mpu6050_int_pin;
+ /delete-node/ apds99xx_int_pin;
+ /delete-node/ ak8963_int_pin;
+
+ pmx_mdss {
+ mdss_dsi_active: mdss_dsi_active {
+ mux {
+ pins = "gpio25", "gpio37", "gpio59";
+ };
+ config {
+ pins = "gpio25", "gpio37", "gpio59";
+ };
+ };
+ mdss_dsi_suspend: mdss_dsi_suspend {
+ mux {
+ pins = "gpio25", "gpio37", "gpio59";
+ };
+ config {
+ pins = "gpio25", "gpio37", "gpio59";
+ };
+ };
+ };
+};
+
&i2c_3 {
qcom,actuator@0 {
/delete-property/ cam_vaf-supply;
diff --git a/arch/arm64/boot/dts/qcom/Makefile b/arch/arm64/boot/dts/qcom/Makefile
index 31d4b0d..3c2ae63 100644
--- a/arch/arm64/boot/dts/qcom/Makefile
+++ b/arch/arm64/boot/dts/qcom/Makefile
@@ -398,6 +398,10 @@
apq8053-iot-mtp.dtb \
apq8053-lite-dragon-v1.0.dtb \
apq8053-lite-dragon-v2.0.dtb \
+ apq8053-lite-lenovo-v1.0.dtb \
+ apq8053-lite-lenovo-v1.1.dtb \
+ apq8053-lite-harman-v1.0.dtb \
+ apq8053-lite-lge-v1.0.dtb \
msm8953-pmi8940-cdp.dtb \
msm8953-pmi8940-mtp.dtb \
msm8953-pmi8937-cdp.dtb \
@@ -422,6 +426,7 @@
apq8009w-bg-wtp-v2.dtb \
apq8009w-bg-alpha.dtb \
apq8009-mtp-wcd9326-refboard.dtb \
+ apq8009-robot-som-refboard.dtb \
apq8009-dragon.dtb
dtb-$(CONFIG_ARCH_SDM450) += sdm450-rcm.dtb \
diff --git a/arch/arm64/boot/dts/qcom/apq8009-dragon.dts b/arch/arm64/boot/dts/qcom/apq8009-dragon.dts
index 6b3aea0..12a4363 100644
--- a/arch/arm64/boot/dts/qcom/apq8009-dragon.dts
+++ b/arch/arm64/boot/dts/qcom/apq8009-dragon.dts
@@ -66,6 +66,11 @@
mdss_mdp: qcom,mdss_mdp@1a00000 {
status = "disabled";
};
+
+ bluetooth: bt_qca9379 {
+ compatible = "qca,qca9379";
+ qca,bt-reset-gpio = <&msm_gpio 47 0>; /* BT_EN */
+ };
};
&sdhc_2 {
@@ -133,3 +138,7 @@
&pm8916_bms {
status = "ok";
};
+
+&blsp1_uart2_hs {
+ status = "ok";
+};
diff --git a/arch/arm64/boot/dts/qcom/apq8009-robot-som-refboard.dts b/arch/arm64/boot/dts/qcom/apq8009-robot-som-refboard.dts
new file mode 100644
index 0000000..72486b2
--- /dev/null
+++ b/arch/arm64/boot/dts/qcom/apq8009-robot-som-refboard.dts
@@ -0,0 +1,111 @@
+/*
+ * Copyright (c) 2018, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+/dts-v1/;
+
+#include "msm8909-mtp.dtsi"
+#include "8909-pm8916.dtsi"
+#include "msm8909-pm8916-mtp.dtsi"
+#include "apq8009-audio-external_codec.dtsi"
+
+/ {
+ model = "Qualcomm Technologies, Inc. APQ8009 Robot SOM refboard";
+ compatible = "qcom,apq8009-mtp", "qcom,apq8009", "qcom,mtp";
+ qcom,msm-id = <265 2>;
+ qcom,board-id = <8 0x15>;
+};
+
+&soc {
+ ext-codec {
+ qcom,msm-mbhc-hphl-swh = <0>;
+ qcom,audio-routing =
+ "AIF4 VI", "MCLK",
+ "RX_BIAS", "MCLK",
+ "MADINPUT", "MCLK",
+ "AMIC2", "MIC BIAS2",
+ "MIC BIAS2", "Headset Mic",
+ "DMIC0", "MIC BIAS1",
+ "MIC BIAS1", "Digital Mic0",
+ "DMIC1", "MIC BIAS1",
+ "MIC BIAS1", "Digital Mic1",
+ "DMIC2", "MIC BIAS3",
+ "MIC BIAS3", "Digital Mic2",
+ "DMIC3", "MIC BIAS3",
+ "MIC BIAS3", "Digital Mic3",
+ "SpkrLeft IN", "SPK1 OUT",
+ "SpkrRight IN", "SPK2 OUT";
+ };
+
+ sound-9335 {
+ status = "disabled";
+ };
+
+ i2c@78b8000 {
+ wcd9xxx_codec@d {
+ status = "disabled";
+ };
+ };
+
+ vph_pwr_vreg: vph_pwr_vreg {
+ compatible = "regulator-fixed";
+ status = "ok";
+ regulator-name = "vph_pwr";
+ regulator-always-on;
+ };
+
+ mdss_mdp: qcom,mdss_mdp@1a00000 {
+ status = "disabled";
+ };
+
+ bluetooth: bt_qca9379 {
+ compatible = "qca,qca9379";
+ qca,bt-reset-gpio = <&msm_gpio 47 0>; /* BT_EN */
+ };
+};
+
+&sdhc_2 {
+ status = "disabled";
+};
+
+&usb_otg {
+ interrupts = <0 134 0>,<0 140 0>,<0 136 0>;
+ interrupt-names = "core_irq", "async_irq", "phy_irq";
+ qcom,hsusb-otg-mode = <3>;
+ qcom,switch-vbus-w-id;
+ vbus_otg-supply = <&vph_pwr_vreg>;
+ extcon = <&pm8916_chg>;
+};
+
+&external_image_mem {
+ reg = <0x0 0x87a00000 0x0 0x0600000>;
+};
+
+&modem_adsp_mem {
+ reg = <0x0 0x88000000 0x0 0x01e00000>;
+};
+
+&peripheral_mem {
+ reg = <0x0 0x89e00000 0x0 0x0700000>;
+};
+
+&pm8916_chg {
+ status = "ok";
+};
+
+&pm8916_bms {
+ status = "ok";
+};
+
+&blsp1_uart2_hs {
+ status = "ok";
+};
diff --git a/arch/arm64/boot/dts/qcom/apq8009w-bg-alpha.dts b/arch/arm64/boot/dts/qcom/apq8009w-bg-alpha.dts
index 89e1b76..1fe7b15 100644
--- a/arch/arm64/boot/dts/qcom/apq8009w-bg-alpha.dts
+++ b/arch/arm64/boot/dts/qcom/apq8009w-bg-alpha.dts
@@ -300,4 +300,5 @@
&mdss_dsi0 {
qcom,dsi-pref-prim-pan = <&dsi_auo_390p_cmd>;
qcom,platform-bklight-en-gpio = <&msm_gpio 52 0>;
+ qcom,platform-enable-gpio = <&msm_gpio 59 0>;
};
diff --git a/arch/arm64/boot/dts/qcom/apq8009w-bg-wtp-v2.dts b/arch/arm64/boot/dts/qcom/apq8009w-bg-wtp-v2.dts
index 9fb6626..8113670 100644
--- a/arch/arm64/boot/dts/qcom/apq8009w-bg-wtp-v2.dts
+++ b/arch/arm64/boot/dts/qcom/apq8009w-bg-wtp-v2.dts
@@ -317,4 +317,5 @@
&mdss_dsi0 {
qcom,dsi-pref-prim-pan = <&dsi_auo_390p_cmd>;
qcom,platform-bklight-en-gpio = <&msm_gpio 52 0>;
+ qcom,platform-enable-gpio = <&msm_gpio 59 0>;
};
diff --git a/arch/arm64/boot/dts/qcom/apq8053-lite-dragon-v2.0.dts b/arch/arm64/boot/dts/qcom/apq8053-lite-dragon-v2.0.dts
index d7836e5..55d8b7b 100644
--- a/arch/arm64/boot/dts/qcom/apq8053-lite-dragon-v2.0.dts
+++ b/arch/arm64/boot/dts/qcom/apq8053-lite-dragon-v2.0.dts
@@ -13,7 +13,6 @@
/dts-v1/;
-#include "apq8053-lite.dtsi"
#include "apq8053-lite-dragon-v2.0.dtsi"
/ {
diff --git a/arch/arm64/boot/dts/qcom/apq8053-lite-dragon-v2.0.dtsi b/arch/arm64/boot/dts/qcom/apq8053-lite-dragon-v2.0.dtsi
index 947af4d..bb40018 100644
--- a/arch/arm64/boot/dts/qcom/apq8053-lite-dragon-v2.0.dtsi
+++ b/arch/arm64/boot/dts/qcom/apq8053-lite-dragon-v2.0.dtsi
@@ -12,68 +12,25 @@
*/
#include "apq8053-lite-dragon.dtsi"
-#include "msm8953-mdss-panels.dtsi"
-
-&i2c_3 {
- status = "okay";
- himax_ts@48 {
- compatible = "himax,hxcommon";
- reg = <0x48>;
- interrupt-parent = <&tlmm>;
- interrupts = <65 0x2>;
- vdd-supply = <&pm8953_l10>;
- avdd-supply = <&pm8953_l5>;
- 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>;
- himax,panel-coords = <0 800 0 1280>;
- himax,display-coords = <0 800 0 1280>;
- himax,irq-gpio = <&tlmm 65 0x2008>;
- report_type = <1>;
- };
-};
-
-&mdss_mdp {
- qcom,mdss-pref-prim-intf = "dsi";
-};
-
-&mdss_dsi {
- hw-config = "single_dsi";
-};
&mdss_dsi0 {
- qcom,dsi-pref-prim-pan = <&dsi_boyi_hx83100a_800p_video>;
- pinctrl-names = "mdss_default", "mdss_sleep";
pinctrl-0 = <&mdss_dsi_active &mdss_te_active &mdss_dsi_gpio>;
pinctrl-1 = <&mdss_dsi_suspend &mdss_te_suspend &mdss_dsi_gpio>;
-
- vdd-supply = <&pm8953_l10>;
- vddio-supply = <&pm8953_l6>;
- lab-supply = <&lab_regulator>;
- ibb-supply = <&ibb_regulator>;
-
- qcom,platform-te-gpio = <&tlmm 24 0>;
- qcom,platform-reset-gpio = <&tlmm 61 0>;
- qcom,platform-bklight-en-gpio = <&tlmm 100 0>;
-};
-
-&mdss_dsi1 {
- status = "disabled";
-};
-
-&labibb {
- status = "okay";
- qpnp,qpnp-labibb-mode = "lcd";
-};
-
-&wled {
- qcom,cons-sync-write-delay-us = <1000>;
- qcom,led-strings-list = [00 01 02 03];
};
&pm8953_l4 {
status = "okay";
regulator-always-on;
};
+
+&camera0 {
+ qcom,mount-angle = <90>;
+};
+
+&camera1 {
+ qcom,mount-angle = <90>;
+};
+
+&camera2{
+ qcom,mount-angle = <90>;
+};
diff --git a/arch/arm64/boot/dts/qcom/apq8053-lite-dragon.dtsi b/arch/arm64/boot/dts/qcom/apq8053-lite-dragon.dtsi
index 5e3ddce..e5b4c84 100644
--- a/arch/arm64/boot/dts/qcom/apq8053-lite-dragon.dtsi
+++ b/arch/arm64/boot/dts/qcom/apq8053-lite-dragon.dtsi
@@ -11,6 +11,7 @@
* GNU General Public License for more details.
*/
+#include "apq8053-lite.dtsi"
#include "msm8953-pinctrl.dtsi"
#include "apq8053-camera-sensor-dragon.dtsi"
#include "pmi8950.dtsi"
@@ -185,39 +186,22 @@
&i2c_3 {
status = "okay";
- focaltech@38 {
- compatible = "focaltech,5x06";
- reg = <0x38>;
+ himax_ts@48 {
+ compatible = "himax,hxcommon";
+ reg = <0x48>;
interrupt-parent = <&tlmm>;
interrupts = <65 0x2>;
vdd-supply = <&pm8953_l10>;
- vcc_i2c-supply = <&pm8953_l5>;
- /* pins used by touchscreen */
+ avdd-supply = <&pm8953_l5>;
pinctrl-names = "pmx_ts_active","pmx_ts_suspend",
- "pmx_ts_release";
+ "pmx_ts_release";
pinctrl-0 = <&ts_int_active &ts_reset_active>;
pinctrl-1 = <&ts_int_suspend &ts_reset_suspend>;
pinctrl-2 = <&ts_release>;
- focaltech,name = "ft5606";
- focaltech,family-id = <0x08>;
- focaltech,reset-gpio = <&tlmm 64 0x0>;
- focaltech,irq-gpio = <&tlmm 65 0x2008>;
- focaltech,display-coords = <0 0 1919 1199>;
- focaltech,panel-coords = <0 0 1919 1199>;
- focaltech,no-force-update;
- focaltech,i2c-pull-up;
- focaltech,group-id = <1>;
- focaltech,hard-reset-delay-ms = <20>;
- focaltech,soft-reset-delay-ms = <200>;
- focaltech,num-max-touches = <5>;
- focaltech,fw-delay-aa-ms = <30>;
- focaltech,fw-delay-55-ms = <30>;
- focaltech,fw-upgrade-id1 = <0x79>;
- focaltech,fw-upgrade-id2 = <0x08>;
- focaltech,fw-delay-readid-ms = <10>;
- focaltech,fw-delay-era-flsh-ms = <2000>;
- focaltech,fw-auto-cal;
- focaltech,resume-in-workqueue;
+ himax,panel-coords = <0 800 0 1280>;
+ himax,display-coords = <0 800 0 1280>;
+ himax,irq-gpio = <&tlmm 65 0x2008>;
+ report_type = <1>;
};
};
@@ -229,6 +213,71 @@
status = "disabled";
};
+#include "msm8953-mdss-panels.dtsi"
+&mdss_mdp {
+ qcom,mdss-pref-prim-intf = "dsi";
+};
+
+&mdss_dsi {
+ hw-config = "single_dsi";
+};
+
+&mdss_dsi_active {
+ mux {
+ pins = "gpio61", "gpio100";
+ function = "gpio";
+ };
+
+ config {
+ pins = "gpio61", "gpio100";
+ drive-strength = <8>;
+ bias-disable = <0>;
+ output-high;
+ };
+};
+
+&mdss_dsi_suspend {
+ mux {
+ pins = "gpio61", "gpio100";
+ function = "gpio";
+ };
+
+ config {
+ pins = "gpio61", "gpio100";
+ drive-strength = <2>;
+ bias-pull-down;
+ };
+};
+
+&mdss_dsi0 {
+ qcom,dsi-pref-prim-pan = <&dsi_boyi_hx83100a_800p_video>;
+ pinctrl-names = "mdss_default", "mdss_sleep";
+ pinctrl-0 = <&mdss_dsi_active &mdss_te_active>;
+ pinctrl-1 = <&mdss_dsi_suspend &mdss_te_suspend>;
+
+ vdd-supply = <&pm8953_l10>;
+ vddio-supply = <&pm8953_l6>;
+ lab-supply = <&lab_regulator>;
+ ibb-supply = <&ibb_regulator>;
+
+ qcom,platform-te-gpio = <&tlmm 24 0>;
+ qcom,platform-reset-gpio = <&tlmm 61 0>;
+ qcom,platform-bklight-en-gpio = <&tlmm 100 0>;
+};
+
+&mdss_dsi1 {
+ status = "disabled";
+};
+
+&labibb {
+ status = "okay";
+ qpnp,qpnp-labibb-mode = "lcd";
+};
+
+&wled {
+ qcom,cons-sync-write-delay-us = <1000>;
+ qcom,led-strings-list = [00 01 02 03];
+};
&blsp1_uart0 {
status = "ok";
pinctrl-names = "default";
@@ -301,7 +350,7 @@
pinctrl-1 = <&sdc2_clk_off &sdc2_cmd_off &sdc2_data_off
&sdc2_wlan_gpio_off>;
- qcom,clk-rates = <400000 20000000 25000000 50000000>;
+ qcom,clk-rates = <400000 20000000 25000000 50000000 100000000>;
qcom,bus-speed-mode = "SDR12", "SDR25", "SDR50", "DDR50", "SDR104";
status = "ok";
diff --git a/arch/arm64/boot/dts/qcom/apq8053-lite-harman-v1.0.dts b/arch/arm64/boot/dts/qcom/apq8053-lite-harman-v1.0.dts
new file mode 100644
index 0000000..203b6b8
--- /dev/null
+++ b/arch/arm64/boot/dts/qcom/apq8053-lite-harman-v1.0.dts
@@ -0,0 +1,27 @@
+/*
+ * Copyright (c) 2017-2018, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * 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 "apq8053-lite-harman-v1.0.dtsi"
+
+/ {
+ model = "Qualcomm Technologies, Inc. APQ8053 Lite Harman v1.0 Board";
+ compatible = "qcom,apq8053-lite-dragonboard", "qcom,apq8053",
+ "qcom,dragonboard";
+ qcom,board-id= <0x01020020 0>;
+};
+
+&blsp2_uart0 {
+ status = "okay";
+};
diff --git a/arch/arm64/boot/dts/qcom/apq8053-lite-harman-v1.0.dtsi b/arch/arm64/boot/dts/qcom/apq8053-lite-harman-v1.0.dtsi
new file mode 100644
index 0000000..5cf8ac0
--- /dev/null
+++ b/arch/arm64/boot/dts/qcom/apq8053-lite-harman-v1.0.dtsi
@@ -0,0 +1,80 @@
+/*
+ * Copyright (c) 2017-2018, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * 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 "apq8053-lite-dragon.dtsi"
+
+&pm8953_l4 {
+ status = "okay";
+ regulator-always-on;
+};
+
+&pm8953_l10 {
+ status = "okay";
+ regulator-min-microvolt = <3000000>;
+ regulator-max-microvolt = <3300000>;
+ regulator-always-on;
+};
+
+&pm8953_l2 {
+ status = "okay";
+ regulator-always-on;
+};
+
+&pm8953_l17 {
+ status = "okay";
+ regulator-always-on;
+};
+
+&pm8953_l22 {
+ status = "okay";
+ regulator-always-on;
+};
+
+&i2c_3 {
+ status = "okay";
+ /delete-node/ himax_ts@48;
+ focaltech_ts@38 {
+ compatible = "focaltech,fts";
+ reg = <0x38>;
+ interrupt-parent = <&tlmm>;
+ interrupts = <65 0x2>;
+ vdd-supply = <&pm8953_l10>;
+ avdd-supply = <&pm8953_l6>;
+ 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>;
+ focaltech,display-coords = <0 0 800 1280>;
+ focaltech,reset-gpio = <&tlmm 64 0x0>;
+ focaltech,irq-gpio = <&tlmm 65 0x2>;
+ focaltech,max-touch-number = <5>;
+ report_type = <1>;
+ };
+};
+
+&wled {
+ qcom,led-strings-list = [00 01 02];
+};
+
+&camera0 {
+ qcom,mount-angle = <90>;
+};
+
+&camera1 {
+ qcom,mount-angle = <90>;
+};
+
+&camera2{
+ qcom,mount-angle = <90>;
+};
diff --git a/arch/arm64/boot/dts/qcom/apq8053-lite-lenovo-v1.0.dts b/arch/arm64/boot/dts/qcom/apq8053-lite-lenovo-v1.0.dts
new file mode 100644
index 0000000..325accf
--- /dev/null
+++ b/arch/arm64/boot/dts/qcom/apq8053-lite-lenovo-v1.0.dts
@@ -0,0 +1,27 @@
+/*
+ * Copyright (c) 2017-2018, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * 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 "apq8053-lite-lenovo-v1.0.dtsi"
+
+/ {
+ model = "Qualcomm Technologies, Inc. APQ8053 Lite Lenovo v1.0 Board";
+ compatible = "qcom,apq8053-lite-dragonboard", "qcom,apq8053",
+ "qcom,dragonboard";
+ qcom,board-id= <0x01010020 0>;
+};
+
+&blsp2_uart0 {
+ status = "okay";
+};
diff --git a/arch/arm64/boot/dts/qcom/apq8053-lite-lenovo-v1.0.dtsi b/arch/arm64/boot/dts/qcom/apq8053-lite-lenovo-v1.0.dtsi
new file mode 100644
index 0000000..4d9c40c
--- /dev/null
+++ b/arch/arm64/boot/dts/qcom/apq8053-lite-lenovo-v1.0.dtsi
@@ -0,0 +1,68 @@
+/*
+ * Copyright (c) 2017-2018, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * 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 "apq8053-lite-dragon.dtsi"
+
+&mdss_dsi0 {
+ qcom,ext_vdd-gpio = <&tlmm 100 0>;
+ qcom,platform-bklight-en-gpio = <&tlmm 95 0>;
+
+ qcom,platform-lane-config = [00 00 ff 0f
+ 00 00 ff 0f
+ 00 00 ff 0f
+ 00 00 ff 0f
+ 00 00 ff 8f];
+};
+
+&eeprom0 {
+ gpios = <&tlmm 26 0>,
+ <&tlmm 40 0>,
+ <&tlmm 118 0>,
+ <&tlmm 119 0>,
+ <&tlmm 39 0>;
+ qcom,gpio-vdig = <3>;
+ qcom,gpio-vana = <4>;
+ qcom,gpio-req-tbl-num = <0 1 2 3 4>;
+ qcom,gpio-req-tbl-flags = <1 0 0 0 0>;
+ qcom,gpio-req-tbl-label = "CAMIF_MCLK0",
+ "CAM_RESET0",
+ "CAM_VDIG",
+ "CAM_VANA",
+ "CAM_STANDBY0";
+};
+
+&camera0 {
+ qcom,mount-angle = <270>;
+ gpios = <&tlmm 26 0>,
+ <&tlmm 40 0>,
+ <&tlmm 39 0>,
+ <&tlmm 118 0>,
+ <&tlmm 119 0>;
+ qcom,gpio-vdig = <3>;
+ qcom,gpio-vana = <4>;
+ qcom,gpio-req-tbl-num = <0 1 2 3 4>;
+ qcom,gpio-req-tbl-flags = <1 0 0 0 0>;
+ qcom,gpio-req-tbl-label = "CAMIF_MCLK0",
+ "CAM_RESET0",
+ "CAM_STANDBY0",
+ "CAM_VDIG",
+ "CAM_VANA";
+};
+
+&camera1 {
+ qcom,mount-angle = <270>;
+};
+
+&camera2{
+ qcom,mount-angle = <270>;
+};
diff --git a/arch/arm64/boot/dts/qcom/apq8053-lite-lenovo-v1.1.dts b/arch/arm64/boot/dts/qcom/apq8053-lite-lenovo-v1.1.dts
new file mode 100644
index 0000000..0c7b557
--- /dev/null
+++ b/arch/arm64/boot/dts/qcom/apq8053-lite-lenovo-v1.1.dts
@@ -0,0 +1,27 @@
+/*
+ * Copyright (c) 2017-2018, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * 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 "apq8053-lite-lenovo-v1.1.dtsi"
+
+/ {
+ model = "Qualcomm Technologies, Inc. APQ8053 Lite Lenovo v1.1 Board";
+ compatible = "qcom,apq8053-lite-dragonboard", "qcom,apq8053",
+ "qcom,dragonboard";
+ qcom,board-id= <0x01010120 0>;
+};
+
+&blsp2_uart0 {
+ status = "okay";
+};
diff --git a/arch/arm64/boot/dts/qcom/apq8053-lite-lenovo-v1.1.dtsi b/arch/arm64/boot/dts/qcom/apq8053-lite-lenovo-v1.1.dtsi
new file mode 100644
index 0000000..396fd55
--- /dev/null
+++ b/arch/arm64/boot/dts/qcom/apq8053-lite-lenovo-v1.1.dtsi
@@ -0,0 +1,62 @@
+/*
+ * Copyright (c) 2017-2018, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * 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 "apq8053-lite-dragon.dtsi"
+
+&i2c_3 {
+ status = "okay";
+ /delete-node/ himax_ts@48;
+};
+
+&eeprom0 {
+ gpios = <&tlmm 26 0>,
+ <&tlmm 40 0>,
+ <&tlmm 118 0>,
+ <&tlmm 119 0>,
+ <&tlmm 39 0>;
+ qcom,gpio-vdig = <3>;
+ qcom,gpio-vana = <4>;
+ qcom,gpio-req-tbl-num = <0 1 2 3 4>;
+ qcom,gpio-req-tbl-flags = <1 0 0 0 0>;
+ qcom,gpio-req-tbl-label = "CAMIF_MCLK0",
+ "CAM_RESET0",
+ "CAM_VDIG",
+ "CAM_VANA",
+ "CAM_STANDBY0";
+};
+
+&camera0 {
+ qcom,mount-angle = <270>;
+ gpios = <&tlmm 26 0>,
+ <&tlmm 40 0>,
+ <&tlmm 39 0>,
+ <&tlmm 118 0>,
+ <&tlmm 119 0>;
+ qcom,gpio-vdig = <3>;
+ qcom,gpio-vana = <4>;
+ qcom,gpio-req-tbl-num = <0 1 2 3 4>;
+ qcom,gpio-req-tbl-flags = <1 0 0 0 0>;
+ qcom,gpio-req-tbl-label = "CAMIF_MCLK0",
+ "CAM_RESET0",
+ "CAM_STANDBY0",
+ "CAM_VDIG",
+ "CAM_VANA";
+};
+
+&camera1 {
+ qcom,mount-angle = <270>;
+};
+
+&camera2{
+ qcom,mount-angle = <270>;
+};
diff --git a/arch/arm64/boot/dts/qcom/apq8053-lite-lge-v1.0.dts b/arch/arm64/boot/dts/qcom/apq8053-lite-lge-v1.0.dts
new file mode 100644
index 0000000..70952dc
--- /dev/null
+++ b/arch/arm64/boot/dts/qcom/apq8053-lite-lge-v1.0.dts
@@ -0,0 +1,27 @@
+/*
+ * Copyright (c) 2017-2018, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * 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 "apq8053-lite-lge-v1.0.dtsi"
+
+/ {
+ model = "Qualcomm Technologies, Inc. APQ8053 Lite LGE v1.0 Board";
+ compatible = "qcom,apq8053-lite-dragonboard", "qcom,apq8053",
+ "qcom,dragonboard";
+ qcom,board-id= <0x01030020 0>;
+};
+
+&blsp2_uart0 {
+ status = "okay";
+};
diff --git a/arch/arm64/boot/dts/qcom/apq8053-lite-lge-v1.0.dtsi b/arch/arm64/boot/dts/qcom/apq8053-lite-lge-v1.0.dtsi
new file mode 100644
index 0000000..db0331e
--- /dev/null
+++ b/arch/arm64/boot/dts/qcom/apq8053-lite-lge-v1.0.dtsi
@@ -0,0 +1,30 @@
+/*
+ * Copyright (c) 2017-2018, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * 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 "apq8053-lite-dragon.dtsi"
+
+&wled {
+ qcom,fs-curr-ua = <17500>;
+};
+
+&camera0 {
+ qcom,mount-angle = <180>;
+};
+
+&camera1 {
+ qcom,mount-angle = <180>;
+};
+
+&camera2 {
+ qcom,mount-angle = <180>;
+};
diff --git a/arch/arm64/boot/dts/qcom/msm8909w-bg-wtp-v2.dts b/arch/arm64/boot/dts/qcom/msm8909w-bg-wtp-v2.dts
index 8149eb2..9dd80f0 100644
--- a/arch/arm64/boot/dts/qcom/msm8909w-bg-wtp-v2.dts
+++ b/arch/arm64/boot/dts/qcom/msm8909w-bg-wtp-v2.dts
@@ -320,4 +320,5 @@
&mdss_dsi0 {
qcom,dsi-pref-prim-pan = <&dsi_auo_390p_cmd>;
qcom,platform-bklight-en-gpio = <&msm_gpio 52 0>;
+ qcom,platform-enable-gpio = <&msm_gpio 59 0>;
};
diff --git a/arch/arm64/boot/dts/qcom/msm8917-qrd.dtsi b/arch/arm64/boot/dts/qcom/msm8917-qrd.dtsi
index f897b59..fae258d0 100644
--- a/arch/arm64/boot/dts/qcom/msm8917-qrd.dtsi
+++ b/arch/arm64/boot/dts/qcom/msm8917-qrd.dtsi
@@ -78,6 +78,12 @@
};
&soc {
+ int_codec: sound {
+ status = "okay";
+ qcom,msm-mbhc-hphl-swh = <1>;
+ qcom,msm-hs-micbias-type = "internal";
+ };
+
gpio_keys {
compatible = "gpio-keys";
input-name = "gpio-keys";
diff --git a/arch/arm64/boot/dts/qcom/msm8953-iot-mtp.dts b/arch/arm64/boot/dts/qcom/msm8953-iot-mtp.dts
index 39c76cc..c3d4cc5 100644
--- a/arch/arm64/boot/dts/qcom/msm8953-iot-mtp.dts
+++ b/arch/arm64/boot/dts/qcom/msm8953-iot-mtp.dts
@@ -25,3 +25,18 @@
qcom,pmic-id = <0x010016 0x010011 0x0 0x0>;
};
+&kgsl_smmu {
+ qcom,hibernation-support;
+ qcom,static-ns-cbs = <0>;
+ /delete-property/ qcom,skip-init;
+};
+
+&apps_iommu {
+ qcom,hibernation-support;
+ qcom,static-ns-cbs =
+ <15 16 17 18 19>,
+ <20 21 22 23 24 25 26 27 28 29 30>,
+ <31>;
+
+ /delete-property/ qcom,skip-init;
+};
diff --git a/arch/arm64/boot/dts/qcom/pmi632.dtsi b/arch/arm64/boot/dts/qcom/pmi632.dtsi
index 80480f1..8a640a5 100644
--- a/arch/arm64/boot/dts/qcom/pmi632.dtsi
+++ b/arch/arm64/boot/dts/qcom/pmi632.dtsi
@@ -44,8 +44,6 @@
interrupt-names = "eoc-int-en-set";
qcom,adc-vdd-reference = <1875>;
qcom,adc-full-scale-code = <0x70e4>;
- pinctrl-names = "default";
- pinctrl-0 = <&quiet_therm_default &smb_therm_default>;
chan@0 {
label = "ref_gnd";
@@ -305,21 +303,6 @@
gpio-controller;
#gpio-cells = <2>;
qcom,gpios-disallowed = <1>;
-
- quiet_therm {
- quiet_therm_default: quiet_therm_default {
- pins = "gpio3";
- bias-high-impedance;
- };
- };
-
- smb_therm {
- smb_therm_default: smb_therm_default {
- pins = "gpio4";
- bias-high-impedance;
- };
- };
-
};
pmi632_charger: qcom,qpnp-smb5 {
@@ -331,6 +314,7 @@
qcom,pmic-revid = <&pmi632_revid>;
dpdm-supply = <&qusb_phy>;
qcom,auto-recharge-soc = <98>;
+ qcom,chg-vadc = <&pmi632_vadc>;
qcom,thermal-mitigation
= <3000000 2500000 2000000 1500000
diff --git a/arch/arm64/boot/dts/qcom/qcs605.dtsi b/arch/arm64/boot/dts/qcom/qcs605.dtsi
index be88a5d..747593f 100644
--- a/arch/arm64/boot/dts/qcom/qcs605.dtsi
+++ b/arch/arm64/boot/dts/qcom/qcs605.dtsi
@@ -19,39 +19,39 @@
};
&pil_modem_mem {
- reg = <0 0x8b000000 0 0x3e00000>;
+ reg = <0 0x8b000000 0 0x3100000>;
};
&pil_video_mem {
- reg = <0 0x8ee00000 0 0x500000>;
+ reg = <0 0x8e100000 0 0x500000>;
};
&wlan_msa_mem {
- reg = <0 0x8f300000 0 0x100000>;
+ reg = <0 0x8e600000 0 0x100000>;
};
&pil_cdsp_mem {
- reg = <0 0x8f400000 0 0x800000>;
+ reg = <0 0x8e700000 0 0x800000>;
};
&pil_mba_mem {
- reg = <0 0x8fc00000 0 0x200000>;
+ reg = <0 0x8ef00000 0 0x200000>;
};
&pil_adsp_mem {
- reg = <0 0x8fe00000 0 0x1e00000>;
+ reg = <0 0x8f100000 0 0x1e00000>;
};
&pil_ipa_fw_mem {
- reg = <0 0x91c00000 0 0x10000>;
+ reg = <0 0x90f00000 0 0x10000>;
};
&pil_ipa_gsi_mem {
- reg = <0 0x91c10000 0 0x5000>;
+ reg = <0 0x90f10000 0 0x5000>;
};
&pil_gpu_mem {
- reg = <0 0x91c15000 0 0x2000>;
+ reg = <0 0x90f15000 0 0x2000>;
};
&adsp_mem {
diff --git a/arch/arm64/boot/dts/qcom/sdm429-qrd.dtsi b/arch/arm64/boot/dts/qcom/sdm429-qrd.dtsi
index 7116662..dde9c56 100644
--- a/arch/arm64/boot/dts/qcom/sdm429-qrd.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm429-qrd.dtsi
@@ -12,3 +12,7 @@
*/
#include "sdm439-qrd.dtsi"
+
+&mdss_dsi0 {
+ qcom,dsi-pref-prim-pan = <&dsi_hx8399c_hd_vid>;
+};
diff --git a/arch/arm64/boot/dts/qcom/sdm429.dtsi b/arch/arm64/boot/dts/qcom/sdm429.dtsi
index 24e9905..19df054 100644
--- a/arch/arm64/boot/dts/qcom/sdm429.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm429.dtsi
@@ -207,3 +207,9 @@
"byte1_src";
#clock-cells = <1>;
};
+
+/* GPU overrides */
+&msm_gpu {
+ /* Update GPU chip ID*/
+ qcom,chipid = <0x05000400>;
+};
diff --git a/arch/arm64/boot/dts/qcom/sdm439-audio.dtsi b/arch/arm64/boot/dts/qcom/sdm439-audio.dtsi
index f6751d2..dba56a4 100644
--- a/arch/arm64/boot/dts/qcom/sdm439-audio.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm439-audio.dtsi
@@ -15,6 +15,7 @@
int_codec: sound {
qcom,model = "sdm439-snd-card-mtp";
qcom,msm-hs-micbias-type = "internal";
+ qcom,msm-micbias2-ext-cap;
asoc-codec = <&stub_codec>, <&msm_digital_codec>,
<&pmic_analog_codec>;
diff --git a/arch/arm64/boot/dts/qcom/sdm439-cdp.dtsi b/arch/arm64/boot/dts/qcom/sdm439-cdp.dtsi
index 08745cd..fd66f4b 100644
--- a/arch/arm64/boot/dts/qcom/sdm439-cdp.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm439-cdp.dtsi
@@ -150,6 +150,30 @@
};
};
+&cdc_pdm_lines_2_act {
+ mux {
+ pins = "gpio70", "gpio71", "gpio72";
+ function = "cdc_pdm0";
+ };
+
+ config {
+ pins = "gpio70", "gpio71", "gpio72";
+ drive-strength = <16>;
+ };
+};
+
+&cdc_pdm_lines_act {
+ mux {
+ pins = "gpio69", "gpio73", "gpio74";
+ function = "cdc_pdm0";
+ };
+
+ config {
+ pins = "gpio69", "gpio73", "gpio74";
+ drive-strength = <16>;
+ };
+};
+
&pm8953_pwm {
status = "ok";
};
diff --git a/arch/arm64/boot/dts/qcom/sdm439-qrd.dtsi b/arch/arm64/boot/dts/qcom/sdm439-qrd.dtsi
index 18714ce..bfe7bfa 100644
--- a/arch/arm64/boot/dts/qcom/sdm439-qrd.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm439-qrd.dtsi
@@ -297,3 +297,13 @@
qcom,mdss-dsi-pan-fps-update =
"dfps_immediate_porch_mode_vfp";
};
+
+&dsi_hx8399c_hd_vid {
+ /delete-property/ qcom,mdss-dsi-panel-timings;
+ qcom,mdss-dsi-panel-timings-phy-12nm = [08 06 0a 02 00 04 02 08];
+ qcom,panel-supply-entries = <&dsi_panel_pwr_supply>;
+ qcom,mdss-dsi-bl-pmic-control-type = "bl_ctrl_pwm";
+ qcom,mdss-dsi-bl-pmic-pwm-frequency = <100>;
+ qcom,mdss-dsi-bl-pmic-bank-select = <0>;
+ qcom,mdss-dsi-pwm-gpio = <&pm8953_gpios 8 0>;
+};
diff --git a/arch/arm64/boot/dts/qcom/sdm439-regulator.dtsi b/arch/arm64/boot/dts/qcom/sdm439-regulator.dtsi
index f325925..a8b8291 100644
--- a/arch/arm64/boot/dts/qcom/sdm439-regulator.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm439-regulator.dtsi
@@ -468,5 +468,19 @@
qcom,cpr-quot-adjust-scaling-factor-max = <0 1400 1400>;
qcom,cpr-voltage-scaling-factor-max = <0 2000 2000>;
qcom,cpr-scaled-init-voltage-as-ceiling;
+
+ qcom,cpr-fuse-version-map =
+ /* <Speed-bin pvs-version cpr-rev ... ... ...> */
+ <(-1) (-1) ( 0) (-1) (-1) (-1)>,
+ <(-1) (-1) (-1) (-1) (-1) (-1)>;
+
+ qcom,cpr-quotient-adjustment =
+ <66 77 66>, /* SVSP_30mV, NOM_35mV, TUR_30mV */
+ <0 0 0>;
+
+ qcom,cpr-voltage-ceiling-override =
+ <(-1) (-1) 795000 795000 835000 910000 910000>;
+
+ qcom,cpr-enable;
};
};
diff --git a/arch/arm64/boot/dts/qcom/sdm450-pmi632.dtsi b/arch/arm64/boot/dts/qcom/sdm450-pmi632.dtsi
index 5c127bc..e09d637 100644
--- a/arch/arm64/boot/dts/qcom/sdm450-pmi632.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm450-pmi632.dtsi
@@ -41,6 +41,27 @@
};
};
+&pmi632_vadc {
+ pinctrl-names = "default";
+ pinctrl-0 = <&quiet_therm_default &smb_therm_default>;
+};
+
+&pmi632_gpios {
+ quiet_therm {
+ quiet_therm_default: quiet_therm_default {
+ pins = "gpio3";
+ bias-high-impedance;
+ };
+ };
+
+ smb_therm {
+ smb_therm_default: smb_therm_default {
+ pins = "gpio4";
+ bias-high-impedance;
+ };
+ };
+};
+
&pmi632_qg {
qcom,battery-data = <&mtp_batterydata>;
};
diff --git a/arch/arm64/boot/dts/qcom/sdm670-cdp.dtsi b/arch/arm64/boot/dts/qcom/sdm670-cdp.dtsi
index bd88087..da4d27d 100644
--- a/arch/arm64/boot/dts/qcom/sdm670-cdp.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm670-cdp.dtsi
@@ -1,4 +1,4 @@
-/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2017-2018, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -143,7 +143,7 @@
cam_snapshot {
label = "cam_snapshot";
- gpios = <&tlmm 91 0>;
+ gpios = <&tlmm 91 GPIO_ACTIVE_LOW>;
linux,input-type = <1>;
linux,code = <766>;
gpio-key,wakeup;
@@ -153,7 +153,7 @@
cam_focus {
label = "cam_focus";
- gpios = <&tlmm 92 0>;
+ gpios = <&tlmm 92 GPIO_ACTIVE_HIGH>;
linux,input-type = <1>;
linux,code = <528>;
gpio-key,wakeup;
diff --git a/arch/arm64/boot/dts/qcom/sdm670-mtp.dtsi b/arch/arm64/boot/dts/qcom/sdm670-mtp.dtsi
index cc55127..7764837 100644
--- a/arch/arm64/boot/dts/qcom/sdm670-mtp.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm670-mtp.dtsi
@@ -202,7 +202,7 @@
cam_snapshot {
label = "cam_snapshot";
- gpios = <&tlmm 91 0>;
+ gpios = <&tlmm 91 GPIO_ACTIVE_LOW>;
linux,input-type = <1>;
linux,code = <766>;
gpio-key,wakeup;
@@ -212,7 +212,7 @@
cam_focus {
label = "cam_focus";
- gpios = <&tlmm 92 0>;
+ gpios = <&tlmm 92 GPIO_ACTIVE_HIGH>;
linux,input-type = <1>;
linux,code = <528>;
gpio-key,wakeup;
diff --git a/arch/arm64/configs/msm8937-perf_defconfig b/arch/arm64/configs/msm8937-perf_defconfig
index 4c6b6a1..72a8fb4 100644
--- a/arch/arm64/configs/msm8937-perf_defconfig
+++ b/arch/arm64/configs/msm8937-perf_defconfig
@@ -68,7 +68,6 @@
CONFIG_ZSMALLOC=y
CONFIG_PROCESS_RECLAIM=y
CONFIG_SECCOMP=y
-CONFIG_HARDEN_BRANCH_PREDICTOR=y
CONFIG_ARMV8_DEPRECATED=y
CONFIG_SWP_EMULATION=y
CONFIG_CP15_BARRIER_EMULATION=y
@@ -148,6 +147,7 @@
CONFIG_NETFILTER_XT_TARGET_TRACE=y
CONFIG_NETFILTER_XT_TARGET_SECMARK=y
CONFIG_NETFILTER_XT_TARGET_TCPMSS=y
+CONFIG_NETFILTER_XT_MATCH_BPF=y
CONFIG_NETFILTER_XT_MATCH_COMMENT=y
CONFIG_NETFILTER_XT_MATCH_CONNLIMIT=y
CONFIG_NETFILTER_XT_MATCH_CONNMARK=y
diff --git a/arch/arm64/configs/msm8937_defconfig b/arch/arm64/configs/msm8937_defconfig
index 11bd200..fc7dc6c 100644
--- a/arch/arm64/configs/msm8937_defconfig
+++ b/arch/arm64/configs/msm8937_defconfig
@@ -71,7 +71,6 @@
CONFIG_ZSMALLOC=y
CONFIG_PROCESS_RECLAIM=y
CONFIG_SECCOMP=y
-CONFIG_HARDEN_BRANCH_PREDICTOR=y
CONFIG_ARMV8_DEPRECATED=y
CONFIG_SWP_EMULATION=y
CONFIG_CP15_BARRIER_EMULATION=y
@@ -152,6 +151,7 @@
CONFIG_NETFILTER_XT_TARGET_TRACE=y
CONFIG_NETFILTER_XT_TARGET_SECMARK=y
CONFIG_NETFILTER_XT_TARGET_TCPMSS=y
+CONFIG_NETFILTER_XT_MATCH_BPF=y
CONFIG_NETFILTER_XT_MATCH_COMMENT=y
CONFIG_NETFILTER_XT_MATCH_CONNLIMIT=y
CONFIG_NETFILTER_XT_MATCH_CONNMARK=y
diff --git a/drivers/gpu/drm/msm/dsi-staging/dsi_display.c b/drivers/gpu/drm/msm/dsi-staging/dsi_display.c
index adcec15..5c34b8e 100644
--- a/drivers/gpu/drm/msm/dsi-staging/dsi_display.c
+++ b/drivers/gpu/drm/msm/dsi-staging/dsi_display.c
@@ -559,9 +559,6 @@
if (dsi_ctrl_validate_host_state(ctrl->ctrl))
return 1;
- /* acquire panel_lock to make sure no commands are in progress */
- dsi_panel_acquire_panel_lock(panel);
-
config = &(panel->esd_config);
lenp = config->status_valid_params ?: config->status_cmds_rlen;
count = config->status_cmd.count;
@@ -580,7 +577,7 @@
rc = dsi_ctrl_cmd_transfer(ctrl->ctrl, &cmds[i].msg, flags);
if (rc <= 0) {
pr_err("rx cmd transfer failed rc=%d\n", rc);
- goto error;
+ return rc;
}
memcpy(config->return_buf + start,
@@ -588,9 +585,6 @@
start += lenp[i];
}
-error:
- /* release panel_lock */
- dsi_panel_release_panel_lock(panel);
return rc;
}
@@ -709,15 +703,16 @@
u32 status_mode;
int rc = 0x1;
- if (dsi_display == NULL)
+ if (!dsi_display || !dsi_display->panel)
return -EINVAL;
- mutex_lock(&dsi_display->display_lock);
-
panel = dsi_display->panel;
+
+ dsi_panel_acquire_panel_lock(panel);
+
if (!panel->panel_initialized) {
pr_debug("Panel not initialized\n");
- mutex_unlock(&dsi_display->display_lock);
+ dsi_panel_release_panel_lock(panel);
return rc;
}
@@ -742,7 +737,7 @@
dsi_display_clk_ctrl(dsi_display->dsi_clk_handle,
DSI_ALL_CLKS, DSI_CLK_OFF);
- mutex_unlock(&dsi_display->display_lock);
+ dsi_panel_release_panel_lock(panel);
return rc;
}
diff --git a/drivers/gpu/drm/msm/dsi-staging/dsi_drm.c b/drivers/gpu/drm/msm/dsi-staging/dsi_drm.c
index 5b47865..6b5bfb4 100644
--- a/drivers/gpu/drm/msm/dsi-staging/dsi_drm.c
+++ b/drivers/gpu/drm/msm/dsi-staging/dsi_drm.c
@@ -186,6 +186,7 @@
{
int rc = 0;
struct dsi_bridge *c_bridge = to_dsi_bridge(bridge);
+ struct dsi_display *display;
if (!bridge) {
pr_err("Invalid params\n");
@@ -197,11 +198,15 @@
pr_debug("[%d] seamless enable\n", c_bridge->id);
return;
}
+ display = c_bridge->display;
- rc = dsi_display_post_enable(c_bridge->display);
+ rc = dsi_display_post_enable(display);
if (rc)
pr_err("[%d] DSI display post enabled failed, rc=%d\n",
c_bridge->id, rc);
+
+ if (display && display->drm_conn)
+ sde_connector_helper_bridge_enable(display->drm_conn);
}
static void dsi_bridge_disable(struct drm_bridge *bridge)
diff --git a/drivers/gpu/drm/msm/msm_drv.c b/drivers/gpu/drm/msm/msm_drv.c
index 19df8be..0697db8 100644
--- a/drivers/gpu/drm/msm/msm_drv.c
+++ b/drivers/gpu/drm/msm/msm_drv.c
@@ -947,6 +947,14 @@
struct msm_kms *kms = priv->kms;
int i;
+ /* check for splash status before triggering cleanup
+ * if we end up here with splash status ON i.e before first
+ * commit then ignore the last close call
+ */
+ if (kms && kms->funcs && kms->funcs->check_for_splash
+ && kms->funcs->check_for_splash(kms))
+ return;
+
/*
* clean up vblank disable immediately as this is the last close.
*/
diff --git a/drivers/gpu/drm/msm/msm_kms.h b/drivers/gpu/drm/msm/msm_kms.h
index db9e7ee..e99ff9c 100644
--- a/drivers/gpu/drm/msm/msm_kms.h
+++ b/drivers/gpu/drm/msm/msm_kms.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2016-2017, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2016-2018, The Linux Foundation. All rights reserved.
* Copyright (C) 2013 Red Hat
* Author: Rob Clark <robdclark@gmail.com>
*
@@ -106,6 +106,8 @@
unsigned int domain);
/* handle continuous splash */
int (*cont_splash_config)(struct msm_kms *kms);
+ /* check for continuous splash status */
+ bool (*check_for_splash)(struct msm_kms *kms);
};
struct msm_kms {
diff --git a/drivers/gpu/drm/msm/sde/sde_ad4.h b/drivers/gpu/drm/msm/sde/sde_ad4.h
index bf08360..b254d7d 100644
--- a/drivers/gpu/drm/msm/sde/sde_ad4.h
+++ b/drivers/gpu/drm/msm/sde/sde_ad4.h
@@ -52,6 +52,7 @@
AD_IPC_SUSPEND,
AD_IPC_RESUME,
AD_IPC_RESET,
+ AD_VSYNC_UPDATE,
AD_PROPMAX,
};
diff --git a/drivers/gpu/drm/msm/sde/sde_color_processing.c b/drivers/gpu/drm/msm/sde/sde_color_processing.c
index 0f55b19..47ff024 100644
--- a/drivers/gpu/drm/msm/sde/sde_color_processing.c
+++ b/drivers/gpu/drm/msm/sde/sde_color_processing.c
@@ -88,6 +88,7 @@
enum ad_property ad_prop);
static void sde_cp_notify_hist_event(struct drm_crtc *crtc_drm, void *arg);
+static void sde_cp_update_ad_vsync_prop(struct sde_crtc *sde_crtc, u32 val);
#define setup_dspp_prop_install_funcs(func) \
do { \
@@ -138,6 +139,7 @@
SDE_CP_CRTC_DSPP_AD_ASSERTIVENESS,
SDE_CP_CRTC_DSPP_AD_BACKLIGHT,
SDE_CP_CRTC_DSPP_AD_STRENGTH,
+ SDE_CP_CRTC_DSPP_AD_VSYNC_COUNT,
SDE_CP_CRTC_DSPP_MAX,
/* DSPP features end */
@@ -407,6 +409,7 @@
if (IS_ERR(sde_crtc->hist_blob))
sde_crtc->hist_blob = NULL;
+ sde_crtc->ad_vsync_count = 0;
mutex_init(&sde_crtc->crtc_cp_lock);
INIT_LIST_HEAD(&sde_crtc->active_list);
INIT_LIST_HEAD(&sde_crtc->dirty_list);
@@ -789,6 +792,9 @@
ad_cfg.prop = AD_MODE;
ad_cfg.hw_cfg = &hw_cfg;
hw_dspp->ops.setup_ad(hw_dspp, &ad_cfg);
+ sde_crtc->ad_vsync_count = 0;
+ sde_cp_update_ad_vsync_prop(sde_crtc,
+ sde_crtc->ad_vsync_count);
break;
case SDE_CP_CRTC_DSPP_AD_INIT:
if (!hw_dspp || !hw_dspp->ops.setup_ad) {
@@ -798,6 +804,9 @@
ad_cfg.prop = AD_INIT;
ad_cfg.hw_cfg = &hw_cfg;
hw_dspp->ops.setup_ad(hw_dspp, &ad_cfg);
+ sde_crtc->ad_vsync_count = 0;
+ sde_cp_update_ad_vsync_prop(sde_crtc,
+ sde_crtc->ad_vsync_count);
break;
case SDE_CP_CRTC_DSPP_AD_CFG:
if (!hw_dspp || !hw_dspp->ops.setup_ad) {
@@ -807,6 +816,9 @@
ad_cfg.prop = AD_CFG;
ad_cfg.hw_cfg = &hw_cfg;
hw_dspp->ops.setup_ad(hw_dspp, &ad_cfg);
+ sde_crtc->ad_vsync_count = 0;
+ sde_cp_update_ad_vsync_prop(sde_crtc,
+ sde_crtc->ad_vsync_count);
break;
case SDE_CP_CRTC_DSPP_AD_INPUT:
if (!hw_dspp || !hw_dspp->ops.setup_ad) {
@@ -816,6 +828,9 @@
ad_cfg.prop = AD_INPUT;
ad_cfg.hw_cfg = &hw_cfg;
hw_dspp->ops.setup_ad(hw_dspp, &ad_cfg);
+ sde_crtc->ad_vsync_count = 0;
+ sde_cp_update_ad_vsync_prop(sde_crtc,
+ sde_crtc->ad_vsync_count);
break;
case SDE_CP_CRTC_DSPP_AD_ASSERTIVENESS:
if (!hw_dspp || !hw_dspp->ops.setup_ad) {
@@ -825,6 +840,9 @@
ad_cfg.prop = AD_ASSERTIVE;
ad_cfg.hw_cfg = &hw_cfg;
hw_dspp->ops.setup_ad(hw_dspp, &ad_cfg);
+ sde_crtc->ad_vsync_count = 0;
+ sde_cp_update_ad_vsync_prop(sde_crtc,
+ sde_crtc->ad_vsync_count);
break;
case SDE_CP_CRTC_DSPP_AD_BACKLIGHT:
if (!hw_dspp || !hw_dspp->ops.setup_ad) {
@@ -834,6 +852,9 @@
ad_cfg.prop = AD_BACKLIGHT;
ad_cfg.hw_cfg = &hw_cfg;
hw_dspp->ops.setup_ad(hw_dspp, &ad_cfg);
+ sde_crtc->ad_vsync_count = 0;
+ sde_cp_update_ad_vsync_prop(sde_crtc,
+ sde_crtc->ad_vsync_count);
break;
case SDE_CP_CRTC_DSPP_AD_STRENGTH:
if (!hw_dspp || !hw_dspp->ops.setup_ad) {
@@ -843,6 +864,9 @@
ad_cfg.prop = AD_STRENGTH;
ad_cfg.hw_cfg = &hw_cfg;
hw_dspp->ops.setup_ad(hw_dspp, &ad_cfg);
+ sde_crtc->ad_vsync_count = 0;
+ sde_cp_update_ad_vsync_prop(sde_crtc,
+ sde_crtc->ad_vsync_count);
break;
default:
ret = -EINVAL;
@@ -924,10 +948,15 @@
DRM_DEBUG_DRIVER("Dirty list is empty\n");
goto exit;
}
- sde_cp_ad_set_prop(sde_crtc, AD_IPC_RESET);
set_dspp_flush = true;
}
+ if (!list_empty(&sde_crtc->ad_active)) {
+ sde_cp_ad_set_prop(sde_crtc, AD_IPC_RESET);
+ sde_cp_ad_set_prop(sde_crtc, AD_VSYNC_UPDATE);
+ sde_cp_update_ad_vsync_prop(sde_crtc, sde_crtc->ad_vsync_count);
+ }
+
list_for_each_entry_safe(prop_node, n, &sde_crtc->dirty_list,
dirty_list) {
sde_dspp_feature = crtc_feature_map[prop_node->feature];
@@ -1449,6 +1478,9 @@
"SDE_DSPP_AD_V4_BACKLIGHT",
SDE_CP_CRTC_DSPP_AD_BACKLIGHT, 0, (BIT(16) - 1),
0);
+ sde_cp_crtc_install_range_property(crtc,
+ "SDE_DSPP_AD_V4_VSYNC_COUNT",
+ SDE_CP_CRTC_DSPP_AD_VSYNC_COUNT, 0, U32_MAX, 0);
break;
default:
DRM_ERROR("version %d not supported\n", version);
@@ -1867,6 +1899,11 @@
hw_cfg.displayh = num_mixers * hw_lm->cfg.out_width;
hw_cfg.displayv = hw_lm->cfg.out_height;
hw_cfg.mixer_info = hw_lm;
+
+ if (ad_prop == AD_VSYNC_UPDATE) {
+ hw_cfg.payload = &sde_crtc->ad_vsync_count;
+ hw_cfg.len = sizeof(sde_crtc->ad_vsync_count);
+ }
ad_cfg.prop = ad_prop;
ad_cfg.hw_cfg = &hw_cfg;
ret = hw_dspp->ops.validate_ad(hw_dspp, (u32 *)&ad_prop);
@@ -2118,3 +2155,35 @@
exit:
return ret;
}
+
+void sde_cp_update_ad_vsync_count(struct drm_crtc *crtc, u32 val)
+{
+ struct sde_crtc *sde_crtc;
+
+ if (!crtc) {
+ DRM_ERROR("invalid crtc %pK\n", crtc);
+ return;
+ }
+
+ sde_crtc = to_sde_crtc(crtc);
+ if (!sde_crtc) {
+ DRM_ERROR("invalid sde_crtc %pK\n", sde_crtc);
+ return;
+ }
+
+ sde_crtc->ad_vsync_count = val;
+ sde_cp_update_ad_vsync_prop(sde_crtc, val);
+}
+
+static void sde_cp_update_ad_vsync_prop(struct sde_crtc *sde_crtc, u32 val)
+{
+ struct sde_cp_node *prop_node = NULL;
+
+ list_for_each_entry(prop_node, &sde_crtc->feature_list, feature_list) {
+ if (prop_node->feature == SDE_CP_CRTC_DSPP_AD_VSYNC_COUNT) {
+ prop_node->prop_val = val;
+ pr_debug("AD vsync count updated to %d\n", val);
+ return;
+ }
+ }
+}
diff --git a/drivers/gpu/drm/msm/sde/sde_color_processing.h b/drivers/gpu/drm/msm/sde/sde_color_processing.h
index 7eb1738..620db26 100644
--- a/drivers/gpu/drm/msm/sde/sde_color_processing.h
+++ b/drivers/gpu/drm/msm/sde/sde_color_processing.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2016-2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2016-2018, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -146,4 +146,11 @@
*/
int sde_cp_hist_interrupt(struct drm_crtc *crtc_drm, bool en,
struct sde_irq_callback *hist_irq);
+
+/**
+ * sde_cp_update_ad_vsync_count: Api to update AD vsync count
+ * @crtc: Pointer to crtc.
+ * @val: vsync count value
+ */
+void sde_cp_update_ad_vsync_count(struct drm_crtc *crtc, u32 val);
#endif /*_SDE_COLOR_PROCESSING_H */
diff --git a/drivers/gpu/drm/msm/sde/sde_connector.c b/drivers/gpu/drm/msm/sde/sde_connector.c
index d2f8d12..15b5465 100644
--- a/drivers/gpu/drm/msm/sde/sde_connector.c
+++ b/drivers/gpu/drm/msm/sde/sde_connector.c
@@ -623,6 +623,7 @@
void sde_connector_helper_bridge_disable(struct drm_connector *connector)
{
int rc;
+ struct sde_connector *c_conn = NULL;
if (!connector)
return;
@@ -633,6 +634,34 @@
connector->base.id, rc);
SDE_EVT32(connector->base.id, SDE_EVTLOG_ERROR);
}
+
+ /* Disable ESD thread */
+ sde_connector_schedule_status_work(connector, false);
+
+ c_conn = to_sde_connector(connector);
+ if (c_conn->panel_dead) {
+ c_conn->bl_device->props.power = FB_BLANK_POWERDOWN;
+ c_conn->bl_device->props.state |= BL_CORE_FBBLANK;
+ backlight_update_status(c_conn->bl_device);
+ }
+}
+
+void sde_connector_helper_bridge_enable(struct drm_connector *connector)
+{
+ struct sde_connector *c_conn = NULL;
+
+ if (!connector)
+ return;
+
+ c_conn = to_sde_connector(connector);
+
+ /* Special handling for ESD recovery case */
+ if (c_conn->panel_dead) {
+ c_conn->bl_device->props.power = FB_BLANK_UNBLANK;
+ c_conn->bl_device->props.state &= ~BL_CORE_FBBLANK;
+ backlight_update_status(c_conn->bl_device);
+ c_conn->panel_dead = false;
+ }
}
int sde_connector_clk_ctrl(struct drm_connector *connector, bool enable)
@@ -1734,15 +1763,15 @@
static void _sde_connector_report_panel_dead(struct sde_connector *conn)
{
struct drm_event event;
- bool panel_dead = true;
if (!conn)
return;
+ conn->panel_dead = true;
event.type = DRM_EVENT_PANEL_DEAD;
event.length = sizeof(bool);
msm_mode_object_event_notify(&conn->base.base,
- conn->base.dev, &event, (u8 *)&panel_dead);
+ conn->base.dev, &event, (u8 *)&conn->panel_dead);
sde_encoder_display_failure_notification(conn->encoder);
SDE_EVT32(SDE_EVTLOG_ERROR);
SDE_ERROR("esd check failed report PANEL_DEAD conn_id: %d enc_id: %d\n",
diff --git a/drivers/gpu/drm/msm/sde/sde_connector.h b/drivers/gpu/drm/msm/sde/sde_connector.h
index c6f348e..0c24454 100644
--- a/drivers/gpu/drm/msm/sde/sde_connector.h
+++ b/drivers/gpu/drm/msm/sde/sde_connector.h
@@ -324,6 +324,8 @@
* @status_work: work object to perform status checks
* @force_panel_dead: variable to trigger forced ESD recovery
* @esd_status_interval: variable to change ESD check interval in millisec
+ * @panel_dead: Flag to indicate if panel has gone bad
+ * @esd_status_check: Flag to indicate if ESD thread is scheduled or not
* @bl_scale_dirty: Flag to indicate PP BL scale value(s) is changed
* @bl_scale: BL scale value for ABA feature
* @bl_scale_ad: BL scale value for AD feature
@@ -365,7 +367,7 @@
struct delayed_work status_work;
u32 force_panel_dead;
u32 esd_status_interval;
-
+ bool panel_dead;
bool esd_status_check;
bool bl_scale_dirty;
@@ -763,6 +765,12 @@
void sde_connector_helper_bridge_disable(struct drm_connector *connector);
/**
+ * sde_connector_helper_bridge_enable - helper function for drm bridge enable
+ * @connector: Pointer to DRM connector object
+ */
+void sde_connector_helper_bridge_enable(struct drm_connector *connector);
+
+/**
* sde_connector_get_panel_vfp - helper to get panel vfp
* @connector: pointer to drm connector
* @h_active: panel width
diff --git a/drivers/gpu/drm/msm/sde/sde_crtc.c b/drivers/gpu/drm/msm/sde/sde_crtc.c
index 7daab2a..7df1cc9 100644
--- a/drivers/gpu/drm/msm/sde/sde_crtc.c
+++ b/drivers/gpu/drm/msm/sde/sde_crtc.c
@@ -2440,6 +2440,23 @@
}
/**
+ * _sde_crtc_clear_dim_layers_v1 - clear all dim layer settings
+ * @cstate: Pointer to sde crtc state
+ */
+static void _sde_crtc_clear_dim_layers_v1(struct sde_crtc_state *cstate)
+{
+ u32 i;
+
+ if (!cstate)
+ return;
+
+ for (i = 0; i < cstate->num_dim_layers; i++)
+ memset(&cstate->dim_layer[i], 0, sizeof(cstate->dim_layer[i]));
+
+ cstate->num_dim_layers = 0;
+}
+
+/**
* _sde_crtc_set_dim_layer_v1 - copy dim layer settings from userspace
* @cstate: Pointer to sde crtc state
* @user_ptr: User ptr for sde_drm_dim_layer_v1 struct
@@ -2459,6 +2476,8 @@
dim_layer = cstate->dim_layer;
if (!usr_ptr) {
+ /* usr_ptr is null when setting the default property value */
+ _sde_crtc_clear_dim_layers_v1(cstate);
SDE_DEBUG("dim_layer data removed\n");
return;
}
@@ -4125,6 +4144,7 @@
event.type = DRM_EVENT_CRTC_POWER;
event.length = sizeof(u32);
sde_cp_crtc_suspend(crtc);
+ sde_cp_update_ad_vsync_count(crtc, 0);
power_on = 0;
msm_mode_object_event_notify(&crtc->base, crtc->dev, &event,
(u8 *)&power_on);
diff --git a/drivers/gpu/drm/msm/sde/sde_crtc.h b/drivers/gpu/drm/msm/sde/sde_crtc.h
index 64f3821..116af84 100644
--- a/drivers/gpu/drm/msm/sde/sde_crtc.h
+++ b/drivers/gpu/drm/msm/sde/sde_crtc.h
@@ -155,6 +155,7 @@
* @dirty_list : list of color processing features are dirty
* @ad_dirty: list containing ad properties that are dirty
* @ad_active: list containing ad properties that are active
+ * @ad_vsync_count : count of vblank since last reset for AD
* @crtc_lock : crtc lock around create, destroy and access.
* @frame_pending : Whether or not an update is pending
* @frame_events : static allocation of in-flight frame events
@@ -224,6 +225,7 @@
struct list_head ad_dirty;
struct list_head ad_active;
struct list_head user_event_list;
+ u32 ad_vsync_count;
struct mutex crtc_lock;
struct mutex crtc_cp_lock;
diff --git a/drivers/gpu/drm/msm/sde/sde_encoder.c b/drivers/gpu/drm/msm/sde/sde_encoder.c
index bea6693..d287b71 100644
--- a/drivers/gpu/drm/msm/sde/sde_encoder.c
+++ b/drivers/gpu/drm/msm/sde/sde_encoder.c
@@ -80,6 +80,11 @@
/* Maximum number of VSYNC wait attempts for RSC state transition */
#define MAX_RSC_WAIT 5
+#define TOPOLOGY_DUALPIPE_MERGE_MODE(x) \
+ (((x) == SDE_RM_TOPOLOGY_DUALPIPE_DSCMERGE) || \
+ ((x) == SDE_RM_TOPOLOGY_DUALPIPE_3DMERGE) || \
+ ((x) == SDE_RM_TOPOLOGY_DUALPIPE_3DMERGE_DSC))
+
/**
* enum sde_enc_rc_events - events for resource control state machine
* @SDE_ENC_RC_EVENT_KICKOFF:
@@ -215,6 +220,7 @@
struct sde_encoder_virt {
struct drm_encoder base;
spinlock_t enc_spinlock;
+ struct mutex vblank_ctl_lock;
uint32_t bus_scaling_client;
uint32_t display_num_of_h_tiles;
@@ -2648,7 +2654,6 @@
struct sde_encoder_virt *sde_enc = NULL;
struct msm_drm_private *priv;
struct sde_kms *sde_kms;
- struct drm_connector *drm_conn = NULL;
enum sde_intf_mode intf_mode;
int i = 0;
@@ -2677,10 +2682,6 @@
SDE_EVT32(DRMID(drm_enc));
- /* Disable ESD thread */
- drm_conn = sde_enc->cur_master->connector;
- sde_connector_schedule_status_work(drm_conn, false);
-
/* wait for idle */
sde_encoder_wait_for_event(drm_enc, MSM_ENC_TX_COMPLETE);
@@ -3402,13 +3403,14 @@
static void _sde_encoder_setup_dither(struct sde_encoder_phys *phys)
{
void *dither_cfg;
- int ret = 0, rc;
+ int ret = 0, rc, i = 0;
size_t len = 0;
enum sde_rm_topology_name topology;
struct drm_encoder *drm_enc;
struct msm_mode_info mode_info;
struct msm_display_dsc_info *dsc = NULL;
struct sde_encoder_virt *sde_enc;
+ struct sde_hw_pingpong *hw_pp;
if (!phys || !phys->connector || !phys->hw_pp ||
!phys->hw_pp->ops.setup_dither || !phys->parent)
@@ -3431,12 +3433,24 @@
/* disable dither for 10 bpp or 10bpc dsc config */
if (dsc->bpp == 10 || dsc->bpc == 10) {
phys->hw_pp->ops.setup_dither(phys->hw_pp, NULL, 0);
+ return;
+ }
+
+ ret = sde_connector_get_dither_cfg(phys->connector,
+ phys->connector->state, &dither_cfg, &len);
+ if (ret)
+ return;
+
+ if (TOPOLOGY_DUALPIPE_MERGE_MODE(topology)) {
+ for (i = 0; i < MAX_CHANNELS_PER_ENC; i++) {
+ hw_pp = sde_enc->hw_pp[i];
+ if (hw_pp) {
+ phys->hw_pp->ops.setup_dither(hw_pp, dither_cfg,
+ len);
+ }
+ }
} else {
- ret = sde_connector_get_dither_cfg(phys->connector,
- phys->connector->state, &dither_cfg, &len);
- if (!ret)
- phys->hw_pp->ops.setup_dither(phys->hw_pp,
- dither_cfg, len);
+ phys->hw_pp->ops.setup_dither(phys->hw_pp, dither_cfg, len);
}
}
@@ -4388,6 +4402,7 @@
phys_params.parent = &sde_enc->base;
phys_params.parent_ops = parent_ops;
phys_params.enc_spinlock = &sde_enc->enc_spinlock;
+ phys_params.vblank_ctl_lock = &sde_enc->vblank_ctl_lock;
SDE_DEBUG("\n");
@@ -4530,6 +4545,7 @@
sde_enc->cur_master = NULL;
spin_lock_init(&sde_enc->enc_spinlock);
+ mutex_init(&sde_enc->vblank_ctl_lock);
drm_enc = &sde_enc->base;
drm_encoder_init(dev, drm_enc, &sde_encoder_funcs, drm_enc_mode, NULL);
drm_encoder_helper_add(drm_enc, &sde_encoder_helper_funcs);
@@ -4858,10 +4874,14 @@
SDE_EVT32_VERBOSE(DRMID(enc));
disp_thread = &priv->disp_thread[sde_enc->crtc->index];
-
- kthread_queue_work(&disp_thread->worker,
- &sde_enc->esd_trigger_work);
- kthread_flush_work(&sde_enc->esd_trigger_work);
+ if (current->tgid == disp_thread->thread->tgid) {
+ sde_encoder_resource_control(&sde_enc->base,
+ SDE_ENC_RC_EVENT_KICKOFF);
+ } else {
+ kthread_queue_work(&disp_thread->worker,
+ &sde_enc->esd_trigger_work);
+ kthread_flush_work(&sde_enc->esd_trigger_work);
+ }
/**
* panel may stop generating te signal (vsync) during esd failure. rsc
* hardware may hang without vsync. Avoid rsc hang by generating the
diff --git a/drivers/gpu/drm/msm/sde/sde_encoder.h b/drivers/gpu/drm/msm/sde/sde_encoder.h
index 0e8e9dd..8225dcd 100644
--- a/drivers/gpu/drm/msm/sde/sde_encoder.h
+++ b/drivers/gpu/drm/msm/sde/sde_encoder.h
@@ -242,10 +242,6 @@
* esd timeout or other display failure notification. This event flows from
* dsi, sde_connector to sde_encoder.
*
- * This api must not be called from crtc_commit (display) thread because it
- * requests the flush work on same thread. It is called from esd check thread
- * based on current design.
- *
* TODO: manage the event at sde_kms level for forward processing.
* @drm_enc: Pointer to drm encoder structure
* @Return: true if successful in updating the encoder structure
diff --git a/drivers/gpu/drm/msm/sde/sde_encoder_phys.h b/drivers/gpu/drm/msm/sde/sde_encoder_phys.h
index 9557d41..ea53af9 100644
--- a/drivers/gpu/drm/msm/sde/sde_encoder_phys.h
+++ b/drivers/gpu/drm/msm/sde/sde_encoder_phys.h
@@ -284,6 +284,7 @@
enum msm_display_compression_type comp_type;
spinlock_t *enc_spinlock;
enum sde_enc_enable_state enable_state;
+ struct mutex *vblank_ctl_lock;
atomic_t vblank_refcount;
atomic_t vsync_cnt;
atomic_t underrun_cnt;
@@ -434,6 +435,7 @@
enum sde_wb wb_idx;
enum msm_display_compression_type comp_type;
spinlock_t *enc_spinlock;
+ struct mutex *vblank_ctl_lock;
};
/**
diff --git a/drivers/gpu/drm/msm/sde/sde_encoder_phys_cmd.c b/drivers/gpu/drm/msm/sde/sde_encoder_phys_cmd.c
index 828d771..f06ceb7 100644
--- a/drivers/gpu/drm/msm/sde/sde_encoder_phys_cmd.c
+++ b/drivers/gpu/drm/msm/sde/sde_encoder_phys_cmd.c
@@ -680,6 +680,7 @@
return -EINVAL;
}
+ mutex_lock(phys_enc->vblank_ctl_lock);
refcount = atomic_read(&phys_enc->vblank_refcount);
/* Slave encoders don't report vblank */
@@ -713,6 +714,7 @@
enable, refcount, SDE_EVTLOG_ERROR);
}
+ mutex_unlock(phys_enc->vblank_ctl_lock);
return ret;
}
@@ -1398,6 +1400,7 @@
phys_enc->split_role = p->split_role;
phys_enc->intf_mode = INTF_MODE_CMD;
phys_enc->enc_spinlock = p->enc_spinlock;
+ phys_enc->vblank_ctl_lock = p->vblank_ctl_lock;
cmd_enc->stream_sel = 0;
phys_enc->enable_state = SDE_ENC_DISABLED;
phys_enc->comp_type = p->comp_type;
diff --git a/drivers/gpu/drm/msm/sde/sde_encoder_phys_vid.c b/drivers/gpu/drm/msm/sde/sde_encoder_phys_vid.c
index 3fd45cb..d363d62 100644
--- a/drivers/gpu/drm/msm/sde/sde_encoder_phys_vid.c
+++ b/drivers/gpu/drm/msm/sde/sde_encoder_phys_vid.c
@@ -625,6 +625,7 @@
return -EINVAL;
}
+ mutex_lock(phys_enc->vblank_ctl_lock);
refcount = atomic_read(&phys_enc->vblank_refcount);
vid_enc = to_sde_encoder_phys_vid(phys_enc);
@@ -666,6 +667,7 @@
vid_enc->hw_intf->idx - INTF_0,
enable, refcount, SDE_EVTLOG_ERROR);
}
+ mutex_unlock(phys_enc->vblank_ctl_lock);
return ret;
}
@@ -1239,6 +1241,7 @@
phys_enc->split_role = p->split_role;
phys_enc->intf_mode = INTF_MODE_VIDEO;
phys_enc->enc_spinlock = p->enc_spinlock;
+ phys_enc->vblank_ctl_lock = p->vblank_ctl_lock;
phys_enc->comp_type = p->comp_type;
for (i = 0; i < INTR_IDX_MAX; i++) {
irq = &phys_enc->irq[i];
diff --git a/drivers/gpu/drm/msm/sde/sde_encoder_phys_wb.c b/drivers/gpu/drm/msm/sde/sde_encoder_phys_wb.c
index 9a90075..a3f2c73 100644
--- a/drivers/gpu/drm/msm/sde/sde_encoder_phys_wb.c
+++ b/drivers/gpu/drm/msm/sde/sde_encoder_phys_wb.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2015-2017 The Linux Foundation. All rights reserved.
+ * Copyright (c) 2015-2018 The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -1410,6 +1410,7 @@
phys_enc->intf_mode = INTF_MODE_WB_LINE;
phys_enc->intf_idx = p->intf_idx;
phys_enc->enc_spinlock = p->enc_spinlock;
+ phys_enc->vblank_ctl_lock = p->vblank_ctl_lock;
atomic_set(&phys_enc->pending_retire_fence_cnt, 0);
INIT_LIST_HEAD(&wb_enc->irq_cb.list);
diff --git a/drivers/gpu/drm/msm/sde/sde_hw_ad4.c b/drivers/gpu/drm/msm/sde/sde_hw_ad4.c
index 66445da..e60defd 100644
--- a/drivers/gpu/drm/msm/sde/sde_hw_ad4.c
+++ b/drivers/gpu/drm/msm/sde/sde_hw_ad4.c
@@ -109,6 +109,9 @@
static int ad4_cfg_ipc_reset(struct sde_hw_dspp *dspp,
struct sde_ad_hw_cfg *cfg);
+static int ad4_vsync_update(struct sde_hw_dspp *dspp,
+ struct sde_ad_hw_cfg *cfg);
+
static ad4_prop_setup prop_set_func[ad4_state_max][AD_PROPMAX] = {
[ad4_state_idle][AD_MODE] = ad4_mode_setup_common,
[ad4_state_idle][AD_INIT] = ad4_init_setup_idle,
@@ -121,6 +124,7 @@
[ad4_state_idle][AD_IPC_SUSPEND] = ad4_no_op_setup,
[ad4_state_idle][AD_IPC_RESUME] = ad4_no_op_setup,
[ad4_state_idle][AD_IPC_RESET] = ad4_no_op_setup,
+ [ad4_state_idle][AD_VSYNC_UPDATE] = ad4_no_op_setup,
[ad4_state_startup][AD_MODE] = ad4_mode_setup_common,
[ad4_state_startup][AD_INIT] = ad4_init_setup,
@@ -133,6 +137,7 @@
[ad4_state_startup][AD_STRENGTH] = ad4_no_op_setup,
[ad4_state_startup][AD_IPC_RESUME] = ad4_no_op_setup,
[ad4_state_startup][AD_IPC_RESET] = ad4_ipc_reset_setup_startup,
+ [ad4_state_startup][AD_VSYNC_UPDATE] = ad4_vsync_update,
[ad4_state_run][AD_MODE] = ad4_mode_setup_common,
[ad4_state_run][AD_INIT] = ad4_init_setup_run,
@@ -145,6 +150,7 @@
[ad4_state_run][AD_IPC_SUSPEND] = ad4_ipc_suspend_setup_run,
[ad4_state_run][AD_IPC_RESUME] = ad4_no_op_setup,
[ad4_state_run][AD_IPC_RESET] = ad4_setup_debug,
+ [ad4_state_run][AD_VSYNC_UPDATE] = ad4_vsync_update,
[ad4_state_ipcs][AD_MODE] = ad4_no_op_setup,
[ad4_state_ipcs][AD_INIT] = ad4_no_op_setup,
@@ -157,6 +163,7 @@
[ad4_state_ipcs][AD_IPC_SUSPEND] = ad4_no_op_setup,
[ad4_state_ipcs][AD_IPC_RESUME] = ad4_ipc_resume_setup_ipcs,
[ad4_state_ipcs][AD_IPC_RESET] = ad4_no_op_setup,
+ [ad4_state_ipcs][AD_VSYNC_UPDATE] = ad4_no_op_setup,
[ad4_state_ipcr][AD_MODE] = ad4_mode_setup_common,
[ad4_state_ipcr][AD_INIT] = ad4_init_setup_ipcr,
@@ -169,6 +176,7 @@
[ad4_state_ipcr][AD_IPC_SUSPEND] = ad4_ipc_suspend_setup_ipcr,
[ad4_state_ipcr][AD_IPC_RESUME] = ad4_no_op_setup,
[ad4_state_ipcr][AD_IPC_RESET] = ad4_ipc_reset_setup_ipcr,
+ [ad4_state_ipcr][AD_VSYNC_UPDATE] = ad4_no_op_setup,
[ad4_state_manual][AD_MODE] = ad4_mode_setup_common,
[ad4_state_manual][AD_INIT] = ad4_init_setup,
@@ -181,6 +189,7 @@
[ad4_state_manual][AD_IPC_SUSPEND] = ad4_no_op_setup,
[ad4_state_manual][AD_IPC_RESUME] = ad4_no_op_setup,
[ad4_state_manual][AD_IPC_RESET] = ad4_setup_debug_manual,
+ [ad4_state_manual][AD_VSYNC_UPDATE] = ad4_no_op_setup,
};
struct ad4_info {
@@ -201,6 +210,7 @@
u32 irdx_control_0;
u32 tf_ctrl;
u32 vc_control_0;
+ u32 frame_pushes;
};
static struct ad4_info info[DSPP_MAX] = {
@@ -905,6 +915,8 @@
val = (ad_cfg->cfg_param_046 & (BIT(16) - 1));
SDE_REG_WRITE(&dspp->hw, dspp->cap->sblk->ad.base + blk_offset, val);
+ info[dspp->idx].frame_pushes = ad_cfg->cfg_param_047;
+
return 0;
}
@@ -1546,3 +1558,25 @@
ad4_mode_setup(dspp, info[dspp->idx].mode);
return 0;
}
+
+static int ad4_vsync_update(struct sde_hw_dspp *dspp,
+ struct sde_ad_hw_cfg *cfg)
+{
+ u32 *count;
+ struct sde_hw_mixer *hw_lm;
+
+ if (cfg->hw_cfg->len != sizeof(u32) || !cfg->hw_cfg->payload) {
+ DRM_ERROR("invalid sz param exp %zd given %d cfg %pK\n",
+ sizeof(u32), cfg->hw_cfg->len, cfg->hw_cfg->payload);
+ return -EINVAL;
+ }
+
+ count = (u32 *)(cfg->hw_cfg->payload);
+ hw_lm = cfg->hw_cfg->mixer_info;
+
+ if (hw_lm && !hw_lm->cfg.right_mixer &&
+ (*count < info[dspp->idx].frame_pushes))
+ (*count)++;
+
+ return 0;
+}
diff --git a/drivers/gpu/drm/msm/sde/sde_hw_mdss.h b/drivers/gpu/drm/msm/sde/sde_hw_mdss.h
index 8022f23..9b4e03d 100644
--- a/drivers/gpu/drm/msm/sde/sde_hw_mdss.h
+++ b/drivers/gpu/drm/msm/sde/sde_hw_mdss.h
@@ -570,7 +570,8 @@
/**
* struct sde_splash_data - Struct contains details of continuous splash
* memory region and initial pipeline configuration.
- * @smmu_handoff_pending:boolean to notify handoff from splash memory to smmu
+ * @resource_handoff_pending: boolean to notify boot up resource handoff
+ * is pending.
* @splash_base: Base address of continuous splash region reserved
* by bootloader
* @splash_size: Size of continuous splash region
@@ -585,7 +586,7 @@
* @single_flush_en: Stores if the single flush is enabled.
*/
struct sde_splash_data {
- bool smmu_handoff_pending;
+ bool resource_handoff_pending;
unsigned long splash_base;
u32 splash_size;
struct ctl_top top[CTL_MAX - CTL_0];
diff --git a/drivers/gpu/drm/msm/sde/sde_kms.c b/drivers/gpu/drm/msm/sde/sde_kms.c
index 33bdec9..b0a52a7 100644
--- a/drivers/gpu/drm/msm/sde/sde_kms.c
+++ b/drivers/gpu/drm/msm/sde/sde_kms.c
@@ -936,9 +936,6 @@
}
}
- if (sde_kms->splash_data.smmu_handoff_pending)
- sde_kms->splash_data.smmu_handoff_pending = false;
-
/*
* NOTE: for secure use cases we want to apply the new HW
* configuration only after completing preparation for secure
@@ -972,6 +969,62 @@
}
}
+static void _sde_kms_release_splash_resource(struct sde_kms *sde_kms,
+ struct drm_atomic_state *old_state)
+{
+ struct drm_crtc *crtc;
+ struct drm_crtc_state *crtc_state;
+ bool primary_crtc_active = false;
+ struct msm_drm_private *priv;
+ int i, rc = 0;
+
+ priv = sde_kms->dev->dev_private;
+
+ if (!sde_kms->splash_data.resource_handoff_pending)
+ return;
+
+ SDE_EVT32(SDE_EVTLOG_FUNC_CASE1);
+ for_each_crtc_in_state(old_state, crtc, crtc_state, i) {
+ if (crtc->state->active)
+ primary_crtc_active = true;
+ SDE_EVT32(crtc->base.id, crtc->state->active);
+ }
+
+ if (!primary_crtc_active) {
+ SDE_EVT32(SDE_EVTLOG_FUNC_CASE2);
+ return;
+ }
+
+ sde_kms->splash_data.resource_handoff_pending = false;
+
+ if (sde_kms->splash_data.cont_splash_en) {
+ SDE_DEBUG("disabling cont_splash feature\n");
+ sde_kms->splash_data.cont_splash_en = false;
+
+ for (i = 0; i < SDE_POWER_HANDLE_DBUS_ID_MAX; i++)
+ sde_power_data_bus_set_quota(&priv->phandle,
+ sde_kms->core_client,
+ SDE_POWER_HANDLE_DATA_BUS_CLIENT_RT, i,
+ SDE_POWER_HANDLE_ENABLE_BUS_AB_QUOTA,
+ SDE_POWER_HANDLE_ENABLE_BUS_IB_QUOTA);
+
+ sde_power_resource_enable(&priv->phandle, sde_kms->core_client,
+ false);
+ }
+
+ if (sde_kms->splash_data.splash_base) {
+ _sde_kms_splash_smmu_unmap(sde_kms);
+
+ rc = _sde_kms_release_splash_buffer(
+ sde_kms->splash_data.splash_base,
+ sde_kms->splash_data.splash_size);
+ if (rc)
+ pr_err("failed to release splash memory\n");
+ sde_kms->splash_data.splash_base = 0;
+ sde_kms->splash_data.splash_size = 0;
+ }
+}
+
static void sde_kms_complete_commit(struct msm_kms *kms,
struct drm_atomic_state *old_state)
{
@@ -1019,39 +1072,9 @@
sde_power_resource_enable(&priv->phandle, sde_kms->core_client, false);
+ _sde_kms_release_splash_resource(sde_kms, old_state);
+
SDE_EVT32_VERBOSE(SDE_EVTLOG_FUNC_EXIT);
-
- if (sde_kms->splash_data.cont_splash_en) {
- /* Releasing splash resources as we have first frame update */
- rc = _sde_kms_splash_smmu_unmap(sde_kms);
- SDE_DEBUG("Disabling cont_splash feature\n");
- sde_kms->splash_data.cont_splash_en = false;
-
- for (i = 0; i < SDE_POWER_HANDLE_DBUS_ID_MAX; i++)
- sde_power_data_bus_set_quota(&priv->phandle,
- sde_kms->core_client,
- SDE_POWER_HANDLE_DATA_BUS_CLIENT_RT, i,
- SDE_POWER_HANDLE_ENABLE_BUS_AB_QUOTA,
- SDE_POWER_HANDLE_ENABLE_BUS_IB_QUOTA);
-
- sde_power_resource_enable(&priv->phandle,
- sde_kms->core_client, false);
- SDE_DEBUG("removing Vote for MDP Resources\n");
- }
-
- /*
- * Even for continuous splash disabled cases we have to release
- * splash memory reservation back to system after first frame update.
- */
- if (sde_kms->splash_data.splash_base) {
- rc = _sde_kms_release_splash_buffer(
- sde_kms->splash_data.splash_base,
- sde_kms->splash_data.splash_size);
- if (rc)
- pr_err("Failed to release splash memory\n");
- sde_kms->splash_data.splash_base = 0;
- sde_kms->splash_data.splash_size = 0;
- }
}
static void sde_kms_wait_for_commit_done(struct msm_kms *kms,
@@ -2704,6 +2727,19 @@
return rc;
}
+static bool sde_kms_check_for_splash(struct msm_kms *kms)
+{
+ struct sde_kms *sde_kms;
+
+ if (!kms) {
+ SDE_ERROR("invalid kms\n");
+ return false;
+ }
+
+ sde_kms = to_sde_kms(kms);
+ return sde_kms->splash_data.cont_splash_en;
+}
+
static int sde_kms_pm_suspend(struct device *dev)
{
struct drm_device *ddev;
@@ -2904,6 +2940,7 @@
.register_events = _sde_kms_register_events,
.get_address_space = _sde_kms_get_address_space,
.postopen = _sde_kms_post_open,
+ .check_for_splash = sde_kms_check_for_splash,
};
/* the caller api needs to turn on clock before calling it */
@@ -2956,8 +2993,7 @@
* address. To facilitate this requirement we need to have a
* one to one mapping on SMMU until we have our first frame.
*/
- if ((i == MSM_SMMU_DOMAIN_UNSECURE) &&
- sde_kms->splash_data.smmu_handoff_pending) {
+ if (i == MSM_SMMU_DOMAIN_UNSECURE) {
ret = mmu->funcs->set_attribute(mmu,
DOMAIN_ATTR_EARLY_MAP,
&early_map);
@@ -2986,27 +3022,27 @@
}
aspace->domain_attached = true;
early_map = 0;
+
/* Mapping splash memory block */
if ((i == MSM_SMMU_DOMAIN_UNSECURE) &&
- sde_kms->splash_data.smmu_handoff_pending) {
+ sde_kms->splash_data.splash_base) {
ret = _sde_kms_splash_smmu_map(sde_kms->dev, mmu,
&sde_kms->splash_data);
if (ret) {
SDE_ERROR("failed to map ret:%d\n", ret);
goto fail;
}
- /*
- * Turning off early map after generating one to one
- * mapping for splash address space.
- */
- ret = mmu->funcs->set_attribute(mmu,
- DOMAIN_ATTR_EARLY_MAP,
- &early_map);
- if (ret) {
- SDE_ERROR("failed to set map att ret:%d\n",
- ret);
- goto early_map_fail;
- }
+ }
+
+ /*
+ * Turning off early map after generating one to one
+ * mapping for splash address space.
+ */
+ ret = mmu->funcs->set_attribute(mmu, DOMAIN_ATTR_EARLY_MAP,
+ &early_map);
+ if (ret) {
+ SDE_ERROR("failed to set map att ret:%d\n", ret);
+ goto early_map_fail;
}
}
@@ -3285,12 +3321,7 @@
&sde_kms->splash_data,
sde_kms->catalog);
- /*
- * SMMU handoff is necessary for continuous splash enabled
- * scenario.
- */
- if (sde_kms->splash_data.cont_splash_en)
- sde_kms->splash_data.smmu_handoff_pending = true;
+ sde_kms->splash_data.resource_handoff_pending = true;
/* Initialize reg dma block which is a singleton */
rc = sde_reg_dma_init(sde_kms->reg_dma, sde_kms->catalog,
diff --git a/drivers/iio/imu/Kconfig b/drivers/iio/imu/Kconfig
index 1f1ad41..0a8b763 100644
--- a/drivers/iio/imu/Kconfig
+++ b/drivers/iio/imu/Kconfig
@@ -39,6 +39,7 @@
be called kmx61.
source "drivers/iio/imu/inv_mpu6050/Kconfig"
+source "drivers/iio/imu/inv_icm20602/Kconfig"
endmenu
diff --git a/drivers/iio/imu/Makefile b/drivers/iio/imu/Makefile
index c71bcd3..fab6a5e 100644
--- a/drivers/iio/imu/Makefile
+++ b/drivers/iio/imu/Makefile
@@ -15,5 +15,6 @@
obj-y += bmi160/
obj-y += inv_mpu6050/
+obj-y += inv_icm20602/
obj-$(CONFIG_KMX61) += kmx61.o
diff --git a/drivers/iio/imu/inv_icm20602/Kconfig b/drivers/iio/imu/inv_icm20602/Kconfig
new file mode 100644
index 0000000..fa88689
--- /dev/null
+++ b/drivers/iio/imu/inv_icm20602/Kconfig
@@ -0,0 +1,14 @@
+#
+# inv-icm20602 drivers for Invensense MPU devices and combos
+#
+config INV_ICM20602_IIO
+ tristate "Invensense ICM20602 devices"
+ depends on I2C && SYSFS
+ select IIO_BUFFER
+ select IIO_BUFFER_CB
+ select IIO_TRIGGERED_BUFFER
+ help
+ This driver supports the Invensense ICM20602 devices.
+ It is a gyroscope/accelerometer combo device.
+ This driver can be built as a module. The module will be called
+ inv-icm20602.
diff --git a/drivers/iio/imu/inv_icm20602/Makefile b/drivers/iio/imu/inv_icm20602/Makefile
new file mode 100644
index 0000000..d60c10a
--- /dev/null
+++ b/drivers/iio/imu/inv_icm20602/Makefile
@@ -0,0 +1,6 @@
+#
+# Makefile for Invensense icm20602 device.
+#
+
+obj-$(CONFIG_INV_ICM20602_IIO) += inv-icm20602.o
+inv-icm20602-objs := inv_icm20602_core.o inv_icm20602_ring.o inv_icm20602_trigger.o inv_icm20602_bsp.o
diff --git a/drivers/iio/imu/inv_icm20602/inv_icm20602_bsp.c b/drivers/iio/imu/inv_icm20602/inv_icm20602_bsp.c
new file mode 100644
index 0000000..bfff285
--- /dev/null
+++ b/drivers/iio/imu/inv_icm20602/inv_icm20602_bsp.c
@@ -0,0 +1,932 @@
+/*
+ * Copyright (c) 2018 The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/err.h>
+#include <linux/delay.h>
+#include <linux/sysfs.h>
+#include <linux/jiffies.h>
+#include <linux/irq.h>
+#include <linux/interrupt.h>
+#include <linux/kfifo.h>
+#include <linux/poll.h>
+#include <linux/i2c.h>
+#include <linux/spi/spi.h>
+#include "inv_icm20602_bsp.h"
+#include "inv_icm20602_iio.h"
+
+#define icm20602_init_reg_addr(head, tail, reg_map) { \
+ enum inv_icm20602_reg_addr i;\
+ for (i = head; i <= tail; i++) {\
+ reg_map->address = i;\
+ reg_map->reg_u.reg = 0x0;\
+ reg_map++;\
+ } \
+}
+
+#define icm20602_write_reg_simple(st, register) \
+ icm20602_write_reg(st, \
+ register.address, \
+ register.reg_u.reg)
+
+static struct inv_icm20602_reg_map reg_set_20602;
+
+int icm20602_init_reg_map(void)
+{
+ struct struct_XG_OFFS_TC_H *reg_map = &(reg_set_20602.XG_OFFS_TC_H);
+
+ icm20602_init_reg_addr(ADDR_XG_OFFS_TC_H,
+ ADDR_XG_OFFS_TC_L, reg_map);
+
+ icm20602_init_reg_addr(ADDR_YG_OFFS_TC_H,
+ ADDR_YG_OFFS_TC_L, reg_map);
+
+ icm20602_init_reg_addr(ADDR_ZG_OFFS_TC_H,
+ ADDR_ZG_OFFS_TC_L, reg_map);
+
+ icm20602_init_reg_addr(ADDR_SELF_TEST_X_ACCEL,
+ ADDR_SELF_TEST_Z_ACCEL, reg_map);
+
+ icm20602_init_reg_addr(ADDR_XG_OFFS_USRH,
+ ADDR_LP_MODE_CFG, reg_map);
+
+ icm20602_init_reg_addr(ADDR_ACCEL_WOM_X_THR,
+ ADDR_FIFO_EN, reg_map);
+
+ icm20602_init_reg_addr(ADDR_FSYNC_INT,
+ ADDR_GYRO_ZOUT_L, reg_map);
+
+ icm20602_init_reg_addr(ADDR_SELF_TEST_X_GYRO,
+ ADDR_SELF_TEST_Z_GYRO, reg_map);
+
+ icm20602_init_reg_addr(ADDR_FIFO_WM_TH1,
+ ADDR_FIFO_WM_TH2, reg_map);
+
+ icm20602_init_reg_addr(ADDR_SIGNAL_PATH_RESET,
+ ADDR_PWR_MGMT_2, reg_map);
+
+ icm20602_init_reg_addr(ADDR_I2C_IF,
+ ADDR_I2C_IF, reg_map);
+
+ icm20602_init_reg_addr(ADDR_FIFO_COUNTH,
+ ADDR_XA_OFFSET_L, reg_map);
+
+ icm20602_init_reg_addr(ADDR_YA_OFFSET_H,
+ ADDR_YA_OFFSET_L, reg_map);
+
+ icm20602_init_reg_addr(ADDR_ZA_OFFSET_H,
+ ADDR_ZA_OFFSET_L, reg_map);
+
+ return MPU_SUCCESS;
+}
+
+#define W_FLG 0
+#define R_FLG 1
+int icm20602_bulk_read(struct inv_icm20602_state *st,
+ int reg, char *buf, int size)
+{
+ int result = MPU_SUCCESS;
+ char tx_buf[2] = {0x0, 0x0};
+ int tmp_size = size;
+ int tmp_buf = buf;
+ struct i2c_msg msg[2];
+
+ if (!st || !buf)
+ return -MPU_FAIL;
+
+ if (st->interface == ICM20602_SPI) {
+ tx_buf[0] = ICM20602_READ_REG(reg);
+ result = spi_write_then_read(st->spi, &tx_buf[0],
+ 1, tmp_buf, size);
+ if (result) {
+ pr_err("mpu read reg %u failed, rc %d\n",
+ reg, result);
+ result = -MPU_READ_FAIL;
+ }
+ } else {
+ result = size;
+ while (tmp_size > 0) {
+#ifdef ICM20602_I2C_SMBUS
+ result += i2c_smbus_read_i2c_block_data(st->client,
+ reg, (tmp_size < 32)?tmp_size:32, tmp_buf);
+ tmp_size -= 32;
+ tmp_buf += tmp_size;
+#else
+ tx_buf[0] = reg;
+ msg[0].addr = st->client->addr;
+ msg[0].flags = W_FLG;
+ msg[0].len = 1;
+ msg[0].buf = tx_buf;
+
+ msg[1].addr = st->client->addr;
+ msg[1].flags = I2C_M_RD;
+ msg[1].len = (tmp_size < 32)?tmp_size:32;
+ msg[1].buf = tmp_buf;
+ i2c_transfer(st->client->adapter, msg, ARRAY_SIZE(msg));
+ tmp_size -= 32;
+ tmp_buf += tmp_size;
+#endif
+ }
+ }
+
+ return result;
+}
+
+static int icm20602_write_reg(struct inv_icm20602_state *st,
+ uint8_t reg, uint8_t val)
+{
+ int result = MPU_SUCCESS;
+ char txbuf[2] = {0x0, 0x0};
+ struct i2c_msg msg[1];
+
+ if (st->interface == ICM20602_SPI) {
+ txbuf[0] = ICM20602_WRITE_REG(reg);
+ txbuf[1] = val;
+ result = spi_write_then_read(st->spi, &txbuf[0], 2, NULL, 0);
+ if (result) {
+ pr_err("mpu write reg %u failed, rc %d\n",
+ reg, val);
+ result = -MPU_READ_FAIL;
+ }
+ } else if (st->interface == ICM20602_I2C) {
+#ifdef ICM20602_I2C_SMBUS
+ result = i2c_smbus_write_i2c_block_data(st->client,
+ reg, 1, &val);
+#else
+ txbuf[0] = reg;
+ txbuf[1] = val;
+ msg[0].addr = st->client->addr;
+ msg[0].flags = I2C_M_IGNORE_NAK;
+ msg[0].len = 2;
+ msg[0].buf = txbuf;
+
+ i2c_transfer(st->client->adapter, msg, ARRAY_SIZE(msg));
+#endif
+ }
+
+ return result;
+}
+
+static int icm20602_read_reg(struct inv_icm20602_state *st,
+ uint8_t reg, uint8_t *val)
+{
+ int result = MPU_SUCCESS;
+ char txbuf[1] = {0x0};
+ char rxbuf[1] = {0x0};
+ struct i2c_msg msg[2];
+
+ if (st->interface == ICM20602_SPI) {
+ txbuf[0] = ICM20602_READ_REG(reg);
+ result = spi_write_then_read(st->spi,
+ &txbuf[0], 1, rxbuf, 1);
+ if (result) {
+ pr_err("mpu read reg %u failed, rc %d\n",
+ reg, result);
+ result = -MPU_READ_FAIL;
+ }
+ } else if (st->interface == ICM20602_I2C) {
+#ifdef ICM20602_I2C_SMBUS
+ result = i2c_smbus_read_i2c_block_data(st->client,
+ reg, 1, rxbuf);
+ if (result != 1) {
+ pr_err("mpu read reg %u failed, rc %d\n",
+ reg, result);
+ result = -MPU_READ_FAIL;
+ }
+#else
+ txbuf[0] = reg;
+ msg[0].addr = st->client->addr;
+ msg[0].flags = W_FLG;
+ msg[0].len = 1;
+ msg[0].buf = txbuf;
+
+ msg[1].addr = st->client->addr;
+ msg[1].flags = I2C_M_RD;
+ msg[1].len = 1;
+ msg[1].buf = rxbuf;
+
+ i2c_transfer(st->client->adapter, msg, ARRAY_SIZE(msg));
+#endif
+ }
+ *val = rxbuf[0];
+
+ return result;
+}
+
+#define combine_8_to_16(upper, lower) ((upper << 8) | lower)
+
+int icm20602_read_raw(struct inv_icm20602_state *st,
+ struct struct_icm20602_real_data *real_data, uint32_t type)
+{
+ struct struct_icm20602_raw_data raw_data;
+
+ if (type & ACCEL != 0) {
+ icm20602_read_reg(st,
+ reg_set_20602.ACCEL_XOUT_H.address,
+ &raw_data.ACCEL_XOUT_H);
+ icm20602_read_reg(st,
+ reg_set_20602.ACCEL_XOUT_L.address,
+ &raw_data.ACCEL_XOUT_L);
+ real_data->ACCEL_XOUT =
+ combine_8_to_16(raw_data.ACCEL_XOUT_H,
+ raw_data.ACCEL_XOUT_L);
+
+ icm20602_read_reg(st,
+ reg_set_20602.ACCEL_YOUT_H.address,
+ &raw_data.ACCEL_YOUT_H);
+ icm20602_read_reg(st,
+ reg_set_20602.ACCEL_YOUT_L.address,
+ &raw_data.ACCEL_YOUT_L);
+ real_data->ACCEL_YOUT =
+ combine_8_to_16(raw_data.ACCEL_YOUT_H,
+ raw_data.ACCEL_YOUT_L);
+
+ icm20602_read_reg(st,
+ reg_set_20602.ACCEL_ZOUT_H.address,
+ &raw_data.ACCEL_ZOUT_H);
+ icm20602_read_reg(st,
+ reg_set_20602.ACCEL_ZOUT_L.address,
+ &raw_data.ACCEL_ZOUT_L);
+ real_data->ACCEL_ZOUT =
+ combine_8_to_16(raw_data.ACCEL_ZOUT_H,
+ raw_data.ACCEL_ZOUT_L);
+ }
+
+ if (type & GYRO != 0) {
+ icm20602_read_reg(st,
+ reg_set_20602.GYRO_XOUT_H.address,
+ &raw_data.GYRO_XOUT_H);
+ icm20602_read_reg(st,
+ reg_set_20602.GYRO_XOUT_L.address,
+ &raw_data.GYRO_XOUT_L);
+ real_data->GYRO_XOUT =
+ combine_8_to_16(raw_data.GYRO_XOUT_H,
+ raw_data.GYRO_XOUT_L);
+
+ icm20602_read_reg(st,
+ reg_set_20602.GYRO_YOUT_H.address,
+ &raw_data.GYRO_YOUT_H);
+ icm20602_read_reg(st,
+ reg_set_20602.GYRO_YOUT_L.address,
+ &raw_data.GYRO_YOUT_L);
+ real_data->GYRO_YOUT =
+ combine_8_to_16(raw_data.GYRO_YOUT_H,
+ raw_data.GYRO_YOUT_L);
+
+ icm20602_read_reg(st,
+ reg_set_20602.GYRO_ZOUT_H.address,
+ &raw_data.GYRO_ZOUT_H);
+ icm20602_read_reg(st,
+ reg_set_20602.GYRO_ZOUT_L.address,
+ &raw_data.GYRO_ZOUT_L);
+ real_data->GYRO_ZOUT =
+ combine_8_to_16(raw_data.GYRO_ZOUT_H,
+ raw_data.GYRO_ZOUT_L);
+ }
+
+ return MPU_SUCCESS;
+}
+
+int icm20602_read_fifo(struct inv_icm20602_state *st,
+ void *buf, const int size)
+{
+ return icm20602_bulk_read(st,
+ reg_set_20602.FIFO_R_W.address, buf, size);
+}
+
+int icm20602_start_fifo(struct inv_icm20602_state *st)
+{
+ struct icm20602_user_config *config = NULL;
+
+ config = st->config;
+
+ /* enable fifo */
+ if (config->fifo_enabled) {
+ reg_set_20602.USER_CTRL.reg_u.REG.FIFO_EN = 0x1;
+ if (icm20602_write_reg_simple(st,
+ reg_set_20602.USER_CTRL)) {
+ pr_err("icm20602 start fifo failed\n");
+ return -MPU_FAIL;
+ }
+
+ /* enable interrupt, need to test */
+ reg_set_20602.INT_ENABLE.reg_u.REG.FIFO_OFLOW_EN = 0x1;
+ reg_set_20602.INT_ENABLE.reg_u.REG.DATA_RDY_INT_EN = 0x0;
+ if (icm20602_write_reg_simple(st,
+ reg_set_20602.INT_ENABLE)) {
+ pr_err("icm20602 set FIFO_OFLOW_EN failed\n");
+ return -MPU_FAIL;
+ }
+ }
+
+ return MPU_SUCCESS;
+}
+
+static int icm20602_stop_fifo(struct inv_icm20602_state *st)
+{
+ struct icm20602_user_config *config = NULL;
+
+ config = st->config;
+ /* disable fifo */
+ if (config->fifo_enabled) {
+ reg_set_20602.USER_CTRL.reg_u.REG.FIFO_EN = 0x0;
+ reg_set_20602.USER_CTRL.reg_u.REG.FIFO_RST = 0x1;
+ if (icm20602_write_reg_simple(st,
+ reg_set_20602.USER_CTRL)) {
+ reg_set_20602.USER_CTRL.reg_u.REG.FIFO_RST = 0x0;
+ pr_err("icm20602 stop fifo failed\n");
+ return -MPU_FAIL;
+ }
+ reg_set_20602.USER_CTRL.reg_u.REG.FIFO_RST = 0x0;
+ }
+
+ return MPU_SUCCESS;
+}
+
+static int icm20602_config_waterlevel(struct inv_icm20602_state *st)
+{
+ struct icm20602_user_config *config = NULL;
+ uint8_t val = 0;
+
+ config = st->config;
+ if (config->fifo_enabled != true)
+ return MPU_SUCCESS;
+ /* config waterlevel as the fps need */
+ config->fifo_waterlevel = (config->user_fps_in_ms /
+ (1000 / config->gyro_accel_sample_rate))
+ *ICM20602_PACKAGE_SIZE;
+
+ if (config->fifo_waterlevel > 1023 ||
+ config->fifo_waterlevel/50 >
+ (1023-config->fifo_waterlevel)/ICM20602_PACKAGE_SIZE) {
+ pr_err("set fifo_waterlevel failed %d\n",
+ config->fifo_waterlevel);
+ return MPU_FAIL;
+ }
+ reg_set_20602.FIFO_WM_TH1.reg_u.reg =
+ (config->fifo_waterlevel & 0xff00) >> 8;
+ reg_set_20602.FIFO_WM_TH2.reg_u.reg =
+ (config->fifo_waterlevel & 0x00ff);
+
+ icm20602_write_reg_simple(st, reg_set_20602.FIFO_WM_TH1);
+ icm20602_write_reg_simple(st, reg_set_20602.FIFO_WM_TH2);
+ icm20602_read_reg(st, reg_set_20602.FIFO_WM_TH1.address, &val);
+ icm20602_read_reg(st, reg_set_20602.FIFO_WM_TH2.address, &val);
+
+ return MPU_SUCCESS;
+}
+
+static int icm20602_read_ST_code(struct inv_icm20602_state *st)
+{
+ struct icm20602_user_config *config = NULL;
+ int result = 0;
+
+ config = st->config;
+ result |= icm20602_read_reg(st, reg_set_20602.SELF_TEST_X_ACCEL.address,
+ &(config->acc_self_test.X));
+ result |= icm20602_read_reg(st, reg_set_20602.SELF_TEST_Y_ACCEL.address,
+ &(config->acc_self_test.Y));
+ result |= icm20602_read_reg(st, reg_set_20602.SELF_TEST_Z_ACCEL.address,
+ &(config->acc_self_test.Z));
+
+ result |= icm20602_read_reg(st, reg_set_20602.SELF_TEST_X_GYRO.address,
+ &(config->gyro_self_test.X));
+ result |= icm20602_read_reg(st, reg_set_20602.SELF_TEST_Y_GYRO.address,
+ &(config->gyro_self_test.Y));
+ result |= icm20602_read_reg(st, reg_set_20602.SELF_TEST_Z_GYRO.address,
+ &(config->gyro_self_test.Z));
+
+ return result;
+}
+
+static int icm20602_set_self_test(struct inv_icm20602_state *st)
+{
+ uint8_t raw_data[6] = {0, 0, 0, 0, 0, 0};
+ uint8_t selfTest[6];
+ float factory_trim[6];
+ int result = 0;
+ int ii;
+
+ reg_set_20602.SMPLRT_DIV.reg_u.REG.SMPLRT_DIV = 0;
+ result |= icm20602_write_reg_simple(st, reg_set_20602.SMPLRT_DIV);
+
+ reg_set_20602.CONFIG.reg_u.REG.DLFP_CFG = INV_ICM20602_GYRO_LFP_92HZ;
+ result |= icm20602_write_reg_simple(st, reg_set_20602.CONFIG);
+
+ reg_set_20602.GYRO_CONFIG.reg_u.REG.FCHOICE_B = 0x0;
+ reg_set_20602.GYRO_CONFIG.reg_u.REG.FS_SEL = ICM20602_GYRO_FSR_250DPS;
+ result |= icm20602_write_reg_simple(st, reg_set_20602.GYRO_CONFIG);
+
+ reg_set_20602.ACCEL_CONFIG2.reg_u.REG.A_DLPF_CFG = ICM20602_ACCLFP_99;
+ reg_set_20602.ACCEL_CONFIG2.reg_u.REG.ACCEL_FCHOICE_B = 0X0;
+ result |= icm20602_write_reg_simple(st, reg_set_20602.ACCEL_CONFIG2);
+
+ reg_set_20602.ACCEL_CONFIG.reg_u.REG.ACCEL_FS_SEL = ICM20602_ACC_FSR_2G;
+ result |= icm20602_write_reg_simple(st, reg_set_20602.ACCEL_CONFIG);
+
+ //icm20602_read_ST_code(st);
+
+ return 0;
+}
+
+static int icm20602_do_test_acc(struct inv_icm20602_state *st,
+ struct X_Y_Z *acc, struct X_Y_Z *acc_st)
+{
+ struct struct_icm20602_real_data *real_data =
+ kmalloc(sizeof(struct inv_icm20602_state), GFP_ATOMIC);
+ struct icm20602_user_config *config = st->config;
+ int i, j;
+
+ for (i = 0; i < SELFTEST_COUNT; i++) {
+ icm20602_read_raw(st, real_data, ACCEL);
+ acc->X += real_data->ACCEL_XOUT;
+ acc->Y += real_data->ACCEL_YOUT;
+ acc->Z += real_data->ACCEL_ZOUT;
+ usleep_range(1000, 1001);
+ }
+ acc->X /= SELFTEST_COUNT;
+ acc->X *= ST_PRECISION;
+
+ acc->Y /= SELFTEST_COUNT;
+ acc->Y *= ST_PRECISION;
+
+ acc->Z /= SELFTEST_COUNT;
+ acc->Z *= ST_PRECISION;
+
+ reg_set_20602.ACCEL_CONFIG.reg_u.REG.XG_ST = 0x1;
+ reg_set_20602.ACCEL_CONFIG.reg_u.REG.YG_ST = 0x1;
+ reg_set_20602.ACCEL_CONFIG.reg_u.REG.ZG_ST = 0x1;
+ icm20602_write_reg_simple(st, reg_set_20602.ACCEL_CONFIG);
+
+ for (i = 0; i < SELFTEST_COUNT; i++) {
+ icm20602_read_raw(st, real_data, ACCEL);
+ acc_st->X += real_data->ACCEL_XOUT;
+ acc_st->Y += real_data->ACCEL_YOUT;
+ acc_st->Z += real_data->ACCEL_ZOUT;
+ usleep_range(1000, 1001);
+ }
+ acc_st->X /= SELFTEST_COUNT;
+ acc_st->X *= ST_PRECISION;
+
+ acc_st->Y /= SELFTEST_COUNT;
+ acc_st->Y *= ST_PRECISION;
+
+ acc_st->Z /= SELFTEST_COUNT;
+ acc_st->Z *= ST_PRECISION;
+
+ return MPU_SUCCESS;
+}
+
+static int icm20602_do_test_gyro(struct inv_icm20602_state *st,
+ struct X_Y_Z *gyro, struct X_Y_Z *gyro_st)
+{
+ struct struct_icm20602_real_data *real_data =
+ kmalloc(sizeof(struct inv_icm20602_state), GFP_ATOMIC);
+ int i, j;
+
+ for (i = 0; i < SELFTEST_COUNT; i++) {
+ icm20602_read_raw(st, real_data, GYRO);
+ gyro->X += real_data->GYRO_XOUT;
+ gyro->Y += real_data->GYRO_YOUT;
+ gyro->Z += real_data->GYRO_ZOUT;
+ usleep_range(1000, 1001);
+ }
+ gyro->X /= SELFTEST_COUNT;
+ gyro->X *= ST_PRECISION;
+
+ gyro->Y /= SELFTEST_COUNT;
+ gyro->Y *= ST_PRECISION;
+
+ gyro->Z /= SELFTEST_COUNT;
+ gyro->Z *= ST_PRECISION;
+
+ reg_set_20602.GYRO_CONFIG.reg_u.REG.XG_ST = 0x1;
+ reg_set_20602.GYRO_CONFIG.reg_u.REG.YG_ST = 0x1;
+ reg_set_20602.GYRO_CONFIG.reg_u.REG.ZG_ST = 0x1;
+ icm20602_write_reg_simple(st, reg_set_20602.GYRO_CONFIG);
+
+ for (i = 0; i < SELFTEST_COUNT; i++) {
+ icm20602_read_raw(st, real_data, ACCEL);
+ gyro_st->X += real_data->GYRO_XOUT;
+ gyro_st->Y += real_data->GYRO_YOUT;
+ gyro_st->Z += real_data->GYRO_ZOUT;
+ usleep_range(1000, 1001);
+ }
+ gyro_st->X /= SELFTEST_COUNT;
+ gyro_st->X *= ST_PRECISION;
+
+ gyro_st->Y /= SELFTEST_COUNT;
+ gyro_st->Y *= ST_PRECISION;
+
+ gyro_st->Z /= SELFTEST_COUNT;
+ gyro_st->Z *= ST_PRECISION;
+
+ return MPU_SUCCESS;
+}
+
+static bool icm20602_check_acc_selftest(struct inv_icm20602_state *st,
+ struct X_Y_Z *acc, struct X_Y_Z *acc_st)
+{
+ struct X_Y_Z acc_ST_code, st_otp, st_shift_cust;
+ bool otp_value_zero = false, test_result = true;
+
+ acc_ST_code.X = st->config->acc_self_test.X;
+ acc_ST_code.Y = st->config->acc_self_test.Y;
+ acc_ST_code.Z = st->config->acc_self_test.Z;
+
+ st_otp.X = (st_otp.X != 0) ? mpu_st_tb[acc_ST_code.X - 1] : 0;
+ st_otp.Y = (st_otp.Y != 0) ? mpu_st_tb[acc_ST_code.Y - 1] : 0;
+ st_otp.Z = (st_otp.Z != 0) ? mpu_st_tb[acc_ST_code.Z - 1] : 0;
+
+ if (st_otp.X & st_otp.Y & st_otp.Z == 0)
+ otp_value_zero = true;
+
+ st_shift_cust.X = acc_st->X - acc->X;
+ st_shift_cust.Y = acc_st->X - acc->Y;
+ st_shift_cust.Z = acc_st->X - acc->Z;
+ if (!otp_value_zero) {
+ if (
+ st_shift_cust.X <
+ (st_otp.X * ST_PRECISION * ACC_ST_SHIFT_MIN / 100) ||
+ st_shift_cust.Y <
+ (st_otp.Y * ST_PRECISION * ACC_ST_SHIFT_MIN / 100) ||
+ st_shift_cust.Z <
+ (st_otp.Z * ST_PRECISION * ACC_ST_SHIFT_MIN / 100) ||
+
+ st_shift_cust.X >
+ (st_otp.X * ST_PRECISION * ACC_ST_SHIFT_MAX / 100) ||
+ st_shift_cust.Y >
+ (st_otp.Y * ST_PRECISION * ACC_ST_SHIFT_MAX / 100) ||
+ st_shift_cust.Z >
+ (st_otp.Z * ST_PRECISION * ACC_ST_SHIFT_MAX / 100)
+ ) {
+ test_result = false;
+ }
+ } else {
+ if (
+ abs(st_shift_cust.X) <
+ (ACC_ST_AL_MIN * 16384 / 1000 * ST_PRECISION) ||
+ abs(st_shift_cust.Y) <
+ (ACC_ST_AL_MIN * 16384 / 1000 * ST_PRECISION) ||
+ abs(st_shift_cust.Z) <
+ (ACC_ST_AL_MIN * 16384 / 1000 * ST_PRECISION) ||
+
+ abs(st_shift_cust.X) >
+ (ACC_ST_AL_MAX * 16384 / 1000 * ST_PRECISION) ||
+ abs(st_shift_cust.Y) >
+ (ACC_ST_AL_MAX * 16384 / 1000 * ST_PRECISION) ||
+ abs(st_shift_cust.Z) >
+ (ACC_ST_AL_MAX * 16384 / 1000 * ST_PRECISION)
+ ) {
+ test_result = false;
+ }
+ }
+
+ return test_result;
+}
+
+static int icm20602_check_gyro_selftest(struct inv_icm20602_state *st,
+ struct X_Y_Z *gyro, struct X_Y_Z *gyro_st)
+{
+ struct X_Y_Z gyro_ST_code, st_otp, st_shift_cust;
+ bool otp_value_zero = false, test_result = true;
+
+ gyro_ST_code.X = st->config->gyro_self_test.X;
+ gyro_ST_code.Y = st->config->gyro_self_test.Y;
+ gyro_ST_code.Z = st->config->gyro_self_test.Z;
+
+ st_otp.X = (st_otp.X != 0) ? mpu_st_tb[gyro_ST_code.X - 1] : 0;
+ st_otp.Y = (st_otp.Y != 0) ? mpu_st_tb[gyro_ST_code.Y - 1] : 0;
+ st_otp.Z = (st_otp.Z != 0) ? mpu_st_tb[gyro_ST_code.Z - 1] : 0;
+
+ if (st_otp.X & st_otp.Y & st_otp.Z == 0)
+ otp_value_zero = true;
+
+ st_shift_cust.X = gyro_st->X - gyro->X;
+ st_shift_cust.Y = gyro_st->X - gyro->Y;
+ st_shift_cust.Z = gyro_st->X - gyro->Z;
+ if (!otp_value_zero) {
+ if (
+ st_shift_cust.X <
+ (st_otp.X * ST_PRECISION * GYRO_ST_SHIFT / 100) ||
+ st_shift_cust.Y <
+ (st_otp.Y * ST_PRECISION * GYRO_ST_SHIFT / 100) ||
+ st_shift_cust.Z <
+ (st_otp.Z * ST_PRECISION * GYRO_ST_SHIFT / 100)
+ ) {
+ test_result = false;
+ }
+ } else {
+ if (
+ abs(st_shift_cust.X) <
+ (GYRO_ST_AL * 32768 / 250 * ST_PRECISION) ||
+ abs(st_shift_cust.Y) <
+ (GYRO_ST_AL * 32768 / 250 * ST_PRECISION) ||
+ abs(st_shift_cust.Z) <
+ (GYRO_ST_AL * 32768 / 250 * ST_PRECISION)
+ ) {
+ test_result = false;
+ }
+ }
+
+ if (test_result == true) {
+ /* Self Test Pass/Fail Criteria C */
+ if (
+ abs(st_shift_cust.X) >
+ GYRO_OFFSET_MAX * 32768 / 250 * ST_PRECISION ||
+ abs(st_shift_cust.Y) >
+ GYRO_OFFSET_MAX * 32768 / 250 * ST_PRECISION ||
+ abs(st_shift_cust.Z) >
+ GYRO_OFFSET_MAX * 32768 / 250 * ST_PRECISION
+ ) {
+ test_result = false;
+ }
+ }
+
+ return test_result;
+}
+
+bool icm20602_self_test(struct inv_icm20602_state *st)
+{
+ struct X_Y_Z acc, acc_st;
+ struct X_Y_Z gyro, gyro_st;
+ bool test_result = true;
+
+ icm20602_set_self_test(st);
+ icm20602_do_test_acc(st, &acc, &acc_st);
+ icm20602_do_test_gyro(st, &gyro, &gyro_st);
+ test_result = icm20602_check_acc_selftest(st, &acc, &acc_st);
+ test_result = icm20602_check_gyro_selftest(st, &gyro, &gyro_st);
+
+ return test_result;
+}
+
+static int icm20602_config_fifo(struct inv_icm20602_state *st)
+{
+ struct icm20602_user_config *config = NULL;
+
+ config = st->config;
+ if (config->fifo_enabled != true)
+ return MPU_SUCCESS;
+
+ /*
+ * Set CONFIG.USER_SET_BIT = 0, No reason as datasheet said
+ */
+ reg_set_20602.CONFIG.reg_u.REG.USER_SET_BIT = 0x0;
+ /*
+ * Set CONFIG.FIFO_MODE = 1,
+ * i.e. when FIFO is full, additional writes will
+ * not be written to FIFO
+ */
+ reg_set_20602.CONFIG.reg_u.REG.FIFO_MODE = 0x1;
+ if (icm20602_write_reg_simple(st, reg_set_20602.CONFIG))
+ return -MPU_FAIL;
+
+ /* reset fifo */
+ reg_set_20602.USER_CTRL.reg_u.REG.FIFO_RST = 0x1;
+ if (icm20602_write_reg_simple(st, reg_set_20602.USER_CTRL)) {
+ reg_set_20602.USER_CTRL.reg_u.REG.FIFO_RST = 0x0;
+ return -MPU_FAIL;
+ }
+ reg_set_20602.USER_CTRL.reg_u.REG.FIFO_RST = 0x0;
+
+ /* Enable FIFO on specified sensors */
+ reg_set_20602.FIFO_EN.reg_u.REG.GYRO_FIFO_EN = 0x1;
+ reg_set_20602.FIFO_EN.reg_u.REG.ACCEL_FIFO_EN = 0x1;
+ if (icm20602_write_reg_simple(st, reg_set_20602.FIFO_EN))
+ return -MPU_FAIL;
+
+ if (icm20602_config_waterlevel(st))
+ return -MPU_FAIL;
+
+ if (icm20602_start_fifo(st))
+ return -MPU_FAIL;
+
+ return MPU_SUCCESS;
+}
+
+static int icm20602_initialize_gyro(struct inv_icm20602_state *st)
+{
+ struct icm20602_user_config *config = NULL;
+ int result = MPU_SUCCESS;
+ int sample_rate;
+ uint8_t fchoice_b;
+
+ if (st == NULL)
+ return -MPU_FAIL;
+
+ /*
+ * ICM20602 supports gyro sampling rate up to 32KHz
+ * when fchoice_b != 0x00
+ * In our driver, we supports up to 8KHz
+ * thus always set fchoice_b to 0x00;
+ */
+ config = st->config;
+ /*
+ * SAPLRT_DIV in ICM20602_REG_SMPLRT_DIV is only used for 1kHz internal
+ * sampling, i.e. fchoice_b in ICM20602_REG_GYRO_CONFIG is 00
+ * and 0 < dlpf_cfg in ICM20602_REG_CONFIG < 7
+ * SAMPLE_RATE=Internal_Sample_Rate / (1 + SMPLRT_DIV)
+ */
+ if (config->gyro_accel_sample_rate <= ICM20602_SAMPLE_RATE_1000HZ)
+ reg_set_20602.SMPLRT_DIV.reg_u.reg =
+ ICM20602_INTERNAL_SAMPLE_RATE_HZ /
+ config->gyro_accel_sample_rate - 1;
+
+ result = icm20602_write_reg_simple(st, reg_set_20602.SMPLRT_DIV);
+
+ /* Set gyro&temperature(combine) LPF */
+ reg_set_20602.CONFIG.reg_u.REG.DLFP_CFG = config->gyro_lpf;
+ result |= icm20602_write_reg_simple(st, reg_set_20602.CONFIG);
+
+ /* Set gyro full scale range */
+ reg_set_20602.GYRO_CONFIG.reg_u.REG.FCHOICE_B = 0x0;
+ reg_set_20602.GYRO_CONFIG.reg_u.REG.FS_SEL = config->gyro_fsr;
+ result |= icm20602_write_reg_simple(st, reg_set_20602.GYRO_CONFIG);
+
+ /* Set Accel full scale range */
+ reg_set_20602.ACCEL_CONFIG.reg_u.REG.ACCEL_FS_SEL = config->acc_fsr;
+ result |= icm20602_write_reg_simple(st, reg_set_20602.ACCEL_CONFIG);
+
+ /*
+ * Set accel LPF
+ * Support accel sample rate up to 1KHz
+ * thus set accel_fchoice_b to 0x00
+ * The actual accel sample rate is 1KHz/(1+SMPLRT_DIV)
+ */
+ reg_set_20602.ACCEL_CONFIG2.reg_u.REG.ACCEL_FCHOICE_B = 0x0;
+ reg_set_20602.ACCEL_CONFIG2.reg_u.REG.A_DLPF_CFG = config->acc_lpf;
+ result |= icm20602_write_reg_simple(st,
+ reg_set_20602.ACCEL_CONFIG2);
+
+ if (result) {
+ pr_err("icm20602 init gyro and accel failed\n");
+ return -MPU_FAIL;
+ }
+
+ return result;
+}
+
+int icm20602_set_power_itg(struct inv_icm20602_state *st, bool power_on)
+{
+ int result = MPU_SUCCESS;
+
+ if (power_on) {
+ reg_set_20602.PWR_MGMT_1.reg_u.reg = 0;
+ result = icm20602_write_reg_simple(st,
+ reg_set_20602.PWR_MGMT_1);
+ } else {
+ reg_set_20602.PWR_MGMT_1.reg_u.REG.SLEEP = 0x1;
+ result = icm20602_write_reg_simple(st,
+ reg_set_20602.PWR_MGMT_1);
+ }
+ if (result) {
+ pr_err("set power failed power %d err %d\n",
+ power_on, result);
+ return result;
+ }
+
+ if (power_on)
+ msleep(30);
+
+ return result;
+}
+
+int icm20602_init_device(struct inv_icm20602_state *st)
+{
+ int result = MPU_SUCCESS;
+ struct icm20602_user_config *config = NULL;
+ int package_count;
+
+ config = st->config;
+ if (st == NULL || st->config == NULL) {
+ pr_err("icm20602 validate config failed\n");
+ return -MPU_FAIL;
+ }
+
+ /* turn on gyro and accel */
+ reg_set_20602.PWR_MGMT_2.reg_u.reg = 0x0;
+ result |= icm20602_write_reg_simple(st, reg_set_20602.PWR_MGMT_2);
+ msleep(30);
+
+ /* disable INT */
+ reg_set_20602.INT_ENABLE.reg_u.reg = 0x0;
+ result |= icm20602_write_reg_simple(st, reg_set_20602.INT_ENABLE);
+
+ /* disbale FIFO */
+ reg_set_20602.FIFO_EN.reg_u.reg = 0x0;
+ result |= icm20602_write_reg_simple(st, reg_set_20602.FIFO_EN);
+
+ /* reset FIFO */
+ reg_set_20602.USER_CTRL.reg_u.REG.FIFO_RST = 0x1;
+ result |= icm20602_write_reg_simple(st, reg_set_20602.USER_CTRL);
+ reg_set_20602.USER_CTRL.reg_u.REG.FIFO_RST = 0x0;
+ msleep(30);
+
+ /* init gyro and accel */
+ if (icm20602_initialize_gyro(st)) {
+ pr_err("icm20602 init device failed\n");
+ return -MPU_FAIL;
+ }
+
+ /* if FIFO enable, config FIFO */
+ if (config->fifo_enabled) {
+ if (icm20602_config_fifo(st)) {
+ pr_err("icm20602 init config fifo failed\n");
+ return -MPU_FAIL;
+ }
+ } else {
+ /* enable interrupt */
+ reg_set_20602.INT_ENABLE.reg_u.REG.DATA_RDY_INT_EN = 0x0;
+ if (icm20602_write_reg_simple(st,
+ reg_set_20602.INT_ENABLE)) {
+ pr_err("icm20602 set raw rdy failed\n");
+ return -MPU_FAIL;
+ }
+ }
+
+ /* buffer malloc */
+ package_count = config->fifo_waterlevel / ICM20602_PACKAGE_SIZE;
+
+ st->buf = kzalloc(sizeof(config->fifo_waterlevel * 2), GFP_ATOMIC);
+ if (!st->buf)
+ return -ENOMEM;
+
+ st->data_push = kcalloc(package_count,
+ sizeof(struct struct_icm20602_data), GFP_ATOMIC);
+ if (!st->data_push)
+ return -ENOMEM;
+
+ return result;
+}
+
+void icm20602_rw_test(struct inv_icm20602_state *st)
+{
+ uint8_t val = 0;
+
+ reg_set_20602.PWR_MGMT_2.reg_u.REG.STBY_ZG = 0x1;
+ icm20602_write_reg_simple(st, reg_set_20602.PWR_MGMT_2);
+ reg_set_20602.CONFIG.reg_u.REG.FIFO_MODE = 0x1;
+ icm20602_write_reg_simple(st, reg_set_20602.CONFIG);
+
+ icm20602_read_reg(st, reg_set_20602.PWR_MGMT_2.address, &val);
+ icm20602_read_reg(st, reg_set_20602.CONFIG.address, &val);
+
+}
+
+int icm20602_detect(struct inv_icm20602_state *st)
+{
+ int result = MPU_SUCCESS;
+ uint8_t retry = 0, val = 0;
+ uint8_t usr_ctrl = 0;
+
+ pr_debug("icm20602_detect\n");
+ /* reset to make sure previous state are not there */
+ reg_set_20602.PWR_MGMT_1.reg_u.REG.DEVICE_RESET = 0x1;
+ result = icm20602_write_reg_simple(st, reg_set_20602.PWR_MGMT_1);
+ if (result) {
+ pr_err("mpu write reg 0x%x value 0x%x failed\n",
+ reg_set_20602.PWR_MGMT_1.reg_u.reg,
+ reg_set_20602.PWR_MGMT_1.reg_u.REG.DEVICE_RESET);
+ return result;
+ }
+ reg_set_20602.PWR_MGMT_1.reg_u.REG.DEVICE_RESET = 0x0;
+
+ /* the power up delay */
+ msleep(30);
+
+ /* out of sleep */
+ result = icm20602_set_power_itg(st, true);
+ if (result)
+ return result;
+ /* get who am i register */
+ while (retry < 10) {
+ /* get version (expecting 0x12 for the icm20602) */
+ icm20602_read_reg(st, reg_set_20602.WHO_AM_I.address, &val);
+ if (val == ICM20602_WHO_AM_I)
+ break;
+ retry++;
+ }
+
+ if (val != ICM20602_WHO_AM_I) {
+ pr_err("detect mpu failed,whoami reg 0x%x\n", val);
+ result = -MPU_FAIL;
+ } else {
+ pr_debug("detect mpu ok,whoami reg 0x%x\n", val);
+ }
+ icm20602_rw_test(st);
+
+ return result;
+}
diff --git a/drivers/iio/imu/inv_icm20602/inv_icm20602_bsp.h b/drivers/iio/imu/inv_icm20602/inv_icm20602_bsp.h
new file mode 100644
index 0000000..9c4dbd0
--- /dev/null
+++ b/drivers/iio/imu/inv_icm20602/inv_icm20602_bsp.h
@@ -0,0 +1,978 @@
+/*
+ * Copyright (c) 2018 The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+/* register high bit=1 for read */
+#define ICM20602_READ_REG(reg) (reg | 0x80)
+
+/* register high bit=0 for write */
+#define ICM20602_WRITE_REG(reg) (reg & (~0x80))
+#define ICM20602_WHO_AM_I 0x12
+#define ICM20602_INTERNAL_SAMPLE_RATE_HZ 1000
+
+#define SELFTEST_COUNT 200
+#define ST_PRECISION 1000
+#define ACC_ST_SHIFT_MAX 150
+#define ACC_ST_SHIFT_MIN 50
+#define ACC_ST_AL_MIN 225
+#define ACC_ST_AL_MAX 675
+#define GYRO_ST_SHIFT 50
+#define GYRO_ST_AL 60
+#define GYRO_OFFSET_MAX 20
+
+static const uint16_t mpu_st_tb[256] = {
+ 2620, 2646, 2672, 2699, 2726, 2753, 2781, 2808,
+ 2837, 2865, 2894, 2923, 2952, 2981, 3011, 3041,
+ 3072, 3102, 3133, 3165, 3196, 3228, 3261, 3293,
+ 3326, 3359, 3393, 3427, 3461, 3496, 3531, 3566,
+ 3602, 3638, 3674, 3711, 3748, 3786, 3823, 3862,
+ 3900, 3939, 3979, 4019, 4059, 4099, 4140, 4182,
+ 4224, 4266, 4308, 4352, 4395, 4439, 4483, 4528,
+ 4574, 4619, 4665, 4712, 4759, 4807, 4855, 4903,
+ 4953, 5002, 5052, 5103, 5154, 5205, 5257, 5310,
+ 5363, 5417, 5471, 5525, 5581, 5636, 5693, 5750,
+ 5807, 5865, 5924, 5983, 6043, 6104, 6165, 6226,
+ 6289, 6351, 6415, 6479, 6544, 6609, 6675, 6742,
+ 6810, 6878, 6946, 7016, 7086, 7157, 7229, 7301,
+ 7374, 7448, 7522, 7597, 7673, 7750, 7828, 7906,
+ 7985, 8065, 8145, 8227, 8309, 8392, 8476, 8561,
+ 8647, 8733, 8820, 8909, 8998, 9088, 9178, 9270,
+ 9363, 9457, 9551, 9647, 9743, 9841, 9939, 10038,
+ 10139, 10240, 10343, 10446, 10550, 10656, 10763, 10870,
+ 10979, 11089, 11200, 11312, 11425, 11539, 11654, 11771,
+ 11889, 12008, 12128, 12249, 12371, 12495, 12620, 12746,
+ 12874, 13002, 13132, 13264, 13396, 13530, 13666, 13802,
+ 13940, 14080, 14221, 14363, 14506, 14652, 14798, 14946,
+ 15096, 15247, 15399, 15553, 15709, 15866, 16024, 16184,
+ 16346, 16510, 16675, 16842, 17010, 17180, 17352, 17526,
+ 17701, 17878, 18057, 18237, 18420, 18604, 18790, 18978,
+ 19167, 19359, 19553, 19748, 19946, 20145, 20347, 20550,
+ 20756, 20963, 21173, 21385, 21598, 21814, 22033, 22253,
+ 22475, 22700, 22927, 23156, 23388, 23622, 23858, 24097,
+ 24338, 24581, 24827, 25075, 25326, 25579, 25835, 26093,
+ 26354, 26618, 26884, 27153, 27424, 27699, 27976, 28255,
+ 28538, 28823, 29112, 29403, 29697, 29994, 30294, 30597,
+ 30903, 31212, 31524, 31839, 32157, 32479, 32804
+};
+
+enum inv_icm20602_reg_addr {
+ ADDR_XG_OFFS_TC_H = 0x04,
+ ADDR_XG_OFFS_TC_L,
+ ADDR_YG_OFFS_TC_H = 0x07,
+ ADDR_YG_OFFS_TC_L,
+ ADDR_ZG_OFFS_TC_H = 0x0A,
+ ADDR_ZG_OFFS_TC_L,
+
+ ADDR_SELF_TEST_X_ACCEL = 0x0D,
+ ADDR_SELF_TEST_Y_ACCEL,
+ ADDR_SELF_TEST_Z_ACCEL,
+
+ ADDR_XG_OFFS_USRH = 0x13,
+ ADDR_XG_OFFS_USRL,
+ ADDR_YG_OFFS_USRH,
+ ADDR_YG_OFFS_USRL,
+ ADDR_ZG_OFFS_USRH,
+ ADDR_ZG_OFFS_USRL,
+
+ ADDR_SMPLRT_DIV,
+ ADDR_CONFIG,
+
+ ADDR_GYRO_CONFIG,
+
+ ADDR_ACCEL_CONFIG,
+ ADDR_ACCEL_CONFIG2,
+
+ ADDR_LP_MODE_CFG,
+
+ ADDR_ACCEL_WOM_X_THR = 0x20,
+ ADDR_ACCEL_WOM_Y_THR,
+ ADDR_ACCEL_WOM_Z_THR,
+ ADDR_FIFO_EN,
+
+ ADDR_FSYNC_INT = 0x36,
+ ADDR_INT_PIN_CFG,
+ ADDR_INT_ENABLE,
+ ADDR_FIFO_WM_INT_STATUS,
+ ADDR_INT_STATUS,
+
+ ADDR_ACCEL_XOUT_H,
+ ADDR_ACCEL_XOUT_L,
+ ADDR_ACCEL_YOUT_H,
+ ADDR_ACCEL_YOUT_L,
+ ADDR_ACCEL_ZOUT_H,
+ ADDR_ACCEL_ZOUT_L,
+
+ ADDR_TEMP_OUT_H,
+ ADDR_TEMP_OUT_L,
+
+ ADDR_GYRO_XOUT_H,
+ ADDR_GYRO_XOUT_L,
+ ADDR_GYRO_YOUT_H,
+ ADDR_GYRO_YOUT_L,
+ ADDR_GYRO_ZOUT_H,
+ ADDR_GYRO_ZOUT_L,
+
+ ADDR_SELF_TEST_X_GYRO = 0x50,
+ ADDR_SELF_TEST_Y_GYRO,
+ ADDR_SELF_TEST_Z_GYRO,
+
+ ADDR_FIFO_WM_TH1 = 0x60,
+ ADDR_FIFO_WM_TH2,
+
+ ADDR_SIGNAL_PATH_RESET = 0x68,
+ ADDR_ACCEL_INTEL_CTRL,
+ ADDR_USER_CTRL,
+
+ ADDR_PWR_MGMT_1,
+ ADDR_PWR_MGMT_2,
+
+ ADDR_I2C_IF = 0x70,
+
+ ADDR_FIFO_COUNTH = 0x72,
+ ADDR_FIFO_COUNTL,
+ ADDR_FIFO_R_W,
+
+ ADDR_WHO_AM_I,
+
+ ADDR_XA_OFFSET_H,
+ ADDR_XA_OFFSET_L,
+ ADDR_YA_OFFSET_H = 0x7A,
+ ADDR_YA_OFFSET_L,
+ ADDR_ZA_OFFSET_H = 0x7D,
+ ADDR_ZA_OFFSET_L
+};
+
+struct struct_XG_OFFS_TC_H {
+ enum inv_icm20602_reg_addr address;
+ union {
+ struct {
+ u8 REG_XG_OFFS_TC_H :2;
+ u8 REG_XG_OFFS_LP :6;
+ } REG;
+ u8 reg;
+ } reg_u;
+};
+
+struct struct_XG_OFFS_TC_L {
+ enum inv_icm20602_reg_addr address;
+ union {
+ struct {
+ u8 REG_XG_OFFS_TC_L :8;
+ } REG;
+ u8 reg;
+ } reg_u;
+};
+
+struct struct_YG_OFFS_TC_H {
+ enum inv_icm20602_reg_addr address;
+ union {
+ struct {
+ u8 REG_YG_OFFS_TC_H :2;
+ u8 REG_YG_OFFS_LP :6;
+ } REG;
+ u8 reg;
+ } reg_u;
+};
+
+struct struct_YG_OFFS_TC_L {
+ enum inv_icm20602_reg_addr address;
+ union {
+ struct {
+ u8 REG_YG_OFFS_TC_L :8;
+ } REG;
+ u8 reg;
+ } reg_u;
+};
+
+struct struct_ZG_OFFS_TC_H {
+ enum inv_icm20602_reg_addr address;
+ union {
+ struct {
+ u8 REG_ZG_OFFS_TC_H :2;
+ u8 REG_ZG_OFFS_LP :6;
+ } REG;
+ u8 reg;
+ } reg_u;
+};
+
+struct struct_ZG_OFFS_TC_L {
+ enum inv_icm20602_reg_addr address;
+ union {
+ struct {
+ u8 REG_ZG_OFFS_TC_L :8;
+ } REG;
+ u8 reg;
+ } reg_u;
+};
+
+struct struct_SELF_TEST_X_ACCEL {
+ enum inv_icm20602_reg_addr address;
+ union {
+ struct {
+ u8 XA_ST_DATA :8;
+ } REG;
+ u8 reg;
+ } reg_u;
+};
+
+struct struct_SELF_TEST_Y_ACCEL {
+ enum inv_icm20602_reg_addr address;
+ union {
+ struct {
+ u8 YA_ST_DATA :8;
+ } REG;
+ u8 reg;
+ } reg_u;
+};
+
+struct struct_SELF_TEST_Z_ACCEL {
+ enum inv_icm20602_reg_addr address;
+ union {
+ struct {
+ u8 ZA_ST_DATA :8;
+ } REG;
+ u8 reg;
+ } reg_u;
+};
+
+struct struct_XG_OFFS_USRH {
+ enum inv_icm20602_reg_addr address;
+ union {
+ struct {
+ u8 X_OFFS_USR :8;
+ } REG;
+ u8 reg;
+ } reg_u;
+};
+
+struct struct_XG_OFFS_USRL {
+ enum inv_icm20602_reg_addr address;
+ union {
+ struct {
+ u8 X_OFFS_USR :8;
+ } REG;
+ u8 reg;
+ } reg_u;
+};
+
+struct struct_YG_OFFS_USRH {
+ enum inv_icm20602_reg_addr address;
+ union {
+ struct {
+ u8 Y_OFFS_USR :8;
+ } REG;
+ u8 reg;
+ } reg_u;
+};
+
+struct struct_YG_OFFS_USRL {
+ enum inv_icm20602_reg_addr address;
+ union {
+ struct {
+ u8 Y_OFFS_USR :8;
+ } REG;
+ u8 reg;
+ } reg_u;
+};
+
+struct struct_ZG_OFFS_USRH {
+ enum inv_icm20602_reg_addr address;
+ union {
+ struct {
+ u8 Z_OFFS_USR :8;
+ } REG;
+ u8 reg;
+ } reg_u;
+};
+
+struct struct_ZG_OFFS_USRL {
+ enum inv_icm20602_reg_addr address;
+ union {
+ struct {
+ u8 Z_OFFS_USR :8;
+ } REG;
+ u8 reg;
+ } reg_u;
+};
+
+struct struct_SMPLRT_DIV {
+ enum inv_icm20602_reg_addr address;
+ union {
+ struct {
+ u8 SMPLRT_DIV :8;
+ } REG;
+ u8 reg;
+ } reg_u;
+};
+
+struct struct_CONFIG {
+ enum inv_icm20602_reg_addr address;
+ union {
+ struct {
+ u8 DLFP_CFG :3;
+ u8 EXT_SYNC_SET :3;
+ u8 FIFO_MODE :1;
+ u8 USER_SET_BIT :1;
+ } REG;
+ u8 reg;
+ } reg_u;
+};
+
+struct struct_GYRO_CONFIG {
+ enum inv_icm20602_reg_addr address;
+ union {
+ struct {
+ u8 FCHOICE_B :2;
+ u8 RESERVE0 :1;
+ u8 FS_SEL :2;
+ u8 ZG_ST :1;
+ u8 YG_ST :1;
+ u8 XG_ST :1;
+ } REG;
+ u8 reg;
+ } reg_u;
+};
+
+struct struct_ACCEL_CONFIG {
+ enum inv_icm20602_reg_addr address;
+ union {
+ struct {
+ u8 RESERVE0 :3;
+ u8 ACCEL_FS_SEL :2;
+ u8 ZG_ST :1;
+ u8 YG_ST :1;
+ u8 XG_ST :1;
+ } REG;
+ u8 reg;
+ } reg_u;
+};
+
+struct struct_ACCEL_CONFIG2 {
+ enum inv_icm20602_reg_addr address;
+ union {
+ struct {
+ u8 A_DLPF_CFG :3;
+ u8 ACCEL_FCHOICE_B :1;
+ u8 DEC2_CFG :2;
+ u8 RESERVE0 :2;
+ } REG;
+ u8 reg;
+ } reg_u;
+};
+
+struct struct_LP_MODE_CFG {
+ enum inv_icm20602_reg_addr address;
+ union {
+ struct {
+ u8 RESERVE0 :4;
+ u8 G_AVGCFG :3;
+ u8 GYRO_CYCLE :1;
+ } REG;
+ u8 reg;
+ } reg_u;
+};
+
+struct struct_ACCEL_WOM_X_THR {
+ enum inv_icm20602_reg_addr address;
+ union {
+ struct {
+ u8 WOM_X_TH :8;
+ } REG;
+ u8 reg;
+ } reg_u;
+};
+
+
+struct struct_ACCEL_WOM_Y_THR {
+ enum inv_icm20602_reg_addr address;
+ union {
+ struct {
+ u8 WOM_Y_TH :8;
+ } REG;
+ u8 reg;
+ } reg_u;
+};
+
+struct struct_ACCEL_WOM_Z_THR {
+ enum inv_icm20602_reg_addr address;
+ union {
+ struct {
+ u8 WOM_Z_TH :8;
+ } REG;
+ u8 reg;
+ } reg_u;
+};
+
+struct struct_FIFO_EN {
+ enum inv_icm20602_reg_addr address;
+ union {
+ struct {
+ u8 RESERVE1 :3;
+ u8 ACCEL_FIFO_EN :1;
+ u8 GYRO_FIFO_EN :1;
+ u8 RESERVE0 :3;
+ } REG;
+ u8 reg;
+ } reg_u;
+};
+
+struct struct_FSYNC_INT {
+ enum inv_icm20602_reg_addr address;
+ union {
+ struct {
+ u8 RESERVE0 :7;
+ u8 FSYNC_INT :1;
+ } REG;
+ u8 reg;
+ } reg_u;
+};
+
+struct struct_INT_PIN_CFG {
+ enum inv_icm20602_reg_addr address;
+ union {
+ struct {
+ u8 RESERVE0 :2;
+ u8 FSYNC_INT_MODE_EN:1;
+ u8 FSYNC_INT_LEVEL :1;
+ u8 INT_RD_CLEAR :1;
+ u8 LATCH_INT_EN :1;
+ u8 INT_OPEN :1;
+ u8 INT_LEVEL :1;
+ } REG;
+ u8 reg;
+ } reg_u;
+};
+
+struct struct_INT_ENABLE {
+ enum inv_icm20602_reg_addr address;
+ union {
+ struct {
+ u8 DATA_RDY_INT_EN :1;
+ u8 RESERVE0 :1;
+ u8 GDRIVE_INT_EN :1;
+ u8 FSYNC_INT_EN :1;
+ u8 FIFO_OFLOW_EN :1;
+ u8 WOM_Z_INT_EN :1;
+ u8 WOM_Y_INT_EN :1;
+ u8 WOM_X_INT_EN :1;
+ } REG;
+ u8 reg;
+ } reg_u;
+};
+
+struct struct_FIFO_WM_INT_STATUS {
+ enum inv_icm20602_reg_addr address;
+ union {
+ struct {
+ u8 RESERVE1 :6;
+ u8 FIFO_WM_INT :1;
+ u8 RESERVE0 :1;
+ } REG;
+ u8 reg;
+ } reg_u;
+};
+
+struct struct_INT_STATUS {
+ enum inv_icm20602_reg_addr address;
+ union {
+ struct {
+ u8 DATA_RDY_INT :1;
+ u8 RESERVE1 :1;
+ u8 GDRIVE_INT :1;
+ u8 RESERVE0 :1;
+ u8 FIFO_OFLOW_INT :1;
+ u8 WOM_Z_INT :1;
+ u8 WOM_Y_INT :1;
+ u8 WOM_X_INT :1;
+ } REG;
+ u8 reg;
+ } reg_u;
+};
+
+struct struct_ACCEL_XOUT_H {
+ enum inv_icm20602_reg_addr address;
+ union {
+ struct {
+ u8 ACCEL_XOUT :8;
+ } REG;
+ u8 reg;
+ } reg_u;
+};
+
+struct struct_ACCEL_XOUT_L {
+ enum inv_icm20602_reg_addr address;
+ union {
+ struct {
+ u8 ACCEL_XOUT :8;
+ } REG;
+ u8 reg;
+ } reg_u;
+};
+
+struct struct_ACCEL_YOUT_H {
+ enum inv_icm20602_reg_addr address;
+ union {
+ struct {
+ u8 ACCEL_YOUT :8;
+ } REG;
+ u8 reg;
+ } reg_u;
+};
+
+struct struct_ACCEL_YOUT_L {
+ enum inv_icm20602_reg_addr address;
+ union {
+ struct {
+ u8 ACCEL_YOUT :8;
+ } REG;
+ u8 reg;
+ } reg_u;
+};
+
+struct struct_ACCEL_ZOUT_H {
+ enum inv_icm20602_reg_addr address;
+ union {
+ struct {
+ u8 ACCEL_ZOUT :8;
+ } REG;
+ u8 reg;
+ } reg_u;
+};
+
+struct struct_ACCEL_ZOUT_L {
+ enum inv_icm20602_reg_addr address;
+ union {
+ struct {
+ u8 ACCEL_ZOUT :8;
+ } REG;
+ u8 reg;
+ } reg_u;
+};
+
+struct struct_TEMP_OUT_H {
+ enum inv_icm20602_reg_addr address;
+ union {
+ struct {
+ u8 TEMP_OUT :8;
+ } REG;
+ u8 reg;
+ } reg_u;
+};
+
+struct struct_TEMP_OUT_L {
+ enum inv_icm20602_reg_addr address;
+ union {
+ struct {
+ u8 TEMP_OUT :8;
+ } REG;
+ u8 reg;
+ } reg_u;
+};
+
+struct struct_GYRO_XOUT_H {
+ enum inv_icm20602_reg_addr address;
+ union {
+ struct {
+ u8 GYRO_XOUT :8;
+ } REG;
+ u8 reg;
+ } reg_u;
+};
+
+struct struct_GYRO_XOUT_L {
+ enum inv_icm20602_reg_addr address;
+ union {
+ struct {
+ u8 GYRO_XOUT :8;
+ } REG;
+ u8 reg;
+ } reg_u;
+};
+
+struct struct_GYRO_YOUT_H {
+ enum inv_icm20602_reg_addr address;
+ union {
+ struct {
+ u8 GYRO_YOUT :8;
+ } REG;
+ u8 reg;
+ } reg_u;
+};
+
+struct struct_GYRO_YOUT_L {
+ enum inv_icm20602_reg_addr address;
+ union {
+ struct {
+ u8 GYRO_YOUT :8;
+ } REG;
+ u8 reg;
+ } reg_u;
+};
+
+struct struct_GYRO_ZOUT_H {
+ enum inv_icm20602_reg_addr address;
+ union {
+ struct {
+ u8 GYRO_ZOUT :8;
+ } REG;
+ u8 reg;
+ } reg_u;
+};
+
+struct struct_GYRO_ZOUT_L {
+ enum inv_icm20602_reg_addr address;
+ union {
+ struct {
+ u8 GYRO_ZOUT :8;
+ } REG;
+ u8 reg;
+ } reg_u;
+};
+
+struct struct_SELF_TEST_X_GYRO {
+ enum inv_icm20602_reg_addr address;
+ union {
+ struct {
+ u8 XG_ST_DATA :8;
+ } REG;
+ u8 reg;
+ } reg_u;
+};
+
+struct struct_SELF_TEST_Y_GYRO {
+ enum inv_icm20602_reg_addr address;
+ union {
+ struct {
+ u8 YG_ST_DATA :8;
+ } REG;
+ u8 reg;
+ } reg_u;
+};
+
+struct struct_SELF_TEST_Z_GYRO {
+ enum inv_icm20602_reg_addr address;
+ union {
+ struct {
+ u8 ZG_ST_DATA :8;
+ } REG;
+ u8 reg;
+ } reg_u;
+};
+
+struct struct_FIFO_WM_TH1 {
+ enum inv_icm20602_reg_addr address;
+ union {
+ struct {
+ u8 RESERVE0 :6;
+ u8 FIFO_WM_TH :2;
+ } REG;
+ u8 reg;
+ } reg_u;
+};
+
+struct struct_FIFO_WM_TH2 {
+ enum inv_icm20602_reg_addr address;
+ union {
+ struct {
+ u8 FIFO_WM_TH :8;
+ } REG;
+ u8 reg;
+ } reg_u;
+};
+
+struct struct_SIGNAL_PATH_RESET {
+ enum inv_icm20602_reg_addr address;
+ union {
+ struct {
+ u8 TEMP_RST :1;
+ u8 ACCEL_RST :1;
+ u8 FIFO_WM_TH :6;
+ } REG;
+ u8 reg;
+ } reg_u;
+};
+
+struct struct_ACCEL_INTEL_CTRL {
+ enum inv_icm20602_reg_addr address;
+ union {
+ struct {
+ u8 WOM_TH_MODE :1;
+ u8 OUTPUT_LIMIT :1;
+ u8 RESERVE0 :4;
+ u8 ACCEL_INTEL_MODE :1;
+ u8 ACCEL_INTEL_EN :1;
+ } REG;
+ u8 reg;
+ } reg_u;
+};
+
+struct struct_USER_CTRL {
+ enum inv_icm20602_reg_addr address;
+ union {
+ struct {
+ u8 SIG_COND_RST :1;
+ u8 RESERVE2 :1;
+ u8 FIFO_RST :1;
+ u8 RESERVE1 :3;
+ u8 FIFO_EN :1;
+ u8 RESERVE0 :1;
+ } REG;
+ u8 reg;
+ } reg_u;
+};
+
+struct struct_PWR_MGMT_1 {
+ enum inv_icm20602_reg_addr address;
+ union {
+ struct {
+ u8 CLKSEL :3;
+ u8 TEMP_DIS :1;
+ u8 GYRO_STANDBY :1;
+ u8 CYCLE :1;
+ u8 SLEEP :1;
+ u8 DEVICE_RESET :1;
+ } REG;
+ u8 reg;
+ } reg_u;
+};
+
+struct struct_PWR_MGMT_2 {
+ enum inv_icm20602_reg_addr address;
+ union {
+ struct {
+ u8 STBY_ZG :1;
+ u8 STBY_YG :1;
+ u8 STBY_XG :1;
+ u8 STBY_ZA :1;
+ u8 STBY_YA :1;
+ u8 STBY_XA :1;
+ u8 RESERVE0 :2;
+ } REG;
+ u8 reg;
+ } reg_u;
+};
+
+struct struct_I2C_IF {
+ enum inv_icm20602_reg_addr address;
+ union {
+ struct {
+ u8 RESERVE1 :6;
+ u8 I2C_IF_DIS :1;
+ u8 RESERVE0 :1;
+ } REG;
+ u8 reg;
+ } reg_u;
+};
+
+struct struct_FIFO_COUNTH {
+ enum inv_icm20602_reg_addr address;
+ union {
+ struct {
+ u8 FIFO_COUNT :8;
+ } REG;
+ u8 reg;
+ } reg_u;
+};
+
+struct struct_FIFO_COUNTL {
+ enum inv_icm20602_reg_addr address;
+ union {
+ struct {
+ u8 FIFO_COUNT :8;
+ } REG;
+ u8 reg;
+ } reg_u;
+};
+
+struct struct_FIFO_R_W {
+ enum inv_icm20602_reg_addr address;
+ union {
+ struct {
+ u8 FIFO_DATA :8;
+ } REG;
+ u8 reg;
+ } reg_u;
+};
+
+struct struct_WHO_AM_I {
+ enum inv_icm20602_reg_addr address;
+ union {
+ struct {
+ u8 WHOAMI :8;
+ } REG;
+ u8 reg;
+ } reg_u;
+};
+
+struct struct_XA_OFFSET_H {
+ enum inv_icm20602_reg_addr address;
+ union {
+ struct {
+ u8 XA_OFFS :8;
+ } REG;
+ u8 reg;
+ } reg_u;
+};
+
+struct struct_XA_OFFSET_L {
+ enum inv_icm20602_reg_addr address;
+ union {
+ struct {
+ u8 XA_OFFS :8;
+ } REG;
+ u8 reg;
+ } reg_u;
+};
+
+struct struct_YA_OFFSET_H {
+ enum inv_icm20602_reg_addr address;
+ union {
+ struct {
+ u8 YA_OFFS :8;
+ } REG;
+ u8 reg;
+ } reg_u;
+};
+
+struct struct_YA_OFFSET_L {
+ enum inv_icm20602_reg_addr address;
+ union {
+ struct {
+ u8 YA_OFFS :8;
+ } REG;
+ u8 reg;
+ } reg_u;
+};
+
+struct struct_ZA_OFFSET_H {
+ enum inv_icm20602_reg_addr address;
+ union {
+ struct {
+ u8 ZA_OFFS :8;
+ } REG;
+ u8 reg;
+ } reg_u;
+};
+
+struct struct_ZA_OFFSET_L {
+ enum inv_icm20602_reg_addr address;
+ union {
+ struct {
+ u8 ZA_OFFS :8;
+ } REG;
+ u8 reg;
+ } reg_u;
+};
+
+/*
+ * struct inv_icm20602_reg_map - Notable registers.
+ * @sample_rate_div: Divider applied to gyro output rate.
+ * @lpf: Configures internal low pass filter.
+ * @user_ctrl: Enables/resets the FIFO.
+ * @fifo_en: Determines which data will appear in FIFO.
+ * @gyro_config: gyro config register.
+ * @accl_config: accel config register
+ * @fifo_count_h: Upper byte of FIFO count.
+ * @fifo_r_w: FIFO register.
+ * @raw_gyro: Address of first gyro register.
+ * @raw_accl: Address of first accel register.
+ * @temperature: temperature register
+ * @int_enable: Interrupt enable register.
+ * @pwr_mgmt_1: Controls chip's power state and clock source.
+ * @pwr_mgmt_2: Controls power state of individual sensors.
+ */
+struct inv_icm20602_reg_map {
+ struct struct_XG_OFFS_TC_H XG_OFFS_TC_H;
+ struct struct_XG_OFFS_TC_L XG_OFFS_TC_L;
+ struct struct_YG_OFFS_TC_H YG_OFFS_TC_H;
+ struct struct_YG_OFFS_TC_L YG_OFFS_TC_L;
+ struct struct_ZG_OFFS_TC_H ZG_OFFS_TC_H;
+ struct struct_ZG_OFFS_TC_L ZG_OFFS_TC_L;
+
+ struct struct_SELF_TEST_X_ACCEL SELF_TEST_X_ACCEL;
+ struct struct_SELF_TEST_Y_ACCEL SELF_TEST_Y_ACCEL;
+ struct struct_SELF_TEST_Z_ACCEL SELF_TEST_Z_ACCEL;
+
+ struct struct_XG_OFFS_USRH XG_OFFS_USRH;
+ struct struct_XG_OFFS_USRL XG_OFFS_USRL;
+ struct struct_YG_OFFS_USRH YG_OFFS_USRH;
+ struct struct_YG_OFFS_USRL YG_OFFS_USRL;
+ struct struct_ZG_OFFS_USRH ZG_OFFS_USRH;
+ struct struct_ZG_OFFS_USRL ZG_OFFS_USRL;
+
+ struct struct_SMPLRT_DIV SMPLRT_DIV;
+ struct struct_CONFIG CONFIG;
+
+ struct struct_GYRO_CONFIG GYRO_CONFIG;
+
+ struct struct_ACCEL_CONFIG ACCEL_CONFIG;
+ struct struct_ACCEL_CONFIG2 ACCEL_CONFIG2;
+
+ struct struct_LP_MODE_CFG LP_MODE_CFG;
+
+ struct struct_ACCEL_WOM_X_THR ACCEL_WOM_X_THR;
+ struct struct_ACCEL_WOM_Y_THR ACCEL_WOM_Y_THR;
+ struct struct_ACCEL_WOM_Z_THR ACCEL_WOM_Z_THR;
+
+ struct struct_FIFO_EN FIFO_EN;
+ struct struct_FSYNC_INT FSYNC_INT;
+ struct struct_INT_PIN_CFG INT_PIN_CFG;
+ struct struct_INT_ENABLE INT_ENABLE;
+ struct struct_FIFO_WM_INT_STATUS FIFO_WM_INT_STATUS;
+ struct struct_INT_STATUS INT_STATUS;
+
+ struct struct_ACCEL_XOUT_H ACCEL_XOUT_H;
+ struct struct_ACCEL_XOUT_L ACCEL_XOUT_L;
+ struct struct_ACCEL_YOUT_H ACCEL_YOUT_H;
+ struct struct_ACCEL_YOUT_L ACCEL_YOUT_L;
+ struct struct_ACCEL_ZOUT_H ACCEL_ZOUT_H;
+ struct struct_ACCEL_ZOUT_L ACCEL_ZOUT_L;
+
+ struct struct_TEMP_OUT_H TEMP_OUT_H;
+ struct struct_TEMP_OUT_L TEMP_OUT_L;
+
+ struct struct_GYRO_XOUT_H GYRO_XOUT_H;
+ struct struct_GYRO_XOUT_L GYRO_XOUT_L;
+ struct struct_GYRO_YOUT_H GYRO_YOUT_H;
+ struct struct_GYRO_YOUT_L GYRO_YOUT_L;
+ struct struct_GYRO_ZOUT_H GYRO_ZOUT_H;
+ struct struct_GYRO_ZOUT_L GYRO_ZOUT_L;
+
+ struct struct_SELF_TEST_X_GYRO SELF_TEST_X_GYRO;
+ struct struct_SELF_TEST_Y_GYRO SELF_TEST_Y_GYRO;
+ struct struct_SELF_TEST_Z_GYRO SELF_TEST_Z_GYRO;
+ struct struct_FIFO_WM_TH1 FIFO_WM_TH1;
+ struct struct_FIFO_WM_TH2 FIFO_WM_TH2;
+ struct struct_SIGNAL_PATH_RESET SIGNAL_PATH_RESET;
+ struct struct_ACCEL_INTEL_CTRL ACCEL_INTEL_CTRL;
+ struct struct_USER_CTRL USER_CTRL;
+
+ struct struct_PWR_MGMT_1 PWR_MGMT_1;
+ struct struct_PWR_MGMT_2 PWR_MGMT_2;
+
+ struct struct_I2C_IF I2C_IF;
+
+ struct struct_FIFO_COUNTH FIFO_COUNTH;
+ struct struct_FIFO_COUNTL FIFO_COUNTL;
+ struct struct_FIFO_R_W FIFO_R_W;
+
+ struct struct_WHO_AM_I WHO_AM_I;
+
+ struct struct_XA_OFFSET_H XA_OFFSET_H;
+ struct struct_XA_OFFSET_L XA_OFFSET_L;
+ struct struct_YA_OFFSET_H YA_OFFSET_H;
+ struct struct_YA_OFFSET_L YA_OFFSET_L;
+ struct struct_ZA_OFFSET_H ZA_OFFSET_H;
+ struct struct_ZA_OFFSET_L ZA_OFFSET_L;
+};
+
+
diff --git a/drivers/iio/imu/inv_icm20602/inv_icm20602_core.c b/drivers/iio/imu/inv_icm20602/inv_icm20602_core.c
new file mode 100644
index 0000000..0f7fc92
--- /dev/null
+++ b/drivers/iio/imu/inv_icm20602/inv_icm20602_core.c
@@ -0,0 +1,547 @@
+/*
+ * Copyright (c) 2018, The Linux Foundation. All rights reserved.
+ *
+ * Copyright (C) 2012 Invensense, Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/err.h>
+#include <linux/delay.h>
+#include <linux/sysfs.h>
+#include <linux/jiffies.h>
+#include <linux/irq.h>
+#include <linux/interrupt.h>
+#include <linux/kfifo.h>
+#include <linux/spinlock.h>
+#include <linux/spi/spi.h>
+#include <linux/i2c.h>
+#include <linux/of.h>
+#include <linux/of_gpio.h>
+#include "inv_icm20602_iio.h"
+
+/* Attribute of icm20602 device init show */
+static ssize_t inv_icm20602_init_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ return 0;
+}
+
+static void inv_icm20602_def_config(struct inv_icm20602_state *st)
+{
+ struct icm20602_user_config *config = st->config;
+
+ config->user_fps_in_ms = 10;
+ config->gyro_lpf = INV_ICM20602_GYRO_LFP_92HZ;
+ config->gyro_fsr = ICM20602_GYRO_FSR_1000DPS;
+ config->acc_lpf = ICM20602_ACCLFP_99;
+ config->acc_fsr = ICM20602_ACC_FSR_4G;
+ config->gyro_accel_sample_rate = ICM20602_SAMPLE_RATE_100HZ;
+ config->fifo_enabled = true;
+
+}
+
+static void inv_icm20602_load_config(struct inv_icm20602_state *st)
+{
+ struct icm20602_user_config *config = st->config;
+
+ if (config->user_fps_in_ms == 0)
+ inv_icm20602_def_config(st);
+ config->fifo_enabled = true;
+
+}
+
+static ssize_t inv_icm20602_init_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ int result = MPU_SUCCESS;
+ struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+ struct inv_icm20602_state *st = iio_priv(indio_dev);
+
+ inv_icm20602_load_config(st);
+ result |= icm20602_detect(st);
+ result |= icm20602_init_device(st);
+ icm20602_start_fifo(st);
+ if (result)
+ return result;
+
+ return count;
+}
+
+static IIO_DEVICE_ATTR(
+ inv_icm20602_init,
+ 0644,
+ inv_icm20602_init_show,
+ inv_icm20602_init_store,
+ 0);
+
+/* Attribute of gyro lpf base on the enum inv_icm20602_gyro_temp_lpf_e */
+static ssize_t inv_gyro_lpf_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ return 0;
+}
+
+static ssize_t inv_gyro_lpf_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+ struct inv_icm20602_state *st = iio_priv(indio_dev);
+ struct icm20602_user_config *config = st->config;
+ int gyro_lpf;
+
+ if (kstrtoint(buf, 10, &gyro_lpf))
+ return -EINVAL;
+ if (gyro_lpf > INV_ICM20602_GYRO_LFP_NUM)
+ return -EINVAL;
+ config->gyro_lpf = gyro_lpf;
+ return count;
+}
+static IIO_DEVICE_ATTR(
+ inv_icm20602_gyro_lpf,
+ 0644,
+ inv_gyro_lpf_show,
+ inv_gyro_lpf_store,
+ 0);
+
+/* Attribute of gyro fsr base on enum inv_icm20602_gyro_temp_lpf_e */
+static ssize_t inv_gyro_fsr_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ return 0;
+}
+
+static ssize_t inv_gyro_fsr_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+ struct inv_icm20602_state *st = iio_priv(indio_dev);
+ struct icm20602_user_config *config = st->config;
+ int gyro_fsr;
+
+ if (kstrtoint(buf, 10, &gyro_fsr))
+ return -EINVAL;
+ if (gyro_fsr > ICM20602_GYRO_FSR_NUM)
+ return -EINVAL;
+
+ config->gyro_fsr = gyro_fsr;
+ return count;
+}
+
+static IIO_DEVICE_ATTR(
+ inv_icm20602_gyro_fsr,
+ 0644,
+ inv_gyro_fsr_show,
+ inv_gyro_fsr_store,
+ 0);
+
+/* Attribute of gyro_self_test */
+static ssize_t inv_gyro_self_test_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ return 0;
+}
+
+static ssize_t inv_gyro_self_test_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ return 0;
+}
+static IIO_DEVICE_ATTR(
+ inv_icm20602_gyro_self_test,
+ 0644,
+ inv_gyro_self_test_show,
+ inv_gyro_self_test_store,
+ 0);
+
+/* Attribute of gyro fsr base on enum inv_icm20602_acc_fsr_e */
+static ssize_t inv_gyro_acc_fsr_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ return 0;
+}
+
+static ssize_t inv_gyro_acc_fsr_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+ struct inv_icm20602_state *st = iio_priv(indio_dev);
+ struct icm20602_user_config *config = st->config;
+ int acc_fsr;
+
+ if (kstrtoint(buf, 10, &acc_fsr))
+ return -EINVAL;
+ if (acc_fsr > ICM20602_ACC_FSR_NUM)
+ return -EINVAL;
+
+ config->acc_fsr = acc_fsr;
+ return count;
+}
+static IIO_DEVICE_ATTR(
+ inv_icm20602_acc_fsr,
+ 0644,
+ inv_gyro_acc_fsr_show,
+ inv_gyro_acc_fsr_store,
+ 0);
+
+/* Attribute of gyro fsr base on enum inv_icm20602_acc_lpf_e */
+static ssize_t inv_gyro_acc_lpf_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ return 0;
+}
+
+static ssize_t inv_gyro_acc_lpf_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+ struct inv_icm20602_state *st = iio_priv(indio_dev);
+ struct icm20602_user_config *config = st->config;
+ int acc_lpf;
+
+ if (kstrtoint(buf, 10, &acc_lpf))
+ return -EINVAL;
+ if (acc_lpf > ICM20602_ACCLPF_NUM)
+ return -EINVAL;
+
+ config->acc_fsr = acc_lpf;
+ return count;
+}
+static IIO_DEVICE_ATTR(
+ inv_icm20602_acc_lpf,
+ 0644,
+ inv_gyro_acc_lpf_show,
+ inv_gyro_acc_lpf_store,
+ 0);
+
+/* Attribute of acc_self_test */
+static ssize_t inv_acc_self_test_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ return 0;
+}
+
+static ssize_t inv_acc_self_test_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ return 0;
+}
+static IIO_DEVICE_ATTR(
+ inv_icm20602_acc_self_test,
+ 0644,
+ inv_acc_self_test_show,
+ inv_acc_self_test_store,
+ 0);
+
+/* Attribute of user_fps_in_ms */
+static ssize_t inv_user_fps_in_ms_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ return 0;
+}
+
+static ssize_t inv_user_fps_in_ms_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+ struct inv_icm20602_state *st = iio_priv(indio_dev);
+ struct icm20602_user_config *config = st->config;
+ int user_fps_in_ms;
+
+ if (kstrtoint(buf, 10, &user_fps_in_ms))
+ return -EINVAL;
+ if (user_fps_in_ms < 10)
+ return -EINVAL;
+
+ config->user_fps_in_ms = user_fps_in_ms;
+ return count;
+}
+static IIO_DEVICE_ATTR(
+ inv_user_fps_in_ms,
+ 0644,
+ inv_user_fps_in_ms_show,
+ inv_user_fps_in_ms_store,
+ 0);
+
+/* Attribute of gyro_accel_sample_rate */
+static ssize_t inv_sampling_frequency_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ return 0;
+}
+
+static ssize_t inv_sampling_frequency_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ return 0;
+}
+static IIO_DEV_ATTR_SAMP_FREQ(
+ 0644,
+ inv_sampling_frequency_show,
+ inv_sampling_frequency_store);
+
+static struct attribute *inv_icm20602_attributes[] = {
+ &iio_dev_attr_inv_icm20602_init.dev_attr.attr,
+
+ &iio_dev_attr_inv_icm20602_gyro_self_test.dev_attr.attr,
+ &iio_dev_attr_inv_icm20602_gyro_fsr.dev_attr.attr,
+ &iio_dev_attr_inv_icm20602_gyro_lpf.dev_attr.attr,
+
+ &iio_dev_attr_inv_icm20602_acc_self_test.dev_attr.attr,
+ &iio_dev_attr_inv_icm20602_acc_fsr.dev_attr.attr,
+ &iio_dev_attr_inv_icm20602_acc_lpf.dev_attr.attr,
+
+ &iio_dev_attr_sampling_frequency.dev_attr.attr,
+
+ &iio_dev_attr_inv_user_fps_in_ms.dev_attr.attr,
+ NULL,
+};
+
+#define INV_ICM20602_CHAN(_type, _channel2, _index) \
+{ \
+ .type = _type, \
+ .modified = 1, \
+ .channel2 = _channel2, \
+ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
+ .scan_index = _index, \
+ .scan_type = { \
+ .sign = 's', \
+ .realbits = 16, \
+ .storagebits = 16, \
+ .shift = 0, \
+ .endianness = IIO_BE, \
+ }, \
+}
+
+static const struct iio_chan_spec inv_icm20602_channels[] = {
+ IIO_CHAN_SOFT_TIMESTAMP(INV_ICM20602_SCAN_TIMESTAMP),
+
+ {
+ .type = IIO_TEMP,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW)
+ | BIT(IIO_CHAN_INFO_OFFSET)
+ | BIT(IIO_CHAN_INFO_SCALE),
+ .scan_index = INV_ICM20602_SCAN_TEMP,
+ .channel2 = IIO_MOD_X,
+ .scan_type = {
+ .sign = 's',
+ .realbits = 16,
+ .storagebits = 16,
+ .shift = 0,
+ .endianness = IIO_BE,
+ },
+ },
+ INV_ICM20602_CHAN(IIO_ANGL_VEL, IIO_MOD_X, INV_ICM20602_SCAN_GYRO_X),
+ INV_ICM20602_CHAN(IIO_ANGL_VEL, IIO_MOD_Y, INV_ICM20602_SCAN_GYRO_Y),
+ INV_ICM20602_CHAN(IIO_ANGL_VEL, IIO_MOD_Z, INV_ICM20602_SCAN_GYRO_Z),
+
+ INV_ICM20602_CHAN(IIO_ACCEL, IIO_MOD_X, INV_ICM20602_SCAN_ACCL_X),
+ INV_ICM20602_CHAN(IIO_ACCEL, IIO_MOD_Y, INV_ICM20602_SCAN_ACCL_Y),
+ INV_ICM20602_CHAN(IIO_ACCEL, IIO_MOD_Z, INV_ICM20602_SCAN_ACCL_Z),
+};
+
+static const struct attribute_group inv_icm20602_attribute_group = {
+ .attrs = inv_icm20602_attributes,
+};
+
+static const struct iio_info icm20602_info = {
+ .driver_module = THIS_MODULE,
+ .read_raw = NULL,
+ .write_raw = NULL,
+ .attrs = &inv_icm20602_attribute_group,
+ .validate_trigger = inv_icm20602_validate_trigger,
+};
+
+static int of_populate_icm20602_dt(struct inv_icm20602_state *st)
+{
+ int result = MPU_SUCCESS;
+
+ /* use client device irq */
+ st->gpio = of_get_named_gpio(st->client->dev.of_node,
+ "invensense,icm20602-gpio", 0);
+ result = gpio_is_valid(st->gpio);
+ if (!result) {
+ pr_err("gpio_is_valid %d failed\n", st->gpio);
+ return -MPU_FAIL;
+ }
+
+ result = gpio_request(st->gpio, "icm20602-irq");
+ if (result) {
+ pr_err("gpio_request failed\n");
+ return -MPU_FAIL;
+ }
+
+ result = gpio_direction_input(st->gpio);
+ if (result) {
+ pr_err("gpio_direction_input failed\n");
+ return -MPU_FAIL;
+ }
+
+ st->client->irq = gpio_to_irq(st->gpio);
+ if (st->client->irq < 0) {
+ pr_err("gpio_to_irq failed\n");
+ return -MPU_FAIL;
+ }
+
+ return MPU_SUCCESS;
+}
+
+/*
+ * inv_icm20602_probe() - probe function.
+ * @client: i2c client.
+ * @id: i2c device id.
+ *
+ * Returns 0 on success, a negative error code otherwise.
+ * The I2C address of the ICM-20602 is 0x68 or 0x69
+ * depending upon the value driven on AD0 pin.
+ */
+static int inv_icm20602_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct inv_icm20602_state *st;
+ struct iio_dev *indio_dev;
+ int result = MPU_SUCCESS;
+
+ indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*st));
+ if (indio_dev == NULL) {
+ result = -ENOMEM;
+ goto out_remove_trigger;
+ }
+ st = iio_priv(indio_dev);
+ st->client = client;
+ st->interface = ICM20602_I2C;
+
+ pr_debug("i2c address is %x\n", client->addr);
+ result = of_populate_icm20602_dt(st);
+ if (result)
+ return result;
+
+ st->config = kzalloc(sizeof(struct icm20602_user_config), GFP_ATOMIC);
+ if (st->config == NULL)
+ return -ENOMEM;
+
+ icm20602_init_reg_map();
+
+ i2c_set_clientdata(client, indio_dev);
+
+ dev_set_drvdata(&client->dev, indio_dev);
+ indio_dev->dev.parent = &client->dev;
+ indio_dev->name = ICM20602_DEV_NAME;
+ indio_dev->channels = inv_icm20602_channels;
+ indio_dev->num_channels = ARRAY_SIZE(inv_icm20602_channels);
+
+ indio_dev->info = &icm20602_info;
+ indio_dev->modes = INDIO_BUFFER_TRIGGERED;
+ result = iio_triggered_buffer_setup(indio_dev,
+ inv_icm20602_irq_handler, inv_icm20602_read_fifo_fn, NULL);
+ if (result) {
+ dev_err(&st->client->dev, " configure buffer fail %d\n",
+ result);
+ goto out_remove_trigger;
+ }
+
+ result = inv_icm20602_probe_trigger(indio_dev);
+ if (result) {
+ dev_err(&st->client->dev, "trigger probe fail %d\n", result);
+ goto out_unreg_ring;
+ }
+
+ INIT_KFIFO(st->timestamps);
+ spin_lock_init(&st->time_stamp_lock);
+ result = iio_device_register(indio_dev);
+ if (result) {
+ dev_err(&st->client->dev, "IIO register fail %d\n", result);
+ goto out_remove_trigger;
+ }
+
+ return 0;
+
+out_remove_trigger:
+ inv_icm20602_remove_trigger(st);
+out_unreg_ring:
+ iio_triggered_buffer_cleanup(indio_dev);
+out_free:
+ iio_device_free(indio_dev);
+ gpio_free(st->gpio);
+
+ return 0;
+}
+
+static int inv_icm20602_remove(struct i2c_client *client)
+{
+ struct iio_dev *indio_dev = i2c_get_clientdata(client);
+ struct inv_icm20602_state *st = iio_priv(indio_dev);
+
+ gpio_free(st->gpio);
+ iio_device_unregister(indio_dev);
+ inv_icm20602_remove_trigger(st);
+ iio_triggered_buffer_cleanup(indio_dev);
+ iio_device_free(indio_dev);
+
+ return 0;
+}
+
+static int inv_icm20602_suspend(struct device *dev)
+{
+ return 0;
+}
+
+static int inv_icm20602_resume(struct device *dev)
+{
+ return 0;
+}
+
+static const struct dev_pm_ops icm20602_pm_ops = {
+ .resume = inv_icm20602_resume,
+ .suspend = inv_icm20602_suspend,
+};
+
+static const struct of_device_id icm20602_match_table[] = {
+ {.compatible = "invensense,icm20602"},
+ {}
+};
+MODULE_DEVICE_TABLE(of, icm20602_match_table);
+
+static const struct i2c_device_id inv_icm20602_id[] = {
+ {"icm20602", 0},
+ {}
+};
+
+static struct i2c_driver icm20602_i2c_driver = {
+ .probe = inv_icm20602_probe,
+ .remove = inv_icm20602_remove,
+ .id_table = inv_icm20602_id,
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = "inv-icm20602",
+ .of_match_table = icm20602_match_table,
+ .probe_type = PROBE_PREFER_ASYNCHRONOUS,
+ .pm = &icm20602_pm_ops,
+ },
+};
+
+static int __init inv_icm20602_init(void)
+{
+ return i2c_add_driver(&icm20602_i2c_driver);
+}
+module_init(inv_icm20602_init);
+
+static void __exit inv_icm20602_exit(void)
+{
+ i2c_del_driver(&icm20602_i2c_driver);
+}
+module_exit(inv_icm20602_exit);
+
+MODULE_DESCRIPTION("icm20602 IMU driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/imu/inv_icm20602/inv_icm20602_iio.h b/drivers/iio/imu/inv_icm20602/inv_icm20602_iio.h
new file mode 100644
index 0000000..943fc1e
--- /dev/null
+++ b/drivers/iio/imu/inv_icm20602/inv_icm20602_iio.h
@@ -0,0 +1,282 @@
+/*
+ * Copyright (c) 2018, The Linux Foundation. All rights reserved.
+ *
+ * Copyright (C) 2012 Invensense, Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _INV_ICM20602_IIO_H_
+#define _INV_ICM20602_IIO_H_
+
+#include <linux/i2c.h>
+#include <linux/kfifo.h>
+#include <linux/spinlock.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/buffer.h>
+#include <linux/iio/sysfs.h>
+#include <linux/iio/kfifo_buf.h>
+#include <linux/iio/trigger.h>
+#include <linux/iio/triggered_buffer.h>
+#include <linux/iio/trigger_consumer.h>
+#include <linux/platform_data/invensense_mpu6050.h>
+
+/*
+ * it uses sensor irq to trigger
+ * if set INV20602_DEVICE_IRQ_TRIGGER as 1,
+ * otherwise use SMD IRQ to trigger
+ */
+#define INV20602_DEVICE_IRQ_TRIGGER 0
+
+#if INV20602_DEVICE_IRQ_TRIGGER
+#define INV20602_SMD_IRQ_TRIGGER 0
+#else
+#define INV20602_SMD_IRQ_TRIGGER 1
+#endif
+
+#define INV_ICM20602_TIME_STAMP_TOR 5
+#define ICM20602_PACKAGE_SIZE 14
+
+/* device enum */
+enum inv_devices {
+ INV_ICM20602,
+ INV_NUM_PARTS,
+};
+
+enum _mpu_err {
+ MPU_SUCCESS = 0,
+ MPU_FAIL = 1,
+ MPU_READ_FAIL = 2,
+ MPU_WRITE_FAIL = 3,
+};
+
+/* Gyro Full Scale Range Enum */
+enum inv_icm20602_gyro_fsr_e {
+ ICM20602_GYRO_FSR_250DPS = 0,
+ ICM20602_GYRO_FSR_500DPS,
+ ICM20602_GYRO_FSR_1000DPS,
+ ICM20602_GYRO_FSR_2000DPS,
+ ICM20602_GYRO_FSR_NUM,
+};
+
+/* Accelerometor Full Scale Range Enum */
+enum inv_icm20602_acc_fsr_e {
+ ICM20602_ACC_FSR_2G = 0,
+ ICM20602_ACC_FSR_4G,
+ ICM20602_ACC_FSR_8G,
+ ICM20602_ACC_FSR_16G,
+ ICM20602_ACC_FSR_NUM,
+};
+
+/* scan element definition */
+enum inv_icm20602_scan {
+ INV_ICM20602_SCAN_ACCL_X,
+ INV_ICM20602_SCAN_ACCL_Y,
+ INV_ICM20602_SCAN_ACCL_Z,
+ INV_ICM20602_SCAN_GYRO_X,
+ INV_ICM20602_SCAN_GYRO_Y,
+ INV_ICM20602_SCAN_GYRO_Z,
+ INV_ICM20602_SCAN_TEMP,
+ INV_ICM20602_SCAN_TIMESTAMP,
+};
+
+/* this is for CONFIGURATION reg bit[2:0] DLFP_CFG */
+enum inv_icm20602_gyro_temp_lpf_e {
+ INV_ICM20602_GLFP_250HZ = 0, /* 8KHz */
+ INV_ICM20602_GYRO_LFP_176HZ, /* 1KHz */
+ INV_ICM20602_GYRO_LFP_92HZ,
+ INV_ICM20602_GYRO_LFP_41HZ,
+ INV_ICM20602_GYRO_LFP_20HZ,
+ INV_ICM20602_GYRO_LFP_10HZ,
+ INV_ICM20602_GYRO_LFP_5HZ,
+ INV_ICM20602_GYRO_LFP_NUM,
+};
+
+enum inv_icm20602_gyro_sample_rate_e {
+ ICM20602_SAMPLE_RATE_100HZ = 100,
+ ICM20602_SAMPLE_RATE_200HZ = 200,
+ ICM20602_SAMPLE_RATE_500HZ = 500,
+ ICM20602_SAMPLE_RATE_1000HZ = 1000,
+};
+
+/* this is for ACCEL CONFIGURATION 2 reg */
+enum inv_icm20602_acc_lpf_e {
+ ICM20602_ACCLFP_218 = 1,
+ ICM20602_ACCLFP_99,
+ ICM20602_ACCLFP_44,
+ ICM20602_ACCLFP_21,
+ ICM20602_ACCLFP_10,
+ ICM20602_ACCLFP_5,
+ ICM20602_ACCLFP_420_NOLPF,
+ ICM20602_ACCLPF_NUM = 7,
+};
+
+/* IIO attribute address */
+enum INV_ICM20602_IIO_ATTR_ADDR {
+ ATTR_ICM20602_GYRO_MATRIX,
+ ATTR_ICM20602_ACCL_MATRIX,
+};
+
+/* this is for GYRO CONFIGURATION reg */
+enum inv_icm20602_fs_e {
+ INV_ICM20602_FS_250DPS = 0,
+ INV_ICM20602_FS_500DPS,
+ INV_ICM20602_FS_1000DPS,
+ INV_ICM20602_FS_2000DPS,
+ NUM_ICM20602_FS,
+};
+
+enum inv_icm20602_clock_sel_e {
+ INV_ICM20602_CLK_INTERNAL = 0,
+ INV_ICM20602_CLK_PLL,
+ INV_NUM_CLK,
+};
+
+enum inv_icm20602_spi_freq {
+ MPU_SPI_FREQUENCY_1MHZ = 960000UL,
+ MPU_SPI_FREQUENCY_5MHZ = 4800000UL,
+ MPU_SPI_FREQUENCY_8MHZ = 8000000UL,
+ MPU_SPI_FREQUENCY_10MHZ = 10000000UL,
+ MPU_SPI_FREQUENCY_15MHZ = 15000000UL,
+ MPU_SPI_FREQUENCY_20MHZ = 20000000UL,
+};
+
+#define MPU_SPI_BUF_LEN 512
+#define ICM20602_DEV_NAME "icm20602_iio"
+
+struct inv_icm20602_platform_data {
+ __s8 orientation[9];
+};
+
+struct X_Y_Z {
+ u8 X;
+ u8 Y;
+ u8 Z;
+};
+
+enum RAW_TYPE {
+ ACCEL = 1,
+ GYRO = 2,
+ TEMP = 4,
+};
+
+struct icm20602_user_config {
+ enum inv_icm20602_gyro_temp_lpf_e gyro_lpf;
+ enum inv_icm20602_gyro_fsr_e gyro_fsr;
+ struct X_Y_Z gyro_self_test;
+
+ enum inv_icm20602_acc_lpf_e acc_lpf;
+ enum inv_icm20602_acc_fsr_e acc_fsr;
+ struct X_Y_Z acc_self_test;
+
+ uint32_t gyro_accel_sample_rate;
+ uint32_t user_fps_in_ms;
+
+ bool fifo_enabled;
+ uint32_t fifo_waterlevel;
+ struct X_Y_Z wake_on_motion;
+};
+
+enum inv_icm20602_interface {
+ ICM20602_I2C = 0,
+ ICM20602_SPI
+};
+
+/*
+ * struct inv_icm20602_state - Driver state variables.
+ * @TIMESTAMP_FIFO_SIZE: fifo size for timestamp.
+ * @trig: IIO trigger.
+ * @chip_config: Cached attribute information.
+ * @reg: Map of important registers.
+ * @hw: Other hardware-specific information.
+ * @chip_type: chip type.
+ * @time_stamp_lock: spin lock to time stamp.
+ * @spi: spi devices
+ * @plat_data: platform data.
+ * @timestamps: kfifo queue to store time stamp.
+ */
+struct inv_icm20602_state {
+#define TIMESTAMP_FIFO_SIZE 32
+ enum inv_icm20602_interface interface;
+ struct iio_trigger *trig;
+ const struct inv_icm20602_reg_map *reg;
+ struct icm20602_user_config *config;
+ spinlock_t time_stamp_lock;
+ struct spi_device *spi;
+ struct i2c_client *client;
+ u8 fifo_packet_size;
+ int fifo_cnt_threshold;
+ char *buf;
+ struct struct_icm20602_data *data_push;
+ enum inv_devices chip_type;
+ int gpio;
+ DECLARE_KFIFO(timestamps, long long, TIMESTAMP_FIFO_SIZE);
+};
+
+struct struct_icm20602_raw_data {
+ u8 ACCEL_XOUT_H;
+ u8 ACCEL_XOUT_L;
+
+ u8 ACCEL_YOUT_H;
+ u8 ACCEL_YOUT_L;
+
+ u8 ACCEL_ZOUT_H;
+ u8 ACCEL_ZOUT_L;
+
+ u8 TEMP_OUT_H;
+ u8 TEMP_OUT_L;
+
+ u8 GYRO_XOUT_H;
+ u8 GYRO_XOUT_L;
+
+ u8 GYRO_YOUT_H;
+ u8 GYRO_YOUT_L;
+
+ u8 GYRO_ZOUT_H;
+ u8 GYRO_ZOUT_L;
+};
+
+struct struct_icm20602_real_data {
+ u16 ACCEL_XOUT;
+ u16 ACCEL_YOUT;
+ u16 ACCEL_ZOUT;
+
+ u16 GYRO_XOUT;
+ u16 GYRO_YOUT;
+ u16 GYRO_ZOUT;
+
+ u16 TEMP_OUT;
+};
+
+struct struct_icm20602_data {
+ s64 timestamps;
+ struct struct_icm20602_raw_data raw_data;
+ struct struct_icm20602_real_data real_data;
+};
+
+extern struct iio_trigger *inv_trig;
+irqreturn_t inv_icm20602_irq_handler(int irq, void *p);
+irqreturn_t inv_icm20602_read_fifo_fn(int irq, void *p);
+int inv_icm20602_reset_fifo(struct iio_dev *indio_dev);
+
+int inv_icm20602_probe_trigger(struct iio_dev *indio_dev);
+void inv_icm20602_remove_trigger(struct inv_icm20602_state *st);
+int inv_icm20602_validate_trigger(struct iio_dev *indio_dev,
+ struct iio_trigger *trig);
+
+int icm20602_read_raw(struct inv_icm20602_state *st,
+ struct struct_icm20602_real_data *real_data, uint32_t type);
+int icm20602_init_reg_map(void);
+int icm20602_init_device(struct inv_icm20602_state *st);
+int icm20602_detect(struct inv_icm20602_state *st);
+int icm20602_read_fifo(struct inv_icm20602_state *st,
+ void *buf, const int size);
+int icm20602_start_fifo(struct inv_icm20602_state *st);
+#endif
diff --git a/drivers/iio/imu/inv_icm20602/inv_icm20602_ring.c b/drivers/iio/imu/inv_icm20602/inv_icm20602_ring.c
new file mode 100644
index 0000000..b0f93be
--- /dev/null
+++ b/drivers/iio/imu/inv_icm20602/inv_icm20602_ring.c
@@ -0,0 +1,131 @@
+/*
+ * Copyright (c) 2018, The Linux Foundation. All rights reserved.
+ *
+ * Copyright (C) 2012 Invensense, Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/err.h>
+#include <linux/delay.h>
+#include <linux/sysfs.h>
+#include <linux/jiffies.h>
+#include <linux/irq.h>
+#include <linux/interrupt.h>
+#include <linux/kfifo.h>
+#include <linux/poll.h>
+#include <linux/spi/spi.h>
+#include "inv_icm20602_iio.h"
+#include <linux/i2c.h>
+
+#define combine_8_to_16(upper, lower) ((_upper << 8) | _lower)
+
+static void inv_clear_kfifo(struct inv_icm20602_state *st)
+{
+ unsigned long flags;
+
+ /* Take the spin lock sem to avoid interrupt kick in */
+ spin_lock_irqsave(&st->time_stamp_lock, flags);
+ kfifo_reset(&st->timestamps);
+ spin_unlock_irqrestore(&st->time_stamp_lock, flags);
+}
+
+int inv_icm20602_reset_fifo(struct iio_dev *indio_dev)
+{
+ int result;
+ //u8 d;
+ //struct inv_icm20602_state *st = iio_priv(indio_dev);
+
+ return result;
+}
+
+/*
+ * inv_icm20602_irq_handler() - Cache a timestamp at each data ready interrupt.
+ */
+irqreturn_t inv_icm20602_irq_handler(int irq, void *p)
+{
+ struct iio_poll_func *pf = p;
+ struct iio_dev *indio_dev = pf->indio_dev;
+ struct inv_icm20602_state *st = iio_priv(indio_dev);
+ s64 timestamp;
+
+ timestamp = iio_get_time_ns(indio_dev);
+
+ kfifo_in_spinlocked(&st->timestamps, ×tamp, 1,
+ &st->time_stamp_lock);
+
+ return IRQ_WAKE_THREAD;
+}
+
+static int inv_icm20602_read_data(struct iio_dev *indio_dev)
+{
+ int result = MPU_SUCCESS;
+ struct inv_icm20602_state *st = iio_priv(indio_dev);
+ struct icm20602_user_config *config = st->config;
+ int package_count;
+ //char *buf = st->buf;
+ //struct struct_icm20602_data *data_push = st->data_push;
+ s64 timestamp;
+ int i;
+
+ if (!st)
+ return -MPU_FAIL;
+ package_count = config->fifo_waterlevel / ICM20602_PACKAGE_SIZE;
+ mutex_lock(&indio_dev->mlock);
+ if (config->fifo_enabled) {
+ result = icm20602_read_fifo(st,
+ st->buf, config->fifo_waterlevel);
+ if (result != config->fifo_waterlevel) {
+ pr_err("icm20602 read fifo failed, result = %d\n",
+ result);
+ goto flush_fifo;
+ }
+
+ for (i = 0; i < package_count; i++) {
+ memcpy((char *)(&st->data_push[i].raw_data),
+ st->buf, ICM20602_PACKAGE_SIZE);
+ result = kfifo_out(&st->timestamps,
+ ×tamp, 1);
+ /* when there is no timestamp, put it as 0 */
+ if (result == 0)
+ timestamp = 0;
+ st->data_push[i].timestamps = timestamp;
+ iio_push_to_buffers(indio_dev, st->data_push+i);
+ st->buf += ICM20602_PACKAGE_SIZE;
+ }
+ }
+//end_session:
+ mutex_unlock(&indio_dev->mlock);
+ iio_trigger_notify_done(indio_dev->trig);
+ return MPU_SUCCESS;
+
+flush_fifo:
+ /* Flush HW and SW FIFOs. */
+ inv_clear_kfifo(st);
+ inv_icm20602_reset_fifo(indio_dev);
+ mutex_unlock(&indio_dev->mlock);
+ iio_trigger_notify_done(indio_dev->trig);
+ return MPU_SUCCESS;
+}
+
+/*
+ * inv_icm20602_read_fifo() - Transfer data from hardware FIFO to KFIFO.
+ */
+irqreturn_t inv_icm20602_read_fifo_fn(int irq, void *p)
+{
+ struct iio_poll_func *pf = p;
+ struct iio_dev *indio_dev = pf->indio_dev;
+
+ inv_icm20602_read_data(indio_dev);
+ return IRQ_HANDLED;
+}
diff --git a/drivers/iio/imu/inv_icm20602/inv_icm20602_trigger.c b/drivers/iio/imu/inv_icm20602/inv_icm20602_trigger.c
new file mode 100644
index 0000000..f05a631
--- /dev/null
+++ b/drivers/iio/imu/inv_icm20602/inv_icm20602_trigger.c
@@ -0,0 +1,83 @@
+/*
+ * Copyright (c) 2018, The Linux Foundation. All rights reserved.
+ *
+ * Copyright (C) 2012 Invensense, Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/spi/spi.h>
+#include <linux/sched/rt.h>
+#include <linux/delay.h>
+#include "inv_icm20602_iio.h"
+#include <linux/i2c.h>
+
+static const struct iio_trigger_ops inv_icm20602_trigger_ops = {
+ .owner = THIS_MODULE,
+};
+
+int inv_icm20602_validate_trigger(struct iio_dev *indio_dev,
+ struct iio_trigger *trig)
+{
+ struct inv_icm20602_state *st = iio_priv(indio_dev);
+
+ if (st->trig != trig)
+ return -EINVAL;
+
+ return 0;
+}
+
+int inv_icm20602_probe_trigger(struct iio_dev *indio_dev)
+{
+ int ret;
+ struct inv_icm20602_state *st = iio_priv(indio_dev);
+
+ st->trig = iio_trigger_alloc("%s-dev%d",
+ indio_dev->name,
+ indio_dev->id);
+ if (st->trig == NULL) {
+ ret = -ENOMEM;
+ goto error_ret;
+ }
+
+ ret = request_irq(st->client->irq, &iio_trigger_generic_data_rdy_poll,
+ IRQF_TRIGGER_RISING,
+ "inv_icm20602",
+ st->trig);
+ if (ret) {
+ pr_err("request_irq failed\n");
+ goto error_free_trig;
+ }
+ st->trig->dev.parent = &st->client->dev;
+ st->trig->ops = &inv_icm20602_trigger_ops;
+ iio_trigger_set_drvdata(st->trig, indio_dev);
+ ret = iio_trigger_register(st->trig);
+ if (ret) {
+ pr_err("iio_trigger_register failed\n");
+ goto error_free_irq;
+ }
+ indio_dev->trig = st->trig;
+
+ return 0;
+
+error_free_irq:
+ free_irq(st->client->irq, st->trig);
+error_free_trig:
+ iio_trigger_free(st->trig);
+error_ret:
+ return ret;
+}
+
+void inv_icm20602_remove_trigger(struct inv_icm20602_state *st)
+{
+ iio_trigger_unregister(st->trig);
+ free_irq(st->client->irq, st->trig);
+ iio_trigger_free(st->trig);
+}
diff --git a/drivers/media/platform/msm/camera_v2/sensor/csiphy/msm_csiphy.c b/drivers/media/platform/msm/camera_v2/sensor/csiphy/msm_csiphy.c
index 72586fa..6fc8e1e 100644
--- a/drivers/media/platform/msm/camera_v2/sensor/csiphy/msm_csiphy.c
+++ b/drivers/media/platform/msm/camera_v2/sensor/csiphy/msm_csiphy.c
@@ -293,10 +293,18 @@
void __iomem *csiphybase;
enum snps_csiphy_mode mode = INVALID_MODE;
uint32_t value, num_tries, num_lanes, offset;
+ uint32_t clk_mux_reg = 0;
csiphybase = csiphy_dev->base;
+ if (csiphy_dev->clk_mux_base != NULL)
+ clk_mux_reg = msm_camera_io_r(csiphy_dev->clk_mux_base);
+ else {
+ pr_err("%s: invalid clk_mux_base\n", __func__);
+ return -EINVAL;
+ }
+
/* lane mask usage
- * BIT LANE
+ * BIT LANE
* 0(LSB) PHY A data 0
* 1 PHY A data 1
* 2 PHY B data 0
@@ -318,6 +326,9 @@
return -EINVAL;
}
csiphy_dev->snps_state = CONFIGURED_AGGREGATE_MODE;
+ clk_mux_reg &= ~0xff;
+ clk_mux_reg |= csiphy_params->csid_core << 4;
+ clk_mux_reg |= (uint32_t)csiphy_params->csid_core;
} else if (lane_mask == LANE_MASK_PHY_A) { /* PHY A */
/* 2 lane config */
num_lanes = 2;
@@ -333,6 +344,8 @@
pr_err("%s: invalid request\n", __func__);
return -EINVAL;
}
+ clk_mux_reg &= ~0xf;
+ clk_mux_reg |= (uint32_t)csiphy_params->csid_core;
} else if (lane_mask == LANE_MASK_PHY_B) { /* PHY B */
/* 2 lane config */
num_lanes = 2;
@@ -348,11 +361,17 @@
pr_err("%s: invalid request\n", __func__);
return -EINVAL;
}
+ clk_mux_reg &= ~0xf0;
+ clk_mux_reg |= csiphy_params->csid_core << 4;
} else { /* None of available configurations */
pr_err("%s: invalid configuration requested\n", __func__);
return -EINVAL;
}
+ msm_camera_io_w(clk_mux_reg, csiphy_dev->clk_mux_base);
+ /* ensure write is done */
+ mb();
+
if (mode == AGGREGATE_MODE || mode == TWO_LANE_PHY_A) {
ret = msm_csiphy_snps_2_lane_config(csiphy_dev,
csiphy_params, TWO_LANE_PHY_A, num_lanes);
diff --git a/drivers/platform/msm/ipa/ipa_v2/ipa_qmi_service.h b/drivers/platform/msm/ipa/ipa_v2/ipa_qmi_service.h
index 1f5d619..98f5574 100644
--- a/drivers/platform/msm/ipa/ipa_v2/ipa_qmi_service.h
+++ b/drivers/platform/msm/ipa/ipa_v2/ipa_qmi_service.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2013-2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2013-2018, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -281,7 +281,7 @@
{
}
-static int rmnet_ipa_reset_tethering_stats
+static inline int rmnet_ipa_reset_tethering_stats
(
struct wan_ioctl_reset_tether_stats *data
)
diff --git a/drivers/power/supply/qcom/Makefile b/drivers/power/supply/qcom/Makefile
index da20d93..ffc1ce3 100644
--- a/drivers/power/supply/qcom/Makefile
+++ b/drivers/power/supply/qcom/Makefile
@@ -7,7 +7,7 @@
obj-$(CONFIG_SMB1351_USB_CHARGER) += smb1351-charger.o pmic-voter.o battery.o
obj-$(CONFIG_QPNP_SMB2) += step-chg-jeita.o battery.o qpnp-smb2.o smb-lib.o pmic-voter.o storm-watch.o
obj-$(CONFIG_SMB138X_CHARGER) += step-chg-jeita.o smb138x-charger.o smb-lib.o pmic-voter.o storm-watch.o battery.o
-obj-$(CONFIG_QPNP_QG) += qpnp-qg.o pmic-voter.o qg-util.o qg-soc.o qg-sdam.o qg-battery-profile.o qg-profile-lib.o
+obj-$(CONFIG_QPNP_QG) += qpnp-qg.o pmic-voter.o qg-util.o qg-soc.o qg-sdam.o qg-battery-profile.o qg-profile-lib.o fg-alg.o
obj-$(CONFIG_QPNP_QNOVO) += qpnp-qnovo.o battery.o
obj-$(CONFIG_QPNP_TYPEC) += qpnp-typec.o
obj-$(CONFIG_QPNP_SMB5) += step-chg-jeita.o battery.o qpnp-smb5.o smb5-lib.o pmic-voter.o storm-watch.o schgm-flash.o
diff --git a/drivers/power/supply/qcom/qg-battery-profile.c b/drivers/power/supply/qcom/qg-battery-profile.c
index 441c759..36edd76 100644
--- a/drivers/power/supply/qcom/qg-battery-profile.c
+++ b/drivers/power/supply/qcom/qg-battery-profile.c
@@ -22,6 +22,7 @@
#include <linux/slab.h>
#include <linux/uaccess.h>
#include <uapi/linux/qg-profile.h>
+#include "qg-battery-profile.h"
#include "qg-profile-lib.h"
#include "qg-defs.h"
@@ -410,6 +411,26 @@
return 0;
}
+int qg_get_nominal_capacity(u32 *nom_cap_uah, int batt_temp, bool charging)
+{
+ u8 table_index = charging ? TABLE_FCC1 : TABLE_FCC2;
+ u32 fcc_mah;
+
+ if (!the_battery || !the_battery->profile) {
+ pr_err("Battery profile not loaded\n");
+ return -ENODEV;
+ }
+
+ fcc_mah = interpolate_single_row_lut(
+ &the_battery->profile[table_index],
+ batt_temp, DEGC_SCALE);
+ fcc_mah = CAP(QG_MIN_FCC_MAH, QG_MAX_FCC_MAH, fcc_mah);
+
+ *nom_cap_uah = fcc_mah * 1000;
+
+ return 0;
+}
+
int qg_batterydata_init(struct device_node *profile_node)
{
int rc = 0;
diff --git a/drivers/power/supply/qcom/qg-battery-profile.h b/drivers/power/supply/qcom/qg-battery-profile.h
index 143a4f2..1b06277 100644
--- a/drivers/power/supply/qcom/qg-battery-profile.h
+++ b/drivers/power/supply/qcom/qg-battery-profile.h
@@ -15,5 +15,6 @@
int qg_batterydata_init(struct device_node *node);
void qg_batterydata_exit(void);
int lookup_soc_ocv(u32 *soc, u32 ocv_uv, int batt_temp, bool charging);
+int qg_get_nominal_capacity(u32 *nom_cap_uah, int batt_temp, bool charging);
#endif /* __QG_BATTERY_PROFILE_H__ */
diff --git a/drivers/power/supply/qcom/qg-core.h b/drivers/power/supply/qcom/qg-core.h
index 6e44f33..05ca452 100644
--- a/drivers/power/supply/qcom/qg-core.h
+++ b/drivers/power/supply/qcom/qg-core.h
@@ -12,6 +12,9 @@
#ifndef __QG_CORE_H__
#define __QG_CORE_H__
+#include <linux/kernel.h>
+#include "fg-alg.h"
+
struct qg_batt_props {
const char *batt_type_str;
int float_volt_uv;
@@ -49,6 +52,8 @@
int cold_temp_threshold;
bool hold_soc_while_full;
bool linearize_soc;
+ bool cl_disable;
+ bool cl_feedback_on;
};
struct qpnp_qg {
@@ -109,12 +114,19 @@
int maint_soc;
int msoc;
int pon_soc;
+ int batt_soc;
+ int cc_soc;
struct alarm alarm_timer;
u32 sdam_data[SDAM_MAX];
/* DT */
struct qg_dt dt;
struct qg_batt_props bp;
+ /* capacity learning */
+ struct cap_learning *cl;
+ /* charge counter */
+ struct cycle_counter *counter;
+ char counter_buf[BUCKET_COUNT * 8];
};
enum ocv_type {
@@ -135,6 +147,7 @@
QG_DEBUG_PM = BIT(7),
QG_DEBUG_BUS_READ = BIT(8),
QG_DEBUG_BUS_WRITE = BIT(9),
+ QG_DEBUG_ALG_CL = BIT(10),
};
enum qg_irq {
diff --git a/drivers/power/supply/qcom/qg-defs.h b/drivers/power/supply/qcom/qg-defs.h
index 6ae9aa2..2061208 100644
--- a/drivers/power/supply/qcom/qg-defs.h
+++ b/drivers/power/supply/qcom/qg-defs.h
@@ -47,4 +47,7 @@
#define CAP(min, max, value) \
((min > value) ? min : ((value > max) ? max : value))
+#define QG_SOC_FULL 10000
+#define BATT_SOC_32BIT GENMASK(31, 0)
+
#endif /* __QG_DEFS_H__ */
diff --git a/drivers/power/supply/qcom/qg-reg.h b/drivers/power/supply/qcom/qg-reg.h
index 96533d4..66f9be1 100644
--- a/drivers/power/supply/qcom/qg-reg.h
+++ b/drivers/power/supply/qcom/qg-reg.h
@@ -87,6 +87,8 @@
#define QG_SDAM_OCV_OFFSET 0x4C
#define QG_SDAM_IBAT_OFFSET 0x50
#define QG_SDAM_TIME_OFFSET 0x54
+#define QG_SDAM_CYCLE_COUNT_OFFSET 0x58
+#define QG_SDAM_LEARNED_CAPACITY_OFFSET 0x68
#define QG_SDAM_PON_OCV_OFFSET 0x7C
#endif
diff --git a/drivers/power/supply/qcom/qg-sdam.c b/drivers/power/supply/qcom/qg-sdam.c
index 54eed16..7bc4afa 100644
--- a/drivers/power/supply/qcom/qg-sdam.c
+++ b/drivers/power/supply/qcom/qg-sdam.c
@@ -130,6 +130,53 @@
return rc;
}
+int qg_sdam_multibyte_write(u32 offset, u8 *data, u32 length)
+{
+ int rc, i;
+ struct qg_sdam *chip = the_chip;
+
+ if (!chip) {
+ pr_err("Invalid sdam-chip pointer\n");
+ return -EINVAL;
+ }
+
+ offset = chip->sdam_base + offset;
+ rc = regmap_bulk_write(chip->regmap, offset, data, (size_t)length);
+ if (rc < 0) {
+ pr_err("Failed to write offset=%0x4x value=%d\n",
+ offset, *data);
+ } else {
+ for (i = 0; i < length; i++)
+ pr_debug("QG SDAM write offset=%0x4x value=%d\n",
+ offset++, data[i]);
+ }
+
+ return rc;
+}
+
+int qg_sdam_multibyte_read(u32 offset, u8 *data, u32 length)
+{
+ int rc, i;
+ struct qg_sdam *chip = the_chip;
+
+ if (!chip) {
+ pr_err("Invalid sdam-chip pointer\n");
+ return -EINVAL;
+ }
+
+ offset = chip->sdam_base + offset;
+ rc = regmap_raw_read(chip->regmap, offset, (u8 *)data, (size_t)length);
+ if (rc < 0) {
+ pr_err("Failed to read offset=%0x4x\n", offset);
+ } else {
+ for (i = 0; i < length; i++)
+ pr_debug("QG SDAM read offset=%0x4x value=%d\n",
+ offset++, data[i]);
+ }
+
+ return rc;
+}
+
int qg_sdam_read_all(u32 *sdam_data)
{
int i, rc;
diff --git a/drivers/power/supply/qcom/qg-sdam.h b/drivers/power/supply/qcom/qg-sdam.h
index 51af04c..10e684f 100644
--- a/drivers/power/supply/qcom/qg-sdam.h
+++ b/drivers/power/supply/qcom/qg-sdam.h
@@ -37,5 +37,7 @@
int qg_sdam_read(u8 param, u32 *data);
int qg_sdam_write_all(u32 *sdam_data);
int qg_sdam_read_all(u32 *sdam_data);
+int qg_sdam_multibyte_write(u32 offset, u8 *sdam_data, u32 length);
+int qg_sdam_multibyte_read(u32 offset, u8 *sdam_data, u32 length);
#endif
diff --git a/drivers/power/supply/qcom/qg-soc.c b/drivers/power/supply/qcom/qg-soc.c
index c4d5043..af8b158 100644
--- a/drivers/power/supply/qcom/qg-soc.c
+++ b/drivers/power/supply/qcom/qg-soc.c
@@ -13,9 +13,11 @@
#define pr_fmt(fmt) "QG-K: %s: " fmt, __func__
#include <linux/alarmtimer.h>
+#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/power_supply.h>
#include <uapi/linux/qg.h>
+#include "fg-alg.h"
#include "qg-sdam.h"
#include "qg-core.h"
#include "qg-reg.h"
@@ -98,11 +100,12 @@
static void update_msoc(struct qpnp_qg *chip)
{
- int rc;
+ int rc = 0, batt_temp = 0, batt_soc_32bit = 0;
+ bool usb_present = is_usb_present(chip);
if (chip->catch_up_soc > chip->msoc) {
/* SOC increased */
- if (is_usb_present(chip)) /* Increment if USB is present */
+ if (usb_present) /* Increment if USB is present */
chip->msoc += chip->dt.delta_soc;
} else if (chip->catch_up_soc < chip->msoc) {
/* SOC dropped */
@@ -130,6 +133,25 @@
if (rc < 0)
pr_err("Failed to update SDAM with MSOC rc=%d\n", rc);
+ if (!chip->dt.cl_disable && chip->cl->active) {
+ rc = qg_get_battery_temp(chip, &batt_temp);
+ if (rc < 0) {
+ pr_err("Failed to read BATT_TEMP rc=%d\n", rc);
+ } else {
+ batt_soc_32bit = div64_u64(
+ chip->batt_soc * BATT_SOC_32BIT,
+ QG_SOC_FULL);
+ cap_learning_update(chip->cl, batt_temp, batt_soc_32bit,
+ chip->charge_status, chip->charge_done,
+ usb_present, false);
+ }
+ }
+
+ cycle_count_update(chip->counter,
+ DIV_ROUND_CLOSEST(chip->msoc * 255, 100),
+ chip->charge_status, chip->charge_done,
+ usb_present);
+
qg_dbg(chip, QG_DEBUG_SOC,
"SOC scale: Update maint_soc=%d msoc=%d catch_up_soc=%d delta_soc=%d\n",
chip->maint_soc, chip->msoc,
diff --git a/drivers/power/supply/qcom/qpnp-qg.c b/drivers/power/supply/qcom/qpnp-qg.c
index f7a6b7a..09705ad 100644
--- a/drivers/power/supply/qcom/qpnp-qg.c
+++ b/drivers/power/supply/qcom/qpnp-qg.c
@@ -16,6 +16,7 @@
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/interrupt.h>
+#include <linux/kernel.h>
#include <linux/ktime.h>
#include <linux/module.h>
#include <linux/of.h>
@@ -28,6 +29,7 @@
#include <linux/pmic-voter.h>
#include <linux/qpnp/qpnp-adc.h>
#include <uapi/linux/qg.h>
+#include "fg-alg.h"
#include "qg-sdam.h"
#include "qg-core.h"
#include "qg-reg.h"
@@ -641,6 +643,12 @@
struct qpnp_qg, udata_work);
int rc;
+ if (chip->udata.param[QG_CC_SOC].valid)
+ chip->cc_soc = chip->udata.param[QG_CC_SOC].data;
+
+ if (chip->udata.param[QG_BATT_SOC].valid)
+ chip->batt_soc = chip->udata.param[QG_BATT_SOC].data;
+
if (chip->udata.param[QG_SOC].valid) {
qg_dbg(chip, QG_DEBUG_SOC, "udata SOC=%d last SOC=%d\n",
chip->udata.param[QG_SOC].data, chip->catch_up_soc);
@@ -946,6 +954,127 @@
return 0;
}
+/* ALG callback functions below */
+
+static int qg_get_learned_capacity(void *data, int64_t *learned_cap_uah)
+{
+ struct qpnp_qg *chip = data;
+ int16_t cc_mah;
+ int rc;
+
+ if (!chip)
+ return -ENODEV;
+
+ if (chip->battery_missing || !chip->profile_loaded)
+ return -EPERM;
+
+ rc = qg_sdam_multibyte_read(QG_SDAM_LEARNED_CAPACITY_OFFSET,
+ (u8 *)&cc_mah, 2);
+ if (rc < 0) {
+ pr_err("Error in reading learned_capacity, rc=%d\n", rc);
+ return rc;
+ }
+ *learned_cap_uah = cc_mah * 1000;
+
+ qg_dbg(chip, QG_DEBUG_ALG_CL, "Retrieved learned capacity %llduah\n",
+ *learned_cap_uah);
+ return 0;
+}
+
+static int qg_store_learned_capacity(void *data, int64_t learned_cap_uah)
+{
+ struct qpnp_qg *chip = data;
+ int16_t cc_mah;
+ int rc;
+
+ if (!chip)
+ return -ENODEV;
+
+ if (chip->battery_missing || !learned_cap_uah)
+ return -EPERM;
+
+ cc_mah = div64_s64(learned_cap_uah, 1000);
+ rc = qg_sdam_multibyte_write(QG_SDAM_LEARNED_CAPACITY_OFFSET,
+ (u8 *)&cc_mah, 2);
+ if (rc < 0) {
+ pr_err("Error in writing learned_capacity, rc=%d\n", rc);
+ return rc;
+ }
+
+ qg_dbg(chip, QG_DEBUG_ALG_CL, "Stored learned capacity %llduah\n",
+ learned_cap_uah);
+ return 0;
+}
+
+static int qg_get_cc_soc(void *data, int *cc_soc)
+{
+ struct qpnp_qg *chip = data;
+
+ if (!chip)
+ return -ENODEV;
+
+ if (chip->cc_soc == INT_MIN)
+ return -EINVAL;
+
+ *cc_soc = chip->cc_soc;
+
+ return 0;
+}
+
+static int qg_restore_cycle_count(void *data, u16 *buf, int length)
+{
+ struct qpnp_qg *chip = data;
+ int id, rc = 0;
+ u8 tmp[2];
+
+ if (!chip)
+ return -ENODEV;
+
+ if (chip->battery_missing || !chip->profile_loaded)
+ return -EPERM;
+
+ if (!buf || length > BUCKET_COUNT)
+ return -EINVAL;
+
+ for (id = 0; id < length; id++) {
+ rc = qg_sdam_multibyte_read(
+ QG_SDAM_CYCLE_COUNT_OFFSET + (id * 2),
+ (u8 *)tmp, 2);
+ if (rc < 0) {
+ pr_err("failed to read bucket %d rc=%d\n", id, rc);
+ return rc;
+ }
+ *buf++ = tmp[0] | tmp[1] << 8;
+ }
+
+ return rc;
+}
+
+static int qg_store_cycle_count(void *data, u16 *buf, int id, int length)
+{
+ struct qpnp_qg *chip = data;
+ int rc = 0;
+
+ if (!chip)
+ return -ENODEV;
+
+ if (chip->battery_missing || !chip->profile_loaded)
+ return -EPERM;
+
+ if (!buf || length > BUCKET_COUNT * 2 || id < 0 ||
+ id > BUCKET_COUNT - 1 ||
+ (((id * 2) + length) > BUCKET_COUNT * 2))
+ return -EINVAL;
+
+ rc = qg_sdam_multibyte_write(
+ QG_SDAM_CYCLE_COUNT_OFFSET + (id * 2),
+ (u8 *)buf, length);
+ if (rc < 0)
+ pr_err("failed to write bucket %d rc=%d\n", id, rc);
+
+ return rc;
+}
+
#define DEFAULT_BATT_TYPE "Unknown Battery"
#define MISSING_BATT_TYPE "Missing Battery"
#define DEBUG_BATT_TYPE "Debug Board"
@@ -1042,10 +1171,62 @@
return 0;
}
+static const char *qg_get_cycle_counts(struct qpnp_qg *chip)
+{
+ int i, rc, len = 0;
+ char *buf;
+
+ buf = chip->counter_buf;
+ for (i = 1; i <= BUCKET_COUNT; i++) {
+ chip->counter->id = i;
+ rc = get_cycle_count(chip->counter);
+ if (rc < 0) {
+ pr_err("Couldn't get cycle count rc=%d\n", rc);
+ return NULL;
+ }
+
+ if (sizeof(chip->counter_buf) - len < 8) {
+ pr_err("Invalid length %d\n", len);
+ return NULL;
+ }
+
+ len += snprintf(buf+len, 8, "%d ", rc);
+ }
+
+ buf[len] = '\0';
+ return buf;
+}
+
static int qg_psy_set_property(struct power_supply *psy,
enum power_supply_property psp,
const union power_supply_propval *pval)
{
+ struct qpnp_qg *chip = power_supply_get_drvdata(psy);
+ int rc = 0;
+
+ switch (psp) {
+ case POWER_SUPPLY_PROP_CHARGE_FULL:
+ if (chip->dt.cl_disable) {
+ pr_warn("Capacity learning disabled!\n");
+ return 0;
+ }
+ if (chip->cl->active) {
+ pr_warn("Capacity learning active!\n");
+ return 0;
+ }
+ if (pval->intval <= 0 || pval->intval > chip->cl->nom_cap_uah) {
+ pr_err("charge_full is out of bounds\n");
+ return -EINVAL;
+ }
+ mutex_lock(&chip->cl->lock);
+ rc = qg_store_learned_capacity(chip, pval->intval);
+ if (!rc)
+ chip->cl->learned_cap_uah = pval->intval;
+ mutex_unlock(&chip->cl->lock);
+ break;
+ default:
+ break;
+ }
return 0;
}
@@ -1055,6 +1236,7 @@
{
struct qpnp_qg *chip = power_supply_get_drvdata(psy);
int rc = 0;
+ int64_t temp = 0;
pval->intval = 0;
@@ -1106,6 +1288,22 @@
case POWER_SUPPLY_PROP_CHARGE_COUNTER:
pval->intval = chip->charge_counter_uah;
break;
+ case POWER_SUPPLY_PROP_CHARGE_FULL:
+ if (!chip->dt.cl_disable && chip->dt.cl_feedback_on)
+ rc = qg_get_learned_capacity(chip, &temp);
+ else
+ rc = qg_get_nominal_capacity((int *)&temp, 250, true);
+ if (!rc)
+ pval->intval = (int)temp;
+ break;
+ case POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN:
+ rc = qg_get_nominal_capacity((int *)&temp, 250, true);
+ if (!rc)
+ pval->intval = (int)temp;
+ break;
+ case POWER_SUPPLY_PROP_CYCLE_COUNTS:
+ pval->strval = qg_get_cycle_counts(chip);
+ break;
default:
pr_debug("Unsupported property %d\n", psp);
break;
@@ -1117,6 +1315,12 @@
static int qg_property_is_writeable(struct power_supply *psy,
enum power_supply_property psp)
{
+ switch (psp) {
+ case POWER_SUPPLY_PROP_CHARGE_FULL:
+ return 1;
+ default:
+ break;
+ }
return 0;
}
@@ -1136,6 +1340,9 @@
POWER_SUPPLY_PROP_VOLTAGE_MAX,
POWER_SUPPLY_PROP_BATT_FULL_CURRENT,
POWER_SUPPLY_PROP_BATT_PROFILE_VERSION,
+ POWER_SUPPLY_PROP_CYCLE_COUNTS,
+ POWER_SUPPLY_PROP_CHARGE_FULL,
+ POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN,
};
static const struct power_supply_desc qg_psy_desc = {
@@ -1262,7 +1469,7 @@
struct qpnp_qg *chip = container_of(work,
struct qpnp_qg, qg_status_change_work);
union power_supply_propval prop = {0, };
- int rc = 0;
+ int rc = 0, batt_temp = 0, batt_soc_32b = 0;
if (!is_batt_available(chip)) {
pr_debug("batt-psy not available\n");
@@ -1294,6 +1501,24 @@
if (rc < 0)
pr_err("Failed to update usb status, rc=%d\n", rc);
+ cycle_count_update(chip->counter,
+ DIV_ROUND_CLOSEST(chip->msoc * 255, 100),
+ chip->charge_status, chip->charge_done,
+ chip->usb_present);
+
+ if (!chip->dt.cl_disable) {
+ rc = qg_get_battery_temp(chip, &batt_temp);
+ if (rc < 0) {
+ pr_err("Failed to read BATT_TEMP at PON rc=%d\n", rc);
+ } else {
+ batt_soc_32b = div64_u64(
+ chip->batt_soc * BATT_SOC_32BIT,
+ QG_SOC_FULL);
+ cap_learning_update(chip->cl, batt_temp, batt_soc_32b,
+ chip->charge_status, chip->charge_done,
+ chip->usb_present, false);
+ }
+ }
rc = qg_charge_full_update(chip);
if (rc < 0)
pr_err("Failed in charge_full_update, rc=%d\n", rc);
@@ -2069,6 +2294,64 @@
return 0;
}
+static int qg_alg_init(struct qpnp_qg *chip)
+{
+ struct cycle_counter *counter;
+ struct cap_learning *cl;
+ struct device_node *node = chip->dev->of_node;
+ int rc;
+
+ counter = devm_kzalloc(chip->dev, sizeof(*counter), GFP_KERNEL);
+ if (!counter)
+ return -ENOMEM;
+
+ counter->restore_count = qg_restore_cycle_count;
+ counter->store_count = qg_store_cycle_count;
+ counter->data = chip;
+
+ rc = cycle_count_init(counter);
+ if (rc < 0) {
+ dev_err(chip->dev, "Error in initializing cycle counter, rc:%d\n",
+ rc);
+ counter->data = NULL;
+ devm_kfree(chip->dev, counter);
+ return rc;
+ }
+
+ chip->counter = counter;
+
+ chip->dt.cl_disable = of_property_read_bool(node,
+ "qcom,cl-disable");
+
+ /*Return if capacity learning is disabled*/
+ if (chip->dt.cl_disable)
+ return 0;
+
+ cl = devm_kzalloc(chip->dev, sizeof(*cl), GFP_KERNEL);
+ if (!cl)
+ return -ENOMEM;
+
+ cl->cc_soc_max = QG_SOC_FULL;
+ cl->get_cc_soc = qg_get_cc_soc;
+ cl->get_learned_capacity = qg_get_learned_capacity;
+ cl->store_learned_capacity = qg_store_learned_capacity;
+ cl->data = chip;
+
+ rc = cap_learning_init(cl);
+ if (rc < 0) {
+ dev_err(chip->dev, "Error in initializing capacity learning, rc:%d\n",
+ rc);
+ counter->data = NULL;
+ cl->data = NULL;
+ devm_kfree(chip->dev, counter);
+ devm_kfree(chip->dev, cl);
+ return rc;
+ }
+
+ chip->cl = cl;
+ return 0;
+}
+
#define DEFAULT_VBATT_EMPTY_MV 3200
#define DEFAULT_VBATT_EMPTY_COLD_MV 3000
#define DEFAULT_VBATT_CUTOFF_MV 3400
@@ -2082,6 +2365,14 @@
#define DEFAULT_DELTA_SOC 1
#define DEFAULT_SHUTDOWN_SOC_SECS 360
#define DEFAULT_COLD_TEMP_THRESHOLD 0
+#define DEFAULT_CL_MIN_START_SOC 10
+#define DEFAULT_CL_MAX_START_SOC 15
+#define DEFAULT_CL_MIN_TEMP_DECIDEGC 150
+#define DEFAULT_CL_MAX_TEMP_DECIDEGC 500
+#define DEFAULT_CL_MAX_INC_DECIPERC 5
+#define DEFAULT_CL_MAX_DEC_DECIPERC 100
+#define DEFAULT_CL_MIN_LIM_DECIPERC 0
+#define DEFAULT_CL_MAX_LIM_DECIPERC 0
static int qg_parse_dt(struct qpnp_qg *chip)
{
int rc = 0;
@@ -2275,6 +2566,65 @@
else
chip->dt.rbat_conn_mohm = temp;
+ /* Capacity learning params*/
+ if (!chip->dt.cl_disable) {
+ chip->dt.cl_feedback_on = of_property_read_bool(node,
+ "qcom,cl-feedback-on");
+
+ rc = of_property_read_u32(node, "qcom,cl-min-start-soc", &temp);
+ if (rc < 0)
+ chip->cl->dt.min_start_soc = DEFAULT_CL_MIN_START_SOC;
+ else
+ chip->cl->dt.min_start_soc = temp;
+
+ rc = of_property_read_u32(node, "qcom,cl-max-start-soc", &temp);
+ if (rc < 0)
+ chip->cl->dt.max_start_soc = DEFAULT_CL_MAX_START_SOC;
+ else
+ chip->cl->dt.max_start_soc = temp;
+
+ rc = of_property_read_u32(node, "qcom,cl-min-temp", &temp);
+ if (rc < 0)
+ chip->cl->dt.min_temp = DEFAULT_CL_MIN_TEMP_DECIDEGC;
+ else
+ chip->cl->dt.min_temp = temp;
+
+ rc = of_property_read_u32(node, "qcom,cl-max-temp", &temp);
+ if (rc < 0)
+ chip->cl->dt.max_temp = DEFAULT_CL_MAX_TEMP_DECIDEGC;
+ else
+ chip->cl->dt.max_temp = temp;
+
+ rc = of_property_read_u32(node, "qcom,cl-max-increment", &temp);
+ if (rc < 0)
+ chip->cl->dt.max_cap_inc = DEFAULT_CL_MAX_INC_DECIPERC;
+ else
+ chip->cl->dt.max_cap_inc = temp;
+
+ rc = of_property_read_u32(node, "qcom,cl-max-decrement", &temp);
+ if (rc < 0)
+ chip->cl->dt.max_cap_dec = DEFAULT_CL_MAX_DEC_DECIPERC;
+ else
+ chip->cl->dt.max_cap_dec = temp;
+
+ rc = of_property_read_u32(node, "qcom,cl-min-limit", &temp);
+ if (rc < 0)
+ chip->cl->dt.min_cap_limit =
+ DEFAULT_CL_MIN_LIM_DECIPERC;
+ else
+ chip->cl->dt.min_cap_limit = temp;
+
+ rc = of_property_read_u32(node, "qcom,cl-max-limit", &temp);
+ if (rc < 0)
+ chip->cl->dt.max_cap_limit =
+ DEFAULT_CL_MAX_LIM_DECIPERC;
+ else
+ chip->cl->dt.max_cap_limit = temp;
+
+ qg_dbg(chip, QG_DEBUG_PON, "DT: cl_min_start_soc=%d cl_max_start_soc=%d cl_min_temp=%d cl_max_temp=%d\n",
+ chip->cl->dt.min_start_soc, chip->cl->dt.max_start_soc,
+ chip->cl->dt.min_temp, chip->cl->dt.max_temp);
+ }
qg_dbg(chip, QG_DEBUG_PON, "DT: vbatt_empty_mv=%dmV vbatt_low_mv=%dmV delta_soc=%d\n",
chip->dt.vbatt_empty_mv, chip->dt.vbatt_low_mv,
chip->dt.delta_soc);
@@ -2464,7 +2814,7 @@
static int qpnp_qg_probe(struct platform_device *pdev)
{
- int rc = 0, soc = 0;
+ int rc = 0, soc = 0, nom_cap_uah;
struct qpnp_qg *chip;
chip = devm_kzalloc(&pdev->dev, sizeof(*chip), GFP_KERNEL);
@@ -2497,6 +2847,14 @@
mutex_init(&chip->data_lock);
init_waitqueue_head(&chip->qg_wait_q);
chip->maint_soc = -EINVAL;
+ chip->batt_soc = INT_MIN;
+ chip->cc_soc = INT_MIN;
+
+ rc = qg_alg_init(chip);
+ if (rc < 0) {
+ pr_err("Error in alg_init, rc:%d\n", rc);
+ return rc;
+ }
rc = qg_parse_dt(chip);
if (rc < 0) {
@@ -2534,6 +2892,30 @@
return rc;
}
+ if (chip->profile_loaded) {
+ if (!chip->dt.cl_disable) {
+ /*
+ * Use FCC @ 25 C and charge-profile for
+ * Nominal Capacity
+ */
+ rc = qg_get_nominal_capacity(&nom_cap_uah, 250, true);
+ if (!rc) {
+ rc = cap_learning_post_profile_init(chip->cl,
+ nom_cap_uah);
+ if (rc < 0) {
+ pr_err("Error in cap_learning_post_profile_init rc=%d\n",
+ rc);
+ return rc;
+ }
+ }
+ }
+ rc = restore_cycle_count(chip->counter);
+ if (rc < 0) {
+ pr_err("Error in restoring cycle_count, rc=%d\n", rc);
+ return rc;
+ }
+ }
+
rc = qg_determine_pon_soc(chip);
if (rc < 0) {
pr_err("Failed to determine initial state, rc=%d\n", rc);
@@ -2627,6 +3009,22 @@
return 0;
}
+static void qpnp_qg_shutdown(struct platform_device *pdev)
+{
+ struct qpnp_qg *chip = platform_get_drvdata(pdev);
+
+ if (!is_usb_present(chip) || !chip->profile_loaded)
+ return;
+ /*
+ * Charging status doesn't matter when the device shuts down and we
+ * have to treat this as charge done. Hence pass charge_done as true.
+ */
+ cycle_count_update(chip->counter,
+ DIV_ROUND_CLOSEST(chip->msoc * 255, 100),
+ POWER_SUPPLY_STATUS_NOT_CHARGING,
+ true, chip->usb_present);
+}
+
static const struct of_device_id match_table[] = {
{ .compatible = "qcom,qpnp-qg", },
{ },
@@ -2641,6 +3039,7 @@
},
.probe = qpnp_qg_probe,
.remove = qpnp_qg_remove,
+ .shutdown = qpnp_qg_shutdown,
};
module_platform_driver(qpnp_qg_driver);
diff --git a/drivers/power/supply/qcom/qpnp-smb5.c b/drivers/power/supply/qcom/qpnp-smb5.c
index d3aaf26..462e55f 100644
--- a/drivers/power/supply/qcom/qpnp-smb5.c
+++ b/drivers/power/supply/qcom/qpnp-smb5.c
@@ -52,6 +52,13 @@
.max_u = 3000000,
.step_u = 50000,
},
+ .icl_max_stat = {
+ .name = "dcdc icl max status",
+ .reg = ICL_MAX_STATUS_REG,
+ .min_u = 0,
+ .max_u = 3000000,
+ .step_u = 50000,
+ },
.icl_stat = {
.name = "input current limit status",
.reg = AICL_ICL_STATUS_REG,
@@ -90,7 +97,7 @@
},
};
-static struct smb_params smb5_pmi855_params = {
+static struct smb_params smb5_pm855b_params = {
.fcc = {
.name = "fast charge current",
.reg = CHGR_FAST_CHARGE_CURRENT_CFG_REG,
@@ -112,8 +119,15 @@
.max_u = 5000000,
.step_u = 50000,
},
+ .icl_max_stat = {
+ .name = "dcdc icl max status",
+ .reg = ICL_MAX_STATUS_REG,
+ .min_u = 0,
+ .max_u = 5000000,
+ .step_u = 50000,
+ },
.icl_stat = {
- .name = "input current limit status",
+ .name = "aicl icl status",
.reg = AICL_ICL_STATUS_REG,
.min_u = 0,
.max_u = 5000000,
@@ -177,6 +191,11 @@
debug_mask, __debug_mask, int, 0600
);
+static int __pd_disabled;
+module_param_named(
+ pd_disabled, __pd_disabled, int, 0600
+);
+
static int __weak_chg_icl_ua = 500000;
module_param_named(
weak_chg_icl_ua, __weak_chg_icl_ua, int, 0600
@@ -216,7 +235,7 @@
switch (pmic_rev_id->pmic_subtype) {
case PM855B_SUBTYPE:
chip->chg.smb_version = PM855B_SUBTYPE;
- chg->param = smb5_pmi855_params;
+ chg->param = smb5_pm855b_params;
chg->name = "pm855b_charger";
break;
case PMI632_SUBTYPE:
@@ -434,7 +453,6 @@
POWER_SUPPLY_PROP_TYPEC_MODE,
POWER_SUPPLY_PROP_TYPEC_POWER_ROLE,
POWER_SUPPLY_PROP_TYPEC_CC_ORIENTATION,
- POWER_SUPPLY_PROP_PD_ALLOWED,
POWER_SUPPLY_PROP_PD_ACTIVE,
POWER_SUPPLY_PROP_INPUT_CURRENT_SETTLED,
POWER_SUPPLY_PROP_INPUT_CURRENT_NOW,
@@ -515,9 +533,6 @@
else
rc = smblib_get_prop_typec_cc_orientation(chg, val);
break;
- case POWER_SUPPLY_PROP_PD_ALLOWED:
- rc = smblib_get_prop_pd_allowed(chg, val);
- break;
case POWER_SUPPLY_PROP_PD_ACTIVE:
val->intval = chg->pd_active;
break;
@@ -605,12 +620,6 @@
struct smb_charger *chg = &chip->chg;
int rc = 0;
- mutex_lock(&chg->lock);
- if (!chg->typec_present) {
- rc = -EINVAL;
- goto unlock;
- }
-
switch (psp) {
case POWER_SUPPLY_PROP_PD_CURRENT_MAX:
rc = smblib_set_prop_pd_current_max(chg, val);
@@ -652,8 +661,6 @@
break;
}
-unlock:
- mutex_unlock(&chg->lock);
return rc;
}
@@ -1425,6 +1432,15 @@
{
int rc;
+ /* disable apsd */
+ rc = smblib_masked_write(chg, USBIN_OPTIONS_1_CFG_REG,
+ HVDCP_EN_BIT | BC1P2_SRC_DETECT_BIT,
+ 0);
+ if (rc < 0) {
+ dev_err(chg->dev, "Couldn't disable APSD rc=%d\n", rc);
+ return rc;
+ }
+
rc = smblib_write(chg, TYPE_C_INTERRUPT_EN_CFG_1_REG,
TYPEC_CCOUT_DETACH_INT_EN_BIT |
TYPEC_CCOUT_ATTACH_INT_EN_BIT);
@@ -1459,14 +1475,6 @@
{
int rc;
- /* configure micro USB mode */
- rc = smblib_masked_write(chg, TYPEC_U_USB_CFG_REG,
- EN_MICRO_USB_MODE_BIT, EN_MICRO_USB_MODE_BIT);
- if (rc < 0) {
- dev_err(chg->dev, "Couldn't enable micro USB mode rc=%d\n", rc);
- return rc;
- }
-
rc = smblib_masked_write(chg, TYPE_C_INTERRUPT_EN_CFG_2_REG,
MICRO_USB_STATE_CHANGE_INT_EN_BIT,
MICRO_USB_STATE_CHANGE_INT_EN_BIT);
@@ -1500,7 +1508,6 @@
&chg->default_icl_ua);
/* Use SW based VBUS control, disable HW autonomous mode */
- /* TODO: auth can be enabled through vote based on APSD flow */
rc = smblib_masked_write(chg, USBIN_OPTIONS_1_CFG_REG,
HVDCP_AUTH_ALG_EN_CFG_BIT | HVDCP_AUTONOMOUS_MODE_EN_CFG_BIT,
HVDCP_AUTH_ALG_EN_CFG_BIT);
@@ -1537,10 +1544,22 @@
type = !!(val & EN_MICRO_USB_MODE_BIT);
}
- chg->connector_type = type ? POWER_SUPPLY_CONNECTOR_MICRO_USB
- : POWER_SUPPLY_CONNECTOR_TYPEC;
pr_debug("Connector type=%s\n", type ? "Micro USB" : "TypeC");
+ if (type) {
+ chg->connector_type = POWER_SUPPLY_CONNECTOR_MICRO_USB;
+ smblib_rerun_apsd_if_required(chg);
+ rc = smb5_configure_micro_usb(chg);
+ } else {
+ chg->connector_type = POWER_SUPPLY_CONNECTOR_TYPEC;
+ rc = smb5_configure_typec(chg);
+ }
+ if (rc < 0) {
+ dev_err(chg->dev,
+ "Couldn't configure TypeC/micro-USB mode rc=%d\n", rc);
+ return rc;
+ }
+
/*
* PMI632 based hw init:
* - Initialize flash module for PMI632
@@ -1548,8 +1567,6 @@
if (chg->smb_version == PMI632_SUBTYPE)
schgm_flash_init(chg);
- smblib_rerun_apsd_if_required(chg);
-
/* vote 0mA on usb_icl for non battery platforms */
vote(chg->usb_icl_votable,
DEFAULT_VOTER, chip->dt.no_battery, 0);
@@ -1565,12 +1582,6 @@
vote(chg->fv_votable,
BATT_PROFILE_VOTER, chg->batt_profile_fv_uv > 0,
chg->batt_profile_fv_uv);
- vote(chg->pd_disallowed_votable_indirect, CC_DETACHED_VOTER,
- true, 0);
- vote(chg->pd_disallowed_votable_indirect, APSD_VOTER,
- true, 0);
- vote(chg->pd_disallowed_votable_indirect, MICRO_USB_VOTER,
- chg->connector_type == POWER_SUPPLY_CONNECTOR_MICRO_USB, 0);
/* Some h/w limit maximum supported ICL */
vote(chg->usb_icl_votable, HW_LIMIT_VOTER,
@@ -1594,16 +1605,6 @@
return rc;
}
- if (chg->connector_type == POWER_SUPPLY_CONNECTOR_MICRO_USB)
- rc = smb5_configure_micro_usb(chg);
- else
- rc = smb5_configure_typec(chg);
- if (rc < 0) {
- dev_err(chg->dev,
- "Couldn't configure TypeC/micro-USB mode rc=%d\n", rc);
- return rc;
- }
-
/* configure VBUS for software control */
rc = smblib_masked_write(chg, DCDC_OTG_CFG_REG, OTG_EN_SRC_CFG_BIT, 0);
if (rc < 0) {
@@ -1812,11 +1813,21 @@
{
struct smb_irq_data irq_data = {chip, "determine-initial-status"};
struct smb_charger *chg = &chip->chg;
+ union power_supply_propval val;
+ int rc;
+
+ rc = smblib_get_prop_usb_present(chg, &val);
+ if (rc < 0) {
+ pr_err("Couldn't get usb present rc=%d\n", rc);
+ return rc;
+ }
+ chg->early_usb_attach = val.intval;
if (chg->bms_psy)
smblib_suspend_on_debug_battery(chg);
usb_plugin_irq_handler(0, &irq_data);
+ typec_attach_detach_irq_handler(0, &irq_data);
typec_state_change_irq_handler(0, &irq_data);
usb_source_change_irq_handler(0, &irq_data);
chg_state_change_irq_handler(0, &irq_data);
@@ -2008,6 +2019,7 @@
},
[TYPEC_ATTACH_DETACH_IRQ] = {
.name = "typec-attach-detach",
+ .handler = typec_attach_detach_irq_handler,
},
[TYPEC_LEGACY_CABLE_DETECT_IRQ] = {
.name = "typec-legacy-cable-detect",
@@ -2303,6 +2315,7 @@
chg = &chip->chg;
chg->dev = &pdev->dev;
chg->debug_mask = &__debug_mask;
+ chg->pd_disabled = &__pd_disabled;
chg->weak_chg_icl_ua = &__weak_chg_icl_ua;
chg->mode = PARALLEL_MASTER;
chg->irq_info = smb5_irqs;
@@ -2452,6 +2465,10 @@
struct smb5 *chip = platform_get_drvdata(pdev);
struct smb_charger *chg = &chip->chg;
+ /* force enable APSD */
+ smblib_masked_write(chg, USBIN_OPTIONS_1_CFG_REG,
+ BC1P2_SRC_DETECT_BIT, BC1P2_SRC_DETECT_BIT);
+
smb5_free_interrupts(chg);
smblib_deinit(chg);
platform_set_drvdata(pdev, NULL);
diff --git a/drivers/power/supply/qcom/qpnp-smbcharger.c b/drivers/power/supply/qcom/qpnp-smbcharger.c
index 9b5c25f..3bb4810 100644
--- a/drivers/power/supply/qcom/qpnp-smbcharger.c
+++ b/drivers/power/supply/qcom/qpnp-smbcharger.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2014-2016 The Linux Foundation. All rights reserved.
+/* Copyright (c) 2014-2016, 2018 The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -8488,6 +8488,8 @@
out:
handle_usb_removal(chip);
votables_cleanup:
+ if (chip->hvdcp_enable_votable)
+ destroy_votable(chip->hvdcp_enable_votable);
if (chip->aicl_deglitch_short_votable)
destroy_votable(chip->aicl_deglitch_short_votable);
if (chip->hw_aicl_rerun_enable_indirect_votable)
diff --git a/drivers/power/supply/qcom/smb5-lib.c b/drivers/power/supply/qcom/smb5-lib.c
index 8756186..9d7bb83 100644
--- a/drivers/power/supply/qcom/smb5-lib.c
+++ b/drivers/power/supply/qcom/smb5-lib.c
@@ -39,6 +39,10 @@
__func__, ##__VA_ARGS__); \
} while (0)
+#define typec_rp_med_high(chg, typec_mode) \
+ ((typec_mode == POWER_SUPPLY_TYPEC_SOURCE_MEDIUM \
+ || typec_mode == POWER_SUPPLY_TYPEC_SOURCE_HIGH) \
+ && !chg->typec_legacy)
int smblib_read(struct smb_charger *chg, u16 addr, u8 *val)
{
@@ -654,7 +658,7 @@
return 0;
}
-#define USBIN_100MA 100000
+#define SDP_100_MA 100000
static void smblib_uusb_removal(struct smb_charger *chg)
{
int rc;
@@ -663,10 +667,6 @@
cancel_delayed_work_sync(&chg->pl_enable_work);
- rc = smblib_request_dpdm(chg, false);
- if (rc < 0)
- smblib_err(chg, "Couldn't to disable DPDM rc=%d\n", rc);
-
if (chg->wa_flags & BOOST_BACK_WA) {
data = chg->irq_info[SWITCHER_POWER_OK_IRQ].irq_data;
if (data) {
@@ -683,6 +683,7 @@
/* reset both usbin current and voltage votes */
vote(chg->pl_enable_votable_indirect, USBIN_I_VOTER, false, 0);
vote(chg->pl_enable_votable_indirect, USBIN_V_VOTER, false, 0);
+ vote(chg->usb_icl_votable, SW_ICL_MAX_VOTER, true, SDP_100_MA);
vote(chg->usb_icl_votable, SW_QC3_VOTER, false, 0);
/* reconfigure allowed voltage for HVDCP */
@@ -705,8 +706,6 @@
smblib_err(chg, "Couldn't write float charger options rc=%d\n",
rc);
- /* leave the ICL configured to 100mA for next insertion */
- vote(chg->usb_icl_votable, DEFAULT_100MA_VOTER, true, USBIN_100MA);
/* clear USB ICL vote for USB_PSY_VOTER */
rc = vote(chg->usb_icl_votable, USB_PSY_VOTER, false, 0);
if (rc < 0)
@@ -772,6 +771,7 @@
}
#define USBIN_25MA 25000
+#define USBIN_100MA 100000
#define USBIN_150MA 150000
#define USBIN_500MA 500000
#define USBIN_900MA 900000
@@ -885,6 +885,10 @@
set_mode:
rc = smblib_masked_write(chg, USBIN_ICL_OPTIONS_REG,
USBIN_MODE_CHG_BIT, hc_mode ? USBIN_MODE_CHG_BIT : 0);
+ if (rc < 0) {
+ smblib_err(chg, "Couldn't set USBIN_ICL_OPTIONS rc=%d\n", rc);
+ goto out;
+ }
/* unsuspend after configuring current and override */
rc = smblib_set_usb_suspend(chg, false);
@@ -925,7 +929,8 @@
return INT_MAX;
/* override is set */
- rc = smblib_get_charge_param(chg, &chg->param.usb_icl, icl_ua);
+ rc = smblib_get_charge_param(chg, &chg->param.icl_max_stat,
+ icl_ua);
if (rc < 0) {
smblib_err(chg, "Couldn't get HC ICL rc=%d\n", rc);
return rc;
@@ -954,18 +959,6 @@
return smblib_set_dc_suspend(chg, (bool)suspend);
}
-static int smblib_pd_disallowed_votable_indirect_callback(
- struct votable *votable, void *data, int disallowed, const char *client)
-{
- struct smb_charger *chg = data;
- int rc;
-
- rc = vote(chg->pd_allowed_votable, PD_DISALLOWED_INDIRECT_VOTER,
- !disallowed, 0);
-
- return rc;
-}
-
static int smblib_awake_vote_callback(struct votable *votable, void *data,
int awake, const char *client)
{
@@ -1989,13 +1982,6 @@
return rc;
}
-int smblib_get_prop_pd_allowed(struct smb_charger *chg,
- union power_supply_propval *val)
-{
- val->intval = get_effective_result(chg->pd_allowed_votable);
- return 0;
-}
-
int smblib_get_prop_input_current_settled(struct smb_charger *chg,
union power_supply_propval *val)
{
@@ -2039,13 +2025,7 @@
int smblib_get_pe_start(struct smb_charger *chg,
union power_supply_propval *val)
{
- /*
- * hvdcp timeout voter is the last one to allow pd. Use its vote
- * to indicate start of pe engine
- */
- val->intval
- = !get_client_vote_locked(chg->pd_disallowed_votable_indirect,
- APSD_VOTER);
+ val->intval = chg->ok_to_pd;
return 0;
}
@@ -2132,24 +2112,40 @@
if (chg->real_charger_type == POWER_SUPPLY_TYPE_USB_FLOAT) {
if (usb_current == -ETIMEDOUT) {
- /*
- * Valid FLOAT charger, report the current based
- * of Rp
- */
+ if ((chg->float_cfg & FLOAT_OPTIONS_MASK)
+ == FORCE_FLOAT_SDP_CFG_BIT) {
+ /*
+ * Confiugure USB500 mode if Float charger is
+ * configured for SDP.
+ */
+ rc = set_sdp_current(chg, USBIN_500MA);
+ if (rc < 0)
+ smblib_err(chg,
+ "Couldn't set SDP ICL rc=%d\n",
+ rc);
+
+ return rc;
+ }
+
if (chg->connector_type ==
POWER_SUPPLY_CONNECTOR_TYPEC) {
+ /*
+ * Valid FLOAT charger, report the current
+ * based of Rp.
+ */
typec_mode = smblib_get_prop_typec_mode(chg);
rp_ua = get_rp_based_dcp_current(chg,
typec_mode);
rc = vote(chg->usb_icl_votable,
- DYNAMIC_RP_VOTER, true, rp_ua);
- if (rc < 0) {
- pr_err("Couldn't vote ICL DYNAMIC_RP_VOTER rc=%d\n",
- rc);
+ SW_ICL_MAX_VOTER, true, rp_ua);
+ if (rc < 0)
return rc;
- }
+ } else {
+ rc = vote(chg->usb_icl_votable,
+ SW_ICL_MAX_VOTER, true, DCP_CURRENT_UA);
+ if (rc < 0)
+ return rc;
}
- /* No specific handling required for micro-USB */
} else {
/*
* FLOAT charger detected as SDP by USB driver,
@@ -2158,12 +2154,13 @@
*/
chg->real_charger_type = POWER_SUPPLY_TYPE_USB;
rc = vote(chg->usb_icl_votable, USB_PSY_VOTER,
- true, usb_current);
- if (rc < 0) {
- pr_err("Couldn't vote ICL USB_PSY_VOTER rc=%d\n",
- rc);
+ true, usb_current);
+ if (rc < 0)
return rc;
- }
+ rc = vote(chg->usb_icl_votable, SW_ICL_MAX_VOTER,
+ false, 0);
+ if (rc < 0)
+ return rc;
}
} else {
rc = vote(chg->usb_icl_votable, USB_PSY_VOTER,
@@ -2173,12 +2170,12 @@
return rc;
}
- }
+ rc = vote(chg->usb_icl_votable, SW_ICL_MAX_VOTER, false, 0);
+ if (rc < 0) {
+ pr_err("Couldn't remove SW_ICL_MAX vote rc=%d\n", rc);
+ return rc;
+ }
- rc = vote(chg->usb_icl_votable, DEFAULT_100MA_VOTER, false, 0);
- if (rc < 0) {
- pr_err("Couldn't unvote ICL DEFAULT_100MA_VOTER rc=%d\n", rc);
- return rc;
}
return 0;
@@ -2298,14 +2295,14 @@
return rc;
}
-static int __smblib_set_prop_pd_active(struct smb_charger *chg, bool pd_active)
+int smblib_set_prop_pd_active(struct smb_charger *chg,
+ const union power_supply_propval *val)
{
int rc = 0;
- chg->pd_active = pd_active;
+ chg->pd_active = val->intval;
if (chg->pd_active) {
- vote(chg->pd_allowed_votable, PD_VOTER, true, 0);
vote(chg->usb_irq_enable_votable, PD_VOTER, true, 0);
/*
@@ -2313,24 +2310,26 @@
* It is guaranteed that pd_active is set prior to
* pd_current_max
*/
- rc = vote(chg->usb_icl_votable, PD_VOTER, true, USBIN_500MA);
- if (rc < 0)
- smblib_err(chg, "Couldn't vote for USB ICL rc=%d\n",
- rc);
-
- /* clear USB ICL vote for DCP_VOTER */
- rc = vote(chg->usb_icl_votable, DCP_VOTER, false, 0);
- if (rc < 0)
- smblib_err(chg, "Couldn't un-vote DCP from USB ICL rc=%d\n",
- rc);
-
- /* remove USB_PSY_VOTER */
- rc = vote(chg->usb_icl_votable, USB_PSY_VOTER, false, 0);
- if (rc < 0)
- smblib_err(chg, "Couldn't unvote USB_PSY rc=%d\n", rc);
+ vote(chg->usb_icl_votable, PD_VOTER, true, USBIN_500MA);
+ vote(chg->usb_icl_votable, SW_ICL_MAX_VOTER, false, 0);
} else {
- vote(chg->pd_allowed_votable, PD_VOTER, true, 0);
+ vote(chg->usb_icl_votable, SW_ICL_MAX_VOTER, true, SDP_100_MA);
+ vote(chg->usb_icl_votable, PD_VOTER, false, 0);
vote(chg->usb_irq_enable_votable, PD_VOTER, false, 0);
+
+ /* PD hard resets failed, rerun apsd */
+ if (chg->ok_to_pd) {
+ chg->ok_to_pd = false;
+ rc = smblib_masked_write(chg, USBIN_OPTIONS_1_CFG_REG,
+ HVDCP_EN_BIT | BC1P2_SRC_DETECT_BIT,
+ HVDCP_EN_BIT | BC1P2_SRC_DETECT_BIT);
+ if (rc < 0) {
+ dev_err(chg->dev,
+ "Couldn't disable APSD rc=%d\n", rc);
+ return rc;
+ }
+ smblib_rerun_apsd(chg);
+ }
}
smblib_update_usb_type(chg);
@@ -2338,15 +2337,6 @@
return rc;
}
-int smblib_set_prop_pd_active(struct smb_charger *chg,
- const union power_supply_propval *val)
-{
- if (!get_effective_result(chg->pd_allowed_votable))
- return -EINVAL;
-
- return __smblib_set_prop_pd_active(chg, val->intval);
-}
-
int smblib_set_prop_ship_mode(struct smb_charger *chg,
const union power_supply_propval *val)
{
@@ -2657,11 +2647,7 @@
static void smblib_micro_usb_plugin(struct smb_charger *chg, bool vbus_rising)
{
- if (vbus_rising) {
- /* use the typec flag even though its not typec */
- chg->typec_present = 1;
- } else {
- chg->typec_present = 0;
+ if (!vbus_rising) {
smblib_update_usb_type(chg);
smblib_notify_device_mode(chg, false);
smblib_uusb_removal(chg);
@@ -2764,12 +2750,10 @@
struct smb_irq_data *irq_data = data;
struct smb_charger *chg = irq_data->parent_data;
- mutex_lock(&chg->lock);
if (chg->pd_hard_reset)
smblib_usb_plugin_hard_reset_locked(chg);
else
smblib_usb_plugin_locked(chg);
- mutex_unlock(&chg->lock);
return IRQ_HANDLED;
}
@@ -2858,8 +2842,6 @@
if (!rising)
return;
- vote(chg->pd_disallowed_votable_indirect, APSD_VOTER, false, 0);
-
if (chg->mode == PARALLEL_MASTER)
vote(chg->pl_enable_votable_indirect, USBIN_V_VOTER, true, 0);
@@ -2872,33 +2854,18 @@
static void smblib_handle_hvdcp_check_timeout(struct smb_charger *chg,
bool rising, bool qc_charger)
{
- const struct apsd_result *apsd_result = smblib_get_apsd_result(chg);
-
- /* Hold off PD only until hvdcp 2.0 detection timeout */
if (rising) {
- /* enable HDC and ICL irq for QC2/3 charger */
- if (qc_charger)
+ if (qc_charger) {
+ /* enable HDC and ICL irq for QC2/3 charger */
vote(chg->usb_irq_enable_votable, QC_VOTER, true, 0);
- else
- vote(chg->pd_disallowed_votable_indirect, APSD_VOTER,
- false, 0);
-
- /*
- * HVDCP detection timeout done
- * If adapter is not QC2.0/QC3.0 - it is a plain old DCP.
- */
- if (!qc_charger && (apsd_result->bit & DCP_CHARGER_BIT))
- /* enforce DCP ICL if specified */
+ vote(chg->usb_icl_votable, SW_ICL_MAX_VOTER, true,
+ HVDCP_CURRENT_UA);
+ } else {
+ /* A plain DCP, enforce DCP ICL if specified */
vote(chg->usb_icl_votable, DCP_VOTER,
chg->dcp_icl_ua != -EINVAL, chg->dcp_icl_ua);
-
- /*
- * if pd is not allowed, then set pd_active = false right here,
- * so that it starts the hvdcp engine
- */
- if (!get_effective_result(chg->pd_allowed_votable))
- __smblib_set_prop_pd_active(chg, 0);
+ }
}
smblib_dbg(chg, PR_INTERRUPT, "IRQ: %s %s\n", __func__,
@@ -2913,6 +2880,69 @@
rising ? "rising" : "falling");
}
+static void update_sw_icl_max(struct smb_charger *chg, int pst)
+{
+ int typec_mode;
+ int rp_ua;
+
+ /* while PD is active it should have complete ICL control */
+ if (chg->pd_active)
+ return;
+
+ /*
+ * HVDCP 2/3, handled separately
+ * For UNKNOWN(input not present) return without updating ICL
+ */
+ if (pst == POWER_SUPPLY_TYPE_USB_HVDCP
+ || pst == POWER_SUPPLY_TYPE_USB_HVDCP_3
+ || pst == POWER_SUPPLY_TYPE_UNKNOWN)
+ return;
+
+ /* TypeC rp med or high, use rp value */
+ typec_mode = smblib_get_prop_typec_mode(chg);
+ if (typec_rp_med_high(chg, typec_mode)) {
+ rp_ua = get_rp_based_dcp_current(chg, typec_mode);
+ vote(chg->usb_icl_votable, SW_ICL_MAX_VOTER, true, rp_ua);
+ return;
+ }
+
+ /* rp-std or legacy, USB BC 1.2 */
+ switch (pst) {
+ case POWER_SUPPLY_TYPE_USB:
+ /*
+ * USB_PSY will vote to increase the current to 500/900mA once
+ * enumeration is done.
+ */
+ if (!is_client_vote_enabled(chg->usb_icl_votable,
+ USB_PSY_VOTER))
+ vote(chg->usb_icl_votable, USB_PSY_VOTER, true,
+ SDP_100_MA);
+ vote(chg->usb_icl_votable, SW_ICL_MAX_VOTER, false, 0);
+ break;
+ case POWER_SUPPLY_TYPE_USB_CDP:
+ vote(chg->usb_icl_votable, SW_ICL_MAX_VOTER, true,
+ CDP_CURRENT_UA);
+ break;
+ case POWER_SUPPLY_TYPE_USB_DCP:
+ vote(chg->usb_icl_votable, SW_ICL_MAX_VOTER, true,
+ DCP_CURRENT_UA);
+ break;
+ case POWER_SUPPLY_TYPE_USB_FLOAT:
+ /*
+ * limit ICL to 100mA, the USB driver will enumerate to check
+ * if this is a SDP and appropriately set the current
+ */
+ vote(chg->usb_icl_votable, SW_ICL_MAX_VOTER, true,
+ SDP_100_MA);
+ break;
+ default:
+ smblib_err(chg, "Unknown APSD %d; forcing 500mA\n", pst);
+ vote(chg->usb_icl_votable, SW_ICL_MAX_VOTER, true,
+ SDP_CURRENT_UA);
+ break;
+ }
+}
+
static void smblib_handle_apsd_done(struct smb_charger *chg, bool rising)
{
const struct apsd_result *apsd_result;
@@ -2922,25 +2952,18 @@
apsd_result = smblib_update_usb_type(chg);
+ update_sw_icl_max(chg, apsd_result->pst);
+
switch (apsd_result->bit) {
case SDP_CHARGER_BIT:
case CDP_CHARGER_BIT:
case FLOAT_CHARGER_BIT:
- /* if not DCP then no hvdcp timeout happens. Enable pd here */
- vote(chg->pd_disallowed_votable_indirect, APSD_VOTER,
- false, 0);
if ((chg->connector_type == POWER_SUPPLY_CONNECTOR_MICRO_USB)
|| chg->use_extcon)
smblib_notify_device_mode(chg, true);
break;
case OCP_CHARGER_BIT:
- vote(chg->usb_icl_votable, DEFAULT_100MA_VOTER, false, 0);
- /* if not DCP then no hvdcp timeout happens, Enable pd here. */
- vote(chg->pd_disallowed_votable_indirect, APSD_VOTER,
- false, 0);
- break;
case DCP_CHARGER_BIT:
- vote(chg->usb_icl_votable, DEFAULT_100MA_VOTER, false, 0);
break;
default:
break;
@@ -2957,6 +2980,14 @@
int rc = 0;
u8 stat;
+ /*
+ * Prepared to run PD or PD is active. At this moment, APSD is disabled,
+ * but there still can be irq on apsd_done from previously unfinished
+ * APSD run, skip it.
+ */
+ if (chg->ok_to_pd)
+ return IRQ_HANDLED;
+
rc = smblib_read(chg, APSD_STATUS_REG, &stat);
if (rc < 0) {
smblib_err(chg, "Couldn't read APSD_STATUS rc=%d\n", rc);
@@ -3011,32 +3042,68 @@
static void typec_sink_insertion(struct smb_charger *chg)
{
- /* when a sink is inserted we should not wait on hvdcp timeout to
- * enable pd
- */
- vote(chg->pd_disallowed_votable_indirect, APSD_VOTER, false, 0);
+ vote(chg->usb_icl_votable, OTG_VOTER, true, 0);
+
if (chg->use_extcon) {
smblib_notify_usb_host(chg, true);
chg->otg_present = true;
}
+
+ if (!chg->pr_swap_in_progress)
+ chg->ok_to_pd = !(*chg->pd_disabled) || chg->early_usb_attach;
+}
+
+static void typec_src_insertion(struct smb_charger *chg)
+{
+ int rc = 0;
+ u8 stat;
+
+ if (chg->pr_swap_in_progress)
+ return;
+
+ rc = smblib_read(chg, LEGACY_CABLE_STATUS_REG, &stat);
+ if (rc < 0) {
+ smblib_err(chg, "Couldn't read TYPE_C_STATE_MACHINE_STATUS_REG rc=%d\n",
+ rc);
+ return;
+ }
+
+ chg->typec_legacy = stat & TYPEC_LEGACY_CABLE_STATUS_BIT;
+ chg->ok_to_pd = !(chg->typec_legacy || *chg->pd_disabled)
+ || chg->early_usb_attach;
+ if (!chg->ok_to_pd) {
+ rc = smblib_masked_write(chg, USBIN_OPTIONS_1_CFG_REG,
+ HVDCP_EN_BIT | BC1P2_SRC_DETECT_BIT,
+ HVDCP_EN_BIT | BC1P2_SRC_DETECT_BIT);
+ if (rc < 0) {
+ dev_err(chg->dev,
+ "Couldn't disable APSD rc=%d\n", rc);
+ return;
+ }
+ smblib_rerun_apsd(chg);
+ }
}
static void typec_sink_removal(struct smb_charger *chg)
{
- smblib_set_charge_param(chg, &chg->param.freq_switcher,
- chg->chg_freq.freq_above_otg_threshold);
- chg->boost_current_ua = 0;
+ vote(chg->usb_icl_votable, OTG_VOTER, false, 0);
}
-static void smblib_handle_typec_removal(struct smb_charger *chg)
+static void typec_src_removal(struct smb_charger *chg)
{
int rc;
struct smb_irq_data *data;
struct storm_watch *wdata;
- rc = smblib_request_dpdm(chg, false);
+ /* disable apsd */
+ rc = smblib_masked_write(chg, USBIN_OPTIONS_1_CFG_REG,
+ HVDCP_EN_BIT | BC1P2_SRC_DETECT_BIT,
+ 0);
if (rc < 0)
- smblib_err(chg, "Couldn't disable DPDM rc=%d\n", rc);
+ smblib_err(chg,
+ "Couldn't disable APSD rc=%d\n", rc);
+
+ smblib_update_usb_type(chg);
if (chg->wa_flags & BOOST_BACK_WA) {
data = chg->irq_info[SWITCHER_POWER_OK_IRQ].irq_data;
@@ -3052,7 +3119,7 @@
cancel_delayed_work_sync(&chg->pl_enable_work);
/* reset input current limit voters */
- vote(chg->usb_icl_votable, DEFAULT_100MA_VOTER, true, USBIN_100MA);
+ vote(chg->usb_icl_votable, SW_ICL_MAX_VOTER, true, SDP_100_MA);
vote(chg->usb_icl_votable, PD_VOTER, false, 0);
vote(chg->usb_icl_votable, USB_PSY_VOTER, false, 0);
vote(chg->usb_icl_votable, DCP_VOTER, false, 0);
@@ -3060,12 +3127,6 @@
vote(chg->usb_icl_votable, SW_QC3_VOTER, false, 0);
vote(chg->usb_icl_votable, OTG_VOTER, false, 0);
vote(chg->usb_icl_votable, CTM_VOTER, false, 0);
- vote(chg->usb_icl_votable, DYNAMIC_RP_VOTER, false, 0);
-
- /* reset power delivery voters */
- vote(chg->pd_allowed_votable, PD_VOTER, false, 0);
- vote(chg->pd_disallowed_votable_indirect, CC_DETACHED_VOTER, true, 0);
- vote(chg->pd_disallowed_votable_indirect, APSD_VOTER, true, 0);
/* reset usb irq voters */
vote(chg->usb_irq_enable_votable, PD_VOTER, false, 0);
@@ -3078,14 +3139,10 @@
vote(chg->pl_enable_votable_indirect, USBIN_V_VOTER, false, 0);
vote(chg->awake_votable, PL_DELAY_VOTER, false, 0);
- chg->vconn_attempts = 0;
- chg->otg_attempts = 0;
chg->pulse_cnt = 0;
chg->usb_icl_delta_ua = 0;
chg->voltage_min_uv = MICRO_5V;
chg->voltage_max_uv = MICRO_5V;
- chg->pd_active = 0;
- chg->pd_hard_reset = 0;
/* write back the default FLOAT charger configuration */
rc = smblib_masked_write(chg, USBIN_OPTIONS_2_CFG_REG,
@@ -3094,12 +3151,6 @@
smblib_err(chg, "Couldn't write float charger options rc=%d\n",
rc);
- /* reset back to 103mS tCC debounce */
- rc = smblib_masked_write(chg, TYPE_C_DEBOUNCE_OPTION_REG,
- REDUCE_TCCDEBOUNCE_TO_2MS_BIT, 0);
- if (rc < 0)
- smblib_err(chg, "Couldn't set 120mS tCC debounce rc=%d\n", rc);
-
/* reconfigure allowed voltage for HVDCP */
rc = smblib_set_adapter_allowance(chg,
USBIN_ADAPTER_ALLOW_5V_OR_9V_TO_12V);
@@ -3107,157 +3158,40 @@
smblib_err(chg, "Couldn't set USBIN_ADAPTER_ALLOW_5V_OR_9V_TO_12V rc=%d\n",
rc);
- /* enable DRP */
- rc = smblib_masked_write(chg, TYPE_C_MODE_CFG_REG,
- TYPEC_POWER_ROLE_CMD_MASK, 0);
- if (rc < 0)
- smblib_err(chg, "Couldn't enable DRP rc=%d\n", rc);
-
- /* HW controlled CC_OUT */
- rc = smblib_masked_write(chg, TYPE_C_CCOUT_CONTROL_REG,
- TYPEC_CCOUT_SRC_BIT, 0);
- if (rc < 0)
- smblib_err(chg, "Couldn't enable HW cc_out rc=%d\n", rc);
-
- /* clear exit sink based on cc */
- rc = smblib_masked_write(chg, TYPE_C_EXIT_STATE_CFG_REG,
- EXIT_SNK_BASED_ON_CC_BIT, 0);
- if (rc < 0)
- smblib_err(chg, "Couldn't clear exit_sink_based_on_cc rc=%d\n",
- rc);
-
- typec_sink_removal(chg);
- smblib_update_usb_type(chg);
-
if (chg->use_extcon) {
if (chg->otg_present)
smblib_notify_usb_host(chg, false);
else
smblib_notify_device_mode(chg, false);
}
+
chg->otg_present = false;
-}
-
-static void smblib_handle_typec_insertion(struct smb_charger *chg)
-{
- int rc;
- u8 stat;
-
- vote(chg->pd_disallowed_votable_indirect, CC_DETACHED_VOTER, false, 0);
-
- rc = smblib_read(chg, TYPE_C_MISC_STATUS_REG, &stat);
- if (rc < 0) {
- smblib_err(chg, "Couldn't read TYPE_C_STATUS_4 rc=%d\n", rc);
- return;
- }
-
- if (stat & SNK_SRC_MODE_BIT) {
- typec_sink_insertion(chg);
- } else {
- rc = smblib_request_dpdm(chg, true);
- if (rc < 0)
- smblib_err(chg, "Couldn't to enable DPDM rc=%d\n", rc);
- typec_sink_removal(chg);
- }
+ chg->typec_legacy = false;
}
static void smblib_handle_rp_change(struct smb_charger *chg, int typec_mode)
{
- int rp_ua;
const struct apsd_result *apsd = smblib_get_apsd_result(chg);
- if ((apsd->pst != POWER_SUPPLY_TYPE_USB_DCP)
- && (apsd->pst != POWER_SUPPLY_TYPE_USB_FLOAT))
- return;
-
- /*
- * if APSD indicates FLOAT and the USB stack had detected SDP,
- * do not respond to Rp changes as we do not confirm that its
- * a legacy cable
- */
- if (chg->real_charger_type == POWER_SUPPLY_TYPE_USB)
- return;
/*
* We want the ICL vote @ 100mA for a FLOAT charger
* until the detection by the USB stack is complete.
* Ignore the Rp changes unless there is a
- * pre-existing valid vote.
+ * pre-existing valid vote or FLOAT is configured for
+ * SDP current.
*/
- if (apsd->pst == POWER_SUPPLY_TYPE_USB_FLOAT &&
- (get_client_vote(chg->usb_icl_votable, DEFAULT_100MA_VOTER)
- <= USBIN_100MA))
+ if (apsd->pst == POWER_SUPPLY_TYPE_USB_FLOAT) {
+ if (get_client_vote(chg->usb_icl_votable, SW_ICL_MAX_VOTER)
+ <= USBIN_100MA
+ || (chg->float_cfg & FLOAT_OPTIONS_MASK)
+ == FORCE_FLOAT_SDP_CFG_BIT)
return;
+ }
- /*
- * handle Rp change for DCP/FLOAT/OCP.
- * Update the current only if the Rp is different from
- * the last Rp value.
- */
+ update_sw_icl_max(chg, apsd->pst);
+
smblib_dbg(chg, PR_MISC, "CC change old_mode=%d new_mode=%d\n",
chg->typec_mode, typec_mode);
-
- rp_ua = get_rp_based_dcp_current(chg, typec_mode);
- vote(chg->usb_icl_votable, DYNAMIC_RP_VOTER, true, rp_ua);
-}
-
-static void smblib_handle_typec_cc_state_change(struct smb_charger *chg)
-{
- u8 stat;
- int typec_mode, rc;
-
- if (chg->pr_swap_in_progress)
- return;
-
- typec_mode = smblib_get_prop_typec_mode(chg);
- if (chg->typec_present && (typec_mode != chg->typec_mode))
- smblib_handle_rp_change(chg, typec_mode);
-
- chg->typec_mode = typec_mode;
-
- if (!chg->typec_present && chg->typec_mode != POWER_SUPPLY_TYPEC_NONE) {
- chg->typec_present = true;
- smblib_dbg(chg, PR_MISC, "TypeC %s insertion\n",
- smblib_typec_mode_name[chg->typec_mode]);
- smblib_handle_typec_insertion(chg);
- } else if (chg->typec_present &&
- chg->typec_mode == POWER_SUPPLY_TYPEC_NONE) {
- chg->typec_present = false;
- smblib_dbg(chg, PR_MISC, "TypeC removal\n");
- smblib_handle_typec_removal(chg);
- }
-
- rc = smblib_read(chg, TYPE_C_MISC_STATUS_REG, &stat);
- if (rc < 0) {
- smblib_err(chg, "Couldn't read TYPE_C_STATUS_4 rc=%d\n", rc);
- return;
- }
- /* suspend usb if sink */
- if ((stat & SNK_SRC_MODE_BIT) && chg->typec_present)
- vote(chg->usb_icl_votable, OTG_VOTER, true, 0);
- else
- vote(chg->usb_icl_votable, OTG_VOTER, false, 0);
-
- smblib_dbg(chg, PR_INTERRUPT, "IRQ: cc-state-change; Type-C %s detected\n",
- smblib_typec_mode_name[chg->typec_mode]);
-}
-
-static void smblib_usb_typec_change(struct smb_charger *chg)
-{
- int rc;
- u8 stat;
-
- smblib_handle_typec_cc_state_change(chg);
-
- rc = smblib_read(chg, TYPE_C_MISC_STATUS_REG, &stat);
- if (rc < 0) {
- smblib_err(chg, "Couldn't read TYPE_C_STATUS_4 rc=%d\n", rc);
- return;
- }
-
- if (stat & TYPEC_VBUS_ERROR_STATUS_BIT)
- smblib_dbg(chg, PR_INTERRUPT, "IRQ: vbus-error\n");
-
- power_supply_changed(chg->usb_psy);
}
irqreturn_t typec_or_rid_detection_change_irq_handler(int irq, void *data)
@@ -3281,17 +3215,81 @@
{
struct smb_irq_data *irq_data = data;
struct smb_charger *chg = irq_data->parent_data;
+ int typec_mode;
- if ((chg->connector_type == POWER_SUPPLY_CONNECTOR_MICRO_USB)
- || chg->pr_swap_in_progress) {
+ if (chg->connector_type == POWER_SUPPLY_CONNECTOR_MICRO_USB) {
smblib_dbg(chg, PR_INTERRUPT,
- "Ignoring since pr_swap_in_progress\n");
+ "Ignoring for micro USB\n");
return IRQ_HANDLED;
}
- mutex_lock(&chg->lock);
- smblib_usb_typec_change(chg);
- mutex_unlock(&chg->lock);
+ typec_mode = smblib_get_prop_typec_mode(chg);
+ if (chg->sink_src_mode != UNATTACHED_MODE
+ && (typec_mode != chg->typec_mode))
+ smblib_handle_rp_change(chg, typec_mode);
+ chg->typec_mode = typec_mode;
+
+ smblib_dbg(chg, PR_INTERRUPT, "IRQ: cc-state-change; Type-C %s detected\n",
+ smblib_typec_mode_name[chg->typec_mode]);
+
+ power_supply_changed(chg->usb_psy);
+
+ return IRQ_HANDLED;
+}
+
+irqreturn_t typec_attach_detach_irq_handler(int irq, void *data)
+{
+ struct smb_irq_data *irq_data = data;
+ struct smb_charger *chg = irq_data->parent_data;
+ u8 stat;
+ int rc;
+
+ smblib_dbg(chg, PR_INTERRUPT, "IRQ: %s\n", irq_data->name);
+
+ rc = smblib_read(chg, TYPE_C_STATE_MACHINE_STATUS_REG, &stat);
+ if (rc < 0) {
+ smblib_err(chg, "Couldn't read TYPE_C_STATE_MACHINE_STATUS_REG rc=%d\n",
+ rc);
+ return IRQ_HANDLED;
+ }
+
+ if (stat & TYPEC_ATTACH_DETACH_STATE_BIT) {
+ rc = smblib_read(chg, TYPE_C_MISC_STATUS_REG, &stat);
+ if (rc < 0) {
+ smblib_err(chg, "Couldn't read TYPE_C_MISC_STATUS_REG rc=%d\n",
+ rc);
+ return IRQ_HANDLED;
+ }
+
+ if (stat & SNK_SRC_MODE_BIT) {
+ chg->sink_src_mode = SRC_MODE;
+ typec_sink_insertion(chg);
+ } else {
+ chg->sink_src_mode = SINK_MODE;
+ typec_src_insertion(chg);
+ }
+
+ } else {
+ switch (chg->sink_src_mode) {
+ case SRC_MODE:
+ typec_sink_removal(chg);
+ break;
+ case SINK_MODE:
+ typec_src_removal(chg);
+ break;
+ default:
+ break;
+ }
+
+ if (!chg->pr_swap_in_progress) {
+ chg->ok_to_pd = false;
+ chg->sink_src_mode = UNATTACHED_MODE;
+ chg->early_usb_attach = false;
+ }
+ }
+
+ power_supply_changed(chg->usb_psy);
+
return IRQ_HANDLED;
}
@@ -3426,19 +3424,55 @@
const union power_supply_propval *val)
{
int rc;
+ u8 stat = 0, orientation;
chg->pr_swap_in_progress = val->intval;
- /*
- * call the cc changed irq to handle real removals while
- * PR_SWAP was in progress
- */
- smblib_usb_typec_change(chg);
rc = smblib_masked_write(chg, TYPE_C_DEBOUNCE_OPTION_REG,
REDUCE_TCCDEBOUNCE_TO_2MS_BIT,
val->intval ? REDUCE_TCCDEBOUNCE_TO_2MS_BIT : 0);
if (rc < 0)
smblib_err(chg, "Couldn't set tCC debounce rc=%d\n", rc);
+
+ rc = smblib_masked_write(chg, TYPE_C_EXIT_STATE_CFG_REG,
+ BYPASS_VSAFE0V_DURING_ROLE_SWAP_BIT,
+ val->intval ? BYPASS_VSAFE0V_DURING_ROLE_SWAP_BIT : 0);
+ if (rc < 0)
+ smblib_err(chg, "Couldn't set exit state cfg rc=%d\n", rc);
+
+ if (chg->pr_swap_in_progress) {
+ rc = smblib_read(chg, TYPE_C_MISC_STATUS_REG, &stat);
+ if (rc < 0) {
+ smblib_err(chg, "Couldn't read TYPE_C_STATUS_4 rc=%d\n",
+ rc);
+ }
+
+ orientation =
+ stat & CC_ORIENTATION_BIT ? TYPEC_CCOUT_VALUE_BIT : 0;
+ rc = smblib_masked_write(chg, TYPE_C_CCOUT_CONTROL_REG,
+ TYPEC_CCOUT_SRC_BIT | TYPEC_CCOUT_BUFFER_EN_BIT
+ | TYPEC_CCOUT_VALUE_BIT,
+ TYPEC_CCOUT_SRC_BIT | TYPEC_CCOUT_BUFFER_EN_BIT
+ | orientation);
+ if (rc < 0) {
+ smblib_err(chg, "Couldn't read TYPE_C_CCOUT_CONTROL_REG rc=%d\n",
+ rc);
+ }
+ } else {
+ rc = smblib_masked_write(chg, TYPE_C_CCOUT_CONTROL_REG,
+ TYPEC_CCOUT_SRC_BIT, 0);
+ if (rc < 0) {
+ smblib_err(chg, "Couldn't read TYPE_C_CCOUT_CONTROL_REG rc=%d\n",
+ rc);
+ }
+
+ /* enable DRP */
+ rc = smblib_masked_write(chg, TYPE_C_MODE_CFG_REG,
+ TYPEC_POWER_ROLE_CMD_MASK, 0);
+ if (rc < 0)
+ smblib_err(chg, "Couldn't enable DRP rc=%d\n", rc);
+ }
+
return 0;
}
@@ -3681,23 +3715,6 @@
return rc;
}
- chg->pd_disallowed_votable_indirect
- = create_votable("PD_DISALLOWED_INDIRECT", VOTE_SET_ANY,
- smblib_pd_disallowed_votable_indirect_callback, chg);
- if (IS_ERR(chg->pd_disallowed_votable_indirect)) {
- rc = PTR_ERR(chg->pd_disallowed_votable_indirect);
- chg->pd_disallowed_votable_indirect = NULL;
- return rc;
- }
-
- chg->pd_allowed_votable = create_votable("PD_ALLOWED",
- VOTE_SET_ANY, NULL, NULL);
- if (IS_ERR(chg->pd_allowed_votable)) {
- rc = PTR_ERR(chg->pd_allowed_votable);
- chg->pd_allowed_votable = NULL;
- return rc;
- }
-
chg->awake_votable = create_votable("AWAKE", VOTE_SET_ANY,
smblib_awake_vote_callback,
chg);
@@ -3735,10 +3752,6 @@
destroy_votable(chg->dc_suspend_votable);
if (chg->usb_icl_votable)
destroy_votable(chg->usb_icl_votable);
- if (chg->pd_disallowed_votable_indirect)
- destroy_votable(chg->pd_disallowed_votable_indirect);
- if (chg->pd_allowed_votable)
- destroy_votable(chg->pd_allowed_votable);
if (chg->awake_votable)
destroy_votable(chg->awake_votable);
if (chg->chg_disable_votable)
@@ -3750,7 +3763,6 @@
int rc = 0;
mutex_init(&chg->lock);
- mutex_init(&chg->otg_oc_lock);
INIT_WORK(&chg->bms_update_work, bms_update_work);
INIT_WORK(&chg->pl_update_work, pl_update_work);
INIT_WORK(&chg->jeita_update_work, jeita_update_work);
@@ -3763,6 +3775,7 @@
chg->fake_input_current_limited = -EINVAL;
chg->fake_batt_status = -EINVAL;
chg->jeita_configured = false;
+ chg->sink_src_mode = UNATTACHED_MODE;
switch (chg->mode) {
case PARALLEL_MASTER:
diff --git a/drivers/power/supply/qcom/smb5-lib.h b/drivers/power/supply/qcom/smb5-lib.h
index 5d6d3a6..7e2558c 100644
--- a/drivers/power/supply/qcom/smb5-lib.h
+++ b/drivers/power/supply/qcom/smb5-lib.h
@@ -56,6 +56,7 @@
#define CTM_VOTER "CTM_VOTER"
#define SW_QC3_VOTER "SW_QC3_VOTER"
#define AICL_RERUN_VOTER "AICL_RERUN_VOTER"
+#define SW_ICL_MAX_VOTER "SW_ICL_MAX_VOTER"
#define QNOVO_VOTER "QNOVO_VOTER"
#define BATT_PROFILE_VOTER "BATT_PROFILE_VOTER"
#define OTG_DELAY_VOTER "OTG_DELAY_VOTER"
@@ -65,8 +66,6 @@
#define PL_FCC_LOW_VOTER "PL_FCC_LOW_VOTER"
#define WBC_VOTER "WBC_VOTER"
#define HW_LIMIT_VOTER "HW_LIMIT_VOTER"
-#define DYNAMIC_RP_VOTER "DYNAMIC_RP_VOTER"
-#define DEFAULT_100MA_VOTER "DEFAULT_100MA_VOTER"
#define FORCE_RECHARGE_VOTER "FORCE_RECHARGE_VOTER"
#define BOOST_BACK_STORM_COUNT 3
@@ -80,6 +79,12 @@
NUM_MODES,
};
+enum sink_src_mode {
+ SINK_MODE,
+ SRC_MODE,
+ UNATTACHED_MODE,
+};
+
enum {
BOOST_BACK_WA = BIT(0),
};
@@ -228,6 +233,7 @@
struct smb_chg_param fcc;
struct smb_chg_param fv;
struct smb_chg_param usb_icl;
+ struct smb_chg_param icl_max_stat;
struct smb_chg_param icl_stat;
struct smb_chg_param otg_cl;
struct smb_chg_param jeita_cc_comp_hot;
@@ -259,6 +265,7 @@
struct smb_params param;
struct smb_iio iio;
int *debug_mask;
+ int *pd_disabled;
enum smb_mode mode;
struct smb_chg_freq chg_freq;
int smb_version;
@@ -269,7 +276,6 @@
/* locks */
struct mutex lock;
struct mutex ps_change_lock;
- struct mutex otg_oc_lock;
/* power supplies */
struct power_supply *batt_psy;
@@ -296,8 +302,6 @@
struct votable *fcc_votable;
struct votable *fv_votable;
struct votable *usb_icl_votable;
- struct votable *pd_disallowed_votable_indirect;
- struct votable *pd_allowed_votable;
struct votable *awake_votable;
struct votable *pl_disable_votable;
struct votable *chg_disable_votable;
@@ -315,10 +319,17 @@
struct delayed_work uusb_otg_work;
struct delayed_work bb_removal_work;
- /* cached status */
+ /* pd */
int voltage_min_uv;
int voltage_max_uv;
int pd_active;
+ bool pd_hard_reset;
+ bool pr_swap_in_progress;
+ bool early_usb_attach;
+ bool ok_to_pd;
+ bool typec_legacy;
+
+ /* cached status */
bool system_suspend_supported;
int boost_threshold_ua;
int system_temp_level;
@@ -334,15 +345,10 @@
int connector_type;
bool otg_en;
bool suspend_input_on_debug_batt;
- int otg_attempts;
- int vconn_attempts;
int default_icl_ua;
int otg_cl_ua;
bool uusb_apsd_rerun_done;
- bool pd_hard_reset;
- bool typec_present;
int fake_input_current_limited;
- bool pr_swap_in_progress;
int typec_mode;
int usb_icl_change_irq_enabled;
u32 jeita_status;
@@ -352,6 +358,7 @@
int hw_max_icl_ua;
int auto_recharge_soc;
bool jeita_configured;
+ enum sink_src_mode sink_src_mode;
/* workaround flag */
u32 wa_flags;
@@ -420,6 +427,7 @@
irqreturn_t usb_source_change_irq_handler(int irq, void *data);
irqreturn_t icl_change_irq_handler(int irq, void *data);
irqreturn_t typec_state_change_irq_handler(int irq, void *data);
+irqreturn_t typec_attach_detach_irq_handler(int irq, void *data);
irqreturn_t dc_plugin_irq_handler(int irq, void *data);
irqreturn_t high_duty_cycle_irq_handler(int irq, void *data);
irqreturn_t switcher_power_ok_irq_handler(int irq, void *data);
@@ -485,8 +493,6 @@
union power_supply_propval *val);
int smblib_get_prop_typec_power_role(struct smb_charger *chg,
union power_supply_propval *val);
-int smblib_get_prop_pd_allowed(struct smb_charger *chg,
- union power_supply_propval *val);
int smblib_get_prop_input_current_settled(struct smb_charger *chg,
union power_supply_propval *val);
int smblib_get_prop_input_voltage_settled(struct smb_charger *chg,
diff --git a/drivers/power/supply/qcom/smb5-reg.h b/drivers/power/supply/qcom/smb5-reg.h
index e199087..4fdf866 100644
--- a/drivers/power/supply/qcom/smb5-reg.h
+++ b/drivers/power/supply/qcom/smb5-reg.h
@@ -105,6 +105,8 @@
/********************************
* DCDC Peripheral Registers *
********************************/
+#define ICL_MAX_STATUS_REG (DCDC_BASE + 0x06)
+
#define AICL_ICL_STATUS_REG (DCDC_BASE + 0x08)
#define AICL_STATUS_REG (DCDC_BASE + 0x0A)
@@ -215,6 +217,7 @@
#define HVDCP_AUTH_ALG_EN_CFG_BIT BIT(6)
#define HVDCP_AUTONOMOUS_MODE_EN_CFG_BIT BIT(5)
#define BC1P2_SRC_DETECT_BIT BIT(3)
+#define HVDCP_EN_BIT BIT(2)
#define USBIN_OPTIONS_2_CFG_REG (USBIN_BASE + 0x63)
#define FLOAT_OPTIONS_MASK GENMASK(2, 0)
@@ -262,6 +265,9 @@
#define SRC_RA_OPEN_BIT BIT(1)
#define AUDIO_ACCESS_RA_RA_BIT BIT(0)
+#define TYPE_C_STATE_MACHINE_STATUS_REG (TYPEC_BASE + 0x09)
+#define TYPEC_ATTACH_DETACH_STATE_BIT BIT(5)
+
#define TYPE_C_MISC_STATUS_REG (TYPEC_BASE + 0x0B)
#define SNK_SRC_MODE_BIT BIT(6)
#define TYPEC_VBUS_ERROR_STATUS_BIT BIT(4)
@@ -269,6 +275,7 @@
#define CC_ATTACHED_BIT BIT(0)
#define LEGACY_CABLE_STATUS_REG (TYPEC_BASE + 0x0D)
+#define TYPEC_LEGACY_CABLE_STATUS_BIT BIT(1)
#define TYPEC_NONCOMP_LEGACY_CABLE_STATUS_BIT BIT(0)
#define TYPEC_U_USB_STATUS_REG (TYPEC_BASE + 0x0F)
@@ -287,10 +294,12 @@
#define VCONN_EN_SRC_BIT BIT(0)
#define TYPE_C_CCOUT_CONTROL_REG (TYPEC_BASE + 0x48)
+#define TYPEC_CCOUT_BUFFER_EN_BIT BIT(2)
#define TYPEC_CCOUT_VALUE_BIT BIT(1)
#define TYPEC_CCOUT_SRC_BIT BIT(0)
#define TYPE_C_EXIT_STATE_CFG_REG (TYPEC_BASE + 0x50)
+#define BYPASS_VSAFE0V_DURING_ROLE_SWAP_BIT BIT(3)
#define EXIT_SNK_BASED_ON_CC_BIT BIT(0)
#define TYPE_C_INTERRUPT_EN_CFG_1_REG (TYPEC_BASE + 0x5E)
diff --git a/drivers/thermal/tsens1xxx.c b/drivers/thermal/tsens1xxx.c
index d63fcd1..19e2b5a 100644
--- a/drivers/thermal/tsens1xxx.c
+++ b/drivers/thermal/tsens1xxx.c
@@ -517,8 +517,8 @@
th_temp = code_to_degc((threshold &
TSENS_UPPER_THRESHOLD_MASK) >>
TSENS_UPPER_THRESHOLD_SHIFT,
- tm->sensor);
- if (th_temp > temp) {
+ (tm->sensor + i));
+ if (th_temp > (temp/TSENS_SCALE_MILLIDEG)) {
pr_debug("Re-arm high threshold\n");
rc = tsens_tz_activate_trip_type(
&tm->sensor[i],
@@ -539,8 +539,8 @@
tm->tsens_tm_addr + addr_offset));
th_temp = code_to_degc((threshold &
TSENS_LOWER_THRESHOLD_MASK),
- tm->sensor);
- if (th_temp < temp) {
+ (tm->sensor + i));
+ if (th_temp < (temp/TSENS_SCALE_MILLIDEG)) {
pr_debug("Re-arm Low threshold\n");
rc = tsens_tz_activate_trip_type(
&tm->sensor[i],
diff --git a/drivers/usb/pd/policy_engine.c b/drivers/usb/pd/policy_engine.c
index 6111d39..d21823b 100644
--- a/drivers/usb/pd/policy_engine.c
+++ b/drivers/usb/pd/policy_engine.c
@@ -942,7 +942,7 @@
/* check against received length to avoid overrun */
if (bytes_to_copy > len - sizeof(ext_hdr)) {
- usbpd_warn(&pd->dev, "not enough bytes in chunk, expected:%u received:%lu\n",
+ usbpd_warn(&pd->dev, "not enough bytes in chunk, expected:%u received:%zu\n",
bytes_to_copy, len - sizeof(ext_hdr));
bytes_to_copy = len - sizeof(ext_hdr);
}
diff --git a/drivers/video/fbdev/msm/mdss_dsi_host.c b/drivers/video/fbdev/msm/mdss_dsi_host.c
index ca0af2b..56ed15d 100644
--- a/drivers/video/fbdev/msm/mdss_dsi_host.c
+++ b/drivers/video/fbdev/msm/mdss_dsi_host.c
@@ -161,7 +161,14 @@
MDSS_XLOG(ctrl->ndx, enable, ctrl->mdp_busy, current->pid,
client);
- if (enable == 0) {
+ /*
+ * ensure that before going into ecg or turning
+ * off the clocks, cmd_mdp_busy is not true. During a
+ * race condition, clocks are turned off and so the
+ * isr for cmd_mdp_busy does not get cleared in hw.
+ */
+ if (enable == MDSS_DSI_CLK_OFF ||
+ enable == MDSS_DSI_CLK_EARLY_GATE) {
/* need wait before disable */
mutex_lock(&ctrl->cmd_mutex);
mdss_dsi_cmd_mdp_busy(ctrl);
diff --git a/drivers/video/fbdev/msm/mdss_fb.c b/drivers/video/fbdev/msm/mdss_fb.c
index 27299d10..7ca94df 100644
--- a/drivers/video/fbdev/msm/mdss_fb.c
+++ b/drivers/video/fbdev/msm/mdss_fb.c
@@ -1161,11 +1161,24 @@
if (pdata->next) {
spt = mdss_panel_get_timing_by_name(pdata->next,
modedb[i].name);
- if (!IS_ERR_OR_NULL(spt))
+ /* for split config, recalculate xres and pixel clock */
+ if (!IS_ERR_OR_NULL(spt)) {
+ unsigned long pclk, h_total, v_total;
modedb[i].xres += spt->xres;
- else
+ h_total = modedb[i].xres +
+ modedb[i].left_margin +
+ modedb[i].right_margin +
+ modedb[i].hsync_len;
+ v_total = modedb[i].yres +
+ modedb[i].lower_margin +
+ modedb[i].upper_margin +
+ modedb[i].vsync_len;
+ pclk = h_total * v_total * modedb[i].refresh;
+ modedb[i].pixclock = KHZ2PICOS(pclk / 1000);
+ } else {
pr_debug("no matching split config for %s\n",
modedb[i].name);
+ }
/*
* if no panel timing found for current, need to
diff --git a/include/trace/events/sched.h b/include/trace/events/sched.h
index e06df4d..b355ebf 100644
--- a/include/trace/events/sched.h
+++ b/include/trace/events/sched.h
@@ -713,10 +713,10 @@
TRACE_EVENT(sched_task_util,
TP_PROTO(struct task_struct *p, int next_cpu, int backup_cpu,
- int target_cpu, bool sync, bool need_idle,
+ int target_cpu, bool sync, bool need_idle, int fastpath,
bool placement_boost, int rtg_cpu, u64 start_t),
- TP_ARGS(p, next_cpu, backup_cpu, target_cpu, sync, need_idle,
+ TP_ARGS(p, next_cpu, backup_cpu, target_cpu, sync, need_idle, fastpath,
placement_boost, rtg_cpu, start_t),
TP_STRUCT__entry(
@@ -729,6 +729,7 @@
__field(int, target_cpu )
__field(bool, sync )
__field(bool, need_idle )
+ __field(int, fastpath )
__field(bool, placement_boost )
__field(int, rtg_cpu )
__field(u64, latency )
@@ -744,13 +745,14 @@
__entry->target_cpu = target_cpu;
__entry->sync = sync;
__entry->need_idle = need_idle;
+ __entry->fastpath = fastpath;
__entry->placement_boost = placement_boost;
__entry->rtg_cpu = rtg_cpu;
__entry->latency = (sched_clock() - start_t);
),
- TP_printk("pid=%d comm=%s util=%lu prev_cpu=%d next_cpu=%d backup_cpu=%d target_cpu=%d sync=%d need_idle=%d placement_boost=%d rtg_cpu=%d latency=%llu",
- __entry->pid, __entry->comm, __entry->util, __entry->prev_cpu, __entry->next_cpu, __entry->backup_cpu, __entry->target_cpu, __entry->sync, __entry->need_idle, __entry->placement_boost, __entry->rtg_cpu, __entry->latency)
+ TP_printk("pid=%d comm=%s util=%lu prev_cpu=%d next_cpu=%d backup_cpu=%d target_cpu=%d sync=%d need_idle=%d fastpath=%d placement_boost=%d rtg_cpu=%d latency=%llu",
+ __entry->pid, __entry->comm, __entry->util, __entry->prev_cpu, __entry->next_cpu, __entry->backup_cpu, __entry->target_cpu, __entry->sync, __entry->need_idle, __entry->fastpath, __entry->placement_boost, __entry->rtg_cpu, __entry->latency)
);
#endif
diff --git a/include/uapi/linux/qg.h b/include/uapi/linux/qg.h
index 54488ff..2c7b49a 100644
--- a/include/uapi/linux/qg.h
+++ b/include/uapi/linux/qg.h
@@ -12,8 +12,8 @@
QG_ESR,
QG_CHARGE_COUNTER,
QG_FIFO_TIME_DELTA,
- QG_RESERVED_1,
- QG_RESERVED_2,
+ QG_BATT_SOC,
+ QG_CC_SOC,
QG_RESERVED_3,
QG_RESERVED_4,
QG_RESERVED_5,
@@ -25,6 +25,9 @@
QG_MAX,
};
+#define QG_BATT_SOC QG_BATT_SOC
+#define QG_CC_SOC QG_CC_SOC
+
struct fifo_data {
unsigned int v;
unsigned int i;
diff --git a/include/uapi/media/msm_media_info.h b/include/uapi/media/msm_media_info.h
index 4f12e5c..3fd0c88 100644
--- a/include/uapi/media/msm_media_info.h
+++ b/include/uapi/media/msm_media_info.h
@@ -1229,6 +1229,7 @@
{
const unsigned int extra_size = VENUS_EXTRADATA_SIZE(width, height);
unsigned int uv_alignment = 0, size = 0;
+ unsigned int w_alignment = 512;
unsigned int y_plane, uv_plane, y_stride,
uv_stride, y_sclines, uv_sclines;
unsigned int y_ubwc_plane = 0, uv_ubwc_plane = 0;
@@ -1252,6 +1253,17 @@
switch (color_fmt) {
case COLOR_FMT_NV21:
case COLOR_FMT_NV12:
+ uv_alignment = 4096;
+ y_plane = y_stride * y_sclines;
+ uv_plane = uv_stride * uv_sclines + uv_alignment;
+ size = y_plane + uv_plane +
+ MSM_MEDIA_MAX(extra_size, 8 * y_stride);
+ size = MSM_MEDIA_ALIGN(size, 4096);
+
+ /* Additional size to cover last row of non-aligned frame */
+ size += MSM_MEDIA_ALIGN(width, w_alignment) * w_alignment;
+ size = MSM_MEDIA_ALIGN(size, 4096);
+ break;
case COLOR_FMT_P010:
uv_alignment = 4096;
y_plane = y_stride * y_sclines;
@@ -1288,6 +1300,10 @@
uv_meta_plane)*2 +
MSM_MEDIA_MAX(extra_size + 8192, 48 * y_stride);
size = MSM_MEDIA_ALIGN(size, 4096);
+
+ /* Additional size to cover last row of non-aligned frame */
+ size += MSM_MEDIA_ALIGN(width, w_alignment) * w_alignment;
+ size = MSM_MEDIA_ALIGN(size, 4096);
break;
case COLOR_FMT_NV12_BPP10_UBWC:
y_ubwc_plane = MSM_MEDIA_ALIGN(y_stride * y_sclines, 4096);
diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c
index 66a4658..7e55c81 100644
--- a/kernel/sched/fair.c
+++ b/kernel/sched/fair.c
@@ -7358,6 +7358,12 @@
}
#endif
+enum fastpaths {
+ NONE = 0,
+ SYNC_WAKEUP,
+ PREV_CPU_BIAS,
+};
+
static int select_energy_cpu_brute(struct task_struct *p, int prev_cpu, int sync)
{
bool boosted, prefer_idle;
@@ -7368,6 +7374,7 @@
struct cpumask *rtg_target = find_rtg_target(p);
struct find_best_target_env fbt_env;
u64 start_t = 0;
+ int fastpath = 0;
if (trace_sched_task_util_enabled())
start_t = sched_clock();
@@ -7404,12 +7411,17 @@
if (bias_to_waker_cpu(p, cpu, rtg_target)) {
schedstat_inc(p->se.statistics.nr_wakeups_secb_sync);
schedstat_inc(this_rq()->eas_stats.secb_sync);
- return cpu;
+ target_cpu = cpu;
+ fastpath = SYNC_WAKEUP;
+ goto out;
}
}
- if (bias_to_prev_cpu(p, rtg_target))
- return prev_cpu;
+ if (bias_to_prev_cpu(p, rtg_target)) {
+ target_cpu = prev_cpu;
+ fastpath = PREV_CPU_BIAS;
+ goto out;
+ }
rcu_read_lock();
@@ -7496,11 +7508,12 @@
schedstat_inc(this_rq()->eas_stats.secb_count);
unlock:
- trace_sched_task_util(p, next_cpu, backup_cpu, target_cpu, sync,
- fbt_env.need_idle, fbt_env.placement_boost,
- rtg_target ? cpumask_first(rtg_target) : -1,
- start_t);
rcu_read_unlock();
+out:
+ trace_sched_task_util(p, next_cpu, backup_cpu, target_cpu, sync,
+ fbt_env.need_idle, fastpath,
+ fbt_env.placement_boost, rtg_target ?
+ cpumask_first(rtg_target) : -1, start_t);
return target_cpu;
}
diff --git a/net/ipv6/ip6_output.c b/net/ipv6/ip6_output.c
index 22c73c3..de0188e 100644
--- a/net/ipv6/ip6_output.c
+++ b/net/ipv6/ip6_output.c
@@ -1279,7 +1279,7 @@
if (np->frag_size)
mtu = np->frag_size;
}
- if (mtu < IPV6_MIN_MTU)
+ if (!(rt->dst.flags & DST_XFRM_TUNNEL) && mtu < IPV6_MIN_MTU)
return -EINVAL;
cork->base.fragsize = mtu;
if (dst_allfrag(rt->dst.path))