Merge "power: qpnp-qg: Add a workaround to force recharge"
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/crypto/msm/qcedev.txt b/Documentation/devicetree/bindings/crypto/msm/qcedev.txt
index 6f1d8e3..addfd46 100644
--- a/Documentation/devicetree/bindings/crypto/msm/qcedev.txt
+++ b/Documentation/devicetree/bindings/crypto/msm/qcedev.txt
@@ -1,5 +1,8 @@
* QCEDEV (QTI Crypto Engine Device)
+[Root level node]
+Crypto Engine
+============
Required properties:
- compatible : should be "qcom,qcedev"
- reg : should contain crypto, BAM register map.
@@ -23,6 +26,19 @@
- qcom,smmu-s1-enable : Boolean flag to enable SMMU stage 1 translation.
- iommus : A list of phandle and IOMMU specifier pairs that describe the IOMMU master interfaces of the device.
+
+[Second level nodes]
+Context banks
+=============
+Required properties:
+ - compatible : should be "qcom,qcedev,context-bank"
+ - iommus : A phandle parsed by smmu driver. Number of entries will vary across targets.
+
+Optional properties:
+ - label - string describing iommu domain usage.
+ - virtual-addr : start of virtual address pool.
+ - virtual-size : size of virtual address pool.
+
Example:
qcom,qcedev@fd440000 {
@@ -42,4 +58,15 @@
<56 512 0 0>,
<56 512 3936000 393600>,
qcom,ce-opp-freq = <100000000>;
+
+ qcom_cedev_ns_cb {
+ compatible = "qcom,qcedev,context-bank";
+ label = "ns_context";
+ iommus = <&anoc2_smmu 0x1878>,
+ <&anoc2_smmu 0x1879>,
+ <&anoc2_smmu 0x187c>,
+ <&anoc2_smmu 0x187f>;
+ virtual-addr = <0x60000000>;
+ virtual-size = <0x00200000>;
+ };
};
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-fg-gen3.txt b/Documentation/devicetree/bindings/power/supply/qcom/qpnp-fg-gen3.txt
index 392ee7b..3d054f3 100644
--- a/Documentation/devicetree/bindings/power/supply/qcom/qpnp-fg-gen3.txt
+++ b/Documentation/devicetree/bindings/power/supply/qcom/qpnp-fg-gen3.txt
@@ -36,6 +36,8 @@
Definition: For details about IIO bindings see:
Documentation/devicetree/bindings/iio/iio-bindings.txt
+- #thermal-sensor-cells: Should be 0. See thermal.txt for a description.
+
- qcom,rradc-base
Usage: required
Value type: <u32>
@@ -489,6 +491,7 @@
qcom,slope-limit-temp-threshold = <100>;
qcom,slope-limit-coeffs = <10 11 12 13>;
qcom,battery-thermal-coefficients = [9d 50 ff];
+ #thermal-sensor-cells = <0>;
status = "okay";
qcom,fg-batt-soc@4000 {
@@ -517,3 +520,32 @@
reg = <0x4400 0x100>;
};
};
+
+======================================
+Example for thermal zone configuration
+======================================
+
+thermal_zones {
+ pmi8998_fg {
+ polling-delay-passive = <200>;
+ polling-delay = <200>;
+ thermal-governor = <userspace>;
+ thermal-sensors = <&pmi8998_fg>;
+
+ pmi8998_fg_trip1: pmi8998-fg-trip0 {
+ temperature = <45000>;
+ hysteresis = <0>;
+ type = "passive";
+ };
+ pmi8998_fg_trip2: pmi8998-fg-trip2 {
+ temperature = <50000>;
+ hysteresis = <0>;
+ type = "hot";
+ };
+ pmi8998_fg_trip3: pmi8998-fg-trip3 {
+ temperature = <60000>;
+ hysteresis = <0>;
+ type = "alert";
+ };
+ };
+};
diff --git a/Documentation/devicetree/bindings/pwm/pwm-qti-lpg.txt b/Documentation/devicetree/bindings/pwm/pwm-qti-lpg.txt
index 3174ccb..ddd90e1 100644
--- a/Documentation/devicetree/bindings/pwm/pwm-qti-lpg.txt
+++ b/Documentation/devicetree/bindings/pwm/pwm-qti-lpg.txt
@@ -11,26 +11,137 @@
- reg:
Usage: required
Value type: <prop-encoded-array>
- Definition: Register base and length for LPG modules. The length
- varies based on the number of channels available in
- the PMIC chips.
+ Definition: Register base and length for LPG and LUT modules. LPG size
+ or length available per channel varies depending on the
+ number of channels in PMIC.
- reg-names:
Usage: required
Value type: <string>
Definition: The name of the register defined in the reg property.
- It must be "lpg-base".
+ It must have "lpg-base", "lut-base" is optional but
+ it's required if any LPG channels support LUT mode.
- #pwm-cells:
Usage: required
Value type: <u32>
- Definition: See Documentation/devicetree/bindings/pwm/pwm.txt;
+ Definition: The number of cells in "pwms" property specified in
+ PWM user nodes. It should be 2. The first cell is
+ the PWM channel ID indexed from 0, and the second
+ cell is the PWM default period in nanoseconds.
+- qcom,lut-patterns:
+ Usage: optional
+ Value type: <prop-encoded-array>
+ Definition: Duty ratios in percentages for LPG working at LUT mode.
+ These duty ratios will be translated into PWM values
+ and stored in LUT module. The LUT module has resource
+ to store 47 PWM values at max and shared for all LPG
+ channels. This property is required if any LPG channels
+ support LUT mode.
+
+Subnode is optional if LUT mode is not required, it's required if any LPG
+channels expected to be supported in LUT mode.
+
+Subnode properties:
+Subnodes for each LPG channel (lpg@X) can be defined if any of the following
+parameters needs to be configured for that channel.
+
+- qcom,lpg-chan-id:
+ Usage: required
+ Value type: <u32>
+ Definition: The LPG channel's hardware ID indexed from 1. Allowed
+ range is 1 - 8. Maximum value depends on the number of
+ channels supported on PMIC.
+
+- qcom,ramp-step-ms:
+ Usage: required
+ Value type: <u32>
+ Definition: The step duration in milliseconds for LPG staying at each
+ duty specified in the LUT pattern. Allowed range is
+ 1 - 511.
+
+- qcom,ramp-high-index:
+ Usage: required
+ Value type: <u32>
+ Definition: The high index of the LUT pattern where LPG ends up
+ ramping to. Allowed range is 1 - 47.
+
+- qcom,ramp-low-index:
+ Usage: required
+ Value type: <u32>
+ Definition: The low index of the LUT pattern from where LPG begins
+ ramping from. Allowed range is 0 - 46.
+
+- qcom,ramp-from-low-to-high:
+ Usage: optional
+ Value type: <empty>
+ Definition: The flag to specify the LPG ramping direction. The ramping
+ direction is from low index to high index of the LUT
+ pattern if it's specified.
+
+- qcom,ramp-pattern-repeat:
+ Usage: optional
+ Value type: <empty>
+ Definition: The flag to specify if LPG would be ramping with the LUT
+ pattern repeatedly.
+
+- qcom,ramp-toggle:
+ Usage: optional
+ Value type: <empty>
+ Definition: The flag to specify if LPG would toggle the LUT pattern
+ in ramping. If toggling enabled, LPG would return to the
+ low index when high index is reached, or return to the high
+ index when low index is reached.
+
+- qcom,ramp-pause-hi-count:
+ Usage: optional
+ Value type: <u32>
+ Definition: The step count that LPG stop the output when it ramped up
+ to the high index of the LUT.
+
+- qcom,ramp-pause-lo-count:
+ Usage: optional
+ Value type: <u32>
+ Definition: The step count that LPG stop the output when it ramped up
+ to the low index of the LUT.
Example:
pmi8998_lpg: lpg@b100 {
compatible = "qcom,pwm-lpg";
- reg = <0xb100 0x600>;
- reg-names = "lpg-base";
+ reg = <0xb100 0x600>, <0xb000 0x100>;
+ reg-names = "lpg-base", "lut-base";
#pwm-cells = <2>;
+ qcom,lut-patterns = <0 14 28 42 56 70 84 100
+ 100 84 70 56 42 28 14 0>;
+ lpg@3 {
+ qcom,lpg-chan-id = <3>;
+ qcom,ramp-step-ms = <200>;
+ qcom,ramp-pause-hi-count = <10>;
+ qcom,ramp-pause-lo-count = <10>;
+ qcom,ramp-low-index = <0>;
+ qcom,ramp-high-index = <15>;
+ qcom,ramp-from-low-to-high;
+ qcom,ramp-pattern-repeat;
+ };
+ lpg@4 {
+ qcom,lpg-chan-id = <4>;
+ qcom,ramp-step-ms = <200>;
+ qcom,ramp-pause-hi-count = <10>;
+ qcom,ramp-pause-lo-count = <10>;
+ qcom,ramp-low-index = <0>;
+ qcom,ramp-high-index = <15>;
+ qcom,ramp-from-low-to-high;
+ qcom,ramp-pattern-repeat;
+ };
+ lpg@5 {
+ qcom,lpg-chan-id = <5>;
+ qcom,ramp-step-ms = <200>;
+ qcom,ramp-pause-hi-count = <10>;
+ qcom,ramp-pause-lo-count = <10>;
+ qcom,ramp-low-index = <0>;
+ qcom,ramp-high-index = <15>;
+ qcom,ramp-from-low-to-high;
+ qcom,ramp-pattern-repeat;
+ };
};
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..75372aa 100644
--- a/arch/arm/configs/msm8909w-perf_defconfig
+++ b/arch/arm/configs/msm8909w-perf_defconfig
@@ -33,6 +33,7 @@
CONFIG_EMBEDDED=y
CONFIG_PROFILING=y
CONFIG_OPROFILE=y
+CONFIG_CC_STACKPROTECTOR_STRONG=y
CONFIG_MODULES=y
CONFIG_MODULE_UNLOAD=y
CONFIG_MODULE_FORCE_UNLOAD=y
diff --git a/arch/arm/configs/msm8909w_defconfig b/arch/arm/configs/msm8909w_defconfig
index 46052bd..086b127 100644
--- a/arch/arm/configs/msm8909w_defconfig
+++ b/arch/arm/configs/msm8909w_defconfig
@@ -33,6 +33,7 @@
CONFIG_EMBEDDED=y
CONFIG_PROFILING=y
CONFIG_OPROFILE=y
+CONFIG_CC_STACKPROTECTOR_STRONG=y
CONFIG_MODULES=y
CONFIG_MODULE_UNLOAD=y
CONFIG_MODULE_FORCE_UNLOAD=y
diff --git a/arch/arm/configs/msm8953-perf_defconfig b/arch/arm/configs/msm8953-perf_defconfig
index 5779552..e63cc3355 100644
--- a/arch/arm/configs/msm8953-perf_defconfig
+++ b/arch/arm/configs/msm8953-perf_defconfig
@@ -578,6 +578,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/msm8953_defconfig b/arch/arm/configs/msm8953_defconfig
index 6ac9d99..653e5a4 100644
--- a/arch/arm/configs/msm8953_defconfig
+++ b/arch/arm/configs/msm8953_defconfig
@@ -594,6 +594,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 c9996f8..3947406 100644
--- a/arch/arm64/boot/dts/qcom/8909w-pm660.dtsi
+++ b/arch/arm64/boot/dts/qcom/8909w-pm660.dtsi
@@ -450,7 +450,7 @@
pins = "gpio12";
function = "normal";
output-enable;
- qcom,drive-strength = "medium";
+ qcom,drive-strength = <2>;
};
};
@@ -459,7 +459,7 @@
pins = "gpio5";
function = "func1";
output-enable;
- qcom,drive-strength = "medium";
+ qcom,drive-strength = <2>;
bias-disable;
};
};
@@ -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/msm8909-ion.dtsi b/arch/arm64/boot/dts/qcom/msm8909-ion.dtsi
index 74f2be0..858429b 100644
--- a/arch/arm64/boot/dts/qcom/msm8909-ion.dtsi
+++ b/arch/arm64/boot/dts/qcom/msm8909-ion.dtsi
@@ -39,6 +39,12 @@
qcom,ion-heap-type = "DMA";
};
+ qcom,ion-heap@19 { /* QSEECOM TA HEAP */
+ reg = <19>;
+ memory-region = <&qseecom_ta_mem>;
+ qcom,ion-heap-type = "DMA";
+ };
+
adsp_heap: qcom,ion-heap@22 { /* MODEM HEAP */
reg = <22>;
memory-region = <&adsp_mem>;
diff --git a/arch/arm64/boot/dts/qcom/msm8909.dtsi b/arch/arm64/boot/dts/qcom/msm8909.dtsi
index ec16e60e..eed3623 100644
--- a/arch/arm64/boot/dts/qcom/msm8909.dtsi
+++ b/arch/arm64/boot/dts/qcom/msm8909.dtsi
@@ -194,6 +194,14 @@
size = <0 0x0800000>;
};
+ qseecom_ta_mem: qseecom_ta_region {
+ compatible = "shared-dma-pool";
+ alloc-ranges = <0 0x00000000 0 0xffffffff>;
+ reusable;
+ alignment = <0 0x400000>;
+ size = <0 0x400000>;
+ };
+
audio_mem: audio_region@0 {
compatible = "shared-dma-pool";
reusable;
diff --git a/arch/arm64/boot/dts/qcom/msm8909w-bg-wtp-v2.dts b/arch/arm64/boot/dts/qcom/msm8909w-bg-wtp-v2.dts
index 8f2eac0..d9fa6fb 100644
--- a/arch/arm64/boot/dts/qcom/msm8909w-bg-wtp-v2.dts
+++ b/arch/arm64/boot/dts/qcom/msm8909w-bg-wtp-v2.dts
@@ -69,6 +69,17 @@
/delete-node/ it7260@46;
};
+ qcom,blackghost {
+ compatible = "qcom,pil-blackghost";
+ qcom,firmware-name = "bg-wear";
+ /* GPIO inputs from blackghost */
+ qcom,bg2ap-status-gpio = <&msm_gpio 97 0>;
+ qcom,bg2ap-errfatal-gpio = <&msm_gpio 95 0>;
+ /* GPIO output to blackghost */
+ qcom,ap2bg-status-gpio = <&msm_gpio 17 0>;
+ qcom,ap2bg-errfatal-gpio = <&msm_gpio 23 0>;
+ };
+
qcom,msm-ssc-sensors {
compatible = "qcom,msm-ssc-sensors";
};
@@ -155,6 +166,8 @@
qcom,bg-daemon {
compatible = "qcom,bg-daemon";
qcom,bg-reset-gpio = <&pm660_gpios 5 0>;
+ ssr-reg1-supply = <&pm660_l3>;
+ ssr-reg2-supply = <&pm660_l9>;
};
qcom,bcl {
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/msm8953-cdp.dtsi b/arch/arm64/boot/dts/qcom/msm8953-cdp.dtsi
index 8782325..9b78253 100644
--- a/arch/arm64/boot/dts/qcom/msm8953-cdp.dtsi
+++ b/arch/arm64/boot/dts/qcom/msm8953-cdp.dtsi
@@ -42,6 +42,7 @@
qcom,nq-ven = <&tlmm 16 0x00>;
qcom,nq-firm = <&tlmm 62 0x00>;
qcom,nq-clkreq = <&pm8953_gpios 2 0x00>;
+ qcom,nq-esepwr = <&tlmm 141 0x00>;
interrupt-parent = <&tlmm>;
qcom,clk-src = "BBCLK2";
interrupts = <17 0>;
diff --git a/arch/arm64/boot/dts/qcom/msm8953-ext-codec-mtp-overlay.dts b/arch/arm64/boot/dts/qcom/msm8953-ext-codec-mtp-overlay.dts
index 08a343e..b182a25 100644
--- a/arch/arm64/boot/dts/qcom/msm8953-ext-codec-mtp-overlay.dts
+++ b/arch/arm64/boot/dts/qcom/msm8953-ext-codec-mtp-overlay.dts
@@ -17,7 +17,81 @@
#include "msm8953-mtp.dtsi"
/ {
- model = "Ext Codec MTP";
+ model = "Qualcomm Technologies, Inc. MSM8953 + PMI8950 Ext Codec MTP";
+ compatible = "qcom,msm8953-mtp", "qcom,msm8953", "qcom,mtp";
qcom,board-id= <8 1>;
+ qcom,pmic-id = <0x010016 0x010011 0x0 0x0>;
};
+&int_codec {
+ status = "disabled";
+};
+
+&pmic_analog_codec {
+ status = "disabled";
+};
+
+&wsa881x_i2c_f {
+ status = "disabled";
+};
+
+&wsa881x_i2c_45 {
+ status = "disabled";
+};
+
+&cdc_pri_mi2s_gpios {
+ status = "disabled";
+};
+
+&wsa881x_analog_vi_gpio {
+ status = "disabled";
+};
+
+&wsa881x_analog_clk_gpio {
+ status = "disabled";
+};
+
+&wsa881x_analog_reset_gpio {
+ status = "disabled";
+};
+
+&cdc_comp_gpios {
+ status = "disabled";
+};
+
+&slim_msm {
+ status = "okay";
+};
+
+&dai_slim {
+ status = "okay";
+};
+
+&wcd9xxx_intc {
+ status = "okay";
+};
+
+&clock_audio {
+ status = "okay";
+};
+
+&wcd9335 {
+ status = "okay";
+};
+
+&cdc_us_euro_sw {
+ status = "okay";
+};
+
+&cdc_quin_mi2s_gpios {
+ status = "okay";
+};
+
+&wcd_rst_gpio {
+ status = "okay";
+};
+
+&ext_codec {
+ qcom,model = "msm8953-tasha-snd-card";
+ status = "okay";
+};
diff --git a/arch/arm64/boot/dts/qcom/msm8953-ext-codec-mtp.dts b/arch/arm64/boot/dts/qcom/msm8953-ext-codec-mtp.dts
index e3a5b4a..b80583e 100644
--- a/arch/arm64/boot/dts/qcom/msm8953-ext-codec-mtp.dts
+++ b/arch/arm64/boot/dts/qcom/msm8953-ext-codec-mtp.dts
@@ -25,75 +25,3 @@
qcom,pmic-id = <0x010016 0x010011 0x0 0x0>;
};
-&int_codec {
- status = "disabled";
-};
-
-&pmic_analog_codec {
- status = "disabled";
-};
-
-&wsa881x_i2c_f {
- status = "disabled";
-};
-
-&wsa881x_i2c_45 {
- status = "disabled";
-};
-
-&cdc_pri_mi2s_gpios {
- status = "disabled";
-};
-
-&wsa881x_analog_vi_gpio {
- status = "disabled";
-};
-
-&wsa881x_analog_clk_gpio {
- status = "disabled";
-};
-
-&wsa881x_analog_reset_gpio {
- status = "disabled";
-};
-
-&cdc_comp_gpios {
- status = "disabled";
-};
-
-&slim_msm {
- status = "okay";
-};
-
-&dai_slim {
- status = "okay";
-};
-
-&wcd9xxx_intc {
- status = "okay";
-};
-
-&clock_audio {
- status = "okay";
-};
-
-&wcd9335 {
- status = "okay";
-};
-
-&cdc_us_euro_sw {
- status = "okay";
-};
-
-&cdc_quin_mi2s_gpios {
- status = "okay";
-};
-
-&wcd_rst_gpio {
- status = "okay";
-};
-
-&ext_codec {
- qcom,model = "msm8953-tasha-snd-card";
- status = "okay";
-};
diff --git a/arch/arm64/boot/dts/qcom/msm8953-ipc.dtsi b/arch/arm64/boot/dts/qcom/msm8953-ipc.dtsi
index b62d12d..0e635d4 100644
--- a/arch/arm64/boot/dts/qcom/msm8953-ipc.dtsi
+++ b/arch/arm64/boot/dts/qcom/msm8953-ipc.dtsi
@@ -18,3 +18,61 @@
pinctrl-names = "default";
pinctrl-0 = <&uart_console_active>;
};
+
+&sdhc_1 {
+ /* device core power supply */
+ vdd-supply = <&pm8953_l8>;
+ qcom,vdd-voltage-level = <2900000 2900000>;
+ qcom,vdd-current-level = <200 570000>;
+
+ /* device communication power supply */
+ vdd-io-supply = <&pm8953_l5>;
+ qcom,vdd-io-always-on;
+ qcom,vdd-io-lpm-sup;
+ qcom,vdd-io-voltage-level = <1800000 1800000>;
+ qcom,vdd-io-current-level = <200 325000>;
+
+ pinctrl-names = "active", "sleep";
+ pinctrl-0 = <&sdc1_clk_on &sdc1_cmd_on &sdc1_data_on &sdc1_rclk_on>;
+ pinctrl-1 = <&sdc1_clk_off &sdc1_cmd_off &sdc1_data_off &sdc1_rclk_off>;
+
+ qcom,clk-rates = <400000 20000000 25000000 50000000 100000000 192000000
+ 384000000>;
+ qcom,nonremovable;
+ qcom,bus-speed-mode = "HS400_1p8v", "HS200_1p8v", "DDR_1p8v";
+
+ status = "ok";
+};
+
+&sdhc_2 {
+ /* device core power supply */
+ vdd-supply = <&pm8953_l11>;
+ qcom,vdd-voltage-level = <2950000 2950000>;
+ qcom,vdd-current-level = <15000 800000>;
+
+ /* device communication power supply */
+ vdd-io-supply = <&pm8953_l12>;
+ qcom,vdd-io-voltage-level = <1800000 2950000>;
+ qcom,vdd-io-current-level = <200 22000>;
+
+ pinctrl-names = "active", "sleep";
+ pinctrl-0 = <&sdc2_clk_on &sdc2_cmd_on &sdc2_data_on &sdc2_cd_on>;
+ pinctrl-1 = <&sdc2_clk_off &sdc2_cmd_off &sdc2_data_off>;
+
+ #address-cells = <0>;
+ interrupt-parent = <&sdhc_2>;
+ interrupts = <0 1 2>;
+ #interrupt-cells = <1>;
+ interrupt-map-mask = <0xffffffff>;
+ interrupt-map = <0 &intc 0 125 0
+ 1 &intc 0 221 0
+ 2 &tlmm 133 0>;
+ interrupt-names = "hc_irq", "pwr_irq", "status_irq";
+ cd-gpios = <&tlmm 133 0x1>;
+
+ qcom,clk-rates = <400000 20000000 25000000 50000000 100000000
+ 200000000>;
+ qcom,bus-speed-mode = "SDR12", "SDR25", "SDR50", "DDR50", "SDR104";
+
+ status = "ok";
+};
diff --git a/arch/arm64/boot/dts/qcom/msm8953-mtp.dtsi b/arch/arm64/boot/dts/qcom/msm8953-mtp.dtsi
index b861573..cc4bc7f 100644
--- a/arch/arm64/boot/dts/qcom/msm8953-mtp.dtsi
+++ b/arch/arm64/boot/dts/qcom/msm8953-mtp.dtsi
@@ -41,6 +41,7 @@
qcom,nq-ven = <&tlmm 16 0x00>;
qcom,nq-firm = <&tlmm 62 0x00>;
qcom,nq-clkreq = <&pm8953_gpios 2 0x00>;
+ qcom,nq-esepwr = <&tlmm 141 0x00>;
interrupt-parent = <&tlmm>;
qcom,clk-src = "BBCLK2";
interrupts = <17 0>;
diff --git a/arch/arm64/boot/dts/qcom/msm8953-pinctrl.dtsi b/arch/arm64/boot/dts/qcom/msm8953-pinctrl.dtsi
index c8fcfd5..7d73a69 100644
--- a/arch/arm64/boot/dts/qcom/msm8953-pinctrl.dtsi
+++ b/arch/arm64/boot/dts/qcom/msm8953-pinctrl.dtsi
@@ -746,39 +746,67 @@
};
};
- pmx_rd_nfc_int {
- /*qcom,pins = <&gp 17>;*/
- pins = "gpio17";
- qcom,pin-func = <0>;
- qcom,num-grp-pins = <1>;
- label = "pmx_nfc_int";
+ nfc {
+ nfc_int_active: nfc_int_active {
+ /* active state */
+ mux {
+ /* GPIO 17 NFC Read Interrupt */
+ pins = "gpio17";
+ function = "gpio";
+ };
- nfc_int_active: active {
- drive-strength = <6>;
- bias-pull-up;
+ config {
+ pins = "gpio17";
+ drive-strength = <2>; /* 2 MA */
+ bias-pull-up;
+ };
};
- nfc_int_suspend: suspend {
- drive-strength = <6>;
- bias-pull-up;
- };
- };
+ nfc_int_suspend: nfc_int_suspend {
+ /* sleep state */
+ mux {
+ /* GPIO 17 NFC Read Interrupt */
+ pins = "gpio17";
+ function = "gpio";
+ };
- pmx_nfc_reset {
- /*qcom,pins = <&gp 16>;*/
- pins = "gpio16";
- qcom,pin-func = <0>;
- qcom,num-grp-pins = <1>;
- label = "pmx_nfc_disable";
-
- nfc_disable_active: active {
- drive-strength = <6>;
- bias-pull-up;
+ config {
+ pins = "gpio17";
+ drive-strength = <2>; /* 2 MA */
+ bias-pull-up;
+ };
};
- nfc_disable_suspend: suspend {
- drive-strength = <6>;
- bias-disable;
+ nfc_disable_active: nfc_disable_active {
+ /* active state */
+ mux {
+ /* 16: NFC ENABLE 62: FW DNLD */
+ /* 141: ESE Enable */
+ pins = "gpio16", "gpio62", "gpio141";
+ function = "gpio";
+ };
+
+ config {
+ pins = "gpio16", "gpio62", "gpio141";
+ drive-strength = <2>; /* 2 MA */
+ bias-pull-up;
+ };
+ };
+
+ nfc_disable_suspend: nfc_disable_suspend {
+ /* sleep state */
+ mux {
+ /* 16: NFC ENABLE 62: FW DNLD */
+ /* 141: ESE Enable */
+ pins = "gpio16", "gpio62", "gpio141";
+ function = "gpio";
+ };
+
+ config {
+ pins = "gpio16", "gpio62", "gpio141";
+ drive-strength = <2>; /* 2 MA */
+ bias-disable;
+ };
};
};
diff --git a/arch/arm64/boot/dts/qcom/msm8953-qrd.dtsi b/arch/arm64/boot/dts/qcom/msm8953-qrd.dtsi
index 253e87e..1f0ad88 100644
--- a/arch/arm64/boot/dts/qcom/msm8953-qrd.dtsi
+++ b/arch/arm64/boot/dts/qcom/msm8953-qrd.dtsi
@@ -69,6 +69,7 @@
qcom,nq-ven = <&tlmm 16 0x00>;
qcom,nq-firm = <&tlmm 62 0x00>;
qcom,nq-clkreq = <&pm8953_gpios 2 0x00>;
+ qcom,nq-esepwr = <&tlmm 141 0x00>;
interrupt-parent = <&tlmm>;
qcom,clk-src = "BBCLK2";
interrupts = <17 0>;
diff --git a/arch/arm64/boot/dts/qcom/msm8953.dtsi b/arch/arm64/boot/dts/qcom/msm8953.dtsi
index 50ee0e8..b33a7db 100644
--- a/arch/arm64/boot/dts/qcom/msm8953.dtsi
+++ b/arch/arm64/boot/dts/qcom/msm8953.dtsi
@@ -349,11 +349,6 @@
compatible = "qcom,mem-dump";
memory-region = <&dump_mem>;
- rpmh_dump {
- qcom,dump-size = <0x2000000>;
- qcom,dump-id = <0xec>;
- };
-
fcm_dump {
qcom,dump-size = <0x8400>;
qcom,dump-id = <0xee>;
diff --git a/arch/arm64/boot/dts/qcom/pm660.dtsi b/arch/arm64/boot/dts/qcom/pm660.dtsi
index fa10500..e8a82aa 100644
--- a/arch/arm64/boot/dts/qcom/pm660.dtsi
+++ b/arch/arm64/boot/dts/qcom/pm660.dtsi
@@ -32,7 +32,7 @@
reg = <0x900 0x100>;
};
- qcom,power-on@800 {
+ pm660_pon: qcom,power-on@800 {
compatible = "qcom,qpnp-power-on";
reg = <0x800 0x100>;
interrupts = <0x0 0x8 0x0 IRQ_TYPE_NONE>,
@@ -349,6 +349,12 @@
};
};
+ pm660_pbs: qcom,pbs@7400 {
+ compatible = "qcom,qpnp-pbs";
+ reg = <0x7400 0x100>;
+ status = "disabled";
+ };
+
bcl_sensor: bcl@4200 {
compatible = "qcom,msm-bcl-lmh";
reg = <0x4200 0xff>,
diff --git a/arch/arm64/boot/dts/qcom/qg-batterydata-ascent-3450mah.dtsi b/arch/arm64/boot/dts/qcom/qg-batterydata-ascent-3450mah.dtsi
index 8af4254..f28f19e2 100644
--- a/arch/arm64/boot/dts/qcom/qg-batterydata-ascent-3450mah.dtsi
+++ b/arch/arm64/boot/dts/qcom/qg-batterydata-ascent-3450mah.dtsi
@@ -32,6 +32,11 @@
4201000 4300000 2760000
4301000 4350000 2070000>;
+ /* COOL = 5 DegC, WARM = 40 DegC */
+ qcom,jeita-soft-thresholds = <0x44bd 0x1fc4>;
+ /* COLD = 0 DegC, HOT = 45 DegC */
+ qcom,jeita-hard-thresholds = <0x4aa5 0x1bfb>;
+
qcom,fcc1-temp-lut {
qcom,lut-col-legend = <0 10 25 40 50>;
qcom,lut-data = <3377 3428 3481 3496 3500>;
diff --git a/arch/arm64/boot/dts/qcom/qg-batterydata-mlp356477-2800mah.dtsi b/arch/arm64/boot/dts/qcom/qg-batterydata-mlp356477-2800mah.dtsi
index 6bcfd37..3b18010 100644
--- a/arch/arm64/boot/dts/qcom/qg-batterydata-mlp356477-2800mah.dtsi
+++ b/arch/arm64/boot/dts/qcom/qg-batterydata-mlp356477-2800mah.dtsi
@@ -29,6 +29,11 @@
151 450 4400000
451 550 4150000>;
+ /* COOL = 15 DegC, WARM = 45 DegC */
+ qcom,jeita-soft-thresholds = <0x4621 0x20b8>;
+ /* COLD = 0 DegC, HOT = 55 DegC */
+ qcom,jeita-hard-thresholds = <0x58cd 0x181d>;
+
qcom,fcc1-temp-lut {
qcom,lut-col-legend = <0 10 25 40 50>;
qcom,lut-data = <2715 2788 2861 2898 2908>;
diff --git a/arch/arm64/boot/dts/qcom/sda450-cdp.dts b/arch/arm64/boot/dts/qcom/sda450-cdp.dts
new file mode 100644
index 0000000..e8f22b8
--- /dev/null
+++ b/arch/arm64/boot/dts/qcom/sda450-cdp.dts
@@ -0,0 +1,26 @@
+/*
+ * Copyright (c) 2018, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+/dts-v1/;
+
+#include "sda450.dtsi"
+#include "pmi8950.dtsi"
+#include "msm8953-cdp.dtsi"
+#include "msm8953-pmi8950.dtsi"
+
+/ {
+ model = "Qualcomm Technologies, Inc. SDA450 + PMI8950 CDP";
+ compatible = "qcom,sda450-cdp", "qcom,sda450", "qcom,cdp";
+ qcom,board-id = <1 0>;
+ qcom,pmic-id = <0x010016 0x010011 0x0 0x0>;
+};
diff --git a/arch/arm64/boot/dts/qcom/sda450-mtp.dts b/arch/arm64/boot/dts/qcom/sda450-mtp.dts
new file mode 100644
index 0000000..06002e0
--- /dev/null
+++ b/arch/arm64/boot/dts/qcom/sda450-mtp.dts
@@ -0,0 +1,44 @@
+/*
+ * Copyright (c) 2018, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+/dts-v1/;
+
+#include "sda450.dtsi"
+#include "pmi8950.dtsi"
+#include "msm8953-mtp.dtsi"
+#include "msm8953-pmi8950.dtsi"
+
+/ {
+ model = "Qualcomm Technologies, Inc. SDA450 + PMI8950 MTP";
+ compatible = "qcom,sda450-mtp", "qcom,sda450", "qcom,mtp";
+ qcom,board-id = <8 0>;
+ qcom,pmic-id = <0x010016 0x010011 0x0 0x0>;
+};
+
+/{
+ mtp_batterydata: qcom,battery-data {
+ qcom,batt-id-range-pct = <15>;
+ #include "batterydata-itech-3000mah.dtsi"
+ #include "batterydata-ascent-3450mAh.dtsi"
+ };
+};
+
+&qpnp_fg {
+ qcom,battery-data = <&mtp_batterydata>;
+};
+
+&qpnp_smbcharger {
+ qcom,battery-data = <&mtp_batterydata>;
+ qcom,chg-led-sw-controls;
+ qcom,chg-led-support;
+};
diff --git a/arch/arm64/boot/dts/qcom/sda450-pmi632-cdp-s2.dts b/arch/arm64/boot/dts/qcom/sda450-pmi632-cdp-s2.dts
new file mode 100644
index 0000000..14c5e22
--- /dev/null
+++ b/arch/arm64/boot/dts/qcom/sda450-pmi632-cdp-s2.dts
@@ -0,0 +1,26 @@
+/*
+ * Copyright (c) 2018, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+/dts-v1/;
+
+#include "sda450.dtsi"
+#include "sdm450-pmi632-cdp-s2.dtsi"
+#include "sdm450-pmi632.dtsi"
+
+/ {
+ model = "Qualcomm Technologies, Inc. SDA450 + PMI632 CDP S2";
+ compatible = "qcom,sda450-cdp", "qcom,sda450", "qcom,cdp";
+ qcom,board-id = <1 2>;
+ qcom,pmic-id = <0x010016 0x25 0x0 0x0>;
+};
+
diff --git a/arch/arm64/boot/dts/qcom/sda450-pmi632-mtp-s3.dts b/arch/arm64/boot/dts/qcom/sda450-pmi632-mtp-s3.dts
new file mode 100644
index 0000000..c907977
--- /dev/null
+++ b/arch/arm64/boot/dts/qcom/sda450-pmi632-mtp-s3.dts
@@ -0,0 +1,26 @@
+/*
+ * Copyright (c) 2018, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+/dts-v1/;
+
+#include "sda450.dtsi"
+#include "sdm450-pmi632-mtp-s3.dtsi"
+#include "sdm450-pmi632.dtsi"
+
+/ {
+ model = "Qualcomm Technologies, Inc. SDA450 + PMI632 MTP S3";
+ compatible = "qcom,sda450-mtp", "qcom,sda450", "qcom,mtp";
+ qcom,board-id = <8 3>;
+ qcom,pmic-id = <0x010016 0x25 0x0 0x0>;
+};
+
diff --git a/arch/arm64/boot/dts/qcom/sda450-pmi632.dts b/arch/arm64/boot/dts/qcom/sda450-pmi632.dts
new file mode 100644
index 0000000..1bb0b47
--- /dev/null
+++ b/arch/arm64/boot/dts/qcom/sda450-pmi632.dts
@@ -0,0 +1,25 @@
+/*
+ * Copyright (c) 2018, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+/dts-v1/;
+
+#include "sda450.dtsi"
+#include "sdm450-pmi632.dtsi"
+
+/ {
+ model = "Qualcomm Technologies, Inc. SDA450 + PMI632 SOC";
+ compatible = "qcom,sda450";
+ qcom,pmic-id = <0x010016 0x25 0x0 0x0>;
+ qcom,pmic-name = "PMI632";
+};
+
diff --git a/arch/arm64/boot/dts/qcom/sda450.dts b/arch/arm64/boot/dts/qcom/sda450.dts
new file mode 100644
index 0000000..13b1622
--- /dev/null
+++ b/arch/arm64/boot/dts/qcom/sda450.dts
@@ -0,0 +1,26 @@
+/*
+ * Copyright (c) 2018, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+/dts-v1/;
+
+#include "sda450.dtsi"
+#include "pmi8950.dtsi"
+#include "msm8953-pmi8950.dtsi"
+
+/ {
+ model = "Qualcomm Technologies, Inc. SDA450 + PMI8950 SOC";
+ compatible = "qcom,sda450";
+ qcom,pmic-id = <0x010016 0x010011 0x0 0x0>;
+ qcom,pmic-name = "PMI8950";
+};
+
diff --git a/arch/arm64/boot/dts/qcom/sda450.dtsi b/arch/arm64/boot/dts/qcom/sda450.dtsi
new file mode 100644
index 0000000..ba99fe9
--- /dev/null
+++ b/arch/arm64/boot/dts/qcom/sda450.dtsi
@@ -0,0 +1,22 @@
+/*
+ * Copyright (c) 2018, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include "sdm450.dtsi"
+
+/ {
+ model = "Qualcomm Technologies, Inc. SDA450";
+ compatible = "qcom,sda450";
+ qcom,msm-id = <351 0x0>;
+ qcom,msm-name = "SDA450";
+};
+
diff --git a/arch/arm64/boot/dts/qcom/sdm439-pmi632.dtsi b/arch/arm64/boot/dts/qcom/sdm439-pmi632.dtsi
index 5075862..300c83a 100644
--- a/arch/arm64/boot/dts/qcom/sdm439-pmi632.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm439-pmi632.dtsi
@@ -39,6 +39,10 @@
qcom,battery-data = <&mtp_batterydata>;
};
+&pmi632_charger {
+ qcom,battery-data = <&mtp_batterydata>;
+};
+
&pm8953_typec {
status = "disabled";
};
diff --git a/arch/arm64/boot/dts/qcom/sdm450-pmi632.dtsi b/arch/arm64/boot/dts/qcom/sdm450-pmi632.dtsi
index a4b6054..5c127bc 100644
--- a/arch/arm64/boot/dts/qcom/sdm450-pmi632.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm450-pmi632.dtsi
@@ -45,6 +45,10 @@
qcom,battery-data = <&mtp_batterydata>;
};
+&pmi632_charger {
+ qcom,battery-data = <&mtp_batterydata>;
+};
+
&pm8953_gpios {
bklt_en {
bklt_en_default: bklt_en_default {
diff --git a/arch/arm64/boot/dts/qcom/sdm845.dtsi b/arch/arm64/boot/dts/qcom/sdm845.dtsi
index ffc68d2..22b4b90 100644
--- a/arch/arm64/boot/dts/qcom/sdm845.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm845.dtsi
@@ -2570,9 +2570,31 @@
<&clock_gcc GCC_CE1_AXI_CLK>;
qcom,ce-opp-freq = <171430000>;
qcom,request-bw-before-clk;
+
qcom,smmu-s1-enable;
iommus = <&apps_smmu 0x706 0x1>,
<&apps_smmu 0x716 0x1>;
+
+ qcom_cedev_ns_cb {
+ compatible = "qcom,qcedev,context-bank";
+ label = "ns_context";
+ iommus = <&apps_smmu 0x712 0>,
+ <&apps_smmu 0x71f 0>;
+ virtual-addr = <0x60000000>;
+ virtual-size = <0x40000000>;
+ };
+
+ qcom_cedev_s_cb {
+ compatible = "qcom,qcedev,context-bank";
+ label = "secure_context";
+ iommus = <&apps_smmu 0x713 0>,
+ <&apps_smmu 0x71c 0>,
+ <&apps_smmu 0x71d 0>,
+ <&apps_smmu 0x71e 0>;
+ virtual-addr = <0x60200000>;
+ virtual-size = <0x40000000>;
+ qcom,secure-context-bank;
+ };
};
qcom_msmhdcp: qcom,msm_hdcp {
diff --git a/arch/arm64/configs/msm8953-perf_defconfig b/arch/arm64/configs/msm8953-perf_defconfig
index 0fe026f..a101b43 100644
--- a/arch/arm64/configs/msm8953-perf_defconfig
+++ b/arch/arm64/configs/msm8953-perf_defconfig
@@ -587,6 +587,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/msm8953_defconfig b/arch/arm64/configs/msm8953_defconfig
index b15ef8a..1cfee45 100644
--- a/arch/arm64/configs/msm8953_defconfig
+++ b/arch/arm64/configs/msm8953_defconfig
@@ -607,6 +607,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..6bfe775 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
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..64cdeae 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
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 7351019..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;
}
@@ -2668,7 +2688,7 @@
static int fastrpc_file_free(struct fastrpc_file *fl)
{
struct hlist_node *n = NULL;
- struct fastrpc_mmap *map = NULL;
+ struct fastrpc_mmap *map = NULL, *lmap = NULL;
struct fastrpc_perf *perf = NULL, *fperf = NULL;
int cid;
@@ -2692,9 +2712,15 @@
fastrpc_context_list_dtor(fl);
fastrpc_buf_list_free(fl);
mutex_lock(&fl->fl_map_mutex);
- hlist_for_each_entry_safe(map, n, &fl->maps, hn) {
- fastrpc_mmap_free(map, 1);
- }
+ do {
+ lmap = NULL;
+ hlist_for_each_entry_safe(map, n, &fl->maps, hn) {
+ hlist_del_init(&map->hn);
+ lmap = map;
+ break;
+ }
+ fastrpc_mmap_free(lmap, 1);
+ } while (lmap);
mutex_unlock(&fl->fl_map_mutex);
if (fl->refcount && (fl->ssrcount == fl->apps->channel[cid].ssrcount))
kref_put_mutex(&fl->apps->channel[cid].kref,
@@ -3090,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;
}
@@ -3129,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;
@@ -3369,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)
{
@@ -3406,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) {
@@ -3414,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;
}
@@ -3481,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)
@@ -3720,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/cpuidle/lpm-levels.c b/drivers/cpuidle/lpm-levels.c
index 69ac505..9ae640d 100644
--- a/drivers/cpuidle/lpm-levels.c
+++ b/drivers/cpuidle/lpm-levels.c
@@ -110,7 +110,7 @@
static DEFINE_PER_CPU(struct lpm_cpu*, cpu_lpm);
static bool suspend_in_progress;
static struct hrtimer lpm_hrtimer;
-static struct hrtimer histtimer;
+static DEFINE_PER_CPU(struct hrtimer, histtimer);
static struct lpm_debug *lpm_debug;
static phys_addr_t lpm_debug_phys;
static const int num_dbg_elements = 0x100;
@@ -339,7 +339,10 @@
static void histtimer_cancel(void)
{
- hrtimer_try_to_cancel(&histtimer);
+ unsigned int cpu = raw_smp_processor_id();
+ struct hrtimer *cpu_histtimer = &per_cpu(histtimer, cpu);
+
+ hrtimer_try_to_cancel(cpu_histtimer);
}
static enum hrtimer_restart histtimer_fn(struct hrtimer *h)
@@ -355,9 +358,11 @@
{
uint64_t time_ns = time_us * NSEC_PER_USEC;
ktime_t hist_ktime = ns_to_ktime(time_ns);
+ unsigned int cpu = raw_smp_processor_id();
+ struct hrtimer *cpu_histtimer = &per_cpu(histtimer, cpu);
- histtimer.function = histtimer_fn;
- hrtimer_start(&histtimer, hist_ktime, HRTIMER_MODE_REL_PINNED);
+ cpu_histtimer->function = histtimer_fn;
+ hrtimer_start(cpu_histtimer, hist_ktime, HRTIMER_MODE_REL_PINNED);
}
static void cluster_timer_init(struct lpm_cluster *cluster)
@@ -1667,6 +1672,8 @@
{
int ret;
int size;
+ unsigned int cpu;
+ struct hrtimer *cpu_histtimer;
struct kobject *module_kobj = NULL;
struct md_region md_entry;
@@ -1691,7 +1698,11 @@
suspend_set_ops(&lpm_suspend_ops);
freeze_set_ops(&lpm_freeze_ops);
hrtimer_init(&lpm_hrtimer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
- hrtimer_init(&histtimer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
+ for_each_possible_cpu(cpu) {
+ cpu_histtimer = &per_cpu(histtimer, cpu);
+ hrtimer_init(cpu_histtimer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
+ }
+
cluster_timer_init(lpm_root_node);
size = num_dbg_elements * sizeof(struct lpm_debug);
diff --git a/drivers/crypto/msm/Makefile b/drivers/crypto/msm/Makefile
index 9ecb646..b712fc1 100644
--- a/drivers/crypto/msm/Makefile
+++ b/drivers/crypto/msm/Makefile
@@ -1,5 +1,6 @@
obj-$(CONFIG_CRYPTO_DEV_QCOM_MSM_QCE) += qce50.o
obj-$(CONFIG_CRYPTO_DEV_QCEDEV) += qcedev.o
+obj-$(CONFIG_CRYPTO_DEV_QCEDEV) += qcedev_smmu.o
obj-$(CONFIG_CRYPTO_DEV_QCRYPTO) += qcrypto.o
obj-$(CONFIG_CRYPTO_DEV_OTA_CRYPTO) += ota_crypto.o
obj-$(CONFIG_CRYPTO_DEV_QCOM_ICE) += ice.o
diff --git a/drivers/crypto/msm/compat_qcedev.c b/drivers/crypto/msm/compat_qcedev.c
index d61b6f3..41b13a7d 100644
--- a/drivers/crypto/msm/compat_qcedev.c
+++ b/drivers/crypto/msm/compat_qcedev.c
@@ -1,7 +1,7 @@
/*
* QTI CE 32-bit compatibility syscall for 64-bit systems
*
- * Copyright (c) 2014-2017, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2014-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
@@ -254,6 +254,75 @@
return err;
}
+static int compat_xfer_qcedev_map_buf_req(
+ struct compat_qcedev_map_buf_req __user *data32,
+ struct qcedev_map_buf_req __user *data, bool to_get)
+{
+ int rc = 0, i = 0, fd = -1;
+ uint32_t fd_size, fd_offset, num_fds, buf_vaddr;
+
+ if (to_get) {
+ /* copy from compat struct */
+ for (i = 0; i < QCEDEV_MAX_BUFFERS; i++) {
+ rc |= get_user(fd, &data32->fd[i]);
+ rc |= put_user(fd, &data->fd[i]);
+ rc |= get_user(fd_size, &data32->fd_size[i]);
+ rc |= put_user(fd_size, &data->fd_size[i]);
+ rc |= get_user(fd_offset, &data32->fd_offset[i]);
+ rc |= put_user(fd_offset, &data->fd_offset[i]);
+ rc |= get_user(buf_vaddr, &data32->buf_vaddr[i]);
+ rc |= put_user(buf_vaddr, &data->buf_vaddr[i]);
+ }
+
+ rc |= get_user(num_fds, &data32->num_fds);
+ rc |= put_user(num_fds, &data->num_fds);
+ } else {
+ /* copy to compat struct */
+ for (i = 0; i < QCEDEV_MAX_BUFFERS; i++) {
+ rc |= get_user(fd, &data->fd[i]);
+ rc |= put_user(fd, &data32->fd[i]);
+ rc |= get_user(fd_size, &data->fd_size[i]);
+ rc |= put_user(fd_size, &data32->fd_size[i]);
+ rc |= get_user(fd_offset, &data->fd_offset[i]);
+ rc |= put_user(fd_offset, &data32->fd_offset[i]);
+ rc |= get_user(buf_vaddr, &data->buf_vaddr[i]);
+ rc |= put_user(buf_vaddr, &data32->buf_vaddr[i]);
+ }
+ rc |= get_user(num_fds, &data->num_fds);
+ rc |= put_user(num_fds, &data32->num_fds);
+ }
+
+ return rc;
+}
+
+static int compat_xfer_qcedev_unmap_buf_req(
+ struct compat_qcedev_unmap_buf_req __user *data32,
+ struct qcedev_unmap_buf_req __user *data, bool to_get)
+{
+ int i = 0, rc = 0, fd = -1;
+ uint32_t num_fds;
+
+ if (to_get) {
+ /* copy from compat struct */
+ for (i = 0; i < QCEDEV_MAX_BUFFERS; i++) {
+ rc |= get_user(fd, &data32->fd[i]);
+ rc |= put_user(fd, &data->fd[i]);
+ }
+ rc |= get_user(num_fds, &data32->num_fds);
+ rc |= put_user(num_fds, &data->num_fds);
+ } else {
+ /* copy to compat struct */
+ for (i = 0; i < QCEDEV_MAX_BUFFERS; i++) {
+ rc |= get_user(fd, &data->fd[i]);
+ rc |= put_user(fd, &data32->fd[i]);
+ }
+ rc |= get_user(num_fds, &data->num_fds);
+ rc |= put_user(num_fds, &data32->num_fds);
+ }
+ return rc;
+}
+
+
static int compat_get_qcedev_sha_op_req(
struct compat_qcedev_sha_op_req __user *data32,
struct qcedev_sha_op_req __user *data)
@@ -359,6 +428,10 @@
return QCEDEV_IOCTL_GET_SHA_REQ;
case COMPAT_QCEDEV_IOCTL_GET_CMAC_REQ:
return QCEDEV_IOCTL_GET_CMAC_REQ;
+ case COMPAT_QCEDEV_IOCTL_MAP_BUF_REQ:
+ return QCEDEV_IOCTL_MAP_BUF_REQ;
+ case COMPAT_QCEDEV_IOCTL_UNMAP_BUF_REQ:
+ return QCEDEV_IOCTL_UNMAP_BUF_REQ;
default:
return cmd;
}
@@ -412,6 +485,46 @@
err = compat_put_qcedev_sha_op_req(data32, data);
return ret ? ret : err;
}
+ case COMPAT_QCEDEV_IOCTL_MAP_BUF_REQ: {
+ struct compat_qcedev_map_buf_req __user *data32;
+ struct qcedev_map_buf_req __user *data;
+ int err;
+
+ data32 = compat_ptr(arg);
+ data = compat_alloc_user_space(sizeof(*data));
+ if (!data)
+ return -EINVAL;
+
+ err = compat_xfer_qcedev_map_buf_req(data32, data, true);
+ if (err)
+ return err;
+
+ ret = qcedev_ioctl(file, convert_cmd(cmd), (unsigned long)data);
+ err = compat_xfer_qcedev_map_buf_req(data32, data, false);
+ return ret ? ret : err;
+
+ break;
+ }
+ case COMPAT_QCEDEV_IOCTL_UNMAP_BUF_REQ: {
+ struct compat_qcedev_unmap_buf_req __user *data32;
+ struct qcedev_unmap_buf_req __user *data;
+ int err;
+
+ data32 = compat_ptr(arg);
+ data = compat_alloc_user_space(sizeof(*data));
+ if (!data)
+ return -EINVAL;
+
+ err = compat_xfer_qcedev_unmap_buf_req(data32, data, true);
+ if (err)
+ return err;
+
+ ret = qcedev_ioctl(file, convert_cmd(cmd), (unsigned long)data);
+ err = compat_xfer_qcedev_unmap_buf_req(data32, data, false);
+ return ret ? ret : err;
+
+ break;
+ }
default:
return -ENOIOCTLCMD;
}
diff --git a/drivers/crypto/msm/compat_qcedev.h b/drivers/crypto/msm/compat_qcedev.h
index 6c041cb..4cefd0a 100644
--- a/drivers/crypto/msm/compat_qcedev.h
+++ b/drivers/crypto/msm/compat_qcedev.h
@@ -151,6 +151,33 @@
enum qcedev_sha_alg_enum alg;
};
+/**
+ * struct compact_qcedev_map_buf_req - Holds the mapping request information
+ * fd (IN): Array of fds.
+ * num_fds (IN): Number of fds in fd[].
+ * fd_size (IN): Array of sizes corresponding to each fd in fd[].
+ * fd_offset (IN): Array of offset corresponding to each fd in fd[].
+ * vaddr (OUT): Array of mapped virtual address corresponding to
+ * each fd in fd[].
+ */
+struct compat_qcedev_map_buf_req {
+ compat_long_t fd[QCEDEV_MAX_BUFFERS];
+ compat_ulong_t num_fds;
+ compat_ulong_t fd_size[QCEDEV_MAX_BUFFERS];
+ compat_ulong_t fd_offset[QCEDEV_MAX_BUFFERS];
+ compat_u64 buf_vaddr[QCEDEV_MAX_BUFFERS];
+};
+
+/**
+ * struct compat_qcedev_unmap_buf_req - Holds the hashing request information
+ * fd (IN): Array of fds to unmap
+ * num_fds (IN): Number of fds in fd[].
+ */
+struct compat_qcedev_unmap_buf_req {
+ compat_long_t fd[QCEDEV_MAX_BUFFERS];
+ compat_ulong_t num_fds;
+};
+
struct file;
extern long compat_qcedev_ioctl(struct file *file,
unsigned int cmd, unsigned long arg);
@@ -173,6 +200,9 @@
_IO(QCEDEV_IOC_MAGIC, 8)
#define COMPAT_QCEDEV_IOCTL_GET_CMAC_REQ \
_IOWR(QCEDEV_IOC_MAGIC, 9, struct compat_qcedev_sha_op_req)
-
+#define COMPAT_QCEDEV_IOCTL_MAP_BUF_REQ \
+ _IOWR(QCEDEV_IOC_MAGIC, 10, struct compat_qcedev_map_buf_req)
+#define COMPAT_QCEDEV_IOCTL_UNMAP_BUF_REQ \
+ _IOWR(QCEDEV_IOC_MAGIC, 11, struct compat_qcedev_unmap_buf_req)
#endif /* CONFIG_COMPAT */
#endif /* _UAPI_COMPAT_QCEDEV__H */
diff --git a/drivers/crypto/msm/qcedev.c b/drivers/crypto/msm/qcedev.c
index 958fb91..c8d1158 100644
--- a/drivers/crypto/msm/qcedev.c
+++ b/drivers/crypto/msm/qcedev.c
@@ -1,7 +1,7 @@
/*
* QTI CE device driver.
*
- * Copyright (c) 2010-2017, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2010-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
@@ -35,6 +35,7 @@
#include <crypto/hash.h>
#include "qcedevi.h"
#include "qce.h"
+#include "qcedev_smmu.h"
#include <linux/compat.h>
#include "compat_qcedev.h"
@@ -59,6 +60,14 @@
static DEFINE_MUTEX(qcedev_sent_bw_req);
static DEFINE_MUTEX(hash_access_lock);
+MODULE_DEVICE_TABLE(of, qcedev_match);
+
+static const struct of_device_id qcedev_match[] = {
+ { .compatible = "qcom,qcedev"},
+ { .compatible = "qcom,qcedev,context-bank"},
+ {}
+};
+
static int qcedev_control_clocks(struct qcedev_control *podev, bool enable)
{
unsigned int control_flag;
@@ -259,6 +268,9 @@
file->private_data = handle;
if (podev->platform_support.bus_scale_table != NULL)
qcedev_ce_high_bw_req(podev, true);
+
+ mutex_init(&handle->registeredbufs.lock);
+ INIT_LIST_HEAD(&handle->registeredbufs.list);
return 0;
}
@@ -1857,6 +1869,62 @@
}
break;
+ case QCEDEV_IOCTL_MAP_BUF_REQ:
+ {
+ unsigned long long vaddr = 0;
+ struct qcedev_map_buf_req map_buf = { {0} };
+ int i = 0;
+
+ if (copy_from_user(&map_buf,
+ (void __user *)arg, sizeof(map_buf)))
+ return -EFAULT;
+
+ for (i = 0; i < map_buf.num_fds; i++) {
+ err = qcedev_check_and_map_buffer(handle,
+ map_buf.fd[i],
+ map_buf.fd_offset[i],
+ map_buf.fd_size[i],
+ &vaddr);
+ if (err) {
+ pr_err(
+ "%s: err: failed to map fd(%d) - %d\n",
+ __func__, map_buf.fd[i], err);
+ return err;
+ }
+ map_buf.buf_vaddr[i] = vaddr;
+ pr_info("%s: info: vaddr = %llx\n",
+ __func__, vaddr);
+ }
+
+ if (copy_to_user((void __user *)arg, &map_buf,
+ sizeof(map_buf)))
+ return -EFAULT;
+ break;
+ }
+
+ case QCEDEV_IOCTL_UNMAP_BUF_REQ:
+ {
+ struct qcedev_unmap_buf_req unmap_buf = { { 0 } };
+ int i = 0;
+
+ if (copy_from_user(&unmap_buf,
+ (void __user *)arg, sizeof(unmap_buf)))
+ return -EFAULT;
+
+ for (i = 0; i < unmap_buf.num_fds; i++) {
+ err = qcedev_check_and_unmap_buffer(handle,
+ unmap_buf.fd[i]);
+ if (err) {
+ pr_err(
+ "%s: err: failed to unmap fd(%d) - %d\n",
+ __func__,
+ unmap_buf.fd[i], err);
+ return err;
+ }
+ }
+ break;
+ }
+
default:
return -ENOTTY;
}
@@ -1864,7 +1932,7 @@
return err;
}
-static int qcedev_probe(struct platform_device *pdev)
+static int qcedev_probe_device(struct platform_device *pdev)
{
void *handle = NULL;
int rc = 0;
@@ -1877,6 +1945,8 @@
INIT_LIST_HEAD(&podev->ready_commands);
podev->active_command = NULL;
+ INIT_LIST_HEAD(&podev->context_banks);
+
spin_lock_init(&podev->lock);
tasklet_init(&podev->done_tasklet, req_done, (unsigned long)podev);
@@ -1934,11 +2004,33 @@
}
rc = misc_register(&podev->miscdevice);
- if (rc >= 0)
- return 0;
+ if (rc) {
+ pr_err("%s: err: register failed for misc: %d\n", __func__, rc);
+ goto exit_qce_close;
+ }
+
+ podev->mem_client = qcedev_mem_new_client(MEM_ION);
+ if (!podev->mem_client) {
+ pr_err("%s: err: qcedev_mem_new_client failed\n", __func__);
+ goto err;
+ }
+
+ rc = of_platform_populate(pdev->dev.of_node, qcedev_match,
+ NULL, &pdev->dev);
+ if (rc) {
+ pr_err("%s: err: of_platform_populate failed: %d\n",
+ __func__, rc);
+ goto err;
+ }
+
+ return 0;
+
+err:
+ if (podev->mem_client)
+ qcedev_mem_delete_client(podev->mem_client);
+ podev->mem_client = NULL;
misc_deregister(&podev->miscdevice);
-
exit_qce_close:
if (handle)
qce_close(handle);
@@ -1947,11 +2039,23 @@
exit_unregister_bus_scale:
if (podev->platform_support.bus_scale_table != NULL)
msm_bus_scale_unregister_client(podev->bus_scale_handle);
+ podev->bus_scale_handle = 0;
platform_set_drvdata(pdev, NULL);
podev->pdev = NULL;
podev->qce = NULL;
return rc;
+}
+
+static int qcedev_probe(struct platform_device *pdev)
+{
+ if (of_device_is_compatible(pdev->dev.of_node, "qcom,qcedev"))
+ return qcedev_probe_device(pdev);
+ else if (of_device_is_compatible(pdev->dev.of_node,
+ "qcom,qcedev,context-bank"))
+ return qcedev_parse_context_bank(pdev);
+
+ return -EINVAL;
};
static int qcedev_remove(struct platform_device *pdev)
@@ -2017,12 +2121,6 @@
return 0;
}
-static const struct of_device_id qcedev_match[] = {
- { .compatible = "qcom,qcedev",
- },
- {}
-};
-
static struct platform_driver qcedev_plat_driver = {
.probe = qcedev_probe,
.remove = qcedev_remove,
diff --git a/drivers/crypto/msm/qcedev_smmu.c b/drivers/crypto/msm/qcedev_smmu.c
new file mode 100644
index 0000000..c99b493
--- /dev/null
+++ b/drivers/crypto/msm/qcedev_smmu.c
@@ -0,0 +1,546 @@
+/* Qti (or) Qualcomm Technologies Inc CE device driver.
+ *
+ * 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 <asm/dma-iommu.h>
+#include <linux/dma-mapping.h>
+#include <linux/list.h>
+#include <linux/qcedev.h>
+#include "qcedevi.h"
+#include "qcedev_smmu.h"
+#include "soc/qcom/secure_buffer.h"
+
+static bool compare_ion_buffers(struct qcedev_mem_client *mem_client,
+ struct ion_handle *hndl, int fd);
+
+static int qcedev_setup_context_bank(struct context_bank_info *cb,
+ struct device *dev)
+{
+ int rc = 0;
+ int secure_vmid = VMID_INVAL;
+ struct bus_type *bus;
+
+ if (!dev || !cb) {
+ pr_err("%s err: invalid input params\n", __func__);
+ return -EINVAL;
+ }
+ cb->dev = dev;
+
+ bus = cb->dev->bus;
+ if (IS_ERR_OR_NULL(bus)) {
+ pr_err("%s err: failed to get bus type\n", __func__);
+ rc = PTR_ERR(bus) ?: -ENODEV;
+ goto remove_cb;
+ }
+
+ cb->mapping = arm_iommu_create_mapping(bus, cb->start_addr, cb->size);
+ if (IS_ERR_OR_NULL(cb->mapping)) {
+ pr_err("%s err: failed to create mapping\n", __func__);
+ rc = PTR_ERR(cb->mapping) ?: -ENODEV;
+ goto remove_cb;
+ }
+
+ if (cb->is_secure) {
+ /* Hardcoded since we only have this vmid.*/
+ secure_vmid = VMID_CP_BITSTREAM;
+ rc = iommu_domain_set_attr(cb->mapping->domain,
+ DOMAIN_ATTR_SECURE_VMID, &secure_vmid);
+ if (rc) {
+ pr_err("%s err: programming secure vmid failed %s %d\n",
+ __func__, dev_name(dev), rc);
+ goto release_mapping;
+ }
+ }
+
+ rc = arm_iommu_attach_device(cb->dev, cb->mapping);
+ if (rc) {
+ pr_err("%s err: Failed to attach %s - %d\n",
+ __func__, dev_name(dev), rc);
+ goto release_mapping;
+ }
+
+ pr_info("%s Attached %s and create mapping\n", __func__, dev_name(dev));
+ pr_info("%s Context Bank name:%s, is_secure:%d, start_addr:%#x\n",
+ __func__, cb->name, cb->is_secure, cb->start_addr);
+ pr_info("%s size:%#x, dev:%pK, mapping:%pK\n", __func__, cb->size,
+ cb->dev, cb->mapping);
+ return rc;
+
+release_mapping:
+ arm_iommu_release_mapping(cb->mapping);
+remove_cb:
+ return rc;
+}
+
+int qcedev_parse_context_bank(struct platform_device *pdev)
+{
+ struct qcedev_control *podev;
+ struct context_bank_info *cb = NULL;
+ struct device_node *np = NULL;
+ int rc = 0;
+
+ if (!pdev) {
+ pr_err("%s err: invalid platform devices\n", __func__);
+ return -EINVAL;
+ }
+ if (!pdev->dev.parent) {
+ pr_err("%s err: failed to find a parent for %s\n",
+ __func__, dev_name(&pdev->dev));
+ return -EINVAL;
+ }
+
+ podev = dev_get_drvdata(pdev->dev.parent);
+ np = pdev->dev.of_node;
+ cb = devm_kzalloc(&pdev->dev, sizeof(*cb), GFP_KERNEL);
+ if (!cb) {
+ pr_err("%s ERROR = Failed to allocate cb\n", __func__);
+ return -ENOMEM;
+ }
+
+ INIT_LIST_HEAD(&cb->list);
+ list_add_tail(&cb->list, &podev->context_banks);
+
+ rc = of_property_read_string(np, "label", &cb->name);
+ if (rc)
+ pr_debug("%s ERROR = Unable to read label\n", __func__);
+
+ rc = of_property_read_u32(np, "virtual-addr", &cb->start_addr);
+ if (rc) {
+ pr_err("%s err: cannot read virtual region addr %d\n",
+ __func__, rc);
+ goto err_setup_cb;
+ }
+
+ rc = of_property_read_u32(np, "virtual-size", &cb->size);
+ if (rc) {
+ pr_err("%s err: cannot read virtual region size %d\n",
+ __func__, rc);
+ goto err_setup_cb;
+ }
+
+ cb->is_secure = of_property_read_bool(np, "qcom,secure-context-bank");
+
+ rc = qcedev_setup_context_bank(cb, &pdev->dev);
+ if (rc) {
+ pr_err("%s err: cannot setup context bank %d\n", __func__, rc);
+ goto err_setup_cb;
+ }
+
+ return 0;
+
+err_setup_cb:
+ devm_kfree(&pdev->dev, cb);
+ list_del(&cb->list);
+ return rc;
+}
+
+struct qcedev_mem_client *qcedev_mem_new_client(enum qcedev_mem_type mtype)
+{
+ struct qcedev_mem_client *mem_client = NULL;
+ struct ion_client *clnt = NULL;
+
+ switch (mtype) {
+ case MEM_ION:
+ clnt = msm_ion_client_create("qcedev_client");
+ if (!clnt)
+ pr_err("%s: err: failed to allocate ion client\n",
+ __func__);
+ break;
+
+ default:
+ pr_err("%s: err: Mem type not supported\n", __func__);
+ }
+
+ if (clnt) {
+ mem_client = kzalloc(sizeof(*mem_client), GFP_KERNEL);
+ if (!mem_client)
+ goto err;
+ mem_client->mtype = mtype;
+ mem_client->client = clnt;
+ }
+
+ return mem_client;
+
+err:
+ if (clnt)
+ ion_client_destroy(clnt);
+ return NULL;
+}
+
+void qcedev_mem_delete_client(struct qcedev_mem_client *mem_client)
+{
+ if (mem_client && mem_client->client)
+ ion_client_destroy(mem_client->client);
+
+ kfree(mem_client);
+}
+
+static bool is_iommu_present(struct qcedev_handle *qce_hndl)
+{
+ return !list_empty(&qce_hndl->cntl->context_banks);
+}
+
+static struct context_bank_info *get_context_bank(
+ struct qcedev_handle *qce_hndl, bool is_secure)
+{
+ struct qcedev_control *podev = qce_hndl->cntl;
+ struct context_bank_info *cb = NULL, *match = NULL;
+
+ list_for_each_entry(cb, &podev->context_banks, list) {
+ if (cb->is_secure == is_secure) {
+ match = cb;
+ break;
+ }
+ }
+ return match;
+}
+
+static int ion_map_buffer(struct qcedev_handle *qce_hndl,
+ struct qcedev_mem_client *mem_client, int fd,
+ unsigned int fd_size, struct qcedev_reg_buf_info *binfo)
+{
+ struct ion_client *clnt = mem_client->client;
+ struct ion_handle *hndl = NULL;
+ unsigned long ion_flags = 0;
+ int rc = 0;
+ struct dma_buf *buf = NULL;
+ struct dma_buf_attachment *attach = NULL;
+ struct sg_table *table = NULL;
+ struct context_bank_info *cb = NULL;
+
+ buf = dma_buf_get(fd);
+ if (IS_ERR_OR_NULL(buf))
+ return -EINVAL;
+
+ hndl = ion_import_dma_buf(clnt, buf);
+ if (IS_ERR_OR_NULL(hndl)) {
+ pr_err("%s: err: invalid ion_handle\n", __func__);
+ rc = -ENOMEM;
+ goto import_buf_err;
+ }
+
+ rc = ion_handle_get_flags(clnt, hndl, &ion_flags);
+ if (rc) {
+ pr_err("%s: err: failed to get ion flags: %d\n", __func__, rc);
+ goto map_err;
+ }
+
+ if (is_iommu_present(qce_hndl)) {
+ cb = get_context_bank(qce_hndl, ion_flags & ION_FLAG_SECURE);
+ if (!cb) {
+ pr_err("%s: err: failed to get context bank info\n",
+ __func__);
+ rc = -EIO;
+ goto map_err;
+ }
+
+ /* Prepare a dma buf for dma on the given device */
+ attach = dma_buf_attach(buf, cb->dev);
+ if (IS_ERR_OR_NULL(attach)) {
+ rc = PTR_ERR(attach) ?: -ENOMEM;
+ pr_err("%s: err: failed to attach dmabuf\n", __func__);
+ goto map_err;
+ }
+
+ /* Get the scatterlist for the given attachment */
+ table = dma_buf_map_attachment(attach, DMA_BIDIRECTIONAL);
+ if (IS_ERR_OR_NULL(table)) {
+ rc = PTR_ERR(table) ?: -ENOMEM;
+ pr_err("%s: err: failed to map table\n", __func__);
+ goto map_table_err;
+ }
+
+ /* Map a scatterlist into an SMMU */
+ rc = msm_dma_map_sg_lazy(cb->dev, table->sgl, table->nents,
+ DMA_BIDIRECTIONAL, buf);
+ if (rc != table->nents) {
+ pr_err(
+ "%s: err: mapping failed with rc(%d), expected rc(%d)\n",
+ __func__, rc, table->nents);
+ rc = -ENOMEM;
+ goto map_sg_err;
+ }
+
+ if (table->sgl) {
+ binfo->ion_buf.iova = table->sgl->dma_address;
+ binfo->ion_buf.mapped_buf_size = sg_dma_len(table->sgl);
+ if (binfo->ion_buf.mapped_buf_size < fd_size) {
+ pr_err("%s: err: mapping failed, size mismatch",
+ __func__);
+ rc = -ENOMEM;
+ goto map_sg_err;
+ }
+
+
+ } else {
+ pr_err("%s: err: sg list is NULL\n", __func__);
+ rc = -ENOMEM;
+ goto map_sg_err;
+ }
+
+ binfo->ion_buf.mapping_info.dev = cb->dev;
+ binfo->ion_buf.mapping_info.mapping = cb->mapping;
+ binfo->ion_buf.mapping_info.table = table;
+ binfo->ion_buf.mapping_info.attach = attach;
+ binfo->ion_buf.mapping_info.buf = buf;
+ binfo->ion_buf.hndl = hndl;
+ } else {
+ pr_err("%s: err: smmu not enabled\n", __func__);
+ rc = -EIO;
+ goto map_err;
+ }
+
+ return 0;
+
+map_sg_err:
+ dma_buf_unmap_attachment(attach, table, DMA_BIDIRECTIONAL);
+map_table_err:
+ dma_buf_detach(buf, attach);
+map_err:
+ if (hndl)
+ ion_free(clnt, hndl);
+import_buf_err:
+ dma_buf_put(buf);
+ return rc;
+}
+
+static int ion_unmap_buffer(struct qcedev_handle *qce_hndl,
+ struct qcedev_reg_buf_info *binfo)
+{
+ struct dma_mapping_info *mapping_info = &binfo->ion_buf.mapping_info;
+ struct qcedev_mem_client *mem_client = qce_hndl->cntl->mem_client;
+
+ if (is_iommu_present(qce_hndl)) {
+ msm_dma_unmap_sg(mapping_info->dev, mapping_info->table->sgl,
+ mapping_info->table->nents, DMA_BIDIRECTIONAL,
+ mapping_info->buf);
+ dma_buf_unmap_attachment(mapping_info->attach,
+ mapping_info->table, DMA_BIDIRECTIONAL);
+ dma_buf_detach(mapping_info->buf, mapping_info->attach);
+ dma_buf_put(mapping_info->buf);
+
+ if (binfo->ion_buf.hndl)
+ ion_free(mem_client->client, binfo->ion_buf.hndl);
+
+ }
+ return 0;
+}
+
+static int qcedev_map_buffer(struct qcedev_handle *qce_hndl,
+ struct qcedev_mem_client *mem_client, int fd,
+ unsigned int fd_size, struct qcedev_reg_buf_info *binfo)
+{
+ int rc = 0;
+
+ switch (mem_client->mtype) {
+ case MEM_ION:
+ rc = ion_map_buffer(qce_hndl, mem_client, fd, fd_size, binfo);
+ break;
+ default:
+ pr_err("%s: err: Mem type not supported\n", __func__);
+ break;
+ }
+
+ if (rc)
+ pr_err("%s: err: failed to map buffer\n", __func__);
+
+ return rc;
+}
+
+static int qcedev_unmap_buffer(struct qcedev_handle *qce_hndl,
+ struct qcedev_mem_client *mem_client,
+ struct qcedev_reg_buf_info *binfo)
+{
+ int rc = 0;
+
+ switch (mem_client->mtype) {
+ case MEM_ION:
+ rc = ion_unmap_buffer(qce_hndl, binfo);
+ break;
+ default:
+ pr_err("%s: err: Mem type not supported\n", __func__);
+ break;
+ }
+
+ if (rc)
+ pr_err("%s: err: failed to unmap buffer\n", __func__);
+
+ return rc;
+}
+
+static bool compare_ion_buffers(struct qcedev_mem_client *mem_client,
+ struct ion_handle *hndl, int fd)
+{
+ bool match = false;
+ struct ion_handle *fd_hndl = NULL;
+ struct dma_buf *dma_buf;
+
+ dma_buf = dma_buf_get(fd);
+ if (IS_ERR_OR_NULL(dma_buf))
+ return false;
+
+ fd_hndl = ion_import_dma_buf(mem_client->client, dma_buf);
+ if (IS_ERR_OR_NULL(fd_hndl)) {
+ match = false;
+ goto err_exit;
+ }
+
+ match = fd_hndl == hndl ? true : false;
+
+ if (fd_hndl)
+ ion_free(mem_client->client, fd_hndl);
+err_exit:
+ dma_buf_put(dma_buf);
+ return match;
+}
+
+int qcedev_check_and_map_buffer(void *handle,
+ int fd, unsigned int offset, unsigned int fd_size,
+ unsigned long long *vaddr)
+{
+ bool found = false;
+ struct qcedev_reg_buf_info *binfo = NULL, *temp = NULL;
+ struct qcedev_mem_client *mem_client = NULL;
+ struct qcedev_handle *qce_hndl = handle;
+ int rc = 0;
+ unsigned long mapped_size = 0;
+
+ if (!handle || !vaddr || fd < 0 || offset >= fd_size) {
+ pr_err("%s: err: invalid input arguments\n", __func__);
+ return -EINVAL;
+ }
+
+ if (!qce_hndl->cntl || !qce_hndl->cntl->mem_client) {
+ pr_err("%s: err: invalid qcedev handle\n", __func__);
+ return -EINVAL;
+ }
+ mem_client = qce_hndl->cntl->mem_client;
+
+ if (mem_client->mtype != MEM_ION)
+ return -EPERM;
+
+ /* Check if the buffer fd is already mapped */
+ mutex_lock(&qce_hndl->registeredbufs.lock);
+ list_for_each_entry(temp, &qce_hndl->registeredbufs.list, list) {
+ found = compare_ion_buffers(mem_client, temp->ion_buf.hndl, fd);
+ if (found) {
+ *vaddr = temp->ion_buf.iova;
+ mapped_size = temp->ion_buf.mapped_buf_size;
+ atomic_inc(&temp->ref_count);
+ break;
+ }
+ }
+ mutex_unlock(&qce_hndl->registeredbufs.lock);
+
+ /* If buffer fd is not mapped then create a fresh mapping */
+ if (!found) {
+ pr_debug("%s: info: ion fd not registered with driver\n",
+ __func__);
+ binfo = kzalloc(sizeof(*binfo), GFP_KERNEL);
+ if (!binfo) {
+ pr_err("%s: err: failed to allocate binfo\n",
+ __func__);
+ rc = -ENOMEM;
+ goto error;
+ }
+ rc = qcedev_map_buffer(qce_hndl, mem_client, fd,
+ fd_size, binfo);
+ if (rc) {
+ pr_err("%s: err: failed to map fd (%d) error = %d\n",
+ __func__, fd, rc);
+ goto error;
+ }
+
+ *vaddr = binfo->ion_buf.iova;
+ mapped_size = binfo->ion_buf.mapped_buf_size;
+ atomic_inc(&binfo->ref_count);
+
+ /* Add buffer mapping information to regd buffer list */
+ mutex_lock(&qce_hndl->registeredbufs.lock);
+ list_add_tail(&binfo->list, &qce_hndl->registeredbufs.list);
+ mutex_unlock(&qce_hndl->registeredbufs.lock);
+ }
+
+ /* Make sure the offset is within the mapped range */
+ if (offset >= mapped_size) {
+ pr_err(
+ "%s: err: Offset (%u) exceeds mapped size(%lu) for fd: %d\n",
+ __func__, offset, mapped_size, fd);
+ rc = -ERANGE;
+ goto unmap;
+ }
+
+ /* return the mapped virtual address adjusted by offset */
+ *vaddr += offset;
+
+ return 0;
+
+unmap:
+ if (!found)
+ qcedev_unmap_buffer(handle, mem_client, binfo);
+
+error:
+ kfree(binfo);
+ return rc;
+}
+
+int qcedev_check_and_unmap_buffer(void *handle, int fd)
+{
+ struct qcedev_reg_buf_info *binfo = NULL, *dummy = NULL;
+ struct qcedev_mem_client *mem_client = NULL;
+ struct qcedev_handle *qce_hndl = handle;
+ bool found = false;
+
+ if (!handle || fd < 0) {
+ pr_err("%s: err: invalid input arguments\n", __func__);
+ return -EINVAL;
+ }
+
+ if (!qce_hndl->cntl || !qce_hndl->cntl->mem_client) {
+ pr_err("%s: err: invalid qcedev handle\n", __func__);
+ return -EINVAL;
+ }
+ mem_client = qce_hndl->cntl->mem_client;
+
+ if (mem_client->mtype != MEM_ION)
+ return -EPERM;
+
+ /* Check if the buffer fd is mapped and present in the regd list. */
+ mutex_lock(&qce_hndl->registeredbufs.lock);
+ list_for_each_entry_safe(binfo, dummy,
+ &qce_hndl->registeredbufs.list, list) {
+
+ found = compare_ion_buffers(mem_client,
+ binfo->ion_buf.hndl, fd);
+ if (found) {
+ atomic_dec(&binfo->ref_count);
+
+ /* Unmap only if there are no more references */
+ if (atomic_read(&binfo->ref_count) == 0) {
+ qcedev_unmap_buffer(qce_hndl,
+ mem_client, binfo);
+ list_del(&binfo->list);
+ kfree(binfo);
+ }
+ break;
+ }
+ }
+ mutex_unlock(&qce_hndl->registeredbufs.lock);
+
+ if (!found) {
+ pr_err("%s: err: calling unmap on unknown fd %d\n",
+ __func__, fd);
+ return -EINVAL;
+ }
+
+ return 0;
+}
diff --git a/drivers/crypto/msm/qcedev_smmu.h b/drivers/crypto/msm/qcedev_smmu.h
new file mode 100644
index 0000000..17e688a
--- /dev/null
+++ b/drivers/crypto/msm/qcedev_smmu.h
@@ -0,0 +1,89 @@
+/* Qti (or) Qualcomm Technologies Inc CE device driver.
+ *
+ * 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 _DRIVERS_CRYPTO_PARSE_H_
+#define _DRIVERS_CRYPTO_PARSE_H_
+
+#include <asm/dma-iommu.h>
+#include <linux/dma-buf.h>
+#include <linux/dma-direction.h>
+#include <linux/iommu.h>
+#include <linux/msm_dma_iommu_mapping.h>
+#include <linux/msm_ion.h>
+#include <linux/of.h>
+#include <linux/of_platform.h>
+#include <linux/slab.h>
+#include <linux/types.h>
+
+struct context_bank_info {
+ struct list_head list;
+ const char *name;
+ u32 buffer_type;
+ u32 start_addr;
+ u32 size;
+ bool is_secure;
+ struct device *dev;
+ struct dma_iommu_mapping *mapping;
+};
+
+enum qcedev_mem_type {
+ MEM_ION,
+};
+
+struct qcedev_mem_client {
+ enum qcedev_mem_type mtype;
+ void *client;
+};
+
+struct dma_mapping_info {
+ struct device *dev;
+ struct dma_iommu_mapping *mapping;
+ struct sg_table *table;
+ struct dma_buf_attachment *attach;
+ struct dma_buf *buf;
+};
+
+struct qcedev_ion_buf_info {
+ struct ion_handle *hndl;
+ struct dma_mapping_info mapping_info;
+ ion_phys_addr_t iova;
+ unsigned long mapped_buf_size;
+};
+
+struct qcedev_reg_buf_info {
+ struct list_head list;
+ union {
+ struct qcedev_ion_buf_info ion_buf;
+ };
+ atomic_t ref_count;
+};
+
+struct qcedev_buffer_list {
+ struct list_head list;
+ struct mutex lock;
+};
+
+int qcedev_parse_context_bank(struct platform_device *pdev);
+struct qcedev_mem_client *qcedev_mem_new_client(enum qcedev_mem_type mtype);
+void qcedev_mem_delete_client(struct qcedev_mem_client *mem_client);
+int qcedev_check_and_map_buffer(void *qce_hndl,
+ int fd, unsigned int offset, unsigned int fd_size,
+ unsigned long long *vaddr);
+int qcedev_check_and_unmap_buffer(void *handle, int fd);
+
+extern struct qcedev_reg_buf_info *global_binfo_in;
+extern struct qcedev_reg_buf_info *global_binfo_out;
+extern struct qcedev_reg_buf_info *global_binfo_res;
+#endif
+
diff --git a/drivers/crypto/msm/qcedevi.h b/drivers/crypto/msm/qcedevi.h
index c26ed71..f99adaf 100644
--- a/drivers/crypto/msm/qcedevi.h
+++ b/drivers/crypto/msm/qcedevi.h
@@ -1,6 +1,6 @@
/* QTI crypto Driver
*
- * Copyright (c) 2014-2017, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2014-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
@@ -21,6 +21,7 @@
#include <linux/platform_data/qcom_crypto_device.h>
#include <linux/fips_status.h>
#include "qce.h"
+#include "qcedev_smmu.h"
#define CACHE_LINE_SIZE 32
#define CE_SHA_BLOCK_SIZE SHA256_BLOCK_SIZE
@@ -107,6 +108,8 @@
struct qcedev_async_req *active_command;
spinlock_t lock;
struct tasklet_struct done_tasklet;
+ struct list_head context_banks;
+ struct qcedev_mem_client *mem_client;
};
struct qcedev_handle {
@@ -114,6 +117,8 @@
struct qcedev_control *cntl;
/* qce internal sha context*/
struct qcedev_sha_ctxt sha_ctxt;
+ /* qcedev mapped buffer list */
+ struct qcedev_buffer_list registeredbufs;
};
void qcedev_cipher_req_cb(void *cookie, unsigned char *icv,
diff --git a/drivers/gpu/msm/adreno-gpulist.h b/drivers/gpu/msm/adreno-gpulist.h
index 6b9d0e7..f7e9eb3 100644
--- a/drivers/gpu/msm/adreno-gpulist.h
+++ b/drivers/gpu/msm/adreno-gpulist.h
@@ -382,7 +382,8 @@
.minor = 5,
.patchid = ANY_ID,
.features = ADRENO_64BIT | ADRENO_RPMH | ADRENO_PREEMPTION |
- ADRENO_GPMU | ADRENO_CONTENT_PROTECTION | ADRENO_IFPC,
+ ADRENO_GPMU | ADRENO_CONTENT_PROTECTION | ADRENO_IFPC |
+ ADRENO_IOCOHERENT,
.sqefw_name = "a630_sqe.fw",
.zap_name = "a615_zap",
.gpudev = &adreno_a6xx_gpudev,
@@ -400,7 +401,8 @@
.minor = 6,
.patchid = ANY_ID,
.features = ADRENO_64BIT | ADRENO_RPMH | ADRENO_PREEMPTION |
- ADRENO_GPMU | ADRENO_CONTENT_PROTECTION | ADRENO_IFPC,
+ ADRENO_GPMU | ADRENO_CONTENT_PROTECTION | ADRENO_IFPC |
+ ADRENO_IOCOHERENT,
.sqefw_name = "a630_sqe.fw",
.zap_name = "a615_zap",
.gpudev = &adreno_a6xx_gpudev,
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/leds/leds-qti-tri-led.c b/drivers/leds/leds-qti-tri-led.c
index f638bc9..c303893 100644
--- a/drivers/leds/leds-qti-tri-led.c
+++ b/drivers/leds/leds-qti-tri-led.c
@@ -53,6 +53,7 @@
u32 off_ms;
enum led_brightness brightness;
bool blink;
+ bool breath;
};
struct qpnp_led_dev {
@@ -66,6 +67,7 @@
const char *default_trigger;
u8 id;
bool blinking;
+ bool breathing;
};
struct qpnp_tri_led_chip {
@@ -119,6 +121,10 @@
pstate.enabled = !!(pwm->duty_ns != 0);
pstate.period = pwm->period_ns;
pstate.duty_cycle = pwm->duty_ns;
+ pstate.output_type = led->led_setting.breath ?
+ PWM_OUTPUT_MODULATED : PWM_OUTPUT_FIXED;
+ /* Use default pattern in PWM device */
+ pstate.output_pattern = NULL;
rc = pwm_apply_state(led->pwm_dev, &pstate);
if (rc < 0)
@@ -183,7 +189,9 @@
/* Use initial period if no blinking is required */
period_ns = led->pwm_setting.pre_period_ns;
- if (period_ns > INT_MAX / brightness)
+ if (brightness == LED_OFF)
+ duty_ns = 0;
+ else if (period_ns > INT_MAX / brightness)
duty_ns = (period_ns / LED_FULL) * brightness;
else
duty_ns = (period_ns * brightness) / LED_FULL;
@@ -207,9 +215,15 @@
if (led->led_setting.blink) {
led->cdev.brightness = LED_FULL;
led->blinking = true;
+ led->breathing = false;
+ } else if (led->led_setting.breath) {
+ led->cdev.brightness = LED_FULL;
+ led->blinking = false;
+ led->breathing = true;
} else {
led->cdev.brightness = led->led_setting.brightness;
led->blinking = false;
+ led->breathing = false;
}
return rc;
@@ -227,7 +241,7 @@
brightness = LED_FULL;
if (brightness == led->led_setting.brightness &&
- !led->blinking) {
+ !led->blinking && !led->breathing) {
mutex_unlock(&led->lock);
return 0;
}
@@ -238,6 +252,7 @@
else
led->led_setting.on_ms = 0;
led->led_setting.blink = false;
+ led->led_setting.breath = false;
rc = qpnp_tri_led_set(led);
if (rc)
@@ -273,14 +288,17 @@
if (*on_ms == 0) {
led->led_setting.blink = false;
+ led->led_setting.breath = false;
led->led_setting.brightness = LED_OFF;
} else if (*off_ms == 0) {
led->led_setting.blink = false;
+ led->led_setting.breath = false;
led->led_setting.brightness = led->cdev.brightness;
} else {
led->led_setting.on_ms = *on_ms;
led->led_setting.off_ms = *off_ms;
led->led_setting.blink = true;
+ led->led_setting.breath = false;
}
rc = qpnp_tri_led_set(led);
@@ -292,6 +310,52 @@
return rc;
}
+static ssize_t breath_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct led_classdev *led_cdev = dev_get_drvdata(dev);
+ struct qpnp_led_dev *led =
+ container_of(led_cdev, struct qpnp_led_dev, cdev);
+
+ return snprintf(buf, PAGE_SIZE, "%d\n", led->led_setting.breath);
+}
+
+static ssize_t breath_store(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ int rc;
+ bool breath;
+ struct led_classdev *led_cdev = dev_get_drvdata(dev);
+ struct qpnp_led_dev *led =
+ container_of(led_cdev, struct qpnp_led_dev, cdev);
+
+ rc = kstrtobool(buf, &breath);
+ if (rc < 0)
+ return rc;
+
+ mutex_lock(&led->lock);
+ if (led->breathing == breath)
+ goto unlock;
+
+ led->led_setting.blink = false;
+ led->led_setting.breath = breath;
+ led->led_setting.brightness = breath ? LED_FULL : LED_OFF;
+ rc = qpnp_tri_led_set(led);
+ if (rc < 0)
+ dev_err(led->chip->dev, "Set led failed for %s, rc=%d\n",
+ led->label, rc);
+
+unlock:
+ mutex_unlock(&led->lock);
+ return (rc < 0) ? rc : count;
+}
+
+static DEVICE_ATTR(breath, 0644, breath_show, breath_store);
+static const struct attribute *breath_attrs[] = {
+ &dev_attr_breath.attr,
+ NULL
+};
+
static int qpnp_tri_led_register(struct qpnp_tri_led_chip *chip)
{
struct qpnp_led_dev *led;
@@ -313,15 +377,30 @@
if (rc < 0) {
dev_err(chip->dev, "%s led class device registering failed, rc=%d\n",
led->label, rc);
- goto destroy;
+ goto err_out;
+ }
+
+ if (pwm_get_output_type_supported(led->pwm_dev)
+ & PWM_OUTPUT_MODULATED) {
+ rc = sysfs_create_files(&led->cdev.dev->kobj,
+ breath_attrs);
+ if (rc < 0) {
+ dev_err(chip->dev, "Create breath file for %s led failed, rc=%d\n",
+ led->label, rc);
+ goto err_out;
+ }
}
}
return 0;
-destroy:
- for (j = 0; j <= i; j++)
- mutex_destroy(&chip->leds[i].lock);
+err_out:
+ for (j = 0; j <= i; j++) {
+ if (j < i)
+ sysfs_remove_files(&chip->leds[j].cdev.dev->kobj,
+ breath_attrs);
+ mutex_destroy(&chip->leds[j].lock);
+ }
return rc;
}
@@ -483,8 +562,10 @@
struct qpnp_tri_led_chip *chip = dev_get_drvdata(&pdev->dev);
mutex_destroy(&chip->bus_lock);
- for (i = 0; i < chip->num_leds; i++)
+ for (i = 0; i < chip->num_leds; i++) {
+ sysfs_remove_files(&chip->leds[i].cdev.dev->kobj, breath_attrs);
mutex_destroy(&chip->leds[i].lock);
+ }
dev_set_drvdata(chip->dev, NULL);
return 0;
}
diff --git a/drivers/media/platform/msm/camera/cam_core/cam_context.h b/drivers/media/platform/msm/camera/cam_core/cam_context.h
index 8324e78..ffceea2 100644
--- a/drivers/media/platform/msm/camera/cam_core/cam_context.h
+++ b/drivers/media/platform/msm/camera/cam_core/cam_context.h
@@ -70,7 +70,7 @@
uint32_t num_in_map_entries;
struct cam_hw_fence_map_entry out_map_entries[CAM_CTX_CFG_MAX];
uint32_t num_out_map_entries;
- uint32_t num_in_acked;
+ atomic_t num_in_acked;
uint32_t num_out_acked;
int flushed;
struct cam_context *ctx;
diff --git a/drivers/media/platform/msm/camera/cam_core/cam_context_utils.c b/drivers/media/platform/msm/camera/cam_core/cam_context_utils.c
index b4f83f7..f167ef7 100644
--- a/drivers/media/platform/msm/camera/cam_core/cam_context_utils.c
+++ b/drivers/media/platform/msm/camera/cam_core/cam_context_utils.c
@@ -189,8 +189,7 @@
return;
}
- req->num_in_acked++;
- if (req->num_in_acked == req->num_in_map_entries) {
+ if (atomic_inc_return(&req->num_in_acked) == req->num_in_map_entries) {
apply.request_id = req->request_id;
/*
* take mutex to ensure that another thread does
@@ -342,6 +341,7 @@
req->num_hw_update_entries = cfg.num_hw_update_entries;
req->num_out_map_entries = cfg.num_out_map_entries;
req->num_in_map_entries = cfg.num_in_map_entries;
+ atomic_set(&req->num_in_acked, 0);
req->request_id = packet->header.request_id;
req->status = 1;
req->req_priv = cfg.priv;
@@ -492,6 +492,7 @@
struct cam_ctx_request *req;
uint32_t i;
int rc = 0;
+ bool free_req;
CAM_DBG(CAM_CTXT, "[%s] E: NRT flush ctx", ctx->dev_name);
@@ -528,6 +529,21 @@
flush_args.flush_req_pending[flush_args.num_req_pending++] =
req->req_priv;
+
+ free_req = false;
+ for (i = 0; i < req->num_in_map_entries; i++) {
+ rc = cam_sync_deregister_callback(
+ cam_context_sync_callback,
+ (void *)req,
+ req->in_map_entries[i].sync_id);
+ if (!rc) {
+ cam_context_putref(ctx);
+ if (atomic_inc_return(&req->num_in_acked) ==
+ req->num_in_map_entries)
+ free_req = true;
+ }
+ }
+
for (i = 0; i < req->num_out_map_entries; i++) {
if (req->out_map_entries[i].sync_id != -1) {
rc = cam_sync_signal(
@@ -543,6 +559,17 @@
}
}
+ /*
+ * If we have deregistered the last sync callback, req will
+ * not be put on the free list. So put it on the free list here
+ */
+ if (free_req) {
+ req->ctx = NULL;
+ spin_lock(&ctx->lock);
+ list_add_tail(&req->list, &ctx->free_req_list);
+ spin_unlock(&ctx->lock);
+ }
+
if (cam_debug_ctx_req_list & ctx->dev_id)
CAM_INFO(CAM_CTXT,
"[%s][%d] : Deleting req[%llu] from temp_list",
@@ -630,6 +657,7 @@
uint32_t i;
int32_t sync_id = 0;
int rc = 0;
+ bool free_req = false;
CAM_DBG(CAM_CTXT, "[%s] E: NRT flush req", ctx->dev_name);
@@ -682,6 +710,22 @@
}
if (req) {
+ if (flush_args.num_req_pending) {
+ for (i = 0; i < req->num_in_map_entries; i++) {
+ rc = cam_sync_deregister_callback(
+ cam_context_sync_callback,
+ (void *)req,
+ req->in_map_entries[i].sync_id);
+ if (rc)
+ continue;
+
+ cam_context_putref(ctx);
+ if (atomic_inc_return(&req->num_in_acked) ==
+ req->num_in_map_entries)
+ free_req = true;
+ }
+ }
+
if (flush_args.num_req_pending || flush_args.num_req_active) {
for (i = 0; i < req->num_out_map_entries; i++) {
sync_id =
@@ -697,17 +741,20 @@
}
}
}
- if (flush_args.num_req_active) {
+ if (flush_args.num_req_active || free_req) {
+ req->ctx = NULL;
spin_lock(&ctx->lock);
list_add_tail(&req->list, &ctx->free_req_list);
spin_unlock(&ctx->lock);
- req->ctx = NULL;
if (cam_debug_ctx_req_list & ctx->dev_id)
CAM_INFO(CAM_CTXT,
- "[%s][%d] : Moving req[%llu] from active_list to free_list",
+ "[%s][%d] : Moving req[%llu] from %s to free_list",
ctx->dev_name, ctx->ctx_id,
- req->request_id);
+ req->request_id,
+ flush_args.num_req_active ?
+ "active_list" :
+ "pending_list");
}
}
}
diff --git a/drivers/media/platform/msm/camera/cam_icp/icp_hw/icp_hw_mgr/cam_icp_hw_mgr.c b/drivers/media/platform/msm/camera/cam_icp/icp_hw/icp_hw_mgr/cam_icp_hw_mgr.c
index 9ed71d2..ba4385f5 100644
--- a/drivers/media/platform/msm/camera/cam_icp/icp_hw/icp_hw_mgr/cam_icp_hw_mgr.c
+++ b/drivers/media/platform/msm/camera/cam_icp/icp_hw/icp_hw_mgr/cam_icp_hw_mgr.c
@@ -57,6 +57,8 @@
#define ICP_DEV_TYPE_TO_CLK_TYPE(dev_type) \
((dev_type == CAM_ICP_RES_TYPE_BPS) ? ICP_CLK_HW_BPS : ICP_CLK_HW_IPE)
+#define ICP_DEVICE_IDLE_TIMEOUT 400
+
static struct cam_icp_hw_mgr icp_hw_mgr;
static int cam_icp_send_ubwc_cfg(struct cam_icp_hw_mgr *hw_mgr)
@@ -549,7 +551,7 @@
for (i = 0; i < ICP_CLK_HW_MAX; i++) {
if (!hw_mgr->clk_info[i].watch_dog) {
rc = crm_timer_init(&hw_mgr->clk_info[i].watch_dog,
- 3000, &hw_mgr->clk_info[i],
+ ICP_DEVICE_IDLE_TIMEOUT, &hw_mgr->clk_info[i],
&cam_icp_device_timer_cb);
if (rc)
diff --git a/drivers/media/platform/msm/camera/cam_isp/cam_isp_context.c b/drivers/media/platform/msm/camera/cam_isp/cam_isp_context.c
index 1106453..97e977d 100644
--- a/drivers/media/platform/msm/camera/cam_isp/cam_isp_context.c
+++ b/drivers/media/platform/msm/camera/cam_isp/cam_isp_context.c
@@ -447,6 +447,33 @@
return rc;
}
+static void __cam_isp_ctx_send_sof_boot_timestamp(
+ struct cam_isp_context *ctx_isp, uint64_t request_id,
+ uint32_t sof_event_status)
+{
+ struct cam_req_mgr_message req_msg;
+
+ req_msg.session_hdl = ctx_isp->base->session_hdl;
+ req_msg.u.frame_msg.frame_id = ctx_isp->frame_id;
+ req_msg.u.frame_msg.request_id = request_id;
+ req_msg.u.frame_msg.timestamp = ctx_isp->boot_timestamp;
+ req_msg.u.frame_msg.link_hdl = ctx_isp->base->link_hdl;
+ req_msg.u.frame_msg.sof_status = sof_event_status;
+
+ CAM_DBG(CAM_ISP,
+ "request id:%lld frame number:%lld boot time stamp:0x%llx",
+ request_id, ctx_isp->frame_id,
+ ctx_isp->boot_timestamp);
+
+ if (cam_req_mgr_notify_message(&req_msg,
+ V4L_EVENT_CAM_REQ_MGR_SOF_BOOT_TS,
+ V4L_EVENT_CAM_REQ_MGR_EVENT))
+ CAM_ERR(CAM_ISP,
+ "Error in notifying the boot time for req id:%lld",
+ request_id);
+}
+
+
static void __cam_isp_ctx_send_sof_timestamp(
struct cam_isp_context *ctx_isp, uint64_t request_id,
uint32_t sof_event_status)
@@ -464,13 +491,17 @@
"request id:%lld frame number:%lld SOF time stamp:0x%llx",
request_id, ctx_isp->frame_id,
ctx_isp->sof_timestamp_val);
- CAM_DBG(CAM_ISP, " sof status:%d", sof_event_status);
+ CAM_DBG(CAM_ISP, "sof status:%d", sof_event_status);
if (cam_req_mgr_notify_message(&req_msg,
V4L_EVENT_CAM_REQ_MGR_SOF, V4L_EVENT_CAM_REQ_MGR_EVENT))
CAM_ERR(CAM_ISP,
"Error in notifying the sof time for req id:%lld",
request_id);
+
+ __cam_isp_ctx_send_sof_boot_timestamp(ctx_isp,
+ request_id, sof_event_status);
+
}
static int __cam_isp_ctx_reg_upd_in_activated_state(
@@ -617,6 +648,7 @@
ctx_isp->frame_id++;
ctx_isp->sof_timestamp_val = sof_event_data->timestamp;
+ ctx_isp->boot_timestamp = sof_event_data->boot_time;
__cam_isp_ctx_update_state_monitor_array(ctx_isp,
CAM_ISP_STATE_CHANGE_TRIGGER_SOF, req->request_id);
CAM_DBG(CAM_ISP, "frame id: %lld time stamp:0x%llx",
@@ -773,6 +805,7 @@
ctx_isp->frame_id++;
ctx_isp->sof_timestamp_val = sof_event_data->timestamp;
+ ctx_isp->boot_timestamp = sof_event_data->boot_time;
if (list_empty(&ctx->active_req_list))
ctx_isp->substate_activated = CAM_ISP_CTX_ACTIVATED_SOF;
@@ -1045,6 +1078,7 @@
}
ctx_isp->frame_id++;
ctx_isp->sof_timestamp_val = sof_event_data->timestamp;
+ ctx_isp->boot_timestamp = sof_event_data->boot_time;
CAM_DBG(CAM_ISP, "frame id: %lld time stamp:0x%llx",
ctx_isp->frame_id, ctx_isp->sof_timestamp_val);
@@ -1470,6 +1504,8 @@
ctx_isp->frame_id++;
ctx_isp->sof_timestamp_val = sof_event_data->timestamp;
+ ctx_isp->boot_timestamp = sof_event_data->boot_time;
+
CAM_DBG(CAM_ISP, "frame id: %lld time stamp:0x%llx",
ctx_isp->frame_id, ctx_isp->sof_timestamp_val);
@@ -1523,6 +1559,7 @@
ctx_isp->frame_id++;
ctx_isp->sof_timestamp_val = sof_event_data->timestamp;
+ ctx_isp->boot_timestamp = sof_event_data->boot_time;
CAM_DBG(CAM_ISP, "frame id: %lld time stamp:0x%llx",
ctx_isp->frame_id, ctx_isp->sof_timestamp_val);
@@ -1554,6 +1591,7 @@
ctx_isp->frame_id++;
ctx_isp->sof_timestamp_val = sof_event_data->timestamp;
+ ctx_isp->boot_timestamp = sof_event_data->boot_time;
CAM_DBG(CAM_ISP, "frame id: %lld time stamp:0x%llx",
ctx_isp->frame_id, ctx_isp->sof_timestamp_val);
@@ -1639,6 +1677,7 @@
ctx_isp->frame_id++;
ctx_isp->sof_timestamp_val = sof_event_data->timestamp;
+ ctx_isp->boot_timestamp = sof_event_data->boot_time;
CAM_DBG(CAM_ISP, "frame id: %lld time stamp:0x%llx",
ctx_isp->frame_id, ctx_isp->sof_timestamp_val);
/*
@@ -1701,6 +1740,7 @@
struct cam_req_mgr_trigger_notify notify;
uint64_t request_id = 0;
+ ctx_isp->substate_activated = CAM_ISP_CTX_ACTIVATED_EPOCH;
/* notify reqmgr with sof signal*/
if (ctx->ctx_crm_intf && ctx->ctx_crm_intf->notify_trigger) {
if (list_empty(&ctx->pending_req_list)) {
@@ -1712,13 +1752,19 @@
list_del_init(&req->list);
req_isp = (struct cam_isp_ctx_req *) req->req_priv;
- request_id = req->request_id;
+ request_id =
+ (req_isp->hw_update_data.packet_opcode_type ==
+ CAM_ISP_PACKET_INIT_DEV) ?
+ 0 : req->request_id;
+
if (req_isp->num_fence_map_out != 0) {
list_add_tail(&req->list, &ctx->active_req_list);
ctx_isp->active_req_cnt++;
CAM_DBG(CAM_ISP,
"move request %lld to active list(cnt = %d)",
req->request_id, ctx_isp->active_req_cnt);
+ /* if packet has buffers, set correct request id */
+ request_id = req->request_id;
} else {
/* no io config, so the request is completed. */
list_add_tail(&req->list, &ctx->free_req_list);
@@ -1738,10 +1784,11 @@
} else {
CAM_ERR(CAM_ISP, "Can not notify SOF to CRM");
}
+ if (request_id)
+ ctx_isp->reported_req_id = request_id;
+
__cam_isp_ctx_send_sof_timestamp(ctx_isp, request_id,
CAM_REQ_MGR_SOF_EVENT_SUCCESS);
-
- ctx_isp->substate_activated = CAM_ISP_CTX_ACTIVATED_EPOCH;
CAM_DBG(CAM_ISP, "next substate %d", ctx_isp->substate_activated);
return 0;
@@ -2042,7 +2089,7 @@
req->request_id = packet->header.request_id;
req->status = 1;
- CAM_DBG(CAM_ISP, "Packet request id 0x%llx packet opcode:%d",
+ CAM_DBG(CAM_ISP, "Packet request id %lld packet opcode:%d",
packet->header.request_id,
req_isp->hw_update_data.packet_opcode_type);
@@ -2185,6 +2232,7 @@
cam_isp_ctx_activated_state_machine;
}
+ ctx_isp->rdi_only_context = hw_cmd_args.u.is_rdi_only_context;
ctx_isp->hw_ctx = param.ctxt_to_hw_map;
req_hdl_param.session_hdl = cmd->session_handle;
@@ -2330,7 +2378,8 @@
ctx_isp->frame_id = 0;
ctx_isp->active_req_cnt = 0;
ctx_isp->reported_req_id = 0;
- ctx_isp->substate_activated = CAM_ISP_CTX_ACTIVATED_SOF;
+ ctx_isp->substate_activated = ctx_isp->rdi_only_context ?
+ CAM_ISP_CTX_ACTIVATED_APPLIED : CAM_ISP_CTX_ACTIVATED_SOF;
/*
* Only place to change state before calling the hw due to
diff --git a/drivers/media/platform/msm/camera/cam_isp/cam_isp_context.h b/drivers/media/platform/msm/camera/cam_isp/cam_isp_context.h
index 1eae89f..a939f2d 100644
--- a/drivers/media/platform/msm/camera/cam_isp/cam_isp_context.h
+++ b/drivers/media/platform/msm/camera/cam_isp/cam_isp_context.h
@@ -149,6 +149,7 @@
* @req_isp: ISP private request object storage
* @hw_ctx: HW object returned by the acquire device command
* @sof_timestamp_val: Captured time stamp value at sof hw event
+ * @boot_timestamp: Boot time stamp for a given req_id
* @active_req_cnt: Counter for the active request
* @reported_req_id: Last reported request id
* @subscribe_event: The irq event mask that CRM subscribes to, IFE
@@ -157,6 +158,8 @@
* @frame_skip_count: Number of frame to skip before change state
* @state_monitor_head: Write index to the state monitoring array
* @cam_isp_ctx_state_monitor: State monitoring array
+ * @rdi_only_context: Get context type information.
+ * true, if context is rdi only context
*
*/
struct cam_isp_context {
@@ -172,6 +175,7 @@
void *hw_ctx;
uint64_t sof_timestamp_val;
+ uint64_t boot_timestamp;
int32_t active_req_cnt;
int64_t reported_req_id;
uint32_t subscribe_event;
@@ -180,6 +184,7 @@
atomic64_t state_monitor_head;
struct cam_isp_context_state_monitor cam_isp_ctx_state_monitor[
CAM_ISP_CTX_STATE_MONITOR_MAX_ENTRIES];
+ bool rdi_only_context;
};
/**
diff --git a/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/cam_ife_hw_mgr.c b/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/cam_ife_hw_mgr.c
index 6e140e2..0a127ef 100644
--- a/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/cam_ife_hw_mgr.c
+++ b/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/cam_ife_hw_mgr.c
@@ -1418,7 +1418,7 @@
in_port = memdup_user((void __user *)isp_resource[i].res_hdl,
isp_resource[i].length);
- if (in_port > 0) {
+ if (!IS_ERR(in_port)) {
rc = cam_ife_mgr_acquire_hw_for_ctx(ife_ctx, in_port,
&num_pix_port_per_in, &num_rdi_port_per_in);
total_pix_port += num_pix_port_per_in;
@@ -1611,6 +1611,9 @@
cdm_cmd->cmd[i].len = cmd->len;
}
+ if (cfg->request_id == 1)
+ init_completion(&ctx->config_done_complete);
+
CAM_DBG(CAM_ISP, "Submit to CDM");
rc = cam_cdm_submit_bls(ctx->cdm_handle, cdm_cmd);
if (rc) {
@@ -1619,7 +1622,6 @@
}
if (cfg->request_id == 1) {
- init_completion(&ctx->config_done_complete);
rc = wait_for_completion_timeout(
&ctx->config_done_complete,
msecs_to_jiffies(30));
@@ -2675,7 +2677,8 @@
static int cam_ife_mgr_cmd_get_sof_timestamp(
struct cam_ife_hw_mgr_ctx *ife_ctx,
- uint64_t *time_stamp)
+ uint64_t *time_stamp,
+ uint64_t *boot_time_stamp)
{
int rc = -EINVAL;
uint32_t i;
@@ -2704,9 +2707,12 @@
&csid_get_time,
sizeof(
struct cam_csid_get_time_stamp_args));
- if (!rc)
+ if (!rc) {
*time_stamp =
csid_get_time.time_stamp_val;
+ *boot_time_stamp =
+ csid_get_time.boot_timestamp;
+ }
/*
* Single VFE case, Get the time stamp from available
* one csid hw in the context
@@ -3537,7 +3543,8 @@
if (!sof_status && !sof_sent) {
cam_ife_mgr_cmd_get_sof_timestamp(
ife_hw_mgr_ctx,
- &sof_done_event_data.timestamp);
+ &sof_done_event_data.timestamp,
+ &sof_done_event_data.boot_time);
ife_hw_irq_sof_cb(
ife_hw_mgr_ctx->common.cb_priv,
@@ -3558,7 +3565,8 @@
if (!sof_status && !sof_sent) {
cam_ife_mgr_cmd_get_sof_timestamp(
ife_hw_mgr_ctx,
- &sof_done_event_data.timestamp);
+ &sof_done_event_data.timestamp,
+ &sof_done_event_data.boot_time);
ife_hw_irq_sof_cb(
ife_hw_mgr_ctx->common.cb_priv,
@@ -4172,6 +4180,8 @@
g_ife_hw_mgr.ctx_pool[i].common.tasklet_info =
g_ife_hw_mgr.mgr_common.tasklet_pool[i];
+
+ init_completion(&g_ife_hw_mgr.ctx_pool[i].config_done_complete);
list_add_tail(&g_ife_hw_mgr.ctx_pool[i].list,
&g_ife_hw_mgr.free_ctx_list);
}
diff --git a/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/include/cam_isp_hw_mgr_intf.h b/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/include/cam_isp_hw_mgr_intf.h
index 2601190..5410858 100644
--- a/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/include/cam_isp_hw_mgr_intf.h
+++ b/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/include/cam_isp_hw_mgr_intf.h
@@ -108,11 +108,13 @@
/**
* struct cam_isp_hw_sof_event_data - Event payload for CAM_HW_EVENT_SOF
*
- * @timestamp: Time stamp for the sof event
+ * @timestamp: Time stamp for the sof event
+ * @boot_time: Boot time stamp for the sof event
*
*/
struct cam_isp_hw_sof_event_data {
uint64_t timestamp;
+ uint64_t boot_time;
};
/**
diff --git a/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/ife_csid_hw/cam_ife_csid_core.c b/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/ife_csid_hw/cam_ife_csid_core.c
index 34f8c41..053eb00 100644
--- a/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/ife_csid_hw/cam_ife_csid_core.c
+++ b/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/ife_csid_hw/cam_ife_csid_core.c
@@ -856,7 +856,27 @@
path_data->height = reserve->in_port->height;
path_data->start_line = reserve->in_port->line_start;
path_data->end_line = reserve->in_port->line_stop;
- path_data->crop_enable = true;
+
+ /* Enable RDI crop for single ife use case only */
+ switch (reserve->res_id) {
+ case CAM_IFE_PIX_PATH_RES_RDI_0:
+ case CAM_IFE_PIX_PATH_RES_RDI_1:
+ case CAM_IFE_PIX_PATH_RES_RDI_2:
+ case CAM_IFE_PIX_PATH_RES_RDI_3:
+ if (reserve->in_port->usage_type)
+ path_data->crop_enable = false;
+ else
+ path_data->crop_enable = true;
+
+ break;
+ case CAM_IFE_PIX_PATH_RES_IPP:
+ path_data->crop_enable = true;
+ break;
+ default:
+ rc = -EINVAL;
+ goto end;
+ }
+
CAM_DBG(CAM_ISP, "Res id: %d height:%d line_start %d line_stop %d",
reserve->res_id, reserve->in_port->height,
reserve->in_port->line_start, reserve->in_port->line_stop);
@@ -1884,6 +1904,7 @@
struct cam_ife_csid_reg_offset *csid_reg;
struct cam_hw_soc_info *soc_info;
struct cam_ife_csid_rdi_reg_offset *rdi_reg;
+ struct timespec64 ts;
uint32_t time_32, id;
time_stamp = (struct cam_csid_get_time_stamp_args *)cmd_args;
@@ -1932,6 +1953,10 @@
CAM_IFE_CSID_QTIMER_MUL_FACTOR,
CAM_IFE_CSID_QTIMER_DIV_FACTOR);
+ get_monotonic_boottime64(&ts);
+ time_stamp->boot_timestamp = (uint64_t)((ts.tv_sec * 1000000000) +
+ ts.tv_nsec);
+
return 0;
}
diff --git a/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/include/cam_ife_csid_hw_intf.h b/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/include/cam_ife_csid_hw_intf.h
index ceeacbe..8911f99 100644
--- a/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/include/cam_ife_csid_hw_intf.h
+++ b/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/include/cam_ife_csid_hw_intf.h
@@ -138,12 +138,13 @@
/**
* struct cam_csid_get_time_stamp_args- time stamp capture arguments
* @res_node : resource to get the time stamp
- * @ time_stamp_val : captured time stamp
- *
+ * @time_stamp_val : captured time stamp
+ * @boot_timestamp : boot time stamp
*/
struct cam_csid_get_time_stamp_args {
struct cam_isp_resource_node *node_res;
uint64_t time_stamp_val;
+ uint64_t boot_timestamp;
};
/**
diff --git a/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/vfe_bus/cam_vfe_bus_ver2.c b/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/vfe_bus/cam_vfe_bus_ver2.c
index 969b75a..be4db8a 100644
--- a/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/vfe_bus/cam_vfe_bus_ver2.c
+++ b/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/vfe_bus/cam_vfe_bus_ver2.c
@@ -863,34 +863,16 @@
rsrc_data->stride = rsrc_data->width;
break;
case CAM_FORMAT_PLAIN16_10:
+ case CAM_FORMAT_PLAIN16_12:
+ case CAM_FORMAT_PLAIN16_14:
+ case CAM_FORMAT_PLAIN16_16:
+ case CAM_FORMAT_PLAIN32_20:
rsrc_data->width = CAM_VFE_RDI_BUS_DEFAULT_WIDTH;
rsrc_data->height = 0;
rsrc_data->stride = CAM_VFE_RDI_BUS_DEFAULT_STRIDE;
rsrc_data->pack_fmt = 0x0;
rsrc_data->en_cfg = 0x3;
break;
- case CAM_FORMAT_PLAIN16_12:
- rsrc_data->en_cfg = 0x1;
- rsrc_data->pack_fmt = 0x3;
- rsrc_data->width = rsrc_data->width * 2;
- rsrc_data->stride = rsrc_data->width;
- break;
- case CAM_FORMAT_PLAIN16_14:
- rsrc_data->en_cfg = 0x1;
- rsrc_data->pack_fmt = 0x4;
- rsrc_data->width = rsrc_data->width * 2;
- rsrc_data->stride = rsrc_data->width;
- break;
- case CAM_FORMAT_PLAIN16_16:
- rsrc_data->en_cfg = 0x1;
- rsrc_data->pack_fmt = 0x5;
- rsrc_data->width = rsrc_data->width * 2;
- rsrc_data->stride = rsrc_data->width;
- break;
- case CAM_FORMAT_PLAIN32_20:
- rsrc_data->en_cfg = 0x1;
- rsrc_data->pack_fmt = 0x9;
- break;
case CAM_FORMAT_PLAIN64:
rsrc_data->en_cfg = 0x1;
rsrc_data->pack_fmt = 0xA;
diff --git a/drivers/media/platform/msm/camera/cam_req_mgr/cam_mem_mgr.c b/drivers/media/platform/msm/camera/cam_req_mgr/cam_mem_mgr.c
index 643b0afc..93e4249 100644
--- a/drivers/media/platform/msm/camera/cam_req_mgr/cam_mem_mgr.c
+++ b/drivers/media/platform/msm/camera/cam_req_mgr/cam_mem_mgr.c
@@ -646,6 +646,10 @@
CAM_SMMU_REGION_IO);
if (rc)
goto map_fail;
+ } else {
+ rc = ion_handle_get_size(tbl.client, ion_hdl, &len);
+ if (rc)
+ return rc;
}
idx = cam_mem_get_slot();
diff --git a/drivers/media/platform/msm/camera/cam_sync/cam_sync.c b/drivers/media/platform/msm/camera/cam_sync/cam_sync.c
index d625a20..517b7df 100644
--- a/drivers/media/platform/msm/camera/cam_sync/cam_sync.c
+++ b/drivers/media/platform/msm/camera/cam_sync/cam_sync.c
@@ -123,6 +123,7 @@
{
struct sync_table_row *row = NULL;
struct sync_callback_info *sync_cb, *temp;
+ bool found = false;
if (sync_obj >= CAM_SYNC_MAX_OBJS || sync_obj <= 0)
return -EINVAL;
@@ -145,11 +146,12 @@
sync_cb->cb_data == userdata) {
list_del_init(&sync_cb->list);
kfree(sync_cb);
+ found = true;
}
}
spin_unlock_bh(&sync_dev->row_spinlocks[sync_obj]);
- return 0;
+ return found ? 0 : -ENOENT;
}
int cam_sync_signal(int32_t sync_obj, uint32_t status)
diff --git a/drivers/misc/qpnp-misc.c b/drivers/misc/qpnp-misc.c
index 690b28b..eec0875 100644
--- a/drivers/misc/qpnp-misc.c
+++ b/drivers/misc/qpnp-misc.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2013-2014,2016-2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2013-2014,2016-2018, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -17,6 +17,7 @@
#include <linux/regmap.h>
#include <linux/of.h>
#include <linux/of_device.h>
+#include <linux/notifier.h>
#include <linux/qpnp/qpnp-misc.h>
#define QPNP_MISC_DEV_NAME "qcom,qpnp-misc"
@@ -29,8 +30,20 @@
#define PWM_SEL_MAX 0x03
#define GP_DRIVER_EN_BIT BIT(0)
+enum twm {
+ TWM_MODE_1 = 1,
+ TWM_MODE_2,
+ TWM_MODE_3,
+};
+
+enum twm_attrib {
+ TWM_ENABLE,
+ TWM_EXIT,
+};
+
static DEFINE_MUTEX(qpnp_misc_dev_list_mutex);
static LIST_HEAD(qpnp_misc_dev_list);
+static RAW_NOTIFIER_HEAD(twm_notifier);
struct qpnp_misc_version {
u8 subtype;
@@ -55,10 +68,14 @@
struct device *dev;
struct regmap *regmap;
struct qpnp_misc_version version;
+ struct class twm_class;
+ u8 twm_mode;
u32 base;
u8 pwm_sel;
bool enable_gp_driver;
+ bool support_twm_config;
+ bool twm_enable;
};
static const struct of_device_id qpnp_misc_match_table[] = {
@@ -211,12 +228,100 @@
return __misc_irqs_available(mdev_found);
}
+#define MISC_SPARE_1 0x50
+#define MISC_SPARE_2 0x51
+#define ENABLE_TWM_MODE 0x80
+#define DISABLE_TWM_MODE 0x0
+#define TWM_EXIT_BIT BIT(0)
+static ssize_t twm_enable_store(struct class *c,
+ struct class_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct qpnp_misc_dev *mdev = container_of(c,
+ struct qpnp_misc_dev, twm_class);
+ u8 val = 0;
+ ssize_t rc = 0;
+
+ rc = kstrtou8(buf, 10, &val);
+ if (rc < 0)
+ return rc;
+
+ mdev->twm_enable = val ? true : false;
+
+ /* Notify the TWM state */
+ raw_notifier_call_chain(&twm_notifier,
+ mdev->twm_enable ? PMIC_TWM_ENABLE : PMIC_TWM_CLEAR, NULL);
+
+ return count;
+}
+
+static ssize_t twm_enable_show(struct class *c,
+ struct class_attribute *attr, char *buf)
+{
+ struct qpnp_misc_dev *mdev = container_of(c,
+ struct qpnp_misc_dev, twm_class);
+
+ return snprintf(buf, PAGE_SIZE, "%d\n", mdev->twm_enable);
+}
+
+static ssize_t twm_exit_show(struct class *c,
+ struct class_attribute *attr, char *buf)
+{
+ struct qpnp_misc_dev *mdev = container_of(c,
+ struct qpnp_misc_dev, twm_class);
+ int rc = 0;
+ u8 val = 0;
+
+ rc = qpnp_read_byte(mdev, MISC_SPARE_1, &val);
+ if (rc < 0) {
+ pr_err("Failed to read TWM enable (misc_spare_1) rc=%d\n", rc);
+ return rc;
+ }
+
+ pr_debug("TWM_EXIT (misc_spare_1) register = 0x%02x\n", val);
+
+ return snprintf(buf, PAGE_SIZE, "%d\n", !!(val & TWM_EXIT_BIT));
+}
+
+static struct class_attribute twm_attributes[] = {
+ [TWM_ENABLE] = __ATTR(twm_enable, 0644,
+ twm_enable_show, twm_enable_store),
+ [TWM_EXIT] = __ATTR(twm_exit, 0644,
+ twm_exit_show, NULL),
+ __ATTR_NULL,
+};
+
+int qpnp_misc_twm_notifier_register(struct notifier_block *nb)
+{
+ return raw_notifier_chain_register(&twm_notifier, nb);
+}
+EXPORT_SYMBOL(qpnp_misc_twm_notifier_register);
+
+int qpnp_misc_twm_notifier_unregister(struct notifier_block *nb)
+{
+ return raw_notifier_chain_unregister(&twm_notifier, nb);
+}
+EXPORT_SYMBOL(qpnp_misc_twm_notifier_unregister);
+
static int qpnp_misc_dt_init(struct qpnp_misc_dev *mdev)
{
struct device_node *node = mdev->dev->of_node;
u32 val;
int rc;
+ if (of_property_read_bool(mdev->dev->of_node,
+ "qcom,support-twm-config")) {
+ mdev->support_twm_config = true;
+ mdev->twm_mode = TWM_MODE_3;
+ rc = of_property_read_u8(mdev->dev->of_node, "qcom,twm-mode",
+ &mdev->twm_mode);
+ if (!rc && (mdev->twm_mode < TWM_MODE_1 ||
+ mdev->twm_mode > TWM_MODE_3)) {
+ pr_err("Invalid TWM mode %d\n", mdev->twm_mode);
+ return -EINVAL;
+ }
+ }
+
rc = of_property_read_u32(node, "reg", &mdev->base);
if (rc < 0 || !mdev->base) {
dev_err(mdev->dev, "Base address not defined or invalid\n");
@@ -270,6 +375,18 @@
break;
}
+ if (mdev->support_twm_config) {
+ mdev->twm_class.name = "pmic_twm",
+ mdev->twm_class.owner = THIS_MODULE,
+ mdev->twm_class.class_attrs = twm_attributes;
+
+ rc = class_register(&mdev->twm_class);
+ if (rc < 0) {
+ pr_err("Failed to register pmic_twm class rc=%d\n", rc);
+ return rc;
+ }
+ }
+
return 0;
}
@@ -283,6 +400,7 @@
return -ENOMEM;
mdev->dev = &pdev->dev;
+ dev_set_drvdata(&pdev->dev, mdev);
mdev->regmap = dev_get_regmap(mdev->dev->parent, NULL);
if (!mdev->regmap) {
dev_err(mdev->dev, "Parent regmap is unavailable\n");
@@ -325,8 +443,33 @@
return 0;
}
+static void qpnp_misc_shutdown(struct platform_device *pdev)
+{
+ struct qpnp_misc_dev *mdev = dev_get_drvdata(&pdev->dev);
+ int rc;
+
+ if (mdev->support_twm_config) {
+ rc = qpnp_write_byte(mdev, MISC_SPARE_2,
+ mdev->twm_enable ? mdev->twm_mode : 0x0);
+ if (rc < 0)
+ pr_err("Failed to write MISC_SPARE_2 (twm_mode) val=%d rc=%d\n",
+ mdev->twm_enable ? mdev->twm_mode : 0x0, rc);
+
+ rc = qpnp_write_byte(mdev, MISC_SPARE_1,
+ mdev->twm_enable ? ENABLE_TWM_MODE : 0x0);
+ if (rc < 0)
+ pr_err("Failed to write MISC_SPARE_1 (twm_state) val=%d rc=%d\n",
+ mdev->twm_enable ? ENABLE_TWM_MODE : 0x0, rc);
+
+ pr_debug("PMIC configured for TWM-%s MODE=%d\n",
+ mdev->twm_enable ? "enabled" : "disabled",
+ mdev->twm_mode);
+ }
+}
+
static struct platform_driver qpnp_misc_driver = {
.probe = qpnp_misc_probe,
+ .shutdown = qpnp_misc_shutdown,
.driver = {
.name = QPNP_MISC_DEV_NAME,
.owner = THIS_MODULE,
diff --git a/drivers/net/wireless/cnss2/debug.c b/drivers/net/wireless/cnss2/debug.c
index a3cf6c2..b1fbbd8 100644
--- a/drivers/net/wireless/cnss2/debug.c
+++ b/drivers/net/wireless/cnss2/debug.c
@@ -137,11 +137,17 @@
{
struct cnss_plat_data *plat_priv =
((struct seq_file *)fp->private_data)->private;
+ struct cnss_pci_data *pci_priv;
char buf[64];
char *cmd;
unsigned int len = 0;
int ret = 0;
+ if (!plat_priv)
+ return -ENODEV;
+
+ pci_priv = plat_priv->bus_priv;
+
len = min(count, sizeof(buf) - 1);
if (copy_from_user(buf, user_buf, len))
return -EFAULT;
@@ -157,11 +163,11 @@
ret = cnss_pci_init(plat_priv);
} else if (sysfs_streq(cmd, "download")) {
set_bit(CNSS_DRIVER_DEBUG, &plat_priv->driver_state);
- ret = cnss_pci_start_mhi(plat_priv->bus_priv);
+ ret = cnss_pci_start_mhi(pci_priv);
} else if (sysfs_streq(cmd, "linkup")) {
- ret = cnss_resume_pci_link(plat_priv->bus_priv);
+ ret = cnss_resume_pci_link(pci_priv);
} else if (sysfs_streq(cmd, "linkdown")) {
- ret = cnss_suspend_pci_link(plat_priv->bus_priv);
+ ret = cnss_suspend_pci_link(pci_priv);
} else if (sysfs_streq(cmd, "powerup")) {
set_bit(CNSS_DRIVER_DEBUG, &plat_priv->driver_state);
ret = cnss_driver_event_post(plat_priv,
@@ -172,6 +178,8 @@
CNSS_DRIVER_EVENT_POWER_DOWN,
0, NULL);
clear_bit(CNSS_DRIVER_DEBUG, &plat_priv->driver_state);
+ } else if (sysfs_streq(cmd, "assert")) {
+ ret = cnss_force_fw_assert(&pci_priv->pci_dev->dev);
} else {
cnss_pr_err("Device boot debugfs command is invalid\n");
ret = -EINVAL;
@@ -195,6 +203,7 @@
seq_puts(s, "linkdown: bring down PCIe link\n");
seq_puts(s, "powerup: full power on sequence to boot device, download FW and do QMI handshake with FW\n");
seq_puts(s, "shutdown: full power off sequence to shutdown device\n");
+ seq_puts(s, "assert: trigger firmware assert\n");
return 0;
}
diff --git a/drivers/net/wireless/cnss2/pci.c b/drivers/net/wireless/cnss2/pci.c
index 098631e..9d1fbf9 100644
--- a/drivers/net/wireless/cnss2/pci.c
+++ b/drivers/net/wireless/cnss2/pci.c
@@ -1458,8 +1458,7 @@
ret = mhi_pm_resume(pci_priv->mhi_ctrl);
break;
case CNSS_MHI_TRIGGER_RDDM:
- cnss_pr_dbg("Bypass MHI state: %s(%d)\n",
- cnss_mhi_state_to_str(mhi_state), mhi_state);
+ ret = mhi_force_rddm_mode(pci_priv->mhi_ctrl);
break;
default:
cnss_pr_err("Unhandled MHI state (%d)\n", mhi_state);
diff --git a/drivers/platform/msm/ep_pcie/ep_pcie_core.c b/drivers/platform/msm/ep_pcie/ep_pcie_core.c
index db6f689..4558530 100644
--- a/drivers/platform/msm/ep_pcie/ep_pcie_core.c
+++ b/drivers/platform/msm/ep_pcie/ep_pcie_core.c
@@ -526,12 +526,14 @@
ep_pcie_write_reg(dev->parf, PCIE20_PARF_DEVICE_TYPE, 0x0);
/* adjust DBI base address */
- if (dev->dbi_base_reg)
- writel_relaxed(0x3FFFE000,
- dev->parf + dev->dbi_base_reg);
- else
- writel_relaxed(0x3FFFE000,
- dev->parf + PCIE20_PARF_DBI_BASE_ADDR);
+ if (dev->phy_rev < 6) {
+ if (dev->dbi_base_reg)
+ writel_relaxed(0x3FFFE000,
+ dev->parf + dev->dbi_base_reg);
+ else
+ writel_relaxed(0x3FFFE000,
+ dev->parf + PCIE20_PARF_DBI_BASE_ADDR);
+ }
/* Configure PCIe core to support 1GB aperture */
if (dev->slv_space_reg)
diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa.c b/drivers/platform/msm/ipa/ipa_v3/ipa.c
index da60ff5..065f97f 100644
--- a/drivers/platform/msm/ipa/ipa_v3/ipa.c
+++ b/drivers/platform/msm/ipa/ipa_v3/ipa.c
@@ -373,7 +373,7 @@
{
ipa3_active_clients_log_print_table(active_clients_table_buf,
IPA3_ACTIVE_CLIENTS_TABLE_BUF_SIZE);
- IPAERR("%s", active_clients_table_buf);
+ IPAERR("%s\n", active_clients_table_buf);
return NOTIFY_DONE;
}
@@ -4280,6 +4280,9 @@
if (res)
IPAERR("uC panic handler failed %d\n", res);
+ if (atomic_read(&ipa3_ctx->ipa3_active_clients.cnt) != 0)
+ ipahal_print_all_regs();
+
return NOTIFY_DONE;
}
diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_debugfs.c b/drivers/platform/msm/ipa/ipa_v3/ipa_debugfs.c
index 07773eb..ee9c49c 100644
--- a/drivers/platform/msm/ipa/ipa_v3/ipa_debugfs.c
+++ b/drivers/platform/msm/ipa/ipa_v3/ipa_debugfs.c
@@ -1894,6 +1894,16 @@
return simple_read_from_buffer(ubuf, count, ppos, dbg_buff, cnt);
}
+static ssize_t ipa3_read_ipahal_regs(struct file *file, char __user *ubuf,
+ size_t count, loff_t *ppos)
+{
+ IPA_ACTIVE_CLIENTS_INC_SIMPLE();
+ ipahal_print_all_regs();
+ IPA_ACTIVE_CLIENTS_DEC_SIMPLE();
+
+ return 0;
+}
+
static void ipa_dump_status(struct ipahal_pkt_status *status)
{
IPA_DUMP_STATUS_FIELD(status_opcode);
@@ -2160,6 +2170,10 @@
"enable_low_prio_print", IPA_WRITE_ONLY_MODE, NULL, {
.write = ipa3_enable_ipc_low,
}
+ }, {
+ "ipa_dump_regs", IPA_READ_ONLY_MODE, NULL, {
+ .read = ipa3_read_ipahal_regs,
+ }
}
};
diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_i.h b/drivers/platform/msm/ipa/ipa_v3/ipa_i.h
index 386ad51..6f87ece 100644
--- a/drivers/platform/msm/ipa/ipa_v3/ipa_i.h
+++ b/drivers/platform/msm/ipa/ipa_v3/ipa_i.h
@@ -2116,6 +2116,7 @@
int __ipa3_release_hdr_proc_ctx(u32 proc_ctx_hdl);
int _ipa_read_ep_reg_v3_0(char *buf, int max_len, int pipe);
int _ipa_read_ep_reg_v4_0(char *buf, int max_len, int pipe);
+int _ipa_read_ipahal_regs(void);
void _ipa_enable_clks_v3_0(void);
void _ipa_disable_clks_v3_0(void);
struct device *ipa3_get_dma_dev(void);
diff --git a/drivers/platform/msm/ipa/ipa_v3/ipahal/ipahal_i.h b/drivers/platform/msm/ipa/ipa_v3/ipahal/ipahal_i.h
index 8f78d56..26b7f0f 100644
--- a/drivers/platform/msm/ipa/ipa_v3/ipahal/ipahal_i.h
+++ b/drivers/platform/msm/ipa/ipa_v3/ipahal/ipahal_i.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
@@ -46,6 +46,15 @@
IPAHAL_DRV_NAME " %s:%d " fmt, ## args); \
} while (0)
+#define IPAHAL_DBG_REG(fmt, args...) \
+ do { \
+ pr_err(fmt, ## args); \
+ IPA_IPC_LOGGING(ipa_get_ipc_logbuf(), \
+ " %s:%d " fmt, ## args); \
+ IPA_IPC_LOGGING(ipa_get_ipc_logbuf_low(), \
+ " %s:%d " fmt, ## args); \
+ } while (0)
+
#define IPAHAL_ERR_RL(fmt, args...) \
do { \
pr_err_ratelimited_ipa(IPAHAL_DRV_NAME " %s:%d " fmt, \
diff --git a/drivers/platform/msm/ipa/ipa_v3/ipahal/ipahal_reg.c b/drivers/platform/msm/ipa/ipa_v3/ipahal/ipahal_reg.c
index 66837d0..ce59488 100644
--- a/drivers/platform/msm/ipa/ipa_v3/ipahal/ipahal_reg.c
+++ b/drivers/platform/msm/ipa/ipa_v3/ipahal/ipahal_reg.c
@@ -18,6 +18,8 @@
#include "ipahal_reg.h"
#include "ipahal_reg_i.h"
+#define IPA_MAX_MSG_LEN 4096
+
static const char *ipareg_name_to_str[IPA_REG_MAX] = {
__stringify(IPA_ROUTE),
__stringify(IPA_IRQ_STTS_EE_n),
@@ -26,6 +28,9 @@
__stringify(IPA_IRQ_SUSPEND_INFO_EE_n),
__stringify(IPA_SUSPEND_IRQ_EN_EE_n),
__stringify(IPA_SUSPEND_IRQ_CLR_EE_n),
+ __stringify(IPA_HOLB_DROP_IRQ_INFO_EE_n),
+ __stringify(IPA_HOLB_DROP_IRQ_EN_EE_n),
+ __stringify(IPA_HOLB_DROP_IRQ_CLR_EE_n),
__stringify(IPA_BCR),
__stringify(IPA_ENABLED_PIPES),
__stringify(IPA_COMP_SW_RESET),
@@ -35,7 +40,20 @@
__stringify(IPA_SPARE_REG_1),
__stringify(IPA_SPARE_REG_2),
__stringify(IPA_COMP_CFG),
+ __stringify(IPA_STATE_TX_WRAPPER),
+ __stringify(IPA_STATE_TX1),
+ __stringify(IPA_STATE_FETCHER),
+ __stringify(IPA_STATE_FETCHER_MASK),
+ __stringify(IPA_STATE_DFETCHER),
+ __stringify(IPA_STATE_ACL),
+ __stringify(IPA_STATE),
+ __stringify(IPA_STATE_RX_ACTIVE),
+ __stringify(IPA_STATE_TX0),
__stringify(IPA_STATE_AGGR_ACTIVE),
+ __stringify(IPA_STATE_GSI_TLV),
+ __stringify(IPA_STATE_GSI_AOS),
+ __stringify(IPA_STATE_GSI_IF),
+ __stringify(IPA_STATE_GSI_SKIP),
__stringify(IPA_ENDP_INIT_HDR_n),
__stringify(IPA_ENDP_INIT_HDR_EXT_n),
__stringify(IPA_ENDP_INIT_AGGR_n),
@@ -46,6 +64,7 @@
__stringify(IPA_ENDP_INIT_CONN_TRACK_n),
__stringify(IPA_ENDP_INIT_CTRL_n),
__stringify(IPA_ENDP_INIT_CTRL_SCND_n),
+ __stringify(IPA_ENDP_INIT_CTRL_STATUS_n),
__stringify(IPA_ENDP_INIT_HOL_BLOCK_EN_n),
__stringify(IPA_ENDP_INIT_HOL_BLOCK_TIMER_n),
__stringify(IPA_ENDP_INIT_DEAGGR_n),
@@ -55,6 +74,7 @@
__stringify(IPA_IRQ_EE_UC_n),
__stringify(IPA_ENDP_INIT_HDR_METADATA_MASK_n),
__stringify(IPA_ENDP_INIT_HDR_METADATA_n),
+ __stringify(IPA_ENDP_INIT_PROD_CFG_n),
__stringify(IPA_ENDP_INIT_RSRC_GRP_n),
__stringify(IPA_SHARED_MEM_SIZE),
__stringify(IPA_SRAM_DIRECT_ACCESS_n),
@@ -66,6 +86,8 @@
__stringify(IPA_SYS_PKT_PROC_CNTXT_BASE),
__stringify(IPA_LOCAL_PKT_PROC_CNTXT_BASE),
__stringify(IPA_ENDP_STATUS_n),
+ __stringify(IPA_ENDP_WEIGHTS_n),
+ __stringify(IPA_ENDP_YELLOW_RED_MARKER),
__stringify(IPA_ENDP_FILTER_ROUTER_HSH_CFG_n),
__stringify(IPA_SRC_RSRC_GRP_01_RSRC_TYPE_n),
__stringify(IPA_SRC_RSRC_GRP_23_RSRC_TYPE_n),
@@ -105,6 +127,12 @@
__stringify(IPA_STAT_ROUTER_IPV6_END_ID),
__stringify(IPA_STAT_DROP_CNT_BASE_n),
__stringify(IPA_STAT_DROP_CNT_MASK_n),
+ __stringify(IPA_SNOC_FEC_EE_n),
+ __stringify(IPA_FEC_ADDR_EE_n),
+ __stringify(IPA_FEC_ADDR_MSB_EE_n),
+ __stringify(IPA_FEC_ATTR_EE_n),
+ __stringify(IPA_MBIM_DEAGGR_FEC_ATTR_EE_n),
+ __stringify(IPA_GEN_DEAGGR_FEC_ATTR_EE_n),
};
static void ipareg_construct_dummy(enum ipahal_reg_name reg,
@@ -1651,6 +1679,9 @@
* @parse - CB to parse register value to abstracted structure
* @offset - register offset relative to base address
* @n_ofst - N parameterized register sub-offset
+ * @n_start - starting n for n_registers
+ * @n_end - ending n for n_registers
+ * @en_print - enable this register to be printed when the device crashes
*/
struct ipahal_reg_obj {
void (*construct)(enum ipahal_reg_name reg, const void *fields,
@@ -1659,6 +1690,9 @@
u32 val);
u32 offset;
u32 n_ofst;
+ int n_start;
+ int n_end;
+ bool en_print;
};
/*
@@ -1676,365 +1710,543 @@
/* IPAv3 */
[IPA_HW_v3_0][IPA_ROUTE] = {
ipareg_construct_route, ipareg_parse_dummy,
- 0x00000048, 0},
+ 0x00000048, 0, 0, 0, 0},
[IPA_HW_v3_0][IPA_IRQ_STTS_EE_n] = {
ipareg_construct_dummy, ipareg_parse_dummy,
- 0x00003008, 0x1000},
+ 0x00003008, 0x1000, 0, 0, 0},
[IPA_HW_v3_0][IPA_IRQ_EN_EE_n] = {
ipareg_construct_dummy, ipareg_parse_dummy,
- 0x0000300c, 0x1000},
+ 0x0000300c, 0x1000, 0, 0, 0},
[IPA_HW_v3_0][IPA_IRQ_CLR_EE_n] = {
ipareg_construct_dummy, ipareg_parse_dummy,
- 0x00003010, 0x1000},
+ 0x00003010, 0x1000, 0, 0, 0},
[IPA_HW_v3_0][IPA_IRQ_SUSPEND_INFO_EE_n] = {
ipareg_construct_dummy, ipareg_parse_dummy,
- 0x00003098, 0x1000},
+ 0x00003098, 0x1000, 0, 0, 0},
[IPA_HW_v3_0][IPA_BCR] = {
ipareg_construct_dummy, ipareg_parse_dummy,
- 0x000001D0, 0},
+ 0x000001D0, 0, 0, 0, 0},
[IPA_HW_v3_0][IPA_ENABLED_PIPES] = {
ipareg_construct_dummy, ipareg_parse_dummy,
- 0x00000038, 0},
+ 0x00000038, 0, 0, 0, 0},
[IPA_HW_v3_0][IPA_COMP_SW_RESET] = {
ipareg_construct_dummy, ipareg_parse_dummy,
- 0x00000040, 0},
+ 0x00000040, 0, 0, 0, 0},
[IPA_HW_v3_0][IPA_VERSION] = {
ipareg_construct_dummy, ipareg_parse_dummy,
- 0x00000034, 0},
+ 0x00000034, 0, 0, 0, 0},
[IPA_HW_v3_0][IPA_TAG_TIMER] = {
ipareg_construct_dummy, ipareg_parse_dummy,
- 0x00000060, 0 },
+ 0x00000060, 0, 0, 0, 0},
[IPA_HW_v3_0][IPA_COMP_HW_VERSION] = {
ipareg_construct_dummy, ipareg_parse_dummy,
- 0x00000030, 0},
+ 0x00000030, 0, 0, 0, 0},
[IPA_HW_v3_0][IPA_SPARE_REG_1] = {
ipareg_construct_dummy, ipareg_parse_dummy,
- 0x00005090, 0},
+ 0x00005090, 0, 0, 0, 0},
[IPA_HW_v3_0][IPA_SPARE_REG_2] = {
ipareg_construct_dummy, ipareg_parse_dummy,
- 0x00005094, 0},
+ 0x00005094, 0, 0, 0, 0},
[IPA_HW_v3_0][IPA_COMP_CFG] = {
ipareg_construct_comp_cfg, ipareg_parse_comp_cfg,
- 0x0000003C, 0},
+ 0x0000003C, 0, 0, 0, 0},
[IPA_HW_v3_0][IPA_STATE_AGGR_ACTIVE] = {
ipareg_construct_dummy, ipareg_parse_dummy,
- 0x0000010C, 0},
+ 0x0000010C, 0, 0, 0, 0},
[IPA_HW_v3_0][IPA_ENDP_INIT_HDR_n] = {
ipareg_construct_endp_init_hdr_n, ipareg_parse_dummy,
- 0x00000810, 0x70},
+ 0x00000810, 0x70, 0, 0, 0},
[IPA_HW_v3_0][IPA_ENDP_INIT_HDR_EXT_n] = {
ipareg_construct_endp_init_hdr_ext_n, ipareg_parse_dummy,
- 0x00000814, 0x70},
+ 0x00000814, 0x70, 0, 0, 0},
[IPA_HW_v3_0][IPA_ENDP_INIT_AGGR_n] = {
ipareg_construct_endp_init_aggr_n,
ipareg_parse_endp_init_aggr_n,
- 0x00000824, 0x70},
+ 0x00000824, 0x70, 0, 0, 0},
[IPA_HW_v3_0][IPA_AGGR_FORCE_CLOSE] = {
ipareg_construct_dummy, ipareg_parse_dummy,
- 0x000001EC, 0},
+ 0x000001EC, 0, 0, 0, 0},
[IPA_HW_v3_0][IPA_ENDP_INIT_ROUTE_n] = {
ipareg_construct_endp_init_route_n, ipareg_parse_dummy,
- 0x00000828, 0x70},
+ 0x00000828, 0x70, 0, 0, 0},
[IPA_HW_v3_0][IPA_ENDP_INIT_MODE_n] = {
ipareg_construct_endp_init_mode_n, ipareg_parse_dummy,
- 0x00000820, 0x70},
+ 0x00000820, 0x70, 0, 0, 0},
[IPA_HW_v3_0][IPA_ENDP_INIT_NAT_n] = {
ipareg_construct_endp_init_nat_n, ipareg_parse_dummy,
- 0x0000080C, 0x70},
+ 0x0000080C, 0x70, 0, 0, 0},
[IPA_HW_v3_0][IPA_ENDP_INIT_CTRL_n] = {
ipareg_construct_endp_init_ctrl_n,
ipareg_parse_endp_init_ctrl_n,
- 0x00000800, 0x70},
+ 0x00000800, 0x70, 0, 0, 0},
[IPA_HW_v3_0][IPA_ENDP_INIT_CTRL_SCND_n] = {
ipareg_construct_endp_init_ctrl_scnd_n, ipareg_parse_dummy,
- 0x00000804, 0x70 },
+ 0x00000804, 0x70, 0, 0, 0},
[IPA_HW_v3_0][IPA_ENDP_INIT_HOL_BLOCK_EN_n] = {
ipareg_construct_endp_init_hol_block_en_n,
ipareg_parse_dummy,
- 0x0000082c, 0x70},
+ 0x0000082c, 0x70, 0, 0, 0},
[IPA_HW_v3_0][IPA_ENDP_INIT_HOL_BLOCK_TIMER_n] = {
ipareg_construct_endp_init_hol_block_timer_n,
ipareg_parse_dummy,
- 0x00000830, 0x70},
+ 0x00000830, 0x70, 0, 0, 0},
[IPA_HW_v3_0][IPA_ENDP_INIT_DEAGGR_n] = {
ipareg_construct_endp_init_deaggr_n,
ipareg_parse_dummy,
- 0x00000834, 0x70},
+ 0x00000834, 0x70, 0, 0, 0},
[IPA_HW_v3_0][IPA_ENDP_INIT_SEQ_n] = {
ipareg_construct_dummy, ipareg_parse_dummy,
- 0x0000083C, 0x70},
+ 0x0000083C, 0x70, 0, 0, 0},
[IPA_HW_v3_0][IPA_DEBUG_CNT_REG_n] = {
ipareg_construct_dummy, ipareg_parse_dummy,
- 0x00000600, 0x4},
+ 0x00000600, 0x4, 0, 0, 0},
[IPA_HW_v3_0][IPA_ENDP_INIT_CFG_n] = {
ipareg_construct_endp_init_cfg_n, ipareg_parse_dummy,
- 0x00000808, 0x70},
+ 0x00000808, 0x70, 0, 0, 0},
[IPA_HW_v3_0][IPA_IRQ_EE_UC_n] = {
ipareg_construct_dummy, ipareg_parse_dummy,
- 0x0000301c, 0x1000},
+ 0x0000301c, 0x1000, 0, 0, 0},
[IPA_HW_v3_0][IPA_ENDP_INIT_HDR_METADATA_MASK_n] = {
ipareg_construct_endp_init_hdr_metadata_mask_n,
ipareg_parse_dummy,
- 0x00000818, 0x70},
+ 0x00000818, 0x70, 0, 0, 0},
[IPA_HW_v3_0][IPA_ENDP_INIT_HDR_METADATA_n] = {
ipareg_construct_endp_init_hdr_metadata_n,
ipareg_parse_dummy,
- 0x0000081c, 0x70},
+ 0x0000081c, 0x70, 0, 0, 0},
[IPA_HW_v3_0][IPA_ENDP_INIT_RSRC_GRP_n] = {
ipareg_construct_endp_init_rsrc_grp_n,
ipareg_parse_dummy,
- 0x00000838, 0x70},
+ 0x00000838, 0x70, 0, 0, 0},
[IPA_HW_v3_0][IPA_SHARED_MEM_SIZE] = {
ipareg_construct_dummy, ipareg_parse_shared_mem_size,
- 0x00000054, 0},
+ 0x00000054, 0, 0, 0, 0},
[IPA_HW_v3_0][IPA_SRAM_DIRECT_ACCESS_n] = {
ipareg_construct_dummy, ipareg_parse_dummy,
- 0x00007000, 0x4},
+ 0x00007000, 0x4, 0, 0, 0},
[IPA_HW_v3_0][IPA_DEBUG_CNT_CTRL_n] = {
ipareg_construct_debug_cnt_ctrl_n, ipareg_parse_dummy,
- 0x00000640, 0x4},
+ 0x00000640, 0x4, 0, 0, 0},
[IPA_HW_v3_0][IPA_UC_MAILBOX_m_n] = {
ipareg_construct_dummy, ipareg_parse_dummy,
- 0x00032000, 0x4},
+ 0x00032000, 0x4, 0, 0, 0},
[IPA_HW_v3_0][IPA_FILT_ROUT_HASH_FLUSH] = {
ipareg_construct_dummy, ipareg_parse_dummy,
- 0x00000090, 0},
+ 0x00000090, 0, 0, 0, 0},
[IPA_HW_v3_0][IPA_SINGLE_NDP_MODE] = {
ipareg_construct_single_ndp_mode, ipareg_parse_single_ndp_mode,
- 0x00000068, 0},
+ 0x00000068, 0, 0, 0, 0},
[IPA_HW_v3_0][IPA_QCNCM] = {
ipareg_construct_qcncm, ipareg_parse_qcncm,
- 0x00000064, 0},
+ 0x00000064, 0, 0, 0, 0},
[IPA_HW_v3_0][IPA_SYS_PKT_PROC_CNTXT_BASE] = {
ipareg_construct_dummy, ipareg_parse_dummy,
- 0x000001e0, 0},
+ 0x000001e0, 0, 0, 0, 0},
[IPA_HW_v3_0][IPA_LOCAL_PKT_PROC_CNTXT_BASE] = {
ipareg_construct_dummy, ipareg_parse_dummy,
- 0x000001e8, 0},
+ 0x000001e8, 0, 0, 0, 0},
[IPA_HW_v3_0][IPA_ENDP_STATUS_n] = {
ipareg_construct_endp_status_n, ipareg_parse_dummy,
- 0x00000840, 0x70},
+ 0x00000840, 0x70, 0, 0, 0},
[IPA_HW_v3_0][IPA_ENDP_FILTER_ROUTER_HSH_CFG_n] = {
ipareg_construct_hash_cfg_n, ipareg_parse_hash_cfg_n,
- 0x0000085C, 0x70},
+ 0x0000085C, 0x70, 0, 0, 0},
[IPA_HW_v3_0][IPA_SRC_RSRC_GRP_01_RSRC_TYPE_n] = {
ipareg_construct_rsrg_grp_xy, ipareg_parse_dummy,
- 0x00000400, 0x20},
+ 0x00000400, 0x20, 0, 0, 0},
[IPA_HW_v3_0][IPA_SRC_RSRC_GRP_23_RSRC_TYPE_n] = {
ipareg_construct_rsrg_grp_xy, ipareg_parse_dummy,
- 0x00000404, 0x20},
+ 0x00000404, 0x20, 0, 0, 0},
[IPA_HW_v3_0][IPA_SRC_RSRC_GRP_45_RSRC_TYPE_n] = {
ipareg_construct_rsrg_grp_xy, ipareg_parse_dummy,
- 0x00000408, 0x20},
+ 0x00000408, 0x20, 0, 0, 0},
[IPA_HW_v3_0][IPA_SRC_RSRC_GRP_67_RSRC_TYPE_n] = {
ipareg_construct_rsrg_grp_xy, ipareg_parse_dummy,
- 0x0000040C, 0x20},
+ 0x0000040C, 0x20, 0, 0, 0},
[IPA_HW_v3_0][IPA_DST_RSRC_GRP_01_RSRC_TYPE_n] = {
ipareg_construct_rsrg_grp_xy, ipareg_parse_dummy,
- 0x00000500, 0x20},
+ 0x00000500, 0x20, 0, 0, 0},
[IPA_HW_v3_0][IPA_DST_RSRC_GRP_23_RSRC_TYPE_n] = {
ipareg_construct_rsrg_grp_xy, ipareg_parse_dummy,
- 0x00000504, 0x20},
+ 0x00000504, 0x20, 0, 0, 0},
[IPA_HW_v3_0][IPA_DST_RSRC_GRP_45_RSRC_TYPE_n] = {
ipareg_construct_rsrg_grp_xy, ipareg_parse_dummy,
- 0x00000508, 0x20},
+ 0x00000508, 0x20, 0, 0, 0},
[IPA_HW_v3_0][IPA_DST_RSRC_GRP_67_RSRC_TYPE_n] = {
ipareg_construct_rsrg_grp_xy, ipareg_parse_dummy,
- 0x0000050c, 0x20},
+ 0x0000050c, 0x20, 0, 0, 0},
[IPA_HW_v3_0][IPA_RX_HPS_CLIENTS_MIN_DEPTH_0] = {
ipareg_construct_rx_hps_clients_depth0, ipareg_parse_dummy,
- 0x000023C4, 0},
+ 0x000023C4, 0, 0, 0, 0},
[IPA_HW_v3_0][IPA_RX_HPS_CLIENTS_MIN_DEPTH_1] = {
ipareg_construct_rx_hps_clients_depth1, ipareg_parse_dummy,
- 0x000023C8, 0},
+ 0x000023C8, 0, 0, 0, 0},
[IPA_HW_v3_0][IPA_RX_HPS_CLIENTS_MAX_DEPTH_0] = {
ipareg_construct_rx_hps_clients_depth0, ipareg_parse_dummy,
- 0x000023CC, 0},
+ 0x000023CC, 0, 0, 0, 0},
[IPA_HW_v3_0][IPA_RX_HPS_CLIENTS_MAX_DEPTH_1] = {
ipareg_construct_rx_hps_clients_depth1, ipareg_parse_dummy,
- 0x000023D0, 0},
+ 0x000023D0, 0, 0, 0, 0},
[IPA_HW_v3_0][IPA_QSB_MAX_WRITES] = {
ipareg_construct_qsb_max_writes, ipareg_parse_dummy,
- 0x00000074, 0},
+ 0x00000074, 0, 0, 0, 0},
[IPA_HW_v3_0][IPA_QSB_MAX_READS] = {
ipareg_construct_qsb_max_reads, ipareg_parse_dummy,
- 0x00000078, 0},
+ 0x00000078, 0, 0, 0, 0},
[IPA_HW_v3_0][IPA_DPS_SEQUENCER_FIRST] = {
ipareg_construct_dummy, ipareg_parse_dummy,
- 0x0001e000, 0},
+ 0x0001e000, 0, 0, 0, 0},
[IPA_HW_v3_0][IPA_HPS_SEQUENCER_FIRST] = {
ipareg_construct_dummy, ipareg_parse_dummy,
- 0x0001e080, 0},
+ 0x0001e080, 0, 0, 0, 0},
/* IPAv3.1 */
[IPA_HW_v3_1][IPA_IRQ_SUSPEND_INFO_EE_n] = {
ipareg_construct_dummy, ipareg_parse_dummy,
- 0x00003030, 0x1000},
+ 0x00003030, 0x1000, 0, 0, 0},
[IPA_HW_v3_1][IPA_SUSPEND_IRQ_EN_EE_n] = {
ipareg_construct_dummy, ipareg_parse_dummy,
- 0x00003034, 0x1000},
+ 0x00003034, 0x1000, 0, 0, 0},
[IPA_HW_v3_1][IPA_SUSPEND_IRQ_CLR_EE_n] = {
ipareg_construct_dummy, ipareg_parse_dummy,
- 0x00003038, 0x1000},
+ 0x00003038, 0x1000, 0, 0, 0},
/* IPAv3.5 */
[IPA_HW_v3_5][IPA_TX_CFG] = {
ipareg_construct_tx_cfg, ipareg_parse_tx_cfg,
- 0x000001FC, 0},
+ 0x000001FC, 0, 0, 0, 0},
[IPA_HW_v3_5][IPA_SRC_RSRC_GRP_01_RSRC_TYPE_n] = {
ipareg_construct_rsrg_grp_xy_v3_5, ipareg_parse_dummy,
- 0x00000400, 0x20},
+ 0x00000400, 0x20, 0, 0, 0},
[IPA_HW_v3_5][IPA_SRC_RSRC_GRP_23_RSRC_TYPE_n] = {
ipareg_construct_rsrg_grp_xy_v3_5, ipareg_parse_dummy,
- 0x00000404, 0x20},
+ 0x00000404, 0x20, 0, 0, 0},
[IPA_HW_v3_5][IPA_SRC_RSRC_GRP_45_RSRC_TYPE_n] = {
ipareg_construct_dummy, ipareg_parse_dummy,
- -1, 0},
+ -1, 0, 0, 0, 0},
[IPA_HW_v3_5][IPA_SRC_RSRC_GRP_67_RSRC_TYPE_n] = {
ipareg_construct_dummy, ipareg_parse_dummy,
- -1, 0},
+ -1, 0, 0, 0, 0},
[IPA_HW_v3_5][IPA_DST_RSRC_GRP_01_RSRC_TYPE_n] = {
ipareg_construct_rsrg_grp_xy_v3_5, ipareg_parse_dummy,
- 0x00000500, 0x20},
+ 0x00000500, 0x20, 0, 0, 0},
[IPA_HW_v3_5][IPA_DST_RSRC_GRP_23_RSRC_TYPE_n] = {
ipareg_construct_rsrg_grp_xy_v3_5, ipareg_parse_dummy,
- 0x00000504, 0x20},
+ 0x00000504, 0x20, 0, 0, 0},
[IPA_HW_v3_5][IPA_DST_RSRC_GRP_45_RSRC_TYPE_n] = {
ipareg_construct_dummy, ipareg_parse_dummy,
- -1, 0},
+ -1, 0, 0, 0, 0},
[IPA_HW_v3_5][IPA_DST_RSRC_GRP_67_RSRC_TYPE_n] = {
ipareg_construct_dummy, ipareg_parse_dummy,
- -1, 0},
+ -1, 0, 0, 0, 0},
[IPA_HW_v3_5][IPA_ENDP_INIT_RSRC_GRP_n] = {
ipareg_construct_endp_init_rsrc_grp_n_v3_5,
ipareg_parse_dummy,
- 0x00000838, 0x70},
+ 0x00000838, 0x70, 0, 0, 0},
[IPA_HW_v3_5][IPA_RX_HPS_CLIENTS_MIN_DEPTH_0] = {
ipareg_construct_rx_hps_clients_depth0_v3_5,
ipareg_parse_dummy,
- 0x000023C4, 0},
+ 0x000023C4, 0, 0, 0, 0},
[IPA_HW_v3_5][IPA_RX_HPS_CLIENTS_MIN_DEPTH_1] = {
ipareg_construct_dummy, ipareg_parse_dummy,
- -1, 0},
+ -1, 0, 0, 0, 0},
[IPA_HW_v3_5][IPA_RX_HPS_CLIENTS_MAX_DEPTH_0] = {
ipareg_construct_rx_hps_clients_depth0_v3_5,
ipareg_parse_dummy,
- 0x000023CC, 0},
+ 0x000023CC, 0, 0, 0, 0},
[IPA_HW_v3_5][IPA_RX_HPS_CLIENTS_MAX_DEPTH_1] = {
ipareg_construct_dummy, ipareg_parse_dummy,
- -1, 0},
+ -1, 0, 0, 0, 0},
[IPA_HW_v3_5][IPA_SPARE_REG_1] = {
ipareg_construct_dummy, ipareg_parse_dummy,
- 0x00002780, 0},
+ 0x00002780, 0, 0, 0, 0},
[IPA_HW_v3_5][IPA_SPARE_REG_2] = {
ipareg_construct_dummy, ipareg_parse_dummy,
- 0x00002784, 0},
+ 0x00002784, 0, 0, 0, 0},
[IPA_HW_v3_5][IPA_IDLE_INDICATION_CFG] = {
ipareg_construct_idle_indication_cfg, ipareg_parse_dummy,
- 0x00000220, 0},
+ 0x00000220, 0, 0, 0, 0},
[IPA_HW_v3_5][IPA_HPS_FTCH_ARB_QUEUE_WEIGHT] = {
ipareg_construct_hps_queue_weights,
- ipareg_parse_hps_queue_weights, 0x000005a4, 0},
+ ipareg_parse_hps_queue_weights, 0x000005a4, 0, 0, 0, 0},
/* IPAv4.0 */
+ [IPA_HW_v4_0][IPA_IRQ_SUSPEND_INFO_EE_n] = {
+ ipareg_construct_dummy, ipareg_parse_dummy,
+ 0x00003030, 0x1000, 0, 1, 1},
+ [IPA_HW_v4_0][IPA_SUSPEND_IRQ_EN_EE_n] = {
+ ipareg_construct_dummy, ipareg_parse_dummy,
+ 0x00003034, 0x1000, 0, 1, 1},
+ [IPA_HW_v4_0][IPA_SUSPEND_IRQ_CLR_EE_n] = {
+ ipareg_construct_dummy, ipareg_parse_dummy,
+ 0x00003038, 0x1000, 0, 1, 1},
+ [IPA_HW_v4_0][IPA_IRQ_EN_EE_n] = {
+ ipareg_construct_dummy, ipareg_parse_dummy,
+ 0x0000300c, 0x1000, 0, 1, 1},
+ [IPA_HW_v4_0][IPA_TAG_TIMER] = {
+ ipareg_construct_dummy, ipareg_parse_dummy,
+ 0x00000060, 0, 0, 0, 1},
[IPA_HW_v4_0][IPA_ENDP_INIT_CTRL_n] = {
ipareg_construct_endp_init_ctrl_n_v4_0, ipareg_parse_dummy,
- 0x00000800, 0x70 },
+ 0x00000800, 0x70, 0, 23, 1},
+ [IPA_HW_v4_0][IPA_ENDP_INIT_HDR_EXT_n] = {
+ ipareg_construct_endp_init_hdr_ext_n, ipareg_parse_dummy,
+ 0x00000814, 0x70, 0, 23, 1},
+ [IPA_HW_v4_0][IPA_ENDP_INIT_AGGR_n] = {
+ ipareg_construct_endp_init_aggr_n,
+ ipareg_parse_endp_init_aggr_n,
+ 0x00000824, 0x70, 0, 23, 1},
[IPA_HW_v4_0][IPA_TX_CFG] = {
ipareg_construct_tx_cfg_v4_0, ipareg_parse_tx_cfg_v4_0,
- 0x000001FC, 0},
+ 0x000001FC, 0, 0, 0, 0},
[IPA_HW_v4_0][IPA_DEBUG_CNT_REG_n] = {
ipareg_construct_dummy, ipareg_parse_dummy,
- -1, 0},
+ -1, 0, 0, 0, 0},
[IPA_HW_v4_0][IPA_DEBUG_CNT_CTRL_n] = {
ipareg_construct_debug_cnt_ctrl_n, ipareg_parse_dummy,
- -1, 0},
+ -1, 0, 0, 0, 0},
[IPA_HW_v4_0][IPA_QCNCM] = {
ipareg_construct_qcncm, ipareg_parse_qcncm,
- -1, 0},
+ -1, 0, 0, 0, 0},
[IPA_HW_v4_0][IPA_SINGLE_NDP_MODE] = {
ipareg_construct_single_ndp_mode, ipareg_parse_single_ndp_mode,
- -1, 0},
+ -1, 0, 0, 0, 0},
[IPA_HW_v4_0][IPA_QSB_MAX_READS] = {
ipareg_construct_qsb_max_reads_v4_0, ipareg_parse_dummy,
- 0x00000078, 0},
+ 0x00000078, 0, 0, 0, 0},
[IPA_HW_v4_0][IPA_FILT_ROUT_HASH_FLUSH] = {
ipareg_construct_dummy, ipareg_parse_dummy,
- 0x0000014c, 0},
- [IPA_HW_v4_0][IPA_STATE_AGGR_ACTIVE] = {
- ipareg_construct_dummy, ipareg_parse_dummy,
- 0x000000b4, 0},
+ 0x0000014c, 0, 0, 0, 0},
+ [IPA_HW_v4_0][IPA_ENDP_INIT_HDR_n] = {
+ ipareg_construct_endp_init_hdr_n, ipareg_parse_dummy,
+ 0x00000810, 0x70, 0, 23, 1},
[IPA_HW_v4_0][IPA_ENDP_INIT_ROUTE_n] = {
ipareg_construct_endp_init_route_n, ipareg_parse_dummy,
- -1, 0},
+ -1, 0, 0, 0, 0},
+ [IPA_HW_v4_0][IPA_ENDP_INIT_MODE_n] = {
+ ipareg_construct_endp_init_mode_n, ipareg_parse_dummy,
+ 0x00000820, 0x70, 0, 10, 1},
+ [IPA_HW_v4_0][IPA_ENDP_INIT_NAT_n] = {
+ ipareg_construct_endp_init_nat_n, ipareg_parse_dummy,
+ 0x0000080C, 0x70, 0, 10, 1},
[IPA_HW_v4_0][IPA_ENDP_STATUS_n] = {
ipareg_construct_endp_status_n_v4_0, ipareg_parse_dummy,
- 0x00000840, 0x70},
- [IPA_HW_v4_0][IPA_CLKON_CFG] = {
- ipareg_construct_clkon_cfg, ipareg_parse_clkon_cfg,
- 0x00000044, 0},
+ 0x00000840, 0x70, 0, 23, 1},
+ [IPA_HW_v4_0][IPA_ENDP_FILTER_ROUTER_HSH_CFG_n] = {
+ ipareg_construct_hash_cfg_n, ipareg_parse_hash_cfg_n,
+ 0x0000085C, 0x70, 0, 32, 1},
[IPA_HW_v4_0][IPA_ENDP_INIT_CONN_TRACK_n] = {
ipareg_construct_endp_init_conn_track_n,
ipareg_parse_dummy,
- 0x00000850, 0x70},
+ 0x00000850, 0x70, 0, 10, 1},
+ [IPA_HW_v4_0][IPA_ENDP_INIT_CTRL_SCND_n] = {
+ ipareg_construct_endp_init_ctrl_scnd_n, ipareg_parse_dummy,
+ 0x00000804, 0x70, 0, 23, 1},
+ [IPA_HW_v4_0][IPA_ENDP_INIT_HOL_BLOCK_EN_n] = {
+ ipareg_construct_endp_init_hol_block_en_n,
+ ipareg_parse_dummy,
+ 0x0000082c, 0x70, 10, 23, 1},
+ [IPA_HW_v4_0][IPA_ENDP_INIT_HOL_BLOCK_TIMER_n] = {
+ ipareg_construct_endp_init_hol_block_timer_n,
+ ipareg_parse_dummy,
+ 0x00000830, 0x70, 10, 23, 1},
+ [IPA_HW_v4_0][IPA_ENDP_INIT_DEAGGR_n] = {
+ ipareg_construct_endp_init_deaggr_n,
+ ipareg_parse_dummy,
+ 0x00000834, 0x70, 0, 10, 1},
+ [IPA_HW_v4_0][IPA_ENDP_INIT_SEQ_n] = {
+ ipareg_construct_dummy, ipareg_parse_dummy,
+ 0x0000083C, 0x70, 0, 10, 1},
+ [IPA_HW_v4_0][IPA_ENDP_INIT_CFG_n] = {
+ ipareg_construct_endp_init_cfg_n, ipareg_parse_dummy,
+ 0x00000808, 0x70, 0, 23, 1},
+ [IPA_HW_v4_0][IPA_IRQ_EE_UC_n] = {
+ ipareg_construct_dummy, ipareg_parse_dummy,
+ 0x0000301c, 0x1000, 0, 0, 1},
+ [IPA_HW_v4_0][IPA_ENDP_INIT_HDR_METADATA_MASK_n] = {
+ ipareg_construct_endp_init_hdr_metadata_mask_n,
+ ipareg_parse_dummy,
+ 0x00000818, 0x70, 10, 23, 1},
+ [IPA_HW_v4_0][IPA_ENDP_INIT_HDR_METADATA_n] = {
+ ipareg_construct_endp_init_hdr_metadata_n,
+ ipareg_parse_dummy,
+ 0x0000081c, 0x70, 0, 10, 1},
+ [IPA_HW_v4_0][IPA_CLKON_CFG] = {
+ ipareg_construct_clkon_cfg, ipareg_parse_clkon_cfg,
+ 0x00000044, 0, 0, 0, 0},
[IPA_HW_v4_0][IPA_STAT_QUOTA_BASE_n] = {
ipareg_construct_dummy, ipareg_parse_dummy,
- 0x00000700, 0x4 },
+ 0x00000700, 0x4, 0, 0, 0},
[IPA_HW_v4_0][IPA_STAT_QUOTA_MASK_n] = {
ipareg_construct_dummy, ipareg_parse_dummy,
- 0x00000708, 0x4 },
+ 0x00000708, 0x4, 0, 0, 0},
[IPA_HW_v4_0][IPA_STAT_TETHERING_BASE_n] = {
ipareg_construct_dummy, ipareg_parse_dummy,
- 0x00000710, 0x4 },
+ 0x00000710, 0x4, 0, 0, 0},
[IPA_HW_v4_0][IPA_STAT_TETHERING_MASK_n] = {
ipareg_construct_dummy, ipareg_parse_dummy,
- 0x00000718, 0x4 },
+ 0x00000718, 0x4, 0, 0, 0},
[IPA_HW_v4_0][IPA_STAT_FILTER_IPV4_BASE] = {
ipareg_construct_dummy, ipareg_parse_dummy,
- 0x00000720, 0x0 },
+ 0x00000720, 0, 0, 0, 0},
[IPA_HW_v4_0][IPA_STAT_FILTER_IPV6_BASE] = {
ipareg_construct_dummy, ipareg_parse_dummy,
- 0x00000724, 0x0 },
+ 0x00000724, 0, 0, 0, 0},
[IPA_HW_v4_0][IPA_STAT_ROUTER_IPV4_BASE] = {
ipareg_construct_dummy, ipareg_parse_dummy,
- 0x00000728, 0x0 },
+ 0x00000728, 0, 0, 0, 0},
[IPA_HW_v4_0][IPA_STAT_ROUTER_IPV6_BASE] = {
ipareg_construct_dummy, ipareg_parse_dummy,
- 0x0000072C, 0x0 },
+ 0x0000072C, 0, 0, 0, 0},
[IPA_HW_v4_0][IPA_STAT_FILTER_IPV4_START_ID] = {
ipareg_construct_dummy, ipareg_parse_dummy,
- 0x00000730, 0x0 },
+ 0x00000730, 0, 0, 0, 0},
[IPA_HW_v4_0][IPA_STAT_FILTER_IPV6_START_ID] = {
ipareg_construct_dummy, ipareg_parse_dummy,
- 0x00000734, 0x0 },
+ 0x00000734, 0, 0, 0, 0},
[IPA_HW_v4_0][IPA_STAT_ROUTER_IPV4_START_ID] = {
ipareg_construct_dummy, ipareg_parse_dummy,
- 0x00000738, 0x0 },
+ 0x00000738, 0, 0, 0, 0},
[IPA_HW_v4_0][IPA_STAT_ROUTER_IPV6_START_ID] = {
ipareg_construct_dummy, ipareg_parse_dummy,
- 0x0000073C, 0x0 },
+ 0x0000073C, 0, 0, 0, 0},
[IPA_HW_v4_0][IPA_STAT_FILTER_IPV4_END_ID] = {
ipareg_construct_dummy, ipareg_parse_dummy,
- 0x00000740, 0x0 },
+ 0x00000740, 0, 0, 0, 0},
[IPA_HW_v4_0][IPA_STAT_FILTER_IPV6_END_ID] = {
ipareg_construct_dummy, ipareg_parse_dummy,
- 0x00000744, 0x0 },
+ 0x00000744, 0, 0, 0, 0},
[IPA_HW_v4_0][IPA_STAT_ROUTER_IPV4_END_ID] = {
ipareg_construct_dummy, ipareg_parse_dummy,
- 0x00000748, 0x0 },
+ 0x00000748, 0, 0, 0, 0},
[IPA_HW_v4_0][IPA_STAT_ROUTER_IPV6_END_ID] = {
ipareg_construct_dummy, ipareg_parse_dummy,
- 0x0000074C, 0x0 },
+ 0x0000074C, 0, 0, 0, 0},
[IPA_HW_v4_0][IPA_STAT_DROP_CNT_BASE_n] = {
ipareg_construct_dummy, ipareg_parse_dummy,
- 0x00000750, 0x4 },
+ 0x00000750, 0x4, 3, 1},
[IPA_HW_v4_0][IPA_STAT_DROP_CNT_MASK_n] = {
ipareg_construct_dummy, ipareg_parse_dummy,
- 0x00000758, 0x4 },
+ 0x00000758, 0x4, 0, 0, 1},
+ [IPA_HW_v4_0][IPA_STATE_TX_WRAPPER] = {
+ ipareg_construct_dummy, ipareg_parse_dummy,
+ 0x00000090, 0, 0, 0, 1},
+ [IPA_HW_v4_0][IPA_STATE_TX1] = {
+ ipareg_construct_dummy, ipareg_parse_dummy,
+ 0x00000094, 0, 0, 0, 1},
+ [IPA_HW_v4_0][IPA_STATE_FETCHER] = {
+ ipareg_construct_dummy, ipareg_parse_dummy,
+ 0x00000098, 0, 0, 0, 1},
+ [IPA_HW_v4_0][IPA_STATE_FETCHER_MASK] = {
+ ipareg_construct_dummy, ipareg_parse_dummy,
+ 0x0000009C, 0, 0, 0, 1},
+ [IPA_HW_v4_0][IPA_STATE_DFETCHER] = {
+ ipareg_construct_dummy, ipareg_parse_dummy,
+ 0x000000A0, 0, 0, 0, 1},
+ [IPA_HW_v4_0][IPA_STATE_ACL] = {
+ ipareg_construct_dummy, ipareg_parse_dummy,
+ 0x000000A4, 0, 0, 0, 1},
+ [IPA_HW_v4_0][IPA_STATE] = {
+ ipareg_construct_dummy, ipareg_parse_dummy,
+ 0x000000A8, 0, 0, 0, 1},
+ [IPA_HW_v4_0][IPA_STATE_RX_ACTIVE] = {
+ ipareg_construct_dummy, ipareg_parse_dummy,
+ 0x000000AC, 0, 0, 0, 1},
+ [IPA_HW_v4_0][IPA_STATE_TX0] = {
+ ipareg_construct_dummy, ipareg_parse_dummy,
+ 0x000000B0, 0, 0, 0, 1},
+ [IPA_HW_v4_0][IPA_STATE_AGGR_ACTIVE] = {
+ ipareg_construct_dummy, ipareg_parse_dummy,
+ 0x000000B4, 0, 0, 0, 1},
+ [IPA_HW_v4_0][IPA_STATE_GSI_TLV] = {
+ ipareg_construct_dummy, ipareg_parse_dummy,
+ 0x000000B8, 0, 0, 0, 1},
+ [IPA_HW_v4_0][IPA_STATE_GSI_AOS] = {
+ ipareg_construct_dummy, ipareg_parse_dummy,
+ 0x000000B8, 0, 0, 0, 1},
+ [IPA_HW_v4_0][IPA_STATE_GSI_IF] = {
+ ipareg_construct_dummy, ipareg_parse_dummy,
+ 0x000000C0, 0, 0, 0, 1},
+ [IPA_HW_v4_0][IPA_STATE_GSI_SKIP] = {
+ ipareg_construct_dummy, ipareg_parse_dummy,
+ 0x000000C4, 0, 0, 0, 1},
+ [IPA_HW_v4_0][IPA_SNOC_FEC_EE_n] = {
+ ipareg_construct_dummy, ipareg_parse_dummy,
+ 0x00003018, 0x1000, 0, 0, 1},
+ [IPA_HW_v4_0][IPA_FEC_ADDR_EE_n] = {
+ ipareg_construct_dummy, ipareg_parse_dummy,
+ 0x00003020, 0x1000, 0, 0, 1},
+ [IPA_HW_v4_0][IPA_FEC_ADDR_MSB_EE_n] = {
+ ipareg_construct_dummy, ipareg_parse_dummy,
+ 0x00003024, 0x1000, 0, 0, 1},
+ [IPA_HW_v4_0][IPA_FEC_ATTR_EE_n] = {
+ ipareg_construct_dummy, ipareg_parse_dummy,
+ 0x00003028, 0x1000, 0, 0, 1},
+ [IPA_HW_v4_0][IPA_MBIM_DEAGGR_FEC_ATTR_EE_n] = {
+ ipareg_construct_dummy, ipareg_parse_dummy,
+ 0x00003028, 0x1000, 0, 0, 1},
+ [IPA_HW_v4_0][IPA_GEN_DEAGGR_FEC_ATTR_EE_n] = {
+ ipareg_construct_dummy, ipareg_parse_dummy,
+ 0x00003028, 0x1000, 0, 0, 1},
+ [IPA_HW_v4_0][IPA_HOLB_DROP_IRQ_INFO_EE_n] = {
+ ipareg_construct_dummy, ipareg_parse_dummy,
+ 0x0000303C, 0x1000, 0, 0, 1},
+ [IPA_HW_v4_0][IPA_HOLB_DROP_IRQ_EN_EE_n] = {
+ ipareg_construct_dummy, ipareg_parse_dummy,
+ 0x00003040, 0x1000, 0, 0, 1},
+ [IPA_HW_v4_0][IPA_HOLB_DROP_IRQ_CLR_EE_n] = {
+ ipareg_construct_dummy, ipareg_parse_dummy,
+ 0x00003044, 0x1000, 0, 0, 1},
+ [IPA_HW_v4_0][IPA_ENDP_INIT_CTRL_STATUS_n] = {
+ ipareg_construct_dummy, ipareg_parse_dummy,
+ 0x00000864, 0x70, 0, 23, 1},
+ [IPA_HW_v4_0][IPA_ENDP_INIT_PROD_CFG_n] = {
+ ipareg_construct_dummy, ipareg_parse_dummy,
+ 0x00000CC8, 0x70, 10, 23, 1},
+ [IPA_HW_v4_0][IPA_ENDP_INIT_RSRC_GRP_n] = {
+ ipareg_construct_endp_init_rsrc_grp_n_v3_5,
+ ipareg_parse_dummy,
+ 0x00000838, 0x70, 0, 23, 1},
+ [IPA_HW_v4_0][IPA_ENDP_WEIGHTS_n] = {
+ ipareg_construct_dummy, ipareg_parse_dummy,
+ 0x00000CA4, 0x70, 10, 23, 1},
+ [IPA_HW_v4_0][IPA_ENDP_YELLOW_RED_MARKER] = {
+ ipareg_construct_dummy, ipareg_parse_dummy,
+ 0x00000CC0, 0x70, 10, 23, 1},
};
+int ipahal_print_all_regs(void)
+{
+ int i, j;
+
+ IPAHAL_DBG("Printing all registers for ipa_hw_type %d\n",
+ ipahal_ctx->hw_type);
+
+ if ((ipahal_ctx->hw_type < IPA_HW_v4_0) ||
+ (ipahal_ctx->hw_type >= IPA_HW_MAX)) {
+ IPAHAL_ERR("invalid IPA HW type (%d)\n", ipahal_ctx->hw_type);
+ return -EINVAL;
+ }
+
+ for (i = 0; i < IPA_REG_MAX ; i++) {
+ if (!ipahal_reg_objs[IPA_HW_v4_0][i].en_print)
+ continue;
+
+ j = ipahal_reg_objs[ipahal_ctx->hw_type][i].n_start;
+
+ if (j == ipahal_reg_objs[ipahal_ctx->hw_type][i].n_end)
+ IPAHAL_DBG_REG("%s=0x%x\n", ipahal_reg_name_str(i),
+ ipahal_read_reg_n(i, j));
+
+ for (; j < ipahal_reg_objs[ipahal_ctx->hw_type][i].n_end; j++)
+ IPAHAL_DBG_REG("%s_%u=0x%x\n", ipahal_reg_name_str(i),
+ j, ipahal_read_reg_n(i, j));
+ }
+ return 0;
+}
+
/*
* ipahal_reg_init() - Build the registers information table
* See ipahal_reg_objs[][] comments
diff --git a/drivers/platform/msm/ipa/ipa_v3/ipahal/ipahal_reg.h b/drivers/platform/msm/ipa/ipa_v3/ipahal/ipahal_reg.h
index 2675771..7e8e8ba 100644
--- a/drivers/platform/msm/ipa/ipa_v3/ipahal/ipahal_reg.h
+++ b/drivers/platform/msm/ipa/ipa_v3/ipahal/ipahal_reg.h
@@ -29,6 +29,9 @@
IPA_IRQ_SUSPEND_INFO_EE_n,
IPA_SUSPEND_IRQ_EN_EE_n,
IPA_SUSPEND_IRQ_CLR_EE_n,
+ IPA_HOLB_DROP_IRQ_INFO_EE_n,
+ IPA_HOLB_DROP_IRQ_EN_EE_n,
+ IPA_HOLB_DROP_IRQ_CLR_EE_n,
IPA_BCR,
IPA_ENABLED_PIPES,
IPA_COMP_SW_RESET,
@@ -38,7 +41,20 @@
IPA_SPARE_REG_1,
IPA_SPARE_REG_2,
IPA_COMP_CFG,
+ IPA_STATE_TX_WRAPPER,
+ IPA_STATE_TX1,
+ IPA_STATE_FETCHER,
+ IPA_STATE_FETCHER_MASK,
+ IPA_STATE_DFETCHER,
+ IPA_STATE_ACL,
+ IPA_STATE,
+ IPA_STATE_RX_ACTIVE,
+ IPA_STATE_TX0,
IPA_STATE_AGGR_ACTIVE,
+ IPA_STATE_GSI_TLV,
+ IPA_STATE_GSI_AOS,
+ IPA_STATE_GSI_IF,
+ IPA_STATE_GSI_SKIP,
IPA_ENDP_INIT_HDR_n,
IPA_ENDP_INIT_HDR_EXT_n,
IPA_ENDP_INIT_AGGR_n,
@@ -49,6 +65,7 @@
IPA_ENDP_INIT_CONN_TRACK_n,
IPA_ENDP_INIT_CTRL_n,
IPA_ENDP_INIT_CTRL_SCND_n,
+ IPA_ENDP_INIT_CTRL_STATUS_n,
IPA_ENDP_INIT_HOL_BLOCK_EN_n,
IPA_ENDP_INIT_HOL_BLOCK_TIMER_n,
IPA_ENDP_INIT_DEAGGR_n,
@@ -58,6 +75,7 @@
IPA_IRQ_EE_UC_n,
IPA_ENDP_INIT_HDR_METADATA_MASK_n,
IPA_ENDP_INIT_HDR_METADATA_n,
+ IPA_ENDP_INIT_PROD_CFG_n,
IPA_ENDP_INIT_RSRC_GRP_n,
IPA_SHARED_MEM_SIZE,
IPA_SRAM_DIRECT_ACCESS_n,
@@ -69,6 +87,8 @@
IPA_SYS_PKT_PROC_CNTXT_BASE,
IPA_LOCAL_PKT_PROC_CNTXT_BASE,
IPA_ENDP_STATUS_n,
+ IPA_ENDP_WEIGHTS_n,
+ IPA_ENDP_YELLOW_RED_MARKER,
IPA_ENDP_FILTER_ROUTER_HSH_CFG_n,
IPA_SRC_RSRC_GRP_01_RSRC_TYPE_n,
IPA_SRC_RSRC_GRP_23_RSRC_TYPE_n,
@@ -108,6 +128,12 @@
IPA_STAT_ROUTER_IPV6_END_ID,
IPA_STAT_DROP_CNT_BASE_n,
IPA_STAT_DROP_CNT_MASK_n,
+ IPA_SNOC_FEC_EE_n,
+ IPA_FEC_ADDR_EE_n,
+ IPA_FEC_ADDR_MSB_EE_n,
+ IPA_FEC_ATTR_EE_n,
+ IPA_MBIM_DEAGGR_FEC_ATTR_EE_n,
+ IPA_GEN_DEAGGR_FEC_ATTR_EE_n,
IPA_REG_MAX,
};
@@ -492,6 +518,9 @@
bool endp_delay;
};
+
+int ipahal_print_all_regs(void);
+
/*
* ipahal_reg_name_str() - returns string that represent the register
* @reg_name: [in] register name
diff --git a/drivers/platform/msm/qcom-geni-se.c b/drivers/platform/msm/qcom-geni-se.c
index 348b287..6ce2161 100644
--- a/drivers/platform/msm/qcom-geni-se.c
+++ b/drivers/platform/msm/qcom-geni-se.c
@@ -1,5 +1,5 @@
/*
- * 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
@@ -165,6 +165,38 @@
}
EXPORT_SYMBOL(get_se_proto);
+/**
+ * get_se_m_fw() - Read the Firmware ver for the Main seqeuncer engine
+ * @base: Base address of the serial engine's register block.
+ *
+ * Return: Firmware version for the Main seqeuncer engine
+ */
+int get_se_m_fw(void __iomem *base)
+{
+ int fw_ver_m;
+
+ fw_ver_m = ((geni_read_reg(base, GENI_FW_REVISION_RO)
+ & FW_REV_VERSION_MSK));
+ return fw_ver_m;
+}
+EXPORT_SYMBOL(get_se_m_fw);
+
+/**
+ * get_se_s_fw() - Read the Firmware ver for the Secondry seqeuncer engine
+ * @base: Base address of the serial engine's register block.
+ *
+ * Return: Firmware version for the Secondry seqeuncer engine
+ */
+int get_se_s_fw(void __iomem *base)
+{
+ int fw_ver_s;
+
+ fw_ver_s = ((geni_read_reg(base, GENI_FW_S_REVISION_RO)
+ & FW_REV_VERSION_MSK));
+ return fw_ver_s;
+}
+EXPORT_SYMBOL(get_se_s_fw);
+
static int se_geni_irq_en(void __iomem *base)
{
unsigned int common_geni_m_irq_en;
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/fg-core.h b/drivers/power/supply/qcom/fg-core.h
index 4bd6c4f..f73e647 100644
--- a/drivers/power/supply/qcom/fg-core.h
+++ b/drivers/power/supply/qcom/fg-core.h
@@ -412,6 +412,7 @@
};
struct fg_chip {
+ struct thermal_zone_device *tz_dev;
struct device *dev;
struct pmic_revid_data *pmic_rev_id;
struct regmap *regmap;
diff --git a/drivers/power/supply/qcom/qpnp-fg-gen3.c b/drivers/power/supply/qcom/qpnp-fg-gen3.c
index 5a9b8f6..440bf74 100644
--- a/drivers/power/supply/qcom/qpnp-fg-gen3.c
+++ b/drivers/power/supply/qcom/qpnp-fg-gen3.c
@@ -18,6 +18,7 @@
#include <linux/of_platform.h>
#include <linux/of_batterydata.h>
#include <linux/platform_device.h>
+#include <linux/thermal.h>
#include <linux/iio/consumer.h>
#include <linux/qpnp/qpnp-revid.h>
#include "fg-core.h"
@@ -5355,6 +5356,29 @@
dev_set_drvdata(chip->dev, NULL);
}
+static int fg_tz_get_temp(void *data, int *temperature)
+{
+ struct fg_chip *chip = (struct fg_chip *)data;
+ int rc, batt_temp = 0;
+
+ if (!temperature)
+ return -EINVAL;
+
+ rc = fg_get_battery_temp(chip, &batt_temp);
+ if (rc < 0) {
+ pr_err("Error in getting batt_temp\n");
+ return rc;
+ }
+
+ /* Convert deciDegC to milliDegC */
+ *temperature = batt_temp * 100;
+ return 0;
+}
+
+static struct thermal_zone_of_device_ops fg_gen3_tz_ops = {
+ .get_temp = fg_tz_get_temp,
+};
+
static int fg_gen3_probe(struct platform_device *pdev)
{
struct fg_chip *chip;
@@ -5537,6 +5561,15 @@
pr_err("Error in configuring ESR filter rc:%d\n", rc);
}
+ chip->tz_dev = thermal_zone_of_sensor_register(chip->dev, 0, chip,
+ &fg_gen3_tz_ops);
+ if (IS_ERR_OR_NULL(chip->tz_dev)) {
+ rc = PTR_ERR(chip->tz_dev);
+ chip->tz_dev = NULL;
+ dev_err(chip->dev, "thermal_zone_of_sensor_register() failed rc:%d\n",
+ rc);
+ }
+
device_init_wakeup(chip->dev, true);
schedule_delayed_work(&chip->profile_load_work, 0);
@@ -5601,6 +5634,8 @@
{
struct fg_chip *chip = dev_get_drvdata(&pdev->dev);
+ if (chip->tz_dev)
+ thermal_zone_of_sensor_unregister(chip->dev, chip->tz_dev);
fg_cleanup(chip);
return 0;
}
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/pwm/core.c b/drivers/pwm/core.c
index 172ef82..17b808c 100644
--- a/drivers/pwm/core.c
+++ b/drivers/pwm/core.c
@@ -285,6 +285,7 @@
pwm->pwm = chip->base + i;
pwm->hwpwm = i;
pwm->state.polarity = polarity;
+ pwm->state.output_type = PWM_OUTPUT_FIXED;
if (chip->ops->get_state)
chip->ops->get_state(chip, pwm, &pwm->state);
@@ -498,6 +499,31 @@
pwm->state.polarity = state->polarity;
}
+ if (state->output_type != pwm->state.output_type) {
+ if (!pwm->chip->ops->set_output_type)
+ return -ENOTSUPP;
+
+ err = pwm->chip->ops->set_output_type(pwm->chip, pwm,
+ state->output_type);
+ if (err)
+ return err;
+
+ pwm->state.output_type = state->output_type;
+ }
+
+ if (state->output_pattern != pwm->state.output_pattern &&
+ state->output_pattern != NULL) {
+ if (!pwm->chip->ops->set_output_pattern)
+ return -ENOTSUPP;
+
+ err = pwm->chip->ops->set_output_pattern(pwm->chip,
+ pwm, state->output_pattern);
+ if (err)
+ return err;
+
+ pwm->state.output_pattern = state->output_pattern;
+ }
+
if (state->period != pwm->state.period ||
state->duty_cycle != pwm->state.duty_cycle) {
err = pwm->chip->ops->config(pwm->chip, pwm,
diff --git a/drivers/pwm/pwm-qti-lpg.c b/drivers/pwm/pwm-qti-lpg.c
index 85a5ea0..31f5204 100644
--- a/drivers/pwm/pwm-qti-lpg.c
+++ b/drivers/pwm/pwm-qti-lpg.c
@@ -24,11 +24,16 @@
#include <linux/platform_device.h>
#include <linux/pwm.h>
#include <linux/regmap.h>
+#include <linux/slab.h>
#include <linux/types.h>
#define REG_SIZE_PER_LPG 0x100
+#define LPG_BASE "lpg-base"
+#define LUT_BASE "lut-base"
+/* LPG module registers */
#define REG_LPG_PERPH_SUBTYPE 0x05
+#define REG_LPG_PATTERN_CONFIG 0x40
#define REG_LPG_PWM_SIZE_CLK 0x41
#define REG_LPG_PWM_FREQ_PREDIV_CLK 0x42
#define REG_LPG_PWM_TYPE_CONFIG 0x43
@@ -36,16 +41,29 @@
#define REG_LPG_PWM_VALUE_MSB 0x45
#define REG_LPG_ENABLE_CONTROL 0x46
#define REG_LPG_PWM_SYNC 0x47
+#define REG_LPG_RAMP_STEP_DURATION_LSB 0x50
+#define REG_LPG_RAMP_STEP_DURATION_MSB 0x51
+#define REG_LPG_PAUSE_HI_MULTIPLIER 0x52
+#define REG_LPG_PAUSE_LO_MULTIPLIER 0x54
+#define REG_LPG_HI_INDEX 0x56
+#define REG_LPG_LO_INDEX 0x57
+
+/* REG_LPG_PATTERN_CONFIG */
+#define LPG_PATTERN_EN_PAUSE_LO BIT(0)
+#define LPG_PATTERN_EN_PAUSE_HI BIT(1)
+#define LPG_PATTERN_RAMP_TOGGLE BIT(2)
+#define LPG_PATTERN_REPEAT BIT(3)
+#define LPG_PATTERN_RAMP_LO_TO_HI BIT(4)
/* REG_LPG_PERPH_SUBTYPE */
#define SUBTYPE_PWM 0x0b
#define SUBTYPE_LPG_LITE 0x11
/* REG_LPG_PWM_SIZE_CLK */
-#define LPG_PWM_SIZE_MASK_LPG BIT(4)
-#define LPG_PWM_SIZE_MASK_PWM BIT(2)
-#define LPG_PWM_SIZE_SHIFT_LPG 4
-#define LPG_PWM_SIZE_SHIFT_PWM 2
+#define LPG_PWM_SIZE_LPG_MASK BIT(4)
+#define LPG_PWM_SIZE_PWM_MASK BIT(2)
+#define LPG_PWM_SIZE_LPG_SHIFT 4
+#define LPG_PWM_SIZE_PWM_SHIFT 2
#define LPG_PWM_CLK_FREQ_SEL_MASK GENMASK(1, 0)
/* REG_LPG_PWM_FREQ_PREDIV_CLK */
@@ -64,6 +82,7 @@
/* REG_LPG_ENABLE_CONTROL */
#define LPG_EN_LPG_OUT_BIT BIT(7)
+#define LPG_EN_LPG_OUT_SHIFT 7
#define LPG_PWM_SRC_SELECT_MASK BIT(2)
#define LPG_PWM_SRC_SELECT_SHIFT 2
#define LPG_EN_RAMP_GEN_MASK BIT(1)
@@ -77,9 +96,18 @@
#define NUM_CLK_PREDIV 4
#define NUM_PWM_EXP 8
-enum {
+#define LPG_HI_LO_IDX_MASK GENMASK(5, 0)
+
+/* LUT module registers */
+#define REG_LPG_LUT_1_LSB 0x42
+#define REG_LPG_LUT_RAMP_CONTROL 0xc8
+
+#define LPG_LUT_VALUE_MSB_MASK BIT(0)
+#define LPG_LUT_COUNT_MAX 47
+
+enum lpg_src {
LUT_PATTERN = 0,
- PWM_OUTPUT,
+ PWM_VALUE,
};
static const int pwm_size[NUM_PWM_SIZE] = {6, 9};
@@ -87,6 +115,19 @@
static const int clk_prediv[NUM_CLK_PREDIV] = {1, 3, 5, 6};
static const int pwm_exponent[NUM_PWM_EXP] = {0, 1, 2, 3, 4, 5, 6, 7};
+struct lpg_ramp_config {
+ u16 step_ms;
+ u8 pause_hi_count;
+ u8 pause_lo_count;
+ u8 hi_idx;
+ u8 lo_idx;
+ bool ramp_dir_low_to_hi;
+ bool pattern_repeat;
+ bool toggle;
+ u32 *pattern;
+ u32 pattern_length;
+};
+
struct lpg_pwm_config {
u32 pwm_size;
u32 pwm_clk;
@@ -96,13 +137,23 @@
u32 best_period_ns;
};
+struct qpnp_lpg_lut {
+ struct qpnp_lpg_chip *chip;
+ struct mutex lock;
+ u32 reg_base;
+ u32 *pattern; /* patterns in percentage */
+};
+
struct qpnp_lpg_channel {
struct qpnp_lpg_chip *chip;
struct lpg_pwm_config pwm_config;
+ struct lpg_ramp_config ramp_config;
u32 lpg_idx;
u32 reg_base;
+ u32 max_pattern_length;
u8 src_sel;
u8 subtype;
+ bool lut_written;
int current_period_ns;
int current_duty_ns;
};
@@ -112,6 +163,7 @@
struct regmap *regmap;
struct device *dev;
struct qpnp_lpg_channel *lpgs;
+ struct qpnp_lpg_lut *lut;
struct mutex bus_lock;
u32 num_lpgs;
};
@@ -163,6 +215,36 @@
return rc;
}
+static int qpnp_lut_write(struct qpnp_lpg_lut *lut, u16 addr, u8 val)
+{
+ int rc;
+
+ mutex_lock(&lut->chip->bus_lock);
+ rc = regmap_write(lut->chip->regmap, lut->reg_base + addr, val);
+ if (rc < 0)
+ dev_err(lut->chip->dev, "Write addr 0x%x with value %d failed, rc=%d\n",
+ lut->reg_base + addr, val, rc);
+ mutex_unlock(&lut->chip->bus_lock);
+
+ return rc;
+}
+
+static int qpnp_lut_masked_write(struct qpnp_lpg_lut *lut,
+ u16 addr, u8 mask, u8 val)
+{
+ int rc;
+
+ mutex_lock(&lut->chip->bus_lock);
+ rc = regmap_update_bits(lut->chip->regmap, lut->reg_base + addr,
+ mask, val);
+ if (rc < 0)
+ dev_err(lut->chip->dev, "Update addr 0x%x to val 0x%x with mask 0x%x failed, rc=%d\n",
+ lut->reg_base + addr, val, mask, rc);
+ mutex_unlock(&lut->chip->bus_lock);
+
+ return rc;
+}
+
static struct qpnp_lpg_channel *pwm_dev_to_qpnp_lpg(struct pwm_chip *pwm_chip,
struct pwm_device *pwm) {
@@ -227,11 +309,11 @@
/* pwm_clk_idx is 1 bit lower than the register value */
pwm_clk_idx += 1;
if (lpg->subtype == SUBTYPE_PWM) {
- shift = LPG_PWM_SIZE_SHIFT_PWM;
- mask = LPG_PWM_SIZE_MASK_PWM;
+ shift = LPG_PWM_SIZE_PWM_SHIFT;
+ mask = LPG_PWM_SIZE_PWM_MASK;
} else {
- shift = LPG_PWM_SIZE_SHIFT_LPG;
- mask = LPG_PWM_SIZE_MASK_LPG;
+ shift = LPG_PWM_SIZE_LPG_SHIFT;
+ mask = LPG_PWM_SIZE_LPG_MASK;
}
val = pwm_size_idx << shift | pwm_clk_idx;
@@ -252,6 +334,9 @@
return rc;
}
+ if (lpg->src_sel == LUT_PATTERN)
+ return 0;
+
val = lpg->pwm_config.pwm_value & LPG_PWM_VALUE_LSB_MASK;
rc = qpnp_lpg_write(lpg, REG_LPG_PWM_VALUE_LSB, val);
if (rc < 0) {
@@ -280,6 +365,145 @@
return rc;
}
+static int qpnp_lpg_set_lut_pattern(struct qpnp_lpg_channel *lpg,
+ unsigned int *pattern, unsigned int length)
+{
+ struct qpnp_lpg_lut *lut = lpg->chip->lut;
+ int i, rc = 0;
+ u16 full_duty_value, pwm_values[LPG_LUT_COUNT_MAX + 1] = {0};
+ u8 lsb, msb, addr;
+
+ if (length > lpg->max_pattern_length) {
+ dev_err(lpg->chip->dev, "new pattern length (%d) larger than predefined (%d)\n",
+ length, lpg->max_pattern_length);
+ return -EINVAL;
+ }
+
+ /* Program LUT pattern */
+ mutex_lock(&lut->lock);
+ addr = REG_LPG_LUT_1_LSB + lpg->ramp_config.lo_idx * 2;
+ for (i = 0; i < length; i++) {
+ full_duty_value = 1 << lpg->pwm_config.pwm_size;
+ pwm_values[i] = pattern[i] * full_duty_value / 100;
+
+ if (unlikely(pwm_values[i] > full_duty_value)) {
+ dev_err(lpg->chip->dev, "PWM value %d exceed the max %d\n",
+ pwm_values[i], full_duty_value);
+ rc = -EINVAL;
+ goto unlock;
+ }
+
+ if (pwm_values[i] == full_duty_value)
+ pwm_values[i] = full_duty_value - 1;
+
+ lsb = pwm_values[i] & 0xff;
+ msb = pwm_values[i] >> 8;
+ rc = qpnp_lut_write(lut, addr++, lsb);
+ if (rc < 0) {
+ dev_err(lpg->chip->dev, "Write NO.%d LUT pattern LSB (%d) failed, rc=%d",
+ i, lsb, rc);
+ goto unlock;
+ }
+
+ rc = qpnp_lut_masked_write(lut, addr++,
+ LPG_LUT_VALUE_MSB_MASK, msb);
+ if (rc < 0) {
+ dev_err(lpg->chip->dev, "Write NO.%d LUT pattern MSB (%d) failed, rc=%d",
+ i, msb, rc);
+ goto unlock;
+ }
+ }
+ lpg->ramp_config.pattern_length = length;
+unlock:
+ mutex_unlock(&lut->lock);
+
+ return rc;
+}
+
+static int qpnp_lpg_set_ramp_config(struct qpnp_lpg_channel *lpg)
+{
+ struct lpg_ramp_config *ramp = &lpg->ramp_config;
+ u8 lsb, msb, addr, mask, val;
+ int rc = 0;
+
+ /* Set ramp step duration */
+ lsb = ramp->step_ms & 0xff;
+ msb = ramp->step_ms >> 8;
+ addr = REG_LPG_RAMP_STEP_DURATION_LSB;
+ rc = qpnp_lpg_write(lpg, addr, lsb);
+ if (rc < 0) {
+ dev_err(lpg->chip->dev, "Write RAMP_STEP_DURATION_LSB failed, rc=%d\n",
+ rc);
+ return rc;
+ }
+ rc = qpnp_lpg_write(lpg, addr + 1, msb);
+ if (rc < 0) {
+ dev_err(lpg->chip->dev, "Write RAMP_STEP_DURATION_MSB failed, rc=%d\n",
+ rc);
+ return rc;
+ }
+
+ /* Set hi_idx and lo_idx */
+ rc = qpnp_lpg_masked_write(lpg, REG_LPG_HI_INDEX,
+ LPG_HI_LO_IDX_MASK, ramp->hi_idx);
+ if (rc < 0) {
+ dev_err(lpg->chip->dev, "Write LPG_HI_IDX failed, rc=%d\n",
+ rc);
+ return rc;
+ }
+
+ rc = qpnp_lpg_masked_write(lpg, REG_LPG_LO_INDEX,
+ LPG_HI_LO_IDX_MASK, ramp->lo_idx);
+ if (rc < 0) {
+ dev_err(lpg->chip->dev, "Write LPG_LO_IDX failed, rc=%d\n",
+ rc);
+ return rc;
+ }
+
+ /* Set pause_hi/lo_count */
+ rc = qpnp_lpg_write(lpg, REG_LPG_PAUSE_HI_MULTIPLIER,
+ ramp->pause_hi_count);
+ if (rc < 0) {
+ dev_err(lpg->chip->dev, "Write LPG_PAUSE_HI_MULTIPLIER failed, rc=%d\n",
+ rc);
+ return rc;
+ }
+
+ rc = qpnp_lpg_write(lpg, REG_LPG_PAUSE_LO_MULTIPLIER,
+ ramp->pause_lo_count);
+ if (rc < 0) {
+ dev_err(lpg->chip->dev, "Write LPG_PAUSE_LO_MULTIPLIER failed, rc=%d\n",
+ rc);
+ return rc;
+ }
+
+ /* Set LPG_PATTERN_CONFIG */
+ addr = REG_LPG_PATTERN_CONFIG;
+ mask = LPG_PATTERN_EN_PAUSE_LO | LPG_PATTERN_EN_PAUSE_HI
+ | LPG_PATTERN_RAMP_TOGGLE | LPG_PATTERN_REPEAT
+ | LPG_PATTERN_RAMP_LO_TO_HI;
+ val = 0;
+ if (ramp->pause_lo_count != 0)
+ val |= LPG_PATTERN_EN_PAUSE_LO;
+ if (ramp->pause_hi_count != 0)
+ val |= LPG_PATTERN_EN_PAUSE_HI;
+ if (ramp->ramp_dir_low_to_hi)
+ val |= LPG_PATTERN_RAMP_LO_TO_HI;
+ if (ramp->pattern_repeat)
+ val |= LPG_PATTERN_REPEAT;
+ if (ramp->toggle)
+ val |= LPG_PATTERN_RAMP_TOGGLE;
+
+ rc = qpnp_lpg_masked_write(lpg, addr, mask, val);
+ if (rc < 0) {
+ dev_err(lpg->chip->dev, "Write LPG_PATTERN_CONFIG failed, rc=%d\n",
+ rc);
+ return rc;
+ }
+
+ return rc;
+}
+
static void __qpnp_lpg_calc_pwm_period(int period_ns,
struct lpg_pwm_config *pwm_config)
{
@@ -396,17 +620,202 @@
return -EINVAL;
}
- if (period_ns != lpg->current_period_ns)
+ if (period_ns != lpg->current_period_ns) {
__qpnp_lpg_calc_pwm_period(period_ns, &lpg->pwm_config);
+ /* program LUT if PWM period is changed */
+ if (lpg->src_sel == LUT_PATTERN) {
+ rc = qpnp_lpg_set_lut_pattern(lpg,
+ lpg->ramp_config.pattern,
+ lpg->ramp_config.pattern_length);
+ if (rc < 0) {
+ dev_err(pwm_chip->dev, "set LUT pattern failed for LPG%d, rc=%d\n",
+ lpg->lpg_idx, rc);
+ return rc;
+ }
+ lpg->lut_written = true;
+ }
+ }
+
if (period_ns != lpg->current_period_ns ||
duty_ns != lpg->current_duty_ns)
__qpnp_lpg_calc_pwm_duty(period_ns, duty_ns, &lpg->pwm_config);
rc = qpnp_lpg_set_pwm_config(lpg);
- if (rc < 0)
+ if (rc < 0) {
dev_err(pwm_chip->dev, "Config PWM failed for channel %d, rc=%d\n",
lpg->lpg_idx, rc);
+ return rc;
+ }
+
+ lpg->current_period_ns = period_ns;
+ lpg->current_duty_ns = duty_ns;
+
+ return rc;
+}
+
+static int qpnp_lpg_pwm_src_enable(struct qpnp_lpg_channel *lpg, bool en)
+{
+ struct qpnp_lpg_chip *chip = lpg->chip;
+ struct qpnp_lpg_lut *lut = chip->lut;
+ u8 mask, val;
+ int rc;
+
+ mask = LPG_PWM_SRC_SELECT_MASK | LPG_EN_LPG_OUT_BIT |
+ LPG_EN_RAMP_GEN_MASK;
+ val = lpg->src_sel << LPG_PWM_SRC_SELECT_SHIFT;
+
+ if (lpg->src_sel == LUT_PATTERN)
+ val |= 1 << LPG_EN_RAMP_GEN_SHIFT;
+
+ if (en)
+ val |= 1 << LPG_EN_LPG_OUT_SHIFT;
+
+ rc = qpnp_lpg_masked_write(lpg, REG_LPG_ENABLE_CONTROL, mask, val);
+ if (rc < 0) {
+ dev_err(chip->dev, "Write LPG_ENABLE_CONTROL failed, rc=%d\n",
+ rc);
+ return rc;
+ }
+
+ if (lpg->src_sel == LUT_PATTERN && en) {
+ mutex_lock(&lut->lock);
+ val = 1 << lpg->lpg_idx;
+ rc = qpnp_lut_write(lut, REG_LPG_LUT_RAMP_CONTROL, val);
+ if (rc < 0)
+ dev_err(chip->dev, "Write LPG_LUT_RAMP_CONTROL failed, rc=%d\n",
+ rc);
+ mutex_unlock(&lut->lock);
+ }
+
+ return rc;
+}
+
+static int qpnp_lpg_pwm_set_output_type(struct pwm_chip *pwm_chip,
+ struct pwm_device *pwm, enum pwm_output_type output_type)
+{
+ struct qpnp_lpg_channel *lpg;
+ enum lpg_src src_sel;
+ int rc;
+
+ lpg = pwm_dev_to_qpnp_lpg(pwm_chip, pwm);
+ if (lpg == NULL) {
+ dev_err(pwm_chip->dev, "lpg not found\n");
+ return -ENODEV;
+ }
+
+ if (lpg->chip->lut == NULL) {
+ pr_debug("lpg%d only support PWM mode\n", lpg->lpg_idx);
+ return 0;
+ }
+
+ src_sel = (output_type == PWM_OUTPUT_MODULATED) ?
+ LUT_PATTERN : PWM_VALUE;
+ if (src_sel == lpg->src_sel)
+ return 0;
+
+ if (src_sel == LUT_PATTERN) {
+ /* program LUT if it's never been programmed */
+ if (!lpg->lut_written) {
+ rc = qpnp_lpg_set_lut_pattern(lpg,
+ lpg->ramp_config.pattern,
+ lpg->ramp_config.pattern_length);
+ if (rc < 0) {
+ dev_err(pwm_chip->dev, "set LUT pattern failed for LPG%d, rc=%d\n",
+ lpg->lpg_idx, rc);
+ return rc;
+ }
+ lpg->lut_written = true;
+ }
+
+ rc = qpnp_lpg_set_ramp_config(lpg);
+ if (rc < 0) {
+ dev_err(pwm_chip->dev, "Config LPG%d ramping failed, rc=%d\n",
+ lpg->lpg_idx, rc);
+ return rc;
+ }
+ }
+
+ lpg->src_sel = src_sel;
+
+ if (pwm_is_enabled(pwm)) {
+ rc = qpnp_lpg_pwm_src_enable(lpg, true);
+ if (rc < 0) {
+ dev_err(pwm_chip->dev, "Enable PWM output failed for channel %d, rc=%d\n",
+ lpg->lpg_idx, rc);
+ return rc;
+ }
+ }
+
+ return 0;
+}
+
+static int qpnp_lpg_pwm_set_output_pattern(struct pwm_chip *pwm_chip,
+ struct pwm_device *pwm, struct pwm_output_pattern *output_pattern)
+{
+ struct qpnp_lpg_channel *lpg;
+ int rc = 0, i, period_ns, duty_ns;
+ u32 *percentages;
+
+ lpg = pwm_dev_to_qpnp_lpg(pwm_chip, pwm);
+ if (lpg == NULL) {
+ dev_err(pwm_chip->dev, "lpg not found\n");
+ return -ENODEV;
+ }
+
+ if (output_pattern->num_entries > lpg->max_pattern_length) {
+ dev_err(lpg->chip->dev, "pattern length %d shouldn't exceed %d\n",
+ output_pattern->num_entries,
+ lpg->max_pattern_length);
+ return -EINVAL;
+ }
+
+ percentages = kcalloc(output_pattern->num_entries,
+ sizeof(u32), GFP_KERNEL);
+ if (!percentages)
+ return -ENOMEM;
+
+ period_ns = pwm_get_period(pwm);
+ for (i = 0; i < output_pattern->num_entries; i++) {
+ duty_ns = output_pattern->duty_pattern[i];
+ if (duty_ns > period_ns) {
+ dev_err(lpg->chip->dev, "duty %dns is larger than period %dns\n",
+ duty_ns, period_ns);
+ goto err;
+ }
+ /* Translate the pattern in duty_ns to percentage */
+ if ((INT_MAX / duty_ns) < 100)
+ percentages[i] = duty_ns / (period_ns / 100);
+ else
+ percentages[i] = (duty_ns * 100) / period_ns;
+ }
+
+ rc = qpnp_lpg_set_lut_pattern(lpg, percentages,
+ output_pattern->num_entries);
+ if (rc < 0) {
+ dev_err(lpg->chip->dev, "Set LUT pattern failed for LPG%d, rc=%d\n",
+ lpg->lpg_idx, rc);
+ goto err;
+ }
+
+ lpg->lut_written = true;
+ memcpy(lpg->ramp_config.pattern, percentages,
+ output_pattern->num_entries);
+ lpg->ramp_config.hi_idx = lpg->ramp_config.lo_idx +
+ output_pattern->num_entries - 1;
+ if ((INT_MAX / period_ns) > output_pattern->cycles_per_duty)
+ lpg->ramp_config.step_ms = output_pattern->cycles_per_duty *
+ period_ns / NSEC_PER_MSEC;
+ else
+ lpg->ramp_config.step_ms = (period_ns / NSEC_PER_MSEC) *
+ output_pattern->cycles_per_duty;
+
+ rc = qpnp_lpg_set_ramp_config(lpg);
+ if (rc < 0)
+ dev_err(pwm_chip->dev, "Config LPG%d ramping failed, rc=%d\n",
+ lpg->lpg_idx, rc);
+err:
+ kfree(percentages);
return rc;
}
@@ -416,7 +825,6 @@
{
struct qpnp_lpg_channel *lpg;
int rc = 0;
- u8 mask, val;
lpg = pwm_dev_to_qpnp_lpg(pwm_chip, pwm);
if (lpg == NULL) {
@@ -431,10 +839,7 @@
return rc;
}
- mask = LPG_PWM_SRC_SELECT_MASK | LPG_EN_LPG_OUT_BIT;
- val = lpg->src_sel << LPG_PWM_SRC_SELECT_SHIFT | LPG_EN_LPG_OUT_BIT;
-
- rc = qpnp_lpg_masked_write(lpg, REG_LPG_ENABLE_CONTROL, mask, val);
+ rc = qpnp_lpg_pwm_src_enable(lpg, true);
if (rc < 0)
dev_err(pwm_chip->dev, "Enable PWM output failed for channel %d, rc=%d\n",
lpg->lpg_idx, rc);
@@ -447,7 +852,6 @@
{
struct qpnp_lpg_channel *lpg;
int rc;
- u8 mask, val;
lpg = pwm_dev_to_qpnp_lpg(pwm_chip, pwm);
if (lpg == NULL) {
@@ -455,10 +859,7 @@
return;
}
- mask = LPG_PWM_SRC_SELECT_MASK | LPG_EN_LPG_OUT_BIT;
- val = lpg->src_sel << LPG_PWM_SRC_SELECT_SHIFT;
-
- rc = qpnp_lpg_masked_write(lpg, REG_LPG_ENABLE_CONTROL, mask, val);
+ rc = qpnp_lpg_pwm_src_enable(lpg, false);
if (rc < 0) {
dev_err(pwm_chip->dev, "Disable PWM output failed for channel %d, rc=%d\n",
lpg->lpg_idx, rc);
@@ -471,13 +872,32 @@
rc);
}
+static int qpnp_lpg_pwm_output_types_supported(struct pwm_chip *pwm_chip,
+ struct pwm_device *pwm)
+{
+ enum pwm_output_type type = PWM_OUTPUT_FIXED;
+ struct qpnp_lpg_channel *lpg;
+
+ lpg = pwm_dev_to_qpnp_lpg(pwm_chip, pwm);
+ if (lpg == NULL) {
+ dev_err(pwm_chip->dev, "lpg not found\n");
+ return type;
+ }
+
+ if (lpg->chip->lut != NULL)
+ type |= PWM_OUTPUT_MODULATED;
+
+ return type;
+}
+
#ifdef CONFIG_DEBUG_FS
static void qpnp_lpg_pwm_dbg_show(struct pwm_chip *pwm_chip, struct seq_file *s)
{
struct qpnp_lpg_channel *lpg;
struct lpg_pwm_config *cfg;
+ struct lpg_ramp_config *ramp;
struct pwm_device *pwm;
- int i;
+ int i, j;
for (i = 0; i < pwm_chip->npwm; i++) {
pwm = &pwm_chip->pwms[i];
@@ -512,12 +932,39 @@
seq_printf(s, " pwm_value = %d\n", cfg->pwm_value);
seq_printf(s, " Requested period: %dns, best period = %dns\n",
pwm_get_period(pwm), cfg->best_period_ns);
+
+ ramp = &lpg->ramp_config;
+ if (pwm_get_output_type(pwm) == PWM_OUTPUT_MODULATED) {
+ seq_puts(s, " ramping duty percentages:");
+ for (j = 0; j < ramp->pattern_length; j++)
+ seq_printf(s, " %d", ramp->pattern[j]);
+ seq_puts(s, "\n");
+ seq_printf(s, " ramping time per step: %dms\n",
+ ramp->step_ms);
+ seq_printf(s, " ramping low index: %d\n",
+ ramp->lo_idx);
+ seq_printf(s, " ramping high index: %d\n",
+ ramp->hi_idx);
+ seq_printf(s, " ramping from low to high: %d\n",
+ ramp->ramp_dir_low_to_hi);
+ seq_printf(s, " ramping pattern repeat: %d\n",
+ ramp->pattern_repeat);
+ seq_printf(s, " ramping toggle: %d\n",
+ ramp->toggle);
+ seq_printf(s, " ramping pause count at low index: %d\n",
+ ramp->pause_lo_count);
+ seq_printf(s, " ramping pause count at high index: %d\n",
+ ramp->pause_hi_count);
+ }
}
}
#endif
static const struct pwm_ops qpnp_lpg_pwm_ops = {
.config = qpnp_lpg_pwm_config,
+ .get_output_type_supported = qpnp_lpg_pwm_output_types_supported,
+ .set_output_type = qpnp_lpg_pwm_set_output_type,
+ .set_output_pattern = qpnp_lpg_pwm_set_output_pattern,
.enable = qpnp_lpg_pwm_enable,
.disable = qpnp_lpg_pwm_disable,
#ifdef CONFIG_DEBUG_FS
@@ -528,15 +975,19 @@
static int qpnp_lpg_parse_dt(struct qpnp_lpg_chip *chip)
{
+ struct device_node *child;
+ struct qpnp_lpg_channel *lpg;
+ struct lpg_ramp_config *ramp;
int rc = 0, i;
- u64 base, length;
+ u32 base, length, lpg_chan_id, tmp;
const __be32 *addr;
addr = of_get_address(chip->dev->of_node, 0, NULL, NULL);
if (!addr) {
- dev_err(chip->dev, "Getting address failed\n");
+ dev_err(chip->dev, "Get %s address failed\n", LPG_BASE);
return -EINVAL;
}
+
base = be32_to_cpu(addr[0]);
length = be32_to_cpu(addr[1]);
@@ -550,7 +1001,7 @@
chip->lpgs[i].chip = chip;
chip->lpgs[i].lpg_idx = i;
chip->lpgs[i].reg_base = base + i * REG_SIZE_PER_LPG;
- chip->lpgs[i].src_sel = PWM_OUTPUT;
+ chip->lpgs[i].src_sel = PWM_VALUE;
rc = qpnp_lpg_read(&chip->lpgs[i], REG_LPG_PERPH_SUBTYPE,
&chip->lpgs[i].subtype);
if (rc < 0) {
@@ -559,7 +1010,142 @@
}
}
- return rc;
+ addr = of_get_address(chip->dev->of_node, 1, NULL, NULL);
+ if (!addr) {
+ pr_debug("NO LUT address assigned\n");
+ return 0;
+ }
+
+ chip->lut = devm_kmalloc(chip->dev, sizeof(*chip->lut), GFP_KERNEL);
+ if (!chip->lut)
+ return -ENOMEM;
+
+ chip->lut->chip = chip;
+ chip->lut->reg_base = be32_to_cpu(*addr);
+ mutex_init(&chip->lut->lock);
+
+ rc = of_property_count_elems_of_size(chip->dev->of_node,
+ "qcom,lut-patterns", sizeof(u32));
+ if (rc < 0) {
+ dev_err(chip->dev, "Read qcom,lut-patterns failed, rc=%d\n",
+ rc);
+ return rc;
+ }
+
+ length = rc;
+ if (length > LPG_LUT_COUNT_MAX) {
+ dev_err(chip->dev, "qcom,lut-patterns length %d exceed max %d\n",
+ length, LPG_LUT_COUNT_MAX);
+ return -EINVAL;
+ }
+
+ chip->lut->pattern = devm_kcalloc(chip->dev, LPG_LUT_COUNT_MAX,
+ sizeof(*chip->lut->pattern), GFP_KERNEL);
+ if (!chip->lut->pattern)
+ return -ENOMEM;
+
+ rc = of_property_read_u32_array(chip->dev->of_node, "qcom,lut-patterns",
+ chip->lut->pattern, length);
+ if (rc < 0) {
+ dev_err(chip->dev, "Get qcom,lut-patterns failed, rc=%d\n",
+ rc);
+ return rc;
+ }
+
+ if (of_get_available_child_count(chip->dev->of_node) == 0) {
+ dev_err(chip->dev, "No ramp configuration for any LPG\n");
+ return -EINVAL;
+ }
+
+ for_each_available_child_of_node(chip->dev->of_node, child) {
+ rc = of_property_read_u32(child, "qcom,lpg-chan-id",
+ &lpg_chan_id);
+ if (rc < 0) {
+ dev_err(chip->dev, "Get qcom,lpg-chan-id failed for node %s, rc=%d\n",
+ child->name, rc);
+ return rc;
+ }
+
+ if (lpg_chan_id > chip->num_lpgs) {
+ dev_err(chip->dev, "lpg-chann-id %d is out of range 1~%d\n",
+ lpg_chan_id, chip->num_lpgs);
+ return -EINVAL;
+ }
+
+ /* lpg channel id is indexed from 1 in hardware */
+ lpg = &chip->lpgs[lpg_chan_id - 1];
+ ramp = &lpg->ramp_config;
+
+ rc = of_property_read_u32(child, "qcom,ramp-step-ms", &tmp);
+ if (rc < 0) {
+ dev_err(chip->dev, "get qcom,ramp-step-ms failed for lpg%d, rc=%d\n",
+ lpg_chan_id, rc);
+ return rc;
+ }
+ ramp->step_ms = (u16)tmp;
+
+ rc = of_property_read_u32(child, "qcom,ramp-low-index", &tmp);
+ if (rc < 0) {
+ dev_err(chip->dev, "get qcom,ramp-low-index failed for lpg%d, rc=%d\n",
+ lpg_chan_id, rc);
+ return rc;
+ }
+ ramp->lo_idx = (u8)tmp;
+ if (ramp->lo_idx >= LPG_LUT_COUNT_MAX) {
+ dev_err(chip->dev, "qcom,ramp-low-index should less than max %d\n",
+ LPG_LUT_COUNT_MAX);
+ return -EINVAL;
+ }
+
+ rc = of_property_read_u32(child, "qcom,ramp-high-index", &tmp);
+ if (rc < 0) {
+ dev_err(chip->dev, "get qcom,ramp-high-index failed for lpg%d, rc=%d\n",
+ lpg_chan_id, rc);
+ return rc;
+ }
+ ramp->hi_idx = (u8)tmp;
+
+ if (ramp->hi_idx > LPG_LUT_COUNT_MAX) {
+ dev_err(chip->dev, "qcom,ramp-high-index shouldn't exceed max %d\n",
+ LPG_LUT_COUNT_MAX);
+ return -EINVAL;
+ }
+
+ if (ramp->hi_idx <= ramp->lo_idx) {
+ dev_err(chip->dev, "high-index(%d) should be larger than low-index(%d)\n",
+ ramp->hi_idx, ramp->lo_idx);
+ return -EINVAL;
+ }
+
+ ramp->pattern_length = ramp->hi_idx - ramp->lo_idx + 1;
+ ramp->pattern = &chip->lut->pattern[ramp->lo_idx];
+ lpg->max_pattern_length = ramp->pattern_length;
+
+ rc = of_property_read_u32(child,
+ "qcom,ramp-pause-hi-count", &tmp);
+ if (rc < 0)
+ ramp->pause_hi_count = 0;
+ else
+ ramp->pause_hi_count = (u8)tmp;
+
+ rc = of_property_read_u32(child,
+ "qcom,ramp-pause-lo-count", &tmp);
+ if (rc < 0)
+ ramp->pause_lo_count = 0;
+ else
+ ramp->pause_lo_count = (u8)tmp;
+
+ ramp->ramp_dir_low_to_hi = of_property_read_bool(child,
+ "qcom,ramp-from-low-to-high");
+
+ ramp->pattern_repeat = of_property_read_bool(child,
+ "qcom,ramp-pattern-repeat");
+
+ ramp->toggle = of_property_read_bool(child,
+ "qcom,ramp-toggle");
+ }
+
+ return 0;
}
static int qpnp_lpg_probe(struct platform_device *pdev)
@@ -583,7 +1169,7 @@
if (rc < 0) {
dev_err(chip->dev, "Devicetree properties parsing failed, rc=%d\n",
rc);
- goto destroy;
+ goto err_out;
}
dev_set_drvdata(chip->dev, chip);
@@ -595,11 +1181,11 @@
rc = pwmchip_add(&chip->pwm_chip);
if (rc < 0) {
dev_err(chip->dev, "Add pwmchip failed, rc=%d\n", rc);
- goto destroy;
+ goto err_out;
}
return 0;
-destroy:
+err_out:
mutex_destroy(&chip->bus_lock);
return rc;
}
diff --git a/drivers/pwm/sysfs.c b/drivers/pwm/sysfs.c
index a813239..ea2b53d 100644
--- a/drivers/pwm/sysfs.c
+++ b/drivers/pwm/sysfs.c
@@ -223,11 +223,60 @@
return sprintf(buf, "%u %u\n", result.period, result.duty_cycle);
}
+static ssize_t output_type_show(struct device *child,
+ struct device_attribute *attr,
+ char *buf)
+{
+ const struct pwm_device *pwm = child_to_pwm_device(child);
+ const char *output_type = "unknown";
+ struct pwm_state state;
+
+ pwm_get_state(pwm, &state);
+ switch (state.output_type) {
+ case PWM_OUTPUT_FIXED:
+ output_type = "fixed";
+ break;
+ case PWM_OUTPUT_MODULATED:
+ output_type = "modulated";
+ break;
+ default:
+ break;
+ }
+
+ return snprintf(buf, PAGE_SIZE, "%s\n", output_type);
+}
+
+static ssize_t output_type_store(struct device *child,
+ struct device_attribute *attr,
+ const char *buf, size_t size)
+{
+ struct pwm_export *export = child_to_pwm_export(child);
+ struct pwm_device *pwm = export->pwm;
+ struct pwm_state state;
+ int ret = -EINVAL;
+
+ mutex_lock(&export->lock);
+ pwm_get_state(pwm, &state);
+ if (sysfs_streq(buf, "fixed"))
+ state.output_type = PWM_OUTPUT_FIXED;
+ else if (sysfs_streq(buf, "modulated"))
+ state.output_type = PWM_OUTPUT_MODULATED;
+ else
+ goto unlock;
+
+ ret = pwm_apply_state(pwm, &state);
+unlock:
+ mutex_unlock(&export->lock);
+
+ return ret ? : size;
+}
+
static DEVICE_ATTR_RW(period);
static DEVICE_ATTR_RW(duty_cycle);
static DEVICE_ATTR_RW(enable);
static DEVICE_ATTR_RW(polarity);
static DEVICE_ATTR_RO(capture);
+static DEVICE_ATTR_RW(output_type);
static struct attribute *pwm_attrs[] = {
&dev_attr_period.attr,
@@ -235,6 +284,7 @@
&dev_attr_enable.attr,
&dev_attr_polarity.attr,
&dev_attr_capture.attr,
+ &dev_attr_output_type.attr,
NULL
};
ATTRIBUTE_GROUPS(pwm);
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/tty/serial/msm_geni_serial.c b/drivers/tty/serial/msm_geni_serial.c
index 148568b..f76b3db 100644
--- a/drivers/tty/serial/msm_geni_serial.c
+++ b/drivers/tty/serial/msm_geni_serial.c
@@ -96,6 +96,7 @@
/* UART S_CMD OP codes */
#define UART_START_READ (0x1)
#define UART_PARAM (0x1)
+#define UART_PARAM_RFR_OPEN (BIT(7))
/* UART DMA Rx GP_IRQ_BITS */
#define UART_DMA_RX_PARITY_ERR BIT(5)
@@ -613,6 +614,27 @@
geni_write_reg(FORCE_DEFAULT, uport->membase, GENI_FORCE_DEFAULT_REG);
}
+static void msm_geni_serial_complete_rx_eot(struct uart_port *uport)
+{
+ int poll_done = 0, tries = 0;
+ u32 geni_status = 0;
+ struct msm_geni_serial_port *port = GET_DEV_PORT(uport);
+
+ do {
+ poll_done = msm_geni_serial_poll_bit(uport, SE_DMA_RX_IRQ_STAT,
+ RX_EOT, true);
+ tries++;
+ } while (!poll_done && tries < 5);
+
+ geni_status = geni_read_reg_nolog(uport->membase, SE_GENI_STATUS);
+
+ if (!poll_done)
+ IPC_LOG_MSG(port->ipc_log_misc, "%s: RX_EOT, GENI:0x%x\n",
+ __func__, geni_status);
+ else
+ geni_write_reg_nolog(RX_EOT, uport->membase, SE_DMA_RX_IRQ_CLR);
+}
+
#ifdef CONFIG_CONSOLE_POLL
static int msm_geni_serial_get_char(struct uart_port *uport)
{
@@ -999,12 +1021,14 @@
unsigned int geni_status;
struct msm_geni_serial_port *port = GET_DEV_PORT(uport);
int ret;
+ u32 geni_se_param = UART_PARAM_RFR_OPEN;
geni_status = geni_read_reg_nolog(uport->membase, SE_GENI_STATUS);
if (geni_status & S_GENI_CMD_ACTIVE)
msm_geni_serial_stop_rx(uport);
- geni_setup_s_cmd(uport->membase, UART_START_READ, 0);
+ /* Start RX with the RFR_OPEN to keep RFR in always ready state */
+ geni_setup_s_cmd(uport->membase, UART_START_READ, geni_se_param);
if (port->xfer_mode == FIFO_MODE) {
geni_s_irq_en = geni_read_reg_nolog(uport->membase,
@@ -1078,7 +1102,7 @@
unsigned int geni_m_irq_en;
unsigned int geni_status;
struct msm_geni_serial_port *port = GET_DEV_PORT(uport);
- u32 irq_clear = S_CMD_DONE_EN;
+ u32 irq_clear = S_CMD_CANCEL_EN;
bool done;
IPC_LOG_MSG(port->ipc_log_misc, "%s\n", __func__);
@@ -1100,22 +1124,31 @@
/* Possible stop rx is called multiple times. */
if (!(geni_status & S_GENI_CMD_ACTIVE))
goto exit_rx_seq;
+
geni_cancel_s_cmd(uport->membase);
/*
* Ensure that the cancel goes through before polling for the
* cancel control bit.
*/
mb();
+ msm_geni_serial_complete_rx_eot(uport);
done = msm_geni_serial_poll_bit(uport, SE_GENI_S_CMD_CTRL_REG,
S_GENI_CMD_CANCEL, false);
- geni_status = geni_read_reg_nolog(uport->membase, SE_GENI_STATUS);
- if (!done)
+ if (done) {
+ geni_write_reg_nolog(irq_clear, uport->membase,
+ SE_GENI_S_IRQ_CLEAR);
+ goto exit_rx_seq;
+ } else {
IPC_LOG_MSG(port->ipc_log_misc, "%s Cancel fail 0x%x\n",
- __func__, geni_status);
+ __func__, geni_status);
+ }
- geni_write_reg_nolog(irq_clear, uport->membase, SE_GENI_S_IRQ_CLEAR);
- if ((geni_status & S_GENI_CMD_ACTIVE))
+ geni_status = geni_read_reg_nolog(uport->membase, SE_GENI_STATUS);
+ if ((geni_status & S_GENI_CMD_ACTIVE)) {
+ IPC_LOG_MSG(port->ipc_log_misc, "%s:Abort Rx, GENI:0x%x\n",
+ __func__, geni_status);
msm_geni_serial_abort_rx(uport);
+ }
exit_rx_seq:
if (port->xfer_mode == SE_DMA && port->rx_dma) {
msm_geni_serial_rx_fsm_rst(uport);
@@ -1693,6 +1726,9 @@
ret = -ENXIO;
goto exit_startup;
}
+ IPC_LOG_MSG(msm_port->ipc_log_misc, "%s: FW Ver:0x%x%x\n",
+ __func__,
+ get_se_m_fw(uport->membase), get_se_s_fw(uport->membase));
get_tx_fifo_size(msm_port);
if (!msm_port->port_setup) {
@@ -2525,7 +2561,6 @@
struct platform_device *pdev = to_platform_device(dev);
struct msm_geni_serial_port *port = platform_get_drvdata(pdev);
int ret = 0;
- u32 uart_manual_rfr = 0;
u32 geni_status = geni_read_reg_nolog(port->uport.membase,
SE_GENI_STATUS);
@@ -2537,28 +2572,8 @@
* Resources off
*/
disable_irq(port->uport.irq);
- /*
- * If the clients haven't done a manual flow on/off then go ahead and
- * set this to manual flow on.
- */
- if (!port->manual_flow) {
- u32 geni_ios;
-
- uart_manual_rfr |= (UART_MANUAL_RFR_EN | UART_RFR_READY);
- geni_write_reg_nolog(uart_manual_rfr, port->uport.membase,
- SE_UART_MANUAL_RFR);
- /*
- * Ensure that the manual flow on writes go through before
- * doing a stop_rx else we could end up flowing off the peer.
- */
- mb();
- geni_ios = geni_read_reg_nolog(port->uport.membase,
- SE_GENI_IOS);
- IPC_LOG_MSG(port->ipc_log_pwr, "%s: Manual Flow ON 0x%x 0x%x\n",
- __func__, uart_manual_rfr, geni_ios);
- udelay(10);
- }
stop_rx_sequencer(&port->uport);
+ geni_status = geni_read_reg_nolog(port->uport.membase, SE_GENI_STATUS);
if ((geni_status & M_GENI_CMD_ACTIVE))
stop_tx_sequencer(&port->uport);
ret = se_geni_resources_off(&port->serial_rsc);
@@ -2603,9 +2618,6 @@
goto exit_runtime_resume;
}
start_rx_sequencer(&port->uport);
- if (!port->manual_flow)
- geni_write_reg_nolog(0, port->uport.membase,
- SE_UART_MANUAL_RFR);
/* Ensure that the Rx is running before enabling interrupts */
mb();
if (pm_runtime_enabled(dev))
diff --git a/drivers/usb/dwc3/dwc3-msm.c b/drivers/usb/dwc3/dwc3-msm.c
index 3650b98..989956d 100644
--- a/drivers/usb/dwc3/dwc3-msm.c
+++ b/drivers/usb/dwc3/dwc3-msm.c
@@ -983,13 +983,17 @@
gsi_dbl_address_lsb = devm_ioremap_nocache(mdwc->dev,
request->db_reg_phs_addr_lsb, sizeof(u32));
- if (!gsi_dbl_address_lsb)
- dev_dbg(mdwc->dev, "Failed to get GSI DBL address LSB\n");
+ if (!gsi_dbl_address_lsb) {
+ dev_err(mdwc->dev, "Failed to get GSI DBL address LSB\n");
+ return;
+ }
gsi_dbl_address_msb = devm_ioremap_nocache(mdwc->dev,
request->db_reg_phs_addr_msb, sizeof(u32));
- if (!gsi_dbl_address_msb)
- dev_dbg(mdwc->dev, "Failed to get GSI DBL address MSB\n");
+ if (!gsi_dbl_address_msb) {
+ dev_err(mdwc->dev, "Failed to get GSI DBL address MSB\n");
+ return;
+ }
offset = dwc3_trb_dma_offset(dep, &dep->trb_pool[num_trbs-1]);
dev_dbg(mdwc->dev, "Writing link TRB addr: %pa to %pK (%x) for ep:%s\n",
@@ -4096,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/ci13xxx_udc.c b/drivers/usb/gadget/ci13xxx_udc.c
index 10d737f..939c219 100644
--- a/drivers/usb/gadget/ci13xxx_udc.c
+++ b/drivers/usb/gadget/ci13xxx_udc.c
@@ -3474,6 +3474,7 @@
if (udc->udc_driver->notify_event)
udc->udc_driver->notify_event(udc,
CI13XXX_CONTROLLER_DISCONNECT_EVENT);
+ usb_gadget_set_state(&udc->gadget, USB_STATE_NOTATTACHED);
}
return 0;
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/drivers/video/fbdev/msm/mdp3_ctrl.c b/drivers/video/fbdev/msm/mdp3_ctrl.c
index 4a844e9..589f2df 100644
--- a/drivers/video/fbdev/msm/mdp3_ctrl.c
+++ b/drivers/video/fbdev/msm/mdp3_ctrl.c
@@ -1738,12 +1738,15 @@
break;
case metadata_op_get_ion_fd:
if (mfd->fb_ion_handle && mfd->fb_ion_client) {
+ get_dma_buf(mfd->fbmem_buf);
metadata->data.fbmem_ionfd =
ion_share_dma_buf_fd(mfd->fb_ion_client,
mfd->fb_ion_handle);
- if (metadata->data.fbmem_ionfd < 0)
+ if (metadata->data.fbmem_ionfd < 0) {
+ dma_buf_put(mfd->fbmem_buf);
pr_err("fd allocation failed. fd = %d\n",
- metadata->data.fbmem_ionfd);
+ metadata->data.fbmem_ionfd);
+ }
}
break;
default:
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/pwm.h b/include/linux/pwm.h
index 2c6c511..6c0168b 100644
--- a/include/linux/pwm.h
+++ b/include/linux/pwm.h
@@ -47,6 +47,29 @@
PWMF_EXPORTED = 1 << 1,
};
+/**
+ * enum pwm_output_type - output type of the PWM signal
+ * @PWM_OUTPUT_FIXED: PWM output is fixed until a change request
+ * @PWM_OUTPUT_MODULATED: PWM output is modulated in hardware
+ * autonomously with a predefined pattern
+ */
+enum pwm_output_type {
+ PWM_OUTPUT_FIXED = 1 << 0,
+ PWM_OUTPUT_MODULATED = 1 << 1,
+};
+
+/**
+ * struct pwm_output_pattern - PWM duty pattern for MODULATED duty type
+ * @duty_pattern: PWM duty cycles in the pattern for duty modulation
+ * @num_entries: number of entries in the pattern
+ * @cycles_per_duty: number of PWM period cycles an entry stays at
+ */
+struct pwm_output_pattern {
+ unsigned int *duty_pattern;
+ unsigned int num_entries;
+ unsigned int cycles_per_duty;
+};
+
/*
* struct pwm_state - state of a PWM channel
* @period: PWM period (in nanoseconds)
@@ -58,6 +81,8 @@
unsigned int period;
unsigned int duty_cycle;
enum pwm_polarity polarity;
+ enum pwm_output_type output_type;
+ struct pwm_output_pattern *output_pattern;
bool enabled;
};
@@ -143,6 +168,26 @@
return state.polarity;
}
+static inline enum pwm_output_type pwm_get_output_type(
+ const struct pwm_device *pwm)
+{
+ struct pwm_state state;
+
+ pwm_get_state(pwm, &state);
+
+ return state.output_type;
+}
+
+static inline struct pwm_output_pattern *pwm_get_output_pattern(
+ struct pwm_device *pwm)
+{
+ struct pwm_state state;
+
+ pwm_get_state(pwm, &state);
+
+ return pwm->state.output_pattern ?: NULL;
+}
+
static inline void pwm_get_args(const struct pwm_device *pwm,
struct pwm_args *args)
{
@@ -246,6 +291,9 @@
* @capture: capture and report PWM signal
* @enable: enable PWM output toggling
* @disable: disable PWM output toggling
+ * @get_output_type_supported: get the supported output type
+ * @set_output_type: set PWM output type
+ * @set_output_pattern: set the pattern for the modulated output
* @apply: atomically apply a new PWM config. The state argument
* should be adjusted with the real hardware config (if the
* approximate the period or duty_cycle value, state should
@@ -267,6 +315,13 @@
struct pwm_capture *result, unsigned long timeout);
int (*enable)(struct pwm_chip *chip, struct pwm_device *pwm);
void (*disable)(struct pwm_chip *chip, struct pwm_device *pwm);
+ int (*get_output_type_supported)(struct pwm_chip *chip,
+ struct pwm_device *pwm);
+ int (*set_output_type)(struct pwm_chip *chip, struct pwm_device *pwm,
+ enum pwm_output_type output_type);
+ int (*set_output_pattern)(struct pwm_chip *chip,
+ struct pwm_device *pwm,
+ struct pwm_output_pattern *output_pattern);
int (*apply)(struct pwm_chip *chip, struct pwm_device *pwm,
struct pwm_state *state);
void (*get_state)(struct pwm_chip *chip, struct pwm_device *pwm,
@@ -323,6 +378,21 @@
int pwm_adjust_config(struct pwm_device *pwm);
/**
+ * pwm_output_type_support()
+ * @pwm: PWM device
+ *
+ * Returns: output types supported by the PWM device
+ */
+static inline int pwm_get_output_type_supported(struct pwm_device *pwm)
+{
+ if (pwm->chip->ops->get_output_type_supported != NULL)
+ return pwm->chip->ops->
+ get_output_type_supported(pwm->chip, pwm);
+ else
+ return PWM_OUTPUT_FIXED;
+}
+
+/**
* pwm_config() - change a PWM device configuration
* @pwm: PWM device
* @duty_ns: "on" time (in nanoseconds)
diff --git a/include/linux/qcom-geni-se.h b/include/linux/qcom-geni-se.h
index fc02ece..b8dd63a 100644
--- a/include/linux/qcom-geni-se.h
+++ b/include/linux/qcom-geni-se.h
@@ -1,5 +1,5 @@
/*
- * 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
@@ -154,6 +154,7 @@
/* FW_REVISION_RO fields */
#define FW_REV_PROTOCOL_MSK (GENMASK(15, 8))
#define FW_REV_PROTOCOL_SHFT (8)
+#define FW_REV_VERSION_MSK (GENMASK(7, 0))
/* GENI_CLK_SEL fields */
#define CLK_SEL_MSK (GENMASK(2, 0))
@@ -405,6 +406,22 @@
int get_se_proto(void __iomem *base);
/**
+ * get_se_m_fw() - Read the Firmware ver for the Main seqeuncer engine
+ * @base: Base address of the serial engine's register block.
+ *
+ * Return: Firmware version for the Main seqeuncer engine
+ */
+int get_se_m_fw(void __iomem *base);
+
+/**
+ * get_se_s_fw() - Read the Firmware ver for the Secondry seqeuncer engine
+ * @base: Base address of the serial engine's register block.
+ *
+ * Return: Firmware version for the Secondry seqeuncer engine
+ */
+int get_se_s_fw(void __iomem *base);
+
+/**
* geni_se_init() - Initialize the GENI Serial Engine
* @base: Base address of the serial engine's register block.
* @rx_wm: Receive watermark to be configured.
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/uapi/linux/qcedev.h b/include/uapi/linux/qcedev.h
index fb51c23..108af3c 100644
--- a/include/uapi/linux/qcedev.h
+++ b/include/uapi/linux/qcedev.h
@@ -228,6 +228,33 @@
void *kernel;
};
+/**
+ * struct qcedev_map_buf_req - Holds the mapping request information
+ * fd (IN): Array of fds.
+ * num_fds (IN): Number of fds in fd[].
+ * fd_size (IN): Array of sizes corresponding to each fd in fd[].
+ * fd_offset (IN): Array of offset corresponding to each fd in fd[].
+ * vaddr (OUT): Array of mapped virtual address corresponding to
+ * each fd in fd[].
+ */
+struct qcedev_map_buf_req {
+ int32_t fd[QCEDEV_MAX_BUFFERS];
+ uint32_t num_fds;
+ uint32_t fd_size[QCEDEV_MAX_BUFFERS];
+ uint32_t fd_offset[QCEDEV_MAX_BUFFERS];
+ uint64_t buf_vaddr[QCEDEV_MAX_BUFFERS];
+};
+
+/**
+ * struct qcedev_unmap_buf_req - Holds the hashing request information
+ * fd (IN): Array of fds to unmap
+ * num_fds (IN): Number of fds in fd[].
+ */
+struct qcedev_unmap_buf_req {
+ int32_t fd[QCEDEV_MAX_BUFFERS];
+ uint32_t num_fds;
+};
+
struct file;
#define QCEDEV_IOC_MAGIC 0x87
@@ -250,8 +277,8 @@
_IO(QCEDEV_IOC_MAGIC, 8)
#define QCEDEV_IOCTL_GET_CMAC_REQ \
_IOWR(QCEDEV_IOC_MAGIC, 9, struct qcedev_sha_op_req)
-#define QCEDEV_IOCTL_UPDATE_FIPS_STATUS \
- _IOWR(QCEDEV_IOC_MAGIC, 10, enum fips_status)
-#define QCEDEV_IOCTL_QUERY_FIPS_STATUS \
- _IOR(QCEDEV_IOC_MAGIC, 11, enum fips_status)
+#define QCEDEV_IOCTL_MAP_BUF_REQ \
+ _IOWR(QCEDEV_IOC_MAGIC, 10, struct qcedev_map_buf_req)
+#define QCEDEV_IOCTL_UNMAP_BUF_REQ \
+ _IOWR(QCEDEV_IOC_MAGIC, 11, struct qcedev_unmap_buf_req)
#endif /* _UAPI_QCEDEV__H */
diff --git a/include/uapi/media/cam_req_mgr.h b/include/uapi/media/cam_req_mgr.h
index 3f1facd..841c40a 100644
--- a/include/uapi/media/cam_req_mgr.h
+++ b/include/uapi/media/cam_req_mgr.h
@@ -41,9 +41,9 @@
#define V4L_EVENT_CAM_REQ_MGR_EVENT (V4L2_EVENT_PRIVATE_START + 0)
/* Specific event ids to get notified in user space */
-#define V4L_EVENT_CAM_REQ_MGR_SOF 0
-#define V4L_EVENT_CAM_REQ_MGR_ERROR 1
-#define V4L_EVENT_CAM_REQ_MGR_MAX 2
+#define V4L_EVENT_CAM_REQ_MGR_SOF 0
+#define V4L_EVENT_CAM_REQ_MGR_ERROR 1
+#define V4L_EVENT_CAM_REQ_MGR_SOF_BOOT_TS 2
/* SOF Event status */
#define CAM_REQ_MGR_SOF_EVENT_SUCCESS 0
diff --git a/kernel/sched/core.c b/kernel/sched/core.c
index 23a7f9c..0ca1647 100644
--- a/kernel/sched/core.c
+++ b/kernel/sched/core.c
@@ -2330,6 +2330,7 @@
p->se.nr_migrations = 0;
p->se.vruntime = 0;
p->last_sleep_ts = 0;
+ p->last_cpu_selected_ts = 0;
INIT_LIST_HEAD(&p->se.group_node);
diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c
index c00e731..04ba6d0 100644
--- a/kernel/sched/fair.c
+++ b/kernel/sched/fair.c
@@ -7296,6 +7296,39 @@
task_fits_max(p, cpu);
}
+#define SCHED_SELECT_PREV_CPU_NSEC 2000000
+#define SCHED_FORCE_CPU_SELECTION_NSEC 20000000
+
+static inline bool
+bias_to_prev_cpu(struct task_struct *p, struct cpumask *rtg_target)
+{
+ int prev_cpu = task_cpu(p);
+#ifdef CONFIG_SCHED_WALT
+ u64 ms = p->ravg.mark_start;
+#else
+ u64 ms = sched_clock();
+#endif
+
+ if (cpu_isolated(prev_cpu) || !idle_cpu(prev_cpu))
+ return false;
+
+ if (!ms)
+ return false;
+
+ if (ms - p->last_cpu_selected_ts >= SCHED_SELECT_PREV_CPU_NSEC) {
+ p->last_cpu_selected_ts = ms;
+ return false;
+ }
+
+ if (ms - p->last_sleep_ts >= SCHED_SELECT_PREV_CPU_NSEC)
+ return false;
+
+ if (rtg_target && !cpumask_test_cpu(prev_cpu, rtg_target))
+ return false;
+
+ return true;
+}
+
#ifdef CONFIG_SCHED_WALT
static inline struct cpumask *find_rtg_target(struct task_struct *p)
{
@@ -7374,6 +7407,9 @@
}
}
+ if (bias_to_prev_cpu(p, rtg_target))
+ return prev_cpu;
+
rcu_read_lock();
sd = rcu_dereference(per_cpu(sd_ea, prev_cpu));
diff --git a/kernel/sched/sched.h b/kernel/sched/sched.h
index 3637d96..f27ab13 100644
--- a/kernel/sched/sched.h
+++ b/kernel/sched/sched.h
@@ -823,6 +823,8 @@
int prev_top;
int curr_top;
bool notif_pending;
+ u64 last_cc_update;
+ u64 cycles;
#endif
#ifdef CONFIG_IRQ_TIME_ACCOUNTING
diff --git a/kernel/sched/walt.c b/kernel/sched/walt.c
index e6a11c1..a9fb367 100644
--- a/kernel/sched/walt.c
+++ b/kernel/sched/walt.c
@@ -301,10 +301,27 @@
return 0;
}
-static void update_task_cpu_cycles(struct task_struct *p, int cpu)
+/*
+ * Assumes rq_lock is held and wallclock was recorded in the same critical
+ * section as this function's invocation.
+ */
+static inline u64 read_cycle_counter(int cpu, u64 wallclock)
+{
+ struct rq *rq = cpu_rq(cpu);
+
+ if (rq->last_cc_update != wallclock) {
+ rq->cycles = cpu_cycle_counter_cb.get_cpu_cycle_counter(cpu);
+ rq->last_cc_update = wallclock;
+ }
+
+ return rq->cycles;
+}
+
+static void update_task_cpu_cycles(struct task_struct *p, int cpu,
+ u64 wallclock)
{
if (use_cycle_counter)
- p->cpu_cycles = cpu_cycle_counter_cb.get_cpu_cycle_counter(cpu);
+ p->cpu_cycles = read_cycle_counter(cpu, wallclock);
}
void clear_ed_task(struct task_struct *p, struct rq *rq)
@@ -348,7 +365,7 @@
if (is_idle_task(curr)) {
/* We're here without rq->lock held, IRQ disabled */
raw_spin_lock(&rq->lock);
- update_task_cpu_cycles(curr, cpu);
+ update_task_cpu_cycles(curr, cpu, ktime_get_ns());
raw_spin_unlock(&rq->lock);
}
}
@@ -757,7 +774,7 @@
update_task_ravg(p, task_rq(p), TASK_MIGRATE,
wallclock, 0);
- update_task_cpu_cycles(p, new_cpu);
+ update_task_cpu_cycles(p, new_cpu, wallclock);
/*
* When a task is migrating during the wakeup, adjust
@@ -1839,7 +1856,7 @@
return;
}
- cur_cycles = cpu_cycle_counter_cb.get_cpu_cycle_counter(cpu);
+ cur_cycles = read_cycle_counter(cpu, wallclock);
/*
* If current task is idle task and irqtime == 0 CPU was
@@ -1904,7 +1921,7 @@
old_window_start = update_window_start(rq, wallclock, event);
if (!p->ravg.mark_start) {
- update_task_cpu_cycles(p, cpu_of(rq));
+ update_task_cpu_cycles(p, cpu_of(rq), wallclock);
goto done;
}
@@ -2035,7 +2052,7 @@
p->ravg.mark_start = p->last_wake_ts = wallclock;
p->last_enqueued_ts = wallclock;
p->last_switch_out_ts = 0;
- update_task_cpu_cycles(p, cpu_of(rq));
+ update_task_cpu_cycles(p, cpu_of(rq), wallclock);
}
static cpumask_t all_cluster_cpus = CPU_MASK_NONE;
@@ -3255,6 +3272,8 @@
rq->curr_table = 0;
rq->prev_top = 0;
rq->curr_top = 0;
+ rq->last_cc_update = 0;
+ rq->cycles = 0;
for (j = 0; j < NUM_TRACKED_WINDOWS; j++) {
memset(&rq->load_subs[j], 0,
sizeof(struct load_subtractions));