Merge "cnss: Add Manufacturer Id for QCA9379 card"
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/Documentation/devicetree/bindings/power/supply/qcom/qpnp-qg.txt b/Documentation/devicetree/bindings/power/supply/qcom/qpnp-qg.txt
index f87f7db..efa67f5 100644
--- a/Documentation/devicetree/bindings/power/supply/qcom/qpnp-qg.txt
+++ b/Documentation/devicetree/bindings/power/supply/qcom/qpnp-qg.txt
@@ -41,6 +41,16 @@
when this interrupt fires. If not specified, the
default value is 3200 mV.
+- qcom,vbatt-empty-cold-mv
+ Usage: optional
+ Value type: <u32>
+ Definition: The battery voltage threshold (in mV) at which the
+ vbatt-empty interrupt fires. This threshold is only
+ applied at cold temperature specified by
+ 'qcom,cold-temp-threshold'. The SOC is forced to 0
+ when this interrupt fires. If not specified, the
+ default value is 3000 mV.
+
- qcom,vbatt-cutoff-mv
Usage: optional
Value type: <u32>
@@ -56,6 +66,16 @@
the action when this interrupt fires. If not specified
the default value is 3500 mV.
+- qcom,vbatt-low-cold-mv
+ Usage: optional
+ Value type: <u32>
+ Definition: The battery voltage threshold (in mV) at which the
+ the VBAT_LOW interrupt fires. The threshold is only
+ applied at cold temperature specified by
+ 'qcom,cold-temp-threshold'. Software can take necessary
+ the action when this interrupt fires. If not specified
+ the default value is 3800 mV.
+
- qcom,qg-iterm-ma
Usage: optional
Value type: <u32>
@@ -164,6 +184,13 @@
improve the user experience. This is applicable only if
"qcom,hold-soc-while-full" is specified.
+- qcom,cold-temp-threshold
+ Usage: optional
+ Value type: <u32>
+ Definition: Temperature threshold in decidegree at which the low
+ temperature specific configuration as applied. If not
+ specified, the default value is 0 degree centigrade.
+
==========================================================
Second Level Nodes - Peripherals managed by QGAUGE driver
==========================================================
diff --git a/Documentation/devicetree/bindings/qdsp/msm-fastrpc.txt b/Documentation/devicetree/bindings/qdsp/msm-fastrpc.txt
index de40a7c..a034acc 100644
--- a/Documentation/devicetree/bindings/qdsp/msm-fastrpc.txt
+++ b/Documentation/devicetree/bindings/qdsp/msm-fastrpc.txt
@@ -15,6 +15,7 @@
- qcom,rpc-latency-us: FastRPC QoS latency vote
- qcom,adsp-remoteheap-vmid: FastRPC remote heap VMID list
- qcom,fastrpc-adsp-audio-pdr: Flag to enable ADSP Audio PDR
+- qcom,fastrpc-adsp-sensors-pdr: Flag to enable Sensors PDR
Optional subnodes:
- qcom,msm_fastrpc_compute_cb : Child nodes representing the compute context
@@ -25,12 +26,17 @@
- iommus : A list of phandle and IOMMU specifier pairs that describe the
IOMMU master interfaces of the device
+Subnode Optional properties:
+- shared-cb : Present if context bank need to be shared
+
+
Example:
qcom,msm_fastrpc {
compatible = "qcom,msm-fastrpc-adsp";
qcom,fastrpc-glink;
qcom,rpc-latency-us = <2343>;
qcom,adsp-remoteheap-vmid = <22 37>;
+ qcom,fastrpc-adsp-sensors-pdr;
qcom,msm_fastrpc_compute_cb_1 {
compatible = "qcom,msm-fastrpc-compute-cb";
@@ -41,6 +47,7 @@
compatible = "qcom,msm-fastrpc-compute-cb";
label = "adsprpc-smd";
iommus = <&lpass_q6_smmu 9>,
+ shared-cb;
};
};
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..98fb09b 100644
--- a/arch/arm/configs/msm8909w-perf_defconfig
+++ b/arch/arm/configs/msm8909w-perf_defconfig
@@ -28,11 +28,13 @@
CONFIG_DEFAULT_USE_ENERGY_AWARE=y
CONFIG_RELAY=y
CONFIG_BLK_DEV_INITRD=y
+CONFIG_CC_OPTIMIZE_FOR_SIZE=y
CONFIG_KALLSYMS_ALL=y
CONFIG_BPF_SYSCALL=y
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
@@ -216,6 +218,7 @@
CONFIG_QSEECOM=y
CONFIG_UID_SYS_STATS=y
CONFIG_MEMORY_STATE_TIME=y
+CONFIG_QPNP_MISC=y
CONFIG_SCSI=y
CONFIG_BLK_DEV_SD=y
CONFIG_CHR_DEV_SG=y
@@ -317,6 +320,10 @@
CONFIG_QCOM_KGSL=y
CONFIG_FB=y
CONFIG_FB_VIRTUAL=y
+CONFIG_FB_MSM=y
+CONFIG_FB_MSM_MDSS=y
+CONFIG_FB_MSM_MDSS_WRITEBACK=y
+CONFIG_FB_MSM_MDSS_MDP3=y
CONFIG_BACKLIGHT_LCD_SUPPORT=y
CONFIG_BACKLIGHT_CLASS_DEVICE=y
CONFIG_LOGO=y
@@ -378,11 +385,14 @@
CONFIG_MMC_SDHCI=y
CONFIG_MMC_SDHCI_PLTFM=y
CONFIG_MMC_SDHCI_MSM=y
+CONFIG_NEW_LEDS=y
+CONFIG_LEDS_CLASS=y
+CONFIG_LEDS_QPNP_HAPTICS=y
+CONFIG_LEDS_TRIGGERS=y
CONFIG_RTC_CLASS=y
CONFIG_RTC_DRV_QPNP=y
CONFIG_DMADEVICES=y
CONFIG_QCOM_SPS_DMA=y
-CONFIG_SYNC_FILE=y
CONFIG_UIO=y
CONFIG_UIO_MSM_SHAREDMEM=y
CONFIG_STAGING=y
@@ -394,6 +404,7 @@
CONFIG_SPS_SUPPORT_NDP_BAM=y
CONFIG_QPNP_REVID=y
CONFIG_USB_BAM=y
+CONFIG_MSM_MDSS_PLL=y
CONFIG_REMOTE_SPINLOCK_MSM=y
CONFIG_MAILBOX=y
CONFIG_ARM_SMMU=y
@@ -405,6 +416,7 @@
CONFIG_MSM_CORE_HANG_DETECT=y
CONFIG_MSM_GLADIATOR_HANG_DETECT=y
CONFIG_QCOM_WATCHDOG_V2=y
+CONFIG_QPNP_PBS=y
CONFIG_QCOM_MEMORY_DUMP_V2=y
CONFIG_QCOM_BUS_SCALING=y
CONFIG_QCOM_SECURE_BUFFER=y
@@ -431,6 +443,11 @@
CONFIG_QTI_RPM_STATS_LOG=y
CONFIG_QCOM_FORCE_WDOG_BITE_ON_PANIC=y
CONFIG_MSM_BAM_DMUX=y
+CONFIG_MSM_GLINK_BGCOM_XPRT=y
+CONFIG_MSM_BGCOM_INTERFACE=y
+CONFIG_MSM_BGRSB=y
+CONFIG_MSM_PIL_SSR_BG=y
+CONFIG_MSM_BGCOM=y
CONFIG_WCNSS_CORE=y
CONFIG_WCNSS_CORE_PRONTO=y
CONFIG_WCNSS_REGISTER_DUMP_ON_BITE=y
diff --git a/arch/arm/configs/msm8909w_defconfig b/arch/arm/configs/msm8909w_defconfig
index 46052bd..9d1fc06 100644
--- a/arch/arm/configs/msm8909w_defconfig
+++ b/arch/arm/configs/msm8909w_defconfig
@@ -28,11 +28,13 @@
CONFIG_SCHED_TUNE=y
CONFIG_DEFAULT_USE_ENERGY_AWARE=y
CONFIG_BLK_DEV_INITRD=y
+CONFIG_CC_OPTIMIZE_FOR_SIZE=y
CONFIG_KALLSYMS_ALL=y
CONFIG_BPF_SYSCALL=y
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
@@ -219,6 +221,7 @@
CONFIG_QSEECOM=y
CONFIG_UID_SYS_STATS=y
CONFIG_MEMORY_STATE_TIME=y
+CONFIG_QPNP_MISC=y
CONFIG_SCSI=y
CONFIG_BLK_DEV_SD=y
CONFIG_CHR_DEV_SG=y
@@ -322,6 +325,10 @@
CONFIG_QCOM_KGSL=y
CONFIG_FB=y
CONFIG_FB_VIRTUAL=y
+CONFIG_FB_MSM=y
+CONFIG_FB_MSM_MDSS=y
+CONFIG_FB_MSM_MDSS_WRITEBACK=y
+CONFIG_FB_MSM_MDSS_MDP3=y
CONFIG_BACKLIGHT_LCD_SUPPORT=y
CONFIG_BACKLIGHT_CLASS_DEVICE=y
CONFIG_LOGO=y
@@ -383,11 +390,14 @@
CONFIG_MMC_SDHCI=y
CONFIG_MMC_SDHCI_PLTFM=y
CONFIG_MMC_SDHCI_MSM=y
+CONFIG_NEW_LEDS=y
+CONFIG_LEDS_CLASS=y
+CONFIG_LEDS_QPNP_HAPTICS=y
+CONFIG_LEDS_TRIGGERS=y
CONFIG_RTC_CLASS=y
CONFIG_RTC_DRV_QPNP=y
CONFIG_DMADEVICES=y
CONFIG_QCOM_SPS_DMA=y
-CONFIG_SYNC_FILE=y
CONFIG_UIO=y
CONFIG_UIO_MSM_SHAREDMEM=y
CONFIG_STAGING=y
@@ -399,6 +409,7 @@
CONFIG_SPS_SUPPORT_NDP_BAM=y
CONFIG_QPNP_REVID=y
CONFIG_USB_BAM=y
+CONFIG_MSM_MDSS_PLL=y
CONFIG_REMOTE_SPINLOCK_MSM=y
CONFIG_MAILBOX=y
CONFIG_ARM_SMMU=y
@@ -410,6 +421,7 @@
CONFIG_MSM_CORE_HANG_DETECT=y
CONFIG_MSM_GLADIATOR_HANG_DETECT=y
CONFIG_QCOM_WATCHDOG_V2=y
+CONFIG_QPNP_PBS=y
CONFIG_QCOM_MEMORY_DUMP_V2=y
CONFIG_QCOM_BUS_SCALING=y
CONFIG_QCOM_SECURE_BUFFER=y
@@ -436,6 +448,11 @@
CONFIG_QTI_RPM_STATS_LOG=y
CONFIG_QCOM_FORCE_WDOG_BITE_ON_PANIC=y
CONFIG_MSM_BAM_DMUX=y
+CONFIG_MSM_GLINK_BGCOM_XPRT=y
+CONFIG_MSM_BGCOM_INTERFACE=y
+CONFIG_MSM_BGRSB=y
+CONFIG_MSM_PIL_SSR_BG=y
+CONFIG_MSM_BGCOM=y
CONFIG_WCNSS_CORE=y
CONFIG_WCNSS_CORE_PRONTO=y
CONFIG_WCNSS_REGISTER_DUMP_ON_BITE=y
diff --git a/arch/arm/configs/msm8937-perf_defconfig b/arch/arm/configs/msm8937-perf_defconfig
index 2413675..df466cf 100644
--- a/arch/arm/configs/msm8937-perf_defconfig
+++ b/arch/arm/configs/msm8937-perf_defconfig
@@ -572,6 +572,7 @@
CONFIG_TMPFS=y
CONFIG_ECRYPT_FS=y
CONFIG_ECRYPT_FS_MESSAGING=y
+CONFIG_SDCARD_FS=y
# CONFIG_NETWORK_FILESYSTEMS is not set
CONFIG_NLS_CODEPAGE_437=y
CONFIG_NLS_ISO8859_1=y
diff --git a/arch/arm/configs/msm8937_defconfig b/arch/arm/configs/msm8937_defconfig
index df2dc40..e1c655b 100644
--- a/arch/arm/configs/msm8937_defconfig
+++ b/arch/arm/configs/msm8937_defconfig
@@ -592,6 +592,7 @@
CONFIG_TMPFS=y
CONFIG_ECRYPT_FS=y
CONFIG_ECRYPT_FS_MESSAGING=y
+CONFIG_SDCARD_FS=y
# CONFIG_NETWORK_FILESYSTEMS is not set
CONFIG_NLS_CODEPAGE_437=y
CONFIG_NLS_ISO8859_1=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/apq8053-lite-dragon.dtsi b/arch/arm64/boot/dts/qcom/apq8053-lite-dragon.dtsi
index c203e17..48fbc40 100644
--- a/arch/arm64/boot/dts/qcom/apq8053-lite-dragon.dtsi
+++ b/arch/arm64/boot/dts/qcom/apq8053-lite-dragon.dtsi
@@ -92,7 +92,7 @@
android {
vbmeta {
compatible = "android,vbmeta";
- parts = "vbmeta,boot,system,vendor,bluetooth,modem";
+ parts = "vbmeta,boot,system,vendor,bluetooth,modem,oem";
};
fstab {
/delete-node/ system;
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/pmi632.dtsi b/arch/arm64/boot/dts/qcom/pmi632.dtsi
index 8bb9dbe..b8f8b68 100644
--- a/arch/arm64/boot/dts/qcom/pmi632.dtsi
+++ b/arch/arm64/boot/dts/qcom/pmi632.dtsi
@@ -29,7 +29,9 @@
pmi632_pon: qcom,power-on@800 {
compatible = "qcom,qpnp-power-on";
reg = <0x800 0x100>;
+
qcom,secondary-pon-reset;
+ qcom,s3-src = "kpdpwr";
};
pmi632_vadc: vadc@3100 {
@@ -464,9 +466,6 @@
#address-cells = <1>;
#size-cells = <1>;
- qcom,vbatt-empty-mv = <3300>;
- qcom,vbatt-low-mv = <3500>;
- qcom,vbatt-cutoff-mv = <3400>;
qcom,qg-iterm-ma = <100>;
qcom,hold-soc-while-full;
qcom,linearize-soc;
@@ -476,11 +475,12 @@
qcom,qgauge@4800 {
status = "okay";
reg = <0x4800 0x100>;
- interrupts = <0x2 0x48 0x0 IRQ_TYPE_EDGE_BOTH>,
- <0x2 0x48 0x1 IRQ_TYPE_EDGE_BOTH>,
- <0x2 0x48 0x2 IRQ_TYPE_EDGE_BOTH>,
- <0x2 0x48 0x3 IRQ_TYPE_EDGE_BOTH>,
- <0x2 0x48 0x4 IRQ_TYPE_EDGE_BOTH>;
+ interrupts =
+ <0x2 0x48 0x0 IRQ_TYPE_EDGE_BOTH>,
+ <0x2 0x48 0x1 IRQ_TYPE_EDGE_BOTH>,
+ <0x2 0x48 0x2 IRQ_TYPE_EDGE_RISING>,
+ <0x2 0x48 0x3 IRQ_TYPE_EDGE_RISING>,
+ <0x2 0x48 0x4 IRQ_TYPE_EDGE_RISING>;
interrupt-names = "qg-batt-missing",
"qg-vbat-low",
"qg-vbat-empty",
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/msm8937-perf_defconfig b/arch/arm64/configs/msm8937-perf_defconfig
index 363f2dd..df62bd1 100644
--- a/arch/arm64/configs/msm8937-perf_defconfig
+++ b/arch/arm64/configs/msm8937-perf_defconfig
@@ -584,6 +584,7 @@
CONFIG_TMPFS_POSIX_ACL=y
CONFIG_ECRYPT_FS=y
CONFIG_ECRYPT_FS_MESSAGING=y
+CONFIG_SDCARD_FS=y
# CONFIG_NETWORK_FILESYSTEMS is not set
CONFIG_NLS_CODEPAGE_437=y
CONFIG_NLS_ISO8859_1=y
diff --git a/arch/arm64/configs/msm8937_defconfig b/arch/arm64/configs/msm8937_defconfig
index 66b402f..9d35d7d 100644
--- a/arch/arm64/configs/msm8937_defconfig
+++ b/arch/arm64/configs/msm8937_defconfig
@@ -608,6 +608,7 @@
CONFIG_TMPFS_POSIX_ACL=y
CONFIG_ECRYPT_FS=y
CONFIG_ECRYPT_FS_MESSAGING=y
+CONFIG_SDCARD_FS=y
# CONFIG_NETWORK_FILESYSTEMS is not set
CONFIG_NLS_CODEPAGE_437=y
CONFIG_NLS_ISO8859_1=y
diff --git a/arch/arm64/configs/sdm670_defconfig b/arch/arm64/configs/sdm670_defconfig
index 09de6e8..ee3418b 100644
--- a/arch/arm64/configs/sdm670_defconfig
+++ b/arch/arm64/configs/sdm670_defconfig
@@ -619,11 +619,6 @@
CONFIG_SLUB_DEBUG_PANIC_ON=y
CONFIG_DEBUG_PAGEALLOC_ENABLE_DEFAULT=y
CONFIG_PAGE_POISONING=y
-CONFIG_DEBUG_OBJECTS=y
-CONFIG_DEBUG_OBJECTS_FREE=y
-CONFIG_DEBUG_OBJECTS_TIMERS=y
-CONFIG_DEBUG_OBJECTS_WORK=y
-CONFIG_DEBUG_OBJECTS_PERCPU_COUNTER=y
CONFIG_SLUB_DEBUG_ON=y
CONFIG_DEBUG_KMEMLEAK=y
CONFIG_DEBUG_KMEMLEAK_EARLY_LOG_SIZE=4000
@@ -649,6 +644,7 @@
CONFIG_QCOM_RTB=y
CONFIG_QCOM_RTB_SEPARATE_CPUS=y
CONFIG_FUNCTION_TRACER=y
+CONFIG_PREEMPTIRQ_EVENTS=y
CONFIG_IRQSOFF_TRACER=y
CONFIG_PREEMPT_TRACER=y
CONFIG_BLK_DEV_IO_TRACE=y
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..8dc560d 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
@@ -618,11 +617,6 @@
CONFIG_SLUB_DEBUG_PANIC_ON=y
CONFIG_DEBUG_PAGEALLOC_ENABLE_DEFAULT=y
CONFIG_PAGE_POISONING=y
-CONFIG_DEBUG_OBJECTS=y
-CONFIG_DEBUG_OBJECTS_FREE=y
-CONFIG_DEBUG_OBJECTS_TIMERS=y
-CONFIG_DEBUG_OBJECTS_WORK=y
-CONFIG_DEBUG_OBJECTS_PERCPU_COUNTER=y
CONFIG_SLUB_DEBUG_ON=y
CONFIG_DEBUG_KMEMLEAK=y
CONFIG_DEBUG_KMEMLEAK_EARLY_LOG_SIZE=4000
@@ -650,6 +644,7 @@
CONFIG_QCOM_RTB=y
CONFIG_QCOM_RTB_SEPARATE_CPUS=y
CONFIG_FUNCTION_TRACER=y
+CONFIG_PREEMPTIRQ_EVENTS=y
CONFIG_IRQSOFF_TRACER=y
CONFIG_PREEMPT_TRACER=y
CONFIG_BLK_DEV_IO_TRACE=y
diff --git a/arch/arm64/include/asm/esr.h b/arch/arm64/include/asm/esr.h
index 85997c0..e52727c 100644
--- a/arch/arm64/include/asm/esr.h
+++ b/arch/arm64/include/asm/esr.h
@@ -181,6 +181,85 @@
#define ESR_ELx_SYS64_ISS_SYS_CNTFRQ (ESR_ELx_SYS64_ISS_SYS_VAL(3, 3, 0, 14, 0) | \
ESR_ELx_SYS64_ISS_DIR_READ)
+/* ISS field definitions for CP15 AArch32 access traps */
+#define ESR_ELx_CP15_32_ISS_CV_SHIFT 24
+#define ESR_ELx_CP15_32_ISS_CV_MASK \
+ (UL(0x1) << ESR_ELx_CP15_32_ISS_CV_SHIFT)
+#define ESR_ELx_CP15_32_ISS_DIR_MASK 0x1
+#define ESR_ELx_CP15_32_ISS_DIR_READ 0x1
+#define ESR_ELx_CP15_32_ISS_DIR_WRITE 0x0
+
+#define ESR_ELx_CP15_32_ISS_RT_SHIFT 5
+#define ESR_ELx_CP15_32_ISS_RT_MASK \
+ (UL(0x1f) << ESR_ELx_CP15_32_ISS_RT_SHIFT)
+#define ESR_ELx_CP15_32_ISS_CRM_SHIFT 1
+#define ESR_ELx_CP15_32_ISS_CRM_MASK \
+ (UL(0xf) << ESR_ELx_CP15_32_ISS_CRM_SHIFT)
+#define ESR_ELx_CP15_32_ISS_CRN_SHIFT 10
+#define ESR_ELx_CP15_32_ISS_CRN_MASK \
+ (UL(0xf) << ESR_ELx_CP15_32_ISS_CRN_SHIFT)
+#define ESR_ELx_CP15_32_ISS_OP1_SHIFT 14
+#define ESR_ELx_CP15_32_ISS_OP1_MASK \
+ (UL(0x7) << ESR_ELx_CP15_32_ISS_OP1_SHIFT)
+#define ESR_ELx_CP15_32_ISS_OP2_SHIFT 17
+#define ESR_ELx_CP15_32_ISS_OP2_MASK \
+ (UL(0x7) << ESR_ELx_CP15_32_ISS_OP2_SHIFT)
+#define ESR_ELx_CP15_32_ISS_COND_SHIFT 20
+#define ESR_ELx_CP15_32_ISS_COND_MASK \
+ (UL(0xf) << ESR_ELx_CP15_32_ISS_COND_SHIFT)
+#define ESR_ELx_CP15_32_ISS_SYS_MASK (ESR_ELx_CP15_32_ISS_OP1_MASK | \
+ ESR_ELx_CP15_32_ISS_OP2_MASK | \
+ ESR_ELx_CP15_32_ISS_CRN_MASK | \
+ ESR_ELx_CP15_32_ISS_CRM_MASK)
+#define ESR_ELx_CP15_32_ISS_SYS_VAL(op1, op2, crn, crm) \
+ (((op1) << ESR_ELx_CP15_32_ISS_OP1_SHIFT) | \
+ ((op2) << ESR_ELx_CP15_32_ISS_OP2_SHIFT) | \
+ ((crn) << ESR_ELx_CP15_32_ISS_CRN_SHIFT) | \
+ ((crm) << ESR_ELx_CP15_32_ISS_CRM_SHIFT))
+
+#define ESR_ELx_CP15_32_ISS_SYS_OP_MASK (ESR_ELx_CP15_32_ISS_SYS_MASK | \
+ ESR_ELx_CP15_32_ISS_DIR_MASK)
+
+#define ESR_ELx_CP15_32_ISS_SYS_CNTFRQ \
+ (ESR_ELx_CP15_32_ISS_SYS_VAL(0, 0, 14, 0) | \
+ ESR_ELx_CP15_32_ISS_DIR_READ)
+
+/* ISS field definitions for CP15 AArch32 64-bit access traps */
+#define ESR_ELx_CP15_64_ISS_CV_SHIFT 24
+#define ESR_ELx_CP15_64_ISS_CV_MASK \
+ (UL(0x1) << ESR_ELx_CP15_64_ISS_CV_SHIFT)
+#define ESR_ELx_CP15_64_ISS_DIR_MASK 0x1
+#define ESR_ELx_CP15_64_ISS_DIR_READ 0x1
+#define ESR_ELx_CP15_64_ISS_DIR_WRITE 0x0
+
+#define ESR_ELx_CP15_64_ISS_RT_SHIFT 5
+#define ESR_ELx_CP15_64_ISS_RT_MASK \
+ (UL(0x1f) << ESR_ELx_CP15_64_ISS_RT_SHIFT)
+#define ESR_ELx_CP15_64_ISS_CRM_SHIFT 1
+#define ESR_ELx_CP15_64_ISS_CRM_MASK \
+ (UL(0xf) << ESR_ELx_CP15_64_ISS_CRM_SHIFT)
+#define ESR_ELx_CP15_64_ISS_RT2_SHIFT 10
+#define ESR_ELx_CP15_64_ISS_RT2_MASK \
+ (UL(0x1f) << ESR_ELx_CP15_64_ISS_RT2_SHIFT)
+#define ESR_ELx_CP15_64_ISS_OP1_SHIFT 16
+#define ESR_ELx_CP15_64_ISS_OP1_MASK \
+ (UL(0xf) << ESR_ELx_CP15_64_ISS_OP1_SHIFT)
+#define ESR_ELx_CP15_64_ISS_COND_SHIFT 20
+#define ESR_ELx_CP15_64_ISS_COND_MASK \
+ (UL(0xf) << ESR_ELx_CP15_64_ISS_COND_SHIFT)
+#define ESR_ELx_CP15_64_ISS_SYS_MASK (ESR_ELx_CP15_64_ISS_OP1_MASK | \
+ ESR_ELx_CP15_64_ISS_CRM_MASK)
+#define ESR_ELx_CP15_64_ISS_SYS_VAL(op1, crm) \
+ (((op1) << ESR_ELx_CP15_64_ISS_OP1_SHIFT) | \
+ ((crm) << ESR_ELx_CP15_64_ISS_CRM_SHIFT))
+
+#define ESR_ELx_CP15_64_ISS_SYS_OP_MASK (ESR_ELx_CP15_64_ISS_SYS_MASK | \
+ ESR_ELx_CP15_64_ISS_DIR_MASK)
+
+#define ESR_ELx_CP15_64_ISS_SYS_CNTVCT \
+ (ESR_ELx_CP15_64_ISS_SYS_VAL(1, 14) | \
+ ESR_ELx_CP15_64_ISS_DIR_READ)
+
#ifndef __ASSEMBLY__
#include <asm/types.h>
diff --git a/arch/arm64/include/asm/ptrace.h b/arch/arm64/include/asm/ptrace.h
index 458773a..69f6cae 100644
--- a/arch/arm64/include/asm/ptrace.h
+++ b/arch/arm64/include/asm/ptrace.h
@@ -130,8 +130,12 @@
#ifdef CONFIG_COMPAT
#define compat_thumb_mode(regs) \
(((regs)->pstate & COMPAT_PSR_T_BIT))
+#define compat_arm_instr_set(regs) \
+ (((regs)->pstate & (COMPAT_PSR_T_BIT | COMPAT_PSR_J_BIT)) == 0)
#else
#define compat_thumb_mode(regs) (0)
+#define compat_arm_instr_set(regs) (0)
+
#endif
#define user_mode(regs) \
diff --git a/arch/arm64/kernel/entry.S b/arch/arm64/kernel/entry.S
index 3b22fa3..f8ba35d 100644
--- a/arch/arm64/kernel/entry.S
+++ b/arch/arm64/kernel/entry.S
@@ -601,9 +601,9 @@
cmp x24, #ESR_ELx_EC_UNKNOWN // unknown exception in EL0
b.eq el0_undef
cmp x24, #ESR_ELx_EC_CP15_32 // CP15 MRC/MCR trap
- b.eq el0_undef
+ b.eq el0_cp15_32_compat
cmp x24, #ESR_ELx_EC_CP15_64 // CP15 MRRC/MCRR trap
- b.eq el0_undef
+ b.eq el0_cp15_64_compat
cmp x24, #ESR_ELx_EC_CP14_MR // CP14 MRC/MCR trap
b.eq el0_undef
cmp x24, #ESR_ELx_EC_CP14_LS // CP14 LDC/STC trap
@@ -622,6 +622,28 @@
mov sc_nr, #__NR_compat_syscalls
b el0_svc_naked
+el0_cp15_32_compat:
+ /*
+ * AArch32 CP15 MRC/MCR trap handling
+ */
+ enable_dbg_and_irq
+ ct_user_exit
+ mov x0, x25
+ mov x1, sp
+ bl do_cp15_32_instr_compat
+ b ret_to_user
+
+el0_cp15_64_compat:
+ /*
+ * AArch32 CP15 MRRC/MCRR trap handling
+ */
+ enable_dbg_and_irq
+ ct_user_exit
+ mov x0, x25
+ mov x1, sp
+ bl do_cp15_64_instr_compat
+ b ret_to_user
+
.align 6
el0_irq_compat:
kernel_entry 0, 32
diff --git a/arch/arm64/kernel/traps.c b/arch/arm64/kernel/traps.c
index cd53836..fcd2b33 100644
--- a/arch/arm64/kernel/traps.c
+++ b/arch/arm64/kernel/traps.c
@@ -606,6 +606,124 @@
force_signal_inject(SIGILL, ILL_ILLOPC, regs, 0);
}
+#ifdef CONFIG_COMPAT
+static void cntfrq_cp15_32_read_handler(unsigned int esr, struct pt_regs *regs)
+{
+ int rt =
+ (esr & ESR_ELx_CP15_32_ISS_RT_MASK) >> ESR_ELx_CP15_32_ISS_RT_SHIFT;
+ int cv =
+ (esr & ESR_ELx_CP15_32_ISS_CV_MASK) >> ESR_ELx_CP15_32_ISS_CV_SHIFT;
+ int cond =
+ (esr & ESR_ELx_CP15_32_ISS_COND_MASK) >>
+ ESR_ELx_CP15_32_ISS_COND_SHIFT;
+ bool read_reg = 1;
+
+ if (rt == 13 && !compat_arm_instr_set(regs))
+ read_reg = 0;
+
+ if (cv && cond != 0xf &&
+ !(*aarch32_opcode_cond_checks[cond])(regs->pstate & 0xffffffff))
+ read_reg = 0;
+
+ if (read_reg)
+ regs->regs[rt] = read_sysreg(cntfrq_el0);
+ regs->pc += 4;
+}
+
+struct cp15_32_hook {
+ unsigned int esr_mask;
+ unsigned int esr_val;
+ void (*handler)(unsigned int esr, struct pt_regs *regs);
+};
+
+static struct cp15_32_hook cp15_32_hooks[] = {
+ {
+ /* Trap CP15 AArch32 read access to CNTFRQ_EL0 */
+ .esr_mask = ESR_ELx_CP15_32_ISS_SYS_OP_MASK,
+ .esr_val = ESR_ELx_CP15_32_ISS_SYS_CNTFRQ,
+ .handler = cntfrq_cp15_32_read_handler,
+ },
+ {},
+};
+
+asmlinkage void __exception do_cp15_32_instr_compat(unsigned int esr,
+ struct pt_regs *regs)
+{
+ struct cp15_32_hook *hook;
+
+ for (hook = cp15_32_hooks; hook->handler; hook++)
+ if ((hook->esr_mask & esr) == hook->esr_val) {
+ hook->handler(esr, regs);
+ return;
+ }
+
+ force_signal_inject(SIGILL, ILL_ILLOPC, regs, 0);
+}
+
+static void cntvct_cp15_64_read_handler(unsigned int esr, struct pt_regs *regs)
+{
+ int rt =
+ (esr & ESR_ELx_CP15_64_ISS_RT_MASK) >> ESR_ELx_CP15_64_ISS_RT_SHIFT;
+ int rt2 =
+ (esr & ESR_ELx_CP15_64_ISS_RT2_MASK) >> ESR_ELx_CP15_64_ISS_RT2_SHIFT;
+ int cv =
+ (esr & ESR_ELx_CP15_64_ISS_CV_MASK) >> ESR_ELx_CP15_64_ISS_CV_SHIFT;
+ int cond =
+ (esr & ESR_ELx_CP15_64_ISS_COND_MASK) >>
+ ESR_ELx_CP15_64_ISS_COND_SHIFT;
+ bool read_reg = 1;
+
+ if (rt == 15 || rt2 == 15 || rt == rt2)
+ read_reg = 0;
+
+ if ((rt == 13 || rt2 == 13) && !compat_arm_instr_set(regs))
+ read_reg = 0;
+
+ if (cv && cond != 0xf &&
+ !(*aarch32_opcode_cond_checks[cond])(regs->pstate & 0xffffffff))
+ read_reg = 0;
+
+ if (read_reg) {
+ u64 cval = arch_counter_get_cntvct();
+
+ regs->regs[rt] = cval & 0xffffffff;
+ regs->regs[rt2] = cval >> 32;
+ }
+ regs->pc += 4;
+}
+
+struct cp15_64_hook {
+ unsigned int esr_mask;
+ unsigned int esr_val;
+ void (*handler)(unsigned int esr, struct pt_regs *regs);
+};
+
+static struct cp15_64_hook cp15_64_hooks[] = {
+ {
+ /* Trap CP15 AArch32 read access to CNTVCT_EL0 */
+ .esr_mask = ESR_ELx_CP15_64_ISS_SYS_OP_MASK,
+ .esr_val = ESR_ELx_CP15_64_ISS_SYS_CNTVCT,
+ .handler = cntvct_cp15_64_read_handler,
+ },
+ {},
+};
+
+asmlinkage void __exception do_cp15_64_instr_compat(unsigned int esr,
+ struct pt_regs *regs)
+{
+ struct cp15_64_hook *hook;
+
+ for (hook = cp15_64_hooks; hook->handler; hook++)
+ if ((hook->esr_mask & esr) == hook->esr_val) {
+ hook->handler(esr, regs);
+ return;
+ }
+
+ force_signal_inject(SIGILL, ILL_ILLOPC, regs, 0);
+}
+
+#endif
+
long compat_arm_syscall(struct pt_regs *regs);
asmlinkage long do_ni_syscall(struct pt_regs *regs)
diff --git a/drivers/char/adsprpc.c b/drivers/char/adsprpc.c
index 44e0644..7f8a158 100644
--- a/drivers/char/adsprpc.c
+++ b/drivers/char/adsprpc.c
@@ -66,6 +66,9 @@
#define AUDIO_PDR_SERVICE_LOCATION_CLIENT_NAME "audio_pdr_adsprpc"
#define AUDIO_PDR_ADSP_SERVICE_NAME "avs/audio"
+#define SENSORS_PDR_SERVICE_LOCATION_CLIENT_NAME "sensors_pdr_adsprpc"
+#define SENSORS_PDR_ADSP_SERVICE_NAME "tms/servreg"
+
#define RPC_TIMEOUT (5 * HZ)
#define BALIGN 128
#define NUM_CHANNELS 4 /* adsp, mdsp, slpi, cdsp*/
@@ -120,7 +123,7 @@
static int fastrpc_glink_open(int cid);
static void fastrpc_glink_close(void *chan, int cid);
-static int fastrpc_audio_pdr_notifier_cb(struct notifier_block *nb,
+static int fastrpc_pdr_notifier_cb(struct notifier_block *nb,
unsigned long code,
void *data);
static struct dentry *debugfs_root;
@@ -230,6 +233,7 @@
int faults;
int secure;
int coherent;
+ int sharedcb;
};
struct fastrpc_session_ctx {
@@ -366,6 +370,7 @@
int pd;
char *spdname;
int file_close;
+ int sharedcb;
struct fastrpc_apps *apps;
struct hlist_head perf;
struct dentry *debugfs_file;
@@ -391,7 +396,13 @@
.spdname =
AUDIO_PDR_SERVICE_LOCATION_CLIENT_NAME,
.pdrnb.notifier_call =
- fastrpc_audio_pdr_notifier_cb,
+ fastrpc_pdr_notifier_cb,
+ },
+ {
+ .spdname =
+ SENSORS_PDR_SERVICE_LOCATION_CLIENT_NAME,
+ .pdrnb.notifier_call =
+ fastrpc_pdr_notifier_cb,
}
},
},
@@ -1760,7 +1771,7 @@
if (err)
goto bail;
- VERIFY(err, ((me->ctxtable[index]->ctxid == (rsp.ctx & ~1)) &&
+ VERIFY(err, ((me->ctxtable[index]->ctxid == (rsp.ctx & ~3)) &&
me->ctxtable[index]->magic == FASTRPC_CTX_MAGIC));
if (err)
goto bail;
@@ -1956,7 +1967,8 @@
VERIFY(err, 0 == (err = fastrpc_channel_open(fl)));
if (err)
goto bail;
- if (init->flags == FASTRPC_INIT_ATTACH) {
+ if (init->flags == FASTRPC_INIT_ATTACH ||
+ init->flags == FASTRPC_INIT_ATTACH_SENSORS) {
remote_arg_t ra[1];
int tgid = fl->tgid;
@@ -1968,7 +1980,12 @@
ioctl.fds = NULL;
ioctl.attrs = NULL;
ioctl.crc = NULL;
- fl->pd = 0;
+ if (init->flags == FASTRPC_INIT_ATTACH)
+ fl->pd = 0;
+ else if (init->flags == FASTRPC_INIT_ATTACH_SENSORS) {
+ fl->spdname = SENSORS_PDR_SERVICE_LOCATION_CLIENT_NAME;
+ fl->pd = 2;
+ }
VERIFY(err, !(err = fastrpc_internal_invoke(fl,
FASTRPC_MODE_PARALLEL, 1, &ioctl)));
if (err)
@@ -2078,6 +2095,7 @@
if (err)
goto bail;
+ fl->pd = 1;
inbuf.pgid = current->tgid;
inbuf.namelen = init->filelen;
inbuf.pageslen = 0;
@@ -2541,15 +2559,17 @@
static void fastrpc_context_list_dtor(struct fastrpc_file *fl);
static int fastrpc_session_alloc_locked(struct fastrpc_channel_ctx *chan,
- int secure, struct fastrpc_session_ctx **session)
+ int secure, int sharedcb, struct fastrpc_session_ctx **session)
{
struct fastrpc_apps *me = &gfa;
int idx = 0, err = 0;
if (chan->sesscount) {
for (idx = 0; idx < chan->sesscount; ++idx) {
- if (!chan->session[idx].used &&
- chan->session[idx].smmu.secure == secure) {
+ if ((sharedcb && chan->session[idx].smmu.sharedcb) ||
+ (!chan->session[idx].used &&
+ chan->session[idx].smmu.secure
+ == secure && !sharedcb)) {
chan->session[idx].used = 1;
break;
}
@@ -2605,7 +2625,7 @@
if (err)
goto bail;
- VERIFY(err, ((me->ctxtable[index]->ctxid == (rsp->ctx & ~1)) &&
+ VERIFY(err, ((me->ctxtable[index]->ctxid == (rsp->ctx & ~3)) &&
me->ctxtable[index]->magic == FASTRPC_CTX_MAGIC));
if (err)
goto bail;
@@ -2650,7 +2670,7 @@
mutex_lock(&me->smd_mutex);
if (!*session)
- err = fastrpc_session_alloc_locked(chan, secure, session);
+ err = fastrpc_session_alloc_locked(chan, secure, 0, session);
mutex_unlock(&me->smd_mutex);
return err;
}
@@ -3096,7 +3116,7 @@
fl->cid = cid;
fl->ssrcount = fl->apps->channel[cid].ssrcount;
VERIFY(err, !fastrpc_session_alloc_locked(
- &fl->apps->channel[cid], 0, &fl->sctx));
+ &fl->apps->channel[cid], 0, fl->sharedcb, &fl->sctx));
if (err)
goto bail;
}
@@ -3135,6 +3155,9 @@
} else
pm_qos_update_request(&fl->pm_qos_req, latency);
break;
+ case FASTRPC_CONTROL_SMMU:
+ fl->sharedcb = cp->smmu.sharedcb;
+ break;
default:
err = -ENOTTY;
break;
@@ -3375,7 +3398,7 @@
return NOTIFY_DONE;
}
-static int fastrpc_audio_pdr_notifier_cb(struct notifier_block *pdrnb,
+static int fastrpc_pdr_notifier_cb(struct notifier_block *pdrnb,
unsigned long code,
void *data)
{
@@ -3412,7 +3435,7 @@
{
struct fastrpc_static_pd *spd;
struct pd_qmi_client_data *pdr = data;
- int curr_state = 0;
+ int curr_state = 0, i = 0;
spd = container_of(nb, struct fastrpc_static_pd, get_service_nb);
if (opcode == LOCATOR_DOWN) {
@@ -3420,15 +3443,21 @@
return NOTIFY_DONE;
}
- if (pdr->total_domains == 1) {
- spd->pdrhandle = service_notif_register_notifier(
- pdr->domain_list[0].name,
- pdr->domain_list[0].instance_id,
+ for (i = 0; i < pdr->total_domains; i++) {
+ if ((!strcmp(pdr->domain_list[i].name,
+ "msm/adsp/audio_pd")) ||
+ (!strcmp(pdr->domain_list[i].name,
+ "msm/adsp/sensor_pd"))) {
+ spd->pdrhandle =
+ service_notif_register_notifier(
+ pdr->domain_list[i].name,
+ pdr->domain_list[i].instance_id,
&spd->pdrnb, &curr_state);
- if (IS_ERR(spd->pdrhandle))
- pr_err("ADSPRPC: Unable to register notifier\n");
- } else
- pr_err("ADSPRPC: Service returned invalid domains\n");
+ if (IS_ERR(spd->pdrhandle))
+ pr_err("ADSPRPC: Unable to register notifier\n");
+ break;
+ }
+ }
return NOTIFY_DONE;
}
@@ -3487,6 +3516,8 @@
sess->used = 0;
sess->smmu.coherent = of_property_read_bool(dev->of_node,
"dma-coherent");
+ sess->smmu.sharedcb = of_property_read_bool(dev->of_node,
+ "shared-cb");
sess->smmu.secure = of_property_read_bool(dev->of_node,
"qcom,secure-context-bank");
if (sess->smmu.secure)
@@ -3726,6 +3757,24 @@
pr_err("ADSPRPC: Get service location failed: %d\n",
ret);
}
+ if (of_property_read_bool(dev->of_node,
+ "qcom,fastrpc-adsp-sensors-pdr")) {
+ int session;
+
+ VERIFY(err, !fastrpc_get_adsp_session(
+ SENSORS_PDR_SERVICE_LOCATION_CLIENT_NAME, &session));
+ if (err)
+ goto spdbail;
+ me->channel[0].spd[session].get_service_nb.notifier_call =
+ fastrpc_get_service_location_notify;
+ ret = get_service_location(
+ SENSORS_PDR_SERVICE_LOCATION_CLIENT_NAME,
+ SENSORS_PDR_ADSP_SERVICE_NAME,
+ &me->channel[0].spd[session].get_service_nb);
+ if (ret)
+ pr_err("ADSPRPC: Get service location failed: %d\n",
+ ret);
+ }
spdbail:
err = 0;
VERIFY(err, !of_platform_populate(pdev->dev.of_node,
diff --git a/drivers/char/adsprpc_shared.h b/drivers/char/adsprpc_shared.h
index bb7b654..de0dd01 100644
--- a/drivers/char/adsprpc_shared.h
+++ b/drivers/char/adsprpc_shared.h
@@ -66,6 +66,7 @@
#define FASTRPC_INIT_ATTACH 0
#define FASTRPC_INIT_CREATE 1
#define FASTRPC_INIT_CREATE_STATIC 2
+#define FASTRPC_INIT_ATTACH_SENSORS 3
/* Retrives number of input buffers from the scalars parameter */
#define REMOTE_SCALARS_INBUFS(sc) (((sc) >> 16) & 0x0ff)
@@ -229,11 +230,15 @@
uint32_t enable; //!latency control enable
uint32_t level; //!level of control
};
-
+#define FASTRPC_CONTROL_SMMU (2)
+struct fastrpc_ctrl_smmu {
+ uint32_t sharedcb;
+};
struct fastrpc_ioctl_control {
uint32_t req;
union {
struct fastrpc_ctrl_latency lp;
+ struct fastrpc_ctrl_smmu smmu;
};
};
diff --git a/drivers/char/diag/diag_dci.c b/drivers/char/diag/diag_dci.c
index a469eb9..286418f 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;
@@ -687,7 +688,7 @@
byte_mask = 0x01 << (item_num % 8);
offset = equip_id * 514;
- if (offset + byte_index > DCI_LOG_MASK_SIZE) {
+ if (offset + byte_index >= DCI_LOG_MASK_SIZE) {
pr_err("diag: In %s, invalid offset: %d, log_code: %d, byte_index: %d\n",
__func__, offset, log_code, byte_index);
return 0;
@@ -714,7 +715,7 @@
bit_index = event_id % 8;
byte_mask = 0x1 << bit_index;
- if (byte_index > DCI_EVENT_MASK_SIZE) {
+ if (byte_index >= DCI_EVENT_MASK_SIZE) {
pr_err("diag: In %s, invalid, event_id: %d, byte_index: %d\n",
__func__, event_id, byte_index);
return 0;
@@ -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/char/diag/diagchar_core.c b/drivers/char/diag/diagchar_core.c
index ba321b0..0d3389a 100644
--- a/drivers/char/diag/diagchar_core.c
+++ b/drivers/char/diag/diagchar_core.c
@@ -1715,8 +1715,8 @@
static int diag_switch_logging(struct diag_logging_mode_param_t *param)
{
int new_mode, i = 0;
- int curr_mode, err = 0;
- uint8_t do_switch = 1, peripheral = 0;
+ int curr_mode, err = 0, peripheral = 0;
+ uint8_t do_switch = 1;
uint32_t peripheral_mask = 0, pd_mask = 0;
if (!param)
@@ -1730,6 +1730,10 @@
if (param->pd_mask) {
pd_mask = diag_translate_mask(param->pd_mask);
+ param->diag_id = 0;
+ param->pd_val = 0;
+ param->peripheral = -EINVAL;
+
for (i = UPD_WLAN; i < NUM_MD_SESSIONS; i++) {
if (pd_mask & (1 << i)) {
if (diag_search_diagid_by_pd(i, ¶m->diag_id,
@@ -1739,6 +1743,12 @@
}
}
}
+
+ DIAG_LOG(DIAG_DEBUG_USERSPACE,
+ "diag: pd_mask = %d, diag_id = %d, peripheral = %d, pd_val = %d\n",
+ param->pd_mask, param->diag_id,
+ param->peripheral, param->pd_val);
+
if (!param->diag_id ||
(param->pd_val < UPD_WLAN) ||
(param->pd_val >= NUM_MD_SESSIONS)) {
@@ -1748,22 +1758,26 @@
return -EINVAL;
}
- DIAG_LOG(DIAG_DEBUG_USERSPACE,
- "diag: pd_mask = %d, diag_id = %d, peripheral = %d, pd_val = %d\n",
- param->pd_mask, param->diag_id,
- param->peripheral, param->pd_val);
-
peripheral = param->peripheral;
+ if ((peripheral < PERIPHERAL_MODEM) ||
+ (peripheral >= NUM_PERIPHERALS)) {
+ DIAG_LOG(DIAG_DEBUG_USERSPACE,
+ "Invalid peripheral: %d\n", peripheral);
+ return -EINVAL;
+ }
i = param->pd_val - UPD_WLAN;
+ mutex_lock(&driver->md_session_lock);
if (driver->md_session_map[peripheral] &&
(MD_PERIPHERAL_MASK(peripheral) &
diag_mux->mux_mask) &&
!driver->pd_session_clear[i]) {
DIAG_LOG(DIAG_DEBUG_USERSPACE,
"diag_fr: User PD is already logging onto active peripheral logging\n");
+ mutex_unlock(&driver->md_session_lock);
driver->pd_session_clear[i] = 0;
return -EINVAL;
}
+ mutex_unlock(&driver->md_session_lock);
peripheral_mask =
diag_translate_mask(param->pd_mask);
param->peripheral_mask = peripheral_mask;
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/media/platform/msm/camera_v2/isp/msm_isp_axi_util.c b/drivers/media/platform/msm/camera_v2/isp/msm_isp_axi_util.c
index 73a5ebc..0b84327 100644
--- a/drivers/media/platform/msm/camera_v2/isp/msm_isp_axi_util.c
+++ b/drivers/media/platform/msm/camera_v2/isp/msm_isp_axi_util.c
@@ -3154,7 +3154,7 @@
return -EINVAL;
msm_isp_get_timestamp(×tamp, vfe_dev_ioctl);
-
+ mutex_lock(&vfe_dev_ioctl->buf_mgr->lock);
for (i = 0; i < stream_cfg_cmd->num_streams; i++) {
if (stream_cfg_cmd->stream_handle[i] == 0)
continue;
@@ -3162,6 +3162,7 @@
HANDLE_TO_IDX(stream_cfg_cmd->stream_handle[i]));
if (!stream_info) {
pr_err("%s: stream_info is NULL", __func__);
+ mutex_unlock(&vfe_dev_ioctl->buf_mgr->lock);
return -EINVAL;
}
if (SRC_TO_INTF(stream_info->stream_src) < VFE_SRC_MAX)
@@ -3171,6 +3172,7 @@
else {
ISP_DBG("%s: invalid src info index\n", __func__);
rc = -EINVAL;
+ mutex_unlock(&vfe_dev_ioctl->buf_mgr->lock);
goto error;
}
spin_lock_irqsave(&stream_info->lock, flags);
@@ -3182,6 +3184,7 @@
}
if (rc) {
spin_unlock_irqrestore(&stream_info->lock, flags);
+ mutex_unlock(&vfe_dev_ioctl->buf_mgr->lock);
goto error;
}
@@ -3204,6 +3207,7 @@
HANDLE_TO_IDX(
stream_cfg_cmd->stream_handle[i]));
spin_unlock_irqrestore(&stream_info->lock, flags);
+ mutex_unlock(&vfe_dev_ioctl->buf_mgr->lock);
goto error;
}
for (k = 0; k < stream_info->num_isp; k++) {
@@ -3262,6 +3266,7 @@
spin_unlock_irqrestore(&stream_info->lock, flags);
streams[num_streams++] = stream_info;
}
+ mutex_unlock(&vfe_dev_ioctl->buf_mgr->lock);
for (i = 0; i < MAX_VFE; i++) {
vfe_dev = update_vfes[i];
diff --git a/drivers/media/platform/msm/camera_v2/isp/msm_isp_stats_util.c b/drivers/media/platform/msm/camera_v2/isp/msm_isp_stats_util.c
index 0d7a1e0..0634c0f 100644
--- a/drivers/media/platform/msm/camera_v2/isp/msm_isp_stats_util.c
+++ b/drivers/media/platform/msm/camera_v2/isp/msm_isp_stats_util.c
@@ -1105,6 +1105,7 @@
struct vfe_device *vfe_dev;
msm_isp_get_timestamp(×tamp, vfe_dev_ioctl);
+ mutex_lock(&vfe_dev_ioctl->buf_mgr->lock);
num_stats_comp_mask =
vfe_dev_ioctl->hw_info->stats_hw_info->num_stats_comp_mask;
@@ -1125,6 +1126,7 @@
}
if (rc) {
spin_unlock_irqrestore(&stream_info->lock, flags);
+ mutex_unlock(&vfe_dev_ioctl->buf_mgr->lock);
goto error;
}
rc = msm_isp_init_stats_ping_pong_reg(
@@ -1132,6 +1134,7 @@
if (rc < 0) {
spin_unlock_irqrestore(&stream_info->lock, flags);
pr_err("%s: No buffer for stream%d\n", __func__, idx);
+ mutex_unlock(&vfe_dev_ioctl->buf_mgr->lock);
return rc;
}
init_completion(&stream_info->active_comp);
@@ -1166,6 +1169,7 @@
stats_data->num_active_stream);
streams[num_stream++] = stream_info;
}
+ mutex_unlock(&vfe_dev_ioctl->buf_mgr->lock);
for (k = 0; k < MAX_VFE; k++) {
if (!update_vfes[k] || num_active_streams[k])
diff --git a/drivers/media/platform/msm/camera_v2/isp/msm_isp_util.c b/drivers/media/platform/msm/camera_v2/isp/msm_isp_util.c
index d3a68b1..b589afa 100644
--- a/drivers/media/platform/msm/camera_v2/isp/msm_isp_util.c
+++ b/drivers/media/platform/msm/camera_v2/isp/msm_isp_util.c
@@ -896,9 +896,7 @@
case VIDIOC_MSM_ISP_CFG_STREAM:
mutex_lock(&vfe_dev->core_mutex);
MSM_ISP_DUAL_VFE_MUTEX_LOCK(vfe_dev);
- mutex_lock(&vfe_dev->buf_mgr->lock);
rc = msm_isp_cfg_axi_stream(vfe_dev, arg);
- mutex_unlock(&vfe_dev->buf_mgr->lock);
MSM_ISP_DUAL_VFE_MUTEX_UNLOCK(vfe_dev);
mutex_unlock(&vfe_dev->core_mutex);
break;
@@ -1015,9 +1013,7 @@
case VIDIOC_MSM_ISP_CFG_STATS_STREAM:
mutex_lock(&vfe_dev->core_mutex);
MSM_ISP_DUAL_VFE_MUTEX_LOCK(vfe_dev);
- mutex_lock(&vfe_dev->buf_mgr->lock);
rc = msm_isp_cfg_stats_stream(vfe_dev, arg);
- mutex_unlock(&vfe_dev->buf_mgr->lock);
MSM_ISP_DUAL_VFE_MUTEX_UNLOCK(vfe_dev);
mutex_unlock(&vfe_dev->core_mutex);
break;
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/cnss/cnss_sdio.c b/drivers/net/wireless/cnss/cnss_sdio.c
index 098596f..8ed2630 100644
--- a/drivers/net/wireless/cnss/cnss_sdio.c
+++ b/drivers/net/wireless/cnss/cnss_sdio.c
@@ -274,11 +274,6 @@
return ret;
}
- if (!cnss_pdata->regulator.wlan_vreg) {
- pr_debug("wlan_vreg regulator is invalid\n");
- return 0;
- }
-
ret = mmc_power_save_host(host);
if (ret) {
pr_err("Failed to Power Save Host err:%d\n",
@@ -286,7 +281,11 @@
return ret;
}
- regulator_disable(cnss_pdata->regulator.wlan_vreg);
+ if (cnss_pdata->regulator.wlan_vreg)
+ regulator_disable(cnss_pdata->regulator.wlan_vreg);
+ else
+ pr_debug("wlan_vreg regulator is invalid\n");
+
info->cnss_hw_state = CNSS_HW_SLEEP;
return ret;
@@ -320,22 +319,23 @@
return ret;
}
- if (!cnss_pdata->regulator.wlan_vreg) {
+ if (cnss_pdata->regulator.wlan_vreg) {
+ ret = regulator_enable(cnss_pdata->regulator.wlan_vreg);
+ if (ret) {
+ pr_err("Failed to enable wlan vreg\n");
+ return ret;
+ }
+ } else {
pr_debug("wlan_vreg regulator is invalid\n");
- return 0;
}
- ret = regulator_enable(cnss_pdata->regulator.wlan_vreg);
- if (ret) {
- pr_err("Failed to enable wlan vreg\n");
- return ret;
- }
ret = mmc_power_restore_host(host);
if (ret) {
pr_err("Failed to restore host power ret:%d\n",
ret);
- regulator_disable(cnss_pdata->regulator.wlan_vreg);
+ if (cnss_pdata->regulator.wlan_vreg)
+ regulator_disable(cnss_pdata->regulator.wlan_vreg);
return ret;
}
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/platform/msm/ipa/ipa_api.c b/drivers/platform/msm/ipa/ipa_api.c
index b99435d..7d1af47 100644
--- a/drivers/platform/msm/ipa/ipa_api.c
+++ b/drivers/platform/msm/ipa/ipa_api.c
@@ -294,6 +294,46 @@
return dest;
}
+int ipa_smmu_store_sgt(struct sg_table **out_ch_ptr,
+ struct sg_table *in_sgt_ptr)
+{
+ unsigned int nents;
+
+ if (in_sgt_ptr != NULL) {
+ *out_ch_ptr = kzalloc(sizeof(struct sg_table), GFP_KERNEL);
+ if (*out_ch_ptr == NULL)
+ return -ENOMEM;
+
+ nents = in_sgt_ptr->nents;
+
+ (*out_ch_ptr)->sgl =
+ kcalloc(nents, sizeof(struct scatterlist),
+ GFP_KERNEL);
+ if ((*out_ch_ptr)->sgl == NULL) {
+ kfree(*out_ch_ptr);
+ *out_ch_ptr = NULL;
+ return -ENOMEM;
+ }
+
+ memcpy((*out_ch_ptr)->sgl, in_sgt_ptr->sgl,
+ nents*sizeof((*out_ch_ptr)->sgl));
+ (*out_ch_ptr)->nents = nents;
+ (*out_ch_ptr)->orig_nents = in_sgt_ptr->orig_nents;
+ }
+ return 0;
+}
+
+int ipa_smmu_free_sgt(struct sg_table **out_sgt_ptr)
+{
+ if (*out_sgt_ptr != NULL) {
+ kfree((*out_sgt_ptr)->sgl);
+ (*out_sgt_ptr)->sgl = NULL;
+ kfree(*out_sgt_ptr);
+ *out_sgt_ptr = NULL;
+ }
+ return 0;
+}
+
/**
* ipa_connect() - low-level IPA client connect
* @in: [in] input parameters from client
@@ -3111,12 +3151,12 @@
* ipa_tear_down_uc_offload_pipes() - tear down uc offload pipes
*/
int ipa_tear_down_uc_offload_pipes(int ipa_ep_idx_ul,
- int ipa_ep_idx_dl)
+ int ipa_ep_idx_dl, struct ipa_ntn_conn_in_params *params)
{
int ret;
IPA_API_DISPATCH_RETURN(ipa_tear_down_uc_offload_pipes, ipa_ep_idx_ul,
- ipa_ep_idx_dl);
+ ipa_ep_idx_dl, params);
return ret;
}
diff --git a/drivers/platform/msm/ipa/ipa_api.h b/drivers/platform/msm/ipa/ipa_api.h
index fc4362f..0319827 100644
--- a/drivers/platform/msm/ipa/ipa_api.h
+++ b/drivers/platform/msm/ipa/ipa_api.h
@@ -394,7 +394,7 @@
struct ipa_ntn_conn_out_params *);
int (*ipa_tear_down_uc_offload_pipes)(int ipa_ep_idx_ul,
- int ipa_ep_idx_dl);
+ int ipa_ep_idx_dl, struct ipa_ntn_conn_in_params *params);
struct device *(*ipa_get_pdev)(void);
diff --git a/drivers/platform/msm/ipa/ipa_clients/ipa_uc_offload.c b/drivers/platform/msm/ipa/ipa_clients/ipa_uc_offload.c
index f0c67b1..b16cc0c 100644
--- a/drivers/platform/msm/ipa/ipa_clients/ipa_uc_offload.c
+++ b/drivers/platform/msm/ipa/ipa_clients/ipa_uc_offload.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2016-2017 The Linux Foundation. All rights reserved.
+/* Copyright (c) 2016-2018 The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -73,6 +73,7 @@
ipa_notify_cb notify;
struct completion ntn_completion;
u32 pm_hdl;
+ struct ipa_ntn_conn_in_params conn;
};
static struct ipa_uc_offload_ctx *ipa_uc_offload_ctx[IPA_UC_MAX_PROT_SIZE];
@@ -403,6 +404,51 @@
}
}
+static int ipa_uc_ntn_alloc_conn_smmu_info(struct ipa_ntn_setup_info *dest,
+ struct ipa_ntn_setup_info *source)
+{
+ int result;
+
+ IPA_UC_OFFLOAD_DBG("Allocating smmu info\n");
+
+ memcpy(dest, source, sizeof(struct ipa_ntn_setup_info));
+
+ dest->data_buff_list =
+ kcalloc(dest->num_buffers, sizeof(struct ntn_buff_smmu_map),
+ GFP_KERNEL);
+ if (dest->data_buff_list == NULL) {
+ IPA_UC_OFFLOAD_ERR("failed to alloc smmu info\n");
+ return -ENOMEM;
+ }
+
+ memcpy(dest->data_buff_list, source->data_buff_list,
+ sizeof(struct ntn_buff_smmu_map) * dest->num_buffers);
+
+ result = ipa_smmu_store_sgt(&dest->buff_pool_base_sgt,
+ source->buff_pool_base_sgt);
+ if (result) {
+ kfree(dest->data_buff_list);
+ return result;
+ }
+
+ result = ipa_smmu_store_sgt(&dest->ring_base_sgt,
+ source->ring_base_sgt);
+ if (result) {
+ kfree(dest->data_buff_list);
+ ipa_smmu_free_sgt(&dest->buff_pool_base_sgt);
+ return result;
+ }
+
+ return 0;
+}
+
+static void ipa_uc_ntn_free_conn_smmu_info(struct ipa_ntn_setup_info *params)
+{
+ kfree(params->data_buff_list);
+ ipa_smmu_free_sgt(¶ms->buff_pool_base_sgt);
+ ipa_smmu_free_sgt(¶ms->ring_base_sgt);
+}
+
int ipa_uc_ntn_conn_pipes(struct ipa_ntn_conn_in_params *inp,
struct ipa_ntn_conn_out_params *outp,
struct ipa_uc_offload_ctx *ntn_ctx)
@@ -410,6 +456,11 @@
int result = 0;
enum ipa_uc_offload_state prev_state;
+ if (ntn_ctx->conn.dl.smmu_enabled != ntn_ctx->conn.ul.smmu_enabled) {
+ IPA_UC_OFFLOAD_ERR("ul and dl smmu enablement do not match\n");
+ return -EINVAL;
+ }
+
prev_state = ntn_ctx->state;
if (inp->dl.ring_base_pa % IPA_NTN_DMA_POOL_ALIGNMENT ||
inp->dl.buff_pool_base_pa % IPA_NTN_DMA_POOL_ALIGNMENT) {
@@ -467,7 +518,21 @@
goto fail;
}
- return 0;
+ if (ntn_ctx->conn.dl.smmu_enabled) {
+ result = ipa_uc_ntn_alloc_conn_smmu_info(&ntn_ctx->conn.dl,
+ &inp->dl);
+ if (result) {
+ IPA_UC_OFFLOAD_ERR("alloc failure on TX\n");
+ goto fail;
+ }
+ result = ipa_uc_ntn_alloc_conn_smmu_info(&ntn_ctx->conn.ul,
+ &inp->ul);
+ if (result) {
+ ipa_uc_ntn_free_conn_smmu_info(&ntn_ctx->conn.dl);
+ IPA_UC_OFFLOAD_ERR("alloc failure on RX\n");
+ goto fail;
+ }
+ }
fail:
if (!ipa_pm_is_used())
@@ -563,6 +628,11 @@
int ipa_ep_idx_ul, ipa_ep_idx_dl;
int ret = 0;
+ if (ntn_ctx->conn.dl.smmu_enabled != ntn_ctx->conn.ul.smmu_enabled) {
+ IPA_UC_OFFLOAD_ERR("ul and dl smmu enablement do not match\n");
+ return -EINVAL;
+ }
+
ntn_ctx->state = IPA_UC_OFFLOAD_STATE_INITIALIZED;
#ifdef CONFIG_IPA3
@@ -594,12 +664,16 @@
ipa_ep_idx_ul = ipa_get_ep_mapping(IPA_CLIENT_ETHERNET_PROD);
ipa_ep_idx_dl = ipa_get_ep_mapping(IPA_CLIENT_ETHERNET_CONS);
- ret = ipa_tear_down_uc_offload_pipes(ipa_ep_idx_ul, ipa_ep_idx_dl);
+ ret = ipa_tear_down_uc_offload_pipes(ipa_ep_idx_ul, ipa_ep_idx_dl,
+ &ntn_ctx->conn);
if (ret) {
IPA_UC_OFFLOAD_ERR("fail to tear down ntn offload pipes, %d\n",
ret);
return -EFAULT;
}
+ if (ntn_ctx->conn.dl.smmu_enabled)
+ ipa_uc_ntn_free_conn_smmu_info(&ntn_ctx->conn.dl);
+ ipa_uc_ntn_free_conn_smmu_info(&ntn_ctx->conn.ul);
return ret;
}
diff --git a/drivers/platform/msm/ipa/ipa_clients/ipa_usb.c b/drivers/platform/msm/ipa/ipa_clients/ipa_usb.c
index e23541c..ebf1d29 100644
--- a/drivers/platform/msm/ipa/ipa_clients/ipa_usb.c
+++ b/drivers/platform/msm/ipa/ipa_clients/ipa_usb.c
@@ -1149,7 +1149,8 @@
if (ipa3_usb_ctx->smmu_reg_map.cnt == 0) {
ipa3_usb_ctx->smmu_reg_map.addr = gevntcount_r;
result = ipa3_smmu_map_peer_reg(
- ipa3_usb_ctx->smmu_reg_map.addr, true);
+ ipa3_usb_ctx->smmu_reg_map.addr, true,
+ IPA_SMMU_CB_AP);
if (result) {
IPA_USB_ERR("failed to map USB regs %d\n",
result);
@@ -1172,7 +1173,8 @@
if (ipa3_usb_ctx->smmu_reg_map.cnt == 1) {
result = ipa3_smmu_map_peer_reg(
- ipa3_usb_ctx->smmu_reg_map.addr, false);
+ ipa3_usb_ctx->smmu_reg_map.addr, false,
+ IPA_SMMU_CB_AP);
if (result) {
IPA_USB_ERR("failed to unmap USB regs %d\n",
result);
@@ -1184,14 +1186,16 @@
result = ipa3_smmu_map_peer_buff(params->xfer_ring_base_addr_iova,
- params->xfer_ring_len, map, params->sgt_xfer_rings);
+ params->xfer_ring_len, map, params->sgt_xfer_rings,
+ IPA_SMMU_CB_AP);
if (result) {
IPA_USB_ERR("failed to map Xfer ring %d\n", result);
return result;
}
result = ipa3_smmu_map_peer_buff(params->data_buff_base_addr_iova,
- params->data_buff_base_len, map, params->sgt_data_buff);
+ params->data_buff_base_len, map, params->sgt_data_buff,
+ IPA_SMMU_CB_AP);
if (result) {
IPA_USB_ERR("failed to map TRBs buff %d\n", result);
return result;
@@ -1200,43 +1204,6 @@
return 0;
}
-static int ipa3_usb_smmu_store_sgt(struct sg_table **out_ch_ptr,
- struct sg_table *in_sgt_ptr)
-{
- unsigned int nents;
-
- if (in_sgt_ptr != NULL) {
- *out_ch_ptr = kzalloc(sizeof(struct sg_table), GFP_KERNEL);
- if (*out_ch_ptr == NULL)
- return -ENOMEM;
-
- nents = in_sgt_ptr->nents;
-
- (*out_ch_ptr)->sgl =
- kcalloc(nents, sizeof(struct scatterlist),
- GFP_KERNEL);
- if ((*out_ch_ptr)->sgl == NULL)
- return -ENOMEM;
-
- memcpy((*out_ch_ptr)->sgl, in_sgt_ptr->sgl,
- nents*sizeof((*out_ch_ptr)->sgl));
- (*out_ch_ptr)->nents = nents;
- (*out_ch_ptr)->orig_nents = in_sgt_ptr->orig_nents;
- }
- return 0;
-}
-
-static int ipa3_usb_smmu_free_sgt(struct sg_table **out_sgt_ptr)
-{
- if (*out_sgt_ptr != NULL) {
- kfree((*out_sgt_ptr)->sgl);
- (*out_sgt_ptr)->sgl = NULL;
- kfree(*out_sgt_ptr);
- *out_sgt_ptr = NULL;
- }
- return 0;
-}
-
static int ipa3_usb_request_xdci_channel(
struct ipa_usb_xdci_chan_params *params,
enum ipa_usb_direction dir,
@@ -1327,18 +1294,17 @@
xdci_ch_params = &ipa3_usb_ctx->ttype_ctx[ttype].dl_ch_params;
*xdci_ch_params = *params;
- result = ipa3_usb_smmu_store_sgt(
+ result = ipa_smmu_store_sgt(
&xdci_ch_params->sgt_xfer_rings,
params->sgt_xfer_rings);
- if (result) {
- ipa3_usb_smmu_free_sgt(&xdci_ch_params->sgt_xfer_rings);
+ if (result)
return result;
- }
- result = ipa3_usb_smmu_store_sgt(
+
+ result = ipa_smmu_store_sgt(
&xdci_ch_params->sgt_data_buff,
params->sgt_data_buff);
if (result) {
- ipa3_usb_smmu_free_sgt(&xdci_ch_params->sgt_data_buff);
+ ipa_smmu_free_sgt(&xdci_ch_params->sgt_xfer_rings);
return result;
}
chan_params.keep_ipa_awake = params->keep_ipa_awake;
@@ -1443,9 +1409,9 @@
result = ipa3_usb_smmu_map_xdci_channel(xdci_ch_params, false);
if (xdci_ch_params->sgt_xfer_rings != NULL)
- ipa3_usb_smmu_free_sgt(&xdci_ch_params->sgt_xfer_rings);
+ ipa_smmu_free_sgt(&xdci_ch_params->sgt_xfer_rings);
if (xdci_ch_params->sgt_data_buff != NULL)
- ipa3_usb_smmu_free_sgt(&xdci_ch_params->sgt_data_buff);
+ ipa_smmu_free_sgt(&xdci_ch_params->sgt_data_buff);
/* Change ipa_usb state to INITIALIZED */
if (!ipa3_usb_set_state(IPA_USB_INITIALIZED, false, ttype))
diff --git a/drivers/platform/msm/ipa/ipa_common_i.h b/drivers/platform/msm/ipa/ipa_common_i.h
index b37a127..92f1ab65 100644
--- a/drivers/platform/msm/ipa/ipa_common_i.h
+++ b/drivers/platform/msm/ipa/ipa_common_i.h
@@ -408,7 +408,8 @@
ipa_notify_cb notify, void *priv, u8 hdr_len,
struct ipa_ntn_conn_out_params *outp);
-int ipa_tear_down_uc_offload_pipes(int ipa_ep_idx_ul, int ipa_ep_idx_dl);
+int ipa_tear_down_uc_offload_pipes(int ipa_ep_idx_ul, int ipa_ep_idx_dl,
+ struct ipa_ntn_conn_in_params *params);
u8 *ipa_write_64(u64 w, u8 *dest);
u8 *ipa_write_32(u32 w, u8 *dest);
u8 *ipa_write_16(u16 hw, u8 *dest);
@@ -434,4 +435,8 @@
bool ipa_pm_is_used(void);
+int ipa_smmu_store_sgt(struct sg_table **out_ch_ptr,
+ struct sg_table *in_sgt_ptr);
+int ipa_smmu_free_sgt(struct sg_table **out_sgt_ptr);
+
#endif /* _IPA_COMMON_I_H_ */
diff --git a/drivers/platform/msm/ipa/ipa_uc_offload_common_i.h b/drivers/platform/msm/ipa/ipa_uc_offload_common_i.h
index 0bc4b76..3caac8c 100644
--- a/drivers/platform/msm/ipa/ipa_uc_offload_common_i.h
+++ b/drivers/platform/msm/ipa/ipa_uc_offload_common_i.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012-2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2012-2018, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -19,7 +19,9 @@
int ipa_setup_uc_ntn_pipes(struct ipa_ntn_conn_in_params *in,
ipa_notify_cb notify, void *priv, u8 hdr_len,
struct ipa_ntn_conn_out_params *outp);
-int ipa_tear_down_uc_offload_pipes(int ipa_ep_idx_ul, int ipa_ep_idx_dl);
+
+int ipa_tear_down_uc_offload_pipes(int ipa_ep_idx_ul, int ipa_ep_idx_dl,
+ struct ipa_ntn_conn_in_params *params);
int ipa_ntn_uc_reg_rdyCB(void (*ipauc_ready_cb)(void *user_data),
void *user_data);
diff --git a/drivers/platform/msm/ipa/ipa_v2/ipa_i.h b/drivers/platform/msm/ipa/ipa_v2/ipa_i.h
index bd7f600..b1608b4 100644
--- a/drivers/platform/msm/ipa/ipa_v2/ipa_i.h
+++ b/drivers/platform/msm/ipa/ipa_v2/ipa_i.h
@@ -1584,7 +1584,8 @@
int ipa2_setup_uc_ntn_pipes(struct ipa_ntn_conn_in_params *inp,
ipa_notify_cb notify, void *priv, u8 hdr_len,
struct ipa_ntn_conn_out_params *outp);
-int ipa2_tear_down_uc_offload_pipes(int ipa_ep_idx_ul, int ipa_ep_idx_dl);
+int ipa2_tear_down_uc_offload_pipes(int ipa_ep_idx_ul, int ipa_ep_idx_dl,
+ struct ipa_ntn_conn_in_params *params);
int ipa2_ntn_uc_reg_rdyCB(void (*ipauc_ready_cb)(void *), void *priv);
void ipa2_ntn_uc_dereg_rdyCB(void);
diff --git a/drivers/platform/msm/ipa/ipa_v2/ipa_uc_ntn.c b/drivers/platform/msm/ipa/ipa_v2/ipa_uc_ntn.c
index d4116eb..6b671da 100644
--- a/drivers/platform/msm/ipa/ipa_v2/ipa_uc_ntn.c
+++ b/drivers/platform/msm/ipa/ipa_v2/ipa_uc_ntn.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2016-2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2016-2018, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -377,7 +377,7 @@
*/
int ipa2_tear_down_uc_offload_pipes(int ipa_ep_idx_ul,
- int ipa_ep_idx_dl)
+ int ipa_ep_idx_dl, struct ipa_ntn_conn_in_params *params)
{
struct ipa_mem_buffer cmd;
struct ipa_ep_context *ep_ul, *ep_dl;
diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa.c b/drivers/platform/msm/ipa/ipa_v3/ipa.c
index 065f97f..4782f37 100644
--- a/drivers/platform/msm/ipa/ipa_v3/ipa.c
+++ b/drivers/platform/msm/ipa/ipa_v3/ipa.c
@@ -501,6 +501,19 @@
return NULL;
}
+struct iommu_domain *ipa3_get_smmu_domain_by_type(enum ipa_smmu_cb_type cb_type)
+{
+
+ if (cb_type == IPA_SMMU_CB_WLAN && smmu_cb[IPA_SMMU_CB_WLAN].valid)
+ return smmu_cb[IPA_SMMU_CB_WLAN].iommu;
+
+ if (smmu_cb[cb_type].valid)
+ return smmu_cb[cb_type].mapping->domain;
+
+ IPAERR("CB#%d not valid\n", cb_type);
+
+ return NULL;
+}
struct device *ipa3_get_dma_dev(void)
{
diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_client.c b/drivers/platform/msm/ipa/ipa_v3/ipa_client.c
index 93f2597..fe39440 100644
--- a/drivers/platform/msm/ipa/ipa_v3/ipa_client.c
+++ b/drivers/platform/msm/ipa/ipa_v3/ipa_client.c
@@ -516,15 +516,23 @@
return true;
}
-int ipa3_smmu_map_peer_reg(phys_addr_t phys_addr, bool map)
+int ipa3_smmu_map_peer_reg(phys_addr_t phys_addr, bool map,
+ enum ipa_smmu_cb_type cb_type)
{
struct iommu_domain *smmu_domain;
int res;
- if (ipa3_ctx->s1_bypass_arr[IPA_SMMU_CB_AP])
- return 0;
+ if (cb_type >= IPA_SMMU_CB_MAX) {
+ IPAERR("invalid cb_type\n");
+ return -EINVAL;
+ }
- smmu_domain = ipa3_get_smmu_domain();
+ if (ipa3_ctx->s1_bypass_arr[cb_type]) {
+ IPADBG("CB# %d is set to s1 bypass\n", cb_type);
+ return 0;
+ }
+
+ smmu_domain = ipa3_get_smmu_domain_by_type(cb_type);
if (!smmu_domain) {
IPAERR("invalid smmu domain\n");
return -EINVAL;
@@ -548,7 +556,8 @@
return 0;
}
-int ipa3_smmu_map_peer_buff(u64 iova, u32 size, bool map, struct sg_table *sgt)
+int ipa3_smmu_map_peer_buff(u64 iova, u32 size, bool map, struct sg_table *sgt,
+ enum ipa_smmu_cb_type cb_type)
{
struct iommu_domain *smmu_domain;
int res;
@@ -560,10 +569,17 @@
int i;
struct page *page;
- if (ipa3_ctx->s1_bypass_arr[IPA_SMMU_CB_AP])
- return 0;
+ if (cb_type >= IPA_SMMU_CB_MAX) {
+ IPAERR("invalid cb_type\n");
+ return -EINVAL;
+ }
- smmu_domain = ipa3_get_smmu_domain();
+ if (ipa3_ctx->s1_bypass_arr[cb_type]) {
+ IPADBG("CB# %d is set to s1 bypass\n", cb_type);
+ return 0;
+ }
+
+ smmu_domain = ipa3_get_smmu_domain_by_type(cb_type);
if (!smmu_domain) {
IPAERR("invalid smmu domain\n");
return -EINVAL;
diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_i.h b/drivers/platform/msm/ipa/ipa_v3/ipa_i.h
index 6f87ece..01049d3 100644
--- a/drivers/platform/msm/ipa/ipa_v3/ipa_i.h
+++ b/drivers/platform/msm/ipa/ipa_v3/ipa_i.h
@@ -116,6 +116,14 @@
} \
} while (0)
+/* round addresses for closes page per SMMU requirements */
+#define IPA_SMMU_ROUND_TO_PAGE(iova, pa, size, iova_p, pa_p, size_p) \
+ do { \
+ (iova_p) = rounddown((iova), PAGE_SIZE); \
+ (pa_p) = rounddown((pa), PAGE_SIZE); \
+ (size_p) = roundup((size) + (pa) - (pa_p), PAGE_SIZE); \
+ } while (0)
+
#define WLAN_AMPDU_TX_EP 15
#define WLAN_PROD_TX_EP 19
#define WLAN1_CONS_RX_EP 14
@@ -1914,7 +1922,8 @@
int ipa3_setup_uc_ntn_pipes(struct ipa_ntn_conn_in_params *in,
ipa_notify_cb notify, void *priv, u8 hdr_len,
struct ipa_ntn_conn_out_params *outp);
-int ipa3_tear_down_uc_offload_pipes(int ipa_ep_idx_ul, int ipa_ep_idx_dl);
+int ipa3_tear_down_uc_offload_pipes(int ipa_ep_idx_ul, int ipa_ep_idx_dl,
+ struct ipa_ntn_conn_in_params *params);
int ipa3_ntn_uc_reg_rdyCB(void (*ipauc_ready_cb)(void *), void *priv);
void ipa3_ntn_uc_dereg_rdyCB(void);
int ipa3_conn_wdi3_pipes(struct ipa_wdi_conn_in_params *in,
@@ -2291,6 +2300,8 @@
struct iommu_domain *ipa3_get_smmu_domain(void);
struct iommu_domain *ipa3_get_uc_smmu_domain(void);
struct iommu_domain *ipa3_get_wlan_smmu_domain(void);
+struct iommu_domain *ipa3_get_smmu_domain_by_type
+ (enum ipa_smmu_cb_type cb_type);
int ipa3_iommu_map(struct iommu_domain *domain, unsigned long iova,
phys_addr_t paddr, size_t size, int prot);
int ipa3_ap_suspend(struct device *dev);
@@ -2325,8 +2336,10 @@
int ipa_gsi_ch20_wa(void);
int ipa3_rx_poll(u32 clnt_hdl, int budget);
void ipa3_recycle_wan_skb(struct sk_buff *skb);
-int ipa3_smmu_map_peer_reg(phys_addr_t phys_addr, bool map);
-int ipa3_smmu_map_peer_buff(u64 iova, u32 size, bool map, struct sg_table *sgt);
+int ipa3_smmu_map_peer_reg(phys_addr_t phys_addr, bool map,
+ enum ipa_smmu_cb_type cb_type);
+int ipa3_smmu_map_peer_buff(u64 iova, u32 size, bool map, struct sg_table *sgt,
+ enum ipa_smmu_cb_type cb_type);
void ipa3_reset_freeze_vote(void);
int ipa3_ntn_init(void);
int ipa3_get_ntn_stats(struct Ipa3HwStatsNTNInfoData_t *stats);
diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_uc_ntn.c b/drivers/platform/msm/ipa/ipa_v3/ipa_uc_ntn.c
index f821dd2..f4068bf 100644
--- a/drivers/platform/msm/ipa/ipa_v3/ipa_uc_ntn.c
+++ b/drivers/platform/msm/ipa/ipa_v3/ipa_uc_ntn.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2016-2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2016-2018, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -221,8 +221,11 @@
IPADBG("ring_base_pa = 0x%pa\n",
&ntn_info->ring_base_pa);
+ IPADBG("ring_base_iova = 0x%pa\n",
+ &ntn_info->ring_base_iova);
IPADBG("ntn_ring_size = %d\n", ntn_info->ntn_ring_size);
IPADBG("buff_pool_base_pa = 0x%pa\n", &ntn_info->buff_pool_base_pa);
+ IPADBG("buff_pool_base_iova = 0x%pa\n", &ntn_info->buff_pool_base_iova);
IPADBG("num_buffers = %d\n", ntn_info->num_buffers);
IPADBG("data_buff_size = %d\n", ntn_info->data_buff_size);
IPADBG("tail_ptr_base_pa = 0x%pa\n", &ntn_info->ntn_reg_base_ptr_pa);
@@ -248,8 +251,15 @@
Ntn_params = &cmd_data->SetupCh_params.NtnSetupCh_params;
}
- Ntn_params->ring_base_pa = ntn_info->ring_base_pa;
- Ntn_params->buff_pool_base_pa = ntn_info->buff_pool_base_pa;
+ if (ntn_info->smmu_enabled) {
+ Ntn_params->ring_base_pa = (u32)ntn_info->ring_base_iova;
+ Ntn_params->buff_pool_base_pa =
+ (u32)ntn_info->buff_pool_base_iova;
+ } else {
+ Ntn_params->ring_base_pa = ntn_info->ring_base_pa;
+ Ntn_params->buff_pool_base_pa = ntn_info->buff_pool_base_pa;
+ }
+
Ntn_params->ntn_ring_size = ntn_info->ntn_ring_size;
Ntn_params->num_buffers = ntn_info->num_buffers;
Ntn_params->ntn_reg_base_ptr_pa = ntn_info->ntn_reg_base_ptr_pa;
@@ -268,6 +278,128 @@
return result;
}
+static int ipa3_smmu_map_uc_ntn_pipes(struct ipa_ntn_setup_info *params,
+ bool map)
+{
+ struct iommu_domain *smmu_domain;
+ int result;
+ int i;
+ u64 iova;
+ phys_addr_t pa;
+ u64 iova_p;
+ phys_addr_t pa_p;
+ u32 size_p;
+
+ if (params->data_buff_size > PAGE_SIZE) {
+ IPAERR("invalid data buff size\n");
+ return -EINVAL;
+ }
+
+ result = ipa3_smmu_map_peer_reg(rounddown(params->ntn_reg_base_ptr_pa,
+ PAGE_SIZE), map, IPA_SMMU_CB_UC);
+ if (result) {
+ IPAERR("failed to %s uC regs %d\n",
+ map ? "map" : "unmap", result);
+ goto fail;
+ }
+
+ if (params->smmu_enabled) {
+ IPADBG("smmu is enabled on EMAC\n");
+ result = ipa3_smmu_map_peer_buff((u64)params->ring_base_iova,
+ params->ntn_ring_size, map, params->ring_base_sgt,
+ IPA_SMMU_CB_UC);
+ if (result) {
+ IPAERR("failed to %s ntn ring %d\n",
+ map ? "map" : "unmap", result);
+ goto fail_map_ring;
+ }
+ result = ipa3_smmu_map_peer_buff(
+ (u64)params->buff_pool_base_iova,
+ params->num_buffers * 4, map,
+ params->buff_pool_base_sgt, IPA_SMMU_CB_UC);
+ if (result) {
+ IPAERR("failed to %s pool buffs %d\n",
+ map ? "map" : "unmap", result);
+ goto fail_map_buffer_smmu_enabled;
+ }
+ } else {
+ IPADBG("smmu is disabled on EMAC\n");
+ result = ipa3_smmu_map_peer_buff((u64)params->ring_base_pa,
+ params->ntn_ring_size, map, NULL, IPA_SMMU_CB_UC);
+ if (result) {
+ IPAERR("failed to %s ntn ring %d\n",
+ map ? "map" : "unmap", result);
+ goto fail_map_ring;
+ }
+ result = ipa3_smmu_map_peer_buff(params->buff_pool_base_pa,
+ params->num_buffers * 4, map, NULL, IPA_SMMU_CB_UC);
+ if (result) {
+ IPAERR("failed to %s pool buffs %d\n",
+ map ? "map" : "unmap", result);
+ goto fail_map_buffer_smmu_disabled;
+ }
+ }
+
+ if (ipa3_ctx->s1_bypass_arr[IPA_SMMU_CB_AP]) {
+ IPADBG("AP SMMU is set to s1 bypass\n");
+ return 0;
+ }
+
+ smmu_domain = ipa3_get_smmu_domain();
+ if (!smmu_domain) {
+ IPAERR("invalid smmu domain\n");
+ return -EINVAL;
+ }
+
+ for (i = 0; i < params->num_buffers; i++) {
+ iova = (u64)params->data_buff_list[i].iova;
+ pa = (phys_addr_t)params->data_buff_list[i].pa;
+ IPA_SMMU_ROUND_TO_PAGE(iova, pa, params->data_buff_size, iova_p,
+ pa_p, size_p);
+ IPADBG("%s 0x%llx to 0x%pa size %d\n", map ? "mapping" :
+ "unmapping", iova_p, &pa_p, size_p);
+ if (map) {
+ result = ipa3_iommu_map(smmu_domain, iova_p, pa_p,
+ size_p, IOMMU_READ | IOMMU_WRITE);
+ if (result)
+ IPAERR("Fail to map 0x%llx\n", iova);
+ } else {
+ result = iommu_unmap(smmu_domain, iova_p, size_p);
+ if (result != params->data_buff_size)
+ IPAERR("Fail to unmap 0x%llx\n", iova);
+ }
+ if (result) {
+ if (params->smmu_enabled)
+ goto fail_map_data_buff_smmu_enabled;
+ else
+ goto fail_map_data_buff_smmu_disabled;
+ }
+ }
+ return 0;
+
+fail_map_data_buff_smmu_enabled:
+ ipa3_smmu_map_peer_buff((u64)params->buff_pool_base_iova,
+ params->num_buffers * 4, !map, NULL, IPA_SMMU_CB_UC);
+ goto fail_map_buffer_smmu_enabled;
+fail_map_data_buff_smmu_disabled:
+ ipa3_smmu_map_peer_buff(params->buff_pool_base_pa,
+ params->num_buffers * 4, !map, NULL, IPA_SMMU_CB_UC);
+ goto fail_map_buffer_smmu_disabled;
+fail_map_buffer_smmu_enabled:
+ ipa3_smmu_map_peer_buff((u64)params->ring_base_iova,
+ params->ntn_ring_size, !map, params->ring_base_sgt,
+ IPA_SMMU_CB_UC);
+ goto fail_map_ring;
+fail_map_buffer_smmu_disabled:
+ ipa3_smmu_map_peer_buff((u64)params->ring_base_pa,
+ params->ntn_ring_size, !map, NULL, IPA_SMMU_CB_UC);
+fail_map_ring:
+ ipa3_smmu_map_peer_reg(rounddown(params->ntn_reg_base_ptr_pa,
+ PAGE_SIZE), !map, IPA_SMMU_CB_UC);
+fail:
+ return result;
+}
+
/**
* ipa3_setup_uc_ntn_pipes() - setup uc offload pipes
*/
@@ -324,10 +456,16 @@
goto fail;
}
+ result = ipa3_smmu_map_uc_ntn_pipes(&in->ul, true);
+ if (result) {
+ IPAERR("failed to map SMMU for UL %d\n", result);
+ goto fail;
+ }
+
if (ipa3_uc_send_ntn_setup_pipe_cmd(&in->ul, IPA_NTN_RX_DIR)) {
IPAERR("fail to send cmd to uc for ul pipe\n");
result = -EFAULT;
- goto fail;
+ goto fail_smmu_map_ul;
}
ipa3_install_dflt_flt_rules(ipa_ep_idx_ul);
outp->ul_uc_db_pa = IPA_UC_NTN_DB_PA_RX;
@@ -346,13 +484,19 @@
if (ipa3_cfg_ep(ipa_ep_idx_dl, &ep_dl->cfg)) {
IPAERR("fail to setup dl pipe cfg\n");
result = -EFAULT;
- goto fail;
+ goto fail_smmu_map_ul;
+ }
+
+ result = ipa3_smmu_map_uc_ntn_pipes(&in->dl, true);
+ if (result) {
+ IPAERR("failed to map SMMU for DL %d\n", result);
+ goto fail_smmu_map_ul;
}
if (ipa3_uc_send_ntn_setup_pipe_cmd(&in->dl, IPA_NTN_TX_DIR)) {
IPAERR("fail to send cmd to uc for dl pipe\n");
result = -EFAULT;
- goto fail;
+ goto fail_smmu_map_dl;
}
outp->dl_uc_db_pa = IPA_UC_NTN_DB_PA_TX;
ep_dl->uc_offload_state |= IPA_UC_OFFLOAD_CONNECTED;
@@ -362,11 +506,17 @@
IPAERR("Enable data path failed res=%d clnt=%d.\n", result,
ipa_ep_idx_dl);
result = -EFAULT;
- goto fail;
+ goto fail_smmu_map_dl;
}
IPADBG("client %d (ep: %d) connected\n", in->dl.client,
ipa_ep_idx_dl);
+ return 0;
+
+fail_smmu_map_dl:
+ ipa3_smmu_map_uc_ntn_pipes(&in->dl, false);
+fail_smmu_map_ul:
+ ipa3_smmu_map_uc_ntn_pipes(&in->ul, false);
fail:
IPA_ACTIVE_CLIENTS_DEC_SIMPLE();
return result;
@@ -377,7 +527,7 @@
*/
int ipa3_tear_down_uc_offload_pipes(int ipa_ep_idx_ul,
- int ipa_ep_idx_dl)
+ int ipa_ep_idx_dl, struct ipa_ntn_conn_in_params *params)
{
struct ipa_mem_buffer cmd;
struct ipa3_ep_context *ep_ul, *ep_dl;
@@ -442,6 +592,13 @@
goto fail;
}
+ /* unmap the DL pipe */
+ result = ipa3_smmu_map_uc_ntn_pipes(¶ms->dl, false);
+ if (result) {
+ IPAERR("failed to unmap SMMU for DL %d\n", result);
+ goto fail;
+ }
+
/* teardown the UL pipe */
tear->params.ipa_pipe_number = ipa_ep_idx_ul;
result = ipa3_uc_send_cmd((u32)(cmd.phys_base),
@@ -453,6 +610,14 @@
result = -EFAULT;
goto fail;
}
+
+ /* unmap the UL pipe */
+ result = ipa3_smmu_map_uc_ntn_pipes(¶ms->ul, false);
+ if (result) {
+ IPAERR("failed to unmap SMMU for UL %d\n", result);
+ goto fail;
+ }
+
ipa3_delete_dflt_flt_rules(ipa_ep_idx_ul);
memset(&ipa3_ctx->ep[ipa_ep_idx_ul], 0, sizeof(struct ipa3_ep_context));
IPADBG("ul client (ep: %d) disconnected\n", ipa_ep_idx_ul);
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/power/supply/qcom/qg-core.h b/drivers/power/supply/qcom/qg-core.h
index 55dba4e..6e44f33 100644
--- a/drivers/power/supply/qcom/qg-core.h
+++ b/drivers/power/supply/qcom/qg-core.h
@@ -29,7 +29,9 @@
struct qg_dt {
int vbatt_empty_mv;
+ int vbatt_empty_cold_mv;
int vbatt_low_mv;
+ int vbatt_low_cold_mv;
int vbatt_cutoff_mv;
int iterm_ma;
int s2_fifo_length;
@@ -44,6 +46,7 @@
int delta_soc;
int rbat_conn_mohm;
int ignore_shutdown_soc_secs;
+ int cold_temp_threshold;
bool hold_soc_while_full;
bool linearize_soc;
};
@@ -147,6 +150,7 @@
enum qg_wa_flags {
QG_VBAT_LOW_WA = BIT(0),
+ QG_RECHARGE_SOC_WA = BIT(1),
};
diff --git a/drivers/power/supply/qcom/qg-soc.c b/drivers/power/supply/qcom/qg-soc.c
index 660f6f1..c4d5043 100644
--- a/drivers/power/supply/qcom/qg-soc.c
+++ b/drivers/power/supply/qcom/qg-soc.c
@@ -24,11 +24,15 @@
#define DEFAULT_UPDATE_TIME_MS 64000
#define SOC_SCALE_HYST_MS 2000
-#define SOC_SCALE_LOW_TEMP_THRESHOLD 100
static int qg_delta_soc_interval_ms = 20000;
module_param_named(
- delta_soc_interval_ms, qg_delta_soc_interval_ms, int, 0600
+ soc_interval_ms, qg_delta_soc_interval_ms, int, 0600
+);
+
+static int qg_delta_soc_cold_interval_ms = 4000;
+module_param_named(
+ soc_cold_interval_ms, qg_delta_soc_cold_interval_ms, int, 0600
);
static void get_next_update_time(struct qpnp_qg *chip)
@@ -52,9 +56,11 @@
rc = qg_get_battery_temp(chip, &batt_temp);
if (rc < 0)
pr_err("Failed to read battery temperature rc=%d\n", rc);
+ else if (batt_temp < chip->dt.cold_temp_threshold)
+ min_delta_soc_interval_ms = qg_delta_soc_cold_interval_ms;
- if (batt_temp < SOC_SCALE_LOW_TEMP_THRESHOLD)
- min_delta_soc_interval_ms = min_delta_soc_interval_ms / 2;
+ if (!min_delta_soc_interval_ms)
+ min_delta_soc_interval_ms = 1000; /* 1 second */
chip->next_wakeup_ms = (full_time_ms / (soc_points + 1))
- SOC_SCALE_HYST_MS;
diff --git a/drivers/power/supply/qcom/qpnp-qg.c b/drivers/power/supply/qcom/qpnp-qg.c
index effb093..f7a6b7a 100644
--- a/drivers/power/supply/qcom/qpnp-qg.c
+++ b/drivers/power/supply/qcom/qpnp-qg.c
@@ -114,9 +114,11 @@
return rc;
}
+#define DEFAULT_S3_FIFO_LENGTH 3
static int qg_update_fifo_length(struct qpnp_qg *chip, u8 length)
{
int rc;
+ u8 s3_entry_fifo_length = 0;
if (!length || length > 8) {
pr_err("Invalid FIFO length %d\n", length);
@@ -128,6 +130,21 @@
if (rc < 0)
pr_err("Failed to write S2 FIFO length, rc=%d\n", rc);
+ /* update the S3 FIFO length, when S2 length is updated */
+ if (length > 3)
+ s3_entry_fifo_length = (chip->dt.s3_entry_fifo_length > 0) ?
+ chip->dt.s3_entry_fifo_length : DEFAULT_S3_FIFO_LENGTH;
+ else /* Use S3 length as 1 for any S2 length <= 3 */
+ s3_entry_fifo_length = 1;
+
+ rc = qg_masked_write(chip,
+ chip->qg_base + QG_S3_SLEEP_OCV_IBAT_CTL1_REG,
+ SLEEP_IBAT_QUALIFIED_LENGTH_MASK,
+ s3_entry_fifo_length - 1);
+ if (rc < 0)
+ pr_err("Failed to write S3-entry fifo-length, rc=%d\n",
+ rc);
+
return rc;
}
@@ -412,8 +429,19 @@
#define VBAT_LOW_HYST_UV 50000 /* 50mV */
static int qg_vbat_low_wa(struct qpnp_qg *chip)
{
- int rc, i;
- u32 vbat_low_uv = chip->dt.vbatt_low_mv * 1000 + VBAT_LOW_HYST_UV;
+ int rc, i, temp = 0;
+ u32 vbat_low_uv = 0;
+
+ rc = qg_get_battery_temp(chip, &temp);
+ if (rc < 0) {
+ pr_err("Failed to read batt_temp rc=%d\n", rc);
+ temp = 250;
+ }
+
+ vbat_low_uv = 1000 * ((temp < chip->dt.cold_temp_threshold) ?
+ chip->dt.vbatt_low_cold_mv :
+ chip->dt.vbatt_low_mv);
+ vbat_low_uv += VBAT_LOW_HYST_UV;
if (!(chip->wa_flags & QG_VBAT_LOW_WA) || !chip->vbat_low)
return 0;
@@ -459,6 +487,73 @@
return rc;
}
+static int qg_vbat_thresholds_config(struct qpnp_qg *chip)
+{
+ int rc, temp = 0, vbat_mv;
+ u8 reg;
+
+ rc = qg_get_battery_temp(chip, &temp);
+ if (rc < 0) {
+ pr_err("Failed to read batt_temp rc=%d\n", rc);
+ return rc;
+ }
+
+ vbat_mv = (temp < chip->dt.cold_temp_threshold) ?
+ chip->dt.vbatt_empty_cold_mv :
+ chip->dt.vbatt_empty_mv;
+
+ rc = qg_read(chip, chip->qg_base + QG_VBAT_EMPTY_THRESHOLD_REG,
+ ®, 1);
+ if (rc < 0) {
+ pr_err("Failed to read vbat-empty, rc=%d\n", rc);
+ return rc;
+ }
+
+ if (vbat_mv == (reg * 50)) /* No change */
+ goto config_vbat_low;
+
+ reg = vbat_mv / 50;
+ rc = qg_write(chip, chip->qg_base + QG_VBAT_EMPTY_THRESHOLD_REG,
+ ®, 1);
+ if (rc < 0) {
+ pr_err("Failed to write vbat-empty, rc=%d\n", rc);
+ return rc;
+ }
+
+ qg_dbg(chip, QG_DEBUG_STATUS,
+ "VBAT EMPTY threshold updated to %dmV temp=%d\n",
+ vbat_mv, temp);
+
+config_vbat_low:
+ vbat_mv = (temp < chip->dt.cold_temp_threshold) ?
+ chip->dt.vbatt_low_cold_mv :
+ chip->dt.vbatt_low_mv;
+
+ rc = qg_read(chip, chip->qg_base + QG_VBAT_LOW_THRESHOLD_REG,
+ ®, 1);
+ if (rc < 0) {
+ pr_err("Failed to read vbat-low, rc=%d\n", rc);
+ return rc;
+ }
+
+ if (vbat_mv == (reg * 50)) /* No change */
+ return 0;
+
+ reg = vbat_mv / 50;
+ rc = qg_write(chip, chip->qg_base + QG_VBAT_LOW_THRESHOLD_REG,
+ ®, 1);
+ if (rc < 0) {
+ pr_err("Failed to write vbat-low, rc=%d\n", rc);
+ return rc;
+ }
+
+ qg_dbg(chip, QG_DEBUG_STATUS,
+ "VBAT LOW threshold updated to %dmV temp=%d\n",
+ vbat_mv, temp);
+
+ return rc;
+}
+
#define MIN_FIFO_FULL_TIME_MS 12000
static int process_rt_fifo_data(struct qpnp_qg *chip,
bool vbat_low, bool update_smb)
@@ -609,6 +704,10 @@
goto done;
}
+ rc = qg_vbat_thresholds_config(chip);
+ if (rc < 0)
+ pr_err("Failed to apply VBAT EMPTY config rc=%d\n", rc);
+
rc = qg_vbat_low_wa(chip);
if (rc < 0) {
pr_err("Failed to apply VBAT LOW WA, rc=%d\n", rc);
@@ -1081,12 +1180,26 @@
chip->charge_full = true;
qg_dbg(chip, QG_DEBUG_STATUS, "Setting charge_full (0->1) @ msoc=%d\n",
chip->msoc);
- } else {
+ } else if (health != POWER_SUPPLY_HEALTH_GOOD) {
+ /* terminated in JEITA */
qg_dbg(chip, QG_DEBUG_STATUS, "Terminated charging @ msoc=%d\n",
chip->msoc);
}
} else if ((!chip->charge_done || chip->msoc < recharge_soc)
&& chip->charge_full) {
+
+ if (chip->wa_flags & QG_RECHARGE_SOC_WA) {
+ /* Force recharge */
+ prop.intval = 0;
+ rc = power_supply_set_property(chip->batt_psy,
+ POWER_SUPPLY_PROP_RECHARGE_SOC, &prop);
+ if (rc < 0)
+ pr_err("Failed to force recharge rc=%d\n", rc);
+ else
+ qg_dbg(chip, QG_DEBUG_STATUS,
+ "Forced recharge\n");
+ }
+
/*
* If recharge or discharge has started and
* if linearize soc dtsi property defined
@@ -1668,6 +1781,7 @@
{
switch (chip->pmic_rev_id->pmic_subtype) {
case PMI632_SUBTYPE:
+ chip->wa_flags |= QG_RECHARGE_SOC_WA;
if (chip->pmic_rev_id->rev4 == PMI632_V1P0_REV4)
chip->wa_flags |= QG_VBAT_LOW_WA;
break;
@@ -1840,31 +1954,25 @@
}
}
- /* vbat low */
+ /* vbat based configs */
if (chip->dt.vbatt_low_mv < 0)
chip->dt.vbatt_low_mv = 0;
else if (chip->dt.vbatt_low_mv > 12750)
chip->dt.vbatt_low_mv = 12750;
- reg = chip->dt.vbatt_low_mv / 50;
- rc = qg_write(chip, chip->qg_base + QG_VBAT_LOW_THRESHOLD_REG,
- ®, 1);
- if (rc < 0) {
- pr_err("Failed to write vbat-low, rc=%d\n", rc);
- return rc;
- }
-
- /* vbat empty */
if (chip->dt.vbatt_empty_mv < 0)
chip->dt.vbatt_empty_mv = 0;
else if (chip->dt.vbatt_empty_mv > 12750)
chip->dt.vbatt_empty_mv = 12750;
- reg = chip->dt.vbatt_empty_mv / 50;
- rc = qg_write(chip, chip->qg_base + QG_VBAT_EMPTY_THRESHOLD_REG,
- ®, 1);
+ if (chip->dt.vbatt_empty_cold_mv < 0)
+ chip->dt.vbatt_empty_cold_mv = 0;
+ else if (chip->dt.vbatt_empty_cold_mv > 12750)
+ chip->dt.vbatt_empty_cold_mv = 12750;
+
+ rc = qg_vbat_thresholds_config(chip);
if (rc < 0) {
- pr_err("Failed to write vbat-empty, rc=%d\n", rc);
+ pr_err("Failed to configure VBAT empty/low rc=%d\n", rc);
return rc;
}
@@ -1962,8 +2070,10 @@
}
#define DEFAULT_VBATT_EMPTY_MV 3200
+#define DEFAULT_VBATT_EMPTY_COLD_MV 3000
#define DEFAULT_VBATT_CUTOFF_MV 3400
#define DEFAULT_VBATT_LOW_MV 3500
+#define DEFAULT_VBATT_LOW_COLD_MV 3800
#define DEFAULT_ITERM_MA 100
#define DEFAULT_S2_FIFO_LENGTH 5
#define DEFAULT_S2_VBAT_LOW_LENGTH 2
@@ -1971,6 +2081,7 @@
#define DEFAULT_S2_ACC_INTVL_MS 100
#define DEFAULT_DELTA_SOC 1
#define DEFAULT_SHUTDOWN_SOC_SECS 360
+#define DEFAULT_COLD_TEMP_THRESHOLD 0
static int qg_parse_dt(struct qpnp_qg *chip)
{
int rc = 0;
@@ -2103,12 +2214,30 @@
else
chip->dt.vbatt_empty_mv = temp;
+ rc = of_property_read_u32(node, "qcom,vbatt-empty-cold-mv", &temp);
+ if (rc < 0)
+ chip->dt.vbatt_empty_cold_mv = DEFAULT_VBATT_EMPTY_COLD_MV;
+ else
+ chip->dt.vbatt_empty_cold_mv = temp;
+
+ rc = of_property_read_u32(node, "qcom,cold-temp-threshold", &temp);
+ if (rc < 0)
+ chip->dt.cold_temp_threshold = DEFAULT_COLD_TEMP_THRESHOLD;
+ else
+ chip->dt.cold_temp_threshold = temp;
+
rc = of_property_read_u32(node, "qcom,vbatt-low-mv", &temp);
if (rc < 0)
chip->dt.vbatt_low_mv = DEFAULT_VBATT_LOW_MV;
else
chip->dt.vbatt_low_mv = temp;
+ rc = of_property_read_u32(node, "qcom,vbatt-low-cold-mv", &temp);
+ if (rc < 0)
+ chip->dt.vbatt_low_cold_mv = DEFAULT_VBATT_LOW_COLD_MV;
+ else
+ chip->dt.vbatt_low_cold_mv = temp;
+
rc = of_property_read_u32(node, "qcom,vbatt-cutoff-mv", &temp);
if (rc < 0)
chip->dt.vbatt_cutoff_mv = DEFAULT_VBATT_CUTOFF_MV;
diff --git a/drivers/power/supply/qcom/qpnp-smb2.c b/drivers/power/supply/qcom/qpnp-smb2.c
index ccc0d5d..286d305 100644
--- a/drivers/power/supply/qcom/qpnp-smb2.c
+++ b/drivers/power/supply/qcom/qpnp-smb2.c
@@ -2119,6 +2119,7 @@
[SWITCH_POWER_OK_IRQ] = {
.name = "switcher-power-ok",
.handler = smblib_handle_switcher_power_ok,
+ .wake = true,
.storm_data = {true, 1000, 8},
},
};
diff --git a/drivers/power/supply/qcom/qpnp-smb5.c b/drivers/power/supply/qcom/qpnp-smb5.c
index 20dd78e..7a3a4dc 100644
--- a/drivers/power/supply/qcom/qpnp-smb5.c
+++ b/drivers/power/supply/qcom/qpnp-smb5.c
@@ -949,6 +949,7 @@
*************************/
static enum power_supply_property smb5_batt_props[] = {
POWER_SUPPLY_PROP_INPUT_SUSPEND,
+ POWER_SUPPLY_PROP_BATTERY_CHARGING_ENABLED,
POWER_SUPPLY_PROP_STATUS,
POWER_SUPPLY_PROP_HEALTH,
POWER_SUPPLY_PROP_PRESENT,
@@ -995,6 +996,10 @@
case POWER_SUPPLY_PROP_INPUT_SUSPEND:
rc = smblib_get_prop_input_suspend(chg, val);
break;
+ case POWER_SUPPLY_PROP_BATTERY_CHARGING_ENABLED:
+ val->intval = !get_client_vote(chg->chg_disable_votable,
+ USER_VOTER);
+ break;
case POWER_SUPPLY_PROP_CHARGE_TYPE:
rc = smblib_get_prop_batt_charge_type(chg, val);
break;
@@ -1093,6 +1098,9 @@
case POWER_SUPPLY_PROP_INPUT_SUSPEND:
rc = smblib_set_prop_input_suspend(chg, val);
break;
+ case POWER_SUPPLY_PROP_BATTERY_CHARGING_ENABLED:
+ vote(chg->chg_disable_votable, USER_VOTER, !val->intval, 0);
+ break;
case POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT:
rc = smblib_set_prop_system_temp_level(chg, val);
break;
@@ -1147,6 +1155,17 @@
chg->die_health = val->intval;
power_supply_changed(chg->batt_psy);
break;
+ case POWER_SUPPLY_PROP_RECHARGE_SOC:
+ if (chg->smb_version == PMI632_SUBTYPE) {
+ /* toggle charging to force recharge */
+ vote(chg->chg_disable_votable, FORCE_RECHARGE_VOTER,
+ true, 0);
+ /* charge disable delay */
+ msleep(50);
+ vote(chg->chg_disable_votable, FORCE_RECHARGE_VOTER,
+ false, 0);
+ }
+ break;
default:
rc = -EINVAL;
}
@@ -1169,6 +1188,7 @@
case POWER_SUPPLY_PROP_STEP_CHARGING_ENABLED:
case POWER_SUPPLY_PROP_SW_JEITA_ENABLED:
case POWER_SUPPLY_PROP_DIE_HEALTH:
+ case POWER_SUPPLY_PROP_BATTERY_CHARGING_ENABLED:
return 1;
default:
break;
diff --git a/drivers/power/supply/qcom/qpnp-smbcharger.c b/drivers/power/supply/qcom/qpnp-smbcharger.c
index 403b670..59478a9 100644
--- a/drivers/power/supply/qcom/qpnp-smbcharger.c
+++ b/drivers/power/supply/qcom/qpnp-smbcharger.c
@@ -352,6 +352,7 @@
#define WEAK_CHARGER_ICL_VOTER "WEAK_CHARGER_ICL_VOTER"
#define SW_AICL_ICL_VOTER "SW_AICL_ICL_VOTER"
#define CHG_SUSPEND_WORKAROUND_ICL_VOTER "CHG_SUSPEND_WORKAROUND_ICL_VOTER"
+#define SHUTDOWN_WORKAROUND_ICL_VOTER "SHUTDOWN_WORKAROUND_ICL_VOTER"
/* USB SUSPEND VOTERS */
/* userspace has suspended charging altogether */
@@ -1061,6 +1062,33 @@
return ua;
}
+#define DEFAULT_BATT_RESISTANCE_ID 0
+static int get_prop_batt_resistance_id(struct smbchg_chip *chip)
+{
+ int rbatt, rc;
+
+ rc = get_property_from_fg(chip, POWER_SUPPLY_PROP_RESISTANCE_ID,
+ &rbatt);
+ if (rc) {
+ pr_smb(PR_STATUS, "Couldn't get resistance id rc = %d\n", rc);
+ rbatt = DEFAULT_BATT_RESISTANCE_ID;
+ }
+ return rbatt;
+}
+
+#define DEFAULT_BATT_FULL_CHG_CAPACITY 0
+static int get_prop_batt_full_charge(struct smbchg_chip *chip)
+{
+ int bfc, rc;
+
+ rc = get_property_from_fg(chip, POWER_SUPPLY_PROP_CHARGE_FULL, &bfc);
+ if (rc) {
+ pr_smb(PR_STATUS, "Couldn't get charge_full rc = %d\n", rc);
+ bfc = DEFAULT_BATT_FULL_CHG_CAPACITY;
+ }
+ return bfc;
+}
+
#define DEFAULT_BATT_VOLTAGE_NOW 0
static int get_prop_batt_voltage_now(struct smbchg_chip *chip)
{
@@ -1876,6 +1904,7 @@
return 0;
}
+ fg_current_now = abs(fg_current_now) / 1000;
icl_ma = max(chip->iterm_ma + ESR_PULSE_CURRENT_DELTA_MA,
fg_current_now - ESR_PULSE_CURRENT_DELTA_MA);
rc = vote(chip->fcc_votable, ESR_PULSE_FCC_VOTER, en, icl_ma);
@@ -4013,11 +4042,11 @@
reg = CHG_LED_OFF << CHG_LED_SHIFT;
} else {
if (blinking == 1)
- reg = LED_BLINKING_PATTERN1 << CHG_LED_SHIFT;
- else if (blinking == 2)
reg = LED_BLINKING_PATTERN2 << CHG_LED_SHIFT;
- else
+ else if (blinking == 2)
reg = LED_BLINKING_PATTERN1 << CHG_LED_SHIFT;
+ else
+ reg = LED_BLINKING_PATTERN2 << CHG_LED_SHIFT;
}
rc = smbchg_sec_masked_write(chip,
@@ -4410,6 +4439,20 @@
if (type == POWER_SUPPLY_TYPE_UNKNOWN)
chip->usb_supply_type = type;
+ /*
+ * Update TYPE property to DCP for HVDCP/HVDCP3 charger types
+ * so that they can be recongized as AC chargers by healthd.
+ * Don't report UNKNOWN charger type to prevent healthd missing
+ * detecting this power_supply status change.
+ */
+ if (chip->usb_supply_type == POWER_SUPPLY_TYPE_USB_HVDCP_3
+ || chip->usb_supply_type == POWER_SUPPLY_TYPE_USB_HVDCP)
+ chip->usb_psy_d.type = POWER_SUPPLY_TYPE_USB_DCP;
+ else if (chip->usb_supply_type == POWER_SUPPLY_TYPE_UNKNOWN)
+ chip->usb_psy_d.type = POWER_SUPPLY_TYPE_USB;
+ else
+ chip->usb_psy_d.type = chip->usb_supply_type;
+
if (!chip->skip_usb_notification)
power_supply_changed(chip->usb_psy);
@@ -4602,6 +4645,8 @@
/* Clear typec current status */
if (chip->typec_psy)
chip->typec_current_ma = 0;
+ /* cancel/wait for hvdcp pending work if any */
+ cancel_delayed_work_sync(&chip->hvdcp_det_work);
smbchg_change_usb_supply_type(chip, POWER_SUPPLY_TYPE_UNKNOWN);
extcon_set_cable_state_(chip->extcon, EXTCON_USB, chip->usb_present);
if (chip->dpdm_reg)
@@ -5226,6 +5271,15 @@
*/
chip->parallel.enabled_once = false;
+ /* Enable AICL */
+ pr_smb(PR_MISC, "Enable AICL\n");
+ rc = smbchg_sec_masked_write(chip, chip->usb_chgpth_base + USB_AICL_CFG,
+ AICL_EN_BIT, AICL_EN_BIT);
+ if (rc < 0) {
+ pr_err("Couldn't enable AICL rc=%d\n", rc);
+ goto out;
+ }
+
/* fake an insertion */
pr_smb(PR_MISC, "Faking Insertion\n");
rc = fake_insertion_removal(chip, true);
@@ -5235,15 +5289,6 @@
}
chip->hvdcp_3_det_ignore_uv = false;
- /* Enable AICL */
- pr_smb(PR_MISC, "Enable AICL\n");
- rc = smbchg_sec_masked_write(chip, chip->usb_chgpth_base + USB_AICL_CFG,
- AICL_EN_BIT, 0);
- if (rc < 0) {
- pr_err("Couldn't enable AICL rc=%d\n", rc);
- return rc;
- }
-
out:
/*
* There are many QC 2.0 chargers that collapse before the aicl deglitch
@@ -5276,49 +5321,63 @@
#define APSD_RERUN BIT(0)
static int rerun_apsd(struct smbchg_chip *chip)
{
- int rc;
+ int rc = 0;
- reinit_completion(&chip->src_det_raised);
- reinit_completion(&chip->usbin_uv_lowered);
- reinit_completion(&chip->src_det_lowered);
- reinit_completion(&chip->usbin_uv_raised);
+ chip->hvdcp_3_det_ignore_uv = true;
- /* re-run APSD */
- rc = smbchg_masked_write(chip, chip->usb_chgpth_base + USB_CMD_APSD,
- APSD_RERUN, APSD_RERUN);
- if (rc) {
- pr_err("Couldn't re-run APSD rc=%d\n", rc);
- return rc;
+ if (chip->schg_version == QPNP_SCHG_LITE) {
+ pr_smb(PR_STATUS, "Re-running APSD\n");
+ reinit_completion(&chip->src_det_raised);
+ reinit_completion(&chip->usbin_uv_lowered);
+ reinit_completion(&chip->src_det_lowered);
+ reinit_completion(&chip->usbin_uv_raised);
+
+ /* re-run APSD */
+ rc = smbchg_masked_write(chip,
+ chip->usb_chgpth_base + USB_CMD_APSD,
+ APSD_RERUN, APSD_RERUN);
+ if (rc) {
+ pr_err("Couldn't re-run APSD rc=%d\n", rc);
+ goto out;
+ }
+
+ pr_smb(PR_MISC, "Waiting on rising usbin uv\n");
+ rc = wait_for_usbin_uv(chip, true);
+ if (rc < 0) {
+ pr_err("wait for usbin uv failed rc = %d\n", rc);
+ goto out;
+ }
+
+ pr_smb(PR_MISC, "Waiting on falling src det\n");
+ rc = wait_for_src_detect(chip, false);
+ if (rc < 0) {
+ pr_err("wait for src detect failed rc = %d\n", rc);
+ goto out;
+ }
+
+ pr_smb(PR_MISC, "Waiting on falling usbin uv\n");
+ rc = wait_for_usbin_uv(chip, false);
+ if (rc < 0) {
+ pr_err("wait for usbin uv failed rc = %d\n", rc);
+ goto out;
+ }
+
+ pr_smb(PR_MISC, "Waiting on rising src det\n");
+ rc = wait_for_src_detect(chip, true);
+ if (rc < 0) {
+ pr_err("wait for src detect failed rc = %d\n", rc);
+ goto out;
+ }
+ } else {
+ pr_smb(PR_STATUS, "Faking Removal\n");
+ rc = fake_insertion_removal(chip, false);
+ msleep(500);
+ pr_smb(PR_STATUS, "Faking Insertion\n");
+ rc = fake_insertion_removal(chip, true);
}
- pr_smb(PR_MISC, "Waiting on rising usbin uv\n");
- rc = wait_for_usbin_uv(chip, true);
- if (rc < 0) {
- pr_err("wait for usbin uv failed rc = %d\n", rc);
- return rc;
- }
-
- pr_smb(PR_MISC, "Waiting on falling src det\n");
- rc = wait_for_src_detect(chip, false);
- if (rc < 0) {
- pr_err("wait for src detect failed rc = %d\n", rc);
- return rc;
- }
-
- pr_smb(PR_MISC, "Waiting on falling usbin uv\n");
- rc = wait_for_usbin_uv(chip, false);
- if (rc < 0) {
- pr_err("wait for usbin uv failed rc = %d\n", rc);
- return rc;
- }
-
- pr_smb(PR_MISC, "Waiting on rising src det\n");
- rc = wait_for_src_detect(chip, true);
- if (rc < 0) {
- pr_err("wait for src detect failed rc = %d\n", rc);
- return rc;
- }
-
+out:
+ chip->hvdcp_3_det_ignore_uv = false;
return rc;
}
@@ -5395,8 +5454,6 @@
smbchg_sec_masked_write(chip, chip->usb_chgpth_base + USB_AICL_CFG,
AICL_EN_BIT, 0);
- chip->hvdcp_3_det_ignore_uv = true;
-
/* re-run APSD */
rc = rerun_apsd(chip);
if (rc) {
@@ -5404,8 +5461,6 @@
goto out;
}
- chip->hvdcp_3_det_ignore_uv = false;
-
pr_smb(PR_MISC, "Enable AICL\n");
smbchg_sec_masked_write(chip, chip->usb_chgpth_base + USB_AICL_CFG,
AICL_EN_BIT, AICL_EN_BIT);
@@ -5434,6 +5489,10 @@
out:
chip->hvdcp_3_det_ignore_uv = false;
restore_from_hvdcp_detection(chip);
+ if (!is_src_detect_high(chip)) {
+ pr_smb(PR_MISC, "HVDCP removed - force removal\n");
+ update_usb_status(chip, 0, true);
+ }
return rc;
}
@@ -5453,6 +5512,10 @@
if (rc < 0)
pr_err("Couldn't retract HVDCP ICL vote rc=%d\n", rc);
+ if (!is_src_detect_high(chip)) {
+ pr_smb(PR_MISC, "HVDCP removed\n");
+ update_usb_status(chip, 0, 0);
+ }
smbchg_handle_hvdcp3_disable(chip);
return rc;
@@ -5631,6 +5694,9 @@
val->intval = chip->usb_online;
break;
case POWER_SUPPLY_PROP_TYPE:
+ val->intval = chip->usb_psy_d.type;
+ break;
+ case POWER_SUPPLY_PROP_REAL_TYPE:
val->intval = chip->usb_supply_type;
break;
case POWER_SUPPLY_PROP_HEALTH:
@@ -5688,6 +5754,7 @@
POWER_SUPPLY_PROP_ONLINE,
POWER_SUPPLY_PROP_CURRENT_MAX,
POWER_SUPPLY_PROP_TYPE,
+ POWER_SUPPLY_PROP_REAL_TYPE,
POWER_SUPPLY_PROP_HEALTH,
};
@@ -5733,6 +5800,8 @@
POWER_SUPPLY_PROP_CURRENT_NOW,
POWER_SUPPLY_PROP_TEMP,
POWER_SUPPLY_PROP_VOLTAGE_NOW,
+ POWER_SUPPLY_PROP_RESISTANCE_ID,
+ POWER_SUPPLY_PROP_CHARGE_FULL,
POWER_SUPPLY_PROP_SAFETY_TIMER_ENABLE,
POWER_SUPPLY_PROP_INPUT_CURRENT_MAX,
POWER_SUPPLY_PROP_INPUT_CURRENT_SETTLED,
@@ -5924,6 +5993,12 @@
case POWER_SUPPLY_PROP_VOLTAGE_NOW:
val->intval = get_prop_batt_voltage_now(chip);
break;
+ case POWER_SUPPLY_PROP_RESISTANCE_ID:
+ val->intval = get_prop_batt_resistance_id(chip);
+ break;
+ case POWER_SUPPLY_PROP_CHARGE_FULL:
+ val->intval = get_prop_batt_full_charge(chip);
+ break;
case POWER_SUPPLY_PROP_TEMP:
val->intval = get_prop_batt_temp(chip);
break;
@@ -6609,7 +6684,6 @@
} else {
usbid_change_handler(0, chip);
}
- src_detect_handler(0, chip);
chip->usb_present = is_usb_present(chip);
chip->dc_present = is_dc_present(chip);
@@ -7968,20 +8042,18 @@
pr_err("Couldn't vote for 300mA for suspend wa, going ahead rc=%d\n",
rc);
- pr_smb(PR_STATUS, "Faking Removal\n");
- fake_insertion_removal(chip, false);
- msleep(500);
- pr_smb(PR_STATUS, "Faking Insertion\n");
- fake_insertion_removal(chip, true);
+ rc = rerun_apsd(chip);
+ if (rc)
+ pr_err("APSD rerun failed rc=%d\n", rc);
read_usb_type(chip, &usb_type_name, &usb_supply_type);
if (usb_supply_type != POWER_SUPPLY_TYPE_USB_DCP) {
msleep(500);
- pr_smb(PR_STATUS, "Fake Removal again as type!=DCP\n");
- fake_insertion_removal(chip, false);
- msleep(500);
- pr_smb(PR_STATUS, "Fake Insert again as type!=DCP\n");
- fake_insertion_removal(chip, true);
+ pr_smb(PR_STATUS, "Rerun APSD as type !=DCP\n");
+
+ rc = rerun_apsd(chip);
+ if (rc)
+ pr_err("APSD rerun failed rc=%d\n", rc);
}
rc = vote(chip->usb_icl_votable,
@@ -7989,6 +8061,14 @@
if (rc < 0)
pr_err("Couldn't vote for 0 for suspend wa, going ahead rc=%d\n",
rc);
+
+ /* Schedule work for HVDCP detection */
+ if (!chip->hvdcp_not_supported) {
+ cancel_delayed_work_sync(&chip->hvdcp_det_work);
+ smbchg_stay_awake(chip, PM_DETECT_HVDCP);
+ schedule_delayed_work(&chip->hvdcp_det_work,
+ msecs_to_jiffies(HVDCP_NOTIFY_MS));
+ }
}
}
@@ -8385,6 +8465,12 @@
if (!is_hvdcp_present(chip))
return;
+ pr_smb(PR_MISC, "Reducing to 500mA\n");
+ rc = vote(chip->usb_icl_votable, SHUTDOWN_WORKAROUND_ICL_VOTER, true,
+ 500);
+ if (rc < 0)
+ pr_err("Couldn't vote 500mA ICL\n");
+
pr_smb(PR_MISC, "Disable Parallel\n");
mutex_lock(&chip->parallel.lock);
smbchg_parallel_en = 0;
diff --git a/drivers/power/supply/qcom/smb5-lib.h b/drivers/power/supply/qcom/smb5-lib.h
index 7ee4a3a..668ce5f 100644
--- a/drivers/power/supply/qcom/smb5-lib.h
+++ b/drivers/power/supply/qcom/smb5-lib.h
@@ -67,6 +67,7 @@
#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
#define WEAK_CHG_STORM_COUNT 8
diff --git a/drivers/soc/qcom/dcc_v2.c b/drivers/soc/qcom/dcc_v2.c
index 7c54073..e641290 100644
--- a/drivers/soc/qcom/dcc_v2.c
+++ b/drivers/soc/qcom/dcc_v2.c
@@ -887,7 +887,8 @@
goto err;
}
- if (!len) {
+ /* Check the len to avoid allocate huge memory */
+ if (!len || len > (drvdata->ram_size / 8)) {
dev_err(drvdata->dev, "DCC: Invalid length\n");
ret = -EINVAL;
goto err;
diff --git a/drivers/soc/qcom/icnss.c b/drivers/soc/qcom/icnss.c
index 8e0a61d..2e012a2 100644
--- a/drivers/soc/qcom/icnss.c
+++ b/drivers/soc/qcom/icnss.c
@@ -2571,6 +2571,8 @@
break;
case QMI_SERVER_EXIT:
+ set_bit(ICNSS_FW_DOWN, &penv->state);
+ icnss_ignore_qmi_timeout(true);
ret = icnss_driver_event_post(ICNSS_DRIVER_EVENT_SERVER_EXIT,
0, NULL);
break;
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/drivers/usb/dwc3/dwc3-msm.c b/drivers/usb/dwc3/dwc3-msm.c
index 94aa290..989956d 100644
--- a/drivers/usb/dwc3/dwc3-msm.c
+++ b/drivers/usb/dwc3/dwc3-msm.c
@@ -4100,7 +4100,7 @@
usb_speed = (event == 0 ? USB_SPEED_HIGH : USB_SPEED_SUPER);
if (dwc->maximum_speed == usb_speed)
- goto err;
+ return 0;
dbg_event(0xFF, "fw_restarthost", 0);
flush_delayed_work(&mdwc->sm_work);
diff --git a/drivers/usb/gadget/function/f_mass_storage.c b/drivers/usb/gadget/function/f_mass_storage.c
index b24ad72..675741b 100644
--- a/drivers/usb/gadget/function/f_mass_storage.c
+++ b/drivers/usb/gadget/function/f_mass_storage.c
@@ -2282,18 +2282,6 @@
}
}
- /* Disable the endpoints */
- if (fsg->bulk_in_enabled) {
- usb_ep_disable(fsg->bulk_in);
- fsg->bulk_in_enabled = 0;
- }
- if (fsg->bulk_out_enabled) {
- usb_ep_disable(fsg->bulk_out);
- fsg->bulk_out_enabled = 0;
- }
-
- /* allow usb LPM after eps are disabled */
- usb_gadget_autopm_put_async(common->gadget);
common->fsg = NULL;
wake_up(&common->fsg_wait);
}
@@ -2305,28 +2293,6 @@
common->fsg = new_fsg;
fsg = common->fsg;
- /* Enable the endpoints */
- rc = config_ep_by_speed(common->gadget, &(fsg->function), fsg->bulk_in);
- if (rc)
- goto reset;
- rc = usb_ep_enable(fsg->bulk_in);
- if (rc)
- goto reset;
- fsg->bulk_in->driver_data = common;
- fsg->bulk_in_enabled = 1;
-
- rc = config_ep_by_speed(common->gadget, &(fsg->function),
- fsg->bulk_out);
- if (rc)
- goto reset;
- rc = usb_ep_enable(fsg->bulk_out);
- if (rc)
- goto reset;
- fsg->bulk_out->driver_data = common;
- fsg->bulk_out_enabled = 1;
- common->bulk_out_maxpacket = usb_endpoint_maxp(fsg->bulk_out->desc);
- clear_bit(IGNORE_BULK_OUT, &fsg->atomic_bitflags);
-
/* Allocate the requests */
for (i = 0; i < common->fsg_num_buffers; ++i) {
struct fsg_buffhd *bh = &common->buffhds[i];
@@ -2357,20 +2323,71 @@
static int fsg_set_alt(struct usb_function *f, unsigned intf, unsigned alt)
{
struct fsg_dev *fsg = fsg_from_func(f);
- fsg->common->new_fsg = fsg;
+ int rc;
+ fsg->common->new_fsg = fsg;
/* prevents usb LPM until thread runs to completion */
usb_gadget_autopm_get_async(fsg->common->gadget);
+ /* Enable the endpoints */
+ rc = config_ep_by_speed(fsg->common->gadget, &(fsg->function),
+ fsg->bulk_in);
+ if (rc)
+ goto err_exit;
+ rc = usb_ep_enable(fsg->bulk_in);
+ if (rc)
+ goto err_exit;
+ fsg->bulk_in->driver_data = fsg->common;
+ fsg->bulk_in_enabled = 1;
+
+ rc = config_ep_by_speed(fsg->common->gadget, &(fsg->function),
+ fsg->bulk_out);
+ if (rc)
+ goto reset_bulk_int;
+
+ rc = usb_ep_enable(fsg->bulk_out);
+ if (rc)
+ goto reset_bulk_int;
+
+ fsg->bulk_out->driver_data = fsg->common;
+ fsg->bulk_out_enabled = 1;
+ fsg->common->bulk_out_maxpacket =
+ usb_endpoint_maxp(fsg->bulk_out->desc);
+ clear_bit(IGNORE_BULK_OUT, &fsg->atomic_bitflags);
+
+
raise_exception(fsg->common, FSG_STATE_CONFIG_CHANGE);
return USB_GADGET_DELAYED_STATUS;
+
+reset_bulk_int:
+ usb_ep_disable(fsg->bulk_in);
+ fsg->bulk_in->driver_data = NULL;
+ fsg->bulk_in_enabled = 0;
+err_exit:
+ return rc;
}
static void fsg_disable(struct usb_function *f)
{
struct fsg_dev *fsg = fsg_from_func(f);
+
+ /* Disable the endpoints */
+ if (fsg->bulk_in_enabled) {
+ usb_ep_disable(fsg->bulk_in);
+ fsg->bulk_in->driver_data = NULL;
+ fsg->bulk_in_enabled = 0;
+ }
+
+ if (fsg->bulk_out_enabled) {
+ usb_ep_disable(fsg->bulk_out);
+ fsg->bulk_out->driver_data = NULL;
+ fsg->bulk_out_enabled = 0;
+ }
+
fsg->common->new_fsg = NULL;
raise_exception(fsg->common, FSG_STATE_CONFIG_CHANGE);
+ /* allow usb LPM after eps are disabled */
+ usb_gadget_autopm_put_async(fsg->common->gadget);
}
diff --git a/drivers/usb/pd/policy_engine.c b/drivers/usb/pd/policy_engine.c
index 56b2a6d..f68f413 100644
--- a/drivers/usb/pd/policy_engine.c
+++ b/drivers/usb/pd/policy_engine.c
@@ -1818,6 +1818,7 @@
pd->num_svids = 0;
kfree(pd->vdm_tx);
pd->vdm_tx = NULL;
+ pd->ss_lane_svid = 0x0;
}
static void dr_swap(struct usbpd *pd)
diff --git a/include/linux/ipa_uc_offload.h b/include/linux/ipa_uc_offload.h
index 85d0ce9..94d8a8a 100644
--- a/include/linux/ipa_uc_offload.h
+++ b/include/linux/ipa_uc_offload.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
@@ -70,12 +70,28 @@
};
/**
+ * struct ntn_buff_smmu_map - IPA iova->pa SMMU mapping
+ * @iova: virtual address of the data buffer
+ * @pa: physical address of the data buffer
+ */
+struct ntn_buff_smmu_map {
+ dma_addr_t iova;
+ phys_addr_t pa;
+};
+
+/**
* struct ipa_ntn_setup_info - NTN TX/Rx configuration
* @client: type of "client" (IPA_CLIENT_ODU#_PROD/CONS)
+ * @smmu_enabled: SMMU is enabled for uC or not
* @ring_base_pa: physical address of the base of the Tx/Rx ring
+ * @ring_base_iova: virtual address of the base of the Tx/Rx ring
+ * @ring_base_sgt:Scatter table for ntn_rings,contains valid non NULL
+ * value when ENAC S1-SMMU enabed, else NULL.
* @ntn_ring_size: size of the Tx/Rx ring (in terms of elements)
- * @buff_pool_base_pa: physical address of the base of the Tx/Rx
- * buffer pool
+ * @buff_pool_base_pa: physical address of the base of the Tx/Rx buffer pool
+ * @buff_pool_base_iova: virtual address of the base of the Tx/Rx buffer pool
+ * @buff_pool_base_sgt: Scatter table for buffer pools,contains valid non NULL
+ * value when EMAC S1-SMMU enabed, else NULL.
* @num_buffers: Rx/Tx buffer pool size (in terms of elements)
* @data_buff_size: size of the each data buffer allocated in DDR
* @ntn_reg_base_ptr_pa: physical address of the Tx/Rx NTN Ring's
@@ -83,11 +99,21 @@
*/
struct ipa_ntn_setup_info {
enum ipa_client_type client;
+ bool smmu_enabled;
phys_addr_t ring_base_pa;
+ dma_addr_t ring_base_iova;
+ struct sg_table *ring_base_sgt;
+
u32 ntn_ring_size;
phys_addr_t buff_pool_base_pa;
+ dma_addr_t buff_pool_base_iova;
+ struct sg_table *buff_pool_base_sgt;
+
+ struct ntn_buff_smmu_map *data_buff_list;
+
u32 num_buffers;
+
u32 data_buff_size;
phys_addr_t ntn_reg_base_ptr_pa;
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/include/linux/sched/sysctl.h b/include/linux/sched/sysctl.h
index 0f57407..99948cb 100644
--- a/include/linux/sched/sysctl.h
+++ b/include/linux/sched/sysctl.h
@@ -42,6 +42,11 @@
#endif /* CONFIG_SCHED_WALT */
+#if defined(CONFIG_PREEMPT_TRACER) || defined(CONFIG_IRQSOFF_TRACER)
+extern unsigned int sysctl_preemptoff_tracing_threshold_ns;
+extern unsigned int sysctl_irqsoff_tracing_threshold_ns;
+#endif
+
enum sched_tunable_scaling {
SCHED_TUNABLESCALING_NONE,
SCHED_TUNABLESCALING_LOG,
diff --git a/include/trace/events/preemptirq.h b/include/trace/events/preemptirq.h
index f5024c5..3c51180 100644
--- a/include/trace/events/preemptirq.h
+++ b/include/trace/events/preemptirq.h
@@ -52,19 +52,50 @@
TP_ARGS(ip, parent_ip));
#endif
+TRACE_EVENT(irqs_disable,
+
+ TP_PROTO(u64 delta, unsigned long caddr0, unsigned long caddr1,
+ unsigned long caddr2, unsigned long caddr3),
+
+ TP_ARGS(delta, caddr0, caddr1, caddr2, caddr3),
+
+ TP_STRUCT__entry(
+ __field(u64, delta)
+ __field(void*, caddr0)
+ __field(void*, caddr1)
+ __field(void*, caddr2)
+ __field(void*, caddr3)
+ ),
+
+ TP_fast_assign(
+ __entry->delta = delta;
+ __entry->caddr0 = (void *)caddr0;
+ __entry->caddr1 = (void *)caddr1;
+ __entry->caddr2 = (void *)caddr2;
+ __entry->caddr3 = (void *)caddr3;
+ ),
+
+ TP_printk("delta=%llu(ns) Callers:(%pf<-%pf<-%pf<-%pf)", __entry->delta,
+ __entry->caddr0, __entry->caddr1,
+ __entry->caddr2, __entry->caddr3)
+);
+
#endif /* _TRACE_PREEMPTIRQ_H */
#include <trace/define_trace.h>
-#else /* !CONFIG_PREEMPTIRQ_EVENTS */
+#endif /* !CONFIG_PREEMPTIRQ_EVENTS */
+#if !defined(CONFIG_PREEMPTIRQ_EVENTS) || defined(CONFIG_PROVE_LOCKING)
#define trace_irq_enable(...)
#define trace_irq_disable(...)
-#define trace_preempt_enable(...)
-#define trace_preempt_disable(...)
#define trace_irq_enable_rcuidle(...)
#define trace_irq_disable_rcuidle(...)
+#endif
+
+#if !defined(CONFIG_PREEMPTIRQ_EVENTS) || !defined(CONFIG_DEBUG_PREEMPT)
+#define trace_preempt_enable(...)
+#define trace_preempt_disable(...)
#define trace_preempt_enable_rcuidle(...)
#define trace_preempt_disable_rcuidle(...)
-
#endif
diff --git a/include/trace/events/sched.h b/include/trace/events/sched.h
index 4ad7cbd..e06df4d 100644
--- a/include/trace/events/sched.h
+++ b/include/trace/events/sched.h
@@ -1875,6 +1875,39 @@
__entry->requested_cpu, __entry->isolated_cpus,
__entry->time, __entry->isolate)
);
+
+TRACE_EVENT(sched_preempt_disable,
+
+ TP_PROTO(u64 delta, bool irqs_disabled,
+ unsigned long caddr0, unsigned long caddr1,
+ unsigned long caddr2, unsigned long caddr3),
+
+ TP_ARGS(delta, irqs_disabled, caddr0, caddr1, caddr2, caddr3),
+
+ TP_STRUCT__entry(
+ __field(u64, delta)
+ __field(bool, irqs_disabled)
+ __field(void*, caddr0)
+ __field(void*, caddr1)
+ __field(void*, caddr2)
+ __field(void*, caddr3)
+ ),
+
+ TP_fast_assign(
+ __entry->delta = delta;
+ __entry->irqs_disabled = irqs_disabled;
+ __entry->caddr0 = (void *)caddr0;
+ __entry->caddr1 = (void *)caddr1;
+ __entry->caddr2 = (void *)caddr2;
+ __entry->caddr3 = (void *)caddr3;
+ ),
+
+ TP_printk("delta=%llu(ns) irqs_d=%d Callers:(%pf<-%pf<-%pf<-%pf)",
+ __entry->delta, __entry->irqs_disabled,
+ __entry->caddr0, __entry->caddr1,
+ __entry->caddr2, __entry->caddr3)
+);
+
#endif /* _TRACE_SCHED_H */
/* This part must be outside protection */
diff --git a/kernel/sched/core.c b/kernel/sched/core.c
index 23a7f9c..ef34eb5 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);
@@ -3322,16 +3323,38 @@
#if defined(CONFIG_PREEMPT) && (defined(CONFIG_DEBUG_PREEMPT) || \
defined(CONFIG_PREEMPT_TRACER))
/*
+ * preemptoff stack tracing threshold in ns.
+ * default: 1ms
+ */
+unsigned int sysctl_preemptoff_tracing_threshold_ns = 1000000UL;
+
+struct preempt_store {
+ u64 ts;
+ unsigned long caddr[4];
+ bool irqs_disabled;
+};
+
+static DEFINE_PER_CPU(struct preempt_store, the_ps);
+/*
* If the value passed in is equal to the current preempt count
* then we just disabled preemption. Start timing the latency.
*/
static inline void preempt_latency_start(int val)
{
+ struct preempt_store *ps = &per_cpu(the_ps, raw_smp_processor_id());
+
if (preempt_count() == val) {
unsigned long ip = get_lock_parent_ip();
#ifdef CONFIG_DEBUG_PREEMPT
current->preempt_disable_ip = ip;
#endif
+ ps->ts = sched_clock();
+ ps->caddr[0] = CALLER_ADDR0;
+ ps->caddr[1] = CALLER_ADDR1;
+ ps->caddr[2] = CALLER_ADDR2;
+ ps->caddr[3] = CALLER_ADDR3;
+ ps->irqs_disabled = irqs_disabled();
+
trace_preempt_off(CALLER_ADDR0, ip);
}
}
@@ -3364,8 +3387,21 @@
*/
static inline void preempt_latency_stop(int val)
{
- if (preempt_count() == val)
+ if (preempt_count() == val) {
+ struct preempt_store *ps = &per_cpu(the_ps,
+ raw_smp_processor_id());
+ u64 delta = sched_clock() - ps->ts;
+
+ /*
+ * Trace preempt disable stack if preemption
+ * is disabled for more than the threshold.
+ */
+ if (delta > sysctl_preemptoff_tracing_threshold_ns)
+ trace_sched_preempt_disable(delta, ps->irqs_disabled,
+ ps->caddr[0], ps->caddr[1],
+ ps->caddr[2], ps->caddr[3]);
trace_preempt_on(CALLER_ADDR0, get_lock_parent_ip());
+ }
}
void preempt_count_sub(int val)
@@ -4293,7 +4329,8 @@
struct rq *rq;
/* may grab non-irq protected spin_locks */
- BUG_ON(in_interrupt());
+ if (pi)
+ BUG_ON(in_interrupt());
recheck:
/* double check policy once rq lock held */
if (policy < 0) {
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));
diff --git a/kernel/sysctl.c b/kernel/sysctl.c
index 6340010..f41c0e9 100644
--- a/kernel/sysctl.c
+++ b/kernel/sysctl.c
@@ -291,6 +291,22 @@
.mode = 0644,
.proc_handler = proc_dointvec,
},
+#if defined(CONFIG_PREEMPT_TRACER) || defined(CONFIG_IRQSOFF_TRACER)
+ {
+ .procname = "preemptoff_tracing_threshold_ns",
+ .data = &sysctl_preemptoff_tracing_threshold_ns,
+ .maxlen = sizeof(unsigned int),
+ .mode = 0644,
+ .proc_handler = proc_dointvec,
+ },
+ {
+ .procname = "irqsoff_tracing_threshold_ns",
+ .data = &sysctl_irqsoff_tracing_threshold_ns,
+ .maxlen = sizeof(unsigned int),
+ .mode = 0644,
+ .proc_handler = proc_dointvec,
+ },
+#endif
#ifdef CONFIG_SCHED_WALT
{
.procname = "sched_cpu_high_irqload",
diff --git a/kernel/trace/trace_irqsoff.c b/kernel/trace/trace_irqsoff.c
index c180fe5..79bbaf0 100644
--- a/kernel/trace/trace_irqsoff.c
+++ b/kernel/trace/trace_irqsoff.c
@@ -13,6 +13,7 @@
#include <linux/uaccess.h>
#include <linux/module.h>
#include <linux/ftrace.h>
+#include <linux/sched/sysctl.h>
#include "trace.h"
@@ -39,6 +40,12 @@
static void stop_irqsoff_tracer(struct trace_array *tr, int graph);
static int start_irqsoff_tracer(struct trace_array *tr, int graph);
+/*
+ * irqsoff stack tracing threshold in ns.
+ * default: 1ms
+ */
+unsigned int sysctl_irqsoff_tracing_threshold_ns = 1000000UL;
+
#ifdef CONFIG_PREEMPT_TRACER
static inline int
preempt_trace(void)
@@ -454,17 +461,53 @@
#else /* !CONFIG_PROVE_LOCKING */
+#ifdef CONFIG_PREEMPTIRQ_EVENTS
+struct irqsoff_store {
+ u64 ts;
+ unsigned long caddr[4];
+};
+
+static DEFINE_PER_CPU(struct irqsoff_store, the_irqsoff);
+#endif /* CONFIG_PREEMPTIRQ_EVENTS */
+
/*
* We are only interested in hardirq on/off events:
*/
static inline void tracer_hardirqs_on(void)
{
+#ifdef CONFIG_PREEMPTIRQ_EVENTS
+ struct irqsoff_store *is = &per_cpu(the_irqsoff,
+ raw_smp_processor_id());
+
+ if (!is->ts) {
+ is->ts = sched_clock();
+ is->caddr[0] = CALLER_ADDR0;
+ is->caddr[1] = CALLER_ADDR1;
+ is->caddr[2] = CALLER_ADDR2;
+ is->caddr[3] = CALLER_ADDR3;
+ }
+#endif /* CONFIG_PREEMPTIRQ_EVENTS */
+
if (!preempt_trace() && irq_trace())
stop_critical_timing(CALLER_ADDR0, CALLER_ADDR1);
}
static inline void tracer_hardirqs_off(void)
{
+#ifdef CONFIG_PREEMPTIRQ_EVENTS
+ struct irqsoff_store *is = &per_cpu(the_irqsoff,
+ raw_smp_processor_id());
+ u64 delta = 0;
+
+ if (is->ts) {
+ delta = sched_clock() - is->ts;
+ is->ts = 0;
+ }
+ if (delta > sysctl_irqsoff_tracing_threshold_ns)
+ trace_irqs_disable(delta, is->caddr[0], is->caddr[1],
+ is->caddr[2], is->caddr[3]);
+#endif /* CONFIG_PREEMPTIRQ_EVENTS */
+
if (!preempt_trace() && irq_trace())
start_critical_timing(CALLER_ADDR0, CALLER_ADDR1);
}