Merge "ARM: dts: msm: Add oem in vbmeta parts for apq8053 lite"
diff --git a/Documentation/devicetree/bindings/arm/msm/msm.txt b/Documentation/devicetree/bindings/arm/msm/msm.txt
index 931ef7a..a55ef6c 100644
--- a/Documentation/devicetree/bindings/arm/msm/msm.txt
+++ b/Documentation/devicetree/bindings/arm/msm/msm.txt
@@ -110,6 +110,9 @@
- SDM450
compatible = "qcom,sdm450"
+- SDA450
+ compatible = "qcom,sda450"
+
- SDM632
compatible = "qcom,sdm632"
@@ -357,6 +360,8 @@
compatible = "qcom,sdm450-mtp"
compatible = "qcom,sdm450-cdp"
compatible = "qcom,sdm450-qrd"
+compatible = "qcom,sda450-mtp"
+compatible = "qcom,sda450-cdp"
compatible = "qcom,sdm632-rumi"
compatible = "qcom,sdm632-cdp"
compatible = "qcom,sdm632-mtp"
diff --git a/Documentation/devicetree/bindings/input/qpnp-power-on.txt b/Documentation/devicetree/bindings/input/qpnp-power-on.txt
index 0f1d9e1..9addf15 100644
--- a/Documentation/devicetree/bindings/input/qpnp-power-on.txt
+++ b/Documentation/devicetree/bindings/input/qpnp-power-on.txt
@@ -115,6 +115,10 @@
- qcom,use-legacy-hard-reset-offset Boolean property to support legacy
hard-reset offset of the PON_RB_SPARE register for
some (PON gen2) platforms.
+- qcom,support-twm-config Boolean property to allow the PON module to be
+ configured to support TWM modes.
+- qcom,pbs-client Phandle of the PBS client node. Should be
+ defined if 'qcom,support-twm-config' is present.
All the below properties are in the sub-node section (properties of the child
node).
diff --git a/Documentation/devicetree/bindings/misc/qpnp-misc.txt b/Documentation/devicetree/bindings/misc/qpnp-misc.txt
index a34cbde..ada8da9 100644
--- a/Documentation/devicetree/bindings/misc/qpnp-misc.txt
+++ b/Documentation/devicetree/bindings/misc/qpnp-misc.txt
@@ -16,6 +16,12 @@
if a non-zero PWM source is specified under
"qcom,pwm-sel" property.
+- qcom,support-twm-config Enable configuration for TWM mode.
+
+- qcom,twm-mode The TWM mode which PMIC enters post power-off.
+ Valid only if 'qcom,support-twm-config' is
+ defined. If not specified, the default mode
+ is 3.
Example:
qcom,misc@900 {
compatible = "qcom,qpnp-misc";
diff --git a/arch/arm/boot/dts/qcom/sdxpoorwills.dtsi b/arch/arm/boot/dts/qcom/sdxpoorwills.dtsi
index 51df1d7..38ba7fc 100644
--- a/arch/arm/boot/dts/qcom/sdxpoorwills.dtsi
+++ b/arch/arm/boot/dts/qcom/sdxpoorwills.dtsi
@@ -826,8 +826,6 @@
qcom,rmnet-ipa {
compatible = "qcom,rmnet-ipa3";
qcom,rmnet-ipa-ssr;
- qcom,ipa-loaduC;
- qcom,ipa-advertise-sg-support;
};
dcc: dcc_v2@10a2000 {
diff --git a/arch/arm/configs/msm8909w-perf_defconfig b/arch/arm/configs/msm8909w-perf_defconfig
index cdfe536..75372aa 100644
--- a/arch/arm/configs/msm8909w-perf_defconfig
+++ b/arch/arm/configs/msm8909w-perf_defconfig
@@ -33,6 +33,7 @@
CONFIG_EMBEDDED=y
CONFIG_PROFILING=y
CONFIG_OPROFILE=y
+CONFIG_CC_STACKPROTECTOR_STRONG=y
CONFIG_MODULES=y
CONFIG_MODULE_UNLOAD=y
CONFIG_MODULE_FORCE_UNLOAD=y
diff --git a/arch/arm/configs/msm8909w_defconfig b/arch/arm/configs/msm8909w_defconfig
index 46052bd..086b127 100644
--- a/arch/arm/configs/msm8909w_defconfig
+++ b/arch/arm/configs/msm8909w_defconfig
@@ -33,6 +33,7 @@
CONFIG_EMBEDDED=y
CONFIG_PROFILING=y
CONFIG_OPROFILE=y
+CONFIG_CC_STACKPROTECTOR_STRONG=y
CONFIG_MODULES=y
CONFIG_MODULE_UNLOAD=y
CONFIG_MODULE_FORCE_UNLOAD=y
diff --git a/arch/arm64/boot/dts/qcom/8909w-pm660.dtsi b/arch/arm64/boot/dts/qcom/8909w-pm660.dtsi
index 2a3468d..3947406 100644
--- a/arch/arm64/boot/dts/qcom/8909w-pm660.dtsi
+++ b/arch/arm64/boot/dts/qcom/8909w-pm660.dtsi
@@ -469,6 +469,15 @@
qcom,support-twm-config;
};
+&pm660_pbs {
+ status = "okay";
+};
+
+&pm660_pon {
+ qcom,support-twm-config;
+ qcom,pbs-client = <&pm660_pbs>;
+};
+
/ {
/delete-node/ qcom,battery-data;
mtp_batterydata: qcom,battery-data {
diff --git a/arch/arm64/boot/dts/qcom/Makefile b/arch/arm64/boot/dts/qcom/Makefile
index 7457bbb..85c4df1 100644
--- a/arch/arm64/boot/dts/qcom/Makefile
+++ b/arch/arm64/boot/dts/qcom/Makefile
@@ -317,6 +317,7 @@
msm8953-mtp-overlay.dtbo-base := sdm450.dtb \
msm8953.dtb \
apq8053.dtb \
+ sda450.dtb \
msm8953-pmi8940.dtb \
msm8953-pmi8937.dtb \
sdm450-pmi8940.dtb \
@@ -324,6 +325,7 @@
msm8953-cdp-overlay.dtbo-base := sdm450.dtb \
msm8953.dtb \
apq8053.dtb \
+ sda450.dtb \
msm8953-pmi8940.dtb \
msm8953-pmi8937.dtb
msm8953-rcm-overlay.dtbo-base := sdm450.dtb \
@@ -346,10 +348,12 @@
sdm450-cdp-s2-overlay.dtbo-base := sdm450-pmi632.dtb \
sdm632.dtb \
sdm632-pm8004.dtb \
- msm8953-pmi632.dtb
+ msm8953-pmi632.dtb \
+ sda450-pmi632.dtb
sdm450-mtp-s3-overlay.dtbo-base := sdm450-pmi632.dtb \
sdm632.dtb \
- sdm632-pm8004.dtb
+ sdm632-pm8004.dtb \
+ sda450-pmi632.dtb
sdm450-qrd-sku4-overlay.dtbo-base := sdm450-pmi632.dtb \
sdm632.dtb \
sdm632-pm8004.dtb
@@ -422,13 +426,17 @@
dtb-$(CONFIG_ARCH_SDM450) += sdm450-rcm.dtb \
sdm450-cdp.dtb \
sdm450-mtp.dtb \
+ sda450-cdp.dtb \
+ sda450-mtp.dtb \
sdm450-qrd.dtb \
sdm450-pmi8940-mtp.dtb \
sdm450-pmi8937-mtp.dtb \
sdm450-iot-mtp.dtb \
sdm450-qrd-sku4.dtb \
sdm450-pmi632-cdp-s2.dtb \
- sdm450-pmi632-mtp-s3.dtb
+ sdm450-pmi632-mtp-s3.dtb \
+ sda450-pmi632-cdp-s2.dtb \
+ sda450-pmi632-mtp-s3.dtb
dtb-$(CONFIG_ARCH_SDM632) += sdm632-rumi.dtb \
sdm632-cdp-s2.dtb \
diff --git a/arch/arm64/boot/dts/qcom/msm8917-pmi8937-qrd-sku5.dts b/arch/arm64/boot/dts/qcom/msm8917-pmi8937-qrd-sku5.dts
index d857c82..ab86021 100644
--- a/arch/arm64/boot/dts/qcom/msm8917-pmi8937-qrd-sku5.dts
+++ b/arch/arm64/boot/dts/qcom/msm8917-pmi8937-qrd-sku5.dts
@@ -61,3 +61,46 @@
qcom,esd-check-enabled;
qcom,panel-supply-entries = <&dsi_panel_pwr_supply>;
};
+
+&soc {
+ i2c@78b7000 {
+ status = "ok";
+ focaltech@38 {
+ compatible = "focaltech,5x06";
+ reg = <0x38>;
+ interrupt-parent = <&tlmm>;
+ interrupts = <65 0x2>;
+ vdd-supply = <&pm8937_l10>;
+ vcc_i2c-supply = <&pm8937_l5>;
+ /* pins used by touchscreen */
+ 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,name = "ft5436";
+ focaltech,family-id = <0x06>;
+ focaltech,reset-gpio = <&tlmm 64 0x0>;
+ focaltech,irq-gpio = <&tlmm 65 0x2008>;
+ focaltech,display-coords = <0 0 720 1280>;
+ focaltech,panel-coords = <0 0 720 1400>;
+ focaltech,button-map= <139 102 158>;
+ 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,ignore-id-check;
+ focaltech,resume-in-workqueue;
+ };
+ };
+};
diff --git a/arch/arm64/boot/dts/qcom/msm8937.dtsi b/arch/arm64/boot/dts/qcom/msm8937.dtsi
index 632c924..7b9f74b 100644
--- a/arch/arm64/boot/dts/qcom/msm8937.dtsi
+++ b/arch/arm64/boot/dts/qcom/msm8937.dtsi
@@ -191,6 +191,17 @@
<0x0b002000 0x1000>;
};
+ dcc: dcc@b3000 {
+ compatible = "qcom,dcc";
+ reg = <0xb3000 0x1000>,
+ <0xb4000 0x2000>;
+ reg-names = "dcc-base", "dcc-ram-base";
+
+ clocks = <&clock_gcc clk_gcc_dcc_clk>;
+ clock-names = "apb_pclk";
+ qcom,save-reg;
+ };
+
wakegic: wake-gic {
compatible = "qcom,mpm-gic-msm8937", "qcom,mpm-gic";
interrupts = <GIC_SPI 171 IRQ_TYPE_EDGE_RISING>;
diff --git a/arch/arm64/boot/dts/qcom/pm660.dtsi b/arch/arm64/boot/dts/qcom/pm660.dtsi
index fa10500..e8a82aa 100644
--- a/arch/arm64/boot/dts/qcom/pm660.dtsi
+++ b/arch/arm64/boot/dts/qcom/pm660.dtsi
@@ -32,7 +32,7 @@
reg = <0x900 0x100>;
};
- qcom,power-on@800 {
+ pm660_pon: qcom,power-on@800 {
compatible = "qcom,qpnp-power-on";
reg = <0x800 0x100>;
interrupts = <0x0 0x8 0x0 IRQ_TYPE_NONE>,
@@ -349,6 +349,12 @@
};
};
+ pm660_pbs: qcom,pbs@7400 {
+ compatible = "qcom,qpnp-pbs";
+ reg = <0x7400 0x100>;
+ status = "disabled";
+ };
+
bcl_sensor: bcl@4200 {
compatible = "qcom,msm-bcl-lmh";
reg = <0x4200 0xff>,
diff --git a/arch/arm64/boot/dts/qcom/qg-batterydata-ascent-3450mah.dtsi b/arch/arm64/boot/dts/qcom/qg-batterydata-ascent-3450mah.dtsi
index 8af4254..f28f19e2 100644
--- a/arch/arm64/boot/dts/qcom/qg-batterydata-ascent-3450mah.dtsi
+++ b/arch/arm64/boot/dts/qcom/qg-batterydata-ascent-3450mah.dtsi
@@ -32,6 +32,11 @@
4201000 4300000 2760000
4301000 4350000 2070000>;
+ /* COOL = 5 DegC, WARM = 40 DegC */
+ qcom,jeita-soft-thresholds = <0x44bd 0x1fc4>;
+ /* COLD = 0 DegC, HOT = 45 DegC */
+ qcom,jeita-hard-thresholds = <0x4aa5 0x1bfb>;
+
qcom,fcc1-temp-lut {
qcom,lut-col-legend = <0 10 25 40 50>;
qcom,lut-data = <3377 3428 3481 3496 3500>;
diff --git a/arch/arm64/boot/dts/qcom/qg-batterydata-mlp356477-2800mah.dtsi b/arch/arm64/boot/dts/qcom/qg-batterydata-mlp356477-2800mah.dtsi
index 6bcfd37..3b18010 100644
--- a/arch/arm64/boot/dts/qcom/qg-batterydata-mlp356477-2800mah.dtsi
+++ b/arch/arm64/boot/dts/qcom/qg-batterydata-mlp356477-2800mah.dtsi
@@ -29,6 +29,11 @@
151 450 4400000
451 550 4150000>;
+ /* COOL = 15 DegC, WARM = 45 DegC */
+ qcom,jeita-soft-thresholds = <0x4621 0x20b8>;
+ /* COLD = 0 DegC, HOT = 55 DegC */
+ qcom,jeita-hard-thresholds = <0x58cd 0x181d>;
+
qcom,fcc1-temp-lut {
qcom,lut-col-legend = <0 10 25 40 50>;
qcom,lut-data = <2715 2788 2861 2898 2908>;
diff --git a/arch/arm64/boot/dts/qcom/sda450-cdp.dts b/arch/arm64/boot/dts/qcom/sda450-cdp.dts
new file mode 100644
index 0000000..e8f22b8
--- /dev/null
+++ b/arch/arm64/boot/dts/qcom/sda450-cdp.dts
@@ -0,0 +1,26 @@
+/*
+ * Copyright (c) 2018, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+/dts-v1/;
+
+#include "sda450.dtsi"
+#include "pmi8950.dtsi"
+#include "msm8953-cdp.dtsi"
+#include "msm8953-pmi8950.dtsi"
+
+/ {
+ model = "Qualcomm Technologies, Inc. SDA450 + PMI8950 CDP";
+ compatible = "qcom,sda450-cdp", "qcom,sda450", "qcom,cdp";
+ qcom,board-id = <1 0>;
+ qcom,pmic-id = <0x010016 0x010011 0x0 0x0>;
+};
diff --git a/arch/arm64/boot/dts/qcom/sda450-mtp.dts b/arch/arm64/boot/dts/qcom/sda450-mtp.dts
new file mode 100644
index 0000000..06002e0
--- /dev/null
+++ b/arch/arm64/boot/dts/qcom/sda450-mtp.dts
@@ -0,0 +1,44 @@
+/*
+ * 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 "sda450.dtsi"
+#include "pmi8950.dtsi"
+#include "msm8953-mtp.dtsi"
+#include "msm8953-pmi8950.dtsi"
+
+/ {
+ model = "Qualcomm Technologies, Inc. SDA450 + PMI8950 MTP";
+ compatible = "qcom,sda450-mtp", "qcom,sda450", "qcom,mtp";
+ qcom,board-id = <8 0>;
+ qcom,pmic-id = <0x010016 0x010011 0x0 0x0>;
+};
+
+/{
+ mtp_batterydata: qcom,battery-data {
+ qcom,batt-id-range-pct = <15>;
+ #include "batterydata-itech-3000mah.dtsi"
+ #include "batterydata-ascent-3450mAh.dtsi"
+ };
+};
+
+&qpnp_fg {
+ qcom,battery-data = <&mtp_batterydata>;
+};
+
+&qpnp_smbcharger {
+ qcom,battery-data = <&mtp_batterydata>;
+ qcom,chg-led-sw-controls;
+ qcom,chg-led-support;
+};
diff --git a/arch/arm64/boot/dts/qcom/sda450-pmi632-cdp-s2.dts b/arch/arm64/boot/dts/qcom/sda450-pmi632-cdp-s2.dts
new file mode 100644
index 0000000..14c5e22
--- /dev/null
+++ b/arch/arm64/boot/dts/qcom/sda450-pmi632-cdp-s2.dts
@@ -0,0 +1,26 @@
+/*
+ * Copyright (c) 2018, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+/dts-v1/;
+
+#include "sda450.dtsi"
+#include "sdm450-pmi632-cdp-s2.dtsi"
+#include "sdm450-pmi632.dtsi"
+
+/ {
+ model = "Qualcomm Technologies, Inc. SDA450 + PMI632 CDP S2";
+ compatible = "qcom,sda450-cdp", "qcom,sda450", "qcom,cdp";
+ qcom,board-id = <1 2>;
+ qcom,pmic-id = <0x010016 0x25 0x0 0x0>;
+};
+
diff --git a/arch/arm64/boot/dts/qcom/sda450-pmi632-mtp-s3.dts b/arch/arm64/boot/dts/qcom/sda450-pmi632-mtp-s3.dts
new file mode 100644
index 0000000..c907977
--- /dev/null
+++ b/arch/arm64/boot/dts/qcom/sda450-pmi632-mtp-s3.dts
@@ -0,0 +1,26 @@
+/*
+ * Copyright (c) 2018, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+/dts-v1/;
+
+#include "sda450.dtsi"
+#include "sdm450-pmi632-mtp-s3.dtsi"
+#include "sdm450-pmi632.dtsi"
+
+/ {
+ model = "Qualcomm Technologies, Inc. SDA450 + PMI632 MTP S3";
+ compatible = "qcom,sda450-mtp", "qcom,sda450", "qcom,mtp";
+ qcom,board-id = <8 3>;
+ qcom,pmic-id = <0x010016 0x25 0x0 0x0>;
+};
+
diff --git a/arch/arm64/boot/dts/qcom/sda450-pmi632.dts b/arch/arm64/boot/dts/qcom/sda450-pmi632.dts
new file mode 100644
index 0000000..1bb0b47
--- /dev/null
+++ b/arch/arm64/boot/dts/qcom/sda450-pmi632.dts
@@ -0,0 +1,25 @@
+/*
+ * 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 "sda450.dtsi"
+#include "sdm450-pmi632.dtsi"
+
+/ {
+ model = "Qualcomm Technologies, Inc. SDA450 + PMI632 SOC";
+ compatible = "qcom,sda450";
+ qcom,pmic-id = <0x010016 0x25 0x0 0x0>;
+ qcom,pmic-name = "PMI632";
+};
+
diff --git a/arch/arm64/boot/dts/qcom/sda450.dts b/arch/arm64/boot/dts/qcom/sda450.dts
new file mode 100644
index 0000000..13b1622
--- /dev/null
+++ b/arch/arm64/boot/dts/qcom/sda450.dts
@@ -0,0 +1,26 @@
+/*
+ * Copyright (c) 2018, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+/dts-v1/;
+
+#include "sda450.dtsi"
+#include "pmi8950.dtsi"
+#include "msm8953-pmi8950.dtsi"
+
+/ {
+ model = "Qualcomm Technologies, Inc. SDA450 + PMI8950 SOC";
+ compatible = "qcom,sda450";
+ qcom,pmic-id = <0x010016 0x010011 0x0 0x0>;
+ qcom,pmic-name = "PMI8950";
+};
+
diff --git a/arch/arm64/boot/dts/qcom/sda450.dtsi b/arch/arm64/boot/dts/qcom/sda450.dtsi
new file mode 100644
index 0000000..ba99fe9
--- /dev/null
+++ b/arch/arm64/boot/dts/qcom/sda450.dtsi
@@ -0,0 +1,22 @@
+/*
+ * Copyright (c) 2018, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include "sdm450.dtsi"
+
+/ {
+ model = "Qualcomm Technologies, Inc. SDA450";
+ compatible = "qcom,sda450";
+ qcom,msm-id = <351 0x0>;
+ qcom,msm-name = "SDA450";
+};
+
diff --git a/arch/arm64/boot/dts/qcom/sdm439-pmi632.dtsi b/arch/arm64/boot/dts/qcom/sdm439-pmi632.dtsi
index 5075862..300c83a 100644
--- a/arch/arm64/boot/dts/qcom/sdm439-pmi632.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm439-pmi632.dtsi
@@ -39,6 +39,10 @@
qcom,battery-data = <&mtp_batterydata>;
};
+&pmi632_charger {
+ qcom,battery-data = <&mtp_batterydata>;
+};
+
&pm8953_typec {
status = "disabled";
};
diff --git a/arch/arm64/boot/dts/qcom/sdm450-pmi632.dtsi b/arch/arm64/boot/dts/qcom/sdm450-pmi632.dtsi
index a4b6054..5c127bc 100644
--- a/arch/arm64/boot/dts/qcom/sdm450-pmi632.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm450-pmi632.dtsi
@@ -45,6 +45,10 @@
qcom,battery-data = <&mtp_batterydata>;
};
+&pmi632_charger {
+ qcom,battery-data = <&mtp_batterydata>;
+};
+
&pm8953_gpios {
bklt_en {
bklt_en_default: bklt_en_default {
diff --git a/arch/arm64/configs/sdm845-perf_defconfig b/arch/arm64/configs/sdm845-perf_defconfig
index 8f213f4..f5dc2a8 100644
--- a/arch/arm64/configs/sdm845-perf_defconfig
+++ b/arch/arm64/configs/sdm845-perf_defconfig
@@ -545,7 +545,6 @@
CONFIG_QTI_RPM_STATS_LOG=y
CONFIG_QCOM_FORCE_WDOG_BITE_ON_PANIC=y
CONFIG_QMP_DEBUGFS_CLIENT=y
-CONFIG_MEM_SHARE_QMI_SERVICE=y
CONFIG_QSEE_IPC_IRQ_BRIDGE=y
CONFIG_QCOM_BIMC_BWMON=y
CONFIG_ARM_MEMLAT_MON=y
diff --git a/arch/arm64/configs/sdm845_defconfig b/arch/arm64/configs/sdm845_defconfig
index ac25fb9..8a84a2d 100644
--- a/arch/arm64/configs/sdm845_defconfig
+++ b/arch/arm64/configs/sdm845_defconfig
@@ -564,7 +564,6 @@
CONFIG_QTI_RPM_STATS_LOG=y
CONFIG_QCOM_FORCE_WDOG_BITE_ON_PANIC=y
CONFIG_QMP_DEBUGFS_CLIENT=y
-CONFIG_MEM_SHARE_QMI_SERVICE=y
CONFIG_MSM_REMOTEQDSS=y
CONFIG_QSEE_IPC_IRQ_BRIDGE=y
CONFIG_QCOM_BIMC_BWMON=y
diff --git a/drivers/char/diag/diag_dci.c b/drivers/char/diag/diag_dci.c
index a469eb9..6861cd5 100644
--- a/drivers/char/diag/diag_dci.c
+++ b/drivers/char/diag/diag_dci.c
@@ -26,6 +26,7 @@
#include <linux/reboot.h>
#include <asm/current.h>
#include <soc/qcom/restart.h>
+#include <linux/vmalloc.h>
#ifdef CONFIG_DIAG_OVER_USB
#include <linux/usb/usbdiag.h>
#endif
@@ -259,7 +260,7 @@
switch (type) {
case DCI_BUF_PRIMARY:
buffer->capacity = IN_BUF_SIZE;
- buffer->data = kzalloc(buffer->capacity, GFP_KERNEL);
+ buffer->data = vzalloc(buffer->capacity);
if (!buffer->data)
return -ENOMEM;
break;
@@ -269,7 +270,7 @@
break;
case DCI_BUF_CMD:
buffer->capacity = DIAG_MAX_REQ_SIZE + DCI_BUF_SIZE;
- buffer->data = kzalloc(buffer->capacity, GFP_KERNEL);
+ buffer->data = vzalloc(buffer->capacity);
if (!buffer->data)
return -ENOMEM;
break;
@@ -2726,7 +2727,7 @@
create_dci_event_mask_tbl(temp->event_mask_composite);
}
- partial_pkt.data = kzalloc(MAX_DCI_PACKET_SZ, GFP_KERNEL);
+ partial_pkt.data = vzalloc(MAX_DCI_PACKET_SZ);
if (!partial_pkt.data)
return -ENOMEM;
@@ -2780,7 +2781,7 @@
goto err;
if (driver->apps_dci_buf == NULL) {
- driver->apps_dci_buf = kzalloc(DCI_BUF_SIZE, GFP_KERNEL);
+ driver->apps_dci_buf = vzalloc(DCI_BUF_SIZE);
if (driver->apps_dci_buf == NULL)
goto err;
}
@@ -2797,12 +2798,12 @@
return DIAG_DCI_NO_ERROR;
err:
pr_err("diag: Could not initialize diag DCI buffers");
- kfree(driver->apps_dci_buf);
+ vfree(driver->apps_dci_buf);
driver->apps_dci_buf = NULL;
if (driver->diag_dci_wq)
destroy_workqueue(driver->diag_dci_wq);
- kfree(partial_pkt.data);
+ vfree(partial_pkt.data);
partial_pkt.data = NULL;
mutex_destroy(&driver->dci_mutex);
mutex_destroy(&dci_log_mask_mutex);
@@ -2822,9 +2823,9 @@
void diag_dci_exit(void)
{
- kfree(partial_pkt.data);
+ vfree(partial_pkt.data);
partial_pkt.data = NULL;
- kfree(driver->apps_dci_buf);
+ vfree(driver->apps_dci_buf);
driver->apps_dci_buf = NULL;
mutex_destroy(&driver->dci_mutex);
mutex_destroy(&dci_log_mask_mutex);
@@ -2962,7 +2963,7 @@
new_entry->in_service = 0;
INIT_LIST_HEAD(&new_entry->list_write_buf);
mutex_init(&new_entry->write_buf_mutex);
- new_entry->dci_log_mask = kzalloc(DCI_LOG_MASK_SIZE, GFP_KERNEL);
+ new_entry->dci_log_mask = vzalloc(DCI_LOG_MASK_SIZE);
if (!new_entry->dci_log_mask) {
pr_err("diag: Unable to create log mask for client, %d",
driver->dci_client_id);
@@ -2970,14 +2971,14 @@
}
create_dci_log_mask_tbl(new_entry->dci_log_mask, DCI_LOG_MASK_CLEAN);
- new_entry->dci_event_mask = kzalloc(DCI_EVENT_MASK_SIZE, GFP_KERNEL);
+ new_entry->dci_event_mask = vzalloc(DCI_EVENT_MASK_SIZE);
if (!new_entry->dci_event_mask)
goto fail_alloc;
create_dci_event_mask_tbl(new_entry->dci_event_mask);
new_entry->buffers = kzalloc(new_entry->num_buffers *
sizeof(struct diag_dci_buf_peripheral_t),
- GFP_KERNEL);
+ GFP_KERNEL);
if (!new_entry->buffers) {
pr_err("diag: Unable to allocate buffers for peripherals in %s\n",
__func__);
@@ -3001,7 +3002,7 @@
if (!proc_buf->buf_primary)
goto fail_alloc;
proc_buf->buf_cmd = kzalloc(sizeof(struct diag_dci_buffer_t),
- GFP_KERNEL);
+ GFP_KERNEL);
if (!proc_buf->buf_cmd)
goto fail_alloc;
err = diag_dci_init_buffer(proc_buf->buf_primary,
@@ -3034,7 +3035,7 @@
if (proc_buf) {
mutex_destroy(&proc_buf->health_mutex);
if (proc_buf->buf_primary) {
- kfree(proc_buf->buf_primary->data);
+ vfree(proc_buf->buf_primary->data);
proc_buf->buf_primary->data = NULL;
mutex_destroy(
&proc_buf->buf_primary->data_mutex);
@@ -3042,7 +3043,7 @@
kfree(proc_buf->buf_primary);
proc_buf->buf_primary = NULL;
if (proc_buf->buf_cmd) {
- kfree(proc_buf->buf_cmd->data);
+ vfree(proc_buf->buf_cmd->data);
proc_buf->buf_cmd->data = NULL;
mutex_destroy(
&proc_buf->buf_cmd->data_mutex);
@@ -3051,9 +3052,9 @@
proc_buf->buf_cmd = NULL;
}
}
- kfree(new_entry->dci_event_mask);
+ vfree(new_entry->dci_event_mask);
new_entry->dci_event_mask = NULL;
- kfree(new_entry->dci_log_mask);
+ vfree(new_entry->dci_log_mask);
new_entry->dci_log_mask = NULL;
kfree(new_entry->buffers);
new_entry->buffers = NULL;
@@ -3088,7 +3089,7 @@
* Clear the client's log and event masks, update the cumulative
* masks and send the masks to peripherals
*/
- kfree(entry->dci_log_mask);
+ vfree(entry->dci_log_mask);
entry->dci_log_mask = NULL;
diag_dci_invalidate_cumulative_log_mask(token);
if (token == DCI_LOCAL_PROC)
@@ -3096,7 +3097,7 @@
ret = dci_ops_tbl[token].send_log_mask(token);
if (ret != DIAG_DCI_NO_ERROR)
return ret;
- kfree(entry->dci_event_mask);
+ vfree(entry->dci_event_mask);
entry->dci_event_mask = NULL;
diag_dci_invalidate_cumulative_event_mask(token);
if (token == DCI_LOCAL_PROC)
@@ -3159,12 +3160,12 @@
}
mutex_lock(&proc_buf->buf_primary->data_mutex);
- kfree(proc_buf->buf_primary->data);
+ vfree(proc_buf->buf_primary->data);
proc_buf->buf_primary->data = NULL;
mutex_unlock(&proc_buf->buf_primary->data_mutex);
mutex_lock(&proc_buf->buf_cmd->data_mutex);
- kfree(proc_buf->buf_cmd->data);
+ vfree(proc_buf->buf_cmd->data);
proc_buf->buf_cmd->data = NULL;
mutex_unlock(&proc_buf->buf_cmd->data_mutex);
diff --git a/drivers/gpu/msm/adreno_a6xx_preempt.c b/drivers/gpu/msm/adreno_a6xx_preempt.c
index 97b0cb2..6c8e664 100644
--- a/drivers/gpu/msm/adreno_a6xx_preempt.c
+++ b/drivers/gpu/msm/adreno_a6xx_preempt.c
@@ -749,6 +749,9 @@
if (context->flags & KGSL_CONTEXT_SECURE)
flags |= KGSL_MEMFLAGS_SECURE;
+ if (kgsl_is_compat_task())
+ flags |= KGSL_MEMFLAGS_FORCE_32BIT;
+
/*
* gpumem_alloc_entry takes an extra refcount. Put it only when
* destroying the context to keep the context record valid
diff --git a/drivers/gpu/msm/kgsl.c b/drivers/gpu/msm/kgsl.c
index 2e617429..bb9f9ff 100644
--- a/drivers/gpu/msm/kgsl.c
+++ b/drivers/gpu/msm/kgsl.c
@@ -2402,6 +2402,9 @@
if (!MMU_FEATURE(mmu, KGSL_MMU_IO_COHERENT))
param->flags &= ~((uint64_t)KGSL_MEMFLAGS_IOCOHERENT);
+ if (kgsl_is_compat_task())
+ param->flags |= KGSL_MEMFLAGS_FORCE_32BIT;
+
entry->memdesc.flags = param->flags;
if (MMU_FEATURE(mmu, KGSL_MMU_NEED_GUARD_PAGE))
@@ -2692,8 +2695,10 @@
if (!MMU_FEATURE(mmu, KGSL_MMU_IO_COHERENT))
param->flags &= ~((uint64_t)KGSL_MEMFLAGS_IOCOHERENT);
- entry->memdesc.flags = ((uint64_t) param->flags)
- | KGSL_MEMFLAGS_FORCE_32BIT;
+ entry->memdesc.flags = (uint64_t) param->flags;
+
+ if (kgsl_is_compat_task())
+ entry->memdesc.flags |= KGSL_MEMFLAGS_FORCE_32BIT;
if (!kgsl_mmu_use_cpu_map(mmu))
entry->memdesc.flags &= ~((uint64_t) KGSL_MEMFLAGS_USE_CPU_MAP);
@@ -3197,6 +3202,9 @@
struct kgsl_gpuobj_alloc *param = data;
struct kgsl_mem_entry *entry;
+ if (kgsl_is_compat_task())
+ param->flags |= KGSL_MEMFLAGS_FORCE_32BIT;
+
entry = gpumem_alloc_entry(dev_priv, param->size, param->flags);
if (IS_ERR(entry))
@@ -3224,7 +3232,9 @@
/* Legacy functions doesn't support these advanced features */
flags &= ~((uint64_t) KGSL_MEMFLAGS_USE_CPU_MAP);
- flags |= KGSL_MEMFLAGS_FORCE_32BIT;
+
+ if (kgsl_is_compat_task())
+ flags |= KGSL_MEMFLAGS_FORCE_32BIT;
entry = gpumem_alloc_entry(dev_priv, (uint64_t) param->size, flags);
@@ -3248,7 +3258,8 @@
struct kgsl_mem_entry *entry;
uint64_t flags = param->flags;
- flags |= KGSL_MEMFLAGS_FORCE_32BIT;
+ if (kgsl_is_compat_task())
+ flags |= KGSL_MEMFLAGS_FORCE_32BIT;
entry = gpumem_alloc_entry(dev_priv, (uint64_t) param->size, flags);
diff --git a/drivers/gpu/msm/kgsl.h b/drivers/gpu/msm/kgsl.h
index f4a2de5..1fa2717 100644
--- a/drivers/gpu/msm/kgsl.h
+++ b/drivers/gpu/msm/kgsl.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2008-2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2008-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
@@ -27,6 +27,7 @@
#include <linux/uaccess.h>
#include <linux/kthread.h>
#include <asm/cacheflush.h>
+#include <linux/compat.h>
/*
* --- kgsl drawobj flags ---
@@ -628,4 +629,9 @@
kernfs_create_link(dst->sd, dst_name, old);
}
+
+static inline bool kgsl_is_compat_task(void)
+{
+ return (BITS_PER_LONG == 32) || is_compat_task();
+}
#endif /* __KGSL_H */
diff --git a/drivers/gpu/msm/kgsl_iommu.c b/drivers/gpu/msm/kgsl_iommu.c
index c4296c8..0ce72f6 100644
--- a/drivers/gpu/msm/kgsl_iommu.c
+++ b/drivers/gpu/msm/kgsl_iommu.c
@@ -1052,7 +1052,7 @@
if (pagetable->name != KGSL_MMU_GLOBAL_PT &&
pagetable->name != KGSL_MMU_SECURE_PT) {
- if ((BITS_PER_LONG == 32) || is_compat_task()) {
+ if (kgsl_is_compat_task()) {
pt->svm_start = KGSL_IOMMU_SVM_BASE32;
pt->svm_end = KGSL_IOMMU_SECURE_BASE(mmu);
} else {
diff --git a/drivers/input/misc/qpnp-power-on.c b/drivers/input/misc/qpnp-power-on.c
index 36d07c5..93c28ef 100644
--- a/drivers/input/misc/qpnp-power-on.c
+++ b/drivers/input/misc/qpnp-power-on.c
@@ -31,6 +31,8 @@
#include <linux/regulator/machine.h>
#include <linux/regulator/of_regulator.h>
#include <linux/input/qpnp-power-on.h>
+#include <linux/qpnp/qpnp-pbs.h>
+#include <linux/qpnp/qpnp-misc.h>
#include <linux/power_supply.h>
#define PMIC_VER_8941 0x01
@@ -203,6 +205,7 @@
struct list_head list;
struct delayed_work bark_work;
struct dentry *debugfs;
+ struct device_node *pbs_dev_node;
int pon_trigger_reason;
int pon_power_off_reason;
int num_pon_reg;
@@ -220,6 +223,7 @@
u8 pon_ver;
u8 warm_reset_reason1;
u8 warm_reset_reason2;
+ u8 twm_state;
bool is_spon;
bool store_hard_reset_reason;
bool resin_hard_reset_disable;
@@ -227,8 +231,10 @@
bool ps_hold_hard_reset_disable;
bool ps_hold_shutdown_disable;
bool kpdpwr_dbc_enable;
+ bool support_twm_config;
bool resin_pon_reset;
ktime_t kpdpwr_last_release_time;
+ struct notifier_block pon_nb;
bool legacy_hard_reset_offset;
};
@@ -483,6 +489,7 @@
static DEVICE_ATTR(debounce_us, 0664, qpnp_pon_dbc_show, qpnp_pon_dbc_store);
+#define PON_TWM_ENTRY_PBS_BIT BIT(0)
static int qpnp_pon_reset_config(struct qpnp_pon *pon,
enum pon_power_off_type type)
{
@@ -490,6 +497,19 @@
bool disable = false;
u16 rst_en_reg;
+ /* Ignore the PS_HOLD reset config if TWM ENTRY is enabled */
+ if (pon->support_twm_config && pon->twm_state == PMIC_TWM_ENABLE) {
+ rc = qpnp_pbs_trigger_event(pon->pbs_dev_node,
+ PON_TWM_ENTRY_PBS_BIT);
+ if (rc < 0) {
+ pr_err("Unable to trigger PBS trigger for TWM entry rc=%d\n",
+ rc);
+ return rc;
+ }
+ pr_crit("PMIC configured for TWM entry\n");
+ return 0;
+ }
+
if (pon->pon_ver == QPNP_PON_GEN1_V1)
rst_en_reg = QPNP_PON_PS_HOLD_RST_CTL(pon);
else
@@ -2087,6 +2107,35 @@
return 0;
}
+static int pon_twm_notifier_cb(struct notifier_block *nb,
+ unsigned long action, void *data)
+{
+ struct qpnp_pon *pon = container_of(nb, struct qpnp_pon, pon_nb);
+
+ if (action != PMIC_TWM_CLEAR &&
+ action != PMIC_TWM_ENABLE) {
+ pr_debug("Unsupported option %lu\n", action);
+ return NOTIFY_OK;
+ }
+
+ pon->twm_state = (u8)action;
+ pr_debug("TWM state = %d\n", pon->twm_state);
+
+ return NOTIFY_OK;
+}
+
+static int pon_register_twm_notifier(struct qpnp_pon *pon)
+{
+ int rc;
+
+ pon->pon_nb.notifier_call = pon_twm_notifier_cb;
+ rc = qpnp_misc_twm_notifier_register(&pon->pon_nb);
+ if (rc < 0)
+ pr_err("Failed to register pon_twm_notifier_cb rc=%d\n", rc);
+
+ return rc;
+}
+
static int qpnp_pon_probe(struct platform_device *pdev)
{
struct qpnp_pon *pon;
@@ -2364,6 +2413,22 @@
goto err_out;
}
+ if (of_property_read_bool(pon->pdev->dev.of_node,
+ "qcom,support-twm-config")) {
+ pon->support_twm_config = true;
+ rc = pon_register_twm_notifier(pon);
+ if (rc < 0) {
+ pr_err("Failed to register TWM notifier rc=%d\n", rc);
+ return rc;
+ }
+ pon->pbs_dev_node = of_parse_phandle(pon->pdev->dev.of_node,
+ "qcom,pbs-client", 0);
+ if (!pon->pbs_dev_node) {
+ pr_err("Missing qcom,pbs-client property\n");
+ return -EINVAL;
+ }
+ }
+
rc = of_property_read_u32(pon->pdev->dev.of_node,
"qcom,pon-dbc-delay", &delay);
if (rc) {
diff --git a/drivers/misc/qpnp-misc.c b/drivers/misc/qpnp-misc.c
index 690b28b..eec0875 100644
--- a/drivers/misc/qpnp-misc.c
+++ b/drivers/misc/qpnp-misc.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2013-2014,2016-2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2013-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
@@ -17,6 +17,7 @@
#include <linux/regmap.h>
#include <linux/of.h>
#include <linux/of_device.h>
+#include <linux/notifier.h>
#include <linux/qpnp/qpnp-misc.h>
#define QPNP_MISC_DEV_NAME "qcom,qpnp-misc"
@@ -29,8 +30,20 @@
#define PWM_SEL_MAX 0x03
#define GP_DRIVER_EN_BIT BIT(0)
+enum twm {
+ TWM_MODE_1 = 1,
+ TWM_MODE_2,
+ TWM_MODE_3,
+};
+
+enum twm_attrib {
+ TWM_ENABLE,
+ TWM_EXIT,
+};
+
static DEFINE_MUTEX(qpnp_misc_dev_list_mutex);
static LIST_HEAD(qpnp_misc_dev_list);
+static RAW_NOTIFIER_HEAD(twm_notifier);
struct qpnp_misc_version {
u8 subtype;
@@ -55,10 +68,14 @@
struct device *dev;
struct regmap *regmap;
struct qpnp_misc_version version;
+ struct class twm_class;
+ u8 twm_mode;
u32 base;
u8 pwm_sel;
bool enable_gp_driver;
+ bool support_twm_config;
+ bool twm_enable;
};
static const struct of_device_id qpnp_misc_match_table[] = {
@@ -211,12 +228,100 @@
return __misc_irqs_available(mdev_found);
}
+#define MISC_SPARE_1 0x50
+#define MISC_SPARE_2 0x51
+#define ENABLE_TWM_MODE 0x80
+#define DISABLE_TWM_MODE 0x0
+#define TWM_EXIT_BIT BIT(0)
+static ssize_t twm_enable_store(struct class *c,
+ struct class_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct qpnp_misc_dev *mdev = container_of(c,
+ struct qpnp_misc_dev, twm_class);
+ u8 val = 0;
+ ssize_t rc = 0;
+
+ rc = kstrtou8(buf, 10, &val);
+ if (rc < 0)
+ return rc;
+
+ mdev->twm_enable = val ? true : false;
+
+ /* Notify the TWM state */
+ raw_notifier_call_chain(&twm_notifier,
+ mdev->twm_enable ? PMIC_TWM_ENABLE : PMIC_TWM_CLEAR, NULL);
+
+ return count;
+}
+
+static ssize_t twm_enable_show(struct class *c,
+ struct class_attribute *attr, char *buf)
+{
+ struct qpnp_misc_dev *mdev = container_of(c,
+ struct qpnp_misc_dev, twm_class);
+
+ return snprintf(buf, PAGE_SIZE, "%d\n", mdev->twm_enable);
+}
+
+static ssize_t twm_exit_show(struct class *c,
+ struct class_attribute *attr, char *buf)
+{
+ struct qpnp_misc_dev *mdev = container_of(c,
+ struct qpnp_misc_dev, twm_class);
+ int rc = 0;
+ u8 val = 0;
+
+ rc = qpnp_read_byte(mdev, MISC_SPARE_1, &val);
+ if (rc < 0) {
+ pr_err("Failed to read TWM enable (misc_spare_1) rc=%d\n", rc);
+ return rc;
+ }
+
+ pr_debug("TWM_EXIT (misc_spare_1) register = 0x%02x\n", val);
+
+ return snprintf(buf, PAGE_SIZE, "%d\n", !!(val & TWM_EXIT_BIT));
+}
+
+static struct class_attribute twm_attributes[] = {
+ [TWM_ENABLE] = __ATTR(twm_enable, 0644,
+ twm_enable_show, twm_enable_store),
+ [TWM_EXIT] = __ATTR(twm_exit, 0644,
+ twm_exit_show, NULL),
+ __ATTR_NULL,
+};
+
+int qpnp_misc_twm_notifier_register(struct notifier_block *nb)
+{
+ return raw_notifier_chain_register(&twm_notifier, nb);
+}
+EXPORT_SYMBOL(qpnp_misc_twm_notifier_register);
+
+int qpnp_misc_twm_notifier_unregister(struct notifier_block *nb)
+{
+ return raw_notifier_chain_unregister(&twm_notifier, nb);
+}
+EXPORT_SYMBOL(qpnp_misc_twm_notifier_unregister);
+
static int qpnp_misc_dt_init(struct qpnp_misc_dev *mdev)
{
struct device_node *node = mdev->dev->of_node;
u32 val;
int rc;
+ if (of_property_read_bool(mdev->dev->of_node,
+ "qcom,support-twm-config")) {
+ mdev->support_twm_config = true;
+ mdev->twm_mode = TWM_MODE_3;
+ rc = of_property_read_u8(mdev->dev->of_node, "qcom,twm-mode",
+ &mdev->twm_mode);
+ if (!rc && (mdev->twm_mode < TWM_MODE_1 ||
+ mdev->twm_mode > TWM_MODE_3)) {
+ pr_err("Invalid TWM mode %d\n", mdev->twm_mode);
+ return -EINVAL;
+ }
+ }
+
rc = of_property_read_u32(node, "reg", &mdev->base);
if (rc < 0 || !mdev->base) {
dev_err(mdev->dev, "Base address not defined or invalid\n");
@@ -270,6 +375,18 @@
break;
}
+ if (mdev->support_twm_config) {
+ mdev->twm_class.name = "pmic_twm",
+ mdev->twm_class.owner = THIS_MODULE,
+ mdev->twm_class.class_attrs = twm_attributes;
+
+ rc = class_register(&mdev->twm_class);
+ if (rc < 0) {
+ pr_err("Failed to register pmic_twm class rc=%d\n", rc);
+ return rc;
+ }
+ }
+
return 0;
}
@@ -283,6 +400,7 @@
return -ENOMEM;
mdev->dev = &pdev->dev;
+ dev_set_drvdata(&pdev->dev, mdev);
mdev->regmap = dev_get_regmap(mdev->dev->parent, NULL);
if (!mdev->regmap) {
dev_err(mdev->dev, "Parent regmap is unavailable\n");
@@ -325,8 +443,33 @@
return 0;
}
+static void qpnp_misc_shutdown(struct platform_device *pdev)
+{
+ struct qpnp_misc_dev *mdev = dev_get_drvdata(&pdev->dev);
+ int rc;
+
+ if (mdev->support_twm_config) {
+ rc = qpnp_write_byte(mdev, MISC_SPARE_2,
+ mdev->twm_enable ? mdev->twm_mode : 0x0);
+ if (rc < 0)
+ pr_err("Failed to write MISC_SPARE_2 (twm_mode) val=%d rc=%d\n",
+ mdev->twm_enable ? mdev->twm_mode : 0x0, rc);
+
+ rc = qpnp_write_byte(mdev, MISC_SPARE_1,
+ mdev->twm_enable ? ENABLE_TWM_MODE : 0x0);
+ if (rc < 0)
+ pr_err("Failed to write MISC_SPARE_1 (twm_state) val=%d rc=%d\n",
+ mdev->twm_enable ? ENABLE_TWM_MODE : 0x0, rc);
+
+ pr_debug("PMIC configured for TWM-%s MODE=%d\n",
+ mdev->twm_enable ? "enabled" : "disabled",
+ mdev->twm_mode);
+ }
+}
+
static struct platform_driver qpnp_misc_driver = {
.probe = qpnp_misc_probe,
+ .shutdown = qpnp_misc_shutdown,
.driver = {
.name = QPNP_MISC_DEV_NAME,
.owner = THIS_MODULE,
diff --git a/drivers/net/wireless/cnss2/debug.c b/drivers/net/wireless/cnss2/debug.c
index a3cf6c2..b1fbbd8 100644
--- a/drivers/net/wireless/cnss2/debug.c
+++ b/drivers/net/wireless/cnss2/debug.c
@@ -137,11 +137,17 @@
{
struct cnss_plat_data *plat_priv =
((struct seq_file *)fp->private_data)->private;
+ struct cnss_pci_data *pci_priv;
char buf[64];
char *cmd;
unsigned int len = 0;
int ret = 0;
+ if (!plat_priv)
+ return -ENODEV;
+
+ pci_priv = plat_priv->bus_priv;
+
len = min(count, sizeof(buf) - 1);
if (copy_from_user(buf, user_buf, len))
return -EFAULT;
@@ -157,11 +163,11 @@
ret = cnss_pci_init(plat_priv);
} else if (sysfs_streq(cmd, "download")) {
set_bit(CNSS_DRIVER_DEBUG, &plat_priv->driver_state);
- ret = cnss_pci_start_mhi(plat_priv->bus_priv);
+ ret = cnss_pci_start_mhi(pci_priv);
} else if (sysfs_streq(cmd, "linkup")) {
- ret = cnss_resume_pci_link(plat_priv->bus_priv);
+ ret = cnss_resume_pci_link(pci_priv);
} else if (sysfs_streq(cmd, "linkdown")) {
- ret = cnss_suspend_pci_link(plat_priv->bus_priv);
+ ret = cnss_suspend_pci_link(pci_priv);
} else if (sysfs_streq(cmd, "powerup")) {
set_bit(CNSS_DRIVER_DEBUG, &plat_priv->driver_state);
ret = cnss_driver_event_post(plat_priv,
@@ -172,6 +178,8 @@
CNSS_DRIVER_EVENT_POWER_DOWN,
0, NULL);
clear_bit(CNSS_DRIVER_DEBUG, &plat_priv->driver_state);
+ } else if (sysfs_streq(cmd, "assert")) {
+ ret = cnss_force_fw_assert(&pci_priv->pci_dev->dev);
} else {
cnss_pr_err("Device boot debugfs command is invalid\n");
ret = -EINVAL;
@@ -195,6 +203,7 @@
seq_puts(s, "linkdown: bring down PCIe link\n");
seq_puts(s, "powerup: full power on sequence to boot device, download FW and do QMI handshake with FW\n");
seq_puts(s, "shutdown: full power off sequence to shutdown device\n");
+ seq_puts(s, "assert: trigger firmware assert\n");
return 0;
}
diff --git a/drivers/net/wireless/cnss2/pci.c b/drivers/net/wireless/cnss2/pci.c
index 098631e..9d1fbf9 100644
--- a/drivers/net/wireless/cnss2/pci.c
+++ b/drivers/net/wireless/cnss2/pci.c
@@ -1458,8 +1458,7 @@
ret = mhi_pm_resume(pci_priv->mhi_ctrl);
break;
case CNSS_MHI_TRIGGER_RDDM:
- cnss_pr_dbg("Bypass MHI state: %s(%d)\n",
- cnss_mhi_state_to_str(mhi_state), mhi_state);
+ ret = mhi_force_rddm_mode(pci_priv->mhi_ctrl);
break;
default:
cnss_pr_err("Unhandled MHI state (%d)\n", mhi_state);
diff --git a/drivers/power/supply/power_supply_sysfs.c b/drivers/power/supply/power_supply_sysfs.c
index d675e46..4d4b4cf 100644
--- a/drivers/power/supply/power_supply_sysfs.c
+++ b/drivers/power/supply/power_supply_sysfs.c
@@ -337,6 +337,7 @@
POWER_SUPPLY_ATTR(manufacturer),
POWER_SUPPLY_ATTR(serial_number),
POWER_SUPPLY_ATTR(battery_type),
+ POWER_SUPPLY_ATTR(cycle_counts),
};
static struct attribute *
diff --git a/drivers/power/supply/qcom/fg-alg.c b/drivers/power/supply/qcom/fg-alg.c
new file mode 100644
index 0000000..3d74f7e
--- /dev/null
+++ b/drivers/power/supply/qcom/fg-alg.c
@@ -0,0 +1,606 @@
+/* Copyright (c) 2018, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#define pr_fmt(fmt) "ALG: %s: " fmt, __func__
+
+#include <linux/err.h>
+#include <linux/kernel.h>
+#include <linux/mutex.h>
+#include <linux/power_supply.h>
+#include "fg-alg.h"
+
+#define FULL_SOC_RAW 255
+#define CAPACITY_DELTA_DECIPCT 500
+
+/* Cycle counter APIs */
+
+/**
+ * restore_cycle_count -
+ * @counter: Cycle counter object
+ *
+ * Restores all the counters back from FG/QG during boot
+ *
+ */
+int restore_cycle_count(struct cycle_counter *counter)
+{
+ int rc = 0;
+
+ if (!counter)
+ return -ENODEV;
+
+ mutex_lock(&counter->lock);
+ rc = counter->restore_count(counter->data, counter->count,
+ BUCKET_COUNT);
+ if (rc < 0)
+ pr_err("failed to restore cycle counter rc=%d\n", rc);
+ mutex_unlock(&counter->lock);
+
+ return rc;
+}
+
+/**
+ * clear_cycle_count -
+ * @counter: Cycle counter object
+ *
+ * Clears all the counters stored by FG/QG when a battery is inserted
+ * or the profile is re-loaded.
+ *
+ */
+void clear_cycle_count(struct cycle_counter *counter)
+{
+ int rc = 0, i;
+
+ if (!counter)
+ return;
+
+ mutex_lock(&counter->lock);
+ memset(counter->count, 0, sizeof(counter->count));
+ for (i = 0; i < BUCKET_COUNT; i++) {
+ counter->started[i] = false;
+ counter->last_soc[i] = 0;
+ }
+
+ rc = counter->store_count(counter->data, counter->count, 0,
+ BUCKET_COUNT * 2);
+ if (rc < 0)
+ pr_err("failed to clear cycle counter rc=%d\n", rc);
+
+ mutex_unlock(&counter->lock);
+}
+
+/**
+ * store_cycle_count -
+ * @counter: Cycle counter object
+ * @id: Cycle counter bucket id
+ *
+ * Stores the cycle counter for a bucket in FG/QG.
+ *
+ */
+static int store_cycle_count(struct cycle_counter *counter, int id)
+{
+ int rc = 0;
+ u16 cyc_count;
+
+ if (!counter)
+ return -ENODEV;
+
+ if (id < 0 || (id > BUCKET_COUNT - 1)) {
+ pr_err("Invalid id %d\n", id);
+ return -EINVAL;
+ }
+
+ cyc_count = counter->count[id];
+ cyc_count++;
+
+ rc = counter->store_count(counter->data, &cyc_count, id, 2);
+ if (rc < 0) {
+ pr_err("failed to write cycle_count[%d] rc=%d\n",
+ id, rc);
+ return rc;
+ }
+
+ counter->count[id] = cyc_count;
+ pr_debug("Stored count %d in id %d\n", cyc_count, id);
+
+ return rc;
+}
+
+/**
+ * cycle_count_update -
+ * @counter: Cycle counter object
+ * @batt_soc: Battery State of Charge (SOC)
+ * @charge_status: Charging status from power supply
+ * @charge_done: Indicator for charge termination
+ * @input_present: Indicator for input presence
+ *
+ * Called by FG/QG whenever there is a state change (Charging status, SOC)
+ *
+ */
+void cycle_count_update(struct cycle_counter *counter, int batt_soc,
+ int charge_status, bool charge_done, bool input_present)
+{
+ int rc = 0, id, i, soc_thresh;
+
+ if (!counter)
+ return;
+
+ mutex_lock(&counter->lock);
+
+ /* Find out which id the SOC falls in */
+ id = batt_soc / BUCKET_SOC_PCT;
+
+ if (charge_status == POWER_SUPPLY_STATUS_CHARGING) {
+ if (!counter->started[id] && id != counter->last_bucket) {
+ counter->started[id] = true;
+ counter->last_soc[id] = batt_soc;
+ }
+ } else if (charge_done || !input_present) {
+ for (i = 0; i < BUCKET_COUNT; i++) {
+ soc_thresh = counter->last_soc[i] + BUCKET_SOC_PCT / 2;
+ if (counter->started[i] && batt_soc > soc_thresh) {
+ rc = store_cycle_count(counter, i);
+ if (rc < 0)
+ pr_err("Error in storing cycle_ctr rc: %d\n",
+ rc);
+ counter->last_soc[i] = 0;
+ counter->started[i] = false;
+ counter->last_bucket = i;
+ }
+ }
+ }
+
+ pr_debug("batt_soc: %d id: %d chg_status: %d\n", batt_soc, id,
+ charge_status);
+ mutex_unlock(&counter->lock);
+}
+
+/**
+ * get_cycle_count -
+ * @counter: Cycle counter object
+ *
+ * Returns the cycle counter for a SOC bucket.
+ *
+ */
+int get_cycle_count(struct cycle_counter *counter)
+{
+ int count;
+
+ if (!counter)
+ return 0;
+
+ if ((counter->id <= 0) || (counter->id > BUCKET_COUNT))
+ return -EINVAL;
+
+ mutex_lock(&counter->lock);
+ count = counter->count[counter->id - 1];
+ mutex_unlock(&counter->lock);
+ return count;
+}
+
+/**
+ * cycle_count_init -
+ * @counter: Cycle counter object
+ *
+ * FG/QG have to call this during driver probe to validate the required
+ * parameters after allocating cycle_counter object.
+ *
+ */
+int cycle_count_init(struct cycle_counter *counter)
+{
+ if (!counter)
+ return -ENODEV;
+
+ if (!counter->data || !counter->restore_count ||
+ !counter->store_count) {
+ pr_err("Invalid parameters for using cycle counter\n");
+ return -EINVAL;
+ }
+
+ mutex_init(&counter->lock);
+ counter->last_bucket = -1;
+ return 0;
+}
+
+/* Capacity learning algorithm APIs */
+
+/**
+ * cap_learning_post_process -
+ * @cl: Capacity learning object
+ *
+ * Does post processing on the learnt capacity based on the user specified
+ * or default parameters for the capacity learning algorithm.
+ *
+ */
+static void cap_learning_post_process(struct cap_learning *cl)
+{
+ int64_t max_inc_val, min_dec_val, old_cap;
+ int rc;
+
+ if (cl->dt.skew_decipct) {
+ pr_debug("applying skew %d on current learnt capacity %lld\n",
+ cl->dt.skew_decipct, cl->final_cap_uah);
+ cl->final_cap_uah = cl->final_cap_uah *
+ (1000 + cl->dt.skew_decipct);
+ cl->final_cap_uah = div64_u64(cl->final_cap_uah, 1000);
+ }
+
+ max_inc_val = cl->learned_cap_uah * (1000 + cl->dt.max_cap_inc);
+ max_inc_val = div64_u64(max_inc_val, 1000);
+
+ min_dec_val = cl->learned_cap_uah * (1000 - cl->dt.max_cap_dec);
+ min_dec_val = div64_u64(min_dec_val, 1000);
+
+ old_cap = cl->learned_cap_uah;
+ if (cl->final_cap_uah > max_inc_val)
+ cl->learned_cap_uah = max_inc_val;
+ else if (cl->final_cap_uah < min_dec_val)
+ cl->learned_cap_uah = min_dec_val;
+ else
+ cl->learned_cap_uah = cl->final_cap_uah;
+
+ if (cl->dt.max_cap_limit) {
+ max_inc_val = (int64_t)cl->nom_cap_uah * (1000 +
+ cl->dt.max_cap_limit);
+ max_inc_val = div64_u64(max_inc_val, 1000);
+ if (cl->final_cap_uah > max_inc_val) {
+ pr_debug("learning capacity %lld goes above max limit %lld\n",
+ cl->final_cap_uah, max_inc_val);
+ cl->learned_cap_uah = max_inc_val;
+ }
+ }
+
+ if (cl->dt.min_cap_limit) {
+ min_dec_val = (int64_t)cl->nom_cap_uah * (1000 -
+ cl->dt.min_cap_limit);
+ min_dec_val = div64_u64(min_dec_val, 1000);
+ if (cl->final_cap_uah < min_dec_val) {
+ pr_debug("learning capacity %lld goes below min limit %lld\n",
+ cl->final_cap_uah, min_dec_val);
+ cl->learned_cap_uah = min_dec_val;
+ }
+ }
+
+ if (cl->store_learned_capacity) {
+ rc = cl->store_learned_capacity(cl->data, cl->learned_cap_uah);
+ if (rc < 0)
+ pr_err("Error in storing learned_cap_uah, rc=%d\n", rc);
+ }
+
+ pr_debug("final cap_uah = %lld, learned capacity %lld -> %lld uah\n",
+ cl->final_cap_uah, old_cap, cl->learned_cap_uah);
+}
+
+/**
+ * cap_learning_process_full_data -
+ * @cl: Capacity learning object
+ *
+ * Processes the coulomb counter during charge termination and calculates the
+ * delta w.r.to the coulomb counter obtained earlier when the learning begun.
+ *
+ */
+static int cap_learning_process_full_data(struct cap_learning *cl)
+{
+ int rc, cc_soc_sw, cc_soc_delta_pct;
+ int64_t delta_cap_uah;
+
+ rc = cl->get_cc_soc(cl->data, &cc_soc_sw);
+ if (rc < 0) {
+ pr_err("Error in getting CC_SOC_SW, rc=%d\n", rc);
+ return rc;
+ }
+
+ cc_soc_delta_pct =
+ div64_s64((int64_t)(cc_soc_sw - cl->init_cc_soc_sw) * 100,
+ cl->cc_soc_max);
+
+ /* If the delta is < 50%, then skip processing full data */
+ if (cc_soc_delta_pct < 50) {
+ pr_err("cc_soc_delta_pct: %d\n", cc_soc_delta_pct);
+ return -ERANGE;
+ }
+
+ delta_cap_uah = div64_s64(cl->learned_cap_uah * cc_soc_delta_pct, 100);
+ cl->final_cap_uah = cl->init_cap_uah + delta_cap_uah;
+ pr_debug("Current cc_soc=%d cc_soc_delta_pct=%d total_cap_uah=%lld\n",
+ cc_soc_sw, cc_soc_delta_pct, cl->final_cap_uah);
+ return 0;
+}
+
+/**
+ * cap_learning_begin -
+ * @cl: Capacity learning object
+ * @batt_soc: Battery State of Charge (SOC)
+ *
+ * Gets the coulomb counter from FG/QG when the conditions are suitable for
+ * beginning capacity learning. Also, primes the coulomb counter based on
+ * battery SOC if required.
+ *
+ */
+static int cap_learning_begin(struct cap_learning *cl, u32 batt_soc)
+{
+ int rc, cc_soc_sw, batt_soc_msb;
+
+ batt_soc_msb = batt_soc >> 24;
+ if (DIV_ROUND_CLOSEST(batt_soc_msb * 100, FULL_SOC_RAW) >
+ cl->dt.start_soc) {
+ pr_debug("Battery SOC %d is high!, not starting\n",
+ batt_soc_msb);
+ return -EINVAL;
+ }
+
+ cl->init_cap_uah = div64_s64(cl->learned_cap_uah * batt_soc_msb,
+ FULL_SOC_RAW);
+
+ if (cl->prime_cc_soc) {
+ /*
+ * Prime cc_soc_sw with battery SOC when capacity learning
+ * begins.
+ */
+ rc = cl->prime_cc_soc(cl->data, batt_soc);
+ if (rc < 0) {
+ pr_err("Error in writing cc_soc_sw, rc=%d\n", rc);
+ goto out;
+ }
+ }
+
+ rc = cl->get_cc_soc(cl->data, &cc_soc_sw);
+ if (rc < 0) {
+ pr_err("Error in getting CC_SOC_SW, rc=%d\n", rc);
+ goto out;
+ }
+
+ cl->init_cc_soc_sw = cc_soc_sw;
+ pr_debug("Capacity learning started @ battery SOC %d init_cc_soc_sw:%d\n",
+ batt_soc_msb, cl->init_cc_soc_sw);
+out:
+ return rc;
+}
+
+/**
+ * cap_learning_done -
+ * @cl: Capacity learning object
+ *
+ * Top level function for getting coulomb counter and post processing the
+ * data once the capacity learning is complete after charge termination.
+ *
+ */
+static int cap_learning_done(struct cap_learning *cl)
+{
+ int rc;
+
+ rc = cap_learning_process_full_data(cl);
+ if (rc < 0) {
+ pr_err("Error in processing cap learning full data, rc=%d\n",
+ rc);
+ goto out;
+ }
+
+ if (cl->prime_cc_soc) {
+ /* Write a FULL value to cc_soc_sw */
+ rc = cl->prime_cc_soc(cl->data, cl->cc_soc_max);
+ if (rc < 0) {
+ pr_err("Error in writing cc_soc_sw, rc=%d\n", rc);
+ goto out;
+ }
+ }
+
+ cap_learning_post_process(cl);
+out:
+ return rc;
+}
+
+/**
+ * cap_learning_update -
+ * @cl: Capacity learning object
+ * @batt_temp - Battery temperature
+ * @batt_soc: Battery State of Charge (SOC)
+ * @charge_status: Charging status from power supply
+ * @charge_done: Indicator for charge termination
+ * @input_present: Indicator for input presence
+ * @qnovo_en: Indicator for Qnovo enable status
+ *
+ * Called by FG/QG driver when there is a state change (Charging status, SOC)
+ *
+ */
+void cap_learning_update(struct cap_learning *cl, int batt_temp,
+ int batt_soc, int charge_status, bool charge_done,
+ bool input_present, bool qnovo_en)
+{
+ int rc, batt_soc_msb, batt_soc_prime;
+ bool prime_cc = false;
+
+ if (!cl)
+ return;
+
+ mutex_lock(&cl->lock);
+
+ if (batt_temp > cl->dt.max_temp || batt_temp < cl->dt.min_temp ||
+ !cl->learned_cap_uah) {
+ cl->active = false;
+ cl->init_cap_uah = 0;
+ goto out;
+ }
+
+ batt_soc_msb = (u32)batt_soc >> 24;
+ pr_debug("Charge_status: %d active: %d batt_soc: %d\n",
+ charge_status, cl->active, batt_soc_msb);
+
+ /* Initialize the starting point of learning capacity */
+ if (!cl->active) {
+ if (charge_status == POWER_SUPPLY_STATUS_CHARGING) {
+ rc = cap_learning_begin(cl, batt_soc);
+ cl->active = (rc == 0);
+ } else {
+ if (charge_status == POWER_SUPPLY_STATUS_DISCHARGING ||
+ charge_done)
+ prime_cc = true;
+ }
+ } else {
+ if (charge_done) {
+ rc = cap_learning_done(cl);
+ if (rc < 0)
+ pr_err("Error in completing capacity learning, rc=%d\n",
+ rc);
+
+ cl->active = false;
+ cl->init_cap_uah = 0;
+ }
+
+ if (charge_status == POWER_SUPPLY_STATUS_DISCHARGING) {
+ if (!input_present) {
+ pr_debug("Capacity learning aborted @ battery SOC %d\n",
+ batt_soc_msb);
+ cl->active = false;
+ cl->init_cap_uah = 0;
+ prime_cc = true;
+ }
+ }
+
+ if (charge_status == POWER_SUPPLY_STATUS_NOT_CHARGING) {
+ if (qnovo_en && input_present) {
+ /*
+ * Don't abort the capacity learning when qnovo
+ * is enabled and input is present where the
+ * charging status can go to "not charging"
+ * intermittently.
+ */
+ } else {
+ pr_debug("Capacity learning aborted @ battery SOC %d\n",
+ batt_soc_msb);
+ cl->active = false;
+ cl->init_cap_uah = 0;
+ prime_cc = true;
+ }
+ }
+ }
+
+ /*
+ * Prime CC_SOC_SW when the device is not charging or during charge
+ * termination when the capacity learning is not active.
+ */
+
+ if (prime_cc && cl->prime_cc_soc) {
+ if (charge_done)
+ batt_soc_prime = cl->cc_soc_max;
+ else
+ batt_soc_prime = batt_soc;
+
+ rc = cl->prime_cc_soc(cl->data, batt_soc_prime);
+ if (rc < 0)
+ pr_err("Error in writing cc_soc_sw, rc=%d\n",
+ rc);
+ }
+
+out:
+ mutex_unlock(&cl->lock);
+}
+
+/**
+ * cap_learning_abort -
+ * @cl: Capacity learning object
+ *
+ * Aborts the capacity learning and initializes variables
+ *
+ */
+void cap_learning_abort(struct cap_learning *cl)
+{
+ if (!cl)
+ return;
+
+ mutex_lock(&cl->lock);
+ pr_debug("Aborting cap_learning\n");
+ cl->active = false;
+ cl->init_cap_uah = 0;
+ mutex_lock(&cl->lock);
+}
+
+/**
+ * cap_learning_post_profile_init -
+ * @cl: Capacity learning object
+ * @nom_cap_uah: Nominal capacity of battery in uAh
+ *
+ * Called by FG/QG once the profile load is complete and nominal capacity
+ * of battery is known. This also gets the last learned capacity back from
+ * FG/QG to feed back to the algorithm.
+ *
+ */
+int cap_learning_post_profile_init(struct cap_learning *cl, int64_t nom_cap_uah)
+{
+ int64_t delta_cap_uah, pct_nom_cap_uah;
+ int rc;
+
+ if (!cl || !cl->data)
+ return -EINVAL;
+
+ mutex_lock(&cl->lock);
+ cl->nom_cap_uah = nom_cap_uah;
+ rc = cl->get_learned_capacity(cl->data, &cl->learned_cap_uah);
+ if (rc < 0) {
+ pr_err("Couldn't get learned capacity, rc=%d\n", rc);
+ goto out;
+ }
+
+ if (cl->learned_cap_uah != cl->nom_cap_uah) {
+ if (cl->learned_cap_uah == 0)
+ cl->learned_cap_uah = cl->nom_cap_uah;
+
+ delta_cap_uah = abs(cl->learned_cap_uah - cl->nom_cap_uah);
+ pct_nom_cap_uah = div64_s64((int64_t)cl->nom_cap_uah *
+ CAPACITY_DELTA_DECIPCT, 1000);
+ /*
+ * If the learned capacity is out of range by 50% from the
+ * nominal capacity, then overwrite the learned capacity with
+ * the nominal capacity.
+ */
+ if (cl->nom_cap_uah && delta_cap_uah > pct_nom_cap_uah) {
+ pr_debug("learned_cap_uah: %lld is higher than expected, capping it to nominal: %lld\n",
+ cl->learned_cap_uah, cl->nom_cap_uah);
+ cl->learned_cap_uah = cl->nom_cap_uah;
+ }
+
+ rc = cl->store_learned_capacity(cl->data, cl->learned_cap_uah);
+ if (rc < 0)
+ pr_err("Error in storing learned_cap_uah, rc=%d\n", rc);
+ }
+
+out:
+ mutex_unlock(&cl->lock);
+ return rc;
+}
+
+/**
+ * cap_learning_init -
+ * @cl: Capacity learning object
+ *
+ * FG/QG have to call this during driver probe to validate the required
+ * parameters after allocating cap_learning object.
+ *
+ */
+int cap_learning_init(struct cap_learning *cl)
+{
+ if (!cl)
+ return -ENODEV;
+
+ if (!cl->get_learned_capacity || !cl->store_learned_capacity ||
+ !cl->get_cc_soc) {
+ pr_err("Insufficient functions for supporting capacity learning\n");
+ return -EINVAL;
+ }
+
+ if (!cl->cc_soc_max) {
+ pr_err("Insufficient parameters for supporting capacity learning\n");
+ return -EINVAL;
+ }
+
+ mutex_init(&cl->lock);
+ return 0;
+}
diff --git a/drivers/power/supply/qcom/fg-alg.h b/drivers/power/supply/qcom/fg-alg.h
new file mode 100644
index 0000000..ff5bece
--- /dev/null
+++ b/drivers/power/supply/qcom/fg-alg.h
@@ -0,0 +1,73 @@
+/* Copyright (c) 2018, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef __FG_ALG_H__
+#define __FG_ALG_H__
+
+#define BUCKET_COUNT 8
+#define BUCKET_SOC_PCT (256 / BUCKET_COUNT)
+
+struct cycle_counter {
+ void *data;
+ bool started[BUCKET_COUNT];
+ u16 count[BUCKET_COUNT];
+ u8 last_soc[BUCKET_COUNT];
+ int id;
+ int last_bucket;
+ struct mutex lock;
+ int (*restore_count)(void *data, u16 *buf, int num_bytes);
+ int (*store_count)(void *data, u16 *buf, int id, int num_bytes);
+};
+
+struct cl_params {
+ int start_soc;
+ int max_temp;
+ int min_temp;
+ int max_cap_inc;
+ int max_cap_dec;
+ int max_cap_limit;
+ int min_cap_limit;
+ int skew_decipct;
+};
+
+struct cap_learning {
+ void *data;
+ int init_cc_soc_sw;
+ int cc_soc_max;
+ int64_t nom_cap_uah;
+ int64_t init_cap_uah;
+ int64_t final_cap_uah;
+ int64_t learned_cap_uah;
+ bool active;
+ struct mutex lock;
+ struct cl_params dt;
+ int (*get_learned_capacity)(void *data, int64_t *learned_cap_uah);
+ int (*store_learned_capacity)(void *data, int64_t learned_cap_uah);
+ int (*get_cc_soc)(void *data, int *cc_soc_sw);
+ int (*prime_cc_soc)(void *data, u32 cc_soc_sw);
+};
+
+int restore_cycle_count(struct cycle_counter *counter);
+void clear_cycle_count(struct cycle_counter *counter);
+void cycle_count_update(struct cycle_counter *counter, int batt_soc,
+ int charge_status, bool charge_done, bool input_present);
+int get_cycle_count(struct cycle_counter *counter);
+int cycle_count_init(struct cycle_counter *counter);
+void cap_learning_abort(struct cap_learning *cl);
+void cap_learning_update(struct cap_learning *cl, int batt_temp,
+ int batt_soc, int charge_status, bool charge_done,
+ bool input_present, bool qnovo_en);
+int cap_learning_init(struct cap_learning *cl);
+int cap_learning_post_profile_init(struct cap_learning *cl,
+ int64_t nom_cap_uah);
+
+#endif
diff --git a/drivers/soc/qcom/socinfo.c b/drivers/soc/qcom/socinfo.c
index a82cb43..e02bf84 100644
--- a/drivers/soc/qcom/socinfo.c
+++ b/drivers/soc/qcom/socinfo.c
@@ -605,6 +605,7 @@
/* SDM450 ID */
[338] = {MSM_CPU_SDM450, "SDM450"},
+ [351] = {MSM_CPU_SDM450, "SDA450"},
/* SDM632 ID */
[349] = {MSM_CPU_SDM632, "SDM632"},
diff --git a/drivers/thermal/tsens-dbg.c b/drivers/thermal/tsens-dbg.c
index e1fc6b9..9f128fe 100644
--- a/drivers/thermal/tsens-dbg.c
+++ b/drivers/thermal/tsens-dbg.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2017-2018, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -190,17 +190,34 @@
__ATTR(zonehist, 0644, zonehist_show, zonehist_store),
};
+static struct device_attribute tsens_mtc_dev_attr_V14[] = {
+ __ATTR(zonemask, 0644, zonemask_show, zonemask_store),
+ __ATTR(zonelog, 0644, zonelog_show, zonelog_store),
+};
+
static int tsens_dbg_mtc_data(struct tsens_device *data,
u32 id, u32 dbg_type, int *val)
{
int result = 0, i;
struct tsens_device *tmdev = NULL;
struct device_attribute *attr_ptr = NULL;
+ u32 ver_major;
+ u32 ver_minor;
+ u32 num_elem;
- attr_ptr = tsens_mtc_dev_attr;
tmdev = data;
+ ver_major = tmdev->ctrl_data->ver_major;
+ ver_minor = tmdev->ctrl_data->ver_minor;
- for (i = 0; i < ARRAY_SIZE(tsens_mtc_dev_attr); i++) {
+ if (ver_major == 1 && ver_minor == 4) {
+ attr_ptr = tsens_mtc_dev_attr_V14;
+ num_elem = ARRAY_SIZE(tsens_mtc_dev_attr_V14);
+ } else {
+ attr_ptr = tsens_mtc_dev_attr;
+ num_elem = ARRAY_SIZE(tsens_mtc_dev_attr);
+ }
+
+ for (i = 0; i < num_elem; i++) {
result = device_create_file(&tmdev->pdev->dev, &attr_ptr[i]);
if (result < 0)
goto error;
diff --git a/drivers/thermal/tsens-mtc.c b/drivers/thermal/tsens-mtc.c
index 451c6c7..6e43533 100644
--- a/drivers/thermal/tsens-mtc.c
+++ b/drivers/thermal/tsens-mtc.c
@@ -76,6 +76,8 @@
unsigned int reg_cntl;
void __iomem *sensor_addr;
struct tsens_device *tmdev = NULL;
+ u32 ver_major;
+ u32 ver_minor;
if (zone > TSENS_NUM_MTC_ZONES_SUPPORT)
return -EINVAL;
@@ -86,8 +88,16 @@
return -EPROBE_DEFER;
}
- sensor_addr = TSENS_TM_MTC_ZONE0_SW_MASK_ADDR
- (tmdev->tsens_tm_addr);
+ ver_major = tmdev->ctrl_data->ver_major;
+ ver_minor = tmdev->ctrl_data->ver_minor;
+
+ if (ver_major == 1 && ver_minor == 4) {
+ sensor_addr = TSENS_TM_MTC_ZONE0_SW_MASK_ADDR_V14
+ (tmdev->tsens_tm_addr);
+ } else {
+ sensor_addr = TSENS_TM_MTC_ZONE0_SW_MASK_ADDR
+ (tmdev->tsens_tm_addr);
+ }
if (th1_enable && th2_enable)
writel_relaxed(TSENS_MTC_IN_EFFECT,
@@ -120,6 +130,8 @@
int *zlog = (int *)zone_log;
void __iomem *sensor_addr;
struct tsens_device *tmdev = NULL;
+ u32 ver_major;
+ u32 ver_minor;
if (zone > TSENS_NUM_MTC_ZONES_SUPPORT)
return -EINVAL;
@@ -130,8 +142,13 @@
return -EPROBE_DEFER;
}
- sensor_addr = TSENS_TM_MTC_ZONE0_LOG(tmdev->tsens_tm_addr);
+ ver_major = tmdev->ctrl_data->ver_major;
+ ver_minor = tmdev->ctrl_data->ver_minor;
+ if (ver_major == 1 && ver_minor == 4)
+ sensor_addr = TSENS_TM_MTC_ZONE0_LOG_V14(tmdev->tsens_tm_addr);
+ else
+ sensor_addr = TSENS_TM_MTC_ZONE0_LOG(tmdev->tsens_tm_addr);
reg_cntl = readl_relaxed((sensor_addr +
(zone * TSENS_SN_ADDR_OFFSET)));
is_valid = (reg_cntl & TSENS_LOGS_VALID_MASK)
diff --git a/drivers/thermal/tsens-mtc.h b/drivers/thermal/tsens-mtc.h
index 8782728..b3a2c8e 100644
--- a/drivers/thermal/tsens-mtc.h
+++ b/drivers/thermal/tsens-mtc.h
@@ -15,6 +15,9 @@
#define TSENS_TM_MTC_ZONE0_SW_MASK_ADDR(n) ((n) + 0x140)
#define TSENS_TM_MTC_ZONE0_LOG(n) ((n) + 0x150)
#define TSENS_TM_MTC_ZONE0_HISTORY(n) ((n) + 0x160)
+#define TSENS_TM_MTC_ZONE0_SW_MASK_ADDR_V14(n) ((n) + 0xC0)
+#define TSENS_TM_MTC_ZONE0_LOG_V14(n) ((n) + 0xD0)
+
#define TSENS_SN_ADDR_OFFSET 0x4
#define TSENS_RESET_HISTORY_MASK 0x4
#define TSENS_ZONEMASK_PARAMS 3
diff --git a/drivers/thermal/tsens.h b/drivers/thermal/tsens.h
index 885b15c..730d124 100644
--- a/drivers/thermal/tsens.h
+++ b/drivers/thermal/tsens.h
@@ -127,6 +127,8 @@
u32 wd_bark_mask;
bool mtc;
bool valid_status_check;
+ u32 ver_major;
+ u32 ver_minor;
};
struct tsens_mtc_sysfs {
diff --git a/drivers/thermal/tsens1xxx.c b/drivers/thermal/tsens1xxx.c
index 0ea4a46..d63fcd1 100644
--- a/drivers/thermal/tsens1xxx.c
+++ b/drivers/thermal/tsens1xxx.c
@@ -306,6 +306,10 @@
*temp = code_to_degc(last_temp, sensor);
*temp = *temp * TSENS_SCALE_MILLIDEG;
+ if (tmdev->ops->dbg)
+ tmdev->ops->dbg(tmdev, (u32)sensor->hw_id,
+ TSENS_DBG_LOG_TEMP_READS, temp);
+
return 0;
}
@@ -565,6 +569,9 @@
/* Disable monitoring sensor trip threshold for triggered sensor */
mb();
+ if (tm->ops->dbg)
+ tm->ops->dbg(tm, 0, TSENS_DBG_LOG_INTERRUPT_TIMESTAMP, NULL);
+
return IRQ_HANDLED;
}
@@ -600,6 +607,11 @@
spin_lock_init(&tmdev->tsens_upp_low_lock);
+ if (tmdev->ctrl_data->mtc) {
+ if (tmdev->ops->dbg)
+ tmdev->ops->dbg(tmdev, 0, TSENS_DBG_MTC_DATA, NULL);
+ }
+
return 0;
}
@@ -646,13 +658,16 @@
.hw_init = tsens1xxx_hw_init,
.get_temp = tsens1xxx_get_temp,
.set_trips = tsens1xxx_set_trip_temp,
- .interrupts_reg = tsens1xxx_register_interrupts,
+ .interrupts_reg = tsens1xxx_register_interrupts,
.sensor_en = tsens1xxx_hw_sensor_en,
.calibrate = calibrate_8937,
+ .dbg = tsens2xxx_dbg,
};
const struct tsens_data data_tsens14xx = {
- .ops = &ops_tsens1xxx,
- .valid_status_check = true,
- .mtc = true,
+ .ops = &ops_tsens1xxx,
+ .valid_status_check = true,
+ .mtc = true,
+ .ver_major = 1,
+ .ver_minor = 4,
};
diff --git a/include/linux/power_supply.h b/include/linux/power_supply.h
index b59d548..4331b83 100644
--- a/include/linux/power_supply.h
+++ b/include/linux/power_supply.h
@@ -288,6 +288,7 @@
POWER_SUPPLY_PROP_MANUFACTURER,
POWER_SUPPLY_PROP_SERIAL_NUMBER,
POWER_SUPPLY_PROP_BATTERY_TYPE,
+ POWER_SUPPLY_PROP_CYCLE_COUNTS,
};
enum power_supply_type {
diff --git a/include/linux/qpnp/qpnp-misc.h b/include/linux/qpnp/qpnp-misc.h
index 7d95bf2..a7a5616 100644
--- a/include/linux/qpnp/qpnp-misc.h
+++ b/include/linux/qpnp/qpnp-misc.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2013-2014, 2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2013-2014, 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
@@ -15,6 +15,11 @@
#include <linux/errno.h>
+enum twm_state {
+ PMIC_TWM_CLEAR,
+ PMIC_TWM_ENABLE,
+};
+
#ifdef CONFIG_QPNP_MISC
/**
* qpnp_misc_irqs_available - check if IRQs are available
@@ -42,6 +47,25 @@
*/
int qpnp_misc_read_reg(struct device_node *node, u16 addr, u8 *val);
+/**
+ * qpnp_misc_twm_notifier_register - register to the twm mode notifier
+ *
+ * @nb: pointer to the client's notifier handle
+ *
+ * This function returns 0 if the client is successfuly added to the
+ * notifer list.
+ */
+int qpnp_misc_twm_notifier_register(struct notifier_block *nb);
+
+/**
+ * qpnp_misc_twm_notifier_unregister - unregister to the twm mode notifier
+ *
+ * @nb: pointer to the client's notifier handle
+ *
+ * This function returns 0 if the client is successfuly removed from the
+ * notifer list.
+ */
+int qpnp_misc_twm_notifier_unregister(struct notifier_block *nb);
#else
static inline int qpnp_misc_irqs_available(struct device *consumer_dev)
{
@@ -52,5 +76,13 @@
{
return 0;
}
+static inline int qpnp_misc_twm_notifier_register(struct notifier_block *nb)
+{
+ return 0;
+}
+static inline int qpnp_misc_twm_notifier_unregister(struct notifier_block *nb)
+{
+ return 0;
+}
#endif
#endif
diff --git a/include/linux/sched.h b/include/linux/sched.h
index bae4f35..4b2a1bc 100644
--- a/include/linux/sched.h
+++ b/include/linux/sched.h
@@ -1768,6 +1768,7 @@
struct sched_entity se;
struct sched_rt_entity rt;
u64 last_sleep_ts;
+ u64 last_cpu_selected_ts;
#ifdef CONFIG_SCHED_WALT
struct ravg ravg;
/*
diff --git a/kernel/sched/core.c b/kernel/sched/core.c
index 23a7f9c..0ca1647 100644
--- a/kernel/sched/core.c
+++ b/kernel/sched/core.c
@@ -2330,6 +2330,7 @@
p->se.nr_migrations = 0;
p->se.vruntime = 0;
p->last_sleep_ts = 0;
+ p->last_cpu_selected_ts = 0;
INIT_LIST_HEAD(&p->se.group_node);
diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c
index c00e731..04ba6d0 100644
--- a/kernel/sched/fair.c
+++ b/kernel/sched/fair.c
@@ -7296,6 +7296,39 @@
task_fits_max(p, cpu);
}
+#define SCHED_SELECT_PREV_CPU_NSEC 2000000
+#define SCHED_FORCE_CPU_SELECTION_NSEC 20000000
+
+static inline bool
+bias_to_prev_cpu(struct task_struct *p, struct cpumask *rtg_target)
+{
+ int prev_cpu = task_cpu(p);
+#ifdef CONFIG_SCHED_WALT
+ u64 ms = p->ravg.mark_start;
+#else
+ u64 ms = sched_clock();
+#endif
+
+ if (cpu_isolated(prev_cpu) || !idle_cpu(prev_cpu))
+ return false;
+
+ if (!ms)
+ return false;
+
+ if (ms - p->last_cpu_selected_ts >= SCHED_SELECT_PREV_CPU_NSEC) {
+ p->last_cpu_selected_ts = ms;
+ return false;
+ }
+
+ if (ms - p->last_sleep_ts >= SCHED_SELECT_PREV_CPU_NSEC)
+ return false;
+
+ if (rtg_target && !cpumask_test_cpu(prev_cpu, rtg_target))
+ return false;
+
+ return true;
+}
+
#ifdef CONFIG_SCHED_WALT
static inline struct cpumask *find_rtg_target(struct task_struct *p)
{
@@ -7374,6 +7407,9 @@
}
}
+ if (bias_to_prev_cpu(p, rtg_target))
+ return prev_cpu;
+
rcu_read_lock();
sd = rcu_dereference(per_cpu(sd_ea, prev_cpu));
diff --git a/kernel/sched/sched.h b/kernel/sched/sched.h
index 3637d96..f27ab13 100644
--- a/kernel/sched/sched.h
+++ b/kernel/sched/sched.h
@@ -823,6 +823,8 @@
int prev_top;
int curr_top;
bool notif_pending;
+ u64 last_cc_update;
+ u64 cycles;
#endif
#ifdef CONFIG_IRQ_TIME_ACCOUNTING
diff --git a/kernel/sched/walt.c b/kernel/sched/walt.c
index e6a11c1..a9fb367 100644
--- a/kernel/sched/walt.c
+++ b/kernel/sched/walt.c
@@ -301,10 +301,27 @@
return 0;
}
-static void update_task_cpu_cycles(struct task_struct *p, int cpu)
+/*
+ * Assumes rq_lock is held and wallclock was recorded in the same critical
+ * section as this function's invocation.
+ */
+static inline u64 read_cycle_counter(int cpu, u64 wallclock)
+{
+ struct rq *rq = cpu_rq(cpu);
+
+ if (rq->last_cc_update != wallclock) {
+ rq->cycles = cpu_cycle_counter_cb.get_cpu_cycle_counter(cpu);
+ rq->last_cc_update = wallclock;
+ }
+
+ return rq->cycles;
+}
+
+static void update_task_cpu_cycles(struct task_struct *p, int cpu,
+ u64 wallclock)
{
if (use_cycle_counter)
- p->cpu_cycles = cpu_cycle_counter_cb.get_cpu_cycle_counter(cpu);
+ p->cpu_cycles = read_cycle_counter(cpu, wallclock);
}
void clear_ed_task(struct task_struct *p, struct rq *rq)
@@ -348,7 +365,7 @@
if (is_idle_task(curr)) {
/* We're here without rq->lock held, IRQ disabled */
raw_spin_lock(&rq->lock);
- update_task_cpu_cycles(curr, cpu);
+ update_task_cpu_cycles(curr, cpu, ktime_get_ns());
raw_spin_unlock(&rq->lock);
}
}
@@ -757,7 +774,7 @@
update_task_ravg(p, task_rq(p), TASK_MIGRATE,
wallclock, 0);
- update_task_cpu_cycles(p, new_cpu);
+ update_task_cpu_cycles(p, new_cpu, wallclock);
/*
* When a task is migrating during the wakeup, adjust
@@ -1839,7 +1856,7 @@
return;
}
- cur_cycles = cpu_cycle_counter_cb.get_cpu_cycle_counter(cpu);
+ cur_cycles = read_cycle_counter(cpu, wallclock);
/*
* If current task is idle task and irqtime == 0 CPU was
@@ -1904,7 +1921,7 @@
old_window_start = update_window_start(rq, wallclock, event);
if (!p->ravg.mark_start) {
- update_task_cpu_cycles(p, cpu_of(rq));
+ update_task_cpu_cycles(p, cpu_of(rq), wallclock);
goto done;
}
@@ -2035,7 +2052,7 @@
p->ravg.mark_start = p->last_wake_ts = wallclock;
p->last_enqueued_ts = wallclock;
p->last_switch_out_ts = 0;
- update_task_cpu_cycles(p, cpu_of(rq));
+ update_task_cpu_cycles(p, cpu_of(rq), wallclock);
}
static cpumask_t all_cluster_cpus = CPU_MASK_NONE;
@@ -3255,6 +3272,8 @@
rq->curr_table = 0;
rq->prev_top = 0;
rq->curr_top = 0;
+ rq->last_cc_update = 0;
+ rq->cycles = 0;
for (j = 0; j < NUM_TRACKED_WINDOWS; j++) {
memset(&rq->load_subs[j], 0,
sizeof(struct load_subtractions));