Merge "drm: msm: sde: add api to get if dma wait is needed"
diff --git a/Documentation/devicetree/bindings/arm/msm/imem.txt b/Documentation/devicetree/bindings/arm/msm/imem.txt
index 654f26e..440628d 100644
--- a/Documentation/devicetree/bindings/arm/msm/imem.txt
+++ b/Documentation/devicetree/bindings/arm/msm/imem.txt
@@ -46,6 +46,12 @@
-compatible: "qcom,msm-imem-restart_reason
-reg: start address and size of restart_reason region in imem
+Download Mode Type:
+-------------------
+Required properties:
+-compatible: "qcom,msm-imem-dload-type"
+-reg: start address and size of dload type region in imem
+
Download Mode:
--------------
Required properties:
diff --git a/Documentation/devicetree/bindings/arm/msm/lpm-levels.txt b/Documentation/devicetree/bindings/arm/msm/lpm-levels.txt
index c757233..55d06b2 100644
--- a/Documentation/devicetree/bindings/arm/msm/lpm-levels.txt
+++ b/Documentation/devicetree/bindings/arm/msm/lpm-levels.txt
@@ -77,6 +77,8 @@
- qcom,pm-cpu-levels: The different low power modes that a CPU could
enter. The following section explains the required properties of this
node.
+ -qcom,use-prediction: This optional property is used to indicate the
+ the LPM governor is to apply sleep prediction to this cluster.
[Node bindings for qcom,pm-cpu-levels]
Required properties:
diff --git a/Documentation/devicetree/bindings/arm/msm/msm.txt b/Documentation/devicetree/bindings/arm/msm/msm.txt
index 765b5e4..327a7d4 100644
--- a/Documentation/devicetree/bindings/arm/msm/msm.txt
+++ b/Documentation/devicetree/bindings/arm/msm/msm.txt
@@ -166,6 +166,8 @@
- VR device:
compatible = "qcom,qvr"
+- HDK device:
+ compatible = "qcom,hdk"
Boards (SoC type + board variant):
@@ -279,9 +281,11 @@
compatible = "qcom,sda845-cdp"
compatible = "qcom,sda845-mtp"
compatible = "qcom,sda845-qrd"
+compatible = "qcom,sda845-hdk"
compatible = "qcom,sdm670-rumi"
compatible = "qcom,sdm670-cdp"
compatible = "qcom,sdm670-mtp"
+compatible = "qcom,sdm670-qrd"
compatible = "qcom,qcs605-cdp"
compatible = "qcom,qcs605-mtp"
compatible = "qcom,sda670-cdp"
@@ -321,3 +325,5 @@
compatible = "qcom,apq8009-cdp"
compatible = "qcom,apq8009-mtp"
compatible = "qcom,sdxpoorwills-rumi"
+compatible = "qcom,sdxpoorwills-mtp"
+compatible = "qcom,sdxpoorwills-cdp"
diff --git a/Documentation/devicetree/bindings/arm/msm/qcom,llcc.txt b/Documentation/devicetree/bindings/arm/msm/qcom,llcc.txt
index 628b2aa..90bc368 100644
--- a/Documentation/devicetree/bindings/arm/msm/qcom,llcc.txt
+++ b/Documentation/devicetree/bindings/arm/msm/qcom,llcc.txt
@@ -31,6 +31,9 @@
4. LLCC AMON Driver:
Keeps track of the data progress within the internal channels of LLCC.
+5. LLCC Performance Monitor
+Used to monitor the events of LLCC sub blocks.
+
== llcc device ==
Require Properties:
@@ -107,6 +110,10 @@
compatible = "qcom,llcc-amon";
qcom,fg-cnt = <0x7>;
};
+
+ qcom,llcc-perfmon {
+ compatible = "qcom,llcc-perfmon";
+ };
};
== Client ==
diff --git a/Documentation/devicetree/bindings/arm/msm/qcom,osm.txt b/Documentation/devicetree/bindings/arm/msm/qcom,osm.txt
index cc4c3cc..bce983a 100644
--- a/Documentation/devicetree/bindings/arm/msm/qcom,osm.txt
+++ b/Documentation/devicetree/bindings/arm/msm/qcom,osm.txt
@@ -9,397 +9,27 @@
- compatible
Usage: required
Value type: <string>
- Definition: must be "qcom,clk-cpu-osm" or "qcom,clk-cpu-osm-v2".
+ Definition: must be "qcom,clk-cpu-osm", "qcom,clk-cpu-osm-v2" or
+ "qcom,clk-cpu-osm-sdm670".
- reg
Usage: required
Value type: <prop-encoded-array>
- Definition: Addresses and sizes for the memory of the OSM controller,
- cluster PLL management, and APCS common register regions.
- Optionally, the address of the efuse registers used to
- determine the pwrcl or perfcl speed-bins and/or the ACD
- register space to initialize prior to enabling OSM.
+ Definition: Addresses and sizes for the memory of the OSM controller.
- reg-names
Usage: required
Value type: <stringlist>
Definition: Address names. Must be "osm_l3_base", "osm_pwrcl_base",
- "osm_perfcl_base", "l3_pll", "pwrcl_pll", "perfcl_pll",
- "l3_sequencer", "pwrcl_sequencer", or "perfcl_sequencer".
- Optionally, "l3_efuse", "pwrcl_efuse", "perfcl_efuse",
- "pwrcl_acd", "perfcl_acd", "l3_acd".
+ "osm_perfcl_base".
Must be specified in the same order as the corresponding
addresses are specified in the reg property.
-- vdd-l3-supply
- Usage: required
+- l3-devs
+ Usage: optional
Value type: <phandle>
- Definition: phandle of the underlying regulator device that manages
- the voltage supply of the L3 cluster.
-
-- vdd-pwrcl-supply
- Usage: required
- Value type: <phandle>
- Definition: phandle of the underlying regulator device that manages
- the voltage supply of the Power cluster.
-
-- vdd-perfcl-supply
- Usage: required
- Value type: <phandle>
- Definition: phandle of the underlying regulator device that manages
- the voltage supply of the Performance cluster.
-
-- interrupts
- Usage: required
- Value type: <prop-encoded-array>
- Definition: OSM interrupt specifier.
-
-- interrupt-names
- Usage: required
- Value type: <stringlist>
- Definition: Interrupt names. this list must match up 1-to-1 with the
- interrupts specified in the 'interrupts' property.
- "pwrcl-irq" and "perfcl-irq" must be specified.
-
-- qcom,l3-speedbinX-v0
- Usage: required
- Value type: <prop-encoded-array>
- Definition: Array which defines the frequency in Hertz, frequency,
- PLL override data, ACC level, and virtual corner used
- by the OSM hardware for each supported DCVS setpoint
- of the L3 cluster.
-
-- qcom,pwrcl-speedbinX-v0
- Usage: required
- Value type: <prop-encoded-array>
- Definition: Array which defines the frequency in Hertz, frequency,
- PLL override data, ACC level, and virtual corner used
- by the OSM hardware for each supported DCVS setpoint
- of the Power cluster.
-
-- qcom,perfcl-speedbinX-v0
- Usage: required
- Value type: <prop-encoded-array>
- Definition: Array which defines the frequency in Hertz, frequency,
- PLL override data, ACC level and virtual corner used
- by the OSM hardware for each supported DCVS setpoint
- of the Performance cluster.
-
-- qcom,osm-no-tz
- Usage: optional
- Value type: <empty>
- Definition: Boolean flag which indicates that there is no programming
- of the OSM hardware performed by the secure world.
-
-- qcom,osm-pll-setup
- Usage: optional
- Value type: <empty>
- Definition: Boolean flag which indicates that the PLL setup sequence
- must be executed for each clock domain managed by the OSM
- controller.
-
-- qcom,up-timer
- Usage: optional
- Value type: <prop-encoded-array>
- Definition: Array which defines the DCVS up timer value in nanoseconds
- for each of the three clock domains managed by the OSM
- controller.
-
-- qcom,down-timer
- Usage: optional
- Value type: <prop-encoded-array>
- Definition: Array which defines the DCVS down timer value in nanoseconds
- for each of the three clock domains managed by the OSM
- controller.
-
-- qcom,pc-override-index
- Usage: optional
- Value type: <prop-encoded-array>
- Definition: Array which defines the OSM performance index to be used
- when each cluster enters certain low power modes.
-
-- qcom,set-ret-inactive
- Usage: optional
- Value type: <empty>
- Definition: Boolean flag which indicates if domains in retention must
- be treated as inactive.
-
-- qcom,enable-llm-freq-vote
- Usage: optional
- Value type: <empty>
- Definition: Boolean flag which indicates if Limits hardware frequency
- votes must be honored by OSM.
-
-- qcom,llm-freq-up-timer
- Usage: optional
- Value type: <prop-encoded-array>
- Definition: Array which defines the LLM frequency up timer value in
- nanoseconds for each of the three clock domains managed by
- the OSM controller.
-
-- qcom,llm-freq-down-timer
- Usage: optional
- Value type: <prop-encoded-array>
- Definition: Array which defines the LLM frequency down timer value in
- nanoseconds for each of the three clock domains managed by
- the OSM controller.
-
-- qcom,enable-llm-volt-vote
- Usage: optional
- Value type: <empty>
- Definition: Boolean flag which indicates if Limits hardware voltage
- votes must be honored by OSM.
-
-- qcom,llm-volt-up-timer
- Usage: optional
- Value type: <prop-encoded-array>
- Definition: Array which defines the LLM voltage up timer value in
- nanoseconds for each of the three clock domains managed by
- the OSM controller.
-
-- qcom,llm-volt-down-timer
- Usage: optional
- Value type: <prop-encoded-array>
- Definition: Array which defines the LLM voltage down timer value in
- nanoseconds for each of the three clock domains managed by
- the OSM controller.
-
-- qcom,cc-reads
- Usage: optional
- Value type: <integer>
- Definition: Defines the number of times the cycle counters must be
- read to determine the performance level of each clock
- domain.
-
-- qcom,l-val-base
- Usage: required
- Value type: <prop-encoded-array>
- Definition: Array which defines the register addresses of the L_VAL
- control register for each of the three clock domains
- managed by the OSM controller.
-
-- qcom,apcs-pll-user-ctl
- Usage: required
- Value type: <prop-encoded-array>
- Definition: Array which defines the register addresses of the PLL
- user control register for each of the three clock domains
- managed by the OSM controller.
-
-- qcom,perfcl-apcs-apm-threshold-voltage
- Usage: required
- Value type: <u32>
- Definition: Specifies the APM threshold voltage in microvolts. If the
- VDD_APCC supply voltage is above or at this level, then the
- APM is switched to use VDD_APCC. If VDD_APCC is below
- this level, then the APM is switched to use VDD_MX.
-
-- qcom,apm-mode-ctl
- Usage: required
- Value type: <prop-encoded-array>
- Definition: Array which defines the register addresses of the APM
- control register for each of the two clusters managed
- by the OSM controller.
-
-- qcom,apm-status-ctrl
- Usage: required
- Value type: <prop-encoded-array>
- Definition: Array which defines the register addresses of the APM
- controller status register for each of the three clock
- domains managed by the OSM controller.
-
-- qcom,perfcl-isense-addr
- Usage: required
- Value type: <u32>
- Definition: Contains the ISENSE register address.
-
-- qcom,l3-mem-acc-addr
- Usage: required if qcom,osm-no-tz is specified
- Value type: <prop-encoded-array>
- Definition: Array which defines the addresses of the mem-acc
- configuration registers for the L3 cluster.
- The array must contain exactly three elements.
-
-- qcom,pwrcl-mem-acc-addr
- Usage: required if qcom,osm-no-tz is specified
- Value type: <prop-encoded-array>
- Definition: Array which defines the addresses of the mem-acc
- configuration registers for the Power cluster.
- The array must contain exactly three elements.
-
-- qcom,perfcl-mem-acc-addr
- Usage: required if qcom,osm-no-tz is specified
- Value type: <prop-encoded-array>
- Definition: Array which defines the addresses of the mem-acc
- configuration registers for the Performance cluster.
- The array must contain exactly three elements.
-
-- qcom,perfcl-apcs-mem-acc-threshold-voltage
- Usage: optional
- Value type: <u32>
- Definition: Specifies the highest MEM ACC threshold voltage in
- microvolts for the Performance cluster. This voltage is
- used to determine which MEM ACC setting is used for the
- highest frequencies. If specified, the voltage must match
- the MEM ACC threshold voltage specified for the
- corresponding CPRh device.
-
-- qcom,l3-memacc-level-vc-binX
- Usage: required
- Value type: <prop-encoded-array>
- Definition: Array which defines the NOM and TURBO VCs for the L3 clock
- on that BIN part.
- The array must contain exactly two elements.
-
-- qcom,pwrcl-memacc-level-vc-binX
- Usage: required
- Value type: <prop-encoded-array>
- Definition: Array which defines the NOM and TURBO VCs for the Power
- cluster clock on that BIN part.
- The array must contain exactly two elements.
-
-- qcom,perfcl-memacc-level-vc-binX
- Usage: required
- Value type: <prop-encoded-array>
- Definition: Array which defines the NOM and TURBO VCs for the
- Performance cluster clock on that BIN part.
- The array must contain exactly two elements.
-
-- qcom,apcs-cbc-addr
- Usage: required
- Value type: <prop-encoded-array>
- Definition: Array which defines the addresses of the APCS_CBC_ADDR
- registers for all three clock domains.
-
-- qcom,apcs-ramp-ctl-addr
- Usage: required
- Value type: <prop-encoded-array>
- Definition: Array which defines the addresses of the APCS_RAMP_CTL_ADDR
- registers for all three clock domains.
-
-- qcom,red-fsm-en
- Usage: optional
- Value type: <empty>
- Definition: Boolean flag which indicates if the reduction FSM
- should be enabled.
-
-- qcom,boost-fsm-en
- Usage: optional
- Value type: <empty>
- Definition: Boolean flag which indicates if the boost FSM should
- be enabled.
-
-- qcom,safe-fsm-en
- Usage: optional
- Value type: <empty>
- Definition: Boolean flag which indicates if the safe FSM should
- be enabled.
-
-- qcom,ps-fsm-en
- Usage: optional
- Value type: <empty>
- Definition: Boolean flag which indicates if the PS FSM should be
- enabled.
-
-- qcom,droop-fsm-en
- Usage: optional
- Value type: <empty>
- Definition: Boolean flag which indicates if the droop FSM should
- be enabled.
-
-- qcom,set-c3-active
- Usage: optional
- Value type: <empty>
- Definition: Boolean flag which indicates if the cores in C3 are to
- be treated as active for core count calculations.
-
-- qcom,set-c2-active
- Usage: optional
- Value type: <empty>
- Definition: Boolean flag which indicates if the cores in C2 are to
- be treated as active for core count calculations.
-
-- qcom,disable-cc-dvcs
- Usage: optional
- Value type: <empty>
- Definition: Boolean flag which indicates if core count based DCVS is
- to be disabled.
-
-- qcom,apcs-pll-min-freq
- Usage: required
- Value type: <u32>
- Definition: Contains the addresses of the RAILx_CLKDOMy_PLL_MIN_FREQ
- registers for the three clock domains.
-
-- qcom,acdtd-val
- Usage: required if pwrcl_acd, perfcl_acd or l3_acd registers are
- specified
- Value type: <prop-encoded-array>
- Definition: Array which defines the values to program to the ACD
- Tunable-Length Delay register for the L3, power and
- performance clusters.
-
-- qcom,acdcr-val
- Usage: required if pwrcl_acd, perfcl_acd or l3_acd registers are
- specified
- Value type: <prop-encoded-array>
- Definition: Array which defines the values for the ACD control register
- for the L3, power and performance clusters.
-
-- qcom,acdsscr-val
- Usage: required if pwrcl_acd, perfcl_acd or l3_acd registers are
- specified
- Value type: <prop-encoded-array>
- Definition: Array which defines the values for the ACD Soft Start Control
- register for the L3, power and performance clusters.
-
-- qcom,acdextint0-val
- Usage: required if pwrcl_acd, perfcl_acd or l3_acd registers are
- specified
- Value type: <prop-encoded-array>
- Definition: Array which defines the initial values for the ACD
- external interface configuration register for the L3, power
- and performance clusters.
-
-- qcom,acdextint1-val
- Usage: required if pwrcl_acd, perfcl_acd or l3_acd registers are
- specified
- Value type: <prop-encoded-array>
- Definition: Array which defines the final values for the ACD
- external interface configuration register for the L3, power
- and performance clusters.
-
-- qcom,acdautoxfer-val
- Usage: required if pwrcl_acd, perfcl_acd or l3_acd registers are
- specified
- Value type: <prop-encoded-array>
- Definition: Array which defines the values for the ACD auto transfer
- control register for the L3, power and performance clusters.
-
-- qcom,acdavg-init
- Usage: optional if pwrcl_acd, perfcl_acd or l3_acd registers are
- specified
- Value type: <prop-encoded-array>
- Definition: Array which defines if the AVG feature for ACD should be
- initialized for the L3, power and performance clusters.
- Valid values are 0 or 1.
-
-- qcom,acdavgcfg0-val
- Usage: required if qcom,acdavg-init is true for an ACD clock domain
- Value type: <prop-encoded-array>
- Definition: Array which defines the values for the ACD AVG CFG0
- registers for the L3, power and performance clusters.
-
-- qcom,acdavgcfg1-val
- Usage: required if qcom,acdavg-init is true for an ACD clock domain
- Value type: <prop-encoded-array>
- Definition: Array which defines the values for the ACD AVG CFG1
- registers for the L3, power and performance clusters.
-
-- qcom,acdavgcfg2-val
- Usage: required if qcom,acdavg-init is true for an ACD clock domain
- Value type: <prop-encoded-array>
- Definition: Array which defines the values for the ACD AVG CFG2
- registers for the L3, power and performance clusters.
+ Definition: List of phandles to devices that the OPP tables with the L3
+ frequency and voltage mappings are loaded for.
- clock-names
Usage: required
@@ -416,156 +46,12 @@
compatible = "qcom,clk-cpu-osm";
reg = <0x17d41000 0x1400>,
<0x17d43000 0x1400>,
- <0x17d45800 0x1400>,
- <0x178d0000 0x1000>,
- <0x178c0000 0x1000>,
- <0x178b0000 0x1000>,
- <0x17d42400 0x0c00>,
- <0x17d44400 0x0c00>,
- <0x17d46c00 0x0c00>,
- <0x17930000 0x10000>,
- <0x17920000 0x10000>,
- <0x17910000 0x10000>;
- reg-names = "osm_l3_base", "osm_pwrcl_base", "osm_perfcl_base",
- "l3_pll", "pwrcl_pll", "perfcl_pll",
- "l3_sequencer", "pwrcl_sequencer",
- "perfcl_sequencer", "l3_acd", "pwrcl_acd",
- "perfcl_acd";
+ <0x17d45800 0x1400>;
+ reg-names = "osm_l3_base", "osm_pwrcl_base", "osm_perfcl_base";
- /* ACD configurations for L3, Silver, and Gold clusters */
- qcom,acdtd-val = <0x0000b411 0x0000b411 0x0000b411>;
- qcom,acdcr-val = <0x002c5ffd 0x002c5ffd 0x002c5ffd>;
- qcom,acdsscr-val = <0x00000901 0x00000901 0x00000901>;
- qcom,acdextint0-val = <0x2cf9ae8 0x2cf9ae8 0x2cf9ae8>;
- qcom,acdextint1-val = <0x2cf9afe 0x2cf9afe 0x2cf9afe>;
- qcom,acdautoxfer-val = <0x00000015 0x00000015 0x00000015>;
- qcom,acdavgcfg2-val = <0x0 0x56a38822 0x56a38822>;
- qcom,acdavgcfg1-val = <0x0 0x27104e20 0x27104e20>;
- qcom,acdavgcfg0-val = <0x0 0xa08007a1 0xa08007a1>;
- qcom,acdavg-init = <0 1 1>;
-
- vdd-l3-supply = <&apc0_l3_vreg>;
- vdd-pwrcl-supply = <&apc0_pwrcl_vreg>;
- vdd-perfcl-supply = <&apc1_perfcl_vreg>;
-
- qcom,l3-speedbin0-v0 =
- < 300000000 0x000c000f 0x00002020 0x1 1 >,
- < 422400000 0x50140116 0x00002020 0x1 2 >,
- < 499200000 0x5014021a 0x00002020 0x1 3 >,
- < 576000000 0x5014031e 0x00002020 0x1 4 >,
- < 652800000 0x501c0422 0x00002020 0x1 5 >,
- < 729600000 0x501c0526 0x00002020 0x1 6 >,
- < 806400000 0x501c062a 0x00002222 0x1 7 >,
- < 883200000 0x4024072b 0x00002525 0x1 8 >,
- < 960000000 0x40240832 0x00002828 0x2 9 >;
-
- qcom,pwrcl-speedbin0-v0 =
- < 300000000 0x000c000f 0x00002020 0x1 1 >,
- < 422400000 0x50140116 0x00002020 0x1 2 >,
- < 499200000 0x5014021a 0x00002020 0x1 3 >,
- < 576000000 0x5014031e 0x00002020 0x1 4 >,
- < 652800000 0x501c0422 0x00002020 0x1 5 >,
- < 748800000 0x501c0527 0x00002020 0x1 6 >,
- < 825600000 0x401c062b 0x00002222 0x1 7 >,
- < 902400000 0x4024072f 0x00002626 0x1 8 >,
- < 979200000 0x40240833 0x00002929 0x1 9 >,
- < 1056000000 0x402c0937 0x00002c2c 0x1 10 >,
- < 1132800000 0x402c0a3b 0x00002f2f 0x1 11 >,
- < 1209600000 0x402c0b3f 0x00003333 0x1 12 >,
- < 1286400000 0x40340c43 0x00003636 0x1 13 >,
- < 1363200000 0x40340d47 0x00003939 0x1 14 >,
- < 1440000000 0x403c0e4b 0x00003c3c 0x1 15 >,
- < 1516800000 0x403c0f4f 0x00004040 0x2 16 >,
- < 1593600000 0x403c1053 0x00004343 0x2 17 >;
-
- qcom,perfcl-speedbin0-v0 =
- < 300000000 0x000c000f 0x00002020 0x1 1 >,
- < 422400000 0x50140116 0x00002020 0x1 2 >,
- < 499200000 0x5014021a 0x00002020 0x1 3 >,
- < 576000000 0x5014031e 0x00002020 0x1 4 >,
- < 652800000 0x501c0422 0x00002020 0x1 5 >,
- < 729600000 0x501c0526 0x00002020 0x1 6 >,
- < 806400000 0x501c062a 0x00002222 0x1 7 >,
- < 883200000 0x4024072b 0x00002525 0x1 8 >,
- < 960000000 0x40240832 0x00002828 0x1 9 >,
- < 1036800000 0x40240936 0x00002b2b 0x1 10 >,
- < 1113600000 0x402c0a3a 0x00002e2e 0x1 11 >,
- < 1190400000 0x402c0b3e 0x00003232 0x1 12 >,
- < 1267200000 0x40340c42 0x00003535 0x1 13 >,
- < 1344000000 0x40340d46 0x00003838 0x1 14 >,
- < 1420800000 0x40340e4a 0x00003b3b 0x1 15 >,
- < 1497600000 0x403c0f4e 0x00003e3e 0x1 16 >,
- < 1574400000 0x403c1052 0x00004242 0x2 17 >,
- < 1651200000 0x403c1156 0x00004545 0x2 18 >,
- < 1728000000 0x4044125a 0x00004848 0x2 19 >,
- < 1804800000 0x4044135e 0x00004b4b 0x2 20 >,
- < 1881600000 0x404c1462 0x00004e4e 0x2 21 >,
- < 1958400000 0x404c1566 0x00005252 0x3 22 >;
-
- qcom,l3-memacc-level-vc-bin0 = <7 63>;
- qcom,l3-memacc-level-vc-bin1 = <7 9>;
- qcom,l3-memacc-level-vc-bin2 = <7 9>;
-
- qcom,pwrcl-memacc-level-vc-bin0 = <12 63>;
- qcom,pwrcl-memacc-level-vc-bin1 = <12 17>;
- qcom,pwrcl-memacc-level-vc-bin2 = <12 17>;
-
- qcom,perfcl-memacc-level-vc-bin0 = <12 18>;
- qcom,perfcl-memacc-level-vc-bin1 = <12 18>;
- qcom,perfcl-memacc-level-vc-bin2 = <12 18>;
-
- qcom,up-timer =
- <1000 1000 1000>;
- qcom,down-timer =
- <100000 100000 100000>;
- qcom,pc-override-index =
- <0 0 0>;
- qcom,set-ret-inactive;
- qcom,enable-llm-freq-vote;
- qcom,llm-freq-up-timer =
- <1000 1000 1000>;
- qcom,llm-freq-down-timer =
- <327675 327675 327675>;
- qcom,enable-llm-volt-vote;
- qcom,llm-volt-up-timer =
- <1000 1000 1000>;
- qcom,llm-volt-down-timer =
- <327675 327675 327675>;
- qcom,cc-reads = <10>;
- qcom,cc-delay = <5>;
- qcom,cc-factor = <100>;
- qcom,osm-clk-rate = <100000000>;
- qcom,xo-clk-rate = <19200000>;
-
- qcom,l-val-base =
- <0x178d0004 0x178c0004 0x178b0004>;
- qcom,apcs-pll-user-ctl =
- <0x178d000c 0x178c000c 0x178b000c>;
- qcom,apcs-pll-min-freq =
- <0x17d41094 0x17d43094 0x17d45894>;
- qcom,apm-mode-ctl =
- <0x0 0x0 0x17d20010>;
- qcom,apm-status-ctrl =
- <0x0 0x0 0x17d20000>;
- qcom,perfcl-isense-addr = <0x17871480>;
- qcom,l3-mem-acc-addr = <0x17990170 0x17990170 0x17990170>;
- qcom,pwrcl-mem-acc-addr = <0x17990160 0x17990164 0x17990164>;
- qcom,perfcl-mem-acc-addr = <0x17990168 0x1799016c 0x1799016c>;
- qcom,cfg-gfmux-addr =<0x178d0084 0x178c0084 0x178b0084>;
- qcom,apcs-cbc-addr = <0x178d008c 0x178c008c 0x178b008c>;
- qcom,apcs-ramp-ctl-addr = <0x17840904 0x17840904 0x17830904>;
-
- qcom,perfcl-apcs-apm-threshold-voltage = <800000>;
- qcom,perfcl-apcs-mem-acc-threshold-voltage = <852000>;
- qcom,boost-fsm-en;
- qcom,safe-fsm-en;
- qcom,ps-fsm-en;
- qcom,droop-fsm-en;
- qcom,osm-no-tz;
- qcom,osm-pll-setup;
+ l3-devs = <&phandle0 &phandle1 &phandle2>;
clock-names = "xo_ao";
clocks = <&clock_rpmh RPMH_CXO_CLK_A>;
#clock-cells = <1>;
- #reset-cells = <1>;
};
diff --git a/Documentation/devicetree/bindings/clock/qcom,gcc.txt b/Documentation/devicetree/bindings/clock/qcom,gcc.txt
index 538fb6d..78bb87a 100644
--- a/Documentation/devicetree/bindings/clock/qcom,gcc.txt
+++ b/Documentation/devicetree/bindings/clock/qcom,gcc.txt
@@ -18,6 +18,7 @@
"qcom,gcc-mdm9615"
"qcom,gcc-sdm845"
"qcom,gcc-sdm845-v2"
+ "qcom,gcc-sdm845-v2.1"
"qcom,gcc-sdm670"
"qcom,debugcc-sdm845"
diff --git a/Documentation/devicetree/bindings/crypto/msm/ice.txt b/Documentation/devicetree/bindings/crypto/msm/ice.txt
index 2d0e580..fe8671f 100644
--- a/Documentation/devicetree/bindings/crypto/msm/ice.txt
+++ b/Documentation/devicetree/bindings/crypto/msm/ice.txt
@@ -5,16 +5,22 @@
- reg : <register mapping>
Optional properties:
- - interrupt-names : name describing the interrupts for ICE IRQ
- - interrupts : <interrupt mapping for ICE IRQ>
- - qcom,enable-ice-clk : should enable clocks for ICE HW
- - clocks : List of phandle and clock specifier pairs
- - clock-names : List of clock input name strings sorted in the same
- order as the clocks property.
- - qocm,op-freq-hz : max clock speed sorted in the same order as the clocks
- property.
- - qcom,instance-type : describe the storage type for which ICE node is defined
- currently, only "ufs" and "sdcc" are supported storage type
+ - interrupt-names : name describing the interrupts for ICE IRQ
+ - interrupts : <interrupt mapping for ICE IRQ>
+ - qcom,enable-ice-clk : should enable clocks for ICE HW
+ - clocks : List of phandle and clock specifier pairs
+ - clock-names : List of clock input name strings sorted in the same
+ order as the clocks property.
+ - qocm,op-freq-hz : max clock speed sorted in the same order as the clocks
+ property.
+ - qcom,instance-type : describe the storage type for which ICE node is defined
+ currently, only "ufs" and "sdcc" are supported storage type
+ - vdd-hba-supply : regulated supply to be used by ICE HW
+ - qcom,msm-bus,name : bus for ICE transactions
+ - qcom,msm-bus,num-cases : bus case mapping for ICE HW
+ - qcom,msm-bus,num-paths : bus path mapping for iCE HW
+ - qcom,msm-bus,vectors-KBps : bus bandwidth to be voted
+ - qcom,bus-vector-names : bus vectors mapping
Example:
ufs_ice: ufsice@630000 {
@@ -30,3 +36,26 @@
qcom,instance-type = "ufs";
status = "disabled";
};
+
+ ufs_card_ice: ufscardice@1db0000 {
+ compatible = "qcom,ice_card";
+ reg = <0x1db0000 0x8000>;
+ qcom,enable-ice-clk;
+ clock-names = "ufs_core_clk", "bus_clk",
+ "iface_clk", "ice_core_clk";
+ clocks = <&clock_gcc GCC_UFS_CARD_AXI_CLK>,
+ <&clock_gcc GCC_UFS_CARD_CLKREF_CLK>,
+ <&clock_gcc GCC_UFS_CARD_AHB_CLK>,
+ <&clock_gcc GCC_UFS_CARD_ICE_CORE_CLK>;
+ qcom,op-freq-hz = <0>, <0>, <0>, <300000000>;
+ vdd-hba-supply = <&ufs_card_gdsc>;
+ qcom,msm-bus,name = "ufs_card_ice_noc";
+ qcom,msm-bus,num-cases = <2>;
+ qcom,msm-bus,num-paths = <1>;
+ qcom,msm-bus,vectors-KBps =
+ <1 650 0 0>, /* No vote */
+ <1 650 1000 0>; /* Max. bandwidth */
+ qcom,bus-vector-names = "MIN",
+ "MAX";
+ qcom,instance-type = "ufs_card";
+ };
diff --git a/Documentation/devicetree/bindings/crypto/msm/qcedev.txt b/Documentation/devicetree/bindings/crypto/msm/qcedev.txt
index 051b315..6f1d8e3 100644
--- a/Documentation/devicetree/bindings/crypto/msm/qcedev.txt
+++ b/Documentation/devicetree/bindings/crypto/msm/qcedev.txt
@@ -20,7 +20,7 @@
- qcom,ce-hw-key : optional, indicates if the hardware supports use of HW KEY.
- qcom,support-core-clk-only : optional, indicates if the HW supports single crypto core clk.
- qcom,bsm-ee : optional, indicate the BAM EE value, changes from target to target. Default value is 1 if not specified.
- - qcom,smmu-s1-bypass : Boolean flag to bypass SMMU stage 1 translation.
+ - 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.
Example:
diff --git a/Documentation/devicetree/bindings/crypto/msm/qcrypto.txt b/Documentation/devicetree/bindings/crypto/msm/qcrypto.txt
index fa27198..231f31a 100644
--- a/Documentation/devicetree/bindings/crypto/msm/qcrypto.txt
+++ b/Documentation/devicetree/bindings/crypto/msm/qcrypto.txt
@@ -40,7 +40,7 @@
required. For other targets such as fsm, they do not perform
bus scaling. It is not required for those targets.
- - qcom,smmu-s1-bypass : Boolean flag to bypass SMMU stage 1 translation.
+ - qcom,smmu-s1-enable : Boolean flag to bypass SMMU stage 1 translation.
- iommus : A list of phandle and IOMMU specifier pairs that describe the IOMMU master interfaces of the device.
Example:
diff --git a/Documentation/devicetree/bindings/devfreq/arm-memlat-mon.txt b/Documentation/devicetree/bindings/devfreq/arm-memlat-mon.txt
index 67dc991..6f2fac7 100644
--- a/Documentation/devicetree/bindings/devfreq/arm-memlat-mon.txt
+++ b/Documentation/devicetree/bindings/devfreq/arm-memlat-mon.txt
@@ -15,6 +15,8 @@
Defaults to 0x17 if not specified.
- qcom,inst-ev: The instruction count event that this monitor is supposed to measure.
Defaults to 0x08 if not specified.
+- qcom,stall-cycle-ev: The stall cycle count that this monitor is supposed to measure.
+ Assumes 100% stall if not specified.
Example:
@@ -24,6 +26,7 @@
qcom,target-dev = <&memlat0>;
qcom,cachemiss-ev = <0x2A>;
qcom,inst-ev = <0x08>;
+ qcom,stall-cycle-ev = <0xE7>;
qcom,core-dev-table =
< 300000 1525>,
< 499200 3143>,
diff --git a/Documentation/devicetree/bindings/display/msm/sde.txt b/Documentation/devicetree/bindings/display/msm/sde.txt
index 2782428..22b4e91 100644
--- a/Documentation/devicetree/bindings/display/msm/sde.txt
+++ b/Documentation/devicetree/bindings/display/msm/sde.txt
@@ -331,9 +331,22 @@
- qcom,sde-danger-lut: A 4 cell property, with a format of <linear,
tile, nrt, cwb>,
indicating the danger luts on sspp.
-- qcom,sde-safe-lut: A 4 cell property, with a format of <linear,
- tile, nrt, cwb>,
- indicating the safe luts on sspp.
+- qcom,sde-safe-lut-linear: Array of 2 cell property, with a format of
+ <fill level, lut> in ascending fill level
+ indicating the safe luts for linear format on sspp.
+ Zero fill level on the last entry identifies the default lut.
+- qcom,sde-safe-lut-macrotile: Array of 2 cell property, with a format of
+ <fill level, lut> in ascending fill level
+ indicating the safe luts for macrotile format on sspp.
+ Zero fill level on the last entry identifies the default lut.
+- qcom,sde-safe-lut-nrt: Array of 2 cell property, with a format of
+ <fill level, lut> in ascending fill level
+ indicating the safe luts for nrt (e.g wfd) on sspp.
+ Zero fill level on the last entry identifies the default lut.
+- qcom,sde-safe-lut-cwb: Array of 2 cell property, with a format of
+ <fill level, lut> in ascending fill level
+ indicating the safe luts for cwb on sspp.
+ Zero fill level on the last entry identifies the default lut.
- qcom,sde-qos-lut-linear: Array of 3 cell property, with a format of
<fill level, lut hi, lut lo> in ascending fill level
indicating the qos luts for linear format on sspp.
@@ -367,6 +380,14 @@
match the number of xin-ids defined in
property: qcom,sde-inline-rot-xin
- #power-domain-cells: Number of cells in a power-domain specifier and should contain 0.
+- qcom,sde-mixer-display-pref: A string array indicating the preferred display type
+ for the mixer block. Possible values:
+ "primary" - preferred for primary display
+ "none" - no preference on display
+- qcom,sde-ctl-display-pref: A string array indicating the preferred display type
+ for the ctl block. Possible values:
+ "primary" - preferred for primary display
+ "none" - no preference on display
Bus Scaling Subnodes:
- qcom,sde-reg-bus: Property to provide Bus scaling for register access for
@@ -452,8 +473,12 @@
qcom,sde-off = <0x1000>;
qcom,sde-ctl-off = <0x00002000 0x00002200 0x00002400
0x00002600 0x00002800>;
+ qcom,sde-ctl-display-pref = "primary", "none", "none",
+ "none", "none";
qcom,sde-mixer-off = <0x00045000 0x00046000
0x00047000 0x0004a000>;
+ qcom,sde-mixer-display-pref = "primary", "none",
+ "none", "none";
qcom,sde-dspp-top-off = <0x1300>;
qcom,sde-dspp-off = <0x00055000 0x00057000>;
qcom,sde-dspp-ad-off = <0x24000 0x22800>;
diff --git a/Documentation/devicetree/bindings/interrupt-controller/qti,pdc.txt b/Documentation/devicetree/bindings/interrupt-controller/qti,pdc.txt
index 26cd70e..07667a4 100644
--- a/Documentation/devicetree/bindings/interrupt-controller/qti,pdc.txt
+++ b/Documentation/devicetree/bindings/interrupt-controller/qti,pdc.txt
@@ -29,6 +29,7 @@
Definition: Should contain "qcom,pdc-<target>"
* "qcom,pdc-sdm845": For sdm845 pin data
+ * "qcom,pdc-sdm845-v2": For sdm845 v2 pin data
* "qcom,pdc-sdm670": For sdm670 pin data
- reg:
diff --git a/Documentation/devicetree/bindings/media/video/msm-cam-cpas.txt b/Documentation/devicetree/bindings/media/video/msm-cam-cpas.txt
index 99f3ba2..4b16103 100644
--- a/Documentation/devicetree/bindings/media/video/msm-cam-cpas.txt
+++ b/Documentation/devicetree/bindings/media/video/msm-cam-cpas.txt
@@ -66,6 +66,11 @@
Value type: <u32>
Definition: Interrupt associated with CAMNOC HW.
+- qcom,cpas-hw-ver
+ Usage: required
+ Value type: <u32>
+ Definition: CAM HW Version information.
+
- regulator-names
Usage: required
Value type: <string>
@@ -181,6 +186,7 @@
reg-cam-base = <0x40000 0x42000>;
interrupt-names = "cpas_camnoc";
interrupts = <0 459 0>;
+ qcom,cpas-hw-ver = <0x170100>; /* Titan v170 v1.0.0 */
regulator-names = "camss-vdd";
camss-vdd-supply = <&titan_top_gdsc>;
clock-names = "gcc_ahb_clk",
diff --git a/Documentation/devicetree/bindings/mmc/sdhci-msm.txt b/Documentation/devicetree/bindings/mmc/sdhci-msm.txt
index d3098be..6222881 100644
--- a/Documentation/devicetree/bindings/mmc/sdhci-msm.txt
+++ b/Documentation/devicetree/bindings/mmc/sdhci-msm.txt
@@ -86,7 +86,11 @@
contents will not be retained. It is software responsibility to restore the
SDCC registers before resuming to normal operation.
- qcom,force-sdhc1-probe: Force probing sdhc1 even if it is not the boot device.
-
+ - qcom,ddr-config: Certain chipsets and platforms require particular settings for
+ the RCLK delay DLL configuration register for HS400 mode to work.
+ This value can vary between platforms and msms. If a msm/platform
+ require a different DLL setting than the default/POR setting for
+ HS400 mode, it can be specified using this field.
In the following, <supply> can be vdd (flash core voltage) or vdd-io (I/O voltage).
- qcom,<supply>-always-on - specifies whether supply should be kept "on" always.
- qcom,<supply>-lpm_sup - specifies whether supply can be kept in low power mode (lpm).
diff --git a/Documentation/devicetree/bindings/pci/msm_pcie.txt b/Documentation/devicetree/bindings/pci/msm_pcie.txt
index 2a5096f..6af2bac 100644
--- a/Documentation/devicetree/bindings/pci/msm_pcie.txt
+++ b/Documentation/devicetree/bindings/pci/msm_pcie.txt
@@ -95,6 +95,9 @@
and assign for each endpoint.
- qcom,ep-latency: The time (unit: ms) to wait for the PCIe endpoint to become
stable after power on, before de-assert the PERST to the endpoint.
+ - qcom,switch-latency: The time (unit: ms) to wait for the PCIe endpoint's link
+ training with switch downstream port after the link between switch upstream
+ port and RC is up.
- qcom,wr-halt-size: With base 2, this exponent determines the size of the
data that PCIe core will halt on for each write transaction.
- qcom,slv-addr-space-size: The memory space size of PCIe Root Complex.
@@ -268,6 +271,7 @@
qcom,smmu-exist;
qcom,smmu-sid-base = <0x1480>;
qcom,ep-latency = <100>;
+ qcom,switch-latency = <100>;
qcom,wr-halt-size = <0xa>; /* 1KB */
qcom,slv-addr-space-size = <0x1000000>; /* 16MB */
qcom,cpl-timeout = <0x2>;
diff --git a/Documentation/devicetree/bindings/pil/pil-q6v5-mss.txt b/Documentation/devicetree/bindings/pil/pil-q6v5-mss.txt
index 85b0fe9..abbc560 100644
--- a/Documentation/devicetree/bindings/pil/pil-q6v5-mss.txt
+++ b/Documentation/devicetree/bindings/pil/pil-q6v5-mss.txt
@@ -32,6 +32,7 @@
This may be a shared regulator that is already voted
on in the PIL proxy voting code (and also managed by the
modem on its own), hence we mark it as as optional.
+- vdd_mss-uV: Voltage to set for vdd_mss.
- vdd_pll-supply: Reference to the regulator that supplies the PLL's rail.
- qcom,vdd_pll: Voltage to be set for the PLL's rail.
- reg-names: "cxrail_bhs_reg" - control register for modem power
diff --git a/Documentation/devicetree/bindings/pinctrl/qcom,msm8953-pinctrl.txt b/Documentation/devicetree/bindings/pinctrl/qcom,msm8953-pinctrl.txt
index d4bf1ce..4b483e5 100644
--- a/Documentation/devicetree/bindings/pinctrl/qcom,msm8953-pinctrl.txt
+++ b/Documentation/devicetree/bindings/pinctrl/qcom,msm8953-pinctrl.txt
@@ -1,4 +1,4 @@
-Qualcomm MSM8953 TLMM block
+Qualcomm Technologies, Inc. MSM8953 TLMM block
This binding describes the Top Level Mode Multiplexer block found in the
MSM8953 platform.
diff --git a/Documentation/devicetree/bindings/platform/msm/ipa.txt b/Documentation/devicetree/bindings/platform/msm/ipa.txt
index e821feb..aede546 100644
--- a/Documentation/devicetree/bindings/platform/msm/ipa.txt
+++ b/Documentation/devicetree/bindings/platform/msm/ipa.txt
@@ -28,7 +28,6 @@
- qcom,lan-rx-ring-size: size of LAN rx ring, default is 192
- qcom,arm-smmu: SMMU is present and ARM SMMU driver is used
- qcom,msm-smmu: SMMU is present and QSMMU driver is used
-- qcom,smmu-s1-bypass: Boolean context flag to set SMMU to S1 bypass
- qcom,smmu-fast-map: Boolean context flag to set SMMU to fastpath mode
- ipa_smmu_ap: AP general purpose SMMU device
compatible "qcom,ipa-smmu-ap-cb"
@@ -122,6 +121,9 @@
-compatible: "qcom,ipa-smmu-uc-cb" - represents IPA uC context bank (for uC
offload scenarios).
+
+- qcom,smmu-s1-bypass: Boolean context flag to set SMMU to S1 bypass.
+
- iommus : the phandle and stream IDs for the SMMU used by this root
- qcom,iova-mapping: specifies the start address and size of iova space.
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 d205b0b..f50fd88 100644
--- a/Documentation/devicetree/bindings/power/supply/qcom/qpnp-fg-gen3.txt
+++ b/Documentation/devicetree/bindings/power/supply/qcom/qpnp-fg-gen3.txt
@@ -208,14 +208,14 @@
Value type: <u32>
Definition: Lower limit of battery temperature to start the capacity
learning. If this is not specified, then the default value
- used will be 150. Unit is in decidegC.
+ used will be 150 (15 C). Unit is in decidegC.
- qcom,cl-max-temp
Usage: optional
Value type: <u32>
Definition: Upper limit of battery temperature to start the capacity
learning. If this is not specified, then the default value
- used will be 450 (45C). Unit is in decidegC.
+ used will be 500 (50 C). Unit is in decidegC.
- qcom,cl-max-increment
Usage: optional
diff --git a/Documentation/devicetree/bindings/power/supply/qcom/qpnp-fg.txt b/Documentation/devicetree/bindings/power/supply/qcom/qpnp-fg.txt
new file mode 100644
index 0000000..f6a7a1b
--- /dev/null
+++ b/Documentation/devicetree/bindings/power/supply/qcom/qpnp-fg.txt
@@ -0,0 +1,275 @@
+QTI's QPNP PMIC Fuel Gauge Device
+
+QPNP PMIC FG provides interface to clients to read properties related
+to the battery. Its main function is to retrieve the State of Charge (SOC),
+a 0-100 percentage representing the amount of charge left in the battery.
+
+There are two required peripherals in the FG driver, both implemented as
+subnodes in the example. These peripherals must not be disabled if the FG
+device is to enabled:
+
+- qcom,fg-soc : The main FG device. Supports battery fuel gauge controls and
+ sensors.
+- qcom,fg-batt : The FG battery device supports interrupts and controls with
+ respect to the state of the connected battery.For example: the
+ peripheral informs the driver if the battery has been identified
+ by the fuel gauge based on a given battery resistance range.
+
+Optionally ADC nodes can be added
+- qcom,revid-tp-rev: A subnode with a register address for the TP_REV register
+ in the REVID peripheral. This is used to apply workarounds that
+ may depend on the trim program.
+- qcom,fg-adc-vbat : A subnode with a register address for the FG_ADC_USR
+ peripheral which is used mainly for battery current limiting (BCL).
+ This node maps out the VBAT reading register which allows to have
+ a +/- 32 mV accurate reading of VBAT.
+- qcom,fg-adc-ibat : A subnode with a register address for the FG_ADC_USR
+ peripheral which is used mainly for battery current limiting (BCL).
+ This node maps out the IBAT current reading register which allows
+ to have a +/- 32 mA accurate reading of IBAT.
+
+Parent node required properties:
+- compatible : should be "qcom,qpnp-fg" for the FG driver.
+- qcom,pmic-revid : Should specify the phandle of PMIC
+ revid module. This is used to identify
+ the PMIC subtype.
+
+Parent node optional properties:
+- qcom,warm-bat-decidegc: Warm battery temperature in decidegC.
+- qcom,cool-bat-decidegc: Cool battery temperature in decidegC.
+- qcom,hot-bat-decidegc: Hot battery temperature in decidegC.
+- qcom,cold-bat-decidegc: Cold battery temperature in decidegC.
+- qcom,cold-hot-jeita-hysteresis: A tuple of 2. Index[0] is cold
+ hysteresis and index[1] is hot
+ hysterisis(in decidegC).
+- qcom,ext-sense-type: Current sense channel used by the FG.
+ Set this to use external rsense.
+- qcom,thermal-coefficients: Byte array of thermal coefficients for
+ reading battery thermistor. This should
+ be exactly 6 bytes in length.
+ Example: [01 02 03 04 05 06]
+- qcom,resume-soc: soc to resume charging in percentage.
+- qcom,resume-soc-raw: soc to resume charging in the scale of
+ [0-255]. This overrides qcom,resume-soc
+ if defined.
+- qcom,hold-soc-while-full: A boolean property that when defined
+ holds SOC at 100% when the battery is
+ full.
+- qcom,bcl-lm-threshold-ma: BCL LPM to MPM mode transition threshold
+ in milliAmpere.
+- qcom,bcl-mh-threshold-ma: BCL MPM to HPM mode transition threshold
+ in milliAmpere.
+- qcom,use-otp-profile: Specify this flag to avoid RAM loading
+ any battery profile.
+- qcom,sw-rbias-control: Boolean property which defines whether
+ the Rbias needs to be controlled by
+ software. If this is not set, it will
+ be controlled by hardware (default).
+- qcom,fg-iterm-ma: Battery current at which the fuel gauge
+ will try to scale 100% towards. When
+ the charge current goes above this, the
+ SoC should be at 100%.
+- qcom,fg-chg-iterm-ma: Battery current at which the fuel gauge
+ will issue end of charge if the charger
+ is configured to use the fuel gauge
+ ADCs for end of charge detection. This
+ property is in milliamps and should be
+ positive (e.g. 100mA to terminate at
+ -100mA).
+- qcom,irq-volt-empty-mv: The voltage threshold that the empty
+ soc interrupt will be triggered. When
+ the empty soc interrupt fires, battery
+ soc will be pulled to 0 and the
+ userspace will be notified via the
+ power supply framework. The userspace
+ will read 0% soc and immediately
+ shutdown.
+- qcom,fg-cutoff-voltage-mv: The voltage where the fuel gauge will
+ steer the SOC to be zero. For example,
+ if the cutoff voltage is set to 3400mv,
+ the fuel gauge will try to count SoC so
+ that the battery SoC will be 0 when it
+ is 3400mV.
+- qcom,fg-vbat-estimate-diff-mv: If the estimated voltage based on SoC
+ and battery current/resistance differs
+ from the actual voltage by more than
+ this amount, the fuel gauge will
+ redo the first SoC estimate when the
+ driver probes.
+- qcom,fg-delta-soc: How many percent the monotonic SoC must
+ change before a new delta_soc interrupt
+ is asserted. If this value is raised
+ above 3-4, some period workarounds may
+ not function well, so it's best to
+ leave this at 1 or 2%.
+- qcom,fg-vbatt-low-threshold: Voltage (in mV) which upon set will be
+ used for configuring the low battery
+ voltage threshold. Interrupt will be
+ asserted and handled based upon
+ this. If this property is not specified,
+ low battery voltage threshold will be
+ configured to 4200 mV.
+- qcom,cycle-counter-en: Boolean property which enables the cycle
+ counter feature. If this property is
+ present, then the following properties
+ to specify low and high soc thresholds
+ should be defined.
+- qcom,capacity-learning-on: A boolean property to have the fuel
+ gauge driver attempt to learn the
+ battery capacity when charging. Takes
+ precedence over capacity-estimation-on.
+- qcom,capacity-learning-feedback: A boolean property to have the fuel
+ gauge driver to feedback the learned
+ capacity into the capacity learning
+ algorithm. This has to be used only if
+ the property "qcom,capacity-learning-on"
+ is specified.
+- qcom,cl-max-increment-deciperc: The maximum percent that the capacity
+ can rise as the result of a single
+ charge cycle. This property corresponds
+ to .1% increments.
+- qcom,cl-max-decrement-deciperc: The maximum percent that the capacity
+ can fall as the result of a single
+ charge cycle. This property corresponds
+ to .1% decrements.
+- qcom,cl-max-temp-decidegc: Above this temperature, capacity
+ learning will be canceled.
+- qcom,cl-mix-temp-decidegc: Below this temperature, capacity
+ learning will be canceled.
+- qcom,cl-max-start-soc: The battery soc has to be below this
+ value at the start of a charge cycle
+ for capacity learning to be run.
+- qcom,cl-vbat-est-thr-uv: The maximum difference between the
+ battery voltage shadow and the current
+ predicted voltage in uV to initiate
+ capacity learning.
+- qcom,capacity-estimation-on: A boolean property to have the fuel
+ gauge driver attempt to estimate the
+ battery capacity using battery
+ resistance.
+- qcom,aging-eval-current-ma: Current used to evaluate battery aging.
+ This value should be around the steady
+ state current drawn from the battery
+ when the phone is low on battery.
+- qcom,fg-cc-cv-threshold-mv: Voltage threshold in mV for configuring
+ constant charge (CC) to constant
+ voltage (CV) setpoint in FG upon
+ which the battery EOC status will
+ be determined. This value should be
+ 10 mV less than the float voltage
+ configured in the charger.
+ This property should only be specified
+ if "qcom,autoadjust-vfloat" property is
+ specified in the charger driver to
+ ensure a proper operation.
+- qcom,bad-battery-detection-enable: A boolean property to enable the fuel
+ gauge driver to detect the damaged battery
+ when the safety-timer expires by using the
+ coulomb count.
+- qcom,fg-therm-delay-us: The time in microseconds to delay battery
+ thermistor biasing.
+- qcom,esr-pulse-tuning-en: A boolean property to enable ESR pulse
+ tuning feature. If this is enabled,
+ ESR pulse extraction will be disabled
+ when state of charge (SOC) is less than
+ 2%. It will be enabled back when SOC
+ gets above 2%. In addition, for SOC
+ between 2% and 5%, ESR pulse timing
+ settings will be different from default.
+ Once SOC crosses 5%, ESR pulse timings
+ will be restored back to default.
+
+qcom,fg-soc node required properties:
+- reg : offset and length of the PMIC peripheral register map.
+- interrupts : the interrupt mappings.
+ The format should be
+ <slave-id peripheral-id interrupt-number>.
+- interrupt-names : names for the mapped fg soc interrupts
+ The following interrupts are required:
+ 0: high-soc
+ 1: low-soc
+ 2: full-soc
+ 3: empty-soc
+ 4: delta-soc
+ 5: first-est-done
+ 6: sw-fallbk-ocv
+ 7: sw-fallbk-new-batt
+
+qcom,fg-memif node required properties:
+- reg : offset and length of the PMIC peripheral register map.
+- interrupts : the interrupt mappings.
+ The format should be
+ <slave-id peripheral-id interrupt-number>.
+- interrupt-names : names for the mapped fg adc interrupts
+ The following interrupts are required:
+ 0: mem-avail
+
+Example:
+pmi8994_fg: qcom,fg {
+ compatible = "qcom,qpnp-fg";
+ #address-cells = <1>;
+ #size-cells = <1>;
+ status = "disabled";
+ qcom,pmic-revid = <&pmi8994_revid>;
+
+ qcom,fg-soc@4000 {
+ reg = <0x4000 0x100>;
+ interrupts = <0x2 0x40 0x0>,
+ <0x2 0x40 0x1>,
+ <0x2 0x40 0x2>,
+ <0x2 0x40 0x3>,
+ <0x2 0x40 0x4>,
+ <0x2 0x40 0x5>,
+ <0x2 0x40 0x6>,
+ <0x2 0x40 0x7>;
+
+ interrupt-names = "high-soc",
+ "low-soc",
+ "full-soc",
+ "empty-soc",
+ "delta-soc",
+ "first-est-done",
+ "sw-fallbk-ocv",
+ "sw-fallbk-new-batt";
+ };
+
+ qcom,fg-batt@4100 {
+ reg = <0x4100 0x100>;
+ interrupts = <0x2 0x41 0x0>,
+ <0x2 0x41 0x1>,
+ <0x2 0x41 0x2>,
+ <0x2 0x41 0x3>,
+ <0x2 0x41 0x4>,
+ <0x2 0x41 0x5>,
+ <0x2 0x41 0x6>,
+ <0x2 0x41 0x7>;
+
+ interrupt-names = "soft-cold",
+ "soft-hot",
+ "vbatt-low",
+ "batt-ided",
+ "batt-id-req",
+ "batt-unknown",
+ "batt-missing",
+ "batt-match";
+ };
+
+ qcom,fg-adc-vbat@4254 {
+ reg = <0x4254 0x1>;
+ };
+
+ qcom,fg-adc-ibat@4255 {
+ reg = <0x4255 0x1>;
+ };
+
+ qcom,fg-memif@4400 {
+ reg = <0x4400 0x100>;
+ interrupts = <0x2 0x44 0x0>,
+ <0x2 0x44 0x1>;
+
+ interrupt-names = "mem-avail",
+ "data-rcvry-sug";
+
+ qcom,cold-hot-jeita-hysteresis = <30 50>;
+ };
+};
diff --git a/Documentation/devicetree/bindings/power/supply/qcom/qpnp-smbcharger.txt b/Documentation/devicetree/bindings/power/supply/qcom/qpnp-smbcharger.txt
new file mode 100644
index 0000000..efd64cd
--- /dev/null
+++ b/Documentation/devicetree/bindings/power/supply/qcom/qpnp-smbcharger.txt
@@ -0,0 +1,394 @@
+QPNP SMB Battery Charger
+
+QPNP SMB Charger is a single-cell switching mode battery charger. It can charge
+the battery and power the system via the USB and AC adapter input.
+
+The QPNP SMB Charger interfaces via the SPMI bus.
+
+There are six different peripherals adding the following functionality.
+Each of these peripherals are implemented as subnodes in the example at the
+end of this file.
+
+- qcom,chgr: Supports charging control and status
+ reporting.
+- qcom,bat-if: Battery status reporting such as presence,
+ temperature reporting and voltage collapse
+ protection.
+- qcom,usb-chgpth: USB charge path detection and input current
+ limiting configuration.
+- qcom,dc-chgpth: DC charge path detection and input current
+ limiting configuration.
+- qcom,chg-misc: Miscellaneous features such as watchdog timers
+ and SYSOK pin control
+- qcom,chg-otg: OTG configuration control.
+
+Parent node required properties:
+- compatible: Must be "qcom,qpnp-smbcharger"
+- #address-cells: Must be <1>
+- #size-cells: Must be <1>
+- qcom,pmic-revid: Should specify the phandle of PMIC
+ revid module. This is used to identify
+ the PMIC subtype.
+
+
+
+Sub node required properties:
+- reg: The SPMI address for this peripheral
+- interrupts: Specifies the interrupt associated with the peripheral.
+- interrupt-names: Specifies the interrupt names for the peripheral. Every
+ available interrupt needs to have an associated name
+ with it to indentify its purpose.
+
+ The following lists each subnode and their corresponding
+ required interrupt names:
+
+ qcom,chgr:
+ - chg-tcc-thr: Triggers on charge completion.
+ - chg-taper-thr: Triggers on the taper charge
+ transtion.
+ - chg-inhibit: Notifies on battery voltage
+ being too high to resume
+ charging.
+ - chg-p2f-thr: Triggers on transitioning from
+ precharge to fastcharge.
+ - chg-rechg-thr: Triggers on battery voltage
+ falling below the resume
+ threshold.
+
+ qcom,bat-if:
+ - batt-hot: Triggers on battery temperature
+ hitting the hot threshold.
+ Charging stops.
+ - batt-warm: Triggers on battery temperature
+ hitting the warm threshold.
+ Charging current is reduced.
+ - batt-cool: Triggers on battery temperature
+ hitting the cool threshold.
+ Charging current is reduced
+ - batt-cold: Triggers on battery temperature
+ hitting the cold threshold.
+ Charging stops.
+ - batt-missing: Battery missing status
+ interrupt.
+ - batt-low: Triggers on battery voltage
+ falling across a low threshold.
+
+ qcom,usb-chgpth:
+ - usbin-uv: USB input voltage falls below a
+ valid threshold.
+ - usbin-src-det: USB automatic source detection
+ finishes.
+
+ qcom,dc-chgpth:
+ - dcin-uv: DC input voltage falls below a
+ valid threshold.
+
+ qcom,chgr-misc:
+ - wdog-timeout-mins: Charger watchdog timer
+ interrupt.
+ - temp-shutdown: Triggers when charger goes
+ overtemp and causes a shutdown.
+ - power-ok: Triggers when the charger
+ switcher turns on or off.
+
+Regulator Subnodes:
+- qcom,smbcharger-boost-otg A subnode for a regulator device that turns on
+ the charger boost for OTG operation.
+- qcom,smbcharger-external-otg A subnode for a regulator device that switches
+ off charging and the USB input charge path
+ in order to allow an external regulator to
+ operate. This can be used in place of the
+ qcom,smbcharger-boost-otg if an external boost
+ is available.
+
+Regulator Sub node required properties:
+- regulator-name A name string for the regulator in question
+
+Optional Properties:
+- qcom,battery-psy-name The name of the main battery power supply that
+ the charger will register. Failing to define
+ this property will default the name to
+ "battery".
+- qcom,bms-psy-name The psy name to use for reporting battery
+ capacity. If left unspecified the capacity uses
+ a preprogrammed default value of 50.
+- qcom,float-voltage-mv Float Voltage in mV - the maximum voltage up
+ to which the battery is charged. Supported
+ range 3600mV to 4500mV
+- qcom,float-voltage-comp Specifies the JEITA float voltage compensation.
+ Value ranges from 0 to 63.
+- qcom,fastchg-current-ma Specifies the fast charge current in mA. Supported
+ range is from 300mA to 3000mA.
+- qcom,fastchg-current-comp Specifies the fast charge current compensation in
+ mA. Supported values are 250, 700, 900 and 1200mA.
+- qcom,charging-timeout-mins Maximum duration in minutes that a single
+ charge cycle may last. Supported values are:
+ 0, 192, 384, 768, and 1536. A value of 0
+ means that no charge cycle timeout is used and
+ charging can continue indefinitely.
+- qcom,precharging-timeout-mins Maximum duration in minutes that a single
+ precharge cycle may last. Supported values
+ are: 0, 24, 48, 96, 192. A value of 0 means
+ that no precharge cycle timeout is used and
+ charging can continue indefinitely. Note that
+ the qcom,charging-timeout-mins property must
+ be specified in order for this to take effect.
+- qcom,dc-psy-type The type of charger connected to the DC path.
+ Can be "Mains", "Wireless" or "Wipower"
+- qcom,dc-psy-ma The current in mA dc path can support. Must be
+ specified if dc-psy-type is specified. Valid
+ range 300mA to 2000mA.
+- qcom,dcin-vadc The phandle to pmi8994 voltage adc. The ADC is
+ used to get notifications when the DCIN voltage
+ crosses a programmed min/max threshold. This is
+ used to make configurations for optimized power
+ draw for Wipower.
+- qcom,wipower-div2-ilim-map
+- qcom,wipower-pt-ilim-map
+- qcom,wipower-default-ilim-map
+ Array of 5 elements to indicate the voltage ranges and their corresponding
+ current limits. The 5 elements with index [0..4] are:
+ [0] => voltage_low in uV
+ [1] => voltage_high in uV
+ [2] => current limit for pass through in mA
+ [3] => current limit for div2 mode dcin low voltage in mA
+ [4] => current limit for div2 mode dcin high voltage in mA
+ The div2 and pt tables indicate the current limits
+ to use when Wipower is operating in divide_by_2 mode
+ and pass through mode respectively.
+ The default table is used when the voltage ranges
+ are beyond the ones specified in the mapping table.
+ Note that if dcin-vadc or any of these mapping
+ tables are not specified, dynamic dcin input
+ is disabled.
+- qcom,charging-disabled Set this if charging should be disabled in the
+ build by default.
+- qcom,resume-delta-mv Specifies the minimum voltage drop in
+ millivolts below the float voltage that is
+ required in order to initiate a new charging
+ cycle. Supported values are: 50, 100, 200 and
+ 300mV.
+- qcom,chg-inhibit-en Boolean that indicates whether the charge inhibit
+ feature needs to be enabled. If this is not set,
+ charge inhibit feature is disabled by default.
+- qcom,chg-inhibit-fg Indicates if the recharge threshold source has
+ to be Fuel gauge ADC. If this is not set, it
+ will be analog sensor by default.
+- qcom,bmd-algo-disabled Indicates if the battery missing detection
+ algorithm is disabled. If this node is present
+ SMB uses the THERM pin for battery missing
+ detection.
+- qcom,charge-unknown-battery Boolean that indicates whether an unknown
+ battery without a matching profile will be
+ charged. If this is not set, if the fuel gauge
+ does not recognize the battery based on its
+ battery ID, the charger will not start
+ charging.
+- qcom,bmd-pin-src A string that indicates the source pin for the
+ battery missind detection. This can be either:
+ - "bpd_none"
+ battery is considered always present
+ - "bpd_id"
+ battery id pin is used
+ - "bpd_thm"
+ battery therm pin is used
+ - "bpd_thm_id"
+ both pins are used (battery is
+ considered missing if either pin is
+ floating).
+- qcom,iterm-ma Specifies the termination current to indicate
+ end-of-charge. Possible values in mA:
+ 50, 100, 150, 200, 250, 300, 500, 600.
+- qcom,iterm-disabled Disables the termination current feature. This
+ is a boolean property.
+- otg-parent-supply A phandle to an external boost regulator for
+ OTG if it exists.
+- qcom,thermal-mitigation: Array of input current limit values for
+ different system thermal mitigation levels.
+ This should be a flat array that denotates the
+ maximum charge current in mA for each thermal
+ level.
+- qcom,rparasitics-uohm: The parasitic resistance of the board following
+ the line from the battery connectors through
+ vph_power. This is used to calculate maximum
+ available current of the battery.
+- qcom,vled-max-uv: The maximum input voltage of the flash leds.
+ This is used to calculate maximum available
+ current of the battery.
+- qcom,autoadjust-vfloat A boolean property that when set, makes the
+ driver automatically readjust vfloat using the
+ fuel gauge ADC readings to make charging more
+ accurate.
+- qcom,jeita-temp-hard-limit property when present will enable or disable
+ the jeita temperature hard limit based on the
+ value 1 or 0. Specify 0 if the jeita temp hard
+ limit needs to be disabled. If it is not present,
+ jeita temperature hard limit will be based on what
+ the bootloader had set earlier.
+- qcom,low-volt-dcin: A boolean property which upon set will enable the
+ AICL deglitch configuration dynamically. This needs
+ to be set if the DCIN supply is going to be less
+ than or equal to 5V.
+- qcom,force-aicl-rerun: A boolean property which upon set will enable the
+ AICL rerun by default along with the deglitch time
+ configured to long interval (20 ms). Also, specifying
+ this property will not adjust the AICL deglitch time
+ dynamically for handling the battery over-voltage
+ oscillations when the charger is headroom limited.
+- qcom,aicl-rerun-period-s If force-aicl-rerun is on, this property dictates
+ how often aicl is reran in seconds. Possible values
+ are 45, 90, 180, and 360.
+- qcom,ibat-ocp-threshold-ua Maximum current before the battery will trigger
+ overcurrent protection. Use the recommended
+ battery pack value minus some margin.
+- qcom,soft-vfloat-comp-disabled Set this property when the battery is
+ powered via external source and could
+ go above the float voltage.
+- qcom,parallel-usb-min-current-ma Minimum current drawn by the primary
+ charger before enabling the parallel
+ charger if one exists. Do not define
+ this property if no parallel chargers
+ exist.
+- qcom,parallel-usb-9v-min-current-ma Minimum current drawn by the primary
+ charger before enabling the parallel
+ charger if one exists. This property
+ applies only for 9V chargers.
+- qcom,parallel-allowed-lowering-ma Acceptable current drop from the initial limit
+ to keep parallel charger activated. If the
+ charger current reduces beyond this threshold
+ parallel charger is disabled. Must be specified
+ if parallel charger is used.
+- qcom,parallel-main-chg-fcc-percent Percentage of the fast charge current allotted to the
+ main charger when parallel charging is enabled and
+ operational. If this property is not defined, the
+ driver defaults to a 50%/50% split between the main
+ and parallel charger.
+- qcom,parallel-main-chg-icl-percent Percentage of the input current allotted to the
+ main charger when parallel charging is enabled and
+ operational. If this property is not defined, the
+ driver defaults to a 60%/40% split between the main
+ and parallel charger.
+- qcom,battery-data Points to the phandle of node which
+ contains the battery-profiles supported
+ by the charger/FG.
+- qcom,chg-led-support A bool property to support the charger led feature.
+- qcom,chg-led-sw-controls A bool property to allow the software to control
+ the charger led without a valid charger.
+- qcom,skip-usb-notification A boolean property to be used when usb gets present
+ and type from other means. Especially true on
+ liquid hardware, where usb presence is detected based on GPIO.
+- qcom,skip-usb-suspend-for-fake-battery A boolean property to skip
+ suspending USB path for fake
+ battery.
+- qcom,vchg_sns-vadc Phandle of the VADC node.
+- qcom,vchg-adc-channel-id The ADC channel to which the VCHG is routed.
+
+Example:
+ qcom,qpnp-smbcharger {
+ compatible = "qcom,qpnp-smbcharger";
+ #address-cells = <1>;
+ #size-cells = <1>;
+
+ qcom,iterm-ma = <100>;
+ qcom,float-voltage-mv = <4200>;
+ qcom,resume-delta-mv = <100>;
+ qcom,bmd-pin-src = "bpd_thm_id";
+ qcom,dc-psy-type = "Mains";
+ qcom,dc-psy-ma = <1500>;
+ qcom,bms-psy-name = "bms";
+ qcom,battery-psy-name = "battery";
+ qcom,thermal-mitigation = <1500 700 600 325>;
+ qcom,vchg_sns-vadc = <&pmi8950_vadc>;
+ qcom,vchg-adc-channel-id = <3>;
+
+ qcom,chgr@1000 {
+ reg = <0x1000 0x100>;
+ interrupts = <0x2 0x10 0x0>,
+ <0x2 0x10 0x1>,
+ <0x2 0x10 0x2>,
+ <0x2 0x10 0x3>,
+ <0x2 0x10 0x4>,
+ <0x2 0x10 0x5>,
+ <0x2 0x10 0x6>,
+ <0x2 0x10 0x7>;
+
+ interrupt-names = "chg-error",
+ "chg-inhibit",
+ "chg-prechg-sft",
+ "chg-complete-chg-sft",
+ "chg-p2f-thr",
+ "chg-rechg-thr",
+ "chg-taper-thr",
+ "chg-tcc-thr";
+ };
+
+ qcom,otg@1100 {
+ reg = <0x1100 0x100>;
+ };
+
+ qcom,bat-if@1200 {
+ reg = <0x1200 0x100>;
+ interrupts = <0x2 0x12 0x0>,
+ <0x2 0x12 0x1>,
+ <0x2 0x12 0x2>,
+ <0x2 0x12 0x3>,
+ <0x2 0x12 0x4>,
+ <0x2 0x12 0x5>,
+ <0x2 0x12 0x6>,
+ <0x2 0x12 0x7>;
+
+ interrupt-names = "batt-hot",
+ "batt-warm",
+ "batt-cold",
+ "batt-cool",
+ "batt-ov",
+ "batt-low",
+ "batt-missing",
+ "batt-term-missing";
+ };
+
+ qcom,usb-chgpth@1300 {
+ reg = <0x1300 0x100>;
+ interrupts = <0x2 0x13 0x0>,
+ <0x2 0x13 0x1>,
+ <0x2 0x13 0x2>,
+ <0x2 0x13 0x3>,
+ <0x2 0x13 0x4>,
+ <0x2 0x13 0x5>,
+ <0x2 0x13 0x6>;
+
+ interrupt-names = "usbin-uv",
+ "usbin-ov",
+ "usbin-src-det",
+ "otg-fail",
+ "otg-oc",
+ "aicl-done",
+ "usbid-change";
+ };
+
+ qcom,dc-chgpth@1400 {
+ reg = <0x1400 0x100>;
+ interrupts = <0x2 0x14 0x0>,
+ <0x2 0x14 0x1>;
+
+ interrupt-names = "dcin-uv",
+ "dcin-ov";
+ };
+
+ qcom,chgr-misc@1600 {
+ reg = <0x1600 0x100>;
+ interrupts = <0x2 0x16 0x0>,
+ <0x2 0x16 0x1>,
+ <0x2 0x16 0x2>,
+ <0x2 0x16 0x3>,
+ <0x2 0x16 0x4>,
+ <0x2 0x16 0x5>;
+
+ interrupt-names = "power-ok",
+ "temp-shutdown",
+ "wdog-timeout",
+ "flash-fail",
+ "otst2",
+ "otst3";
+ };
+ };
diff --git a/Documentation/devicetree/bindings/regulator/qpnp-labibb-regulator.txt b/Documentation/devicetree/bindings/regulator/qpnp-labibb-regulator.txt
index c9cfc88..795ee95 100644
--- a/Documentation/devicetree/bindings/regulator/qpnp-labibb-regulator.txt
+++ b/Documentation/devicetree/bindings/regulator/qpnp-labibb-regulator.txt
@@ -82,8 +82,10 @@
- qcom,qpnp-lab-init-lcd-voltage: The default output voltage when LAB regulator
is configured in lcd mode.
- qcom,qpnp-lab-ps-threshold: The threshold in mA of Pulse Skip Mode for
- LAB regulator. Supported values are 20, 30,
- 40 and 50.
+ LAB regulator. Supported values for
+ PMI8994/6 are 20, 30, 40 and 50.
+ Supported values for PMI8998/PM660A are
+ 50, 60, 70 and 80.
- interrupts: Specify the interrupts as per the interrupt
encoding.
Currently "lab-vreg-ok" is required for
diff --git a/Documentation/devicetree/bindings/sound/qcom-audio-dev.txt b/Documentation/devicetree/bindings/sound/qcom-audio-dev.txt
index 57a227e..d4db970 100644
--- a/Documentation/devicetree/bindings/sound/qcom-audio-dev.txt
+++ b/Documentation/devicetree/bindings/sound/qcom-audio-dev.txt
@@ -2640,6 +2640,11 @@
- qcom,cdc-comp-gpios : phandle for compander gpios.
- qcom,cdc-dmic-gpios : phandle for Digital mic clk and data gpios.
- qcom,cdc-sdw-gpios : phandle for soundwire clk and data gpios.
+- qcom,pri-mi2s-gpios : phandle for primary MI2S clk, word select and data gpios.
+- qcom,sec-mi2s-gpios : phandle for secondary MI2S clk, word select and data gpios.
+- qcom,tert-mi2s-gpios : phandle for tertiary MI2S clk, word select and data gpios.
+- qcom,quat-mi2s-gpios : phandle for quaternary MI2S clk, word select and data gpios.
+- qcom,quin-mi2s-gpios : phandle for quinary MI2S clk, word select and data gpios.
- qcom,msm-mbhc-moist-cfg: This property is used to set moisture detection
threshold values for different codecs. First parameter is V(voltage)
second one is i(current), third one is r (resistance). Depending on the
@@ -2746,6 +2751,11 @@
- qcom,wsa-max-devs : Maximum number of WSA881x devices present in the target
- qcom,wsa-devs : List of phandles for all possible WSA881x devices supported for the target
- qcom,wsa-aux-dev-prefix : Name prefix with Left/Right configuration for WSA881x device
+- qcom,pri-mi2s-gpios : phandle for primary MI2S clk, word select and data gpios.
+- qcom,sec-mi2s-gpios : phandle for secondary MI2S clk, word select and data gpios.
+- qcom,tert-mi2s-gpios : phandle for tertiary MI2S clk, word select and data gpios.
+- qcom,quat-mi2s-gpios : phandle for quaternary MI2S clk, word select and data gpios.
+- qcom,quin-mi2s-gpios : phandle for quinary MI2S clk, word select and data gpios.
Example:
diff --git a/Documentation/devicetree/bindings/spi/qcom,spi-geni-qcom.txt b/Documentation/devicetree/bindings/spi/qcom,spi-geni-qcom.txt
index cd2d2ea..866d004 100644
--- a/Documentation/devicetree/bindings/spi/qcom,spi-geni-qcom.txt
+++ b/Documentation/devicetree/bindings/spi/qcom,spi-geni-qcom.txt
@@ -25,6 +25,10 @@
Documentation/devicetree/bindings/spi/spi-bus.txt
- qcom,wrapper-core: Wrapper QUPv3 core containing this SPI controller.
+Optional properties:
+- qcom,rt: Specifies if the framework worker thread for this
+ controller device should have "real-time" priority.
+
SPI slave nodes must be children of the SPI master node and can contain
properties described in Documentation/devicetree/bindings/spi/spi-bus.txt
diff --git a/Documentation/devicetree/bindings/ufs/ufshcd-pltfrm.txt b/Documentation/devicetree/bindings/ufs/ufshcd-pltfrm.txt
index c3e2cab..7f79f40 100644
--- a/Documentation/devicetree/bindings/ufs/ufshcd-pltfrm.txt
+++ b/Documentation/devicetree/bindings/ufs/ufshcd-pltfrm.txt
@@ -73,6 +73,8 @@
3: 52 MHz
Defaults to 26 MHz if not specified.
- extcon: phandle to external connector (Refer Documentation/devicetree/bindings/extcon/extcon-gpio.txt for more details).
+- non-removable : defines if the connected ufs device is not removable
+
Note: If above properties are not defined it can be assumed that the supply
regulators or clocks are always on.
diff --git a/Documentation/devicetree/bindings/uio/msm_sharedmem.txt b/Documentation/devicetree/bindings/uio/msm_sharedmem.txt
index 749c6e85..4c89846 100644
--- a/Documentation/devicetree/bindings/uio/msm_sharedmem.txt
+++ b/Documentation/devicetree/bindings/uio/msm_sharedmem.txt
@@ -9,10 +9,18 @@
- reg-names : Indicates various client-names.
- qcom,client-id : The client id for the QMI clients.
+Optional properties:
+- qcom,guard-memory: If this dtsi property is set, then the shared memory
+ region will be guarded by SZ_4K at the start and at the end.
+ This is needed to overcome the XPU limitation on few MSM HW,
+ so as to make this memory not contiguous with other allocations
+ that may possibly happen from other clients.
+
Example:
qcom,msm_sharedmem@0dc80000 {
compatible = "qcom,sharedmem-uio";
reg = <0x0dc80000 0x00180000>,
reg-names = "rmtfs";
qcom,client-id = <0x00000001>;
+ qcom,guard-memory;
};
diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig
index 21c66eb..63da745 100644
--- a/arch/arm/Kconfig
+++ b/arch/arm/Kconfig
@@ -55,6 +55,7 @@
select HAVE_FTRACE_MCOUNT_RECORD if (!XIP_KERNEL)
select HAVE_FUNCTION_GRAPH_TRACER if (!THUMB2_KERNEL)
select HAVE_FUNCTION_TRACER if (!XIP_KERNEL)
+ select HAVE_FUTEX_CMPXCHG if FUTEX
select HAVE_GCC_PLUGINS
select HAVE_GENERIC_DMA_COHERENT
select HAVE_HW_BREAKPOINT if (PERF_EVENTS && (CPU_V6 || CPU_V6K || CPU_V7))
diff --git a/arch/arm/boot/dts/qcom/Makefile b/arch/arm/boot/dts/qcom/Makefile
index 7eb0c7f..3826bad 100644
--- a/arch/arm/boot/dts/qcom/Makefile
+++ b/arch/arm/boot/dts/qcom/Makefile
@@ -1,5 +1,7 @@
-dtb-$(CONFIG_ARCH_SDXPOORWILLS) += sdxpoorwills-rumi.dtb
+dtb-$(CONFIG_ARCH_SDXPOORWILLS) += sdxpoorwills-rumi.dtb \
+ sdxpoorwills-cdp.dtb \
+ sdxpoorwills-mtp.dtb
ifeq ($(CONFIG_ARM64),y)
diff --git a/arch/arm/boot/dts/qcom/pm8950.dtsi b/arch/arm/boot/dts/qcom/pm8950.dtsi
new file mode 100644
index 0000000..f47872a
--- /dev/null
+++ b/arch/arm/boot/dts/qcom/pm8950.dtsi
@@ -0,0 +1,388 @@
+/* Copyright (c) 2015-2017, 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.
+ */
+
+&spmi_bus {
+ qcom,pm8950@0 {
+ compatible ="qcom,spmi-pmic";
+ reg = <0x0 SPMI_USID>;
+ #address-cells = <2>;
+ #size-cells = <0>;
+
+ pm8950_revid: qcom,revid@100 {
+ compatible = "qcom,qpnp-revid";
+ reg = <0x100 0x100>;
+ };
+
+ pm8950_temp_alarm: qcom,temp-alarm@2400 {
+ compatible = "qcom,qpnp-temp-alarm";
+ reg = <0x2400 0x100>;
+ interrupts = <0x0 0x24 0x0>;
+ label = "pm8950_tz";
+ qcom,channel-num = <8>;
+ qcom,threshold-set = <0>;
+ qcom,temp_alarm-vadc = <&pm8950_vadc>;
+ };
+
+ qcom,power-on@800 {
+ compatible = "qcom,qpnp-power-on";
+ reg = <0x800 0x100>;
+ interrupts = <0x0 0x8 0x0>,
+ <0x0 0x8 0x1>,
+ <0x0 0x8 0x4>,
+ <0x0 0x8 0x5>;
+ interrupt-names = "kpdpwr", "resin",
+ "resin-bark", "kpdpwr-resin-bark";
+ qcom,pon-dbc-delay = <15625>;
+ qcom,system-reset;
+
+ qcom,pon_1 {
+ qcom,pon-type = <0>;
+ qcom,pull-up = <1>;
+ linux,code = <116>;
+ };
+
+ qcom,pon_2 {
+ qcom,pon-type = <1>;
+ qcom,pull-up = <1>;
+ linux,code = <114>;
+ };
+ };
+
+ pm8950_coincell: qcom,coincell@2800 {
+ compatible = "qcom,qpnp-coincell";
+ reg = <0x2800 0x100>;
+ };
+
+ pm8950_mpps: mpps {
+ compatible = "qcom,qpnp-pin";
+ spmi-dev-container;
+ gpio-controller;
+ #gpio-cells = <2>;
+ #address-cells = <1>;
+ #size-cells = <1>;
+ label = "pm8950-mpp";
+
+ mpp@a000 {
+ reg = <0xa000 0x100>;
+ qcom,pin-num = <1>;
+ status = "disabled";
+ };
+
+ mpp@a100 {
+ /* MPP2 - PA_THERM config */
+ reg = <0xa100 0x100>;
+ qcom,pin-num = <2>;
+ qcom,mode = <4>; /* AIN input */
+ qcom,invert = <1>; /* Enable MPP */
+ qcom,ain-route = <1>; /* AMUX 6 */
+ qcom,master-en = <1>;
+ qcom,src-sel = <0>; /* Function constant */
+ };
+
+ mpp@a200 {
+ reg = <0xa200 0x100>;
+ qcom,pin-num = <3>;
+ status = "disabled";
+ };
+
+ mpp@a300 {
+ /* MPP4 - CASE_THERM config */
+ reg = <0xa300 0x100>;
+ qcom,pin-num = <4>;
+ qcom,mode = <4>; /* AIN input */
+ qcom,invert = <1>; /* Enable MPP */
+ qcom,ain-route = <3>; /* AMUX 8 */
+ qcom,master-en = <1>;
+ qcom,src-sel = <0>; /* Function constant */
+ };
+ };
+
+ pm8950_gpios: gpios {
+ spmi-dev-container;
+ compatible = "qcom,qpnp-pin";
+ gpio-controller;
+ #gpio-cells = <2>;
+ #address-cells = <1>;
+ #size-cells = <1>;
+ label = "pm8950-gpio";
+
+ gpio@c000 {
+ reg = <0xc000 0x100>;
+ qcom,pin-num = <1>;
+ status = "disabled";
+ };
+
+ gpio@c100 {
+ reg = <0xc100 0x100>;
+ qcom,pin-num = <2>;
+ status = "disabled";
+ };
+
+ gpio@c200 {
+ reg = <0xc200 0x100>;
+ qcom,pin-num = <3>;
+ status = "disabled";
+ };
+
+ gpio@c300 {
+ reg = <0xc300 0x100>;
+ qcom,pin-num = <4>;
+ status = "disabled";
+ };
+
+ gpio@c400 {
+ reg = <0xc400 0x100>;
+ qcom,pin-num = <5>;
+ status = "disabled";
+ };
+
+ gpio@c500 {
+ reg = <0xc500 0x100>;
+ qcom,pin-num = <6>;
+ status = "disabled";
+ };
+
+ gpio@c600 {
+ reg = <0xc600 0x100>;
+ qcom,pin-num = <7>;
+ status = "disabled";
+ };
+
+ gpio@c700 {
+ reg = <0xc700 0x100>;
+ qcom,pin-num = <8>;
+ status = "disabled";
+ };
+ };
+
+ pm8950_vadc: vadc@3100 {
+ compatible = "qcom,qpnp-vadc";
+ reg = <0x3100 0x100>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+ interrupts = <0x0 0x31 0x0>;
+ interrupt-names = "eoc-int-en-set";
+ qcom,adc-bit-resolution = <15>;
+ qcom,adc-vdd-reference = <1800>;
+ qcom,vadc-poll-eoc;
+ qcom,pmic-revid = <&pm8950_revid>;
+
+ chan@5 {
+ label = "vcoin";
+ reg = <5>;
+ qcom,decimation = <0>;
+ qcom,pre-div-channel-scaling = <1>;
+ qcom,calibration-type = "absolute";
+ qcom,scale-function = <0>;
+ qcom,hw-settle-time = <0>;
+ qcom,fast-avg-setup = <0>;
+ };
+
+ chan@7 {
+ label = "vph_pwr";
+ reg = <7>;
+ qcom,decimation = <0>;
+ qcom,pre-div-channel-scaling = <1>;
+ qcom,calibration-type = "absolute";
+ qcom,scale-function = <0>;
+ qcom,hw-settle-time = <0>;
+ qcom,fast-avg-setup = <0>;
+ };
+
+ chan@8 {
+ label = "die_temp";
+ reg = <8>;
+ qcom,decimation = <0>;
+ qcom,pre-div-channel-scaling = <0>;
+ qcom,calibration-type = "absolute";
+ qcom,scale-function = <3>;
+ qcom,hw-settle-time = <0>;
+ qcom,fast-avg-setup = <0>;
+ };
+
+ chan@9 {
+ label = "ref_625mv";
+ reg = <9>;
+ qcom,decimation = <0>;
+ qcom,pre-div-channel-scaling = <0>;
+ qcom,calibration-type = "absolute";
+ qcom,scale-function = <0>;
+ qcom,hw-settle-time = <0>;
+ qcom,fast-avg-setup = <0>;
+ };
+
+ chan@a {
+ label = "ref_1250v";
+ reg = <0xa>;
+ qcom,decimation = <0>;
+ qcom,pre-div-channel-scaling = <0>;
+ qcom,calibration-type = "absolute";
+ qcom,scale-function = <0>;
+ qcom,hw-settle-time = <0>;
+ qcom,fast-avg-setup = <0>;
+ };
+
+ chan@c {
+ label = "ref_buf_625mv";
+ reg = <0xc>;
+ qcom,decimation = <0>;
+ qcom,pre-div-channel-scaling = <0>;
+ qcom,calibration-type = "absolute";
+ qcom,scale-function = <0>;
+ qcom,hw-settle-time = <0>;
+ qcom,fast-avg-setup = <0>;
+ };
+
+ chan@36 {
+ label = "pa_therm0";
+ reg = <0x36>;
+ qcom,decimation = <0>;
+ qcom,pre-div-channel-scaling = <0>;
+ qcom,calibration-type = "ratiometric";
+ qcom,scale-function = <2>;
+ qcom,hw-settle-time = <2>;
+ qcom,fast-avg-setup = <0>;
+ };
+
+ chan@11 {
+ label = "pa_therm1";
+ reg = <0x11>;
+ qcom,decimation = <0>;
+ qcom,pre-div-channel-scaling = <0>;
+ qcom,calibration-type = "ratiometric";
+ qcom,scale-function = <2>;
+ qcom,hw-settle-time = <2>;
+ qcom,fast-avg-setup = <0>;
+ qcom,vadc-thermal-node;
+ };
+
+ chan@32 {
+ label = "xo_therm";
+ reg = <0x32>;
+ qcom,decimation = <0>;
+ qcom,pre-div-channel-scaling = <0>;
+ qcom,calibration-type = "ratiometric";
+ qcom,scale-function = <4>;
+ qcom,hw-settle-time = <2>;
+ qcom,fast-avg-setup = <0>;
+ qcom,vadc-thermal-node;
+ };
+
+ chan@3c {
+ label = "xo_therm_buf";
+ reg = <0x3c>;
+ qcom,decimation = <0>;
+ qcom,pre-div-channel-scaling = <0>;
+ qcom,calibration-type = "ratiometric";
+ qcom,scale-function = <4>;
+ qcom,hw-settle-time = <2>;
+ qcom,fast-avg-setup = <0>;
+ qcom,vadc-thermal-node;
+ };
+
+ chan@13 {
+ label = "case_therm";
+ reg = <0x13>;
+ qcom,decimation = <0>;
+ qcom,pre-div-channel-scaling = <0>;
+ qcom,calibration-type = "ratiometric";
+ qcom,scale-function = <2>;
+ qcom,hw-settle-time = <2>;
+ qcom,fast-avg-setup = <0>;
+ qcom,vadc-thermal-node;
+ };
+ };
+
+ pm8950_adc_tm: vadc@3400 {
+ compatible = "qcom,qpnp-adc-tm";
+ reg = <0x3400 0x100>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+ interrupts = <0x0 0x34 0x0>,
+ <0x0 0x34 0x3>,
+ <0x0 0x34 0x4>;
+ interrupt-names = "eoc-int-en-set",
+ "high-thr-en-set",
+ "low-thr-en-set";
+ qcom,adc-bit-resolution = <15>;
+ qcom,adc-vdd-reference = <1800>;
+ qcom,adc_tm-vadc = <&pm8950_vadc>;
+ qcom,pmic-revid = <&pm8950_revid>;
+
+ chan@36 {
+ label = "pa_therm0";
+ reg = <0x36>;
+ qcom,decimation = <0>;
+ qcom,pre-div-channel-scaling = <0>;
+ qcom,calibration-type = "ratiometric";
+ qcom,scale-function = <2>;
+ qcom,hw-settle-time = <2>;
+ qcom,fast-avg-setup = <0>;
+ qcom,btm-channel-number = <0x48>;
+ qcom,thermal-node;
+ };
+
+ chan@7 {
+ label = "vph_pwr";
+ reg = <0x7>;
+ qcom,decimation = <0>;
+ qcom,pre-div-channel-scaling = <1>;
+ qcom,calibration-type = "absolute";
+ qcom,scale-function = <0>;
+ qcom,hw-settle-time = <0>;
+ qcom,fast-avg-setup = <0>;
+ qcom,btm-channel-number = <0x68>;
+ };
+ };
+
+ pm8950_rtc: qcom,pm8950_rtc {
+ spmi-dev-container;
+ compatible = "qcom,qpnp-rtc";
+ #address-cells = <1>;
+ #size-cells = <1>;
+ qcom,qpnp-rtc-write = <0>;
+ qcom,qpnp-rtc-alarm-pwrup = <0>;
+
+ qcom,pm8950_rtc_rw@6000 {
+ reg = <0x6000 0x100>;
+ };
+
+ qcom,pm8950_rtc_alarm@6100 {
+ reg = <0x6100 0x100>;
+ interrupts = <0x0 0x61 0x1>;
+ };
+ };
+
+ qcom,leds@a300 {
+ compatible = "qcom,leds-qpnp";
+ reg = <0xa300 0x100>;
+ label = "mpp";
+ };
+ };
+
+ pm8950_1: qcom,pm8950@1 {
+ compatible ="qcom,spmi-pmic";
+ reg = <0x1 SPMI_USID>;
+ #address-cells = <2>;
+ #size-cells = <0>;
+
+ pm8950_pwm: pwm@bc00 {
+ status = "disabled";
+ compatible = "qcom,qpnp-pwm";
+ reg = <0xbc00 0x100>;
+ reg-names = "qpnp-lpg-channel-base";
+ qcom,channel-id = <0>;
+ qcom,supported-sizes = <6>, <9>;
+ #pwm-cells = <2>;
+ };
+ };
+};
diff --git a/arch/arm/boot/dts/qcom/pmi8950.dtsi b/arch/arm/boot/dts/qcom/pmi8950.dtsi
new file mode 100644
index 0000000..0ec1f0b
--- /dev/null
+++ b/arch/arm/boot/dts/qcom/pmi8950.dtsi
@@ -0,0 +1,641 @@
+/* Copyright (c) 2015-2017, 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 <dt-bindings/msm/power-on.h>
+
+&spmi_bus {
+ qcom,pmi8950@2 {
+ compatible ="qcom,spmi-pmic";
+ reg = <0x2 SPMI_USID>;
+ #address-cells = <2>;
+ #size-cells = <0>;
+
+ pmi8950_revid: qcom,revid@100 {
+ compatible = "qcom,qpnp-revid";
+ reg = <0x100 0x100>;
+ };
+
+ qcom,power-on@800 {
+ compatible = "qcom,qpnp-power-on";
+ reg = <0x800 0x100>;
+ qcom,secondary-pon-reset;
+ qcom,hard-reset-poweroff-type =
+ <PON_POWER_OFF_SHUTDOWN>;
+
+ pon_perph_reg: qcom,pon_perph_reg {
+ regulator-name = "pon_spare_reg";
+ qcom,pon-spare-reg-addr = <0x8c>;
+ qcom,pon-spare-reg-bit = <1>;
+ };
+ };
+
+ pmi8950_vadc: vadc@3100 {
+ compatible = "qcom,qpnp-vadc";
+ reg = <0x3100 0x100>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+ interrupts = <0x2 0x31 0x0>;
+ interrupt-names = "eoc-int-en-set";
+ qcom,adc-bit-resolution = <15>;
+ qcom,adc-vdd-reference = <1800>;
+ qcom,vadc-poll-eoc;
+
+ chan@0 {
+ label = "usbin";
+ reg = <0>;
+ qcom,decimation = <0>;
+ qcom,pre-div-channel-scaling = <4>;
+ qcom,calibration-type = "absolute";
+ qcom,scale-function = <0>;
+ qcom,hw-settle-time = <0>;
+ qcom,fast-avg-setup = <0>;
+ };
+
+ chan@1 {
+ label = "dcin";
+ reg = <1>;
+ qcom,decimation = <0>;
+ qcom,pre-div-channel-scaling = <4>;
+ qcom,calibration-type = "absolute";
+ qcom,scale-function = <0>;
+ qcom,hw-settle-time = <0>;
+ qcom,fast-avg-setup = <0>;
+ };
+
+ chan@3 {
+ label = "vchg_sns";
+ reg = <3>;
+ qcom,decimation = <0>;
+ qcom,pre-div-channel-scaling = <1>;
+ qcom,calibration-type = "absolute";
+ qcom,scale-function = <0>;
+ qcom,hw-settle-time = <0>;
+ qcom,fast-avg-setup = <0>;
+ };
+
+ chan@9 {
+ label = "ref_625mv";
+ reg = <9>;
+ qcom,decimation = <0>;
+ qcom,pre-div-channel-scaling = <0>;
+ qcom,calibration-type = "absolute";
+ qcom,scale-function = <0>;
+ qcom,hw-settle-time = <0>;
+ qcom,fast-avg-setup = <0>;
+ };
+
+ chan@a {
+ label = "ref_1250v";
+ reg = <0xa>;
+ qcom,decimation = <0>;
+ qcom,pre-div-channel-scaling = <0>;
+ qcom,calibration-type = "absolute";
+ qcom,scale-function = <0>;
+ qcom,hw-settle-time = <0>;
+ qcom,fast-avg-setup = <0>;
+ };
+
+ chan@d {
+ label = "chg_temp";
+ reg = <0xd>;
+ qcom,decimation = <0>;
+ qcom,pre-div-channel-scaling = <0>;
+ qcom,calibration-type = "absolute";
+ qcom,scale-function = <16>;
+ qcom,hw-settle-time = <0>;
+ qcom,fast-avg-setup = <0>;
+ qcom,vadc-thermal-node;
+ };
+
+ chan@43 {
+ label = "usb_dp";
+ reg = <0x43>;
+ qcom,decimation = <0>;
+ qcom,pre-div-channel-scaling = <1>;
+ qcom,calibration-type = "absolute";
+ qcom,scale-function = <0>;
+ qcom,hw-settle-time = <0>;
+ qcom,fast-avg-setup = <0>;
+ };
+
+ chan@44 {
+ label = "usb_dm";
+ reg = <0x44>;
+ qcom,decimation = <0>;
+ qcom,pre-div-channel-scaling = <1>;
+ qcom,calibration-type = "absolute";
+ qcom,scale-function = <0>;
+ qcom,hw-settle-time = <0>;
+ qcom,fast-avg-setup = <0>;
+ };
+ };
+
+ pmi8950_gpios: gpios {
+ spmi-dev-container;
+ compatible = "qcom,qpnp-pin";
+ gpio-controller;
+ #gpio-cells = <2>;
+ #address-cells = <1>;
+ #size-cells = <1>;
+ label = "pmi8950-gpio";
+
+ gpio@c000 {
+ reg = <0xc000 0x100>;
+ qcom,pin-num = <1>;
+ status = "disabled";
+ };
+
+ gpio@c100 {
+ reg = <0xc100 0x100>;
+ qcom,pin-num = <2>;
+ status = "disabled";
+ };
+ };
+
+ pmi8950_mpps: mpps {
+ spmi-dev-container;
+ compatible = "qcom,qpnp-pin";
+ gpio-controller;
+ #gpio-cells = <2>;
+ #address-cells = <1>;
+ #size-cells = <1>;
+ label = "pmi8950-mpp";
+
+ mpp@a000 {
+ reg = <0xa000 0x100>;
+ qcom,pin-num = <1>;
+ status = "disabled";
+ };
+
+ mpp@a100 {
+ reg = <0xa100 0x100>;
+ qcom,pin-num = <2>;
+ status = "disabled";
+ };
+
+ mpp@a200 {
+ reg = <0xa200 0x100>;
+ qcom,pin-num = <3>;
+ status = "disabled";
+ };
+
+ mpp@a300 {
+ reg = <0xa300 0x100>;
+ qcom,pin-num = <4>;
+ status = "disabled";
+ };
+ };
+
+ pmi8950_charger: qcom,qpnp-smbcharger {
+ spmi-dev-container;
+ compatible = "qcom,qpnp-smbcharger";
+ #address-cells = <1>;
+ #size-cells = <1>;
+
+ qcom,iterm-ma = <100>;
+ qcom,float-voltage-mv = <4200>;
+ qcom,resume-delta-mv = <200>;
+ qcom,chg-inhibit-fg;
+ qcom,rparasitic-uohm = <100000>;
+ qcom,bms-psy-name = "bms";
+ qcom,thermal-mitigation = <1500 700 600 0>;
+ qcom,parallel-usb-min-current-ma = <1400>;
+ qcom,parallel-usb-9v-min-current-ma = <900>;
+ qcom,parallel-allowed-lowering-ma = <500>;
+ qcom,pmic-revid = <&pmi8950_revid>;
+ qcom,force-aicl-rerun;
+ qcom,aicl-rerun-period-s = <180>;
+ qcom,autoadjust-vfloat;
+
+ qcom,chgr@1000 {
+ reg = <0x1000 0x100>;
+ interrupts = <0x2 0x10 0x0>,
+ <0x2 0x10 0x1>,
+ <0x2 0x10 0x2>,
+ <0x2 0x10 0x3>,
+ <0x2 0x10 0x4>,
+ <0x2 0x10 0x5>,
+ <0x2 0x10 0x6>,
+ <0x2 0x10 0x7>;
+
+ interrupt-names = "chg-error",
+ "chg-inhibit",
+ "chg-prechg-sft",
+ "chg-complete-chg-sft",
+ "chg-p2f-thr",
+ "chg-rechg-thr",
+ "chg-taper-thr",
+ "chg-tcc-thr";
+ };
+
+ qcom,otg@1100 {
+ reg = <0x1100 0x100>;
+ interrupts = <0x2 0x11 0x0>,
+ <0x2 0x11 0x1>,
+ <0x2 0x11 0x3>;
+ interrupt-names = "otg-fail",
+ "otg-oc",
+ "usbid-change";
+ };
+
+ qcom,bat-if@1200 {
+ reg = <0x1200 0x100>;
+ interrupts = <0x2 0x12 0x0>,
+ <0x2 0x12 0x1>,
+ <0x2 0x12 0x2>,
+ <0x2 0x12 0x3>,
+ <0x2 0x12 0x4>,
+ <0x2 0x12 0x5>,
+ <0x2 0x12 0x6>,
+ <0x2 0x12 0x7>;
+
+ interrupt-names = "batt-hot",
+ "batt-warm",
+ "batt-cold",
+ "batt-cool",
+ "batt-ov",
+ "batt-low",
+ "batt-missing",
+ "batt-term-missing";
+ };
+
+ qcom,usb-chgpth@1300 {
+ reg = <0x1300 0x100>;
+ interrupts = <0x2 0x13 0x0>,
+ <0x2 0x13 0x1>,
+ <0x2 0x13 0x2>,
+ <0x2 0x13 0x5>;
+
+ interrupt-names = "usbin-uv",
+ "usbin-ov",
+ "usbin-src-det",
+ "aicl-done";
+ };
+
+ qcom,dc-chgpth@1400 {
+ reg = <0x1400 0x100>;
+ interrupts = <0x2 0x14 0x0>,
+ <0x2 0x14 0x1>;
+ interrupt-names = "dcin-uv",
+ "dcin-ov";
+ };
+
+ qcom,chgr-misc@1600 {
+ reg = <0x1600 0x100>;
+ interrupts = <0x2 0x16 0x0>,
+ <0x2 0x16 0x1>,
+ <0x2 0x16 0x2>,
+ <0x2 0x16 0x3>,
+ <0x2 0x16 0x4>,
+ <0x2 0x16 0x5>;
+
+ interrupt-names = "power-ok",
+ "temp-shutdown",
+ "wdog-timeout",
+ "flash-fail",
+ "otst2",
+ "otst3";
+ };
+
+ smbcharger_charger_otg: qcom,smbcharger-boost-otg {
+ regulator-name = "smbcharger_charger_otg";
+ };
+ };
+
+ pmi8950_fg: qcom,fg {
+ spmi-dev-container;
+ compatible = "qcom,qpnp-fg";
+ #address-cells = <1>;
+ #size-cells = <1>;
+ qcom,resume-soc = <95>;
+ status = "okay";
+ qcom,bcl-lm-threshold-ma = <127>;
+ qcom,bcl-mh-threshold-ma = <405>;
+ qcom,fg-iterm-ma = <150>;
+ qcom,fg-chg-iterm-ma = <100>;
+ qcom,pmic-revid = <&pmi8950_revid>;
+ qcom,fg-cutoff-voltage-mv = <3500>;
+ qcom,cycle-counter-en;
+ qcom,capacity-learning-on;
+
+ qcom,fg-soc@4000 {
+ status = "okay";
+ reg = <0x4000 0x100>;
+ interrupts = <0x2 0x40 0x0>,
+ <0x2 0x40 0x1>,
+ <0x2 0x40 0x2>,
+ <0x2 0x40 0x3>,
+ <0x2 0x40 0x4>,
+ <0x2 0x40 0x5>,
+ <0x2 0x40 0x6>;
+
+ interrupt-names = "high-soc",
+ "low-soc",
+ "full-soc",
+ "empty-soc",
+ "delta-soc",
+ "first-est-done",
+ "update-soc";
+ };
+
+ qcom,fg-batt@4100 {
+ reg = <0x4100 0x100>;
+ interrupts = <0x2 0x41 0x0>,
+ <0x2 0x41 0x1>,
+ <0x2 0x41 0x2>,
+ <0x2 0x41 0x3>,
+ <0x2 0x41 0x4>,
+ <0x2 0x41 0x5>,
+ <0x2 0x41 0x6>,
+ <0x2 0x41 0x7>;
+
+ interrupt-names = "soft-cold",
+ "soft-hot",
+ "vbatt-low",
+ "batt-ided",
+ "batt-id-req",
+ "batt-unknown",
+ "batt-missing",
+ "batt-match";
+ };
+
+ qcom,revid-tp-rev@1f1 {
+ reg = <0x1f1 0x1>;
+ };
+
+ qcom,fg-memif@4400 {
+ status = "okay";
+ reg = <0x4400 0x100>;
+ interrupts = <0x2 0x44 0x0>,
+ <0x2 0x44 0x2>;
+
+ interrupt-names = "mem-avail",
+ "data-rcvry-sug";
+ };
+ };
+
+ bcl@4200 {
+ compatible = "qcom,msm-bcl";
+ reg = <0x4200 0xFF 0x88E 0x2>;
+ reg-names = "fg_user_adc", "pon_spare";
+ interrupts = <0x2 0x42 0x0>,
+ <0x2 0x42 0x1>;
+ interrupt-names = "bcl-high-ibat-int",
+ "bcl-low-vbat-int";
+ qcom,vbat-scaling-factor = <39000>;
+ qcom,vbat-gain-numerator = <1>;
+ qcom,vbat-gain-denominator = <128>;
+ qcom,vbat-polling-delay-ms = <100>;
+ qcom,ibat-scaling-factor = <39000>;
+ qcom,ibat-gain-numerator = <1>;
+ qcom,ibat-gain-denominator = <128>;
+ qcom,ibat-offset-numerator = <1200>;
+ qcom,ibat-offset-denominator = <1>;
+ qcom,ibat-polling-delay-ms = <100>;
+ qcom,inhibit-derating-ua = <550000>;
+ };
+
+ qcom,leds@a100 {
+ compatible = "qcom,leds-qpnp";
+ reg = <0xa100 0x100>;
+ label = "mpp";
+ };
+ };
+
+ qcom,pmi8950@3 {
+ compatible ="qcom,spmi-pmic";
+ reg = <0x3 SPMI_USID>;
+ #address-cells = <1>;
+ #size-cells = <1>;
+
+ pmi8950_pwm: pwm@b000 {
+ status = "disabled";
+ compatible = "qcom,qpnp-pwm";
+ reg = <0xb000 0x100>;
+ reg-names = "qpnp-lpg-channel-base";
+ qcom,channel-id = <0>;
+ qcom,supported-sizes = <6>, <9>;
+ #pwm-cells = <2>;
+ };
+
+ labibb: qpnp-labibb-regulator {
+ status = "disabled";
+ spmi-dev-container;
+ compatible = "qcom,qpnp-labibb-regulator";
+ #address-cells = <1>;
+ #size-cells = <1>;
+ qcom,pmic-revid = <&pmi8950_revid>;
+
+ ibb_regulator: qcom,ibb@dc00 {
+ reg = <0xdc00 0x100>;
+ reg-names = "ibb_reg";
+ regulator-name = "ibb_reg";
+
+ regulator-min-microvolt = <4600000>;
+ regulator-max-microvolt = <6000000>;
+
+ qcom,qpnp-ibb-min-voltage = <1400000>;
+ qcom,qpnp-ibb-step-size = <100000>;
+ qcom,qpnp-ibb-slew-rate = <2000000>;
+ qcom,qpnp-ibb-use-default-voltage;
+ qcom,qpnp-ibb-init-voltage = <5500000>;
+ qcom,qpnp-ibb-init-amoled-voltage = <4000000>;
+ qcom,qpnp-ibb-init-lcd-voltage = <5500000>;
+
+ qcom,qpnp-ibb-soft-start = <1000>;
+
+ qcom,qpnp-ibb-discharge-resistor = <32>;
+ qcom,qpnp-ibb-lab-pwrup-delay = <8000>;
+ qcom,qpnp-ibb-lab-pwrdn-delay = <8000>;
+ qcom,qpnp-ibb-en-discharge;
+
+ qcom,qpnp-ibb-full-pull-down;
+ qcom,qpnp-ibb-pull-down-enable;
+ qcom,qpnp-ibb-switching-clock-frequency =
+ <1480>;
+ qcom,qpnp-ibb-limit-maximum-current = <1550>;
+ qcom,qpnp-ibb-debounce-cycle = <16>;
+ qcom,qpnp-ibb-limit-max-current-enable;
+ qcom,qpnp-ibb-ps-enable;
+ };
+
+ lab_regulator: qcom,lab@de00 {
+ reg = <0xde00 0x100>;
+ reg-names = "lab";
+ regulator-name = "lab_reg";
+
+ regulator-min-microvolt = <4600000>;
+ regulator-max-microvolt = <6000000>;
+
+ qcom,qpnp-lab-min-voltage = <4600000>;
+ qcom,qpnp-lab-step-size = <100000>;
+ qcom,qpnp-lab-slew-rate = <5000>;
+ qcom,qpnp-lab-use-default-voltage;
+ qcom,qpnp-lab-init-voltage = <5500000>;
+ qcom,qpnp-lab-init-amoled-voltage = <4600000>;
+ qcom,qpnp-lab-init-lcd-voltage = <5500000>;
+
+ qcom,qpnp-lab-soft-start = <800>;
+
+ qcom,qpnp-lab-full-pull-down;
+ qcom,qpnp-lab-pull-down-enable;
+ qcom,qpnp-lab-switching-clock-frequency =
+ <1600>;
+ qcom,qpnp-lab-limit-maximum-current = <800>;
+ qcom,qpnp-lab-limit-max-current-enable;
+ qcom,qpnp-lab-ps-threshold = <40>;
+ qcom,qpnp-lab-ps-enable;
+ qcom,qpnp-lab-nfet-size = <100>;
+ qcom,qpnp-lab-pfet-size = <100>;
+ qcom,qpnp-lab-max-precharge-time = <500>;
+ };
+
+ };
+
+ wled: qcom,leds@d800 {
+ compatible = "qcom,qpnp-wled";
+ reg = <0xd800 0x100>,
+ <0xd900 0x100>,
+ <0xdc00 0x100>,
+ <0xde00 0x100>;
+ reg-names = "qpnp-wled-ctrl-base",
+ "qpnp-wled-sink-base",
+ "qpnp-wled-ibb-base",
+ "qpnp-wled-lab-base";
+ interrupts = <0x3 0xd8 0x2>;
+ interrupt-names = "sc-irq";
+ status = "okay";
+ linux,name = "wled";
+ linux,default-trigger = "bkl-trigger";
+ qcom,fdbk-output = "auto";
+ qcom,vref-mv = <350>;
+ qcom,switch-freq-khz = <800>;
+ qcom,ovp-mv = <29500>;
+ qcom,ilim-ma = <980>;
+ qcom,boost-duty-ns = <26>;
+ qcom,mod-freq-khz = <9600>;
+ qcom,dim-mode = "hybrid";
+ qcom,dim-method = "linear";
+ qcom,hyb-thres = <625>;
+ qcom,sync-dly-us = <800>;
+ qcom,fs-curr-ua = <20000>;
+ qcom,led-strings-list = [00 01];
+ qcom,en-ext-pfet-sc-pro;
+ qcom,cons-sync-write-delay-us = <1000>;
+ };
+
+ flash_led: qcom,leds@d300 {
+ compatible = "qcom,qpnp-flash-led";
+ status = "okay";
+ reg = <0xd300 0x100>;
+ label = "flash";
+ qcom,headroom = <500>;
+ qcom,startup-dly = <128>;
+ qcom,clamp-curr = <200>;
+ qcom,pmic-charger-support;
+ qcom,self-check-enabled;
+ qcom,thermal-derate-enabled;
+ qcom,thermal-derate-threshold = <100>;
+ qcom,thermal-derate-rate = "5_PERCENT";
+ qcom,current-ramp-enabled;
+ qcom,ramp_up_step = "6P7_US";
+ qcom,ramp_dn_step = "6P7_US";
+ qcom,vph-pwr-droop-enabled;
+ qcom,vph-pwr-droop-threshold = <3000>;
+ qcom,vph-pwr-droop-debounce-time = <10>;
+ qcom,headroom-sense-ch0-enabled;
+ qcom,headroom-sense-ch1-enabled;
+ qcom,pmic-revid = <&pmi8950_revid>;
+
+ pmi8950_flash0: qcom,flash_0 {
+ label = "flash";
+ qcom,led-name = "led:flash_0";
+ qcom,default-led-trigger =
+ "flash0_trigger";
+ qcom,max-current = <1000>;
+ qcom,duration = <1280>;
+ qcom,id = <0>;
+ qcom,current = <625>;
+ };
+
+ pmi8950_flash1: qcom,flash_1 {
+ label = "flash";
+ qcom,led-name = "led:flash_1";
+ qcom,default-led-trigger =
+ "flash1_trigger";
+ qcom,max-current = <1000>;
+ qcom,duration = <1280>;
+ qcom,id = <1>;
+ qcom,current = <625>;
+ };
+
+ pmi8950_torch0: qcom,torch_0 {
+ label = "torch";
+ qcom,led-name = "led:torch_0";
+ qcom,default-led-trigger =
+ "torch0_trigger";
+ qcom,max-current = <200>;
+ qcom,id = <0>;
+ qcom,current = <120>;
+ };
+
+ pmi8950_torch1: qcom,torch_1 {
+ label = "torch";
+ qcom,led-name = "led:torch_1";
+ qcom,default-led-trigger =
+ "torch1_trigger";
+ qcom,max-current = <200>;
+ qcom,id = <1>;
+ qcom,current = <120>;
+ };
+
+ pmi8950_switch: qcom,switch {
+ label = "switch";
+ qcom,led-name = "led:switch";
+ qcom,default-led-trigger =
+ "switch_trigger";
+ qcom,max-current = <1000>;
+ qcom,duration = <1280>;
+ qcom,id = <2>;
+ qcom,current = <625>;
+ reg0 {
+ regulator-name = "pon_spare_reg";
+ };
+ };
+ };
+
+ pmi_haptic: qcom,haptic@c000 {
+ compatible = "qcom,qpnp-haptic";
+ reg = <0xc000 0x100>;
+ interrupts = <0x3 0xc0 0x0>,
+ <0x3 0xc0 0x1>;
+ interrupt-names = "sc-irq", "play-irq";
+ qcom,pmic-revid = <&pmi8950_revid>;
+ vcc_pon-supply = <&pon_perph_reg>;
+ qcom,play-mode = "direct";
+ qcom,wave-play-rate-us = <5263>;
+ qcom,actuator-type = "erm";
+ qcom,wave-shape = "square";
+ qcom,vmax-mv = <2000>;
+ qcom,ilim-ma = <800>;
+ qcom,sc-deb-cycles = <8>;
+ qcom,int-pwm-freq-khz = <505>;
+ qcom,en-brake;
+ qcom,brake-pattern = [03 03 00 00];
+ qcom,use-play-irq;
+ qcom,use-sc-irq;
+ qcom,wave-samples = [3e 3e 3e 3e 3e 3e 3e 3e];
+ qcom,wave-rep-cnt = <1>;
+ qcom,wave-samp-rep-cnt = <1>;
+ };
+ };
+};
diff --git a/arch/arm/boot/dts/qcom/pmxpoorwills.dtsi b/arch/arm/boot/dts/qcom/pmxpoorwills.dtsi
index a3df1f4..e3f154b 100644
--- a/arch/arm/boot/dts/qcom/pmxpoorwills.dtsi
+++ b/arch/arm/boot/dts/qcom/pmxpoorwills.dtsi
@@ -51,6 +51,40 @@
linux,code = <114>;
};
};
+
+ pmxpoorwills_gpios: pinctrl@c000 {
+ compatible = "qcom,spmi-gpio";
+ reg = <0xc000 0x900>;
+ interrupts = <0x0 0xc1 0 IRQ_TYPE_NONE>,
+ <0x0 0xc2 0 IRQ_TYPE_NONE>,
+ <0x0 0xc3 0 IRQ_TYPE_NONE>,
+ <0x0 0xc4 0 IRQ_TYPE_NONE>,
+ <0x0 0xc5 0 IRQ_TYPE_NONE>;
+ interrupt-names = "pmxpoorwills_gpio2",
+ "pmxpoorwills_gpio3",
+ "pmxpoorwills_gpio4",
+ "pmxpoorwills_gpio5",
+ "pmxpoorwills_gpio6";
+ gpio-controller;
+ #gpio-cells = <2>;
+ qcom,gpios-disallowed = <1 7 8 9>;
+ };
+
+ pmxpoorwills_rtc: qcom,pmxpoorwills_rtc {
+ compatible = "qcom,qpnp-rtc";
+ #address-cells = <1>;
+ #size-cells = <1>;
+ qcom,qpnp-rtc-write = <0>;
+ qcom,qpnp-rtc-alarm-pwrup = <0>;
+
+ qcom,pmxpoorwills_rtc_rw@6000 {
+ reg = <0x6000 0x100>;
+ };
+ qcom,pmxpoorwills_rtc_alarm@6100 {
+ reg = <0x6100 0x100>;
+ interrupts = <0x0 0x61 0x1 IRQ_TYPE_NONE>;
+ };
+ };
};
qcom,pmxpoorwills@1 {
@@ -58,5 +92,45 @@
reg = <0x1 SPMI_USID>;
#address-cells = <2>;
#size-cells = <0>;
+
+ pmxpoorwills_pwm_1: pwm@bc00 {
+ compatible = "qcom,qpnp-pwm";
+ reg = <0xbc00 0x100>;
+ reg-names = "qpnp-lpg-channel-base";
+ qcom,channel-id = <0>;
+ qcom,supported-sizes = <6>, <9>;
+ #pwm-cells = <2>;
+ status = "disabled";
+ };
+
+ pmxpoorwills_pwm_2: pwm@bd00 {
+ compatible = "qcom,qpnp-pwm";
+ reg = <0xbd00 0x100>;
+ reg-names = "qpnp-lpg-channel-base";
+ qcom,channel-id = <1>;
+ qcom,supported-sizes = <6>, <9>;
+ #pwm-cells = <2>;
+ status = "disabled";
+ };
+
+ pmxpoorwills_pwm_3: pwm@be00 {
+ compatible = "qcom,qpnp-pwm";
+ reg = <0xbe00 0x100>;
+ reg-names = "qpnp-lpg-channel-base";
+ qcom,channel-id = <2>;
+ qcom,supported-sizes = <6>, <9>;
+ #pwm-cells = <2>;
+ status = "disabled";
+ };
+
+ pmxpoorwills_pwm_4: pwm@bf00 {
+ compatible = "qcom,qpnp-pwm";
+ reg = <0xbf00 0x100>;
+ reg-names = "qpnp-lpg-channel-base";
+ qcom,channel-id = <3>;
+ qcom,supported-sizes = <6>, <9>;
+ #pwm-cells = <2>;
+ status = "disabled";
+ };
};
};
diff --git a/arch/arm/boot/dts/qcom/sdxpoorwills-blsp.dtsi b/arch/arm/boot/dts/qcom/sdxpoorwills-blsp.dtsi
new file mode 100644
index 0000000..4fe2d1e
--- /dev/null
+++ b/arch/arm/boot/dts/qcom/sdxpoorwills-blsp.dtsi
@@ -0,0 +1,573 @@
+/* Copyright (c) 2017, 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 "sdxpoorwills-pinctrl.dtsi"
+
+/ {
+ aliases {
+ spi1 = &spi_1;
+ spi2 = &spi_2;
+ spi3 = &spi_3;
+ spi4 = &spi_4;
+ i2c1 = &i2c_1;
+ i2c2 = &i2c_2;
+ i2c3 = &i2c_3;
+ i2c4 = &i2c_4;
+ i2c5 = &i2c_5;
+ i2c6 = &i2c_6;
+ i2c7 = &i2c_7;
+ };
+};
+
+
+&soc {
+ dma_blsp1: qcom,sps-dma@804000 { /* BLSP1 */
+ #dma-cells = <4>;
+ compatible = "qcom,sps-dma";
+ reg = <0x804000 0x23000>;
+ interrupts = <0 58 0>;
+ qcom,summing-threshold = <0x10>;
+ };
+
+ i2c_1: i2c@835000 { /* BLSP1 QUP1: GPIO: 2,3 */
+ compatible = "qcom,i2c-msm-v2";
+ #address-cells = <1>;
+ #size-cells = <0>;
+ reg = <0x835000 0x600>;
+ reg-names = "qup_phys_addr";
+ interrupt-names = "qup_irq";
+ interrupts = <0 31 0>;
+ dmas = <&dma_blsp1 8 64 0x20000020 0x20>,
+ <&dma_blsp1 9 32 0x20000020 0x20>;
+ dma-names = "tx", "rx";
+ qcom,master-id = <86>;
+ qcom,clk-freq-out = <400000>;
+ qcom,clk-freq-in = <19200000>;
+ clock-names = "iface_clk", "core_clk";
+ clocks = <&clock_gcc GCC_BLSP1_AHB_CLK>,
+ <&clock_gcc GCC_BLSP1_QUP1_I2C_APPS_CLK>;
+ pinctrl-names = "i2c_active", "i2c_sleep";
+ pinctrl-0 = <&i2c_1_active>;
+ pinctrl-1 = <&i2c_1_sleep>;
+ status = "disabled";
+ };
+
+ i2c_2: i2c@836000 { /* BLSP1 QUP2: GPIO: 6,7 */
+ compatible = "qcom,i2c-msm-v2";
+ #address-cells = <1>;
+ #size-cells = <0>;
+ reg = <0x836000 0x600>;
+ reg-names = "qup_phys_addr";
+ interrupt-names = "qup_irq";
+ interrupts = <0 32 0>;
+ dmas = <&dma_blsp1 10 64 0x20000020 0x20>,
+ <&dma_blsp1 11 32 0x20000020 0x20>;
+ dma-names = "tx", "rx";
+ qcom,master-id = <86>;
+ qcom,clk-freq-out = <400000>;
+ qcom,clk-freq-in = <19200000>;
+ clock-names = "iface_clk", "core_clk";
+ clocks = <&clock_gcc GCC_BLSP1_AHB_CLK>,
+ <&clock_gcc GCC_BLSP1_QUP2_I2C_APPS_CLK>;
+ pinctrl-names = "i2c_active", "i2c_sleep";
+ pinctrl-0 = <&i2c_2_active>;
+ pinctrl-1 = <&i2c_2_sleep>;
+ status = "disabled";
+ };
+
+ i2c_3: i2c@837000 { /* BLSP1 QUP3: GPIO: 10,11 */
+ compatible = "qcom,i2c-msm-v2";
+ #address-cells = <1>;
+ #size-cells = <0>;
+ reg = <0x837000 0x600>;
+ reg-names = "qup_phys_addr";
+ interrupt-names = "qup_irq";
+ interrupts = <0 33 0>;
+ dmas = <&dma_blsp1 12 64 0x20000020 0x20>,
+ <&dma_blsp1 13 32 0x20000020 0x20>;
+ dma-names = "tx", "rx";
+ qcom,master-id = <86>;
+ qcom,clk-freq-out = <400000>;
+ qcom,clk-freq-in = <19200000>;
+ clock-names = "iface_clk", "core_clk";
+ clocks = <&clock_gcc GCC_BLSP1_AHB_CLK>,
+ <&clock_gcc GCC_BLSP1_QUP3_I2C_APPS_CLK>;
+ pinctrl-names = "i2c_active", "i2c_sleep";
+ pinctrl-0 = <&i2c_3_active>;
+ pinctrl-1 = <&i2c_3_sleep>;
+ status = "disabled";
+ };
+
+ i2c_4: i2c@838000 { /* BLSP1 QUP4: GPIO: 76,77 */
+ compatible = "qcom,i2c-msm-v2";
+ #address-cells = <1>;
+ #size-cells = <0>;
+ reg = <0x838000 0x600>;
+ reg-names = "qup_phys_addr";
+ interrupt-names = "qup_irq";
+ interrupts = <0 34 0>;
+ dmas = <&dma_blsp1 14 64 0x20000020 0x20>,
+ <&dma_blsp1 15 32 0x20000020 0x20>;
+ dma-names = "tx", "rx";
+ qcom,master-id = <86>;
+ qcom,clk-freq-out = <400000>;
+ qcom,clk-freq-in = <19200000>;
+ clock-names = "iface_clk", "core_clk";
+ clocks = <&clock_gcc GCC_BLSP1_AHB_CLK>,
+ <&clock_gcc GCC_BLSP1_QUP4_I2C_APPS_CLK>;
+ pinctrl-names = "i2c_active", "i2c_sleep";
+ pinctrl-0 = <&i2c_4_active>;
+ pinctrl-1 = <&i2c_4_sleep>;
+ status = "disabled";
+ };
+
+ i2c_5: i2c@835000 { /* BLSP1 QUP1: GPIO: 74,75 */
+ compatible = "qcom,i2c-msm-v2";
+ #address-cells = <1>;
+ #size-cells = <0>;
+ reg = <0x835000 0x600>;
+ reg-names = "qup_phys_addr";
+ interrupt-names = "qup_irq";
+ interrupts = <0 31 0>;
+ dmas = <&dma_blsp1 8 64 0x20000020 0x20>,
+ <&dma_blsp1 9 32 0x20000020 0x20>;
+ dma-names = "tx", "rx";
+ qcom,master-id = <86>;
+ qcom,clk-freq-out = <400000>;
+ qcom,clk-freq-in = <19200000>;
+ clock-names = "iface_clk", "core_clk";
+ clocks = <&clock_gcc GCC_BLSP1_AHB_CLK>,
+ <&clock_gcc GCC_BLSP1_QUP1_I2C_APPS_CLK>;
+ pinctrl-names = "i2c_active", "i2c_sleep";
+ pinctrl-0 = <&i2c_5_active>;
+ pinctrl-1 = <&i2c_5_sleep>;
+ status = "disabled";
+ };
+
+ i2c_6: i2c@836000 { /* BLSP1 QUP2: GPIO: 65,66 */
+ compatible = "qcom,i2c-msm-v2";
+ #address-cells = <1>;
+ #size-cells = <0>;
+ reg = <0x836000 0x600>;
+ reg-names = "qup_phys_addr";
+ interrupt-names = "qup_irq";
+ interrupts = <0 32 0>;
+ dmas = <&dma_blsp1 10 64 0x20000020 0x20>,
+ <&dma_blsp1 11 32 0x20000020 0x20>;
+ dma-names = "tx", "rx";
+ qcom,master-id = <86>;
+ qcom,clk-freq-out = <400000>;
+ qcom,clk-freq-in = <19200000>;
+ clock-names = "iface_clk", "core_clk";
+ clocks = <&clock_gcc GCC_BLSP1_AHB_CLK>,
+ <&clock_gcc GCC_BLSP1_QUP2_I2C_APPS_CLK>;
+ pinctrl-names = "i2c_active", "i2c_sleep";
+ pinctrl-0 = <&i2c_6_active>;
+ pinctrl-1 = <&i2c_6_sleep>;
+ status = "disabled";
+ };
+
+ i2c_7: i2c@838000 { /* BLSP1 QUP4: GPIO: 18,19 */
+ compatible = "qcom,i2c-msm-v2";
+ #address-cells = <1>;
+ #size-cells = <0>;
+ reg = <0x838000 0x600>;
+ reg-names = "qup_phys_addr";
+ interrupt-names = "qup_irq";
+ interrupts = <0 34 0>;
+ dmas = <&dma_blsp1 14 64 0x20000020 0x20>,
+ <&dma_blsp1 15 32 0x20000020 0x20>;
+ dma-names = "tx", "rx";
+ qcom,master-id = <86>;
+ qcom,clk-freq-out = <400000>;
+ qcom,clk-freq-in = <19200000>;
+ clock-names = "iface_clk", "core_clk";
+ clocks = <&clock_gcc GCC_BLSP1_AHB_CLK>,
+ <&clock_gcc GCC_BLSP1_QUP4_I2C_APPS_CLK>;
+ pinctrl-names = "i2c_active", "i2c_sleep";
+ pinctrl-0 = <&i2c_7_active>;
+ pinctrl-1 = <&i2c_7_sleep>;
+ status = "disabled";
+ };
+
+ spi_1: spi@835000 { /* BLSP1 QUP1: GPIO: 72,73,74,75 */
+ compatible = "qcom,spi-qup-v2";
+ #address-cells = <1>;
+ #size-cells = <0>;
+ reg-names = "spi_physical", "spi_bam_physical";
+ reg = <0x835000 0x600>,
+ <0x804000 0x23000>;
+ interrupt-names = "spi_irq", "spi_bam_irq";
+ interrupts = <0 31 0>, <0 58 0>;
+ spi-max-frequency = <50000000>;
+ qcom,use-bam;
+ qcom,ver-reg-exists;
+ qcom,bam-consumer-pipe-index = <8>;
+ qcom,bam-producer-pipe-index = <9>;
+ qcom,master-id = <86>;
+ qcom,use-pinctrl;
+ pinctrl-names = "spi_default", "spi_sleep";
+ pinctrl-0 = <&spi_1_active>;
+ pinctrl-1 = <&spi_1_sleep>;
+ clock-names = "iface_clk", "core_clk";
+ clocks = <&clock_gcc GCC_BLSP1_AHB_CLK>,
+ <&clock_gcc GCC_BLSP1_QUP1_SPI_APPS_CLK>;
+ status = "disabled";
+ };
+
+ spi_2: spi@836000 { /* BLSP1 QUP2: GPIO: 4,5,6,7 */
+ compatible = "qcom,spi-qup-v2";
+ #address-cells = <1>;
+ #size-cells = <0>;
+ reg-names = "spi_physical", "spi_bam_physical";
+ reg = <0x836000 0x600>,
+ <0x804000 0x23000>;
+ interrupt-names = "spi_irq", "spi_bam_irq";
+ interrupts = <0 32 0>, <0 58 0>;
+ spi-max-frequency = <50000000>;
+ qcom,use-bam;
+ qcom,ver-reg-exists;
+ qcom,bam-consumer-pipe-index = <10>;
+ qcom,bam-producer-pipe-index = <11>;
+ qcom,master-id = <86>;
+ qcom,use-pinctrl;
+ pinctrl-names = "spi_default", "spi_sleep";
+ pinctrl-0 = <&spi_2_active>;
+ pinctrl-1 = <&spi_2_sleep>;
+ clock-names = "iface_clk", "core_clk";
+ clocks = <&clock_gcc GCC_BLSP1_AHB_CLK>,
+ <&clock_gcc GCC_BLSP1_QUP2_SPI_APPS_CLK>;
+ status = "disabled";
+ };
+
+ spi_3: spi@837000 { /* BLSP1 QUP3: GPIO: 8,9,10,11 */
+ compatible = "qcom,spi-qup-v2";
+ #address-cells = <1>;
+ #size-cells = <0>;
+ reg-names = "spi_physical", "spi_bam_physical";
+ reg = <0x837000 0x600>,
+ <0x804000 0x23000>;
+ interrupt-names = "spi_irq", "spi_bam_irq";
+ interrupts = <0 33 0>, <0 58 0>;
+ spi-max-frequency = <50000000>;
+ qcom,use-bam;
+ qcom,ver-reg-exists;
+ qcom,bam-consumer-pipe-index = <12>;
+ qcom,bam-producer-pipe-index = <13>;
+ qcom,master-id = <86>;
+ qcom,use-pinctrl;
+ pinctrl-names = "spi_default", "spi_sleep";
+ pinctrl-0 = <&spi_3_active>;
+ pinctrl-1 = <&spi_3_sleep>;
+ clock-names = "iface_clk", "core_clk";
+ clocks = <&clock_gcc GCC_BLSP1_AHB_CLK>,
+ <&clock_gcc GCC_BLSP1_QUP3_SPI_APPS_CLK>;
+ status = "disabled";
+ };
+
+ spi_4: spi@838000 { /* BLSP1 QUP4: GPIO: 16,17,18,19 */
+ compatible = "qcom,spi-qup-v2";
+ #address-cells = <1>;
+ #size-cells = <0>;
+ reg-names = "spi_physical", "spi_bam_physical";
+ reg = <0x838000 0x600>,
+ <0x804000 0x23000>;
+ interrupt-names = "spi_irq", "spi_bam_irq";
+ interrupts = <0 34 0>, <0 58 0>;
+ spi-max-frequency = <50000000>;
+ qcom,use-bam;
+ qcom,ver-reg-exists;
+ qcom,bam-consumer-pipe-index = <14>;
+ qcom,bam-producer-pipe-index = <15>;
+ qcom,master-id = <86>;
+ qcom,use-pinctrl;
+ pinctrl-names = "spi_default", "spi_sleep";
+ pinctrl-0 = <&spi_4_active>;
+ pinctrl-1 = <&spi_4_sleep>;
+ clock-names = "iface_clk", "core_clk";
+ clocks = <&clock_gcc GCC_BLSP1_AHB_CLK>,
+ <&clock_gcc GCC_BLSP1_QUP4_SPI_APPS_CLK>;
+ status = "disabled";
+ };
+
+ blsp1_uart1a_hs: uarta@82f000 { /* BLSP1 UART1: GPIO: 0,1,2,3 */
+ compatible = "qcom,msm-hsuart-v14";
+ reg = <0x82f000 0x200>,
+ <0x804000 0x23000>;
+ reg-names = "core_mem", "bam_mem";
+ interrupt-names = "core_irq", "bam_irq", "wakeup_irq";
+ #address-cells = <0>;
+ interrupt-parent = <&blsp1_uart1a_hs>;
+ interrupts = <0 1 2>;
+ #interrupt-cells = <1>;
+ interrupt-map-mask = <0xffffffff>;
+ interrupt-map = <0 &intc 0 24 0
+ 1 &intc 0 58 0
+ 2 &tlmm 1 0>;
+
+ qcom,inject-rx-on-wakeup;
+ qcom,rx-char-to-inject = <0xfd>;
+
+ qcom,bam-tx-ep-pipe-index = <0>;
+ qcom,bam-rx-ep-pipe-index = <1>;
+ qcom,master-id = <86>;
+ clock-names = "core_clk", "iface_clk";
+ clocks = <&clock_gcc GCC_BLSP1_UART1_APPS_CLK>,
+ <&clock_gcc GCC_BLSP1_AHB_CLK>;
+ pinctrl-names = "sleep", "default";
+ pinctrl-0 = <&blsp1_uart1a_tx_sleep>,
+ <&blsp1_uart1a_rxcts_sleep>, <&blsp1_uart1a_rfr_sleep>;
+ pinctrl-1 = <&blsp1_uart1a_tx_active>,
+ <&blsp1_uart1a_rxcts_active>, <&blsp1_uart1a_rfr_active>;
+
+ qcom,msm-bus,name = "buart1a";
+ qcom,msm-bus,num-cases = <2>;
+ qcom,msm-bus,num-paths = <1>;
+ qcom,msm-bus,vectors-KBps =
+ <86 512 0 0>,
+ <86 512 500 800>;
+ status = "disabled";
+ };
+
+ blsp1_uart1b_hs: uartb@82f000 { /* BLSP1 UART1: GPIO: 20,21,22,23 */
+ compatible = "qcom,msm-hsuart-v14";
+ reg = <0x82f000 0x200>,
+ <0x804000 0x23000>;
+ reg-names = "core_mem", "bam_mem";
+ interrupt-names = "core_irq", "bam_irq", "wakeup_irq";
+ #address-cells = <0>;
+ interrupt-parent = <&blsp1_uart1b_hs>;
+ interrupts = <0 1 2>;
+ #interrupt-cells = <1>;
+ interrupt-map-mask = <0xffffffff>;
+ interrupt-map = <0 &intc 0 24 0
+ 1 &intc 0 58 0
+ 2 &tlmm 21 0>;
+
+ qcom,inject-rx-on-wakeup;
+ qcom,rx-char-to-inject = <0xfd>;
+
+ qcom,bam-tx-ep-pipe-index = <0>;
+ qcom,bam-rx-ep-pipe-index = <1>;
+ qcom,master-id = <86>;
+ clock-names = "core_clk", "iface_clk";
+ clocks = <&clock_gcc GCC_BLSP1_UART1_APPS_CLK>,
+ <&clock_gcc GCC_BLSP1_AHB_CLK>;
+ pinctrl-names = "sleep", "default";
+ pinctrl-0 = <&blsp1_uart1b_tx_sleep>,
+ <&blsp1_uart1b_rxcts_sleep>, <&blsp1_uart1b_rfr_sleep>;
+ pinctrl-1 = <&blsp1_uart1b_tx_active>,
+ <&blsp1_uart1b_rxcts_active>, <&blsp1_uart1b_rfr_active>;
+
+ qcom,msm-bus,name = "buart1b";
+ qcom,msm-bus,num-cases = <2>;
+ qcom,msm-bus,num-paths = <1>;
+ qcom,msm-bus,vectors-KBps =
+ <86 512 0 0>,
+ <86 512 500 800>;
+ status = "disabled";
+ };
+
+ blsp1_uart2a_hs: uarta@830000 { /* BLSP1 UART2 : GPIO: 4,5,6,7 */
+ compatible = "qcom,msm-hsuart-v14";
+ reg = <0x830000 0x200>,
+ <0x804000 0x23000>;
+ reg-names = "core_mem", "bam_mem";
+ interrupt-names = "core_irq", "bam_irq", "wakeup_irq";
+ #address-cells = <0>;
+ interrupt-parent = <&blsp1_uart2a_hs>;
+ interrupts = <0 1 2>;
+ #interrupt-cells = <1>;
+ interrupt-map-mask = <0xffffffff>;
+ interrupt-map = <0 &intc 0 25 0
+ 1 &intc 0 58 0
+ 2 &tlmm 5 0>;
+
+ qcom,inject-rx-on-wakeup;
+ qcom,rx-char-to-inject = <0xfd>;
+
+ qcom,bam-tx-ep-pipe-index = <2>;
+ qcom,bam-rx-ep-pipe-index = <3>;
+ qcom,master-id = <86>;
+ clock-names = "core_clk", "iface_clk";
+ clocks = <&clock_gcc GCC_BLSP1_UART2_APPS_CLK>,
+ <&clock_gcc GCC_BLSP1_AHB_CLK>;
+ pinctrl-names = "sleep", "default";
+ pinctrl-0 = <&blsp1_uart2a_tx_sleep>,
+ <&blsp1_uart2a_rxcts_sleep>, <&blsp1_uart2a_rfr_sleep>;
+ pinctrl-1 = <&blsp1_uart2b_tx_active>,
+ <&blsp1_uart2b_rxcts_active>, <&blsp1_uart2b_rfr_active>;
+
+ qcom,msm-bus,name = "buart2a";
+ qcom,msm-bus,num-cases = <2>;
+ qcom,msm-bus,num-paths = <1>;
+ qcom,msm-bus,vectors-KBps =
+ <86 512 0 0>,
+ <86 512 500 800>;
+ status = "disabled";
+ };
+
+ blsp1_uart2b_hs: uartb@830000 { /* BLSP1 UART2 : GPIO: 63,64,65,66 */
+ compatible = "qcom,msm-hsuart-v14";
+ reg = <0x830000 0x200>,
+ <0x804000 0x23000>;
+ reg-names = "core_mem", "bam_mem";
+ interrupt-names = "core_irq", "bam_irq", "wakeup_irq";
+ #address-cells = <0>;
+ interrupt-parent = <&blsp1_uart2b_hs>;
+ interrupts = <0 1 2>;
+ #interrupt-cells = <1>;
+ interrupt-map-mask = <0xffffffff>;
+ interrupt-map = <0 &intc 0 25 0
+ 1 &intc 0 58 0
+ 2 &tlmm 64 0>;
+
+ qcom,inject-rx-on-wakeup;
+ qcom,rx-char-to-inject = <0xfd>;
+
+ qcom,bam-tx-ep-pipe-index = <2>;
+ qcom,bam-rx-ep-pipe-index = <3>;
+ qcom,master-id = <86>;
+ clock-names = "core_clk", "iface_clk";
+ clocks = <&clock_gcc GCC_BLSP1_UART2_APPS_CLK>,
+ <&clock_gcc GCC_BLSP1_AHB_CLK>;
+ pinctrl-names = "sleep", "default";
+ pinctrl-0 = <&blsp1_uart2b_tx_sleep>,
+ <&blsp1_uart2b_rxcts_sleep>, <&blsp1_uart2b_rfr_sleep>;
+ pinctrl-1 = <&blsp1_uart2b_tx_active>,
+ <&blsp1_uart2b_rxcts_active>, <&blsp1_uart2b_rfr_active>;
+
+ qcom,msm-bus,name = "buart2b";
+ qcom,msm-bus,num-cases = <2>;
+ qcom,msm-bus,num-paths = <1>;
+ qcom,msm-bus,vectors-KBps =
+ <86 512 0 0>,
+ <86 512 500 800>;
+ status = "disabled";
+ };
+
+ blsp1_uart3_hs: uart@831000 { /* BLSP1 UART3: GPIO: 8,9,10,11 */
+ compatible = "qcom,msm-hsuart-v14";
+ reg = <0x831000 0x200>,
+ <0x804000 0x23000>;
+ reg-names = "core_mem", "bam_mem";
+ interrupt-names = "core_irq", "bam_irq", "wakeup_irq";
+ #address-cells = <0>;
+ interrupt-parent = <&blsp1_uart3_hs>;
+ interrupts = <0 1 2>;
+ #interrupt-cells = <1>;
+ interrupt-map-mask = <0xffffffff>;
+ interrupt-map = <0 &intc 0 26 0
+ 1 &intc 0 58 0
+ 2 &tlmm 9 0>;
+
+ qcom,inject-rx-on-wakeup;
+ qcom,rx-char-to-inject = <0xfd>;
+
+ qcom,bam-tx-ep-pipe-index = <4>;
+ qcom,bam-rx-ep-pipe-index = <5>;
+ qcom,master-id = <86>;
+ clock-names = "core_clk", "iface_clk";
+ clocks = <&clock_gcc GCC_BLSP1_UART3_APPS_CLK>,
+ <&clock_gcc GCC_BLSP1_AHB_CLK>;
+ pinctrl-names = "sleep", "default";
+ pinctrl-0 = <&blsp1_uart3_tx_sleep>,
+ <&blsp1_uart3_rxcts_sleep>, <&blsp1_uart3_rfr_sleep>;
+ pinctrl-1 = <&blsp1_uart3_tx_active>,
+ <&blsp1_uart3_rxcts_active>, <&blsp1_uart3_rfr_active>;
+
+ qcom,msm-bus,name = "buart3";
+ qcom,msm-bus,num-cases = <2>;
+ qcom,msm-bus,num-paths = <1>;
+ qcom,msm-bus,vectors-KBps =
+ <86 512 0 0>,
+ <86 512 500 800>;
+ status = "disabled";
+ };
+
+ blsp1_uart4a_hs: uarta@832000 { /* BLSP1 UART4 : GPIO: 20,21,22,23 */
+ compatible = "qcom,msm-hsuart-v14";
+ reg = <0x832000 0x200>,
+ <0x804000 0x23000>;
+ reg-names = "core_mem", "bam_mem";
+ interrupt-names = "core_irq", "bam_irq", "wakeup_irq";
+ #address-cells = <0>;
+ interrupt-parent = <&blsp1_uart4a_hs>;
+ interrupts = <0 1 2>;
+ #interrupt-cells = <1>;
+ interrupt-map-mask = <0xffffffff>;
+ interrupt-map = <0 &intc 0 27 0
+ 1 &intc 0 58 0
+ 2 &tlmm 21 0>;
+
+ qcom,inject-rx-on-wakeup;
+ qcom,rx-char-to-inject = <0xfd>;
+
+ qcom,bam-tx-ep-pipe-index = <6>;
+ qcom,bam-rx-ep-pipe-index = <7>;
+ qcom,master-id = <86>;
+ clock-names = "core_clk", "iface_clk";
+ clocks = <&clock_gcc GCC_BLSP1_UART4_APPS_CLK>,
+ <&clock_gcc GCC_BLSP1_AHB_CLK>;
+ pinctrl-names = "sleep", "default";
+ pinctrl-0 = <&blsp1_uart4a_tx_active>,
+ <&blsp1_uart4a_rxcts_sleep>, <&blsp1_uart4a_rfr_sleep>;
+ pinctrl-1 = <&blsp1_uart4a_tx_active>,
+ <&blsp1_uart4a_rxcts_active>, <&blsp1_uart4a_rfr_active>;
+
+ qcom,msm-bus,name = "buart4a";
+ qcom,msm-bus,num-cases = <2>;
+ qcom,msm-bus,num-paths = <1>;
+ qcom,msm-bus,vectors-KBps =
+ <86 512 0 0>,
+ <86 512 500 800>;
+ status = "disabled";
+ };
+
+ blsp1_uart4b_hs: uartb@832000 { /* BLSP1 UART4 : GPIO: 16,17,18,19 */
+ compatible = "qcom,msm-hsuart-v14";
+ reg = <0x832000 0x200>,
+ <0x804000 0x23000>;
+ reg-names = "core_mem", "bam_mem";
+ interrupt-names = "core_irq", "bam_irq", "wakeup_irq";
+ #address-cells = <0>;
+ interrupt-parent = <&blsp1_uart4b_hs>;
+ interrupts = <0 1 2>;
+ #interrupt-cells = <1>;
+ interrupt-map-mask = <0xffffffff>;
+ interrupt-map = <0 &intc 0 27 0
+ 1 &intc 0 58 0
+ 2 &tlmm 17 0>;
+
+ qcom,inject-rx-on-wakeup;
+ qcom,rx-char-to-inject = <0xfd>;
+
+ qcom,bam-tx-ep-pipe-index = <6>;
+ qcom,bam-rx-ep-pipe-index = <7>;
+ qcom,master-id = <86>;
+ clock-names = "core_clk", "iface_clk";
+ clocks = <&clock_gcc GCC_BLSP1_UART4_APPS_CLK>,
+ <&clock_gcc GCC_BLSP1_AHB_CLK>;
+ pinctrl-names = "sleep", "default";
+ pinctrl-0 = <&blsp1_uart4b_tx_sleep>,
+ <&blsp1_uart4b_rxcts_sleep>, <&blsp1_uart4b_rfr_sleep>;
+ pinctrl-1 = <&blsp1_uart4b_tx_active>,
+ <&blsp1_uart4b_rxcts_active>, <&blsp1_uart4b_rfr_active>;
+
+ qcom,msm-bus,name = "buart4b";
+ qcom,msm-bus,num-cases = <2>;
+ qcom,msm-bus,num-paths = <1>;
+ qcom,msm-bus,vectors-KBps =
+ <86 512 0 0>,
+ <86 512 500 800>;
+ status = "disabled";
+ };
+};
diff --git a/arch/arm/boot/dts/qcom/sdxpoorwills-bus.dtsi b/arch/arm/boot/dts/qcom/sdxpoorwills-bus.dtsi
new file mode 100644
index 0000000..d1d44ec
--- /dev/null
+++ b/arch/arm/boot/dts/qcom/sdxpoorwills-bus.dtsi
@@ -0,0 +1,804 @@
+/* Copyright (c) 2017, 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 <dt-bindings/msm/msm-bus-ids.h>
+
+&soc {
+ ad_hoc_bus: ad-hoc-bus {
+ compatible = "qcom,msm-bus-device";
+ reg = <0x1100000 0x400000>,
+ <0x1100000 0x400000>,
+ <0x1620000 0x400000>,
+ <0x1620000 0x400000>;
+
+ reg-names = "mc_virt-base", "mem_noc-base",
+ "system_noc-base", "ipa_virt-base";
+
+ /*RSCs*/
+ rsc_apps: rsc-apps {
+ cell-id = <MSM_BUS_RSC_APPS>;
+ label = "apps_rsc";
+ qcom,rsc-dev;
+ qcom,req-state = <2>;
+ };
+
+ /*BCMs*/
+ bcm_alc: bcm-alc {
+ cell-id = <MSM_BUS_BCM_ALC>;
+ label = "ALC";
+ qcom,bcm-name = "ALC";
+ qcom,rscs = <&rsc_apps>;
+ qcom,bcm-dev;
+ };
+
+ bcm_ce: bcm-ce {
+ cell-id = <MSM_BUS_BCM_CE>;
+ label = "CE";
+ qcom,bcm-name = "CE";
+ qcom,rscs = <&rsc_apps>;
+ qcom,bcm-dev;
+ };
+
+ bcm_mc0: bcm-mc0 {
+ cell-id = <MSM_BUS_BCM_MC0>;
+ label = "MC0";
+ qcom,bcm-name = "MC0";
+ qcom,rscs = <&rsc_apps>;
+ qcom,bcm-dev;
+ };
+
+ bcm_ip0: bcm-ip0 {
+ cell-id = <MSM_BUS_BCM_IP0>;
+ label = "IP0";
+ qcom,bcm-name = "CE";
+ qcom,rscs = <&rsc_apps>;
+ qcom,bcm-dev;
+ };
+
+ bcm_sh0: bcm-sh0 {
+ cell-id = <MSM_BUS_BCM_SH0>;
+ label = "SH0";
+ qcom,bcm-name = "SH0";
+ qcom,rscs = <&rsc_apps>;
+ qcom,bcm-dev;
+ };
+
+ bcm_pn0: bcm-pn0 {
+ cell-id = <MSM_BUS_BCM_PN0>;
+ label = "PN0";
+ qcom,bcm-name = "PN0";
+ qcom,rscs = <&rsc_apps>;
+ qcom,bcm-dev;
+ };
+
+ bcm_sh1: bcm-sh1 {
+ cell-id = <MSM_BUS_BCM_SH1>;
+ label = "SH1";
+ qcom,bcm-name = "SH1";
+ qcom,rscs = <&rsc_apps>;
+ qcom,bcm-dev;
+ };
+
+ bcm_sh3: bcm-sh3 {
+ cell-id = <MSM_BUS_BCM_SH3>;
+ label = "SH3";
+ qcom,bcm-name = "SH3";
+ qcom,rscs = <&rsc_apps>;
+ qcom,bcm-dev;
+ };
+
+ bcm_sh4: bcm-sh4 {
+ cell-id = <MSM_BUS_BCM_SH4>;
+ label = "SH4";
+ qcom,bcm-name = "SH4";
+ qcom,rscs = <&rsc_apps>;
+ qcom,bcm-dev;
+ };
+
+ bcm_sn0: bcm-sn0 {
+ cell-id = <MSM_BUS_BCM_SN0>;
+ label = "SN0";
+ qcom,bcm-name = "SN0";
+ qcom,rscs = <&rsc_apps>;
+ qcom,bcm-dev;
+ };
+
+ bcm_sn1: bcm-sn1 {
+ cell-id = <MSM_BUS_BCM_SN1>;
+ label = "SN1";
+ qcom,bcm-name = "SN1";
+ qcom,rscs = <&rsc_apps>;
+ qcom,bcm-dev;
+ };
+
+ bcm_pn1: bcm-pn1 {
+ cell-id = <MSM_BUS_BCM_PN1>;
+ label = "PN1";
+ qcom,bcm-name = "PN1";
+ qcom,rscs = <&rsc_apps>;
+ qcom,bcm-dev;
+ };
+
+ bcm_pn2: bcm-pn2 {
+ cell-id = <MSM_BUS_BCM_PN2>;
+ label = "PN2";
+ qcom,bcm-name = "PN2";
+ qcom,rscs = <&rsc_apps>;
+ qcom,bcm-dev;
+ };
+
+ bcm_sn3: bcm-sn3 {
+ cell-id = <MSM_BUS_BCM_SN3>;
+ label = "SN3";
+ qcom,bcm-name = "SN3";
+ qcom,rscs = <&rsc_apps>;
+ qcom,bcm-dev;
+ };
+
+ bcm_pn3: bcm-pn3 {
+ cell-id = <MSM_BUS_BCM_PN3>;
+ label = "PN3";
+ qcom,bcm-name = "PN3";
+ qcom,rscs = <&rsc_apps>;
+ qcom,bcm-dev;
+ };
+
+ bcm_pn5: bcm-pn5 {
+ cell-id = <MSM_BUS_BCM_PN5>;
+ label = "PN5";
+ qcom,bcm-name = "PN5";
+ qcom,rscs = <&rsc_apps>;
+ qcom,bcm-dev;
+ };
+
+ bcm_sn6: bcm-sn6 {
+ cell-id = <MSM_BUS_BCM_SN6>;
+ label = "SN6";
+ qcom,bcm-name = "SN6";
+ qcom,rscs = <&rsc_apps>;
+ qcom,bcm-dev;
+ };
+
+ bcm_sn7: bcm-sn7 {
+ cell-id = <MSM_BUS_BCM_SN7>;
+ label = "SN7";
+ qcom,bcm-name = "SN7";
+ qcom,rscs = <&rsc_apps>;
+ qcom,bcm-dev;
+ };
+
+ bcm_sn8: bcm-sn8 {
+ cell-id = <MSM_BUS_BCM_SN8>;
+ label = "SN8";
+ qcom,bcm-name = "SN8";
+ qcom,rscs = <&rsc_apps>;
+ qcom,bcm-dev;
+ };
+
+ bcm_sn9: bcm-sn9 {
+ cell-id = <MSM_BUS_BCM_SN9>;
+ label = "SN9";
+ qcom,bcm-name = "SN9";
+ qcom,rscs = <&rsc_apps>;
+ qcom,bcm-dev;
+ };
+
+ bcm_sn11: bcm-sn11 {
+ cell-id = <MSM_BUS_BCM_SN11>;
+ label = "SN11";
+ qcom,bcm-name = "SN11";
+ qcom,rscs = <&rsc_apps>;
+ qcom,bcm-dev;
+ };
+
+ /*Buses*/
+ fab_ipa_virt: fab-ipa_virt{
+ cell-id = <MSM_BUS_FAB_IPA_VIRT>;
+ label = "fab-ipa_virt";
+ qcom,fab-dev;
+ qcom,base-name = "ipa_virt-base";
+ qcom,qos-off = <0>;
+ qcom,base-offset = <0>;
+ qcom,bypass-qos-prg;
+ clocks = <>;
+ };
+
+ fab_mc_virt: fab-mc_virt{
+ cell-id = <MSM_BUS_FAB_MC_VIRT>;
+ label = "fab-mc_virt";
+ qcom,fab-dev;
+ qcom,base-name = "mc_virt-base";
+ qcom,qos-off = <0>;
+ qcom,base-offset = <0>;
+ qcom,bypass-qos-prg;
+ clocks = <>;
+ };
+
+ fab_mem_noc: fab-mem_noc {
+ cell-id = <MSM_BUS_FAB_MEM_NOC>;
+ label = "fab-mem_noc";
+ qcom,fab-dev;
+ qcom,base-name = "mem_noc-base";
+ qcom,qos-off = <4096>;
+ qcom,base-offset = <65536>;
+ qcom,bypass-qos-prg;
+ qcom,bus-type = <1>;
+ clocks = <>;
+ };
+
+ fab_system_noc: fab-system_noc {
+ cell-id = <MSM_BUS_FAB_SYS_NOC>;
+ label = "fab-system_noc";
+ qcom,fab-dev;
+ qcom,base-name = "system_noc-base";
+ qcom,qos-off = <0>;
+ qcom,base-offset = <0>;
+ qcom,bypass-qos-prg;
+ qcom,bus-type = <1>;
+ clocks = <>;
+ };
+
+ /*Masters*/
+
+ mas_ipa_core_master: mas-ipa-core-master {
+ cell-id = <MSM_BUS_MASTER_IPA_CORE>;
+ label = "mas-ipa-core-master";
+ qcom,buswidth = <8>;
+ qcom,agg-ports = <1>;
+ qcom,connections = <&slv_ipa_core_slave>;
+ qcom,bus-dev = <&fab_ipa_virt>;
+ };
+
+ mas_llcc_mc: mas-llcc-mc {
+ cell-id = <MSM_BUS_MASTER_LLCC>;
+ label = "mas-llcc-mc";
+ qcom,buswidth = <16>;
+ qcom,agg-ports = <1>;
+ qcom,connections = <&slv_ebi>;
+ qcom,bus-dev = <&fab_mc_virt>;
+ };
+
+ mas_acm_tcu: mas-acm-tcu {
+ cell-id = <MSM_BUS_MASTER_TCU_0>;
+ label = "mas-acm-tcu";
+ qcom,buswidth = <8>;
+ qcom,agg-ports = <1>;
+ qcom,qport = <0>;
+ qcom,connections = <&slv_qns_llcc>;
+ qcom,bus-dev = <&fab_mem_noc>;
+ qcom,bcms = <&bcm_sh1>;
+ qcom,ap-owned;
+ qcom,prio = <0>;
+ };
+
+ mas_qnm_snoc_gc: mas-qnm-snoc-gc {
+ cell-id = <MSM_BUS_MASTER_SNOC_GC_MEM_NOC>;
+ label = "mas-qnm-snoc-gc";
+ qcom,buswidth = <8>;
+ qcom,agg-ports = <1>;
+ qcom,qport = <8>;
+ qcom,connections = <&slv_qns_llcc>;
+ qcom,bus-dev = <&fab_mem_noc>;
+ qcom,ap-owned;
+ qcom,prio = <0>;
+ };
+
+ mas_xm_apps_rdwr: mas-xm-apps-rdwr {
+ cell-id = <MSM_BUS_MASTER_AMPSS_M0>;
+ label = "mas-xm-apps-rdwr";
+ qcom,buswidth = <16>;
+ qcom,agg-ports = <1>;
+ qcom,qport = <3>;
+ qcom,connections = <&slv_qns_llcc &slv_qns_memnoc_snoc>;
+ qcom,bus-dev = <&fab_mem_noc>;
+ qcom,bcms = <&bcm_sh3>;
+ qcom,ap-owned;
+ qcom,prio = <0>;
+ };
+
+ mas_qhm_audio: mas-qhm-audio {
+ cell-id = <MSM_BUS_MASTER_AUDIO>;
+ label = "mas-qhm-audio";
+ qcom,buswidth = <4>;
+ qcom,agg-ports = <1>;
+ qcom,connections = <&slv_qns_aggre_noc>;
+ qcom,bus-dev = <&fab_system_noc>;
+ qcom,bcms = <&bcm_pn2>;
+ };
+
+ mas_qhm_blsp1: mas-qhm-blsp1 {
+ cell-id = <MSM_BUS_MASTER_BLSP_1>;
+ label = "mas-qhm-blsp1";
+ qcom,buswidth = <4>;
+ qcom,agg-ports = <1>;
+ qcom,connections = <&slv_qns_aggre_noc>;
+ qcom,bus-dev = <&fab_system_noc>;
+ qcom,bcms = <&bcm_pn3>;
+ };
+
+ mas_qhm_qdss_bam: mas-qhm-qdss-bam {
+ cell-id = <MSM_BUS_MASTER_QDSS_BAM>;
+ label = "mas-qhm-qdss-bam";
+ qcom,buswidth = <4>;
+ qcom,agg-ports = <1>;
+ qcom,connections = <&slv_qhs_crypto_cfg
+ &slv_qhs_snoc_cfg &slv_qhs_emac_cfg
+ &slv_qhs_aoss &slv_qhs_spmi_fetcher
+ &slv_qhs_pdm &slv_qns_snoc_memnoc
+ &slv_qhs_tcsr &slv_qhs_qpic
+ &slv_qxs_imem &slv_qhs_ipa
+ &slv_qhs_usb3_phy &slv_qhs_aop
+ &slv_qhs_blsp1 &slv_qhs_sdc1
+ &slv_qhs_pcie_parf &slv_qhs_audio
+ &slv_qhs_tlmm &slv_qhs_prng
+ &slv_xs_sys_tcu_cfg &slv_qhs_clk_ctl
+ &slv_qhs_usb3>;
+ qcom,bus-dev = <&fab_system_noc>;
+ qcom,bcms = <&bcm_sn8>;
+ };
+
+ mas_qhm_qpic: mas-qhm-qpic {
+ cell-id = <MSM_BUS_MASTER_QPIC>;
+ label = "mas-qhm-qpic";
+ qcom,buswidth = <4>;
+ qcom,agg-ports = <1>;
+ qcom,connections = <&slv_qhs_aoss &slv_qns_aggre_noc>;
+ qcom,bus-dev = <&fab_system_noc>;
+ qcom,bcms = <&bcm_pn3>;
+ };
+
+ mas_qhm_snoc_cfg: mas-qhm-snoc-cfg {
+ cell-id = <MSM_BUS_MASTER_SNOC_CFG>;
+ label = "mas-qhm-snoc-cfg";
+ qcom,buswidth = <4>;
+ qcom,agg-ports = <1>;
+ qcom,connections = <&slv_srvc_snoc>;
+ qcom,bus-dev = <&fab_system_noc>;
+ };
+
+ mas_qhm_spmi_fetcher1: mas-qhm-spmi-fetcher1 {
+ cell-id = <MSM_BUS_MASTER_SPMI_FETCHER>;
+ label = "mas-qhm-spmi-fetcher1";
+ qcom,buswidth = <4>;
+ qcom,agg-ports = <1>;
+ qcom,connections = <&slv_qns_aggre_noc &slv_qhs_aop>;
+ qcom,bus-dev = <&fab_system_noc>;
+ qcom,bcms = <&bcm_pn2>;
+ };
+
+ mas_qnm_aggre_noc: mas-qnm-aggre-noc {
+ cell-id = <MSM_BUS_MASTER_ANOC_SNOC>;
+ label = "mas-qnm-aggre-noc";
+ qcom,buswidth = <8>;
+ qcom,agg-ports = <1>;
+ qcom,connections = <&slv_qhs_crypto_cfg
+ &slv_qhs_snoc_cfg &slv_qhs_emac_cfg
+ &slv_qhs_aoss &slv_qhs_spmi_fetcher
+ &slv_qhs_pdm &slv_qns_snoc_memnoc
+ &slv_qhs_tcsr &slv_xs_qdss_stm
+ &slv_qhs_qpic &slv_qxs_imem
+ &slv_qhs_ipa &slv_qhs_usb3_phy
+ &slv_qhs_aop &slv_qhs_blsp1
+ &slv_qhs_sdc1 &slv_qhs_pcie_parf
+ &slv_qhs_audio &slv_qxs_pcie
+ &slv_qhs_tlmm &slv_qhs_prng
+ &slv_xs_sys_tcu_cfg &slv_qhs_clk_ctl
+ &slv_qhs_usb3>;
+ qcom,bus-dev = <&fab_system_noc>;
+ qcom,bcms = <&bcm_sn7>;
+ };
+
+ mas_qnm_aggre_noc_ipa: mas-qnm-aggre-noc-ipa {
+ cell-id = <MSM_BUS_MASTER_ANOC_IPA>;
+ label = "mas-qnm-aggre-noc-ipa";
+ qcom,buswidth = <8>;
+ qcom,agg-ports = <1>;
+ qcom,connections = <&slv_qhs_crypto_cfg
+ &slv_qhs_snoc_cfg &slv_qhs_emac_cfg
+ &slv_qhs_aoss &slv_qhs_spmi_fetcher
+ &slv_qhs_pdm &slv_qns_snoc_memnoc
+ &slv_qhs_tcsr &slv_xs_qdss_stm
+ &slv_qhs_qpic &slv_qxs_imem
+ &slv_qhs_ipa &slv_qhs_usb3_phy
+ &slv_qhs_aop &slv_qhs_blsp1
+ &slv_qhs_sdc1 &slv_qhs_pcie_parf
+ &slv_qhs_audio &slv_qhs_tlmm
+ &slv_qhs_prng &slv_xs_sys_tcu_cfg
+ &slv_qhs_clk_ctl &slv_qhs_usb3>;
+ qcom,bus-dev = <&fab_system_noc>;
+ };
+
+ mas_qnm_memnoc: mas-qnm-memnoc {
+ cell-id = <MSM_BUS_MASTER_MEM_NOC_SNOC>;
+ label = "mas-qnm-memnoc";
+ qcom,buswidth = <8>;
+ qcom,agg-ports = <1>;
+ qcom,connections = <&slv_qhs_crypto_cfg
+ &slv_qhs_snoc_cfg &slv_qhs_emac_cfg
+ &slv_qhs_aoss &slv_qhs_spmi_fetcher
+ &slv_qhs_pdm &slv_qhs_tcsr
+ &slv_xs_qdss_stm &slv_qhs_qpic
+ &slv_qxs_imem &slv_qhs_ipa
+ &slv_qhs_usb3_phy &slv_qhs_aop
+ &slv_qhs_blsp1 &slv_qhs_sdc1
+ &slv_qhs_pcie_parf &slv_qhs_audio
+ &slv_qhs_tlmm &slv_qhs_prng
+ &slv_xs_sys_tcu_cfg &slv_qhs_clk_ctl
+ &slv_qhs_usb3>;
+ qcom,bus-dev = <&fab_system_noc>;
+ qcom,bcms = <&bcm_sn9>;
+ };
+
+ mas_qxm_crypto: mas-qxm-crypto {
+ cell-id = <MSM_BUS_MASTER_CRYPTO_CORE_0>;
+ label = "mas-qxm-crypto";
+ qcom,buswidth = <8>;
+ qcom,agg-ports = <1>;
+ qcom,connections = <&slv_qhs_aoss &slv_qns_aggre_noc>;
+ qcom,bus-dev = <&fab_system_noc>;
+ qcom,bcms = <&bcm_ce>;
+ };
+
+ mas_qxm_ipa: mas-qxm-ipa {
+ cell-id = <MSM_BUS_MASTER_IPA>;
+ label = "mas-qxm-ipa";
+ qcom,buswidth = <8>;
+ qcom,agg-ports = <1>;
+ qcom,connections = <&slv_qns_aggre_noc_ipa>;
+ qcom,bus-dev = <&fab_system_noc>;
+ qcom,bcms = <&bcm_sn11>;
+ };
+
+ mas_xm_emac: mas-xm-emac {
+ cell-id = <MSM_BUS_MASTER_EMAC>;
+ label = "mas-xm-emac";
+ qcom,buswidth = <8>;
+ qcom,agg-ports = <1>;
+ qcom,connections = <&slv_qns_aggre_noc>;
+ qcom,bus-dev = <&fab_system_noc>;
+ };
+
+ mas_xm_pcie: mas-xm-pcie {
+ cell-id = <MSM_BUS_MASTER_PCIE>;
+ label = "mas-xm-pcie";
+ qcom,buswidth = <8>;
+ qcom,agg-ports = <1>;
+ qcom,connections = <&slv_qns_aggre_noc>;
+ qcom,bus-dev = <&fab_system_noc>;
+ };
+
+ mas_xm_qdss_etr: mas-xm-qdss-etr {
+ cell-id = <MSM_BUS_MASTER_QDSS_ETR>;
+ label = "mas-xm-qdss-etr";
+ qcom,buswidth = <8>;
+ qcom,agg-ports = <1>;
+ qcom,connections = <&slv_qhs_crypto_cfg
+ &slv_qhs_snoc_cfg &slv_qhs_emac_cfg
+ &slv_qhs_aoss &slv_qhs_spmi_fetcher
+ &slv_qhs_pdm &slv_qns_snoc_memnoc
+ &slv_qhs_tcsr &slv_qhs_qpic
+ &slv_qxs_imem &slv_qhs_ipa
+ &slv_qhs_usb3_phy &slv_qhs_aop
+ &slv_qhs_blsp1 &slv_qhs_sdc1
+ &slv_qhs_pcie_parf &slv_qhs_audio
+ &slv_qhs_tlmm &slv_qhs_prng
+ &slv_xs_sys_tcu_cfg &slv_qhs_clk_ctl
+ &slv_qhs_usb3>;
+ qcom,bus-dev = <&fab_system_noc>;
+ qcom,bcms = <&bcm_sn8>;
+ };
+
+ mas_xm_sdc1: mas-xm-sdc1 {
+ cell-id = <MSM_BUS_MASTER_SDCC_1>;
+ label = "mas-xm-sdc1";
+ qcom,buswidth = <8>;
+ qcom,agg-ports = <1>;
+ qcom,connections = <&slv_qhs_aoss &slv_qns_aggre_noc>;
+ qcom,bus-dev = <&fab_system_noc>;
+ qcom,bcms = <&bcm_pn1>;
+ };
+
+ mas_xm_usb3: mas-xm-usb3 {
+ cell-id = <MSM_BUS_MASTER_USB3>;
+ label = "mas-xm-usb3";
+ qcom,buswidth = <8>;
+ qcom,agg-ports = <1>;
+ qcom,connections = <&slv_qns_aggre_noc>;
+ qcom,bus-dev = <&fab_system_noc>;
+ };
+
+ /*Internal nodes*/
+
+ /*Slaves*/
+
+ slv_ipa_core_slave:slv-ipa-core-slave {
+ cell-id = <MSM_BUS_SLAVE_IPA_CORE>;
+ label = "slv-ipa-core-slave";
+ qcom,buswidth = <8>;
+ qcom,agg-ports = <1>;
+ qcom,bus-dev = <&fab_ipa_virt>;
+ qcom,bcms = <&bcm_ip0>;
+ };
+
+ slv_ebi:slv-ebi {
+ cell-id = <MSM_BUS_SLAVE_EBI_CH0>;
+ label = "slv-ebi";
+ qcom,buswidth = <16>;
+ qcom,agg-ports = <1>;
+ qcom,bus-dev = <&fab_mc_virt>;
+ qcom,bcms = <&bcm_mc0>;
+ };
+
+ slv_qns_llcc:slv-qns-llcc {
+ cell-id = <MSM_BUS_SLAVE_LLCC>;
+ label = "slv-qns-llcc";
+ qcom,buswidth = <16>;
+ qcom,agg-ports = <1>;
+ qcom,bus-dev = <&fab_mem_noc>;
+ qcom,connections = <&mas_llcc_mc>;
+ qcom,bcms = <&bcm_sh0>;
+ };
+
+ slv_qns_memnoc_snoc:slv-qns-memnoc-snoc {
+ cell-id = <MSM_BUS_SLAVE_MEM_NOC_SNOC>;
+ label = "slv-qns-memnoc-snoc";
+ qcom,buswidth = <8>;
+ qcom,agg-ports = <1>;
+ qcom,bus-dev = <&fab_mem_noc>;
+ qcom,connections = <&mas_qnm_memnoc>;
+ qcom,bcms = <&bcm_sh4>;
+ };
+
+ slv_qhs_aop:slv-qhs-aop {
+ cell-id = <MSM_BUS_SLAVE_AOP>;
+ label = "slv-qhs-aop";
+ qcom,buswidth = <4>;
+ qcom,agg-ports = <1>;
+ qcom,bus-dev = <&fab_system_noc>;
+ qcom,bcms = <&bcm_pn0>;
+ };
+
+ slv_qhs_aoss:slv-qhs-aoss {
+ cell-id = <MSM_BUS_SLAVE_AOSS>;
+ label = "slv-qhs-aoss";
+ qcom,buswidth = <4>;
+ qcom,agg-ports = <1>;
+ qcom,bus-dev = <&fab_system_noc>;
+ qcom,bcms = <&bcm_pn0>;
+ };
+
+ slv_qhs_audio:slv-qhs-audio {
+ cell-id = <MSM_BUS_SLAVE_AUDIO>;
+ label = "slv-qhs-audio";
+ qcom,buswidth = <4>;
+ qcom,agg-ports = <1>;
+ qcom,bus-dev = <&fab_system_noc>;
+ qcom,bcms = <&bcm_pn0>;
+ };
+
+ slv_qhs_blsp1:slv-qhs-blsp1 {
+ cell-id = <MSM_BUS_SLAVE_BLSP_1>;
+ label = "slv-qhs-blsp1";
+ qcom,buswidth = <4>;
+ qcom,agg-ports = <1>;
+ qcom,bus-dev = <&fab_system_noc>;
+ qcom,bcms = <&bcm_pn0>;
+ };
+
+ slv_qhs_clk_ctl:slv-qhs-clk-ctl {
+ cell-id = <MSM_BUS_SLAVE_CLK_CTL>;
+ label = "slv-qhs-clk-ctl";
+ qcom,buswidth = <4>;
+ qcom,agg-ports = <1>;
+ qcom,bus-dev = <&fab_system_noc>;
+ qcom,bcms = <&bcm_pn0>;
+ };
+
+ slv_qhs_crypto_cfg:slv-qhs-crypto-cfg {
+ cell-id = <MSM_BUS_SLAVE_CRYPTO_0_CFG>;
+ label = "slv-qhs-crypto-cfg";
+ qcom,buswidth = <4>;
+ qcom,agg-ports = <1>;
+ qcom,bus-dev = <&fab_system_noc>;
+ qcom,bcms = <&bcm_pn0>;
+ };
+
+ slv_qhs_emac_cfg:slv-qhs-emac-cfg {
+ cell-id = <MSM_BUS_SLAVE_EMAC_CFG>;
+ label = "slv-qhs-emac-cfg";
+ qcom,buswidth = <4>;
+ qcom,agg-ports = <1>;
+ qcom,bus-dev = <&fab_system_noc>;
+ qcom,bcms = <&bcm_pn0>;
+ };
+
+ slv_qhs_ipa:slv-qhs-ipa {
+ cell-id = <MSM_BUS_SLAVE_IPA_CFG>;
+ label = "slv-qhs-ipa";
+ qcom,buswidth = <4>;
+ qcom,agg-ports = <1>;
+ qcom,bus-dev = <&fab_system_noc>;
+ qcom,bcms = <&bcm_pn0>;
+ };
+
+ slv_qhs_pcie_parf:slv-qhs-pcie-parf {
+ cell-id = <MSM_BUS_SLAVE_PCIE_PARF>;
+ label = "slv-qhs-pcie-parf";
+ qcom,buswidth = <4>;
+ qcom,agg-ports = <1>;
+ qcom,bus-dev = <&fab_system_noc>;
+ qcom,bcms = <&bcm_pn0>;
+ };
+
+ slv_qhs_pdm:slv-qhs-pdm {
+ cell-id = <MSM_BUS_SLAVE_PDM>;
+ label = "slv-qhs-pdm";
+ qcom,buswidth = <4>;
+ qcom,agg-ports = <1>;
+ qcom,bus-dev = <&fab_system_noc>;
+ qcom,bcms = <&bcm_pn0>;
+ };
+
+ slv_qhs_prng:slv-qhs-prng {
+ cell-id = <MSM_BUS_SLAVE_PRNG>;
+ label = "slv-qhs-prng";
+ qcom,buswidth = <4>;
+ qcom,agg-ports = <1>;
+ qcom,bus-dev = <&fab_system_noc>;
+ qcom,bcms = <&bcm_pn0>;
+ };
+
+ slv_qhs_qpic:slv-qhs-qpic {
+ cell-id = <MSM_BUS_SLAVE_QPIC>;
+ label = "slv-qhs-qpic";
+ qcom,buswidth = <4>;
+ qcom,agg-ports = <1>;
+ qcom,bus-dev = <&fab_system_noc>;
+ qcom,bcms = <&bcm_pn0>;
+ };
+
+ slv_qhs_sdc1:slv-qhs-sdc1 {
+ cell-id = <MSM_BUS_SLAVE_SDCC_1>;
+ label = "slv-qhs-sdc1";
+ qcom,buswidth = <4>;
+ qcom,agg-ports = <1>;
+ qcom,bus-dev = <&fab_system_noc>;
+ qcom,bcms = <&bcm_pn0>;
+ };
+
+ slv_qhs_snoc_cfg:slv-qhs-snoc-cfg {
+ cell-id = <MSM_BUS_SLAVE_SNOC_CFG>;
+ label = "slv-qhs-snoc-cfg";
+ qcom,buswidth = <4>;
+ qcom,agg-ports = <1>;
+ qcom,bus-dev = <&fab_system_noc>;
+ qcom,connections = <&mas_qhm_snoc_cfg>;
+ qcom,bcms = <&bcm_pn0>;
+ };
+
+ slv_qhs_spmi_fetcher:slv-qhs-spmi-fetcher {
+ cell-id = <MSM_BUS_SLAVE_SPMI_FETCHER>;
+ label = "slv-qhs-spmi-fetcher";
+ qcom,buswidth = <4>;
+ qcom,agg-ports = <1>;
+ qcom,bus-dev = <&fab_system_noc>;
+ qcom,bcms = <&bcm_pn0>;
+ };
+
+ slv_qhs_tcsr:slv-qhs-tcsr {
+ cell-id = <MSM_BUS_SLAVE_TCSR>;
+ label = "slv-qhs-tcsr";
+ qcom,buswidth = <4>;
+ qcom,agg-ports = <1>;
+ qcom,bus-dev = <&fab_system_noc>;
+ qcom,bcms = <&bcm_pn0>;
+ };
+
+ slv_qhs_tlmm:slv-qhs-tlmm {
+ cell-id = <MSM_BUS_SLAVE_TLMM>;
+ label = "slv-qhs-tlmm";
+ qcom,buswidth = <4>;
+ qcom,agg-ports = <1>;
+ qcom,bus-dev = <&fab_system_noc>;
+ qcom,bcms = <&bcm_pn0>;
+ };
+
+ slv_qhs_usb3:slv-qhs-usb3 {
+ cell-id = <MSM_BUS_SLAVE_USB3>;
+ label = "slv-qhs-usb3";
+ qcom,buswidth = <4>;
+ qcom,agg-ports = <1>;
+ qcom,bus-dev = <&fab_system_noc>;
+ qcom,bcms = <&bcm_pn0>;
+ };
+
+ slv_qhs_usb3_phy:slv-qhs-usb3-phy {
+ cell-id = <MSM_BUS_SLAVE_USB3_PHY_CFG>;
+ label = "slv-qhs-usb3-phy";
+ qcom,buswidth = <4>;
+ qcom,agg-ports = <1>;
+ qcom,bus-dev = <&fab_system_noc>;
+ qcom,bcms = <&bcm_pn0>;
+ };
+
+ slv_qns_aggre_noc:slv-qns-aggre-noc {
+ cell-id = <MSM_BUS_SLAVE_ANOC_SNOC>;
+ label = "slv-qns-aggre-noc";
+ qcom,buswidth = <8>;
+ qcom,agg-ports = <1>;
+ qcom,bus-dev = <&fab_system_noc>;
+ qcom,connections = <&mas_qnm_aggre_noc>;
+ };
+
+ slv_qns_aggre_noc_ipa:slv-qns-aggre-noc-ipa {
+ cell-id = <MSM_BUS_SLAVE_ANOC_IPA>;
+ label = "slv-qns-aggre-noc-ipa";
+ qcom,buswidth = <8>;
+ qcom,agg-ports = <1>;
+ qcom,bus-dev = <&fab_system_noc>;
+ qcom,connections = <&mas_qnm_aggre_noc_ipa>;
+ };
+
+ slv_qns_snoc_memnoc:slv-qns-snoc-memnoc {
+ cell-id = <MSM_BUS_SLAVE_SNOC_MEM_NOC_GC>;
+ label = "slv-qns-snoc-memnoc";
+ qcom,buswidth = <8>;
+ qcom,agg-ports = <1>;
+ qcom,bus-dev = <&fab_system_noc>;
+ qcom,connections = <&mas_qnm_snoc_gc>;
+ qcom,bcms = <&bcm_sn0>;
+ };
+
+ slv_qxs_imem:slv-qxs-imem {
+ cell-id = <MSM_BUS_SLAVE_OCIMEM>;
+ label = "slv-qxs-imem";
+ qcom,buswidth = <8>;
+ qcom,agg-ports = <1>;
+ qcom,bus-dev = <&fab_system_noc>;
+ qcom,bcms = <&bcm_sn1>;
+ };
+
+ slv_qxs_pcie:slv-qxs-pcie {
+ cell-id = <MSM_BUS_SLAVE_PCIE_0>;
+ label = "slv-qxs-pcie";
+ qcom,buswidth = <8>;
+ qcom,agg-ports = <1>;
+ qcom,bus-dev = <&fab_system_noc>;
+ qcom,bcms = <&bcm_sn6>;
+ };
+
+ slv_srvc_snoc:slv-srvc-snoc {
+ cell-id = <MSM_BUS_SLAVE_SERVICE_SNOC>;
+ label = "slv-srvc-snoc";
+ qcom,buswidth = <4>;
+ qcom,agg-ports = <1>;
+ qcom,bus-dev = <&fab_system_noc>;
+ };
+
+ slv_xs_qdss_stm:slv-xs-qdss-stm {
+ cell-id = <MSM_BUS_SLAVE_QDSS_STM>;
+ label = "slv-xs-qdss-stm";
+ qcom,buswidth = <4>;
+ qcom,agg-ports = <1>;
+ qcom,bus-dev = <&fab_system_noc>;
+ qcom,bcms = <&bcm_sn3>;
+ };
+
+ slv_xs_sys_tcu_cfg:slv-xs-sys-tcu-cfg {
+ cell-id = <MSM_BUS_SLAVE_TCU>;
+ label = "slv-xs-sys-tcu-cfg";
+ qcom,buswidth = <8>;
+ qcom,agg-ports = <1>;
+ qcom,bus-dev = <&fab_system_noc>;
+ };
+ };
+};
diff --git a/arch/arm/boot/dts/qcom/sdxpoorwills-cdp.dts b/arch/arm/boot/dts/qcom/sdxpoorwills-cdp.dts
new file mode 100644
index 0000000..6be47b4
--- /dev/null
+++ b/arch/arm/boot/dts/qcom/sdxpoorwills-cdp.dts
@@ -0,0 +1,31 @@
+/* Copyright (c) 2017, 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 "sdxpoorwills.dtsi"
+#include "sdxpoorwills-pinctrl.dtsi"
+
+/ {
+ model = "Qualcomm Technologies, Inc. SDXPOORWILLS CDP";
+ compatible = "qcom,sdxpoorwills-cdp",
+ "qcom,sdxpoorwills", "qcom,cdp";
+ qcom,board-id = <1 0x0>, <1 0x100>;
+};
+
+&blsp1_uart2 {
+ pinctrl-names = "default";
+ pinctrl-0 = <&uart2_console_active>;
+ status = "ok";
+};
+
diff --git a/arch/arm/boot/dts/qcom/sdxpoorwills-mtp.dts b/arch/arm/boot/dts/qcom/sdxpoorwills-mtp.dts
new file mode 100644
index 0000000..15ae24c
--- /dev/null
+++ b/arch/arm/boot/dts/qcom/sdxpoorwills-mtp.dts
@@ -0,0 +1,31 @@
+/* Copyright (c) 2017, 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 "sdxpoorwills.dtsi"
+#include "sdxpoorwills-pinctrl.dtsi"
+
+/ {
+ model = "Qualcomm Technologies, Inc. SDXPOORWILLS MTP";
+ compatible = "qcom,sdxpoorwills-mtp",
+ "qcom,sdxpoorwills", "qcom,mtp";
+ qcom,board-id = <8 0x0>, <8 0x100>;
+};
+
+&blsp1_uart2 {
+ pinctrl-names = "default";
+ pinctrl-0 = <&uart2_console_active>;
+ status = "ok";
+};
+
diff --git a/arch/arm/boot/dts/qcom/sdxpoorwills-pinctrl.dtsi b/arch/arm/boot/dts/qcom/sdxpoorwills-pinctrl.dtsi
index ac02429..8181fa8 100644
--- a/arch/arm/boot/dts/qcom/sdxpoorwills-pinctrl.dtsi
+++ b/arch/arm/boot/dts/qcom/sdxpoorwills-pinctrl.dtsi
@@ -31,5 +31,893 @@
bias-disable;
};
};
+
+ /* I2C CONFIGURATION */
+ i2c_1 {
+ i2c_1_active: i2c_1_active {
+ mux {
+ pins = "gpio2", "gpio3";
+ function = "blsp_i2c1";
+ };
+
+ config {
+ pins = "gpio2", "gpio3";
+ drive-strength = <2>;
+ bias-disable;
+ };
+ };
+
+ i2c_1_sleep: i2c_1_sleep {
+ mux {
+ pins = "gpio2", "gpio3";
+ function = "blsp_i2c1";
+ };
+
+ config {
+ pins = "gpio2", "gpio3";
+ drive-strength = <2>;
+ bias-pull-up;
+ };
+ };
+ };
+
+ i2c_2 {
+ i2c_2_active: i2c_2_active {
+ mux {
+ pins = "gpio6", "gpio7";
+ function = "blsp_i2c2";
+ };
+
+ config {
+ pins = "gpio6", "gpio7";
+ drive-strength = <2>;
+ bias-disable;
+ };
+ };
+
+ i2c_2_sleep: i2c_2_sleep {
+ mux {
+ pins = "gpio6", "gpio7";
+ function = "blsp_i2c2";
+ };
+
+ config {
+ pins = "gpio6", "gpio7";
+ drive-strength = <2>;
+ bias-pull-up;
+ };
+ };
+ };
+
+ i2c_3 {
+ i2c_3_active: i2c_3_active {
+ mux {
+ pins = "gpio10", "gpio11";
+ function = "blsp_i2c3";
+ };
+
+ config {
+ pins = "gpio10", "gpio11";
+ drive-strength = <2>;
+ bias-disable;
+ };
+ };
+
+ i2c_3_sleep: i2c_3_sleep {
+ mux {
+ pins = "gpio10", "gpio11";
+ function = "blsp_i2c3";
+ };
+
+ config {
+ pins = "gpio10", "gpio11";
+ drive-strength = <2>;
+ bias-pull-up;
+ };
+ };
+ };
+
+ i2c_4 {
+ i2c_4_active: i2c_4_active {
+ mux {
+ pins = "gpio76", "gpio77";
+ function = "blsp_i2c4";
+ };
+
+ config {
+ pins = "gpio76", "gpio77";
+ drive-strength = <2>;
+ bias-disable;
+ };
+ };
+
+ i2c_4_sleep: i2c_4_sleep {
+ mux {
+ pins = "gpio76", "gpio77";
+ function = "blsp_i2c4";
+ };
+
+ config {
+ pins = "gpio76", "gpio77";
+ drive-strength = <2>;
+ bias-pull-up;
+ };
+ };
+ };
+
+ i2c_5 {
+ i2c_5_active: i2c_5_active {
+ mux {
+ pins = "gpio74", "gpio75";
+ function = "blsp_i2c1";
+ };
+
+ config {
+ pins = "gpio74", "gpio75";
+ drive-strength = <2>;
+ bias-disable;
+ };
+ };
+
+ i2c_5_sleep: i2c_5_sleep {
+ mux {
+ pins = "gpio74", "gpio75";
+ function = "blsp_i2c1";
+ };
+
+ config {
+ pins = "gpio74", "gpio75";
+ drive-strength = <2>;
+ bias-pull-up;
+ };
+ };
+ };
+
+ i2c_6 {
+ i2c_6_active: i2c_6_active {
+ mux {
+ pins = "gpio65", "gpio66";
+ function = "blsp_i2c2";
+ };
+
+ config {
+ pins = "gpio65", "gpio66";
+ drive-strength = <2>;
+ bias-disable;
+ };
+ };
+
+ i2c_6_sleep: i2c_6_sleep {
+ mux {
+ pins = "gpio65", "gpio66";
+ function = "blsp_i2c2";
+ };
+
+ config {
+ pins = "gpio65", "gpio66";
+ drive-strength = <2>;
+ bias-pull-up;
+ };
+ };
+ };
+
+ i2c_7 {
+ i2c_7_active: i2c_7_active {
+ mux {
+ pins = "gpio18", "gpio19";
+ function = "blsp_i2c4";
+ };
+
+ config {
+ pins = "gpio18", "gpio19";
+ drive-strength = <2>;
+ bias-disable;
+ };
+ };
+
+ i2c_7_sleep: i2c_7_sleep {
+ mux {
+ pins = "gpio18", "gpio19";
+ function = "blsp_i2c4";
+ };
+
+ config {
+ pins = "gpio18", "gpio19";
+ drive-strength = <2>;
+ bias-pull-up;
+ };
+ };
+ };
+
+ /* SPI CONFIGURATION */
+ spi_1 {
+ spi_1_active: spi_1_active {
+ mux {
+ pins = "gpio72", "gpio73",
+ "gpio74", "gpio75";
+ function = "blsp_spi1";
+ };
+
+ config {
+ pins = "gpio72", "gpio73",
+ "gpio74", "gpio75";
+ drive-strength = <6>;
+ bias-disable;
+ };
+ };
+
+ spi_1_sleep: spi_1_sleep {
+ mux {
+ pins = "gpio72", "gpio73",
+ "gpio74", "gpio75";
+ function = "blsp_spi1";
+ };
+
+ config {
+ pins = "gpio72", "gpio73",
+ "gpio74", "gpio75";
+ drive-strength = <6>;
+ bias-disable;
+ };
+ };
+ };
+
+ spi_2 {
+ spi_2_active: spi_2_active {
+ mux {
+ pins = "gpio4", "gpio5",
+ "gpio6", "gpio7";
+ function = "blsp_spi2";
+ };
+
+ config {
+ pins = "gpio4", "gpio5",
+ "gpio6", "gpio7";
+ drive-strength = <6>;
+ bias-disable;
+ };
+ };
+
+ spi_2_sleep: spi_2_sleep {
+ mux {
+ pins = "gpio4", "gpio5",
+ "gpio6", "gpio7";
+ function = "blsp_spi2";
+ };
+
+ config {
+ pins = "gpio4", "gpio5",
+ "gpio6", "gpio7";
+ drive-strength = <6>;
+ bias-disable;
+ };
+ };
+ };
+
+ spi_3 {
+ spi_3_active: spi_3_active {
+ mux {
+ pins = "gpio8", "gpio9",
+ "gpio10", "gpio11";
+ function = "blsp_spi3";
+ };
+
+ config {
+ pins = "gpio8", "gpio9",
+ "gpio10", "gpio11";
+ drive-strength = <6>;
+ bias-disable;
+ };
+ };
+
+ spi_3_sleep: spi_3_sleep {
+ mux {
+ pins = "gpio8", "gpio9",
+ "gpio10", "gpio11";
+ function = "blsp_spi3";
+ };
+
+ config {
+ pins = "gpio8", "gpio9",
+ "gpio10", "gpio11";
+ drive-strength = <6>;
+ bias-disable;
+ };
+ };
+ };
+
+ spi_4 {
+ spi_4_active: spi_4_active {
+ mux {
+ pins = "gpio16", "gpio17",
+ "gpio18", "gpio19";
+ function = "blsp_spi4";
+ };
+
+ config {
+ pins = "gpio16", "gpio17",
+ "gpio18", "gpio19";
+ drive-strength = <6>;
+ bias-disable;
+ };
+ };
+
+ spi_4_sleep: spi_4_sleep {
+ mux {
+ pins = "gpio16", "gpio17",
+ "gpio18", "gpio19";
+ function = "blsp_spi4";
+ };
+
+ config {
+ pins = "gpio16", "gpio17",
+ "gpio18", "gpio19";
+ drive-strength = <6>;
+ bias-disable;
+ };
+ };
+ };
+
+ /* HS UART CONFIGURATION */
+
+ blsp1_uart1a: blsp1_uart1a {
+ blsp1_uart1a_tx_active: blsp1_uart1a_tx_active {
+ mux {
+ pins = "gpio0";
+ function = "blsp_uart1";
+ };
+
+ config {
+ pins = "gpio0";
+ drive-strength = <2>;
+ bias-disable;
+ };
+ };
+
+ blsp1_uart1a_tx_sleep: blsp1_uart1a_tx_sleep {
+ mux {
+ pins = "gpio0";
+ function = "gpio";
+ };
+
+ config {
+ pins = "gpio0";
+ drive-strength = <2>;
+ bias-pull-up;
+ };
+ };
+
+ blsp1_uart1a_rxcts_active: blsp1_uart1a_rxcts_active {
+ mux {
+ pins = "gpio1", "gpio2";
+ function = "blsp_uart1";
+ };
+
+ config {
+ pins = "gpio1", "gpio2";
+ drive-strength = <2>;
+ bias-disable;
+ };
+ };
+
+ blsp1_uart1a_rxcts_sleep: blsp1_uart1a_rxcts_sleep {
+ mux {
+ pins = "gpio1", "gpio2";
+ function = "gpio";
+ };
+
+ config {
+ pins = "gpio1", "gpio2";
+ drive-strength = <2>;
+ bias-no-pull;
+ };
+ };
+
+ blsp1_uart1a_rfr_active: blsp1_uart1a_rfr_active {
+ mux {
+ pins = "gpio3";
+ function = "blsp_uart1";
+ };
+
+ config {
+ pins = "gpio3";
+ drive-strength = <2>;
+ bias-disable;
+ };
+ };
+
+ blsp1_uart1a_rfr_sleep: blsp1_uart1a_rfr_sleep {
+ mux {
+ pins = "gpio3";
+ function = "gpio";
+ };
+
+ config {
+ pins = "gpio3";
+ drive-strength = <2>;
+ bias-no-pull;
+ };
+ };
+ };
+
+ blsp1_uart1b: blsp1_uart1b {
+ blsp1_uart1b_tx_active: blsp1_uart1b_tx_active {
+ mux {
+ pins = "gpio20";
+ function = "blsp_uart1";
+ };
+
+ config {
+ pins = "gpio20";
+ drive-strength = <2>;
+ bias-disable;
+ };
+ };
+
+ blsp1_uart1b_tx_sleep: blsp1_uart1b_tx_sleep {
+ mux {
+ pins = "gpio20";
+ function = "gpio";
+ };
+
+ config {
+ pins = "gpio20";
+ drive-strength = <2>;
+ bias-pull-up;
+ };
+ };
+
+ blsp1_uart1b_rxcts_active: blsp1_uart1b_rxcts_active {
+ mux {
+ pins = "gpio21", "gpio22";
+ function = "blsp_uart1";
+ };
+
+ config {
+ pins = "gpio21", "gpio22";
+ drive-strength = <2>;
+ bias-disable;
+ };
+ };
+
+ blsp1_uart1b_rxcts_sleep: blsp1_uart1b_rxcts_sleep {
+ mux {
+ pins = "gpio21", "gpio22";
+ function = "gpio";
+ };
+
+ config {
+ pins = "gpio21", "gpio22";
+ drive-strength = <2>;
+ bias-no-pull;
+ };
+ };
+
+ blsp1_uart1b_rfr_active: blsp1_uart1b_rfr_active {
+ mux {
+ pins = "gpio23";
+ function = "blsp_uart1";
+ };
+
+ config {
+ pins = "gpio23";
+ drive-strength = <2>;
+ bias-disable;
+ };
+ };
+
+ blsp1_uart1b_rfr_sleep: blsp1_uart1b_rfr_sleep {
+ mux {
+ pins = "gpio23";
+ function = "gpio";
+ };
+
+ config {
+ pins = "gpio23";
+ drive-strength = <2>;
+ bias-no-pull;
+ };
+ };
+ };
+
+ blsp1_uart2a: blsp1_uart2a {
+ blsp1_uart2a_tx_active: blsp1_uart2a_tx_active {
+ mux {
+ pins = "gpio4";
+ function = "blsp_uart2";
+ };
+
+ config {
+ pins = "gpio4";
+ drive-strength = <2>;
+ bias-disable;
+ };
+ };
+
+ blsp1_uart2a_tx_sleep: blsp1_uart2a_tx_sleep {
+ mux {
+ pins = "gpio4";
+ function = "gpio";
+ };
+
+ config {
+ pins = "gpio4";
+ drive-strength = <2>;
+ bias-pull-up;
+ };
+ };
+
+ blsp1_uart2a_rxcts_active: blsp1_uart2a_rxcts_active {
+ mux {
+ pins = "gpio5", "gpio6";
+ function = "blsp_uart2";
+ };
+
+ config {
+ pins = "gpio5", "gpio6";
+ drive-strength = <2>;
+ bias-disable;
+ };
+ };
+
+ blsp1_uart2a_rxcts_sleep: blsp1_uart2a_rxcts_sleep {
+ mux {
+ pins = "gpio5", "gpio6";
+ function = "gpio";
+ };
+
+ config {
+ pins = "gpio1", "gpio2";
+ drive-strength = <2>;
+ bias-no-pull;
+ };
+ };
+
+ blsp1_uart2a_rfr_active: blsp1_uart2a_rfr_active {
+ mux {
+ pins = "gpio7";
+ function = "blsp_uart2";
+ };
+
+ config {
+ pins = "gpio7";
+ drive-strength = <2>;
+ bias-disable;
+ };
+ };
+
+ blsp1_uart2a_rfr_sleep: blsp1_uart2a_rfr_sleep {
+ mux {
+ pins = "gpio7";
+ function = "gpio";
+ };
+
+ config {
+ pins = "gpio7";
+ drive-strength = <2>;
+ bias-no-pull;
+ };
+ };
+ };
+
+ blsp1_uart2b: blsp1_uart2b {
+ blsp1_uart2b_tx_active: blsp1_uart2b_tx_active {
+ mux {
+ pins = "gpio63";
+ function = "blsp_uart2";
+ };
+
+ config {
+ pins = "gpio63";
+ drive-strength = <2>;
+ bias-disable;
+ };
+ };
+
+ blsp1_uart2b_tx_sleep: blsp1_uart2b_tx_sleep {
+ mux {
+ pins = "gpio63";
+ function = "gpio";
+ };
+
+ config {
+ pins = "gpio63";
+ drive-strength = <2>;
+ bias-pull-up;
+ };
+ };
+
+ blsp1_uart2b_rxcts_active: blsp1_uart2b_rxcts_active {
+ mux {
+ pins = "gpio64", "gpio65";
+ function = "blsp_uart2";
+ };
+
+ config {
+ pins = "gpio64", "gpio65";
+ drive-strength = <2>;
+ bias-disable;
+ };
+ };
+
+ blsp1_uart2b_rxcts_sleep: blsp1_uart2b_rxcts_sleep {
+ mux {
+ pins = "gpio64", "gpio65";
+ function = "gpio";
+ };
+
+ config {
+ pins = "gpio64", "gpio65";
+ drive-strength = <2>;
+ bias-no-pull;
+ };
+ };
+
+ blsp1_uart2b_rfr_active: blsp1_uart2b_rfr_active {
+ mux {
+ pins = "gpio66";
+ function = "blsp_uart2";
+ };
+
+ config {
+ pins = "gpio66";
+ drive-strength = <2>;
+ bias-disable;
+ };
+ };
+
+ blsp1_uart2b_rfr_sleep: blsp1_uart2b_rfr_sleep {
+ mux {
+ pins = "gpio66";
+ function = "gpio";
+ };
+
+ config {
+ pins = "gpio66";
+ drive-strength = <2>;
+ bias-no-pull;
+ };
+ };
+ };
+
+ blsp1_uart3: blsp1_uart3 {
+ blsp1_uart3_tx_active: blsp1_uart3_tx_active {
+ mux {
+ pins = "gpio8";
+ function = "blsp_uart3";
+ };
+
+ config {
+ pins = "gpio8";
+ drive-strength = <2>;
+ bias-disable;
+ };
+ };
+
+ blsp1_uart3_tx_sleep: blsp1_uart3_tx_sleep {
+ mux {
+ pins = "gpio8";
+ function = "gpio";
+ };
+
+ config {
+ pins = "gpio8";
+ drive-strength = <2>;
+ bias-pull-up;
+ };
+ };
+
+ blsp1_uart3_rxcts_active: blsp1_uart3_rxcts_active {
+ mux {
+ pins = "gpio9", "gpio10";
+ function = "blsp_uart3";
+ };
+
+ config {
+ pins = "gpio9", "gpio10";
+ drive-strength = <2>;
+ bias-disable;
+ };
+ };
+
+ blsp1_uart3_rxcts_sleep: blsp1_uart3_rxcts_sleep {
+ mux {
+ pins = "gpio9", "gpio10";
+ function = "gpio";
+ };
+
+ config {
+ pins = "gpio9", "gpio10";
+ drive-strength = <2>;
+ bias-no-pull;
+ };
+ };
+
+ blsp1_uart3_rfr_active: blsp1_uart3_rfr_active {
+ mux {
+ pins = "gpio11";
+ function = "blsp_uart3";
+ };
+
+ config {
+ pins = "gpio11";
+ drive-strength = <2>;
+ bias-disable;
+ };
+ };
+
+ blsp1_uart3_rfr_sleep: blsp1_uart3_rfr_sleep {
+ mux {
+ pins = "gpio11";
+ function = "gpio";
+ };
+
+ config {
+ pins = "gpio11";
+ drive-strength = <2>;
+ bias-no-pull;
+ };
+ };
+ };
+
+ blsp1_uart4a: blsp1_uart4a {
+ blsp1_uart4a_tx_active: blsp1_uart4a_tx_active {
+ mux {
+ pins = "gpio20";
+ function = "blsp_uart4";
+ };
+
+ config {
+ pins = "gpio20";
+ drive-strength = <2>;
+ bias-disable;
+ };
+ };
+
+ blsp1_uart4a_tx_sleep: blsp1_uart4a_tx_sleep {
+ mux {
+ pins = "gpio20";
+ function = "gpio";
+ };
+
+ config {
+ pins = "gpio20";
+ drive-strength = <2>;
+ bias-pull-up;
+ };
+ };
+
+ blsp1_uart4a_rxcts_active: blsp1_uart4a_rxcts_active {
+ mux {
+ pins = "gpio21", "gpio22";
+ function = "blsp_uart4";
+ };
+
+ config {
+ pins = "gpio21", "gpio22";
+ drive-strength = <2>;
+ bias-disable;
+ };
+ };
+
+ blsp1_uart4a_rxcts_sleep: blsp1_uart4a_rxcts_sleep {
+ mux {
+ pins = "gpio21", "gpio22";
+ function = "gpio";
+ };
+
+ config {
+ pins = "gpio21", "gpio22";
+ drive-strength = <2>;
+ bias-no-pull;
+ };
+ };
+
+ blsp1_uart4a_rfr_active: blsp1_uart4a_rfr_active {
+ mux {
+ pins = "gpio23";
+ function = "blsp_uart4";
+ };
+
+ config {
+ pins = "gpio23";
+ drive-strength = <2>;
+ bias-disable;
+ };
+ };
+
+ blsp1_uart4a_rfr_sleep: blsp1_uart4a_rfr_sleep {
+ mux {
+ pins = "gpio23";
+ function = "gpio";
+ };
+
+ config {
+ pins = "gpio23";
+ drive-strength = <2>;
+ bias-no-pull;
+ };
+ };
+ };
+
+ blsp1_uart4b: blsp1_uart4b {
+ blsp1_uart4b_tx_active: blsp1_uart4b_tx_active {
+ mux {
+ pins = "gpio16";
+ function = "blsp_uart4";
+ };
+
+ config {
+ pins = "gpio16";
+ drive-strength = <2>;
+ bias-disable;
+ };
+ };
+
+ blsp1_uart4b_tx_sleep: blsp1_uart4b_tx_sleep {
+ mux {
+ pins = "gpio16";
+ function = "gpio";
+ };
+
+ config {
+ pins = "gpio16";
+ drive-strength = <2>;
+ bias-pull-up;
+ };
+ };
+
+ blsp1_uart4b_rxcts_active: blsp1_uart4b_rxcts_active {
+ mux {
+ pins = "gpio17", "gpio18";
+ function = "blsp_uart4";
+ };
+
+ config {
+ pins = "gpio17", "gpio18";
+ drive-strength = <2>;
+ bias-disable;
+ };
+ };
+
+ blsp1_uart4b_rxcts_sleep: blsp1_uart4b_rxcts_sleep {
+ mux {
+ pins = "gpio17", "gpio18";
+ function = "gpio";
+ };
+
+ config {
+ pins = "gpio17", "gpio18";
+ drive-strength = <2>;
+ bias-no-pull;
+ };
+ };
+
+ blsp1_uart4b_rfr_active: blsp1_uart4b_rfr_active {
+ mux {
+ pins = "gpio19";
+ function = "blsp_uart4";
+ };
+
+ config {
+ pins = "gpio19";
+ drive-strength = <2>;
+ bias-disable;
+ };
+ };
+
+ blsp1_uart4b_rfr_sleep: blsp1_uart4b_rfr_sleep {
+ mux {
+ pins = "gpio19";
+ function = "gpio";
+ };
+
+ config {
+ pins = "gpio19";
+ drive-strength = <2>;
+ bias-no-pull;
+ };
+ };
+ };
};
};
diff --git a/arch/arm/boot/dts/qcom/sdxpoorwills.dtsi b/arch/arm/boot/dts/qcom/sdxpoorwills.dtsi
index d538efe..146fc9c 100644
--- a/arch/arm/boot/dts/qcom/sdxpoorwills.dtsi
+++ b/arch/arm/boot/dts/qcom/sdxpoorwills.dtsi
@@ -459,6 +459,8 @@
};
#include "pmxpoorwills.dtsi"
+#include "sdxpoorwills-blsp.dtsi"
#include "sdxpoorwills-regulator.dtsi"
#include "sdxpoorwills-smp2p.dtsi"
#include "sdxpoorwills-usb.dtsi"
+#include "sdxpoorwills-bus.dtsi"
diff --git a/arch/arm/configs/sdxpoorwills-perf_defconfig b/arch/arm/configs/sdxpoorwills-perf_defconfig
index 2765e20..877406f 100644
--- a/arch/arm/configs/sdxpoorwills-perf_defconfig
+++ b/arch/arm/configs/sdxpoorwills-perf_defconfig
@@ -28,6 +28,7 @@
CONFIG_PREEMPT=y
CONFIG_AEABI=y
CONFIG_CMA=y
+CONFIG_BUILD_ARM_APPENDED_DTB_IMAGE=y
CONFIG_CPU_FREQ=y
CONFIG_CPU_FREQ_GOV_INTERACTIVE=y
CONFIG_CPU_IDLE=y
@@ -191,6 +192,7 @@
# CONFIG_INPUT_KEYBOARD is not set
# CONFIG_INPUT_MOUSE is not set
CONFIG_INPUT_MISC=y
+CONFIG_INPUT_QPNP_POWER_ON=y
CONFIG_INPUT_UINPUT=y
CONFIG_INPUT_GPIO=m
CONFIG_SERIO_LIBPS2=y
@@ -200,20 +202,29 @@
CONFIG_HW_RANDOM=y
CONFIG_I2C=y
CONFIG_I2C_CHARDEV=y
+CONFIG_I2C_MSM_V2=y
CONFIG_SPI=y
CONFIG_SPI_QUP=y
CONFIG_SPI_SPIDEV=m
+CONFIG_SPMI=y
+CONFIG_SPMI_MSM_PMIC_ARB_DEBUG=y
CONFIG_PINCTRL_SDXPOORWILLS=y
+CONFIG_PINCTRL_QCOM_SPMI_PMIC=y
CONFIG_DEBUG_GPIO=y
CONFIG_GPIO_SYSFS=y
CONFIG_POWER_RESET=y
CONFIG_POWER_RESET_QCOM=y
CONFIG_QCOM_DLOAD_MODE=y
CONFIG_POWER_SUPPLY=y
+CONFIG_SMB138X_CHARGER=y
CONFIG_THERMAL=y
+CONFIG_THERMAL_QPNP=y
CONFIG_THERMAL_TSENS=y
+CONFIG_MFD_I2C_PMIC=y
+CONFIG_MFD_SPMI_PMIC=y
CONFIG_REGULATOR=y
CONFIG_REGULATOR_FIXED_VOLTAGE=y
+CONFIG_REGULATOR_QPNP=y
CONFIG_SOUND=y
CONFIG_SND=y
CONFIG_SND_SOC=y
@@ -265,7 +276,9 @@
CONFIG_MMC_SDHCI_PLTFM=y
CONFIG_MMC_SDHCI_MSM=y
CONFIG_RTC_CLASS=y
+CONFIG_RTC_DRV_QPNP=y
CONFIG_DMADEVICES=y
+CONFIG_QCOM_SPS_DMA=y
CONFIG_UIO=y
CONFIG_STAGING=y
CONFIG_GSI=y
@@ -276,6 +289,7 @@
CONFIG_IPA_UT=y
CONFIG_SPS=y
CONFIG_SPS_SUPPORT_NDP_BAM=y
+CONFIG_QPNP_REVID=y
CONFIG_USB_BAM=y
CONFIG_REMOTE_SPINLOCK_MSM=y
CONFIG_MAILBOX=y
@@ -287,14 +301,15 @@
CONFIG_MSM_GLINK_SMEM_NATIVE_XPRT=y
CONFIG_TRACER_PKT=y
CONFIG_MSM_SMP2P=y
-CONFIG_MSM_SMP2P_TEST=y
CONFIG_MSM_IPC_ROUTER_GLINK_XPRT=y
CONFIG_MSM_QMI_INTERFACE=y
CONFIG_MSM_GLINK_PKT=y
CONFIG_MSM_SUBSYSTEM_RESTART=y
CONFIG_MSM_PIL=y
CONFIG_MSM_PIL_SSR_GENERIC=y
+CONFIG_IIO=y
CONFIG_PWM=y
+CONFIG_PWM_QPNP=y
CONFIG_QCOM_SHOW_RESUME_IRQ=y
CONFIG_ANDROID=y
CONFIG_EXT3_FS=y
diff --git a/arch/arm/configs/sdxpoorwills_defconfig b/arch/arm/configs/sdxpoorwills_defconfig
index 1062175..d860595 100644
--- a/arch/arm/configs/sdxpoorwills_defconfig
+++ b/arch/arm/configs/sdxpoorwills_defconfig
@@ -30,6 +30,7 @@
CONFIG_PREEMPT=y
CONFIG_AEABI=y
CONFIG_CMA=y
+CONFIG_BUILD_ARM_APPENDED_DTB_IMAGE=y
CONFIG_CPU_FREQ=y
CONFIG_CPU_FREQ_GOV_INTERACTIVE=y
CONFIG_CPU_IDLE=y
@@ -183,6 +184,7 @@
# CONFIG_INPUT_KEYBOARD is not set
# CONFIG_INPUT_MOUSE is not set
CONFIG_INPUT_MISC=y
+CONFIG_INPUT_QPNP_POWER_ON=y
CONFIG_INPUT_UINPUT=y
CONFIG_INPUT_GPIO=m
CONFIG_SERIO_LIBPS2=y
@@ -199,18 +201,26 @@
CONFIG_SPI_QUP=y
CONFIG_SPI_SPIDEV=m
CONFIG_SLIMBUS=y
+CONFIG_SPMI=y
+CONFIG_SPMI_MSM_PMIC_ARB_DEBUG=y
CONFIG_PINCTRL_SDXPOORWILLS=y
+CONFIG_PINCTRL_QCOM_SPMI_PMIC=y
CONFIG_POWER_RESET=y
CONFIG_POWER_RESET_QCOM=y
CONFIG_QCOM_DLOAD_MODE=y
CONFIG_POWER_SUPPLY=y
+CONFIG_SMB138X_CHARGER=y
CONFIG_THERMAL=y
+CONFIG_THERMAL_QPNP=y
CONFIG_THERMAL_TSENS=y
+CONFIG_MFD_I2C_PMIC=y
+CONFIG_MFD_SPMI_PMIC=y
CONFIG_MFD_SYSCON=y
CONFIG_MSM_CDC_PINCTRL=y
CONFIG_MSM_CDC_SUPPLY=y
CONFIG_REGULATOR=y
CONFIG_REGULATOR_FIXED_VOLTAGE=y
+CONFIG_REGULATOR_QPNP=y
CONFIG_REGULATOR_STUB=y
CONFIG_FB=y
CONFIG_SOUND=y
@@ -263,6 +273,7 @@
CONFIG_MMC_SDHCI=y
CONFIG_MMC_SDHCI_PLTFM=y
CONFIG_RTC_CLASS=y
+CONFIG_RTC_DRV_QPNP=y
CONFIG_DMADEVICES=y
CONFIG_QCOM_SPS_DMA=y
CONFIG_UIO=y
@@ -275,6 +286,7 @@
CONFIG_IPA_UT=y
CONFIG_SPS=y
CONFIG_SPS_SUPPORT_NDP_BAM=y
+CONFIG_QPNP_REVID=y
CONFIG_REMOTE_SPINLOCK_MSM=y
CONFIG_MAILBOX=y
CONFIG_QCOM_SCM=y
@@ -285,7 +297,6 @@
CONFIG_MSM_GLINK_SMEM_NATIVE_XPRT=y
CONFIG_TRACER_PKT=y
CONFIG_MSM_SMP2P=y
-CONFIG_MSM_SMP2P_TEST=y
CONFIG_MSM_IPC_ROUTER_GLINK_XPRT=y
CONFIG_MSM_QMI_INTERFACE=y
CONFIG_MSM_GLINK_PKT=y
@@ -293,7 +304,9 @@
CONFIG_MSM_PIL=y
CONFIG_MSM_PIL_SSR_GENERIC=y
CONFIG_DEVFREQ_GOV_SIMPLE_ONDEMAND=y
+CONFIG_IIO=y
CONFIG_PWM=y
+CONFIG_PWM_QPNP=y
CONFIG_QCOM_SHOW_RESUME_IRQ=y
CONFIG_ANDROID=y
CONFIG_STM=y
diff --git a/arch/arm64/boot/dts/qcom/Makefile b/arch/arm64/boot/dts/qcom/Makefile
index aefdb52..aec9930 100644
--- a/arch/arm64/boot/dts/qcom/Makefile
+++ b/arch/arm64/boot/dts/qcom/Makefile
@@ -17,6 +17,12 @@
sdm845-v2-4k-panel-mtp-overlay.dtbo \
sdm845-v2-4k-panel-cdp-overlay.dtbo \
sdm845-v2-4k-panel-qrd-overlay.dtbo \
+ sdm845-v2.1-cdp-overlay.dtbo \
+ sdm845-v2.1-mtp-overlay.dtbo \
+ sdm845-v2.1-qrd-overlay.dtbo \
+ sdm845-v2.1-4k-panel-mtp-overlay.dtbo \
+ sdm845-v2.1-4k-panel-cdp-overlay.dtbo \
+ sdm845-v2.1-4k-panel-qrd-overlay.dtbo \
sda845-cdp-overlay.dtbo \
sda845-mtp-overlay.dtbo \
sda845-qrd-overlay.dtbo \
@@ -26,6 +32,7 @@
sda845-v2-cdp-overlay.dtbo \
sda845-v2-mtp-overlay.dtbo \
sda845-v2-qrd-overlay.dtbo \
+ sda845-v2-hdk-overlay.dtbo \
sda845-v2-4k-panel-mtp-overlay.dtbo \
sda845-v2-4k-panel-cdp-overlay.dtbo \
sda845-v2-4k-panel-qrd-overlay.dtbo
@@ -44,6 +51,12 @@
sdm845-v2-4k-panel-mtp-overlay.dtbo-base := sdm845-v2.dtb
sdm845-v2-4k-panel-cdp-overlay.dtbo-base := sdm845-v2.dtb
sdm845-v2-4k-panel-qrd-overlay.dtbo-base := sdm845-v2.dtb
+sdm845-v2.1-cdp-overlay.dtbo-base := sdm845-v2.1.dtb
+sdm845-v2.1-mtp-overlay.dtbo-base := sdm845-v2.1.dtb
+sdm845-v2.1-qrd-overlay.dtbo-base := sdm845-v2.1.dtb
+sdm845-v2.1-4k-panel-mtp-overlay.dtbo-base := sdm845-v2.1.dtb
+sdm845-v2.1-4k-panel-cdp-overlay.dtbo-base := sdm845-v2.1.dtb
+sdm845-v2.1-4k-panel-qrd-overlay.dtbo-base := sdm845-v2.1.dtb
sda845-cdp-overlay.dtbo-base := sda845.dtb
sda845-mtp-overlay.dtbo-base := sda845.dtb
sda845-qrd-overlay.dtbo-base := sda845.dtb
@@ -53,6 +66,7 @@
sda845-v2-cdp-overlay.dtbo-base := sda845-v2.dtb
sda845-v2-mtp-overlay.dtbo-base := sda845-v2.dtb
sda845-v2-qrd-overlay.dtbo-base := sda845-v2.dtb
+sda845-v2-hdk-overlay.dtbo-base := sda845-v2.dtb
sda845-v2-4k-panel-mtp-overlay.dtbo-base := sda845-v2.dtb
sda845-v2-4k-panel-cdp-overlay.dtbo-base := sda845-v2.dtb
sda845-v2-4k-panel-qrd-overlay.dtbo-base := sda845-v2.dtb
@@ -79,6 +93,8 @@
sdm670-cdp-overlay.dtbo \
sdm670-mtp-overlay.dtbo \
sdm670-rumi-overlay.dtbo \
+ sdm670-qrd-overlay.dtbo \
+ sdm670-qrd-sku2-overlay.dtbo \
sdm670-pm660a-cdp-overlay.dtbo \
sdm670-pm660a-mtp-overlay.dtbo \
sdm670-external-codec-cdp-overlay.dtbo \
@@ -104,6 +120,8 @@
sdm670-cdp-overlay.dtbo-base := sdm670.dtb
sdm670-mtp-overlay.dtbo-base := sdm670.dtb
sdm670-rumi-overlay.dtbo-base := sdm670.dtb
+sdm670-qrd-overlay.dtbo-base := sdm670.dtb
+sdm670-qrd-sku2-overlay.dtbo-base := sdm670.dtb
sdm670-pm660a-cdp-overlay.dtbo-base := sdm670.dtb
sdm670-pm660a-mtp-overlay.dtbo-base := sdm670.dtb
sdm670-external-codec-cdp-overlay.dtbo-base := sdm670.dtb
@@ -130,6 +148,8 @@
dtb-$(CONFIG_ARCH_SDM670) += sdm670-rumi.dtb \
sdm670-mtp.dtb \
sdm670-cdp.dtb \
+ sdm670-qrd.dtb \
+ sdm670-qrd-sku2.dtb \
sdm670-pm660a-mtp.dtb \
sdm670-pm660a-cdp.dtb \
sdm670-external-codec-cdp.dtb \
diff --git a/arch/arm64/boot/dts/qcom/dsi-panel-nt35597-dualmipi-wqxga-cmd.dtsi b/arch/arm64/boot/dts/qcom/dsi-panel-nt35597-dualmipi-wqxga-cmd.dtsi
new file mode 100644
index 0000000..78f933a
--- /dev/null
+++ b/arch/arm64/boot/dts/qcom/dsi-panel-nt35597-dualmipi-wqxga-cmd.dtsi
@@ -0,0 +1,112 @@
+/* Copyright (c) 2015-2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+&mdss_mdp {
+ dsi_dual_nt35597_cmd: qcom,mdss_dsi_nt35597_wqxga_cmd{
+ qcom,mdss-dsi-panel-name =
+ "Dual nt35597 cmd mode dsi panel without DSC";
+ qcom,mdss-dsi-panel-type = "dsi_cmd_mode";
+ qcom,mdss-dsi-virtual-channel-id = <0>;
+ qcom,mdss-dsi-stream = <0>;
+ qcom,mdss-dsi-bpp = <24>;
+ qcom,mdss-dsi-color-order = "rgb_swap_rgb";
+ qcom,mdss-dsi-underflow-color = <0xff>;
+ qcom,mdss-dsi-border-color = <0>;
+ qcom,mdss-dsi-traffic-mode = "non_burst_sync_event";
+ qcom,mdss-dsi-bllp-eof-power-mode;
+ qcom,mdss-dsi-bllp-power-mode;
+ qcom,mdss-dsi-lane-0-state;
+ qcom,mdss-dsi-lane-1-state;
+ qcom,mdss-dsi-lane-2-state;
+ qcom,mdss-dsi-lane-3-state;
+ qcom,adjust-timer-wakeup-ms = <1>;
+ qcom,mdss-dsi-reset-sequence = <1 10>, <0 10>, <1 10>;
+ qcom,mdss-pan-physical-width-dimension = <74>;
+ qcom,mdss-pan-physical-height-dimension = <131>;
+ qcom,mdss-dsi-t-clk-post = <0x0d>;
+ qcom,mdss-dsi-t-clk-pre = <0x2d>;
+ qcom,mdss-dsi-bl-max-level = <4095>;
+ qcom,mdss-dsi-dma-trigger = "trigger_sw";
+ qcom,mdss-dsi-mdp-trigger = "none";
+ qcom,mdss-dsi-te-pin-select = <1>;
+ qcom,mdss-dsi-wr-mem-start = <0x2c>;
+ qcom,mdss-dsi-wr-mem-continue = <0x3c>;
+ qcom,mdss-dsi-te-dcs-command = <1>;
+ qcom,mdss-dsi-te-check-enable;
+ qcom,mdss-dsi-te-using-te-pin;
+ qcom,ulps-enabled;
+
+ qcom,mdss-dsi-display-timings {
+ timing@0{
+ qcom,mdss-dsi-panel-width = <720>;
+ qcom,mdss-dsi-panel-height = <2560>;
+ qcom,mdss-dsi-h-front-porch = <100>;
+ qcom,mdss-dsi-h-back-porch = <32>;
+ qcom,mdss-dsi-h-pulse-width = <16>;
+ qcom,mdss-dsi-h-sync-skew = <0>;
+ qcom,mdss-dsi-v-back-porch = <7>;
+ qcom,mdss-dsi-v-front-porch = <8>;
+ qcom,mdss-dsi-v-pulse-width = <1>;
+ qcom,mdss-dsi-h-sync-pulse = <0>;
+ qcom,mdss-dsi-h-left-border = <0>;
+ qcom,mdss-dsi-h-right-border = <0>;
+ qcom,mdss-dsi-v-top-border = <0>;
+ qcom,mdss-dsi-v-bottom-border = <0>;
+ qcom,mdss-dsi-panel-framerate = <60>;
+ qcom,mdss-dsi-on-command =
+ [15 01 00 00 10 00 02 ff 10
+ 15 01 00 00 10 00 02 fb 01
+ 15 01 00 00 10 00 02 ba 03
+ 15 01 00 00 10 00 02 e5 01
+ 15 01 00 00 10 00 02 35 00
+ 15 01 00 00 10 00 02 bb 10
+ 15 01 00 00 10 00 02 b0 03
+ 15 01 00 00 10 00 02 ff e0
+ 15 01 00 00 10 00 02 fb 01
+ 15 01 00 00 10 00 02 6b 3d
+ 15 01 00 00 10 00 02 6c 3d
+ 15 01 00 00 10 00 02 6d 3d
+ 15 01 00 00 10 00 02 6e 3d
+ 15 01 00 00 10 00 02 6f 3d
+ 15 01 00 00 10 00 02 35 02
+ 15 01 00 00 10 00 02 36 72
+ 15 01 00 00 10 00 02 37 10
+ 15 01 00 00 10 00 02 08 c0
+ 15 01 00 00 10 00 02 ff 24
+ 15 01 00 00 10 00 02 fb 01
+ 15 01 00 00 10 00 02 c6 06
+ 15 01 00 00 10 00 02 ff 10
+ 05 01 00 00 a0 00 02 11 00
+ 05 01 00 00 a0 00 02 29 00];
+
+ qcom,mdss-dsi-off-command = [05 01 00 00 0a 00
+ 02 28 00 05 01 00 00 3c 00 02 10 00];
+
+ qcom,mdss-dsi-on-command-state = "dsi_lp_mode";
+ qcom,mdss-dsi-off-command-state = "dsi_hs_mode";
+ qcom,mdss-dsi-panel-timings = [cd 32 22 00 60
+ 64 26 34 29 03 04 00];
+
+ qcom,config-select =
+ <&dsi_dual_nt35597_cmd_config0>;
+
+ dsi_dual_nt35597_cmd_config0: config0 {
+ qcom,split-mode = "dualctl-split";
+ };
+
+ dsi_dual_nt35597_cmd_config1: config1 {
+ qcom,split-mode = "pingpong-split";
+ };
+ };
+ };
+ };
+};
diff --git a/arch/arm64/boot/dts/qcom/dsi-panel-nt35597-dualmipi-wqxga-video.dtsi b/arch/arm64/boot/dts/qcom/dsi-panel-nt35597-dualmipi-wqxga-video.dtsi
new file mode 100644
index 0000000..9cd5815
--- /dev/null
+++ b/arch/arm64/boot/dts/qcom/dsi-panel-nt35597-dualmipi-wqxga-video.dtsi
@@ -0,0 +1,111 @@
+/* Copyright (c) 2015-2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+&mdss_mdp {
+ dsi_dual_nt35597_video: qcom,mdss_dsi_nt35597_wqxga_video {
+ qcom,mdss-dsi-panel-name =
+ "Dual nt35597 video mode dsi panel without DSC";
+ qcom,mdss-dsi-panel-type = "dsi_video_mode";
+ qcom,mdss-dsi-virtual-channel-id = <0>;
+ qcom,mdss-dsi-stream = <0>;
+ qcom,mdss-dsi-bpp = <24>;
+ qcom,mdss-dsi-underflow-color = <0x3ff>;
+ qcom,mdss-dsi-border-color = <0>;
+ qcom,mdss-dsi-panel-hdr-enabled;
+ qcom,mdss-dsi-panel-hdr-color-primaries = <14500 15500 32000
+ 17000 15500 30000 8000 3000>;
+ qcom,mdss-dsi-panel-peak-brightness = <4200000>;
+ qcom,mdss-dsi-panel-blackness-level = <3230>;
+ qcom,mdss-dsi-traffic-mode = "non_burst_sync_event";
+ qcom,mdss-dsi-bllp-eof-power-mode;
+ qcom,mdss-dsi-bllp-power-mode;
+ qcom,mdss-dsi-lane-0-state;
+ qcom,mdss-dsi-lane-1-state;
+ qcom,mdss-dsi-lane-2-state;
+ qcom,mdss-dsi-lane-3-state;
+ qcom,mdss-dsi-dma-trigger = "trigger_sw";
+ qcom,mdss-dsi-mdp-trigger = "none";
+ qcom,mdss-dsi-reset-sequence = <1 10>, <0 10>, <1 10>;
+ qcom,mdss-pan-physical-width-dimension = <74>;
+ qcom,mdss-pan-physical-height-dimension = <131>;
+ qcom,cmd-sync-wait-broadcast;
+ qcom,mdss-dsi-t-clk-post = <0x0d>;
+ qcom,mdss-dsi-t-clk-pre = <0x2d>;
+ qcom,mdss-dsi-min-refresh-rate = <55>;
+ qcom,mdss-dsi-max-refresh-rate = <60>;
+ qcom,mdss-dsi-pan-enable-dynamic-fps;
+ qcom,mdss-dsi-pan-fps-update =
+ "dfps_immediate_porch_mode_vfp";
+
+ qcom,mdss-dsi-display-timings {
+ timing@0{
+ qcom,mdss-dsi-panel-width = <720>;
+ qcom,mdss-dsi-panel-height = <2560>;
+ qcom,mdss-dsi-h-front-porch = <100>;
+ qcom,mdss-dsi-h-back-porch = <32>;
+ qcom,mdss-dsi-h-pulse-width = <16>;
+ qcom,mdss-dsi-h-sync-skew = <0>;
+ qcom,mdss-dsi-v-back-porch = <7>;
+ qcom,mdss-dsi-v-front-porch = <8>;
+ qcom,mdss-dsi-v-pulse-width = <1>;
+ qcom,mdss-dsi-panel-framerate = <60>;
+ qcom,mdss-dsi-on-command =
+ [15 01 00 00 00 00 02 ff 10
+ 15 01 00 00 00 00 02 fb 01
+ 15 01 00 00 00 00 02 ba 03
+ 15 01 00 00 00 00 02 e5 01
+ 15 01 00 00 00 00 02 35 00
+ 15 01 00 00 00 00 02 bb 03
+ 15 01 00 00 00 00 02 b0 03
+ 39 01 00 00 00 00 06 3b 03
+ 08 08 64 9a
+ 15 01 00 00 00 00 02 ff e0
+ 15 01 00 00 00 00 02 fb 01
+ 15 01 00 00 00 00 02 6b 3d
+ 15 01 00 00 00 00 02 6c 3d
+ 15 01 00 00 00 00 02 6d 3d
+ 15 01 00 00 00 00 02 6e 3d
+ 15 01 00 00 00 00 02 6f 3d
+ 15 01 00 00 00 00 02 35 02
+ 15 01 00 00 00 00 02 36 72
+ 15 01 00 00 00 00 02 37 10
+ 15 01 00 00 00 00 02 08 c0
+ 15 01 00 00 00 00 02 ff 10
+ 05 01 00 00 78 00 02 11 00
+ 05 01 00 00 32 00 02 29 00];
+ qcom,mdss-dsi-off-command = [05 01 00 00
+ 0a 00 02 28 00 05 01 00 00 3c 00
+ 02 10 00];
+ qcom,mdss-dsi-on-command-state = "dsi_lp_mode";
+ qcom,mdss-dsi-off-command-state = "dsi_hs_mode";
+ qcom,mdss-dsi-h-sync-pulse = <0>;
+ qcom,mdss-dsi-panel-timings = [e2 36 24 00 66
+ 6a 28 38 2a 03 04 00];
+
+ qcom,config-select =
+ <&dsi_dual_nt35597_video_config0>;
+
+ dsi_dual_nt35597_video_config0:
+ config0 {
+ qcom,split-mode =
+ "dualctl-split";
+ };
+
+ dsi_dual_nt35597_video_config1:
+ config1 {
+ qcom,split-mode =
+ "pingpong-split";
+ };
+ };
+ };
+ };
+};
diff --git a/arch/arm64/boot/dts/qcom/dsi-panel-nt35695b-truly-fhd-cmd.dtsi b/arch/arm64/boot/dts/qcom/dsi-panel-nt35695b-truly-fhd-cmd.dtsi
new file mode 100644
index 0000000..5529ed1
--- /dev/null
+++ b/arch/arm64/boot/dts/qcom/dsi-panel-nt35695b-truly-fhd-cmd.dtsi
@@ -0,0 +1,196 @@
+/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+&mdss_mdp {
+ dsi_nt35695b_truly_fhd_cmd: qcom,mdss_dsi_nt35695b_truly_fhd_cmd {
+ qcom,mdss-dsi-panel-name =
+ "nt35695b truly fhd command mode dsi panel";
+ qcom,mdss-dsi-panel-type = "dsi_cmd_mode";
+ qcom,mdss-dsi-virtual-channel-id = <0>;
+ qcom,mdss-dsi-stream = <0>;
+ qcom,mdss-dsi-bpp = <24>;
+ qcom,mdss-dsi-underflow-color = <0xff>;
+ qcom,mdss-dsi-border-color = <0>;
+ qcom,mdss-dsi-traffic-mode = "burst_mode";
+ qcom,mdss-dsi-te-pin-select = <1>;
+ qcom,mdss-dsi-te-dcs-command = <1>;
+ qcom,mdss-dsi-te-check-enable;
+ qcom,mdss-dsi-te-using-te-pin;
+ qcom,mdss-dsi-bllp-eof-power-mode;
+ qcom,mdss-dsi-bllp-power-mode;
+ qcom,mdss-dsi-lane-0-state;
+ qcom,mdss-dsi-lane-1-state;
+ qcom,mdss-dsi-lane-2-state;
+ qcom,mdss-dsi-lane-3-state;
+ qcom,mdss-dsi-bl-pmic-control-type = "bl_ctrl_wled";
+ qcom,mdss-dsi-reset-sequence = <1 10>, <0 10>, <1 10>;
+ qcom,mdss-dsi-tx-eot-append;
+ qcom,mdss-dsi-post-init-delay = <1>;
+ qcom,ulps-enabled;
+ qcom,mdss-dsi-t-clk-post = <0x0d>;
+ qcom,mdss-dsi-t-clk-pre = <0x2f>;
+ qcom,mdss-dsi-bl-min-level = <1>;
+ qcom,mdss-dsi-bl-max-level = <4095>;
+ qcom,mdss-dsi-dma-trigger = "trigger_sw";
+ qcom,mdss-dsi-mdp-trigger = "none";
+
+ qcom,mdss-dsi-display-timings {
+ timing@0{
+ qcom,mdss-dsi-panel-width = <1080>;
+ qcom,mdss-dsi-panel-height = <1920>;
+ qcom,mdss-dsi-h-front-porch = <120>;
+ qcom,mdss-dsi-h-back-porch = <60>;
+ qcom,mdss-dsi-h-pulse-width = <12>;
+ qcom,mdss-dsi-h-sync-skew = <0>;
+ qcom,mdss-dsi-v-back-porch = <2>;
+ qcom,mdss-dsi-v-front-porch = <12>;
+ qcom,mdss-dsi-v-pulse-width = <2>;
+ qcom,mdss-dsi-h-sync-pulse = <0>;
+ qcom,mdss-dsi-h-left-border = <0>;
+ qcom,mdss-dsi-h-right-border = <0>;
+ qcom,mdss-dsi-v-top-border = <0>;
+ qcom,mdss-dsi-v-bottom-border = <0>;
+ qcom,mdss-dsi-panel-framerate = <60>;
+ qcom,mdss-dsi-on-command =
+ [15 01 00 00 10 00 02 ff 20
+ 15 01 00 00 00 00 02 fb 01
+ 15 01 00 00 00 00 02 00 01
+ 15 01 00 00 00 00 02 01 55
+ 15 01 00 00 00 00 02 02 45
+ 15 01 00 00 00 00 02 03 55
+ 15 01 00 00 00 00 02 05 50
+ 15 01 00 00 00 00 02 06 a8
+ 15 01 00 00 00 00 02 07 ad
+ 15 01 00 00 00 00 02 08 0c
+ 15 01 00 00 00 00 02 0b aa
+ 15 01 00 00 00 00 02 0c aa
+ 15 01 00 00 00 00 02 0e b0
+ 15 01 00 00 00 00 02 0f b3
+ 15 01 00 00 00 00 02 11 28
+ 15 01 00 00 00 00 02 12 10
+ 15 01 00 00 00 00 02 13 01
+ 15 01 00 00 00 00 02 14 4a
+ 15 01 00 00 00 00 02 15 12
+ 15 01 00 00 00 00 02 16 12
+ 15 01 00 00 00 00 02 30 01
+ 15 01 00 00 00 00 02 72 11
+ 15 01 00 00 00 00 02 58 82
+ 15 01 00 00 00 00 02 59 00
+ 15 01 00 00 00 00 02 5a 02
+ 15 01 00 00 00 00 02 5b 00
+ 15 01 00 00 00 00 02 5c 82
+ 15 01 00 00 00 00 02 5d 80
+ 15 01 00 00 00 00 02 5e 02
+ 15 01 00 00 00 00 02 5f 00
+ 15 01 00 00 00 00 02 ff 24
+ 15 01 00 00 00 00 02 fb 01
+ 15 01 00 00 00 00 02 00 01
+ 15 01 00 00 00 00 02 01 0b
+ 15 01 00 00 00 00 02 02 0c
+ 15 01 00 00 00 00 02 03 89
+ 15 01 00 00 00 00 02 04 8a
+ 15 01 00 00 00 00 02 05 0f
+ 15 01 00 00 00 00 02 06 10
+ 15 01 00 00 00 00 02 07 10
+ 15 01 00 00 00 00 02 08 1c
+ 15 01 00 00 00 00 02 09 00
+ 15 01 00 00 00 00 02 0a 00
+ 15 01 00 00 00 00 02 0b 00
+ 15 01 00 00 00 00 02 0c 00
+ 15 01 00 00 00 00 02 0d 13
+ 15 01 00 00 00 00 02 0e 15
+ 15 01 00 00 00 00 02 0f 17
+ 15 01 00 00 00 00 02 10 01
+ 15 01 00 00 00 00 02 11 0b
+ 15 01 00 00 00 00 02 12 0c
+ 15 01 00 00 00 00 02 13 89
+ 15 01 00 00 00 00 02 14 8a
+ 15 01 00 00 00 00 02 15 0f
+ 15 01 00 00 00 00 02 16 10
+ 15 01 00 00 00 00 02 17 10
+ 15 01 00 00 00 00 02 18 1c
+ 15 01 00 00 00 00 02 19 00
+ 15 01 00 00 00 00 02 1a 00
+ 15 01 00 00 00 00 02 1b 00
+ 15 01 00 00 00 00 02 1c 00
+ 15 01 00 00 00 00 02 1d 13
+ 15 01 00 00 00 00 02 1e 15
+ 15 01 00 00 00 00 02 1f 17
+ 15 01 00 00 00 00 02 20 00
+ 15 01 00 00 00 00 02 21 01
+ 15 01 00 00 00 00 02 22 00
+ 15 01 00 00 00 00 02 23 40
+ 15 01 00 00 00 00 02 24 40
+ 15 01 00 00 00 00 02 25 6d
+ 15 01 00 00 00 00 02 26 40
+ 15 01 00 00 00 00 02 27 40
+ 15 01 00 00 00 00 02 29 d8
+ 15 01 00 00 00 00 02 2a 2a
+ 15 01 00 00 00 00 02 4b 03
+ 15 01 00 00 00 00 02 4c 11
+ 15 01 00 00 00 00 02 4d 10
+ 15 01 00 00 00 00 02 4e 01
+ 15 01 00 00 00 00 02 4f 01
+ 15 01 00 00 00 00 02 50 10
+ 15 01 00 00 00 00 02 51 00
+ 15 01 00 00 00 00 02 52 80
+ 15 01 00 00 00 00 02 53 00
+ 15 01 00 00 00 00 02 54 07
+ 15 01 00 00 00 00 02 55 25
+ 15 01 00 00 00 00 02 56 00
+ 15 01 00 00 00 00 02 58 07
+ 15 01 00 00 00 00 02 5b 43
+ 15 01 00 00 00 00 02 5c 00
+ 15 01 00 00 00 00 02 5f 73
+ 15 01 00 00 00 00 02 60 73
+ 15 01 00 00 00 00 02 63 22
+ 15 01 00 00 00 00 02 64 00
+ 15 01 00 00 00 00 02 67 08
+ 15 01 00 00 00 00 02 68 04
+ 15 01 00 00 00 00 02 7a 80
+ 15 01 00 00 00 00 02 7b 91
+ 15 01 00 00 00 00 02 7c d8
+ 15 01 00 00 00 00 02 7d 60
+ 15 01 00 00 00 00 02 93 06
+ 15 01 00 00 00 00 02 94 06
+ 15 01 00 00 00 00 02 8a 00
+ 15 01 00 00 00 00 02 9b 0f
+ 15 01 00 00 00 00 02 b3 c0
+ 15 01 00 00 00 00 02 b4 00
+ 15 01 00 00 00 00 02 b5 00
+ 15 01 00 00 00 00 02 b6 21
+ 15 01 00 00 00 00 02 b7 22
+ 15 01 00 00 00 00 02 b8 07
+ 15 01 00 00 00 00 02 b9 07
+ 15 01 00 00 00 00 02 ba 22
+ 15 01 00 00 00 00 02 bd 20
+ 15 01 00 00 00 00 02 be 07
+ 15 01 00 00 00 00 02 bf 07
+ 15 01 00 00 00 00 02 c1 6d
+ 15 01 00 00 00 00 02 c4 24
+ 15 01 00 00 00 00 02 e3 00
+ 15 01 00 00 00 00 02 ec 00
+ 15 01 00 00 00 00 02 ff 10
+ 15 01 00 00 00 00 02 bb 10
+ 15 01 00 00 00 00 02 35 02
+ 05 01 00 00 78 00 02 11 00
+ 05 01 00 00 78 00 02 29 00];
+ qcom,mdss-dsi-off-command = [05 01 00 00 14
+ 00 02 28 00 05 01 00 00 78 00 02 10 00];
+ qcom,mdss-dsi-on-command-state = "dsi_lp_mode";
+ qcom,mdss-dsi-off-command-state = "dsi_lp_mode";
+ qcom,mdss-dsi-panel-timings = [e6 38 26 00 68 6e
+ 2a 3c 44 03 04 00];
+ };
+ };
+ };
+};
diff --git a/arch/arm64/boot/dts/qcom/dsi-panel-nt35695b-truly-fhd-video.dtsi b/arch/arm64/boot/dts/qcom/dsi-panel-nt35695b-truly-fhd-video.dtsi
new file mode 100644
index 0000000..720bb1d
--- /dev/null
+++ b/arch/arm64/boot/dts/qcom/dsi-panel-nt35695b-truly-fhd-video.dtsi
@@ -0,0 +1,191 @@
+/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+&mdss_mdp {
+ dsi_nt35695b_truly_fhd_video: qcom,mdss_dsi_nt35695b_truly_fhd_video {
+ qcom,mdss-dsi-panel-name =
+ "nt35695b truly fhd video mode dsi panel";
+ qcom,mdss-dsi-panel-type = "dsi_video_mode";
+ qcom,mdss-dsi-virtual-channel-id = <0>;
+ qcom,mdss-dsi-stream = <0>;
+ qcom,mdss-dsi-bpp = <24>;
+ qcom,mdss-dsi-underflow-color = <0xff>;
+ qcom,mdss-dsi-border-color = <0>;
+ qcom,mdss-dsi-traffic-mode = "burst_mode";
+ qcom,mdss-dsi-bllp-eof-power-mode;
+ qcom,mdss-dsi-bllp-power-mode;
+ qcom,mdss-dsi-lane-0-state;
+ qcom,mdss-dsi-lane-1-state;
+ qcom,mdss-dsi-lane-2-state;
+ qcom,mdss-dsi-lane-3-state;
+ qcom,mdss-dsi-bl-min-level = <1>;
+ qcom,mdss-dsi-bl-max-level = <4095>;
+ qcom,mdss-dsi-dma-trigger = "trigger_sw";
+ qcom,mdss-dsi-mdp-trigger = "none";
+ qcom,mdss-dsi-bl-pmic-control-type = "bl_ctrl_wled";
+ qcom,mdss-dsi-reset-sequence = <1 10>, <0 10>, <1 10>;
+ qcom,mdss-dsi-tx-eot-append;
+ qcom,mdss-dsi-post-init-delay = <1>;
+ qcom,mdss-dsi-t-clk-post = <0x0d>;
+ qcom,mdss-dsi-t-clk-pre = <0x2f>;
+
+ qcom,mdss-dsi-display-timings {
+ timing@0{
+ qcom,mdss-dsi-panel-width = <1080>;
+ qcom,mdss-dsi-panel-height = <1920>;
+ qcom,mdss-dsi-h-front-porch = <120>;
+ qcom,mdss-dsi-h-back-porch = <60>;
+ qcom,mdss-dsi-h-pulse-width = <12>;
+ qcom,mdss-dsi-h-sync-skew = <0>;
+ qcom,mdss-dsi-h-sync-pulse = <0>;
+ qcom,mdss-dsi-v-back-porch = <2>;
+ qcom,mdss-dsi-v-front-porch = <12>;
+ qcom,mdss-dsi-v-pulse-width = <2>;
+ qcom,mdss-dsi-panel-framerate = <60>;
+ qcom,mdss-dsi-h-left-border = <0>;
+ qcom,mdss-dsi-h-right-border = <0>;
+ qcom,mdss-dsi-v-top-border = <0>;
+ qcom,mdss-dsi-v-bottom-border = <0>;
+ qcom,mdss-dsi-on-command =
+ [15 01 00 00 10 00 02 ff 20
+ 15 01 00 00 00 00 02 fb 01
+ 15 01 00 00 00 00 02 00 01
+ 15 01 00 00 00 00 02 01 55
+ 15 01 00 00 00 00 02 02 45
+ 15 01 00 00 00 00 02 03 55
+ 15 01 00 00 00 00 02 05 50
+ 15 01 00 00 00 00 02 06 a8
+ 15 01 00 00 00 00 02 07 ad
+ 15 01 00 00 00 00 02 08 0c
+ 15 01 00 00 00 00 02 0b aa
+ 15 01 00 00 00 00 02 0c aa
+ 15 01 00 00 00 00 02 0e b0
+ 15 01 00 00 00 00 02 0f b3
+ 15 01 00 00 00 00 02 11 28
+ 15 01 00 00 00 00 02 12 10
+ 15 01 00 00 00 00 02 13 01
+ 15 01 00 00 00 00 02 14 4a
+ 15 01 00 00 00 00 02 15 12
+ 15 01 00 00 00 00 02 16 12
+ 15 01 00 00 00 00 02 30 01
+ 15 01 00 00 00 00 02 72 11
+ 15 01 00 00 00 00 02 58 82
+ 15 01 00 00 00 00 02 59 00
+ 15 01 00 00 00 00 02 5a 02
+ 15 01 00 00 00 00 02 5b 00
+ 15 01 00 00 00 00 02 5c 82
+ 15 01 00 00 00 00 02 5d 80
+ 15 01 00 00 00 00 02 5e 02
+ 15 01 00 00 00 00 02 5f 00
+ 15 01 00 00 00 00 02 ff 24
+ 15 01 00 00 00 00 02 fb 01
+ 15 01 00 00 00 00 02 00 01
+ 15 01 00 00 00 00 02 01 0b
+ 15 01 00 00 00 00 02 02 0c
+ 15 01 00 00 00 00 02 03 89
+ 15 01 00 00 00 00 02 04 8a
+ 15 01 00 00 00 00 02 05 0f
+ 15 01 00 00 00 00 02 06 10
+ 15 01 00 00 00 00 02 07 10
+ 15 01 00 00 00 00 02 08 1c
+ 15 01 00 00 00 00 02 09 00
+ 15 01 00 00 00 00 02 0a 00
+ 15 01 00 00 00 00 02 0b 00
+ 15 01 00 00 00 00 02 0c 00
+ 15 01 00 00 00 00 02 0d 13
+ 15 01 00 00 00 00 02 0e 15
+ 15 01 00 00 00 00 02 0f 17
+ 15 01 00 00 00 00 02 10 01
+ 15 01 00 00 00 00 02 11 0b
+ 15 01 00 00 00 00 02 12 0c
+ 15 01 00 00 00 00 02 13 89
+ 15 01 00 00 00 00 02 14 8a
+ 15 01 00 00 00 00 02 15 0f
+ 15 01 00 00 00 00 02 16 10
+ 15 01 00 00 00 00 02 17 10
+ 15 01 00 00 00 00 02 18 1c
+ 15 01 00 00 00 00 02 19 00
+ 15 01 00 00 00 00 02 1a 00
+ 15 01 00 00 00 00 02 1b 00
+ 15 01 00 00 00 00 02 1c 00
+ 15 01 00 00 00 00 02 1d 13
+ 15 01 00 00 00 00 02 1e 15
+ 15 01 00 00 00 00 02 1f 17
+ 15 01 00 00 00 00 02 20 00
+ 15 01 00 00 00 00 02 21 01
+ 15 01 00 00 00 00 02 22 00
+ 15 01 00 00 00 00 02 23 40
+ 15 01 00 00 00 00 02 24 40
+ 15 01 00 00 00 00 02 25 6d
+ 15 01 00 00 00 00 02 26 40
+ 15 01 00 00 00 00 02 27 40
+ 15 01 00 00 00 00 02 29 d8
+ 15 01 00 00 00 00 02 2a 2a
+ 15 01 00 00 00 00 02 4b 03
+ 15 01 00 00 00 00 02 4c 11
+ 15 01 00 00 00 00 02 4d 10
+ 15 01 00 00 00 00 02 4e 01
+ 15 01 00 00 00 00 02 4f 01
+ 15 01 00 00 00 00 02 50 10
+ 15 01 00 00 00 00 02 51 00
+ 15 01 00 00 00 00 02 52 80
+ 15 01 00 00 00 00 02 53 00
+ 15 01 00 00 00 00 02 54 07
+ 15 01 00 00 00 00 02 55 25
+ 15 01 00 00 00 00 02 56 00
+ 15 01 00 00 00 00 02 58 07
+ 15 01 00 00 00 00 02 5b 43
+ 15 01 00 00 00 00 02 5c 00
+ 15 01 00 00 00 00 02 5f 73
+ 15 01 00 00 00 00 02 60 73
+ 15 01 00 00 00 00 02 63 22
+ 15 01 00 00 00 00 02 64 00
+ 15 01 00 00 00 00 02 67 08
+ 15 01 00 00 00 00 02 68 04
+ 15 01 00 00 00 00 02 7a 80
+ 15 01 00 00 00 00 02 7b 91
+ 15 01 00 00 00 00 02 7c d8
+ 15 01 00 00 00 00 02 7d 60
+ 15 01 00 00 00 00 02 93 06
+ 15 01 00 00 00 00 02 94 06
+ 15 01 00 00 00 00 02 8a 00
+ 15 01 00 00 00 00 02 9b 0f
+ 15 01 00 00 00 00 02 b3 c0
+ 15 01 00 00 00 00 02 b4 00
+ 15 01 00 00 00 00 02 b5 00
+ 15 01 00 00 00 00 02 b6 21
+ 15 01 00 00 00 00 02 b7 22
+ 15 01 00 00 00 00 02 b8 07
+ 15 01 00 00 00 00 02 b9 07
+ 15 01 00 00 00 00 02 ba 22
+ 15 01 00 00 00 00 02 bd 20
+ 15 01 00 00 00 00 02 be 07
+ 15 01 00 00 00 00 02 bf 07
+ 15 01 00 00 00 00 02 c1 6d
+ 15 01 00 00 00 00 02 c4 24
+ 15 01 00 00 00 00 02 e3 00
+ 15 01 00 00 00 00 02 ec 00
+ 15 01 00 00 00 00 02 ff 10
+ 15 01 00 00 00 00 02 bb 03
+ 05 01 00 00 78 00 02 11 00
+ 05 01 00 00 78 00 02 29 00];
+ qcom,mdss-dsi-off-command = [05 01 00 00
+ 14 00 02 28 00 05 01 00 00 78 00
+ 02 10 00];
+ qcom,mdss-dsi-on-command-state = "dsi_lp_mode";
+ qcom,mdss-dsi-off-command-state = "dsi_lp_mode";
+ qcom,mdss-dsi-panel-timings = [e6 38 26 00
+ 68 6e 2a 3c 44 03 04 00];
+ };
+ };
+ };
+};
diff --git a/arch/arm64/boot/dts/qcom/dsi-panel-rm67195-amoled-fhd-cmd.dtsi b/arch/arm64/boot/dts/qcom/dsi-panel-rm67195-amoled-fhd-cmd.dtsi
new file mode 100644
index 0000000..a13486d
--- /dev/null
+++ b/arch/arm64/boot/dts/qcom/dsi-panel-rm67195-amoled-fhd-cmd.dtsi
@@ -0,0 +1,137 @@
+/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+&mdss_mdp {
+ dsi_rm67195_amoled_fhd_cmd: qcom,mdss_dsi_rm67195_amoled_fhd_cmd{
+ qcom,mdss-dsi-panel-name =
+ "rm67195 amoled fhd cmd mode dsi panel";
+ qcom,mdss-dsi-panel-type = "dsi_cmd_mode";
+ qcom,mdss-dsi-virtual-channel-id = <0>;
+ qcom,mdss-dsi-stream = <0>;
+ qcom,mdss-dsi-bpp = <24>;
+ qcom,mdss-dsi-underflow-color = <0xff>;
+ qcom,mdss-dsi-border-color = <0>;
+ qcom,mdss-dsi-color-order = "rgb_swap_rgb";
+ qcom,mdss-dsi-traffic-mode = "non_burst_sync_pulse";
+ qcom,mdss-dsi-lane-map = "lane_map_0123";
+ qcom,mdss-dsi-bllp-eof-power-mode;
+ qcom,mdss-dsi-bllp-power-mode;
+ qcom,mdss-dsi-lane-0-state;
+ qcom,mdss-dsi-lane-1-state;
+ qcom,mdss-dsi-lane-2-state;
+ qcom,mdss-dsi-lane-3-state;
+ qcom,mdss-pan-physical-width-dimension = <70>;
+ qcom,mdss-pan-physical-height-dimension = <125>;
+ qcom,mdss-dsi-reset-sequence = <1 20>, <0 20>, <1 20>;
+ qcom,mdss-dsi-panel-orientation = "180";
+ qcom,mdss-dsi-dma-trigger = "trigger_sw";
+ qcom,mdss-dsi-mdp-trigger = "none";
+ qcom,mdss-dsi-t-clk-post = <0x0d>;
+ qcom,mdss-dsi-t-clk-pre = <0x2f>;
+ qcom,mdss-dsi-wr-mem-start = <0x2c>;
+ qcom,mdss-dsi-wr-mem-continue = <0x3c>;
+ qcom,mdss-dsi-te-pin-select = <1>;
+ qcom,mdss-dsi-te-dcs-command = <1>;
+ qcom,mdss-dsi-te-check-enable;
+ qcom,mdss-dsi-te-using-te-pin;
+ qcom,mdss-dsi-lp11-init;
+ qcom,mdss-dsi-bl-pmic-control-type = "bl_ctrl_dcs";
+ qcom,mdss-dsi-bl-min-level = <1>;
+ qcom,mdss-dsi-bl-max-level = <255>;
+
+ qcom,mdss-dsi-display-timings {
+ timing@0{
+ qcom,mdss-dsi-panel-width = <1080>;
+ qcom,mdss-dsi-panel-height = <1920>;
+ qcom,mdss-dsi-h-front-porch = <120>;
+ qcom,mdss-dsi-h-back-porch = <60>;
+ qcom,mdss-dsi-h-pulse-width = <12>;
+ qcom,mdss-dsi-h-sync-skew = <0>;
+ qcom,mdss-dsi-v-back-porch = <12>;
+ qcom,mdss-dsi-v-front-porch = <8>;
+ qcom,mdss-dsi-v-pulse-width = <4>;
+ qcom,mdss-dsi-panel-framerate = <60>;
+ qcom,mdss-dsi-h-left-border = <0>;
+ qcom,mdss-dsi-h-right-border = <0>;
+ qcom,mdss-dsi-v-top-border = <0>;
+ qcom,mdss-dsi-v-bottom-border = <0>;
+ qcom,mdss-dsi-on-command = [
+ 15 01 00 00 00 00 02 fe 0d
+ 15 01 00 00 00 00 02 0b c0
+ 15 01 00 00 00 00 02 42 00
+ 15 01 00 00 00 00 02 18 08
+ 15 01 00 00 00 00 02 08 41
+ 15 01 00 00 00 00 02 46 02
+ 15 01 00 00 00 00 02 1e 04
+ 15 01 00 00 02 00 02 1e 00
+ 15 01 00 00 00 00 02 fe 0a
+ 15 01 00 00 00 00 02 24 17
+ 15 01 00 00 00 00 02 04 07
+ 15 01 00 00 00 00 02 1a 0c
+ 15 01 00 00 02 00 02 0f 44
+ 15 01 00 00 00 00 02 fe 0b
+ 15 01 00 00 00 00 02 28 40
+ 15 01 00 00 02 00 02 29 4f
+ 15 01 00 00 00 00 02 fe 04
+ 15 01 00 00 00 00 02 0a d8
+ 15 01 00 00 00 00 02 0c e6
+ 15 01 00 00 00 00 02 4e 20
+ 15 01 00 00 00 00 02 4f 1b
+ 15 01 00 00 00 00 02 50 2f
+ 15 01 00 00 02 00 02 51 08
+ 15 01 00 00 00 00 02 fe 09
+ 15 01 00 00 00 00 02 00 08
+ 15 01 00 00 00 00 02 01 08
+ 15 01 00 00 00 00 02 02 00
+ 15 01 00 00 00 00 02 03 00
+ 15 01 00 00 00 00 02 04 10
+ 15 01 00 00 00 00 02 05 00
+ 15 01 00 00 00 00 02 06 08
+ 15 01 00 00 00 00 02 07 08
+ 15 01 00 00 00 00 02 08 00
+ 15 01 00 00 00 00 02 12 24
+ 15 01 00 00 00 00 02 13 49
+ 15 01 00 00 00 00 02 14 92
+ 15 01 00 00 00 00 02 15 49
+ 15 01 00 00 00 00 02 16 92
+ 15 01 00 00 00 00 02 17 24
+ 15 01 00 00 00 00 02 18 24
+ 15 01 00 00 00 00 02 19 49
+ 15 01 00 00 00 00 02 1a 92
+ 15 01 00 00 00 00 02 1b 49
+ 15 01 00 00 00 00 02 1c 92
+ 15 01 00 00 00 00 02 1d 24
+ 15 01 00 00 00 00 02 1e 24
+ 15 01 00 00 00 00 02 1f 49
+ 15 01 00 00 00 00 02 20 92
+ 15 01 00 00 00 00 02 21 49
+ 15 01 00 00 00 00 02 22 92
+ 15 01 00 00 00 00 02 23 24
+ 15 01 00 00 00 00 02 9b 07
+ 15 01 00 00 02 00 02 9c a5
+ 15 01 00 00 00 00 02 fe 00
+ 15 01 00 00 00 00 02 c2 08
+ 15 01 00 00 02 00 02 35 00
+ 39 01 00 00 00 00 03 44 03 e8
+ 05 01 00 00 82 00 02 11 00
+ 05 01 00 00 14 00 02 29 00];
+
+ qcom,mdss-dsi-off-command = [05 01 00 00 14
+ 00 02 28 00 05 01 00 00 82 00 02 10 00];
+ qcom,mdss-dsi-on-command-state = "dsi_lp_mode";
+ qcom,mdss-dsi-off-command-state = "dsi_hs_mode";
+ qcom,mdss-dsi-h-sync-pulse = <0>;
+ };
+ };
+
+ };
+};
diff --git a/arch/arm64/boot/dts/qcom/fg-gen3-batterydata-mlp446579-3800mah.dtsi b/arch/arm64/boot/dts/qcom/fg-gen3-batterydata-mlp446579-3800mah.dtsi
new file mode 100644
index 0000000..ca43a45
--- /dev/null
+++ b/arch/arm64/boot/dts/qcom/fg-gen3-batterydata-mlp446579-3800mah.dtsi
@@ -0,0 +1,81 @@
+/* Copyright (c) 2017, 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.
+ */
+
+qcom,mlp446579_3800mah {
+ /* #MLP446579_3800mAh_averaged_MasterSlave_Oct9th2017*/
+ qcom,max-voltage-uv = <4400000>;
+ qcom,fg-cc-cv-threshold-mv = <4390>;
+ qcom,fastchg-current-ma = <3800>;
+ qcom,batt-id-kohm = <91>;
+ qcom,battery-beta = <4250>;
+ qcom,battery-type = "mlp446579_3800mah_averaged_masterslave_oct9th2017";
+ qcom,checksum = <0x3F0A>;
+ qcom,gui-version = "PMI8998GUI - 2.0.0.58";
+ qcom,fg-profile-data = [
+ 64 1F B8 05
+ 8F 0A 0F 06
+ A0 1D F3 FC
+ 41 13 70 1D
+ 1E 18 1E 23
+ 92 45 88 52
+ 52 00 00 00
+ 12 00 00 00
+ 00 00 04 00
+ FD D4 68 C3
+ 23 00 08 00
+ C3 D2 52 DC
+ 33 05 BB FB
+ 84 05 BE 13
+ 18 07 9E 2B
+ 21 06 09 20
+ 27 00 14 00
+ B7 1F A2 05
+ 8E 0A 1C 06
+ 7F 1D D9 ED
+ 20 12 FE 1D
+ F4 18 0A 23
+ 4C 45 49 53
+ 54 00 00 00
+ 0E 00 00 00
+ 00 00 F8 07
+ 03 00 27 B2
+ 1A 00 00 00
+ FE E2 52 DC
+ FE 05 79 FA
+ 56 05 D5 FA
+ 27 F5 5C 22
+ BC 33 CC FF
+ 07 10 00 00
+ F0 0E 66 46
+ 1A 00 40 00
+ A7 01 0A FA
+ FF 00 00 00
+ 00 00 00 00
+ 00 00 00 00
+ 00 00 00 00
+ 00 00 00 00
+ 00 00 00 00
+ 00 00 00 00
+ 00 00 00 00
+ 00 00 00 00
+ 00 00 00 00
+ 00 00 00 00
+ 00 00 00 00
+ 00 00 00 00
+ 00 00 00 00
+ 00 00 00 00
+ 00 00 00 00
+ 00 00 00 00
+ 00 00 00 00
+ 00 00 00 00
+ ];
+};
diff --git a/arch/arm64/boot/dts/qcom/msm-arm-smmu-sdm670.dtsi b/arch/arm64/boot/dts/qcom/msm-arm-smmu-sdm670.dtsi
index 8e5d854..25a332b 100644
--- a/arch/arm64/boot/dts/qcom/msm-arm-smmu-sdm670.dtsi
+++ b/arch/arm64/boot/dts/qcom/msm-arm-smmu-sdm670.dtsi
@@ -36,11 +36,9 @@
<GIC_SPI 371 IRQ_TYPE_EDGE_RISING>;
clock-names = "gcc_ddrss_gpu_axi_clk",
"gcc_gpu_memnoc_gfx_clk",
- "gpu_cc_ahb_clk",
"gpu_cc_cx_gmu_clk";
clocks = <&clock_gcc GCC_DDRSS_GPU_AXI_CLK>,
<&clock_gcc GCC_GPU_MEMNOC_GFX_CLK>,
- <&clock_gpucc GPU_CC_AHB_CLK>,
<&clock_gpucc GPU_CC_CX_GMU_CLK>;
attach-impl-defs =
<0x6000 0x2378>,
diff --git a/arch/arm64/boot/dts/qcom/msm-arm-smmu-sdm845.dtsi b/arch/arm64/boot/dts/qcom/msm-arm-smmu-sdm845.dtsi
index a48fba0..0a8fb4a 100644
--- a/arch/arm64/boot/dts/qcom/msm-arm-smmu-sdm845.dtsi
+++ b/arch/arm64/boot/dts/qcom/msm-arm-smmu-sdm845.dtsi
@@ -35,10 +35,8 @@
<GIC_SPI 369 IRQ_TYPE_EDGE_RISING>,
<GIC_SPI 370 IRQ_TYPE_EDGE_RISING>,
<GIC_SPI 371 IRQ_TYPE_EDGE_RISING>;
- clock-names = "gcc_gpu_memnoc_gfx_clk",
- "gpu_cc_ahb_clk";
- clocks = <&clock_gcc GCC_GPU_MEMNOC_GFX_CLK>,
- <&clock_gpucc GPU_CC_AHB_CLK>;
+ clock-names = "gcc_gpu_memnoc_gfx_clk";
+ clocks = <&clock_gcc GCC_GPU_MEMNOC_GFX_CLK>;
attach-impl-defs =
<0x6000 0x2378>,
<0x6060 0x1055>,
@@ -316,6 +314,7 @@
};
kgsl_iommu_test_device {
+ status = "disabled";
compatible = "iommu-debug-test";
/*
* 0x7 isn't a valid sid, but should pass the sid sanity check.
diff --git a/arch/arm64/boot/dts/qcom/msm-gdsc-sdm845.dtsi b/arch/arm64/boot/dts/qcom/msm-gdsc-sdm845.dtsi
index dcc646c93b..b43c876 100644
--- a/arch/arm64/boot/dts/qcom/msm-gdsc-sdm845.dtsi
+++ b/arch/arm64/boot/dts/qcom/msm-gdsc-sdm845.dtsi
@@ -181,6 +181,8 @@
qcom,poll-cfg-gdscr;
qcom,support-hw-trigger;
status = "disabled";
+ proxy-supply = <&mdss_core_gdsc>;
+ qcom,proxy-consumer-enable;
};
/* GDSCs in Graphics CC */
diff --git a/arch/arm64/boot/dts/qcom/pm660.dtsi b/arch/arm64/boot/dts/qcom/pm660.dtsi
index 1fdb3f6..df5a970 100644
--- a/arch/arm64/boot/dts/qcom/pm660.dtsi
+++ b/arch/arm64/boot/dts/qcom/pm660.dtsi
@@ -371,6 +371,16 @@
"bcl-crit-low-vbat";
#thermal-sensor-cells = <1>;
};
+
+ pm660_div_clk: qcom,clkdiv@5b00 {
+ compatible = "qcom,qpnp-clkdiv";
+ reg = <0x5b00 0x100>;
+ #clock-cells = <1>;
+ qcom,cxo-freq = <19200000>;
+ qcom,clkdiv-id = <1>;
+ qcom,clkdiv-init-freq = <19200000>;
+ status = "disabled";
+ };
};
pm660_1: qcom,pm660@1 {
diff --git a/arch/arm64/boot/dts/qcom/pm660l.dtsi b/arch/arm64/boot/dts/qcom/pm660l.dtsi
index aaf5d05..bcb5138 100644
--- a/arch/arm64/boot/dts/qcom/pm660l.dtsi
+++ b/arch/arm64/boot/dts/qcom/pm660l.dtsi
@@ -50,24 +50,20 @@
pm660l_gpios: pinctrl@c000 {
compatible = "qcom,spmi-gpio";
reg = <0xc000 0xc00>;
- interrupts = <0x2 0xc0 0 IRQ_TYPE_NONE>,
- <0x2 0xc2 0 IRQ_TYPE_NONE>,
+ interrupts = <0x2 0xc2 0 IRQ_TYPE_NONE>,
<0x2 0xc3 0 IRQ_TYPE_NONE>,
<0x2 0xc4 0 IRQ_TYPE_NONE>,
<0x2 0xc5 0 IRQ_TYPE_NONE>,
<0x2 0xc6 0 IRQ_TYPE_NONE>,
<0x2 0xc7 0 IRQ_TYPE_NONE>,
- <0x2 0xc8 0 IRQ_TYPE_NONE>,
- <0x2 0xca 0 IRQ_TYPE_NONE>,
- <0x2 0xcb 0 IRQ_TYPE_NONE>;
- interrupt-names = "pm660l_gpio1", "pm660l_gpio3",
- "pm660l_gpio4", "pm660l_gpio5",
- "pm660l_gpio6", "pm660l_gpio7",
- "pm660l_gpio8", "pm660l_gpio9",
- "pm660l_gpio11", "pm660l_gpio12";
+ <0x2 0xc8 0 IRQ_TYPE_NONE>;
+ interrupt-names = "pm660l_gpio3", "pm660l_gpio4",
+ "pm660l_gpio5", "pm660l_gpio6",
+ "pm660l_gpio7", "pm660l_gpio8",
+ "pm660l_gpio9";
gpio-controller;
#gpio-cells = <2>;
- qcom,gpios-disallowed = <2 10>;
+ qcom,gpios-disallowed = <1 2 10 11 12>;
};
};
diff --git a/arch/arm64/boot/dts/qcom/pm8950.dtsi b/arch/arm64/boot/dts/qcom/pm8950.dtsi
new file mode 100644
index 0000000..f47872a
--- /dev/null
+++ b/arch/arm64/boot/dts/qcom/pm8950.dtsi
@@ -0,0 +1,388 @@
+/* Copyright (c) 2015-2017, 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.
+ */
+
+&spmi_bus {
+ qcom,pm8950@0 {
+ compatible ="qcom,spmi-pmic";
+ reg = <0x0 SPMI_USID>;
+ #address-cells = <2>;
+ #size-cells = <0>;
+
+ pm8950_revid: qcom,revid@100 {
+ compatible = "qcom,qpnp-revid";
+ reg = <0x100 0x100>;
+ };
+
+ pm8950_temp_alarm: qcom,temp-alarm@2400 {
+ compatible = "qcom,qpnp-temp-alarm";
+ reg = <0x2400 0x100>;
+ interrupts = <0x0 0x24 0x0>;
+ label = "pm8950_tz";
+ qcom,channel-num = <8>;
+ qcom,threshold-set = <0>;
+ qcom,temp_alarm-vadc = <&pm8950_vadc>;
+ };
+
+ qcom,power-on@800 {
+ compatible = "qcom,qpnp-power-on";
+ reg = <0x800 0x100>;
+ interrupts = <0x0 0x8 0x0>,
+ <0x0 0x8 0x1>,
+ <0x0 0x8 0x4>,
+ <0x0 0x8 0x5>;
+ interrupt-names = "kpdpwr", "resin",
+ "resin-bark", "kpdpwr-resin-bark";
+ qcom,pon-dbc-delay = <15625>;
+ qcom,system-reset;
+
+ qcom,pon_1 {
+ qcom,pon-type = <0>;
+ qcom,pull-up = <1>;
+ linux,code = <116>;
+ };
+
+ qcom,pon_2 {
+ qcom,pon-type = <1>;
+ qcom,pull-up = <1>;
+ linux,code = <114>;
+ };
+ };
+
+ pm8950_coincell: qcom,coincell@2800 {
+ compatible = "qcom,qpnp-coincell";
+ reg = <0x2800 0x100>;
+ };
+
+ pm8950_mpps: mpps {
+ compatible = "qcom,qpnp-pin";
+ spmi-dev-container;
+ gpio-controller;
+ #gpio-cells = <2>;
+ #address-cells = <1>;
+ #size-cells = <1>;
+ label = "pm8950-mpp";
+
+ mpp@a000 {
+ reg = <0xa000 0x100>;
+ qcom,pin-num = <1>;
+ status = "disabled";
+ };
+
+ mpp@a100 {
+ /* MPP2 - PA_THERM config */
+ reg = <0xa100 0x100>;
+ qcom,pin-num = <2>;
+ qcom,mode = <4>; /* AIN input */
+ qcom,invert = <1>; /* Enable MPP */
+ qcom,ain-route = <1>; /* AMUX 6 */
+ qcom,master-en = <1>;
+ qcom,src-sel = <0>; /* Function constant */
+ };
+
+ mpp@a200 {
+ reg = <0xa200 0x100>;
+ qcom,pin-num = <3>;
+ status = "disabled";
+ };
+
+ mpp@a300 {
+ /* MPP4 - CASE_THERM config */
+ reg = <0xa300 0x100>;
+ qcom,pin-num = <4>;
+ qcom,mode = <4>; /* AIN input */
+ qcom,invert = <1>; /* Enable MPP */
+ qcom,ain-route = <3>; /* AMUX 8 */
+ qcom,master-en = <1>;
+ qcom,src-sel = <0>; /* Function constant */
+ };
+ };
+
+ pm8950_gpios: gpios {
+ spmi-dev-container;
+ compatible = "qcom,qpnp-pin";
+ gpio-controller;
+ #gpio-cells = <2>;
+ #address-cells = <1>;
+ #size-cells = <1>;
+ label = "pm8950-gpio";
+
+ gpio@c000 {
+ reg = <0xc000 0x100>;
+ qcom,pin-num = <1>;
+ status = "disabled";
+ };
+
+ gpio@c100 {
+ reg = <0xc100 0x100>;
+ qcom,pin-num = <2>;
+ status = "disabled";
+ };
+
+ gpio@c200 {
+ reg = <0xc200 0x100>;
+ qcom,pin-num = <3>;
+ status = "disabled";
+ };
+
+ gpio@c300 {
+ reg = <0xc300 0x100>;
+ qcom,pin-num = <4>;
+ status = "disabled";
+ };
+
+ gpio@c400 {
+ reg = <0xc400 0x100>;
+ qcom,pin-num = <5>;
+ status = "disabled";
+ };
+
+ gpio@c500 {
+ reg = <0xc500 0x100>;
+ qcom,pin-num = <6>;
+ status = "disabled";
+ };
+
+ gpio@c600 {
+ reg = <0xc600 0x100>;
+ qcom,pin-num = <7>;
+ status = "disabled";
+ };
+
+ gpio@c700 {
+ reg = <0xc700 0x100>;
+ qcom,pin-num = <8>;
+ status = "disabled";
+ };
+ };
+
+ pm8950_vadc: vadc@3100 {
+ compatible = "qcom,qpnp-vadc";
+ reg = <0x3100 0x100>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+ interrupts = <0x0 0x31 0x0>;
+ interrupt-names = "eoc-int-en-set";
+ qcom,adc-bit-resolution = <15>;
+ qcom,adc-vdd-reference = <1800>;
+ qcom,vadc-poll-eoc;
+ qcom,pmic-revid = <&pm8950_revid>;
+
+ chan@5 {
+ label = "vcoin";
+ reg = <5>;
+ qcom,decimation = <0>;
+ qcom,pre-div-channel-scaling = <1>;
+ qcom,calibration-type = "absolute";
+ qcom,scale-function = <0>;
+ qcom,hw-settle-time = <0>;
+ qcom,fast-avg-setup = <0>;
+ };
+
+ chan@7 {
+ label = "vph_pwr";
+ reg = <7>;
+ qcom,decimation = <0>;
+ qcom,pre-div-channel-scaling = <1>;
+ qcom,calibration-type = "absolute";
+ qcom,scale-function = <0>;
+ qcom,hw-settle-time = <0>;
+ qcom,fast-avg-setup = <0>;
+ };
+
+ chan@8 {
+ label = "die_temp";
+ reg = <8>;
+ qcom,decimation = <0>;
+ qcom,pre-div-channel-scaling = <0>;
+ qcom,calibration-type = "absolute";
+ qcom,scale-function = <3>;
+ qcom,hw-settle-time = <0>;
+ qcom,fast-avg-setup = <0>;
+ };
+
+ chan@9 {
+ label = "ref_625mv";
+ reg = <9>;
+ qcom,decimation = <0>;
+ qcom,pre-div-channel-scaling = <0>;
+ qcom,calibration-type = "absolute";
+ qcom,scale-function = <0>;
+ qcom,hw-settle-time = <0>;
+ qcom,fast-avg-setup = <0>;
+ };
+
+ chan@a {
+ label = "ref_1250v";
+ reg = <0xa>;
+ qcom,decimation = <0>;
+ qcom,pre-div-channel-scaling = <0>;
+ qcom,calibration-type = "absolute";
+ qcom,scale-function = <0>;
+ qcom,hw-settle-time = <0>;
+ qcom,fast-avg-setup = <0>;
+ };
+
+ chan@c {
+ label = "ref_buf_625mv";
+ reg = <0xc>;
+ qcom,decimation = <0>;
+ qcom,pre-div-channel-scaling = <0>;
+ qcom,calibration-type = "absolute";
+ qcom,scale-function = <0>;
+ qcom,hw-settle-time = <0>;
+ qcom,fast-avg-setup = <0>;
+ };
+
+ chan@36 {
+ label = "pa_therm0";
+ reg = <0x36>;
+ qcom,decimation = <0>;
+ qcom,pre-div-channel-scaling = <0>;
+ qcom,calibration-type = "ratiometric";
+ qcom,scale-function = <2>;
+ qcom,hw-settle-time = <2>;
+ qcom,fast-avg-setup = <0>;
+ };
+
+ chan@11 {
+ label = "pa_therm1";
+ reg = <0x11>;
+ qcom,decimation = <0>;
+ qcom,pre-div-channel-scaling = <0>;
+ qcom,calibration-type = "ratiometric";
+ qcom,scale-function = <2>;
+ qcom,hw-settle-time = <2>;
+ qcom,fast-avg-setup = <0>;
+ qcom,vadc-thermal-node;
+ };
+
+ chan@32 {
+ label = "xo_therm";
+ reg = <0x32>;
+ qcom,decimation = <0>;
+ qcom,pre-div-channel-scaling = <0>;
+ qcom,calibration-type = "ratiometric";
+ qcom,scale-function = <4>;
+ qcom,hw-settle-time = <2>;
+ qcom,fast-avg-setup = <0>;
+ qcom,vadc-thermal-node;
+ };
+
+ chan@3c {
+ label = "xo_therm_buf";
+ reg = <0x3c>;
+ qcom,decimation = <0>;
+ qcom,pre-div-channel-scaling = <0>;
+ qcom,calibration-type = "ratiometric";
+ qcom,scale-function = <4>;
+ qcom,hw-settle-time = <2>;
+ qcom,fast-avg-setup = <0>;
+ qcom,vadc-thermal-node;
+ };
+
+ chan@13 {
+ label = "case_therm";
+ reg = <0x13>;
+ qcom,decimation = <0>;
+ qcom,pre-div-channel-scaling = <0>;
+ qcom,calibration-type = "ratiometric";
+ qcom,scale-function = <2>;
+ qcom,hw-settle-time = <2>;
+ qcom,fast-avg-setup = <0>;
+ qcom,vadc-thermal-node;
+ };
+ };
+
+ pm8950_adc_tm: vadc@3400 {
+ compatible = "qcom,qpnp-adc-tm";
+ reg = <0x3400 0x100>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+ interrupts = <0x0 0x34 0x0>,
+ <0x0 0x34 0x3>,
+ <0x0 0x34 0x4>;
+ interrupt-names = "eoc-int-en-set",
+ "high-thr-en-set",
+ "low-thr-en-set";
+ qcom,adc-bit-resolution = <15>;
+ qcom,adc-vdd-reference = <1800>;
+ qcom,adc_tm-vadc = <&pm8950_vadc>;
+ qcom,pmic-revid = <&pm8950_revid>;
+
+ chan@36 {
+ label = "pa_therm0";
+ reg = <0x36>;
+ qcom,decimation = <0>;
+ qcom,pre-div-channel-scaling = <0>;
+ qcom,calibration-type = "ratiometric";
+ qcom,scale-function = <2>;
+ qcom,hw-settle-time = <2>;
+ qcom,fast-avg-setup = <0>;
+ qcom,btm-channel-number = <0x48>;
+ qcom,thermal-node;
+ };
+
+ chan@7 {
+ label = "vph_pwr";
+ reg = <0x7>;
+ qcom,decimation = <0>;
+ qcom,pre-div-channel-scaling = <1>;
+ qcom,calibration-type = "absolute";
+ qcom,scale-function = <0>;
+ qcom,hw-settle-time = <0>;
+ qcom,fast-avg-setup = <0>;
+ qcom,btm-channel-number = <0x68>;
+ };
+ };
+
+ pm8950_rtc: qcom,pm8950_rtc {
+ spmi-dev-container;
+ compatible = "qcom,qpnp-rtc";
+ #address-cells = <1>;
+ #size-cells = <1>;
+ qcom,qpnp-rtc-write = <0>;
+ qcom,qpnp-rtc-alarm-pwrup = <0>;
+
+ qcom,pm8950_rtc_rw@6000 {
+ reg = <0x6000 0x100>;
+ };
+
+ qcom,pm8950_rtc_alarm@6100 {
+ reg = <0x6100 0x100>;
+ interrupts = <0x0 0x61 0x1>;
+ };
+ };
+
+ qcom,leds@a300 {
+ compatible = "qcom,leds-qpnp";
+ reg = <0xa300 0x100>;
+ label = "mpp";
+ };
+ };
+
+ pm8950_1: qcom,pm8950@1 {
+ compatible ="qcom,spmi-pmic";
+ reg = <0x1 SPMI_USID>;
+ #address-cells = <2>;
+ #size-cells = <0>;
+
+ pm8950_pwm: pwm@bc00 {
+ status = "disabled";
+ compatible = "qcom,qpnp-pwm";
+ reg = <0xbc00 0x100>;
+ reg-names = "qpnp-lpg-channel-base";
+ qcom,channel-id = <0>;
+ qcom,supported-sizes = <6>, <9>;
+ #pwm-cells = <2>;
+ };
+ };
+};
diff --git a/arch/arm64/boot/dts/qcom/pmi8950.dtsi b/arch/arm64/boot/dts/qcom/pmi8950.dtsi
new file mode 100644
index 0000000..0ec1f0b
--- /dev/null
+++ b/arch/arm64/boot/dts/qcom/pmi8950.dtsi
@@ -0,0 +1,641 @@
+/* Copyright (c) 2015-2017, 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 <dt-bindings/msm/power-on.h>
+
+&spmi_bus {
+ qcom,pmi8950@2 {
+ compatible ="qcom,spmi-pmic";
+ reg = <0x2 SPMI_USID>;
+ #address-cells = <2>;
+ #size-cells = <0>;
+
+ pmi8950_revid: qcom,revid@100 {
+ compatible = "qcom,qpnp-revid";
+ reg = <0x100 0x100>;
+ };
+
+ qcom,power-on@800 {
+ compatible = "qcom,qpnp-power-on";
+ reg = <0x800 0x100>;
+ qcom,secondary-pon-reset;
+ qcom,hard-reset-poweroff-type =
+ <PON_POWER_OFF_SHUTDOWN>;
+
+ pon_perph_reg: qcom,pon_perph_reg {
+ regulator-name = "pon_spare_reg";
+ qcom,pon-spare-reg-addr = <0x8c>;
+ qcom,pon-spare-reg-bit = <1>;
+ };
+ };
+
+ pmi8950_vadc: vadc@3100 {
+ compatible = "qcom,qpnp-vadc";
+ reg = <0x3100 0x100>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+ interrupts = <0x2 0x31 0x0>;
+ interrupt-names = "eoc-int-en-set";
+ qcom,adc-bit-resolution = <15>;
+ qcom,adc-vdd-reference = <1800>;
+ qcom,vadc-poll-eoc;
+
+ chan@0 {
+ label = "usbin";
+ reg = <0>;
+ qcom,decimation = <0>;
+ qcom,pre-div-channel-scaling = <4>;
+ qcom,calibration-type = "absolute";
+ qcom,scale-function = <0>;
+ qcom,hw-settle-time = <0>;
+ qcom,fast-avg-setup = <0>;
+ };
+
+ chan@1 {
+ label = "dcin";
+ reg = <1>;
+ qcom,decimation = <0>;
+ qcom,pre-div-channel-scaling = <4>;
+ qcom,calibration-type = "absolute";
+ qcom,scale-function = <0>;
+ qcom,hw-settle-time = <0>;
+ qcom,fast-avg-setup = <0>;
+ };
+
+ chan@3 {
+ label = "vchg_sns";
+ reg = <3>;
+ qcom,decimation = <0>;
+ qcom,pre-div-channel-scaling = <1>;
+ qcom,calibration-type = "absolute";
+ qcom,scale-function = <0>;
+ qcom,hw-settle-time = <0>;
+ qcom,fast-avg-setup = <0>;
+ };
+
+ chan@9 {
+ label = "ref_625mv";
+ reg = <9>;
+ qcom,decimation = <0>;
+ qcom,pre-div-channel-scaling = <0>;
+ qcom,calibration-type = "absolute";
+ qcom,scale-function = <0>;
+ qcom,hw-settle-time = <0>;
+ qcom,fast-avg-setup = <0>;
+ };
+
+ chan@a {
+ label = "ref_1250v";
+ reg = <0xa>;
+ qcom,decimation = <0>;
+ qcom,pre-div-channel-scaling = <0>;
+ qcom,calibration-type = "absolute";
+ qcom,scale-function = <0>;
+ qcom,hw-settle-time = <0>;
+ qcom,fast-avg-setup = <0>;
+ };
+
+ chan@d {
+ label = "chg_temp";
+ reg = <0xd>;
+ qcom,decimation = <0>;
+ qcom,pre-div-channel-scaling = <0>;
+ qcom,calibration-type = "absolute";
+ qcom,scale-function = <16>;
+ qcom,hw-settle-time = <0>;
+ qcom,fast-avg-setup = <0>;
+ qcom,vadc-thermal-node;
+ };
+
+ chan@43 {
+ label = "usb_dp";
+ reg = <0x43>;
+ qcom,decimation = <0>;
+ qcom,pre-div-channel-scaling = <1>;
+ qcom,calibration-type = "absolute";
+ qcom,scale-function = <0>;
+ qcom,hw-settle-time = <0>;
+ qcom,fast-avg-setup = <0>;
+ };
+
+ chan@44 {
+ label = "usb_dm";
+ reg = <0x44>;
+ qcom,decimation = <0>;
+ qcom,pre-div-channel-scaling = <1>;
+ qcom,calibration-type = "absolute";
+ qcom,scale-function = <0>;
+ qcom,hw-settle-time = <0>;
+ qcom,fast-avg-setup = <0>;
+ };
+ };
+
+ pmi8950_gpios: gpios {
+ spmi-dev-container;
+ compatible = "qcom,qpnp-pin";
+ gpio-controller;
+ #gpio-cells = <2>;
+ #address-cells = <1>;
+ #size-cells = <1>;
+ label = "pmi8950-gpio";
+
+ gpio@c000 {
+ reg = <0xc000 0x100>;
+ qcom,pin-num = <1>;
+ status = "disabled";
+ };
+
+ gpio@c100 {
+ reg = <0xc100 0x100>;
+ qcom,pin-num = <2>;
+ status = "disabled";
+ };
+ };
+
+ pmi8950_mpps: mpps {
+ spmi-dev-container;
+ compatible = "qcom,qpnp-pin";
+ gpio-controller;
+ #gpio-cells = <2>;
+ #address-cells = <1>;
+ #size-cells = <1>;
+ label = "pmi8950-mpp";
+
+ mpp@a000 {
+ reg = <0xa000 0x100>;
+ qcom,pin-num = <1>;
+ status = "disabled";
+ };
+
+ mpp@a100 {
+ reg = <0xa100 0x100>;
+ qcom,pin-num = <2>;
+ status = "disabled";
+ };
+
+ mpp@a200 {
+ reg = <0xa200 0x100>;
+ qcom,pin-num = <3>;
+ status = "disabled";
+ };
+
+ mpp@a300 {
+ reg = <0xa300 0x100>;
+ qcom,pin-num = <4>;
+ status = "disabled";
+ };
+ };
+
+ pmi8950_charger: qcom,qpnp-smbcharger {
+ spmi-dev-container;
+ compatible = "qcom,qpnp-smbcharger";
+ #address-cells = <1>;
+ #size-cells = <1>;
+
+ qcom,iterm-ma = <100>;
+ qcom,float-voltage-mv = <4200>;
+ qcom,resume-delta-mv = <200>;
+ qcom,chg-inhibit-fg;
+ qcom,rparasitic-uohm = <100000>;
+ qcom,bms-psy-name = "bms";
+ qcom,thermal-mitigation = <1500 700 600 0>;
+ qcom,parallel-usb-min-current-ma = <1400>;
+ qcom,parallel-usb-9v-min-current-ma = <900>;
+ qcom,parallel-allowed-lowering-ma = <500>;
+ qcom,pmic-revid = <&pmi8950_revid>;
+ qcom,force-aicl-rerun;
+ qcom,aicl-rerun-period-s = <180>;
+ qcom,autoadjust-vfloat;
+
+ qcom,chgr@1000 {
+ reg = <0x1000 0x100>;
+ interrupts = <0x2 0x10 0x0>,
+ <0x2 0x10 0x1>,
+ <0x2 0x10 0x2>,
+ <0x2 0x10 0x3>,
+ <0x2 0x10 0x4>,
+ <0x2 0x10 0x5>,
+ <0x2 0x10 0x6>,
+ <0x2 0x10 0x7>;
+
+ interrupt-names = "chg-error",
+ "chg-inhibit",
+ "chg-prechg-sft",
+ "chg-complete-chg-sft",
+ "chg-p2f-thr",
+ "chg-rechg-thr",
+ "chg-taper-thr",
+ "chg-tcc-thr";
+ };
+
+ qcom,otg@1100 {
+ reg = <0x1100 0x100>;
+ interrupts = <0x2 0x11 0x0>,
+ <0x2 0x11 0x1>,
+ <0x2 0x11 0x3>;
+ interrupt-names = "otg-fail",
+ "otg-oc",
+ "usbid-change";
+ };
+
+ qcom,bat-if@1200 {
+ reg = <0x1200 0x100>;
+ interrupts = <0x2 0x12 0x0>,
+ <0x2 0x12 0x1>,
+ <0x2 0x12 0x2>,
+ <0x2 0x12 0x3>,
+ <0x2 0x12 0x4>,
+ <0x2 0x12 0x5>,
+ <0x2 0x12 0x6>,
+ <0x2 0x12 0x7>;
+
+ interrupt-names = "batt-hot",
+ "batt-warm",
+ "batt-cold",
+ "batt-cool",
+ "batt-ov",
+ "batt-low",
+ "batt-missing",
+ "batt-term-missing";
+ };
+
+ qcom,usb-chgpth@1300 {
+ reg = <0x1300 0x100>;
+ interrupts = <0x2 0x13 0x0>,
+ <0x2 0x13 0x1>,
+ <0x2 0x13 0x2>,
+ <0x2 0x13 0x5>;
+
+ interrupt-names = "usbin-uv",
+ "usbin-ov",
+ "usbin-src-det",
+ "aicl-done";
+ };
+
+ qcom,dc-chgpth@1400 {
+ reg = <0x1400 0x100>;
+ interrupts = <0x2 0x14 0x0>,
+ <0x2 0x14 0x1>;
+ interrupt-names = "dcin-uv",
+ "dcin-ov";
+ };
+
+ qcom,chgr-misc@1600 {
+ reg = <0x1600 0x100>;
+ interrupts = <0x2 0x16 0x0>,
+ <0x2 0x16 0x1>,
+ <0x2 0x16 0x2>,
+ <0x2 0x16 0x3>,
+ <0x2 0x16 0x4>,
+ <0x2 0x16 0x5>;
+
+ interrupt-names = "power-ok",
+ "temp-shutdown",
+ "wdog-timeout",
+ "flash-fail",
+ "otst2",
+ "otst3";
+ };
+
+ smbcharger_charger_otg: qcom,smbcharger-boost-otg {
+ regulator-name = "smbcharger_charger_otg";
+ };
+ };
+
+ pmi8950_fg: qcom,fg {
+ spmi-dev-container;
+ compatible = "qcom,qpnp-fg";
+ #address-cells = <1>;
+ #size-cells = <1>;
+ qcom,resume-soc = <95>;
+ status = "okay";
+ qcom,bcl-lm-threshold-ma = <127>;
+ qcom,bcl-mh-threshold-ma = <405>;
+ qcom,fg-iterm-ma = <150>;
+ qcom,fg-chg-iterm-ma = <100>;
+ qcom,pmic-revid = <&pmi8950_revid>;
+ qcom,fg-cutoff-voltage-mv = <3500>;
+ qcom,cycle-counter-en;
+ qcom,capacity-learning-on;
+
+ qcom,fg-soc@4000 {
+ status = "okay";
+ reg = <0x4000 0x100>;
+ interrupts = <0x2 0x40 0x0>,
+ <0x2 0x40 0x1>,
+ <0x2 0x40 0x2>,
+ <0x2 0x40 0x3>,
+ <0x2 0x40 0x4>,
+ <0x2 0x40 0x5>,
+ <0x2 0x40 0x6>;
+
+ interrupt-names = "high-soc",
+ "low-soc",
+ "full-soc",
+ "empty-soc",
+ "delta-soc",
+ "first-est-done",
+ "update-soc";
+ };
+
+ qcom,fg-batt@4100 {
+ reg = <0x4100 0x100>;
+ interrupts = <0x2 0x41 0x0>,
+ <0x2 0x41 0x1>,
+ <0x2 0x41 0x2>,
+ <0x2 0x41 0x3>,
+ <0x2 0x41 0x4>,
+ <0x2 0x41 0x5>,
+ <0x2 0x41 0x6>,
+ <0x2 0x41 0x7>;
+
+ interrupt-names = "soft-cold",
+ "soft-hot",
+ "vbatt-low",
+ "batt-ided",
+ "batt-id-req",
+ "batt-unknown",
+ "batt-missing",
+ "batt-match";
+ };
+
+ qcom,revid-tp-rev@1f1 {
+ reg = <0x1f1 0x1>;
+ };
+
+ qcom,fg-memif@4400 {
+ status = "okay";
+ reg = <0x4400 0x100>;
+ interrupts = <0x2 0x44 0x0>,
+ <0x2 0x44 0x2>;
+
+ interrupt-names = "mem-avail",
+ "data-rcvry-sug";
+ };
+ };
+
+ bcl@4200 {
+ compatible = "qcom,msm-bcl";
+ reg = <0x4200 0xFF 0x88E 0x2>;
+ reg-names = "fg_user_adc", "pon_spare";
+ interrupts = <0x2 0x42 0x0>,
+ <0x2 0x42 0x1>;
+ interrupt-names = "bcl-high-ibat-int",
+ "bcl-low-vbat-int";
+ qcom,vbat-scaling-factor = <39000>;
+ qcom,vbat-gain-numerator = <1>;
+ qcom,vbat-gain-denominator = <128>;
+ qcom,vbat-polling-delay-ms = <100>;
+ qcom,ibat-scaling-factor = <39000>;
+ qcom,ibat-gain-numerator = <1>;
+ qcom,ibat-gain-denominator = <128>;
+ qcom,ibat-offset-numerator = <1200>;
+ qcom,ibat-offset-denominator = <1>;
+ qcom,ibat-polling-delay-ms = <100>;
+ qcom,inhibit-derating-ua = <550000>;
+ };
+
+ qcom,leds@a100 {
+ compatible = "qcom,leds-qpnp";
+ reg = <0xa100 0x100>;
+ label = "mpp";
+ };
+ };
+
+ qcom,pmi8950@3 {
+ compatible ="qcom,spmi-pmic";
+ reg = <0x3 SPMI_USID>;
+ #address-cells = <1>;
+ #size-cells = <1>;
+
+ pmi8950_pwm: pwm@b000 {
+ status = "disabled";
+ compatible = "qcom,qpnp-pwm";
+ reg = <0xb000 0x100>;
+ reg-names = "qpnp-lpg-channel-base";
+ qcom,channel-id = <0>;
+ qcom,supported-sizes = <6>, <9>;
+ #pwm-cells = <2>;
+ };
+
+ labibb: qpnp-labibb-regulator {
+ status = "disabled";
+ spmi-dev-container;
+ compatible = "qcom,qpnp-labibb-regulator";
+ #address-cells = <1>;
+ #size-cells = <1>;
+ qcom,pmic-revid = <&pmi8950_revid>;
+
+ ibb_regulator: qcom,ibb@dc00 {
+ reg = <0xdc00 0x100>;
+ reg-names = "ibb_reg";
+ regulator-name = "ibb_reg";
+
+ regulator-min-microvolt = <4600000>;
+ regulator-max-microvolt = <6000000>;
+
+ qcom,qpnp-ibb-min-voltage = <1400000>;
+ qcom,qpnp-ibb-step-size = <100000>;
+ qcom,qpnp-ibb-slew-rate = <2000000>;
+ qcom,qpnp-ibb-use-default-voltage;
+ qcom,qpnp-ibb-init-voltage = <5500000>;
+ qcom,qpnp-ibb-init-amoled-voltage = <4000000>;
+ qcom,qpnp-ibb-init-lcd-voltage = <5500000>;
+
+ qcom,qpnp-ibb-soft-start = <1000>;
+
+ qcom,qpnp-ibb-discharge-resistor = <32>;
+ qcom,qpnp-ibb-lab-pwrup-delay = <8000>;
+ qcom,qpnp-ibb-lab-pwrdn-delay = <8000>;
+ qcom,qpnp-ibb-en-discharge;
+
+ qcom,qpnp-ibb-full-pull-down;
+ qcom,qpnp-ibb-pull-down-enable;
+ qcom,qpnp-ibb-switching-clock-frequency =
+ <1480>;
+ qcom,qpnp-ibb-limit-maximum-current = <1550>;
+ qcom,qpnp-ibb-debounce-cycle = <16>;
+ qcom,qpnp-ibb-limit-max-current-enable;
+ qcom,qpnp-ibb-ps-enable;
+ };
+
+ lab_regulator: qcom,lab@de00 {
+ reg = <0xde00 0x100>;
+ reg-names = "lab";
+ regulator-name = "lab_reg";
+
+ regulator-min-microvolt = <4600000>;
+ regulator-max-microvolt = <6000000>;
+
+ qcom,qpnp-lab-min-voltage = <4600000>;
+ qcom,qpnp-lab-step-size = <100000>;
+ qcom,qpnp-lab-slew-rate = <5000>;
+ qcom,qpnp-lab-use-default-voltage;
+ qcom,qpnp-lab-init-voltage = <5500000>;
+ qcom,qpnp-lab-init-amoled-voltage = <4600000>;
+ qcom,qpnp-lab-init-lcd-voltage = <5500000>;
+
+ qcom,qpnp-lab-soft-start = <800>;
+
+ qcom,qpnp-lab-full-pull-down;
+ qcom,qpnp-lab-pull-down-enable;
+ qcom,qpnp-lab-switching-clock-frequency =
+ <1600>;
+ qcom,qpnp-lab-limit-maximum-current = <800>;
+ qcom,qpnp-lab-limit-max-current-enable;
+ qcom,qpnp-lab-ps-threshold = <40>;
+ qcom,qpnp-lab-ps-enable;
+ qcom,qpnp-lab-nfet-size = <100>;
+ qcom,qpnp-lab-pfet-size = <100>;
+ qcom,qpnp-lab-max-precharge-time = <500>;
+ };
+
+ };
+
+ wled: qcom,leds@d800 {
+ compatible = "qcom,qpnp-wled";
+ reg = <0xd800 0x100>,
+ <0xd900 0x100>,
+ <0xdc00 0x100>,
+ <0xde00 0x100>;
+ reg-names = "qpnp-wled-ctrl-base",
+ "qpnp-wled-sink-base",
+ "qpnp-wled-ibb-base",
+ "qpnp-wled-lab-base";
+ interrupts = <0x3 0xd8 0x2>;
+ interrupt-names = "sc-irq";
+ status = "okay";
+ linux,name = "wled";
+ linux,default-trigger = "bkl-trigger";
+ qcom,fdbk-output = "auto";
+ qcom,vref-mv = <350>;
+ qcom,switch-freq-khz = <800>;
+ qcom,ovp-mv = <29500>;
+ qcom,ilim-ma = <980>;
+ qcom,boost-duty-ns = <26>;
+ qcom,mod-freq-khz = <9600>;
+ qcom,dim-mode = "hybrid";
+ qcom,dim-method = "linear";
+ qcom,hyb-thres = <625>;
+ qcom,sync-dly-us = <800>;
+ qcom,fs-curr-ua = <20000>;
+ qcom,led-strings-list = [00 01];
+ qcom,en-ext-pfet-sc-pro;
+ qcom,cons-sync-write-delay-us = <1000>;
+ };
+
+ flash_led: qcom,leds@d300 {
+ compatible = "qcom,qpnp-flash-led";
+ status = "okay";
+ reg = <0xd300 0x100>;
+ label = "flash";
+ qcom,headroom = <500>;
+ qcom,startup-dly = <128>;
+ qcom,clamp-curr = <200>;
+ qcom,pmic-charger-support;
+ qcom,self-check-enabled;
+ qcom,thermal-derate-enabled;
+ qcom,thermal-derate-threshold = <100>;
+ qcom,thermal-derate-rate = "5_PERCENT";
+ qcom,current-ramp-enabled;
+ qcom,ramp_up_step = "6P7_US";
+ qcom,ramp_dn_step = "6P7_US";
+ qcom,vph-pwr-droop-enabled;
+ qcom,vph-pwr-droop-threshold = <3000>;
+ qcom,vph-pwr-droop-debounce-time = <10>;
+ qcom,headroom-sense-ch0-enabled;
+ qcom,headroom-sense-ch1-enabled;
+ qcom,pmic-revid = <&pmi8950_revid>;
+
+ pmi8950_flash0: qcom,flash_0 {
+ label = "flash";
+ qcom,led-name = "led:flash_0";
+ qcom,default-led-trigger =
+ "flash0_trigger";
+ qcom,max-current = <1000>;
+ qcom,duration = <1280>;
+ qcom,id = <0>;
+ qcom,current = <625>;
+ };
+
+ pmi8950_flash1: qcom,flash_1 {
+ label = "flash";
+ qcom,led-name = "led:flash_1";
+ qcom,default-led-trigger =
+ "flash1_trigger";
+ qcom,max-current = <1000>;
+ qcom,duration = <1280>;
+ qcom,id = <1>;
+ qcom,current = <625>;
+ };
+
+ pmi8950_torch0: qcom,torch_0 {
+ label = "torch";
+ qcom,led-name = "led:torch_0";
+ qcom,default-led-trigger =
+ "torch0_trigger";
+ qcom,max-current = <200>;
+ qcom,id = <0>;
+ qcom,current = <120>;
+ };
+
+ pmi8950_torch1: qcom,torch_1 {
+ label = "torch";
+ qcom,led-name = "led:torch_1";
+ qcom,default-led-trigger =
+ "torch1_trigger";
+ qcom,max-current = <200>;
+ qcom,id = <1>;
+ qcom,current = <120>;
+ };
+
+ pmi8950_switch: qcom,switch {
+ label = "switch";
+ qcom,led-name = "led:switch";
+ qcom,default-led-trigger =
+ "switch_trigger";
+ qcom,max-current = <1000>;
+ qcom,duration = <1280>;
+ qcom,id = <2>;
+ qcom,current = <625>;
+ reg0 {
+ regulator-name = "pon_spare_reg";
+ };
+ };
+ };
+
+ pmi_haptic: qcom,haptic@c000 {
+ compatible = "qcom,qpnp-haptic";
+ reg = <0xc000 0x100>;
+ interrupts = <0x3 0xc0 0x0>,
+ <0x3 0xc0 0x1>;
+ interrupt-names = "sc-irq", "play-irq";
+ qcom,pmic-revid = <&pmi8950_revid>;
+ vcc_pon-supply = <&pon_perph_reg>;
+ qcom,play-mode = "direct";
+ qcom,wave-play-rate-us = <5263>;
+ qcom,actuator-type = "erm";
+ qcom,wave-shape = "square";
+ qcom,vmax-mv = <2000>;
+ qcom,ilim-ma = <800>;
+ qcom,sc-deb-cycles = <8>;
+ qcom,int-pwm-freq-khz = <505>;
+ qcom,en-brake;
+ qcom,brake-pattern = [03 03 00 00];
+ qcom,use-play-irq;
+ qcom,use-sc-irq;
+ qcom,wave-samples = [3e 3e 3e 3e 3e 3e 3e 3e];
+ qcom,wave-rep-cnt = <1>;
+ qcom,wave-samp-rep-cnt = <1>;
+ };
+ };
+};
diff --git a/arch/arm64/boot/dts/qcom/pmi8998.dtsi b/arch/arm64/boot/dts/qcom/pmi8998.dtsi
index a8b826a..8d8bd63 100644
--- a/arch/arm64/boot/dts/qcom/pmi8998.dtsi
+++ b/arch/arm64/boot/dts/qcom/pmi8998.dtsi
@@ -71,7 +71,7 @@
qcom,gpios-disallowed = <4 7 13>;
};
- qcom,qpnp-qnovo@1500 {
+ pmi8998_qnovo: qcom,qpnp-qnovo@1500 {
compatible = "qcom,qpnp-qnovo";
reg = <0x1500 0x100>;
interrupts = <0x2 0x15 0x0 IRQ_TYPE_NONE>;
@@ -328,7 +328,8 @@
reg = <0x4400 0x100>;
interrupts = <0x2 0x44 0x0 IRQ_TYPE_EDGE_BOTH>,
<0x2 0x44 0x1 IRQ_TYPE_EDGE_BOTH>,
- <0x2 0x44 0x2 IRQ_TYPE_EDGE_BOTH>;
+ <0x2 0x44 0x2
+ IRQ_TYPE_EDGE_RISING>;
interrupt-names = "ima-rdy",
"mem-xcp",
"dma-grant";
@@ -529,7 +530,7 @@
<1600>;
qcom,qpnp-lab-limit-maximum-current = <1600>;
qcom,qpnp-lab-limit-max-current-enable;
- qcom,qpnp-lab-ps-threshold = <20>;
+ qcom,qpnp-lab-ps-threshold = <70>;
qcom,qpnp-lab-ps-enable;
qcom,qpnp-lab-nfet-size = <100>;
qcom,qpnp-lab-pfet-size = <100>;
diff --git a/arch/arm64/boot/dts/qcom/sda845-v2-hdk-overlay.dts b/arch/arm64/boot/dts/qcom/sda845-v2-hdk-overlay.dts
new file mode 100644
index 0000000..f836f50
--- /dev/null
+++ b/arch/arm64/boot/dts/qcom/sda845-v2-hdk-overlay.dts
@@ -0,0 +1,29 @@
+/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+/dts-v1/;
+/plugin/;
+
+#include <dt-bindings/clock/qcom,gcc-sdm845.h>
+#include <dt-bindings/clock/qcom,camcc-sdm845.h>
+#include <dt-bindings/clock/qcom,dispcc-sdm845.h>
+#include <dt-bindings/clock/qcom,rpmh.h>
+#include <dt-bindings/interrupt-controller/arm-gic.h>
+
+#include "sda845-v2-hdk.dtsi"
+
+/ {
+ model = "Qualcomm Technologies, Inc. SDA845 v2 HDK";
+ compatible = "qcom,sda845-hdk", "qcom,sda845", "qcom,hdk";
+ qcom,msm-id = <341 0x20000>;
+ qcom,board-id = <0x01001F 0x00>;
+};
diff --git a/drivers/media/platform/msm/camera/cam_jpeg/jpeg_hw/include/cam_dma_hw_intf.h b/arch/arm64/boot/dts/qcom/sda845-v2-hdk.dts
similarity index 61%
copy from drivers/media/platform/msm/camera/cam_jpeg/jpeg_hw/include/cam_dma_hw_intf.h
copy to arch/arm64/boot/dts/qcom/sda845-v2-hdk.dts
index 71b21b9..17f8324 100644
--- a/drivers/media/platform/msm/camera/cam_jpeg/jpeg_hw/include/cam_dma_hw_intf.h
+++ b/arch/arm64/boot/dts/qcom/sda845-v2-hdk.dts
@@ -10,19 +10,14 @@
* GNU General Public License for more details.
*/
-#ifndef CAM_JPEG_DMA_HW_INTF_H
-#define CAM_JPEG_DMA_HW_INTF_H
-#include <uapi/media/cam_defs.h>
-#include <media/cam_jpeg.h>
+/dts-v1/;
-#include "cam_hw_mgr_intf.h"
-#include "cam_jpeg_hw_intf.h"
+#include "sda845-v2.dtsi"
+#include "sda845-v2-hdk.dtsi"
-enum cam_jpeg_dma_cmd_type {
- CAM_JPEG_DMA_CMD_CDM_CFG,
- CAM_JPEG_DMA_CMD_SET_IRQ_CB,
- CAM_JPEG_DMA_CMD_MAX,
+/ {
+ model = "Qualcomm Technologies, Inc. SDA845 HDK";
+ compatible = "qcom,sda845-hdk", "qcom,sda845", "qcom,hdk";
+ qcom,board-id = <0x01001F 0x00>;
};
-
-#endif /* CAM_JPEG_DMA_HW_INTF_H */
diff --git a/drivers/media/platform/msm/camera/cam_jpeg/jpeg_hw/include/cam_dma_hw_intf.h b/arch/arm64/boot/dts/qcom/sda845-v2-hdk.dtsi
similarity index 61%
copy from drivers/media/platform/msm/camera/cam_jpeg/jpeg_hw/include/cam_dma_hw_intf.h
copy to arch/arm64/boot/dts/qcom/sda845-v2-hdk.dtsi
index 71b21b9..53617dc 100644
--- a/drivers/media/platform/msm/camera/cam_jpeg/jpeg_hw/include/cam_dma_hw_intf.h
+++ b/arch/arm64/boot/dts/qcom/sda845-v2-hdk.dtsi
@@ -10,19 +10,11 @@
* GNU General Public License for more details.
*/
-#ifndef CAM_JPEG_DMA_HW_INTF_H
-#define CAM_JPEG_DMA_HW_INTF_H
+#include "sdm845-qvr.dtsi"
+#include "sdm845-camera-sensor-mtp.dtsi"
-#include <uapi/media/cam_defs.h>
-#include <media/cam_jpeg.h>
-
-#include "cam_hw_mgr_intf.h"
-#include "cam_jpeg_hw_intf.h"
-
-enum cam_jpeg_dma_cmd_type {
- CAM_JPEG_DMA_CMD_CDM_CFG,
- CAM_JPEG_DMA_CMD_SET_IRQ_CB,
- CAM_JPEG_DMA_CMD_MAX,
+&vendor {
+ qcom,battery-data {
+ #include "fg-gen3-batterydata-mlp356477-2800mah.dtsi"
+ };
};
-
-#endif /* CAM_JPEG_DMA_HW_INTF_H */
diff --git a/arch/arm64/boot/dts/qcom/sdm670-audio.dtsi b/arch/arm64/boot/dts/qcom/sdm670-audio.dtsi
index ef92cdd..bbf6683 100644
--- a/arch/arm64/boot/dts/qcom/sdm670-audio.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm670-audio.dtsi
@@ -12,8 +12,8 @@
*/
#include "msm-audio-lpass.dtsi"
-#include "sdm670-wsa881x.dtsi"
#include "sdm670-wcd.dtsi"
+#include "sdm670-wsa881x.dtsi"
#include "sdm670-lpi.dtsi"
#include <dt-bindings/clock/qcom,audio-ext-clk.h>
@@ -100,7 +100,7 @@
int_codec: sound {
status = "okay";
compatible = "qcom,sdm670-asoc-snd";
- qcom,model = "sdm670-snd-card-mtp";
+ qcom,model = "sdm670-mtp-snd-card";
qcom,wcn-btfm;
qcom,mi2s-audio-intf;
qcom,auxpcm-audio-intf;
diff --git a/arch/arm64/boot/dts/qcom/sdm670-bus.dtsi b/arch/arm64/boot/dts/qcom/sdm670-bus.dtsi
index 8749145..6f22264 100644
--- a/arch/arm64/boot/dts/qcom/sdm670-bus.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm670-bus.dtsi
@@ -713,10 +713,10 @@
&slv_qhs_ipa &slv_qhs_cpr_cx
&slv_qhs_a1_noc_cfg &slv_qhs_aoss
&slv_qhs_prng &slv_qhs_vsense_ctrl_cfg
- &slv_qhs_qupv3_south &slv_qhs_spdm
- &slv_qhs_crypto0_cfg &slv_qhs_pimem_cfg
- &slv_qhs_tlmm_north &slv_qhs_clk_ctl
- &slv_qhs_imem_cfg>;
+ &slv_qhs_emmc_cfg &slv_qhs_qupv3_south
+ &slv_qhs_spdm &slv_qhs_crypto0_cfg
+ &slv_qhs_pimem_cfg &slv_qhs_tlmm_north
+ &slv_qhs_clk_ctl &slv_qhs_imem_cfg>;
qcom,bus-dev = <&fab_config_noc>;
qcom,bcms = <&bcm_cn0>;
};
@@ -741,10 +741,11 @@
&slv_qhs_usb3_0 &slv_qhs_ipa
&slv_qhs_cpr_cx &slv_qhs_a1_noc_cfg
&slv_qhs_aoss &slv_qhs_prng
- &slv_qhs_vsense_ctrl_cfg &slv_qhs_qupv3_south
- &slv_qhs_spdm &slv_qhs_crypto0_cfg
- &slv_qhs_pimem_cfg &slv_qhs_tlmm_north
- &slv_qhs_clk_ctl &slv_qhs_imem_cfg>;
+ &slv_qhs_vsense_ctrl_cfg &slv_qhs_emmc_cfg
+ &slv_qhs_qupv3_south &slv_qhs_spdm
+ &slv_qhs_crypto0_cfg &slv_qhs_pimem_cfg
+ &slv_qhs_tlmm_north &slv_qhs_clk_ctl
+ &slv_qhs_imem_cfg>;
qcom,bus-dev = <&fab_config_noc>;
qcom,bcms = <&bcm_cn0>;
};
@@ -1379,6 +1380,15 @@
qcom,bcms = <&bcm_cn0>;
};
+ slv_qhs_emmc_cfg:slv-qhs-emmc-cfg {
+ cell-id = <MSM_BUS_SLAVE_EMMC_CFG>;
+ label = "slv-qhs-emmc-cfg";
+ qcom,buswidth = <4>;
+ qcom,agg-ports = <1>;
+ qcom,bus-dev = <&fab_config_noc>;
+ qcom,bcms = <&bcm_cn0>;
+ };
+
slv_qhs_glm:slv-qhs-glm {
cell-id = <MSM_BUS_SLAVE_GLM>;
label = "slv-qhs-glm";
diff --git a/arch/arm64/boot/dts/qcom/sdm670-cdp.dtsi b/arch/arm64/boot/dts/qcom/sdm670-cdp.dtsi
index fa06779..8e152b0 100644
--- a/arch/arm64/boot/dts/qcom/sdm670-cdp.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm670-cdp.dtsi
@@ -61,7 +61,7 @@
};
&qupv3_se6_4uart {
- status = "disabled";
+ status = "ok";
};
&sdhc_1 {
@@ -95,15 +95,6 @@
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 &sdc2_cd_off>;
- #address-cells = <0>;
- interrupt-parent = <&sdhc_2>;
- interrupts = <0 1 2>;
- #interrupt-cells = <1>;
- interrupt-map-mask = <0xffffffff>;
- interrupt-map = <0 &intc 0 0 204 0
- 1 &intc 0 0 222 0
- 2 &tlmm 96 0>;
- interrupt-names = "hc_irq", "pwr_irq", "status_irq";
cd-gpios = <&tlmm 96 0x1>;
status = "ok";
@@ -233,7 +224,54 @@
qcom,platform-reset-gpio = <&tlmm 75 0>;
};
-&dsi_dual_nt35597_truly_video {
+&dsi_dual_nt35597_video {
+ qcom,panel-supply-entries = <&dsi_panel_pwr_supply>;
+ qcom,mdss-dsi-bl-pmic-control-type = "bl_ctrl_wled";
+ qcom,mdss-dsi-bl-min-level = <1>;
+ qcom,mdss-dsi-bl-max-level = <4095>;
+ qcom,mdss-dsi-mode-sel-gpio-state = "dual_port";
+ qcom,panel-mode-gpio = <&tlmm 76 0>;
+ qcom,platform-reset-gpio = <&tlmm 75 0>;
+};
+
+&dsi_dual_nt35597_cmd {
+ qcom,panel-supply-entries = <&dsi_panel_pwr_supply>;
+ qcom,mdss-dsi-bl-pmic-control-type = "bl_ctrl_wled";
+ qcom,mdss-dsi-bl-min-level = <1>;
+ qcom,mdss-dsi-bl-max-level = <4095>;
+ qcom,mdss-dsi-mode-sel-gpio-state = "dual_port";
+ qcom,panel-mode-gpio = <&tlmm 76 0>;
+ qcom,platform-reset-gpio = <&tlmm 75 0>;
+ qcom,platform-te-gpio = <&tlmm 10 0>;
+};
+
+&dsi_rm67195_amoled_fhd_cmd {
+ qcom,mdss-dsi-bl-pmic-control-type = "bl_ctrl_wled";
+ qcom,mdss-dsi-bl-min-level = <1>;
+ qcom,mdss-dsi-bl-max-level = <4095>;
+ qcom,panel-supply-entries = <&dsi_panel_pwr_supply_labibb_amoled>;
+ qcom,platform-reset-gpio = <&tlmm 75 0>;
+ qcom,platform-te-gpio = <&tlmm 10 0>;
+};
+
+&dsi_nt35695b_truly_fhd_video {
+ qcom,mdss-dsi-bl-pmic-control-type = "bl_ctrl_wled";
+ qcom,mdss-dsi-bl-min-level = <1>;
+ qcom,mdss-dsi-bl-max-level = <4095>;
+ qcom,panel-supply-entries = <&dsi_panel_pwr_supply>;
+ qcom,platform-reset-gpio = <&tlmm 75 0>;
+};
+
+&dsi_nt35695b_truly_fhd_cmd {
+ qcom,mdss-dsi-bl-pmic-control-type = "bl_ctrl_wled";
+ qcom,mdss-dsi-bl-min-level = <1>;
+ qcom,mdss-dsi-bl-max-level = <4095>;
+ qcom,panel-supply-entries = <&dsi_panel_pwr_supply>;
+ qcom,platform-reset-gpio = <&tlmm 75 0>;
+ qcom,platform-te-gpio = <&tlmm 10 0>;
+};
+
+&dsi_dual_nt35597_truly_video_display {
qcom,dsi-display-active;
};
diff --git a/arch/arm64/boot/dts/qcom/sdm670-coresight.dtsi b/arch/arm64/boot/dts/qcom/sdm670-coresight.dtsi
index 8b79d8b..d054164 100644
--- a/arch/arm64/boot/dts/qcom/sdm670-coresight.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm670-coresight.dtsi
@@ -71,6 +71,197 @@
};
};
+ replicator_swao: replicator@6b0a000 {
+ compatible = "arm,primecell";
+ arm,primecell-periphid = <0x0003b909>;
+
+ reg = <0x6b0a000 0x1000>;
+ reg-names = "replicator-base";
+
+ coresight-name = "coresight-replicator-swao";
+
+ clocks = <&clock_aop QDSS_CLK>;
+ clock-names = "apb_pclk";
+
+ ports {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ port@0 {
+ reg = <0>;
+ replicator_swao_in_tmc_etf_swao: endpoint {
+ slave-mode;
+ remote-endpoint =
+ <&tmc_etf_swao_out_replicator>;
+ };
+ };
+
+ port@1 {
+ reg = <0>;
+ replicator_swao_out_funnel_in2: endpoint {
+ remote-endpoint =
+ <&funnel_in2_in_replicator_swao>;
+ };
+ };
+ };
+ };
+
+ tmc_etf_swao: tmc@6b09000 {
+ compatible = "arm,primecell";
+ arm,primecell-periphid = <0x0003b961>;
+
+ reg = <0x6b09000 0x1000>;
+ reg-names = "tmc-base";
+
+ coresight-name = "coresight-tmc-etf-swao";
+
+ clocks = <&clock_aop QDSS_CLK>;
+ clock-names = "apb_pclk";
+
+ ports {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ port@0 {
+ reg = <0>;
+ tmc_etf_swao_out_replicator: endpoint {
+ remote-endpoint=
+ <&replicator_swao_in_tmc_etf_swao>;
+ };
+ };
+
+ port@1 {
+ reg = <0>;
+ tmc_etf_swao_in_funnel_swao: endpoint {
+ slave-mode;
+ remote-endpoint=
+ <&funnel_swao_out_tmc_etf_swao>;
+ };
+ };
+ };
+ };
+
+ funnel_swao:funnel@0x6b08000 {
+ compatible = "arm,primecell";
+ arm,primecell-periphid = <0x0003b908>;
+
+ reg = <0x6b08000 0x1000>;
+ reg-names = "funnel-base";
+
+ coresight-name = "coresight-funnel-swao";
+
+ clocks = <&clock_aop QDSS_CLK>;
+ clock-names = "apb_pclk";
+
+ ports {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ port@0 {
+ reg = <0>;
+ funnel_swao_out_tmc_etf_swao: endpoint {
+ remote-endpoint =
+ <&tmc_etf_swao_in_funnel_swao>;
+ };
+ };
+
+ port@1 {
+ reg = <7>;
+ funnel_swao_in_tpda_swao: endpoint {
+ slave-mode;
+ remote-endpoint=
+ <&tpda_swao_out_funnel_swao>;
+ };
+ };
+ };
+ };
+
+ tpda_swao: tpda@6b01000 {
+ compatible = "arm,primecell";
+ arm,primecell-periphid = <0x0003b969>;
+ reg = <0x6b01000 0x1000>;
+ reg-names = "tpda-base";
+
+ coresight-name = "coresight-tpda-swao";
+
+ qcom,tpda-atid = <71>;
+ qcom,dsb-elem-size = <1 32>;
+ qcom,cmb-elem-size = <0 64>;
+
+ clocks = <&clock_aop QDSS_CLK>;
+ clock-names = "apb_pclk";
+
+ ports {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ port@0 {
+ reg = <0>;
+ tpda_swao_out_funnel_swao: endpoint {
+ remote-endpoint =
+ <&funnel_swao_in_tpda_swao>;
+ };
+ };
+
+ port@1 {
+ reg = <0>;
+ tpda_swao_in_tpdm_swao0: endpoint {
+ slave-mode;
+ remote-endpoint =
+ <&tpdm_swao0_out_tpda_swao>;
+ };
+ };
+
+ port@2 {
+ reg = <1>;
+ tpda_swao_in_tpdm_swao1: endpoint {
+ slave-mode;
+ remote-endpoint =
+ <&tpdm_swao1_out_tpda_swao>;
+ };
+ };
+ };
+ };
+
+ tpdm_swao0: tpdm@6b02000 {
+ compatible = "arm,primecell";
+ arm,primecell-periphid = <0x0003b968>;
+
+ reg = <0x6b02000 0x1000>;
+ reg-names = "tpdm-base";
+
+ coresight-name = "coresight-tpdm-swao-0";
+
+ clocks = <&clock_aop QDSS_CLK>;
+ clock-names = "apb_pclk";
+
+ port {
+ tpdm_swao0_out_tpda_swao: endpoint {
+ remote-endpoint = <&tpda_swao_in_tpdm_swao0>;
+ };
+ };
+ };
+
+ tpdm_swao1: tpdm@6b03000 {
+ compatible = "arm,primecell";
+ arm,primecell-periphid = <0x0003b968>;
+ reg = <0x6b03000 0x1000>;
+ reg-names = "tpdm-base";
+
+ coresight-name="coresight-tpdm-swao-1";
+
+ clocks = <&clock_aop QDSS_CLK>;
+ clock-names = "apb_pclk";
+
+ qcom,msr-fix-req;
+
+ port {
+ tpdm_swao1_out_tpda_swao: endpoint {
+ remote-endpoint = <&tpda_swao_in_tpdm_swao1>;
+ };
+ };
+ };
+
tmc_etf: tmc@6047000 {
compatible = "arm,primecell";
arm,primecell-periphid = <0x0003b961>;
@@ -277,6 +468,15 @@
};
port@1 {
+ reg = <0>;
+ funnel_in1_in_audio_etm0: endpoint {
+ slave-mode;
+ remote-endpoint =
+ <&audio_etm0_out_funnel_in1>;
+ };
+ };
+
+ port@2 {
reg = <3>;
funnel_in1_in_funnel_modem: endpoint {
slave-mode;
@@ -322,6 +522,14 @@
};
port@2 {
+ reg = <1>;
+ funnel_in2_in_replicator_swao: endpoint {
+ slave-mode;
+ remote-endpoint =
+ <&replicator_swao_out_funnel_in2>;
+ };
+ };
+ port@3 {
reg = <5>;
funnel_in2_in_funnel_apss_merg: endpoint {
slave-mode;
@@ -329,7 +537,6 @@
<&funnel_apss_merg_out_funnel_in2>;
};
};
-
};
};
@@ -544,6 +751,8 @@
coresight-name = "coresight-tpdm-center";
+ qcom,msr-fix-req;
+
clocks = <&clock_aop QDSS_CLK>;
clock-names = "apb_pclk";
@@ -560,6 +769,8 @@
reg = <0x6a24000 0x1000>;
reg-names = "tpdm-base";
+ qcom,msr-fix-req;
+
coresight-name = "coresight-tpdm-north";
clocks = <&clock_aop QDSS_CLK>;
@@ -644,6 +855,116 @@
};
};
+ tpda_llm_silver: tpda@78c0000 {
+ compatible = "arm,primecell";
+ arm,primecell-periphid = <0x0003b969>;
+ reg = <0x78c0000 0x1000>;
+ reg-names = "tpda-base";
+
+ coresight-name = "coresight-tpda-llm-silver";
+
+ qcom,tpda-atid = <72>;
+ qcom,cmb-elem-size = <0 32>;
+
+ clocks = <&clock_aop QDSS_CLK>;
+ clock-names = "apb_pclk";
+
+ ports {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ port@0 {
+ reg = <0>;
+ tpda_llm_silver_out_funnel_apss_merg: endpoint {
+ remote-endpoint =
+ <&funnel_apss_merg_in_tpda_llm_silver>;
+ };
+ };
+
+ port@1 {
+ reg = <0>;
+ tpda_llm_silver_in_tpdm_llm_silver: endpoint {
+ slave-mode;
+ remote-endpoint =
+ <&tpdm_llm_silver_out_tpda_llm_silver>;
+ };
+ };
+ };
+ };
+
+ tpdm_llm_silver: tpdm@78a0000 {
+ compatible = "arm,primecell";
+ arm,primecell-periphid = <0x0003b968>;
+ reg = <0x78a0000 0x1000>;
+ reg-names = "tpdm-base";
+
+ coresight-name = "coresight-tpdm-llm-silver";
+
+ clocks = <&clock_aop QDSS_CLK>;
+ clock-names = "apb_pclk";
+
+ port {
+ tpdm_llm_silver_out_tpda_llm_silver: endpoint {
+ remote-endpoint =
+ <&tpda_llm_silver_in_tpdm_llm_silver>;
+ };
+ };
+ };
+
+ tpda_llm_gold: tpda@78d0000 {
+ compatible = "arm,primecell";
+ arm,primecell-periphid = <0x0003b969>;
+ reg = <0x78d0000 0x1000>;
+ reg-names = "tpda-base";
+
+ coresight-name = "coresight-tpda-llm-gold";
+
+ qcom,tpda-atid = <73>;
+ qcom,cmb-elem-size = <0 32>;
+
+ clocks = <&clock_aop QDSS_CLK>;
+ clock-names = "apb_pclk";
+
+ ports {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ port@0 {
+ reg = <0>;
+ tpda_llm_gold_out_funnel_apss_merg: endpoint {
+ remote-endpoint =
+ <&funnel_apss_merg_in_tpda_llm_gold>;
+ };
+ };
+
+ port@1 {
+ reg = <0>;
+ tpda_llm_gold_in_tpdm_llm_gold: endpoint {
+ slave-mode;
+ remote-endpoint =
+ <&tpdm_llm_gold_out_tpda_llm_gold>;
+ };
+ };
+ };
+ };
+
+ tpdm_llm_gold: tpdm@78b0000 {
+ compatible = "arm,primecell";
+ arm,primecell-periphid = <0x0003b968>;
+ reg = <0x78b0000 0x1000>;
+ reg-names = "tpdm-base";
+
+ coresight-name = "coresight-tpdm-llm-gold";
+
+ clocks = <&clock_aop QDSS_CLK>;
+ clock-names = "apb_pclk";
+
+ port {
+ tpdm_llm_gold_out_tpda_llm_gold: endpoint {
+ remote-endpoint =
+ <&tpda_llm_gold_in_tpdm_llm_gold>;
+ };
+ };
+ };
+
funnel_dl_mm: funnel@6c0b000 {
compatible = "arm,primecell";
arm,primecell-periphid = <0x0003b908>;
@@ -687,6 +1008,8 @@
coresight-name = "coresight-tpdm-mm";
+ qcom,msr-fix-req;
+
clocks = <&clock_aop QDSS_CLK>;
clock-names = "apb_pclk";
@@ -729,13 +1052,42 @@
<&tpdm_turing_out_funnel_turing>;
};
};
+ };
+ };
- port@2 {
+ funnel_turing_1: funnel_1@6861000 {
+ compatible = "arm,primecell";
+ arm,primecell-periphid = <0x0003b908>;
+
+ reg = <0x6867000 0x10>,
+ <0x6861000 0x1000>;
+ reg-names = "funnel-base-dummy", "funnel-base-real";
+
+ coresight-name = "coresight-funnel-turing-1";
+
+ clocks = <&clock_aop QDSS_CLK>;
+ clock-names = "apb_pclk";
+
+ qcom,duplicate-funnel;
+
+ ports {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ port@0 {
+ reg = <0>;
+ funnel_turing_1_out_funnel_qatb: endpoint {
+ remote-endpoint =
+ <&funnel_qatb_in_funnel_turing_1>;
+ };
+ };
+
+ port@1 {
reg = <1>;
- funnel_turing_in_turing_etm0: endpoint {
+ funnel_turing_1_in_turing_etm0: endpoint {
slave-mode;
remote-endpoint =
- <&turing_etm0_out_funnel_turing>;
+ <&turing_etm0_out_funnel_turing_1>;
};
};
};
@@ -746,7 +1098,8 @@
arm,primecell-periphid = <0x0003b968>;
reg = <0x6860000 0x1000>;
reg-names = "tpdm-base";
- status = "disabled";
+
+ qcom,msr-fix-req;
coresight-name = "coresight-tpdm-turing";
@@ -804,6 +1157,8 @@
coresight-name = "coresight-tpdm-ddr";
+ qcom,msr-fix-req;
+
clocks = <&clock_aop QDSS_CLK>;
clock-names = "apb_pclk";
@@ -935,6 +1290,15 @@
<&tpda_out_funnel_qatb>;
};
};
+
+ port@2 {
+ reg = <7>;
+ funnel_qatb_in_funnel_turing_1: endpoint {
+ slave-mode;
+ remote-endpoint =
+ <&funnel_turing_1_out_funnel_qatb>;
+ };
+ };
};
};
@@ -1562,9 +1926,9 @@
qcom,inst-id = <13>;
port{
- turing_etm0_out_funnel_turing: endpoint {
+ turing_etm0_out_funnel_turing_1: endpoint {
remote-endpoint =
- <&funnel_turing_in_turing_etm0>;
+ <&funnel_turing_1_in_turing_etm0>;
};
};
};
@@ -1583,6 +1947,20 @@
};
};
+ audio_etm0 {
+ compatible = "qcom,coresight-remote-etm";
+
+ coresight-name = "coresight-audio-etm0";
+ qcom,inst-id = <2>;
+
+ port {
+ audio_etm0_out_funnel_in1: endpoint {
+ remote-endpoint =
+ <&funnel_in1_in_audio_etm0>;
+ };
+ };
+ };
+
funnel_apss_merg: funnel@7810000 {
compatible = "arm,primecell";
arm,primecell-periphid = <0x0003b908>;
@@ -1633,6 +2011,22 @@
<&tpda_apss_out_funnel_apss_merg>;
};
};
+ port@4 {
+ reg = <5>;
+ funnel_apss_merg_in_tpda_llm_silver: endpoint {
+ slave-mode;
+ remote-endpoint =
+ <&tpda_llm_silver_out_funnel_apss_merg>;
+ };
+ };
+ port@5 {
+ reg = <6>;
+ funnel_apss_merg_in_tpda_llm_gold: endpoint {
+ slave-mode;
+ remote-endpoint =
+ <&tpda_llm_gold_out_funnel_apss_merg>;
+ };
+ };
};
};
diff --git a/arch/arm64/boot/dts/qcom/sdm670-gpu.dtsi b/arch/arm64/boot/dts/qcom/sdm670-gpu.dtsi
index 89dee0c..4783396 100644
--- a/arch/arm64/boot/dts/qcom/sdm670-gpu.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm670-gpu.dtsi
@@ -79,11 +79,10 @@
<&clock_gpucc GPU_CC_CXO_CLK>,
<&clock_gcc GCC_DDRSS_GPU_AXI_CLK>,
<&clock_gcc GCC_GPU_MEMNOC_GFX_CLK>,
- <&clock_gpucc GPU_CC_CX_GMU_CLK>,
- <&clock_gpucc GPU_CC_AHB_CLK>;
+ <&clock_gpucc GPU_CC_CX_GMU_CLK>;
clock-names = "core_clk", "rbbmtimer_clk", "mem_clk",
- "mem_iface_clk", "gmu_clk", "ahb_clk";
+ "mem_iface_clk", "gmu_clk";
/* Bus Scale Settings */
qcom,gpubw-dev = <&gpubw>;
@@ -275,11 +274,10 @@
clocks = <&clock_gpucc GPU_CC_CX_GMU_CLK>,
<&clock_gpucc GPU_CC_CXO_CLK>,
<&clock_gcc GCC_DDRSS_GPU_AXI_CLK>,
- <&clock_gcc GCC_GPU_MEMNOC_GFX_CLK>,
- <&clock_gpucc GPU_CC_AHB_CLK>;
+ <&clock_gcc GCC_GPU_MEMNOC_GFX_CLK>;
clock-names = "gmu_clk", "cxo_clk", "axi_clk",
- "memnoc_clk", "ahb_clk";
+ "memnoc_clk";
qcom,gmu-pwrlevels {
#address-cells = <1>;
@@ -289,12 +287,12 @@
qcom,gmu-pwrlevel@0 {
reg = <0>;
- qcom,gmu-freq = <200000000>;
+ qcom,gmu-freq = <0>;
};
qcom,gmu-pwrlevel@1 {
reg = <1>;
- qcom,gmu-freq = <0>;
+ qcom,gmu-freq = <200000000>;
};
};
diff --git a/arch/arm64/boot/dts/qcom/sdm670-lpi.dtsi b/arch/arm64/boot/dts/qcom/sdm670-lpi.dtsi
index 6e92f0e..c64ed2c 100644
--- a/arch/arm64/boot/dts/qcom/sdm670-lpi.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm670-lpi.dtsi
@@ -26,8 +26,8 @@
config {
pins = "gpio18";
- drive-strength = <8>;
- output-high;
+ drive-strength = <2>;
+ output-low;
};
};
@@ -53,8 +53,8 @@
config {
pins = "gpio19";
- drive-strength = <8>;
- output-high;
+ drive-strength = <2>;
+ output-low;
};
};
@@ -80,8 +80,8 @@
config {
pins = "gpio21";
- drive-strength = <8>;
- output-high;
+ drive-strength = <2>;
+ output-low;
};
};
@@ -107,8 +107,8 @@
config {
pins = "gpio23", "gpio25";
- drive-strength = <8>;
- output-high;
+ drive-strength = <2>;
+ output-low;
};
};
@@ -159,7 +159,7 @@
config {
pins = "gpio22";
- drive-strength = <8>;
+ drive-strength = <2>;
};
};
@@ -184,7 +184,7 @@
config {
pins = "gpio24";
- drive-strength = <8>;
+ drive-strength = <2>;
};
};
diff --git a/arch/arm64/boot/dts/qcom/sdm670-mtp.dtsi b/arch/arm64/boot/dts/qcom/sdm670-mtp.dtsi
index 466062b..016917b 100644
--- a/arch/arm64/boot/dts/qcom/sdm670-mtp.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm670-mtp.dtsi
@@ -13,6 +13,7 @@
#include <dt-bindings/gpio/gpio.h>
#include "sdm670-pmic-overlay.dtsi"
#include "sdm670-sde-display.dtsi"
+#include "smb1355.dtsi"
&ufsphy_mem {
compatible = "qcom,ufs-phy-qmp-v3";
@@ -57,11 +58,11 @@
};
&qupv3_se10_i2c {
- status = "disabled";
+ status = "ok";
};
&qupv3_se6_4uart {
- status = "disabled";
+ status = "ok";
};
&sdhc_1 {
@@ -95,15 +96,6 @@
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 &sdc2_cd_off>;
- #address-cells = <0>;
- interrupt-parent = <&sdhc_2>;
- interrupts = <0 1 2>;
- #interrupt-cells = <1>;
- interrupt-map-mask = <0xffffffff>;
- interrupt-map = <0 &intc 0 0 204 0
- 1 &intc 0 0 222 0
- 2 &tlmm 96 0>;
- interrupt-names = "hc_irq", "pwr_irq", "status_irq";
cd-gpios = <&tlmm 96 0x1>;
status = "ok";
@@ -122,6 +114,51 @@
qcom,battery-data = <&mtp_batterydata>;
};
+&tlmm {
+ smb_int_default: smb_int_default {
+ mux {
+ pins = "gpio54";
+ function = "gpio";
+ };
+ config {
+ pins = "gpio54";
+ drive-strength = <2>;
+ bias-pull-up;
+ input-enable;
+ };
+ };
+};
+
+&smb1355_0 {
+ pinctrl-names = "default";
+ pinctrl-0 = <&smb_int_default
+ &smb_shutdown_default>;
+ interrupt-parent = <&tlmm>;
+ interrupts = <54 IRQ_TYPE_LEVEL_LOW>;
+ smb1355_charger_0: qcom,smb1355-charger@1000 {
+ io-channels = <&pm660_rradc 2>,
+ <&pm660_rradc 12>;
+ io-channel-names = "charger_temp",
+ "charger_temp_max";
+ status ="ok";
+ };
+};
+
+&smb1355_1 {
+ pinctrl-names = "default";
+ pinctrl-0 = <&smb_int_default
+ &smb_shutdown_default>;
+ interrupt-parent = <&tlmm>;
+ interrupts = <54 IRQ_TYPE_LEVEL_LOW>;
+ smb1355_charger_1: qcom,smb1355-charger@1000 {
+ io-channels = <&pm660_rradc 2>,
+ <&pm660_rradc 12>;
+ io-channel-names = "charger_temp",
+ "charger_temp_max";
+ status ="ok";
+ };
+};
+
&soc {
gpio_keys {
compatible = "gpio-keys";
@@ -242,7 +279,54 @@
qcom,platform-reset-gpio = <&tlmm 75 0>;
};
-&dsi_dual_nt35597_truly_video {
+&dsi_dual_nt35597_video {
+ qcom,panel-supply-entries = <&dsi_panel_pwr_supply>;
+ qcom,mdss-dsi-bl-pmic-control-type = "bl_ctrl_wled";
+ qcom,mdss-dsi-bl-min-level = <1>;
+ qcom,mdss-dsi-bl-max-level = <4095>;
+ qcom,mdss-dsi-mode-sel-gpio-state = "dual_port";
+ qcom,panel-mode-gpio = <&tlmm 76 0>;
+ qcom,platform-reset-gpio = <&tlmm 75 0>;
+};
+
+&dsi_dual_nt35597_cmd {
+ qcom,panel-supply-entries = <&dsi_panel_pwr_supply>;
+ qcom,mdss-dsi-bl-pmic-control-type = "bl_ctrl_wled";
+ qcom,mdss-dsi-bl-min-level = <1>;
+ qcom,mdss-dsi-bl-max-level = <4095>;
+ qcom,mdss-dsi-mode-sel-gpio-state = "dual_port";
+ qcom,panel-mode-gpio = <&tlmm 76 0>;
+ qcom,platform-reset-gpio = <&tlmm 75 0>;
+ qcom,platform-te-gpio = <&tlmm 10 0>;
+};
+
+&dsi_rm67195_amoled_fhd_cmd {
+ qcom,mdss-dsi-bl-pmic-control-type = "bl_ctrl_wled";
+ qcom,mdss-dsi-bl-min-level = <1>;
+ qcom,mdss-dsi-bl-max-level = <4095>;
+ qcom,panel-supply-entries = <&dsi_panel_pwr_supply_labibb_amoled>;
+ qcom,platform-reset-gpio = <&tlmm 75 0>;
+ qcom,platform-te-gpio = <&tlmm 10 0>;
+};
+
+&dsi_nt35695b_truly_fhd_video {
+ qcom,mdss-dsi-bl-pmic-control-type = "bl_ctrl_wled";
+ qcom,mdss-dsi-bl-min-level = <1>;
+ qcom,mdss-dsi-bl-max-level = <4095>;
+ qcom,panel-supply-entries = <&dsi_panel_pwr_supply>;
+ qcom,platform-reset-gpio = <&tlmm 75 0>;
+};
+
+&dsi_nt35695b_truly_fhd_cmd {
+ qcom,mdss-dsi-bl-pmic-control-type = "bl_ctrl_wled";
+ qcom,mdss-dsi-bl-min-level = <1>;
+ qcom,mdss-dsi-bl-max-level = <4095>;
+ qcom,panel-supply-entries = <&dsi_panel_pwr_supply>;
+ qcom,platform-reset-gpio = <&tlmm 75 0>;
+ qcom,platform-te-gpio = <&tlmm 10 0>;
+};
+
+&dsi_dual_nt35597_truly_video_display {
qcom,dsi-display-active;
};
diff --git a/arch/arm64/boot/dts/qcom/sdm670-pinctrl.dtsi b/arch/arm64/boot/dts/qcom/sdm670-pinctrl.dtsi
index 177813f..2bf00fb 100644
--- a/arch/arm64/boot/dts/qcom/sdm670-pinctrl.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm670-pinctrl.dtsi
@@ -1625,6 +1625,20 @@
};
&pm660l_gpios {
+ camera0_dvdd_en_default: camera0_dvdd_en_default {
+ pins = "gpio3";
+ function = "normal";
+ power-source = <0>;
+ output-low;
+ };
+
+ camera1_dvdd_en_default: camera1_dvdd_en_default {
+ pins = "gpio4";
+ function = "normal";
+ power-source = <0>;
+ output-low;
+ };
+
key_vol_up {
key_vol_up_default: key_vol_up_default {
pins = "gpio7";
@@ -1635,3 +1649,13 @@
};
};
};
+
+&pm660_gpios {
+ smb_shutdown_default: smb_shutdown_default {
+ pins = "gpio11";
+ function = "normal";
+ power-source = <0>;
+ qcom,drive-strength = <3>;
+ output-high;
+ };
+};
diff --git a/arch/arm64/boot/dts/qcom/sdm670-pmic-overlay.dtsi b/arch/arm64/boot/dts/qcom/sdm670-pmic-overlay.dtsi
index aa6be24..c39978e 100644
--- a/arch/arm64/boot/dts/qcom/sdm670-pmic-overlay.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm670-pmic-overlay.dtsi
@@ -30,8 +30,7 @@
qcom,wipower-max-uw = <5000000>;
- /* Enable after the qusb_phy0 device node is added */
- /* dpdm-supply = <&qusb_phy0>; */
+ dpdm-supply = <&qusb_phy0>;
qcom,thermal-mitigation
= <3000000 2500000 2000000 1500000
diff --git a/arch/arm64/boot/dts/qcom/sdm670-qrd-overlay.dts b/arch/arm64/boot/dts/qcom/sdm670-qrd-overlay.dts
new file mode 100644
index 0000000..36d485e
--- /dev/null
+++ b/arch/arm64/boot/dts/qcom/sdm670-qrd-overlay.dts
@@ -0,0 +1,32 @@
+/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+/dts-v1/;
+/plugin/;
+
+#include <dt-bindings/clock/qcom,gcc-sdm845.h>
+#include <dt-bindings/clock/qcom,camcc-sdm845.h>
+#include <dt-bindings/clock/qcom,dispcc-sdm845.h>
+#include <dt-bindings/clock/qcom,rpmh.h>
+#include <dt-bindings/interrupt-controller/arm-gic.h>
+
+#include "sdm670-qrd.dtsi"
+
+/ {
+ model = "Qualcomm Technologies, Inc. SDM670 PM660 + PM660L QRD";
+ compatible = "qcom,sdm670-qrd", "qcom,sdm670", "qcom,qrd";
+ qcom,msm-id = <336 0x0>;
+ qcom,board-id = <0x0002000b 0>;
+ qcom,pmic-id = <0x0001001b 0x0101011a 0x0 0x0>,
+ <0x0001001b 0x0102001a 0x0 0x0>,
+ <0x0001001b 0x0201011a 0x0 0x0>;
+};
diff --git a/arch/arm64/boot/dts/qcom/sdm670-qrd-sku2-overlay.dts b/arch/arm64/boot/dts/qcom/sdm670-qrd-sku2-overlay.dts
new file mode 100644
index 0000000..37eb4cd
--- /dev/null
+++ b/arch/arm64/boot/dts/qcom/sdm670-qrd-sku2-overlay.dts
@@ -0,0 +1,32 @@
+/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+/dts-v1/;
+/plugin/;
+
+#include <dt-bindings/clock/qcom,gcc-sdm845.h>
+#include <dt-bindings/clock/qcom,camcc-sdm845.h>
+#include <dt-bindings/clock/qcom,dispcc-sdm845.h>
+#include <dt-bindings/clock/qcom,rpmh.h>
+#include <dt-bindings/interrupt-controller/arm-gic.h>
+
+#include "sdm670-qrd.dtsi"
+
+/ {
+ model = "Qualcomm Technologies, Inc. SDM670 PM660 + PM660L QRD SKU2";
+ compatible = "qcom,sdm670-qrd", "qcom,sdm670", "qcom,qrd";
+ qcom,msm-id = <336 0x0>;
+ qcom,board-id = <0x0012000b 0>;
+ qcom,pmic-id = <0x0001001b 0x0101011a 0x0 0x0>,
+ <0x0001001b 0x0102001a 0x0 0x0>,
+ <0x0001001b 0x0201011a 0x0 0x0>;
+};
diff --git a/arch/arm64/boot/dts/qcom/sdm670-qrd-sku2.dts b/arch/arm64/boot/dts/qcom/sdm670-qrd-sku2.dts
new file mode 100644
index 0000000..dada4c6
--- /dev/null
+++ b/arch/arm64/boot/dts/qcom/sdm670-qrd-sku2.dts
@@ -0,0 +1,26 @@
+/* Copyright (c) 2017, 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 "sdm670.dtsi"
+#include "sdm670-qrd.dtsi"
+
+/ {
+ model = "Qualcomm Technologies, Inc. SDM670 PM660 + PM660L QRD SKU2";
+ compatible = "qcom,sdm670-qrd", "qcom,sdm670", "qcom,qrd";
+ qcom,board-id = <0x0012000b 0>;
+ qcom,pmic-id = <0x0001001b 0x0101011a 0x0 0x0>,
+ <0x0001001b 0x0102001a 0x0 0x0>,
+ <0x0001001b 0x0201011a 0x0 0x0>;
+};
diff --git a/arch/arm64/boot/dts/qcom/sdm670-qrd.dts b/arch/arm64/boot/dts/qcom/sdm670-qrd.dts
new file mode 100644
index 0000000..c22afa4
--- /dev/null
+++ b/arch/arm64/boot/dts/qcom/sdm670-qrd.dts
@@ -0,0 +1,26 @@
+/* Copyright (c) 2017, 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 "sdm670.dtsi"
+#include "sdm670-qrd.dtsi"
+
+/ {
+ model = "Qualcomm Technologies, Inc. SDM670 PM660 + PM660L QRD";
+ compatible = "qcom,sdm670-qrd", "qcom,sdm670", "qcom,qrd";
+ qcom,board-id = <0x0002000b 0>;
+ qcom,pmic-id = <0x0001001b 0x0101011a 0x0 0x0>,
+ <0x0001001b 0x0102001a 0x0 0x0>,
+ <0x0001001b 0x0201011a 0x0 0x0>;
+};
diff --git a/arch/arm64/boot/dts/qcom/sdm670-qrd.dtsi b/arch/arm64/boot/dts/qcom/sdm670-qrd.dtsi
new file mode 100644
index 0000000..1925989
--- /dev/null
+++ b/arch/arm64/boot/dts/qcom/sdm670-qrd.dtsi
@@ -0,0 +1,147 @@
+/* Copyright (c) 2017, 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 <dt-bindings/gpio/gpio.h>
+#include "sdm670-pmic-overlay.dtsi"
+#include "sdm670-audio-overlay.dtsi"
+#include "smb1355.dtsi"
+
+&qupv3_se9_2uart {
+ status = "disabled";
+};
+
+&qupv3_se12_2uart {
+ status = "ok";
+};
+
+&qupv3_se8_spi {
+ status = "disabled";
+};
+
+&qupv3_se3_i2c {
+ status = "disabled";
+};
+
+&qupv3_se10_i2c {
+ status = "ok";
+};
+
+&qupv3_se6_4uart {
+ status = "ok";
+};
+
+&vendor {
+ qrd_batterydata: qcom,battery-data {
+ qcom,batt-id-range-pct = <15>;
+ #include "fg-gen3-batterydata-mlp356477-2800mah.dtsi"
+ };
+};
+
+&pm660_fg {
+ qcom,battery-data = <&qrd_batterydata>;
+ qcom,fg-bmd-en-delay-ms = <300>;
+};
+
+&tlmm {
+ smb_int_default: smb_int_default {
+ mux {
+ pins = "gpio54";
+ function = "gpio";
+ };
+ config {
+ pins = "gpio54";
+ drive-strength = <2>;
+ bias-pull-up;
+ input-enable;
+ };
+ };
+};
+
+&smb1355_0 {
+ pinctrl-names = "default";
+ pinctrl-0 = <&smb_int_default
+ &smb_shutdown_default>;
+ interrupt-parent = <&tlmm>;
+ interrupts = <54 IRQ_TYPE_LEVEL_LOW>;
+ smb1355_charger_0: qcom,smb1355-charger@1000 {
+ io-channels = <&pm660_rradc 2>,
+ <&pm660_rradc 12>;
+ io-channel-names = "charger_temp",
+ "charger_temp_max";
+ status = "ok";
+ };
+};
+
+&smb1355_1 {
+ pinctrl-names = "default";
+ pinctrl-0 = <&smb_int_default
+ &smb_shutdown_default>;
+ interrupt-parent = <&tlmm>;
+ interrupts = <54 IRQ_TYPE_LEVEL_LOW>;
+ smb1355_charger_1: qcom,smb1355-charger@1000 {
+ io-channels = <&pm660_rradc 2>,
+ <&pm660_rradc 12>;
+ io-channel-names = "charger_temp",
+ "charger_temp_max";
+ status = "ok";
+ };
+};
+
+&soc {
+ gpio_keys {
+ compatible = "gpio-keys";
+ label = "gpio-keys";
+ pinctrl-names = "default";
+ pinctrl-0 = <&key_vol_up_default>;
+
+ vol_up {
+ label = "volume_up";
+ gpios = <&pm660l_gpios 7 GPIO_ACTIVE_LOW>;
+ linux,input-type = <1>;
+ linux,code = <115>;
+ gpio-key,wakeup;
+ debounce-interval = <15>;
+ linux,can-disable;
+ };
+ };
+};
+
+&pm660_haptics {
+ qcom,vmax-mv = <1800>;
+ qcom,wave-play-rate-us = <4255>;
+ qcom,lra-auto-mode;
+ status = "okay";
+};
+
+&int_codec {
+ qcom,model = "sdm660-skuw-snd-card";
+ qcom,audio-routing =
+ "RX_BIAS", "INT_MCLK0",
+ "SPK_RX_BIAS", "INT_MCLK0",
+ "INT_LDO_H", "INT_MCLK0",
+ "MIC BIAS External2", "Headset Mic",
+ "AMIC2", "MIC BIAS External2",
+ "MIC BIAS External", "Handset Mic",
+ "AMIC1", "MIC BIAS External",
+ "MIC BIAS External", "Secondary Mic",
+ "AMIC3", "MIC BIAS External",
+ "SpkrLeft IN", "SPK1 OUT",
+ "PDM_IN_RX1", "PDM_OUT_RX1",
+ "PDM_IN_RX2", "PDM_OUT_RX2",
+ "PDM_IN_RX3", "PDM_OUT_RX3",
+ "ADC1_IN", "ADC1_OUT",
+ "ADC2_IN", "ADC2_OUT",
+ "ADC3_IN", "ADC3_OUT";
+ qcom,wsa-max-devs = <1>;
+ qcom,wsa-devs = <&wsa881x_211_en>, <&wsa881x_213_en>;
+ qcom,wsa-aux-dev-prefix = "SpkrLeft", "SpkrLeft";
+};
diff --git a/arch/arm64/boot/dts/qcom/sdm670-sde-display.dtsi b/arch/arm64/boot/dts/qcom/sdm670-sde-display.dtsi
index 3e9d967..2a61e18 100644
--- a/arch/arm64/boot/dts/qcom/sdm670-sde-display.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm670-sde-display.dtsi
@@ -20,6 +20,11 @@
#include "dsi-panel-nt35597-truly-dualmipi-wqxga-cmd.dtsi"
#include "dsi-panel-nt35597-truly-dsc-wqxga-cmd.dtsi"
#include "dsi-panel-nt35597-truly-dsc-wqxga-video.dtsi"
+#include "dsi-panel-nt35597-dualmipi-wqxga-video.dtsi"
+#include "dsi-panel-nt35597-dualmipi-wqxga-cmd.dtsi"
+#include "dsi-panel-nt35695b-truly-fhd-video.dtsi"
+#include "dsi-panel-nt35695b-truly-fhd-cmd.dtsi"
+#include "dsi-panel-rm67195-amoled-fhd-cmd.dtsi"
#include <dt-bindings/clock/mdss-10nm-pll-clk.h>
&soc {
@@ -97,6 +102,56 @@
};
};
+ dsi_panel_pwr_supply_labibb_amoled: dsi_panel_pwr_supply_labibb_amoled {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ qcom,panel-supply-entry@0 {
+ reg = <0>;
+ qcom,supply-name = "wqhd-vddio";
+ qcom,supply-min-voltage = <1800000>;
+ qcom,supply-max-voltage = <1950000>;
+ qcom,supply-enable-load = <32000>;
+ qcom,supply-disable-load = <80>;
+ };
+
+ qcom,panel-supply-entry@1 {
+ reg = <1>;
+ qcom,supply-name = "vdda-3p3";
+ qcom,supply-min-voltage = <3300000>;
+ qcom,supply-max-voltage = <3300000>;
+ qcom,supply-enable-load = <13200>;
+ qcom,supply-disable-load = <80>;
+ };
+
+ qcom,panel-supply-entry@2 {
+ reg = <2>;
+ qcom,supply-name = "lab";
+ qcom,supply-min-voltage = <4600000>;
+ qcom,supply-max-voltage = <6100000>;
+ qcom,supply-enable-load = <100000>;
+ qcom,supply-disable-load = <100>;
+ };
+
+ qcom,panel-supply-entry@3 {
+ reg = <3>;
+ qcom,supply-name = "ibb";
+ qcom,supply-min-voltage = <4000000>;
+ qcom,supply-max-voltage = <6300000>;
+ qcom,supply-enable-load = <100000>;
+ qcom,supply-disable-load = <100>;
+ };
+
+ qcom,panel-supply-entry@4 {
+ reg = <4>;
+ qcom,supply-name = "oledb";
+ qcom,supply-min-voltage = <5000000>;
+ qcom,supply-max-voltage = <8100000>;
+ qcom,supply-enable-load = <100000>;
+ qcom,supply-disable-load = <100>;
+ };
+ };
+
dsi_dual_nt35597_truly_video_display: qcom,dsi-display@0 {
compatible = "qcom,dsi-display";
label = "dsi_dual_nt35597_truly_video_display";
@@ -300,6 +355,120 @@
qcom,dsi-panel = <&dsi_dual_sim_dsc_375_cmd>;
};
+ dsi_dual_nt35597_video_display: qcom,dsi-display@10 {
+ compatible = "qcom,dsi-display";
+ label = "dsi_dual_nt35597_video_display";
+ qcom,display-type = "primary";
+
+ qcom,dsi-ctrl = <&mdss_dsi0 &mdss_dsi1>;
+ qcom,dsi-phy = <&mdss_dsi_phy0 &mdss_dsi_phy1>;
+ clocks = <&mdss_dsi0_pll BYTECLK_MUX_0_CLK>,
+ <&mdss_dsi0_pll PCLK_MUX_0_CLK>;
+ clock-names = "src_byte_clk", "src_pixel_clk";
+
+ pinctrl-names = "panel_active", "panel_suspend";
+ pinctrl-0 = <&sde_dsi_active &sde_te_active>;
+ pinctrl-1 = <&sde_dsi_suspend &sde_te_suspend>;
+ qcom,platform-reset-gpio = <&tlmm 75 0>;
+ qcom,panel-mode-gpio = <&tlmm 76 0>;
+
+ qcom,dsi-panel = <&dsi_dual_nt35597_video>;
+ vddio-supply = <&pm660_l11>;
+ lab-supply = <&lcdb_ldo_vreg>;
+ ibb-supply = <&lcdb_ncp_vreg>;
+ };
+
+ dsi_dual_nt35597_cmd_display: qcom,dsi-display@11 {
+ compatible = "qcom,dsi-display";
+ label = "dsi_dual_nt35597_cmd_display";
+ qcom,display-type = "primary";
+
+ qcom,dsi-ctrl = <&mdss_dsi0 &mdss_dsi1>;
+ qcom,dsi-phy = <&mdss_dsi_phy0 &mdss_dsi_phy1>;
+ clocks = <&mdss_dsi0_pll BYTECLK_MUX_0_CLK>,
+ <&mdss_dsi0_pll PCLK_MUX_0_CLK>;
+ clock-names = "src_byte_clk", "src_pixel_clk";
+
+ pinctrl-names = "panel_active", "panel_suspend";
+ pinctrl-0 = <&sde_dsi_active &sde_te_active>;
+ pinctrl-1 = <&sde_dsi_suspend &sde_te_suspend>;
+ qcom,platform-reset-gpio = <&tlmm 75 0>;
+ qcom,panel-mode-gpio = <&tlmm 76 0>;
+
+ qcom,dsi-panel = <&dsi_dual_nt35597_cmd>;
+ vddio-supply = <&pm660_l11>;
+ lab-supply = <&lcdb_ldo_vreg>;
+ ibb-supply = <&lcdb_ncp_vreg>;
+ };
+
+ dsi_rm67195_amoled_fhd_cmd_display: qcom,dsi-display@12 {
+ compatible = "qcom,dsi-display";
+ label = "dsi_rm67195_amoled_fhd_cmd_display";
+ qcom,display-type = "primary";
+
+ qcom,dsi-ctrl = <&mdss_dsi0>;
+ qcom,dsi-phy = <&mdss_dsi_phy0>;
+ clocks = <&mdss_dsi0_pll BYTECLK_MUX_0_CLK>,
+ <&mdss_dsi0_pll PCLK_MUX_0_CLK>;
+ clock-names = "src_byte_clk", "src_pixel_clk";
+
+ pinctrl-names = "panel_active", "panel_suspend";
+ pinctrl-0 = <&sde_dsi_active &sde_te_active>;
+ pinctrl-1 = <&sde_dsi_suspend &sde_te_suspend>;
+ qcom,platform-te-gpio = <&tlmm 10 0>;
+ qcom,platform-reset-gpio = <&tlmm 75 0>;
+
+ qcom,dsi-panel = <&dsi_rm67195_amoled_fhd_cmd>;
+ vddio-supply = <&pm660_l11>;
+ lab-supply = <&lcdb_ldo_vreg>;
+ ibb-supply = <&lcdb_ncp_vreg>;
+ };
+
+ dsi_nt35695b_truly_fhd_video_display: qcom,dsi-display@13 {
+ compatible = "qcom,dsi-display";
+ label = "dsi_nt35695b_truly_fhd_video_display";
+ qcom,display-type = "primary";
+
+ qcom,dsi-ctrl = <&mdss_dsi0>;
+ qcom,dsi-phy = <&mdss_dsi_phy0>;
+ clocks = <&mdss_dsi0_pll BYTECLK_MUX_0_CLK>,
+ <&mdss_dsi0_pll PCLK_MUX_0_CLK>;
+ clock-names = "src_byte_clk", "src_pixel_clk";
+
+ pinctrl-names = "panel_active", "panel_suspend";
+ pinctrl-0 = <&sde_dsi_active &sde_te_active>;
+ pinctrl-1 = <&sde_dsi_suspend &sde_te_suspend>;
+ qcom,platform-reset-gpio = <&tlmm 75 0>;
+
+ qcom,dsi-panel = <&dsi_nt35695b_truly_fhd_video>;
+ vddio-supply = <&pm660_l11>;
+ lab-supply = <&lcdb_ldo_vreg>;
+ ibb-supply = <&lcdb_ncp_vreg>;
+ };
+
+ dsi_nt35695b_truly_fhd_cmd_display: qcom,dsi-display@14 {
+ compatible = "qcom,dsi-display";
+ label = "dsi_nt35695b_truly_fhd_cmd_display";
+ qcom,display-type = "primary";
+
+ qcom,dsi-ctrl = <&mdss_dsi0>;
+ qcom,dsi-phy = <&mdss_dsi_phy0>;
+ clocks = <&mdss_dsi0_pll BYTECLK_MUX_0_CLK>,
+ <&mdss_dsi0_pll PCLK_MUX_0_CLK>;
+ clock-names = "src_byte_clk", "src_pixel_clk";
+
+ pinctrl-names = "panel_active", "panel_suspend";
+ pinctrl-0 = <&sde_dsi_active &sde_te_active>;
+ pinctrl-1 = <&sde_dsi_suspend &sde_te_suspend>;
+ qcom,platform-te-gpio = <&tlmm 10 0>;
+ qcom,platform-reset-gpio = <&tlmm 75 0>;
+
+ qcom,dsi-panel = <&dsi_nt35695b_truly_fhd_cmd>;
+ vddio-supply = <&pm660_l11>;
+ lab-supply = <&lcdb_ldo_vreg>;
+ ibb-supply = <&lcdb_ncp_vreg>;
+ };
+
sde_wb: qcom,wb-display@0 {
compatible = "qcom,wb-display";
cell-index = <0>;
@@ -308,7 +477,6 @@
ext_disp: qcom,msm-ext-disp {
compatible = "qcom,msm-ext-disp";
- status = "disabled";
ext_disp_audio_codec: qcom,msm-ext-disp-audio-codec-rx {
compatible = "qcom,msm-ext-disp-audio-codec-rx";
@@ -318,7 +486,6 @@
sde_dp: qcom,dp_display@0{
cell-index = <0>;
compatible = "qcom,dp-display";
- status = "disabled";
gdsc-supply = <&mdss_core_gdsc>;
vdda-1p2-supply = <&pm660_l1>;
@@ -331,10 +498,11 @@
<0xaf02000 0x1a0>,
<0x780000 0x621c>,
<0x88ea030 0x10>,
+ <0x88e8000 0x20>,
<0x0aee1000 0x034>;
reg-names = "dp_ctrl", "dp_phy", "dp_ln_tx0", "dp_ln_tx1",
"dp_mmss_cc", "qfprom_physical", "dp_pll",
- "hdcp_physical";
+ "usb3_dp_com", "hdcp_physical";
interrupt-parent = <&mdss_mdp>;
interrupts = <12 0>;
@@ -417,7 +585,6 @@
};
&sde_dp {
- status = "disabled";
pinctrl-names = "mdss_dp_active", "mdss_dp_sleep";
pinctrl-0 = <&sde_dp_aux_active &sde_dp_usbplug_cc_active>;
pinctrl-1 = <&sde_dp_aux_suspend &sde_dp_usbplug_cc_suspend>;
@@ -427,7 +594,7 @@
};
&mdss_mdp {
- connectors = <&sde_rscc &sde_wb>;
+ connectors = <&sde_rscc &sde_wb &sde_dp>;
};
&dsi_dual_nt35597_truly_video {
@@ -595,3 +762,70 @@
};
};
};
+
+&dsi_dual_nt35597_video {
+ qcom,mdss-dsi-t-clk-post = <0x0d>;
+ qcom,mdss-dsi-t-clk-pre = <0x2d>;
+ qcom,mdss-dsi-display-timings {
+ timing@0 {
+ qcom,mdss-dsi-panel-timings = [00 1c 08 07 23 22 07 07
+ 05 03 04 00];
+ qcom,display-topology = <2 0 2>,
+ <1 0 2>;
+ qcom,default-topology-index = <0>;
+ };
+ };
+};
+
+&dsi_dual_nt35597_cmd {
+ qcom,mdss-dsi-t-clk-post = <0x0d>;
+ qcom,mdss-dsi-t-clk-pre = <0x2d>;
+ qcom,mdss-dsi-display-timings {
+ timing@0 {
+ qcom,mdss-dsi-panel-timings = [00 1c 08 07 23 22 07 07
+ 05 03 04 00];
+ qcom,display-topology = <2 0 2>,
+ <1 0 2>;
+ qcom,default-topology-index = <0>;
+ };
+ };
+};
+
+&dsi_rm67195_amoled_fhd_cmd {
+ qcom,mdss-dsi-t-clk-post = <0x07>;
+ qcom,mdss-dsi-t-clk-pre = <0x1c>;
+ qcom,mdss-dsi-display-timings {
+ timing@0 {
+ qcom,mdss-dsi-panel-phy-timings = [00 1c 05 06 0b 0c
+ 05 07 05 03 04 00];
+ qcom,display-topology = <1 0 1>;
+ qcom,default-topology-index = <0>;
+ };
+ };
+};
+
+&dsi_nt35695b_truly_fhd_video {
+ qcom,mdss-dsi-t-clk-post = <0x07>;
+ qcom,mdss-dsi-t-clk-pre = <0x1c>;
+ qcom,mdss-dsi-display-timings {
+ timing@0 {
+ qcom,mdss-dsi-panel-phy-timings = [00 1c 05 06 0b 0c
+ 05 07 05 03 04 00];
+ qcom,display-topology = <1 0 1>;
+ qcom,default-topology-index = <0>;
+ };
+ };
+};
+
+&dsi_nt35695b_truly_fhd_cmd {
+ qcom,mdss-dsi-t-clk-post = <0x07>;
+ qcom,mdss-dsi-t-clk-pre = <0x1c>;
+ qcom,mdss-dsi-display-timings {
+ timing@0 {
+ qcom,mdss-dsi-panel-phy-timings = [00 1c 05 06 0b 0c
+ 05 07 05 03 04 00];
+ qcom,display-topology = <1 0 1>;
+ qcom,default-topology-index = <0>;
+ };
+ };
+};
diff --git a/arch/arm64/boot/dts/qcom/sdm670-sde-pll.dtsi b/arch/arm64/boot/dts/qcom/sdm670-sde-pll.dtsi
index 78e5d94..72e3f5f 100644
--- a/arch/arm64/boot/dts/qcom/sdm670-sde-pll.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm670-sde-pll.dtsi
@@ -67,7 +67,6 @@
mdss_dp_pll: qcom,mdss_dp_pll@88ea000 {
compatible = "qcom,mdss_dp_pll_10nm";
- status = "disabled";
label = "MDSS DP PLL";
cell-index = <0>;
#clock-cells = <1>;
diff --git a/arch/arm64/boot/dts/qcom/sdm670-sde.dtsi b/arch/arm64/boot/dts/qcom/sdm670-sde.dtsi
index bb30a20..2878184 100644
--- a/arch/arm64/boot/dts/qcom/sdm670-sde.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm670-sde.dtsi
@@ -190,10 +190,14 @@
qcom,sde-dspp-blocks {
qcom,sde-dspp-igc = <0x0 0x00030001>;
+ qcom,sde-dspp-hsic = <0x800 0x00010007>;
+ qcom,sde-dspp-memcolor = <0x880 0x00010007>;
+ qcom,sde-dspp-sixzone= <0x900 0x00010007>;
qcom,sde-dspp-vlut = <0xa00 0x00010008>;
qcom,sde-dspp-gamut = <0x1000 0x00040000>;
qcom,sde-dspp-pcc = <0x1700 0x00040000>;
qcom,sde-dspp-gc = <0x17c0 0x00010008>;
+ qcom,sde-dspp-hist = <0x800 0x00010007>;
};
qcom,platform-supply-entries {
diff --git a/arch/arm64/boot/dts/qcom/sdm670-thermal.dtsi b/arch/arm64/boot/dts/qcom/sdm670-thermal.dtsi
index e4993d6..1ce8dba 100644
--- a/arch/arm64/boot/dts/qcom/sdm670-thermal.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm670-thermal.dtsi
@@ -518,7 +518,7 @@
cooling-device = <&CPU6 9 9>;
};
gpu_vdd_cdev {
- trip = <&aoss0_trip>;
+ trip = <&cpu0_trip>;
cooling-device = <&msm_gpu 1 1>;
};
cx_vdd_cdev {
@@ -567,7 +567,7 @@
cooling-device = <&CPU6 9 9>;
};
gpu_vdd_cdev {
- trip = <&aoss0_trip>;
+ trip = <&cpu1_trip>;
cooling-device = <&msm_gpu 1 1>;
};
cx_vdd_cdev {
@@ -616,7 +616,7 @@
cooling-device = <&CPU6 9 9>;
};
gpu_vdd_cdev {
- trip = <&aoss0_trip>;
+ trip = <&cpu2_trip>;
cooling-device = <&msm_gpu 1 1>;
};
cx_vdd_cdev {
@@ -665,7 +665,7 @@
cooling-device = <&CPU6 9 9>;
};
gpu_vdd_cdev {
- trip = <&aoss0_trip>;
+ trip = <&cpu3_trip>;
cooling-device = <&msm_gpu 1 1>;
};
cx_vdd_cdev {
@@ -714,7 +714,7 @@
cooling-device = <&CPU6 9 9>;
};
gpu_vdd_cdev {
- trip = <&aoss0_trip>;
+ trip = <&cpu4_trip>;
cooling-device = <&msm_gpu 1 1>;
};
cx_vdd_cdev {
@@ -763,7 +763,7 @@
cooling-device = <&CPU6 9 9>;
};
gpu_vdd_cdev {
- trip = <&aoss0_trip>;
+ trip = <&cpu5_trip>;
cooling-device = <&msm_gpu 1 1>;
};
cx_vdd_cdev {
@@ -812,7 +812,7 @@
cooling-device = <&CPU6 9 9>;
};
gpu_vdd_cdev {
- trip = <&aoss0_trip>;
+ trip = <&l3_0_trip>;
cooling-device = <&msm_gpu 1 1>;
};
cx_vdd_cdev {
@@ -861,7 +861,7 @@
cooling-device = <&CPU6 9 9>;
};
gpu_vdd_cdev {
- trip = <&aoss0_trip>;
+ trip = <&l3_1_trip>;
cooling-device = <&msm_gpu 1 1>;
};
cx_vdd_cdev {
@@ -910,7 +910,7 @@
cooling-device = <&CPU6 9 9>;
};
gpu_vdd_cdev {
- trip = <&aoss0_trip>;
+ trip = <&cpug0_trip>;
cooling-device = <&msm_gpu 1 1>;
};
cx_vdd_cdev {
@@ -959,7 +959,7 @@
cooling-device = <&CPU6 9 9>;
};
gpu_vdd_cdev {
- trip = <&aoss0_trip>;
+ trip = <&cpug1_trip>;
cooling-device = <&msm_gpu 1 1>;
};
cx_vdd_cdev {
@@ -1008,7 +1008,7 @@
cooling-device = <&CPU6 9 9>;
};
gpu_vdd_cdev {
- trip = <&aoss0_trip>;
+ trip = <&gpu0_trip_l>;
cooling-device = <&msm_gpu 1 1>;
};
cx_vdd_cdev {
@@ -1057,7 +1057,7 @@
cooling-device = <&CPU6 9 9>;
};
gpu_vdd_cdev {
- trip = <&aoss0_trip>;
+ trip = <&gpu1_trip_l>;
cooling-device = <&msm_gpu 1 1>;
};
cx_vdd_cdev {
@@ -1106,7 +1106,7 @@
cooling-device = <&CPU6 9 9>;
};
gpu_vdd_cdev {
- trip = <&aoss0_trip>;
+ trip = <&aoss1_trip>;
cooling-device = <&msm_gpu 1 1>;
};
cx_vdd_cdev {
@@ -1155,7 +1155,7 @@
cooling-device = <&CPU6 9 9>;
};
gpu_vdd_cdev {
- trip = <&aoss0_trip>;
+ trip = <&dsp_trip>;
cooling-device = <&msm_gpu 1 1>;
};
cx_vdd_cdev {
@@ -1204,7 +1204,7 @@
cooling-device = <&CPU6 9 9>;
};
gpu_vdd_cdev {
- trip = <&aoss0_trip>;
+ trip = <&ddr_trip>;
cooling-device = <&msm_gpu 1 1>;
};
cx_vdd_cdev {
@@ -1253,7 +1253,7 @@
cooling-device = <&CPU6 9 9>;
};
gpu_vdd_cdev {
- trip = <&aoss0_trip>;
+ trip = <&wlan_trip>;
cooling-device = <&msm_gpu 1 1>;
};
cx_vdd_cdev {
@@ -1302,7 +1302,7 @@
cooling-device = <&CPU6 9 9>;
};
gpu_vdd_cdev {
- trip = <&aoss0_trip>;
+ trip = <&hvx_trip>;
cooling-device = <&msm_gpu 1 1>;
};
cx_vdd_cdev {
@@ -1351,7 +1351,7 @@
cooling-device = <&CPU6 9 9>;
};
gpu_vdd_cdev {
- trip = <&aoss0_trip>;
+ trip = <&camera_trip>;
cooling-device = <&msm_gpu 1 1>;
};
cx_vdd_cdev {
@@ -1400,7 +1400,7 @@
cooling-device = <&CPU6 9 9>;
};
gpu_vdd_cdev {
- trip = <&aoss0_trip>;
+ trip = <&mmss_trip>;
cooling-device = <&msm_gpu 1 1>;
};
cx_vdd_cdev {
@@ -1449,7 +1449,7 @@
cooling-device = <&CPU6 9 9>;
};
gpu_vdd_cdev {
- trip = <&aoss0_trip>;
+ trip = <&mdm_trip>;
cooling-device = <&msm_gpu 1 1>;
};
cx_vdd_cdev {
diff --git a/arch/arm64/boot/dts/qcom/sdm670.dtsi b/arch/arm64/boot/dts/qcom/sdm670.dtsi
index 9aafe03..7efdde1 100644
--- a/arch/arm64/boot/dts/qcom/sdm670.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm670.dtsi
@@ -56,7 +56,7 @@
reg = <0x0 0x0>;
enable-method = "psci";
efficiency = <1024>;
- cache-size = <0x8000>;
+ cache-size = <0x10000>;
cpu-release-addr = <0x0 0x90000000>;
next-level-cache = <&L2_0>;
sched-energy-costs = <&CPU_COST_0 &CLUSTER_COST_0>;
@@ -75,11 +75,11 @@
};
L1_I_0: l1-icache {
compatible = "arm,arch-cache";
- qcom,dump-size = <0x9000>;
+ qcom,dump-size = <0x12000>;
};
L1_D_0: l1-dcache {
compatible = "arm,arch-cache";
- qcom,dump-size = <0x9000>;
+ qcom,dump-size = <0xa000>;
};
L1_TLB_0: l1-tlb {
qcom,dump-size = <0x3000>;
@@ -92,7 +92,7 @@
reg = <0x0 0x100>;
enable-method = "psci";
efficiency = <1024>;
- cache-size = <0x8000>;
+ cache-size = <0x10000>;
cpu-release-addr = <0x0 0x90000000>;
next-level-cache = <&L2_100>;
sched-energy-costs = <&CPU_COST_0 &CLUSTER_COST_0>;
@@ -106,11 +106,11 @@
};
L1_I_100: l1-icache {
compatible = "arm,arch-cache";
- qcom,dump-size = <0x9000>;
+ qcom,dump-size = <0x12000>;
};
L1_D_100: l1-dcache {
compatible = "arm,arch-cache";
- qcom,dump-size = <0x9000>;
+ qcom,dump-size = <0xa000>;
};
L1_TLB_100: l1-tlb {
qcom,dump-size = <0x3000>;
@@ -123,7 +123,7 @@
reg = <0x0 0x200>;
enable-method = "psci";
efficiency = <1024>;
- cache-size = <0x8000>;
+ cache-size = <0x10000>;
cpu-release-addr = <0x0 0x90000000>;
next-level-cache = <&L2_200>;
sched-energy-costs = <&CPU_COST_0 &CLUSTER_COST_0>;
@@ -137,11 +137,11 @@
};
L1_I_200: l1-icache {
compatible = "arm,arch-cache";
- qcom,dump-size = <0x9000>;
+ qcom,dump-size = <0x12000>;
};
L1_D_200: l1-dcache {
compatible = "arm,arch-cache";
- qcom,dump-size = <0x9000>;
+ qcom,dump-size = <0xa000>;
};
L1_TLB_200: l1-tlb {
qcom,dump-size = <0x3000>;
@@ -154,7 +154,7 @@
reg = <0x0 0x300>;
enable-method = "psci";
efficiency = <1024>;
- cache-size = <0x8000>;
+ cache-size = <0x10000>;
cpu-release-addr = <0x0 0x90000000>;
next-level-cache = <&L2_300>;
sched-energy-costs = <&CPU_COST_0 &CLUSTER_COST_0>;
@@ -168,11 +168,11 @@
};
L1_I_300: l1-icache {
compatible = "arm,arch-cache";
- qcom,dump-size = <0x9000>;
+ qcom,dump-size = <0x12000>;
};
L1_D_300: l1-dcache {
compatible = "arm,arch-cache";
- qcom,dump-size = <0x9000>;
+ qcom,dump-size = <0xa000>;
};
L1_TLB_300: l1-tlb {
qcom,dump-size = <0x3000>;
@@ -185,7 +185,7 @@
reg = <0x0 0x400>;
enable-method = "psci";
efficiency = <1024>;
- cache-size = <0x8000>;
+ cache-size = <0x10000>;
cpu-release-addr = <0x0 0x90000000>;
next-level-cache = <&L2_400>;
sched-energy-costs = <&CPU_COST_0 &CLUSTER_COST_0>;
@@ -199,11 +199,11 @@
};
L1_I_400: l1-icache {
compatible = "arm,arch-cache";
- qcom,dump-size = <0x9000>;
+ qcom,dump-size = <0x12000>;
};
L1_D_400: l1-dcache {
compatible = "arm,arch-cache";
- qcom,dump-size = <0x9000>;
+ qcom,dump-size = <0xa000>;
};
L1_TLB_400: l1-tlb {
qcom,dump-size = <0x3000>;
@@ -216,7 +216,7 @@
reg = <0x0 0x500>;
enable-method = "psci";
efficiency = <1024>;
- cache-size = <0x8000>;
+ cache-size = <0x10000>;
cpu-release-addr = <0x0 0x90000000>;
next-level-cache = <&L2_500>;
sched-energy-costs = <&CPU_COST_0 &CLUSTER_COST_0>;
@@ -230,11 +230,11 @@
};
L1_I_500: l1-icache {
compatible = "arm,arch-cache";
- qcom,dump-size = <0x9000>;
+ qcom,dump-size = <0x12000>;
};
L1_D_500: l1-dcache {
compatible = "arm,arch-cache";
- qcom,dump-size = <0x9000>;
+ qcom,dump-size = <0xa000>;
};
L1_TLB_500: l1-tlb {
qcom,dump-size = <0x3000>;
@@ -247,7 +247,7 @@
reg = <0x0 0x600>;
enable-method = "psci";
efficiency = <1740>;
- cache-size = <0x10000>;
+ cache-size = <0x20000>;
cpu-release-addr = <0x0 0x90000000>;
next-level-cache = <&L2_600>;
sched-energy-costs = <&CPU_COST_1 &CLUSTER_COST_1>;
@@ -261,11 +261,11 @@
};
L1_I_600: l1-icache {
compatible = "arm,arch-cache";
- qcom,dump-size = <0x12000>;
+ qcom,dump-size = <0x24000>;
};
L1_D_600: l1-dcache {
compatible = "arm,arch-cache";
- qcom,dump-size = <0x12000>;
+ qcom,dump-size = <0x14000>;
};
L1_TLB_600: l1-tlb {
qcom,dump-size = <0x3c000>;
@@ -278,7 +278,7 @@
reg = <0x0 0x700>;
enable-method = "psci";
efficiency = <1740>;
- cache-size = <0x10000>;
+ cache-size = <0x20000>;
cpu-release-addr = <0x0 0x90000000>;
next-level-cache = <&L2_700>;
sched-energy-costs = <&CPU_COST_1 &CLUSTER_COST_1>;
@@ -292,11 +292,11 @@
};
L1_I_700: l1-icache {
compatible = "arm,arch-cache";
- qcom,dump-size = <0x12000>;
+ qcom,dump-size = <0x24000>;
};
L1_D_700: l1-dcache {
compatible = "arm,arch-cache";
- qcom,dump-size = <0x12000>;
+ qcom,dump-size = <0x14000>;
};
L1_TLB_700: l1-tlb {
qcom,dump-size = <0x3c000>;
@@ -612,6 +612,94 @@
ranges = <0 0 0 0xffffffff>;
compatible = "simple-bus";
+ jtag_mm0: jtagmm@7040000 {
+ compatible = "qcom,jtagv8-mm";
+ reg = <0x7040000 0x1000>;
+ reg-names = "etm-base";
+
+ clocks = <&clock_aop QDSS_CLK>;
+ clock-names = "core_clk";
+
+ qcom,coresight-jtagmm-cpu = <&CPU0>;
+ };
+
+ jtag_mm1: jtagmm@7140000 {
+ compatible = "qcom,jtagv8-mm";
+ reg = <0x7140000 0x1000>;
+ reg-names = "etm-base";
+
+ clocks = <&clock_aop QDSS_CLK>;
+ clock-names = "core_clk";
+
+ qom,coresight-jtagmm-cpu = <&CPU1>;
+ };
+
+ jtag_mm2: jtagmm@7240000 {
+ compatible = "qcom,jtagv8-mm";
+ reg = <0x7240000 0x1000>;
+ reg-names = "etm-base";
+
+ clocks = <&clock_aop QDSS_CLK>;
+ clock-names = "core_clk";
+
+ qcom,coresight-jtagmm-cpu = <&CPU2>;
+ };
+
+ jtag_mm3: jtagmm@7340000 {
+ compatible = "qcom,jtagv8-mm";
+ reg = <0x7340000 0x1000>;
+ reg-names = "etm-base";
+
+ clocks = <&clock_aop QDSS_CLK>;
+ clock-names = "core_clk";
+
+ qcom,coresight-jtagmm-cpu = <&CPU3>;
+ };
+
+ jtag_mm4: jtagmm@7440000 {
+ compatible = "qcom,jtagv8-mm";
+ reg = <0x7440000 0x1000>;
+ reg-names = "etm-base";
+
+ clocks = <&clock_aop QDSS_CLK>;
+ clock-names = "core_clk";
+
+ qcom,coresight-jtagmm-cpu = <&CPU4>;
+ };
+
+ jtag_mm5: jtagmm@7540000 {
+ compatible = "qcom,jtagv8-mm";
+ reg = <0x7540000 0x1000>;
+ reg-names = "etm-base";
+
+ clocks = <&clock_aop QDSS_CLK>;
+ clock-names = "core_clk";
+
+ qcom,coresight-jtagmm-cpu = <&CPU5>;
+ };
+
+ jtag_mm6: jtagmm@7640000 {
+ compatible = "qcom,jtagv8-mm";
+ reg = <0x7640000 0x1000>;
+ reg-names = "etm-base";
+
+ clocks = <&clock_aop QDSS_CLK>;
+ clock-names = "core_clk";
+
+ qcom,coresight-jtagmm-cpu = <&CPU6>;
+ };
+
+ jtag_mm7: jtagmm@7740000 {
+ compatible = "qcom,jtagv8-mm";
+ reg = <0x7740000 0x1000>;
+ reg-names = "etm-base";
+
+ clocks = <&clock_aop QDSS_CLK>;
+ clock-names = "core_clk";
+
+ qcom,coresight-jtagmm-cpu = <&CPU7>;
+ };
+
intc: interrupt-controller@17a00000 {
compatible = "arm,gic-v3";
#interrupt-cells = <3>;
@@ -638,6 +726,73 @@
qcom,pipe-attr-ee;
};
+ qcom_cedev: qcedev@1de0000 {
+ compatible = "qcom,qcedev";
+ reg = <0x1de0000 0x20000>,
+ <0x1dc4000 0x24000>;
+ reg-names = "crypto-base","crypto-bam-base";
+ interrupts = <0 272 0>;
+ qcom,bam-pipe-pair = <3>;
+ qcom,ce-hw-instance = <0>;
+ qcom,ce-device = <0>;
+ qcom,ce-hw-shared;
+ qcom,bam-ee = <0>;
+ qcom,msm-bus,name = "qcedev-noc";
+ qcom,msm-bus,num-cases = <2>;
+ qcom,msm-bus,num-paths = <1>;
+ qcom,msm-bus,vectors-KBps =
+ <125 512 0 0>,
+ <125 512 393600 393600>;
+ clock-names = "core_clk_src", "core_clk",
+ "iface_clk", "bus_clk";
+ clocks = <&clock_gcc GCC_CE1_CLK>,
+ <&clock_gcc GCC_CE1_CLK>,
+ <&clock_gcc GCC_CE1_AHB_CLK>,
+ <&clock_gcc GCC_CE1_AXI_CLK>;
+ qcom,ce-opp-freq = <171430000>;
+ qcom,request-bw-before-clk;
+ qcom,smmu-s1-bypass;
+ iommus = <&apps_smmu 0x706 0x3>,
+ <&apps_smmu 0x716 0x3>;
+ };
+
+ qcom_crypto: qcrypto@1de0000 {
+ compatible = "qcom,qcrypto";
+ reg = <0x1de0000 0x20000>,
+ <0x1dc4000 0x24000>;
+ reg-names = "crypto-base","crypto-bam-base";
+ interrupts = <0 272 0>;
+ qcom,bam-pipe-pair = <2>;
+ qcom,ce-hw-instance = <0>;
+ qcom,ce-device = <0>;
+ qcom,bam-ee = <0>;
+ qcom,ce-hw-shared;
+ qcom,clk-mgmt-sus-res;
+ qcom,msm-bus,name = "qcrypto-noc";
+ qcom,msm-bus,num-cases = <2>;
+ qcom,msm-bus,num-paths = <1>;
+ qcom,msm-bus,vectors-KBps =
+ <125 512 0 0>,
+ <125 512 393600 393600>;
+ clock-names = "core_clk_src", "core_clk",
+ "iface_clk", "bus_clk";
+ clocks = <&clock_gcc GCC_CE1_CLK>,
+ <&clock_gcc GCC_CE1_CLK>,
+ <&clock_gcc GCC_CE1_AHB_CLK>,
+ <&clock_gcc GCC_CE1_AXI_CLK>;
+ qcom,ce-opp-freq = <171430000>;
+ qcom,request-bw-before-clk;
+ qcom,use-sw-aes-cbc-ecb-ctr-algo;
+ qcom,use-sw-aes-xts-algo;
+ qcom,use-sw-aes-ccm-algo;
+ qcom,use-sw-aead-algo;
+ qcom,use-sw-ahash-algo;
+ qcom,use-sw-hmac-algo;
+ qcom,smmu-s1-bypass;
+ iommus = <&apps_smmu 0x704 0x3>,
+ <&apps_smmu 0x714 0x3>;
+ };
+
qcom,qbt1000 {
compatible = "qcom,qbt1000";
clock-names = "core", "iface";
@@ -646,6 +801,58 @@
qcom,finger-detect-gpio = <&tlmm 122 0>;
};
+ qcom_seecom: qseecom@86d00000 {
+ compatible = "qcom,qseecom";
+ reg = <0x86d00000 0x2200000>;
+ reg-names = "secapp-region";
+ qcom,hlos-num-ce-hw-instances = <1>;
+ qcom,hlos-ce-hw-instance = <0>;
+ qcom,qsee-ce-hw-instance = <0>;
+ qcom,disk-encrypt-pipe-pair = <2>;
+ qcom,support-fde;
+ qcom,no-clock-support;
+ qcom,appsbl-qseecom-support;
+ qcom,msm-bus,name = "qseecom-noc";
+ qcom,msm-bus,num-cases = <4>;
+ qcom,msm-bus,num-paths = <1>;
+ qcom,msm-bus,vectors-KBps =
+ <125 512 0 0>,
+ <125 512 200000 400000>,
+ <125 512 300000 800000>,
+ <125 512 400000 1000000>;
+ clock-names = "core_clk_src", "core_clk",
+ "iface_clk", "bus_clk";
+ clocks = <&clock_gcc GCC_CE1_CLK>,
+ <&clock_gcc GCC_CE1_CLK>,
+ <&clock_gcc GCC_CE1_AHB_CLK>,
+ <&clock_gcc GCC_CE1_AXI_CLK>;
+ qcom,ce-opp-freq = <171430000>;
+ qcom,qsee-reentrancy-support = <2>;
+ };
+
+ qcom_tzlog: tz-log@146bf720 {
+ compatible = "qcom,tz-log";
+ reg = <0x146bf720 0x3000>;
+ qcom,hyplog-enabled;
+ hyplog-address-offset = <0x410>;
+ hyplog-size-offset = <0x414>;
+ };
+
+ qcom_rng: qrng@793000{
+ compatible = "qcom,msm-rng";
+ reg = <0x793000 0x1000>;
+ qcom,msm-rng-iface-clk;
+ qcom,no-qrng-config;
+ qcom,msm-bus,name = "msm-rng-noc";
+ qcom,msm-bus,num-cases = <2>;
+ qcom,msm-bus,num-paths = <1>;
+ qcom,msm-bus,vectors-KBps =
+ <1 618 0 0>, /* No vote */
+ <1 618 0 800>; /* 100 KHz */
+ clocks = <&clock_gcc GCC_PRNG_AHB_CLK>;
+ clock-names = "iface_clk";
+ };
+
thermal_zones: thermal-zones {};
tsens0: tsens@c222000 {
@@ -872,7 +1079,7 @@
reg-names = "slimbus_physical", "slimbus_bam_physical";
interrupts = <0 291 0>, <0 292 0>;
interrupt-names = "slimbus_irq", "slimbus_bam_irq";
- status = "disabled";
+ status = "ok";
qcom,iommu-s1-bypass;
iommu_slim_qca_ctrl_cb: qcom,iommu_slim_ctrl_cb {
@@ -880,13 +1087,20 @@
iommus = <&apps_smmu 0x1833 0x0>;
};
+ /* Slimbus Slave DT for WCN3990 */
+ btfmslim_codec: wcn3990 {
+ compatible = "qcom,btfmslim_slave";
+ elemental-addr = [00 01 20 02 17 02];
+ qcom,btfm-slim-ifd = "btfmslim_slave_ifd";
+ qcom,btfm-slim-ifd-elemental-addr = [00 00 20 02 17 02];
+ };
};
wdog: qcom,wdt@17980000{
compatible = "qcom,msm-watchdog";
reg = <0x17980000 0x1000>;
reg-names = "wdt-base";
- interrupts = <0 3 0>, <0 4 0>;
+ interrupts = <0 0 0>, <0 1 0>;
qcom,bark-time = <11000>;
qcom,pet-time = <10000>;
qcom,ipi-ping;
@@ -1554,6 +1768,7 @@
<0 0>,
<0 0>;
+ non-removable;
qcom,msm-bus,name = "ufshc_mem";
qcom,msm-bus,num-cases = <12>;
qcom,msm-bus,num-paths = <2>;
@@ -1637,6 +1852,10 @@
qcom,client-id = <0x00000001>;
};
+ qcom,msm_gsi {
+ compatible = "qcom,msm_gsi";
+ };
+
qcom,rmnet-ipa {
compatible = "qcom,rmnet-ipa3";
qcom,rmnet-ipa-ssr;
@@ -1842,7 +2061,7 @@
qcom,ssctl-instance-id = <0x12>;
qcom,override-acc;
qcom,qdsp6v65-1-0;
- qcom,mss_pdc_offset = <8>;
+ qcom,mss_pdc_offset = <9>;
status = "ok";
memory-region = <&pil_modem_mem>;
qcom,mem-protect-id = <0xF>;
@@ -1942,30 +2161,30 @@
qcom,msm-bus,num-paths = <2>;
qcom,msm-bus,vectors-KBps =
/* No vote */
- <78 512 0 0>, <1 606 0 0>,
+ <150 512 0 0>, <1 606 0 0>,
/* 400 KB/s*/
- <78 512 1046 1600>,
+ <150 512 1046 1600>,
<1 606 1600 1600>,
/* 20 MB/s */
- <78 512 52286 80000>,
+ <150 512 52286 80000>,
<1 606 80000 80000>,
/* 25 MB/s */
- <78 512 65360 100000>,
+ <150 512 65360 100000>,
<1 606 100000 100000>,
/* 50 MB/s */
- <78 512 130718 200000>,
+ <150 512 130718 200000>,
<1 606 133320 133320>,
/* 100 MB/s */
- <78 512 130718 200000>,
+ <150 512 130718 200000>,
<1 606 150000 150000>,
/* 200 MB/s */
- <78 512 261438 400000>,
+ <150 512 261438 400000>,
<1 606 300000 300000>,
/* 400 MB/s */
- <78 512 261438 400000>,
+ <150 512 261438 400000>,
<1 606 300000 300000>,
/* Max. bandwidth */
- <78 512 1338562 4096000>,
+ <150 512 1338562 4096000>,
<1 606 1338562 4096000>;
qcom,bus-bw-vectors-bps = <0 400000 20000000 25000000 50000000
100000000 200000000 400000000 4294967295>;
@@ -1978,8 +2197,15 @@
qcom,pm-qos-legacy-latency-us = <70 70>, <70 70>;
clocks = <&clock_gcc GCC_SDCC1_AHB_CLK>,
- <&clock_gcc GCC_SDCC1_APPS_CLK>;
- clock-names = "iface_clk", "core_clk";
+ <&clock_gcc GCC_SDCC1_APPS_CLK>,
+ <&clock_gcc GCC_SDCC1_ICE_CORE_CLK>,
+ <&clock_gcc GCC_AGGRE_UFS_PHY_AXI_CLK>;
+ clock-names = "iface_clk", "core_clk", "ice_core_clk",
+ "bus_aggr_clk";
+
+ qcom,ice-clk-rates = <300000000 75000000>;
+
+ qcom,ddr-config = <0xC3040873>;
qcom,nonremovable;
@@ -2129,12 +2355,36 @@
iommus = <&apps_smmu 0x1805 0x0>;
dma-coherent;
};
+ qcom,msm_fastrpc_compute_cb12 {
+ compatible = "qcom,msm-fastrpc-compute-cb";
+ label = "adsprpc-smd";
+ iommus = <&apps_smmu 0x1806 0x0>;
+ dma-coherent;
+ };
+ };
+
+ bluetooth: bt_wcn3990 {
+ compatible = "qca,wcn3990";
+ qca,bt-vdd-core-supply = <&pm660_l9>;
+ qca,bt-vdd-pa-supply = <&pm660_l6>;
+ qca,bt-vdd-ldo-supply = <&pm660_l19>;
+
+ qca,bt-vdd-core-voltage-level = <1800000 1900000>;
+ qca,bt-vdd-pa-voltage-level = <1304000 1370000>;
+ qca,bt-vdd-ldo-voltage-level = <3312000 3400000>;
+
+ qca,bt-vdd-core-current-level = <1>; /* LPM/PFM */
+ qca,bt-vdd-pa-current-level = <1>; /* LPM/PFM */
+ qca,bt-vdd-ldo-current-level = <1>; /* LPM/PFM */
};
qcom,icnss@18800000 {
- status = "disabled";
compatible = "qcom,icnss";
- reg = <0x18800000 0x800000>;
+ reg = <0x18800000 0x800000>,
+ <0xa0000000 0x10000000>,
+ <0xb0000000 0x10000>;
+ reg-names = "membase", "smmu_iova_base", "smmu_iova_ipa";
+ iommus = <&apps_smmu 0x0040 0x1>;
interrupts = <0 414 0 /* CE0 */ >,
<0 415 0 /* CE1 */ >,
<0 416 0 /* CE2 */ >,
@@ -2147,6 +2397,10 @@
<0 423 0 /* CE9 */ >,
<0 424 0 /* CE10 */ >,
<0 425 0 /* CE11 */ >;
+ vdd-0.8-cx-mx-supply = <&pm660_l5>;
+ vdd-1.8-xo-supply = <&pm660_l9>;
+ vdd-1.3-rfa-supply = <&pm660_l6>;
+ vdd-3.3-ch0-supply = <&pm660_l19>;
qcom,wlan-msa-memory = <0x100000>;
qcom,smmu-s1-bypass;
};
@@ -2155,32 +2409,7 @@
compatible = "qcom,devbw";
governor = "performance";
qcom,src-dst-ports =
- <MSM_BUS_MASTER_AMPSS_M0 MSM_BUS_SLAVE_LLCC>;
- qcom,active-only;
- qcom,bw-tbl =
- < MHZ_TO_MBPS(150, 16) >, /* 2288 MB/s */
- < MHZ_TO_MBPS(300, 16) >, /* 4577 MB/s */
- < MHZ_TO_MBPS(466, 16) >, /* 7110 MB/s */
- < MHZ_TO_MBPS(600, 16) >, /* 9155 MB/s */
- < MHZ_TO_MBPS(806, 16) >, /* 12298 MB/s */
- < MHZ_TO_MBPS(933, 16) >; /* 14236 MB/s */
- };
-
- bwmon: qcom,cpu-bwmon {
- compatible = "qcom,bimc-bwmon4";
- reg = <0x1436400 0x300>, <0x1436300 0x200>;
- reg-names = "base", "global_base";
- interrupts = <0 581 4>;
- qcom,mport = <0>;
- qcom,hw-timer-hz = <19200000>;
- qcom,target-dev = <&cpubw>;
- };
-
- llccbw: qcom,llccbw {
- compatible = "qcom,devbw";
- governor = "powersave";
- qcom,src-dst-ports =
- <MSM_BUS_MASTER_LLCC MSM_BUS_SLAVE_EBI_CH0>;
+ <MSM_BUS_MASTER_AMPSS_M0 MSM_BUS_SLAVE_EBI_CH0>;
qcom,active-only;
qcom,bw-tbl =
< MHZ_TO_MBPS( 100, 4) >, /* 381 MB/s */
@@ -2196,16 +2425,15 @@
< MHZ_TO_MBPS(1804, 4) >; /* 6881 MB/s */
};
- llcc_bwmon: qcom,llcc-bwmon {
- compatible = "qcom,bimc-bwmon5";
- reg = <0x0114a000 0x1000>;
- reg-names = "base";
- interrupts = <GIC_SPI 580 IRQ_TYPE_LEVEL_HIGH>;
+ bwmon: qcom,cpu-bwmon {
+ compatible = "qcom,bimc-bwmon4";
+ reg = <0x1436400 0x300>, <0x1436300 0x200>;
+ reg-names = "base", "global_base";
+ interrupts = <0 581 4>;
+ qcom,mport = <0>;
+ qcom,count-unit = <0x10000>;
qcom,hw-timer-hz = <19200000>;
- qcom,target-dev = <&llccbw>;
- qcom,count-unit = <0x200000>;
- qcom,byte-mid-mask = <0xe000>;
- qcom,byte-mid-match = <0xe000>;
+ qcom,target-dev = <&cpubw>;
};
memlat_cpu0: qcom,memlat-cpu0 {
@@ -2227,7 +2455,7 @@
< MHZ_TO_MBPS(1804, 4) >; /* 6881 MB/s */
};
- memlat_cpu4: qcom,memlat-cpu4 {
+ memlat_cpu6: qcom,memlat-cpu6 {
compatible = "qcom,devbw";
governor = "powersave";
qcom,src-dst-ports = <1 512>;
@@ -2249,9 +2477,9 @@
devfreq_memlat_0: qcom,cpu0-memlat-mon {
compatible = "qcom,arm-memlat-mon";
- qcom,cpulist = <&CPU0 &CPU1 &CPU2 &CPU3>;
+ qcom,cpulist = <&CPU0 &CPU1 &CPU2 &CPU3 &CPU4 &CPU5>;
qcom,target-dev = <&memlat_cpu0>;
- qcom,cachemiss-ev = <0x24>;
+ qcom,cachemiss-ev = <0x2a>;
qcom,core-dev-table =
< 748800 MHZ_TO_MBPS( 300, 4) >,
< 998400 MHZ_TO_MBPS( 451, 4) >,
@@ -2260,11 +2488,11 @@
< 1728000 MHZ_TO_MBPS(1017, 4) >;
};
- devfreq_memlat_4: qcom,cpu4-memlat-mon {
+ devfreq_memlat_6: qcom,cpu6-memlat-mon {
compatible = "qcom,arm-memlat-mon";
- qcom,cpulist = <&CPU4 &CPU5 &CPU6 &CPU7>;
- qcom,target-dev = <&memlat_cpu4>;
- qcom,cachemiss-ev = <0x24>;
+ qcom,cpulist = <&CPU6 &CPU7>;
+ qcom,target-dev = <&memlat_cpu6>;
+ qcom,cachemiss-ev = <0x2a>;
qcom,core-dev-table =
< 787200 MHZ_TO_MBPS( 300, 4) >,
< 1113600 MHZ_TO_MBPS( 547, 4) >,
@@ -2280,7 +2508,7 @@
governor = "performance";
};
- l3_cpu4: qcom,l3-cpu4 {
+ l3_cpu6: qcom,l3-cpu6 {
compatible = "devfreq-simple-dev";
clock-names = "devfreq_clk";
clocks = <&clock_cpucc L3_CLUSTER1_VOTE_CLK>;
@@ -2289,7 +2517,7 @@
devfreq_l3lat_0: qcom,cpu0-l3lat-mon {
compatible = "qcom,arm-memlat-mon";
- qcom,cpulist = <&CPU0 &CPU1 &CPU2 &CPU3>;
+ qcom,cpulist = <&CPU0 &CPU1 &CPU2 &CPU3 &CPU4 &CPU5>;
qcom,target-dev = <&l3_cpu0>;
qcom,cachemiss-ev = <0x17>;
qcom,core-dev-table =
@@ -2301,10 +2529,10 @@
< 1728000 1440000000 >;
};
- devfreq_l3lat_4: qcom,cpu4-l3lat-mon {
+ devfreq_l3lat_6: qcom,cpu6-l3lat-mon {
compatible = "qcom,arm-memlat-mon";
- qcom,cpulist = <&CPU4 &CPU5 &CPU6 &CPU7>;
- qcom,target-dev = <&l3_cpu4>;
+ qcom,cpulist = <&CPU6 &CPU7>;
+ qcom,target-dev = <&l3_cpu6>;
qcom,cachemiss-ev = <0x17>;
qcom,core-dev-table =
< 1113600 566400000 >,
@@ -2341,7 +2569,7 @@
< 1209600 MHZ_TO_MBPS( 451, 4) >,
< 1612000 MHZ_TO_MBPS( 547, 4) >,
< 1728000 MHZ_TO_MBPS( 768, 4) >;
- cpu-to-dev-map-4 =
+ cpu-to-dev-map-6 =
< 1113600 MHZ_TO_MBPS( 300, 4) >,
< 1344000 MHZ_TO_MBPS( 547, 4) >,
< 1728000 MHZ_TO_MBPS( 768, 4) >,
@@ -2495,3 +2723,7 @@
#include "sdm670-gpu.dtsi"
#include "sdm670-thermal.dtsi"
#include "sdm670-bus.dtsi"
+
+&pm660_div_clk {
+ status = "ok";
+};
diff --git a/arch/arm64/boot/dts/qcom/sdm845-camera-sensor-cdp.dtsi b/arch/arm64/boot/dts/qcom/sdm845-camera-sensor-cdp.dtsi
index 04418d4..7ca2645 100644
--- a/arch/arm64/boot/dts/qcom/sdm845-camera-sensor-cdp.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm845-camera-sensor-cdp.dtsi
@@ -122,12 +122,13 @@
cam_vana-supply = <&pmi8998_bob>;
cam_vdig-supply = <&camera_rear_ldo>;
cam_clk-supply = <&titan_top_gdsc>;
+ cam_vaf-supply = <&actuator_regulator>;
regulator-names = "cam_vio", "cam_vana", "cam_vdig",
- "cam_clk";
+ "cam_clk", "cam_vaf";
rgltr-cntrl-support;
- rgltr-min-voltage = <0 3312000 1050000 0>;
- rgltr-max-voltage = <0 3600000 1050000 0>;
- rgltr-load-current = <0 80000 105000 0>;
+ rgltr-min-voltage = <0 3312000 1050000 0 2800000>;
+ rgltr-max-voltage = <0 3600000 1050000 0 2800000>;
+ rgltr-load-current = <0 80000 105000 0 0>;
gpio-no-mux = <0>;
pinctrl-names = "cam_default", "cam_suspend";
pinctrl-0 = <&cam_sensor_mclk0_active
@@ -136,17 +137,14 @@
&cam_sensor_rear_suspend>;
gpios = <&tlmm 13 0>,
<&tlmm 80 0>,
- <&tlmm 79 0>,
- <&tlmm 27 0>;
+ <&tlmm 79 0>;
gpio-reset = <1>;
gpio-vana = <2>;
- gpio-vaf = <3>;
- gpio-req-tbl-num = <0 1 2 3>;
- gpio-req-tbl-flags = <1 0 0 0>;
+ gpio-req-tbl-num = <0 1 2>;
+ gpio-req-tbl-flags = <1 0 0>;
gpio-req-tbl-label = "CAMIF_MCLK0",
"CAM_RESET0",
- "CAM_VANA0",
- "CAM_VAF";
+ "CAM_VANA0";
sensor-position = <0>;
sensor-mode = <0>;
cci-master = <0>;
@@ -165,12 +163,13 @@
cam_vio-supply = <&pm8998_lvs1>;
cam_vana-supply = <&pmi8998_bob>;
cam_clk-supply = <&titan_top_gdsc>;
+ cam_vaf-supply = <&actuator_regulator>;
regulator-names = "cam_vdig", "cam_vio", "cam_vana",
- "cam_clk";
+ "cam_clk", "cam_vaf";
rgltr-cntrl-support;
- rgltr-min-voltage = <1050000 0 3312000 0>;
- rgltr-max-voltage = <1050000 0 3600000 0>;
- rgltr-load-current = <105000 0 80000 0>;
+ rgltr-min-voltage = <1050000 0 3312000 0 2800000>;
+ rgltr-max-voltage = <1050000 0 3600000 0 2800000>;
+ rgltr-load-current = <105000 0 80000 0 0>;
gpio-no-mux = <0>;
pinctrl-names = "cam_default", "cam_suspend";
pinctrl-0 = <&cam_sensor_mclk2_active
@@ -205,12 +204,13 @@
cam_vana-supply = <&pmi8998_bob>;
cam_vdig-supply = <&camera_ldo>;
cam_clk-supply = <&titan_top_gdsc>;
+ cam_vaf-supply = <&actuator_regulator>;
regulator-names = "cam_vio", "cam_vana", "cam_vdig",
- "cam_clk";
+ "cam_clk", "cam_vaf";
rgltr-cntrl-support;
- rgltr-min-voltage = <0 3312000 1050000 0>;
- rgltr-max-voltage = <0 3600000 1050000 0>;
- rgltr-load-current = <0 80000 105000 0>;
+ rgltr-min-voltage = <0 3312000 1050000 0 2800000>;
+ rgltr-max-voltage = <0 3600000 1050000 0 2800000>;
+ rgltr-load-current = <0 80000 105000 0 0>;
gpio-no-mux = <0>;
pinctrl-names = "cam_default", "cam_suspend";
pinctrl-0 = <&cam_sensor_mclk1_active
@@ -219,17 +219,14 @@
&cam_sensor_front_suspend>;
gpios = <&tlmm 14 0>,
<&tlmm 28 0>,
- <&tlmm 8 0>,
- <&tlmm 27 0>;
+ <&tlmm 8 0>;
gpio-reset = <1>;
gpio-vana = <2>;
- gpio-vaf = <3>;
- gpio-req-tbl-num = <0 1 2 3>;
- gpio-req-tbl-flags = <1 0 0 0>;
+ gpio-req-tbl-num = <0 1 2>;
+ gpio-req-tbl-flags = <1 0 0>;
gpio-req-tbl-label = "CAMIF_MCLK2",
"CAM_RESET2",
- "CAM_VANA2",
- "CAM_VAF";
+ "CAM_VANA2";
sensor-position = <1>;
sensor-mode = <0>;
cci-master = <1>;
diff --git a/arch/arm64/boot/dts/qcom/sdm845-camera-sensor-mtp.dtsi b/arch/arm64/boot/dts/qcom/sdm845-camera-sensor-mtp.dtsi
index 9088fac..aa55698 100644
--- a/arch/arm64/boot/dts/qcom/sdm845-camera-sensor-mtp.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm845-camera-sensor-mtp.dtsi
@@ -122,12 +122,13 @@
cam_vana-supply = <&pmi8998_bob>;
cam_vdig-supply = <&camera_rear_ldo>;
cam_clk-supply = <&titan_top_gdsc>;
+ cam_vaf-supply = <&actuator_regulator>;
regulator-names = "cam_vio", "cam_vana", "cam_vdig",
- "cam_clk";
+ "cam_clk", "cam_vaf";
rgltr-cntrl-support;
- rgltr-min-voltage = <0 3312000 1050000 0>;
- rgltr-max-voltage = <0 3600000 1050000 0>;
- rgltr-load-current = <0 80000 105000 0>;
+ rgltr-min-voltage = <0 3312000 1050000 0 2800000>;
+ rgltr-max-voltage = <0 3600000 1050000 0 2800000>;
+ rgltr-load-current = <0 80000 105000 0 0>;
gpio-no-mux = <0>;
pinctrl-names = "cam_default", "cam_suspend";
pinctrl-0 = <&cam_sensor_mclk0_active
@@ -136,17 +137,14 @@
&cam_sensor_rear_suspend>;
gpios = <&tlmm 13 0>,
<&tlmm 80 0>,
- <&tlmm 79 0>,
- <&tlmm 27 0>;
+ <&tlmm 79 0>;
gpio-reset = <1>;
gpio-vana = <2>;
- gpio-vaf = <3>;
- gpio-req-tbl-num = <0 1 2 3>;
- gpio-req-tbl-flags = <1 0 0 0>;
+ gpio-req-tbl-num = <0 1 2>;
+ gpio-req-tbl-flags = <1 0 0>;
gpio-req-tbl-label = "CAMIF_MCLK0",
"CAM_RESET0",
- "CAM_VANA0",
- "CAM_VAF";
+ "CAM_VANA0";
sensor-position = <0>;
sensor-mode = <0>;
cci-master = <0>;
@@ -165,12 +163,13 @@
cam_vio-supply = <&pm8998_lvs1>;
cam_vana-supply = <&pmi8998_bob>;
cam_clk-supply = <&titan_top_gdsc>;
+ cam_vaf-supply = <&actuator_regulator>;
regulator-names = "cam_vdig", "cam_vio", "cam_vana",
- "cam_clk";
+ "cam_clk", "cam_vaf";
rgltr-cntrl-support;
- rgltr-min-voltage = <1050000 0 3312000 0>;
- rgltr-max-voltage = <1050000 0 3600000 0>;
- rgltr-load-current = <105000 0 80000 0>;
+ rgltr-min-voltage = <1050000 0 3312000 0 2800000>;
+ rgltr-max-voltage = <1050000 0 3600000 0 2800000>;
+ rgltr-load-current = <105000 0 80000 0 0>;
gpio-no-mux = <0>;
pinctrl-names = "cam_default", "cam_suspend";
pinctrl-0 = <&cam_sensor_mclk2_active
@@ -205,12 +204,13 @@
cam_vana-supply = <&pmi8998_bob>;
cam_vdig-supply = <&camera_ldo>;
cam_clk-supply = <&titan_top_gdsc>;
+ cam_vaf-supply = <&actuator_regulator>;
regulator-names = "cam_vio", "cam_vana", "cam_vdig",
- "cam_clk";
+ "cam_clk", "cam_vaf";
rgltr-cntrl-support;
- rgltr-min-voltage = <0 3312000 1050000 0>;
- rgltr-max-voltage = <0 3600000 1050000 0>;
- rgltr-load-current = <0 80000 105000 0>;
+ rgltr-min-voltage = <0 3312000 1050000 0 2800000>;
+ rgltr-max-voltage = <0 3600000 1050000 0 2800000>;
+ rgltr-load-current = <0 80000 105000 0 0>;
gpio-no-mux = <0>;
pinctrl-names = "cam_default", "cam_suspend";
pinctrl-0 = <&cam_sensor_mclk1_active
@@ -219,17 +219,14 @@
&cam_sensor_front_suspend>;
gpios = <&tlmm 14 0>,
<&tlmm 28 0>,
- <&tlmm 8 0>,
- <&tlmm 27 0>;
+ <&tlmm 8 0>;
gpio-reset = <1>;
gpio-vana = <2>;
- gpio-vaf = <3>;
- gpio-req-tbl-num = <0 1 2 3>;
- gpio-req-tbl-flags = <1 0 0 0>;
+ gpio-req-tbl-num = <0 1 2>;
+ gpio-req-tbl-flags = <1 0 0>;
gpio-req-tbl-label = "CAMIF_MCLK2",
"CAM_RESET2",
- "CAM_VANA2",
- "CAM_VAF";
+ "CAM_VANA2";
sensor-position = <1>;
sensor-mode = <0>;
cci-master = <1>;
diff --git a/arch/arm64/boot/dts/qcom/sdm845-camera.dtsi b/arch/arm64/boot/dts/qcom/sdm845-camera.dtsi
index 7c9482c..db57aae 100644
--- a/arch/arm64/boot/dts/qcom/sdm845-camera.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm845-camera.dtsi
@@ -28,7 +28,7 @@
gdscr-supply = <&titan_top_gdsc>;
regulator-names = "gdscr";
csi-vdd-voltage = <1200000>;
- mipi-csi-vdd-supply = <&pm8998_l26>;
+ mipi-csi-vdd-supply = <&pm8998_l1>;
clocks = <&clock_camcc CAM_CC_CAMNOC_AXI_CLK>,
<&clock_camcc CAM_CC_SOC_AHB_CLK>,
<&clock_camcc CAM_CC_SLOW_AHB_CLK_SRC>,
@@ -62,7 +62,7 @@
gdscr-supply = <&titan_top_gdsc>;
regulator-names = "gdscr";
csi-vdd-voltage = <1200000>;
- mipi-csi-vdd-supply = <&pm8998_l26>;
+ mipi-csi-vdd-supply = <&pm8998_l1>;
clocks = <&clock_camcc CAM_CC_CAMNOC_AXI_CLK>,
<&clock_camcc CAM_CC_SOC_AHB_CLK>,
<&clock_camcc CAM_CC_SLOW_AHB_CLK_SRC>,
@@ -97,7 +97,7 @@
gdscr-supply = <&titan_top_gdsc>;
regulator-names = "gdscr";
csi-vdd-voltage = <1200000>;
- mipi-csi-vdd-supply = <&pm8998_l26>;
+ mipi-csi-vdd-supply = <&pm8998_l1>;
clocks = <&clock_camcc CAM_CC_CAMNOC_AXI_CLK>,
<&clock_camcc CAM_CC_SOC_AHB_CLK>,
<&clock_camcc CAM_CC_SLOW_AHB_CLK_SRC>,
@@ -358,6 +358,7 @@
reg-cam-base = <0x40000 0x42000>;
interrupt-names = "cpas_camnoc";
interrupts = <0 459 0>;
+ qcom,cpas-hw-ver = <0x170100>; /* Titan v170 v1.0.0 */
regulator-names = "camss-vdd";
camss-vdd-supply = <&titan_top_gdsc>;
clock-names = "gcc_ahb_clk",
diff --git a/arch/arm64/boot/dts/qcom/sdm845-cdp.dtsi b/arch/arm64/boot/dts/qcom/sdm845-cdp.dtsi
index 81ce1e5..dffb5e0 100644
--- a/arch/arm64/boot/dts/qcom/sdm845-cdp.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm845-cdp.dtsi
@@ -269,6 +269,27 @@
qcom,platform-reset-gpio = <&tlmm 6 0>;
};
+&dsi_dual_nt35597_video {
+ qcom,panel-supply-entries = <&dsi_panel_pwr_supply>;
+ qcom,mdss-dsi-bl-pmic-control-type = "bl_ctrl_wled";
+ qcom,mdss-dsi-bl-min-level = <1>;
+ qcom,mdss-dsi-bl-max-level = <4095>;
+ qcom,mdss-dsi-mode-sel-gpio-state = "dual_port";
+ qcom,panel-mode-gpio = <&tlmm 52 0>;
+ qcom,platform-reset-gpio = <&tlmm 6 0>;
+};
+
+&dsi_dual_nt35597_cmd {
+ qcom,panel-supply-entries = <&dsi_panel_pwr_supply>;
+ qcom,mdss-dsi-bl-pmic-control-type = "bl_ctrl_wled";
+ qcom,mdss-dsi-bl-min-level = <1>;
+ qcom,mdss-dsi-bl-max-level = <4095>;
+ qcom,mdss-dsi-mode-sel-gpio-state = "dual_port";
+ qcom,panel-mode-gpio = <&tlmm 52 0>;
+ qcom,platform-reset-gpio = <&tlmm 6 0>;
+ qcom,platform-te-gpio = <&tlmm 10 0>;
+};
+
&dsi_nt35597_truly_dsc_cmd_display {
qcom,dsi-display-active;
};
diff --git a/arch/arm64/boot/dts/qcom/sdm845-gpu.dtsi b/arch/arm64/boot/dts/qcom/sdm845-gpu.dtsi
index 11b6a4d..eac21c8 100644
--- a/arch/arm64/boot/dts/qcom/sdm845-gpu.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm845-gpu.dtsi
@@ -58,6 +58,7 @@
qcom,initial-pwrlevel = <5>;
qcom,gpu-quirk-hfi-use-reg;
+ qcom,gpu-quirk-secvid-set-once;
qcom,idle-timeout = <80>; //msecs
qcom,no-nap;
@@ -81,11 +82,10 @@
<&clock_gpucc GPU_CC_CXO_CLK>,
<&clock_gcc GCC_DDRSS_GPU_AXI_CLK>,
<&clock_gcc GCC_GPU_MEMNOC_GFX_CLK>,
- <&clock_gpucc GPU_CC_CX_GMU_CLK>,
- <&clock_gpucc GPU_CC_AHB_CLK>;
+ <&clock_gpucc GPU_CC_CX_GMU_CLK>;
clock-names = "core_clk", "rbbmtimer_clk", "mem_clk",
- "mem_iface_clk", "gmu_clk", "ahb_clk";
+ "mem_iface_clk", "gmu_clk";
qcom,isense-clk-on-level = <1>;
@@ -267,7 +267,8 @@
compatible = "qcom,kgsl-smmu-v2";
reg = <0x05040000 0x10000>;
- qcom,protect = <0x40000 0x10000>;
+ /* CB5(ATOS) & CB5/6/7 are protected by HYP */
+ qcom,protect = <0x40000 0xc000>;
qcom,micro-mmu-control = <0x6000>;
clocks =<&clock_gcc GCC_GPU_CFG_AHB_CLK>,
@@ -277,6 +278,7 @@
clock-names = "iface_clk", "mem_clk", "mem_iface_clk";
qcom,secure_align_mask = <0xfff>;
+ qcom,retention;
qcom,hyp_secure_alloc;
gfx3d_user: gfx3d_user {
@@ -288,7 +290,7 @@
gfx3d_secure: gfx3d_secure {
compatible = "qcom,smmu-kgsl-cb";
- iommus = <&kgsl_smmu 2>;
+ iommus = <&kgsl_smmu 2>, <&kgsl_smmu 1>;
};
};
@@ -317,11 +319,10 @@
clocks = <&clock_gpucc GPU_CC_CX_GMU_CLK>,
<&clock_gpucc GPU_CC_CXO_CLK>,
<&clock_gcc GCC_DDRSS_GPU_AXI_CLK>,
- <&clock_gcc GCC_GPU_MEMNOC_GFX_CLK>,
- <&clock_gpucc GPU_CC_AHB_CLK>;
+ <&clock_gcc GCC_GPU_MEMNOC_GFX_CLK>;
clock-names = "gmu_clk", "cxo_clk", "axi_clk",
- "memnoc_clk", "ahb_clk";
+ "memnoc_clk";
qcom,gmu-pwrlevels {
#address-cells = <1>;
diff --git a/arch/arm64/boot/dts/qcom/sdm845-interposer-pm660.dtsi b/arch/arm64/boot/dts/qcom/sdm845-interposer-pm660.dtsi
index cc609aa..1a8de22 100644
--- a/arch/arm64/boot/dts/qcom/sdm845-interposer-pm660.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm845-interposer-pm660.dtsi
@@ -54,6 +54,18 @@
ibb-supply = <&lcdb_ncp_vreg>;
};
+&dsi_dual_nt35597_video_display {
+ vddio-supply = <&pm660_l11>;
+ lab-supply = <&lcdb_ldo_vreg>;
+ ibb-supply = <&lcdb_ncp_vreg>;
+};
+
+&dsi_dual_nt35597_cmd_display {
+ vddio-supply = <&pm660_l11>;
+ lab-supply = <&lcdb_ldo_vreg>;
+ ibb-supply = <&lcdb_ncp_vreg>;
+};
+
&sde_dp {
status = "disabled";
/delete-property/ vdda-1p2-supply;
@@ -230,12 +242,17 @@
&soc {
/delete-node/ gpio_keys;
+ qcom,mss@4080000 {
+ /delete-property/ vdd_mss-supply;
+ };
+
qcom,lpass@17300000 {
/delete-property/ vdd_cx-supply;
};
qcom,ssc@5c00000 {
/delete-property/ vdd_cx-supply;
+ /delete-property/ vdd_mx-supply;
};
qcom,spss@1880000 {
@@ -325,8 +342,6 @@
&soc {
/* Delete all regulators */
- /delete-node/ cprh-ctrl@17dc0000;
- /delete-node/ cprh-ctrl@17db0000;
/delete-node/ rpmh-regulator-ebilvl;
/delete-node/ rpmh-regulator-smpa2;
/delete-node/ rpmh-regulator-smpa3;
diff --git a/arch/arm64/boot/dts/qcom/sdm845-mtp.dtsi b/arch/arm64/boot/dts/qcom/sdm845-mtp.dtsi
index f0d16ec..48a4a8b 100644
--- a/arch/arm64/boot/dts/qcom/sdm845-mtp.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm845-mtp.dtsi
@@ -164,6 +164,27 @@
qcom,platform-reset-gpio = <&tlmm 6 0>;
};
+&dsi_dual_nt35597_video {
+ qcom,panel-supply-entries = <&dsi_panel_pwr_supply>;
+ qcom,mdss-dsi-bl-pmic-control-type = "bl_ctrl_wled";
+ qcom,mdss-dsi-bl-min-level = <1>;
+ qcom,mdss-dsi-bl-max-level = <4095>;
+ qcom,mdss-dsi-mode-sel-gpio-state = "dual_port";
+ qcom,panel-mode-gpio = <&tlmm 52 0>;
+ qcom,platform-reset-gpio = <&tlmm 6 0>;
+};
+
+&dsi_dual_nt35597_cmd {
+ qcom,panel-supply-entries = <&dsi_panel_pwr_supply>;
+ qcom,mdss-dsi-bl-pmic-control-type = "bl_ctrl_wled";
+ qcom,mdss-dsi-bl-min-level = <1>;
+ qcom,mdss-dsi-bl-max-level = <4095>;
+ qcom,mdss-dsi-mode-sel-gpio-state = "dual_port";
+ qcom,panel-mode-gpio = <&tlmm 52 0>;
+ qcom,platform-reset-gpio = <&tlmm 6 0>;
+ qcom,platform-te-gpio = <&tlmm 10 0>;
+};
+
&dsi_nt35597_truly_dsc_cmd_display {
qcom,dsi-display-active;
};
diff --git a/arch/arm64/boot/dts/qcom/sdm845-pcie.dtsi b/arch/arm64/boot/dts/qcom/sdm845-pcie.dtsi
index 322c5f5..daf5687 100644
--- a/arch/arm64/boot/dts/qcom/sdm845-pcie.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm845-pcie.dtsi
@@ -214,7 +214,8 @@
qcom,smmu-sid-base = <0x1c10>;
- iommu-map = <0x100 &apps_smmu 0x1c11 0x1>,
+ iommu-map = <0x0 &apps_smmu 0x1c10 0x1>,
+ <0x100 &apps_smmu 0x1c11 0x1>,
<0x200 &apps_smmu 0x1c12 0x1>,
<0x300 &apps_smmu 0x1c13 0x1>,
<0x400 &apps_smmu 0x1c14 0x1>,
@@ -547,7 +548,8 @@
qcom,smmu-sid-base = <0x1c00>;
- iommu-map = <0x100 &apps_smmu 0x1c01 0x1>,
+ iommu-map = <0x0 &apps_smmu 0x1c00 0x1>,
+ <0x100 &apps_smmu 0x1c01 0x1>,
<0x200 &apps_smmu 0x1c02 0x1>,
<0x300 &apps_smmu 0x1c03 0x1>,
<0x400 &apps_smmu 0x1c04 0x1>,
diff --git a/arch/arm64/boot/dts/qcom/sdm845-pinctrl-overlay.dtsi b/arch/arm64/boot/dts/qcom/sdm845-pinctrl-overlay.dtsi
index 8dd75b69..67933c3 100644
--- a/arch/arm64/boot/dts/qcom/sdm845-pinctrl-overlay.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm845-pinctrl-overlay.dtsi
@@ -20,6 +20,18 @@
};
};
+ qnovo_fet_ctrl {
+ qnovo_fet_ctrl_default: qnovo_fet_ctrl_default {
+ pins = "gpio6";
+ function = "func1";
+ output-low;
+ input-disable;
+ bias-disable;
+ power-source = <0>;
+ qcom,drive-strength = <1>;
+ };
+ };
+
usb2_vbus_det {
usb2_vbus_det_default: usb2_vbus_det_default {
pins = "gpio8";
diff --git a/arch/arm64/boot/dts/qcom/sdm845-pm.dtsi b/arch/arm64/boot/dts/qcom/sdm845-pm.dtsi
index 73b0a32..8f1afe9 100644
--- a/arch/arm64/boot/dts/qcom/sdm845-pm.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm845-pm.dtsi
@@ -34,51 +34,15 @@
qcom,time-overhead = <99>;
};
- qcom,pm-cluster-level@1 { /* D2 */
+ qcom,pm-cluster-level@1 { /* LLCC off, AOSS sleep */
reg = <1>;
- label = "l3-dyn-ret";
- qcom,psci-mode = <0x2>;
- qcom,latency-us = <659>;
- qcom,ss-power = <434>;
- qcom,energy-overhead = <465725>;
- qcom,time-overhead = <976>;
- qcom,min-child-idx = <1>;
- };
-
- qcom,pm-cluster-level@2 { /* D4, D3 is not supported */
- reg = <2>;
- label = "l3-pc";
- qcom,psci-mode = <0x4>;
- qcom,latency-us = <3201>;
- qcom,ss-power = <408>;
- qcom,energy-overhead = <2421840>;
- qcom,time-overhead = <5376>;
- qcom,min-child-idx = <2>;
- qcom,is-reset;
- };
-
- qcom,pm-cluster-level@3 { /* Cx off */
- reg = <3>;
- label = "cx-off";
- qcom,psci-mode = <0x224>;
- qcom,latency-us = <5562>;
- qcom,ss-power = <308>;
- qcom,energy-overhead = <2521840>;
- qcom,time-overhead = <6376>;
- qcom,min-child-idx = <3>;
- qcom,is-reset;
- qcom,notify-rpm;
- };
-
- qcom,pm-cluster-level@4 { /* LLCC off, AOSS sleep */
- reg = <4>;
label = "llcc-off";
qcom,psci-mode = <0xC24>;
qcom,latency-us = <6562>;
qcom,ss-power = <108>;
- qcom,energy-overhead = <2621840>;
- qcom,time-overhead = <7376>;
- qcom,min-child-idx = <3>;
+ qcom,energy-overhead = <4000000>;
+ qcom,time-overhead = <5000>;
+ qcom,min-child-idx = <2>;
qcom,is-reset;
qcom,notify-rpm;
};
@@ -88,6 +52,7 @@
#size-cells = <0>;
qcom,psci-mode-shift = <0>;
qcom,psci-mode-mask = <0xf>;
+ qcom,use-prediction;
qcom,cpu = <&CPU0 &CPU1 &CPU2 &CPU3>;
qcom,pm-cpu-level@0 { /* C1 */
@@ -95,41 +60,31 @@
label = "wfi";
qcom,psci-cpu-mode = <0x1>;
qcom,latency-us = <43>;
- qcom,ss-power = <454>;
- qcom,energy-overhead = <38639>;
- qcom,time-overhead = <83>;
+ qcom,ss-power = <150>;
+ qcom,energy-overhead = <10000>;
+ qcom,time-overhead = <100>;
};
- qcom,pm-cpu-level@1 { /* C2D */
+ qcom,pm-cpu-level@1 { /* C3 */
reg = <1>;
- label = "ret";
- qcom,psci-cpu-mode = <0x2>;
- qcom,latency-us = <119>;
- qcom,ss-power = <449>;
- qcom,energy-overhead = <78456>;
- qcom,time-overhead = <167>;
- };
-
- qcom,pm-cpu-level@2 { /* C3 */
- reg = <2>;
label = "pc";
qcom,psci-cpu-mode = <0x3>;
qcom,latency-us = <461>;
- qcom,ss-power = <436>;
- qcom,energy-overhead = <418225>;
- qcom,time-overhead = <885>;
+ qcom,ss-power = <100>;
+ qcom,energy-overhead = <400000>;
+ qcom,time-overhead = <500>;
qcom,is-reset;
qcom,use-broadcast-timer;
};
- qcom,pm-cpu-level@3 { /* C4 */
- reg = <3>;
+ qcom,pm-cpu-level@2 { /* C4 */
+ reg = <2>;
label = "rail-pc";
qcom,psci-cpu-mode = <0x4>;
qcom,latency-us = <531>;
- qcom,ss-power = <400>;
- qcom,energy-overhead = <428225>;
- qcom,time-overhead = <1000>;
+ qcom,ss-power = <73>;
+ qcom,energy-overhead = <500000>;
+ qcom,time-overhead = <600>;
qcom,is-reset;
qcom,use-broadcast-timer;
};
@@ -152,18 +107,8 @@
qcom,time-overhead = <83>;
};
- qcom,pm-cpu-level@1 { /* C2D */
+ qcom,pm-cpu-level@1 { /* C3 */
reg = <1>;
- label = "ret";
- qcom,psci-cpu-mode = <0x2>;
- qcom,latency-us = <116>;
- qcom,ss-power = <449>;
- qcom,energy-overhead = <78456>;
- qcom,time-overhead = <167>;
- };
-
- qcom,pm-cpu-level@2 { /* C3 */
- reg = <2>;
label = "pc";
qcom,psci-cpu-mode = <0x3>;
qcom,latency-us = <621>;
@@ -174,8 +119,8 @@
qcom,use-broadcast-timer;
};
- qcom,pm-cpu-level@3 { /* C4 */
- reg = <3>;
+ qcom,pm-cpu-level@2 { /* C4 */
+ reg = <2>;
label = "rail-pc";
qcom,psci-cpu-mode = <0x4>;
qcom,latency-us = <1061>;
diff --git a/arch/arm64/boot/dts/qcom/sdm845-pmic-overlay.dtsi b/arch/arm64/boot/dts/qcom/sdm845-pmic-overlay.dtsi
index 2ac313d..48040a3 100644
--- a/arch/arm64/boot/dts/qcom/sdm845-pmic-overlay.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm845-pmic-overlay.dtsi
@@ -37,6 +37,11 @@
};
};
+&pmi8998_qnovo {
+ pinctrl-names = "default";
+ pinctrl-0 = <&qnovo_fet_ctrl_default>;
+};
+
&usb0 {
extcon = <&pmi8998_pdphy>, <&pmi8998_pdphy>, <&eud>;
};
diff --git a/arch/arm64/boot/dts/qcom/sdm845-qupv3.dtsi b/arch/arm64/boot/dts/qcom/sdm845-qupv3.dtsi
index a805e2e..810afde 100644
--- a/arch/arm64/boot/dts/qcom/sdm845-qupv3.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm845-qupv3.dtsi
@@ -78,6 +78,9 @@
clocks = <&clock_gcc GCC_QUPV3_WRAP0_S0_CLK>,
<&clock_gcc GCC_QUPV3_WRAP_0_M_AHB_CLK>,
<&clock_gcc GCC_QUPV3_WRAP_0_S_AHB_CLK>;
+ dmas = <&gpi_dma0 0 0 3 64 0>,
+ <&gpi_dma0 1 0 3 64 0>;
+ dma-names = "tx", "rx";
pinctrl-names = "default", "sleep";
pinctrl-0 = <&qupv3_se0_i2c_active>;
pinctrl-1 = <&qupv3_se0_i2c_sleep>;
@@ -95,6 +98,9 @@
clocks = <&clock_gcc GCC_QUPV3_WRAP0_S1_CLK>,
<&clock_gcc GCC_QUPV3_WRAP_0_M_AHB_CLK>,
<&clock_gcc GCC_QUPV3_WRAP_0_S_AHB_CLK>;
+ dmas = <&gpi_dma0 0 1 3 64 0>,
+ <&gpi_dma0 1 1 3 64 0>;
+ dma-names = "tx", "rx";
pinctrl-names = "default", "sleep";
pinctrl-0 = <&qupv3_se1_i2c_active>;
pinctrl-1 = <&qupv3_se1_i2c_sleep>;
@@ -112,6 +118,9 @@
clocks = <&clock_gcc GCC_QUPV3_WRAP0_S2_CLK>,
<&clock_gcc GCC_QUPV3_WRAP_0_M_AHB_CLK>,
<&clock_gcc GCC_QUPV3_WRAP_0_S_AHB_CLK>;
+ dmas = <&gpi_dma0 0 2 3 64 0>,
+ <&gpi_dma0 1 2 3 64 0>;
+ dma-names = "tx", "rx";
pinctrl-names = "default", "sleep";
pinctrl-0 = <&qupv3_se2_i2c_active>;
pinctrl-1 = <&qupv3_se2_i2c_sleep>;
@@ -129,6 +138,9 @@
clocks = <&clock_gcc GCC_QUPV3_WRAP0_S3_CLK>,
<&clock_gcc GCC_QUPV3_WRAP_0_M_AHB_CLK>,
<&clock_gcc GCC_QUPV3_WRAP_0_S_AHB_CLK>;
+ dmas = <&gpi_dma0 0 3 3 64 0>,
+ <&gpi_dma0 1 3 3 64 0>;
+ dma-names = "tx", "rx";
pinctrl-names = "default", "sleep";
pinctrl-0 = <&qupv3_se3_i2c_active>;
pinctrl-1 = <&qupv3_se3_i2c_sleep>;
@@ -146,6 +158,9 @@
clocks = <&clock_gcc GCC_QUPV3_WRAP0_S4_CLK>,
<&clock_gcc GCC_QUPV3_WRAP_0_M_AHB_CLK>,
<&clock_gcc GCC_QUPV3_WRAP_0_S_AHB_CLK>;
+ dmas = <&gpi_dma0 0 4 3 64 0>,
+ <&gpi_dma0 1 4 3 64 0>;
+ dma-names = "tx", "rx";
pinctrl-names = "default", "sleep";
pinctrl-0 = <&qupv3_se4_i2c_active>;
pinctrl-1 = <&qupv3_se4_i2c_sleep>;
@@ -163,6 +178,9 @@
clocks = <&clock_gcc GCC_QUPV3_WRAP0_S5_CLK>,
<&clock_gcc GCC_QUPV3_WRAP_0_M_AHB_CLK>,
<&clock_gcc GCC_QUPV3_WRAP_0_S_AHB_CLK>;
+ dmas = <&gpi_dma0 0 5 3 64 0>,
+ <&gpi_dma0 1 5 3 64 0>;
+ dma-names = "tx", "rx";
pinctrl-names = "default", "sleep";
pinctrl-0 = <&qupv3_se5_i2c_active>;
pinctrl-1 = <&qupv3_se5_i2c_sleep>;
@@ -180,6 +198,9 @@
clocks = <&clock_gcc GCC_QUPV3_WRAP0_S6_CLK>,
<&clock_gcc GCC_QUPV3_WRAP_0_M_AHB_CLK>,
<&clock_gcc GCC_QUPV3_WRAP_0_S_AHB_CLK>;
+ dmas = <&gpi_dma0 0 6 3 64 0>,
+ <&gpi_dma0 1 6 3 64 0>;
+ dma-names = "tx", "rx";
pinctrl-names = "default", "sleep";
pinctrl-0 = <&qupv3_se6_i2c_active>;
pinctrl-1 = <&qupv3_se6_i2c_sleep>;
@@ -197,6 +218,9 @@
clocks = <&clock_gcc GCC_QUPV3_WRAP0_S7_CLK>,
<&clock_gcc GCC_QUPV3_WRAP_0_M_AHB_CLK>,
<&clock_gcc GCC_QUPV3_WRAP_0_S_AHB_CLK>;
+ dmas = <&gpi_dma0 0 7 3 64 0>,
+ <&gpi_dma0 1 7 3 64 0>;
+ dma-names = "tx", "rx";
pinctrl-names = "default", "sleep";
pinctrl-0 = <&qupv3_se7_i2c_active>;
pinctrl-1 = <&qupv3_se7_i2c_sleep>;
@@ -442,6 +466,9 @@
clocks = <&clock_gcc GCC_QUPV3_WRAP1_S0_CLK>,
<&clock_gcc GCC_QUPV3_WRAP_1_M_AHB_CLK>,
<&clock_gcc GCC_QUPV3_WRAP_1_S_AHB_CLK>;
+ dmas = <&gpi_dma1 0 0 3 64 0>,
+ <&gpi_dma1 1 0 3 64 0>;
+ dma-names = "tx", "rx";
pinctrl-names = "default", "sleep";
pinctrl-0 = <&qupv3_se8_i2c_active>;
pinctrl-1 = <&qupv3_se8_i2c_sleep>;
@@ -459,6 +486,9 @@
clocks = <&clock_gcc GCC_QUPV3_WRAP1_S1_CLK>,
<&clock_gcc GCC_QUPV3_WRAP_1_M_AHB_CLK>,
<&clock_gcc GCC_QUPV3_WRAP_1_S_AHB_CLK>;
+ dmas = <&gpi_dma1 0 1 3 64 0>,
+ <&gpi_dma1 1 1 3 64 0>;
+ dma-names = "tx", "rx";
pinctrl-names = "default", "sleep";
pinctrl-0 = <&qupv3_se9_i2c_active>;
pinctrl-1 = <&qupv3_se9_i2c_sleep>;
@@ -476,6 +506,9 @@
clocks = <&clock_gcc GCC_QUPV3_WRAP1_S2_CLK>,
<&clock_gcc GCC_QUPV3_WRAP_1_M_AHB_CLK>,
<&clock_gcc GCC_QUPV3_WRAP_1_S_AHB_CLK>;
+ dmas = <&gpi_dma1 0 2 3 64 0>,
+ <&gpi_dma1 1 2 3 64 0>;
+ dma-names = "tx", "rx";
pinctrl-names = "default", "sleep";
pinctrl-0 = <&qupv3_se10_i2c_active>;
pinctrl-1 = <&qupv3_se10_i2c_sleep>;
@@ -493,6 +526,9 @@
clocks = <&clock_gcc GCC_QUPV3_WRAP1_S3_CLK>,
<&clock_gcc GCC_QUPV3_WRAP_1_M_AHB_CLK>,
<&clock_gcc GCC_QUPV3_WRAP_1_S_AHB_CLK>;
+ dmas = <&gpi_dma1 0 3 3 64 0>,
+ <&gpi_dma1 1 3 3 64 0>;
+ dma-names = "tx", "rx";
pinctrl-names = "default", "sleep";
pinctrl-0 = <&qupv3_se11_i2c_active>;
pinctrl-1 = <&qupv3_se11_i2c_sleep>;
@@ -510,6 +546,9 @@
clocks = <&clock_gcc GCC_QUPV3_WRAP1_S4_CLK>,
<&clock_gcc GCC_QUPV3_WRAP_1_M_AHB_CLK>,
<&clock_gcc GCC_QUPV3_WRAP_1_S_AHB_CLK>;
+ dmas = <&gpi_dma1 0 4 3 64 0>,
+ <&gpi_dma1 1 4 3 64 0>;
+ dma-names = "tx", "rx";
pinctrl-names = "default", "sleep";
pinctrl-0 = <&qupv3_se12_i2c_active>;
pinctrl-1 = <&qupv3_se12_i2c_sleep>;
@@ -527,6 +566,9 @@
clocks = <&clock_gcc GCC_QUPV3_WRAP1_S5_CLK>,
<&clock_gcc GCC_QUPV3_WRAP_1_M_AHB_CLK>,
<&clock_gcc GCC_QUPV3_WRAP_1_S_AHB_CLK>;
+ dmas = <&gpi_dma1 0 5 3 64 0>,
+ <&gpi_dma1 1 5 3 64 0>;
+ dma-names = "tx", "rx";
pinctrl-names = "default", "sleep";
pinctrl-0 = <&qupv3_se13_i2c_active>;
pinctrl-1 = <&qupv3_se13_i2c_sleep>;
@@ -544,6 +586,9 @@
clocks = <&clock_gcc GCC_QUPV3_WRAP1_S6_CLK>,
<&clock_gcc GCC_QUPV3_WRAP_1_M_AHB_CLK>,
<&clock_gcc GCC_QUPV3_WRAP_1_S_AHB_CLK>;
+ dmas = <&gpi_dma1 0 6 3 64 0>,
+ <&gpi_dma1 1 6 3 64 0>;
+ dma-names = "tx", "rx";
pinctrl-names = "default", "sleep";
pinctrl-0 = <&qupv3_se14_i2c_active>;
pinctrl-1 = <&qupv3_se14_i2c_sleep>;
@@ -561,6 +606,9 @@
clocks = <&clock_gcc GCC_QUPV3_WRAP1_S7_CLK>,
<&clock_gcc GCC_QUPV3_WRAP_1_M_AHB_CLK>,
<&clock_gcc GCC_QUPV3_WRAP_1_S_AHB_CLK>;
+ dmas = <&gpi_dma1 0 7 3 64 0>,
+ <&gpi_dma1 1 7 3 64 0>;
+ dma-names = "tx", "rx";
pinctrl-names = "default", "sleep";
pinctrl-0 = <&qupv3_se15_i2c_active>;
pinctrl-1 = <&qupv3_se15_i2c_sleep>;
@@ -581,7 +629,7 @@
<&clock_gcc GCC_QUPV3_WRAP_1_S_AHB_CLK>;
pinctrl-names = "default", "sleep";
pinctrl-0 = <&qupv3_se8_spi_active>;
- pinctrl-1 = <&qupv3_se8_spi_sleep>;
+ pinctrl-1 = <&qupv3_se8_spi_active>;
interrupts = <GIC_SPI 353 0>;
spi-max-frequency = <50000000>;
qcom,wrapper-core = <&qupv3_1>;
diff --git a/arch/arm64/boot/dts/qcom/sdm845-qvr-overlay.dts b/arch/arm64/boot/dts/qcom/sdm845-qvr-overlay.dts
index 2fac9e8..fb99157 100644
--- a/arch/arm64/boot/dts/qcom/sdm845-qvr-overlay.dts
+++ b/arch/arm64/boot/dts/qcom/sdm845-qvr-overlay.dts
@@ -19,6 +19,7 @@
#include <dt-bindings/clock/qcom,rpmh.h>
#include <dt-bindings/interrupt-controller/arm-gic.h>
+#include "sdm845-sde-display.dtsi"
#include "sdm845-qvr.dtsi"
/ {
diff --git a/arch/arm64/boot/dts/qcom/sdm845-qvr.dtsi b/arch/arm64/boot/dts/qcom/sdm845-qvr.dtsi
index d89722f..2d701a5 100644
--- a/arch/arm64/boot/dts/qcom/sdm845-qvr.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm845-qvr.dtsi
@@ -14,11 +14,19 @@
#include "sdm845-pinctrl-overlay.dtsi"
#include "smb1355.dtsi"
+&vendor {
+ qvr_batterydata: qcom,battery-data {
+ qcom,batt-id-range-pct = <15>;
+ #include "fg-gen3-batterydata-mlp446579-3800mah.dtsi"
+ };
+};
+
&pmi8998_pdphy {
vbus-supply = <&smb2_vbus>;
};
&pmi8998_fg {
+ qcom,battery-data = <&qvr_batterydata>;
qcom,fg-bmd-en-delay-ms = <300>;
};
@@ -75,6 +83,10 @@
status = "okay";
};
+&qupv3_se9_2uart {
+ status = "ok";
+};
+
&ufsphy_mem {
compatible = "qcom,ufs-phy-qmp-v3";
diff --git a/arch/arm64/boot/dts/qcom/sdm845-regulator.dtsi b/arch/arm64/boot/dts/qcom/sdm845-regulator.dtsi
index d22c28a..da4d41c 100644
--- a/arch/arm64/boot/dts/qcom/sdm845-regulator.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm845-regulator.dtsi
@@ -29,690 +29,7 @@
};
};
-&spmi_bus {
- qcom,pm8998@1 {
- /* PM8998 S12 + S11 + S10 = VDD_APC1 supply */
- pm8998_s12: regulator@3500 {
- compatible = "qcom,qpnp-regulator";
- reg = <0x3500 0x100>;
- regulator-name = "pm8998_s12";
- regulator-min-microvolt = <568000>;
- regulator-max-microvolt = <1136000>;
- qcom,enable-time = <500>;
- regulator-always-on;
- };
-
- /* PM8998 S13 = VDD_APC0 supply */
- pm8998_s13: regulator@3800 {
- compatible = "qcom,qpnp-regulator";
- reg = <0x3800 0x100>;
- regulator-name = "pm8998_s13";
- regulator-min-microvolt = <568000>;
- regulator-max-microvolt = <996000>;
- qcom,enable-time = <500>;
- regulator-always-on;
- };
- };
-};
-
&soc {
- /* CPR controller regulators */
- apc0_cpr: cprh-ctrl@17dc0000 {
- compatible = "qcom,cprh-sdm845-v1-kbss-regulator";
- reg = <0x17dc0000 0x4000>,
- <0x00784000 0x1000>,
- <0x17840000 0x1000>;
- reg-names = "cpr_ctrl", "fuse_base", "saw";
- clocks = <&clock_gcc GCC_CPUSS_RBCPR_CLK>;
- clock-names = "core_clk";
- qcom,cpr-ctrl-name = "apc0";
- qcom,cpr-controller-id = <0>;
-
- qcom,cpr-sensor-time = <1000>;
- qcom,cpr-loop-time = <5000000>;
- qcom,cpr-idle-cycles = <15>;
- qcom,cpr-up-down-delay-time = <3000>;
- qcom,cpr-step-quot-init-min = <11>;
- qcom,cpr-step-quot-init-max = <12>;
- qcom,cpr-count-mode = <0>; /* All at once */
- qcom,cpr-count-repeat = <20>;
- qcom,cpr-down-error-step-limit = <1>;
- qcom,cpr-up-error-step-limit = <1>;
- qcom,cpr-corner-switch-delay-time = <1042>;
- qcom,cpr-voltage-settling-time = <1760>;
- qcom,cpr-reset-step-quot-loop-en;
-
- qcom,voltage-step = <4000>;
- qcom,voltage-base = <352000>;
- qcom,cpr-saw-use-unit-mV;
-
- qcom,saw-avs-ctrl = <0x101C031>;
- qcom,saw-avs-limit = <0x3A003A0>;
-
- qcom,cpr-enable;
- qcom,cpr-hw-closed-loop;
-
- qcom,cpr-panic-reg-addr-list =
- <0x17dc3a84 0x17dc3a88 0x17840c18>;
- qcom,cpr-panic-reg-name-list =
- "APSS_SILVER_CPRH_STATUS_0",
- "APSS_SILVER_CPRH_STATUS_1",
- "SILVER_SAW4_PMIC_STS";
-
- qcom,cpr-aging-ref-voltage = <996000>;
- vdd-supply = <&pm8998_s13>;
-
- thread@1 {
- qcom,cpr-thread-id = <1>;
- qcom,cpr-consecutive-up = <0>;
- qcom,cpr-consecutive-down = <0>;
- qcom,cpr-up-threshold = <2>;
- qcom,cpr-down-threshold = <2>;
-
- apc0_pwrcl_vreg: regulator {
- regulator-name = "apc0_pwrcl_corner";
- regulator-min-microvolt = <1>;
- regulator-max-microvolt = <19>;
-
- qcom,cpr-fuse-corners = <4>;
- qcom,cpr-fuse-combos = <24>;
- qcom,cpr-speed-bins = <3>;
- qcom,cpr-speed-bin-corners = <19 19 19>;
- qcom,cpr-corners = <19>;
-
- qcom,cpr-corner-fmax-map = <6 12 17 19>;
-
- qcom,cpr-voltage-ceiling =
- <872000 872000 872000 872000 872000
- 872000 872000 872000 872000 872000
- 872000 872000 872000 872000 872000
- 872000 928000 996000 996000>;
-
- qcom,cpr-voltage-floor =
- /* Speed bin 0 */
- <568000 568000 568000 568000 568000
- 568000 568000 568000 568000 568000
- 568000 568000 568000 568000 568000
- 568000 568000 996000 996000>,
- /* Speed bin 1 */
- <568000 568000 568000 568000 568000
- 568000 568000 568000 568000 568000
- 568000 568000 568000 568000 568000
- 568000 568000 568000 568000>,
- /* Speed bin 2 */
- <568000 568000 568000 568000 568000
- 568000 568000 568000 568000 568000
- 568000 568000 568000 568000 568000
- 568000 568000 568000 568000>;
-
- qcom,cpr-floor-to-ceiling-max-range =
- <32000 32000 32000 32000 32000
- 32000 32000 32000 32000 32000
- 32000 32000 32000 32000 32000
- 32000 32000 40000 40000>;
-
- qcom,corner-frequencies =
- /* Speed bin 0 */
- <300000000 422400000 499200000
- 576000000 652800000 748800000
- 825600000 902400000 979200000
- 1056000000 1132800000 1209600000
- 1286400000 1363200000 1440000000
- 1516800000 1593600000 1651200000
- 1708800000>,
- /* Speed bin 1 */
- <300000000 422400000 499200000
- 576000000 652800000 748800000
- 825600000 902400000 979200000
- 1056000000 1132800000 1209600000
- 1286400000 1363200000 1440000000
- 1516800000 1593600000 1651200000
- 1708800000>,
- /* Speed bin 2 */
- <300000000 422400000 499200000
- 576000000 652800000 748800000
- 825600000 902400000 979200000
- 1056000000 1132800000 1209600000
- 1286400000 1363200000 1440000000
- 1516800000 1593600000 1670400000
- 1747200000>;
-
- qcom,cpr-ro-scaling-factor =
- <2594 2795 2576 2761 2469 2673 2198
- 2553 3188 3255 3191 2962 3055 2984
- 2043 2947>,
- <2594 2795 2576 2761 2469 2673 2198
- 2553 3188 3255 3191 2962 3055 2984
- 2043 2947>,
- <2259 2389 2387 2531 2294 2464 2218
- 2476 2525 2855 2817 2836 2740 2490
- 1950 2632>,
- <2259 2389 2387 2531 2294 2464 2218
- 2476 2525 2855 2817 2836 2740 2490
- 1950 2632>;
-
- qcom,cpr-open-loop-voltage-fuse-adjustment =
- /* Speed bin 0 */
- <100000 100000 100000 100000>,
- < 0 0 0 100000>,
- < 0 0 0 0>,
- < 0 0 0 0>,
- < 0 0 0 0>,
- < 0 0 0 0>,
- < 0 0 0 0>,
- < 0 0 0 0>,
- /* Speed bin 1 */
- <100000 100000 100000 100000>,
- < 0 0 0 100000>,
- < 0 0 0 0>,
- < 0 0 0 0>,
- < 0 0 0 0>,
- < 0 0 0 0>,
- < 0 0 0 0>,
- < 0 0 0 0>,
- /* Speed bin 2 */
- <100000 100000 100000 100000>,
- < 0 0 0 100000>,
- < 0 0 0 0>,
- < 0 0 0 0>,
- < 0 0 0 0>,
- < 0 0 0 0>,
- < 0 0 0 0>,
- < 0 0 0 0>;
-
- qcom,cpr-closed-loop-voltage-fuse-adjustment =
- /* Speed bin 0 */
- <100000 100000 100000 100000>,
- < 0 0 0 100000>,
- < 0 0 0 0>,
- < 0 0 0 0>,
- < 0 0 0 0>,
- < 0 0 0 0>,
- < 0 0 0 0>,
- < 0 0 0 0>,
- /* Speed bin 1 */
- <100000 100000 100000 100000>,
- < 0 0 0 100000>,
- < 0 0 0 0>,
- < 0 0 0 0>,
- < 0 0 0 0>,
- < 0 0 0 0>,
- < 0 0 0 0>,
- < 0 0 0 0>,
- /* Speed bin 2 */
- <100000 100000 100000 100000>,
- < 0 0 0 100000>,
- < 0 0 0 0>,
- < 0 0 0 0>,
- < 0 0 0 0>,
- < 0 0 0 0>,
- < 0 0 0 0>,
- < 0 0 0 0>;
-
- qcom,allow-voltage-interpolation;
- qcom,allow-quotient-interpolation;
- qcom,cpr-scaled-open-loop-voltage-as-ceiling;
-
- qcom,cpr-aging-max-voltage-adjustment = <15000>;
- qcom,cpr-aging-ref-corner = <19>;
- qcom,cpr-aging-ro-scaling-factor = <1620>;
- qcom,allow-aging-voltage-adjustment =
- /* Speed bin 0 */
- <0 1 1 1 1 1 1 1>,
- /* Speed bin 1 */
- <0 1 1 1 1 1 1 1>,
- /* Speed bin 2 */
- <0 1 1 1 1 1 1 1>;
- qcom,allow-aging-open-loop-voltage-adjustment =
- <1>;
- };
- };
-
- thread@0 {
- qcom,cpr-thread-id = <0>;
- qcom,cpr-consecutive-up = <0>;
- qcom,cpr-consecutive-down = <0>;
- qcom,cpr-up-threshold = <2>;
- qcom,cpr-down-threshold = <2>;
-
- apc0_l3_vreg: regulator {
- regulator-name = "apc0_l3_corner";
- regulator-min-microvolt = <1>;
- regulator-max-microvolt = <13>;
-
- qcom,cpr-fuse-corners = <4>;
- qcom,cpr-fuse-combos = <24>;
- qcom,cpr-speed-bins = <3>;
- qcom,cpr-speed-bin-corners = <11 11 13>;
- qcom,cpr-corners =
- /* Speed bin 0 */
- <11 11 11 11 11 11 11 11>,
- /* Speed bin 1 */
- <11 11 11 11 11 11 11 11>,
- /* Speed bin 2 */
- <13 13 13 13 13 13 13 13>;
-
- qcom,cpr-corner-fmax-map =
- /* Speed bin 0 */
- <4 7 9 11>,
- /* Speed bin 1 */
- <4 7 9 11>,
- /* Speed bin 2 */
- <4 7 9 13>;
-
- qcom,cpr-voltage-ceiling =
- /* Speed bin 0 */
- <872000 872000 872000 872000 872000
- 872000 872000 872000 928000 996000
- 996000>,
- /* Speed bin 1 */
- <872000 872000 872000 872000 872000
- 872000 872000 872000 928000 996000
- 996000>,
- /* Speed bin 2 */
- <872000 872000 872000 872000 872000
- 872000 872000 872000 928000 996000
- 996000 996000 996000>;
-
- qcom,cpr-voltage-floor =
- /* Speed bin 0 */
- <568000 568000 568000 568000 568000
- 568000 568000 568000 568000 996000
- 996000>,
- /* Speed bin 1 */
- <568000 568000 568000 568000 568000
- 568000 568000 568000 568000 568000
- 568000>,
- /* Speed bin 2 */
- <568000 568000 568000 568000 568000
- 568000 568000 568000 568000 568000
- 568000 568000 568000>;
-
- qcom,cpr-floor-to-ceiling-max-range =
- /* Speed bin 0 */
- <32000 32000 32000 32000 32000
- 32000 32000 32000 32000 40000
- 40000>,
- /* Speed bin 1 */
- <32000 32000 32000 32000 32000
- 32000 32000 32000 32000 40000
- 40000>,
- /* Speed bin 2 */
- <32000 32000 32000 32000 32000
- 32000 32000 32000 32000 40000
- 40000 40000 40000>;
-
- qcom,corner-frequencies =
- /* Speed bin 0 */
- <300000000 422400000 499200000
- 576000000 652800000 729600000
- 806400000 883200000 960000000
- 1036800000 1094400000>,
- /* Speed bin 1 */
- <300000000 422400000 499200000
- 576000000 652800000 729600000
- 806400000 883200000 960000000
- 1036800000 1094400000>,
- /* Speed bin 2 */
- <300000000 422400000 499200000
- 576000000 652800000 729600000
- 806400000 883200000 960000000
- 1036800000 1113600000 1209600000
- 1305600000>;
-
- qcom,cpr-ro-scaling-factor =
- <2857 3056 2828 2952 2699 2796 2447
- 2631 2630 2579 2244 3343 3287 3137
- 3164 2656>,
- <2857 3056 2828 2952 2699 2796 2447
- 2631 2630 2579 2244 3343 3287 3137
- 3164 2656>,
- <2439 2577 2552 2667 2461 2577 2394
- 2536 2132 2307 2191 2903 2838 2912
- 2501 2095>,
- <2439 2577 2552 2667 2461 2577 2394
- 2536 2132 2307 2191 2903 2838 2912
- 2501 2095>;
-
- qcom,cpr-open-loop-voltage-fuse-adjustment =
- /* Speed bin 0 */
- <100000 100000 100000 100000>,
- < 0 24000 4000 100000>,
- < 0 24000 4000 0>,
- < 0 24000 4000 0>,
- < 0 24000 4000 0>,
- < 0 24000 4000 0>,
- < 0 24000 4000 0>,
- < 0 24000 4000 0>,
- /* Speed bin 1 */
- <100000 100000 100000 100000>,
- < 0 24000 4000 100000>,
- < 0 24000 4000 20000>,
- < 0 24000 4000 20000>,
- < 0 24000 4000 20000>,
- < 0 24000 4000 20000>,
- < 0 24000 4000 20000>,
- < 0 24000 4000 20000>,
- /* Speed bin 2 */
- <100000 100000 100000 100000>,
- < 0 24000 4000 100000>,
- < 0 24000 4000 40000>,
- < 0 24000 4000 40000>,
- < 0 24000 4000 40000>,
- < 0 24000 4000 40000>,
- < 0 24000 4000 40000>,
- < 0 24000 4000 40000>;
-
- qcom,cpr-closed-loop-voltage-fuse-adjustment =
- /* Speed bin 0 */
- <100000 100000 100000 100000>,
- < 0 29000 6000 100000>,
- < 0 29000 6000 0>,
- < 0 29000 6000 0>,
- < 0 29000 6000 0>,
- < 0 29000 6000 0>,
- < 0 29000 6000 0>,
- < 0 29000 6000 0>,
- /* Speed bin 1 */
- <100000 100000 100000 100000>,
- < 0 29000 6000 100000>,
- < 0 29000 6000 20000>,
- < 0 29000 6000 20000>,
- < 0 29000 6000 20000>,
- < 0 29000 6000 20000>,
- < 0 29000 6000 20000>,
- < 0 29000 6000 20000>,
- /* Speed bin 2 */
- <100000 100000 100000 100000>,
- < 0 29000 6000 100000>,
- < 0 29000 6000 40000>,
- < 0 29000 6000 40000>,
- < 0 29000 6000 40000>,
- < 0 29000 6000 40000>,
- < 0 29000 6000 40000>,
- < 0 29000 6000 40000>;
-
- qcom,allow-voltage-interpolation;
- qcom,allow-quotient-interpolation;
- qcom,cpr-scaled-open-loop-voltage-as-ceiling;
-
- qcom,cpr-aging-max-voltage-adjustment = <15000>;
- qcom,cpr-aging-ref-corner = <11 11 13>;
- qcom,cpr-aging-ro-scaling-factor = <1620>;
- qcom,allow-aging-voltage-adjustment =
- /* Speed bin 0 */
- <0 1 1 1 1 1 1 1>,
- /* Speed bin 1 */
- <0 1 1 1 1 1 1 1>,
- /* Speed bin 2 */
- <0 1 1 1 1 1 1 1>;
- qcom,allow-aging-open-loop-voltage-adjustment =
- <1>;
- };
- };
- };
-
- apc1_cpr: cprh-ctrl@17db0000 {
- compatible = "qcom,cprh-sdm845-v1-kbss-regulator";
- reg = <0x17db0000 0x4000>,
- <0x00784000 0x1000>,
- <0x17830000 0x1000>;
- reg-names = "cpr_ctrl", "fuse_base", "saw";
- clocks = <&clock_gcc GCC_CPUSS_RBCPR_CLK>;
- clock-names = "core_clk";
- qcom,cpr-ctrl-name = "apc1";
- qcom,cpr-controller-id = <1>;
-
- qcom,cpr-sensor-time = <1000>;
- qcom,cpr-loop-time = <5000000>;
- qcom,cpr-idle-cycles = <15>;
- qcom,cpr-up-down-delay-time = <3000>;
- qcom,cpr-step-quot-init-min = <9>;
- qcom,cpr-step-quot-init-max = <14>;
- qcom,cpr-count-mode = <0>; /* All at once */
- qcom,cpr-count-repeat = <20>;
- qcom,cpr-down-error-step-limit = <1>;
- qcom,cpr-up-error-step-limit = <1>;
- qcom,cpr-corner-switch-delay-time = <1042>;
- qcom,cpr-voltage-settling-time = <1760>;
- qcom,cpr-reset-step-quot-loop-en;
-
- qcom,apm-threshold-voltage = <800000>;
- qcom,apm-crossover-voltage = <880000>;
- qcom,mem-acc-threshold-voltage = <852000>;
- qcom,mem-acc-crossover-voltage = <852000>;
-
- qcom,voltage-step = <4000>;
- qcom,voltage-base = <352000>;
- qcom,cpr-saw-use-unit-mV;
-
- qcom,saw-avs-ctrl = <0x101C031>;
- qcom,saw-avs-limit = <0x4200420>;
-
- qcom,cpr-enable;
- qcom,cpr-hw-closed-loop;
-
- qcom,cpr-panic-reg-addr-list =
- <0x17db3a84 0x17830c18>;
- qcom,cpr-panic-reg-name-list =
- "APSS_GOLD_CPRH_STATUS_0", "GOLD_SAW4_PMIC_STS";
-
- qcom,cpr-aging-ref-voltage = <1136000>;
- vdd-supply = <&pm8998_s12>;
-
- thread@0 {
- qcom,cpr-thread-id = <0>;
- qcom,cpr-consecutive-up = <0>;
- qcom,cpr-consecutive-down = <0>;
- qcom,cpr-up-threshold = <2>;
- qcom,cpr-down-threshold = <2>;
-
- apc1_perfcl_vreg: regulator {
- regulator-name = "apc1_perfcl_corner";
- regulator-min-microvolt = <1>;
- regulator-max-microvolt = <27>;
-
- qcom,cpr-fuse-corners = <3>;
- qcom,cpr-fuse-combos = <24>;
- qcom,cpr-speed-bins = <3>;
- qcom,cpr-speed-bin-corners = <22 24 25>;
- qcom,cpr-corners =
- /* Speed bin 0 */
- <22 22 22 22 22 22 22 22>,
- /* Speed bin 1 */
- <24 24 24 24 24 24 24 24>,
- /* Speed bin 2 */
- <25 25 25 25 25 25 25 25>;
-
- qcom,cpr-corner-fmax-map =
- /* Speed bin 0 */
- <10 17 22>,
- /* Speed bin 1 */
- <10 17 24>,
- /* Speed bin 2 */
- <10 17 25>;
-
- qcom,cpr-voltage-ceiling =
- /* Speed bin 0 */
- <828000 828000 828000 828000 828000
- 828000 828000 828000 828000 828000
- 828000 828000 828000 828000 828000
- 828000 828000 884000 952000 952000
- 1136000 1136000>,
- /* Speed bin 1 */
- <828000 828000 828000 828000 828000
- 828000 828000 828000 828000 828000
- 828000 828000 828000 828000 828000
- 828000 828000 884000 952000 952000
- 1136000 1136000 1136000 1136000>,
- /* Speed bin 2 */
- <828000 828000 828000 828000 828000
- 828000 828000 828000 828000 828000
- 828000 828000 828000 828000 828000
- 828000 828000 884000 952000 952000
- 1136000 1136000 1136000 1136000
- 1136000>;
-
- qcom,cpr-voltage-floor =
- /* Speed bin 0 */
- <568000 568000 568000 568000 568000
- 568000 568000 568000 568000 568000
- 568000 568000 568000 568000 568000
- 568000 568000 568000 568000 568000
- 568000 568000>,
- /* Speed bin 1 */
- <568000 568000 568000 568000 568000
- 568000 568000 568000 568000 568000
- 568000 568000 568000 568000 568000
- 568000 568000 568000 568000 568000
- 568000 568000 568000 568000>,
- /* Speed bin 2 */
- <568000 568000 568000 568000 568000
- 568000 568000 568000 568000 568000
- 568000 568000 568000 568000 568000
- 568000 568000 568000 568000 568000
- 568000 568000 568000 568000
- 568000>;
-
- qcom,cpr-floor-to-ceiling-max-range =
- /* Speed bin 0 */
- <32000 32000 32000 32000 32000
- 32000 32000 32000 32000 32000
- 32000 32000 32000 32000 32000
- 32000 32000 40000 40000 40000
- 40000 40000>,
- /* Speed bin 1 */
- <32000 32000 32000 32000 32000
- 32000 32000 32000 32000 32000
- 32000 32000 32000 32000 32000
- 32000 32000 40000 40000 40000
- 40000 40000 40000 40000>,
- /* Speed bin 2 */
- <32000 32000 32000 32000 32000
- 32000 32000 32000 32000 32000
- 32000 32000 32000 32000 32000
- 32000 32000 40000 40000 40000
- 40000 40000 40000 40000 40000>;
-
- qcom,corner-frequencies =
- /* Speed bin 0 */
- <300000000 422400000 499200000
- 576000000 652800000 729600000
- 806400000 883200000 960000000
- 1036800000 1113600000 1190400000
- 1267200000 1344000000 1420800000
- 1497600000 1574400000 1651200000
- 1728000000 1804800000 1881600000
- 1958400000>,
- /* Speed bin 1 */
- <300000000 422400000 499200000
- 576000000 652800000 729600000
- 806400000 883200000 960000000
- 1036800000 1113600000 1190400000
- 1267200000 1344000000 1420800000
- 1497600000 1574400000 1651200000
- 1728000000 1804800000 1881600000
- 1958400000 2035200000 2092800000>,
- /* Speed bin 2 */
- <300000000 422400000 499200000
- 576000000 652800000 729600000
- 806400000 883200000 960000000
- 1036800000 1113600000 1190400000
- 1267200000 1344000000 1420800000
- 1497600000 1574400000 1651200000
- 1728000000 1804800000 1881600000
- 1958400000 2035200000 2112000000
- 2208000000>;
-
- qcom,cpr-ro-scaling-factor =
- <2857 3056 2828 2952 2699 2796 2447
- 2631 2630 2579 2244 3343 3287 3137
- 3164 2656>,
- <2857 3056 2828 2952 2699 2796 2447
- 2631 2630 2579 2244 3343 3287 3137
- 3164 2656>,
- <2086 2208 2273 2408 2203 2327 2213
- 2340 1755 2039 2049 2474 2437 2618
- 2003 1675>;
-
- qcom,cpr-open-loop-voltage-fuse-adjustment =
- /* Speed bin 0 */
- <100000 100000 100000>,
- < 0 0 0>,
- < 0 0 0>,
- < 0 0 0>,
- < 0 0 0>,
- < 0 0 0>,
- < 0 0 0>,
- < 0 0 0>,
- /* Speed bin 1 */
- <100000 100000 100000>,
- < 0 0 0>,
- < 0 0 0>,
- < 0 0 0>,
- < 0 0 0>,
- < 0 0 0>,
- < 0 0 0>,
- < 0 0 0>,
- /* Speed bin 2 */
- <100000 100000 100000>,
- < 0 0 0>,
- < 0 0 0>,
- < 0 0 0>,
- < 0 0 0>,
- < 0 0 0>,
- < 0 0 0>,
- < 0 0 0>;
-
- qcom,cpr-closed-loop-voltage-fuse-adjustment =
- /* Speed bin 0 */
- <100000 100000 100000>,
- < 0 0 0>,
- < 0 0 0>,
- < 0 0 0>,
- < 0 0 0>,
- < 0 0 0>,
- < 0 0 0>,
- < 0 0 0>,
- /* Speed bin 1 */
- <100000 100000 100000>,
- < 0 0 0>,
- < 0 0 0>,
- < 0 0 0>,
- < 0 0 0>,
- < 0 0 0>,
- < 0 0 0>,
- < 0 0 0>,
- /* Speed bin 2 */
- <100000 100000 100000>,
- < 0 0 0>,
- < 0 0 0>,
- < 0 0 0>,
- < 0 0 0>,
- < 0 0 0>,
- < 0 0 0>,
- < 0 0 0>;
-
- qcom,allow-voltage-interpolation;
- qcom,allow-quotient-interpolation;
- qcom,cpr-scaled-open-loop-voltage-as-ceiling;
-
- qcom,cpr-aging-max-voltage-adjustment = <15000>;
- qcom,cpr-aging-ref-corner = <22 24 25>;
- qcom,cpr-aging-ro-scaling-factor = <1700>;
- qcom,allow-aging-voltage-adjustment =
- /* Speed bin 0 */
- <0 1 1 1 1 1 1 1>,
- /* Speed bin 1 */
- <0 1 1 1 1 1 1 1>,
- /* Speed bin 2 */
- <0 1 1 1 1 1 1 1>;
- qcom,allow-aging-open-loop-voltage-adjustment =
- <1>;
- };
- };
- };
-
/* RPMh regulators: */
/* PM8998 S1 = VDD_EBI supply */
@@ -854,11 +171,14 @@
<RPMH_REGULATOR_MODE_LDO_LPM
RPMH_REGULATOR_MODE_LDO_HPM>;
qcom,mode-threshold-currents = <0 1>;
+ proxy-supply = <&pm8998_l1>;
pm8998_l1: regulator-l1 {
regulator-name = "pm8998_l1";
qcom,set = <RPMH_REGULATOR_SET_ALL>;
regulator-min-microvolt = <880000>;
regulator-max-microvolt = <880000>;
+ qcom,proxy-consumer-enable;
+ qcom,proxy-consumer-current = <72000>;
qcom,init-voltage = <880000>;
qcom,init-mode = <RPMH_REGULATOR_MODE_LDO_LPM>;
};
@@ -1103,9 +423,12 @@
<RPMH_REGULATOR_MODE_LDO_LPM
RPMH_REGULATOR_MODE_LDO_HPM>;
qcom,mode-threshold-currents = <0 10000>;
+ proxy-supply = <&pm8998_l14>;
pm8998_l14: regulator-l14 {
regulator-name = "pm8998_l14";
qcom,set = <RPMH_REGULATOR_SET_ALL>;
+ qcom,proxy-consumer-enable;
+ qcom,proxy-consumer-current = <115000>;
regulator-min-microvolt = <1800000>;
regulator-max-microvolt = <1880000>;
qcom,init-voltage = <1800000>;
@@ -1320,11 +643,14 @@
<RPMH_REGULATOR_MODE_LDO_LPM
RPMH_REGULATOR_MODE_LDO_HPM>;
qcom,mode-threshold-currents = <0 1>;
+ proxy-supply = <&pm8998_l26>;
pm8998_l26: regulator-l26 {
regulator-name = "pm8998_l26";
qcom,set = <RPMH_REGULATOR_SET_ALL>;
regulator-min-microvolt = <1200000>;
regulator-max-microvolt = <1200000>;
+ qcom,proxy-consumer-enable;
+ qcom,proxy-consumer-current = <43600>;
qcom,init-voltage = <1200000>;
qcom,init-mode = <RPMH_REGULATOR_MODE_LDO_LPM>;
};
diff --git a/arch/arm64/boot/dts/qcom/sdm845-rumi.dtsi b/arch/arm64/boot/dts/qcom/sdm845-rumi.dtsi
index d607f75..34de0a0 100644
--- a/arch/arm64/boot/dts/qcom/sdm845-rumi.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm845-rumi.dtsi
@@ -132,14 +132,6 @@
};
};
-&apc0_cpr {
- qcom,cpr-ignore-invalid-fuses;
-};
-
-&apc1_cpr {
- qcom,cpr-ignore-invalid-fuses;
-};
-
&pmi8998_charger {
qcom,suspend-input;
};
diff --git a/arch/arm64/boot/dts/qcom/sdm845-sde-display.dtsi b/arch/arm64/boot/dts/qcom/sdm845-sde-display.dtsi
index 4337da7..4254fcd 100644
--- a/arch/arm64/boot/dts/qcom/sdm845-sde-display.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm845-sde-display.dtsi
@@ -25,6 +25,8 @@
#include "dsi-panel-sharp-1080p-cmd.dtsi"
#include "dsi-panel-sharp-dualmipi-1080p-120hz.dtsi"
#include "dsi-panel-s6e3ha3-amoled-dualmipi-wqhd-cmd.dtsi"
+#include "dsi-panel-nt35597-dualmipi-wqxga-video.dtsi"
+#include "dsi-panel-nt35597-dualmipi-wqxga-cmd.dtsi"
#include <dt-bindings/clock/mdss-10nm-pll-clk.h>
&soc {
@@ -401,6 +403,54 @@
qcom,dsi-panel = <&dsi_dual_sim_dsc_375_cmd>;
};
+ dsi_dual_nt35597_video_display: qcom,dsi-display@14 {
+ compatible = "qcom,dsi-display";
+ label = "dsi_dual_nt35597_video_display";
+ qcom,display-type = "primary";
+
+ qcom,dsi-ctrl = <&mdss_dsi0 &mdss_dsi1>;
+ qcom,dsi-phy = <&mdss_dsi_phy0 &mdss_dsi_phy1>;
+ clocks = <&mdss_dsi0_pll BYTECLK_MUX_0_CLK>,
+ <&mdss_dsi0_pll PCLK_MUX_0_CLK>;
+ clock-names = "src_byte_clk", "src_pixel_clk";
+
+ pinctrl-names = "panel_active", "panel_suspend";
+ pinctrl-0 = <&sde_dsi_active &sde_te_active>;
+ pinctrl-1 = <&sde_dsi_suspend &sde_te_suspend>;
+ qcom,platform-reset-gpio = <&tlmm 6 0>;
+ qcom,panel-mode-gpio = <&tlmm 52 0>;
+
+ qcom,dsi-panel = <&dsi_dual_nt35597_video>;
+ vddio-supply = <&pm8998_l14>;
+ lab-supply = <&lab_regulator>;
+ ibb-supply = <&ibb_regulator>;
+ };
+
+
+ dsi_dual_nt35597_cmd_display: qcom,dsi-display@15 {
+ compatible = "qcom,dsi-display";
+ label = "dsi_dual_nt35597_cmd_display";
+ qcom,display-type = "primary";
+
+ qcom,dsi-ctrl = <&mdss_dsi0 &mdss_dsi1>;
+ qcom,dsi-phy = <&mdss_dsi_phy0 &mdss_dsi_phy1>;
+ clocks = <&mdss_dsi0_pll BYTECLK_MUX_0_CLK>,
+ <&mdss_dsi0_pll PCLK_MUX_0_CLK>;
+ clock-names = "src_byte_clk", "src_pixel_clk";
+
+ pinctrl-names = "panel_active", "panel_suspend";
+ pinctrl-0 = <&sde_dsi_active &sde_te_active>;
+ pinctrl-1 = <&sde_dsi_suspend &sde_te_suspend>;
+ qcom,platform-te-gpio = <&tlmm 10 0>;
+ qcom,platform-reset-gpio = <&tlmm 6 0>;
+ qcom,panel-mode-gpio = <&tlmm 52 0>;
+
+ qcom,dsi-panel = <&dsi_dual_nt35597_cmd>;
+ vddio-supply = <&pm8998_l14>;
+ lab-supply = <&lab_regulator>;
+ ibb-supply = <&ibb_regulator>;
+ };
+
sde_wb: qcom,wb-display@0 {
compatible = "qcom,wb-display";
cell-index = <0>;
@@ -545,8 +595,9 @@
qcom,mdss-dsi-t-clk-pre = <0x29>;
qcom,mdss-dsi-display-timings {
timing@0{
- qcom,mdss-dsi-panel-phy-timings = [00 1A 06 06 22 20 07
- 07 04 03 04 00];
+ qcom,mdss-dsi-panel-phy-timings = [00 1E 08 08 24 22 08
+ 08 05 03 04 00];
+ qcom,mdss-dsi-panel-clockrate = <900000000>;
qcom,display-topology = <1 0 1>;
qcom,default-topology-index = <0>;
};
@@ -660,3 +711,31 @@
};
};
};
+
+&dsi_dual_nt35597_video {
+ qcom,mdss-dsi-t-clk-post = <0x0d>;
+ qcom,mdss-dsi-t-clk-pre = <0x2d>;
+ qcom,mdss-dsi-display-timings {
+ timing@0 {
+ qcom,mdss-dsi-panel-timings = [00 1c 08 07 23 22 07 07
+ 05 03 04 00];
+ qcom,display-topology = <2 0 2>,
+ <1 0 2>;
+ qcom,default-topology-index = <0>;
+ };
+ };
+};
+
+&dsi_dual_nt35597_cmd {
+ qcom,mdss-dsi-t-clk-post = <0x0d>;
+ qcom,mdss-dsi-t-clk-pre = <0x2d>;
+ qcom,mdss-dsi-display-timings {
+ timing@0 {
+ qcom,mdss-dsi-panel-timings = [00 1c 08 07 23 22 07 07
+ 05 03 04 00];
+ qcom,display-topology = <2 0 2>,
+ <1 0 2>;
+ qcom,default-topology-index = <0>;
+ };
+ };
+};
diff --git a/arch/arm64/boot/dts/qcom/sdm845-sde.dtsi b/arch/arm64/boot/dts/qcom/sdm845-sde.dtsi
index e7a946c..7c8eab4 100644
--- a/arch/arm64/boot/dts/qcom/sdm845-sde.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm845-sde.dtsi
@@ -55,10 +55,13 @@
qcom,sde-ctl-off = <0x2000 0x2200 0x2400
0x2600 0x2800>;
qcom,sde-ctl-size = <0xE4>;
+ qcom,sde-ctl-display-pref = "primary", "primary", "none",
+ "none", "none";
- qcom,sde-mixer-off = <0x45000 0x46000 0x47000
- 0x48000 0x49000 0x4a000>;
+ qcom,sde-mixer-off = <0x45000 0x46000 0x47000 0 0 0x4a000>;
qcom,sde-mixer-size = <0x320>;
+ qcom,sde-mixer-display-pref = "primary", "primary", "none",
+ "none", "none", "none";
qcom,sde-dspp-top-off = <0x1300>;
qcom,sde-dspp-top-size = <0xc>;
@@ -160,7 +163,18 @@
qcom,sde-danger-lut = <0x0000000f 0x0000ffff 0x00000000
0x00000000>;
- qcom,sde-safe-lut = <0xfffc 0xff00 0xffff 0xffff>;
+ qcom,sde-safe-lut-linear =
+ <4 0xfff8>,
+ <0 0xfff0>;
+ qcom,sde-safe-lut-macrotile =
+ <10 0xfe00>,
+ <11 0xfc00>,
+ <12 0xf800>,
+ <0 0xf000>;
+ qcom,sde-safe-lut-nrt =
+ <0 0xffff>;
+ qcom,sde-safe-lut-cwb =
+ <0 0xffff>;
qcom,sde-qos-lut-linear =
<4 0x00000000 0x00000357>,
<5 0x00000000 0x00003357>,
@@ -207,6 +221,9 @@
qcom,sde-dspp-blocks {
qcom,sde-dspp-igc = <0x0 0x00030001>;
+ qcom,sde-dspp-hsic = <0x800 0x00010007>;
+ qcom,sde-dspp-memcolor = <0x880 0x00010007>;
+ qcom,sde-dspp-sixzone= <0x900 0x00010007>;
qcom,sde-dspp-vlut = <0xa00 0x00010008>;
qcom,sde-dspp-gamut = <0x1000 0x00040000>;
qcom,sde-dspp-pcc = <0x1700 0x00040000>;
diff --git a/arch/arm64/boot/dts/qcom/sdm845-usb.dtsi b/arch/arm64/boot/dts/qcom/sdm845-usb.dtsi
index 09f4efa..ba397e5 100644
--- a/arch/arm64/boot/dts/qcom/sdm845-usb.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm845-usb.dtsi
@@ -78,6 +78,7 @@
snps,disable-clk-gating;
snps,has-lpm-erratum;
snps,hird-threshold = /bits/ 8 <0x10>;
+ snps,usb3_lpm_capable;
usb-core-id = <0>;
};
@@ -387,6 +388,7 @@
snps,disable-clk-gating;
snps,has-lpm-erratum;
snps,hird-threshold = /bits/ 8 <0x10>;
+ snps,usb3_lpm_capable;
usb-core-id = <1>;
};
};
diff --git a/arch/arm64/boot/dts/qcom/sdm845-v2-camera.dtsi b/arch/arm64/boot/dts/qcom/sdm845-v2-camera.dtsi
index 0b6e27e..ca83bed 100644
--- a/arch/arm64/boot/dts/qcom/sdm845-v2-camera.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm845-v2-camera.dtsi
@@ -256,6 +256,7 @@
reg-cam-base = <0x40000 0x42000>;
interrupt-names = "cpas_camnoc";
interrupts = <0 459 0>;
+ qcom,cpas-hw-ver = <0x170110>; /* Titan v170 v1.1.0 */
regulator-names = "camss-vdd";
camss-vdd-supply = <&titan_top_gdsc>;
clock-names = "gcc_ahb_clk",
diff --git a/arch/arm64/boot/dts/qcom/sdm845-v2.1-4k-panel-cdp-overlay.dts b/arch/arm64/boot/dts/qcom/sdm845-v2.1-4k-panel-cdp-overlay.dts
new file mode 100644
index 0000000..b7bc3dc
--- /dev/null
+++ b/arch/arm64/boot/dts/qcom/sdm845-v2.1-4k-panel-cdp-overlay.dts
@@ -0,0 +1,66 @@
+/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+
+/dts-v1/;
+/plugin/;
+
+#include <dt-bindings/clock/qcom,gcc-sdm845.h>
+#include <dt-bindings/clock/qcom,camcc-sdm845.h>
+#include <dt-bindings/clock/qcom,dispcc-sdm845.h>
+#include <dt-bindings/clock/qcom,rpmh.h>
+#include <dt-bindings/interrupt-controller/arm-gic.h>
+
+#include "sdm845-sde-display.dtsi"
+#include "sdm845-cdp.dtsi"
+#include "sdm845-cdp-audio-overlay.dtsi"
+
+/ {
+ model = "Qualcomm Technologies, Inc. sdm845 v2.1 4K Display Panel CDP";
+ compatible = "qcom,sdm845-cdp", "qcom,sdm845", "qcom,cdp";
+ qcom,msm-id = <321 0x20001>;
+ qcom,board-id = <1 1>;
+};
+
+&dsi_nt35597_truly_dsc_cmd_display {
+ /delete-property/ qcom,dsi-display-active;
+};
+
+&mdss_mdp {
+ connectors = <&sde_rscc &sde_wb>;
+};
+
+&dsi_sharp_4k_dsc_video {
+ qcom,panel-supply-entries = <&dsi_panel_pwr_supply>;
+ qcom,mdss-dsi-bl-pmic-control-type = "bl_ctrl_wled";
+ qcom,mdss-dsi-bl-min-level = <1>;
+ qcom,mdss-dsi-bl-max-level = <4095>;
+ qcom,mdss-dsi-mode-sel-gpio-state = "dual_port";
+ qcom,panel-mode-gpio = <&tlmm 52 0>;
+ qcom,platform-te-gpio = <&tlmm 10 0>;
+ qcom,platform-reset-gpio = <&tlmm 6 0>;
+};
+
+&dsi_sharp_4k_dsc_cmd {
+ qcom,panel-supply-entries = <&dsi_panel_pwr_supply>;
+ qcom,mdss-dsi-bl-pmic-control-type = "bl_ctrl_wled";
+ qcom,mdss-dsi-bl-min-level = <1>;
+ qcom,mdss-dsi-bl-max-level = <4095>;
+ qcom,mdss-dsi-mode-sel-gpio-state = "dual_port";
+ qcom,panel-mode-gpio = <&tlmm 52 0>;
+ qcom,platform-te-gpio = <&tlmm 10 0>;
+ qcom,platform-reset-gpio = <&tlmm 6 0>;
+};
+
+&dsi_sharp_4k_dsc_video_display {
+ qcom,dsi-display-active;
+};
diff --git a/arch/arm64/boot/dts/qcom/sdm845-v2.1-4k-panel-mtp-overlay.dts b/arch/arm64/boot/dts/qcom/sdm845-v2.1-4k-panel-mtp-overlay.dts
new file mode 100644
index 0000000..95bd94b
--- /dev/null
+++ b/arch/arm64/boot/dts/qcom/sdm845-v2.1-4k-panel-mtp-overlay.dts
@@ -0,0 +1,66 @@
+/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+
+/dts-v1/;
+/plugin/;
+
+#include <dt-bindings/clock/qcom,gcc-sdm845.h>
+#include <dt-bindings/clock/qcom,camcc-sdm845.h>
+#include <dt-bindings/clock/qcom,dispcc-sdm845.h>
+#include <dt-bindings/clock/qcom,rpmh.h>
+#include <dt-bindings/interrupt-controller/arm-gic.h>
+
+#include "sdm845-sde-display.dtsi"
+#include "sdm845-mtp.dtsi"
+#include "sdm845-audio-overlay.dtsi"
+
+/ {
+ model = "Qualcomm Technologies, Inc. sdm845 v2.1 4K Display Panel MTP";
+ compatible = "qcom,sdm845-mtp", "qcom,sdm845", "qcom,mtp";
+ qcom,msm-id = <321 0x20001>;
+ qcom,board-id = <8 1>;
+};
+
+&dsi_nt35597_truly_dsc_cmd_display {
+ /delete-property/ qcom,dsi-display-active;
+};
+
+&mdss_mdp {
+ connectors = <&sde_rscc &sde_wb &sde_dp>;
+};
+
+&dsi_sharp_4k_dsc_video {
+ qcom,panel-supply-entries = <&dsi_panel_pwr_supply>;
+ qcom,mdss-dsi-bl-pmic-control-type = "bl_ctrl_wled";
+ qcom,mdss-dsi-bl-min-level = <1>;
+ qcom,mdss-dsi-bl-max-level = <4095>;
+ qcom,mdss-dsi-mode-sel-gpio-state = "dual_port";
+ qcom,panel-mode-gpio = <&tlmm 52 0>;
+ qcom,platform-te-gpio = <&tlmm 10 0>;
+ qcom,platform-reset-gpio = <&tlmm 6 0>;
+};
+
+&dsi_sharp_4k_dsc_cmd {
+ qcom,panel-supply-entries = <&dsi_panel_pwr_supply>;
+ qcom,mdss-dsi-bl-pmic-control-type = "bl_ctrl_wled";
+ qcom,mdss-dsi-bl-min-level = <1>;
+ qcom,mdss-dsi-bl-max-level = <4095>;
+ qcom,mdss-dsi-mode-sel-gpio-state = "dual_port";
+ qcom,panel-mode-gpio = <&tlmm 52 0>;
+ qcom,platform-te-gpio = <&tlmm 10 0>;
+ qcom,platform-reset-gpio = <&tlmm 6 0>;
+};
+
+&dsi_sharp_4k_dsc_video_display {
+ qcom,dsi-display-active;
+};
diff --git a/arch/arm64/boot/dts/qcom/sdm845-v2.1-4k-panel-qrd-overlay.dts b/arch/arm64/boot/dts/qcom/sdm845-v2.1-4k-panel-qrd-overlay.dts
new file mode 100644
index 0000000..42ccbf5
--- /dev/null
+++ b/arch/arm64/boot/dts/qcom/sdm845-v2.1-4k-panel-qrd-overlay.dts
@@ -0,0 +1,64 @@
+/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+
+/dts-v1/;
+/plugin/;
+
+#include <dt-bindings/clock/qcom,gcc-sdm845.h>
+#include <dt-bindings/clock/qcom,camcc-sdm845.h>
+#include <dt-bindings/clock/qcom,dispcc-sdm845.h>
+#include <dt-bindings/clock/qcom,rpmh.h>
+#include <dt-bindings/interrupt-controller/arm-gic.h>
+
+#include "sdm845-sde-display.dtsi"
+#include "sdm845-qrd.dtsi"
+#include "sdm845-qrd-audio-overlay.dtsi"
+
+/ {
+ model = "Qualcomm Technologies, Inc. sdm845 v2.1 4K Display Panel QRD";
+ compatible = "qcom,sdm845-qrd", "qcom,sdm845", "qcom,qrd";
+ qcom,msm-id = <321 0x20001>;
+ qcom,board-id = <11 1>;
+};
+
+&dsi_nt35597_truly_dsc_cmd_display {
+ /delete-property/ qcom,dsi-display-active;
+};
+
+&dsi_sharp_4k_dsc_video {
+ qcom,panel-supply-entries = <&dsi_panel_pwr_supply>;
+ qcom,mdss-dsi-bl-pmic-control-type = "bl_ctrl_wled";
+ qcom,mdss-dsi-bl-min-level = <1>;
+ qcom,mdss-dsi-bl-max-level = <4095>;
+ qcom,mdss-dsi-mode-sel-gpio-state = "dual_port";
+ qcom,panel-mode-gpio = <&tlmm 52 0>;
+ qcom,platform-te-gpio = <&tlmm 10 0>;
+ qcom,platform-reset-gpio = <&tlmm 6 0>;
+ qcom,mdss-dsi-panel-orientation = "180";
+};
+
+&dsi_sharp_4k_dsc_cmd {
+ qcom,panel-supply-entries = <&dsi_panel_pwr_supply>;
+ qcom,mdss-dsi-bl-pmic-control-type = "bl_ctrl_wled";
+ qcom,mdss-dsi-bl-min-level = <1>;
+ qcom,mdss-dsi-bl-max-level = <4095>;
+ qcom,mdss-dsi-mode-sel-gpio-state = "dual_port";
+ qcom,panel-mode-gpio = <&tlmm 52 0>;
+ qcom,platform-te-gpio = <&tlmm 10 0>;
+ qcom,platform-reset-gpio = <&tlmm 6 0>;
+ qcom,mdss-dsi-panel-orientation = "180";
+};
+
+&dsi_sharp_4k_dsc_video_display {
+ qcom,dsi-display-active;
+};
diff --git a/arch/arm64/boot/dts/qcom/sdm845-v2.1-cdp-overlay.dts b/arch/arm64/boot/dts/qcom/sdm845-v2.1-cdp-overlay.dts
new file mode 100644
index 0000000..0825f4b
--- /dev/null
+++ b/arch/arm64/boot/dts/qcom/sdm845-v2.1-cdp-overlay.dts
@@ -0,0 +1,31 @@
+/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+/dts-v1/;
+/plugin/;
+
+#include <dt-bindings/clock/qcom,gcc-sdm845.h>
+#include <dt-bindings/clock/qcom,camcc-sdm845.h>
+#include <dt-bindings/clock/qcom,dispcc-sdm845.h>
+#include <dt-bindings/clock/qcom,rpmh.h>
+#include <dt-bindings/interrupt-controller/arm-gic.h>
+
+#include "sdm845-sde-display.dtsi"
+#include "sdm845-cdp.dtsi"
+#include "sdm845-cdp-audio-overlay.dtsi"
+
+/ {
+ model = "Qualcomm Technologies, Inc. SDM845 v2.1 CDP";
+ compatible = "qcom,sdm845-cdp", "qcom,sdm845", "qcom,cdp";
+ qcom,msm-id = <321 0x20001>;
+ qcom,board-id = <1 0>;
+};
diff --git a/arch/arm64/boot/dts/qcom/sdm845-v2.1-mtp-overlay.dts b/arch/arm64/boot/dts/qcom/sdm845-v2.1-mtp-overlay.dts
new file mode 100644
index 0000000..7b3573a
--- /dev/null
+++ b/arch/arm64/boot/dts/qcom/sdm845-v2.1-mtp-overlay.dts
@@ -0,0 +1,31 @@
+/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+/dts-v1/;
+/plugin/;
+
+#include <dt-bindings/clock/qcom,gcc-sdm845.h>
+#include <dt-bindings/clock/qcom,camcc-sdm845.h>
+#include <dt-bindings/clock/qcom,dispcc-sdm845.h>
+#include <dt-bindings/clock/qcom,rpmh.h>
+#include <dt-bindings/interrupt-controller/arm-gic.h>
+
+#include "sdm845-sde-display.dtsi"
+#include "sdm845-mtp.dtsi"
+#include "sdm845-audio-overlay.dtsi"
+
+/ {
+ model = "Qualcomm Technologies, Inc. SDM845 v2.1 MTP";
+ compatible = "qcom,sdm845-mtp", "qcom,sdm845", "qcom,mtp";
+ qcom,msm-id = <321 0x20001>;
+ qcom,board-id = <8 0>;
+};
diff --git a/arch/arm64/boot/dts/qcom/sdm845-v2.1-qrd-overlay.dts b/arch/arm64/boot/dts/qcom/sdm845-v2.1-qrd-overlay.dts
new file mode 100644
index 0000000..15d79c2
--- /dev/null
+++ b/arch/arm64/boot/dts/qcom/sdm845-v2.1-qrd-overlay.dts
@@ -0,0 +1,31 @@
+/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+/dts-v1/;
+/plugin/;
+
+#include <dt-bindings/clock/qcom,gcc-sdm845.h>
+#include <dt-bindings/clock/qcom,camcc-sdm845.h>
+#include <dt-bindings/clock/qcom,dispcc-sdm845.h>
+#include <dt-bindings/clock/qcom,rpmh.h>
+#include <dt-bindings/interrupt-controller/arm-gic.h>
+
+#include "sdm845-sde-display.dtsi"
+#include "sdm845-qrd.dtsi"
+#include "sdm845-qrd-audio-overlay.dtsi"
+
+/ {
+ model = "Qualcomm Technologies, Inc. SDM845 v2.1 QRD";
+ compatible = "qcom,sdm845-qrd", "qcom,sdm845", "qcom,qrd";
+ qcom,msm-id = <321 0x20001>;
+ qcom,board-id = <11 0>;
+};
diff --git a/drivers/media/platform/msm/camera/cam_jpeg/jpeg_hw/include/cam_dma_hw_intf.h b/arch/arm64/boot/dts/qcom/sdm845-v2.1.dts
similarity index 61%
rename from drivers/media/platform/msm/camera/cam_jpeg/jpeg_hw/include/cam_dma_hw_intf.h
rename to arch/arm64/boot/dts/qcom/sdm845-v2.1.dts
index 71b21b9..2902a60 100644
--- a/drivers/media/platform/msm/camera/cam_jpeg/jpeg_hw/include/cam_dma_hw_intf.h
+++ b/arch/arm64/boot/dts/qcom/sdm845-v2.1.dts
@@ -10,19 +10,13 @@
* GNU General Public License for more details.
*/
-#ifndef CAM_JPEG_DMA_HW_INTF_H
-#define CAM_JPEG_DMA_HW_INTF_H
+/dts-v1/;
-#include <uapi/media/cam_defs.h>
-#include <media/cam_jpeg.h>
+#include "sdm845-v2.1.dtsi"
-#include "cam_hw_mgr_intf.h"
-#include "cam_jpeg_hw_intf.h"
-
-enum cam_jpeg_dma_cmd_type {
- CAM_JPEG_DMA_CMD_CDM_CFG,
- CAM_JPEG_DMA_CMD_SET_IRQ_CB,
- CAM_JPEG_DMA_CMD_MAX,
+/ {
+ model = "Qualcomm Technologies, Inc. SDM845 v2.1 SoC";
+ compatible = "qcom,sdm845";
+ qcom,board-id = <0 0>;
};
-#endif /* CAM_JPEG_DMA_HW_INTF_H */
diff --git a/drivers/media/platform/msm/camera/cam_jpeg/jpeg_hw/include/cam_dma_hw_intf.h b/arch/arm64/boot/dts/qcom/sdm845-v2.1.dtsi
similarity index 61%
copy from drivers/media/platform/msm/camera/cam_jpeg/jpeg_hw/include/cam_dma_hw_intf.h
copy to arch/arm64/boot/dts/qcom/sdm845-v2.1.dtsi
index 71b21b9..ff8c01a 100644
--- a/drivers/media/platform/msm/camera/cam_jpeg/jpeg_hw/include/cam_dma_hw_intf.h
+++ b/arch/arm64/boot/dts/qcom/sdm845-v2.1.dtsi
@@ -10,19 +10,18 @@
* GNU General Public License for more details.
*/
-#ifndef CAM_JPEG_DMA_HW_INTF_H
-#define CAM_JPEG_DMA_HW_INTF_H
+#include "sdm845-v2.dtsi"
+#include "sdm845-v2-camera.dtsi"
-#include <uapi/media/cam_defs.h>
-#include <media/cam_jpeg.h>
-
-#include "cam_hw_mgr_intf.h"
-#include "cam_jpeg_hw_intf.h"
-
-enum cam_jpeg_dma_cmd_type {
- CAM_JPEG_DMA_CMD_CDM_CFG,
- CAM_JPEG_DMA_CMD_SET_IRQ_CB,
- CAM_JPEG_DMA_CMD_MAX,
+/ {
+ model = "Qualcomm Technologies, Inc. SDM845 V2.1";
+ qcom,msm-id = <321 0x20001>;
};
-#endif /* CAM_JPEG_DMA_HW_INTF_H */
+&spmi_bus {
+ /delete-property/ qcom,enable-ahb-bus-workaround;
+};
+
+&clock_gcc {
+ compatible = "qcom,gcc-sdm845-v2.1", "syscon";
+};
diff --git a/arch/arm64/boot/dts/qcom/sdm845-v2.dtsi b/arch/arm64/boot/dts/qcom/sdm845-v2.dtsi
index 6c85daf..88e2d50 100644
--- a/arch/arm64/boot/dts/qcom/sdm845-v2.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm845-v2.dtsi
@@ -22,674 +22,13 @@
/delete-property/ qcom,sdr104-wa;
};
-/delete-node/ &apc0_cpr;
-/delete-node/ &apc1_cpr;
+/delete-node/ &pdc;
&tlmm {
compatible = "qcom,sdm845-pinctrl-v2";
};
&soc {
- /* CPR controller regulators */
- apc0_cpr: cprh-ctrl@17dc0000 {
- compatible = "qcom,cprh-sdm845-v2-kbss-regulator";
- reg = <0x17dc0000 0x4000>,
- <0x00784000 0x1000>,
- <0x17840000 0x1000>;
- reg-names = "cpr_ctrl", "fuse_base", "saw";
- clocks = <&clock_gcc GCC_CPUSS_RBCPR_CLK>;
- clock-names = "core_clk";
- qcom,cpr-ctrl-name = "apc0";
- qcom,cpr-controller-id = <0>;
-
- qcom,cpr-sensor-time = <1000>;
- qcom,cpr-loop-time = <5000000>;
- qcom,cpr-idle-cycles = <15>;
- qcom,cpr-up-down-delay-time = <3000>;
- qcom,cpr-step-quot-init-min = <11>;
- qcom,cpr-step-quot-init-max = <12>;
- qcom,cpr-count-mode = <0>; /* All at once */
- qcom,cpr-count-repeat = <20>;
- qcom,cpr-down-error-step-limit = <1>;
- qcom,cpr-up-error-step-limit = <1>;
- qcom,cpr-corner-switch-delay-time = <1042>;
- qcom,cpr-voltage-settling-time = <1760>;
- qcom,cpr-reset-step-quot-loop-en;
-
- qcom,voltage-step = <4000>;
- qcom,voltage-base = <352000>;
- qcom,cpr-saw-use-unit-mV;
-
- qcom,saw-avs-ctrl = <0x101C031>;
- qcom,saw-avs-limit = <0x3E803E8>;
-
- qcom,cpr-enable;
- qcom,cpr-hw-closed-loop;
-
- qcom,cpr-panic-reg-addr-list =
- <0x17dc3a84 0x17dc3a88 0x17840c18>;
- qcom,cpr-panic-reg-name-list =
- "APSS_SILVER_CPRH_STATUS_0",
- "APSS_SILVER_CPRH_STATUS_1",
- "SILVER_SAW4_PMIC_STS";
-
- qcom,cpr-aging-ref-voltage = <1000000>;
- vdd-supply = <&pm8998_s13>;
-
- thread@0 {
- qcom,cpr-thread-id = <0>;
- qcom,cpr-consecutive-up = <0>;
- qcom,cpr-consecutive-down = <0>;
- qcom,cpr-up-threshold = <2>;
- qcom,cpr-down-threshold = <2>;
-
- apc0_pwrcl_vreg: regulator {
- regulator-name = "apc0_pwrcl_corner";
- regulator-min-microvolt = <1>;
- regulator-max-microvolt = <18>;
-
- qcom,cpr-fuse-corners = <4>;
- qcom,cpr-fuse-combos = <24>;
- qcom,cpr-speed-bins = <3>;
- qcom,cpr-speed-bin-corners = <18 18 18>;
- qcom,cpr-corners = <18>;
-
- qcom,cpr-corner-fmax-map = <6 12 15 18>;
-
- qcom,cpr-voltage-ceiling =
- <828000 828000 828000 828000 828000
- 828000 828000 828000 828000 828000
- 828000 828000 828000 828000 828000
- 932000 1000000 1000000>;
-
- qcom,cpr-voltage-floor =
- <568000 568000 568000 568000 568000
- 568000 568000 568000 568000 568000
- 568000 568000 568000 568000 568000
- 568000 568000 568000>;
-
- qcom,cpr-floor-to-ceiling-max-range =
- <32000 32000 32000 32000 32000
- 32000 32000 32000 32000 32000
- 32000 32000 32000 32000 32000
- 32000 40000 40000>;
-
- qcom,corner-frequencies =
- <300000000 403200000 480000000
- 576000000 652800000 748800000
- 825600000 902400000 979200000
- 1056000000 1132800000 1228800000
- 1324800000 1420800000 1516800000
- 1612800000 1689600000 1766400000>;
-
- qcom,cpr-ro-scaling-factor =
- <2594 2795 2576 2761 2469 2673 2198
- 2553 3188 3255 3191 2962 3055 2984
- 2043 2947>,
- <2594 2795 2576 2761 2469 2673 2198
- 2553 3188 3255 3191 2962 3055 2984
- 2043 2947>,
- <2259 2389 2387 2531 2294 2464 2218
- 2476 2525 2855 2817 2836 2740 2490
- 1950 2632>,
- <2259 2389 2387 2531 2294 2464 2218
- 2476 2525 2855 2817 2836 2740 2490
- 1950 2632>;
-
- qcom,cpr-open-loop-voltage-fuse-adjustment =
- /* Speed bin 0 */
- < 0 0 12000 12000>,
- <(-15000) (-15000) (-3000) (-3000)>,
- <(-15000) (-15000) (-3000) (-3000)>,
- <(-15000) (-15000) (-3000) (-3000)>,
- <(-15000) (-15000) (-3000) (-3000)>,
- <(-15000) (-15000) (-3000) (-3000)>,
- <(-15000) (-15000) (-3000) (-3000)>,
- <(-15000) (-15000) (-3000) (-3000)>,
- /* Speed bin 1 */
- < 0 0 12000 12000>,
- <(-15000) (-15000) (-3000) (-3000)>,
- <(-15000) (-15000) (-3000) (-3000)>,
- <(-15000) (-15000) (-3000) (-3000)>,
- <(-15000) (-15000) (-3000) (-3000)>,
- <(-15000) (-15000) (-3000) (-3000)>,
- <(-15000) (-15000) (-3000) (-3000)>,
- <(-15000) (-15000) (-3000) (-3000)>,
- /* Speed bin 2 */
- < 0 0 12000 12000>,
- <(-15000) (-15000) (-3000) (-3000)>,
- <(-15000) (-15000) (-3000) (-3000)>,
- <(-15000) (-15000) (-3000) (-3000)>,
- <(-15000) (-15000) (-3000) (-3000)>,
- <(-15000) (-15000) (-3000) (-3000)>,
- <(-15000) (-15000) (-3000) (-3000)>,
- <(-15000) (-15000) (-3000) (-3000)>;
-
- qcom,cpr-closed-loop-voltage-fuse-adjustment =
- /* Speed bin 0 */
- < 0 0 12000 10000>,
- <(-15000) (-15000) (-3000) (-5000)>,
- <(-15000) (-15000) (-3000) (-5000)>,
- <(-15000) (-15000) (-3000) (-5000)>,
- <(-15000) (-15000) (-3000) (-5000)>,
- <(-15000) (-15000) (-3000) (-5000)>,
- <(-15000) (-15000) (-3000) (-5000)>,
- <(-15000) (-15000) (-3000) (-5000)>,
- /* Speed bin 1 */
- < 0 0 12000 10000>,
- <(-15000) (-15000) (-3000) (-5000)>,
- <(-15000) (-15000) (-3000) (-5000)>,
- <(-15000) (-15000) (-3000) (-5000)>,
- <(-15000) (-15000) (-3000) (-5000)>,
- <(-15000) (-15000) (-3000) (-5000)>,
- <(-15000) (-15000) (-3000) (-5000)>,
- <(-15000) (-15000) (-3000) (-5000)>,
- /* Speed bin 2 */
- < 0 0 12000 10000>,
- <(-15000) (-15000) (-3000) (-5000)>,
- <(-15000) (-15000) (-3000) (-5000)>,
- <(-15000) (-15000) (-3000) (-5000)>,
- <(-15000) (-15000) (-3000) (-5000)>,
- <(-15000) (-15000) (-3000) (-5000)>,
- <(-15000) (-15000) (-3000) (-5000)>,
- <(-15000) (-15000) (-3000) (-5000)>;
-
- qcom,allow-voltage-interpolation;
- qcom,allow-quotient-interpolation;
- qcom,cpr-scaled-open-loop-voltage-as-ceiling;
-
- qcom,cpr-aging-max-voltage-adjustment = <15000>;
- qcom,cpr-aging-ref-corner = <18>;
- qcom,cpr-aging-ro-scaling-factor = <1620>;
- qcom,allow-aging-voltage-adjustment =
- /* Speed bin 0 */
- <0 1 1 1 1 1 1 1>,
- /* Speed bin 1 */
- <0 1 1 1 1 1 1 1>,
- /* Speed bin 2 */
- <0 1 1 1 1 1 1 1>;
- qcom,allow-aging-open-loop-voltage-adjustment =
- <1>;
- };
- };
-
- thread@1 {
- qcom,cpr-thread-id = <1>;
- qcom,cpr-consecutive-up = <0>;
- qcom,cpr-consecutive-down = <0>;
- qcom,cpr-up-threshold = <2>;
- qcom,cpr-down-threshold = <2>;
-
- apc0_l3_vreg: regulator {
- regulator-name = "apc0_l3_corner";
- regulator-min-microvolt = <1>;
- regulator-max-microvolt = <15>;
-
- qcom,cpr-fuse-corners = <4>;
- qcom,cpr-fuse-combos = <24>;
- qcom,cpr-speed-bins = <3>;
- qcom,cpr-speed-bin-corners = <14 15 15>;
- qcom,cpr-corners =
- /* Speed bin 0 */
- <14 14 14 14 14 14 14 14>,
- /* Speed bin 1 */
- <15 15 15 15 15 15 15 15>,
- /* Speed bin 2 */
- <15 15 15 15 15 15 15 15>;
-
- qcom,cpr-corner-fmax-map =
- /* Speed bin 0 */
- <4 8 11 14>,
- /* Speed bin 1 */
- <4 8 11 15>,
- /* Speed bin 2 */
- <4 8 11 15>;
-
- qcom,cpr-voltage-ceiling =
- /* Speed bin 0 */
- <828000 828000 828000 828000 828000
- 828000 828000 828000 828000 828000
- 828000 932000 932000 1000000>,
- /* Speed bin 1 */
- <828000 828000 828000 828000 828000
- 828000 828000 828000 828000 828000
- 828000 932000 932000 1000000
- 1000000>,
- /* Speed bin 2 */
- <828000 828000 828000 828000 828000
- 828000 828000 828000 828000 828000
- 828000 932000 932000 1000000
- 1000000>;
-
- qcom,cpr-voltage-floor =
- /* Speed bin 0 */
- <568000 568000 568000 568000 568000
- 568000 568000 568000 568000 568000
- 568000 568000 568000 568000>,
- /* Speed bin 1 */
- <568000 568000 568000 568000 568000
- 568000 568000 568000 568000 568000
- 568000 568000 568000 568000
- 568000>,
- /* Speed bin 2 */
- <568000 568000 568000 568000 568000
- 568000 568000 568000 568000 568000
- 568000 568000 568000 568000
- 568000>;
-
- qcom,cpr-floor-to-ceiling-max-range =
- /* Speed bin 0 */
- <32000 32000 32000 32000 32000
- 32000 32000 32000 32000 32000
- 32000 32000 32000 40000>,
- /* Speed bin 1 */
- <32000 32000 32000 32000 32000
- 32000 32000 32000 32000 32000
- 32000 32000 32000 40000 40000>,
- /* Speed bin 2 */
- <32000 32000 32000 32000 32000
- 32000 32000 32000 32000 32000
- 32000 32000 32000 40000 40000>;
-
- qcom,corner-frequencies =
- /* Speed bin 0 */
- <300000000 403200000 480000000
- 576000000 652800000 748800000
- 844800000 940800000 1036800000
- 1132800000 1209600000 1305600000
- 1401600000 1478400000>,
- /* Speed bin 1 */
- <300000000 403200000 480000000
- 576000000 652800000 748800000
- 844800000 940800000 1036800000
- 1132800000 1209600000 1305600000
- 1401600000 1497600000 1593600000>,
- /* Speed bin 2 */
- <300000000 403200000 480000000
- 576000000 652800000 748800000
- 844800000 940800000 1036800000
- 1132800000 1209600000 1305600000
- 1401600000 1497600000 1593600000>;
-
- qcom,cpr-ro-scaling-factor =
- <2857 3056 2828 2952 2699 2796 2447
- 2631 2630 2579 2244 3343 3287 3137
- 3164 2656>,
- <2857 3056 2828 2952 2699 2796 2447
- 2631 2630 2579 2244 3343 3287 3137
- 3164 2656>,
- <2439 2577 2552 2667 2461 2577 2394
- 2536 2132 2307 2191 2903 2838 2912
- 2501 2095>,
- <2439 2577 2552 2667 2461 2577 2394
- 2536 2132 2307 2191 2903 2838 2912
- 2501 2095>;
-
- qcom,cpr-open-loop-voltage-fuse-adjustment =
- /* Speed bin 0 */
- < 8000 16000 16000 12000>,
- < (-7000) 1000 1000 (-3000)>,
- < (-7000) 1000 1000 (-3000)>,
- < (-7000) 1000 1000 (-3000)>,
- < (-7000) 1000 1000 (-3000)>,
- < (-7000) 1000 1000 (-3000)>,
- < (-7000) 1000 1000 (-3000)>,
- < (-7000) 1000 1000 (-3000)>,
- /* Speed bin 1 */
- < 8000 16000 16000 12000>,
- < (-7000) 1000 1000 (-3000)>,
- < (-7000) 1000 1000 (-3000)>,
- < (-7000) 1000 1000 (-3000)>,
- < (-7000) 1000 1000 (-3000)>,
- < (-7000) 1000 1000 (-3000)>,
- < (-7000) 1000 1000 (-3000)>,
- < (-7000) 1000 1000 (-3000)>,
- /* Speed bin 2 */
- < 8000 16000 16000 12000>,
- < (-7000) 1000 1000 (-3000)>,
- < (-7000) 1000 1000 (-3000)>,
- < (-7000) 1000 1000 (-3000)>,
- < (-7000) 1000 1000 (-3000)>,
- < (-7000) 1000 1000 (-3000)>,
- < (-7000) 1000 1000 (-3000)>,
- < (-7000) 1000 1000 (-3000)>;
-
- qcom,cpr-closed-loop-voltage-fuse-adjustment =
- /* Speed bin 0 */
- < 6000 14000 16000 12000>,
- < (-9000) (-1000) 1000 (-3000)>,
- < (-9000) (-1000) 1000 (-3000)>,
- < (-9000) (-1000) 1000 (-3000)>,
- < (-9000) (-1000) 1000 (-3000)>,
- < (-9000) (-1000) 1000 (-3000)>,
- < (-9000) (-1000) 1000 (-3000)>,
- < (-9000) (-1000) 1000 (-3000)>,
- /* Speed bin 1 */
- < 6000 14000 16000 12000>,
- < (-9000) (-1000) 1000 (-3000)>,
- < (-9000) (-1000) 1000 (-3000)>,
- < (-9000) (-1000) 1000 (-3000)>,
- < (-9000) (-1000) 1000 (-3000)>,
- < (-9000) (-1000) 1000 (-3000)>,
- < (-9000) (-1000) 1000 (-3000)>,
- < (-9000) (-1000) 1000 (-3000)>,
- /* Speed bin 2 */
- < 6000 14000 16000 12000>,
- < (-9000) (-1000) 1000 (-3000)>,
- < (-9000) (-1000) 1000 (-3000)>,
- < (-9000) (-1000) 1000 (-3000)>,
- < (-9000) (-1000) 1000 (-3000)>,
- < (-9000) (-1000) 1000 (-3000)>,
- < (-9000) (-1000) 1000 (-3000)>,
- < (-9000) (-1000) 1000 (-3000)>;
-
- qcom,allow-voltage-interpolation;
- qcom,allow-quotient-interpolation;
- qcom,cpr-scaled-open-loop-voltage-as-ceiling;
-
- qcom,cpr-aging-max-voltage-adjustment = <15000>;
- qcom,cpr-aging-ref-corner = <14 15 15>;
- qcom,cpr-aging-ro-scaling-factor = <1620>;
- qcom,allow-aging-voltage-adjustment =
- /* Speed bin 0 */
- <0 1 1 1 1 1 1 1>,
- /* Speed bin 1 */
- <0 1 1 1 1 1 1 1>,
- /* Speed bin 2 */
- <0 1 1 1 1 1 1 1>;
- qcom,allow-aging-open-loop-voltage-adjustment =
- <1>;
- };
- };
- };
-
- apc1_cpr: cprh-ctrl@17db0000 {
- compatible = "qcom,cprh-sdm845-v2-kbss-regulator";
- reg = <0x17db0000 0x4000>,
- <0x00784000 0x1000>,
- <0x17830000 0x1000>;
- reg-names = "cpr_ctrl", "fuse_base", "saw";
- clocks = <&clock_gcc GCC_CPUSS_RBCPR_CLK>;
- clock-names = "core_clk";
- qcom,cpr-ctrl-name = "apc1";
- qcom,cpr-controller-id = <1>;
-
- qcom,cpr-sensor-time = <1000>;
- qcom,cpr-loop-time = <5000000>;
- qcom,cpr-idle-cycles = <15>;
- qcom,cpr-up-down-delay-time = <3000>;
- qcom,cpr-step-quot-init-min = <9>;
- qcom,cpr-step-quot-init-max = <14>;
- qcom,cpr-count-mode = <0>; /* All at once */
- qcom,cpr-count-repeat = <20>;
- qcom,cpr-down-error-step-limit = <1>;
- qcom,cpr-up-error-step-limit = <1>;
- qcom,cpr-corner-switch-delay-time = <1042>;
- qcom,cpr-voltage-settling-time = <1760>;
- qcom,cpr-reset-step-quot-loop-en;
-
- qcom,apm-threshold-voltage = <800000>;
- qcom,apm-crossover-voltage = <880000>;
- qcom,mem-acc-threshold-voltage = <852000>;
- qcom,mem-acc-crossover-voltage = <852000>;
-
- qcom,voltage-step = <4000>;
- qcom,voltage-base = <352000>;
- qcom,cpr-saw-use-unit-mV;
-
- qcom,saw-avs-ctrl = <0x101C031>;
- qcom,saw-avs-limit = <0x4880488>;
-
- qcom,cpr-enable;
- qcom,cpr-hw-closed-loop;
-
- qcom,cpr-panic-reg-addr-list =
- <0x17db3a84 0x17830c18>;
- qcom,cpr-panic-reg-name-list =
- "APSS_GOLD_CPRH_STATUS_0", "GOLD_SAW4_PMIC_STS";
-
- qcom,cpr-aging-ref-voltage = <1160000>;
- vdd-supply = <&pm8998_s12>;
-
- thread@0 {
- qcom,cpr-thread-id = <0>;
- qcom,cpr-consecutive-up = <0>;
- qcom,cpr-consecutive-down = <0>;
- qcom,cpr-up-threshold = <2>;
- qcom,cpr-down-threshold = <2>;
-
- apc1_perfcl_vreg: regulator {
- regulator-name = "apc1_perfcl_corner";
- regulator-min-microvolt = <1>;
- regulator-max-microvolt = <35>;
-
- qcom,cpr-fuse-corners = <5>;
- qcom,cpr-fuse-combos = <24>;
- qcom,cpr-speed-bins = <3>;
- qcom,cpr-speed-bin-corners = <28 31 33>;
- qcom,cpr-corners =
- /* Speed bin 0 */
- <28 28 28 28 28 28 28 28>,
- /* Speed bin 1 */
- <31 31 31 31 31 31 31 31>,
- /* Speed bin 2 */
- <33 33 33 33 33 33 33 33>;
-
- qcom,cpr-corner-fmax-map =
- /* Speed bin 0 */
- <7 14 22 27 28>,
- /* Speed bin 1 */
- <7 14 22 27 31>,
- /* Speed bin 2 */
- <7 14 22 30 33>;
-
- qcom,cpr-voltage-ceiling =
- /* Speed bin 0 */
- <828000 828000 828000 828000 828000
- 828000 828000 828000 828000 828000
- 828000 828000 828000 828000 828000
- 828000 828000 828000 932000 932000
- 932000 932000 1104000 1104000 1104000
- 1104000 1160000 1160000>,
- /* Speed bin 1 */
- <828000 828000 828000 828000 828000
- 828000 828000 828000 828000 828000
- 828000 828000 828000 828000 828000
- 828000 828000 828000 932000 932000
- 932000 932000 1104000 1104000 1104000
- 1104000 1160000 1160000 1160000 1160000
- 1160000>,
- /* Speed bin 2 */
- <828000 828000 828000 828000 828000
- 828000 828000 828000 828000 828000
- 828000 828000 828000 828000 828000
- 828000 828000 828000 932000 932000
- 932000 932000 1104000 1104000 1104000
- 1104000 1160000 1160000 1160000 1160000
- 1160000 1160000 1160000>;
-
- qcom,cpr-voltage-floor =
- /* Speed bin 0 */
- <568000 568000 568000 568000 568000
- 568000 568000 568000 568000 568000
- 568000 568000 568000 568000 568000
- 568000 568000 568000 568000 568000
- 568000 568000 568000 568000 568000
- 568000 568000 568000>,
- /* Speed bin 1 */
- <568000 568000 568000 568000 568000
- 568000 568000 568000 568000 568000
- 568000 568000 568000 568000 568000
- 568000 568000 568000 568000 568000
- 568000 568000 568000 568000 568000
- 568000 568000 568000 568000 568000
- 568000>,
- /* Speed bin 2 */
- <568000 568000 568000 568000 568000
- 568000 568000 568000 568000 568000
- 568000 568000 568000 568000 568000
- 568000 568000 568000 568000 568000
- 568000 568000 568000 568000 568000
- 568000 568000 568000 568000 568000
- 568000 568000 568000>;
-
- qcom,cpr-floor-to-ceiling-max-range =
- /* Speed bin 0 */
- <32000 32000 32000 32000 32000
- 32000 32000 32000 32000 32000
- 32000 32000 32000 32000 32000
- 32000 32000 32000 32000 32000
- 32000 32000 32000 32000 32000
- 32000 32000 32000>,
- /* Speed bin 1 */
- <32000 32000 32000 32000 32000
- 32000 32000 32000 32000 32000
- 32000 32000 32000 32000 32000
- 32000 32000 32000 32000 32000
- 32000 32000 32000 32000 32000
- 32000 32000 40000 40000 40000
- 40000>,
- /* Speed bin 2 */
- <32000 32000 32000 32000 32000
- 32000 32000 32000 32000 32000
- 32000 32000 32000 32000 32000
- 32000 32000 32000 32000 32000
- 32000 32000 32000 32000 32000
- 32000 32000 40000 40000 40000
- 40000 40000 40000>;
-
- qcom,corner-frequencies =
- /* Speed bin 0 */
- <300000000 403200000 480000000
- 576000000 652800000 748800000
- 825600000 902400000 979200000
- 1056000000 1132800000 1209600000
- 1286400000 1363200000 1459200000
- 1536000000 1612800000 1689600000
- 1766400000 1843200000 1920000000
- 1996800000 2092800000 2169600000
- 2246400000 2323200000 2400000000
- 2400000000>,
- /* Speed bin 1 */
- <300000000 403200000 480000000
- 576000000 652800000 748800000
- 825600000 902400000 979200000
- 1056000000 1132800000 1209600000
- 1286400000 1363200000 1459200000
- 1536000000 1612800000 1689600000
- 1766400000 1843200000 1920000000
- 1996800000 2092800000 2169600000
- 2246400000 2323200000 2400000000
- 2476800000 2553600000 2649600000
- 2707200000>,
- /* Speed bin 2 */
- <300000000 403200000 480000000
- 576000000 652800000 748800000
- 825600000 902400000 979200000
- 1056000000 1132800000 1209600000
- 1286400000 1363200000 1459200000
- 1536000000 1612800000 1689600000
- 1766400000 1843200000 1920000000
- 1996800000 2092800000 2169600000
- 2246400000 2323200000 2400000000
- 2476800000 2553600000 2649600000
- 2707200000 2726400000 2745600000>;
-
- qcom,cpr-ro-scaling-factor =
- <2857 3056 2828 2952 2699 2796 2447
- 2631 2630 2579 2244 3343 3287 3137
- 3164 2656>,
- <2857 3056 2828 2952 2699 2796 2447
- 2631 2630 2579 2244 3343 3287 3137
- 3164 2656>,
- <2086 2208 2273 2408 2203 2327 2213
- 2340 1755 2039 2049 2474 2437 2618
- 2003 1675>,
- <2086 2208 2273 2408 2203 2327 2213
- 2340 1755 2039 2049 2474 2437 2618
- 2003 1675>,
- <2086 2208 2273 2408 2203 2327 2213
- 2340 1755 2039 2049 2474 2437 2618
- 2003 1675>;
-
- qcom,cpr-open-loop-voltage-fuse-adjustment =
- /* Speed bin 0 */
- < 8000 8000 8000 0 0>,
- < (-7000) (-7000) (-7000) (-15000) (-15000)>,
- < (-7000) (-7000) (-7000) (-15000) (-15000)>,
- < (-7000) (-7000) (-7000) (-15000) (-15000)>,
- < (-7000) (-7000) (-7000) (-15000) (-15000)>,
- < (-7000) (-7000) (-7000) (-15000) (-15000)>,
- < (-7000) (-7000) (-7000) (-15000) (-15000)>,
- < (-7000) (-7000) (-7000) (-15000) (-15000)>,
- /* Speed bin 1 */
- < 8000 8000 8000 0 16000>,
- < (-7000) (-7000) (-7000) (-15000) 1000>,
- < (-7000) (-7000) (-7000) (-15000) 1000>,
- < (-7000) (-7000) (-7000) (-15000) 1000>,
- < (-7000) (-7000) (-7000) (-15000) 1000>,
- < (-7000) (-7000) (-7000) (-15000) 1000>,
- < (-7000) (-7000) (-7000) (-15000) 1000>,
- < (-7000) (-7000) (-7000) (-15000) 1000>,
- /* Speed bin 2 */
- < 8000 8000 8000 0 16000>,
- < (-7000) (-7000) (-7000) (-15000) 1000>,
- < (-7000) (-7000) (-7000) (-15000) 1000>,
- < (-7000) (-7000) (-7000) (-15000) 1000>,
- < (-7000) (-7000) (-7000) (-15000) 1000>,
- < (-7000) (-7000) (-7000) (-15000) 1000>,
- < (-7000) (-7000) (-7000) (-15000) 1000>,
- < (-7000) (-7000) (-7000) (-15000) 1000>;
-
- qcom,cpr-closed-loop-voltage-fuse-adjustment =
- /* Speed bin 0 */
- < 6000 6000 8000 0 0>,
- < (-9000) (-9000) (-7000) (-15000) (-15000)>,
- < (-9000) (-9000) (-7000) (-15000) (-15000)>,
- < (-9000) (-9000) (-7000) (-15000) (-15000)>,
- < (-9000) (-9000) (-7000) (-15000) (-15000)>,
- < (-9000) (-9000) (-7000) (-15000) (-15000)>,
- < (-9000) (-9000) (-7000) (-15000) (-15000)>,
- < (-9000) (-9000) (-7000) (-15000) (-15000)>,
- /* Speed bin 1 */
- < 6000 6000 8000 0 16000>,
- < (-9000) (-9000) (-7000) (-15000) 1000>,
- < (-9000) (-9000) (-7000) (-15000) 1000>,
- < (-9000) (-9000) (-7000) (-15000) 1000>,
- < (-9000) (-9000) (-7000) (-15000) 1000>,
- < (-9000) (-9000) (-7000) (-15000) 1000>,
- < (-9000) (-9000) (-7000) (-15000) 1000>,
- < (-9000) (-9000) (-7000) (-15000) 1000>,
- /* Speed bin 2 */
- < 6000 6000 8000 0 16000>,
- < (-9000) (-9000) (-7000) (-15000) 1000>,
- < (-9000) (-9000) (-7000) (-15000) 1000>,
- < (-9000) (-9000) (-7000) (-15000) 1000>,
- < (-9000) (-9000) (-7000) (-15000) 1000>,
- < (-9000) (-9000) (-7000) (-15000) 1000>,
- < (-9000) (-9000) (-7000) (-15000) 1000>,
- < (-9000) (-9000) (-7000) (-15000) 1000>;
-
- qcom,allow-voltage-interpolation;
- qcom,allow-quotient-interpolation;
- qcom,cpr-scaled-open-loop-voltage-as-ceiling;
-
- qcom,cpr-aging-max-voltage-adjustment = <15000>;
- qcom,cpr-aging-ref-corner = <27 31 33>;
- qcom,cpr-aging-ro-scaling-factor = <1700>;
- qcom,allow-aging-voltage-adjustment =
- /* Speed bin 0 */
- <0 1 1 1 1 1 1 1>,
- /* Speed bin 1 */
- <0 1 1 1 1 1 1 1>,
- /* Speed bin 2 */
- <0 1 1 1 1 1 1 1>;
- qcom,allow-aging-open-loop-voltage-adjustment =
- <1>;
- };
- };
- };
-
gpu_gx_domain_addr: syscon@0x5091508 {
compatible = "syscon";
reg = <0x5091508 0x4>;
@@ -699,250 +38,23 @@
compatible = "syscon";
reg = <0x5091008 0x4>;
};
+
+ pdc: interrupt-controller@0xb220000{
+ compatible = "qcom,pdc-sdm845-v2";
+ reg = <0xb220000 0x400>;
+ #interrupt-cells = <3>;
+ interrupt-parent = <&intc>;
+ interrupt-controller;
+ };
+
};
&pil_modem {
qcom,mss_pdc_offset = <9>;
};
-/* VDD_APC0 */
-&pm8998_s13 {
- regulator-min-microvolt = <568000>;
- regulator-max-microvolt = <1000000>;
-};
-
-/* VDD_APC1 */
-&pm8998_s12 {
- regulator-min-microvolt = <568000>;
- regulator-max-microvolt = <1160000>;
-};
-
&clock_cpucc {
compatible = "qcom,clk-cpu-osm-v2";
-
- vdd-l3-supply = <&apc0_l3_vreg>;
- vdd-pwrcl-supply = <&apc0_pwrcl_vreg>;
- vdd-perfcl-supply = <&apc1_perfcl_vreg>;
-
- qcom,l3-speedbin0-v0 =
- < 300000000 0x000c000f 0x00002020 0x1 1 >,
- < 403200000 0x500c0115 0x00002020 0x1 2 >,
- < 480000000 0x50140219 0x00002020 0x1 3 >,
- < 576000000 0x5014031e 0x00002020 0x1 4 >,
- < 652800000 0x401c0422 0x00002020 0x1 5 >,
- < 748800000 0x401c0527 0x00002020 0x1 6 >,
- < 844800000 0x4024062c 0x00002323 0x2 7 >,
- < 940800000 0x40240731 0x00002727 0x2 8 >,
- < 1036800000 0x40240836 0x00002b2b 0x2 9 >,
- < 1132800000 0x402c093b 0x00002f2f 0x2 10 >,
- < 1209600000 0x402c0a3f 0x00003232 0x2 11 >,
- < 1305600000 0x40340b44 0x00003636 0x2 12 >,
- < 1401600000 0x40340c49 0x00003a3a 0x2 13 >,
- < 1478400000 0x403c0d4d 0x00003e3e 0x2 14 >;
-
- qcom,l3-speedbin1-v0 =
- < 300000000 0x000c000f 0x00002020 0x1 1 >,
- < 403200000 0x500c0115 0x00002020 0x1 2 >,
- < 480000000 0x50140219 0x00002020 0x1 3 >,
- < 576000000 0x5014031e 0x00002020 0x1 4 >,
- < 652800000 0x401c0422 0x00002020 0x1 5 >,
- < 748800000 0x401c0527 0x00002020 0x1 6 >,
- < 844800000 0x4024062c 0x00002323 0x2 7 >,
- < 940800000 0x40240731 0x00002727 0x2 8 >,
- < 1036800000 0x40240836 0x00002b2b 0x2 9 >,
- < 1132800000 0x402c093b 0x00002f2f 0x2 10 >,
- < 1209600000 0x402c0a3f 0x00003232 0x2 11 >,
- < 1305600000 0x40340b44 0x00003636 0x2 12 >,
- < 1401600000 0x40340c49 0x00003a3a 0x2 13 >,
- < 1497600000 0x403c0d4e 0x00003e3e 0x2 14 >,
- < 1593600000 0x403c0e53 0x00004242 0x2 15 >;
-
- qcom,l3-speedbin2-v0 =
- < 300000000 0x000c000f 0x00002020 0x1 1 >,
- < 403200000 0x500c0115 0x00002020 0x1 2 >,
- < 480000000 0x50140219 0x00002020 0x1 3 >,
- < 576000000 0x5014031e 0x00002020 0x1 4 >,
- < 652800000 0x401c0422 0x00002020 0x1 5 >,
- < 748800000 0x401c0527 0x00002020 0x1 6 >,
- < 844800000 0x4024062c 0x00002323 0x2 7 >,
- < 940800000 0x40240731 0x00002727 0x2 8 >,
- < 1036800000 0x40240836 0x00002b2b 0x2 9 >,
- < 1132800000 0x402c093b 0x00002f2f 0x2 10 >,
- < 1209600000 0x402c0a3f 0x00003232 0x2 11 >,
- < 1305600000 0x40340b44 0x00003636 0x2 12 >,
- < 1401600000 0x40340c49 0x00003a3a 0x2 13 >,
- < 1497600000 0x403c0d4e 0x00003e3e 0x2 14 >,
- < 1593600000 0x403c0e53 0x00004242 0x2 15 >;
-
- qcom,pwrcl-speedbin0-v0 =
- < 300000000 0x000c000f 0x00002020 0x1 1 >,
- < 403200000 0x500c0115 0x00002020 0x1 2 >,
- < 480000000 0x50140219 0x00002020 0x1 3 >,
- < 576000000 0x5014031e 0x00002020 0x1 4 >,
- < 652800000 0x401c0422 0x00002020 0x1 5 >,
- < 748800000 0x401c0527 0x00002020 0x1 6 >,
- < 825600000 0x401c062b 0x00002222 0x1 7 >,
- < 902400000 0x4024072f 0x00002626 0x1 8 >,
- < 979200000 0x40240833 0x00002929 0x1 9 >,
- < 1056000000 0x402c0937 0x00002c2c 0x2 10 >,
- < 1132800000 0x402c0a3b 0x00002f2f 0x2 11 >,
- < 1228800000 0x402c0b40 0x00003333 0x2 12 >,
- < 1324800000 0x40340c45 0x00003737 0x2 13 >,
- < 1420800000 0x40340d4a 0x00003b3b 0x2 14 >,
- < 1516800000 0x403c0e4f 0x00003f3f 0x2 15 >,
- < 1612800000 0x403c0f54 0x00004343 0x2 16 >,
- < 1689600000 0x40441058 0x00004646 0x2 17 >,
- < 1766400000 0x4044115c 0x00004a4a 0x2 18 >;
-
- qcom,pwrcl-speedbin1-v0 =
- < 300000000 0x000c000f 0x00002020 0x1 1 >,
- < 403200000 0x500c0115 0x00002020 0x1 2 >,
- < 480000000 0x50140219 0x00002020 0x1 3 >,
- < 576000000 0x5014031e 0x00002020 0x1 4 >,
- < 652800000 0x401c0422 0x00002020 0x1 5 >,
- < 748800000 0x401c0527 0x00002020 0x1 6 >,
- < 825600000 0x401c062b 0x00002222 0x1 7 >,
- < 902400000 0x4024072f 0x00002626 0x1 8 >,
- < 979200000 0x40240833 0x00002929 0x1 9 >,
- < 1056000000 0x402c0937 0x00002c2c 0x2 10 >,
- < 1132800000 0x402c0a3b 0x00002f2f 0x2 11 >,
- < 1228800000 0x402c0b40 0x00003333 0x2 12 >,
- < 1324800000 0x40340c45 0x00003737 0x2 13 >,
- < 1420800000 0x40340d4a 0x00003b3b 0x2 14 >,
- < 1516800000 0x403c0e4f 0x00003f3f 0x2 15 >,
- < 1612800000 0x403c0f54 0x00004343 0x2 16 >,
- < 1689600000 0x40441058 0x00004646 0x2 17 >,
- < 1766400000 0x4044115c 0x00004a4a 0x2 18 >;
-
- qcom,pwrcl-speedbin2-v0 =
- < 300000000 0x000c000f 0x00002020 0x1 1 >,
- < 403200000 0x500c0115 0x00002020 0x1 2 >,
- < 480000000 0x50140219 0x00002020 0x1 3 >,
- < 576000000 0x5014031e 0x00002020 0x1 4 >,
- < 652800000 0x401c0422 0x00002020 0x1 5 >,
- < 748800000 0x401c0527 0x00002020 0x1 6 >,
- < 825600000 0x401c062b 0x00002222 0x1 7 >,
- < 902400000 0x4024072f 0x00002626 0x1 8 >,
- < 979200000 0x40240833 0x00002929 0x1 9 >,
- < 1056000000 0x402c0937 0x00002c2c 0x2 10 >,
- < 1132800000 0x402c0a3b 0x00002f2f 0x2 11 >,
- < 1228800000 0x402c0b40 0x00003333 0x2 12 >,
- < 1324800000 0x40340c45 0x00003737 0x2 13 >,
- < 1420800000 0x40340d4a 0x00003b3b 0x2 14 >,
- < 1516800000 0x403c0e4f 0x00003f3f 0x2 15 >,
- < 1612800000 0x403c0f54 0x00004343 0x2 16 >,
- < 1689600000 0x40441058 0x00004646 0x2 17 >,
- < 1766400000 0x4044115c 0x00004a4a 0x2 18 >;
-
- qcom,perfcl-speedbin0-v0 =
- < 300000000 0x000c000f 0x00002020 0x1 1 >,
- < 403200000 0x500c0115 0x00002020 0x1 2 >,
- < 480000000 0x50140219 0x00002020 0x1 3 >,
- < 576000000 0x5014031e 0x00002020 0x1 4 >,
- < 652800000 0x401c0422 0x00002020 0x1 5 >,
- < 748800000 0x401c0527 0x00002020 0x1 6 >,
- < 825600000 0x401c062b 0x00002222 0x1 7 >,
- < 902400000 0x4024072f 0x00002626 0x1 8 >,
- < 979200000 0x40240833 0x00002929 0x1 9 >,
- < 1056000000 0x402c0937 0x00002c2c 0x1 10 >,
- < 1132800000 0x402c0a3b 0x00002f2f 0x1 11 >,
- < 1209600000 0x402c0b3f 0x00003232 0x2 12 >,
- < 1286400000 0x40340c43 0x00003636 0x2 13 >,
- < 1363200000 0x40340d47 0x00003939 0x2 14 >,
- < 1459200000 0x403c0e4c 0x00003d3d 0x2 15 >,
- < 1536000000 0x403c0f50 0x00004040 0x2 16 >,
- < 1612800000 0x403c1054 0x00004343 0x2 17 >,
- < 1689600000 0x40441158 0x00004646 0x2 18 >,
- < 1766400000 0x4044125c 0x00004a4a 0x2 19 >,
- < 1843200000 0x40441360 0x00004d4d 0x2 20 >,
- < 1920000000 0x404c1464 0x00005050 0x2 21 >,
- < 1996800000 0x404c1568 0x00005353 0x2 22 >,
- < 2092800000 0x4054166d 0x00005757 0x2 23 >,
- < 2169600000 0x40541771 0x00005a5a 0x2 24 >,
- < 2246400000 0x40541875 0x00005e5e 0x2 25 >,
- < 2323200000 0x40541979 0x00006161 0x2 26 >,
- < 2400000000 0x40541a7d 0x00006464 0x2 27 >;
-
- qcom,perfcl-speedbin1-v0 =
- < 300000000 0x000c000f 0x00002020 0x1 1 >,
- < 403200000 0x500c0115 0x00002020 0x1 2 >,
- < 480000000 0x50140219 0x00002020 0x1 3 >,
- < 576000000 0x5014031e 0x00002020 0x1 4 >,
- < 652800000 0x401c0422 0x00002020 0x1 5 >,
- < 748800000 0x401c0527 0x00002020 0x1 6 >,
- < 825600000 0x401c062b 0x00002222 0x1 7 >,
- < 902400000 0x4024072f 0x00002626 0x1 8 >,
- < 979200000 0x40240833 0x00002929 0x1 9 >,
- < 1056000000 0x402c0937 0x00002c2c 0x1 10 >,
- < 1132800000 0x402c0a3b 0x00002f2f 0x1 11 >,
- < 1209600000 0x402c0b3f 0x00003232 0x2 12 >,
- < 1286400000 0x40340c43 0x00003636 0x2 13 >,
- < 1363200000 0x40340d47 0x00003939 0x2 14 >,
- < 1459200000 0x403c0e4c 0x00003d3d 0x2 15 >,
- < 1536000000 0x403c0f50 0x00004040 0x2 16 >,
- < 1612800000 0x403c1054 0x00004343 0x2 17 >,
- < 1689600000 0x40441158 0x00004646 0x2 18 >,
- < 1766400000 0x4044125c 0x00004a4a 0x2 19 >,
- < 1843200000 0x40441360 0x00004d4d 0x2 20 >,
- < 1920000000 0x404c1464 0x00005050 0x2 21 >,
- < 1996800000 0x404c1568 0x00005353 0x2 22 >,
- < 2092800000 0x4054166d 0x00005757 0x2 23 >,
- < 2169600000 0x40541771 0x00005a5a 0x2 24 >,
- < 2246400000 0x40541875 0x00005e5e 0x2 25 >,
- < 2323200000 0x40541979 0x00006161 0x2 26 >,
- < 2400000000 0x40541a7d 0x00006464 0x2 27 >,
- < 2476800000 0x40541b81 0x00006767 0x2 28 >,
- < 2553600000 0x40541c85 0x00006a6a 0x2 29 >,
- < 2649600000 0x40541d8a 0x00006e6e 0x2 30 >,
- < 2745600000 0x40511e8f 0x00007272 0x2 31 >;
-
- qcom,perfcl-speedbin2-v0 =
- < 300000000 0x000c000f 0x00002020 0x1 1 >,
- < 403200000 0x500c0115 0x00002020 0x1 2 >,
- < 480000000 0x50140219 0x00002020 0x1 3 >,
- < 576000000 0x5014031e 0x00002020 0x1 4 >,
- < 652800000 0x401c0422 0x00002020 0x1 5 >,
- < 748800000 0x401c0527 0x00002020 0x1 6 >,
- < 825600000 0x401c062b 0x00002222 0x1 7 >,
- < 902400000 0x4024072f 0x00002626 0x1 8 >,
- < 979200000 0x40240833 0x00002929 0x1 9 >,
- < 1056000000 0x402c0937 0x00002c2c 0x1 10 >,
- < 1132800000 0x402c0a3b 0x00002f2f 0x1 11 >,
- < 1209600000 0x402c0b3f 0x00003232 0x2 12 >,
- < 1286400000 0x40340c43 0x00003636 0x2 13 >,
- < 1363200000 0x40340d47 0x00003939 0x2 14 >,
- < 1459200000 0x403c0e4c 0x00003d3d 0x2 15 >,
- < 1536000000 0x403c0f50 0x00004040 0x2 16 >,
- < 1612800000 0x403c1054 0x00004343 0x2 17 >,
- < 1689600000 0x40441158 0x00004646 0x2 18 >,
- < 1766400000 0x4044125c 0x00004a4a 0x2 19 >,
- < 1843200000 0x40441360 0x00004d4d 0x2 20 >,
- < 1920000000 0x404c1464 0x00005050 0x2 21 >,
- < 1996800000 0x404c1568 0x00005353 0x2 22 >,
- < 2092800000 0x4054166d 0x00005757 0x2 23 >,
- < 2169600000 0x40541771 0x00005a5a 0x2 24 >,
- < 2246400000 0x40541875 0x00005e5e 0x2 25 >,
- < 2323200000 0x40541979 0x00006161 0x2 26 >,
- < 2400000000 0x40541a7d 0x00006464 0x2 27 >,
- < 2476800000 0x40541b81 0x00006767 0x2 28 >,
- < 2553600000 0x40541c85 0x00006a6a 0x2 29 >,
- < 2649600000 0x40541d8a 0x00006e6e 0x2 30 >,
- < 2707200000 0x40511e8d 0x00007171 0x2 30 >,
- < 2764800000 0x40511f90 0x00007373 0x2 31 >,
- < 2784000000 0x40512091 0x00007474 0x2 32 >,
- < 2803200000 0x40512192 0x00007575 0x2 33 >;
-
- qcom,l3-memacc-level-vc-bin0 = <8 13>;
- qcom,l3-memacc-level-vc-bin1 = <8 13>;
- qcom,l3-memacc-level-vc-bin2 = <8 13>;
-
- qcom,pwrcl-memacc-level-vc-bin0 = <12 16>;
- qcom,pwrcl-memacc-level-vc-bin1 = <12 16>;
- qcom,pwrcl-memacc-level-vc-bin2 = <12 16>;
-
- qcom,perfcl-memacc-level-vc-bin0 = <14 22>;
- qcom,perfcl-memacc-level-vc-bin1 = <14 22>;
- qcom,perfcl-memacc-level-vc-bin2 = <14 22>;
};
&pcie1 {
@@ -1162,7 +274,7 @@
mincpubw-cpufreq {
cpu-to-dev-map-4 =
< 1881600 MHZ_TO_MBPS(200, 4) >,
- < 2400000 MHZ_TO_MBPS(681, 4) >;
+ < 2400000 MHZ_TO_MBPS(1017, 4) >;
};
};
@@ -1198,7 +310,6 @@
&refgen {
status = "ok";
- regulator-always-on;
};
&spss_utils {
diff --git a/arch/arm64/boot/dts/qcom/sdm845.dtsi b/arch/arm64/boot/dts/qcom/sdm845.dtsi
index 8fd582f..e2f404c 100644
--- a/arch/arm64/boot/dts/qcom/sdm845.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm845.dtsi
@@ -79,7 +79,7 @@
};
L1_I_0: l1-icache {
compatible = "arm,arch-cache";
- qcom,dump-size = <0xa000>;
+ qcom,dump-size = <0x12000>;
};
L1_D_0: l1-dcache {
compatible = "arm,arch-cache";
@@ -110,7 +110,7 @@
};
L1_I_100: l1-icache {
compatible = "arm,arch-cache";
- qcom,dump-size = <0xa000>;
+ qcom,dump-size = <0x12000>;
};
L1_D_100: l1-dcache {
compatible = "arm,arch-cache";
@@ -141,7 +141,7 @@
};
L1_I_200: l1-icache {
compatible = "arm,arch-cache";
- qcom,dump-size = <0xa000>;
+ qcom,dump-size = <0x12000>;
};
L1_D_200: l1-dcache {
compatible = "arm,arch-cache";
@@ -172,7 +172,7 @@
};
L1_I_300: l1-icache {
compatible = "arm,arch-cache";
- qcom,dump-size = <0xa000>;
+ qcom,dump-size = <0x12000>;
};
L1_D_300: l1-dcache {
compatible = "arm,arch-cache";
@@ -203,14 +203,14 @@
};
L1_I_400: l1-icache {
compatible = "arm,arch-cache";
- qcom,dump-size = <0x14000>;
+ qcom,dump-size = <0x24000>;
};
L1_D_400: l1-dcache {
compatible = "arm,arch-cache";
qcom,dump-size = <0x14000>;
};
L1_TLB_400: l1-tlb {
- qcom,dump-size = <0x3c000>;
+ qcom,dump-size = <0x3c00>;
};
};
@@ -234,14 +234,14 @@
};
L1_I_500: l1-icache {
compatible = "arm,arch-cache";
- qcom,dump-size = <0x14000>;
+ qcom,dump-size = <0x24000>;
};
L1_D_500: l1-dcache {
compatible = "arm,arch-cache";
qcom,dump-size = <0x14000>;
};
L1_TLB_500: l1-tlb {
- qcom,dump-size = <0x3c000>;
+ qcom,dump-size = <0x3c00>;
};
};
@@ -265,14 +265,14 @@
};
L1_I_600: l1-icache {
compatible = "arm,arch-cache";
- qcom,dump-size = <0x14000>;
+ qcom,dump-size = <0x24000>;
};
L1_D_600: l1-dcache {
compatible = "arm,arch-cache";
qcom,dump-size = <0x14000>;
};
L1_TLB_600: l1-tlb {
- qcom,dump-size = <0x3c000>;
+ qcom,dump-size = <0x3c00>;
};
};
@@ -296,14 +296,14 @@
};
L1_I_700: l1-icache {
compatible = "arm,arch-cache";
- qcom,dump-size = <0x14000>;
+ qcom,dump-size = <0x24000>;
};
L1_D_700: l1-dcache {
compatible = "arm,arch-cache";
qcom,dump-size = <0x14000>;
};
L1_TLB_700: l1-tlb {
- qcom,dump-size = <0x3c000>;
+ qcom,dump-size = <0x3c00>;
};
};
@@ -610,6 +610,11 @@
size = <0 0x800000>;
};
+ cont_splash_memory: cont_splash_region@9d400000 {
+ reg = <0x0 0x9d400000 0x0 0x02400000>;
+ label = "cont_splash_region";
+ };
+
secure_display_memory: secure_display_region {
compatible = "shared-dma-pool";
alloc-ranges = <0 0x00000000 0 0xffffffff>;
@@ -1207,267 +1212,14 @@
compatible = "qcom,clk-cpu-osm";
reg = <0x17d41000 0x1400>,
<0x17d43000 0x1400>,
- <0x17d45800 0x1400>,
- <0x178d0000 0x1000>,
- <0x178c0000 0x1000>,
- <0x178b0000 0x1000>,
- <0x17d42400 0x0c00>,
- <0x17d44400 0x0c00>,
- <0x17d46c00 0x0c00>,
- <0x00784130 0x4>,
- <0x00784130 0x4>,
- <0x00784130 0x4>;
- reg-names = "osm_l3_base", "osm_pwrcl_base", "osm_perfcl_base",
- "l3_pll", "pwrcl_pll", "perfcl_pll", "l3_sequencer",
- "pwrcl_sequencer", "perfcl_sequencer", "l3_efuse",
- "pwrcl_efuse", "perfcl_efuse";
+ <0x17d45800 0x1400>;
+ reg-names = "osm_l3_base", "osm_pwrcl_base", "osm_perfcl_base";
- vdd-l3-supply = <&apc0_l3_vreg>;
- vdd-pwrcl-supply = <&apc0_pwrcl_vreg>;
- vdd-perfcl-supply = <&apc1_perfcl_vreg>;
-
- l3-dev0 = <&l3_cpu0>;
- l3-dev4 = <&l3_cpu4>;
-
- qcom,l3-speedbin0-v0 =
- < 300000000 0x000c000f 0x00002020 0x1 1 >,
- < 422400000 0x50140116 0x00002020 0x1 2 >,
- < 499200000 0x5014021a 0x00002020 0x1 3 >,
- < 576000000 0x5014031e 0x00002020 0x1 4 >,
- < 652800000 0x401c0422 0x00002020 0x1 5 >,
- < 729600000 0x401c0526 0x00002020 0x1 6 >,
- < 806400000 0x401c062a 0x00002222 0x1 7 >,
- < 883200000 0x4024072e 0x00002525 0x1 8 >,
- < 960000000 0x40240832 0x00002828 0x1 9 >;
-
- qcom,l3-speedbin1-v0 =
- < 300000000 0x000c000f 0x00002020 0x1 1 >,
- < 422400000 0x50140116 0x00002020 0x1 2 >,
- < 499200000 0x5014021a 0x00002020 0x1 3 >,
- < 576000000 0x5014031e 0x00002020 0x1 4 >,
- < 652800000 0x401c0422 0x00002020 0x1 5 >,
- < 729600000 0x401c0526 0x00002020 0x1 6 >,
- < 806400000 0x401c062a 0x00002222 0x1 7 >,
- < 883200000 0x4024072e 0x00002525 0x1 8 >,
- < 960000000 0x40240832 0x00002828 0x1 9 >,
- < 1036800000 0x40240936 0x00002b2b 0x1 10 >,
- < 1094400000 0x402c0a39 0x00002e2e 0x1 11 >;
-
- qcom,l3-speedbin2-v0 =
- < 300000000 0x000c000f 0x00002020 0x1 1 >,
- < 422400000 0x50140116 0x00002020 0x1 2 >,
- < 499200000 0x5014021a 0x00002020 0x1 3 >,
- < 576000000 0x5014031e 0x00002020 0x1 4 >,
- < 652800000 0x401c0422 0x00002020 0x1 5 >,
- < 729600000 0x401c0526 0x00002020 0x1 6 >,
- < 806400000 0x401c062a 0x00002222 0x1 7 >,
- < 883200000 0x4024072e 0x00002525 0x1 8 >,
- < 960000000 0x40240832 0x00002828 0x1 9 >,
- < 1036800000 0x40240936 0x00002b2b 0x1 10 >,
- < 1113600000 0x402c0a3a 0x00002e2e 0x1 11 >,
- < 1209600000 0x402c0b3f 0x00003232 0x1 12 >,
- < 1305600000 0x40340c44 0x00003636 0x1 13 >;
-
- qcom,pwrcl-speedbin0-v0 =
- < 300000000 0x000c000f 0x00002020 0x1 1 >,
- < 422400000 0x50140116 0x00002020 0x1 2 >,
- < 499200000 0x5014021a 0x00002020 0x1 3 >,
- < 576000000 0x5014031e 0x00002020 0x1 4 >,
- < 652800000 0x401c0422 0x00002020 0x1 5 >,
- < 748800000 0x401c0527 0x00002020 0x1 6 >,
- < 825600000 0x401c062b 0x00002222 0x1 7 >,
- < 902400000 0x4024072f 0x00002626 0x1 8 >,
- < 979200000 0x40240833 0x00002929 0x1 9 >,
- < 1056000000 0x402c0937 0x00002c2c 0x1 10 >,
- < 1132800000 0x402c0a3b 0x00002f2f 0x1 11 >,
- < 1209600000 0x402c0b3f 0x00003232 0x1 12 >,
- < 1286400000 0x40340c43 0x00003636 0x1 13 >,
- < 1363200000 0x40340d47 0x00003939 0x1 14 >,
- < 1440000000 0x40340e4b 0x00003c3c 0x1 15 >,
- < 1516800000 0x403c0f4f 0x00003f3f 0x1 16 >,
- < 1593600000 0x403c1053 0x00004242 0x1 17 >;
-
- qcom,pwrcl-speedbin1-v0 =
- < 300000000 0x000c000f 0x00002020 0x1 1 >,
- < 422400000 0x50140116 0x00002020 0x1 2 >,
- < 499200000 0x5014021a 0x00002020 0x1 3 >,
- < 576000000 0x5014031e 0x00002020 0x1 4 >,
- < 652800000 0x401c0422 0x00002020 0x1 5 >,
- < 748800000 0x401c0527 0x00002020 0x1 6 >,
- < 825600000 0x401c062b 0x00002222 0x1 7 >,
- < 902400000 0x4024072f 0x00002626 0x1 8 >,
- < 979200000 0x40240833 0x00002929 0x1 9 >,
- < 1056000000 0x402c0937 0x00002c2c 0x1 10 >,
- < 1132800000 0x402c0a3b 0x00002f2f 0x1 11 >,
- < 1209600000 0x402c0b3f 0x00003232 0x1 12 >,
- < 1286400000 0x40340c43 0x00003636 0x1 13 >,
- < 1363200000 0x40340d47 0x00003939 0x1 14 >,
- < 1440000000 0x40340e4b 0x00003c3c 0x1 15 >,
- < 1516800000 0x403c0f4f 0x00003f3f 0x1 16 >,
- < 1593600000 0x403c1053 0x00004242 0x1 17 >,
- < 1651200000 0x403c1156 0x00004545 0x1 18 >,
- < 1708800000 0x40441259 0x00004747 0x1 19 >;
-
- qcom,pwrcl-speedbin2-v0 =
- < 300000000 0x000c000f 0x00002020 0x1 1 >,
- < 422400000 0x50140116 0x00002020 0x1 2 >,
- < 499200000 0x5014021a 0x00002020 0x1 3 >,
- < 576000000 0x5014031e 0x00002020 0x1 4 >,
- < 652800000 0x401c0422 0x00002020 0x1 5 >,
- < 748800000 0x401c0527 0x00002020 0x1 6 >,
- < 825600000 0x401c062b 0x00002222 0x1 7 >,
- < 902400000 0x4024072f 0x00002626 0x1 8 >,
- < 979200000 0x40240833 0x00002929 0x1 9 >,
- < 1056000000 0x402c0937 0x00002c2c 0x1 10 >,
- < 1132800000 0x402c0a3b 0x00002f2f 0x1 11 >,
- < 1209600000 0x402c0b3f 0x00003232 0x1 12 >,
- < 1286400000 0x40340c43 0x00003636 0x1 13 >,
- < 1363200000 0x40340d47 0x00003939 0x1 14 >,
- < 1440000000 0x40340e4b 0x00003c3c 0x1 15 >,
- < 1516800000 0x403c0f4f 0x00003f3f 0x1 16 >,
- < 1593600000 0x403c1053 0x00004242 0x1 17 >,
- < 1670400000 0x40441157 0x00004646 0x1 18 >,
- < 1747200000 0x4044125b 0x00004949 0x1 19 >;
-
- qcom,perfcl-speedbin0-v0 =
- < 300000000 0x000c000f 0x00002020 0x1 1 >,
- < 422400000 0x50140116 0x00002020 0x1 2 >,
- < 499200000 0x5014021a 0x00002020 0x1 3 >,
- < 576000000 0x5014031e 0x00002020 0x1 4 >,
- < 652800000 0x401c0422 0x00002020 0x1 5 >,
- < 729600000 0x401c0526 0x00002020 0x1 6 >,
- < 806400000 0x401c062a 0x00002222 0x1 7 >,
- < 883200000 0x4024072e 0x00002525 0x1 8 >,
- < 960000000 0x40240832 0x00002828 0x1 9 >,
- < 1036800000 0x40240936 0x00002b2b 0x1 10 >,
- < 1113600000 0x402c0a3a 0x00002e2e 0x1 11 >,
- < 1190400000 0x402c0b3e 0x00003232 0x1 12 >,
- < 1267200000 0x40340c42 0x00003535 0x1 13 >,
- < 1344000000 0x40340d46 0x00003838 0x1 14 >,
- < 1420800000 0x40340e4a 0x00003b3b 0x1 15 >,
- < 1497600000 0x403c0f4e 0x00003e3e 0x1 16 >,
- < 1574400000 0x403c1052 0x00004242 0x1 17 >,
- < 1651200000 0x403c1156 0x00004545 0x1 18 >,
- < 1728000000 0x4044125a 0x00004848 0x1 19 >,
- < 1804800000 0x4044135e 0x00004b4b 0x1 20 >,
- < 1881600000 0x404c1462 0x00004e4e 0x1 21 >,
- < 1958400000 0x404c1566 0x00005252 0x1 22 >;
-
- qcom,perfcl-speedbin1-v0 =
- < 300000000 0x000c000f 0x00002020 0x1 1 >,
- < 422400000 0x50140116 0x00002020 0x1 2 >,
- < 499200000 0x5014021a 0x00002020 0x1 3 >,
- < 576000000 0x5014031e 0x00002020 0x1 4 >,
- < 652800000 0x401c0422 0x00002020 0x1 5 >,
- < 729600000 0x401c0526 0x00002020 0x1 6 >,
- < 806400000 0x401c062a 0x00002222 0x1 7 >,
- < 883200000 0x4024072e 0x00002525 0x1 8 >,
- < 960000000 0x40240832 0x00002828 0x1 9 >,
- < 1036800000 0x40240936 0x00002b2b 0x1 10 >,
- < 1113600000 0x402c0a3a 0x00002e2e 0x1 11 >,
- < 1190400000 0x402c0b3e 0x00003232 0x1 12 >,
- < 1267200000 0x40340c42 0x00003535 0x1 13 >,
- < 1344000000 0x40340d46 0x00003838 0x1 14 >,
- < 1420800000 0x40340e4a 0x00003b3b 0x1 15 >,
- < 1497600000 0x403c0f4e 0x00003e3e 0x1 16 >,
- < 1574400000 0x403c1052 0x00004242 0x1 17 >,
- < 1651200000 0x403c1156 0x00004545 0x1 18 >,
- < 1728000000 0x4044125a 0x00004848 0x1 19 >,
- < 1804800000 0x4044135e 0x00004b4b 0x1 20 >,
- < 1881600000 0x404c1462 0x00004e4e 0x1 21 >,
- < 1958400000 0x404c1566 0x00005252 0x1 22 >,
- < 2035200000 0x404c166a 0x00005555 0x1 23 >,
- < 2092800000 0x4054176d 0x00005757 0x1 24 >;
-
- qcom,perfcl-speedbin2-v0 =
- < 300000000 0x000c000f 0x00002020 0x1 1 >,
- < 422400000 0x50140116 0x00002020 0x1 2 >,
- < 499200000 0x5014021a 0x00002020 0x1 3 >,
- < 576000000 0x5014031e 0x00002020 0x1 4 >,
- < 652800000 0x401c0422 0x00002020 0x1 5 >,
- < 729600000 0x401c0526 0x00002020 0x1 6 >,
- < 806400000 0x401c062a 0x00002222 0x1 7 >,
- < 883200000 0x4024072e 0x00002525 0x1 8 >,
- < 960000000 0x40240832 0x00002828 0x1 9 >,
- < 1036800000 0x40240936 0x00002b2b 0x1 10 >,
- < 1113600000 0x402c0a3a 0x00002e2e 0x1 11 >,
- < 1190400000 0x402c0b3e 0x00003232 0x1 12 >,
- < 1267200000 0x40340c42 0x00003535 0x1 13 >,
- < 1344000000 0x40340d46 0x00003838 0x1 14 >,
- < 1420800000 0x40340e4a 0x00003b3b 0x1 15 >,
- < 1497600000 0x403c0f4e 0x00003e3e 0x1 16 >,
- < 1574400000 0x403c1052 0x00004242 0x1 17 >,
- < 1651200000 0x403c1156 0x00004545 0x1 18 >,
- < 1728000000 0x4044125a 0x00004848 0x1 19 >,
- < 1804800000 0x4044135e 0x00004b4b 0x1 20 >,
- < 1881600000 0x404c1462 0x00004e4e 0x1 21 >,
- < 1958400000 0x404c1566 0x00005252 0x1 22 >,
- < 2035200000 0x404c166a 0x00005555 0x1 23 >,
- < 2112000000 0x4054176e 0x00005858 0x1 24 >,
- < 2208000000 0x40541873 0x00005c5c 0x1 25 >;
-
- qcom,l3-memacc-level-vc-bin0 = <7 63>;
- qcom,l3-memacc-level-vc-bin1 = <7 9>;
- qcom,l3-memacc-level-vc-bin2 = <7 9>;
-
- qcom,pwrcl-memacc-level-vc-bin0 = <12 63>;
- qcom,pwrcl-memacc-level-vc-bin1 = <12 17>;
- qcom,pwrcl-memacc-level-vc-bin2 = <12 17>;
-
- qcom,perfcl-memacc-level-vc-bin0 = <12 18>;
- qcom,perfcl-memacc-level-vc-bin1 = <12 18>;
- qcom,perfcl-memacc-level-vc-bin2 = <12 18>;
-
- qcom,up-timer =
- <1000 1000 1000>;
- qcom,down-timer =
- <100000 100000 100000>;
- qcom,set-ret-inactive;
- qcom,enable-llm-freq-vote;
- qcom,llm-freq-up-timer =
- <1000 1000 1000>;
- qcom,llm-freq-down-timer =
- <327675 327675 327675>;
- qcom,enable-llm-volt-vote;
- qcom,llm-volt-up-timer =
- <1000 1000 1000>;
- qcom,llm-volt-down-timer =
- <327675 327675 327675>;
- qcom,cc-reads = <10>;
- qcom,cc-delay = <5>;
- qcom,cc-factor = <100>;
- qcom,osm-clk-rate = <100000000>;
- qcom,xo-clk-rate = <19200000>;
-
- qcom,l-val-base =
- <0x178d0004 0x178c0004 0x178b0004>;
- qcom,apcs-pll-user-ctl =
- <0x178d000c 0x178c000c 0x178b000c>;
- qcom,apcs-pll-min-freq =
- <0x17d41094 0x17d43094 0x17d45894>;
- qcom,apm-mode-ctl =
- <0x0 0x0 0x17d20010>;
- qcom,apm-status-ctrl =
- <0x0 0x0 0x17d20000>;
- qcom,perfcl-isense-addr = <0x17871480>;
- qcom,l3-mem-acc-addr = <0x17990170 0x17990170 0x17990170>;
- qcom,pwrcl-mem-acc-addr = <0x17990160 0x17990164 0x17990164>;
- qcom,perfcl-mem-acc-addr = <0x17990168 0x1799016c 0x1799016c>;
- qcom,cfg-gfmux-addr =<0x178d0084 0x178c0084 0x178b0084>;
- qcom,apcs-cbc-addr = <0x178d008c 0x178c008c 0x178b008c>;
- qcom,apcs-ramp-ctl-addr = <0x17840904 0x17840904 0x17830904>;
-
- qcom,perfcl-apcs-apm-threshold-voltage = <800000>;
- qcom,perfcl-apcs-mem-acc-threshold-voltage = <852000>;
- qcom,boost-fsm-en;
- qcom,safe-fsm-en;
- qcom,ps-fsm-en;
- qcom,droop-fsm-en;
+ l3-devs = <&l3_cpu0 &l3_cpu4>;
clock-names = "xo_ao";
clocks = <&clock_rpmh RPMH_CXO_CLK_A>;
#clock-cells = <1>;
- #reset-cells = <1>;
};
clock_debug: qcom,cc-debug@100000 {
@@ -1573,6 +1325,7 @@
<0 0>,
<0 0>;
+ non-removable;
qcom,msm-bus,name = "ufshc_mem";
qcom,msm-bus,num-cases = <22>;
qcom,msm-bus,num-paths = <2>;
@@ -1689,6 +1442,12 @@
<&clock_gcc GCC_SDCC2_APPS_CLK>;
clock-names = "iface_clk", "core_clk";
+ /* PM QoS */
+ qcom,pm-qos-irq-type = "affine_irq";
+ qcom,pm-qos-irq-latency = <70 70>;
+ qcom,pm-qos-cpu-groups = <0x3f 0xc0>;
+ qcom,pm-qos-legacy-latency-us = <70 70>, <70 70>;
+
status = "disabled";
};
@@ -1727,6 +1486,8 @@
vdd_cx-voltage = <RPMH_REGULATOR_LEVEL_TURBO>;
vdd_mx-supply = <&pm8998_s6_level>;
vdd_mx-uV = <RPMH_REGULATOR_LEVEL_TURBO>;
+ vdd_mss-supply = <&pm8005_s2_level>;
+ vdd_mss-uV = <RPMH_REGULATOR_LEVEL_NOM>;
qcom,firmware-name = "modem";
qcom,pil-self-auth;
qcom,sysmon-id = <0>;
@@ -1800,7 +1561,9 @@
vdd_cx-supply = <&pm8998_l27_level>;
qcom,vdd_cx-uV-uA = <RPMH_REGULATOR_LEVEL_TURBO 0>;
- qcom,proxy-reg-names = "vdd_cx";
+ vdd_mx-supply = <&pm8998_l4_level>;
+ qcom,vdd_mx-uV-uA = <RPMH_REGULATOR_LEVEL_TURBO 0>;
+ qcom,proxy-reg-names = "vdd_cx", "vdd_mx";
qcom,keep-proxy-regs-on;
clocks = <&clock_rpmh RPMH_CXO_CLK>;
@@ -2281,6 +2044,10 @@
max-slices = <32>;
};
+ qcom,llcc-perfmon {
+ compatible = "qcom,llcc-perfmon";
+ };
+
qcom,llcc-erp {
compatible = "qcom,llcc-erp";
interrupt-names = "ecc_irq";
@@ -2719,7 +2486,7 @@
<0x1dc4000 0x24000>;
reg-names = "crypto-base","crypto-bam-base";
interrupts = <0 272 0>;
- qcom,bam-pipe-pair = <1>;
+ qcom,bam-pipe-pair = <3>;
qcom,ce-hw-instance = <0>;
qcom,ce-device = <0>;
qcom,ce-hw-shared;
@@ -2738,9 +2505,9 @@
<&clock_gcc GCC_CE1_AXI_CLK>;
qcom,ce-opp-freq = <171430000>;
qcom,request-bw-before-clk;
- qcom,smmu-s1-bypass;
- iommus = <&apps_smmu 0x702 0x1>,
- <&apps_smmu 0x712 0x1>;
+ qcom,smmu-s1-enable;
+ iommus = <&apps_smmu 0x706 0x1>,
+ <&apps_smmu 0x716 0x1>;
};
qcom_msmhdcp: qcom,msm_hdcp {
@@ -2779,9 +2546,9 @@
qcom,use-sw-ahash-algo;
qcom,use-sw-aead-algo;
qcom,use-sw-hmac-algo;
- qcom,smmu-s1-bypass;
- iommus = <&apps_smmu 0x704 0x3>,
- <&apps_smmu 0x714 0x3>;
+ qcom,smmu-s1-enable;
+ iommus = <&apps_smmu 0x704 0x1>,
+ <&apps_smmu 0x714 0x1>;
};
qcom,msm_gsi {
@@ -2793,6 +2560,7 @@
reg = <0x0 0x200000>;
reg-names = "rmtfs";
qcom,client-id = <0x00000001>;
+ qcom,guard-memory;
};
qcom,rmnet-ipa {
@@ -2820,7 +2588,6 @@
qcom,ipa-wdi2;
qcom,use-64-bit-dma-mask;
qcom,arm-smmu;
- qcom,smmu-s1-bypass;
qcom,bandwidth-vote-for-ipa;
qcom,msm-bus,name = "ipa";
qcom,msm-bus,num-cases = <5>;
@@ -2945,15 +2712,23 @@
compatible = "qcom,ipa-smmu-ap-cb";
iommus = <&apps_smmu 0x720 0x0>;
qcom,iova-mapping = <0x20000000 0x40000000>;
+ qcom,additional-mapping =
+ /* modem tables in IMEM */
+ <0x146BD000 0x146BD000 0x2000>;
};
ipa_smmu_wlan: ipa_smmu_wlan {
compatible = "qcom,ipa-smmu-wlan-cb";
+ qcom,smmu-s1-bypass;
iommus = <&apps_smmu 0x721 0x0>;
+ qcom,additional-mapping =
+ /* ipa-uc ram */
+ <0x1E60000 0x1E60000 0x80000>;
};
ipa_smmu_uc: ipa_smmu_uc {
compatible = "qcom,ipa-smmu-uc-cb";
+ qcom,smmu-s1-bypass;
iommus = <&apps_smmu 0x722 0x0>;
qcom,iova-mapping = <0x40000000 0x20000000>;
};
diff --git a/arch/arm64/configs/msm8953-perf_defconfig b/arch/arm64/configs/msm8953-perf_defconfig
index 4e7fe31..6b83b36 100644
--- a/arch/arm64/configs/msm8953-perf_defconfig
+++ b/arch/arm64/configs/msm8953-perf_defconfig
@@ -405,7 +405,6 @@
CONFIG_MSM_GLINK_SMEM_NATIVE_XPRT=y
CONFIG_MSM_GLINK_SPI_XPRT=y
CONFIG_MSM_SMP2P=y
-CONFIG_MSM_SMP2P_TEST=y
CONFIG_MSM_IPC_ROUTER_GLINK_XPRT=y
CONFIG_MSM_QMI_INTERFACE=y
CONFIG_MSM_GLINK_PKT=y
diff --git a/arch/arm64/configs/msm8953_defconfig b/arch/arm64/configs/msm8953_defconfig
index 6951086..791d349 100644
--- a/arch/arm64/configs/msm8953_defconfig
+++ b/arch/arm64/configs/msm8953_defconfig
@@ -422,7 +422,6 @@
CONFIG_MSM_GLINK_SPI_XPRT=y
CONFIG_TRACER_PKT=y
CONFIG_MSM_SMP2P=y
-CONFIG_MSM_SMP2P_TEST=y
CONFIG_MSM_IPC_ROUTER_GLINK_XPRT=y
CONFIG_MSM_QMI_INTERFACE=y
CONFIG_MSM_GLINK_PKT=y
diff --git a/arch/arm64/configs/sdm670-perf_defconfig b/arch/arm64/configs/sdm670-perf_defconfig
index e05f395..784a986 100644
--- a/arch/arm64/configs/sdm670-perf_defconfig
+++ b/arch/arm64/configs/sdm670-perf_defconfig
@@ -230,6 +230,7 @@
CONFIG_IPC_ROUTER=y
CONFIG_IPC_ROUTER_SECURITY=y
CONFIG_FW_LOADER_USER_HELPER_FALLBACK=y
+CONFIG_REGMAP_ALLOW_WRITE_DEBUGFS=y
CONFIG_DMA_CMA=y
CONFIG_ZRAM=y
CONFIG_BLK_DEV_LOOP=y
@@ -282,6 +283,7 @@
# CONFIG_WIL6210_TRACING is not set
CONFIG_WCNSS_MEM_PRE_ALLOC=y
CONFIG_CLD_LL_CORE=y
+CONFIG_CNSS_GENL=y
CONFIG_INPUT_EVDEV=y
CONFIG_KEYBOARD_GPIO=y
# CONFIG_INPUT_MOUSE is not set
@@ -513,7 +515,6 @@
CONFIG_TRACER_PKT=y
CONFIG_QTI_RPMH_API=y
CONFIG_MSM_SMP2P=y
-CONFIG_MSM_SMP2P_TEST=y
CONFIG_MSM_IPC_ROUTER_GLINK_XPRT=y
CONFIG_MSM_QMI_INTERFACE=y
CONFIG_MSM_GLINK_PKT=y
@@ -530,7 +531,6 @@
CONFIG_MSM_EVENT_TIMER=y
CONFIG_MSM_PM=y
CONFIG_MSM_QBT1000=y
-CONFIG_APSS_CORE_EA=y
CONFIG_QTI_RPM_STATS_LOG=y
CONFIG_QCOM_FORCE_WDOG_BITE_ON_PANIC=y
CONFIG_QMP_DEBUGFS_CLIENT=y
@@ -567,6 +567,7 @@
CONFIG_TMPFS_POSIX_ACL=y
CONFIG_ECRYPT_FS=y
CONFIG_ECRYPT_FS_MESSAGING=y
+CONFIG_SDCARD_FS=y
CONFIG_NLS_CODEPAGE_437=y
CONFIG_NLS_ISO8859_1=y
CONFIG_PRINTK_TIME=y
diff --git a/arch/arm64/configs/sdm670_defconfig b/arch/arm64/configs/sdm670_defconfig
index b47850c..6c29dff 100644
--- a/arch/arm64/configs/sdm670_defconfig
+++ b/arch/arm64/configs/sdm670_defconfig
@@ -287,6 +287,7 @@
CONFIG_WIL6210=m
CONFIG_WCNSS_MEM_PRE_ALLOC=y
CONFIG_CLD_LL_CORE=y
+CONFIG_CNSS_GENL=y
CONFIG_INPUT_EVDEV=y
CONFIG_KEYBOARD_GPIO=y
# CONFIG_INPUT_MOUSE is not set
@@ -446,8 +447,6 @@
CONFIG_EDAC_MM_EDAC=y
CONFIG_EDAC_KRYO3XX_ARM64=y
CONFIG_EDAC_KRYO3XX_ARM64_PANIC_ON_UE=y
-CONFIG_EDAC_QCOM_LLCC=y
-CONFIG_EDAC_QCOM_LLCC_PANIC_ON_UE=y
CONFIG_RTC_CLASS=y
CONFIG_RTC_DRV_QPNP=y
CONFIG_DMADEVICES=y
@@ -519,7 +518,6 @@
CONFIG_TRACER_PKT=y
CONFIG_QTI_RPMH_API=y
CONFIG_MSM_SMP2P=y
-CONFIG_MSM_SMP2P_TEST=y
CONFIG_MSM_IPC_ROUTER_GLINK_XPRT=y
CONFIG_MSM_QMI_INTERFACE=y
CONFIG_MSM_GLINK_PKT=y
@@ -537,7 +535,6 @@
CONFIG_MSM_EVENT_TIMER=y
CONFIG_MSM_PM=y
CONFIG_MSM_QBT1000=y
-CONFIG_APSS_CORE_EA=y
CONFIG_QCOM_DCC_V2=y
CONFIG_QTI_RPM_STATS_LOG=y
CONFIG_QCOM_FORCE_WDOG_BITE_ON_PANIC=y
@@ -576,6 +573,7 @@
CONFIG_EFIVAR_FS=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
@@ -608,7 +606,6 @@
CONFIG_PANIC_ON_RT_THROTTLING=y
CONFIG_SCHEDSTATS=y
CONFIG_SCHED_STACK_END_CHECK=y
-# CONFIG_DEBUG_PREEMPT is not set
CONFIG_DEBUG_SPINLOCK=y
CONFIG_DEBUG_MUTEXES=y
CONFIG_DEBUG_ATOMIC_SLEEP=y
diff --git a/arch/arm64/configs/sdm845-perf_defconfig b/arch/arm64/configs/sdm845-perf_defconfig
index 7f21956..cdfa1eb 100644
--- a/arch/arm64/configs/sdm845-perf_defconfig
+++ b/arch/arm64/configs/sdm845-perf_defconfig
@@ -188,6 +188,7 @@
CONFIG_IP_NF_ARP_MANGLE=y
CONFIG_NF_CONNTRACK_IPV6=y
CONFIG_IP6_NF_IPTABLES=y
+CONFIG_IP6_NF_IPTABLES_128=y
CONFIG_IP6_NF_MATCH_RPFILTER=y
CONFIG_IP6_NF_FILTER=y
CONFIG_IP6_NF_TARGET_REJECT=y
@@ -229,6 +230,7 @@
CONFIG_IPC_ROUTER=y
CONFIG_IPC_ROUTER_SECURITY=y
CONFIG_FW_LOADER_USER_HELPER_FALLBACK=y
+CONFIG_REGMAP_ALLOW_WRITE_DEBUGFS=y
CONFIG_DMA_CMA=y
CONFIG_ZRAM=y
CONFIG_BLK_DEV_LOOP=y
@@ -343,7 +345,6 @@
CONFIG_MFD_SPMI_PMIC=y
CONFIG_REGULATOR_FIXED_VOLTAGE=y
CONFIG_REGULATOR_PROXY_CONSUMER=y
-CONFIG_REGULATOR_CPRH_KBSS=y
CONFIG_REGULATOR_QPNP_LABIBB=y
CONFIG_REGULATOR_QPNP=y
CONFIG_REGULATOR_REFGEN=y
@@ -489,6 +490,7 @@
CONFIG_QCOM_RUN_QUEUE_STATS=y
CONFIG_QCOM_LLCC=y
CONFIG_QCOM_SDM845_LLCC=y
+CONFIG_QCOM_LLCC_PERFMON=m
CONFIG_MSM_SERVICE_LOCATOR=y
CONFIG_MSM_SERVICE_NOTIFIER=y
CONFIG_MSM_BOOT_STATS=y
@@ -509,7 +511,6 @@
CONFIG_TRACER_PKT=y
CONFIG_QTI_RPMH_API=y
CONFIG_MSM_SMP2P=y
-CONFIG_MSM_SMP2P_TEST=y
CONFIG_MSM_IPC_ROUTER_GLINK_XPRT=y
CONFIG_MSM_QMI_INTERFACE=y
CONFIG_MSM_GLINK_PKT=y
@@ -526,7 +527,6 @@
CONFIG_MSM_EVENT_TIMER=y
CONFIG_MSM_PM=y
CONFIG_MSM_QBT1000=y
-CONFIG_APSS_CORE_EA=y
CONFIG_QTI_RPM_STATS_LOG=y
CONFIG_QCOM_FORCE_WDOG_BITE_ON_PANIC=y
CONFIG_QMP_DEBUGFS_CLIENT=y
@@ -563,6 +563,7 @@
CONFIG_TMPFS_POSIX_ACL=y
CONFIG_ECRYPT_FS=y
CONFIG_ECRYPT_FS_MESSAGING=y
+CONFIG_SDCARD_FS=y
CONFIG_NLS_CODEPAGE_437=y
CONFIG_NLS_ISO8859_1=y
CONFIG_PRINTK_TIME=y
diff --git a/arch/arm64/configs/sdm845_defconfig b/arch/arm64/configs/sdm845_defconfig
index 509c257..a2a9c12 100644
--- a/arch/arm64/configs/sdm845_defconfig
+++ b/arch/arm64/configs/sdm845_defconfig
@@ -194,6 +194,7 @@
CONFIG_IP_NF_ARP_MANGLE=y
CONFIG_NF_CONNTRACK_IPV6=y
CONFIG_IP6_NF_IPTABLES=y
+CONFIG_IP6_NF_IPTABLES_128=y
CONFIG_IP6_NF_MATCH_RPFILTER=y
CONFIG_IP6_NF_FILTER=y
CONFIG_IP6_NF_TARGET_REJECT=y
@@ -300,6 +301,8 @@
# CONFIG_SERIO_SERPORT is not set
# CONFIG_VT is not set
# CONFIG_LEGACY_PTYS is not set
+# CONFIG_DEVMEM is not set
+# CONFIG_DEVKMEM is not set
CONFIG_SERIAL_MSM_GENI=y
CONFIG_SERIAL_MSM_GENI_CONSOLE=y
CONFIG_DIAG_CHAR=y
@@ -351,7 +354,6 @@
CONFIG_MFD_SPMI_PMIC=y
CONFIG_REGULATOR_FIXED_VOLTAGE=y
CONFIG_REGULATOR_PROXY_CONSUMER=y
-CONFIG_REGULATOR_CPRH_KBSS=y
CONFIG_REGULATOR_QPNP_LABIBB=y
CONFIG_REGULATOR_QPNP=y
CONFIG_REGULATOR_REFGEN=y
@@ -504,6 +506,7 @@
CONFIG_QCOM_RUN_QUEUE_STATS=y
CONFIG_QCOM_LLCC=y
CONFIG_QCOM_SDM845_LLCC=y
+CONFIG_QCOM_LLCC_PERFMON=m
CONFIG_MSM_SERVICE_LOCATOR=y
CONFIG_MSM_SERVICE_NOTIFIER=y
CONFIG_MSM_BOOT_STATS=y
@@ -526,7 +529,6 @@
CONFIG_TRACER_PKT=y
CONFIG_QTI_RPMH_API=y
CONFIG_MSM_SMP2P=y
-CONFIG_MSM_SMP2P_TEST=y
CONFIG_MSM_IPC_ROUTER_GLINK_XPRT=y
CONFIG_MSM_QMI_INTERFACE=y
CONFIG_MSM_GLINK_PKT=y
@@ -544,7 +546,6 @@
CONFIG_MSM_EVENT_TIMER=y
CONFIG_MSM_PM=y
CONFIG_MSM_QBT1000=y
-CONFIG_APSS_CORE_EA=y
CONFIG_QCOM_DCC_V2=y
CONFIG_QTI_RPM_STATS_LOG=y
CONFIG_QCOM_FORCE_WDOG_BITE_ON_PANIC=y
@@ -585,6 +586,7 @@
CONFIG_EFIVAR_FS=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/include/asm/module.h b/arch/arm64/include/asm/module.h
index 06ff7fd..b6c6fa2 100644
--- a/arch/arm64/include/asm/module.h
+++ b/arch/arm64/include/asm/module.h
@@ -22,14 +22,19 @@
#define MODULE_ARCH_VERMAGIC "aarch64"
#ifdef CONFIG_ARM64_MODULE_PLTS
-struct mod_arch_specific {
+struct mod_plt_sec {
struct elf64_shdr *plt;
int plt_num_entries;
int plt_max_entries;
};
+
+struct mod_arch_specific {
+ struct mod_plt_sec core;
+ struct mod_plt_sec init;
+};
#endif
-u64 module_emit_plt_entry(struct module *mod, const Elf64_Rela *rela,
+u64 module_emit_plt_entry(struct module *mod, void *loc, const Elf64_Rela *rela,
Elf64_Sym *sym);
#ifdef CONFIG_RANDOMIZE_BASE
diff --git a/arch/arm64/kernel/module-plts.c b/arch/arm64/kernel/module-plts.c
index 1ce90d8..d05dbe6 100644
--- a/arch/arm64/kernel/module-plts.c
+++ b/arch/arm64/kernel/module-plts.c
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2014-2016 Linaro Ltd. <ard.biesheuvel@linaro.org>
+ * Copyright (C) 2014-2017 Linaro Ltd. <ard.biesheuvel@linaro.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
@@ -26,35 +26,21 @@
__le32 br; /* br x16 */
};
-u64 module_emit_plt_entry(struct module *mod, const Elf64_Rela *rela,
+static bool in_init(const struct module *mod, void *loc)
+{
+ return (u64)loc - (u64)mod->init_layout.base < mod->init_layout.size;
+}
+
+u64 module_emit_plt_entry(struct module *mod, void *loc, const Elf64_Rela *rela,
Elf64_Sym *sym)
{
- struct plt_entry *plt = (struct plt_entry *)mod->arch.plt->sh_addr;
- int i = mod->arch.plt_num_entries;
+ struct mod_plt_sec *pltsec = !in_init(mod, loc) ? &mod->arch.core :
+ &mod->arch.init;
+ struct plt_entry *plt = (struct plt_entry *)pltsec->plt->sh_addr;
+ int i = pltsec->plt_num_entries;
u64 val = sym->st_value + rela->r_addend;
/*
- * We only emit PLT entries against undefined (SHN_UNDEF) symbols,
- * which are listed in the ELF symtab section, but without a type
- * or a size.
- * So, similar to how the module loader uses the Elf64_Sym::st_value
- * field to store the resolved addresses of undefined symbols, let's
- * borrow the Elf64_Sym::st_size field (whose value is never used by
- * the module loader, even for symbols that are defined) to record
- * the address of a symbol's associated PLT entry as we emit it for a
- * zero addend relocation (which is the only kind we have to deal with
- * in practice). This allows us to find duplicates without having to
- * go through the table every time.
- */
- if (rela->r_addend == 0 && sym->st_size != 0) {
- BUG_ON(sym->st_size < (u64)plt || sym->st_size >= (u64)&plt[i]);
- return sym->st_size;
- }
-
- mod->arch.plt_num_entries++;
- BUG_ON(mod->arch.plt_num_entries > mod->arch.plt_max_entries);
-
- /*
* MOVK/MOVN/MOVZ opcode:
* +--------+------------+--------+-----------+-------------+---------+
* | sf[31] | opc[30:29] | 100101 | hw[22:21] | imm16[20:5] | Rd[4:0] |
@@ -72,8 +58,19 @@
cpu_to_le32(0xd61f0200)
};
- if (rela->r_addend == 0)
- sym->st_size = (u64)&plt[i];
+ /*
+ * Check if the entry we just created is a duplicate. Given that the
+ * relocations are sorted, this will be the last entry we allocated.
+ * (if one exists).
+ */
+ if (i > 0 &&
+ plt[i].mov0 == plt[i - 1].mov0 &&
+ plt[i].mov1 == plt[i - 1].mov1 &&
+ plt[i].mov2 == plt[i - 1].mov2)
+ return (u64)&plt[i - 1];
+
+ pltsec->plt_num_entries++;
+ BUG_ON(pltsec->plt_num_entries > pltsec->plt_max_entries);
return (u64)&plt[i];
}
@@ -104,7 +101,8 @@
return num > 0 && cmp_rela(rela + num, rela + num - 1) == 0;
}
-static unsigned int count_plts(Elf64_Sym *syms, Elf64_Rela *rela, int num)
+static unsigned int count_plts(Elf64_Sym *syms, Elf64_Rela *rela, int num,
+ Elf64_Word dstidx)
{
unsigned int ret = 0;
Elf64_Sym *s;
@@ -116,13 +114,17 @@
case R_AARCH64_CALL26:
/*
* We only have to consider branch targets that resolve
- * to undefined symbols. This is not simply a heuristic,
- * it is a fundamental limitation, since the PLT itself
- * is part of the module, and needs to be within 128 MB
- * as well, so modules can never grow beyond that limit.
+ * to symbols that are defined in a different section.
+ * This is not simply a heuristic, it is a fundamental
+ * limitation, since there is no guaranteed way to emit
+ * PLT entries sufficiently close to the branch if the
+ * section size exceeds the range of a branch
+ * instruction. So ignore relocations against defined
+ * symbols if they live in the same section as the
+ * relocation target.
*/
s = syms + ELF64_R_SYM(rela[i].r_info);
- if (s->st_shndx != SHN_UNDEF)
+ if (s->st_shndx == dstidx)
break;
/*
@@ -149,7 +151,8 @@
int module_frob_arch_sections(Elf_Ehdr *ehdr, Elf_Shdr *sechdrs,
char *secstrings, struct module *mod)
{
- unsigned long plt_max_entries = 0;
+ unsigned long core_plts = 0;
+ unsigned long init_plts = 0;
Elf64_Sym *syms = NULL;
int i;
@@ -158,14 +161,16 @@
* entries. Record the symtab address as well.
*/
for (i = 0; i < ehdr->e_shnum; i++) {
- if (strcmp(".plt", secstrings + sechdrs[i].sh_name) == 0)
- mod->arch.plt = sechdrs + i;
+ if (!strcmp(secstrings + sechdrs[i].sh_name, ".plt"))
+ mod->arch.core.plt = sechdrs + i;
+ else if (!strcmp(secstrings + sechdrs[i].sh_name, ".init.plt"))
+ mod->arch.init.plt = sechdrs + i;
else if (sechdrs[i].sh_type == SHT_SYMTAB)
syms = (Elf64_Sym *)sechdrs[i].sh_addr;
}
- if (!mod->arch.plt) {
- pr_err("%s: module PLT section missing\n", mod->name);
+ if (!mod->arch.core.plt || !mod->arch.init.plt) {
+ pr_err("%s: module PLT section(s) missing\n", mod->name);
return -ENOEXEC;
}
if (!syms) {
@@ -188,14 +193,27 @@
/* sort by type, symbol index and addend */
sort(rels, numrels, sizeof(Elf64_Rela), cmp_rela, NULL);
- plt_max_entries += count_plts(syms, rels, numrels);
+ if (strncmp(secstrings + dstsec->sh_name, ".init", 5) != 0)
+ core_plts += count_plts(syms, rels, numrels,
+ sechdrs[i].sh_info);
+ else
+ init_plts += count_plts(syms, rels, numrels,
+ sechdrs[i].sh_info);
}
- mod->arch.plt->sh_type = SHT_NOBITS;
- mod->arch.plt->sh_flags = SHF_EXECINSTR | SHF_ALLOC;
- mod->arch.plt->sh_addralign = L1_CACHE_BYTES;
- mod->arch.plt->sh_size = plt_max_entries * sizeof(struct plt_entry);
- mod->arch.plt_num_entries = 0;
- mod->arch.plt_max_entries = plt_max_entries;
+ mod->arch.core.plt->sh_type = SHT_NOBITS;
+ mod->arch.core.plt->sh_flags = SHF_EXECINSTR | SHF_ALLOC;
+ mod->arch.core.plt->sh_addralign = L1_CACHE_BYTES;
+ mod->arch.core.plt->sh_size = (core_plts + 1) * sizeof(struct plt_entry);
+ mod->arch.core.plt_num_entries = 0;
+ mod->arch.core.plt_max_entries = core_plts;
+
+ mod->arch.init.plt->sh_type = SHT_NOBITS;
+ mod->arch.init.plt->sh_flags = SHF_EXECINSTR | SHF_ALLOC;
+ mod->arch.init.plt->sh_addralign = L1_CACHE_BYTES;
+ mod->arch.init.plt->sh_size = (init_plts + 1) * sizeof(struct plt_entry);
+ mod->arch.init.plt_num_entries = 0;
+ mod->arch.init.plt_max_entries = init_plts;
+
return 0;
}
diff --git a/arch/arm64/kernel/module.c b/arch/arm64/kernel/module.c
index 7f31698..c9a2ab4 100644
--- a/arch/arm64/kernel/module.c
+++ b/arch/arm64/kernel/module.c
@@ -380,7 +380,7 @@
if (IS_ENABLED(CONFIG_ARM64_MODULE_PLTS) &&
ovf == -ERANGE) {
- val = module_emit_plt_entry(me, &rel[i], sym);
+ val = module_emit_plt_entry(me, loc, &rel[i], sym);
ovf = reloc_insn_imm(RELOC_OP_PREL, loc, val, 2,
26, AARCH64_INSN_IMM_26);
}
diff --git a/arch/arm64/kernel/module.lds b/arch/arm64/kernel/module.lds
index 8949f6c..f7c9781 100644
--- a/arch/arm64/kernel/module.lds
+++ b/arch/arm64/kernel/module.lds
@@ -1,3 +1,4 @@
SECTIONS {
.plt (NOLOAD) : { BYTE(0) }
+ .init.plt (NOLOAD) : { BYTE(0) }
}
diff --git a/arch/arm64/kernel/perf_event.c b/arch/arm64/kernel/perf_event.c
index b325f74..c1e932d 100644
--- a/arch/arm64/kernel/perf_event.c
+++ b/arch/arm64/kernel/perf_event.c
@@ -1161,8 +1161,8 @@
{
int ret;
- ret = cpuhp_setup_state_nocalls(CPUHP_AP_NOTIFY_ONLINE,
- "PERF_EVENT/CPUHP_AP_NOTIFY_ONLINE",
+ ret = cpuhp_setup_state_nocalls(CPUHP_AP_NOTIFY_PERF_ONLINE,
+ "PERF_EVENT/CPUHP_AP_NOTIFY_PERF_ONLINE",
perf_event_hotplug_coming_up,
perf_event_hotplug_going_down);
if (ret)
diff --git a/drivers/base/core.c b/drivers/base/core.c
index 35ab4d5..ac43d6f 100644
--- a/drivers/base/core.c
+++ b/drivers/base/core.c
@@ -1116,13 +1116,7 @@
error = dpm_sysfs_add(dev);
if (error)
goto DPMError;
- if ((dev->pm_domain) || (dev->type && dev->type->pm)
- || (dev->class && (dev->class->pm || dev->class->resume))
- || (dev->bus && (dev->bus->pm || dev->bus->resume)) ||
- (dev->driver && dev->driver->pm)) {
- device_pm_add(dev);
- }
-
+ device_pm_add(dev);
if (MAJOR(dev->devt)) {
error = device_create_file(dev, &dev_attr_dev);
diff --git a/drivers/base/power/main.c b/drivers/base/power/main.c
index c1e56c3..4f99101 100644
--- a/drivers/base/power/main.c
+++ b/drivers/base/power/main.c
@@ -162,12 +162,6 @@
pr_debug("PM: Moving %s:%s before %s:%s\n",
deva->bus ? deva->bus->name : "No Bus", dev_name(deva),
devb->bus ? devb->bus->name : "No Bus", dev_name(devb));
- if (!((devb->pm_domain) || (devb->type && devb->type->pm)
- || (devb->class && (devb->class->pm || devb->class->resume))
- || (devb->bus && (devb->bus->pm || devb->bus->resume)) ||
- (devb->driver && devb->driver->pm))) {
- device_pm_add(devb);
- }
/* Delete deva from dpm_list and reinsert before devb. */
list_move_tail(&deva->power.entry, &devb->power.entry);
}
@@ -182,12 +176,6 @@
pr_debug("PM: Moving %s:%s after %s:%s\n",
deva->bus ? deva->bus->name : "No Bus", dev_name(deva),
devb->bus ? devb->bus->name : "No Bus", dev_name(devb));
- if (!((devb->pm_domain) || (devb->type && devb->type->pm)
- || (devb->class && (devb->class->pm || devb->class->resume))
- || (devb->bus && (devb->bus->pm || devb->bus->resume)) ||
- (devb->driver && devb->driver->pm))) {
- device_pm_add(devb);
- }
/* Delete deva from dpm_list and reinsert after devb. */
list_move(&deva->power.entry, &devb->power.entry);
}
diff --git a/drivers/block/zram/zram_drv.c b/drivers/block/zram/zram_drv.c
index 3868665..9701cc2 100644
--- a/drivers/block/zram/zram_drv.c
+++ b/drivers/block/zram/zram_drv.c
@@ -41,6 +41,12 @@
static int zram_major;
static const char *default_compressor = "lzo";
+/*
+ * We don't need to see memory allocation errors more than once every 1
+ * second to know that a problem is occurring.
+ */
+#define ALLOC_ERROR_LOG_RATE_MS 1000
+
/* Module params (documentation at end) */
static unsigned int num_devices = 1;
@@ -668,6 +674,7 @@
struct zram_meta *meta = zram->meta;
struct zcomp_strm *zstrm = NULL;
unsigned long alloced_pages;
+ static unsigned long zram_rs_time;
page = bvec->bv_page;
if (is_partial_io(bvec)) {
@@ -761,8 +768,10 @@
if (handle)
goto compress_again;
- pr_err("Error allocating memory for compressed page: %u, size=%u\n",
- index, clen);
+ if (printk_timed_ratelimit(&zram_rs_time,
+ ALLOC_ERROR_LOG_RATE_MS))
+ pr_err("Error allocating memory for compressed page: %u, size=%u\n",
+ index, clen);
ret = -ENOMEM;
goto out;
}
diff --git a/drivers/char/adsprpc.c b/drivers/char/adsprpc.c
index 89bf7c6..7de9b79c 100644
--- a/drivers/char/adsprpc.c
+++ b/drivers/char/adsprpc.c
@@ -42,13 +42,17 @@
#include <linux/sort.h>
#include <linux/msm_dma_iommu_mapping.h>
#include <asm/dma-iommu.h>
+#include <soc/qcom/scm.h>
#include "adsprpc_compat.h"
#include "adsprpc_shared.h"
+#include <soc/qcom/ramdump.h>
#include <linux/debugfs.h>
#include <linux/pm_qos.h>
#define TZ_PIL_PROTECT_MEM_SUBSYS_ID 0x0C
#define TZ_PIL_CLEAR_PROTECT_MEM_SUBSYS_ID 0x0D
#define TZ_PIL_AUTH_QDSP6_PROC 1
+#define ADSP_MMAP_HEAP_ADDR 4
+#define ADSP_MMAP_REMOTE_HEAP_ADDR 8
#define FASTRPC_ENOSUCH 39
#define VMID_SSC_Q6 5
#define VMID_ADSP_Q6 6
@@ -61,6 +65,7 @@
#define M_FDLIST (16)
#define M_CRCLIST (64)
#define SESSION_ID_INDEX (30)
+#define FASTRPC_CTX_MAGIC (0xbeeddeed)
#define IS_CACHE_ALIGNED(x) (((x) & ((L1_CACHE_BYTES)-1)) == 0)
@@ -76,6 +81,9 @@
#define FASTRPC_STATIC_HANDLE_MAX (20)
#define FASTRPC_LATENCY_CTRL_ENB (1)
+#define INIT_FILELEN_MAX (2*1024*1024)
+#define INIT_MEMLEN_MAX (8*1024*1024)
+
#define PERF_END (void)0
#define PERF(enb, cnt, ff) \
@@ -176,6 +184,7 @@
struct overlap **overps;
struct smq_msg msg;
uint32_t *crc;
+ unsigned int magic;
};
struct fastrpc_ctx_lst {
@@ -184,6 +193,7 @@
};
struct fastrpc_smmu {
+ struct device *dev;
struct dma_iommu_mapping *mapping;
int cb;
int enabled;
@@ -220,7 +230,10 @@
int ssrcount;
void *handle;
int prevssrcount;
+ int issubsystemup;
int vmid;
+ int ramdumpenabled;
+ void *remoteheap_ramdump_dev;
struct fastrpc_glink_info link;
};
@@ -231,6 +244,7 @@
struct mutex smd_mutex;
struct smq_phy_page range;
struct hlist_head maps;
+ uint32_t staticpd_flags;
dev_t dev_no;
int compat;
struct hlist_head drivers;
@@ -252,7 +266,7 @@
struct ion_handle *handle;
uint64_t phys;
ssize_t size;
- uintptr_t va;
+ uintptr_t __user va;
ssize_t len;
int refs;
uintptr_t raddr;
@@ -338,7 +352,7 @@
static void fastrpc_buf_free(struct fastrpc_buf *buf, int cache)
{
- struct fastrpc_file *fl = buf == 0 ? 0 : buf->fl;
+ struct fastrpc_file *fl = buf == NULL ? NULL : buf->fl;
int vmid;
if (!fl)
@@ -362,7 +376,7 @@
hyp_assign_phys(buf->phys, buf_page_size(buf->size),
srcVM, 2, destVM, destVMperm, 1);
}
- dma_free_coherent(fl->sctx->dev, buf->size, buf->virt,
+ dma_free_coherent(fl->sctx->smmu.dev, buf->size, buf->virt,
buf->phys);
}
kfree(buf);
@@ -375,7 +389,7 @@
do {
struct hlist_node *n;
- free = 0;
+ free = NULL;
spin_lock(&fl->hlock);
hlist_for_each_entry_safe(buf, n, &fl->bufs, hn) {
hlist_del_init(&buf->hn);
@@ -390,31 +404,57 @@
static void fastrpc_mmap_add(struct fastrpc_mmap *map)
{
- struct fastrpc_file *fl = map->fl;
+ if (map->flags == ADSP_MMAP_HEAP_ADDR ||
+ map->flags == ADSP_MMAP_REMOTE_HEAP_ADDR) {
+ struct fastrpc_apps *me = &gfa;
- spin_lock(&fl->hlock);
- hlist_add_head(&map->hn, &fl->maps);
- spin_unlock(&fl->hlock);
+ spin_lock(&me->hlock);
+ hlist_add_head(&map->hn, &me->maps);
+ spin_unlock(&me->hlock);
+ } else {
+ struct fastrpc_file *fl = map->fl;
+
+ spin_lock(&fl->hlock);
+ hlist_add_head(&map->hn, &fl->maps);
+ spin_unlock(&fl->hlock);
+ }
}
-static int fastrpc_mmap_find(struct fastrpc_file *fl, int fd, uintptr_t va,
- ssize_t len, int mflags, int refs, struct fastrpc_mmap **ppmap)
+static int fastrpc_mmap_find(struct fastrpc_file *fl, int fd,
+ uintptr_t __user va, ssize_t len, int mflags, int refs,
+ struct fastrpc_mmap **ppmap)
{
- struct fastrpc_mmap *match = 0, *map;
+ struct fastrpc_apps *me = &gfa;
+ struct fastrpc_mmap *match = NULL, *map = NULL;
struct hlist_node *n;
-
- spin_lock(&fl->hlock);
- hlist_for_each_entry_safe(map, n, &fl->maps, hn) {
- if (va >= map->va &&
- va + len <= map->va + map->len &&
- map->fd == fd) {
- if (refs)
- map->refs++;
- match = map;
- break;
+ if (mflags == ADSP_MMAP_HEAP_ADDR ||
+ mflags == ADSP_MMAP_REMOTE_HEAP_ADDR) {
+ spin_lock(&me->hlock);
+ hlist_for_each_entry_safe(map, n, &me->maps, hn) {
+ if (va >= map->va &&
+ va + len <= map->va + map->len &&
+ map->fd == fd) {
+ if (refs)
+ map->refs++;
+ match = map;
+ break;
+ }
}
+ spin_unlock(&me->hlock);
+ } else {
+ spin_lock(&fl->hlock);
+ hlist_for_each_entry_safe(map, n, &fl->maps, hn) {
+ if (va >= map->va &&
+ va + len <= map->va + map->len &&
+ map->fd == fd) {
+ if (refs)
+ map->refs++;
+ match = map;
+ break;
+ }
+ }
+ spin_unlock(&fl->hlock);
}
- spin_unlock(&fl->hlock);
if (match) {
*ppmap = match;
return 0;
@@ -422,10 +462,28 @@
return -ENOTTY;
}
+static int dma_alloc_memory(phys_addr_t *region_start, ssize_t size)
+{
+ struct fastrpc_apps *me = &gfa;
+ void *vaddr = NULL;
+
+ if (me->dev == NULL) {
+ pr_err("device adsprpc-mem is not initialized\n");
+ return -ENODEV;
+ }
+ vaddr = dma_alloc_coherent(me->dev, size, region_start, GFP_KERNEL);
+ if (!vaddr) {
+ pr_err("ADSPRPC: Failed to allocate %x remote heap memory\n",
+ (unsigned int)size);
+ return -ENOMEM;
+ }
+ return 0;
+}
+
static int fastrpc_mmap_remove(struct fastrpc_file *fl, uintptr_t va,
ssize_t len, struct fastrpc_mmap **ppmap)
{
- struct fastrpc_mmap *match = 0, *map;
+ struct fastrpc_mmap *match = NULL, *map;
struct hlist_node *n;
struct fastrpc_apps *me = &gfa;
@@ -464,51 +522,75 @@
static void fastrpc_mmap_free(struct fastrpc_mmap *map)
{
+ struct fastrpc_apps *me = &gfa;
struct fastrpc_file *fl;
int vmid;
struct fastrpc_session_ctx *sess;
- int destVM[1] = {VMID_HLOS};
- int destVMperm[1] = {PERM_READ | PERM_WRITE | PERM_EXEC};
if (!map)
return;
fl = map->fl;
- spin_lock(&fl->hlock);
- map->refs--;
- if (!map->refs)
- hlist_del_init(&map->hn);
- spin_unlock(&fl->hlock);
+ if (map->flags == ADSP_MMAP_HEAP_ADDR ||
+ map->flags == ADSP_MMAP_REMOTE_HEAP_ADDR) {
+ spin_lock(&me->hlock);
+ map->refs--;
+ if (!map->refs)
+ hlist_del_init(&map->hn);
+ spin_unlock(&me->hlock);
+ } else {
+ spin_lock(&fl->hlock);
+ map->refs--;
+ if (!map->refs)
+ hlist_del_init(&map->hn);
+ spin_unlock(&fl->hlock);
+ }
if (map->refs > 0)
return;
- if (map->secure)
- sess = fl->secsctx;
- else
- sess = fl->sctx;
+ if (map->flags == ADSP_MMAP_HEAP_ADDR ||
+ map->flags == ADSP_MMAP_REMOTE_HEAP_ADDR) {
- if (!IS_ERR_OR_NULL(map->handle))
- ion_free(fl->apps->client, map->handle);
- if (sess && sess->smmu.enabled) {
- if (map->size || map->phys)
- msm_dma_unmap_sg(sess->dev,
- map->table->sgl,
- map->table->nents, DMA_BIDIRECTIONAL,
- map->buf);
+ if (me->dev == NULL) {
+ pr_err("failed to free remote heap allocation\n");
+ return;
+ }
+ if (map->phys) {
+ dma_free_coherent(me->dev, map->size,
+ &(map->va), map->phys);
+ }
+ } else {
+ int destVM[1] = {VMID_HLOS};
+ int destVMperm[1] = {PERM_READ | PERM_WRITE | PERM_EXEC};
+
+ if (map->secure)
+ sess = fl->secsctx;
+ else
+ sess = fl->sctx;
+
+ if (!IS_ERR_OR_NULL(map->handle))
+ ion_free(fl->apps->client, map->handle);
+ if (sess && sess->smmu.enabled) {
+ if (map->size || map->phys)
+ msm_dma_unmap_sg(sess->smmu.dev,
+ map->table->sgl,
+ map->table->nents, DMA_BIDIRECTIONAL,
+ map->buf);
+ }
+ vmid = fl->apps->channel[fl->cid].vmid;
+ if (vmid && map->phys) {
+ int srcVM[2] = {VMID_HLOS, vmid};
+
+ hyp_assign_phys(map->phys, buf_page_size(map->size),
+ srcVM, 2, destVM, destVMperm, 1);
+ }
+
+ if (!IS_ERR_OR_NULL(map->table))
+ dma_buf_unmap_attachment(map->attach, map->table,
+ DMA_BIDIRECTIONAL);
+ if (!IS_ERR_OR_NULL(map->attach))
+ dma_buf_detach(map->buf, map->attach);
+ if (!IS_ERR_OR_NULL(map->buf))
+ dma_buf_put(map->buf);
}
- vmid = fl->apps->channel[fl->cid].vmid;
- if (vmid && map->phys) {
- int srcVM[2] = {VMID_HLOS, vmid};
-
- hyp_assign_phys(map->phys, buf_page_size(map->size),
- srcVM, 2, destVM, destVMperm, 1);
- }
-
- if (!IS_ERR_OR_NULL(map->table))
- dma_buf_unmap_attachment(map->attach, map->table,
- DMA_BIDIRECTIONAL);
- if (!IS_ERR_OR_NULL(map->attach))
- dma_buf_detach(map->buf, map->attach);
- if (!IS_ERR_OR_NULL(map->buf))
- dma_buf_put(map->buf);
kfree(map);
}
@@ -516,15 +598,17 @@
struct fastrpc_session_ctx **session);
static int fastrpc_mmap_create(struct fastrpc_file *fl, int fd,
- unsigned int attr, uintptr_t va, ssize_t len, int mflags,
+ unsigned int attr, uintptr_t __user va, ssize_t len, int mflags,
struct fastrpc_mmap **ppmap)
{
+ struct fastrpc_apps *me = &gfa;
struct fastrpc_session_ctx *sess;
struct fastrpc_apps *apps = fl->apps;
int cid = fl->cid;
struct fastrpc_channel_ctx *chan = &apps->channel[cid];
- struct fastrpc_mmap *map = 0;
+ struct fastrpc_mmap *map = NULL;
unsigned long attrs;
+ phys_addr_t region_start = 0;
unsigned long flags;
int err = 0, vmid;
@@ -540,47 +624,58 @@
map->fl = fl;
map->fd = fd;
map->attr = attr;
- VERIFY(err, !IS_ERR_OR_NULL(map->handle =
- ion_import_dma_buf_fd(fl->apps->client, fd)));
- if (err)
- goto bail;
- VERIFY(err, !ion_handle_get_flags(fl->apps->client, map->handle,
- &flags));
- if (err)
- goto bail;
-
- map->uncached = !ION_IS_CACHED(flags);
- if (map->attr & FASTRPC_ATTR_NOVA)
- map->uncached = 1;
-
- map->secure = flags & ION_FLAG_SECURE;
- if (map->secure) {
- if (!fl->secsctx)
- err = fastrpc_session_alloc(chan, 1,
- &fl->secsctx);
+ if (mflags == ADSP_MMAP_HEAP_ADDR ||
+ mflags == ADSP_MMAP_REMOTE_HEAP_ADDR) {
+ map->apps = me;
+ map->fl = NULL;
+ VERIFY(err, !dma_alloc_memory(®ion_start, len));
if (err)
goto bail;
- }
- if (map->secure)
- sess = fl->secsctx;
- else
- sess = fl->sctx;
- VERIFY(err, !IS_ERR_OR_NULL(sess));
- if (err)
- goto bail;
- VERIFY(err, !IS_ERR_OR_NULL(map->buf = dma_buf_get(fd)));
- if (err)
- goto bail;
- VERIFY(err, !IS_ERR_OR_NULL(map->attach =
- dma_buf_attach(map->buf, sess->dev)));
- if (err)
- goto bail;
- VERIFY(err, !IS_ERR_OR_NULL(map->table =
- dma_buf_map_attachment(map->attach,
- DMA_BIDIRECTIONAL)));
- if (err)
- goto bail;
- if (sess->smmu.enabled) {
+ map->phys = (uintptr_t)region_start;
+ map->size = len;
+ map->va = (uintptr_t __user)map->phys;
+ } else {
+ VERIFY(err, !IS_ERR_OR_NULL(map->handle =
+ ion_import_dma_buf_fd(fl->apps->client, fd)));
+ if (err)
+ goto bail;
+ VERIFY(err, !ion_handle_get_flags(fl->apps->client, map->handle,
+ &flags));
+ if (err)
+ goto bail;
+
+ map->uncached = !ION_IS_CACHED(flags);
+ if (map->attr & FASTRPC_ATTR_NOVA)
+ map->uncached = 1;
+
+ map->secure = flags & ION_FLAG_SECURE;
+ if (map->secure) {
+ if (!fl->secsctx)
+ err = fastrpc_session_alloc(chan, 1,
+ &fl->secsctx);
+ if (err)
+ goto bail;
+ }
+ if (map->secure)
+ sess = fl->secsctx;
+ else
+ sess = fl->sctx;
+ VERIFY(err, !IS_ERR_OR_NULL(sess));
+ if (err)
+ goto bail;
+ VERIFY(err, !IS_ERR_OR_NULL(map->buf = dma_buf_get(fd)));
+ if (err)
+ goto bail;
+ VERIFY(err, !IS_ERR_OR_NULL(map->attach =
+ dma_buf_attach(map->buf, sess->smmu.dev)));
+ if (err)
+ goto bail;
+ VERIFY(err, !IS_ERR_OR_NULL(map->table =
+ dma_buf_map_attachment(map->attach,
+ DMA_BIDIRECTIONAL)));
+ if (err)
+ goto bail;
+ if (sess->smmu.enabled) {
attrs = DMA_ATTR_EXEC_MAPPING;
if (map->attr & FASTRPC_ATTR_NON_COHERENT ||
@@ -590,37 +685,38 @@
attrs |= DMA_ATTR_FORCE_COHERENT;
VERIFY(err, map->table->nents ==
- msm_dma_map_sg_attrs(sess->dev,
+ msm_dma_map_sg_attrs(sess->smmu.dev,
map->table->sgl, map->table->nents,
DMA_BIDIRECTIONAL, map->buf, attrs));
- if (err)
+ if (err)
+ goto bail;
+ } else {
+ VERIFY(err, map->table->nents == 1);
+ if (err)
goto bail;
- } else {
- VERIFY(err, map->table->nents == 1);
- if (err)
- goto bail;
- }
- map->phys = sg_dma_address(map->table->sgl);
- if (sess->smmu.cb) {
- map->phys += ((uint64_t)sess->smmu.cb << 32);
- map->size = sg_dma_len(map->table->sgl);
- } else {
- map->size = buf_page_size(len);
- }
- vmid = fl->apps->channel[fl->cid].vmid;
- if (vmid) {
- int srcVM[1] = {VMID_HLOS};
- int destVM[2] = {VMID_HLOS, vmid};
- int destVMperm[2] = {PERM_READ | PERM_WRITE,
- PERM_READ | PERM_WRITE | PERM_EXEC};
+ }
+ map->phys = sg_dma_address(map->table->sgl);
+ if (sess->smmu.cb) {
+ map->phys += ((uint64_t)sess->smmu.cb << 32);
+ map->size = sg_dma_len(map->table->sgl);
+ } else {
+ map->size = buf_page_size(len);
+ }
+ vmid = fl->apps->channel[fl->cid].vmid;
+ if (vmid) {
+ int srcVM[1] = {VMID_HLOS};
+ int destVM[2] = {VMID_HLOS, vmid};
+ int destVMperm[2] = {PERM_READ | PERM_WRITE,
+ PERM_READ | PERM_WRITE | PERM_EXEC};
- VERIFY(err, !hyp_assign_phys(map->phys,
- buf_page_size(map->size),
- srcVM, 1, destVM, destVMperm, 2));
- if (err)
- goto bail;
+ VERIFY(err, !hyp_assign_phys(map->phys,
+ buf_page_size(map->size),
+ srcVM, 1, destVM, destVMperm, 2));
+ if (err)
+ goto bail;
+ }
+ map->va = va;
}
- map->va = va;
map->len = len;
fastrpc_mmap_add(map);
@@ -636,7 +732,7 @@
struct fastrpc_buf **obuf)
{
int err = 0, vmid;
- struct fastrpc_buf *buf = 0, *fr = 0;
+ struct fastrpc_buf *buf = NULL, *fr = NULL;
struct hlist_node *n;
VERIFY(err, size > 0);
@@ -656,21 +752,21 @@
*obuf = fr;
return 0;
}
- buf = 0;
- VERIFY(err, buf = kzalloc(sizeof(*buf), GFP_KERNEL));
+ buf = NULL;
+ VERIFY(err, NULL != (buf = kzalloc(sizeof(*buf), GFP_KERNEL)));
if (err)
goto bail;
INIT_HLIST_NODE(&buf->hn);
buf->fl = fl;
- buf->virt = 0;
+ buf->virt = NULL;
buf->phys = 0;
buf->size = size;
- buf->virt = dma_alloc_coherent(fl->sctx->dev, buf->size,
+ buf->virt = dma_alloc_coherent(fl->sctx->smmu.dev, buf->size,
(void *)&buf->phys, GFP_KERNEL);
if (IS_ERR_OR_NULL(buf->virt)) {
/* free cache and retry */
fastrpc_buf_list_free(fl);
- buf->virt = dma_alloc_coherent(fl->sctx->dev, buf->size,
+ buf->virt = dma_alloc_coherent(fl->sctx->smmu.dev, buf->size,
(void *)&buf->phys, GFP_KERNEL);
VERIFY(err, !IS_ERR_OR_NULL(buf->virt));
}
@@ -704,7 +800,7 @@
struct smq_invoke_ctx **po)
{
int err = 0;
- struct smq_invoke_ctx *ctx = 0, *ictx = 0;
+ struct smq_invoke_ctx *ctx = NULL, *ictx = NULL;
struct hlist_node *n;
struct fastrpc_ioctl_invoke *invoke = &inv->inv;
@@ -759,7 +855,7 @@
ctx->overs[i].raix = i;
ctx->overps[i] = &ctx->overs[i];
}
- sort(ctx->overps, nbufs, sizeof(*ctx->overps), overlap_ptr_cmp, 0);
+ sort(ctx->overps, nbufs, sizeof(*ctx->overps), overlap_ptr_cmp, NULL);
max.start = 0;
max.end = 0;
for (i = 0; i < nbufs; ++i) {
@@ -788,7 +884,8 @@
#define K_COPY_FROM_USER(err, kernel, dst, src, size) \
do {\
if (!(kernel))\
- VERIFY(err, 0 == copy_from_user((dst), (src),\
+ VERIFY(err, 0 == copy_from_user((dst),\
+ (void const __user *)(src),\
(size)));\
else\
memmove((dst), (src), (size));\
@@ -797,8 +894,8 @@
#define K_COPY_TO_USER(err, kernel, dst, src, size) \
do {\
if (!(kernel))\
- VERIFY(err, 0 == copy_to_user((dst), (src),\
- (size)));\
+ VERIFY(err, 0 == copy_to_user((void __user *)(dst), \
+ (src), (size)));\
else\
memmove((dst), (src), (size));\
} while (0)
@@ -811,7 +908,7 @@
struct smq_invoke_ctx **po)
{
int err = 0, bufs, size = 0;
- struct smq_invoke_ctx *ctx = 0;
+ struct smq_invoke_ctx *ctx = NULL;
struct fastrpc_ctx_lst *clst = &fl->clst;
struct fastrpc_ioctl_invoke *invoke = &invokefd->inv;
@@ -822,7 +919,7 @@
sizeof(*ctx->overs) * (bufs) +
sizeof(*ctx->overps) * (bufs);
- VERIFY(err, ctx = kzalloc(sizeof(*ctx) + size, GFP_KERNEL));
+ VERIFY(err, NULL != (ctx = kzalloc(sizeof(*ctx) + size, GFP_KERNEL)));
if (err)
goto bail;
@@ -836,7 +933,7 @@
ctx->overs = (struct overlap *)(&ctx->attrs[bufs]);
ctx->overps = (struct overlap **)(&ctx->overs[bufs]);
- K_COPY_FROM_USER(err, kernel, ctx->lpra, invoke->pra,
+ K_COPY_FROM_USER(err, kernel, (void *)ctx->lpra, invoke->pra,
bufs * sizeof(*ctx->lpra));
if (err)
goto bail;
@@ -864,6 +961,7 @@
ctx->pid = current->pid;
ctx->tgid = fl->tgid;
init_completion(&ctx->work);
+ ctx->magic = FASTRPC_CTX_MAGIC;
spin_lock(&fl->hlock);
hlist_add_head(&ctx->hn, &clst->pending);
@@ -899,6 +997,7 @@
for (i = 0; i < nbufs; ++i)
fastrpc_mmap_free(ctx->maps[i]);
fastrpc_buf_free(ctx->buf, 1);
+ ctx->magic = 0;
kfree(ctx);
}
@@ -947,11 +1046,11 @@
static void fastrpc_context_list_dtor(struct fastrpc_file *fl)
{
struct fastrpc_ctx_lst *clst = &fl->clst;
- struct smq_invoke_ctx *ictx = 0, *ctxfree;
+ struct smq_invoke_ctx *ictx = NULL, *ctxfree;
struct hlist_node *n;
do {
- ctxfree = 0;
+ ctxfree = NULL;
spin_lock(&fl->hlock);
hlist_for_each_entry_safe(ictx, n, &clst->interrupted, hn) {
hlist_del_init(&ictx->hn);
@@ -963,7 +1062,7 @@
context_free(ctxfree);
} while (ctxfree);
do {
- ctxfree = 0;
+ ctxfree = NULL;
spin_lock(&fl->hlock);
hlist_for_each_entry_safe(ictx, n, &clst->pending, hn) {
hlist_del_init(&ictx->hn);
@@ -983,7 +1082,7 @@
struct hlist_node *n;
do {
- free = 0;
+ free = NULL;
spin_lock(&me->hlock);
hlist_for_each_entry_safe(fl, n, &me->drivers, hn) {
hlist_del_init(&fl->hn);
@@ -1015,13 +1114,13 @@
uint32_t *crclist;
/* calculate size of the metadata */
- rpra = 0;
+ rpra = NULL;
list = smq_invoke_buf_start(rpra, sc);
pages = smq_phy_page_start(sc, list);
ipage = pages;
for (i = 0; i < bufs; ++i) {
- uintptr_t buf = (uintptr_t)lpra[i].buf.pv;
+ uintptr_t __user buf = (uintptr_t __user)lpra[i].buf.pv;
ssize_t len = lpra[i].buf.len;
if (ctx->fds[i] && (ctx->fds[i] != -1))
@@ -1089,9 +1188,10 @@
list[i].pgidx = ipage - pages;
ipage++;
}
+
/* map ion buffers */
PERF(ctx->fl->profile, ctx->fl->perf.map,
- for (i = 0; i < inbufs + outbufs; ++i) {
+ for (i = 0; rpra && i < inbufs + outbufs; ++i) {
struct fastrpc_mmap *map = ctx->maps[i];
uint64_t buf = ptr_to_uint64(lpra[i].buf.pv);
ssize_t len = lpra[i].buf.len;
@@ -1199,7 +1299,7 @@
uint64_to_ptr(rpra[i].buf.pv + rpra[i].buf.len));
}
PERF_END);
- for (i = bufs; i < bufs + handles; i++) {
+ for (i = bufs; rpra && i < bufs + handles; i++) {
rpra[i].dma.fd = ctx->fds[i];
rpra[i].dma.len = (uint32_t)lpra[i].buf.len;
rpra[i].dma.offset = (uint32_t)(uintptr_t)lpra[i].buf.pv;
@@ -1246,7 +1346,7 @@
goto bail;
} else {
fastrpc_mmap_free(ctx->maps[i]);
- ctx->maps[i] = 0;
+ ctx->maps[i] = NULL;
}
}
if (inbufs + outbufs + handles) {
@@ -1259,7 +1359,7 @@
}
}
if (ctx->crc && crclist && rpra)
- K_COPY_TO_USER(err, kernel, (void __user *)ctx->crc,
+ K_COPY_TO_USER(err, kernel, ctx->crc,
crclist, M_CRCLIST*sizeof(uint32_t));
bail:
@@ -1350,7 +1450,7 @@
struct fastrpc_channel_ctx *channel_ctx = &fl->apps->channel[fl->cid];
int err = 0;
- VERIFY(err, 0 != channel_ctx->chan);
+ VERIFY(err, NULL != channel_ctx->chan);
if (err)
goto bail;
msg->pid = fl->tgid;
@@ -1401,7 +1501,7 @@
uint32_t kernel,
struct fastrpc_ioctl_invoke_crc *inv)
{
- struct smq_invoke_ctx *ctx = 0;
+ struct smq_invoke_ctx *ctx = NULL;
struct fastrpc_ioctl_invoke *invoke = &inv->inv;
int cid = fl->cid;
int interrupted = 0;
@@ -1487,8 +1587,7 @@
if (fl->profile && !interrupted) {
if (invoke->handle != FASTRPC_STATIC_HANDLE_LISTENER)
fl->perf.invoke += getnstimediff(&invoket);
- if (!(invoke->handle >= 0 &&
- invoke->handle <= FASTRPC_STATIC_HANDLE_MAX))
+ if (invoke->handle > FASTRPC_STATIC_HANDLE_MAX)
fl->perf.count++;
}
return err;
@@ -1499,10 +1598,16 @@
struct fastrpc_ioctl_init_attrs *uproc)
{
int err = 0;
+ struct fastrpc_apps *me = &gfa;
struct fastrpc_ioctl_invoke_crc ioctl;
struct fastrpc_ioctl_init *init = &uproc->init;
struct smq_phy_page pages[1];
- struct fastrpc_mmap *file = 0, *mem = 0;
+ struct fastrpc_mmap *file = NULL, *mem = NULL;
+ char *proc_name = NULL;
+ int srcVM[1] = {VMID_HLOS};
+ int destVM[1] = {VMID_ADSP_Q6};
+ int destVMperm[1] = {PERM_READ | PERM_WRITE | PERM_EXEC};
+ int hlosVMperm[1] = {PERM_READ | PERM_WRITE | PERM_EXEC};
VERIFY(err, !fastrpc_channel_open(fl));
if (err)
@@ -1516,8 +1621,8 @@
ioctl.inv.handle = 1;
ioctl.inv.sc = REMOTE_SCALARS_MAKE(0, 1, 0);
ioctl.inv.pra = ra;
- ioctl.fds = 0;
- ioctl.attrs = 0;
+ ioctl.fds = NULL;
+ ioctl.attrs = NULL;
ioctl.crc = NULL;
fl->pd = 0;
VERIFY(err, !(err = fastrpc_internal_invoke(fl,
@@ -1547,6 +1652,7 @@
if (err)
goto bail;
}
+
inbuf.pageslen = 1;
VERIFY(err, !fastrpc_mmap_create(fl, init->memfd, 0,
init->mem, init->memlen, mflags, &mem));
@@ -1587,7 +1693,79 @@
ioctl.inv.sc = REMOTE_SCALARS_MAKE(7, 6, 0);
ioctl.inv.pra = ra;
ioctl.fds = fds;
- ioctl.attrs = 0;
+ ioctl.attrs = NULL;
+ ioctl.crc = NULL;
+ VERIFY(err, !(err = fastrpc_internal_invoke(fl,
+ FASTRPC_MODE_PARALLEL, 1, &ioctl)));
+ if (err)
+ goto bail;
+ } else if (init->flags == FASTRPC_INIT_CREATE_STATIC) {
+ remote_arg_t ra[3];
+ uint64_t phys = 0;
+ ssize_t size = 0;
+ int fds[3];
+ struct {
+ int pgid;
+ int namelen;
+ int pageslen;
+ } inbuf;
+
+ if (!init->filelen)
+ goto bail;
+
+ proc_name = kzalloc(init->filelen, GFP_KERNEL);
+ VERIFY(err, !IS_ERR_OR_NULL(proc_name));
+ if (err)
+ goto bail;
+ VERIFY(err, 0 == copy_from_user((void *)proc_name,
+ (void __user *)init->file, init->filelen));
+ if (err)
+ goto bail;
+
+ inbuf.pgid = current->tgid;
+ inbuf.namelen = strlen(proc_name)+1;
+ inbuf.pageslen = 0;
+ if (!me->staticpd_flags) {
+ inbuf.pageslen = 1;
+ VERIFY(err, !fastrpc_mmap_create(fl, -1, 0, init->mem,
+ init->memlen, ADSP_MMAP_REMOTE_HEAP_ADDR,
+ &mem));
+ if (err)
+ goto bail;
+ phys = mem->phys;
+ size = mem->size;
+ VERIFY(err, !hyp_assign_phys(phys, (uint64_t)size,
+ srcVM, 1, destVM, destVMperm, 1));
+ if (err) {
+ pr_err("ADSPRPC: hyp_assign_phys fail err %d",
+ err);
+ pr_err("map->phys %llx, map->size %d\n",
+ phys, (int)size);
+ goto bail;
+ }
+ me->staticpd_flags = 1;
+ }
+
+ ra[0].buf.pv = (void *)&inbuf;
+ ra[0].buf.len = sizeof(inbuf);
+ fds[0] = 0;
+
+ ra[1].buf.pv = (void *)proc_name;
+ ra[1].buf.len = inbuf.namelen;
+ fds[1] = 0;
+
+ pages[0].addr = phys;
+ pages[0].size = size;
+
+ ra[2].buf.pv = (void *)pages;
+ ra[2].buf.len = sizeof(*pages);
+ fds[2] = 0;
+ ioctl.inv.handle = 1;
+
+ ioctl.inv.sc = REMOTE_SCALARS_MAKE(8, 3, 0);
+ ioctl.inv.pra = ra;
+ ioctl.fds = NULL;
+ ioctl.attrs = NULL;
ioctl.crc = NULL;
VERIFY(err, !(err = fastrpc_internal_invoke(fl,
FASTRPC_MODE_PARALLEL, 1, &ioctl)));
@@ -1597,8 +1775,14 @@
err = -ENOTTY;
}
bail:
- if (mem && err)
+ if (err && (init->flags == FASTRPC_INIT_CREATE_STATIC))
+ me->staticpd_flags = 0;
+ if (mem && err) {
+ if (mem->flags == ADSP_MMAP_REMOTE_HEAP_ADDR)
+ hyp_assign_phys(mem->phys, (uint64_t)mem->size,
+ destVM, 1, srcVM, hlosVMperm, 1);
fastrpc_mmap_free(mem);
+ }
if (file)
fastrpc_mmap_free(file);
return err;
@@ -1614,7 +1798,7 @@
VERIFY(err, fl->cid >= 0 && fl->cid < NUM_CHANNELS);
if (err)
goto bail;
- VERIFY(err, fl->apps->channel[fl->cid].chan != 0);
+ VERIFY(err, fl->apps->channel[fl->cid].chan != NULL);
if (err)
goto bail;
tgid = fl->tgid;
@@ -1623,8 +1807,8 @@
ioctl.inv.handle = 1;
ioctl.inv.sc = REMOTE_SCALARS_MAKE(1, 1, 0);
ioctl.inv.pra = ra;
- ioctl.fds = 0;
- ioctl.attrs = 0;
+ ioctl.fds = NULL;
+ ioctl.attrs = NULL;
ioctl.crc = NULL;
VERIFY(err, 0 == (err = fastrpc_internal_invoke(fl,
FASTRPC_MODE_PARALLEL, 1, &ioctl)));
@@ -1670,13 +1854,86 @@
else
ioctl.inv.sc = REMOTE_SCALARS_MAKE(2, 2, 1);
ioctl.inv.pra = ra;
- ioctl.fds = 0;
- ioctl.attrs = 0;
+ ioctl.fds = NULL;
+ ioctl.attrs = NULL;
ioctl.crc = NULL;
VERIFY(err, 0 == (err = fastrpc_internal_invoke(fl,
FASTRPC_MODE_PARALLEL, 1, &ioctl)));
map->raddr = (uintptr_t)routargs.vaddrout;
+ if (err)
+ goto bail;
+ if (flags == ADSP_MMAP_HEAP_ADDR) {
+ struct scm_desc desc = {0};
+ desc.args[0] = TZ_PIL_AUTH_QDSP6_PROC;
+ desc.args[1] = map->phys;
+ desc.args[2] = map->size;
+ desc.arginfo = SCM_ARGS(3);
+ err = scm_call2(SCM_SIP_FNID(SCM_SVC_PIL,
+ TZ_PIL_PROTECT_MEM_SUBSYS_ID), &desc);
+ } else if (flags == ADSP_MMAP_REMOTE_HEAP_ADDR) {
+
+ int srcVM[1] = {VMID_HLOS};
+ int destVM[1] = {VMID_ADSP_Q6};
+ int destVMperm[1] = {PERM_READ | PERM_WRITE | PERM_EXEC};
+
+ VERIFY(err, !hyp_assign_phys(map->phys, (uint64_t)map->size,
+ srcVM, 1, destVM, destVMperm, 1));
+ if (err)
+ goto bail;
+ }
+bail:
+ return err;
+}
+
+static int fastrpc_munmap_on_dsp_rh(struct fastrpc_file *fl,
+ struct fastrpc_mmap *map)
+{
+ int err = 0;
+ int srcVM[1] = {VMID_ADSP_Q6};
+ int destVM[1] = {VMID_HLOS};
+ int destVMperm[1] = {PERM_READ | PERM_WRITE | PERM_EXEC};
+
+ if (map->flags == ADSP_MMAP_HEAP_ADDR) {
+ struct fastrpc_ioctl_invoke_crc ioctl;
+ struct scm_desc desc = {0};
+ remote_arg_t ra[1];
+ int err = 0;
+ struct {
+ uint8_t skey;
+ } routargs;
+
+ ra[0].buf.pv = (void *)&routargs;
+ ra[0].buf.len = sizeof(routargs);
+
+ ioctl.inv.handle = 1;
+ ioctl.inv.sc = REMOTE_SCALARS_MAKE(7, 0, 1);
+ ioctl.inv.pra = ra;
+ ioctl.fds = NULL;
+ ioctl.attrs = NULL;
+ ioctl.crc = NULL;
+ if (fl == NULL)
+ goto bail;
+
+ VERIFY(err, 0 == (err = fastrpc_internal_invoke(fl,
+ FASTRPC_MODE_PARALLEL, 1, &ioctl)));
+ if (err)
+ goto bail;
+ desc.args[0] = TZ_PIL_AUTH_QDSP6_PROC;
+ desc.args[1] = map->phys;
+ desc.args[2] = map->size;
+ desc.args[3] = routargs.skey;
+ desc.arginfo = SCM_ARGS(4);
+ err = scm_call2(SCM_SIP_FNID(SCM_SVC_PIL,
+ TZ_PIL_CLEAR_PROTECT_MEM_SUBSYS_ID), &desc);
+ } else if (map->flags == ADSP_MMAP_REMOTE_HEAP_ADDR) {
+ VERIFY(err, !hyp_assign_phys(map->phys, (uint64_t)map->size,
+ srcVM, 1, destVM, destVMperm, 1));
+ if (err)
+ goto bail;
+ }
+
+bail:
return err;
}
@@ -1704,11 +1961,66 @@
else
ioctl.inv.sc = REMOTE_SCALARS_MAKE(3, 1, 0);
ioctl.inv.pra = ra;
- ioctl.fds = 0;
- ioctl.attrs = 0;
+ ioctl.fds = NULL;
+ ioctl.attrs = NULL;
ioctl.crc = NULL;
VERIFY(err, 0 == (err = fastrpc_internal_invoke(fl,
FASTRPC_MODE_PARALLEL, 1, &ioctl)));
+ if (err)
+ goto bail;
+ if (map->flags == ADSP_MMAP_HEAP_ADDR ||
+ map->flags == ADSP_MMAP_REMOTE_HEAP_ADDR) {
+ VERIFY(err, !fastrpc_munmap_on_dsp_rh(fl, map));
+ if (err)
+ goto bail;
+ }
+bail:
+ return err;
+}
+
+static int fastrpc_mmap_remove_ssr(struct fastrpc_file *fl)
+{
+ struct fastrpc_mmap *match = NULL, *map = NULL;
+ struct hlist_node *n = NULL;
+ int err = 0, ret = 0;
+ struct fastrpc_apps *me = &gfa;
+ struct ramdump_segment *ramdump_segments_rh = NULL;
+
+ do {
+ match = NULL;
+ spin_lock(&me->hlock);
+ hlist_for_each_entry_safe(map, n, &me->maps, hn) {
+ match = map;
+ hlist_del_init(&map->hn);
+ break;
+ }
+ spin_unlock(&me->hlock);
+
+ if (match) {
+ VERIFY(err, !fastrpc_munmap_on_dsp_rh(fl, match));
+ if (err)
+ goto bail;
+ if (me->channel[0].ramdumpenabled) {
+ ramdump_segments_rh = kcalloc(1,
+ sizeof(struct ramdump_segment), GFP_KERNEL);
+ if (ramdump_segments_rh) {
+ ramdump_segments_rh->address =
+ match->phys;
+ ramdump_segments_rh->size = match->size;
+ ret = do_elf_ramdump(
+ me->channel[0].remoteheap_ramdump_dev,
+ ramdump_segments_rh, 1);
+ if (ret < 0)
+ pr_err("ADSPRPC: unable to dump heap");
+ kfree(ramdump_segments_rh);
+ }
+ }
+ fastrpc_mmap_free(match);
+ }
+ } while (match);
+bail:
+ if (err && match)
+ fastrpc_mmap_add(match);
return err;
}
@@ -1721,7 +2033,7 @@
struct fastrpc_ioctl_munmap *ud)
{
int err = 0;
- struct fastrpc_mmap *map = 0;
+ struct fastrpc_mmap *map = NULL;
VERIFY(err, !fastrpc_mmap_remove(fl, ud->vaddrout, ud->size, &map));
if (err)
@@ -1740,15 +2052,16 @@
struct fastrpc_ioctl_mmap *ud)
{
- struct fastrpc_mmap *map = 0;
+ struct fastrpc_mmap *map = NULL;
int err = 0;
- if (!fastrpc_mmap_find(fl, ud->fd, (uintptr_t)ud->vaddrin, ud->size,
- ud->flags, 1, &map))
+ if (!fastrpc_mmap_find(fl, ud->fd, (uintptr_t __user)ud->vaddrin,
+ ud->size, ud->flags, 1, &map))
return 0;
VERIFY(err, !fastrpc_mmap_create(fl, ud->fd, 0,
- (uintptr_t)ud->vaddrin, ud->size, ud->flags, &map));
+ (uintptr_t __user)ud->vaddrin, ud->size,
+ ud->flags, &map));
if (err)
goto bail;
VERIFY(err, 0 == fastrpc_mmap_on_dsp(fl, ud->flags, map));
@@ -1770,7 +2083,7 @@
ctx = container_of(kref, struct fastrpc_channel_ctx, kref);
cid = ctx - &gcinfo[0];
fastrpc_glink_close(ctx->chan, cid);
- ctx->chan = 0;
+ ctx->chan = NULL;
glink_unregister_link_state_cb(ctx->link.link_notify_handle);
ctx->link.link_notify_handle = NULL;
mutex_unlock(&me->smd_mutex);
@@ -1803,6 +2116,7 @@
if (err)
goto bail;
chan->session[0].dev = me->dev;
+ chan->session[0].smmu.dev = me->dev;
}
*session = &chan->session[idx];
@@ -1810,78 +2124,43 @@
return err;
}
-bool fastrpc_glink_notify_rx_intent_req(void *h, const void *priv, size_t size)
+static bool fastrpc_glink_notify_rx_intent_req(void *h, const void *priv,
+ size_t size)
{
if (glink_queue_rx_intent(h, NULL, size))
return false;
return true;
}
-void fastrpc_glink_notify_tx_done(void *handle, const void *priv,
+static void fastrpc_glink_notify_tx_done(void *handle, const void *priv,
const void *pkt_priv, const void *ptr)
{
}
-static int fastrpc_search_ctx(uint64_t rctx)
-{
- struct fastrpc_apps *me = &gfa;
- struct fastrpc_file *fl;
- struct hlist_node *n, *m;
- struct smq_invoke_ctx *ictx = NULL;
- struct smq_invoke_ctx *ctx;
- int bfound = 0;
-
- ctx = (struct smq_invoke_ctx *)(uint64_to_ptr(rctx));
- if (!ctx)
- return bfound;
-
- spin_lock(&me->hlock);
- hlist_for_each_entry_safe(fl, n, &me->drivers, hn) {
- if (ctx->fl != fl)
- continue;
- spin_lock(&fl->hlock);
- hlist_for_each_entry_safe(ictx, m, &fl->clst.pending, hn) {
- if (ptr_to_uint64(ictx) == rctx) {
- bfound = 1;
- break;
- }
- }
- hlist_for_each_entry_safe(ictx, m, &fl->clst.interrupted, hn) {
- if (ptr_to_uint64(ictx) == rctx) {
- bfound = 1;
- break;
- }
- }
- spin_unlock(&fl->hlock);
- if (bfound)
- break;
- }
- spin_unlock(&me->hlock);
- return bfound;
-}
-
-void fastrpc_glink_notify_rx(void *handle, const void *priv,
+static void fastrpc_glink_notify_rx(void *handle, const void *priv,
const void *pkt_priv, const void *ptr, size_t size)
{
struct smq_invoke_rsp *rsp = (struct smq_invoke_rsp *)ptr;
- int bfound = 0;
+ struct smq_invoke_ctx *ctx;
+ int err = 0;
- if (!rsp || (size < sizeof(*rsp)))
+ VERIFY(err, (rsp && size >= sizeof(*rsp)));
+ if (err)
goto bail;
- bfound = fastrpc_search_ctx((uint64_t)(rsp->ctx & ~1));
- if (!bfound) {
- pr_err("adsprpc: invalid context %pK\n", (void *)rsp->ctx);
+ ctx = (struct smq_invoke_ctx *)(uint64_to_ptr(rsp->ctx & ~1));
+ VERIFY(err, (ctx && ctx->magic == FASTRPC_CTX_MAGIC));
+ if (err)
goto bail;
- }
- rsp->ctx = rsp->ctx & ~1;
- context_notify_user(uint64_to_ptr(rsp->ctx), rsp->retval);
+ context_notify_user(ctx, rsp->retval);
bail:
+ if (err)
+ pr_err("adsprpc: invalid response or context\n");
glink_rx_done(handle, ptr, true);
}
-void fastrpc_glink_notify_state(void *handle, const void *priv,
+static void fastrpc_glink_notify_state(void *handle, const void *priv,
unsigned int event)
{
struct fastrpc_apps *me = &gfa;
@@ -1902,7 +2181,7 @@
case GLINK_REMOTE_DISCONNECTED:
if (me->channel[cid].chan) {
fastrpc_glink_close(me->channel[cid].chan, cid);
- me->channel[cid].chan = 0;
+ me->channel[cid].chan = NULL;
}
break;
default:
@@ -1936,7 +2215,7 @@
static int fastrpc_file_free(struct fastrpc_file *fl)
{
struct hlist_node *n;
- struct fastrpc_mmap *map = 0;
+ struct fastrpc_mmap *map = NULL;
int cid;
if (!fl)
@@ -1982,7 +2261,7 @@
if (fl->debugfs_file != NULL)
debugfs_remove(fl->debugfs_file);
fastrpc_file_free(fl);
- file->private_data = 0;
+ file->private_data = NULL;
}
return 0;
}
@@ -2088,8 +2367,11 @@
cfg->notify_rx_intent_req = fastrpc_glink_notify_rx_intent_req;
handle = glink_open(cfg);
VERIFY(err, !IS_ERR_OR_NULL(handle));
- if (err)
+ if (err) {
+ if (link->port_state == FASTRPC_LINK_CONNECTING)
+ link->port_state = FASTRPC_LINK_DISCONNECTED;
goto bail;
+ }
me->channel[cid].chan = handle;
bail:
return err;
@@ -2106,9 +2388,9 @@
{
struct fastrpc_file *fl = filp->private_data;
struct hlist_node *n;
- struct fastrpc_buf *buf = 0;
- struct fastrpc_mmap *map = 0;
- struct smq_invoke_ctx *ictx = 0;
+ struct fastrpc_buf *buf = NULL;
+ struct fastrpc_mmap *map = NULL;
+ struct smq_invoke_ctx *ictx = NULL;
struct fastrpc_channel_ctx *chan;
struct fastrpc_session_ctx *sess;
unsigned int len = 0;
@@ -2219,12 +2501,20 @@
if (err)
goto bail;
cid = fl->cid;
+ if (me->channel[cid].ssrcount !=
+ me->channel[cid].prevssrcount) {
+ if (!me->channel[cid].issubsystemup) {
+ VERIFY(err, 0);
+ if (err)
+ goto bail;
+ }
+ }
VERIFY(err, cid >= 0 && cid < NUM_CHANNELS);
if (err)
goto bail;
fl->ssrcount = me->channel[cid].ssrcount;
if ((kref_get_unless_zero(&me->channel[cid].kref) == 0) ||
- (me->channel[cid].chan == 0)) {
+ (me->channel[cid].chan == NULL)) {
VERIFY(err, 0 == fastrpc_glink_register(cid, me));
if (err)
goto bail;
@@ -2236,7 +2526,7 @@
wait_for_completion_timeout(&me->channel[cid].workport,
RPC_TIMEOUT));
if (err) {
- me->channel[cid].chan = 0;
+ me->channel[cid].chan = NULL;
goto bail;
}
kref_init(&me->channel[cid].kref);
@@ -2249,6 +2539,8 @@
cid, err);
if (me->channel[cid].ssrcount !=
me->channel[cid].prevssrcount) {
+ if (fastrpc_mmap_remove_ssr(fl))
+ pr_err("ADSPRPC: SSR: Failed to unmap remote heap\n");
me->channel[cid].prevssrcount =
me->channel[cid].ssrcount;
}
@@ -2263,10 +2555,10 @@
{
int err = 0;
struct dentry *debugfs_file;
- struct fastrpc_file *fl = 0;
+ struct fastrpc_file *fl = NULL;
struct fastrpc_apps *me = &gfa;
- VERIFY(err, fl = kzalloc(sizeof(*fl), GFP_KERNEL));
+ VERIFY(err, NULL != (fl = kzalloc(sizeof(*fl), GFP_KERNEL)));
if (err)
return err;
debugfs_file = debugfs_create_file(current->comm, 0644, debugfs_root,
@@ -2297,7 +2589,7 @@
int err = 0;
uint32_t cid;
- VERIFY(err, fl != 0);
+ VERIFY(err, fl != NULL);
if (err)
goto bail;
if (fl->cid == -1) {
@@ -2371,8 +2663,8 @@
int size = 0, err = 0;
uint32_t info;
- p.inv.fds = 0;
- p.inv.attrs = 0;
+ p.inv.fds = NULL;
+ p.inv.attrs = NULL;
p.inv.crc = NULL;
spin_lock(&fl->hlock);
if (fl->file_close == 1) {
@@ -2398,7 +2690,7 @@
case FASTRPC_IOCTL_INVOKE_CRC:
if (!size)
size = sizeof(struct fastrpc_ioctl_invoke_crc);
- VERIFY(err, 0 == copy_from_user(&p.inv, param, size));
+ K_COPY_FROM_USER(err, 0, &p.inv, param, size);
if (err)
goto bail;
VERIFY(err, 0 == (err = fastrpc_internal_invoke(fl, fl->mode,
@@ -2407,20 +2699,20 @@
goto bail;
break;
case FASTRPC_IOCTL_MMAP:
- VERIFY(err, 0 == copy_from_user(&p.mmap, param,
- sizeof(p.mmap)));
+ K_COPY_FROM_USER(err, 0, &p.mmap, param,
+ sizeof(p.mmap));
if (err)
goto bail;
VERIFY(err, 0 == (err = fastrpc_internal_mmap(fl, &p.mmap)));
if (err)
goto bail;
- VERIFY(err, 0 == copy_to_user(param, &p.mmap, sizeof(p.mmap)));
+ K_COPY_TO_USER(err, 0, param, &p.mmap, sizeof(p.mmap));
if (err)
goto bail;
break;
case FASTRPC_IOCTL_MUNMAP:
- VERIFY(err, 0 == copy_from_user(&p.munmap, param,
- sizeof(p.munmap)));
+ K_COPY_FROM_USER(err, 0, &p.munmap, param,
+ sizeof(p.munmap));
if (err)
goto bail;
VERIFY(err, 0 == (err = fastrpc_internal_munmap(fl,
@@ -2447,30 +2739,30 @@
}
break;
case FASTRPC_IOCTL_GETPERF:
- VERIFY(err, 0 == copy_from_user(&p.perf,
- param, sizeof(p.perf)));
+ K_COPY_FROM_USER(err, 0, &p.perf,
+ param, sizeof(p.perf));
if (err)
goto bail;
p.perf.numkeys = sizeof(struct fastrpc_perf)/sizeof(int64_t);
if (p.perf.keys) {
char *keys = PERF_KEYS;
- VERIFY(err, 0 == copy_to_user((char *)p.perf.keys,
- keys, strlen(keys)+1));
+ K_COPY_TO_USER(err, 0, (void *)p.perf.keys,
+ keys, strlen(keys)+1);
if (err)
goto bail;
}
if (p.perf.data) {
- VERIFY(err, 0 == copy_to_user((int64_t *)p.perf.data,
- &fl->perf, sizeof(fl->perf)));
+ K_COPY_TO_USER(err, 0, (void *)p.perf.data,
+ &fl->perf, sizeof(fl->perf));
}
- VERIFY(err, 0 == copy_to_user(param, &p.perf, sizeof(p.perf)));
+ K_COPY_TO_USER(err, 0, param, &p.perf, sizeof(p.perf));
if (err)
goto bail;
break;
case FASTRPC_IOCTL_CONTROL:
- VERIFY(err, 0 == copy_from_user(&p.cp, (void __user *)param,
- sizeof(p.cp)));
+ K_COPY_FROM_USER(err, 0, &p.cp, param,
+ sizeof(p.cp));
if (err)
goto bail;
VERIFY(err, 0 == (err = fastrpc_internal_control(fl, &p.cp)));
@@ -2478,13 +2770,13 @@
goto bail;
break;
case FASTRPC_IOCTL_GETINFO:
- VERIFY(err, 0 == copy_from_user(&info, param, sizeof(info)));
+ K_COPY_FROM_USER(err, 0, &info, param, sizeof(info));
if (err)
goto bail;
VERIFY(err, 0 == (err = fastrpc_get_info(fl, &info)));
if (err)
goto bail;
- VERIFY(err, 0 == copy_to_user(param, &info, sizeof(info)));
+ K_COPY_TO_USER(err, 0, param, &info, sizeof(info));
if (err)
goto bail;
break;
@@ -2496,11 +2788,15 @@
case FASTRPC_IOCTL_INIT_ATTRS:
if (!size)
size = sizeof(struct fastrpc_ioctl_init_attrs);
- VERIFY(err, 0 == copy_from_user(&p.init, param, size));
+ K_COPY_FROM_USER(err, 0, &p.init, param, size);
if (err)
goto bail;
VERIFY(err, p.init.init.filelen >= 0 &&
- p.init.init.memlen >= 0);
+ p.init.init.filelen < INIT_FILELEN_MAX);
+ if (err)
+ goto bail;
+ VERIFY(err, p.init.init.memlen >= 0 &&
+ p.init.init.memlen < INIT_MEMLEN_MAX);
if (err)
goto bail;
VERIFY(err, 0 == fastrpc_init_process(fl, &p.init));
@@ -2523,6 +2819,7 @@
{
struct fastrpc_apps *me = &gfa;
struct fastrpc_channel_ctx *ctx;
+ struct notif_data *notifdata = data;
int cid;
ctx = container_of(nb, struct fastrpc_channel_ctx, nb);
@@ -2530,14 +2827,24 @@
if (code == SUBSYS_BEFORE_SHUTDOWN) {
mutex_lock(&me->smd_mutex);
ctx->ssrcount++;
+ ctx->issubsystemup = 0;
if (ctx->chan) {
fastrpc_glink_close(ctx->chan, cid);
- ctx->chan = 0;
+ ctx->chan = NULL;
pr_info("'restart notifier: closed /dev/%s c %d %d'\n",
gcinfo[cid].name, MAJOR(me->dev_no), cid);
}
mutex_unlock(&me->smd_mutex);
+ if (cid == 0)
+ me->staticpd_flags = 0;
fastrpc_notify_drivers(me, cid);
+ } else if (code == SUBSYS_RAMDUMP_NOTIFICATION) {
+ if (me->channel[0].remoteheap_ramdump_dev &&
+ notifdata->enable_ramdump) {
+ me->channel[0].ramdumpenabled = 1;
+ }
+ } else if (code == SUBSYS_AFTER_POWERUP) {
+ ctx->issubsystemup = 1;
}
return NOTIFY_DONE;
@@ -2568,7 +2875,8 @@
int err = 0, i;
int secure_vmid = VMID_CP_PIXEL;
- VERIFY(err, 0 != (name = of_get_property(dev->of_node, "label", NULL)));
+ VERIFY(err, NULL != (name = of_get_property(dev->of_node,
+ "label", NULL)));
if (err)
goto bail;
for (i = 0; i < NUM_CHANNELS; i++) {
@@ -2612,7 +2920,7 @@
VERIFY(err, !arm_iommu_attach_device(dev, sess->smmu.mapping));
if (err)
goto bail;
- sess->dev = dev;
+ sess->smmu.dev = dev;
sess->smmu.enabled = 1;
chan->sesscount++;
debugfs_global_file = debugfs_create_file("global", 0644, debugfs_root,
@@ -2699,18 +3007,17 @@
if (chan->chan) {
kref_put_mutex(&chan->kref,
fastrpc_channel_close, &me->smd_mutex);
- chan->chan = 0;
+ chan->chan = NULL;
}
for (j = 0; j < NUM_SESSIONS; j++) {
struct fastrpc_session_ctx *sess = &chan->session[j];
-
- if (sess->smmu.enabled) {
- arm_iommu_detach_device(sess->dev);
- sess->dev = 0;
+ if (sess->smmu.dev) {
+ arm_iommu_detach_device(sess->smmu.dev);
+ sess->smmu.dev = NULL;
}
if (sess->smmu.mapping) {
arm_iommu_release_mapping(sess->smmu.mapping);
- sess->smmu.mapping = 0;
+ sess->smmu.mapping = NULL;
}
}
}
@@ -2728,7 +3035,7 @@
static int __init fastrpc_device_init(void)
{
struct fastrpc_apps *me = &gfa;
- struct device *dev = 0;
+ struct device *dev = NULL;
int err = 0, i;
memset(me, 0, sizeof(*me));
@@ -2763,6 +3070,9 @@
me->channel[i].dev = dev;
me->channel[i].ssrcount = 0;
me->channel[i].prevssrcount = 0;
+ me->channel[i].issubsystemup = 1;
+ me->channel[i].ramdumpenabled = 0;
+ me->channel[i].remoteheap_ramdump_dev = NULL;
me->channel[i].nb.notifier_call = fastrpc_restart_notifier_cb;
me->channel[i].handle = subsys_notif_register_notifier(
gcinfo[i].subsys,
diff --git a/drivers/char/adsprpc_shared.h b/drivers/char/adsprpc_shared.h
index 2e8bfc2..43edf71 100644
--- a/drivers/char/adsprpc_shared.h
+++ b/drivers/char/adsprpc_shared.h
@@ -58,6 +58,7 @@
/* INIT a new process or attach to guestos */
#define FASTRPC_INIT_ATTACH 0
#define FASTRPC_INIT_CREATE 1
+#define FASTRPC_INIT_CREATE_STATIC 2
/* Retrives number of input buffers from the scalars parameter */
#define REMOTE_SCALARS_INBUFS(sc) (((sc) >> 16) & 0x0ff)
diff --git a/drivers/char/diag/diag_dci.c b/drivers/char/diag/diag_dci.c
index e2d39e7..0aad08a 100644
--- a/drivers/char/diag/diag_dci.c
+++ b/drivers/char/diag/diag_dci.c
@@ -134,6 +134,35 @@
void diag_dci_record_traffic(int read_bytes, uint8_t ch_type,
uint8_t peripheral, uint8_t proc) { }
#endif
+
+static int check_peripheral_dci_support(int peripheral_id, int dci_proc_id)
+{
+ int dci_peripheral_list = 0;
+
+ if (dci_proc_id < 0 || dci_proc_id >= NUM_DCI_PROC) {
+ pr_err("diag:In %s,not a supported DCI proc id\n", __func__);
+ return 0;
+ }
+ if (peripheral_id < 0 || peripheral_id >= NUM_PERIPHERALS) {
+ pr_err("diag:In %s,not a valid peripheral id\n", __func__);
+ return 0;
+ }
+ dci_peripheral_list = dci_ops_tbl[dci_proc_id].peripheral_status;
+
+ if (dci_peripheral_list <= 0 || dci_peripheral_list > DIAG_CON_ALL) {
+ pr_err("diag:In %s,not a valid dci peripheral mask\n",
+ __func__);
+ return 0;
+ }
+ /* Remove APSS bit mask information */
+ dci_peripheral_list = dci_peripheral_list >> 1;
+
+ if ((1 << peripheral_id) & (dci_peripheral_list))
+ return 1;
+ else
+ return 0;
+}
+
static void create_dci_log_mask_tbl(unsigned char *mask, uint8_t dirty)
{
unsigned char *temp = mask;
@@ -1440,6 +1469,8 @@
struct siginfo info;
struct list_head *start, *temp;
struct diag_dci_client_tbl *entry = NULL;
+ struct pid *pid_struct = NULL;
+ struct task_struct *dci_task = NULL;
memset(&info, 0, sizeof(struct siginfo));
info.si_code = SI_QUEUE;
@@ -1457,20 +1488,32 @@
continue;
if (entry->client_info.notification_list & peripheral_mask) {
info.si_signo = entry->client_info.signal_type;
- if (entry->client &&
- entry->tgid == entry->client->tgid) {
- DIAG_LOG(DIAG_DEBUG_DCI,
- "entry tgid = %d, dci client tgid = %d\n",
- entry->tgid, entry->client->tgid);
- stat = send_sig_info(
- entry->client_info.signal_type,
- &info, entry->client);
- if (stat)
- pr_err("diag: Err sending dci signal to client, signal data: 0x%x, stat: %d\n",
+ pid_struct = find_get_pid(entry->tgid);
+ if (pid_struct) {
+ dci_task = get_pid_task(pid_struct,
+ PIDTYPE_PID);
+ if (!dci_task) {
+ DIAG_LOG(DIAG_DEBUG_PERIPHERALS,
+ "diag: dci client with pid = %d Exited..\n",
+ entry->tgid);
+ mutex_unlock(&driver->dci_mutex);
+ return;
+ }
+ if (entry->client &&
+ entry->tgid == dci_task->tgid) {
+ DIAG_LOG(DIAG_DEBUG_DCI,
+ "entry tgid = %d, dci client tgid = %d\n",
+ entry->tgid, dci_task->tgid);
+ stat = send_sig_info(
+ entry->client_info.signal_type,
+ &info, dci_task);
+ if (stat)
+ pr_err("diag: Err sending dci signal to client, signal data: 0x%x, stat: %d\n",
info.si_int, stat);
- } else
- pr_err("diag: client data is corrupted, signal data: 0x%x, stat: %d\n",
+ } else
+ pr_err("diag: client data is corrupted, signal data: 0x%x, stat: %d\n",
info.si_int, stat);
+ }
}
}
mutex_unlock(&driver->dci_mutex);
@@ -2396,10 +2439,12 @@
* is down. It may also mean that the peripheral doesn't
* support DCI.
*/
- err = diag_dci_write_proc(i, DIAG_CNTL_TYPE, buf,
- header_size + DCI_EVENT_MASK_SIZE);
- if (err != DIAG_DCI_NO_ERROR)
- ret = DIAG_DCI_SEND_DATA_FAIL;
+ if (check_peripheral_dci_support(i, DCI_LOCAL_PROC)) {
+ err = diag_dci_write_proc(i, DIAG_CNTL_TYPE, buf,
+ header_size + DCI_EVENT_MASK_SIZE);
+ if (err != DIAG_DCI_NO_ERROR)
+ ret = DIAG_DCI_SEND_DATA_FAIL;
+ }
}
mutex_unlock(&event_mask.lock);
@@ -2581,11 +2626,13 @@
}
write_len = dci_fill_log_mask(buf, log_mask_ptr);
for (j = 0; j < NUM_PERIPHERALS && write_len; j++) {
- err = diag_dci_write_proc(j, DIAG_CNTL_TYPE, buf,
- write_len);
- if (err != DIAG_DCI_NO_ERROR) {
- updated = 0;
- ret = DIAG_DCI_SEND_DATA_FAIL;
+ if (check_peripheral_dci_support(j, DCI_LOCAL_PROC)) {
+ err = diag_dci_write_proc(j, DIAG_CNTL_TYPE,
+ buf, write_len);
+ if (err != DIAG_DCI_NO_ERROR) {
+ updated = 0;
+ ret = DIAG_DCI_SEND_DATA_FAIL;
+ }
}
}
if (updated)
diff --git a/drivers/char/diag/diag_debugfs.c b/drivers/char/diag/diag_debugfs.c
index 177bbdb..40bfd74 100644
--- a/drivers/char/diag/diag_debugfs.c
+++ b/drivers/char/diag/diag_debugfs.c
@@ -50,7 +50,7 @@
static int diag_dbgfs_finished;
static int diag_dbgfs_dci_data_index;
static int diag_dbgfs_dci_finished;
-
+static struct mutex diag_dci_dbgfs_mutex;
static ssize_t diag_dbgfs_read_status(struct file *file, char __user *ubuf,
size_t count, loff_t *ppos)
{
@@ -152,6 +152,7 @@
buf_size = ksize(buf);
bytes_remaining = buf_size;
+ mutex_lock(&diag_dci_dbgfs_mutex);
if (diag_dbgfs_dci_data_index == 0) {
bytes_written =
scnprintf(buf, buf_size,
@@ -207,8 +208,8 @@
}
temp_data++;
}
-
diag_dbgfs_dci_data_index = (i >= DIAG_DCI_DEBUG_CNT) ? 0 : i + 1;
+ mutex_unlock(&diag_dci_dbgfs_mutex);
bytes_written = simple_read_from_buffer(ubuf, count, ppos, buf,
bytes_in_buf);
kfree(buf);
@@ -1061,6 +1062,7 @@
pr_warn("diag: could not allocate memory for dci debug info\n");
mutex_init(&dci_stat_mutex);
+ mutex_init(&diag_dci_dbgfs_mutex);
return 0;
err:
kfree(dci_traffic);
@@ -1074,6 +1076,7 @@
diag_dbgfs_dent = NULL;
kfree(dci_traffic);
mutex_destroy(&dci_stat_mutex);
+ mutex_destroy(&diag_dci_dbgfs_mutex);
}
#else
int diag_debugfs_init(void) { return 0; }
diff --git a/drivers/char/diag/diag_masks.c b/drivers/char/diag/diag_masks.c
index ee76d39..2df62e4 100644
--- a/drivers/char/diag/diag_masks.c
+++ b/drivers/char/diag/diag_masks.c
@@ -1662,6 +1662,7 @@
struct diag_msg_mask_t *src_mask = NULL;
struct diag_msg_mask_t *dest_mask = NULL;
struct diag_ssid_range_t range;
+ int mask_size = 0;
if (!src || !dest)
return -EINVAL;
@@ -1682,8 +1683,11 @@
err = diag_create_msg_mask_table_entry(dest_mask, &range);
if (err)
break;
- memcpy(dest_mask->ptr, src_mask->ptr,
- dest_mask->range * sizeof(uint32_t));
+ if (src_mask->range_tools < dest_mask->range)
+ mask_size = src_mask->range_tools * sizeof(uint32_t);
+ else
+ mask_size = dest_mask->range * sizeof(uint32_t);
+ memcpy(dest_mask->ptr, src_mask->ptr, mask_size);
src_mask++;
dest_mask++;
}
diff --git a/drivers/char/diag/diag_memorydevice.c b/drivers/char/diag/diag_memorydevice.c
index 7e3fe90..dabb1f4 100644
--- a/drivers/char/diag/diag_memorydevice.c
+++ b/drivers/char/diag/diag_memorydevice.c
@@ -218,6 +218,7 @@
uint8_t drain_again = 0;
int peripheral = 0;
struct diag_md_session_t *session_info = NULL;
+ struct pid *pid_struct = NULL;
for (i = 0; i < NUM_DIAG_MD_DEV && !err; i++) {
ch = &diag_md[i];
@@ -240,6 +241,14 @@
if ((info && (info->peripheral_mask &
MD_PERIPHERAL_MASK(peripheral)) == 0))
goto drop_data;
+ pid_struct = find_get_pid(session_info->pid);
+ if (!pid_struct) {
+ err = -ESRCH;
+ DIAG_LOG(DIAG_DEBUG_PERIPHERALS,
+ "diag: No such md_session_map[%d] with pid = %d err=%d exists..\n",
+ peripheral, session_info->pid, err);
+ goto drop_data;
+ }
/*
* If the data is from remote processor, copy the remote
* token first
@@ -259,27 +268,35 @@
}
if (i > 0) {
remote_token = diag_get_remote(i);
- err = copy_to_user(buf + ret, &remote_token,
- sizeof(int));
+ if (get_pid_task(pid_struct, PIDTYPE_PID)) {
+ err = copy_to_user(buf + ret,
+ &remote_token,
+ sizeof(int));
+ if (err)
+ goto drop_data;
+ ret += sizeof(int);
+ }
+ }
+
+ /* Copy the length of data being passed */
+ if (get_pid_task(pid_struct, PIDTYPE_PID)) {
+ err = copy_to_user(buf + ret,
+ (void *)&(entry->len),
+ sizeof(int));
if (err)
goto drop_data;
ret += sizeof(int);
}
- /* Copy the length of data being passed */
- err = copy_to_user(buf + ret, (void *)&(entry->len),
- sizeof(int));
- if (err)
- goto drop_data;
- ret += sizeof(int);
-
/* Copy the actual data being passed */
- err = copy_to_user(buf + ret, (void *)entry->buf,
- entry->len);
- if (err)
- goto drop_data;
- ret += entry->len;
-
+ if (get_pid_task(pid_struct, PIDTYPE_PID)) {
+ err = copy_to_user(buf + ret,
+ (void *)entry->buf,
+ entry->len);
+ if (err)
+ goto drop_data;
+ ret += entry->len;
+ }
/*
* The data is now copied to the user space client,
* Notify that the write is complete and delete its
@@ -301,7 +318,11 @@
}
*pret = ret;
- err = copy_to_user(buf + sizeof(int), (void *)&num_data, sizeof(int));
+ if (pid_struct && get_pid_task(pid_struct, PIDTYPE_PID)) {
+ err = copy_to_user(buf + sizeof(int),
+ (void *)&num_data,
+ sizeof(int));
+ }
diag_ws_on_copy_complete(DIAG_WS_MUX);
if (drain_again)
chk_logging_wakeup();
diff --git a/drivers/char/diag/diagchar.h b/drivers/char/diag/diagchar.h
index ac3c1fd..c9ae689 100644
--- a/drivers/char/diag/diagchar.h
+++ b/drivers/char/diag/diagchar.h
@@ -21,7 +21,7 @@
#include <linux/spinlock.h>
#include <linux/workqueue.h>
#include <linux/sched.h>
-#include <linux/wakelock.h>
+#include <linux/device.h>
#include <linux/atomic.h>
#include "diagfwd_bridge.h"
diff --git a/drivers/char/diag/diagchar_core.c b/drivers/char/diag/diagchar_core.c
index 543f0a2..54e6486 100644
--- a/drivers/char/diag/diagchar_core.c
+++ b/drivers/char/diag/diagchar_core.c
@@ -1304,11 +1304,9 @@
mutex_unlock(&driver->md_session_lock);
return -ENOMEM;
}
-
new_session->peripheral_mask = 0;
new_session->pid = current->tgid;
new_session->task = current;
-
new_session->log_mask = kzalloc(sizeof(struct diag_mask_info),
GFP_KERNEL);
if (!new_session->log_mask) {
@@ -1426,7 +1424,6 @@
struct diag_md_session_t *diag_md_session_get_pid(int pid)
{
int i;
-
for (i = 0; i < NUM_MD_SESSIONS; i++) {
if (driver->md_session_map[i] &&
driver->md_session_map[i]->pid == pid)
@@ -1542,7 +1539,10 @@
* If this session owns all the requested peripherals, then
* call function to switch the modes/masks for the md_session
*/
+ mutex_lock(&driver->md_session_lock);
session_info = diag_md_session_get_pid(current->tgid);
+ mutex_unlock(&driver->md_session_lock);
+
if (!session_info) {
*change_mode = 1;
return 0;
@@ -1571,7 +1571,9 @@
* owned by this md session
*/
change_mask = driver->md_session_mask & param->peripheral_mask;
+ mutex_lock(&driver->md_session_lock);
session_info = diag_md_session_get_pid(current->tgid);
+ mutex_unlock(&driver->md_session_lock);
if (session_info) {
if ((session_info->peripheral_mask & change_mask)
@@ -1999,8 +2001,9 @@
{
uint8_t hdlc_support;
struct diag_md_session_t *session_info = NULL;
-
+ mutex_lock(&driver->md_session_lock);
session_info = diag_md_session_get_pid(current->tgid);
+ mutex_unlock(&driver->md_session_lock);
if (copy_from_user(&hdlc_support, (void __user *)ioarg,
sizeof(uint8_t)))
return -EFAULT;
@@ -2768,10 +2771,13 @@
} else {
wait_event_interruptible(driver->wait_q,
(driver->in_busy_pktdata == 0));
+ mutex_lock(&driver->md_session_lock);
info = diag_md_session_get_pid(current->tgid);
+ mutex_unlock(&driver->md_session_lock);
ret = diag_process_apps_pkt(user_space_data, len, info);
if (ret == 1)
- diag_send_error_rsp((void *)(user_space_data), len);
+ diag_send_error_rsp((void *)(user_space_data), len,
+ info);
}
fail:
diagmem_free(driver, user_space_data, mempool);
@@ -2835,7 +2841,9 @@
/* send masks to local processor now */
if (!remote_proc) {
+ mutex_lock(&driver->md_session_lock);
session_info = diag_md_session_get_pid(current->tgid);
+ mutex_unlock(&driver->md_session_lock);
if (!session_info) {
pr_err("diag:In %s request came from invalid md session pid:%d",
__func__, current->tgid);
@@ -3010,7 +3018,9 @@
goto exit;
/* place holder for number of data field */
ret += sizeof(int);
+ mutex_lock(&driver->md_session_lock);
session_info = diag_md_session_get_pid(current->tgid);
+ mutex_unlock(&driver->md_session_lock);
exit_stat = diag_md_copy_to_user(buf, &ret, count,
session_info);
goto exit;
@@ -3028,7 +3038,9 @@
if (ret == -EFAULT)
goto exit;
+ mutex_lock(&driver->md_session_lock);
session_info = diag_md_session_get_pid(current->tgid);
+ mutex_unlock(&driver->md_session_lock);
if (session_info) {
COPY_USER_SPACE_OR_ERR(buf+4,
session_info->hdlc_disabled,
diff --git a/drivers/char/diag/diagfwd.c b/drivers/char/diag/diagfwd.c
index b59f245..fc67c1a 100644
--- a/drivers/char/diag/diagfwd.c
+++ b/drivers/char/diag/diagfwd.c
@@ -240,10 +240,11 @@
}
}
-static void pack_rsp_and_send(unsigned char *buf, int len)
+static void pack_rsp_and_send(unsigned char *buf, int len,
+ struct diag_md_session_t *info)
{
int err;
- int retry_count = 0;
+ int retry_count = 0, i, rsp_ctxt;
uint32_t write_len = 0;
unsigned long flags;
unsigned char *rsp_ptr = driver->encoded_rsp_buf;
@@ -259,6 +260,26 @@
}
/*
+ * Explicitly check for the Peripheral Modem here
+ * is necessary till a way to identify a peripheral
+ * if its supporting qshrink4 feature.
+ */
+ if (info && info->peripheral_mask) {
+ if (info->peripheral_mask == DIAG_CON_ALL ||
+ (info->peripheral_mask & (1 << APPS_DATA)) ||
+ (info->peripheral_mask & (1 << PERIPHERAL_MODEM))) {
+ rsp_ctxt = SET_BUF_CTXT(APPS_DATA, TYPE_CMD, 1);
+ } else {
+ for (i = 0; i < NUM_MD_SESSIONS; i++) {
+ if (info->peripheral_mask & (1 << i))
+ break;
+ }
+ rsp_ctxt = SET_BUF_CTXT(i, TYPE_CMD, 1);
+ }
+ } else
+ rsp_ctxt = driver->rsp_buf_ctxt;
+
+ /*
* Keep trying till we get the buffer back. It should probably
* take one or two iterations. When this loops till UINT_MAX, it
* means we did not get a write complete for the previous
@@ -299,8 +320,7 @@
*(uint8_t *)(rsp_ptr + write_len) = CONTROL_CHAR;
write_len += sizeof(uint8_t);
- err = diag_mux_write(DIAG_LOCAL_PROC, rsp_ptr, write_len,
- driver->rsp_buf_ctxt);
+ err = diag_mux_write(DIAG_LOCAL_PROC, rsp_ptr, write_len, rsp_ctxt);
if (err) {
pr_err("diag: In %s, unable to write to mux, err: %d\n",
__func__, err);
@@ -310,12 +330,13 @@
}
}
-static void encode_rsp_and_send(unsigned char *buf, int len)
+static void encode_rsp_and_send(unsigned char *buf, int len,
+ struct diag_md_session_t *info)
{
struct diag_send_desc_type send = { NULL, NULL, DIAG_STATE_START, 0 };
struct diag_hdlc_dest_type enc = { NULL, NULL, 0 };
unsigned char *rsp_ptr = driver->encoded_rsp_buf;
- int err, retry_count = 0;
+ int err, i, rsp_ctxt, retry_count = 0;
unsigned long flags;
if (!rsp_ptr || !buf)
@@ -328,6 +349,26 @@
}
/*
+ * Explicitly check for the Peripheral Modem here
+ * is necessary till a way to identify a peripheral
+ * if its supporting qshrink4 feature.
+ */
+ if (info && info->peripheral_mask) {
+ if (info->peripheral_mask == DIAG_CON_ALL ||
+ (info->peripheral_mask & (1 << APPS_DATA)) ||
+ (info->peripheral_mask & (1 << PERIPHERAL_MODEM))) {
+ rsp_ctxt = SET_BUF_CTXT(APPS_DATA, TYPE_CMD, 1);
+ } else {
+ for (i = 0; i < NUM_MD_SESSIONS; i++) {
+ if (info->peripheral_mask & (1 << i))
+ break;
+ }
+ rsp_ctxt = SET_BUF_CTXT(i, TYPE_CMD, 1);
+ }
+ } else
+ rsp_ctxt = driver->rsp_buf_ctxt;
+
+ /*
* Keep trying till we get the buffer back. It should probably
* take one or two iterations. When this loops till UINT_MAX, it
* means we did not get a write complete for the previous
@@ -370,7 +411,7 @@
diag_hdlc_encode(&send, &enc);
driver->encoded_rsp_len = (int)(enc.dest - (void *)rsp_ptr);
err = diag_mux_write(DIAG_LOCAL_PROC, rsp_ptr, driver->encoded_rsp_len,
- driver->rsp_buf_ctxt);
+ rsp_ctxt);
if (err) {
pr_err("diag: In %s, Unable to write to device, err: %d\n",
__func__, err);
@@ -381,21 +422,23 @@
memset(buf, '\0', DIAG_MAX_RSP_SIZE);
}
-void diag_send_rsp(unsigned char *buf, int len)
+static void diag_send_rsp(unsigned char *buf, int len,
+ struct diag_md_session_t *info)
{
struct diag_md_session_t *session_info = NULL;
uint8_t hdlc_disabled;
- session_info = diag_md_session_get_peripheral(APPS_DATA);
+ session_info = (info) ? info :
+ diag_md_session_get_peripheral(APPS_DATA);
if (session_info)
hdlc_disabled = session_info->hdlc_disabled;
else
hdlc_disabled = driver->hdlc_disabled;
if (hdlc_disabled)
- pack_rsp_and_send(buf, len);
+ pack_rsp_and_send(buf, len, session_info);
else
- encode_rsp_and_send(buf, len);
+ encode_rsp_and_send(buf, len, session_info);
}
void diag_update_pkt_buffer(unsigned char *buf, uint32_t len, int type)
@@ -926,7 +969,8 @@
return write_len;
}
-void diag_send_error_rsp(unsigned char *buf, int len)
+void diag_send_error_rsp(unsigned char *buf, int len,
+ struct diag_md_session_t *info)
{
/* -1 to accommodate the first byte 0x13 */
if (len > (DIAG_MAX_RSP_SIZE - 1)) {
@@ -936,7 +980,7 @@
*(uint8_t *)driver->apps_rsp_buf = DIAG_CMD_ERROR;
memcpy((driver->apps_rsp_buf + sizeof(uint8_t)), buf, len);
- diag_send_rsp(driver->apps_rsp_buf, len + 1);
+ diag_send_rsp(driver->apps_rsp_buf, len + 1, info);
}
int diag_process_apps_pkt(unsigned char *buf, int len,
@@ -956,7 +1000,7 @@
/* Check if the command is a supported mask command */
mask_ret = diag_process_apps_masks(buf, len, info);
if (mask_ret > 0) {
- diag_send_rsp(driver->apps_rsp_buf, mask_ret);
+ diag_send_rsp(driver->apps_rsp_buf, mask_ret, info);
return 0;
}
@@ -978,7 +1022,7 @@
driver->apps_rsp_buf,
DIAG_MAX_RSP_SIZE);
if (write_len > 0)
- diag_send_rsp(driver->apps_rsp_buf, write_len);
+ diag_send_rsp(driver->apps_rsp_buf, write_len, info);
return 0;
}
@@ -988,13 +1032,15 @@
reg_item = container_of(temp_entry, struct diag_cmd_reg_t,
entry);
if (info) {
- if (MD_PERIPHERAL_MASK(reg_item->proc) &
- info->peripheral_mask)
+ if ((MD_PERIPHERAL_MASK(reg_item->proc) &
+ info->peripheral_mask) ||
+ (MD_PERIPHERAL_PD_MASK(reg_item->proc) &
+ info->peripheral_mask))
write_len = diag_send_data(reg_item, buf, len);
} else {
if (MD_PERIPHERAL_MASK(reg_item->proc) &
driver->logging_mask)
- diag_send_error_rsp(buf, len);
+ diag_send_error_rsp(buf, len, info);
else
write_len = diag_send_data(reg_item, buf, len);
}
@@ -1010,13 +1056,13 @@
for (i = 0; i < 4; i++)
*(driver->apps_rsp_buf+i) = *(buf+i);
*(uint32_t *)(driver->apps_rsp_buf+4) = DIAG_MAX_REQ_SIZE;
- diag_send_rsp(driver->apps_rsp_buf, 8);
+ diag_send_rsp(driver->apps_rsp_buf, 8, info);
return 0;
} else if ((*buf == 0x4b) && (*(buf+1) == 0x12) &&
(*(uint16_t *)(buf+2) == DIAG_DIAG_STM)) {
len = diag_process_stm_cmd(buf, driver->apps_rsp_buf);
if (len > 0) {
- diag_send_rsp(driver->apps_rsp_buf, len);
+ diag_send_rsp(driver->apps_rsp_buf, len, info);
return 0;
}
return len;
@@ -1029,7 +1075,7 @@
driver->apps_rsp_buf,
DIAG_MAX_RSP_SIZE);
if (write_len > 0)
- diag_send_rsp(driver->apps_rsp_buf, write_len);
+ diag_send_rsp(driver->apps_rsp_buf, write_len, info);
return 0;
}
/* Check for time sync switch command */
@@ -1040,7 +1086,7 @@
driver->apps_rsp_buf,
DIAG_MAX_RSP_SIZE);
if (write_len > 0)
- diag_send_rsp(driver->apps_rsp_buf, write_len);
+ diag_send_rsp(driver->apps_rsp_buf, write_len, info);
return 0;
}
/* Check for diag id command */
@@ -1051,14 +1097,14 @@
driver->apps_rsp_buf,
DIAG_MAX_RSP_SIZE);
if (write_len > 0)
- diag_send_rsp(driver->apps_rsp_buf, write_len);
+ diag_send_rsp(driver->apps_rsp_buf, write_len, info);
return 0;
}
/* Check for download command */
else if ((chk_apps_master()) && (*buf == 0x3A)) {
/* send response back */
driver->apps_rsp_buf[0] = *buf;
- diag_send_rsp(driver->apps_rsp_buf, 1);
+ diag_send_rsp(driver->apps_rsp_buf, 1, info);
msleep(5000);
/* call download API */
msm_set_restart_mode(RESTART_DLOAD);
@@ -1078,7 +1124,7 @@
for (i = 0; i < 13; i++)
driver->apps_rsp_buf[i+3] = 0;
- diag_send_rsp(driver->apps_rsp_buf, 16);
+ diag_send_rsp(driver->apps_rsp_buf, 16, info);
return 0;
}
}
@@ -1087,7 +1133,7 @@
(*(buf+2) == 0x04) && (*(buf+3) == 0x0)) {
memcpy(driver->apps_rsp_buf, buf, 4);
driver->apps_rsp_buf[4] = wrap_enabled;
- diag_send_rsp(driver->apps_rsp_buf, 5);
+ diag_send_rsp(driver->apps_rsp_buf, 5, info);
return 0;
}
/* Wrap the Delayed Rsp ID */
@@ -1096,7 +1142,7 @@
wrap_enabled = true;
memcpy(driver->apps_rsp_buf, buf, 4);
driver->apps_rsp_buf[4] = wrap_count;
- diag_send_rsp(driver->apps_rsp_buf, 6);
+ diag_send_rsp(driver->apps_rsp_buf, 6, info);
return 0;
}
/* Mobile ID Rsp */
@@ -1107,7 +1153,7 @@
driver->apps_rsp_buf,
DIAG_MAX_RSP_SIZE);
if (write_len > 0) {
- diag_send_rsp(driver->apps_rsp_buf, write_len);
+ diag_send_rsp(driver->apps_rsp_buf, write_len, info);
return 0;
}
}
@@ -1127,7 +1173,7 @@
for (i = 0; i < 55; i++)
driver->apps_rsp_buf[i] = 0;
- diag_send_rsp(driver->apps_rsp_buf, 55);
+ diag_send_rsp(driver->apps_rsp_buf, 55, info);
return 0;
}
/* respond to 0x7c command */
@@ -1140,14 +1186,14 @@
chk_config_get_id();
*(unsigned char *)(driver->apps_rsp_buf + 12) = '\0';
*(unsigned char *)(driver->apps_rsp_buf + 13) = '\0';
- diag_send_rsp(driver->apps_rsp_buf, 14);
+ diag_send_rsp(driver->apps_rsp_buf, 14, info);
return 0;
}
}
write_len = diag_cmd_chk_stats(buf, len, driver->apps_rsp_buf,
DIAG_MAX_RSP_SIZE);
if (write_len > 0) {
- diag_send_rsp(driver->apps_rsp_buf, write_len);
+ diag_send_rsp(driver->apps_rsp_buf, write_len, info);
return 0;
}
write_len = diag_cmd_disable_hdlc(buf, len, driver->apps_rsp_buf,
@@ -1159,7 +1205,7 @@
* before disabling HDLC encoding on Apps processor.
*/
mutex_lock(&driver->hdlc_disable_mutex);
- diag_send_rsp(driver->apps_rsp_buf, write_len);
+ diag_send_rsp(driver->apps_rsp_buf, write_len, info);
/*
* Set the value of hdlc_disabled after sending the response to
* the tools. This is required since the tools is expecting a
@@ -1179,7 +1225,7 @@
/* We have now come to the end of the function. */
if (chk_apps_only())
- diag_send_error_rsp(buf, len);
+ diag_send_error_rsp(buf, len, info);
return 0;
}
@@ -1262,7 +1308,7 @@
* recovery algorithm. Send an error response if the
* packet is not in expected format.
*/
- diag_send_error_rsp(driver->hdlc_buf, driver->hdlc_buf_len);
+ diag_send_error_rsp(driver->hdlc_buf, driver->hdlc_buf_len, info);
driver->hdlc_buf_len = 0;
end:
mutex_unlock(&driver->diag_hdlc_mutex);
@@ -1535,7 +1581,7 @@
if (actual_pkt->start != CONTROL_CHAR) {
diag_hdlc_start_recovery(buf, len, info);
- diag_send_error_rsp(buf, len);
+ diag_send_error_rsp(buf, len, info);
goto end;
}
mutex_lock(&driver->hdlc_recovery_mutex);
@@ -1625,15 +1671,14 @@
case TYPE_CMD:
if (peripheral >= 0 && peripheral < NUM_PERIPHERALS) {
diagfwd_write_done(peripheral, type, num);
- } else if (peripheral == APPS_DATA) {
+ }
+ if (peripheral == APPS_DATA ||
+ ctxt == DIAG_MEMORY_DEVICE_MODE) {
spin_lock_irqsave(&driver->rsp_buf_busy_lock, flags);
driver->rsp_buf_busy = 0;
driver->encoded_rsp_len = 0;
spin_unlock_irqrestore(&driver->rsp_buf_busy_lock,
flags);
- } else {
- pr_err_ratelimited("diag: Invalid peripheral %d in %s, type: %d\n",
- peripheral, __func__, type);
}
break;
default:
diff --git a/drivers/char/diag/diagfwd.h b/drivers/char/diag/diagfwd.h
index 677099f..0e0bf2d 100644
--- a/drivers/char/diag/diagfwd.h
+++ b/drivers/char/diag/diagfwd.h
@@ -47,7 +47,8 @@
void diag_update_sleeping_process(int process_id, int data_type);
int diag_process_apps_pkt(unsigned char *buf, int len,
struct diag_md_session_t *info);
-void diag_send_error_rsp(unsigned char *buf, int len);
+void diag_send_error_rsp(unsigned char *buf, int len,
+ struct diag_md_session_t *info);
void diag_update_pkt_buffer(unsigned char *buf, uint32_t len, int type);
int diag_process_stm_cmd(unsigned char *buf, unsigned char *dest_buf);
void diag_md_hdlc_reset_timer_func(unsigned long pid);
diff --git a/drivers/char/diag/diagfwd_cntl.c b/drivers/char/diag/diagfwd_cntl.c
index 710271e..d8c107e 100644
--- a/drivers/char/diag/diagfwd_cntl.c
+++ b/drivers/char/diag/diagfwd_cntl.c
@@ -111,6 +111,8 @@
{
int stat = 0;
struct siginfo info;
+ struct pid *pid_struct;
+ struct task_struct *result;
if (peripheral > NUM_PERIPHERALS)
return;
@@ -123,18 +125,38 @@
info.si_code = SI_QUEUE;
info.si_int = (PERIPHERAL_MASK(peripheral) | data);
info.si_signo = SIGCONT;
- if (driver->md_session_map[peripheral] &&
- driver->md_session_map[peripheral]->task) {
- if (driver->md_session_map[peripheral]->pid ==
- driver->md_session_map[peripheral]->task->tgid) {
+
+ if (!driver->md_session_map[peripheral] ||
+ driver->md_session_map[peripheral]->pid <= 0) {
+ pr_err("diag: md_session_map[%d] is invalid\n", peripheral);
+ mutex_unlock(&driver->md_session_lock);
+ return;
+ }
+
+ pid_struct = find_get_pid(
+ driver->md_session_map[peripheral]->pid);
+ DIAG_LOG(DIAG_DEBUG_PERIPHERALS,
+ "md_session_map[%d] pid = %d task = %pK\n",
+ peripheral,
+ driver->md_session_map[peripheral]->pid,
+ driver->md_session_map[peripheral]->task);
+
+ if (pid_struct) {
+ result = get_pid_task(pid_struct, PIDTYPE_PID);
+
+ if (!result) {
DIAG_LOG(DIAG_DEBUG_PERIPHERALS,
- "md_session %d pid = %d, md_session %d task tgid = %d\n",
+ "diag: md_session_map[%d] with pid = %d Exited..\n",
peripheral,
- driver->md_session_map[peripheral]->pid,
- peripheral,
- driver->md_session_map[peripheral]->task->tgid);
- stat = send_sig_info(info.si_signo, &info,
- driver->md_session_map[peripheral]->task);
+ driver->md_session_map[peripheral]->pid);
+ mutex_unlock(&driver->md_session_lock);
+ return;
+ }
+
+ if (driver->md_session_map[peripheral] &&
+ driver->md_session_map[peripheral]->task == result) {
+ stat = send_sig_info(info.si_signo,
+ &info, result);
if (stat)
pr_err("diag: Err sending signal to memory device client, signal data: 0x%x, stat: %d\n",
info.si_int, stat);
diff --git a/drivers/char/diag/diagfwd_peripheral.c b/drivers/char/diag/diagfwd_peripheral.c
index e00a61d..4d4b660 100644
--- a/drivers/char/diag/diagfwd_peripheral.c
+++ b/drivers/char/diag/diagfwd_peripheral.c
@@ -222,12 +222,22 @@
struct diagfwd_info *fwd_info = NULL;
peripheral = GET_BUF_PERIPHERAL(ctxt);
- if (peripheral < 0 || peripheral > NUM_PERIPHERALS)
+
+ /* Check for peripheral value within bounds
+ * of peripherals and UPD combined.
+ */
+ if (peripheral < 0 || peripheral > NUM_MD_SESSIONS)
return -EINVAL;
if (peripheral == APPS_DATA)
return peripheral;
+ /* With peripheral value bound checked
+ * return user pd value.
+ */
+ if (peripheral > NUM_PERIPHERALS)
+ return peripheral;
+
type = GET_BUF_TYPE(ctxt);
if (type < 0 || type >= NUM_TYPES)
return -EINVAL;
@@ -836,6 +846,7 @@
uint8_t peripheral;
uint8_t type;
struct diagfwd_info *fwd_info = NULL;
+ int transport = 0;
diag_socket_exit();
@@ -857,7 +868,10 @@
driver->diagfwd_dci_cmd[peripheral] = NULL;
}
- kfree(early_init_info);
+ for (transport = 0; transport < NUM_TRANSPORT; transport++) {
+ kfree(early_init_info[transport]);
+ early_init_info[transport] = NULL;
+ }
}
int diagfwd_cntl_register(uint8_t transport, uint8_t peripheral, void *ctxt,
diff --git a/drivers/clk/qcom/clk-cpu-osm.c b/drivers/clk/qcom/clk-cpu-osm.c
index 93a08db..ec4c83e 100644
--- a/drivers/clk/qcom/clk-cpu-osm.c
+++ b/drivers/clk/qcom/clk-cpu-osm.c
@@ -18,7 +18,6 @@
#include <linux/module.h>
#include <linux/init.h>
#include <linux/io.h>
-#include <linux/delay.h>
#include <linux/err.h>
#include <linux/errno.h>
#include <linux/clk.h>
@@ -27,20 +26,15 @@
#include <linux/platform_device.h>
#include <linux/of_platform.h>
#include <linux/pm_opp.h>
-#include <linux/pm_qos.h>
#include <linux/interrupt.h>
-#include <linux/regulator/driver.h>
-#include <linux/regmap.h>
#include <linux/uaccess.h>
#include <linux/sched.h>
#include <linux/cpufreq.h>
#include <linux/slab.h>
-#include <soc/qcom/scm.h>
#include <dt-bindings/clock/qcom,cpucc-sdm845.h>
#include "common.h"
#include "clk-regmap.h"
-#include "clk-rcg.h"
#include "clk-voter.h"
#include "clk-debug.h"
@@ -50,454 +44,74 @@
#define SINGLE_CORE 1
#define MAX_CLUSTER_CNT 3
#define MAX_MEM_ACC_VAL_PER_LEVEL 3
-#define MAX_CORE_COUNT 4
#define CORE_COUNT_VAL(val) ((val & GENMASK(18, 16)) >> 16)
-#define OSM_CYCLE_COUNTER_CTRL_REG 0x760
-#define OSM_CYCLE_COUNTER_USE_XO_EDGE_EN BIT(8)
-
#define OSM_REG_SIZE 32
-#define L3_EFUSE_SHIFT 29
-#define L3_EFUSE_MASK 0x7
-#define PWRCL_EFUSE_SHIFT 29
-#define PWRCL_EFUSE_MASK 0x7
-#define PERFCL_EFUSE_SHIFT 29
-#define PERFCL_EFUSE_MASK 0x7
-
#define ENABLE_REG 0x0
-#define ENABLE_OSM BIT(0)
#define FREQ_REG 0x110
#define VOLT_REG 0x114
-#define OVERRIDE_REG 0x118
-#define SPM_CC_INC_HYSTERESIS 0x1c
-#define SPM_CC_DEC_HYSTERESIS 0x20
-#define SPM_CORE_INACTIVE_MAPPING 0x28
-#define CC_ZERO_BEHAV_CTRL 0xc
-#define ENABLE_OVERRIDE BIT(0)
-#define SPM_CC_DCVS_DISABLE 0x24
-#define LLM_FREQ_VOTE_INC_HYSTERESIS 0x30
-#define LLM_FREQ_VOTE_DEC_HYSTERESIS 0x34
-#define LLM_INTF_DCVS_DISABLE 0x40
-#define LLM_VOLTAGE_VOTE_INC_HYSTERESIS 0x38
-#define LLM_VOLTAGE_VOTE_DEC_HYSTERESIS 0x3c
-#define VMIN_REDUCTION_ENABLE_REG 0x48
-#define VMIN_REDUCTION_TIMER_REG 0x4c
-#define PDN_FSM_CTRL_REG 0x54
-#define DELTA_DEX_VAL BVAL(31, 23, 0xa)
-#define IGNORE_PLL_LOCK BIT(15)
-#define CC_BOOST_FSM_EN BIT(0)
-#define CC_BOOST_FSM_TIMERS_REG0 0x58
-#define CC_BOOST_FSM_TIMERS_REG1 0x5c
-#define CC_BOOST_FSM_TIMERS_REG2 0x60
-#define DCVS_BOOST_FSM_EN_MASK BIT(2)
-#define DCVS_BOOST_FSM_TIMERS_REG0 0x64
-#define DCVS_BOOST_FSM_TIMERS_REG1 0x68
-#define DCVS_BOOST_FSM_TIMERS_REG2 0x6c
-#define PS_BOOST_FSM_EN_MASK BIT(1)
-#define PS_BOOST_FSM_TIMERS_REG0 0x74
-#define PS_BOOST_FSM_TIMERS_REG1 0x78
-#define PS_BOOST_FSM_TIMERS_REG2 0x7c
-#define BOOST_PROG_SYNC_DELAY_REG 0x80
-#define DCVS_DROOP_FSM_EN_MASK BIT(5)
-#define DROOP_PROG_SYNC_DELAY_REG 0x9c
-#define DROOP_RELEASE_TIMER_CTRL 0x88
-#define DROOP_CTRL_REG 0x84
-#define DCVS_DROOP_TIMER_CTRL 0x98
-#define PLL_SW_OVERRIDE_ENABLE 0xa0
-#define PLL_SW_OVERRIDE_DROOP_EN BIT(0)
-#define SPM_CORE_COUNT_CTRL 0x2c
#define CORE_DCVS_CTRL 0xbc
-#define OVERRIDE_CLUSTER_IDLE_ACK 0x800
-#define REQ_GEN_FSM_STATUS 0x70c
-
-#define PLL_MIN_LVAL 0x21
-#define PLL_MIN_FREQ_REG 0x94
-#define PLL_POST_DIV1 0x09
-#define PLL_POST_DIV2 0x109
-#define PLL_MODE 0x0
-#define PLL_L_VAL 0x4
-#define PLL_USER_CTRL 0xc
-#define PLL_CONFIG_CTL_LO 0x10
-#define PLL_CONFIG_CTL_HI 0x14
-#define MIN_VCO_VAL 0x2b
-
-#define MAX_VC 63
-#define MEM_ACC_LEVELS_LUT 2
-#define MAX_MEM_ACC_LEVELS 3
-#define MAX_MEM_ACC_VAL_PER_LEVEL 3
-#define MAX_MEM_ACC_VALUES (MAX_MEM_ACC_LEVELS * \
- MAX_MEM_ACC_VAL_PER_LEVEL)
-#define MEM_ACC_ADDRS 3
-
-#define ISENSE_ON_DATA 0xf
-#define ISENSE_OFF_DATA 0x0
-#define CONSTANT_32 0x20
-
-#define APM_MX_MODE 0x4100000
-#define APM_APC_MODE 0x4100002
-#define APM_READ_DATA_MASK 0xc
-#define APM_MX_MODE_VAL 0x4
-#define APM_APC_READ_VAL 0x8
-#define APM_MX_READ_VAL 0x4
-#define APM_CROSSOVER_VC 0xb0
-
-#define MEM_ACC_SEQ_CONST(n) (n)
-#define MEM_ACC_APM_READ_MASK 0xff
-#define MEMACC_CROSSOVER_VC 0xb8
-
-#define PLL_WAIT_LOCK_TIME_US 10
-#define PLL_WAIT_LOCK_TIME_NS (PLL_WAIT_LOCK_TIME_US * 1000)
-#define SAFE_FREQ_WAIT_NS 5000
-#define DEXT_DECREMENT_WAIT_NS 1000
-
-#define DATA_MEM(n) (0x400 + (n) * 4)
#define DCVS_PERF_STATE_DESIRED_REG_0_V1 0x780
#define DCVS_PERF_STATE_DESIRED_REG_0_V2 0x920
-#define DCVS_PERF_STATE_DESIRED_REG(n, v2) \
- (((v2) ? DCVS_PERF_STATE_DESIRED_REG_0_V2 \
- : DCVS_PERF_STATE_DESIRED_REG_0_V1) + 4 * (n))
+#define DCVS_PERF_STATE_DESIRED_REG(n, v1) \
+ (((v1) ? DCVS_PERF_STATE_DESIRED_REG_0_V1 \
+ : DCVS_PERF_STATE_DESIRED_REG_0_V2) + 4 * (n))
#define OSM_CYCLE_COUNTER_STATUS_REG_0_V1 0x7d0
#define OSM_CYCLE_COUNTER_STATUS_REG_0_V2 0x9c0
-#define OSM_CYCLE_COUNTER_STATUS_REG(n, v2) \
- (((v2) ? OSM_CYCLE_COUNTER_STATUS_REG_0_V2 \
- : OSM_CYCLE_COUNTER_STATUS_REG_0_V1) + 4 * (n))
-
-/* ACD registers */
-#define ACD_HW_VERSION 0x0
-#define ACDCR 0x4
-#define ACDTD 0x8
-#define ACDSSCR 0x28
-#define ACD_EXTINT_CFG 0x30
-#define ACD_DCVS_SW 0x34
-#define ACD_GFMUX_CFG 0x3c
-#define ACD_READOUT_CFG 0x48
-#define ACD_AVG_CFG_0 0x4c
-#define ACD_AVG_CFG_1 0x50
-#define ACD_AVG_CFG_2 0x54
-#define ACD_AUTOXFER_CFG 0x80
-#define ACD_AUTOXFER 0x84
-#define ACD_AUTOXFER_CTL 0x88
-#define ACD_AUTOXFER_STATUS 0x8c
-#define ACD_WRITE_CTL 0x90
-#define ACD_WRITE_STATUS 0x94
-#define ACD_READOUT 0x98
-
-#define ACD_MASTER_ONLY_REG_ADDR 0x80
-#define ACD_1P1_MAX_REG_OFFSET 0x100
-#define ACD_WRITE_CTL_UPDATE_EN BIT(0)
-#define ACD_WRITE_CTL_SELECT_SHIFT 1
-#define ACD_GFMUX_CFG_SELECT BIT(0)
-#define ACD_AUTOXFER_START_CLEAR 0
-#define ACD_AUTOXFER_START_SET 1
-#define AUTO_XFER_DONE_MASK BIT(0)
-#define ACD_DCVS_SW_DCVS_IN_PRGR_SET BIT(0)
-#define ACD_DCVS_SW_DCVS_IN_PRGR_CLEAR 0
-#define ACD_LOCAL_TRANSFER_TIMEOUT_NS 500
-
-#define ACD_REG_RELATIVE_ADDR(addr) (addr / 4)
-#define ACD_REG_RELATIVE_ADDR_BITMASK(addr) \
- (1 << (ACD_REG_RELATIVE_ADDR(addr)))
-
-static const struct regmap_config osm_qcom_regmap_config = {
- .reg_bits = 32,
- .reg_stride = 4,
- .val_bits = 32,
- .fast_io = true,
-};
-
-enum clk_osm_bases {
- OSM_BASE,
- PLL_BASE,
- EFUSE_BASE,
- SEQ_BASE,
- ACD_BASE,
- NUM_BASES,
-};
-
-enum clk_osm_lut_data {
- FREQ,
- FREQ_DATA,
- PLL_OVERRIDES,
- MEM_ACC_LEVEL,
- VIRTUAL_CORNER,
- NUM_FIELDS,
-};
+#define OSM_CYCLE_COUNTER_STATUS_REG(n, v1) \
+ (((v1) ? OSM_CYCLE_COUNTER_STATUS_REG_0_V1 \
+ : OSM_CYCLE_COUNTER_STATUS_REG_0_V2) + 4 * (n))
struct osm_entry {
u16 virtual_corner;
u16 open_loop_volt;
- u32 freq_data;
- u32 override_data;
- u32 mem_acc_level;
long frequency;
};
-static struct dentry *osm_debugfs_base;
-
struct clk_osm {
struct clk_hw hw;
struct osm_entry osm_table[OSM_TABLE_SIZE];
struct dentry *debugfs;
- struct regulator *vdd_reg;
- struct platform_device *vdd_dev;
- void *vbases[NUM_BASES];
- unsigned long pbases[NUM_BASES];
+ void __iomem *vbase;
+ phys_addr_t pbase;
spinlock_t lock;
-
+ bool per_core_dcvs;
u32 num_entries;
u32 cluster_num;
u32 core_num;
- u32 apm_crossover_vc;
- u32 apm_threshold_vc;
- u32 mem_acc_crossover_vc;
- u32 mem_acc_threshold_vc;
- u32 min_cpr_vc;
- u32 cycle_counter_reads;
- u32 cycle_counter_delay;
- u32 cycle_counter_factor;
u64 total_cycle_counter;
u32 prev_cycle_counter;
- u32 l_val_base;
- u32 apcs_pll_user_ctl;
- u32 apcs_pll_min_freq;
- u32 cfg_gfmux_addr;
- u32 apcs_cbc_addr;
- u32 speedbin;
- u32 mem_acc_crossover_vc_addr;
- u32 mem_acc_addr[MEM_ACC_ADDRS];
- u32 mem_acc_level_vc[MEM_ACC_LEVELS_LUT];
- u32 ramp_ctl_addr;
- u32 apm_mode_ctl;
- u32 apm_status_ctl;
- u32 osm_clk_rate;
- u32 xo_clk_rate;
- bool secure_init;
- bool per_core_dcvs;
- bool red_fsm_en;
- bool boost_fsm_en;
- bool safe_fsm_en;
- bool ps_fsm_en;
- bool droop_fsm_en;
-
- struct notifier_block panic_notifier;
- u32 trace_periodic_timer;
- bool trace_en;
- bool wdog_trace_en;
-
- bool acd_init;
- u32 acd_td;
- u32 acd_cr;
- u32 acd_sscr;
- u32 acd_extint0_cfg;
- u32 acd_extint1_cfg;
- u32 acd_autoxfer_ctl;
- u32 acd_debugfs_addr;
- bool acd_avg_init;
- u32 acd_avg_cfg0;
- u32 acd_avg_cfg1;
- u32 acd_avg_cfg2;
+ u32 max_core_count;
};
-static struct regulator *vdd_l3;
-static struct regulator *vdd_pwrcl;
-static struct regulator *vdd_perfcl;
-
-static inline int clk_osm_acd_mb(struct clk_osm *c)
-{
- return readl_relaxed_no_log((char *)c->vbases[ACD_BASE] +
- ACD_HW_VERSION);
-}
-
-static int clk_osm_acd_local_read_reg(struct clk_osm *c, u32 offset)
-{
- u32 reg = 0;
- int timeout;
-
- if (offset >= ACD_MASTER_ONLY_REG_ADDR) {
- pr_err("ACD register at offset=0x%x not locally readable\n",
- offset);
- return -EINVAL;
- }
-
- /* Set select field in read control register */
- writel_relaxed(ACD_REG_RELATIVE_ADDR(offset),
- (char *)c->vbases[ACD_BASE] + ACD_READOUT_CFG);
-
- /* Clear write control register */
- writel_relaxed(reg, (char *)c->vbases[ACD_BASE] + ACD_WRITE_CTL);
-
- /* Set select and update_en fields in write control register */
- reg = (ACD_REG_RELATIVE_ADDR(ACD_READOUT_CFG)
- << ACD_WRITE_CTL_SELECT_SHIFT)
- | ACD_WRITE_CTL_UPDATE_EN;
- writel_relaxed(reg, (char *)c->vbases[ACD_BASE] + ACD_WRITE_CTL);
-
- /* Ensure writes complete before polling */
- clk_osm_acd_mb(c);
-
- /* Poll write status register */
- for (timeout = ACD_LOCAL_TRANSFER_TIMEOUT_NS; timeout > 0;
- timeout -= 100) {
- reg = readl_relaxed((char *)c->vbases[ACD_BASE]
- + ACD_WRITE_STATUS);
- if ((reg & (ACD_REG_RELATIVE_ADDR_BITMASK(ACD_READOUT_CFG))))
- break;
- ndelay(100);
- }
-
- if (!timeout) {
- pr_err("local read timed out, offset=0x%x status=0x%x\n",
- offset, reg);
- return -ETIMEDOUT;
- }
-
- reg = readl_relaxed((char *)c->vbases[ACD_BASE] + ACD_READOUT);
- return reg;
-}
-
-static int clk_osm_acd_local_write_reg(struct clk_osm *c, u32 val, u32 offset)
-{
- u32 reg = 0;
- int timeout;
-
- if (offset >= ACD_MASTER_ONLY_REG_ADDR) {
- pr_err("ACD register at offset=0x%x not transferrable\n",
- offset);
- return -EINVAL;
- }
-
- /* Clear write control register */
- writel_relaxed(reg, (char *)c->vbases[ACD_BASE] + ACD_WRITE_CTL);
-
- /* Set select and update_en fields in write control register */
- reg = (ACD_REG_RELATIVE_ADDR(offset) << ACD_WRITE_CTL_SELECT_SHIFT)
- | ACD_WRITE_CTL_UPDATE_EN;
- writel_relaxed(reg, (char *)c->vbases[ACD_BASE] + ACD_WRITE_CTL);
-
- /* Ensure writes complete before polling */
- clk_osm_acd_mb(c);
-
- /* Poll write status register */
- for (timeout = ACD_LOCAL_TRANSFER_TIMEOUT_NS; timeout > 0;
- timeout -= 100) {
- reg = readl_relaxed((char *)c->vbases[ACD_BASE]
- + ACD_WRITE_STATUS);
- if ((reg & (ACD_REG_RELATIVE_ADDR_BITMASK(offset))))
- break;
- ndelay(100);
- }
-
- if (!timeout) {
- pr_err("local write timed out, offset=0x%x val=0x%x status=0x%x\n",
- offset, val, reg);
- return -ETIMEDOUT;
- }
-
- return 0;
-}
-
-static int clk_osm_acd_master_write_through_reg(struct clk_osm *c,
- u32 val, u32 offset)
-{
- writel_relaxed(val, (char *)c->vbases[ACD_BASE] + offset);
-
- /* Ensure writes complete before transfer to local copy */
- clk_osm_acd_mb(c);
-
- return clk_osm_acd_local_write_reg(c, val, offset);
-}
-
-static int clk_osm_acd_auto_local_write_reg(struct clk_osm *c, u32 mask)
-{
- u32 numregs, bitmask = mask;
- u32 reg = 0;
- int timeout;
-
- /* count number of bits set in register mask */
- for (numregs = 0; bitmask; numregs++)
- bitmask &= bitmask - 1;
-
- /* Program auto-transfer mask */
- writel_relaxed(mask, (char *)c->vbases[ACD_BASE] + ACD_AUTOXFER_CFG);
-
- /* Clear start field in auto-transfer register */
- writel_relaxed(ACD_AUTOXFER_START_CLEAR,
- (char *)c->vbases[ACD_BASE] + ACD_AUTOXFER);
-
- /* Set start field in auto-transfer register */
- writel_relaxed(ACD_AUTOXFER_START_SET,
- (char *)c->vbases[ACD_BASE] + ACD_AUTOXFER);
-
- /* Ensure writes complete before polling */
- clk_osm_acd_mb(c);
-
- /* Poll auto-transfer status register */
- for (timeout = ACD_LOCAL_TRANSFER_TIMEOUT_NS * numregs;
- timeout > 0; timeout -= 100) {
- reg = readl_relaxed((char *)c->vbases[ACD_BASE]
- + ACD_AUTOXFER_STATUS);
- if (reg & AUTO_XFER_DONE_MASK)
- break;
- ndelay(100);
- }
-
- if (!timeout) {
- pr_err("local register auto-transfer timed out, mask=0x%x registers=%d status=0x%x\n",
- mask, numregs, reg);
- return -ETIMEDOUT;
- }
-
- return 0;
-}
-
-static bool is_v2;
-static bool osm_tz_enabled;
+static bool is_sdm845v1;
static inline struct clk_osm *to_clk_osm(struct clk_hw *_hw)
{
return container_of(_hw, struct clk_osm, hw);
}
-static inline void clk_osm_masked_write_reg(struct clk_osm *c, u32 val,
- u32 offset, u32 mask)
+static inline void clk_osm_write_reg(struct clk_osm *c, u32 val, u32 offset)
{
- u32 val2, orig_val;
-
- val2 = orig_val = readl_relaxed((char *)c->vbases[OSM_BASE] + offset);
- val2 &= ~mask;
- val2 |= val & mask;
-
- if (val2 != orig_val)
- writel_relaxed(val2, (char *)c->vbases[OSM_BASE] + offset);
-}
-
-static inline void clk_osm_write_seq_reg(struct clk_osm *c, u32 val, u32 offset)
-{
- writel_relaxed(val, (char *)c->vbases[SEQ_BASE] + offset);
-}
-
-static inline void clk_osm_write_reg(struct clk_osm *c, u32 val, u32 offset,
- int base)
-{
- writel_relaxed(val, (char *)c->vbases[base] + offset);
+ writel_relaxed(val, c->vbase + offset);
}
static inline int clk_osm_read_reg(struct clk_osm *c, u32 offset)
{
- return readl_relaxed((char *)c->vbases[OSM_BASE] + offset);
+ return readl_relaxed(c->vbase + offset);
}
static inline int clk_osm_read_reg_no_log(struct clk_osm *c, u32 offset)
{
- return readl_relaxed_no_log((char *)c->vbases[OSM_BASE] + offset);
+ return readl_relaxed_no_log(c->vbase + offset);
}
-static inline int clk_osm_mb(struct clk_osm *c, int base)
+static inline int clk_osm_mb(struct clk_osm *c)
{
- return readl_relaxed_no_log((char *)c->vbases[base] + ENABLE_REG);
+ return readl_relaxed_no_log(c->vbase + ENABLE_REG);
}
static long clk_osm_list_rate(struct clk_hw *hw, unsigned int n,
@@ -559,23 +173,6 @@
return -EINVAL;
}
-static int clk_osm_enable(struct clk_hw *hw)
-{
- struct clk_osm *cpuclk = to_clk_osm(hw);
-
- clk_osm_masked_write_reg(cpuclk, ENABLE_OSM, ENABLE_REG, ENABLE_OSM);
-
- /* Make sure the write goes through before proceeding */
- clk_osm_mb(cpuclk, OSM_BASE);
-
- /* Wait for 5us for OSM hardware to enable */
- udelay(5);
-
- pr_debug("OSM clk enabled for cluster=%d\n", cpuclk->cluster_num);
-
- return 0;
-}
-
const struct clk_ops clk_ops_cpu_osm = {
.round_rate = clk_osm_round_rate,
.list_rate = clk_osm_list_rate,
@@ -608,11 +205,11 @@
}
pr_debug("rate: %lu --> index %d\n", rate, index);
- clk_osm_write_reg(cpuclk, index, DCVS_PERF_STATE_DESIRED_REG(0, is_v2),
- OSM_BASE);
+ clk_osm_write_reg(cpuclk, index,
+ DCVS_PERF_STATE_DESIRED_REG(0, is_sdm845v1));
/* Make sure the write goes through before proceeding */
- clk_osm_mb(cpuclk, OSM_BASE);
+ clk_osm_mb(cpuclk);
return 0;
}
@@ -626,7 +223,8 @@
if (!cpuclk)
return -EINVAL;
- index = clk_osm_read_reg(cpuclk, DCVS_PERF_STATE_DESIRED_REG(0, is_v2));
+ index = clk_osm_read_reg(cpuclk,
+ DCVS_PERF_STATE_DESIRED_REG(0, is_sdm845v1));
pr_debug("%s: Index %d, freq %ld\n", __func__, index,
cpuclk->osm_table[index].frequency);
@@ -637,7 +235,6 @@
static struct clk_ops clk_ops_l3_osm = {
- .enable = clk_osm_enable,
.round_rate = clk_osm_round_rate,
.list_rate = clk_osm_list_rate,
.recalc_rate = l3_clk_recalc_rate,
@@ -668,14 +265,17 @@
static struct clk_osm l3_clk = {
.cluster_num = 0,
+ .max_core_count = 4,
.hw.init = &osm_clks_init[0],
};
static DEFINE_CLK_VOTER(l3_cluster0_vote_clk, l3_clk, 0);
static DEFINE_CLK_VOTER(l3_cluster1_vote_clk, l3_clk, 0);
+static DEFINE_CLK_VOTER(l3_misc_vote_clk, l3_clk, 0);
static struct clk_osm pwrcl_clk = {
.cluster_num = 1,
+ .max_core_count = 4,
.hw.init = &osm_clks_init[1],
};
@@ -727,8 +327,33 @@
},
};
+static struct clk_osm cpu4_pwrcl_clk = {
+ .core_num = 4,
+ .total_cycle_counter = 0,
+ .prev_cycle_counter = 0,
+ .hw.init = &(struct clk_init_data){
+ .name = "cpu4_pwrcl_clk",
+ .parent_names = (const char *[]){ "pwrcl_clk" },
+ .num_parents = 1,
+ .ops = &clk_dummy_ops,
+ },
+};
+
+static struct clk_osm cpu5_pwrcl_clk = {
+ .core_num = 5,
+ .total_cycle_counter = 0,
+ .prev_cycle_counter = 0,
+ .hw.init = &(struct clk_init_data){
+ .name = "cpu5_pwrcl_clk",
+ .parent_names = (const char *[]){ "pwrcl_clk" },
+ .num_parents = 1,
+ .ops = &clk_dummy_ops,
+ },
+};
+
static struct clk_osm perfcl_clk = {
.cluster_num = 2,
+ .max_core_count = 4,
.hw.init = &osm_clks_init[2],
};
@@ -781,16 +406,11 @@
},
};
-/*
- * Use the cpu* clocks only for writing to the PERF_STATE_DESIRED registers.
- * Note that we are currently NOT programming the APSS_LMH_GFMUX_CFG &
- * APSS_OSM_GFMUX_CFG registers.
- */
-
static struct clk_hw *osm_qcom_clk_hws[] = {
[L3_CLK] = &l3_clk.hw,
[L3_CLUSTER0_VOTE_CLK] = &l3_cluster0_vote_clk.hw,
[L3_CLUSTER1_VOTE_CLK] = &l3_cluster1_vote_clk.hw,
+ [L3_MISC_VOTE_CLK] = &l3_misc_vote_clk.hw,
[PWRCL_CLK] = &pwrcl_clk.hw,
[CPU0_PWRCL_CLK] = &cpu0_pwrcl_clk.hw,
[CPU1_PWRCL_CLK] = &cpu1_pwrcl_clk.hw,
@@ -801,6 +421,19 @@
[CPU5_PERFCL_CLK] = &cpu5_perfcl_clk.hw,
[CPU6_PERFCL_CLK] = &cpu6_perfcl_clk.hw,
[CPU7_PERFCL_CLK] = &cpu7_perfcl_clk.hw,
+ [CPU4_PWRCL_CLK] = NULL,
+ [CPU5_PWRCL_CLK] = NULL,
+};
+
+static struct clk_osm *clk_cpu_map[] = {
+ &cpu0_pwrcl_clk,
+ &cpu1_pwrcl_clk,
+ &cpu2_pwrcl_clk,
+ &cpu3_pwrcl_clk,
+ &cpu4_perfcl_clk,
+ &cpu5_perfcl_clk,
+ &cpu6_perfcl_clk,
+ &cpu7_perfcl_clk,
};
static struct clk_osm *logical_cpu_to_clk(int cpu)
@@ -809,16 +442,6 @@
const u32 *cell;
u64 hwid;
static struct clk_osm *cpu_clk_map[NR_CPUS];
- struct clk_osm *clk_cpu_map[] = {
- &cpu0_pwrcl_clk,
- &cpu1_pwrcl_clk,
- &cpu2_pwrcl_clk,
- &cpu3_pwrcl_clk,
- &cpu4_perfcl_clk,
- &cpu5_perfcl_clk,
- &cpu6_perfcl_clk,
- &cpu7_perfcl_clk,
- };
if (!cpu_clk_map[cpu]) {
cpu_node = of_get_cpu_node(cpu, NULL);
@@ -894,11 +517,11 @@
static void
osm_set_index(struct clk_osm *c, unsigned int index, unsigned int num)
{
- clk_osm_write_reg(c, index, DCVS_PERF_STATE_DESIRED_REG(num, is_v2),
- OSM_BASE);
+ clk_osm_write_reg(c, index,
+ DCVS_PERF_STATE_DESIRED_REG(num, is_sdm845v1));
/* Make sure the write goes through before proceeding */
- clk_osm_mb(c, OSM_BASE);
+ clk_osm_mb(c);
}
static int
@@ -921,7 +544,7 @@
c = policy->driver_data;
index = clk_osm_read_reg(c,
- DCVS_PERF_STATE_DESIRED_REG(c->core_num, is_v2));
+ DCVS_PERF_STATE_DESIRED_REG(c->core_num, is_sdm845v1));
return policy->freq_table[index].frequency;
}
@@ -947,7 +570,7 @@
}
parent = to_clk_osm(p_hw);
- c->vbases[OSM_BASE] = parent->vbases[OSM_BASE];
+ c->vbase = parent->vbase;
p_hw = clk_hw_get_parent(p_hw);
if (!p_hw) {
@@ -975,7 +598,7 @@
table[i].frequency = xo_kHz * lval;
table[i].driver_data = table[i].frequency;
- if (core_count != MAX_CORE_COUNT)
+ if (core_count != parent->max_core_count)
table[i].frequency = CPUFREQ_ENTRY_INVALID;
/* Two of the same frequencies means end of table */
@@ -999,10 +622,6 @@
}
policy->driver_data = c;
-
- clk_osm_enable(&parent->hw);
- udelay(300);
-
return 0;
err:
@@ -1036,704 +655,6 @@
.boost_enabled = true,
};
-static inline int clk_osm_count_ns(struct clk_osm *c, u64 nsec)
-{
- u64 temp;
-
- temp = (u64)c->osm_clk_rate * nsec;
- do_div(temp, 1000000000);
-
- return temp;
-}
-
-static void clk_osm_program_mem_acc_regs(struct clk_osm *c)
-{
- if (c->secure_init) {
- clk_osm_write_seq_reg(c,
- c->pbases[OSM_BASE] + MEMACC_CROSSOVER_VC,
- DATA_MEM(57));
- clk_osm_write_seq_reg(c, c->mem_acc_addr[0], DATA_MEM(48));
- clk_osm_write_seq_reg(c, c->mem_acc_addr[1], DATA_MEM(49));
- clk_osm_write_seq_reg(c, c->mem_acc_addr[2], DATA_MEM(50));
- clk_osm_write_seq_reg(c, c->mem_acc_crossover_vc,
- DATA_MEM(78));
- clk_osm_write_seq_reg(c, c->mem_acc_level_vc[0], DATA_MEM(79));
- clk_osm_write_seq_reg(c, c->mem_acc_level_vc[1], DATA_MEM(80));
- /*
- * Note that DATA_MEM[81] -> DATA_MEM[89] values will be
- * confirmed post-si. Use a value of 1 for DATA_MEM[89] and
- * leave the rest of them as 0.
- */
- clk_osm_write_seq_reg(c, 1, DATA_MEM(89));
- } else {
- scm_io_write(c->pbases[SEQ_BASE] + DATA_MEM(78),
- c->mem_acc_crossover_vc);
- scm_io_write(c->pbases[SEQ_BASE] + DATA_MEM(79),
- c->mem_acc_level_vc[0]);
- scm_io_write(c->pbases[SEQ_BASE] + DATA_MEM(80),
- c->mem_acc_level_vc[1]);
- }
-}
-
-static void clk_osm_program_apm_regs(struct clk_osm *c)
-{
- if (c == &l3_clk || c == &pwrcl_clk)
- return;
-
- /*
- * Program address of the control register used to configure
- * the Array Power Mux controller
- */
- clk_osm_write_seq_reg(c, c->apm_mode_ctl, DATA_MEM(41));
-
- /* Program address of controller status register */
- clk_osm_write_seq_reg(c, c->apm_status_ctl, DATA_MEM(43));
-
- /* Program address of crossover register */
- clk_osm_write_seq_reg(c, c->pbases[OSM_BASE] + APM_CROSSOVER_VC,
- DATA_MEM(44));
-
- /* Program mode value to switch APM to VDD_APC */
- clk_osm_write_seq_reg(c, APM_APC_MODE, DATA_MEM(72));
-
- /* Program mode value to switch APM to VDD_MX */
- clk_osm_write_seq_reg(c, APM_MX_MODE, DATA_MEM(73));
-
- /* Program mask used to move into read_mask port */
- clk_osm_write_seq_reg(c, APM_READ_DATA_MASK, DATA_MEM(74));
-
- /* Value used to move into read_exp port */
- clk_osm_write_seq_reg(c, APM_APC_READ_VAL, DATA_MEM(75));
- clk_osm_write_seq_reg(c, APM_MX_READ_VAL, DATA_MEM(76));
-}
-
-static void clk_osm_do_additional_setup(struct clk_osm *c,
- struct platform_device *pdev)
-{
- if (!c->secure_init)
- return;
-
- dev_info(&pdev->dev, "Performing additional OSM setup due to lack of TZ for cluster=%d\n",
- c->cluster_num);
-
- /* PLL L_VAL & post-div programming */
- clk_osm_write_seq_reg(c, c->apcs_pll_min_freq, DATA_MEM(32));
- clk_osm_write_seq_reg(c, c->l_val_base, DATA_MEM(33));
- clk_osm_write_seq_reg(c, c->apcs_pll_user_ctl, DATA_MEM(34));
- clk_osm_write_seq_reg(c, PLL_POST_DIV1, DATA_MEM(35));
- clk_osm_write_seq_reg(c, PLL_POST_DIV2, DATA_MEM(36));
-
- /* APM Programming */
- clk_osm_program_apm_regs(c);
-
- /* GFMUX Programming */
- clk_osm_write_seq_reg(c, c->cfg_gfmux_addr, DATA_MEM(37));
- clk_osm_write_seq_reg(c, 0x1, DATA_MEM(65));
- clk_osm_write_seq_reg(c, 0x2, DATA_MEM(66));
- clk_osm_write_seq_reg(c, 0x3, DATA_MEM(67));
- clk_osm_write_seq_reg(c, 0x40000000, DATA_MEM(68));
- clk_osm_write_seq_reg(c, 0x20000000, DATA_MEM(69));
- clk_osm_write_seq_reg(c, 0x10000000, DATA_MEM(70));
- clk_osm_write_seq_reg(c, 0x70000000, DATA_MEM(71));
-
- /* Override programming */
- clk_osm_write_seq_reg(c, c->pbases[OSM_BASE] +
- OVERRIDE_CLUSTER_IDLE_ACK, DATA_MEM(54));
- clk_osm_write_seq_reg(c, 0x3, DATA_MEM(55));
- clk_osm_write_seq_reg(c, c->pbases[OSM_BASE] + PDN_FSM_CTRL_REG,
- DATA_MEM(40));
- clk_osm_write_seq_reg(c, c->pbases[OSM_BASE] + REQ_GEN_FSM_STATUS,
- DATA_MEM(60));
- clk_osm_write_seq_reg(c, 0x10, DATA_MEM(61));
- clk_osm_write_seq_reg(c, 0x70, DATA_MEM(62));
- clk_osm_write_seq_reg(c, c->apcs_cbc_addr, DATA_MEM(112));
- clk_osm_write_seq_reg(c, 0x2, DATA_MEM(113));
-
- if (c == &perfcl_clk) {
- int rc;
- u32 isense_addr;
-
- /* Performance cluster isense programming */
- rc = of_property_read_u32(pdev->dev.of_node,
- "qcom,perfcl-isense-addr", &isense_addr);
- if (rc) {
- dev_err(&pdev->dev, "unable to find qcom,perfcl-isense-addr property, rc=%d\n",
- rc);
- return;
- }
- clk_osm_write_seq_reg(c, isense_addr, DATA_MEM(45));
- clk_osm_write_seq_reg(c, ISENSE_ON_DATA, DATA_MEM(46));
- clk_osm_write_seq_reg(c, ISENSE_OFF_DATA, DATA_MEM(47));
- }
-
- clk_osm_write_seq_reg(c, c->ramp_ctl_addr, DATA_MEM(105));
- clk_osm_write_seq_reg(c, CONSTANT_32, DATA_MEM(92));
-
- /* Enable/disable CPR ramp settings */
- clk_osm_write_seq_reg(c, 0x101C031, DATA_MEM(106));
- clk_osm_write_seq_reg(c, 0x1010031, DATA_MEM(107));
-}
-
-static void clk_osm_setup_fsms(struct clk_osm *c)
-{
- u32 val;
-
- /* Voltage Reduction FSM */
- if (c->red_fsm_en) {
- val = clk_osm_read_reg(c, VMIN_REDUCTION_ENABLE_REG) | BIT(0);
- val |= BVAL(6, 1, c->min_cpr_vc);
- clk_osm_write_reg(c, val, VMIN_REDUCTION_ENABLE_REG,
- OSM_BASE);
-
- clk_osm_write_reg(c, clk_osm_count_ns(c, 10000),
- VMIN_REDUCTION_TIMER_REG, OSM_BASE);
- }
-
- /* Boost FSM */
- if (c->boost_fsm_en) {
- val = clk_osm_read_reg(c, PDN_FSM_CTRL_REG);
- val |= DELTA_DEX_VAL | CC_BOOST_FSM_EN | IGNORE_PLL_LOCK;
- clk_osm_write_reg(c, val, PDN_FSM_CTRL_REG, OSM_BASE);
-
- val = clk_osm_read_reg(c, CC_BOOST_FSM_TIMERS_REG0);
- val |= BVAL(15, 0, clk_osm_count_ns(c, PLL_WAIT_LOCK_TIME_NS));
- val |= BVAL(31, 16, clk_osm_count_ns(c, SAFE_FREQ_WAIT_NS));
- clk_osm_write_reg(c, val, CC_BOOST_FSM_TIMERS_REG0, OSM_BASE);
-
- val = clk_osm_read_reg(c, CC_BOOST_FSM_TIMERS_REG1);
- val |= BVAL(15, 0, clk_osm_count_ns(c, PLL_WAIT_LOCK_TIME_NS));
- val |= BVAL(31, 16, clk_osm_count_ns(c, PLL_WAIT_LOCK_TIME_NS));
- clk_osm_write_reg(c, val, CC_BOOST_FSM_TIMERS_REG1, OSM_BASE);
-
- val = clk_osm_read_reg(c, CC_BOOST_FSM_TIMERS_REG2);
- val |= BVAL(15, 0, clk_osm_count_ns(c, DEXT_DECREMENT_WAIT_NS));
- clk_osm_write_reg(c, val, CC_BOOST_FSM_TIMERS_REG2, OSM_BASE);
- }
-
- /* Safe Freq FSM */
- if (c->safe_fsm_en) {
- val = clk_osm_read_reg(c, PDN_FSM_CTRL_REG);
- clk_osm_write_reg(c, val | DCVS_BOOST_FSM_EN_MASK,
- PDN_FSM_CTRL_REG, OSM_BASE);
-
- val = clk_osm_read_reg(c, DCVS_BOOST_FSM_TIMERS_REG0);
- val |= BVAL(31, 16, clk_osm_count_ns(c, 1000));
- clk_osm_write_reg(c, val, DCVS_BOOST_FSM_TIMERS_REG0, OSM_BASE);
-
- val = clk_osm_read_reg(c, DCVS_BOOST_FSM_TIMERS_REG1);
- val |= BVAL(15, 0, clk_osm_count_ns(c, SAFE_FREQ_WAIT_NS));
- clk_osm_write_reg(c, val, DCVS_BOOST_FSM_TIMERS_REG1, OSM_BASE);
-
- val = clk_osm_read_reg(c, DCVS_BOOST_FSM_TIMERS_REG2);
- val |= BVAL(15, 0, clk_osm_count_ns(c, DEXT_DECREMENT_WAIT_NS));
- clk_osm_write_reg(c, val, DCVS_BOOST_FSM_TIMERS_REG2, OSM_BASE);
-
- }
-
- /* Pulse Swallowing FSM */
- if (c->ps_fsm_en) {
- val = clk_osm_read_reg(c, PDN_FSM_CTRL_REG);
- clk_osm_write_reg(c, val | PS_BOOST_FSM_EN_MASK,
- PDN_FSM_CTRL_REG, OSM_BASE);
-
- val = clk_osm_read_reg(c, PS_BOOST_FSM_TIMERS_REG0);
- val |= BVAL(15, 0, clk_osm_count_ns(c, SAFE_FREQ_WAIT_NS));
- val |= BVAL(31, 16, clk_osm_count_ns(c, 1000));
- clk_osm_write_reg(c, val, PS_BOOST_FSM_TIMERS_REG0, OSM_BASE);
-
- val = clk_osm_read_reg(c, PS_BOOST_FSM_TIMERS_REG1);
- val |= BVAL(15, 0, clk_osm_count_ns(c, SAFE_FREQ_WAIT_NS));
- val |= BVAL(31, 16, clk_osm_count_ns(c, 1000));
- clk_osm_write_reg(c, val, PS_BOOST_FSM_TIMERS_REG1, OSM_BASE);
-
- val = clk_osm_read_reg(c, PS_BOOST_FSM_TIMERS_REG2);
- val |= BVAL(15, 0, clk_osm_count_ns(c, DEXT_DECREMENT_WAIT_NS));
- clk_osm_write_reg(c, val, PS_BOOST_FSM_TIMERS_REG2, OSM_BASE);
- }
-
- /* PLL signal timing control */
- if (c->boost_fsm_en || c->safe_fsm_en || c->ps_fsm_en)
- clk_osm_write_reg(c, 0x2, BOOST_PROG_SYNC_DELAY_REG, OSM_BASE);
-
- /* DCVS droop FSM - only if RCGwRC is not used for di/dt control */
- if (c->droop_fsm_en) {
- val = clk_osm_read_reg(c, PDN_FSM_CTRL_REG);
- clk_osm_write_reg(c, val | DCVS_DROOP_FSM_EN_MASK,
- PDN_FSM_CTRL_REG, OSM_BASE);
- }
-
- if (c->ps_fsm_en || c->droop_fsm_en) {
- clk_osm_write_reg(c, 0x1, DROOP_PROG_SYNC_DELAY_REG, OSM_BASE);
- clk_osm_write_reg(c, clk_osm_count_ns(c, 100),
- DROOP_RELEASE_TIMER_CTRL, OSM_BASE);
- clk_osm_write_reg(c, clk_osm_count_ns(c, 150),
- DCVS_DROOP_TIMER_CTRL, OSM_BASE);
- /*
- * TODO: Check if DCVS_DROOP_CODE used is correct. Also check
- * if RESYNC_CTRL should be set for L3.
- */
- val = BIT(31) | BVAL(22, 16, 0x2) | BVAL(6, 0, 0x8);
- clk_osm_write_reg(c, val, DROOP_CTRL_REG, OSM_BASE);
- }
-}
-
-static int clk_osm_set_llm_volt_policy(struct platform_device *pdev)
-{
- struct device_node *of = pdev->dev.of_node;
- u32 *array;
- int rc = 0, val, regval;
-
- array = devm_kzalloc(&pdev->dev, MAX_CLUSTER_CNT * sizeof(u32),
- GFP_KERNEL);
- if (!array)
- return -ENOMEM;
-
- /*
- * Setup Timer to control how long OSM should wait before performing
- * DCVS when a LLM up voltage request is received.
- * Time is specified in us.
- */
- rc = of_property_read_u32_array(of, "qcom,llm-volt-up-timer",
- array, MAX_CLUSTER_CNT);
- if (rc) {
- dev_dbg(&pdev->dev, "No LLM voltage up timer value, rc=%d\n",
- rc);
- } else {
- val = clk_osm_count_ns(&l3_clk, array[l3_clk.cluster_num]);
- clk_osm_write_reg(&l3_clk, val,
- LLM_VOLTAGE_VOTE_INC_HYSTERESIS,
- OSM_BASE);
-
- val = clk_osm_count_ns(&pwrcl_clk,
- array[pwrcl_clk.cluster_num]);
- clk_osm_write_reg(&pwrcl_clk, val,
- LLM_VOLTAGE_VOTE_INC_HYSTERESIS,
- OSM_BASE);
-
- val = clk_osm_count_ns(&perfcl_clk,
- array[perfcl_clk.cluster_num]);
- clk_osm_write_reg(&perfcl_clk, val,
- LLM_VOLTAGE_VOTE_INC_HYSTERESIS,
- OSM_BASE);
- }
-
- /*
- * Setup Timer to control how long OSM should wait before performing
- * DCVS when a LLM down voltage request is received.
- * Time is specified in us.
- */
- rc = of_property_read_u32_array(of, "qcom,llm-volt-down-timer",
- array, MAX_CLUSTER_CNT);
- if (rc) {
- dev_dbg(&pdev->dev, "No LLM Voltage down timer value: %d\n",
- rc);
- } else {
- val = clk_osm_count_ns(&l3_clk, array[l3_clk.cluster_num]);
- clk_osm_write_reg(&l3_clk, val,
- LLM_VOLTAGE_VOTE_DEC_HYSTERESIS,
- OSM_BASE);
-
- val = clk_osm_count_ns(&pwrcl_clk,
- array[pwrcl_clk.cluster_num]);
- clk_osm_write_reg(&pwrcl_clk, val,
- LLM_VOLTAGE_VOTE_DEC_HYSTERESIS,
- OSM_BASE);
-
- val = clk_osm_count_ns(&perfcl_clk,
- array[perfcl_clk.cluster_num]);
- clk_osm_write_reg(&perfcl_clk, val,
- LLM_VOLTAGE_VOTE_DEC_HYSTERESIS,
- OSM_BASE);
- }
-
- /* Enable or disable honoring of LLM Voltage requests */
- rc = of_property_read_bool(pdev->dev.of_node,
- "qcom,enable-llm-volt-vote");
- if (rc) {
- dev_dbg(&pdev->dev, "Honoring LLM Voltage requests\n");
- val = 0;
- } else
- val = 1;
-
- /* Enable or disable LLM VOLT DVCS */
- regval = val | clk_osm_read_reg(&l3_clk, LLM_INTF_DCVS_DISABLE);
- clk_osm_write_reg(&l3_clk, regval, LLM_INTF_DCVS_DISABLE, OSM_BASE);
- regval = val | clk_osm_read_reg(&pwrcl_clk, LLM_INTF_DCVS_DISABLE);
- clk_osm_write_reg(&pwrcl_clk, regval, LLM_INTF_DCVS_DISABLE, OSM_BASE);
- regval = val | clk_osm_read_reg(&perfcl_clk, LLM_INTF_DCVS_DISABLE);
- clk_osm_write_reg(&perfcl_clk, regval, LLM_INTF_DCVS_DISABLE, OSM_BASE);
-
- /* Wait for the writes to complete */
- clk_osm_mb(&perfcl_clk, OSM_BASE);
-
- devm_kfree(&pdev->dev, array);
- return 0;
-}
-
-static int clk_osm_set_llm_freq_policy(struct platform_device *pdev)
-{
- struct device_node *of = pdev->dev.of_node;
- u32 *array;
- int rc = 0, val, regval;
-
- array = devm_kzalloc(&pdev->dev, MAX_CLUSTER_CNT * sizeof(u32),
- GFP_KERNEL);
- if (!array)
- return -ENOMEM;
-
- /*
- * Setup Timer to control how long OSM should wait before performing
- * DCVS when a LLM up frequency request is received.
- * Time is specified in us.
- */
- rc = of_property_read_u32_array(of, "qcom,llm-freq-up-timer", array,
- MAX_CLUSTER_CNT);
- if (rc) {
- dev_dbg(&pdev->dev, "Unable to get CC up timer value: %d\n",
- rc);
- } else {
- val = clk_osm_count_ns(&l3_clk, array[l3_clk.cluster_num]);
- clk_osm_write_reg(&l3_clk, val, LLM_FREQ_VOTE_INC_HYSTERESIS,
- OSM_BASE);
-
- val = clk_osm_count_ns(&pwrcl_clk,
- array[pwrcl_clk.cluster_num]);
- clk_osm_write_reg(&pwrcl_clk, val,
- LLM_FREQ_VOTE_INC_HYSTERESIS,
- OSM_BASE);
-
- val = clk_osm_count_ns(&perfcl_clk,
- array[perfcl_clk.cluster_num]);
- clk_osm_write_reg(&perfcl_clk, val,
- LLM_FREQ_VOTE_INC_HYSTERESIS,
- OSM_BASE);
- }
-
- /*
- * Setup Timer to control how long OSM should wait before performing
- * DCVS when a LLM down frequency request is received.
- * Time is specified in us.
- */
- rc = of_property_read_u32_array(of, "qcom,llm-freq-down-timer",
- array, MAX_CLUSTER_CNT);
- if (rc) {
- dev_dbg(&pdev->dev, "No LLM Frequency down timer value: %d\n",
- rc);
- } else {
- val = clk_osm_count_ns(&l3_clk, array[l3_clk.cluster_num]);
- clk_osm_write_reg(&l3_clk, val, LLM_FREQ_VOTE_DEC_HYSTERESIS,
- OSM_BASE);
-
- val = clk_osm_count_ns(&pwrcl_clk,
- array[pwrcl_clk.cluster_num]);
- clk_osm_write_reg(&pwrcl_clk, val,
- LLM_FREQ_VOTE_DEC_HYSTERESIS, OSM_BASE);
-
- val = clk_osm_count_ns(&perfcl_clk,
- array[perfcl_clk.cluster_num]);
- clk_osm_write_reg(&perfcl_clk, val,
- LLM_FREQ_VOTE_DEC_HYSTERESIS, OSM_BASE);
- }
-
- /* Enable or disable honoring of LLM frequency requests */
- rc = of_property_read_bool(pdev->dev.of_node,
- "qcom,enable-llm-freq-vote");
- if (rc) {
- dev_dbg(&pdev->dev, "Honoring LLM Frequency requests\n");
- val = 0;
- } else
- val = BIT(1);
-
- /* Enable or disable LLM FREQ DVCS */
- regval = val | clk_osm_read_reg(&l3_clk, LLM_INTF_DCVS_DISABLE);
- clk_osm_write_reg(&l3_clk, regval, LLM_INTF_DCVS_DISABLE, OSM_BASE);
- regval = val | clk_osm_read_reg(&pwrcl_clk, LLM_INTF_DCVS_DISABLE);
- clk_osm_write_reg(&pwrcl_clk, regval, LLM_INTF_DCVS_DISABLE, OSM_BASE);
- regval = val | clk_osm_read_reg(&perfcl_clk, LLM_INTF_DCVS_DISABLE);
- clk_osm_write_reg(&perfcl_clk, regval, LLM_INTF_DCVS_DISABLE, OSM_BASE);
-
- /* Wait for the write to complete */
- clk_osm_mb(&perfcl_clk, OSM_BASE);
-
- devm_kfree(&pdev->dev, array);
- return 0;
-}
-
-static int clk_osm_set_cc_policy(struct platform_device *pdev)
-{
- int rc = 0, val;
- u32 *array;
- struct device_node *of = pdev->dev.of_node;
-
- array = devm_kzalloc(&pdev->dev, MAX_CLUSTER_CNT * sizeof(u32),
- GFP_KERNEL);
- if (!array)
- return -ENOMEM;
-
- rc = of_property_read_u32_array(of, "qcom,up-timer", array,
- MAX_CLUSTER_CNT);
- if (rc) {
- dev_dbg(&pdev->dev, "No up timer value, rc=%d\n",
- rc);
- } else {
- val = clk_osm_count_ns(&l3_clk,
- array[l3_clk.cluster_num]);
- clk_osm_write_reg(&l3_clk, val, SPM_CC_INC_HYSTERESIS,
- OSM_BASE);
-
- val = clk_osm_count_ns(&pwrcl_clk,
- array[pwrcl_clk.cluster_num]);
- clk_osm_write_reg(&pwrcl_clk, val, SPM_CC_INC_HYSTERESIS,
- OSM_BASE);
-
- val = clk_osm_count_ns(&perfcl_clk,
- array[perfcl_clk.cluster_num]);
- clk_osm_write_reg(&perfcl_clk, val, SPM_CC_INC_HYSTERESIS,
- OSM_BASE);
- }
-
- rc = of_property_read_u32_array(of, "qcom,down-timer",
- array, MAX_CLUSTER_CNT);
- if (rc) {
- dev_dbg(&pdev->dev, "No down timer value, rc=%d\n", rc);
- } else {
- val = clk_osm_count_ns(&l3_clk,
- array[l3_clk.cluster_num]);
- clk_osm_write_reg(&l3_clk, val, SPM_CC_DEC_HYSTERESIS,
- OSM_BASE);
-
- val = clk_osm_count_ns(&pwrcl_clk,
- array[pwrcl_clk.cluster_num]);
- clk_osm_write_reg(&pwrcl_clk, val, SPM_CC_DEC_HYSTERESIS,
- OSM_BASE);
-
- clk_osm_count_ns(&perfcl_clk,
- array[perfcl_clk.cluster_num]);
- clk_osm_write_reg(&perfcl_clk, val, SPM_CC_DEC_HYSTERESIS,
- OSM_BASE);
- }
-
- /* OSM index override for cluster PC */
- rc = of_property_read_u32_array(of, "qcom,pc-override-index",
- array, MAX_CLUSTER_CNT);
- if (rc) {
- dev_dbg(&pdev->dev, "No PC override index value, rc=%d\n",
- rc);
- clk_osm_write_reg(&pwrcl_clk, 0, CC_ZERO_BEHAV_CTRL, OSM_BASE);
- clk_osm_write_reg(&perfcl_clk, 0, CC_ZERO_BEHAV_CTRL,
- OSM_BASE);
- } else {
- val = BVAL(6, 1, array[pwrcl_clk.cluster_num])
- | ENABLE_OVERRIDE;
- clk_osm_write_reg(&pwrcl_clk, val, CC_ZERO_BEHAV_CTRL,
- OSM_BASE);
- val = BVAL(6, 1, array[perfcl_clk.cluster_num])
- | ENABLE_OVERRIDE;
- clk_osm_write_reg(&perfcl_clk, val, CC_ZERO_BEHAV_CTRL,
- OSM_BASE);
- }
-
- /* Wait for the writes to complete */
- clk_osm_mb(&perfcl_clk, OSM_BASE);
-
- rc = of_property_read_bool(pdev->dev.of_node, "qcom,set-c3-active");
- if (rc) {
- dev_dbg(&pdev->dev, "Treat cores in C3 as active\n");
-
- val = clk_osm_read_reg(&l3_clk, SPM_CORE_INACTIVE_MAPPING);
- val &= ~BIT(2);
- clk_osm_write_reg(&l3_clk, val, SPM_CORE_INACTIVE_MAPPING,
- OSM_BASE);
-
- val = clk_osm_read_reg(&pwrcl_clk, SPM_CORE_INACTIVE_MAPPING);
- val &= ~BIT(2);
- clk_osm_write_reg(&pwrcl_clk, val, SPM_CORE_INACTIVE_MAPPING,
- OSM_BASE);
-
- val = clk_osm_read_reg(&perfcl_clk, SPM_CORE_INACTIVE_MAPPING);
- val &= ~BIT(2);
- clk_osm_write_reg(&perfcl_clk, val, SPM_CORE_INACTIVE_MAPPING,
- OSM_BASE);
- }
-
- rc = of_property_read_bool(pdev->dev.of_node, "qcom,set-c2-active");
- if (rc) {
- dev_dbg(&pdev->dev, "Treat cores in C2 as active\n");
-
- val = clk_osm_read_reg(&l3_clk, SPM_CORE_INACTIVE_MAPPING);
- val &= ~BIT(1);
- clk_osm_write_reg(&l3_clk, val, SPM_CORE_INACTIVE_MAPPING,
- OSM_BASE);
-
- val = clk_osm_read_reg(&pwrcl_clk, SPM_CORE_INACTIVE_MAPPING);
- val &= ~BIT(1);
- clk_osm_write_reg(&pwrcl_clk, val, SPM_CORE_INACTIVE_MAPPING,
- OSM_BASE);
-
- val = clk_osm_read_reg(&perfcl_clk, SPM_CORE_INACTIVE_MAPPING);
- val &= ~BIT(1);
- clk_osm_write_reg(&perfcl_clk, val, SPM_CORE_INACTIVE_MAPPING,
- OSM_BASE);
- }
-
- rc = of_property_read_bool(pdev->dev.of_node, "qcom,disable-cc-dvcs");
- if (rc) {
- dev_dbg(&pdev->dev, "Disabling CC based DCVS\n");
- val = 1;
- } else
- val = 0;
-
- clk_osm_write_reg(&l3_clk, val, SPM_CC_DCVS_DISABLE, OSM_BASE);
- clk_osm_write_reg(&pwrcl_clk, val, SPM_CC_DCVS_DISABLE, OSM_BASE);
- clk_osm_write_reg(&perfcl_clk, val, SPM_CC_DCVS_DISABLE, OSM_BASE);
-
- /* Wait for the writes to complete */
- clk_osm_mb(&perfcl_clk, OSM_BASE);
-
- devm_kfree(&pdev->dev, array);
- return 0;
-}
-
-static void clk_osm_setup_cluster_pll(struct clk_osm *c)
-{
- writel_relaxed(0x0, c->vbases[PLL_BASE] + PLL_MODE);
- writel_relaxed(0x26, c->vbases[PLL_BASE] + PLL_L_VAL);
- writel_relaxed(0x8, c->vbases[PLL_BASE] +
- PLL_USER_CTRL);
- writel_relaxed(0x20000AA8, c->vbases[PLL_BASE] +
- PLL_CONFIG_CTL_LO);
- writel_relaxed(0x000003D2, c->vbases[PLL_BASE] +
- PLL_CONFIG_CTL_HI);
- writel_relaxed(0x2, c->vbases[PLL_BASE] +
- PLL_MODE);
-
- /* Ensure writes complete before delaying */
- clk_osm_mb(c, PLL_BASE);
-
- udelay(PLL_WAIT_LOCK_TIME_US);
-
- writel_relaxed(0x6, c->vbases[PLL_BASE] + PLL_MODE);
-
- /* Ensure write completes before delaying */
- clk_osm_mb(c, PLL_BASE);
-
- usleep_range(50, 75);
-
- writel_relaxed(0x7, c->vbases[PLL_BASE] + PLL_MODE);
-}
-
-static void clk_osm_misc_programming(struct clk_osm *c)
-{
- u32 lval = 0xFF, val;
- int i;
-
- clk_osm_write_reg(c, BVAL(23, 16, 0xF), SPM_CORE_COUNT_CTRL,
- OSM_BASE);
- clk_osm_write_reg(c, PLL_MIN_LVAL, PLL_MIN_FREQ_REG, OSM_BASE);
-
- /* Pattern to set/clear PLL lock in PDN_FSM_CTRL_REG */
- val = clk_osm_read_reg(c, PDN_FSM_CTRL_REG);
- if (c->secure_init) {
- val |= IGNORE_PLL_LOCK;
- clk_osm_write_seq_reg(c, val, DATA_MEM(108));
- val &= ~IGNORE_PLL_LOCK;
- clk_osm_write_seq_reg(c, val, DATA_MEM(109));
- clk_osm_write_seq_reg(c, MIN_VCO_VAL, DATA_MEM(110));
- } else {
- val |= IGNORE_PLL_LOCK;
- scm_io_write(c->pbases[SEQ_BASE] + DATA_MEM(108), val);
- val &= ~IGNORE_PLL_LOCK;
- scm_io_write(c->pbases[SEQ_BASE] + DATA_MEM(109), val);
- }
-
- /* Program LVAL corresponding to first turbo VC */
- for (i = 0; i < c->num_entries; i++) {
- if (c->osm_table[i].virtual_corner ==
- c->mem_acc_level_vc[1]) {
- lval = c->osm_table[i].freq_data & GENMASK(7, 0);
- break;
- }
- }
-
- if (c->secure_init)
- clk_osm_write_seq_reg(c, lval, DATA_MEM(114));
- else
- scm_io_write(c->pbases[SEQ_BASE] + DATA_MEM(114), lval);
-
-}
-
-static int clk_osm_setup_hw_table(struct clk_osm *c)
-{
- struct osm_entry *entry = c->osm_table;
- int i;
- u32 freq_val = 0, volt_val = 0, override_val = 0;
- u32 table_entry_offset, last_mem_acc_level, last_virtual_corner = 0;
-
- for (i = 0; i < OSM_TABLE_SIZE; i++) {
- if (i < c->num_entries) {
- freq_val = entry[i].freq_data;
- volt_val = BVAL(27, 24, entry[i].mem_acc_level)
- | BVAL(21, 16, entry[i].virtual_corner)
- | BVAL(11, 0, entry[i].open_loop_volt);
- override_val = entry[i].override_data;
-
- if (last_virtual_corner && last_virtual_corner ==
- entry[i].virtual_corner && last_mem_acc_level !=
- entry[i].mem_acc_level) {
- pr_err("invalid LUT entry at row=%d virtual_corner=%d, mem_acc_level=%d\n",
- i, entry[i].virtual_corner,
- entry[i].mem_acc_level);
- return -EINVAL;
- }
- last_virtual_corner = entry[i].virtual_corner;
- last_mem_acc_level = entry[i].mem_acc_level;
- }
-
- table_entry_offset = i * OSM_REG_SIZE;
- clk_osm_write_reg(c, freq_val, FREQ_REG + table_entry_offset,
- OSM_BASE);
- clk_osm_write_reg(c, volt_val, VOLT_REG + table_entry_offset,
- OSM_BASE);
- clk_osm_write_reg(c, override_val, OVERRIDE_REG +
- table_entry_offset, OSM_BASE);
- }
-
- /* Make sure all writes go through */
- clk_osm_mb(c, OSM_BASE);
-
- return 0;
-}
-
-static void clk_osm_print_osm_table(struct clk_osm *c)
-{
- int i;
- struct osm_entry *table = c->osm_table;
- u32 pll_src, pll_div, lval, core_count;
-
- pr_debug("Index, Frequency, VC, OLV (mv), Core Count, PLL Src, PLL Div, L-Val, ACC Level\n");
- for (i = 0; i < c->num_entries; i++) {
- pll_src = (table[i].freq_data & GENMASK(31, 30)) >> 30;
- pll_div = (table[i].freq_data & GENMASK(29, 28)) >> 28;
- lval = table[i].freq_data & GENMASK(7, 0);
- core_count = (table[i].freq_data & GENMASK(18, 16)) >> 16;
-
- pr_debug("%3d, %11lu, %2u, %5u, %2u, %6u, %8u, %7u, %5u\n",
- i,
- table[i].frequency,
- table[i].virtual_corner,
- table[i].open_loop_volt,
- core_count,
- pll_src,
- pll_div,
- lval,
- table[i].mem_acc_level);
- }
- pr_debug("APM threshold corner=%d, crossover corner=%d\n",
- c->apm_threshold_vc, c->apm_crossover_vc);
- pr_debug("MEM-ACC threshold corner=%d, crossover corner=%d\n",
- c->mem_acc_threshold_vc, c->mem_acc_crossover_vc);
-}
-
static u32 find_voltage(struct clk_osm *c, unsigned long rate)
{
struct osm_entry *table = c->osm_table;
@@ -1749,12 +670,12 @@
return -EINVAL;
}
-static int add_opp(struct clk_osm *c, struct device *dev)
+static int add_opp(struct clk_osm *c, struct device **device_list, int count)
{
unsigned long rate = 0;
u32 uv;
long rc;
- int j = 0;
+ int i, j = 0;
unsigned long min_rate = c->hw.init->rate_max[0];
unsigned long max_rate =
c->hw.init->rate_max[c->hw.init->num_rate_max - 1];
@@ -1767,10 +688,12 @@
return -EINVAL;
}
- rc = dev_pm_opp_add(dev, rate, uv);
- if (rc) {
- pr_warn("failed to add OPP for %lu\n", rate);
- return rc;
+ for (i = 0; i < count; i++) {
+ rc = dev_pm_opp_add(device_list[i], rate, uv);
+ if (rc) {
+ pr_warn("failed to add OPP for %lu\n", rate);
+ return rc;
+ }
}
/*
@@ -1779,13 +702,18 @@
* this information will be used by thermal mitigation and the
* scheduler.
*/
- if (rate == min_rate)
- pr_info("Set OPP pair (%lu Hz, %d uv) on %s\n",
- rate, uv, dev_name(dev));
+ if (rate == min_rate) {
+ for (i = 0; i < count; i++) {
+ pr_info("Set OPP pair (%lu Hz, %d uv) on %s\n",
+ rate, uv, dev_name(device_list[i]));
+ }
+ }
if (rate == max_rate && max_rate != min_rate) {
- pr_info("Set OPP pair (%lu Hz, %d uv) on %s\n",
- rate, uv, dev_name(dev));
+ for (i = 0; i < count; i++) {
+ pr_info("Set OPP pair (%lu Hz, %d uv) on %s\n",
+ rate, uv, dev_name(device_list[i]));
+ }
break;
}
@@ -1795,14 +723,70 @@
return 0;
}
+static int derive_device_list(struct device **device_list,
+ struct device_node *np,
+ char *phandle_name, int count)
+{
+ int i;
+ struct platform_device *pdev;
+ struct device_node *dev_node;
+
+ for (i = 0; i < count; i++) {
+ dev_node = of_parse_phandle(np, phandle_name, i);
+ if (!dev_node) {
+ pr_err("Unable to get device_node pointer for opp-handle (%s)\n",
+ phandle_name);
+ return -ENODEV;
+ }
+
+ pdev = of_find_device_by_node(dev_node);
+ if (!pdev) {
+ pr_err("Unable to find platform_device node for opp-handle (%s)\n",
+ phandle_name);
+ return -ENODEV;
+ }
+ device_list[i] = &pdev->dev;
+ }
+ return 0;
+}
+
+static void populate_l3_opp_table(struct device_node *np, char *phandle_name)
+{
+ struct device **device_list;
+ int len, count, ret = 0;
+
+ if (of_find_property(np, phandle_name, &len)) {
+ count = len / sizeof(u32);
+
+ device_list = kcalloc(count, sizeof(struct device *),
+ GFP_KERNEL);
+ if (!device_list)
+ return;
+
+ ret = derive_device_list(device_list, np, phandle_name, count);
+ if (ret < 0) {
+ pr_err("Failed to fill device_list for %s\n",
+ phandle_name);
+ return;
+ }
+ } else {
+ pr_debug("Unable to find %s\n", phandle_name);
+ return;
+ }
+
+ if (add_opp(&l3_clk, device_list, count))
+ pr_err("Failed to add OPP levels for %s\n", phandle_name);
+
+ kfree(device_list);
+}
+
static void populate_opp_table(struct platform_device *pdev)
{
int cpu;
struct device *cpu_dev;
struct clk_osm *c, *parent;
struct clk_hw *hw_parent;
- struct device_node *l3_node_0, *l3_node_4;
- struct platform_device *l3_dev_0, *l3_dev_4;
+ struct device_node *np = pdev->dev.of_node;
for_each_possible_cpu(cpu) {
c = logical_cpu_to_clk(cpu);
@@ -1815,40 +799,12 @@
parent = to_clk_osm(hw_parent);
cpu_dev = get_cpu_device(cpu);
if (cpu_dev)
- if (add_opp(parent, cpu_dev))
+ if (add_opp(parent, &cpu_dev, 1))
pr_err("Failed to add OPP levels for %s\n",
dev_name(cpu_dev));
}
- l3_node_0 = of_parse_phandle(pdev->dev.of_node, "l3-dev0", 0);
- if (!l3_node_0) {
- pr_err("can't find the L3 cluster 0 dt node\n");
- return;
- }
-
- l3_dev_0 = of_find_device_by_node(l3_node_0);
- if (!l3_dev_0) {
- pr_err("can't find the L3 cluster 0 dt device\n");
- return;
- }
-
- if (add_opp(&l3_clk, &l3_dev_0->dev))
- pr_err("Failed to add OPP levels for L3 cluster 0\n");
-
- l3_node_4 = of_parse_phandle(pdev->dev.of_node, "l3-dev4", 0);
- if (!l3_node_4) {
- pr_err("can't find the L3 cluster 1 dt node\n");
- return;
- }
-
- l3_dev_4 = of_find_device_by_node(l3_node_4);
- if (!l3_dev_4) {
- pr_err("can't find the L3 cluster 1 dt device\n");
- return;
- }
-
- if (add_opp(&l3_clk, &l3_dev_4->dev))
- pr_err("Failed to add OPP levels for L3 cluster 1\n");
+ populate_l3_opp_table(np, "l3-devs");
}
static u64 clk_osm_get_cpu_cycle_counter(int cpu)
@@ -1873,7 +829,7 @@
*/
core_num = parent->per_core_dcvs ? c->core_num : 0;
val = clk_osm_read_reg_no_log(parent,
- OSM_CYCLE_COUNTER_STATUS_REG(core_num, is_v2));
+ OSM_CYCLE_COUNTER_STATUS_REG(core_num, is_sdm845v1));
if (val < c->prev_cycle_counter) {
/* Handle counter overflow */
@@ -1890,196 +846,6 @@
return cycle_counter_ret;
}
-static void clk_osm_setup_cycle_counters(struct clk_osm *c)
-{
- u32 ratio = c->osm_clk_rate;
- u32 val = 0;
-
- /* Enable cycle counter */
- val = BIT(0);
- /* Setup OSM clock to XO ratio */
- do_div(ratio, c->xo_clk_rate);
- val |= BVAL(5, 1, ratio - 1) | OSM_CYCLE_COUNTER_USE_XO_EDGE_EN;
-
- clk_osm_write_reg(c, val, OSM_CYCLE_COUNTER_CTRL_REG, OSM_BASE);
- pr_debug("OSM to XO clock ratio: %d\n", ratio);
-}
-
-static int clk_osm_resolve_crossover_corners(struct clk_osm *c,
- struct platform_device *pdev)
-{
- struct regulator *regulator = c->vdd_reg;
- int count, vc, i, memacc_threshold, apm_threshold;
- int rc = 0;
- u32 corner_volt;
-
- if (c == &l3_clk || c == &pwrcl_clk)
- return rc;
-
- rc = of_property_read_u32(pdev->dev.of_node,
- "qcom,perfcl-apcs-apm-threshold-voltage",
- &apm_threshold);
- if (rc) {
- pr_err("qcom,perfcl-apcs-apm-threshold-voltage property not specified\n");
- return rc;
- }
-
- rc = of_property_read_u32(pdev->dev.of_node,
- "qcom,perfcl-apcs-mem-acc-threshold-voltage",
- &memacc_threshold);
- if (rc) {
- pr_err("qcom,perfcl-apcs-mem-acc-threshold-voltage property not specified\n");
- return rc;
- }
-
- /*
- * Initialize VC settings in case none of them go above the voltage
- * limits
- */
- c->apm_threshold_vc = c->apm_crossover_vc = c->mem_acc_crossover_vc =
- c->mem_acc_threshold_vc = MAX_VC;
-
- count = regulator_count_voltages(regulator);
- if (count < 0) {
- pr_err("Failed to get the number of virtual corners supported\n");
- return count;
- }
-
- c->apm_crossover_vc = count - 2;
- c->mem_acc_crossover_vc = count - 1;
-
- for (i = 0; i < OSM_TABLE_SIZE; i++) {
- vc = c->osm_table[i].virtual_corner + 1;
- corner_volt = regulator_list_corner_voltage(regulator, vc);
-
- if (c->apm_threshold_vc == MAX_VC &&
- corner_volt >= apm_threshold)
- c->apm_threshold_vc = c->osm_table[i].virtual_corner;
-
- if (c->mem_acc_threshold_vc == MAX_VC &&
- corner_volt >= memacc_threshold)
- c->mem_acc_threshold_vc =
- c->osm_table[i].virtual_corner;
- }
-
- return rc;
-}
-
-static int clk_osm_resolve_open_loop_voltages(struct clk_osm *c)
-{
- struct regulator *regulator = c->vdd_reg;
- u32 vc, mv;
- int i;
-
- for (i = 0; i < OSM_TABLE_SIZE; i++) {
- vc = c->osm_table[i].virtual_corner + 1;
- /* Voltage is in uv. Convert to mv */
- mv = regulator_list_corner_voltage(regulator, vc) / 1000;
- c->osm_table[i].open_loop_volt = mv;
- }
-
- return 0;
-}
-
-static int clk_osm_get_lut(struct platform_device *pdev,
- struct clk_osm *c, char *prop_name)
-{
- struct device_node *of = pdev->dev.of_node;
- int prop_len, total_elems, num_rows, i, j, k;
- int rc = 0;
- u32 *array;
- u32 *fmax_temp;
- u32 data;
- unsigned long abs_fmax = 0;
- bool last_entry = false;
-
- if (!of_find_property(of, prop_name, &prop_len)) {
- dev_err(&pdev->dev, "missing %s\n", prop_name);
- return -EINVAL;
- }
-
- total_elems = prop_len / sizeof(u32);
- if (total_elems % NUM_FIELDS) {
- dev_err(&pdev->dev, "bad length %d\n", prop_len);
- return -EINVAL;
- }
-
- num_rows = total_elems / NUM_FIELDS;
-
- fmax_temp = devm_kzalloc(&pdev->dev, num_rows * sizeof(unsigned long),
- GFP_KERNEL);
- if (!fmax_temp)
- return -ENOMEM;
-
- array = devm_kzalloc(&pdev->dev, prop_len, GFP_KERNEL);
- if (!array)
- return -ENOMEM;
-
- rc = of_property_read_u32_array(of, prop_name, array, total_elems);
- if (rc) {
- dev_err(&pdev->dev, "Unable to parse OSM table, rc=%d\n", rc);
- goto exit;
- }
-
- pr_debug("%s: Entries in Table: %d\n", __func__, num_rows);
- c->num_entries = num_rows;
- if (c->num_entries > OSM_TABLE_SIZE) {
- pr_err("LUT entries %d exceed maximum size %d\n",
- c->num_entries, OSM_TABLE_SIZE);
- return -EINVAL;
- }
-
- for (i = 0, j = 0, k = 0; j < OSM_TABLE_SIZE; j++) {
- c->osm_table[j].frequency = array[i + FREQ];
- c->osm_table[j].freq_data = array[i + FREQ_DATA];
- c->osm_table[j].override_data = array[i + PLL_OVERRIDES];
- c->osm_table[j].mem_acc_level = array[i + MEM_ACC_LEVEL];
- /* Voltage corners are 0 based in the OSM LUT */
- c->osm_table[j].virtual_corner = array[i + VIRTUAL_CORNER] - 1;
- pr_debug("index=%d freq=%ld virtual_corner=%d freq_data=0x%x override_data=0x%x mem_acc_level=0x%x\n",
- j, c->osm_table[j].frequency,
- c->osm_table[j].virtual_corner,
- c->osm_table[j].freq_data,
- c->osm_table[j].override_data,
- c->osm_table[j].mem_acc_level);
-
- data = (array[i + FREQ_DATA] & GENMASK(29, 28)) >> 28;
- if (j && !c->min_cpr_vc && !data)
- c->min_cpr_vc = c->osm_table[j].virtual_corner;
-
- data = (array[i + FREQ_DATA] & GENMASK(18, 16)) >> 16;
- if (!last_entry && data == MAX_CORE_COUNT) {
- fmax_temp[k] = array[i];
- k++;
- }
-
- if (i < total_elems - NUM_FIELDS)
- i += NUM_FIELDS;
- else {
- abs_fmax = array[i];
- last_entry = true;
- }
- }
- fmax_temp[k++] = abs_fmax;
-
- osm_clks_init[c->cluster_num].rate_max = devm_kzalloc(&pdev->dev,
- k * sizeof(unsigned long),
- GFP_KERNEL);
- if (!osm_clks_init[c->cluster_num].rate_max) {
- rc = -ENOMEM;
- goto exit;
- }
-
- for (i = 0; i < k; i++)
- osm_clks_init[c->cluster_num].rate_max[i] = fmax_temp[i];
-
- osm_clks_init[c->cluster_num].num_rate_max = k;
-exit:
- devm_kfree(&pdev->dev, fmax_temp);
- devm_kfree(&pdev->dev, array);
- return rc;
-}
-
static int clk_osm_read_lut(struct platform_device *pdev, struct clk_osm *c)
{
u32 data, src, lval, i, j = OSM_TABLE_SIZE;
@@ -2123,494 +889,9 @@
return 0;
}
-static int clk_osm_parse_acd_dt_configs(struct platform_device *pdev)
-{
- struct device_node *of = pdev->dev.of_node;
- u32 *array;
- int rc = 0;
-
- array = devm_kzalloc(&pdev->dev, MAX_CLUSTER_CNT * sizeof(u32),
- GFP_KERNEL);
- if (!array)
- return -ENOMEM;
-
- l3_clk.acd_init = platform_get_resource_byname(pdev, IORESOURCE_MEM,
- "l3_acd") != NULL ? true : false;
- pwrcl_clk.acd_init = platform_get_resource_byname(pdev, IORESOURCE_MEM,
- "pwrcl_acd") != NULL ? true : false;
- perfcl_clk.acd_init = platform_get_resource_byname(pdev, IORESOURCE_MEM,
- "perfcl_acd") != NULL ? true : false;
-
- if (pwrcl_clk.acd_init || perfcl_clk.acd_init || l3_clk.acd_init) {
- rc = of_property_read_u32_array(of, "qcom,acdtd-val",
- array, MAX_CLUSTER_CNT);
- if (rc) {
- dev_err(&pdev->dev, "unable to find qcom,acdtd-val property, rc=%d\n",
- rc);
- return -EINVAL;
- }
-
- pwrcl_clk.acd_td = array[pwrcl_clk.cluster_num];
- perfcl_clk.acd_td = array[perfcl_clk.cluster_num];
- l3_clk.acd_td = array[l3_clk.cluster_num];
-
- rc = of_property_read_u32_array(of, "qcom,acdcr-val",
- array, MAX_CLUSTER_CNT);
- if (rc) {
- dev_err(&pdev->dev, "unable to find qcom,acdcr-val property, rc=%d\n",
- rc);
- return -EINVAL;
- }
-
- pwrcl_clk.acd_cr = array[pwrcl_clk.cluster_num];
- perfcl_clk.acd_cr = array[perfcl_clk.cluster_num];
- l3_clk.acd_cr = array[l3_clk.cluster_num];
-
- rc = of_property_read_u32_array(of, "qcom,acdsscr-val",
- array, MAX_CLUSTER_CNT);
- if (rc) {
- dev_err(&pdev->dev, "unable to find qcom,acdsscr-val property, rc=%d\n",
- rc);
- return -EINVAL;
- }
-
- pwrcl_clk.acd_sscr = array[pwrcl_clk.cluster_num];
- perfcl_clk.acd_sscr = array[perfcl_clk.cluster_num];
- l3_clk.acd_sscr = array[l3_clk.cluster_num];
-
- rc = of_property_read_u32_array(of, "qcom,acdextint0-val",
- array, MAX_CLUSTER_CNT);
- if (rc) {
- dev_err(&pdev->dev, "unable to find qcom,acdextint0-val property, rc=%d\n",
- rc);
- return -EINVAL;
- }
-
- pwrcl_clk.acd_extint0_cfg = array[pwrcl_clk.cluster_num];
- perfcl_clk.acd_extint0_cfg = array[perfcl_clk.cluster_num];
- l3_clk.acd_extint0_cfg = array[l3_clk.cluster_num];
-
- rc = of_property_read_u32_array(of, "qcom,acdextint1-val",
- array, MAX_CLUSTER_CNT);
- if (rc) {
- dev_err(&pdev->dev, "unable to find qcom,acdextint1-val property, rc=%d\n",
- rc);
- return -EINVAL;
- }
-
- pwrcl_clk.acd_extint1_cfg = array[pwrcl_clk.cluster_num];
- perfcl_clk.acd_extint1_cfg = array[perfcl_clk.cluster_num];
- l3_clk.acd_extint1_cfg = array[l3_clk.cluster_num];
-
- rc = of_property_read_u32_array(of, "qcom,acdautoxfer-val",
- array, MAX_CLUSTER_CNT);
- if (rc) {
- dev_err(&pdev->dev, "unable to find qcom,acdautoxfer-val property, rc=%d\n",
- rc);
- return -EINVAL;
- }
-
- pwrcl_clk.acd_autoxfer_ctl = array[pwrcl_clk.cluster_num];
- perfcl_clk.acd_autoxfer_ctl = array[perfcl_clk.cluster_num];
- l3_clk.acd_autoxfer_ctl = array[l3_clk.cluster_num];
-
- rc = of_property_read_u32_array(of, "qcom,acdavg-init",
- array, MAX_CLUSTER_CNT);
- if (rc) {
- dev_err(&pdev->dev, "unable to find qcom,acdavg-init property, rc=%d\n",
- rc);
- return -EINVAL;
- }
- pwrcl_clk.acd_avg_init = array[pwrcl_clk.cluster_num];
- perfcl_clk.acd_avg_init = array[perfcl_clk.cluster_num];
- l3_clk.acd_avg_init = array[l3_clk.cluster_num];
- }
-
- if (pwrcl_clk.acd_avg_init || perfcl_clk.acd_avg_init ||
- l3_clk.acd_avg_init) {
- rc = of_property_read_u32_array(of, "qcom,acdavgcfg0-val",
- array, MAX_CLUSTER_CNT);
- if (rc) {
- dev_err(&pdev->dev, "unable to find qcom,acdavgcfg0-val property, rc=%d\n",
- rc);
- return -EINVAL;
- }
- pwrcl_clk.acd_avg_cfg0 = array[pwrcl_clk.cluster_num];
- perfcl_clk.acd_avg_cfg0 = array[perfcl_clk.cluster_num];
- l3_clk.acd_avg_cfg0 = array[l3_clk.cluster_num];
-
- rc = of_property_read_u32_array(of, "qcom,acdavgcfg1-val",
- array, MAX_CLUSTER_CNT);
- if (rc) {
- dev_err(&pdev->dev, "unable to find qcom,acdavgcfg1-val property, rc=%d\n",
- rc);
- return -EINVAL;
- }
- pwrcl_clk.acd_avg_cfg1 = array[pwrcl_clk.cluster_num];
- perfcl_clk.acd_avg_cfg1 = array[perfcl_clk.cluster_num];
- l3_clk.acd_avg_cfg1 = array[l3_clk.cluster_num];
-
- rc = of_property_read_u32_array(of, "qcom,acdavgcfg2-val",
- array, MAX_CLUSTER_CNT);
- if (rc) {
- dev_err(&pdev->dev, "unable to find qcom,acdavgcfg2-val property, rc=%d\n",
- rc);
- return -EINVAL;
- }
- pwrcl_clk.acd_avg_cfg2 = array[pwrcl_clk.cluster_num];
- perfcl_clk.acd_avg_cfg2 = array[perfcl_clk.cluster_num];
- l3_clk.acd_avg_cfg2 = array[l3_clk.cluster_num];
- }
-
- devm_kfree(&pdev->dev, array);
- return rc;
-}
-
-static int clk_osm_parse_dt_configs(struct platform_device *pdev)
-{
- struct device_node *of = pdev->dev.of_node;
- u32 *array;
- char memacc_str[40];
- int rc = 0;
- struct resource *res;
-
- array = devm_kzalloc(&pdev->dev, MAX_CLUSTER_CNT * sizeof(u32),
- GFP_KERNEL);
- if (!array)
- return -ENOMEM;
-
- rc = of_property_read_u32_array(of, "qcom,l-val-base",
- array, MAX_CLUSTER_CNT);
- if (rc) {
- dev_err(&pdev->dev, "unable to find qcom,l-val-base property, rc=%d\n",
- rc);
- return -EINVAL;
- }
-
- l3_clk.l_val_base = array[l3_clk.cluster_num];
- pwrcl_clk.l_val_base = array[pwrcl_clk.cluster_num];
- perfcl_clk.l_val_base = array[perfcl_clk.cluster_num];
-
- rc = of_property_read_u32_array(of, "qcom,apcs-pll-user-ctl",
- array, MAX_CLUSTER_CNT);
- if (rc) {
- dev_err(&pdev->dev, "unable to find qcom,apcs-pll-user-ctl property, rc=%d\n",
- rc);
- return -EINVAL;
- }
-
- l3_clk.apcs_pll_user_ctl = array[l3_clk.cluster_num];
- pwrcl_clk.apcs_pll_user_ctl = array[pwrcl_clk.cluster_num];
- perfcl_clk.apcs_pll_user_ctl = array[perfcl_clk.cluster_num];
-
- rc = of_property_read_u32_array(of, "qcom,apcs-pll-min-freq",
- array, MAX_CLUSTER_CNT);
- if (rc) {
- dev_err(&pdev->dev, "unable to find qcom,apcs-pll-min-freq property, rc=%d\n",
- rc);
- return -EINVAL;
- }
-
- l3_clk.apcs_pll_min_freq = array[l3_clk.cluster_num];
- pwrcl_clk.apcs_pll_min_freq = array[pwrcl_clk.cluster_num];
- perfcl_clk.apcs_pll_min_freq = array[perfcl_clk.cluster_num];
-
- rc = of_property_read_u32_array(of, "qcom,apm-mode-ctl",
- array, MAX_CLUSTER_CNT);
- if (rc) {
- dev_err(&pdev->dev, "unable to find qcom,apm-mode-ctl property, rc=%d\n",
- rc);
- return -EINVAL;
- }
-
- l3_clk.apm_mode_ctl = array[l3_clk.cluster_num];
- pwrcl_clk.apm_mode_ctl = array[pwrcl_clk.cluster_num];
- perfcl_clk.apm_mode_ctl = array[perfcl_clk.cluster_num];
-
- rc = of_property_read_u32_array(of, "qcom,apm-status-ctrl",
- array, MAX_CLUSTER_CNT);
- if (rc) {
- dev_err(&pdev->dev, "unable to find qcom,apm-status-ctrl property, rc=%d\n",
- rc);
- return -EINVAL;
- }
-
- l3_clk.apm_status_ctl = array[l3_clk.cluster_num];
- pwrcl_clk.apm_status_ctl = array[pwrcl_clk.cluster_num];
- perfcl_clk.apm_status_ctl = array[perfcl_clk.cluster_num];
-
- rc = of_property_read_u32_array(of, "qcom,cfg-gfmux-addr",
- array, MAX_CLUSTER_CNT);
- if (rc) {
- dev_err(&pdev->dev, "unable to find qcom,cfg-gfmux-addr property, rc=%d\n",
- rc);
- return -EINVAL;
- }
-
- l3_clk.cfg_gfmux_addr = array[l3_clk.cluster_num];
- pwrcl_clk.cfg_gfmux_addr = array[pwrcl_clk.cluster_num];
- perfcl_clk.cfg_gfmux_addr = array[perfcl_clk.cluster_num];
-
- rc = of_property_read_u32_array(of, "qcom,apcs-cbc-addr",
- array, MAX_CLUSTER_CNT);
- if (rc) {
- dev_err(&pdev->dev, "unable to find qcom,apcs-cbc-addr property, rc=%d\n",
- rc);
- return -EINVAL;
- }
-
- l3_clk.apcs_cbc_addr = array[l3_clk.cluster_num];
- pwrcl_clk.apcs_cbc_addr = array[pwrcl_clk.cluster_num];
- perfcl_clk.apcs_cbc_addr = array[perfcl_clk.cluster_num];
-
- rc = of_property_read_u32_array(of, "qcom,apcs-ramp-ctl-addr",
- array, MAX_CLUSTER_CNT);
- if (rc) {
- dev_err(&pdev->dev, "unable to find qcom,apcs-ramp-ctl-addr property, rc=%d\n",
- rc);
- return -EINVAL;
- }
-
- l3_clk.ramp_ctl_addr = array[l3_clk.cluster_num];
- pwrcl_clk.ramp_ctl_addr = array[pwrcl_clk.cluster_num];
- perfcl_clk.ramp_ctl_addr = array[perfcl_clk.cluster_num];
-
- rc = of_property_read_u32(of, "qcom,xo-clk-rate",
- &pwrcl_clk.xo_clk_rate);
- if (rc) {
- dev_err(&pdev->dev, "unable to find qcom,xo-clk-rate property, rc=%d\n",
- rc);
- return -EINVAL;
- }
-
- l3_clk.xo_clk_rate = perfcl_clk.xo_clk_rate = pwrcl_clk.xo_clk_rate;
-
- rc = of_property_read_u32(of, "qcom,osm-clk-rate",
- &pwrcl_clk.osm_clk_rate);
- if (rc) {
- dev_err(&pdev->dev, "unable to find qcom,osm-clk-rate property, rc=%d\n",
- rc);
- return -EINVAL;
- }
- l3_clk.osm_clk_rate = perfcl_clk.osm_clk_rate = pwrcl_clk.osm_clk_rate;
-
- rc = of_property_read_u32(of, "qcom,cc-reads",
- &pwrcl_clk.cycle_counter_reads);
- if (rc) {
- dev_err(&pdev->dev, "unable to find qcom,cc-reads property, rc=%d\n",
- rc);
- return -EINVAL;
- }
- l3_clk.cycle_counter_reads = perfcl_clk.cycle_counter_reads =
- pwrcl_clk.cycle_counter_reads;
-
- rc = of_property_read_u32(of, "qcom,cc-delay",
- &pwrcl_clk.cycle_counter_delay);
- if (rc)
- dev_dbg(&pdev->dev, "no delays between cycle counter reads\n");
- else
- l3_clk.cycle_counter_delay = perfcl_clk.cycle_counter_delay =
- pwrcl_clk.cycle_counter_delay;
-
- rc = of_property_read_u32(of, "qcom,cc-factor",
- &pwrcl_clk.cycle_counter_factor);
- if (rc)
- dev_dbg(&pdev->dev, "no factor specified for cycle counter estimation\n");
- else
- l3_clk.cycle_counter_factor = perfcl_clk.cycle_counter_factor =
- pwrcl_clk.cycle_counter_factor;
-
- l3_clk.red_fsm_en = perfcl_clk.red_fsm_en = pwrcl_clk.red_fsm_en =
- of_property_read_bool(of, "qcom,red-fsm-en");
-
- l3_clk.boost_fsm_en = perfcl_clk.boost_fsm_en =
- pwrcl_clk.boost_fsm_en =
- of_property_read_bool(of, "qcom,boost-fsm-en");
-
- l3_clk.safe_fsm_en = perfcl_clk.safe_fsm_en = pwrcl_clk.safe_fsm_en =
- of_property_read_bool(of, "qcom,safe-fsm-en");
-
- l3_clk.ps_fsm_en = perfcl_clk.ps_fsm_en = pwrcl_clk.ps_fsm_en =
- of_property_read_bool(of, "qcom,ps-fsm-en");
-
- l3_clk.droop_fsm_en = perfcl_clk.droop_fsm_en =
- pwrcl_clk.droop_fsm_en =
- of_property_read_bool(of, "qcom,droop-fsm-en");
-
- devm_kfree(&pdev->dev, array);
-
- res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
- "l3_sequencer");
- if (!res) {
- dev_err(&pdev->dev,
- "Unable to get platform resource for l3_sequencer\n");
- return -ENOMEM;
- }
-
- l3_clk.pbases[SEQ_BASE] = (unsigned long)res->start;
- l3_clk.vbases[SEQ_BASE] = devm_ioremap(&pdev->dev, res->start,
- resource_size(res));
-
- if (!l3_clk.vbases[SEQ_BASE]) {
- dev_err(&pdev->dev, "Unable to map l3_sequencer base\n");
- return -ENOMEM;
- }
-
- res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
- "pwrcl_sequencer");
- if (!res) {
- dev_err(&pdev->dev,
- "Unable to get platform resource for pwrcl_sequencer\n");
- return -ENOMEM;
- }
-
- pwrcl_clk.pbases[SEQ_BASE] = (unsigned long)res->start;
- pwrcl_clk.vbases[SEQ_BASE] = devm_ioremap(&pdev->dev, res->start,
- resource_size(res));
-
- if (!pwrcl_clk.vbases[SEQ_BASE]) {
- dev_err(&pdev->dev, "Unable to map pwrcl_sequencer base\n");
- return -ENOMEM;
- }
-
- res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
- "perfcl_sequencer");
- if (!res) {
- dev_err(&pdev->dev,
- "Unable to get platform resource for perfcl_sequencer\n");
- return -ENOMEM;
- }
-
- perfcl_clk.pbases[SEQ_BASE] = (unsigned long)res->start;
- perfcl_clk.vbases[SEQ_BASE] = devm_ioremap(&pdev->dev, res->start,
- resource_size(res));
-
- if (!perfcl_clk.vbases[SEQ_BASE]) {
- dev_err(&pdev->dev, "Unable to map perfcl_sequencer base\n");
- return -ENOMEM;
- }
-
- snprintf(memacc_str, ARRAY_SIZE(memacc_str),
- "qcom,l3-memacc-level-vc-bin%d", l3_clk.speedbin);
- rc = of_property_read_u32_array(of, memacc_str, l3_clk.mem_acc_level_vc,
- MEM_ACC_LEVELS_LUT);
- if (rc) {
- dev_err(&pdev->dev, "unable to find %s property, rc=%d\n",
- memacc_str, rc);
- return rc;
- }
-
- snprintf(memacc_str, ARRAY_SIZE(memacc_str),
- "qcom,pwrcl-memacc-level-vc-bin%d", pwrcl_clk.speedbin);
- rc = of_property_read_u32_array(of, memacc_str,
- pwrcl_clk.mem_acc_level_vc, MEM_ACC_LEVELS_LUT);
- if (rc) {
- dev_err(&pdev->dev, "unable to find %s property, rc=%d\n",
- memacc_str, rc);
- return rc;
- }
-
- snprintf(memacc_str, ARRAY_SIZE(memacc_str),
- "qcom,perfcl-memacc-level-vc-bin%d", pwrcl_clk.speedbin);
- rc = of_property_read_u32_array(of, memacc_str,
- perfcl_clk.mem_acc_level_vc, MEM_ACC_LEVELS_LUT);
- if (rc) {
- dev_err(&pdev->dev, "unable to find %s property, rc=%d\n",
- memacc_str, rc);
- return rc;
- }
-
- l3_clk.secure_init = perfcl_clk.secure_init = pwrcl_clk.secure_init =
- of_property_read_bool(pdev->dev.of_node, "qcom,osm-no-tz");
-
- if (!pwrcl_clk.secure_init)
- return rc;
-
- rc = of_property_read_u32_array(of, "qcom,l3-mem-acc-addr",
- l3_clk.mem_acc_addr, MEM_ACC_ADDRS);
- if (rc) {
- dev_err(&pdev->dev, "unable to find qcom,l3-mem-acc-addr property, rc=%d\n",
- rc);
- return -EINVAL;
- }
-
- rc = of_property_read_u32_array(of, "qcom,pwrcl-mem-acc-addr",
- pwrcl_clk.mem_acc_addr, MEM_ACC_ADDRS);
- if (rc) {
- dev_err(&pdev->dev, "unable to find qcom,pwrcl-mem-acc-addr property, rc=%d\n",
- rc);
- return -EINVAL;
- }
-
- rc = of_property_read_u32_array(of, "qcom,perfcl-mem-acc-addr",
- perfcl_clk.mem_acc_addr, MEM_ACC_ADDRS);
- if (rc) {
- dev_err(&pdev->dev, "unable to find qcom,perfcl-mem-acc-addr property, rc=%d\n",
- rc);
- return -EINVAL;
- }
-
- return rc;
-}
-
-static int clk_osm_acd_resources_init(struct platform_device *pdev)
-{
- struct resource *res;
- unsigned long pbase;
- void *vbase;
- int rc = 0;
-
- res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
- "pwrcl_acd");
- if (res) {
- pbase = (unsigned long)res->start;
- vbase = devm_ioremap(&pdev->dev, res->start,
- resource_size(res));
- if (!vbase) {
- dev_err(&pdev->dev, "Unable to map pwrcl_acd base\n");
- return -ENOMEM;
- }
- pwrcl_clk.pbases[ACD_BASE] = pbase;
- pwrcl_clk.vbases[ACD_BASE] = vbase;
- }
-
- res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
- "perfcl_acd");
- if (res) {
- pbase = (unsigned long)res->start;
- vbase = devm_ioremap(&pdev->dev, res->start,
- resource_size(res));
- if (!vbase) {
- dev_err(&pdev->dev, "Unable to map perfcl_acd base\n");
- return -ENOMEM;
- }
- perfcl_clk.pbases[ACD_BASE] = pbase;
- perfcl_clk.vbases[ACD_BASE] = vbase;
- }
-
- res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
- "l3_acd");
- if (res) {
- pbase = (unsigned long)res->start;
- vbase = devm_ioremap(&pdev->dev, res->start,
- resource_size(res));
- if (!vbase) {
- dev_err(&pdev->dev, "Unable to map l3_acd base\n");
- return -ENOMEM;
- }
- l3_clk.pbases[ACD_BASE] = pbase;
- l3_clk.vbases[ACD_BASE] = vbase;
- }
- return rc;
-}
-
static int clk_osm_resources_init(struct platform_device *pdev)
{
- struct device_node *node;
struct resource *res;
- unsigned long pbase;
- int rc = 0;
- void *vbase;
res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
"osm_l3_base");
@@ -2620,21 +901,14 @@
return -ENOMEM;
}
- l3_clk.pbases[OSM_BASE] = (unsigned long)res->start;
- l3_clk.vbases[OSM_BASE] = devm_ioremap(&pdev->dev, res->start,
- resource_size(res));
+ l3_clk.pbase = (unsigned long)res->start;
+ l3_clk.vbase = devm_ioremap(&pdev->dev, res->start, resource_size(res));
- if (!l3_clk.vbases[OSM_BASE]) {
+ if (!l3_clk.vbase) {
dev_err(&pdev->dev, "Unable to map osm_l3_base base\n");
return -ENOMEM;
}
- /* Check if OSM has been enabled already by trustzone. */
- if (readl_relaxed(l3_clk.vbases[OSM_BASE] + ENABLE_REG) & ENABLE_OSM) {
- dev_info(&pdev->dev, "OSM has been initialized and enabled by TZ software\n");
- osm_tz_enabled = true;
- }
-
res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
"osm_pwrcl_base");
if (!res) {
@@ -2643,10 +917,10 @@
return -ENOMEM;
}
- pwrcl_clk.pbases[OSM_BASE] = (unsigned long)res->start;
- pwrcl_clk.vbases[OSM_BASE] = devm_ioremap(&pdev->dev, res->start,
+ pwrcl_clk.pbase = (unsigned long)res->start;
+ pwrcl_clk.vbase = devm_ioremap(&pdev->dev, res->start,
resource_size(res));
- if (!pwrcl_clk.vbases[OSM_BASE]) {
+ if (!pwrcl_clk.vbase) {
dev_err(&pdev->dev, "Unable to map osm_pwrcl_base base\n");
return -ENOMEM;
}
@@ -2659,394 +933,43 @@
return -ENOMEM;
}
- perfcl_clk.pbases[OSM_BASE] = (unsigned long)res->start;
- perfcl_clk.vbases[OSM_BASE] = devm_ioremap(&pdev->dev, res->start,
+ perfcl_clk.pbase = (unsigned long)res->start;
+ perfcl_clk.vbase = devm_ioremap(&pdev->dev, res->start,
resource_size(res));
- if (!perfcl_clk.vbases[OSM_BASE]) {
+ if (!perfcl_clk.vbase) {
dev_err(&pdev->dev, "Unable to map osm_perfcl_base base\n");
return -ENOMEM;
}
- if (osm_tz_enabled)
- return rc;
-
- res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "l3_pll");
- if (!res) {
- dev_err(&pdev->dev,
- "Unable to get platform resource for l3_pll\n");
- return -ENOMEM;
- }
- pbase = (unsigned long)res->start;
- vbase = devm_ioremap(&pdev->dev, res->start, resource_size(res));
-
- if (!vbase) {
- dev_err(&pdev->dev, "Unable to map l3_pll base\n");
- return -ENOMEM;
- }
-
- l3_clk.pbases[PLL_BASE] = pbase;
- l3_clk.vbases[PLL_BASE] = vbase;
-
- res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "pwrcl_pll");
- if (!res) {
- dev_err(&pdev->dev,
- "Unable to get platform resource for pwrcl_pll\n");
- return -ENOMEM;
- }
- pbase = (unsigned long)res->start;
- vbase = devm_ioremap(&pdev->dev, res->start, resource_size(res));
-
- if (!vbase) {
- dev_err(&pdev->dev, "Unable to map pwrcl_pll base\n");
- return -ENOMEM;
- }
-
- pwrcl_clk.pbases[PLL_BASE] = pbase;
- pwrcl_clk.vbases[PLL_BASE] = vbase;
-
- res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "perfcl_pll");
- if (!res) {
- dev_err(&pdev->dev,
- "Unable to get platform resource for perfcl_pll\n");
- return -ENOMEM;
- }
- pbase = (unsigned long)res->start;
- vbase = devm_ioremap(&pdev->dev, res->start, resource_size(res));
-
- if (!vbase) {
- dev_err(&pdev->dev, "Unable to map perfcl_pll base\n");
- return -ENOMEM;
- }
-
- perfcl_clk.pbases[PLL_BASE] = pbase;
- perfcl_clk.vbases[PLL_BASE] = vbase;
-
- /* efuse speed bin fuses are optional */
- res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
- "l3_efuse");
- if (res) {
- pbase = (unsigned long)res->start;
- vbase = devm_ioremap(&pdev->dev, res->start,
- resource_size(res));
- if (!vbase) {
- dev_err(&pdev->dev, "Unable to map in l3_efuse base\n");
- return -ENOMEM;
- }
- l3_clk.pbases[EFUSE_BASE] = pbase;
- l3_clk.vbases[EFUSE_BASE] = vbase;
- }
-
- res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
- "pwrcl_efuse");
- if (res) {
- pbase = (unsigned long)res->start;
- vbase = devm_ioremap(&pdev->dev, res->start,
- resource_size(res));
- if (!vbase) {
- dev_err(&pdev->dev, "Unable to map pwrcl_efuse base\n");
- return -ENOMEM;
- }
- pwrcl_clk.pbases[EFUSE_BASE] = pbase;
- pwrcl_clk.vbases[EFUSE_BASE] = vbase;
- }
-
- res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
- "perfcl_efuse");
- if (res) {
- pbase = (unsigned long)res->start;
- vbase = devm_ioremap(&pdev->dev, res->start,
- resource_size(res));
- if (!vbase) {
- dev_err(&pdev->dev, "Unable to map perfcl_efuse base\n");
- return -ENOMEM;
- }
- perfcl_clk.pbases[EFUSE_BASE] = pbase;
- perfcl_clk.vbases[EFUSE_BASE] = vbase;
- }
-
- vdd_l3 = devm_regulator_get(&pdev->dev, "vdd-l3");
- if (IS_ERR(vdd_l3)) {
- rc = PTR_ERR(vdd_l3);
- if (rc != -EPROBE_DEFER)
- dev_err(&pdev->dev, "Unable to get the l3 vreg, rc=%d\n",
- rc);
- return rc;
- }
- l3_clk.vdd_reg = vdd_l3;
-
- vdd_pwrcl = devm_regulator_get(&pdev->dev, "vdd-pwrcl");
- if (IS_ERR(vdd_pwrcl)) {
- rc = PTR_ERR(vdd_pwrcl);
- if (rc != -EPROBE_DEFER)
- dev_err(&pdev->dev, "Unable to get the pwrcl vreg, rc=%d\n",
- rc);
- return rc;
- }
- pwrcl_clk.vdd_reg = vdd_pwrcl;
-
- vdd_perfcl = devm_regulator_get(&pdev->dev, "vdd-perfcl");
- if (IS_ERR(vdd_perfcl)) {
- rc = PTR_ERR(vdd_perfcl);
- if (rc != -EPROBE_DEFER)
- dev_err(&pdev->dev, "Unable to get the perfcl vreg, rc=%d\n",
- rc);
- return rc;
- }
- perfcl_clk.vdd_reg = vdd_perfcl;
-
- node = of_parse_phandle(pdev->dev.of_node, "vdd-l3-supply", 0);
- if (!node) {
- pr_err("Unable to find vdd-l3-supply\n");
- return -EINVAL;
- }
-
- l3_clk.vdd_dev = of_find_device_by_node(node->parent->parent);
- if (!l3_clk.vdd_dev) {
- pr_err("Unable to find device for vdd-l3-supply node\n");
- return -EINVAL;
- }
-
- node = of_parse_phandle(pdev->dev.of_node, "vdd-pwrcl-supply", 0);
- if (!node) {
- pr_err("Unable to find vdd-pwrcl-supply\n");
- return -EINVAL;
- }
-
- pwrcl_clk.vdd_dev = of_find_device_by_node(node->parent->parent);
- if (!pwrcl_clk.vdd_dev) {
- pr_err("Unable to find device for vdd-pwrcl-supply node\n");
- return -EINVAL;
- }
-
- node = of_parse_phandle(pdev->dev.of_node, "vdd-perfcl-supply", 0);
- if (!node) {
- pr_err("Unable to find vdd-perfcl-supply\n");
- return -EINVAL;
- }
-
- perfcl_clk.vdd_dev = of_find_device_by_node(node->parent->parent);
- if (!perfcl_clk.vdd_dev) {
- pr_err("Unable to find device for vdd-perfcl-supply\n");
- return -EINVAL;
- }
-
return 0;
}
-static int debugfs_get_debug_reg(void *data, u64 *val)
+static void clk_cpu_osm_driver_sdm670_fixup(void)
{
- struct clk_osm *c = data;
+ osm_qcom_clk_hws[CPU4_PERFCL_CLK] = NULL;
+ osm_qcom_clk_hws[CPU5_PERFCL_CLK] = NULL;
+ osm_qcom_clk_hws[CPU4_PWRCL_CLK] = &cpu4_pwrcl_clk.hw;
+ osm_qcom_clk_hws[CPU5_PWRCL_CLK] = &cpu5_pwrcl_clk.hw;
- if (c->acd_debugfs_addr >= ACD_MASTER_ONLY_REG_ADDR)
- *val = readl_relaxed((char *)c->vbases[ACD_BASE] +
- c->acd_debugfs_addr);
- else
- *val = clk_osm_acd_local_read_reg(c, c->acd_debugfs_addr);
- return 0;
-}
+ clk_cpu_map[4] = &cpu4_pwrcl_clk;
+ clk_cpu_map[5] = &cpu5_pwrcl_clk;
-static int debugfs_set_debug_reg(void *data, u64 val)
-{
- struct clk_osm *c = data;
+ cpu6_perfcl_clk.core_num = 0;
+ cpu7_perfcl_clk.core_num = 1;
- if (c->acd_debugfs_addr >= ACD_MASTER_ONLY_REG_ADDR)
- clk_osm_write_reg(c, val, c->acd_debugfs_addr, ACD_BASE);
- else
- clk_osm_acd_master_write_through_reg(c, val,
- c->acd_debugfs_addr);
-
- return 0;
-}
-DEFINE_SIMPLE_ATTRIBUTE(debugfs_acd_debug_reg_fops,
- debugfs_get_debug_reg,
- debugfs_set_debug_reg,
- "0x%llx\n");
-
-static int debugfs_get_debug_reg_addr(void *data, u64 *val)
-{
- struct clk_osm *c = data;
-
- *val = c->acd_debugfs_addr;
- return 0;
-}
-
-static int debugfs_set_debug_reg_addr(void *data, u64 val)
-{
- struct clk_osm *c = data;
-
- if (val > ACD_1P1_MAX_REG_OFFSET) {
- pr_err("invalid ACD register address offset, must be between 0-0x%x\n",
- ACD_1P1_MAX_REG_OFFSET);
- return 0;
- }
-
- c->acd_debugfs_addr = val;
- return 0;
-}
-DEFINE_SIMPLE_ATTRIBUTE(debugfs_acd_debug_reg_addr_fops,
- debugfs_get_debug_reg_addr,
- debugfs_set_debug_reg_addr,
- "%llu\n");
-
-static void populate_debugfs_dir(struct clk_osm *c)
-{
- struct dentry *temp;
-
- if (osm_debugfs_base == NULL) {
- osm_debugfs_base = debugfs_create_dir("osm", NULL);
- if (IS_ERR_OR_NULL(osm_debugfs_base)) {
- pr_err("osm debugfs base directory creation failed\n");
- osm_debugfs_base = NULL;
- return;
- }
- }
-
- c->debugfs = debugfs_create_dir(clk_hw_get_name(&c->hw),
- osm_debugfs_base);
- if (IS_ERR_OR_NULL(c->debugfs)) {
- pr_err("osm debugfs directory creation failed\n");
- return;
- }
-
- temp = debugfs_create_file("acd_debug_reg",
- 0644,
- c->debugfs, c,
- &debugfs_acd_debug_reg_fops);
- if (IS_ERR_OR_NULL(temp)) {
- pr_err("debugfs_acd_debug_reg_fops debugfs file creation failed\n");
- goto exit;
- }
-
- temp = debugfs_create_file("acd_debug_reg_addr",
- 0644,
- c->debugfs, c,
- &debugfs_acd_debug_reg_addr_fops);
- if (IS_ERR_OR_NULL(temp)) {
- pr_err("debugfs_acd_debug_reg_addr_fops debugfs file creation failed\n");
- goto exit;
- }
-
-exit:
- if (IS_ERR_OR_NULL(temp))
- debugfs_remove_recursive(c->debugfs);
-}
-
-static int clk_osm_acd_init(struct clk_osm *c)
-{
-
- int rc = 0;
- u32 auto_xfer_mask = 0;
-
- if (c->secure_init) {
- clk_osm_write_reg(c, c->pbases[ACD_BASE] + ACDCR,
- DATA_MEM(115), OSM_BASE);
- clk_osm_write_reg(c, c->pbases[ACD_BASE] + ACD_WRITE_CTL,
- DATA_MEM(116), OSM_BASE);
- }
-
- if (!c->acd_init)
- return 0;
-
- c->acd_debugfs_addr = ACD_HW_VERSION;
-
- /* Program ACD tunable-length delay register */
- clk_osm_write_reg(c, c->acd_td, ACDTD, ACD_BASE);
- auto_xfer_mask |= ACD_REG_RELATIVE_ADDR_BITMASK(ACDTD);
-
- /* Program ACD control register */
- clk_osm_write_reg(c, c->acd_cr, ACDCR, ACD_BASE);
- auto_xfer_mask |= ACD_REG_RELATIVE_ADDR_BITMASK(ACDCR);
-
- /* Program ACD soft start control register */
- clk_osm_write_reg(c, c->acd_sscr, ACDSSCR, ACD_BASE);
- auto_xfer_mask |= ACD_REG_RELATIVE_ADDR_BITMASK(ACDSSCR);
-
- /* Program initial ACD external interface configuration register */
- clk_osm_write_reg(c, c->acd_extint0_cfg, ACD_EXTINT_CFG, ACD_BASE);
- auto_xfer_mask |= ACD_REG_RELATIVE_ADDR_BITMASK(ACD_EXTINT_CFG);
-
- /* Program ACD auto-register transfer control register */
- clk_osm_write_reg(c, c->acd_autoxfer_ctl, ACD_AUTOXFER_CTL, ACD_BASE);
-
- /* Ensure writes complete before transfers to local copy */
- clk_osm_acd_mb(c);
-
- /* Transfer master copies */
- rc = clk_osm_acd_auto_local_write_reg(c, auto_xfer_mask);
- if (rc)
- return rc;
-
- /* Switch CPUSS clock source to ACD clock */
- auto_xfer_mask |= ACD_REG_RELATIVE_ADDR_BITMASK(ACD_GFMUX_CFG);
- rc = clk_osm_acd_master_write_through_reg(c, ACD_GFMUX_CFG_SELECT,
- ACD_GFMUX_CFG);
- if (rc)
- return rc;
-
- /* Program ACD_DCVS_SW */
- rc = clk_osm_acd_master_write_through_reg(c,
- ACD_DCVS_SW_DCVS_IN_PRGR_SET,
- ACD_DCVS_SW);
- if (rc)
- return rc;
-
- rc = clk_osm_acd_master_write_through_reg(c,
- ACD_DCVS_SW_DCVS_IN_PRGR_CLEAR,
- ACD_DCVS_SW);
- if (rc)
- return rc;
-
- udelay(1);
-
- /* Program final ACD external interface configuration register */
- rc = clk_osm_acd_master_write_through_reg(c, c->acd_extint1_cfg,
- ACD_EXTINT_CFG);
- if (rc)
- return rc;
-
- if (c->acd_avg_init) {
- auto_xfer_mask |= ACD_REG_RELATIVE_ADDR_BITMASK(ACD_AVG_CFG_2);
- rc = clk_osm_acd_master_write_through_reg(c, c->acd_avg_cfg2,
- ACD_AVG_CFG_2);
- if (rc)
- return rc;
-
- auto_xfer_mask |= ACD_REG_RELATIVE_ADDR_BITMASK(ACD_AVG_CFG_1);
- rc = clk_osm_acd_master_write_through_reg(c, c->acd_avg_cfg1,
- ACD_AVG_CFG_1);
- if (rc)
- return rc;
-
- auto_xfer_mask |= ACD_REG_RELATIVE_ADDR_BITMASK(ACD_AVG_CFG_0);
- rc = clk_osm_acd_master_write_through_reg(c, c->acd_avg_cfg0,
- ACD_AVG_CFG_0);
- if (rc)
- return rc;
- }
-
- /*
- * ACDCR, ACDTD, ACDSSCR, ACD_EXTINT_CFG, ACD_GFMUX_CFG
- * must be copied from master to local copy on PC exit.
- * Also, ACD_AVG_CFG0, ACF_AVG_CFG1, and ACD_AVG_CFG2 when
- * AVG is enabled.
- */
- clk_osm_write_reg(c, auto_xfer_mask, ACD_AUTOXFER_CFG, ACD_BASE);
- return 0;
+ pwrcl_clk.max_core_count = 6;
+ perfcl_clk.max_core_count = 2;
}
static int clk_cpu_osm_driver_probe(struct platform_device *pdev)
{
int rc = 0, i;
- int pvs_ver = 0;
- u32 pte_efuse, val;
+ u32 val;
int num_clks = ARRAY_SIZE(osm_qcom_clk_hws);
struct clk *ext_xo_clk, *clk;
struct device *dev = &pdev->dev;
struct clk_onecell_data *clk_data;
- char l3speedbinstr[] = "qcom,l3-speedbin0-v0";
- char perfclspeedbinstr[] = "qcom,perfcl-speedbin0-v0";
- char pwrclspeedbinstr[] = "qcom,pwrcl-speedbin0-v0";
struct cpu_cycle_counter_cb cb = {
.get_cpu_cycle_counter = clk_osm_get_cpu_cycle_counter,
};
@@ -3062,8 +985,12 @@
return PTR_ERR(ext_xo_clk);
}
- is_v2 = of_device_is_compatible(pdev->dev.of_node,
- "qcom,clk-cpu-osm-v2");
+ is_sdm845v1 = of_device_is_compatible(pdev->dev.of_node,
+ "qcom,clk-cpu-osm");
+
+ if (of_device_is_compatible(pdev->dev.of_node,
+ "qcom,clk-cpu-osm-sdm670"))
+ clk_cpu_osm_driver_sdm670_fixup();
clk_data = devm_kzalloc(&pdev->dev, sizeof(struct clk_onecell_data),
GFP_KERNEL);
@@ -3085,282 +1012,34 @@
return rc;
}
- if (!osm_tz_enabled) {
- if (l3_clk.vbases[EFUSE_BASE]) {
- /* Multiple speed-bins are supported */
- pte_efuse = readl_relaxed(l3_clk.vbases[EFUSE_BASE]);
- l3_clk.speedbin = ((pte_efuse >> L3_EFUSE_SHIFT) &
- L3_EFUSE_MASK);
- snprintf(l3speedbinstr, ARRAY_SIZE(l3speedbinstr),
- "qcom,l3-speedbin%d-v%d", l3_clk.speedbin, pvs_ver);
- }
+ /* Check if per-core DCVS is enabled/not */
+ val = clk_osm_read_reg(&pwrcl_clk, CORE_DCVS_CTRL);
+ if (val && BIT(0))
+ pwrcl_clk.per_core_dcvs = true;
- dev_info(&pdev->dev, "using L3 speed bin %u and pvs_ver %d\n",
- l3_clk.speedbin, pvs_ver);
+ val = clk_osm_read_reg(&perfcl_clk, CORE_DCVS_CTRL);
+ if (val && BIT(0))
+ perfcl_clk.per_core_dcvs = true;
- rc = clk_osm_get_lut(pdev, &l3_clk, l3speedbinstr);
- if (rc) {
- dev_err(&pdev->dev, "Unable to get OSM LUT for L3, rc=%d\n",
- rc);
- return rc;
- }
+ rc = clk_osm_read_lut(pdev, &l3_clk);
+ if (rc) {
+ dev_err(&pdev->dev, "Unable to read OSM LUT for L3, rc=%d\n",
+ rc);
+ return rc;
+ }
- if (pwrcl_clk.vbases[EFUSE_BASE]) {
- /* Multiple speed-bins are supported */
- pte_efuse = readl_relaxed(pwrcl_clk.vbases[EFUSE_BASE]);
- pwrcl_clk.speedbin = ((pte_efuse >> PWRCL_EFUSE_SHIFT) &
- PWRCL_EFUSE_MASK);
- snprintf(pwrclspeedbinstr, ARRAY_SIZE(pwrclspeedbinstr),
- "qcom,pwrcl-speedbin%d-v%d", pwrcl_clk.speedbin,
- pvs_ver);
- }
+ rc = clk_osm_read_lut(pdev, &pwrcl_clk);
+ if (rc) {
+ dev_err(&pdev->dev, "Unable to read OSM LUT for power cluster, rc=%d\n",
+ rc);
+ return rc;
+ }
- dev_info(&pdev->dev, "using pwrcl speed bin %u and pvs_ver %d\n",
- pwrcl_clk.speedbin, pvs_ver);
-
- rc = clk_osm_get_lut(pdev, &pwrcl_clk, pwrclspeedbinstr);
- if (rc) {
- dev_err(&pdev->dev, "Unable to get OSM LUT for power cluster, rc=%d\n",
- rc);
- return rc;
- }
-
- if (perfcl_clk.vbases[EFUSE_BASE]) {
- /* Multiple speed-bins are supported */
- pte_efuse =
- readl_relaxed(perfcl_clk.vbases[EFUSE_BASE]);
- perfcl_clk.speedbin = ((pte_efuse >> PERFCL_EFUSE_SHIFT)
- & PERFCL_EFUSE_MASK);
- snprintf(perfclspeedbinstr,
- ARRAY_SIZE(perfclspeedbinstr),
- "qcom,perfcl-speedbin%d-v%d",
- perfcl_clk.speedbin, pvs_ver);
- }
-
- dev_info(&pdev->dev, "using perfcl speed bin %u and pvs_ver %d\n",
- perfcl_clk.speedbin, pvs_ver);
-
- rc = clk_osm_get_lut(pdev, &perfcl_clk, perfclspeedbinstr);
- if (rc) {
- dev_err(&pdev->dev, "Unable to get OSM LUT for perf cluster, rc=%d\n",
- rc);
- return rc;
- }
-
- rc = clk_osm_parse_dt_configs(pdev);
- if (rc) {
- dev_err(&pdev->dev, "Unable to parse OSM device tree configurations\n");
- return rc;
- }
-
- rc = clk_osm_parse_acd_dt_configs(pdev);
- if (rc) {
- dev_err(&pdev->dev, "Unable to parse ACD device tree configurations\n");
- return rc;
- }
-
- rc = clk_osm_acd_resources_init(pdev);
- if (rc) {
- dev_err(&pdev->dev, "ACD resources init failed, rc=%d\n",
- rc);
- return rc;
- }
-
- rc = clk_osm_resolve_open_loop_voltages(&l3_clk);
- if (rc) {
- if (rc == -EPROBE_DEFER)
- return rc;
- dev_err(&pdev->dev, "Unable to determine open-loop voltages for L3, rc=%d\n",
- rc);
- return rc;
- }
- rc = clk_osm_resolve_open_loop_voltages(&pwrcl_clk);
- if (rc) {
- if (rc == -EPROBE_DEFER)
- return rc;
- dev_err(&pdev->dev, "Unable to determine open-loop voltages for power cluster, rc=%d\n",
- rc);
- return rc;
- }
- rc = clk_osm_resolve_open_loop_voltages(&perfcl_clk);
- if (rc) {
- if (rc == -EPROBE_DEFER)
- return rc;
- dev_err(&pdev->dev, "Unable to determine open-loop voltages for perf cluster, rc=%d\n",
- rc);
- return rc;
- }
-
- rc = clk_osm_resolve_crossover_corners(&l3_clk, pdev);
- if (rc)
- dev_info(&pdev->dev,
- "No APM crossover corner programmed for L3\n");
- rc = clk_osm_resolve_crossover_corners(&pwrcl_clk, pdev);
- if (rc)
- dev_info(&pdev->dev,
- "No APM crossover corner programmed for pwrcl_clk\n");
- rc = clk_osm_resolve_crossover_corners(&perfcl_clk, pdev);
- if (rc)
- dev_info(&pdev->dev, "No MEM-ACC crossover corner programmed\n");
-
- clk_osm_setup_cycle_counters(&l3_clk);
- clk_osm_setup_cycle_counters(&pwrcl_clk);
- clk_osm_setup_cycle_counters(&perfcl_clk);
-
- clk_osm_print_osm_table(&l3_clk);
- clk_osm_print_osm_table(&pwrcl_clk);
- clk_osm_print_osm_table(&perfcl_clk);
-
- rc = clk_osm_setup_hw_table(&l3_clk);
- if (rc) {
- dev_err(&pdev->dev, "failed to setup l3 hardware table\n");
- goto exit;
- }
- rc = clk_osm_setup_hw_table(&pwrcl_clk);
- if (rc) {
- dev_err(&pdev->dev, "failed to setup power cluster hardware table\n");
- goto exit;
- }
- rc = clk_osm_setup_hw_table(&perfcl_clk);
- if (rc) {
- dev_err(&pdev->dev, "failed to setup perf cluster hardware table\n");
- goto exit;
- }
-
- /* Policy tuning */
- rc = clk_osm_set_cc_policy(pdev);
- if (rc < 0) {
- dev_err(&pdev->dev, "cc policy setup failed");
- goto exit;
- }
-
- /* LLM Freq Policy Tuning */
- rc = clk_osm_set_llm_freq_policy(pdev);
- if (rc < 0) {
- dev_err(&pdev->dev, "LLM Frequency Policy setup failed");
- goto exit;
- }
-
- /* LLM Voltage Policy Tuning */
- rc = clk_osm_set_llm_volt_policy(pdev);
- if (rc < 0) {
- dev_err(&pdev->dev, "Failed to set LLM voltage Policy");
- goto exit;
- }
-
- clk_osm_setup_fsms(&l3_clk);
- clk_osm_setup_fsms(&pwrcl_clk);
- clk_osm_setup_fsms(&perfcl_clk);
-
- /*
- * Program the VC at which the array power supply
- * needs to be switched.
- */
- clk_osm_write_reg(&perfcl_clk, perfcl_clk.apm_threshold_vc,
- APM_CROSSOVER_VC, OSM_BASE);
- if (perfcl_clk.secure_init) {
- clk_osm_write_seq_reg(&perfcl_clk,
- perfcl_clk.apm_crossover_vc, DATA_MEM(77));
- clk_osm_write_seq_reg(&perfcl_clk,
- (0x39 | (perfcl_clk.apm_threshold_vc << 6)),
- DATA_MEM(111));
- } else {
- scm_io_write(perfcl_clk.pbases[SEQ_BASE] + DATA_MEM(77),
- perfcl_clk.apm_crossover_vc);
- scm_io_write(perfcl_clk.pbases[SEQ_BASE] +
- DATA_MEM(111),
- (0x39 | (perfcl_clk.apm_threshold_vc << 6)));
- }
-
- /*
- * Perform typical secure-world HW initialization
- * as necessary.
- */
- clk_osm_do_additional_setup(&l3_clk, pdev);
- clk_osm_do_additional_setup(&pwrcl_clk, pdev);
- clk_osm_do_additional_setup(&perfcl_clk, pdev);
-
- /* MEM-ACC Programming */
- clk_osm_program_mem_acc_regs(&l3_clk);
- clk_osm_program_mem_acc_regs(&pwrcl_clk);
- clk_osm_program_mem_acc_regs(&perfcl_clk);
-
- if (of_property_read_bool(pdev->dev.of_node,
- "qcom,osm-pll-setup")) {
- clk_osm_setup_cluster_pll(&l3_clk);
- clk_osm_setup_cluster_pll(&pwrcl_clk);
- clk_osm_setup_cluster_pll(&perfcl_clk);
- }
-
- /* Misc programming */
- clk_osm_misc_programming(&l3_clk);
- clk_osm_misc_programming(&pwrcl_clk);
- clk_osm_misc_programming(&perfcl_clk);
-
- rc = clk_osm_acd_init(&l3_clk);
- if (rc) {
- pr_err("failed to initialize ACD for L3, rc=%d\n", rc);
- goto exit;
- }
- rc = clk_osm_acd_init(&pwrcl_clk);
- if (rc) {
- pr_err("failed to initialize ACD for pwrcl, rc=%d\n",
- rc);
- goto exit;
- }
- rc = clk_osm_acd_init(&perfcl_clk);
- if (rc) {
- pr_err("failed to initialize ACD for perfcl, rc=%d\n",
- rc);
- goto exit;
- }
-
- pwrcl_clk.per_core_dcvs = perfcl_clk.per_core_dcvs =
- of_property_read_bool(pdev->dev.of_node,
- "qcom,enable-per-core-dcvs");
- if (pwrcl_clk.per_core_dcvs) {
- val = clk_osm_read_reg(&pwrcl_clk, CORE_DCVS_CTRL);
- val |= BIT(0);
- clk_osm_write_reg(&pwrcl_clk, val, CORE_DCVS_CTRL,
- OSM_BASE);
- val = clk_osm_read_reg(&perfcl_clk, CORE_DCVS_CTRL);
- val |= BIT(0);
- clk_osm_write_reg(&perfcl_clk, val, CORE_DCVS_CTRL,
- OSM_BASE);
- }
- } else {
- /* OSM has been enabled already by trustzone */
- rc = clk_osm_read_lut(pdev, &l3_clk);
- if (rc) {
- dev_err(&pdev->dev, "Unable to read OSM LUT for L3, rc=%d\n",
- rc);
- return rc;
- }
-
- rc = clk_osm_read_lut(pdev, &pwrcl_clk);
- if (rc) {
- dev_err(&pdev->dev, "Unable to read OSM LUT for power cluster, rc=%d\n",
- rc);
- return rc;
- }
-
- rc = clk_osm_read_lut(pdev, &perfcl_clk);
- if (rc) {
- dev_err(&pdev->dev, "Unable to read OSM LUT for perf cluster, rc=%d\n",
- rc);
- return rc;
- }
-
- /* Check if per-core DCVS is enabled/not */
- val = clk_osm_read_reg(&pwrcl_clk, CORE_DCVS_CTRL);
- if (val && BIT(0))
- pwrcl_clk.per_core_dcvs = true;
-
- val = clk_osm_read_reg(&perfcl_clk, CORE_DCVS_CTRL);
- if (val && BIT(0))
- perfcl_clk.per_core_dcvs = true;
-
- clk_ops_l3_osm.enable = NULL;
+ rc = clk_osm_read_lut(pdev, &perfcl_clk);
+ if (rc) {
+ dev_err(&pdev->dev, "Unable to read OSM LUT for perf cluster, rc=%d\n",
+ rc);
+ return rc;
}
spin_lock_init(&l3_clk.lock);
@@ -3369,6 +1048,9 @@
/* Register OSM l3, pwr and perf clocks with Clock Framework */
for (i = 0; i < num_clks; i++) {
+ if (!osm_qcom_clk_hws[i])
+ continue;
+
clk = devm_clk_register(&pdev->dev, osm_qcom_clk_hws[i]);
if (IS_ERR(clk)) {
dev_err(&pdev->dev, "Unable to register CPU clock at index %d\n",
@@ -3387,34 +1069,12 @@
get_online_cpus();
- if (!osm_tz_enabled) {
- populate_debugfs_dir(&l3_clk);
- populate_debugfs_dir(&pwrcl_clk);
- populate_debugfs_dir(&perfcl_clk);
-
- /* Configure default rate to lowest frequency */
- for (i = 0; i < MAX_CORE_COUNT; i++) {
- osm_set_index(&pwrcl_clk, 0, i);
- osm_set_index(&perfcl_clk, 0, i);
- }
- }
- /*
- * Set the L3 clock to run off GPLL0 and enable OSM for the domain.
- * In the case that trustzone has already enabled OSM, bring the L3
- * clock rate to a safe level until the devfreq driver comes up and
- * votes for its desired frequency.
- */
- rc = clk_set_rate(l3_clk.hw.clk, OSM_INIT_RATE);
- if (rc) {
- dev_err(&pdev->dev, "Unable to set init rate on L3 cluster, rc=%d\n",
- rc);
- goto provider_err;
- }
WARN(clk_prepare_enable(l3_cluster0_vote_clk.hw.clk),
"clk: Failed to enable cluster0 clock for L3\n");
WARN(clk_prepare_enable(l3_cluster1_vote_clk.hw.clk),
"clk: Failed to enable cluster1 clock for L3\n");
- udelay(300);
+ WARN(clk_prepare_enable(l3_misc_vote_clk.hw.clk),
+ "clk: Failed to enable misc clock for L3\n");
populate_opp_table(pdev);
@@ -3443,6 +1103,7 @@
static const struct of_device_id match_table[] = {
{ .compatible = "qcom,clk-cpu-osm" },
{ .compatible = "qcom,clk-cpu-osm-v2" },
+ { .compatible = "qcom,clk-cpu-osm-sdm670" },
{}
};
diff --git a/drivers/clk/qcom/debugcc-sdm845.c b/drivers/clk/qcom/debugcc-sdm845.c
index be84b46..45bd556 100644
--- a/drivers/clk/qcom/debugcc-sdm845.c
+++ b/drivers/clk/qcom/debugcc-sdm845.c
@@ -247,7 +247,6 @@
"gcc_sdcc1_apps_clk",
"gcc_sdcc1_ice_core_clk",
"gpu_cc_acd_cxo_clk",
- "gpu_cc_ahb_clk",
"gpu_cc_crc_ahb_clk",
"gpu_cc_cx_apb_clk",
"gpu_cc_cx_gfx3d_clk",
@@ -720,8 +719,6 @@
0x42, 0x3FF, 0, 0xF, 0, 4, 0x62008, 0x62000, 0x62004 },
{ "gpu_cc_acd_cxo_clk", 0x144, 4, GPU_CC,
0x1F, 0xFF, 0, 0x3, 0, 1, 0x1568, 0x10FC, 0x1100 },
- { "gpu_cc_ahb_clk", 0x144, 4, GPU_CC,
- 0x11, 0xFF, 0, 0x3, 0, 1, 0x1568, 0x10FC, 0x1100 },
{ "gpu_cc_crc_ahb_clk", 0x144, 4, GPU_CC,
0x12, 0xFF, 0, 0x3, 0, 1, 0x1568, 0x10FC, 0x1100 },
{ "gpu_cc_cx_apb_clk", 0x144, 4, GPU_CC,
diff --git a/drivers/clk/qcom/gcc-sdm845.c b/drivers/clk/qcom/gcc-sdm845.c
index 4142dd5..555b8bd 100644
--- a/drivers/clk/qcom/gcc-sdm845.c
+++ b/drivers/clk/qcom/gcc-sdm845.c
@@ -36,9 +36,6 @@
#include "clk-alpha-pll.h"
#include "vdd-level-sdm845.h"
-#define GCC_APCS_CLOCK_SLEEP_ENA_VOTE_OFFSET 0x52008
-#define CPUSS_AHB_CLK_SLEEP_ENA BIT(21)
-#define SYS_NOC_CPUSS_AHB_CLK_SLEEP_ENA BIT(0)
#define GCC_MMSS_MISC 0x09FFC
#define GCC_GPU_MISC 0x71028
@@ -4071,6 +4068,7 @@
static const struct of_device_id gcc_sdm845_match_table[] = {
{ .compatible = "qcom,gcc-sdm845" },
{ .compatible = "qcom,gcc-sdm845-v2" },
+ { .compatible = "qcom,gcc-sdm845-v2.1" },
{ .compatible = "qcom,gcc-sdm670" },
{ }
};
@@ -4270,7 +4268,8 @@
if (!compat || (compatlen <= 0))
return -EINVAL;
- if (!strcmp(compat, "qcom,gcc-sdm845-v2"))
+ if (!strcmp(compat, "qcom,gcc-sdm845-v2") ||
+ !strcmp(compat, "qcom,gcc-sdm845-v2.1"))
gcc_sdm845_fixup_sdm845v2();
else if (!strcmp(compat, "qcom,gcc-sdm670"))
gcc_sdm845_fixup_sdm670();
@@ -4288,14 +4287,6 @@
if (IS_ERR(regmap))
return PTR_ERR(regmap);
- /*
- * Set the *_SLEEP_ENA bits to allow certain cpuss* clocks to be
- * turned off by hardware during certain apps low power modes.
- */
- regmap_update_bits(regmap, GCC_APCS_CLOCK_SLEEP_ENA_VOTE_OFFSET,
- CPUSS_AHB_CLK_SLEEP_ENA | SYS_NOC_CPUSS_AHB_CLK_SLEEP_ENA,
- CPUSS_AHB_CLK_SLEEP_ENA | SYS_NOC_CPUSS_AHB_CLK_SLEEP_ENA);
-
vdd_cx.regulator[0] = devm_regulator_get(&pdev->dev, "vdd_cx");
if (IS_ERR(vdd_cx.regulator[0])) {
if (!(PTR_ERR(vdd_cx.regulator[0]) == -EPROBE_DEFER))
@@ -4333,8 +4324,8 @@
regmap_update_bits(regmap, GCC_MMSS_MISC, 0x3, 0x3);
regmap_update_bits(regmap, GCC_GPU_MISC, 0x3, 0x3);
- /* Keep this clock on all the time on SDM845 v1 */
- if (of_device_is_compatible(pdev->dev.of_node, "qcom,gcc-sdm845"))
+ /* Keep this clock on all the times except on SDM845 v2.1 */
+ if (!of_device_is_compatible(pdev->dev.of_node, "qcom,gcc-sdm845-v2.1"))
clk_prepare_enable(gcc_aggre_noc_pcie_tbu_clk.clkr.hw.clk);
/* DFS clock registration */
diff --git a/drivers/clk/qcom/gpucc-sdm845.c b/drivers/clk/qcom/gpucc-sdm845.c
index 35a23f7..55d14ff 100644
--- a/drivers/clk/qcom/gpucc-sdm845.c
+++ b/drivers/clk/qcom/gpucc-sdm845.c
@@ -348,19 +348,6 @@
},
};
-static struct clk_branch gpu_cc_ahb_clk = {
- .halt_reg = 0x1078,
- .halt_check = BRANCH_HALT,
- .clkr = {
- .enable_reg = 0x1078,
- .enable_mask = BIT(0),
- .hw.init = &(struct clk_init_data){
- .name = "gpu_cc_ahb_clk",
- .ops = &clk_branch2_ops,
- },
- },
-};
-
static struct clk_branch gpu_cc_crc_ahb_clk = {
.halt_reg = 0x107c,
.halt_check = BRANCH_HALT,
@@ -545,7 +532,6 @@
static struct clk_regmap *gpu_cc_sdm845_clocks[] = {
[GPU_CC_ACD_AHB_CLK] = &gpu_cc_acd_ahb_clk.clkr,
[GPU_CC_ACD_CXO_CLK] = &gpu_cc_acd_cxo_clk.clkr,
- [GPU_CC_AHB_CLK] = &gpu_cc_ahb_clk.clkr,
[GPU_CC_CRC_AHB_CLK] = &gpu_cc_crc_ahb_clk.clkr,
[GPU_CC_CX_APB_CLK] = &gpu_cc_cx_apb_clk.clkr,
[GPU_CC_CX_GFX3D_CLK] = &gpu_cc_cx_gfx3d_clk.clkr,
diff --git a/drivers/clk/qcom/videocc-sdm845.c b/drivers/clk/qcom/videocc-sdm845.c
index f8fdf3f..79cc5cf 100644
--- a/drivers/clk/qcom/videocc-sdm845.c
+++ b/drivers/clk/qcom/videocc-sdm845.c
@@ -118,7 +118,7 @@
F(100000000, P_VIDEO_PLL0_OUT_MAIN, 4, 0, 0),
F(200000000, P_VIDEO_PLL0_OUT_MAIN, 2, 0, 0),
F(330000000, P_VIDEO_PLL0_OUT_MAIN, 2, 0, 0),
- F(364800000, P_VIDEO_PLL0_OUT_MAIN, 2, 0, 0),
+ F(364700000, P_VIDEO_PLL0_OUT_MAIN, 2, 0, 0),
F(404000000, P_VIDEO_PLL0_OUT_MAIN, 1, 0, 0),
F(444000000, P_VIDEO_PLL0_OUT_MAIN, 1, 0, 0),
F(533000000, P_VIDEO_PLL0_OUT_MAIN, 1, 0, 0),
diff --git a/drivers/cpufreq/cpu-boost.c b/drivers/cpufreq/cpu-boost.c
index e67f12b..6a4008c 100644
--- a/drivers/cpufreq/cpu-boost.c
+++ b/drivers/cpufreq/cpu-boost.c
@@ -75,7 +75,7 @@
for (i = 0; i < ntokens; i += 2) {
if (sscanf(cp, "%u:%u", &cpu, &val) != 2)
return -EINVAL;
- if (cpu > num_possible_cpus())
+ if (cpu >= num_possible_cpus())
return -EINVAL;
per_cpu(sync_info, cpu).input_boost_freq = val;
diff --git a/drivers/cpuidle/lpm-levels-of.c b/drivers/cpuidle/lpm-levels-of.c
index fb11acd..fec75b1 100644
--- a/drivers/cpuidle/lpm-levels-of.c
+++ b/drivers/cpuidle/lpm-levels-of.c
@@ -410,6 +410,9 @@
{
struct lpm_level_avail *avail = cpu_level_available[cpu];
+ if (lpm_pdev && !index)
+ return 1;
+
if (!lpm_pdev || !avail)
return !from_idle;
@@ -710,6 +713,9 @@
if (ret)
goto failed_parse_params;
+ key = "qcom,use-prediction";
+ cpu->lpm_prediction = of_property_read_bool(node, key);
+
key = "parse_cpu";
ret = parse_cpu(node, cpu);
if (ret)
diff --git a/drivers/cpuidle/lpm-levels.c b/drivers/cpuidle/lpm-levels.c
index 746cdc0..630cda2 100644
--- a/drivers/cpuidle/lpm-levels.c
+++ b/drivers/cpuidle/lpm-levels.c
@@ -84,12 +84,13 @@
static bool lpm_prediction = true;
module_param_named(lpm_prediction, lpm_prediction, bool, 0664);
-static uint32_t ref_stddev = 100;
+static uint32_t ref_stddev = 500;
module_param_named(ref_stddev, ref_stddev, uint, 0664);
-static uint32_t tmr_add = 100;
+static uint32_t tmr_add = 1000;
module_param_named(tmr_add, tmr_add, uint, 0664);
+static uint32_t ref_premature_cnt = 1;
static uint32_t bias_hyst;
module_param_named(bias_hyst, bias_hyst, uint, 0664);
@@ -436,8 +437,9 @@
int64_t thresh = LLONG_MAX;
struct lpm_history *history = &per_cpu(hist, dev->cpu);
uint32_t *min_residency = get_per_cpu_min_residency(dev->cpu);
+ uint32_t *max_residency = get_per_cpu_max_residency(dev->cpu);
- if (!lpm_prediction)
+ if (!lpm_prediction || !cpu->lpm_prediction)
return 0;
/*
@@ -522,9 +524,17 @@
total += history->resi[i];
}
}
- if (failed > (MAXSAMPLES/2)) {
+ if (failed >= ref_premature_cnt) {
*idx_restrict = j;
do_div(total, failed);
+ for (i = 0; i < j; i++) {
+ if (total < max_residency[i]) {
+ *idx_restrict = i+1;
+ total = max_residency[i];
+ break;
+ }
+ }
+
*idx_restrict_time = total;
history->stime = ktime_to_us(ktime_get())
+ *idx_restrict_time;
@@ -615,7 +625,7 @@
struct power_params *pwr_params = &level->pwr;
bool allow;
- allow = lpm_cpu_mode_allow(dev->cpu, i, true);
+ allow = i ? lpm_cpu_mode_allow(dev->cpu, i, true) : true;
if (!allow)
continue;
@@ -1006,9 +1016,13 @@
bool from_idle, int predicted)
{
struct lpm_cluster_level *level = &cluster->levels[idx];
+ struct cpumask online_cpus;
+
+ cpumask_and(&online_cpus, &cluster->num_children_in_sync,
+ cpu_online_mask);
if (!cpumask_equal(&cluster->num_children_in_sync, &cluster->child_cpus)
- || is_IPI_pending(&cluster->num_children_in_sync)) {
+ || is_IPI_pending(&online_cpus)) {
return -EPERM;
}
@@ -1460,7 +1474,8 @@
struct lpm_cpu_level *cpu_level = &lpm_cpu->levels[i];
snprintf(st->name, CPUIDLE_NAME_LEN, "C%u\n", i);
- snprintf(st->desc, CPUIDLE_DESC_LEN, cpu_level->name);
+ snprintf(st->desc, CPUIDLE_DESC_LEN, "%s",
+ cpu_level->name);
st->flags = 0;
st->exit_latency = cpu_level->pwr.latency_us;
st->power_usage = cpu_level->pwr.ss_power;
diff --git a/drivers/cpuidle/lpm-levels.h b/drivers/cpuidle/lpm-levels.h
index a458475..b3364b4 100644
--- a/drivers/cpuidle/lpm-levels.h
+++ b/drivers/cpuidle/lpm-levels.h
@@ -44,6 +44,7 @@
int nlevels;
unsigned int psci_mode_shift;
unsigned int psci_mode_mask;
+ bool lpm_prediction;
struct cpuidle_driver *drv;
struct lpm_cluster *parent;
};
diff --git a/drivers/crypto/msm/qce50.c b/drivers/crypto/msm/qce50.c
index 35d7542..4426bc7 100644
--- a/drivers/crypto/msm/qce50.c
+++ b/drivers/crypto/msm/qce50.c
@@ -162,7 +162,7 @@
bool cadence_flag;
uint8_t *dummyreq_in_buf;
struct dma_iommu_mapping *smmu_mapping;
- bool bypass_s1_smmu;
+ bool enable_s1_smmu;
};
static void print_notify_debug(struct sps_event_notify *notify);
@@ -5716,8 +5716,8 @@
pce_dev->ce_opp_freq_hz = CE_CLK_100MHZ;
}
- if (of_property_read_bool((&pdev->dev)->of_node, "qcom,smmu-s1-bypass"))
- pce_dev->bypass_s1_smmu = true;
+ if (of_property_read_bool((&pdev->dev)->of_node, "qcom,smmu-s1-enable"))
+ pce_dev->enable_s1_smmu = true;
pce_dev->ce_bam_info.dest_pipe_index =
2 * pce_dev->ce_bam_info.pipe_pair_index;
@@ -5963,7 +5963,7 @@
static int qce_smmu_init(struct qce_device *pce_dev)
{
struct dma_iommu_mapping *mapping;
- int s1_bypass = 1;
+ int attr = 1;
int ret = 0;
mapping = arm_iommu_create_mapping(&platform_bus_type,
@@ -5975,9 +5975,16 @@
}
ret = iommu_domain_set_attr(mapping->domain,
- DOMAIN_ATTR_S1_BYPASS, &s1_bypass);
+ DOMAIN_ATTR_ATOMIC, &attr);
if (ret < 0) {
- pr_err("Set s1_bypass attribute failed, err = %d\n", ret);
+ pr_err("Set ATOMIC attr failed, err = %d\n", ret);
+ goto ext_fail_set_attr;
+ }
+
+ ret = iommu_domain_set_attr(mapping->domain,
+ DOMAIN_ATTR_UPSTREAM_IOVA_ALLOCATOR, &attr);
+ if (ret < 0) {
+ pr_err("Set UPSTREAM_IOVA_ALLOCATOR failed, err = %d\n", ret);
goto ext_fail_set_attr;
}
@@ -6020,6 +6027,13 @@
goto err_pce_dev;
}
+ if (pce_dev->enable_s1_smmu) {
+ if (qce_smmu_init(pce_dev)) {
+ *rc = -EIO;
+ goto err_pce_dev;
+ }
+ }
+
for (i = 0; i < MAX_QCE_ALLOC_BAM_REQ; i++)
atomic_set(&pce_dev->ce_request_info[i].in_use, false);
pce_dev->ce_request_index = 0;
@@ -6051,13 +6065,6 @@
if (*rc)
goto err_enable_clk;
- if (pce_dev->bypass_s1_smmu) {
- if (qce_smmu_init(pce_dev)) {
- *rc = -EIO;
- goto err_smmu;
- }
- }
-
if (_probe_ce_engine(pce_dev)) {
*rc = -ENXIO;
goto err;
@@ -6084,9 +6091,6 @@
mutex_unlock(&qce_iomap_mutex);
return pce_dev;
err:
- if (pce_dev->bypass_s1_smmu)
- qce_iommu_release_iomapping(pce_dev);
-err_smmu:
qce_disable_clk(pce_dev);
err_enable_clk:
@@ -6099,6 +6103,9 @@
dma_free_coherent(pce_dev->pdev, pce_dev->memsize,
pce_dev->coh_vmem, pce_dev->coh_pmem);
err_iobase:
+ if (pce_dev->enable_s1_smmu)
+ qce_iommu_release_iomapping(pce_dev);
+
if (pce_dev->iobase)
iounmap(pce_dev->iobase);
err_pce_dev:
@@ -6128,7 +6135,7 @@
kfree(pce_dev->dummyreq_in_buf);
kfree(pce_dev->iovec_vmem);
- if (pce_dev->bypass_s1_smmu)
+ if (pce_dev->enable_s1_smmu)
qce_iommu_release_iomapping(pce_dev);
qce_disable_clk(pce_dev);
diff --git a/drivers/crypto/msm/qcedev.c b/drivers/crypto/msm/qcedev.c
index 94b3c17..958fb91 100644
--- a/drivers/crypto/msm/qcedev.c
+++ b/drivers/crypto/msm/qcedev.c
@@ -903,6 +903,7 @@
memset(&handle->sha_ctxt.trailing_buf[0], 0, 64);
kzfree(k_buf_src);
+ qcedev_areq->sha_req.sreq.src = NULL;
return err;
}
@@ -1066,6 +1067,7 @@
handle->sha_ctxt.first_blk = 0;
kzfree(k_src);
+ qcedev_areq->sha_req.sreq.src = NULL;
return err;
}
@@ -1220,8 +1222,10 @@
if (err == 0 && copy_to_user(
(void __user *)creq->vbuf.dst[dst_i].vaddr,
(k_align_dst + byteoffset),
- creq->vbuf.dst[dst_i].len))
- return -EFAULT;
+ creq->vbuf.dst[dst_i].len)) {
+ err = -EFAULT;
+ goto exit;
+ }
k_align_dst += creq->vbuf.dst[dst_i].len +
byteoffset;
@@ -1231,8 +1235,10 @@
if (err == 0 && copy_to_user(
(void __user *)creq->vbuf.dst[dst_i].vaddr,
(k_align_dst + byteoffset),
- creq->data_len))
- return -EFAULT;
+ creq->data_len)) {
+ err = -EFAULT;
+ goto exit;
+ }
k_align_dst += creq->data_len;
creq->vbuf.dst[dst_i].len -= creq->data_len;
@@ -1241,7 +1247,9 @@
}
}
*di = dst_i;
-
+exit:
+ areq->cipher_req.creq.src = NULL;
+ areq->cipher_req.creq.dst = NULL;
return err;
};
diff --git a/drivers/crypto/msm/qcrypto.c b/drivers/crypto/msm/qcrypto.c
index b3269a6..fd42575 100644
--- a/drivers/crypto/msm/qcrypto.c
+++ b/drivers/crypto/msm/qcrypto.c
@@ -3964,7 +3964,7 @@
struct qcrypto_sha_ctx *sha_ctx = crypto_tfm_ctx(&tfm->base);
uint8_t *in_buf;
int ret = 0;
- struct scatterlist sg;
+ struct scatterlist sg = {0};
struct ahash_request *ahash_req;
struct completion ahash_req_complete;
diff --git a/drivers/devfreq/arm-memlat-mon.c b/drivers/devfreq/arm-memlat-mon.c
index f4f503e..9943c8c 100644
--- a/drivers/devfreq/arm-memlat-mon.c
+++ b/drivers/devfreq/arm-memlat-mon.c
@@ -36,6 +36,7 @@
INST_IDX,
CM_IDX,
CYC_IDX,
+ STALL_CYC_IDX,
NUM_EVENTS
};
#define INST_EV 0x08
@@ -105,12 +106,19 @@
{
struct cpu_pmu_stats *cpustats = to_cpustats(cpu_grp, cpu);
struct dev_stats *devstats = to_devstats(cpu_grp, cpu);
- unsigned long cyc_cnt;
+ unsigned long cyc_cnt, stall_cnt;
devstats->inst_count = read_event(&cpustats->events[INST_IDX]);
devstats->mem_count = read_event(&cpustats->events[CM_IDX]);
cyc_cnt = read_event(&cpustats->events[CYC_IDX]);
devstats->freq = compute_freq(cpustats, cyc_cnt);
+ if (cpustats->events[STALL_CYC_IDX].pevent) {
+ stall_cnt = read_event(&cpustats->events[STALL_CYC_IDX]);
+ stall_cnt = min(stall_cnt, cyc_cnt);
+ devstats->stall_pct = mult_frac(100, stall_cnt, cyc_cnt);
+ } else {
+ devstats->stall_pct = 100;
+ }
}
static unsigned long get_cnt(struct memlat_hwmon *hw)
@@ -130,7 +138,10 @@
for (i = 0; i < ARRAY_SIZE(cpustats->events); i++) {
cpustats->events[i].prev_count = 0;
- perf_event_release_kernel(cpustats->events[i].pevent);
+ if (cpustats->events[i].pevent) {
+ perf_event_release_kernel(cpustats->events[i].pevent);
+ cpustats->events[i].pevent = NULL;
+ }
}
}
@@ -149,6 +160,7 @@
devstats->inst_count = 0;
devstats->mem_count = 0;
devstats->freq = 0;
+ devstats->stall_pct = 0;
}
mutex_lock(&list_lock);
if (!cpumask_equal(&cpu_grp->cpus, &cpu_grp->inited_cpus))
@@ -182,6 +194,7 @@
struct perf_event *pevent;
struct perf_event_attr *attr;
int err, i;
+ unsigned int event_id;
struct cpu_pmu_stats *cpustats = to_cpustats(cpu_grp, cpu);
/* Allocate an attribute for event initialization */
@@ -190,7 +203,11 @@
return -ENOMEM;
for (i = 0; i < ARRAY_SIZE(cpustats->events); i++) {
- attr->config = cpu_grp->event_ids[i];
+ event_id = cpu_grp->event_ids[i];
+ if (!event_id)
+ continue;
+
+ attr->config = event_id;
pevent = perf_event_create_kernel_counter(attr, cpu, NULL,
NULL, NULL);
if (IS_ERR(pevent))
@@ -348,6 +365,13 @@
}
cpu_grp->event_ids[INST_IDX] = event_id;
+ ret = of_property_read_u32(dev->of_node, "qcom,stall-cycle-ev",
+ &event_id);
+ if (ret)
+ dev_dbg(dev, "Stall cycle event not specified. Event ignored.\n");
+ else
+ cpu_grp->event_ids[STALL_CYC_IDX] = event_id;
+
for_each_cpu(cpu, &cpu_grp->cpus)
to_devstats(cpu_grp, cpu)->id = cpu;
diff --git a/drivers/devfreq/governor_bw_hwmon.c b/drivers/devfreq/governor_bw_hwmon.c
index 53c0f8a..a1d9b50 100644
--- a/drivers/devfreq/governor_bw_hwmon.c
+++ b/drivers/devfreq/governor_bw_hwmon.c
@@ -820,11 +820,13 @@
static int devfreq_bw_hwmon_ev_handler(struct devfreq *df,
unsigned int event, void *data)
{
- int ret;
+ int ret = 0;
unsigned int sample_ms;
struct hwmon_node *node;
struct bw_hwmon *hw;
+ mutex_lock(&state_lock);
+
switch (event) {
case DEVFREQ_GOV_START:
sample_ms = df->profile->polling_ms;
@@ -834,7 +836,7 @@
ret = gov_start(df);
if (ret)
- return ret;
+ goto out;
dev_dbg(df->dev.parent,
"Enabled dev BW HW monitor governor\n");
@@ -864,7 +866,7 @@
if (ret) {
dev_err(df->dev.parent,
"Unable to resume HW monitor (%d)\n", ret);
- return ret;
+ goto out;
}
break;
@@ -874,7 +876,7 @@
dev_err(df->dev.parent,
"Unable to suspend BW HW mon governor (%d)\n",
ret);
- return ret;
+ goto out;
}
dev_dbg(df->dev.parent, "Suspended BW HW mon governor\n");
@@ -886,14 +888,17 @@
dev_err(df->dev.parent,
"Unable to resume BW HW mon governor (%d)\n",
ret);
- return ret;
+ goto out;
}
dev_dbg(df->dev.parent, "Resumed BW HW mon governor\n");
break;
}
- return 0;
+out:
+ mutex_unlock(&state_lock);
+
+ return ret;
}
static struct devfreq_governor devfreq_gov_bw_hwmon = {
diff --git a/drivers/devfreq/governor_memlat.c b/drivers/devfreq/governor_memlat.c
index 1a8ef1f..9688502 100644
--- a/drivers/devfreq/governor_memlat.c
+++ b/drivers/devfreq/governor_memlat.c
@@ -35,6 +35,7 @@
struct memlat_node {
unsigned int ratio_ceil;
+ unsigned int stall_floor;
bool mon_started;
bool already_zero;
struct list_head list;
@@ -247,9 +248,11 @@
hw->core_stats[i].id,
hw->core_stats[i].inst_count,
hw->core_stats[i].mem_count,
- hw->core_stats[i].freq, ratio);
+ hw->core_stats[i].freq,
+ hw->core_stats[i].stall_pct, ratio);
if (ratio <= node->ratio_ceil
+ && hw->core_stats[i].stall_pct >= node->stall_floor
&& hw->core_stats[i].freq > max_freq) {
lat_dev = i;
max_freq = hw->core_stats[i].freq;
@@ -275,9 +278,11 @@
}
gov_attr(ratio_ceil, 1U, 10000U);
+gov_attr(stall_floor, 0U, 100U);
static struct attribute *dev_attr[] = {
&dev_attr_ratio_ceil.attr,
+ &dev_attr_stall_floor.attr,
&dev_attr_freq_map.attr,
NULL,
};
diff --git a/drivers/devfreq/governor_memlat.h b/drivers/devfreq/governor_memlat.h
index 8c533ee..f2ba534 100644
--- a/drivers/devfreq/governor_memlat.h
+++ b/drivers/devfreq/governor_memlat.h
@@ -29,6 +29,7 @@
unsigned long inst_count;
unsigned long mem_count;
unsigned long freq;
+ unsigned long stall_pct;
};
struct core_dev_map {
diff --git a/drivers/gpu/drm/msm/dp/dp_catalog.c b/drivers/gpu/drm/msm/dp/dp_catalog.c
index 2894e82..b9b996a 100644
--- a/drivers/gpu/drm/msm/dp/dp_catalog.c
+++ b/drivers/gpu/drm/msm/dp/dp_catalog.c
@@ -604,7 +604,7 @@
static void dp_catalog_ctrl_config_misc(struct dp_catalog_ctrl *ctrl,
u32 cc, u32 tb)
{
- u32 misc_val;
+ u32 misc_val = cc;
struct dp_catalog_private *catalog;
void __iomem *base;
@@ -616,8 +616,6 @@
dp_catalog_get_priv(ctrl);
base = catalog->io->ctrl_io.base;
- misc_val = dp_read(base + DP_MISC1_MISC0);
- misc_val |= cc;
misc_val |= (tb << 5);
misc_val |= BIT(0); /* Configure clock to synchronous mode */
diff --git a/drivers/gpu/drm/msm/dp/dp_display.c b/drivers/gpu/drm/msm/dp/dp_display.c
index ea07d15..7fbc63a 100644
--- a/drivers/gpu/drm/msm/dp/dp_display.c
+++ b/drivers/gpu/drm/msm/dp/dp_display.c
@@ -88,6 +88,7 @@
struct workqueue_struct *hdcp_workqueue;
struct delayed_work hdcp_cb_work;
struct mutex hdcp_mutex;
+ struct mutex session_lock;
int hdcp_status;
};
@@ -500,6 +501,8 @@
if (!wait_for_completion_timeout(&dp->notification_comp, HZ * 5)) {
pr_warn("%s timeout\n", hpd ? "connect" : "disconnect");
+ /* cancel any pending request */
+ dp->ctrl->abort(dp->ctrl);
return -EINVAL;
}
@@ -634,6 +637,7 @@
dp->ctrl->push_idle(dp->ctrl);
dp->ctrl->off(dp->ctrl);
+ dp->power_on = false;
}
static int dp_display_usbpd_disconnect_cb(struct device *dev)
@@ -662,6 +666,8 @@
rc = dp_display_send_hpd_notification(dp, false);
+ mutex_lock(&dp->session_lock);
+
/* if cable is disconnected, reset psm_enabled flag */
if (!dp->usbpd->alt_mode_cfg_done)
dp->link->psm_enabled = false;
@@ -670,6 +676,8 @@
dp_display_clean(dp);
dp_display_host_deinit(dp);
+
+ mutex_unlock(&dp->session_lock);
end:
return rc;
}
@@ -760,6 +768,7 @@
dp_catalog_put(dp->catalog);
dp_parser_put(dp->parser);
dp_usbpd_put(dp->usbpd);
+ mutex_destroy(&dp->session_lock);
dp_debug_put(dp->debug);
}
@@ -787,6 +796,8 @@
goto error;
}
+ mutex_init(&dp->session_lock);
+
dp->parser = dp_parser_get(dp->pdev);
if (IS_ERR(dp->parser)) {
rc = PTR_ERR(dp->parser);
@@ -890,6 +901,7 @@
dp_parser_put(dp->parser);
error_parser:
dp_usbpd_put(dp->usbpd);
+ mutex_destroy(&dp->session_lock);
error:
return rc;
}
@@ -897,20 +909,20 @@
static int dp_display_set_mode(struct dp_display *dp_display,
struct dp_display_mode *mode)
{
- int rc = 0;
struct dp_display_private *dp;
if (!dp_display) {
pr_err("invalid input\n");
- rc = -EINVAL;
- goto error;
+ return -EINVAL;
}
dp = container_of(dp_display, struct dp_display_private, dp_display);
+ mutex_lock(&dp->session_lock);
dp->panel->pinfo = mode->timing;
dp->panel->init_info(dp->panel);
-error:
- return rc;
+ mutex_unlock(&dp->session_lock);
+
+ return 0;
}
static int dp_display_prepare(struct dp_display *dp)
@@ -925,37 +937,44 @@
if (!dp_display) {
pr_err("invalid input\n");
- rc = -EINVAL;
- goto error;
+ return -EINVAL;
}
dp = container_of(dp_display, struct dp_display_private, dp_display);
+ mutex_lock(&dp->session_lock);
+
if (dp->power_on) {
pr_debug("Link already setup, return\n");
- return 0;
+ goto end;
}
rc = dp->ctrl->on(dp->ctrl);
if (!rc)
dp->power_on = true;
-error:
+end:
+ mutex_unlock(&dp->session_lock);
return rc;
}
static int dp_display_post_enable(struct dp_display *dp_display)
{
- int rc = 0;
struct dp_display_private *dp;
if (!dp_display) {
pr_err("invalid input\n");
- rc = -EINVAL;
- goto end;
+ return -EINVAL;
}
dp = container_of(dp_display, struct dp_display_private, dp_display);
+ mutex_lock(&dp->session_lock);
+
+ if (!dp->power_on) {
+ pr_debug("Link not setup, return\n");
+ goto end;
+ }
+
if (dp->audio_supported) {
dp->audio->bw_code = dp->link->link_params.bw_code;
dp->audio->lane_count = dp->link->link_params.lane_count;
@@ -973,23 +992,30 @@
queue_delayed_work(dp->hdcp_workqueue,
&dp->hdcp_cb_work, HZ / 2);
}
+
end:
- return rc;
+ mutex_unlock(&dp->session_lock);
+ return 0;
}
static int dp_display_pre_disable(struct dp_display *dp_display)
{
- int rc = 0;
struct dp_display_private *dp;
if (!dp_display) {
pr_err("invalid input\n");
- rc = -EINVAL;
- goto error;
+ return -EINVAL;
}
dp = container_of(dp_display, struct dp_display_private, dp_display);
+ mutex_lock(&dp->session_lock);
+
+ if (!dp->power_on) {
+ pr_debug("Link already powered off, return\n");
+ goto end;
+ }
+
if (dp_display_is_hdcp_enabled(dp)) {
dp->hdcp_status = HDCP_STATE_INACTIVE;
@@ -1003,33 +1029,38 @@
dp->link->psm_config(dp->link, &dp->panel->link_info, true);
dp->ctrl->push_idle(dp->ctrl);
-error:
- return rc;
+
+end:
+ mutex_unlock(&dp->session_lock);
+ return 0;
}
static int dp_display_disable(struct dp_display *dp_display)
{
- int rc = 0;
struct dp_display_private *dp;
if (!dp_display) {
pr_err("invalid input\n");
- rc = -EINVAL;
- goto error;
+ return -EINVAL;
}
dp = container_of(dp_display, struct dp_display_private, dp_display);
- if (!dp->power_on || !dp->core_initialized)
- goto error;
+ mutex_lock(&dp->session_lock);
+
+ if (!dp->power_on || !dp->core_initialized) {
+ pr_debug("Link already powered off, return\n");
+ goto end;
+ }
dp->ctrl->off(dp->ctrl);
dp->power_on = false;
+end:
complete_all(&dp->notification_comp);
-error:
- return rc;
+ mutex_unlock(&dp->session_lock);
+ return 0;
}
static int dp_request_irq(struct dp_display *dp_display)
diff --git a/drivers/gpu/drm/msm/dp/dp_drm.c b/drivers/gpu/drm/msm/dp/dp_drm.c
index 254343a..170734f 100644
--- a/drivers/gpu/drm/msm/dp/dp_drm.c
+++ b/drivers/gpu/drm/msm/dp/dp_drm.c
@@ -306,7 +306,7 @@
}
int dp_connector_get_mode_info(const struct drm_display_mode *drm_mode,
- struct msm_mode_info *mode_info, u32 max_mixer_width)
+ struct msm_mode_info *mode_info, u32 max_mixer_width, void *display)
{
const u32 dual_lm = 2;
const u32 single_lm = 1;
diff --git a/drivers/gpu/drm/msm/dp/dp_drm.h b/drivers/gpu/drm/msm/dp/dp_drm.h
index 53570f5..eb78e71 100644
--- a/drivers/gpu/drm/msm/dp/dp_drm.h
+++ b/drivers/gpu/drm/msm/dp/dp_drm.h
@@ -78,11 +78,12 @@
* @drm_mode: Display mode set for the display
* @mode_info: Out parameter. Information of the mode
* @max_mixer_width: max width supported by HW layer mixer
+ * @display: Pointer to private display structure
* Returns: zero on success
*/
int dp_connector_get_mode_info(const struct drm_display_mode *drm_mode,
struct msm_mode_info *mode_info,
- u32 max_mixer_width);
+ u32 max_mixer_width, void *display);
int dp_connector_get_info(struct msm_display_info *info, void *display);
diff --git a/drivers/gpu/drm/msm/dp/dp_power.c b/drivers/gpu/drm/msm/dp/dp_power.c
index eb787fa..d793365 100644
--- a/drivers/gpu/drm/msm/dp/dp_power.c
+++ b/drivers/gpu/drm/msm/dp/dp_power.c
@@ -562,6 +562,15 @@
power = container_of(dp_power, struct dp_power_private, dp_power);
dp_power_clk_enable(dp_power, DP_CORE_PM, false);
+ /*
+ * If the display power on event was not successful, for example if
+ * there was a link training failure, then the link clocks could
+ * possibly still be on. In this scenario, we need to turn off the
+ * link clocks as soon as the cable is disconnected so that the clock
+ * state is cleaned up before subsequent connection events.
+ */
+ if (power->link_clks_on)
+ dp_power_clk_enable(dp_power, DP_CTRL_PM, false);
rc = sde_power_resource_enable(power->phandle,
power->dp_core_client, false);
if (rc) {
diff --git a/drivers/gpu/drm/msm/dsi-staging/dsi_ctrl.c b/drivers/gpu/drm/msm/dsi-staging/dsi_ctrl.c
index 790ee22..21a23e2 100644
--- a/drivers/gpu/drm/msm/dsi-staging/dsi_ctrl.c
+++ b/drivers/gpu/drm/msm/dsi-staging/dsi_ctrl.c
@@ -785,10 +785,14 @@
if (host_cfg->data_lanes & DSI_DATA_LANE_3)
num_of_lanes++;
- h_period = DSI_H_TOTAL_DSC(timing);
- v_period = DSI_V_TOTAL(timing);
+ if (config->bit_clk_rate_hz == 0) {
+ h_period = DSI_H_TOTAL_DSC(timing);
+ v_period = DSI_V_TOTAL(timing);
+ bit_rate = h_period * v_period * timing->refresh_rate * bpp * 8;
+ } else {
+ bit_rate = config->bit_clk_rate_hz * num_of_lanes;
+ }
- bit_rate = h_period * v_period * timing->refresh_rate * bpp * 8;
bit_rate_per_lane = bit_rate;
do_div(bit_rate_per_lane, num_of_lanes);
pclk_rate = bit_rate;
diff --git a/drivers/gpu/drm/msm/dsi-staging/dsi_defs.h b/drivers/gpu/drm/msm/dsi-staging/dsi_defs.h
index 96ed47e..2f0d25f 100644
--- a/drivers/gpu/drm/msm/dsi-staging/dsi_defs.h
+++ b/drivers/gpu/drm/msm/dsi-staging/dsi_defs.h
@@ -355,6 +355,7 @@
* @v_front_porch: Vertical front porch in lines.
* @v_sync_polarity: Polarity of VSYNC (false is active low).
* @refresh_rate: Refresh rate in Hz.
+ * @clk_rate_hz: DSI bit clock rate per lane in Hz.
* @dsc_enabled: DSC compression enabled.
* @dsc: DSC compression configuration.
*/
@@ -373,7 +374,7 @@
bool v_sync_polarity;
u32 refresh_rate;
-
+ u64 clk_rate_hz;
bool dsc_enabled;
struct msm_display_dsc_info *dsc;
};
@@ -500,6 +501,7 @@
* @phy_timing_len: Phy timing array length
* @panel_jitter: Panel jitter for RSC backoff
* @panel_prefill_lines: Panel prefill lines for RSC
+ * @clk_rate_hz: DSI bit clock per lane in hz.
* @topology: Topology selected for the panel
* @dsc: DSC compression info
* @dsc_enabled: DSC compression enabled
@@ -513,6 +515,7 @@
u32 panel_jitter_numer;
u32 panel_jitter_denom;
u32 panel_prefill_lines;
+ u64 clk_rate_hz;
struct msm_display_topology topology;
struct msm_display_dsc_info dsc;
diff --git a/drivers/gpu/drm/msm/dsi-staging/dsi_drm.c b/drivers/gpu/drm/msm/dsi-staging/dsi_drm.c
index af721eb..280c754 100644
--- a/drivers/gpu/drm/msm/dsi-staging/dsi_drm.c
+++ b/drivers/gpu/drm/msm/dsi-staging/dsi_drm.c
@@ -297,7 +297,7 @@
int dsi_conn_get_mode_info(const struct drm_display_mode *drm_mode,
struct msm_mode_info *mode_info,
- u32 max_mixer_width)
+ u32 max_mixer_width, void *display)
{
struct dsi_display_mode dsi_mode;
struct dsi_mode_info *timing;
@@ -318,6 +318,7 @@
mode_info->prefill_lines = dsi_mode.priv_info->panel_prefill_lines;
mode_info->jitter_numer = dsi_mode.priv_info->panel_jitter_numer;
mode_info->jitter_denom = dsi_mode.priv_info->panel_jitter_denom;
+ mode_info->clk_rate = dsi_mode.priv_info->clk_rate_hz;
memcpy(&mode_info->topology, &dsi_mode.priv_info->topology,
sizeof(struct msm_display_topology));
@@ -631,10 +632,17 @@
struct dsi_display_ctrl *m_ctrl, *ctrl;
int i, rc = 0;
- if (!connector || !connector->state->best_encoder)
+ if (!connector || !connector->state) {
+ pr_err("invalid connector or connector state");
return -EINVAL;
+ }
encoder = connector->state->best_encoder;
+ if (!encoder) {
+ pr_debug("best encoder is not available");
+ return 0;
+ }
+
c_bridge = to_dsi_bridge(encoder->bridge);
adj_mode = c_bridge->dsi_mode;
display = c_bridge->display;
diff --git a/drivers/gpu/drm/msm/dsi-staging/dsi_drm.h b/drivers/gpu/drm/msm/dsi-staging/dsi_drm.h
index 9700e68..828e65d 100644
--- a/drivers/gpu/drm/msm/dsi-staging/dsi_drm.h
+++ b/drivers/gpu/drm/msm/dsi-staging/dsi_drm.h
@@ -76,10 +76,12 @@
* @drm_mode: Display mode set for the display
* @mode_info: Out parameter. information of the mode.
* @max_mixer_width: max width supported by HW layer mixer
+ * @display: Pointer to private display structure
* Returns: Zero on success
*/
int dsi_conn_get_mode_info(const struct drm_display_mode *drm_mode,
- struct msm_mode_info *mode_info, u32 max_mixer_width);
+ struct msm_mode_info *mode_info, u32 max_mixer_width,
+ void *display);
/**
* dsi_conn_mode_valid - callback to determine if specified mode is valid
diff --git a/drivers/gpu/drm/msm/dsi-staging/dsi_panel.c b/drivers/gpu/drm/msm/dsi-staging/dsi_panel.c
index 6718156..4688741 100644
--- a/drivers/gpu/drm/msm/dsi-staging/dsi_panel.c
+++ b/drivers/gpu/drm/msm/dsi-staging/dsi_panel.c
@@ -683,6 +683,21 @@
struct device_node *of_node)
{
int rc = 0;
+ u64 tmp64;
+ struct dsi_display_mode *display_mode;
+
+ display_mode = container_of(mode, struct dsi_display_mode, timing);
+
+ rc = of_property_read_u64(of_node,
+ "qcom,mdss-dsi-panel-clockrate", &tmp64);
+ if (rc == -EOVERFLOW) {
+ tmp64 = 0;
+ rc = of_property_read_u32(of_node,
+ "qcom,mdss-dsi-panel-clockrate", (u32 *)&tmp64);
+ }
+
+ mode->clk_rate_hz = !rc ? tmp64 : 0;
+ display_mode->priv_info->clk_rate_hz = mode->clk_rate_hz;
rc = of_property_read_u32(of_node, "qcom,mdss-dsi-panel-framerate",
&mode->refresh_rate);
@@ -2512,8 +2527,10 @@
struct property *data;
const char *string;
struct drm_panel_esd_config *esd_config;
+ u8 *esd_mode = NULL;
esd_config = &panel->esd_config;
+ esd_config->status_mode = ESD_MODE_MAX;
esd_config->esd_enabled = of_property_read_bool(of_node,
"qcom,esd-check-enabled");
@@ -2635,6 +2652,15 @@
esd_config->groups * status_len);
}
+ if (panel->esd_config.status_mode == ESD_MODE_REG_READ)
+ esd_mode = "register_read";
+ else if (panel->esd_config.status_mode == ESD_MODE_SW_BTA)
+ esd_mode = "bta_trigger";
+ else if (panel->esd_config.status_mode == ESD_MODE_PANEL_TE)
+ esd_mode = "te_check";
+
+ pr_info("ESD enabled with mode: %s\n", esd_mode);
+
return 0;
error4:
@@ -2725,20 +2751,8 @@
pr_debug("failed to get dms info, rc=%d\n", rc);
rc = dsi_panel_parse_esd_config(panel, of_node);
- if (rc) {
+ if (rc)
pr_debug("failed to parse esd config, rc=%d\n", rc);
- } else {
- u8 *esd_mode = NULL;
-
- if (panel->esd_config.status_mode == ESD_MODE_REG_READ)
- esd_mode = "register_read";
- else if (panel->esd_config.status_mode == ESD_MODE_SW_BTA)
- esd_mode = "bta_trigger";
- else if (panel->esd_config.status_mode == ESD_MODE_PANEL_TE)
- esd_mode = "te_check";
-
- pr_info("ESD enabled with mode: %s\n", esd_mode);
- }
panel->panel_of_node = of_node;
drm_panel_init(&panel->drm_panel);
@@ -3075,6 +3089,7 @@
config->video_timing.dsc_enabled = mode->priv_info->dsc_enabled;
config->video_timing.dsc = &mode->priv_info->dsc;
+ config->bit_clk_rate_hz = mode->priv_info->clk_rate_hz;
config->esc_clk_rate_hz = 19200000;
mutex_unlock(&panel->panel_lock);
return rc;
diff --git a/drivers/gpu/drm/msm/msm_atomic.c b/drivers/gpu/drm/msm/msm_atomic.c
index cbcf580..28f2e7c 100644
--- a/drivers/gpu/drm/msm/msm_atomic.c
+++ b/drivers/gpu/drm/msm/msm_atomic.c
@@ -555,6 +555,11 @@
struct drm_plane_state *plane_state;
int i, ret;
+ if (!priv || priv->shutdown_in_progress) {
+ DRM_ERROR("priv is null or shutdwon is in-progress\n");
+ return -EINVAL;
+ }
+
SDE_ATRACE_BEGIN("atomic_commit");
ret = drm_atomic_helper_prepare_planes(dev, state);
if (ret) {
@@ -610,8 +615,7 @@
* are dispatched to work queues, so that the fence preparation is
* finished before the .atomic_commit returns.
*/
- if (priv && priv->kms && priv->kms->funcs &&
- priv->kms->funcs->prepare_fence)
+ if (priv->kms && priv->kms->funcs && priv->kms->funcs->prepare_fence)
priv->kms->funcs->prepare_fence(priv->kms, state);
/*
diff --git a/drivers/gpu/drm/msm/msm_drv.c b/drivers/gpu/drm/msm/msm_drv.c
index 2f9571b..a75a126 100644
--- a/drivers/gpu/drm/msm/msm_drv.c
+++ b/drivers/gpu/drm/msm/msm_drv.c
@@ -57,8 +57,6 @@
#define MSM_VERSION_MINOR 2
#define MSM_VERSION_PATCHLEVEL 0
-#define TEARDOWN_DEADLOCK_RETRY_MAX 5
-
static void msm_fb_output_poll_changed(struct drm_device *dev)
{
struct msm_drm_private *priv = NULL;
@@ -1930,6 +1928,28 @@
return 0;
}
+static void msm_pdev_shutdown(struct platform_device *pdev)
+{
+ struct drm_device *ddev = platform_get_drvdata(pdev);
+ struct msm_drm_private *priv = NULL;
+
+ if (!ddev) {
+ DRM_ERROR("invalid drm device node\n");
+ return;
+ }
+
+ priv = ddev->dev_private;
+ if (!priv) {
+ DRM_ERROR("invalid msm drm private node\n");
+ return;
+ }
+
+ msm_lastclose(ddev);
+
+ /* set this after lastclose to allow kickoff from lastclose */
+ priv->shutdown_in_progress = true;
+}
+
static const struct of_device_id dt_match[] = {
{ .compatible = "qcom,mdp4", .data = (void *)4 }, /* MDP4 */
{ .compatible = "qcom,mdss", .data = (void *)5 }, /* MDP5 MDSS */
@@ -1941,6 +1961,7 @@
static struct platform_driver msm_platform_driver = {
.probe = msm_pdev_probe,
.remove = msm_pdev_remove,
+ .shutdown = msm_pdev_shutdown,
.driver = {
.name = "msm_drm",
.of_match_table = dt_match,
diff --git a/drivers/gpu/drm/msm/msm_drv.h b/drivers/gpu/drm/msm/msm_drv.h
index e19d1db..cf61fac 100644
--- a/drivers/gpu/drm/msm/msm_drv.h
+++ b/drivers/gpu/drm/msm/msm_drv.h
@@ -71,6 +71,8 @@
#define MAX_BRIDGES 8
#define MAX_CONNECTORS 8
+#define TEARDOWN_DEADLOCK_RETRY_MAX 5
+
struct msm_file_private {
/* currently we don't do anything useful with this.. but when
* per-context address spaces are supported we'd keep track of
@@ -404,6 +406,7 @@
* @prefill_lines: prefill lines based on porches.
* @jitter_numer: display panel jitter numerator configuration
* @jitter_denom: display panel jitter denominator configuration
+ * @clk_rate: DSI bit clock per lane in HZ.
* @topology: supported topology for the mode
* @comp_info: compression info supported
*/
@@ -413,6 +416,7 @@
uint32_t prefill_lines;
uint32_t jitter_numer;
uint32_t jitter_denom;
+ uint64_t clk_rate;
struct msm_display_topology topology;
struct msm_compression_info comp_info;
};
@@ -431,6 +435,7 @@
* this is max width supported by controller
* @max_height: Max height of display. In case of hot pluggable display
* this is max height supported by controller
+ * @clk_rate: DSI bit clock per lane in HZ.
* @is_primary: Set to true if display is primary display
* @is_te_using_watchdog_timer: Boolean to indicate watchdog TE is
* used instead of panel TE in cmd mode panels
@@ -450,6 +455,7 @@
uint32_t max_width;
uint32_t max_height;
+ uint64_t clk_rate;
bool is_primary;
bool is_te_using_watchdog_timer;
@@ -609,6 +615,9 @@
/* msm drv debug root node */
struct dentry *debug_root;
+
+ /* update the flag when msm driver receives shutdown notification */
+ bool shutdown_in_progress;
};
/* get struct msm_kms * from drm_device * */
diff --git a/drivers/gpu/drm/msm/msm_smmu.c b/drivers/gpu/drm/msm/msm_smmu.c
index 730fc06..92d1865 100644
--- a/drivers/gpu/drm/msm/msm_smmu.c
+++ b/drivers/gpu/drm/msm/msm_smmu.c
@@ -455,6 +455,7 @@
const struct msm_smmu_domain *domain)
{
int rc;
+ int mdphtw_llc_enable = 1;
client->mmu_mapping = arm_iommu_create_mapping(&platform_bus_type,
domain->va_start, domain->va_size);
@@ -465,6 +466,14 @@
return PTR_ERR(client->mmu_mapping);
}
+ rc = iommu_domain_set_attr(client->mmu_mapping->domain,
+ DOMAIN_ATTR_USE_UPSTREAM_HINT, &mdphtw_llc_enable);
+ if (rc) {
+ dev_err(client->dev, "couldn't enable mdp pagetable walks: %d\n",
+ rc);
+ goto error;
+ }
+
if (domain->secure) {
int secure_vmid = VMID_CP_PIXEL;
diff --git a/drivers/gpu/drm/msm/sde/sde_color_processing.c b/drivers/gpu/drm/msm/sde/sde_color_processing.c
index e9ffb96..2c5b7ea 100644
--- a/drivers/gpu/drm/msm/sde/sde_color_processing.c
+++ b/drivers/gpu/drm/msm/sde/sde_color_processing.c
@@ -52,6 +52,10 @@
static void dspp_hsic_install_property(struct drm_crtc *crtc);
+static void dspp_memcolor_install_property(struct drm_crtc *crtc);
+
+static void dspp_sixzone_install_property(struct drm_crtc *crtc);
+
static void dspp_ad_install_property(struct drm_crtc *crtc);
static void dspp_vlut_install_property(struct drm_crtc *crtc);
@@ -85,6 +89,8 @@
do { \
func[SDE_DSPP_PCC] = dspp_pcc_install_property; \
func[SDE_DSPP_HSIC] = dspp_hsic_install_property; \
+ func[SDE_DSPP_MEMCOLOR] = dspp_memcolor_install_property; \
+ func[SDE_DSPP_SIXZONE] = dspp_sixzone_install_property; \
func[SDE_DSPP_AD] = dspp_ad_install_property; \
func[SDE_DSPP_VLUT] = dspp_vlut_install_property; \
func[SDE_DSPP_GAMUT] = dspp_gamut_install_property; \
@@ -108,11 +114,11 @@
SDE_CP_CRTC_DSPP_IGC,
SDE_CP_CRTC_DSPP_PCC,
SDE_CP_CRTC_DSPP_GC,
- SDE_CP_CRTC_DSPP_HUE,
- SDE_CP_CRTC_DSPP_SAT,
- SDE_CP_CRTC_DSPP_VAL,
- SDE_CP_CRTC_DSPP_CONT,
- SDE_CP_CRTC_DSPP_MEMCOLOR,
+ SDE_CP_CRTC_DSPP_HSIC,
+ SDE_CP_CRTC_DSPP_MEMCOL_SKIN,
+ SDE_CP_CRTC_DSPP_MEMCOL_SKY,
+ SDE_CP_CRTC_DSPP_MEMCOL_FOLIAGE,
+ SDE_CP_CRTC_DSPP_MEMCOL_PROT,
SDE_CP_CRTC_DSPP_SIXZONE,
SDE_CP_CRTC_DSPP_GAMUT,
SDE_CP_CRTC_DSPP_DITHER,
@@ -667,40 +673,40 @@
}
hw_dspp->ops.setup_gc(hw_dspp, &hw_cfg);
break;
- case SDE_CP_CRTC_DSPP_HUE:
- if (!hw_dspp || !hw_dspp->ops.setup_hue) {
+ case SDE_CP_CRTC_DSPP_HSIC:
+ if (!hw_dspp || !hw_dspp->ops.setup_pa_hsic) {
ret = -EINVAL;
continue;
}
- hw_dspp->ops.setup_hue(hw_dspp, &hw_cfg);
+ hw_dspp->ops.setup_pa_hsic(hw_dspp, &hw_cfg);
break;
- case SDE_CP_CRTC_DSPP_SAT:
- if (!hw_dspp || !hw_dspp->ops.setup_sat) {
+ case SDE_CP_CRTC_DSPP_MEMCOL_SKIN:
+ if (!hw_dspp || !hw_dspp->ops.setup_pa_memcol_skin) {
ret = -EINVAL;
continue;
}
- hw_dspp->ops.setup_sat(hw_dspp, &hw_cfg);
+ hw_dspp->ops.setup_pa_memcol_skin(hw_dspp, &hw_cfg);
break;
- case SDE_CP_CRTC_DSPP_VAL:
- if (!hw_dspp || !hw_dspp->ops.setup_val) {
+ case SDE_CP_CRTC_DSPP_MEMCOL_SKY:
+ if (!hw_dspp || !hw_dspp->ops.setup_pa_memcol_sky) {
ret = -EINVAL;
continue;
}
- hw_dspp->ops.setup_val(hw_dspp, &hw_cfg);
+ hw_dspp->ops.setup_pa_memcol_sky(hw_dspp, &hw_cfg);
break;
- case SDE_CP_CRTC_DSPP_CONT:
- if (!hw_dspp || !hw_dspp->ops.setup_cont) {
+ case SDE_CP_CRTC_DSPP_MEMCOL_FOLIAGE:
+ if (!hw_dspp || !hw_dspp->ops.setup_pa_memcol_foliage) {
ret = -EINVAL;
continue;
}
- hw_dspp->ops.setup_cont(hw_dspp, &hw_cfg);
+ hw_dspp->ops.setup_pa_memcol_foliage(hw_dspp, &hw_cfg);
break;
- case SDE_CP_CRTC_DSPP_MEMCOLOR:
- if (!hw_dspp || !hw_dspp->ops.setup_pa_memcolor) {
+ case SDE_CP_CRTC_DSPP_MEMCOL_PROT:
+ if (!hw_dspp || !hw_dspp->ops.setup_pa_memcol_prot) {
ret = -EINVAL;
continue;
}
- hw_dspp->ops.setup_pa_memcolor(hw_dspp, &hw_cfg);
+ hw_dspp->ops.setup_pa_memcol_prot(hw_dspp, &hw_cfg);
break;
case SDE_CP_CRTC_DSPP_SIXZONE:
if (!hw_dspp || !hw_dspp->ops.setup_sixzone) {
@@ -1214,9 +1220,72 @@
switch (version) {
case 1:
snprintf(feature_name, ARRAY_SIZE(feature_name), "%s%d",
- "SDE_DSPP_HUE_V", version);
- sde_cp_crtc_install_range_property(crtc, feature_name,
- SDE_CP_CRTC_DSPP_HUE, 0, U32_MAX, 0);
+ "SDE_DSPP_PA_HSIC_V", version);
+ sde_cp_crtc_install_blob_property(crtc, feature_name,
+ SDE_CP_CRTC_DSPP_HSIC, sizeof(struct drm_msm_pa_hsic));
+ break;
+ default:
+ DRM_ERROR("version %d not supported\n", version);
+ break;
+ }
+}
+
+static void dspp_memcolor_install_property(struct drm_crtc *crtc)
+{
+ char feature_name[256];
+ struct sde_kms *kms = NULL;
+ struct sde_mdss_cfg *catalog = NULL;
+ u32 version;
+
+ kms = get_kms(crtc);
+ catalog = kms->catalog;
+ version = catalog->dspp[0].sblk->memcolor.version >> 16;
+ switch (version) {
+ case 1:
+ snprintf(feature_name, ARRAY_SIZE(feature_name), "%s%d",
+ "SDE_DSPP_PA_MEMCOL_SKIN_V", version);
+ sde_cp_crtc_install_blob_property(crtc, feature_name,
+ SDE_CP_CRTC_DSPP_MEMCOL_SKIN,
+ sizeof(struct drm_msm_memcol));
+ snprintf(feature_name, ARRAY_SIZE(feature_name), "%s%d",
+ "SDE_DSPP_PA_MEMCOL_SKY_V", version);
+ sde_cp_crtc_install_blob_property(crtc, feature_name,
+ SDE_CP_CRTC_DSPP_MEMCOL_SKY,
+ sizeof(struct drm_msm_memcol));
+ snprintf(feature_name, ARRAY_SIZE(feature_name), "%s%d",
+ "SDE_DSPP_PA_MEMCOL_FOLIAGE_V", version);
+ sde_cp_crtc_install_blob_property(crtc, feature_name,
+ SDE_CP_CRTC_DSPP_MEMCOL_FOLIAGE,
+ sizeof(struct drm_msm_memcol));
+ snprintf(feature_name, ARRAY_SIZE(feature_name), "%s%d",
+ "SDE_DSPP_PA_MEMCOL_PROT_V", version);
+ sde_cp_crtc_install_blob_property(crtc, feature_name,
+ SDE_CP_CRTC_DSPP_MEMCOL_PROT,
+ sizeof(struct drm_msm_memcol));
+ break;
+ default:
+ DRM_ERROR("version %d not supported\n", version);
+ break;
+ }
+}
+
+static void dspp_sixzone_install_property(struct drm_crtc *crtc)
+{
+ char feature_name[256];
+ struct sde_kms *kms = NULL;
+ struct sde_mdss_cfg *catalog = NULL;
+ u32 version;
+
+ kms = get_kms(crtc);
+ catalog = kms->catalog;
+ version = catalog->dspp[0].sblk->sixzone.version >> 16;
+ switch (version) {
+ case 1:
+ snprintf(feature_name, ARRAY_SIZE(feature_name), "%s%d",
+ "SDE_DSPP_PA_SIXZONE_V", version);
+ sde_cp_crtc_install_blob_property(crtc, feature_name,
+ SDE_CP_CRTC_DSPP_SIXZONE,
+ sizeof(struct drm_msm_sixzone));
break;
default:
DRM_ERROR("version %d not supported\n", version);
diff --git a/drivers/gpu/drm/msm/sde/sde_color_processing.h b/drivers/gpu/drm/msm/sde/sde_color_processing.h
index aff07ef..7eb1738 100644
--- a/drivers/gpu/drm/msm/sde/sde_color_processing.h
+++ b/drivers/gpu/drm/msm/sde/sde_color_processing.h
@@ -26,7 +26,8 @@
enum sde_memcolor_type {
MEMCOLOR_SKIN = 0,
MEMCOLOR_SKY,
- MEMCOLOR_FOLIAGE
+ MEMCOLOR_FOLIAGE,
+ MEMCOLOR_MAX
};
/*
diff --git a/drivers/gpu/drm/msm/sde/sde_connector.h b/drivers/gpu/drm/msm/sde/sde_connector.h
index 4018441..35aec8c 100644
--- a/drivers/gpu/drm/msm/sde/sde_connector.h
+++ b/drivers/gpu/drm/msm/sde/sde_connector.h
@@ -134,11 +134,12 @@
* @drm_mode: Display mode set for the display
* @mode_info: Out parameter. information of the display mode
* @max_mixer_width: max width supported by HW layer mixer
+ * @display: Pointer to private display structure
* Returns: Zero on success
*/
int (*get_mode_info)(const struct drm_display_mode *drm_mode,
struct msm_mode_info *mode_info,
- u32 max_mixer_width);
+ u32 max_mixer_width, void *display);
/**
* enable_event - notify display of event registration/unregistration
@@ -319,7 +320,7 @@
* Returns: Pointer to associated private display structure
*/
#define sde_connector_get_display(C) \
- ((C) ? to_sde_connector((C))->display : 0)
+ ((C) ? to_sde_connector((C))->display : NULL)
/**
* sde_connector_get_panel - get sde connector's private panel pointer
@@ -335,7 +336,7 @@
* Returns: Pointer to associated private encoder structure
*/
#define sde_connector_get_encoder(C) \
- ((C) ? to_sde_connector((C))->encoder : 0)
+ ((C) ? to_sde_connector((C))->encoder : NULL)
/**
* sde_connector_get_propinfo - get sde connector's property info pointer
@@ -343,7 +344,7 @@
* Returns: Pointer to associated private property info structure
*/
#define sde_connector_get_propinfo(C) \
- ((C) ? &to_sde_connector((C))->property_info : 0)
+ ((C) ? &to_sde_connector((C))->property_info : NULL)
/**
* struct sde_connector_state - private connector status structure
diff --git a/drivers/gpu/drm/msm/sde/sde_core_perf.c b/drivers/gpu/drm/msm/sde/sde_core_perf.c
index 9ddca8c..6b36745 100644
--- a/drivers/gpu/drm/msm/sde/sde_core_perf.c
+++ b/drivers/gpu/drm/msm/sde/sde_core_perf.c
@@ -169,6 +169,7 @@
perf->core_clk_rate = kms->perf.fix_core_clk_rate;
}
+ SDE_EVT32(crtc->base.id, perf->core_clk_rate);
SDE_DEBUG(
"crtc=%d clk_rate=%llu core_ib=%llu core_ab=%llu llcc_ib=%llu llcc_ab=%llu mem_ib=%llu mem_ab=%llu\n",
crtc->base.id, perf->core_clk_rate,
@@ -293,7 +294,8 @@
}
static void _sde_core_perf_crtc_update_bus(struct sde_kms *kms,
- struct drm_crtc *crtc, u32 bus_id)
+ struct drm_crtc *crtc, u32 bus_id,
+ struct sde_core_perf_params *crtc_perf)
{
u64 bw_sum_of_intfs = 0, bus_ab_quota, bus_ib_quota;
struct sde_core_perf_params perf = { { 0 } };
@@ -303,21 +305,36 @@
struct sde_crtc_state *sde_cstate;
struct msm_drm_private *priv = kms->dev->dev_private;
+ u64 tmp_max_per_pipe_ib;
+ u64 tmp_bw_ctl;
+
drm_for_each_crtc(tmp_crtc, crtc->dev) {
if (_sde_core_perf_crtc_is_power_on(tmp_crtc) &&
_is_crtc_client_type_matches(tmp_crtc, curr_client_type,
&kms->perf)) {
- sde_cstate = to_sde_crtc_state(tmp_crtc->state);
+
+ if (crtc->base.id == tmp_crtc->base.id) {
+ /* for current crtc use the cached values */
+ tmp_max_per_pipe_ib =
+ crtc_perf->max_per_pipe_ib[bus_id];
+ tmp_bw_ctl = crtc_perf->bw_ctl[bus_id];
+ } else {
+ sde_cstate = to_sde_crtc_state(tmp_crtc->state);
+ tmp_max_per_pipe_ib =
+ sde_cstate->new_perf.max_per_pipe_ib[bus_id];
+ tmp_bw_ctl =
+ sde_cstate->new_perf.bw_ctl[bus_id];
+ }
perf.max_per_pipe_ib[bus_id] =
max(perf.max_per_pipe_ib[bus_id],
- sde_cstate->new_perf.max_per_pipe_ib[bus_id]);
+ tmp_max_per_pipe_ib);
- bw_sum_of_intfs += sde_cstate->new_perf.bw_ctl[bus_id];
+ bw_sum_of_intfs += tmp_bw_ctl;
- SDE_DEBUG("crtc=%d bus_id=%d bw=%llu\n",
+ SDE_DEBUG("crtc=%d bus_id=%d bw=%llu perf_pipe:%llu\n",
tmp_crtc->base.id, bus_id,
- sde_cstate->new_perf.bw_ctl[bus_id]);
+ tmp_bw_ctl, tmp_max_per_pipe_ib);
}
}
@@ -442,22 +459,32 @@
SDE_DEBUG("Release BW crtc=%d\n", crtc->base.id);
for (i = 0; i < SDE_POWER_HANDLE_DBUS_ID_MAX; i++) {
sde_crtc->cur_perf.bw_ctl[i] = 0;
- _sde_core_perf_crtc_update_bus(kms, crtc, i);
+ _sde_core_perf_crtc_update_bus(kms, crtc, i,
+ &sde_crtc->cur_perf);
}
}
}
-static u64 _sde_core_perf_get_core_clk_rate(struct sde_kms *kms)
+static u64 _sde_core_perf_get_core_clk_rate(struct sde_kms *kms,
+ struct sde_core_perf_params *crct_perf, struct drm_crtc *crtc)
{
u64 clk_rate = kms->perf.perf_tune.min_core_clk;
- struct drm_crtc *crtc;
+ struct drm_crtc *tmp_crtc;
struct sde_crtc_state *sde_cstate;
+ u64 tmp_rate;
- drm_for_each_crtc(crtc, kms->dev) {
- if (_sde_core_perf_crtc_is_power_on(crtc)) {
- sde_cstate = to_sde_crtc_state(crtc->state);
- clk_rate = max(sde_cstate->new_perf.core_clk_rate,
- clk_rate);
+ drm_for_each_crtc(tmp_crtc, kms->dev) {
+ if (_sde_core_perf_crtc_is_power_on(tmp_crtc)) {
+
+ if (crtc->base.id == tmp_crtc->base.id) {
+ /* for current CRTC, use the cached value */
+ tmp_rate = crct_perf->core_clk_rate;
+ } else {
+ sde_cstate = to_sde_crtc_state(tmp_crtc->state);
+ tmp_rate = sde_cstate->new_perf.core_clk_rate;
+ }
+ clk_rate = max(tmp_rate, clk_rate);
+
clk_rate = clk_round_rate(kms->perf.core_clk, clk_rate);
}
}
@@ -504,8 +531,17 @@
SDE_DEBUG("crtc:%d stop_req:%d core_clk:%llu\n",
crtc->base.id, stop_req, kms->perf.core_clk_rate);
+ /*
+ * cache the performance numbers in the crtc prior to the
+ * crtc kickoff, so the same numbers are used during the
+ * perf update that happens post kickoff.
+ */
+ if (params_changed)
+ memcpy(&sde_crtc->new_perf, &sde_cstate->new_perf,
+ sizeof(struct sde_core_perf_params));
+
old = &sde_crtc->cur_perf;
- new = &sde_cstate->new_perf;
+ new = &sde_crtc->new_perf;
if (_sde_core_perf_crtc_is_power_on(crtc) && !stop_req) {
for (i = 0; i < SDE_POWER_HANDLE_DBUS_ID_MAX; i++) {
@@ -580,7 +616,7 @@
for (i = 0; i < SDE_POWER_HANDLE_DBUS_ID_MAX; i++) {
if (update_bus & BIT(i))
- _sde_core_perf_crtc_update_bus(kms, crtc, i);
+ _sde_core_perf_crtc_update_bus(kms, crtc, i, old);
}
/*
@@ -588,9 +624,10 @@
* bandwidth is available before clock rate is increased.
*/
if (update_clk) {
- clk_rate = _sde_core_perf_get_core_clk_rate(kms);
+ clk_rate = _sde_core_perf_get_core_clk_rate(kms, old, crtc);
- SDE_EVT32(kms->dev, stop_req, clk_rate);
+ SDE_EVT32(kms->dev, stop_req, clk_rate, params_changed,
+ old->core_clk_rate, new->core_clk_rate);
ret = sde_power_clk_set_rate(&priv->phandle,
kms->perf.clk_name, clk_rate);
if (ret) {
diff --git a/drivers/gpu/drm/msm/sde/sde_crtc.c b/drivers/gpu/drm/msm/sde/sde_crtc.c
index 23ca2e9..03dab22 100644
--- a/drivers/gpu/drm/msm/sde/sde_crtc.c
+++ b/drivers/gpu/drm/msm/sde/sde_crtc.c
@@ -1124,7 +1124,7 @@
struct sde_crtc *sde_crtc;
struct sde_crtc_state *crtc_state;
const struct sde_rect *crtc_roi;
- struct drm_plane_state *pstate;
+ const struct drm_plane_state *pstate;
struct drm_plane *plane;
if (!crtc || !state)
@@ -1143,10 +1143,9 @@
if (sde_kms_rect_is_null(crtc_roi))
return 0;
- drm_atomic_crtc_state_for_each_plane(plane, state) {
+ drm_atomic_crtc_state_for_each_plane_state(plane, pstate, state) {
struct sde_rect plane_roi, intersection;
- pstate = drm_atomic_get_plane_state(state->state, plane);
if (IS_ERR_OR_NULL(pstate)) {
int rc = PTR_ERR(pstate);
@@ -1600,6 +1599,8 @@
SDE_DEBUG("crtc%d, secure_level%d old_valid_fb%d\n",
crtc->base.id, secure_level, old_valid_fb);
+ SDE_EVT32_VERBOSE(DRMID(crtc), secure_level, smmu_state->state,
+ old_valid_fb, SDE_EVTLOG_FUNC_ENTRY);
/**
* SMMU operations need to be delayed in case of
* video mode panels when switching back to non_secure
@@ -1609,7 +1610,7 @@
if (encoder->crtc != crtc)
continue;
- post_commit &= sde_encoder_check_mode(encoder,
+ post_commit |= sde_encoder_check_mode(encoder,
MSM_DISPLAY_CAP_VID_MODE);
}
@@ -1682,6 +1683,11 @@
SDE_DEBUG("SMMU State:%d, type:%d ops:%x\n", smmu_state->state,
smmu_state->transition_type, ops);
+ /* log only during actual transition times */
+ if (ops)
+ SDE_EVT32(DRMID(crtc), secure_level, translation_mode,
+ smmu_state->state, smmu_state->transition_type,
+ ops, old_valid_fb, SDE_EVTLOG_FUNC_EXIT);
return ops;
}
@@ -1725,6 +1731,9 @@
SDE_ERROR("Error:scm_call2, vmid (%lld): ret%d\n",
desc.args[3], ret);
}
+ SDE_EVT32(mem_protect_sd_ctrl_id,
+ desc.args[0], desc.args[3], num_sids,
+ sec_sid[0], sec_sid[1], ret);
kfree(sec_sid);
return ret;
@@ -1819,6 +1828,9 @@
smmu_state = &sde_crtc->smmu_state;
old_smmu_state = smmu_state->state;
+ SDE_EVT32(DRMID(crtc), smmu_state->state, smmu_state->transition_type,
+ post_commit, SDE_EVTLOG_FUNC_ENTRY);
+
if ((!smmu_state->transition_type) ||
((smmu_state->transition_type == POST_COMMIT) && !post_commit))
/* Bail out */
@@ -1903,6 +1915,9 @@
error:
smmu_state->transition_error = ret ? true : false;
+ SDE_EVT32(DRMID(crtc), smmu_state->state, smmu_state->transition_type,
+ smmu_state->transition_error, ret,
+ SDE_EVTLOG_FUNC_EXIT);
return ret;
}
@@ -2530,6 +2545,11 @@
}
count = ds_data.num_dest_scaler;
+ if (!count) {
+ SDE_DEBUG("no ds data available\n");
+ return 0;
+ }
+
if (!sde_crtc->num_mixers || count > sde_crtc->num_mixers ||
(count && (count != sde_crtc->num_mixers) &&
!(ds_data.ds_cfg[0].flags & SDE_DRM_DESTSCALER_PU_ENABLE))) {
@@ -3848,7 +3868,8 @@
atomic_read(&sde_crtc->frame_pending));
SDE_EVT32(DRMID(crtc), sde_crtc->enabled, sde_crtc->suspend,
- sde_crtc->vblank_requested);
+ sde_crtc->vblank_requested,
+ crtc->state->active, crtc->state->enable);
if (sde_crtc->enabled && !sde_crtc->suspend &&
sde_crtc->vblank_requested) {
ret = _sde_crtc_vblank_enable_no_lock(sde_crtc, false);
@@ -3893,11 +3914,14 @@
/**
* All callbacks are unregistered and frame done waits are complete
* at this point. No buffers are accessed by hardware.
- * reset the fence timeline if there is any issue.
+ * reset the fence timeline if crtc will not be enabled for this commit
*/
- sde_fence_signal(&sde_crtc->output_fence, ktime_get(), true);
- for (i = 0; i < cstate->num_connectors; ++i)
- sde_connector_commit_reset(cstate->connectors[i], ktime_get());
+ if (!crtc->state->active || !crtc->state->enable) {
+ sde_fence_signal(&sde_crtc->output_fence, ktime_get(), true);
+ for (i = 0; i < cstate->num_connectors; ++i)
+ sde_connector_commit_reset(cstate->connectors[i],
+ ktime_get());
+ }
memset(sde_crtc->mixers, 0, sizeof(sde_crtc->mixers));
sde_crtc->num_mixers = 0;
@@ -3933,6 +3957,22 @@
SDE_EVT32_VERBOSE(DRMID(crtc));
sde_crtc = to_sde_crtc(crtc);
+ mutex_lock(&sde_crtc->crtc_lock);
+ SDE_EVT32(DRMID(crtc), sde_crtc->enabled, sde_crtc->suspend,
+ sde_crtc->vblank_requested);
+
+ /* return early if crtc is already enabled */
+ if (sde_crtc->enabled) {
+ if (msm_is_mode_seamless_dms(&crtc->state->adjusted_mode))
+ SDE_DEBUG("%s extra crtc enable expected during DMS\n",
+ sde_crtc->name);
+ else
+ WARN(1, "%s unexpected crtc enable\n", sde_crtc->name);
+
+ mutex_unlock(&sde_crtc->crtc_lock);
+ return;
+ }
+
drm_for_each_encoder(encoder, crtc->dev) {
if (encoder->crtc != crtc)
continue;
@@ -3940,9 +3980,6 @@
sde_crtc_frame_event_cb, (void *)crtc);
}
- mutex_lock(&sde_crtc->crtc_lock);
- SDE_EVT32(DRMID(crtc), sde_crtc->enabled, sde_crtc->suspend,
- sde_crtc->vblank_requested);
if (!sde_crtc->enabled && !sde_crtc->suspend &&
sde_crtc->vblank_requested) {
ret = _sde_crtc_vblank_enable_no_lock(sde_crtc, true);
@@ -5367,6 +5404,7 @@
INIT_LIST_HEAD(&sde_crtc->rp_head);
init_completion(&sde_crtc->frame_done_comp);
+ sde_crtc->enabled = false;
INIT_LIST_HEAD(&sde_crtc->frame_event_list);
INIT_LIST_HEAD(&sde_crtc->user_event_list);
diff --git a/drivers/gpu/drm/msm/sde/sde_crtc.h b/drivers/gpu/drm/msm/sde/sde_crtc.h
index 59bfc47..a4c0501 100644
--- a/drivers/gpu/drm/msm/sde/sde_crtc.h
+++ b/drivers/gpu/drm/msm/sde/sde_crtc.h
@@ -280,6 +280,7 @@
struct sde_power_event *power_event;
struct sde_core_perf_params cur_perf;
+ struct sde_core_perf_params new_perf;
struct mutex rp_lock;
struct list_head rp_head;
diff --git a/drivers/gpu/drm/msm/sde/sde_encoder.c b/drivers/gpu/drm/msm/sde/sde_encoder.c
index 0e3b9c6..7d1e4bf 100644
--- a/drivers/gpu/drm/msm/sde/sde_encoder.c
+++ b/drivers/gpu/drm/msm/sde/sde_encoder.c
@@ -194,7 +194,7 @@
* clks and resources after IDLE_TIMEOUT time.
* @vsync_event_work: worker to handle vsync event for autorefresh
* @topology: topology of the display
- * @mode_set_complete: flag to indicate modeset completion
+ * @vblank_enabled: boolean to track userspace vblank vote
* @rsc_config: rsc configuration for display vtotal, fps, etc.
* @cur_conn_roi: current connector roi
* @prv_conn_roi: previous connector roi to optimize if unchanged
@@ -241,7 +241,7 @@
struct kthread_delayed_work delayed_off_work;
struct kthread_work vsync_event_work;
struct msm_display_topology topology;
- bool mode_set_complete;
+ bool vblank_enabled;
struct sde_rsc_cmd_config rsc_config;
struct sde_rect cur_conn_roi;
@@ -559,6 +559,7 @@
}
hw_res->topology = sde_enc->mode_info.topology;
+ hw_res->is_primary = sde_enc->disp_info.is_primary;
}
void sde_encoder_destroy(struct drm_encoder *drm_enc)
@@ -697,6 +698,7 @@
struct sde_kms *sde_kms;
const struct drm_display_mode *mode;
struct drm_display_mode *adj_mode;
+ struct sde_connector *sde_conn = NULL;
int i = 0;
int ret = 0;
@@ -713,6 +715,7 @@
sde_kms = to_sde_kms(priv->kms);
mode = &crtc_state->mode;
adj_mode = &crtc_state->adjusted_mode;
+ sde_conn = to_sde_connector(conn_state->connector);
SDE_EVT32(DRMID(drm_enc));
/*
@@ -742,17 +745,43 @@
}
}
- /* Reserve dynamic resources now. Indicating AtomicTest phase */
- if (!ret) {
- /*
- * Avoid reserving resources when mode set is pending. Topology
- * info may not be available to complete reservation.
+
+ if (!ret && sde_conn && drm_atomic_crtc_needs_modeset(crtc_state)) {
+ struct msm_display_topology *topology = NULL;
+
+ ret = sde_conn->ops.get_mode_info(adj_mode,
+ &sde_enc->mode_info,
+ sde_kms->catalog->max_mixer_width,
+ sde_conn->display);
+ if (ret) {
+ SDE_ERROR_ENC(sde_enc,
+ "failed to get mode info, rc = %d\n", ret);
+ return ret;
+ }
+
+ /* Reserve dynamic resources, indicating atomic_check phase */
+ ret = sde_rm_reserve(&sde_kms->rm, drm_enc, crtc_state,
+ conn_state, true);
+ if (ret) {
+ SDE_ERROR_ENC(sde_enc,
+ "RM failed to reserve resources, rc = %d\n",
+ ret);
+ return ret;
+ }
+
+ /**
+ * Update connector state with the topology selected for the
+ * resource set validated. Reset the topology if we are
+ * de-activating crtc.
*/
- if (drm_atomic_crtc_needs_modeset(crtc_state)
- && sde_enc->mode_set_complete) {
- ret = sde_rm_reserve(&sde_kms->rm, drm_enc, crtc_state,
- conn_state, true);
- sde_enc->mode_set_complete = false;
+ if (crtc_state->active)
+ topology = &sde_enc->mode_info.topology;
+
+ ret = sde_rm_update_topology(conn_state, topology);
+ if (ret) {
+ SDE_ERROR_ENC(sde_enc,
+ "RM failed to update topology, rc: %d\n", ret);
+ return ret;
}
}
@@ -1243,13 +1272,9 @@
static int _sde_encoder_dsc_disable(struct sde_encoder_virt *sde_enc)
{
- enum sde_rm_topology_name topology;
- struct drm_connector *drm_conn;
int i, ret = 0;
- struct sde_hw_pingpong *hw_pp[MAX_CHANNELS_PER_ENC];
- struct sde_hw_dsc *hw_dsc[MAX_CHANNELS_PER_ENC] = {NULL};
- int pp_count = 0;
- int dsc_count = 0;
+ struct sde_hw_pingpong *hw_pp = NULL;
+ struct sde_hw_dsc *hw_dsc = NULL;
if (!sde_enc || !sde_enc->phys_encs[0] ||
!sde_enc->phys_encs[0]->connector) {
@@ -1258,80 +1283,16 @@
return -EINVAL;
}
- drm_conn = sde_enc->phys_encs[0]->connector;
-
- topology = sde_connector_get_topology_name(drm_conn);
- if (topology == SDE_RM_TOPOLOGY_NONE) {
- SDE_ERROR_ENC(sde_enc, "topology not set yet\n");
- return -EINVAL;
- }
-
- switch (topology) {
- case SDE_RM_TOPOLOGY_SINGLEPIPE:
- case SDE_RM_TOPOLOGY_SINGLEPIPE_DSC:
- /* single PP */
- hw_pp[0] = sde_enc->hw_pp[0];
- hw_dsc[0] = sde_enc->hw_dsc[0];
- pp_count = 1;
- dsc_count = 1;
- break;
- case SDE_RM_TOPOLOGY_DUALPIPE_DSC:
- case SDE_RM_TOPOLOGY_DUALPIPE_3DMERGE_DSC:
- case SDE_RM_TOPOLOGY_DUALPIPE_DSCMERGE:
- /* dual dsc */
- for (i = 0; i < MAX_CHANNELS_PER_ENC; i++) {
- hw_dsc[i] = sde_enc->hw_dsc[i];
- if (hw_dsc[i])
- dsc_count++;
- }
- /* fall through */
- case SDE_RM_TOPOLOGY_DUALPIPE:
- case SDE_RM_TOPOLOGY_DUALPIPE_3DMERGE:
- /* dual pp */
- for (i = 0; i < MAX_CHANNELS_PER_ENC; i++) {
- hw_pp[i] = sde_enc->hw_pp[i];
- if (hw_pp[i])
- pp_count++;
- }
- break;
- default:
- SDE_DEBUG_ENC(sde_enc, "Unexpected topology:%d\n", topology);
- return -EINVAL;
- };
-
- SDE_EVT32(DRMID(&sde_enc->base), topology, pp_count, dsc_count);
-
- if (pp_count > MAX_CHANNELS_PER_ENC ||
- dsc_count > MAX_CHANNELS_PER_ENC) {
- SDE_ERROR_ENC(sde_enc, "Wrong count pp:%d dsc:%d top:%d\n",
- pp_count, dsc_count, topology);
- return -EINVAL;
- }
-
/* Disable DSC for all the pp's present in this topology */
- for (i = 0; i < pp_count; i++) {
+ for (i = 0; i < MAX_CHANNELS_PER_ENC; i++) {
+ hw_pp = sde_enc->hw_pp[i];
+ hw_dsc = sde_enc->hw_dsc[i];
- if (!hw_pp[i]) {
- SDE_ERROR_ENC(sde_enc, "null pp:%d top:%d cnt:%d\n",
- i, topology, pp_count);
- return -EINVAL;
- }
+ if (hw_pp && hw_pp->ops.disable_dsc)
+ hw_pp->ops.disable_dsc(hw_pp);
- if (hw_pp[i]->ops.disable_dsc)
- hw_pp[i]->ops.disable_dsc(hw_pp[i]);
- }
-
- /* Disable DSC HW */
- for (i = 0; i < dsc_count; i++) {
-
- if (!hw_dsc[i]) {
- SDE_ERROR_ENC(sde_enc, "null dsc:%d top:%d cnt:%d\n",
- i, topology, dsc_count);
- return -EINVAL;
- }
-
- if (hw_dsc[i]->ops.dsc_disable)
- hw_dsc[i]->ops.dsc_disable(hw_dsc[i]);
+ if (hw_dsc && hw_dsc->ops.dsc_disable)
+ hw_dsc->ops.dsc_disable(hw_dsc);
}
return ret;
@@ -1491,6 +1452,44 @@
}
+/* keep track of the userspace vblank during modeset */
+static void _sde_encoder_modeset_helper_locked(struct drm_encoder *drm_enc,
+ u32 sw_event)
+{
+ struct sde_encoder_virt *sde_enc;
+ bool enable;
+ int i;
+
+ if (!drm_enc) {
+ SDE_ERROR("invalid encoder\n");
+ return;
+ }
+
+ sde_enc = to_sde_encoder_virt(drm_enc);
+ SDE_DEBUG_ENC(sde_enc, "sw_event:%d, vblank_enabled:%d\n",
+ sw_event, sde_enc->vblank_enabled);
+
+ /* nothing to do if vblank not enabled by userspace */
+ if (!sde_enc->vblank_enabled)
+ return;
+
+ /* disable vblank on pre_modeset */
+ if (sw_event == SDE_ENC_RC_EVENT_PRE_MODESET)
+ enable = false;
+ /* enable vblank on post_modeset */
+ else if (sw_event == SDE_ENC_RC_EVENT_POST_MODESET)
+ enable = true;
+ else
+ return;
+
+ for (i = 0; i < sde_enc->num_phys_encs; i++) {
+ struct sde_encoder_phys *phys = sde_enc->phys_encs[i];
+
+ if (phys && phys->ops.control_vblank_irq)
+ phys->ops.control_vblank_irq(phys, enable);
+ }
+}
+
struct sde_rsc_client *sde_encoder_get_rsc_client(struct drm_encoder *drm_enc)
{
struct sde_encoder_virt *sde_enc;
@@ -1710,7 +1709,7 @@
else
idle_timeout = sde_enc->idle_timeout;
- if (!autorefresh_enabled)
+ if (!autorefresh_enabled && idle_timeout)
kthread_queue_delayed_work(
&disp_thread->worker,
&sde_enc->delayed_off_work,
@@ -1830,6 +1829,7 @@
}
_sde_encoder_irq_control(drm_enc, false);
+ _sde_encoder_modeset_helper_locked(drm_enc, sw_event);
SDE_EVT32(DRMID(drm_enc), sw_event, sde_enc->rc_state,
SDE_ENC_RC_STATE_MODESET, SDE_EVTLOG_FUNC_CASE5);
@@ -1852,6 +1852,7 @@
return -EINVAL;
}
+ _sde_encoder_modeset_helper_locked(drm_enc, sw_event);
_sde_encoder_irq_control(drm_enc, true);
_sde_encoder_update_rsc_client(drm_enc, NULL, true);
@@ -1959,7 +1960,8 @@
sde_conn = to_sde_connector(conn);
if (sde_conn) {
ret = sde_conn->ops.get_mode_info(adj_mode, &sde_enc->mode_info,
- sde_kms->catalog->max_mixer_width);
+ sde_kms->catalog->max_mixer_width,
+ sde_conn->display);
if (ret) {
SDE_ERROR_ENC(sde_enc,
"invalid topology for the mode\n");
@@ -2031,8 +2033,6 @@
if (msm_is_mode_seamless_dms(adj_mode))
sde_encoder_resource_control(&sde_enc->base,
SDE_ENC_RC_EVENT_POST_MODESET);
-
- sde_enc->mode_set_complete = true;
}
static void _sde_encoder_virt_enable_helper(struct drm_encoder *drm_enc)
@@ -2327,6 +2327,7 @@
if (phys && phys->ops.control_vblank_irq)
phys->ops.control_vblank_irq(phys, enable);
}
+ sde_enc->vblank_enabled = enable;
}
void sde_encoder_register_frame_event_callback(struct drm_encoder *drm_enc,
@@ -2441,8 +2442,8 @@
}
ctl = phys->hw_ctl;
- if (!ctl || !ctl->ops.trigger_flush) {
- SDE_ERROR("missing trigger cb\n");
+ if (!ctl || !phys->ops.trigger_flush) {
+ SDE_ERROR("missing ctl/trigger cb\n");
return;
}
@@ -2462,7 +2463,7 @@
if (extra_flush_bits && ctl->ops.update_pending_flush)
ctl->ops.update_pending_flush(ctl, extra_flush_bits);
- ctl->ops.trigger_flush(ctl);
+ phys->ops.trigger_flush(phys);
if (ctl->ops.get_pending_flush)
SDE_EVT32(DRMID(drm_enc), phys->intf_idx, pending_kickoff_cnt,
@@ -2502,6 +2503,20 @@
phys->ops.trigger_start(phys);
}
+void sde_encoder_helper_trigger_flush(struct sde_encoder_phys *phys_enc)
+{
+ struct sde_hw_ctl *ctl;
+
+ if (!phys_enc) {
+ SDE_ERROR("invalid encoder\n");
+ return;
+ }
+
+ ctl = phys_enc->hw_ctl;
+ if (ctl && ctl->ops.trigger_flush)
+ ctl->ops.trigger_flush(ctl);
+}
+
void sde_encoder_helper_trigger_start(struct sde_encoder_phys *phys_enc)
{
struct sde_hw_ctl *ctl;
@@ -3809,6 +3824,7 @@
kthread_init_delayed_work(&sde_enc->delayed_off_work,
sde_encoder_off_work);
sde_enc->idle_timeout = IDLE_TIMEOUT;
+ sde_enc->vblank_enabled = false;
kthread_init_work(&sde_enc->vsync_event_work,
sde_encoder_vsync_event_work_handler);
diff --git a/drivers/gpu/drm/msm/sde/sde_encoder.h b/drivers/gpu/drm/msm/sde/sde_encoder.h
index baf59b4..9a9ff86 100644
--- a/drivers/gpu/drm/msm/sde/sde_encoder.h
+++ b/drivers/gpu/drm/msm/sde/sde_encoder.h
@@ -40,6 +40,7 @@
* @needs_cdm: Encoder requests a CDM based on pixel format conversion needs
* @display_num_of_h_tiles: Number of horizontal tiles in case of split
* interface
+ * @is_primary: set to true if the display is primary display
* @topology: Topology of the display
*/
struct sde_encoder_hw_resources {
@@ -47,6 +48,7 @@
enum sde_intf_mode wbs[WB_MAX];
bool needs_cdm;
u32 display_num_of_h_tiles;
+ bool is_primary;
struct msm_display_topology topology;
};
diff --git a/drivers/gpu/drm/msm/sde/sde_encoder_phys.h b/drivers/gpu/drm/msm/sde/sde_encoder_phys.h
index 8bd69e3..e5a4da4 100644
--- a/drivers/gpu/drm/msm/sde/sde_encoder_phys.h
+++ b/drivers/gpu/drm/msm/sde/sde_encoder_phys.h
@@ -117,6 +117,7 @@
* @prepare_for_kickoff: Do any work necessary prior to a kickoff
* For CMD encoder, may wait for previous tx done
* @handle_post_kickoff: Do any work necessary post-kickoff work
+ * @trigger_flush: Process flush event on physical encoder
* @trigger_start: Process start event on physical encoder
* @needs_single_flush: Whether encoder slaves need to be flushed
* @setup_misr: Sets up MISR, enable and disables based on sysfs
@@ -162,6 +163,7 @@
void (*prepare_for_kickoff)(struct sde_encoder_phys *phys_enc,
struct sde_encoder_kickoff_params *params);
void (*handle_post_kickoff)(struct sde_encoder_phys *phys_enc);
+ void (*trigger_flush)(struct sde_encoder_phys *phys_enc);
void (*trigger_start)(struct sde_encoder_phys *phys_enc);
bool (*needs_single_flush)(struct sde_encoder_phys *phys_enc);
@@ -466,6 +468,14 @@
struct sde_rect *wb_roi);
/**
+ * sde_encoder_helper_trigger_flush - control flush helper function
+ * This helper function may be optionally specified by physical
+ * encoders if they require ctl_flush triggering.
+ * @phys_enc: Pointer to physical encoder structure
+ */
+void sde_encoder_helper_trigger_flush(struct sde_encoder_phys *phys_enc);
+
+/**
* sde_encoder_helper_trigger_start - control start helper function
* This helper function may be optionally specified by physical
* encoders if they require ctl_start triggering.
diff --git a/drivers/gpu/drm/msm/sde/sde_encoder_phys_cmd.c b/drivers/gpu/drm/msm/sde/sde_encoder_phys_cmd.c
index 4291098..9976f85 100644
--- a/drivers/gpu/drm/msm/sde/sde_encoder_phys_cmd.c
+++ b/drivers/gpu/drm/msm/sde/sde_encoder_phys_cmd.c
@@ -177,6 +177,13 @@
return;
SDE_ATRACE_BEGIN("pp_done_irq");
+
+ /* handle rare cases where the ctl_start_irq is not received */
+ if (sde_encoder_phys_cmd_is_master(phys_enc)
+ && atomic_add_unless(&phys_enc->pending_retire_fence_cnt, -1, 0))
+ phys_enc->parent_ops.handle_frame_done(phys_enc->parent,
+ phys_enc, SDE_ENCODER_FRAME_EVENT_SIGNAL_RETIRE_FENCE);
+
/* notify all synchronous clients first, then asynchronous clients */
if (phys_enc->parent_ops.handle_frame_done)
phys_enc->parent_ops.handle_frame_done(phys_enc->parent,
@@ -1254,6 +1261,7 @@
ops->prepare_for_kickoff = sde_encoder_phys_cmd_prepare_for_kickoff;
ops->wait_for_tx_complete = sde_encoder_phys_cmd_wait_for_tx_complete;
ops->wait_for_vblank = sde_encoder_phys_cmd_wait_for_vblank;
+ ops->trigger_flush = sde_encoder_helper_trigger_flush;
ops->trigger_start = sde_encoder_phys_cmd_trigger_start;
ops->needs_single_flush = sde_encoder_phys_cmd_needs_single_flush;
ops->hw_reset = sde_encoder_helper_hw_reset;
diff --git a/drivers/gpu/drm/msm/sde/sde_encoder_phys_vid.c b/drivers/gpu/drm/msm/sde/sde_encoder_phys_vid.c
index efa882a..a983b7c 100644
--- a/drivers/gpu/drm/msm/sde/sde_encoder_phys_vid.c
+++ b/drivers/gpu/drm/msm/sde/sde_encoder_phys_vid.c
@@ -973,6 +973,7 @@
ops->needs_single_flush = sde_encoder_phys_vid_needs_single_flush;
ops->setup_misr = sde_encoder_phys_vid_setup_misr;
ops->collect_misr = sde_encoder_phys_vid_collect_misr;
+ ops->trigger_flush = sde_encoder_helper_trigger_flush;
ops->hw_reset = sde_encoder_helper_hw_reset;
ops->get_line_count = sde_encoder_phys_vid_get_line_count;
ops->wait_dma_trigger = sde_encoder_phys_vid_wait_dma_trigger;
diff --git a/drivers/gpu/drm/msm/sde/sde_encoder_phys_wb.c b/drivers/gpu/drm/msm/sde/sde_encoder_phys_wb.c
index 12115756..d7084dd 100644
--- a/drivers/gpu/drm/msm/sde/sde_encoder_phys_wb.c
+++ b/drivers/gpu/drm/msm/sde/sde_encoder_phys_wb.c
@@ -458,10 +458,11 @@
SDE_DEBUG("[roi:%u,%u,%u,%u]\n", wb_roi.x, wb_roi.y,
wb_roi.w, wb_roi.h);
+ /* bypass check if commit with no framebuffer */
fb = sde_wb_connector_state_get_output_fb(conn_state);
if (!fb) {
- SDE_ERROR("no output framebuffer\n");
- return -EINVAL;
+ SDE_DEBUG("no output framebuffer\n");
+ return 0;
}
SDE_DEBUG("[fb_id:%u][fb:%u,%u]\n", fb->base.id,
@@ -847,7 +848,14 @@
return -EWOULDBLOCK;
}
- SDE_EVT32(DRMID(phys_enc->parent), WBID(wb_enc), wb_enc->frame_count);
+ SDE_EVT32(DRMID(phys_enc->parent), WBID(wb_enc), wb_enc->frame_count,
+ !!wb_enc->wb_fb);
+
+ /* signal completion if commit with no framebuffer */
+ if (!wb_enc->wb_fb) {
+ SDE_DEBUG("no output framebuffer\n");
+ sde_encoder_phys_wb_done_irq(wb_enc, wb_enc->irq_idx);
+ }
ret = wait_for_completion_timeout(&wb_enc->wbdone_complete,
msecs_to_jiffies(timeout));
@@ -950,6 +958,30 @@
}
/**
+ * sde_encoder_phys_wb_trigger_flush - trigger flush processing
+ * @phys_enc: Pointer to physical encoder
+ */
+static void sde_encoder_phys_wb_trigger_flush(struct sde_encoder_phys *phys_enc)
+{
+ struct sde_encoder_phys_wb *wb_enc = to_sde_encoder_phys_wb(phys_enc);
+
+ if (!phys_enc || !wb_enc->hw_wb) {
+ SDE_ERROR("invalid encoder\n");
+ return;
+ }
+
+ SDE_DEBUG("[wb:%d]\n", wb_enc->hw_wb->idx - WB_0);
+
+ /* clear pending flush if commit with no framebuffer */
+ if (!wb_enc->wb_fb) {
+ SDE_DEBUG("no output framebuffer\n");
+ return;
+ }
+
+ sde_encoder_helper_trigger_flush(phys_enc);
+}
+
+/**
* sde_encoder_phys_wb_handle_post_kickoff - post-kickoff processing
* @phys_enc: Pointer to physical encoder
*/
@@ -1167,7 +1199,7 @@
struct sde_encoder_phys_wb *wb_enc = to_sde_encoder_phys_wb(phys_enc);
struct sde_hw_wb *hw_wb;
struct drm_framebuffer *fb;
- const struct sde_format *fmt;
+ const struct sde_format *fmt = NULL;
if (!phys_enc) {
SDE_ERROR("invalid encoder\n");
@@ -1175,22 +1207,19 @@
}
fb = sde_wb_connector_state_get_output_fb(conn_state);
- if (!fb) {
- SDE_ERROR("no output framebuffer\n");
- return;
- }
-
- fmt = sde_get_sde_format_ext(fb->pixel_format, fb->modifier,
- drm_format_num_planes(fb->pixel_format));
- if (!fmt) {
- SDE_ERROR("unsupported output pixel format:%d\n",
- fb->pixel_format);
- return;
+ if (fb) {
+ fmt = sde_get_sde_format_ext(fb->pixel_format, fb->modifier,
+ drm_format_num_planes(fb->pixel_format));
+ if (!fmt) {
+ SDE_ERROR("unsupported output pixel format:%d\n",
+ fb->pixel_format);
+ return;
+ }
}
hw_wb = wb_enc->hw_wb;
hw_res->wbs[hw_wb->idx - WB_0] = phys_enc->intf_mode;
- hw_res->needs_cdm = SDE_FORMAT_IS_YUV(fmt);
+ hw_res->needs_cdm = fmt ? SDE_FORMAT_IS_YUV(fmt) : false;
SDE_DEBUG("[wb:%d] intf_mode=%d needs_cdm=%d\n", hw_wb->idx - WB_0,
hw_res->wbs[hw_wb->idx - WB_0],
hw_res->needs_cdm);
@@ -1274,6 +1303,7 @@
ops->wait_for_commit_done = sde_encoder_phys_wb_wait_for_commit_done;
ops->prepare_for_kickoff = sde_encoder_phys_wb_prepare_for_kickoff;
ops->handle_post_kickoff = sde_encoder_phys_wb_handle_post_kickoff;
+ ops->trigger_flush = sde_encoder_phys_wb_trigger_flush;
ops->trigger_start = sde_encoder_helper_trigger_start;
ops->hw_reset = sde_encoder_helper_hw_reset;
}
diff --git a/drivers/gpu/drm/msm/sde/sde_formats.c b/drivers/gpu/drm/msm/sde/sde_formats.c
index 6880f7b..d09054e 100644
--- a/drivers/gpu/drm/msm/sde/sde_formats.c
+++ b/drivers/gpu/drm/msm/sde/sde_formats.c
@@ -263,109 +263,109 @@
INTERLEAVED_RGB_FMT(RGB565,
0, COLOR_5BIT, COLOR_6BIT, COLOR_5BIT,
- C1_B_Cb, C0_G_Y, C2_R_Cr, 0, 3,
+ C2_R_Cr, C0_G_Y, C1_B_Cb, 0, 3,
false, 2, 0,
SDE_FETCH_LINEAR, 1),
INTERLEAVED_RGB_FMT(BGR565,
0, COLOR_5BIT, COLOR_6BIT, COLOR_5BIT,
- C2_R_Cr, C0_G_Y, C1_B_Cb, 0, 3,
+ C1_B_Cb, C0_G_Y, C2_R_Cr, 0, 3,
false, 2, 0,
SDE_FETCH_LINEAR, 1),
INTERLEAVED_RGB_FMT(ARGB1555,
COLOR_ALPHA_1BIT, COLOR_5BIT, COLOR_5BIT, COLOR_5BIT,
- C1_B_Cb, C0_G_Y, C2_R_Cr, C3_ALPHA, 4,
+ C2_R_Cr, C0_G_Y, C1_B_Cb, C3_ALPHA, 4,
true, 2, 0,
SDE_FETCH_LINEAR, 1),
INTERLEAVED_RGB_FMT(ABGR1555,
COLOR_ALPHA_1BIT, COLOR_5BIT, COLOR_5BIT, COLOR_5BIT,
- C2_R_Cr, C0_G_Y, C1_B_Cb, C3_ALPHA, 4,
+ C1_B_Cb, C0_G_Y, C2_R_Cr, C3_ALPHA, 4,
true, 2, 0,
SDE_FETCH_LINEAR, 1),
INTERLEAVED_RGB_FMT(RGBA5551,
COLOR_ALPHA_1BIT, COLOR_5BIT, COLOR_5BIT, COLOR_5BIT,
- C3_ALPHA, C1_B_Cb, C0_G_Y, C2_R_Cr, 4,
+ C3_ALPHA, C2_R_Cr, C0_G_Y, C1_B_Cb, 4,
true, 2, 0,
SDE_FETCH_LINEAR, 1),
INTERLEAVED_RGB_FMT(BGRA5551,
COLOR_ALPHA_1BIT, COLOR_5BIT, COLOR_5BIT, COLOR_5BIT,
- C3_ALPHA, C2_R_Cr, C0_G_Y, C1_B_Cb, 4,
+ C3_ALPHA, C1_B_Cb, C0_G_Y, C2_R_Cr, 4,
true, 2, 0,
SDE_FETCH_LINEAR, 1),
INTERLEAVED_RGB_FMT(XRGB1555,
COLOR_ALPHA_1BIT, COLOR_5BIT, COLOR_5BIT, COLOR_5BIT,
- C1_B_Cb, C0_G_Y, C2_R_Cr, C3_ALPHA, 4,
+ C2_R_Cr, C0_G_Y, C1_B_Cb, C3_ALPHA, 4,
false, 2, 0,
SDE_FETCH_LINEAR, 1),
INTERLEAVED_RGB_FMT(XBGR1555,
COLOR_ALPHA_1BIT, COLOR_5BIT, COLOR_5BIT, COLOR_5BIT,
- C2_R_Cr, C0_G_Y, C1_B_Cb, C3_ALPHA, 4,
+ C1_B_Cb, C0_G_Y, C2_R_Cr, C3_ALPHA, 4,
false, 2, 0,
SDE_FETCH_LINEAR, 1),
INTERLEAVED_RGB_FMT(RGBX5551,
COLOR_ALPHA_1BIT, COLOR_5BIT, COLOR_5BIT, COLOR_5BIT,
- C3_ALPHA, C1_B_Cb, C0_G_Y, C2_R_Cr, 4,
+ C3_ALPHA, C2_R_Cr, C0_G_Y, C1_B_Cb, 4,
false, 2, 0,
SDE_FETCH_LINEAR, 1),
INTERLEAVED_RGB_FMT(BGRX5551,
COLOR_ALPHA_1BIT, COLOR_5BIT, COLOR_5BIT, COLOR_5BIT,
- C3_ALPHA, C2_R_Cr, C0_G_Y, C1_B_Cb, 4,
+ C3_ALPHA, C1_B_Cb, C0_G_Y, C2_R_Cr, 4,
false, 2, 0,
SDE_FETCH_LINEAR, 1),
INTERLEAVED_RGB_FMT(ARGB4444,
COLOR_ALPHA_4BIT, COLOR_4BIT, COLOR_4BIT, COLOR_4BIT,
- C1_B_Cb, C0_G_Y, C2_R_Cr, C3_ALPHA, 4,
+ C2_R_Cr, C0_G_Y, C1_B_Cb, C3_ALPHA, 4,
true, 2, 0,
SDE_FETCH_LINEAR, 1),
INTERLEAVED_RGB_FMT(ABGR4444,
COLOR_ALPHA_4BIT, COLOR_4BIT, COLOR_4BIT, COLOR_4BIT,
- C2_R_Cr, C0_G_Y, C1_B_Cb, C3_ALPHA, 4,
+ C1_B_Cb, C0_G_Y, C2_R_Cr, C3_ALPHA, 4,
true, 2, 0,
SDE_FETCH_LINEAR, 1),
INTERLEAVED_RGB_FMT(RGBA4444,
COLOR_ALPHA_4BIT, COLOR_4BIT, COLOR_4BIT, COLOR_4BIT,
- C3_ALPHA, C1_B_Cb, C0_G_Y, C2_R_Cr, 4,
+ C3_ALPHA, C2_R_Cr, C0_G_Y, C1_B_Cb, 4,
true, 2, 0,
SDE_FETCH_LINEAR, 1),
INTERLEAVED_RGB_FMT(BGRA4444,
COLOR_ALPHA_4BIT, COLOR_4BIT, COLOR_4BIT, COLOR_4BIT,
- C3_ALPHA, C2_R_Cr, C0_G_Y, C1_B_Cb, 4,
+ C3_ALPHA, C1_B_Cb, C0_G_Y, C2_R_Cr, 4,
true, 2, 0,
SDE_FETCH_LINEAR, 1),
INTERLEAVED_RGB_FMT(XRGB4444,
COLOR_ALPHA_4BIT, COLOR_4BIT, COLOR_4BIT, COLOR_4BIT,
- C1_B_Cb, C0_G_Y, C2_R_Cr, C3_ALPHA, 4,
+ C2_R_Cr, C0_G_Y, C1_B_Cb, C3_ALPHA, 4,
false, 2, 0,
SDE_FETCH_LINEAR, 1),
INTERLEAVED_RGB_FMT(XBGR4444,
COLOR_ALPHA_4BIT, COLOR_4BIT, COLOR_4BIT, COLOR_4BIT,
- C2_R_Cr, C0_G_Y, C1_B_Cb, C3_ALPHA, 4,
+ C1_B_Cb, C0_G_Y, C2_R_Cr, C3_ALPHA, 4,
false, 2, 0,
SDE_FETCH_LINEAR, 1),
INTERLEAVED_RGB_FMT(RGBX4444,
COLOR_ALPHA_4BIT, COLOR_4BIT, COLOR_4BIT, COLOR_4BIT,
- C3_ALPHA, C1_B_Cb, C0_G_Y, C2_R_Cr, 4,
+ C3_ALPHA, C2_R_Cr, C0_G_Y, C1_B_Cb, 4,
false, 2, 0,
SDE_FETCH_LINEAR, 1),
INTERLEAVED_RGB_FMT(BGRX4444,
COLOR_ALPHA_4BIT, COLOR_4BIT, COLOR_4BIT, COLOR_4BIT,
- C3_ALPHA, C2_R_Cr, C0_G_Y, C1_B_Cb, 4,
+ C3_ALPHA, C1_B_Cb, C0_G_Y, C2_R_Cr, 4,
false, 2, 0,
SDE_FETCH_LINEAR, 1),
diff --git a/drivers/gpu/drm/msm/sde/sde_hw_catalog.c b/drivers/gpu/drm/msm/sde/sde_hw_catalog.c
index aea2c6b..b8b0967 100644
--- a/drivers/gpu/drm/msm/sde/sde_hw_catalog.c
+++ b/drivers/gpu/drm/msm/sde/sde_hw_catalog.c
@@ -122,6 +122,7 @@
enum {
HW_OFF,
HW_LEN,
+ HW_DISP,
HW_PROP_MAX,
};
@@ -166,7 +167,10 @@
PERF_XTRA_PREFILL_LINES,
PERF_AMORTIZABLE_THRESHOLD,
PERF_DANGER_LUT,
- PERF_SAFE_LUT,
+ PERF_SAFE_LUT_LINEAR,
+ PERF_SAFE_LUT_MACROTILE,
+ PERF_SAFE_LUT_NRT,
+ PERF_SAFE_LUT_CWB,
PERF_QOS_LUT_LINEAR,
PERF_QOS_LUT_MACROTILE,
PERF_QOS_LUT_NRT,
@@ -288,6 +292,7 @@
MIXER_LEN,
MIXER_PAIR_MASK,
MIXER_BLOCKS,
+ MIXER_DISP,
MIXER_PROP_MAX,
};
@@ -424,7 +429,14 @@
{PERF_AMORTIZABLE_THRESHOLD, "qcom,sde-amortizable-threshold",
false, PROP_TYPE_U32},
{PERF_DANGER_LUT, "qcom,sde-danger-lut", false, PROP_TYPE_U32_ARRAY},
- {PERF_SAFE_LUT, "qcom,sde-safe-lut", false, PROP_TYPE_U32_ARRAY},
+ {PERF_SAFE_LUT_LINEAR, "qcom,sde-safe-lut-linear", false,
+ PROP_TYPE_U32_ARRAY},
+ {PERF_SAFE_LUT_MACROTILE, "qcom,sde-safe-lut-macrotile", false,
+ PROP_TYPE_U32_ARRAY},
+ {PERF_SAFE_LUT_NRT, "qcom,sde-safe-lut-nrt", false,
+ PROP_TYPE_U32_ARRAY},
+ {PERF_SAFE_LUT_CWB, "qcom,sde-safe-lut-cwb", false,
+ PROP_TYPE_U32_ARRAY},
{PERF_QOS_LUT_LINEAR, "qcom,sde-qos-lut-linear", false,
PROP_TYPE_U32_ARRAY},
{PERF_QOS_LUT_MACROTILE, "qcom,sde-qos-lut-macrotile", false,
@@ -433,6 +445,7 @@
PROP_TYPE_U32_ARRAY},
{PERF_QOS_LUT_CWB, "qcom,sde-qos-lut-cwb", false,
PROP_TYPE_U32_ARRAY},
+
{PERF_CDP_SETTING, "qcom,sde-cdp-setting", false,
PROP_TYPE_U32_ARRAY},
};
@@ -475,6 +488,7 @@
static struct sde_prop_type ctl_prop[] = {
{HW_OFF, "qcom,sde-ctl-off", true, PROP_TYPE_U32_ARRAY},
{HW_LEN, "qcom,sde-ctl-size", false, PROP_TYPE_U32},
+ {HW_DISP, "qcom,sde-ctl-display-pref", false, PROP_TYPE_STRING_ARRAY},
};
struct sde_prop_type mixer_blend_prop[] = {
@@ -488,6 +502,8 @@
{MIXER_PAIR_MASK, "qcom,sde-mixer-pair-mask", true,
PROP_TYPE_U32_ARRAY},
{MIXER_BLOCKS, "qcom,sde-mixer-blocks", false, PROP_TYPE_NODE},
+ {MIXER_DISP, "qcom,sde-mixer-display-pref", false,
+ PROP_TYPE_STRING_ARRAY},
};
static struct sde_prop_type mixer_blocks_prop[] = {
@@ -1292,6 +1308,8 @@
goto end;
for (i = 0; i < off_count; i++) {
+ const char *disp_pref = NULL;
+
ctl = sde_cfg->ctl + i;
ctl->base = PROP_VALUE_ACCESS(prop_value, HW_OFF, i);
ctl->len = PROP_VALUE_ACCESS(prop_value, HW_LEN, 0);
@@ -1299,6 +1317,10 @@
snprintf(ctl->name, SDE_HW_BLK_NAME_LEN, "ctl_%u",
ctl->id - CTL_0);
+ of_property_read_string_index(np,
+ ctl_prop[HW_DISP].prop_name, i, &disp_pref);
+ if (disp_pref && !strcmp(disp_pref, "primary"))
+ set_bit(SDE_CTL_PRIMARY_PREF, &ctl->features);
if (i < MAX_SPLIT_DISPLAY_CTL)
set_bit(SDE_CTL_SPLIT_DISPLAY, &ctl->features);
if (i < MAX_PP_SPLIT_DISPLAY_CTL)
@@ -1306,7 +1328,6 @@
if (sde_cfg->has_sbuf)
set_bit(SDE_CTL_SBUF, &ctl->features);
}
-
end:
kfree(prop_value);
return rc;
@@ -1326,8 +1347,9 @@
u32 off_count, blend_off_count, max_blendstages, lm_pair_mask;
struct sde_lm_cfg *mixer;
struct sde_lm_sub_blks *sblk;
- int pp_count, dspp_count, ds_count;
+ int pp_count, dspp_count, ds_count, mixer_count;
u32 pp_idx, dspp_idx, ds_idx;
+ u32 mixer_base;
struct device_node *snp = NULL;
if (!sde_cfg) {
@@ -1349,8 +1371,6 @@
if (rc)
goto end;
- sde_cfg->mixer_count = off_count;
-
rc = _read_dt_entry(np, mixer_prop, ARRAY_SIZE(mixer_prop), prop_count,
prop_exists, prop_value);
if (rc)
@@ -1398,8 +1418,16 @@
if (rc)
goto end;
- for (i = 0, pp_idx = 0, dspp_idx = 0, ds_idx = 0; i < off_count; i++) {
- mixer = sde_cfg->mixer + i;
+ for (i = 0, mixer_count = 0, pp_idx = 0, dspp_idx = 0,
+ ds_idx = 0; i < off_count; i++) {
+ const char *disp_pref = NULL;
+
+ mixer_base = PROP_VALUE_ACCESS(prop_value, MIXER_OFF, i);
+ if (!mixer_base)
+ continue;
+
+ mixer = sde_cfg->mixer + mixer_count;
+
sblk = kzalloc(sizeof(*sblk), GFP_KERNEL);
if (!sblk) {
rc = -ENOMEM;
@@ -1408,7 +1436,7 @@
}
mixer->sblk = sblk;
- mixer->base = PROP_VALUE_ACCESS(prop_value, MIXER_OFF, i);
+ mixer->base = mixer_base;
mixer->len = PROP_VALUE_ACCESS(prop_value, MIXER_LEN, 0);
mixer->id = LM_0 + i;
snprintf(mixer->name, SDE_HW_BLK_NAME_LEN, "lm_%u",
@@ -1435,23 +1463,24 @@
if (sde_cfg->has_dim_layer)
set_bit(SDE_DIM_LAYER, &mixer->features);
- if ((i < ROT_LM_OFFSET) || (i >= LINE_LM_OFFSET)) {
- mixer->pingpong = pp_count > 0 ? pp_idx + PINGPONG_0
- : PINGPONG_MAX;
- mixer->dspp = dspp_count > 0 ? dspp_idx + DSPP_0
- : DSPP_MAX;
- mixer->ds = ds_count > 0 ? ds_idx + DS_0 : DS_MAX;
- pp_count--;
- dspp_count--;
- ds_count--;
- pp_idx++;
- dspp_idx++;
- ds_idx++;
- } else {
- mixer->pingpong = PINGPONG_MAX;
- mixer->dspp = DSPP_MAX;
- mixer->ds = DS_MAX;
- }
+ of_property_read_string_index(np,
+ mixer_prop[MIXER_DISP].prop_name, i, &disp_pref);
+ if (disp_pref && !strcmp(disp_pref, "primary"))
+ set_bit(SDE_DISP_PRIMARY_PREF, &mixer->features);
+
+ mixer->pingpong = pp_count > 0 ? pp_idx + PINGPONG_0
+ : PINGPONG_MAX;
+ mixer->dspp = dspp_count > 0 ? dspp_idx + DSPP_0
+ : DSPP_MAX;
+ mixer->ds = ds_count > 0 ? ds_idx + DS_0 : DS_MAX;
+ pp_count--;
+ dspp_count--;
+ ds_count--;
+ pp_idx++;
+ dspp_idx++;
+ ds_idx++;
+
+ mixer_count++;
sblk->gc.id = SDE_MIXER_GC;
if (blocks_prop_value && blocks_prop_exists[MIXER_GC_PROP]) {
@@ -1463,6 +1492,7 @@
set_bit(SDE_MIXER_GC, &mixer->features);
}
}
+ sde_cfg->mixer_count = mixer_count;
end:
kfree(prop_value);
@@ -2805,8 +2835,23 @@
if (rc)
goto freeprop;
- rc = _validate_dt_entry(np, &sde_perf_prop[PERF_SAFE_LUT], 1,
- &prop_count[PERF_SAFE_LUT], NULL);
+ rc = _validate_dt_entry(np, &sde_perf_prop[PERF_SAFE_LUT_LINEAR], 1,
+ &prop_count[PERF_SAFE_LUT_LINEAR], NULL);
+ if (rc)
+ goto freeprop;
+
+ rc = _validate_dt_entry(np, &sde_perf_prop[PERF_SAFE_LUT_MACROTILE], 1,
+ &prop_count[PERF_SAFE_LUT_MACROTILE], NULL);
+ if (rc)
+ goto freeprop;
+
+ rc = _validate_dt_entry(np, &sde_perf_prop[PERF_SAFE_LUT_NRT], 1,
+ &prop_count[PERF_SAFE_LUT_NRT], NULL);
+ if (rc)
+ goto freeprop;
+
+ rc = _validate_dt_entry(np, &sde_perf_prop[PERF_SAFE_LUT_CWB], 1,
+ &prop_count[PERF_SAFE_LUT_CWB], NULL);
if (rc)
goto freeprop;
@@ -2931,15 +2976,46 @@
}
}
- if (prop_exists[PERF_SAFE_LUT] && prop_count[PERF_SAFE_LUT] <=
- SDE_QOS_LUT_USAGE_MAX) {
- for (j = 0; j < prop_count[PERF_SAFE_LUT]; j++) {
- cfg->perf.safe_lut_tbl[j] =
- PROP_VALUE_ACCESS(prop_value,
- PERF_SAFE_LUT, j);
- SDE_DEBUG("safe usage:%d lut:0x%x\n",
- j, cfg->perf.safe_lut_tbl[j]);
+ for (j = 0; j < SDE_QOS_LUT_USAGE_MAX; j++) {
+ static const u32 safe_key[SDE_QOS_LUT_USAGE_MAX] = {
+ [SDE_QOS_LUT_USAGE_LINEAR] =
+ PERF_SAFE_LUT_LINEAR,
+ [SDE_QOS_LUT_USAGE_MACROTILE] =
+ PERF_SAFE_LUT_MACROTILE,
+ [SDE_QOS_LUT_USAGE_NRT] =
+ PERF_SAFE_LUT_NRT,
+ [SDE_QOS_LUT_USAGE_CWB] =
+ PERF_SAFE_LUT_CWB,
+ };
+ const u32 entry_size = 2;
+ int m, count;
+ int key = safe_key[j];
+
+ if (!prop_exists[key])
+ continue;
+
+ count = prop_count[key] / entry_size;
+
+ cfg->perf.sfe_lut_tbl[j].entries = kcalloc(count,
+ sizeof(struct sde_qos_lut_entry), GFP_KERNEL);
+ if (!cfg->perf.sfe_lut_tbl[j].entries) {
+ rc = -ENOMEM;
+ goto freeprop;
}
+
+ for (k = 0, m = 0; k < count; k++, m += entry_size) {
+ u64 lut_lo;
+
+ cfg->perf.sfe_lut_tbl[j].entries[k].fl =
+ PROP_VALUE_ACCESS(prop_value, key, m);
+ lut_lo = PROP_VALUE_ACCESS(prop_value, key, m + 1);
+ cfg->perf.sfe_lut_tbl[j].entries[k].lut = lut_lo;
+ SDE_DEBUG("safe usage:%d.%d fl:%d lut:0x%llx\n",
+ j, k,
+ cfg->perf.sfe_lut_tbl[j].entries[k].fl,
+ cfg->perf.sfe_lut_tbl[j].entries[k].lut);
+ }
+ cfg->perf.sfe_lut_tbl[j].nentry = count;
}
for (j = 0; j < SDE_QOS_LUT_USAGE_MAX; j++) {
diff --git a/drivers/gpu/drm/msm/sde/sde_hw_catalog.h b/drivers/gpu/drm/msm/sde/sde_hw_catalog.h
index e60b5ca..d56ad06 100644
--- a/drivers/gpu/drm/msm/sde/sde_hw_catalog.h
+++ b/drivers/gpu/drm/msm/sde/sde_hw_catalog.h
@@ -152,6 +152,7 @@
* @SDE_MIXER_SOURCESPLIT Layer mixer supports source-split configuration
* @SDE_MIXER_GC Gamma correction block
* @SDE_DIM_LAYER Layer mixer supports dim layer
+ * @SDE_DISP_PRIMARY_PREF Layer mixer preferred for primary display
* @SDE_MIXER_MAX maximum value
*/
enum {
@@ -159,6 +160,7 @@
SDE_MIXER_SOURCESPLIT,
SDE_MIXER_GC,
SDE_DIM_LAYER,
+ SDE_DISP_PRIMARY_PREF,
SDE_MIXER_MAX
};
@@ -217,12 +219,14 @@
* @SDE_CTL_SPLIT_DISPLAY CTL supports video mode split display
* @SDE_CTL_PINGPONG_SPLIT CTL supports pingpong split
* @SDE_CTL_SBUF CTL supports inline stream buffer
+ * @SDE_CTL_PRIMARY_PREF CTL preferred for primary display
* @SDE_CTL_MAX
*/
enum {
SDE_CTL_SPLIT_DISPLAY = 0x1,
SDE_CTL_PINGPONG_SPLIT,
SDE_CTL_SBUF,
+ SDE_CTL_PRIMARY_PREF,
SDE_CTL_MAX
};
@@ -858,8 +862,8 @@
* @downscaling_prefill_lines downscaling latency in lines
* @amortizable_theshold minimum y position for traffic shaping prefill
* @min_prefill_lines minimum pipeline latency in lines
- * @safe_lut_tbl: LUT tables for safe signals
* @danger_lut_tbl: LUT tables for danger signals
+ * @sfe_lut_tbl: LUT tables for safe signals
* @qos_lut_tbl: LUT tables for QoS signals
* @cdp_cfg cdp use case configurations
*/
@@ -882,8 +886,8 @@
u32 downscaling_prefill_lines;
u32 amortizable_threshold;
u32 min_prefill_lines;
- u32 safe_lut_tbl[SDE_QOS_LUT_USAGE_MAX];
u32 danger_lut_tbl[SDE_QOS_LUT_USAGE_MAX];
+ struct sde_qos_lut_tbl sfe_lut_tbl[SDE_QOS_LUT_USAGE_MAX];
struct sde_qos_lut_tbl qos_lut_tbl[SDE_QOS_LUT_USAGE_MAX];
struct sde_perf_cdp_cfg cdp_cfg[SDE_PERF_CDP_USAGE_MAX];
};
diff --git a/drivers/gpu/drm/msm/sde/sde_hw_color_proc_common_v4.h b/drivers/gpu/drm/msm/sde/sde_hw_color_proc_common_v4.h
index 6896ba7..3d2c0a5 100644
--- a/drivers/gpu/drm/msm/sde/sde_hw_color_proc_common_v4.h
+++ b/drivers/gpu/drm/msm/sde/sde_hw_color_proc_common_v4.h
@@ -66,4 +66,42 @@
#define PCC_GG_OFF 0x70
#define PCC_BB_OFF 0x7c
+#define PA_EN BIT(20)
+#define PA_HUE_EN BIT(25)
+#define PA_SAT_EN BIT(26)
+#define PA_VAL_EN BIT(27)
+#define PA_CONT_EN BIT(28)
+
+#define PA_SIXZONE_HUE_EN BIT(29)
+#define PA_SIXZONE_SAT_EN BIT(30)
+#define PA_SIXZONE_VAL_EN BIT(31)
+
+#define PA_HIST_EN BIT(16)
+
+#define PA_SKIN_EN BIT(7)
+#define PA_FOL_EN BIT(6)
+#define PA_SKY_EN BIT(5)
+
+#define PA_HUE_MASK (BIT(12) - 1)
+#define PA_SAT_MASK (BIT(16) - 1)
+#define PA_VAL_MASK (BIT(8) - 1)
+#define PA_CONT_MASK (BIT(8) - 1)
+
+#define PA_HUE_OFF 0x1c
+#define PA_SAT_OFF 0x20
+#define PA_VAL_OFF 0x24
+#define PA_CONT_OFF 0x28
+#define PA_PWL_HOLD_OFF 0x40
+
+#define PA_DISABLE_REQUIRED(x) \
+ !((x) & (PA_SKIN_EN | PA_SKY_EN | \
+ PA_FOL_EN | PA_HUE_EN | \
+ PA_SAT_EN | PA_VAL_EN | \
+ PA_CONT_EN | PA_HIST_EN | \
+ PA_SIXZONE_HUE_EN | PA_SIXZONE_SAT_EN | \
+ PA_SIXZONE_VAL_EN))
+
+#define SIXZONE_ADJ_CURVE_P1_OFF 0x4
+#define SIXZONE_THRESHOLDS_OFF 0x8
+
#endif /* _SDE_HW_COLOR_PROC_COMMON_V4_H_ */
diff --git a/drivers/gpu/drm/msm/sde/sde_hw_color_processing_v1_7.c b/drivers/gpu/drm/msm/sde/sde_hw_color_processing_v1_7.c
index 8e54a2a..d32459a 100644
--- a/drivers/gpu/drm/msm/sde/sde_hw_color_processing_v1_7.c
+++ b/drivers/gpu/drm/msm/sde/sde_hw_color_processing_v1_7.c
@@ -14,15 +14,17 @@
#include "sde_hw_color_processing_v1_7.h"
#include "sde_hw_ctl.h"
+#define REG_MASK_SHIFT(n, shift) ((REG_MASK(n)) << (shift))
+
#define PA_HUE_VIG_OFF 0x110
#define PA_SAT_VIG_OFF 0x114
#define PA_VAL_VIG_OFF 0x118
#define PA_CONT_VIG_OFF 0x11C
-#define PA_HUE_DSPP_OFF 0x238
-#define PA_SAT_DSPP_OFF 0x23C
-#define PA_VAL_DSPP_OFF 0x240
-#define PA_CONT_DSPP_OFF 0x244
+#define PA_HUE_DSPP_OFF 0x1c
+#define PA_SAT_DSPP_OFF 0x20
+#define PA_VAL_DSPP_OFF 0x24
+#define PA_CONT_DSPP_OFF 0x28
#define PA_HIST_CTRL_DSPP_OFF 0x4
#define PA_HIST_DATA_DSPP_OFF 0x400
@@ -78,18 +80,44 @@
#define DSPP_OP_PA_FOL_EN BIT(6)
#define DSPP_OP_PA_SKY_EN BIT(7)
+#define DSPP_SZ_ADJ_CURVE_P1_OFF 0x4
+#define DSPP_SZ_THRESHOLDS_OFF 0x8
+#define DSPP_PA_PWL_HOLD_OFF 0x40
+
+#define DSPP_MEMCOL_SIZE0 0x14
+#define DSPP_MEMCOL_SIZE1 0x8
+#define DSPP_MEMCOL_PWL0_OFF 0x0
+#define DSPP_MEMCOL_PWL2_OFF 0x3C
+#define DSPP_MEMCOL_HOLD_SIZE 0x4
+
+#define DSPP_MEMCOL_PROT_VAL_EN BIT(24)
+#define DSPP_MEMCOL_PROT_SAT_EN BIT(23)
+#define DSPP_MEMCOL_PROT_HUE_EN BIT(22)
+#define DSPP_MEMCOL_PROT_CONT_EN BIT(18)
+#define DSPP_MEMCOL_PROT_SIXZONE_EN BIT(17)
+#define DSPP_MEMCOL_PROT_BLEND_EN BIT(3)
+
+#define DSPP_MEMCOL_MASK \
+ (DSPP_OP_PA_SKIN_EN | DSPP_OP_PA_SKY_EN | DSPP_OP_PA_FOL_EN)
+
+#define DSPP_MEMCOL_PROT_MASK \
+ (DSPP_MEMCOL_PROT_HUE_EN | DSPP_MEMCOL_PROT_SAT_EN | \
+ DSPP_MEMCOL_PROT_VAL_EN | DSPP_MEMCOL_PROT_CONT_EN | \
+ DSPP_MEMCOL_PROT_SIXZONE_EN | DSPP_MEMCOL_PROT_BLEND_EN)
+
#define PA_VIG_DISABLE_REQUIRED(x) \
!((x) & (VIG_OP_PA_SKIN_EN | VIG_OP_PA_SKY_EN | \
VIG_OP_PA_FOL_EN | VIG_OP_PA_HUE_EN | \
VIG_OP_PA_SAT_EN | VIG_OP_PA_VAL_EN | \
VIG_OP_PA_CONT_EN))
-
#define PA_DSPP_DISABLE_REQUIRED(x) \
!((x) & (DSPP_OP_PA_SKIN_EN | DSPP_OP_PA_SKY_EN | \
DSPP_OP_PA_FOL_EN | DSPP_OP_PA_HUE_EN | \
DSPP_OP_PA_SAT_EN | DSPP_OP_PA_VAL_EN | \
- DSPP_OP_PA_CONT_EN | DSPP_OP_PA_LUTV_EN))
+ DSPP_OP_PA_CONT_EN | DSPP_OP_PA_HIST_EN | \
+ DSPP_OP_SZ_HUE_EN | DSPP_OP_SZ_SAT_EN | \
+ DSPP_OP_SZ_VAL_EN))
#define DSPP_OP_PCC_ENABLE BIT(0)
#define PCC_OP_MODE_OFF 0
@@ -116,30 +144,27 @@
static void __setup_pa_hue(struct sde_hw_blk_reg_map *hw,
- const struct sde_pp_blk *blk, uint32_t hue,
- int location)
+ const struct sde_pp_blk *blk, u32 hue, int loc)
{
u32 base = blk->base;
- u32 offset = (location == DSPP) ? PA_HUE_DSPP_OFF : PA_HUE_VIG_OFF;
- u32 op_hue_en = (location == DSPP) ? DSPP_OP_PA_HUE_EN :
- VIG_OP_PA_HUE_EN;
- u32 op_pa_en = (location == DSPP) ? DSPP_OP_PA_EN : VIG_OP_PA_EN;
+ u32 offset = (loc == DSPP) ? PA_HUE_DSPP_OFF : PA_HUE_VIG_OFF;
+ u32 op_hue_en = (loc == DSPP) ? DSPP_OP_PA_HUE_EN : VIG_OP_PA_HUE_EN;
+ u32 op_pa_en = (loc == DSPP) ? DSPP_OP_PA_EN : VIG_OP_PA_EN;
u32 disable_req;
u32 opmode;
- SDE_REG_WRITE(hw, base + offset, hue & PA_HUE_MASK);
-
opmode = SDE_REG_READ(hw, base);
+ SDE_REG_WRITE(hw, base + offset, hue & PA_HUE_MASK);
if (!hue) {
opmode &= ~op_hue_en;
- disable_req = (location == DSPP) ?
+ disable_req = (loc == DSPP) ?
PA_DSPP_DISABLE_REQUIRED(opmode) :
PA_VIG_DISABLE_REQUIRED(opmode);
if (disable_req)
opmode &= ~op_pa_en;
} else {
- opmode |= op_hue_en | op_pa_en;
+ opmode |= (op_hue_en | op_pa_en);
}
SDE_REG_WRITE(hw, base, opmode);
@@ -152,38 +177,28 @@
__setup_pa_hue(&ctx->hw, &ctx->cap->sblk->hsic_blk, hue, SSPP);
}
-void sde_setup_dspp_pa_hue_v1_7(struct sde_hw_dspp *ctx, void *cfg)
-{
- uint32_t hue = *((uint32_t *)cfg);
-
- __setup_pa_hue(&ctx->hw, &ctx->cap->sblk->hsic, hue, DSPP);
-}
-
static void __setup_pa_sat(struct sde_hw_blk_reg_map *hw,
- const struct sde_pp_blk *blk, uint32_t sat,
- int location)
+ const struct sde_pp_blk *blk, u32 sat, int loc)
{
u32 base = blk->base;
- u32 offset = (location == DSPP) ? PA_SAT_DSPP_OFF : PA_SAT_VIG_OFF;
- u32 op_sat_en = (location == DSPP) ?
- DSPP_OP_PA_SAT_EN : VIG_OP_PA_SAT_EN;
- u32 op_pa_en = (location == DSPP) ? DSPP_OP_PA_EN : VIG_OP_PA_EN;
+ u32 offset = (loc == DSPP) ? PA_SAT_DSPP_OFF : PA_SAT_VIG_OFF;
+ u32 op_sat_en = (loc == DSPP) ? DSPP_OP_PA_SAT_EN : VIG_OP_PA_SAT_EN;
+ u32 op_pa_en = (loc == DSPP) ? DSPP_OP_PA_EN : VIG_OP_PA_EN;
u32 disable_req;
u32 opmode;
- SDE_REG_WRITE(hw, base + offset, sat & PA_SAT_MASK);
-
opmode = SDE_REG_READ(hw, base);
+ SDE_REG_WRITE(hw, base + offset, sat & PA_SAT_MASK);
if (!sat) {
opmode &= ~op_sat_en;
- disable_req = (location == DSPP) ?
+ disable_req = (loc == DSPP) ?
PA_DSPP_DISABLE_REQUIRED(opmode) :
PA_VIG_DISABLE_REQUIRED(opmode);
if (disable_req)
opmode &= ~op_pa_en;
} else {
- opmode |= op_sat_en | op_pa_en;
+ opmode |= (op_sat_en | op_pa_en);
}
SDE_REG_WRITE(hw, base, opmode);
@@ -197,30 +212,27 @@
}
static void __setup_pa_val(struct sde_hw_blk_reg_map *hw,
- const struct sde_pp_blk *blk, uint32_t value,
- int location)
+ const struct sde_pp_blk *blk, u32 value, int loc)
{
u32 base = blk->base;
- u32 offset = (location == DSPP) ? PA_VAL_DSPP_OFF : PA_VAL_VIG_OFF;
- u32 op_val_en = (location == DSPP) ?
- DSPP_OP_PA_VAL_EN : VIG_OP_PA_VAL_EN;
- u32 op_pa_en = (location == DSPP) ? DSPP_OP_PA_EN : VIG_OP_PA_EN;
+ u32 offset = (loc == DSPP) ? PA_VAL_DSPP_OFF : PA_VAL_VIG_OFF;
+ u32 op_val_en = (loc == DSPP) ? DSPP_OP_PA_VAL_EN : VIG_OP_PA_VAL_EN;
+ u32 op_pa_en = (loc == DSPP) ? DSPP_OP_PA_EN : VIG_OP_PA_EN;
u32 disable_req;
u32 opmode;
- SDE_REG_WRITE(hw, base + offset, value & PA_VAL_MASK);
-
opmode = SDE_REG_READ(hw, base);
+ SDE_REG_WRITE(hw, base + offset, value & PA_VAL_MASK);
if (!value) {
opmode &= ~op_val_en;
- disable_req = (location == DSPP) ?
+ disable_req = (loc == DSPP) ?
PA_DSPP_DISABLE_REQUIRED(opmode) :
PA_VIG_DISABLE_REQUIRED(opmode);
if (disable_req)
opmode &= ~op_pa_en;
} else {
- opmode |= op_val_en | op_pa_en;
+ opmode |= (op_val_en | op_pa_en);
}
SDE_REG_WRITE(hw, base, opmode);
@@ -234,30 +246,28 @@
}
static void __setup_pa_cont(struct sde_hw_blk_reg_map *hw,
- const struct sde_pp_blk *blk, uint32_t contrast,
- int location)
+ const struct sde_pp_blk *blk, u32 contrast, int loc)
{
u32 base = blk->base;
- u32 offset = (location == DSPP) ? PA_CONT_DSPP_OFF : PA_CONT_VIG_OFF;
- u32 op_cont_en = (location == DSPP) ? DSPP_OP_PA_CONT_EN :
- VIG_OP_PA_CONT_EN;
- u32 op_pa_en = (location == DSPP) ? DSPP_OP_PA_EN : VIG_OP_PA_EN;
+ u32 offset = (loc == DSPP) ? PA_CONT_DSPP_OFF : PA_CONT_VIG_OFF;
+ u32 op_cont_en = (loc == DSPP) ?
+ DSPP_OP_PA_CONT_EN : VIG_OP_PA_CONT_EN;
+ u32 op_pa_en = (loc == DSPP) ? DSPP_OP_PA_EN : VIG_OP_PA_EN;
u32 disable_req;
u32 opmode;
- SDE_REG_WRITE(hw, base + offset, contrast & PA_CONT_MASK);
-
opmode = SDE_REG_READ(hw, base);
+ SDE_REG_WRITE(hw, base + offset, contrast & PA_CONT_MASK);
if (!contrast) {
opmode &= ~op_cont_en;
- disable_req = (location == DSPP) ?
+ disable_req = (loc == DSPP) ?
PA_DSPP_DISABLE_REQUIRED(opmode) :
PA_VIG_DISABLE_REQUIRED(opmode);
if (disable_req)
opmode &= ~op_pa_en;
} else {
- opmode |= op_cont_en | op_pa_en;
+ opmode |= (op_cont_en | op_pa_en);
}
SDE_REG_WRITE(hw, base, opmode);
@@ -270,6 +280,120 @@
__setup_pa_cont(&ctx->hw, &ctx->cap->sblk->hsic_blk, contrast, SSPP);
}
+void sde_setup_dspp_pa_hsic_v17(struct sde_hw_dspp *ctx, void *cfg)
+{
+ struct sde_hw_cp_cfg *hw_cfg = cfg;
+ struct drm_msm_pa_hsic *hsic_cfg;
+ u32 hue = 0;
+ u32 sat = 0;
+ u32 val = 0;
+ u32 cont = 0;
+
+ if (!ctx || !cfg) {
+ DRM_ERROR("invalid param ctx %pK cfg %pK\n", ctx, cfg);
+ return;
+ }
+
+ if (hw_cfg->payload &&
+ (hw_cfg->len != sizeof(struct drm_msm_pa_hsic))) {
+ DRM_ERROR("invalid size of payload len %d exp %zd\n",
+ hw_cfg->len, sizeof(struct drm_msm_pa_hsic));
+ return;
+ }
+
+ if (!hw_cfg->payload) {
+ DRM_DEBUG_DRIVER("disable pa hsic feature\n");
+ } else {
+ hsic_cfg = hw_cfg->payload;
+ if (hsic_cfg->flags & PA_HSIC_HUE_ENABLE)
+ hue = hsic_cfg->hue;
+ if (hsic_cfg->flags & PA_HSIC_SAT_ENABLE)
+ sat = hsic_cfg->saturation;
+ if (hsic_cfg->flags & PA_HSIC_VAL_ENABLE)
+ val = hsic_cfg->value;
+ if (hsic_cfg->flags & PA_HSIC_CONT_ENABLE)
+ cont = hsic_cfg->contrast;
+ }
+
+ __setup_pa_hue(&ctx->hw, &ctx->cap->sblk->hsic, hue, DSPP);
+ __setup_pa_sat(&ctx->hw, &ctx->cap->sblk->hsic, sat, DSPP);
+ __setup_pa_val(&ctx->hw, &ctx->cap->sblk->hsic, val, DSPP);
+ __setup_pa_cont(&ctx->hw, &ctx->cap->sblk->hsic, cont, DSPP);
+}
+
+void sde_setup_dspp_sixzone_v17(struct sde_hw_dspp *ctx, void *cfg)
+{
+ struct sde_hw_cp_cfg *hw_cfg = cfg;
+ struct drm_msm_sixzone *sixzone;
+ u32 opcode = 0, local_opcode = 0;
+ u32 reg = 0, hold = 0, local_hold = 0;
+ u32 addr = 0;
+ int i = 0;
+
+ if (!ctx || !cfg) {
+ DRM_ERROR("invalid param ctx %pK cfg %pK\n", ctx, cfg);
+ return;
+ }
+
+ opcode = SDE_REG_READ(&ctx->hw, ctx->cap->sblk->hsic.base);
+
+ if (!hw_cfg->payload) {
+ DRM_DEBUG_DRIVER("disable sixzone feature\n");
+ opcode &= ~(DSPP_OP_SZ_HUE_EN | DSPP_OP_SZ_SAT_EN |
+ DSPP_OP_SZ_VAL_EN);
+ if (PA_DSPP_DISABLE_REQUIRED(opcode))
+ opcode &= ~DSPP_OP_PA_EN;
+ SDE_REG_WRITE(&ctx->hw, ctx->cap->sblk->hsic.base, opcode);
+ return;
+ }
+
+ if (hw_cfg->len != sizeof(struct drm_msm_sixzone)) {
+ DRM_ERROR("invalid size of payload len %d exp %zd\n",
+ hw_cfg->len, sizeof(struct drm_msm_sixzone));
+ return;
+ }
+
+ sixzone = hw_cfg->payload;
+
+ reg = BIT(26);
+ SDE_REG_WRITE(&ctx->hw, ctx->cap->sblk->sixzone.base, reg);
+
+ addr = ctx->cap->sblk->sixzone.base + DSPP_SZ_ADJ_CURVE_P1_OFF;
+ for (i = 0; i < SIXZONE_LUT_SIZE; i++) {
+ SDE_REG_WRITE(&ctx->hw, addr, sixzone->curve[i].p1);
+ SDE_REG_WRITE(&ctx->hw, (addr - 4), sixzone->curve[i].p0);
+ }
+
+ addr = ctx->cap->sblk->sixzone.base + DSPP_SZ_THRESHOLDS_OFF;
+ SDE_REG_WRITE(&ctx->hw, addr, sixzone->threshold);
+ SDE_REG_WRITE(&ctx->hw, (addr + 4), sixzone->adjust_p0);
+ SDE_REG_WRITE(&ctx->hw, (addr + 8), sixzone->adjust_p1);
+
+ hold = SDE_REG_READ(&ctx->hw,
+ (ctx->cap->sblk->hsic.base + DSPP_PA_PWL_HOLD_OFF));
+ local_hold = ((sixzone->sat_hold & REG_MASK(2)) << 12);
+ local_hold |= ((sixzone->val_hold & REG_MASK(2)) << 14);
+ hold &= ~REG_MASK_SHIFT(4, 12);
+ hold |= local_hold;
+ SDE_REG_WRITE(&ctx->hw,
+ (ctx->cap->sblk->hsic.base + DSPP_PA_PWL_HOLD_OFF),
+ hold);
+
+ if (sixzone->flags & SIXZONE_HUE_ENABLE)
+ local_opcode |= DSPP_OP_SZ_HUE_EN;
+ if (sixzone->flags & SIXZONE_SAT_ENABLE)
+ local_opcode |= DSPP_OP_SZ_SAT_EN;
+ if (sixzone->flags & SIXZONE_VAL_ENABLE)
+ local_opcode |= DSPP_OP_SZ_VAL_EN;
+
+ if (local_opcode)
+ local_opcode |= DSPP_OP_PA_EN;
+
+ opcode &= ~REG_MASK_SHIFT(3, 29);
+ opcode |= local_opcode;
+ SDE_REG_WRITE(&ctx->hw, ctx->cap->sblk->hsic.base, opcode);
+}
+
void sde_setup_pipe_pa_memcol_v1_7(struct sde_hw_pipe *ctx,
enum sde_memcolor_type type,
void *cfg)
@@ -333,6 +457,220 @@
SDE_REG_WRITE(&ctx->hw, base, op);
}
+static void __setup_dspp_memcol(struct sde_hw_dspp *ctx,
+ enum sde_memcolor_type type,
+ struct drm_msm_memcol *memcolor)
+{
+ u32 addr = 0, offset = 0, idx = 0;
+ u32 hold = 0, local_hold = 0, hold_shift = 0;
+
+ switch (type) {
+ case MEMCOLOR_SKIN:
+ idx = 0;
+ break;
+ case MEMCOLOR_SKY:
+ idx = 1;
+ break;
+ case MEMCOLOR_FOLIAGE:
+ idx = 2;
+ break;
+ default:
+ DRM_ERROR("Invalid memory color type %d\n", type);
+ return;
+ }
+
+ offset = DSPP_MEMCOL_PWL0_OFF + (idx * DSPP_MEMCOL_SIZE0);
+ addr = ctx->cap->sblk->memcolor.base + offset;
+ hold_shift = idx * DSPP_MEMCOL_HOLD_SIZE;
+
+ SDE_REG_WRITE(&ctx->hw, addr, memcolor->color_adjust_p0);
+ addr += 4;
+ SDE_REG_WRITE(&ctx->hw, addr, memcolor->color_adjust_p1);
+ addr += 4;
+ SDE_REG_WRITE(&ctx->hw, addr, memcolor->hue_region);
+ addr += 4;
+ SDE_REG_WRITE(&ctx->hw, addr, memcolor->sat_region);
+ addr += 4;
+ SDE_REG_WRITE(&ctx->hw, addr, memcolor->val_region);
+
+ offset = DSPP_MEMCOL_PWL2_OFF + (idx * DSPP_MEMCOL_SIZE1);
+ addr = ctx->cap->sblk->memcolor.base + offset;
+
+ SDE_REG_WRITE(&ctx->hw, addr, memcolor->color_adjust_p2);
+ addr += 4;
+ SDE_REG_WRITE(&ctx->hw, addr, memcolor->blend_gain);
+
+ addr = ctx->cap->sblk->hsic.base + DSPP_PA_PWL_HOLD_OFF;
+ hold = SDE_REG_READ(&ctx->hw, addr);
+ local_hold = ((memcolor->sat_hold & REG_MASK(2)) << hold_shift);
+ local_hold |=
+ ((memcolor->val_hold & REG_MASK(2)) << (hold_shift + 2));
+ hold &= ~REG_MASK_SHIFT(4, hold_shift);
+ hold |= local_hold;
+ SDE_REG_WRITE(&ctx->hw, addr, hold);
+}
+
+void sde_setup_dspp_memcol_skin_v17(struct sde_hw_dspp *ctx, void *cfg)
+{
+ struct sde_hw_cp_cfg *hw_cfg = cfg;
+ struct drm_msm_memcol *memcolor;
+ u32 opcode = 0;
+
+ if (!ctx || !cfg) {
+ DRM_ERROR("invalid param ctx %pK cfg %pK\n", ctx, cfg);
+ return;
+ }
+
+ opcode = SDE_REG_READ(&ctx->hw, ctx->cap->sblk->hsic.base);
+
+ if (!hw_cfg->payload) {
+ DRM_DEBUG_DRIVER("disable memcolor skin feature\n");
+ opcode &= ~(DSPP_OP_PA_SKIN_EN);
+ if (PA_DSPP_DISABLE_REQUIRED(opcode))
+ opcode &= ~DSPP_OP_PA_EN;
+ SDE_REG_WRITE(&ctx->hw, ctx->cap->sblk->hsic.base, opcode);
+ return;
+ }
+
+ if (hw_cfg->len != sizeof(struct drm_msm_memcol)) {
+ DRM_ERROR("invalid size of payload len %d exp %zd\n",
+ hw_cfg->len, sizeof(struct drm_msm_memcol));
+ return;
+ }
+
+ memcolor = hw_cfg->payload;
+
+ __setup_dspp_memcol(ctx, MEMCOLOR_SKIN, memcolor);
+
+ opcode |= (DSPP_OP_PA_SKIN_EN | DSPP_OP_PA_EN);
+ SDE_REG_WRITE(&ctx->hw, ctx->cap->sblk->hsic.base, opcode);
+}
+
+void sde_setup_dspp_memcol_sky_v17(struct sde_hw_dspp *ctx, void *cfg)
+{
+ struct sde_hw_cp_cfg *hw_cfg = cfg;
+ struct drm_msm_memcol *memcolor;
+ u32 opcode = 0;
+
+ if (!ctx || !cfg) {
+ DRM_ERROR("invalid param ctx %pK cfg %pK\n", ctx, cfg);
+ return;
+ }
+
+ opcode = SDE_REG_READ(&ctx->hw, ctx->cap->sblk->hsic.base);
+
+ if (!hw_cfg->payload) {
+ DRM_DEBUG_DRIVER("disable memcolor sky feature\n");
+ opcode &= ~(DSPP_OP_PA_SKY_EN);
+ if (PA_DSPP_DISABLE_REQUIRED(opcode))
+ opcode &= ~DSPP_OP_PA_EN;
+ SDE_REG_WRITE(&ctx->hw, ctx->cap->sblk->hsic.base, opcode);
+ return;
+ }
+
+ if (hw_cfg->len != sizeof(struct drm_msm_memcol)) {
+ DRM_ERROR("invalid size of payload len %d exp %zd\n",
+ hw_cfg->len, sizeof(struct drm_msm_memcol));
+ return;
+ }
+
+ memcolor = hw_cfg->payload;
+
+ __setup_dspp_memcol(ctx, MEMCOLOR_SKY, memcolor);
+
+ opcode |= (DSPP_OP_PA_SKY_EN | DSPP_OP_PA_EN);
+ SDE_REG_WRITE(&ctx->hw, ctx->cap->sblk->hsic.base, opcode);
+}
+
+void sde_setup_dspp_memcol_foliage_v17(struct sde_hw_dspp *ctx, void *cfg)
+{
+ struct sde_hw_cp_cfg *hw_cfg = cfg;
+ struct drm_msm_memcol *memcolor;
+ u32 opcode = 0;
+
+ if (!ctx || !cfg) {
+ DRM_ERROR("invalid param ctx %pK cfg %pK\n", ctx, cfg);
+ return;
+ }
+
+ opcode = SDE_REG_READ(&ctx->hw, ctx->cap->sblk->hsic.base);
+
+ if (!hw_cfg->payload) {
+ DRM_DEBUG_DRIVER("disable memcolor foliage feature\n");
+ opcode &= ~(DSPP_OP_PA_FOL_EN);
+ if (PA_DSPP_DISABLE_REQUIRED(opcode))
+ opcode &= ~DSPP_OP_PA_EN;
+ SDE_REG_WRITE(&ctx->hw, ctx->cap->sblk->hsic.base, opcode);
+ return;
+ }
+
+ if (hw_cfg->len != sizeof(struct drm_msm_memcol)) {
+ DRM_ERROR("invalid size of payload len %d exp %zd\n",
+ hw_cfg->len, sizeof(struct drm_msm_memcol));
+ return;
+ }
+
+ memcolor = hw_cfg->payload;
+
+ __setup_dspp_memcol(ctx, MEMCOLOR_FOLIAGE, memcolor);
+
+ opcode |= (DSPP_OP_PA_FOL_EN | DSPP_OP_PA_EN);
+ SDE_REG_WRITE(&ctx->hw, ctx->cap->sblk->hsic.base, opcode);
+}
+
+void sde_setup_dspp_memcol_prot_v17(struct sde_hw_dspp *ctx, void *cfg)
+{
+ struct sde_hw_cp_cfg *hw_cfg = cfg;
+ struct drm_msm_memcol *memcolor;
+ u32 opcode = 0, local_opcode = 0;
+
+ if (!ctx || !cfg) {
+ DRM_ERROR("invalid param ctx %pK cfg %pK\n", ctx, cfg);
+ return;
+ }
+
+ opcode = SDE_REG_READ(&ctx->hw, ctx->cap->sblk->hsic.base);
+
+ if (!hw_cfg->payload) {
+ DRM_DEBUG_DRIVER("disable memcolor prot feature\n");
+ opcode &= ~(DSPP_MEMCOL_PROT_MASK);
+ if (PA_DSPP_DISABLE_REQUIRED(opcode))
+ opcode &= ~DSPP_OP_PA_EN;
+ SDE_REG_WRITE(&ctx->hw, ctx->cap->sblk->hsic.base, opcode);
+ return;
+ }
+
+ if (hw_cfg->len != sizeof(struct drm_msm_memcol)) {
+ DRM_ERROR("invalid size of payload len %d exp %zd\n",
+ hw_cfg->len, sizeof(struct drm_msm_memcol));
+ return;
+ }
+
+ memcolor = hw_cfg->payload;
+
+ if (memcolor->prot_flags) {
+ if (memcolor->prot_flags & MEMCOL_PROT_HUE)
+ local_opcode |= DSPP_MEMCOL_PROT_HUE_EN;
+ if (memcolor->prot_flags & MEMCOL_PROT_SAT)
+ local_opcode |= DSPP_MEMCOL_PROT_SAT_EN;
+ if (memcolor->prot_flags & MEMCOL_PROT_VAL)
+ local_opcode |= DSPP_MEMCOL_PROT_VAL_EN;
+ if (memcolor->prot_flags & MEMCOL_PROT_CONT)
+ local_opcode |= DSPP_MEMCOL_PROT_CONT_EN;
+ if (memcolor->prot_flags & MEMCOL_PROT_SIXZONE)
+ local_opcode |= DSPP_MEMCOL_PROT_SIXZONE_EN;
+ if (memcolor->prot_flags & MEMCOL_PROT_BLEND)
+ local_opcode |= DSPP_MEMCOL_PROT_BLEND_EN;
+ }
+
+ if (local_opcode) {
+ local_opcode |= DSPP_OP_PA_EN;
+ opcode &= ~(DSPP_MEMCOL_PROT_MASK);
+ opcode |= local_opcode;
+ SDE_REG_WRITE(&ctx->hw, ctx->cap->sblk->hsic.base, opcode);
+ }
+}
+
void sde_setup_dspp_pcc_v1_7(struct sde_hw_dspp *ctx, void *cfg)
{
struct sde_hw_cp_cfg *hw_cfg = cfg;
diff --git a/drivers/gpu/drm/msm/sde/sde_hw_color_processing_v1_7.h b/drivers/gpu/drm/msm/sde/sde_hw_color_processing_v1_7.h
index 74018a3..3c783ee 100644
--- a/drivers/gpu/drm/msm/sde/sde_hw_color_processing_v1_7.h
+++ b/drivers/gpu/drm/msm/sde/sde_hw_color_processing_v1_7.h
@@ -62,11 +62,46 @@
void sde_setup_dspp_pcc_v1_7(struct sde_hw_dspp *ctx, void *cfg);
/**
- * sde_setup_dspp_pa_hue_v1_7 - setup DSPP hue feature in v1.7 hardware
+ * sde_setup_dspp_pa_hsic_v17 - setup DSPP hsic feature in v1.7 hardware
* @ctx: Pointer to DSPP context
- * @cfg: Pointer to hue data
+ * @cfg: Pointer to hsic data
*/
-void sde_setup_dspp_pa_hue_v1_7(struct sde_hw_dspp *ctx, void *cfg);
+void sde_setup_dspp_pa_hsic_v17(struct sde_hw_dspp *ctx, void *cfg);
+
+/**
+ * sde_setup_dspp_memcol_skin_v17 - setup DSPP memcol skin in v1.7 hardware
+ * @ctx: Pointer to DSPP context
+ * @cfg: Pointer to memcolor config data
+ */
+void sde_setup_dspp_memcol_skin_v17(struct sde_hw_dspp *ctx, void *cfg);
+
+/**
+ * sde_setup_dspp_memcol_sky_v17 - setup DSPP memcol sky in v1.7 hardware
+ * @ctx: Pointer to DSPP context
+ * @cfg: Pointer to memcolor config data
+ */
+void sde_setup_dspp_memcol_sky_v17(struct sde_hw_dspp *ctx, void *cfg);
+
+/**
+ * sde_setup_dspp_memcol_foliage_v17 - setup DSPP memcol fol in v1.7 hardware
+ * @ctx: Pointer to DSPP context
+ * @cfg: Pointer to memcolor config data
+ */
+void sde_setup_dspp_memcol_foliage_v17(struct sde_hw_dspp *ctx, void *cfg);
+
+/**
+ * sde_setup_dspp_memcol_prot_v17 - setup DSPP memcol prot in v1.7 hardware
+ * @ctx: Pointer to DSPP context
+ * @cfg: Pointer to memcolor config data
+ */
+void sde_setup_dspp_memcol_prot_v17(struct sde_hw_dspp *ctx, void *cfg);
+
+/**
+ * sde_setup_dspp_sixzone_v17 - setup DSPP sixzone feature in v1.7 hardware
+ * @ctx: Pointer to DSPP context
+ * @cfg: Pointer to sixzone data
+ */
+void sde_setup_dspp_sixzone_v17(struct sde_hw_dspp *ctx, void *cfg);
/**
* sde_setup_dspp_pa_vlut_v1_7 - setup DSPP PA vLUT feature in v1.7 hardware
diff --git a/drivers/gpu/drm/msm/sde/sde_hw_ctl.c b/drivers/gpu/drm/msm/sde/sde_hw_ctl.c
index 82b3383..88f821d 100644
--- a/drivers/gpu/drm/msm/sde/sde_hw_ctl.c
+++ b/drivers/gpu/drm/msm/sde/sde_hw_ctl.c
@@ -378,10 +378,12 @@
int i;
for (i = 0; i < ctx->mixer_count; i++) {
- SDE_REG_WRITE(c, CTL_LAYER(LM_0 + i), 0);
- SDE_REG_WRITE(c, CTL_LAYER_EXT(LM_0 + i), 0);
- SDE_REG_WRITE(c, CTL_LAYER_EXT2(LM_0 + i), 0);
- SDE_REG_WRITE(c, CTL_LAYER_EXT3(LM_0 + i), 0);
+ int mixer_id = ctx->mixer_hw_caps[i].id;
+
+ SDE_REG_WRITE(c, CTL_LAYER(mixer_id), 0);
+ SDE_REG_WRITE(c, CTL_LAYER_EXT(mixer_id), 0);
+ SDE_REG_WRITE(c, CTL_LAYER_EXT2(mixer_id), 0);
+ SDE_REG_WRITE(c, CTL_LAYER_EXT3(mixer_id), 0);
}
}
diff --git a/drivers/gpu/drm/msm/sde/sde_hw_dspp.c b/drivers/gpu/drm/msm/sde/sde_hw_dspp.c
index 36e30b7..b268e8f 100644
--- a/drivers/gpu/drm/msm/sde/sde_hw_dspp.c
+++ b/drivers/gpu/drm/msm/sde/sde_hw_dspp.c
@@ -71,8 +71,28 @@
break;
case SDE_DSPP_HSIC:
if (c->cap->sblk->hsic.version ==
- (SDE_COLOR_PROCESS_VER(0x1, 0x7)))
- c->ops.setup_hue = sde_setup_dspp_pa_hue_v1_7;
+ SDE_COLOR_PROCESS_VER(0x1, 0x7))
+ c->ops.setup_pa_hsic =
+ sde_setup_dspp_pa_hsic_v17;
+ break;
+ case SDE_DSPP_MEMCOLOR:
+ if (c->cap->sblk->memcolor.version ==
+ SDE_COLOR_PROCESS_VER(0x1, 0x7)) {
+ c->ops.setup_pa_memcol_skin =
+ sde_setup_dspp_memcol_skin_v17;
+ c->ops.setup_pa_memcol_sky =
+ sde_setup_dspp_memcol_sky_v17;
+ c->ops.setup_pa_memcol_foliage =
+ sde_setup_dspp_memcol_foliage_v17;
+ c->ops.setup_pa_memcol_prot =
+ sde_setup_dspp_memcol_prot_v17;
+ }
+ break;
+ case SDE_DSPP_SIXZONE:
+ if (c->cap->sblk->sixzone.version ==
+ SDE_COLOR_PROCESS_VER(0x1, 0x7))
+ c->ops.setup_sixzone =
+ sde_setup_dspp_sixzone_v17;
break;
case SDE_DSPP_VLUT:
if (c->cap->sblk->vlut.version ==
diff --git a/drivers/gpu/drm/msm/sde/sde_hw_dspp.h b/drivers/gpu/drm/msm/sde/sde_hw_dspp.h
index 4878fc6..2b64165 100644
--- a/drivers/gpu/drm/msm/sde/sde_hw_dspp.h
+++ b/drivers/gpu/drm/msm/sde/sde_hw_dspp.h
@@ -52,11 +52,11 @@
void (*setup_igc)(struct sde_hw_dspp *ctx, void *cfg);
/**
- * setup_pa - setup dspp pa
+ * setup_pa_hsic - setup dspp pa hsic
* @ctx: Pointer to dspp context
* @cfg: Pointer to configuration
*/
- void (*setup_pa)(struct sde_hw_dspp *dspp, void *cfg);
+ void (*setup_pa_hsic)(struct sde_hw_dspp *dspp, void *cfg);
/**
* setup_pcc - setup dspp pcc
@@ -73,11 +73,32 @@
void (*setup_sharpening)(struct sde_hw_dspp *ctx, void *cfg);
/**
- * setup_pa_memcolor - setup dspp memcolor
+ * setup_pa_memcol_skin - setup dspp memcolor skin
* @ctx: Pointer to dspp context
* @cfg: Pointer to configuration
*/
- void (*setup_pa_memcolor)(struct sde_hw_dspp *ctx, void *cfg);
+ void (*setup_pa_memcol_skin)(struct sde_hw_dspp *ctx, void *cfg);
+
+ /**
+ * setup_pa_memcol_sky - setup dspp memcolor sky
+ * @ctx: Pointer to dspp context
+ * @cfg: Pointer to configuration
+ */
+ void (*setup_pa_memcol_sky)(struct sde_hw_dspp *ctx, void *cfg);
+
+ /**
+ * setup_pa_memcol_foliage - setup dspp memcolor foliage
+ * @ctx: Pointer to dspp context
+ * @cfg: Pointer to configuration
+ */
+ void (*setup_pa_memcol_foliage)(struct sde_hw_dspp *ctx, void *cfg);
+
+ /**
+ * setup_pa_memcol_prot - setup dspp memcolor protection
+ * @ctx: Pointer to dspp context
+ * @cfg: Pointer to configuration
+ */
+ void (*setup_pa_memcol_prot)(struct sde_hw_dspp *ctx, void *cfg);
/**
* setup_sixzone - setup dspp six zone
@@ -101,34 +122,6 @@
void (*setup_dither)(struct sde_hw_dspp *ctx, void *cfg);
/**
- * setup_hue - setup dspp PA hue
- * @ctx: Pointer to dspp context
- * @cfg: Pointer to configuration
- */
- void (*setup_hue)(struct sde_hw_dspp *ctx, void *cfg);
-
- /**
- * setup_sat - setup dspp PA saturation
- * @ctx: Pointer to dspp context
- * @cfg: Pointer to configuration
- */
- void (*setup_sat)(struct sde_hw_dspp *ctx, void *cfg);
-
- /**
- * setup_val - setup dspp PA value
- * @ctx: Pointer to dspp context
- * @cfg: Pointer to configuration
- */
- void (*setup_val)(struct sde_hw_dspp *ctx, void *cfg);
-
- /**
- * setup_cont - setup dspp PA contrast
- * @ctx: Pointer to dspp context
- * @cfg: Pointer to configuration
- */
- void (*setup_cont)(struct sde_hw_dspp *ctx, void *cfg);
-
- /**
* setup_vlut - setup dspp PA VLUT
* @ctx: Pointer to dspp context
* @cfg: Pointer to configuration
diff --git a/drivers/gpu/drm/msm/sde/sde_hw_reg_dma_v1_color_proc.c b/drivers/gpu/drm/msm/sde/sde_hw_reg_dma_v1_color_proc.c
index b59dd16..0dc3fed 100644
--- a/drivers/gpu/drm/msm/sde/sde_hw_reg_dma_v1_color_proc.c
+++ b/drivers/gpu/drm/msm/sde/sde_hw_reg_dma_v1_color_proc.c
@@ -39,7 +39,14 @@
#define PCC_MEM_SIZE (PCC_LEN + \
REG_DMA_HEADERS_BUFFER_SZ)
+#define HSIC_MEM_SIZE ((sizeof(struct drm_msm_pa_hsic)) + \
+ REG_DMA_HEADERS_BUFFER_SZ)
+
+#define SIXZONE_MEM_SIZE ((sizeof(struct drm_msm_sixzone)) + \
+ REG_DMA_HEADERS_BUFFER_SZ)
+
#define REG_MASK(n) ((BIT(n)) - 1)
+#define REG_MASK_SHIFT(n, shift) ((REG_MASK(n)) << (shift))
static struct sde_reg_dma_buffer *dspp_buf[REG_DMA_FEATURES_MAX][DSPP_MAX];
@@ -49,9 +56,9 @@
[SDE_DSPP_IGC] = IGC,
[SDE_DSPP_PCC] = PCC,
[SDE_DSPP_GC] = GC,
- [SDE_DSPP_HSIC] = REG_DMA_FEATURES_MAX,
+ [SDE_DSPP_HSIC] = HSIC,
[SDE_DSPP_MEMCOLOR] = REG_DMA_FEATURES_MAX,
- [SDE_DSPP_SIXZONE] = REG_DMA_FEATURES_MAX,
+ [SDE_DSPP_SIXZONE] = SIX_ZONE,
[SDE_DSPP_DITHER] = REG_DMA_FEATURES_MAX,
[SDE_DSPP_HIST] = REG_DMA_FEATURES_MAX,
[SDE_DSPP_AD] = REG_DMA_FEATURES_MAX,
@@ -63,6 +70,8 @@
[SDE_DSPP_GC] = GC_LUT_MEM_SIZE,
[SDE_DSPP_IGC] = IGC_LUT_MEM_SIZE,
[SDE_DSPP_PCC] = PCC_MEM_SIZE,
+ [SDE_DSPP_HSIC] = HSIC_MEM_SIZE,
+ [SDE_DSPP_SIXZONE] = SIXZONE_MEM_SIZE,
};
static u32 dspp_mapping[DSPP_MAX] = {
@@ -295,8 +304,8 @@
if (!hw_cfg->payload) {
DRM_DEBUG_DRIVER("Disable vlut feature\n");
SDE_REG_WRITE(&ctx->hw, PA_LUTV_OPMODE_OFF, 0);
- if (op_mode & (~(BIT(20))))
- op_mode = 0;
+ if (PA_DISABLE_REQUIRED(op_mode))
+ op_mode &= ~PA_EN;
SDE_REG_WRITE(&ctx->hw, PA_OP_MODE_OFF, op_mode);
return;
}
@@ -919,6 +928,233 @@
kfree(data);
}
+void reg_dmav1_setup_dspp_pa_hsicv18(struct sde_hw_dspp *ctx, void *cfg)
+{
+ struct sde_hw_reg_dma_ops *dma_ops;
+ struct sde_reg_dma_kickoff_cfg kick_off;
+ struct sde_hw_cp_cfg *hw_cfg = cfg;
+ struct sde_reg_dma_setup_ops_cfg dma_write_cfg;
+ struct drm_msm_pa_hsic *hsic_cfg;
+ u32 reg = 0, opcode = 0, local_opcode = 0;
+ int rc;
+
+ opcode = SDE_REG_READ(&ctx->hw, ctx->cap->sblk->hsic.base);
+
+ rc = reg_dma_dspp_check(ctx, cfg, HSIC);
+ if (rc)
+ return;
+
+ if (!hw_cfg->payload) {
+ DRM_DEBUG_DRIVER("disable pa hsic feature\n");
+ opcode &= ~(PA_HUE_EN | PA_SAT_EN | PA_VAL_EN | PA_CONT_EN);
+ if (PA_DISABLE_REQUIRED(opcode))
+ opcode &= ~PA_EN;
+ SDE_REG_WRITE(&ctx->hw, ctx->cap->sblk->hsic.base, opcode);
+ return;
+ }
+
+ if (hw_cfg->len != sizeof(struct drm_msm_pa_hsic)) {
+ DRM_ERROR("invalid size of payload len %d exp %zd\n",
+ hw_cfg->len, sizeof(struct drm_msm_pa_hsic));
+ return;
+ }
+
+ hsic_cfg = hw_cfg->payload;
+
+ dma_ops = sde_reg_dma_get_ops();
+ dma_ops->reset_reg_dma_buf(dspp_buf[HSIC][ctx->idx]);
+
+ REG_DMA_INIT_OPS(dma_write_cfg, dspp_mapping[ctx->idx],
+ HSIC, dspp_buf[HSIC][ctx->idx]);
+
+ REG_DMA_SETUP_OPS(dma_write_cfg, 0, NULL, 0, HW_BLK_SELECT, 0, 0);
+ rc = dma_ops->setup_payload(&dma_write_cfg);
+ if (rc) {
+ DRM_ERROR("write decode select failed ret %d\n", rc);
+ return;
+ }
+
+ if (hsic_cfg->flags & PA_HSIC_HUE_ENABLE) {
+ reg = hsic_cfg->hue & PA_HUE_MASK;
+ REG_DMA_SETUP_OPS(dma_write_cfg,
+ ctx->cap->sblk->hsic.base + PA_HUE_OFF,
+ ®, sizeof(reg), REG_SINGLE_WRITE, 0, 0);
+ rc = dma_ops->setup_payload(&dma_write_cfg);
+ if (rc) {
+ DRM_ERROR("hsic hue write failed ret %d\n", rc);
+ return;
+ }
+ local_opcode |= PA_HUE_EN;
+ } else if (opcode & PA_HUE_EN)
+ opcode &= ~PA_HUE_EN;
+
+ if (hsic_cfg->flags & PA_HSIC_SAT_ENABLE) {
+ reg = hsic_cfg->saturation & PA_SAT_MASK;
+ REG_DMA_SETUP_OPS(dma_write_cfg,
+ ctx->cap->sblk->hsic.base + PA_SAT_OFF,
+ ®, sizeof(reg), REG_SINGLE_WRITE, 0, 0);
+ rc = dma_ops->setup_payload(&dma_write_cfg);
+ if (rc) {
+ DRM_ERROR("hsic saturation write failed ret %d\n", rc);
+ return;
+ }
+ local_opcode |= PA_SAT_EN;
+ } else if (opcode & PA_SAT_EN)
+ opcode &= ~PA_SAT_EN;
+
+ if (hsic_cfg->flags & PA_HSIC_VAL_ENABLE) {
+ reg = hsic_cfg->value & PA_VAL_MASK;
+ REG_DMA_SETUP_OPS(dma_write_cfg,
+ ctx->cap->sblk->hsic.base + PA_VAL_OFF,
+ ®, sizeof(reg), REG_SINGLE_WRITE, 0, 0);
+ rc = dma_ops->setup_payload(&dma_write_cfg);
+ if (rc) {
+ DRM_ERROR("hsic value write failed ret %d\n", rc);
+ return;
+ }
+ local_opcode |= PA_VAL_EN;
+ } else if (opcode & PA_VAL_EN)
+ opcode &= ~PA_VAL_EN;
+
+ if (hsic_cfg->flags & PA_HSIC_CONT_ENABLE) {
+ reg = hsic_cfg->contrast & PA_CONT_MASK;
+ REG_DMA_SETUP_OPS(dma_write_cfg,
+ ctx->cap->sblk->hsic.base + PA_CONT_OFF,
+ ®, sizeof(reg), REG_SINGLE_WRITE, 0, 0);
+ rc = dma_ops->setup_payload(&dma_write_cfg);
+ if (rc) {
+ DRM_ERROR("hsic contrast write failed ret %d\n", rc);
+ return;
+ }
+ local_opcode |= PA_CONT_EN;
+ } else if (opcode & PA_CONT_EN)
+ opcode &= ~PA_CONT_EN;
+
+ if (local_opcode)
+ opcode |= (local_opcode | PA_EN);
+ else {
+ DRM_ERROR("Invalid hsic config\n");
+ return;
+ }
+
+ REG_DMA_SETUP_KICKOFF(kick_off, hw_cfg->ctl, dspp_buf[HSIC][ctx->idx],
+ REG_DMA_WRITE, DMA_CTL_QUEUE0, WRITE_IMMEDIATE);
+ rc = dma_ops->kick_off(&kick_off);
+ if (rc)
+ DRM_ERROR("failed to kick off ret %d\n", rc);
+
+ SDE_REG_WRITE(&ctx->hw, ctx->cap->sblk->hsic.base, opcode);
+}
+
+void reg_dmav1_setup_dspp_sixzonev18(struct sde_hw_dspp *ctx, void *cfg)
+{
+ struct sde_hw_reg_dma_ops *dma_ops;
+ struct sde_reg_dma_kickoff_cfg kick_off;
+ struct sde_hw_cp_cfg *hw_cfg = cfg;
+ struct sde_reg_dma_setup_ops_cfg dma_write_cfg;
+ struct drm_msm_sixzone *sixzone;
+ u32 reg = 0, hold = 0, local_hold = 0;
+ u32 opcode = 0, local_opcode = 0;
+ int rc;
+
+ opcode = SDE_REG_READ(&ctx->hw, ctx->cap->sblk->hsic.base);
+
+ rc = reg_dma_dspp_check(ctx, cfg, SIX_ZONE);
+ if (rc)
+ return;
+
+ if (!hw_cfg->payload) {
+ DRM_DEBUG_DRIVER("disable sixzone feature\n");
+ opcode &= ~(PA_SIXZONE_HUE_EN | PA_SIXZONE_SAT_EN |
+ PA_SIXZONE_VAL_EN);
+ if (PA_DISABLE_REQUIRED(opcode))
+ opcode &= ~PA_EN;
+ SDE_REG_WRITE(&ctx->hw, ctx->cap->sblk->hsic.base, opcode);
+ return;
+ }
+
+ if (hw_cfg->len != sizeof(struct drm_msm_sixzone)) {
+ DRM_ERROR("invalid size of payload len %d exp %zd\n",
+ hw_cfg->len, sizeof(struct drm_msm_sixzone));
+ return;
+ }
+
+ sixzone = hw_cfg->payload;
+
+ dma_ops = sde_reg_dma_get_ops();
+ dma_ops->reset_reg_dma_buf(dspp_buf[SIX_ZONE][ctx->idx]);
+
+ REG_DMA_INIT_OPS(dma_write_cfg, dspp_mapping[ctx->idx],
+ SIX_ZONE, dspp_buf[SIX_ZONE][ctx->idx]);
+
+ REG_DMA_SETUP_OPS(dma_write_cfg, 0, NULL, 0, HW_BLK_SELECT, 0, 0);
+ rc = dma_ops->setup_payload(&dma_write_cfg);
+ if (rc) {
+ DRM_ERROR("write decode select failed ret %d\n", rc);
+ return;
+ }
+
+ reg = BIT(26);
+ REG_DMA_SETUP_OPS(dma_write_cfg,
+ ctx->cap->sblk->sixzone.base,
+ ®, sizeof(reg), REG_SINGLE_WRITE, 0, 0);
+ rc = dma_ops->setup_payload(&dma_write_cfg);
+ if (rc) {
+ DRM_ERROR("setting lut index failed ret %d\n", rc);
+ return;
+ }
+
+ REG_DMA_SETUP_OPS(dma_write_cfg,
+ (ctx->cap->sblk->sixzone.base + SIXZONE_ADJ_CURVE_P1_OFF),
+ &sixzone->curve[0].p1, (SIXZONE_LUT_SIZE * sizeof(u32) * 2),
+ REG_BLK_WRITE_MULTIPLE, 2, 0);
+ rc = dma_ops->setup_payload(&dma_write_cfg);
+ if (rc) {
+ DRM_ERROR("write sixzone lut failed ret %d\n", rc);
+ return;
+ }
+
+ REG_DMA_SETUP_OPS(dma_write_cfg,
+ ctx->cap->sblk->sixzone.base + SIXZONE_THRESHOLDS_OFF,
+ &sixzone->threshold, 3 * sizeof(u32),
+ REG_BLK_WRITE_SINGLE, 0, 0);
+ rc = dma_ops->setup_payload(&dma_write_cfg);
+ if (rc) {
+ DRM_ERROR("write sixzone threshold failed ret %d\n", rc);
+ return;
+ }
+
+ REG_DMA_SETUP_KICKOFF(kick_off, hw_cfg->ctl,
+ dspp_buf[SIX_ZONE][ctx->idx],
+ REG_DMA_WRITE, DMA_CTL_QUEUE0, WRITE_IMMEDIATE);
+ rc = dma_ops->kick_off(&kick_off);
+ if (rc)
+ DRM_ERROR("failed to kick off ret %d\n", rc);
+
+ hold = SDE_REG_READ(&ctx->hw,
+ (ctx->cap->sblk->hsic.base + PA_PWL_HOLD_OFF));
+ local_hold = ((sixzone->sat_hold & REG_MASK(2)) << 12);
+ local_hold |= ((sixzone->val_hold & REG_MASK(2)) << 14);
+ hold &= ~REG_MASK_SHIFT(4, 12);
+ hold |= local_hold;
+ SDE_REG_WRITE(&ctx->hw,
+ (ctx->cap->sblk->hsic.base + PA_PWL_HOLD_OFF), hold);
+
+ if (sixzone->flags & SIXZONE_HUE_ENABLE)
+ local_opcode |= PA_SIXZONE_HUE_EN;
+ if (sixzone->flags & SIXZONE_SAT_ENABLE)
+ local_opcode |= PA_SIXZONE_SAT_EN;
+ if (sixzone->flags & SIXZONE_VAL_ENABLE)
+ local_opcode |= PA_SIXZONE_VAL_EN;
+
+ if (local_opcode)
+ local_opcode |= PA_EN;
+
+ opcode &= ~REG_MASK_SHIFT(3, 29);
+ opcode |= local_opcode;
+ SDE_REG_WRITE(&ctx->hw, ctx->cap->sblk->hsic.base, opcode);
+}
+
int reg_dmav1_deinit_dspp_ops(enum sde_dspp idx)
{
int i;
diff --git a/drivers/gpu/drm/msm/sde/sde_hw_reg_dma_v1_color_proc.h b/drivers/gpu/drm/msm/sde/sde_hw_reg_dma_v1_color_proc.h
index bb72c8f..a8115d6 100644
--- a/drivers/gpu/drm/msm/sde/sde_hw_reg_dma_v1_color_proc.h
+++ b/drivers/gpu/drm/msm/sde/sde_hw_reg_dma_v1_color_proc.h
@@ -67,6 +67,20 @@
void reg_dmav1_setup_dspp_pccv4(struct sde_hw_dspp *ctx, void *cfg);
/**
+ * reg_dmav1_setup_dspp_pa_hsicv18() - pa hsic v18 impl using reg dma v1.
+ * @ctx: dspp ctx info
+ * @cfg: pointer to struct sde_hw_cp_cfg
+ */
+void reg_dmav1_setup_dspp_pa_hsicv18(struct sde_hw_dspp *ctx, void *cfg);
+
+/**
+ * reg_dmav1_setup_dspp_sixzonev18() - sixzone v18 impl using reg dma v1.
+ * @ctx: dspp ctx info
+ * @cfg: pointer to struct sde_hw_cp_cfg
+ */
+void reg_dmav1_setup_dspp_sixzonev18(struct sde_hw_dspp *ctx, void *cfg);
+
+/**
* reg_dmav1_deinit_dspp_ops() - deinitialize the dspp feature op for sde v4
* which were initialized.
* @idx: dspp idx
diff --git a/drivers/gpu/drm/msm/sde/sde_hw_top.c b/drivers/gpu/drm/msm/sde/sde_hw_top.c
index ecb445d..b48022f 100644
--- a/drivers/gpu/drm/msm/sde/sde_hw_top.c
+++ b/drivers/gpu/drm/msm/sde/sde_hw_top.c
@@ -176,6 +176,7 @@
new_val = reg_val & ~BIT(bit_off);
SDE_REG_WRITE(c, reg_off, new_val);
+ wmb(); /* ensure write finished before progressing */
clk_forced_on = !(reg_val & BIT(bit_off));
diff --git a/drivers/gpu/drm/msm/sde/sde_irq.c b/drivers/gpu/drm/msm/sde/sde_irq.c
index 76f89f4..d6a1d30 100644
--- a/drivers/gpu/drm/msm/sde/sde_irq.c
+++ b/drivers/gpu/drm/msm/sde/sde_irq.c
@@ -23,7 +23,6 @@
void sde_irq_update(struct msm_kms *msm_kms, bool enable)
{
- int irq_num;
struct sde_kms *sde_kms = to_sde_kms(msm_kms);
if (!msm_kms || !sde_kms) {
@@ -31,16 +30,10 @@
return;
}
- irq_num = platform_get_irq(sde_kms->dev->platformdev, 0);
- if (irq_num < 0) {
- SDE_ERROR("invalid irq number\n");
- return;
- }
-
if (enable)
- enable_irq(irq_num);
+ enable_irq(sde_kms->irq_num);
else
- disable_irq(irq_num);
+ disable_irq(sde_kms->irq_num);
}
irqreturn_t sde_irq(struct msm_kms *kms)
@@ -103,6 +96,15 @@
}
sde_core_irq_preinstall(sde_kms);
+
+ sde_kms->irq_num = platform_get_irq(sde_kms->dev->platformdev, 0);
+ if (sde_kms->irq_num < 0) {
+ SDE_ERROR("invalid irq number %d\n", sde_kms->irq_num);
+ return;
+ }
+
+ /* disable irq until power event enables it */
+ irq_set_status_flags(sde_kms->irq_num, IRQ_NOAUTOEN);
}
int sde_irq_postinstall(struct msm_kms *kms)
diff --git a/drivers/gpu/drm/msm/sde/sde_kms.c b/drivers/gpu/drm/msm/sde/sde_kms.c
index bf06dfb..92d16e5 100644
--- a/drivers/gpu/drm/msm/sde/sde_kms.c
+++ b/drivers/gpu/drm/msm/sde/sde_kms.c
@@ -433,8 +433,7 @@
* secure transition can be initiated.
*/
ops = sde_crtc_get_secure_transition_ops(crtc,
- old_crtc_state,
- old_valid_fb);
+ old_crtc_state, old_valid_fb);
if (ops < 0) {
SDE_ERROR("invalid secure operations %x\n", ops);
return ops;
@@ -444,9 +443,8 @@
goto no_ops;
SDE_DEBUG("%d:secure operations(%x) started on state:%pK\n",
- crtc->base.id,
- ops,
- crtc->state);
+ crtc->base.id, ops, crtc->state);
+ SDE_EVT32(DRMID(crtc), ops, crtc->state, old_valid_fb);
/* 3. Perform operations needed for secure transition */
if (ops & SDE_KMS_OPS_WAIT_FOR_TX_DONE) {
@@ -485,6 +483,7 @@
}
}
}
+ SDE_EVT32(DRMID(crtc), SDE_EVTLOG_FUNC_EXIT);
SDE_DEBUG("secure operations completed\n");
}
@@ -1569,15 +1568,132 @@
kfree(sde_kms);
}
+static void _sde_kms_plane_force_remove(struct drm_plane *plane,
+ struct drm_atomic_state *state)
+{
+ struct drm_plane_state *plane_state;
+ int ret = 0;
+
+ if (!plane->crtc)
+ return;
+
+ plane_state = drm_atomic_get_plane_state(state, plane);
+ if (IS_ERR(plane_state)) {
+ ret = PTR_ERR(plane_state);
+ SDE_ERROR("error %d getting plane %d state\n",
+ ret, plane->base.id);
+ return;
+ }
+
+ plane->old_fb = plane->fb;
+
+ SDE_DEBUG("disabling plane %d\n", plane->base.id);
+
+ ret = __drm_atomic_helper_disable_plane(plane, plane_state);
+ if (ret != 0)
+ SDE_ERROR("error %d disabling plane %d\n", ret,
+ plane->base.id);
+}
+
+static int _sde_kms_remove_fbs(struct sde_kms *sde_kms, struct drm_file *file,
+ struct drm_atomic_state *state)
+{
+ struct drm_device *dev = sde_kms->dev;
+ struct drm_framebuffer *fb, *tfb;
+ struct list_head fbs;
+ struct drm_plane *plane;
+ int ret = 0;
+ u32 plane_mask = 0;
+
+ INIT_LIST_HEAD(&fbs);
+
+ list_for_each_entry_safe(fb, tfb, &file->fbs, filp_head) {
+ if (drm_framebuffer_read_refcount(fb) > 1) {
+ list_move_tail(&fb->filp_head, &fbs);
+
+ drm_for_each_plane(plane, dev) {
+ if (plane->fb == fb) {
+ plane_mask |=
+ 1 << drm_plane_index(plane);
+ _sde_kms_plane_force_remove(
+ plane, state);
+ }
+ }
+ } else {
+ list_del_init(&fb->filp_head);
+ drm_framebuffer_unreference(fb);
+ }
+ }
+
+ if (list_empty(&fbs)) {
+ SDE_DEBUG("skip commit as no fb(s)\n");
+ drm_atomic_state_free(state);
+ return 0;
+ }
+
+ SDE_DEBUG("committing after removing all the pipes\n");
+ ret = drm_atomic_commit(state);
+
+ if (ret) {
+ /*
+ * move the fbs back to original list, so it would be
+ * handled during drm_release
+ */
+ list_for_each_entry_safe(fb, tfb, &fbs, filp_head)
+ list_move_tail(&fb->filp_head, &file->fbs);
+
+ SDE_ERROR("atomic commit failed in preclose, ret:%d\n", ret);
+ goto end;
+ }
+
+ while (!list_empty(&fbs)) {
+ fb = list_first_entry(&fbs, typeof(*fb), filp_head);
+
+ list_del_init(&fb->filp_head);
+ drm_framebuffer_unreference(fb);
+ }
+
+end:
+ drm_atomic_clean_old_fb(dev, plane_mask, ret);
+
+ return ret;
+}
+
static void sde_kms_preclose(struct msm_kms *kms, struct drm_file *file)
{
struct sde_kms *sde_kms = to_sde_kms(kms);
struct drm_device *dev = sde_kms->dev;
struct msm_drm_private *priv = dev->dev_private;
unsigned int i;
+ struct drm_atomic_state *state = NULL;
+ int ret = 0;
for (i = 0; i < priv->num_crtcs; i++)
sde_crtc_cancel_pending_flip(priv->crtcs[i], file);
+
+ drm_modeset_lock_all(dev);
+ state = drm_atomic_state_alloc(dev);
+ if (!state) {
+ ret = -ENOMEM;
+ goto end;
+ }
+
+ state->acquire_ctx = dev->mode_config.acquire_ctx;
+
+ for (i = 0; i < TEARDOWN_DEADLOCK_RETRY_MAX; i++) {
+ ret = _sde_kms_remove_fbs(sde_kms, file, state);
+ if (ret != -EDEADLK)
+ break;
+ drm_atomic_state_clear(state);
+ drm_atomic_legacy_backoff(state);
+ }
+
+end:
+ if ((ret != 0) && state)
+ drm_atomic_state_free(state);
+
+ SDE_DEBUG("sde preclose done, ret:%d\n", ret);
+ drm_modeset_unlock_all(dev);
}
static int sde_kms_check_secure_transition(struct msm_kms *kms,
diff --git a/drivers/gpu/drm/msm/sde/sde_kms.h b/drivers/gpu/drm/msm/sde/sde_kms.h
index aacff78..f047305 100644
--- a/drivers/gpu/drm/msm/sde/sde_kms.h
+++ b/drivers/gpu/drm/msm/sde/sde_kms.h
@@ -202,6 +202,7 @@
struct sde_hw_intr *hw_intr;
struct sde_irq irq_obj;
+ int irq_num; /* mdss irq number */
struct sde_core_perf perf;
diff --git a/drivers/gpu/drm/msm/sde/sde_plane.c b/drivers/gpu/drm/msm/sde/sde_plane.c
index f4672b8..ef06f39 100644
--- a/drivers/gpu/drm/msm/sde/sde_plane.c
+++ b/drivers/gpu/drm/msm/sde/sde_plane.c
@@ -223,6 +223,7 @@
u32 fixed_buff_size;
u32 total_fl;
u32 hflip_bytes;
+ u32 unused_space;
if (!plane || !fmt || !plane->state || !src_width || !fmt->bpp) {
SDE_ERROR("invalid arguments\n");
@@ -250,29 +251,36 @@
hflip_bytes = 0;
if (fmt->fetch_planes == SDE_PLANE_PSEUDO_PLANAR) {
+
+ unused_space = 23 * 128;
if (fmt->chroma_sample == SDE_CHROMA_420) {
/* NV12 */
- total_fl = (fixed_buff_size / 2 - hflip_bytes) /
- ((src_width + 32) * fmt->bpp);
+ total_fl = (fixed_buff_size / 2 - hflip_bytes -
+ unused_space) / ((src_width + 32) * fmt->bpp);
} else {
/* non NV12 */
- total_fl = (fixed_buff_size / 2 - hflip_bytes) * 2 /
- ((src_width + 32) * fmt->bpp);
+ total_fl = (fixed_buff_size / 2 - hflip_bytes -
+ unused_space) * 2 / ((src_width + 32) *
+ fmt->bpp);
}
} else {
+
+ unused_space = 6 * 128;
if (pstate->multirect_mode == SDE_SSPP_MULTIRECT_PARALLEL) {
- total_fl = (fixed_buff_size / 2 - hflip_bytes) * 2 /
- ((src_width + 32) * fmt->bpp);
+ total_fl = (fixed_buff_size / 2 - hflip_bytes -
+ unused_space) * 2 / ((src_width + 32) *
+ fmt->bpp);
} else {
- total_fl = (fixed_buff_size - hflip_bytes) * 2 /
- ((src_width + 32) * fmt->bpp);
+ total_fl = (fixed_buff_size - hflip_bytes -
+ unused_space) * 2 / ((src_width + 32) *
+ fmt->bpp);
}
}
- SDE_DEBUG("plane%u: pnum:%d fmt: %4.4s w:%u hf:%d fl:%u\n",
+ SDE_DEBUG("plane%u: pnum:%d fmt: %4.4s w:%u hf:%d us:%d fl:%u\n",
plane->base.id, psde->pipe - SSPP_VIG0,
(char *)&fmt->base.pixel_format,
- src_width, hflip_bytes, total_fl);
+ src_width, hflip_bytes, unused_space, total_fl);
return total_fl;
}
@@ -375,6 +383,7 @@
struct sde_plane *psde;
const struct sde_format *fmt = NULL;
u32 danger_lut, safe_lut;
+ u32 total_fl = 0, lut_usage;
if (!plane || !fb) {
SDE_ERROR("invalid arguments\n");
@@ -393,27 +402,29 @@
if (!psde->is_rt_pipe) {
danger_lut = psde->catalog->perf.danger_lut_tbl
[SDE_QOS_LUT_USAGE_NRT];
- safe_lut = psde->catalog->perf.safe_lut_tbl
- [SDE_QOS_LUT_USAGE_NRT];
+ lut_usage = SDE_QOS_LUT_USAGE_NRT;
} else {
fmt = sde_get_sde_format_ext(
fb->pixel_format,
fb->modifier,
drm_format_num_planes(fb->pixel_format));
+ total_fl = _sde_plane_calc_fill_level(plane, fmt,
+ psde->pipe_cfg.src_rect.w);
if (fmt && SDE_FORMAT_IS_LINEAR(fmt)) {
danger_lut = psde->catalog->perf.danger_lut_tbl
[SDE_QOS_LUT_USAGE_LINEAR];
- safe_lut = psde->catalog->perf.safe_lut_tbl
- [SDE_QOS_LUT_USAGE_LINEAR];
+ lut_usage = SDE_QOS_LUT_USAGE_LINEAR;
} else {
danger_lut = psde->catalog->perf.danger_lut_tbl
[SDE_QOS_LUT_USAGE_MACROTILE];
- safe_lut = psde->catalog->perf.safe_lut_tbl
- [SDE_QOS_LUT_USAGE_MACROTILE];
+ lut_usage = SDE_QOS_LUT_USAGE_MACROTILE;
}
}
+ safe_lut = (u32) _sde_plane_get_qos_lut(
+ &psde->catalog->perf.sfe_lut_tbl[lut_usage], total_fl);
+
psde->pipe_qos_cfg.danger_lut = danger_lut;
psde->pipe_qos_cfg.safe_lut = safe_lut;
@@ -423,11 +434,11 @@
psde->pipe_qos_cfg.danger_lut,
psde->pipe_qos_cfg.safe_lut);
- SDE_DEBUG("plane%u: pnum:%d fmt: %4.4s mode:%d luts[0x%x, 0x%x]\n",
+ SDE_DEBUG("plane%u: pnum:%d fmt:%4.4s mode:%d fl:%d luts[0x%x,0x%x]\n",
plane->base.id,
psde->pipe - SSPP_VIG0,
fmt ? (char *)&fmt->base.pixel_format : NULL,
- fmt ? fmt->fetch_mode : -1,
+ fmt ? fmt->fetch_mode : -1, total_fl,
psde->pipe_qos_cfg.danger_lut,
psde->pipe_qos_cfg.safe_lut);
@@ -895,7 +906,8 @@
{
struct sde_plane *psde;
struct msm_gem_address_space *aspace = NULL;
- int ret;
+ int ret, mode;
+ bool secure = false;
if (!plane || !pstate || !pipe_cfg || !fb) {
SDE_ERROR(
@@ -923,6 +935,7 @@
* expected for one or two frames during the transition.
*/
if (aspace && pstate->defer_prepare_fb) {
+ SDE_EVT32(DRMID(plane), psde->pipe, aspace->domain_attached);
ret = msm_framebuffer_prepare(fb, pstate->aspace);
if (ret) {
SDE_ERROR_PLANE(psde,
@@ -931,6 +944,9 @@
}
pstate->defer_prepare_fb = false;
}
+ mode = sde_plane_get_property(pstate, PLANE_PROP_FB_TRANSLATION_MODE);
+ if ((mode == SDE_DRM_FB_SEC) || (mode == SDE_DRM_FB_SEC_DIR_TRANS))
+ secure = true;
ret = sde_format_populate_layout(aspace, fb, &pipe_cfg->layout);
if (ret == -EAGAIN)
@@ -949,7 +965,8 @@
pipe_cfg->layout.plane_size[2],
pipe_cfg->layout.plane_addr[3],
pipe_cfg->layout.plane_size[3],
- pstate->multirect_index);
+ pstate->multirect_index,
+ secure);
psde->pipe_hw->ops.setup_sourceaddress(psde->pipe_hw, pipe_cfg,
pstate->multirect_index);
}
@@ -2134,6 +2151,7 @@
}
if (new_pstate->defer_prepare_fb) {
+ SDE_EVT32(DRMID(plane));
SDE_DEBUG(
"plane%d, domain not attached, prepare fb handled later\n",
plane->base.id);
@@ -2438,6 +2456,7 @@
* This can be expected for one or two frames during the transition.
*/
if (pstate->aspace && pstate->defer_prepare_fb) {
+ SDE_EVT32(DRMID(plane), pstate->aspace->domain_attached);
/* prepare rotator input buffer */
ret = msm_framebuffer_prepare(state->fb, pstate->aspace);
if (ret) {
@@ -2841,6 +2860,7 @@
}
if (pstate->defer_prepare_fb) {
+ SDE_EVT32(DRMID(plane), psde->pipe);
SDE_DEBUG_PLANE(psde,
"domain not attached, prepare_fb handled later\n");
return 0;
@@ -2868,20 +2888,76 @@
return 0;
}
+/**
+ * _sde_plane_fetch_halt - halts vbif transactions for a plane
+ * @plane: Pointer to plane
+ * Returns: 0 on success
+ */
+static int _sde_plane_fetch_halt(struct drm_plane *plane)
+{
+ struct sde_plane *psde;
+ int xin_id;
+ enum sde_clk_ctrl_type clk_ctrl;
+ struct msm_drm_private *priv;
+ struct sde_kms *sde_kms;
+
+ psde = to_sde_plane(plane);
+ if (!plane || !plane->dev || !psde->pipe_hw) {
+ SDE_ERROR("invalid arguments\n");
+ return -EINVAL;
+ }
+
+ priv = plane->dev->dev_private;
+ if (!priv || !priv->kms) {
+ SDE_ERROR("invalid KMS reference\n");
+ return -EINVAL;
+ }
+
+ sde_kms = to_sde_kms(priv->kms);
+ clk_ctrl = psde->pipe_hw->cap->clk_ctrl;
+ xin_id = psde->pipe_hw->cap->xin_id;
+ SDE_DEBUG_PLANE(psde, "pipe:%d xin_id:%d clk_ctrl:%d\n",
+ psde->pipe - SSPP_VIG0, xin_id, clk_ctrl);
+ SDE_EVT32_VERBOSE(psde, psde->pipe - SSPP_VIG0, xin_id, clk_ctrl);
+
+ return sde_vbif_halt_plane_xin(sde_kms, xin_id, clk_ctrl);
+}
+
static void sde_plane_cleanup_fb(struct drm_plane *plane,
struct drm_plane_state *old_state)
{
struct sde_plane *psde = to_sde_plane(plane);
struct sde_plane_state *old_pstate;
struct sde_plane_rot_state *old_rstate;
+ int ret;
- if (!old_state || !old_state->fb)
+ if (!old_state || !old_state->fb || !plane || !plane->state)
return;
old_pstate = to_sde_plane_state(old_state);
SDE_DEBUG_PLANE(psde, "FB[%u]\n", old_state->fb->base.id);
+ /*
+ * plane->state gets populated for next frame after swap_state. If
+ * plane->state->crtc pointer is not populated then it is not used in
+ * the next frame, hence making it an unused plane.
+ */
+ if ((plane->state->crtc == NULL) && !psde->is_virtual) {
+ SDE_DEBUG_PLANE(psde, "unused pipe:%u\n",
+ psde->pipe - SSPP_VIG0);
+
+ /* halt this plane now */
+ ret = _sde_plane_fetch_halt(plane);
+ if (ret) {
+ SDE_ERROR_PLANE(psde,
+ "unused pipe %u halt failed\n",
+ psde->pipe - SSPP_VIG0);
+ SDE_EVT32(DRMID(plane), psde->pipe - SSPP_VIG0,
+ ret, SDE_EVTLOG_ERROR);
+ }
+ }
+
old_rstate = &old_pstate->rot;
msm_framebuffer_cleanup(old_rstate->out_fb, old_pstate->aspace);
@@ -3001,8 +3077,9 @@
return -EINVAL;
}
- /* don't run checks unless scaler data was changed */
- if (pstate->scaler_check_state != SDE_PLANE_SCLCHECK_SCALER_V2_CHECK)
+ if (psde->debugfs_default_scale ||
+ (pstate->scaler_check_state != SDE_PLANE_SCLCHECK_SCALER_V2 &&
+ pstate->scaler_check_state != SDE_PLANE_SCLCHECK_SCALER_V2_CHECK))
return 0;
pstate->scaler_check_state = SDE_PLANE_SCLCHECK_INVALID;
diff --git a/drivers/gpu/drm/msm/sde/sde_rm.c b/drivers/gpu/drm/msm/sde/sde_rm.c
index ebcf9e0..21fbcb5 100644
--- a/drivers/gpu/drm/msm/sde/sde_rm.c
+++ b/drivers/gpu/drm/msm/sde/sde_rm.c
@@ -419,8 +419,8 @@
struct sde_lm_cfg *lm = &cat->mixer[i];
if (lm->pingpong == PINGPONG_MAX) {
- SDE_DEBUG("skip mixer %d without pingpong\n", lm->id);
- continue;
+ SDE_ERROR("mixer %d without pingpong\n", lm->id);
+ goto fail;
}
rc = _sde_rm_hw_blk_create(rm, cat, mmio, SDE_HW_BLK_LM,
@@ -570,14 +570,16 @@
const struct sde_pingpong_cfg *pp_cfg;
struct sde_rm_hw_iter iter;
bool is_valid_dspp, is_valid_ds, ret;
+ u32 display_pref;
*dspp = NULL;
*ds = NULL;
*pp = NULL;
+ display_pref = lm_cfg->features & BIT(SDE_DISP_PRIMARY_PREF);
- SDE_DEBUG("check lm %d: dspp %d ds %d pp %d\n",
- lm_cfg->id, lm_cfg->dspp,
- lm_cfg->ds, lm_cfg->pingpong);
+ SDE_DEBUG("check lm %d: dspp %d ds %d pp %d display_pref: %d\n",
+ lm_cfg->id, lm_cfg->dspp, lm_cfg->ds,
+ lm_cfg->pingpong, display_pref);
/* Check if this layer mixer is a peer of the proposed primary LM */
if (primary_lm) {
@@ -591,28 +593,39 @@
}
}
- is_valid_dspp = (lm_cfg->dspp != DSPP_MAX) ? true : false;
- is_valid_ds = (lm_cfg->ds != DS_MAX) ? true : false;
+ /* bypass rest of the checks if LM for primary display is found */
+ if (!display_pref) {
+ is_valid_dspp = (lm_cfg->dspp != DSPP_MAX) ? true : false;
+ is_valid_ds = (lm_cfg->ds != DS_MAX) ? true : false;
- /**
- * RM_RQ_X: specification of which LMs to choose
- * is_valid_X: indicates whether LM is tied with block X
- * ret: true if given LM matches the user requirement, false otherwise
- */
- if (RM_RQ_DSPP(reqs) && RM_RQ_DS(reqs))
- ret = (is_valid_dspp && is_valid_ds);
- else if (RM_RQ_DSPP(reqs))
- ret = is_valid_dspp;
- else if (RM_RQ_DS(reqs))
- ret = is_valid_ds;
- else
- ret = !(is_valid_dspp || is_valid_ds);
+ /**
+ * RM_RQ_X: specification of which LMs to choose
+ * is_valid_X: indicates whether LM is tied with block X
+ * ret: true if given LM matches the user requirement,
+ * false otherwise
+ */
+ if (RM_RQ_DSPP(reqs) && RM_RQ_DS(reqs))
+ ret = (is_valid_dspp && is_valid_ds);
+ else if (RM_RQ_DSPP(reqs))
+ ret = is_valid_dspp;
+ else if (RM_RQ_DS(reqs))
+ ret = is_valid_ds;
+ else
+ ret = !(is_valid_dspp || is_valid_ds);
- if (!ret) {
- SDE_DEBUG("fail:lm(%d)req_dspp(%d)dspp(%d)req_ds(%d)ds(%d)\n",
- lm_cfg->id, (bool)(RM_RQ_DSPP(reqs)), lm_cfg->dspp,
- (bool)(RM_RQ_DS(reqs)), lm_cfg->ds);
- return ret;
+ if (!ret) {
+ SDE_DEBUG(
+ "fail:lm(%d)req_dspp(%d)dspp(%d)req_ds(%d)ds(%d)\n",
+ lm_cfg->id, (bool)(RM_RQ_DSPP(reqs)),
+ lm_cfg->dspp, (bool)(RM_RQ_DS(reqs)),
+ lm_cfg->ds);
+ return ret;
+ }
+ } else if (!(reqs->hw_res.is_primary && display_pref)) {
+ SDE_DEBUG(
+ "display preference is not met. is_primary: %d display_pref: %d\n",
+ (int)reqs->hw_res.is_primary, (int)display_pref);
+ return false;
}
/* Already reserved? */
@@ -804,6 +817,7 @@
static int _sde_rm_reserve_ctls(
struct sde_rm *rm,
struct sde_rm_rsvp *rsvp,
+ struct sde_rm_requirements *reqs,
const struct sde_rm_topology_def *top)
{
struct sde_rm_hw_blk *ctls[MAX_BLOCKS];
@@ -816,21 +830,34 @@
while (_sde_rm_get_hw_locked(rm, &iter)) {
const struct sde_hw_ctl *ctl = to_sde_hw_ctl(iter.blk->hw);
unsigned long features = ctl->caps->features;
- bool has_split_display, has_ppsplit;
+ bool has_split_display, has_ppsplit, primary_pref;
if (RESERVED_BY_OTHER(iter.blk, rsvp))
continue;
has_split_display = BIT(SDE_CTL_SPLIT_DISPLAY) & features;
has_ppsplit = BIT(SDE_CTL_PINGPONG_SPLIT) & features;
+ primary_pref = BIT(SDE_CTL_PRIMARY_PREF) & features;
SDE_DEBUG("ctl %d caps 0x%lX\n", iter.blk->id, features);
- if (top->needs_split_display != has_split_display)
- continue;
+ /*
+ * bypass rest feature checks on finding CTL preferred
+ * for primary displays.
+ */
+ if (!primary_pref) {
+ if (top->needs_split_display != has_split_display)
+ continue;
- if (top->top_name == SDE_RM_TOPOLOGY_PPSPLIT && !has_ppsplit)
+ if (top->top_name == SDE_RM_TOPOLOGY_PPSPLIT &&
+ !has_ppsplit)
+ continue;
+ } else if (!(reqs->hw_res.is_primary && primary_pref)) {
+ SDE_DEBUG(
+ "display pref not met. is_primary: %d primary_pref: %d\n",
+ reqs->hw_res.is_primary, primary_pref);
continue;
+ }
ctls[i] = iter.blk;
SDE_DEBUG("ctl %d match\n", iter.blk->id);
@@ -1032,11 +1059,11 @@
* - Check mixers without Split Display
* - Only then allow to grab from CTLs with split display capability
*/
- _sde_rm_reserve_ctls(rm, rsvp, reqs->topology);
+ _sde_rm_reserve_ctls(rm, rsvp, reqs, reqs->topology);
if (ret && !reqs->topology->needs_split_display) {
memcpy(&topology, reqs->topology, sizeof(topology));
topology.needs_split_display = true;
- _sde_rm_reserve_ctls(rm, rsvp, &topology);
+ _sde_rm_reserve_ctls(rm, rsvp, reqs, &topology);
}
if (ret) {
SDE_ERROR("unable to find appropriate CTL\n");
@@ -1084,10 +1111,6 @@
return -EINVAL;
}
- /* DSC blocks are hardwired for control path 0 and 1 */
- if (reqs->topology->num_comp_enc)
- reqs->top_ctrl |= BIT(SDE_RM_TOPCTL_DSPP);
-
/**
* Set the requirement based on caps if not set from user space
* This will ensure to select LM tied with DS blocks
@@ -1145,6 +1168,33 @@
return NULL;
}
+int sde_rm_update_topology(struct drm_connector_state *conn_state,
+ struct msm_display_topology *topology)
+{
+ int i, ret = 0;
+ struct msm_display_topology top;
+ enum sde_rm_topology_name top_name = SDE_RM_TOPOLOGY_NONE;
+
+ if (!conn_state)
+ return -EINVAL;
+
+ if (topology) {
+ top = *topology;
+ for (i = 0; i < SDE_RM_TOPOLOGY_MAX; i++)
+ if (RM_IS_TOPOLOGY_MATCH(g_top_table[i], top)) {
+ top_name = g_top_table[i].top_name;
+ break;
+ }
+ }
+
+ ret = msm_property_set_property(
+ sde_connector_get_propinfo(conn_state->connector),
+ sde_connector_get_property_state(conn_state),
+ CONNECTOR_PROP_TOPOLOGY_NAME, top_name);
+
+ return ret;
+}
+
/**
* _sde_rm_release_rsvp - release resources and release a reservation
* @rm: KMS handle
@@ -1226,12 +1276,6 @@
SDE_DEBUG("release rsvp[s%de%d]\n", rsvp->seq,
rsvp->enc_id);
_sde_rm_release_rsvp(rm, rsvp, conn);
-
- (void) msm_property_set_property(
- sde_connector_get_propinfo(conn),
- sde_connector_get_property_state(conn->state),
- CONNECTOR_PROP_TOPOLOGY_NAME,
- SDE_RM_TOPOLOGY_NONE);
}
end:
@@ -1247,18 +1291,6 @@
enum sde_hw_blk_type type;
int ret = 0;
- ret = msm_property_set_property(
- sde_connector_get_propinfo(conn_state->connector),
- sde_connector_get_property_state(conn_state),
- CONNECTOR_PROP_TOPOLOGY_NAME,
- rsvp->topology);
- if (ret) {
- SDE_ERROR("failed to set topology name property, ret %d\n",
- ret);
- _sde_rm_release_rsvp(rm, rsvp, conn_state->connector);
- return ret;
- }
-
/* Swap next rsvp to be the active */
for (type = 0; type < SDE_HW_BLK_MAX; type++) {
list_for_each_entry(blk, &rm->hw_blks[type], list) {
@@ -1343,12 +1375,6 @@
_sde_rm_release_rsvp(rm, rsvp_cur, conn_state->connector);
rsvp_cur = NULL;
_sde_rm_print_rsvps(rm, SDE_RM_STAGE_AFTER_CLEAR);
- (void) msm_property_set_property(
- sde_connector_get_propinfo(
- conn_state->connector),
- sde_connector_get_property_state(conn_state),
- CONNECTOR_PROP_TOPOLOGY_NAME,
- SDE_RM_TOPOLOGY_NONE);
}
/* Check the proposed reservation, store it in hw's "next" field */
diff --git a/drivers/gpu/drm/msm/sde/sde_rm.h b/drivers/gpu/drm/msm/sde/sde_rm.h
index 1e48855..3b9b82f 100644
--- a/drivers/gpu/drm/msm/sde/sde_rm.h
+++ b/drivers/gpu/drm/msm/sde/sde_rm.h
@@ -197,4 +197,13 @@
*/
int sde_rm_check_property_topctl(uint64_t val);
+/**
+ * sde_rm_update_topology - sets topology property of the connector
+ * @conn_state: drm state of the connector
+ * @topology: topology selected for the display
+ * @return: 0 on success or error
+ */
+int sde_rm_update_topology(struct drm_connector_state *conn_state,
+ struct msm_display_topology *topology);
+
#endif /* __SDE_RM_H__ */
diff --git a/drivers/gpu/drm/msm/sde/sde_vbif.c b/drivers/gpu/drm/msm/sde/sde_vbif.c
index 7fcd03e..2cdc2f3 100644
--- a/drivers/gpu/drm/msm/sde/sde_vbif.c
+++ b/drivers/gpu/drm/msm/sde/sde_vbif.c
@@ -60,6 +60,67 @@
return rc;
}
+int sde_vbif_halt_plane_xin(struct sde_kms *sde_kms, u32 xin_id, u32 clk_ctrl)
+{
+ struct sde_hw_vbif *vbif = NULL;
+ struct sde_hw_mdp *mdp;
+ bool forced_on = false;
+ bool status;
+ int rc = 0;
+
+ if (!sde_kms) {
+ SDE_ERROR("invalid argument\n");
+ return -EINVAL;
+ }
+
+ vbif = sde_kms->hw_vbif[VBIF_RT];
+ mdp = sde_kms->hw_mdp;
+ if (!vbif || !mdp || !vbif->ops.get_halt_ctrl ||
+ !vbif->ops.set_halt_ctrl ||
+ !mdp->ops.setup_clk_force_ctrl) {
+ SDE_ERROR("invalid vbif or mdp arguments\n");
+ return -EINVAL;
+ }
+
+ /*
+ * If status is 0, then make sure client clock is not gated
+ * while halting by forcing it ON only if it was not previously
+ * forced on. If status is 1 then its already halted.
+ */
+ status = vbif->ops.get_halt_ctrl(vbif, xin_id);
+ if (status == 0)
+ forced_on = mdp->ops.setup_clk_force_ctrl(mdp, clk_ctrl, true);
+ else
+ return 0;
+
+ /* send halt request for unused plane's xin client */
+ vbif->ops.set_halt_ctrl(vbif, xin_id, true);
+
+ rc = _sde_vbif_wait_for_xin_halt(vbif, xin_id);
+ if (rc) {
+ SDE_ERROR(
+ "wait failed for pipe halt:xin_id %u, clk_ctrl %u, rc %u\n",
+ xin_id, clk_ctrl, rc);
+ SDE_EVT32(xin_id, clk_ctrl, rc, SDE_EVTLOG_ERROR);
+ return rc;
+ }
+
+ status = vbif->ops.get_halt_ctrl(vbif, xin_id);
+ if (status == 0) {
+ SDE_ERROR("halt failed for pipe xin_id %u halt clk_ctrl %u\n",
+ xin_id, clk_ctrl);
+ SDE_EVT32(xin_id, clk_ctrl, SDE_EVTLOG_ERROR);
+ return -ETIMEDOUT;
+ }
+
+ /* open xin client to enable transactions */
+ vbif->ops.set_halt_ctrl(vbif, xin_id, false);
+ if (forced_on)
+ mdp->ops.setup_clk_force_ctrl(mdp, clk_ctrl, false);
+
+ return 0;
+}
+
/**
* _sde_vbif_apply_dynamic_ot_limit - determine OT based on usecase parameters
* @vbif: Pointer to hardware vbif driver
diff --git a/drivers/gpu/drm/msm/sde/sde_vbif.h b/drivers/gpu/drm/msm/sde/sde_vbif.h
index a4830a0..30cc416 100644
--- a/drivers/gpu/drm/msm/sde/sde_vbif.h
+++ b/drivers/gpu/drm/msm/sde/sde_vbif.h
@@ -78,6 +78,20 @@
*/
void sde_vbif_init_memtypes(struct sde_kms *sde_kms);
+/**
+ * sde_vbif_halt_plane_xin - halts the xin client for the unused plane
+ * On unused plane, check if the vbif for this plane is idle or not.
+ * If not then first force_on the planes clock and then send the
+ * halt request. Wait for some time then check for the vbif idle
+ * or not again.
+ * @sde_kms: SDE handler
+ * @xin_id: xin id of the unused plane
+ * @clk_ctrl: clk ctrl type for the unused plane
+ * Returns: 0 on success, error code otherwise
+ */
+int sde_vbif_halt_plane_xin(struct sde_kms *sde_kms, u32 xin_id,
+ u32 clk_ctrl);
+
#ifdef CONFIG_DEBUG_FS
int sde_debugfs_vbif_init(struct sde_kms *sde_kms, struct dentry *debugfs_root);
void sde_debugfs_vbif_destroy(struct sde_kms *sde_kms);
diff --git a/drivers/gpu/drm/msm/sde/sde_wb.c b/drivers/gpu/drm/msm/sde/sde_wb.c
index 576a8f3..8c0854f 100644
--- a/drivers/gpu/drm/msm/sde/sde_wb.c
+++ b/drivers/gpu/drm/msm/sde/sde_wb.c
@@ -162,7 +162,9 @@
u32 count_modes, struct drm_mode_modeinfo __user *modes,
bool connected)
{
+ struct drm_mode_modeinfo *modeinfo = NULL;
int ret = 0;
+ int i;
if (!wb_dev || !wb_dev->connector ||
(wb_dev->connector->connector_type !=
@@ -176,6 +178,56 @@
if (connected) {
SDE_DEBUG("connect\n");
+ if (count_modes && modes) {
+ modeinfo = kcalloc(count_modes,
+ sizeof(struct drm_mode_modeinfo),
+ GFP_KERNEL);
+ if (!modeinfo) {
+ SDE_ERROR("invalid params\n");
+ ret = -ENOMEM;
+ goto error;
+ }
+
+ if (copy_from_user(modeinfo, modes,
+ count_modes *
+ sizeof(struct drm_mode_modeinfo))) {
+ SDE_ERROR("failed to copy modes\n");
+ kfree(modeinfo);
+ ret = -EFAULT;
+ goto error;
+ }
+
+ for (i = 0; i < count_modes; i++) {
+ struct drm_display_mode dispmode;
+
+ memset(&dispmode, 0, sizeof(dispmode));
+ ret = drm_mode_convert_umode(&dispmode,
+ &modeinfo[i]);
+ if (ret) {
+ SDE_ERROR(
+ "failed to convert mode %d:\"%s\" %d %d %d %d %d %d %d %d %d %d 0x%x 0x%x status:%d rc:%d\n",
+ i,
+ modeinfo[i].name,
+ modeinfo[i].vrefresh,
+ modeinfo[i].clock,
+ modeinfo[i].hdisplay,
+ modeinfo[i].hsync_start,
+ modeinfo[i].hsync_end,
+ modeinfo[i].htotal,
+ modeinfo[i].vdisplay,
+ modeinfo[i].vsync_start,
+ modeinfo[i].vsync_end,
+ modeinfo[i].vtotal,
+ modeinfo[i].type,
+ modeinfo[i].flags,
+ dispmode.status,
+ ret);
+ kfree(modeinfo);
+ goto error;
+ }
+ }
+ }
+
if (wb_dev->modes) {
wb_dev->count_modes = 0;
@@ -183,29 +235,8 @@
wb_dev->modes = NULL;
}
- if (count_modes && modes) {
- wb_dev->modes = kcalloc(count_modes,
- sizeof(struct drm_mode_modeinfo),
- GFP_KERNEL);
- if (!wb_dev->modes) {
- SDE_ERROR("invalid params\n");
- ret = -ENOMEM;
- goto error;
- }
-
- if (copy_from_user(wb_dev->modes, modes,
- count_modes *
- sizeof(struct drm_mode_modeinfo))) {
- SDE_ERROR("failed to copy modes\n");
- kfree(wb_dev->modes);
- wb_dev->modes = NULL;
- ret = -EFAULT;
- goto error;
- }
-
- wb_dev->count_modes = count_modes;
- }
-
+ wb_dev->count_modes = count_modes;
+ wb_dev->modes = modeinfo;
wb_dev->detect_status = connector_status_connected;
} else {
SDE_DEBUG("disconnect\n");
@@ -289,22 +320,30 @@
}
int sde_wb_get_mode_info(const struct drm_display_mode *drm_mode,
- struct msm_mode_info *mode_info, u32 max_mixer_width)
+ struct msm_mode_info *mode_info, u32 max_mixer_width, void *display)
{
const u32 dual_lm = 2;
const u32 single_lm = 1;
const u32 single_intf = 1;
const u32 no_enc = 0;
struct msm_display_topology *topology;
+ struct sde_wb_device *wb_dev = display;
+ u16 hdisplay;
+ int i;
- if (!drm_mode || !mode_info || !max_mixer_width) {
+ if (!drm_mode || !mode_info || !max_mixer_width || !display) {
pr_err("invalid params\n");
return -EINVAL;
}
+ hdisplay = drm_mode->hdisplay;
+
+ /* find maximum display width to support */
+ for (i = 0; i < wb_dev->count_modes; i++)
+ hdisplay = max(hdisplay, wb_dev->modes[i].hdisplay);
+
topology = &mode_info->topology;
- topology->num_lm = (max_mixer_width <= drm_mode->hdisplay) ?
- dual_lm : single_lm;
+ topology->num_lm = (max_mixer_width <= hdisplay) ? dual_lm : single_lm;
topology->num_enc = no_enc;
topology->num_intf = single_intf;
diff --git a/drivers/gpu/drm/msm/sde/sde_wb.h b/drivers/gpu/drm/msm/sde/sde_wb.h
index aa57d3e..c3f9e06 100644
--- a/drivers/gpu/drm/msm/sde/sde_wb.h
+++ b/drivers/gpu/drm/msm/sde/sde_wb.h
@@ -190,10 +190,12 @@
* @drm_mode: Display mode set for the display
* @mode_info: Out parameter. information of the mode.
* @max_mixer_width: max width supported by HW layer mixer
+ * @display: Pointer to private display structure
* Returns: zero on success
*/
int sde_wb_get_mode_info(const struct drm_display_mode *drm_mode,
- struct msm_mode_info *mode_info, u32 max_mixer_width);
+ struct msm_mode_info *mode_info, u32 max_mixer_width,
+ void *display);
/**
* sde_wb_connector_get_wb - retrieve writeback device of the given connector
diff --git a/drivers/gpu/drm/msm/sde_dbg.c b/drivers/gpu/drm/msm/sde_dbg.c
index dc16ab1..c2ce9f0 100644
--- a/drivers/gpu/drm/msm/sde_dbg.c
+++ b/drivers/gpu/drm/msm/sde_dbg.c
@@ -168,6 +168,7 @@
* @evtlog: event log instance
* @reg_base_list: list of register dumping regions
* @dev: device pointer
+ * @mutex: mutex to serialize access to serialze dumps, debugfs access
* @power_ctrl: callback structure for enabling power for reading hw registers
* @req_dump_blks: list of blocks requested for dumping
* @panic_on_err: whether to kernel panic after triggering dump via debugfs
@@ -183,6 +184,7 @@
struct sde_dbg_evtlog *evtlog;
struct list_head reg_base_list;
struct device *dev;
+ struct mutex mutex;
struct sde_dbg_power_ctrl power_ctrl;
struct sde_dbg_reg_base *req_dump_blks[SDE_DBG_BASE_MAX];
@@ -2554,6 +2556,8 @@
{
int i;
+ mutex_lock(&sde_dbg_base.mutex);
+
sde_evtlog_dump_all(sde_dbg_base.evtlog);
if (dump_all || !blk_arr || !len) {
@@ -2577,6 +2581,8 @@
if (do_panic && sde_dbg_base.panic_on_err)
panic(name);
+
+ mutex_unlock(&sde_dbg_base.mutex);
}
/**
@@ -2679,6 +2685,9 @@
*/
static int sde_dbg_debugfs_open(struct inode *inode, struct file *file)
{
+ if (!inode || !file)
+ return -EINVAL;
+
/* non-seekable */
file->f_mode &= ~(FMODE_LSEEK | FMODE_PREAD | FMODE_PWRITE);
file->private_data = inode->i_private;
@@ -2698,6 +2707,9 @@
ssize_t len = 0;
char evtlog_buf[SDE_EVTLOG_BUF_MAX];
+ if (!buff || !ppos)
+ return -EINVAL;
+
len = sde_evtlog_dump_to_buffer(sde_dbg_base.evtlog, evtlog_buf,
SDE_EVTLOG_BUF_MAX);
if (copy_to_user(buff, evtlog_buf, len))
@@ -2759,7 +2771,7 @@
*/
static int sde_evtlog_filter_open(struct inode *inode, struct file *file)
{
- if (!file)
+ if (!inode || !file)
return -EINVAL;
return single_open(file, sde_evtlog_filter_show, inode->i_private);
@@ -2778,6 +2790,9 @@
char *tmp_filter = NULL;
ssize_t rc = 0;
+ if (!user_buf)
+ return -EINVAL;
+
if (count > 0) {
/* copy user provided string and null terminate it */
tmp_filter = kzalloc(count + 1, GFP_KERNEL);
@@ -2813,13 +2828,23 @@
*/
static int sde_dbg_reg_base_release(struct inode *inode, struct file *file)
{
- struct sde_dbg_reg_base *dbg = file->private_data;
+ struct sde_dbg_reg_base *dbg;
+ if (!file)
+ return -EINVAL;
+
+ dbg = file->private_data;
+ if (!dbg)
+ return -ENODEV;
+
+ mutex_lock(&sde_dbg_base.mutex);
if (dbg && dbg->buf) {
kfree(dbg->buf);
dbg->buf_len = 0;
dbg->buf = NULL;
}
+ mutex_unlock(&sde_dbg_base.mutex);
+
return 0;
}
@@ -2834,11 +2859,15 @@
static ssize_t sde_dbg_reg_base_offset_write(struct file *file,
const char __user *user_buf, size_t count, loff_t *ppos)
{
- struct sde_dbg_reg_base *dbg = file->private_data;
+ struct sde_dbg_reg_base *dbg;
u32 off = 0;
u32 cnt = DEFAULT_BASE_REG_CNT;
char buf[24];
+ if (!file)
+ return -EINVAL;
+
+ dbg = file->private_data;
if (!dbg)
return -ENODEV;
@@ -2862,8 +2891,13 @@
if (cnt > (dbg->max_offset - off))
cnt = dbg->max_offset - off;
+ if (cnt == 0)
+ return -EINVAL;
+
+ mutex_lock(&sde_dbg_base.mutex);
dbg->off = off;
dbg->cnt = cnt;
+ mutex_unlock(&sde_dbg_base.mutex);
pr_debug("offset=%x cnt=%x\n", off, cnt);
@@ -2880,27 +2914,42 @@
static ssize_t sde_dbg_reg_base_offset_read(struct file *file,
char __user *buff, size_t count, loff_t *ppos)
{
- struct sde_dbg_reg_base *dbg = file->private_data;
+ struct sde_dbg_reg_base *dbg;
int len = 0;
char buf[24] = {'\0'};
+ if (!file)
+ return -EINVAL;
+
+ dbg = file->private_data;
if (!dbg)
return -ENODEV;
+ if (!ppos)
+ return -EINVAL;
+
if (*ppos)
return 0; /* the end */
- if (dbg->off % sizeof(u32))
+ mutex_lock(&sde_dbg_base.mutex);
+ if (dbg->off % sizeof(u32)) {
+ mutex_unlock(&sde_dbg_base.mutex);
return -EFAULT;
+ }
len = snprintf(buf, sizeof(buf), "0x%08zx %zx\n", dbg->off, dbg->cnt);
- if (len < 0 || len >= sizeof(buf))
+ if (len < 0 || len >= sizeof(buf)) {
+ mutex_unlock(&sde_dbg_base.mutex);
return 0;
+ }
- if ((count < sizeof(buf)) || copy_to_user(buff, buf, len))
+ if ((count < sizeof(buf)) || copy_to_user(buff, buf, len)) {
+ mutex_unlock(&sde_dbg_base.mutex);
return -EFAULT;
+ }
*ppos += len; /* increase offset */
+ mutex_unlock(&sde_dbg_base.mutex);
return len;
}
@@ -2915,11 +2964,15 @@
static ssize_t sde_dbg_reg_base_reg_write(struct file *file,
const char __user *user_buf, size_t count, loff_t *ppos)
{
- struct sde_dbg_reg_base *dbg = file->private_data;
+ struct sde_dbg_reg_base *dbg;
size_t off;
u32 data, cnt;
char buf[24];
+ if (!file)
+ return -EINVAL;
+
+ dbg = file->private_data;
if (!dbg)
return -ENODEV;
@@ -2939,8 +2992,11 @@
if (off % sizeof(u32))
return -EFAULT;
- if (off >= dbg->max_offset)
+ mutex_lock(&sde_dbg_base.mutex);
+ if (off >= dbg->max_offset) {
+ mutex_unlock(&sde_dbg_base.mutex);
return -EFAULT;
+ }
_sde_dbg_enable_power(true);
@@ -2948,6 +3004,8 @@
_sde_dbg_enable_power(false);
+ mutex_unlock(&sde_dbg_base.mutex);
+
pr_debug("addr=%zx data=%x\n", off, data);
return count;
@@ -2963,14 +3021,22 @@
static ssize_t sde_dbg_reg_base_reg_read(struct file *file,
char __user *user_buf, size_t count, loff_t *ppos)
{
- struct sde_dbg_reg_base *dbg = file->private_data;
+ struct sde_dbg_reg_base *dbg;
size_t len;
+ if (!file)
+ return -EINVAL;
+
+ dbg = file->private_data;
if (!dbg) {
pr_err("invalid handle\n");
return -ENODEV;
}
+ if (!ppos)
+ return -EINVAL;
+
+ mutex_lock(&sde_dbg_base.mutex);
if (!dbg->buf) {
char dump_buf[64];
char *ptr;
@@ -2980,11 +3046,15 @@
DIV_ROUND_UP(dbg->cnt, ROW_BYTES);
dbg->buf = kzalloc(dbg->buf_len, GFP_KERNEL);
- if (!dbg->buf)
+ if (!dbg->buf) {
+ mutex_unlock(&sde_dbg_base.mutex);
return -ENOMEM;
+ }
- if (dbg->off % sizeof(u32))
+ if (dbg->off % sizeof(u32)) {
+ mutex_unlock(&sde_dbg_base.mutex);
return -EFAULT;
+ }
ptr = dbg->base + dbg->off;
tot = 0;
@@ -3012,16 +3082,20 @@
dbg->buf_len = tot;
}
- if (*ppos >= dbg->buf_len)
+ if (*ppos >= dbg->buf_len) {
+ mutex_unlock(&sde_dbg_base.mutex);
return 0; /* done reading */
+ }
len = min(count, dbg->buf_len - (size_t) *ppos);
if (copy_to_user(user_buf, dbg->buf + *ppos, len)) {
+ mutex_unlock(&sde_dbg_base.mutex);
pr_err("failed to copy to user\n");
return -EFAULT;
}
*ppos += len; /* increase offset */
+ mutex_unlock(&sde_dbg_base.mutex);
return len;
}
@@ -3135,6 +3209,7 @@
return -EINVAL;
}
+ mutex_init(&sde_dbg_base.mutex);
INIT_LIST_HEAD(&sde_dbg_base.reg_base_list);
sde_dbg_base.dev = dev;
sde_dbg_base.power_ctrl = *power_ctrl;
@@ -3187,6 +3262,7 @@
sde_evtlog_destroy(sde_dbg_base.evtlog);
sde_dbg_base.evtlog = NULL;
sde_dbg_reg_base_destroy();
+ mutex_destroy(&sde_dbg_base.mutex);
}
int sde_dbg_reg_register_base(const char *name, void __iomem *base,
diff --git a/drivers/gpu/msm/a6xx_reg.h b/drivers/gpu/msm/a6xx_reg.h
index c2eb9ea..728e897 100644
--- a/drivers/gpu/msm/a6xx_reg.h
+++ b/drivers/gpu/msm/a6xx_reg.h
@@ -1004,6 +1004,18 @@
#define PDC_GPU_TCS1_CMD0_MSGID 0x21575
#define PDC_GPU_TCS1_CMD0_ADDR 0x21576
#define PDC_GPU_TCS1_CMD0_DATA 0x21577
+#define PDC_GPU_TCS2_CONTROL 0x215A4
+#define PDC_GPU_TCS2_CMD_ENABLE_BANK 0x215A5
+#define PDC_GPU_TCS2_CMD_WAIT_FOR_CMPL_BANK 0x215A6
+#define PDC_GPU_TCS2_CMD0_MSGID 0x215A7
+#define PDC_GPU_TCS2_CMD0_ADDR 0x215A8
+#define PDC_GPU_TCS2_CMD0_DATA 0x215A9
+#define PDC_GPU_TCS3_CONTROL 0x215D6
+#define PDC_GPU_TCS3_CMD_ENABLE_BANK 0x215D7
+#define PDC_GPU_TCS3_CMD_WAIT_FOR_CMPL_BANK 0x215D8
+#define PDC_GPU_TCS3_CMD0_MSGID 0x215D9
+#define PDC_GPU_TCS3_CMD0_ADDR 0x215DA
+#define PDC_GPU_TCS3_CMD0_DATA 0x215DB
#define PDC_GPU_SEQ_MEM_0 0xA0000
#endif /* _A6XX_REG_H */
diff --git a/drivers/gpu/msm/adreno-gpulist.h b/drivers/gpu/msm/adreno-gpulist.h
index b451750..a56a593 100644
--- a/drivers/gpu/msm/adreno-gpulist.h
+++ b/drivers/gpu/msm/adreno-gpulist.h
@@ -325,6 +325,26 @@
.core = 6,
.major = 3,
.minor = 0,
+ .patchid = 0,
+ .features = ADRENO_64BIT | ADRENO_RPMH |
+ ADRENO_GPMU | ADRENO_CONTENT_PROTECTION | ADRENO_LM,
+ .sqefw_name = "a630_sqe.fw",
+ .zap_name = "a630_zap",
+ .gpudev = &adreno_a6xx_gpudev,
+ .gmem_size = SZ_1M,
+ .num_protected_regs = 0x20,
+ .busy_mask = 0xFFFFFFFE,
+ .gpmufw_name = "a630_gmu.bin",
+ .gpmu_major = 0x0,
+ .gpmu_minor = 0x005,
+ .gpmu_tsens = 0x000C000D,
+ .max_power = 5448,
+ },
+ {
+ .gpurev = ADRENO_REV_A630,
+ .core = 6,
+ .major = 3,
+ .minor = 0,
.patchid = ANY_ID,
.features = ADRENO_64BIT | ADRENO_RPMH |
ADRENO_GPMU | ADRENO_CONTENT_PROTECTION | ADRENO_LM,
diff --git a/drivers/gpu/msm/adreno.c b/drivers/gpu/msm/adreno.c
index 844142a..ae5a78d 100644
--- a/drivers/gpu/msm/adreno.c
+++ b/drivers/gpu/msm/adreno.c
@@ -112,6 +112,11 @@
.active_list_lock = __SPIN_LOCK_UNLOCKED(device_3d0.active_list_lock),
.gpu_llc_slice_enable = true,
.gpuhtw_llc_slice_enable = true,
+ .preempt = {
+ .preempt_level = 1,
+ .skipsaverestore = 1,
+ .usesgmem = 1,
+ },
};
/* Ptr to array for the current set of fault detect registers */
@@ -852,13 +857,13 @@
struct device_node *parent)
{
struct device_node *node, *child;
+ unsigned int bin = 0;
node = of_find_node_by_name(parent, "qcom,gpu-pwrlevel-bins");
if (node == NULL)
return adreno_of_get_legacy_pwrlevels(adreno_dev, parent);
for_each_child_of_node(node, child) {
- unsigned int bin;
if (of_property_read_u32(child, "qcom,speed-bin", &bin))
continue;
@@ -874,6 +879,8 @@
}
}
+ KGSL_CORE_ERR("GPU speed_bin:%d mismatch for efused bin:%d\n",
+ adreno_dev->speed_bin, bin);
return -ENODEV;
}
@@ -898,6 +905,7 @@
{ ADRENO_QUIRK_DISABLE_LMLOADKILL,
"qcom,gpu-quirk-lmloadkill-disable" },
{ ADRENO_QUIRK_HFI_USE_REG, "qcom,gpu-quirk-hfi-use-reg" },
+ { ADRENO_QUIRK_SECVID_SET_ONCE, "qcom,gpu-quirk-secvid-set-once" },
};
static int adreno_of_get_power(struct adreno_device *adreno_dev,
@@ -1266,6 +1274,10 @@
struct adreno_gpudev *gpudev = ADRENO_GPU_DEVICE(adreno_dev);
int ret;
+ if (!adreno_is_a3xx(adreno_dev))
+ kgsl_sharedmem_set(device, &device->scratch, 0, 0,
+ device->scratch.size);
+
ret = kgsl_pwrctrl_change_state(device, KGSL_STATE_INIT);
if (ret)
return ret;
@@ -1389,9 +1401,10 @@
static void _set_secvid(struct kgsl_device *device)
{
struct adreno_device *adreno_dev = ADRENO_DEVICE(device);
+ static bool set;
/* Program GPU contect protection init values */
- if (device->mmu.secured) {
+ if (device->mmu.secured && !set) {
if (adreno_is_a4xx(adreno_dev))
adreno_writereg(adreno_dev,
ADRENO_REG_RBBM_SECVID_TRUST_CONFIG, 0x2);
@@ -1405,6 +1418,8 @@
adreno_writereg(adreno_dev,
ADRENO_REG_RBBM_SECVID_TSB_TRUSTED_SIZE,
KGSL_IOMMU_SECURE_SIZE);
+ if (ADRENO_QUIRK(adreno_dev, ADRENO_QUIRK_SECVID_SET_ONCE))
+ set = true;
}
}
@@ -1455,6 +1470,22 @@
return ret;
}
+static void adreno_set_active_ctxs_null(struct adreno_device *adreno_dev)
+{
+ int i;
+ struct adreno_ringbuffer *rb;
+
+ FOR_EACH_RINGBUFFER(adreno_dev, rb, i) {
+ if (rb->drawctxt_active)
+ kgsl_context_put(&(rb->drawctxt_active->base));
+ rb->drawctxt_active = NULL;
+
+ kgsl_sharedmem_writel(KGSL_DEVICE(adreno_dev),
+ &rb->pagetable_desc, PT_INFO_OFFSET(current_rb_ptname),
+ 0);
+ }
+}
+
/**
* _adreno_start - Power up the GPU and prepare to accept commands
* @adreno_dev: Pointer to an adreno_device structure
@@ -1492,6 +1523,9 @@
if (status)
goto error_pwr_off;
+ /* Set any stale active contexts to NULL */
+ adreno_set_active_ctxs_null(adreno_dev);
+
/* Set the bit to indicate that we've just powered on */
set_bit(ADRENO_DEVICE_PWRON, &adreno_dev->priv);
@@ -1676,22 +1710,6 @@
return ret;
}
-static void adreno_set_active_ctxs_null(struct adreno_device *adreno_dev)
-{
- int i;
- struct adreno_ringbuffer *rb;
-
- FOR_EACH_RINGBUFFER(adreno_dev, rb, i) {
- if (rb->drawctxt_active)
- kgsl_context_put(&(rb->drawctxt_active->base));
- rb->drawctxt_active = NULL;
-
- kgsl_sharedmem_writel(KGSL_DEVICE(adreno_dev),
- &rb->pagetable_desc, PT_INFO_OFFSET(current_rb_ptname),
- 0);
- }
-}
-
static int adreno_stop(struct kgsl_device *device)
{
struct adreno_device *adreno_dev = ADRENO_DEVICE(device);
@@ -1712,8 +1730,6 @@
}
}
- adreno_set_active_ctxs_null(adreno_dev);
-
adreno_dispatcher_stop(adreno_dev);
adreno_ringbuffer_stop(adreno_dev);
@@ -1752,6 +1768,12 @@
kgsl_mmu_stop(&device->mmu);
+ /*
+ * At this point, MMU is turned off so we can safely
+ * destroy any pending contexts and their pagetables
+ */
+ adreno_set_active_ctxs_null(adreno_dev);
+
clear_bit(ADRENO_DEVICE_STARTED, &adreno_dev->priv);
return 0;
@@ -2377,9 +2399,9 @@
int ret;
if (gpudev->oob_set) {
- ret = gpudev->oob_set(adreno_dev, OOB_CPINIT_SET_MASK,
- OOB_CPINIT_CHECK_MASK,
- OOB_CPINIT_CLEAR_MASK);
+ ret = gpudev->oob_set(adreno_dev, OOB_GPU_SET_MASK,
+ OOB_GPU_CHECK_MASK,
+ OOB_GPU_CLEAR_MASK);
if (ret)
return ret;
}
@@ -2403,7 +2425,7 @@
ret = _soft_reset(adreno_dev);
if (ret) {
if (gpudev->oob_clear)
- gpudev->oob_clear(adreno_dev, OOB_CPINIT_CLEAR_MASK);
+ gpudev->oob_clear(adreno_dev, OOB_GPU_CLEAR_MASK);
return ret;
}
@@ -2453,7 +2475,7 @@
adreno_perfcounter_restore(adreno_dev);
if (gpudev->oob_clear)
- gpudev->oob_clear(adreno_dev, OOB_CPINIT_CLEAR_MASK);
+ gpudev->oob_clear(adreno_dev, OOB_GPU_CLEAR_MASK);
return ret;
}
diff --git a/drivers/gpu/msm/adreno.h b/drivers/gpu/msm/adreno.h
index 3118375..d6cba9d 100644
--- a/drivers/gpu/msm/adreno.h
+++ b/drivers/gpu/msm/adreno.h
@@ -140,6 +140,8 @@
#define ADRENO_QUIRK_DISABLE_LMLOADKILL BIT(5)
/* Allow HFI to use registers to send message to GMU */
#define ADRENO_QUIRK_HFI_USE_REG BIT(6)
+/* Only set protected SECVID registers once */
+#define ADRENO_QUIRK_SECVID_SET_ONCE BIT(7)
/* Flags to control command packet settings */
#define KGSL_CMD_FLAGS_NONE 0
@@ -208,6 +210,7 @@
#define ADRENO_IOMMU_PAGE_FAULT BIT(3)
#define ADRENO_PREEMPT_FAULT BIT(4)
#define ADRENO_GMU_FAULT BIT(5)
+#define ADRENO_CTX_DETATCH_TIMEOUT_FAULT BIT(6)
#define ADRENO_SPTP_PC_CTRL 0
#define ADRENO_PPD_CTRL 1
@@ -258,6 +261,9 @@
* @work: A work struct for the preemption worker (for 5XX)
* @token_submit: Indicates if a preempt token has been submitted in
* current ringbuffer (for 4XX)
+ * preempt_level: The level of preemption (for 6XX)
+ * skipsaverestore: To skip saverestore during L1 preemption (for 6XX)
+ * usesgmem: enable GMEM save/restore across preemption (for 6XX)
*/
struct adreno_preemption {
atomic_t state;
@@ -265,6 +271,9 @@
struct timer_list timer;
struct work_struct work;
bool token_submit;
+ unsigned int preempt_level;
+ bool skipsaverestore;
+ bool usesgmem;
};
@@ -483,10 +492,6 @@
void *gpuhtw_llc_slice;
bool gpuhtw_llc_slice_enable;
unsigned int zap_loaded;
- unsigned int preempt_level;
- bool usesgmem;
- bool skipsaverestore;
-
};
/**
diff --git a/drivers/gpu/msm/adreno_a5xx.c b/drivers/gpu/msm/adreno_a5xx.c
index 434fef8..f3e8650 100644
--- a/drivers/gpu/msm/adreno_a5xx.c
+++ b/drivers/gpu/msm/adreno_a5xx.c
@@ -1277,8 +1277,8 @@
{
struct kgsl_device *device = KGSL_DEVICE(adreno_dev);
const struct firmware *fw;
- uint32_t block_size = 0, block_total = 0, fw_size;
- uint32_t *block;
+ uint64_t block_size = 0, block_total = 0;
+ uint32_t fw_size, *block;
int ret = -EINVAL;
if (!adreno_dev->gpucore->regfw_name)
@@ -1300,7 +1300,8 @@
/* All offset numbers calculated from file description */
while (block_total < fw_size) {
block_size = block[0];
- if (block_size >= fw_size || block_size < 2)
+ if (((block_total + block_size) >= fw_size)
+ || block_size < 5)
goto err;
if (block[1] != GPMU_SEQUENCE_ID)
goto err;
@@ -1316,6 +1317,9 @@
goto err;
adreno_dev->lm_fw = fw;
+
+ if (block[2] > (block_size - 2))
+ goto err;
adreno_dev->lm_sequence = block + block[2] + 3;
adreno_dev->lm_size = block_size - block[2] - 2;
}
@@ -1328,7 +1332,7 @@
err:
release_firmware(fw);
KGSL_PWR_ERR(device,
- "Register file failed to load sz=%d bsz=%d header=%d\n",
+ "Register file failed to load sz=%d bsz=%llu header=%d\n",
fw_size, block_size, ret);
}
diff --git a/drivers/gpu/msm/adreno_a6xx.c b/drivers/gpu/msm/adreno_a6xx.c
index de4677a..8cbc75e 100644
--- a/drivers/gpu/msm/adreno_a6xx.c
+++ b/drivers/gpu/msm/adreno_a6xx.c
@@ -65,10 +65,10 @@
unsigned int val;
};
static const struct kgsl_hwcg_reg a630_hwcg_regs[] = {
- {A6XX_RBBM_CLOCK_CNTL_SP0, 0x22222222},
- {A6XX_RBBM_CLOCK_CNTL_SP1, 0x22222222},
- {A6XX_RBBM_CLOCK_CNTL_SP2, 0x22222222},
- {A6XX_RBBM_CLOCK_CNTL_SP3, 0x22222222},
+ {A6XX_RBBM_CLOCK_CNTL_SP0, 0x02222222},
+ {A6XX_RBBM_CLOCK_CNTL_SP1, 0x02222222},
+ {A6XX_RBBM_CLOCK_CNTL_SP2, 0x02222222},
+ {A6XX_RBBM_CLOCK_CNTL_SP3, 0x02222222},
{A6XX_RBBM_CLOCK_CNTL2_SP0, 0x02022220},
{A6XX_RBBM_CLOCK_CNTL2_SP1, 0x02022220},
{A6XX_RBBM_CLOCK_CNTL2_SP2, 0x02022220},
@@ -143,6 +143,72 @@
{A6XX_RBBM_CLOCK_CNTL2_RB1, 0x00002222},
{A6XX_RBBM_CLOCK_CNTL2_RB2, 0x00002222},
{A6XX_RBBM_CLOCK_CNTL2_RB3, 0x00002222},
+ {A6XX_RBBM_CLOCK_CNTL_CCU0, 0x00000000},
+ {A6XX_RBBM_CLOCK_CNTL_CCU1, 0x00000000},
+ {A6XX_RBBM_CLOCK_CNTL_CCU2, 0x00000000},
+ {A6XX_RBBM_CLOCK_CNTL_CCU3, 0x00000000},
+ {A6XX_RBBM_CLOCK_HYST_RB_CCU0, 0x00040F00},
+ {A6XX_RBBM_CLOCK_HYST_RB_CCU1, 0x00040F00},
+ {A6XX_RBBM_CLOCK_HYST_RB_CCU2, 0x00040F00},
+ {A6XX_RBBM_CLOCK_HYST_RB_CCU3, 0x00040F00},
+ {A6XX_RBBM_CLOCK_CNTL_RAC, 0x00022022},
+ {A6XX_RBBM_CLOCK_CNTL2_RAC, 0x00005550},
+ {A6XX_RBBM_CLOCK_DELAY_RAC, 0x00010011},
+ {A6XX_RBBM_CLOCK_HYST_RAC, 0x00445044},
+ {A6XX_RBBM_CLOCK_CNTL_TSE_RAS_RBBM, 0x04222220},
+ {A6XX_RBBM_CLOCK_MODE_GPC, 0x00202222},
+ {A6XX_RBBM_CLOCK_MODE_VFD, 0x00002222},
+ {A6XX_RBBM_CLOCK_HYST_TSE_RAS_RBBM, 0x00000000},
+ {A6XX_RBBM_CLOCK_HYST_GPC, 0x04104004},
+ {A6XX_RBBM_CLOCK_HYST_VFD, 0x00000000},
+ {A6XX_RBBM_CLOCK_DELAY_HLSQ, 0x00000000},
+ {A6XX_RBBM_CLOCK_DELAY_TSE_RAS_RBBM, 0x00004000},
+ {A6XX_RBBM_CLOCK_DELAY_GPC, 0x00000200},
+ {A6XX_RBBM_CLOCK_DELAY_VFD, 0x00002222},
+ {A6XX_RBBM_CLOCK_DELAY_HLSQ_2, 0x00000002},
+ {A6XX_RBBM_CLOCK_MODE_HLSQ, 0x00002222},
+ {A6XX_RBBM_CLOCK_CNTL_GMU_GX, 0x00000222},
+ {A6XX_RBBM_CLOCK_DELAY_GMU_GX, 0x00000111},
+ {A6XX_RBBM_CLOCK_HYST_GMU_GX, 0x00000555}
+};
+
+static const struct kgsl_hwcg_reg a615_hwcg_regs[] = {
+ {A6XX_RBBM_CLOCK_CNTL_SP0, 0x22222222},
+ {A6XX_RBBM_CLOCK_CNTL2_SP0, 0x02222220},
+ {A6XX_RBBM_CLOCK_DELAY_SP0, 0x00000081},
+ {A6XX_RBBM_CLOCK_HYST_SP0, 0x0000F3CF},
+ {A6XX_RBBM_CLOCK_CNTL_TP0, 0x22222222},
+ {A6XX_RBBM_CLOCK_CNTL_TP1, 0x22222222},
+ {A6XX_RBBM_CLOCK_CNTL2_TP0, 0x22222222},
+ {A6XX_RBBM_CLOCK_CNTL2_TP1, 0x22222222},
+ {A6XX_RBBM_CLOCK_CNTL3_TP0, 0x22222222},
+ {A6XX_RBBM_CLOCK_CNTL3_TP1, 0x22222222},
+ {A6XX_RBBM_CLOCK_CNTL4_TP0, 0x00022222},
+ {A6XX_RBBM_CLOCK_CNTL4_TP1, 0x00022222},
+ {A6XX_RBBM_CLOCK_HYST_TP0, 0x77777777},
+ {A6XX_RBBM_CLOCK_HYST_TP1, 0x77777777},
+ {A6XX_RBBM_CLOCK_HYST2_TP0, 0x77777777},
+ {A6XX_RBBM_CLOCK_HYST2_TP1, 0x77777777},
+ {A6XX_RBBM_CLOCK_HYST3_TP0, 0x77777777},
+ {A6XX_RBBM_CLOCK_HYST3_TP1, 0x77777777},
+ {A6XX_RBBM_CLOCK_HYST4_TP0, 0x00077777},
+ {A6XX_RBBM_CLOCK_HYST4_TP1, 0x00077777},
+ {A6XX_RBBM_CLOCK_DELAY_TP0, 0x11111111},
+ {A6XX_RBBM_CLOCK_DELAY_TP1, 0x11111111},
+ {A6XX_RBBM_CLOCK_DELAY2_TP0, 0x11111111},
+ {A6XX_RBBM_CLOCK_DELAY2_TP1, 0x11111111},
+ {A6XX_RBBM_CLOCK_DELAY3_TP0, 0x11111111},
+ {A6XX_RBBM_CLOCK_DELAY3_TP1, 0x11111111},
+ {A6XX_RBBM_CLOCK_DELAY4_TP0, 0x00011111},
+ {A6XX_RBBM_CLOCK_DELAY4_TP1, 0x00011111},
+ {A6XX_RBBM_CLOCK_CNTL_UCHE, 0x22222222},
+ {A6XX_RBBM_CLOCK_CNTL2_UCHE, 0x22222222},
+ {A6XX_RBBM_CLOCK_CNTL3_UCHE, 0x22222222},
+ {A6XX_RBBM_CLOCK_CNTL4_UCHE, 0x00222222},
+ {A6XX_RBBM_CLOCK_HYST_UCHE, 0x00000004},
+ {A6XX_RBBM_CLOCK_DELAY_UCHE, 0x00000002},
+ {A6XX_RBBM_CLOCK_CNTL_RB0, 0x22222222},
+ {A6XX_RBBM_CLOCK_CNTL2_RB0, 0x00002222},
{A6XX_RBBM_CLOCK_CNTL_CCU0, 0x00002220},
{A6XX_RBBM_CLOCK_CNTL_CCU1, 0x00002220},
{A6XX_RBBM_CLOCK_CNTL_CCU2, 0x00002220},
@@ -153,7 +219,7 @@
{A6XX_RBBM_CLOCK_HYST_RB_CCU3, 0x00040F00},
{A6XX_RBBM_CLOCK_CNTL_RAC, 0x05022022},
{A6XX_RBBM_CLOCK_CNTL2_RAC, 0x00005555},
- {A6XX_RBBM_CLOCK_DELAY_RAC, 0x00010011},
+ {A6XX_RBBM_CLOCK_DELAY_RAC, 0x00000011},
{A6XX_RBBM_CLOCK_HYST_RAC, 0x00445044},
{A6XX_RBBM_CLOCK_CNTL_TSE_RAS_RBBM, 0x04222222},
{A6XX_RBBM_CLOCK_MODE_GPC, 0x02222222},
@@ -178,7 +244,7 @@
unsigned int count;
} a6xx_hwcg_registers[] = {
{adreno_is_a630, a630_hwcg_regs, ARRAY_SIZE(a630_hwcg_regs)},
- {adreno_is_a615, a630_hwcg_regs, ARRAY_SIZE(a630_hwcg_regs)},
+ {adreno_is_a615, a615_hwcg_regs, ARRAY_SIZE(a615_hwcg_regs)},
};
static struct a6xx_protected_regs {
@@ -937,42 +1003,42 @@
_regwrite(gmu->pdc_reg_virt, PDC_GPU_SEQ_MEM_0 + 4, 0x002081FC);
/* Set TCS commands used by PDC sequence for low power modes */
- _regwrite(gmu->pdc_reg_virt, PDC_GPU_TCS0_CMD_ENABLE_BANK, 7);
- _regwrite(gmu->pdc_reg_virt, PDC_GPU_TCS0_CMD_WAIT_FOR_CMPL_BANK, 0);
- _regwrite(gmu->pdc_reg_virt, PDC_GPU_TCS0_CONTROL, 0);
- _regwrite(gmu->pdc_reg_virt, PDC_GPU_TCS0_CMD0_MSGID, 0x10108);
- _regwrite(gmu->pdc_reg_virt, PDC_GPU_TCS0_CMD0_ADDR, 0x30010);
- _regwrite(gmu->pdc_reg_virt, PDC_GPU_TCS0_CMD0_DATA, 1);
- _regwrite(gmu->pdc_reg_virt,
- PDC_GPU_TCS0_CMD0_MSGID + PDC_CMD_OFFSET, 0x10108);
- _regwrite(gmu->pdc_reg_virt,
- PDC_GPU_TCS0_CMD0_ADDR + PDC_CMD_OFFSET, 0x30000);
- _regwrite(gmu->pdc_reg_virt,
- PDC_GPU_TCS0_CMD0_DATA + PDC_CMD_OFFSET, 0x0);
- _regwrite(gmu->pdc_reg_virt,
- PDC_GPU_TCS0_CMD0_MSGID + PDC_CMD_OFFSET * 2, 0x10108);
- _regwrite(gmu->pdc_reg_virt,
- PDC_GPU_TCS0_CMD0_ADDR + PDC_CMD_OFFSET * 2, 0x30080);
- _regwrite(gmu->pdc_reg_virt,
- PDC_GPU_TCS0_CMD0_DATA + PDC_CMD_OFFSET * 2, 0x0);
_regwrite(gmu->pdc_reg_virt, PDC_GPU_TCS1_CMD_ENABLE_BANK, 7);
_regwrite(gmu->pdc_reg_virt, PDC_GPU_TCS1_CMD_WAIT_FOR_CMPL_BANK, 0);
_regwrite(gmu->pdc_reg_virt, PDC_GPU_TCS1_CONTROL, 0);
_regwrite(gmu->pdc_reg_virt, PDC_GPU_TCS1_CMD0_MSGID, 0x10108);
_regwrite(gmu->pdc_reg_virt, PDC_GPU_TCS1_CMD0_ADDR, 0x30010);
- _regwrite(gmu->pdc_reg_virt, PDC_GPU_TCS1_CMD0_DATA, 2);
+ _regwrite(gmu->pdc_reg_virt, PDC_GPU_TCS1_CMD0_DATA, 1);
_regwrite(gmu->pdc_reg_virt,
PDC_GPU_TCS1_CMD0_MSGID + PDC_CMD_OFFSET, 0x10108);
_regwrite(gmu->pdc_reg_virt,
PDC_GPU_TCS1_CMD0_ADDR + PDC_CMD_OFFSET, 0x30000);
_regwrite(gmu->pdc_reg_virt,
- PDC_GPU_TCS1_CMD0_DATA + PDC_CMD_OFFSET, 0x3);
+ PDC_GPU_TCS1_CMD0_DATA + PDC_CMD_OFFSET, 0x0);
_regwrite(gmu->pdc_reg_virt,
PDC_GPU_TCS1_CMD0_MSGID + PDC_CMD_OFFSET * 2, 0x10108);
_regwrite(gmu->pdc_reg_virt,
PDC_GPU_TCS1_CMD0_ADDR + PDC_CMD_OFFSET * 2, 0x30080);
_regwrite(gmu->pdc_reg_virt,
- PDC_GPU_TCS1_CMD0_DATA + PDC_CMD_OFFSET * 2, 0x3);
+ PDC_GPU_TCS1_CMD0_DATA + PDC_CMD_OFFSET * 2, 0x0);
+ _regwrite(gmu->pdc_reg_virt, PDC_GPU_TCS3_CMD_ENABLE_BANK, 7);
+ _regwrite(gmu->pdc_reg_virt, PDC_GPU_TCS3_CMD_WAIT_FOR_CMPL_BANK, 0);
+ _regwrite(gmu->pdc_reg_virt, PDC_GPU_TCS3_CONTROL, 0);
+ _regwrite(gmu->pdc_reg_virt, PDC_GPU_TCS3_CMD0_MSGID, 0x10108);
+ _regwrite(gmu->pdc_reg_virt, PDC_GPU_TCS3_CMD0_ADDR, 0x30010);
+ _regwrite(gmu->pdc_reg_virt, PDC_GPU_TCS3_CMD0_DATA, 2);
+ _regwrite(gmu->pdc_reg_virt,
+ PDC_GPU_TCS3_CMD0_MSGID + PDC_CMD_OFFSET, 0x10108);
+ _regwrite(gmu->pdc_reg_virt,
+ PDC_GPU_TCS3_CMD0_ADDR + PDC_CMD_OFFSET, 0x30000);
+ _regwrite(gmu->pdc_reg_virt,
+ PDC_GPU_TCS3_CMD0_DATA + PDC_CMD_OFFSET, 0x3);
+ _regwrite(gmu->pdc_reg_virt,
+ PDC_GPU_TCS3_CMD0_MSGID + PDC_CMD_OFFSET * 2, 0x10108);
+ _regwrite(gmu->pdc_reg_virt,
+ PDC_GPU_TCS3_CMD0_ADDR + PDC_CMD_OFFSET * 2, 0x30080);
+ _regwrite(gmu->pdc_reg_virt,
+ PDC_GPU_TCS3_CMD0_DATA + PDC_CMD_OFFSET * 2, 0x3);
/* Setup GPU PDC */
_regwrite(gmu->pdc_reg_virt, PDC_GPU_SEQ_START_ADDR, 0);
@@ -1014,7 +1080,6 @@
return 0;
/* Wait 100us to reduce unnecessary AHB bus traffic */
udelay(100);
- cond_resched();
} while (!time_after(jiffies, t));
/* Double check one last time */
@@ -1028,11 +1093,11 @@
/*
* The lowest 16 bits of this value are the number of XO clock cycles
* for main hysteresis. This is the first hysteresis. Here we set it
- * to 0x5DC cycles, or 78.1 us. The highest 16 bits of this value are
+ * to 0x1680 cycles, or 300 us. The highest 16 bits of this value are
* the number of XO clock cycles for short hysteresis. This happens
* after main hysteresis. Here we set it to 0xA cycles, or 0.5 us.
*/
-#define GMU_PWR_COL_HYST 0x000A05DC
+#define GMU_PWR_COL_HYST 0x000A1680
/*
* a6xx_gmu_power_config() - Configure and enable GMU's low power mode
@@ -1780,7 +1845,6 @@
/* Wait 100us to reduce unnecessary AHB bus traffic */
udelay(100);
- cond_resched();
}
/* Check one last time */
diff --git a/drivers/gpu/msm/adreno_a6xx_preempt.c b/drivers/gpu/msm/adreno_a6xx_preempt.c
index ca011e4..1eec381 100644
--- a/drivers/gpu/msm/adreno_a6xx_preempt.c
+++ b/drivers/gpu/msm/adreno_a6xx_preempt.c
@@ -208,10 +208,11 @@
unsigned int contextidr;
unsigned long flags;
uint32_t preempt_level, usesgmem, skipsaverestore;
+ struct adreno_preemption *preempt = &adreno_dev->preempt;
- preempt_level = adreno_dev->preempt_level;
- usesgmem = adreno_dev->usesgmem;
- skipsaverestore = adreno_dev->skipsaverestore;
+ preempt_level = preempt->preempt_level;
+ usesgmem = preempt->usesgmem;
+ skipsaverestore = preempt->skipsaverestore;
/* Put ourselves into a possible trigger state */
if (!adreno_move_preempt_state(adreno_dev,
diff --git a/drivers/gpu/msm/adreno_a6xx_snapshot.c b/drivers/gpu/msm/adreno_a6xx_snapshot.c
index e865f20..4357518 100644
--- a/drivers/gpu/msm/adreno_a6xx_snapshot.c
+++ b/drivers/gpu/msm/adreno_a6xx_snapshot.c
@@ -1481,6 +1481,7 @@
{
struct kgsl_device *device = KGSL_DEVICE(adreno_dev);
struct adreno_gpudev *gpudev = ADRENO_GPU_DEVICE(adreno_dev);
+ unsigned int val;
if (!kgsl_gmu_isenabled(device))
return;
@@ -1488,10 +1489,16 @@
adreno_snapshot_registers(device, snapshot, a6xx_gmu_registers,
ARRAY_SIZE(a6xx_gmu_registers) / 2);
- if (gpudev->gx_is_on(adreno_dev))
+ if (gpudev->gx_is_on(adreno_dev)) {
+ /* Set fence to ALLOW mode so registers can be read */
+ kgsl_regwrite(device, A6XX_GMU_AO_AHB_FENCE_CTRL, 0);
+ kgsl_regread(device, A6XX_GMU_AO_AHB_FENCE_CTRL, &val);
+
+ KGSL_DRV_ERR(device, "set FENCE to ALLOW mode:%x\n", val);
adreno_snapshot_registers(device, snapshot,
a6xx_gmu_gx_registers,
ARRAY_SIZE(a6xx_gmu_gx_registers) / 2);
+ }
}
/* a6xx_snapshot_sqe() - Dump SQE data in snapshot */
@@ -1579,9 +1586,6 @@
bool sptprac_on;
unsigned int i;
- /* Make sure the fence is in ALLOW mode so registers can be read */
- kgsl_regwrite(device, A6XX_GMU_AO_AHB_FENCE_CTRL, 0);
-
/* GMU TCM data dumped through AHB */
a6xx_snapshot_gmu(adreno_dev, snapshot);
diff --git a/drivers/gpu/msm/adreno_dispatch.c b/drivers/gpu/msm/adreno_dispatch.c
index cb42a70..902dc0a 100644
--- a/drivers/gpu/msm/adreno_dispatch.c
+++ b/drivers/gpu/msm/adreno_dispatch.c
@@ -1836,7 +1836,8 @@
* because we won't see this cmdobj again
*/
- if (fault & ADRENO_TIMEOUT_FAULT)
+ if ((fault & ADRENO_TIMEOUT_FAULT) ||
+ (fault & ADRENO_CTX_DETATCH_TIMEOUT_FAULT))
bitmap_zero(&cmdobj->fault_policy, BITS_PER_LONG);
/*
diff --git a/drivers/gpu/msm/adreno_drawctxt.c b/drivers/gpu/msm/adreno_drawctxt.c
index 2615d44..d984c6d 100644
--- a/drivers/gpu/msm/adreno_drawctxt.c
+++ b/drivers/gpu/msm/adreno_drawctxt.c
@@ -304,6 +304,7 @@
/* Give the bad news to everybody waiting around */
wake_up_all(&drawctxt->waiting);
wake_up_all(&drawctxt->wq);
+ wake_up_all(&drawctxt->timeout);
}
/*
@@ -398,6 +399,7 @@
spin_lock_init(&drawctxt->lock);
init_waitqueue_head(&drawctxt->wq);
init_waitqueue_head(&drawctxt->waiting);
+ init_waitqueue_head(&drawctxt->timeout);
/* Set the context priority */
_set_context_priority(drawctxt);
@@ -520,21 +522,32 @@
drawctxt->internal_timestamp, 30 * 1000);
/*
- * If the wait for global fails due to timeout then nothing after this
- * point is likely to work very well - Get GPU snapshot and BUG_ON()
- * so we can take advantage of the debug tools to figure out what the
- * h - e - double hockey sticks happened. If EAGAIN error is returned
+ * If the wait for global fails due to timeout then mark it as
+ * context detach timeout fault and schedule dispatcher to kick
+ * in GPU recovery. For a ADRENO_CTX_DETATCH_TIMEOUT_FAULT we clear
+ * the policy and invalidate the context. If EAGAIN error is returned
* then recovery will kick in and there will be no more commands in the
- * RB pipe from this context which is waht we are waiting for, so ignore
- * -EAGAIN error
+ * RB pipe from this context which is what we are waiting for, so ignore
+ * -EAGAIN error.
*/
if (ret && ret != -EAGAIN) {
- KGSL_DRV_ERR(device, "Wait for global ts=%d type=%d error=%d\n",
- drawctxt->internal_timestamp,
+ KGSL_DRV_ERR(device,
+ "Wait for global ctx=%d ts=%d type=%d error=%d\n",
+ drawctxt->base.id, drawctxt->internal_timestamp,
drawctxt->type, ret);
- device->force_panic = 1;
- kgsl_device_snapshot(device, context,
- adreno_gmu_gpu_fault(adreno_dev));
+
+ adreno_set_gpu_fault(adreno_dev,
+ ADRENO_CTX_DETATCH_TIMEOUT_FAULT);
+ mutex_unlock(&device->mutex);
+
+ /* Schedule dispatcher to kick in recovery */
+ adreno_dispatcher_schedule(device);
+
+ /* Wait for context to be invalidated and release context */
+ wait_event_interruptible_timeout(drawctxt->timeout,
+ kgsl_context_invalid(&drawctxt->base),
+ msecs_to_jiffies(5000));
+ return;
}
kgsl_sharedmem_writel(device, &device->memstore,
diff --git a/drivers/gpu/msm/adreno_drawctxt.h b/drivers/gpu/msm/adreno_drawctxt.h
index aeacf09..eef506f 100644
--- a/drivers/gpu/msm/adreno_drawctxt.h
+++ b/drivers/gpu/msm/adreno_drawctxt.h
@@ -40,6 +40,7 @@
* @pending: Priority list node for the dispatcher list of pending contexts
* @wq: Workqueue structure for contexts to sleep pending room in the queue
* @waiting: Workqueue structure for contexts waiting for a timestamp or event
+ * @timeout: Workqueue structure for contexts waiting to invalidate
* @queued: Number of commands queued in the drawqueue
* @fault_policy: GFT fault policy set in _skip_cmd();
* @debug_root: debugfs entry for this context.
@@ -68,6 +69,7 @@
struct plist_node pending;
wait_queue_head_t wq;
wait_queue_head_t waiting;
+ wait_queue_head_t timeout;
int queued;
unsigned int fault_policy;
diff --git a/drivers/gpu/msm/adreno_ringbuffer.c b/drivers/gpu/msm/adreno_ringbuffer.c
index d248479..fb545e7 100644
--- a/drivers/gpu/msm/adreno_ringbuffer.c
+++ b/drivers/gpu/msm/adreno_ringbuffer.c
@@ -86,7 +86,7 @@
* This delay must be less than the IFPC main hysteresis or
* the GMU will start shutting down before we try again.
*/
-#define GMU_WAKEUP_DELAY 20
+#define GMU_WAKEUP_DELAY 10
/* Max amount of tries to wake up the GMU. */
#define GMU_WAKEUP_RETRY_MAX 60
@@ -225,8 +225,9 @@
FOR_EACH_RINGBUFFER(adreno_dev, rb, i) {
kgsl_sharedmem_set(device, &(rb->buffer_desc),
0, 0xAA, KGSL_RB_SIZE);
- kgsl_sharedmem_writel(device, &device->scratch,
- SCRATCH_RPTR_OFFSET(rb->id), 0);
+ if (!adreno_is_a3xx(adreno_dev))
+ kgsl_sharedmem_writel(device, &device->scratch,
+ SCRATCH_RPTR_OFFSET(rb->id), 0);
rb->wptr = 0;
rb->_wptr = 0;
rb->wptr_preempt_end = 0xFFFFFFFF;
@@ -287,9 +288,16 @@
int adreno_ringbuffer_probe(struct adreno_device *adreno_dev, bool nopreempt)
{
- int status = 0;
+ struct kgsl_device *device = KGSL_DEVICE(adreno_dev);
struct adreno_gpudev *gpudev = ADRENO_GPU_DEVICE(adreno_dev);
- int i;
+ int i, status;
+
+ if (!adreno_is_a3xx(adreno_dev)) {
+ status = kgsl_allocate_global(device, &device->scratch,
+ PAGE_SIZE, 0, 0, "scratch");
+ if (status != 0)
+ return status;
+ }
if (nopreempt == false)
adreno_dev->num_ringbuffers = gpudev->num_prio_levels;
@@ -325,9 +333,13 @@
void adreno_ringbuffer_close(struct adreno_device *adreno_dev)
{
+ struct kgsl_device *device = KGSL_DEVICE(adreno_dev);
struct adreno_ringbuffer *rb;
int i;
+ if (!adreno_is_a3xx(adreno_dev))
+ kgsl_free_global(device, &device->scratch);
+
FOR_EACH_RINGBUFFER(adreno_dev, rb, i)
_adreno_ringbuffer_close(adreno_dev, rb);
}
@@ -469,7 +481,8 @@
total_sizedwords += (secured_ctxt) ? 26 : 0;
/* _seq mem write for each submission */
- total_sizedwords += 4;
+ if (adreno_is_a5xx(adreno_dev))
+ total_sizedwords += 4;
/* context rollover */
if (adreno_is_a3xx(adreno_dev))
@@ -622,9 +635,11 @@
* early detection of timestamp interrupt storms to stave
* off system collapse.
*/
- ringcmds += cp_mem_write(adreno_dev, ringcmds,
- MEMSTORE_ID_GPU_ADDR(device, KGSL_MEMSTORE_GLOBAL,
- ref_wait_ts), ++_seq_cnt);
+ if (adreno_is_a5xx(adreno_dev))
+ ringcmds += cp_mem_write(adreno_dev, ringcmds,
+ MEMSTORE_ID_GPU_ADDR(device,
+ KGSL_MEMSTORE_GLOBAL,
+ ref_wait_ts), ++_seq_cnt);
/*
* end-of-pipeline timestamp. If per context timestamps is not
diff --git a/drivers/gpu/msm/adreno_sysfs.c b/drivers/gpu/msm/adreno_sysfs.c
index 2d001af6..fcf0417 100644
--- a/drivers/gpu/msm/adreno_sysfs.c
+++ b/drivers/gpu/msm/adreno_sysfs.c
@@ -54,38 +54,50 @@
static int _preempt_level_store(struct adreno_device *adreno_dev,
unsigned int val)
{
+ struct adreno_preemption *preempt = &adreno_dev->preempt;
+
if (val <= 2)
- adreno_dev->preempt_level = val;
+ preempt->preempt_level = val;
return 0;
}
static unsigned int _preempt_level_show(struct adreno_device *adreno_dev)
{
- return adreno_dev->preempt_level;
+ struct adreno_preemption *preempt = &adreno_dev->preempt;
+
+ return preempt->preempt_level;
}
static int _usesgmem_store(struct adreno_device *adreno_dev,
unsigned int val)
{
- adreno_dev->usesgmem = val ? 1 : 0;
+ struct adreno_preemption *preempt = &adreno_dev->preempt;
+
+ preempt->usesgmem = val ? 1 : 0;
return 0;
}
static unsigned int _usesgmem_show(struct adreno_device *adreno_dev)
{
- return adreno_dev->usesgmem;
+ struct adreno_preemption *preempt = &adreno_dev->preempt;
+
+ return preempt->usesgmem;
}
static int _skipsaverestore_store(struct adreno_device *adreno_dev,
unsigned int val)
{
- adreno_dev->skipsaverestore = val ? 1 : 0;
+ struct adreno_preemption *preempt = &adreno_dev->preempt;
+
+ preempt->skipsaverestore = val ? 1 : 0;
return 0;
}
static unsigned int _skipsaverestore_show(struct adreno_device *adreno_dev)
{
- return adreno_dev->skipsaverestore;
+ struct adreno_preemption *preempt = &adreno_dev->preempt;
+
+ return preempt->skipsaverestore;
}
static int _ft_pagefault_policy_store(struct adreno_device *adreno_dev,
@@ -220,6 +232,23 @@
return 0;
}
+static int _gmu_idle_level_store(struct adreno_device *adreno_dev,
+ unsigned int val)
+{
+ struct kgsl_device *device = KGSL_DEVICE(adreno_dev);
+ struct gmu_device *gmu = &device->gmu;
+
+ mutex_lock(&device->mutex);
+
+ /* Power down the GPU before changing the idle level */
+ kgsl_pwrctrl_change_state(device, KGSL_STATE_SUSPEND);
+ gmu->idle_level = val;
+ kgsl_pwrctrl_change_state(device, KGSL_STATE_SLUMBER);
+
+ mutex_unlock(&device->mutex);
+ return 0;
+}
+
static unsigned int _preemption_show(struct adreno_device *adreno_dev)
{
return adreno_is_preemption_execution_enabled(adreno_dev);
@@ -268,6 +297,40 @@
return test_bit(ADRENO_LM_CTRL, &adreno_dev->pwrctrl_flag);
}
+static int _ifpc_store(struct adreno_device *adreno_dev, unsigned int val)
+{
+ struct kgsl_device *device = KGSL_DEVICE(adreno_dev);
+ struct gmu_device *gmu = &device->gmu;
+ unsigned int requested_idle_level;
+
+ if (!kgsl_gmu_isenabled(device) ||
+ !ADRENO_FEATURE(adreno_dev, ADRENO_IFPC))
+ return -EINVAL;
+
+ if ((val && gmu->idle_level >= GPU_HW_IFPC) ||
+ (!val && gmu->idle_level < GPU_HW_IFPC))
+ return 0;
+
+ if (val)
+ requested_idle_level = GPU_HW_IFPC;
+ else {
+ if (ADRENO_FEATURE(adreno_dev, ADRENO_SPTP_PC))
+ requested_idle_level = GPU_HW_SPTP_PC;
+ else
+ requested_idle_level = GPU_HW_ACTIVE;
+ }
+
+ return _gmu_idle_level_store(adreno_dev, requested_idle_level);
+}
+
+static unsigned int _ifpc_show(struct adreno_device *adreno_dev)
+{
+ struct kgsl_device *device = KGSL_DEVICE(adreno_dev);
+ struct gmu_device *gmu = &device->gmu;
+
+ return kgsl_gmu_isenabled(device) && gmu->idle_level >= GPU_HW_IFPC;
+}
+
static ssize_t _sysfs_store_u32(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
@@ -366,6 +429,7 @@
static ADRENO_SYSFS_BOOL(preemption);
static ADRENO_SYSFS_BOOL(hwcg);
static ADRENO_SYSFS_BOOL(throttling);
+static ADRENO_SYSFS_BOOL(ifpc);
@@ -386,6 +450,7 @@
&adreno_attr_preempt_level.attr,
&adreno_attr_usesgmem.attr,
&adreno_attr_skipsaverestore.attr,
+ &adreno_attr_ifpc.attr,
NULL,
};
diff --git a/drivers/gpu/msm/kgsl.c b/drivers/gpu/msm/kgsl.c
index 0a7d165..31868a0 100644
--- a/drivers/gpu/msm/kgsl.c
+++ b/drivers/gpu/msm/kgsl.c
@@ -1131,8 +1131,6 @@
atomic_inc(&device->active_cnt);
kgsl_sharedmem_set(device, &device->memstore, 0, 0,
device->memstore.size);
- kgsl_sharedmem_set(device, &device->scratch, 0, 0,
- device->scratch.size);
result = device->ftbl->init(device);
if (result)
@@ -2763,6 +2761,10 @@
int cacheop;
int mode;
+ /* Cache ops are not allowed on secure memory */
+ if (entry->memdesc.flags & KGSL_MEMFLAGS_SECURE)
+ return 0;
+
/*
* Flush is defined as (clean | invalidate). If both bits are set, then
* do a flush, otherwise check for the individual bits and clean or inv
@@ -3461,6 +3463,7 @@
return 0;
}
+/* entry->bind_lock must be held by the caller */
static int _sparse_add_to_bind_tree(struct kgsl_mem_entry *entry,
uint64_t v_offset,
struct kgsl_memdesc *memdesc,
@@ -3489,10 +3492,16 @@
parent = *node;
this = rb_entry(parent, struct sparse_bind_object, node);
- if (new->v_off < this->v_off)
+ if ((new->v_off < this->v_off) &&
+ ((new->v_off + new->size) <= this->v_off))
node = &parent->rb_left;
- else if (new->v_off > this->v_off)
+ else if ((new->v_off > this->v_off) &&
+ (new->v_off >= (this->v_off + this->size)))
node = &parent->rb_right;
+ else {
+ kfree(new);
+ return -EADDRINUSE;
+ }
}
rb_link_node(&new->node, parent, node);
@@ -3713,8 +3722,11 @@
return ret;
}
+ spin_lock(&virt_entry->bind_lock);
ret = _sparse_add_to_bind_tree(virt_entry, v_offset, memdesc,
p_offset, size, flags);
+ spin_unlock(&virt_entry->bind_lock);
+
if (ret == 0)
memdesc->cur_bindings += size / PAGE_SIZE;
@@ -4368,13 +4380,13 @@
if (!kgsl_memdesc_use_cpu_map(&entry->memdesc)) {
val = get_unmapped_area(NULL, addr, len, 0, flags);
if (IS_ERR_VALUE(val))
- KGSL_MEM_ERR(device,
+ KGSL_DRV_ERR_RATELIMIT(device,
"get_unmapped_area: pid %d addr %lx pgoff %lx len %ld failed error %d\n",
private->pid, addr, pgoff, len, (int) val);
} else {
val = _get_svm_area(private, entry, addr, len, flags);
if (IS_ERR_VALUE(val))
- KGSL_MEM_ERR(device,
+ KGSL_DRV_ERR_RATELIMIT(device,
"_get_svm_area: pid %d mmap_base %lx addr %lx pgoff %lx len %ld failed error %d\n",
private->pid, current->mm->mmap_base, addr,
pgoff, len, (int) val);
@@ -4675,11 +4687,6 @@
if (status != 0)
goto error_close_mmu;
- status = kgsl_allocate_global(device, &device->scratch,
- PAGE_SIZE, 0, 0, "scratch");
- if (status != 0)
- goto error_free_memstore;
-
/*
* The default request type PM_QOS_REQ_ALL_CORES is
* applicable to all CPU cores that are online and
@@ -4725,8 +4732,6 @@
return 0;
-error_free_memstore:
- kgsl_free_global(device, &device->memstore);
error_close_mmu:
kgsl_mmu_close(device);
error_pwrctrl_close:
@@ -4754,8 +4759,6 @@
idr_destroy(&device->context_idr);
- kgsl_free_global(device, &device->scratch);
-
kgsl_free_global(device, &device->memstore);
kgsl_mmu_close(device);
diff --git a/drivers/gpu/msm/kgsl_gmu.c b/drivers/gpu/msm/kgsl_gmu.c
index 78ef8e5..0fd7286 100644
--- a/drivers/gpu/msm/kgsl_gmu.c
+++ b/drivers/gpu/msm/kgsl_gmu.c
@@ -10,6 +10,7 @@
* GNU General Public License for more details.
*
*/
+#include <linux/module.h>
#include <linux/types.h>
#include <linux/device.h>
#include <linux/iommu.h>
@@ -25,6 +26,13 @@
#include "a6xx_reg.h"
#include "adreno.h"
+#undef MODULE_PARAM_PREFIX
+#define MODULE_PARAM_PREFIX "kgsl_gmu."
+
+static bool nogmu;
+module_param(nogmu, bool, 0444);
+MODULE_PARM_DESC(nogmu, "Disable the GMU");
+
#define GMU_CONTEXT_USER 0
#define GMU_CONTEXT_KERNEL 1
#define GMU_KERNEL_ENTRIES 8
@@ -109,9 +117,9 @@
struct gmu_device *gmu = &device->gmu;
struct adreno_device *adreno_dev = ADRENO_DEVICE(device);
- if (gmu->pdev && ADRENO_FEATURE(adreno_dev, ADRENO_GPMU))
+ if (!nogmu && gmu->pdev &&
+ ADRENO_FEATURE(adreno_dev, ADRENO_GPMU))
return true;
-
return false;
}
@@ -799,6 +807,15 @@
if (status & GMU_INT_HOST_AHB_BUS_ERR)
dev_err_ratelimited(&gmu->pdev->dev,
"AHB bus error interrupt received\n");
+ if (status & GMU_INT_FENCE_ERR) {
+ unsigned int fence_status;
+
+ adreno_read_gmureg(ADRENO_DEVICE(device),
+ ADRENO_REG_GMU_AHB_FENCE_STATUS, &fence_status);
+ dev_err_ratelimited(&gmu->pdev->dev,
+ "FENCE error interrupt received %x\n", fence_status);
+ }
+
if (status & ~GMU_AO_INT_MASK)
dev_err_ratelimited(&gmu->pdev->dev,
"Unhandled GMU interrupts 0x%lx\n",
@@ -1266,7 +1283,7 @@
return ret;
}
-#define CX_GDSC_TIMEOUT 500 /* ms */
+#define CX_GDSC_TIMEOUT 5000 /* ms */
static int gmu_disable_gdsc(struct gmu_device *gmu)
{
int ret;
@@ -1285,7 +1302,7 @@
/*
* After GX GDSC is off, CX GDSC must be off
* Voting off alone from GPU driver cannot
- * Guarantee CX GDSC off. Polling with 10ms
+ * Guarantee CX GDSC off. Polling with 5s
* timeout to ensure
*/
t = jiffies + msecs_to_jiffies(CX_GDSC_TIMEOUT);
@@ -1300,37 +1317,6 @@
return -ETIMEDOUT;
}
-static int gmu_fast_boot(struct kgsl_device *device)
-{
- int ret;
- struct adreno_device *adreno_dev = ADRENO_DEVICE(device);
- struct adreno_gpudev *gpudev = ADRENO_GPU_DEVICE(adreno_dev);
- struct gmu_device *gmu = &device->gmu;
-
- hfi_stop(gmu);
- clear_bit(GMU_HFI_ON, &gmu->flags);
-
- ret = gpudev->rpmh_gpu_pwrctrl(adreno_dev, GMU_FW_START,
- GMU_RESET, 0);
- if (ret)
- return ret;
-
- /*FIXME: enabling WD interrupt*/
-
- ret = hfi_start(gmu, GMU_WARM_BOOT);
- if (ret)
- return ret;
-
- ret = gpudev->oob_set(adreno_dev, OOB_CPINIT_SET_MASK,
- OOB_CPINIT_CHECK_MASK, OOB_CPINIT_CLEAR_MASK);
-
- if (ADRENO_QUIRK(adreno_dev, ADRENO_QUIRK_HFI_USE_REG))
- gpudev->oob_clear(adreno_dev,
- OOB_BOOT_SLUMBER_CLEAR_MASK);
-
- return ret;
-}
-
static int gmu_suspend(struct kgsl_device *device)
{
struct adreno_device *adreno_dev = ADRENO_DEVICE(device);
@@ -1386,6 +1372,31 @@
gmu->fault_count++;
}
+static void gmu_change_gpu_pwrlevel(struct kgsl_device *device,
+ unsigned int new_level) {
+
+ struct kgsl_pwrctrl *pwr = &device->pwrctrl;
+ unsigned int old_level = pwr->active_pwrlevel;
+
+ /*
+ * Update the level according to any thermal,
+ * max/min, or power constraints.
+ */
+ new_level = kgsl_pwrctrl_adjust_pwrlevel(device, new_level);
+
+ /*
+ * If thermal cycling is required and the new level hits the
+ * thermal limit, kick off the cycling.
+ */
+ kgsl_pwrctrl_set_thermal_cycle(device, new_level);
+
+ pwr->active_pwrlevel = new_level;
+ pwr->previous_pwrlevel = old_level;
+
+ /* Request adjusted DCVS level */
+ kgsl_clk_set_rate(device, pwr->active_pwrlevel);
+}
+
/* To be called to power on both GPU and GMU */
int gmu_start(struct kgsl_device *device)
{
@@ -1420,8 +1431,7 @@
goto error_gmu;
/* Request default DCVS level */
- kgsl_pwrctrl_pwrlevel_change(device, pwr->default_pwrlevel);
-
+ gmu_change_gpu_pwrlevel(device, pwr->default_pwrlevel);
msm_bus_scale_client_update_request(gmu->pcl, 0);
break;
@@ -1440,7 +1450,7 @@
if (ret)
goto error_gmu;
- kgsl_pwrctrl_pwrlevel_change(device, pwr->default_pwrlevel);
+ gmu_change_gpu_pwrlevel(device, pwr->default_pwrlevel);
break;
case KGSL_STATE_RESET:
@@ -1462,16 +1472,21 @@
goto error_gmu;
/* Send DCVS level prior to reset*/
- kgsl_pwrctrl_pwrlevel_change(device,
+ gmu_change_gpu_pwrlevel(device,
pwr->default_pwrlevel);
+ } else {
+ /* GMU fast boot */
+ hfi_stop(gmu);
- ret = gpudev->oob_set(adreno_dev,
- OOB_CPINIT_SET_MASK,
- OOB_CPINIT_CHECK_MASK,
- OOB_CPINIT_CLEAR_MASK);
+ ret = gpudev->rpmh_gpu_pwrctrl(adreno_dev, GMU_FW_START,
+ GMU_RESET, 0);
+ if (ret)
+ goto error_gmu;
- } else
- gmu_fast_boot(device);
+ ret = hfi_start(gmu, GMU_WARM_BOOT);
+ if (ret)
+ goto error_gmu;
+ }
break;
default:
break;
@@ -1480,6 +1495,9 @@
return ret;
error_gmu:
+ if (ADRENO_QUIRK(adreno_dev, ADRENO_QUIRK_HFI_USE_REG))
+ gpudev->oob_clear(adreno_dev,
+ OOB_BOOT_SLUMBER_CLEAR_MASK);
gmu_snapshot(device);
return ret;
}
@@ -1490,7 +1508,7 @@
struct gmu_device *gmu = &device->gmu;
struct adreno_device *adreno_dev = ADRENO_DEVICE(device);
struct adreno_gpudev *gpudev = ADRENO_GPU_DEVICE(adreno_dev);
- bool idle = true;
+ int ret = 0;
if (!test_bit(GMU_CLK_ON, &gmu->flags))
return;
@@ -1498,38 +1516,37 @@
/* Wait for the lowest idle level we requested */
if (gpudev->wait_for_lowest_idle &&
gpudev->wait_for_lowest_idle(adreno_dev))
- idle = false;
+ goto error;
- gpudev->rpmh_gpu_pwrctrl(adreno_dev, GMU_NOTIFY_SLUMBER, 0, 0);
- if (!idle || (gpudev->wait_for_gmu_idle &&
- gpudev->wait_for_gmu_idle(adreno_dev))) {
- dev_err(&gmu->pdev->dev, "Stopping GMU before it is idle\n");
- idle = false;
- set_bit(GMU_FAULT, &gmu->flags);
- } else {
- idle = true;
- }
+ ret = gpudev->rpmh_gpu_pwrctrl(adreno_dev, GMU_NOTIFY_SLUMBER, 0, 0);
+ if (ret)
+ goto error;
- if (idle) {
- /* Pending message in all queues are abandoned */
- hfi_stop(gmu);
- clear_bit(GMU_HFI_ON, &gmu->flags);
- gmu_irq_disable(device);
+ if (gpudev->wait_for_gmu_idle &&
+ gpudev->wait_for_gmu_idle(adreno_dev))
+ goto error;
- gpudev->rpmh_gpu_pwrctrl(adreno_dev, GMU_FW_STOP, 0, 0);
- gmu_disable_clks(gmu);
- gmu_disable_gdsc(gmu);
- } else {
- /*
- * The power controller will change state to SLUMBER anyway
- * Set GMU_FAULT flag to indicate to power contrller
- * that hang recovery is needed to power on GPU
- */
- set_bit(GMU_FAULT, &gmu->flags);
- gmu_snapshot(device);
- }
+ /* Pending message in all queues are abandoned */
+ hfi_stop(gmu);
+ clear_bit(GMU_HFI_ON, &gmu->flags);
+ gmu_irq_disable(device);
+
+ gpudev->rpmh_gpu_pwrctrl(adreno_dev, GMU_FW_STOP, 0, 0);
+ gmu_disable_clks(gmu);
+ gmu_disable_gdsc(gmu);
msm_bus_scale_client_update_request(gmu->pcl, 0);
+ return;
+
+error:
+ /*
+ * The power controller will change state to SLUMBER anyway
+ * Set GMU_FAULT flag to indicate to power contrller
+ * that hang recovery is needed to power on GPU
+ */
+ set_bit(GMU_FAULT, &gmu->flags);
+ dev_err(&gmu->pdev->dev, "Failed to stop GMU\n");
+ gmu_snapshot(device);
}
void gmu_remove(struct kgsl_device *device)
diff --git a/drivers/gpu/msm/kgsl_gmu.h b/drivers/gpu/msm/kgsl_gmu.h
index fc6bafa..e0c857f 100644
--- a/drivers/gpu/msm/kgsl_gmu.h
+++ b/drivers/gpu/msm/kgsl_gmu.h
@@ -22,11 +22,13 @@
#define GMU_INT_WDOG_BITE BIT(0)
#define GMU_INT_RSCC_COMP BIT(1)
+#define GMU_INT_FENCE_ERR BIT(3)
#define GMU_INT_DBD_WAKEUP BIT(4)
#define GMU_INT_HOST_AHB_BUS_ERR BIT(5)
#define GMU_AO_INT_MASK \
(GMU_INT_WDOG_BITE | \
- GMU_INT_HOST_AHB_BUS_ERR)
+ GMU_INT_HOST_AHB_BUS_ERR | \
+ GMU_INT_FENCE_ERR)
#define MAX_GMUFW_SIZE 0x2000 /* in dwords */
#define FENCE_RANGE_MASK ((0x1 << 31) | (0x0A << 18) | (0x8A0))
@@ -70,15 +72,12 @@
#define OOB_DCVS_SET_MASK BIT(23)
#define OOB_DCVS_CHECK_MASK BIT(31)
#define OOB_DCVS_CLEAR_MASK BIT(31)
-#define OOB_CPINIT_SET_MASK BIT(16)
-#define OOB_CPINIT_CHECK_MASK BIT(24)
-#define OOB_CPINIT_CLEAR_MASK BIT(24)
+#define OOB_GPU_SET_MASK BIT(16)
+#define OOB_GPU_CHECK_MASK BIT(24)
+#define OOB_GPU_CLEAR_MASK BIT(24)
#define OOB_PERFCNTR_SET_MASK BIT(17)
#define OOB_PERFCNTR_CHECK_MASK BIT(25)
#define OOB_PERFCNTR_CLEAR_MASK BIT(25)
-#define OOB_GPU_SET_MASK BIT(18)
-#define OOB_GPU_CHECK_MASK BIT(26)
-#define OOB_GPU_CLEAR_MASK BIT(26)
/* Bits for the flags field in the gmu structure */
enum gmu_flags {
diff --git a/drivers/gpu/msm/kgsl_hfi.h b/drivers/gpu/msm/kgsl_hfi.h
index 191987e..105599c 100644
--- a/drivers/gpu/msm/kgsl_hfi.h
+++ b/drivers/gpu/msm/kgsl_hfi.h
@@ -115,7 +115,7 @@
HFI_F2H_QPRI_DEBUG = 40,
};
-#define HFI_RSP_TIMEOUT 500 /* msec */
+#define HFI_RSP_TIMEOUT 5000 /* msec */
#define HFI_H2F_CMD_IRQ_MASK BIT(0)
enum hfi_msg_type {
diff --git a/drivers/gpu/msm/kgsl_log.h b/drivers/gpu/msm/kgsl_log.h
index d79a410..4f1241b 100644
--- a/drivers/gpu/msm/kgsl_log.h
+++ b/drivers/gpu/msm/kgsl_log.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2002,2008-2011,2013-2014,2016 The Linux Foundation.
+/* Copyright (c) 2002,2008-2011,2013-2014,2016-2017 The Linux Foundation.
* All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
@@ -67,6 +67,13 @@
__func__, ##args);\
} while (0)
+#define KGSL_LOG_ERR_RATELIMITED(dev, lvl, fmt, args...) \
+ do { \
+ if ((lvl) >= 3) \
+ dev_err_ratelimited(dev, "|%s| " fmt, \
+ __func__, ##args);\
+ } while (0)
+
#define KGSL_DRV_INFO(_dev, fmt, args...) \
KGSL_LOG_INFO(_dev->dev, _dev->drv_log, fmt, ##args)
#define KGSL_DRV_WARN(_dev, fmt, args...) \
@@ -77,6 +84,8 @@
KGSL_LOG_CRIT(_dev->dev, _dev->drv_log, fmt, ##args)
#define KGSL_DRV_CRIT_RATELIMIT(_dev, fmt, args...) \
KGSL_LOG_CRIT_RATELIMITED(_dev->dev, _dev->drv_log, fmt, ##args)
+#define KGSL_DRV_ERR_RATELIMIT(_dev, fmt, args...) \
+KGSL_LOG_ERR_RATELIMITED(_dev->dev, _dev->drv_log, fmt, ##args)
#define KGSL_DRV_FATAL(_dev, fmt, args...) \
KGSL_LOG_FATAL((_dev)->dev, (_dev)->drv_log, fmt, ##args)
diff --git a/drivers/gpu/msm/kgsl_mmu.c b/drivers/gpu/msm/kgsl_mmu.c
index 8ea4492..a0fd3ec 100644
--- a/drivers/gpu/msm/kgsl_mmu.c
+++ b/drivers/gpu/msm/kgsl_mmu.c
@@ -26,6 +26,17 @@
static void pagetable_remove_sysfs_objects(struct kgsl_pagetable *pagetable);
+static void _deferred_destroy(struct work_struct *ws)
+{
+ struct kgsl_pagetable *pagetable = container_of(ws,
+ struct kgsl_pagetable, destroy_ws);
+
+ if (PT_OP_VALID(pagetable, mmu_destroy_pagetable))
+ pagetable->pt_ops->mmu_destroy_pagetable(pagetable);
+
+ kfree(pagetable);
+}
+
static void kgsl_destroy_pagetable(struct kref *kref)
{
struct kgsl_pagetable *pagetable = container_of(kref,
@@ -33,10 +44,7 @@
kgsl_mmu_detach_pagetable(pagetable);
- if (PT_OP_VALID(pagetable, mmu_destroy_pagetable))
- pagetable->pt_ops->mmu_destroy_pagetable(pagetable);
-
- kfree(pagetable);
+ kgsl_schedule_work(&pagetable->destroy_ws);
}
static inline void kgsl_put_pagetable(struct kgsl_pagetable *pagetable)
@@ -299,6 +307,7 @@
kref_init(&pagetable->refcount);
spin_lock_init(&pagetable->lock);
+ INIT_WORK(&pagetable->destroy_ws, _deferred_destroy);
pagetable->mmu = mmu;
pagetable->name = name;
diff --git a/drivers/gpu/msm/kgsl_mmu.h b/drivers/gpu/msm/kgsl_mmu.h
index 56bb317..7a8ab74 100644
--- a/drivers/gpu/msm/kgsl_mmu.h
+++ b/drivers/gpu/msm/kgsl_mmu.h
@@ -42,6 +42,7 @@
struct list_head list;
unsigned int name;
struct kobject *kobj;
+ struct work_struct destroy_ws;
struct {
atomic_t entries;
diff --git a/drivers/gpu/msm/kgsl_pwrctrl.c b/drivers/gpu/msm/kgsl_pwrctrl.c
index 97e5c22..940a741f 100644
--- a/drivers/gpu/msm/kgsl_pwrctrl.c
+++ b/drivers/gpu/msm/kgsl_pwrctrl.c
@@ -223,7 +223,7 @@
}
if (ret)
- KGSL_PWR_ERR(device, "GPU BW scaling failure\n");
+ KGSL_PWR_ERR(device, "GPU BW scaling failure: %d\n", ret);
return ret;
}
@@ -233,7 +233,7 @@
* @device: Pointer to the kgsl_device struct
* @pwrlevel: power level in pwrlevels[] table
*/
-static int kgsl_clk_set_rate(struct kgsl_device *device,
+int kgsl_clk_set_rate(struct kgsl_device *device,
unsigned int pwrlevel)
{
struct gmu_device *gmu = &device->gmu;
@@ -263,7 +263,7 @@
ret = clk_set_rate(pwr->grp_clks[0], pl->gpu_freq);
if (ret)
- KGSL_PWR_ERR(device, "GPU clk freq set failure\n");
+ KGSL_PWR_ERR(device, "GPU clk freq set failure: %d\n", ret);
return ret;
}
@@ -344,12 +344,14 @@
/**
* kgsl_pwrctrl_set_thermal_cycle() - set the thermal cycle if required
- * @pwr: Pointer to the kgsl_pwrctrl struct
+ * @device: Pointer to the kgsl_device struct
* @new_level: the level to transition to
*/
-static void kgsl_pwrctrl_set_thermal_cycle(struct kgsl_pwrctrl *pwr,
+void kgsl_pwrctrl_set_thermal_cycle(struct kgsl_device *device,
unsigned int new_level)
{
+ struct kgsl_pwrctrl *pwr = &device->pwrctrl;
+
if ((new_level != pwr->thermal_pwrlevel) || !pwr->sysfs_pwr_limit)
return;
if (pwr->thermal_pwrlevel == pwr->sysfs_pwr_limit->level) {
@@ -370,21 +372,15 @@
}
/**
- * kgsl_pwrctrl_pwrlevel_change() - Validate and change power levels
+ * kgsl_pwrctrl_adjust_pwrlevel() - Adjust the power level if
+ * required by thermal, max/min, constraints, etc
* @device: Pointer to the kgsl_device struct
* @new_level: Requested powerlevel, an index into the pwrlevel array
- *
- * Check that any power level constraints are still valid. Update the
- * requested level according to any thermal, max/min, or power constraints.
- * If a new GPU level is going to be set, update the bus to that level's
- * default value. Do not change the bus if a constraint keeps the new
- * level at the current level. Set the new GPU frequency.
*/
-void kgsl_pwrctrl_pwrlevel_change(struct kgsl_device *device,
+unsigned int kgsl_pwrctrl_adjust_pwrlevel(struct kgsl_device *device,
unsigned int new_level)
{
struct kgsl_pwrctrl *pwr = &device->pwrctrl;
- struct kgsl_pwrlevel *pwrlevel;
unsigned int old_level = pwr->active_pwrlevel;
/* If a pwr constraint is expired, remove it */
@@ -402,14 +398,35 @@
* Adjust the power level if required by thermal, max/min,
* constraints, etc
*/
- new_level = _adjust_pwrlevel(pwr, new_level, &pwr->constraint,
+ return _adjust_pwrlevel(pwr, new_level, &pwr->constraint,
device->pwrscale.popp_level);
+}
+
+/**
+ * kgsl_pwrctrl_pwrlevel_change() - Validate and change power levels
+ * @device: Pointer to the kgsl_device struct
+ * @new_level: Requested powerlevel, an index into the pwrlevel array
+ *
+ * Check that any power level constraints are still valid. Update the
+ * requested level according to any thermal, max/min, or power constraints.
+ * If a new GPU level is going to be set, update the bus to that level's
+ * default value. Do not change the bus if a constraint keeps the new
+ * level at the current level. Set the new GPU frequency.
+ */
+void kgsl_pwrctrl_pwrlevel_change(struct kgsl_device *device,
+ unsigned int new_level)
+{
+ struct kgsl_pwrctrl *pwr = &device->pwrctrl;
+ struct kgsl_pwrlevel *pwrlevel;
+ unsigned int old_level = pwr->active_pwrlevel;
+
+ new_level = kgsl_pwrctrl_adjust_pwrlevel(device, new_level);
/*
* If thermal cycling is required and the new level hits the
* thermal limit, kick off the cycling.
*/
- kgsl_pwrctrl_set_thermal_cycle(pwr, new_level);
+ kgsl_pwrctrl_set_thermal_cycle(device, new_level);
if (new_level == old_level &&
!test_bit(GMU_DCVS_REPLAY, &device->gmu.flags))
@@ -722,7 +739,7 @@
{
int i;
- for (i = pwr->num_pwrlevels - 1; i >= 0; i--) {
+ for (i = pwr->num_pwrlevels - 2; i >= 0; i--) {
if (abs(pwr->pwrlevels[i].gpu_freq - clock) < 5000000)
return i;
}
@@ -2485,9 +2502,6 @@
static void kgsl_pwrctrl_disable(struct kgsl_device *device)
{
if (kgsl_gmu_isenabled(device)) {
- struct kgsl_pwrctrl *pwr = &device->pwrctrl;
-
- pwr->active_pwrlevel = pwr->num_pwrlevels - 1;
kgsl_pwrctrl_axi(device, KGSL_PWRFLAGS_OFF);
return gmu_stop(device);
}
diff --git a/drivers/gpu/msm/kgsl_pwrctrl.h b/drivers/gpu/msm/kgsl_pwrctrl.h
index 85bab11..2a45de7 100644
--- a/drivers/gpu/msm/kgsl_pwrctrl.h
+++ b/drivers/gpu/msm/kgsl_pwrctrl.h
@@ -237,6 +237,12 @@
int kgsl_pwrctrl_init_sysfs(struct kgsl_device *device);
void kgsl_pwrctrl_uninit_sysfs(struct kgsl_device *device);
int kgsl_pwrctrl_change_state(struct kgsl_device *device, int state);
+int kgsl_clk_set_rate(struct kgsl_device *device,
+ unsigned int pwrlevel);
+unsigned int kgsl_pwrctrl_adjust_pwrlevel(struct kgsl_device *device,
+ unsigned int new_level);
+void kgsl_pwrctrl_set_thermal_cycle(struct kgsl_device *device,
+ unsigned int new_level);
static inline unsigned long kgsl_get_clkrate(struct clk *clk)
{
diff --git a/drivers/gpu/msm/kgsl_sync.c b/drivers/gpu/msm/kgsl_sync.c
index 015d07f..d4165b3 100644
--- a/drivers/gpu/msm/kgsl_sync.c
+++ b/drivers/gpu/msm/kgsl_sync.c
@@ -339,10 +339,10 @@
return -ENOENT;
snprintf(ktimeline_name, sizeof(ktimeline_name),
- "%s_%.15s(%d)-%.15s(%d)-%d",
- context->device->name,
+ "%s_%d-%.15s(%d)-%.15s(%d)",
+ context->device->name, context->id,
current->group_leader->comm, current->group_leader->pid,
- current->comm, current->pid, context->id);
+ current->comm, current->pid);
ktimeline = kzalloc(sizeof(*ktimeline), GFP_KERNEL);
if (ktimeline == NULL) {
diff --git a/drivers/hwmon/qpnp-adc-current.c b/drivers/hwmon/qpnp-adc-current.c
index a597ef9..c4da780 100644
--- a/drivers/hwmon/qpnp-adc-current.c
+++ b/drivers/hwmon/qpnp-adc-current.c
@@ -27,17 +27,11 @@
#include <linux/spmi.h>
#include <linux/platform_device.h>
#include <linux/of_irq.h>
-#ifdef CONFIG_WAKELOCK
-#include <linux/wakelock.h>
-#endif
#include <linux/interrupt.h>
#include <linux/completion.h>
#include <linux/hwmon-sysfs.h>
#include <linux/qpnp/qpnp-adc.h>
#include <linux/platform_device.h>
-#ifdef CONFIG_WAKELOCK
-#include <linux/wakelock.h>
-#endif
/* QPNP IADC register definition */
#define QPNP_IADC_REVISION1 0x0
diff --git a/drivers/i2c/busses/i2c-qcom-geni.c b/drivers/i2c/busses/i2c-qcom-geni.c
index 2460ba7..793cbb5 100644
--- a/drivers/i2c/busses/i2c-qcom-geni.c
+++ b/drivers/i2c/busses/i2c-qcom-geni.c
@@ -881,9 +881,17 @@
return ret;
if (gi2c->se_mode == UNINITIALIZED) {
- u32 se_mode = readl_relaxed(gi2c->base +
- GENI_IF_FIFO_DISABLE_RO);
+ int proto = get_se_proto(gi2c->base);
+ u32 se_mode;
+ if (unlikely(proto != I2C)) {
+ dev_err(gi2c->dev, "Invalid proto %d\n", proto);
+ se_geni_resources_off(&gi2c->i2c_rsc);
+ return -ENXIO;
+ }
+
+ se_mode = readl_relaxed(gi2c->base +
+ GENI_IF_FIFO_DISABLE_RO);
if (se_mode) {
gi2c->se_mode = GSI_ONLY;
geni_se_select_mode(gi2c->base, GSI_DMA);
diff --git a/drivers/iommu/arm-smmu.c b/drivers/iommu/arm-smmu.c
index a0e1f59..7df1f56 100644
--- a/drivers/iommu/arm-smmu.c
+++ b/drivers/iommu/arm-smmu.c
@@ -101,6 +101,9 @@
#define sCR0_VMID16EN (1 << 31)
#define sCR0_BSU_SHIFT 14
#define sCR0_BSU_MASK 0x3
+#define sCR0_SHCFG_SHIFT 22
+#define sCR0_SHCFG_MASK 0x3
+#define sCR0_SHCFG_NSH 3
/* Auxiliary Configuration register */
#define ARM_SMMU_GR0_sACR 0x10
@@ -177,6 +180,9 @@
#define S2CR_CBNDX_MASK 0xff
#define S2CR_TYPE_SHIFT 16
#define S2CR_TYPE_MASK 0x3
+#define S2CR_SHCFG_SHIFT 8
+#define S2CR_SHCFG_MASK 0x3
+#define S2CR_SHCFG_NSH 0x3
enum arm_smmu_s2cr_type {
S2CR_TYPE_TRANS,
S2CR_TYPE_BYPASS,
@@ -251,6 +257,9 @@
#define ARM_SMMU_CB_ATS1PR 0x800
#define ARM_SMMU_CB_ATSR 0x8f0
+#define SCTLR_SHCFG_SHIFT 22
+#define SCTLR_SHCFG_MASK 0x3
+#define SCTLR_SHCFG_NSH 0x3
#define SCTLR_S1_ASIDPNE (1 << 12)
#define SCTLR_CFCFG (1 << 7)
#define SCTLR_HUPCF (1 << 8)
@@ -1181,6 +1190,7 @@
list_for_each_entry_safe(it, i, &smmu_domain->secure_pool_list, list) {
arm_smmu_unprepare_pgtable(smmu_domain, it->addr, it->size);
/* pages will be freed later (after being unassigned) */
+ list_del(&it->list);
kfree(it);
}
}
@@ -1526,6 +1536,9 @@
/* SCTLR */
reg = SCTLR_CFCFG | SCTLR_CFIE | SCTLR_CFRE | SCTLR_AFE | SCTLR_TRE;
+ /* Ensure bypass transactions are Non-shareable */
+ reg |= SCTLR_SHCFG_NSH << SCTLR_SHCFG_SHIFT;
+
if (smmu_domain->attributes & (1 << DOMAIN_ATTR_CB_STALL_DISABLE)) {
reg &= ~SCTLR_CFCFG;
reg |= SCTLR_HUPCF;
@@ -1929,7 +1942,8 @@
struct arm_smmu_s2cr *s2cr = smmu->s2crs + idx;
u32 reg = (s2cr->type & S2CR_TYPE_MASK) << S2CR_TYPE_SHIFT |
(s2cr->cbndx & S2CR_CBNDX_MASK) << S2CR_CBNDX_SHIFT |
- (s2cr->privcfg & S2CR_PRIVCFG_MASK) << S2CR_PRIVCFG_SHIFT;
+ (s2cr->privcfg & S2CR_PRIVCFG_MASK) << S2CR_PRIVCFG_SHIFT |
+ S2CR_SHCFG_NSH << S2CR_SHCFG_SHIFT;
writel_relaxed(reg, ARM_SMMU_GR0(smmu) + ARM_SMMU_GR0_S2CR(idx));
}
@@ -2392,10 +2406,6 @@
if (!ops)
return -ENODEV;
- ret = arm_smmu_domain_power_on(domain, smmu_domain->smmu);
- if (ret)
- return ret;
-
arm_smmu_secure_domain_lock(smmu_domain);
__saved_iova_start = iova;
@@ -2436,7 +2446,6 @@
iova = __saved_iova_start;
}
arm_smmu_secure_domain_unlock(smmu_domain);
- arm_smmu_domain_power_off(domain, smmu_domain->smmu);
return iova - __saved_iova_start;
}
@@ -2950,11 +2959,22 @@
1 << DOMAIN_ATTR_UPSTREAM_IOVA_ALLOCATOR;
ret = 0;
break;
+ /*
+ * fast_smmu_unmap_page() and fast_smmu_alloc_iova() both
+ * expect that the bus/clock/regulator are already on. Thus also
+ * force DOMAIN_ATTR_ATOMIC to bet set.
+ */
case DOMAIN_ATTR_FAST:
- if (*((int *)data))
+ {
+ int fast = *((int *)data);
+
+ if (fast) {
smmu_domain->attributes |= 1 << DOMAIN_ATTR_FAST;
+ smmu_domain->attributes |= 1 << DOMAIN_ATTR_ATOMIC;
+ }
ret = 0;
break;
+ }
case DOMAIN_ATTR_USE_UPSTREAM_HINT:
/* can't be changed while attached */
if (smmu_domain->smmu != NULL) {
@@ -3430,6 +3450,10 @@
if (smmu->features & ARM_SMMU_FEAT_VMID16)
reg |= sCR0_VMID16EN;
+ /* Force bypass transaction to be Non-Shareable & not io-coherent */
+ reg &= ~(sCR0_SHCFG_MASK << sCR0_SHCFG_SHIFT);
+ reg |= sCR0_SHCFG_NSH;
+
/* Push the button */
__arm_smmu_tlb_sync(smmu);
writel(reg, ARM_SMMU_GR0_NS(smmu) + ARM_SMMU_GR0_sCR0);
diff --git a/drivers/iommu/dma-mapping-fast.c b/drivers/iommu/dma-mapping-fast.c
index 04a5c09..de95d7a 100644
--- a/drivers/iommu/dma-mapping-fast.c
+++ b/drivers/iommu/dma-mapping-fast.c
@@ -867,7 +867,7 @@
int fast_smmu_init_mapping(struct device *dev,
struct dma_iommu_mapping *mapping)
{
- int err, atomic_domain = 1;
+ int err;
struct iommu_domain *domain = mapping->domain;
struct iommu_group *group;
struct iommu_pgtbl_info info;
@@ -878,10 +878,6 @@
return -EINVAL;
}
- if (iommu_domain_set_attr(domain, DOMAIN_ATTR_ATOMIC,
- &atomic_domain))
- return -EINVAL;
-
mapping->fast = __fast_smmu_create_mapping_sized(mapping->base, size);
if (IS_ERR(mapping->fast))
return -ENOMEM;
diff --git a/drivers/iommu/iommu-debug.c b/drivers/iommu/iommu-debug.c
index 56eff61b..6d79cfb 100644
--- a/drivers/iommu/iommu-debug.c
+++ b/drivers/iommu/iommu-debug.c
@@ -102,20 +102,25 @@
struct iommu_debug_attachment *attach;
struct iommu_group *group;
- group = iommu_group_get(dev);
+ group = dev->iommu_group;
if (!group)
return;
+ mutex_lock(&iommu_debug_attachments_lock);
+ list_for_each_entry(attach, &iommu_debug_attachments, list)
+ if ((attach->domain == domain) && (attach->group == group))
+ goto out;
+
attach = kzalloc(sizeof(*attach), GFP_KERNEL);
if (!attach)
- return;
+ goto out;
attach->domain = domain;
attach->group = group;
INIT_LIST_HEAD(&attach->list);
- mutex_lock(&iommu_debug_attachments_lock);
list_add(&attach->list, &iommu_debug_attachments);
+out:
mutex_unlock(&iommu_debug_attachments_lock);
}
@@ -128,7 +133,6 @@
if (it->domain != domain)
continue;
list_del(&it->list);
- iommu_group_put(it->group);
kfree(it);
}
diff --git a/drivers/irqchip/qcom/pdc-sdm845.c b/drivers/irqchip/qcom/pdc-sdm845.c
index 178cf1f0..2f90e10 100644
--- a/drivers/irqchip/qcom/pdc-sdm845.c
+++ b/drivers/irqchip/qcom/pdc-sdm845.c
@@ -130,10 +130,135 @@
{-1}
};
+static struct pdc_pin sdm845_data_v2[] = {
+ {0, 512}, /* rpmh_wake */
+ {1, 513}, /* ee0_apps_hlos_spmi_periph_irq */
+ {2, 514}, /* ee1_apps_trustzone_spmi_periph_irq */
+ {3, 515}, /* secure_wdog_expired */
+ {4, 516}, /* secure_wdog_bark_irq */
+ {5, 517}, /* aop_wdog_expired_irq */
+ {6, 518}, /* qmp_usb3_lfps_rxterm_irq */
+ {7, 519}, /* qmp_usb3_lfps_rxterm_irq */
+ {8, 520}, /* eud_p0_dmse_int_mx */
+ {9, 521}, /* eud_p0_dpse_int_mx */
+ {10, 522}, /* eud_p1_dmse_int_mx */
+ {11, 523}, /* eud_p1_dpse_int_mx */
+ {12, 524}, /* eud_int_mx[1] */
+ {13, 525}, /* ssc_xpu_irq_summary */
+ {14, 526}, /* wd_bite_apps */
+ {15, 527}, /* ssc_vmidmt_irq_summary */
+ {16, 528}, /* q6ss_irq_out_apps_ipc[4] */
+ {17, 529}, /* not-connected */
+ {18, 530}, /* aoss_pmic_arb_mpu_xpu_summary_irq */
+ {19, 531}, /* apps_pdc_irq_in_19 */
+ {20, 532}, /* apps_pdc_irq_in_20 */
+ {21, 533}, /* apps_pdc_irq_in_21 */
+ {22, 534}, /* pdc_apps_epcb_timeout_summary_irq */
+ {23, 535}, /* spmi_protocol_irq */
+ {24, 536}, /* tsense0_tsense_max_min_int */
+ {25, 537}, /* tsense1_tsense_max_min_int */
+ {26, 538}, /* tsense0_upper_lower_intr */
+ {27, 539}, /* tsense1_upper_lower_intr */
+ {28, 540}, /* tsense0_critical_intr */
+ {29, 541}, /* tsense1_critical_intr */
+ {30, 542}, /* core_bi_px_gpio_1 */
+ {31, 543}, /* core_bi_px_gpio_3 */
+ {32, 544}, /* core_bi_px_gpio_5 */
+ {33, 545}, /* core_bi_px_gpio_10 */
+ {34, 546}, /* core_bi_px_gpio_11 */
+ {35, 547}, /* core_bi_px_gpio_20 */
+ {36, 548}, /* core_bi_px_gpio_22 */
+ {37, 549}, /* core_bi_px_gpio_24 */
+ {38, 550}, /* core_bi_px_gpio_26 */
+ {39, 551}, /* core_bi_px_gpio_30 */
+ {41, 553}, /* core_bi_px_gpio_32 */
+ {42, 554}, /* core_bi_px_gpio_34 */
+ {43, 555}, /* core_bi_px_gpio_36 */
+ {44, 556}, /* core_bi_px_gpio_37 */
+ {45, 557}, /* core_bi_px_gpio_38 */
+ {46, 558}, /* core_bi_px_gpio_39 */
+ {47, 559}, /* core_bi_px_gpio_40 */
+ {49, 561}, /* core_bi_px_gpio_43 */
+ {50, 562}, /* core_bi_px_gpio_44 */
+ {51, 563}, /* core_bi_px_gpio_46 */
+ {52, 564}, /* core_bi_px_gpio_48 */
+ {54, 566}, /* core_bi_px_gpio_52 */
+ {55, 567}, /* core_bi_px_gpio_53 */
+ {56, 568}, /* core_bi_px_gpio_54 */
+ {57, 569}, /* core_bi_px_gpio_56 */
+ {58, 570}, /* core_bi_px_gpio_57 */
+ {59, 571}, /* core_bi_px_gpio_58 */
+ {60, 572}, /* core_bi_px_gpio_59 */
+ {61, 573}, /* core_bi_px_gpio_60 */
+ {62, 574}, /* core_bi_px_gpio_61 */
+ {63, 575}, /* core_bi_px_gpio_62 */
+ {64, 576}, /* core_bi_px_gpio_63 */
+ {65, 577}, /* core_bi_px_gpio_64 */
+ {66, 578}, /* core_bi_px_gpio_66 */
+ {67, 579}, /* core_bi_px_gpio_68 */
+ {68, 580}, /* core_bi_px_gpio_71 */
+ {69, 581}, /* core_bi_px_gpio_73 */
+ {70, 582}, /* core_bi_px_gpio_77 */
+ {71, 583}, /* core_bi_px_gpio_78 */
+ {72, 584}, /* core_bi_px_gpio_79 */
+ {73, 585}, /* core_bi_px_gpio_80 */
+ {74, 586}, /* core_bi_px_gpio_84 */
+ {75, 587}, /* core_bi_px_gpio_85 */
+ {76, 588}, /* core_bi_px_gpio_86 */
+ {77, 589}, /* core_bi_px_gpio_88 */
+ {79, 591}, /* core_bi_px_gpio_91 */
+ {80, 592}, /* core_bi_px_gpio_92 */
+ {81, 593}, /* core_bi_px_gpio_95 */
+ {82, 594}, /* core_bi_px_gpio_96 */
+ {83, 595}, /* core_bi_px_gpio_97 */
+ {84, 596}, /* core_bi_px_gpio_101 */
+ {85, 597}, /* core_bi_px_gpio_103 */
+ {86, 598}, /* core_bi_px_gpio_104 */
+ {87, 599}, /* core_bi_px_to_mpm[6] */
+ {88, 600}, /* core_bi_px_to_mpm[0] */
+ {89, 601}, /* core_bi_px_to_mpm[1] */
+ {90, 602}, /* core_bi_px_gpio_115 */
+ {91, 603}, /* core_bi_px_gpio_116 */
+ {92, 604}, /* core_bi_px_gpio_117 */
+ {93, 605}, /* core_bi_px_gpio_118 */
+ {94, 641}, /* core_bi_px_gpio_119 */
+ {95, 642}, /* core_bi_px_gpio_120 */
+ {96, 643}, /* core_bi_px_gpio_121 */
+ {97, 644}, /* core_bi_px_gpio_122 */
+ {98, 645}, /* core_bi_px_gpio_123 */
+ {99, 646}, /* core_bi_px_gpio_124 */
+ {100, 647}, /* core_bi_px_gpio_125 */
+ {101, 648}, /* core_bi_px_to_mpm[5] */
+ {102, 649}, /* core_bi_px_gpio_127 */
+ {103, 650}, /* core_bi_px_gpio_128 */
+ {104, 651}, /* core_bi_px_gpio_129 */
+ {105, 652}, /* core_bi_px_gpio_130 */
+ {106, 653}, /* core_bi_px_gpio_132 */
+ {107, 654}, /* core_bi_px_gpio_133 */
+ {108, 655}, /* core_bi_px_gpio_145 */
+ {119, 666}, /* core_bi_px_to_mpm[2] */
+ {120, 667}, /* core_bi_px_to_mpm[3] */
+ {121, 668}, /* core_bi_px_to_mpm[4] */
+ {115, 662}, /* core_bi_px_gpio_41 */
+ {116, 663}, /* core_bi_px_gpio_89 */
+ {117, 664}, /* core_bi_px_gpio_31 */
+ {118, 665}, /* core_bi_px_gpio_49 */
+ {-1}
+};
+
static int __init qcom_pdc_gic_init(struct device_node *node,
struct device_node *parent)
{
+ pr_info("PDC SDM845 v1 initialized\n");
return qcom_pdc_init(node, parent, sdm845_data);
}
+static int __init qcom_pdc_v2_gic_init(struct device_node *node,
+ struct device_node *parent)
+{
+ pr_info("PDC SDM845 v2 initialized\n");
+ return qcom_pdc_init(node, parent, sdm845_data_v2);
+}
+
IRQCHIP_DECLARE(pdc_sdm845, "qcom,pdc-sdm845", qcom_pdc_gic_init);
+IRQCHIP_DECLARE(pdc_sdm845_v2, "qcom,pdc-sdm845-v2", qcom_pdc_v2_gic_init);
diff --git a/drivers/leds/leds-qpnp-wled.c b/drivers/leds/leds-qpnp-wled.c
index 5c9c75c..98ca29e 100644
--- a/drivers/leds/leds-qpnp-wled.c
+++ b/drivers/leds/leds-qpnp-wled.c
@@ -63,7 +63,6 @@
#define QPNP_WLED_VLOOP_COMP_RES_MASK 0xF0
#define QPNP_WLED_VLOOP_COMP_RES_OVERWRITE 0x80
-#define QPNP_WLED_LOOP_COMP_RES_DFLT_AMOLED_KOHM 320
#define QPNP_WLED_LOOP_COMP_RES_STEP_KOHM 20
#define QPNP_WLED_LOOP_COMP_RES_MIN_KOHM 20
#define QPNP_WLED_LOOP_COMP_RES_MAX_KOHM 320
@@ -106,10 +105,8 @@
#define QPNP_WLED_BOOST_DUTY_MIN_NS 26
#define QPNP_WLED_BOOST_DUTY_MAX_NS 156
#define QPNP_WLED_DEF_BOOST_DUTY_NS 104
-#define QPNP_WLED_SWITCH_FREQ_MASK 0x70
-#define QPNP_WLED_SWITCH_FREQ_800_KHZ 800
-#define QPNP_WLED_SWITCH_FREQ_1600_KHZ 1600
-#define QPNP_WLED_SWITCH_FREQ_OVERWRITE 0x80
+#define QPNP_WLED_SWITCH_FREQ_MASK GENMASK(3, 0)
+#define QPNP_WLED_SWITCH_FREQ_OVERWRITE BIT(7)
#define QPNP_WLED_OVP_MASK GENMASK(1, 0)
#define QPNP_WLED_TEST4_EN_DEB_BYPASS_ILIM_BIT BIT(6)
#define QPNP_WLED_TEST4_EN_SH_FOR_SS_BIT BIT(5)
@@ -125,6 +122,10 @@
#define QPNP_WLED_SC_FAULT_BIT BIT(2)
#define QPNP_WLED_OVP_FLT_RT_STS_BIT BIT(1)
+/* QPNP_WLED_SOFTSTART_RAMP_DLY */
+#define SOFTSTART_OVERWRITE_BIT BIT(7)
+#define SOFTSTART_RAMP_DELAY_MASK GENMASK(2, 0)
+
/* sink registers */
#define QPNP_WLED_CURR_SINK_REG(b) (b + 0x46)
#define QPNP_WLED_SYNC_REG(b) (b + 0x47)
@@ -404,6 +405,7 @@
bool ovp_irq_disabled;
bool auto_calib_enabled;
bool auto_calib_done;
+ bool module_dis_perm;
ktime_t start_ovp_fault_time;
};
@@ -600,6 +602,9 @@
{
int rc;
+ if (wled->module_dis_perm)
+ return 0;
+
rc = qpnp_wled_masked_write_reg(wled,
QPNP_WLED_MODULE_EN_REG(base_addr),
QPNP_WLED_MODULE_EN_MASK,
@@ -1098,7 +1103,7 @@
return 0;
}
-#define AUTO_CALIB_BRIGHTNESS 16
+#define AUTO_CALIB_BRIGHTNESS 200
static int wled_auto_calibrate(struct qpnp_wled *wled)
{
int rc = 0, i;
@@ -1128,6 +1133,17 @@
goto failed_calib;
}
+ if (wled->en_cabc) {
+ for (i = 0; i < wled->max_strings; i++) {
+ reg = 0;
+ rc = qpnp_wled_masked_write_reg(wled,
+ QPNP_WLED_CABC_REG(wled->sink_base, i),
+ QPNP_WLED_CABC_MASK, reg);
+ if (rc < 0)
+ goto failed_calib;
+ }
+ }
+
/* disable all sinks */
rc = qpnp_wled_write_reg(wled,
QPNP_WLED_CURR_SINK_REG(wled->sink_base), 0);
@@ -1136,21 +1152,6 @@
goto failed_calib;
}
- rc = qpnp_wled_masked_write_reg(wled,
- QPNP_WLED_MODULE_EN_REG(wled->ctrl_base),
- QPNP_WLED_MODULE_EN_MASK,
- QPNP_WLED_MODULE_EN_MASK);
- if (rc < 0) {
- pr_err("Failed to enable WLED module rc=%d\n", rc);
- goto failed_calib;
- }
- /*
- * Delay for the WLED soft-start, check the OVP status
- * only after soft-start is complete
- */
- usleep_range(QPNP_WLED_SOFT_START_DLY_US,
- QPNP_WLED_SOFT_START_DLY_US + 1000);
-
/* iterate through the strings one by one */
for (i = 0; i < wled->max_strings; i++) {
sink_test = 1 << (QPNP_WLED_CURR_SINK_SHIFT + i);
@@ -1174,6 +1175,15 @@
goto failed_calib;
}
+ /* Enable the module */
+ rc = qpnp_wled_masked_write_reg(wled,
+ QPNP_WLED_MODULE_EN_REG(wled->ctrl_base),
+ QPNP_WLED_MODULE_EN_MASK, QPNP_WLED_MODULE_EN_MASK);
+ if (rc < 0) {
+ pr_err("Failed to enable WLED module rc=%d\n", rc);
+ goto failed_calib;
+ }
+
/* delay for WLED soft-start */
usleep_range(QPNP_WLED_SOFT_START_DLY_US,
QPNP_WLED_SOFT_START_DLY_US + 1000);
@@ -1190,6 +1200,15 @@
i + 1);
else
sink_valid |= sink_test;
+
+ /* Disable the module */
+ rc = qpnp_wled_masked_write_reg(wled,
+ QPNP_WLED_MODULE_EN_REG(wled->ctrl_base),
+ QPNP_WLED_MODULE_EN_MASK, 0);
+ if (rc < 0) {
+ pr_err("Failed to disable WLED module rc=%d\n", rc);
+ goto failed_calib;
+ }
}
if (sink_valid == sink_config) {
@@ -1203,14 +1222,7 @@
if (!sink_config) {
pr_warn("No valid WLED sinks found\n");
- goto failed_calib;
- }
-
- rc = qpnp_wled_masked_write_reg(wled,
- QPNP_WLED_MODULE_EN_REG(wled->ctrl_base),
- QPNP_WLED_MODULE_EN_MASK, 0);
- if (rc < 0) {
- pr_err("Failed to disable WLED module rc=%d\n", rc);
+ wled->module_dis_perm = true;
goto failed_calib;
}
@@ -1224,6 +1236,15 @@
/* MODULATOR_EN setting for valid sinks */
for (i = 0; i < wled->max_strings; i++) {
+ if (wled->en_cabc) {
+ reg = 1 << QPNP_WLED_CABC_SHIFT;
+ rc = qpnp_wled_masked_write_reg(wled,
+ QPNP_WLED_CABC_REG(wled->sink_base, i),
+ QPNP_WLED_CABC_MASK, reg);
+ if (rc < 0)
+ goto failed_calib;
+ }
+
if (sink_config & (1 << (QPNP_WLED_CURR_SINK_SHIFT + i)))
reg = (QPNP_WLED_MOD_EN << QPNP_WLED_MOD_EN_SHFT);
else
@@ -1456,20 +1477,26 @@
u8 mask = 0, reg = 0;
/* Configure the LOOP COMP GM register */
- if (wled->pmic_rev_id->pmic_subtype == PMI8998_SUBTYPE ||
- wled->pmic_rev_id->pmic_subtype == PM660L_SUBTYPE) {
- if (wled->loop_auto_gm_en)
- reg |= QPNP_WLED_VLOOP_COMP_AUTO_GM_EN;
+ if ((wled->pmic_rev_id->pmic_subtype == PMI8998_SUBTYPE ||
+ wled->pmic_rev_id->pmic_subtype == PM660L_SUBTYPE)) {
+ if (wled->disp_type_amoled) {
+ reg = 0;
+ mask |= QPNP_WLED_VLOOP_COMP_AUTO_GM_EN |
+ QPNP_WLED_VLOOP_COMP_AUTO_GM_THRESH_MASK;
+ } else {
+ if (wled->loop_auto_gm_en)
+ reg |= QPNP_WLED_VLOOP_COMP_AUTO_GM_EN;
- if (wled->loop_auto_gm_thresh >
- QPNP_WLED_LOOP_AUTO_GM_THRESH_MAX)
- wled->loop_auto_gm_thresh =
- QPNP_WLED_LOOP_AUTO_GM_THRESH_MAX;
+ if (wled->loop_auto_gm_thresh >
+ QPNP_WLED_LOOP_AUTO_GM_THRESH_MAX)
+ wled->loop_auto_gm_thresh =
+ QPNP_WLED_LOOP_AUTO_GM_THRESH_MAX;
- reg |= wled->loop_auto_gm_thresh <<
- QPNP_WLED_VLOOP_COMP_AUTO_GM_THRESH_SHIFT;
- mask |= QPNP_WLED_VLOOP_COMP_AUTO_GM_EN |
- QPNP_WLED_VLOOP_COMP_AUTO_GM_THRESH_MASK;
+ reg |= wled->loop_auto_gm_thresh <<
+ QPNP_WLED_VLOOP_COMP_AUTO_GM_THRESH_SHIFT;
+ mask |= QPNP_WLED_VLOOP_COMP_AUTO_GM_EN |
+ QPNP_WLED_VLOOP_COMP_AUTO_GM_THRESH_MASK;
+ }
}
if (wled->loop_ea_gm < QPNP_WLED_LOOP_EA_GM_MIN)
@@ -1762,8 +1789,17 @@
/* Configure the Soft start Ramp delay: for AMOLED - 0,for LCD - 2 */
reg = (wled->disp_type_amoled) ? 0 : 2;
- rc = qpnp_wled_write_reg(wled,
- QPNP_WLED_SOFTSTART_RAMP_DLY(wled->ctrl_base), reg);
+ mask = SOFTSTART_RAMP_DELAY_MASK;
+ if ((wled->pmic_rev_id->pmic_subtype == PMI8998_SUBTYPE ||
+ wled->pmic_rev_id->pmic_subtype == PM660L_SUBTYPE)
+ && wled->disp_type_amoled) {
+ reg |= SOFTSTART_OVERWRITE_BIT;
+ mask |= SOFTSTART_OVERWRITE_BIT;
+ }
+
+ rc = qpnp_wled_masked_write_reg(wled,
+ QPNP_WLED_SOFTSTART_RAMP_DLY(wled->ctrl_base),
+ mask, reg);
if (rc)
return rc;
@@ -1785,21 +1821,24 @@
return rc;
/* Configure the SWITCHING FREQ register */
- if (wled->switch_freq_khz == QPNP_WLED_SWITCH_FREQ_1600_KHZ)
- temp = QPNP_WLED_SWITCH_FREQ_1600_KHZ_CODE;
+ if (wled->switch_freq_khz == 1600)
+ reg = QPNP_WLED_SWITCH_FREQ_1600_KHZ_CODE;
else
- temp = QPNP_WLED_SWITCH_FREQ_800_KHZ_CODE;
+ reg = QPNP_WLED_SWITCH_FREQ_800_KHZ_CODE;
- rc = qpnp_wled_read_reg(wled,
- QPNP_WLED_SWITCH_FREQ_REG(wled->ctrl_base), ®);
+ /*
+ * Do not set the overwrite bit when switching frequency is selected
+ * for AMOLED. This register is in logic reset block which can cause
+ * the value to be overwritten during module enable/disable.
+ */
+ mask = QPNP_WLED_SWITCH_FREQ_MASK | QPNP_WLED_SWITCH_FREQ_OVERWRITE;
+ if (!wled->disp_type_amoled)
+ reg |= QPNP_WLED_SWITCH_FREQ_OVERWRITE;
+
+ rc = qpnp_wled_masked_write_reg(wled,
+ QPNP_WLED_SWITCH_FREQ_REG(wled->ctrl_base), mask, reg);
if (rc < 0)
return rc;
- reg &= QPNP_WLED_SWITCH_FREQ_MASK;
- reg |= (temp | QPNP_WLED_SWITCH_FREQ_OVERWRITE);
- rc = qpnp_wled_write_reg(wled,
- QPNP_WLED_SWITCH_FREQ_REG(wled->ctrl_base), reg);
- if (rc)
- return rc;
rc = qpnp_wled_ovp_config(wled);
if (rc < 0) {
@@ -2108,8 +2147,11 @@
return rc;
}
- wled->loop_comp_res_kohm =
- QPNP_WLED_LOOP_COMP_RES_DFLT_AMOLED_KOHM;
+ wled->loop_comp_res_kohm = 320;
+ if (wled->pmic_rev_id->pmic_subtype == PMI8998_SUBTYPE ||
+ wled->pmic_rev_id->pmic_subtype == PM660L_SUBTYPE)
+ wled->loop_comp_res_kohm = 300;
+
rc = of_property_read_u32(pdev->dev.of_node,
"qcom,loop-comp-res-kohm", &temp_val);
if (!rc) {
@@ -2237,7 +2279,7 @@
return rc;
}
- wled->switch_freq_khz = QPNP_WLED_SWITCH_FREQ_800_KHZ;
+ wled->switch_freq_khz = wled->disp_type_amoled ? 1600 : 800;
rc = of_property_read_u32(pdev->dev.of_node,
"qcom,switch-freq-khz", &temp_val);
if (!rc) {
@@ -2442,7 +2484,7 @@
wled->pmic_rev_id->pmic_subtype, wled->pmic_rev_id->rev4);
prop = of_get_address_by_name(pdev->dev.of_node, QPNP_WLED_SINK_BASE,
- 0, 0);
+ NULL, NULL);
if (!prop) {
dev_err(&pdev->dev, "Couldnt find sink's addr rc %d\n", rc);
return rc;
@@ -2450,7 +2492,7 @@
wled->sink_base = be32_to_cpu(*prop);
prop = of_get_address_by_name(pdev->dev.of_node, QPNP_WLED_CTRL_BASE,
- 0, 0);
+ NULL, NULL);
if (!prop) {
dev_err(&pdev->dev, "Couldnt find ctrl's addr rc = %d\n", rc);
return rc;
diff --git a/drivers/md/dm-req-crypt.c b/drivers/md/dm-req-crypt.c
index 3ffe7e5..b4bdbc0 100644
--- a/drivers/md/dm-req-crypt.c
+++ b/drivers/md/dm-req-crypt.c
@@ -217,6 +217,7 @@
* this should never happen
*/
WARN_ON(1);
+ return;
}
} else {
DMERR("%s io is NULL\n", __func__);
@@ -225,6 +226,7 @@
* this should never happen
*/
WARN_ON(1);
+ return;
}
atomic_dec(&io->pending);
@@ -253,6 +255,7 @@
* this should never happen
*/
WARN_ON(1);
+ return;
}
} else {
DMERR("%s io is NULL\n",
@@ -262,6 +265,7 @@
* this should never happen
*/
WARN_ON(1);
+ return;
}
/* Should never get here if io or Clone is NULL */
@@ -445,6 +449,7 @@
if (!io || !io->cloned_request) {
DMERR("%s io is invalid\n", __func__);
WARN_ON(1); /* should not happen */
+ return;
}
clone = io->cloned_request;
@@ -696,6 +701,7 @@
if (!io || !io->cloned_request) {
DMERR("%s io is invalid\n", __func__);
WARN_ON(1); /* should not happen */
+ return;
}
clone = io->cloned_request;
@@ -740,6 +746,7 @@
err = DM_REQ_CRYPT_ERROR;
/* If io is not populated this should not be called */
WARN_ON(1);
+ return;
}
req = skcipher_request_alloc(tfm, GFP_KERNEL);
if (!req) {
diff --git a/drivers/media/dvb-core/dvb_demux.c b/drivers/media/dvb-core/dvb_demux.c
index 474684f..b2459ff 100644
--- a/drivers/media/dvb-core/dvb_demux.c
+++ b/drivers/media/dvb-core/dvb_demux.c
@@ -347,6 +347,9 @@
buf_size)
break;
+ if (current_size >= DVB_DMX_MAX_PATTERN_LEN)
+ break;
+
if (dvb_dmx_patterns_match(
(patterns[j]->pattern + current_size),
buf, (patterns[j]->mask + current_size),
diff --git a/drivers/media/platform/msm/broadcast/tspp.c b/drivers/media/platform/msm/broadcast/tspp.c
index 2c90e47..be7f4a1 100644
--- a/drivers/media/platform/msm/broadcast/tspp.c
+++ b/drivers/media/platform/msm/broadcast/tspp.c
@@ -36,7 +36,6 @@
#include <linux/regulator/consumer.h>
#include <dt-bindings/regulator/qcom,rpmh-regulator.h>
#include <linux/msm-sps.h> /* BAM stuff */
-#include <linux/wakelock.h> /* Locking functions */
#include <linux/timer.h> /* Timer services */
#include <linux/jiffies.h> /* Jiffies counter */
#include <linux/qcom_tspp.h>
diff --git a/drivers/media/platform/msm/camera/cam_core/cam_context.c b/drivers/media/platform/msm/camera/cam_core/cam_context.c
index 8f625ae..bfa1bdd 100644
--- a/drivers/media/platform/msm/camera/cam_core/cam_context.c
+++ b/drivers/media/platform/msm/camera/cam_core/cam_context.c
@@ -36,6 +36,26 @@
return rc;
}
+int cam_context_shutdown(struct cam_context *ctx)
+{
+ int rc = 0;
+
+ if (ctx->state_machine[ctx->state].ioctl_ops.stop_dev) {
+ rc = ctx->state_machine[ctx->state].ioctl_ops.stop_dev(
+ ctx, NULL);
+ if (rc < 0)
+ CAM_ERR(CAM_CORE, "Error while dev stop %d", rc);
+ }
+ if (ctx->state_machine[ctx->state].ioctl_ops.release_dev) {
+ rc = ctx->state_machine[ctx->state].ioctl_ops.release_dev(
+ ctx, NULL);
+ if (rc < 0)
+ CAM_ERR(CAM_CORE, "Error while dev release %d", rc);
+ }
+
+ return rc;
+}
+
int cam_context_handle_crm_get_dev_info(struct cam_context *ctx,
struct cam_req_mgr_device_info *info)
{
@@ -334,7 +354,7 @@
}
memset(ctx, 0, sizeof(*ctx));
-
+ ctx->dev_hdl = -1;
INIT_LIST_HEAD(&ctx->list);
mutex_init(&ctx->ctx_mutex);
spin_lock_init(&ctx->lock);
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 d87c984..10285cb 100644
--- a/drivers/media/platform/msm/camera/cam_core/cam_context.h
+++ b/drivers/media/platform/msm/camera/cam_core/cam_context.h
@@ -186,12 +186,22 @@
};
/**
+ * cam_context_shutdown()
+ *
+ * @brief: Calls while device close or shutdown
+ *
+ * @ctx: Object pointer for cam_context
+ *
+ */
+int cam_context_shutdown(struct cam_context *ctx);
+
+/**
* cam_context_handle_crm_get_dev_info()
*
* @brief: Handle get device information command
*
- * @ctx: Object pointer for cam_context
- * @info: Device information returned
+ * @ctx: Object pointer for cam_context
+ * @info: Device information returned
*
*/
int cam_context_handle_crm_get_dev_info(struct cam_context *ctx,
@@ -202,8 +212,8 @@
*
* @brief: Handle link command
*
- * @ctx: Object pointer for cam_context
- * @link: Link command payload
+ * @ctx: Object pointer for cam_context
+ * @link: Link command payload
*
*/
int cam_context_handle_crm_link(struct cam_context *ctx,
@@ -214,8 +224,8 @@
*
* @brief: Handle unlink command
*
- * @ctx: Object pointer for cam_context
- * @unlink: Unlink command payload
+ * @ctx: Object pointer for cam_context
+ * @unlink: Unlink command payload
*
*/
int cam_context_handle_crm_unlink(struct cam_context *ctx,
@@ -226,8 +236,8 @@
*
* @brief: Handle apply request command
*
- * @ctx: Object pointer for cam_context
- * @apply: Apply request command payload
+ * @ctx: Object pointer for cam_context
+ * @apply: Apply request command payload
*
*/
int cam_context_handle_crm_apply_req(struct cam_context *ctx,
@@ -238,8 +248,8 @@
*
* @brief: Handle flush request command
*
- * @ctx: Object pointer for cam_context
- * @apply: Flush request command payload
+ * @ctx: Object pointer for cam_context
+ * @apply: Flush request command payload
*
*/
int cam_context_handle_crm_flush_req(struct cam_context *ctx,
@@ -250,8 +260,8 @@
*
* @brief: Handle acquire device command
*
- * @ctx: Object pointer for cam_context
- * @cmd: Acquire device command payload
+ * @ctx: Object pointer for cam_context
+ * @cmd: Acquire device command payload
*
*/
int cam_context_handle_acquire_dev(struct cam_context *ctx,
@@ -262,8 +272,8 @@
*
* @brief: Handle release device command
*
- * @ctx: Object pointer for cam_context
- * @cmd: Release device command payload
+ * @ctx: Object pointer for cam_context
+ * @cmd: Release device command payload
*
*/
int cam_context_handle_release_dev(struct cam_context *ctx,
@@ -274,8 +284,8 @@
*
* @brief: Handle config device command
*
- * @ctx: Object pointer for cam_context
- * @cmd: Config device command payload
+ * @ctx: Object pointer for cam_context
+ * @cmd: Config device command payload
*
*/
int cam_context_handle_config_dev(struct cam_context *ctx,
@@ -286,8 +296,8 @@
*
* @brief: Handle start device command
*
- * @ctx: Object pointer for cam_context
- * @cmd: Start device command payload
+ * @ctx: Object pointer for cam_context
+ * @cmd: Start device command payload
*
*/
int cam_context_handle_start_dev(struct cam_context *ctx,
@@ -298,8 +308,8 @@
*
* @brief: Handle stop device command
*
- * @ctx: Object pointer for cam_context
- * @cmd: Stop device command payload
+ * @ctx: Object pointer for cam_context
+ * @cmd: Stop device command payload
*
*/
int cam_context_handle_stop_dev(struct cam_context *ctx,
@@ -310,7 +320,7 @@
*
* @brief: Camera context deinitialize function
*
- * @ctx: Object pointer for cam_context
+ * @ctx: Object pointer for cam_context
*
*/
int cam_context_deinit(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 01c629d..714891e 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
@@ -180,9 +180,9 @@
ctx->hw_mgr_intf->hw_release(ctx->hw_mgr_intf->hw_mgr_priv, &arg);
ctx->ctxt_to_hw_map = NULL;
- ctx->session_hdl = 0;
- ctx->dev_hdl = 0;
- ctx->link_hdl = 0;
+ ctx->session_hdl = -1;
+ ctx->dev_hdl = -1;
+ ctx->link_hdl = -1;
while (!list_empty(&ctx->active_req_list)) {
req = list_first_entry(&ctx->active_req_list,
diff --git a/drivers/media/platform/msm/camera/cam_core/cam_node.c b/drivers/media/platform/msm/camera/cam_core/cam_node.c
index 6e48c6a..11e9290 100644
--- a/drivers/media/platform/msm/camera/cam_core/cam_node.c
+++ b/drivers/media/platform/msm/camera/cam_core/cam_node.c
@@ -17,12 +17,6 @@
#include "cam_node.h"
#include "cam_trace.h"
#include "cam_debug_util.h"
-static void __cam_node_handle_shutdown(struct cam_node *node)
-{
- if (node->hw_mgr_intf.hw_close)
- node->hw_mgr_intf.hw_close(node->hw_mgr_intf.hw_mgr_priv,
- NULL);
-}
static int __cam_node_handle_query_cap(struct cam_node *node,
struct cam_query_cap_cmd *query)
@@ -307,6 +301,29 @@
return 0;
}
+int cam_node_shutdown(struct cam_node *node)
+{
+ int i = 0;
+
+ if (!node)
+ return -EINVAL;
+
+ for (i = 0; i < node->ctx_size; i++) {
+ if (node->ctx_list[i].dev_hdl >= 0) {
+ cam_context_shutdown(&(node->ctx_list[i]));
+ cam_destroy_device_hdl(node->ctx_list[i].dev_hdl);
+ list_add_tail(&(node->ctx_list[i].list),
+ &node->free_ctx_list);
+ }
+ }
+
+ if (node->hw_mgr_intf.hw_close)
+ node->hw_mgr_intf.hw_close(node->hw_mgr_intf.hw_mgr_priv,
+ NULL);
+
+ return 0;
+}
+
int cam_node_init(struct cam_node *node, struct cam_hw_mgr_intf *hw_mgr_intf,
struct cam_context *ctx_list, uint32_t ctx_size, char *name)
{
@@ -456,9 +473,6 @@
}
break;
}
- case CAM_SD_SHUTDOWN:
- __cam_node_handle_shutdown(node);
- break;
default:
CAM_ERR(CAM_CORE, "Unknown op code %d", cmd->op_code);
rc = -EINVAL;
diff --git a/drivers/media/platform/msm/camera/cam_core/cam_node.h b/drivers/media/platform/msm/camera/cam_core/cam_node.h
index 6e4a641..02e153d 100644
--- a/drivers/media/platform/msm/camera/cam_core/cam_node.h
+++ b/drivers/media/platform/msm/camera/cam_core/cam_node.h
@@ -73,6 +73,16 @@
int cam_node_deinit(struct cam_node *node);
/**
+ * cam_node_shutdown()
+ *
+ * @brief: Shutdowns/Closes the cam node.
+ *
+ * @node: Cam_node pointer
+ *
+ */
+int cam_node_shutdown(struct cam_node *node);
+
+/**
* cam_node_init()
*
* @brief: Initialization function for the Node interface.
diff --git a/drivers/media/platform/msm/camera/cam_cpas/cam_cpas_hw.c b/drivers/media/platform/msm/camera/cam_cpas/cam_cpas_hw.c
index 6bf81af..8518862 100644
--- a/drivers/media/platform/msm/camera/cam_cpas/cam_cpas_hw.c
+++ b/drivers/media/platform/msm/camera/cam_cpas/cam_cpas_hw.c
@@ -1302,6 +1302,27 @@
return rc;
}
+static int cam_cpas_util_get_hw_version(struct platform_device *pdev,
+ struct cam_hw_soc_info *soc_info)
+{
+ struct device_node *of_node = pdev->dev.of_node;
+ int rc;
+
+ soc_info->hw_version = 0;
+
+ rc = of_property_read_u32(of_node,
+ "qcom,cpas-hw-ver", &soc_info->hw_version);
+
+ CAM_DBG(CAM_CPAS, "CPAS HW VERSION %x", soc_info->hw_version);
+
+ if (rc) {
+ CAM_ERR(CAM_CPAS, "failed to get CPAS HW Version rc=%d", rc);
+ return -EINVAL;
+ }
+
+ return rc;
+}
+
int cam_cpas_hw_probe(struct platform_device *pdev,
struct cam_hw_intf **hw_intf)
{
@@ -1442,6 +1463,10 @@
if (rc)
goto axi_cleanup;
+ rc = cam_cpas_util_get_hw_version(pdev, &cpas_hw->soc_info);
+ if (rc)
+ goto axi_cleanup;
+
*hw_intf = cpas_hw_intf;
return 0;
diff --git a/drivers/media/platform/msm/camera/cam_cpas/cam_cpas_intf.c b/drivers/media/platform/msm/camera/cam_cpas/cam_cpas_intf.c
index 3846784..0ba3bb2 100644
--- a/drivers/media/platform/msm/camera/cam_cpas/cam_cpas_intf.c
+++ b/drivers/media/platform/msm/camera/cam_cpas/cam_cpas_intf.c
@@ -50,6 +50,33 @@
static struct cam_cpas_intf *g_cpas_intf;
+int cam_cpas_get_cpas_hw_version(uint32_t *hw_version)
+{
+ struct cam_hw_info *cpas_hw = NULL;
+
+ if (!CAM_CPAS_INTF_INITIALIZED()) {
+ CAM_ERR(CAM_CPAS, "cpas intf not initialized");
+ return -ENODEV;
+ }
+
+ if (!hw_version) {
+ CAM_ERR(CAM_CPAS, "invalid input %pK", hw_version);
+ return -EINVAL;
+ }
+
+ cpas_hw = (struct cam_hw_info *) g_cpas_intf->hw_intf->hw_priv;
+
+ *hw_version = cpas_hw->soc_info.hw_version;
+
+ if (*hw_version == CAM_CPAS_TITAN_NONE) {
+ CAM_DBG(CAM_CPAS, "Didn't find a valid HW Version %d",
+ *hw_version);
+ }
+
+ return 0;
+}
+
+
int cam_cpas_get_hw_info(uint32_t *camera_family,
struct cam_hw_version *camera_version,
struct cam_hw_version *cpas_version)
diff --git a/drivers/media/platform/msm/camera/cam_cpas/cpas_top/cam_cpastop_hw.c b/drivers/media/platform/msm/camera/cam_cpas/cpas_top/cam_cpastop_hw.c
index e3d46df..4b0cc74 100644
--- a/drivers/media/platform/msm/camera/cam_cpas/cpas_top/cam_cpastop_hw.c
+++ b/drivers/media/platform/msm/camera/cam_cpas/cpas_top/cam_cpastop_hw.c
@@ -372,6 +372,8 @@
&camnoc_info->specific[i].safe_lut);
cam_cpas_util_reg_update(cpas_hw, CAM_CPAS_REG_CAMNOC,
&camnoc_info->specific[i].ubwc_ctl);
+ cam_cpas_util_reg_update(cpas_hw, CAM_CPAS_REG_CAMNOC,
+ &camnoc_info->specific[i].flag_out_set0_low);
}
}
diff --git a/drivers/media/platform/msm/camera/cam_cpas/cpas_top/cam_cpastop_hw.h b/drivers/media/platform/msm/camera/cam_cpas/cpas_top/cam_cpastop_hw.h
index d5bb363..e3639a6 100644
--- a/drivers/media/platform/msm/camera/cam_cpas/cpas_top/cam_cpastop_hw.h
+++ b/drivers/media/platform/msm/camera/cam_cpas/cpas_top/cam_cpastop_hw.h
@@ -110,6 +110,7 @@
struct cam_cpas_reg danger_lut;
struct cam_cpas_reg safe_lut;
struct cam_cpas_reg ubwc_ctl;
+ struct cam_cpas_reg flag_out_set0_low;
};
/**
diff --git a/drivers/media/platform/msm/camera/cam_cpas/cpas_top/cpastop_v170_110.h b/drivers/media/platform/msm/camera/cam_cpas/cpas_top/cpastop_v170_110.h
index 918258d..55cb07b 100644
--- a/drivers/media/platform/msm/camera/cam_cpas/cpas_top/cpastop_v170_110.h
+++ b/drivers/media/platform/msm/camera/cam_cpas/cpas_top/cpastop_v170_110.h
@@ -494,8 +494,15 @@
},
{
.port_type = CAM_CAMNOC_ICP,
- .enable = false,
- }
+ .enable = true,
+ .flag_out_set0_low = {
+ .enable = true,
+ .access_type = CAM_REG_TYPE_WRITE,
+ .masked_value = 0,
+ .offset = 0x2088,
+ .value = 0x100000,
+ },
+ },
};
static uint32_t cam_cpas110_slave_error_logger[] = {
diff --git a/drivers/media/platform/msm/camera/cam_cpas/include/cam_cpas_api.h b/drivers/media/platform/msm/camera/cam_cpas/include/cam_cpas_api.h
index 3977b68..aa8b266 100644
--- a/drivers/media/platform/msm/camera/cam_cpas/include/cam_cpas_api.h
+++ b/drivers/media/platform/msm/camera/cam_cpas/include/cam_cpas_api.h
@@ -37,6 +37,20 @@
};
/**
+ * enum cam_cpas_hw_version - Enum for Titan CPAS HW Versions
+ */
+enum cam_cpas_hw_version {
+ CAM_CPAS_TITAN_NONE = 0,
+ CAM_CPAS_TITAN_170_V100 = 0x170100,
+ CAM_CPAS_TITAN_170_V110 = 0x170110,
+ CAM_CPAS_TITAN_170_V120 = 0x170120,
+ CAM_CPAS_TITAN_175_V100 = 0x175100,
+ CAM_CPAS_TITAN_175_V101 = 0x175101,
+ CAM_CPAS_TITAN_MAX
+};
+
+
+/**
* enum cam_camnoc_irq_type - Enum for camnoc irq types
*
* @CAM_CAMNOC_IRQ_SLAVE_ERROR: Each slave port in CAMNOC (3 QSB ports and
@@ -309,4 +323,17 @@
struct cam_hw_version *camera_version,
struct cam_hw_version *cpas_version);
+/**
+ * cam_cpas_get_cpas_hw_version()
+ *
+ * @brief: API to get camera cpas hw version
+ *
+ * @hw_version : Camera cpas hw version
+ *
+ * @return 0 on success.
+ *
+ */
+int cam_cpas_get_cpas_hw_version(
+ uint32_t *hw_version);
+
#endif /* _CAM_CPAS_API_H_ */
diff --git a/drivers/media/platform/msm/camera/cam_fd/cam_fd_dev.c b/drivers/media/platform/msm/camera/cam_fd/cam_fd_dev.c
index 27f5518..260dcd8 100644
--- a/drivers/media/platform/msm/camera/cam_fd/cam_fd_dev.c
+++ b/drivers/media/platform/msm/camera/cam_fd/cam_fd_dev.c
@@ -67,6 +67,7 @@
struct v4l2_subdev_fh *fh)
{
struct cam_fd_dev *fd_dev = &g_fd_dev;
+ struct cam_node *node = v4l2_get_subdevdata(sd);
if (!fd_dev->probe_done) {
CAM_ERR(CAM_FD, "FD Dev not initialized, fd_dev=%pK", fd_dev);
@@ -78,6 +79,13 @@
CAM_DBG(CAM_FD, "FD Subdev open count %d", fd_dev->open_cnt);
mutex_unlock(&fd_dev->lock);
+ if (!node) {
+ CAM_ERR(CAM_FD, "Node ptr is NULL");
+ return -EINVAL;
+ }
+
+ cam_node_shutdown(node);
+
return 0;
}
diff --git a/drivers/media/platform/msm/camera/cam_fd/fd_hw_mgr/fd_hw/cam_fd_hw_core.c b/drivers/media/platform/msm/camera/cam_fd/fd_hw_mgr/fd_hw/cam_fd_hw_core.c
index b8a5685..d9be53d 100644
--- a/drivers/media/platform/msm/camera/cam_fd/fd_hw_mgr/fd_hw/cam_fd_hw_core.c
+++ b/drivers/media/platform/msm/camera/cam_fd/fd_hw_mgr/fd_hw/cam_fd_hw_core.c
@@ -112,6 +112,9 @@
/* Before triggering reset to HW, clear the reset complete */
reinit_completion(&fd_core->reset_complete);
+ cam_fd_soc_register_write(soc_info, CAM_FD_REG_CORE,
+ hw_static_info->core_regs.control, 0x1);
+
if (hw_static_info->enable_errata_wa.single_irq_only) {
cam_fd_soc_register_write(soc_info, CAM_FD_REG_WRAPPER,
hw_static_info->wrapper_regs.irq_mask,
@@ -126,9 +129,6 @@
if (time_left <= 0)
CAM_WARN(CAM_FD, "HW reset timeout time_left=%d", time_left);
- cam_fd_soc_register_write(soc_info, CAM_FD_REG_CORE,
- hw_static_info->core_regs.control, 0x1);
-
CAM_DBG(CAM_FD, "FD Wrapper SW Sync Reset complete");
return 0;
@@ -424,9 +424,10 @@
struct cam_fd_hw_req_private *req_private;
uint32_t base, face_cnt;
uint32_t *buffer;
+ unsigned long flags;
int i;
- spin_lock(&fd_core->spin_lock);
+ spin_lock_irqsave(&fd_core->spin_lock, flags);
if ((fd_core->core_state != CAM_FD_CORE_STATE_IDLE) ||
(fd_core->results_valid == false) ||
!fd_core->hw_req_private) {
@@ -434,12 +435,12 @@
"Invalid state for results state=%d, results=%d %pK",
fd_core->core_state, fd_core->results_valid,
fd_core->hw_req_private);
- spin_unlock(&fd_core->spin_lock);
+ spin_unlock_irqrestore(&fd_core->spin_lock, flags);
return -EINVAL;
}
fd_core->core_state = CAM_FD_CORE_STATE_READING_RESULTS;
req_private = fd_core->hw_req_private;
- spin_unlock(&fd_core->spin_lock);
+ spin_unlock_irqrestore(&fd_core->spin_lock, flags);
/*
* Copy the register value as is into output buffers.
@@ -511,10 +512,10 @@
}
}
- spin_lock(&fd_core->spin_lock);
+ spin_lock_irqsave(&fd_core->spin_lock, flags);
fd_core->hw_req_private = NULL;
fd_core->core_state = CAM_FD_CORE_STATE_IDLE;
- spin_unlock(&fd_core->spin_lock);
+ spin_unlock_irqrestore(&fd_core->spin_lock, flags);
return 0;
}
@@ -776,6 +777,9 @@
{
struct cam_hw_info *fd_hw = (struct cam_hw_info *)hw_priv;
struct cam_fd_core *fd_core;
+ struct cam_fd_hw_static_info *hw_static_info;
+ struct cam_hw_soc_info *soc_info;
+ unsigned long flags;
int rc;
if (!fd_hw) {
@@ -784,18 +788,23 @@
}
fd_core = (struct cam_fd_core *)fd_hw->core_info;
+ hw_static_info = fd_core->hw_static_info;
+ soc_info = &fd_hw->soc_info;
- spin_lock(&fd_core->spin_lock);
+ spin_lock_irqsave(&fd_core->spin_lock, flags);
if (fd_core->core_state == CAM_FD_CORE_STATE_RESET_PROGRESS) {
CAM_ERR(CAM_FD, "Reset not allowed in %d state",
fd_core->core_state);
- spin_unlock(&fd_core->spin_lock);
+ spin_unlock_irqrestore(&fd_core->spin_lock, flags);
return -EINVAL;
}
fd_core->results_valid = false;
fd_core->core_state = CAM_FD_CORE_STATE_RESET_PROGRESS;
- spin_unlock(&fd_core->spin_lock);
+ spin_unlock_irqrestore(&fd_core->spin_lock, flags);
+
+ cam_fd_soc_register_write(soc_info, CAM_FD_REG_WRAPPER,
+ hw_static_info->wrapper_regs.cgc_disable, 0x1);
rc = cam_fd_hw_util_fdwrapper_halt(fd_hw);
if (rc) {
@@ -809,9 +818,12 @@
return rc;
}
- spin_lock(&fd_core->spin_lock);
+ cam_fd_soc_register_write(soc_info, CAM_FD_REG_WRAPPER,
+ hw_static_info->wrapper_regs.cgc_disable, 0x0);
+
+ spin_lock_irqsave(&fd_core->spin_lock, flags);
fd_core->core_state = CAM_FD_CORE_STATE_IDLE;
- spin_unlock(&fd_core->spin_lock);
+ spin_unlock_irqrestore(&fd_core->spin_lock, flags);
return rc;
}
@@ -824,6 +836,7 @@
struct cam_fd_hw_cmd_start_args *start_args =
(struct cam_fd_hw_cmd_start_args *)hw_start_args;
struct cam_fd_ctx_hw_private *ctx_hw_private;
+ unsigned long flags;
int rc;
if (!hw_priv || !start_args) {
@@ -841,11 +854,11 @@
fd_core = (struct cam_fd_core *)fd_hw->core_info;
hw_static_info = fd_core->hw_static_info;
- spin_lock(&fd_core->spin_lock);
+ spin_lock_irqsave(&fd_core->spin_lock, flags);
if (fd_core->core_state != CAM_FD_CORE_STATE_IDLE) {
CAM_ERR(CAM_FD, "Cannot start in %d state",
fd_core->core_state);
- spin_unlock(&fd_core->spin_lock);
+ spin_unlock_irqrestore(&fd_core->spin_lock, flags);
return -EINVAL;
}
@@ -858,7 +871,7 @@
fd_core->hw_req_private = start_args->hw_req_private;
fd_core->core_state = CAM_FD_CORE_STATE_PROCESSING;
fd_core->results_valid = false;
- spin_unlock(&fd_core->spin_lock);
+ spin_unlock_irqrestore(&fd_core->spin_lock, flags);
ctx_hw_private = start_args->ctx_hw_private;
@@ -903,9 +916,9 @@
return 0;
error:
- spin_lock(&fd_core->spin_lock);
+ spin_lock_irqsave(&fd_core->spin_lock, flags);
fd_core->core_state = CAM_FD_CORE_STATE_IDLE;
- spin_unlock(&fd_core->spin_lock);
+ spin_unlock_irqrestore(&fd_core->spin_lock, flags);
return rc;
}
@@ -914,6 +927,9 @@
{
struct cam_hw_info *fd_hw = (struct cam_hw_info *)hw_priv;
struct cam_fd_core *fd_core;
+ struct cam_fd_hw_static_info *hw_static_info;
+ struct cam_hw_soc_info *soc_info;
+ unsigned long flags;
int rc;
if (!fd_hw) {
@@ -922,19 +938,24 @@
}
fd_core = (struct cam_fd_core *)fd_hw->core_info;
+ hw_static_info = fd_core->hw_static_info;
+ soc_info = &fd_hw->soc_info;
- spin_lock(&fd_core->spin_lock);
+ spin_lock_irqsave(&fd_core->spin_lock, flags);
if ((fd_core->core_state == CAM_FD_CORE_STATE_POWERDOWN) ||
(fd_core->core_state == CAM_FD_CORE_STATE_RESET_PROGRESS)) {
CAM_ERR(CAM_FD, "Reset not allowed in %d state",
fd_core->core_state);
- spin_unlock(&fd_core->spin_lock);
+ spin_unlock_irqrestore(&fd_core->spin_lock, flags);
return -EINVAL;
}
fd_core->results_valid = false;
fd_core->core_state = CAM_FD_CORE_STATE_RESET_PROGRESS;
- spin_unlock(&fd_core->spin_lock);
+ spin_unlock_irqrestore(&fd_core->spin_lock, flags);
+
+ cam_fd_soc_register_write(soc_info, CAM_FD_REG_WRAPPER,
+ hw_static_info->wrapper_regs.cgc_disable, 0x1);
rc = cam_fd_hw_util_fdwrapper_halt(fd_hw);
if (rc) {
@@ -949,9 +970,12 @@
return rc;
}
- spin_lock(&fd_core->spin_lock);
+ cam_fd_soc_register_write(soc_info, CAM_FD_REG_WRAPPER,
+ hw_static_info->wrapper_regs.cgc_disable, 0x0);
+
+ spin_lock_irqsave(&fd_core->spin_lock, flags);
fd_core->core_state = CAM_FD_CORE_STATE_IDLE;
- spin_unlock(&fd_core->spin_lock);
+ spin_unlock_irqrestore(&fd_core->spin_lock, flags);
return rc;
}
diff --git a/drivers/media/platform/msm/camera/cam_fd/fd_hw_mgr/fd_hw/cam_fd_hw_core.h b/drivers/media/platform/msm/camera/cam_fd/fd_hw_mgr/fd_hw/cam_fd_hw_core.h
index bdd72af..3d9c5f0 100644
--- a/drivers/media/platform/msm/camera/cam_fd/fd_hw_mgr/fd_hw/cam_fd_hw_core.h
+++ b/drivers/media/platform/msm/camera/cam_fd/fd_hw_mgr/fd_hw/cam_fd_hw_core.h
@@ -30,7 +30,7 @@
#define CAM_FD_IRQ_TO_MASK(irq) (1 << (irq))
#define CAM_FD_MASK_TO_IRQ(mask, irq) ((mask) >> (irq))
-#define CAM_FD_HW_HALT_RESET_TIMEOUT 100
+#define CAM_FD_HW_HALT_RESET_TIMEOUT 750
/**
* enum cam_fd_core_state - FD Core internal states
diff --git a/drivers/media/platform/msm/camera/cam_icp/cam_icp_subdev.c b/drivers/media/platform/msm/camera/cam_icp/cam_icp_subdev.c
index 8a0d5f7..905cc97 100644
--- a/drivers/media/platform/msm/camera/cam_icp/cam_icp_subdev.c
+++ b/drivers/media/platform/msm/camera/cam_icp/cam_icp_subdev.c
@@ -114,7 +114,7 @@
goto end;
}
- rc = hw_mgr_intf->hw_close(hw_mgr_intf->hw_mgr_priv, NULL);
+ rc = cam_node_shutdown(node);
if (rc < 0) {
CAM_ERR(CAM_ICP, "HW close failed");
goto end;
diff --git a/drivers/media/platform/msm/camera/cam_icp/hfi.c b/drivers/media/platform/msm/camera/cam_icp/hfi.c
index a315268..cdb0cfa 100644
--- a/drivers/media/platform/msm/camera/cam_icp/hfi.c
+++ b/drivers/media/platform/msm/camera/cam_icp/hfi.c
@@ -385,10 +385,15 @@
ICP_FLAG_CSR_WAKE_UP_EN | ICP_CSR_EN_CLKGATE_WFI),
icp_base + HFI_REG_A5_CSR_A5_CONTROL);
} else {
+ /* Due to hardware bug in V1 ICP clock gating has to be
+ * disabled, this is supposed to be fixed in V-2. But enabling
+ * the clock gating is causing the firmware hang, hence
+ * disabling the clock gating on both V1 and V2 until the
+ * hardware team root causes this
+ */
cam_io_w((uint32_t)ICP_FLAG_CSR_A5_EN |
ICP_FLAG_CSR_WAKE_UP_EN |
- ((soc_version == SOC_VERSION_HW1) ?
- (ICP_CSR_EN_CLKGATE_WFI) : (0x0)),
+ ICP_CSR_EN_CLKGATE_WFI,
icp_base + HFI_REG_A5_CSR_A5_CONTROL);
}
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 aad7902..16c02d8 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
@@ -151,6 +151,13 @@
{
int i;
+ if (req_isp->num_fence_map_out >= CAM_ISP_CTX_RES_MAX) {
+ CAM_ERR_RATE_LIMIT(CAM_ISP,
+ "Num Resources exceed mMAX %d >= %d ",
+ req_isp->num_fence_map_out, CAM_ISP_CTX_RES_MAX);
+ return;
+ }
+
CAM_ERR_RATE_LIMIT(CAM_ISP,
"Resource Handles that fail to generate buf_done in prev frame");
for (i = 0; i < req_isp->num_fence_map_out; i++) {
@@ -202,27 +209,35 @@
continue;
}
- if (!bubble_state) {
- CAM_DBG(CAM_ISP, "Sync with success: fd 0x%x",
- req_isp->fence_map_out[j].sync_id);
- if (req_isp->fence_map_out[j].sync_id == -1)
- __cam_isp_ctx_handle_buf_done_fail_log(req_isp);
- else
- rc = cam_sync_signal(req_isp->
- fence_map_out[j].sync_id,
- CAM_SYNC_STATE_SIGNALED_SUCCESS);
- if (rc)
- CAM_ERR(CAM_ISP, "Sync failed with rc = %d",
- rc);
+ if (req_isp->fence_map_out[j].sync_id == -1) {
+ __cam_isp_ctx_handle_buf_done_fail_log(req_isp);
+ continue;
+ }
+ if (!bubble_state) {
+ CAM_DBG(CAM_ISP,
+ "Sync with success: req %lld res 0x%x fd 0x%x",
+ req->request_id,
+ req_isp->fence_map_out[j].resource_handle,
+ req_isp->fence_map_out[j].sync_id);
+
+ rc = cam_sync_signal(req_isp->fence_map_out[j].sync_id,
+ CAM_SYNC_STATE_SIGNALED_SUCCESS);
+ if (rc)
+ CAM_DBG(CAM_ISP, "Sync failed with rc = %d",
+ rc);
} else if (!req_isp->bubble_report) {
- CAM_DBG(CAM_ISP, "Sync with failure: fd 0x%x",
- req_isp->fence_map_out[j].sync_id);
+ CAM_DBG(CAM_ISP,
+ "Sync with failure: req %lld res 0x%x fd 0x%x",
+ req->request_id,
+ req_isp->fence_map_out[j].resource_handle,
+ req_isp->fence_map_out[j].sync_id);
+
rc = cam_sync_signal(req_isp->fence_map_out[j].sync_id,
CAM_SYNC_STATE_SIGNALED_ERROR);
if (rc)
CAM_ERR(CAM_ISP, "Sync failed with rc = %d",
- rc);
+ rc);
} else {
/*
* Ignore the buffer done if bubble detect is on
@@ -230,18 +245,31 @@
* bubble detects. But for safety, we just move the
* current active request to the pending list here.
*/
+ CAM_DBG(CAM_ISP,
+ "buf done with bubble state %d recovery %d",
+ bubble_state, req_isp->bubble_report);
list_del_init(&req->list);
list_add(&req->list, &ctx->pending_req_list);
continue;
}
CAM_DBG(CAM_ISP, "req %lld, reset sync id 0x%x",
- req->request_id,
- req_isp->fence_map_out[j].sync_id);
- req_isp->num_acked++;
- req_isp->fence_map_out[j].sync_id = -1;
+ req->request_id,
+ req_isp->fence_map_out[j].sync_id);
+ if (!rc) {
+ req_isp->num_acked++;
+ req_isp->fence_map_out[j].sync_id = -1;
+ }
}
+ if (req_isp->num_acked > req_isp->num_fence_map_out) {
+ /* Should not happen */
+ CAM_ERR(CAM_ISP,
+ "WARNING: req_id %lld num_acked %d > map_out %d",
+ req->request_id, req_isp->num_acked,
+ req_isp->num_fence_map_out);
+ WARN_ON(req_isp->num_acked > req_isp->num_fence_map_out);
+ }
if (req_isp->num_acked == req_isp->num_fence_map_out) {
list_del_init(&req->list);
list_add_tail(&req->list, &ctx->free_req_list);
@@ -779,7 +807,9 @@
{
int rc = 0;
struct cam_ctx_request *req;
+ struct cam_ctx_request *active_req;
struct cam_isp_ctx_req *req_isp;
+ struct cam_isp_ctx_req *active_req_isp;
struct cam_isp_context *ctx_isp;
struct cam_hw_config_args cfg;
@@ -817,11 +847,22 @@
if (ctx_isp->active_req_cnt >= 2) {
CAM_ERR_RATE_LIMIT(CAM_ISP,
- "Reject apply request due to congestion(cnt = %d)",
+ "Reject apply request (id %lld) due to congestion(cnt = %d)",
+ req->request_id,
ctx_isp->active_req_cnt);
- __cam_isp_ctx_handle_buf_done_fail_log(req_isp);
- rc = -EFAULT;
- goto end;
+ if (!list_empty(&ctx->active_req_list)) {
+ active_req = list_first_entry(&ctx->active_req_list,
+ struct cam_ctx_request, list);
+ active_req_isp =
+ (struct cam_isp_ctx_req *) active_req->req_priv;
+ __cam_isp_ctx_handle_buf_done_fail_log(active_req_isp);
+ } else {
+ CAM_ERR_RATE_LIMIT(CAM_ISP,
+ "WARNING: should not happen (cnt = %d) but active_list empty",
+ ctx_isp->active_req_cnt);
+ }
+ rc = -EFAULT;
+ goto end;
}
req_isp->bubble_report = apply->report_if_bubble;
@@ -1212,6 +1253,7 @@
CAM_SYNC_STATE_SIGNALED_ERROR);
}
list_add_tail(&req->list, &ctx->free_req_list);
+ ctx_isp->active_req_cnt--;
}
/* notify reqmgr with sof signal */
@@ -1454,9 +1496,9 @@
ctx_isp->hw_ctx = NULL;
}
- ctx->session_hdl = 0;
- ctx->dev_hdl = 0;
- ctx->link_hdl = 0;
+ ctx->session_hdl = -1;
+ ctx->dev_hdl = -1;
+ ctx->link_hdl = -1;
ctx->ctx_crm_intf = NULL;
ctx_isp->frame_id = 0;
ctx_isp->active_req_cnt = 0;
diff --git a/drivers/media/platform/msm/camera/cam_isp/cam_isp_dev.c b/drivers/media/platform/msm/camera/cam_isp/cam_isp_dev.c
index 2bf7795..bcdba05 100644
--- a/drivers/media/platform/msm/camera/cam_isp/cam_isp_dev.c
+++ b/drivers/media/platform/msm/camera/cam_isp/cam_isp_dev.c
@@ -36,6 +36,25 @@
{}
};
+static int cam_isp_subdev_close(struct v4l2_subdev *sd,
+ struct v4l2_subdev_fh *fh)
+{
+ struct cam_node *node = v4l2_get_subdevdata(sd);
+
+ if (!node) {
+ CAM_ERR(CAM_ISP, "Node ptr is NULL");
+ return -EINVAL;
+ }
+
+ cam_node_shutdown(node);
+
+ return 0;
+}
+
+static const struct v4l2_subdev_internal_ops cam_isp_subdev_internal_ops = {
+ .close = cam_isp_subdev_close,
+};
+
static int cam_isp_dev_remove(struct platform_device *pdev)
{
int rc = 0;
@@ -64,6 +83,7 @@
struct cam_hw_mgr_intf hw_mgr_intf;
struct cam_node *node;
+ g_isp_dev.sd.internal_ops = &cam_isp_subdev_internal_ops;
/* Initialze the v4l2 subdevice first. (create cam_node) */
rc = cam_subdev_probe(&g_isp_dev.sd, pdev, CAM_ISP_DEV_NAME,
CAM_IFE_DEVICE_TYPE);
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 6060278..f7b40a4 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
@@ -778,10 +778,11 @@
uint32_t cid_res_id)
{
int rc = -1;
- int i, j;
+ int i;
struct cam_ife_hw_mgr *ife_hw_mgr;
struct cam_ife_hw_mgr_res *csid_res;
+ struct cam_ife_hw_mgr_res *cid_res;
struct cam_hw_intf *hw_intf;
struct cam_csid_hw_reserve_resource_args csid_acquire;
@@ -800,69 +801,64 @@
csid_acquire.in_port = in_port;
csid_acquire.out_port = in_port->data;
- if (in_port->usage_type)
- csid_acquire.sync_mode = CAM_ISP_HW_SYNC_MASTER;
- else
- csid_acquire.sync_mode = CAM_ISP_HW_SYNC_NONE;
-
-
-
- for (i = 0; i < CAM_IFE_CSID_HW_NUM_MAX; i++) {
- if (!ife_hw_mgr->csid_devices[i])
- continue;
-
- hw_intf = ife_hw_mgr->csid_devices[i];
- rc = hw_intf->hw_ops.reserve(hw_intf->hw_priv, &csid_acquire,
- sizeof(csid_acquire));
- if (rc)
- continue;
- else
- break;
- }
-
- if (i == CAM_IFE_CSID_HW_NUM_MAX) {
- CAM_ERR(CAM_ISP, "Can not acquire ife csid ipp resource");
- goto err;
- }
-
- CAM_DBG(CAM_ISP, "acquired csid(%d) left ipp resource successfully",
- i);
-
csid_res->res_type = CAM_ISP_RESOURCE_PIX_PATH;
csid_res->res_id = CAM_IFE_PIX_PATH_RES_IPP;
csid_res->is_dual_vfe = in_port->usage_type;
- csid_res->hw_res[0] = csid_acquire.node_res;
- csid_res->hw_res[1] = NULL;
- if (csid_res->is_dual_vfe) {
- csid_acquire.sync_mode = CAM_ISP_HW_SYNC_SLAVE;
- csid_acquire.master_idx = csid_res->hw_res[0]->hw_intf->hw_idx;
+ if (in_port->usage_type)
+ csid_res->is_dual_vfe = 1;
+ else {
+ csid_res->is_dual_vfe = 0;
+ csid_acquire.sync_mode = CAM_ISP_HW_SYNC_NONE;
+ }
- for (j = i + 1; j < CAM_IFE_CSID_HW_NUM_MAX; j++) {
- if (!ife_hw_mgr->csid_devices[j])
+ list_for_each_entry(cid_res, &ife_ctx->res_list_ife_cid,
+ list) {
+ if (cid_res->res_id != cid_res_id)
+ continue;
+
+ for (i = 0; i < CAM_ISP_HW_SPLIT_MAX; i++) {
+ if (!cid_res->hw_res[i])
continue;
- hw_intf = ife_hw_mgr->csid_devices[j];
+ csid_acquire.node_res = NULL;
+ if (csid_res->is_dual_vfe) {
+ if (i == CAM_ISP_HW_SPLIT_LEFT)
+ csid_acquire.sync_mode =
+ CAM_ISP_HW_SYNC_MASTER;
+ else
+ csid_acquire.sync_mode =
+ CAM_ISP_HW_SYNC_SLAVE;
+ }
+
+ hw_intf = ife_hw_mgr->csid_devices[
+ cid_res->hw_res[i]->hw_intf->hw_idx];
rc = hw_intf->hw_ops.reserve(hw_intf->hw_priv,
&csid_acquire, sizeof(csid_acquire));
- if (rc)
- continue;
- else
- break;
+ if (rc) {
+ CAM_ERR(CAM_ISP,
+ "Cannot acquire ife csid ipp resource");
+ goto err;
+ }
+
+ csid_res->hw_res[i] = csid_acquire.node_res;
+ CAM_DBG(CAM_ISP,
+ "acquired csid(%s)=%d ipp rsrc successfully",
+ (i == 0) ? "left" : "right",
+ hw_intf->hw_idx);
+
}
- if (j == CAM_IFE_CSID_HW_NUM_MAX) {
+ if (i == CAM_IFE_CSID_HW_NUM_MAX) {
CAM_ERR(CAM_ISP,
- "Can not acquire ife csid rdi resrouce");
+ "Can not acquire ife csid ipp resource");
goto err;
}
- csid_res->hw_res[1] = csid_acquire.node_res;
- CAM_DBG(CAM_ISP,
- "acquired csid(%d)right ipp resrouce successfully", j);
-
+ csid_res->parent = cid_res;
+ cid_res->child[cid_res->num_children++] = csid_res;
}
- csid_res->parent = &ife_ctx->res_list_ife_in;
+
CAM_DBG(CAM_ISP, "acquire res %d", csid_acquire.res_id);
return 0;
@@ -909,7 +905,8 @@
struct cam_ife_hw_mgr *ife_hw_mgr;
struct cam_ife_hw_mgr_res *csid_res;
- struct cam_hw_intf *hw_intf;
+ struct cam_ife_hw_mgr_res *cid_res;
+ struct cam_hw_intf *hw_intf;
struct cam_isp_out_port_info *out_port;
struct cam_csid_hw_reserve_resource_args csid_acquire;
@@ -934,6 +931,7 @@
* between the csid rdi type and out port rdi type
*/
+ memset(&csid_acquire, 0, sizeof(csid_acquire));
csid_acquire.res_id =
cam_ife_hw_mgr_get_ife_csid_rdi_res_type(
out_port->res_type);
@@ -944,37 +942,58 @@
csid_acquire.out_port = out_port;
csid_acquire.sync_mode = CAM_ISP_HW_SYNC_NONE;
- for (j = 0; j < CAM_IFE_CSID_HW_NUM_MAX; j++) {
- if (!ife_hw_mgr->csid_devices[j])
+ list_for_each_entry(cid_res, &ife_ctx->res_list_ife_cid,
+ list) {
+ if (cid_res->res_id != cid_res_id)
continue;
- hw_intf = ife_hw_mgr->csid_devices[j];
- rc = hw_intf->hw_ops.reserve(hw_intf->hw_priv,
- &csid_acquire, sizeof(csid_acquire));
- if (rc)
- continue;
- else
+ for (j = 0; j < CAM_ISP_HW_SPLIT_MAX; j++) {
+ if (!cid_res->hw_res[j])
+ continue;
+
+ csid_acquire.node_res = NULL;
+
+ hw_intf = ife_hw_mgr->csid_devices[
+ cid_res->hw_res[j]->hw_intf->hw_idx];
+ rc = hw_intf->hw_ops.reserve(hw_intf->hw_priv,
+ &csid_acquire, sizeof(csid_acquire));
+ if (rc) {
+ CAM_DBG(CAM_ISP,
+ "CSID Path reserve failed hw=%d rc=%d",
+ hw_intf->hw_idx, rc);
+ continue;
+ }
+
+ /* RDI does not need Dual ISP. Break */
break;
- }
+ }
- if (j == CAM_IFE_CSID_HW_NUM_MAX) {
- CAM_ERR(CAM_ISP,
- "Can not acquire ife csid rdi resource");
- goto err;
- }
+ if (j == CAM_ISP_HW_SPLIT_MAX &&
+ csid_acquire.node_res == NULL) {
+ CAM_ERR(CAM_ISP,
+ "acquire csid rdi rsrc failed, cid %d",
+ cid_res_id);
+ goto err;
+ }
- csid_res->res_type = CAM_ISP_RESOURCE_PIX_PATH;
- csid_res->res_id = csid_acquire.res_id;
- csid_res->is_dual_vfe = 0;
- csid_res->hw_res[0] = csid_acquire.node_res;
- csid_res->hw_res[1] = NULL;
- CAM_DBG(CAM_ISP, "acquire res %d", csid_acquire.res_id);
- csid_res->parent = &ife_ctx->res_list_ife_in;
+ csid_res->res_type = CAM_ISP_RESOURCE_PIX_PATH;
+ csid_res->res_id = csid_acquire.res_id;
+ csid_res->is_dual_vfe = 0;
+ csid_res->hw_res[0] = csid_acquire.node_res;
+ csid_res->hw_res[1] = NULL;
+ CAM_DBG(CAM_ISP, "acquire res %d",
+ csid_acquire.res_id);
+ csid_res->parent = cid_res;
+ cid_res->child[cid_res->num_children++] =
+ csid_res;
+
+ /* Done with cid_res_id. Break */
+ break;
+ }
}
return 0;
err:
- /* resource resources at entry funciton */
return rc;
}
diff --git a/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/hw_utils/cam_isp_packet_parser.c b/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/hw_utils/cam_isp_packet_parser.c
index 5f68f21..8514ab3 100644
--- a/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/hw_utils/cam_isp_packet_parser.c
+++ b/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/hw_utils/cam_isp_packet_parser.c
@@ -513,8 +513,8 @@
for (i = 0; i < prepare->packet->num_io_configs; i++) {
CAM_DBG(CAM_ISP, "======= io config idx %d ============", i);
- CAM_DBG(CAM_ISP, "resource_type:%d fence:%d",
- io_cfg[i].resource_type, io_cfg[i].fence);
+ CAM_DBG(CAM_ISP, "i %d resource_type:%d fence:%d",
+ i, io_cfg[i].resource_type, io_cfg[i].fence);
CAM_DBG(CAM_ISP, "format: %d", io_cfg[i].format);
CAM_DBG(CAM_ISP, "direction %d",
io_cfg[i].direction);
diff --git a/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/hw_utils/irq_controller/cam_irq_controller.c b/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/hw_utils/irq_controller/cam_irq_controller.c
index dcbea8d..c6d5601 100644
--- a/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/hw_utils/irq_controller/cam_irq_controller.c
+++ b/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/hw_utils/irq_controller/cam_irq_controller.c
@@ -613,7 +613,6 @@
i, j, need_th_processing[j]);
}
}
- read_unlock(&controller->rw_lock);
CAM_DBG(CAM_ISP, "unlocked controller %pK name %s rw_lock %pK",
controller, controller->name, &controller->rw_lock);
@@ -632,6 +631,7 @@
&controller->th_list_head[i]);
}
}
+ read_unlock(&controller->rw_lock);
return IRQ_HANDLED;
}
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 7d6e758..cd92035 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
@@ -627,6 +627,12 @@
goto end;
}
+ if (cid_reserv->in_port->res_type == CAM_ISP_IFE_IN_RES_PHY_3 &&
+ csid_hw->hw_intf->hw_idx != 2) {
+ rc = -EINVAL;
+ goto end;
+ }
+
if (csid_hw->csi2_reserve_cnt) {
/* current configure res type should match requested res type */
if (csid_hw->res_type != cid_reserv->in_port->res_type) {
@@ -1247,6 +1253,11 @@
CSID_CSI2_RX_ERROR_LANE3_FIFO_OVERFLOW |
CSID_CSI2_RX_ERROR_CPHY_EOT_RECEPTION |
CSID_CSI2_RX_ERROR_CPHY_SOT_RECEPTION |
+ CSID_CSI2_RX_ERROR_CRC |
+ CSID_CSI2_RX_ERROR_ECC |
+ CSID_CSI2_RX_ERROR_MMAPPED_VC_DT |
+ CSID_CSI2_RX_ERROR_STREAM_UNDERFLOW |
+ CSID_CSI2_RX_ERROR_UNBOUNDED_FRAME |
CSID_CSI2_RX_ERROR_CPHY_PH_CRC;
/* Enable the interrupt based on csid debug info set */
@@ -2539,8 +2550,8 @@
struct cam_ife_csid_hw *csid_hw;
struct cam_hw_soc_info *soc_info;
struct cam_ife_csid_reg_offset *csid_reg;
- uint32_t i, irq_status_top, irq_status_rx, irq_status_ipp = 0,
- irq_status_rdi[4];
+ uint32_t i, irq_status_top, irq_status_rx, irq_status_ipp = 0;
+ uint32_t irq_status_rdi[4] = {0, 0, 0, 0};
uint32_t val;
csid_hw = (struct cam_ife_csid_hw *)data;
@@ -2587,8 +2598,12 @@
cam_io_w_mb(1, soc_info->reg_map[0].mem_base +
csid_reg->cmn_reg->csid_irq_cmd_addr);
+ CAM_DBG(CAM_ISP, "irq_status_top = 0x%x", irq_status_top);
CAM_DBG(CAM_ISP, "irq_status_rx = 0x%x", irq_status_rx);
CAM_DBG(CAM_ISP, "irq_status_ipp = 0x%x", irq_status_ipp);
+ CAM_DBG(CAM_ISP, "irq_status_rdi0= 0x%x", irq_status_rdi[0]);
+ CAM_DBG(CAM_ISP, "irq_status_rdi1= 0x%x", irq_status_rdi[1]);
+ CAM_DBG(CAM_ISP, "irq_status_rdi2= 0x%x", irq_status_rdi[2]);
if (irq_status_top) {
CAM_DBG(CAM_ISP, "CSID global reset complete......Exit");
@@ -2603,35 +2618,55 @@
}
if (irq_status_rx & CSID_CSI2_RX_ERROR_LANE0_FIFO_OVERFLOW) {
- pr_err_ratelimited("CSID:%d lane 0 over flow",
+ CAM_ERR_RATE_LIMIT(CAM_ISP, "CSID:%d lane 0 over flow",
csid_hw->hw_intf->hw_idx);
}
if (irq_status_rx & CSID_CSI2_RX_ERROR_LANE1_FIFO_OVERFLOW) {
- pr_err_ratelimited("CSID:%d lane 1 over flow",
+ CAM_ERR_RATE_LIMIT(CAM_ISP, "CSID:%d lane 1 over flow",
csid_hw->hw_intf->hw_idx);
}
if (irq_status_rx & CSID_CSI2_RX_ERROR_LANE2_FIFO_OVERFLOW) {
- pr_err_ratelimited("CSID:%d lane 2 over flow",
+ CAM_ERR_RATE_LIMIT(CAM_ISP, "CSID:%d lane 2 over flow",
csid_hw->hw_intf->hw_idx);
}
if (irq_status_rx & CSID_CSI2_RX_ERROR_LANE3_FIFO_OVERFLOW) {
- pr_err_ratelimited("CSID:%d lane 3 over flow",
+ CAM_ERR_RATE_LIMIT(CAM_ISP, "CSID:%d lane 3 over flow",
csid_hw->hw_intf->hw_idx);
}
if (irq_status_rx & CSID_CSI2_RX_ERROR_TG_FIFO_OVERFLOW) {
- pr_err_ratelimited("CSID:%d TG OVER FLOW",
+ CAM_ERR_RATE_LIMIT(CAM_ISP, "CSID:%d TG OVER FLOW",
csid_hw->hw_intf->hw_idx);
}
if (irq_status_rx & CSID_CSI2_RX_ERROR_CPHY_EOT_RECEPTION) {
- pr_err_ratelimited("CSID:%d CPHY_EOT_RECEPTION",
+ CAM_ERR_RATE_LIMIT(CAM_ISP, "CSID:%d CPHY_EOT_RECEPTION",
csid_hw->hw_intf->hw_idx);
}
if (irq_status_rx & CSID_CSI2_RX_ERROR_CPHY_SOT_RECEPTION) {
- pr_err_ratelimited("CSID:%d CPHY_SOT_RECEPTION",
+ CAM_ERR_RATE_LIMIT(CAM_ISP, "CSID:%d CPHY_SOT_RECEPTION",
csid_hw->hw_intf->hw_idx);
}
if (irq_status_rx & CSID_CSI2_RX_ERROR_CPHY_PH_CRC) {
- pr_err_ratelimited("CSID:%d CPHY_PH_CRC",
+ CAM_ERR_RATE_LIMIT(CAM_ISP, "CSID:%d CPHY_PH_CRC",
+ csid_hw->hw_intf->hw_idx);
+ }
+ if (irq_status_rx & CSID_CSI2_RX_ERROR_CRC) {
+ CAM_ERR_RATE_LIMIT(CAM_ISP, "CSID:%d ERROR_CRC",
+ csid_hw->hw_intf->hw_idx);
+ }
+ if (irq_status_rx & CSID_CSI2_RX_ERROR_ECC) {
+ CAM_ERR_RATE_LIMIT(CAM_ISP, "CSID:%d ERROR_ECC",
+ csid_hw->hw_intf->hw_idx);
+ }
+ if (irq_status_rx & CSID_CSI2_RX_ERROR_MMAPPED_VC_DT) {
+ CAM_ERR_RATE_LIMIT(CAM_ISP, "CSID:%d MMAPPED_VC_DT",
+ csid_hw->hw_intf->hw_idx);
+ }
+ if (irq_status_rx & CSID_CSI2_RX_ERROR_STREAM_UNDERFLOW) {
+ CAM_ERR_RATE_LIMIT(CAM_ISP, "CSID:%d ERROR_STREAM_UNDERFLOW",
+ csid_hw->hw_intf->hw_idx);
+ }
+ if (irq_status_rx & CSID_CSI2_RX_ERROR_UNBOUNDED_FRAME) {
+ CAM_ERR_RATE_LIMIT(CAM_ISP, "CSID:%d UNBOUNDED_FRAME",
csid_hw->hw_intf->hw_idx);
}
diff --git a/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/cam_vfe_core.c b/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/cam_vfe_core.c
index 77b830c..8e83cb0 100644
--- a/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/cam_vfe_core.c
+++ b/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/cam_vfe_core.c
@@ -34,7 +34,7 @@
static uint32_t camif_irq_reg_mask[CAM_IFE_IRQ_REGISTERS_MAX] = {
0x0003FD1F,
- 0x0FFF7EB3,
+ 0x0FFF7EBC,
};
static uint32_t rdi_irq_reg_mask[CAM_IFE_IRQ_REGISTERS_MAX] = {
@@ -507,8 +507,6 @@
core_info->vfe_top->top_priv, isp_res,
sizeof(struct cam_isp_resource_node));
} else if (isp_res->res_type == CAM_ISP_RESOURCE_VFE_OUT) {
- cam_irq_controller_unsubscribe_irq(
- core_info->vfe_irq_controller, isp_res->irq_handle);
rc = core_info->vfe_bus->hw_ops.stop(isp_res, NULL, 0);
} else {
CAM_ERR(CAM_ISP, "Invalid res type:%d", isp_res->res_type);
diff --git a/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/vfe170/Makefile b/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/vfe170/Makefile
index 77e4eb3..39a5603 100644
--- a/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/vfe170/Makefile
+++ b/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/vfe170/Makefile
@@ -11,4 +11,4 @@
ccflags-y += -Idrivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/vfe_bus
ccflags-y += -Idrivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/vfe_hw
-obj-$(CONFIG_SPECTRA_CAMERA) += cam_vfe170.o
+obj-$(CONFIG_SPECTRA_CAMERA) += cam_vfe170.o cam_vfe_lite170.o
diff --git a/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/vfe170/cam_vfe170.h b/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/vfe170/cam_vfe170.h
index e3a6f7b..5773bbe 100644
--- a/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/vfe170/cam_vfe170.h
+++ b/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/vfe170/cam_vfe170.h
@@ -222,11 +222,12 @@
.comp_error_status = 0x0000206C,
.comp_ovrwr_status = 0x00002070,
.dual_comp_error_status = 0x00002074,
- .dual_comp_error_status = 0x00002078,
+ .dual_comp_ovrwr_status = 0x00002078,
.addr_sync_cfg = 0x0000207C,
.addr_sync_frame_hdr = 0x00002080,
.addr_sync_no_sync = 0x00002084,
},
+ .num_client = 20,
.bus_client_reg = {
/* BUS Client 0 */
{
@@ -707,8 +708,24 @@
.addr_sync_mask = 0x0000209C,
},
},
+ .num_out = 18,
.vfe_out_hw_info = {
{
+ .vfe_out_type = CAM_VFE_BUS_VER2_VFE_OUT_RDI0,
+ .max_width = -1,
+ .max_height = -1,
+ },
+ {
+ .vfe_out_type = CAM_VFE_BUS_VER2_VFE_OUT_RDI1,
+ .max_width = -1,
+ .max_height = -1,
+ },
+ {
+ .vfe_out_type = CAM_VFE_BUS_VER2_VFE_OUT_RDI2,
+ .max_width = -1,
+ .max_height = -1,
+ },
+ {
.vfe_out_type = CAM_VFE_BUS_VER2_VFE_OUT_FULL,
.max_width = 4096,
.max_height = 4096,
@@ -739,21 +756,6 @@
.max_height = -1,
},
{
- .vfe_out_type = CAM_VFE_BUS_VER2_VFE_OUT_RDI0,
- .max_width = -1,
- .max_height = -1,
- },
- {
- .vfe_out_type = CAM_VFE_BUS_VER2_VFE_OUT_RDI1,
- .max_width = -1,
- .max_height = -1,
- },
- {
- .vfe_out_type = CAM_VFE_BUS_VER2_VFE_OUT_RDI2,
- .max_width = -1,
- .max_height = -1,
- },
- {
.vfe_out_type =
CAM_VFE_BUS_VER2_VFE_OUT_STATS_HDR_BE,
.max_width = -1,
diff --git a/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/vfe170/cam_vfe_lite170.c b/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/vfe170/cam_vfe_lite170.c
new file mode 100644
index 0000000..3c8abbf
--- /dev/null
+++ b/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/vfe170/cam_vfe_lite170.c
@@ -0,0 +1,51 @@
+/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/module.h>
+#include "cam_vfe_lite170.h"
+#include "cam_vfe_hw_intf.h"
+#include "cam_vfe_core.h"
+#include "cam_vfe_dev.h"
+
+static const struct of_device_id cam_vfe170_dt_match[] = {
+ {
+ .compatible = "qcom,vfe-lite170",
+ .data = &cam_vfe_lite170_hw_info,
+ },
+ {}
+};
+MODULE_DEVICE_TABLE(of, cam_vfe170_dt_match);
+
+static struct platform_driver cam_vfe170_driver = {
+ .probe = cam_vfe_probe,
+ .remove = cam_vfe_remove,
+ .driver = {
+ .name = "cam_vfe_lite170",
+ .owner = THIS_MODULE,
+ .of_match_table = cam_vfe170_dt_match,
+ },
+};
+
+static int __init cam_vfe170_init_module(void)
+{
+ return platform_driver_register(&cam_vfe170_driver);
+}
+
+static void __exit cam_vfe170_exit_module(void)
+{
+ platform_driver_unregister(&cam_vfe170_driver);
+}
+
+module_init(cam_vfe170_init_module);
+module_exit(cam_vfe170_exit_module);
+MODULE_DESCRIPTION("CAM VFE170 driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/vfe170/cam_vfe_lite170.h b/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/vfe170/cam_vfe_lite170.h
new file mode 100644
index 0000000..2f95feb
--- /dev/null
+++ b/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/vfe170/cam_vfe_lite170.h
@@ -0,0 +1,336 @@
+/* Copyright (c) 2017, 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 _CAM_VFE_LITE170_H_
+#define _CAM_VFE_LITE170_H_
+
+#include "cam_vfe_bus_ver2.h"
+#include "cam_irq_controller.h"
+#include "cam_vfe_top_ver2.h"
+#include "cam_vfe_core.h"
+
+static struct cam_irq_register_set vfe170_top_irq_reg_set[2] = {
+ {
+ .mask_reg_offset = 0x0000005C,
+ .clear_reg_offset = 0x00000064,
+ .status_reg_offset = 0x0000006C,
+ },
+ {
+ .mask_reg_offset = 0x00000060,
+ .clear_reg_offset = 0x00000068,
+ .status_reg_offset = 0x00000070,
+ },
+};
+
+static struct cam_irq_controller_reg_info vfe170_top_irq_reg_info = {
+ .num_registers = 2,
+ .irq_reg_set = vfe170_top_irq_reg_set,
+ .global_clear_offset = 0x00000058,
+ .global_clear_bitmask = 0x00000001,
+};
+
+static struct cam_vfe_top_ver2_reg_offset_common vfe170_top_common_reg = {
+ .hw_version = 0x00000000,
+ .hw_capability = 0x00000004,
+ .lens_feature = 0x00000008,
+ .stats_feature = 0x0000000C,
+ .color_feature = 0x00000010,
+ .zoom_feature = 0x00000014,
+ .global_reset_cmd = 0x00000018,
+ .module_ctrl = {
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ },
+ .bus_cgc_ovd = 0x0000003C,
+ .core_cfg = 0x00000000,
+ .three_D_cfg = 0x00000000,
+ .violation_status = 0x0000007C,
+ .reg_update_cmd = 0x000004AC,
+};
+
+static struct cam_vfe_rdi_ver2_reg vfe170_rdi_reg = {
+ .reg_update_cmd = 0x000004AC,
+};
+
+static struct cam_vfe_rdi_reg_data vfe170_rdi_0_data = {
+ .reg_update_cmd_data = 0x2,
+ .sof_irq_mask = 0x8000000,
+ .reg_update_irq_mask = 0x20,
+};
+
+static struct cam_vfe_rdi_reg_data vfe170_rdi_1_data = {
+ .reg_update_cmd_data = 0x4,
+ .sof_irq_mask = 0x10000000,
+ .reg_update_irq_mask = 0x40,
+};
+
+static struct cam_vfe_rdi_reg_data vfe170_rdi_2_data = {
+ .reg_update_cmd_data = 0x8,
+ .sof_irq_mask = 0x20000000,
+ .reg_update_irq_mask = 0x80,
+};
+
+static struct cam_vfe_rdi_reg_data vfe170_rdi_3_data = {
+ .reg_update_cmd_data = 0x10,
+ .sof_irq_mask = 0x40000000,
+ .reg_update_irq_mask = 0x100,
+};
+
+static struct cam_vfe_top_ver2_hw_info vfe170_top_hw_info = {
+ .common_reg = &vfe170_top_common_reg,
+ .camif_hw_info = {
+ .common_reg = NULL,
+ .camif_reg = NULL,
+ .reg_data = NULL,
+ },
+ .rdi_hw_info = {
+ .common_reg = &vfe170_top_common_reg,
+ .rdi_reg = &vfe170_rdi_reg,
+ .reg_data = {
+ &vfe170_rdi_0_data,
+ &vfe170_rdi_1_data,
+ &vfe170_rdi_2_data,
+ &vfe170_rdi_3_data,
+ },
+ },
+ .mux_type = {
+ CAM_VFE_RDI_VER_1_0,
+ CAM_VFE_RDI_VER_1_0,
+ CAM_VFE_RDI_VER_1_0,
+ CAM_VFE_RDI_VER_1_0,
+ },
+};
+
+static struct cam_irq_register_set vfe170_bus_irq_reg[3] = {
+ {
+ .mask_reg_offset = 0x00002044,
+ .clear_reg_offset = 0x00002050,
+ .status_reg_offset = 0x0000205C,
+ },
+ {
+ .mask_reg_offset = 0x00002048,
+ .clear_reg_offset = 0x00002054,
+ .status_reg_offset = 0x00002060,
+ },
+ {
+ .mask_reg_offset = 0x0000204C,
+ .clear_reg_offset = 0x00002058,
+ .status_reg_offset = 0x00002064,
+ },
+};
+
+static struct cam_vfe_bus_ver2_hw_info vfe170_bus_hw_info = {
+ .common_reg = {
+ .hw_version = 0x00002000,
+ .hw_capability = 0x00002004,
+ .sw_reset = 0x00002008,
+ .cgc_ovd = 0x0000200C,
+ .pwr_iso_cfg = 0x000020CC,
+ .dual_master_comp_cfg = 0x00002028,
+ .irq_reg_info = {
+ .num_registers = 3,
+ .irq_reg_set = vfe170_bus_irq_reg,
+ .global_clear_offset = 0x00002068,
+ .global_clear_bitmask = 0x00000001,
+ },
+ .comp_error_status = 0x0000206C,
+ .comp_ovrwr_status = 0x00002070,
+ .dual_comp_error_status = 0x00002074,
+ .dual_comp_ovrwr_status = 0x00002078,
+ .addr_sync_cfg = 0x0000207C,
+ .addr_sync_frame_hdr = 0x00002080,
+ .addr_sync_no_sync = 0x00002084,
+ },
+ .num_client = 4,
+ .bus_client_reg = {
+ /* BUS Client 0 */
+ {
+ .status0 = 0x00002200,
+ .status1 = 0x00002204,
+ .cfg = 0x00002208,
+ .header_addr = 0x0000220C,
+ .header_cfg = 0x00002210,
+ .image_addr = 0x00002214,
+ .image_addr_offset = 0x00002218,
+ .buffer_width_cfg = 0x0000221C,
+ .buffer_height_cfg = 0x00002220,
+ .packer_cfg = 0x00002224,
+ .stride = 0x00002228,
+ .irq_subsample_period = 0x00002248,
+ .irq_subsample_pattern = 0x0000224C,
+ .framedrop_period = 0x00002250,
+ .framedrop_pattern = 0x00002254,
+ .frame_inc = 0x00002258,
+ .burst_limit = 0x0000225C,
+ .ubwc_regs = NULL,
+ },
+ /* BUS Client 1 */
+ {
+ .status0 = 0x00002300,
+ .status1 = 0x00002304,
+ .cfg = 0x00002308,
+ .header_addr = 0x0000230C,
+ .header_cfg = 0x00002310,
+ .image_addr = 0x00002314,
+ .image_addr_offset = 0x00002318,
+ .buffer_width_cfg = 0x0000231C,
+ .buffer_height_cfg = 0x00002320,
+ .packer_cfg = 0x00002324,
+ .stride = 0x00002328,
+ .irq_subsample_period = 0x00002348,
+ .irq_subsample_pattern = 0x0000234C,
+ .framedrop_period = 0x00002350,
+ .framedrop_pattern = 0x00002354,
+ .frame_inc = 0x00002358,
+ .burst_limit = 0x0000235C,
+ .ubwc_regs = NULL,
+ },
+ /* BUS Client 2 */
+ {
+ .status0 = 0x00002400,
+ .status1 = 0x00002404,
+ .cfg = 0x00002408,
+ .header_addr = 0x0000240C,
+ .header_cfg = 0x00002410,
+ .image_addr = 0x00002414,
+ .image_addr_offset = 0x00002418,
+ .buffer_width_cfg = 0x0000241C,
+ .buffer_height_cfg = 0x00002420,
+ .packer_cfg = 0x00002424,
+ .stride = 0x00002428,
+ .irq_subsample_period = 0x00002448,
+ .irq_subsample_pattern = 0x0000244C,
+ .framedrop_period = 0x00002450,
+ .framedrop_pattern = 0x00002454,
+ .frame_inc = 0x00002458,
+ .burst_limit = 0x0000245C,
+ .ubwc_regs = NULL,
+ },
+ /* BUS Client 3 */
+ {
+ .status0 = 0x00002500,
+ .status1 = 0x00002504,
+ .cfg = 0x00002508,
+ .header_addr = 0x0000250C,
+ .header_cfg = 0x00002510,
+ .image_addr = 0x00002514,
+ .image_addr_offset = 0x00002518,
+ .buffer_width_cfg = 0x0000251C,
+ .buffer_height_cfg = 0x00002520,
+ .packer_cfg = 0x00002524,
+ .stride = 0x00002528,
+ .irq_subsample_period = 0x00002548,
+ .irq_subsample_pattern = 0x0000254C,
+ .framedrop_period = 0x00002550,
+ .framedrop_pattern = 0x00002554,
+ .frame_inc = 0x00002558,
+ .burst_limit = 0x0000255C,
+ .ubwc_regs = NULL,
+ },
+ },
+ .comp_grp_reg = {
+ /* CAM_VFE_BUS_VER2_COMP_GRP_0 */
+ {
+ .comp_mask = 0x00002010,
+ },
+ /* CAM_VFE_BUS_VER2_COMP_GRP_1 */
+ {
+ .comp_mask = 0x00002014,
+ },
+ /* CAM_VFE_BUS_VER2_COMP_GRP_2 */
+ {
+ .comp_mask = 0x00002018,
+ },
+ /* CAM_VFE_BUS_VER2_COMP_GRP_3 */
+ {
+ .comp_mask = 0x0000201C,
+ },
+ /* CAM_VFE_BUS_VER2_COMP_GRP_4 */
+ {
+ .comp_mask = 0x00002020,
+ },
+ /* CAM_VFE_BUS_VER2_COMP_GRP_5 */
+ {
+ .comp_mask = 0x00002024,
+ },
+ /* CAM_VFE_BUS_VER2_COMP_GRP_DUAL_0 */
+ {
+ .comp_mask = 0x0000202C,
+ .addr_sync_mask = 0x00002088,
+ },
+ /* CAM_VFE_BUS_VER2_COMP_GRP_DUAL_1 */
+ {
+ .comp_mask = 0x00002030,
+ .addr_sync_mask = 0x0000208C,
+
+ },
+ /* CAM_VFE_BUS_VER2_COMP_GRP_DUAL_2 */
+ {
+ .comp_mask = 0x00002034,
+ .addr_sync_mask = 0x00002090,
+
+ },
+ /* CAM_VFE_BUS_VER2_COMP_GRP_DUAL_3 */
+ {
+ .comp_mask = 0x00002038,
+ .addr_sync_mask = 0x00002094,
+ },
+ /* CAM_VFE_BUS_VER2_COMP_GRP_DUAL_4 */
+ {
+ .comp_mask = 0x0000203C,
+ .addr_sync_mask = 0x00002098,
+ },
+ /* CAM_VFE_BUS_VER2_COMP_GRP_DUAL_5 */
+ {
+ .comp_mask = 0x00002040,
+ .addr_sync_mask = 0x0000209C,
+ },
+ },
+ .num_out = 4,
+ .vfe_out_hw_info = {
+ {
+ .vfe_out_type = CAM_VFE_BUS_VER2_VFE_OUT_RDI0,
+ .max_width = -1,
+ .max_height = -1,
+ },
+ {
+ .vfe_out_type = CAM_VFE_BUS_VER2_VFE_OUT_RDI1,
+ .max_width = -1,
+ .max_height = -1,
+ },
+ {
+ .vfe_out_type = CAM_VFE_BUS_VER2_VFE_OUT_RDI2,
+ .max_width = -1,
+ .max_height = -1,
+ },
+ {
+ .vfe_out_type = CAM_VFE_BUS_VER2_VFE_OUT_RDI3,
+ .max_width = -1,
+ .max_height = -1,
+ },
+ },
+};
+
+static struct cam_vfe_hw_info cam_vfe_lite170_hw_info = {
+ .irq_reg_info = &vfe170_top_irq_reg_info,
+
+ .bus_version = CAM_VFE_BUS_VER_2_0,
+ .bus_hw_info = &vfe170_bus_hw_info,
+
+ .top_version = CAM_VFE_TOP_VER_2_0,
+ .top_hw_info = &vfe170_top_hw_info,
+
+};
+
+#endif /* _CAM_VFE_LITE170_H_ */
diff --git a/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/vfe_bus/Makefile b/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/vfe_bus/Makefile
index 4a328ee..24133a6 100644
--- a/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/vfe_bus/Makefile
+++ b/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/vfe_bus/Makefile
@@ -9,5 +9,6 @@
ccflags-y += -Idrivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/vfe_hw
ccflags-y += -Idrivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/vfe_top/include
ccflags-y += -Idrivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/vfe_bus/include
+ccflags-y += -Idrivers/media/platform/msm/camera/cam_cpas/include
obj-$(CONFIG_SPECTRA_CAMERA) += cam_vfe_bus.o cam_vfe_bus_ver2.o
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 005d7e0..f7c62a1 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
@@ -25,6 +25,7 @@
#include "cam_vfe_bus_ver2.h"
#include "cam_vfe_core.h"
#include "cam_debug_util.h"
+#include "cam_cpas_api.h"
static const char drv_name[] = "vfe_bus";
@@ -184,6 +185,8 @@
struct cam_vfe_bus_ver2_priv {
struct cam_vfe_bus_ver2_common_data common_data;
+ uint32_t num_client;
+ uint32_t num_out;
struct cam_isp_resource_node bus_client[CAM_VFE_BUS_VER2_MAX_CLIENTS];
struct cam_isp_resource_node comp_grp[CAM_VFE_BUS_VER2_COMP_GRP_MAX];
@@ -279,23 +282,37 @@
uint32_t *intra_client_mask)
{
int rc = 0;
+ uint32_t camera_hw_version = 0;
+ uint32_t version_based_intra_client_mask = 0x1;
*intra_client_mask = 0;
+
if (dual_slave_core == current_core) {
CAM_ERR(CAM_ISP,
"Invalid params. Same core as Master and Slave");
return -EINVAL;
}
+ rc = cam_cpas_get_cpas_hw_version(&camera_hw_version);
+
+ CAM_DBG(CAM_ISP, "CPAS VERSION %d", camera_hw_version);
+
+ switch (camera_hw_version) {
+ case CAM_CPAS_TITAN_170_V100:
+ version_based_intra_client_mask = 0x3;
+ break;
+ default:
+ version_based_intra_client_mask = 0x1;
+ break;
+ }
+
+
switch (current_core) {
case CAM_VFE_BUS_VER2_VFE_CORE_0:
switch (dual_slave_core) {
case CAM_VFE_BUS_VER2_VFE_CORE_1:
- *intra_client_mask = 0x3;
- break;
- case CAM_VFE_BUS_VER2_VFE_CORE_2:
- *intra_client_mask = 0x2;
+ *intra_client_mask = version_based_intra_client_mask;
break;
default:
CAM_ERR(CAM_ISP, "Invalid value for slave core %u",
@@ -307,25 +324,7 @@
case CAM_VFE_BUS_VER2_VFE_CORE_1:
switch (dual_slave_core) {
case CAM_VFE_BUS_VER2_VFE_CORE_0:
- *intra_client_mask = 0x1;
- break;
- case CAM_VFE_BUS_VER2_VFE_CORE_2:
- *intra_client_mask = 0x2;
- break;
- default:
- CAM_ERR(CAM_ISP, "Invalid value for slave core %u",
- dual_slave_core);
- rc = -EINVAL;
- break;
- }
- break;
- case CAM_VFE_BUS_VER2_VFE_CORE_2:
- switch (dual_slave_core) {
- case CAM_VFE_BUS_VER2_VFE_CORE_0:
- *intra_client_mask = 0x1;
- break;
- case CAM_VFE_BUS_VER2_VFE_CORE_1:
- *intra_client_mask = 0x2;
+ *intra_client_mask = version_based_intra_client_mask;
break;
default:
CAM_ERR(CAM_ISP, "Invalid value for slave core %u",
@@ -394,6 +393,8 @@
return CAM_VFE_BUS_VER2_VFE_OUT_RDI1;
case CAM_ISP_IFE_OUT_RES_RDI_2:
return CAM_VFE_BUS_VER2_VFE_OUT_RDI2;
+ case CAM_ISP_IFE_OUT_RES_RDI_3:
+ return CAM_VFE_BUS_VER2_VFE_OUT_RDI3;
case CAM_ISP_IFE_OUT_RES_STATS_HDR_BE:
return CAM_VFE_BUS_VER2_VFE_OUT_STATS_HDR_BE;
case CAM_ISP_IFE_OUT_RES_STATS_HDR_BHIST:
@@ -425,6 +426,7 @@
case CAM_VFE_BUS_VER2_VFE_OUT_RDI0:
case CAM_VFE_BUS_VER2_VFE_OUT_RDI1:
case CAM_VFE_BUS_VER2_VFE_OUT_RDI2:
+ case CAM_VFE_BUS_VER2_VFE_OUT_RDI3:
switch (format) {
case CAM_FORMAT_MIPI_RAW_8:
case CAM_FORMAT_MIPI_RAW_10:
@@ -551,6 +553,42 @@
int wm_idx = -1;
switch (vfe_out_res_id) {
+ case CAM_VFE_BUS_VER2_VFE_OUT_RDI0:
+ switch (plane) {
+ case PLANE_Y:
+ wm_idx = 0;
+ break;
+ default:
+ break;
+ }
+ break;
+ case CAM_VFE_BUS_VER2_VFE_OUT_RDI1:
+ switch (plane) {
+ case PLANE_Y:
+ wm_idx = 1;
+ break;
+ default:
+ break;
+ }
+ break;
+ case CAM_VFE_BUS_VER2_VFE_OUT_RDI2:
+ switch (plane) {
+ case PLANE_Y:
+ wm_idx = 2;
+ break;
+ default:
+ break;
+ }
+ break;
+ case CAM_VFE_BUS_VER2_VFE_OUT_RDI3:
+ switch (plane) {
+ case PLANE_Y:
+ wm_idx = 3;
+ break;
+ default:
+ break;
+ }
+ break;
case CAM_VFE_BUS_VER2_VFE_OUT_FULL:
switch (plane) {
case PLANE_Y:
@@ -611,33 +649,6 @@
break;
}
break;
- case CAM_VFE_BUS_VER2_VFE_OUT_RDI0:
- switch (plane) {
- case PLANE_Y:
- wm_idx = 0;
- break;
- default:
- break;
- }
- break;
- case CAM_VFE_BUS_VER2_VFE_OUT_RDI1:
- switch (plane) {
- case PLANE_Y:
- wm_idx = 1;
- break;
- default:
- break;
- }
- break;
- case CAM_VFE_BUS_VER2_VFE_OUT_RDI2:
- switch (plane) {
- case PLANE_Y:
- wm_idx = 2;
- break;
- default:
- break;
- }
- break;
case CAM_VFE_BUS_VER2_VFE_OUT_STATS_HDR_BE:
switch (plane) {
case PLANE_Y:
@@ -792,7 +803,7 @@
/* No need to allocate for BUS VER2. VFE OUT to WM is fixed. */
wm_idx = cam_vfe_bus_get_wm_idx(vfe_out_res_id, plane);
- if (wm_idx < 0 || wm_idx >= CAM_VFE_BUS_VER2_MAX_CLIENTS) {
+ if (wm_idx < 0 || wm_idx >= ver2_bus_priv->num_client) {
CAM_ERR(CAM_ISP, "Unsupported VFE out %d plane %d",
vfe_out_res_id, plane);
return -EINVAL;
@@ -840,10 +851,11 @@
rsrc_data->stride = rsrc_data->width;
break;
case CAM_FORMAT_PLAIN16_10:
- rsrc_data->en_cfg = 0x1;
- rsrc_data->pack_fmt = 0x2;
- rsrc_data->width = rsrc_data->width * 2;
- rsrc_data->stride = rsrc_data->width;
+ 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;
@@ -2148,13 +2160,30 @@
return rc;
}
-static int cam_vfe_bus_init_vfe_out_resource(uint32_t index,
- struct cam_vfe_bus_ver2_priv *ver2_bus_priv,
- struct cam_vfe_bus_ver2_hw_info *ver2_hw_info,
- struct cam_isp_resource_node *vfe_out)
+static int cam_vfe_bus_init_vfe_out_resource(uint32_t index,
+ struct cam_vfe_bus_ver2_priv *ver2_bus_priv,
+ struct cam_vfe_bus_ver2_hw_info *ver2_hw_info)
{
+ struct cam_isp_resource_node *vfe_out = NULL;
struct cam_vfe_bus_ver2_vfe_out_data *rsrc_data = NULL;
int rc = 0;
+ int32_t vfe_out_type =
+ ver2_hw_info->vfe_out_hw_info[index].vfe_out_type;
+
+ if (vfe_out_type < 0 ||
+ vfe_out_type >= CAM_VFE_BUS_VER2_VFE_OUT_MAX) {
+ CAM_ERR(CAM_ISP, "Init VFE Out failed, Invalid type=%d",
+ vfe_out_type);
+ return -EINVAL;
+ }
+
+ vfe_out = &ver2_bus_priv->vfe_out[vfe_out_type];
+ if (vfe_out->res_state != CAM_ISP_RESOURCE_STATE_UNAVAILABLE ||
+ vfe_out->res_priv) {
+ CAM_ERR(CAM_ISP,
+ "Error. Looks like same resource is init again");
+ return -EFAULT;
+ }
rsrc_data = kzalloc(sizeof(struct cam_vfe_bus_ver2_vfe_out_data),
GFP_KERNEL);
@@ -2162,13 +2191,15 @@
rc = -ENOMEM;
return rc;
}
+
vfe_out->res_priv = rsrc_data;
vfe_out->res_type = CAM_ISP_RESOURCE_VFE_OUT;
vfe_out->res_state = CAM_ISP_RESOURCE_STATE_AVAILABLE;
INIT_LIST_HEAD(&vfe_out->list);
- rsrc_data->out_type = index;
+ rsrc_data->out_type =
+ ver2_hw_info->vfe_out_hw_info[index].vfe_out_type;
rsrc_data->common_data = &ver2_bus_priv->common_data;
rsrc_data->max_width =
ver2_hw_info->vfe_out_hw_info[index].max_width;
@@ -2192,6 +2223,15 @@
{
struct cam_vfe_bus_ver2_vfe_out_data *rsrc_data = vfe_out->res_priv;
+ if (vfe_out->res_state == CAM_ISP_RESOURCE_STATE_UNAVAILABLE) {
+ /*
+ * This is not error. It can happen if the resource is
+ * never supported in the HW.
+ */
+ CAM_DBG(CAM_ISP, "HW%d Res %d already deinitialized");
+ return 0;
+ }
+
vfe_out->start = NULL;
vfe_out->stop = NULL;
vfe_out->top_half_handler = NULL;
@@ -2267,7 +2307,8 @@
CAM_VFE_ADD_REG_VAL_PAIR(reg_val_pair, j,
wm_data->hw_regs->buffer_width_cfg,
wm_data->width);
- CAM_DBG(CAM_ISP, "image width 0x%x", wm_data->width);
+ CAM_DBG(CAM_ISP, "WM %d image width 0x%x",
+ wm_data->index, wm_data->width);
/* For initial configuration program all bus registers */
if ((wm_data->stride != io_cfg->planes[i].plane_stride ||
@@ -2276,8 +2317,9 @@
wm_data->hw_regs->stride,
io_cfg->planes[i].plane_stride);
wm_data->stride = io_cfg->planes[i].plane_stride;
+ CAM_DBG(CAM_ISP, "WM %d image stride 0x%x",
+ wm_data->index, wm_data->stride);
}
- CAM_DBG(CAM_ISP, "image stride 0x%x", wm_data->stride);
if (wm_data->framedrop_pattern != io_cfg->framedrop_pattern ||
!wm_data->hfr_cfg_done) {
@@ -2285,9 +2327,11 @@
wm_data->hw_regs->framedrop_pattern,
io_cfg->framedrop_pattern);
wm_data->framedrop_pattern = io_cfg->framedrop_pattern;
+ CAM_DBG(CAM_ISP, "WM %d framedrop pattern 0x%x",
+ wm_data->index,
+ wm_data->framedrop_pattern);
}
- CAM_DBG(CAM_ISP, "framedrop pattern 0x%x",
- wm_data->framedrop_pattern);
+
if (wm_data->framedrop_period != io_cfg->framedrop_period ||
!wm_data->hfr_cfg_done) {
@@ -2295,9 +2339,10 @@
wm_data->hw_regs->framedrop_period,
io_cfg->framedrop_period);
wm_data->framedrop_period = io_cfg->framedrop_period;
+ CAM_DBG(CAM_ISP, "WM %d framedrop period 0x%x",
+ wm_data->index,
+ wm_data->framedrop_period);
}
- CAM_DBG(CAM_ISP, "framedrop period 0x%x",
- wm_data->framedrop_period);
if (wm_data->irq_subsample_period != io_cfg->subsample_period
|| !wm_data->hfr_cfg_done) {
@@ -2306,9 +2351,10 @@
io_cfg->subsample_period);
wm_data->irq_subsample_period =
io_cfg->subsample_period;
+ CAM_DBG(CAM_ISP, "WM %d irq subsample period 0x%x",
+ wm_data->index,
+ wm_data->irq_subsample_period);
}
- CAM_DBG(CAM_ISP, "irq subsample period 0x%x",
- wm_data->irq_subsample_period);
if (wm_data->irq_subsample_pattern != io_cfg->subsample_pattern
|| !wm_data->hfr_cfg_done) {
@@ -2317,9 +2363,10 @@
io_cfg->subsample_pattern);
wm_data->irq_subsample_pattern =
io_cfg->subsample_pattern;
+ CAM_DBG(CAM_ISP, "WM %d irq subsample pattern 0x%x",
+ wm_data->index,
+ wm_data->irq_subsample_pattern);
}
- CAM_DBG(CAM_ISP, "irq subsample pattern 0x%x",
- wm_data->irq_subsample_pattern);
if (wm_data->en_ubwc) {
if (!wm_data->hw_regs->ubwc_regs) {
@@ -2335,9 +2382,9 @@
io_cfg->planes[i].packer_config);
wm_data->packer_cfg =
io_cfg->planes[i].packer_config;
+ CAM_DBG(CAM_ISP, "WM %d packer cfg 0x%x",
+ wm_data->index, wm_data->packer_cfg);
}
- CAM_DBG(CAM_ISP, "packer cfg 0x%x",
- wm_data->packer_cfg);
if (wm_data->is_dual) {
CAM_VFE_ADD_REG_VAL_PAIR(reg_val_pair, j,
@@ -2351,8 +2398,9 @@
io_cfg->planes[i].tile_config);
wm_data->tile_cfg =
io_cfg->planes[i].tile_config;
+ CAM_DBG(CAM_ISP, "WM %d tile cfg 0x%x",
+ wm_data->index, wm_data->tile_cfg);
}
- CAM_DBG(CAM_ISP, "tile cfg 0x%x", wm_data->tile_cfg);
if (wm_data->is_dual) {
if ((wm_data->h_init != wm_data->offset) ||
@@ -2375,8 +2423,9 @@
wm_data->hw_regs->ubwc_regs->h_init,
io_cfg->planes[i].h_init);
wm_data->h_init = io_cfg->planes[i].h_init;
+ CAM_DBG(CAM_ISP, "WM %d h_init 0x%x",
+ wm_data->index, wm_data->h_init);
}
- CAM_DBG(CAM_ISP, "h_init 0x%x", wm_data->h_init);
if (wm_data->v_init != io_cfg->planes[i].v_init ||
!wm_data->init_cfg_done) {
@@ -2384,8 +2433,9 @@
wm_data->hw_regs->ubwc_regs->v_init,
io_cfg->planes[i].v_init);
wm_data->v_init = io_cfg->planes[i].v_init;
+ CAM_DBG(CAM_ISP, "WM %d v_init 0x%x",
+ wm_data->index, wm_data->v_init);
}
- CAM_DBG(CAM_ISP, "v_init 0x%x", wm_data->v_init);
if (wm_data->ubwc_meta_stride !=
io_cfg->planes[i].meta_stride ||
@@ -2396,9 +2446,10 @@
io_cfg->planes[i].meta_stride);
wm_data->ubwc_meta_stride =
io_cfg->planes[i].meta_stride;
+ CAM_DBG(CAM_ISP, "WM %d meta stride 0x%x",
+ wm_data->index,
+ wm_data->ubwc_meta_stride);
}
- CAM_DBG(CAM_ISP, "meta stride 0x%x",
- wm_data->ubwc_meta_stride);
if (wm_data->ubwc_mode_cfg !=
io_cfg->planes[i].mode_config ||
@@ -2408,9 +2459,9 @@
io_cfg->planes[i].mode_config);
wm_data->ubwc_mode_cfg =
io_cfg->planes[i].mode_config;
+ CAM_DBG(CAM_ISP, "WM %d ubwc mode cfg 0x%x",
+ wm_data->index, wm_data->ubwc_mode_cfg);
}
- CAM_DBG(CAM_ISP, "ubwc mode cfg 0x%x",
- wm_data->ubwc_mode_cfg);
if (wm_data->ubwc_meta_offset !=
io_cfg->planes[i].meta_offset ||
@@ -2421,16 +2472,17 @@
io_cfg->planes[i].meta_offset);
wm_data->ubwc_meta_offset =
io_cfg->planes[i].meta_offset;
+ CAM_DBG(CAM_ISP, "WM %d ubwc meta offset 0x%x",
+ wm_data->index,
+ wm_data->ubwc_meta_offset);
}
- CAM_DBG(CAM_ISP, "ubwc meta offset 0x%x",
- wm_data->ubwc_meta_offset);
/* UBWC meta address */
CAM_VFE_ADD_REG_VAL_PAIR(reg_val_pair, j,
wm_data->hw_regs->ubwc_regs->meta_addr,
update_buf->image_buf[i]);
- CAM_DBG(CAM_ISP, "ubwc meta addr 0x%llx",
- update_buf->image_buf[i]);
+ CAM_DBG(CAM_ISP, "WM %d ubwc meta addr 0x%llx",
+ wm_data->index, update_buf->image_buf[i]);
}
/* WM Image address */
@@ -2445,12 +2497,16 @@
update_buf->image_buf[i] +
wm_data->offset);
- CAM_DBG(CAM_ISP, "image address 0x%x", reg_val_pair[j-1]);
+ CAM_DBG(CAM_ISP, "WM %d image address 0x%x",
+ wm_data->index, reg_val_pair[j-1]);
frame_inc = io_cfg->planes[i].plane_stride *
io_cfg->planes[i].slice_height;
CAM_VFE_ADD_REG_VAL_PAIR(reg_val_pair, j,
wm_data->hw_regs->frame_inc, frame_inc);
+ CAM_DBG(CAM_ISP, "WM %d frame_inc %d",
+ wm_data->index, frame_inc);
+
/* enable the WM */
CAM_VFE_ADD_REG_VAL_PAIR(reg_val_pair, j,
@@ -2523,9 +2579,9 @@
wm_data->hw_regs->framedrop_pattern,
hfr_cfg->framedrop_pattern);
wm_data->framedrop_pattern = hfr_cfg->framedrop_pattern;
+ CAM_DBG(CAM_ISP, "WM %d framedrop pattern 0x%x",
+ wm_data->index, wm_data->framedrop_pattern);
}
- CAM_DBG(CAM_ISP, "framedrop pattern 0x%x",
- wm_data->framedrop_pattern);
if (wm_data->framedrop_period != hfr_cfg->framedrop_period ||
!wm_data->hfr_cfg_done) {
@@ -2533,9 +2589,9 @@
wm_data->hw_regs->framedrop_period,
hfr_cfg->framedrop_period);
wm_data->framedrop_period = hfr_cfg->framedrop_period;
+ CAM_DBG(CAM_ISP, "WM %d framedrop period 0x%x",
+ wm_data->index, wm_data->framedrop_period);
}
- CAM_DBG(CAM_ISP, "framedrop period 0x%x",
- wm_data->framedrop_period);
if (wm_data->irq_subsample_period != hfr_cfg->subsample_period
|| !wm_data->hfr_cfg_done) {
@@ -2544,9 +2600,9 @@
hfr_cfg->subsample_period);
wm_data->irq_subsample_period =
hfr_cfg->subsample_period;
+ CAM_DBG(CAM_ISP, "WM %d irq subsample period 0x%x",
+ wm_data->index, wm_data->irq_subsample_period);
}
- CAM_DBG(CAM_ISP, "irq subsample period 0x%x",
- wm_data->irq_subsample_period);
if (wm_data->irq_subsample_pattern != hfr_cfg->subsample_pattern
|| !wm_data->hfr_cfg_done) {
@@ -2555,9 +2611,9 @@
hfr_cfg->subsample_pattern);
wm_data->irq_subsample_pattern =
hfr_cfg->subsample_pattern;
+ CAM_DBG(CAM_ISP, "WM %d irq subsample pattern 0x%x",
+ wm_data->index, wm_data->irq_subsample_pattern);
}
- CAM_DBG(CAM_ISP, "irq subsample pattern 0x%x",
- wm_data->irq_subsample_pattern);
/* set initial configuration done */
if (!wm_data->hfr_cfg_done)
@@ -2783,6 +2839,8 @@
}
vfe_bus_local->bus_priv = bus_priv;
+ bus_priv->num_client = ver2_hw_info->num_client;
+ bus_priv->num_out = ver2_hw_info->num_out;
bus_priv->common_data.num_sec_out = 0;
bus_priv->common_data.secure_mode = CAM_SECURE_MODE_NON_SECURE;
bus_priv->common_data.core_index = soc_info->index;
@@ -2808,7 +2866,7 @@
INIT_LIST_HEAD(&bus_priv->free_dual_comp_grp);
INIT_LIST_HEAD(&bus_priv->used_comp_grp);
- for (i = 0; i < CAM_VFE_BUS_VER2_MAX_CLIENTS; i++) {
+ for (i = 0; i < bus_priv->num_client; i++) {
rc = cam_vfe_bus_init_wm_resource(i, bus_priv, bus_hw_info,
&bus_priv->bus_client[i]);
if (rc < 0) {
@@ -2826,9 +2884,9 @@
}
}
- for (i = 0; i < CAM_VFE_BUS_VER2_VFE_OUT_MAX; i++) {
- rc = cam_vfe_bus_init_vfe_out_resource(i, bus_priv, bus_hw_info,
- &bus_priv->vfe_out[i]);
+ for (i = 0; i < bus_priv->num_out; i++) {
+ rc = cam_vfe_bus_init_vfe_out_resource(i, bus_priv,
+ bus_hw_info);
if (rc < 0) {
CAM_ERR(CAM_ISP, "Init VFE Out failed rc=%d", rc);
goto deinit_vfe_out;
@@ -2871,7 +2929,7 @@
deinit_wm:
if (i < 0)
- i = CAM_VFE_BUS_VER2_MAX_CLIENTS;
+ i = bus_priv->num_client;
for (--i; i >= 0; i--)
cam_vfe_bus_deinit_wm_resource(&bus_priv->bus_client[i]);
@@ -2909,7 +2967,7 @@
for (i = 0; i < CAM_VFE_BUS_VER2_PAYLOAD_MAX; i++)
INIT_LIST_HEAD(&bus_priv->common_data.evt_payload[i].list);
- for (i = 0; i < CAM_VFE_BUS_VER2_MAX_CLIENTS; i++) {
+ for (i = 0; i < bus_priv->num_client; i++) {
rc = cam_vfe_bus_deinit_wm_resource(&bus_priv->bus_client[i]);
if (rc < 0)
CAM_ERR(CAM_ISP,
diff --git a/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/vfe_bus/cam_vfe_bus_ver2.h b/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/vfe_bus/cam_vfe_bus_ver2.h
index c90d4ce..ed7d5fe 100644
--- a/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/vfe_bus/cam_vfe_bus_ver2.h
+++ b/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/vfe_bus/cam_vfe_bus_ver2.h
@@ -21,7 +21,6 @@
enum cam_vfe_bus_ver2_vfe_core_id {
CAM_VFE_BUS_VER2_VFE_CORE_0,
CAM_VFE_BUS_VER2_VFE_CORE_1,
- CAM_VFE_BUS_VER2_VFE_CORE_2,
CAM_VFE_BUS_VER2_VFE_CORE_MAX,
};
@@ -42,15 +41,16 @@
};
enum cam_vfe_bus_ver2_vfe_out_type {
+ CAM_VFE_BUS_VER2_VFE_OUT_RDI0,
+ CAM_VFE_BUS_VER2_VFE_OUT_RDI1,
+ CAM_VFE_BUS_VER2_VFE_OUT_RDI2,
+ CAM_VFE_BUS_VER2_VFE_OUT_RDI3,
CAM_VFE_BUS_VER2_VFE_OUT_FULL,
CAM_VFE_BUS_VER2_VFE_OUT_DS4,
CAM_VFE_BUS_VER2_VFE_OUT_DS16,
CAM_VFE_BUS_VER2_VFE_OUT_RAW_DUMP,
CAM_VFE_BUS_VER2_VFE_OUT_FD,
CAM_VFE_BUS_VER2_VFE_OUT_PDAF,
- CAM_VFE_BUS_VER2_VFE_OUT_RDI0,
- CAM_VFE_BUS_VER2_VFE_OUT_RDI1,
- CAM_VFE_BUS_VER2_VFE_OUT_RDI2,
CAM_VFE_BUS_VER2_VFE_OUT_STATS_HDR_BE,
CAM_VFE_BUS_VER2_VFE_OUT_STATS_HDR_BHIST,
CAM_VFE_BUS_VER2_VFE_OUT_STATS_TL_BG,
@@ -160,11 +160,13 @@
* @vfe_out_hw_info: VFE output capability
*/
struct cam_vfe_bus_ver2_hw_info {
- struct cam_vfe_bus_ver2_reg_offset_common common_reg;
+ struct cam_vfe_bus_ver2_reg_offset_common common_reg;
+ uint32_t num_client;
struct cam_vfe_bus_ver2_reg_offset_bus_client
bus_client_reg[CAM_VFE_BUS_VER2_MAX_CLIENTS];
struct cam_vfe_bus_ver2_reg_offset_comp_grp
comp_grp_reg[CAM_VFE_BUS_VER2_COMP_GRP_MAX];
+ uint32_t num_out;
struct cam_vfe_bus_ver2_vfe_out_hw_info
vfe_out_hw_info[CAM_VFE_BUS_VER2_VFE_OUT_MAX];
};
diff --git a/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/vfe_bus/include/cam_vfe_bus.h b/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/vfe_bus/include/cam_vfe_bus.h
index c089911..0763bca 100644
--- a/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/vfe_bus/include/cam_vfe_bus.h
+++ b/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/vfe_bus/include/cam_vfe_bus.h
@@ -17,8 +17,8 @@
#include "cam_hw_intf.h"
#include "cam_isp_hw.h"
-#define CAM_VFE_BUS_VER_1_0 0x1000
-#define CAM_VFE_BUS_VER_2_0 0x2000
+#define CAM_VFE_BUS_VER_1_0 0x1000
+#define CAM_VFE_BUS_VER_2_0 0x2000
enum cam_vfe_bus_plane_type {
PLANE_Y,
diff --git a/drivers/media/platform/msm/camera/cam_jpeg/cam_jpeg_dev.c b/drivers/media/platform/msm/camera/cam_jpeg/cam_jpeg_dev.c
index fb68ddb..a400388 100644
--- a/drivers/media/platform/msm/camera/cam_jpeg/cam_jpeg_dev.c
+++ b/drivers/media/platform/msm/camera/cam_jpeg/cam_jpeg_dev.c
@@ -34,6 +34,25 @@
{ }
};
+static int cam_jpeg_subdev_close(struct v4l2_subdev *sd,
+ struct v4l2_subdev_fh *fh)
+{
+ struct cam_node *node = v4l2_get_subdevdata(sd);
+
+ if (!node) {
+ CAM_ERR(CAM_JPEG, "Node ptr is NULL");
+ return -EINVAL;
+ }
+
+ cam_node_shutdown(node);
+
+ return 0;
+}
+
+static const struct v4l2_subdev_internal_ops cam_jpeg_subdev_internal_ops = {
+ .close = cam_jpeg_subdev_close,
+};
+
static int cam_jpeg_dev_remove(struct platform_device *pdev)
{
int rc;
@@ -60,6 +79,7 @@
struct cam_hw_mgr_intf hw_mgr_intf;
struct cam_node *node;
+ g_jpeg_dev.sd.internal_ops = &cam_jpeg_subdev_internal_ops;
rc = cam_subdev_probe(&g_jpeg_dev.sd, pdev, CAM_JPEG_DEV_NAME,
CAM_JPEG_DEVICE_TYPE);
if (rc) {
diff --git a/drivers/media/platform/msm/camera/cam_jpeg/jpeg_hw/cam_jpeg_hw_mgr.c b/drivers/media/platform/msm/camera/cam_jpeg/jpeg_hw/cam_jpeg_hw_mgr.c
index 2cd6b04..35c2717 100644
--- a/drivers/media/platform/msm/camera/cam_jpeg/jpeg_hw/cam_jpeg_hw_mgr.c
+++ b/drivers/media/platform/msm/camera/cam_jpeg/jpeg_hw/cam_jpeg_hw_mgr.c
@@ -28,8 +28,6 @@
#include "cam_hw_mgr_intf.h"
#include "cam_jpeg_hw_mgr_intf.h"
#include "cam_jpeg_hw_mgr.h"
-#include "cam_enc_hw_intf.h"
-#include "cam_dma_hw_intf.h"
#include "cam_smmu_api.h"
#include "cam_mem_mgr.h"
#include "cam_req_mgr_workq.h"
@@ -38,6 +36,9 @@
#include "cam_debug_util.h"
#define CAM_JPEG_HW_ENTRIES_MAX 20
+#define CAM_JPEG_CHBASE 0
+#define CAM_JPEG_CFG 1
+#define CAM_JPEG_PARAM 2
static struct cam_jpeg_hw_mgr g_jpeg_hw_mgr;
@@ -88,13 +89,20 @@
}
rc = hw_mgr->devices[dev_type][0]->hw_ops.process_cmd(
hw_mgr->devices[dev_type][0]->hw_priv,
- CAM_JPEG_ENC_CMD_SET_IRQ_CB,
+ CAM_JPEG_CMD_SET_IRQ_CB,
&irq_cb, sizeof(irq_cb));
if (rc) {
CAM_ERR(CAM_JPEG, "CMD_SET_IRQ_CB failed %d", rc);
return rc;
}
+ if (hw_mgr->devices[dev_type][0]->hw_ops.deinit) {
+ rc = hw_mgr->devices[dev_type][0]->hw_ops.deinit(
+ hw_mgr->devices[dev_type][0]->hw_priv, NULL, 0);
+ if (rc)
+ CAM_ERR(CAM_JPEG, "Failed to Deinit %d HW", dev_type);
+ }
+
mutex_lock(&g_jpeg_hw_mgr.hw_mgr_mutex);
hw_mgr->device_in_use[dev_type][0] = false;
p_cfg_req = hw_mgr->dev_hw_cfg_args[dev_type][0];
@@ -126,7 +134,8 @@
}
rc = cam_mem_get_cpu_buf(
- p_cfg_req->hw_cfg_args.hw_update_entries[1].handle,
+ p_cfg_req->hw_cfg_args.
+ hw_update_entries[CAM_JPEG_PARAM].handle,
(uint64_t *)&kaddr, &cmd_buf_len);
if (rc) {
CAM_ERR(CAM_JPEG, "unable to get info for cmd buf: %x %d",
@@ -137,8 +146,8 @@
cmd_buf_kaddr = (uint32_t *)kaddr;
cmd_buf_kaddr =
- (cmd_buf_kaddr +
- (p_cfg_req->hw_cfg_args.hw_update_entries[1].offset/4));
+ (cmd_buf_kaddr + (p_cfg_req->hw_cfg_args.
+ hw_update_entries[CAM_JPEG_PARAM].offset / sizeof(uint32_t)));
p_params = (struct cam_jpeg_config_inout_param_info *)cmd_buf_kaddr;
@@ -234,6 +243,59 @@
return 0;
}
+static int cam_jpeg_insert_cdm_change_base(
+ struct cam_hw_config_args *config_args,
+ struct cam_jpeg_hw_ctx_data *ctx_data,
+ struct cam_jpeg_hw_mgr *hw_mgr)
+{
+ int rc;
+ uint32_t dev_type;
+ struct cam_cdm_bl_request *cdm_cmd;
+ uint32_t size;
+ uint32_t mem_cam_base;
+ uint64_t iova_addr;
+ uint32_t *ch_base_iova_addr;
+ size_t ch_base_len;
+
+ rc = cam_mem_get_cpu_buf(config_args->
+ hw_update_entries[CAM_JPEG_CHBASE].handle,
+ &iova_addr, &ch_base_len);
+ if (rc) {
+ CAM_ERR(CAM_JPEG,
+ "unable to get src buf info for cmd buf: %d", rc);
+ return rc;
+ }
+ CAM_DBG(CAM_JPEG, "iova %pK len %zu offset %d",
+ (void *)iova_addr, ch_base_len,
+ config_args->hw_update_entries[CAM_JPEG_CHBASE].offset);
+ ch_base_iova_addr = (uint32_t *)iova_addr;
+ ch_base_iova_addr = (ch_base_iova_addr +
+ (config_args->hw_update_entries[CAM_JPEG_CHBASE].offset /
+ sizeof(uint32_t)));
+
+ dev_type = ctx_data->jpeg_dev_acquire_info.dev_type;
+ mem_cam_base = hw_mgr->cdm_reg_map[dev_type][0]->mem_cam_base;
+ size = hw_mgr->cdm_info[dev_type][0].cdm_ops->
+ cdm_required_size_changebase();
+ hw_mgr->cdm_info[dev_type][0].cdm_ops->
+ cdm_write_changebase(ch_base_iova_addr, mem_cam_base);
+
+ cdm_cmd = ctx_data->cdm_cmd;
+ cdm_cmd->cmd[cdm_cmd->cmd_arrary_count].bl_addr.mem_handle =
+ config_args->hw_update_entries[CAM_JPEG_CHBASE].handle;
+ cdm_cmd->cmd[cdm_cmd->cmd_arrary_count].offset =
+ config_args->hw_update_entries[CAM_JPEG_CHBASE].offset;
+ cdm_cmd->cmd[cdm_cmd->cmd_arrary_count].len = size * sizeof(uint32_t);
+ cdm_cmd->cmd_arrary_count++;
+
+ ch_base_iova_addr += size;
+ *ch_base_iova_addr = 0;
+ ch_base_iova_addr += size;
+ *ch_base_iova_addr = 0;
+
+ return rc;
+}
+
static int cam_jpeg_mgr_process_cmd(void *priv, void *data)
{
int rc;
@@ -249,11 +311,10 @@
uint32_t dev_type;
struct cam_jpeg_set_irq_cb irq_cb;
struct cam_jpeg_hw_cfg_req *p_cfg_req = NULL;
+ struct cam_hw_done_event_data buf_data;
uint32_t size = 0;
uint32_t mem_cam_base = 0;
- struct cam_hw_done_event_data buf_data;
- CAM_DBG(CAM_JPEG, "in cam_jpeg_mgr_process_cmd");
if (!hw_mgr || !task_data) {
CAM_ERR(CAM_JPEG, "Invalid arguments %pK %pK",
hw_mgr, task_data);
@@ -296,7 +357,8 @@
if (!config_args->num_hw_update_entries) {
CAM_ERR(CAM_JPEG, "No hw update enteries are available");
- return -EINVAL;
+ rc = -EINVAL;
+ goto end_unusedev;
}
mutex_lock(&hw_mgr->hw_mgr_mutex);
@@ -304,7 +366,8 @@
if (!ctx_data->in_use) {
CAM_ERR(CAM_JPEG, "ctx is not in use");
mutex_unlock(&hw_mgr->hw_mgr_mutex);
- return -EINVAL;
+ rc = -EINVAL;
+ goto end_unusedev;
}
mutex_unlock(&hw_mgr->hw_mgr_mutex);
@@ -313,99 +376,123 @@
if (dev_type != p_cfg_req->dev_type)
CAM_WARN(CAM_JPEG, "dev types not same something wrong");
+ if (!hw_mgr->devices[dev_type][0]->hw_ops.init) {
+ CAM_ERR(CAM_JPEG, "hw op init null ");
+ rc = -EFAULT;
+ goto end;
+ }
+ rc = hw_mgr->devices[dev_type][0]->hw_ops.init(
+ hw_mgr->devices[dev_type][0]->hw_priv,
+ ctx_data,
+ sizeof(ctx_data));
+ if (rc) {
+ CAM_ERR(CAM_JPEG, "Failed to Init %d HW", dev_type);
+ goto end;
+ }
+
irq_cb.jpeg_hw_mgr_cb = cam_jpeg_hw_mgr_cb;
irq_cb.data = (void *)ctx_data;
irq_cb.b_set_cb = true;
if (!hw_mgr->devices[dev_type][0]->hw_ops.process_cmd) {
CAM_ERR(CAM_JPEG, "op process_cmd null ");
- return -EINVAL;
+ rc = -EFAULT;
+ goto end_callcb;
}
rc = hw_mgr->devices[dev_type][0]->hw_ops.process_cmd(
hw_mgr->devices[dev_type][0]->hw_priv,
- CAM_JPEG_ENC_CMD_SET_IRQ_CB,
+ CAM_JPEG_CMD_SET_IRQ_CB,
&irq_cb, sizeof(irq_cb));
if (rc) {
CAM_ERR(CAM_JPEG, "SET_IRQ_CB failed %d", rc);
- return -EINVAL;
+ goto end_callcb;
}
if (!hw_mgr->devices[dev_type][0]->hw_ops.reset) {
CAM_ERR(CAM_JPEG, "op reset null ");
- return -EINVAL;
+ rc = -EFAULT;
+ goto end_callcb;
}
rc = hw_mgr->devices[dev_type][0]->hw_ops.reset(
hw_mgr->devices[dev_type][0]->hw_priv,
NULL, 0);
if (rc) {
CAM_ERR(CAM_JPEG, "jpeg hw reset failed %d", rc);
- return -EINVAL;
+ goto end_callcb;
}
- mem_cam_base = (uint64_t)hw_mgr->cdm_reg_map[dev_type][0]->
- mem_cam_base;
- size = hw_mgr->cdm_info[dev_type][0].cdm_ops->
- cdm_required_size_changebase();
- hw_mgr->cdm_info[dev_type][0].cdm_ops->
- cdm_write_changebase(ctx_data->cmd_chbase_buf_addr,
- (uint64_t)hw_mgr->cdm_reg_map[dev_type][0]->mem_cam_base);
- ctx_data->cdm_cmd_chbase->cmd_arrary_count = 1;
- ctx_data->cdm_cmd_chbase->type = CAM_CDM_BL_CMD_TYPE_KERNEL_IOVA;
- ctx_data->cdm_cmd_chbase->flag = false;
- ctx_data->cdm_cmd_chbase->userdata = NULL;
- ctx_data->cdm_cmd_chbase->cookie = 0;
- ctx_data->cdm_cmd_chbase->cmd[0].bl_addr.kernel_iova =
- ctx_data->cmd_chbase_buf_addr;
- ctx_data->cdm_cmd_chbase->cmd[0].offset = 0;
- ctx_data->cdm_cmd_chbase->cmd[0].len = size;
- rc = cam_cdm_submit_bls(hw_mgr->cdm_info[dev_type][0].cdm_handle,
- ctx_data->cdm_cmd_chbase);
- if (rc)
- CAM_ERR(CAM_JPEG, "failed cdm cmd %d", rc);
+ cdm_cmd = ctx_data->cdm_cmd;
+ cdm_cmd->type = CAM_CDM_BL_CMD_TYPE_MEM_HANDLE;
+ cdm_cmd->flag = false;
+ cdm_cmd->userdata = NULL;
+ cdm_cmd->cookie = 0;
+ cdm_cmd->cmd_arrary_count = 0;
- CAM_DBG(CAM_JPEG, "cfg e %pK num %d",
- config_args->hw_update_entries,
- config_args->num_hw_update_entries);
-
- if (config_args->num_hw_update_entries > 0) {
- cdm_cmd = ctx_data->cdm_cmd;
- cdm_cmd->cmd_arrary_count =
- config_args->num_hw_update_entries - 1;
- cdm_cmd->type = CAM_CDM_BL_CMD_TYPE_MEM_HANDLE;
- cdm_cmd->flag = false;
- cdm_cmd->userdata = NULL;
- cdm_cmd->cookie = 0;
-
- for (i = 0; i <= cdm_cmd->cmd_arrary_count; i++) {
- cmd = (config_args->hw_update_entries + i);
- cdm_cmd->cmd[i].bl_addr.mem_handle = cmd->handle;
- cdm_cmd->cmd[i].offset = cmd->offset;
- cdm_cmd->cmd[i].len = cmd->len;
- }
-
- rc = cam_cdm_submit_bls(
- hw_mgr->cdm_info[dev_type][0].cdm_handle,
- cdm_cmd);
+ /* if for backward compat */
+ if (config_args->hw_update_entries[CAM_JPEG_CHBASE].handle) {
+ rc = cam_jpeg_insert_cdm_change_base(config_args,
+ ctx_data, hw_mgr);
if (rc) {
- CAM_ERR(CAM_JPEG, "Failed to apply the configs %d",
- rc);
- goto end_callcb;
- }
-
- if (!hw_mgr->devices[dev_type][0]->hw_ops.start) {
- CAM_ERR(CAM_JPEG, "op start null ");
- rc = -EINVAL;
- goto end_callcb;
- }
- rc = hw_mgr->devices[dev_type][0]->hw_ops.start(
- hw_mgr->devices[dev_type][0]->hw_priv,
- NULL, 0);
- if (rc) {
- CAM_ERR(CAM_JPEG, "Failed to apply the configs %d",
- rc);
+ CAM_ERR(CAM_JPEG, "insert change base failed %d", rc);
goto end_callcb;
}
} else {
- CAM_ERR(CAM_JPEG, "No commands to config");
+ mem_cam_base = hw_mgr->cdm_reg_map[dev_type][0]->
+ mem_cam_base;
+ size = hw_mgr->cdm_info[dev_type][0].cdm_ops->
+ cdm_required_size_changebase();
+ hw_mgr->cdm_info[dev_type][0].cdm_ops->
+ cdm_write_changebase(ctx_data->cmd_chbase_buf_addr,
+ hw_mgr->cdm_reg_map[dev_type][0]->mem_cam_base);
+ ctx_data->cdm_cmd_chbase->cmd_arrary_count = 1;
+ ctx_data->cdm_cmd_chbase->type =
+ CAM_CDM_BL_CMD_TYPE_KERNEL_IOVA;
+ ctx_data->cdm_cmd_chbase->flag = false;
+ ctx_data->cdm_cmd_chbase->userdata = NULL;
+ ctx_data->cdm_cmd_chbase->cookie = 0;
+ ctx_data->cdm_cmd_chbase->cmd[0].bl_addr.kernel_iova =
+ ctx_data->cmd_chbase_buf_addr;
+ ctx_data->cdm_cmd_chbase->cmd[0].offset = 0;
+ ctx_data->cdm_cmd_chbase->cmd[0].len = size;
+ cam_cdm_submit_bls(hw_mgr->cdm_info[dev_type][0].
+ cdm_handle,
+ ctx_data->cdm_cmd_chbase);
+ }
+
+ CAM_DBG(CAM_JPEG, "num hw up %d", config_args->num_hw_update_entries);
+ for (i = CAM_JPEG_CFG; i < (config_args->num_hw_update_entries - 1);
+ i++) {
+ cmd = (config_args->hw_update_entries + i);
+ cdm_cmd->cmd[cdm_cmd->cmd_arrary_count].
+ bl_addr.mem_handle = cmd->handle;
+ cdm_cmd->cmd[cdm_cmd->cmd_arrary_count].offset =
+ cmd->offset;
+ cdm_cmd->cmd[cdm_cmd->cmd_arrary_count].len =
+ cmd->len;
+ CAM_DBG(CAM_JPEG, "i %d entry h %d o %d l %d",
+ i, cmd->handle, cmd->offset, cmd->len);
+ cdm_cmd->cmd_arrary_count++;
+ }
+
+ rc = cam_cdm_submit_bls(
+ hw_mgr->cdm_info[dev_type][0].cdm_handle,
+ cdm_cmd);
+ if (rc) {
+ CAM_ERR(CAM_JPEG, "Failed to apply the configs %d", rc);
+ goto end_callcb;
+ }
+
+ if (!hw_mgr->devices[dev_type][0]->hw_ops.start) {
+ CAM_ERR(CAM_JPEG, "op start null ");
+ rc = -EINVAL;
+ goto end_callcb;
+ }
+ rc = hw_mgr->devices[dev_type][0]->hw_ops.start(
+ hw_mgr->devices[dev_type][0]->hw_priv,
+ NULL, 0);
+ if (rc) {
+ CAM_ERR(CAM_JPEG, "Failed to apply the configs %d",
+ rc);
+ goto end_callcb;
}
return rc;
@@ -423,6 +510,12 @@
(uint64_t)p_cfg_req->hw_cfg_args.priv;
ctx_data->ctxt_event_cb(ctx_data->context_priv, 0, &buf_data);
}
+end_unusedev:
+ mutex_lock(&hw_mgr->hw_mgr_mutex);
+ hw_mgr->device_in_use[p_cfg_req->dev_type][0] = false;
+ hw_mgr->dev_hw_cfg_args[p_cfg_req->dev_type][0] = NULL;
+ mutex_unlock(&hw_mgr->hw_mgr_mutex);
+
end:
return rc;
@@ -535,6 +628,7 @@
struct cam_packet *packet = NULL;
struct cam_cmd_buf_desc *cmd_desc = NULL;
struct cam_buf_io_cfg *io_cfg_ptr = NULL;
+ struct cam_kmd_buf_info kmd_buf;
if (!prepare_args || !hw_mgr) {
CAM_ERR(CAM_JPEG, "Invalid args %pK %pK",
@@ -564,7 +658,17 @@
packet->header.op_code & 0xff);
return -EINVAL;
}
- if ((packet->num_cmd_buf > 2) || !packet->num_patches ||
+
+ /* if for backward compat */
+ if (packet->kmd_cmd_buf_index != -1) {
+ rc = cam_packet_util_validate_packet(packet);
+ if (rc) {
+ CAM_ERR(CAM_JPEG, "invalid packet %d", rc);
+ return rc;
+ }
+ }
+
+ if ((packet->num_cmd_buf > 5) || !packet->num_patches ||
!packet->num_io_configs) {
CAM_ERR(CAM_JPEG, "wrong number of cmd/patch info: %u %u",
packet->num_cmd_buf,
@@ -611,16 +715,35 @@
i, io_cfg_ptr[i].direction, io_cfg_ptr[i].fence);
}
- for (i = 0; i < packet->num_cmd_buf; i++) {
- prepare_args->hw_update_entries[i].len =
- (uint32_t)cmd_desc[i].length;
- prepare_args->hw_update_entries[i].handle =
- (uint32_t)cmd_desc[i].mem_handle;
- prepare_args->hw_update_entries[i].offset =
- (uint32_t)cmd_desc[i].offset;
- prepare_args->num_hw_update_entries++;
+ j = prepare_args->num_hw_update_entries;
+ /* if-else for backward compat */
+ if (packet->kmd_cmd_buf_index != -1) {
+ rc = cam_packet_util_get_kmd_buffer(packet, &kmd_buf);
+ if (rc) {
+ CAM_ERR(CAM_JPEG, "get kmd buf failed %d", rc);
+ return rc;
+ }
+ } else {
+ memset(&kmd_buf, 0x0, sizeof(kmd_buf));
}
+ /* fill kmd buf info into 1st hw update entry */
+ prepare_args->hw_update_entries[j].len =
+ (uint32_t)kmd_buf.used_bytes;
+ prepare_args->hw_update_entries[j].handle =
+ (uint32_t)kmd_buf.handle;
+ prepare_args->hw_update_entries[j].offset =
+ (uint32_t)kmd_buf.offset;
+ j++;
+ for (i = 0; i < packet->num_cmd_buf; i++, j++) {
+ prepare_args->hw_update_entries[j].len =
+ (uint32_t)cmd_desc[i].length;
+ prepare_args->hw_update_entries[j].handle =
+ (uint32_t)cmd_desc[i].mem_handle;
+ prepare_args->hw_update_entries[j].offset =
+ (uint32_t)cmd_desc[i].offset;
+ }
+ prepare_args->num_hw_update_entries = j;
prepare_args->priv = (void *)packet->header.request_id;
CAM_DBG(CAM_JPEG, "will wait on input sync sync_id %d",
@@ -651,6 +774,11 @@
dev_type = ctx_data->jpeg_dev_acquire_info.dev_type;
mutex_lock(&hw_mgr->hw_mgr_mutex);
+ if (hw_mgr->cdm_info[dev_type][0].ref_cnt == 0) {
+ mutex_unlock(&hw_mgr->hw_mgr_mutex);
+ CAM_ERR(CAM_JPEG, "Error Unbalanced deinit");
+ return -EFAULT;
+ }
hw_mgr->cdm_info[dev_type][0].ref_cnt--;
if (!(hw_mgr->cdm_info[dev_type][0].ref_cnt)) {
@@ -663,12 +791,6 @@
cam_cdm_release(hw_mgr->cdm_info[dev_type][0].cdm_handle);
}
- if (g_jpeg_hw_mgr.devices[dev_type][0]->hw_ops.deinit) {
- rc = g_jpeg_hw_mgr.devices[dev_type][0]->hw_ops.deinit(
- g_jpeg_hw_mgr.devices[dev_type][0]->hw_priv, NULL, 0);
- if (rc)
- CAM_ERR(CAM_JPEG, "Failed to Init %d HW", dev_type);
- }
mutex_unlock(&hw_mgr->hw_mgr_mutex);
rc = cam_jpeg_mgr_release_ctx(hw_mgr, ctx_data);
@@ -787,20 +909,6 @@
goto start_cdm_hdl_failed;
}
- if (!g_jpeg_hw_mgr.devices[dev_type][0]->hw_ops.init) {
- CAM_ERR(CAM_JPEG, "hw op init null ");
- rc = -EINVAL;
- goto start_cdm_hdl_failed;
- }
- rc = g_jpeg_hw_mgr.devices[dev_type][0]->hw_ops.init(
- g_jpeg_hw_mgr.devices[dev_type][0]->hw_priv,
- ctx_data,
- sizeof(ctx_data));
- if (rc) {
- CAM_ERR(CAM_JPEG, "Failed to Init %d HW", dev_type);
- goto start_cdm_hdl_failed;
- }
-
if (hw_mgr->cdm_info[dev_type][0].ref_cnt == 1)
if (cam_cdm_stream_on(
hw_mgr->cdm_info[dev_type][0].cdm_handle)) {
@@ -833,9 +941,12 @@
return rc;
copy_to_user_failed:
- cam_cdm_stream_off(hw_mgr->cdm_info[dev_type][0].cdm_handle);
+ if (hw_mgr->cdm_info[dev_type][0].ref_cnt == 1)
+ cam_cdm_stream_off(hw_mgr->cdm_info[dev_type][0].cdm_handle);
start_cdm_hdl_failed:
- cam_cdm_release(hw_mgr->cdm_info[dev_type][0].cdm_handle);
+ if (hw_mgr->cdm_info[dev_type][0].ref_cnt == 1)
+ cam_cdm_release(hw_mgr->cdm_info[dev_type][0].cdm_handle);
+ hw_mgr->cdm_info[dev_type][0].ref_cnt--;
acq_cdm_hdl_failed:
kfree(ctx_data->cdm_cmd);
cam_jpeg_mgr_release_ctx(hw_mgr, ctx_data);
diff --git a/drivers/media/platform/msm/camera/cam_jpeg/jpeg_hw/include/cam_enc_hw_intf.h b/drivers/media/platform/msm/camera/cam_jpeg/jpeg_hw/include/cam_enc_hw_intf.h
deleted file mode 100644
index f0b4e00..0000000
--- a/drivers/media/platform/msm/camera/cam_jpeg/jpeg_hw/include/cam_enc_hw_intf.h
+++ /dev/null
@@ -1,28 +0,0 @@
-/* Copyright (c) 2017, 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 CAM_JPEG_ENC_HW_INTF_H
-#define CAM_JPEG_ENC_HW_INTF_H
-
-#include <uapi/media/cam_defs.h>
-#include <media/cam_jpeg.h>
-
-#include "cam_hw_mgr_intf.h"
-#include "cam_jpeg_hw_intf.h"
-
-enum cam_jpeg_enc_cmd_type {
- CAM_JPEG_ENC_CMD_CDM_CFG,
- CAM_JPEG_ENC_CMD_SET_IRQ_CB,
- CAM_JPEG_ENC_CMD_MAX,
-};
-
-#endif /* CAM_JPEG_ENC_HW_INTF_H */
diff --git a/drivers/media/platform/msm/camera/cam_jpeg/jpeg_hw/include/cam_jpeg_hw_intf.h b/drivers/media/platform/msm/camera/cam_jpeg/jpeg_hw/include/cam_jpeg_hw_intf.h
index 3204388..44b134a 100644
--- a/drivers/media/platform/msm/camera/cam_jpeg/jpeg_hw/include/cam_jpeg_hw_intf.h
+++ b/drivers/media/platform/msm/camera/cam_jpeg/jpeg_hw/include/cam_jpeg_hw_intf.h
@@ -13,14 +13,32 @@
#ifndef CAM_JPEG_HW_INTF_H
#define CAM_JPEG_HW_INTF_H
+#include "cam_cpas_api.h"
+
#define CAM_JPEG_CTX_MAX 8
#define CAM_JPEG_DEV_PER_TYPE_MAX 1
#define CAM_JPEG_CMD_BUF_MAX_SIZE 128
#define CAM_JPEG_MSG_BUF_MAX_SIZE CAM_JPEG_CMD_BUF_MAX_SIZE
+#define JPEG_VOTE 640000000
+
enum cam_jpeg_hw_type {
CAM_JPEG_DEV_ENC,
CAM_JPEG_DEV_DMA,
};
+
+struct cam_jpeg_set_irq_cb {
+ int32_t (*jpeg_hw_mgr_cb)(uint32_t irq_status,
+ int32_t result_size, void *data);
+ void *data;
+ uint32_t b_set_cb;
+};
+
+enum cam_jpeg_cmd_type {
+ CAM_JPEG_CMD_CDM_CFG,
+ CAM_JPEG_CMD_SET_IRQ_CB,
+ CAM_JPEG_CMD_MAX,
+};
+
#endif
diff --git a/drivers/media/platform/msm/camera/cam_jpeg/jpeg_hw/include/cam_jpeg_hw_mgr_intf.h b/drivers/media/platform/msm/camera/cam_jpeg/jpeg_hw/include/cam_jpeg_hw_mgr_intf.h
index d5c8c9d..5fb4e3ad 100644
--- a/drivers/media/platform/msm/camera/cam_jpeg/jpeg_hw/include/cam_jpeg_hw_mgr_intf.h
+++ b/drivers/media/platform/msm/camera/cam_jpeg/jpeg_hw/include/cam_jpeg_hw_mgr_intf.h
@@ -17,34 +17,8 @@
#include <uapi/media/cam_defs.h>
#include <linux/of.h>
-#include "cam_cpas_api.h"
-
-#define JPEG_TURBO_VOTE 640000000
int cam_jpeg_hw_mgr_init(struct device_node *of_node,
uint64_t *hw_mgr_hdl);
-/**
- * struct cam_jpeg_cpas_vote
- * @ahb_vote: AHB vote info
- * @axi_vote: AXI vote info
- * @ahb_vote_valid: Flag for ahb vote data
- * @axi_vote_valid: Flag for axi vote data
- */
-struct cam_jpeg_cpas_vote {
- struct cam_ahb_vote ahb_vote;
- struct cam_axi_vote axi_vote;
- uint32_t ahb_vote_valid;
- uint32_t axi_vote_valid;
-};
-
-struct cam_jpeg_set_irq_cb {
- int32_t (*jpeg_hw_mgr_cb)(
- uint32_t irq_status,
- int32_t result_size,
- void *data);
- void *data;
- uint32_t b_set_cb;
-};
-
#endif /* CAM_JPEG_HW_MGR_INTF_H */
diff --git a/drivers/media/platform/msm/camera/cam_jpeg/jpeg_hw/jpeg_dma_hw/jpeg_dma_core.c b/drivers/media/platform/msm/camera/cam_jpeg/jpeg_hw/jpeg_dma_hw/jpeg_dma_core.c
index 0a15f71..2d343dd 100644
--- a/drivers/media/platform/msm/camera/cam_jpeg/jpeg_hw/jpeg_dma_hw/jpeg_dma_core.c
+++ b/drivers/media/platform/msm/camera/cam_jpeg/jpeg_hw/jpeg_dma_hw/jpeg_dma_core.c
@@ -26,7 +26,6 @@
#include "jpeg_dma_soc.h"
#include "cam_soc_util.h"
#include "cam_io_util.h"
-#include "cam_dma_hw_intf.h"
#include "cam_jpeg_hw_intf.h"
#include "cam_jpeg_hw_mgr_intf.h"
#include "cam_cpas_api.h"
@@ -38,7 +37,8 @@
struct cam_hw_info *jpeg_dma_dev = device_priv;
struct cam_hw_soc_info *soc_info = NULL;
struct cam_jpeg_dma_device_core_info *core_info = NULL;
- struct cam_jpeg_cpas_vote cpas_vote;
+ struct cam_ahb_vote ahb_vote;
+ struct cam_axi_vote axi_vote;
int rc;
if (!device_priv) {
@@ -57,20 +57,19 @@
return -EINVAL;
}
-
mutex_lock(&core_info->core_mutex);
if (++core_info->ref_count > 1) {
mutex_unlock(&core_info->core_mutex);
return 0;
}
- cpas_vote.ahb_vote.type = CAM_VOTE_ABSOLUTE;
- cpas_vote.ahb_vote.vote.level = CAM_SVS_VOTE;
- cpas_vote.axi_vote.compressed_bw = JPEG_TURBO_VOTE;
- cpas_vote.axi_vote.uncompressed_bw = JPEG_TURBO_VOTE;
+ ahb_vote.type = CAM_VOTE_ABSOLUTE;
+ ahb_vote.vote.level = CAM_SVS_VOTE;
+ axi_vote.compressed_bw = JPEG_VOTE;
+ axi_vote.uncompressed_bw = JPEG_VOTE;
rc = cam_cpas_start(core_info->cpas_handle,
- &cpas_vote.ahb_vote, &cpas_vote.axi_vote);
+ &ahb_vote, &axi_vote);
if (rc) {
CAM_ERR(CAM_JPEG, "cpass start failed: %d", rc);
goto cpas_failed;
@@ -91,6 +90,7 @@
cpas_failed:
--core_info->ref_count;
mutex_unlock(&core_info->core_mutex);
+
return rc;
}
@@ -154,7 +154,7 @@
return -EINVAL;
}
- if (cmd_type >= CAM_JPEG_DMA_CMD_MAX) {
+ if (cmd_type >= CAM_JPEG_CMD_MAX) {
CAM_ERR(CAM_JPEG, "Invalid command : %x", cmd_type);
return -EINVAL;
}
@@ -164,7 +164,7 @@
core_info;
switch (cmd_type) {
- case CAM_JPEG_DMA_CMD_SET_IRQ_CB:
+ case CAM_JPEG_CMD_SET_IRQ_CB:
{
struct cam_jpeg_set_irq_cb *irq_cb = cmd_args;
diff --git a/drivers/media/platform/msm/camera/cam_jpeg/jpeg_hw/jpeg_dma_hw/jpeg_dma_core.h b/drivers/media/platform/msm/camera/cam_jpeg/jpeg_hw/jpeg_dma_hw/jpeg_dma_core.h
index 1e0c2e2..a4d5d89 100644
--- a/drivers/media/platform/msm/camera/cam_jpeg/jpeg_hw/jpeg_dma_hw/jpeg_dma_core.h
+++ b/drivers/media/platform/msm/camera/cam_jpeg/jpeg_hw/jpeg_dma_hw/jpeg_dma_core.h
@@ -19,16 +19,12 @@
#include <linux/platform_device.h>
#include <linux/dma-buf.h>
+#include "cam_jpeg_hw_intf.h"
+
struct cam_jpeg_dma_device_hw_info {
uint32_t reserved;
};
-struct cam_jpeg_dma_set_irq_cb {
- int32_t (*jpeg_hw_mgr_cb)(uint32_t irq_status,
- int32_t result_size, void *data);
- void *data;
-};
-
enum cam_jpeg_dma_core_state {
CAM_JPEG_DMA_CORE_NOT_READY,
CAM_JPEG_DMA_CORE_READY,
@@ -40,7 +36,7 @@
enum cam_jpeg_dma_core_state core_state;
struct cam_jpeg_dma_device_hw_info *jpeg_dma_hw_info;
uint32_t cpas_handle;
- struct cam_jpeg_dma_set_irq_cb irq_cb;
+ struct cam_jpeg_set_irq_cb irq_cb;
int32_t ref_count;
struct mutex core_mutex;
};
diff --git a/drivers/media/platform/msm/camera/cam_jpeg/jpeg_hw/jpeg_enc_hw/cam_jpeg_enc_hw_info_ver_4_2_0.h b/drivers/media/platform/msm/camera/cam_jpeg/jpeg_hw/jpeg_enc_hw/cam_jpeg_enc_hw_info_ver_4_2_0.h
new file mode 100644
index 0000000..725af47
--- /dev/null
+++ b/drivers/media/platform/msm/camera/cam_jpeg/jpeg_hw/jpeg_enc_hw/cam_jpeg_enc_hw_info_ver_4_2_0.h
@@ -0,0 +1,74 @@
+/* Copyright (c) 2017, 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 CAM_JPEG_ENC_HW_INFO_TITAN170_H
+#define CAM_JPEG_ENC_HW_INFO_TITAN170_H
+
+#define CAM_JPEG_HW_IRQ_STATUS_FRAMEDONE_MASK 0x00000001
+#define CAM_JPEG_HW_IRQ_STATUS_FRAMEDONE_SHIFT 0x00000000
+
+#define CAM_JPEG_HW_IRQ_STATUS_RESET_ACK_MASK 0x10000000
+#define CAM_JPEG_HW_IRQ_STATUS_RESET_ACK_SHIFT 0x0000000a
+
+#define CAM_JPEG_HW_IRQ_STATUS_BUS_ERROR_MASK 0x00000800
+#define CAM_JPEG_HW_IRQ_STATUS_BUS_ERROR_SHIFT 0x0000000b
+
+#define CAM_JPEG_HW_IRQ_STATUS_DCD_UNESCAPED_FF (0x1<<19)
+#define CAM_JPEG_HW_IRQ_STATUS_DCD_HUFFMAN_ERROR (0x1<<20)
+#define CAM_JPEG_HW_IRQ_STATUS_DCD_COEFFICIENT_ERR (0x1<<21)
+#define CAM_JPEG_HW_IRQ_STATUS_DCD_MISSING_BIT_STUFF (0x1<<22)
+#define CAM_JPEG_HW_IRQ_STATUS_DCD_SCAN_UNDERFLOW (0x1<<23)
+#define CAM_JPEG_HW_IRQ_STATUS_DCD_INVALID_RSM (0x1<<24)
+#define CAM_JPEG_HW_IRQ_STATUS_DCD_INVALID_RSM_SEQ (0x1<<25)
+#define CAM_JPEG_HW_IRQ_STATUS_DCD_MISSING_RSM (0x1<<26)
+#define CAM_JPEG_HW_IRQ_STATUS_VIOLATION_MASK (0x1<<29)
+
+#define CAM_JPEG_HW_MASK_COMP_FRAMEDONE \
+ CAM_JPEG_HW_IRQ_STATUS_FRAMEDONE_MASK
+#define CAM_JPEG_HW_MASK_COMP_RESET_ACK \
+ CAM_JPEG_HW_IRQ_STATUS_RESET_ACK_MASK
+#define CAM_JPEG_HW_MASK_COMP_ERR \
+ (CAM_JPEG_HW_IRQ_STATUS_DCD_UNESCAPED_FF | \
+ CAM_JPEG_HW_IRQ_STATUS_DCD_HUFFMAN_ERROR | \
+ CAM_JPEG_HW_IRQ_STATUS_DCD_COEFFICIENT_ERR | \
+ CAM_JPEG_HW_IRQ_STATUS_DCD_MISSING_BIT_STUFF | \
+ CAM_JPEG_HW_IRQ_STATUS_DCD_SCAN_UNDERFLOW | \
+ CAM_JPEG_HW_IRQ_STATUS_DCD_INVALID_RSM | \
+ CAM_JPEG_HW_IRQ_STATUS_DCD_INVALID_RSM_SEQ | \
+ CAM_JPEG_HW_IRQ_STATUS_DCD_MISSING_RSM | \
+ CAM_JPEG_HW_IRQ_STATUS_VIOLATION_MASK)
+
+static struct cam_jpeg_enc_device_hw_info cam_jpeg_enc_hw_info = {
+ .reg_offset = {
+ .hw_version = 0x0,
+ .int_clr = 0x1c,
+ .int_status = 0x20,
+ .int_mask = 0x18,
+ .hw_cmd = 0x10,
+ .reset_cmd = 0x8,
+ .encode_size = 0x180,
+ },
+ .reg_val = {
+ .int_clr_clearall = 0xFFFFFFFF,
+ .int_mask_disable_all = 0x00000000,
+ .int_mask_enable_all = 0xFFFFFFFF,
+ .hw_cmd_start = 0x00000001,
+ .reset_cmd = 0x00032093,
+ },
+ .int_status = {
+ .framedone = CAM_JPEG_HW_MASK_COMP_FRAMEDONE,
+ .resetdone = CAM_JPEG_HW_MASK_COMP_RESET_ACK,
+ .iserror = CAM_JPEG_HW_MASK_COMP_ERR,
+ }
+};
+
+#endif /* CAM_JPEG_ENC_HW_INFO_TITAN170_H */
diff --git a/drivers/media/platform/msm/camera/cam_jpeg/jpeg_hw/jpeg_enc_hw/jpeg_enc_core.c b/drivers/media/platform/msm/camera/cam_jpeg/jpeg_hw/jpeg_enc_hw/jpeg_enc_core.c
index 06ad260..a7c4e06 100644
--- a/drivers/media/platform/msm/camera/cam_jpeg/jpeg_hw/jpeg_enc_hw/jpeg_enc_core.c
+++ b/drivers/media/platform/msm/camera/cam_jpeg/jpeg_hw/jpeg_enc_hw/jpeg_enc_core.c
@@ -26,52 +26,17 @@
#include "jpeg_enc_soc.h"
#include "cam_soc_util.h"
#include "cam_io_util.h"
-#include "cam_enc_hw_intf.h"
#include "cam_jpeg_hw_intf.h"
#include "cam_jpeg_hw_mgr_intf.h"
#include "cam_cpas_api.h"
#include "cam_debug_util.h"
-#define CAM_JPEG_HW_IRQ_STATUS_FRAMEDONE_MASK 0x00000001
-#define CAM_JPEG_HW_IRQ_STATUS_FRAMEDONE_SHIFT 0x00000000
-
-#define CAM_JPEG_HW_IRQ_STATUS_RESET_ACK_MASK 0x10000000
-#define CAM_JPEG_HW_IRQ_STATUS_RESET_ACK_SHIFT 0x0000000a
-
-#define CAM_JPEG_HW_IRQ_STATUS_BUS_ERROR_MASK 0x00000800
-#define CAM_JPEG_HW_IRQ_STATUS_BUS_ERROR_SHIFT 0x0000000b
-
-#define CAM_JPEG_HW_IRQ_STATUS_DCD_UNESCAPED_FF (0x1<<19)
-#define CAM_JPEG_HW_IRQ_STATUS_DCD_HUFFMAN_ERROR (0x1<<20)
-#define CAM_JPEG_HW_IRQ_STATUS_DCD_COEFFICIENT_ERR (0x1<<21)
-#define CAM_JPEG_HW_IRQ_STATUS_DCD_MISSING_BIT_STUFF (0x1<<22)
-#define CAM_JPEG_HW_IRQ_STATUS_DCD_SCAN_UNDERFLOW (0x1<<23)
-#define CAM_JPEG_HW_IRQ_STATUS_DCD_INVALID_RSM (0x1<<24)
-#define CAM_JPEG_HW_IRQ_STATUS_DCD_INVALID_RSM_SEQ (0x1<<25)
-#define CAM_JPEG_HW_IRQ_STATUS_DCD_MISSING_RSM (0x1<<26)
-#define CAM_JPEG_HW_IRQ_STATUS_VIOLATION_MASK (0x1<<29)
-
-#define CAM_JPEG_HW_MASK_COMP_FRAMEDONE \
- CAM_JPEG_HW_IRQ_STATUS_FRAMEDONE_MASK
-#define CAM_JPEG_HW_MASK_COMP_RESET_ACK \
- CAM_JPEG_HW_IRQ_STATUS_RESET_ACK_MASK
-#define CAM_JPEG_HW_MASK_COMP_ERR \
- (CAM_JPEG_HW_IRQ_STATUS_DCD_UNESCAPED_FF | \
- CAM_JPEG_HW_IRQ_STATUS_DCD_HUFFMAN_ERROR | \
- CAM_JPEG_HW_IRQ_STATUS_DCD_COEFFICIENT_ERR | \
- CAM_JPEG_HW_IRQ_STATUS_DCD_MISSING_BIT_STUFF | \
- CAM_JPEG_HW_IRQ_STATUS_DCD_SCAN_UNDERFLOW | \
- CAM_JPEG_HW_IRQ_STATUS_DCD_INVALID_RSM | \
- CAM_JPEG_HW_IRQ_STATUS_DCD_INVALID_RSM_SEQ | \
- CAM_JPEG_HW_IRQ_STATUS_DCD_MISSING_RSM | \
- CAM_JPEG_HW_IRQ_STATUS_VIOLATION_MASK)
-
-#define CAM_JPEG_HW_IRQ_IS_FRAME_DONE(jpeg_irq_status) \
- (jpeg_irq_status & CAM_JPEG_HW_MASK_COMP_FRAMEDONE)
-#define CAM_JPEG_HW_IRQ_IS_RESET_ACK(jpeg_irq_status) \
- (jpeg_irq_status & CAM_JPEG_HW_MASK_COMP_RESET_ACK)
-#define CAM_JPEG_HW_IRQ_IS_ERR(jpeg_irq_status) \
- (jpeg_irq_status & CAM_JPEG_HW_MASK_COMP_ERR)
+#define CAM_JPEG_HW_IRQ_IS_FRAME_DONE(jpeg_irq_status, hi) \
+ ((jpeg_irq_status) & (hi)->int_status.framedone)
+#define CAM_JPEG_HW_IRQ_IS_RESET_ACK(jpeg_irq_status, hi) \
+ ((jpeg_irq_status) & (hi)->int_status.resetdone)
+#define CAM_JPEG_HW_IRQ_IS_ERR(jpeg_irq_status, hi) \
+ ((jpeg_irq_status) & (hi)->int_status.iserror)
#define CAM_JPEG_ENC_RESET_TIMEOUT msecs_to_jiffies(500)
@@ -81,7 +46,8 @@
struct cam_hw_info *jpeg_enc_dev = device_priv;
struct cam_hw_soc_info *soc_info = NULL;
struct cam_jpeg_enc_device_core_info *core_info = NULL;
- struct cam_jpeg_cpas_vote cpas_vote;
+ struct cam_ahb_vote ahb_vote;
+ struct cam_axi_vote axi_vote;
int rc;
if (!device_priv) {
@@ -100,20 +66,19 @@
return -EINVAL;
}
-
mutex_lock(&core_info->core_mutex);
if (++core_info->ref_count > 1) {
mutex_unlock(&core_info->core_mutex);
return 0;
}
- cpas_vote.ahb_vote.type = CAM_VOTE_ABSOLUTE;
- cpas_vote.ahb_vote.vote.level = CAM_SVS_VOTE;
- cpas_vote.axi_vote.compressed_bw = JPEG_TURBO_VOTE;
- cpas_vote.axi_vote.uncompressed_bw = JPEG_TURBO_VOTE;
+ ahb_vote.type = CAM_VOTE_ABSOLUTE;
+ ahb_vote.vote.level = CAM_SVS_VOTE;
+ axi_vote.compressed_bw = JPEG_VOTE;
+ axi_vote.uncompressed_bw = JPEG_VOTE;
rc = cam_cpas_start(core_info->cpas_handle,
- &cpas_vote.ahb_vote, &cpas_vote.axi_vote);
+ &ahb_vote, &axi_vote);
if (rc) {
CAM_ERR(CAM_JPEG, "cpass start failed: %d", rc);
goto cpas_failed;
@@ -134,6 +99,7 @@
cpas_failed:
--core_info->ref_count;
mutex_unlock(&core_info->core_mutex);
+
return rc;
}
@@ -174,7 +140,7 @@
rc = cam_jpeg_enc_disable_soc_resources(soc_info);
if (rc)
- CAM_ERR(CAM_JPEG, "soc enable failed %d", rc);
+ CAM_ERR(CAM_JPEG, "soc disable failed %d", rc);
rc = cam_cpas_stop(core_info->cpas_handle);
if (rc)
@@ -207,17 +173,19 @@
mem_base = soc_info->reg_map[0].mem_base;
irq_status = cam_io_r_mb(mem_base +
- core_info->jpeg_enc_hw_info->int_status);
+ core_info->jpeg_enc_hw_info->reg_offset.int_status);
cam_io_w_mb(irq_status,
soc_info->reg_map[0].mem_base +
- core_info->jpeg_enc_hw_info->int_clr);
+ core_info->jpeg_enc_hw_info->reg_offset.int_clr);
CAM_DBG(CAM_JPEG, "irq_num %d irq_status = %x , core_state %d",
irq_num, irq_status, core_info->core_state);
- if (CAM_JPEG_HW_IRQ_IS_FRAME_DONE(irq_status)) {
+ if (CAM_JPEG_HW_IRQ_IS_FRAME_DONE(irq_status, hw_info)) {
if (core_info->core_state == CAM_JPEG_ENC_CORE_READY) {
- encoded_size = cam_io_r_mb(mem_base + 0x180);
+ encoded_size = cam_io_r_mb(mem_base +
+ core_info->jpeg_enc_hw_info->reg_offset.
+ encode_size);
if (core_info->irq_cb.jpeg_hw_mgr_cb) {
core_info->irq_cb.jpeg_hw_mgr_cb(irq_status,
encoded_size,
@@ -229,7 +197,7 @@
core_info->core_state = CAM_JPEG_ENC_CORE_NOT_READY;
}
- if (CAM_JPEG_HW_IRQ_IS_RESET_ACK(irq_status)) {
+ if (CAM_JPEG_HW_IRQ_IS_RESET_ACK(irq_status, hw_info)) {
if (core_info->core_state == CAM_JPEG_ENC_CORE_RESETTING) {
core_info->core_state = CAM_JPEG_ENC_CORE_READY;
complete(&jpeg_enc_dev->hw_complete);
@@ -238,7 +206,7 @@
}
}
/* Unexpected/unintended HW interrupt */
- if (CAM_JPEG_HW_IRQ_IS_ERR(irq_status)) {
+ if (CAM_JPEG_HW_IRQ_IS_ERR(irq_status, hw_info)) {
core_info->core_state = CAM_JPEG_ENC_CORE_NOT_READY;
CAM_ERR_RATE_LIMIT(CAM_JPEG,
"error irq_num %d irq_status = %x , core_state %d",
@@ -285,10 +253,14 @@
core_info->core_state = CAM_JPEG_ENC_CORE_RESETTING;
- cam_io_w_mb(0x00000000, mem_base + hw_info->int_mask);
- cam_io_w_mb(0xFFFFFFFF, mem_base + hw_info->int_clr);
- cam_io_w_mb(0xFFFFFFFF, mem_base + hw_info->int_mask);
- cam_io_w_mb(0x00032093, mem_base + hw_info->reset_cmd);
+ cam_io_w_mb(hw_info->reg_val.int_mask_disable_all,
+ mem_base + hw_info->reg_offset.int_mask);
+ cam_io_w_mb(hw_info->reg_val.int_clr_clearall,
+ mem_base + hw_info->reg_offset.int_clr);
+ cam_io_w_mb(hw_info->reg_val.int_mask_enable_all,
+ mem_base + hw_info->reg_offset.int_mask);
+ cam_io_w_mb(hw_info->reg_val.reset_cmd,
+ mem_base + hw_info->reg_offset.reset_cmd);
rem_jiffies = wait_for_completion_timeout(&jpeg_enc_dev->hw_complete,
CAM_JPEG_ENC_RESET_TIMEOUT);
@@ -325,7 +297,8 @@
return -EINVAL;
}
- cam_io_w_mb(0x00000001, mem_base + 0x00000010);
+ cam_io_w_mb(hw_info->reg_val.hw_cmd_start,
+ mem_base + hw_info->reg_offset.hw_cmd);
return 0;
}
@@ -342,7 +315,7 @@
return -EINVAL;
}
- if (cmd_type >= CAM_JPEG_ENC_CMD_MAX) {
+ if (cmd_type >= CAM_JPEG_CMD_MAX) {
CAM_ERR(CAM_JPEG, "Invalid command : %x", cmd_type);
return -EINVAL;
}
@@ -352,7 +325,7 @@
core_info;
switch (cmd_type) {
- case CAM_JPEG_ENC_CMD_SET_IRQ_CB:
+ case CAM_JPEG_CMD_SET_IRQ_CB:
{
struct cam_jpeg_set_irq_cb *irq_cb = cmd_args;
diff --git a/drivers/media/platform/msm/camera/cam_jpeg/jpeg_hw/jpeg_enc_hw/jpeg_enc_core.h b/drivers/media/platform/msm/camera/cam_jpeg/jpeg_hw/jpeg_enc_hw/jpeg_enc_core.h
index eb5caef..4f5d625 100644
--- a/drivers/media/platform/msm/camera/cam_jpeg/jpeg_hw/jpeg_enc_hw/jpeg_enc_core.h
+++ b/drivers/media/platform/msm/camera/cam_jpeg/jpeg_hw/jpeg_enc_hw/jpeg_enc_core.h
@@ -19,18 +19,36 @@
#include <linux/platform_device.h>
#include <linux/dma-buf.h>
-struct cam_jpeg_enc_device_hw_info {
+#include "cam_jpeg_hw_intf.h"
+
+struct cam_jpeg_enc_reg_offsets {
uint32_t hw_version;
uint32_t int_status;
uint32_t int_clr;
uint32_t int_mask;
+ uint32_t hw_cmd;
+ uint32_t reset_cmd;
+ uint32_t encode_size;
+};
+
+struct cam_jpeg_enc_regval {
+ uint32_t int_clr_clearall;
+ uint32_t int_mask_disable_all;
+ uint32_t int_mask_enable_all;
+ uint32_t hw_cmd_start;
uint32_t reset_cmd;
};
-struct cam_jpeg_enc_set_irq_cb {
- int32_t (*jpeg_hw_mgr_cb)(uint32_t irq_status,
- int32_t result_size, void *data);
- void *data;
+struct cam_jpeg_enc_int_status {
+ uint32_t framedone;
+ uint32_t resetdone;
+ uint32_t iserror;
+};
+
+struct cam_jpeg_enc_device_hw_info {
+ struct cam_jpeg_enc_reg_offsets reg_offset;
+ struct cam_jpeg_enc_regval reg_val;
+ struct cam_jpeg_enc_int_status int_status;
};
enum cam_jpeg_enc_core_state {
@@ -44,7 +62,7 @@
enum cam_jpeg_enc_core_state core_state;
struct cam_jpeg_enc_device_hw_info *jpeg_enc_hw_info;
uint32_t cpas_handle;
- struct cam_jpeg_enc_set_irq_cb irq_cb;
+ struct cam_jpeg_set_irq_cb irq_cb;
int32_t ref_count;
struct mutex core_mutex;
};
diff --git a/drivers/media/platform/msm/camera/cam_jpeg/jpeg_hw/jpeg_enc_hw/jpeg_enc_dev.c b/drivers/media/platform/msm/camera/cam_jpeg/jpeg_hw/jpeg_enc_hw/jpeg_enc_dev.c
index 570d9f9..735bd21 100644
--- a/drivers/media/platform/msm/camera/cam_jpeg/jpeg_hw/jpeg_enc_hw/jpeg_enc_dev.c
+++ b/drivers/media/platform/msm/camera/cam_jpeg/jpeg_hw/jpeg_enc_hw/jpeg_enc_dev.c
@@ -25,15 +25,7 @@
#include "cam_jpeg_hw_mgr_intf.h"
#include "cam_cpas_api.h"
#include "cam_debug_util.h"
-
-static struct cam_jpeg_enc_device_hw_info cam_jpeg_enc_hw_info = {
- .int_clr = 0x1c,
- .int_status = 0x20,
- .int_mask = 0x18,
- .reset_cmd = 0x8,
- .hw_version = 0x0,
-};
-EXPORT_SYMBOL(cam_jpeg_enc_hw_info);
+#include "cam_jpeg_enc_hw_info_ver_4_2_0.h"
static int cam_jpeg_enc_register_cpas(struct cam_hw_soc_info *soc_info,
struct cam_jpeg_enc_device_core_info *core_info,
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 9d454e9..88efcb5 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
@@ -115,56 +115,6 @@
return rc;
}
-static int cam_mem_mgr_cleanup_table(void)
-{
- int i;
-
- mutex_lock(&tbl.m_lock);
- for (i = 1; i < CAM_MEM_BUFQ_MAX; i++) {
- if (!tbl.bufq[i].active) {
- CAM_DBG(CAM_CRM,
- "Buffer inactive at idx=%d, continuing", i);
- continue;
- } else {
- CAM_ERR(CAM_CRM,
- "Active buffer at idx=%d, possible leak", i);
- }
-
- mutex_lock(&tbl.bufq[i].q_lock);
- ion_free(tbl.client, tbl.bufq[i].i_hdl);
- tbl.bufq[i].fd = -1;
- tbl.bufq[i].flags = 0;
- tbl.bufq[i].buf_handle = -1;
- tbl.bufq[i].vaddr = 0;
- tbl.bufq[i].len = 0;
- memset(tbl.bufq[i].hdls, 0,
- sizeof(int32_t) * tbl.bufq[i].num_hdl);
- tbl.bufq[i].num_hdl = 0;
- tbl.bufq[i].i_hdl = NULL;
- tbl.bufq[i].active = false;
- mutex_unlock(&tbl.bufq[i].q_lock);
- mutex_destroy(&tbl.bufq[i].q_lock);
- }
- bitmap_zero(tbl.bitmap, tbl.bits);
- /* We need to reserve slot 0 because 0 is invalid */
- set_bit(0, tbl.bitmap);
- mutex_unlock(&tbl.m_lock);
-
- return 0;
-}
-
-void cam_mem_mgr_deinit(void)
-{
- cam_mem_mgr_cleanup_table();
- mutex_lock(&tbl.m_lock);
- bitmap_zero(tbl.bitmap, tbl.bits);
- kfree(tbl.bitmap);
- tbl.bitmap = NULL;
- cam_mem_util_client_destroy();
- mutex_unlock(&tbl.m_lock);
- mutex_destroy(&tbl.m_lock);
-}
-
static int32_t cam_mem_get_slot(void)
{
int32_t idx;
@@ -734,6 +684,70 @@
return rc;
}
+static void cam_mem_mgr_unmap_active_buf(int idx)
+{
+ enum cam_smmu_region_id region = CAM_SMMU_REGION_SHARED;
+
+ if (tbl.bufq[idx].flags & CAM_MEM_FLAG_HW_SHARED_ACCESS)
+ region = CAM_SMMU_REGION_SHARED;
+ else if (tbl.bufq[idx].flags & CAM_MEM_FLAG_HW_READ_WRITE)
+ region = CAM_SMMU_REGION_IO;
+
+ cam_mem_util_unmap_hw_va(idx, region);
+}
+
+static int cam_mem_mgr_cleanup_table(void)
+{
+ int i;
+
+ mutex_lock(&tbl.m_lock);
+ for (i = 1; i < CAM_MEM_BUFQ_MAX; i++) {
+ if (!tbl.bufq[i].active) {
+ CAM_DBG(CAM_CRM,
+ "Buffer inactive at idx=%d, continuing", i);
+ continue;
+ } else {
+ CAM_INFO(CAM_CRM,
+ "Active buffer at idx=%d, possible leak needs unmapping",
+ i);
+ cam_mem_mgr_unmap_active_buf(i);
+ }
+
+ mutex_lock(&tbl.bufq[i].q_lock);
+ ion_free(tbl.client, tbl.bufq[i].i_hdl);
+ tbl.bufq[i].fd = -1;
+ tbl.bufq[i].flags = 0;
+ tbl.bufq[i].buf_handle = -1;
+ tbl.bufq[i].vaddr = 0;
+ tbl.bufq[i].len = 0;
+ memset(tbl.bufq[i].hdls, 0,
+ sizeof(int32_t) * tbl.bufq[i].num_hdl);
+ tbl.bufq[i].num_hdl = 0;
+ tbl.bufq[i].i_hdl = NULL;
+ tbl.bufq[i].active = false;
+ mutex_unlock(&tbl.bufq[i].q_lock);
+ mutex_destroy(&tbl.bufq[i].q_lock);
+ }
+ bitmap_zero(tbl.bitmap, tbl.bits);
+ /* We need to reserve slot 0 because 0 is invalid */
+ set_bit(0, tbl.bitmap);
+ mutex_unlock(&tbl.m_lock);
+
+ return 0;
+}
+
+void cam_mem_mgr_deinit(void)
+{
+ cam_mem_mgr_cleanup_table();
+ mutex_lock(&tbl.m_lock);
+ bitmap_zero(tbl.bitmap, tbl.bits);
+ kfree(tbl.bitmap);
+ tbl.bitmap = NULL;
+ cam_mem_util_client_destroy();
+ mutex_unlock(&tbl.m_lock);
+ mutex_destroy(&tbl.m_lock);
+}
+
static int cam_mem_util_unmap(int32_t idx)
{
int rc = 0;
diff --git a/drivers/media/platform/msm/camera/cam_req_mgr/cam_req_mgr_core.c b/drivers/media/platform/msm/camera/cam_req_mgr/cam_req_mgr_core.c
index 8565ad1..a6b097d 100644
--- a/drivers/media/platform/msm/camera/cam_req_mgr/cam_req_mgr_core.c
+++ b/drivers/media/platform/msm/camera/cam_req_mgr/cam_req_mgr_core.c
@@ -21,9 +21,27 @@
#include "cam_req_mgr_debug.h"
#include "cam_trace.h"
#include "cam_debug_util.h"
+#include "cam_req_mgr_dev.h"
static struct cam_req_mgr_core_device *g_crm_core_dev;
+
+void cam_req_mgr_handle_core_shutdown(void)
+{
+ struct cam_req_mgr_core_session *session;
+ struct cam_req_mgr_core_session *tsession;
+ struct cam_req_mgr_session_info ses_info;
+
+ if (!list_empty(&g_crm_core_dev->session_head)) {
+ list_for_each_entry_safe(session, tsession,
+ &g_crm_core_dev->session_head, entry) {
+ ses_info.session_hdl =
+ session->session_hdl;
+ cam_req_mgr_destroy_session(&ses_info);
+ }
+ }
+}
+
static int __cam_req_mgr_setup_payload(struct cam_req_mgr_core_workq *workq)
{
int32_t i = 0;
diff --git a/drivers/media/platform/msm/camera/cam_req_mgr/cam_req_mgr_core.h b/drivers/media/platform/msm/camera/cam_req_mgr/cam_req_mgr_core.h
index 3687a5a..db34157 100644
--- a/drivers/media/platform/msm/camera/cam_req_mgr/cam_req_mgr_core.h
+++ b/drivers/media/platform/msm/camera/cam_req_mgr/cam_req_mgr_core.h
@@ -406,5 +406,11 @@
* @brief: cleanp crm core
*/
int cam_req_mgr_core_device_deinit(void);
+
+/**
+ * cam_req_mgr_handle_core_shutdown()
+ * @brief: Handles camera close
+ */
+void cam_req_mgr_handle_core_shutdown(void);
#endif
diff --git a/drivers/media/platform/msm/camera/cam_req_mgr/cam_req_mgr_dev.c b/drivers/media/platform/msm/camera/cam_req_mgr/cam_req_mgr_dev.c
index e4bc98f..c316dbb 100644
--- a/drivers/media/platform/msm/camera/cam_req_mgr/cam_req_mgr_dev.c
+++ b/drivers/media/platform/msm/camera/cam_req_mgr/cam_req_mgr_dev.c
@@ -151,9 +151,6 @@
static int cam_req_mgr_close(struct file *filep)
{
- struct v4l2_subdev *sd;
- struct cam_control cam_ctrl;
-
mutex_lock(&g_dev.cam_lock);
if (g_dev.open_cnt <= 0) {
@@ -161,14 +158,7 @@
return -EINVAL;
}
- cam_ctrl.op_code = CAM_SD_SHUTDOWN;
- list_for_each_entry(sd, &g_dev.v4l2_dev->subdevs, list) {
- if (!(sd->flags & V4L2_SUBDEV_FL_HAS_DEVNODE))
- continue;
- v4l2_subdev_call(sd, core, ioctl, VIDIOC_CAM_CONTROL,
- &cam_ctrl);
- }
-
+ cam_req_mgr_handle_core_shutdown();
g_dev.open_cnt--;
v4l2_fh_release(filep);
diff --git a/drivers/media/platform/msm/camera/cam_sensor_module/cam_actuator/cam_actuator_core.c b/drivers/media/platform/msm/camera/cam_sensor_module/cam_actuator/cam_actuator_core.c
index c3475b6..85db1b1 100644
--- a/drivers/media/platform/msm/camera/cam_sensor_module/cam_actuator/cam_actuator_core.c
+++ b/drivers/media/platform/msm/camera/cam_sensor_module/cam_actuator/cam_actuator_core.c
@@ -317,7 +317,7 @@
cmd_buf += (sizeof(struct cam_cmd_i2c_info)/sizeof(uint32_t));
i2c_data->init_settings.request_id = 0;
i2c_reg_settings->is_settings_valid = 1;
- rc = cam_sensor_i2c_pkt_parser(i2c_reg_settings,
+ rc = cam_sensor_i2c_command_parser(i2c_reg_settings,
&cmd_desc[1], 1);
if (rc < 0) {
CAM_ERR(CAM_ACTUATOR, "Actuator pkt parsing failed: %d",
@@ -326,7 +326,7 @@
}
} else if ((csl_packet->header.op_code & 0xFFFFFF) ==
CAM_ACTUATOR_PACKET_AUTO_MOVE_LENS) {
- a_ctrl->act_apply_state =
+ a_ctrl->setting_apply_state =
ACT_APPLY_SETTINGS_NOW;
i2c_data = &(a_ctrl->i2c_data);
@@ -338,7 +338,7 @@
offset = (uint32_t *)&csl_packet->payload;
offset += csl_packet->cmd_buf_offset / sizeof(uint32_t);
cmd_desc = (struct cam_cmd_buf_desc *)(offset);
- rc = cam_sensor_i2c_pkt_parser(i2c_reg_settings,
+ rc = cam_sensor_i2c_command_parser(i2c_reg_settings,
cmd_desc, 1);
if (rc < 0) {
CAM_ERR(CAM_ACTUATOR, "Actuator pkt parsing failed: %d",
@@ -358,7 +358,7 @@
offset = (uint32_t *)&csl_packet->payload;
offset += csl_packet->cmd_buf_offset / sizeof(uint32_t);
cmd_desc = (struct cam_cmd_buf_desc *)(offset);
- rc = cam_sensor_i2c_pkt_parser(i2c_reg_settings,
+ rc = cam_sensor_i2c_command_parser(i2c_reg_settings,
cmd_desc, 1);
if (rc < 0) {
CAM_ERR(CAM_ACTUATOR, "Actuator pkt parsing failed: %d",
@@ -401,12 +401,16 @@
return -EINVAL;
}
- if (config)
+ if (config) {
+ rc = cam_soc_util_request_platform_resource(soc_info,
+ NULL, NULL);
rc = cam_soc_util_enable_platform_resource(soc_info, false, 0,
false);
- else
+ } else {
rc = cam_soc_util_disable_platform_resource(soc_info, false,
false);
+ rc = cam_soc_util_release_platform_resource(soc_info);
+ }
return rc;
}
@@ -429,15 +433,6 @@
if (soc_info->gpio_data &&
gpio_num_info &&
gpio_num_info->valid[SENSOR_VAF] == 1) {
- rc = cam_soc_util_request_platform_resource(&a_ctrl->soc_info,
- NULL, NULL);
- rc = cam_soc_util_enable_platform_resource(&a_ctrl->soc_info,
- false, 0, false);
- if (rc < 0) {
- CAM_ERR(CAM_ACTUATOR, "Failed in req gpio: %d", rc);
- return rc;
- }
-
gpio_set_value_cansleep(
gpio_num_info->gpio_num[SENSOR_VAF],
1);
@@ -456,12 +451,6 @@
&a_ctrl->soc_info;
struct msm_camera_gpio_num_info *gpio_num_info = NULL;
- rc = cam_actuator_vreg_control(a_ctrl, 0);
- if (rc < 0) {
- CAM_ERR(CAM_ACTUATOR, "Failed %d");
- return rc;
- }
-
gpio_num_info = a_ctrl->gpio_num_info;
if (soc_info->gpio_data &&
@@ -471,18 +460,43 @@
gpio_set_value_cansleep(
gpio_num_info->gpio_num[SENSOR_VAF],
GPIOF_OUT_INIT_LOW);
-
- rc = cam_soc_util_release_platform_resource(&a_ctrl->soc_info);
- rc |= cam_soc_util_disable_platform_resource(&a_ctrl->soc_info,
- 0, 0);
- if (rc < 0)
- CAM_ERR(CAM_ACTUATOR,
- "Failed to disable platform resources: %d", rc);
}
+ rc = cam_actuator_vreg_control(a_ctrl, 0);
+ if (rc < 0)
+ CAM_ERR(CAM_ACTUATOR, "Disable Regulator Failed: %d", rc);
+
return rc;
}
+void cam_actuator_shutdown(struct cam_actuator_ctrl_t *a_ctrl)
+{
+ int rc;
+
+ if (a_ctrl->cam_act_state == CAM_ACTUATOR_INIT)
+ return;
+
+ if (a_ctrl->cam_act_state == CAM_ACTUATOR_START) {
+ rc = camera_io_release(&a_ctrl->io_master_info);
+ if (rc < 0)
+ CAM_ERR(CAM_ACTUATOR, "Failed in releasing CCI");
+ rc = cam_actuator_power_down(a_ctrl);
+ if (rc < 0)
+ CAM_ERR(CAM_ACTUATOR, "Actuator Power down failed");
+ a_ctrl->cam_act_state = CAM_ACTUATOR_ACQUIRE;
+ }
+
+ if (a_ctrl->cam_act_state == CAM_ACTUATOR_ACQUIRE) {
+ rc = cam_destroy_device_hdl(a_ctrl->bridge_intf.device_hdl);
+ if (rc < 0)
+ CAM_ERR(CAM_ACTUATOR, "destroying dhdl failed");
+ a_ctrl->bridge_intf.device_hdl = -1;
+ a_ctrl->bridge_intf.link_hdl = -1;
+ a_ctrl->bridge_intf.session_hdl = -1;
+ }
+ a_ctrl->cam_act_state = CAM_ACTUATOR_INIT;
+}
+
int32_t cam_actuator_driver_cmd(struct cam_actuator_ctrl_t *a_ctrl,
void *arg)
{
@@ -535,7 +549,7 @@
rc = -EFAULT;
goto release_mutex;
}
-
+ a_ctrl->cam_act_state = CAM_ACTUATOR_ACQUIRE;
}
break;
case CAM_RELEASE_DEV: {
@@ -552,6 +566,7 @@
a_ctrl->bridge_intf.device_hdl = -1;
a_ctrl->bridge_intf.link_hdl = -1;
a_ctrl->bridge_intf.session_hdl = -1;
+ a_ctrl->cam_act_state = CAM_ACTUATOR_INIT;
}
break;
case CAM_QUERY_CAP: {
@@ -591,9 +606,13 @@
rc = -EINVAL;
goto release_mutex;
}
+ a_ctrl->cam_act_state = CAM_ACTUATOR_START;
}
break;
case CAM_STOP_DEV: {
+ struct i2c_settings_array *i2c_set = NULL;
+ int i;
+
rc = camera_io_release(&a_ctrl->io_master_info);
if (rc < 0)
CAM_ERR(CAM_ACTUATOR, "Failed in releasing CCI");
@@ -602,17 +621,29 @@
CAM_ERR(CAM_ACTUATOR, "Actuator Power down failed");
goto release_mutex;
}
+ for (i = 0; i < MAX_PER_FRAME_ARRAY; i++) {
+ i2c_set = &(a_ctrl->i2c_data.per_frame[i]);
+
+ if (i2c_set->is_settings_valid == 1) {
+ rc = delete_request(i2c_set);
+ if (rc < 0)
+ CAM_ERR(CAM_SENSOR,
+ "delete request: %lld rc: %d",
+ i2c_set->request_id, rc);
+ }
+ }
+ a_ctrl->cam_act_state = CAM_ACTUATOR_ACQUIRE;
}
break;
case CAM_CONFIG_DEV: {
- a_ctrl->act_apply_state =
+ a_ctrl->setting_apply_state =
ACT_APPLY_SETTINGS_LATER;
rc = cam_actuator_i2c_pkt_parse(a_ctrl, arg);
if (rc < 0) {
CAM_ERR(CAM_ACTUATOR, "Failed in actuator Parsing");
}
- if (a_ctrl->act_apply_state ==
+ if (a_ctrl->setting_apply_state ==
ACT_APPLY_SETTINGS_NOW) {
rc = cam_actuator_apply_settings(a_ctrl,
&a_ctrl->i2c_data.init_settings);
@@ -631,8 +662,6 @@
}
}
break;
- case CAM_SD_SHUTDOWN:
- break;
default:
CAM_ERR(CAM_ACTUATOR, "Invalid Opcode %d", cmd->op_code);
}
@@ -642,3 +671,50 @@
return rc;
}
+
+int32_t cam_actuator_flush_request(struct cam_req_mgr_flush_request *flush_req)
+{
+ int32_t rc = 0, i;
+ uint32_t cancel_req_id_found = 0;
+ struct cam_actuator_ctrl_t *a_ctrl = NULL;
+ struct i2c_settings_array *i2c_set = NULL;
+
+ if (!flush_req)
+ return -EINVAL;
+
+ a_ctrl = (struct cam_actuator_ctrl_t *)
+ cam_get_device_priv(flush_req->dev_hdl);
+ if (!a_ctrl) {
+ CAM_ERR(CAM_ACTUATOR, "Device data is NULL");
+ return -EINVAL;
+ }
+
+ for (i = 0; i < MAX_PER_FRAME_ARRAY; i++) {
+ i2c_set = &(a_ctrl->i2c_data.per_frame[i]);
+
+ if ((flush_req->type == CAM_REQ_MGR_FLUSH_TYPE_CANCEL_REQ)
+ && (i2c_set->request_id != flush_req->req_id))
+ continue;
+
+ if (i2c_set->is_settings_valid == 1) {
+ rc = delete_request(i2c_set);
+ if (rc < 0)
+ CAM_ERR(CAM_ACTUATOR,
+ "delete request: %lld rc: %d",
+ i2c_set->request_id, rc);
+
+ if (flush_req->type ==
+ CAM_REQ_MGR_FLUSH_TYPE_CANCEL_REQ) {
+ cancel_req_id_found = 1;
+ break;
+ }
+ }
+ }
+
+ if (flush_req->type == CAM_REQ_MGR_FLUSH_TYPE_CANCEL_REQ &&
+ !cancel_req_id_found)
+ CAM_DBG(CAM_ACTUATOR,
+ "Flush request id:%lld not found in the pending list",
+ flush_req->req_id);
+ return rc;
+}
diff --git a/drivers/media/platform/msm/camera/cam_sensor_module/cam_actuator/cam_actuator_core.h b/drivers/media/platform/msm/camera/cam_sensor_module/cam_actuator/cam_actuator_core.h
index d2cb96d..f24070e 100644
--- a/drivers/media/platform/msm/camera/cam_sensor_module/cam_actuator/cam_actuator_core.h
+++ b/drivers/media/platform/msm/camera/cam_sensor_module/cam_actuator/cam_actuator_core.h
@@ -30,6 +30,14 @@
int32_t cam_actuator_publish_dev_info(struct cam_req_mgr_device_info *info);
/**
+ * @flush: Req mgr structure for flushing request
+ *
+ * This API flushes the request that is mentioned
+ */
+int cam_actuator_flush_request(struct cam_req_mgr_flush_request *flush);
+
+
+/**
* @link: Link setup info
*
* This API establishes link actuator subdevice with req mgr
@@ -45,4 +53,11 @@
*/
int32_t cam_actuator_driver_cmd(struct cam_actuator_ctrl_t *a_ctrl, void *arg);
+/**
+ * @a_ctrl: Actuator ctrl structure
+ *
+ * This API handles the shutdown ioctl/close
+ */
+void cam_actuator_shutdown(struct cam_actuator_ctrl_t *a_ctrl);
+
#endif /* _CAM_ACTUATOR_CORE_H_ */
diff --git a/drivers/media/platform/msm/camera/cam_sensor_module/cam_actuator/cam_actuator_dev.c b/drivers/media/platform/msm/camera/cam_sensor_module/cam_actuator/cam_actuator_dev.c
index 45dbba1..465f5e2 100644
--- a/drivers/media/platform/msm/camera/cam_sensor_module/cam_actuator/cam_actuator_dev.c
+++ b/drivers/media/platform/msm/camera/cam_sensor_module/cam_actuator/cam_actuator_dev.c
@@ -79,6 +79,24 @@
}
#endif
+static int cam_actuator_subdev_close(struct v4l2_subdev *sd,
+ struct v4l2_subdev_fh *fh)
+{
+ struct cam_actuator_ctrl_t *a_ctrl =
+ v4l2_get_subdevdata(sd);
+
+ if (!a_ctrl) {
+ CAM_ERR(CAM_ACTUATOR, "a_ctrl ptr is NULL");
+ return -EINVAL;
+ }
+
+ mutex_lock(&(a_ctrl->actuator_mutex));
+ cam_actuator_shutdown(a_ctrl);
+ mutex_unlock(&(a_ctrl->actuator_mutex));
+
+ return 0;
+}
+
static struct v4l2_subdev_core_ops cam_actuator_subdev_core_ops = {
.ioctl = cam_actuator_subdev_ioctl,
#ifdef CONFIG_COMPAT
@@ -90,7 +108,9 @@
.core = &cam_actuator_subdev_core_ops,
};
-static const struct v4l2_subdev_internal_ops cam_actuator_internal_ops;
+static const struct v4l2_subdev_internal_ops cam_actuator_internal_ops = {
+ .close = cam_actuator_subdev_close,
+};
static int cam_actuator_init_subdev(struct cam_actuator_ctrl_t *a_ctrl)
{
@@ -191,6 +211,9 @@
cam_actuator_apply_request;
v4l2_set_subdevdata(&(a_ctrl->v4l2_dev_str.sd), a_ctrl);
+
+ a_ctrl->cam_act_state = CAM_ACTUATOR_INIT;
+
return rc;
free_mem:
kfree(a_ctrl->i2c_data.per_frame);
@@ -307,6 +330,8 @@
cam_actuator_establish_link;
a_ctrl->bridge_intf.ops.apply_req =
cam_actuator_apply_request;
+ a_ctrl->bridge_intf.ops.flush_req =
+ cam_actuator_flush_request;
platform_set_drvdata(pdev, a_ctrl);
v4l2_set_subdevdata(&a_ctrl->v4l2_dev_str.sd, a_ctrl);
diff --git a/drivers/media/platform/msm/camera/cam_sensor_module/cam_actuator/cam_actuator_dev.h b/drivers/media/platform/msm/camera/cam_sensor_module/cam_actuator/cam_actuator_dev.h
index ad42a3d..bd5d50f 100644
--- a/drivers/media/platform/msm/camera/cam_sensor_module/cam_actuator/cam_actuator_dev.h
+++ b/drivers/media/platform/msm/camera/cam_sensor_module/cam_actuator/cam_actuator_dev.h
@@ -48,11 +48,18 @@
#define ACTUATOR_MAX_POLL_COUNT 10
-enum msm_actuator_state_t {
+enum cam_actuator_apply_state_t {
ACT_APPLY_SETTINGS_NOW,
ACT_APPLY_SETTINGS_LATER,
};
+enum cam_actator_state {
+ CAM_ACTUATOR_INIT,
+ CAM_ACTUATOR_ACQUIRE,
+ CAM_ACTUATOR_START,
+ CAM_ACTUATOR_RELEASE,
+};
+
/**
* struct intf_params
* @device_hdl: Device Handle
@@ -76,6 +83,9 @@
* @io_master_info: Information about the communication master
* @actuator_mutex: Actuator mutex
* @act_apply_state: Actuator settings aRegulator config
+ * @id: Cell Index
+ * @res_apply_state: Actuator settings apply state
+ * @cam_act_state: Actuator state
* @gconf: GPIO config
* @pinctrl_info: Pinctrl information
* @v4l2_dev_str: V4L2 device structure
@@ -90,7 +100,9 @@
struct camera_io_master io_master_info;
struct cam_hw_soc_info soc_info;
struct mutex actuator_mutex;
- enum msm_actuator_state_t act_apply_state;
+ uint32_t id;
+ enum cam_actuator_apply_state_t setting_apply_state;
+ enum cam_actator_state cam_act_state;
struct msm_camera_gpio_num_info *gpio_num_info;
uint8_t cam_pinctrl_status;
struct cam_subdev v4l2_dev_str;
diff --git a/drivers/media/platform/msm/camera/cam_sensor_module/cam_csiphy/cam_csiphy_core.c b/drivers/media/platform/msm/camera/cam_sensor_module/cam_csiphy/cam_csiphy_core.c
index 57d576a..7cc26c1 100644
--- a/drivers/media/platform/msm/camera/cam_sensor_module/cam_csiphy/cam_csiphy_core.c
+++ b/drivers/media/platform/msm/camera/cam_sensor_module/cam_csiphy/cam_csiphy_core.c
@@ -346,6 +346,44 @@
return rc;
}
+void cam_csiphy_shutdown(struct csiphy_device *csiphy_dev)
+{
+ struct cam_hw_soc_info *soc_info;
+
+ if (csiphy_dev->csiphy_state == CAM_CSIPHY_INIT)
+ return;
+
+ if (csiphy_dev->csiphy_state == CAM_CSIPHY_START) {
+ soc_info = &csiphy_dev->soc_info;
+
+ cam_csiphy_reset(csiphy_dev);
+ cam_soc_util_disable_platform_resource(soc_info, true, true);
+
+ cam_cpas_stop(csiphy_dev->cpas_handle);
+ csiphy_dev->csiphy_state = CAM_CSIPHY_ACQUIRE;
+ }
+
+ if (csiphy_dev->csiphy_state == CAM_CSIPHY_ACQUIRE) {
+ if (csiphy_dev->bridge_intf.device_hdl[0] != -1)
+ cam_destroy_device_hdl(
+ csiphy_dev->bridge_intf.device_hdl[0]);
+ if (csiphy_dev->bridge_intf.device_hdl[1] != -1)
+ cam_destroy_device_hdl(
+ csiphy_dev->bridge_intf.device_hdl[1]);
+ csiphy_dev->bridge_intf.device_hdl[0] = -1;
+ csiphy_dev->bridge_intf.device_hdl[1] = -1;
+ csiphy_dev->bridge_intf.link_hdl[0] = -1;
+ csiphy_dev->bridge_intf.link_hdl[1] = -1;
+ csiphy_dev->bridge_intf.session_hdl[0] = -1;
+ csiphy_dev->bridge_intf.session_hdl[1] = -1;
+ }
+
+ csiphy_dev->ref_count = 0;
+ csiphy_dev->is_acquired_dev_combo_mode = 0;
+ csiphy_dev->acquire_count = 0;
+ csiphy_dev->csiphy_state = CAM_CSIPHY_INIT;
+}
+
int32_t cam_csiphy_core_cfg(void *phy_dev,
void *arg)
{
@@ -480,8 +518,7 @@
CAM_ERR(CAM_CSIPHY, "de-voting CPAS: %d", rc);
goto release_mutex;
}
- csiphy_dev->csiphy_info.combo_mode = 0;
- csiphy_dev->csiphy_state = CAM_CSIPHY_STOP;
+ csiphy_dev->csiphy_state = CAM_CSIPHY_ACQUIRE;
}
break;
case CAM_RELEASE_DEV: {
@@ -517,8 +554,6 @@
csiphy_dev->config_count--;
csiphy_dev->acquire_count--;
- if (csiphy_dev->acquire_count == 0)
- csiphy_dev->csiphy_state = CAM_CSIPHY_RELEASE;
if (csiphy_dev->csiphy_info.secure_mode &&
(!csiphy_dev->config_count)) {
@@ -528,6 +563,9 @@
csiphy_dev->soc_info.index,
CAM_SECURE_MODE_NON_SECURE);
}
+
+ if (csiphy_dev->acquire_count == 0)
+ csiphy_dev->csiphy_state = CAM_CSIPHY_INIT;
}
break;
case CAM_CONFIG_DEV: {
@@ -584,8 +622,6 @@
csiphy_dev->csiphy_state = CAM_CSIPHY_START;
}
break;
- case CAM_SD_SHUTDOWN:
- break;
default:
CAM_ERR(CAM_CSIPHY, "Invalid Opcode: %d", cmd->op_code);
rc = -EINVAL;
diff --git a/drivers/media/platform/msm/camera/cam_sensor_module/cam_csiphy/cam_csiphy_core.h b/drivers/media/platform/msm/camera/cam_sensor_module/cam_csiphy/cam_csiphy_core.h
index 6eeeea4..361004b 100644
--- a/drivers/media/platform/msm/camera/cam_sensor_module/cam_csiphy/cam_csiphy_core.h
+++ b/drivers/media/platform/msm/camera/cam_sensor_module/cam_csiphy/cam_csiphy_core.h
@@ -49,4 +49,11 @@
*/
irqreturn_t cam_csiphy_irq(int irq_num, void *data);
+/**
+ * @csiphy_dev: CSIPhy device structure
+ *
+ * This API handles the CSIPhy close
+ */
+void cam_csiphy_shutdown(struct csiphy_device *csiphy_dev);
+
#endif /* _CAM_CSIPHY_CORE_H_ */
diff --git a/drivers/media/platform/msm/camera/cam_sensor_module/cam_csiphy/cam_csiphy_dev.c b/drivers/media/platform/msm/camera/cam_sensor_module/cam_csiphy/cam_csiphy_dev.c
index a136fa7..e2f061f 100644
--- a/drivers/media/platform/msm/camera/cam_sensor_module/cam_csiphy/cam_csiphy_dev.c
+++ b/drivers/media/platform/msm/camera/cam_sensor_module/cam_csiphy/cam_csiphy_dev.c
@@ -38,6 +38,24 @@
return rc;
}
+static int cam_csiphy_subdev_close(struct v4l2_subdev *sd,
+ struct v4l2_subdev_fh *fh)
+{
+ struct csiphy_device *csiphy_dev =
+ v4l2_get_subdevdata(sd);
+
+ if (!csiphy_dev) {
+ CAM_ERR(CAM_CSIPHY, "csiphy_dev ptr is NULL");
+ return -EINVAL;
+ }
+
+ mutex_lock(&csiphy_dev->mutex);
+ cam_csiphy_shutdown(csiphy_dev);
+ mutex_unlock(&csiphy_dev->mutex);
+
+ return 0;
+}
+
#ifdef CONFIG_COMPAT
static long cam_csiphy_subdev_compat_ioctl(struct v4l2_subdev *sd,
unsigned int cmd, unsigned long arg)
@@ -89,7 +107,9 @@
.core = &csiphy_subdev_core_ops,
};
-static const struct v4l2_subdev_internal_ops csiphy_subdev_intern_ops;
+static const struct v4l2_subdev_internal_ops csiphy_subdev_intern_ops = {
+ .close = cam_csiphy_subdev_close,
+};
static int32_t cam_csiphy_platform_probe(struct platform_device *pdev)
{
diff --git a/drivers/media/platform/msm/camera/cam_sensor_module/cam_csiphy/cam_csiphy_dev.h b/drivers/media/platform/msm/camera/cam_sensor_module/cam_csiphy/cam_csiphy_dev.h
index 25891c5..afe4239 100644
--- a/drivers/media/platform/msm/camera/cam_sensor_module/cam_csiphy/cam_csiphy_dev.h
+++ b/drivers/media/platform/msm/camera/cam_sensor_module/cam_csiphy/cam_csiphy_dev.h
@@ -68,10 +68,9 @@
#endif
enum cam_csiphy_state {
+ CAM_CSIPHY_INIT,
CAM_CSIPHY_ACQUIRE,
- CAM_CSIPHY_RELEASE,
CAM_CSIPHY_START,
- CAM_CSIPHY_STOP,
};
/**
diff --git a/drivers/media/platform/msm/camera/cam_sensor_module/cam_eeprom/cam_eeprom_core.c b/drivers/media/platform/msm/camera/cam_sensor_module/cam_eeprom/cam_eeprom_core.c
index a173954..bd9f0fe 100644
--- a/drivers/media/platform/msm/camera/cam_sensor_module/cam_eeprom/cam_eeprom_core.c
+++ b/drivers/media/platform/msm/camera/cam_sensor_module/cam_eeprom/cam_eeprom_core.c
@@ -307,7 +307,7 @@
CAM_ERR(CAM_EEPROM, "failed: eeprom power down rc %d", rc);
return rc;
power_down:
- rc = cam_eeprom_power_down(e_ctrl);
+ cam_eeprom_power_down(e_ctrl);
data_mem_free:
kfree(e_ctrl->cal_data.mapdata);
kfree(e_ctrl->cal_data.map);
@@ -668,7 +668,7 @@
case CAM_EEPROM_PACKET_OPCODE_INIT:
if (e_ctrl->userspace_probe == false) {
rc = cam_eeprom_parse_read_memory_map(
- e_ctrl->pdev->dev.of_node, e_ctrl);
+ e_ctrl->soc_info.dev->of_node, e_ctrl);
if (rc < 0) {
CAM_ERR(CAM_EEPROM, "Failed: rc : %d", rc);
return rc;
@@ -723,7 +723,7 @@
}
return rc;
power_down:
- rc = cam_eeprom_power_down(e_ctrl);
+ cam_eeprom_power_down(e_ctrl);
memdata_free:
kfree(e_ctrl->cal_data.mapdata);
error:
@@ -733,6 +733,35 @@
return rc;
}
+void cam_eeprom_shutdown(struct cam_eeprom_ctrl_t *e_ctrl)
+{
+ int rc;
+
+ if (e_ctrl->cam_eeprom_state == CAM_EEPROM_INIT)
+ return;
+
+ if (e_ctrl->cam_eeprom_state == CAM_EEPROM_START) {
+ rc = camera_io_release(&e_ctrl->io_master_info);
+ if (rc < 0)
+ CAM_ERR(CAM_EEPROM, "Failed in releasing CCI");
+ rc = cam_eeprom_power_down(e_ctrl);
+ if (rc < 0)
+ CAM_ERR(CAM_EEPROM, "EEPROM Power down failed");
+ e_ctrl->cam_eeprom_state = CAM_EEPROM_ACQUIRE;
+ }
+
+ if (e_ctrl->cam_eeprom_state == CAM_EEPROM_ACQUIRE) {
+ rc = cam_destroy_device_hdl(e_ctrl->bridge_intf.device_hdl);
+ if (rc < 0)
+ CAM_ERR(CAM_EEPROM, "destroying the device hdl");
+ e_ctrl->bridge_intf.device_hdl = -1;
+ e_ctrl->bridge_intf.link_hdl = -1;
+ e_ctrl->bridge_intf.session_hdl = -1;
+ }
+
+ e_ctrl->cam_eeprom_state = CAM_EEPROM_INIT;
+}
+
/**
* cam_eeprom_driver_cmd - Handle eeprom cmds
* @e_ctrl: ctrl structure
@@ -775,6 +804,7 @@
CAM_ERR(CAM_EEPROM, "Failed to acquire dev");
goto release_mutex;
}
+ e_ctrl->cam_eeprom_state = CAM_EEPROM_ACQUIRE;
break;
case CAM_RELEASE_DEV:
if (e_ctrl->bridge_intf.device_hdl == -1) {
@@ -792,6 +822,7 @@
e_ctrl->bridge_intf.device_hdl = -1;
e_ctrl->bridge_intf.link_hdl = -1;
e_ctrl->bridge_intf.session_hdl = -1;
+ e_ctrl->cam_eeprom_state = CAM_EEPROM_INIT;
break;
case CAM_CONFIG_DEV:
rc = cam_eeprom_pkt_parse(e_ctrl, arg);
diff --git a/drivers/media/platform/msm/camera/cam_sensor_module/cam_eeprom/cam_eeprom_core.h b/drivers/media/platform/msm/camera/cam_sensor_module/cam_eeprom/cam_eeprom_core.h
index 84736df..c9fccbb 100644
--- a/drivers/media/platform/msm/camera/cam_sensor_module/cam_eeprom/cam_eeprom_core.h
+++ b/drivers/media/platform/msm/camera/cam_sensor_module/cam_eeprom/cam_eeprom_core.h
@@ -17,5 +17,12 @@
int32_t cam_eeprom_driver_cmd(struct cam_eeprom_ctrl_t *e_ctrl, void *arg);
int32_t cam_eeprom_parse_read_memory_map(struct device_node *of_node,
struct cam_eeprom_ctrl_t *e_ctrl);
+/**
+ * @e_ctrl: EEPROM ctrl structure
+ *
+ * This API handles the shutdown ioctl/close
+ */
+void cam_eeprom_shutdown(struct cam_eeprom_ctrl_t *e_ctrl);
+
#endif
/* _CAM_EEPROM_CORE_H_ */
diff --git a/drivers/media/platform/msm/camera/cam_sensor_module/cam_eeprom/cam_eeprom_dev.c b/drivers/media/platform/msm/camera/cam_sensor_module/cam_eeprom/cam_eeprom_dev.c
index 88d7665..d667cf4 100644
--- a/drivers/media/platform/msm/camera/cam_sensor_module/cam_eeprom/cam_eeprom_dev.c
+++ b/drivers/media/platform/msm/camera/cam_sensor_module/cam_eeprom/cam_eeprom_dev.c
@@ -34,6 +34,24 @@
return rc;
}
+static int cam_eeprom_subdev_close(struct v4l2_subdev *sd,
+ struct v4l2_subdev_fh *fh)
+{
+ struct cam_eeprom_ctrl_t *e_ctrl =
+ v4l2_get_subdevdata(sd);
+
+ if (!e_ctrl) {
+ CAM_ERR(CAM_EEPROM, "e_ctrl ptr is NULL");
+ return -EINVAL;
+ }
+
+ mutex_lock(&(e_ctrl->eeprom_mutex));
+ cam_eeprom_shutdown(e_ctrl);
+ mutex_unlock(&(e_ctrl->eeprom_mutex));
+
+ return 0;
+}
+
int32_t cam_eeprom_update_i2c_info(struct cam_eeprom_ctrl_t *e_ctrl,
struct cam_eeprom_i2c_info_t *i2c_info)
{
@@ -98,7 +116,9 @@
}
#endif
-static const struct v4l2_subdev_internal_ops cam_eeprom_internal_ops;
+static const struct v4l2_subdev_internal_ops cam_eeprom_internal_ops = {
+ .close = cam_eeprom_subdev_close,
+};
static struct v4l2_subdev_core_ops cam_eeprom_subdev_core_ops = {
.ioctl = cam_eeprom_subdev_ioctl,
@@ -205,6 +225,7 @@
e_ctrl->bridge_intf.ops.link_setup = NULL;
e_ctrl->bridge_intf.ops.apply_req = NULL;
v4l2_set_subdevdata(&e_ctrl->v4l2_dev_str.sd, e_ctrl);
+ e_ctrl->cam_eeprom_state = CAM_EEPROM_INIT;
return rc;
free_soc:
@@ -404,6 +425,7 @@
goto free_cci_client;
}
e_ctrl->soc_info.soc_private = soc_private;
+ soc_private->power_info.dev = &pdev->dev;
/* Initialize mutex */
mutex_init(&(e_ctrl->eeprom_mutex));
diff --git a/drivers/media/platform/msm/camera/cam_sensor_module/cam_eeprom/cam_eeprom_dev.h b/drivers/media/platform/msm/camera/cam_sensor_module/cam_eeprom/cam_eeprom_dev.h
index e06e22a..fa4a3dd 100644
--- a/drivers/media/platform/msm/camera/cam_sensor_module/cam_eeprom/cam_eeprom_dev.h
+++ b/drivers/media/platform/msm/camera/cam_sensor_module/cam_eeprom/cam_eeprom_dev.h
@@ -37,6 +37,13 @@
#define MSM_EEPROM_MAX_MEM_MAP_CNT 8
#define MSM_EEPROM_MEM_MAP_PROPERTIES_CNT 8
+enum cam_eeprom_state {
+ CAM_EEPROM_INIT,
+ CAM_EEPROM_ACQUIRE,
+ CAM_EEPROM_START,
+ CAM_EEPROM_RELEASE,
+};
+
/**
* struct cam_eeprom_map_t - eeprom map
* @data_type : Data type
@@ -154,6 +161,7 @@
* @cci_i2c_master : I2C structure
* @v4l2_dev_str : V4L2 device structure
* @bridge_intf : bridge interface params
+ * @cam_eeprom_state: eeprom_device_state
* @userspace_probe : flag indicates userspace or kernel probe
* @cal_data : Calibration data
* @device_name : Device name
@@ -170,6 +178,7 @@
struct cam_subdev v4l2_dev_str;
struct cam_eeprom_intf_params bridge_intf;
enum msm_camera_device_type_t eeprom_device_type;
+ enum cam_eeprom_state cam_eeprom_state;
bool userspace_probe;
struct cam_eeprom_memory_block_t cal_data;
char device_name[20];
diff --git a/drivers/media/platform/msm/camera/cam_sensor_module/cam_flash/cam_flash_core.c b/drivers/media/platform/msm/camera/cam_sensor_module/cam_flash/cam_flash_core.c
index b0fe293..8573f00 100644
--- a/drivers/media/platform/msm/camera/cam_sensor_module/cam_flash/cam_flash_core.c
+++ b/drivers/media/platform/msm/camera/cam_sensor_module/cam_flash/cam_flash_core.c
@@ -45,6 +45,7 @@
return rc;
}
flash_ctrl->is_regulator_enabled = false;
+ flash_ctrl->flash_state = CAM_FLASH_STATE_RELEASE;
} else {
CAM_ERR(CAM_FLASH, "Wrong Flash State : %d",
flash_ctrl->flash_state);
@@ -751,6 +752,41 @@
return rc;
}
+void cam_flash_shutdown(struct cam_flash_ctrl *fctrl)
+{
+ int rc, i, j;
+
+ for (i = 0; i < MAX_PER_FRAME_ARRAY; i++) {
+ fctrl->per_frame[i].cmn_attr.request_id = 0;
+ fctrl->per_frame[i].cmn_attr.is_settings_valid = false;
+ fctrl->per_frame[i].cmn_attr.count = 0;
+ for (j = 0; j < CAM_FLASH_MAX_LED_TRIGGERS; j++)
+ fctrl->per_frame[i].led_current_ma[j] = 0;
+ }
+
+ cam_flash_flush_nrt(fctrl);
+
+ if ((fctrl->flash_state != CAM_FLASH_STATE_RELEASE) &&
+ (fctrl->is_regulator_enabled == true)) {
+ rc = cam_flash_prepare(fctrl, CAM_FLASH_STATE_RELEASE);
+ if (rc)
+ CAM_ERR(CAM_FLASH, "Disable Regulator Failed ret = %d",
+ rc);
+ }
+
+ if (fctrl->bridge_intf.device_hdl != -1) {
+ rc = cam_destroy_device_hdl(fctrl->bridge_intf.
+ device_hdl);
+ if (rc)
+ CAM_ERR(CAM_FLASH,
+ "Failed in destroying the device Handle rc= %d",
+ rc);
+ fctrl->bridge_intf.device_hdl = -1;
+ fctrl->bridge_intf.link_hdl = -1;
+ fctrl->bridge_intf.session_hdl = -1;
+ }
+}
+
int cam_flash_apply_request(struct cam_req_mgr_apply_request *apply)
{
int rc = 0;
diff --git a/drivers/media/platform/msm/camera/cam_sensor_module/cam_flash/cam_flash_core.h b/drivers/media/platform/msm/camera/cam_sensor_module/cam_flash/cam_flash_core.h
index 4b0cf8d..f2a782b 100644
--- a/drivers/media/platform/msm/camera/cam_sensor_module/cam_flash/cam_flash_core.h
+++ b/drivers/media/platform/msm/camera/cam_sensor_module/cam_flash/cam_flash_core.h
@@ -29,5 +29,5 @@
int cam_flash_off(struct cam_flash_ctrl *fctrl);
int cam_flash_prepare(struct cam_flash_ctrl *flash_ctrl,
enum cam_flash_state state);
-
+void cam_flash_shutdown(struct cam_flash_ctrl *flash_ctrl);
#endif /*_CAM_FLASH_CORE_H_*/
diff --git a/drivers/media/platform/msm/camera/cam_sensor_module/cam_flash/cam_flash_dev.c b/drivers/media/platform/msm/camera/cam_sensor_module/cam_flash/cam_flash_dev.c
index d743cf0..57f1f0f 100644
--- a/drivers/media/platform/msm/camera/cam_sensor_module/cam_flash/cam_flash_dev.c
+++ b/drivers/media/platform/msm/camera/cam_sensor_module/cam_flash/cam_flash_dev.c
@@ -71,6 +71,7 @@
goto release_mutex;
}
break;
+ fctrl->flash_state = CAM_FLASH_STATE_ACQUIRE;
}
case CAM_RELEASE_DEV: {
CAM_DBG(CAM_FLASH, "CAM_RELEASE_DEV");
@@ -91,6 +92,7 @@
fctrl->bridge_intf.link_hdl = -1;
fctrl->bridge_intf.session_hdl = -1;
break;
+ fctrl->flash_state = CAM_FLASH_STATE_RELEASE;
}
case CAM_QUERY_CAP: {
struct cam_flash_query_cap_info flash_cap = {0};
@@ -130,6 +132,7 @@
CAM_ERR(CAM_FLASH, "cannot apply settings rc = %d", rc);
goto release_mutex;
}
+ fctrl->flash_state = CAM_FLASH_STATE_INIT;
break;
}
case CAM_STOP_DEV: {
@@ -143,8 +146,7 @@
rc);
goto release_mutex;
}
- fctrl->flash_state = CAM_FLASH_STATE_RELEASE;
-
+ fctrl->flash_state = CAM_FLASH_STATE_ACQUIRE;
break;
}
case CAM_CONFIG_DEV: {
@@ -256,6 +258,24 @@
return 0;
}
+static int cam_flash_subdev_close(struct v4l2_subdev *sd,
+ struct v4l2_subdev_fh *fh)
+{
+ struct cam_flash_ctrl *flash_ctrl =
+ v4l2_get_subdevdata(sd);
+
+ if (!flash_ctrl) {
+ CAM_ERR(CAM_FLASH, "Flash ctrl ptr is NULL");
+ return -EINVAL;
+ }
+
+ mutex_lock(&flash_ctrl->flash_mutex);
+ cam_flash_shutdown(flash_ctrl);
+ mutex_unlock(&flash_ctrl->flash_mutex);
+
+ return 0;
+}
+
static struct v4l2_subdev_core_ops cam_flash_subdev_core_ops = {
.ioctl = cam_flash_subdev_ioctl,
#ifdef CONFIG_COMPAT
@@ -267,7 +287,9 @@
.core = &cam_flash_subdev_core_ops,
};
-static const struct v4l2_subdev_internal_ops cam_flash_internal_ops;
+static const struct v4l2_subdev_internal_ops cam_flash_internal_ops = {
+ .close = cam_flash_subdev_close,
+};
static int32_t cam_flash_platform_probe(struct platform_device *pdev)
{
diff --git a/drivers/media/platform/msm/camera/cam_sensor_module/cam_flash/cam_flash_dev.h b/drivers/media/platform/msm/camera/cam_sensor_module/cam_flash/cam_flash_dev.h
index 1897eb6..1583c27 100644
--- a/drivers/media/platform/msm/camera/cam_sensor_module/cam_flash/cam_flash_dev.h
+++ b/drivers/media/platform/msm/camera/cam_sensor_module/cam_flash/cam_flash_dev.h
@@ -49,6 +49,7 @@
enum cam_flash_state {
CAM_FLASH_STATE_INIT,
+ CAM_FLASH_STATE_ACQUIRE,
CAM_FLASH_STATE_LOW,
CAM_FLASH_STATE_HIGH,
CAM_FLASH_STATE_RELEASE,
diff --git a/drivers/media/platform/msm/camera/cam_sensor_module/cam_ois/cam_ois_core.c b/drivers/media/platform/msm/camera/cam_sensor_module/cam_ois/cam_ois_core.c
index 0a283ab..2a877fd 100644
--- a/drivers/media/platform/msm/camera/cam_sensor_module/cam_ois/cam_ois_core.c
+++ b/drivers/media/platform/msm/camera/cam_sensor_module/cam_ois/cam_ois_core.c
@@ -77,12 +77,16 @@
return -EINVAL;
}
- if (config)
+ if (config) {
+ rc = cam_soc_util_request_platform_resource(soc_info,
+ NULL, NULL);
rc = cam_soc_util_enable_platform_resource(soc_info, false, 0,
false);
- else
+ } else {
rc = cam_soc_util_disable_platform_resource(soc_info, false,
false);
+ rc = cam_soc_util_release_platform_resource(soc_info);
+ }
return rc;
}
@@ -105,15 +109,6 @@
if (soc_info->gpio_data &&
gpio_num_info &&
gpio_num_info->valid[SENSOR_VAF] == 1) {
- rc = cam_soc_util_request_platform_resource(&o_ctrl->soc_info,
- NULL, NULL);
- rc = cam_soc_util_enable_platform_resource(&o_ctrl->soc_info,
- false, 0, false);
- if (rc < 0) {
- CAM_ERR(CAM_OIS, "Failed in req gpio: %d", rc);
- return rc;
- }
-
gpio_set_value_cansleep(
gpio_num_info->gpio_num[SENSOR_VAF],
1);
@@ -132,12 +127,6 @@
&o_ctrl->soc_info;
struct msm_camera_gpio_num_info *gpio_num_info = NULL;
- rc = cam_ois_vreg_control(o_ctrl, 0);
- if (rc < 0) {
- CAM_ERR(CAM_OIS, "Failed %d");
- return rc;
- }
-
gpio_num_info = o_ctrl->gpio_num_info;
if (soc_info->gpio_data &&
@@ -147,15 +136,12 @@
gpio_set_value_cansleep(
gpio_num_info->gpio_num[SENSOR_VAF],
GPIOF_OUT_INIT_LOW);
-
- rc = cam_soc_util_release_platform_resource(&o_ctrl->soc_info);
- rc |= cam_soc_util_disable_platform_resource(&o_ctrl->soc_info,
- 0, 0);
- if (rc < 0)
- CAM_ERR(CAM_OIS,
- "Failed to disable platform resources: %d", rc);
}
+ rc = cam_ois_vreg_control(o_ctrl, 0);
+ if (rc < 0)
+ CAM_ERR(CAM_OIS, "Disable regualtor Failed %d", rc);
+
return rc;
}
@@ -430,7 +416,7 @@
i2c_reg_settings = &(o_ctrl->i2c_init_data);
i2c_reg_settings->is_settings_valid = 1;
i2c_reg_settings->request_id = 0;
- rc = cam_sensor_i2c_pkt_parser(i2c_reg_settings,
+ rc = cam_sensor_i2c_command_parser(i2c_reg_settings,
&cmd_desc[1], 1);
if (rc < 0) {
CAM_ERR(CAM_OIS, "OIS pkt parsing failed: %d",
@@ -442,7 +428,7 @@
i2c_reg_settings = &(o_ctrl->i2c_calib_data);
i2c_reg_settings->is_settings_valid = 1;
i2c_reg_settings->request_id = 0;
- rc = cam_sensor_i2c_pkt_parser(i2c_reg_settings,
+ rc = cam_sensor_i2c_command_parser(i2c_reg_settings,
&cmd_desc[2], 1);
if (rc < 0) {
CAM_ERR(CAM_OIS,
@@ -458,7 +444,7 @@
i2c_reg_settings = &(o_ctrl->i2c_mode_data);
i2c_reg_settings->is_settings_valid = 1;
i2c_reg_settings->request_id = 0;
- rc = cam_sensor_i2c_pkt_parser(i2c_reg_settings,
+ rc = cam_sensor_i2c_command_parser(i2c_reg_settings,
cmd_desc, 1);
if (rc < 0) {
CAM_ERR(CAM_OIS, "OIS pkt parsing failed: %d", rc);
@@ -475,6 +461,35 @@
return rc;
}
+void cam_ois_shutdown(struct cam_ois_ctrl_t *o_ctrl)
+{
+ int rc;
+
+ if (o_ctrl->cam_ois_state == CAM_OIS_INIT)
+ return;
+
+ if (o_ctrl->cam_ois_state == CAM_OIS_START) {
+ rc = camera_io_release(&o_ctrl->io_master_info);
+ if (rc < 0)
+ CAM_ERR(CAM_OIS, "Failed in releasing CCI");
+ rc = cam_ois_power_down(o_ctrl);
+ if (rc < 0)
+ CAM_ERR(CAM_OIS, "OIS Power down failed");
+ o_ctrl->cam_ois_state = CAM_OIS_ACQUIRE;
+ }
+
+ if (o_ctrl->cam_ois_state == CAM_OIS_ACQUIRE) {
+ rc = cam_destroy_device_hdl(o_ctrl->bridge_intf.device_hdl);
+ if (rc < 0)
+ CAM_ERR(CAM_OIS, "destroying the device hdl");
+ o_ctrl->bridge_intf.device_hdl = -1;
+ o_ctrl->bridge_intf.link_hdl = -1;
+ o_ctrl->bridge_intf.session_hdl = -1;
+ }
+
+ o_ctrl->cam_ois_state = CAM_OIS_INIT;
+}
+
/**
* cam_ois_driver_cmd - Handle ois cmds
* @e_ctrl: ctrl structure
@@ -513,6 +528,7 @@
CAM_ERR(CAM_OIS, "Failed to acquire dev");
goto release_mutex;
}
+ o_ctrl->cam_ois_state = CAM_OIS_ACQUIRE;
break;
case CAM_START_DEV:
rc = cam_ois_power_up(o_ctrl);
@@ -548,6 +564,7 @@
goto pwr_dwn;
}
}
+ o_ctrl->cam_ois_state = CAM_OIS_START;
break;
case CAM_CONFIG_DEV:
rc = cam_ois_pkt_parse(o_ctrl, arg);
@@ -570,6 +587,7 @@
o_ctrl->bridge_intf.device_hdl = -1;
o_ctrl->bridge_intf.link_hdl = -1;
o_ctrl->bridge_intf.session_hdl = -1;
+ o_ctrl->cam_ois_state = CAM_OIS_INIT;
break;
case CAM_STOP_DEV:
rc = camera_io_release(&o_ctrl->io_master_info);
@@ -580,6 +598,7 @@
CAM_ERR(CAM_OIS, "OIS Power down failed");
goto release_mutex;
}
+ o_ctrl->cam_ois_state = CAM_OIS_ACQUIRE;
break;
default:
CAM_ERR(CAM_OIS, "invalid opcode");
diff --git a/drivers/media/platform/msm/camera/cam_sensor_module/cam_ois/cam_ois_core.h b/drivers/media/platform/msm/camera/cam_sensor_module/cam_ois/cam_ois_core.h
index 13da82a..6f81d09 100644
--- a/drivers/media/platform/msm/camera/cam_sensor_module/cam_ois/cam_ois_core.h
+++ b/drivers/media/platform/msm/camera/cam_sensor_module/cam_ois/cam_ois_core.h
@@ -16,5 +16,12 @@
int cam_ois_driver_cmd(struct cam_ois_ctrl_t *e_ctrl, void *arg);
+/**
+ * @o_ctrl: OIS ctrl structure
+ *
+ * This API handles the shutdown ioctl/close
+ */
+void cam_ois_shutdown(struct cam_ois_ctrl_t *o_ctrl);
+
#endif
/* _CAM_OIS_CORE_H_ */
diff --git a/drivers/media/platform/msm/camera/cam_sensor_module/cam_ois/cam_ois_dev.c b/drivers/media/platform/msm/camera/cam_sensor_module/cam_ois/cam_ois_dev.c
index aeab388..2629180 100644
--- a/drivers/media/platform/msm/camera/cam_sensor_module/cam_ois/cam_ois_dev.c
+++ b/drivers/media/platform/msm/camera/cam_sensor_module/cam_ois/cam_ois_dev.c
@@ -34,6 +34,24 @@
return rc;
}
+static int cam_ois_subdev_close(struct v4l2_subdev *sd,
+ struct v4l2_subdev_fh *fh)
+{
+ struct cam_ois_ctrl_t *o_ctrl =
+ v4l2_get_subdevdata(sd);
+
+ if (!o_ctrl) {
+ CAM_ERR(CAM_OIS, "o_ctrl ptr is NULL");
+ return -EINVAL;
+ }
+
+ mutex_lock(&(o_ctrl->ois_mutex));
+ cam_ois_shutdown(o_ctrl);
+ mutex_unlock(&(o_ctrl->ois_mutex));
+
+ return 0;
+}
+
static int32_t cam_ois_update_i2c_info(struct cam_ois_ctrl_t *o_ctrl,
struct cam_ois_i2c_info_t *i2c_info)
{
@@ -99,7 +117,9 @@
}
#endif
-static const struct v4l2_subdev_internal_ops cam_ois_internal_ops;
+static const struct v4l2_subdev_internal_ops cam_ois_internal_ops = {
+ .close = cam_ois_subdev_close,
+};
static struct v4l2_subdev_core_ops cam_ois_subdev_core_ops = {
.ioctl = cam_ois_subdev_ioctl,
@@ -183,6 +203,7 @@
rc = cam_ois_init_subdev_param(o_ctrl);
if (rc)
goto octrl_free;
+ o_ctrl->cam_ois_state = CAM_OIS_INIT;
return rc;
diff --git a/drivers/media/platform/msm/camera/cam_sensor_module/cam_ois/cam_ois_dev.h b/drivers/media/platform/msm/camera/cam_sensor_module/cam_ois/cam_ois_dev.h
index 794b65a..e341bb7 100644
--- a/drivers/media/platform/msm/camera/cam_sensor_module/cam_ois/cam_ois_dev.h
+++ b/drivers/media/platform/msm/camera/cam_sensor_module/cam_ois/cam_ois_dev.h
@@ -31,6 +31,13 @@
#define DEFINE_MSM_MUTEX(mutexname) \
static struct mutex mutexname = __MUTEX_INITIALIZER(mutexname)
+enum cam_ois_state {
+ CAM_OIS_INIT,
+ CAM_OIS_ACQUIRE,
+ CAM_OIS_START,
+ CAM_OIS_RELEASE,
+};
+
/**
* struct cam_ois_registered_driver_t - registered driver info
* @platform_driver : flag indicates if platform driver is registered
@@ -95,6 +102,7 @@
* @i2c_mode_data : ois i2c mode settings
* @i2c_calib_data : ois i2c calib settings
* @ois_device_type : ois device type
+ * @cam_ois_state : ois_device_state
* @ois_name : ois name
* @ois_fw_flag : flag for firmware download
* @is_ois_calib : flag for Calibration data
@@ -115,6 +123,7 @@
struct i2c_settings_array i2c_calib_data;
struct i2c_settings_array i2c_mode_data;
enum msm_camera_device_type_t ois_device_type;
+ enum cam_ois_state cam_ois_state;
char device_name[20];
char ois_name[32];
uint8_t ois_fw_flag;
diff --git a/drivers/media/platform/msm/camera/cam_sensor_module/cam_sensor/cam_sensor_core.c b/drivers/media/platform/msm/camera/cam_sensor_module/cam_sensor/cam_sensor_core.c
index cd96129..ac9235d 100644
--- a/drivers/media/platform/msm/camera/cam_sensor_module/cam_sensor/cam_sensor_core.c
+++ b/drivers/media/platform/msm/camera/cam_sensor_module/cam_sensor/cam_sensor_core.c
@@ -17,6 +17,82 @@
#include "cam_soc_util.h"
#include "cam_trace.h"
+
+static void cam_sensor_update_req_mgr(
+ struct cam_sensor_ctrl_t *s_ctrl,
+ struct cam_packet *csl_packet)
+{
+ struct cam_req_mgr_add_request add_req;
+
+ add_req.link_hdl = s_ctrl->bridge_intf.link_hdl;
+ add_req.req_id = csl_packet->header.request_id;
+ CAM_DBG(CAM_SENSOR, " Rxed Req Id: %lld",
+ csl_packet->header.request_id);
+ add_req.dev_hdl = s_ctrl->bridge_intf.device_hdl;
+ add_req.skip_before_applying = 0;
+ if (s_ctrl->bridge_intf.crm_cb &&
+ s_ctrl->bridge_intf.crm_cb->add_req)
+ s_ctrl->bridge_intf.crm_cb->add_req(&add_req);
+
+ CAM_DBG(CAM_SENSOR, " add req to req mgr: %lld",
+ add_req.req_id);
+}
+
+static void cam_sensor_release_resource(
+ struct cam_sensor_ctrl_t *s_ctrl)
+{
+ struct i2c_settings_array *i2c_set = NULL;
+ int i, rc;
+
+ i2c_set = &(s_ctrl->i2c_data.init_settings);
+ if (i2c_set->is_settings_valid == 1) {
+ i2c_set->is_settings_valid = -1;
+ rc = delete_request(i2c_set);
+ if (rc < 0)
+ CAM_ERR(CAM_SENSOR,
+ "failed while deleting Init settings");
+ }
+
+ i2c_set = &(s_ctrl->i2c_data.res_settings);
+ if (i2c_set->is_settings_valid == 1) {
+ i2c_set->is_settings_valid = -1;
+ rc = delete_request(i2c_set);
+ if (rc < 0)
+ CAM_ERR(CAM_SENSOR,
+ "failed while deleting Res settings");
+ }
+ i2c_set = &(s_ctrl->i2c_data.streamoff_settings);
+ if (i2c_set->is_settings_valid == 1) {
+ i2c_set->is_settings_valid = -1;
+ rc = delete_request(i2c_set);
+ if (rc < 0)
+ CAM_ERR(CAM_SENSOR,
+ "failed while deleting Streamoff settings");
+ }
+ i2c_set = &(s_ctrl->i2c_data.streamon_settings);
+ if (i2c_set->is_settings_valid == 1) {
+ i2c_set->is_settings_valid = -1;
+ rc = delete_request(i2c_set);
+ if (rc < 0)
+ CAM_ERR(CAM_SENSOR,
+ "failed while deleting Streamoff settings");
+ }
+ if (s_ctrl->i2c_data.per_frame != NULL) {
+ for (i = 0; i < MAX_PER_FRAME_ARRAY; i++) {
+ i2c_set = &(s_ctrl->i2c_data.per_frame[i]);
+
+ if (i2c_set->is_settings_valid == 1) {
+ i2c_set->is_settings_valid = -1;
+ rc = delete_request(i2c_set);
+ if (rc < 0)
+ CAM_ERR(CAM_SENSOR,
+ "delete request: %lld rc: %d",
+ i2c_set->request_id, rc);
+ }
+ }
+ }
+}
+
static int32_t cam_sensor_i2c_pkt_parse(struct cam_sensor_ctrl_t *s_ctrl,
void *arg)
{
@@ -30,7 +106,6 @@
uint32_t *offset = NULL;
struct cam_config_dev_cmd config;
struct i2c_data_settings *i2c_data = NULL;
- struct cam_req_mgr_add_request add_req;
ioctl_ctrl = (struct cam_control *)arg;
@@ -63,13 +138,32 @@
i2c_data = &(s_ctrl->i2c_data);
CAM_DBG(CAM_SENSOR, "Header OpCode: %d", csl_packet->header.op_code);
- if ((csl_packet->header.op_code & 0xFFFFFF) ==
- CAM_SENSOR_PACKET_OPCODE_SENSOR_INITIAL_CONFIG) {
+ switch (csl_packet->header.op_code & 0xFFFFFF) {
+ case CAM_SENSOR_PACKET_OPCODE_SENSOR_INITIAL_CONFIG: {
i2c_reg_settings = &i2c_data->init_settings;
i2c_reg_settings->request_id = 0;
i2c_reg_settings->is_settings_valid = 1;
- } else if ((csl_packet->header.op_code & 0xFFFFFF) ==
- CAM_SENSOR_PACKET_OPCODE_SENSOR_UPDATE) {
+ break;
+ }
+ case CAM_SENSOR_PACKET_OPCODE_SENSOR_RESCONFIG: {
+ i2c_reg_settings = &i2c_data->res_settings;
+ i2c_reg_settings->request_id = 0;
+ i2c_reg_settings->is_settings_valid = 1;
+ break;
+ }
+ case CAM_SENSOR_PACKET_OPCODE_SENSOR_STREAMON: {
+ i2c_reg_settings = &i2c_data->streamon_settings;
+ i2c_reg_settings->request_id = 0;
+ i2c_reg_settings->is_settings_valid = 1;
+ break;
+ }
+ case CAM_SENSOR_PACKET_OPCODE_SENSOR_STREAMOFF: {
+ i2c_reg_settings = &i2c_data->streamoff_settings;
+ i2c_reg_settings->request_id = 0;
+ i2c_reg_settings->is_settings_valid = 1;
+ break;
+ }
+ case CAM_SENSOR_PACKET_OPCODE_SENSOR_UPDATE: {
i2c_reg_settings =
&i2c_data->
per_frame[csl_packet->header.request_id %
@@ -91,10 +185,14 @@
i2c_reg_settings->request_id =
csl_packet->header.request_id;
i2c_reg_settings->is_settings_valid = 1;
- } else if ((csl_packet->header.op_code & 0xFFFFFF) ==
- CAM_PKT_NOP_OPCODE) {
- goto update_req_mgr;
- } else {
+ cam_sensor_update_req_mgr(s_ctrl, csl_packet);
+ break;
+ }
+ case CAM_SENSOR_PACKET_OPCODE_SENSOR_NOP: {
+ cam_sensor_update_req_mgr(s_ctrl, csl_packet);
+ return rc;
+ }
+ default:
CAM_ERR(CAM_SENSOR, "Invalid Packet Header");
return -EINVAL;
}
@@ -103,28 +201,11 @@
offset += csl_packet->cmd_buf_offset / 4;
cmd_desc = (struct cam_cmd_buf_desc *)(offset);
- rc = cam_sensor_i2c_pkt_parser(i2c_reg_settings, cmd_desc, 1);
+ rc = cam_sensor_i2c_command_parser(i2c_reg_settings, cmd_desc, 1);
if (rc < 0) {
CAM_ERR(CAM_SENSOR, "Fail parsing I2C Pkt: %d", rc);
return rc;
}
-
-update_req_mgr:
- if (((csl_packet->header.op_code & 0xFFFFFF) ==
- CAM_PKT_NOP_OPCODE) || (csl_packet->header.op_code ==
- CAM_SENSOR_PACKET_OPCODE_SENSOR_UPDATE)) {
- add_req.link_hdl = s_ctrl->bridge_intf.link_hdl;
- add_req.req_id = csl_packet->header.request_id;
- CAM_DBG(CAM_SENSOR, " Rxed Req Id: %lld",
- csl_packet->header.request_id);
- add_req.dev_hdl = s_ctrl->bridge_intf.device_hdl;
- add_req.skip_before_applying = 0;
- if (s_ctrl->bridge_intf.crm_cb &&
- s_ctrl->bridge_intf.crm_cb->add_req)
- s_ctrl->bridge_intf.crm_cb->add_req(&add_req);
- CAM_DBG(CAM_SENSOR, " add req to req mgr: %lld",
- add_req.req_id);
- }
return rc;
}
@@ -377,6 +458,41 @@
return sensor_id;
}
+void cam_sensor_shutdown(struct cam_sensor_ctrl_t *s_ctrl)
+{
+ struct cam_sensor_power_ctrl_t *power_info =
+ &s_ctrl->sensordata->power_info;
+ int rc = 0;
+
+ s_ctrl->is_probe_succeed = 0;
+ if (s_ctrl->sensor_state == CAM_SENSOR_INIT)
+ return;
+
+ cam_sensor_release_resource(s_ctrl);
+
+ if (s_ctrl->sensor_state == CAM_SENSOR_START) {
+ cam_sensor_power_down(s_ctrl);
+ s_ctrl->sensor_state = CAM_SENSOR_ACQUIRE;
+ }
+
+ if (s_ctrl->sensor_state == CAM_SENSOR_ACQUIRE) {
+ rc = cam_destroy_device_hdl(s_ctrl->bridge_intf.device_hdl);
+ if (rc < 0)
+ CAM_ERR(CAM_SENSOR, " failed destroying dhdl");
+ s_ctrl->bridge_intf.device_hdl = -1;
+ s_ctrl->bridge_intf.link_hdl = -1;
+ s_ctrl->bridge_intf.session_hdl = -1;
+ s_ctrl->sensor_state = CAM_SENSOR_PROBE;
+ }
+
+ if (s_ctrl->sensor_state == CAM_SENSOR_PROBE) {
+ kfree(power_info->power_setting);
+ kfree(power_info->power_down_setting);
+ }
+
+ s_ctrl->sensor_state = CAM_SENSOR_INIT;
+}
+
int cam_sensor_match_id(struct cam_sensor_ctrl_t *s_ctrl)
{
int rc = 0;
@@ -511,8 +627,12 @@
goto release_mutex;
}
- CAM_DBG(CAM_SENSOR, "Probe Succeeded on the slot: %d",
- s_ctrl->soc_info.index);
+ CAM_INFO(CAM_SENSOR,
+ "Probe Succees, slot:%d slave_addr: 0x%x, slave_id: %d",
+ s_ctrl->soc_info.index,
+ s_ctrl->sensordata->slave_info.sensor_slave_addr,
+ s_ctrl->sensordata->slave_info.sensor_id);
+
rc = cam_sensor_power_down(s_ctrl);
if (rc < 0) {
CAM_ERR(CAM_SENSOR, "fail in Sensor Power Down");
@@ -525,6 +645,7 @@
* probed on this slot
*/
s_ctrl->is_probe_succeed = 1;
+ s_ctrl->sensor_state = CAM_SENSOR_INIT;
}
break;
case CAM_ACQUIRE_DEV: {
@@ -565,6 +686,7 @@
}
break;
case CAM_RELEASE_DEV: {
+ cam_sensor_release_resource(s_ctrl);
if (s_ctrl->bridge_intf.device_hdl == -1) {
CAM_ERR(CAM_SENSOR,
"Invalid Handles: link hdl: %d device hdl: %d",
@@ -595,26 +717,29 @@
break;
}
case CAM_START_DEV: {
- rc = cam_sensor_power_up(s_ctrl);
- if (rc < 0) {
- CAM_ERR(CAM_SENSOR, "Sensor Power up failed");
- goto release_mutex;
+ if (s_ctrl->i2c_data.streamon_settings.is_settings_valid &&
+ (s_ctrl->i2c_data.streamon_settings.request_id == 0)) {
+ rc = cam_sensor_apply_settings(s_ctrl, 0,
+ CAM_SENSOR_PACKET_OPCODE_SENSOR_STREAMON);
+ if (rc < 0) {
+ CAM_ERR(CAM_SENSOR,
+ "cannot apply streamon settings");
+ goto release_mutex;
+ }
}
- rc = cam_sensor_apply_settings(s_ctrl, 0);
- if (rc < 0) {
- CAM_ERR(CAM_SENSOR, "cannot apply settings");
- goto release_mutex;
- }
- rc = delete_request(&s_ctrl->i2c_data.init_settings);
- if (rc < 0) {
- CAM_ERR(CAM_SENSOR,
- "Fail in deleting the Init settings");
- rc = -EINVAL;
- goto release_mutex;
- }
+ s_ctrl->sensor_state = CAM_SENSOR_START;
}
break;
case CAM_STOP_DEV: {
+ if (s_ctrl->i2c_data.streamoff_settings.is_settings_valid &&
+ (s_ctrl->i2c_data.streamoff_settings.request_id == 0)) {
+ rc = cam_sensor_apply_settings(s_ctrl, 0,
+ CAM_SENSOR_PACKET_OPCODE_SENSOR_STREAMOFF);
+ if (rc < 0) {
+ CAM_ERR(CAM_SENSOR,
+ "cannot apply streamoff settings");
+ }
+ }
rc = cam_sensor_power_down(s_ctrl);
if (rc < 0) {
CAM_ERR(CAM_SENSOR, "Sensor Power Down failed");
@@ -628,10 +753,48 @@
CAM_ERR(CAM_SENSOR, "Failed CCI Config: %d", rc);
goto release_mutex;
}
+ if (s_ctrl->i2c_data.init_settings.is_settings_valid &&
+ (s_ctrl->i2c_data.init_settings.request_id == 0)) {
+ rc = cam_sensor_power_up(s_ctrl);
+ if (rc < 0) {
+ CAM_ERR(CAM_SENSOR, "Sensor Power up failed");
+ goto release_mutex;
+ }
+ rc = cam_sensor_apply_settings(s_ctrl, 0,
+ CAM_SENSOR_PACKET_OPCODE_SENSOR_INITIAL_CONFIG);
+ if (rc < 0) {
+ CAM_ERR(CAM_SENSOR,
+ "cannot apply init settings");
+ goto release_mutex;
+ }
+ rc = delete_request(&s_ctrl->i2c_data.init_settings);
+ if (rc < 0) {
+ CAM_ERR(CAM_SENSOR,
+ "Fail in deleting the Init settings");
+ goto release_mutex;
+ }
+ s_ctrl->i2c_data.init_settings.request_id = -1;
+ }
+
+ if (s_ctrl->i2c_data.res_settings.is_settings_valid &&
+ (s_ctrl->i2c_data.res_settings.request_id == 0)) {
+ rc = cam_sensor_apply_settings(s_ctrl, 0,
+ CAM_SENSOR_PACKET_OPCODE_SENSOR_RESCONFIG);
+ if (rc < 0) {
+ CAM_ERR(CAM_SENSOR,
+ "cannot apply res settings");
+ goto release_mutex;
+ }
+ rc = delete_request(&s_ctrl->i2c_data.res_settings);
+ if (rc < 0) {
+ CAM_ERR(CAM_SENSOR,
+ "Fail in deleting the res settings");
+ goto release_mutex;
+ }
+ s_ctrl->i2c_data.res_settings.request_id = -1;
+ }
}
break;
- case CAM_SD_SHUTDOWN:
- break;
default:
CAM_ERR(CAM_SENSOR, "Invalid Opcode: %d", cmd->op_code);
rc = -EINVAL;
@@ -687,9 +850,9 @@
struct cam_sensor_ctrl_t *s_ctrl = v4l2_get_subdevdata(sd);
mutex_lock(&(s_ctrl->cam_sensor_mutex));
- if (!on && s_ctrl->sensor_state == CAM_SENSOR_POWER_UP) {
+ if (!on && s_ctrl->sensor_state == CAM_SENSOR_START) {
cam_sensor_power_down(s_ctrl);
- s_ctrl->sensor_state = CAM_SENSOR_POWER_DOWN;
+ s_ctrl->sensor_state = CAM_SENSOR_ACQUIRE;
}
mutex_unlock(&(s_ctrl->cam_sensor_mutex));
@@ -731,8 +894,6 @@
}
}
- s_ctrl->sensor_state = CAM_SENSOR_POWER_UP;
-
return rc;
}
@@ -763,20 +924,39 @@
if (s_ctrl->io_master_info.master_type == CCI_MASTER)
camera_io_release(&(s_ctrl->io_master_info));
- s_ctrl->sensor_state = CAM_SENSOR_POWER_DOWN;
-
return rc;
}
int cam_sensor_apply_settings(struct cam_sensor_ctrl_t *s_ctrl,
- int64_t req_id)
+ int64_t req_id, enum cam_sensor_packet_opcodes opcode)
{
int rc = 0, offset, del_req_id;
struct i2c_settings_array *i2c_set = NULL;
struct i2c_settings_list *i2c_list;
if (req_id == 0) {
- i2c_set = &s_ctrl->i2c_data.init_settings;
+ switch (opcode) {
+ case CAM_SENSOR_PACKET_OPCODE_SENSOR_STREAMON: {
+ i2c_set = &s_ctrl->i2c_data.streamon_settings;
+ break;
+ }
+ case CAM_SENSOR_PACKET_OPCODE_SENSOR_INITIAL_CONFIG: {
+ i2c_set = &s_ctrl->i2c_data.init_settings;
+ break;
+ }
+ case CAM_SENSOR_PACKET_OPCODE_SENSOR_RESCONFIG: {
+ i2c_set = &s_ctrl->i2c_data.res_settings;
+ break;
+ }
+ case CAM_SENSOR_PACKET_OPCODE_SENSOR_STREAMOFF: {
+ i2c_set = &s_ctrl->i2c_data.streamoff_settings;
+ break;
+ }
+ case CAM_SENSOR_PACKET_OPCODE_SENSOR_UPDATE:
+ case CAM_SENSOR_PACKET_OPCODE_SENSOR_PROBE:
+ default:
+ return 0;
+ }
if (i2c_set->is_settings_valid == 1) {
list_for_each_entry(i2c_list,
&(i2c_set->list_head), list) {
@@ -790,12 +970,7 @@
return rc;
}
}
- rc = delete_request(&(s_ctrl->i2c_data.init_settings));
i2c_set->is_settings_valid = 0;
- if (rc < 0) {
- CAM_ERR(CAM_SENSOR,
- "Failed in deleting the Init request: %d", rc);
- }
}
} else {
offset = req_id % MAX_PER_FRAME_ARRAY;
@@ -836,6 +1011,7 @@
del_req_id, rc);
}
}
+
return rc;
}
@@ -855,7 +1031,8 @@
}
CAM_DBG(CAM_SENSOR, " Req Id: %lld", apply->request_id);
trace_cam_apply_req("Sensor", apply->request_id);
- rc = cam_sensor_apply_settings(s_ctrl, apply->request_id);
+ rc = cam_sensor_apply_settings(s_ctrl, apply->request_id,
+ CAM_SENSOR_PACKET_OPCODE_SENSOR_UPDATE);
return rc;
}
diff --git a/drivers/media/platform/msm/camera/cam_sensor_module/cam_sensor/cam_sensor_core.h b/drivers/media/platform/msm/camera/cam_sensor_module/cam_sensor/cam_sensor_core.h
index c8158fa..4570179 100644
--- a/drivers/media/platform/msm/camera/cam_sensor_module/cam_sensor/cam_sensor_core.h
+++ b/drivers/media/platform/msm/camera/cam_sensor_module/cam_sensor/cam_sensor_core.h
@@ -40,10 +40,12 @@
/**
* @s_ctrl: Sensor ctrl structure
* @req_id: Request id
+ * @opcode: opcode for settings
*
* This API applies the req_id settings to sensor
*/
-int cam_sensor_apply_settings(struct cam_sensor_ctrl_t *s_ctrl, int64_t req_id);
+int cam_sensor_apply_settings(struct cam_sensor_ctrl_t *s_ctrl, int64_t req_id,
+ enum cam_sensor_packet_opcodes opcode);
/**
* @apply: Req mgr structure for applying request
@@ -81,4 +83,11 @@
*/
int32_t cam_sensor_driver_cmd(struct cam_sensor_ctrl_t *s_ctrl, void *arg);
+/**
+ * @s_ctrl: Sensor ctrl structure
+ *
+ * This API handles the camera sensor close/shutdown
+ */
+void cam_sensor_shutdown(struct cam_sensor_ctrl_t *s_ctrl);
+
#endif /* _CAM_SENSOR_CORE_H_ */
diff --git a/drivers/media/platform/msm/camera/cam_sensor_module/cam_sensor/cam_sensor_dev.c b/drivers/media/platform/msm/camera/cam_sensor_module/cam_sensor/cam_sensor_dev.c
index 122aa3e..f915a0e 100644
--- a/drivers/media/platform/msm/camera/cam_sensor_module/cam_sensor/cam_sensor_dev.c
+++ b/drivers/media/platform/msm/camera/cam_sensor_module/cam_sensor/cam_sensor_dev.c
@@ -34,6 +34,24 @@
return rc;
}
+static int cam_sensor_subdev_close(struct v4l2_subdev *sd,
+ struct v4l2_subdev_fh *fh)
+{
+ struct cam_sensor_ctrl_t *s_ctrl =
+ v4l2_get_subdevdata(sd);
+
+ if (!s_ctrl) {
+ CAM_ERR(CAM_SENSOR, "s_ctrl ptr is NULL");
+ return -EINVAL;
+ }
+
+ mutex_lock(&(s_ctrl->cam_sensor_mutex));
+ cam_sensor_shutdown(s_ctrl);
+ mutex_unlock(&(s_ctrl->cam_sensor_mutex));
+
+ return 0;
+}
+
#ifdef CONFIG_COMPAT
static long cam_sensor_init_subdev_do_ioctl(struct v4l2_subdev *sd,
unsigned int cmd, unsigned long arg)
@@ -85,7 +103,9 @@
.core = &cam_sensor_subdev_core_ops,
};
-static const struct v4l2_subdev_internal_ops cam_sensor_internal_ops;
+static const struct v4l2_subdev_internal_ops cam_sensor_internal_ops = {
+ .close = cam_sensor_subdev_close,
+};
static int cam_sensor_init_subdev_params(struct cam_sensor_ctrl_t *s_ctrl)
{
@@ -163,6 +183,9 @@
}
INIT_LIST_HEAD(&(s_ctrl->i2c_data.init_settings.list_head));
+ INIT_LIST_HEAD(&(s_ctrl->i2c_data.res_settings.list_head));
+ INIT_LIST_HEAD(&(s_ctrl->i2c_data.streamon_settings.list_head));
+ INIT_LIST_HEAD(&(s_ctrl->i2c_data.streamoff_settings.list_head));
for (i = 0; i < MAX_PER_FRAME_ARRAY; i++)
INIT_LIST_HEAD(&(s_ctrl->i2c_data.per_frame[i].list_head));
@@ -269,6 +292,9 @@
}
INIT_LIST_HEAD(&(s_ctrl->i2c_data.init_settings.list_head));
+ INIT_LIST_HEAD(&(s_ctrl->i2c_data.res_settings.list_head));
+ INIT_LIST_HEAD(&(s_ctrl->i2c_data.streamon_settings.list_head));
+ INIT_LIST_HEAD(&(s_ctrl->i2c_data.streamoff_settings.list_head));
for (i = 0; i < MAX_PER_FRAME_ARRAY; i++)
INIT_LIST_HEAD(&(s_ctrl->i2c_data.per_frame[i].list_head));
@@ -283,6 +309,8 @@
platform_set_drvdata(pdev, s_ctrl);
v4l2_set_subdevdata(&(s_ctrl->v4l2_dev_str.sd), s_ctrl);
+ s_ctrl->sensor_state = CAM_SENSOR_INIT;
+
return rc;
unreg_subdev:
cam_unregister_subdev(&(s_ctrl->v4l2_dev_str));
diff --git a/drivers/media/platform/msm/camera/cam_sensor_module/cam_sensor/cam_sensor_dev.h b/drivers/media/platform/msm/camera/cam_sensor_module/cam_sensor/cam_sensor_dev.h
index f3c70c4..8c49837 100644
--- a/drivers/media/platform/msm/camera/cam_sensor_module/cam_sensor/cam_sensor_dev.h
+++ b/drivers/media/platform/msm/camera/cam_sensor_module/cam_sensor/cam_sensor_dev.h
@@ -50,8 +50,10 @@
#define CAMX_SENSOR_DEV_NAME "cam-sensor-driver"
enum cam_sensor_state_t {
- CAM_SENSOR_POWER_DOWN,
- CAM_SENSOR_POWER_UP,
+ CAM_SENSOR_INIT,
+ CAM_SENSOR_PROBE,
+ CAM_SENSOR_ACQUIRE,
+ CAM_SENSOR_START,
};
/**
diff --git a/drivers/media/platform/msm/camera/cam_sensor_module/cam_sensor_utils/cam_sensor_cmn_header.h b/drivers/media/platform/msm/camera/cam_sensor_module/cam_sensor_utils/cam_sensor_cmn_header.h
index 97b4c01..ac1e23b 100644
--- a/drivers/media/platform/msm/camera/cam_sensor_module/cam_sensor_utils/cam_sensor_cmn_header.h
+++ b/drivers/media/platform/msm/camera/cam_sensor_module/cam_sensor_utils/cam_sensor_cmn_header.h
@@ -153,7 +153,10 @@
CAM_SENSOR_PACKET_OPCODE_SENSOR_STREAMON,
CAM_SENSOR_PACKET_OPCODE_SENSOR_UPDATE,
CAM_SENSOR_PACKET_OPCODE_SENSOR_INITIAL_CONFIG,
- CAM_SENSOR_PACKET_OPCODE_SENSOR_PROBE
+ CAM_SENSOR_PACKET_OPCODE_SENSOR_PROBE,
+ CAM_SENSOR_PACKET_OPCODE_SENSOR_RESCONFIG,
+ CAM_SENSOR_PACKET_OPCODE_SENSOR_STREAMOFF,
+ CAM_SENSOR_PACKET_OPCODE_SENSOR_NOP = 127
};
enum cam_actuator_packet_opcodes {
@@ -279,6 +282,9 @@
struct i2c_data_settings {
struct i2c_settings_array init_settings;
+ struct i2c_settings_array res_settings;
+ struct i2c_settings_array streamon_settings;
+ struct i2c_settings_array streamoff_settings;
struct i2c_settings_array *per_frame;
};
diff --git a/drivers/media/platform/msm/camera/cam_sensor_module/cam_sensor_utils/cam_sensor_util.c b/drivers/media/platform/msm/camera/cam_sensor_module/cam_sensor_utils/cam_sensor_util.c
index bcdaf6d..85d7b74 100644
--- a/drivers/media/platform/msm/camera/cam_sensor_module/cam_sensor_utils/cam_sensor_util.c
+++ b/drivers/media/platform/msm/camera/cam_sensor_module/cam_sensor_utils/cam_sensor_util.c
@@ -250,7 +250,7 @@
}
/**
- * Name : cam_sensor_i2c_pkt_parser
+ * Name : cam_sensor_i2c_command_parser
* Description : Parse CSL CCI packet and apply register settings
* Parameters : s_ctrl input/output sub_device
* arg input cam_control
@@ -260,7 +260,7 @@
* WAIT + n x RND_WR with num_cmd_buf = 1. Do not exepect RD/WR
* with different cmd_type and op_code in one command buffer.
*/
-int cam_sensor_i2c_pkt_parser(struct i2c_settings_array *i2c_reg_settings,
+int cam_sensor_i2c_command_parser(struct i2c_settings_array *i2c_reg_settings,
struct cam_cmd_buf_desc *cmd_desc, int32_t num_cmd_buffers)
{
int16_t rc = 0, i = 0;
diff --git a/drivers/media/platform/msm/camera/cam_sensor_module/cam_sensor_utils/cam_sensor_util.h b/drivers/media/platform/msm/camera/cam_sensor_module/cam_sensor_utils/cam_sensor_util.h
index 8a26369..d2079b0 100644
--- a/drivers/media/platform/msm/camera/cam_sensor_module/cam_sensor_utils/cam_sensor_util.h
+++ b/drivers/media/platform/msm/camera/cam_sensor_module/cam_sensor_utils/cam_sensor_util.h
@@ -34,7 +34,7 @@
int msm_camera_pinctrl_init
(struct msm_pinctrl_info *sensor_pctrl, struct device *dev);
-int cam_sensor_i2c_pkt_parser(struct i2c_settings_array *i2c_reg_settings,
+int cam_sensor_i2c_command_parser(struct i2c_settings_array *i2c_reg_settings,
struct cam_cmd_buf_desc *cmd_desc, int32_t num_cmd_buffers);
int32_t delete_request(struct i2c_settings_array *i2c_array);
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 3fe7b00..bfeb7c3 100644
--- a/drivers/media/platform/msm/camera/cam_sync/cam_sync.c
+++ b/drivers/media/platform/msm/camera/cam_sync/cam_sync.c
@@ -986,7 +986,7 @@
set_bit(0, sync_dev->bitmap);
sync_dev->work_queue = alloc_workqueue(CAM_SYNC_WORKQUEUE_NAME,
- WQ_HIGHPRI | WQ_UNBOUND, 0);
+ WQ_HIGHPRI | WQ_UNBOUND, 1);
if (!sync_dev->work_queue) {
CAM_ERR(CAM_SYNC,
diff --git a/drivers/media/platform/msm/camera/cam_utils/cam_debug_util.h b/drivers/media/platform/msm/camera/cam_utils/cam_debug_util.h
index 40dbe00..9745d45 100644
--- a/drivers/media/platform/msm/camera/cam_utils/cam_debug_util.h
+++ b/drivers/media/platform/msm/camera/cam_utils/cam_debug_util.h
@@ -119,7 +119,7 @@
* @brief : This Macro will prevent error print logs with ratelimit
*/
#define CAM_ERR_RATE_LIMIT(__module, fmt, args...) \
- pr_err_ratelimited("CAM_ERR: %s: %s: %d\n" fmt, \
+ pr_err_ratelimited("CAM_ERR: %s: %s: %d" fmt "\n", \
cam_get_module_name(__module), __func__, __LINE__, ##args)
#endif /* _CAM_DEBUG_UTIL_H_ */
diff --git a/drivers/media/platform/msm/camera/cam_utils/cam_soc_util.h b/drivers/media/platform/msm/camera/cam_utils/cam_soc_util.h
index 86c2737..8bd8275 100644
--- a/drivers/media/platform/msm/camera/cam_utils/cam_soc_util.h
+++ b/drivers/media/platform/msm/camera/cam_utils/cam_soc_util.h
@@ -35,7 +35,7 @@
#define CAM_SOC_MAX_BASE CAM_SOC_MAX_BLOCK
/* maximum number of device regulator */
-#define CAM_SOC_MAX_REGULATOR 4
+#define CAM_SOC_MAX_REGULATOR 5
/* maximum number of device clock */
#define CAM_SOC_MAX_CLK 32
diff --git a/drivers/media/platform/msm/sde/rotator/sde_rotator_core.c b/drivers/media/platform/msm/sde/rotator/sde_rotator_core.c
index a54f028..ab3223e 100644
--- a/drivers/media/platform/msm/sde/rotator/sde_rotator_core.c
+++ b/drivers/media/platform/msm/sde/rotator/sde_rotator_core.c
@@ -25,7 +25,6 @@
#include <linux/msm-bus-board.h>
#include <linux/regulator/consumer.h>
#include <linux/dma-direction.h>
-#include <linux/sde_rsc.h>
#include <soc/qcom/scm.h>
#include <soc/qcom/secure_buffer.h>
#include <asm/cacheflush.h>
@@ -301,7 +300,7 @@
static int sde_rotator_footswitch_ctrl(struct sde_rot_mgr *mgr, bool on)
{
- int ret;
+ int ret = 0;
if (mgr->regulator_enable == on) {
SDEROT_ERR("Regulators already in selected mode on=%d\n", on);
@@ -319,16 +318,13 @@
if (mgr->ops_hw_pre_pmevent)
mgr->ops_hw_pre_pmevent(mgr, on);
- if (mgr->rsc_client)
- ret = sde_rsc_client_state_update(mgr->rsc_client,
- on ? SDE_RSC_CLK_STATE : SDE_RSC_IDLE_STATE,
- NULL, SDE_RSC_INVALID_CRTC_ID, NULL);
- else
+ if (!sde_rot_mgr_pd_enabled(mgr))
ret = sde_rot_enable_vreg(mgr->module_power.vreg_config,
mgr->module_power.num_vreg, on);
if (ret) {
pr_err("rotator regulator failed to %s ret:%d client:%d\n",
- on ? "enable" : "disable", ret, mgr->rsc_client != NULL);
+ on ? "enable" : "disable", ret,
+ sde_rot_mgr_pd_enabled(mgr));
return ret;
}
@@ -2970,16 +2966,7 @@
{
int ret;
- mgr->rsc_client = sde_rsc_client_create(
- SDE_RSC_INDEX, "sde_rotator_core", false);
- if (IS_ERR(mgr->rsc_client)) {
- ret = PTR_ERR(mgr->rsc_client);
- pr_err("rsc client create returned %d\n", ret);
- mgr->rsc_client = NULL;
- return ret;
- }
-
- if (!mgr->rsc_client) {
+ if (!sde_rot_mgr_pd_enabled(mgr)) {
ret = sde_rotator_get_dt_vreg_data(
&pdev->dev, &mgr->module_power);
if (ret)
@@ -3007,12 +2994,8 @@
sde_rotator_unregister_clk(mgr);
sde_rotator_bus_scale_unregister(mgr);
- if (mgr->rsc_client) {
- sde_rsc_client_destroy(mgr->rsc_client);
- mgr->rsc_client = NULL;
- } else {
+ if (!sde_rot_mgr_pd_enabled(mgr))
sde_rotator_put_dt_vreg_data(&pdev->dev, &mgr->module_power);
- }
}
int sde_rotator_core_init(struct sde_rot_mgr **pmgr,
diff --git a/drivers/media/platform/msm/sde/rotator/sde_rotator_core.h b/drivers/media/platform/msm/sde/rotator/sde_rotator_core.h
index e2f5465..57a68ed 100644
--- a/drivers/media/platform/msm/sde/rotator/sde_rotator_core.h
+++ b/drivers/media/platform/msm/sde/rotator/sde_rotator_core.h
@@ -385,7 +385,6 @@
* @reg_bus: register bus configuration state
* @module_power: power/clock configuration state
* @regulator_enable: true if foot switch is enabled; false otherwise
- * @rsc_client: pointer to rsc client handle
* @res_ref_cnt: reference count of how many times resource is requested
* @rot_enable_clk_cnt: reference count of how many times clock is requested
* @rot_clk: array of rotator and periphery clocks
@@ -431,8 +430,6 @@
struct sde_module_power module_power;
bool regulator_enable;
- struct sde_rsc_client *rsc_client;
-
int res_ref_cnt;
int rot_enable_clk_cnt;
struct sde_rot_clk *rot_clk;
@@ -768,6 +765,15 @@
mutex_unlock(&mgr->lock);
}
+/*
+ * sde_rot_mgr_pd_enabled - return true if power domain is enabled
+ * @mgr: Pointer to rotator manager
+ */
+static inline bool sde_rot_mgr_pd_enabled(struct sde_rot_mgr *mgr)
+{
+ return mgr && mgr->device && mgr->device->pm_domain;
+}
+
#if defined(CONFIG_PM)
int sde_rotator_runtime_resume(struct device *dev);
int sde_rotator_runtime_suspend(struct device *dev);
diff --git a/drivers/media/platform/msm/sde/rotator/sde_rotator_dev.c b/drivers/media/platform/msm/sde/rotator/sde_rotator_dev.c
index f81cd2f..976155e 100644
--- a/drivers/media/platform/msm/sde/rotator/sde_rotator_dev.c
+++ b/drivers/media/platform/msm/sde/rotator/sde_rotator_dev.c
@@ -1162,7 +1162,6 @@
struct sde_rotator_request *request)
{
struct sde_rotator_ctx *ctx;
- struct sde_rot_entry_container *req;
if (!request || !request->ctx) {
SDEROT_ERR("invalid parameters\n");
@@ -1170,11 +1169,7 @@
}
ctx = request->ctx;
- req = request->req;
-
- if (req && req->entries && req->count)
- ctx->retired_sequence_id =
- req->entries[req->count - 1].item.sequence_id;
+ ctx->retired_sequence_id = request->sequence_id;
wake_up(&ctx->wait_queue);
@@ -1199,6 +1194,7 @@
ctx = request->ctx;
request->req = NULL;
+ request->sequence_id = 0;
request->committed = false;
spin_lock(&ctx->list_lock);
list_del_init(&request->list);
@@ -1216,17 +1212,14 @@
static bool sde_rotator_is_request_retired(struct sde_rotator_request *request)
{
struct sde_rotator_ctx *ctx;
- struct sde_rot_entry_container *req;
u32 sequence_id;
s32 retire_delta;
- if (!request || !request->ctx || !request->req ||
- !request->req->entries || !request->req->count)
+ if (!request || !request->ctx)
return true;
ctx = request->ctx;
- req = request->req;
- sequence_id = req->entries[req->count - 1].item.sequence_id;
+ sequence_id = request->sequence_id;
retire_delta = (s32) (ctx->retired_sequence_id - sequence_id);
@@ -1643,6 +1636,7 @@
}
request->req = req;
+ request->sequence_id = req->entries[0].item.sequence_id;
spin_lock(&ctx->list_lock);
list_del_init(&request->list);
@@ -3000,6 +2994,8 @@
sde_rotator_queue_request(rot_dev->mgr, ctx->private, req);
request->req = req;
+ request->sequence_id = item.sequence_id;
+ request->committed = true;
return 0;
error_handle_request:
@@ -3008,6 +3004,8 @@
error_fence_wait:
error_null_buffer:
request->req = NULL;
+ request->sequence_id = 0;
+ request->committed = false;
return ret;
}
diff --git a/drivers/media/platform/msm/sde/rotator/sde_rotator_dev.h b/drivers/media/platform/msm/sde/rotator/sde_rotator_dev.h
index 5ea9e15..a464a39 100644
--- a/drivers/media/platform/msm/sde/rotator/sde_rotator_dev.h
+++ b/drivers/media/platform/msm/sde/rotator/sde_rotator_dev.h
@@ -90,9 +90,12 @@
* @list: list head for submit/retire list
* @submit_work: submit work structure
* @retire_work: retire work structure
- * @request: Pointer to core layer rotator manager request
+ * @req: Pointer to core layer rotator manager request
+ * Request can be freed by core layer during sde_rotator_stop_streaming.
+ * Avoid dereference in dev layer if possible.
* @ctx: Pointer to parent context
* @committed: true if request committed to hardware
+ * @sequence_id: sequence identifier of this request
*/
struct sde_rotator_request {
struct list_head list;
@@ -101,6 +104,7 @@
struct sde_rot_entry_container *req;
struct sde_rotator_ctx *ctx;
bool committed;
+ u32 sequence_id;
};
/*
diff --git a/drivers/media/platform/msm/vidc/msm_venc.c b/drivers/media/platform/msm/vidc/msm_venc.c
index 22c0df1..266c50e 100644
--- a/drivers/media/platform/msm/vidc/msm_venc.c
+++ b/drivers/media/platform/msm/vidc/msm_venc.c
@@ -1317,6 +1317,18 @@
break;
case V4L2_CID_MPEG_VIDC_VIDEO_RATE_CONTROL:
{
+ struct v4l2_ctrl *hybrid_hp = TRY_GET_CTRL(
+ V4L2_CID_MPEG_VIDC_VIDEO_HYBRID_HIERP_MODE);
+ if ((ctrl->val ==
+ V4L2_CID_MPEG_VIDC_VIDEO_RATE_CONTROL_CBR_VFR
+ || ctrl->val ==
+ V4L2_CID_MPEG_VIDC_VIDEO_RATE_CONTROL_VBR_VFR)
+ && hybrid_hp->val) {
+ dprintk(VIDC_ERR,
+ "CBR_VFR/VBR_VFR not allowed with Hybrid HP\n");
+ rc = -ENOTSUPP;
+ break;
+ }
property_id = HAL_PARAM_VENC_RATE_CONTROL;
property_val = ctrl->val;
pdata = &property_val;
@@ -1756,10 +1768,26 @@
pdata = &enable;
break;
case V4L2_CID_MPEG_VIDC_VIDEO_HYBRID_HIERP_MODE:
+ {
+ struct v4l2_ctrl *rate_control;
+
+ rate_control =
+ TRY_GET_CTRL(V4L2_CID_MPEG_VIDC_VIDEO_RATE_CONTROL);
+ if ((rate_control->val ==
+ V4L2_CID_MPEG_VIDC_VIDEO_RATE_CONTROL_CBR_VFR ||
+ rate_control->val ==
+ V4L2_CID_MPEG_VIDC_VIDEO_RATE_CONTROL_VBR_VFR)
+ && ctrl->val) {
+ dprintk(VIDC_ERR,
+ "Hybrid HP not allowed with CBR_VFR/VBR_VFR\n");
+ rc = -ENOTSUPP;
+ break;
+ }
property_id = HAL_PARAM_VENC_HIER_P_HYBRID_MODE;
hyb_hierp.layers = ctrl->val;
pdata = &hyb_hierp;
break;
+ }
case V4L2_CID_VIDC_QBUF_MODE:
property_id = HAL_PARAM_SYNC_BASED_INTERRUPT;
enable.enable = ctrl->val == V4L2_VIDC_QBUF_BATCHED;
diff --git a/drivers/media/platform/msm/vidc/msm_vidc.c b/drivers/media/platform/msm/vidc/msm_vidc.c
index ae2a2c6..907e01f 100644
--- a/drivers/media/platform/msm/vidc/msm_vidc.c
+++ b/drivers/media/platform/msm/vidc/msm_vidc.c
@@ -626,6 +626,17 @@
"Failed to find buffer queue for type = %d\n", i);
return -EINVAL;
}
+
+ if (!inst->in_reconfig) {
+ dprintk(VIDC_DBG, "%s: inst %pK release resources\n",
+ __func__, inst);
+ rc = msm_comm_try_state(inst, MSM_VIDC_RELEASE_RESOURCES_DONE);
+ if (rc)
+ dprintk(VIDC_ERR,
+ "%s: inst %pK move to rel res done failed\n",
+ __func__, inst);
+ }
+
dprintk(VIDC_DBG, "Calling streamoff\n");
mutex_lock(&q->lock);
rc = vb2_streamoff(&q->vb2_bufq, i);
@@ -835,7 +846,8 @@
/* For decoder No need to sanity till LOAD_RESOURCES */
if (inst->session_type == MSM_VIDC_DECODER &&
- inst->state < MSM_VIDC_LOAD_RESOURCES_DONE) {
+ (inst->state < MSM_VIDC_LOAD_RESOURCES_DONE ||
+ inst->state >= MSM_VIDC_RELEASE_RESOURCES_DONE)) {
dprintk(VIDC_DBG,
"No need to verify buffer counts : %pK\n", inst);
return 0;
@@ -1159,14 +1171,17 @@
mbuf = msm_comm_get_vidc_buffer(inst, vb2);
if (IS_ERR_OR_NULL(mbuf)) {
- if (PTR_ERR(mbuf) != -EEXIST)
- print_vb2_buffer(VIDC_ERR, "failed to get vidc-buf",
- inst, vb2);
- return;
+ if (PTR_ERR(mbuf) == -EEXIST)
+ return;
+ print_vb2_buffer(VIDC_ERR, "failed to get vidc-buf",
+ inst, vb2);
+ rc = -EINVAL;
+ goto error;
}
if (!kref_get_mbuf(inst, mbuf)) {
dprintk(VIDC_ERR, "%s: mbuf not found\n", __func__);
- return;
+ rc = -EINVAL;
+ goto error;
}
rc = msm_comm_qbuf(inst, mbuf);
@@ -1174,6 +1189,10 @@
print_vidc_buffer(VIDC_ERR, "failed qbuf", inst, mbuf);
kref_put_mbuf(mbuf);
+
+error:
+ if (rc)
+ msm_comm_generate_session_error(inst);
}
static const struct vb2_ops msm_vidc_vb2q_ops = {
@@ -1488,6 +1507,16 @@
HAL_BUFFER_INPUT);
return -EINVAL;
}
+
+ if (inst->session_type == MSM_VIDC_DECODER &&
+ !(inst->flags & VIDC_THUMBNAIL) &&
+ inst->fmts[OUTPUT_PORT].fourcc ==
+ V4L2_PIX_FMT_VP9 &&
+ bufreq->buffer_count_min_host <
+ MIN_NUM_OUTPUT_BUFFERS_VP9)
+ bufreq->buffer_count_min_host =
+ MIN_NUM_OUTPUT_BUFFERS_VP9;
+
ctrl->val = bufreq->buffer_count_min_host;
break;
case V4L2_CID_MPEG_VIDC_VIDEO_TME_PAYLOAD_VERSION:
diff --git a/drivers/media/platform/msm/vidc/msm_vidc_clocks.c b/drivers/media/platform/msm/vidc/msm_vidc_clocks.c
index f2f6d58..5183ddd 100644
--- a/drivers/media/platform/msm/vidc/msm_vidc_clocks.c
+++ b/drivers/media/platform/msm/vidc/msm_vidc_clocks.c
@@ -22,6 +22,19 @@
#define MSM_VIDC_MIN_UBWC_COMPRESSION_RATIO (1 << 16)
#define MSM_VIDC_MAX_UBWC_COMPRESSION_RATIO (5 << 16)
+static inline void msm_dcvs_print_dcvs_stats(struct clock_data *dcvs)
+{
+ dprintk(VIDC_PROF,
+ "DCVS: Load_Low %d, Load Norm %d, Load High %d\n",
+ dcvs->load_low,
+ dcvs->load_norm,
+ dcvs->load_high);
+
+ dprintk(VIDC_PROF,
+ "DCVS: min_threshold %d, max_threshold %d\n",
+ dcvs->min_threshold, dcvs->max_threshold);
+}
+
static inline unsigned long int get_ubwc_compression_ratio(
struct ubwc_cr_stats_info_type ubwc_stats_info)
{
@@ -509,6 +522,14 @@
unsigned long vpp_cycles = 0, vsp_cycles = 0;
u32 vpp_cycles_per_mb;
u32 mbs_per_second;
+ struct msm_vidc_core *core = NULL;
+ int i = 0;
+ struct allowed_clock_rates_table *allowed_clks_tbl = NULL;
+ u64 rate = 0;
+ struct clock_data *dcvs = NULL;
+
+ core = inst->core;
+ dcvs = &inst->clk_data;
mbs_per_second = msm_comm_get_inst_load_per_core(inst,
LOAD_CALC_NO_QUIRKS);
@@ -544,6 +565,22 @@
freq = max(vpp_cycles, vsp_cycles);
+ dprintk(VIDC_DBG, "Update DCVS Load\n");
+ allowed_clks_tbl = core->resources.allowed_clks_tbl;
+ for (i = core->resources.allowed_clks_tbl_size - 1; i >= 0; i--) {
+ rate = allowed_clks_tbl[i].clock_rate;
+ if (rate >= freq)
+ break;
+ }
+
+ dcvs->load_norm = rate;
+ dcvs->load_low = i < (core->resources.allowed_clks_tbl_size - 1) ?
+ allowed_clks_tbl[i+1].clock_rate : dcvs->load_norm;
+ dcvs->load_high = i > 0 ? allowed_clks_tbl[i-1].clock_rate :
+ dcvs->load_norm;
+
+ msm_dcvs_print_dcvs_stats(dcvs);
+
dprintk(VIDC_PROF, "%s Inst %pK : Filled Len = %d Freq = %lu\n",
__func__, inst, filled_len, freq);
@@ -652,7 +689,9 @@
operating_rate = operating_rate >> 16;
- if ((curr_operating_rate + ops_left) >= operating_rate) {
+ if ((curr_operating_rate + ops_left) >= operating_rate ||
+ !msm_vidc_clock_scaling ||
+ inst->clk_data.buffer_counter < DCVS_FTB_WINDOW) {
dprintk(VIDC_DBG,
"Requestd operating rate is valid %u\n",
operating_rate);
@@ -796,19 +835,6 @@
return rc;
}
-static inline void msm_dcvs_print_dcvs_stats(struct clock_data *dcvs)
-{
- dprintk(VIDC_PROF,
- "DCVS: Load_Low %d, Load Norm %d, Load High %d\n",
- dcvs->load_low,
- dcvs->load_norm,
- dcvs->load_high);
-
- dprintk(VIDC_PROF,
- "DCVS: min_threshold %d, max_threshold %d\n",
- dcvs->min_threshold, dcvs->max_threshold);
-}
-
void msm_clock_data_reset(struct msm_vidc_inst *inst)
{
struct msm_vidc_core *core;
@@ -852,6 +878,10 @@
}
dcvs->max_threshold = output_buf_req->buffer_count_actual -
output_buf_req->buffer_count_min_host + 1;
+ /* Compensate for decode only frames */
+ if (inst->fmts[OUTPUT_PORT].fourcc == V4L2_PIX_FMT_VP9)
+ dcvs->max_threshold += 2;
+
dcvs->min_threshold =
msm_vidc_get_extra_buff_count(inst, dcvs->buffer_type);
} else {
diff --git a/drivers/media/platform/msm/vidc/msm_vidc_common.c b/drivers/media/platform/msm/vidc/msm_vidc_common.c
index f8c5798..ed3cfa3 100644
--- a/drivers/media/platform/msm/vidc/msm_vidc_common.c
+++ b/drivers/media/platform/msm/vidc/msm_vidc_common.c
@@ -70,8 +70,6 @@
"Extradata UBWC CR stats info",
};
-static void msm_comm_generate_session_error(struct msm_vidc_inst *inst);
-static void msm_comm_generate_sys_error(struct msm_vidc_inst *inst);
static void handle_session_error(enum hal_command_response cmd, void *data);
static void msm_vidc_print_running_insts(struct msm_vidc_core *core);
@@ -3336,20 +3334,11 @@
}
mutex_lock(&core->lock);
- if (core->state != VIDC_CORE_INIT_DONE) {
- dprintk(VIDC_ERR,
- "%s - fw is not in proper state, skip suspend\n",
- __func__);
- rc = -EINVAL;
- goto exit;
- }
-
rc = call_hfi_op(hdev, suspend, hdev->hfi_device_data);
if (rc)
dprintk(VIDC_WARN, "Failed to suspend\n");
-
-exit:
mutex_unlock(&core->lock);
+
return rc;
}
@@ -5302,6 +5291,8 @@
return -EINVAL;
}
hdev = core->device;
+
+ mutex_lock(&core->lock);
if (core->state == VIDC_CORE_INIT_DONE) {
/*
* In current implementation user-initiated SSR triggers
@@ -5317,7 +5308,11 @@
__func__);
core->trigger_ssr = false;
}
+ } else {
+ dprintk(VIDC_WARN, "%s: video core %pK not initialized\n",
+ __func__, core);
}
+ mutex_unlock(&core->lock);
return rc;
}
@@ -5471,15 +5466,15 @@
rotation = msm_comm_g_ctrl_for_id(inst,
V4L2_CID_MPEG_VIDC_VIDEO_ROTATION);
- output_height = inst->prop.height[CAPTURE_PORT];
- output_width = inst->prop.width[CAPTURE_PORT];
+ output_height = ALIGN(inst->prop.height[CAPTURE_PORT], 16);
+ output_width = ALIGN(inst->prop.width[CAPTURE_PORT], 16);
if ((output_width != output_height) &&
(rotation == V4L2_CID_MPEG_VIDC_VIDEO_ROTATION_90 ||
rotation == V4L2_CID_MPEG_VIDC_VIDEO_ROTATION_270)) {
- output_width = inst->prop.height[CAPTURE_PORT];
- output_height = inst->prop.width[CAPTURE_PORT];
+ output_width = ALIGN(inst->prop.height[CAPTURE_PORT], 16);
+ output_height = ALIGN(inst->prop.width[CAPTURE_PORT], 16);
dprintk(VIDC_DBG,
"Rotation=%u Swapped Output W=%u H=%u to check capability",
rotation, output_width, output_height);
@@ -5520,7 +5515,7 @@
return rc;
}
-static void msm_comm_generate_session_error(struct msm_vidc_inst *inst)
+void msm_comm_generate_session_error(struct msm_vidc_inst *inst)
{
enum hal_command_response cmd = HAL_SESSION_ERROR;
struct msm_vidc_cb_cmd_done response = {0};
@@ -5535,7 +5530,7 @@
handle_session_error(cmd, (void *)&response);
}
-static void msm_comm_generate_sys_error(struct msm_vidc_inst *inst)
+void msm_comm_generate_sys_error(struct msm_vidc_inst *inst)
{
struct msm_vidc_core *core;
enum hal_command_response cmd = HAL_SYS_ERROR;
diff --git a/drivers/media/platform/msm/vidc/msm_vidc_common.h b/drivers/media/platform/msm/vidc/msm_vidc_common.h
index 79f3c6b..fa9cdee 100644
--- a/drivers/media/platform/msm/vidc/msm_vidc_common.h
+++ b/drivers/media/platform/msm/vidc/msm_vidc_common.h
@@ -77,6 +77,8 @@
V4L2_CTRL_DRIVER_PRIV(idx))
void msm_comm_session_clean(struct msm_vidc_inst *inst);
int msm_comm_kill_session(struct msm_vidc_inst *inst);
+void msm_comm_generate_session_error(struct msm_vidc_inst *inst);
+void msm_comm_generate_sys_error(struct msm_vidc_inst *inst);
enum multi_stream msm_comm_get_stream_output_mode(struct msm_vidc_inst *inst);
enum hal_buffer msm_comm_get_hal_output_buffer(struct msm_vidc_inst *inst);
int msm_comm_smem_alloc(struct msm_vidc_inst *inst, size_t size, u32 align,
diff --git a/drivers/media/platform/msm/vidc/msm_vidc_internal.h b/drivers/media/platform/msm/vidc/msm_vidc_internal.h
index 37645fe..2e2dd13 100644
--- a/drivers/media/platform/msm/vidc/msm_vidc_internal.h
+++ b/drivers/media/platform/msm/vidc/msm_vidc_internal.h
@@ -45,6 +45,7 @@
#define MIN_SUPPORTED_HEIGHT 32
#define DEFAULT_FPS 15
#define MIN_NUM_OUTPUT_BUFFERS 1
+#define MIN_NUM_OUTPUT_BUFFERS_VP9 6
#define MIN_NUM_CAPTURE_BUFFERS 1
#define MAX_NUM_OUTPUT_BUFFERS VIDEO_MAX_FRAME // same as VB2_MAX_FRAME
#define MAX_NUM_CAPTURE_BUFFERS VIDEO_MAX_FRAME // same as VB2_MAX_FRAME
diff --git a/drivers/media/platform/msm/vidc/msm_vidc_platform.c b/drivers/media/platform/msm/vidc/msm_vidc_platform.c
index 56524ccd4..1818788 100644
--- a/drivers/media/platform/msm/vidc/msm_vidc_platform.c
+++ b/drivers/media/platform/msm/vidc/msm_vidc_platform.c
@@ -66,6 +66,7 @@
CODEC_ENTRY(V4L2_PIX_FMT_H264, MSM_VIDC_ENCODER, 125, 675, 320),
CODEC_ENTRY(V4L2_PIX_FMT_HEVC, MSM_VIDC_ENCODER, 125, 675, 320),
CODEC_ENTRY(V4L2_PIX_FMT_VP8, MSM_VIDC_ENCODER, 125, 675, 320),
+ CODEC_ENTRY(V4L2_PIX_FMT_TME, MSM_VIDC_ENCODER, 0, 540, 540),
CODEC_ENTRY(V4L2_PIX_FMT_MPEG2, MSM_VIDC_DECODER, 50, 200, 200),
CODEC_ENTRY(V4L2_PIX_FMT_H264, MSM_VIDC_DECODER, 50, 200, 200),
CODEC_ENTRY(V4L2_PIX_FMT_HEVC, MSM_VIDC_DECODER, 50, 200, 200),
diff --git a/drivers/media/platform/msm/vidc/venus_hfi.c b/drivers/media/platform/msm/vidc/venus_hfi.c
index e260886..60169e9 100644
--- a/drivers/media/platform/msm/vidc/venus_hfi.c
+++ b/drivers/media/platform/msm/vidc/venus_hfi.c
@@ -1029,8 +1029,10 @@
flush_delayed_work(&venus_hfi_pm_work);
mutex_lock(&device->lock);
- if (device->power_enabled)
+ if (device->power_enabled) {
+ dprintk(VIDC_WARN, "%s: Venus is busy\n", __func__);
rc = -EBUSY;
+ }
mutex_unlock(&device->lock);
return rc;
}
diff --git a/drivers/misc/qseecom.c b/drivers/misc/qseecom.c
index 7567f86..fe5dad7 100644
--- a/drivers/misc/qseecom.c
+++ b/drivers/misc/qseecom.c
@@ -252,7 +252,6 @@
bool whitelist_support;
bool commonlib_loaded;
bool commonlib64_loaded;
- struct ion_handle *cmnlib_ion_handle;
struct ce_hw_usage_info ce_info;
int qsee_bw_count;
@@ -4298,6 +4297,7 @@
void *cmd_buf = NULL;
size_t cmd_len;
uint32_t app_arch = 0;
+ struct ion_handle *cmnlib_ion_handle = NULL;
if (!cmnlib_name) {
pr_err("cmnlib_name is NULL\n");
@@ -4312,7 +4312,7 @@
if (__qseecom_get_fw_size(cmnlib_name, &fw_size, &app_arch))
return -EIO;
- ret = __qseecom_allocate_img_data(&qseecom.cmnlib_ion_handle,
+ ret = __qseecom_allocate_img_data(&cmnlib_ion_handle,
&img_data, fw_size, &pa);
if (ret)
return -EIO;
@@ -4353,7 +4353,7 @@
goto exit_unregister_bus_bw_need;
}
- ret = msm_ion_do_cache_op(qseecom.ion_clnt, qseecom.cmnlib_ion_handle,
+ ret = msm_ion_do_cache_op(qseecom.ion_clnt, cmnlib_ion_handle,
img_data, fw_size,
ION_IOC_CLEAN_INV_CACHES);
if (ret) {
@@ -4401,7 +4401,7 @@
}
exit_free_img_data:
- __qseecom_free_img_data(&qseecom.cmnlib_ion_handle);
+ __qseecom_free_img_data(&cmnlib_ion_handle);
return ret;
}
@@ -7856,7 +7856,7 @@
uint32_t hlos_num_ce_hw_instances;
uint32_t disk_encrypt_pipe;
uint32_t file_encrypt_pipe;
- uint32_t hlos_ce_hw_instance[MAX_CE_PIPE_PAIR_PER_UNIT];
+ uint32_t hlos_ce_hw_instance[MAX_CE_PIPE_PAIR_PER_UNIT] = {0};
int i;
const int *tbl;
int size;
diff --git a/drivers/mmc/card/block.c b/drivers/mmc/card/block.c
index eb14f0f..a209aa6 100644
--- a/drivers/mmc/card/block.c
+++ b/drivers/mmc/card/block.c
@@ -3461,6 +3461,8 @@
else
mrq->data->error = -ETIMEDOUT;
+ host->err_stats[MMC_ERR_CMDQ_REQ_TIMEOUT]++;
+
if (mrq->cmd && mrq->cmd->error) {
if (!(mrq->req->cmd_flags & REQ_PREFLUSH)) {
/*
diff --git a/drivers/mmc/core/bus.c b/drivers/mmc/core/bus.c
index a378b8c..fb2ca3d 100644
--- a/drivers/mmc/core/bus.c
+++ b/drivers/mmc/core/bus.c
@@ -431,7 +431,6 @@
}
kfree(card->wr_pack_stats.packing_events);
- kfree(card->cached_ext_csd);
put_device(&card->dev);
}
diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c
index 727b301..edbf682 100644
--- a/drivers/mmc/core/core.c
+++ b/drivers/mmc/core/core.c
@@ -4134,6 +4134,7 @@
if (!host->max_busy_timeout ||
(host->caps2 & MMC_CAP2_MAX_DISCARD_SIZE))
+ return UINT_MAX;
/*
* Without erase_group_def set, MMC erase timeout depends on clock
@@ -4424,6 +4425,7 @@
goto out;
}
mmc_rescan_try_freq(host, host->f_min);
+ host->err_stats[MMC_ERR_CMD_TIMEOUT] = 0;
mmc_release_host(host);
out:
diff --git a/drivers/mmc/core/debugfs.c b/drivers/mmc/core/debugfs.c
index d1a0235..7163e34 100644
--- a/drivers/mmc/core/debugfs.c
+++ b/drivers/mmc/core/debugfs.c
@@ -381,6 +381,79 @@
DEFINE_SIMPLE_ATTRIBUTE(mmc_err_state, mmc_err_state_get,
mmc_err_state_clear, "%llu\n");
+static int mmc_err_stats_show(struct seq_file *file, void *data)
+{
+ struct mmc_host *host = (struct mmc_host *)file->private;
+
+ if (!host)
+ return -EINVAL;
+
+ seq_printf(file, "# Command Timeout Occurred:\t %d\n",
+ host->err_stats[MMC_ERR_CMD_TIMEOUT]);
+
+ seq_printf(file, "# Command CRC Errors Occurred:\t %d\n",
+ host->err_stats[MMC_ERR_CMD_CRC]);
+
+ seq_printf(file, "# Data Timeout Occurred:\t %d\n",
+ host->err_stats[MMC_ERR_DAT_TIMEOUT]);
+
+ seq_printf(file, "# Data CRC Errors Occurred:\t %d\n",
+ host->err_stats[MMC_ERR_DAT_CRC]);
+
+ seq_printf(file, "# Auto-Cmd Error Occurred:\t %d\n",
+ host->err_stats[MMC_ERR_ADMA]);
+
+ seq_printf(file, "# ADMA Error Occurred:\t %d\n",
+ host->err_stats[MMC_ERR_ADMA]);
+
+ seq_printf(file, "# Tuning Error Occurred:\t %d\n",
+ host->err_stats[MMC_ERR_TUNING]);
+
+ seq_printf(file, "# CMDQ RED Errors:\t\t %d\n",
+ host->err_stats[MMC_ERR_CMDQ_RED]);
+
+ seq_printf(file, "# CMDQ GCE Errors:\t\t %d\n",
+ host->err_stats[MMC_ERR_CMDQ_GCE]);
+
+ seq_printf(file, "# CMDQ ICCE Errors:\t\t %d\n",
+ host->err_stats[MMC_ERR_CMDQ_ICCE]);
+
+ seq_printf(file, "# Request Timedout:\t %d\n",
+ host->err_stats[MMC_ERR_REQ_TIMEOUT]);
+
+ seq_printf(file, "# CMDQ Request Timedout:\t %d\n",
+ host->err_stats[MMC_ERR_CMDQ_REQ_TIMEOUT]);
+
+ seq_printf(file, "# ICE Config Errors:\t\t %d\n",
+ host->err_stats[MMC_ERR_ICE_CFG]);
+ return 0;
+}
+
+static int mmc_err_stats_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, mmc_err_stats_show, inode->i_private);
+}
+
+static ssize_t mmc_err_stats_write(struct file *filp, const char __user *ubuf,
+ size_t cnt, loff_t *ppos)
+{
+ struct mmc_host *host = filp->f_mapping->host->i_private;
+
+ if (!host)
+ return -EINVAL;
+
+ pr_debug("%s: Resetting MMC error statistics", __func__);
+ memset(host->err_stats, 0, sizeof(host->err_stats));
+
+ return cnt;
+}
+
+static const struct file_operations mmc_err_stats_fops = {
+ .open = mmc_err_stats_open,
+ .read = seq_read,
+ .write = mmc_err_stats_write,
+};
+
void mmc_add_host_debugfs(struct mmc_host *host)
{
struct dentry *root;
@@ -430,6 +503,10 @@
&mmc_err_state))
goto err_node;
+ if (!debugfs_create_file("err_stats", 0600, root, host,
+ &mmc_err_stats_fops))
+ goto err_node;
+
#ifdef CONFIG_MMC_CLKGATE
if (!debugfs_create_u32("clk_delay", (S_IRUSR | S_IWUSR),
root, &host->clk_delay))
diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c
index 36295f5..efb1b81 100644
--- a/drivers/mmc/core/mmc.c
+++ b/drivers/mmc/core/mmc.c
@@ -750,7 +750,6 @@
return err;
}
- card->cached_ext_csd = ext_csd;
err = mmc_decode_ext_csd(card, ext_csd);
kfree(ext_csd);
return err;
@@ -1315,6 +1314,8 @@
if (card->ext_csd.strobe_support && host->ops->enhanced_strobe) {
mmc_host_clk_hold(host);
err = host->ops->enhanced_strobe(host);
+ if (!err)
+ host->ios.enhanced_strobe = true;
mmc_host_clk_release(host);
} else if ((host->caps2 & MMC_CAP2_HS400_POST_TUNING) &&
host->ops->execute_tuning) {
@@ -1604,12 +1605,19 @@
/* For Enhance Strobe HS400 flow */
if (card->ext_csd.strobe_support &&
card->mmc_avail_type & EXT_CSD_CARD_TYPE_HS400 &&
- card->host->caps & MMC_CAP_8_BIT_DATA)
- err = mmc_select_hs400es(card);
- else if (card->mmc_avail_type & EXT_CSD_CARD_TYPE_HS200)
+ card->host->caps & MMC_CAP_8_BIT_DATA) {
+ err = mmc_select_hs400(card);
+ if (err) {
+ pr_err("%s: %s: mmc_select_hs400 failed : %d\n",
+ mmc_hostname(card->host), __func__,
+ err);
+ err = mmc_select_hs400es(card);
+ }
+ } else if (card->mmc_avail_type & EXT_CSD_CARD_TYPE_HS200) {
err = mmc_select_hs200(card);
- else if (card->mmc_avail_type & EXT_CSD_CARD_TYPE_HS)
+ } else if (card->mmc_avail_type & EXT_CSD_CARD_TYPE_HS) {
err = mmc_select_hs(card);
+ }
if (err && err != -EBADMSG)
return err;
@@ -2863,10 +2871,13 @@
int err = 0;
MMC_TRACE(host, "%s: Enter\n", __func__);
+ err = _mmc_resume(host);
+ pm_runtime_set_active(&host->card->dev);
+ pm_runtime_mark_last_busy(&host->card->dev);
pm_runtime_enable(&host->card->dev);
-
MMC_TRACE(host, "%s: Exit err: %d\n", __func__, err);
- return 0;
+
+ return err;
}
#define MAX_DEFER_SUSPEND_COUNTER 20
diff --git a/drivers/mmc/core/sd.c b/drivers/mmc/core/sd.c
index d591ded..10d55b8 100644
--- a/drivers/mmc/core/sd.c
+++ b/drivers/mmc/core/sd.c
@@ -1348,10 +1348,13 @@
int err = 0;
MMC_TRACE(host, "%s: Enter\n", __func__);
+ err = _mmc_sd_resume(host);
+ pm_runtime_set_active(&host->card->dev);
+ pm_runtime_mark_last_busy(&host->card->dev);
pm_runtime_enable(&host->card->dev);
-
MMC_TRACE(host, "%s: Exit err: %d\n", __func__, err);
- return 0;
+
+ return err;
}
/*
diff --git a/drivers/mmc/host/cmdq_hci.c b/drivers/mmc/host/cmdq_hci.c
index 591d6b2..f16a999 100644
--- a/drivers/mmc/host/cmdq_hci.c
+++ b/drivers/mmc/host/cmdq_hci.c
@@ -803,6 +803,7 @@
if (cq_host->ops->crypto_cfg) {
err = cq_host->ops->crypto_cfg(mmc, mrq, tag, &ice_ctx);
if (err) {
+ mmc->err_stats[MMC_ERR_ICE_CFG]++;
pr_err("%s: failed to configure crypto: err %d tag %d\n",
mmc_hostname(mmc), err, tag);
goto out;
@@ -1015,6 +1016,7 @@
mmc->card->bkops.needs_check = true;
mrq->cmdq_req->resp_err = true;
+ mmc->err_stats[MMC_ERR_CMDQ_RED]++;
pr_err("%s: Response error (0x%08x) from card !!!",
mmc_hostname(mmc), cmdq_readl(cq_host, CQCRA));
@@ -1030,6 +1032,7 @@
if (stat_err & CQIS_GCE) {
if (mrq->data)
mrq->data->error = -EIO;
+ mmc->err_stats[MMC_ERR_CMDQ_GCE]++;
pr_err("%s: Crypto generic error while processing task %lu!",
mmc_hostname(mmc), tag);
MMC_TRACE(mmc, "%s: GCE error detected with tag %lu\n",
@@ -1054,6 +1057,7 @@
if (dbr_set ^ dev_pend_set)
tag = ffs(dbr_set ^ dev_pend_set) - 1;
mrq = get_req_by_tag(cq_host, tag);
+ mmc->err_stats[MMC_ERR_CMDQ_ICCE]++;
pr_err("%s: Crypto config error while processing task %lu!",
mmc_hostname(mmc), tag);
MMC_TRACE(mmc, "%s: ICCE error with tag %lu\n",
diff --git a/drivers/mmc/host/sdhci-msm.c b/drivers/mmc/host/sdhci-msm.c
index 55d9cf5..6b018e1 100644
--- a/drivers/mmc/host/sdhci-msm.c
+++ b/drivers/mmc/host/sdhci-msm.c
@@ -968,7 +968,10 @@
* Reprogramming the value in case it might have been modified by
* bootloaders.
*/
- if (msm_host->rclk_delay_fix) {
+ if (msm_host->pdata->rclk_wa) {
+ writel_relaxed(msm_host->pdata->ddr_config, host->ioaddr +
+ msm_host_offset->CORE_DDR_CONFIG_2);
+ } else if (msm_host->rclk_delay_fix) {
writel_relaxed(DDR_CONFIG_2_POR_VAL, host->ioaddr +
msm_host_offset->CORE_DDR_CONFIG_2);
} else {
@@ -1281,6 +1284,11 @@
pr_debug("%s: %s: found *** good *** phase = %d\n",
mmc_hostname(mmc), __func__, phase);
} else {
+ /* Ignore crc errors occurred during tuning */
+ if (cmd.error)
+ mmc->err_stats[MMC_ERR_CMD_CRC]--;
+ else if (data.error)
+ mmc->err_stats[MMC_ERR_DAT_CRC]--;
pr_debug("%s: %s: found ## bad ## phase = %d\n",
mmc_hostname(mmc), __func__, phase);
}
@@ -1986,6 +1994,9 @@
msm_host->regs_restore.is_supported =
of_property_read_bool(np, "qcom,restore-after-cx-collapse");
+ if (!of_property_read_u32(np, "qcom,ddr-config", &pdata->ddr_config))
+ pdata->rclk_wa = true;
+
return pdata;
out:
return NULL;
@@ -3013,11 +3024,20 @@
}
}
+ if (!IS_ERR(msm_host->bus_aggr_clk)) {
+ rc = clk_prepare_enable(msm_host->bus_aggr_clk);
+ if (rc) {
+ pr_err("%s: %s: failed to enable the bus aggr clk with error %d\n",
+ mmc_hostname(host->mmc), __func__, rc);
+ goto disable_pclk;
+ }
+ }
+
rc = clk_prepare_enable(msm_host->clk);
if (rc) {
pr_err("%s: %s: failed to enable the host-clk with error %d\n",
mmc_hostname(host->mmc), __func__, rc);
- goto disable_pclk;
+ goto disable_bus_aggr_clk;
}
if (!IS_ERR(msm_host->ice_clk)) {
@@ -3037,6 +3057,9 @@
disable_host_clk:
if (!IS_ERR(msm_host->clk))
clk_disable_unprepare(msm_host->clk);
+disable_bus_aggr_clk:
+ if (!IS_ERR(msm_host->bus_aggr_clk))
+ clk_disable_unprepare(msm_host->bus_aggr_clk);
disable_pclk:
if (!IS_ERR(msm_host->pclk))
clk_disable_unprepare(msm_host->pclk);
@@ -3058,6 +3081,8 @@
clk_disable_unprepare(msm_host->clk);
if (!IS_ERR(msm_host->ice_clk))
clk_disable_unprepare(msm_host->ice_clk);
+ if (!IS_ERR(msm_host->bus_aggr_clk))
+ clk_disable_unprepare(msm_host->bus_aggr_clk);
if (!IS_ERR(msm_host->pclk))
clk_disable_unprepare(msm_host->pclk);
sdhci_msm_bus_voting(host, 0);
@@ -3149,6 +3174,8 @@
clk_disable_unprepare(msm_host->clk);
if (!IS_ERR(msm_host->ice_clk))
clk_disable_unprepare(msm_host->ice_clk);
+ if (!IS_ERR_OR_NULL(msm_host->bus_aggr_clk))
+ clk_disable_unprepare(msm_host->bus_aggr_clk);
if (!IS_ERR_OR_NULL(msm_host->pclk))
clk_disable_unprepare(msm_host->pclk);
atomic_set(&msm_host->controller_clock, 0);
@@ -4468,6 +4495,16 @@
}
atomic_set(&msm_host->controller_clock, 1);
+ /* Setup SDC ufs bus aggr clock */
+ msm_host->bus_aggr_clk = devm_clk_get(&pdev->dev, "bus_aggr_clk");
+ if (!IS_ERR(msm_host->bus_aggr_clk)) {
+ ret = clk_prepare_enable(msm_host->bus_aggr_clk);
+ if (ret) {
+ dev_err(&pdev->dev, "Bus aggregate clk not enabled\n");
+ goto pclk_disable;
+ }
+ }
+
if (msm_host->ice.pdev) {
/* Setup SDC ICE clock */
msm_host->ice_clk = devm_clk_get(&pdev->dev, "ice_core_clk");
@@ -4479,11 +4516,11 @@
dev_err(&pdev->dev, "ICE_CLK rate set failed (%d) for %u\n",
ret,
msm_host->pdata->ice_clk_max);
- goto pclk_disable;
+ goto bus_aggr_clk_disable;
}
ret = clk_prepare_enable(msm_host->ice_clk);
if (ret)
- goto pclk_disable;
+ goto bus_aggr_clk_disable;
msm_host->ice_clk_rate =
msm_host->pdata->ice_clk_max;
@@ -4494,18 +4531,18 @@
msm_host->clk = devm_clk_get(&pdev->dev, "core_clk");
if (IS_ERR(msm_host->clk)) {
ret = PTR_ERR(msm_host->clk);
- goto pclk_disable;
+ goto bus_aggr_clk_disable;
}
/* Set to the minimum supported clock frequency */
ret = clk_set_rate(msm_host->clk, sdhci_msm_get_min_clock(host));
if (ret) {
dev_err(&pdev->dev, "MClk rate set failed (%d)\n", ret);
- goto pclk_disable;
+ goto bus_aggr_clk_disable;
}
ret = clk_prepare_enable(msm_host->clk);
if (ret)
- goto pclk_disable;
+ goto bus_aggr_clk_disable;
msm_host->clk_rate = sdhci_msm_get_min_clock(host);
atomic_set(&msm_host->clks_on, 1);
@@ -4870,6 +4907,9 @@
clk_disable:
if (!IS_ERR(msm_host->clk))
clk_disable_unprepare(msm_host->clk);
+bus_aggr_clk_disable:
+ if (!IS_ERR(msm_host->bus_aggr_clk))
+ clk_disable_unprepare(msm_host->bus_aggr_clk);
pclk_disable:
if (!IS_ERR(msm_host->pclk))
clk_disable_unprepare(msm_host->pclk);
diff --git a/drivers/mmc/host/sdhci-msm.h b/drivers/mmc/host/sdhci-msm.h
index 7c0b873..2c6c0d7 100644
--- a/drivers/mmc/host/sdhci-msm.h
+++ b/drivers/mmc/host/sdhci-msm.h
@@ -153,6 +153,8 @@
bool sdr104_wa;
u32 ice_clk_max;
u32 ice_clk_min;
+ u32 ddr_config;
+ bool rclk_wa;
};
struct sdhci_msm_bus_vote {
@@ -203,6 +205,7 @@
int pwr_irq; /* power irq */
struct clk *clk; /* main SD/MMC bus clock */
struct clk *pclk; /* SDHC peripheral bus clock */
+ struct clk *bus_aggr_clk; /* Axi clock shared with UFS */
struct clk *bus_clk; /* SDHC bus voter clock */
struct clk *ff_clk; /* CDC calibration fixed feedback clock */
struct clk *sleep_clk; /* CDC calibration sleep clock */
diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c
index 4476e51..566be69 100644
--- a/drivers/mmc/host/sdhci.c
+++ b/drivers/mmc/host/sdhci.c
@@ -2491,6 +2491,8 @@
*/
mmc_retune_disable(mmc);
err = host->ops->platform_execute_tuning(host, opcode);
+ if (err)
+ host->mmc->err_stats[MMC_ERR_TUNING]++;
mmc_retune_enable(mmc);
return err;
}
@@ -2701,8 +2703,8 @@
data->host_cookie = COOKIE_UNMAPPED;
- if (host->ops->pre_req)
- host->ops->pre_req(host, mrq);
+ if (host->ops->post_req)
+ host->ops->post_req(host, mrq);
}
static void sdhci_pre_req(struct mmc_host *mmc, struct mmc_request *mrq,
@@ -2714,6 +2716,9 @@
if (host->flags & SDHCI_REQ_USE_DMA)
sdhci_pre_dma_transfer(host, mrq->data, COOKIE_PRE_MAPPED);
+
+ if (host->ops->pre_req)
+ host->ops->pre_req(host, mrq);
}
static inline bool sdhci_has_requests(struct sdhci_host *host)
@@ -2916,8 +2921,10 @@
spin_lock_irqsave(&host->lock, flags);
if (host->cmd && !sdhci_data_line_cmd(host->cmd)) {
+ host->mmc->err_stats[MMC_ERR_REQ_TIMEOUT]++;
pr_err("%s: Timeout waiting for hardware cmd interrupt.\n",
mmc_hostname(host->mmc));
+ MMC_TRACE(host->mmc, "Timeout waiting for h/w interrupt\n");
sdhci_dumpregs(host);
host->cmd->error = -ETIMEDOUT;
@@ -2939,6 +2946,7 @@
if (host->data || host->data_cmd ||
(host->cmd && sdhci_data_line_cmd(host->cmd))) {
+ host->mmc->err_stats[MMC_ERR_REQ_TIMEOUT]++;
pr_err("%s: Timeout waiting for hardware interrupt.\n",
mmc_hostname(host->mmc));
MMC_TRACE(host->mmc, "Timeout waiting for h/w interrupt\n");
@@ -2997,13 +3005,17 @@
if (intmask & (SDHCI_INT_TIMEOUT | SDHCI_INT_CRC |
SDHCI_INT_END_BIT | SDHCI_INT_INDEX |
SDHCI_INT_AUTO_CMD_ERR)) {
- if (intmask & SDHCI_INT_TIMEOUT)
+ if (intmask & SDHCI_INT_TIMEOUT) {
host->cmd->error = -ETIMEDOUT;
- else
+ host->mmc->err_stats[MMC_ERR_CMD_TIMEOUT]++;
+ } else {
host->cmd->error = -EILSEQ;
+ host->mmc->err_stats[MMC_ERR_CMD_CRC]++;
+ }
if (intmask & SDHCI_INT_AUTO_CMD_ERR) {
auto_cmd_status = host->auto_cmd_err_sts;
+ host->mmc->err_stats[MMC_ERR_AUTO_CMD]++;
pr_err_ratelimited("%s: %s: AUTO CMD err sts 0x%08x\n",
mmc_hostname(host->mmc), __func__, auto_cmd_status);
if (auto_cmd_status & (SDHCI_AUTO_CMD12_NOT_EXEC |
@@ -3131,6 +3143,7 @@
if (intmask & SDHCI_INT_DATA_TIMEOUT) {
host->data_cmd = NULL;
data_cmd->error = -ETIMEDOUT;
+ host->mmc->err_stats[MMC_ERR_CMD_TIMEOUT]++;
sdhci_finish_mrq(host, data_cmd->mrq);
return;
}
@@ -3154,16 +3167,21 @@
return;
}
- if (intmask & SDHCI_INT_DATA_TIMEOUT)
+ if (intmask & SDHCI_INT_DATA_TIMEOUT) {
host->data->error = -ETIMEDOUT;
+ host->mmc->err_stats[MMC_ERR_DAT_TIMEOUT]++;
+ }
else if (intmask & SDHCI_INT_DATA_END_BIT)
host->data->error = -EILSEQ;
else if ((intmask & SDHCI_INT_DATA_CRC) &&
- (command != MMC_BUS_TEST_R))
+ (command != MMC_BUS_TEST_R)) {
host->data->error = -EILSEQ;
+ host->mmc->err_stats[MMC_ERR_DAT_CRC]++;
+ }
else if (intmask & SDHCI_INT_ADMA_ERROR) {
pr_err("%s: ADMA error\n", mmc_hostname(host->mmc));
sdhci_adma_show_error(host);
+ host->mmc->err_stats[MMC_ERR_ADMA]++;
host->data->error = -EIO;
if (host->ops->adma_workaround)
host->ops->adma_workaround(host, intmask);
@@ -3242,24 +3260,31 @@
}
#ifdef CONFIG_MMC_CQ_HCI
-static int sdhci_get_cmd_err(u32 intmask)
+static int sdhci_get_cmd_err(struct sdhci_host *host, u32 intmask)
{
- if (intmask & SDHCI_INT_TIMEOUT)
+ if (intmask & SDHCI_INT_TIMEOUT) {
+ host->mmc->err_stats[MMC_ERR_CMD_TIMEOUT]++;
return -ETIMEDOUT;
- else if (intmask & (SDHCI_INT_CRC | SDHCI_INT_END_BIT |
- SDHCI_INT_INDEX))
+ } else if (intmask & (SDHCI_INT_CRC | SDHCI_INT_END_BIT |
+ SDHCI_INT_INDEX)) {
+ host->mmc->err_stats[MMC_ERR_CMD_CRC]++;
return -EILSEQ;
+ }
return 0;
}
-static int sdhci_get_data_err(u32 intmask)
+static int sdhci_get_data_err(struct sdhci_host *host, u32 intmask)
{
- if (intmask & SDHCI_INT_DATA_TIMEOUT)
+ if (intmask & SDHCI_INT_DATA_TIMEOUT) {
+ host->mmc->err_stats[MMC_ERR_DAT_TIMEOUT]++;
return -ETIMEDOUT;
- else if (intmask & (SDHCI_INT_DATA_END_BIT | SDHCI_INT_DATA_CRC))
+ } else if (intmask & (SDHCI_INT_DATA_END_BIT | SDHCI_INT_DATA_CRC)) {
+ host->mmc->err_stats[MMC_ERR_DAT_CRC]++;
return -EILSEQ;
- else if (intmask & SDHCI_INT_ADMA_ERROR)
+ } else if (intmask & MMC_ERR_ADMA) {
+ host->mmc->err_stats[MMC_ERR_ADMA]++;
return -EIO;
+ }
return 0;
}
@@ -3270,9 +3295,9 @@
irqreturn_t ret;
if (intmask & SDHCI_INT_CMD_MASK)
- err = sdhci_get_cmd_err(intmask);
+ err = sdhci_get_cmd_err(host, intmask);
else if (intmask & SDHCI_INT_DATA_MASK)
- err = sdhci_get_data_err(intmask);
+ err = sdhci_get_data_err(host, intmask);
ret = cmdq_irq(host->mmc, err);
if (err) {
diff --git a/drivers/net/wireless/ath/wil6210/ftm.c b/drivers/net/wireless/ath/wil6210/ftm.c
index f94c894..17939ce 100644
--- a/drivers/net/wireless/ath/wil6210/ftm.c
+++ b/drivers/net/wireless/ath/wil6210/ftm.c
@@ -38,6 +38,9 @@
/* initial token to use on non-secure FTM measurement */
#define WIL_TOF_FTM_DEFAULT_INITIAL_TOKEN 2
+/* maximum AOA burst period, limited by FW */
+#define WIL_AOA_MAX_BURST_PERIOD 255
+
#define WIL_TOF_FTM_MAX_LCI_LENGTH (240)
#define WIL_TOF_FTM_MAX_LCR_LENGTH (240)
@@ -62,6 +65,7 @@
[QCA_WLAN_VENDOR_ATTR_FTM_PEER_MEAS_FLAGS] = { .type = NLA_U32 },
[QCA_WLAN_VENDOR_ATTR_FTM_PEER_MEAS_PARAMS] = { .type = NLA_NESTED },
[QCA_WLAN_VENDOR_ATTR_FTM_PEER_SECURE_TOKEN_ID] = { .type = NLA_U8 },
+ [QCA_WLAN_VENDOR_ATTR_FTM_PEER_AOA_BURST_PERIOD] = { .type = NLA_U16 },
[QCA_WLAN_VENDOR_ATTR_FTM_PEER_FREQ] = { .type = NLA_U32 },
};
@@ -315,8 +319,8 @@
struct wmi_tof_session_start_cmd *cmd;
mutex_lock(&wil->ftm.lock);
- if (wil->ftm.session_started) {
- wil_err(wil, "FTM session already running\n");
+ if (wil->ftm.session_started || wil->ftm.aoa_started) {
+ wil_err(wil, "FTM or AOA session already running\n");
rc = -EAGAIN;
goto out;
}
@@ -360,6 +364,7 @@
}
cmd->session_id = cpu_to_le32(WIL_FTM_FW_SESSION_ID);
+ cmd->aoa_type = request->aoa_type;
cmd->num_of_dest = cpu_to_le16(request->n_peers);
for (i = 0; i < request->n_peers; i++) {
ether_addr_copy(cmd->ftm_dest_info[i].dst_mac,
@@ -402,6 +407,8 @@
request->peers[i].params.burst_duration;
cmd->ftm_dest_info[i].burst_period =
cpu_to_le16(request->peers[i].params.burst_period);
+ cmd->ftm_dest_info[i].num_burst_per_aoa_meas =
+ request->peers[i].aoa_burst_period;
}
rc = wmi_send(wil, WMI_TOF_SESSION_START_CMDID, cmd, cmd_len);
@@ -487,8 +494,8 @@
mutex_lock(&wil->ftm.lock);
- if (wil->ftm.aoa_started) {
- wil_err(wil, "AOA measurement already running\n");
+ if (wil->ftm.aoa_started || wil->ftm.session_started) {
+ wil_err(wil, "AOA or FTM measurement already running\n");
rc = -EAGAIN;
goto out;
}
@@ -529,8 +536,8 @@
mutex_lock(&wil->ftm.lock);
- if (!wil->ftm.aoa_started) {
- wil_info(wil, "AOA not started, not sending result\n");
+ if (!wil->ftm.aoa_started && !wil->ftm.session_started) {
+ wil_info(wil, "AOA/FTM not started, not sending result\n");
goto out;
}
@@ -683,6 +690,10 @@
int data_len = len - offsetof(struct wmi_aoa_meas_event, meas_data);
struct wil_aoa_meas_result *res;
+ if (data_len < 0) {
+ wil_err(wil, "AOA event too short (%d)\n", len);
+ return;
+ }
data_len = min_t(int, le16_to_cpu(evt->length), data_len);
res = kmalloc(sizeof(*res) + data_len, GFP_KERNEL);
@@ -754,6 +765,7 @@
struct nlattr *tb2[QCA_WLAN_VENDOR_ATTR_FTM_PEER_MAX + 1];
struct nlattr *peer;
int rc, n_peers = 0, index = 0, tmp;
+ u32 aoa_type = 0;
if (!test_bit(WMI_FW_CAPABILITY_FTM, wil->fw_capabilities))
return -ENOTSUPP;
@@ -775,6 +787,14 @@
return -EINVAL;
}
+ if (tb[QCA_WLAN_VENDOR_ATTR_AOA_TYPE]) {
+ aoa_type = nla_get_u32(tb[QCA_WLAN_VENDOR_ATTR_AOA_TYPE]);
+ if (aoa_type >= QCA_WLAN_VENDOR_ATTR_AOA_TYPE_MAX) {
+ wil_err(wil, "invalid AOA type: %d\n", aoa_type);
+ return -EINVAL;
+ }
+ }
+
nla_for_each_nested(peer, tb[QCA_WLAN_VENDOR_ATTR_FTM_MEAS_PEERS],
tmp)
n_peers++;
@@ -798,6 +818,7 @@
request->session_cookie =
nla_get_u64(tb[QCA_WLAN_VENDOR_ATTR_FTM_SESSION_COOKIE]);
+ request->aoa_type = aoa_type;
request->n_peers = n_peers;
nla_for_each_nested(peer, tb[QCA_WLAN_VENDOR_ATTR_FTM_MEAS_PEERS],
tmp) {
@@ -826,6 +847,18 @@
if (tb2[QCA_WLAN_VENDOR_ATTR_FTM_PEER_SECURE_TOKEN_ID])
request->peers[index].secure_token_id = nla_get_u8(
tb2[QCA_WLAN_VENDOR_ATTR_FTM_PEER_SECURE_TOKEN_ID]);
+ if (tb2[QCA_WLAN_VENDOR_ATTR_FTM_PEER_AOA_BURST_PERIOD]) {
+ request->peers[index].aoa_burst_period = nla_get_u16(
+ tb2[QCA_WLAN_VENDOR_ATTR_FTM_PEER_AOA_BURST_PERIOD]);
+ if (request->peers[index].aoa_burst_period >
+ WIL_AOA_MAX_BURST_PERIOD) {
+ wil_err(wil, "Invalid AOA burst period at index: %d\n",
+ index);
+ rc = -EINVAL;
+ goto out;
+ }
+ }
+
rc = wil_ftm_parse_meas_params(
wil,
tb2[QCA_WLAN_VENDOR_ATTR_FTM_PEER_MEAS_PARAMS],
diff --git a/drivers/net/wireless/ath/wil6210/ftm.h b/drivers/net/wireless/ath/wil6210/ftm.h
index 8efa292..21923c2 100644
--- a/drivers/net/wireless/ath/wil6210/ftm.h
+++ b/drivers/net/wireless/ath/wil6210/ftm.h
@@ -437,12 +437,14 @@
u32 flags; /* enum qca_wlan_vendor_attr_ftm_peer_meas_flags */
struct wil_ftm_meas_params params;
u8 secure_token_id;
+ u16 aoa_burst_period; /* 0 if no AOA, >0 every <value> bursts */
};
/* session request, passed to wil_ftm_cfg80211_start_session */
struct wil_ftm_session_request {
u64 session_cookie;
u32 n_peers;
+ u32 aoa_type; /* enum qca_wlan_vendor_attr_aoa_type */
/* keep last, variable size according to n_peers */
struct wil_ftm_meas_peer_info peers[0];
};
diff --git a/drivers/net/wireless/ath/wil6210/pcie_bus.c b/drivers/net/wireless/ath/wil6210/pcie_bus.c
index 663e163..89e3fbf 100644
--- a/drivers/net/wireless/ath/wil6210/pcie_bus.c
+++ b/drivers/net/wireless/ath/wil6210/pcie_bus.c
@@ -23,9 +23,9 @@
#include <linux/rtnetlink.h>
#include <linux/pm_runtime.h>
-static bool use_msi = true;
+static bool use_msi;
module_param(use_msi, bool, 0444);
-MODULE_PARM_DESC(use_msi, " Use MSI interrupt, default - true");
+MODULE_PARM_DESC(use_msi, " Use MSI interrupt, default - false");
static bool ftm_mode;
module_param(ftm_mode, bool, 0444);
diff --git a/drivers/of/of_reserved_mem.c b/drivers/of/of_reserved_mem.c
index c807c28..4a1cab5 100644
--- a/drivers/of/of_reserved_mem.c
+++ b/drivers/of/of_reserved_mem.c
@@ -24,6 +24,7 @@
#include <linux/of_reserved_mem.h>
#include <linux/sort.h>
#include <linux/slab.h>
+#include <linux/kmemleak.h>
#define MAX_RESERVED_REGIONS 32
static struct reserved_mem reserved_mem[MAX_RESERVED_REGIONS];
@@ -54,8 +55,10 @@
}
*res_base = base;
- if (nomap)
+ if (nomap) {
+ kmemleak_ignore_phys(base);
return memblock_remove(base, size);
+ }
return 0;
}
#else
diff --git a/drivers/pci/host/pci-msm.c b/drivers/pci/host/pci-msm.c
index 57dc4a0..9e0989c 100644
--- a/drivers/pci/host/pci-msm.c
+++ b/drivers/pci/host/pci-msm.c
@@ -19,6 +19,7 @@
#include <linux/clk.h>
#include <linux/debugfs.h>
#include <linux/delay.h>
+#include <linux/jiffies.h>
#include <linux/gpio.h>
#include <linux/iopoll.h>
#include <linux/kernel.h>
@@ -120,6 +121,12 @@
#define PCIE20_PLR_IATU_LTAR 0x918
#define PCIE20_PLR_IATU_UTAR 0x91c
+
+#define PCIE20_PORT_LINK_CTRL_REG 0x710
+#define PCIE20_GEN3_RELATED_REG 0x890
+#define PCIE20_PIPE_LOOPBACK_CONTROL 0x8b8
+#define LOOPBACK_BASE_ADDR_OFFSET 0x8000
+
#define PCIE20_CTRL1_TYPE_CFG0 0x04
#define PCIE20_CTRL1_TYPE_CFG1 0x05
@@ -140,11 +147,15 @@
#define PERST_PROPAGATION_DELAY_US_MIN 1000
#define PERST_PROPAGATION_DELAY_US_MAX 1005
+#define SWITCH_DELAY_MAX 20
#define REFCLK_STABILIZATION_DELAY_US_MIN 1000
#define REFCLK_STABILIZATION_DELAY_US_MAX 1005
#define LINK_UP_TIMEOUT_US_MIN 5000
#define LINK_UP_TIMEOUT_US_MAX 5100
#define LINK_UP_CHECK_MAX_COUNT 20
+#define EP_UP_TIMEOUT_US_MIN 1000
+#define EP_UP_TIMEOUT_US_MAX 1005
+#define EP_UP_TIMEOUT_US 1000000
#define PHY_STABILIZATION_DELAY_US_MIN 995
#define PHY_STABILIZATION_DELAY_US_MAX 1005
#define POWER_DOWN_DELAY_US_MIN 10
@@ -492,6 +503,7 @@
uint32_t max_link_speed;
bool ext_ref_clk;
uint32_t ep_latency;
+ uint32_t switch_latency;
uint32_t wr_halt_size;
uint32_t slv_addr_space_size;
uint32_t cpl_timeout;
@@ -546,12 +558,37 @@
struct msm_pcie_device_info pcidev_table[MAX_DEVICE_NUM];
};
-
/* debug mask sys interface */
static int msm_pcie_debug_mask;
module_param_named(debug_mask, msm_pcie_debug_mask,
int, 0644);
+/*
+ * For each bit set, invert the default capability
+ * option for the corresponding root complex
+ * and its devices.
+ */
+static int msm_pcie_invert_l0s_support;
+module_param_named(invert_l0s_support, msm_pcie_invert_l0s_support,
+ int, 0644);
+static int msm_pcie_invert_l1_support;
+module_param_named(invert_l1_support, msm_pcie_invert_l1_support,
+ int, 0644);
+static int msm_pcie_invert_l1ss_support;
+module_param_named(invert_l1ss_support, msm_pcie_invert_l1ss_support,
+ int, 0644);
+static int msm_pcie_invert_aer_support;
+module_param_named(invert_aer_support, msm_pcie_invert_aer_support,
+ int, 0644);
+
+/*
+ * For each bit set, keep the resources on when link training fails
+ * or linkdown occurs for the corresponding root complex
+ */
+static int msm_pcie_keep_resources_on;
+module_param_named(keep_resources_on, msm_pcie_keep_resources_on,
+ int, 0644);
+
/* debugfs values */
static u32 rc_sel;
static u32 base_sel;
@@ -731,6 +768,8 @@
};
static int msm_pcie_config_device(struct pci_dev *dev, void *pdev);
+static void msm_pcie_config_link_pm_rc(struct msm_pcie_dev_t *dev,
+ struct pci_dev *pdev, bool enable);
#ifdef CONFIG_ARM
#define PCIE_BUS_PRIV_DATA(bus) \
@@ -784,6 +823,17 @@
wmb();
}
+static inline void msm_pcie_config_clear_set_dword(struct pci_dev *pdev,
+ int pos, u32 clear, u32 set)
+{
+ u32 val;
+
+ pci_read_config_dword(pdev, pos, &val);
+ val &= ~clear;
+ val |= set;
+ pci_write_config_dword(pdev, pos, val);
+}
+
static inline void msm_pcie_config_clock_mem(struct msm_pcie_dev_t *dev,
struct msm_pcie_clk_info_t *info)
{
@@ -1112,6 +1162,8 @@
dev->n_fts);
PCIE_DBG_FS(dev, "ep_latency: %dms\n",
dev->ep_latency);
+ PCIE_DBG_FS(dev, "switch_latency: %dms\n",
+ dev->switch_latency);
PCIE_DBG_FS(dev, "wr_halt_size: 0x%x\n",
dev->wr_halt_size);
PCIE_DBG_FS(dev, "slv_addr_space_size: 0x%x\n",
@@ -1184,6 +1236,14 @@
static void msm_pcie_sel_debug_testcase(struct msm_pcie_dev_t *dev,
u32 testcase)
{
+ phys_addr_t dbi_base_addr =
+ dev->res[MSM_PCIE_RES_DM_CORE].resource->start;
+ phys_addr_t loopback_lbar_phy =
+ dbi_base_addr + LOOPBACK_BASE_ADDR_OFFSET;
+ static uint32_t loopback_val = 0x1;
+ static u64 loopback_ddr_phy;
+ static uint32_t *loopback_ddr_vir;
+ static void __iomem *loopback_lbar_vir;
int ret, i;
u32 base_sel_size = 0;
u32 val = 0;
@@ -1597,6 +1657,282 @@
readl_relaxed(dev->res[base_sel - 1].base + (i + 28)));
}
break;
+ case 14:
+ PCIE_DBG_FS(dev,
+ "PCIe: RC%d: Allocate 4K DDR memory and map LBAR.\n",
+ dev->rc_idx);
+ loopback_ddr_vir = dma_alloc_coherent(&dev->pdev->dev,
+ (SZ_1K * sizeof(*loopback_ddr_vir)),
+ &loopback_ddr_phy, GFP_KERNEL);
+ if (!loopback_ddr_vir) {
+ PCIE_DBG_FS(dev,
+ "PCIe: RC%d: failed to dma_alloc_coherent.\n",
+ dev->rc_idx);
+ } else {
+ PCIE_DBG_FS(dev,
+ "PCIe: RC%d: VIR DDR memory address: 0x%pK\n",
+ dev->rc_idx, loopback_ddr_vir);
+ PCIE_DBG_FS(dev,
+ "PCIe: RC%d: PHY DDR memory address: 0x%llx\n",
+ dev->rc_idx, loopback_ddr_phy);
+ }
+
+ PCIE_DBG_FS(dev, "PCIe: RC%d: map LBAR: 0x%llx\n",
+ dev->rc_idx, loopback_lbar_phy);
+ loopback_lbar_vir = devm_ioremap(&dev->pdev->dev,
+ loopback_lbar_phy, SZ_4K);
+ if (!loopback_lbar_vir) {
+ PCIE_DBG_FS(dev, "PCIe: RC%d: failed to map 0x%llx\n",
+ dev->rc_idx, loopback_lbar_phy);
+ } else {
+ PCIE_DBG_FS(dev,
+ "PCIe: RC%d: successfully mapped 0x%llx to 0x%pK\n",
+ dev->rc_idx, loopback_lbar_phy,
+ loopback_lbar_vir);
+ }
+ break;
+ case 15:
+ PCIE_DBG_FS(dev,
+ "PCIe: RC%d: Release 4K DDR memory and unmap LBAR.\n",
+ dev->rc_idx);
+
+ if (loopback_ddr_vir) {
+ dma_free_coherent(&dev->pdev->dev, SZ_4K,
+ loopback_ddr_vir, loopback_ddr_phy);
+ loopback_ddr_vir = NULL;
+ }
+
+ if (loopback_lbar_vir) {
+ devm_iounmap(&dev->pdev->dev,
+ loopback_lbar_vir);
+ loopback_lbar_vir = NULL;
+ }
+ break;
+ case 16:
+ PCIE_DBG_FS(dev,
+ "PCIe: RC%d: Print DDR and LBAR addresses.\n",
+ dev->rc_idx);
+
+ if (!loopback_ddr_vir || !loopback_lbar_vir) {
+ PCIE_DBG_FS(dev,
+ "PCIe: RC%d: DDR or LBAR address is not mapped\n",
+ dev->rc_idx);
+ break;
+ }
+
+ PCIE_DBG_FS(dev,
+ "PCIe: RC%d: PHY DDR address: 0x%llx\n",
+ dev->rc_idx, loopback_ddr_phy);
+ PCIE_DBG_FS(dev,
+ "PCIe: RC%d: VIR DDR address: 0x%pK\n",
+ dev->rc_idx, loopback_ddr_vir);
+ PCIE_DBG_FS(dev,
+ "PCIe: RC%d: PHY LBAR address: 0x%llx\n",
+ dev->rc_idx, loopback_lbar_phy);
+ PCIE_DBG_FS(dev,
+ "PCIe: RC%d: VIR LBAR address: 0x%pK\n",
+ dev->rc_idx, loopback_lbar_vir);
+ break;
+ case 17:
+ PCIE_DBG_FS(dev,
+ "PCIe: RC%d: Configure Loopback.\n",
+ dev->rc_idx);
+
+ writel_relaxed(0x10000,
+ dev->dm_core + PCIE20_GEN3_RELATED_REG);
+ PCIE_DBG_FS(dev,
+ "PCIe: RC%d: 0x%llx: 0x%x\n",
+ dev->rc_idx,
+ dbi_base_addr + PCIE20_GEN3_RELATED_REG,
+ readl_relaxed(dev->dm_core +
+ PCIE20_GEN3_RELATED_REG));
+
+ writel_relaxed(0x80000001,
+ dev->dm_core + PCIE20_PIPE_LOOPBACK_CONTROL);
+ PCIE_DBG_FS(dev,
+ "PCIe: RC%d: 0x%llx: 0x%x\n",
+ dev->rc_idx,
+ dbi_base_addr + PCIE20_PIPE_LOOPBACK_CONTROL,
+ readl_relaxed(dev->dm_core +
+ PCIE20_PIPE_LOOPBACK_CONTROL));
+
+ writel_relaxed(0x00010124,
+ dev->dm_core + PCIE20_PORT_LINK_CTRL_REG);
+ PCIE_DBG_FS(dev,
+ "PCIe: RC%d: 0x%llx: 0x%x\n",
+ dev->rc_idx,
+ dbi_base_addr + PCIE20_PORT_LINK_CTRL_REG,
+ readl_relaxed(dev->dm_core +
+ PCIE20_PORT_LINK_CTRL_REG));
+ break;
+ case 18:
+ PCIE_DBG_FS(dev, "PCIe: RC%d: Setup iATU.\n", dev->rc_idx);
+
+ if (!loopback_ddr_vir) {
+ PCIE_DBG_FS(dev,
+ "PCIe: RC%d: DDR address is not mapped.\n",
+ dev->rc_idx);
+ break;
+ }
+
+ writel_relaxed(0x0, dev->dm_core + PCIE20_PLR_IATU_VIEWPORT);
+ PCIE_DBG_FS(dev,
+ "PCIe: RC%d: PCIE20_PLR_IATU_VIEWPORT:\t0x%llx: 0x%x\n",
+ dev->rc_idx, dbi_base_addr + PCIE20_PLR_IATU_VIEWPORT,
+ readl_relaxed(dev->dm_core + PCIE20_PLR_IATU_VIEWPORT));
+
+ writel_relaxed(0x0, dev->dm_core + PCIE20_PLR_IATU_CTRL1);
+ PCIE_DBG_FS(dev,
+ "PCIe: RC%d: PCIE20_PLR_IATU_CTRL1:\t0x%llx: 0x%x\n",
+ dev->rc_idx, dbi_base_addr + PCIE20_PLR_IATU_CTRL1,
+ readl_relaxed(dev->dm_core + PCIE20_PLR_IATU_CTRL1));
+
+ writel_relaxed(loopback_lbar_phy,
+ dev->dm_core + PCIE20_PLR_IATU_LBAR);
+ PCIE_DBG_FS(dev,
+ "PCIe: RC%d: PCIE20_PLR_IATU_LBAR:\t0x%llx: 0x%x\n",
+ dev->rc_idx, dbi_base_addr + PCIE20_PLR_IATU_LBAR,
+ readl_relaxed(dev->dm_core + PCIE20_PLR_IATU_LBAR));
+
+ writel_relaxed(0x0, dev->dm_core + PCIE20_PLR_IATU_UBAR);
+ PCIE_DBG_FS(dev,
+ "PCIe: RC%d: PCIE20_PLR_IATU_UBAR:\t0x%llx: 0x%x\n",
+ dev->rc_idx, dbi_base_addr + PCIE20_PLR_IATU_UBAR,
+ readl_relaxed(dev->dm_core + PCIE20_PLR_IATU_UBAR));
+
+ writel_relaxed(loopback_lbar_phy + 0xfff,
+ dev->dm_core + PCIE20_PLR_IATU_LAR);
+ PCIE_DBG_FS(dev,
+ "PCIe: RC%d: PCIE20_PLR_IATU_LAR:\t0x%llx: 0x%x\n",
+ dev->rc_idx, dbi_base_addr + PCIE20_PLR_IATU_LAR,
+ readl_relaxed(dev->dm_core + PCIE20_PLR_IATU_LAR));
+
+ writel_relaxed(loopback_ddr_phy,
+ dev->dm_core + PCIE20_PLR_IATU_LTAR);
+ PCIE_DBG_FS(dev,
+ "PCIe: RC%d: PCIE20_PLR_IATU_LTAR:\t0x%llx: 0x%x\n",
+ dev->rc_idx, dbi_base_addr + PCIE20_PLR_IATU_LTAR,
+ readl_relaxed(dev->dm_core + PCIE20_PLR_IATU_LTAR));
+
+ writel_relaxed(0, dev->dm_core + PCIE20_PLR_IATU_UTAR);
+ PCIE_DBG_FS(dev,
+ "PCIe: RC%d: PCIE20_PLR_IATU_UTAR:\t0x%llx: 0x%x\n",
+ dev->rc_idx, dbi_base_addr + PCIE20_PLR_IATU_UTAR,
+ readl_relaxed(dev->dm_core + PCIE20_PLR_IATU_UTAR));
+
+ writel_relaxed(0x80000000,
+ dev->dm_core + PCIE20_PLR_IATU_CTRL2);
+ PCIE_DBG_FS(dev,
+ "PCIe: RC%d: PCIE20_PLR_IATU_CTRL2:\t0x%llx: 0x%x\n",
+ dev->rc_idx, dbi_base_addr + PCIE20_PLR_IATU_CTRL2,
+ readl_relaxed(dev->dm_core + PCIE20_PLR_IATU_CTRL2));
+ break;
+ case 19:
+ PCIE_DBG_FS(dev,
+ "PCIe: RC%d: Read DDR values.\n",
+ dev->rc_idx);
+
+ if (!loopback_ddr_vir) {
+ PCIE_DBG_FS(dev,
+ "PCIe: RC%d: DDR is not mapped\n",
+ dev->rc_idx);
+ break;
+ }
+
+ for (i = 0; i < SZ_1K; i += 8) {
+ PCIE_DBG_FS(dev,
+ "0x%04x %08x %08x %08x %08x %08x %08x %08x %08x\n",
+ i,
+ loopback_ddr_vir[i],
+ loopback_ddr_vir[i + 1],
+ loopback_ddr_vir[i + 2],
+ loopback_ddr_vir[i + 3],
+ loopback_ddr_vir[i + 4],
+ loopback_ddr_vir[i + 5],
+ loopback_ddr_vir[i + 6],
+ loopback_ddr_vir[i + 7]);
+ }
+ break;
+ case 20:
+ PCIE_DBG_FS(dev,
+ "PCIe: RC%d: Read LBAR values.\n",
+ dev->rc_idx);
+
+ if (!loopback_lbar_vir) {
+ PCIE_DBG_FS(dev,
+ "PCIe: RC%d: LBAR address is not mapped\n",
+ dev->rc_idx);
+ break;
+ }
+
+ for (i = 0; i < SZ_4K; i += 32) {
+ PCIE_DBG_FS(dev,
+ "0x%04x %08x %08x %08x %08x %08x %08x %08x %08x\n",
+ i,
+ readl_relaxed(loopback_lbar_vir + i),
+ readl_relaxed(loopback_lbar_vir + (i + 4)),
+ readl_relaxed(loopback_lbar_vir + (i + 8)),
+ readl_relaxed(loopback_lbar_vir + (i + 12)),
+ readl_relaxed(loopback_lbar_vir + (i + 16)),
+ readl_relaxed(loopback_lbar_vir + (i + 20)),
+ readl_relaxed(loopback_lbar_vir + (i + 24)),
+ readl_relaxed(loopback_lbar_vir + (i + 28)));
+ }
+ break;
+ case 21:
+ PCIE_DBG_FS(dev, "PCIe: RC%d: Write 0x%x to DDR.\n",
+ dev->rc_idx, loopback_val);
+
+ if (!loopback_ddr_vir) {
+ PCIE_DBG_FS(dev,
+ "PCIe: RC%d: DDR address is not mapped\n",
+ dev->rc_idx);
+ break;
+ }
+
+ memset(loopback_ddr_vir, loopback_val,
+ (SZ_1K * sizeof(*loopback_ddr_vir)));
+
+ if (unlikely(loopback_val == UINT_MAX))
+ loopback_val = 1;
+ else
+ loopback_val++;
+ break;
+ case 22:
+ PCIE_DBG_FS(dev, "PCIe: RC%d: Write 0x%x to LBAR.\n",
+ dev->rc_idx, loopback_val);
+
+ if (!loopback_lbar_vir) {
+ PCIE_DBG_FS(dev,
+ "PCIe: RC%d: LBAR address is not mapped\n",
+ dev->rc_idx);
+ break;
+ }
+
+ for (i = 0; i < SZ_4K; i += 32) {
+ writel_relaxed(loopback_val,
+ loopback_lbar_vir + i),
+ writel_relaxed(loopback_val,
+ loopback_lbar_vir + (i + 4)),
+ writel_relaxed(loopback_val,
+ loopback_lbar_vir + (i + 8)),
+ writel_relaxed(loopback_val,
+ loopback_lbar_vir + (i + 12)),
+ writel_relaxed(loopback_val,
+ loopback_lbar_vir + (i + 16)),
+ writel_relaxed(loopback_val,
+ loopback_lbar_vir + (i + 20)),
+ writel_relaxed(loopback_val,
+ loopback_lbar_vir + (i + 24)),
+ writel_relaxed(loopback_val,
+ loopback_lbar_vir + (i + 28));
+ }
+
+ if (unlikely(loopback_val == UINT_MAX))
+ loopback_val = 1;
+ else
+ loopback_val++;
+ break;
default:
PCIE_DBG_FS(dev, "Invalid testcase: %d.\n", testcase);
break;
@@ -3014,194 +3350,6 @@
}
}
-static void msm_pcie_config_link_state(struct msm_pcie_dev_t *dev)
-{
- u32 val;
- u32 current_offset;
- u32 ep_l1sub_ctrl1_offset = 0;
- u32 ep_l1sub_cap_reg1_offset = 0;
- u32 ep_link_cap_offset = 0;
- u32 ep_link_ctrlstts_offset = 0;
- u32 ep_dev_ctrl2stts2_offset = 0;
-
- /* Enable the AUX Clock and the Core Clk to be synchronous for L1SS*/
- if (!dev->aux_clk_sync && dev->l1ss_supported)
- msm_pcie_write_mask(dev->parf +
- PCIE20_PARF_SYS_CTRL, BIT(3), 0);
-
- current_offset = readl_relaxed(dev->conf + PCIE_CAP_PTR_OFFSET) & 0xff;
-
- while (current_offset) {
- if (msm_pcie_check_align(dev, current_offset))
- return;
-
- val = readl_relaxed(dev->conf + current_offset);
- if ((val & 0xff) == PCIE20_CAP_ID) {
- ep_link_cap_offset = current_offset + 0x0c;
- ep_link_ctrlstts_offset = current_offset + 0x10;
- ep_dev_ctrl2stts2_offset = current_offset + 0x28;
- break;
- }
- current_offset = (val >> 8) & 0xff;
- }
-
- if (!ep_link_cap_offset) {
- PCIE_DBG(dev,
- "RC%d endpoint does not support PCIe capability registers\n",
- dev->rc_idx);
- return;
- }
-
- PCIE_DBG(dev,
- "RC%d: ep_link_cap_offset: 0x%x\n",
- dev->rc_idx, ep_link_cap_offset);
-
- if (dev->common_clk_en) {
- msm_pcie_write_mask(dev->dm_core + PCIE20_CAP_LINKCTRLSTATUS,
- 0, BIT(6));
-
- msm_pcie_write_mask(dev->conf + ep_link_ctrlstts_offset,
- 0, BIT(6));
-
- if (dev->shadow_en) {
- dev->rc_shadow[PCIE20_CAP_LINKCTRLSTATUS / 4] =
- readl_relaxed(dev->dm_core +
- PCIE20_CAP_LINKCTRLSTATUS);
-
- dev->ep_shadow[0][ep_link_ctrlstts_offset / 4] =
- readl_relaxed(dev->conf +
- ep_link_ctrlstts_offset);
- }
-
- PCIE_DBG2(dev, "RC's CAP_LINKCTRLSTATUS:0x%x\n",
- readl_relaxed(dev->dm_core +
- PCIE20_CAP_LINKCTRLSTATUS));
- PCIE_DBG2(dev, "EP's CAP_LINKCTRLSTATUS:0x%x\n",
- readl_relaxed(dev->conf + ep_link_ctrlstts_offset));
- }
-
- if (dev->clk_power_manage_en) {
- val = readl_relaxed(dev->conf + ep_link_cap_offset);
- if (val & BIT(18)) {
- msm_pcie_write_mask(dev->conf + ep_link_ctrlstts_offset,
- 0, BIT(8));
-
- if (dev->shadow_en)
- dev->ep_shadow[0][ep_link_ctrlstts_offset / 4] =
- readl_relaxed(dev->conf +
- ep_link_ctrlstts_offset);
-
- PCIE_DBG2(dev, "EP's CAP_LINKCTRLSTATUS:0x%x\n",
- readl_relaxed(dev->conf +
- ep_link_ctrlstts_offset));
- }
- }
-
- if (dev->l0s_supported) {
- msm_pcie_write_mask(dev->dm_core + PCIE20_CAP_LINKCTRLSTATUS,
- 0, BIT(0));
- msm_pcie_write_mask(dev->conf + ep_link_ctrlstts_offset,
- 0, BIT(0));
- if (dev->shadow_en) {
- dev->rc_shadow[PCIE20_CAP_LINKCTRLSTATUS / 4] =
- readl_relaxed(dev->dm_core +
- PCIE20_CAP_LINKCTRLSTATUS);
- dev->ep_shadow[0][ep_link_ctrlstts_offset / 4] =
- readl_relaxed(dev->conf +
- ep_link_ctrlstts_offset);
- }
- PCIE_DBG2(dev, "RC's CAP_LINKCTRLSTATUS:0x%x\n",
- readl_relaxed(dev->dm_core +
- PCIE20_CAP_LINKCTRLSTATUS));
- PCIE_DBG2(dev, "EP's CAP_LINKCTRLSTATUS:0x%x\n",
- readl_relaxed(dev->conf + ep_link_ctrlstts_offset));
- }
-
- if (dev->l1_supported) {
- msm_pcie_write_mask(dev->dm_core + PCIE20_CAP_LINKCTRLSTATUS,
- 0, BIT(1));
- msm_pcie_write_mask(dev->conf + ep_link_ctrlstts_offset,
- 0, BIT(1));
- if (dev->shadow_en) {
- dev->rc_shadow[PCIE20_CAP_LINKCTRLSTATUS / 4] =
- readl_relaxed(dev->dm_core +
- PCIE20_CAP_LINKCTRLSTATUS);
- dev->ep_shadow[0][ep_link_ctrlstts_offset / 4] =
- readl_relaxed(dev->conf +
- ep_link_ctrlstts_offset);
- }
- PCIE_DBG2(dev, "RC's CAP_LINKCTRLSTATUS:0x%x\n",
- readl_relaxed(dev->dm_core +
- PCIE20_CAP_LINKCTRLSTATUS));
- PCIE_DBG2(dev, "EP's CAP_LINKCTRLSTATUS:0x%x\n",
- readl_relaxed(dev->conf + ep_link_ctrlstts_offset));
- }
-
- if (dev->l1ss_supported) {
- current_offset = PCIE_EXT_CAP_OFFSET;
- while (current_offset) {
- if (msm_pcie_check_align(dev, current_offset))
- return;
-
- val = readl_relaxed(dev->conf + current_offset);
- if ((val & 0xffff) == L1SUB_CAP_ID) {
- ep_l1sub_cap_reg1_offset = current_offset + 0x4;
- ep_l1sub_ctrl1_offset = current_offset + 0x8;
- break;
- }
- current_offset = val >> 20;
- }
- if (!ep_l1sub_ctrl1_offset) {
- PCIE_DBG(dev,
- "RC%d endpoint does not support l1ss registers\n",
- dev->rc_idx);
- return;
- }
-
- val = readl_relaxed(dev->conf + ep_l1sub_cap_reg1_offset);
-
- PCIE_DBG2(dev, "EP's L1SUB_CAPABILITY_REG_1: 0x%x\n", val);
- PCIE_DBG2(dev, "RC%d: ep_l1sub_ctrl1_offset: 0x%x\n",
- dev->rc_idx, ep_l1sub_ctrl1_offset);
-
- val &= 0xf;
-
- msm_pcie_write_reg_field(dev->dm_core, PCIE20_L1SUB_CONTROL1,
- 0xf, val);
- msm_pcie_write_mask(dev->dm_core +
- PCIE20_DEVICE_CONTROL2_STATUS2,
- 0, BIT(10));
- msm_pcie_write_reg_field(dev->conf, ep_l1sub_ctrl1_offset,
- 0xf, val);
- msm_pcie_write_mask(dev->conf + ep_dev_ctrl2stts2_offset,
- 0, BIT(10));
- if (dev->shadow_en) {
- dev->rc_shadow[PCIE20_L1SUB_CONTROL1 / 4] =
- readl_relaxed(dev->dm_core +
- PCIE20_L1SUB_CONTROL1);
- dev->rc_shadow[PCIE20_DEVICE_CONTROL2_STATUS2 / 4] =
- readl_relaxed(dev->dm_core +
- PCIE20_DEVICE_CONTROL2_STATUS2);
- dev->ep_shadow[0][ep_l1sub_ctrl1_offset / 4] =
- readl_relaxed(dev->conf +
- ep_l1sub_ctrl1_offset);
- dev->ep_shadow[0][ep_dev_ctrl2stts2_offset / 4] =
- readl_relaxed(dev->conf +
- ep_dev_ctrl2stts2_offset);
- }
- PCIE_DBG2(dev, "RC's L1SUB_CONTROL1:0x%x\n",
- readl_relaxed(dev->dm_core + PCIE20_L1SUB_CONTROL1));
- PCIE_DBG2(dev, "RC's DEVICE_CONTROL2_STATUS2:0x%x\n",
- readl_relaxed(dev->dm_core +
- PCIE20_DEVICE_CONTROL2_STATUS2));
- PCIE_DBG2(dev, "EP's L1SUB_CONTROL1:0x%x\n",
- readl_relaxed(dev->conf + ep_l1sub_ctrl1_offset));
- PCIE_DBG2(dev, "EP's DEVICE_CONTROL2_STATUS2:0x%x\n",
- readl_relaxed(dev->conf +
- ep_dev_ctrl2stts2_offset));
- }
-}
-
static void msm_pcie_config_msi_controller(struct msm_pcie_dev_t *dev)
{
int i;
@@ -3622,6 +3770,7 @@
uint32_t val;
long int retries = 0;
int link_check_count = 0;
+ unsigned long ep_up_timeout = 0;
PCIE_DBG(dev, "RC%d: entry\n", dev->rc_idx);
@@ -3782,6 +3931,8 @@
1 - dev->gpio[MSM_PCIE_GPIO_PERST].on);
usleep_range(dev->perst_delay_us_min, dev->perst_delay_us_max);
+ ep_up_timeout = jiffies + usecs_to_jiffies(EP_UP_TIMEOUT_US);
+
/* setup Gen3 specific configurations */
if (dev->max_link_speed == GEN3_SPEED)
msm_pcie_setup_gen3(dev);
@@ -3821,24 +3972,59 @@
goto link_fail;
}
- msm_pcie_config_controller(dev);
-
- if (!dev->msi_gicm_addr)
- msm_pcie_config_msi_controller(dev);
-
- msm_pcie_config_link_state(dev);
-
- if (dev->enumerated)
- pci_walk_bus(dev->dev->bus, &msm_pcie_config_device, dev);
-
dev->link_status = MSM_PCIE_LINK_ENABLED;
dev->power_on = true;
dev->suspending = false;
dev->link_turned_on_counter++;
+ if (dev->switch_latency) {
+ PCIE_DBG(dev, "switch_latency: %dms\n",
+ dev->switch_latency);
+ if (dev->switch_latency <= SWITCH_DELAY_MAX)
+ usleep_range(dev->switch_latency * 1000,
+ dev->switch_latency * 1000);
+ else
+ msleep(dev->switch_latency);
+ }
+
+ msm_pcie_config_controller(dev);
+
+ /* check endpoint configuration space is accessible */
+ while (time_before(jiffies, ep_up_timeout)) {
+ if (readl_relaxed(dev->conf) != PCIE_LINK_DOWN)
+ break;
+ usleep_range(EP_UP_TIMEOUT_US_MIN, EP_UP_TIMEOUT_US_MAX);
+ }
+
+ if (readl_relaxed(dev->conf) != PCIE_LINK_DOWN) {
+ PCIE_DBG(dev,
+ "PCIe: RC%d: endpoint config space is accessible\n",
+ dev->rc_idx);
+ } else {
+ PCIE_ERR(dev,
+ "PCIe: RC%d: endpoint config space is not accessible\n",
+ dev->rc_idx);
+ dev->link_status = MSM_PCIE_LINK_DISABLED;
+ dev->power_on = false;
+ dev->link_turned_off_counter++;
+ ret = -ENODEV;
+ goto link_fail;
+ }
+
+ if (!dev->msi_gicm_addr)
+ msm_pcie_config_msi_controller(dev);
+
+ if (dev->enumerated) {
+ pci_walk_bus(dev->dev->bus, &msm_pcie_config_device, dev);
+ msm_pcie_config_link_pm_rc(dev, dev->dev, true);
+ }
+
goto out;
link_fail:
+ if (msm_pcie_keep_resources_on & BIT(dev->rc_idx))
+ goto out;
+
if (dev->gpio[MSM_PCIE_GPIO_EP].num)
gpio_set_value(dev->gpio[MSM_PCIE_GPIO_EP].num,
1 - dev->gpio[MSM_PCIE_GPIO_EP].on);
@@ -4032,8 +4218,9 @@
if (pcie_dev->num_ep > 1)
pcie_dev->pending_ep_reg = true;
- msm_pcie_config_ep_aer(pcie_dev,
- &dev_table_t[index]);
+ if (pcie_dev->aer_enable)
+ msm_pcie_config_ep_aer(pcie_dev,
+ &dev_table_t[index]);
break;
}
@@ -4213,6 +4400,8 @@
ret = -ENODEV;
goto out;
}
+
+ msm_pcie_config_link_pm_rc(dev, dev->dev, true);
} else {
PCIE_ERR(dev, "PCIe: failed to enable RC%d.\n",
dev->rc_idx);
@@ -4547,8 +4736,10 @@
panic("User has chosen to panic on linkdown\n");
/* assert PERST */
- gpio_set_value(dev->gpio[MSM_PCIE_GPIO_PERST].num,
- dev->gpio[MSM_PCIE_GPIO_PERST].on);
+ if (!(msm_pcie_keep_resources_on & BIT(dev->rc_idx)))
+ gpio_set_value(dev->gpio[MSM_PCIE_GPIO_PERST].num,
+ dev->gpio[MSM_PCIE_GPIO_PERST].on);
+
PCIE_ERR(dev, "PCIe link is down for RC%d\n", dev->rc_idx);
if (dev->num_ep > 1) {
@@ -5198,6 +5389,238 @@
disable_irq(dev->wake_n);
}
+static void msm_pcie_config_l0s(struct msm_pcie_dev_t *dev,
+ struct pci_dev *pdev, bool enable)
+{
+ u32 val;
+ u32 lnkcap_offset = pdev->pcie_cap + PCI_EXP_LNKCAP;
+ u32 lnkctl_offset = pdev->pcie_cap + PCI_EXP_LNKCTL;
+
+ pci_read_config_dword(pdev, lnkcap_offset, &val);
+ if (!(val & BIT(10))) {
+ PCIE_DBG(dev,
+ "PCIe: RC%d: PCI device does not support L0s\n",
+ dev->rc_idx);
+ return;
+ }
+
+ if (enable)
+ msm_pcie_config_clear_set_dword(pdev, lnkctl_offset, 0,
+ PCI_EXP_LNKCTL_ASPM_L0S);
+ else
+ msm_pcie_config_clear_set_dword(pdev, lnkctl_offset,
+ PCI_EXP_LNKCTL_ASPM_L0S, 0);
+
+ pci_read_config_dword(pdev, lnkctl_offset, &val);
+ PCIE_DBG2(dev, "PCIe: RC%d: LINKCTRLSTATUS:0x%x\n", dev->rc_idx, val);
+}
+
+static void msm_pcie_config_l1(struct msm_pcie_dev_t *dev,
+ struct pci_dev *pdev, bool enable)
+{
+ u32 val;
+ u32 lnkcap_offset = pdev->pcie_cap + PCI_EXP_LNKCAP;
+ u32 lnkctl_offset = pdev->pcie_cap + PCI_EXP_LNKCTL;
+
+ pci_read_config_dword(pdev, lnkcap_offset, &val);
+ if (!(val & BIT(11))) {
+ PCIE_DBG(dev,
+ "PCIe: RC%d: PCI device does not support L1\n",
+ dev->rc_idx);
+ return;
+ }
+
+ if (enable)
+ msm_pcie_config_clear_set_dword(pdev, lnkctl_offset, 0,
+ PCI_EXP_LNKCTL_ASPM_L1);
+ else
+ msm_pcie_config_clear_set_dword(pdev, lnkctl_offset,
+ PCI_EXP_LNKCTL_ASPM_L1, 0);
+
+ pci_read_config_dword(pdev, lnkctl_offset, &val);
+ PCIE_DBG2(dev, "PCIe: RC%d: LINKCTRLSTATUS:0x%x\n", dev->rc_idx, val);
+}
+
+static void msm_pcie_config_l1ss(struct msm_pcie_dev_t *dev,
+ struct pci_dev *pdev, bool enable)
+{
+ bool l1_1_cap_support, l1_2_cap_support;
+ u32 val, val2;
+ u32 l1ss_cap_id_offset, l1ss_cap_offset, l1ss_ctl1_offset;
+ u32 devctl2_offset = pdev->pcie_cap + PCI_EXP_DEVCTL2;
+
+ l1ss_cap_id_offset = pci_find_ext_capability(pdev, PCI_EXT_CAP_ID_L1SS);
+ if (!l1ss_cap_id_offset) {
+ PCIE_DBG(dev,
+ "PCIe: RC%d could not find L1ss capability register for device\n",
+ dev->rc_idx);
+ return;
+ }
+
+ l1ss_cap_offset = l1ss_cap_id_offset + PCI_L1SS_CAP;
+ l1ss_ctl1_offset = l1ss_cap_id_offset + PCI_L1SS_CTL1;
+
+ pci_read_config_dword(pdev, l1ss_cap_offset, &val);
+ l1_1_cap_support = !!(val & (PCI_L1SS_CAP_ASPM_L1_1));
+ l1_2_cap_support = !!(val & (PCI_L1SS_CAP_ASPM_L1_2));
+ if (!l1_1_cap_support && !l1_2_cap_support) {
+ PCIE_DBG(dev,
+ "PCIe: RC%d: PCI device does not support L1.1 and L1.2\n",
+ dev->rc_idx);
+ return;
+ }
+
+ /* Enable the AUX Clock and the Core Clk to be synchronous for L1ss */
+ if (pci_is_root_bus(pdev->bus) && !dev->aux_clk_sync) {
+ if (enable)
+ msm_pcie_write_mask(dev->parf +
+ PCIE20_PARF_SYS_CTRL, BIT(3), 0);
+ else
+ msm_pcie_write_mask(dev->parf +
+ PCIE20_PARF_SYS_CTRL, 0, BIT(3));
+ }
+
+ if (enable) {
+ msm_pcie_config_clear_set_dword(pdev, devctl2_offset, 0,
+ PCI_EXP_DEVCTL2_LTR_EN);
+ msm_pcie_config_clear_set_dword(pdev, l1ss_ctl1_offset, 0,
+ (l1_1_cap_support ? PCI_L1SS_CTL1_ASPM_L1_1 : 0) |
+ (l1_2_cap_support ? PCI_L1SS_CTL1_ASPM_L1_2 : 0));
+ } else {
+ msm_pcie_config_clear_set_dword(pdev, devctl2_offset,
+ PCI_EXP_DEVCTL2_LTR_EN, 0);
+ msm_pcie_config_clear_set_dword(pdev, l1ss_ctl1_offset,
+ (l1_1_cap_support ? PCI_L1SS_CTL1_ASPM_L1_1 : 0) |
+ (l1_2_cap_support ? PCI_L1SS_CTL1_ASPM_L1_2 : 0), 0);
+ }
+
+ pci_read_config_dword(pdev, l1ss_ctl1_offset, &val);
+ PCIE_DBG2(dev, "PCIe: RC%d: L1SUB_CONTROL1:0x%x\n", dev->rc_idx, val);
+
+ pci_read_config_dword(pdev, devctl2_offset, &val2);
+ PCIE_DBG2(dev, "PCIe: RC%d: DEVICE_CONTROL2_STATUS2::0x%x\n",
+ dev->rc_idx, val2);
+}
+
+static void msm_pcie_config_clock_power_management(struct msm_pcie_dev_t *dev,
+ struct pci_dev *pdev)
+{
+ u32 val;
+ u32 lnkcap_offset = pdev->pcie_cap + PCI_EXP_LNKCAP;
+ u32 lnkctl_offset = pdev->pcie_cap + PCI_EXP_LNKCTL;
+
+ if (pci_is_root_bus(pdev->bus))
+ return;
+
+ pci_read_config_dword(pdev, lnkcap_offset, &val);
+ if (val & PCI_EXP_LNKCAP_CLKPM)
+ msm_pcie_config_clear_set_dword(pdev, lnkctl_offset, 0,
+ PCI_EXP_LNKCTL_CLKREQ_EN);
+ else
+ PCIE_DBG(dev,
+ "PCIe: RC%d: PCI device does not support clock power management\n",
+ dev->rc_idx);
+}
+
+static void msm_pcie_config_link_pm(struct msm_pcie_dev_t *dev,
+ struct pci_dev *pdev, bool enable)
+{
+ if (dev->common_clk_en)
+ msm_pcie_config_clear_set_dword(pdev,
+ pdev->pcie_cap + PCI_EXP_LNKCTL, 0,
+ PCI_EXP_LNKCTL_CCC);
+
+ if (dev->clk_power_manage_en)
+ msm_pcie_config_clock_power_management(dev, pdev);
+ if (dev->l0s_supported)
+ msm_pcie_config_l0s(dev, pdev, enable);
+ if (dev->l1ss_supported)
+ msm_pcie_config_l1ss(dev, pdev, enable);
+ if (dev->l1_supported)
+ msm_pcie_config_l1(dev, pdev, enable);
+}
+
+static void msm_pcie_config_link_pm_rc(struct msm_pcie_dev_t *dev,
+ struct pci_dev *pdev, bool enable)
+{
+ bool child_l0s_enable = 0, child_l1_enable = 0, child_l1ss_enable = 0;
+
+ if (!pdev->subordinate || !(&pdev->subordinate->devices)) {
+ PCIE_DBG(dev,
+ "PCIe: RC%d: no device connected to root complex\n",
+ dev->rc_idx);
+ return;
+ }
+
+ if (dev->l0s_supported) {
+ struct pci_dev *child_pdev, *c_pdev;
+
+ list_for_each_entry_safe(child_pdev, c_pdev,
+ &pdev->subordinate->devices, bus_list) {
+ u32 val;
+
+ pci_read_config_dword(child_pdev,
+ pdev->pcie_cap + PCI_EXP_LNKCTL, &val);
+ child_l0s_enable = !!(val & PCI_EXP_LNKCTL_ASPM_L0S);
+ if (child_l0s_enable)
+ break;
+ }
+
+ if (child_l0s_enable)
+ msm_pcie_config_l0s(dev, pdev, enable);
+ else
+ dev->l0s_supported = false;
+ }
+
+ if (dev->l1ss_supported) {
+ struct pci_dev *child_pdev, *c_pdev;
+
+ list_for_each_entry_safe(child_pdev, c_pdev,
+ &pdev->subordinate->devices, bus_list) {
+ u32 val;
+ u32 l1ss_cap_id_offset =
+ pci_find_ext_capability(child_pdev,
+ PCI_EXT_CAP_ID_L1SS);
+
+ if (!l1ss_cap_id_offset)
+ continue;
+
+ pci_read_config_dword(child_pdev,
+ l1ss_cap_id_offset + PCI_L1SS_CTL1, &val);
+ child_l1ss_enable = !!(val &
+ (PCI_L1SS_CTL1_ASPM_L1_1 |
+ PCI_L1SS_CTL1_ASPM_L1_2));
+ if (child_l1ss_enable)
+ break;
+ }
+
+ if (child_l1ss_enable)
+ msm_pcie_config_l1ss(dev, pdev, enable);
+ else
+ dev->l1ss_supported = false;
+ }
+
+ if (dev->l1_supported) {
+ struct pci_dev *child_pdev, *c_pdev;
+
+ list_for_each_entry_safe(child_pdev, c_pdev,
+ &pdev->subordinate->devices, bus_list) {
+ u32 val;
+
+ pci_read_config_dword(child_pdev,
+ pdev->pcie_cap + PCI_EXP_LNKCTL, &val);
+ child_l1_enable = !!(val & PCI_EXP_LNKCTL_ASPM_L1);
+ if (child_l1_enable)
+ break;
+ }
+
+ if (child_l1_enable)
+ msm_pcie_config_l1(dev, pdev, enable);
+ else
+ dev->l1_supported = false;
+ }
+}
+
static int msm_pcie_config_device(struct pci_dev *dev, void *pdev)
{
struct msm_pcie_dev_t *pcie_dev = (struct msm_pcie_dev_t *)pdev;
@@ -5210,6 +5633,9 @@
msm_pcie_configure_sid(pcie_dev, dev);
+ if (!pci_is_root_bus(dev->bus))
+ msm_pcie_config_link_pm(pcie_dev, dev, true);
+
return 0;
}
@@ -5251,16 +5677,25 @@
msm_pcie_dev[rc_idx].l0s_supported =
of_property_read_bool((&pdev->dev)->of_node,
"qcom,l0s-supported");
+ if (msm_pcie_invert_l0s_support & BIT(rc_idx))
+ msm_pcie_dev[rc_idx].l0s_supported =
+ !msm_pcie_dev[rc_idx].l0s_supported;
PCIE_DBG(&msm_pcie_dev[rc_idx], "L0s is %s supported.\n",
msm_pcie_dev[rc_idx].l0s_supported ? "" : "not");
msm_pcie_dev[rc_idx].l1_supported =
of_property_read_bool((&pdev->dev)->of_node,
"qcom,l1-supported");
+ if (msm_pcie_invert_l1_support & BIT(rc_idx))
+ msm_pcie_dev[rc_idx].l1_supported =
+ !msm_pcie_dev[rc_idx].l1_supported;
PCIE_DBG(&msm_pcie_dev[rc_idx], "L1 is %s supported.\n",
msm_pcie_dev[rc_idx].l1_supported ? "" : "not");
msm_pcie_dev[rc_idx].l1ss_supported =
of_property_read_bool((&pdev->dev)->of_node,
"qcom,l1ss-supported");
+ if (msm_pcie_invert_l1ss_support & BIT(rc_idx))
+ msm_pcie_dev[rc_idx].l1ss_supported =
+ !msm_pcie_dev[rc_idx].l1ss_supported;
PCIE_DBG(&msm_pcie_dev[rc_idx], "L1ss is %s supported.\n",
msm_pcie_dev[rc_idx].l1ss_supported ? "" : "not");
msm_pcie_dev[rc_idx].common_clk_en =
@@ -5366,6 +5801,20 @@
PCIE_DBG(&msm_pcie_dev[rc_idx], "RC%d: ep-latency: 0x%x.\n",
rc_idx, msm_pcie_dev[rc_idx].ep_latency);
+ msm_pcie_dev[rc_idx].switch_latency = 0;
+ ret = of_property_read_u32((&pdev->dev)->of_node,
+ "qcom,switch-latency",
+ &msm_pcie_dev[rc_idx].switch_latency);
+
+ if (ret)
+ PCIE_DBG(&msm_pcie_dev[rc_idx],
+ "RC%d: switch-latency does not exist.\n",
+ rc_idx);
+ else
+ PCIE_DBG(&msm_pcie_dev[rc_idx],
+ "RC%d: switch-latency: 0x%x.\n",
+ rc_idx, msm_pcie_dev[rc_idx].switch_latency);
+
msm_pcie_dev[rc_idx].wr_halt_size = 0;
ret = of_property_read_u32(pdev->dev.of_node,
"qcom,wr-halt-size",
@@ -5500,6 +5949,8 @@
msm_pcie_dev[rc_idx].suspending = false;
msm_pcie_dev[rc_idx].wake_counter = 0;
msm_pcie_dev[rc_idx].aer_enable = true;
+ if (msm_pcie_invert_aer_support)
+ msm_pcie_dev[rc_idx].aer_enable = false;
msm_pcie_dev[rc_idx].power_on = false;
msm_pcie_dev[rc_idx].use_msi = false;
msm_pcie_dev[rc_idx].use_pinctrl = false;
diff --git a/drivers/pinctrl/qcom/pinctrl-msm.c b/drivers/pinctrl/qcom/pinctrl-msm.c
index 13e9a0d..e63f1a0 100644
--- a/drivers/pinctrl/qcom/pinctrl-msm.c
+++ b/drivers/pinctrl/qcom/pinctrl-msm.c
@@ -15,6 +15,7 @@
#include <linux/delay.h>
#include <linux/err.h>
#include <linux/io.h>
+#include <linux/irq.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_irq.h>
@@ -747,11 +748,46 @@
.irq_set_wake = msm_gpio_irq_set_wake,
};
+static bool is_gpio_dual_edge(struct irq_data *d, irq_hw_number_t *dir_conn_irq)
+{
+ struct irq_desc *desc = irq_data_to_desc(d);
+ struct irq_data *parent_data = irq_get_irq_data(desc->parent_irq);
+ struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
+ struct msm_pinctrl *pctrl = gpiochip_get_data(gc);
+ int i;
+
+ if (!parent_data)
+ return false;
+
+ for (i = 0; i < pctrl->soc->n_dir_conns; i++) {
+ const struct msm_dir_conn *dir_conn = &pctrl->soc->dir_conn[i];
+
+ if (dir_conn->gpio == d->hwirq && (dir_conn->hwirq + 32)
+ != parent_data->hwirq) {
+ *dir_conn_irq = dir_conn->hwirq + 32;
+ return true;
+ }
+ }
+ return false;
+}
+
static void msm_dirconn_irq_mask(struct irq_data *d)
{
struct irq_desc *desc = irq_data_to_desc(d);
struct irq_data *parent_data = irq_get_irq_data(desc->parent_irq);
+ irq_hw_number_t dir_conn_irq = 0;
+ if (!parent_data)
+ return;
+
+ if (is_gpio_dual_edge(d, &dir_conn_irq)) {
+ struct irq_data *dir_conn_data =
+ irq_get_irq_data(irq_find_mapping(parent_data->domain,
+ dir_conn_irq));
+
+ if (dir_conn_data && dir_conn_data->chip->irq_mask)
+ dir_conn_data->chip->irq_mask(dir_conn_data);
+ }
if (parent_data->chip->irq_mask)
parent_data->chip->irq_mask(parent_data);
}
@@ -760,7 +796,19 @@
{
struct irq_desc *desc = irq_data_to_desc(d);
struct irq_data *parent_data = irq_get_irq_data(desc->parent_irq);
+ irq_hw_number_t dir_conn_irq = 0;
+ if (!parent_data)
+ return;
+
+ if (is_gpio_dual_edge(d, &dir_conn_irq)) {
+ struct irq_data *dir_conn_data =
+ irq_get_irq_data(irq_find_mapping(parent_data->domain,
+ dir_conn_irq));
+
+ if (dir_conn_data && dir_conn_data->chip->irq_unmask)
+ dir_conn_data->chip->irq_unmask(dir_conn_data);
+ }
if (parent_data->chip->irq_unmask)
parent_data->chip->irq_unmask(parent_data);
}
@@ -789,6 +837,9 @@
struct irq_desc *desc = irq_data_to_desc(d);
struct irq_data *parent_data = irq_get_irq_data(desc->parent_irq);
+ if (!parent_data)
+ return 0;
+
if (parent_data->chip->irq_set_affinity)
return parent_data->chip->irq_set_affinity(parent_data,
maskval, force);
@@ -807,10 +858,158 @@
return 0;
}
+static void msm_dirconn_cfg_reg(struct irq_data *d, u32 offset)
+{
+ u32 val = 0;
+ const struct msm_pingroup *g;
+ unsigned long flags;
+ struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
+ struct msm_pinctrl *pctrl = gpiochip_get_data(gc);
+
+ spin_lock_irqsave(&pctrl->lock, flags);
+ g = &pctrl->soc->groups[d->hwirq];
+
+ val = readl_relaxed(pctrl->regs + g->dir_conn_reg + (offset * 4));
+ val = (d->hwirq) & 0xFF;
+
+ writel_relaxed(val, pctrl->regs + g->dir_conn_reg + (offset * 4));
+
+ //write the dir_conn_en bit
+ val = readl_relaxed(pctrl->regs + g->intr_cfg_reg);
+ val |= BIT(g->dir_conn_en_bit);
+ writel_relaxed(val, pctrl->regs + g->intr_cfg_reg);
+ spin_unlock_irqrestore(&pctrl->lock, flags);
+}
+
+static void msm_dirconn_uncfg_reg(struct irq_data *d, u32 offset)
+{
+ const struct msm_pingroup *g;
+ unsigned long flags;
+ struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
+ struct msm_pinctrl *pctrl = gpiochip_get_data(gc);
+
+ spin_lock_irqsave(&pctrl->lock, flags);
+ g = &pctrl->soc->groups[d->hwirq];
+
+ writel_relaxed(BIT(8), pctrl->regs + g->dir_conn_reg + (offset * 4));
+ spin_unlock_irqrestore(&pctrl->lock, flags);
+}
+
+static int select_dir_conn_mux(struct irq_data *d, irq_hw_number_t *irq)
+{
+ struct msm_dir_conn *dc = NULL;
+ struct irq_desc *desc = irq_data_to_desc(d);
+ struct irq_data *parent_data = irq_get_irq_data(desc->parent_irq);
+ struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
+ struct msm_pinctrl *pctrl = gpiochip_get_data(gc);
+ int i;
+
+ if (!parent_data)
+ return -EINVAL;
+
+ for (i = 0; i < pctrl->soc->n_dir_conns; i++) {
+ struct msm_dir_conn *dir_conn =
+ (struct msm_dir_conn *)&pctrl->soc->dir_conn[i];
+
+ /* Check if there is already mux assigned for this gpio */
+ if (dir_conn->gpio == d->hwirq && (dir_conn->hwirq + 32) !=
+ parent_data->hwirq) {
+ *irq = dir_conn->hwirq + 32;
+ return pctrl->soc->dir_conn_irq_base - dir_conn->hwirq;
+ }
+
+ if (dir_conn->gpio)
+ continue;
+
+ /* Use the first unused direct connect available */
+ dc = dir_conn;
+ break;
+ }
+
+ if (dc) {
+ *irq = dc->hwirq + 32;
+ dc->gpio = (u32)d->hwirq;
+ return pctrl->soc->dir_conn_irq_base - (u32)dc->hwirq;
+ }
+
+ pr_err("%s: No direct connects available for interrupt %lu\n",
+ __func__, d->hwirq);
+ return -EINVAL;
+}
+
+static void add_dirconn_tlmm(struct irq_data *d, irq_hw_number_t irq)
+{
+ struct irq_desc *desc = irq_data_to_desc(d);
+ struct irq_data *parent_data = irq_get_irq_data(desc->parent_irq);
+ struct irq_data *dir_conn_data = NULL;
+ int offset = 0;
+ unsigned int virt = 0;
+
+ offset = select_dir_conn_mux(d, &irq);
+ if (offset < 0 || !parent_data)
+ return;
+
+ virt = irq_find_mapping(parent_data->domain, irq);
+ msm_dirconn_cfg_reg(d, offset);
+ irq_set_handler_data(virt, d);
+ desc = irq_to_desc(virt);
+ if (!desc)
+ return;
+
+ dir_conn_data = &(desc->irq_data);
+
+ if (dir_conn_data) {
+ if (dir_conn_data->chip && dir_conn_data->chip->irq_set_type)
+ dir_conn_data->chip->irq_set_type(dir_conn_data,
+ IRQ_TYPE_EDGE_RISING);
+ if (dir_conn_data->chip && dir_conn_data->chip->irq_unmask)
+ dir_conn_data->chip->irq_unmask(dir_conn_data);
+ }
+}
+
+static void remove_dirconn_tlmm(struct irq_data *d, irq_hw_number_t irq)
+{
+ struct irq_desc *desc = irq_data_to_desc(d);
+ struct irq_data *parent_data = irq_get_irq_data(desc->parent_irq);
+ struct irq_data *dir_conn_data = NULL;
+ int offset = 0;
+ unsigned int virt = 0;
+
+ virt = irq_find_mapping(parent_data->domain, irq);
+ msm_dirconn_uncfg_reg(d, offset);
+ irq_set_handler_data(virt, NULL);
+ desc = irq_to_desc(virt);
+ if (!desc)
+ return;
+
+ dir_conn_data = &(desc->irq_data);
+
+ if (dir_conn_data) {
+ if (dir_conn_data->chip && dir_conn_data->chip->irq_mask)
+ dir_conn_data->chip->irq_mask(dir_conn_data);
+ }
+}
+
static int msm_dirconn_irq_set_type(struct irq_data *d, unsigned int type)
{
struct irq_desc *desc = irq_data_to_desc(d);
struct irq_data *parent_data = irq_get_irq_data(desc->parent_irq);
+ irq_hw_number_t irq = 0;
+
+ if (!parent_data)
+ return 0;
+
+ if (type == IRQ_TYPE_EDGE_BOTH) {
+ add_dirconn_tlmm(d, irq);
+ } else {
+ if (is_gpio_dual_edge(d, &irq))
+ remove_dirconn_tlmm(d, irq);
+ }
+
+ if (type & (IRQ_TYPE_LEVEL_LOW | IRQ_TYPE_LEVEL_HIGH))
+ irq_set_handler_locked(d, handle_level_irq);
+ else if (type & (IRQ_TYPE_EDGE_FALLING | IRQ_TYPE_EDGE_RISING))
+ irq_set_handler_locked(d, handle_edge_irq);
if (parent_data->chip->irq_set_type)
return parent_data->chip->irq_set_type(parent_data, type);
@@ -904,14 +1103,19 @@
fwspec.param_count = 3;
parent_irq = irq_create_fwspec_mapping(&fwspec);
- irq = irq_find_mapping(pctrl->chip.irqdomain, dirconn->gpio);
+ if (dirconn->gpio != 0) {
+ irq = irq_find_mapping(pctrl->chip.irqdomain,
+ dirconn->gpio);
- irq_set_parent(irq, parent_irq);
- irq_set_chip(irq, &msm_dirconn_irq_chip);
- irq_set_chip_data(irq, irq_get_irq_data(parent_irq));
- __irq_set_handler(parent_irq, msm_gpio_dirconn_handler,
+ irq_set_parent(irq, parent_irq);
+ irq_set_chip(irq, &msm_dirconn_irq_chip);
+ __irq_set_handler(parent_irq, msm_gpio_dirconn_handler,
false, NULL);
- irq_set_handler_data(parent_irq, irq_get_irq_data(irq));
+ irq_set_handler_data(parent_irq, irq_get_irq_data(irq));
+ } else {
+ __irq_set_handler(parent_irq, msm_gpio_dirconn_handler,
+ false, NULL);
+ }
}
}
diff --git a/drivers/pinctrl/qcom/pinctrl-msm.h b/drivers/pinctrl/qcom/pinctrl-msm.h
index 988b7ca..1c6df2f 100644
--- a/drivers/pinctrl/qcom/pinctrl-msm.h
+++ b/drivers/pinctrl/qcom/pinctrl-msm.h
@@ -43,6 +43,8 @@
* @intr_status_reg: Offset of the register holding the status bits for this group.
* @intr_target_reg: Offset of the register specifying routing of the interrupts
* from this group.
+ * @dir_conn_reg: Offset of the register specifying direct connect
+ * setup of this group.
* @mux_bit: Offset in @ctl_reg for the pinmux function selection.
* @pull_bit: Offset in @ctl_reg for the bias configuration.
* @drv_bit: Offset in @ctl_reg for the drive strength configuration.
@@ -75,6 +77,7 @@
u32 intr_cfg_reg;
u32 intr_status_reg;
u32 intr_target_reg;
+ u32 dir_conn_reg;
unsigned mux_bit:5;
@@ -95,6 +98,7 @@
unsigned intr_polarity_bit:5;
unsigned intr_detection_bit:5;
unsigned intr_detection_width:5;
+ unsigned dir_conn_en_bit:8;
}
/**
@@ -118,6 +122,8 @@
* @ngpio: The number of pingroups the driver should expose as GPIOs.
* @dir_conn: An array describing all the pins directly connected to GIC.
* @ndirconns: The number of pins directly connected to GIC
+ * @dir_conn_offsets: Direct connect register offsets for each tile.
+ * @dir_conn_irq_base: Direct connect interrupt base register for kpss.
*/
struct msm_pinctrl_soc_data {
const struct pinctrl_pin_desc *pins;
@@ -129,6 +135,7 @@
unsigned ngpios;
const struct msm_dir_conn *dir_conn;
unsigned int n_dir_conns;
+ unsigned int dir_conn_irq_base;
};
int msm_pinctrl_probe(struct platform_device *pdev,
diff --git a/drivers/pinctrl/qcom/pinctrl-sdm845-v2.c b/drivers/pinctrl/qcom/pinctrl-sdm845-v2.c
index f7d551e..e77dcd9 100644
--- a/drivers/pinctrl/qcom/pinctrl-sdm845-v2.c
+++ b/drivers/pinctrl/qcom/pinctrl-sdm845-v2.c
@@ -53,6 +53,8 @@
.intr_cfg_reg = base + 0x8 + REG_SIZE * id, \
.intr_status_reg = base + 0xc + REG_SIZE * id, \
.intr_target_reg = base + 0x8 + REG_SIZE * id, \
+ .dir_conn_reg = (base == NORTH) ? base + 0xa4000 : \
+ ((base == SOUTH) ? base + 0xa8000 : base + 0x9e000), \
.mux_bit = 2, \
.pull_bit = 0, \
.drv_bit = 6, \
@@ -67,6 +69,7 @@
.intr_polarity_bit = 1, \
.intr_detection_bit = 2, \
.intr_detection_width = 2, \
+ .dir_conn_en_bit = 8, \
}
#define SDC_QDSD_PINGROUP(pg_name, ctl, pull, drv) \
@@ -1689,7 +1692,7 @@
{24, 517},
{26, 518},
{30, 519},
- {31, 639},
+ {31, 632},
{32, 521},
{34, 522},
{36, 523},
@@ -1697,12 +1700,12 @@
{38, 525},
{39, 526},
{40, 527},
- {41, 637},
+ {41, 630},
{43, 529},
{44, 530},
{46, 531},
{48, 532},
- {49, 640},
+ {49, 633 },
{52, 534},
{53, 535},
{54, 536},
@@ -1727,7 +1730,7 @@
{85, 555},
{86, 556},
{88, 557},
- {89, 638},
+ {89, 631},
{91, 559},
{92, 560},
{95, 561},
@@ -1747,6 +1750,7 @@
{123, 613},
{124, 614},
{125, 615},
+ {126, 616},
{127, 617},
{128, 618},
{129, 619},
@@ -1754,6 +1758,14 @@
{132, 621},
{133, 622},
{145, 623},
+ {0, 216},
+ {0, 215},
+ {0, 214},
+ {0, 213},
+ {0, 212},
+ {0, 211},
+ {0, 210},
+ {0, 209},
};
static const struct msm_pinctrl_soc_data sdm845_pinctrl = {
@@ -1766,6 +1778,7 @@
.ngpios = 150,
.dir_conn = sdm845_dir_conn,
.n_dir_conns = ARRAY_SIZE(sdm845_dir_conn),
+ .dir_conn_irq_base = 216,
};
static int sdm845_pinctrl_probe(struct platform_device *pdev)
diff --git a/drivers/pinctrl/qcom/pinctrl-sdm845.c b/drivers/pinctrl/qcom/pinctrl-sdm845.c
index b7e06b6..ee5bd19 100644
--- a/drivers/pinctrl/qcom/pinctrl-sdm845.c
+++ b/drivers/pinctrl/qcom/pinctrl-sdm845.c
@@ -52,6 +52,8 @@
.intr_cfg_reg = base + 0x8 + REG_SIZE * id, \
.intr_status_reg = base + 0xc + REG_SIZE * id, \
.intr_target_reg = base + 0x8 + REG_SIZE * id, \
+ .dir_conn_reg = (base == NORTH) ? base + 0xa5000 :\
+ base + 0xa8000, \
.mux_bit = 2, \
.pull_bit = 0, \
.drv_bit = 6, \
@@ -66,6 +68,7 @@
.intr_polarity_bit = 1, \
.intr_detection_bit = 2, \
.intr_detection_width = 2, \
+ .dir_conn_en_bit = 8, \
}
#define SDC_QDSD_PINGROUP(pg_name, ctl, pull, drv) \
@@ -117,6 +120,7 @@
.intr_detection_bit = -1, \
.intr_detection_width = -1, \
}
+
static const struct pinctrl_pin_desc sdm845_pins[] = {
PINCTRL_PIN(0, "GPIO_0"),
PINCTRL_PIN(1, "GPIO_1"),
@@ -1648,7 +1652,7 @@
[153] = UFS_RESET(ufs_reset, 0x99f000),
};
-static const struct msm_dir_conn sdm845_dir_conn[] = {
+static struct msm_dir_conn sdm845_dir_conn[] = {
{1, 510},
{3, 511},
{5, 512},
@@ -1659,7 +1663,7 @@
{24, 517},
{26, 518},
{30, 519},
- {31, 639},
+ {31, 632},
{32, 521},
{34, 522},
{36, 523},
@@ -1667,12 +1671,12 @@
{38, 525},
{39, 526},
{40, 527},
- {41, 637},
+ {41, 630},
{43, 529},
{44, 530},
{46, 531},
{48, 532},
- {49, 640},
+ {49, 633},
{52, 534},
{53, 535},
{54, 536},
@@ -1697,7 +1701,7 @@
{85, 555},
{86, 556},
{88, 557},
- {89, 638},
+ {89, 631},
{91, 559},
{92, 560},
{95, 561},
@@ -1717,6 +1721,7 @@
{123, 613},
{124, 614},
{125, 615},
+ {126, 616},
{127, 617},
{128, 618},
{129, 619},
@@ -1724,6 +1729,14 @@
{132, 621},
{133, 622},
{145, 623},
+ {0, 216},
+ {0, 215},
+ {0, 214},
+ {0, 213},
+ {0, 212},
+ {0, 211},
+ {0, 210},
+ {0, 209},
};
static const struct msm_pinctrl_soc_data sdm845_pinctrl = {
@@ -1736,6 +1749,7 @@
.ngpios = 150,
.dir_conn = sdm845_dir_conn,
.n_dir_conns = ARRAY_SIZE(sdm845_dir_conn),
+ .dir_conn_irq_base = 216,
};
static int sdm845_pinctrl_probe(struct platform_device *pdev)
diff --git a/drivers/platform/msm/ipa/ipa_api.c b/drivers/platform/msm/ipa/ipa_api.c
index 4a9232e..abb714d 100644
--- a/drivers/platform/msm/ipa/ipa_api.c
+++ b/drivers/platform/msm/ipa/ipa_api.c
@@ -3004,6 +3004,57 @@
}
EXPORT_SYMBOL(ipa_ntn_uc_dereg_rdyCB);
+/**
+ * ipa_conn_wdi3_pipes() - connect wdi3 pipes
+ */
+int ipa_conn_wdi3_pipes(struct ipa_wdi3_conn_in_params *in,
+ struct ipa_wdi3_conn_out_params *out)
+{
+ int ret;
+
+ IPA_API_DISPATCH_RETURN(ipa_conn_wdi3_pipes, in, out);
+
+ return ret;
+}
+
+/**
+ * ipa_disconn_wdi3_pipes() - disconnect wdi3 pipes
+ */
+int ipa_disconn_wdi3_pipes(int ipa_ep_idx_tx, int ipa_ep_idx_rx)
+{
+ int ret;
+
+ IPA_API_DISPATCH_RETURN(ipa_disconn_wdi3_pipes, ipa_ep_idx_tx,
+ ipa_ep_idx_rx);
+
+ return ret;
+}
+
+/**
+ * ipa_enable_wdi3_pipes() - enable wdi3 pipes
+ */
+int ipa_enable_wdi3_pipes(int ipa_ep_idx_tx, int ipa_ep_idx_rx)
+{
+ int ret;
+
+ IPA_API_DISPATCH_RETURN(ipa_enable_wdi3_pipes, ipa_ep_idx_tx,
+ ipa_ep_idx_rx);
+
+ return ret;
+}
+
+/**
+ * ipa_disable_wdi3_pipes() - disable wdi3 pipes
+ */
+int ipa_disable_wdi3_pipes(int ipa_ep_idx_tx, int ipa_ep_idx_rx)
+{
+ int ret;
+
+ IPA_API_DISPATCH_RETURN(ipa_disable_wdi3_pipes, ipa_ep_idx_tx,
+ ipa_ep_idx_rx);
+
+ return ret;
+}
static const struct dev_pm_ops ipa_pm_ops = {
.suspend_noirq = ipa_ap_suspend,
diff --git a/drivers/platform/msm/ipa/ipa_api.h b/drivers/platform/msm/ipa/ipa_api.h
index 20471eb..7a48b68 100644
--- a/drivers/platform/msm/ipa/ipa_api.h
+++ b/drivers/platform/msm/ipa/ipa_api.h
@@ -12,6 +12,7 @@
#include <linux/ipa_mhi.h>
#include <linux/ipa_uc_offload.h>
+#include <linux/ipa_wdi3.h>
#include "ipa_common_i.h"
#ifndef _IPA_API_H_
@@ -385,6 +386,18 @@
void *user_data);
void (*ipa_ntn_uc_dereg_rdyCB)(void);
+
+ int (*ipa_conn_wdi3_pipes)(struct ipa_wdi3_conn_in_params *in,
+ struct ipa_wdi3_conn_out_params *out);
+
+ int (*ipa_disconn_wdi3_pipes)(int ipa_ep_idx_tx,
+ int ipa_ep_idx_rx);
+
+ int (*ipa_enable_wdi3_pipes)(int ipa_ep_idx_tx,
+ int ipa_ep_idx_rx);
+
+ int (*ipa_disable_wdi3_pipes)(int ipa_ep_idx_tx,
+ int ipa_ep_idx_rx);
};
#ifdef CONFIG_IPA
diff --git a/drivers/platform/msm/ipa/ipa_clients/Makefile b/drivers/platform/msm/ipa/ipa_clients/Makefile
index 61625f5..738d88f 100644
--- a/drivers/platform/msm/ipa/ipa_clients/Makefile
+++ b/drivers/platform/msm/ipa/ipa_clients/Makefile
@@ -1,4 +1,4 @@
-obj-$(CONFIG_IPA3) += ipa_usb.o odu_bridge.o ipa_mhi_client.o ipa_uc_offload.o
-obj-$(CONFIG_IPA) += odu_bridge.o ipa_mhi_client.o ipa_uc_offload.o
+obj-$(CONFIG_IPA3) += ipa_usb.o odu_bridge.o ipa_mhi_client.o ipa_uc_offload.o ipa_wdi3.o
+obj-$(CONFIG_IPA) += odu_bridge.o ipa_mhi_client.o ipa_uc_offload.o ipa_wdi3.o
obj-$(CONFIG_ECM_IPA) += ecm_ipa.o
obj-$(CONFIG_RNDIS_IPA) += rndis_ipa.o
diff --git a/drivers/platform/msm/ipa/ipa_clients/ipa_mhi_client.c b/drivers/platform/msm/ipa/ipa_clients/ipa_mhi_client.c
index 4d3113f..e18c0d4 100644
--- a/drivers/platform/msm/ipa/ipa_clients/ipa_mhi_client.c
+++ b/drivers/platform/msm/ipa/ipa_clients/ipa_mhi_client.c
@@ -185,6 +185,12 @@
return -ENOMEM;
}
+ res = ipa_dma_enable();
+ if (res) {
+ IPA_MHI_ERR("failed to enable IPA DMA rc=%d\n", res);
+ goto fail_dma_enable;
+ }
+
if (dir == IPA_MHI_DMA_FROM_HOST) {
res = ipa_dma_sync_memcpy(mem.phys_base, host_addr,
size);
@@ -206,8 +212,7 @@
goto fail_memcopy;
}
}
- dma_free_coherent(pdev, mem.size, mem.base,
- mem.phys_base);
+ goto dma_succeed;
} else {
void *host_ptr;
@@ -230,9 +235,14 @@
IPA_MHI_FUNC_EXIT();
return 0;
+dma_succeed:
+ IPA_MHI_FUNC_EXIT();
+ res = 0;
fail_memcopy:
- dma_free_coherent(ipa_get_dma_dev(), mem.size, mem.base,
- mem.phys_base);
+ if (ipa_dma_disable())
+ IPA_MHI_ERR("failed to disable IPA DMA\n");
+fail_dma_enable:
+ dma_free_coherent(pdev, mem.size, mem.base, mem.phys_base);
return res;
}
@@ -2499,6 +2509,7 @@
else
ipa_mhi_delete_rm_resources();
+ ipa_dma_destroy();
ipa_mhi_debugfs_destroy();
destroy_workqueue(ipa_mhi_client_ctx->wq);
kfree(ipa_mhi_client_ctx);
@@ -2714,6 +2725,12 @@
goto fail_create_wq;
}
+ res = ipa_dma_init();
+ if (res) {
+ IPA_MHI_ERR("failed to init ipa dma %d\n", res);
+ goto fail_dma_init;
+ }
+
if (ipa_pm_is_used())
res = ipa_mhi_register_pm();
else
@@ -2737,6 +2754,8 @@
return 0;
fail_rm:
+ ipa_dma_destroy();
+fail_dma_init:
destroy_workqueue(ipa_mhi_client_ctx->wq);
fail_create_wq:
kfree(ipa_mhi_client_ctx);
diff --git a/drivers/platform/msm/ipa/ipa_clients/ipa_wdi3.c b/drivers/platform/msm/ipa/ipa_clients/ipa_wdi3.c
new file mode 100644
index 0000000..f4c8763
--- /dev/null
+++ b/drivers/platform/msm/ipa/ipa_clients/ipa_wdi3.c
@@ -0,0 +1,526 @@
+/* Copyright (c) 2017 The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/ipa_wdi3.h>
+#include <linux/msm_ipa.h>
+#include <linux/string.h>
+#include "../ipa_common_i.h"
+
+#define OFFLOAD_DRV_NAME "ipa_wdi3"
+#define IPA_WDI3_DBG(fmt, args...) \
+ do { \
+ pr_debug(OFFLOAD_DRV_NAME " %s:%d " fmt, \
+ __func__, __LINE__, ## args); \
+ IPA_IPC_LOGGING(ipa_get_ipc_logbuf(), \
+ OFFLOAD_DRV_NAME " %s:%d " fmt, ## args); \
+ IPA_IPC_LOGGING(ipa_get_ipc_logbuf_low(), \
+ OFFLOAD_DRV_NAME " %s:%d " fmt, ## args); \
+ } while (0)
+
+#define IPA_WDI3_DBG_LOW(fmt, args...) \
+ do { \
+ pr_debug(OFFLOAD_DRV_NAME " %s:%d " fmt, \
+ __func__, __LINE__, ## args); \
+ IPA_IPC_LOGGING(ipa_get_ipc_logbuf_low(), \
+ OFFLOAD_DRV_NAME " %s:%d " fmt, ## args); \
+ } while (0)
+
+#define IPA_WDI3_ERR(fmt, args...) \
+ do { \
+ pr_err(OFFLOAD_DRV_NAME " %s:%d " fmt, \
+ __func__, __LINE__, ## args); \
+ IPA_IPC_LOGGING(ipa_get_ipc_logbuf(), \
+ OFFLOAD_DRV_NAME " %s:%d " fmt, ## args); \
+ IPA_IPC_LOGGING(ipa_get_ipc_logbuf_low(), \
+ OFFLOAD_DRV_NAME " %s:%d " fmt, ## args); \
+ } while (0)
+
+struct ipa_wdi3_intf_info {
+ char netdev_name[IPA_RESOURCE_NAME_MAX];
+ u8 hdr_len;
+ u32 partial_hdr_hdl[IPA_IP_MAX];
+ struct list_head link;
+};
+
+struct ipa_wdi3_context {
+ struct list_head head_intf_list;
+ ipa_notify_cb notify;
+ void *priv;
+ struct completion wdi3_completion;
+ struct mutex lock;
+};
+
+static struct ipa_wdi3_context *ipa_wdi3_ctx;
+
+static int ipa_wdi3_commit_partial_hdr(
+ struct ipa_ioc_add_hdr *hdr,
+ const char *netdev_name,
+ struct ipa_wdi3_hdr_info *hdr_info)
+{
+ int i;
+
+ if (!hdr || !hdr_info || !netdev_name) {
+ IPA_WDI3_ERR("Invalid input\n");
+ return -EINVAL;
+ }
+
+ hdr->commit = 1;
+ hdr->num_hdrs = 2;
+
+ snprintf(hdr->hdr[0].name, sizeof(hdr->hdr[0].name),
+ "%s_ipv4", netdev_name);
+ snprintf(hdr->hdr[1].name, sizeof(hdr->hdr[1].name),
+ "%s_ipv6", netdev_name);
+ for (i = IPA_IP_v4; i < IPA_IP_MAX; i++) {
+ hdr->hdr[i].hdr_len = hdr_info[i].hdr_len;
+ memcpy(hdr->hdr[i].hdr, hdr_info[i].hdr, hdr->hdr[i].hdr_len);
+ hdr->hdr[i].type = hdr_info[i].hdr_type;
+ hdr->hdr[i].is_partial = 1;
+ hdr->hdr[i].is_eth2_ofst_valid = 1;
+ hdr->hdr[i].eth2_ofst = hdr_info[i].dst_mac_addr_offset;
+ }
+
+ if (ipa_add_hdr(hdr)) {
+ IPA_WDI3_ERR("fail to add partial headers\n");
+ return -EFAULT;
+ }
+
+ return 0;
+}
+
+int ipa_wdi3_reg_intf(struct ipa_wdi3_reg_intf_in_params *in)
+{
+ struct ipa_ioc_add_hdr *hdr;
+ struct ipa_wdi3_intf_info *new_intf;
+ struct ipa_wdi3_intf_info *entry;
+ struct ipa_tx_intf tx;
+ struct ipa_rx_intf rx;
+ struct ipa_ioc_tx_intf_prop tx_prop[2];
+ struct ipa_ioc_rx_intf_prop rx_prop[2];
+ u32 len;
+ int ret = 0;
+
+ if (in == NULL) {
+ IPA_WDI3_ERR("invalid params in=%pK\n", in);
+ return -EINVAL;
+ }
+
+ if (!ipa_wdi3_ctx) {
+ ipa_wdi3_ctx = kzalloc(sizeof(*ipa_wdi3_ctx), GFP_KERNEL);
+ if (ipa_wdi3_ctx == NULL) {
+ IPA_WDI3_ERR("fail to alloc wdi3 ctx\n");
+ return -ENOMEM;
+ }
+ mutex_init(&ipa_wdi3_ctx->lock);
+ INIT_LIST_HEAD(&ipa_wdi3_ctx->head_intf_list);
+ }
+
+ IPA_WDI3_DBG("register interface for netdev %s\n",
+ in->netdev_name);
+
+ mutex_lock(&ipa_wdi3_ctx->lock);
+ list_for_each_entry(entry, &ipa_wdi3_ctx->head_intf_list, link)
+ if (strcmp(entry->netdev_name, in->netdev_name) == 0) {
+ IPA_WDI3_DBG("intf was added before.\n");
+ mutex_unlock(&ipa_wdi3_ctx->lock);
+ return 0;
+ }
+
+ IPA_WDI3_DBG("intf was not added before, proceed.\n");
+ new_intf = kzalloc(sizeof(*new_intf), GFP_KERNEL);
+ if (new_intf == NULL) {
+ IPA_WDI3_ERR("fail to alloc new intf\n");
+ mutex_unlock(&ipa_wdi3_ctx->lock);
+ return -ENOMEM;
+ }
+
+ INIT_LIST_HEAD(&new_intf->link);
+ strlcpy(new_intf->netdev_name, in->netdev_name,
+ sizeof(new_intf->netdev_name));
+ new_intf->hdr_len = in->hdr_info[0].hdr_len;
+
+ /* add partial header */
+ len = sizeof(struct ipa_ioc_add_hdr) + 2 * sizeof(struct ipa_hdr_add);
+ hdr = kzalloc(len, GFP_KERNEL);
+ if (hdr == NULL) {
+ IPA_WDI3_ERR("fail to alloc %d bytes\n", len);
+ ret = -EFAULT;
+ goto fail_alloc_hdr;
+ }
+
+ if (ipa_wdi3_commit_partial_hdr(hdr, in->netdev_name, in->hdr_info)) {
+ IPA_WDI3_ERR("fail to commit partial headers\n");
+ ret = -EFAULT;
+ goto fail_commit_hdr;
+ }
+
+ new_intf->partial_hdr_hdl[IPA_IP_v4] = hdr->hdr[IPA_IP_v4].hdr_hdl;
+ new_intf->partial_hdr_hdl[IPA_IP_v6] = hdr->hdr[IPA_IP_v6].hdr_hdl;
+ IPA_WDI3_DBG("IPv4 hdr hdl: %d IPv6 hdr hdl: %d\n",
+ hdr->hdr[IPA_IP_v4].hdr_hdl, hdr->hdr[IPA_IP_v6].hdr_hdl);
+
+ /* populate tx prop */
+ tx.num_props = 2;
+ tx.prop = tx_prop;
+
+ memset(tx_prop, 0, sizeof(tx_prop));
+ tx_prop[0].ip = IPA_IP_v4;
+ tx_prop[0].dst_pipe = IPA_CLIENT_WLAN1_CONS;
+ tx_prop[0].hdr_l2_type = in->hdr_info[0].hdr_type;
+ strlcpy(tx_prop[0].hdr_name, hdr->hdr[IPA_IP_v4].name,
+ sizeof(tx_prop[0].hdr_name));
+
+ tx_prop[1].ip = IPA_IP_v6;
+ tx_prop[1].dst_pipe = IPA_CLIENT_WLAN1_CONS;
+ tx_prop[1].hdr_l2_type = in->hdr_info[1].hdr_type;
+ strlcpy(tx_prop[1].hdr_name, hdr->hdr[IPA_IP_v6].name,
+ sizeof(tx_prop[1].hdr_name));
+
+ /* populate rx prop */
+ rx.num_props = 2;
+ rx.prop = rx_prop;
+
+ memset(rx_prop, 0, sizeof(rx_prop));
+ rx_prop[0].ip = IPA_IP_v4;
+ rx_prop[0].src_pipe = IPA_CLIENT_WLAN1_PROD;
+ rx_prop[0].hdr_l2_type = in->hdr_info[0].hdr_type;
+ if (in->is_meta_data_valid) {
+ rx_prop[0].attrib.attrib_mask |= IPA_FLT_META_DATA;
+ rx_prop[0].attrib.meta_data = in->meta_data;
+ rx_prop[0].attrib.meta_data_mask = in->meta_data_mask;
+ }
+
+ rx_prop[1].ip = IPA_IP_v6;
+ rx_prop[1].src_pipe = IPA_CLIENT_WLAN1_PROD;
+ rx_prop[1].hdr_l2_type = in->hdr_info[1].hdr_type;
+ if (in->is_meta_data_valid) {
+ rx_prop[1].attrib.attrib_mask |= IPA_FLT_META_DATA;
+ rx_prop[1].attrib.meta_data = in->meta_data;
+ rx_prop[1].attrib.meta_data_mask = in->meta_data_mask;
+ }
+
+ if (ipa_register_intf(in->netdev_name, &tx, &rx)) {
+ IPA_WDI3_ERR("fail to add interface prop\n");
+ ret = -EFAULT;
+ goto fail_commit_hdr;
+ }
+
+ list_add(&new_intf->link, &ipa_wdi3_ctx->head_intf_list);
+ init_completion(&ipa_wdi3_ctx->wdi3_completion);
+
+ kfree(hdr);
+ mutex_unlock(&ipa_wdi3_ctx->lock);
+ return 0;
+
+fail_commit_hdr:
+ kfree(hdr);
+fail_alloc_hdr:
+ kfree(new_intf);
+ mutex_unlock(&ipa_wdi3_ctx->lock);
+ return ret;
+}
+EXPORT_SYMBOL(ipa_wdi3_reg_intf);
+
+int ipa_wdi3_dereg_intf(const char *netdev_name)
+{
+ int len, ret = 0;
+ struct ipa_ioc_del_hdr *hdr = NULL;
+ struct ipa_wdi3_intf_info *entry;
+ struct ipa_wdi3_intf_info *next;
+
+ if (!netdev_name) {
+ IPA_WDI3_ERR("no netdev name.\n");
+ return -EINVAL;
+ }
+
+ if (!ipa_wdi3_ctx) {
+ IPA_WDI3_ERR("wdi3 ctx is not initialized.\n");
+ return -EPERM;
+ }
+
+ mutex_lock(&ipa_wdi3_ctx->lock);
+ list_for_each_entry_safe(entry, next, &ipa_wdi3_ctx->head_intf_list,
+ link)
+ if (strcmp(entry->netdev_name, netdev_name) == 0) {
+ len = sizeof(struct ipa_ioc_del_hdr) +
+ 2 * sizeof(struct ipa_hdr_del);
+ hdr = kzalloc(len, GFP_KERNEL);
+ if (hdr == NULL) {
+ IPA_WDI3_ERR("fail to alloc %d bytes\n", len);
+ mutex_unlock(&ipa_wdi3_ctx->lock);
+ return -ENOMEM;
+ }
+
+ hdr->commit = 1;
+ hdr->num_hdls = 2;
+ hdr->hdl[0].hdl = entry->partial_hdr_hdl[0];
+ hdr->hdl[1].hdl = entry->partial_hdr_hdl[1];
+ IPA_WDI3_DBG("IPv4 hdr hdl: %d IPv6 hdr hdl: %d\n",
+ hdr->hdl[0].hdl, hdr->hdl[1].hdl);
+
+ if (ipa_del_hdr(hdr)) {
+ IPA_WDI3_ERR("fail to delete partial header\n");
+ ret = -EFAULT;
+ goto fail;
+ }
+
+ if (ipa_deregister_intf(entry->netdev_name)) {
+ IPA_WDI3_ERR("fail to del interface props\n");
+ ret = -EFAULT;
+ goto fail;
+ }
+ list_del(&entry->link);
+ kfree(entry);
+
+ break;
+ }
+
+fail:
+ kfree(hdr);
+ mutex_unlock(&ipa_wdi3_ctx->lock);
+ return ret;
+}
+EXPORT_SYMBOL(ipa_wdi3_dereg_intf);
+
+static void ipa_wdi3_rm_notify(void *user_data, enum ipa_rm_event event,
+ unsigned long data)
+{
+ if (!ipa_wdi3_ctx) {
+ IPA_WDI3_ERR("Invalid context\n");
+ return;
+ }
+
+ switch (event) {
+ case IPA_RM_RESOURCE_GRANTED:
+ complete_all(&ipa_wdi3_ctx->wdi3_completion);
+ break;
+
+ case IPA_RM_RESOURCE_RELEASED:
+ break;
+
+ default:
+ IPA_WDI3_ERR("Invalid RM Evt: %d", event);
+ break;
+ }
+}
+
+static int ipa_wdi3_cons_release(void)
+{
+ return 0;
+}
+
+static int ipa_wdi3_cons_request(void)
+{
+ int ret = 0;
+
+ if (!ipa_wdi3_ctx) {
+ IPA_WDI3_ERR("wdi3 ctx is not initialized\n");
+ ret = -EFAULT;
+ }
+
+ return ret;
+}
+
+int ipa_wdi3_conn_pipes(struct ipa_wdi3_conn_in_params *in,
+ struct ipa_wdi3_conn_out_params *out)
+{
+ int ret = 0;
+ struct ipa_rm_create_params param;
+
+ if (!(in && out)) {
+ IPA_WDI3_ERR("empty parameters. in=%pK out=%pK\n", in, out);
+ return -EINVAL;
+ }
+
+ if (!ipa_wdi3_ctx) {
+ ipa_wdi3_ctx = kzalloc(sizeof(*ipa_wdi3_ctx), GFP_KERNEL);
+ if (ipa_wdi3_ctx == NULL) {
+ IPA_WDI3_ERR("fail to alloc wdi3 ctx\n");
+ return -EFAULT;
+ }
+ mutex_init(&ipa_wdi3_ctx->lock);
+ INIT_LIST_HEAD(&ipa_wdi3_ctx->head_intf_list);
+ }
+ ipa_wdi3_ctx->notify = in->notify;
+ ipa_wdi3_ctx->priv = in->priv;
+
+ memset(¶m, 0, sizeof(param));
+ param.name = IPA_RM_RESOURCE_WLAN_PROD;
+ param.reg_params.user_data = ipa_wdi3_ctx;
+ param.reg_params.notify_cb = ipa_wdi3_rm_notify;
+ param.floor_voltage = IPA_VOLTAGE_SVS;
+ ret = ipa_rm_create_resource(¶m);
+ if (ret) {
+ IPA_WDI3_ERR("fail to create WLAN_PROD resource\n");
+ return -EFAULT;
+ }
+
+ memset(¶m, 0, sizeof(param));
+ param.name = IPA_RM_RESOURCE_WLAN_CONS;
+ param.request_resource = ipa_wdi3_cons_request;
+ param.release_resource = ipa_wdi3_cons_release;
+ ret = ipa_rm_create_resource(¶m);
+ if (ret) {
+ IPA_WDI3_ERR("fail to create WLAN_CONS resource\n");
+ goto fail_create_rm_cons;
+ }
+
+ if (ipa_rm_add_dependency(IPA_RM_RESOURCE_WLAN_PROD,
+ IPA_RM_RESOURCE_APPS_CONS)) {
+ IPA_WDI3_ERR("fail to add rm dependency\n");
+ ret = -EFAULT;
+ goto fail;
+ }
+
+ if (ipa_conn_wdi3_pipes(in, out)) {
+ IPA_WDI3_ERR("fail to setup wdi3 pipes\n");
+ ret = -EFAULT;
+ goto fail;
+ }
+
+ return 0;
+
+fail:
+ ipa_rm_delete_resource(IPA_RM_RESOURCE_WLAN_CONS);
+fail_create_rm_cons:
+ ipa_rm_delete_resource(IPA_RM_RESOURCE_WLAN_PROD);
+
+ return ret;
+}
+EXPORT_SYMBOL(ipa_wdi3_conn_pipes);
+
+int ipa_wdi3_disconn_pipes(void)
+{
+ int ipa_ep_idx_rx, ipa_ep_idx_tx;
+
+ if (!ipa_wdi3_ctx) {
+ IPA_WDI3_ERR("wdi3 ctx is not initialized\n");
+ return -EPERM;
+ }
+
+ ipa_ep_idx_rx = ipa_get_ep_mapping(IPA_CLIENT_WLAN1_PROD);
+ ipa_ep_idx_tx = ipa_get_ep_mapping(IPA_CLIENT_WLAN1_CONS);
+ if (ipa_disconn_wdi3_pipes(ipa_ep_idx_rx, ipa_ep_idx_tx)) {
+ IPA_WDI3_ERR("fail to tear down wdi3 pipes\n");
+ return -EFAULT;
+ }
+
+ if (ipa_rm_delete_dependency(IPA_RM_RESOURCE_WLAN_PROD,
+ IPA_RM_RESOURCE_APPS_CONS)) {
+ IPA_WDI3_ERR("fail to delete rm dependency\n");
+ return -EFAULT;
+ }
+
+ if (ipa_rm_delete_resource(IPA_RM_RESOURCE_WLAN_PROD)) {
+ IPA_WDI3_ERR("fail to delete WLAN_PROD resource\n");
+ return -EFAULT;
+ }
+
+ if (ipa_rm_delete_resource(IPA_RM_RESOURCE_WLAN_CONS)) {
+ IPA_WDI3_ERR("fail to delete WLAN_CONS resource\n");
+ return -EFAULT;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL(ipa_wdi3_disconn_pipes);
+
+int ipa_wdi3_enable_pipes(void)
+{
+ int ret;
+ int ipa_ep_idx_tx, ipa_ep_idx_rx;
+
+ if (!ipa_wdi3_ctx) {
+ IPA_WDI3_ERR("wdi3 ctx is not initialized.\n");
+ return -EPERM;
+ }
+
+ ipa_ep_idx_rx = ipa_get_ep_mapping(IPA_CLIENT_WLAN1_PROD);
+ ipa_ep_idx_tx = ipa_get_ep_mapping(IPA_CLIENT_WLAN1_CONS);
+ if (ipa_enable_wdi3_pipes(ipa_ep_idx_tx, ipa_ep_idx_rx)) {
+ IPA_WDI3_ERR("fail to enable wdi3 pipes\n");
+ return -EFAULT;
+ }
+
+ ret = ipa_rm_request_resource(IPA_RM_RESOURCE_WLAN_PROD);
+ if (ret == -EINPROGRESS) {
+ if (wait_for_completion_timeout(&ipa_wdi3_ctx->wdi3_completion,
+ 10*HZ) == 0) {
+ IPA_WDI3_ERR("WLAN_PROD resource req time out\n");
+ return -EFAULT;
+ }
+ } else if (ret != 0) {
+ IPA_WDI3_ERR("fail to request resource\n");
+ return -EFAULT;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL(ipa_wdi3_enable_pipes);
+
+int ipa_wdi3_disable_pipes(void)
+{
+ int ret;
+ int ipa_ep_idx_tx, ipa_ep_idx_rx;
+
+ if (!ipa_wdi3_ctx) {
+ IPA_WDI3_ERR("wdi3 ctx is not initialized.\n");
+ return -EPERM;
+ }
+
+ ret = ipa_rm_release_resource(IPA_RM_RESOURCE_WLAN_PROD);
+ if (ret != 0) {
+ IPA_WDI3_ERR("fail to release resource\n");
+ return -EFAULT;
+ }
+
+ ipa_ep_idx_rx = ipa_get_ep_mapping(IPA_CLIENT_WLAN1_PROD);
+ ipa_ep_idx_tx = ipa_get_ep_mapping(IPA_CLIENT_WLAN1_CONS);
+ if (ipa_disable_wdi3_pipes(ipa_ep_idx_tx, ipa_ep_idx_rx)) {
+ IPA_WDI3_ERR("fail to disable wdi3 pipes\n");
+ return -EFAULT;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL(ipa_wdi3_disable_pipes);
+
+int ipa_wdi3_set_perf_profile(struct ipa_wdi3_perf_profile *profile)
+{
+ struct ipa_rm_perf_profile rm_profile;
+ enum ipa_rm_resource_name resource_name;
+
+ if (profile == NULL) {
+ IPA_WDI3_ERR("Invalid input\n");
+ return -EINVAL;
+ }
+
+ rm_profile.max_supported_bandwidth_mbps =
+ profile->max_supported_bw_mbps;
+
+ if (profile->client == IPA_CLIENT_WLAN1_PROD) {
+ resource_name = IPA_RM_RESOURCE_WLAN_PROD;
+ } else if (profile->client == IPA_CLIENT_WLAN1_CONS) {
+ resource_name = IPA_RM_RESOURCE_WLAN_CONS;
+ } else {
+ IPA_WDI3_ERR("not supported\n");
+ return -EINVAL;
+ }
+
+ if (ipa_rm_set_perf_profile(resource_name, &rm_profile)) {
+ IPA_WDI3_ERR("fail to setup rm perf profile\n");
+ return -EFAULT;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL(ipa_wdi3_set_perf_profile);
diff --git a/drivers/platform/msm/ipa/ipa_clients/rndis_ipa.c b/drivers/platform/msm/ipa/ipa_clients/rndis_ipa.c
index 1c47e69..2e87bd2 100644
--- a/drivers/platform/msm/ipa/ipa_clients/rndis_ipa.c
+++ b/drivers/platform/msm/ipa/ipa_clients/rndis_ipa.c
@@ -2490,9 +2490,13 @@
(struct file *file,
const char __user *buf, size_t count, loff_t *ppos)
{
- struct rndis_ipa_dev *rndis_ipa_ctx = file->private_data;
+ struct rndis_ipa_dev *rndis_ipa_ctx = NULL;
int result;
+ if (file == NULL)
+ return -EFAULT;
+ rndis_ipa_ctx = file->private_data;
+
result = ipa_cfg_ep(rndis_ipa_ctx->usb_to_ipa_hdl, &ipa_to_usb_ep_cfg);
if (result) {
pr_err("failed to re-configure USB to IPA point\n");
diff --git a/drivers/platform/msm/ipa/ipa_common_i.h b/drivers/platform/msm/ipa/ipa_common_i.h
index a487bf4..fe8cbc0 100644
--- a/drivers/platform/msm/ipa/ipa_common_i.h
+++ b/drivers/platform/msm/ipa/ipa_common_i.h
@@ -18,6 +18,7 @@
#include <linux/ipc_logging.h>
#include <linux/ipa.h>
#include <linux/ipa_uc_offload.h>
+#include <linux/ipa_wdi3.h>
#define __FILENAME__ \
(strrchr(__FILE__, '/') ? strrchr(__FILE__, '/') + 1 : __FILE__)
@@ -383,6 +384,16 @@
int ipa_ntn_uc_reg_rdyCB(void (*ipauc_ready_cb)(void *user_data),
void *user_data);
void ipa_ntn_uc_dereg_rdyCB(void);
+
+int ipa_conn_wdi3_pipes(struct ipa_wdi3_conn_in_params *in,
+ struct ipa_wdi3_conn_out_params *out);
+
+int ipa_disconn_wdi3_pipes(int ipa_ep_idx_tx, int ipa_ep_idx_rx);
+
+int ipa_enable_wdi3_pipes(int ipa_ep_idx_tx, int ipa_ep_idx_rx);
+
+int ipa_disable_wdi3_pipes(int ipa_ep_idx_tx, int ipa_ep_idx_rx);
+
const char *ipa_get_version_string(enum ipa_hw_type ver);
int ipa_start_gsi_channel(u32 clnt_hdl);
diff --git a/drivers/platform/msm/ipa/ipa_v2/Makefile b/drivers/platform/msm/ipa/ipa_v2/Makefile
index 69b8a4c..fb03970 100644
--- a/drivers/platform/msm/ipa/ipa_v2/Makefile
+++ b/drivers/platform/msm/ipa/ipa_v2/Makefile
@@ -1,6 +1,7 @@
obj-$(CONFIG_IPA) += ipat.o
ipat-y := ipa.o ipa_debugfs.o ipa_hdr.o ipa_flt.o ipa_rt.o ipa_dp.o ipa_client.o \
ipa_utils.o ipa_nat.o ipa_intf.o teth_bridge.o ipa_interrupts.o \
- ipa_uc.o ipa_uc_wdi.o ipa_dma.o ipa_uc_mhi.o ipa_mhi.o ipa_uc_ntn.o
+ ipa_uc.o ipa_uc_wdi.o ipa_dma.o ipa_uc_mhi.o ipa_mhi.o ipa_uc_ntn.o \
+ ipa_wdi3_i.o
obj-$(CONFIG_RMNET_IPA) += rmnet_ipa.o ipa_qmi_service_v01.o ipa_qmi_service.o rmnet_ipa_fd_ioctl.o
diff --git a/drivers/platform/msm/ipa/ipa_v2/ipa_flt.c b/drivers/platform/msm/ipa/ipa_v2/ipa_flt.c
index f7b0864..0a079f4 100644
--- a/drivers/platform/msm/ipa/ipa_v2/ipa_flt.c
+++ b/drivers/platform/msm/ipa/ipa_v2/ipa_flt.c
@@ -23,10 +23,10 @@
static int ipa_generate_hw_rule_from_eq(
const struct ipa_ipfltri_rule_eq *attrib, u8 **buf)
{
- int num_offset_meq_32 = attrib->num_offset_meq_32;
- int num_ihl_offset_range_16 = attrib->num_ihl_offset_range_16;
- int num_ihl_offset_meq_32 = attrib->num_ihl_offset_meq_32;
- int num_offset_meq_128 = attrib->num_offset_meq_128;
+ uint8_t num_offset_meq_32 = attrib->num_offset_meq_32;
+ uint8_t num_ihl_offset_range_16 = attrib->num_ihl_offset_range_16;
+ uint8_t num_ihl_offset_meq_32 = attrib->num_ihl_offset_meq_32;
+ uint8_t num_offset_meq_128 = attrib->num_offset_meq_128;
int i;
if (attrib->tos_eq_present) {
@@ -1037,6 +1037,11 @@
goto error;
}
}
+ } else {
+ if (rule->rt_tbl_idx > 0) {
+ IPAERR_RL("invalid RT tbl\n");
+ goto error;
+ }
}
entry = kmem_cache_zalloc(ipa_ctx->flt_rule_cache, GFP_KERNEL);
@@ -1158,6 +1163,11 @@
goto error;
}
}
+ } else {
+ if (frule->rule.rt_tbl_idx > 0) {
+ IPAERR_RL("invalid RT tbl\n");
+ goto error;
+ }
}
entry->rule = frule->rule;
diff --git a/drivers/platform/msm/ipa/ipa_v2/ipa_hdr.c b/drivers/platform/msm/ipa/ipa_v2/ipa_hdr.c
index d4e39d7..2f72d88 100644
--- a/drivers/platform/msm/ipa/ipa_v2/ipa_hdr.c
+++ b/drivers/platform/msm/ipa/ipa_v2/ipa_hdr.c
@@ -1374,6 +1374,7 @@
return -EINVAL;
}
mutex_lock(&ipa_ctx->lock);
+ lookup->name[IPA_RESOURCE_NAME_MAX-1] = '\0';
entry = __ipa_find_hdr(lookup->name);
if (entry) {
lookup->hdl = entry->id;
@@ -1496,6 +1497,7 @@
return -EINVAL;
}
mutex_lock(&ipa_ctx->lock);
+ copy->name[IPA_RESOURCE_NAME_MAX-1] = '\0';
entry = __ipa_find_hdr(copy->name);
if (entry) {
memcpy(copy->hdr, entry->hdr, entry->hdr_len);
diff --git a/drivers/platform/msm/ipa/ipa_v2/ipa_i.h b/drivers/platform/msm/ipa/ipa_v2/ipa_i.h
index 9bfdcdc..67b0be6 100644
--- a/drivers/platform/msm/ipa/ipa_v2/ipa_i.h
+++ b/drivers/platform/msm/ipa/ipa_v2/ipa_i.h
@@ -1546,6 +1546,12 @@
int ipa2_ntn_uc_reg_rdyCB(void (*ipauc_ready_cb)(void *), void *priv);
void ipa2_ntn_uc_dereg_rdyCB(void);
+int ipa2_conn_wdi3_pipes(struct ipa_wdi3_conn_in_params *in,
+ struct ipa_wdi3_conn_out_params *out);
+int ipa2_disconn_wdi3_pipes(int ipa_ep_idx_tx, int ipa_ep_idx_rx);
+int ipa2_enable_wdi3_pipes(int ipa_ep_idx_tx, int ipa_ep_idx_rx);
+int ipa2_disable_wdi3_pipes(int ipa_ep_idx_tx, int ipa_ep_idx_rx);
+
/*
* To retrieve doorbell physical address of
* wlan pipes
diff --git a/drivers/platform/msm/ipa/ipa_v2/ipa_intf.c b/drivers/platform/msm/ipa/ipa_v2/ipa_intf.c
index e6954b7..e6048d1 100644
--- a/drivers/platform/msm/ipa/ipa_v2/ipa_intf.c
+++ b/drivers/platform/msm/ipa/ipa_v2/ipa_intf.c
@@ -234,6 +234,7 @@
}
mutex_lock(&ipa_ctx->lock);
+ lookup->name[IPA_RESOURCE_NAME_MAX-1] = '\0';
list_for_each_entry(entry, &ipa_ctx->intf_list, link) {
if (!strcmp(entry->name, lookup->name)) {
lookup->num_tx_props = entry->num_tx_props;
@@ -269,6 +270,7 @@
}
mutex_lock(&ipa_ctx->lock);
+ tx->name[IPA_RESOURCE_NAME_MAX-1] = '\0';
list_for_each_entry(entry, &ipa_ctx->intf_list, link) {
if (!strcmp(entry->name, tx->name)) {
/* add the entry check */
@@ -310,6 +312,7 @@
}
mutex_lock(&ipa_ctx->lock);
+ rx->name[IPA_RESOURCE_NAME_MAX-1] = '\0';
list_for_each_entry(entry, &ipa_ctx->intf_list, link) {
if (!strcmp(entry->name, rx->name)) {
/* add the entry check */
diff --git a/drivers/platform/msm/ipa/ipa_v2/ipa_qmi_service.c b/drivers/platform/msm/ipa/ipa_v2/ipa_qmi_service.c
index 57e0a53..825c538 100644
--- a/drivers/platform/msm/ipa/ipa_v2/ipa_qmi_service.c
+++ b/drivers/platform/msm/ipa/ipa_v2/ipa_qmi_service.c
@@ -512,6 +512,7 @@
struct ipa_install_fltr_rule_resp_msg_v01 resp;
struct msg_desc req_desc, resp_desc;
int rc;
+ int i;
/* check if the filter rules from IPACM is valid */
if (req->filter_spec_list_len == 0) {
@@ -521,6 +522,38 @@
req->filter_spec_list_len);
}
+ if (req->filter_spec_list_len >= QMI_IPA_MAX_FILTERS_V01) {
+ IPAWANDBG(
+ "IPACM passes the number of filtering rules exceed limit\n");
+ return -EINVAL;
+ } else if (req->source_pipe_index_valid != 0) {
+ IPAWANDBG(
+ "IPACM passes source_pipe_index_valid not zero 0 != %d\n",
+ req->source_pipe_index_valid);
+ return -EINVAL;
+ } else if (req->source_pipe_index >= ipa_ctx->ipa_num_pipes) {
+ IPAWANDBG(
+ "IPACM passes source pipe index not valid ID = %d\n",
+ req->source_pipe_index);
+ return -EINVAL;
+ }
+ for (i = 0; i < req->filter_spec_list_len; i++) {
+ if ((req->filter_spec_list[i].ip_type !=
+ QMI_IPA_IP_TYPE_V4_V01) &&
+ (req->filter_spec_list[i].ip_type !=
+ QMI_IPA_IP_TYPE_V6_V01))
+ return -EINVAL;
+ if (req->filter_spec_list[i].is_mux_id_valid == false)
+ return -EINVAL;
+ if (req->filter_spec_list[i].is_routing_table_index_valid
+ == false)
+ return -EINVAL;
+ if ((req->filter_spec_list[i].filter_action <=
+ QMI_IPA_FILTER_ACTION_INVALID_V01) &&
+ (req->filter_spec_list[i].filter_action >
+ QMI_IPA_FILTER_ACTION_EXCEPTION_V01))
+ return -EINVAL;
+ }
mutex_lock(&ipa_qmi_lock);
if (ipa_qmi_ctx != NULL) {
/* cache the qmi_filter_request */
@@ -655,9 +688,8 @@
/* check if the filter rules from IPACM is valid */
if (req->filter_index_list_len == 0) {
- IPAWANERR(" delete UL filter rule for pipe %d\n",
+ IPAWANDBG(" delete UL filter rule for pipe %d\n",
req->source_pipe_index);
- return -EINVAL;
} else if (req->filter_index_list_len > QMI_IPA_MAX_FILTERS_V01) {
IPAWANERR(" UL filter rule for pipe %d exceed max (%u)\n",
req->source_pipe_index,
@@ -674,6 +706,25 @@
req->filter_index_list[i].filter_handle,
req->filter_index_list[i].filter_index);
return -EINVAL;
+ } else if (req->install_status != IPA_QMI_RESULT_SUCCESS_V01) {
+ IPAWANERR(" UL filter rule for pipe %d install_status = %d\n",
+ req->source_pipe_index, req->install_status);
+ return -EINVAL;
+ } else if (req->source_pipe_index >= ipa_ctx->ipa_num_pipes) {
+ IPAWANERR("IPACM passes source pipe index not valid ID = %d\n",
+ req->source_pipe_index);
+ return -EINVAL;
+ } else if (((req->embedded_pipe_index_valid != true) ||
+ (req->embedded_call_mux_id_valid != true)) &&
+ ((req->embedded_pipe_index_valid != false) ||
+ (req->embedded_call_mux_id_valid != false))) {
+ IPAWANERR(
+ "IPACM passes embedded pipe and mux valid not valid\n");
+ return -EINVAL;
+ } else if (req->embedded_pipe_index >= ipa_ctx->ipa_num_pipes) {
+ IPAWANERR("IPACM passes source pipe index not valid ID = %d\n",
+ req->source_pipe_index);
+ return -EINVAL;
}
mutex_lock(&ipa_qmi_lock);
diff --git a/drivers/platform/msm/ipa/ipa_v2/ipa_rt.c b/drivers/platform/msm/ipa/ipa_v2/ipa_rt.c
index bada5cc..321cc89 100644
--- a/drivers/platform/msm/ipa/ipa_v2/ipa_rt.c
+++ b/drivers/platform/msm/ipa/ipa_v2/ipa_rt.c
@@ -863,6 +863,7 @@
mutex_lock(&ipa_ctx->lock);
/* check if this table exists */
+ in->name[IPA_RESOURCE_NAME_MAX-1] = '\0';
entry = __ipa_find_rt_tbl(in->ip, in->name);
if (!entry) {
mutex_unlock(&ipa_ctx->lock);
@@ -1099,6 +1100,7 @@
mutex_lock(&ipa_ctx->lock);
for (i = 0; i < rules->num_rules; i++) {
+ rules->rt_tbl_name[IPA_RESOURCE_NAME_MAX-1] = '\0';
if (__ipa_add_rt_rule(rules->ip, rules->rt_tbl_name,
&rules->rules[i].rule,
rules->rules[i].at_rear,
@@ -1368,6 +1370,7 @@
return -EINVAL;
}
mutex_lock(&ipa_ctx->lock);
+ lookup->name[IPA_RESOURCE_NAME_MAX-1] = '\0';
entry = __ipa_find_rt_tbl(lookup->ip, lookup->name);
if (entry && entry->cookie == IPA_RT_TBL_COOKIE) {
if (entry->ref_cnt == U32_MAX) {
diff --git a/drivers/platform/msm/ipa/ipa_v2/ipa_uc_offload_i.h b/drivers/platform/msm/ipa/ipa_v2/ipa_uc_offload_i.h
index a98d602..afe6368 100644
--- a/drivers/platform/msm/ipa/ipa_v2/ipa_uc_offload_i.h
+++ b/drivers/platform/msm/ipa/ipa_v2/ipa_uc_offload_i.h
@@ -26,6 +26,9 @@
#define IPA_NTN_TX_DIR 1
#define IPA_NTN_RX_DIR 2
+#define IPA_WDI3_TX_DIR 1
+#define IPA_WDI3_RX_DIR 2
+
/**
* @brief Enum value determined based on the feature it
* corresponds to
@@ -45,16 +48,20 @@
* enum ipa_hw_features - Values that represent the features supported in IPA HW
* @IPA_HW_FEATURE_COMMON : Feature related to common operation of IPA HW
* @IPA_HW_FEATURE_MHI : Feature related to MHI operation in IPA HW
+ * @IPA_HW_FEATURE_POWER_COLLAPSE: Feature related to IPA Power collapse
* @IPA_HW_FEATURE_WDI : Feature related to WDI operation in IPA HW
* @IPA_HW_FEATURE_NTN : Feature related to NTN operation in IPA HW
* @IPA_HW_FEATURE_OFFLOAD : Feature related to NTN operation in IPA HW
+ * @IPA_HW_FEATURE_WDI3 : Feature related to WDI operation in IPA HW
*/
enum ipa_hw_features {
IPA_HW_FEATURE_COMMON = 0x0,
IPA_HW_FEATURE_MHI = 0x1,
+ IPA_HW_FEATURE_POWER_COLLAPSE = 0x2,
IPA_HW_FEATURE_WDI = 0x3,
IPA_HW_FEATURE_NTN = 0x4,
IPA_HW_FEATURE_OFFLOAD = 0x5,
+ IPA_HW_FEATURE_WDI3 = 0x6,
IPA_HW_FEATURE_MAX = IPA_HW_NUM_FEATURES
};
@@ -277,6 +284,33 @@
} __packed;
+struct IpaHwWdi3SetUpCmdData_t {
+ u32 transfer_ring_base_pa;
+ u32 transfer_ring_base_pa_hi;
+
+ u32 transfer_ring_size;
+
+ u32 transfer_ring_doorbell_pa;
+ u32 transfer_ring_doorbell_pa_hi;
+
+ u32 event_ring_base_pa;
+ u32 event_ring_base_pa_hi;
+
+ u32 event_ring_size;
+
+ u32 event_ring_doorbell_pa;
+ u32 event_ring_doorbell_pa_hi;
+
+ u16 num_pkt_buffers;
+ u8 ipa_pipe_number;
+ u8 dir;
+
+ u16 pkt_offset;
+ u16 reserved0;
+
+ u32 desc_format_template[IPA_HW_WDI3_MAX_ER_DESC_SIZE];
+} __packed;
+
/**
* struct IpaHwNtnCommonChCmdData_t - Structure holding the
* parameters for Ntn Tear down command data params
@@ -291,6 +325,13 @@
uint32_t raw32b;
} __packed;
+union IpaHwWdi3CommonChCmdData_t {
+ struct IpaHwWdi3CommonChCmdParams_t {
+ u32 ipa_pipe_number :8;
+ u32 reserved :24;
+ } __packed params;
+ u32 raw32b;
+} __packed;
/**
* struct IpaHwNTNErrorEventData_t - Structure holding the
@@ -408,13 +449,30 @@
* the offload commands from CPU
* @IPA_CPU_2_HW_CMD_OFFLOAD_CHANNEL_SET_UP : Command to set up
* Offload protocol's Tx/Rx Path
- * @IPA_CPU_2_HW_CMD_OFFLOAD_RX_SET_UP : Command to tear down
+ * @IPA_CPU_2_HW_CMD_OFFLOAD_TEAR_DOWN : Command to tear down
+ * Offload protocol's Tx/ Rx Path
+ * @IPA_CPU_2_HW_CMD_OFFLOAD_ENABLE : Command to enable
+ * Offload protocol's Tx/Rx Path
+ * @IPA_CPU_2_HW_CMD_OFFLOAD_DISABLE : Command to disable
+ * Offload protocol's Tx/ Rx Path
+ * @IPA_CPU_2_HW_CMD_OFFLOAD_SUSPEND : Command to suspend
+ * Offload protocol's Tx/Rx Path
+ * @IPA_CPU_2_HW_CMD_OFFLOAD_RESUME : Command to resume
* Offload protocol's Tx/ Rx Path
*/
enum ipa_cpu_2_hw_offload_commands {
IPA_CPU_2_HW_CMD_OFFLOAD_CHANNEL_SET_UP =
FEATURE_ENUM_VAL(IPA_HW_FEATURE_OFFLOAD, 1),
- IPA_CPU_2_HW_CMD_OFFLOAD_TEAR_DOWN,
+ IPA_CPU_2_HW_CMD_OFFLOAD_TEAR_DOWN =
+ FEATURE_ENUM_VAL(IPA_HW_FEATURE_OFFLOAD, 2),
+ IPA_CPU_2_HW_CMD_OFFLOAD_ENABLE =
+ FEATURE_ENUM_VAL(IPA_HW_FEATURE_OFFLOAD, 3),
+ IPA_CPU_2_HW_CMD_OFFLOAD_DISABLE =
+ FEATURE_ENUM_VAL(IPA_HW_FEATURE_OFFLOAD, 4),
+ IPA_CPU_2_HW_CMD_OFFLOAD_SUSPEND =
+ FEATURE_ENUM_VAL(IPA_HW_FEATURE_OFFLOAD, 5),
+ IPA_CPU_2_HW_CMD_OFFLOAD_RESUME =
+ FEATURE_ENUM_VAL(IPA_HW_FEATURE_OFFLOAD, 6),
};
@@ -525,6 +583,7 @@
*/
union IpaHwSetUpCmd {
struct IpaHwNtnSetUpCmdData_t NtnSetupCh_params;
+ struct IpaHwWdi3SetUpCmdData_t Wdi3SetupCh_params;
} __packed;
/**
@@ -545,6 +604,7 @@
*/
union IpaHwCommonChCmd {
union IpaHwNtnCommonChCmdData_t NtnCommonCh_params;
+ union IpaHwWdi3CommonChCmdData_t Wdi3CommonCh_params;
} __packed;
struct IpaHwOffloadCommonChCmdData_t {
diff --git a/drivers/platform/msm/ipa/ipa_v2/ipa_utils.c b/drivers/platform/msm/ipa/ipa_v2/ipa_utils.c
index 2c88244..210ddfe 100644
--- a/drivers/platform/msm/ipa/ipa_v2/ipa_utils.c
+++ b/drivers/platform/msm/ipa/ipa_v2/ipa_utils.c
@@ -5088,6 +5088,10 @@
api_ctrl->ipa_get_pdev = ipa2_get_pdev;
api_ctrl->ipa_ntn_uc_reg_rdyCB = ipa2_ntn_uc_reg_rdyCB;
api_ctrl->ipa_ntn_uc_dereg_rdyCB = ipa2_ntn_uc_dereg_rdyCB;
+ api_ctrl->ipa_conn_wdi3_pipes = ipa2_conn_wdi3_pipes;
+ api_ctrl->ipa_disconn_wdi3_pipes = ipa2_disconn_wdi3_pipes;
+ api_ctrl->ipa_enable_wdi3_pipes = ipa2_enable_wdi3_pipes;
+ api_ctrl->ipa_disable_wdi3_pipes = ipa2_disable_wdi3_pipes;
return 0;
}
diff --git a/drivers/platform/msm/ipa/ipa_v2/ipa_wdi3_i.c b/drivers/platform/msm/ipa/ipa_v2/ipa_wdi3_i.c
new file mode 100644
index 0000000..a2c33a1
--- /dev/null
+++ b/drivers/platform/msm/ipa/ipa_v2/ipa_wdi3_i.c
@@ -0,0 +1,406 @@
+/* Copyright (c) 2017, 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 "ipa_i.h"
+#include "ipa_uc_offload_i.h"
+#include <linux/ipa_wdi3.h>
+
+#define IPA_HW_WDI3_RX_MBOX_START_INDEX 48
+#define IPA_HW_WDI3_TX_MBOX_START_INDEX 50
+
+static int ipa_send_wdi3_setup_pipe_cmd(
+ struct ipa_wdi3_setup_info *info, u8 dir)
+{
+ int ipa_ep_idx;
+ int result = 0;
+ struct ipa_mem_buffer cmd;
+ struct IpaHwWdi3SetUpCmdData_t *wdi3_params;
+ struct IpaHwOffloadSetUpCmdData_t *cmd_data;
+
+ if (info == NULL) {
+ IPAERR("invalid input\n");
+ return -EINVAL;
+ }
+
+ ipa_ep_idx = ipa_get_ep_mapping(info->client);
+ IPAERR("ep number: %d\n", ipa_ep_idx);
+ if (ipa_ep_idx == -1) {
+ IPAERR("fail to get ep idx.\n");
+ return -EFAULT;
+ }
+
+ IPAERR("client=%d ep=%d\n", info->client, ipa_ep_idx);
+ IPAERR("ring_base_pa = 0x%pad\n", &info->transfer_ring_base_pa);
+ IPAERR("ring_size = %hu\n", info->transfer_ring_size);
+ IPAERR("ring_db_pa = 0x%pad\n", &info->transfer_ring_doorbell_pa);
+ IPAERR("evt_ring_base_pa = 0x%pad\n", &info->event_ring_base_pa);
+ IPAERR("evt_ring_size = %hu\n", info->event_ring_size);
+ IPAERR("evt_ring_db_pa = 0x%pad\n", &info->event_ring_doorbell_pa);
+ IPAERR("num_pkt_buffers = %hu\n", info->num_pkt_buffers);
+ IPAERR("pkt_offset = %d.\n", info->pkt_offset);
+
+ cmd.size = sizeof(*cmd_data);
+ cmd.base = dma_alloc_coherent(ipa_ctx->uc_pdev, cmd.size,
+ &cmd.phys_base, GFP_KERNEL);
+ if (cmd.base == NULL) {
+ IPAERR("fail to get DMA memory.\n");
+ return -ENOMEM;
+ }
+ IPAERR("suceeded in allocating memory.\n");
+
+ cmd_data = (struct IpaHwOffloadSetUpCmdData_t *)cmd.base;
+ cmd_data->protocol = IPA_HW_FEATURE_WDI3;
+
+ wdi3_params = &cmd_data->SetupCh_params.Wdi3SetupCh_params;
+ wdi3_params->transfer_ring_base_pa = (u32)info->transfer_ring_base_pa;
+ wdi3_params->transfer_ring_base_pa_hi =
+ (u32)((u64)info->transfer_ring_base_pa >> 32);
+ wdi3_params->transfer_ring_size = info->transfer_ring_size;
+ wdi3_params->transfer_ring_doorbell_pa =
+ (u32)info->transfer_ring_doorbell_pa;
+ wdi3_params->transfer_ring_doorbell_pa_hi =
+ (u32)((u64)info->transfer_ring_doorbell_pa >> 32);
+ wdi3_params->event_ring_base_pa = (u32)info->event_ring_base_pa;
+ wdi3_params->event_ring_base_pa_hi =
+ (u32)((u64)info->event_ring_base_pa >> 32);
+ wdi3_params->event_ring_size = info->event_ring_size;
+ wdi3_params->event_ring_doorbell_pa =
+ (u32)info->event_ring_doorbell_pa;
+ wdi3_params->event_ring_doorbell_pa_hi =
+ (u32)((u64)info->event_ring_doorbell_pa >> 32);
+ wdi3_params->num_pkt_buffers = info->num_pkt_buffers;
+ wdi3_params->ipa_pipe_number = ipa_ep_idx;
+ wdi3_params->dir = dir;
+ wdi3_params->pkt_offset = info->pkt_offset;
+ memcpy(wdi3_params->desc_format_template, info->desc_format_template,
+ sizeof(wdi3_params->desc_format_template));
+ IPAERR("suceeded in populating the command memory.\n");
+
+ result = ipa_uc_send_cmd((u32)(cmd.phys_base),
+ IPA_CPU_2_HW_CMD_OFFLOAD_CHANNEL_SET_UP,
+ IPA_HW_2_CPU_OFFLOAD_CMD_STATUS_SUCCESS,
+ false, 10*HZ);
+ if (result) {
+ IPAERR("uc setup channel cmd failed: %d\n", result);
+ result = -EFAULT;
+ }
+
+ dma_free_coherent(ipa_ctx->uc_pdev, cmd.size, cmd.base, cmd.phys_base);
+ IPAERR("suceeded in freeing memory.\n");
+ return result;
+}
+
+int ipa2_conn_wdi3_pipes(struct ipa_wdi3_conn_in_params *in,
+ struct ipa_wdi3_conn_out_params *out)
+{
+ struct ipa_ep_context *ep_rx;
+ struct ipa_ep_context *ep_tx;
+ int ipa_ep_idx_rx;
+ int ipa_ep_idx_tx;
+ int result = 0;
+
+ if (in == NULL || out == NULL) {
+ IPAERR("invalid input\n");
+ return -EINVAL;
+ }
+
+ ipa_ep_idx_rx = ipa_get_ep_mapping(in->rx.client);
+ ipa_ep_idx_tx = ipa_get_ep_mapping(in->tx.client);
+ if (ipa_ep_idx_rx == -1 || ipa_ep_idx_tx == -1) {
+ IPAERR("fail to alloc EP.\n");
+ return -EFAULT;
+ }
+
+ ep_rx = &ipa_ctx->ep[ipa_ep_idx_rx];
+ ep_tx = &ipa_ctx->ep[ipa_ep_idx_tx];
+
+ if (ep_rx->valid || ep_tx->valid) {
+ IPAERR("EP already allocated.\n");
+ return -EFAULT;
+ }
+
+ memset(ep_rx, 0, offsetof(struct ipa_ep_context, sys));
+ memset(ep_tx, 0, offsetof(struct ipa_ep_context, sys));
+
+ IPA_ACTIVE_CLIENTS_INC_SIMPLE();
+
+ /* setup rx ep cfg */
+ ep_rx->valid = 1;
+ ep_rx->client = in->rx.client;
+ result = ipa_disable_data_path(ipa_ep_idx_rx);
+ if (result) {
+ IPAERR("disable data path failed res=%d clnt=%d.\n", result,
+ ipa_ep_idx_rx);
+ IPA_ACTIVE_CLIENTS_DEC_SIMPLE();
+ return -EFAULT;
+ }
+ ep_rx->client_notify = in->notify;
+ ep_rx->priv = in->priv;
+
+ memcpy(&ep_rx->cfg, &in->rx.ipa_ep_cfg, sizeof(ep_rx->cfg));
+
+ if (ipa_cfg_ep(ipa_ep_idx_rx, &ep_rx->cfg)) {
+ IPAERR("fail to setup rx pipe cfg\n");
+ result = -EFAULT;
+ goto fail;
+ }
+ IPAERR("configured RX EP.\n");
+
+ if (ipa_send_wdi3_setup_pipe_cmd(&in->rx, IPA_WDI3_RX_DIR)) {
+ IPAERR("fail to send cmd to uc for rx pipe\n");
+ result = -EFAULT;
+ goto fail;
+ }
+ IPAERR("rx pipe was setup.\n");
+
+ ipa_install_dflt_flt_rules(ipa_ep_idx_rx);
+ out->rx_uc_db_pa = ipa_ctx->ipa_wrapper_base +
+ IPA_REG_BASE_OFST_v2_5 +
+ IPA_UC_MAILBOX_m_n_OFFS_v2_5(
+ IPA_HW_WDI3_RX_MBOX_START_INDEX/32,
+ IPA_HW_WDI3_RX_MBOX_START_INDEX % 32);
+ IPADBG("client %d (ep: %d) connected\n", in->rx.client,
+ ipa_ep_idx_rx);
+
+ /* setup dl ep cfg */
+ ep_tx->valid = 1;
+ ep_tx->client = in->tx.client;
+ result = ipa_disable_data_path(ipa_ep_idx_tx);
+ if (result) {
+ IPAERR("disable data path failed res=%d clnt=%d.\n", result,
+ ipa_ep_idx_tx);
+ result = -EFAULT;
+ goto fail;
+ }
+
+ memcpy(&ep_tx->cfg, &in->tx.ipa_ep_cfg, sizeof(ep_tx->cfg));
+
+ if (ipa_cfg_ep(ipa_ep_idx_tx, &ep_tx->cfg)) {
+ IPAERR("fail to setup tx pipe cfg\n");
+ result = -EFAULT;
+ goto fail;
+ }
+ IPAERR("configured TX EP in DMA mode.\n");
+
+ if (ipa_send_wdi3_setup_pipe_cmd(&in->tx, IPA_WDI3_TX_DIR)) {
+ IPAERR("fail to send cmd to uc for tx pipe\n");
+ result = -EFAULT;
+ goto fail;
+ }
+ IPAERR("tx pipe was setup.\n");
+
+ out->tx_uc_db_pa = ipa_ctx->ipa_wrapper_base +
+ IPA_REG_BASE_OFST_v2_5 +
+ IPA_UC_MAILBOX_m_n_OFFS_v2_5(
+ IPA_HW_WDI3_TX_MBOX_START_INDEX/32,
+ IPA_HW_WDI3_TX_MBOX_START_INDEX % 32);
+ out->tx_uc_db_va = ioremap(out->tx_uc_db_pa, 4);
+ IPADBG("client %d (ep: %d) connected\n", in->tx.client,
+ ipa_ep_idx_tx);
+
+fail:
+ IPA_ACTIVE_CLIENTS_DEC_SIMPLE();
+ return result;
+}
+
+static int ipa_send_wdi3_common_ch_cmd(int ipa_ep_idx, int command)
+{
+ struct ipa_mem_buffer cmd;
+ struct IpaHwOffloadCommonChCmdData_t *cmd_data;
+ union IpaHwWdi3CommonChCmdData_t *wdi3;
+ int result = 0;
+
+ cmd.size = sizeof(*cmd_data);
+ cmd.base = dma_alloc_coherent(ipa_ctx->uc_pdev, cmd.size,
+ &cmd.phys_base, GFP_KERNEL);
+ if (cmd.base == NULL) {
+ IPAERR("fail to get DMA memory.\n");
+ return -ENOMEM;
+ }
+
+ IPA_ACTIVE_CLIENTS_INC_SIMPLE();
+ /* enable the TX pipe */
+ cmd_data = (struct IpaHwOffloadCommonChCmdData_t *)cmd.base;
+ cmd_data->protocol = IPA_HW_FEATURE_WDI3;
+
+ wdi3 = &cmd_data->CommonCh_params.Wdi3CommonCh_params;
+ wdi3->params.ipa_pipe_number = ipa_ep_idx;
+ IPAERR("cmd: %d ep_idx: %d\n", command, ipa_ep_idx);
+ result = ipa_uc_send_cmd((u32)(cmd.phys_base), command,
+ IPA_HW_2_CPU_OFFLOAD_CMD_STATUS_SUCCESS,
+ false, 10*HZ);
+ if (result) {
+ result = -EFAULT;
+ goto fail;
+ }
+
+fail:
+ dma_free_coherent(ipa_ctx->uc_pdev, cmd.size, cmd.base, cmd.phys_base);
+ IPA_ACTIVE_CLIENTS_DEC_SIMPLE();
+ return result;
+}
+
+int ipa2_disconn_wdi3_pipes(int ipa_ep_idx_tx, int ipa_ep_idx_rx)
+{
+ struct ipa_ep_context *ep_tx, *ep_rx;
+ int result = 0;
+
+ IPADBG("ep_tx = %d\n", ipa_ep_idx_tx);
+ IPADBG("ep_rx = %d\n", ipa_ep_idx_rx);
+
+ ep_tx = &ipa_ctx->ep[ipa_ep_idx_tx];
+ ep_rx = &ipa_ctx->ep[ipa_ep_idx_rx];
+
+ /* tear down tx pipe */
+ if (ipa_send_wdi3_common_ch_cmd(ipa_ep_idx_tx,
+ IPA_CPU_2_HW_CMD_OFFLOAD_TEAR_DOWN)) {
+ IPAERR("fail to tear down tx pipe\n");
+ result = -EFAULT;
+ goto fail;
+ }
+ ipa_disable_data_path(ipa_ep_idx_tx);
+ memset(ep_tx, 0, sizeof(struct ipa_ep_context));
+ IPADBG("tx client (ep: %d) disconnected\n", ipa_ep_idx_tx);
+
+ /* tear down rx pipe */
+ if (ipa_send_wdi3_common_ch_cmd(ipa_ep_idx_rx,
+ IPA_CPU_2_HW_CMD_OFFLOAD_TEAR_DOWN)) {
+ IPAERR("fail to tear down rx pipe\n");
+ result = -EFAULT;
+ goto fail;
+ }
+ ipa_disable_data_path(ipa_ep_idx_rx);
+ ipa_delete_dflt_flt_rules(ipa_ep_idx_rx);
+ memset(ep_rx, 0, sizeof(struct ipa_ep_context));
+ IPADBG("rx client (ep: %d) disconnected\n", ipa_ep_idx_rx);
+
+fail:
+ return result;
+}
+
+int ipa2_enable_wdi3_pipes(int ipa_ep_idx_tx, int ipa_ep_idx_rx)
+{
+ struct ipa_ep_context *ep_tx, *ep_rx;
+ int result = 0;
+
+ IPAERR("ep_tx = %d\n", ipa_ep_idx_tx);
+ IPAERR("ep_rx = %d\n", ipa_ep_idx_rx);
+
+ ep_tx = &ipa_ctx->ep[ipa_ep_idx_tx];
+ ep_rx = &ipa_ctx->ep[ipa_ep_idx_rx];
+
+ /* enable tx pipe */
+ if (ipa_send_wdi3_common_ch_cmd(ipa_ep_idx_tx,
+ IPA_CPU_2_HW_CMD_OFFLOAD_ENABLE)) {
+ IPAERR("fail to enable tx pipe\n");
+ WARN_ON(1);
+ result = -EFAULT;
+ goto fail;
+ }
+
+ /* resume tx pipe */
+ if (ipa_send_wdi3_common_ch_cmd(ipa_ep_idx_tx,
+ IPA_CPU_2_HW_CMD_OFFLOAD_RESUME)) {
+ IPAERR("fail to resume tx pipe\n");
+ WARN_ON(1);
+ result = -EFAULT;
+ goto fail;
+ }
+
+ /* enable rx pipe */
+ if (ipa_send_wdi3_common_ch_cmd(ipa_ep_idx_rx,
+ IPA_CPU_2_HW_CMD_OFFLOAD_ENABLE)) {
+ IPAERR("fail to enable rx pipe\n");
+ WARN_ON(1);
+ result = -EFAULT;
+ goto fail;
+ }
+
+ /* resume rx pipe */
+ if (ipa_send_wdi3_common_ch_cmd(ipa_ep_idx_rx,
+ IPA_CPU_2_HW_CMD_OFFLOAD_RESUME)) {
+ IPAERR("fail to resume rx pipe\n");
+ WARN_ON(1);
+ result = -EFAULT;
+ goto fail;
+ }
+
+ IPA_ACTIVE_CLIENTS_INC_SIMPLE();
+
+ /* enable data path */
+ result = ipa_enable_data_path(ipa_ep_idx_rx);
+ if (result) {
+ IPAERR("enable data path failed res=%d clnt=%d.\n", result,
+ ipa_ep_idx_rx);
+ IPA_ACTIVE_CLIENTS_DEC_SIMPLE();
+ return -EFAULT;
+ }
+
+ result = ipa_enable_data_path(ipa_ep_idx_tx);
+ if (result) {
+ IPAERR("enable data path failed res=%d clnt=%d.\n", result,
+ ipa_ep_idx_tx);
+ IPA_ACTIVE_CLIENTS_DEC_SIMPLE();
+ return -EFAULT;
+ }
+
+ IPA_ACTIVE_CLIENTS_DEC_SIMPLE();
+
+fail:
+ return result;
+}
+
+int ipa2_disable_wdi3_pipes(int ipa_ep_idx_tx, int ipa_ep_idx_rx)
+{
+ struct ipa_ep_context *ep_tx, *ep_rx;
+ int result = 0;
+
+ IPADBG("ep_tx = %d\n", ipa_ep_idx_tx);
+ IPADBG("ep_rx = %d\n", ipa_ep_idx_rx);
+
+ ep_tx = &ipa_ctx->ep[ipa_ep_idx_tx];
+ ep_rx = &ipa_ctx->ep[ipa_ep_idx_rx];
+
+ /* suspend tx pipe */
+ if (ipa_send_wdi3_common_ch_cmd(ipa_ep_idx_tx,
+ IPA_CPU_2_HW_CMD_OFFLOAD_SUSPEND)) {
+ IPAERR("fail to suspend tx pipe\n");
+ result = -EFAULT;
+ goto fail;
+ }
+
+ /* disable tx pipe */
+ if (ipa_send_wdi3_common_ch_cmd(ipa_ep_idx_tx,
+ IPA_CPU_2_HW_CMD_OFFLOAD_DISABLE)) {
+ IPAERR("fail to disable tx pipe\n");
+ result = -EFAULT;
+ goto fail;
+ }
+
+ /* suspend rx pipe */
+ if (ipa_send_wdi3_common_ch_cmd(ipa_ep_idx_rx,
+ IPA_CPU_2_HW_CMD_OFFLOAD_SUSPEND)) {
+ IPAERR("fail to suspend rx pipe\n");
+ result = -EFAULT;
+ goto fail;
+ }
+
+ /* disable rx pipe */
+ if (ipa_send_wdi3_common_ch_cmd(ipa_ep_idx_rx,
+ IPA_CPU_2_HW_CMD_OFFLOAD_DISABLE)) {
+ IPAERR("fail to disable rx pipe\n");
+ result = -EFAULT;
+ goto fail;
+ }
+
+fail:
+ return result;
+}
diff --git a/drivers/platform/msm/ipa/ipa_v2/rmnet_ipa.c b/drivers/platform/msm/ipa/ipa_v2/rmnet_ipa.c
index 2f272d2..8ea1d99 100644
--- a/drivers/platform/msm/ipa/ipa_v2/rmnet_ipa.c
+++ b/drivers/platform/msm/ipa/ipa_v2/rmnet_ipa.c
@@ -649,6 +649,8 @@
return -ENOMEM;
}
+ memset(req, 0, sizeof(struct ipa_fltr_installed_notif_req_msg_v01));
+
param->commit = 1;
param->ep = IPA_CLIENT_APPS_LAN_WAN_PROD;
param->global = false;
@@ -1516,8 +1518,8 @@
/* Get driver name */
case RMNET_IOCTL_GET_DRIVER_NAME:
memcpy(&extend_ioctl_data.u.if_name,
- ipa_netdevs[0]->name,
- sizeof(IFNAMSIZ));
+ ipa_netdevs[0]->name, IFNAMSIZ);
+ extend_ioctl_data.u.if_name[IFNAMSIZ - 1] = '\0';
if (copy_to_user((u8 *)ifr->ifr_ifru.ifru_data,
&extend_ioctl_data,
sizeof(struct rmnet_ioctl_extended_s)))
@@ -1540,6 +1542,8 @@
mutex_unlock(&add_mux_channel_lock);
return -EFAULT;
}
+ extend_ioctl_data.u.rmnet_mux_val.vchannel_name
+ [IFNAMSIZ-1] = '\0';
IPAWANDBG("ADD_MUX_CHANNEL(%d, name: %s)\n",
extend_ioctl_data.u.rmnet_mux_val.mux_id,
extend_ioctl_data.u.rmnet_mux_val.vchannel_name);
@@ -1661,6 +1665,7 @@
sizeof(wan_msg->upstream_ifname);
strlcpy(wan_msg->upstream_ifname,
extend_ioctl_data.u.if_name, len);
+ wan_msg->upstream_ifname[len - 1] = '\0';
memset(&msg_meta, 0, sizeof(struct ipa_msg_meta));
msg_meta.msg_type = WAN_XLAT_CONNECT;
msg_meta.msg_len = sizeof(struct ipa_wan_msg);
@@ -2693,6 +2698,9 @@
enum ipa_upstream_type upstream_type;
int rc = 0;
+ /* prevent string buffer overflows */
+ data->interface_name[IFNAMSIZ-1] = '\0';
+
/* get IPA backhaul type */
upstream_type = find_upstream_type(data->interface_name);
@@ -2982,6 +2990,10 @@
enum ipa_upstream_type upstream_type;
int rc = 0;
+ /* prevent string buffer overflows */
+ data->upstreamIface[IFNAMSIZ-1] = '\0';
+ data->tetherIface[IFNAMSIZ-1] = '\0';
+
/* get IPA backhaul type */
upstream_type = find_upstream_type(data->upstreamIface);
@@ -3016,6 +3028,10 @@
int rc = 0;
memset(&tether_stats, 0, sizeof(struct wan_ioctl_query_tether_stats));
+
+ /* prevent string buffer overflows */
+ data->upstreamIface[IFNAMSIZ-1] = '\0';
+
/* get IPA backhaul type */
upstream_type = find_upstream_type(data->upstreamIface);
@@ -3059,6 +3075,9 @@
memset(&tether_stats, 0, sizeof(struct wan_ioctl_query_tether_stats));
+ /* prevent string buffer overflows */
+ data->upstreamIface[IFNAMSIZ-1] = '\0';
+
/* get IPA backhaul type */
upstream_type = find_upstream_type(data->upstreamIface);
diff --git a/drivers/platform/msm/ipa/ipa_v3/Makefile b/drivers/platform/msm/ipa/ipa_v3/Makefile
index 5db2545..ae4dccf 100644
--- a/drivers/platform/msm/ipa/ipa_v3/Makefile
+++ b/drivers/platform/msm/ipa/ipa_v3/Makefile
@@ -4,6 +4,6 @@
ipat-y := ipa.o ipa_debugfs.o ipa_hdr.o ipa_flt.o ipa_rt.o ipa_dp.o ipa_client.o \
ipa_utils.o ipa_nat.o ipa_intf.o teth_bridge.o ipa_interrupts.o \
ipa_uc.o ipa_uc_wdi.o ipa_dma.o ipa_uc_mhi.o ipa_mhi.o ipa_uc_ntn.o \
- ipa_hw_stats.o ipa_pm.o
+ ipa_hw_stats.o ipa_pm.o ipa_wdi3_i.o
obj-$(CONFIG_RMNET_IPA3) += rmnet_ipa.o ipa_qmi_service_v01.o ipa_qmi_service.o rmnet_ipa_fd_ioctl.o
diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa.c b/drivers/platform/msm/ipa/ipa_v3/ipa.c
index e9df986..d3f1f34 100644
--- a/drivers/platform/msm/ipa/ipa_v3/ipa.c
+++ b/drivers/platform/msm/ipa/ipa_v3/ipa.c
@@ -236,8 +236,8 @@
ipa3_transport_release_resource);
static void ipa_gsi_notify_cb(struct gsi_per_notify *notify);
-static void ipa3_post_init_wq(struct work_struct *work);
-static DECLARE_WORK(ipa3_post_init_work, ipa3_post_init_wq);
+static void ipa3_load_ipa_fw(struct work_struct *work);
+static DECLARE_WORK(ipa3_fw_loading_work, ipa3_load_ipa_fw);
static void ipa_dec_clients_disable_clks_on_wq(struct work_struct *work);
static DECLARE_WORK(ipa_dec_clients_disable_clks_on_wq_work,
@@ -255,7 +255,7 @@
bool present;
bool arm_smmu;
bool fast_map;
- bool s1_bypass;
+ bool s1_bypass_arr[IPA_SMMU_CB_MAX];
bool use_64_bit_dma_mask;
u32 ipa_base;
u32 ipa_size;
@@ -436,14 +436,6 @@
flags);
}
-enum ipa_smmu_cb_type {
- IPA_SMMU_CB_AP,
- IPA_SMMU_CB_WLAN,
- IPA_SMMU_CB_UC,
- IPA_SMMU_CB_MAX
-
-};
-
static struct ipa_smmu_cb_ctx smmu_cb[IPA_SMMU_CB_MAX];
struct iommu_domain *ipa3_get_smmu_domain(void)
@@ -4374,11 +4366,6 @@
return result;
}
-static void ipa3_post_init_wq(struct work_struct *work)
-{
- ipa3_post_init(&ipa3_res, ipa3_ctx->dev);
-}
-
static int ipa3_manual_load_ipa_fws(void)
{
int result;
@@ -4430,19 +4417,42 @@
if (IS_ERR_OR_NULL(subsystem_get_retval)) {
IPAERR("Unable to trigger PIL process for FW loading\n");
return -EINVAL;
- } else {
- subsystem_put(subsystem_get_retval);
}
IPADBG("PIL FW loading process is complete\n");
return 0;
}
+static void ipa3_load_ipa_fw(struct work_struct *work)
+{
+ int result;
+
+ IPADBG("Entry\n");
+
+ IPA_ACTIVE_CLIENTS_INC_SIMPLE();
+
+ if (ipa3_is_msm_device() || (ipa3_ctx->ipa_hw_type >= IPA_HW_v3_5))
+ result = ipa3_pil_load_ipa_fws();
+ else
+ result = ipa3_manual_load_ipa_fws();
+
+ IPA_ACTIVE_CLIENTS_DEC_SIMPLE();
+
+ if (result) {
+ IPAERR("IPA FW loading process has failed\n");
+ return;
+ }
+ pr_info("IPA FW loaded successfully\n");
+
+ result = ipa3_post_init(&ipa3_res, ipa3_ctx->dev);
+ if (result)
+ IPAERR("IPA post init failed %d\n", result);
+}
+
static ssize_t ipa3_write(struct file *file, const char __user *buf,
size_t count, loff_t *ppos)
{
unsigned long missing;
- int result = -EINVAL;
char dbg_buff[16] = { 0 };
@@ -4456,6 +4466,9 @@
return -EFAULT;
}
+ if (count > 0)
+ dbg_buff[count - 1] = '\0';
+
/* Prevent consequent calls from trying to load the FW again. */
if (ipa3_is_ready())
return count;
@@ -4472,24 +4485,10 @@
}
}
- IPA_ACTIVE_CLIENTS_INC_SIMPLE();
-
- if (ipa3_is_msm_device() || (ipa3_ctx->ipa_hw_type >= IPA_HW_v3_5))
- result = ipa3_pil_load_ipa_fws();
- else
- result = ipa3_manual_load_ipa_fws();
-
- IPA_ACTIVE_CLIENTS_DEC_SIMPLE();
-
- if (result) {
- IPAERR("IPA FW loading process has failed\n");
- return result;
- }
-
queue_work(ipa3_ctx->transport_power_mgmt_wq,
- &ipa3_post_init_work);
- pr_info("IPA FW loaded successfully\n");
+ &ipa3_fw_loading_work);
+ IPADBG("Scheduled a work to load IPA FW\n");
return count;
}
@@ -4643,10 +4642,14 @@
ipa3_ctx->pdev = ipa_dev;
ipa3_ctx->uc_pdev = ipa_dev;
ipa3_ctx->smmu_present = smmu_info.present;
- if (!ipa3_ctx->smmu_present)
- ipa3_ctx->smmu_s1_bypass = true;
- else
- ipa3_ctx->smmu_s1_bypass = smmu_info.s1_bypass;
+ if (!ipa3_ctx->smmu_present) {
+ for (i = 0; i < IPA_SMMU_CB_MAX; i++)
+ ipa3_ctx->s1_bypass_arr[i] = true;
+ } else {
+ for (i = 0; i < IPA_SMMU_CB_MAX; i++)
+ ipa3_ctx->s1_bypass_arr[i] = smmu_info.s1_bypass_arr[i];
+ }
+
ipa3_ctx->ipa_wrapper_base = resource_p->ipa_mem_base;
ipa3_ctx->ipa_wrapper_size = resource_p->ipa_mem_size;
ipa3_ctx->ipa_hw_type = resource_p->ipa_hw_type;
@@ -5006,7 +5009,7 @@
if (result) {
IPAERR("Failed to alloc pkt_init payload\n");
result = -ENODEV;
- goto fail_create_apps_resource;
+ goto fail_allok_pkt_init;
}
if (ipa3_ctx->ipa_hw_type >= IPA_HW_v3_5)
@@ -5017,6 +5020,13 @@
init_completion(&ipa3_ctx->init_completion_obj);
init_completion(&ipa3_ctx->uc_loaded_completion_obj);
+ result = ipa3_dma_setup();
+ if (result) {
+ IPAERR("Failed to setup IPA DMA\n");
+ result = -ENODEV;
+ goto fail_ipa_dma_setup;
+ }
+
/*
* We can't register the GSI driver yet, as it expects
* the GSI FW to be up and running before the registration.
@@ -5029,7 +5039,7 @@
if (result) {
IPAERR("gsi pre FW loading config failed\n");
result = -ENODEV;
- goto fail_ipa_init_interrupts;
+ goto fail_gsi_pre_fw_load_init;
}
}
@@ -5049,8 +5059,13 @@
return 0;
fail_cdev_add:
-fail_ipa_init_interrupts:
- if (!ipa3_ctx->use_ipa_pm)
+fail_gsi_pre_fw_load_init:
+ ipa3_dma_shutdown();
+fail_ipa_dma_setup:
+fail_allok_pkt_init:
+ if (ipa3_ctx->use_ipa_pm)
+ ipa_pm_destroy();
+ else
ipa_rm_delete_resource(IPA_RM_RESOURCE_APPS_CONS);
fail_create_apps_resource:
if (!ipa3_ctx->use_ipa_pm)
@@ -5061,6 +5076,7 @@
fail_device_create:
unregister_chrdev_region(ipa3_ctx->dev_num, 1);
fail_alloc_chrdev_region:
+ idr_destroy(&ipa3_ctx->ipa_idr);
rset = &ipa3_ctx->reap_rt_tbl_set[IPA_IP_v6];
idr_destroy(&rset->rule_ids);
rset = &ipa3_ctx->reap_rt_tbl_set[IPA_IP_v4];
@@ -5069,7 +5085,6 @@
idr_destroy(&ipa3_ctx->rt_tbl_set[IPA_IP_v4].rule_ids);
ipa3_free_dma_task_for_gsi();
fail_dma_task:
- idr_destroy(&ipa3_ctx->ipa_idr);
kmem_cache_destroy(ipa3_ctx->rx_pkt_wrapper_cache);
fail_rx_pkt_wrapper_cache:
kmem_cache_destroy(ipa3_ctx->tx_pkt_wrapper_cache);
@@ -5489,7 +5504,8 @@
}
cb->valid = true;
- if (smmu_info.s1_bypass) {
+ if (of_property_read_bool(dev->of_node, "qcom,smmu-s1-bypass")) {
+ smmu_info.s1_bypass_arr[IPA_SMMU_CB_WLAN] = true;
if (iommu_domain_set_attr(cb->iommu,
DOMAIN_ATTR_S1_BYPASS,
&bypass)) {
@@ -5497,8 +5513,9 @@
cb->valid = false;
return -EIO;
}
- IPADBG("SMMU S1 BYPASS\n");
+ IPADBG("WLAN SMMU S1 BYPASS\n");
} else {
+ smmu_info.s1_bypass_arr[IPA_SMMU_CB_WLAN] = false;
if (iommu_domain_set_attr(cb->iommu,
DOMAIN_ATTR_ATOMIC,
&atomic_ctx)) {
@@ -5506,7 +5523,7 @@
cb->valid = false;
return -EIO;
}
- IPADBG("SMMU ATTR ATOMIC\n");
+ IPADBG(" WLAN SMMU ATTR ATOMIC\n");
if (smmu_info.fast_map) {
if (iommu_domain_set_attr(cb->iommu,
@@ -5520,6 +5537,9 @@
}
}
+ pr_info("IPA smmu_info.s1_bypass_arr[WLAN]=%d smmu_info.fast_map=%d\n",
+ smmu_info.s1_bypass_arr[IPA_SMMU_CB_WLAN], smmu_info.fast_map);
+
ret = iommu_attach_device(cb->iommu, dev);
if (ret) {
IPAERR("could not attach device ret=%d\n", ret);
@@ -5607,20 +5627,23 @@
cb->valid = true;
IPADBG("UC CB PROBE sub pdev=%p set attribute\n", dev);
- if (smmu_info.s1_bypass) {
+
+ if (of_property_read_bool(dev->of_node, "qcom,smmu-s1-bypass")) {
+ smmu_info.s1_bypass_arr[IPA_SMMU_CB_UC] = true;
if (iommu_domain_set_attr(cb->mapping->domain,
- DOMAIN_ATTR_S1_BYPASS,
- &bypass)) {
+ DOMAIN_ATTR_S1_BYPASS,
+ &bypass)) {
IPAERR("couldn't set bypass\n");
arm_iommu_release_mapping(cb->mapping);
cb->valid = false;
return -EIO;
}
- IPADBG("SMMU S1 BYPASS\n");
+ IPADBG("UC SMMU S1 BYPASS\n");
} else {
+ smmu_info.s1_bypass_arr[IPA_SMMU_CB_UC] = false;
if (iommu_domain_set_attr(cb->mapping->domain,
- DOMAIN_ATTR_ATOMIC,
- &atomic_ctx)) {
+ DOMAIN_ATTR_ATOMIC,
+ &atomic_ctx)) {
IPAERR("couldn't set domain as atomic\n");
arm_iommu_release_mapping(cb->mapping);
cb->valid = false;
@@ -5630,8 +5653,8 @@
if (smmu_info.fast_map) {
if (iommu_domain_set_attr(cb->mapping->domain,
- DOMAIN_ATTR_FAST,
- &fast)) {
+ DOMAIN_ATTR_FAST,
+ &fast)) {
IPAERR("couldn't set fast map\n");
arm_iommu_release_mapping(cb->mapping);
cb->valid = false;
@@ -5641,6 +5664,9 @@
}
}
+ pr_info("IPA smmu_info.s1_bypass_arr[UC]=%d smmu_info.fast_map=%d\n",
+ smmu_info.s1_bypass_arr[IPA_SMMU_CB_UC], smmu_info.fast_map);
+
IPADBG("UC CB PROBE sub pdev=%p attaching IOMMU device\n", dev);
ret = arm_iommu_attach_device(cb->dev, cb->mapping);
if (ret) {
@@ -5707,7 +5733,9 @@
IPADBG("SMMU mapping created\n");
cb->valid = true;
- if (smmu_info.s1_bypass) {
+ if (of_property_read_bool(dev->of_node,
+ "qcom,smmu-s1-bypass")) {
+ smmu_info.s1_bypass_arr[IPA_SMMU_CB_AP] = true;
if (iommu_domain_set_attr(cb->mapping->domain,
DOMAIN_ATTR_S1_BYPASS,
&bypass)) {
@@ -5716,8 +5744,9 @@
cb->valid = false;
return -EIO;
}
- IPADBG("SMMU S1 BYPASS\n");
+ IPADBG("AP/USB SMMU S1 BYPASS\n");
} else {
+ smmu_info.s1_bypass_arr[IPA_SMMU_CB_AP] = false;
if (iommu_domain_set_attr(cb->mapping->domain,
DOMAIN_ATTR_ATOMIC,
&atomic_ctx)) {
@@ -5726,7 +5755,7 @@
cb->valid = false;
return -EIO;
}
- IPADBG("SMMU atomic set\n");
+ IPADBG("AP/USB SMMU atomic set\n");
if (iommu_domain_set_attr(cb->mapping->domain,
DOMAIN_ATTR_FAST,
@@ -5739,6 +5768,9 @@
IPADBG("SMMU fast map set\n");
}
+ pr_info("IPA smmu_info.s1_bypass_arr[AP]=%d smmu_info.fast_map=%d\n",
+ smmu_info.s1_bypass_arr[IPA_SMMU_CB_AP], smmu_info.fast_map);
+
result = arm_iommu_attach_device(cb->dev, cb->mapping);
if (result) {
IPAERR("couldn't attach to IOMMU ret=%d\n", result);
@@ -5922,17 +5954,12 @@
if (of_property_read_bool(pdev_p->dev.of_node, "qcom,arm-smmu")) {
if (of_property_read_bool(pdev_p->dev.of_node,
- "qcom,smmu-s1-bypass"))
- smmu_info.s1_bypass = true;
- if (of_property_read_bool(pdev_p->dev.of_node,
"qcom,smmu-fast-map"))
smmu_info.fast_map = true;
if (of_property_read_bool(pdev_p->dev.of_node,
"qcom,use-64-bit-dma-mask"))
smmu_info.use_64_bit_dma_mask = true;
smmu_info.arm_smmu = true;
- pr_info("IPA smmu_info.s1_bypass=%d smmu_info.fast_map=%d\n",
- smmu_info.s1_bypass, smmu_info.fast_map);
} else if (of_property_read_bool(pdev_p->dev.of_node,
"qcom,msm-smmu")) {
IPAERR("Legacy IOMMU not supported\n");
diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_client.c b/drivers/platform/msm/ipa/ipa_v3/ipa_client.c
index 1af968e..a859ff7 100644
--- a/drivers/platform/msm/ipa/ipa_v3/ipa_client.c
+++ b/drivers/platform/msm/ipa/ipa_v3/ipa_client.c
@@ -488,7 +488,7 @@
struct iommu_domain *smmu_domain;
int res;
- if (ipa3_ctx->smmu_s1_bypass)
+ if (ipa3_ctx->s1_bypass_arr[IPA_SMMU_CB_AP])
return 0;
smmu_domain = ipa3_get_smmu_domain();
@@ -520,7 +520,7 @@
struct iommu_domain *smmu_domain;
int res;
- if (ipa3_ctx->smmu_s1_bypass)
+ if (ipa3_ctx->s1_bypass_arr[IPA_SMMU_CB_AP])
return 0;
smmu_domain = ipa3_get_smmu_domain();
@@ -743,6 +743,7 @@
return 0;
}
+/* This function called as part of usb pipe resume */
int ipa3_xdci_connect(u32 clnt_hdl)
{
int result;
@@ -782,11 +783,14 @@
return result;
}
+
+/* This function called as part of usb pipe connect */
int ipa3_xdci_start(u32 clnt_hdl, u8 xferrscidx, bool xferrscidx_valid)
{
struct ipa3_ep_context *ep;
int result = -EFAULT;
enum gsi_status gsi_res;
+ struct ipa_ep_cfg_ctrl ep_cfg_ctrl;
IPADBG("entry\n");
if (clnt_hdl >= ipa3_ctx->ipa_num_pipes ||
@@ -808,6 +812,22 @@
goto write_chan_scratch_fail;
}
}
+
+ if (IPA_CLIENT_IS_PROD(ep->client) && ep->skip_ep_cfg) {
+ memset(&ep_cfg_ctrl, 0, sizeof(struct ipa_ep_cfg_ctrl));
+ ep_cfg_ctrl.ipa_ep_delay = true;
+ ep->ep_delay_set = true;
+
+ result = ipa3_cfg_ep_ctrl(clnt_hdl, &ep_cfg_ctrl);
+ if (result)
+ IPAERR("client (ep: %d) failed result=%d\n",
+ clnt_hdl, result);
+ else
+ IPADBG("client (ep: %d) success\n", clnt_hdl);
+ } else {
+ ep->ep_delay_set = false;
+ }
+
gsi_res = gsi_start_channel(ep->gsi_chan_hdl);
if (gsi_res != GSI_STATUS_SUCCESS) {
IPAERR("Error starting channel: %d\n", gsi_res);
@@ -1012,13 +1032,15 @@
/* Clocks should be voted for before invoking this function */
static int ipa3_stop_ul_chan_with_data_drain(u32 qmi_req_id,
- u32 source_pipe_bitmask, bool should_force_clear, u32 clnt_hdl)
+ u32 source_pipe_bitmask, bool should_force_clear, u32 clnt_hdl,
+ bool remove_delay)
{
int result;
bool is_empty = false;
int i;
bool stop_in_proc;
struct ipa3_ep_context *ep;
+ struct ipa_ep_cfg_ctrl ep_cfg_ctrl;
if (clnt_hdl >= ipa3_ctx->ipa_num_pipes ||
ipa3_ctx->ep[clnt_hdl].valid == 0) {
@@ -1039,6 +1061,22 @@
if (!stop_in_proc)
goto exit;
+ if (remove_delay && ep->ep_delay_set == true) {
+ memset(&ep_cfg_ctrl, 0, sizeof(struct ipa_ep_cfg_ctrl));
+ ep_cfg_ctrl.ipa_ep_delay = false;
+ result = ipa3_cfg_ep_ctrl(clnt_hdl,
+ &ep_cfg_ctrl);
+ if (result) {
+ IPAERR
+ ("client (ep: %d) failed to remove delay result=%d\n",
+ clnt_hdl, result);
+ } else {
+ IPADBG("client (ep: %d) delay removed\n",
+ clnt_hdl);
+ ep->ep_delay_set = false;
+ }
+ }
+
/* if stop_in_proc, lets wait for emptiness */
for (i = 0; i < IPA_POLL_FOR_EMPTINESS_NUM; i++) {
result = ipa3_is_xdci_channel_empty(ep, &is_empty);
@@ -1104,6 +1142,21 @@
if (should_force_clear)
ipa3_disable_force_clear(qmi_req_id);
exit:
+ if (remove_delay && ep->ep_delay_set == true) {
+ memset(&ep_cfg_ctrl, 0, sizeof(struct ipa_ep_cfg_ctrl));
+ ep_cfg_ctrl.ipa_ep_delay = false;
+ result = ipa3_cfg_ep_ctrl(clnt_hdl,
+ &ep_cfg_ctrl);
+ if (result) {
+ IPAERR
+ ("client (ep: %d) failed to remove delay result=%d\n",
+ clnt_hdl, result);
+ } else {
+ IPADBG("client (ep: %d) delay removed\n",
+ clnt_hdl);
+ ep->ep_delay_set = false;
+ }
+ }
return result;
}
@@ -1133,7 +1186,8 @@
source_pipe_bitmask = 1 <<
ipa3_get_ep_mapping(ep->client);
result = ipa3_stop_ul_chan_with_data_drain(qmi_req_id,
- source_pipe_bitmask, should_force_clear, clnt_hdl);
+ source_pipe_bitmask, should_force_clear, clnt_hdl,
+ true);
if (result) {
IPAERR("Fail to stop UL channel with data drain\n");
WARN_ON(1);
@@ -1318,7 +1372,8 @@
if (!is_dpl) {
source_pipe_bitmask = 1 << ipa3_get_ep_mapping(ul_ep->client);
result = ipa3_stop_ul_chan_with_data_drain(qmi_req_id,
- source_pipe_bitmask, should_force_clear, ul_clnt_hdl);
+ source_pipe_bitmask, should_force_clear, ul_clnt_hdl,
+ false);
if (result) {
IPAERR("Error stopping UL channel: result = %d\n",
result);
diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_dma.c b/drivers/platform/msm/ipa/ipa_v3/ipa_dma.c
index 5a29cbc..1a612dc 100644
--- a/drivers/platform/msm/ipa/ipa_v3/ipa_dma.c
+++ b/drivers/platform/msm/ipa/ipa_v3/ipa_dma.c
@@ -77,7 +77,7 @@
/**
* struct ipa3_dma_ctx -IPADMA driver context information
- * @is_enabled:is ipa_dma enabled?
+ * @enable_ref_cnt: ipa dma enable reference count
* @destroy_pending: destroy ipa_dma after handling all pending memcpy
* @ipa_dma_xfer_wrapper_cache: cache of ipa3_dma_xfer_wrapper structs
* @sync_lock: lock for synchronisation in sync_memcpy
@@ -96,7 +96,7 @@
* @total_uc_memcpy: total number of uc memcpy (statistics)
*/
struct ipa3_dma_ctx {
- bool is_enabled;
+ unsigned int enable_ref_cnt;
bool destroy_pending;
struct kmem_cache *ipa_dma_xfer_wrapper_cache;
struct mutex sync_lock;
@@ -121,6 +121,70 @@
};
static struct ipa3_dma_ctx *ipa3_dma_ctx;
+/**
+ * struct ipa3_dma_init_refcnt_ctrl -IPADMA driver init control information
+ * @ref_cnt: reference count for initialization operations
+ * @lock: lock for the reference count
+ */
+struct ipa3_dma_init_refcnt_ctrl {
+ unsigned int ref_cnt;
+ struct mutex lock;
+};
+static struct ipa3_dma_init_refcnt_ctrl *ipa3_dma_init_refcnt_ctrl;
+
+/**
+ * ipa3_dma_setup() - One time setup for IPA DMA
+ *
+ * This function should be called once to setup ipa dma
+ * by creating the init reference count controller
+ *
+ * Return codes: 0: success
+ * Negative value: failure
+ */
+int ipa3_dma_setup(void)
+{
+ IPADMA_FUNC_ENTRY();
+
+ if (ipa3_dma_init_refcnt_ctrl) {
+ IPADMA_ERR("Setup already done\n");
+ return -EFAULT;
+ }
+
+ ipa3_dma_init_refcnt_ctrl =
+ kzalloc(sizeof(*(ipa3_dma_init_refcnt_ctrl)), GFP_KERNEL);
+
+ if (!ipa3_dma_init_refcnt_ctrl) {
+ IPADMA_ERR("kzalloc error.\n");
+ return -ENOMEM;
+ }
+
+ mutex_init(&ipa3_dma_init_refcnt_ctrl->lock);
+
+ IPADMA_FUNC_EXIT();
+ return 0;
+}
+
+/**
+ * ipa3_dma_shutdown() - Clear setup operations.
+ *
+ * Cleanup for the setup function.
+ * Should be called during IPA driver unloading.
+ * It assumes all ipa_dma operations are done and ipa_dma is destroyed.
+ *
+ * Return codes: None.
+ */
+void ipa3_dma_shutdown(void)
+{
+ IPADMA_FUNC_ENTRY();
+
+ if (!ipa3_dma_init_refcnt_ctrl)
+ return;
+
+ kfree(ipa3_dma_init_refcnt_ctrl);
+ ipa3_dma_init_refcnt_ctrl = NULL;
+
+ IPADMA_FUNC_EXIT();
+}
/**
* ipa3_dma_init() -Initialize IPADMA.
@@ -129,8 +193,10 @@
* MEMCPY_DMA_SYNC_PROD ->MEMCPY_DMA_SYNC_CONS
* MEMCPY_DMA_ASYNC_PROD->MEMCPY_DMA_SYNC_CONS
*
+ * Can be executed several times (re-entrant)
+ *
* Return codes: 0: success
- * -EFAULT: IPADMA is already initialized
+ * -EFAULT: Mismatch between context existence and init ref_cnt
* -EINVAL: IPA driver is not initialized
* -ENOMEM: allocating memory error
* -EPERM: pipe connection failed
@@ -145,21 +211,43 @@
IPADMA_FUNC_ENTRY();
+ if (!ipa3_dma_init_refcnt_ctrl) {
+ IPADMA_ERR("Setup isn't done yet!\n");
+ return -EINVAL;
+ }
+
+ mutex_lock(&ipa3_dma_init_refcnt_ctrl->lock);
+ if (ipa3_dma_init_refcnt_ctrl->ref_cnt > 0) {
+ IPADMA_DBG("Already initialized refcnt=%d\n",
+ ipa3_dma_init_refcnt_ctrl->ref_cnt);
+ if (!ipa3_dma_ctx) {
+ IPADMA_ERR("Context missing. refcnt=%d\n",
+ ipa3_dma_init_refcnt_ctrl->ref_cnt);
+ res = -EFAULT;
+ } else {
+ ipa3_dma_init_refcnt_ctrl->ref_cnt++;
+ }
+ goto init_unlock;
+ }
+
if (ipa3_dma_ctx) {
- IPADMA_ERR("Already initialized.\n");
- return -EFAULT;
+ IPADMA_ERR("Context already exist\n");
+ res = -EFAULT;
+ goto init_unlock;
}
if (!ipa3_is_ready()) {
IPADMA_ERR("IPA is not ready yet\n");
- return -EINVAL;
+ res = -EINVAL;
+ goto init_unlock;
}
ipa_dma_ctx_t = kzalloc(sizeof(*(ipa3_dma_ctx)), GFP_KERNEL);
if (!ipa_dma_ctx_t) {
IPADMA_ERR("kzalloc error.\n");
- return -ENOMEM;
+ res = -ENOMEM;
+ goto init_unlock;
}
ipa_dma_ctx_t->ipa_dma_xfer_wrapper_cache =
@@ -176,7 +264,7 @@
mutex_init(&ipa_dma_ctx_t->sync_lock);
spin_lock_init(&ipa_dma_ctx_t->pending_lock);
init_completion(&ipa_dma_ctx_t->done);
- ipa_dma_ctx_t->is_enabled = false;
+ ipa_dma_ctx_t->enable_ref_cnt = 0;
ipa_dma_ctx_t->destroy_pending = false;
atomic_set(&ipa_dma_ctx_t->async_memcpy_pending_cnt, 0);
atomic_set(&ipa_dma_ctx_t->sync_memcpy_pending_cnt, 0);
@@ -290,10 +378,12 @@
}
ipa3_dma_debugfs_init();
ipa3_dma_ctx = ipa_dma_ctx_t;
+ ipa3_dma_init_refcnt_ctrl->ref_cnt = 1;
IPADMA_DBG("ASYNC MEMCPY pipes are connected\n");
IPADMA_FUNC_EXIT();
- return res;
+ goto init_unlock;
+
fail_async_cons:
ipa3_teardown_sys_pipe(ipa_dma_ctx_t->ipa_dma_async_prod_hdl);
fail_async_prod:
@@ -309,6 +399,8 @@
fail_mem_ctrl:
kfree(ipa_dma_ctx_t);
ipa3_dma_ctx = NULL;
+init_unlock:
+ mutex_unlock(&ipa3_dma_init_refcnt_ctrl->lock);
return res;
}
@@ -316,26 +408,29 @@
/**
* ipa3_dma_enable() -Vote for IPA clocks.
*
+ * Can be executed several times (re-entrant)
+ *
*Return codes: 0: success
* -EINVAL: IPADMA is not initialized
- * -EPERM: Operation not permitted as ipa_dma is already
- * enabled
*/
int ipa3_dma_enable(void)
{
IPADMA_FUNC_ENTRY();
- if (ipa3_dma_ctx == NULL) {
+ if ((ipa3_dma_ctx == NULL) ||
+ (ipa3_dma_init_refcnt_ctrl->ref_cnt < 1)) {
IPADMA_ERR("IPADMA isn't initialized, can't enable\n");
- return -EPERM;
+ return -EINVAL;
}
mutex_lock(&ipa3_dma_ctx->enable_lock);
- if (ipa3_dma_ctx->is_enabled) {
- IPADMA_ERR("Already enabled.\n");
+ if (ipa3_dma_ctx->enable_ref_cnt > 0) {
+ IPADMA_ERR("Already enabled refcnt=%d\n",
+ ipa3_dma_ctx->enable_ref_cnt);
+ ipa3_dma_ctx->enable_ref_cnt++;
mutex_unlock(&ipa3_dma_ctx->enable_lock);
- return -EPERM;
+ return 0;
}
IPA_ACTIVE_CLIENTS_INC_SPECIAL("DMA");
- ipa3_dma_ctx->is_enabled = true;
+ ipa3_dma_ctx->enable_ref_cnt = 1;
mutex_unlock(&ipa3_dma_ctx->enable_lock);
IPADMA_FUNC_EXIT();
@@ -375,32 +470,45 @@
int ipa3_dma_disable(void)
{
unsigned long flags;
+ int res = 0;
+ bool dec_clks = false;
IPADMA_FUNC_ENTRY();
- if (ipa3_dma_ctx == NULL) {
+ if ((ipa3_dma_ctx == NULL) ||
+ (ipa3_dma_init_refcnt_ctrl->ref_cnt < 1)) {
IPADMA_ERR("IPADMA isn't initialized, can't disable\n");
- return -EPERM;
+ return -EINVAL;
}
mutex_lock(&ipa3_dma_ctx->enable_lock);
spin_lock_irqsave(&ipa3_dma_ctx->pending_lock, flags);
- if (!ipa3_dma_ctx->is_enabled) {
- IPADMA_ERR("Already disabled.\n");
- spin_unlock_irqrestore(&ipa3_dma_ctx->pending_lock, flags);
- mutex_unlock(&ipa3_dma_ctx->enable_lock);
- return -EPERM;
+ if (ipa3_dma_ctx->enable_ref_cnt > 1) {
+ IPADMA_DBG("Multiple enablement done. refcnt=%d\n",
+ ipa3_dma_ctx->enable_ref_cnt);
+ ipa3_dma_ctx->enable_ref_cnt--;
+ goto completed;
}
+
+ if (ipa3_dma_ctx->enable_ref_cnt == 0) {
+ IPADMA_ERR("Already disabled\n");
+ res = -EPERM;
+ goto completed;
+ }
+
if (ipa3_dma_work_pending()) {
IPADMA_ERR("There is pending work, can't disable.\n");
- spin_unlock_irqrestore(&ipa3_dma_ctx->pending_lock, flags);
- mutex_unlock(&ipa3_dma_ctx->enable_lock);
- return -EFAULT;
+ res = -EFAULT;
+ goto completed;
}
- ipa3_dma_ctx->is_enabled = false;
- spin_unlock_irqrestore(&ipa3_dma_ctx->pending_lock, flags);
- IPA_ACTIVE_CLIENTS_DEC_SPECIAL("DMA");
- mutex_unlock(&ipa3_dma_ctx->enable_lock);
+ ipa3_dma_ctx->enable_ref_cnt = 0;
+ dec_clks = true;
IPADMA_FUNC_EXIT();
- return 0;
+
+completed:
+ spin_unlock_irqrestore(&ipa3_dma_ctx->pending_lock, flags);
+ if (dec_clks)
+ IPA_ACTIVE_CLIENTS_DEC_SPECIAL("DMA");
+ mutex_unlock(&ipa3_dma_ctx->enable_lock);
+ return res;
}
/**
@@ -449,7 +557,7 @@
return -EINVAL;
}
spin_lock_irqsave(&ipa3_dma_ctx->pending_lock, flags);
- if (!ipa3_dma_ctx->is_enabled) {
+ if (!ipa3_dma_ctx->enable_ref_cnt) {
IPADMA_ERR("can't memcpy, IPADMA isn't enabled\n");
spin_unlock_irqrestore(&ipa3_dma_ctx->pending_lock, flags);
return -EPERM;
@@ -695,7 +803,7 @@
return -EINVAL;
}
spin_lock_irqsave(&ipa3_dma_ctx->pending_lock, flags);
- if (!ipa3_dma_ctx->is_enabled) {
+ if (!ipa3_dma_ctx->enable_ref_cnt) {
IPADMA_ERR("can't memcpy, IPA_DMA isn't enabled\n");
spin_unlock_irqrestore(&ipa3_dma_ctx->pending_lock, flags);
return -EPERM;
@@ -877,7 +985,7 @@
}
spin_lock_irqsave(&ipa3_dma_ctx->pending_lock, flags);
- if (!ipa3_dma_ctx->is_enabled) {
+ if (!ipa3_dma_ctx->enable_ref_cnt) {
IPADMA_ERR("can't memcpy, IPADMA isn't enabled\n");
spin_unlock_irqrestore(&ipa3_dma_ctx->pending_lock, flags);
return -EPERM;
@@ -911,17 +1019,36 @@
int res = 0;
IPADMA_FUNC_ENTRY();
- if (!ipa3_dma_ctx) {
- IPADMA_ERR("IPADMA isn't initialized\n");
+
+ if (!ipa3_dma_init_refcnt_ctrl) {
+ IPADMA_ERR("Setup isn't done\n");
return;
}
+ mutex_lock(&ipa3_dma_init_refcnt_ctrl->lock);
+ if (ipa3_dma_init_refcnt_ctrl->ref_cnt > 1) {
+ IPADMA_DBG("Multiple initialization done. refcnt=%d\n",
+ ipa3_dma_init_refcnt_ctrl->ref_cnt);
+ ipa3_dma_init_refcnt_ctrl->ref_cnt--;
+ goto completed;
+ }
+
+ if ((!ipa3_dma_ctx) || (ipa3_dma_init_refcnt_ctrl->ref_cnt == 0)) {
+ IPADMA_ERR("IPADMA isn't initialized ctx=%pK\n", ipa3_dma_ctx);
+ goto completed;
+ }
+
if (ipa3_dma_work_pending()) {
ipa3_dma_ctx->destroy_pending = true;
IPADMA_DBG("There are pending memcpy, wait for completion\n");
wait_for_completion(&ipa3_dma_ctx->done);
}
+ if (ipa3_dma_ctx->enable_ref_cnt > 0) {
+ IPADMA_ERR("IPADMA still enabled\n");
+ goto completed;
+ }
+
res = ipa3_teardown_sys_pipe(ipa3_dma_ctx->ipa_dma_async_cons_hdl);
if (res)
IPADMA_ERR("teardown IPADMA ASYNC CONS failed\n");
@@ -947,7 +1074,11 @@
kfree(ipa3_dma_ctx);
ipa3_dma_ctx = NULL;
+ ipa3_dma_init_refcnt_ctrl->ref_cnt = 0;
IPADMA_FUNC_EXIT();
+
+completed:
+ mutex_unlock(&ipa3_dma_init_refcnt_ctrl->lock);
}
/**
@@ -1006,15 +1137,30 @@
{
int nbytes = 0;
+ if (!ipa3_dma_init_refcnt_ctrl) {
+ nbytes += scnprintf(&dbg_buff[nbytes],
+ IPADMA_MAX_MSG_LEN - nbytes,
+ "Setup was not done\n");
+ goto completed;
+
+ }
+
if (!ipa3_dma_ctx) {
nbytes += scnprintf(&dbg_buff[nbytes],
IPADMA_MAX_MSG_LEN - nbytes,
- "Not initialized\n");
+ "Status:\n Not initialized (ref_cnt=%d)\n",
+ ipa3_dma_init_refcnt_ctrl->ref_cnt);
} else {
nbytes += scnprintf(&dbg_buff[nbytes],
IPADMA_MAX_MSG_LEN - nbytes,
- "Status:\n IPADMA is %s\n",
- (ipa3_dma_ctx->is_enabled) ? "Enabled" : "Disabled");
+ "Status:\n Initialized (ref_cnt=%d)\n",
+ ipa3_dma_init_refcnt_ctrl->ref_cnt);
+ nbytes += scnprintf(&dbg_buff[nbytes],
+ IPADMA_MAX_MSG_LEN - nbytes,
+ " %s (ref_cnt=%d)\n",
+ (ipa3_dma_ctx->enable_ref_cnt > 0) ?
+ "Enabled" : "Disabled",
+ ipa3_dma_ctx->enable_ref_cnt);
nbytes += scnprintf(&dbg_buff[nbytes],
IPADMA_MAX_MSG_LEN - nbytes,
"Statistics:\n total sync memcpy: %d\n ",
@@ -1025,17 +1171,23 @@
atomic_read(&ipa3_dma_ctx->total_async_memcpy));
nbytes += scnprintf(&dbg_buff[nbytes],
IPADMA_MAX_MSG_LEN - nbytes,
+ "total uc memcpy: %d\n ",
+ atomic_read(&ipa3_dma_ctx->total_uc_memcpy));
+ nbytes += scnprintf(&dbg_buff[nbytes],
+ IPADMA_MAX_MSG_LEN - nbytes,
"pending sync memcpy jobs: %d\n ",
atomic_read(&ipa3_dma_ctx->sync_memcpy_pending_cnt));
nbytes += scnprintf(&dbg_buff[nbytes],
IPADMA_MAX_MSG_LEN - nbytes,
- "pending async memcpy jobs: %d\n",
+ "pending async memcpy jobs: %d\n ",
atomic_read(&ipa3_dma_ctx->async_memcpy_pending_cnt));
nbytes += scnprintf(&dbg_buff[nbytes],
IPADMA_MAX_MSG_LEN - nbytes,
"pending uc memcpy jobs: %d\n",
atomic_read(&ipa3_dma_ctx->uc_memcpy_pending_cnt));
}
+
+completed:
return simple_read_from_buffer(ubuf, count, ppos, dbg_buff, nbytes);
}
diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_dp.c b/drivers/platform/msm/ipa/ipa_v3/ipa_dp.c
index 7f30a10..ee312c7 100644
--- a/drivers/platform/msm/ipa/ipa_v3/ipa_dp.c
+++ b/drivers/platform/msm/ipa/ipa_v3/ipa_dp.c
@@ -136,8 +136,8 @@
DMA_TO_DEVICE);
} else {
dma_unmap_page(ipa3_ctx->pdev,
- next_pkt->mem.phys_base,
- next_pkt->mem.size,
+ tx_pkt->mem.phys_base,
+ tx_pkt->mem.size,
DMA_TO_DEVICE);
}
}
@@ -3816,7 +3816,7 @@
cnt += IPA_WAN_AGGR_PKT_CNT;
total_cnt++;
- if (ep->sys->len == 0 || total_cnt >= ep->sys->rx_pool_sz) {
+ if (ep->sys->len == 0) {
total_cnt = 0;
cnt = cnt-1;
break;
diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_flt.c b/drivers/platform/msm/ipa/ipa_v3/ipa_flt.c
index 0883e6f..5d1bbe7 100644
--- a/drivers/platform/msm/ipa/ipa_v3/ipa_flt.c
+++ b/drivers/platform/msm/ipa/ipa_v3/ipa_flt.c
@@ -753,6 +753,11 @@
goto error;
}
}
+ } else {
+ if (rule->rt_tbl_idx > 0) {
+ IPAERR("invalid RT tbl\n");
+ goto error;
+ }
}
if (ipa3_ctx->ipa_hw_type >= IPA_HW_v4_0) {
@@ -773,7 +778,8 @@
}
if (rule->rule_id) {
- if (!(rule->rule_id & ipahal_get_rule_id_hi_bit())) {
+ if ((rule->rule_id < ipahal_get_rule_id_hi_bit()) ||
+ (rule->rule_id >= ((ipahal_get_rule_id_hi_bit()<<1)-1))) {
IPAERR("invalid rule_id provided 0x%x\n"
"rule_id with bit 0x%x are auto generated\n",
rule->rule_id, ipahal_get_rule_id_hi_bit());
@@ -879,7 +885,8 @@
ipa_insert_failed:
list_del(&entry->link);
/* if rule id was allocated from idr, remove it */
- if (!(entry->rule_id & ipahal_get_rule_id_hi_bit()))
+ if ((entry->rule_id < ipahal_get_rule_id_hi_bit()) &&
+ (entry->rule_id >= ipahal_get_low_rule_id()))
idr_remove(entry->tbl->rule_ids, entry->rule_id);
kmem_cache_free(ipa3_ctx->flt_rule_cache, entry);
@@ -926,7 +933,8 @@
ipa_insert_failed:
list_del(&entry->link);
/* if rule id was allocated from idr, remove it */
- if (!(entry->rule_id & ipahal_get_rule_id_hi_bit()))
+ if ((entry->rule_id < ipahal_get_rule_id_hi_bit()) &&
+ (entry->rule_id >= ipahal_get_low_rule_id()))
idr_remove(entry->tbl->rule_ids, entry->rule_id);
kmem_cache_free(ipa3_ctx->flt_rule_cache, entry);
@@ -960,7 +968,8 @@
entry->tbl->rule_cnt, entry->rule_id);
entry->cookie = 0;
/* if rule id was allocated from idr, remove it */
- if (!(entry->rule_id & ipahal_get_rule_id_hi_bit()))
+ if ((entry->rule_id < ipahal_get_rule_id_hi_bit()) &&
+ (entry->rule_id >= ipahal_get_low_rule_id()))
idr_remove(entry->tbl->rule_ids, entry->rule_id);
kmem_cache_free(ipa3_ctx->flt_rule_cache, entry);
@@ -1016,6 +1025,11 @@
goto error;
}
}
+ } else {
+ if (frule->rule.rt_tbl_idx > 0) {
+ IPAERR_RL("invalid RT tbl\n");
+ goto error;
+ }
}
entry->rule = frule->rule;
@@ -1380,7 +1394,8 @@
if (entry->rt_tbl)
entry->rt_tbl->ref_cnt--;
/* if rule id was allocated from idr, remove it */
- if (!(entry->rule_id & ipahal_get_rule_id_hi_bit()))
+ if ((entry->rule_id < ipahal_get_rule_id_hi_bit()) &&
+ (entry->rule_id >= ipahal_get_low_rule_id()))
idr_remove(entry->tbl->rule_ids,
entry->rule_id);
entry->cookie = 0;
diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_hdr.c b/drivers/platform/msm/ipa/ipa_v3/ipa_hdr.c
index cc29f8f..34624c0 100644
--- a/drivers/platform/msm/ipa/ipa_v3/ipa_hdr.c
+++ b/drivers/platform/msm/ipa/ipa_v3/ipa_hdr.c
@@ -1133,6 +1133,7 @@
return -EINVAL;
}
mutex_lock(&ipa3_ctx->lock);
+ lookup->name[IPA_RESOURCE_NAME_MAX-1] = '\0';
entry = __ipa_find_hdr(lookup->name);
if (entry) {
lookup->hdl = entry->id;
@@ -1255,6 +1256,7 @@
return -EINVAL;
}
mutex_lock(&ipa3_ctx->lock);
+ copy->name[IPA_RESOURCE_NAME_MAX-1] = '\0';
entry = __ipa_find_hdr(copy->name);
if (entry) {
memcpy(copy->hdr, entry->hdr, entry->hdr_len);
diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_i.h b/drivers/platform/msm/ipa/ipa_v3/ipa_i.h
index 3e1d188..29fe24e 100644
--- a/drivers/platform/msm/ipa/ipa_v3/ipa_i.h
+++ b/drivers/platform/msm/ipa/ipa_v3/ipa_i.h
@@ -556,6 +556,7 @@
u32 qmi_request_sent;
bool napi_enabled;
u32 eot_in_poll_err;
+ bool ep_delay_set;
/* sys MUST be the last element of this struct */
struct ipa3_sys_context *sys;
@@ -1098,6 +1099,13 @@
struct ipa_msg_meta msg_meta;
};
+enum ipa_smmu_cb_type {
+ IPA_SMMU_CB_AP,
+ IPA_SMMU_CB_WLAN,
+ IPA_SMMU_CB_UC,
+ IPA_SMMU_CB_MAX
+};
+
/**
* struct ipa3_context - IPA context
* @class: pointer to the struct class
@@ -1296,7 +1304,7 @@
bool apply_rg10_wa;
bool gsi_ch20_wa;
bool smmu_present;
- bool smmu_s1_bypass;
+ bool s1_bypass_arr[IPA_SMMU_CB_MAX];
u32 wdi_map_cnt;
struct wakeup_source w_lock;
struct ipa3_wakelock_ref_cnt wakelock_ref_cnt;
@@ -1825,6 +1833,11 @@
int ipa3_tear_down_uc_offload_pipes(int ipa_ep_idx_ul, int ipa_ep_idx_dl);
int ipa3_ntn_uc_reg_rdyCB(void (*ipauc_ready_cb)(void *), void *priv);
void ipa3_ntn_uc_dereg_rdyCB(void);
+int ipa3_conn_wdi3_pipes(struct ipa_wdi3_conn_in_params *in,
+ struct ipa_wdi3_conn_out_params *out);
+int ipa3_disconn_wdi3_pipes(int ipa_ep_idx_tx, int ipa_ep_idx_rx);
+int ipa3_enable_wdi3_pipes(int ipa_ep_idx_tx, int ipa_ep_idx_rx);
+int ipa3_disable_wdi3_pipes(int ipa_ep_idx_tx, int ipa_ep_idx_rx);
/*
* To retrieve doorbell physical address of
@@ -2094,6 +2107,8 @@
struct ipa3_uc_hdlrs *hdlrs);
int ipa3_create_nat_device(void);
int ipa3_uc_notify_clk_state(bool enabled);
+int ipa3_dma_setup(void);
+void ipa3_dma_shutdown(void);
void ipa3_dma_async_memcpy_notify_cb(void *priv,
enum ipa_dp_evt_type evt, unsigned long data);
diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_intf.c b/drivers/platform/msm/ipa/ipa_v3/ipa_intf.c
index 2bd7b79..4ada018 100644
--- a/drivers/platform/msm/ipa/ipa_v3/ipa_intf.c
+++ b/drivers/platform/msm/ipa/ipa_v3/ipa_intf.c
@@ -225,6 +225,7 @@
return result;
}
+ lookup->name[IPA_RESOURCE_NAME_MAX-1] = '\0';
if (strnlen(lookup->name, IPA_RESOURCE_NAME_MAX) ==
IPA_RESOURCE_NAME_MAX) {
IPAERR_RL("Interface name too long. (%s)\n", lookup->name);
@@ -267,6 +268,7 @@
return result;
}
+ tx->name[IPA_RESOURCE_NAME_MAX-1] = '\0';
if (strnlen(tx->name, IPA_RESOURCE_NAME_MAX) == IPA_RESOURCE_NAME_MAX) {
IPAERR_RL("Interface name too long. (%s)\n", tx->name);
return result;
@@ -314,6 +316,7 @@
return result;
}
+ rx->name[IPA_RESOURCE_NAME_MAX-1] = '\0';
if (strnlen(rx->name, IPA_RESOURCE_NAME_MAX) == IPA_RESOURCE_NAME_MAX) {
IPAERR_RL("Interface name too long. (%s)\n", rx->name);
return result;
diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_qmi_service.c b/drivers/platform/msm/ipa/ipa_v3/ipa_qmi_service.c
index 4e8c233..e3a3821 100644
--- a/drivers/platform/msm/ipa/ipa_v3/ipa_qmi_service.c
+++ b/drivers/platform/msm/ipa/ipa_v3/ipa_qmi_service.c
@@ -603,15 +603,49 @@
struct ipa_install_fltr_rule_resp_msg_v01 resp;
struct msg_desc req_desc, resp_desc;
int rc;
+ int i;
/* check if the filter rules from IPACM is valid */
- if (req->filter_spec_ex_list_len == 0) {
+ if (req->filter_spec_list_len == 0) {
IPAWANDBG("IPACM pass zero rules to Q6\n");
} else {
IPAWANDBG("IPACM pass %u rules to Q6\n",
req->filter_spec_ex_list_len);
}
+ if (req->filter_spec_list_len >= QMI_IPA_MAX_FILTERS_V01) {
+ IPAWANDBG(
+ "IPACM passes the number of filtering rules exceed limit\n");
+ return -EINVAL;
+ } else if (req->source_pipe_index_valid != 0) {
+ IPAWANDBG(
+ "IPACM passes source_pipe_index_valid not zero 0 != %d\n",
+ req->source_pipe_index_valid);
+ return -EINVAL;
+ } else if (req->source_pipe_index >= ipa3_ctx->ipa_num_pipes) {
+ IPAWANDBG(
+ "IPACM passes source pipe index not valid ID = %d\n",
+ req->source_pipe_index);
+ return -EINVAL;
+ }
+ for (i = 0; i < req->filter_spec_list_len; i++) {
+ if ((req->filter_spec_list[i].ip_type !=
+ QMI_IPA_IP_TYPE_V4_V01) &&
+ (req->filter_spec_list[i].ip_type !=
+ QMI_IPA_IP_TYPE_V6_V01))
+ return -EINVAL;
+ if (req->filter_spec_list[i].is_mux_id_valid == false)
+ return -EINVAL;
+ if (req->filter_spec_list[i].is_routing_table_index_valid
+ == false)
+ return -EINVAL;
+ if ((req->filter_spec_list[i].filter_action <=
+ QMI_IPA_FILTER_ACTION_INVALID_V01) &&
+ (req->filter_spec_list[i].filter_action >
+ QMI_IPA_FILTER_ACTION_EXCEPTION_V01))
+ return -EINVAL;
+ }
+
mutex_lock(&ipa3_qmi_lock);
if (ipa3_qmi_ctx != NULL) {
/* cache the qmi_filter_request */
@@ -653,6 +687,7 @@
struct ipa_install_fltr_rule_resp_ex_msg_v01 resp;
struct msg_desc req_desc, resp_desc;
int rc;
+ int i;
/* check if the filter rules from IPACM is valid */
if (req->filter_spec_ex_list_len == 0) {
@@ -662,6 +697,34 @@
req->filter_spec_ex_list_len);
}
+ if (req->filter_spec_ex_list_len >= QMI_IPA_MAX_FILTERS_EX_V01) {
+ IPAWANDBG(
+ "IPACM pass the number of filtering rules exceed limit\n");
+ return -EINVAL;
+ } else if (req->source_pipe_index_valid != 0) {
+ IPAWANDBG(
+ "IPACM passes source_pipe_index_valid not zero 0 != %d\n",
+ req->source_pipe_index_valid);
+ return -EINVAL;
+ }
+
+ for (i = 0; i < req->filter_spec_ex_list_len-1; i++) {
+ if ((req->filter_spec_ex_list[i].ip_type !=
+ QMI_IPA_IP_TYPE_V4_V01) &&
+ (req->filter_spec_ex_list[i].ip_type !=
+ QMI_IPA_IP_TYPE_V6_V01))
+ return -EINVAL;
+ if (req->filter_spec_ex_list[i].is_mux_id_valid == false)
+ return -EINVAL;
+ if (req->filter_spec_ex_list[i].is_routing_table_index_valid
+ == false)
+ return -EINVAL;
+ if ((req->filter_spec_ex_list[i].filter_action <=
+ QMI_IPA_FILTER_ACTION_INVALID_V01) &&
+ (req->filter_spec_ex_list[i].filter_action >
+ QMI_IPA_FILTER_ACTION_EXCEPTION_V01))
+ return -EINVAL;
+ }
mutex_lock(&ipa3_qmi_lock);
if (ipa3_qmi_ctx != NULL) {
/* cache the qmi_filter_request */
@@ -798,14 +861,37 @@
/* check if the filter rules from IPACM is valid */
if (req->rule_id_len == 0) {
- IPAWANERR(" delete UL filter rule for pipe %d\n",
+ IPAWANDBG(" delete UL filter rule for pipe %d\n",
req->source_pipe_index);
- return -EINVAL;
} else if (req->rule_id_len > QMI_IPA_MAX_FILTERS_V01) {
IPAWANERR(" UL filter rule for pipe %d exceed max (%u)\n",
req->source_pipe_index,
req->rule_id_len);
return -EINVAL;
+ } else if (req->install_status != IPA_QMI_RESULT_SUCCESS_V01) {
+ IPAWANERR(" UL filter rule for pipe %d install_status = %d\n",
+ req->source_pipe_index, req->install_status);
+ return -EINVAL;
+ } else if (req->rule_id_valid != 1) {
+ IPAWANERR(" UL filter rule for pipe %d rule_id_valid = %d\n",
+ req->source_pipe_index, req->rule_id_valid);
+ return -EINVAL;
+ } else if (req->source_pipe_index >= ipa3_ctx->ipa_num_pipes) {
+ IPAWANDBG(
+ "IPACM passes source pipe index not valid ID = %d\n",
+ req->source_pipe_index);
+ return -EINVAL;
+ } else if (((req->embedded_pipe_index_valid != true) ||
+ (req->embedded_call_mux_id_valid != true)) &&
+ ((req->embedded_pipe_index_valid != false) ||
+ (req->embedded_call_mux_id_valid != false))) {
+ IPAWANERR(
+ "IPACM passes embedded pipe and mux valid not valid\n");
+ return -EINVAL;
+ } else if (req->embedded_pipe_index >= ipa3_ctx->ipa_num_pipes) {
+ IPAWANERR("IPACM passes source pipe index not valid ID = %d\n",
+ req->source_pipe_index);
+ return -EINVAL;
}
if (req->source_pipe_index == -1) {
diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_rt.c b/drivers/platform/msm/ipa/ipa_v3/ipa_rt.c
index 8bd7d30..8d7b107 100644
--- a/drivers/platform/msm/ipa/ipa_v3/ipa_rt.c
+++ b/drivers/platform/msm/ipa/ipa_v3/ipa_rt.c
@@ -732,6 +732,7 @@
}
mutex_lock(&ipa3_ctx->lock);
+ in->name[IPA_RESOURCE_NAME_MAX-1] = '\0';
/* check if this table exists */
entry = __ipa3_find_rt_tbl(in->ip, in->name);
if (!entry) {
@@ -1081,6 +1082,7 @@
mutex_lock(&ipa3_ctx->lock);
for (i = 0; i < rules->num_rules; i++) {
+ rules->rt_tbl_name[IPA_RESOURCE_NAME_MAX-1] = '\0';
if (__ipa_add_rt_rule(rules->ip, rules->rt_tbl_name,
&rules->rules[i].rule,
rules->rules[i].at_rear,
@@ -1126,7 +1128,7 @@
}
mutex_lock(&ipa3_ctx->lock);
-
+ rules->rt_tbl_name[IPA_RESOURCE_NAME_MAX-1] = '\0';
tbl = __ipa3_find_rt_tbl(rules->ip, rules->rt_tbl_name);
if (tbl == NULL || (tbl->cookie != IPA_RT_TBL_COOKIE)) {
IPAERR_RL("failed finding rt tbl name = %s\n",
@@ -1456,6 +1458,7 @@
return -EINVAL;
}
mutex_lock(&ipa3_ctx->lock);
+ lookup->name[IPA_RESOURCE_NAME_MAX-1] = '\0';
entry = __ipa3_find_rt_tbl(lookup->ip, lookup->name);
if (entry && entry->cookie == IPA_RT_TBL_COOKIE) {
if (entry->ref_cnt == U32_MAX) {
diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_uc_offload_i.h b/drivers/platform/msm/ipa/ipa_v3/ipa_uc_offload_i.h
index 44afb28..8d415a1 100644
--- a/drivers/platform/msm/ipa/ipa_v3/ipa_uc_offload_i.h
+++ b/drivers/platform/msm/ipa/ipa_v3/ipa_uc_offload_i.h
@@ -26,6 +26,9 @@
#define IPA_NTN_TX_DIR 1
#define IPA_NTN_RX_DIR 2
+#define IPA_WDI3_TX_DIR 1
+#define IPA_WDI3_RX_DIR 2
+
/**
* @brief Enum value determined based on the feature it
* corresponds to
@@ -48,9 +51,9 @@
* @IPA_HW_FEATURE_MHI : Feature related to MHI operation in IPA HW
* @IPA_HW_FEATURE_POWER_COLLAPSE: Feature related to IPA Power collapse
* @IPA_HW_FEATURE_WDI : Feature related to WDI operation in IPA HW
- * @IPA_HW_FEATURE_ZIP: Feature related to CMP/DCMP operation in IPA HW
* @IPA_HW_FEATURE_NTN : Feature related to NTN operation in IPA HW
* @IPA_HW_FEATURE_OFFLOAD : Feature related to NTN operation in IPA HW
+ * @IPA_HW_FEATURE_WDI3 : Feature related to WDI operation in IPA HW
*/
enum ipa3_hw_features {
IPA_HW_FEATURE_COMMON = 0x0,
@@ -59,7 +62,8 @@
IPA_HW_FEATURE_WDI = 0x3,
IPA_HW_FEATURE_ZIP = 0x4,
IPA_HW_FEATURE_NTN = 0x5,
- IPA_HW_FEATURE_OFFLOAD = 0x6,
+ IPA_HW_FEATURE_OFFLOAD = 0x6,
+ IPA_HW_FEATURE_WDI3 = 0x7,
IPA_HW_FEATURE_MAX = IPA_HW_NUM_FEATURES
};
@@ -343,6 +347,33 @@
} __packed;
+struct IpaHwWdi3SetUpCmdData_t {
+ u32 transfer_ring_base_pa;
+ u32 transfer_ring_base_pa_hi;
+
+ u32 transfer_ring_size;
+
+ u32 transfer_ring_doorbell_pa;
+ u32 transfer_ring_doorbell_pa_hi;
+
+ u32 event_ring_base_pa;
+ u32 event_ring_base_pa_hi;
+
+ u32 event_ring_size;
+
+ u32 event_ring_doorbell_pa;
+ u32 event_ring_doorbell_pa_hi;
+
+ u16 num_pkt_buffers;
+ u8 ipa_pipe_number;
+ u8 dir;
+
+ u16 pkt_offset;
+ u16 reserved0;
+
+ u32 desc_format_template[IPA_HW_WDI3_MAX_ER_DESC_SIZE];
+} __packed;
+
/**
* struct Ipa3HwNtnCommonChCmdData_t - Structure holding the
* parameters for Ntn Tear down command data params
@@ -357,6 +388,13 @@
uint32_t raw32b;
} __packed;
+union IpaHwWdi3CommonChCmdData_t {
+ struct IpaHwWdi3CommonChCmdParams_t {
+ u32 ipa_pipe_number :8;
+ u32 reserved :24;
+ } __packed params;
+ u32 raw32b;
+} __packed;
/**
* struct Ipa3HwNTNErrorEventData_t - Structure holding the
@@ -447,13 +485,30 @@
* the offload commands from CPU
* @IPA_CPU_2_HW_CMD_OFFLOAD_CHANNEL_SET_UP : Command to set up
* Offload protocol's Tx/Rx Path
- * @IPA_CPU_2_HW_CMD_OFFLOAD_RX_SET_UP : Command to tear down
+ * @IPA_CPU_2_HW_CMD_OFFLOAD_TEAR_DOWN : Command to tear down
+ * Offload protocol's Tx/ Rx Path
+ * @IPA_CPU_2_HW_CMD_OFFLOAD_ENABLE : Command to enable
+ * Offload protocol's Tx/Rx Path
+ * @IPA_CPU_2_HW_CMD_OFFLOAD_DISABLE : Command to disable
+ * Offload protocol's Tx/ Rx Path
+ * @IPA_CPU_2_HW_CMD_OFFLOAD_SUSPEND : Command to suspend
+ * Offload protocol's Tx/Rx Path
+ * @IPA_CPU_2_HW_CMD_OFFLOAD_RESUME : Command to resume
* Offload protocol's Tx/ Rx Path
*/
enum ipa_cpu_2_hw_offload_commands {
IPA_CPU_2_HW_CMD_OFFLOAD_CHANNEL_SET_UP =
FEATURE_ENUM_VAL(IPA_HW_FEATURE_OFFLOAD, 1),
- IPA_CPU_2_HW_CMD_OFFLOAD_TEAR_DOWN,
+ IPA_CPU_2_HW_CMD_OFFLOAD_TEAR_DOWN =
+ FEATURE_ENUM_VAL(IPA_HW_FEATURE_OFFLOAD, 2),
+ IPA_CPU_2_HW_CMD_OFFLOAD_ENABLE =
+ FEATURE_ENUM_VAL(IPA_HW_FEATURE_OFFLOAD, 3),
+ IPA_CPU_2_HW_CMD_OFFLOAD_DISABLE =
+ FEATURE_ENUM_VAL(IPA_HW_FEATURE_OFFLOAD, 4),
+ IPA_CPU_2_HW_CMD_OFFLOAD_SUSPEND =
+ FEATURE_ENUM_VAL(IPA_HW_FEATURE_OFFLOAD, 5),
+ IPA_CPU_2_HW_CMD_OFFLOAD_RESUME =
+ FEATURE_ENUM_VAL(IPA_HW_FEATURE_OFFLOAD, 6),
};
@@ -523,6 +578,7 @@
*/
union IpaHwSetUpCmd {
struct Ipa3HwNtnSetUpCmdData_t NtnSetupCh_params;
+ struct IpaHwWdi3SetUpCmdData_t Wdi3SetupCh_params;
} __packed;
/**
@@ -553,6 +609,7 @@
*/
union IpaHwCommonChCmd {
union Ipa3HwNtnCommonChCmdData_t NtnCommonCh_params;
+ union IpaHwWdi3CommonChCmdData_t Wdi3CommonCh_params;
} __packed;
struct IpaHwOffloadCommonChCmdData_t {
diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_uc_wdi.c b/drivers/platform/msm/ipa/ipa_v3/ipa_uc_wdi.c
index c97d2b3..b8928da 100644
--- a/drivers/platform/msm/ipa/ipa_v3/ipa_uc_wdi.c
+++ b/drivers/platform/msm/ipa/ipa_v3/ipa_uc_wdi.c
@@ -672,19 +672,19 @@
unsigned long *iova)
{
/* support for SMMU on WLAN but no SMMU on IPA */
- if (wlan_smmu_en && ipa3_ctx->smmu_s1_bypass) {
+ if (wlan_smmu_en && ipa3_ctx->s1_bypass_arr[IPA_SMMU_CB_UC]) {
IPAERR("Unsupported SMMU pairing\n");
return -EINVAL;
}
/* legacy: no SMMUs on either end */
- if (!wlan_smmu_en && ipa3_ctx->smmu_s1_bypass) {
+ if (!wlan_smmu_en && ipa3_ctx->s1_bypass_arr[IPA_SMMU_CB_UC]) {
*iova = pa;
return 0;
}
/* no SMMU on WLAN but SMMU on IPA */
- if (!wlan_smmu_en && !ipa3_ctx->smmu_s1_bypass) {
+ if (!wlan_smmu_en && !ipa3_ctx->s1_bypass_arr[IPA_SMMU_CB_UC]) {
if (ipa_create_uc_smmu_mapping_pa(pa, len,
(res_idx == IPA_WDI_CE_DB_RES) ? true : false, iova)) {
IPAERR("Fail to create mapping res %d\n", res_idx);
@@ -695,7 +695,7 @@
}
/* SMMU on WLAN and SMMU on IPA */
- if (wlan_smmu_en && !ipa3_ctx->smmu_s1_bypass) {
+ if (wlan_smmu_en && !ipa3_ctx->s1_bypass_arr[IPA_SMMU_CB_UC]) {
switch (res_idx) {
case IPA_WDI_RX_RING_RP_RES:
case IPA_WDI_RX_COMP_RING_WP_RES:
@@ -1821,6 +1821,11 @@
return -EINVAL;
}
+ if (ipa3_ctx->s1_bypass_arr[IPA_SMMU_CB_WLAN]) {
+ IPAERR("IPA SMMU not enabled\n");
+ return -EINVAL;
+ }
+
for (i = 0; i < num_buffers; i++) {
IPADBG("i=%d pa=0x%pa iova=0x%lx sz=0x%zx\n", i,
&info[i].pa, info[i].iova, info[i].size);
diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_utils.c b/drivers/platform/msm/ipa/ipa_v3/ipa_utils.c
index f717264..ae05880 100644
--- a/drivers/platform/msm/ipa/ipa_v3/ipa_utils.c
+++ b/drivers/platform/msm/ipa/ipa_v3/ipa_utils.c
@@ -4497,6 +4497,10 @@
api_ctrl->ipa_get_pdev = ipa3_get_pdev;
api_ctrl->ipa_ntn_uc_reg_rdyCB = ipa3_ntn_uc_reg_rdyCB;
api_ctrl->ipa_ntn_uc_dereg_rdyCB = ipa3_ntn_uc_dereg_rdyCB;
+ api_ctrl->ipa_conn_wdi3_pipes = ipa3_conn_wdi3_pipes;
+ api_ctrl->ipa_disconn_wdi3_pipes = ipa3_disconn_wdi3_pipes;
+ api_ctrl->ipa_enable_wdi3_pipes = ipa3_enable_wdi3_pipes;
+ api_ctrl->ipa_disable_wdi3_pipes = ipa3_disable_wdi3_pipes;
return 0;
}
diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_wdi3_i.c b/drivers/platform/msm/ipa/ipa_v3/ipa_wdi3_i.c
new file mode 100644
index 0000000..7801745
--- /dev/null
+++ b/drivers/platform/msm/ipa/ipa_v3/ipa_wdi3_i.c
@@ -0,0 +1,407 @@
+/* Copyright (c) 2017, 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 "ipa_i.h"
+#include <linux/ipa_wdi3.h>
+
+#define IPA_HW_WDI3_RX_MBOX_START_INDEX 48
+#define IPA_HW_WDI3_TX_MBOX_START_INDEX 50
+
+static int ipa3_send_wdi3_setup_pipe_cmd(
+ struct ipa_wdi3_setup_info *info, u8 dir)
+{
+ int ipa_ep_idx;
+ int result = 0;
+ struct ipa_mem_buffer cmd;
+ struct IpaHwWdi3SetUpCmdData_t *wdi3_params;
+ struct IpaHwOffloadSetUpCmdData_t *cmd_data;
+
+ if (info == NULL) {
+ IPAERR("invalid input\n");
+ return -EINVAL;
+ }
+
+ ipa_ep_idx = ipa_get_ep_mapping(info->client);
+ if (ipa_ep_idx == -1) {
+ IPAERR("fail to get ep idx.\n");
+ return -EFAULT;
+ }
+
+ IPADBG("client=%d ep=%d\n", info->client, ipa_ep_idx);
+ IPADBG("ring_base_pa = 0x%pad\n", &info->transfer_ring_base_pa);
+ IPADBG("ring_size = %hu\n", info->transfer_ring_size);
+ IPADBG("ring_db_pa = 0x%pad\n", &info->transfer_ring_doorbell_pa);
+ IPADBG("evt_ring_base_pa = 0x%pad\n", &info->event_ring_base_pa);
+ IPADBG("evt_ring_size = %hu\n", info->event_ring_size);
+ IPADBG("evt_ring_db_pa = 0x%pad\n", &info->event_ring_doorbell_pa);
+ IPADBG("num_pkt_buffers = %hu\n", info->num_pkt_buffers);
+ IPADBG("pkt_offset = %d\n", info->pkt_offset);
+
+ cmd.size = sizeof(*cmd_data);
+ cmd.base = dma_alloc_coherent(ipa3_ctx->uc_pdev, cmd.size,
+ &cmd.phys_base, GFP_KERNEL);
+ if (cmd.base == NULL) {
+ IPAERR("fail to get DMA memory.\n");
+ return -ENOMEM;
+ }
+
+ cmd_data = (struct IpaHwOffloadSetUpCmdData_t *)cmd.base;
+ cmd_data->protocol = IPA_HW_FEATURE_WDI3;
+
+ wdi3_params = &cmd_data->SetupCh_params.Wdi3SetupCh_params;
+ wdi3_params->transfer_ring_base_pa = (u32)info->transfer_ring_base_pa;
+ wdi3_params->transfer_ring_base_pa_hi =
+ (u32)((u64)info->transfer_ring_base_pa >> 32);
+ wdi3_params->transfer_ring_size = info->transfer_ring_size;
+ wdi3_params->transfer_ring_doorbell_pa =
+ (u32)info->transfer_ring_doorbell_pa;
+ wdi3_params->transfer_ring_doorbell_pa_hi =
+ (u32)((u64)info->transfer_ring_doorbell_pa >> 32);
+ wdi3_params->event_ring_base_pa = (u32)info->event_ring_base_pa;
+ wdi3_params->event_ring_base_pa_hi =
+ (u32)((u64)info->event_ring_base_pa >> 32);
+ wdi3_params->event_ring_size = info->event_ring_size;
+ wdi3_params->event_ring_doorbell_pa =
+ (u32)info->event_ring_doorbell_pa;
+ wdi3_params->event_ring_doorbell_pa_hi =
+ (u32)((u64)info->event_ring_doorbell_pa >> 32);
+ wdi3_params->num_pkt_buffers = info->num_pkt_buffers;
+ wdi3_params->ipa_pipe_number = ipa_ep_idx;
+ wdi3_params->dir = dir;
+ wdi3_params->pkt_offset = info->pkt_offset;
+ memcpy(wdi3_params->desc_format_template, info->desc_format_template,
+ sizeof(wdi3_params->desc_format_template));
+
+ result = ipa3_uc_send_cmd((u32)(cmd.phys_base),
+ IPA_CPU_2_HW_CMD_OFFLOAD_CHANNEL_SET_UP,
+ IPA_HW_2_CPU_OFFLOAD_CMD_STATUS_SUCCESS,
+ false, 10*HZ);
+ if (result) {
+ IPAERR("uc setup channel cmd failed: %d\n", result);
+ result = -EFAULT;
+ }
+
+ dma_free_coherent(ipa3_ctx->uc_pdev, cmd.size, cmd.base, cmd.phys_base);
+ return result;
+}
+
+int ipa3_conn_wdi3_pipes(struct ipa_wdi3_conn_in_params *in,
+ struct ipa_wdi3_conn_out_params *out)
+{
+ struct ipa3_ep_context *ep_rx;
+ struct ipa3_ep_context *ep_tx;
+ int ipa_ep_idx_rx;
+ int ipa_ep_idx_tx;
+ int result = 0;
+
+ if (in == NULL || out == NULL) {
+ IPAERR("invalid input\n");
+ return -EINVAL;
+ }
+
+ ipa_ep_idx_rx = ipa_get_ep_mapping(in->rx.client);
+ ipa_ep_idx_tx = ipa_get_ep_mapping(in->tx.client);
+ if (ipa_ep_idx_rx == -1 || ipa_ep_idx_tx == -1) {
+ IPAERR("fail to alloc EP.\n");
+ return -EFAULT;
+ }
+ if (ipa_ep_idx_rx >= IPA3_MAX_NUM_PIPES ||
+ ipa_ep_idx_tx >= IPA3_MAX_NUM_PIPES) {
+ IPAERR("ep out of range.\n");
+ return -EFAULT;
+ }
+
+ ep_rx = &ipa3_ctx->ep[ipa_ep_idx_rx];
+ ep_tx = &ipa3_ctx->ep[ipa_ep_idx_tx];
+
+ if (ep_rx->valid || ep_tx->valid) {
+ IPAERR("EP already allocated.\n");
+ return -EFAULT;
+ }
+
+ memset(ep_rx, 0, offsetof(struct ipa3_ep_context, sys));
+ memset(ep_tx, 0, offsetof(struct ipa3_ep_context, sys));
+
+ IPA_ACTIVE_CLIENTS_INC_SIMPLE();
+
+ /* setup rx ep cfg */
+ ep_rx->valid = 1;
+ ep_rx->client = in->rx.client;
+ result = ipa3_disable_data_path(ipa_ep_idx_rx);
+ if (result) {
+ IPAERR("disable data path failed res=%d clnt=%d.\n", result,
+ ipa_ep_idx_rx);
+ IPA_ACTIVE_CLIENTS_DEC_SIMPLE();
+ return -EFAULT;
+ }
+ ep_rx->client_notify = in->notify;
+ ep_rx->priv = in->priv;
+
+ memcpy(&ep_rx->cfg, &in->rx.ipa_ep_cfg, sizeof(ep_rx->cfg));
+
+ if (ipa3_cfg_ep(ipa_ep_idx_rx, &ep_rx->cfg)) {
+ IPAERR("fail to setup rx pipe cfg\n");
+ result = -EFAULT;
+ goto fail;
+ }
+
+ if (ipa3_send_wdi3_setup_pipe_cmd(&in->rx, IPA_WDI3_RX_DIR)) {
+ IPAERR("fail to send cmd to uc for rx pipe\n");
+ result = -EFAULT;
+ goto fail;
+ }
+ ipa3_install_dflt_flt_rules(ipa_ep_idx_rx);
+ out->rx_uc_db_pa = ipa3_ctx->ipa_wrapper_base +
+ ipahal_get_reg_base() +
+ ipahal_get_reg_mn_ofst(IPA_UC_MAILBOX_m_n,
+ IPA_HW_WDI3_RX_MBOX_START_INDEX/32,
+ IPA_HW_WDI3_RX_MBOX_START_INDEX % 32);
+
+ IPADBG("client %d (ep: %d) connected\n", in->rx.client,
+ ipa_ep_idx_rx);
+
+ /* setup dl ep cfg */
+ ep_tx->valid = 1;
+ ep_tx->client = in->tx.client;
+ result = ipa3_disable_data_path(ipa_ep_idx_tx);
+ if (result) {
+ IPAERR("disable data path failed res=%d ep=%d.\n", result,
+ ipa_ep_idx_tx);
+ result = -EFAULT;
+ goto fail;
+ }
+
+ memcpy(&ep_tx->cfg, &in->tx.ipa_ep_cfg, sizeof(ep_tx->cfg));
+
+ if (ipa3_cfg_ep(ipa_ep_idx_tx, &ep_tx->cfg)) {
+ IPAERR("fail to setup tx pipe cfg\n");
+ result = -EFAULT;
+ goto fail;
+ }
+
+ if (ipa3_send_wdi3_setup_pipe_cmd(&in->tx, IPA_WDI3_TX_DIR)) {
+ IPAERR("fail to send cmd to uc for tx pipe\n");
+ result = -EFAULT;
+ goto fail;
+ }
+ out->tx_uc_db_pa = ipa3_ctx->ipa_wrapper_base +
+ ipahal_get_reg_base() +
+ ipahal_get_reg_mn_ofst(IPA_UC_MAILBOX_m_n,
+ IPA_HW_WDI3_TX_MBOX_START_INDEX/32,
+ IPA_HW_WDI3_TX_MBOX_START_INDEX % 32);
+ out->tx_uc_db_va = ioremap(out->tx_uc_db_pa, 4);
+ if (!out->tx_uc_db_va) {
+ IPAERR("fail to ioremap tx uc db\n");
+ result = -EFAULT;
+ goto fail;
+ }
+ IPADBG("client %d (ep: %d) connected\n", in->tx.client,
+ ipa_ep_idx_tx);
+
+fail:
+ IPA_ACTIVE_CLIENTS_DEC_SIMPLE();
+ return result;
+}
+
+static int ipa3_send_wdi3_common_ch_cmd(int ipa_ep_idx, int command)
+{
+ struct ipa_mem_buffer cmd;
+ struct IpaHwOffloadCommonChCmdData_t *cmd_data;
+ union IpaHwWdi3CommonChCmdData_t *wdi3;
+ int result = 0;
+
+ cmd.size = sizeof(*cmd_data);
+ cmd.base = dma_alloc_coherent(ipa3_ctx->uc_pdev, cmd.size,
+ &cmd.phys_base, GFP_KERNEL);
+ if (cmd.base == NULL) {
+ IPAERR("fail to get DMA memory.\n");
+ return -ENOMEM;
+ }
+
+ IPA_ACTIVE_CLIENTS_INC_SIMPLE();
+ /* enable the TX pipe */
+ cmd_data = (struct IpaHwOffloadCommonChCmdData_t *)cmd.base;
+ cmd_data->protocol = IPA_HW_FEATURE_WDI3;
+
+ wdi3 = &cmd_data->CommonCh_params.Wdi3CommonCh_params;
+ wdi3->params.ipa_pipe_number = ipa_ep_idx;
+ result = ipa3_uc_send_cmd((u32)(cmd.phys_base), command,
+ IPA_HW_2_CPU_OFFLOAD_CMD_STATUS_SUCCESS,
+ false, 10*HZ);
+ if (result) {
+ result = -EFAULT;
+ goto fail;
+ }
+
+fail:
+ dma_free_coherent(ipa3_ctx->uc_pdev, cmd.size, cmd.base, cmd.phys_base);
+ IPA_ACTIVE_CLIENTS_DEC_SIMPLE();
+ return result;
+}
+
+int ipa3_disconn_wdi3_pipes(int ipa_ep_idx_tx, int ipa_ep_idx_rx)
+{
+ struct ipa3_ep_context *ep_tx, *ep_rx;
+ int result = 0;
+
+ IPADBG("ep_tx = %d\n", ipa_ep_idx_tx);
+ IPADBG("ep_rx = %d\n", ipa_ep_idx_rx);
+
+ if (ipa_ep_idx_tx < 0 || ipa_ep_idx_tx >= IPA3_MAX_NUM_PIPES ||
+ ipa_ep_idx_rx < 0 || ipa_ep_idx_rx >= IPA3_MAX_NUM_PIPES) {
+ IPAERR("invalid ipa ep index\n");
+ return -EINVAL;
+ }
+
+ ep_tx = &ipa3_ctx->ep[ipa_ep_idx_tx];
+ ep_rx = &ipa3_ctx->ep[ipa_ep_idx_rx];
+
+ /* tear down tx pipe */
+ if (ipa3_send_wdi3_common_ch_cmd(ipa_ep_idx_tx,
+ IPA_CPU_2_HW_CMD_OFFLOAD_TEAR_DOWN)) {
+ IPAERR("fail to tear down tx pipe\n");
+ result = -EFAULT;
+ goto fail;
+ }
+ ipa3_disable_data_path(ipa_ep_idx_tx);
+ memset(ep_tx, 0, sizeof(struct ipa3_ep_context));
+ IPADBG("tx client (ep: %d) disconnected\n", ipa_ep_idx_tx);
+
+ /* tear down rx pipe */
+ if (ipa3_send_wdi3_common_ch_cmd(ipa_ep_idx_rx,
+ IPA_CPU_2_HW_CMD_OFFLOAD_TEAR_DOWN)) {
+ IPAERR("fail to tear down rx pipe\n");
+ result = -EFAULT;
+ goto fail;
+ }
+ ipa3_disable_data_path(ipa_ep_idx_rx);
+ ipa3_delete_dflt_flt_rules(ipa_ep_idx_rx);
+ memset(ep_rx, 0, sizeof(struct ipa3_ep_context));
+ IPADBG("rx client (ep: %d) disconnected\n", ipa_ep_idx_rx);
+
+fail:
+ return result;
+}
+
+int ipa3_enable_wdi3_pipes(int ipa_ep_idx_tx, int ipa_ep_idx_rx)
+{
+ struct ipa3_ep_context *ep_tx, *ep_rx;
+ int result = 0;
+
+ IPADBG("ep_tx = %d\n", ipa_ep_idx_tx);
+ IPADBG("ep_rx = %d\n", ipa_ep_idx_rx);
+
+ ep_tx = &ipa3_ctx->ep[ipa_ep_idx_tx];
+ ep_rx = &ipa3_ctx->ep[ipa_ep_idx_rx];
+
+ /* enable tx pipe */
+ if (ipa3_send_wdi3_common_ch_cmd(ipa_ep_idx_tx,
+ IPA_CPU_2_HW_CMD_OFFLOAD_ENABLE)) {
+ IPAERR("fail to enable tx pipe\n");
+ result = -EFAULT;
+ goto fail;
+ }
+
+ /* resume tx pipe */
+ if (ipa3_send_wdi3_common_ch_cmd(ipa_ep_idx_tx,
+ IPA_CPU_2_HW_CMD_OFFLOAD_RESUME)) {
+ IPAERR("fail to resume tx pipe\n");
+ result = -EFAULT;
+ goto fail;
+ }
+
+ /* enable rx pipe */
+ if (ipa3_send_wdi3_common_ch_cmd(ipa_ep_idx_rx,
+ IPA_CPU_2_HW_CMD_OFFLOAD_ENABLE)) {
+ IPAERR("fail to enable rx pipe\n");
+ result = -EFAULT;
+ goto fail;
+ }
+
+ /* resume rx pipe */
+ if (ipa3_send_wdi3_common_ch_cmd(ipa_ep_idx_rx,
+ IPA_CPU_2_HW_CMD_OFFLOAD_RESUME)) {
+ IPAERR("fail to resume rx pipe\n");
+ result = -EFAULT;
+ goto fail;
+ }
+
+ IPA_ACTIVE_CLIENTS_INC_SIMPLE();
+
+ /* enable data path */
+ result = ipa3_enable_data_path(ipa_ep_idx_rx);
+ if (result) {
+ IPAERR("enable data path failed res=%d clnt=%d.\n", result,
+ ipa_ep_idx_rx);
+ IPA_ACTIVE_CLIENTS_DEC_SIMPLE();
+ return -EFAULT;
+ }
+
+ result = ipa3_enable_data_path(ipa_ep_idx_tx);
+ if (result) {
+ IPAERR("enable data path failed res=%d clnt=%d.\n", result,
+ ipa_ep_idx_tx);
+ IPA_ACTIVE_CLIENTS_DEC_SIMPLE();
+ return -EFAULT;
+ }
+
+ IPA_ACTIVE_CLIENTS_DEC_SIMPLE();
+
+fail:
+ return result;
+}
+
+int ipa3_disable_wdi3_pipes(int ipa_ep_idx_tx, int ipa_ep_idx_rx)
+{
+ struct ipa3_ep_context *ep_tx, *ep_rx;
+ int result = 0;
+
+ IPADBG("ep_tx = %d\n", ipa_ep_idx_tx);
+ IPADBG("ep_rx = %d\n", ipa_ep_idx_rx);
+
+ ep_tx = &ipa3_ctx->ep[ipa_ep_idx_tx];
+ ep_rx = &ipa3_ctx->ep[ipa_ep_idx_rx];
+
+ /* suspend tx pipe */
+ if (ipa3_send_wdi3_common_ch_cmd(ipa_ep_idx_tx,
+ IPA_CPU_2_HW_CMD_OFFLOAD_SUSPEND)) {
+ IPAERR("fail to suspend tx pipe\n");
+ result = -EFAULT;
+ goto fail;
+ }
+
+ /* disable tx pipe */
+ if (ipa3_send_wdi3_common_ch_cmd(ipa_ep_idx_tx,
+ IPA_CPU_2_HW_CMD_OFFLOAD_DISABLE)) {
+ IPAERR("fail to disable tx pipe\n");
+ result = -EFAULT;
+ goto fail;
+ }
+
+ /* suspend rx pipe */
+ if (ipa3_send_wdi3_common_ch_cmd(ipa_ep_idx_rx,
+ IPA_CPU_2_HW_CMD_OFFLOAD_SUSPEND)) {
+ IPAERR("fail to suspend rx pipe\n");
+ result = -EFAULT;
+ goto fail;
+ }
+
+ /* disable rx pipe */
+ if (ipa3_send_wdi3_common_ch_cmd(ipa_ep_idx_rx,
+ IPA_CPU_2_HW_CMD_OFFLOAD_DISABLE)) {
+ IPAERR("fail to disable rx pipe\n");
+ result = -EFAULT;
+ goto fail;
+ }
+
+fail:
+ return result;
+}
diff --git a/drivers/platform/msm/ipa/ipa_v3/ipahal/ipahal_fltrt.c b/drivers/platform/msm/ipa/ipa_v3/ipahal/ipahal_fltrt.c
index acc72f0..d6dbc85 100644
--- a/drivers/platform/msm/ipa/ipa_v3/ipahal/ipahal_fltrt.c
+++ b/drivers/platform/msm/ipa/ipa_v3/ipahal/ipahal_fltrt.c
@@ -1500,10 +1500,10 @@
static int ipa_fltrt_generate_hw_rule_bdy_from_eq(
const struct ipa_ipfltri_rule_eq *attrib, u8 **buf)
{
- int num_offset_meq_32 = attrib->num_offset_meq_32;
- int num_ihl_offset_range_16 = attrib->num_ihl_offset_range_16;
- int num_ihl_offset_meq_32 = attrib->num_ihl_offset_meq_32;
- int num_offset_meq_128 = attrib->num_offset_meq_128;
+ uint8_t num_offset_meq_32 = attrib->num_offset_meq_32;
+ uint8_t num_ihl_offset_range_16 = attrib->num_ihl_offset_range_16;
+ uint8_t num_ihl_offset_meq_32 = attrib->num_ihl_offset_meq_32;
+ uint8_t num_offset_meq_128 = attrib->num_offset_meq_128;
int i;
int extra_bytes;
u8 *extra;
diff --git a/drivers/platform/msm/ipa/ipa_v3/rmnet_ipa.c b/drivers/platform/msm/ipa/ipa_v3/rmnet_ipa.c
index f08e1e3..e93210d 100644
--- a/drivers/platform/msm/ipa/ipa_v3/rmnet_ipa.c
+++ b/drivers/platform/msm/ipa/ipa_v3/rmnet_ipa.c
@@ -677,6 +677,8 @@
param->global = false;
param->num_rules = (uint8_t)1;
+ memset(req, 0, sizeof(struct ipa_fltr_installed_notif_req_msg_v01));
+
for (i = 0; i < rmnet_ipa3_ctx->num_q6_rules; i++) {
param->ip = ipa3_qmi_ctx->q6_ul_filter_rule[i].ip;
memset(&flt_rule_entry, 0, sizeof(struct ipa_flt_rule_add));
@@ -1639,8 +1641,8 @@
/* Get driver name */
case RMNET_IOCTL_GET_DRIVER_NAME:
memcpy(&extend_ioctl_data.u.if_name,
- IPA_NETDEV()->name,
- sizeof(IFNAMSIZ));
+ IPA_NETDEV()->name, IFNAMSIZ);
+ extend_ioctl_data.u.if_name[IFNAMSIZ - 1] = '\0';
if (copy_to_user((u8 *)ifr->ifr_ifru.ifru_data,
&extend_ioctl_data,
sizeof(struct rmnet_ioctl_extended_s)))
@@ -1665,6 +1667,8 @@
add_mux_channel_lock);
return -EFAULT;
}
+ extend_ioctl_data.u.rmnet_mux_val.vchannel_name
+ [IFNAMSIZ-1] = '\0';
IPAWANDBG("ADD_MUX_CHANNEL(%d, name: %s)\n",
extend_ioctl_data.u.rmnet_mux_val.mux_id,
extend_ioctl_data.u.rmnet_mux_val.vchannel_name);
@@ -1731,6 +1735,7 @@
sizeof(wan_msg->upstream_ifname);
strlcpy(wan_msg->upstream_ifname,
extend_ioctl_data.u.if_name, len);
+ wan_msg->upstream_ifname[len-1] = '\0';
memset(&msg_meta, 0, sizeof(struct ipa_msg_meta));
msg_meta.msg_type = WAN_XLAT_CONNECT;
msg_meta.msg_len = sizeof(struct ipa_wan_msg);
@@ -2984,6 +2989,9 @@
enum ipa_upstream_type upstream_type;
int rc = 0;
+ /* prevent string buffer overflows */
+ data->interface_name[IFNAMSIZ-1] = '\0';
+
/* get IPA backhaul type */
upstream_type = find_upstream_type(data->interface_name);
@@ -3276,6 +3284,10 @@
enum ipa_upstream_type upstream_type;
int rc = 0;
+ /* prevent string buffer overflows */
+ data->upstreamIface[IFNAMSIZ-1] = '\0';
+ data->tetherIface[IFNAMSIZ-1] = '\0';
+
/* get IPA backhaul type */
upstream_type = find_upstream_type(data->upstreamIface);
@@ -3310,6 +3322,10 @@
int rc = 0;
memset(&tether_stats, 0, sizeof(struct wan_ioctl_query_tether_stats));
+
+ /* prevent string buffer overflows */
+ data->upstreamIface[IFNAMSIZ-1] = '\0';
+
/* get IPA backhaul type */
upstream_type = find_upstream_type(data->upstreamIface);
@@ -3353,6 +3369,9 @@
memset(&tether_stats, 0, sizeof(struct wan_ioctl_query_tether_stats));
+ /* prevent string buffer overflows */
+ data->upstreamIface[IFNAMSIZ-1] = '\0';
+
/* get IPA backhaul type */
upstream_type = find_upstream_type(data->upstreamIface);
diff --git a/drivers/platform/msm/ipa/test/ipa_test_dma.c b/drivers/platform/msm/ipa/test/ipa_test_dma.c
index 07be1c3..aa33412 100644
--- a/drivers/platform/msm/ipa/test/ipa_test_dma.c
+++ b/drivers/platform/msm/ipa/test/ipa_test_dma.c
@@ -364,11 +364,11 @@
}
/**
- * TEST: test control API - enable/disable dma
+ * TEST: test enable/disable dma
* 1. enable dma
* 2. disable dma
*/
-static int ipa_test_dma_control_api(void *priv)
+static int ipa_test_dma_enable_disable(void *priv)
{
int rc;
@@ -392,6 +392,92 @@
}
/**
+ * TEST: test init/enable/disable/destroy dma
+ * 1. init dma
+ * 2. enable dma
+ * 3. disable dma
+ * 4. destroy dma
+ */
+static int ipa_test_dma_init_enbl_disable_destroy(void *priv)
+{
+ int rc;
+
+ IPA_UT_LOG("Test Start\n");
+
+ rc = ipa_dma_init();
+ if (rc) {
+ IPA_UT_LOG("DMA Init failed rc=%d\n", rc);
+ IPA_UT_TEST_FAIL_REPORT("fail init dma");
+ return rc;
+ }
+
+ rc = ipa_dma_enable();
+ if (rc) {
+ ipa_dma_destroy();
+ IPA_UT_LOG("DMA enable failed rc=%d\n", rc);
+ IPA_UT_TEST_FAIL_REPORT("fail enable dma");
+ return rc;
+ }
+
+ rc = ipa_dma_disable();
+ if (rc) {
+ IPA_UT_LOG("DMA disable failed rc=%d\n", rc);
+ IPA_UT_TEST_FAIL_REPORT("fail disable dma");
+ return rc;
+ }
+
+ ipa_dma_destroy();
+
+ return 0;
+}
+
+/**
+ * TEST: test enablex2/disablex2 dma
+ * 1. enable dma
+ * 2. enable dma
+ * 3. disable dma
+ * 4. disable dma
+ */
+static int ipa_test_dma_enblx2_disablex2(void *priv)
+{
+ int rc;
+
+ IPA_UT_LOG("Test Start\n");
+
+ rc = ipa_dma_enable();
+ if (rc) {
+ ipa_dma_destroy();
+ IPA_UT_LOG("DMA enable failed rc=%d\n", rc);
+ IPA_UT_TEST_FAIL_REPORT("fail enable dma");
+ return rc;
+ }
+
+ rc = ipa_dma_enable();
+ if (rc) {
+ ipa_dma_destroy();
+ IPA_UT_LOG("DMA enable failed rc=%d\n", rc);
+ IPA_UT_TEST_FAIL_REPORT("fail enable dma");
+ return rc;
+ }
+
+ rc = ipa_dma_disable();
+ if (rc) {
+ IPA_UT_LOG("DMA disable failed rc=%d\n", rc);
+ IPA_UT_TEST_FAIL_REPORT("fail disable dma");
+ return rc;
+ }
+
+ rc = ipa_dma_disable();
+ if (rc) {
+ IPA_UT_LOG("DMA disable failed rc=%d\n", rc);
+ IPA_UT_TEST_FAIL_REPORT("fail disable dma");
+ return rc;
+ }
+
+ return 0;
+}
+
+/**
* TEST: memcpy before dma enable
*
* 1. sync memcpy - should fail
@@ -999,9 +1085,17 @@
IPA_UT_DEFINE_SUITE_START(dma, "DMA for GSI",
ipa_test_dma_setup, ipa_test_dma_teardown)
{
- IPA_UT_ADD_TEST(control_api,
- "Control API",
- ipa_test_dma_control_api,
+ IPA_UT_ADD_TEST(init_enable_disable_destroy,
+ "Init->Enable->Disable->Destroy",
+ ipa_test_dma_enable_disable,
+ true, IPA_HW_v3_0, IPA_HW_MAX),
+ IPA_UT_ADD_TEST(initx2_enable_disable_destroyx2,
+ "Initx2->Enable->Disable->Destroyx2",
+ ipa_test_dma_init_enbl_disable_destroy,
+ true, IPA_HW_v3_0, IPA_HW_MAX),
+ IPA_UT_ADD_TEST(init_enablex2_disablex2_destroy,
+ "Init->Enablex2->Disablex2->Destroy",
+ ipa_test_dma_enblx2_disablex2,
true, IPA_HW_v3_0, IPA_HW_MAX),
IPA_UT_ADD_TEST(memcpy_before_enable,
"Call memcpy before dma enable and expect it to fail",
diff --git a/drivers/platform/msm/ipa/test/ipa_test_mhi.c b/drivers/platform/msm/ipa/test/ipa_test_mhi.c
index 3a89c7d..78cdc50 100644
--- a/drivers/platform/msm/ipa/test/ipa_test_mhi.c
+++ b/drivers/platform/msm/ipa/test/ipa_test_mhi.c
@@ -868,7 +868,7 @@
*
* To be run during tests
* 1. MHI init (Ready state)
- * 2. Conditional MHO start and connect (M0 state)
+ * 2. Conditional MHI start and connect (M0 state)
*/
static int ipa_mhi_test_initialize_driver(bool skip_start_and_conn)
{
@@ -879,7 +879,6 @@
struct ipa_mhi_connect_params cons_params;
struct ipa_mhi_mmio_register_set *p_mmio;
struct ipa_mhi_channel_context_array *p_ch_ctx_array;
- bool is_dma;
u64 phys_addr;
IPA_UT_LOG("Entry\n");
@@ -912,29 +911,6 @@
return -ETIME;
}
- if (ipa_mhi_is_using_dma(&is_dma)) {
- IPA_UT_LOG("is_dma checkign failed. Is MHI loaded?\n");
- IPA_UT_TEST_FAIL_REPORT("failed checking using dma");
- return -EPERM;
- }
-
- if (is_dma) {
- IPA_UT_LOG("init ipa_dma\n");
- rc = ipa_dma_init();
- if (rc && rc != -EFAULT) {
- IPA_UT_LOG("ipa_dma_init failed, %d\n", rc);
- IPA_UT_TEST_FAIL_REPORT("failed init dma");
- return rc;
- }
- IPA_UT_LOG("enable ipa_dma\n");
- rc = ipa_dma_enable();
- if (rc && rc != -EPERM) {
- IPA_UT_LOG("ipa_dma_enable failed, %d\n", rc);
- IPA_UT_TEST_FAIL_REPORT("failed enable dma");
- return rc;
- }
- }
-
if (!skip_start_and_conn) {
memset(&start_params, 0, sizeof(start_params));
start_params.channel_context_array_addr = p_mmio->ccabap;
@@ -1545,7 +1521,7 @@
}
if (!should_success && rc != -EAGAIN) {
- IPA_UT_LOG("ipa_mhi_suspenddid not return -EAGAIN fail %d\n",
+ IPA_UT_LOG("ipa_mhi_suspend did not return -EAGAIN fail %d\n",
rc);
IPA_UT_TEST_FAIL_REPORT("suspend succeeded unexpectedly");
return -EFAULT;
diff --git a/drivers/platform/msm/ipa/test/ipa_ut_framework.c b/drivers/platform/msm/ipa/test/ipa_ut_framework.c
index 3bf9ac1..036902a 100644
--- a/drivers/platform/msm/ipa/test/ipa_ut_framework.c
+++ b/drivers/platform/msm/ipa/test/ipa_ut_framework.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2017, 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 @@
* @ipa_dbgfs_root: IPA root debugfs folder
* @test_dbgfs_root: UT root debugfs folder. Sub-folder of IPA root
* @test_dbgfs_suites: Suites root debugfs folder. Sub-folder of UT root
+ * @wq: workqueue struct for write operations
*/
struct ipa_ut_context {
bool inited;
@@ -43,6 +44,17 @@
struct dentry *ipa_dbgfs_root;
struct dentry *test_dbgfs_root;
struct dentry *test_dbgfs_suites;
+ struct workqueue_struct *wq;
+};
+
+/**
+ * struct ipa_ut_dbgfs_test_write_work_ctx - work_queue context
+ * @dbgfs: work_struct for the write_work
+ * @file: file to be writen to
+ */
+struct ipa_ut_dbgfs_test_write_work_ctx {
+ struct work_struct dbgfs_work;
+ struct file *file;
};
static ssize_t ipa_ut_dbgfs_enable_read(struct file *file,
@@ -183,8 +195,9 @@
}
/**
- * ipa_ut_dbgfs_meta_test_write() - Debugfs write func for a for a meta test
- * @params: write fops
+ * ipa_ut_dbgfs_meta_test_write_work_func() - Debugfs write func for a
+ * for a meta test
+ * @params: work struct containing write fops and completion object
*
* Used to run all/regression tests in a suite
* Create log buffer that the test can use to store ongoing logs
@@ -202,19 +215,28 @@
*
* Return: Negative in failure, given characters amount in success
*/
-static ssize_t ipa_ut_dbgfs_meta_test_write(struct file *file,
- const char __user *buf, size_t count, loff_t *ppos)
+static void ipa_ut_dbgfs_meta_test_write_work_func(struct work_struct *work)
{
+ struct ipa_ut_dbgfs_test_write_work_ctx *write_work_ctx;
struct ipa_ut_suite *suite;
+ struct file *file;
int i;
enum ipa_hw_type ipa_ver;
int rc = 0;
long meta_type;
bool tst_fail = false;
+ write_work_ctx = container_of(work, struct
+ ipa_ut_dbgfs_test_write_work_ctx, dbgfs_work);
+
IPA_UT_DBG("Entry\n");
mutex_lock(&ipa_ut_ctx->lock);
+ file = write_work_ctx->file;
+ if (file == NULL) {
+ rc = -EFAULT;
+ goto unlock_mutex;
+ }
suite = file->f_inode->i_private;
ipa_assert_on(!suite);
meta_type = (long)(file->private_data);
@@ -328,7 +350,37 @@
_IPA_UT_TEST_LOG_BUF_NAME = NULL;
unlock_mutex:
mutex_unlock(&ipa_ut_ctx->lock);
- return ((!rc && !tst_fail) ? count : -EFAULT);
+ kfree(write_work_ctx);
+}
+
+/*
+ * ipa_ut_dbgfs_meta_test_write() - Debugfs write func for a for a meta test
+ * @params: write fops
+ *
+ * Run all tests in a suite using a work queue so it does not race with
+ * debugfs_remove_recursive
+ *
+ * Return: Negative if failure. Amount of characters written if success.
+ */
+static ssize_t ipa_ut_dbgfs_meta_test_write(struct file *file,
+ const char __user *buf, size_t count, loff_t *ppos)
+{
+ struct ipa_ut_dbgfs_test_write_work_ctx *write_work_ctx;
+
+ write_work_ctx = kzalloc(sizeof(*write_work_ctx), GFP_KERNEL);
+ if (!write_work_ctx) {
+ IPA_UT_ERR("kzalloc err.\n");
+ return -ENOMEM;
+ }
+
+ write_work_ctx->file = file;
+
+ INIT_WORK(&write_work_ctx->dbgfs_work,
+ ipa_ut_dbgfs_meta_test_write_work_func);
+
+ queue_work(ipa_ut_ctx->wq, &write_work_ctx->dbgfs_work);
+
+ return count;
}
/**
@@ -458,18 +510,27 @@
*
* Return: Negative in failure, given characters amount in success
*/
-static ssize_t ipa_ut_dbgfs_test_write(struct file *file,
- const char __user *buf, size_t count, loff_t *ppos)
+static void ipa_ut_dbgfs_test_write_work_func(struct work_struct *work)
{
+ struct ipa_ut_dbgfs_test_write_work_ctx *write_work_ctx;
struct ipa_ut_test *test;
struct ipa_ut_suite *suite;
+ struct file *file;
bool tst_fail = false;
int rc = 0;
enum ipa_hw_type ipa_ver;
+ write_work_ctx = container_of(work, struct
+ ipa_ut_dbgfs_test_write_work_ctx, dbgfs_work);
+
IPA_UT_DBG("Entry\n");
mutex_lock(&ipa_ut_ctx->lock);
+ file = write_work_ctx->file;
+ if (file == NULL) {
+ rc = -EFAULT;
+ goto unlock_mutex;
+ }
test = file->f_inode->i_private;
ipa_assert_on(!test);
@@ -558,9 +619,29 @@
_IPA_UT_TEST_LOG_BUF_NAME = NULL;
unlock_mutex:
mutex_unlock(&ipa_ut_ctx->lock);
- return ((!rc && !tst_fail) ? count : -EFAULT);
+ kfree(write_work_ctx);
}
+static ssize_t ipa_ut_dbgfs_test_write(struct file *file,
+ const char __user *buf, size_t count, loff_t *ppos)
+{
+ struct ipa_ut_dbgfs_test_write_work_ctx *write_work_ctx;
+
+ write_work_ctx = kzalloc(sizeof(*write_work_ctx), GFP_KERNEL);
+ if (!write_work_ctx) {
+ IPA_UT_ERR("kzalloc err.\n");
+ return -ENOMEM;
+ }
+
+ write_work_ctx->file = file;
+
+ INIT_WORK(&write_work_ctx->dbgfs_work,
+ ipa_ut_dbgfs_test_write_work_func);
+
+ queue_work(ipa_ut_ctx->wq, &write_work_ctx->dbgfs_work);
+
+ return count;
+}
/**
* ipa_ut_dbgfs_test_read() - Debugfs read function for a test
* @params: read fops
@@ -885,6 +966,13 @@
}
}
+ ipa_ut_ctx->wq = create_singlethread_workqueue("ipa_ut_dbgfs");
+ if (!ipa_ut_ctx->wq) {
+ IPA_UT_ERR("create workqueue failed\n");
+ ret = -ENOMEM;
+ goto unlock_mutex;
+ }
+
ipa_ut_ctx->test_dbgfs_root = debugfs_create_dir("test",
ipa_ut_ctx->ipa_dbgfs_root);
if (!ipa_ut_ctx->test_dbgfs_root ||
@@ -927,6 +1015,7 @@
IPA_UT_DBG("Entry\n");
mutex_lock(&ipa_ut_ctx->lock);
+ destroy_workqueue(ipa_ut_ctx->wq);
if (ipa_ut_ctx->enabled)
ipa_ut_framework_disable();
if (ipa_ut_ctx->inited)
diff --git a/drivers/platform/msm/msm_ext_display.c b/drivers/platform/msm/msm_ext_display.c
index d5fc867..73bf935 100644
--- a/drivers/platform/msm/msm_ext_display.c
+++ b/drivers/platform/msm/msm_ext_display.c
@@ -291,6 +291,13 @@
return msm_ext_disp_register_audio_codec(pdev, ops);
}
+/**
+ * Register audio codec ops to display driver
+ * for HDMI/Display Port usecase support.
+ *
+ * @return 0 on success, negative value on error
+ *
+ */
int msm_ext_disp_register_audio_codec(struct platform_device *pdev,
struct msm_ext_disp_audio_codec_ops *ops)
{
@@ -330,6 +337,7 @@
return ret;
}
+EXPORT_SYMBOL(msm_ext_disp_register_audio_codec);
static int msm_ext_disp_validate_intf(struct msm_ext_disp_init_data *init_data)
{
diff --git a/drivers/platform/msm/qcom-geni-se.c b/drivers/platform/msm/qcom-geni-se.c
index 707c95e..423c8f1 100644
--- a/drivers/platform/msm/qcom-geni-se.c
+++ b/drivers/platform/msm/qcom-geni-se.c
@@ -1284,10 +1284,9 @@
geni_se_dev = dev_get_drvdata(rsc->wrapper_dev);
if (unlikely(!geni_se_dev || !geni_se_dev->bus_bw))
return;
- mutex_lock(&geni_se_dev->ab_ib_lock);
if (unlikely(list_empty(&rsc->ab_list) || list_empty(&rsc->ib_list))) {
GENI_SE_DBG(ipc, false, NULL, "%s: Clocks not on\n", __func__);
- goto exit_geni_se_dump_dbg_regs;
+ return;
}
m_cmd0 = geni_read_reg(base, SE_GENI_M_CMD0);
m_irq_status = geni_read_reg(base, SE_GENI_M_IRQ_STATUS);
@@ -1315,8 +1314,6 @@
se_dma_dbg, m_cmd_ctrl, se_dma_rx_len, se_dma_rx_len_in);
GENI_SE_DBG(ipc, false, NULL,
"dma_txlen:0x%x, dma_txlen_in:0x%x\n", se_dma_tx_len, se_dma_tx_len_in);
-exit_geni_se_dump_dbg_regs:
- mutex_unlock(&geni_se_dev->ab_ib_lock);
}
EXPORT_SYMBOL(geni_se_dump_dbg_regs);
diff --git a/drivers/platform/msm/usb_bam.c b/drivers/platform/msm/usb_bam.c
index 92321ad..93121df 100644
--- a/drivers/platform/msm/usb_bam.c
+++ b/drivers/platform/msm/usb_bam.c
@@ -102,7 +102,6 @@
struct usb_bam_ctx_type {
struct usb_bam_sps_type usb_bam_sps;
struct resource *io_res;
- void __iomem *regs;
int irq;
struct platform_device *usb_bam_pdev;
struct workqueue_struct *usb_bam_wq;
@@ -112,6 +111,7 @@
u32 inactivity_timer_ms;
bool is_bam_inactivity;
struct usb_bam_pipe_connect *usb_bam_connections;
+ struct msm_usb_bam_data *usb_bam_data;
spinlock_t usb_bam_lock;
};
@@ -125,13 +125,13 @@
* CI_CTRL & DWC3_CTRL shouldn't be used simultaneously
* since both share the same prod & cons rm resourses
*/
-static enum ipa_client_type ipa_rm_resource_prod[MAX_BAMS] = {
+static enum ipa_rm_resource_name ipa_rm_resource_prod[MAX_BAMS] = {
[CI_CTRL] = IPA_RM_RESOURCE_USB_PROD,
[HSIC_CTRL] = IPA_RM_RESOURCE_HSIC_PROD,
[DWC3_CTRL] = IPA_RM_RESOURCE_USB_PROD,
};
-static enum ipa_client_type ipa_rm_resource_cons[MAX_BAMS] = {
+static enum ipa_rm_resource_name ipa_rm_resource_cons[MAX_BAMS] = {
[CI_CTRL] = IPA_RM_RESOURCE_USB_CONS,
[HSIC_CTRL] = IPA_RM_RESOURCE_HSIC_CONS,
[DWC3_CTRL] = IPA_RM_RESOURCE_USB_CONS,
@@ -234,10 +234,10 @@
if (dev) {
/* Hold the device until allowing lpm */
info[HSIC_CTRL].in_lpm = false;
- log_event_dbg("%s: Getting hsic device %p\n", __func__, dev);
+ log_event_dbg("%s: Getting hsic device %pK\n", __func__, dev);
pm_runtime_get(dev);
} else if (host_info[HSIC_CTRL].dev) {
- log_event_dbg("%s: Try Putting hsic device %p, lpm:%d\n",
+ log_event_dbg("%s: Try Putting hsic device %pK, lpm:%d\n",
__func__, host_info[HSIC_CTRL].dev,
info[HSIC_CTRL].in_lpm);
/* Just release previous device if not already done */
@@ -319,8 +319,6 @@
{
int ret = 0;
struct usb_bam_ctx_type *ctx = &msm_usb_bam[pipe_connect->bam_type];
- struct msm_usb_bam_platform_data *pdata =
- ctx->usb_bam_pdev->dev.platform_data;
struct sps_mem_buffer *data_buf = &(pipe_connect->data_mem_buf);
struct sps_mem_buffer *desc_buf = &(pipe_connect->desc_mem_buf);
@@ -359,7 +357,7 @@
}
data_buf->phys_base = pipe_connect->data_fifo_base_offset +
- pdata->usb_bam_fifo_baseaddr;
+ ctx->usb_bam_data->usb_bam_fifo_baseaddr;
data_buf->size = pipe_connect->data_fifo_size;
data_buf->base = ioremap(data_buf->phys_base, data_buf->size);
if (!data_buf->base) {
@@ -371,7 +369,7 @@
memset_io(data_buf->base, 0, data_buf->size);
desc_buf->phys_base = pipe_connect->desc_fifo_base_offset +
- pdata->usb_bam_fifo_baseaddr;
+ ctx->usb_bam_data->usb_bam_fifo_baseaddr;
desc_buf->size = pipe_connect->desc_fifo_size;
desc_buf->base = ioremap(desc_buf->phys_base, desc_buf->size);
if (!desc_buf->base) {
@@ -824,7 +822,7 @@
/* Exit from "full suspend" in case of hsic host */
if (host_info[HSIC_CTRL].dev && info[HSIC_CTRL].in_lpm) {
- log_event_dbg("%s: Getting hsic device %p\n", __func__,
+ log_event_dbg("%s: Getting hsic device %pK\n", __func__,
host_info[HSIC_CTRL].dev);
pm_runtime_get(host_info[HSIC_CTRL].dev);
info[HSIC_CTRL].in_lpm = false;
@@ -838,7 +836,7 @@
log_event_dbg("%s: enter\n", __func__);
if (host_info[HSIC_CTRL].dev && !info[HSIC_CTRL].in_lpm) {
- log_event_dbg("%s: Putting hsic host device %p\n", __func__,
+ log_event_dbg("%s: Putting hsic host device %pK\n", __func__,
host_info[HSIC_CTRL].dev);
pm_runtime_put(host_info[HSIC_CTRL].dev);
info[HSIC_CTRL].in_lpm = true;
@@ -1068,7 +1066,6 @@
struct usb_bam_pipe_connect *pipe_connect =
&ctx->usb_bam_connections[idx];
struct device *bam_dev = &ctx->usb_bam_pdev->dev;
- struct msm_usb_bam_platform_data *pdata = bam_dev->platform_data;
enum usb_bam_mode cur_mode;
if (pipe_connect->enabled) {
@@ -1094,7 +1091,7 @@
spin_lock(&ctx->usb_bam_lock);
/* Check if BAM requires RESET before connect and reset of first pipe */
- if ((pdata->reset_on_connect == true) &&
+ if ((ctx->usb_bam_data->reset_on_connect == true) &&
(ctx->pipes_enabled_per_bam == 0)) {
spin_unlock(&ctx->usb_bam_lock);
@@ -1595,8 +1592,6 @@
static void usb_bam_ipa_create_resources(enum usb_ctrl cur_bam)
{
struct usb_bam_ctx_type *ctx = &msm_usb_bam[cur_bam];
- struct msm_usb_bam_platform_data *pdata =
- ctx->usb_bam_pdev->dev.platform_data;
struct ipa_rm_create_params usb_prod_create_params;
struct ipa_rm_create_params usb_cons_create_params;
int ret;
@@ -1605,7 +1600,8 @@
memset(&usb_prod_create_params, 0, sizeof(usb_prod_create_params));
usb_prod_create_params.name = ipa_rm_resource_prod[cur_bam];
usb_prod_create_params.reg_params.notify_cb = usb_prod_notify_cb;
- usb_prod_create_params.reg_params.user_data = &pdata->bam_type;
+ usb_prod_create_params.reg_params.user_data
+ = &ctx->usb_bam_data->bam_type;
usb_prod_create_params.floor_voltage = IPA_VOLTAGE_SVS;
ret = ipa_rm_create_resource(&usb_prod_create_params);
if (ret) {
@@ -1628,6 +1624,22 @@
}
}
+static void usb_bam_ipa_delete_resources(enum usb_ctrl cur_bam)
+{
+ int ret;
+
+ ret = ipa_rm_delete_resource(ipa_rm_resource_prod[cur_bam]);
+ if (ret)
+ log_event_err("%s: Failed to delete USB_PROD resource\n",
+ __func__);
+
+ ret = ipa_rm_delete_resource(ipa_rm_resource_cons[cur_bam]);
+ if (ret)
+ log_event_err("%s: Failed to delete USB_CONS resource\n",
+ __func__);
+
+}
+
static void wait_for_prod_granted(enum usb_ctrl cur_bam)
{
int ret;
@@ -1723,7 +1735,7 @@
/* If we have any remaints in the pipes we don't go to sleep */
prod_pipe = ctx->usb_bam_sps.sps_pipes[src_idx];
cons_pipe = ctx->usb_bam_sps.sps_pipes[dst_idx];
- log_event_dbg("prod_pipe=%p, cons_pipe=%p\n", prod_pipe, cons_pipe);
+ log_event_dbg("prod_pipe=%pK, cons_pipe=%pK\n", prod_pipe, cons_pipe);
if (!cons_pipe || (!prod_pipe &&
prod_pipe_connect->pipe_type == USB_BAM_PIPE_BAM2BAM)) {
@@ -2089,7 +2101,7 @@
}
/* HSIC host will go now to lpm */
- log_event_dbg("%s: vote for suspend hsic %p\n",
+ log_event_dbg("%s: vote for suspend hsic %pK\n",
__func__, host_info[bam_type].dev);
for (i = 0; i < ctx->max_connections; i++) {
@@ -2134,16 +2146,14 @@
int ret;
struct usb_bam_ctx_type *ctx = &msm_usb_bam[cur_bam];
struct ipa_rm_perf_profile ipa_rm_perf_prof;
- struct msm_usb_bam_platform_data *pdata =
- ctx->usb_bam_pdev->dev.platform_data;
if (usb_connection_speed == USB_SPEED_SUPER)
ipa_rm_perf_prof.max_supported_bandwidth_mbps =
- pdata->max_mbps_superspeed;
+ ctx->usb_bam_data->max_mbps_superspeed;
else
/* Bam2Bam is supported only for SS and HS (HW limitation) */
ipa_rm_perf_prof.max_supported_bandwidth_mbps =
- pdata->max_mbps_highspeed;
+ ctx->usb_bam_data->max_mbps_highspeed;
/*
* Having a max mbps property in dtsi file is a must
@@ -2180,7 +2190,6 @@
struct usb_bam_ctx_type *ctx = &msm_usb_bam[cur_bam];
struct usb_bam_pipe_connect *pipe_connect;
struct device *bam_dev = &ctx->usb_bam_pdev->dev;
- struct msm_usb_bam_platform_data *pdata = bam_dev->platform_data;
int ret;
bool bam2bam, is_dpl;
@@ -2256,7 +2265,8 @@
/* Check if BAM requires RESET before connect and reset first pipe */
spin_lock(&ctx->usb_bam_lock);
- if (pdata->reset_on_connect && !ctx->pipes_enabled_per_bam) {
+ if (ctx->usb_bam_data->reset_on_connect &&
+ !ctx->pipes_enabled_per_bam) {
spin_unlock(&ctx->usb_bam_lock);
if (cur_bam == CI_CTRL)
msm_hw_bam_disable(1);
@@ -2450,7 +2460,7 @@
if (pipe_iter->bam_type == pipe_connect->bam_type &&
pipe_iter->dir == PEER_PERIPHERAL_TO_USB &&
pipe_iter->enabled) {
- log_event_dbg("%s: Register wakeup on pipe %p\n",
+ log_event_dbg("%s: Register wakeup on pipe %pK\n",
__func__, pipe_iter);
__usb_bam_register_wake_cb(
pipe_connect->bam_type, i,
@@ -2606,8 +2616,6 @@
struct usb_bam_pipe_connect *pipe_connect;
struct device *bam_dev = &ctx->usb_bam_pdev->dev;
int ret;
- struct msm_usb_bam_platform_data *pdata =
- ctx->usb_bam_pdev->dev.platform_data;
pipe_connect = &ctx->usb_bam_connections[idx];
@@ -2635,7 +2643,8 @@
log_event_dbg("%s: success disconnecting pipe %d\n", __func__, idx);
- if (pdata->reset_on_disconnect && !ctx->pipes_enabled_per_bam) {
+ if (ctx->usb_bam_data->reset_on_disconnect
+ && !ctx->pipes_enabled_per_bam) {
if (bam_type == CI_CTRL)
msm_hw_bam_disable(1);
@@ -2803,10 +2812,10 @@
}
}
-static struct msm_usb_bam_platform_data *usb_bam_dt_to_pdata(
+static struct msm_usb_bam_data *usb_bam_dt_to_data(
struct platform_device *pdev, u32 usb_addr)
{
- struct msm_usb_bam_platform_data *pdata;
+ struct msm_usb_bam_data *usb_bam_data;
struct device_node *node = pdev->dev.of_node;
int rc = 0;
u8 i = 0;
@@ -2815,8 +2824,9 @@
u32 threshold, max_connections = 0;
static struct usb_bam_pipe_connect *usb_bam_connections;
- pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL);
- if (!pdata)
+ usb_bam_data = devm_kzalloc(&pdev->dev, sizeof(*usb_bam_data),
+ GFP_KERNEL);
+ if (!usb_bam_data)
return NULL;
rc = of_property_read_u32(node, "qcom,bam-type", &bam);
@@ -2830,7 +2840,7 @@
__func__, bam);
return NULL;
}
- pdata->bam_type = bam;
+ usb_bam_data->bam_type = bam;
rc = of_property_read_u32(node, "qcom,bam-mode", &bam_mode);
if (rc) {
@@ -2840,49 +2850,49 @@
bam_mode = USB_BAM_DEVICE;
}
- pdata->reset_on_connect = of_property_read_bool(node,
+ usb_bam_data->reset_on_connect = of_property_read_bool(node,
"qcom,reset-bam-on-connect");
- pdata->reset_on_disconnect = of_property_read_bool(node,
+ usb_bam_data->reset_on_disconnect = of_property_read_bool(node,
"qcom,reset-bam-on-disconnect");
rc = of_property_read_u32(node, "qcom,usb-bam-num-pipes",
- &pdata->usb_bam_num_pipes);
+ &usb_bam_data->usb_bam_num_pipes);
if (rc) {
log_event_err("Invalid usb bam num pipes property\n");
return NULL;
}
rc = of_property_read_u32(node, "qcom,usb-bam-max-mbps-highspeed",
- &pdata->max_mbps_highspeed);
+ &usb_bam_data->max_mbps_highspeed);
if (rc)
- pdata->max_mbps_highspeed = 0;
+ usb_bam_data->max_mbps_highspeed = 0;
rc = of_property_read_u32(node, "qcom,usb-bam-max-mbps-superspeed",
- &pdata->max_mbps_superspeed);
+ &usb_bam_data->max_mbps_superspeed);
if (rc)
- pdata->max_mbps_superspeed = 0;
+ usb_bam_data->max_mbps_superspeed = 0;
rc = of_property_read_u32(node, "qcom,usb-bam-fifo-baseaddr", &addr);
if (rc)
pr_debug("%s: Invalid usb base address property\n", __func__);
else
- pdata->usb_bam_fifo_baseaddr = addr;
+ usb_bam_data->usb_bam_fifo_baseaddr = addr;
- pdata->ignore_core_reset_ack = of_property_read_bool(node,
+ usb_bam_data->ignore_core_reset_ack = of_property_read_bool(node,
"qcom,ignore-core-reset-ack");
- pdata->disable_clk_gating = of_property_read_bool(node,
+ usb_bam_data->disable_clk_gating = of_property_read_bool(node,
"qcom,disable-clk-gating");
rc = of_property_read_u32(node, "qcom,usb-bam-override-threshold",
&threshold);
if (rc)
- pdata->override_threshold = USB_THRESHOLD;
+ usb_bam_data->override_threshold = USB_THRESHOLD;
else
- pdata->override_threshold = threshold;
+ usb_bam_data->override_threshold = threshold;
- pdata->enable_hsusb_bam_on_boot = of_property_read_bool(node,
+ usb_bam_data->enable_hsusb_bam_on_boot = of_property_read_bool(node,
"qcom,enable-hsusb-bam-on-boot");
for_each_child_of_node(pdev->dev.of_node, node)
@@ -2918,7 +2928,7 @@
goto err;
if (usb_bam_connections[i].mem_type == OCI_MEM) {
- if (!pdata->usb_bam_fifo_baseaddr) {
+ if (!usb_bam_data->usb_bam_fifo_baseaddr) {
log_event_err("%s: base address is missing\n",
__func__);
goto err;
@@ -3002,7 +3012,7 @@
msm_usb_bam[bam].usb_bam_connections = usb_bam_connections;
msm_usb_bam[bam].max_connections = max_connections;
- return pdata;
+ return usb_bam_data;
err:
log_event_err("%s: failed\n", __func__);
return NULL;
@@ -3011,9 +3021,8 @@
static int usb_bam_init(struct platform_device *pdev)
{
int ret;
- struct msm_usb_bam_platform_data *pdata = pdev->dev.platform_data;
- enum usb_ctrl bam_type = pdata->bam_type;
- struct usb_bam_ctx_type *ctx = &msm_usb_bam[bam_type];
+ struct usb_bam_ctx_type *ctx = dev_get_drvdata(&pdev->dev);
+ enum usb_ctrl bam_type = ctx->usb_bam_data->bam_type;
struct sps_bam_props props;
memset(&props, 0, sizeof(props));
@@ -3022,23 +3031,22 @@
bam_enable_strings[bam_type]);
props.phys_addr = ctx->io_res->start;
- props.virt_addr = ctx->regs;
props.virt_size = resource_size(ctx->io_res);
props.irq = ctx->irq;
- props.summing_threshold = pdata->override_threshold;
- props.event_threshold = pdata->override_threshold;
- props.num_pipes = pdata->usb_bam_num_pipes;
+ props.summing_threshold = ctx->usb_bam_data->override_threshold;
+ props.event_threshold = ctx->usb_bam_data->override_threshold;
+ props.num_pipes = ctx->usb_bam_data->usb_bam_num_pipes;
props.callback = usb_bam_sps_events;
props.user = bam_enable_strings[bam_type];
/*
- * HSUSB and HSIC Cores don't support RESET ACK signal to BAMs.
- * Hence let BAM to ignore acknowledge from USB while resetting PIPE.
- */
- if (pdata->ignore_core_reset_ack && bam_type != DWC3_CTRL)
+ * HSUSB and HSIC Cores don't support RESET ACK signal to BAMs
+ * Hence, let BAM to ignore acknowledge from USB while resetting PIPE
+ */
+ if (ctx->usb_bam_data->ignore_core_reset_ack && bam_type != DWC3_CTRL)
props.options = SPS_BAM_NO_EXT_P_RST;
- if (pdata->disable_clk_gating)
+ if (ctx->usb_bam_data->disable_clk_gating)
props.options |= SPS_BAM_NO_LOCAL_CLK_GATING;
/*
@@ -3046,7 +3054,8 @@
* starting peripheral controller to avoid switching USB core mode
* from legacy to BAM with ongoing data transfers.
*/
- if (pdata->enable_hsusb_bam_on_boot && bam_type == CI_CTRL) {
+ if (ctx->usb_bam_data->enable_hsusb_bam_on_boot
+ && bam_type == CI_CTRL) {
pr_debug("Register and enable HSUSB BAM\n");
props.options |= SPS_BAM_OPT_ENABLE_AT_BOOT;
}
@@ -3063,9 +3072,8 @@
static int enable_usb_bam(struct platform_device *pdev)
{
int ret;
- struct msm_usb_bam_platform_data *pdata = pdev->dev.platform_data;
- enum usb_ctrl bam_type = pdata->bam_type;
- struct usb_bam_ctx_type *ctx = &msm_usb_bam[bam_type];
+ struct usb_bam_ctx_type *ctx = dev_get_drvdata(&pdev->dev);
+ enum usb_ctrl bam_type = ctx->usb_bam_data->bam_type;
ret = usb_bam_init(pdev);
if (ret) {
@@ -3132,14 +3140,19 @@
&usb_bam_panic_blk);
}
+static void usb_bam_unregister_panic_hdlr(void)
+{
+ atomic_notifier_chain_unregister(&panic_notifier_list,
+ &usb_bam_panic_blk);
+}
+
static int usb_bam_probe(struct platform_device *pdev)
{
int ret, i, irq;
struct resource *io_res;
- void __iomem *regs;
enum usb_ctrl bam_type;
struct usb_bam_ctx_type *ctx;
- struct msm_usb_bam_platform_data *pdata;
+ struct msm_usb_bam_data *usb_bam_data;
dev_dbg(&pdev->dev, "usb_bam_probe\n");
@@ -3155,25 +3168,19 @@
return irq;
}
- regs = devm_ioremap(&pdev->dev, io_res->start, resource_size(io_res));
- if (!regs) {
- log_event_err("%s: ioremap failed\n", __func__);
- return -ENOMEM;
- }
-
/* specify BAM physical address to be filled in BAM connections */
- pdata = usb_bam_dt_to_pdata(pdev, io_res->start);
- if (!pdata)
+ usb_bam_data = usb_bam_dt_to_data(pdev, io_res->start);
+ if (!usb_bam_data)
return -EINVAL;
- pdev->dev.platform_data = pdata;
- bam_type = pdata->bam_type;
+ bam_type = usb_bam_data->bam_type;
ctx = &msm_usb_bam[bam_type];
+ dev_set_drvdata(&pdev->dev, ctx);
ctx->usb_bam_pdev = pdev;
ctx->irq = irq;
- ctx->regs = regs;
ctx->io_res = io_res;
+ ctx->usb_bam_data = usb_bam_data;
for (i = 0; i < ctx->max_connections; i++) {
ctx->usb_bam_connections[i].enabled = 0;
@@ -3314,15 +3321,15 @@
bool msm_usb_bam_enable(enum usb_ctrl bam, bool bam_enable)
{
- struct msm_usb_bam_platform_data *pdata;
+ struct msm_usb_bam_data *usb_bam_data;
struct usb_bam_ctx_type *ctx = &msm_usb_bam[bam];
if (!ctx->usb_bam_pdev)
return 0;
- pdata = ctx->usb_bam_pdev->dev.platform_data;
+ usb_bam_data = ctx->usb_bam_data;
if ((bam != CI_CTRL) || !(bam_enable ||
- pdata->enable_hsusb_bam_on_boot))
+ usb_bam_data->enable_hsusb_bam_on_boot))
return 0;
msm_hw_bam_disable(1);
@@ -3404,10 +3411,11 @@
static int usb_bam_remove(struct platform_device *pdev)
{
- struct msm_usb_bam_platform_data *pdata = pdev->dev.platform_data;
- enum usb_ctrl bam_type = pdata->bam_type;
- struct usb_bam_ctx_type *ctx = &msm_usb_bam[bam_type];
+ struct usb_bam_ctx_type *ctx = dev_get_drvdata(&pdev->dev);
+ usb_bam_ipa_delete_resources(ctx->usb_bam_data->bam_type);
+ usb_bam_unregister_panic_hdlr();
+ sps_deregister_bam_device(ctx->h_bam);
destroy_workqueue(ctx->usb_bam_wq);
return 0;
diff --git a/drivers/power/reset/msm-poweroff.c b/drivers/power/reset/msm-poweroff.c
index 9f04957..c090b2a 100644
--- a/drivers/power/reset/msm-poweroff.c
+++ b/drivers/power/reset/msm-poweroff.c
@@ -37,6 +37,7 @@
#define EMERGENCY_DLOAD_MAGIC1 0x322A4F99
#define EMERGENCY_DLOAD_MAGIC2 0xC67E4350
#define EMERGENCY_DLOAD_MAGIC3 0x77777777
+#define EMMC_DLOAD_TYPE 0x2
#define SCM_IO_DISABLE_PMIC_ARBITER 1
#define SCM_IO_DEASSERT_PS_HOLD 2
@@ -47,13 +48,14 @@
static int restart_mode;
-void *restart_reason;
+static void __iomem *restart_reason, *dload_type_addr;
static bool scm_pmic_arbiter_disable_supported;
static bool scm_deassert_ps_hold_supported;
/* Download mode master kill-switch */
static void __iomem *msm_ps_hold;
static phys_addr_t tcsr_boot_misc_detect;
static int download_mode = 1;
+static struct kobject dload_kobj;
#ifdef CONFIG_QCOM_DLOAD_MODE
#define EDL_MODE_PROP "qcom,msm-imem-emergency_download_mode"
@@ -72,8 +74,23 @@
static bool scm_dload_supported;
static int dload_set(const char *val, struct kernel_param *kp);
+/* interface for exporting attributes */
+struct reset_attribute {
+ struct attribute attr;
+ ssize_t (*show)(struct kobject *kobj, struct attribute *attr,
+ char *buf);
+ size_t (*store)(struct kobject *kobj, struct attribute *attr,
+ const char *buf, size_t count);
+};
+#define to_reset_attr(_attr) \
+ container_of(_attr, struct reset_attribute, attr)
+#define RESET_ATTR(_name, _mode, _show, _store) \
+ static struct reset_attribute reset_attr_##_name = \
+ __ATTR(_name, _mode, _show, _store)
+
module_param_call(download_mode, dload_set, param_get_int,
&download_mode, 0644);
+
static int panic_prep_restart(struct notifier_block *this,
unsigned long event, void *ptr)
{
@@ -85,7 +102,7 @@
.notifier_call = panic_prep_restart,
};
-int scm_set_dload_mode(int arg1, int arg2)
+static int scm_set_dload_mode(int arg1, int arg2)
{
struct scm_desc desc = {
.args[0] = arg1,
@@ -397,6 +414,84 @@
pr_err("Powering off has failed\n");
}
+static ssize_t attr_show(struct kobject *kobj, struct attribute *attr,
+ char *buf)
+{
+ struct reset_attribute *reset_attr = to_reset_attr(attr);
+ ssize_t ret = -EIO;
+
+ if (reset_attr->show)
+ ret = reset_attr->show(kobj, attr, buf);
+
+ return ret;
+}
+
+static ssize_t attr_store(struct kobject *kobj, struct attribute *attr,
+ const char *buf, size_t count)
+{
+ struct reset_attribute *reset_attr = to_reset_attr(attr);
+ ssize_t ret = -EIO;
+
+ if (reset_attr->store)
+ ret = reset_attr->store(kobj, attr, buf, count);
+
+ return ret;
+}
+
+static const struct sysfs_ops reset_sysfs_ops = {
+ .show = attr_show,
+ .store = attr_store,
+};
+
+static struct kobj_type reset_ktype = {
+ .sysfs_ops = &reset_sysfs_ops,
+};
+
+static ssize_t show_emmc_dload(struct kobject *kobj, struct attribute *attr,
+ char *buf)
+{
+ uint32_t read_val, show_val;
+
+ read_val = __raw_readl(dload_type_addr);
+ if (read_val == EMMC_DLOAD_TYPE)
+ show_val = 1;
+ else
+ show_val = 0;
+
+ return snprintf(buf, sizeof(show_val), "%u\n", show_val);
+}
+
+static size_t store_emmc_dload(struct kobject *kobj, struct attribute *attr,
+ const char *buf, size_t count)
+{
+ uint32_t enabled;
+ int ret;
+
+ ret = kstrtouint(buf, 0, &enabled);
+ if (ret < 0)
+ return ret;
+
+ if (!((enabled == 0) || (enabled == 1)))
+ return -EINVAL;
+
+ if (enabled == 1)
+ __raw_writel(EMMC_DLOAD_TYPE, dload_type_addr);
+ else
+ __raw_writel(0, dload_type_addr);
+
+ return count;
+}
+RESET_ATTR(emmc_dload, 0644, show_emmc_dload, store_emmc_dload);
+
+static struct attribute *reset_attrs[] = {
+ &reset_attr_emmc_dload.attr,
+ NULL
+};
+
+static struct attribute_group reset_attr_group = {
+ .attrs = reset_attrs,
+};
+
static int msm_restart_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
@@ -438,7 +533,7 @@
pr_err("unable to map imem KASLR offset\n");
}
- if (kaslr_imem_addr && scm_is_secure_device()) {
+ if (kaslr_imem_addr) {
__raw_writel(0xdead4ead, kaslr_imem_addr);
__raw_writel(KASLR_OFFSET_BIT_MASK &
(kimage_vaddr - KIMAGE_VADDR), kaslr_imem_addr + 4);
@@ -448,6 +543,33 @@
iounmap(kaslr_imem_addr);
}
#endif
+ np = of_find_compatible_node(NULL, NULL,
+ "qcom,msm-imem-dload-type");
+ if (!np) {
+ pr_err("unable to find DT imem dload-type node\n");
+ goto skip_sysfs_create;
+ } else {
+ dload_type_addr = of_iomap(np, 0);
+ if (!dload_type_addr) {
+ pr_err("unable to map imem dload-type offset\n");
+ goto skip_sysfs_create;
+ }
+ }
+
+ ret = kobject_init_and_add(&dload_kobj, &reset_ktype,
+ kernel_kobj, "%s", "dload");
+ if (ret) {
+ pr_err("%s:Error in creation kobject_add\n", __func__);
+ kobject_put(&dload_kobj);
+ goto skip_sysfs_create;
+ }
+
+ ret = sysfs_create_group(&dload_kobj, &reset_attr_group);
+ if (ret) {
+ pr_err("%s:Error in creation sysfs_create_group\n", __func__);
+ kobject_del(&dload_kobj);
+ }
+skip_sysfs_create:
#endif
np = of_find_compatible_node(NULL, NULL,
"qcom,msm-imem-restart_reason");
@@ -481,7 +603,6 @@
if (scm_is_call_available(SCM_SVC_PWR, SCM_IO_DEASSERT_PS_HOLD) > 0)
scm_deassert_ps_hold_supported = true;
- download_mode = scm_is_secure_device();
set_dload_mode(download_mode);
return 0;
diff --git a/drivers/power/supply/qcom/Kconfig b/drivers/power/supply/qcom/Kconfig
index 362375f..25ad740 100644
--- a/drivers/power/supply/qcom/Kconfig
+++ b/drivers/power/supply/qcom/Kconfig
@@ -1,5 +1,14 @@
menu "Qualcomm Technologies Inc Charger and Fuel Gauge support"
+config QPNP_FG
+ tristate "QPNP fuel gauge driver"
+ depends on MFD_SPMI_PMIC
+ help
+ Say Y here to enable the Fuel Gauge driver. This adds support for
+ battery fuel gauging and state of charge of battery connected to the
+ fuel gauge. The state of charge is reported through a BMS power
+ supply property and also sends uevents when the capacity is updated.
+
config QPNP_FG_GEN3
tristate "QPNP GEN3 fuel gauge driver"
depends on MFD_SPMI_PMIC
@@ -64,6 +73,15 @@
The driver reports the charger status via the power supply framework.
A charger status change triggers an IRQ via the device STAT pin.
+config QPNP_SMBCHARGER
+ tristate "QPNP SMB Charger driver"
+ depends on MFD_SPMI_PMIC
+ help
+ Say Y here to enable the dual path switch mode battery charger which
+ supports USB detection and battery charging up to 3A.
+ The driver also offers relevant information to userspace via the
+ power supply framework.
+
config QPNP_QNOVO
bool "QPNP QNOVO driver"
depends on MFD_SPMI_PMIC
diff --git a/drivers/power/supply/qcom/Makefile b/drivers/power/supply/qcom/Makefile
index 6cc83ab..21f63ee 100644
--- a/drivers/power/supply/qcom/Makefile
+++ b/drivers/power/supply/qcom/Makefile
@@ -1,7 +1,9 @@
+obj-$(CONFIG_QPNP_FG) += qpnp-fg.o
obj-$(CONFIG_QPNP_FG_GEN3) += qpnp-fg-gen3.o fg-memif.o fg-util.o
+obj-$(CONFIG_QPNP_SMBCHARGER) += qpnp-smbcharger.o pmic-voter.o
obj-$(CONFIG_SMB135X_CHARGER) += smb135x-charger.o pmic-voter.o
obj-$(CONFIG_SMB1355_SLAVE_CHARGER) += smb1355-charger.o pmic-voter.o
obj-$(CONFIG_SMB1351_USB_CHARGER) += smb1351-charger.o pmic-voter.o battery.o
obj-$(CONFIG_QPNP_SMB2) += step-chg-jeita.o battery.o qpnp-smb2.o smb-lib.o pmic-voter.o storm-watch.o
-obj-$(CONFIG_SMB138X_CHARGER) += smb138x-charger.o smb-lib.o pmic-voter.o storm-watch.o battery.o
+obj-$(CONFIG_SMB138X_CHARGER) += step-chg-jeita.o smb138x-charger.o smb-lib.o pmic-voter.o storm-watch.o battery.o
obj-$(CONFIG_QPNP_QNOVO) += qpnp-qnovo.o battery.o
diff --git a/drivers/power/supply/qcom/fg-core.h b/drivers/power/supply/qcom/fg-core.h
index 4303960..7c10e63 100644
--- a/drivers/power/supply/qcom/fg-core.h
+++ b/drivers/power/supply/qcom/fg-core.h
@@ -426,6 +426,7 @@
int batt_id_ohms;
int ki_coeff_full_soc;
int charge_status;
+ int prev_charge_status;
int charge_done;
int charge_type;
int online_status;
@@ -507,6 +508,7 @@
extern int fg_read(struct fg_chip *chip, int addr, u8 *val, int len);
extern int fg_write(struct fg_chip *chip, int addr, u8 *val, int len);
extern int fg_masked_write(struct fg_chip *chip, int addr, u8 mask, u8 val);
+extern int fg_dump_regs(struct fg_chip *chip);
extern int fg_ima_init(struct fg_chip *chip);
extern int fg_dma_init(struct fg_chip *chip);
extern int fg_clear_ima_errors_if_any(struct fg_chip *chip, bool check_hw_sts);
diff --git a/drivers/power/supply/qcom/fg-memif.c b/drivers/power/supply/qcom/fg-memif.c
index 0abc9df..c1b5adc 100644
--- a/drivers/power/supply/qcom/fg-memif.c
+++ b/drivers/power/supply/qcom/fg-memif.c
@@ -792,6 +792,7 @@
} else {
pr_err("wait for mem_grant timed out ret=%d\n",
ret);
+ fg_dump_regs(chip);
}
}
diff --git a/drivers/power/supply/qcom/fg-util.c b/drivers/power/supply/qcom/fg-util.c
index aed2062..622bfad 100644
--- a/drivers/power/supply/qcom/fg-util.c
+++ b/drivers/power/supply/qcom/fg-util.c
@@ -266,8 +266,7 @@
int fg_sram_write(struct fg_chip *chip, u16 address, u8 offset,
u8 *val, int len, int flags)
{
- int rc = 0;
- bool tried_again = false;
+ int rc = 0, tries = 0;
bool atomic_access = false;
if (!chip)
@@ -292,7 +291,7 @@
enable_irq(chip->irqs[SOC_UPDATE_IRQ].irq);
atomic_access = true;
}
-wait:
+
/*
* Atomic access mean waiting upon SOC_UPDATE interrupt from
* FG_ALG and do the transaction after that. This is to make
@@ -301,16 +300,20 @@
* FG cycle (~1.47 seconds).
*/
if (atomic_access) {
- /* Wait for SOC_UPDATE completion */
- rc = wait_for_completion_interruptible_timeout(
- &chip->soc_update,
- msecs_to_jiffies(SOC_UPDATE_WAIT_MS));
+ for (tries = 0; tries < 2; tries++) {
+ /* Wait for SOC_UPDATE completion */
+ rc = wait_for_completion_interruptible_timeout(
+ &chip->soc_update,
+ msecs_to_jiffies(SOC_UPDATE_WAIT_MS));
+ if (rc > 0) {
+ rc = 0;
+ break;
+ } else if (!rc) {
+ rc = -ETIMEDOUT;
+ }
+ }
- /* If we were interrupted wait again one more time. */
- if (rc == -ERESTARTSYS && !tried_again) {
- tried_again = true;
- goto wait;
- } else if (rc <= 0) {
+ if (rc < 0) {
pr_err("wait for soc_update timed out rc=%d\n", rc);
goto out;
}
@@ -492,6 +495,33 @@
return rc;
}
+int fg_dump_regs(struct fg_chip *chip)
+{
+ int i, rc;
+ u8 buf[256];
+
+ if (!chip)
+ return -EINVAL;
+
+ rc = fg_read(chip, chip->batt_soc_base, buf, sizeof(buf));
+ if (rc < 0)
+ return rc;
+
+ pr_info("batt_soc_base registers:\n");
+ for (i = 0; i < sizeof(buf); i++)
+ pr_info("%04x:%02x\n", chip->batt_soc_base + i, buf[i]);
+
+ rc = fg_read(chip, chip->mem_if_base, buf, sizeof(buf));
+ if (rc < 0)
+ return rc;
+
+ pr_info("mem_if_base registers:\n");
+ for (i = 0; i < sizeof(buf); i++)
+ pr_info("%04x:%02x\n", chip->mem_if_base + i, buf[i]);
+
+ return 0;
+}
+
int64_t twos_compliment_extend(int64_t val, int sign_bit_pos)
{
int i, nbytes = DIV_ROUND_UP(sign_bit_pos, 8);
diff --git a/drivers/power/supply/qcom/qpnp-fg-gen3.c b/drivers/power/supply/qcom/qpnp-fg-gen3.c
index f90fce5..2a47442 100644
--- a/drivers/power/supply/qcom/qpnp-fg-gen3.c
+++ b/drivers/power/supply/qcom/qpnp-fg-gen3.c
@@ -562,6 +562,21 @@
return 0;
}
+#define BATT_SOC_32BIT GENMASK(31, 0)
+static int fg_get_charge_counter_shadow(struct fg_chip *chip, int *val)
+{
+ int rc, batt_soc;
+
+ rc = fg_get_sram_prop(chip, FG_SRAM_BATT_SOC, &batt_soc);
+ if (rc < 0) {
+ pr_err("Error in getting BATT_SOC, rc=%d\n", rc);
+ return rc;
+ }
+
+ *val = div_u64((u32)batt_soc * chip->cl.learned_cc_uah, BATT_SOC_32BIT);
+ return 0;
+}
+
static int fg_get_charge_counter(struct fg_chip *chip, int *val)
{
int rc, cc_soc;
@@ -1148,7 +1163,7 @@
enable_irq_wake(chip->irqs[BATT_MISSING_IRQ].irq);
} else {
disable_irq_wake(chip->irqs[BATT_MISSING_IRQ].irq);
- disable_irq(chip->irqs[BATT_MISSING_IRQ].irq);
+ disable_irq_nosync(chip->irqs[BATT_MISSING_IRQ].irq);
}
return 0;
@@ -1167,7 +1182,7 @@
enable_irq_wake(chip->irqs[BSOC_DELTA_IRQ].irq);
} else {
disable_irq_wake(chip->irqs[BSOC_DELTA_IRQ].irq);
- disable_irq(chip->irqs[BSOC_DELTA_IRQ].irq);
+ disable_irq_nosync(chip->irqs[BSOC_DELTA_IRQ].irq);
}
return 0;
@@ -1249,6 +1264,21 @@
return true;
}
+static int fg_prime_cc_soc_sw(struct fg_chip *chip, int cc_soc_sw)
+{
+ int rc;
+
+ rc = fg_sram_write(chip, chip->sp[FG_SRAM_CC_SOC_SW].addr_word,
+ chip->sp[FG_SRAM_CC_SOC_SW].addr_byte, (u8 *)&cc_soc_sw,
+ chip->sp[FG_SRAM_CC_SOC_SW].len, FG_IMA_ATOMIC);
+ if (rc < 0)
+ pr_err("Error in writing cc_soc_sw, rc=%d\n", rc);
+ else
+ fg_dbg(chip, FG_STATUS, "cc_soc_sw: %x\n", cc_soc_sw);
+
+ return rc;
+}
+
static int fg_save_learned_cap_to_sram(struct fg_chip *chip)
{
int16_t cc_mah;
@@ -1434,7 +1464,6 @@
return 0;
}
-#define BATT_SOC_32BIT GENMASK(31, 0)
static int fg_cap_learning_begin(struct fg_chip *chip, u32 batt_soc)
{
int rc, cc_soc_sw, batt_soc_msb;
@@ -1453,16 +1482,13 @@
/* Prime cc_soc_sw with battery SOC when capacity learning begins */
cc_soc_sw = div64_s64((int64_t)batt_soc * CC_SOC_30BIT,
BATT_SOC_32BIT);
- rc = fg_sram_write(chip, chip->sp[FG_SRAM_CC_SOC_SW].addr_word,
- chip->sp[FG_SRAM_CC_SOC_SW].addr_byte, (u8 *)&cc_soc_sw,
- chip->sp[FG_SRAM_CC_SOC_SW].len, FG_IMA_ATOMIC);
+ rc = fg_prime_cc_soc_sw(chip, cc_soc_sw);
if (rc < 0) {
pr_err("Error in writing cc_soc_sw, rc=%d\n", rc);
goto out;
}
chip->cl.init_cc_soc_sw = cc_soc_sw;
- chip->cl.active = true;
fg_dbg(chip, FG_CAP_LEARN, "Capacity learning started @ battery SOC %d init_cc_soc_sw:%d\n",
batt_soc_msb, chip->cl.init_cc_soc_sw);
out:
@@ -1482,9 +1508,7 @@
/* Write a FULL value to cc_soc_sw */
cc_soc_sw = CC_SOC_30BIT;
- rc = fg_sram_write(chip, chip->sp[FG_SRAM_CC_SOC_SW].addr_word,
- chip->sp[FG_SRAM_CC_SOC_SW].addr_byte, (u8 *)&cc_soc_sw,
- chip->sp[FG_SRAM_CC_SOC_SW].len, FG_IMA_ATOMIC);
+ rc = fg_prime_cc_soc_sw(chip, cc_soc_sw);
if (rc < 0) {
pr_err("Error in writing cc_soc_sw, rc=%d\n", rc);
goto out;
@@ -1497,8 +1521,9 @@
static void fg_cap_learning_update(struct fg_chip *chip)
{
- int rc, batt_soc, batt_soc_msb;
+ int rc, batt_soc, batt_soc_msb, cc_soc_sw;
bool input_present = is_input_present(chip);
+ bool prime_cc = false;
mutex_lock(&chip->cl.lock);
@@ -1511,6 +1536,9 @@
goto out;
}
+ if (chip->charge_status == chip->prev_charge_status)
+ goto out;
+
rc = fg_get_sram_prop(chip, FG_SRAM_BATT_SOC, &batt_soc);
if (rc < 0) {
pr_err("Error in getting ACT_BATT_CAP, rc=%d\n", rc);
@@ -1526,8 +1554,12 @@
if (chip->charge_status == POWER_SUPPLY_STATUS_CHARGING) {
rc = fg_cap_learning_begin(chip, batt_soc);
chip->cl.active = (rc == 0);
+ } else {
+ if ((chip->charge_status ==
+ POWER_SUPPLY_STATUS_DISCHARGING) ||
+ chip->charge_done)
+ prime_cc = true;
}
-
} else {
if (chip->charge_done) {
rc = fg_cap_learning_done(chip);
@@ -1545,6 +1577,7 @@
batt_soc_msb);
chip->cl.active = false;
chip->cl.init_cc_uah = 0;
+ prime_cc = true;
}
}
@@ -1561,10 +1594,29 @@
batt_soc_msb);
chip->cl.active = false;
chip->cl.init_cc_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) {
+ if (chip->charge_done)
+ cc_soc_sw = CC_SOC_30BIT;
+ else
+ cc_soc_sw = div_u64((u32)batt_soc *
+ CC_SOC_30BIT, BATT_SOC_32BIT);
+
+ rc = fg_prime_cc_soc_sw(chip, cc_soc_sw);
+ if (rc < 0)
+ pr_err("Error in writing cc_soc_sw, rc=%d\n",
+ rc);
+ }
+
out:
mutex_unlock(&chip->cl.lock);
}
@@ -1779,7 +1831,8 @@
fg_dbg(chip, FG_STATUS, "Terminated charging @ SOC%d\n",
msoc);
}
- } else if (msoc_raw <= recharge_soc && chip->charge_full) {
+ } else if ((msoc_raw <= recharge_soc || !chip->charge_done)
+ && chip->charge_full) {
if (chip->dt.linearize_soc) {
chip->delta_soc = FULL_CAPACITY - msoc;
@@ -2563,7 +2616,7 @@
}
fg_ttf_update(chip);
-
+ chip->prev_charge_status = chip->charge_status;
out:
fg_dbg(chip, FG_POWER_SUPPLY, "charge_status:%d charge_type:%d charge_done:%d\n",
chip->charge_status, chip->charge_type, chip->charge_done);
@@ -3555,6 +3608,9 @@
case POWER_SUPPLY_PROP_CHARGE_COUNTER:
rc = fg_get_charge_counter(chip, &pval->intval);
break;
+ case POWER_SUPPLY_PROP_CHARGE_COUNTER_SHADOW:
+ rc = fg_get_charge_counter_shadow(chip, &pval->intval);
+ break;
case POWER_SUPPLY_PROP_TIME_TO_FULL_AVG:
rc = fg_get_time_to_full(chip, &pval->intval);
break;
@@ -3765,6 +3821,7 @@
POWER_SUPPLY_PROP_CHARGE_NOW,
POWER_SUPPLY_PROP_CHARGE_FULL,
POWER_SUPPLY_PROP_CHARGE_COUNTER,
+ POWER_SUPPLY_PROP_CHARGE_COUNTER_SHADOW,
POWER_SUPPLY_PROP_TIME_TO_FULL_AVG,
POWER_SUPPLY_PROP_TIME_TO_EMPTY_AVG,
POWER_SUPPLY_PROP_SOC_REPORTING_READY,
@@ -4117,8 +4174,7 @@
}
fg_dbg(chip, FG_IRQ, "irq %d triggered, status:%d\n", irq, status);
- if (status & MEM_GNT_BIT)
- complete_all(&chip->mem_grant);
+ complete_all(&chip->mem_grant);
return IRQ_HANDLED;
}
@@ -4586,7 +4642,7 @@
#define DEFAULT_BATT_TEMP_HOT 50
#define DEFAULT_CL_START_SOC 15
#define DEFAULT_CL_MIN_TEMP_DECIDEGC 150
-#define DEFAULT_CL_MAX_TEMP_DECIDEGC 450
+#define DEFAULT_CL_MAX_TEMP_DECIDEGC 500
#define DEFAULT_CL_MAX_INC_DECIPERC 5
#define DEFAULT_CL_MAX_DEC_DECIPERC 100
#define DEFAULT_CL_MIN_LIM_DECIPERC 0
@@ -5001,6 +5057,7 @@
chip->debug_mask = &fg_gen3_debug_mask;
chip->irqs = fg_irqs;
chip->charge_status = -EINVAL;
+ chip->prev_charge_status = -EINVAL;
chip->ki_coeff_full_soc = -EINVAL;
chip->online_status = -EINVAL;
chip->regmap = dev_get_regmap(chip->dev->parent, NULL);
diff --git a/drivers/power/supply/qcom/qpnp-fg.c b/drivers/power/supply/qcom/qpnp-fg.c
new file mode 100644
index 0000000..17b9c1d3
--- /dev/null
+++ b/drivers/power/supply/qcom/qpnp-fg.c
@@ -0,0 +1,7028 @@
+/* Copyright (c) 2014-2017, 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) "FG: %s: " fmt, __func__
+
+#include <linux/atomic.h>
+#include <linux/delay.h>
+#include <linux/kernel.h>
+#include <linux/regmap.h>
+#include <linux/of.h>
+#include <linux/rtc.h>
+#include <linux/err.h>
+#include <linux/debugfs.h>
+#include <linux/slab.h>
+#include <linux/uaccess.h>
+#include <linux/init.h>
+#include <linux/spmi.h>
+#include <linux/platform_device.h>
+#include <linux/of_irq.h>
+#include <linux/interrupt.h>
+#include <linux/bitops.h>
+#include <linux/types.h>
+#include <linux/module.h>
+#include <linux/ktime.h>
+#include <linux/power_supply.h>
+#include <linux/of_batterydata.h>
+#include <linux/string_helpers.h>
+#include <linux/alarmtimer.h>
+#include <linux/qpnp/qpnp-revid.h>
+
+/* Register offsets */
+
+/* Interrupt offsets */
+#define INT_RT_STS(base) (base + 0x10)
+#define INT_EN_CLR(base) (base + 0x16)
+
+/* SPMI Register offsets */
+#define SOC_MONOTONIC_SOC 0x09
+#define SOC_BOOT_MOD 0x50
+#define SOC_RESTART 0x51
+
+#define REG_OFFSET_PERP_SUBTYPE 0x05
+
+/* RAM register offsets */
+#define RAM_OFFSET 0x400
+
+/* Bit/Mask definitions */
+#define FULL_PERCENT 0xFF
+#define MAX_TRIES_SOC 5
+#define MA_MV_BIT_RES 39
+#define MSB_SIGN BIT(7)
+#define IBAT_VBAT_MASK 0x7F
+#define NO_OTP_PROF_RELOAD BIT(6)
+#define REDO_FIRST_ESTIMATE BIT(3)
+#define RESTART_GO BIT(0)
+#define THERM_DELAY_MASK 0xE0
+
+/* SUBTYPE definitions */
+#define FG_SOC 0x9
+#define FG_BATT 0xA
+#define FG_ADC 0xB
+#define FG_MEMIF 0xC
+
+#define QPNP_FG_DEV_NAME "qcom,qpnp-fg"
+#define MEM_IF_TIMEOUT_MS 5000
+#define BUCKET_COUNT 8
+#define BUCKET_SOC_PCT (256 / BUCKET_COUNT)
+
+#define BCL_MA_TO_ADC(_current, _adc_val) { \
+ _adc_val = (u8)((_current) * 100 / 976); \
+}
+
+/* Debug Flag Definitions */
+enum {
+ FG_SPMI_DEBUG_WRITES = BIT(0), /* Show SPMI writes */
+ FG_SPMI_DEBUG_READS = BIT(1), /* Show SPMI reads */
+ FG_IRQS = BIT(2), /* Show interrupts */
+ FG_MEM_DEBUG_WRITES = BIT(3), /* Show SRAM writes */
+ FG_MEM_DEBUG_READS = BIT(4), /* Show SRAM reads */
+ FG_POWER_SUPPLY = BIT(5), /* Show POWER_SUPPLY */
+ FG_STATUS = BIT(6), /* Show FG status changes */
+ FG_AGING = BIT(7), /* Show FG aging algorithm */
+};
+
+/* PMIC REVISIONS */
+#define REVID_RESERVED 0
+#define REVID_VARIANT 1
+#define REVID_ANA_MAJOR 2
+#define REVID_DIG_MAJOR 3
+
+enum dig_major {
+ DIG_REV_1 = 0x1,
+ DIG_REV_2 = 0x2,
+ DIG_REV_3 = 0x3,
+};
+
+enum pmic_subtype {
+ PMI8994 = 10,
+ PMI8950 = 17,
+ PMI8996 = 19,
+ PMI8937 = 55,
+};
+
+enum wa_flags {
+ IADC_GAIN_COMP_WA = BIT(0),
+ USE_CC_SOC_REG = BIT(1),
+ PULSE_REQUEST_WA = BIT(2),
+ BCL_HI_POWER_FOR_CHGLED_WA = BIT(3)
+};
+
+enum current_sense_type {
+ INTERNAL_CURRENT_SENSE,
+ EXTERNAL_CURRENT_SENSE,
+};
+
+struct fg_mem_setting {
+ u16 address;
+ u8 offset;
+ int value;
+};
+
+struct fg_mem_data {
+ u16 address;
+ u8 offset;
+ unsigned int len;
+ int value;
+};
+
+struct fg_learning_data {
+ int64_t cc_uah;
+ int64_t learned_cc_uah;
+ int init_cc_pc_val;
+ bool active;
+ bool feedback_on;
+ struct mutex learning_lock;
+ ktime_t time_stamp;
+ /* configuration properties */
+ int max_start_soc;
+ int max_increment;
+ int max_decrement;
+ int min_temp;
+ int max_temp;
+ int vbat_est_thr_uv;
+};
+
+struct fg_rslow_data {
+ u8 rslow_cfg;
+ u8 rslow_thr;
+ u8 rs_to_rslow[2];
+ u8 rslow_comp[4];
+ uint32_t chg_rs_to_rslow;
+ uint32_t chg_rslow_comp_c1;
+ uint32_t chg_rslow_comp_c2;
+ uint32_t chg_rslow_comp_thr;
+ bool active;
+ struct mutex lock;
+};
+
+struct fg_cyc_ctr_data {
+ bool en;
+ bool started[BUCKET_COUNT];
+ u16 count[BUCKET_COUNT];
+ u8 last_soc[BUCKET_COUNT];
+ int id;
+ struct mutex lock;
+};
+
+struct fg_iadc_comp_data {
+ u8 dfl_gain_reg[2];
+ bool gain_active;
+ int64_t dfl_gain;
+};
+
+struct fg_cc_soc_data {
+ int init_sys_soc;
+ int init_cc_soc;
+ int full_capacity;
+ int delta_soc;
+};
+
+/* FG_MEMIF setting index */
+enum fg_mem_setting_index {
+ FG_MEM_SOFT_COLD = 0,
+ FG_MEM_SOFT_HOT,
+ FG_MEM_HARD_COLD,
+ FG_MEM_HARD_HOT,
+ FG_MEM_RESUME_SOC,
+ FG_MEM_BCL_LM_THRESHOLD,
+ FG_MEM_BCL_MH_THRESHOLD,
+ FG_MEM_TERM_CURRENT,
+ FG_MEM_CHG_TERM_CURRENT,
+ FG_MEM_IRQ_VOLT_EMPTY,
+ FG_MEM_CUTOFF_VOLTAGE,
+ FG_MEM_VBAT_EST_DIFF,
+ FG_MEM_DELTA_SOC,
+ FG_MEM_BATT_LOW,
+ FG_MEM_THERM_DELAY,
+ FG_MEM_SETTING_MAX,
+};
+
+/* FG_MEMIF data index */
+enum fg_mem_data_index {
+ FG_DATA_BATT_TEMP = 0,
+ FG_DATA_OCV,
+ FG_DATA_VOLTAGE,
+ FG_DATA_CURRENT,
+ FG_DATA_BATT_ESR,
+ FG_DATA_BATT_ESR_COUNT,
+ FG_DATA_BATT_SOC,
+ FG_DATA_CC_CHARGE,
+ FG_DATA_VINT_ERR,
+ FG_DATA_CPRED_VOLTAGE,
+ /* values below this only gets read once per profile reload */
+ FG_DATA_BATT_ID,
+ FG_DATA_BATT_ID_INFO,
+ FG_DATA_MAX,
+};
+
+#define SETTING(_idx, _address, _offset, _value) \
+ [FG_MEM_##_idx] = { \
+ .address = _address, \
+ .offset = _offset, \
+ .value = _value, \
+ } \
+
+static struct fg_mem_setting settings[FG_MEM_SETTING_MAX] = {
+ /* ID Address, Offset, Value*/
+ SETTING(SOFT_COLD, 0x454, 0, 100),
+ SETTING(SOFT_HOT, 0x454, 1, 400),
+ SETTING(HARD_COLD, 0x454, 2, 50),
+ SETTING(HARD_HOT, 0x454, 3, 450),
+ SETTING(RESUME_SOC, 0x45C, 1, 0),
+ SETTING(BCL_LM_THRESHOLD, 0x47C, 2, 50),
+ SETTING(BCL_MH_THRESHOLD, 0x47C, 3, 752),
+ SETTING(TERM_CURRENT, 0x40C, 2, 250),
+ SETTING(CHG_TERM_CURRENT, 0x4F8, 2, 250),
+ SETTING(IRQ_VOLT_EMPTY, 0x458, 3, 3100),
+ SETTING(CUTOFF_VOLTAGE, 0x40C, 0, 3200),
+ SETTING(VBAT_EST_DIFF, 0x000, 0, 30),
+ SETTING(DELTA_SOC, 0x450, 3, 1),
+ SETTING(BATT_LOW, 0x458, 0, 4200),
+ SETTING(THERM_DELAY, 0x4AC, 3, 0),
+};
+
+#define DATA(_idx, _address, _offset, _length, _value) \
+ [FG_DATA_##_idx] = { \
+ .address = _address, \
+ .offset = _offset, \
+ .len = _length, \
+ .value = _value, \
+ } \
+
+static struct fg_mem_data fg_data[FG_DATA_MAX] = {
+ /* ID Address, Offset, Length, Value*/
+ DATA(BATT_TEMP, 0x550, 2, 2, -EINVAL),
+ DATA(OCV, 0x588, 3, 2, -EINVAL),
+ DATA(VOLTAGE, 0x5CC, 1, 2, -EINVAL),
+ DATA(CURRENT, 0x5CC, 3, 2, -EINVAL),
+ DATA(BATT_ESR, 0x554, 2, 2, -EINVAL),
+ DATA(BATT_ESR_COUNT, 0x558, 2, 2, -EINVAL),
+ DATA(BATT_SOC, 0x56C, 1, 3, -EINVAL),
+ DATA(CC_CHARGE, 0x570, 0, 4, -EINVAL),
+ DATA(VINT_ERR, 0x560, 0, 4, -EINVAL),
+ DATA(CPRED_VOLTAGE, 0x540, 0, 2, -EINVAL),
+ DATA(BATT_ID, 0x594, 1, 1, -EINVAL),
+ DATA(BATT_ID_INFO, 0x594, 3, 1, -EINVAL),
+};
+
+static int fg_debug_mask;
+module_param_named(
+ debug_mask, fg_debug_mask, int, 00600
+);
+
+static int fg_sense_type = -EINVAL;
+static int fg_restart;
+
+static int fg_est_dump;
+module_param_named(
+ first_est_dump, fg_est_dump, int, 00600
+);
+
+static char *fg_batt_type;
+module_param_named(
+ battery_type, fg_batt_type, charp, 00600
+);
+
+static int fg_sram_update_period_ms = 30000;
+module_param_named(
+ sram_update_period_ms, fg_sram_update_period_ms, int, 00600
+);
+
+struct fg_irq {
+ int irq;
+ unsigned long disabled;
+};
+
+enum fg_soc_irq {
+ HIGH_SOC,
+ LOW_SOC,
+ FULL_SOC,
+ EMPTY_SOC,
+ DELTA_SOC,
+ FIRST_EST_DONE,
+ SW_FALLBK_OCV,
+ SW_FALLBK_NEW_BATT,
+ FG_SOC_IRQ_COUNT,
+};
+
+enum fg_batt_irq {
+ JEITA_SOFT_COLD,
+ JEITA_SOFT_HOT,
+ VBATT_LOW,
+ BATT_IDENTIFIED,
+ BATT_ID_REQ,
+ BATTERY_UNKNOWN,
+ BATT_MISSING,
+ BATT_MATCH,
+ FG_BATT_IRQ_COUNT,
+};
+
+enum fg_mem_if_irq {
+ FG_MEM_AVAIL,
+ TA_RCVRY_SUG,
+ FG_MEM_IF_IRQ_COUNT,
+};
+
+enum fg_batt_aging_mode {
+ FG_AGING_NONE,
+ FG_AGING_ESR,
+ FG_AGING_CC,
+};
+
+enum register_type {
+ MEM_INTF_CFG,
+ MEM_INTF_CTL,
+ MEM_INTF_ADDR_LSB,
+ MEM_INTF_RD_DATA0,
+ MEM_INTF_WR_DATA0,
+ MAX_ADDRESS,
+};
+
+struct register_offset {
+ u16 address[MAX_ADDRESS];
+};
+
+static struct register_offset offset[] = {
+ [0] = {
+ /* CFG CTL LSB RD0 WD0 */
+ .address = {0x40, 0x41, 0x42, 0x4C, 0x48},
+ },
+ [1] = {
+ /* CFG CTL LSB RD0 WD0 */
+ .address = {0x50, 0x51, 0x61, 0x67, 0x63},
+ },
+};
+
+#define MEM_INTF_CFG(chip) \
+ ((chip)->mem_base + (chip)->offset[MEM_INTF_CFG])
+#define MEM_INTF_CTL(chip) \
+ ((chip)->mem_base + (chip)->offset[MEM_INTF_CTL])
+#define MEM_INTF_ADDR_LSB(chip) \
+ ((chip)->mem_base + (chip)->offset[MEM_INTF_ADDR_LSB])
+#define MEM_INTF_RD_DATA0(chip) \
+ ((chip)->mem_base + (chip)->offset[MEM_INTF_RD_DATA0])
+#define MEM_INTF_WR_DATA0(chip) \
+ ((chip)->mem_base + (chip)->offset[MEM_INTF_WR_DATA0])
+
+struct fg_wakeup_source {
+ struct wakeup_source source;
+ unsigned long enabled;
+};
+
+static void fg_stay_awake(struct fg_wakeup_source *source)
+{
+ if (!__test_and_set_bit(0, &source->enabled)) {
+ __pm_stay_awake(&source->source);
+ pr_debug("enabled source %s\n", source->source.name);
+ }
+}
+
+static void fg_relax(struct fg_wakeup_source *source)
+{
+ if (__test_and_clear_bit(0, &source->enabled)) {
+ __pm_relax(&source->source);
+ pr_debug("disabled source %s\n", source->source.name);
+ }
+}
+
+#define THERMAL_COEFF_N_BYTES 6
+struct fg_chip {
+ struct device *dev;
+ struct platform_device *pdev;
+ struct regmap *regmap;
+ u8 pmic_subtype;
+ u8 pmic_revision[4];
+ u8 revision[4];
+ u16 soc_base;
+ u16 batt_base;
+ u16 mem_base;
+ u16 vbat_adc_addr;
+ u16 ibat_adc_addr;
+ u16 tp_rev_addr;
+ u32 wa_flag;
+ atomic_t memif_user_cnt;
+ struct fg_irq soc_irq[FG_SOC_IRQ_COUNT];
+ struct fg_irq batt_irq[FG_BATT_IRQ_COUNT];
+ struct fg_irq mem_irq[FG_MEM_IF_IRQ_COUNT];
+ struct completion sram_access_granted;
+ struct completion sram_access_revoked;
+ struct completion batt_id_avail;
+ struct completion first_soc_done;
+ struct power_supply *bms_psy;
+ struct power_supply_desc bms_psy_d;
+ struct mutex rw_lock;
+ struct mutex sysfs_restart_lock;
+ struct delayed_work batt_profile_init;
+ struct work_struct dump_sram;
+ struct work_struct status_change_work;
+ struct work_struct cycle_count_work;
+ struct work_struct battery_age_work;
+ struct work_struct update_esr_work;
+ struct work_struct set_resume_soc_work;
+ struct work_struct rslow_comp_work;
+ struct work_struct sysfs_restart_work;
+ struct work_struct init_work;
+ struct work_struct charge_full_work;
+ struct work_struct gain_comp_work;
+ struct work_struct bcl_hi_power_work;
+ struct power_supply *batt_psy;
+ struct power_supply *usb_psy;
+ struct power_supply *dc_psy;
+ struct fg_wakeup_source memif_wakeup_source;
+ struct fg_wakeup_source profile_wakeup_source;
+ struct fg_wakeup_source empty_check_wakeup_source;
+ struct fg_wakeup_source resume_soc_wakeup_source;
+ struct fg_wakeup_source gain_comp_wakeup_source;
+ struct fg_wakeup_source capacity_learning_wakeup_source;
+ bool first_profile_loaded;
+ struct fg_wakeup_source update_temp_wakeup_source;
+ struct fg_wakeup_source update_sram_wakeup_source;
+ bool fg_restarting;
+ bool profile_loaded;
+ bool use_otp_profile;
+ bool battery_missing;
+ bool power_supply_registered;
+ bool sw_rbias_ctrl;
+ bool use_thermal_coefficients;
+ bool esr_strict_filter;
+ bool soc_empty;
+ bool charge_done;
+ bool resume_soc_lowered;
+ bool vbat_low_irq_enabled;
+ bool charge_full;
+ bool hold_soc_while_full;
+ bool input_present;
+ bool otg_present;
+ bool safety_timer_expired;
+ bool bad_batt_detection_en;
+ bool bcl_lpm_disabled;
+ bool charging_disabled;
+ struct delayed_work update_jeita_setting;
+ struct delayed_work update_sram_data;
+ struct delayed_work update_temp_work;
+ struct delayed_work check_empty_work;
+ char *batt_profile;
+ u8 thermal_coefficients[THERMAL_COEFF_N_BYTES];
+ u32 cc_cv_threshold_mv;
+ unsigned int batt_profile_len;
+ unsigned int batt_max_voltage_uv;
+ const char *batt_type;
+ const char *batt_psy_name;
+ unsigned long last_sram_update_time;
+ unsigned long last_temp_update_time;
+ int64_t ocv_coeffs[12];
+ int64_t cutoff_voltage;
+ int evaluation_current;
+ int ocv_junction_p1p2;
+ int ocv_junction_p2p3;
+ int nom_cap_uah;
+ int actual_cap_uah;
+ int status;
+ int prev_status;
+ int health;
+ enum fg_batt_aging_mode batt_aging_mode;
+ /* capacity learning */
+ struct fg_learning_data learning_data;
+ struct alarm fg_cap_learning_alarm;
+ struct work_struct fg_cap_learning_work;
+ struct fg_cc_soc_data sw_cc_soc_data;
+ /* rslow compensation */
+ struct fg_rslow_data rslow_comp;
+ /* cycle counter */
+ struct fg_cyc_ctr_data cyc_ctr;
+ /* iadc compensation */
+ struct fg_iadc_comp_data iadc_comp_data;
+ /* interleaved memory access */
+ u16 *offset;
+ bool ima_supported;
+ bool init_done;
+ /* jeita hysteresis */
+ bool jeita_hysteresis_support;
+ bool batt_hot;
+ bool batt_cold;
+ int cold_hysteresis;
+ int hot_hysteresis;
+ /* ESR pulse tuning */
+ struct fg_wakeup_source esr_extract_wakeup_source;
+ struct work_struct esr_extract_config_work;
+ bool esr_extract_disabled;
+ bool imptr_pulse_slow_en;
+ bool esr_pulse_tune_en;
+};
+
+/* FG_MEMIF DEBUGFS structures */
+#define ADDR_LEN 4 /* 3 byte address + 1 space character */
+#define CHARS_PER_ITEM 3 /* Format is 'XX ' */
+#define ITEMS_PER_LINE 4 /* 4 data items per line */
+#define MAX_LINE_LENGTH (ADDR_LEN + (ITEMS_PER_LINE * CHARS_PER_ITEM) + 1)
+#define MAX_REG_PER_TRANSACTION (8)
+
+static const char *DFS_ROOT_NAME = "fg_memif";
+static const mode_t DFS_MODE = 00600;
+static const char *default_batt_type = "Unknown Battery";
+static const char *loading_batt_type = "Loading Battery Data";
+static const char *missing_batt_type = "Disconnected Battery";
+
+/* Log buffer */
+struct fg_log_buffer {
+ size_t rpos; /* Current 'read' position in buffer */
+ size_t wpos; /* Current 'write' position in buffer */
+ size_t len; /* Length of the buffer */
+ char data[0]; /* Log buffer */
+};
+
+/* transaction parameters */
+struct fg_trans {
+ u32 cnt; /* Number of bytes to read */
+ u16 addr; /* 12-bit address in SRAM */
+ u32 offset; /* Offset of last read data + byte offset */
+ struct fg_chip *chip;
+ struct fg_log_buffer *log; /* log buffer */
+ u8 *data; /* fg data that is read */
+};
+
+struct fg_dbgfs {
+ u32 cnt;
+ u32 addr;
+ struct fg_chip *chip;
+ struct dentry *root;
+ struct mutex lock;
+ struct debugfs_blob_wrapper help_msg;
+};
+
+static struct fg_dbgfs dbgfs_data = {
+ .lock = __MUTEX_INITIALIZER(dbgfs_data.lock),
+ .help_msg = {
+ .data =
+"FG Debug-FS support\n"
+"\n"
+"Hierarchy schema:\n"
+"/sys/kernel/debug/fg_memif\n"
+" /help -- Static help text\n"
+" /address -- Starting register address for reads or writes\n"
+" /count -- Number of registers to read (only used for reads)\n"
+" /data -- Initiates the SRAM read (formatted output)\n"
+"\n",
+ },
+};
+
+static const struct of_device_id fg_match_table[] = {
+ { .compatible = QPNP_FG_DEV_NAME, },
+ {}
+};
+
+static char *fg_supplicants[] = {
+ "battery",
+ "bcl",
+ "fg_adc"
+};
+
+#define DEBUG_PRINT_BUFFER_SIZE 64
+static void fill_string(char *str, size_t str_len, u8 *buf, int buf_len)
+{
+ int pos = 0;
+ int i;
+
+ for (i = 0; i < buf_len; i++) {
+ pos += scnprintf(str + pos, str_len - pos, "%02X", buf[i]);
+ if (i < buf_len - 1)
+ pos += scnprintf(str + pos, str_len - pos, " ");
+ }
+}
+
+static int fg_write(struct fg_chip *chip, u8 *val, u16 addr, int len)
+{
+ int rc = 0;
+ struct platform_device *pdev = chip->pdev;
+ char str[DEBUG_PRINT_BUFFER_SIZE];
+
+ if ((addr & 0xff00) == 0) {
+ pr_err("addr cannot be zero base=0x%02x sid=0x%02x rc=%d\n",
+ addr, to_spmi_device(pdev->dev.parent)->usid, rc);
+ return -EINVAL;
+ }
+
+ rc = regmap_bulk_write(chip->regmap, addr, val, len);
+ if (rc) {
+ pr_err("write failed addr=0x%02x sid=0x%02x rc=%d\n",
+ addr, to_spmi_device(pdev->dev.parent)->usid, rc);
+ return rc;
+ }
+
+ if (!rc && (fg_debug_mask & FG_SPMI_DEBUG_WRITES)) {
+ str[0] = '\0';
+ fill_string(str, DEBUG_PRINT_BUFFER_SIZE, val, len);
+ pr_info("write(0x%04X), sid=%d, len=%d; %s\n",
+ addr, to_spmi_device(pdev->dev.parent)->usid, len,
+ str);
+ }
+
+ return rc;
+}
+
+static int fg_read(struct fg_chip *chip, u8 *val, u16 addr, int len)
+{
+ int rc = 0;
+ struct platform_device *pdev = chip->pdev;
+ char str[DEBUG_PRINT_BUFFER_SIZE];
+
+ if ((addr & 0xff00) == 0) {
+ pr_err("base cannot be zero base=0x%02x sid=0x%02x rc=%d\n",
+ addr, to_spmi_device(pdev->dev.parent)->usid, rc);
+ return -EINVAL;
+ }
+
+ rc = regmap_bulk_read(chip->regmap, addr, val, len);
+ if (rc) {
+ pr_err("SPMI read failed base=0x%02x sid=0x%02x rc=%d\n", addr,
+ to_spmi_device(pdev->dev.parent)->usid, rc);
+ return rc;
+ }
+
+ if (!rc && (fg_debug_mask & FG_SPMI_DEBUG_READS)) {
+ str[0] = '\0';
+ fill_string(str, DEBUG_PRINT_BUFFER_SIZE, val, len);
+ pr_info("read(0x%04x), sid=%d, len=%d; %s\n",
+ addr, to_spmi_device(pdev->dev.parent)->usid, len,
+ str);
+ }
+
+ return rc;
+}
+
+static int fg_masked_write(struct fg_chip *chip, u16 addr,
+ u8 mask, u8 val, int len)
+{
+ int rc;
+
+ rc = regmap_update_bits(chip->regmap, addr, mask, val);
+ if (rc) {
+ pr_err("spmi write failed: addr=%03X, rc=%d\n", addr, rc);
+ return rc;
+ }
+
+ return rc;
+}
+
+#define RIF_MEM_ACCESS_REQ BIT(7)
+static int fg_check_rif_mem_access(struct fg_chip *chip, bool *status)
+{
+ int rc;
+ u8 mem_if_sts;
+
+ rc = fg_read(chip, &mem_if_sts, MEM_INTF_CFG(chip), 1);
+ if (rc) {
+ pr_err("failed to read rif_mem status rc=%d\n", rc);
+ return rc;
+ }
+
+ *status = mem_if_sts & RIF_MEM_ACCESS_REQ;
+ return 0;
+}
+
+static bool fg_check_sram_access(struct fg_chip *chip)
+{
+ int rc;
+ u8 mem_if_sts;
+ bool rif_mem_sts = false;
+
+ rc = fg_read(chip, &mem_if_sts, INT_RT_STS(chip->mem_base), 1);
+ if (rc) {
+ pr_err("failed to read mem status rc=%d\n", rc);
+ return false;
+ }
+
+ if ((mem_if_sts & BIT(FG_MEM_AVAIL)) == 0)
+ return false;
+
+ rc = fg_check_rif_mem_access(chip, &rif_mem_sts);
+ if (rc)
+ return false;
+
+ return rif_mem_sts;
+}
+
+static inline int fg_assert_sram_access(struct fg_chip *chip)
+{
+ int rc;
+ u8 mem_if_sts;
+
+ rc = fg_read(chip, &mem_if_sts, INT_RT_STS(chip->mem_base), 1);
+ if (rc) {
+ pr_err("failed to read mem status rc=%d\n", rc);
+ return rc;
+ }
+
+ if ((mem_if_sts & BIT(FG_MEM_AVAIL)) == 0) {
+ pr_err("mem_avail not high: %02x\n", mem_if_sts);
+ return -EINVAL;
+ }
+
+ rc = fg_read(chip, &mem_if_sts, MEM_INTF_CFG(chip), 1);
+ if (rc) {
+ pr_err("failed to read mem status rc=%d\n", rc);
+ return rc;
+ }
+
+ if ((mem_if_sts & RIF_MEM_ACCESS_REQ) == 0) {
+ pr_err("mem_avail not high: %02x\n", mem_if_sts);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+#define INTF_CTL_BURST BIT(7)
+#define INTF_CTL_WR_EN BIT(6)
+static int fg_config_access(struct fg_chip *chip, bool write,
+ bool burst)
+{
+ int rc;
+ u8 intf_ctl = 0;
+
+ intf_ctl = (write ? INTF_CTL_WR_EN : 0) | (burst ? INTF_CTL_BURST : 0);
+
+ rc = fg_write(chip, &intf_ctl, MEM_INTF_CTL(chip), 1);
+ if (rc) {
+ pr_err("failed to set mem access bit\n");
+ return -EIO;
+ }
+
+ return rc;
+}
+
+static int fg_req_and_wait_access(struct fg_chip *chip, int timeout)
+{
+ int rc = 0, ret = 0;
+ bool tried_again = false;
+
+ if (!fg_check_sram_access(chip)) {
+ rc = fg_masked_write(chip, MEM_INTF_CFG(chip),
+ RIF_MEM_ACCESS_REQ, RIF_MEM_ACCESS_REQ, 1);
+ if (rc) {
+ pr_err("failed to set mem access bit\n");
+ return -EIO;
+ }
+ fg_stay_awake(&chip->memif_wakeup_source);
+ }
+
+wait:
+ /* Wait for MEM_AVAIL IRQ. */
+ ret = wait_for_completion_interruptible_timeout(
+ &chip->sram_access_granted,
+ msecs_to_jiffies(timeout));
+ /* If we were interrupted wait again one more time. */
+ if (ret == -ERESTARTSYS && !tried_again) {
+ tried_again = true;
+ goto wait;
+ } else if (ret <= 0) {
+ rc = -ETIMEDOUT;
+ pr_err("transaction timed out rc=%d\n", rc);
+ return rc;
+ }
+
+ return rc;
+}
+
+static int fg_release_access(struct fg_chip *chip)
+{
+ int rc;
+
+ rc = fg_masked_write(chip, MEM_INTF_CFG(chip),
+ RIF_MEM_ACCESS_REQ, 0, 1);
+ fg_relax(&chip->memif_wakeup_source);
+ reinit_completion(&chip->sram_access_granted);
+
+ return rc;
+}
+
+static void fg_release_access_if_necessary(struct fg_chip *chip)
+{
+ mutex_lock(&chip->rw_lock);
+ if (atomic_sub_return(1, &chip->memif_user_cnt) <= 0)
+ fg_release_access(chip);
+ mutex_unlock(&chip->rw_lock);
+}
+
+/*
+ * fg_mem_lock disallows the fuel gauge to release access until it has been
+ * released.
+ *
+ * an equal number of calls must be made to fg_mem_release for the fuel gauge
+ * driver to release the sram access.
+ */
+static void fg_mem_lock(struct fg_chip *chip)
+{
+ mutex_lock(&chip->rw_lock);
+ atomic_add_return(1, &chip->memif_user_cnt);
+ mutex_unlock(&chip->rw_lock);
+}
+
+static void fg_mem_release(struct fg_chip *chip)
+{
+ fg_release_access_if_necessary(chip);
+}
+
+static int fg_set_ram_addr(struct fg_chip *chip, u16 *address)
+{
+ int rc;
+
+ rc = fg_write(chip, (u8 *) address,
+ chip->mem_base + chip->offset[MEM_INTF_ADDR_LSB], 2);
+ if (rc) {
+ pr_err("spmi write failed: addr=%03X, rc=%d\n",
+ chip->mem_base + chip->offset[MEM_INTF_ADDR_LSB], rc);
+ return rc;
+ }
+
+ return rc;
+}
+
+#define BUF_LEN 4
+static int fg_sub_mem_read(struct fg_chip *chip, u8 *val, u16 address, int len,
+ int offset)
+{
+ int rc, total_len;
+ u8 *rd_data = val;
+ char str[DEBUG_PRINT_BUFFER_SIZE];
+
+ rc = fg_config_access(chip, 0, (len > 4));
+ if (rc)
+ return rc;
+
+ rc = fg_set_ram_addr(chip, &address);
+ if (rc)
+ return rc;
+
+ if (fg_debug_mask & FG_MEM_DEBUG_READS)
+ pr_info("length %d addr=%02X\n", len, address);
+
+ total_len = len;
+ while (len > 0) {
+ if (!offset) {
+ rc = fg_read(chip, rd_data, MEM_INTF_RD_DATA0(chip),
+ min(len, BUF_LEN));
+ } else {
+ rc = fg_read(chip, rd_data,
+ MEM_INTF_RD_DATA0(chip) + offset,
+ min(len, BUF_LEN - offset));
+
+ /* manually set address to allow continuous reads */
+ address += BUF_LEN;
+
+ rc = fg_set_ram_addr(chip, &address);
+ if (rc)
+ return rc;
+ }
+ if (rc) {
+ pr_err("spmi read failed: addr=%03x, rc=%d\n",
+ MEM_INTF_RD_DATA0(chip) + offset, rc);
+ return rc;
+ }
+ rd_data += (BUF_LEN - offset);
+ len -= (BUF_LEN - offset);
+ offset = 0;
+ }
+
+ if (fg_debug_mask & FG_MEM_DEBUG_READS) {
+ fill_string(str, DEBUG_PRINT_BUFFER_SIZE, val, total_len);
+ pr_info("data: %s\n", str);
+ }
+ return rc;
+}
+
+static int fg_conventional_mem_read(struct fg_chip *chip, u8 *val, u16 address,
+ int len, int offset, bool keep_access)
+{
+ int rc = 0, user_cnt = 0, orig_address = address;
+
+ if (offset > 3) {
+ pr_err("offset too large %d\n", offset);
+ return -EINVAL;
+ }
+
+ address = ((orig_address + offset) / 4) * 4;
+ offset = (orig_address + offset) % 4;
+
+ user_cnt = atomic_add_return(1, &chip->memif_user_cnt);
+ if (fg_debug_mask & FG_MEM_DEBUG_READS)
+ pr_info("user_cnt %d\n", user_cnt);
+ mutex_lock(&chip->rw_lock);
+ if (!fg_check_sram_access(chip)) {
+ rc = fg_req_and_wait_access(chip, MEM_IF_TIMEOUT_MS);
+ if (rc)
+ goto out;
+ }
+
+ rc = fg_sub_mem_read(chip, val, address, len, offset);
+
+out:
+ user_cnt = atomic_sub_return(1, &chip->memif_user_cnt);
+ if (fg_debug_mask & FG_MEM_DEBUG_READS)
+ pr_info("user_cnt %d\n", user_cnt);
+
+ fg_assert_sram_access(chip);
+
+ if (!keep_access && (user_cnt == 0) && !rc) {
+ rc = fg_release_access(chip);
+ if (rc) {
+ pr_err("failed to set mem access bit\n");
+ rc = -EIO;
+ }
+ }
+
+ mutex_unlock(&chip->rw_lock);
+ return rc;
+}
+
+static int fg_conventional_mem_write(struct fg_chip *chip, u8 *val, u16 address,
+ int len, int offset, bool keep_access)
+{
+ int rc = 0, user_cnt = 0, sublen;
+ bool access_configured = false;
+ u8 *wr_data = val, word[4];
+ char str[DEBUG_PRINT_BUFFER_SIZE];
+
+ if (address < RAM_OFFSET)
+ return -EINVAL;
+
+ if (offset > 3)
+ return -EINVAL;
+
+ address = ((address + offset) / 4) * 4;
+ offset = (address + offset) % 4;
+
+ user_cnt = atomic_add_return(1, &chip->memif_user_cnt);
+ if (fg_debug_mask & FG_MEM_DEBUG_WRITES)
+ pr_info("user_cnt %d\n", user_cnt);
+ mutex_lock(&chip->rw_lock);
+ if (!fg_check_sram_access(chip)) {
+ rc = fg_req_and_wait_access(chip, MEM_IF_TIMEOUT_MS);
+ if (rc)
+ goto out;
+ }
+
+ if (fg_debug_mask & FG_MEM_DEBUG_WRITES) {
+ pr_info("length %d addr=%02X offset=%d\n",
+ len, address, offset);
+ fill_string(str, DEBUG_PRINT_BUFFER_SIZE, wr_data, len);
+ pr_info("writing: %s\n", str);
+ }
+
+ while (len > 0) {
+ if (offset != 0) {
+ sublen = min(4 - offset, len);
+ rc = fg_sub_mem_read(chip, word, address, 4, 0);
+ if (rc)
+ goto out;
+ memcpy(word + offset, wr_data, sublen);
+ /* configure access as burst if more to write */
+ rc = fg_config_access(chip, 1, (len - sublen) > 0);
+ if (rc)
+ goto out;
+ rc = fg_set_ram_addr(chip, &address);
+ if (rc)
+ goto out;
+ offset = 0;
+ access_configured = true;
+ } else if (len >= 4) {
+ if (!access_configured) {
+ rc = fg_config_access(chip, 1, len > 4);
+ if (rc)
+ goto out;
+ rc = fg_set_ram_addr(chip, &address);
+ if (rc)
+ goto out;
+ access_configured = true;
+ }
+ sublen = 4;
+ memcpy(word, wr_data, 4);
+ } else if (len > 0 && len < 4) {
+ sublen = len;
+ rc = fg_sub_mem_read(chip, word, address, 4, 0);
+ if (rc)
+ goto out;
+ memcpy(word, wr_data, sublen);
+ rc = fg_config_access(chip, 1, 0);
+ if (rc)
+ goto out;
+ rc = fg_set_ram_addr(chip, &address);
+ if (rc)
+ goto out;
+ access_configured = true;
+ } else {
+ pr_err("Invalid length: %d\n", len);
+ break;
+ }
+ rc = fg_write(chip, word, MEM_INTF_WR_DATA0(chip), 4);
+ if (rc) {
+ pr_err("spmi write failed: addr=%03x, rc=%d\n",
+ MEM_INTF_WR_DATA0(chip), rc);
+ goto out;
+ }
+ len -= sublen;
+ wr_data += sublen;
+ address += 4;
+ }
+
+out:
+ user_cnt = atomic_sub_return(1, &chip->memif_user_cnt);
+ if (fg_debug_mask & FG_MEM_DEBUG_WRITES)
+ pr_info("user_cnt %d\n", user_cnt);
+
+ fg_assert_sram_access(chip);
+
+ if (!keep_access && (user_cnt == 0) && !rc) {
+ rc = fg_release_access(chip);
+ if (rc) {
+ pr_err("failed to set mem access bit\n");
+ rc = -EIO;
+ }
+ }
+
+ mutex_unlock(&chip->rw_lock);
+ return rc;
+}
+
+#define MEM_INTF_IMA_CFG 0x52
+#define MEM_INTF_IMA_OPR_STS 0x54
+#define MEM_INTF_IMA_ERR_STS 0x5F
+#define MEM_INTF_IMA_EXP_STS 0x55
+#define MEM_INTF_IMA_HW_STS 0x56
+#define MEM_INTF_IMA_BYTE_EN 0x60
+#define IMA_ADDR_STBL_ERR BIT(7)
+#define IMA_WR_ACS_ERR BIT(6)
+#define IMA_RD_ACS_ERR BIT(5)
+#define IMA_IACS_CLR BIT(2)
+#define IMA_IACS_RDY BIT(1)
+static int fg_check_ima_exception(struct fg_chip *chip)
+{
+ int rc = 0, ret = 0;
+ u8 err_sts, exp_sts = 0, hw_sts = 0;
+
+ rc = fg_read(chip, &err_sts,
+ chip->mem_base + MEM_INTF_IMA_ERR_STS, 1);
+ if (rc) {
+ pr_err("failed to read beat count rc=%d\n", rc);
+ return rc;
+ }
+
+ if (err_sts & (IMA_ADDR_STBL_ERR | IMA_WR_ACS_ERR | IMA_RD_ACS_ERR)) {
+ u8 temp;
+
+ fg_read(chip, &exp_sts,
+ chip->mem_base + MEM_INTF_IMA_EXP_STS, 1);
+ fg_read(chip, &hw_sts,
+ chip->mem_base + MEM_INTF_IMA_HW_STS, 1);
+ pr_err("IMA access failed ima_err_sts=%x ima_exp_sts=%x ima_hw_sts=%x\n",
+ err_sts, exp_sts, hw_sts);
+ rc = err_sts;
+
+ /* clear the error */
+ ret |= fg_masked_write(chip, chip->mem_base + MEM_INTF_IMA_CFG,
+ IMA_IACS_CLR, IMA_IACS_CLR, 1);
+ temp = 0x4;
+ ret |= fg_write(chip, &temp, MEM_INTF_ADDR_LSB(chip) + 1, 1);
+ temp = 0x0;
+ ret |= fg_write(chip, &temp, MEM_INTF_WR_DATA0(chip) + 3, 1);
+ ret |= fg_read(chip, &temp, MEM_INTF_RD_DATA0(chip) + 3, 1);
+ ret |= fg_masked_write(chip, chip->mem_base + MEM_INTF_IMA_CFG,
+ IMA_IACS_CLR, 0, 1);
+ if (!ret)
+ return -EAGAIN;
+
+ pr_err("Error clearing IMA exception ret=%d\n", ret);
+ }
+
+ return rc;
+}
+
+static int fg_check_iacs_ready(struct fg_chip *chip)
+{
+ int rc = 0, timeout = 250;
+ u8 ima_opr_sts = 0;
+
+ /*
+ * Additional delay to make sure IACS ready bit is set after
+ * Read/Write operation.
+ */
+
+ usleep_range(30, 35);
+ while (1) {
+ rc = fg_read(chip, &ima_opr_sts,
+ chip->mem_base + MEM_INTF_IMA_OPR_STS, 1);
+ if (!rc && (ima_opr_sts & IMA_IACS_RDY))
+ break;
+
+ if (!(--timeout) || rc)
+ break;
+ /* delay for iacs_ready to be asserted */
+ usleep_range(5000, 7000);
+ }
+
+ if (!timeout || rc) {
+ pr_err("IACS_RDY not set\n");
+ /* perform IACS_CLR sequence */
+ fg_check_ima_exception(chip);
+ return -EBUSY;
+ }
+
+ return 0;
+}
+
+#define IACS_SLCT BIT(5)
+static int __fg_interleaved_mem_write(struct fg_chip *chip, u8 *val,
+ u16 address, int offset, int len)
+{
+ int rc = 0, i;
+ u8 *word = val, byte_enable = 0, num_bytes = 0;
+
+ if (fg_debug_mask & FG_MEM_DEBUG_WRITES)
+ pr_info("length %d addr=%02X offset=%d\n",
+ len, address, offset);
+
+ while (len > 0) {
+ num_bytes = (offset + len) > BUF_LEN ?
+ (BUF_LEN - offset) : len;
+ /* write to byte_enable */
+ for (i = offset; i < (offset + num_bytes); i++)
+ byte_enable |= BIT(i);
+
+ rc = fg_write(chip, &byte_enable,
+ chip->mem_base + MEM_INTF_IMA_BYTE_EN, 1);
+ if (rc) {
+ pr_err("Unable to write to byte_en_reg rc=%d\n", rc);
+ return rc;
+ }
+ /* write data */
+ rc = fg_write(chip, word, MEM_INTF_WR_DATA0(chip) + offset,
+ num_bytes);
+ if (rc) {
+ pr_err("spmi write failed: addr=%03x, rc=%d\n",
+ MEM_INTF_WR_DATA0(chip) + offset, rc);
+ return rc;
+ }
+ /*
+ * The last-byte WR_DATA3 starts the write transaction.
+ * Write a dummy value to WR_DATA3 if it does not have
+ * valid data. This dummy data is not written to the
+ * SRAM as byte_en for WR_DATA3 is not set.
+ */
+ if (!(byte_enable & BIT(3))) {
+ u8 dummy_byte = 0x0;
+
+ rc = fg_write(chip, &dummy_byte,
+ MEM_INTF_WR_DATA0(chip) + 3, 1);
+ if (rc) {
+ pr_err("Unable to write dummy-data to WR_DATA3 rc=%d\n",
+ rc);
+ return rc;
+ }
+ }
+
+ rc = fg_check_iacs_ready(chip);
+ if (rc) {
+ pr_debug("IACS_RDY failed rc=%d\n", rc);
+ return rc;
+ }
+
+ /* check for error condition */
+ rc = fg_check_ima_exception(chip);
+ if (rc) {
+ pr_err("IMA transaction failed rc=%d", rc);
+ return rc;
+ }
+
+ word += num_bytes;
+ len -= num_bytes;
+ offset = byte_enable = 0;
+ }
+
+ return rc;
+}
+
+static int __fg_interleaved_mem_read(struct fg_chip *chip, u8 *val, u16 address,
+ int offset, int len)
+{
+ int rc = 0, total_len;
+ u8 *rd_data = val, num_bytes;
+ char str[DEBUG_PRINT_BUFFER_SIZE];
+
+ if (fg_debug_mask & FG_MEM_DEBUG_READS)
+ pr_info("length %d addr=%02X\n", len, address);
+
+ total_len = len;
+ while (len > 0) {
+ num_bytes = (offset + len) > BUF_LEN ? (BUF_LEN - offset) : len;
+ rc = fg_read(chip, rd_data, MEM_INTF_RD_DATA0(chip) + offset,
+ num_bytes);
+ if (rc) {
+ pr_err("spmi read failed: addr=%03x, rc=%d\n",
+ MEM_INTF_RD_DATA0(chip) + offset, rc);
+ return rc;
+ }
+
+ rd_data += num_bytes;
+ len -= num_bytes;
+ offset = 0;
+
+ rc = fg_check_iacs_ready(chip);
+ if (rc) {
+ pr_debug("IACS_RDY failed rc=%d\n", rc);
+ return rc;
+ }
+
+ /* check for error condition */
+ rc = fg_check_ima_exception(chip);
+ if (rc) {
+ pr_err("IMA transaction failed rc=%d", rc);
+ return rc;
+ }
+
+ if (len && (len + offset) < BUF_LEN) {
+ /* move to single mode */
+ u8 intr_ctl = 0;
+
+ rc = fg_write(chip, &intr_ctl, MEM_INTF_CTL(chip), 1);
+ if (rc) {
+ pr_err("failed to move to single mode rc=%d\n",
+ rc);
+ return -EIO;
+ }
+ }
+ }
+
+ if (fg_debug_mask & FG_MEM_DEBUG_READS) {
+ fill_string(str, DEBUG_PRINT_BUFFER_SIZE, val, total_len);
+ pr_info("data: %s\n", str);
+ }
+
+ return rc;
+}
+
+#define IMA_REQ_ACCESS (IACS_SLCT | RIF_MEM_ACCESS_REQ)
+static int fg_interleaved_mem_config(struct fg_chip *chip, u8 *val,
+ u16 address, int len, int offset, int op)
+{
+ int rc = 0;
+ bool rif_mem_sts = true;
+ int time_count = 0;
+
+ while (1) {
+ rc = fg_check_rif_mem_access(chip, &rif_mem_sts);
+ if (rc)
+ return rc;
+
+ if (!rif_mem_sts)
+ break;
+
+ if (fg_debug_mask & (FG_MEM_DEBUG_READS | FG_MEM_DEBUG_WRITES))
+ pr_info("RIF_MEM_ACCESS_REQ is not clear yet for IMA_%s\n",
+ op ? "write" : "read");
+
+ /*
+ * Try this no more than 4 times. If RIF_MEM_ACCESS_REQ is not
+ * clear, then return an error instead of waiting for it again.
+ */
+ if (time_count > 4) {
+ pr_err("Waited for 1.5 seconds polling RIF_MEM_ACCESS_REQ\n");
+ return -ETIMEDOUT;
+ }
+
+ /* Wait for 4ms before reading RIF_MEM_ACCESS_REQ again */
+ usleep_range(4000, 4100);
+ time_count++;
+ }
+
+ /* configure for IMA access */
+ rc = fg_masked_write(chip, MEM_INTF_CFG(chip),
+ IMA_REQ_ACCESS, IMA_REQ_ACCESS, 1);
+ if (rc) {
+ pr_err("failed to set mem access bit rc = %d\n", rc);
+ return rc;
+ }
+
+ /* configure for the read/write single/burst mode */
+ rc = fg_config_access(chip, op, (offset + len) > 4);
+ if (rc) {
+ pr_err("failed to set configure memory access rc = %d\n", rc);
+ return rc;
+ }
+
+ rc = fg_check_iacs_ready(chip);
+ if (rc) {
+ pr_debug("IACS_RDY failed rc=%d\n", rc);
+ return rc;
+ }
+
+ /* write addresses to the register */
+ rc = fg_set_ram_addr(chip, &address);
+ if (rc) {
+ pr_err("failed to set SRAM address rc = %d\n", rc);
+ return rc;
+ }
+
+ rc = fg_check_iacs_ready(chip);
+ if (rc)
+ pr_debug("IACS_RDY failed rc=%d\n", rc);
+
+ return rc;
+}
+
+#define MEM_INTF_FG_BEAT_COUNT 0x57
+#define BEAT_COUNT_MASK 0x0F
+#define RETRY_COUNT 3
+static int fg_interleaved_mem_read(struct fg_chip *chip, u8 *val, u16 address,
+ int len, int offset)
+{
+ int rc = 0, orig_address = address;
+ u8 start_beat_count, end_beat_count, count = 0;
+ bool retry = false;
+
+ if (offset > 3) {
+ pr_err("offset too large %d\n", offset);
+ return -EINVAL;
+ }
+
+ fg_stay_awake(&chip->memif_wakeup_source);
+ address = ((orig_address + offset) / 4) * 4;
+ offset = (orig_address + offset) % 4;
+
+ if (address < RAM_OFFSET) {
+ /*
+ * OTP memory reads need a conventional memory access, do a
+ * conventional read when SRAM offset < RAM_OFFSET.
+ */
+ rc = fg_conventional_mem_read(chip, val, address, len, offset,
+ 0);
+ if (rc)
+ pr_err("Failed to read OTP memory %d\n", rc);
+ goto exit;
+ }
+
+ mutex_lock(&chip->rw_lock);
+
+retry:
+ rc = fg_interleaved_mem_config(chip, val, address, offset, len, 0);
+ if (rc) {
+ pr_err("failed to configure SRAM for IMA rc = %d\n", rc);
+ goto out;
+ }
+
+ /* read the start beat count */
+ rc = fg_read(chip, &start_beat_count,
+ chip->mem_base + MEM_INTF_FG_BEAT_COUNT, 1);
+ if (rc) {
+ pr_err("failed to read beat count rc=%d\n", rc);
+ goto out;
+ }
+
+ /* read data */
+ rc = __fg_interleaved_mem_read(chip, val, address, offset, len);
+ if (rc) {
+ if ((rc == -EAGAIN) && (count < RETRY_COUNT)) {
+ count++;
+ pr_err("IMA access failed retry_count = %d\n", count);
+ goto retry;
+ } else {
+ pr_err("failed to read SRAM address rc = %d\n", rc);
+ goto out;
+ }
+ }
+
+ /* read the end beat count */
+ rc = fg_read(chip, &end_beat_count,
+ chip->mem_base + MEM_INTF_FG_BEAT_COUNT, 1);
+ if (rc) {
+ pr_err("failed to read beat count rc=%d\n", rc);
+ goto out;
+ }
+
+ start_beat_count &= BEAT_COUNT_MASK;
+ end_beat_count &= BEAT_COUNT_MASK;
+ if (fg_debug_mask & FG_MEM_DEBUG_READS)
+ pr_info("Start beat_count = %x End beat_count = %x\n",
+ start_beat_count, end_beat_count);
+ if (start_beat_count != end_beat_count) {
+ if (fg_debug_mask & FG_MEM_DEBUG_READS)
+ pr_info("Beat count do not match - retry transaction\n");
+ retry = true;
+ }
+out:
+ /* Release IMA access */
+ rc = fg_masked_write(chip, MEM_INTF_CFG(chip), IMA_REQ_ACCESS, 0, 1);
+ if (rc)
+ pr_err("failed to reset IMA access bit rc = %d\n", rc);
+
+ if (retry) {
+ retry = false;
+ goto retry;
+ }
+ mutex_unlock(&chip->rw_lock);
+
+exit:
+ fg_relax(&chip->memif_wakeup_source);
+ return rc;
+}
+
+static int fg_interleaved_mem_write(struct fg_chip *chip, u8 *val, u16 address,
+ int len, int offset)
+{
+ int rc = 0, orig_address = address;
+ u8 count = 0;
+
+ if (address < RAM_OFFSET)
+ return -EINVAL;
+
+ if (offset > 3) {
+ pr_err("offset too large %d\n", offset);
+ return -EINVAL;
+ }
+
+ fg_stay_awake(&chip->memif_wakeup_source);
+ address = ((orig_address + offset) / 4) * 4;
+ offset = (orig_address + offset) % 4;
+
+ mutex_lock(&chip->rw_lock);
+
+retry:
+ rc = fg_interleaved_mem_config(chip, val, address, offset, len, 1);
+ if (rc) {
+ pr_err("failed to xonfigure SRAM for IMA rc = %d\n", rc);
+ goto out;
+ }
+
+ /* write data */
+ rc = __fg_interleaved_mem_write(chip, val, address, offset, len);
+ if (rc) {
+ if ((rc == -EAGAIN) && (count < RETRY_COUNT)) {
+ count++;
+ pr_err("IMA access failed retry_count = %d\n", count);
+ goto retry;
+ } else {
+ pr_err("failed to write SRAM address rc = %d\n", rc);
+ goto out;
+ }
+ }
+
+out:
+ /* Release IMA access */
+ rc = fg_masked_write(chip, MEM_INTF_CFG(chip), IMA_REQ_ACCESS, 0, 1);
+ if (rc)
+ pr_err("failed to reset IMA access bit rc = %d\n", rc);
+
+ mutex_unlock(&chip->rw_lock);
+ fg_relax(&chip->memif_wakeup_source);
+ return rc;
+}
+
+static int fg_mem_read(struct fg_chip *chip, u8 *val, u16 address,
+ int len, int offset, bool keep_access)
+{
+ if (chip->ima_supported)
+ return fg_interleaved_mem_read(chip, val, address,
+ len, offset);
+ else
+ return fg_conventional_mem_read(chip, val, address,
+ len, offset, keep_access);
+}
+
+static int fg_mem_write(struct fg_chip *chip, u8 *val, u16 address,
+ int len, int offset, bool keep_access)
+{
+ if (chip->ima_supported)
+ return fg_interleaved_mem_write(chip, val, address,
+ len, offset);
+ else
+ return fg_conventional_mem_write(chip, val, address,
+ len, offset, keep_access);
+}
+
+static int fg_mem_masked_write(struct fg_chip *chip, u16 addr,
+ u8 mask, u8 val, u8 offset)
+{
+ int rc = 0;
+ u8 reg[4];
+ char str[DEBUG_PRINT_BUFFER_SIZE];
+
+ rc = fg_mem_read(chip, reg, addr, 4, 0, 1);
+ if (rc) {
+ pr_err("spmi read failed: addr=%03X, rc=%d\n", addr, rc);
+ return rc;
+ }
+
+ reg[offset] &= ~mask;
+ reg[offset] |= val & mask;
+
+ str[0] = '\0';
+ fill_string(str, DEBUG_PRINT_BUFFER_SIZE, reg, 4);
+ pr_debug("Writing %s address %03x, offset %d\n", str, addr, offset);
+
+ rc = fg_mem_write(chip, reg, addr, 4, 0, 0);
+ if (rc) {
+ pr_err("spmi write failed: addr=%03X, rc=%d\n", addr, rc);
+ return rc;
+ }
+
+ return rc;
+}
+
+static int soc_to_setpoint(int soc)
+{
+ return DIV_ROUND_CLOSEST(soc * 255, 100);
+}
+
+static void batt_to_setpoint_adc(int vbatt_mv, u8 *data)
+{
+ int val;
+ /* Battery voltage is an offset from 0 V and LSB is 1/2^15. */
+ val = DIV_ROUND_CLOSEST(vbatt_mv * 32768, 5000);
+ data[0] = val & 0xFF;
+ data[1] = val >> 8;
+}
+
+static u8 batt_to_setpoint_8b(int vbatt_mv)
+{
+ int val;
+ /* Battery voltage is an offset from 2.5 V and LSB is 5/2^9. */
+ val = (vbatt_mv - 2500) * 512 / 1000;
+ return DIV_ROUND_CLOSEST(val, 5);
+}
+
+static u8 therm_delay_to_setpoint(u32 delay_us)
+{
+ u8 val;
+
+ if (delay_us < 2560)
+ val = 0;
+ else if (delay_us > 163840)
+ val = 7;
+ else
+ val = ilog2(delay_us / 10) - 7;
+ return val << 5;
+}
+
+static int get_current_time(unsigned long *now_tm_sec)
+{
+ struct rtc_time tm;
+ struct rtc_device *rtc;
+ int rc;
+
+ rtc = rtc_class_open(CONFIG_RTC_HCTOSYS_DEVICE);
+ if (rtc == NULL) {
+ pr_err("%s: unable to open rtc device (%s)\n",
+ __FILE__, CONFIG_RTC_HCTOSYS_DEVICE);
+ return -EINVAL;
+ }
+
+ rc = rtc_read_time(rtc, &tm);
+ if (rc) {
+ pr_err("Error reading rtc device (%s) : %d\n",
+ CONFIG_RTC_HCTOSYS_DEVICE, rc);
+ goto close_time;
+ }
+
+ rc = rtc_valid_tm(&tm);
+ if (rc) {
+ pr_err("Invalid RTC time (%s): %d\n",
+ CONFIG_RTC_HCTOSYS_DEVICE, rc);
+ goto close_time;
+ }
+ rtc_tm_to_time(&tm, now_tm_sec);
+
+close_time:
+ rtc_class_close(rtc);
+ return rc;
+}
+
+#define BATTERY_SOC_REG 0x56C
+#define BATTERY_SOC_OFFSET 1
+#define FULL_PERCENT_3B 0xFFFFFF
+static int get_battery_soc_raw(struct fg_chip *chip)
+{
+ int rc;
+ u8 buffer[3];
+
+ rc = fg_mem_read(chip, buffer, BATTERY_SOC_REG, 3, 1, 0);
+ if (rc) {
+ pr_err("Unable to read battery soc: %d\n", rc);
+ return 0;
+ }
+ return (int)(buffer[2] << 16 | buffer[1] << 8 | buffer[0]);
+}
+
+#define COUNTER_IMPTR_REG 0X558
+#define COUNTER_PULSE_REG 0X55C
+#define SOC_FULL_REG 0x564
+#define COUNTER_IMPTR_OFFSET 2
+#define COUNTER_PULSE_OFFSET 0
+#define SOC_FULL_OFFSET 3
+#define ESR_PULSE_RECONFIG_SOC 0xFFF971
+static int fg_configure_soc(struct fg_chip *chip)
+{
+ u32 batt_soc;
+ u8 cntr[2] = {0, 0};
+ int rc = 0;
+
+ mutex_lock(&chip->rw_lock);
+ atomic_add_return(1, &chip->memif_user_cnt);
+ mutex_unlock(&chip->rw_lock);
+
+ /* Read Battery SOC */
+ batt_soc = get_battery_soc_raw(chip);
+
+ if (batt_soc > ESR_PULSE_RECONFIG_SOC) {
+ if (fg_debug_mask & FG_POWER_SUPPLY)
+ pr_info("Configuring soc registers batt_soc: %x\n",
+ batt_soc);
+ batt_soc = ESR_PULSE_RECONFIG_SOC;
+ rc = fg_mem_write(chip, (u8 *)&batt_soc, BATTERY_SOC_REG, 3,
+ BATTERY_SOC_OFFSET, 1);
+ if (rc) {
+ pr_err("failed to write BATT_SOC rc=%d\n", rc);
+ goto out;
+ }
+
+ rc = fg_mem_write(chip, (u8 *)&batt_soc, SOC_FULL_REG, 3,
+ SOC_FULL_OFFSET, 1);
+ if (rc) {
+ pr_err("failed to write SOC_FULL rc=%d\n", rc);
+ goto out;
+ }
+
+ rc = fg_mem_write(chip, cntr, COUNTER_IMPTR_REG, 2,
+ COUNTER_IMPTR_OFFSET, 1);
+ if (rc) {
+ pr_err("failed to write COUNTER_IMPTR rc=%d\n", rc);
+ goto out;
+ }
+
+ rc = fg_mem_write(chip, cntr, COUNTER_PULSE_REG, 2,
+ COUNTER_PULSE_OFFSET, 0);
+ if (rc)
+ pr_err("failed to write COUNTER_IMPTR rc=%d\n", rc);
+ }
+out:
+ fg_release_access_if_necessary(chip);
+ return rc;
+}
+
+#define SOC_EMPTY BIT(3)
+static bool fg_is_batt_empty(struct fg_chip *chip)
+{
+ u8 fg_soc_sts;
+ int rc;
+
+ rc = fg_read(chip, &fg_soc_sts,
+ INT_RT_STS(chip->soc_base), 1);
+ if (rc) {
+ pr_err("spmi read failed: addr=%03X, rc=%d\n",
+ INT_RT_STS(chip->soc_base), rc);
+ return false;
+ }
+
+ return (fg_soc_sts & SOC_EMPTY) != 0;
+}
+
+static int get_monotonic_soc_raw(struct fg_chip *chip)
+{
+ u8 cap[2];
+ int rc, tries = 0;
+
+ while (tries < MAX_TRIES_SOC) {
+ rc = fg_read(chip, cap,
+ chip->soc_base + SOC_MONOTONIC_SOC, 2);
+ if (rc) {
+ pr_err("spmi read failed: addr=%03x, rc=%d\n",
+ chip->soc_base + SOC_MONOTONIC_SOC, rc);
+ return rc;
+ }
+
+ if (cap[0] == cap[1])
+ break;
+
+ tries++;
+ }
+
+ if (tries == MAX_TRIES_SOC) {
+ pr_err("shadow registers do not match\n");
+ return -EINVAL;
+ }
+
+ if (fg_debug_mask & FG_POWER_SUPPLY)
+ pr_info_ratelimited("raw: 0x%02x\n", cap[0]);
+ return cap[0];
+}
+
+#define EMPTY_CAPACITY 0
+#define DEFAULT_CAPACITY 50
+#define MISSING_CAPACITY 100
+#define FULL_CAPACITY 100
+#define FULL_SOC_RAW 0xFF
+static int get_prop_capacity(struct fg_chip *chip)
+{
+ int msoc;
+
+ if (chip->battery_missing)
+ return MISSING_CAPACITY;
+ if (!chip->profile_loaded && !chip->use_otp_profile)
+ return DEFAULT_CAPACITY;
+ if (chip->charge_full)
+ return FULL_CAPACITY;
+ if (chip->soc_empty) {
+ if (fg_debug_mask & FG_POWER_SUPPLY)
+ pr_info_ratelimited("capacity: %d, EMPTY\n",
+ EMPTY_CAPACITY);
+ return EMPTY_CAPACITY;
+ }
+ msoc = get_monotonic_soc_raw(chip);
+ if (msoc == 0)
+ return EMPTY_CAPACITY;
+ else if (msoc == FULL_SOC_RAW)
+ return FULL_CAPACITY;
+ return DIV_ROUND_CLOSEST((msoc - 1) * (FULL_CAPACITY - 2),
+ FULL_SOC_RAW - 2) + 1;
+}
+
+#define HIGH_BIAS 3
+#define MED_BIAS BIT(1)
+#define LOW_BIAS BIT(0)
+static u8 bias_ua[] = {
+ [HIGH_BIAS] = 150,
+ [MED_BIAS] = 15,
+ [LOW_BIAS] = 5,
+};
+
+static int64_t get_batt_id(unsigned int battery_id_uv, u8 bid_info)
+{
+ u64 battery_id_ohm;
+
+ if ((bid_info & 0x3) == 0) {
+ pr_err("can't determine battery id 0x%02x\n", bid_info);
+ return -EINVAL;
+ }
+
+ battery_id_ohm = div_u64(battery_id_uv, bias_ua[bid_info & 0x3]);
+
+ return battery_id_ohm;
+}
+
+#define DEFAULT_TEMP_DEGC 250
+static int get_sram_prop_now(struct fg_chip *chip, unsigned int type)
+{
+ if (fg_debug_mask & FG_POWER_SUPPLY)
+ pr_info("addr 0x%02X, offset %d value %d\n",
+ fg_data[type].address, fg_data[type].offset,
+ fg_data[type].value);
+
+ if (type == FG_DATA_BATT_ID)
+ return get_batt_id(fg_data[type].value,
+ fg_data[FG_DATA_BATT_ID_INFO].value);
+
+ return fg_data[type].value;
+}
+
+#define MIN_TEMP_DEGC -300
+#define MAX_TEMP_DEGC 970
+static int get_prop_jeita_temp(struct fg_chip *chip, unsigned int type)
+{
+ if (fg_debug_mask & FG_POWER_SUPPLY)
+ pr_info("addr 0x%02X, offset %d\n", settings[type].address,
+ settings[type].offset);
+
+ return settings[type].value;
+}
+
+static int set_prop_jeita_temp(struct fg_chip *chip,
+ unsigned int type, int decidegc)
+{
+ int rc = 0;
+
+ if (fg_debug_mask & FG_POWER_SUPPLY)
+ pr_info("addr 0x%02X, offset %d temp%d\n",
+ settings[type].address,
+ settings[type].offset, decidegc);
+
+ settings[type].value = decidegc;
+
+ cancel_delayed_work_sync(
+ &chip->update_jeita_setting);
+ schedule_delayed_work(
+ &chip->update_jeita_setting, 0);
+
+ return rc;
+}
+
+#define EXTERNAL_SENSE_SELECT 0x4AC
+#define EXTERNAL_SENSE_OFFSET 0x2
+#define EXTERNAL_SENSE_BIT BIT(2)
+static int set_prop_sense_type(struct fg_chip *chip, int ext_sense_type)
+{
+ int rc;
+
+ rc = fg_mem_masked_write(chip, EXTERNAL_SENSE_SELECT,
+ EXTERNAL_SENSE_BIT,
+ ext_sense_type ? EXTERNAL_SENSE_BIT : 0,
+ EXTERNAL_SENSE_OFFSET);
+ if (rc) {
+ pr_err("failed to write profile rc=%d\n", rc);
+ return rc;
+ }
+
+ return 0;
+}
+
+#define EXPONENT_MASK 0xF800
+#define MANTISSA_MASK 0x3FF
+#define SIGN BIT(10)
+#define EXPONENT_SHIFT 11
+#define MICRO_UNIT 1000000ULL
+static int64_t float_decode(u16 reg)
+{
+ int64_t final_val, exponent_val, mantissa_val;
+ int exponent, mantissa, n;
+ bool sign;
+
+ exponent = (reg & EXPONENT_MASK) >> EXPONENT_SHIFT;
+ mantissa = (reg & MANTISSA_MASK);
+ sign = !!(reg & SIGN);
+
+ pr_debug("exponent=%d mantissa=%d sign=%d\n", exponent, mantissa, sign);
+
+ mantissa_val = mantissa * MICRO_UNIT;
+
+ n = exponent - 15;
+ if (n < 0)
+ exponent_val = MICRO_UNIT >> -n;
+ else
+ exponent_val = MICRO_UNIT << n;
+
+ n = n - 10;
+ if (n < 0)
+ mantissa_val >>= -n;
+ else
+ mantissa_val <<= n;
+
+ final_val = exponent_val + mantissa_val;
+
+ if (sign)
+ final_val *= -1;
+
+ return final_val;
+}
+
+#define MIN_HALFFLOAT_EXP_N -15
+#define MAX_HALFFLOAT_EXP_N 16
+static int log2_floor(int64_t uval)
+{
+ int n = 0;
+ int64_t i = MICRO_UNIT;
+
+ if (uval > i) {
+ while (uval > i && n > MIN_HALFFLOAT_EXP_N) {
+ i <<= 1;
+ n += 1;
+ }
+ if (uval < i)
+ n -= 1;
+ } else if (uval < i) {
+ while (uval < i && n < MAX_HALFFLOAT_EXP_N) {
+ i >>= 1;
+ n -= 1;
+ }
+ }
+
+ return n;
+}
+
+static int64_t exp2_int(int64_t n)
+{
+ int p = n - 1;
+
+ if (p > 0)
+ return (2 * MICRO_UNIT) << p;
+ else
+ return (2 * MICRO_UNIT) >> abs(p);
+}
+
+static u16 float_encode(int64_t uval)
+{
+ int sign = 0, n, exp, mantissa;
+ u16 half = 0;
+
+ if (uval < 0) {
+ sign = 1;
+ uval = abs(uval);
+ }
+ n = log2_floor(uval);
+ exp = n + 15;
+ mantissa = div_s64(div_s64((uval - exp2_int(n)) * exp2_int(10 - n),
+ MICRO_UNIT) + MICRO_UNIT / 2, MICRO_UNIT);
+
+ half = (mantissa & MANTISSA_MASK) | ((sign << 10) & SIGN)
+ | ((exp << 11) & EXPONENT_MASK);
+
+ if (fg_debug_mask & FG_STATUS)
+ pr_info("uval = %lld, m = 0x%02x, sign = 0x%02x, exp = 0x%02x, half = 0x%04x\n",
+ uval, mantissa, sign, exp, half);
+ return half;
+}
+
+#define BATT_IDED BIT(3)
+static int fg_is_batt_id_valid(struct fg_chip *chip)
+{
+ u8 fg_batt_sts;
+ int rc;
+
+ rc = fg_read(chip, &fg_batt_sts,
+ INT_RT_STS(chip->batt_base), 1);
+ if (rc) {
+ pr_err("spmi read failed: addr=%03X, rc=%d\n",
+ INT_RT_STS(chip->batt_base), rc);
+ return rc;
+ }
+
+ if (fg_debug_mask & FG_IRQS)
+ pr_info("fg batt sts 0x%x\n", fg_batt_sts);
+
+ return (fg_batt_sts & BATT_IDED) ? 1 : 0;
+}
+
+static int64_t twos_compliment_extend(int64_t val, int nbytes)
+{
+ int i;
+ int64_t mask;
+
+ mask = 0x80LL << ((nbytes - 1) * 8);
+ if (val & mask) {
+ for (i = 8; i > nbytes; i--) {
+ mask = 0xFFLL << ((i - 1) * 8);
+ val |= mask;
+ }
+ }
+
+ return val;
+}
+
+#define LSB_24B_NUMRTR 596046
+#define LSB_24B_DENMTR 1000000
+#define LSB_16B_NUMRTR 152587
+#define LSB_16B_DENMTR 1000
+#define LSB_8B 9800
+#define TEMP_LSB_16B 625
+#define DECIKELVIN 2730
+#define SRAM_PERIOD_NO_ID_UPDATE_MS 100
+#define FULL_PERCENT_28BIT 0xFFFFFFF
+static void update_sram_data(struct fg_chip *chip, int *resched_ms)
+{
+ int i, j, rc = 0;
+ u8 reg[4];
+ int64_t temp;
+ int battid_valid = fg_is_batt_id_valid(chip);
+
+ fg_stay_awake(&chip->update_sram_wakeup_source);
+ if (chip->fg_restarting)
+ goto resched;
+
+ fg_mem_lock(chip);
+ for (i = 1; i < FG_DATA_MAX; i++) {
+ if (chip->profile_loaded && i >= FG_DATA_BATT_ID)
+ continue;
+ rc = fg_mem_read(chip, reg, fg_data[i].address,
+ fg_data[i].len, fg_data[i].offset, 0);
+ if (rc) {
+ pr_err("Failed to update sram data\n");
+ break;
+ }
+
+ temp = 0;
+ for (j = 0; j < fg_data[i].len; j++)
+ temp |= reg[j] << (8 * j);
+
+ switch (i) {
+ case FG_DATA_OCV:
+ case FG_DATA_VOLTAGE:
+ case FG_DATA_CPRED_VOLTAGE:
+ fg_data[i].value = div_u64(
+ (u64)(u16)temp * LSB_16B_NUMRTR,
+ LSB_16B_DENMTR);
+ break;
+ case FG_DATA_CURRENT:
+ temp = twos_compliment_extend(temp, fg_data[i].len);
+ fg_data[i].value = div_s64(
+ (s64)temp * LSB_16B_NUMRTR,
+ LSB_16B_DENMTR);
+ break;
+ case FG_DATA_BATT_ESR:
+ fg_data[i].value = float_decode((u16) temp);
+ break;
+ case FG_DATA_BATT_ESR_COUNT:
+ fg_data[i].value = (u16)temp;
+ break;
+ case FG_DATA_BATT_ID:
+ if (battid_valid)
+ fg_data[i].value = reg[0] * LSB_8B;
+ break;
+ case FG_DATA_BATT_ID_INFO:
+ if (battid_valid)
+ fg_data[i].value = reg[0];
+ break;
+ case FG_DATA_BATT_SOC:
+ fg_data[i].value = div64_s64((temp * 10000),
+ FULL_PERCENT_3B);
+ break;
+ case FG_DATA_CC_CHARGE:
+ temp = twos_compliment_extend(temp, fg_data[i].len);
+ fg_data[i].value = div64_s64(
+ temp * (int64_t)chip->nom_cap_uah,
+ FULL_PERCENT_28BIT);
+ break;
+ case FG_DATA_VINT_ERR:
+ temp = twos_compliment_extend(temp, fg_data[i].len);
+ fg_data[i].value = div64_s64(temp * chip->nom_cap_uah,
+ FULL_PERCENT_3B);
+ break;
+ };
+
+ if (fg_debug_mask & FG_MEM_DEBUG_READS)
+ pr_info("%d %lld %d\n", i, temp, fg_data[i].value);
+ }
+ fg_mem_release(chip);
+
+ if (!rc)
+ get_current_time(&chip->last_sram_update_time);
+
+resched:
+ if (battid_valid) {
+ complete_all(&chip->batt_id_avail);
+ *resched_ms = fg_sram_update_period_ms;
+ } else {
+ *resched_ms = SRAM_PERIOD_NO_ID_UPDATE_MS;
+ }
+ fg_relax(&chip->update_sram_wakeup_source);
+}
+
+#define SRAM_TIMEOUT_MS 3000
+static void update_sram_data_work(struct work_struct *work)
+{
+ struct fg_chip *chip = container_of(work,
+ struct fg_chip,
+ update_sram_data.work);
+ int resched_ms = SRAM_PERIOD_NO_ID_UPDATE_MS, ret;
+ bool tried_again = false;
+
+wait:
+ /* Wait for MEMIF access revoked */
+ ret = wait_for_completion_interruptible_timeout(
+ &chip->sram_access_revoked,
+ msecs_to_jiffies(SRAM_TIMEOUT_MS));
+
+ /* If we were interrupted wait again one more time. */
+ if (ret == -ERESTARTSYS && !tried_again) {
+ tried_again = true;
+ goto wait;
+ } else if (ret <= 0) {
+ pr_err("transaction timed out ret=%d\n", ret);
+ goto out;
+ }
+ update_sram_data(chip, &resched_ms);
+
+out:
+ schedule_delayed_work(
+ &chip->update_sram_data,
+ msecs_to_jiffies(resched_ms));
+}
+
+#define BATT_TEMP_OFFSET 3
+#define BATT_TEMP_CNTRL_MASK 0x17
+#define DISABLE_THERM_BIT BIT(0)
+#define TEMP_SENSE_ALWAYS_BIT BIT(1)
+#define TEMP_SENSE_CHARGE_BIT BIT(2)
+#define FORCE_RBIAS_ON_BIT BIT(4)
+#define BATT_TEMP_OFF DISABLE_THERM_BIT
+#define BATT_TEMP_ON (FORCE_RBIAS_ON_BIT | TEMP_SENSE_ALWAYS_BIT | \
+ TEMP_SENSE_CHARGE_BIT)
+#define TEMP_PERIOD_UPDATE_MS 10000
+#define TEMP_PERIOD_TIMEOUT_MS 3000
+static void update_temp_data(struct work_struct *work)
+{
+ s16 temp;
+ u8 reg[2];
+ bool tried_again = false;
+ int rc, ret, timeout = TEMP_PERIOD_TIMEOUT_MS;
+ struct fg_chip *chip = container_of(work,
+ struct fg_chip,
+ update_temp_work.work);
+
+ if (chip->fg_restarting)
+ goto resched;
+
+ fg_stay_awake(&chip->update_temp_wakeup_source);
+ if (chip->sw_rbias_ctrl) {
+ rc = fg_mem_masked_write(chip, EXTERNAL_SENSE_SELECT,
+ BATT_TEMP_CNTRL_MASK,
+ BATT_TEMP_ON,
+ BATT_TEMP_OFFSET);
+ if (rc) {
+ pr_err("failed to write BATT_TEMP_ON rc=%d\n", rc);
+ goto out;
+ }
+
+wait:
+ /* Wait for MEMIF access revoked */
+ ret = wait_for_completion_interruptible_timeout(
+ &chip->sram_access_revoked,
+ msecs_to_jiffies(timeout));
+
+ /* If we were interrupted wait again one more time. */
+ if (ret == -ERESTARTSYS && !tried_again) {
+ tried_again = true;
+ goto wait;
+ } else if (ret <= 0) {
+ rc = -ETIMEDOUT;
+ pr_err("transaction timed out ret=%d\n", ret);
+ goto out;
+ }
+ }
+
+ /* Read FG_DATA_BATT_TEMP now */
+ rc = fg_mem_read(chip, reg, fg_data[0].address,
+ fg_data[0].len, fg_data[0].offset,
+ chip->sw_rbias_ctrl ? 1 : 0);
+ if (rc) {
+ pr_err("Failed to update temp data\n");
+ goto out;
+ }
+
+ temp = reg[0] | (reg[1] << 8);
+ fg_data[0].value = (temp * TEMP_LSB_16B / 1000)
+ - DECIKELVIN;
+
+ if (fg_debug_mask & FG_MEM_DEBUG_READS)
+ pr_info("BATT_TEMP %d %d\n", temp, fg_data[0].value);
+
+ get_current_time(&chip->last_temp_update_time);
+
+out:
+ if (chip->sw_rbias_ctrl) {
+ rc = fg_mem_masked_write(chip, EXTERNAL_SENSE_SELECT,
+ BATT_TEMP_CNTRL_MASK,
+ BATT_TEMP_OFF,
+ BATT_TEMP_OFFSET);
+ if (rc)
+ pr_err("failed to write BATT_TEMP_OFF rc=%d\n", rc);
+ }
+ fg_relax(&chip->update_temp_wakeup_source);
+
+resched:
+ schedule_delayed_work(
+ &chip->update_temp_work,
+ msecs_to_jiffies(TEMP_PERIOD_UPDATE_MS));
+}
+
+static void update_jeita_setting(struct work_struct *work)
+{
+ struct fg_chip *chip = container_of(work,
+ struct fg_chip,
+ update_jeita_setting.work);
+ u8 reg[4];
+ int i, rc;
+
+ for (i = 0; i < 4; i++)
+ reg[i] = (settings[FG_MEM_SOFT_COLD + i].value / 10) + 30;
+
+ rc = fg_mem_write(chip, reg, settings[FG_MEM_SOFT_COLD].address,
+ 4, settings[FG_MEM_SOFT_COLD].offset, 0);
+ if (rc)
+ pr_err("failed to update JEITA setting rc=%d\n", rc);
+}
+
+static int fg_set_resume_soc(struct fg_chip *chip, u8 threshold)
+{
+ u16 address;
+ int offset, rc;
+
+ address = settings[FG_MEM_RESUME_SOC].address;
+ offset = settings[FG_MEM_RESUME_SOC].offset;
+
+ rc = fg_mem_masked_write(chip, address, 0xFF, threshold, offset);
+
+ if (rc)
+ pr_err("write failed rc=%d\n", rc);
+ else
+ pr_debug("setting resume-soc to %x\n", threshold);
+
+ return rc;
+}
+
+#define VBATT_LOW_STS_BIT BIT(2)
+static int fg_get_vbatt_status(struct fg_chip *chip, bool *vbatt_low_sts)
+{
+ int rc = 0;
+ u8 fg_batt_sts;
+
+ rc = fg_read(chip, &fg_batt_sts, INT_RT_STS(chip->batt_base), 1);
+ if (!rc)
+ *vbatt_low_sts = !!(fg_batt_sts & VBATT_LOW_STS_BIT);
+ return rc;
+}
+
+#define BATT_CYCLE_NUMBER_REG 0x5E8
+#define BATT_CYCLE_OFFSET 0
+static void restore_cycle_counter(struct fg_chip *chip)
+{
+ int rc = 0, i, address;
+ u8 data[2];
+
+ fg_mem_lock(chip);
+ for (i = 0; i < BUCKET_COUNT; i++) {
+ address = BATT_CYCLE_NUMBER_REG + i * 2;
+ rc = fg_mem_read(chip, (u8 *)&data, address, 2,
+ BATT_CYCLE_OFFSET, 0);
+ if (rc)
+ pr_err("Failed to read BATT_CYCLE_NUMBER[%d] rc: %d\n",
+ i, rc);
+ else
+ chip->cyc_ctr.count[i] = data[0] | data[1] << 8;
+ }
+ fg_mem_release(chip);
+}
+
+static void clear_cycle_counter(struct fg_chip *chip)
+{
+ int rc = 0, len, i;
+
+ if (!chip->cyc_ctr.en)
+ return;
+
+ len = sizeof(chip->cyc_ctr.count);
+ memset(chip->cyc_ctr.count, 0, len);
+ for (i = 0; i < BUCKET_COUNT; i++) {
+ chip->cyc_ctr.started[i] = false;
+ chip->cyc_ctr.last_soc[i] = 0;
+ }
+ rc = fg_mem_write(chip, (u8 *)&chip->cyc_ctr.count,
+ BATT_CYCLE_NUMBER_REG, len,
+ BATT_CYCLE_OFFSET, 0);
+ if (rc)
+ pr_err("failed to write BATT_CYCLE_NUMBER rc=%d\n", rc);
+}
+
+static int fg_inc_store_cycle_ctr(struct fg_chip *chip, int bucket)
+{
+ int rc = 0, address;
+ u16 cyc_count;
+ u8 data[2];
+
+ if (bucket < 0 || (bucket > BUCKET_COUNT - 1))
+ return 0;
+
+ cyc_count = chip->cyc_ctr.count[bucket];
+ cyc_count++;
+ data[0] = cyc_count & 0xFF;
+ data[1] = cyc_count >> 8;
+
+ address = BATT_CYCLE_NUMBER_REG + bucket * 2;
+
+ rc = fg_mem_write(chip, data, address, 2, BATT_CYCLE_OFFSET, 0);
+ if (rc)
+ pr_err("failed to write BATT_CYCLE_NUMBER[%d] rc=%d\n",
+ bucket, rc);
+ else
+ chip->cyc_ctr.count[bucket] = cyc_count;
+ return rc;
+}
+
+static void update_cycle_count(struct work_struct *work)
+{
+ int rc = 0, bucket, i;
+ u8 reg[3], batt_soc;
+ struct fg_chip *chip = container_of(work,
+ struct fg_chip,
+ cycle_count_work);
+
+ mutex_lock(&chip->cyc_ctr.lock);
+ rc = fg_mem_read(chip, reg, BATTERY_SOC_REG, 3,
+ BATTERY_SOC_OFFSET, 0);
+ if (rc) {
+ pr_err("Failed to read battery soc rc: %d\n", rc);
+ goto out;
+ }
+ batt_soc = reg[2];
+
+ if (chip->status == POWER_SUPPLY_STATUS_CHARGING) {
+ /* Find out which bucket the SOC falls in */
+ bucket = batt_soc / BUCKET_SOC_PCT;
+
+ if (fg_debug_mask & FG_STATUS)
+ pr_info("batt_soc: %x bucket: %d\n", reg[2], bucket);
+
+ /*
+ * If we've started counting for the previous bucket,
+ * then store the counter for that bucket if the
+ * counter for current bucket is getting started.
+ */
+ if (bucket > 0 && chip->cyc_ctr.started[bucket - 1] &&
+ !chip->cyc_ctr.started[bucket]) {
+ rc = fg_inc_store_cycle_ctr(chip, bucket - 1);
+ if (rc) {
+ pr_err("Error in storing cycle_ctr rc: %d\n",
+ rc);
+ goto out;
+ } else {
+ chip->cyc_ctr.started[bucket - 1] = false;
+ chip->cyc_ctr.last_soc[bucket - 1] = 0;
+ }
+ }
+ if (!chip->cyc_ctr.started[bucket]) {
+ chip->cyc_ctr.started[bucket] = true;
+ chip->cyc_ctr.last_soc[bucket] = batt_soc;
+ }
+ } else {
+ for (i = 0; i < BUCKET_COUNT; i++) {
+ if (chip->cyc_ctr.started[i] &&
+ batt_soc > chip->cyc_ctr.last_soc[i]) {
+ rc = fg_inc_store_cycle_ctr(chip, i);
+ if (rc)
+ pr_err("Error in storing cycle_ctr rc: %d\n",
+ rc);
+ chip->cyc_ctr.last_soc[i] = 0;
+ }
+ chip->cyc_ctr.started[i] = false;
+ }
+ }
+out:
+ mutex_unlock(&chip->cyc_ctr.lock);
+}
+
+static int fg_get_cycle_count(struct fg_chip *chip)
+{
+ int count;
+
+ if (!chip->cyc_ctr.en)
+ return 0;
+
+ if ((chip->cyc_ctr.id <= 0) || (chip->cyc_ctr.id > BUCKET_COUNT))
+ return -EINVAL;
+
+ mutex_lock(&chip->cyc_ctr.lock);
+ count = chip->cyc_ctr.count[chip->cyc_ctr.id - 1];
+ mutex_unlock(&chip->cyc_ctr.lock);
+ return count;
+}
+
+static void half_float_to_buffer(int64_t uval, u8 *buffer)
+{
+ u16 raw;
+
+ raw = float_encode(uval);
+ buffer[0] = (u8)(raw & 0xFF);
+ buffer[1] = (u8)((raw >> 8) & 0xFF);
+}
+
+static int64_t half_float(u8 *buffer)
+{
+ u16 val;
+
+ val = buffer[1] << 8 | buffer[0];
+ return float_decode(val);
+}
+
+static int voltage_2b(u8 *buffer)
+{
+ u16 val;
+
+ val = buffer[1] << 8 | buffer[0];
+ /* the range of voltage 2b is [-5V, 5V], so it will fit in an int */
+ return (int)div_u64(((u64)val) * LSB_16B_NUMRTR, LSB_16B_DENMTR);
+}
+
+static int bcap_uah_2b(u8 *buffer)
+{
+ u16 val;
+
+ val = buffer[1] << 8 | buffer[0];
+ return ((int)val) * 1000;
+}
+
+static int lookup_ocv_for_soc(struct fg_chip *chip, int soc)
+{
+ int64_t *coeffs;
+
+ if (soc > chip->ocv_junction_p1p2 * 10)
+ coeffs = chip->ocv_coeffs;
+ else if (soc > chip->ocv_junction_p2p3 * 10)
+ coeffs = chip->ocv_coeffs + 4;
+ else
+ coeffs = chip->ocv_coeffs + 8;
+ /* the range of ocv will fit in a 32 bit int */
+ return (int)(coeffs[0]
+ + div_s64(coeffs[1] * soc, 1000LL)
+ + div_s64(coeffs[2] * soc * soc, 1000000LL)
+ + div_s64(coeffs[3] * soc * soc * soc, 1000000000LL));
+}
+
+static int lookup_soc_for_ocv(struct fg_chip *chip, int ocv)
+{
+ int64_t val;
+ int soc = -EINVAL;
+ /*
+ * binary search variables representing the valid start and end
+ * percentages to search
+ */
+ int start = 0, end = 1000, mid;
+
+ if (fg_debug_mask & FG_AGING)
+ pr_info("target_ocv = %d\n", ocv);
+ /* do a binary search for the closest soc to match the ocv */
+ while (end - start > 1) {
+ mid = (start + end) / 2;
+ val = lookup_ocv_for_soc(chip, mid);
+ if (fg_debug_mask & FG_AGING)
+ pr_info("start = %d, mid = %d, end = %d, ocv = %lld\n",
+ start, mid, end, val);
+ if (ocv < val) {
+ end = mid;
+ } else if (ocv > val) {
+ start = mid;
+ } else {
+ soc = mid;
+ break;
+ }
+ }
+ /*
+ * if the exact soc was not found and there are two or less values
+ * remaining, just compare them and see which one is closest to the ocv
+ */
+ if (soc == -EINVAL) {
+ if (abs(ocv - lookup_ocv_for_soc(chip, start))
+ > abs(ocv - lookup_ocv_for_soc(chip, end)))
+ soc = end;
+ else
+ soc = start;
+ }
+ if (fg_debug_mask & FG_AGING)
+ pr_info("closest = %d, target_ocv = %d, ocv_found = %d\n",
+ soc, ocv, lookup_ocv_for_soc(chip, soc));
+ return soc;
+}
+
+#define ESR_ACTUAL_REG 0x554
+#define BATTERY_ESR_REG 0x4F4
+#define TEMP_RS_TO_RSLOW_REG 0x514
+static int estimate_battery_age(struct fg_chip *chip, int *actual_capacity)
+{
+ int64_t ocv_cutoff_new, ocv_cutoff_aged, temp_rs_to_rslow;
+ int64_t esr_actual, battery_esr, val;
+ int soc_cutoff_aged, soc_cutoff_new, rc;
+ int battery_soc, unusable_soc, batt_temp;
+ u8 buffer[3];
+
+ if (chip->batt_aging_mode != FG_AGING_ESR)
+ return 0;
+
+ if (chip->nom_cap_uah == 0) {
+ if (fg_debug_mask & FG_AGING)
+ pr_info("ocv coefficients not loaded, aborting\n");
+ return 0;
+ }
+ fg_mem_lock(chip);
+
+ batt_temp = get_sram_prop_now(chip, FG_DATA_BATT_TEMP);
+ if (batt_temp < 150 || batt_temp > 400) {
+ if (fg_debug_mask & FG_AGING)
+ pr_info("Battery temp (%d) out of range, aborting\n",
+ (int)batt_temp);
+ rc = 0;
+ goto done;
+ }
+
+ battery_soc = get_battery_soc_raw(chip) * 100 / FULL_PERCENT_3B;
+ if (battery_soc < 25 || battery_soc > 75) {
+ if (fg_debug_mask & FG_AGING)
+ pr_info("Battery SoC (%d) out of range, aborting\n",
+ (int)battery_soc);
+ rc = 0;
+ goto done;
+ }
+
+ rc = fg_mem_read(chip, buffer, ESR_ACTUAL_REG, 2, 2, 0);
+ esr_actual = half_float(buffer);
+ rc |= fg_mem_read(chip, buffer, BATTERY_ESR_REG, 2, 2, 0);
+ battery_esr = half_float(buffer);
+
+ if (rc) {
+ goto error_done;
+ } else if (esr_actual < battery_esr) {
+ if (fg_debug_mask & FG_AGING)
+ pr_info("Batt ESR lower than ESR actual, aborting\n");
+ rc = 0;
+ goto done;
+ }
+ rc = fg_mem_read(chip, buffer, TEMP_RS_TO_RSLOW_REG, 2, 0, 0);
+ temp_rs_to_rslow = half_float(buffer);
+
+ if (rc)
+ goto error_done;
+
+ fg_mem_release(chip);
+
+ if (fg_debug_mask & FG_AGING) {
+ pr_info("batt_soc = %d, cutoff_voltage = %lld, eval current = %d\n",
+ battery_soc, chip->cutoff_voltage,
+ chip->evaluation_current);
+ pr_info("temp_rs_to_rslow = %lld, batt_esr = %lld, esr_actual = %lld\n",
+ temp_rs_to_rslow, battery_esr, esr_actual);
+ }
+
+ /* calculate soc_cutoff_new */
+ val = (1000000LL + temp_rs_to_rslow) * battery_esr;
+ do_div(val, 1000000);
+ ocv_cutoff_new = div64_s64(chip->evaluation_current * val, 1000)
+ + chip->cutoff_voltage;
+
+ /* calculate soc_cutoff_aged */
+ val = (1000000LL + temp_rs_to_rslow) * esr_actual;
+ do_div(val, 1000000);
+ ocv_cutoff_aged = div64_s64(chip->evaluation_current * val, 1000)
+ + chip->cutoff_voltage;
+
+ if (fg_debug_mask & FG_AGING)
+ pr_info("ocv_cutoff_new = %lld, ocv_cutoff_aged = %lld\n",
+ ocv_cutoff_new, ocv_cutoff_aged);
+
+ soc_cutoff_new = lookup_soc_for_ocv(chip, ocv_cutoff_new);
+ soc_cutoff_aged = lookup_soc_for_ocv(chip, ocv_cutoff_aged);
+
+ if (fg_debug_mask & FG_AGING)
+ pr_info("aged soc = %d, new soc = %d\n",
+ soc_cutoff_aged, soc_cutoff_new);
+ unusable_soc = soc_cutoff_aged - soc_cutoff_new;
+
+ *actual_capacity = div64_s64(((int64_t)chip->nom_cap_uah)
+ * (1000 - unusable_soc), 1000);
+ if (fg_debug_mask & FG_AGING)
+ pr_info("nom cap = %d, actual cap = %d\n",
+ chip->nom_cap_uah, *actual_capacity);
+
+ return rc;
+
+error_done:
+ pr_err("some register reads failed: %d\n", rc);
+done:
+ fg_mem_release(chip);
+ return rc;
+}
+
+static void battery_age_work(struct work_struct *work)
+{
+ struct fg_chip *chip = container_of(work,
+ struct fg_chip,
+ battery_age_work);
+
+ estimate_battery_age(chip, &chip->actual_cap_uah);
+}
+
+static enum power_supply_property fg_power_props[] = {
+ POWER_SUPPLY_PROP_CAPACITY,
+ POWER_SUPPLY_PROP_CAPACITY_RAW,
+ POWER_SUPPLY_PROP_CURRENT_NOW,
+ POWER_SUPPLY_PROP_VOLTAGE_NOW,
+ POWER_SUPPLY_PROP_VOLTAGE_OCV,
+ POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN,
+ POWER_SUPPLY_PROP_CHARGE_NOW,
+ POWER_SUPPLY_PROP_CHARGE_NOW_RAW,
+ POWER_SUPPLY_PROP_CHARGE_NOW_ERROR,
+ POWER_SUPPLY_PROP_CHARGE_FULL,
+ POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN,
+ POWER_SUPPLY_PROP_TEMP,
+ POWER_SUPPLY_PROP_COOL_TEMP,
+ POWER_SUPPLY_PROP_WARM_TEMP,
+ POWER_SUPPLY_PROP_RESISTANCE,
+ POWER_SUPPLY_PROP_RESISTANCE_ID,
+ POWER_SUPPLY_PROP_BATTERY_TYPE,
+ POWER_SUPPLY_PROP_UPDATE_NOW,
+ POWER_SUPPLY_PROP_ESR_COUNT,
+ POWER_SUPPLY_PROP_VOLTAGE_MIN,
+ POWER_SUPPLY_PROP_CYCLE_COUNT,
+ POWER_SUPPLY_PROP_CYCLE_COUNT_ID,
+ POWER_SUPPLY_PROP_HI_POWER,
+};
+
+static int fg_power_get_property(struct power_supply *psy,
+ enum power_supply_property psp,
+ union power_supply_propval *val)
+{
+ struct fg_chip *chip = power_supply_get_drvdata(psy);
+ bool vbatt_low_sts;
+
+ switch (psp) {
+ case POWER_SUPPLY_PROP_BATTERY_TYPE:
+ if (chip->battery_missing)
+ val->strval = missing_batt_type;
+ else if (chip->fg_restarting)
+ val->strval = loading_batt_type;
+ else
+ val->strval = chip->batt_type;
+ break;
+ case POWER_SUPPLY_PROP_CAPACITY:
+ val->intval = get_prop_capacity(chip);
+ break;
+ case POWER_SUPPLY_PROP_CAPACITY_RAW:
+ val->intval = get_sram_prop_now(chip, FG_DATA_BATT_SOC);
+ break;
+ case POWER_SUPPLY_PROP_CHARGE_NOW_ERROR:
+ val->intval = get_sram_prop_now(chip, FG_DATA_VINT_ERR);
+ break;
+ case POWER_SUPPLY_PROP_CURRENT_NOW:
+ val->intval = get_sram_prop_now(chip, FG_DATA_CURRENT);
+ break;
+ case POWER_SUPPLY_PROP_VOLTAGE_NOW:
+ val->intval = get_sram_prop_now(chip, FG_DATA_VOLTAGE);
+ break;
+ case POWER_SUPPLY_PROP_VOLTAGE_OCV:
+ val->intval = get_sram_prop_now(chip, FG_DATA_OCV);
+ break;
+ case POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN:
+ val->intval = chip->batt_max_voltage_uv;
+ break;
+ case POWER_SUPPLY_PROP_TEMP:
+ val->intval = get_sram_prop_now(chip, FG_DATA_BATT_TEMP);
+ break;
+ case POWER_SUPPLY_PROP_COOL_TEMP:
+ val->intval = get_prop_jeita_temp(chip, FG_MEM_SOFT_COLD);
+ break;
+ case POWER_SUPPLY_PROP_WARM_TEMP:
+ val->intval = get_prop_jeita_temp(chip, FG_MEM_SOFT_HOT);
+ break;
+ case POWER_SUPPLY_PROP_RESISTANCE:
+ val->intval = get_sram_prop_now(chip, FG_DATA_BATT_ESR);
+ break;
+ case POWER_SUPPLY_PROP_ESR_COUNT:
+ val->intval = get_sram_prop_now(chip, FG_DATA_BATT_ESR_COUNT);
+ break;
+ case POWER_SUPPLY_PROP_CYCLE_COUNT:
+ val->intval = fg_get_cycle_count(chip);
+ break;
+ case POWER_SUPPLY_PROP_CYCLE_COUNT_ID:
+ val->intval = chip->cyc_ctr.id;
+ break;
+ case POWER_SUPPLY_PROP_RESISTANCE_ID:
+ val->intval = get_sram_prop_now(chip, FG_DATA_BATT_ID);
+ break;
+ case POWER_SUPPLY_PROP_UPDATE_NOW:
+ val->intval = 0;
+ break;
+ case POWER_SUPPLY_PROP_VOLTAGE_MIN:
+ if (!fg_get_vbatt_status(chip, &vbatt_low_sts))
+ val->intval = (int)vbatt_low_sts;
+ else
+ val->intval = 1;
+ break;
+ case POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN:
+ val->intval = chip->nom_cap_uah;
+ break;
+ case POWER_SUPPLY_PROP_CHARGE_FULL:
+ val->intval = chip->learning_data.learned_cc_uah;
+ break;
+ case POWER_SUPPLY_PROP_CHARGE_NOW:
+ val->intval = chip->learning_data.cc_uah;
+ break;
+ case POWER_SUPPLY_PROP_CHARGE_NOW_RAW:
+ val->intval = get_sram_prop_now(chip, FG_DATA_CC_CHARGE);
+ break;
+ case POWER_SUPPLY_PROP_HI_POWER:
+ val->intval = !!chip->bcl_lpm_disabled;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int correction_times[] = {
+ 1470,
+ 2940,
+ 4410,
+ 5880,
+ 7350,
+ 8820,
+ 10290,
+ 11760,
+ 13230,
+ 14700,
+ 16170,
+ 17640,
+ 19110,
+ 20580,
+ 22050,
+ 23520,
+ 24990,
+ 26460,
+ 27930,
+ 29400,
+ 30870,
+ 32340,
+ 33810,
+ 35280,
+ 36750,
+ 38220,
+ 39690,
+ 41160,
+ 42630,
+ 44100,
+ 45570,
+ 47040,
+};
+
+static int correction_factors[] = {
+ 1000000,
+ 1007874,
+ 1015789,
+ 1023745,
+ 1031742,
+ 1039780,
+ 1047859,
+ 1055979,
+ 1064140,
+ 1072342,
+ 1080584,
+ 1088868,
+ 1097193,
+ 1105558,
+ 1113964,
+ 1122411,
+ 1130899,
+ 1139427,
+ 1147996,
+ 1156606,
+ 1165256,
+ 1173947,
+ 1182678,
+ 1191450,
+ 1200263,
+ 1209115,
+ 1218008,
+ 1226942,
+ 1235915,
+ 1244929,
+ 1253983,
+ 1263076,
+};
+
+#define FG_CONVERSION_FACTOR (64198531LL)
+static int iavg_3b_to_uah(u8 *buffer, int delta_ms)
+{
+ int64_t val, i_filtered;
+ int i, correction_factor;
+
+ for (i = 0; i < ARRAY_SIZE(correction_times); i++) {
+ if (correction_times[i] > delta_ms)
+ break;
+ }
+ if (i >= ARRAY_SIZE(correction_times)) {
+ if (fg_debug_mask & FG_STATUS)
+ pr_info("fuel gauge took more than 32 cycles\n");
+ i = ARRAY_SIZE(correction_times) - 1;
+ }
+ correction_factor = correction_factors[i];
+ if (fg_debug_mask & FG_STATUS)
+ pr_info("delta_ms = %d, cycles = %d, correction = %d\n",
+ delta_ms, i, correction_factor);
+ val = buffer[2] << 16 | buffer[1] << 8 | buffer[0];
+ /* convert val from signed 24b to signed 64b */
+ i_filtered = (val << 40) >> 40;
+ val = i_filtered * correction_factor;
+ val = div64_s64(val + FG_CONVERSION_FACTOR / 2, FG_CONVERSION_FACTOR);
+ if (fg_debug_mask & FG_STATUS)
+ pr_info("i_filtered = 0x%llx/%lld, cc_uah = %lld\n",
+ i_filtered, i_filtered, val);
+
+ return val;
+}
+
+static bool fg_is_temperature_ok_for_learning(struct fg_chip *chip)
+{
+ int batt_temp = get_sram_prop_now(chip, FG_DATA_BATT_TEMP);
+
+ if (batt_temp > chip->learning_data.max_temp
+ || batt_temp < chip->learning_data.min_temp) {
+ if (fg_debug_mask & FG_AGING)
+ pr_info("temp (%d) out of range [%d, %d], aborting\n",
+ batt_temp,
+ chip->learning_data.min_temp,
+ chip->learning_data.max_temp);
+ return false;
+ }
+ return true;
+}
+
+static void fg_cap_learning_stop(struct fg_chip *chip)
+{
+ chip->learning_data.cc_uah = 0;
+ chip->learning_data.active = false;
+}
+
+#define I_FILTERED_REG 0x584
+static void fg_cap_learning_work(struct work_struct *work)
+{
+ struct fg_chip *chip = container_of(work,
+ struct fg_chip,
+ fg_cap_learning_work);
+ u8 i_filtered[3], data[3];
+ int rc, cc_uah, delta_ms;
+ ktime_t now_kt, delta_kt;
+
+ mutex_lock(&chip->learning_data.learning_lock);
+ if (!chip->learning_data.active)
+ goto fail;
+ if (!fg_is_temperature_ok_for_learning(chip)) {
+ fg_cap_learning_stop(chip);
+ goto fail;
+ }
+
+ if (chip->wa_flag & USE_CC_SOC_REG) {
+ mutex_unlock(&chip->learning_data.learning_lock);
+ fg_relax(&chip->capacity_learning_wakeup_source);
+ return;
+ }
+
+ fg_mem_lock(chip);
+
+ rc = fg_mem_read(chip, i_filtered, I_FILTERED_REG, 3, 0, 0);
+ if (rc) {
+ pr_err("Failed to read i_filtered: %d\n", rc);
+ fg_mem_release(chip);
+ goto fail;
+ }
+ memset(data, 0, 3);
+ rc = fg_mem_write(chip, data, I_FILTERED_REG, 3, 0, 0);
+ if (rc) {
+ pr_err("Failed to clear i_filtered: %d\n", rc);
+ fg_mem_release(chip);
+ goto fail;
+ }
+ fg_mem_release(chip);
+
+ now_kt = ktime_get_boottime();
+ delta_kt = ktime_sub(now_kt, chip->learning_data.time_stamp);
+ chip->learning_data.time_stamp = now_kt;
+
+ delta_ms = (int)div64_s64(ktime_to_ns(delta_kt), 1000000);
+
+ cc_uah = iavg_3b_to_uah(i_filtered, delta_ms);
+ chip->learning_data.cc_uah -= cc_uah;
+ if (fg_debug_mask & FG_AGING)
+ pr_info("total_cc_uah = %lld\n", chip->learning_data.cc_uah);
+
+fail:
+ mutex_unlock(&chip->learning_data.learning_lock);
+ return;
+
+}
+
+#define CC_SOC_BASE_REG 0x5BC
+#define CC_SOC_OFFSET 3
+#define CC_SOC_MAGNITUDE_MASK 0x1FFFFFFF
+#define CC_SOC_NEGATIVE_BIT BIT(29)
+static int fg_get_cc_soc(struct fg_chip *chip, int *cc_soc)
+{
+ int rc;
+ u8 reg[4];
+ unsigned int temp, magnitude;
+
+ rc = fg_mem_read(chip, reg, CC_SOC_BASE_REG, 4, CC_SOC_OFFSET, 0);
+ if (rc) {
+ pr_err("Failed to read CC_SOC_REG rc=%d\n", rc);
+ return rc;
+ }
+
+ temp = reg[3] << 24 | reg[2] << 16 | reg[1] << 8 | reg[0];
+ magnitude = temp & CC_SOC_MAGNITUDE_MASK;
+ if (temp & CC_SOC_NEGATIVE_BIT)
+ *cc_soc = -1 * (~magnitude + 1);
+ else
+ *cc_soc = magnitude;
+
+ return 0;
+}
+
+static int fg_cap_learning_process_full_data(struct fg_chip *chip)
+{
+ int cc_pc_val, rc = -EINVAL;
+ unsigned int cc_soc_delta_pc;
+ int64_t delta_cc_uah;
+
+ if (!chip->learning_data.active)
+ goto fail;
+
+ if (!fg_is_temperature_ok_for_learning(chip)) {
+ fg_cap_learning_stop(chip);
+ goto fail;
+ }
+
+ rc = fg_get_cc_soc(chip, &cc_pc_val);
+ if (rc) {
+ pr_err("failed to get CC_SOC, stopping capacity learning\n");
+ fg_cap_learning_stop(chip);
+ goto fail;
+ }
+
+ cc_soc_delta_pc = DIV_ROUND_CLOSEST(
+ abs(cc_pc_val - chip->learning_data.init_cc_pc_val)
+ * 100, FULL_PERCENT_28BIT);
+
+ delta_cc_uah = div64_s64(
+ chip->learning_data.learned_cc_uah * cc_soc_delta_pc,
+ 100);
+ chip->learning_data.cc_uah = delta_cc_uah + chip->learning_data.cc_uah;
+
+ if (fg_debug_mask & FG_AGING)
+ pr_info("current cc_soc=%d cc_soc_pc=%d total_cc_uah = %lld\n",
+ cc_pc_val, cc_soc_delta_pc,
+ chip->learning_data.cc_uah);
+
+ return 0;
+
+fail:
+ return rc;
+}
+
+#define FG_CAP_LEARNING_INTERVAL_NS 30000000000
+static enum alarmtimer_restart fg_cap_learning_alarm_cb(struct alarm *alarm,
+ ktime_t now)
+{
+ struct fg_chip *chip = container_of(alarm, struct fg_chip,
+ fg_cap_learning_alarm);
+
+ if (chip->learning_data.active) {
+ if (fg_debug_mask & FG_AGING)
+ pr_info("alarm fired\n");
+ schedule_work(&chip->fg_cap_learning_work);
+ alarm_forward_now(alarm,
+ ns_to_ktime(FG_CAP_LEARNING_INTERVAL_NS));
+ return ALARMTIMER_RESTART;
+ }
+ if (fg_debug_mask & FG_AGING)
+ pr_info("alarm misfired\n");
+ return ALARMTIMER_NORESTART;
+}
+
+#define FG_AGING_STORAGE_REG 0x5E4
+#define ACTUAL_CAPACITY_REG 0x578
+#define MAH_TO_SOC_CONV_REG 0x4A0
+#define CC_SOC_COEFF_OFFSET 0
+#define ACTUAL_CAPACITY_OFFSET 2
+#define MAH_TO_SOC_CONV_CS_OFFSET 0
+static int fg_calc_and_store_cc_soc_coeff(struct fg_chip *chip, int16_t cc_mah)
+{
+ int rc;
+ int64_t cc_to_soc_coeff, mah_to_soc;
+ u8 data[2];
+
+ rc = fg_mem_write(chip, (u8 *)&cc_mah, ACTUAL_CAPACITY_REG, 2,
+ ACTUAL_CAPACITY_OFFSET, 0);
+ if (rc) {
+ pr_err("Failed to store actual capacity: %d\n", rc);
+ return rc;
+ }
+
+ rc = fg_mem_read(chip, (u8 *)&data, MAH_TO_SOC_CONV_REG, 2,
+ MAH_TO_SOC_CONV_CS_OFFSET, 0);
+ if (rc) {
+ pr_err("Failed to read mah_to_soc_conv_cs: %d\n", rc);
+ } else {
+ mah_to_soc = data[1] << 8 | data[0];
+ mah_to_soc *= MICRO_UNIT;
+ cc_to_soc_coeff = div64_s64(mah_to_soc, cc_mah);
+ half_float_to_buffer(cc_to_soc_coeff, data);
+ rc = fg_mem_write(chip, (u8 *)data,
+ ACTUAL_CAPACITY_REG, 2,
+ CC_SOC_COEFF_OFFSET, 0);
+ if (rc)
+ pr_err("Failed to write cc_soc_coeff_offset: %d\n",
+ rc);
+ else if (fg_debug_mask & FG_AGING)
+ pr_info("new cc_soc_coeff %lld [%x %x] saved to sram\n",
+ cc_to_soc_coeff, data[0], data[1]);
+ }
+ return rc;
+}
+
+static void fg_cap_learning_load_data(struct fg_chip *chip)
+{
+ int16_t cc_mah;
+ int64_t old_cap = chip->learning_data.learned_cc_uah;
+ int rc;
+
+ rc = fg_mem_read(chip, (u8 *)&cc_mah, FG_AGING_STORAGE_REG, 2, 0, 0);
+ if (rc) {
+ pr_err("Failed to load aged capacity: %d\n", rc);
+ } else {
+ chip->learning_data.learned_cc_uah = cc_mah * 1000;
+ if (fg_debug_mask & FG_AGING)
+ pr_info("learned capacity %lld-> %lld/%x uah\n",
+ old_cap,
+ chip->learning_data.learned_cc_uah,
+ cc_mah);
+ }
+}
+
+static void fg_cap_learning_save_data(struct fg_chip *chip)
+{
+ int16_t cc_mah;
+ int rc;
+
+ cc_mah = div64_s64(chip->learning_data.learned_cc_uah, 1000);
+
+ rc = fg_mem_write(chip, (u8 *)&cc_mah, FG_AGING_STORAGE_REG, 2, 0, 0);
+ if (rc)
+ pr_err("Failed to store aged capacity: %d\n", rc);
+ else if (fg_debug_mask & FG_AGING)
+ pr_info("learned capacity %lld uah (%d/0x%x uah) saved to sram\n",
+ chip->learning_data.learned_cc_uah,
+ cc_mah, cc_mah);
+
+ if (chip->learning_data.feedback_on) {
+ rc = fg_calc_and_store_cc_soc_coeff(chip, cc_mah);
+ if (rc)
+ pr_err("Error in storing cc_soc_coeff, rc:%d\n", rc);
+ }
+}
+
+static void fg_cap_learning_post_process(struct fg_chip *chip)
+{
+ int64_t max_inc_val, min_dec_val, old_cap;
+
+ max_inc_val = chip->learning_data.learned_cc_uah
+ * (1000 + chip->learning_data.max_increment);
+ do_div(max_inc_val, 1000);
+
+ min_dec_val = chip->learning_data.learned_cc_uah
+ * (1000 - chip->learning_data.max_decrement);
+ do_div(min_dec_val, 1000);
+
+ old_cap = chip->learning_data.learned_cc_uah;
+ if (chip->learning_data.cc_uah > max_inc_val)
+ chip->learning_data.learned_cc_uah = max_inc_val;
+ else if (chip->learning_data.cc_uah < min_dec_val)
+ chip->learning_data.learned_cc_uah = min_dec_val;
+ else
+ chip->learning_data.learned_cc_uah =
+ chip->learning_data.cc_uah;
+
+ fg_cap_learning_save_data(chip);
+ if (fg_debug_mask & FG_AGING)
+ pr_info("final cc_uah = %lld, learned capacity %lld -> %lld uah\n",
+ chip->learning_data.cc_uah,
+ old_cap, chip->learning_data.learned_cc_uah);
+}
+
+static int get_vbat_est_diff(struct fg_chip *chip)
+{
+ return abs(fg_data[FG_DATA_VOLTAGE].value
+ - fg_data[FG_DATA_CPRED_VOLTAGE].value);
+}
+
+#define CBITS_INPUT_FILTER_REG 0x4B4
+#define IBATTF_TAU_MASK 0x38
+#define IBATTF_TAU_99_S 0x30
+static int fg_cap_learning_check(struct fg_chip *chip)
+{
+ u8 data[4];
+ int rc = 0, battery_soc, cc_pc_val;
+ int vbat_est_diff, vbat_est_thr_uv;
+ unsigned int cc_pc_100 = FULL_PERCENT_28BIT;
+
+ mutex_lock(&chip->learning_data.learning_lock);
+ if (chip->status == POWER_SUPPLY_STATUS_CHARGING
+ && !chip->learning_data.active
+ && chip->batt_aging_mode == FG_AGING_CC) {
+ if (chip->learning_data.learned_cc_uah == 0) {
+ if (fg_debug_mask & FG_AGING)
+ pr_info("no capacity, aborting\n");
+ goto fail;
+ }
+
+ if (!fg_is_temperature_ok_for_learning(chip))
+ goto fail;
+
+ fg_mem_lock(chip);
+ if (!chip->learning_data.feedback_on) {
+ vbat_est_diff = get_vbat_est_diff(chip);
+ vbat_est_thr_uv = chip->learning_data.vbat_est_thr_uv;
+ if (vbat_est_diff >= vbat_est_thr_uv &&
+ vbat_est_thr_uv > 0) {
+ if (fg_debug_mask & FG_AGING)
+ pr_info("vbat_est_diff (%d) < threshold (%d)\n",
+ vbat_est_diff, vbat_est_thr_uv);
+ fg_mem_release(chip);
+ fg_cap_learning_stop(chip);
+ goto fail;
+ }
+ }
+ battery_soc = get_battery_soc_raw(chip);
+ if (fg_debug_mask & FG_AGING)
+ pr_info("checking battery soc (%d vs %d)\n",
+ battery_soc * 100 / FULL_PERCENT_3B,
+ chip->learning_data.max_start_soc);
+ /* check if the battery is low enough to start soc learning */
+ if (battery_soc * 100 / FULL_PERCENT_3B
+ > chip->learning_data.max_start_soc) {
+ if (fg_debug_mask & FG_AGING)
+ pr_info("battery soc too low (%d < %d), aborting\n",
+ battery_soc * 100 / FULL_PERCENT_3B,
+ chip->learning_data.max_start_soc);
+ fg_mem_release(chip);
+ fg_cap_learning_stop(chip);
+ goto fail;
+ }
+
+ /* set the coulomb counter to a percentage of the capacity */
+ chip->learning_data.cc_uah = div64_s64(
+ (chip->learning_data.learned_cc_uah * battery_soc),
+ FULL_PERCENT_3B);
+
+ /* Use CC_SOC_REG based capacity learning */
+ if (chip->wa_flag & USE_CC_SOC_REG) {
+ fg_mem_release(chip);
+ /* SW_CC_SOC based capacity learning */
+ if (fg_get_cc_soc(chip, &cc_pc_val)) {
+ pr_err("failed to get CC_SOC, stop capacity learning\n");
+ fg_cap_learning_stop(chip);
+ goto fail;
+ }
+
+ chip->learning_data.init_cc_pc_val = cc_pc_val;
+ chip->learning_data.active = true;
+ if (fg_debug_mask & FG_AGING)
+ pr_info("SW_CC_SOC based learning init_CC_SOC=%d\n",
+ chip->learning_data.init_cc_pc_val);
+ } else {
+ rc = fg_mem_masked_write(chip, CBITS_INPUT_FILTER_REG,
+ IBATTF_TAU_MASK, IBATTF_TAU_99_S, 0);
+ if (rc) {
+ pr_err("Failed to write IF IBAT Tau: %d\n",
+ rc);
+ fg_mem_release(chip);
+ fg_cap_learning_stop(chip);
+ goto fail;
+ }
+
+ /* clear the i_filtered register */
+ memset(data, 0, 4);
+ rc = fg_mem_write(chip, data, I_FILTERED_REG, 3, 0, 0);
+ if (rc) {
+ pr_err("Failed to clear i_filtered: %d\n", rc);
+ fg_mem_release(chip);
+ fg_cap_learning_stop(chip);
+ goto fail;
+ }
+ fg_mem_release(chip);
+ chip->learning_data.time_stamp = ktime_get_boottime();
+ chip->learning_data.active = true;
+
+ if (fg_debug_mask & FG_AGING)
+ pr_info("cap learning started, soc = %d cc_uah = %lld\n",
+ battery_soc * 100 / FULL_PERCENT_3B,
+ chip->learning_data.cc_uah);
+ alarm_start_relative(&chip->fg_cap_learning_alarm,
+ ns_to_ktime(FG_CAP_LEARNING_INTERVAL_NS));
+ }
+ } else if ((chip->status != POWER_SUPPLY_STATUS_CHARGING)
+ && chip->learning_data.active) {
+ if (fg_debug_mask & FG_AGING)
+ pr_info("capacity learning stopped\n");
+ if (!(chip->wa_flag & USE_CC_SOC_REG))
+ alarm_try_to_cancel(&chip->fg_cap_learning_alarm);
+
+ if (chip->status == POWER_SUPPLY_STATUS_FULL) {
+ if (chip->wa_flag & USE_CC_SOC_REG) {
+ rc = fg_cap_learning_process_full_data(chip);
+ if (rc) {
+ fg_cap_learning_stop(chip);
+ goto fail;
+ }
+ /* reset SW_CC_SOC register to 100% */
+ rc = fg_mem_write(chip, (u8 *)&cc_pc_100,
+ CC_SOC_BASE_REG, 4, CC_SOC_OFFSET, 0);
+ if (rc)
+ pr_err("Failed to reset CC_SOC_REG rc=%d\n",
+ rc);
+ }
+ fg_cap_learning_post_process(chip);
+ }
+
+ fg_cap_learning_stop(chip);
+ }
+
+fail:
+ mutex_unlock(&chip->learning_data.learning_lock);
+ return rc;
+}
+
+static bool is_usb_present(struct fg_chip *chip)
+{
+ union power_supply_propval prop = {0,};
+
+ if (!chip->usb_psy)
+ chip->usb_psy = power_supply_get_by_name("usb");
+
+ if (chip->usb_psy)
+ power_supply_get_property(chip->usb_psy,
+ POWER_SUPPLY_PROP_PRESENT, &prop);
+ return prop.intval != 0;
+}
+
+static bool is_dc_present(struct fg_chip *chip)
+{
+ union power_supply_propval prop = {0,};
+
+ if (!chip->dc_psy)
+ chip->dc_psy = power_supply_get_by_name("dc");
+
+ if (chip->dc_psy)
+ power_supply_get_property(chip->dc_psy,
+ POWER_SUPPLY_PROP_PRESENT, &prop);
+ return prop.intval != 0;
+}
+
+static bool is_input_present(struct fg_chip *chip)
+{
+ return is_usb_present(chip) || is_dc_present(chip);
+}
+
+static bool is_otg_present(struct fg_chip *chip)
+{
+ union power_supply_propval prop = {0,};
+
+ if (!chip->usb_psy)
+ chip->usb_psy = power_supply_get_by_name("usb");
+
+ if (chip->usb_psy)
+ power_supply_get_property(chip->usb_psy,
+ POWER_SUPPLY_PROP_USB_OTG, &prop);
+ return prop.intval != 0;
+}
+
+static bool is_charger_available(struct fg_chip *chip)
+{
+ if (!chip->batt_psy_name)
+ return false;
+
+ if (!chip->batt_psy)
+ chip->batt_psy = power_supply_get_by_name(chip->batt_psy_name);
+
+ if (!chip->batt_psy)
+ return false;
+
+ return true;
+}
+
+static int set_prop_enable_charging(struct fg_chip *chip, bool enable)
+{
+ int rc = 0;
+ union power_supply_propval ret = {enable, };
+
+ if (!is_charger_available(chip)) {
+ pr_err("Charger not available yet!\n");
+ return -EINVAL;
+ }
+
+ rc = power_supply_set_property(chip->batt_psy,
+ POWER_SUPPLY_PROP_BATTERY_CHARGING_ENABLED,
+ &ret);
+ if (rc) {
+ pr_err("couldn't configure batt chg %d\n", rc);
+ return rc;
+ }
+
+ chip->charging_disabled = !enable;
+ if (fg_debug_mask & FG_STATUS)
+ pr_info("%sabling charging\n", enable ? "en" : "dis");
+
+ return rc;
+}
+
+#define MAX_BATTERY_CC_SOC_CAPACITY 150
+static void status_change_work(struct work_struct *work)
+{
+ struct fg_chip *chip = container_of(work,
+ struct fg_chip,
+ status_change_work);
+ unsigned long current_time = 0;
+ int cc_soc, rc, capacity = get_prop_capacity(chip);
+
+ if (chip->esr_pulse_tune_en) {
+ fg_stay_awake(&chip->esr_extract_wakeup_source);
+ schedule_work(&chip->esr_extract_config_work);
+ }
+
+ if (chip->status == POWER_SUPPLY_STATUS_FULL) {
+ if (capacity >= 99 && chip->hold_soc_while_full
+ && chip->health == POWER_SUPPLY_HEALTH_GOOD) {
+ if (fg_debug_mask & FG_STATUS)
+ pr_info("holding soc at 100\n");
+ chip->charge_full = true;
+ } else if (fg_debug_mask & FG_STATUS) {
+ pr_info("terminated charging at %d/0x%02x\n",
+ capacity, get_monotonic_soc_raw(chip));
+ }
+ }
+ if (chip->status == POWER_SUPPLY_STATUS_FULL ||
+ chip->status == POWER_SUPPLY_STATUS_CHARGING) {
+ if (!chip->vbat_low_irq_enabled) {
+ enable_irq(chip->batt_irq[VBATT_LOW].irq);
+ enable_irq_wake(chip->batt_irq[VBATT_LOW].irq);
+ chip->vbat_low_irq_enabled = true;
+ }
+ if (!!(chip->wa_flag & PULSE_REQUEST_WA) && capacity == 100)
+ fg_configure_soc(chip);
+ } else if (chip->status == POWER_SUPPLY_STATUS_DISCHARGING) {
+ if (chip->vbat_low_irq_enabled) {
+ disable_irq_wake(chip->batt_irq[VBATT_LOW].irq);
+ disable_irq_nosync(chip->batt_irq[VBATT_LOW].irq);
+ chip->vbat_low_irq_enabled = false;
+ }
+ }
+ fg_cap_learning_check(chip);
+ schedule_work(&chip->update_esr_work);
+
+ if (chip->wa_flag & USE_CC_SOC_REG) {
+ if (fg_get_cc_soc(chip, &cc_soc)) {
+ pr_err("failed to get CC_SOC\n");
+ return;
+ }
+ }
+
+ if (chip->prev_status != chip->status && chip->last_sram_update_time) {
+ get_current_time(¤t_time);
+ /*
+ * When charging status changes, update SRAM parameters if it
+ * was not updated before 5 seconds from now.
+ */
+ if (chip->last_sram_update_time + 5 < current_time) {
+ cancel_delayed_work(&chip->update_sram_data);
+ schedule_delayed_work(&chip->update_sram_data,
+ msecs_to_jiffies(0));
+ }
+ if (chip->cyc_ctr.en)
+ schedule_work(&chip->cycle_count_work);
+ if ((chip->wa_flag & USE_CC_SOC_REG) &&
+ chip->bad_batt_detection_en &&
+ chip->status == POWER_SUPPLY_STATUS_CHARGING) {
+ chip->sw_cc_soc_data.init_sys_soc = capacity;
+ chip->sw_cc_soc_data.init_cc_soc = cc_soc;
+ if (fg_debug_mask & FG_STATUS)
+ pr_info(" Init_sys_soc %d init_cc_soc %d\n",
+ chip->sw_cc_soc_data.init_sys_soc,
+ chip->sw_cc_soc_data.init_cc_soc);
+ }
+ }
+ if ((chip->wa_flag & USE_CC_SOC_REG) && chip->bad_batt_detection_en
+ && chip->safety_timer_expired) {
+ chip->sw_cc_soc_data.delta_soc =
+ DIV_ROUND_CLOSEST(abs(cc_soc -
+ chip->sw_cc_soc_data.init_cc_soc)
+ * 100, FULL_PERCENT_28BIT);
+ chip->sw_cc_soc_data.full_capacity =
+ chip->sw_cc_soc_data.delta_soc +
+ chip->sw_cc_soc_data.init_sys_soc;
+ pr_info("Init_sys_soc %d init_cc_soc %d cc_soc %d delta_soc %d full_capacity %d\n",
+ chip->sw_cc_soc_data.init_sys_soc,
+ chip->sw_cc_soc_data.init_cc_soc, cc_soc,
+ chip->sw_cc_soc_data.delta_soc,
+ chip->sw_cc_soc_data.full_capacity);
+ /*
+ * If sw_cc_soc capacity greater than 150, then it's a bad
+ * battery. else, reset timer and restart charging.
+ */
+ if (chip->sw_cc_soc_data.full_capacity >
+ MAX_BATTERY_CC_SOC_CAPACITY) {
+ pr_info("Battery possibly damaged, do not restart charging\n");
+ } else {
+ pr_info("Reset safety-timer and restart charging\n");
+ rc = set_prop_enable_charging(chip, false);
+ if (rc) {
+ pr_err("failed to disable charging %d\n", rc);
+ return;
+ }
+
+ chip->safety_timer_expired = false;
+ msleep(200);
+
+ rc = set_prop_enable_charging(chip, true);
+ if (rc) {
+ pr_err("failed to enable charging %d\n", rc);
+ return;
+ }
+ }
+ }
+}
+
+/*
+ * Check for change in the status of input or OTG and schedule
+ * IADC gain compensation work.
+ */
+static void check_gain_compensation(struct fg_chip *chip)
+{
+ bool input_present = is_input_present(chip);
+ bool otg_present = is_otg_present(chip);
+
+ if ((chip->wa_flag & IADC_GAIN_COMP_WA)
+ && ((chip->input_present ^ input_present)
+ || (chip->otg_present ^ otg_present))) {
+ fg_stay_awake(&chip->gain_comp_wakeup_source);
+ chip->input_present = input_present;
+ chip->otg_present = otg_present;
+ cancel_work_sync(&chip->gain_comp_work);
+ schedule_work(&chip->gain_comp_work);
+ }
+}
+
+static void fg_hysteresis_config(struct fg_chip *chip)
+{
+ int hard_hot = 0, hard_cold = 0;
+
+ hard_hot = get_prop_jeita_temp(chip, FG_MEM_HARD_HOT);
+ hard_cold = get_prop_jeita_temp(chip, FG_MEM_HARD_COLD);
+ if (chip->health == POWER_SUPPLY_HEALTH_OVERHEAT && !chip->batt_hot) {
+ /* turn down the hard hot threshold */
+ chip->batt_hot = true;
+ set_prop_jeita_temp(chip, FG_MEM_HARD_HOT,
+ hard_hot - chip->hot_hysteresis);
+ if (fg_debug_mask & FG_STATUS)
+ pr_info("hard hot hysteresis: old hot=%d, new hot=%d\n",
+ hard_hot, hard_hot - chip->hot_hysteresis);
+ } else if (chip->health == POWER_SUPPLY_HEALTH_COLD &&
+ !chip->batt_cold) {
+ /* turn up the hard cold threshold */
+ chip->batt_cold = true;
+ set_prop_jeita_temp(chip, FG_MEM_HARD_COLD,
+ hard_cold + chip->cold_hysteresis);
+ if (fg_debug_mask & FG_STATUS)
+ pr_info("hard cold hysteresis: old cold=%d, new cold=%d\n",
+ hard_cold, hard_cold + chip->hot_hysteresis);
+ } else if (chip->health != POWER_SUPPLY_HEALTH_OVERHEAT &&
+ chip->batt_hot) {
+ /* restore the hard hot threshold */
+ set_prop_jeita_temp(chip, FG_MEM_HARD_HOT,
+ hard_hot + chip->hot_hysteresis);
+ chip->batt_hot = !chip->batt_hot;
+ if (fg_debug_mask & FG_STATUS)
+ pr_info("restore hard hot threshold: old hot=%d, new hot=%d\n",
+ hard_hot,
+ hard_hot + chip->hot_hysteresis);
+ } else if (chip->health != POWER_SUPPLY_HEALTH_COLD &&
+ chip->batt_cold) {
+ /* restore the hard cold threshold */
+ set_prop_jeita_temp(chip, FG_MEM_HARD_COLD,
+ hard_cold - chip->cold_hysteresis);
+ chip->batt_cold = !chip->batt_cold;
+ if (fg_debug_mask & FG_STATUS)
+ pr_info("restore hard cold threshold: old cold=%d, new cold=%d\n",
+ hard_cold,
+ hard_cold - chip->cold_hysteresis);
+ }
+}
+
+#define BATT_INFO_STS(base) (base + 0x09)
+#define JEITA_HARD_HOT_RT_STS BIT(6)
+#define JEITA_HARD_COLD_RT_STS BIT(5)
+static int fg_init_batt_temp_state(struct fg_chip *chip)
+{
+ int rc = 0;
+ u8 batt_info_sts;
+ int hard_hot = 0, hard_cold = 0;
+
+ /*
+ * read the batt_info_sts register to parse battery's
+ * initial status and do hysteresis config accordingly.
+ */
+ rc = fg_read(chip, &batt_info_sts,
+ BATT_INFO_STS(chip->batt_base), 1);
+ if (rc) {
+ pr_err("failed to read batt info sts, rc=%d\n", rc);
+ return rc;
+ }
+
+ hard_hot = get_prop_jeita_temp(chip, FG_MEM_HARD_HOT);
+ hard_cold = get_prop_jeita_temp(chip, FG_MEM_HARD_COLD);
+ chip->batt_hot =
+ (batt_info_sts & JEITA_HARD_HOT_RT_STS) ? true : false;
+ chip->batt_cold =
+ (batt_info_sts & JEITA_HARD_COLD_RT_STS) ? true : false;
+ if (chip->batt_hot || chip->batt_cold) {
+ if (chip->batt_hot) {
+ chip->health = POWER_SUPPLY_HEALTH_OVERHEAT;
+ set_prop_jeita_temp(chip, FG_MEM_HARD_HOT,
+ hard_hot - chip->hot_hysteresis);
+ } else {
+ chip->health = POWER_SUPPLY_HEALTH_COLD;
+ set_prop_jeita_temp(chip, FG_MEM_HARD_COLD,
+ hard_cold + chip->cold_hysteresis);
+ }
+ }
+
+ return rc;
+}
+
+static int fg_power_set_property(struct power_supply *psy,
+ enum power_supply_property psp,
+ const union power_supply_propval *val)
+{
+ struct fg_chip *chip = power_supply_get_drvdata(psy);
+ int rc = 0, unused;
+
+ switch (psp) {
+ case POWER_SUPPLY_PROP_COOL_TEMP:
+ rc = set_prop_jeita_temp(chip, FG_MEM_SOFT_COLD, val->intval);
+ break;
+ case POWER_SUPPLY_PROP_WARM_TEMP:
+ rc = set_prop_jeita_temp(chip, FG_MEM_SOFT_HOT, val->intval);
+ break;
+ case POWER_SUPPLY_PROP_UPDATE_NOW:
+ if (val->intval)
+ update_sram_data(chip, &unused);
+ break;
+ case POWER_SUPPLY_PROP_STATUS:
+ chip->prev_status = chip->status;
+ chip->status = val->intval;
+ schedule_work(&chip->status_change_work);
+ check_gain_compensation(chip);
+ break;
+ case POWER_SUPPLY_PROP_HEALTH:
+ chip->health = val->intval;
+ if (chip->health == POWER_SUPPLY_HEALTH_GOOD) {
+ fg_stay_awake(&chip->resume_soc_wakeup_source);
+ schedule_work(&chip->set_resume_soc_work);
+ }
+
+ if (chip->jeita_hysteresis_support)
+ fg_hysteresis_config(chip);
+ break;
+ case POWER_SUPPLY_PROP_CHARGE_DONE:
+ chip->charge_done = val->intval;
+ if (!chip->resume_soc_lowered) {
+ fg_stay_awake(&chip->resume_soc_wakeup_source);
+ schedule_work(&chip->set_resume_soc_work);
+ }
+ break;
+ case POWER_SUPPLY_PROP_CYCLE_COUNT_ID:
+ if ((val->intval > 0) && (val->intval <= BUCKET_COUNT)) {
+ chip->cyc_ctr.id = val->intval;
+ } else {
+ pr_err("rejecting invalid cycle_count_id = %d\n",
+ val->intval);
+ rc = -EINVAL;
+ }
+ break;
+ case POWER_SUPPLY_PROP_SAFETY_TIMER_EXPIRED:
+ chip->safety_timer_expired = val->intval;
+ schedule_work(&chip->status_change_work);
+ break;
+ case POWER_SUPPLY_PROP_HI_POWER:
+ if (chip->wa_flag & BCL_HI_POWER_FOR_CHGLED_WA) {
+ chip->bcl_lpm_disabled = !!val->intval;
+ schedule_work(&chip->bcl_hi_power_work);
+ }
+ break;
+ default:
+ return -EINVAL;
+ };
+
+ return rc;
+};
+
+static int fg_property_is_writeable(struct power_supply *psy,
+ enum power_supply_property psp)
+{
+ switch (psp) {
+ case POWER_SUPPLY_PROP_COOL_TEMP:
+ case POWER_SUPPLY_PROP_WARM_TEMP:
+ case POWER_SUPPLY_PROP_CYCLE_COUNT_ID:
+ return 1;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+#define SRAM_DUMP_START 0x400
+#define SRAM_DUMP_LEN 0x200
+static void dump_sram(struct work_struct *work)
+{
+ int i, rc;
+ u8 *buffer, rt_sts;
+ char str[16];
+ struct fg_chip *chip = container_of(work,
+ struct fg_chip,
+ dump_sram);
+
+ buffer = devm_kzalloc(chip->dev, SRAM_DUMP_LEN, GFP_KERNEL);
+ if (buffer == NULL)
+ return;
+
+ rc = fg_read(chip, &rt_sts, INT_RT_STS(chip->soc_base), 1);
+ if (rc)
+ pr_err("spmi read failed: addr=%03X, rc=%d\n",
+ INT_RT_STS(chip->soc_base), rc);
+ else
+ pr_info("soc rt_sts: 0x%x\n", rt_sts);
+
+ rc = fg_read(chip, &rt_sts, INT_RT_STS(chip->batt_base), 1);
+ if (rc)
+ pr_err("spmi read failed: addr=%03X, rc=%d\n",
+ INT_RT_STS(chip->batt_base), rc);
+ else
+ pr_info("batt rt_sts: 0x%x\n", rt_sts);
+
+ rc = fg_read(chip, &rt_sts, INT_RT_STS(chip->mem_base), 1);
+ if (rc)
+ pr_err("spmi read failed: addr=%03X, rc=%d\n",
+ INT_RT_STS(chip->mem_base), rc);
+ else
+ pr_info("memif rt_sts: 0x%x\n", rt_sts);
+
+ rc = fg_mem_read(chip, buffer, SRAM_DUMP_START, SRAM_DUMP_LEN, 0, 0);
+ if (rc) {
+ pr_err("dump failed: rc = %d\n", rc);
+ return;
+ }
+
+ for (i = 0; i < SRAM_DUMP_LEN; i += 4) {
+ str[0] = '\0';
+ fill_string(str, DEBUG_PRINT_BUFFER_SIZE, buffer + i, 4);
+ pr_info("%03X %s\n", SRAM_DUMP_START + i, str);
+ }
+ devm_kfree(chip->dev, buffer);
+}
+
+#define MAXRSCHANGE_REG 0x434
+#define ESR_VALUE_OFFSET 1
+#define ESR_STRICT_VALUE 0x4120391F391F3019
+#define ESR_DEFAULT_VALUE 0x58CD4A6761C34A67
+static void update_esr_value(struct work_struct *work)
+{
+ union power_supply_propval prop = {0, };
+ u64 esr_value;
+ int rc = 0;
+ struct fg_chip *chip = container_of(work,
+ struct fg_chip,
+ update_esr_work);
+
+ if (!is_charger_available(chip))
+ return;
+
+ power_supply_get_property(chip->batt_psy,
+ POWER_SUPPLY_PROP_CHARGE_TYPE, &prop);
+
+ if (!chip->esr_strict_filter) {
+ if ((prop.intval == POWER_SUPPLY_CHARGE_TYPE_TAPER &&
+ chip->status == POWER_SUPPLY_STATUS_CHARGING) ||
+ (chip->status == POWER_SUPPLY_STATUS_FULL)) {
+ esr_value = ESR_STRICT_VALUE;
+ rc = fg_mem_write(chip, (u8 *)&esr_value,
+ MAXRSCHANGE_REG, 8,
+ ESR_VALUE_OFFSET, 0);
+ if (rc)
+ pr_err("failed to write strict ESR value rc=%d\n",
+ rc);
+ else
+ chip->esr_strict_filter = true;
+ }
+ } else if ((prop.intval != POWER_SUPPLY_CHARGE_TYPE_TAPER &&
+ chip->status == POWER_SUPPLY_STATUS_CHARGING) ||
+ (chip->status == POWER_SUPPLY_STATUS_DISCHARGING)) {
+ esr_value = ESR_DEFAULT_VALUE;
+ rc = fg_mem_write(chip, (u8 *)&esr_value, MAXRSCHANGE_REG, 8,
+ ESR_VALUE_OFFSET, 0);
+ if (rc)
+ pr_err("failed to write default ESR value rc=%d\n", rc);
+ else
+ chip->esr_strict_filter = false;
+ }
+}
+
+#define TEMP_COUNTER_REG 0x580
+#define VBAT_FILTERED_OFFSET 1
+#define GAIN_REG 0x424
+#define GAIN_OFFSET 1
+#define K_VCOR_REG 0x484
+#define DEF_GAIN_OFFSET 2
+#define PICO_UNIT 0xE8D4A51000LL
+#define ATTO_UNIT 0xDE0B6B3A7640000LL
+#define VBAT_REF 3800000
+
+/*
+ * IADC Gain compensation steps:
+ * If Input/OTG absent:
+ * - read VBAT_FILTERED, KVCOR, GAIN
+ * - calculate the gain compensation using following formula:
+ * gain = (1 + gain) * (1 + kvcor * (vbat_filtered - 3800000)) - 1;
+ * else
+ * - reset to the default gain compensation
+ */
+static void iadc_gain_comp_work(struct work_struct *work)
+{
+ u8 reg[4];
+ int rc;
+ uint64_t vbat_filtered;
+ int64_t gain, kvcor, temp, numerator;
+ struct fg_chip *chip = container_of(work, struct fg_chip,
+ gain_comp_work);
+ bool input_present = is_input_present(chip);
+ bool otg_present = is_otg_present(chip);
+
+ if (!chip->init_done)
+ goto done;
+
+ if (!input_present && !otg_present) {
+ /* read VBAT_FILTERED */
+ rc = fg_mem_read(chip, reg, TEMP_COUNTER_REG, 3,
+ VBAT_FILTERED_OFFSET, 0);
+ if (rc) {
+ pr_err("Failed to read VBAT: rc=%d\n", rc);
+ goto done;
+ }
+ temp = (reg[2] << 16) | (reg[1] << 8) | reg[0];
+ vbat_filtered = div_u64((u64)temp * LSB_24B_NUMRTR,
+ LSB_24B_DENMTR);
+
+ /* read K_VCOR */
+ rc = fg_mem_read(chip, reg, K_VCOR_REG, 2, 0, 0);
+ if (rc) {
+ pr_err("Failed to KVCOR rc=%d\n", rc);
+ goto done;
+ }
+ kvcor = half_float(reg);
+
+ /* calculate gain */
+ numerator = (MICRO_UNIT + chip->iadc_comp_data.dfl_gain)
+ * (PICO_UNIT + kvcor * (vbat_filtered - VBAT_REF))
+ - ATTO_UNIT;
+ gain = div64_s64(numerator, PICO_UNIT);
+
+ /* write back gain */
+ half_float_to_buffer(gain, reg);
+ rc = fg_mem_write(chip, reg, GAIN_REG, 2, GAIN_OFFSET, 0);
+ if (rc) {
+ pr_err("Failed to write gain reg rc=%d\n", rc);
+ goto done;
+ }
+
+ if (fg_debug_mask & FG_STATUS)
+ pr_info("IADC gain update [%x %x]\n", reg[1], reg[0]);
+ chip->iadc_comp_data.gain_active = true;
+ } else {
+ /* reset gain register */
+ rc = fg_mem_write(chip, chip->iadc_comp_data.dfl_gain_reg,
+ GAIN_REG, 2, GAIN_OFFSET, 0);
+ if (rc) {
+ pr_err("unable to write gain comp: %d\n", rc);
+ goto done;
+ }
+
+ if (fg_debug_mask & FG_STATUS)
+ pr_info("IADC gain reset [%x %x]\n",
+ chip->iadc_comp_data.dfl_gain_reg[1],
+ chip->iadc_comp_data.dfl_gain_reg[0]);
+ chip->iadc_comp_data.gain_active = false;
+ }
+
+done:
+ fg_relax(&chip->gain_comp_wakeup_source);
+}
+
+#define BATT_MISSING_STS BIT(6)
+static bool is_battery_missing(struct fg_chip *chip)
+{
+ int rc;
+ u8 fg_batt_sts;
+
+ rc = fg_read(chip, &fg_batt_sts,
+ INT_RT_STS(chip->batt_base), 1);
+ if (rc) {
+ pr_err("spmi read failed: addr=%03X, rc=%d\n",
+ INT_RT_STS(chip->batt_base), rc);
+ return false;
+ }
+
+ return (fg_batt_sts & BATT_MISSING_STS) ? true : false;
+}
+
+#define SOC_FIRST_EST_DONE BIT(5)
+static bool is_first_est_done(struct fg_chip *chip)
+{
+ int rc;
+ u8 fg_soc_sts;
+
+ rc = fg_read(chip, &fg_soc_sts,
+ INT_RT_STS(chip->soc_base), 1);
+ if (rc) {
+ pr_err("spmi read failed: addr=%03X, rc=%d\n",
+ INT_RT_STS(chip->soc_base), rc);
+ return false;
+ }
+
+ return (fg_soc_sts & SOC_FIRST_EST_DONE) ? true : false;
+}
+
+static irqreturn_t fg_vbatt_low_handler(int irq, void *_chip)
+{
+ struct fg_chip *chip = _chip;
+ int rc;
+ bool vbatt_low_sts;
+
+ if (fg_debug_mask & FG_IRQS)
+ pr_info("vbatt-low triggered\n");
+
+ if (chip->status == POWER_SUPPLY_STATUS_CHARGING) {
+ rc = fg_get_vbatt_status(chip, &vbatt_low_sts);
+ if (rc) {
+ pr_err("error in reading vbatt_status, rc:%d\n", rc);
+ goto out;
+ }
+ if (!vbatt_low_sts && chip->vbat_low_irq_enabled) {
+ if (fg_debug_mask & FG_IRQS)
+ pr_info("disabling vbatt_low irq\n");
+ disable_irq_wake(chip->batt_irq[VBATT_LOW].irq);
+ disable_irq_nosync(chip->batt_irq[VBATT_LOW].irq);
+ chip->vbat_low_irq_enabled = false;
+ }
+ }
+ if (chip->power_supply_registered)
+ power_supply_changed(chip->bms_psy);
+out:
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t fg_batt_missing_irq_handler(int irq, void *_chip)
+{
+ struct fg_chip *chip = _chip;
+ bool batt_missing = is_battery_missing(chip);
+
+ if (batt_missing) {
+ chip->battery_missing = true;
+ chip->profile_loaded = false;
+ chip->batt_type = default_batt_type;
+ mutex_lock(&chip->cyc_ctr.lock);
+ if (fg_debug_mask & FG_IRQS)
+ pr_info("battery missing, clearing cycle counters\n");
+ clear_cycle_counter(chip);
+ mutex_unlock(&chip->cyc_ctr.lock);
+ } else {
+ if (!chip->use_otp_profile) {
+ reinit_completion(&chip->batt_id_avail);
+ reinit_completion(&chip->first_soc_done);
+ schedule_delayed_work(&chip->batt_profile_init, 0);
+ cancel_delayed_work(&chip->update_sram_data);
+ schedule_delayed_work(
+ &chip->update_sram_data,
+ msecs_to_jiffies(0));
+ } else {
+ chip->battery_missing = false;
+ }
+ }
+
+ if (fg_debug_mask & FG_IRQS)
+ pr_info("batt-missing triggered: %s\n",
+ batt_missing ? "missing" : "present");
+
+ if (chip->power_supply_registered)
+ power_supply_changed(chip->bms_psy);
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t fg_mem_avail_irq_handler(int irq, void *_chip)
+{
+ struct fg_chip *chip = _chip;
+ u8 mem_if_sts;
+ int rc;
+
+ rc = fg_read(chip, &mem_if_sts, INT_RT_STS(chip->mem_base), 1);
+ if (rc) {
+ pr_err("failed to read mem status rc=%d\n", rc);
+ return IRQ_HANDLED;
+ }
+
+ if (fg_check_sram_access(chip)) {
+ if ((fg_debug_mask & FG_IRQS)
+ & (FG_MEM_DEBUG_READS | FG_MEM_DEBUG_WRITES))
+ pr_info("sram access granted\n");
+ reinit_completion(&chip->sram_access_revoked);
+ complete_all(&chip->sram_access_granted);
+ } else {
+ if ((fg_debug_mask & FG_IRQS)
+ & (FG_MEM_DEBUG_READS | FG_MEM_DEBUG_WRITES))
+ pr_info("sram access revoked\n");
+ complete_all(&chip->sram_access_revoked);
+ }
+
+ if (!rc && (fg_debug_mask & FG_IRQS)
+ & (FG_MEM_DEBUG_READS | FG_MEM_DEBUG_WRITES))
+ pr_info("mem_if sts 0x%02x\n", mem_if_sts);
+
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t fg_soc_irq_handler(int irq, void *_chip)
+{
+ struct fg_chip *chip = _chip;
+ u8 soc_rt_sts;
+ int rc;
+
+ rc = fg_read(chip, &soc_rt_sts, INT_RT_STS(chip->soc_base), 1);
+ if (rc) {
+ pr_err("spmi read failed: addr=%03X, rc=%d\n",
+ INT_RT_STS(chip->soc_base), rc);
+ }
+
+ if (fg_debug_mask & FG_IRQS)
+ pr_info("triggered 0x%x\n", soc_rt_sts);
+
+ schedule_work(&chip->battery_age_work);
+
+ if (chip->power_supply_registered)
+ power_supply_changed(chip->bms_psy);
+
+ if (chip->rslow_comp.chg_rs_to_rslow > 0 &&
+ chip->rslow_comp.chg_rslow_comp_c1 > 0 &&
+ chip->rslow_comp.chg_rslow_comp_c2 > 0)
+ schedule_work(&chip->rslow_comp_work);
+ if (chip->cyc_ctr.en)
+ schedule_work(&chip->cycle_count_work);
+ schedule_work(&chip->update_esr_work);
+ if (chip->charge_full)
+ schedule_work(&chip->charge_full_work);
+ if (chip->wa_flag & IADC_GAIN_COMP_WA
+ && chip->iadc_comp_data.gain_active) {
+ fg_stay_awake(&chip->gain_comp_wakeup_source);
+ schedule_work(&chip->gain_comp_work);
+ }
+
+ if (chip->wa_flag & USE_CC_SOC_REG
+ && chip->learning_data.active) {
+ fg_stay_awake(&chip->capacity_learning_wakeup_source);
+ schedule_work(&chip->fg_cap_learning_work);
+ }
+
+ if (chip->esr_pulse_tune_en) {
+ fg_stay_awake(&chip->esr_extract_wakeup_source);
+ schedule_work(&chip->esr_extract_config_work);
+ }
+
+ return IRQ_HANDLED;
+}
+
+#define FG_EMPTY_DEBOUNCE_MS 1500
+static irqreturn_t fg_empty_soc_irq_handler(int irq, void *_chip)
+{
+ struct fg_chip *chip = _chip;
+ u8 soc_rt_sts;
+ int rc;
+
+ rc = fg_read(chip, &soc_rt_sts, INT_RT_STS(chip->soc_base), 1);
+ if (rc) {
+ pr_err("spmi read failed: addr=%03X, rc=%d\n",
+ INT_RT_STS(chip->soc_base), rc);
+ goto done;
+ }
+
+ if (fg_debug_mask & FG_IRQS)
+ pr_info("triggered 0x%x\n", soc_rt_sts);
+ if (fg_is_batt_empty(chip)) {
+ fg_stay_awake(&chip->empty_check_wakeup_source);
+ schedule_delayed_work(&chip->check_empty_work,
+ msecs_to_jiffies(FG_EMPTY_DEBOUNCE_MS));
+ } else {
+ chip->soc_empty = false;
+ }
+
+done:
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t fg_first_soc_irq_handler(int irq, void *_chip)
+{
+ struct fg_chip *chip = _chip;
+
+ if (fg_debug_mask & FG_IRQS)
+ pr_info("triggered\n");
+
+ if (fg_est_dump)
+ schedule_work(&chip->dump_sram);
+
+ if (chip->power_supply_registered)
+ power_supply_changed(chip->bms_psy);
+
+ complete_all(&chip->first_soc_done);
+
+ return IRQ_HANDLED;
+}
+
+static void fg_external_power_changed(struct power_supply *psy)
+{
+ struct fg_chip *chip = power_supply_get_drvdata(psy);
+
+ if (is_input_present(chip) && chip->rslow_comp.active &&
+ chip->rslow_comp.chg_rs_to_rslow > 0 &&
+ chip->rslow_comp.chg_rslow_comp_c1 > 0 &&
+ chip->rslow_comp.chg_rslow_comp_c2 > 0)
+ schedule_work(&chip->rslow_comp_work);
+ if (!is_input_present(chip) && chip->resume_soc_lowered) {
+ fg_stay_awake(&chip->resume_soc_wakeup_source);
+ schedule_work(&chip->set_resume_soc_work);
+ }
+ if (!is_input_present(chip) && chip->charge_full)
+ schedule_work(&chip->charge_full_work);
+}
+
+static void set_resume_soc_work(struct work_struct *work)
+{
+ struct fg_chip *chip = container_of(work,
+ struct fg_chip,
+ set_resume_soc_work);
+ int rc, resume_soc_raw;
+
+ if (is_input_present(chip) && !chip->resume_soc_lowered) {
+ if (!chip->charge_done)
+ goto done;
+ resume_soc_raw = get_monotonic_soc_raw(chip)
+ - (0xFF - settings[FG_MEM_RESUME_SOC].value);
+ if (resume_soc_raw > 0 && resume_soc_raw < FULL_SOC_RAW) {
+ rc = fg_set_resume_soc(chip, resume_soc_raw);
+ if (rc) {
+ pr_err("Couldn't set resume SOC for FG\n");
+ goto done;
+ }
+ if (fg_debug_mask & FG_STATUS) {
+ pr_info("resume soc lowered to 0x%02x\n",
+ resume_soc_raw);
+ }
+ } else if (settings[FG_MEM_RESUME_SOC].value > 0) {
+ pr_err("bad resume soc 0x%02x\n", resume_soc_raw);
+ }
+ chip->charge_done = false;
+ chip->resume_soc_lowered = true;
+ } else if (chip->resume_soc_lowered && (!is_input_present(chip)
+ || chip->health == POWER_SUPPLY_HEALTH_GOOD)) {
+ resume_soc_raw = settings[FG_MEM_RESUME_SOC].value;
+ if (resume_soc_raw > 0 && resume_soc_raw < FULL_SOC_RAW) {
+ rc = fg_set_resume_soc(chip, resume_soc_raw);
+ if (rc) {
+ pr_err("Couldn't set resume SOC for FG\n");
+ goto done;
+ }
+ if (fg_debug_mask & FG_STATUS) {
+ pr_info("resume soc set to 0x%02x\n",
+ resume_soc_raw);
+ }
+ } else if (settings[FG_MEM_RESUME_SOC].value > 0) {
+ pr_err("bad resume soc 0x%02x\n", resume_soc_raw);
+ }
+ chip->resume_soc_lowered = false;
+ }
+done:
+ fg_relax(&chip->resume_soc_wakeup_source);
+}
+
+
+#define OCV_COEFFS_START_REG 0x4C0
+#define OCV_JUNCTION_REG 0x4D8
+#define NOM_CAP_REG 0x4F4
+#define CUTOFF_VOLTAGE_REG 0x40C
+#define RSLOW_CFG_REG 0x538
+#define RSLOW_CFG_OFFSET 2
+#define RSLOW_THRESH_REG 0x52C
+#define RSLOW_THRESH_OFFSET 0
+#define TEMP_RS_TO_RSLOW_OFFSET 2
+#define RSLOW_COMP_REG 0x528
+#define RSLOW_COMP_C1_OFFSET 0
+#define RSLOW_COMP_C2_OFFSET 2
+static int populate_system_data(struct fg_chip *chip)
+{
+ u8 buffer[24];
+ int rc, i;
+ int16_t cc_mah;
+
+ fg_mem_lock(chip);
+ rc = fg_mem_read(chip, buffer, OCV_COEFFS_START_REG, 24, 0, 0);
+ if (rc) {
+ pr_err("Failed to read ocv coefficients: %d\n", rc);
+ goto done;
+ }
+ for (i = 0; i < 12; i += 1)
+ chip->ocv_coeffs[i] = half_float(buffer + (i * 2));
+ if (fg_debug_mask & FG_AGING) {
+ pr_info("coeffs1 = %lld %lld %lld %lld\n",
+ chip->ocv_coeffs[0], chip->ocv_coeffs[1],
+ chip->ocv_coeffs[2], chip->ocv_coeffs[3]);
+ pr_info("coeffs2 = %lld %lld %lld %lld\n",
+ chip->ocv_coeffs[4], chip->ocv_coeffs[5],
+ chip->ocv_coeffs[6], chip->ocv_coeffs[7]);
+ pr_info("coeffs3 = %lld %lld %lld %lld\n",
+ chip->ocv_coeffs[8], chip->ocv_coeffs[9],
+ chip->ocv_coeffs[10], chip->ocv_coeffs[11]);
+ }
+ rc = fg_mem_read(chip, buffer, OCV_JUNCTION_REG, 1, 0, 0);
+ chip->ocv_junction_p1p2 = buffer[0] * 100 / 255;
+ rc |= fg_mem_read(chip, buffer, OCV_JUNCTION_REG, 1, 1, 0);
+ chip->ocv_junction_p2p3 = buffer[0] * 100 / 255;
+ if (rc) {
+ pr_err("Failed to read ocv junctions: %d\n", rc);
+ goto done;
+ }
+ rc = fg_mem_read(chip, buffer, NOM_CAP_REG, 2, 0, 0);
+ if (rc) {
+ pr_err("Failed to read nominal capacitance: %d\n", rc);
+ goto done;
+ }
+ chip->nom_cap_uah = bcap_uah_2b(buffer);
+ chip->actual_cap_uah = chip->nom_cap_uah;
+ if (chip->learning_data.learned_cc_uah == 0) {
+ chip->learning_data.learned_cc_uah = chip->nom_cap_uah;
+ fg_cap_learning_save_data(chip);
+ } else if (chip->learning_data.feedback_on) {
+ cc_mah = div64_s64(chip->learning_data.learned_cc_uah, 1000);
+ rc = fg_calc_and_store_cc_soc_coeff(chip, cc_mah);
+ if (rc)
+ pr_err("Error in restoring cc_soc_coeff, rc:%d\n", rc);
+ }
+ rc = fg_mem_read(chip, buffer, CUTOFF_VOLTAGE_REG, 2, 0, 0);
+ if (rc) {
+ pr_err("Failed to read cutoff voltage: %d\n", rc);
+ goto done;
+ }
+ chip->cutoff_voltage = voltage_2b(buffer);
+ if (fg_debug_mask & FG_AGING)
+ pr_info("cutoff_voltage = %lld, nom_cap_uah = %d p1p2 = %d, p2p3 = %d\n",
+ chip->cutoff_voltage, chip->nom_cap_uah,
+ chip->ocv_junction_p1p2,
+ chip->ocv_junction_p2p3);
+
+ rc = fg_mem_read(chip, buffer, RSLOW_CFG_REG, 1, RSLOW_CFG_OFFSET, 0);
+ if (rc) {
+ pr_err("unable to read rslow cfg: %d\n", rc);
+ goto done;
+ }
+ chip->rslow_comp.rslow_cfg = buffer[0];
+ rc = fg_mem_read(chip, buffer, RSLOW_THRESH_REG, 1,
+ RSLOW_THRESH_OFFSET, 0);
+ if (rc) {
+ pr_err("unable to read rslow thresh: %d\n", rc);
+ goto done;
+ }
+ chip->rslow_comp.rslow_thr = buffer[0];
+ rc = fg_mem_read(chip, buffer, TEMP_RS_TO_RSLOW_REG, 2,
+ RSLOW_THRESH_OFFSET, 0);
+ if (rc) {
+ pr_err("unable to read rs to rslow: %d\n", rc);
+ goto done;
+ }
+ memcpy(chip->rslow_comp.rs_to_rslow, buffer, 2);
+ rc = fg_mem_read(chip, buffer, RSLOW_COMP_REG, 4,
+ RSLOW_COMP_C1_OFFSET, 0);
+ if (rc) {
+ pr_err("unable to read rslow comp: %d\n", rc);
+ goto done;
+ }
+ memcpy(chip->rslow_comp.rslow_comp, buffer, 4);
+
+done:
+ fg_mem_release(chip);
+ return rc;
+}
+
+#define RSLOW_CFG_MASK (BIT(2) | BIT(3) | BIT(4) | BIT(5))
+#define RSLOW_CFG_ON_VAL (BIT(2) | BIT(3))
+#define RSLOW_THRESH_FULL_VAL 0xFF
+static int fg_rslow_charge_comp_set(struct fg_chip *chip)
+{
+ int rc;
+ u8 buffer[2];
+
+ mutex_lock(&chip->rslow_comp.lock);
+ fg_mem_lock(chip);
+
+ rc = fg_mem_masked_write(chip, RSLOW_CFG_REG,
+ RSLOW_CFG_MASK, RSLOW_CFG_ON_VAL, RSLOW_CFG_OFFSET);
+ if (rc) {
+ pr_err("unable to write rslow cfg: %d\n", rc);
+ goto done;
+ }
+ rc = fg_mem_masked_write(chip, RSLOW_THRESH_REG,
+ 0xFF, RSLOW_THRESH_FULL_VAL, RSLOW_THRESH_OFFSET);
+ if (rc) {
+ pr_err("unable to write rslow thresh: %d\n", rc);
+ goto done;
+ }
+
+ half_float_to_buffer(chip->rslow_comp.chg_rs_to_rslow, buffer);
+ rc = fg_mem_write(chip, buffer,
+ TEMP_RS_TO_RSLOW_REG, 2, TEMP_RS_TO_RSLOW_OFFSET, 0);
+ if (rc) {
+ pr_err("unable to write rs to rslow: %d\n", rc);
+ goto done;
+ }
+ half_float_to_buffer(chip->rslow_comp.chg_rslow_comp_c1, buffer);
+ rc = fg_mem_write(chip, buffer,
+ RSLOW_COMP_REG, 2, RSLOW_COMP_C1_OFFSET, 0);
+ if (rc) {
+ pr_err("unable to write rslow comp: %d\n", rc);
+ goto done;
+ }
+ half_float_to_buffer(chip->rslow_comp.chg_rslow_comp_c2, buffer);
+ rc = fg_mem_write(chip, buffer,
+ RSLOW_COMP_REG, 2, RSLOW_COMP_C2_OFFSET, 0);
+ if (rc) {
+ pr_err("unable to write rslow comp: %d\n", rc);
+ goto done;
+ }
+ chip->rslow_comp.active = true;
+ if (fg_debug_mask & FG_STATUS)
+ pr_info("Activated rslow charge comp values\n");
+
+done:
+ fg_mem_release(chip);
+ mutex_unlock(&chip->rslow_comp.lock);
+ return rc;
+}
+
+#define RSLOW_CFG_ORIG_MASK (BIT(4) | BIT(5))
+static int fg_rslow_charge_comp_clear(struct fg_chip *chip)
+{
+ u8 reg;
+ int rc;
+
+ mutex_lock(&chip->rslow_comp.lock);
+ fg_mem_lock(chip);
+
+ reg = chip->rslow_comp.rslow_cfg & RSLOW_CFG_ORIG_MASK;
+ rc = fg_mem_masked_write(chip, RSLOW_CFG_REG,
+ RSLOW_CFG_MASK, reg, RSLOW_CFG_OFFSET);
+ if (rc) {
+ pr_err("unable to write rslow cfg: %d\n", rc);
+ goto done;
+ }
+ rc = fg_mem_masked_write(chip, RSLOW_THRESH_REG,
+ 0xFF, chip->rslow_comp.rslow_thr, RSLOW_THRESH_OFFSET);
+ if (rc) {
+ pr_err("unable to write rslow thresh: %d\n", rc);
+ goto done;
+ }
+
+ rc = fg_mem_write(chip, chip->rslow_comp.rs_to_rslow,
+ TEMP_RS_TO_RSLOW_REG, 2, TEMP_RS_TO_RSLOW_OFFSET, 0);
+ if (rc) {
+ pr_err("unable to write rs to rslow: %d\n", rc);
+ goto done;
+ }
+ rc = fg_mem_write(chip, chip->rslow_comp.rslow_comp,
+ RSLOW_COMP_REG, 4, RSLOW_COMP_C1_OFFSET, 0);
+ if (rc) {
+ pr_err("unable to write rslow comp: %d\n", rc);
+ goto done;
+ }
+ chip->rslow_comp.active = false;
+ if (fg_debug_mask & FG_STATUS)
+ pr_info("Cleared rslow charge comp values\n");
+
+done:
+ fg_mem_release(chip);
+ mutex_unlock(&chip->rslow_comp.lock);
+ return rc;
+}
+
+static void rslow_comp_work(struct work_struct *work)
+{
+ int battery_soc_1b;
+ struct fg_chip *chip = container_of(work,
+ struct fg_chip,
+ rslow_comp_work);
+
+ battery_soc_1b = get_battery_soc_raw(chip) >> 16;
+ if (battery_soc_1b > chip->rslow_comp.chg_rslow_comp_thr
+ && chip->status == POWER_SUPPLY_STATUS_CHARGING) {
+ if (!chip->rslow_comp.active)
+ fg_rslow_charge_comp_set(chip);
+ } else {
+ if (chip->rslow_comp.active)
+ fg_rslow_charge_comp_clear(chip);
+ }
+}
+
+#define MICROUNITS_TO_ADC_RAW(units) \
+ div64_s64(units * LSB_16B_DENMTR, LSB_16B_NUMRTR)
+static int update_chg_iterm(struct fg_chip *chip)
+{
+ u8 data[2];
+ u16 converted_current_raw;
+ s64 current_ma = -settings[FG_MEM_CHG_TERM_CURRENT].value;
+
+ converted_current_raw = (s16)MICROUNITS_TO_ADC_RAW(current_ma * 1000);
+ data[0] = cpu_to_le16(converted_current_raw) & 0xFF;
+ data[1] = cpu_to_le16(converted_current_raw) >> 8;
+
+ if (fg_debug_mask & FG_STATUS)
+ pr_info("current = %lld, converted_raw = %04x, data = %02x %02x\n",
+ current_ma, converted_current_raw, data[0], data[1]);
+ return fg_mem_write(chip, data,
+ settings[FG_MEM_CHG_TERM_CURRENT].address,
+ 2, settings[FG_MEM_CHG_TERM_CURRENT].offset, 0);
+}
+
+#define CC_CV_SETPOINT_REG 0x4F8
+#define CC_CV_SETPOINT_OFFSET 0
+static void update_cc_cv_setpoint(struct fg_chip *chip)
+{
+ int rc;
+ u8 tmp[2];
+
+ if (!chip->cc_cv_threshold_mv)
+ return;
+ batt_to_setpoint_adc(chip->cc_cv_threshold_mv, tmp);
+ rc = fg_mem_write(chip, tmp, CC_CV_SETPOINT_REG, 2,
+ CC_CV_SETPOINT_OFFSET, 0);
+ if (rc) {
+ pr_err("failed to write CC_CV_VOLT rc=%d\n", rc);
+ return;
+ }
+ if (fg_debug_mask & FG_STATUS)
+ pr_info("Wrote %x %x to address %x for CC_CV setpoint\n",
+ tmp[0], tmp[1], CC_CV_SETPOINT_REG);
+}
+
+#define CBITS_INPUT_FILTER_REG 0x4B4
+#define CBITS_RMEAS1_OFFSET 1
+#define CBITS_RMEAS2_OFFSET 2
+#define CBITS_RMEAS1_DEFAULT_VAL 0x65
+#define CBITS_RMEAS2_DEFAULT_VAL 0x65
+#define IMPTR_FAST_TIME_SHIFT 1
+#define IMPTR_LONG_TIME_SHIFT (1 << 4)
+#define IMPTR_PULSE_CTR_CHG 1
+#define IMPTR_PULSE_CTR_DISCHG (1 << 4)
+static int fg_config_imptr_pulse(struct fg_chip *chip, bool slow)
+{
+ int rc;
+ u8 cntr[2] = {0, 0};
+ u8 val;
+
+ if (slow == chip->imptr_pulse_slow_en) {
+ if (fg_debug_mask & FG_STATUS)
+ pr_info("imptr_pulse_slow is %sabled already\n",
+ slow ? "en" : "dis");
+ return 0;
+ }
+
+ fg_mem_lock(chip);
+
+ val = slow ? (IMPTR_FAST_TIME_SHIFT | IMPTR_LONG_TIME_SHIFT) :
+ CBITS_RMEAS1_DEFAULT_VAL;
+ rc = fg_mem_write(chip, &val, CBITS_INPUT_FILTER_REG, 1,
+ CBITS_RMEAS1_OFFSET, 0);
+ if (rc) {
+ pr_err("unable to write cbits_rmeas1_offset rc=%d\n", rc);
+ goto done;
+ }
+
+ val = slow ? (IMPTR_PULSE_CTR_CHG | IMPTR_PULSE_CTR_DISCHG) :
+ CBITS_RMEAS2_DEFAULT_VAL;
+ rc = fg_mem_write(chip, &val, CBITS_INPUT_FILTER_REG, 1,
+ CBITS_RMEAS2_OFFSET, 0);
+ if (rc) {
+ pr_err("unable to write cbits_rmeas2_offset rc=%d\n", rc);
+ goto done;
+ }
+
+ if (slow) {
+ rc = fg_mem_write(chip, cntr, COUNTER_IMPTR_REG, 4,
+ COUNTER_IMPTR_OFFSET, 0);
+ if (rc) {
+ pr_err("failed to write COUNTER_IMPTR rc=%d\n", rc);
+ goto done;
+ }
+
+ rc = fg_mem_write(chip, cntr, COUNTER_PULSE_REG, 2,
+ COUNTER_PULSE_OFFSET, 0);
+ if (rc) {
+ pr_err("failed to write COUNTER_IMPTR rc=%d\n", rc);
+ goto done;
+ }
+ }
+
+ chip->imptr_pulse_slow_en = slow;
+ if (fg_debug_mask & FG_STATUS)
+ pr_info("imptr_pulse_slow is %sabled\n", slow ? "en" : "dis");
+done:
+ fg_mem_release(chip);
+ return rc;
+}
+
+#define CURRENT_DELTA_MIN_REG 0x42C
+#define CURRENT_DELTA_MIN_OFFSET 1
+#define SYS_CFG_1_REG 0x4AC
+#define SYS_CFG_1_OFFSET 0
+#define CURRENT_DELTA_MIN_DEFAULT 0x16
+#define CURRENT_DELTA_MIN_500MA 0xCD
+#define RSLOW_CFG_USE_FIX_RSER_VAL BIT(7)
+#define ENABLE_ESR_PULSE_VAL BIT(3)
+static int fg_config_esr_extract(struct fg_chip *chip, bool disable)
+{
+ int rc;
+ u8 val;
+
+ if (disable == chip->esr_extract_disabled) {
+ if (fg_debug_mask & FG_STATUS)
+ pr_info("ESR extract already %sabled\n",
+ disable ? "dis" : "en");
+ return 0;
+ }
+
+ fg_mem_lock(chip);
+
+ val = disable ? CURRENT_DELTA_MIN_500MA :
+ CURRENT_DELTA_MIN_DEFAULT;
+ rc = fg_mem_write(chip, &val, CURRENT_DELTA_MIN_REG, 1,
+ CURRENT_DELTA_MIN_OFFSET, 0);
+ if (rc) {
+ pr_err("unable to write curr_delta_min rc=%d\n", rc);
+ goto done;
+ }
+
+ val = disable ? RSLOW_CFG_USE_FIX_RSER_VAL : 0;
+ rc = fg_mem_masked_write(chip, RSLOW_CFG_REG,
+ RSLOW_CFG_USE_FIX_RSER_VAL, val, RSLOW_CFG_OFFSET);
+ if (rc) {
+ pr_err("unable to write rslow cfg rc= %d\n", rc);
+ goto done;
+ }
+
+ val = disable ? 0 : ENABLE_ESR_PULSE_VAL;
+ rc = fg_mem_masked_write(chip, SYS_CFG_1_REG,
+ ENABLE_ESR_PULSE_VAL, val, SYS_CFG_1_OFFSET);
+ if (rc) {
+ pr_err("unable to write sys_cfg_1 rc= %d\n", rc);
+ goto done;
+ }
+
+ chip->esr_extract_disabled = disable;
+ if (fg_debug_mask & FG_STATUS)
+ pr_info("ESR extract is %sabled\n", disable ? "dis" : "en");
+done:
+ fg_mem_release(chip);
+ return rc;
+}
+
+#define ESR_EXTRACT_STOP_SOC 2
+#define IMPTR_PULSE_CONFIG_SOC 5
+static void esr_extract_config_work(struct work_struct *work)
+{
+ struct fg_chip *chip = container_of(work, struct fg_chip,
+ esr_extract_config_work);
+ bool input_present = is_input_present(chip);
+ int capacity = get_prop_capacity(chip);
+
+ if (input_present && capacity <= ESR_EXTRACT_STOP_SOC) {
+ fg_config_esr_extract(chip, true);
+ } else if (capacity > ESR_EXTRACT_STOP_SOC) {
+ fg_config_esr_extract(chip, false);
+
+ if (capacity <= IMPTR_PULSE_CONFIG_SOC)
+ fg_config_imptr_pulse(chip, true);
+ else
+ fg_config_imptr_pulse(chip, false);
+ }
+
+ fg_relax(&chip->esr_extract_wakeup_source);
+}
+
+#define LOW_LATENCY BIT(6)
+#define BATT_PROFILE_OFFSET 0x4C0
+#define PROFILE_INTEGRITY_REG 0x53C
+#define PROFILE_INTEGRITY_BIT BIT(0)
+#define FIRST_EST_DONE_BIT BIT(5)
+#define MAX_TRIES_FIRST_EST 3
+#define FIRST_EST_WAIT_MS 2000
+#define PROFILE_LOAD_TIMEOUT_MS 5000
+static int fg_do_restart(struct fg_chip *chip, bool write_profile)
+{
+ int rc, ibat_ua;
+ u8 reg = 0;
+ u8 buf[2];
+ bool tried_once = false;
+
+ if (fg_debug_mask & FG_STATUS)
+ pr_info("restarting fuel gauge...\n");
+
+try_again:
+ if (write_profile) {
+ if (!chip->charging_disabled) {
+ pr_err("Charging not yet disabled!\n");
+ return -EINVAL;
+ }
+
+ ibat_ua = get_sram_prop_now(chip, FG_DATA_CURRENT);
+ if (ibat_ua == -EINVAL) {
+ pr_err("SRAM not updated yet!\n");
+ return ibat_ua;
+ }
+
+ if (ibat_ua < 0) {
+ pr_warn("Charging enabled?, ibat_ua: %d\n", ibat_ua);
+
+ if (!tried_once) {
+ cancel_delayed_work(&chip->update_sram_data);
+ schedule_delayed_work(&chip->update_sram_data,
+ msecs_to_jiffies(0));
+ msleep(1000);
+ tried_once = true;
+ goto try_again;
+ }
+ }
+ }
+
+ chip->fg_restarting = true;
+ /*
+ * save the temperature if the sw rbias control is active so that there
+ * is no gap of time when there is no valid temperature read after the
+ * restart
+ */
+ if (chip->sw_rbias_ctrl) {
+ rc = fg_mem_read(chip, buf,
+ fg_data[FG_DATA_BATT_TEMP].address,
+ fg_data[FG_DATA_BATT_TEMP].len,
+ fg_data[FG_DATA_BATT_TEMP].offset, 0);
+ if (rc) {
+ pr_err("failed to read batt temp rc=%d\n", rc);
+ goto sub_and_fail;
+ }
+ }
+ /*
+ * release the sram access and configure the correct settings
+ * before re-requesting access.
+ */
+ mutex_lock(&chip->rw_lock);
+ fg_release_access(chip);
+
+ rc = fg_masked_write(chip, chip->soc_base + SOC_BOOT_MOD,
+ NO_OTP_PROF_RELOAD, 0, 1);
+ if (rc) {
+ pr_err("failed to set no otp reload bit\n");
+ goto unlock_and_fail;
+ }
+
+ /* unset the restart bits so the fg doesn't continuously restart */
+ reg = REDO_FIRST_ESTIMATE | RESTART_GO;
+ rc = fg_masked_write(chip, chip->soc_base + SOC_RESTART,
+ reg, 0, 1);
+ if (rc) {
+ pr_err("failed to unset fg restart: %d\n", rc);
+ goto unlock_and_fail;
+ }
+
+ rc = fg_masked_write(chip, MEM_INTF_CFG(chip),
+ LOW_LATENCY, LOW_LATENCY, 1);
+ if (rc) {
+ pr_err("failed to set low latency access bit\n");
+ goto unlock_and_fail;
+ }
+ mutex_unlock(&chip->rw_lock);
+
+ /* read once to get a fg cycle in */
+ rc = fg_mem_read(chip, ®, PROFILE_INTEGRITY_REG, 1, 0, 0);
+ if (rc) {
+ pr_err("failed to read profile integrity rc=%d\n", rc);
+ goto fail;
+ }
+
+ /*
+ * If this is not the first time a profile has been loaded, sleep for
+ * 3 seconds to make sure the NO_OTP_RELOAD is cleared in memory
+ */
+ if (chip->first_profile_loaded)
+ msleep(3000);
+
+ mutex_lock(&chip->rw_lock);
+ fg_release_access(chip);
+ rc = fg_masked_write(chip, MEM_INTF_CFG(chip), LOW_LATENCY, 0, 1);
+ if (rc) {
+ pr_err("failed to set low latency access bit\n");
+ goto unlock_and_fail;
+ }
+
+ atomic_add_return(1, &chip->memif_user_cnt);
+ mutex_unlock(&chip->rw_lock);
+
+ if (write_profile) {
+ /* write the battery profile */
+ rc = fg_mem_write(chip, chip->batt_profile, BATT_PROFILE_OFFSET,
+ chip->batt_profile_len, 0, 1);
+ if (rc) {
+ pr_err("failed to write profile rc=%d\n", rc);
+ goto sub_and_fail;
+ }
+ /* write the integrity bits and release access */
+ rc = fg_mem_masked_write(chip, PROFILE_INTEGRITY_REG,
+ PROFILE_INTEGRITY_BIT,
+ PROFILE_INTEGRITY_BIT, 0);
+ if (rc) {
+ pr_err("failed to write profile rc=%d\n", rc);
+ goto sub_and_fail;
+ }
+ }
+
+ /* decrement the user count so that memory access can be released */
+ fg_release_access_if_necessary(chip);
+
+ /*
+ * make sure that the first estimate has completed
+ * in case of a hotswap
+ */
+ rc = wait_for_completion_interruptible_timeout(&chip->first_soc_done,
+ msecs_to_jiffies(PROFILE_LOAD_TIMEOUT_MS));
+ if (rc <= 0) {
+ pr_err("transaction timed out rc=%d\n", rc);
+ rc = -ETIMEDOUT;
+ goto fail;
+ }
+
+ /*
+ * reinitialize the completion so that the driver knows when the restart
+ * finishes
+ */
+ reinit_completion(&chip->first_soc_done);
+
+ if (chip->esr_pulse_tune_en) {
+ fg_stay_awake(&chip->esr_extract_wakeup_source);
+ schedule_work(&chip->esr_extract_config_work);
+ }
+
+ /*
+ * set the restart bits so that the next fg cycle will not reload
+ * the profile
+ */
+ rc = fg_masked_write(chip, chip->soc_base + SOC_BOOT_MOD,
+ NO_OTP_PROF_RELOAD, NO_OTP_PROF_RELOAD, 1);
+ if (rc) {
+ pr_err("failed to set no otp reload bit\n");
+ goto fail;
+ }
+
+ reg = REDO_FIRST_ESTIMATE | RESTART_GO;
+ rc = fg_masked_write(chip, chip->soc_base + SOC_RESTART,
+ reg, reg, 1);
+ if (rc) {
+ pr_err("failed to set fg restart: %d\n", rc);
+ goto fail;
+ }
+
+ /* wait for the first estimate to complete */
+ rc = wait_for_completion_interruptible_timeout(&chip->first_soc_done,
+ msecs_to_jiffies(PROFILE_LOAD_TIMEOUT_MS));
+ if (rc <= 0) {
+ pr_err("transaction timed out rc=%d\n", rc);
+ rc = -ETIMEDOUT;
+ goto fail;
+ }
+ rc = fg_read(chip, ®, INT_RT_STS(chip->soc_base), 1);
+ if (rc) {
+ pr_err("spmi read failed: addr=%03X, rc=%d\n",
+ INT_RT_STS(chip->soc_base), rc);
+ goto fail;
+ }
+ if ((reg & FIRST_EST_DONE_BIT) == 0)
+ pr_err("Battery profile reloading failed, no first estimate\n");
+
+ rc = fg_masked_write(chip, chip->soc_base + SOC_BOOT_MOD,
+ NO_OTP_PROF_RELOAD, 0, 1);
+ if (rc) {
+ pr_err("failed to set no otp reload bit\n");
+ goto fail;
+ }
+ /* unset the restart bits so the fg doesn't continuously restart */
+ reg = REDO_FIRST_ESTIMATE | RESTART_GO;
+ rc = fg_masked_write(chip, chip->soc_base + SOC_RESTART,
+ reg, 0, 1);
+ if (rc) {
+ pr_err("failed to unset fg restart: %d\n", rc);
+ goto fail;
+ }
+
+ /* restore the battery temperature reading here */
+ if (chip->sw_rbias_ctrl) {
+ if (fg_debug_mask & FG_STATUS)
+ pr_info("reloaded 0x%02x%02x into batt temp",
+ buf[0], buf[1]);
+ rc = fg_mem_write(chip, buf,
+ fg_data[FG_DATA_BATT_TEMP].address,
+ fg_data[FG_DATA_BATT_TEMP].len,
+ fg_data[FG_DATA_BATT_TEMP].offset, 0);
+ if (rc) {
+ pr_err("failed to write batt temp rc=%d\n", rc);
+ goto fail;
+ }
+ }
+
+ /* Enable charging now as the first estimate is done now */
+ if (chip->charging_disabled) {
+ rc = set_prop_enable_charging(chip, true);
+ if (rc)
+ pr_err("Failed to enable charging, rc=%d\n", rc);
+ else
+ chip->charging_disabled = false;
+ }
+
+ chip->fg_restarting = false;
+
+ if (fg_debug_mask & FG_STATUS)
+ pr_info("done!\n");
+ return 0;
+
+unlock_and_fail:
+ mutex_unlock(&chip->rw_lock);
+ goto fail;
+sub_and_fail:
+ fg_release_access_if_necessary(chip);
+ goto fail;
+fail:
+ chip->fg_restarting = false;
+ return -EINVAL;
+}
+
+#define FG_PROFILE_LEN 128
+#define PROFILE_COMPARE_LEN 32
+#define THERMAL_COEFF_ADDR 0x444
+#define THERMAL_COEFF_OFFSET 0x2
+#define BATTERY_PSY_WAIT_MS 2000
+static int fg_batt_profile_init(struct fg_chip *chip)
+{
+ int rc = 0, ret, len, batt_id;
+ struct device_node *node = chip->pdev->dev.of_node;
+ struct device_node *batt_node, *profile_node;
+ const char *data, *batt_type_str;
+ bool tried_again = false, vbat_in_range, profiles_same;
+ u8 reg = 0;
+
+wait:
+ fg_stay_awake(&chip->profile_wakeup_source);
+ ret = wait_for_completion_interruptible_timeout(&chip->batt_id_avail,
+ msecs_to_jiffies(PROFILE_LOAD_TIMEOUT_MS));
+ /* If we were interrupted wait again one more time. */
+ if (ret == -ERESTARTSYS && !tried_again) {
+ tried_again = true;
+ pr_debug("interrupted, waiting again\n");
+ goto wait;
+ } else if (ret <= 0) {
+ rc = -ETIMEDOUT;
+ pr_err("profile loading timed out rc=%d\n", rc);
+ goto no_profile;
+ }
+
+ batt_node = of_find_node_by_name(node, "qcom,battery-data");
+ if (!batt_node) {
+ pr_warn("No available batterydata, using OTP defaults\n");
+ rc = 0;
+ goto no_profile;
+ }
+
+ batt_id = get_sram_prop_now(chip, FG_DATA_BATT_ID);
+ batt_id /= 1000;
+ if (fg_debug_mask & FG_STATUS)
+ pr_info("battery id = %dKOhms\n", batt_id);
+
+ profile_node = of_batterydata_get_best_profile(batt_node, batt_id,
+ fg_batt_type);
+ if (IS_ERR_OR_NULL(profile_node)) {
+ rc = PTR_ERR(profile_node);
+ pr_err("couldn't find profile handle %d\n", rc);
+ goto no_profile;
+ }
+
+ /* read rslow compensation values if they're available */
+ rc = of_property_read_u32(profile_node, "qcom,chg-rs-to-rslow",
+ &chip->rslow_comp.chg_rs_to_rslow);
+ if (rc) {
+ chip->rslow_comp.chg_rs_to_rslow = -EINVAL;
+ if (rc != -EINVAL)
+ pr_err("Could not read rs to rslow: %d\n", rc);
+ }
+ rc = of_property_read_u32(profile_node, "qcom,chg-rslow-comp-c1",
+ &chip->rslow_comp.chg_rslow_comp_c1);
+ if (rc) {
+ chip->rslow_comp.chg_rslow_comp_c1 = -EINVAL;
+ if (rc != -EINVAL)
+ pr_err("Could not read rslow comp c1: %d\n", rc);
+ }
+ rc = of_property_read_u32(profile_node, "qcom,chg-rslow-comp-c2",
+ &chip->rslow_comp.chg_rslow_comp_c2);
+ if (rc) {
+ chip->rslow_comp.chg_rslow_comp_c2 = -EINVAL;
+ if (rc != -EINVAL)
+ pr_err("Could not read rslow comp c2: %d\n", rc);
+ }
+ rc = of_property_read_u32(profile_node, "qcom,chg-rslow-comp-thr",
+ &chip->rslow_comp.chg_rslow_comp_thr);
+ if (rc) {
+ chip->rslow_comp.chg_rslow_comp_thr = -EINVAL;
+ if (rc != -EINVAL)
+ pr_err("Could not read rslow comp thr: %d\n", rc);
+ }
+
+ rc = of_property_read_u32(profile_node, "qcom,max-voltage-uv",
+ &chip->batt_max_voltage_uv);
+
+ if (rc)
+ pr_warn("couldn't find battery max voltage\n");
+
+ /*
+ * Only configure from profile if fg-cc-cv-threshold-mv is not
+ * defined in the charger device node.
+ */
+ if (!of_find_property(chip->pdev->dev.of_node,
+ "qcom,fg-cc-cv-threshold-mv", NULL)) {
+ of_property_read_u32(profile_node,
+ "qcom,fg-cc-cv-threshold-mv",
+ &chip->cc_cv_threshold_mv);
+ }
+
+ data = of_get_property(profile_node, "qcom,fg-profile-data", &len);
+ if (!data) {
+ pr_err("no battery profile loaded\n");
+ rc = 0;
+ goto no_profile;
+ }
+
+ if (len != FG_PROFILE_LEN) {
+ pr_err("battery profile incorrect size: %d\n", len);
+ rc = -EINVAL;
+ goto no_profile;
+ }
+
+ rc = of_property_read_string(profile_node, "qcom,battery-type",
+ &batt_type_str);
+ if (rc) {
+ pr_err("Could not find battery data type: %d\n", rc);
+ rc = 0;
+ goto no_profile;
+ }
+
+ if (!chip->batt_profile)
+ chip->batt_profile = devm_kzalloc(chip->dev,
+ sizeof(char) * len, GFP_KERNEL);
+
+ if (!chip->batt_profile) {
+ pr_err("out of memory\n");
+ rc = -ENOMEM;
+ goto no_profile;
+ }
+
+ rc = fg_mem_read(chip, ®, PROFILE_INTEGRITY_REG, 1, 0, 1);
+ if (rc) {
+ pr_err("failed to read profile integrity rc=%d\n", rc);
+ goto no_profile;
+ }
+
+ rc = fg_mem_read(chip, chip->batt_profile, BATT_PROFILE_OFFSET,
+ len, 0, 1);
+ if (rc) {
+ pr_err("failed to read profile rc=%d\n", rc);
+ goto no_profile;
+ }
+
+ /* Check whether the charger is ready */
+ if (!is_charger_available(chip))
+ goto reschedule;
+
+ /* Disable charging for a FG cycle before calculating vbat_in_range */
+ if (!chip->charging_disabled) {
+ rc = set_prop_enable_charging(chip, false);
+ if (rc)
+ pr_err("Failed to disable charging, rc=%d\n", rc);
+
+ goto reschedule;
+ }
+
+ vbat_in_range = get_vbat_est_diff(chip)
+ < settings[FG_MEM_VBAT_EST_DIFF].value * 1000;
+ profiles_same = memcmp(chip->batt_profile, data,
+ PROFILE_COMPARE_LEN) == 0;
+ if (reg & PROFILE_INTEGRITY_BIT) {
+ fg_cap_learning_load_data(chip);
+ if (vbat_in_range && !fg_is_batt_empty(chip) && profiles_same) {
+ if (fg_debug_mask & FG_STATUS)
+ pr_info("Battery profiles same, using default\n");
+ if (fg_est_dump)
+ schedule_work(&chip->dump_sram);
+ goto done;
+ }
+ } else {
+ pr_info("Battery profile not same, clearing data\n");
+ clear_cycle_counter(chip);
+ chip->learning_data.learned_cc_uah = 0;
+ }
+
+ if (fg_est_dump)
+ dump_sram(&chip->dump_sram);
+
+ if ((fg_debug_mask & FG_STATUS) && !vbat_in_range)
+ pr_info("Vbat out of range: v_current_pred: %d, v:%d\n",
+ fg_data[FG_DATA_CPRED_VOLTAGE].value,
+ fg_data[FG_DATA_VOLTAGE].value);
+
+ if ((fg_debug_mask & FG_STATUS) && fg_is_batt_empty(chip))
+ pr_info("battery empty\n");
+
+ if ((fg_debug_mask & FG_STATUS) && !profiles_same)
+ pr_info("profiles differ\n");
+
+ if (fg_debug_mask & FG_STATUS) {
+ pr_info("Using new profile\n");
+ print_hex_dump(KERN_INFO, "FG: loaded profile: ",
+ DUMP_PREFIX_NONE, 16, 1,
+ chip->batt_profile, len, false);
+ }
+
+ if (chip->power_supply_registered)
+ power_supply_changed(chip->bms_psy);
+
+ memcpy(chip->batt_profile, data, len);
+
+ chip->batt_profile_len = len;
+
+ if (fg_debug_mask & FG_STATUS)
+ print_hex_dump(KERN_INFO, "FG: new profile: ",
+ DUMP_PREFIX_NONE, 16, 1, chip->batt_profile,
+ chip->batt_profile_len, false);
+
+ rc = fg_do_restart(chip, true);
+ if (rc) {
+ pr_err("restart failed: %d\n", rc);
+ goto no_profile;
+ }
+
+ /*
+ * Only configure from profile if thermal-coefficients is not
+ * defined in the FG device node.
+ */
+ if (!of_find_property(chip->pdev->dev.of_node,
+ "qcom,thermal-coefficients", NULL)) {
+ data = of_get_property(profile_node,
+ "qcom,thermal-coefficients", &len);
+ if (data && len == THERMAL_COEFF_N_BYTES) {
+ memcpy(chip->thermal_coefficients, data, len);
+ rc = fg_mem_write(chip, chip->thermal_coefficients,
+ THERMAL_COEFF_ADDR, THERMAL_COEFF_N_BYTES,
+ THERMAL_COEFF_OFFSET, 0);
+ if (rc)
+ pr_err("spmi write failed addr:%03x, ret:%d\n",
+ THERMAL_COEFF_ADDR, rc);
+ else if (fg_debug_mask & FG_STATUS)
+ pr_info("Battery thermal coefficients changed\n");
+ }
+ }
+
+done:
+ if (chip->charging_disabled) {
+ rc = set_prop_enable_charging(chip, true);
+ if (rc)
+ pr_err("Failed to enable charging, rc=%d\n", rc);
+ else
+ chip->charging_disabled = false;
+ }
+
+ if (fg_batt_type)
+ chip->batt_type = fg_batt_type;
+ else
+ chip->batt_type = batt_type_str;
+ chip->first_profile_loaded = true;
+ chip->profile_loaded = true;
+ chip->battery_missing = is_battery_missing(chip);
+ update_chg_iterm(chip);
+ update_cc_cv_setpoint(chip);
+ rc = populate_system_data(chip);
+ if (rc) {
+ pr_err("failed to read ocv properties=%d\n", rc);
+ return rc;
+ }
+ estimate_battery_age(chip, &chip->actual_cap_uah);
+ schedule_work(&chip->status_change_work);
+ if (chip->power_supply_registered)
+ power_supply_changed(chip->bms_psy);
+ fg_relax(&chip->profile_wakeup_source);
+ pr_info("Battery SOC: %d, V: %duV\n", get_prop_capacity(chip),
+ fg_data[FG_DATA_VOLTAGE].value);
+ return rc;
+no_profile:
+ if (chip->charging_disabled) {
+ rc = set_prop_enable_charging(chip, true);
+ if (rc)
+ pr_err("Failed to enable charging, rc=%d\n", rc);
+ else
+ chip->charging_disabled = false;
+ }
+
+ if (chip->power_supply_registered)
+ power_supply_changed(chip->bms_psy);
+ fg_relax(&chip->profile_wakeup_source);
+ return rc;
+reschedule:
+ schedule_delayed_work(
+ &chip->batt_profile_init,
+ msecs_to_jiffies(BATTERY_PSY_WAIT_MS));
+ cancel_delayed_work(&chip->update_sram_data);
+ schedule_delayed_work(
+ &chip->update_sram_data,
+ msecs_to_jiffies(0));
+ fg_relax(&chip->profile_wakeup_source);
+ return 0;
+}
+
+static void check_empty_work(struct work_struct *work)
+{
+ struct fg_chip *chip = container_of(work,
+ struct fg_chip,
+ check_empty_work.work);
+
+ if (fg_is_batt_empty(chip)) {
+ if (fg_debug_mask & FG_STATUS)
+ pr_info("EMPTY SOC high\n");
+ chip->soc_empty = true;
+ if (chip->power_supply_registered)
+ power_supply_changed(chip->bms_psy);
+ }
+ fg_relax(&chip->empty_check_wakeup_source);
+}
+
+static void batt_profile_init(struct work_struct *work)
+{
+ struct fg_chip *chip = container_of(work,
+ struct fg_chip,
+ batt_profile_init.work);
+
+ if (fg_batt_profile_init(chip))
+ pr_err("failed to initialize profile\n");
+}
+
+static void sysfs_restart_work(struct work_struct *work)
+{
+ struct fg_chip *chip = container_of(work,
+ struct fg_chip,
+ sysfs_restart_work);
+ int rc;
+
+ rc = fg_do_restart(chip, false);
+ if (rc)
+ pr_err("fg restart failed: %d\n", rc);
+ mutex_lock(&chip->sysfs_restart_lock);
+ fg_restart = 0;
+ mutex_unlock(&chip->sysfs_restart_lock);
+}
+
+#define SRAM_MONOTONIC_SOC_REG 0x574
+#define SRAM_MONOTONIC_SOC_OFFSET 2
+#define SRAM_RELEASE_TIMEOUT_MS 500
+static void charge_full_work(struct work_struct *work)
+{
+ struct fg_chip *chip = container_of(work,
+ struct fg_chip,
+ charge_full_work);
+ int rc;
+ u8 buffer[3];
+ int bsoc;
+ int resume_soc_raw = FULL_SOC_RAW - settings[FG_MEM_RESUME_SOC].value;
+ bool disable = false;
+ u8 reg;
+
+ if (chip->status != POWER_SUPPLY_STATUS_FULL) {
+ if (fg_debug_mask & FG_STATUS)
+ pr_info("battery not full: %d\n", chip->status);
+ disable = true;
+ }
+
+ fg_mem_lock(chip);
+ rc = fg_mem_read(chip, buffer, BATTERY_SOC_REG, 3, 1, 0);
+ if (rc) {
+ pr_err("Unable to read battery soc: %d\n", rc);
+ goto out;
+ }
+ if (buffer[2] <= resume_soc_raw) {
+ if (fg_debug_mask & FG_STATUS)
+ pr_info("bsoc = 0x%02x <= resume = 0x%02x\n",
+ buffer[2], resume_soc_raw);
+ disable = true;
+ }
+ if (!disable)
+ goto out;
+
+ rc = fg_mem_write(chip, buffer, SOC_FULL_REG, 3,
+ SOC_FULL_OFFSET, 0);
+ if (rc) {
+ pr_err("failed to write SOC_FULL rc=%d\n", rc);
+ goto out;
+ }
+ /* force a full soc value into the monotonic in order to display 100 */
+ buffer[0] = 0xFF;
+ buffer[1] = 0xFF;
+ rc = fg_mem_write(chip, buffer, SRAM_MONOTONIC_SOC_REG, 2,
+ SRAM_MONOTONIC_SOC_OFFSET, 0);
+ if (rc) {
+ pr_err("failed to write SOC_FULL rc=%d\n", rc);
+ goto out;
+ }
+ if (fg_debug_mask & FG_STATUS) {
+ bsoc = buffer[0] | buffer[1] << 8 | buffer[2] << 16;
+ pr_info("wrote %06x into soc full\n", bsoc);
+ }
+ fg_mem_release(chip);
+ /*
+ * wait one cycle to make sure the soc is updated before clearing
+ * the soc mask bit
+ */
+ fg_mem_lock(chip);
+ fg_mem_read(chip, ®, PROFILE_INTEGRITY_REG, 1, 0, 0);
+out:
+ fg_mem_release(chip);
+ if (disable)
+ chip->charge_full = false;
+}
+
+static void update_bcl_thresholds(struct fg_chip *chip)
+{
+ u8 data[4];
+ u8 mh_offset = 0, lm_offset = 0;
+ u16 address = 0;
+ int ret = 0;
+
+ address = settings[FG_MEM_BCL_MH_THRESHOLD].address;
+ mh_offset = settings[FG_MEM_BCL_MH_THRESHOLD].offset;
+ lm_offset = settings[FG_MEM_BCL_LM_THRESHOLD].offset;
+ ret = fg_mem_read(chip, data, address, 4, 0, 1);
+ if (ret)
+ pr_err("Error reading BCL LM & MH threshold rc:%d\n", ret);
+ else
+ pr_debug("Old BCL LM threshold:%x MH threshold:%x\n",
+ data[lm_offset], data[mh_offset]);
+ BCL_MA_TO_ADC(settings[FG_MEM_BCL_MH_THRESHOLD].value, data[mh_offset]);
+ BCL_MA_TO_ADC(settings[FG_MEM_BCL_LM_THRESHOLD].value, data[lm_offset]);
+
+ ret = fg_mem_write(chip, data, address, 4, 0, 0);
+ if (ret)
+ pr_err("spmi write failed. addr:%03x, ret:%d\n",
+ address, ret);
+ else
+ pr_debug("New BCL LM threshold:%x MH threshold:%x\n",
+ data[lm_offset], data[mh_offset]);
+}
+
+static int disable_bcl_lpm(struct fg_chip *chip)
+{
+ u8 data[4];
+ u8 lm_offset = 0;
+ u16 address = 0;
+ int rc = 0;
+
+ address = settings[FG_MEM_BCL_LM_THRESHOLD].address;
+ lm_offset = settings[FG_MEM_BCL_LM_THRESHOLD].offset;
+ rc = fg_mem_read(chip, data, address, 4, 0, 1);
+ if (rc) {
+ pr_err("Error reading BCL LM & MH threshold rc:%d\n", rc);
+ return rc;
+ }
+ pr_debug("Old BCL LM threshold:%x\n", data[lm_offset]);
+
+ /* Put BCL always above LPM */
+ BCL_MA_TO_ADC(0, data[lm_offset]);
+
+ rc = fg_mem_write(chip, data, address, 4, 0, 0);
+ if (rc)
+ pr_err("spmi write failed. addr:%03x, rc:%d\n",
+ address, rc);
+ else
+ pr_debug("New BCL LM threshold:%x\n", data[lm_offset]);
+
+ return rc;
+}
+
+static void bcl_hi_power_work(struct work_struct *work)
+{
+ struct fg_chip *chip = container_of(work,
+ struct fg_chip,
+ bcl_hi_power_work);
+ int rc;
+
+ if (chip->bcl_lpm_disabled) {
+ rc = disable_bcl_lpm(chip);
+ if (rc)
+ pr_err("failed to disable bcl low mode %d\n",
+ rc);
+ } else {
+ update_bcl_thresholds(chip);
+ }
+}
+
+#define VOLT_UV_TO_VOLTCMP8(volt_uv) \
+ ((volt_uv - 2500000) / 9766)
+static int update_irq_volt_empty(struct fg_chip *chip)
+{
+ u8 data;
+ int volt_mv = settings[FG_MEM_IRQ_VOLT_EMPTY].value;
+
+ data = (u8)VOLT_UV_TO_VOLTCMP8(volt_mv * 1000);
+
+ if (fg_debug_mask & FG_STATUS)
+ pr_info("voltage = %d, converted_raw = %04x\n", volt_mv, data);
+ return fg_mem_write(chip, &data,
+ settings[FG_MEM_IRQ_VOLT_EMPTY].address, 1,
+ settings[FG_MEM_IRQ_VOLT_EMPTY].offset, 0);
+}
+
+static int update_cutoff_voltage(struct fg_chip *chip)
+{
+ u8 data[2];
+ u16 converted_voltage_raw;
+ s64 voltage_mv = settings[FG_MEM_CUTOFF_VOLTAGE].value;
+
+ converted_voltage_raw = (s16)MICROUNITS_TO_ADC_RAW(voltage_mv * 1000);
+ data[0] = cpu_to_le16(converted_voltage_raw) & 0xFF;
+ data[1] = cpu_to_le16(converted_voltage_raw) >> 8;
+
+ if (fg_debug_mask & FG_STATUS)
+ pr_info("voltage = %lld, converted_raw = %04x, data = %02x %02x\n",
+ voltage_mv, converted_voltage_raw, data[0], data[1]);
+ return fg_mem_write(chip, data, settings[FG_MEM_CUTOFF_VOLTAGE].address,
+ 2, settings[FG_MEM_CUTOFF_VOLTAGE].offset, 0);
+}
+
+static int update_iterm(struct fg_chip *chip)
+{
+ u8 data[2];
+ u16 converted_current_raw;
+ s64 current_ma = -settings[FG_MEM_TERM_CURRENT].value;
+
+ converted_current_raw = (s16)MICROUNITS_TO_ADC_RAW(current_ma * 1000);
+ data[0] = cpu_to_le16(converted_current_raw) & 0xFF;
+ data[1] = cpu_to_le16(converted_current_raw) >> 8;
+
+ if (fg_debug_mask & FG_STATUS)
+ pr_info("current = %lld, converted_raw = %04x, data = %02x %02x\n",
+ current_ma, converted_current_raw, data[0], data[1]);
+ return fg_mem_write(chip, data, settings[FG_MEM_TERM_CURRENT].address,
+ 2, settings[FG_MEM_TERM_CURRENT].offset, 0);
+}
+
+#define OF_READ_SETTING(type, qpnp_dt_property, retval, optional) \
+do { \
+ if (retval) \
+ break; \
+ \
+ retval = of_property_read_u32(chip->pdev->dev.of_node, \
+ "qcom," qpnp_dt_property, \
+ &settings[type].value); \
+ \
+ if ((retval == -EINVAL) && optional) \
+ retval = 0; \
+ else if (retval) \
+ pr_err("Error reading " #qpnp_dt_property \
+ " property rc = %d\n", rc); \
+} while (0)
+
+#define OF_READ_PROPERTY(store, qpnp_dt_property, retval, default_val) \
+do { \
+ if (retval) \
+ break; \
+ \
+ retval = of_property_read_u32(chip->pdev->dev.of_node, \
+ "qcom," qpnp_dt_property, \
+ &store); \
+ \
+ if (retval == -EINVAL) { \
+ retval = 0; \
+ store = default_val; \
+ } else if (retval) { \
+ pr_err("Error reading " #qpnp_dt_property \
+ " property rc = %d\n", rc); \
+ } \
+} while (0)
+
+#define DEFAULT_EVALUATION_CURRENT_MA 1000
+static int fg_of_init(struct fg_chip *chip)
+{
+ int rc = 0, sense_type, len = 0;
+ const char *data;
+ struct device_node *node = chip->pdev->dev.of_node;
+ u32 temp[2] = {0};
+
+ OF_READ_SETTING(FG_MEM_SOFT_HOT, "warm-bat-decidegc", rc, 1);
+ OF_READ_SETTING(FG_MEM_SOFT_COLD, "cool-bat-decidegc", rc, 1);
+ OF_READ_SETTING(FG_MEM_HARD_HOT, "hot-bat-decidegc", rc, 1);
+ OF_READ_SETTING(FG_MEM_HARD_COLD, "cold-bat-decidegc", rc, 1);
+
+ if (of_find_property(node, "qcom,cold-hot-jeita-hysteresis", NULL)) {
+ int hard_hot = 0, soft_hot = 0, hard_cold = 0, soft_cold = 0;
+
+ rc = of_property_read_u32_array(node,
+ "qcom,cold-hot-jeita-hysteresis", temp, 2);
+ if (rc) {
+ pr_err("Error reading cold-hot-jeita-hysteresis rc=%d\n",
+ rc);
+ return rc;
+ }
+
+ chip->jeita_hysteresis_support = true;
+ chip->cold_hysteresis = temp[0];
+ chip->hot_hysteresis = temp[1];
+ hard_hot = settings[FG_MEM_HARD_HOT].value;
+ soft_hot = settings[FG_MEM_SOFT_HOT].value;
+ hard_cold = settings[FG_MEM_HARD_COLD].value;
+ soft_cold = settings[FG_MEM_SOFT_COLD].value;
+ if (((hard_hot - chip->hot_hysteresis) < soft_hot) ||
+ ((hard_cold + chip->cold_hysteresis) > soft_cold)) {
+ chip->jeita_hysteresis_support = false;
+ pr_err("invalid hysteresis: hot_hysterresis = %d cold_hysteresis = %d\n",
+ chip->hot_hysteresis, chip->cold_hysteresis);
+ } else {
+ pr_debug("cold_hysteresis = %d, hot_hysteresis = %d\n",
+ chip->cold_hysteresis, chip->hot_hysteresis);
+ }
+ }
+
+ OF_READ_SETTING(FG_MEM_BCL_LM_THRESHOLD, "bcl-lm-threshold-ma",
+ rc, 1);
+ OF_READ_SETTING(FG_MEM_BCL_MH_THRESHOLD, "bcl-mh-threshold-ma",
+ rc, 1);
+ OF_READ_SETTING(FG_MEM_TERM_CURRENT, "fg-iterm-ma", rc, 1);
+ OF_READ_SETTING(FG_MEM_CHG_TERM_CURRENT, "fg-chg-iterm-ma", rc, 1);
+ OF_READ_SETTING(FG_MEM_CUTOFF_VOLTAGE, "fg-cutoff-voltage-mv", rc, 1);
+ data = of_get_property(chip->pdev->dev.of_node,
+ "qcom,thermal-coefficients", &len);
+ if (data && len == THERMAL_COEFF_N_BYTES) {
+ memcpy(chip->thermal_coefficients, data, len);
+ chip->use_thermal_coefficients = true;
+ }
+ OF_READ_SETTING(FG_MEM_RESUME_SOC, "resume-soc", rc, 1);
+ settings[FG_MEM_RESUME_SOC].value =
+ DIV_ROUND_CLOSEST(settings[FG_MEM_RESUME_SOC].value
+ * FULL_SOC_RAW, FULL_CAPACITY);
+ OF_READ_SETTING(FG_MEM_RESUME_SOC, "resume-soc-raw", rc, 1);
+ OF_READ_SETTING(FG_MEM_IRQ_VOLT_EMPTY, "irq-volt-empty-mv", rc, 1);
+ OF_READ_SETTING(FG_MEM_VBAT_EST_DIFF, "vbat-estimate-diff-mv", rc, 1);
+ OF_READ_SETTING(FG_MEM_DELTA_SOC, "fg-delta-soc", rc, 1);
+ OF_READ_SETTING(FG_MEM_BATT_LOW, "fg-vbatt-low-threshold", rc, 1);
+ OF_READ_SETTING(FG_MEM_THERM_DELAY, "fg-therm-delay-us", rc, 1);
+ OF_READ_PROPERTY(chip->learning_data.max_increment,
+ "cl-max-increment-deciperc", rc, 5);
+ OF_READ_PROPERTY(chip->learning_data.max_decrement,
+ "cl-max-decrement-deciperc", rc, 100);
+ OF_READ_PROPERTY(chip->learning_data.max_temp,
+ "cl-max-temp-decidegc", rc, 450);
+ OF_READ_PROPERTY(chip->learning_data.min_temp,
+ "cl-min-temp-decidegc", rc, 150);
+ OF_READ_PROPERTY(chip->learning_data.max_start_soc,
+ "cl-max-start-capacity", rc, 15);
+ OF_READ_PROPERTY(chip->learning_data.vbat_est_thr_uv,
+ "cl-vbat-est-thr-uv", rc, 40000);
+ OF_READ_PROPERTY(chip->evaluation_current,
+ "aging-eval-current-ma", rc,
+ DEFAULT_EVALUATION_CURRENT_MA);
+ OF_READ_PROPERTY(chip->cc_cv_threshold_mv,
+ "fg-cc-cv-threshold-mv", rc, 0);
+ if (of_property_read_bool(chip->pdev->dev.of_node,
+ "qcom,capacity-learning-on"))
+ chip->batt_aging_mode = FG_AGING_CC;
+ else if (of_property_read_bool(chip->pdev->dev.of_node,
+ "qcom,capacity-estimation-on"))
+ chip->batt_aging_mode = FG_AGING_ESR;
+ else
+ chip->batt_aging_mode = FG_AGING_NONE;
+ if (chip->batt_aging_mode == FG_AGING_CC) {
+ chip->learning_data.feedback_on
+ = of_property_read_bool(chip->pdev->dev.of_node,
+ "qcom,capacity-learning-feedback");
+ }
+ if (fg_debug_mask & FG_AGING)
+ pr_info("battery aging mode: %d\n", chip->batt_aging_mode);
+
+ /* Get the use-otp-profile property */
+ chip->use_otp_profile = of_property_read_bool(chip->pdev->dev.of_node,
+ "qcom,use-otp-profile");
+ chip->hold_soc_while_full
+ = of_property_read_bool(chip->pdev->dev.of_node,
+ "qcom,hold-soc-while-full");
+
+ sense_type = of_property_read_bool(chip->pdev->dev.of_node,
+ "qcom,ext-sense-type");
+ if (rc == 0) {
+ if (fg_sense_type < 0)
+ fg_sense_type = sense_type;
+
+ if (fg_debug_mask & FG_STATUS) {
+ if (fg_sense_type == INTERNAL_CURRENT_SENSE)
+ pr_info("Using internal sense\n");
+ else if (fg_sense_type == EXTERNAL_CURRENT_SENSE)
+ pr_info("Using external sense\n");
+ else
+ pr_info("Using default sense\n");
+ }
+ } else {
+ rc = 0;
+ }
+
+ chip->bad_batt_detection_en = of_property_read_bool(node,
+ "qcom,bad-battery-detection-enable");
+
+ chip->sw_rbias_ctrl = of_property_read_bool(node,
+ "qcom,sw-rbias-control");
+
+ chip->cyc_ctr.en = of_property_read_bool(node,
+ "qcom,cycle-counter-en");
+ if (chip->cyc_ctr.en)
+ chip->cyc_ctr.id = 1;
+
+ chip->esr_pulse_tune_en = of_property_read_bool(node,
+ "qcom,esr-pulse-tuning-en");
+
+ return rc;
+}
+
+static int fg_init_irqs(struct fg_chip *chip)
+{
+ int rc = 0;
+ unsigned int base;
+ struct device_node *child;
+ u8 subtype;
+ struct platform_device *pdev = chip->pdev;
+
+ if (of_get_available_child_count(pdev->dev.of_node) == 0) {
+ pr_err("no child nodes\n");
+ return -ENXIO;
+ }
+
+ for_each_available_child_of_node(pdev->dev.of_node, child) {
+ rc = of_property_read_u32(child, "reg", &base);
+ if (rc < 0) {
+ dev_err(&pdev->dev,
+ "Couldn't find reg in node = %s rc = %d\n",
+ child->full_name, rc);
+ return rc;
+ }
+
+ if ((base == chip->vbat_adc_addr) ||
+ (base == chip->ibat_adc_addr) ||
+ (base == chip->tp_rev_addr))
+ continue;
+
+ rc = fg_read(chip, &subtype,
+ base + REG_OFFSET_PERP_SUBTYPE, 1);
+ if (rc) {
+ pr_err("Peripheral subtype read failed rc=%d\n", rc);
+ return rc;
+ }
+
+ switch (subtype) {
+ case FG_SOC:
+ chip->soc_irq[FULL_SOC].irq = of_irq_get_byname(child,
+ "full-soc");
+ if (chip->soc_irq[FULL_SOC].irq < 0) {
+ pr_err("Unable to get full-soc irq\n");
+ return rc;
+ }
+ chip->soc_irq[EMPTY_SOC].irq = of_irq_get_byname(child,
+ "empty-soc");
+ if (chip->soc_irq[EMPTY_SOC].irq < 0) {
+ pr_err("Unable to get low-soc irq\n");
+ return rc;
+ }
+ chip->soc_irq[DELTA_SOC].irq = of_irq_get_byname(child,
+ "delta-soc");
+ if (chip->soc_irq[DELTA_SOC].irq < 0) {
+ pr_err("Unable to get delta-soc irq\n");
+ return rc;
+ }
+ chip->soc_irq[FIRST_EST_DONE].irq
+ = of_irq_get_byname(child, "first-est-done");
+ if (chip->soc_irq[FIRST_EST_DONE].irq < 0) {
+ pr_err("Unable to get first-est-done irq\n");
+ return rc;
+ }
+
+ rc = devm_request_irq(chip->dev,
+ chip->soc_irq[FULL_SOC].irq,
+ fg_soc_irq_handler, IRQF_TRIGGER_RISING,
+ "full-soc", chip);
+ if (rc < 0) {
+ pr_err("Can't request %d full-soc: %d\n",
+ chip->soc_irq[FULL_SOC].irq, rc);
+ return rc;
+ }
+ rc = devm_request_irq(chip->dev,
+ chip->soc_irq[EMPTY_SOC].irq,
+ fg_empty_soc_irq_handler,
+ IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
+ "empty-soc", chip);
+ if (rc < 0) {
+ pr_err("Can't request %d empty-soc: %d\n",
+ chip->soc_irq[EMPTY_SOC].irq, rc);
+ return rc;
+ }
+ rc = devm_request_irq(chip->dev,
+ chip->soc_irq[DELTA_SOC].irq,
+ fg_soc_irq_handler, IRQF_TRIGGER_RISING,
+ "delta-soc", chip);
+ if (rc < 0) {
+ pr_err("Can't request %d delta-soc: %d\n",
+ chip->soc_irq[DELTA_SOC].irq, rc);
+ return rc;
+ }
+ rc = devm_request_irq(chip->dev,
+ chip->soc_irq[FIRST_EST_DONE].irq,
+ fg_first_soc_irq_handler, IRQF_TRIGGER_RISING,
+ "first-est-done", chip);
+ if (rc < 0) {
+ pr_err("Can't request %d delta-soc: %d\n",
+ chip->soc_irq[FIRST_EST_DONE].irq, rc);
+ return rc;
+ }
+
+ enable_irq_wake(chip->soc_irq[DELTA_SOC].irq);
+ enable_irq_wake(chip->soc_irq[FULL_SOC].irq);
+ enable_irq_wake(chip->soc_irq[EMPTY_SOC].irq);
+ break;
+ case FG_MEMIF:
+ chip->mem_irq[FG_MEM_AVAIL].irq
+ = of_irq_get_byname(child, "mem-avail");
+ if (chip->mem_irq[FG_MEM_AVAIL].irq < 0) {
+ pr_err("Unable to get mem-avail irq\n");
+ return rc;
+ }
+ rc = devm_request_irq(chip->dev,
+ chip->mem_irq[FG_MEM_AVAIL].irq,
+ fg_mem_avail_irq_handler,
+ IRQF_TRIGGER_RISING |
+ IRQF_TRIGGER_FALLING,
+ "mem-avail", chip);
+ if (rc < 0) {
+ pr_err("Can't request %d mem-avail: %d\n",
+ chip->mem_irq[FG_MEM_AVAIL].irq, rc);
+ return rc;
+ }
+ break;
+ case FG_BATT:
+ chip->batt_irq[BATT_MISSING].irq
+ = of_irq_get_byname(child, "batt-missing");
+ if (chip->batt_irq[BATT_MISSING].irq < 0) {
+ pr_err("Unable to get batt-missing irq\n");
+ rc = -EINVAL;
+ return rc;
+ }
+ rc = devm_request_threaded_irq(chip->dev,
+ chip->batt_irq[BATT_MISSING].irq,
+ NULL,
+ fg_batt_missing_irq_handler,
+ IRQF_TRIGGER_RISING |
+ IRQF_TRIGGER_FALLING |
+ IRQF_ONESHOT,
+ "batt-missing", chip);
+ if (rc < 0) {
+ pr_err("Can't request %d batt-missing: %d\n",
+ chip->batt_irq[BATT_MISSING].irq, rc);
+ return rc;
+ }
+ chip->batt_irq[VBATT_LOW].irq
+ = of_irq_get_byname(child, "vbatt-low");
+ if (chip->batt_irq[VBATT_LOW].irq < 0) {
+ pr_err("Unable to get vbatt-low irq\n");
+ rc = -EINVAL;
+ return rc;
+ }
+ rc = devm_request_irq(chip->dev,
+ chip->batt_irq[VBATT_LOW].irq,
+ fg_vbatt_low_handler,
+ IRQF_TRIGGER_RISING |
+ IRQF_TRIGGER_FALLING,
+ "vbatt-low", chip);
+ if (rc < 0) {
+ pr_err("Can't request %d vbatt-low: %d\n",
+ chip->batt_irq[VBATT_LOW].irq, rc);
+ return rc;
+ }
+ disable_irq_nosync(chip->batt_irq[VBATT_LOW].irq);
+ chip->vbat_low_irq_enabled = false;
+ break;
+ case FG_ADC:
+ break;
+ default:
+ pr_err("subtype %d\n", subtype);
+ return -EINVAL;
+ }
+ }
+
+ return rc;
+}
+
+static void fg_cleanup(struct fg_chip *chip)
+{
+ cancel_delayed_work_sync(&chip->update_sram_data);
+ cancel_delayed_work_sync(&chip->update_temp_work);
+ cancel_delayed_work_sync(&chip->update_jeita_setting);
+ cancel_delayed_work_sync(&chip->check_empty_work);
+ cancel_delayed_work_sync(&chip->batt_profile_init);
+ alarm_try_to_cancel(&chip->fg_cap_learning_alarm);
+ cancel_work_sync(&chip->rslow_comp_work);
+ cancel_work_sync(&chip->set_resume_soc_work);
+ cancel_work_sync(&chip->fg_cap_learning_work);
+ cancel_work_sync(&chip->dump_sram);
+ cancel_work_sync(&chip->status_change_work);
+ cancel_work_sync(&chip->cycle_count_work);
+ cancel_work_sync(&chip->update_esr_work);
+ cancel_work_sync(&chip->sysfs_restart_work);
+ cancel_work_sync(&chip->gain_comp_work);
+ cancel_work_sync(&chip->init_work);
+ cancel_work_sync(&chip->charge_full_work);
+ cancel_work_sync(&chip->esr_extract_config_work);
+ mutex_destroy(&chip->rslow_comp.lock);
+ mutex_destroy(&chip->rw_lock);
+ mutex_destroy(&chip->cyc_ctr.lock);
+ mutex_destroy(&chip->learning_data.learning_lock);
+ mutex_destroy(&chip->sysfs_restart_lock);
+ wakeup_source_trash(&chip->resume_soc_wakeup_source.source);
+ wakeup_source_trash(&chip->empty_check_wakeup_source.source);
+ wakeup_source_trash(&chip->memif_wakeup_source.source);
+ wakeup_source_trash(&chip->profile_wakeup_source.source);
+ wakeup_source_trash(&chip->update_temp_wakeup_source.source);
+ wakeup_source_trash(&chip->update_sram_wakeup_source.source);
+ wakeup_source_trash(&chip->gain_comp_wakeup_source.source);
+ wakeup_source_trash(&chip->capacity_learning_wakeup_source.source);
+ wakeup_source_trash(&chip->esr_extract_wakeup_source.source);
+}
+
+static int fg_remove(struct platform_device *pdev)
+{
+ struct fg_chip *chip = dev_get_drvdata(&pdev->dev);
+
+ fg_cleanup(chip);
+ dev_set_drvdata(&pdev->dev, NULL);
+ return 0;
+}
+
+static int fg_memif_data_open(struct inode *inode, struct file *file)
+{
+ struct fg_log_buffer *log;
+ struct fg_trans *trans;
+ u8 *data_buf;
+
+ size_t logbufsize = SZ_4K;
+ size_t databufsize = SZ_4K;
+
+ if (!dbgfs_data.chip) {
+ pr_err("Not initialized data\n");
+ return -EINVAL;
+ }
+
+ /* Per file "transaction" data */
+ trans = kzalloc(sizeof(*trans), GFP_KERNEL);
+ if (!trans)
+ return -ENOMEM;
+
+ /* Allocate log buffer */
+ log = kzalloc(logbufsize, GFP_KERNEL);
+
+ if (!log) {
+ kfree(trans);
+ pr_err("Unable to allocate memory for log buffer\n");
+ return -ENOMEM;
+ }
+
+ log->rpos = 0;
+ log->wpos = 0;
+ log->len = logbufsize - sizeof(*log);
+
+ /* Allocate data buffer */
+ data_buf = kzalloc(databufsize, GFP_KERNEL);
+
+ if (!data_buf) {
+ kfree(trans);
+ kfree(log);
+ pr_err("Unable to allocate memory for data buffer\n");
+ return -ENOMEM;
+ }
+
+ trans->log = log;
+ trans->data = data_buf;
+ trans->cnt = dbgfs_data.cnt;
+ trans->addr = dbgfs_data.addr;
+ trans->chip = dbgfs_data.chip;
+ trans->offset = trans->addr;
+
+ file->private_data = trans;
+ return 0;
+}
+
+static int fg_memif_dfs_close(struct inode *inode, struct file *file)
+{
+ struct fg_trans *trans = file->private_data;
+
+ if (trans && trans->log && trans->data) {
+ file->private_data = NULL;
+ kfree(trans->log);
+ kfree(trans->data);
+ kfree(trans);
+ }
+
+ return 0;
+}
+
+/**
+ * print_to_log: format a string and place into the log buffer
+ * @log: The log buffer to place the result into.
+ * @fmt: The format string to use.
+ * @...: The arguments for the format string.
+ *
+ * The return value is the number of characters written to @log buffer
+ * not including the trailing '\0'.
+ */
+static int print_to_log(struct fg_log_buffer *log, const char *fmt, ...)
+{
+ va_list args;
+ int cnt;
+ char *buf = &log->data[log->wpos];
+ size_t size = log->len - log->wpos;
+
+ va_start(args, fmt);
+ cnt = vscnprintf(buf, size, fmt, args);
+ va_end(args);
+
+ log->wpos += cnt;
+ return cnt;
+}
+
+/**
+ * write_next_line_to_log: Writes a single "line" of data into the log buffer
+ * @trans: Pointer to SRAM transaction data.
+ * @offset: SRAM address offset to start reading from.
+ * @pcnt: Pointer to 'cnt' variable. Indicates the number of bytes to read.
+ *
+ * The 'offset' is a 12-bit SRAM address.
+ *
+ * On a successful read, the pcnt is decremented by the number of data
+ * bytes read from the SRAM. When the cnt reaches 0, all requested bytes have
+ * been read.
+ */
+static int
+write_next_line_to_log(struct fg_trans *trans, int offset, size_t *pcnt)
+{
+ int i, j;
+ u8 data[ITEMS_PER_LINE];
+ struct fg_log_buffer *log = trans->log;
+
+ int cnt = 0;
+ int padding = offset % ITEMS_PER_LINE;
+ int items_to_read = min(ARRAY_SIZE(data) - padding, *pcnt);
+ int items_to_log = min(ITEMS_PER_LINE, padding + items_to_read);
+
+ /* Buffer needs enough space for an entire line */
+ if ((log->len - log->wpos) < MAX_LINE_LENGTH)
+ goto done;
+
+ memcpy(data, trans->data + (offset - trans->addr), items_to_read);
+
+ *pcnt -= items_to_read;
+
+ /* Each line starts with the aligned offset (12-bit address) */
+ cnt = print_to_log(log, "%3.3X ", offset & 0xfff);
+ if (cnt == 0)
+ goto done;
+
+ /* If the offset is unaligned, add padding to right justify items */
+ for (i = 0; i < padding; ++i) {
+ cnt = print_to_log(log, "-- ");
+ if (cnt == 0)
+ goto done;
+ }
+
+ /* Log the data items */
+ for (j = 0; i < items_to_log; ++i, ++j) {
+ cnt = print_to_log(log, "%2.2X ", data[j]);
+ if (cnt == 0)
+ goto done;
+ }
+
+ /* If the last character was a space, then replace it with a newline */
+ if (log->wpos > 0 && log->data[log->wpos - 1] == ' ')
+ log->data[log->wpos - 1] = '\n';
+
+done:
+ return cnt;
+}
+
+/**
+ * get_log_data - reads data from SRAM and saves to the log buffer
+ * @trans: Pointer to SRAM transaction data.
+ *
+ * Returns the number of "items" read or SPMI error code for read failures.
+ */
+static int get_log_data(struct fg_trans *trans)
+{
+ int cnt, rc;
+ int last_cnt;
+ int items_read;
+ int total_items_read = 0;
+ u32 offset = trans->offset;
+ size_t item_cnt = trans->cnt;
+ struct fg_log_buffer *log = trans->log;
+
+ if (item_cnt == 0)
+ return 0;
+
+ if (item_cnt > SZ_4K) {
+ pr_err("Reading too many bytes\n");
+ return -EINVAL;
+ }
+
+ rc = fg_mem_read(trans->chip, trans->data,
+ trans->addr, trans->cnt, 0, 0);
+ if (rc) {
+ pr_err("dump failed: rc = %d\n", rc);
+ return rc;
+ }
+ /* Reset the log buffer 'pointers' */
+ log->wpos = log->rpos = 0;
+
+ /* Keep reading data until the log is full */
+ do {
+ last_cnt = item_cnt;
+ cnt = write_next_line_to_log(trans, offset, &item_cnt);
+ items_read = last_cnt - item_cnt;
+ offset += items_read;
+ total_items_read += items_read;
+ } while (cnt && item_cnt > 0);
+
+ /* Adjust the transaction offset and count */
+ trans->cnt = item_cnt;
+ trans->offset += total_items_read;
+
+ return total_items_read;
+}
+
+/**
+ * fg_memif_dfs_reg_read: reads value(s) from SRAM and fills user's buffer a
+ * byte array (coded as string)
+ * @file: file pointer
+ * @buf: where to put the result
+ * @count: maximum space available in @buf
+ * @ppos: starting position
+ * @return number of user bytes read, or negative error value
+ */
+static ssize_t fg_memif_dfs_reg_read(struct file *file, char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ struct fg_trans *trans = file->private_data;
+ struct fg_log_buffer *log = trans->log;
+ size_t ret;
+ size_t len;
+
+ /* Is the the log buffer empty */
+ if (log->rpos >= log->wpos) {
+ if (get_log_data(trans) <= 0)
+ return 0;
+ }
+
+ len = min(count, log->wpos - log->rpos);
+
+ ret = copy_to_user(buf, &log->data[log->rpos], len);
+ if (ret == len) {
+ pr_err("error copy sram register values to user\n");
+ return -EFAULT;
+ }
+
+ /* 'ret' is the number of bytes not copied */
+ len -= ret;
+
+ *ppos += len;
+ log->rpos += len;
+ return len;
+}
+
+/**
+ * fg_memif_dfs_reg_write: write user's byte array (coded as string) to SRAM.
+ * @file: file pointer
+ * @buf: user data to be written.
+ * @count: maximum space available in @buf
+ * @ppos: starting position
+ * @return number of user byte written, or negative error value
+ */
+static ssize_t fg_memif_dfs_reg_write(struct file *file, const char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ int bytes_read;
+ int data;
+ int pos = 0;
+ int cnt = 0;
+ u8 *values;
+ size_t ret = 0;
+
+ struct fg_trans *trans = file->private_data;
+ u32 offset = trans->offset;
+
+ /* Make a copy of the user data */
+ char *kbuf = kmalloc(count + 1, GFP_KERNEL);
+
+ if (!kbuf)
+ return -ENOMEM;
+
+ ret = copy_from_user(kbuf, buf, count);
+ if (ret == count) {
+ pr_err("failed to copy data from user\n");
+ ret = -EFAULT;
+ goto free_buf;
+ }
+
+ count -= ret;
+ *ppos += count;
+ kbuf[count] = '\0';
+
+ /* Override the text buffer with the raw data */
+ values = kbuf;
+
+ /* Parse the data in the buffer. It should be a string of numbers */
+ while ((pos < count) &&
+ sscanf(kbuf + pos, "%i%n", &data, &bytes_read) == 1) {
+ /*
+ * We shouldn't be receiving a string of characters that
+ * exceeds a size of 5 to keep this functionally correct.
+ * Also, we should make sure that pos never gets overflowed
+ * beyond the limit.
+ */
+ if (bytes_read > 5 || bytes_read > INT_MAX - pos) {
+ cnt = 0;
+ ret = -EINVAL;
+ break;
+ }
+ pos += bytes_read;
+ values[cnt++] = data & 0xff;
+ }
+
+ if (!cnt)
+ goto free_buf;
+
+ pr_info("address %x, count %d\n", offset, cnt);
+ /* Perform the write(s) */
+
+ ret = fg_mem_write(trans->chip, values, offset,
+ cnt, 0, 0);
+ if (ret) {
+ pr_err("SPMI write failed, err = %zu\n", ret);
+ } else {
+ ret = count;
+ trans->offset += cnt > 4 ? 4 : cnt;
+ }
+
+free_buf:
+ kfree(kbuf);
+ return ret;
+}
+
+static const struct file_operations fg_memif_dfs_reg_fops = {
+ .open = fg_memif_data_open,
+ .release = fg_memif_dfs_close,
+ .read = fg_memif_dfs_reg_read,
+ .write = fg_memif_dfs_reg_write,
+};
+
+/**
+ * fg_dfs_create_fs: create debugfs file system.
+ * @return pointer to root directory or NULL if failed to create fs
+ */
+static struct dentry *fg_dfs_create_fs(void)
+{
+ struct dentry *root, *file;
+
+ pr_debug("Creating FG_MEM debugfs file-system\n");
+ root = debugfs_create_dir(DFS_ROOT_NAME, NULL);
+ if (IS_ERR_OR_NULL(root)) {
+ pr_err("Error creating top level directory err:%ld",
+ (long)root);
+ if (PTR_ERR(root) == -ENODEV)
+ pr_err("debugfs is not enabled in the kernel");
+ return NULL;
+ }
+
+ dbgfs_data.help_msg.size = strlen(dbgfs_data.help_msg.data);
+
+ file = debugfs_create_blob("help", 0444, root, &dbgfs_data.help_msg);
+ if (!file) {
+ pr_err("error creating help entry\n");
+ goto err_remove_fs;
+ }
+ return root;
+
+err_remove_fs:
+ debugfs_remove_recursive(root);
+ return NULL;
+}
+
+/**
+ * fg_dfs_get_root: return a pointer to FG debugfs root directory.
+ * @return a pointer to the existing directory, or if no root
+ * directory exists then create one. Directory is created with file that
+ * configures SRAM transaction, namely: address, and count.
+ * @returns valid pointer on success or NULL
+ */
+struct dentry *fg_dfs_get_root(void)
+{
+ if (dbgfs_data.root)
+ return dbgfs_data.root;
+
+ if (mutex_lock_interruptible(&dbgfs_data.lock) < 0)
+ return NULL;
+ /* critical section */
+ if (!dbgfs_data.root) { /* double checking idiom */
+ dbgfs_data.root = fg_dfs_create_fs();
+ }
+ mutex_unlock(&dbgfs_data.lock);
+ return dbgfs_data.root;
+}
+
+/*
+ * fg_dfs_create: adds new fg_mem if debugfs entry
+ * @return zero on success
+ */
+int fg_dfs_create(struct fg_chip *chip)
+{
+ struct dentry *root;
+ struct dentry *file;
+
+ root = fg_dfs_get_root();
+ if (!root)
+ return -ENOENT;
+
+ dbgfs_data.chip = chip;
+
+ file = debugfs_create_u32("count", DFS_MODE, root, &(dbgfs_data.cnt));
+ if (!file) {
+ pr_err("error creating 'count' entry\n");
+ goto err_remove_fs;
+ }
+
+ file = debugfs_create_x32("address", DFS_MODE,
+ root, &(dbgfs_data.addr));
+ if (!file) {
+ pr_err("error creating 'address' entry\n");
+ goto err_remove_fs;
+ }
+
+ file = debugfs_create_file("data", DFS_MODE, root, &dbgfs_data,
+ &fg_memif_dfs_reg_fops);
+ if (!file) {
+ pr_err("error creating 'data' entry\n");
+ goto err_remove_fs;
+ }
+
+ return 0;
+
+err_remove_fs:
+ debugfs_remove_recursive(root);
+ return -ENOMEM;
+}
+
+#define EXTERNAL_SENSE_OFFSET_REG 0x41C
+#define EXT_OFFSET_TRIM_REG 0xF8
+#define SEC_ACCESS_REG 0xD0
+#define SEC_ACCESS_UNLOCK 0xA5
+#define BCL_TRIM_REV_FIXED 12
+static int bcl_trim_workaround(struct fg_chip *chip)
+{
+ u8 reg, rc;
+
+ if (chip->tp_rev_addr == 0)
+ return 0;
+
+ rc = fg_read(chip, ®, chip->tp_rev_addr, 1);
+ if (rc) {
+ pr_err("Failed to read tp reg, rc = %d\n", rc);
+ return rc;
+ }
+ if (reg >= BCL_TRIM_REV_FIXED) {
+ if (fg_debug_mask & FG_STATUS)
+ pr_info("workaround not applied, tp_rev = %d\n", reg);
+ return 0;
+ }
+
+ rc = fg_mem_read(chip, ®, EXTERNAL_SENSE_OFFSET_REG, 1, 2, 0);
+ if (rc) {
+ pr_err("Failed to read ext sense offset trim, rc = %d\n", rc);
+ return rc;
+ }
+ rc = fg_masked_write(chip, chip->soc_base + SEC_ACCESS_REG,
+ SEC_ACCESS_UNLOCK, SEC_ACCESS_UNLOCK, 1);
+
+ rc |= fg_masked_write(chip, chip->soc_base + EXT_OFFSET_TRIM_REG,
+ 0xFF, reg, 1);
+ if (rc) {
+ pr_err("Failed to write ext sense offset trim, rc = %d\n", rc);
+ return rc;
+ }
+
+ return 0;
+}
+
+#define FG_ALG_SYSCTL_1 0x4B0
+#define SOC_CNFG 0x450
+#define SOC_DELTA_OFFSET 3
+#define DELTA_SOC_PERCENT 1
+#define I_TERM_QUAL_BIT BIT(1)
+#define PATCH_NEG_CURRENT_BIT BIT(3)
+#define KI_COEFF_PRED_FULL_ADDR 0x408
+#define KI_COEFF_PRED_FULL_4_0_MSB 0x88
+#define KI_COEFF_PRED_FULL_4_0_LSB 0x00
+#define TEMP_FRAC_SHIFT_REG 0x4A4
+#define FG_ADC_CONFIG_REG 0x4B8
+#define FG_BCL_CONFIG_OFFSET 0x3
+#define BCL_FORCED_HPM_IN_CHARGE BIT(2)
+static int fg_common_hw_init(struct fg_chip *chip)
+{
+ int rc;
+ int resume_soc_raw;
+ u8 val;
+
+ update_iterm(chip);
+ update_cutoff_voltage(chip);
+ update_irq_volt_empty(chip);
+ update_bcl_thresholds(chip);
+
+ resume_soc_raw = settings[FG_MEM_RESUME_SOC].value;
+ if (resume_soc_raw > 0) {
+ rc = fg_set_resume_soc(chip, resume_soc_raw);
+ if (rc) {
+ pr_err("Couldn't set resume SOC for FG\n");
+ return rc;
+ }
+ } else {
+ pr_info("FG auto recharge threshold not specified in DT\n");
+ }
+
+ if (fg_sense_type >= 0) {
+ rc = set_prop_sense_type(chip, fg_sense_type);
+ if (rc) {
+ pr_err("failed to config sense type %d rc=%d\n",
+ fg_sense_type, rc);
+ return rc;
+ }
+ }
+
+ rc = fg_mem_masked_write(chip, settings[FG_MEM_DELTA_SOC].address, 0xFF,
+ soc_to_setpoint(settings[FG_MEM_DELTA_SOC].value),
+ settings[FG_MEM_DELTA_SOC].offset);
+ if (rc) {
+ pr_err("failed to write delta soc rc=%d\n", rc);
+ return rc;
+ }
+
+ rc = fg_mem_masked_write(chip, settings[FG_MEM_BATT_LOW].address, 0xFF,
+ batt_to_setpoint_8b(settings[FG_MEM_BATT_LOW].value),
+ settings[FG_MEM_BATT_LOW].offset);
+ if (rc) {
+ pr_err("failed to write Vbatt_low rc=%d\n", rc);
+ return rc;
+ }
+
+ rc = fg_mem_masked_write(chip, settings[FG_MEM_THERM_DELAY].address,
+ THERM_DELAY_MASK,
+ therm_delay_to_setpoint(settings[FG_MEM_THERM_DELAY].value),
+ settings[FG_MEM_THERM_DELAY].offset);
+ if (rc) {
+ pr_err("failed to write therm_delay rc=%d\n", rc);
+ return rc;
+ }
+
+ if (chip->use_thermal_coefficients) {
+ fg_mem_write(chip, chip->thermal_coefficients,
+ THERMAL_COEFF_ADDR, THERMAL_COEFF_N_BYTES,
+ THERMAL_COEFF_OFFSET, 0);
+ }
+
+ if (!chip->sw_rbias_ctrl) {
+ rc = fg_mem_masked_write(chip, EXTERNAL_SENSE_SELECT,
+ BATT_TEMP_CNTRL_MASK,
+ TEMP_SENSE_ALWAYS_BIT,
+ BATT_TEMP_OFFSET);
+ if (rc) {
+ pr_err("failed to write BATT_TEMP_OFFSET rc=%d\n", rc);
+ return rc;
+ }
+ }
+
+ /* Read the cycle counter back from FG SRAM */
+ if (chip->cyc_ctr.en)
+ restore_cycle_counter(chip);
+
+ if (chip->esr_pulse_tune_en) {
+ rc = fg_mem_read(chip, &val, SYS_CFG_1_REG, 1, SYS_CFG_1_OFFSET,
+ 0);
+ if (rc) {
+ pr_err("unable to read sys_cfg_1: %d\n", rc);
+ return rc;
+ }
+
+ if (!(val & ENABLE_ESR_PULSE_VAL))
+ chip->esr_extract_disabled = true;
+
+ if (fg_debug_mask & FG_STATUS)
+ pr_info("ESR extract is %sabled\n",
+ chip->esr_extract_disabled ? "dis" : "en");
+
+ rc = fg_mem_read(chip, &val, CBITS_INPUT_FILTER_REG, 1,
+ CBITS_RMEAS1_OFFSET, 0);
+ if (rc) {
+ pr_err("unable to read cbits_input_filter_reg: %d\n",
+ rc);
+ return rc;
+ }
+
+ if (val & (IMPTR_FAST_TIME_SHIFT | IMPTR_LONG_TIME_SHIFT))
+ chip->imptr_pulse_slow_en = true;
+
+ if (fg_debug_mask & FG_STATUS)
+ pr_info("imptr_pulse_slow is %sabled\n",
+ chip->imptr_pulse_slow_en ? "en" : "dis");
+
+ rc = fg_mem_read(chip, &val, RSLOW_CFG_REG, 1, RSLOW_CFG_OFFSET,
+ 0);
+ if (rc) {
+ pr_err("unable to read rslow cfg: %d\n", rc);
+ return rc;
+ }
+
+ if (val & RSLOW_CFG_ON_VAL)
+ chip->rslow_comp.active = true;
+
+ if (fg_debug_mask & FG_STATUS)
+ pr_info("rslow_comp active is %sabled\n",
+ chip->rslow_comp.active ? "en" : "dis");
+ }
+
+ return 0;
+}
+
+static int fg_8994_hw_init(struct fg_chip *chip)
+{
+ int rc = 0;
+ u8 data[4];
+ u64 esr_value;
+
+ rc = fg_mem_masked_write(chip, EXTERNAL_SENSE_SELECT,
+ PATCH_NEG_CURRENT_BIT,
+ PATCH_NEG_CURRENT_BIT,
+ EXTERNAL_SENSE_OFFSET);
+ if (rc) {
+ pr_err("failed to write patch current bit rc=%d\n", rc);
+ return rc;
+ }
+
+ rc = bcl_trim_workaround(chip);
+ if (rc) {
+ pr_err("failed to redo bcl trim rc=%d\n", rc);
+ return rc;
+ }
+
+ rc = fg_mem_masked_write(chip, FG_ADC_CONFIG_REG,
+ BCL_FORCED_HPM_IN_CHARGE,
+ BCL_FORCED_HPM_IN_CHARGE,
+ FG_BCL_CONFIG_OFFSET);
+ if (rc) {
+ pr_err("failed to force hpm in charge rc=%d\n", rc);
+ return rc;
+ }
+
+ fg_mem_masked_write(chip, FG_ALG_SYSCTL_1, I_TERM_QUAL_BIT, 0, 0);
+
+ data[0] = 0xA2;
+ data[1] = 0x12;
+
+ rc = fg_mem_write(chip, data, TEMP_FRAC_SHIFT_REG, 2, 2, 0);
+ if (rc) {
+ pr_err("failed to write temp ocv constants rc=%d\n", rc);
+ return rc;
+ }
+
+ data[0] = KI_COEFF_PRED_FULL_4_0_LSB;
+ data[1] = KI_COEFF_PRED_FULL_4_0_MSB;
+ fg_mem_write(chip, data, KI_COEFF_PRED_FULL_ADDR, 2, 2, 0);
+
+ esr_value = ESR_DEFAULT_VALUE;
+ rc = fg_mem_write(chip, (u8 *)&esr_value, MAXRSCHANGE_REG, 8,
+ ESR_VALUE_OFFSET, 0);
+ if (rc)
+ pr_err("failed to write default ESR value rc=%d\n", rc);
+ else
+ pr_debug("set default value to esr filter\n");
+
+ return 0;
+}
+
+#define FG_USBID_CONFIG_OFFSET 0x2
+#define DISABLE_USBID_DETECT_BIT BIT(0)
+static int fg_8996_hw_init(struct fg_chip *chip)
+{
+ int rc;
+
+ rc = fg_mem_masked_write(chip, FG_ADC_CONFIG_REG,
+ BCL_FORCED_HPM_IN_CHARGE,
+ BCL_FORCED_HPM_IN_CHARGE,
+ FG_BCL_CONFIG_OFFSET);
+ if (rc) {
+ pr_err("failed to force hpm in charge rc=%d\n", rc);
+ return rc;
+ }
+
+ /* enable usbid conversions for PMi8996 V1.0 */
+ if (chip->pmic_revision[REVID_DIG_MAJOR] == 1
+ && chip->pmic_revision[REVID_ANA_MAJOR] == 0) {
+ rc = fg_mem_masked_write(chip, FG_ADC_CONFIG_REG,
+ DISABLE_USBID_DETECT_BIT,
+ 0, FG_USBID_CONFIG_OFFSET);
+ if (rc) {
+ pr_err("failed to enable usbid conversions: %d\n", rc);
+ return rc;
+ }
+ }
+
+ return rc;
+}
+
+static int fg_8950_hw_init(struct fg_chip *chip)
+{
+ int rc;
+
+ rc = fg_mem_masked_write(chip, FG_ADC_CONFIG_REG,
+ BCL_FORCED_HPM_IN_CHARGE,
+ BCL_FORCED_HPM_IN_CHARGE,
+ FG_BCL_CONFIG_OFFSET);
+ if (rc)
+ pr_err("failed to force hpm in charge rc=%d\n", rc);
+
+ return rc;
+}
+
+static int fg_hw_init(struct fg_chip *chip)
+{
+ int rc = 0;
+
+ rc = fg_common_hw_init(chip);
+ if (rc) {
+ pr_err("Unable to initialize FG HW rc=%d\n", rc);
+ return rc;
+ }
+
+ /* add PMIC specific hw init */
+ switch (chip->pmic_subtype) {
+ case PMI8994:
+ rc = fg_8994_hw_init(chip);
+ chip->wa_flag |= PULSE_REQUEST_WA;
+ break;
+ case PMI8996:
+ rc = fg_8996_hw_init(chip);
+ /* Setup workaround flag based on PMIC type */
+ if (fg_sense_type == INTERNAL_CURRENT_SENSE)
+ chip->wa_flag |= IADC_GAIN_COMP_WA;
+ if (chip->pmic_revision[REVID_DIG_MAJOR] > 1)
+ chip->wa_flag |= USE_CC_SOC_REG;
+
+ break;
+ case PMI8950:
+ case PMI8937:
+ rc = fg_8950_hw_init(chip);
+ /* Setup workaround flag based on PMIC type */
+ chip->wa_flag |= BCL_HI_POWER_FOR_CHGLED_WA;
+ if (fg_sense_type == INTERNAL_CURRENT_SENSE)
+ chip->wa_flag |= IADC_GAIN_COMP_WA;
+ if (chip->pmic_revision[REVID_DIG_MAJOR] > 1)
+ chip->wa_flag |= USE_CC_SOC_REG;
+
+ break;
+ }
+ if (rc)
+ pr_err("Unable to initialize PMIC specific FG HW rc=%d\n", rc);
+
+ pr_debug("wa_flag=0x%x\n", chip->wa_flag);
+
+ return rc;
+}
+
+#define DIG_MINOR 0x0
+#define DIG_MAJOR 0x1
+#define ANA_MINOR 0x2
+#define ANA_MAJOR 0x3
+#define IACS_INTR_SRC_SLCT BIT(3)
+static int fg_setup_memif_offset(struct fg_chip *chip)
+{
+ int rc;
+
+ rc = fg_read(chip, chip->revision, chip->mem_base + DIG_MINOR, 4);
+ if (rc) {
+ pr_err("Unable to read FG revision rc=%d\n", rc);
+ return rc;
+ }
+
+ switch (chip->revision[DIG_MAJOR]) {
+ case DIG_REV_1:
+ case DIG_REV_2:
+ chip->offset = offset[0].address;
+ break;
+ case DIG_REV_3:
+ chip->offset = offset[1].address;
+ chip->ima_supported = true;
+ break;
+ default:
+ pr_err("Digital Major rev=%d not supported\n",
+ chip->revision[DIG_MAJOR]);
+ return -EINVAL;
+ }
+
+ if (chip->ima_supported) {
+ /*
+ * Change the FG_MEM_INT interrupt to track IACS_READY
+ * condition instead of end-of-transaction. This makes sure
+ * that the next transaction starts only after the hw is ready.
+ */
+ rc = fg_masked_write(chip,
+ chip->mem_base + MEM_INTF_IMA_CFG, IACS_INTR_SRC_SLCT,
+ IACS_INTR_SRC_SLCT, 1);
+ if (rc) {
+ pr_err("failed to configure interrupt source %d\n", rc);
+ return rc;
+ }
+ }
+
+ return 0;
+}
+
+static int fg_detect_pmic_type(struct fg_chip *chip)
+{
+ struct pmic_revid_data *pmic_rev_id;
+ struct device_node *revid_dev_node;
+
+ revid_dev_node = of_parse_phandle(chip->pdev->dev.of_node,
+ "qcom,pmic-revid", 0);
+ if (!revid_dev_node) {
+ pr_err("Missing qcom,pmic-revid property - driver failed\n");
+ return -EINVAL;
+ }
+
+ pmic_rev_id = get_revid_data(revid_dev_node);
+ if (IS_ERR_OR_NULL(pmic_rev_id)) {
+ pr_err("Unable to get pmic_revid rc=%ld\n",
+ PTR_ERR(pmic_rev_id));
+ /*
+ * the revid peripheral must be registered, any failure
+ * here only indicates that the rev-id module has not
+ * probed yet.
+ */
+ return -EPROBE_DEFER;
+ }
+
+ switch (pmic_rev_id->pmic_subtype) {
+ case PMI8994:
+ case PMI8950:
+ case PMI8937:
+ case PMI8996:
+ chip->pmic_subtype = pmic_rev_id->pmic_subtype;
+ chip->pmic_revision[REVID_RESERVED] = pmic_rev_id->rev1;
+ chip->pmic_revision[REVID_VARIANT] = pmic_rev_id->rev2;
+ chip->pmic_revision[REVID_ANA_MAJOR] = pmic_rev_id->rev3;
+ chip->pmic_revision[REVID_DIG_MAJOR] = pmic_rev_id->rev4;
+ break;
+ default:
+ pr_err("PMIC subtype %d not supported\n",
+ pmic_rev_id->pmic_subtype);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+#define INIT_JEITA_DELAY_MS 1000
+
+static void delayed_init_work(struct work_struct *work)
+{
+ u8 reg[2];
+ int rc;
+ struct fg_chip *chip = container_of(work,
+ struct fg_chip,
+ init_work);
+
+ /* hold memory access until initialization finishes */
+ fg_mem_lock(chip);
+
+ rc = fg_hw_init(chip);
+ if (rc) {
+ pr_err("failed to hw init rc = %d\n", rc);
+ fg_mem_release(chip);
+ fg_cleanup(chip);
+ return;
+ }
+ /* release memory access before update_sram_data is called */
+ fg_mem_release(chip);
+
+ schedule_delayed_work(
+ &chip->update_jeita_setting,
+ msecs_to_jiffies(INIT_JEITA_DELAY_MS));
+
+ if (chip->last_sram_update_time == 0)
+ update_sram_data_work(&chip->update_sram_data.work);
+
+ if (chip->last_temp_update_time == 0)
+ update_temp_data(&chip->update_temp_work.work);
+
+ if (!chip->use_otp_profile)
+ schedule_delayed_work(&chip->batt_profile_init, 0);
+
+ if (chip->wa_flag & IADC_GAIN_COMP_WA) {
+ /* read default gain config */
+ rc = fg_mem_read(chip, reg, K_VCOR_REG, 2, DEF_GAIN_OFFSET, 0);
+ if (rc) {
+ pr_err("Failed to read default gain rc=%d\n", rc);
+ goto done;
+ }
+
+ if (reg[1] || reg[0]) {
+ /*
+ * Default gain register has valid value:
+ * - write to gain register.
+ */
+ rc = fg_mem_write(chip, reg, GAIN_REG, 2,
+ GAIN_OFFSET, 0);
+ if (rc) {
+ pr_err("Failed to write gain rc=%d\n", rc);
+ goto done;
+ }
+ } else {
+ /*
+ * Default gain register is invalid:
+ * - read gain register for default gain value
+ * - write to default gain register.
+ */
+ rc = fg_mem_read(chip, reg, GAIN_REG, 2,
+ GAIN_OFFSET, 0);
+ if (rc) {
+ pr_err("Failed to read gain rc=%d\n", rc);
+ goto done;
+ }
+ rc = fg_mem_write(chip, reg, K_VCOR_REG, 2,
+ DEF_GAIN_OFFSET, 0);
+ if (rc) {
+ pr_err("Failed to write default gain rc=%d\n",
+ rc);
+ goto done;
+ }
+ }
+
+ chip->iadc_comp_data.dfl_gain_reg[0] = reg[0];
+ chip->iadc_comp_data.dfl_gain_reg[1] = reg[1];
+ chip->iadc_comp_data.dfl_gain = half_float(reg);
+ chip->input_present = is_input_present(chip);
+ chip->otg_present = is_otg_present(chip);
+ chip->init_done = true;
+
+ pr_debug("IADC gain initial config reg_val 0x%x%x gain %lld\n",
+ reg[1], reg[0], chip->iadc_comp_data.dfl_gain);
+ }
+
+ pr_debug("FG: HW_init success\n");
+
+ return;
+done:
+ fg_cleanup(chip);
+}
+
+static int fg_probe(struct platform_device *pdev)
+{
+ struct device *dev = &(pdev->dev);
+ struct fg_chip *chip;
+ struct device_node *child;
+ unsigned int base;
+ u8 subtype, reg;
+ int rc = 0;
+ struct power_supply_config bms_psy_cfg;
+
+ if (!pdev) {
+ pr_err("no valid spmi pointer\n");
+ return -ENODEV;
+ }
+
+ if (!pdev->dev.of_node) {
+ pr_err("device node missing\n");
+ return -ENODEV;
+ }
+
+ chip = devm_kzalloc(dev, sizeof(struct fg_chip), GFP_KERNEL);
+ if (chip == NULL)
+ return -ENOMEM;
+
+ chip->regmap = dev_get_regmap(pdev->dev.parent, NULL);
+ if (!chip->regmap) {
+ dev_err(&pdev->dev, "Couldn't get parent's regmap\n");
+ return -EINVAL;
+ }
+
+ chip->pdev = pdev;
+ chip->dev = &(pdev->dev);
+
+ wakeup_source_init(&chip->empty_check_wakeup_source.source,
+ "qpnp_fg_empty_check");
+ wakeup_source_init(&chip->memif_wakeup_source.source,
+ "qpnp_fg_memaccess");
+ wakeup_source_init(&chip->profile_wakeup_source.source,
+ "qpnp_fg_profile");
+ wakeup_source_init(&chip->update_temp_wakeup_source.source,
+ "qpnp_fg_update_temp");
+ wakeup_source_init(&chip->update_sram_wakeup_source.source,
+ "qpnp_fg_update_sram");
+ wakeup_source_init(&chip->resume_soc_wakeup_source.source,
+ "qpnp_fg_set_resume_soc");
+ wakeup_source_init(&chip->gain_comp_wakeup_source.source,
+ "qpnp_fg_gain_comp");
+ wakeup_source_init(&chip->capacity_learning_wakeup_source.source,
+ "qpnp_fg_cap_learning");
+ wakeup_source_init(&chip->esr_extract_wakeup_source.source,
+ "qpnp_fg_esr_extract");
+ mutex_init(&chip->rw_lock);
+ mutex_init(&chip->cyc_ctr.lock);
+ mutex_init(&chip->learning_data.learning_lock);
+ mutex_init(&chip->rslow_comp.lock);
+ mutex_init(&chip->sysfs_restart_lock);
+ INIT_DELAYED_WORK(&chip->update_jeita_setting, update_jeita_setting);
+ INIT_DELAYED_WORK(&chip->update_sram_data, update_sram_data_work);
+ INIT_DELAYED_WORK(&chip->update_temp_work, update_temp_data);
+ INIT_DELAYED_WORK(&chip->check_empty_work, check_empty_work);
+ INIT_DELAYED_WORK(&chip->batt_profile_init, batt_profile_init);
+ INIT_WORK(&chip->rslow_comp_work, rslow_comp_work);
+ INIT_WORK(&chip->fg_cap_learning_work, fg_cap_learning_work);
+ INIT_WORK(&chip->dump_sram, dump_sram);
+ INIT_WORK(&chip->status_change_work, status_change_work);
+ INIT_WORK(&chip->cycle_count_work, update_cycle_count);
+ INIT_WORK(&chip->battery_age_work, battery_age_work);
+ INIT_WORK(&chip->update_esr_work, update_esr_value);
+ INIT_WORK(&chip->set_resume_soc_work, set_resume_soc_work);
+ INIT_WORK(&chip->sysfs_restart_work, sysfs_restart_work);
+ INIT_WORK(&chip->init_work, delayed_init_work);
+ INIT_WORK(&chip->charge_full_work, charge_full_work);
+ INIT_WORK(&chip->gain_comp_work, iadc_gain_comp_work);
+ INIT_WORK(&chip->bcl_hi_power_work, bcl_hi_power_work);
+ INIT_WORK(&chip->esr_extract_config_work, esr_extract_config_work);
+ alarm_init(&chip->fg_cap_learning_alarm, ALARM_BOOTTIME,
+ fg_cap_learning_alarm_cb);
+ init_completion(&chip->sram_access_granted);
+ init_completion(&chip->sram_access_revoked);
+ complete_all(&chip->sram_access_revoked);
+ init_completion(&chip->batt_id_avail);
+ init_completion(&chip->first_soc_done);
+ dev_set_drvdata(&pdev->dev, chip);
+
+ if (of_get_available_child_count(pdev->dev.of_node) == 0) {
+ pr_err("no child nodes\n");
+ rc = -ENXIO;
+ goto of_init_fail;
+ }
+
+ for_each_available_child_of_node(pdev->dev.of_node, child) {
+ rc = of_property_read_u32(child, "reg", &base);
+ if (rc < 0) {
+ dev_err(&pdev->dev,
+ "Couldn't find reg in node = %s rc = %d\n",
+ child->full_name, rc);
+ goto of_init_fail;
+ }
+
+ if (strcmp("qcom,fg-adc-vbat", child->name) == 0) {
+ chip->vbat_adc_addr = base;
+ continue;
+ } else if (strcmp("qcom,fg-adc-ibat", child->name) == 0) {
+ chip->ibat_adc_addr = base;
+ continue;
+ } else if (strcmp("qcom,revid-tp-rev", child->name) == 0) {
+ chip->tp_rev_addr = base;
+ continue;
+ }
+
+ rc = fg_read(chip, &subtype,
+ base + REG_OFFSET_PERP_SUBTYPE, 1);
+ if (rc) {
+ pr_err("Peripheral subtype read failed rc=%d\n", rc);
+ goto of_init_fail;
+ }
+
+ switch (subtype) {
+ case FG_SOC:
+ chip->soc_base = base;
+ break;
+ case FG_MEMIF:
+ chip->mem_base = base;
+ break;
+ case FG_BATT:
+ chip->batt_base = base;
+ break;
+ default:
+ pr_err("Invalid peripheral subtype=0x%x\n", subtype);
+ rc = -EINVAL;
+ }
+ }
+
+ rc = fg_detect_pmic_type(chip);
+ if (rc) {
+ pr_err("Unable to detect PMIC type rc=%d\n", rc);
+ return rc;
+ }
+
+ rc = fg_setup_memif_offset(chip);
+ if (rc) {
+ pr_err("Unable to setup mem_if offsets rc=%d\n", rc);
+ goto of_init_fail;
+ }
+
+ rc = fg_of_init(chip);
+ if (rc) {
+ pr_err("failed to parse devicetree rc%d\n", rc);
+ goto of_init_fail;
+ }
+
+ if (chip->jeita_hysteresis_support) {
+ rc = fg_init_batt_temp_state(chip);
+ if (rc) {
+ pr_err("failed to get battery status rc%d\n", rc);
+ goto of_init_fail;
+ }
+ }
+
+ /* check if the first estimate is already finished at this time */
+ if (is_first_est_done(chip))
+ complete_all(&chip->first_soc_done);
+
+ reg = 0xFF;
+ rc = fg_write(chip, ®, INT_EN_CLR(chip->mem_base), 1);
+ if (rc) {
+ pr_err("failed to clear interrupts %d\n", rc);
+ goto of_init_fail;
+ }
+
+ rc = fg_init_irqs(chip);
+ if (rc) {
+ pr_err("failed to request interrupts %d\n", rc);
+ goto cancel_work;
+ }
+
+ chip->batt_type = default_batt_type;
+
+ chip->bms_psy_d.name = "bms";
+ chip->bms_psy_d.type = POWER_SUPPLY_TYPE_BMS;
+ chip->bms_psy_d.properties = fg_power_props;
+ chip->bms_psy_d.num_properties = ARRAY_SIZE(fg_power_props);
+ chip->bms_psy_d.get_property = fg_power_get_property;
+ chip->bms_psy_d.set_property = fg_power_set_property;
+ chip->bms_psy_d.external_power_changed = fg_external_power_changed;
+ chip->bms_psy_d.property_is_writeable = fg_property_is_writeable;
+
+ bms_psy_cfg.drv_data = chip;
+ bms_psy_cfg.supplied_to = fg_supplicants;
+ bms_psy_cfg.num_supplicants = ARRAY_SIZE(fg_supplicants);
+ bms_psy_cfg.of_node = NULL;
+ chip->bms_psy = devm_power_supply_register(chip->dev,
+ &chip->bms_psy_d,
+ &bms_psy_cfg);
+ if (IS_ERR(chip->bms_psy)) {
+ pr_err("batt failed to register rc = %ld\n",
+ PTR_ERR(chip->bms_psy));
+ goto of_init_fail;
+ }
+ chip->power_supply_registered = true;
+ /*
+ * Just initialize the batt_psy_name here. Power supply
+ * will be obtained later.
+ */
+ chip->batt_psy_name = "battery";
+
+ if (chip->mem_base) {
+ rc = fg_dfs_create(chip);
+ if (rc < 0) {
+ pr_err("failed to create debugfs rc = %d\n", rc);
+ goto cancel_work;
+ }
+ }
+
+ schedule_work(&chip->init_work);
+
+ pr_info("FG Probe success - FG Revision DIG:%d.%d ANA:%d.%d PMIC subtype=%d\n",
+ chip->revision[DIG_MAJOR], chip->revision[DIG_MINOR],
+ chip->revision[ANA_MAJOR], chip->revision[ANA_MINOR],
+ chip->pmic_subtype);
+
+ return rc;
+
+cancel_work:
+ cancel_delayed_work_sync(&chip->update_jeita_setting);
+ cancel_delayed_work_sync(&chip->update_sram_data);
+ cancel_delayed_work_sync(&chip->update_temp_work);
+ cancel_delayed_work_sync(&chip->check_empty_work);
+ cancel_delayed_work_sync(&chip->batt_profile_init);
+ alarm_try_to_cancel(&chip->fg_cap_learning_alarm);
+ cancel_work_sync(&chip->set_resume_soc_work);
+ cancel_work_sync(&chip->fg_cap_learning_work);
+ cancel_work_sync(&chip->dump_sram);
+ cancel_work_sync(&chip->status_change_work);
+ cancel_work_sync(&chip->cycle_count_work);
+ cancel_work_sync(&chip->update_esr_work);
+ cancel_work_sync(&chip->rslow_comp_work);
+ cancel_work_sync(&chip->sysfs_restart_work);
+ cancel_work_sync(&chip->gain_comp_work);
+ cancel_work_sync(&chip->init_work);
+ cancel_work_sync(&chip->charge_full_work);
+ cancel_work_sync(&chip->bcl_hi_power_work);
+ cancel_work_sync(&chip->esr_extract_config_work);
+of_init_fail:
+ mutex_destroy(&chip->rslow_comp.lock);
+ mutex_destroy(&chip->rw_lock);
+ mutex_destroy(&chip->cyc_ctr.lock);
+ mutex_destroy(&chip->learning_data.learning_lock);
+ mutex_destroy(&chip->sysfs_restart_lock);
+ wakeup_source_trash(&chip->resume_soc_wakeup_source.source);
+ wakeup_source_trash(&chip->empty_check_wakeup_source.source);
+ wakeup_source_trash(&chip->memif_wakeup_source.source);
+ wakeup_source_trash(&chip->profile_wakeup_source.source);
+ wakeup_source_trash(&chip->update_temp_wakeup_source.source);
+ wakeup_source_trash(&chip->update_sram_wakeup_source.source);
+ wakeup_source_trash(&chip->gain_comp_wakeup_source.source);
+ wakeup_source_trash(&chip->capacity_learning_wakeup_source.source);
+ wakeup_source_trash(&chip->esr_extract_wakeup_source.source);
+ return rc;
+}
+
+static void check_and_update_sram_data(struct fg_chip *chip)
+{
+ unsigned long current_time = 0, next_update_time, time_left;
+
+ get_current_time(¤t_time);
+
+ next_update_time = chip->last_temp_update_time
+ + (TEMP_PERIOD_UPDATE_MS / 1000);
+
+ if (next_update_time > current_time)
+ time_left = next_update_time - current_time;
+ else
+ time_left = 0;
+
+ schedule_delayed_work(
+ &chip->update_temp_work, msecs_to_jiffies(time_left * 1000));
+
+ next_update_time = chip->last_sram_update_time
+ + (fg_sram_update_period_ms / 1000);
+
+ if (next_update_time > current_time)
+ time_left = next_update_time - current_time;
+ else
+ time_left = 0;
+
+ schedule_delayed_work(
+ &chip->update_sram_data, msecs_to_jiffies(time_left * 1000));
+}
+
+static int fg_suspend(struct device *dev)
+{
+ struct fg_chip *chip = dev_get_drvdata(dev);
+
+ if (!chip->sw_rbias_ctrl)
+ return 0;
+
+ cancel_delayed_work(&chip->update_temp_work);
+ cancel_delayed_work(&chip->update_sram_data);
+
+ return 0;
+}
+
+static int fg_resume(struct device *dev)
+{
+ struct fg_chip *chip = dev_get_drvdata(dev);
+
+ if (!chip->sw_rbias_ctrl)
+ return 0;
+
+ check_and_update_sram_data(chip);
+ return 0;
+}
+
+static const struct dev_pm_ops qpnp_fg_pm_ops = {
+ .suspend = fg_suspend,
+ .resume = fg_resume,
+};
+
+static int fg_sense_type_set(const char *val, const struct kernel_param *kp)
+{
+ int rc;
+ struct power_supply *bms_psy;
+ struct fg_chip *chip;
+ int old_fg_sense_type = fg_sense_type;
+
+ rc = param_set_int(val, kp);
+ if (rc) {
+ pr_err("Unable to set fg_sense_type: %d\n", rc);
+ return rc;
+ }
+
+ if (fg_sense_type != 0 && fg_sense_type != 1) {
+ pr_err("Bad value %d\n", fg_sense_type);
+ fg_sense_type = old_fg_sense_type;
+ return -EINVAL;
+ }
+
+ if (fg_debug_mask & FG_STATUS)
+ pr_info("fg_sense_type set to %d\n", fg_sense_type);
+
+ bms_psy = power_supply_get_by_name("bms");
+ if (!bms_psy) {
+ pr_err("bms psy not found\n");
+ return 0;
+ }
+
+ chip = power_supply_get_drvdata(bms_psy);
+ rc = set_prop_sense_type(chip, fg_sense_type);
+ return rc;
+}
+
+static struct kernel_param_ops fg_sense_type_ops = {
+ .set = fg_sense_type_set,
+ .get = param_get_int,
+};
+
+module_param_cb(sense_type, &fg_sense_type_ops, &fg_sense_type, 0644);
+
+static int fg_restart_set(const char *val, const struct kernel_param *kp)
+{
+ struct power_supply *bms_psy;
+ struct fg_chip *chip;
+
+ bms_psy = power_supply_get_by_name("bms");
+ if (!bms_psy) {
+ pr_err("bms psy not found\n");
+ return 0;
+ }
+ chip = power_supply_get_drvdata(bms_psy);
+
+ mutex_lock(&chip->sysfs_restart_lock);
+ if (fg_restart != 0) {
+ mutex_unlock(&chip->sysfs_restart_lock);
+ return 0;
+ }
+ fg_restart = 1;
+ mutex_unlock(&chip->sysfs_restart_lock);
+
+ if (fg_debug_mask & FG_STATUS)
+ pr_info("fuel gauge restart initiated from sysfs...\n");
+
+ schedule_work(&chip->sysfs_restart_work);
+ return 0;
+}
+
+static struct kernel_param_ops fg_restart_ops = {
+ .set = fg_restart_set,
+ .get = param_get_int,
+};
+
+module_param_cb(restart, &fg_restart_ops, &fg_restart, 0644);
+
+static struct platform_driver fg_driver = {
+ .driver = {
+ .name = QPNP_FG_DEV_NAME,
+ .of_match_table = fg_match_table,
+ .pm = &qpnp_fg_pm_ops,
+ },
+ .probe = fg_probe,
+ .remove = fg_remove,
+};
+
+static int __init fg_init(void)
+{
+ return platform_driver_register(&fg_driver);
+}
+
+static void __exit fg_exit(void)
+{
+ return platform_driver_unregister(&fg_driver);
+}
+
+module_init(fg_init);
+module_exit(fg_exit);
+
+MODULE_DESCRIPTION("QPNP Fuel Gauge Driver");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:" QPNP_FG_DEV_NAME);
diff --git a/drivers/power/supply/qcom/qpnp-smb2.c b/drivers/power/supply/qcom/qpnp-smb2.c
index 34514c9..1ab0357 100644
--- a/drivers/power/supply/qcom/qpnp-smb2.c
+++ b/drivers/power/supply/qcom/qpnp-smb2.c
@@ -1664,6 +1664,18 @@
return rc;
}
+ /*
+ * allow DRP.DFP time to exceed by tPDdebounce time.
+ */
+ rc = smblib_masked_write(chg, TAPER_TIMER_SEL_CFG_REG,
+ TYPEC_DRP_DFP_TIME_CFG_BIT,
+ TYPEC_DRP_DFP_TIME_CFG_BIT);
+ if (rc < 0) {
+ dev_err(chg->dev, "Couldn't configure DRP.DFP time rc=%d\n",
+ rc);
+ return rc;
+ }
+
/* configure float charger options */
switch (chip->dt.float_option) {
case 1:
diff --git a/drivers/power/supply/qcom/qpnp-smbcharger.c b/drivers/power/supply/qcom/qpnp-smbcharger.c
new file mode 100644
index 0000000..f9c755c
--- /dev/null
+++ b/drivers/power/supply/qcom/qpnp-smbcharger.c
@@ -0,0 +1,8472 @@
+/* Copyright (c) 2014-2016 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) "SMBCHG: %s: " fmt, __func__
+
+#include <linux/regmap.h>
+#include <linux/spinlock.h>
+#include <linux/gpio.h>
+#include <linux/errno.h>
+#include <linux/delay.h>
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/slab.h>
+#include <linux/sched.h>
+#include <linux/power_supply.h>
+#include <linux/of.h>
+#include <linux/of_gpio.h>
+#include <linux/of_irq.h>
+#include <linux/bitops.h>
+#include <linux/regulator/consumer.h>
+#include <linux/regulator/driver.h>
+#include <linux/regulator/of_regulator.h>
+#include <linux/regulator/machine.h>
+#include <linux/spmi.h>
+#include <linux/platform_device.h>
+#include <linux/printk.h>
+#include <linux/ratelimit.h>
+#include <linux/debugfs.h>
+#include <linux/leds.h>
+#include <linux/rtc.h>
+#include <linux/qpnp/qpnp-adc.h>
+#include <linux/batterydata-lib.h>
+#include <linux/of_batterydata.h>
+#include <linux/msm_bcl.h>
+#include <linux/ktime.h>
+#include <linux/extcon.h>
+#include <linux/pmic-voter.h>
+
+/* Mask/Bit helpers */
+#define _SMB_MASK(BITS, POS) \
+ ((unsigned char)(((1 << (BITS)) - 1) << (POS)))
+#define SMB_MASK(LEFT_BIT_POS, RIGHT_BIT_POS) \
+ _SMB_MASK((LEFT_BIT_POS) - (RIGHT_BIT_POS) + 1, \
+ (RIGHT_BIT_POS))
+/* Config registers */
+struct smbchg_regulator {
+ struct regulator_desc rdesc;
+ struct regulator_dev *rdev;
+};
+
+struct parallel_usb_cfg {
+ struct power_supply *psy;
+ int min_current_thr_ma;
+ int min_9v_current_thr_ma;
+ int allowed_lowering_ma;
+ int current_max_ma;
+ bool avail;
+ struct mutex lock;
+ int initial_aicl_ma;
+ ktime_t last_disabled;
+ bool enabled_once;
+};
+
+struct ilim_entry {
+ int vmin_uv;
+ int vmax_uv;
+ int icl_pt_ma;
+ int icl_lv_ma;
+ int icl_hv_ma;
+};
+
+struct ilim_map {
+ int num;
+ struct ilim_entry *entries;
+};
+
+struct smbchg_version_tables {
+ const int *dc_ilim_ma_table;
+ int dc_ilim_ma_len;
+ const int *usb_ilim_ma_table;
+ int usb_ilim_ma_len;
+ const int *iterm_ma_table;
+ int iterm_ma_len;
+ const int *fcc_comp_table;
+ int fcc_comp_len;
+ const int *aicl_rerun_period_table;
+ int aicl_rerun_period_len;
+ int rchg_thr_mv;
+};
+
+struct smbchg_chip {
+ struct device *dev;
+ struct platform_device *pdev;
+ struct regmap *regmap;
+ int schg_version;
+
+ /* peripheral register address bases */
+ u16 chgr_base;
+ u16 bat_if_base;
+ u16 usb_chgpth_base;
+ u16 dc_chgpth_base;
+ u16 otg_base;
+ u16 misc_base;
+
+ int fake_battery_soc;
+ u8 revision[4];
+
+ /* configuration parameters */
+ int iterm_ma;
+ int usb_max_current_ma;
+ int typec_current_ma;
+ int dc_max_current_ma;
+ int dc_target_current_ma;
+ int cfg_fastchg_current_ma;
+ int fastchg_current_ma;
+ int vfloat_mv;
+ int fastchg_current_comp;
+ int float_voltage_comp;
+ int resume_delta_mv;
+ int safety_time;
+ int prechg_safety_time;
+ int bmd_pin_src;
+ int jeita_temp_hard_limit;
+ int aicl_rerun_period_s;
+ bool use_vfloat_adjustments;
+ bool iterm_disabled;
+ bool bmd_algo_disabled;
+ bool soft_vfloat_comp_disabled;
+ bool chg_enabled;
+ bool charge_unknown_battery;
+ bool chg_inhibit_en;
+ bool chg_inhibit_source_fg;
+ bool low_volt_dcin;
+ bool cfg_chg_led_support;
+ bool cfg_chg_led_sw_ctrl;
+ bool vbat_above_headroom;
+ bool force_aicl_rerun;
+ bool hvdcp3_supported;
+ bool restricted_charging;
+ bool skip_usb_suspend_for_fake_battery;
+ bool hvdcp_not_supported;
+ bool otg_pinctrl;
+ u8 original_usbin_allowance;
+ struct parallel_usb_cfg parallel;
+ struct delayed_work parallel_en_work;
+ struct dentry *debug_root;
+ struct smbchg_version_tables tables;
+
+ /* wipower params */
+ struct ilim_map wipower_default;
+ struct ilim_map wipower_pt;
+ struct ilim_map wipower_div2;
+ struct qpnp_vadc_chip *vadc_dev;
+ bool wipower_dyn_icl_avail;
+ struct ilim_entry current_ilim;
+ struct mutex wipower_config;
+ bool wipower_configured;
+ struct qpnp_adc_tm_btm_param param;
+
+ /* flash current prediction */
+ int rpara_uohm;
+ int rslow_uohm;
+ int vled_max_uv;
+
+ /* vfloat adjustment */
+ int max_vbat_sample;
+ int n_vbat_samples;
+
+ /* status variables */
+ int wake_reasons;
+ int previous_soc;
+ int usb_online;
+ bool dc_present;
+ bool usb_present;
+ bool batt_present;
+ int otg_retries;
+ ktime_t otg_enable_time;
+ bool aicl_deglitch_short;
+ bool safety_timer_en;
+ bool aicl_complete;
+ bool usb_ov_det;
+ bool otg_pulse_skip_dis;
+ const char *battery_type;
+ enum power_supply_type usb_supply_type;
+ bool very_weak_charger;
+ bool parallel_charger_detected;
+ bool chg_otg_enabled;
+ bool flash_triggered;
+ bool flash_active;
+ bool icl_disabled;
+ u32 wa_flags;
+ int usb_icl_delta;
+ bool typec_dfp;
+ unsigned int usb_current_max;
+ unsigned int usb_health;
+
+ /* jeita and temperature */
+ bool batt_hot;
+ bool batt_cold;
+ bool batt_warm;
+ bool batt_cool;
+ unsigned int thermal_levels;
+ unsigned int therm_lvl_sel;
+ unsigned int *thermal_mitigation;
+
+ /* irqs */
+ int batt_hot_irq;
+ int batt_warm_irq;
+ int batt_cool_irq;
+ int batt_cold_irq;
+ int batt_missing_irq;
+ int vbat_low_irq;
+ int chg_hot_irq;
+ int chg_term_irq;
+ int taper_irq;
+ bool taper_irq_enabled;
+ struct mutex taper_irq_lock;
+ int recharge_irq;
+ int fastchg_irq;
+ int wdog_timeout_irq;
+ int power_ok_irq;
+ int dcin_uv_irq;
+ int usbin_uv_irq;
+ int usbin_ov_irq;
+ int src_detect_irq;
+ int otg_fail_irq;
+ int otg_oc_irq;
+ int aicl_done_irq;
+ int usbid_change_irq;
+ int chg_error_irq;
+ bool enable_aicl_wake;
+
+ /* psy */
+ struct power_supply_desc usb_psy_d;
+ struct power_supply *usb_psy;
+ struct power_supply_desc batt_psy_d;
+ struct power_supply *batt_psy;
+ struct power_supply_desc dc_psy_d;
+ struct power_supply *dc_psy;
+ struct power_supply *bms_psy;
+ struct power_supply *typec_psy;
+ int dc_psy_type;
+ const char *bms_psy_name;
+ const char *battery_psy_name;
+
+ struct regulator *dpdm_reg;
+ struct smbchg_regulator otg_vreg;
+ struct smbchg_regulator ext_otg_vreg;
+ struct work_struct usb_set_online_work;
+ struct delayed_work vfloat_adjust_work;
+ struct delayed_work hvdcp_det_work;
+ spinlock_t sec_access_lock;
+ struct mutex therm_lvl_lock;
+ struct mutex usb_set_online_lock;
+ struct mutex pm_lock;
+ /* aicl deglitch workaround */
+ unsigned long first_aicl_seconds;
+ int aicl_irq_count;
+ struct mutex usb_status_lock;
+ bool hvdcp_3_det_ignore_uv;
+ struct completion src_det_lowered;
+ struct completion src_det_raised;
+ struct completion usbin_uv_lowered;
+ struct completion usbin_uv_raised;
+ int pulse_cnt;
+ struct led_classdev led_cdev;
+ bool skip_usb_notification;
+ u32 vchg_adc_channel;
+ struct qpnp_vadc_chip *vchg_vadc_dev;
+
+ /* voters */
+ struct votable *fcc_votable;
+ struct votable *usb_icl_votable;
+ struct votable *dc_icl_votable;
+ struct votable *usb_suspend_votable;
+ struct votable *dc_suspend_votable;
+ struct votable *battchg_suspend_votable;
+ struct votable *hw_aicl_rerun_disable_votable;
+ struct votable *hw_aicl_rerun_enable_indirect_votable;
+ struct votable *aicl_deglitch_short_votable;
+
+ /* extcon for VBUS / ID notification to USB */
+ struct extcon_dev *extcon;
+};
+
+enum qpnp_schg {
+ QPNP_SCHG,
+ QPNP_SCHG_LITE,
+};
+
+static char *version_str[] = {
+ [QPNP_SCHG] = "SCHG",
+ [QPNP_SCHG_LITE] = "SCHG_LITE",
+};
+
+enum pmic_subtype {
+ PMI8994 = 10,
+ PMI8950 = 17,
+ PMI8996 = 19,
+ PMI8937 = 55,
+};
+
+enum smbchg_wa {
+ SMBCHG_AICL_DEGLITCH_WA = BIT(0),
+ SMBCHG_HVDCP_9V_EN_WA = BIT(1),
+ SMBCHG_USB100_WA = BIT(2),
+ SMBCHG_BATT_OV_WA = BIT(3),
+ SMBCHG_CC_ESR_WA = BIT(4),
+ SMBCHG_FLASH_ICL_DISABLE_WA = BIT(5),
+ SMBCHG_RESTART_WA = BIT(6),
+ SMBCHG_FLASH_BUCK_SWITCH_FREQ_WA = BIT(7),
+};
+
+enum print_reason {
+ PR_REGISTER = BIT(0),
+ PR_INTERRUPT = BIT(1),
+ PR_STATUS = BIT(2),
+ PR_DUMP = BIT(3),
+ PR_PM = BIT(4),
+ PR_MISC = BIT(5),
+ PR_WIPOWER = BIT(6),
+ PR_TYPEC = BIT(7),
+};
+
+enum wake_reason {
+ PM_PARALLEL_CHECK = BIT(0),
+ PM_REASON_VFLOAT_ADJUST = BIT(1),
+ PM_ESR_PULSE = BIT(2),
+ PM_PARALLEL_TAPER = BIT(3),
+ PM_DETECT_HVDCP = BIT(4),
+};
+
+/* fcc_voters */
+#define ESR_PULSE_FCC_VOTER "ESR_PULSE_FCC_VOTER"
+#define BATT_TYPE_FCC_VOTER "BATT_TYPE_FCC_VOTER"
+#define RESTRICTED_CHG_FCC_VOTER "RESTRICTED_CHG_FCC_VOTER"
+
+/* ICL VOTERS */
+#define PSY_ICL_VOTER "PSY_ICL_VOTER"
+#define THERMAL_ICL_VOTER "THERMAL_ICL_VOTER"
+#define HVDCP_ICL_VOTER "HVDCP_ICL_VOTER"
+#define USER_ICL_VOTER "USER_ICL_VOTER"
+#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"
+
+/* USB SUSPEND VOTERS */
+/* userspace has suspended charging altogether */
+#define USER_EN_VOTER "USER_EN_VOTER"
+/*
+ * this specific path has been suspended through the power supply
+ * framework
+ */
+#define POWER_SUPPLY_EN_VOTER "POWER_SUPPLY_EN_VOTER"
+/*
+ * the usb driver has suspended this path by setting a current limit
+ * of < 2MA
+ */
+#define USB_EN_VOTER "USB_EN_VOTER"
+/*
+ * the thermal daemon can suspend a charge path when the system
+ * temperature levels rise
+ */
+#define THERMAL_EN_VOTER "THERMAL_EN_VOTER"
+/*
+ * an external OTG supply is being used, suspend charge path so the
+ * charger does not accidentally try to charge from the external supply.
+ */
+#define OTG_EN_VOTER "OTG_EN_VOTER"
+/*
+ * the charger is very weak, do not draw any current from it
+ */
+#define WEAK_CHARGER_EN_VOTER "WEAK_CHARGER_EN_VOTER"
+/*
+ * fake battery voter, if battery id-resistance around 7.5 Kohm
+ */
+#define FAKE_BATTERY_EN_VOTER "FAKE_BATTERY_EN_VOTER"
+
+/* battchg_enable_voters */
+ /* userspace has disabled battery charging */
+#define BATTCHG_USER_EN_VOTER "BATTCHG_USER_EN_VOTER"
+ /* battery charging disabled while loading battery profiles */
+#define BATTCHG_UNKNOWN_BATTERY_EN_VOTER "BATTCHG_UNKNOWN_BATTERY_EN_VOTER"
+
+/* hw_aicl_rerun_enable_indirect_voters */
+/* enabled via device tree */
+#define DEFAULT_CONFIG_HW_AICL_VOTER "DEFAULT_CONFIG_HW_AICL_VOTER"
+/* Varb workaround voter */
+#define VARB_WORKAROUND_VOTER "VARB_WORKAROUND_VOTER"
+/* SHUTDOWN workaround voter */
+#define SHUTDOWN_WORKAROUND_VOTER "SHUTDOWN_WORKAROUND_VOTER"
+
+/* hw_aicl_rerun_disable_voters */
+/* the results from enabling clients */
+#define HW_AICL_RERUN_ENABLE_INDIRECT_VOTER \
+ "HW_AICL_RERUN_ENABLE_INDIRECT_VOTER"
+/* Weak charger voter */
+#define WEAK_CHARGER_HW_AICL_VOTER "WEAK_CHARGER_HW_AICL_VOTER"
+
+/* aicl_short_deglitch_voters */
+/* Varb workaround voter */
+#define VARB_WORKAROUND_SHORT_DEGLITCH_VOTER \
+ "VARB_WRKARND_SHORT_DEGLITCH_VOTER"
+/* QC 2.0 */
+#define HVDCP_SHORT_DEGLITCH_VOTER "HVDCP_SHORT_DEGLITCH_VOTER"
+
+static const unsigned int smbchg_extcon_cable[] = {
+ EXTCON_USB,
+ EXTCON_USB_HOST,
+ EXTCON_NONE,
+};
+
+static int smbchg_debug_mask;
+module_param_named(
+ debug_mask, smbchg_debug_mask, int, 00600
+);
+
+static int smbchg_parallel_en = 1;
+module_param_named(
+ parallel_en, smbchg_parallel_en, int, 00600
+);
+
+static int smbchg_main_chg_fcc_percent = 50;
+module_param_named(
+ main_chg_fcc_percent, smbchg_main_chg_fcc_percent,
+ int, 00600
+);
+
+static int smbchg_main_chg_icl_percent = 60;
+module_param_named(
+ main_chg_icl_percent, smbchg_main_chg_icl_percent,
+ int, 00600
+);
+
+static int smbchg_default_hvdcp_icl_ma = 1800;
+module_param_named(
+ default_hvdcp_icl_ma, smbchg_default_hvdcp_icl_ma,
+ int, 00600
+);
+
+static int smbchg_default_hvdcp3_icl_ma = 3000;
+module_param_named(
+ default_hvdcp3_icl_ma, smbchg_default_hvdcp3_icl_ma,
+ int, 00600
+);
+
+static int smbchg_default_dcp_icl_ma = 1800;
+module_param_named(
+ default_dcp_icl_ma, smbchg_default_dcp_icl_ma,
+ int, 00600
+);
+
+static int wipower_dyn_icl_en;
+module_param_named(
+ dynamic_icl_wipower_en, wipower_dyn_icl_en,
+ int, 00600
+);
+
+static int wipower_dcin_interval = ADC_MEAS1_INTERVAL_2P0MS;
+module_param_named(
+ wipower_dcin_interval, wipower_dcin_interval,
+ int, 00600
+);
+
+#define WIPOWER_DEFAULT_HYSTERISIS_UV 250000
+static int wipower_dcin_hyst_uv = WIPOWER_DEFAULT_HYSTERISIS_UV;
+module_param_named(
+ wipower_dcin_hyst_uv, wipower_dcin_hyst_uv,
+ int, 00600
+);
+
+#define pr_smb(reason, fmt, ...) \
+ do { \
+ if (smbchg_debug_mask & (reason)) \
+ pr_info(fmt, ##__VA_ARGS__); \
+ else \
+ pr_debug(fmt, ##__VA_ARGS__); \
+ } while (0)
+
+#define pr_smb_rt(reason, fmt, ...) \
+ do { \
+ if (smbchg_debug_mask & (reason)) \
+ pr_info_ratelimited(fmt, ##__VA_ARGS__); \
+ else \
+ pr_debug(fmt, ##__VA_ARGS__); \
+ } while (0)
+
+static int smbchg_read(struct smbchg_chip *chip, u8 *val,
+ u16 addr, int count)
+{
+ int rc = 0;
+ struct platform_device *pdev = chip->pdev;
+
+ if (addr == 0) {
+ dev_err(chip->dev, "addr cannot be zero addr=0x%02x sid=0x%02x rc=%d\n",
+ addr, to_spmi_device(pdev->dev.parent)->usid, rc);
+ return -EINVAL;
+ }
+
+ rc = regmap_bulk_read(chip->regmap, addr, val, count);
+ if (rc) {
+ dev_err(chip->dev, "spmi read failed addr=0x%02x sid=0x%02x rc=%d\n",
+ addr, to_spmi_device(pdev->dev.parent)->usid,
+ rc);
+ return rc;
+ }
+ return 0;
+}
+
+/*
+ * Writes a register to the specified by the base and limited by the bit mask
+ *
+ * Do not use this function for register writes if possible. Instead use the
+ * smbchg_masked_write function.
+ *
+ * The sec_access_lock must be held for all register writes and this function
+ * does not do that. If this function is used, please hold the spinlock or
+ * random secure access writes may fail.
+ */
+static int smbchg_masked_write_raw(struct smbchg_chip *chip, u16 base, u8 mask,
+ u8 val)
+{
+ int rc;
+
+ rc = regmap_update_bits(chip->regmap, base, mask, val);
+ if (rc) {
+ dev_err(chip->dev, "spmi write failed: addr=%03X, rc=%d\n",
+ base, rc);
+ return rc;
+ }
+
+ return 0;
+}
+
+/*
+ * Writes a register to the specified by the base and limited by the bit mask
+ *
+ * This function holds a spin lock to ensure secure access register writes goes
+ * through. If the secure access unlock register is armed, any old register
+ * write can unarm the secure access unlock, causing the next write to fail.
+ *
+ * Note: do not use this for sec_access registers. Instead use the function
+ * below: smbchg_sec_masked_write
+ */
+static int smbchg_masked_write(struct smbchg_chip *chip, u16 base, u8 mask,
+ u8 val)
+{
+ unsigned long flags;
+ int rc;
+
+ spin_lock_irqsave(&chip->sec_access_lock, flags);
+ rc = smbchg_masked_write_raw(chip, base, mask, val);
+ spin_unlock_irqrestore(&chip->sec_access_lock, flags);
+
+ return rc;
+}
+
+/*
+ * Unlocks sec access and writes to the register specified.
+ *
+ * This function holds a spin lock to exclude other register writes while
+ * the two writes are taking place.
+ */
+#define SEC_ACCESS_OFFSET 0xD0
+#define SEC_ACCESS_VALUE 0xA5
+#define PERIPHERAL_MASK 0xFF
+static int smbchg_sec_masked_write(struct smbchg_chip *chip, u16 base, u8 mask,
+ u8 val)
+{
+ unsigned long flags;
+ int rc;
+ u16 peripheral_base = base & (~PERIPHERAL_MASK);
+
+ spin_lock_irqsave(&chip->sec_access_lock, flags);
+
+ rc = smbchg_masked_write_raw(chip, peripheral_base + SEC_ACCESS_OFFSET,
+ SEC_ACCESS_VALUE, SEC_ACCESS_VALUE);
+ if (rc) {
+ dev_err(chip->dev, "Unable to unlock sec_access: %d", rc);
+ goto out;
+ }
+
+ rc = smbchg_masked_write_raw(chip, base, mask, val);
+
+out:
+ spin_unlock_irqrestore(&chip->sec_access_lock, flags);
+ return rc;
+}
+
+static void smbchg_stay_awake(struct smbchg_chip *chip, int reason)
+{
+ int reasons;
+
+ mutex_lock(&chip->pm_lock);
+ reasons = chip->wake_reasons | reason;
+ if (reasons != 0 && chip->wake_reasons == 0) {
+ pr_smb(PR_PM, "staying awake: 0x%02x (bit %d)\n",
+ reasons, reason);
+ pm_stay_awake(chip->dev);
+ }
+ chip->wake_reasons = reasons;
+ mutex_unlock(&chip->pm_lock);
+}
+
+static void smbchg_relax(struct smbchg_chip *chip, int reason)
+{
+ int reasons;
+
+ mutex_lock(&chip->pm_lock);
+ reasons = chip->wake_reasons & (~reason);
+ if (reasons == 0 && chip->wake_reasons != 0) {
+ pr_smb(PR_PM, "relaxing: 0x%02x (bit %d)\n",
+ reasons, reason);
+ pm_relax(chip->dev);
+ }
+ chip->wake_reasons = reasons;
+ mutex_unlock(&chip->pm_lock);
+};
+
+enum pwr_path_type {
+ UNKNOWN = 0,
+ PWR_PATH_BATTERY = 1,
+ PWR_PATH_USB = 2,
+ PWR_PATH_DC = 3,
+};
+
+#define PWR_PATH 0x08
+#define PWR_PATH_MASK 0x03
+static enum pwr_path_type smbchg_get_pwr_path(struct smbchg_chip *chip)
+{
+ int rc;
+ u8 reg;
+
+ rc = smbchg_read(chip, ®, chip->usb_chgpth_base + PWR_PATH, 1);
+ if (rc < 0) {
+ dev_err(chip->dev, "Couldn't read PWR_PATH rc = %d\n", rc);
+ return PWR_PATH_BATTERY;
+ }
+
+ return reg & PWR_PATH_MASK;
+}
+
+#define RID_STS 0xB
+#define RID_MASK 0xF
+#define IDEV_STS 0x8
+#define RT_STS 0x10
+#define USBID_MSB 0xE
+#define USBIN_UV_BIT BIT(0)
+#define USBIN_OV_BIT BIT(1)
+#define USBIN_SRC_DET_BIT BIT(2)
+#define FMB_STS_MASK SMB_MASK(3, 0)
+#define USBID_GND_THRESHOLD 0x495
+static bool is_otg_present_schg(struct smbchg_chip *chip)
+{
+ int rc;
+ u8 reg;
+ u8 usbid_reg[2];
+ u16 usbid_val;
+ /*
+ * After the falling edge of the usbid change interrupt occurs,
+ * there may still be some time before the ADC conversion for USB RID
+ * finishes in the fuel gauge. In the worst case, this could be up to
+ * 15 ms.
+ *
+ * Sleep for 20 ms (minimum msleep time) to wait for the conversion to
+ * finish and the USB RID status register to be updated before trying
+ * to detect OTG insertions.
+ */
+
+ msleep(20);
+
+ /*
+ * There is a problem with USBID conversions on PMI8994 revisions
+ * 2.0.0. As a workaround, check that the cable is not
+ * detected as factory test before enabling OTG.
+ */
+ rc = smbchg_read(chip, ®, chip->misc_base + IDEV_STS, 1);
+ if (rc < 0) {
+ dev_err(chip->dev, "Couldn't read IDEV_STS rc = %d\n", rc);
+ return false;
+ }
+
+ if ((reg & FMB_STS_MASK) != 0) {
+ pr_smb(PR_STATUS, "IDEV_STS = %02x, not ground\n", reg);
+ return false;
+ }
+
+ rc = smbchg_read(chip, usbid_reg, chip->usb_chgpth_base + USBID_MSB, 2);
+ if (rc < 0) {
+ dev_err(chip->dev, "Couldn't read USBID rc = %d\n", rc);
+ return false;
+ }
+ usbid_val = (usbid_reg[0] << 8) | usbid_reg[1];
+
+ if (usbid_val > USBID_GND_THRESHOLD) {
+ pr_smb(PR_STATUS, "USBID = 0x%04x, too high to be ground\n",
+ usbid_val);
+ return false;
+ }
+
+ rc = smbchg_read(chip, ®, chip->usb_chgpth_base + RID_STS, 1);
+ if (rc < 0) {
+ dev_err(chip->dev,
+ "Couldn't read usb rid status rc = %d\n", rc);
+ return false;
+ }
+
+ pr_smb(PR_STATUS, "RID_STS = %02x\n", reg);
+
+ return (reg & RID_MASK) == 0;
+}
+
+#define RID_GND_DET_STS BIT(2)
+static bool is_otg_present_schg_lite(struct smbchg_chip *chip)
+{
+ int rc;
+ u8 reg;
+
+ rc = smbchg_read(chip, ®, chip->otg_base + RT_STS, 1);
+ if (rc < 0) {
+ dev_err(chip->dev,
+ "Couldn't read otg RT status rc = %d\n", rc);
+ return false;
+ }
+
+ return !!(reg & RID_GND_DET_STS);
+}
+
+static bool is_otg_present(struct smbchg_chip *chip)
+{
+ if (chip->schg_version == QPNP_SCHG_LITE)
+ return is_otg_present_schg_lite(chip);
+
+ return is_otg_present_schg(chip);
+}
+
+#define USBIN_9V BIT(5)
+#define USBIN_UNREG BIT(4)
+#define USBIN_LV BIT(3)
+#define DCIN_9V BIT(2)
+#define DCIN_UNREG BIT(1)
+#define DCIN_LV BIT(0)
+#define INPUT_STS 0x0D
+#define DCIN_UV_BIT BIT(0)
+#define DCIN_OV_BIT BIT(1)
+static bool is_dc_present(struct smbchg_chip *chip)
+{
+ int rc;
+ u8 reg;
+
+ rc = smbchg_read(chip, ®, chip->dc_chgpth_base + RT_STS, 1);
+ if (rc < 0) {
+ dev_err(chip->dev, "Couldn't read dc status rc = %d\n", rc);
+ return false;
+ }
+
+ if ((reg & DCIN_UV_BIT) || (reg & DCIN_OV_BIT))
+ return false;
+
+ return true;
+}
+
+static bool is_usb_present(struct smbchg_chip *chip)
+{
+ int rc;
+ u8 reg;
+
+ rc = smbchg_read(chip, ®, chip->usb_chgpth_base + RT_STS, 1);
+ if (rc < 0) {
+ dev_err(chip->dev, "Couldn't read usb rt status rc = %d\n", rc);
+ return false;
+ }
+ if (!(reg & USBIN_SRC_DET_BIT) || (reg & USBIN_OV_BIT))
+ return false;
+
+ rc = smbchg_read(chip, ®, chip->usb_chgpth_base + INPUT_STS, 1);
+ if (rc < 0) {
+ dev_err(chip->dev, "Couldn't read usb status rc = %d\n", rc);
+ return false;
+ }
+
+ return !!(reg & (USBIN_9V | USBIN_UNREG | USBIN_LV));
+}
+
+static char *usb_type_str[] = {
+ "SDP", /* bit 0 */
+ "OTHER", /* bit 1 */
+ "DCP", /* bit 2 */
+ "CDP", /* bit 3 */
+ "NONE", /* bit 4 error case */
+};
+
+#define N_TYPE_BITS 4
+#define TYPE_BITS_OFFSET 4
+
+static int get_type(u8 type_reg)
+{
+ unsigned long type = type_reg;
+
+ type >>= TYPE_BITS_OFFSET;
+ return find_first_bit(&type, N_TYPE_BITS);
+}
+
+/* helper to return the string of USB type */
+static inline char *get_usb_type_name(int type)
+{
+ return usb_type_str[type];
+}
+
+static enum power_supply_type usb_type_enum[] = {
+ POWER_SUPPLY_TYPE_USB, /* bit 0 */
+ POWER_SUPPLY_TYPE_USB_DCP, /* bit 1 */
+ POWER_SUPPLY_TYPE_USB_DCP, /* bit 2 */
+ POWER_SUPPLY_TYPE_USB_CDP, /* bit 3 */
+ POWER_SUPPLY_TYPE_USB_DCP, /* bit 4 error case, report DCP */
+};
+
+/* helper to return enum power_supply_type of USB type */
+static inline enum power_supply_type get_usb_supply_type(int type)
+{
+ return usb_type_enum[type];
+}
+
+static bool is_src_detect_high(struct smbchg_chip *chip)
+{
+ int rc;
+ u8 reg;
+
+ rc = smbchg_read(chip, ®, chip->usb_chgpth_base + RT_STS, 1);
+ if (rc < 0) {
+ dev_err(chip->dev, "Couldn't read usb rt status rc = %d\n", rc);
+ return false;
+ }
+ return reg &= USBIN_SRC_DET_BIT;
+}
+
+static void read_usb_type(struct smbchg_chip *chip, char **usb_type_name,
+ enum power_supply_type *usb_supply_type)
+{
+ int rc, type;
+ u8 reg;
+
+ if (!is_src_detect_high(chip)) {
+ pr_smb(PR_MISC, "src det low\n");
+ *usb_type_name = "Absent";
+ *usb_supply_type = POWER_SUPPLY_TYPE_UNKNOWN;
+ return;
+ }
+
+ rc = smbchg_read(chip, ®, chip->misc_base + IDEV_STS, 1);
+ if (rc < 0) {
+ dev_err(chip->dev, "Couldn't read status 5 rc = %d\n", rc);
+ *usb_type_name = "Other";
+ *usb_supply_type = POWER_SUPPLY_TYPE_UNKNOWN;
+ return;
+ }
+ type = get_type(reg);
+ *usb_type_name = get_usb_type_name(type);
+ *usb_supply_type = get_usb_supply_type(type);
+}
+
+#define CHGR_STS 0x0E
+#define BATT_LESS_THAN_2V BIT(4)
+#define CHG_HOLD_OFF_BIT BIT(3)
+#define CHG_TYPE_MASK SMB_MASK(2, 1)
+#define CHG_TYPE_SHIFT 1
+#define BATT_NOT_CHG_VAL 0x0
+#define BATT_PRE_CHG_VAL 0x1
+#define BATT_FAST_CHG_VAL 0x2
+#define BATT_TAPER_CHG_VAL 0x3
+#define CHG_INHIBIT_BIT BIT(1)
+#define BAT_TCC_REACHED_BIT BIT(7)
+static int get_prop_batt_status(struct smbchg_chip *chip)
+{
+ int rc, status = POWER_SUPPLY_STATUS_DISCHARGING;
+ u8 reg = 0, chg_type;
+ bool charger_present, chg_inhibit;
+
+ charger_present = is_usb_present(chip) | is_dc_present(chip) |
+ chip->hvdcp_3_det_ignore_uv;
+ if (!charger_present)
+ return POWER_SUPPLY_STATUS_DISCHARGING;
+
+ rc = smbchg_read(chip, ®, chip->chgr_base + RT_STS, 1);
+ if (rc < 0) {
+ dev_err(chip->dev, "Unable to read RT_STS rc = %d\n", rc);
+ return POWER_SUPPLY_STATUS_UNKNOWN;
+ }
+
+ if (reg & BAT_TCC_REACHED_BIT)
+ return POWER_SUPPLY_STATUS_FULL;
+
+ chg_inhibit = reg & CHG_INHIBIT_BIT;
+ if (chg_inhibit)
+ return POWER_SUPPLY_STATUS_FULL;
+
+ rc = smbchg_read(chip, ®, chip->chgr_base + CHGR_STS, 1);
+ if (rc < 0) {
+ dev_err(chip->dev, "Unable to read CHGR_STS rc = %d\n", rc);
+ return POWER_SUPPLY_STATUS_UNKNOWN;
+ }
+
+ if (reg & CHG_HOLD_OFF_BIT) {
+ /*
+ * when chg hold off happens the battery is
+ * not charging
+ */
+ status = POWER_SUPPLY_STATUS_NOT_CHARGING;
+ goto out;
+ }
+
+ chg_type = (reg & CHG_TYPE_MASK) >> CHG_TYPE_SHIFT;
+
+ if (chg_type == BATT_NOT_CHG_VAL && !chip->hvdcp_3_det_ignore_uv)
+ status = POWER_SUPPLY_STATUS_DISCHARGING;
+ else
+ status = POWER_SUPPLY_STATUS_CHARGING;
+out:
+ pr_smb_rt(PR_MISC, "CHGR_STS = 0x%02x\n", reg);
+ return status;
+}
+
+#define BAT_PRES_STATUS 0x08
+#define BAT_PRES_BIT BIT(7)
+static int get_prop_batt_present(struct smbchg_chip *chip)
+{
+ int rc;
+ u8 reg;
+
+ rc = smbchg_read(chip, ®, chip->bat_if_base + BAT_PRES_STATUS, 1);
+ if (rc < 0) {
+ dev_err(chip->dev, "Unable to read CHGR_STS rc = %d\n", rc);
+ return 0;
+ }
+
+ return !!(reg & BAT_PRES_BIT);
+}
+
+static int get_prop_charge_type(struct smbchg_chip *chip)
+{
+ int rc;
+ u8 reg, chg_type;
+
+ rc = smbchg_read(chip, ®, chip->chgr_base + CHGR_STS, 1);
+ if (rc < 0) {
+ dev_err(chip->dev, "Unable to read CHGR_STS rc = %d\n", rc);
+ return 0;
+ }
+
+ chg_type = (reg & CHG_TYPE_MASK) >> CHG_TYPE_SHIFT;
+ if (chg_type == BATT_NOT_CHG_VAL)
+ return POWER_SUPPLY_CHARGE_TYPE_NONE;
+ else if (chg_type == BATT_TAPER_CHG_VAL)
+ return POWER_SUPPLY_CHARGE_TYPE_TAPER;
+ else if (chg_type == BATT_FAST_CHG_VAL)
+ return POWER_SUPPLY_CHARGE_TYPE_FAST;
+ else if (chg_type == BATT_PRE_CHG_VAL)
+ return POWER_SUPPLY_CHARGE_TYPE_TRICKLE;
+
+ return POWER_SUPPLY_CHARGE_TYPE_NONE;
+}
+
+static int set_property_on_fg(struct smbchg_chip *chip,
+ enum power_supply_property prop, int val)
+{
+ int rc;
+ union power_supply_propval ret = {0, };
+
+ if (!chip->bms_psy && chip->bms_psy_name)
+ chip->bms_psy =
+ power_supply_get_by_name((char *)chip->bms_psy_name);
+ if (!chip->bms_psy) {
+ pr_smb(PR_STATUS, "no bms psy found\n");
+ return -EINVAL;
+ }
+
+ ret.intval = val;
+ rc = power_supply_set_property(chip->bms_psy, prop, &ret);
+ if (rc)
+ pr_smb(PR_STATUS,
+ "bms psy does not allow updating prop %d rc = %d\n",
+ prop, rc);
+
+ return rc;
+}
+
+static int get_property_from_fg(struct smbchg_chip *chip,
+ enum power_supply_property prop, int *val)
+{
+ int rc;
+ union power_supply_propval ret = {0, };
+
+ if (!chip->bms_psy && chip->bms_psy_name)
+ chip->bms_psy =
+ power_supply_get_by_name((char *)chip->bms_psy_name);
+ if (!chip->bms_psy) {
+ pr_smb(PR_STATUS, "no bms psy found\n");
+ return -EINVAL;
+ }
+
+ rc = power_supply_get_property(chip->bms_psy, prop, &ret);
+ if (rc) {
+ pr_smb(PR_STATUS,
+ "bms psy doesn't support reading prop %d rc = %d\n",
+ prop, rc);
+ return rc;
+ }
+
+ *val = ret.intval;
+ return rc;
+}
+
+#define DEFAULT_BATT_CAPACITY 50
+static int get_prop_batt_capacity(struct smbchg_chip *chip)
+{
+ int capacity, rc;
+
+ if (chip->fake_battery_soc >= 0)
+ return chip->fake_battery_soc;
+
+ rc = get_property_from_fg(chip, POWER_SUPPLY_PROP_CAPACITY, &capacity);
+ if (rc) {
+ pr_smb(PR_STATUS, "Couldn't get capacity rc = %d\n", rc);
+ capacity = DEFAULT_BATT_CAPACITY;
+ }
+ return capacity;
+}
+
+#define DEFAULT_BATT_TEMP 200
+static int get_prop_batt_temp(struct smbchg_chip *chip)
+{
+ int temp, rc;
+
+ rc = get_property_from_fg(chip, POWER_SUPPLY_PROP_TEMP, &temp);
+ if (rc) {
+ pr_smb(PR_STATUS, "Couldn't get temperature rc = %d\n", rc);
+ temp = DEFAULT_BATT_TEMP;
+ }
+ return temp;
+}
+
+#define DEFAULT_BATT_CURRENT_NOW 0
+static int get_prop_batt_current_now(struct smbchg_chip *chip)
+{
+ int ua, rc;
+
+ rc = get_property_from_fg(chip, POWER_SUPPLY_PROP_CURRENT_NOW, &ua);
+ if (rc) {
+ pr_smb(PR_STATUS, "Couldn't get current rc = %d\n", rc);
+ ua = DEFAULT_BATT_CURRENT_NOW;
+ }
+ return ua;
+}
+
+#define DEFAULT_BATT_VOLTAGE_NOW 0
+static int get_prop_batt_voltage_now(struct smbchg_chip *chip)
+{
+ int uv, rc;
+
+ rc = get_property_from_fg(chip, POWER_SUPPLY_PROP_VOLTAGE_NOW, &uv);
+ if (rc) {
+ pr_smb(PR_STATUS, "Couldn't get voltage rc = %d\n", rc);
+ uv = DEFAULT_BATT_VOLTAGE_NOW;
+ }
+ return uv;
+}
+
+#define DEFAULT_BATT_VOLTAGE_MAX_DESIGN 4200000
+static int get_prop_batt_voltage_max_design(struct smbchg_chip *chip)
+{
+ int uv, rc;
+
+ rc = get_property_from_fg(chip,
+ POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN, &uv);
+ if (rc) {
+ pr_smb(PR_STATUS, "Couldn't get voltage rc = %d\n", rc);
+ uv = DEFAULT_BATT_VOLTAGE_MAX_DESIGN;
+ }
+ return uv;
+}
+
+static int get_prop_batt_health(struct smbchg_chip *chip)
+{
+ if (chip->batt_hot)
+ return POWER_SUPPLY_HEALTH_OVERHEAT;
+ else if (chip->batt_cold)
+ return POWER_SUPPLY_HEALTH_COLD;
+ else if (chip->batt_warm)
+ return POWER_SUPPLY_HEALTH_WARM;
+ else if (chip->batt_cool)
+ return POWER_SUPPLY_HEALTH_COOL;
+ else
+ return POWER_SUPPLY_HEALTH_GOOD;
+}
+
+static void get_property_from_typec(struct smbchg_chip *chip,
+ enum power_supply_property property,
+ union power_supply_propval *prop)
+{
+ int rc;
+
+ rc = power_supply_get_property(chip->typec_psy,
+ property, prop);
+ if (rc)
+ pr_smb(PR_TYPEC,
+ "typec psy doesn't support reading prop %d rc = %d\n",
+ property, rc);
+}
+
+static void update_typec_status(struct smbchg_chip *chip)
+{
+ union power_supply_propval type = {0, };
+ union power_supply_propval capability = {0, };
+
+ get_property_from_typec(chip, POWER_SUPPLY_PROP_TYPE, &type);
+ if (type.intval != POWER_SUPPLY_TYPE_UNKNOWN) {
+ get_property_from_typec(chip,
+ POWER_SUPPLY_PROP_CURRENT_CAPABILITY,
+ &capability);
+ chip->typec_current_ma = capability.intval;
+ pr_smb(PR_TYPEC, "SMB Type-C mode = %d, current=%d\n",
+ type.intval, capability.intval);
+ } else {
+ pr_smb(PR_TYPEC,
+ "typec detection not completed continuing with USB update\n");
+ }
+}
+
+/*
+ * finds the index of the closest value in the array. If there are two that
+ * are equally close, the lower index will be returned
+ */
+static int find_closest_in_array(const int *arr, int len, int val)
+{
+ int i, closest = 0;
+
+ if (len == 0)
+ return closest;
+ for (i = 0; i < len; i++)
+ if (abs(val - arr[i]) < abs(val - arr[closest]))
+ closest = i;
+
+ return closest;
+}
+
+/* finds the index of the closest smaller value in the array. */
+static int find_smaller_in_array(const int *table, int val, int len)
+{
+ int i;
+
+ for (i = len - 1; i >= 0; i--) {
+ if (val >= table[i])
+ break;
+ }
+
+ return i;
+}
+
+static const int iterm_ma_table_8994[] = {
+ 300,
+ 50,
+ 100,
+ 150,
+ 200,
+ 250,
+ 500,
+ 600
+};
+
+static const int iterm_ma_table_8996[] = {
+ 300,
+ 50,
+ 100,
+ 150,
+ 200,
+ 250,
+ 400,
+ 500
+};
+
+static const int usb_ilim_ma_table_8994[] = {
+ 300,
+ 400,
+ 450,
+ 475,
+ 500,
+ 550,
+ 600,
+ 650,
+ 700,
+ 900,
+ 950,
+ 1000,
+ 1100,
+ 1200,
+ 1400,
+ 1450,
+ 1500,
+ 1600,
+ 1800,
+ 1850,
+ 1880,
+ 1910,
+ 1930,
+ 1950,
+ 1970,
+ 2000,
+ 2050,
+ 2100,
+ 2300,
+ 2400,
+ 2500,
+ 3000
+};
+
+static const int usb_ilim_ma_table_8996[] = {
+ 300,
+ 400,
+ 500,
+ 600,
+ 700,
+ 800,
+ 900,
+ 1000,
+ 1100,
+ 1200,
+ 1300,
+ 1400,
+ 1450,
+ 1500,
+ 1550,
+ 1600,
+ 1700,
+ 1800,
+ 1900,
+ 1950,
+ 2000,
+ 2050,
+ 2100,
+ 2200,
+ 2300,
+ 2400,
+ 2500,
+ 2600,
+ 2700,
+ 2800,
+ 2900,
+ 3000
+};
+
+static int dc_ilim_ma_table_8994[] = {
+ 300,
+ 400,
+ 450,
+ 475,
+ 500,
+ 550,
+ 600,
+ 650,
+ 700,
+ 900,
+ 950,
+ 1000,
+ 1100,
+ 1200,
+ 1400,
+ 1450,
+ 1500,
+ 1600,
+ 1800,
+ 1850,
+ 1880,
+ 1910,
+ 1930,
+ 1950,
+ 1970,
+ 2000,
+};
+
+static int dc_ilim_ma_table_8996[] = {
+ 300,
+ 400,
+ 500,
+ 600,
+ 700,
+ 800,
+ 900,
+ 1000,
+ 1100,
+ 1200,
+ 1300,
+ 1400,
+ 1450,
+ 1500,
+ 1550,
+ 1600,
+ 1700,
+ 1800,
+ 1900,
+ 1950,
+ 2000,
+ 2050,
+ 2100,
+ 2200,
+ 2300,
+ 2400,
+};
+
+static const int fcc_comp_table_8994[] = {
+ 250,
+ 700,
+ 900,
+ 1200,
+};
+
+static const int fcc_comp_table_8996[] = {
+ 250,
+ 1100,
+ 1200,
+ 1500,
+};
+
+static const int aicl_rerun_period[] = {
+ 45,
+ 90,
+ 180,
+ 360,
+};
+
+static const int aicl_rerun_period_schg_lite[] = {
+ 3, /* 2.8s */
+ 6, /* 5.6s */
+ 11, /* 11.3s */
+ 23, /* 22.5s */
+ 45,
+ 90,
+ 180,
+ 360,
+};
+
+static void use_pmi8994_tables(struct smbchg_chip *chip)
+{
+ chip->tables.usb_ilim_ma_table = usb_ilim_ma_table_8994;
+ chip->tables.usb_ilim_ma_len = ARRAY_SIZE(usb_ilim_ma_table_8994);
+ chip->tables.dc_ilim_ma_table = dc_ilim_ma_table_8994;
+ chip->tables.dc_ilim_ma_len = ARRAY_SIZE(dc_ilim_ma_table_8994);
+ chip->tables.iterm_ma_table = iterm_ma_table_8994;
+ chip->tables.iterm_ma_len = ARRAY_SIZE(iterm_ma_table_8994);
+ chip->tables.fcc_comp_table = fcc_comp_table_8994;
+ chip->tables.fcc_comp_len = ARRAY_SIZE(fcc_comp_table_8994);
+ chip->tables.rchg_thr_mv = 200;
+ chip->tables.aicl_rerun_period_table = aicl_rerun_period;
+ chip->tables.aicl_rerun_period_len = ARRAY_SIZE(aicl_rerun_period);
+}
+
+static void use_pmi8996_tables(struct smbchg_chip *chip)
+{
+ chip->tables.usb_ilim_ma_table = usb_ilim_ma_table_8996;
+ chip->tables.usb_ilim_ma_len = ARRAY_SIZE(usb_ilim_ma_table_8996);
+ chip->tables.dc_ilim_ma_table = dc_ilim_ma_table_8996;
+ chip->tables.dc_ilim_ma_len = ARRAY_SIZE(dc_ilim_ma_table_8996);
+ chip->tables.iterm_ma_table = iterm_ma_table_8996;
+ chip->tables.iterm_ma_len = ARRAY_SIZE(iterm_ma_table_8996);
+ chip->tables.fcc_comp_table = fcc_comp_table_8996;
+ chip->tables.fcc_comp_len = ARRAY_SIZE(fcc_comp_table_8996);
+ chip->tables.rchg_thr_mv = 150;
+ chip->tables.aicl_rerun_period_table = aicl_rerun_period;
+ chip->tables.aicl_rerun_period_len = ARRAY_SIZE(aicl_rerun_period);
+}
+
+#define CMD_CHG_REG 0x42
+#define EN_BAT_CHG_BIT BIT(1)
+static int smbchg_charging_en(struct smbchg_chip *chip, bool en)
+{
+ /* The en bit is configured active low */
+ return smbchg_masked_write(chip, chip->bat_if_base + CMD_CHG_REG,
+ EN_BAT_CHG_BIT, en ? 0 : EN_BAT_CHG_BIT);
+}
+
+#define CMD_IL 0x40
+#define USBIN_SUSPEND_BIT BIT(4)
+#define CURRENT_100_MA 100
+#define CURRENT_150_MA 150
+#define CURRENT_500_MA 500
+#define CURRENT_900_MA 900
+#define CURRENT_1500_MA 1500
+#define SUSPEND_CURRENT_MA 2
+#define ICL_OVERRIDE_BIT BIT(2)
+static int smbchg_usb_suspend(struct smbchg_chip *chip, bool suspend)
+{
+ int rc;
+
+ rc = smbchg_masked_write(chip, chip->usb_chgpth_base + CMD_IL,
+ USBIN_SUSPEND_BIT, suspend ? USBIN_SUSPEND_BIT : 0);
+ if (rc < 0)
+ dev_err(chip->dev, "Couldn't set usb suspend rc = %d\n", rc);
+ return rc;
+}
+
+#define DCIN_SUSPEND_BIT BIT(3)
+static int smbchg_dc_suspend(struct smbchg_chip *chip, bool suspend)
+{
+ int rc = 0;
+
+ rc = smbchg_masked_write(chip, chip->usb_chgpth_base + CMD_IL,
+ DCIN_SUSPEND_BIT, suspend ? DCIN_SUSPEND_BIT : 0);
+ if (rc < 0)
+ dev_err(chip->dev, "Couldn't set dc suspend rc = %d\n", rc);
+ return rc;
+}
+
+#define IL_CFG 0xF2
+#define DCIN_INPUT_MASK SMB_MASK(4, 0)
+static int smbchg_set_dc_current_max(struct smbchg_chip *chip, int current_ma)
+{
+ int i;
+ u8 dc_cur_val;
+
+ i = find_smaller_in_array(chip->tables.dc_ilim_ma_table,
+ current_ma, chip->tables.dc_ilim_ma_len);
+
+ if (i < 0) {
+ dev_err(chip->dev, "Cannot find %dma current_table\n",
+ current_ma);
+ return -EINVAL;
+ }
+
+ chip->dc_max_current_ma = chip->tables.dc_ilim_ma_table[i];
+ dc_cur_val = i & DCIN_INPUT_MASK;
+
+ pr_smb(PR_STATUS, "dc current set to %d mA\n",
+ chip->dc_max_current_ma);
+ return smbchg_sec_masked_write(chip, chip->dc_chgpth_base + IL_CFG,
+ DCIN_INPUT_MASK, dc_cur_val);
+}
+
+#define AICL_WL_SEL_CFG 0xF5
+#define AICL_WL_SEL_MASK SMB_MASK(1, 0)
+#define AICL_WL_SEL_SCHG_LITE_MASK SMB_MASK(2, 0)
+static int smbchg_set_aicl_rerun_period_s(struct smbchg_chip *chip,
+ int period_s)
+{
+ int i;
+ u8 reg, mask;
+
+ i = find_smaller_in_array(chip->tables.aicl_rerun_period_table,
+ period_s, chip->tables.aicl_rerun_period_len);
+
+ if (i < 0) {
+ dev_err(chip->dev, "Cannot find %ds in aicl rerun period\n",
+ period_s);
+ return -EINVAL;
+ }
+
+ if (chip->schg_version == QPNP_SCHG_LITE)
+ mask = AICL_WL_SEL_SCHG_LITE_MASK;
+ else
+ mask = AICL_WL_SEL_MASK;
+
+ reg = i & mask;
+
+ pr_smb(PR_STATUS, "aicl rerun period set to %ds\n",
+ chip->tables.aicl_rerun_period_table[i]);
+ return smbchg_sec_masked_write(chip,
+ chip->dc_chgpth_base + AICL_WL_SEL_CFG,
+ mask, reg);
+}
+
+static struct power_supply *get_parallel_psy(struct smbchg_chip *chip)
+{
+ if (!chip->parallel.avail)
+ return NULL;
+ if (chip->parallel.psy)
+ return chip->parallel.psy;
+ chip->parallel.psy = power_supply_get_by_name("usb-parallel");
+ if (!chip->parallel.psy)
+ pr_smb(PR_STATUS, "parallel charger not found\n");
+ return chip->parallel.psy;
+}
+
+static void smbchg_usb_update_online_work(struct work_struct *work)
+{
+ struct smbchg_chip *chip = container_of(work,
+ struct smbchg_chip,
+ usb_set_online_work);
+ bool user_enabled = !get_client_vote(chip->usb_suspend_votable,
+ USER_EN_VOTER);
+ int online;
+
+ online = user_enabled && chip->usb_present && !chip->very_weak_charger;
+
+ mutex_lock(&chip->usb_set_online_lock);
+ if (chip->usb_online != online) {
+ pr_smb(PR_MISC, "setting usb psy online = %d\n", online);
+ chip->usb_online = online;
+ power_supply_changed(chip->usb_psy);
+ }
+ mutex_unlock(&chip->usb_set_online_lock);
+}
+
+#define CHGPTH_CFG 0xF4
+#define CFG_USB_2_3_SEL_BIT BIT(7)
+#define CFG_USB_2 0
+#define CFG_USB_3 BIT(7)
+#define USBIN_INPUT_MASK SMB_MASK(4, 0)
+#define USBIN_MODE_CHG_BIT BIT(0)
+#define USBIN_LIMITED_MODE 0
+#define USBIN_HC_MODE BIT(0)
+#define USB51_MODE_BIT BIT(1)
+#define USB51_100MA 0
+#define USB51_500MA BIT(1)
+static int smbchg_set_high_usb_chg_current(struct smbchg_chip *chip,
+ int current_ma)
+{
+ int i, rc;
+ u8 usb_cur_val;
+
+ if (current_ma == CURRENT_100_MA) {
+ rc = smbchg_sec_masked_write(chip,
+ chip->usb_chgpth_base + CHGPTH_CFG,
+ CFG_USB_2_3_SEL_BIT, CFG_USB_2);
+ if (rc < 0) {
+ pr_err("Couldn't set CFG_USB_2 rc=%d\n", rc);
+ return rc;
+ }
+
+ rc = smbchg_masked_write(chip, chip->usb_chgpth_base + CMD_IL,
+ USBIN_MODE_CHG_BIT | USB51_MODE_BIT | ICL_OVERRIDE_BIT,
+ USBIN_LIMITED_MODE | USB51_100MA | ICL_OVERRIDE_BIT);
+ if (rc < 0) {
+ pr_err("Couldn't set ICL_OVERRIDE rc=%d\n", rc);
+ return rc;
+ }
+
+ pr_smb(PR_STATUS,
+ "Forcing 100mA current limit\n");
+ chip->usb_max_current_ma = CURRENT_100_MA;
+ return rc;
+ }
+
+ i = find_smaller_in_array(chip->tables.usb_ilim_ma_table,
+ current_ma, chip->tables.usb_ilim_ma_len);
+ if (i < 0) {
+ dev_err(chip->dev,
+ "Cannot find %dma current_table using %d\n",
+ current_ma, CURRENT_150_MA);
+
+ rc = smbchg_sec_masked_write(chip,
+ chip->usb_chgpth_base + CHGPTH_CFG,
+ CFG_USB_2_3_SEL_BIT, CFG_USB_3);
+ rc |= smbchg_masked_write(chip, chip->usb_chgpth_base + CMD_IL,
+ USBIN_MODE_CHG_BIT | USB51_MODE_BIT,
+ USBIN_LIMITED_MODE | USB51_100MA);
+ if (rc < 0)
+ dev_err(chip->dev, "Couldn't set %dmA rc=%d\n",
+ CURRENT_150_MA, rc);
+ else
+ chip->usb_max_current_ma = 150;
+ return rc;
+ }
+
+ usb_cur_val = i & USBIN_INPUT_MASK;
+ rc = smbchg_sec_masked_write(chip, chip->usb_chgpth_base + IL_CFG,
+ USBIN_INPUT_MASK, usb_cur_val);
+ if (rc < 0) {
+ dev_err(chip->dev, "cannot write to config c rc = %d\n", rc);
+ return rc;
+ }
+
+ rc = smbchg_masked_write(chip, chip->usb_chgpth_base + CMD_IL,
+ USBIN_MODE_CHG_BIT, USBIN_HC_MODE);
+ if (rc < 0)
+ dev_err(chip->dev, "Couldn't write cfg 5 rc = %d\n", rc);
+ chip->usb_max_current_ma = chip->tables.usb_ilim_ma_table[i];
+ return rc;
+}
+
+/* if APSD results are used
+ * if SDP is detected it will look at 500mA setting
+ * if set it will draw 500mA
+ * if unset it will draw 100mA
+ * if CDP/DCP it will look at 0x0C setting
+ * i.e. values in 0x41[1, 0] does not matter
+ */
+static int smbchg_set_usb_current_max(struct smbchg_chip *chip,
+ int current_ma)
+{
+ int rc = 0;
+
+ /*
+ * if the battery is not present, do not allow the usb ICL to lower in
+ * order to avoid browning out the device during a hotswap.
+ */
+ if (!chip->batt_present && current_ma < chip->usb_max_current_ma) {
+ pr_info_ratelimited("Ignoring usb current->%d, battery is absent\n",
+ current_ma);
+ return 0;
+ }
+ pr_smb(PR_STATUS, "USB current_ma = %d\n", current_ma);
+
+ if (current_ma <= SUSPEND_CURRENT_MA) {
+ /* suspend the usb if current <= 2mA */
+ rc = vote(chip->usb_suspend_votable, USB_EN_VOTER, true, 0);
+ chip->usb_max_current_ma = 0;
+ goto out;
+ } else {
+ rc = vote(chip->usb_suspend_votable, USB_EN_VOTER, false, 0);
+ }
+
+ switch (chip->usb_supply_type) {
+ case POWER_SUPPLY_TYPE_USB:
+ if ((current_ma < CURRENT_150_MA) &&
+ (chip->wa_flags & SMBCHG_USB100_WA))
+ current_ma = CURRENT_150_MA;
+
+ if (current_ma < CURRENT_150_MA) {
+ /* force 100mA */
+ rc = smbchg_sec_masked_write(chip,
+ chip->usb_chgpth_base + CHGPTH_CFG,
+ CFG_USB_2_3_SEL_BIT, CFG_USB_2);
+ if (rc < 0) {
+ pr_err("Couldn't set CHGPTH_CFG rc = %d\n", rc);
+ goto out;
+ }
+ rc = smbchg_masked_write(chip,
+ chip->usb_chgpth_base + CMD_IL,
+ USBIN_MODE_CHG_BIT | USB51_MODE_BIT,
+ USBIN_LIMITED_MODE | USB51_100MA);
+ if (rc < 0) {
+ pr_err("Couldn't set CMD_IL rc = %d\n", rc);
+ goto out;
+ }
+ chip->usb_max_current_ma = 100;
+ }
+ /* specific current values */
+ if (current_ma == CURRENT_150_MA) {
+ rc = smbchg_sec_masked_write(chip,
+ chip->usb_chgpth_base + CHGPTH_CFG,
+ CFG_USB_2_3_SEL_BIT, CFG_USB_3);
+ if (rc < 0) {
+ pr_err("Couldn't set CHGPTH_CFG rc = %d\n", rc);
+ goto out;
+ }
+ rc = smbchg_masked_write(chip,
+ chip->usb_chgpth_base + CMD_IL,
+ USBIN_MODE_CHG_BIT | USB51_MODE_BIT,
+ USBIN_LIMITED_MODE | USB51_100MA);
+ if (rc < 0) {
+ pr_err("Couldn't set CMD_IL rc = %d\n", rc);
+ goto out;
+ }
+ chip->usb_max_current_ma = 150;
+ }
+ if (current_ma == CURRENT_500_MA) {
+ rc = smbchg_sec_masked_write(chip,
+ chip->usb_chgpth_base + CHGPTH_CFG,
+ CFG_USB_2_3_SEL_BIT, CFG_USB_2);
+ if (rc < 0) {
+ pr_err("Couldn't set CHGPTH_CFG rc = %d\n", rc);
+ goto out;
+ }
+ rc = smbchg_masked_write(chip,
+ chip->usb_chgpth_base + CMD_IL,
+ USBIN_MODE_CHG_BIT | USB51_MODE_BIT,
+ USBIN_LIMITED_MODE | USB51_500MA);
+ if (rc < 0) {
+ pr_err("Couldn't set CMD_IL rc = %d\n", rc);
+ goto out;
+ }
+ chip->usb_max_current_ma = 500;
+ }
+ if (current_ma == CURRENT_900_MA) {
+ rc = smbchg_sec_masked_write(chip,
+ chip->usb_chgpth_base + CHGPTH_CFG,
+ CFG_USB_2_3_SEL_BIT, CFG_USB_3);
+ if (rc < 0) {
+ pr_err("Couldn't set CHGPTH_CFG rc = %d\n", rc);
+ goto out;
+ }
+ rc = smbchg_masked_write(chip,
+ chip->usb_chgpth_base + CMD_IL,
+ USBIN_MODE_CHG_BIT | USB51_MODE_BIT,
+ USBIN_LIMITED_MODE | USB51_500MA);
+ if (rc < 0) {
+ pr_err("Couldn't set CMD_IL rc = %d\n", rc);
+ goto out;
+ }
+ chip->usb_max_current_ma = 900;
+ }
+ break;
+ case POWER_SUPPLY_TYPE_USB_CDP:
+ if (current_ma < CURRENT_1500_MA) {
+ /* use override for CDP */
+ rc = smbchg_masked_write(chip,
+ chip->usb_chgpth_base + CMD_IL,
+ ICL_OVERRIDE_BIT, ICL_OVERRIDE_BIT);
+ if (rc < 0)
+ pr_err("Couldn't set override rc = %d\n", rc);
+ }
+ /* fall through */
+ default:
+ rc = smbchg_set_high_usb_chg_current(chip, current_ma);
+ if (rc < 0)
+ pr_err("Couldn't set %dmA rc = %d\n", current_ma, rc);
+ break;
+ }
+
+out:
+ pr_smb(PR_STATUS, "usb type = %d current set to %d mA\n",
+ chip->usb_supply_type, chip->usb_max_current_ma);
+ return rc;
+}
+
+#define USBIN_HVDCP_STS 0x0C
+#define USBIN_HVDCP_SEL_BIT BIT(4)
+#define USBIN_HVDCP_SEL_9V_BIT BIT(1)
+#define SCHG_LITE_USBIN_HVDCP_SEL_9V_BIT BIT(2)
+#define SCHG_LITE_USBIN_HVDCP_SEL_BIT BIT(0)
+static int smbchg_get_min_parallel_current_ma(struct smbchg_chip *chip)
+{
+ int rc;
+ u8 reg, hvdcp_sel, hvdcp_sel_9v;
+
+ rc = smbchg_read(chip, ®,
+ chip->usb_chgpth_base + USBIN_HVDCP_STS, 1);
+ if (rc < 0) {
+ dev_err(chip->dev, "Couldn't read usb status rc = %d\n", rc);
+ return 0;
+ }
+ if (chip->schg_version == QPNP_SCHG_LITE) {
+ hvdcp_sel = SCHG_LITE_USBIN_HVDCP_SEL_BIT;
+ hvdcp_sel_9v = SCHG_LITE_USBIN_HVDCP_SEL_9V_BIT;
+ } else {
+ hvdcp_sel = USBIN_HVDCP_SEL_BIT;
+ hvdcp_sel_9v = USBIN_HVDCP_SEL_9V_BIT;
+ }
+
+ if ((reg & hvdcp_sel) && (reg & hvdcp_sel_9v))
+ return chip->parallel.min_9v_current_thr_ma;
+ return chip->parallel.min_current_thr_ma;
+}
+
+static bool is_hvdcp_present(struct smbchg_chip *chip)
+{
+ int rc;
+ u8 reg, hvdcp_sel;
+
+ rc = smbchg_read(chip, ®,
+ chip->usb_chgpth_base + USBIN_HVDCP_STS, 1);
+ if (rc < 0) {
+ pr_err("Couldn't read hvdcp status rc = %d\n", rc);
+ return false;
+ }
+
+ pr_smb(PR_STATUS, "HVDCP_STS = 0x%02x\n", reg);
+ /*
+ * If a valid HVDCP is detected, notify it to the usb_psy only
+ * if USB is still present.
+ */
+ if (chip->schg_version == QPNP_SCHG_LITE)
+ hvdcp_sel = SCHG_LITE_USBIN_HVDCP_SEL_BIT;
+ else
+ hvdcp_sel = USBIN_HVDCP_SEL_BIT;
+
+ if ((reg & hvdcp_sel) && is_usb_present(chip))
+ return true;
+
+ return false;
+}
+
+#define FCC_CFG 0xF2
+#define FCC_500MA_VAL 0x4
+#define FCC_MASK SMB_MASK(4, 0)
+static int smbchg_set_fastchg_current_raw(struct smbchg_chip *chip,
+ int current_ma)
+{
+ int i, rc;
+ u8 cur_val;
+
+ /* the fcc enumerations are the same as the usb currents */
+ i = find_smaller_in_array(chip->tables.usb_ilim_ma_table,
+ current_ma, chip->tables.usb_ilim_ma_len);
+ if (i < 0) {
+ dev_err(chip->dev,
+ "Cannot find %dma current_table using %d\n",
+ current_ma, CURRENT_500_MA);
+
+ rc = smbchg_sec_masked_write(chip, chip->chgr_base + FCC_CFG,
+ FCC_MASK,
+ FCC_500MA_VAL);
+ if (rc < 0)
+ dev_err(chip->dev, "Couldn't set %dmA rc=%d\n",
+ CURRENT_500_MA, rc);
+ else
+ chip->fastchg_current_ma = 500;
+ return rc;
+ }
+
+ if (chip->tables.usb_ilim_ma_table[i] == chip->fastchg_current_ma) {
+ pr_smb(PR_STATUS, "skipping fastchg current request: %d\n",
+ chip->fastchg_current_ma);
+ return 0;
+ }
+
+ cur_val = i & FCC_MASK;
+ rc = smbchg_sec_masked_write(chip, chip->chgr_base + FCC_CFG,
+ FCC_MASK, cur_val);
+ if (rc < 0) {
+ dev_err(chip->dev, "cannot write to fcc cfg rc = %d\n", rc);
+ return rc;
+ }
+ pr_smb(PR_STATUS, "fastcharge current requested %d, set to %d\n",
+ current_ma, chip->tables.usb_ilim_ma_table[cur_val]);
+
+ chip->fastchg_current_ma = chip->tables.usb_ilim_ma_table[cur_val];
+ return rc;
+}
+
+#define ICL_STS_1_REG 0x7
+#define ICL_STS_2_REG 0x9
+#define ICL_STS_MASK 0x1F
+#define AICL_SUSP_BIT BIT(6)
+#define AICL_STS_BIT BIT(5)
+#define USBIN_SUSPEND_STS_BIT BIT(3)
+#define USBIN_ACTIVE_PWR_SRC_BIT BIT(1)
+#define DCIN_ACTIVE_PWR_SRC_BIT BIT(0)
+#define PARALLEL_REENABLE_TIMER_MS 1000
+#define PARALLEL_CHG_THRESHOLD_CURRENT 1800
+static bool smbchg_is_usbin_active_pwr_src(struct smbchg_chip *chip)
+{
+ int rc;
+ u8 reg;
+
+ rc = smbchg_read(chip, ®,
+ chip->usb_chgpth_base + ICL_STS_2_REG, 1);
+ if (rc < 0) {
+ dev_err(chip->dev, "Could not read usb icl sts 2: %d\n", rc);
+ return false;
+ }
+
+ return !(reg & USBIN_SUSPEND_STS_BIT)
+ && (reg & USBIN_ACTIVE_PWR_SRC_BIT);
+}
+
+static int smbchg_parallel_usb_charging_en(struct smbchg_chip *chip, bool en)
+{
+ struct power_supply *parallel_psy = get_parallel_psy(chip);
+ union power_supply_propval pval = {0, };
+
+ if (!parallel_psy || !chip->parallel_charger_detected)
+ return 0;
+
+ pval.intval = en;
+ return power_supply_set_property(parallel_psy,
+ POWER_SUPPLY_PROP_CHARGING_ENABLED, &pval);
+}
+
+#define ESR_PULSE_CURRENT_DELTA_MA 200
+static int smbchg_sw_esr_pulse_en(struct smbchg_chip *chip, bool en)
+{
+ int rc, fg_current_now, icl_ma;
+
+ rc = get_property_from_fg(chip, POWER_SUPPLY_PROP_CURRENT_NOW,
+ &fg_current_now);
+ if (rc) {
+ pr_smb(PR_STATUS, "bms psy does not support OCV\n");
+ return 0;
+ }
+
+ 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);
+ if (rc < 0) {
+ pr_err("Couldn't Vote FCC en = %d rc = %d\n", en, rc);
+ return rc;
+ }
+ rc = smbchg_parallel_usb_charging_en(chip, !en);
+ return rc;
+}
+
+#define USB_AICL_CFG 0xF3
+#define AICL_EN_BIT BIT(2)
+static void smbchg_rerun_aicl(struct smbchg_chip *chip)
+{
+ pr_smb(PR_STATUS, "Rerunning AICL...\n");
+ smbchg_sec_masked_write(chip, chip->usb_chgpth_base + USB_AICL_CFG,
+ AICL_EN_BIT, 0);
+ /* Add a delay so that AICL successfully clears */
+ msleep(50);
+ smbchg_sec_masked_write(chip, chip->usb_chgpth_base + USB_AICL_CFG,
+ AICL_EN_BIT, AICL_EN_BIT);
+}
+
+static void taper_irq_en(struct smbchg_chip *chip, bool en)
+{
+ mutex_lock(&chip->taper_irq_lock);
+ if (en != chip->taper_irq_enabled) {
+ if (en) {
+ enable_irq(chip->taper_irq);
+ enable_irq_wake(chip->taper_irq);
+ } else {
+ disable_irq_wake(chip->taper_irq);
+ disable_irq_nosync(chip->taper_irq);
+ }
+ chip->taper_irq_enabled = en;
+ }
+ mutex_unlock(&chip->taper_irq_lock);
+}
+
+static int smbchg_get_aicl_level_ma(struct smbchg_chip *chip)
+{
+ int rc;
+ u8 reg;
+
+ rc = smbchg_read(chip, ®,
+ chip->usb_chgpth_base + ICL_STS_1_REG, 1);
+ if (rc < 0) {
+ dev_err(chip->dev, "Could not read usb icl sts 1: %d\n", rc);
+ return 0;
+ }
+ if (reg & AICL_SUSP_BIT) {
+ pr_warn("AICL suspended: %02x\n", reg);
+ return 0;
+ }
+ reg &= ICL_STS_MASK;
+ if (reg >= chip->tables.usb_ilim_ma_len) {
+ pr_warn("invalid AICL value: %02x\n", reg);
+ return 0;
+ }
+ return chip->tables.usb_ilim_ma_table[reg];
+}
+
+static void smbchg_parallel_usb_disable(struct smbchg_chip *chip)
+{
+ struct power_supply *parallel_psy = get_parallel_psy(chip);
+ union power_supply_propval pval = {0, };
+ int fcc_ma, usb_icl_ma;
+
+ if (!parallel_psy || !chip->parallel_charger_detected)
+ return;
+ pr_smb(PR_STATUS, "disabling parallel charger\n");
+ chip->parallel.last_disabled = ktime_get_boottime();
+ taper_irq_en(chip, false);
+ chip->parallel.initial_aicl_ma = 0;
+ chip->parallel.current_max_ma = 0;
+ pval.intval = SUSPEND_CURRENT_MA * 1000;
+ power_supply_set_property(parallel_psy, POWER_SUPPLY_PROP_CURRENT_MAX,
+ &pval);
+
+ pval.intval = false;
+ power_supply_set_property(parallel_psy, POWER_SUPPLY_PROP_PRESENT,
+ &pval);
+
+ fcc_ma = get_effective_result_locked(chip->fcc_votable);
+ usb_icl_ma = get_effective_result_locked(chip->usb_icl_votable);
+ if (fcc_ma < 0)
+ pr_err("no voters for fcc, skip it\n");
+ else
+ smbchg_set_fastchg_current_raw(chip, fcc_ma);
+
+ if (usb_icl_ma < 0)
+ pr_err("no voters for usb_icl, skip it\n");
+ else
+ smbchg_set_usb_current_max(chip, usb_icl_ma);
+
+ smbchg_rerun_aicl(chip);
+}
+
+#define PARALLEL_TAPER_MAX_TRIES 3
+#define PARALLEL_FCC_PERCENT_REDUCTION 75
+#define MINIMUM_PARALLEL_FCC_MA 500
+#define CHG_ERROR_BIT BIT(0)
+#define BAT_TAPER_MODE_BIT BIT(6)
+static void smbchg_parallel_usb_taper(struct smbchg_chip *chip)
+{
+ struct power_supply *parallel_psy = get_parallel_psy(chip);
+ union power_supply_propval pval = {0, };
+ int parallel_fcc_ma, tries = 0;
+ u8 reg = 0;
+
+ if (!parallel_psy || !chip->parallel_charger_detected)
+ return;
+
+ smbchg_stay_awake(chip, PM_PARALLEL_TAPER);
+try_again:
+ mutex_lock(&chip->parallel.lock);
+ if (chip->parallel.current_max_ma == 0) {
+ pr_smb(PR_STATUS, "Not parallel charging, skipping\n");
+ goto done;
+ }
+ power_supply_get_property(parallel_psy,
+ POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX, &pval);
+ tries += 1;
+ parallel_fcc_ma = pval.intval / 1000;
+ pr_smb(PR_STATUS, "try #%d parallel charger fcc = %d\n",
+ tries, parallel_fcc_ma);
+ if (parallel_fcc_ma < MINIMUM_PARALLEL_FCC_MA
+ || tries > PARALLEL_TAPER_MAX_TRIES) {
+ smbchg_parallel_usb_disable(chip);
+ goto done;
+ }
+ pval.intval = ((parallel_fcc_ma
+ * PARALLEL_FCC_PERCENT_REDUCTION) / 100);
+ pr_smb(PR_STATUS, "reducing FCC of parallel charger to %d\n",
+ pval.intval);
+ /* Change it to uA */
+ pval.intval *= 1000;
+ power_supply_set_property(parallel_psy,
+ POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX, &pval);
+ /*
+ * sleep here for 100 ms in order to make sure the charger has a chance
+ * to go back into constant current charging
+ */
+ mutex_unlock(&chip->parallel.lock);
+ msleep(100);
+
+ mutex_lock(&chip->parallel.lock);
+ if (chip->parallel.current_max_ma == 0) {
+ pr_smb(PR_STATUS, "Not parallel charging, skipping\n");
+ goto done;
+ }
+ smbchg_read(chip, ®, chip->chgr_base + RT_STS, 1);
+ if (reg & BAT_TAPER_MODE_BIT) {
+ mutex_unlock(&chip->parallel.lock);
+ goto try_again;
+ }
+ taper_irq_en(chip, true);
+done:
+ mutex_unlock(&chip->parallel.lock);
+ smbchg_relax(chip, PM_PARALLEL_TAPER);
+}
+
+static void smbchg_parallel_usb_enable(struct smbchg_chip *chip,
+ int total_current_ma)
+{
+ struct power_supply *parallel_psy = get_parallel_psy(chip);
+ union power_supply_propval pval = {0, };
+ int new_parallel_cl_ma, set_parallel_cl_ma, new_pmi_cl_ma, rc;
+ int current_table_index, target_icl_ma;
+ int fcc_ma, main_fastchg_current_ma;
+ int target_parallel_fcc_ma, supplied_parallel_fcc_ma;
+ int parallel_chg_fcc_percent;
+
+ if (!parallel_psy || !chip->parallel_charger_detected)
+ return;
+
+ pr_smb(PR_STATUS, "Attempting to enable parallel charger\n");
+ pval.intval = chip->vfloat_mv + 50;
+ rc = power_supply_set_property(parallel_psy,
+ POWER_SUPPLY_PROP_VOLTAGE_MAX, &pval);
+ if (rc < 0) {
+ dev_err(chip->dev,
+ "Couldn't set Vflt on parallel psy rc: %d\n", rc);
+ return;
+ }
+ /* Set USB ICL */
+ target_icl_ma = get_effective_result_locked(chip->usb_icl_votable);
+ if (target_icl_ma < 0) {
+ pr_err("no voters for usb_icl, skip it\n");
+ return;
+ }
+ new_parallel_cl_ma = total_current_ma
+ * (100 - smbchg_main_chg_icl_percent) / 100;
+ taper_irq_en(chip, true);
+
+ pval.intval = true;
+ power_supply_set_property(parallel_psy, POWER_SUPPLY_PROP_PRESENT,
+ &pval);
+
+ pval.intval = new_parallel_cl_ma * 1000;
+ power_supply_set_property(parallel_psy, POWER_SUPPLY_PROP_CURRENT_MAX,
+ &pval);
+
+ /* read back the real amount of current we are getting */
+ power_supply_get_property(parallel_psy,
+ POWER_SUPPLY_PROP_CURRENT_MAX, &pval);
+ set_parallel_cl_ma = pval.intval / 1000;
+ chip->parallel.current_max_ma = new_parallel_cl_ma;
+ pr_smb(PR_MISC, "Requested ICL = %d from parallel, got %d\n",
+ new_parallel_cl_ma, set_parallel_cl_ma);
+ new_pmi_cl_ma = max(0, target_icl_ma - set_parallel_cl_ma);
+ pr_smb(PR_STATUS, "New Total USB current = %d[%d, %d]\n",
+ total_current_ma, new_pmi_cl_ma,
+ set_parallel_cl_ma);
+ smbchg_set_usb_current_max(chip, new_pmi_cl_ma);
+
+ /* begin splitting the fast charge current */
+ fcc_ma = get_effective_result_locked(chip->fcc_votable);
+ if (fcc_ma < 0) {
+ pr_err("no voters for fcc, skip it\n");
+ return;
+ }
+ parallel_chg_fcc_percent = 100 - smbchg_main_chg_fcc_percent;
+ target_parallel_fcc_ma = (fcc_ma * parallel_chg_fcc_percent) / 100;
+ pval.intval = target_parallel_fcc_ma * 1000;
+ power_supply_set_property(parallel_psy,
+ POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX, &pval);
+ /* check how much actual current is supplied by the parallel charger */
+ power_supply_get_property(parallel_psy,
+ POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX, &pval);
+ supplied_parallel_fcc_ma = pval.intval / 1000;
+ pr_smb(PR_MISC, "Requested FCC = %d from parallel, got %d\n",
+ target_parallel_fcc_ma, supplied_parallel_fcc_ma);
+
+ /* then for the main charger, use the left over FCC */
+ current_table_index = find_smaller_in_array(
+ chip->tables.usb_ilim_ma_table,
+ fcc_ma - supplied_parallel_fcc_ma,
+ chip->tables.usb_ilim_ma_len);
+ main_fastchg_current_ma =
+ chip->tables.usb_ilim_ma_table[current_table_index];
+ smbchg_set_fastchg_current_raw(chip, main_fastchg_current_ma);
+ pr_smb(PR_STATUS, "FCC = %d[%d, %d]\n", fcc_ma, main_fastchg_current_ma,
+ supplied_parallel_fcc_ma);
+
+ chip->parallel.enabled_once = true;
+}
+
+static bool smbchg_is_parallel_usb_ok(struct smbchg_chip *chip,
+ int *ret_total_current_ma)
+{
+ struct power_supply *parallel_psy = get_parallel_psy(chip);
+ union power_supply_propval pval = {0, };
+ int min_current_thr_ma, rc, type;
+ int total_current_ma, current_limit_ma, parallel_cl_ma;
+ ktime_t kt_since_last_disable;
+ u8 reg;
+ int fcc_ma = get_effective_result_locked(chip->fcc_votable);
+ const char *fcc_voter
+ = get_effective_client_locked(chip->fcc_votable);
+ int usb_icl_ma = get_effective_result_locked(chip->usb_icl_votable);
+
+ if (!parallel_psy || !smbchg_parallel_en
+ || !chip->parallel_charger_detected) {
+ pr_smb(PR_STATUS, "Parallel charging not enabled\n");
+ return false;
+ }
+
+ if (fcc_ma < 0) {
+ pr_err("no voters for fcc! Can't enable parallel\n");
+ return false;
+ }
+ if (usb_icl_ma < 0) {
+ pr_err("no voters for usb_icl, Can't enable parallel\n");
+ return false;
+ }
+
+ kt_since_last_disable = ktime_sub(ktime_get_boottime(),
+ chip->parallel.last_disabled);
+ if (chip->parallel.current_max_ma == 0
+ && chip->parallel.enabled_once
+ && ktime_to_ms(kt_since_last_disable)
+ < PARALLEL_REENABLE_TIMER_MS) {
+ pr_smb(PR_STATUS, "Only been %lld since disable, skipping\n",
+ ktime_to_ms(kt_since_last_disable));
+ return false;
+ }
+
+ /*
+ * If the battery is not present, try not to change parallel charging
+ * from OFF to ON or from ON to OFF, as it could cause the device to
+ * brown out in the instant that the USB settings are changed.
+ *
+ * Only allow parallel charging check to report false (thereby turnin
+ * off parallel charging) if the battery is still there, or if parallel
+ * charging is disabled in the first place.
+ */
+ if (get_prop_charge_type(chip) != POWER_SUPPLY_CHARGE_TYPE_FAST
+ && (get_prop_batt_present(chip)
+ || chip->parallel.current_max_ma == 0)) {
+ pr_smb(PR_STATUS, "Not in fast charge, skipping\n");
+ return false;
+ }
+
+ if (get_prop_batt_health(chip) != POWER_SUPPLY_HEALTH_GOOD) {
+ pr_smb(PR_STATUS, "JEITA active, skipping\n");
+ return false;
+ }
+
+ rc = smbchg_read(chip, ®, chip->misc_base + IDEV_STS, 1);
+ if (rc < 0) {
+ dev_err(chip->dev, "Couldn't read status 5 rc = %d\n", rc);
+ return false;
+ }
+
+ type = get_type(reg);
+ if (get_usb_supply_type(type) == POWER_SUPPLY_TYPE_USB_CDP) {
+ pr_smb(PR_STATUS, "CDP adapter, skipping\n");
+ return false;
+ }
+
+ if (get_usb_supply_type(type) == POWER_SUPPLY_TYPE_USB) {
+ pr_smb(PR_STATUS, "SDP adapter, skipping\n");
+ return false;
+ }
+
+ /*
+ * If USBIN is suspended or not the active power source, do not enable
+ * parallel charging. The device may be charging off of DCIN.
+ */
+ if (!smbchg_is_usbin_active_pwr_src(chip)) {
+ pr_smb(PR_STATUS, "USB not active power source: %02x\n", reg);
+ return false;
+ }
+
+ min_current_thr_ma = smbchg_get_min_parallel_current_ma(chip);
+ if (min_current_thr_ma <= 0) {
+ pr_smb(PR_STATUS, "parallel charger unavailable for thr: %d\n",
+ min_current_thr_ma);
+ return false;
+ }
+
+ if (usb_icl_ma < min_current_thr_ma) {
+ pr_smb(PR_STATUS, "Weak USB chg skip enable: %d < %d\n",
+ usb_icl_ma, min_current_thr_ma);
+ return false;
+ }
+
+ if (!fcc_voter)
+ return false;
+ /*
+ * Suspend the parallel charger if the charging current is < 1800 mA
+ * and is not because of an ESR pulse.
+ */
+ if ((strcmp(fcc_voter, ESR_PULSE_FCC_VOTER) == 0)
+ && fcc_ma < PARALLEL_CHG_THRESHOLD_CURRENT) {
+ pr_smb(PR_STATUS, "FCC %d lower than %d\n",
+ fcc_ma,
+ PARALLEL_CHG_THRESHOLD_CURRENT);
+ return false;
+ }
+
+ current_limit_ma = smbchg_get_aicl_level_ma(chip);
+ if (current_limit_ma <= 0)
+ return false;
+
+ if (chip->parallel.initial_aicl_ma == 0) {
+ if (current_limit_ma < min_current_thr_ma) {
+ pr_smb(PR_STATUS, "Initial AICL very low: %d < %d\n",
+ current_limit_ma, min_current_thr_ma);
+ return false;
+ }
+ chip->parallel.initial_aicl_ma = current_limit_ma;
+ }
+
+ power_supply_get_property(parallel_psy,
+ POWER_SUPPLY_PROP_CURRENT_MAX, &pval);
+ parallel_cl_ma = pval.intval / 1000;
+ /*
+ * Read back the real amount of current we are getting
+ * Treat 2mA as 0 because that is the suspend current setting
+ */
+ if (parallel_cl_ma <= SUSPEND_CURRENT_MA)
+ parallel_cl_ma = 0;
+
+ /*
+ * Set the parallel charge path's input current limit (ICL)
+ * to the total current / 2
+ */
+ total_current_ma = min(current_limit_ma + parallel_cl_ma, usb_icl_ma);
+
+ if (total_current_ma < chip->parallel.initial_aicl_ma
+ - chip->parallel.allowed_lowering_ma) {
+ pr_smb(PR_STATUS,
+ "Total current reduced a lot: %d (%d + %d) < %d - %d\n",
+ total_current_ma,
+ current_limit_ma, parallel_cl_ma,
+ chip->parallel.initial_aicl_ma,
+ chip->parallel.allowed_lowering_ma);
+ return false;
+ }
+
+ *ret_total_current_ma = total_current_ma;
+ return true;
+}
+
+#define PARALLEL_CHARGER_EN_DELAY_MS 500
+static void smbchg_parallel_usb_en_work(struct work_struct *work)
+{
+ struct smbchg_chip *chip = container_of(work,
+ struct smbchg_chip,
+ parallel_en_work.work);
+ int previous_aicl_ma, total_current_ma, aicl_ma;
+ bool in_progress;
+
+ /* do a check to see if the aicl is stable */
+ previous_aicl_ma = smbchg_get_aicl_level_ma(chip);
+ msleep(PARALLEL_CHARGER_EN_DELAY_MS);
+ aicl_ma = smbchg_get_aicl_level_ma(chip);
+ if (previous_aicl_ma == aicl_ma) {
+ pr_smb(PR_STATUS, "AICL at %d\n", aicl_ma);
+ } else {
+ pr_smb(PR_STATUS,
+ "AICL changed [%d -> %d], recheck %d ms\n",
+ previous_aicl_ma, aicl_ma,
+ PARALLEL_CHARGER_EN_DELAY_MS);
+ goto recheck;
+ }
+
+ mutex_lock(&chip->parallel.lock);
+ in_progress = (chip->parallel.current_max_ma != 0);
+ if (smbchg_is_parallel_usb_ok(chip, &total_current_ma)) {
+ smbchg_parallel_usb_enable(chip, total_current_ma);
+ } else {
+ if (in_progress) {
+ pr_smb(PR_STATUS, "parallel charging unavailable\n");
+ smbchg_parallel_usb_disable(chip);
+ }
+ }
+ mutex_unlock(&chip->parallel.lock);
+ smbchg_relax(chip, PM_PARALLEL_CHECK);
+ return;
+
+recheck:
+ schedule_delayed_work(&chip->parallel_en_work, 0);
+}
+
+static void smbchg_parallel_usb_check_ok(struct smbchg_chip *chip)
+{
+ struct power_supply *parallel_psy = get_parallel_psy(chip);
+
+ if (!parallel_psy || !chip->parallel_charger_detected)
+ return;
+
+ smbchg_stay_awake(chip, PM_PARALLEL_CHECK);
+ schedule_delayed_work(&chip->parallel_en_work, 0);
+}
+
+static int charging_suspend_vote_cb(struct votable *votable, void *data,
+ int suspend,
+ const char *client)
+{
+ int rc;
+ struct smbchg_chip *chip = data;
+
+ if (suspend < 0) {
+ pr_err("No voters\n");
+ suspend = false;
+ }
+
+ rc = smbchg_charging_en(chip, !suspend);
+ if (rc < 0) {
+ dev_err(chip->dev,
+ "Couldn't configure batt chg: 0x%x rc = %d\n",
+ !suspend, rc);
+ }
+
+ return rc;
+}
+
+static int usb_suspend_vote_cb(struct votable *votable,
+ void *data,
+ int suspend,
+ const char *client)
+{
+ int rc;
+ struct smbchg_chip *chip = data;
+
+ if (suspend < 0) {
+ pr_err("No voters\n");
+ suspend = false;
+ }
+
+ rc = smbchg_usb_suspend(chip, suspend);
+ if (rc < 0)
+ return rc;
+
+ if ((strcmp(client, THERMAL_EN_VOTER) == 0)
+ || (strcmp(client, POWER_SUPPLY_EN_VOTER) == 0)
+ || (strcmp(client, USER_EN_VOTER) == 0)
+ || (strcmp(client, FAKE_BATTERY_EN_VOTER) == 0))
+ smbchg_parallel_usb_check_ok(chip);
+
+ return rc;
+}
+
+static int dc_suspend_vote_cb(struct votable *votable,
+ void *data,
+ int suspend,
+ const char *client)
+{
+ int rc;
+ struct smbchg_chip *chip = data;
+
+ if (suspend < 0) {
+ pr_err("No voters\n");
+ suspend = false;
+ }
+
+ rc = smbchg_dc_suspend(chip, suspend);
+ if (rc < 0)
+ return rc;
+
+ if (chip->dc_psy_type != -EINVAL && chip->dc_psy)
+ power_supply_changed(chip->dc_psy);
+
+ return rc;
+}
+
+static int set_fastchg_current_vote_cb(struct votable *votable,
+ void *data,
+ int fcc_ma,
+ const char *client)
+{
+ struct smbchg_chip *chip = data;
+ int rc;
+
+ if (fcc_ma < 0) {
+ pr_err("No voters\n");
+ return 0;
+ }
+
+ if (chip->parallel.current_max_ma == 0) {
+ rc = smbchg_set_fastchg_current_raw(chip, fcc_ma);
+ if (rc < 0) {
+ pr_err("Can't set FCC fcc_ma=%d rc=%d\n", fcc_ma, rc);
+ return rc;
+ }
+ }
+ /*
+ * check if parallel charging can be enabled, and if enabled,
+ * distribute the fcc
+ */
+ smbchg_parallel_usb_check_ok(chip);
+ return 0;
+}
+
+static int smbchg_set_fastchg_current_user(struct smbchg_chip *chip,
+ int current_ma)
+{
+ int rc = 0;
+
+ pr_smb(PR_STATUS, "User setting FCC to %d\n", current_ma);
+
+ rc = vote(chip->fcc_votable, BATT_TYPE_FCC_VOTER, true, current_ma);
+ if (rc < 0)
+ pr_err("Couldn't vote en rc %d\n", rc);
+ return rc;
+}
+
+static struct ilim_entry *smbchg_wipower_find_entry(struct smbchg_chip *chip,
+ struct ilim_map *map, int uv)
+{
+ int i;
+ struct ilim_entry *ret = &(chip->wipower_default.entries[0]);
+
+ for (i = 0; i < map->num; i++) {
+ if (is_between(map->entries[i].vmin_uv, map->entries[i].vmax_uv,
+ uv))
+ ret = &map->entries[i];
+ }
+ return ret;
+}
+
+#define ZIN_ICL_PT 0xFC
+#define ZIN_ICL_LV 0xFD
+#define ZIN_ICL_HV 0xFE
+#define ZIN_ICL_MASK SMB_MASK(4, 0)
+static int smbchg_dcin_ilim_config(struct smbchg_chip *chip, int offset, int ma)
+{
+ int i, rc;
+
+ i = find_smaller_in_array(chip->tables.dc_ilim_ma_table,
+ ma, chip->tables.dc_ilim_ma_len);
+
+ if (i < 0)
+ i = 0;
+
+ rc = smbchg_sec_masked_write(chip, chip->bat_if_base + offset,
+ ZIN_ICL_MASK, i);
+ if (rc)
+ dev_err(chip->dev, "Couldn't write bat if offset %d value = %d rc = %d\n",
+ offset, i, rc);
+ return rc;
+}
+
+static int smbchg_wipower_ilim_config(struct smbchg_chip *chip,
+ struct ilim_entry *ilim)
+{
+ int rc = 0;
+
+ if (chip->current_ilim.icl_pt_ma != ilim->icl_pt_ma) {
+ rc = smbchg_dcin_ilim_config(chip, ZIN_ICL_PT, ilim->icl_pt_ma);
+ if (rc)
+ dev_err(chip->dev, "failed to write batif offset %d %dma rc = %d\n",
+ ZIN_ICL_PT, ilim->icl_pt_ma, rc);
+ else
+ chip->current_ilim.icl_pt_ma = ilim->icl_pt_ma;
+ }
+
+ if (chip->current_ilim.icl_lv_ma != ilim->icl_lv_ma) {
+ rc = smbchg_dcin_ilim_config(chip, ZIN_ICL_LV, ilim->icl_lv_ma);
+ if (rc)
+ dev_err(chip->dev, "failed to write batif offset %d %dma rc = %d\n",
+ ZIN_ICL_LV, ilim->icl_lv_ma, rc);
+ else
+ chip->current_ilim.icl_lv_ma = ilim->icl_lv_ma;
+ }
+
+ if (chip->current_ilim.icl_hv_ma != ilim->icl_hv_ma) {
+ rc = smbchg_dcin_ilim_config(chip, ZIN_ICL_HV, ilim->icl_hv_ma);
+ if (rc)
+ dev_err(chip->dev, "failed to write batif offset %d %dma rc = %d\n",
+ ZIN_ICL_HV, ilim->icl_hv_ma, rc);
+ else
+ chip->current_ilim.icl_hv_ma = ilim->icl_hv_ma;
+ }
+ return rc;
+}
+
+static void btm_notify_dcin(enum qpnp_tm_state state, void *ctx);
+static int smbchg_wipower_dcin_btm_configure(struct smbchg_chip *chip,
+ struct ilim_entry *ilim)
+{
+ int rc;
+
+ if (ilim->vmin_uv == chip->current_ilim.vmin_uv
+ && ilim->vmax_uv == chip->current_ilim.vmax_uv)
+ return 0;
+
+ chip->param.channel = DCIN;
+ chip->param.btm_ctx = chip;
+ if (wipower_dcin_interval < ADC_MEAS1_INTERVAL_0MS)
+ wipower_dcin_interval = ADC_MEAS1_INTERVAL_0MS;
+
+ if (wipower_dcin_interval > ADC_MEAS1_INTERVAL_16S)
+ wipower_dcin_interval = ADC_MEAS1_INTERVAL_16S;
+
+ chip->param.timer_interval = wipower_dcin_interval;
+ chip->param.threshold_notification = &btm_notify_dcin;
+ chip->param.high_thr = ilim->vmax_uv + wipower_dcin_hyst_uv;
+ chip->param.low_thr = ilim->vmin_uv - wipower_dcin_hyst_uv;
+ chip->param.state_request = ADC_TM_HIGH_LOW_THR_ENABLE;
+ rc = qpnp_vadc_channel_monitor(chip->vadc_dev, &chip->param);
+ if (rc) {
+ dev_err(chip->dev, "Couldn't configure btm for dcin rc = %d\n",
+ rc);
+ } else {
+ chip->current_ilim.vmin_uv = ilim->vmin_uv;
+ chip->current_ilim.vmax_uv = ilim->vmax_uv;
+ pr_smb(PR_STATUS, "btm ilim = (%duV %duV %dmA %dmA %dmA)\n",
+ ilim->vmin_uv, ilim->vmax_uv,
+ ilim->icl_pt_ma, ilim->icl_lv_ma, ilim->icl_hv_ma);
+ }
+ return rc;
+}
+
+static int smbchg_wipower_icl_configure(struct smbchg_chip *chip,
+ int dcin_uv, bool div2)
+{
+ int rc = 0;
+ struct ilim_map *map = div2 ? &chip->wipower_div2 : &chip->wipower_pt;
+ struct ilim_entry *ilim = smbchg_wipower_find_entry(chip, map, dcin_uv);
+
+ rc = smbchg_wipower_ilim_config(chip, ilim);
+ if (rc) {
+ dev_err(chip->dev, "failed to config ilim rc = %d, dcin_uv = %d , div2 = %d, ilim = (%duV %duV %dmA %dmA %dmA)\n",
+ rc, dcin_uv, div2,
+ ilim->vmin_uv, ilim->vmax_uv,
+ ilim->icl_pt_ma, ilim->icl_lv_ma, ilim->icl_hv_ma);
+ return rc;
+ }
+
+ rc = smbchg_wipower_dcin_btm_configure(chip, ilim);
+ if (rc) {
+ dev_err(chip->dev, "failed to config btm rc = %d, dcin_uv = %d , div2 = %d, ilim = (%duV %duV %dmA %dmA %dmA)\n",
+ rc, dcin_uv, div2,
+ ilim->vmin_uv, ilim->vmax_uv,
+ ilim->icl_pt_ma, ilim->icl_lv_ma, ilim->icl_hv_ma);
+ return rc;
+ }
+ chip->wipower_configured = true;
+ return 0;
+}
+
+static void smbchg_wipower_icl_deconfigure(struct smbchg_chip *chip)
+{
+ int rc;
+ struct ilim_entry *ilim = &(chip->wipower_default.entries[0]);
+
+ if (!chip->wipower_configured)
+ return;
+
+ rc = smbchg_wipower_ilim_config(chip, ilim);
+ if (rc)
+ dev_err(chip->dev, "Couldn't config default ilim rc = %d\n",
+ rc);
+
+ rc = qpnp_vadc_end_channel_monitor(chip->vadc_dev);
+ if (rc)
+ dev_err(chip->dev, "Couldn't de configure btm for dcin rc = %d\n",
+ rc);
+
+ chip->wipower_configured = false;
+ chip->current_ilim.vmin_uv = 0;
+ chip->current_ilim.vmax_uv = 0;
+ chip->current_ilim.icl_pt_ma = ilim->icl_pt_ma;
+ chip->current_ilim.icl_lv_ma = ilim->icl_lv_ma;
+ chip->current_ilim.icl_hv_ma = ilim->icl_hv_ma;
+ pr_smb(PR_WIPOWER, "De config btm\n");
+}
+
+#define FV_STS 0x0C
+#define DIV2_ACTIVE BIT(7)
+static void __smbchg_wipower_check(struct smbchg_chip *chip)
+{
+ int chg_type;
+ bool usb_present, dc_present;
+ int rc;
+ int dcin_uv;
+ bool div2;
+ struct qpnp_vadc_result adc_result;
+ u8 reg;
+
+ if (!wipower_dyn_icl_en) {
+ smbchg_wipower_icl_deconfigure(chip);
+ return;
+ }
+
+ chg_type = get_prop_charge_type(chip);
+ usb_present = is_usb_present(chip);
+ dc_present = is_dc_present(chip);
+ if (chg_type != POWER_SUPPLY_CHARGE_TYPE_NONE
+ && !usb_present
+ && dc_present
+ && chip->dc_psy_type == POWER_SUPPLY_TYPE_WIPOWER) {
+ rc = qpnp_vadc_read(chip->vadc_dev, DCIN, &adc_result);
+ if (rc) {
+ pr_smb(PR_STATUS, "error DCIN read rc = %d\n", rc);
+ return;
+ }
+ dcin_uv = adc_result.physical;
+
+ /* check div_by_2 */
+ rc = smbchg_read(chip, ®, chip->chgr_base + FV_STS, 1);
+ if (rc) {
+ pr_smb(PR_STATUS, "error DCIN read rc = %d\n", rc);
+ return;
+ }
+ div2 = !!(reg & DIV2_ACTIVE);
+
+ pr_smb(PR_WIPOWER,
+ "config ICL chg_type = %d usb = %d dc = %d dcin_uv(adc_code) = %d (0x%x) div2 = %d\n",
+ chg_type, usb_present, dc_present, dcin_uv,
+ adc_result.adc_code, div2);
+ smbchg_wipower_icl_configure(chip, dcin_uv, div2);
+ } else {
+ pr_smb(PR_WIPOWER,
+ "deconfig ICL chg_type = %d usb = %d dc = %d\n",
+ chg_type, usb_present, dc_present);
+ smbchg_wipower_icl_deconfigure(chip);
+ }
+}
+
+static void smbchg_wipower_check(struct smbchg_chip *chip)
+{
+ if (!chip->wipower_dyn_icl_avail)
+ return;
+
+ mutex_lock(&chip->wipower_config);
+ __smbchg_wipower_check(chip);
+ mutex_unlock(&chip->wipower_config);
+}
+
+static void btm_notify_dcin(enum qpnp_tm_state state, void *ctx)
+{
+ struct smbchg_chip *chip = ctx;
+
+ mutex_lock(&chip->wipower_config);
+ pr_smb(PR_WIPOWER, "%s state\n",
+ state == ADC_TM_LOW_STATE ? "low" : "high");
+ chip->current_ilim.vmin_uv = 0;
+ chip->current_ilim.vmax_uv = 0;
+ __smbchg_wipower_check(chip);
+ mutex_unlock(&chip->wipower_config);
+}
+
+static int force_dcin_icl_write(void *data, u64 val)
+{
+ struct smbchg_chip *chip = data;
+
+ smbchg_wipower_check(chip);
+ return 0;
+}
+DEFINE_SIMPLE_ATTRIBUTE(force_dcin_icl_ops, NULL,
+ force_dcin_icl_write, "0x%02llx\n");
+
+/*
+ * set the dc charge path's maximum allowed current draw
+ * that may be limited by the system's thermal level
+ */
+static int set_dc_current_limit_vote_cb(struct votable *votable,
+ void *data,
+ int icl_ma,
+ const char *client)
+{
+ struct smbchg_chip *chip = data;
+
+ if (icl_ma < 0) {
+ pr_err("No voters\n");
+ return 0;
+ }
+
+ return smbchg_set_dc_current_max(chip, icl_ma);
+}
+
+/*
+ * set the usb charge path's maximum allowed current draw
+ * that may be limited by the system's thermal level
+ */
+static int set_usb_current_limit_vote_cb(struct votable *votable,
+ void *data,
+ int icl_ma,
+ const char *client)
+{
+ struct smbchg_chip *chip = data;
+ int rc, aicl_ma;
+ const char *effective_id;
+
+ if (icl_ma < 0) {
+ pr_err("No voters\n");
+ return 0;
+ }
+ effective_id = get_effective_client_locked(chip->usb_icl_votable);
+
+ if (!effective_id)
+ return 0;
+
+ /* disable parallel charging if HVDCP is voting for 300mA */
+ if (strcmp(effective_id, HVDCP_ICL_VOTER) == 0)
+ smbchg_parallel_usb_disable(chip);
+
+ if (chip->parallel.current_max_ma == 0) {
+ rc = smbchg_set_usb_current_max(chip, icl_ma);
+ if (rc) {
+ pr_err("Failed to set usb current max: %d\n", rc);
+ return rc;
+ }
+ }
+
+ /* skip the aicl rerun if hvdcp icl voter is active */
+ if (strcmp(effective_id, HVDCP_ICL_VOTER) == 0)
+ return 0;
+
+ aicl_ma = smbchg_get_aicl_level_ma(chip);
+ if (icl_ma > aicl_ma)
+ smbchg_rerun_aicl(chip);
+ smbchg_parallel_usb_check_ok(chip);
+ return 0;
+}
+
+static int smbchg_system_temp_level_set(struct smbchg_chip *chip,
+ int lvl_sel)
+{
+ int rc = 0;
+ int prev_therm_lvl;
+ int thermal_icl_ma;
+
+ if (!chip->thermal_mitigation) {
+ dev_err(chip->dev, "Thermal mitigation not supported\n");
+ return -EINVAL;
+ }
+
+ if (lvl_sel < 0) {
+ dev_err(chip->dev, "Unsupported level selected %d\n", lvl_sel);
+ return -EINVAL;
+ }
+
+ if (lvl_sel >= chip->thermal_levels) {
+ dev_err(chip->dev, "Unsupported level selected %d forcing %d\n",
+ lvl_sel, chip->thermal_levels - 1);
+ lvl_sel = chip->thermal_levels - 1;
+ }
+
+ if (lvl_sel == chip->therm_lvl_sel)
+ return 0;
+
+ mutex_lock(&chip->therm_lvl_lock);
+ prev_therm_lvl = chip->therm_lvl_sel;
+ chip->therm_lvl_sel = lvl_sel;
+ if (chip->therm_lvl_sel == (chip->thermal_levels - 1)) {
+ /*
+ * Disable charging if highest value selected by
+ * setting the DC and USB path in suspend
+ */
+ rc = vote(chip->dc_suspend_votable, THERMAL_EN_VOTER, true, 0);
+ if (rc < 0) {
+ dev_err(chip->dev,
+ "Couldn't set dc suspend rc %d\n", rc);
+ goto out;
+ }
+ rc = vote(chip->usb_suspend_votable, THERMAL_EN_VOTER, true, 0);
+ if (rc < 0) {
+ dev_err(chip->dev,
+ "Couldn't set usb suspend rc %d\n", rc);
+ goto out;
+ }
+ goto out;
+ }
+
+ if (chip->therm_lvl_sel == 0) {
+ rc = vote(chip->usb_icl_votable, THERMAL_ICL_VOTER, false, 0);
+ if (rc < 0)
+ pr_err("Couldn't disable USB thermal ICL vote rc=%d\n",
+ rc);
+
+ rc = vote(chip->dc_icl_votable, THERMAL_ICL_VOTER, false, 0);
+ if (rc < 0)
+ pr_err("Couldn't disable DC thermal ICL vote rc=%d\n",
+ rc);
+ } else {
+ thermal_icl_ma =
+ (int)chip->thermal_mitigation[chip->therm_lvl_sel];
+ rc = vote(chip->usb_icl_votable, THERMAL_ICL_VOTER, true,
+ thermal_icl_ma);
+ if (rc < 0)
+ pr_err("Couldn't vote for USB thermal ICL rc=%d\n", rc);
+
+ rc = vote(chip->dc_icl_votable, THERMAL_ICL_VOTER, true,
+ thermal_icl_ma);
+ if (rc < 0)
+ pr_err("Couldn't vote for DC thermal ICL rc=%d\n", rc);
+ }
+
+ if (prev_therm_lvl == chip->thermal_levels - 1) {
+ /*
+ * If previously highest value was selected charging must have
+ * been disabed. Enable charging by taking the DC and USB path
+ * out of suspend.
+ */
+ rc = vote(chip->dc_suspend_votable, THERMAL_EN_VOTER, false, 0);
+ if (rc < 0) {
+ dev_err(chip->dev,
+ "Couldn't set dc suspend rc %d\n", rc);
+ goto out;
+ }
+ rc = vote(chip->usb_suspend_votable, THERMAL_EN_VOTER,
+ false, 0);
+ if (rc < 0) {
+ dev_err(chip->dev,
+ "Couldn't set usb suspend rc %d\n", rc);
+ goto out;
+ }
+ }
+out:
+ mutex_unlock(&chip->therm_lvl_lock);
+ return rc;
+}
+
+static int smbchg_ibat_ocp_threshold_ua = 4500000;
+module_param(smbchg_ibat_ocp_threshold_ua, int, 0644);
+
+#define UCONV 1000000LL
+#define MCONV 1000LL
+#define FLASH_V_THRESHOLD 3000000
+#define FLASH_VDIP_MARGIN 100000
+#define VPH_FLASH_VDIP (FLASH_V_THRESHOLD + FLASH_VDIP_MARGIN)
+#define BUCK_EFFICIENCY 800LL
+static int smbchg_calc_max_flash_current(struct smbchg_chip *chip)
+{
+ int ocv_uv, esr_uohm, rbatt_uohm, ibat_now, rc;
+ int64_t ibat_flash_ua, avail_flash_ua, avail_flash_power_fw;
+ int64_t ibat_safe_ua, vin_flash_uv, vph_flash_uv;
+
+ rc = get_property_from_fg(chip, POWER_SUPPLY_PROP_VOLTAGE_OCV, &ocv_uv);
+ if (rc) {
+ pr_smb(PR_STATUS, "bms psy does not support OCV\n");
+ return 0;
+ }
+
+ rc = get_property_from_fg(chip, POWER_SUPPLY_PROP_RESISTANCE,
+ &esr_uohm);
+ if (rc) {
+ pr_smb(PR_STATUS, "bms psy does not support resistance\n");
+ return 0;
+ }
+
+ rc = msm_bcl_read(BCL_PARAM_CURRENT, &ibat_now);
+ if (rc) {
+ pr_smb(PR_STATUS, "BCL current read failed: %d\n", rc);
+ return 0;
+ }
+
+ rbatt_uohm = esr_uohm + chip->rpara_uohm + chip->rslow_uohm;
+ /*
+ * Calculate the maximum current that can pulled out of the battery
+ * before the battery voltage dips below a safe threshold.
+ */
+ ibat_safe_ua = div_s64((ocv_uv - VPH_FLASH_VDIP) * UCONV,
+ rbatt_uohm);
+
+ if (ibat_safe_ua <= smbchg_ibat_ocp_threshold_ua) {
+ /*
+ * If the calculated current is below the OCP threshold, then
+ * use it as the possible flash current.
+ */
+ ibat_flash_ua = ibat_safe_ua - ibat_now;
+ vph_flash_uv = VPH_FLASH_VDIP;
+ } else {
+ /*
+ * If the calculated current is above the OCP threshold, then
+ * use the ocp threshold instead.
+ *
+ * Any higher current will be tripping the battery OCP.
+ */
+ ibat_flash_ua = smbchg_ibat_ocp_threshold_ua - ibat_now;
+ vph_flash_uv = ocv_uv - div64_s64((int64_t)rbatt_uohm
+ * smbchg_ibat_ocp_threshold_ua, UCONV);
+ }
+ /* Calculate the input voltage of the flash module. */
+ vin_flash_uv = max((chip->vled_max_uv + 500000LL),
+ div64_s64((vph_flash_uv * 1200), 1000));
+ /* Calculate the available power for the flash module. */
+ avail_flash_power_fw = BUCK_EFFICIENCY * vph_flash_uv * ibat_flash_ua;
+ /*
+ * Calculate the available amount of current the flash module can draw
+ * before collapsing the battery. (available power/ flash input voltage)
+ */
+ avail_flash_ua = div64_s64(avail_flash_power_fw, vin_flash_uv * MCONV);
+ pr_smb(PR_MISC,
+ "avail_iflash=%lld, ocv=%d, ibat=%d, rbatt=%d\n",
+ avail_flash_ua, ocv_uv, ibat_now, rbatt_uohm);
+ return (int)avail_flash_ua;
+}
+
+#define FCC_CMP_CFG 0xF3
+#define FCC_COMP_MASK SMB_MASK(1, 0)
+static int smbchg_fastchg_current_comp_set(struct smbchg_chip *chip,
+ int comp_current)
+{
+ int rc;
+ u8 i;
+
+ for (i = 0; i < chip->tables.fcc_comp_len; i++)
+ if (comp_current == chip->tables.fcc_comp_table[i])
+ break;
+
+ if (i >= chip->tables.fcc_comp_len)
+ return -EINVAL;
+
+ rc = smbchg_sec_masked_write(chip, chip->chgr_base + FCC_CMP_CFG,
+ FCC_COMP_MASK, i);
+
+ if (rc)
+ dev_err(chip->dev, "Couldn't set fastchg current comp rc = %d\n",
+ rc);
+
+ return rc;
+}
+
+#define CFG_TCC_REG 0xF9
+#define CHG_ITERM_MASK SMB_MASK(2, 0)
+static int smbchg_iterm_set(struct smbchg_chip *chip, int iterm_ma)
+{
+ int rc;
+ u8 reg;
+
+ reg = find_closest_in_array(
+ chip->tables.iterm_ma_table,
+ chip->tables.iterm_ma_len,
+ iterm_ma);
+
+ rc = smbchg_sec_masked_write(chip,
+ chip->chgr_base + CFG_TCC_REG,
+ CHG_ITERM_MASK, reg);
+ if (rc) {
+ dev_err(chip->dev,
+ "Couldn't set iterm rc = %d\n", rc);
+ return rc;
+ }
+ pr_smb(PR_STATUS, "set tcc (%d) to 0x%02x\n",
+ iterm_ma, reg);
+ chip->iterm_ma = iterm_ma;
+
+ return 0;
+}
+
+#define FV_CMP_CFG 0xF5
+#define FV_COMP_MASK SMB_MASK(5, 0)
+static int smbchg_float_voltage_comp_set(struct smbchg_chip *chip, int code)
+{
+ int rc;
+ u8 val;
+
+ val = code & FV_COMP_MASK;
+ rc = smbchg_sec_masked_write(chip, chip->chgr_base + FV_CMP_CFG,
+ FV_COMP_MASK, val);
+
+ if (rc)
+ dev_err(chip->dev, "Couldn't set float voltage comp rc = %d\n",
+ rc);
+
+ return rc;
+}
+
+#define VFLOAT_CFG_REG 0xF4
+#define MIN_FLOAT_MV 3600
+#define MAX_FLOAT_MV 4500
+#define VFLOAT_MASK SMB_MASK(5, 0)
+
+#define MID_RANGE_FLOAT_MV_MIN 3600
+#define MID_RANGE_FLOAT_MIN_VAL 0x05
+#define MID_RANGE_FLOAT_STEP_MV 20
+
+#define HIGH_RANGE_FLOAT_MIN_MV 4340
+#define HIGH_RANGE_FLOAT_MIN_VAL 0x2A
+#define HIGH_RANGE_FLOAT_STEP_MV 10
+
+#define VHIGH_RANGE_FLOAT_MIN_MV 4360
+#define VHIGH_RANGE_FLOAT_MIN_VAL 0x2C
+#define VHIGH_RANGE_FLOAT_STEP_MV 20
+static int smbchg_float_voltage_set(struct smbchg_chip *chip, int vfloat_mv)
+{
+ struct power_supply *parallel_psy = get_parallel_psy(chip);
+ union power_supply_propval prop;
+ int rc, delta;
+ u8 temp;
+
+ if ((vfloat_mv < MIN_FLOAT_MV) || (vfloat_mv > MAX_FLOAT_MV)) {
+ dev_err(chip->dev, "bad float voltage mv =%d asked to set\n",
+ vfloat_mv);
+ return -EINVAL;
+ }
+
+ if (vfloat_mv <= HIGH_RANGE_FLOAT_MIN_MV) {
+ /* mid range */
+ delta = vfloat_mv - MID_RANGE_FLOAT_MV_MIN;
+ temp = MID_RANGE_FLOAT_MIN_VAL + delta
+ / MID_RANGE_FLOAT_STEP_MV;
+ vfloat_mv -= delta % MID_RANGE_FLOAT_STEP_MV;
+ } else if (vfloat_mv <= VHIGH_RANGE_FLOAT_MIN_MV) {
+ /* high range */
+ delta = vfloat_mv - HIGH_RANGE_FLOAT_MIN_MV;
+ temp = HIGH_RANGE_FLOAT_MIN_VAL + delta
+ / HIGH_RANGE_FLOAT_STEP_MV;
+ vfloat_mv -= delta % HIGH_RANGE_FLOAT_STEP_MV;
+ } else {
+ /* very high range */
+ delta = vfloat_mv - VHIGH_RANGE_FLOAT_MIN_MV;
+ temp = VHIGH_RANGE_FLOAT_MIN_VAL + delta
+ / VHIGH_RANGE_FLOAT_STEP_MV;
+ vfloat_mv -= delta % VHIGH_RANGE_FLOAT_STEP_MV;
+ }
+
+ if (parallel_psy) {
+ prop.intval = vfloat_mv + 50;
+ rc = power_supply_set_property(parallel_psy,
+ POWER_SUPPLY_PROP_VOLTAGE_MAX, &prop);
+ if (rc)
+ dev_err(chip->dev, "Couldn't set float voltage on parallel psy rc: %d\n",
+ rc);
+ }
+
+ rc = smbchg_sec_masked_write(chip, chip->chgr_base + VFLOAT_CFG_REG,
+ VFLOAT_MASK, temp);
+
+ if (rc)
+ dev_err(chip->dev, "Couldn't set float voltage rc = %d\n", rc);
+ else
+ chip->vfloat_mv = vfloat_mv;
+
+ return rc;
+}
+
+static int smbchg_float_voltage_get(struct smbchg_chip *chip)
+{
+ return chip->vfloat_mv;
+}
+
+#define SFT_CFG 0xFD
+#define SFT_EN_MASK SMB_MASK(5, 4)
+#define SFT_TO_MASK SMB_MASK(3, 2)
+#define PRECHG_SFT_TO_MASK SMB_MASK(1, 0)
+#define SFT_TIMER_DISABLE_BIT BIT(5)
+#define PRECHG_SFT_TIMER_DISABLE_BIT BIT(4)
+#define SAFETY_TIME_MINUTES_SHIFT 2
+static int smbchg_safety_timer_enable(struct smbchg_chip *chip, bool enable)
+{
+ int rc;
+ u8 reg;
+
+ if (enable == chip->safety_timer_en)
+ return 0;
+
+ if (enable)
+ reg = 0;
+ else
+ reg = SFT_TIMER_DISABLE_BIT | PRECHG_SFT_TIMER_DISABLE_BIT;
+
+ rc = smbchg_sec_masked_write(chip, chip->chgr_base + SFT_CFG,
+ SFT_EN_MASK, reg);
+ if (rc < 0) {
+ dev_err(chip->dev,
+ "Couldn't %s safety timer rc = %d\n",
+ enable ? "enable" : "disable", rc);
+ return rc;
+ }
+ chip->safety_timer_en = enable;
+ return 0;
+}
+
+enum skip_reason {
+ REASON_OTG_ENABLED = BIT(0),
+ REASON_FLASH_ENABLED = BIT(1)
+};
+
+#define BAT_IF_TRIM7_REG 0xF7
+#define CFG_750KHZ_BIT BIT(1)
+#define MISC_CFG_NTC_VOUT_REG 0xF3
+#define CFG_NTC_VOUT_FSW_BIT BIT(0)
+static int smbchg_switch_buck_frequency(struct smbchg_chip *chip,
+ bool flash_active)
+{
+ int rc;
+
+ if (!(chip->wa_flags & SMBCHG_FLASH_BUCK_SWITCH_FREQ_WA))
+ return 0;
+
+ if (chip->flash_active == flash_active) {
+ pr_smb(PR_STATUS, "Fsw not changed, flash_active: %d\n",
+ flash_active);
+ return 0;
+ }
+
+ /*
+ * As per the systems team recommendation, before the flash fires,
+ * buck switching frequency(Fsw) needs to be increased to 1MHz. Once the
+ * flash is disabled, Fsw needs to be set back to 750KHz.
+ */
+ rc = smbchg_sec_masked_write(chip, chip->misc_base +
+ MISC_CFG_NTC_VOUT_REG, CFG_NTC_VOUT_FSW_BIT,
+ flash_active ? CFG_NTC_VOUT_FSW_BIT : 0);
+ if (rc < 0) {
+ dev_err(chip->dev, "Couldn't set switching frequency multiplier rc=%d\n",
+ rc);
+ return rc;
+ }
+
+ rc = smbchg_sec_masked_write(chip, chip->bat_if_base + BAT_IF_TRIM7_REG,
+ CFG_750KHZ_BIT, flash_active ? 0 : CFG_750KHZ_BIT);
+ if (rc < 0) {
+ dev_err(chip->dev, "Cannot set switching freq: %d\n", rc);
+ return rc;
+ }
+
+ pr_smb(PR_STATUS, "Fsw @ %sHz\n", flash_active ? "1M" : "750K");
+ chip->flash_active = flash_active;
+ return 0;
+}
+
+#define OTG_TRIM6 0xF6
+#define TR_ENB_SKIP_BIT BIT(2)
+#define OTG_EN_BIT BIT(0)
+static int smbchg_otg_pulse_skip_disable(struct smbchg_chip *chip,
+ enum skip_reason reason, bool disable)
+{
+ int rc;
+ bool disabled;
+
+ disabled = !!chip->otg_pulse_skip_dis;
+ pr_smb(PR_STATUS, "%s pulse skip, reason %d\n",
+ disable ? "disabling" : "enabling", reason);
+ if (disable)
+ chip->otg_pulse_skip_dis |= reason;
+ else
+ chip->otg_pulse_skip_dis &= ~reason;
+ if (disabled == !!chip->otg_pulse_skip_dis)
+ return 0;
+ disabled = !!chip->otg_pulse_skip_dis;
+
+ rc = smbchg_sec_masked_write(chip, chip->otg_base + OTG_TRIM6,
+ TR_ENB_SKIP_BIT, disabled ? TR_ENB_SKIP_BIT : 0);
+ if (rc < 0) {
+ dev_err(chip->dev,
+ "Couldn't %s otg pulse skip rc = %d\n",
+ disabled ? "disable" : "enable", rc);
+ return rc;
+ }
+ pr_smb(PR_STATUS, "%s pulse skip\n", disabled ? "disabled" : "enabled");
+ return 0;
+}
+
+#define LOW_PWR_OPTIONS_REG 0xFF
+#define FORCE_TLIM_BIT BIT(4)
+static int smbchg_force_tlim_en(struct smbchg_chip *chip, bool enable)
+{
+ int rc;
+
+ rc = smbchg_sec_masked_write(chip, chip->otg_base + LOW_PWR_OPTIONS_REG,
+ FORCE_TLIM_BIT, enable ? FORCE_TLIM_BIT : 0);
+ if (rc < 0) {
+ dev_err(chip->dev,
+ "Couldn't %s otg force tlim rc = %d\n",
+ enable ? "enable" : "disable", rc);
+ return rc;
+ }
+ return rc;
+}
+
+static void smbchg_vfloat_adjust_check(struct smbchg_chip *chip)
+{
+ if (!chip->use_vfloat_adjustments)
+ return;
+
+ smbchg_stay_awake(chip, PM_REASON_VFLOAT_ADJUST);
+ pr_smb(PR_STATUS, "Starting vfloat adjustments\n");
+ schedule_delayed_work(&chip->vfloat_adjust_work, 0);
+}
+
+#define FV_STS_REG 0xC
+#define AICL_INPUT_STS_BIT BIT(6)
+static bool smbchg_is_input_current_limited(struct smbchg_chip *chip)
+{
+ int rc;
+ u8 reg;
+
+ rc = smbchg_read(chip, ®, chip->chgr_base + FV_STS_REG, 1);
+ if (rc < 0) {
+ dev_err(chip->dev, "Couldn't read FV_STS rc=%d\n", rc);
+ return false;
+ }
+
+ return !!(reg & AICL_INPUT_STS_BIT);
+}
+
+#define SW_ESR_PULSE_MS 1500
+static void smbchg_cc_esr_wa_check(struct smbchg_chip *chip)
+{
+ int rc, esr_count;
+
+ if (!(chip->wa_flags & SMBCHG_CC_ESR_WA))
+ return;
+
+ if (!is_usb_present(chip) && !is_dc_present(chip)) {
+ pr_smb(PR_STATUS, "No inputs present, skipping\n");
+ return;
+ }
+
+ if (get_prop_charge_type(chip) != POWER_SUPPLY_CHARGE_TYPE_FAST) {
+ pr_smb(PR_STATUS, "Not in fast charge, skipping\n");
+ return;
+ }
+
+ if (!smbchg_is_input_current_limited(chip)) {
+ pr_smb(PR_STATUS, "Not input current limited, skipping\n");
+ return;
+ }
+
+ set_property_on_fg(chip, POWER_SUPPLY_PROP_UPDATE_NOW, 1);
+ rc = get_property_from_fg(chip,
+ POWER_SUPPLY_PROP_ESR_COUNT, &esr_count);
+ if (rc) {
+ pr_smb(PR_STATUS,
+ "could not read ESR counter rc = %d\n", rc);
+ return;
+ }
+
+ /*
+ * The esr_count is counting down the number of fuel gauge cycles
+ * before a ESR pulse is needed.
+ *
+ * After a successful ESR pulse, this count is reset to some
+ * high number like 28. If this reaches 0, then the fuel gauge
+ * hardware should force a ESR pulse.
+ *
+ * However, if the device is in constant current charge mode while
+ * being input current limited, the ESR pulse will not affect the
+ * battery current, so the measurement will fail.
+ *
+ * As a failsafe, force a manual ESR pulse if this value is read as
+ * 0.
+ */
+ if (esr_count != 0) {
+ pr_smb(PR_STATUS, "ESR count is not zero, skipping\n");
+ return;
+ }
+
+ pr_smb(PR_STATUS, "Lowering charge current for ESR pulse\n");
+ smbchg_stay_awake(chip, PM_ESR_PULSE);
+ smbchg_sw_esr_pulse_en(chip, true);
+ msleep(SW_ESR_PULSE_MS);
+ pr_smb(PR_STATUS, "Raising charge current for ESR pulse\n");
+ smbchg_relax(chip, PM_ESR_PULSE);
+ smbchg_sw_esr_pulse_en(chip, false);
+}
+
+static void smbchg_soc_changed(struct smbchg_chip *chip)
+{
+ smbchg_cc_esr_wa_check(chip);
+}
+
+#define DC_AICL_CFG 0xF3
+#define MISC_TRIM_OPT_15_8 0xF5
+#define USB_AICL_DEGLITCH_MASK (BIT(5) | BIT(4) | BIT(3))
+#define USB_AICL_DEGLITCH_SHORT (BIT(5) | BIT(4) | BIT(3))
+#define USB_AICL_DEGLITCH_LONG 0
+#define DC_AICL_DEGLITCH_MASK (BIT(5) | BIT(4) | BIT(3))
+#define DC_AICL_DEGLITCH_SHORT (BIT(5) | BIT(4) | BIT(3))
+#define DC_AICL_DEGLITCH_LONG 0
+#define AICL_RERUN_MASK (BIT(5) | BIT(4))
+#define AICL_RERUN_ON (BIT(5) | BIT(4))
+#define AICL_RERUN_OFF 0
+
+static int smbchg_hw_aicl_rerun_enable_indirect_cb(struct votable *votable,
+ void *data,
+ int enable,
+ const char *client)
+{
+ int rc = 0;
+ struct smbchg_chip *chip = data;
+
+ if (enable < 0) {
+ pr_err("No voters\n");
+ enable = 0;
+ }
+ /*
+ * If the indirect voting result of all the clients is to enable hw aicl
+ * rerun, then remove our vote to disable hw aicl rerun
+ */
+ rc = vote(chip->hw_aicl_rerun_disable_votable,
+ HW_AICL_RERUN_ENABLE_INDIRECT_VOTER, !enable, 0);
+ if (rc < 0) {
+ pr_err("Couldn't vote for hw rerun rc= %d\n", rc);
+ return rc;
+ }
+
+ return rc;
+}
+
+static int smbchg_hw_aicl_rerun_disable_cb(struct votable *votable, void *data,
+ int disable,
+ const char *client)
+{
+ int rc = 0;
+ struct smbchg_chip *chip = data;
+
+ if (disable < 0) {
+ pr_err("No voters\n");
+ disable = 0;
+ }
+
+ rc = smbchg_sec_masked_write(chip,
+ chip->misc_base + MISC_TRIM_OPT_15_8,
+ AICL_RERUN_MASK, disable ? AICL_RERUN_OFF : AICL_RERUN_ON);
+ if (rc < 0)
+ pr_err("Couldn't write to MISC_TRIM_OPTIONS_15_8 rc=%d\n", rc);
+
+ return rc;
+}
+
+static int smbchg_aicl_deglitch_config_cb(struct votable *votable, void *data,
+ int shorter,
+ const char *client)
+{
+ int rc = 0;
+ struct smbchg_chip *chip = data;
+
+ if (shorter < 0) {
+ pr_err("No voters\n");
+ shorter = 0;
+ }
+
+ rc = smbchg_sec_masked_write(chip,
+ chip->usb_chgpth_base + USB_AICL_CFG,
+ USB_AICL_DEGLITCH_MASK,
+ shorter ? USB_AICL_DEGLITCH_SHORT : USB_AICL_DEGLITCH_LONG);
+ if (rc < 0) {
+ pr_err("Couldn't write to USB_AICL_CFG rc=%d\n", rc);
+ return rc;
+ }
+ rc = smbchg_sec_masked_write(chip,
+ chip->dc_chgpth_base + DC_AICL_CFG,
+ DC_AICL_DEGLITCH_MASK,
+ shorter ? DC_AICL_DEGLITCH_SHORT : DC_AICL_DEGLITCH_LONG);
+ if (rc < 0) {
+ pr_err("Couldn't write to DC_AICL_CFG rc=%d\n", rc);
+ return rc;
+ }
+ return rc;
+}
+
+static void smbchg_aicl_deglitch_wa_en(struct smbchg_chip *chip, bool en)
+{
+ int rc;
+
+ rc = vote(chip->aicl_deglitch_short_votable,
+ VARB_WORKAROUND_VOTER, en, 0);
+ if (rc < 0) {
+ pr_err("Couldn't vote %s deglitch rc=%d\n",
+ en ? "short" : "long", rc);
+ return;
+ }
+ pr_smb(PR_STATUS, "AICL deglitch set to %s\n", en ? "short" : "long");
+
+ rc = vote(chip->hw_aicl_rerun_enable_indirect_votable,
+ VARB_WORKAROUND_VOTER, en, 0);
+ if (rc < 0) {
+ pr_err("Couldn't vote hw aicl rerun rc= %d\n", rc);
+ return;
+ }
+ chip->aicl_deglitch_short = en;
+}
+
+static void smbchg_aicl_deglitch_wa_check(struct smbchg_chip *chip)
+{
+ union power_supply_propval prop = {0,};
+ int rc;
+ bool low_volt_chgr = true;
+
+ if (!(chip->wa_flags & SMBCHG_AICL_DEGLITCH_WA))
+ return;
+
+ if (!is_usb_present(chip) && !is_dc_present(chip)) {
+ pr_smb(PR_STATUS, "Charger removed\n");
+ smbchg_aicl_deglitch_wa_en(chip, false);
+ return;
+ }
+
+ if (!chip->bms_psy)
+ return;
+
+ if (is_usb_present(chip)) {
+ if (is_hvdcp_present(chip))
+ low_volt_chgr = false;
+ } else if (is_dc_present(chip)) {
+ if (chip->dc_psy_type == POWER_SUPPLY_TYPE_WIPOWER)
+ low_volt_chgr = false;
+ else
+ low_volt_chgr = chip->low_volt_dcin;
+ }
+
+ if (!low_volt_chgr) {
+ pr_smb(PR_STATUS, "High volt charger! Don't set deglitch\n");
+ smbchg_aicl_deglitch_wa_en(chip, false);
+ return;
+ }
+
+ /* It is possible that battery voltage went high above threshold
+ * when the charger is inserted and can go low because of system
+ * load. We shouldn't be reconfiguring AICL deglitch when this
+ * happens as it will lead to oscillation again which is being
+ * fixed here. Do it once when the battery voltage crosses the
+ * threshold (e.g. 4.2 V) and clear it only when the charger
+ * is removed.
+ */
+ if (!chip->vbat_above_headroom) {
+ rc = power_supply_get_property(chip->bms_psy,
+ POWER_SUPPLY_PROP_VOLTAGE_MIN, &prop);
+ if (rc < 0) {
+ pr_err("could not read voltage_min, rc=%d\n", rc);
+ return;
+ }
+ chip->vbat_above_headroom = !prop.intval;
+ }
+ smbchg_aicl_deglitch_wa_en(chip, chip->vbat_above_headroom);
+}
+
+#define MISC_TEST_REG 0xE2
+#define BB_LOOP_DISABLE_ICL BIT(2)
+static int smbchg_icl_loop_disable_check(struct smbchg_chip *chip)
+{
+ bool icl_disabled = !chip->chg_otg_enabled && chip->flash_triggered;
+ int rc = 0;
+
+ if ((chip->wa_flags & SMBCHG_FLASH_ICL_DISABLE_WA)
+ && icl_disabled != chip->icl_disabled) {
+ rc = smbchg_sec_masked_write(chip,
+ chip->misc_base + MISC_TEST_REG,
+ BB_LOOP_DISABLE_ICL,
+ icl_disabled ? BB_LOOP_DISABLE_ICL : 0);
+ chip->icl_disabled = icl_disabled;
+ }
+
+ return rc;
+}
+
+#define UNKNOWN_BATT_TYPE "Unknown Battery"
+#define LOADING_BATT_TYPE "Loading Battery Data"
+static int smbchg_config_chg_battery_type(struct smbchg_chip *chip)
+{
+ int rc = 0, max_voltage_uv = 0, fastchg_ma = 0, ret = 0, iterm_ua = 0;
+ struct device_node *batt_node, *profile_node;
+ struct device_node *node = chip->pdev->dev.of_node;
+ union power_supply_propval prop = {0,};
+
+ rc = power_supply_get_property(chip->bms_psy,
+ POWER_SUPPLY_PROP_BATTERY_TYPE, &prop);
+ if (rc) {
+ pr_smb(PR_STATUS, "Unable to read battery-type rc=%d\n", rc);
+ return 0;
+ }
+ if (!strcmp(prop.strval, UNKNOWN_BATT_TYPE) ||
+ !strcmp(prop.strval, LOADING_BATT_TYPE)) {
+ pr_smb(PR_MISC, "Battery-type not identified\n");
+ return 0;
+ }
+ /* quit if there is no change in the battery-type from previous */
+ if (chip->battery_type && !strcmp(prop.strval, chip->battery_type))
+ return 0;
+
+ chip->battery_type = prop.strval;
+ batt_node = of_parse_phandle(node, "qcom,battery-data", 0);
+ if (!batt_node) {
+ pr_smb(PR_MISC, "No batterydata available\n");
+ return 0;
+ }
+
+ rc = power_supply_get_property(chip->bms_psy,
+ POWER_SUPPLY_PROP_RESISTANCE_ID, &prop);
+ if (rc < 0) {
+ pr_smb(PR_STATUS, "Unable to read battery-id rc=%d\n", rc);
+ return 0;
+ }
+
+ profile_node = of_batterydata_get_best_profile(batt_node,
+ prop.intval / 1000, NULL);
+ if (IS_ERR_OR_NULL(profile_node)) {
+ rc = PTR_ERR(profile_node);
+ pr_err("couldn't find profile handle %d\n", rc);
+ return rc;
+ }
+
+ /* change vfloat */
+ rc = of_property_read_u32(profile_node, "qcom,max-voltage-uv",
+ &max_voltage_uv);
+ if (rc) {
+ pr_warn("couldn't find battery max voltage rc=%d\n", rc);
+ ret = rc;
+ } else {
+ if (chip->vfloat_mv != (max_voltage_uv / 1000)) {
+ pr_info("Vfloat changed from %dmV to %dmV for battery-type %s\n",
+ chip->vfloat_mv, (max_voltage_uv / 1000),
+ chip->battery_type);
+ rc = smbchg_float_voltage_set(chip,
+ (max_voltage_uv / 1000));
+ if (rc < 0) {
+ dev_err(chip->dev,
+ "Couldn't set float voltage rc = %d\n", rc);
+ return rc;
+ }
+ }
+ }
+
+ /* change chg term */
+ rc = of_property_read_u32(profile_node, "qcom,chg-term-ua",
+ &iterm_ua);
+ if (rc && rc != -EINVAL) {
+ pr_warn("couldn't read battery term current=%d\n", rc);
+ ret = rc;
+ } else if (!rc) {
+ if (chip->iterm_ma != (iterm_ua / 1000)
+ && !chip->iterm_disabled) {
+ pr_info("Term current changed from %dmA to %dmA for battery-type %s\n",
+ chip->iterm_ma, (iterm_ua / 1000),
+ chip->battery_type);
+ rc = smbchg_iterm_set(chip,
+ (iterm_ua / 1000));
+ if (rc < 0) {
+ dev_err(chip->dev,
+ "Couldn't set iterm rc = %d\n", rc);
+ return rc;
+ }
+ }
+ chip->iterm_ma = iterm_ua / 1000;
+ }
+
+ /*
+ * Only configure from profile if fastchg-ma is not defined in the
+ * charger device node.
+ */
+ if (!of_find_property(chip->pdev->dev.of_node,
+ "qcom,fastchg-current-ma", NULL)) {
+ rc = of_property_read_u32(profile_node,
+ "qcom,fastchg-current-ma", &fastchg_ma);
+ if (rc) {
+ ret = rc;
+ } else {
+ pr_smb(PR_MISC,
+ "fastchg-ma changed from to %dma for battery-type %s\n",
+ fastchg_ma, chip->battery_type);
+ rc = vote(chip->fcc_votable, BATT_TYPE_FCC_VOTER, true,
+ fastchg_ma);
+ if (rc < 0) {
+ dev_err(chip->dev,
+ "Couldn't vote for fastchg current rc=%d\n",
+ rc);
+ return rc;
+ }
+ }
+ }
+
+ return ret;
+}
+
+#define MAX_INV_BATT_ID 7700
+#define MIN_INV_BATT_ID 7300
+static void check_battery_type(struct smbchg_chip *chip)
+{
+ union power_supply_propval prop = {0,};
+ bool en;
+
+ if (!chip->bms_psy && chip->bms_psy_name)
+ chip->bms_psy =
+ power_supply_get_by_name((char *)chip->bms_psy_name);
+ if (chip->bms_psy) {
+ power_supply_get_property(chip->bms_psy,
+ POWER_SUPPLY_PROP_BATTERY_TYPE, &prop);
+ en = (strcmp(prop.strval, UNKNOWN_BATT_TYPE) != 0
+ || chip->charge_unknown_battery)
+ && (strcmp(prop.strval, LOADING_BATT_TYPE) != 0);
+ vote(chip->battchg_suspend_votable,
+ BATTCHG_UNKNOWN_BATTERY_EN_VOTER, !en, 0);
+
+ if (!chip->skip_usb_suspend_for_fake_battery) {
+ power_supply_get_property(chip->bms_psy,
+ POWER_SUPPLY_PROP_RESISTANCE_ID, &prop);
+ /* suspend USB path for invalid battery-id */
+ en = (prop.intval <= MAX_INV_BATT_ID &&
+ prop.intval >= MIN_INV_BATT_ID) ? 1 : 0;
+ vote(chip->usb_suspend_votable, FAKE_BATTERY_EN_VOTER,
+ en, 0);
+ }
+ }
+}
+
+static void smbchg_external_power_changed(struct power_supply *psy)
+{
+ struct smbchg_chip *chip = power_supply_get_drvdata(psy);
+ union power_supply_propval prop = {0,};
+ int rc, current_limit = 0, soc;
+ enum power_supply_type usb_supply_type;
+ char *usb_type_name = "null";
+
+ if (chip->bms_psy_name)
+ chip->bms_psy =
+ power_supply_get_by_name((char *)chip->bms_psy_name);
+
+ smbchg_aicl_deglitch_wa_check(chip);
+ if (chip->bms_psy) {
+ check_battery_type(chip);
+ soc = get_prop_batt_capacity(chip);
+ if (chip->previous_soc != soc) {
+ chip->previous_soc = soc;
+ smbchg_soc_changed(chip);
+ }
+
+ rc = smbchg_config_chg_battery_type(chip);
+ if (rc)
+ pr_smb(PR_MISC,
+ "Couldn't update charger configuration rc=%d\n",
+ rc);
+ }
+
+ rc = power_supply_get_property(chip->usb_psy,
+ POWER_SUPPLY_PROP_CHARGING_ENABLED, &prop);
+ if (rc == 0)
+ vote(chip->usb_suspend_votable, POWER_SUPPLY_EN_VOTER,
+ !prop.intval, 0);
+
+ current_limit = chip->usb_current_max / 1000;
+
+ /* Override if type-c charger used */
+ if (chip->typec_current_ma > 500 &&
+ current_limit < chip->typec_current_ma)
+ current_limit = chip->typec_current_ma;
+
+ read_usb_type(chip, &usb_type_name, &usb_supply_type);
+
+ if (usb_supply_type != POWER_SUPPLY_TYPE_USB)
+ goto skip_current_for_non_sdp;
+
+ pr_smb(PR_MISC, "usb type = %s current_limit = %d\n",
+ usb_type_name, current_limit);
+
+ rc = vote(chip->usb_icl_votable, PSY_ICL_VOTER, true,
+ current_limit);
+ if (rc < 0)
+ pr_err("Couldn't update USB PSY ICL vote rc=%d\n", rc);
+
+skip_current_for_non_sdp:
+ smbchg_vfloat_adjust_check(chip);
+
+ if (chip->batt_psy)
+ power_supply_changed(chip->batt_psy);
+}
+
+static int smbchg_otg_regulator_enable(struct regulator_dev *rdev)
+{
+ int rc = 0;
+ struct smbchg_chip *chip = rdev_get_drvdata(rdev);
+
+ chip->otg_retries = 0;
+ chip->chg_otg_enabled = true;
+ smbchg_icl_loop_disable_check(chip);
+ smbchg_otg_pulse_skip_disable(chip, REASON_OTG_ENABLED, true);
+
+ /* If pin control mode then return from here */
+ if (chip->otg_pinctrl)
+ return rc;
+
+ /* sleep to make sure the pulse skip is actually disabled */
+ msleep(20);
+ rc = smbchg_masked_write(chip, chip->bat_if_base + CMD_CHG_REG,
+ OTG_EN_BIT, OTG_EN_BIT);
+ if (rc < 0)
+ dev_err(chip->dev, "Couldn't enable OTG mode rc=%d\n", rc);
+ else
+ chip->otg_enable_time = ktime_get();
+ pr_smb(PR_STATUS, "Enabling OTG Boost\n");
+ return rc;
+}
+
+static int smbchg_otg_regulator_disable(struct regulator_dev *rdev)
+{
+ int rc = 0;
+ struct smbchg_chip *chip = rdev_get_drvdata(rdev);
+
+ if (!chip->otg_pinctrl) {
+ rc = smbchg_masked_write(chip, chip->bat_if_base + CMD_CHG_REG,
+ OTG_EN_BIT, 0);
+ if (rc < 0)
+ dev_err(chip->dev, "Couldn't disable OTG mode rc=%d\n",
+ rc);
+ }
+
+ chip->chg_otg_enabled = false;
+ smbchg_otg_pulse_skip_disable(chip, REASON_OTG_ENABLED, false);
+ smbchg_icl_loop_disable_check(chip);
+ pr_smb(PR_STATUS, "Disabling OTG Boost\n");
+ return rc;
+}
+
+static int smbchg_otg_regulator_is_enable(struct regulator_dev *rdev)
+{
+ int rc = 0;
+ u8 reg = 0;
+ struct smbchg_chip *chip = rdev_get_drvdata(rdev);
+
+ rc = smbchg_read(chip, ®, chip->bat_if_base + CMD_CHG_REG, 1);
+ if (rc < 0) {
+ dev_err(chip->dev,
+ "Couldn't read OTG enable bit rc=%d\n", rc);
+ return rc;
+ }
+
+ return (reg & OTG_EN_BIT) ? 1 : 0;
+}
+
+struct regulator_ops smbchg_otg_reg_ops = {
+ .enable = smbchg_otg_regulator_enable,
+ .disable = smbchg_otg_regulator_disable,
+ .is_enabled = smbchg_otg_regulator_is_enable,
+};
+
+#define USBIN_CHGR_CFG 0xF1
+#define ADAPTER_ALLOWANCE_MASK 0x7
+#define USBIN_ADAPTER_9V 0x3
+#define USBIN_ADAPTER_5V_9V_CONT 0x2
+#define USBIN_ADAPTER_5V_UNREGULATED_9V 0x5
+#define HVDCP_EN_BIT BIT(3)
+static int smbchg_external_otg_regulator_enable(struct regulator_dev *rdev)
+{
+ int rc = 0;
+ struct smbchg_chip *chip = rdev_get_drvdata(rdev);
+
+ rc = vote(chip->usb_suspend_votable, OTG_EN_VOTER, true, 0);
+ if (rc < 0) {
+ dev_err(chip->dev, "Couldn't suspend charger rc=%d\n", rc);
+ return rc;
+ }
+
+ rc = smbchg_read(chip, &chip->original_usbin_allowance,
+ chip->usb_chgpth_base + USBIN_CHGR_CFG, 1);
+ if (rc < 0) {
+ dev_err(chip->dev, "Couldn't read usb allowance rc=%d\n", rc);
+ return rc;
+ }
+
+ /*
+ * To disallow source detect and usbin_uv interrupts, set the adapter
+ * allowance to 9V, so that the audio boost operating in reverse never
+ * gets detected as a valid input
+ */
+ rc = smbchg_sec_masked_write(chip,
+ chip->usb_chgpth_base + CHGPTH_CFG,
+ HVDCP_EN_BIT, 0);
+ if (rc < 0) {
+ dev_err(chip->dev, "Couldn't disable HVDCP rc=%d\n", rc);
+ return rc;
+ }
+
+ rc = smbchg_sec_masked_write(chip,
+ chip->usb_chgpth_base + USBIN_CHGR_CFG,
+ 0xFF, USBIN_ADAPTER_9V);
+ if (rc < 0) {
+ dev_err(chip->dev, "Couldn't write usb allowance rc=%d\n", rc);
+ return rc;
+ }
+
+ pr_smb(PR_STATUS, "Enabling OTG Boost\n");
+ return rc;
+}
+
+static int smbchg_external_otg_regulator_disable(struct regulator_dev *rdev)
+{
+ int rc = 0;
+ struct smbchg_chip *chip = rdev_get_drvdata(rdev);
+
+ rc = vote(chip->usb_suspend_votable, OTG_EN_VOTER, false, 0);
+ if (rc < 0) {
+ dev_err(chip->dev, "Couldn't unsuspend charger rc=%d\n", rc);
+ return rc;
+ }
+
+ /*
+ * Reenable HVDCP and set the adapter allowance back to the original
+ * value in order to allow normal USBs to be recognized as a valid
+ * input.
+ */
+ rc = smbchg_sec_masked_write(chip,
+ chip->usb_chgpth_base + CHGPTH_CFG,
+ HVDCP_EN_BIT, HVDCP_EN_BIT);
+ if (rc < 0) {
+ dev_err(chip->dev, "Couldn't enable HVDCP rc=%d\n", rc);
+ return rc;
+ }
+
+ rc = smbchg_sec_masked_write(chip,
+ chip->usb_chgpth_base + USBIN_CHGR_CFG,
+ 0xFF, chip->original_usbin_allowance);
+ if (rc < 0) {
+ dev_err(chip->dev, "Couldn't write usb allowance rc=%d\n", rc);
+ return rc;
+ }
+
+ pr_smb(PR_STATUS, "Disabling OTG Boost\n");
+ return rc;
+}
+
+static int smbchg_external_otg_regulator_is_enable(struct regulator_dev *rdev)
+{
+ struct smbchg_chip *chip = rdev_get_drvdata(rdev);
+
+ return get_client_vote(chip->usb_suspend_votable, OTG_EN_VOTER);
+}
+
+struct regulator_ops smbchg_external_otg_reg_ops = {
+ .enable = smbchg_external_otg_regulator_enable,
+ .disable = smbchg_external_otg_regulator_disable,
+ .is_enabled = smbchg_external_otg_regulator_is_enable,
+};
+
+static int smbchg_regulator_init(struct smbchg_chip *chip)
+{
+ int rc = 0;
+ struct regulator_config cfg = {};
+ struct device_node *regulator_node;
+
+ cfg.dev = chip->dev;
+ cfg.driver_data = chip;
+
+ chip->otg_vreg.rdesc.owner = THIS_MODULE;
+ chip->otg_vreg.rdesc.type = REGULATOR_VOLTAGE;
+ chip->otg_vreg.rdesc.ops = &smbchg_otg_reg_ops;
+ chip->otg_vreg.rdesc.of_match = "qcom,smbcharger-boost-otg";
+ chip->otg_vreg.rdesc.name = "qcom,smbcharger-boost-otg";
+
+ chip->otg_vreg.rdev = devm_regulator_register(chip->dev,
+ &chip->otg_vreg.rdesc, &cfg);
+ if (IS_ERR(chip->otg_vreg.rdev)) {
+ rc = PTR_ERR(chip->otg_vreg.rdev);
+ chip->otg_vreg.rdev = NULL;
+ if (rc != -EPROBE_DEFER)
+ dev_err(chip->dev,
+ "OTG reg failed, rc=%d\n", rc);
+ }
+ if (rc)
+ return rc;
+
+ regulator_node = of_get_child_by_name(chip->dev->of_node,
+ "qcom,smbcharger-external-otg");
+ if (!regulator_node) {
+ dev_dbg(chip->dev, "external-otg node absent\n");
+ return 0;
+ }
+
+ chip->ext_otg_vreg.rdesc.owner = THIS_MODULE;
+ chip->ext_otg_vreg.rdesc.type = REGULATOR_VOLTAGE;
+ chip->ext_otg_vreg.rdesc.ops = &smbchg_external_otg_reg_ops;
+ chip->ext_otg_vreg.rdesc.of_match = "qcom,smbcharger-external-otg";
+ chip->ext_otg_vreg.rdesc.name = "qcom,smbcharger-external-otg";
+ if (of_get_property(chip->dev->of_node, "otg-parent-supply", NULL))
+ chip->ext_otg_vreg.rdesc.supply_name = "otg-parent";
+ cfg.dev = chip->dev;
+ cfg.driver_data = chip;
+
+ chip->ext_otg_vreg.rdev = devm_regulator_register(chip->dev,
+ &chip->ext_otg_vreg.rdesc,
+ &cfg);
+ if (IS_ERR(chip->ext_otg_vreg.rdev)) {
+ rc = PTR_ERR(chip->ext_otg_vreg.rdev);
+ chip->ext_otg_vreg.rdev = NULL;
+ if (rc != -EPROBE_DEFER)
+ dev_err(chip->dev,
+ "external OTG reg failed, rc=%d\n", rc);
+ }
+
+ return rc;
+}
+
+#define CMD_CHG_LED_REG 0x43
+#define CHG_LED_CTRL_BIT BIT(0)
+#define LED_SW_CTRL_BIT 0x1
+#define LED_CHG_CTRL_BIT 0x0
+#define CHG_LED_ON 0x03
+#define CHG_LED_OFF 0x00
+#define LED_BLINKING_PATTERN1 0x01
+#define LED_BLINKING_PATTERN2 0x02
+#define LED_BLINKING_CFG_MASK SMB_MASK(2, 1)
+#define CHG_LED_SHIFT 1
+static int smbchg_chg_led_controls(struct smbchg_chip *chip)
+{
+ u8 reg, mask;
+ int rc;
+
+ if (chip->cfg_chg_led_sw_ctrl) {
+ /* turn-off LED by default for software control */
+ mask = CHG_LED_CTRL_BIT | LED_BLINKING_CFG_MASK;
+ reg = LED_SW_CTRL_BIT;
+ } else {
+ mask = CHG_LED_CTRL_BIT;
+ reg = LED_CHG_CTRL_BIT;
+ }
+
+ rc = smbchg_masked_write(chip, chip->bat_if_base + CMD_CHG_LED_REG,
+ mask, reg);
+ if (rc < 0)
+ dev_err(chip->dev,
+ "Couldn't write LED_CTRL_BIT rc=%d\n", rc);
+ return rc;
+}
+
+static void smbchg_chg_led_brightness_set(struct led_classdev *cdev,
+ enum led_brightness value)
+{
+ struct smbchg_chip *chip = container_of(cdev,
+ struct smbchg_chip, led_cdev);
+ union power_supply_propval pval = {0, };
+ u8 reg;
+ int rc;
+
+ reg = (value > LED_OFF) ? CHG_LED_ON << CHG_LED_SHIFT :
+ CHG_LED_OFF << CHG_LED_SHIFT;
+ pval.intval = value > LED_OFF ? 1 : 0;
+ power_supply_set_property(chip->bms_psy, POWER_SUPPLY_PROP_HI_POWER,
+ &pval);
+ pr_smb(PR_STATUS,
+ "set the charger led brightness to value=%d\n",
+ value);
+ rc = smbchg_sec_masked_write(chip,
+ chip->bat_if_base + CMD_CHG_LED_REG,
+ LED_BLINKING_CFG_MASK, reg);
+ if (rc)
+ dev_err(chip->dev, "Couldn't write CHG_LED rc=%d\n",
+ rc);
+}
+
+static enum
+led_brightness smbchg_chg_led_brightness_get(struct led_classdev *cdev)
+{
+ struct smbchg_chip *chip = container_of(cdev,
+ struct smbchg_chip, led_cdev);
+ u8 reg_val, chg_led_sts;
+ int rc;
+
+ rc = smbchg_read(chip, ®_val, chip->bat_if_base + CMD_CHG_LED_REG,
+ 1);
+ if (rc < 0) {
+ dev_err(chip->dev,
+ "Couldn't read CHG_LED_REG sts rc=%d\n",
+ rc);
+ return rc;
+ }
+
+ chg_led_sts = (reg_val & LED_BLINKING_CFG_MASK) >> CHG_LED_SHIFT;
+
+ pr_smb(PR_STATUS, "chg_led_sts = %02x\n", chg_led_sts);
+
+ return (chg_led_sts == CHG_LED_OFF) ? LED_OFF : LED_FULL;
+}
+
+static void smbchg_chg_led_blink_set(struct smbchg_chip *chip,
+ unsigned long blinking)
+{
+ union power_supply_propval pval = {0, };
+ u8 reg;
+ int rc;
+
+ pval.intval = (blinking == 0) ? 0 : 1;
+ power_supply_set_property(chip->bms_psy, POWER_SUPPLY_PROP_HI_POWER,
+ &pval);
+
+ if (blinking == 0) {
+ 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
+ reg = LED_BLINKING_PATTERN1 << CHG_LED_SHIFT;
+ }
+
+ rc = smbchg_sec_masked_write(chip,
+ chip->bat_if_base + CMD_CHG_LED_REG,
+ LED_BLINKING_CFG_MASK, reg);
+ if (rc)
+ dev_err(chip->dev, "Couldn't write CHG_LED rc=%d\n",
+ rc);
+}
+
+static ssize_t smbchg_chg_led_blink_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t len)
+{
+ struct led_classdev *cdev = dev_get_drvdata(dev);
+ struct smbchg_chip *chip = container_of(cdev, struct smbchg_chip,
+ led_cdev);
+ unsigned long blinking;
+ ssize_t rc = -EINVAL;
+
+ rc = kstrtoul(buf, 10, &blinking);
+ if (rc)
+ return rc;
+
+ smbchg_chg_led_blink_set(chip, blinking);
+
+ return len;
+}
+
+static DEVICE_ATTR(blink, 0664, NULL, smbchg_chg_led_blink_store);
+
+static struct attribute *led_blink_attributes[] = {
+ &dev_attr_blink.attr,
+ NULL,
+};
+
+static struct attribute_group smbchg_led_attr_group = {
+ .attrs = led_blink_attributes
+};
+
+static int smbchg_register_chg_led(struct smbchg_chip *chip)
+{
+ int rc;
+
+ chip->led_cdev.name = "red";
+ chip->led_cdev.brightness_set = smbchg_chg_led_brightness_set;
+ chip->led_cdev.brightness_get = smbchg_chg_led_brightness_get;
+
+ rc = led_classdev_register(chip->dev, &chip->led_cdev);
+ if (rc) {
+ dev_err(chip->dev, "unable to register charger led, rc=%d\n",
+ rc);
+ return rc;
+ }
+
+ rc = sysfs_create_group(&chip->led_cdev.dev->kobj,
+ &smbchg_led_attr_group);
+ if (rc) {
+ dev_err(chip->dev, "led sysfs rc: %d\n", rc);
+ return rc;
+ }
+
+ return rc;
+}
+
+static int vf_adjust_low_threshold = 5;
+module_param(vf_adjust_low_threshold, int, 0644);
+
+static int vf_adjust_high_threshold = 7;
+module_param(vf_adjust_high_threshold, int, 0644);
+
+static int vf_adjust_n_samples = 10;
+module_param(vf_adjust_n_samples, int, 0644);
+
+static int vf_adjust_max_delta_mv = 40;
+module_param(vf_adjust_max_delta_mv, int, 0644);
+
+static int vf_adjust_trim_steps_per_adjust = 1;
+module_param(vf_adjust_trim_steps_per_adjust, int, 0644);
+
+#define CENTER_TRIM_CODE 7
+#define MAX_LIN_CODE 14
+#define MAX_TRIM_CODE 15
+#define SCALE_SHIFT 4
+#define VF_TRIM_OFFSET_MASK SMB_MASK(3, 0)
+#define VF_STEP_SIZE_MV 10
+#define SCALE_LSB_MV 17
+static int smbchg_trim_add_steps(int prev_trim, int delta_steps)
+{
+ int scale_steps;
+ int linear_offset, linear_scale;
+ int offset_code = prev_trim & VF_TRIM_OFFSET_MASK;
+ int scale_code = (prev_trim & ~VF_TRIM_OFFSET_MASK) >> SCALE_SHIFT;
+
+ if (abs(delta_steps) > 1) {
+ pr_smb(PR_STATUS,
+ "Cant trim multiple steps delta_steps = %d\n",
+ delta_steps);
+ return prev_trim;
+ }
+ if (offset_code <= CENTER_TRIM_CODE)
+ linear_offset = offset_code + CENTER_TRIM_CODE;
+ else if (offset_code > CENTER_TRIM_CODE)
+ linear_offset = MAX_TRIM_CODE - offset_code;
+
+ if (scale_code <= CENTER_TRIM_CODE)
+ linear_scale = scale_code + CENTER_TRIM_CODE;
+ else if (scale_code > CENTER_TRIM_CODE)
+ linear_scale = scale_code - (CENTER_TRIM_CODE + 1);
+
+ /* check if we can accommodate delta steps with just the offset */
+ if (linear_offset + delta_steps >= 0
+ && linear_offset + delta_steps <= MAX_LIN_CODE) {
+ linear_offset += delta_steps;
+
+ if (linear_offset > CENTER_TRIM_CODE)
+ offset_code = linear_offset - CENTER_TRIM_CODE;
+ else
+ offset_code = MAX_TRIM_CODE - linear_offset;
+
+ return (prev_trim & ~VF_TRIM_OFFSET_MASK) | offset_code;
+ }
+
+ /* changing offset cannot satisfy delta steps, change the scale bits */
+ scale_steps = delta_steps > 0 ? 1 : -1;
+
+ if (linear_scale + scale_steps < 0
+ || linear_scale + scale_steps > MAX_LIN_CODE) {
+ pr_smb(PR_STATUS,
+ "Cant trim scale_steps = %d delta_steps = %d\n",
+ scale_steps, delta_steps);
+ return prev_trim;
+ }
+
+ linear_scale += scale_steps;
+
+ if (linear_scale > CENTER_TRIM_CODE)
+ scale_code = linear_scale - CENTER_TRIM_CODE;
+ else
+ scale_code = linear_scale + (CENTER_TRIM_CODE + 1);
+ prev_trim = (prev_trim & VF_TRIM_OFFSET_MASK)
+ | scale_code << SCALE_SHIFT;
+
+ /*
+ * now that we have changed scale which is a 17mV jump, change the
+ * offset bits (10mV) too so the effective change is just 7mV
+ */
+ delta_steps = -1 * delta_steps;
+
+ linear_offset = clamp(linear_offset + delta_steps, 0, MAX_LIN_CODE);
+ if (linear_offset > CENTER_TRIM_CODE)
+ offset_code = linear_offset - CENTER_TRIM_CODE;
+ else
+ offset_code = MAX_TRIM_CODE - linear_offset;
+
+ return (prev_trim & ~VF_TRIM_OFFSET_MASK) | offset_code;
+}
+
+#define TRIM_14 0xFE
+#define VF_TRIM_MASK 0xFF
+static int smbchg_adjust_vfloat_mv_trim(struct smbchg_chip *chip,
+ int delta_mv)
+{
+ int sign, delta_steps, rc = 0;
+ u8 prev_trim, new_trim;
+ int i;
+
+ sign = delta_mv > 0 ? 1 : -1;
+ delta_steps = (delta_mv + sign * VF_STEP_SIZE_MV / 2)
+ / VF_STEP_SIZE_MV;
+
+ rc = smbchg_read(chip, &prev_trim, chip->misc_base + TRIM_14, 1);
+ if (rc) {
+ dev_err(chip->dev, "Unable to read trim 14: %d\n", rc);
+ return rc;
+ }
+
+ for (i = 1; i <= abs(delta_steps)
+ && i <= vf_adjust_trim_steps_per_adjust; i++) {
+ new_trim = (u8)smbchg_trim_add_steps(prev_trim,
+ delta_steps > 0 ? 1 : -1);
+ if (new_trim == prev_trim) {
+ pr_smb(PR_STATUS,
+ "VFloat trim unchanged from %02x\n", prev_trim);
+ /* treat no trim change as an error */
+ return -EINVAL;
+ }
+
+ rc = smbchg_sec_masked_write(chip, chip->misc_base + TRIM_14,
+ VF_TRIM_MASK, new_trim);
+ if (rc < 0) {
+ dev_err(chip->dev,
+ "Couldn't change vfloat trim rc=%d\n", rc);
+ }
+ pr_smb(PR_STATUS,
+ "VFlt trim %02x to %02x, delta steps: %d\n",
+ prev_trim, new_trim, delta_steps);
+ prev_trim = new_trim;
+ }
+
+ return rc;
+}
+
+#define VFLOAT_RESAMPLE_DELAY_MS 10000
+static void smbchg_vfloat_adjust_work(struct work_struct *work)
+{
+ struct smbchg_chip *chip = container_of(work,
+ struct smbchg_chip,
+ vfloat_adjust_work.work);
+ int vbat_uv, vbat_mv, ibat_ua, rc, delta_vfloat_mv;
+ bool taper, enable;
+
+ smbchg_stay_awake(chip, PM_REASON_VFLOAT_ADJUST);
+ taper = (get_prop_charge_type(chip)
+ == POWER_SUPPLY_CHARGE_TYPE_TAPER);
+ enable = taper && (chip->parallel.current_max_ma == 0);
+
+ if (!enable) {
+ pr_smb(PR_MISC,
+ "Stopping vfloat adj taper=%d parallel_ma = %d\n",
+ taper, chip->parallel.current_max_ma);
+ goto stop;
+ }
+
+ if (get_prop_batt_health(chip) != POWER_SUPPLY_HEALTH_GOOD) {
+ pr_smb(PR_STATUS, "JEITA active, skipping\n");
+ goto stop;
+ }
+
+ set_property_on_fg(chip, POWER_SUPPLY_PROP_UPDATE_NOW, 1);
+ rc = get_property_from_fg(chip,
+ POWER_SUPPLY_PROP_VOLTAGE_NOW, &vbat_uv);
+ if (rc) {
+ pr_smb(PR_STATUS,
+ "bms psy does not support voltage rc = %d\n", rc);
+ goto stop;
+ }
+ vbat_mv = vbat_uv / 1000;
+
+ if ((vbat_mv - chip->vfloat_mv) < -1 * vf_adjust_max_delta_mv) {
+ pr_smb(PR_STATUS, "Skip vbat out of range: %d\n", vbat_mv);
+ goto reschedule;
+ }
+
+ rc = get_property_from_fg(chip,
+ POWER_SUPPLY_PROP_CURRENT_NOW, &ibat_ua);
+ if (rc) {
+ pr_smb(PR_STATUS,
+ "bms psy does not support current_now rc = %d\n", rc);
+ goto stop;
+ }
+
+ if (ibat_ua / 1000 > -chip->iterm_ma) {
+ pr_smb(PR_STATUS, "Skip ibat too high: %d\n", ibat_ua);
+ goto reschedule;
+ }
+
+ pr_smb(PR_STATUS, "sample number = %d vbat_mv = %d ibat_ua = %d\n",
+ chip->n_vbat_samples,
+ vbat_mv,
+ ibat_ua);
+
+ chip->max_vbat_sample = max(chip->max_vbat_sample, vbat_mv);
+ chip->n_vbat_samples += 1;
+ if (chip->n_vbat_samples < vf_adjust_n_samples) {
+ pr_smb(PR_STATUS, "Skip %d samples; max = %d\n",
+ chip->n_vbat_samples, chip->max_vbat_sample);
+ goto reschedule;
+ }
+ /* if max vbat > target vfloat, delta_vfloat_mv could be negative */
+ delta_vfloat_mv = chip->vfloat_mv - chip->max_vbat_sample;
+ pr_smb(PR_STATUS, "delta_vfloat_mv = %d, samples = %d, mvbat = %d\n",
+ delta_vfloat_mv, chip->n_vbat_samples, chip->max_vbat_sample);
+ /*
+ * enough valid samples has been collected, adjust trim codes
+ * based on maximum of collected vbat samples if necessary
+ */
+ if (delta_vfloat_mv > vf_adjust_high_threshold
+ || delta_vfloat_mv < -1 * vf_adjust_low_threshold) {
+ rc = smbchg_adjust_vfloat_mv_trim(chip, delta_vfloat_mv);
+ if (rc) {
+ pr_smb(PR_STATUS,
+ "Stopping vfloat adj after trim adj rc = %d\n",
+ rc);
+ goto stop;
+ }
+ chip->max_vbat_sample = 0;
+ chip->n_vbat_samples = 0;
+ goto reschedule;
+ }
+
+stop:
+ chip->max_vbat_sample = 0;
+ chip->n_vbat_samples = 0;
+ smbchg_relax(chip, PM_REASON_VFLOAT_ADJUST);
+ return;
+
+reschedule:
+ schedule_delayed_work(&chip->vfloat_adjust_work,
+ msecs_to_jiffies(VFLOAT_RESAMPLE_DELAY_MS));
+}
+
+static int smbchg_charging_status_change(struct smbchg_chip *chip)
+{
+ smbchg_vfloat_adjust_check(chip);
+ set_property_on_fg(chip, POWER_SUPPLY_PROP_STATUS,
+ get_prop_batt_status(chip));
+ return 0;
+}
+
+#define BB_CLMP_SEL 0xF8
+#define BB_CLMP_MASK SMB_MASK(1, 0)
+#define BB_CLMP_VFIX_3338MV 0x1
+#define BB_CLMP_VFIX_3512MV 0x2
+static int smbchg_set_optimal_charging_mode(struct smbchg_chip *chip, int type)
+{
+ int rc;
+ bool hvdcp2 = (type == POWER_SUPPLY_TYPE_USB_HVDCP
+ && smbchg_is_usbin_active_pwr_src(chip));
+
+ /*
+ * Set the charger switching freq to 1MHZ if HVDCP 2.0,
+ * or 750KHZ otherwise
+ */
+ rc = smbchg_sec_masked_write(chip,
+ chip->bat_if_base + BAT_IF_TRIM7_REG,
+ CFG_750KHZ_BIT, hvdcp2 ? 0 : CFG_750KHZ_BIT);
+ if (rc) {
+ dev_err(chip->dev, "Cannot set switching freq: %d\n", rc);
+ return rc;
+ }
+
+ /*
+ * Set the charger switch frequency clamp voltage threshold to 3.338V
+ * if HVDCP 2.0, or 3.512V otherwise.
+ */
+ rc = smbchg_sec_masked_write(chip, chip->bat_if_base + BB_CLMP_SEL,
+ BB_CLMP_MASK,
+ hvdcp2 ? BB_CLMP_VFIX_3338MV : BB_CLMP_VFIX_3512MV);
+ if (rc) {
+ dev_err(chip->dev, "Cannot set switching freq: %d\n", rc);
+ return rc;
+ }
+
+ return 0;
+}
+
+#define DEFAULT_SDP_MA 100
+#define DEFAULT_CDP_MA 1500
+static int smbchg_change_usb_supply_type(struct smbchg_chip *chip,
+ enum power_supply_type type)
+{
+ int rc, current_limit_ma;
+
+ /*
+ * if the type is not unknown, set the type before changing ICL vote
+ * in order to ensure that the correct current limit registers are
+ * used
+ */
+ if (type != POWER_SUPPLY_TYPE_UNKNOWN)
+ chip->usb_supply_type = type;
+
+ /*
+ * Type-C only supports STD(900), MEDIUM(1500) and HIGH(3000) current
+ * modes, skip all BC 1.2 current if external typec is supported.
+ * Note: for SDP supporting current based on USB notifications.
+ */
+ if (chip->typec_psy && (type != POWER_SUPPLY_TYPE_USB))
+ current_limit_ma = chip->typec_current_ma;
+ else if (type == POWER_SUPPLY_TYPE_USB)
+ current_limit_ma = DEFAULT_SDP_MA;
+ else if (type == POWER_SUPPLY_TYPE_USB_CDP)
+ current_limit_ma = DEFAULT_CDP_MA;
+ else if (type == POWER_SUPPLY_TYPE_USB_HVDCP)
+ current_limit_ma = smbchg_default_hvdcp_icl_ma;
+ else if (type == POWER_SUPPLY_TYPE_USB_HVDCP_3)
+ current_limit_ma = smbchg_default_hvdcp3_icl_ma;
+ else
+ current_limit_ma = smbchg_default_dcp_icl_ma;
+
+ pr_smb(PR_STATUS, "Type %d: setting mA = %d\n",
+ type, current_limit_ma);
+ rc = vote(chip->usb_icl_votable, PSY_ICL_VOTER, true,
+ current_limit_ma);
+ if (rc < 0) {
+ pr_err("Couldn't vote for new USB ICL rc=%d\n", rc);
+ goto out;
+ }
+
+ /* otherwise if it is unknown, set type after the vote */
+ if (type == POWER_SUPPLY_TYPE_UNKNOWN)
+ chip->usb_supply_type = type;
+
+ if (!chip->skip_usb_notification)
+ power_supply_changed(chip->usb_psy);
+
+ /* set the correct buck switching frequency */
+ rc = smbchg_set_optimal_charging_mode(chip, type);
+ if (rc < 0)
+ pr_err("Couldn't set charger optimal mode rc=%d\n", rc);
+
+out:
+ return rc;
+}
+
+#define HVDCP_ADAPTER_SEL_MASK SMB_MASK(5, 4)
+#define HVDCP_5V 0x00
+#define HVDCP_9V 0x10
+#define USB_CMD_HVDCP_1 0x42
+#define FORCE_HVDCP_2p0 BIT(3)
+
+static int force_9v_hvdcp(struct smbchg_chip *chip)
+{
+ int rc;
+
+ /* Force 5V HVDCP */
+ rc = smbchg_sec_masked_write(chip,
+ chip->usb_chgpth_base + CHGPTH_CFG,
+ HVDCP_ADAPTER_SEL_MASK, HVDCP_5V);
+ if (rc) {
+ pr_err("Couldn't set hvdcp config in chgpath_chg rc=%d\n", rc);
+ return rc;
+ }
+
+ /* Force QC2.0 */
+ rc = smbchg_masked_write(chip,
+ chip->usb_chgpth_base + USB_CMD_HVDCP_1,
+ FORCE_HVDCP_2p0, FORCE_HVDCP_2p0);
+ rc |= smbchg_masked_write(chip,
+ chip->usb_chgpth_base + USB_CMD_HVDCP_1,
+ FORCE_HVDCP_2p0, 0);
+ if (rc < 0) {
+ pr_err("Couldn't force QC2.0 rc=%d\n", rc);
+ return rc;
+ }
+
+ /* Delay to switch into HVDCP 2.0 and avoid UV */
+ msleep(500);
+
+ /* Force 9V HVDCP */
+ rc = smbchg_sec_masked_write(chip,
+ chip->usb_chgpth_base + CHGPTH_CFG,
+ HVDCP_ADAPTER_SEL_MASK, HVDCP_9V);
+ if (rc)
+ pr_err("Couldn't set hvdcp config in chgpath_chg rc=%d\n", rc);
+
+ return rc;
+}
+
+static void smbchg_hvdcp_det_work(struct work_struct *work)
+{
+ struct smbchg_chip *chip = container_of(work,
+ struct smbchg_chip,
+ hvdcp_det_work.work);
+ int rc;
+
+ if (is_hvdcp_present(chip)) {
+ if (!chip->hvdcp3_supported &&
+ (chip->wa_flags & SMBCHG_HVDCP_9V_EN_WA)) {
+ /* force HVDCP 2.0 */
+ rc = force_9v_hvdcp(chip);
+ if (rc)
+ pr_err("could not force 9V HVDCP continuing rc=%d\n",
+ rc);
+ }
+ smbchg_change_usb_supply_type(chip,
+ POWER_SUPPLY_TYPE_USB_HVDCP);
+ if (chip->batt_psy)
+ power_supply_changed(chip->batt_psy);
+ smbchg_aicl_deglitch_wa_check(chip);
+ }
+ smbchg_relax(chip, PM_DETECT_HVDCP);
+}
+
+static int set_usb_psy_dp_dm(struct smbchg_chip *chip, int state)
+{
+ int rc;
+ u8 reg;
+ union power_supply_propval pval = {0, };
+
+ /*
+ * ensure that we are not in the middle of an insertion where usbin_uv
+ * is low and src_detect hasnt gone high. If so force dp=F dm=F
+ * which guarantees proper type detection
+ */
+ rc = smbchg_read(chip, ®, chip->usb_chgpth_base + RT_STS, 1);
+ if (!rc && !(reg & USBIN_UV_BIT) && !(reg & USBIN_SRC_DET_BIT)) {
+ pr_smb(PR_MISC, "overwriting state = %d with %d\n",
+ state, POWER_SUPPLY_DP_DM_DPF_DMF);
+ if (chip->dpdm_reg && !regulator_is_enabled(chip->dpdm_reg))
+ return regulator_enable(chip->dpdm_reg);
+ }
+ pr_smb(PR_MISC, "setting usb psy dp dm = %d\n", state);
+ pval.intval = state;
+ return power_supply_set_property(chip->usb_psy,
+ POWER_SUPPLY_PROP_DP_DM, &pval);
+}
+
+#define APSD_CFG 0xF5
+#define AUTO_SRC_DETECT_EN_BIT BIT(0)
+#define APSD_TIMEOUT_MS 1500
+static void restore_from_hvdcp_detection(struct smbchg_chip *chip)
+{
+ int rc;
+
+ pr_smb(PR_MISC, "Retracting HVDCP vote for ICL\n");
+ rc = vote(chip->usb_icl_votable, HVDCP_ICL_VOTER, false, 0);
+ if (rc < 0)
+ pr_err("Couldn't retract HVDCP ICL vote rc=%d\n", rc);
+
+ /* switch to 9V HVDCP */
+ rc = smbchg_sec_masked_write(chip, chip->usb_chgpth_base + CHGPTH_CFG,
+ HVDCP_ADAPTER_SEL_MASK, HVDCP_9V);
+ if (rc < 0)
+ pr_err("Couldn't configure HVDCP 9V rc=%d\n", rc);
+
+ /* enable HVDCP */
+ rc = smbchg_sec_masked_write(chip,
+ chip->usb_chgpth_base + CHGPTH_CFG,
+ HVDCP_EN_BIT, HVDCP_EN_BIT);
+ if (rc < 0)
+ pr_err("Couldn't enable HVDCP rc=%d\n", rc);
+
+ /* enable APSD */
+ rc = smbchg_sec_masked_write(chip,
+ chip->usb_chgpth_base + APSD_CFG,
+ AUTO_SRC_DETECT_EN_BIT, AUTO_SRC_DETECT_EN_BIT);
+ if (rc < 0)
+ pr_err("Couldn't enable APSD rc=%d\n", rc);
+
+ /* Reset back to 5V unregulated */
+ rc = smbchg_sec_masked_write(chip,
+ chip->usb_chgpth_base + USBIN_CHGR_CFG,
+ ADAPTER_ALLOWANCE_MASK, USBIN_ADAPTER_5V_UNREGULATED_9V);
+ if (rc < 0)
+ pr_err("Couldn't write usb allowance rc=%d\n", rc);
+
+ 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);
+
+ chip->hvdcp_3_det_ignore_uv = false;
+ chip->pulse_cnt = 0;
+}
+
+#define RESTRICTED_CHG_FCC_PERCENT 50
+static int smbchg_restricted_charging(struct smbchg_chip *chip, bool enable)
+{
+ int current_table_index, fastchg_current;
+ int rc = 0;
+
+ /* If enable, set the fcc to the set point closest
+ * to 50% of the configured fcc while remaining below it
+ */
+ current_table_index = find_smaller_in_array(
+ chip->tables.usb_ilim_ma_table,
+ chip->cfg_fastchg_current_ma
+ * RESTRICTED_CHG_FCC_PERCENT / 100,
+ chip->tables.usb_ilim_ma_len);
+ fastchg_current =
+ chip->tables.usb_ilim_ma_table[current_table_index];
+ rc = vote(chip->fcc_votable, RESTRICTED_CHG_FCC_VOTER, enable,
+ fastchg_current);
+
+ pr_smb(PR_STATUS, "restricted_charging set to %d\n", enable);
+ chip->restricted_charging = enable;
+
+ return rc;
+}
+
+static void handle_usb_removal(struct smbchg_chip *chip)
+{
+ struct power_supply *parallel_psy = get_parallel_psy(chip);
+ union power_supply_propval pval = {0, };
+ int rc;
+
+ pr_smb(PR_STATUS, "triggered\n");
+ smbchg_aicl_deglitch_wa_check(chip);
+ /* Clear the OV detected status set before */
+ if (chip->usb_ov_det)
+ chip->usb_ov_det = false;
+ /* Clear typec current status */
+ if (chip->typec_psy)
+ chip->typec_current_ma = 0;
+ 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)
+ regulator_disable(chip->dpdm_reg);
+ schedule_work(&chip->usb_set_online_work);
+
+ pr_smb(PR_MISC, "setting usb psy health UNKNOWN\n");
+ chip->usb_health = POWER_SUPPLY_HEALTH_UNKNOWN;
+ power_supply_changed(chip->usb_psy);
+
+ if (parallel_psy && chip->parallel_charger_detected) {
+ pval.intval = false;
+ power_supply_set_property(parallel_psy,
+ POWER_SUPPLY_PROP_PRESENT, &pval);
+ }
+ if (chip->parallel.avail && chip->aicl_done_irq
+ && chip->enable_aicl_wake) {
+ disable_irq_wake(chip->aicl_done_irq);
+ chip->enable_aicl_wake = false;
+ }
+ chip->parallel.enabled_once = false;
+ chip->vbat_above_headroom = false;
+ rc = smbchg_masked_write(chip, chip->usb_chgpth_base + CMD_IL,
+ ICL_OVERRIDE_BIT, 0);
+ if (rc < 0)
+ pr_err("Couldn't set override rc = %d\n", rc);
+
+ vote(chip->usb_icl_votable, WEAK_CHARGER_ICL_VOTER, false, 0);
+ chip->usb_icl_delta = 0;
+ vote(chip->usb_icl_votable, SW_AICL_ICL_VOTER, false, 0);
+ vote(chip->aicl_deglitch_short_votable,
+ HVDCP_SHORT_DEGLITCH_VOTER, false, 0);
+ if (!chip->hvdcp_not_supported)
+ restore_from_hvdcp_detection(chip);
+}
+
+static bool is_usbin_uv_high(struct smbchg_chip *chip)
+{
+ int rc;
+ u8 reg;
+
+ rc = smbchg_read(chip, ®, chip->usb_chgpth_base + RT_STS, 1);
+ if (rc < 0) {
+ dev_err(chip->dev, "Couldn't read usb rt status rc = %d\n", rc);
+ return false;
+ }
+ return reg &= USBIN_UV_BIT;
+}
+
+#define HVDCP_NOTIFY_MS 2500
+static void handle_usb_insertion(struct smbchg_chip *chip)
+{
+ struct power_supply *parallel_psy = get_parallel_psy(chip);
+ union power_supply_propval pval = {0, };
+ enum power_supply_type usb_supply_type;
+ int rc;
+ char *usb_type_name = "null";
+
+ pr_smb(PR_STATUS, "triggered\n");
+ /* usb inserted */
+ read_usb_type(chip, &usb_type_name, &usb_supply_type);
+ pr_smb(PR_STATUS,
+ "inserted type = %d (%s)", usb_supply_type, usb_type_name);
+
+ smbchg_aicl_deglitch_wa_check(chip);
+ if (chip->typec_psy)
+ update_typec_status(chip);
+ smbchg_change_usb_supply_type(chip, usb_supply_type);
+
+ /* Only notify USB if it's not a charger */
+ if (usb_supply_type == POWER_SUPPLY_TYPE_USB ||
+ usb_supply_type == POWER_SUPPLY_TYPE_USB_CDP)
+ extcon_set_cable_state_(chip->extcon, EXTCON_USB,
+ chip->usb_present);
+
+ /* Notify the USB psy if OV condition is not present */
+ if (!chip->usb_ov_det) {
+ /*
+ * Note that this could still be a very weak charger
+ * if the handle_usb_insertion was triggered from
+ * the falling edge of an USBIN_OV interrupt
+ */
+ pr_smb(PR_MISC, "setting usb psy health %s\n",
+ chip->very_weak_charger
+ ? "UNSPEC_FAILURE" : "GOOD");
+ chip->usb_health = chip->very_weak_charger
+ ? POWER_SUPPLY_HEALTH_UNSPEC_FAILURE
+ : POWER_SUPPLY_HEALTH_GOOD;
+ power_supply_changed(chip->usb_psy);
+ }
+ schedule_work(&chip->usb_set_online_work);
+
+ if (!chip->hvdcp_not_supported &&
+ (usb_supply_type == POWER_SUPPLY_TYPE_USB_DCP)) {
+ 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));
+ }
+
+ if (parallel_psy) {
+ pval.intval = true;
+ rc = power_supply_set_property(parallel_psy,
+ POWER_SUPPLY_PROP_PRESENT, &pval);
+ chip->parallel_charger_detected = rc ? false : true;
+ if (rc)
+ pr_debug("parallel-charger absent rc=%d\n", rc);
+ }
+
+ if (chip->parallel.avail && chip->aicl_done_irq
+ && !chip->enable_aicl_wake) {
+ rc = enable_irq_wake(chip->aicl_done_irq);
+ chip->enable_aicl_wake = true;
+ }
+}
+
+void update_usb_status(struct smbchg_chip *chip, bool usb_present, bool force)
+{
+ mutex_lock(&chip->usb_status_lock);
+ if (force) {
+ chip->usb_present = usb_present;
+ chip->usb_present ? handle_usb_insertion(chip)
+ : handle_usb_removal(chip);
+ goto unlock;
+ }
+ if (!chip->usb_present && usb_present) {
+ chip->usb_present = usb_present;
+ handle_usb_insertion(chip);
+ } else if (chip->usb_present && !usb_present) {
+ chip->usb_present = usb_present;
+ handle_usb_removal(chip);
+ }
+
+ /* update FG */
+ set_property_on_fg(chip, POWER_SUPPLY_PROP_STATUS,
+ get_prop_batt_status(chip));
+unlock:
+ mutex_unlock(&chip->usb_status_lock);
+}
+
+static int otg_oc_reset(struct smbchg_chip *chip)
+{
+ int rc;
+
+ rc = smbchg_masked_write(chip, chip->bat_if_base + CMD_CHG_REG,
+ OTG_EN_BIT, 0);
+ if (rc)
+ pr_err("Failed to disable OTG rc=%d\n", rc);
+
+ msleep(20);
+
+ /*
+ * There is a possibility that an USBID interrupt might have
+ * occurred notifying USB power supply to disable OTG. We
+ * should not enable OTG in such cases.
+ */
+ if (!is_otg_present(chip)) {
+ pr_smb(PR_STATUS,
+ "OTG is not present, not enabling OTG_EN_BIT\n");
+ goto out;
+ }
+
+ rc = smbchg_masked_write(chip, chip->bat_if_base + CMD_CHG_REG,
+ OTG_EN_BIT, OTG_EN_BIT);
+ if (rc)
+ pr_err("Failed to re-enable OTG rc=%d\n", rc);
+
+out:
+ return rc;
+}
+
+static int get_current_time(unsigned long *now_tm_sec)
+{
+ struct rtc_time tm;
+ struct rtc_device *rtc;
+ int rc;
+
+ rtc = rtc_class_open(CONFIG_RTC_HCTOSYS_DEVICE);
+ if (rtc == NULL) {
+ pr_err("%s: unable to open rtc device (%s)\n",
+ __FILE__, CONFIG_RTC_HCTOSYS_DEVICE);
+ return -EINVAL;
+ }
+
+ rc = rtc_read_time(rtc, &tm);
+ if (rc) {
+ pr_err("Error reading rtc device (%s) : %d\n",
+ CONFIG_RTC_HCTOSYS_DEVICE, rc);
+ goto close_time;
+ }
+
+ rc = rtc_valid_tm(&tm);
+ if (rc) {
+ pr_err("Invalid RTC time (%s): %d\n",
+ CONFIG_RTC_HCTOSYS_DEVICE, rc);
+ goto close_time;
+ }
+ rtc_tm_to_time(&tm, now_tm_sec);
+
+close_time:
+ rtc_class_close(rtc);
+ return rc;
+}
+
+#define AICL_IRQ_LIMIT_SECONDS 60
+#define AICL_IRQ_LIMIT_COUNT 25
+static void increment_aicl_count(struct smbchg_chip *chip)
+{
+ bool bad_charger = false;
+ int max_aicl_count, rc;
+ u8 reg;
+ long elapsed_seconds;
+ unsigned long now_seconds;
+
+ pr_smb(PR_INTERRUPT, "aicl count c:%d dgltch:%d first:%ld\n",
+ chip->aicl_irq_count, chip->aicl_deglitch_short,
+ chip->first_aicl_seconds);
+
+ rc = smbchg_read(chip, ®,
+ chip->usb_chgpth_base + ICL_STS_1_REG, 1);
+ if (!rc)
+ chip->aicl_complete = reg & AICL_STS_BIT;
+ else
+ chip->aicl_complete = false;
+
+ if (chip->aicl_deglitch_short || chip->force_aicl_rerun) {
+ if (!chip->aicl_irq_count)
+ get_current_time(&chip->first_aicl_seconds);
+ get_current_time(&now_seconds);
+ elapsed_seconds = now_seconds
+ - chip->first_aicl_seconds;
+
+ if (elapsed_seconds > AICL_IRQ_LIMIT_SECONDS) {
+ pr_smb(PR_INTERRUPT,
+ "resetting: elp:%ld first:%ld now:%ld c=%d\n",
+ elapsed_seconds, chip->first_aicl_seconds,
+ now_seconds, chip->aicl_irq_count);
+ chip->aicl_irq_count = 1;
+ get_current_time(&chip->first_aicl_seconds);
+ return;
+ }
+ /*
+ * Double the amount of AICLs allowed if parallel charging is
+ * enabled.
+ */
+ max_aicl_count = AICL_IRQ_LIMIT_COUNT
+ * (chip->parallel.avail ? 2 : 1);
+ chip->aicl_irq_count++;
+
+ if (chip->aicl_irq_count > max_aicl_count) {
+ pr_smb(PR_INTERRUPT, "elp:%ld first:%ld now:%ld c=%d\n",
+ elapsed_seconds, chip->first_aicl_seconds,
+ now_seconds, chip->aicl_irq_count);
+ pr_smb(PR_INTERRUPT, "Disable AICL rerun\n");
+ chip->very_weak_charger = true;
+ bad_charger = true;
+
+ /*
+ * Disable AICL rerun since many interrupts were
+ * triggered in a short time
+ */
+ /* disable hw aicl */
+ rc = vote(chip->hw_aicl_rerun_disable_votable,
+ WEAK_CHARGER_HW_AICL_VOTER, true, 0);
+ if (rc < 0) {
+ pr_err("Couldn't disable hw aicl rerun rc=%d\n",
+ rc);
+ return;
+ }
+
+ /* Vote 100mA current limit */
+ rc = vote(chip->usb_icl_votable, WEAK_CHARGER_ICL_VOTER,
+ true, CURRENT_100_MA);
+ if (rc < 0) {
+ pr_err("Can't vote %d current limit rc=%d\n",
+ CURRENT_100_MA, rc);
+ }
+
+ chip->aicl_irq_count = 0;
+ } else if ((get_prop_charge_type(chip) ==
+ POWER_SUPPLY_CHARGE_TYPE_FAST) &&
+ (reg & AICL_SUSP_BIT)) {
+ /*
+ * If the AICL_SUSP_BIT is on, then AICL reruns have
+ * already been disabled. Set the very weak charger
+ * flag so that the driver reports a bad charger
+ * and does not reenable AICL reruns.
+ */
+ chip->very_weak_charger = true;
+ bad_charger = true;
+ }
+ if (bad_charger) {
+ pr_smb(PR_MISC,
+ "setting usb psy health UNSPEC_FAILURE\n");
+ chip->usb_health = POWER_SUPPLY_HEALTH_UNSPEC_FAILURE;
+ power_supply_changed(chip->usb_psy);
+ schedule_work(&chip->usb_set_online_work);
+ }
+ }
+}
+
+static int wait_for_usbin_uv(struct smbchg_chip *chip, bool high)
+{
+ int rc;
+ int tries = 3;
+ struct completion *completion = &chip->usbin_uv_lowered;
+ bool usbin_uv;
+
+ if (high)
+ completion = &chip->usbin_uv_raised;
+
+ while (tries--) {
+ rc = wait_for_completion_interruptible_timeout(
+ completion,
+ msecs_to_jiffies(APSD_TIMEOUT_MS));
+ if (rc >= 0)
+ break;
+ }
+
+ usbin_uv = is_usbin_uv_high(chip);
+
+ if (high == usbin_uv)
+ return 0;
+
+ pr_err("usbin uv didnt go to a %s state, still at %s, tries = %d, rc = %d\n",
+ high ? "risen" : "lowered",
+ usbin_uv ? "high" : "low",
+ tries, rc);
+ return -EINVAL;
+}
+
+static int wait_for_src_detect(struct smbchg_chip *chip, bool high)
+{
+ int rc;
+ int tries = 3;
+ struct completion *completion = &chip->src_det_lowered;
+ bool src_detect;
+
+ if (high)
+ completion = &chip->src_det_raised;
+
+ while (tries--) {
+ rc = wait_for_completion_interruptible_timeout(
+ completion,
+ msecs_to_jiffies(APSD_TIMEOUT_MS));
+ if (rc >= 0)
+ break;
+ }
+
+ src_detect = is_src_detect_high(chip);
+
+ if (high == src_detect)
+ return 0;
+
+ pr_err("src detect didn't go to a %s state, still at %s, tries = %d, rc = %d\n",
+ high ? "risen" : "lowered",
+ src_detect ? "high" : "low",
+ tries, rc);
+ return -EINVAL;
+}
+
+static int fake_insertion_removal(struct smbchg_chip *chip, bool insertion)
+{
+ int rc;
+ bool src_detect;
+ bool usbin_uv;
+
+ if (insertion) {
+ reinit_completion(&chip->src_det_raised);
+ reinit_completion(&chip->usbin_uv_lowered);
+ } else {
+ reinit_completion(&chip->src_det_lowered);
+ reinit_completion(&chip->usbin_uv_raised);
+ }
+
+ /* ensure that usbin uv real time status is in the right state */
+ usbin_uv = is_usbin_uv_high(chip);
+ if (usbin_uv != insertion) {
+ pr_err("Skip faking, usbin uv is already %d\n", usbin_uv);
+ return -EINVAL;
+ }
+
+ /* ensure that src_detect real time status is in the right state */
+ src_detect = is_src_detect_high(chip);
+ if (src_detect == insertion) {
+ pr_err("Skip faking, src detect is already %d\n", src_detect);
+ return -EINVAL;
+ }
+
+ pr_smb(PR_MISC, "Allow only %s charger\n",
+ insertion ? "5-9V" : "9V only");
+ rc = smbchg_sec_masked_write(chip,
+ chip->usb_chgpth_base + USBIN_CHGR_CFG,
+ ADAPTER_ALLOWANCE_MASK,
+ insertion ?
+ USBIN_ADAPTER_5V_9V_CONT : USBIN_ADAPTER_9V);
+ if (rc < 0) {
+ pr_err("Couldn't write usb allowance rc=%d\n", rc);
+ return rc;
+ }
+
+ pr_smb(PR_MISC, "Waiting on %s usbin uv\n",
+ insertion ? "falling" : "rising");
+ rc = wait_for_usbin_uv(chip, !insertion);
+ if (rc < 0) {
+ pr_err("wait for usbin uv failed rc = %d\n", rc);
+ return rc;
+ }
+
+ pr_smb(PR_MISC, "Waiting on %s src det\n",
+ insertion ? "rising" : "falling");
+ rc = wait_for_src_detect(chip, insertion);
+ if (rc < 0) {
+ pr_err("wait for src detect failed rc = %d\n", rc);
+ return rc;
+ }
+
+ return 0;
+}
+
+static int smbchg_prepare_for_pulsing(struct smbchg_chip *chip)
+{
+ int rc = 0;
+ u8 reg;
+
+ /* switch to 5V HVDCP */
+ pr_smb(PR_MISC, "Switch to 5V HVDCP\n");
+ rc = smbchg_sec_masked_write(chip, chip->usb_chgpth_base + CHGPTH_CFG,
+ HVDCP_ADAPTER_SEL_MASK, HVDCP_5V);
+ if (rc < 0) {
+ pr_err("Couldn't configure HVDCP 5V rc=%d\n", rc);
+ goto out;
+ }
+
+ /* wait for HVDCP to lower to 5V */
+ msleep(500);
+ /*
+ * Check if the same hvdcp session is in progress. src_det should be
+ * high and that we are still in 5V hvdcp
+ */
+ if (!is_src_detect_high(chip)) {
+ pr_smb(PR_MISC, "src det low after 500mS sleep\n");
+ goto out;
+ }
+
+ /* disable HVDCP */
+ pr_smb(PR_MISC, "Disable HVDCP\n");
+ rc = smbchg_sec_masked_write(chip, chip->usb_chgpth_base + CHGPTH_CFG,
+ HVDCP_EN_BIT, 0);
+ if (rc < 0) {
+ pr_err("Couldn't disable HVDCP rc=%d\n", rc);
+ goto out;
+ }
+
+ pr_smb(PR_MISC, "HVDCP voting for 300mA ICL\n");
+ rc = vote(chip->usb_icl_votable, HVDCP_ICL_VOTER, true, 300);
+ if (rc < 0) {
+ pr_err("Couldn't vote for 300mA HVDCP ICL rc=%d\n", rc);
+ goto out;
+ }
+
+ pr_smb(PR_MISC, "Disable AICL\n");
+ smbchg_sec_masked_write(chip, chip->usb_chgpth_base + USB_AICL_CFG,
+ AICL_EN_BIT, 0);
+
+ chip->hvdcp_3_det_ignore_uv = true;
+ /* fake a removal */
+ pr_smb(PR_MISC, "Faking Removal\n");
+ rc = fake_insertion_removal(chip, false);
+ if (rc < 0) {
+ pr_err("Couldn't fake removal HVDCP Removed rc=%d\n", rc);
+ goto handle_removal;
+ }
+
+ /* disable APSD */
+ pr_smb(PR_MISC, "Disabling APSD\n");
+ rc = smbchg_sec_masked_write(chip,
+ chip->usb_chgpth_base + APSD_CFG,
+ AUTO_SRC_DETECT_EN_BIT, 0);
+ if (rc < 0) {
+ pr_err("Couldn't disable APSD rc=%d\n", rc);
+ goto out;
+ }
+
+ /* fake an insertion */
+ pr_smb(PR_MISC, "Faking Insertion\n");
+ rc = fake_insertion_removal(chip, true);
+ if (rc < 0) {
+ pr_err("Couldn't fake insertion rc=%d\n", rc);
+ goto handle_removal;
+ }
+ 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);
+
+ set_usb_psy_dp_dm(chip, POWER_SUPPLY_DP_DM_DP0P6_DMF);
+ /*
+ * DCP will switch to HVDCP in this time by removing the short
+ * between DP DM
+ */
+ msleep(HVDCP_NOTIFY_MS);
+ /*
+ * Check if the same hvdcp session is in progress. src_det should be
+ * high and the usb type should be none since APSD was disabled
+ */
+ if (!is_src_detect_high(chip)) {
+ pr_smb(PR_MISC, "src det low after 2s sleep\n");
+ rc = -EINVAL;
+ goto out;
+ }
+
+ smbchg_read(chip, ®, chip->misc_base + IDEV_STS, 1);
+ if ((reg >> TYPE_BITS_OFFSET) != 0) {
+ pr_smb(PR_MISC, "type bits set after 2s sleep - abort\n");
+ rc = -EINVAL;
+ goto out;
+ }
+
+ set_usb_psy_dp_dm(chip, POWER_SUPPLY_DP_DM_DP0P6_DM3P3);
+ /* Wait 60mS after entering continuous mode */
+ msleep(60);
+
+ return 0;
+out:
+ chip->hvdcp_3_det_ignore_uv = false;
+ restore_from_hvdcp_detection(chip);
+ return rc;
+handle_removal:
+ chip->hvdcp_3_det_ignore_uv = false;
+ update_usb_status(chip, 0, 0);
+ return rc;
+}
+
+static int smbchg_unprepare_for_pulsing(struct smbchg_chip *chip)
+{
+ int rc = 0;
+
+ if (chip->dpdm_reg && !regulator_is_enabled(chip->dpdm_reg))
+ rc = regulator_enable(chip->dpdm_reg);
+ if (rc < 0) {
+ pr_err("Couldn't enable DP/DM for pulsing rc=%d\n", rc);
+ return rc;
+ }
+
+ /* switch to 9V HVDCP */
+ pr_smb(PR_MISC, "Switch to 9V HVDCP\n");
+ rc = smbchg_sec_masked_write(chip, chip->usb_chgpth_base + CHGPTH_CFG,
+ HVDCP_ADAPTER_SEL_MASK, HVDCP_9V);
+ if (rc < 0) {
+ pr_err("Couldn't configure HVDCP 9V rc=%d\n", rc);
+ return rc;
+ }
+
+ /* enable HVDCP */
+ pr_smb(PR_MISC, "Enable HVDCP\n");
+ rc = smbchg_sec_masked_write(chip,
+ chip->usb_chgpth_base + CHGPTH_CFG,
+ HVDCP_EN_BIT, HVDCP_EN_BIT);
+ if (rc < 0) {
+ pr_err("Couldn't enable HVDCP rc=%d\n", rc);
+ return rc;
+ }
+
+ /* enable APSD */
+ pr_smb(PR_MISC, "Enabling APSD\n");
+ rc = smbchg_sec_masked_write(chip,
+ chip->usb_chgpth_base + APSD_CFG,
+ AUTO_SRC_DETECT_EN_BIT, AUTO_SRC_DETECT_EN_BIT);
+ if (rc < 0) {
+ pr_err("Couldn't enable APSD rc=%d\n", rc);
+ return rc;
+ }
+
+ /* Disable AICL */
+ pr_smb(PR_MISC, "Disable 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 disable AICL rc=%d\n", rc);
+ return rc;
+ }
+
+ /* fake a removal */
+ chip->hvdcp_3_det_ignore_uv = true;
+ pr_smb(PR_MISC, "Faking Removal\n");
+ rc = fake_insertion_removal(chip, false);
+ if (rc < 0) {
+ pr_err("Couldn't fake removal rc=%d\n", rc);
+ goto out;
+ }
+
+ /*
+ * reset the enabled once flag for parallel charging so
+ * parallel charging can immediately restart after the HVDCP pulsing
+ * is complete
+ */
+ chip->parallel.enabled_once = false;
+
+ /* fake an insertion */
+ pr_smb(PR_MISC, "Faking Insertion\n");
+ rc = fake_insertion_removal(chip, true);
+ if (rc < 0) {
+ pr_err("Couldn't fake insertion rc=%d\n", rc);
+ goto out;
+ }
+ 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
+ * timer can mitigate. Hence set the aicl deglitch time to a shorter
+ * period.
+ */
+
+ rc = vote(chip->aicl_deglitch_short_votable,
+ HVDCP_SHORT_DEGLITCH_VOTER, true, 0);
+ if (rc < 0)
+ pr_err("Couldn't reduce aicl deglitch rc=%d\n", rc);
+
+ pr_smb(PR_MISC, "Retracting HVDCP vote for ICL\n");
+ rc = vote(chip->usb_icl_votable, HVDCP_ICL_VOTER, false, 0);
+ if (rc < 0)
+ pr_err("Couldn't retract HVDCP ICL vote rc=%d\n", rc);
+
+ chip->hvdcp_3_det_ignore_uv = false;
+ if (!is_src_detect_high(chip)) {
+ pr_smb(PR_MISC, "HVDCP removed\n");
+ update_usb_status(chip, 0, 0);
+ }
+ return rc;
+}
+
+#define USB_CMD_APSD 0x41
+#define APSD_RERUN BIT(0)
+static int rerun_apsd(struct smbchg_chip *chip)
+{
+ int rc;
+
+ 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);
+ return rc;
+ }
+
+ 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;
+ }
+
+ return rc;
+}
+
+#define SCHG_LITE_USBIN_HVDCP_5_9V 0x8
+#define SCHG_LITE_USBIN_HVDCP_5_9V_SEL_MASK 0x38
+#define SCHG_LITE_USBIN_HVDCP_SEL_IDLE BIT(3)
+static bool is_hvdcp_5v_cont_mode(struct smbchg_chip *chip)
+{
+ int rc;
+ u8 reg = 0;
+
+ rc = smbchg_read(chip, ®,
+ chip->usb_chgpth_base + USBIN_HVDCP_STS, 1);
+ if (rc) {
+ pr_err("Unable to read HVDCP status rc=%d\n", rc);
+ return false;
+ }
+
+ pr_smb(PR_STATUS, "HVDCP status = %x\n", reg);
+
+ if (reg & SCHG_LITE_USBIN_HVDCP_SEL_IDLE) {
+ rc = smbchg_read(chip, ®,
+ chip->usb_chgpth_base + INPUT_STS, 1);
+ if (rc) {
+ pr_err("Unable to read INPUT status rc=%d\n", rc);
+ return false;
+ }
+ pr_smb(PR_STATUS, "INPUT status = %x\n", reg);
+ if ((reg & SCHG_LITE_USBIN_HVDCP_5_9V_SEL_MASK) ==
+ SCHG_LITE_USBIN_HVDCP_5_9V)
+ return true;
+ }
+ return false;
+}
+
+static int smbchg_prepare_for_pulsing_lite(struct smbchg_chip *chip)
+{
+ int rc = 0;
+
+ /* check if HVDCP is already in 5V continuous mode */
+ if (is_hvdcp_5v_cont_mode(chip)) {
+ pr_smb(PR_MISC, "HVDCP by default is in 5V continuous mode\n");
+ return 0;
+ }
+
+ /* switch to 5V HVDCP */
+ pr_smb(PR_MISC, "Switch to 5V HVDCP\n");
+ rc = smbchg_sec_masked_write(chip, chip->usb_chgpth_base + CHGPTH_CFG,
+ HVDCP_ADAPTER_SEL_MASK, HVDCP_5V);
+ if (rc < 0) {
+ pr_err("Couldn't configure HVDCP 5V rc=%d\n", rc);
+ goto out;
+ }
+
+ /* wait for HVDCP to lower to 5V */
+ msleep(500);
+ /*
+ * Check if the same hvdcp session is in progress. src_det should be
+ * high and that we are still in 5V hvdcp
+ */
+ if (!is_src_detect_high(chip)) {
+ pr_smb(PR_MISC, "src det low after 500mS sleep\n");
+ goto out;
+ }
+
+ pr_smb(PR_MISC, "HVDCP voting for 300mA ICL\n");
+ rc = vote(chip->usb_icl_votable, HVDCP_ICL_VOTER, true, 300);
+ if (rc < 0) {
+ pr_err("Couldn't vote for 300mA HVDCP ICL rc=%d\n", rc);
+ goto out;
+ }
+
+ pr_smb(PR_MISC, "Disable AICL\n");
+ 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) {
+ pr_err("APSD rerun failed\n");
+ 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);
+ /*
+ * DCP will switch to HVDCP in this time by removing the short
+ * between DP DM
+ */
+ msleep(HVDCP_NOTIFY_MS);
+ /*
+ * Check if the same hvdcp session is in progress. src_det should be
+ * high and the usb type should be none since APSD was disabled
+ */
+ if (!is_src_detect_high(chip)) {
+ pr_smb(PR_MISC, "src det low after 2s sleep\n");
+ rc = -EINVAL;
+ goto out;
+ }
+
+ /* We are set if HVDCP in 5V continuous mode */
+ if (!is_hvdcp_5v_cont_mode(chip)) {
+ pr_err("HVDCP could not be set in 5V continuous mode\n");
+ goto out;
+ }
+
+ return 0;
+out:
+ chip->hvdcp_3_det_ignore_uv = false;
+ restore_from_hvdcp_detection(chip);
+ return rc;
+}
+
+static int smbchg_unprepare_for_pulsing_lite(struct smbchg_chip *chip)
+{
+ int rc = 0;
+
+ pr_smb(PR_MISC, "Forcing 9V HVDCP 2.0\n");
+ rc = force_9v_hvdcp(chip);
+ if (rc) {
+ pr_err("Failed to force 9V HVDCP=%d\n", rc);
+ return rc;
+ }
+
+ pr_smb(PR_MISC, "Retracting HVDCP vote for ICL\n");
+ rc = vote(chip->usb_icl_votable, HVDCP_ICL_VOTER, false, 0);
+ if (rc < 0)
+ pr_err("Couldn't retract HVDCP ICL vote rc=%d\n", rc);
+
+ return rc;
+}
+
+#define CMD_HVDCP_2 0x43
+#define SINGLE_INCREMENT BIT(0)
+#define SINGLE_DECREMENT BIT(1)
+static int smbchg_dp_pulse_lite(struct smbchg_chip *chip)
+{
+ int rc = 0;
+
+ pr_smb(PR_MISC, "Increment DP\n");
+ rc = smbchg_masked_write(chip, chip->usb_chgpth_base + CMD_HVDCP_2,
+ SINGLE_INCREMENT, SINGLE_INCREMENT);
+ if (rc)
+ pr_err("Single-increment failed rc=%d\n", rc);
+
+ return rc;
+}
+
+static int smbchg_dm_pulse_lite(struct smbchg_chip *chip)
+{
+ int rc = 0;
+
+ pr_smb(PR_MISC, "Decrement DM\n");
+ rc = smbchg_masked_write(chip, chip->usb_chgpth_base + CMD_HVDCP_2,
+ SINGLE_DECREMENT, SINGLE_DECREMENT);
+ if (rc)
+ pr_err("Single-decrement failed rc=%d\n", rc);
+
+ return rc;
+}
+
+static int smbchg_hvdcp3_confirmed(struct smbchg_chip *chip)
+{
+ int rc = 0;
+
+ /*
+ * reset the enabled once flag for parallel charging because this is
+ * effectively a new insertion.
+ */
+ chip->parallel.enabled_once = false;
+
+ pr_smb(PR_MISC, "Retracting HVDCP vote for ICL\n");
+ rc = vote(chip->usb_icl_votable, HVDCP_ICL_VOTER, false, 0);
+ if (rc < 0)
+ pr_err("Couldn't retract HVDCP ICL vote rc=%d\n", rc);
+
+ smbchg_change_usb_supply_type(chip, POWER_SUPPLY_TYPE_USB_HVDCP_3);
+
+ return rc;
+}
+
+static int smbchg_dp_dm(struct smbchg_chip *chip, int val)
+{
+ int rc = 0;
+ int target_icl_vote_ma;
+
+ switch (val) {
+ case POWER_SUPPLY_DP_DM_PREPARE:
+ if (!is_hvdcp_present(chip)) {
+ pr_err("No pulsing unless HVDCP\n");
+ return -ENODEV;
+ }
+ if (chip->schg_version == QPNP_SCHG_LITE)
+ rc = smbchg_prepare_for_pulsing_lite(chip);
+ else
+ rc = smbchg_prepare_for_pulsing(chip);
+ break;
+ case POWER_SUPPLY_DP_DM_UNPREPARE:
+ if (chip->schg_version == QPNP_SCHG_LITE)
+ rc = smbchg_unprepare_for_pulsing_lite(chip);
+ else
+ rc = smbchg_unprepare_for_pulsing(chip);
+ break;
+ case POWER_SUPPLY_DP_DM_CONFIRMED_HVDCP3:
+ rc = smbchg_hvdcp3_confirmed(chip);
+ break;
+ case POWER_SUPPLY_DP_DM_DP_PULSE:
+ if (chip->schg_version == QPNP_SCHG)
+ rc = set_usb_psy_dp_dm(chip,
+ POWER_SUPPLY_DP_DM_DP_PULSE);
+ else
+ rc = smbchg_dp_pulse_lite(chip);
+ if (!rc)
+ chip->pulse_cnt++;
+ pr_smb(PR_MISC, "pulse_cnt = %d\n", chip->pulse_cnt);
+ break;
+ case POWER_SUPPLY_DP_DM_DM_PULSE:
+ if (chip->schg_version == QPNP_SCHG)
+ rc = set_usb_psy_dp_dm(chip,
+ POWER_SUPPLY_DP_DM_DM_PULSE);
+ else
+ rc = smbchg_dm_pulse_lite(chip);
+ if (!rc && chip->pulse_cnt)
+ chip->pulse_cnt--;
+ pr_smb(PR_MISC, "pulse_cnt = %d\n", chip->pulse_cnt);
+ break;
+ case POWER_SUPPLY_DP_DM_HVDCP3_SUPPORTED:
+ chip->hvdcp3_supported = true;
+ pr_smb(PR_MISC, "HVDCP3 supported\n");
+ break;
+ case POWER_SUPPLY_DP_DM_ICL_DOWN:
+ chip->usb_icl_delta -= 100;
+ target_icl_vote_ma = get_client_vote(chip->usb_icl_votable,
+ PSY_ICL_VOTER);
+ vote(chip->usb_icl_votable, SW_AICL_ICL_VOTER, true,
+ target_icl_vote_ma + chip->usb_icl_delta);
+ break;
+ case POWER_SUPPLY_DP_DM_ICL_UP:
+ chip->usb_icl_delta += 100;
+ target_icl_vote_ma = get_client_vote(chip->usb_icl_votable,
+ PSY_ICL_VOTER);
+ vote(chip->usb_icl_votable, SW_AICL_ICL_VOTER, true,
+ target_icl_vote_ma + chip->usb_icl_delta);
+ break;
+ default:
+ break;
+ }
+
+ return rc;
+}
+
+static void update_typec_capability_status(struct smbchg_chip *chip,
+ const union power_supply_propval *val)
+{
+ pr_smb(PR_TYPEC, "typec capability = %dma\n", val->intval);
+
+ pr_debug("changing ICL from %dma to %dma\n", chip->typec_current_ma,
+ val->intval);
+ chip->typec_current_ma = val->intval;
+ smbchg_change_usb_supply_type(chip, chip->usb_supply_type);
+}
+
+static void update_typec_otg_status(struct smbchg_chip *chip, int mode,
+ bool force)
+{
+ union power_supply_propval pval = {0, };
+
+ pr_smb(PR_TYPEC, "typec mode = %d\n", mode);
+
+ if (mode == POWER_SUPPLY_TYPE_DFP) {
+ chip->typec_dfp = true;
+ pval.intval = 1;
+ extcon_set_cable_state_(chip->extcon, EXTCON_USB_HOST,
+ chip->typec_dfp);
+ /* update FG */
+ set_property_on_fg(chip, POWER_SUPPLY_PROP_STATUS,
+ get_prop_batt_status(chip));
+ } else if (force || chip->typec_dfp) {
+ chip->typec_dfp = false;
+ pval.intval = 0;
+ extcon_set_cable_state_(chip->extcon, EXTCON_USB_HOST,
+ chip->typec_dfp);
+ /* update FG */
+ set_property_on_fg(chip, POWER_SUPPLY_PROP_STATUS,
+ get_prop_batt_status(chip));
+ }
+}
+
+static int smbchg_usb_get_property(struct power_supply *psy,
+ enum power_supply_property psp,
+ union power_supply_propval *val)
+{
+ struct smbchg_chip *chip = power_supply_get_drvdata(psy);
+
+ switch (psp) {
+ case POWER_SUPPLY_PROP_CURRENT_MAX:
+ val->intval = chip->usb_current_max;
+ break;
+ case POWER_SUPPLY_PROP_PRESENT:
+ val->intval = chip->usb_present;
+ break;
+ case POWER_SUPPLY_PROP_ONLINE:
+ val->intval = chip->usb_online;
+ break;
+ case POWER_SUPPLY_PROP_TYPE:
+ val->intval = chip->usb_supply_type;
+ break;
+ case POWER_SUPPLY_PROP_HEALTH:
+ val->intval = chip->usb_health;
+ break;
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static int smbchg_usb_set_property(struct power_supply *psy,
+ enum power_supply_property psp,
+ const union power_supply_propval *val)
+{
+ struct smbchg_chip *chip = power_supply_get_drvdata(psy);
+
+ switch (psp) {
+ case POWER_SUPPLY_PROP_CURRENT_MAX:
+ chip->usb_current_max = val->intval;
+ break;
+ case POWER_SUPPLY_PROP_ONLINE:
+ chip->usb_online = val->intval;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ power_supply_changed(psy);
+ return 0;
+}
+
+static int
+smbchg_usb_is_writeable(struct power_supply *psy,
+ enum power_supply_property psp)
+{
+ switch (psp) {
+ case POWER_SUPPLY_PROP_CURRENT_MAX:
+ return 1;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+
+static char *smbchg_usb_supplicants[] = {
+ "battery",
+ "bms",
+};
+
+static enum power_supply_property smbchg_usb_properties[] = {
+ POWER_SUPPLY_PROP_PRESENT,
+ POWER_SUPPLY_PROP_ONLINE,
+ POWER_SUPPLY_PROP_CURRENT_MAX,
+ POWER_SUPPLY_PROP_TYPE,
+ POWER_SUPPLY_PROP_HEALTH,
+};
+
+#define CHARGE_OUTPUT_VTG_RATIO 840
+static int smbchg_get_iusb(struct smbchg_chip *chip)
+{
+ int rc, iusb_ua = -EINVAL;
+ struct qpnp_vadc_result adc_result;
+
+ if (!is_usb_present(chip) && !is_dc_present(chip))
+ return 0;
+
+ if (chip->vchg_vadc_dev && chip->vchg_adc_channel != -EINVAL) {
+ rc = qpnp_vadc_read(chip->vchg_vadc_dev,
+ chip->vchg_adc_channel, &adc_result);
+ if (rc) {
+ pr_smb(PR_STATUS,
+ "error in VCHG (channel-%d) read rc = %d\n",
+ chip->vchg_adc_channel, rc);
+ return 0;
+ }
+ iusb_ua = div_s64(adc_result.physical * 1000,
+ CHARGE_OUTPUT_VTG_RATIO);
+ }
+
+ return iusb_ua;
+}
+
+static enum power_supply_property smbchg_battery_properties[] = {
+ POWER_SUPPLY_PROP_STATUS,
+ POWER_SUPPLY_PROP_PRESENT,
+ POWER_SUPPLY_PROP_BATTERY_CHARGING_ENABLED,
+ POWER_SUPPLY_PROP_CHARGING_ENABLED,
+ POWER_SUPPLY_PROP_CHARGE_TYPE,
+ POWER_SUPPLY_PROP_CAPACITY,
+ POWER_SUPPLY_PROP_HEALTH,
+ POWER_SUPPLY_PROP_TECHNOLOGY,
+ POWER_SUPPLY_PROP_SYSTEM_TEMP_LEVEL,
+ POWER_SUPPLY_PROP_FLASH_CURRENT_MAX,
+ POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX,
+ POWER_SUPPLY_PROP_VOLTAGE_MAX,
+ POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN,
+ POWER_SUPPLY_PROP_CURRENT_NOW,
+ POWER_SUPPLY_PROP_TEMP,
+ POWER_SUPPLY_PROP_VOLTAGE_NOW,
+ POWER_SUPPLY_PROP_SAFETY_TIMER_ENABLE,
+ POWER_SUPPLY_PROP_INPUT_CURRENT_MAX,
+ POWER_SUPPLY_PROP_INPUT_CURRENT_SETTLED,
+ POWER_SUPPLY_PROP_INPUT_CURRENT_NOW,
+ POWER_SUPPLY_PROP_FLASH_ACTIVE,
+ POWER_SUPPLY_PROP_FLASH_TRIGGER,
+ POWER_SUPPLY_PROP_DP_DM,
+ POWER_SUPPLY_PROP_INPUT_CURRENT_LIMITED,
+ POWER_SUPPLY_PROP_RERUN_AICL,
+ POWER_SUPPLY_PROP_RESTRICTED_CHARGING,
+};
+
+static int smbchg_battery_set_property(struct power_supply *psy,
+ enum power_supply_property prop,
+ const union power_supply_propval *val)
+{
+ int rc = 0;
+ struct smbchg_chip *chip = power_supply_get_drvdata(psy);
+
+ switch (prop) {
+ case POWER_SUPPLY_PROP_BATTERY_CHARGING_ENABLED:
+ vote(chip->battchg_suspend_votable, BATTCHG_USER_EN_VOTER,
+ !val->intval, 0);
+ break;
+ case POWER_SUPPLY_PROP_CHARGING_ENABLED:
+ rc = vote(chip->usb_suspend_votable, USER_EN_VOTER,
+ !val->intval, 0);
+ rc = vote(chip->dc_suspend_votable, USER_EN_VOTER,
+ !val->intval, 0);
+ chip->chg_enabled = val->intval;
+ schedule_work(&chip->usb_set_online_work);
+ break;
+ case POWER_SUPPLY_PROP_CAPACITY:
+ chip->fake_battery_soc = val->intval;
+ if (chip->batt_psy)
+ power_supply_changed(chip->batt_psy);
+ break;
+ case POWER_SUPPLY_PROP_SYSTEM_TEMP_LEVEL:
+ smbchg_system_temp_level_set(chip, val->intval);
+ break;
+ case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX:
+ rc = smbchg_set_fastchg_current_user(chip, val->intval / 1000);
+ break;
+ case POWER_SUPPLY_PROP_VOLTAGE_MAX:
+ rc = smbchg_float_voltage_set(chip, val->intval);
+ break;
+ case POWER_SUPPLY_PROP_SAFETY_TIMER_ENABLE:
+ rc = smbchg_safety_timer_enable(chip, val->intval);
+ break;
+ case POWER_SUPPLY_PROP_FLASH_ACTIVE:
+ rc = smbchg_switch_buck_frequency(chip, val->intval);
+ if (rc) {
+ pr_err("Couldn't switch buck frequency, rc=%d\n", rc);
+ /*
+ * Trigger a panic if there is an error while switching
+ * buck frequency. This will prevent LS FET damage.
+ */
+ WARN_ON(1);
+ }
+
+ rc = smbchg_otg_pulse_skip_disable(chip,
+ REASON_FLASH_ENABLED, val->intval);
+ break;
+ case POWER_SUPPLY_PROP_FLASH_TRIGGER:
+ chip->flash_triggered = !!val->intval;
+ smbchg_icl_loop_disable_check(chip);
+ break;
+ case POWER_SUPPLY_PROP_FORCE_TLIM:
+ rc = smbchg_force_tlim_en(chip, val->intval);
+ break;
+ case POWER_SUPPLY_PROP_DP_DM:
+ rc = smbchg_dp_dm(chip, val->intval);
+ break;
+ case POWER_SUPPLY_PROP_RERUN_AICL:
+ smbchg_rerun_aicl(chip);
+ break;
+ case POWER_SUPPLY_PROP_RESTRICTED_CHARGING:
+ rc = smbchg_restricted_charging(chip, val->intval);
+ break;
+ case POWER_SUPPLY_PROP_CURRENT_CAPABILITY:
+ if (chip->typec_psy)
+ update_typec_capability_status(chip, val);
+ break;
+ case POWER_SUPPLY_PROP_TYPEC_MODE:
+ if (chip->typec_psy)
+ update_typec_otg_status(chip, val->intval, false);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return rc;
+}
+
+static int smbchg_battery_is_writeable(struct power_supply *psy,
+ enum power_supply_property prop)
+{
+ int rc;
+
+ switch (prop) {
+ case POWER_SUPPLY_PROP_BATTERY_CHARGING_ENABLED:
+ case POWER_SUPPLY_PROP_CHARGING_ENABLED:
+ case POWER_SUPPLY_PROP_CAPACITY:
+ case POWER_SUPPLY_PROP_SYSTEM_TEMP_LEVEL:
+ case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX:
+ case POWER_SUPPLY_PROP_VOLTAGE_MAX:
+ case POWER_SUPPLY_PROP_SAFETY_TIMER_ENABLE:
+ case POWER_SUPPLY_PROP_DP_DM:
+ case POWER_SUPPLY_PROP_RERUN_AICL:
+ case POWER_SUPPLY_PROP_RESTRICTED_CHARGING:
+ rc = 1;
+ break;
+ default:
+ rc = 0;
+ break;
+ }
+ return rc;
+}
+
+static int smbchg_battery_get_property(struct power_supply *psy,
+ enum power_supply_property prop,
+ union power_supply_propval *val)
+{
+ struct smbchg_chip *chip = power_supply_get_drvdata(psy);
+
+ switch (prop) {
+ case POWER_SUPPLY_PROP_STATUS:
+ val->intval = get_prop_batt_status(chip);
+ break;
+ case POWER_SUPPLY_PROP_PRESENT:
+ val->intval = get_prop_batt_present(chip);
+ break;
+ case POWER_SUPPLY_PROP_BATTERY_CHARGING_ENABLED:
+ val->intval
+ = get_effective_result(chip->battchg_suspend_votable);
+ if (val->intval < 0) /* no votes */
+ val->intval = 1;
+ else
+ val->intval = !val->intval;
+ break;
+ case POWER_SUPPLY_PROP_CHARGING_ENABLED:
+ val->intval = chip->chg_enabled;
+ break;
+ case POWER_SUPPLY_PROP_CHARGE_TYPE:
+ val->intval = get_prop_charge_type(chip);
+ break;
+ case POWER_SUPPLY_PROP_VOLTAGE_MAX:
+ val->intval = smbchg_float_voltage_get(chip);
+ break;
+ case POWER_SUPPLY_PROP_HEALTH:
+ val->intval = get_prop_batt_health(chip);
+ break;
+ case POWER_SUPPLY_PROP_TECHNOLOGY:
+ val->intval = POWER_SUPPLY_TECHNOLOGY_LION;
+ break;
+ case POWER_SUPPLY_PROP_FLASH_CURRENT_MAX:
+ val->intval = smbchg_calc_max_flash_current(chip);
+ break;
+ case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX:
+ val->intval = chip->fastchg_current_ma * 1000;
+ break;
+ case POWER_SUPPLY_PROP_SYSTEM_TEMP_LEVEL:
+ val->intval = chip->therm_lvl_sel;
+ break;
+ case POWER_SUPPLY_PROP_INPUT_CURRENT_MAX:
+ val->intval = smbchg_get_aicl_level_ma(chip) * 1000;
+ break;
+ case POWER_SUPPLY_PROP_INPUT_CURRENT_SETTLED:
+ val->intval = (int)chip->aicl_complete;
+ break;
+ case POWER_SUPPLY_PROP_RESTRICTED_CHARGING:
+ val->intval = (int)chip->restricted_charging;
+ break;
+ /* properties from fg */
+ case POWER_SUPPLY_PROP_CAPACITY:
+ val->intval = get_prop_batt_capacity(chip);
+ break;
+ case POWER_SUPPLY_PROP_CURRENT_NOW:
+ val->intval = get_prop_batt_current_now(chip);
+ break;
+ case POWER_SUPPLY_PROP_VOLTAGE_NOW:
+ val->intval = get_prop_batt_voltage_now(chip);
+ break;
+ case POWER_SUPPLY_PROP_TEMP:
+ val->intval = get_prop_batt_temp(chip);
+ break;
+ case POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN:
+ val->intval = get_prop_batt_voltage_max_design(chip);
+ break;
+ case POWER_SUPPLY_PROP_SAFETY_TIMER_ENABLE:
+ val->intval = chip->safety_timer_en;
+ break;
+ case POWER_SUPPLY_PROP_FLASH_ACTIVE:
+ val->intval = chip->otg_pulse_skip_dis;
+ break;
+ case POWER_SUPPLY_PROP_FLASH_TRIGGER:
+ val->intval = chip->flash_triggered;
+ break;
+ case POWER_SUPPLY_PROP_DP_DM:
+ val->intval = chip->pulse_cnt;
+ break;
+ case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMITED:
+ val->intval = smbchg_is_input_current_limited(chip);
+ break;
+ case POWER_SUPPLY_PROP_RERUN_AICL:
+ val->intval = 0;
+ break;
+ case POWER_SUPPLY_PROP_INPUT_CURRENT_NOW:
+ val->intval = smbchg_get_iusb(chip);
+ break;
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static char *smbchg_dc_supplicants[] = {
+ "bms",
+};
+
+static enum power_supply_property smbchg_dc_properties[] = {
+ POWER_SUPPLY_PROP_PRESENT,
+ POWER_SUPPLY_PROP_ONLINE,
+ POWER_SUPPLY_PROP_CHARGING_ENABLED,
+ POWER_SUPPLY_PROP_CURRENT_MAX,
+};
+
+static int smbchg_dc_set_property(struct power_supply *psy,
+ enum power_supply_property prop,
+ const union power_supply_propval *val)
+{
+ int rc = 0;
+ struct smbchg_chip *chip = power_supply_get_drvdata(psy);
+
+ switch (prop) {
+ case POWER_SUPPLY_PROP_CHARGING_ENABLED:
+ rc = vote(chip->dc_suspend_votable, POWER_SUPPLY_EN_VOTER,
+ !val->intval, 0);
+ break;
+ case POWER_SUPPLY_PROP_CURRENT_MAX:
+ rc = vote(chip->dc_icl_votable, USER_ICL_VOTER, true,
+ val->intval / 1000);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return rc;
+}
+
+static int smbchg_dc_get_property(struct power_supply *psy,
+ enum power_supply_property prop,
+ union power_supply_propval *val)
+{
+ struct smbchg_chip *chip = power_supply_get_drvdata(psy);
+
+ switch (prop) {
+ case POWER_SUPPLY_PROP_PRESENT:
+ val->intval = is_dc_present(chip);
+ break;
+ case POWER_SUPPLY_PROP_CHARGING_ENABLED:
+ val->intval = get_effective_result(chip->dc_suspend_votable);
+ if (val->intval < 0) /* no votes */
+ val->intval = 1;
+ else
+ val->intval = !val->intval;
+ break;
+ case POWER_SUPPLY_PROP_ONLINE:
+ /* return if dc is charging the battery */
+ val->intval = (smbchg_get_pwr_path(chip) == PWR_PATH_DC)
+ && (get_prop_batt_status(chip)
+ == POWER_SUPPLY_STATUS_CHARGING);
+ break;
+ case POWER_SUPPLY_PROP_CURRENT_MAX:
+ val->intval = chip->dc_max_current_ma * 1000;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int smbchg_dc_is_writeable(struct power_supply *psy,
+ enum power_supply_property prop)
+{
+ int rc;
+
+ switch (prop) {
+ case POWER_SUPPLY_PROP_CHARGING_ENABLED:
+ case POWER_SUPPLY_PROP_CURRENT_MAX:
+ rc = 1;
+ break;
+ default:
+ rc = 0;
+ break;
+ }
+ return rc;
+}
+
+#define HOT_BAT_HARD_BIT BIT(0)
+#define HOT_BAT_SOFT_BIT BIT(1)
+#define COLD_BAT_HARD_BIT BIT(2)
+#define COLD_BAT_SOFT_BIT BIT(3)
+#define BAT_OV_BIT BIT(4)
+#define BAT_LOW_BIT BIT(5)
+#define BAT_MISSING_BIT BIT(6)
+#define BAT_TERM_MISSING_BIT BIT(7)
+static irqreturn_t batt_hot_handler(int irq, void *_chip)
+{
+ struct smbchg_chip *chip = _chip;
+ u8 reg = 0;
+
+ smbchg_read(chip, ®, chip->bat_if_base + RT_STS, 1);
+ chip->batt_hot = !!(reg & HOT_BAT_HARD_BIT);
+ pr_smb(PR_INTERRUPT, "triggered: 0x%02x\n", reg);
+ smbchg_parallel_usb_check_ok(chip);
+ if (chip->batt_psy)
+ power_supply_changed(chip->batt_psy);
+ smbchg_charging_status_change(chip);
+ smbchg_wipower_check(chip);
+ set_property_on_fg(chip, POWER_SUPPLY_PROP_HEALTH,
+ get_prop_batt_health(chip));
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t batt_cold_handler(int irq, void *_chip)
+{
+ struct smbchg_chip *chip = _chip;
+ u8 reg = 0;
+
+ smbchg_read(chip, ®, chip->bat_if_base + RT_STS, 1);
+ chip->batt_cold = !!(reg & COLD_BAT_HARD_BIT);
+ pr_smb(PR_INTERRUPT, "triggered: 0x%02x\n", reg);
+ smbchg_parallel_usb_check_ok(chip);
+ if (chip->batt_psy)
+ power_supply_changed(chip->batt_psy);
+ smbchg_charging_status_change(chip);
+ smbchg_wipower_check(chip);
+ set_property_on_fg(chip, POWER_SUPPLY_PROP_HEALTH,
+ get_prop_batt_health(chip));
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t batt_warm_handler(int irq, void *_chip)
+{
+ struct smbchg_chip *chip = _chip;
+ u8 reg = 0;
+
+ smbchg_read(chip, ®, chip->bat_if_base + RT_STS, 1);
+ chip->batt_warm = !!(reg & HOT_BAT_SOFT_BIT);
+ pr_smb(PR_INTERRUPT, "triggered: 0x%02x\n", reg);
+ smbchg_parallel_usb_check_ok(chip);
+ if (chip->batt_psy)
+ power_supply_changed(chip->batt_psy);
+ set_property_on_fg(chip, POWER_SUPPLY_PROP_HEALTH,
+ get_prop_batt_health(chip));
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t batt_cool_handler(int irq, void *_chip)
+{
+ struct smbchg_chip *chip = _chip;
+ u8 reg = 0;
+
+ smbchg_read(chip, ®, chip->bat_if_base + RT_STS, 1);
+ chip->batt_cool = !!(reg & COLD_BAT_SOFT_BIT);
+ pr_smb(PR_INTERRUPT, "triggered: 0x%02x\n", reg);
+ smbchg_parallel_usb_check_ok(chip);
+ if (chip->batt_psy)
+ power_supply_changed(chip->batt_psy);
+ set_property_on_fg(chip, POWER_SUPPLY_PROP_HEALTH,
+ get_prop_batt_health(chip));
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t batt_pres_handler(int irq, void *_chip)
+{
+ struct smbchg_chip *chip = _chip;
+ u8 reg = 0;
+
+ smbchg_read(chip, ®, chip->bat_if_base + RT_STS, 1);
+ chip->batt_present = !(reg & BAT_MISSING_BIT);
+ pr_smb(PR_INTERRUPT, "triggered: 0x%02x\n", reg);
+ if (chip->batt_psy)
+ power_supply_changed(chip->batt_psy);
+ smbchg_charging_status_change(chip);
+ set_property_on_fg(chip, POWER_SUPPLY_PROP_HEALTH,
+ get_prop_batt_health(chip));
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t vbat_low_handler(int irq, void *_chip)
+{
+ pr_warn_ratelimited("vbat low\n");
+ return IRQ_HANDLED;
+}
+
+#define CHG_COMP_SFT_BIT BIT(3)
+static irqreturn_t chg_error_handler(int irq, void *_chip)
+{
+ struct smbchg_chip *chip = _chip;
+ int rc = 0;
+ u8 reg;
+
+ pr_smb(PR_INTERRUPT, "chg-error triggered\n");
+
+ rc = smbchg_read(chip, ®, chip->chgr_base + RT_STS, 1);
+ if (rc < 0) {
+ dev_err(chip->dev, "Unable to read RT_STS rc = %d\n", rc);
+ } else {
+ pr_smb(PR_INTERRUPT, "triggered: 0x%02x\n", reg);
+ if (reg & CHG_COMP_SFT_BIT)
+ set_property_on_fg(chip,
+ POWER_SUPPLY_PROP_SAFETY_TIMER_EXPIRED,
+ 1);
+ }
+
+ smbchg_parallel_usb_check_ok(chip);
+ if (chip->batt_psy)
+ power_supply_changed(chip->batt_psy);
+ smbchg_charging_status_change(chip);
+ smbchg_wipower_check(chip);
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t fastchg_handler(int irq, void *_chip)
+{
+ struct smbchg_chip *chip = _chip;
+
+ pr_smb(PR_INTERRUPT, "p2f triggered\n");
+ smbchg_parallel_usb_check_ok(chip);
+ if (chip->batt_psy)
+ power_supply_changed(chip->batt_psy);
+ smbchg_charging_status_change(chip);
+ smbchg_wipower_check(chip);
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t chg_hot_handler(int irq, void *_chip)
+{
+ pr_warn_ratelimited("chg hot\n");
+ smbchg_wipower_check(_chip);
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t chg_term_handler(int irq, void *_chip)
+{
+ struct smbchg_chip *chip = _chip;
+
+ pr_smb(PR_INTERRUPT, "tcc triggered\n");
+ /*
+ * Charge termination is a pulse and not level triggered. That means,
+ * TCC bit in RT_STS can get cleared by the time this interrupt is
+ * handled. Instead of relying on that to determine whether the
+ * charge termination had happened, we've to simply notify the FG
+ * about this as long as the interrupt is handled.
+ */
+ set_property_on_fg(chip, POWER_SUPPLY_PROP_CHARGE_DONE, 1);
+
+ smbchg_parallel_usb_check_ok(chip);
+ if (chip->batt_psy)
+ power_supply_changed(chip->batt_psy);
+ smbchg_charging_status_change(chip);
+
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t taper_handler(int irq, void *_chip)
+{
+ struct smbchg_chip *chip = _chip;
+ u8 reg = 0;
+
+ taper_irq_en(chip, false);
+ smbchg_read(chip, ®, chip->chgr_base + RT_STS, 1);
+ pr_smb(PR_INTERRUPT, "triggered: 0x%02x\n", reg);
+ smbchg_parallel_usb_taper(chip);
+ if (chip->batt_psy)
+ power_supply_changed(chip->batt_psy);
+ smbchg_charging_status_change(chip);
+ smbchg_wipower_check(chip);
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t recharge_handler(int irq, void *_chip)
+{
+ struct smbchg_chip *chip = _chip;
+ u8 reg = 0;
+
+ smbchg_read(chip, ®, chip->chgr_base + RT_STS, 1);
+ pr_smb(PR_INTERRUPT, "triggered: 0x%02x\n", reg);
+ smbchg_parallel_usb_check_ok(chip);
+ if (chip->batt_psy)
+ power_supply_changed(chip->batt_psy);
+ smbchg_charging_status_change(chip);
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t wdog_timeout_handler(int irq, void *_chip)
+{
+ struct smbchg_chip *chip = _chip;
+ u8 reg = 0;
+
+ smbchg_read(chip, ®, chip->misc_base + RT_STS, 1);
+ pr_warn_ratelimited("wdog timeout rt_stat = 0x%02x\n", reg);
+ if (chip->batt_psy)
+ power_supply_changed(chip->batt_psy);
+ smbchg_charging_status_change(chip);
+ return IRQ_HANDLED;
+}
+
+/**
+ * power_ok_handler() - called when the switcher turns on or turns off
+ * @chip: pointer to smbchg_chip
+ * @rt_stat: the status bit indicating switcher turning on or off
+ */
+static irqreturn_t power_ok_handler(int irq, void *_chip)
+{
+ struct smbchg_chip *chip = _chip;
+ u8 reg = 0;
+
+ smbchg_read(chip, ®, chip->misc_base + RT_STS, 1);
+ pr_smb(PR_INTERRUPT, "triggered: 0x%02x\n", reg);
+ return IRQ_HANDLED;
+}
+
+/**
+ * dcin_uv_handler() - called when the dc voltage crosses the uv threshold
+ * @chip: pointer to smbchg_chip
+ * @rt_stat: the status bit indicating whether dc voltage is uv
+ */
+#define DCIN_UNSUSPEND_DELAY_MS 1000
+static irqreturn_t dcin_uv_handler(int irq, void *_chip)
+{
+ struct smbchg_chip *chip = _chip;
+ bool dc_present = is_dc_present(chip);
+
+ pr_smb(PR_STATUS, "chip->dc_present = %d dc_present = %d\n",
+ chip->dc_present, dc_present);
+
+ if (chip->dc_present != dc_present) {
+ /* dc changed */
+ chip->dc_present = dc_present;
+ if (chip->dc_psy_type != -EINVAL && chip->batt_psy)
+ power_supply_changed(chip->dc_psy);
+ smbchg_charging_status_change(chip);
+ smbchg_aicl_deglitch_wa_check(chip);
+ chip->vbat_above_headroom = false;
+ }
+
+ smbchg_wipower_check(chip);
+ return IRQ_HANDLED;
+}
+
+/**
+ * usbin_ov_handler() - this is called when an overvoltage condition occurs
+ * @chip: pointer to smbchg_chip chip
+ */
+static irqreturn_t usbin_ov_handler(int irq, void *_chip)
+{
+ struct smbchg_chip *chip = _chip;
+ int rc;
+ u8 reg;
+ bool usb_present;
+
+ rc = smbchg_read(chip, ®, chip->usb_chgpth_base + RT_STS, 1);
+ if (rc < 0) {
+ dev_err(chip->dev, "Couldn't read usb rt status rc = %d\n", rc);
+ goto out;
+ }
+
+ /* OV condition is detected. Notify it to USB psy */
+ if (reg & USBIN_OV_BIT) {
+ chip->usb_ov_det = true;
+ pr_smb(PR_MISC, "setting usb psy health OV\n");
+ chip->usb_health = POWER_SUPPLY_HEALTH_OVERVOLTAGE;
+ power_supply_changed(chip->usb_psy);
+ } else {
+ chip->usb_ov_det = false;
+ /* If USB is present, then handle the USB insertion */
+ usb_present = is_usb_present(chip);
+ if (usb_present)
+ update_usb_status(chip, usb_present, false);
+ }
+out:
+ return IRQ_HANDLED;
+}
+
+/**
+ * usbin_uv_handler() - this is called when USB charger is removed
+ * @chip: pointer to smbchg_chip chip
+ * @rt_stat: the status bit indicating chg insertion/removal
+ */
+#define ICL_MODE_MASK SMB_MASK(5, 4)
+#define ICL_MODE_HIGH_CURRENT 0
+static irqreturn_t usbin_uv_handler(int irq, void *_chip)
+{
+ struct smbchg_chip *chip = _chip;
+ int aicl_level = smbchg_get_aicl_level_ma(chip);
+ int rc;
+ u8 reg;
+
+ rc = smbchg_read(chip, ®, chip->usb_chgpth_base + RT_STS, 1);
+ if (rc) {
+ pr_err("could not read rt sts: %d", rc);
+ goto out;
+ }
+
+ pr_smb(PR_STATUS,
+ "%s chip->usb_present = %d rt_sts = 0x%02x hvdcp_3_det_ignore_uv = %d aicl = %d\n",
+ chip->hvdcp_3_det_ignore_uv ? "Ignoring":"",
+ chip->usb_present, reg, chip->hvdcp_3_det_ignore_uv,
+ aicl_level);
+
+ /*
+ * set usb_psy's dp=f dm=f if this is a new insertion, i.e. it is
+ * not already src_detected and usbin_uv is seen falling
+ */
+ if (!(reg & USBIN_UV_BIT) && !(reg & USBIN_SRC_DET_BIT)) {
+ pr_smb(PR_MISC, "setting usb dp=f dm=f\n");
+ if (chip->dpdm_reg && !regulator_is_enabled(chip->dpdm_reg))
+ rc = regulator_enable(chip->dpdm_reg);
+ if (rc < 0) {
+ pr_err("Couldn't enable DP/DM for pulsing rc=%d\n", rc);
+ return rc;
+ }
+ }
+
+ if (reg & USBIN_UV_BIT)
+ complete_all(&chip->usbin_uv_raised);
+ else
+ complete_all(&chip->usbin_uv_lowered);
+
+ if (chip->hvdcp_3_det_ignore_uv)
+ goto out;
+
+ if ((reg & USBIN_UV_BIT) && (reg & USBIN_SRC_DET_BIT)) {
+ pr_smb(PR_STATUS, "Very weak charger detected\n");
+ chip->very_weak_charger = true;
+ rc = smbchg_read(chip, ®,
+ chip->usb_chgpth_base + ICL_STS_2_REG, 1);
+ if (rc) {
+ dev_err(chip->dev, "Could not read usb icl sts 2: %d\n",
+ rc);
+ goto out;
+ }
+ if ((reg & ICL_MODE_MASK) != ICL_MODE_HIGH_CURRENT) {
+ /*
+ * If AICL is not even enabled, this is either an
+ * SDP or a grossly out of spec charger. Do not
+ * draw any current from it.
+ */
+ rc = vote(chip->usb_suspend_votable,
+ WEAK_CHARGER_EN_VOTER, true, 0);
+ if (rc < 0)
+ pr_err("could not disable charger: %d", rc);
+ } else if (aicl_level == chip->tables.usb_ilim_ma_table[0]) {
+ /*
+ * we are in a situation where the adapter is not able
+ * to supply even 300mA. Disable hw aicl reruns else it
+ * is only a matter of time when we get back here again
+ */
+ rc = vote(chip->hw_aicl_rerun_disable_votable,
+ WEAK_CHARGER_HW_AICL_VOTER, true, 0);
+ if (rc < 0)
+ pr_err("Couldn't disable hw aicl rerun rc=%d\n",
+ rc);
+ }
+ pr_smb(PR_MISC, "setting usb psy health UNSPEC_FAILURE\n");
+ chip->usb_health = POWER_SUPPLY_HEALTH_UNSPEC_FAILURE;
+ power_supply_changed(chip->usb_psy);
+ schedule_work(&chip->usb_set_online_work);
+ }
+
+ smbchg_wipower_check(chip);
+out:
+ return IRQ_HANDLED;
+}
+
+/**
+ * src_detect_handler() - this is called on rising edge when USB charger type
+ * is detected and on falling edge when USB voltage falls
+ * below the coarse detect voltage(1V), use it for
+ * handling USB charger insertion and removal.
+ * @chip: pointer to smbchg_chip
+ * @rt_stat: the status bit indicating chg insertion/removal
+ */
+static irqreturn_t src_detect_handler(int irq, void *_chip)
+{
+ struct smbchg_chip *chip = _chip;
+ bool usb_present = is_usb_present(chip);
+ bool src_detect = is_src_detect_high(chip);
+ int rc;
+
+ pr_smb(PR_STATUS,
+ "%s chip->usb_present = %d usb_present = %d src_detect = %d hvdcp_3_det_ignore_uv=%d\n",
+ chip->hvdcp_3_det_ignore_uv ? "Ignoring":"",
+ chip->usb_present, usb_present, src_detect,
+ chip->hvdcp_3_det_ignore_uv);
+
+ if (src_detect)
+ complete_all(&chip->src_det_raised);
+ else
+ complete_all(&chip->src_det_lowered);
+
+ if (chip->hvdcp_3_det_ignore_uv)
+ goto out;
+
+ /*
+ * When VBAT is above the AICL threshold (4.25V) - 180mV (4.07V),
+ * an input collapse due to AICL will actually cause an USBIN_UV
+ * interrupt to fire as well.
+ *
+ * Handle USB insertions and removals in the source detect handler
+ * instead of the USBIN_UV handler since the latter is untrustworthy
+ * when the battery voltage is high.
+ */
+ chip->very_weak_charger = false;
+ /*
+ * a src detect marks a new insertion or a real removal,
+ * vote for enable aicl hw reruns
+ */
+ rc = vote(chip->hw_aicl_rerun_disable_votable,
+ WEAK_CHARGER_HW_AICL_VOTER, false, 0);
+ if (rc < 0)
+ pr_err("Couldn't enable hw aicl rerun rc=%d\n", rc);
+
+ rc = vote(chip->usb_suspend_votable, WEAK_CHARGER_EN_VOTER, false, 0);
+ if (rc < 0)
+ pr_err("could not enable charger: %d\n", rc);
+
+ if (src_detect) {
+ update_usb_status(chip, usb_present, 0);
+ } else {
+ update_usb_status(chip, 0, false);
+ chip->aicl_irq_count = 0;
+ }
+out:
+ return IRQ_HANDLED;
+}
+
+/**
+ * otg_oc_handler() - called when the usb otg goes over current
+ */
+#define NUM_OTG_RETRIES 5
+#define OTG_OC_RETRY_DELAY_US 50000
+static irqreturn_t otg_oc_handler(int irq, void *_chip)
+{
+ int rc;
+ struct smbchg_chip *chip = _chip;
+ s64 elapsed_us = ktime_us_delta(ktime_get(), chip->otg_enable_time);
+
+ pr_smb(PR_INTERRUPT, "triggered\n");
+
+ if (chip->schg_version == QPNP_SCHG_LITE) {
+ pr_warn("OTG OC triggered - OTG disabled\n");
+ return IRQ_HANDLED;
+ }
+
+ if (elapsed_us > OTG_OC_RETRY_DELAY_US)
+ chip->otg_retries = 0;
+
+ /*
+ * Due to a HW bug in the PMI8994 charger, the current inrush that
+ * occurs when connecting certain OTG devices can cause the OTG
+ * overcurrent protection to trip.
+ *
+ * The work around is to try reenabling the OTG when getting an
+ * overcurrent interrupt once.
+ */
+ if (chip->otg_retries < NUM_OTG_RETRIES) {
+ chip->otg_retries += 1;
+ pr_smb(PR_STATUS,
+ "Retrying OTG enable. Try #%d, elapsed_us %lld\n",
+ chip->otg_retries, elapsed_us);
+ rc = otg_oc_reset(chip);
+ if (rc)
+ pr_err("Failed to reset OTG OC state rc=%d\n", rc);
+ chip->otg_enable_time = ktime_get();
+ }
+ return IRQ_HANDLED;
+}
+
+/**
+ * otg_fail_handler() - called when the usb otg fails
+ * (when vbat < OTG UVLO threshold)
+ */
+static irqreturn_t otg_fail_handler(int irq, void *_chip)
+{
+ pr_smb(PR_INTERRUPT, "triggered\n");
+ return IRQ_HANDLED;
+}
+
+/**
+ * aicl_done_handler() - called when the usb AICL algorithm is finished
+ * and a current is set.
+ */
+static irqreturn_t aicl_done_handler(int irq, void *_chip)
+{
+ struct smbchg_chip *chip = _chip;
+ bool usb_present = is_usb_present(chip);
+ int aicl_level = smbchg_get_aicl_level_ma(chip);
+
+ pr_smb(PR_INTERRUPT, "triggered, aicl: %d\n", aicl_level);
+
+ increment_aicl_count(chip);
+
+ if (usb_present)
+ smbchg_parallel_usb_check_ok(chip);
+
+ if (chip->aicl_complete && chip->batt_psy)
+ power_supply_changed(chip->batt_psy);
+
+ return IRQ_HANDLED;
+}
+
+/**
+ * usbid_change_handler() - called when the usb RID changes.
+ * This is used mostly for detecting OTG
+ */
+static irqreturn_t usbid_change_handler(int irq, void *_chip)
+{
+ struct smbchg_chip *chip = _chip;
+ bool otg_present;
+
+ pr_smb(PR_INTERRUPT, "triggered\n");
+
+ otg_present = is_otg_present(chip);
+ pr_smb(PR_MISC, "setting usb psy OTG = %d\n",
+ otg_present ? 1 : 0);
+
+ extcon_set_cable_state_(chip->extcon, EXTCON_USB_HOST, otg_present);
+
+ if (otg_present)
+ pr_smb(PR_STATUS, "OTG detected\n");
+
+ /* update FG */
+ set_property_on_fg(chip, POWER_SUPPLY_PROP_STATUS,
+ get_prop_batt_status(chip));
+
+ return IRQ_HANDLED;
+}
+
+static int determine_initial_status(struct smbchg_chip *chip)
+{
+ union power_supply_propval type = {0, };
+
+ /*
+ * It is okay to read the interrupt status here since
+ * interrupts aren't requested. reading interrupt status
+ * clears the interrupt so be careful to read interrupt
+ * status only in interrupt handling code
+ */
+
+ batt_pres_handler(0, chip);
+ batt_hot_handler(0, chip);
+ batt_warm_handler(0, chip);
+ batt_cool_handler(0, chip);
+ batt_cold_handler(0, chip);
+ if (chip->typec_psy) {
+ get_property_from_typec(chip, POWER_SUPPLY_PROP_TYPE, &type);
+ update_typec_otg_status(chip, type.intval, true);
+ } 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);
+
+ if (chip->usb_present) {
+ int rc = 0;
+
+ pr_smb(PR_MISC, "setting usb dp=f dm=f\n");
+ if (chip->dpdm_reg && !regulator_is_enabled(chip->dpdm_reg))
+ rc = regulator_enable(chip->dpdm_reg);
+ if (rc < 0) {
+ pr_err("Couldn't enable DP/DM for pulsing rc=%d\n", rc);
+ return rc;
+ }
+ handle_usb_insertion(chip);
+ } else {
+ handle_usb_removal(chip);
+ }
+
+ return 0;
+}
+
+static int prechg_time[] = {
+ 24,
+ 48,
+ 96,
+ 192,
+};
+static int chg_time[] = {
+ 192,
+ 384,
+ 768,
+ 1536,
+};
+
+enum bpd_type {
+ BPD_TYPE_BAT_NONE,
+ BPD_TYPE_BAT_ID,
+ BPD_TYPE_BAT_THM,
+ BPD_TYPE_BAT_THM_BAT_ID,
+ BPD_TYPE_DEFAULT,
+};
+
+static const char * const bpd_label[] = {
+ [BPD_TYPE_BAT_NONE] = "bpd_none",
+ [BPD_TYPE_BAT_ID] = "bpd_id",
+ [BPD_TYPE_BAT_THM] = "bpd_thm",
+ [BPD_TYPE_BAT_THM_BAT_ID] = "bpd_thm_id",
+};
+
+static inline int get_bpd(const char *name)
+{
+ int i = 0;
+
+ for (i = 0; i < ARRAY_SIZE(bpd_label); i++) {
+ if (strcmp(bpd_label[i], name) == 0)
+ return i;
+ }
+ return -EINVAL;
+}
+
+#define REVISION1_REG 0x0
+#define DIG_MINOR 0
+#define DIG_MAJOR 1
+#define ANA_MINOR 2
+#define ANA_MAJOR 3
+#define CHGR_CFG1 0xFB
+#define RECHG_THRESHOLD_SRC_BIT BIT(1)
+#define TERM_I_SRC_BIT BIT(2)
+#define TERM_SRC_FG BIT(2)
+#define CHG_INHIB_CFG_REG 0xF7
+#define CHG_INHIBIT_50MV_VAL 0x00
+#define CHG_INHIBIT_100MV_VAL 0x01
+#define CHG_INHIBIT_200MV_VAL 0x02
+#define CHG_INHIBIT_300MV_VAL 0x03
+#define CHG_INHIBIT_MASK 0x03
+#define USE_REGISTER_FOR_CURRENT BIT(2)
+#define CHGR_CFG2 0xFC
+#define CHG_EN_SRC_BIT BIT(7)
+#define CHG_EN_POLARITY_BIT BIT(6)
+#define P2F_CHG_TRAN BIT(5)
+#define CHG_BAT_OV_ECC BIT(4)
+#define I_TERM_BIT BIT(3)
+#define AUTO_RECHG_BIT BIT(2)
+#define CHARGER_INHIBIT_BIT BIT(0)
+#define USB51_COMMAND_POL BIT(2)
+#define USB51AC_CTRL BIT(1)
+#define TR_8OR32B 0xFE
+#define BUCK_8_16_FREQ_BIT BIT(0)
+#define BM_CFG 0xF3
+#define BATT_MISSING_ALGO_BIT BIT(2)
+#define BMD_PIN_SRC_MASK SMB_MASK(1, 0)
+#define PIN_SRC_SHIFT 0
+#define CHGR_CFG 0xFF
+#define RCHG_LVL_BIT BIT(0)
+#define VCHG_EN_BIT BIT(1)
+#define VCHG_INPUT_CURRENT_BIT BIT(3)
+#define CFG_AFVC 0xF6
+#define VFLOAT_COMP_ENABLE_MASK SMB_MASK(2, 0)
+#define TR_RID_REG 0xFA
+#define FG_INPUT_FET_DELAY_BIT BIT(3)
+#define TRIM_OPTIONS_7_0 0xF6
+#define INPUT_MISSING_POLLER_EN_BIT BIT(3)
+#define CHGR_CCMP_CFG 0xFA
+#define JEITA_TEMP_HARD_LIMIT_BIT BIT(5)
+#define HVDCP_ADAPTER_SEL_MASK SMB_MASK(5, 4)
+#define HVDCP_ADAPTER_SEL_9V_BIT BIT(4)
+#define HVDCP_AUTH_ALG_EN_BIT BIT(6)
+#define CMD_APSD 0x41
+#define APSD_RERUN_BIT BIT(0)
+#define OTG_CFG 0xF1
+#define HICCUP_ENABLED_BIT BIT(6)
+#define OTG_PIN_POLARITY_BIT BIT(4)
+#define OTG_PIN_ACTIVE_LOW BIT(4)
+#define OTG_EN_CTRL_MASK SMB_MASK(3, 2)
+#define OTG_PIN_CTRL_RID_DIS 0x04
+#define OTG_CMD_CTRL_RID_EN 0x08
+#define AICL_ADC_BIT BIT(6)
+static void batt_ov_wa_check(struct smbchg_chip *chip)
+{
+ int rc;
+ u8 reg;
+
+ /* disable-'battery OV disables charging' feature */
+ rc = smbchg_sec_masked_write(chip, chip->chgr_base + CHGR_CFG2,
+ CHG_BAT_OV_ECC, 0);
+ if (rc < 0) {
+ dev_err(chip->dev, "Couldn't set chgr_cfg2 rc=%d\n", rc);
+ return;
+ }
+
+ /*
+ * if battery OV is set:
+ * restart charging by disable/enable charging
+ */
+ rc = smbchg_read(chip, ®, chip->bat_if_base + RT_STS, 1);
+ if (rc < 0) {
+ dev_err(chip->dev,
+ "Couldn't read Battery RT status rc = %d\n", rc);
+ return;
+ }
+
+ if (reg & BAT_OV_BIT) {
+ rc = smbchg_charging_en(chip, false);
+ if (rc < 0) {
+ dev_err(chip->dev,
+ "Couldn't disable charging: rc = %d\n", rc);
+ return;
+ }
+
+ /* delay for charging-disable to take affect */
+ msleep(200);
+
+ rc = smbchg_charging_en(chip, true);
+ if (rc < 0) {
+ dev_err(chip->dev,
+ "Couldn't enable charging: rc = %d\n", rc);
+ return;
+ }
+ }
+}
+
+static int smbchg_hw_init(struct smbchg_chip *chip)
+{
+ int rc, i;
+ u8 reg, mask;
+
+ rc = smbchg_read(chip, chip->revision,
+ chip->misc_base + REVISION1_REG, 4);
+ if (rc < 0) {
+ dev_err(chip->dev, "Couldn't read revision rc=%d\n",
+ rc);
+ return rc;
+ }
+ pr_smb(PR_STATUS, "Charger Revision DIG: %d.%d; ANA: %d.%d\n",
+ chip->revision[DIG_MAJOR], chip->revision[DIG_MINOR],
+ chip->revision[ANA_MAJOR], chip->revision[ANA_MINOR]);
+
+ /* Setup 9V HVDCP */
+ if (!chip->hvdcp_not_supported) {
+ rc = smbchg_sec_masked_write(chip,
+ chip->usb_chgpth_base + CHGPTH_CFG,
+ HVDCP_ADAPTER_SEL_MASK, HVDCP_9V);
+ if (rc < 0) {
+ pr_err("Couldn't set hvdcp config in chgpath_chg rc=%d\n",
+ rc);
+ return rc;
+ }
+ }
+
+ if (chip->aicl_rerun_period_s > 0) {
+ rc = smbchg_set_aicl_rerun_period_s(chip,
+ chip->aicl_rerun_period_s);
+ if (rc < 0) {
+ dev_err(chip->dev, "Couldn't set AICL rerun timer rc=%d\n",
+ rc);
+ return rc;
+ }
+ }
+
+ rc = smbchg_sec_masked_write(chip, chip->usb_chgpth_base + TR_RID_REG,
+ FG_INPUT_FET_DELAY_BIT, FG_INPUT_FET_DELAY_BIT);
+ if (rc < 0) {
+ dev_err(chip->dev, "Couldn't disable fg input fet delay rc=%d\n",
+ rc);
+ return rc;
+ }
+
+ rc = smbchg_sec_masked_write(chip, chip->misc_base + TRIM_OPTIONS_7_0,
+ INPUT_MISSING_POLLER_EN_BIT,
+ INPUT_MISSING_POLLER_EN_BIT);
+ if (rc < 0) {
+ dev_err(chip->dev, "Couldn't enable input missing poller rc=%d\n",
+ rc);
+ return rc;
+ }
+
+ /*
+ * Do not force using current from the register i.e. use auto
+ * power source detect (APSD) mA ratings for the initial current values.
+ *
+ * If this is set, AICL will not rerun at 9V for HVDCPs
+ */
+ rc = smbchg_masked_write(chip, chip->usb_chgpth_base + CMD_IL,
+ USE_REGISTER_FOR_CURRENT, 0);
+
+ if (rc < 0) {
+ dev_err(chip->dev, "Couldn't set input limit cmd rc=%d\n", rc);
+ return rc;
+ }
+
+ /*
+ * set chg en by cmd register, set chg en by writing bit 1,
+ * enable auto pre to fast, enable auto recharge by default.
+ * enable current termination and charge inhibition based on
+ * the device tree configuration.
+ */
+ rc = smbchg_sec_masked_write(chip, chip->chgr_base + CHGR_CFG2,
+ CHG_EN_SRC_BIT | CHG_EN_POLARITY_BIT | P2F_CHG_TRAN
+ | I_TERM_BIT | AUTO_RECHG_BIT | CHARGER_INHIBIT_BIT,
+ CHG_EN_POLARITY_BIT
+ | (chip->chg_inhibit_en ? CHARGER_INHIBIT_BIT : 0)
+ | (chip->iterm_disabled ? I_TERM_BIT : 0));
+ if (rc < 0) {
+ dev_err(chip->dev, "Couldn't set chgr_cfg2 rc=%d\n", rc);
+ return rc;
+ }
+
+ /*
+ * enable battery charging to make sure it hasn't been changed earlier
+ * by the bootloader.
+ */
+ rc = smbchg_charging_en(chip, true);
+ if (rc < 0) {
+ dev_err(chip->dev, "Couldn't enable battery charging=%d\n", rc);
+ return rc;
+ }
+
+ /*
+ * Based on the configuration, use the analog sensors or the fuelgauge
+ * adc for recharge threshold source.
+ */
+
+ if (chip->chg_inhibit_source_fg)
+ rc = smbchg_sec_masked_write(chip, chip->chgr_base + CHGR_CFG1,
+ TERM_I_SRC_BIT | RECHG_THRESHOLD_SRC_BIT,
+ TERM_SRC_FG | RECHG_THRESHOLD_SRC_BIT);
+ else
+ rc = smbchg_sec_masked_write(chip, chip->chgr_base + CHGR_CFG1,
+ TERM_I_SRC_BIT | RECHG_THRESHOLD_SRC_BIT, 0);
+
+ if (rc < 0) {
+ dev_err(chip->dev, "Couldn't set chgr_cfg2 rc=%d\n", rc);
+ return rc;
+ }
+
+ /*
+ * control USB suspend via command bits and set correct 100/500mA
+ * polarity on the usb current
+ */
+ rc = smbchg_sec_masked_write(chip, chip->usb_chgpth_base + CHGPTH_CFG,
+ USB51_COMMAND_POL | USB51AC_CTRL, 0);
+ if (rc < 0) {
+ dev_err(chip->dev, "Couldn't set usb_chgpth cfg rc=%d\n", rc);
+ return rc;
+ }
+
+ check_battery_type(chip);
+
+ /* set the float voltage */
+ if (chip->vfloat_mv != -EINVAL) {
+ rc = smbchg_float_voltage_set(chip, chip->vfloat_mv);
+ if (rc < 0) {
+ dev_err(chip->dev,
+ "Couldn't set float voltage rc = %d\n", rc);
+ return rc;
+ }
+ pr_smb(PR_STATUS, "set vfloat to %d\n", chip->vfloat_mv);
+ }
+
+ /* set the fast charge current compensation */
+ if (chip->fastchg_current_comp != -EINVAL) {
+ rc = smbchg_fastchg_current_comp_set(chip,
+ chip->fastchg_current_comp);
+ if (rc < 0) {
+ dev_err(chip->dev, "Couldn't set fastchg current comp rc = %d\n",
+ rc);
+ return rc;
+ }
+ pr_smb(PR_STATUS, "set fastchg current comp to %d\n",
+ chip->fastchg_current_comp);
+ }
+
+ /* set the float voltage compensation */
+ if (chip->float_voltage_comp != -EINVAL) {
+ rc = smbchg_float_voltage_comp_set(chip,
+ chip->float_voltage_comp);
+ if (rc < 0) {
+ dev_err(chip->dev, "Couldn't set float voltage comp rc = %d\n",
+ rc);
+ return rc;
+ }
+ pr_smb(PR_STATUS, "set float voltage comp to %d\n",
+ chip->float_voltage_comp);
+ }
+
+ /* set iterm */
+ if (chip->iterm_ma != -EINVAL) {
+ if (chip->iterm_disabled) {
+ dev_err(chip->dev, "Error: Both iterm_disabled and iterm_ma set\n");
+ return -EINVAL;
+ }
+
+ smbchg_iterm_set(chip, chip->iterm_ma);
+ }
+
+ /* set the safety time voltage */
+ if (chip->safety_time != -EINVAL) {
+ reg = (chip->safety_time > 0 ? 0 : SFT_TIMER_DISABLE_BIT) |
+ (chip->prechg_safety_time > 0
+ ? 0 : PRECHG_SFT_TIMER_DISABLE_BIT);
+
+ for (i = 0; i < ARRAY_SIZE(chg_time); i++) {
+ if (chip->safety_time <= chg_time[i]) {
+ reg |= i << SAFETY_TIME_MINUTES_SHIFT;
+ break;
+ }
+ }
+ for (i = 0; i < ARRAY_SIZE(prechg_time); i++) {
+ if (chip->prechg_safety_time <= prechg_time[i]) {
+ reg |= i;
+ break;
+ }
+ }
+
+ rc = smbchg_sec_masked_write(chip,
+ chip->chgr_base + SFT_CFG,
+ SFT_EN_MASK | SFT_TO_MASK |
+ (chip->prechg_safety_time > 0
+ ? PRECHG_SFT_TO_MASK : 0), reg);
+ if (rc < 0) {
+ dev_err(chip->dev,
+ "Couldn't set safety timer rc = %d\n",
+ rc);
+ return rc;
+ }
+ chip->safety_timer_en = true;
+ } else {
+ rc = smbchg_read(chip, ®, chip->chgr_base + SFT_CFG, 1);
+ if (rc < 0)
+ dev_err(chip->dev, "Unable to read SFT_CFG rc = %d\n",
+ rc);
+ else if (!(reg & SFT_EN_MASK))
+ chip->safety_timer_en = true;
+ }
+
+ /* configure jeita temperature hard limit */
+ if (chip->jeita_temp_hard_limit >= 0) {
+ rc = smbchg_sec_masked_write(chip,
+ chip->chgr_base + CHGR_CCMP_CFG,
+ JEITA_TEMP_HARD_LIMIT_BIT,
+ chip->jeita_temp_hard_limit
+ ? 0 : JEITA_TEMP_HARD_LIMIT_BIT);
+ if (rc < 0) {
+ dev_err(chip->dev,
+ "Couldn't set jeita temp hard limit rc = %d\n",
+ rc);
+ return rc;
+ }
+ }
+
+ /* make the buck switch faster to prevent some vbus oscillation */
+ rc = smbchg_sec_masked_write(chip,
+ chip->usb_chgpth_base + TR_8OR32B,
+ BUCK_8_16_FREQ_BIT, 0);
+ if (rc < 0) {
+ dev_err(chip->dev, "Couldn't set buck frequency rc = %d\n", rc);
+ return rc;
+ }
+
+ /* battery missing detection */
+ mask = BATT_MISSING_ALGO_BIT;
+ reg = chip->bmd_algo_disabled ? 0 : BATT_MISSING_ALGO_BIT;
+ if (chip->bmd_pin_src < BPD_TYPE_DEFAULT) {
+ mask |= BMD_PIN_SRC_MASK;
+ reg |= chip->bmd_pin_src << PIN_SRC_SHIFT;
+ }
+ rc = smbchg_sec_masked_write(chip,
+ chip->bat_if_base + BM_CFG, mask, reg);
+ if (rc < 0) {
+ dev_err(chip->dev, "Couldn't set batt_missing config = %d\n",
+ rc);
+ return rc;
+ }
+
+ if (chip->vchg_adc_channel != -EINVAL) {
+ /* configure and enable VCHG */
+ rc = smbchg_sec_masked_write(chip, chip->chgr_base + CHGR_CFG,
+ VCHG_INPUT_CURRENT_BIT | VCHG_EN_BIT,
+ VCHG_INPUT_CURRENT_BIT | VCHG_EN_BIT);
+ if (rc < 0) {
+ dev_err(chip->dev, "Couldn't set recharge rc = %d\n",
+ rc);
+ return rc;
+ }
+ }
+
+ smbchg_charging_status_change(chip);
+
+ vote(chip->usb_suspend_votable, USER_EN_VOTER, !chip->chg_enabled, 0);
+ vote(chip->dc_suspend_votable, USER_EN_VOTER, !chip->chg_enabled, 0);
+ /* resume threshold */
+ if (chip->resume_delta_mv != -EINVAL) {
+
+ /*
+ * Configure only if the recharge threshold source is not
+ * fuel gauge ADC.
+ */
+ if (!chip->chg_inhibit_source_fg) {
+ if (chip->resume_delta_mv < 100)
+ reg = CHG_INHIBIT_50MV_VAL;
+ else if (chip->resume_delta_mv < 200)
+ reg = CHG_INHIBIT_100MV_VAL;
+ else if (chip->resume_delta_mv < 300)
+ reg = CHG_INHIBIT_200MV_VAL;
+ else
+ reg = CHG_INHIBIT_300MV_VAL;
+
+ rc = smbchg_sec_masked_write(chip,
+ chip->chgr_base + CHG_INHIB_CFG_REG,
+ CHG_INHIBIT_MASK, reg);
+ if (rc < 0) {
+ dev_err(chip->dev, "Couldn't set inhibit val rc = %d\n",
+ rc);
+ return rc;
+ }
+ }
+
+ rc = smbchg_sec_masked_write(chip,
+ chip->chgr_base + CHGR_CFG,
+ RCHG_LVL_BIT,
+ (chip->resume_delta_mv
+ < chip->tables.rchg_thr_mv)
+ ? 0 : RCHG_LVL_BIT);
+ if (rc < 0) {
+ dev_err(chip->dev, "Couldn't set recharge rc = %d\n",
+ rc);
+ return rc;
+ }
+ }
+
+ /* DC path current settings */
+ if (chip->dc_psy_type != -EINVAL) {
+ rc = vote(chip->dc_icl_votable, PSY_ICL_VOTER, true,
+ chip->dc_target_current_ma);
+ if (rc < 0) {
+ dev_err(chip->dev,
+ "Couldn't vote for initial DC ICL rc=%d\n", rc);
+ return rc;
+ }
+ }
+
+
+ /*
+ * on some devices the battery is powered via external sources which
+ * could raise its voltage above the float voltage. smbchargers go
+ * in to reverse boost in such a situation and the workaround is to
+ * disable float voltage compensation (note that the battery will appear
+ * hot/cold when powered via external source).
+ */
+ if (chip->soft_vfloat_comp_disabled) {
+ rc = smbchg_sec_masked_write(chip, chip->chgr_base + CFG_AFVC,
+ VFLOAT_COMP_ENABLE_MASK, 0);
+ if (rc < 0) {
+ dev_err(chip->dev, "Couldn't disable soft vfloat rc = %d\n",
+ rc);
+ return rc;
+ }
+ }
+
+ rc = vote(chip->fcc_votable, BATT_TYPE_FCC_VOTER, true,
+ chip->cfg_fastchg_current_ma);
+ if (rc < 0) {
+ dev_err(chip->dev, "Couldn't vote fastchg ma rc = %d\n", rc);
+ return rc;
+ }
+
+ rc = smbchg_read(chip, &chip->original_usbin_allowance,
+ chip->usb_chgpth_base + USBIN_CHGR_CFG, 1);
+ if (rc < 0)
+ dev_err(chip->dev, "Couldn't read usb allowance rc=%d\n", rc);
+
+ if (chip->wipower_dyn_icl_avail) {
+ rc = smbchg_wipower_ilim_config(chip,
+ &(chip->wipower_default.entries[0]));
+ if (rc < 0) {
+ dev_err(chip->dev, "Couldn't set default wipower ilim = %d\n",
+ rc);
+ return rc;
+ }
+ }
+ /* unsuspend dc path, it could be suspended by the bootloader */
+ rc = smbchg_dc_suspend(chip, 0);
+ if (rc < 0) {
+ dev_err(chip->dev, "Couldn't unsuspend dc path= %d\n", rc);
+ return rc;
+ }
+
+ if (chip->force_aicl_rerun) {
+ /* vote to enable hw aicl */
+ rc = vote(chip->hw_aicl_rerun_enable_indirect_votable,
+ DEFAULT_CONFIG_HW_AICL_VOTER, true, 0);
+ if (rc < 0) {
+ pr_err("Couldn't vote enable hw aicl rerun rc=%d\n",
+ rc);
+ return rc;
+ }
+ }
+
+ if (chip->schg_version == QPNP_SCHG_LITE) {
+ /* enable OTG hiccup mode */
+ rc = smbchg_sec_masked_write(chip, chip->otg_base + OTG_CFG,
+ HICCUP_ENABLED_BIT, HICCUP_ENABLED_BIT);
+ if (rc < 0)
+ dev_err(chip->dev, "Couldn't set OTG OC config rc = %d\n",
+ rc);
+ }
+
+ if (chip->otg_pinctrl) {
+ /* configure OTG enable to pin control active low */
+ rc = smbchg_sec_masked_write(chip, chip->otg_base + OTG_CFG,
+ OTG_PIN_POLARITY_BIT | OTG_EN_CTRL_MASK,
+ OTG_PIN_ACTIVE_LOW | OTG_PIN_CTRL_RID_DIS);
+ if (rc < 0) {
+ dev_err(chip->dev, "Couldn't set OTG EN config rc = %d\n",
+ rc);
+ return rc;
+ }
+ }
+
+ if (chip->wa_flags & SMBCHG_BATT_OV_WA)
+ batt_ov_wa_check(chip);
+
+ /* turn off AICL adc for improved accuracy */
+ rc = smbchg_sec_masked_write(chip,
+ chip->misc_base + MISC_TRIM_OPT_15_8, AICL_ADC_BIT, 0);
+ if (rc)
+ pr_err("Couldn't write to MISC_TRIM_OPTIONS_15_8 rc=%d\n",
+ rc);
+
+ return rc;
+}
+
+static const struct of_device_id smbchg_match_table[] = {
+ {
+ .compatible = "qcom,qpnp-smbcharger",
+ },
+ { },
+};
+
+#define DC_MA_MIN 300
+#define DC_MA_MAX 2000
+#define OF_PROP_READ(chip, prop, dt_property, retval, optional) \
+do { \
+ if (retval) \
+ break; \
+ if (optional) \
+ prop = -EINVAL; \
+ \
+ retval = of_property_read_u32(chip->pdev->dev.of_node, \
+ "qcom," dt_property, \
+ &prop); \
+ \
+ if ((retval == -EINVAL) && optional) \
+ retval = 0; \
+ else if (retval) \
+ dev_err(chip->dev, "Error reading " #dt_property \
+ " property rc = %d\n", rc); \
+} while (0)
+
+#define ILIM_ENTRIES 3
+#define VOLTAGE_RANGE_ENTRIES 2
+#define RANGE_ENTRY (ILIM_ENTRIES + VOLTAGE_RANGE_ENTRIES)
+static int smb_parse_wipower_map_dt(struct smbchg_chip *chip,
+ struct ilim_map *map, char *property)
+{
+ struct device_node *node = chip->dev->of_node;
+ int total_elements, size;
+ struct property *prop;
+ const __be32 *data;
+ int num, i;
+
+ prop = of_find_property(node, property, &size);
+ if (!prop) {
+ dev_err(chip->dev, "%s missing\n", property);
+ return -EINVAL;
+ }
+
+ total_elements = size / sizeof(int);
+ if (total_elements % RANGE_ENTRY) {
+ dev_err(chip->dev, "%s table not in multiple of %d, total elements = %d\n",
+ property, RANGE_ENTRY, total_elements);
+ return -EINVAL;
+ }
+
+ data = prop->value;
+ num = total_elements / RANGE_ENTRY;
+ map->entries = devm_kzalloc(chip->dev,
+ num * sizeof(struct ilim_entry), GFP_KERNEL);
+ if (!map->entries)
+ return -ENOMEM;
+
+ for (i = 0; i < num; i++) {
+ map->entries[i].vmin_uv = be32_to_cpup(data++);
+ map->entries[i].vmax_uv = be32_to_cpup(data++);
+ map->entries[i].icl_pt_ma = be32_to_cpup(data++);
+ map->entries[i].icl_lv_ma = be32_to_cpup(data++);
+ map->entries[i].icl_hv_ma = be32_to_cpup(data++);
+ }
+ map->num = num;
+ return 0;
+}
+
+static int smb_parse_wipower_dt(struct smbchg_chip *chip)
+{
+ int rc = 0;
+
+ chip->wipower_dyn_icl_avail = false;
+
+ if (!chip->vadc_dev)
+ goto err;
+
+ rc = smb_parse_wipower_map_dt(chip, &chip->wipower_default,
+ "qcom,wipower-default-ilim-map");
+ if (rc) {
+ dev_err(chip->dev, "failed to parse wipower-pt-ilim-map rc = %d\n",
+ rc);
+ goto err;
+ }
+
+ rc = smb_parse_wipower_map_dt(chip, &chip->wipower_pt,
+ "qcom,wipower-pt-ilim-map");
+ if (rc) {
+ dev_err(chip->dev, "failed to parse wipower-pt-ilim-map rc = %d\n",
+ rc);
+ goto err;
+ }
+
+ rc = smb_parse_wipower_map_dt(chip, &chip->wipower_div2,
+ "qcom,wipower-div2-ilim-map");
+ if (rc) {
+ dev_err(chip->dev, "failed to parse wipower-div2-ilim-map rc = %d\n",
+ rc);
+ goto err;
+ }
+ chip->wipower_dyn_icl_avail = true;
+ return 0;
+err:
+ chip->wipower_default.num = 0;
+ chip->wipower_pt.num = 0;
+ chip->wipower_default.num = 0;
+ if (chip->wipower_default.entries)
+ devm_kfree(chip->dev, chip->wipower_default.entries);
+ if (chip->wipower_pt.entries)
+ devm_kfree(chip->dev, chip->wipower_pt.entries);
+ if (chip->wipower_div2.entries)
+ devm_kfree(chip->dev, chip->wipower_div2.entries);
+ chip->wipower_default.entries = NULL;
+ chip->wipower_pt.entries = NULL;
+ chip->wipower_div2.entries = NULL;
+ chip->vadc_dev = NULL;
+ return rc;
+}
+
+#define DEFAULT_VLED_MAX_UV 3500000
+#define DEFAULT_FCC_MA 2000
+static int smb_parse_dt(struct smbchg_chip *chip)
+{
+ int rc = 0, ocp_thresh = -EINVAL;
+ struct device_node *node = chip->dev->of_node;
+ const char *dc_psy_type, *bpd;
+
+ if (!node) {
+ dev_err(chip->dev, "device tree info. missing\n");
+ return -EINVAL;
+ }
+
+ /* read optional u32 properties */
+ OF_PROP_READ(chip, ocp_thresh,
+ "ibat-ocp-threshold-ua", rc, 1);
+ if (ocp_thresh >= 0)
+ smbchg_ibat_ocp_threshold_ua = ocp_thresh;
+ OF_PROP_READ(chip, chip->iterm_ma, "iterm-ma", rc, 1);
+ OF_PROP_READ(chip, chip->cfg_fastchg_current_ma,
+ "fastchg-current-ma", rc, 1);
+ if (chip->cfg_fastchg_current_ma == -EINVAL)
+ chip->cfg_fastchg_current_ma = DEFAULT_FCC_MA;
+ OF_PROP_READ(chip, chip->vfloat_mv, "float-voltage-mv", rc, 1);
+ OF_PROP_READ(chip, chip->safety_time, "charging-timeout-mins", rc, 1);
+ OF_PROP_READ(chip, chip->vled_max_uv, "vled-max-uv", rc, 1);
+ if (chip->vled_max_uv < 0)
+ chip->vled_max_uv = DEFAULT_VLED_MAX_UV;
+ OF_PROP_READ(chip, chip->rpara_uohm, "rparasitic-uohm", rc, 1);
+ if (chip->rpara_uohm < 0)
+ chip->rpara_uohm = 0;
+ OF_PROP_READ(chip, chip->prechg_safety_time, "precharging-timeout-mins",
+ rc, 1);
+ OF_PROP_READ(chip, chip->fastchg_current_comp, "fastchg-current-comp",
+ rc, 1);
+ OF_PROP_READ(chip, chip->float_voltage_comp, "float-voltage-comp",
+ rc, 1);
+ if (chip->safety_time != -EINVAL &&
+ (chip->safety_time > chg_time[ARRAY_SIZE(chg_time) - 1])) {
+ dev_err(chip->dev, "Bad charging-timeout-mins %d\n",
+ chip->safety_time);
+ return -EINVAL;
+ }
+ if (chip->prechg_safety_time != -EINVAL &&
+ (chip->prechg_safety_time >
+ prechg_time[ARRAY_SIZE(prechg_time) - 1])) {
+ dev_err(chip->dev, "Bad precharging-timeout-mins %d\n",
+ chip->prechg_safety_time);
+ return -EINVAL;
+ }
+ OF_PROP_READ(chip, chip->resume_delta_mv, "resume-delta-mv", rc, 1);
+ OF_PROP_READ(chip, chip->parallel.min_current_thr_ma,
+ "parallel-usb-min-current-ma", rc, 1);
+ OF_PROP_READ(chip, chip->parallel.min_9v_current_thr_ma,
+ "parallel-usb-9v-min-current-ma", rc, 1);
+ OF_PROP_READ(chip, chip->parallel.allowed_lowering_ma,
+ "parallel-allowed-lowering-ma", rc, 1);
+ if (chip->parallel.min_current_thr_ma != -EINVAL
+ && chip->parallel.min_9v_current_thr_ma != -EINVAL)
+ chip->parallel.avail = true;
+ /*
+ * use the dt values if they exist, otherwise do not touch the params
+ */
+ of_property_read_u32(node, "qcom,parallel-main-chg-fcc-percent",
+ &smbchg_main_chg_fcc_percent);
+ of_property_read_u32(node, "qcom,parallel-main-chg-icl-percent",
+ &smbchg_main_chg_icl_percent);
+ pr_smb(PR_STATUS, "parallel usb thr: %d, 9v thr: %d\n",
+ chip->parallel.min_current_thr_ma,
+ chip->parallel.min_9v_current_thr_ma);
+ OF_PROP_READ(chip, chip->jeita_temp_hard_limit,
+ "jeita-temp-hard-limit", rc, 1);
+ OF_PROP_READ(chip, chip->aicl_rerun_period_s,
+ "aicl-rerun-period-s", rc, 1);
+ OF_PROP_READ(chip, chip->vchg_adc_channel,
+ "vchg-adc-channel-id", rc, 1);
+
+ /* read boolean configuration properties */
+ chip->use_vfloat_adjustments = of_property_read_bool(node,
+ "qcom,autoadjust-vfloat");
+ chip->bmd_algo_disabled = of_property_read_bool(node,
+ "qcom,bmd-algo-disabled");
+ chip->iterm_disabled = of_property_read_bool(node,
+ "qcom,iterm-disabled");
+ chip->soft_vfloat_comp_disabled = of_property_read_bool(node,
+ "qcom,soft-vfloat-comp-disabled");
+ chip->chg_enabled = !(of_property_read_bool(node,
+ "qcom,charging-disabled"));
+ chip->charge_unknown_battery = of_property_read_bool(node,
+ "qcom,charge-unknown-battery");
+ chip->chg_inhibit_en = of_property_read_bool(node,
+ "qcom,chg-inhibit-en");
+ chip->chg_inhibit_source_fg = of_property_read_bool(node,
+ "qcom,chg-inhibit-fg");
+ chip->low_volt_dcin = of_property_read_bool(node,
+ "qcom,low-volt-dcin");
+ chip->force_aicl_rerun = of_property_read_bool(node,
+ "qcom,force-aicl-rerun");
+ chip->skip_usb_suspend_for_fake_battery = of_property_read_bool(node,
+ "qcom,skip-usb-suspend-for-fake-battery");
+
+ /* parse the battery missing detection pin source */
+ rc = of_property_read_string(chip->pdev->dev.of_node,
+ "qcom,bmd-pin-src", &bpd);
+ if (rc) {
+ /* Select BAT_THM as default BPD scheme */
+ chip->bmd_pin_src = BPD_TYPE_DEFAULT;
+ rc = 0;
+ } else {
+ chip->bmd_pin_src = get_bpd(bpd);
+ if (chip->bmd_pin_src < 0) {
+ dev_err(chip->dev,
+ "failed to determine bpd schema %d\n", rc);
+ return rc;
+ }
+ }
+
+ /* parse the dc power supply configuration */
+ rc = of_property_read_string(node, "qcom,dc-psy-type", &dc_psy_type);
+ if (rc) {
+ chip->dc_psy_type = -EINVAL;
+ rc = 0;
+ } else {
+ if (strcmp(dc_psy_type, "Mains") == 0)
+ chip->dc_psy_type = POWER_SUPPLY_TYPE_MAINS;
+ else if (strcmp(dc_psy_type, "Wireless") == 0)
+ chip->dc_psy_type = POWER_SUPPLY_TYPE_WIRELESS;
+ else if (strcmp(dc_psy_type, "Wipower") == 0)
+ chip->dc_psy_type = POWER_SUPPLY_TYPE_WIPOWER;
+ }
+ if (chip->dc_psy_type != -EINVAL) {
+ OF_PROP_READ(chip, chip->dc_target_current_ma,
+ "dc-psy-ma", rc, 0);
+ if (rc)
+ return rc;
+ if (chip->dc_target_current_ma < DC_MA_MIN
+ || chip->dc_target_current_ma > DC_MA_MAX) {
+ dev_err(chip->dev, "Bad dc mA %d\n",
+ chip->dc_target_current_ma);
+ return -EINVAL;
+ }
+ }
+
+ if (chip->dc_psy_type == POWER_SUPPLY_TYPE_WIPOWER)
+ smb_parse_wipower_dt(chip);
+
+ /* read the bms power supply name */
+ rc = of_property_read_string(node, "qcom,bms-psy-name",
+ &chip->bms_psy_name);
+ if (rc)
+ chip->bms_psy_name = NULL;
+
+ /* read the battery power supply name */
+ rc = of_property_read_string(node, "qcom,battery-psy-name",
+ &chip->battery_psy_name);
+ if (rc)
+ chip->battery_psy_name = "battery";
+
+ /* Get the charger led support property */
+ chip->cfg_chg_led_sw_ctrl =
+ of_property_read_bool(node, "qcom,chg-led-sw-controls");
+ chip->cfg_chg_led_support =
+ of_property_read_bool(node, "qcom,chg-led-support");
+
+ if (of_find_property(node, "qcom,thermal-mitigation",
+ &chip->thermal_levels)) {
+ chip->thermal_mitigation = devm_kzalloc(chip->dev,
+ chip->thermal_levels,
+ GFP_KERNEL);
+
+ if (chip->thermal_mitigation == NULL) {
+ dev_err(chip->dev, "thermal mitigation kzalloc() failed.\n");
+ return -ENOMEM;
+ }
+
+ chip->thermal_levels /= sizeof(int);
+ rc = of_property_read_u32_array(node,
+ "qcom,thermal-mitigation",
+ chip->thermal_mitigation, chip->thermal_levels);
+ if (rc) {
+ dev_err(chip->dev,
+ "Couldn't read threm limits rc = %d\n", rc);
+ return rc;
+ }
+ }
+
+ chip->skip_usb_notification
+ = of_property_read_bool(node,
+ "qcom,skip-usb-notification");
+
+ chip->otg_pinctrl = of_property_read_bool(node, "qcom,otg-pinctrl");
+
+ return 0;
+}
+
+#define SUBTYPE_REG 0x5
+#define SMBCHG_CHGR_SUBTYPE 0x1
+#define SMBCHG_OTG_SUBTYPE 0x8
+#define SMBCHG_BAT_IF_SUBTYPE 0x3
+#define SMBCHG_USB_CHGPTH_SUBTYPE 0x4
+#define SMBCHG_DC_CHGPTH_SUBTYPE 0x5
+#define SMBCHG_MISC_SUBTYPE 0x7
+#define SMBCHG_LITE_CHGR_SUBTYPE 0x51
+#define SMBCHG_LITE_OTG_SUBTYPE 0x58
+#define SMBCHG_LITE_BAT_IF_SUBTYPE 0x53
+#define SMBCHG_LITE_USB_CHGPTH_SUBTYPE 0x54
+#define SMBCHG_LITE_DC_CHGPTH_SUBTYPE 0x55
+#define SMBCHG_LITE_MISC_SUBTYPE 0x57
+static int smbchg_request_irq(struct smbchg_chip *chip,
+ struct device_node *child,
+ int irq_num, char *irq_name,
+ irqreturn_t (irq_handler)(int irq, void *_chip),
+ int flags)
+{
+ int rc;
+
+ irq_num = of_irq_get_byname(child, irq_name);
+ if (irq_num < 0) {
+ dev_err(chip->dev, "Unable to get %s irqn", irq_name);
+ rc = -ENXIO;
+ }
+ rc = devm_request_threaded_irq(chip->dev,
+ irq_num, NULL, irq_handler, flags, irq_name,
+ chip);
+ if (rc < 0) {
+ dev_err(chip->dev, "Unable to request %s irq: %dn",
+ irq_name, rc);
+ rc = -ENXIO;
+ }
+ return 0;
+}
+
+static int smbchg_request_irqs(struct smbchg_chip *chip)
+{
+ int rc = 0;
+ unsigned int base;
+ struct device_node *child;
+ u8 subtype;
+ unsigned long flags = IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING
+ | IRQF_ONESHOT;
+
+ if (of_get_available_child_count(chip->pdev->dev.of_node) == 0) {
+ pr_err("no child nodes\n");
+ return -ENXIO;
+ }
+
+ for_each_available_child_of_node(chip->pdev->dev.of_node, child) {
+ rc = of_property_read_u32(child, "reg", &base);
+ if (rc < 0) {
+ rc = 0;
+ continue;
+ }
+
+ rc = smbchg_read(chip, &subtype, base + SUBTYPE_REG, 1);
+ if (rc) {
+ dev_err(chip->dev, "Peripheral subtype read failed rc=%d\n",
+ rc);
+ return rc;
+ }
+
+ switch (subtype) {
+ case SMBCHG_CHGR_SUBTYPE:
+ case SMBCHG_LITE_CHGR_SUBTYPE:
+ rc = smbchg_request_irq(chip, child,
+ chip->chg_error_irq, "chg-error",
+ chg_error_handler, flags);
+ if (rc < 0)
+ return rc;
+ rc = smbchg_request_irq(chip, child, chip->taper_irq,
+ "chg-taper-thr", taper_handler,
+ (IRQF_TRIGGER_RISING | IRQF_ONESHOT));
+ if (rc < 0)
+ return rc;
+ disable_irq_nosync(chip->taper_irq);
+ rc = smbchg_request_irq(chip, child, chip->chg_term_irq,
+ "chg-tcc-thr", chg_term_handler,
+ (IRQF_TRIGGER_RISING | IRQF_ONESHOT));
+ if (rc < 0)
+ return rc;
+ rc = smbchg_request_irq(chip, child, chip->recharge_irq,
+ "chg-rechg-thr", recharge_handler, flags);
+ if (rc < 0)
+ return rc;
+ rc = smbchg_request_irq(chip, child, chip->fastchg_irq,
+ "chg-p2f-thr", fastchg_handler, flags);
+ if (rc < 0)
+ return rc;
+ enable_irq_wake(chip->chg_term_irq);
+ enable_irq_wake(chip->chg_error_irq);
+ enable_irq_wake(chip->fastchg_irq);
+ break;
+ case SMBCHG_BAT_IF_SUBTYPE:
+ case SMBCHG_LITE_BAT_IF_SUBTYPE:
+ rc = smbchg_request_irq(chip, child, chip->batt_hot_irq,
+ "batt-hot", batt_hot_handler, flags);
+ if (rc < 0)
+ return rc;
+ rc = smbchg_request_irq(chip, child,
+ chip->batt_warm_irq,
+ "batt-warm", batt_warm_handler, flags);
+ if (rc < 0)
+ return rc;
+ rc = smbchg_request_irq(chip, child,
+ chip->batt_cool_irq,
+ "batt-cool", batt_cool_handler, flags);
+ if (rc < 0)
+ return rc;
+ rc = smbchg_request_irq(chip, child,
+ chip->batt_cold_irq,
+ "batt-cold", batt_cold_handler, flags);
+ if (rc < 0)
+ return rc;
+ rc = smbchg_request_irq(chip, child,
+ chip->batt_missing_irq,
+ "batt-missing", batt_pres_handler, flags);
+ if (rc < 0)
+ return rc;
+ rc = smbchg_request_irq(chip, child,
+ chip->vbat_low_irq,
+ "batt-low", vbat_low_handler, flags);
+ if (rc < 0)
+ return rc;
+
+ enable_irq_wake(chip->batt_hot_irq);
+ enable_irq_wake(chip->batt_warm_irq);
+ enable_irq_wake(chip->batt_cool_irq);
+ enable_irq_wake(chip->batt_cold_irq);
+ enable_irq_wake(chip->batt_missing_irq);
+ enable_irq_wake(chip->vbat_low_irq);
+ break;
+ case SMBCHG_USB_CHGPTH_SUBTYPE:
+ case SMBCHG_LITE_USB_CHGPTH_SUBTYPE:
+ rc = smbchg_request_irq(chip, child,
+ chip->usbin_uv_irq,
+ "usbin-uv", usbin_uv_handler,
+ flags | IRQF_EARLY_RESUME);
+ if (rc < 0)
+ return rc;
+ rc = smbchg_request_irq(chip, child,
+ chip->usbin_ov_irq,
+ "usbin-ov", usbin_ov_handler, flags);
+ if (rc < 0)
+ return rc;
+ rc = smbchg_request_irq(chip, child,
+ chip->src_detect_irq,
+ "usbin-src-det",
+ src_detect_handler, flags);
+ if (rc < 0)
+ return rc;
+ rc = smbchg_request_irq(chip, child,
+ chip->aicl_done_irq,
+ "aicl-done",
+ aicl_done_handler,
+ (IRQF_TRIGGER_RISING | IRQF_ONESHOT));
+ if (rc < 0)
+ return rc;
+
+ if (chip->schg_version != QPNP_SCHG_LITE) {
+ rc = smbchg_request_irq(chip, child,
+ chip->otg_fail_irq, "otg-fail",
+ otg_fail_handler, flags);
+ if (rc < 0)
+ return rc;
+ rc = smbchg_request_irq(chip, child,
+ chip->otg_oc_irq, "otg-oc",
+ otg_oc_handler,
+ (IRQF_TRIGGER_RISING | IRQF_ONESHOT));
+ if (rc < 0)
+ return rc;
+ rc = smbchg_request_irq(chip, child,
+ chip->usbid_change_irq, "usbid-change",
+ usbid_change_handler,
+ (IRQF_TRIGGER_FALLING | IRQF_ONESHOT));
+ if (rc < 0)
+ return rc;
+ enable_irq_wake(chip->otg_oc_irq);
+ enable_irq_wake(chip->usbid_change_irq);
+ enable_irq_wake(chip->otg_fail_irq);
+ }
+ enable_irq_wake(chip->usbin_uv_irq);
+ enable_irq_wake(chip->usbin_ov_irq);
+ enable_irq_wake(chip->src_detect_irq);
+ if (chip->parallel.avail && chip->usb_present) {
+ rc = enable_irq_wake(chip->aicl_done_irq);
+ chip->enable_aicl_wake = true;
+ }
+ break;
+ case SMBCHG_DC_CHGPTH_SUBTYPE:
+ case SMBCHG_LITE_DC_CHGPTH_SUBTYPE:
+ rc = smbchg_request_irq(chip, child, chip->dcin_uv_irq,
+ "dcin-uv", dcin_uv_handler, flags);
+ if (rc < 0)
+ return rc;
+ enable_irq_wake(chip->dcin_uv_irq);
+ break;
+ case SMBCHG_MISC_SUBTYPE:
+ case SMBCHG_LITE_MISC_SUBTYPE:
+ rc = smbchg_request_irq(chip, child, chip->power_ok_irq,
+ "power-ok", power_ok_handler, flags);
+ if (rc < 0)
+ return rc;
+ rc = smbchg_request_irq(chip, child, chip->chg_hot_irq,
+ "temp-shutdown", chg_hot_handler, flags);
+ if (rc < 0)
+ return rc;
+ rc = smbchg_request_irq(chip, child,
+ chip->wdog_timeout_irq, "wdog-timeout",
+ wdog_timeout_handler, flags);
+ if (rc < 0)
+ return rc;
+ enable_irq_wake(chip->chg_hot_irq);
+ enable_irq_wake(chip->wdog_timeout_irq);
+ break;
+ case SMBCHG_OTG_SUBTYPE:
+ break;
+ case SMBCHG_LITE_OTG_SUBTYPE:
+ rc = smbchg_request_irq(chip, child,
+ chip->usbid_change_irq, "usbid-change",
+ usbid_change_handler,
+ (IRQF_TRIGGER_FALLING | IRQF_ONESHOT));
+ if (rc < 0)
+ return rc;
+ rc = smbchg_request_irq(chip, child,
+ chip->otg_oc_irq, "otg-oc",
+ otg_oc_handler,
+ (IRQF_TRIGGER_RISING | IRQF_ONESHOT));
+ if (rc < 0)
+ return rc;
+ rc = smbchg_request_irq(chip, child,
+ chip->otg_fail_irq, "otg-fail",
+ otg_fail_handler, flags);
+ if (rc < 0)
+ return rc;
+ enable_irq_wake(chip->usbid_change_irq);
+ enable_irq_wake(chip->otg_oc_irq);
+ enable_irq_wake(chip->otg_fail_irq);
+ break;
+ }
+ }
+
+ return rc;
+}
+
+#define REQUIRE_BASE(chip, base, rc) \
+do { \
+ if (!rc && !chip->base) { \
+ dev_err(chip->dev, "Missing " #base "\n"); \
+ rc = -EINVAL; \
+ } \
+} while (0)
+
+static int smbchg_parse_peripherals(struct smbchg_chip *chip)
+{
+ int rc = 0;
+ unsigned int base;
+ struct device_node *child;
+ u8 subtype;
+
+ if (of_get_available_child_count(chip->pdev->dev.of_node) == 0) {
+ pr_err("no child nodes\n");
+ return -ENXIO;
+ }
+
+ for_each_available_child_of_node(chip->pdev->dev.of_node, child) {
+ rc = of_property_read_u32(child, "reg", &base);
+ if (rc < 0) {
+ rc = 0;
+ continue;
+ }
+
+ rc = smbchg_read(chip, &subtype, base + SUBTYPE_REG, 1);
+ if (rc) {
+ dev_err(chip->dev, "Peripheral subtype read failed rc=%d\n",
+ rc);
+ return rc;
+ }
+
+ switch (subtype) {
+ case SMBCHG_CHGR_SUBTYPE:
+ case SMBCHG_LITE_CHGR_SUBTYPE:
+ chip->chgr_base = base;
+ break;
+ case SMBCHG_BAT_IF_SUBTYPE:
+ case SMBCHG_LITE_BAT_IF_SUBTYPE:
+ chip->bat_if_base = base;
+ break;
+ case SMBCHG_USB_CHGPTH_SUBTYPE:
+ case SMBCHG_LITE_USB_CHGPTH_SUBTYPE:
+ chip->usb_chgpth_base = base;
+ break;
+ case SMBCHG_DC_CHGPTH_SUBTYPE:
+ case SMBCHG_LITE_DC_CHGPTH_SUBTYPE:
+ chip->dc_chgpth_base = base;
+ break;
+ case SMBCHG_MISC_SUBTYPE:
+ case SMBCHG_LITE_MISC_SUBTYPE:
+ chip->misc_base = base;
+ break;
+ case SMBCHG_OTG_SUBTYPE:
+ case SMBCHG_LITE_OTG_SUBTYPE:
+ chip->otg_base = base;
+ break;
+ }
+ }
+
+ REQUIRE_BASE(chip, chgr_base, rc);
+ REQUIRE_BASE(chip, bat_if_base, rc);
+ REQUIRE_BASE(chip, usb_chgpth_base, rc);
+ REQUIRE_BASE(chip, dc_chgpth_base, rc);
+ REQUIRE_BASE(chip, misc_base, rc);
+
+ return rc;
+}
+
+static inline void dump_reg(struct smbchg_chip *chip, u16 addr,
+ const char *name)
+{
+ u8 reg;
+
+ smbchg_read(chip, ®, addr, 1);
+ pr_smb(PR_DUMP, "%s - %04X = %02X\n", name, addr, reg);
+}
+
+/* dumps useful registers for debug */
+static void dump_regs(struct smbchg_chip *chip)
+{
+ u16 addr;
+
+ /* charger peripheral */
+ for (addr = 0xB; addr <= 0x10; addr++)
+ dump_reg(chip, chip->chgr_base + addr, "CHGR Status");
+ for (addr = 0xF0; addr <= 0xFF; addr++)
+ dump_reg(chip, chip->chgr_base + addr, "CHGR Config");
+ /* battery interface peripheral */
+ dump_reg(chip, chip->bat_if_base + RT_STS, "BAT_IF Status");
+ dump_reg(chip, chip->bat_if_base + CMD_CHG_REG, "BAT_IF Command");
+ for (addr = 0xF0; addr <= 0xFB; addr++)
+ dump_reg(chip, chip->bat_if_base + addr, "BAT_IF Config");
+ /* usb charge path peripheral */
+ for (addr = 0x7; addr <= 0x10; addr++)
+ dump_reg(chip, chip->usb_chgpth_base + addr, "USB Status");
+ dump_reg(chip, chip->usb_chgpth_base + CMD_IL, "USB Command");
+ for (addr = 0xF0; addr <= 0xF5; addr++)
+ dump_reg(chip, chip->usb_chgpth_base + addr, "USB Config");
+ /* dc charge path peripheral */
+ dump_reg(chip, chip->dc_chgpth_base + RT_STS, "DC Status");
+ for (addr = 0xF0; addr <= 0xF6; addr++)
+ dump_reg(chip, chip->dc_chgpth_base + addr, "DC Config");
+ /* misc peripheral */
+ dump_reg(chip, chip->misc_base + IDEV_STS, "MISC Status");
+ dump_reg(chip, chip->misc_base + RT_STS, "MISC Status");
+ for (addr = 0xF0; addr <= 0xF3; addr++)
+ dump_reg(chip, chip->misc_base + addr, "MISC CFG");
+}
+
+static int create_debugfs_entries(struct smbchg_chip *chip)
+{
+ struct dentry *ent;
+
+ chip->debug_root = debugfs_create_dir("qpnp-smbcharger", NULL);
+ if (!chip->debug_root) {
+ dev_err(chip->dev, "Couldn't create debug dir\n");
+ return -EINVAL;
+ }
+
+ ent = debugfs_create_file("force_dcin_icl_check",
+ 00100644, chip->debug_root, chip,
+ &force_dcin_icl_ops);
+ if (!ent) {
+ dev_err(chip->dev,
+ "Couldn't create force dcin icl check file\n");
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static int smbchg_check_chg_version(struct smbchg_chip *chip)
+{
+ struct pmic_revid_data *pmic_rev_id;
+ struct device_node *revid_dev_node;
+ int rc;
+
+ revid_dev_node = of_parse_phandle(chip->pdev->dev.of_node,
+ "qcom,pmic-revid", 0);
+ if (!revid_dev_node) {
+ pr_err("Missing qcom,pmic-revid property - driver failed\n");
+ return -EINVAL;
+ }
+
+ pmic_rev_id = get_revid_data(revid_dev_node);
+ if (IS_ERR(pmic_rev_id)) {
+ rc = PTR_ERR(revid_dev_node);
+ if (rc != -EPROBE_DEFER)
+ pr_err("Unable to get pmic_revid rc=%d\n", rc);
+ return rc;
+ }
+
+ switch (pmic_rev_id->pmic_subtype) {
+ case PMI8994:
+ chip->wa_flags |= SMBCHG_AICL_DEGLITCH_WA
+ | SMBCHG_BATT_OV_WA
+ | SMBCHG_CC_ESR_WA
+ | SMBCHG_RESTART_WA;
+ use_pmi8994_tables(chip);
+ chip->schg_version = QPNP_SCHG;
+ break;
+ case PMI8950:
+ case PMI8937:
+ chip->wa_flags |= SMBCHG_BATT_OV_WA;
+ if (pmic_rev_id->rev4 < 2) /* PMI8950 1.0 */ {
+ chip->wa_flags |= SMBCHG_AICL_DEGLITCH_WA;
+ } else { /* rev > PMI8950 v1.0 */
+ chip->wa_flags |= SMBCHG_HVDCP_9V_EN_WA
+ | SMBCHG_USB100_WA;
+ }
+ use_pmi8994_tables(chip);
+ chip->tables.aicl_rerun_period_table =
+ aicl_rerun_period_schg_lite;
+ chip->tables.aicl_rerun_period_len =
+ ARRAY_SIZE(aicl_rerun_period_schg_lite);
+
+ chip->schg_version = QPNP_SCHG_LITE;
+ if (pmic_rev_id->pmic_subtype == PMI8937)
+ chip->hvdcp_not_supported = true;
+ break;
+ case PMI8996:
+ chip->wa_flags |= SMBCHG_CC_ESR_WA
+ | SMBCHG_FLASH_ICL_DISABLE_WA
+ | SMBCHG_RESTART_WA
+ | SMBCHG_FLASH_BUCK_SWITCH_FREQ_WA;
+ use_pmi8996_tables(chip);
+ chip->schg_version = QPNP_SCHG;
+ break;
+ default:
+ pr_err("PMIC subtype %d not supported, WA flags not set\n",
+ pmic_rev_id->pmic_subtype);
+ }
+
+ pr_smb(PR_STATUS, "pmic=%s, wa_flags=0x%x, hvdcp_supported=%s\n",
+ pmic_rev_id->pmic_name, chip->wa_flags,
+ chip->hvdcp_not_supported ? "false" : "true");
+
+ return 0;
+}
+
+static void rerun_hvdcp_det_if_necessary(struct smbchg_chip *chip)
+{
+ enum power_supply_type usb_supply_type;
+ char *usb_type_name;
+ int rc;
+
+ if (!(chip->wa_flags & SMBCHG_RESTART_WA))
+ return;
+
+ read_usb_type(chip, &usb_type_name, &usb_supply_type);
+ if (usb_supply_type == POWER_SUPPLY_TYPE_USB_DCP
+ && !is_hvdcp_present(chip)) {
+ pr_smb(PR_STATUS, "DCP found rerunning APSD\n");
+ rc = vote(chip->usb_icl_votable,
+ CHG_SUSPEND_WORKAROUND_ICL_VOTER, true, 300);
+ if (rc < 0)
+ 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);
+
+ 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);
+ }
+
+ rc = vote(chip->usb_icl_votable,
+ CHG_SUSPEND_WORKAROUND_ICL_VOTER, false, 0);
+ if (rc < 0)
+ pr_err("Couldn't vote for 0 for suspend wa, going ahead rc=%d\n",
+ rc);
+ }
+}
+
+static int smbchg_probe(struct platform_device *pdev)
+{
+ int rc;
+ struct smbchg_chip *chip;
+ struct power_supply *typec_psy = NULL;
+ struct qpnp_vadc_chip *vadc_dev, *vchg_vadc_dev;
+ const char *typec_psy_name;
+ struct power_supply_config usb_psy_cfg = {};
+ struct power_supply_config batt_psy_cfg = {};
+ struct power_supply_config dc_psy_cfg = {};
+
+ if (of_property_read_bool(pdev->dev.of_node, "qcom,external-typec")) {
+ /* read the type power supply name */
+ rc = of_property_read_string(pdev->dev.of_node,
+ "qcom,typec-psy-name", &typec_psy_name);
+ if (rc) {
+ pr_err("failed to get prop typec-psy-name rc=%d\n",
+ rc);
+ return rc;
+ }
+
+ typec_psy = power_supply_get_by_name(typec_psy_name);
+ if (!typec_psy) {
+ pr_smb(PR_STATUS,
+ "Type-C supply not found, deferring probe\n");
+ return -EPROBE_DEFER;
+ }
+ }
+
+ vadc_dev = NULL;
+ if (of_find_property(pdev->dev.of_node, "qcom,dcin-vadc", NULL)) {
+ vadc_dev = qpnp_get_vadc(&pdev->dev, "dcin");
+ if (IS_ERR(vadc_dev)) {
+ rc = PTR_ERR(vadc_dev);
+ if (rc != -EPROBE_DEFER)
+ dev_err(&pdev->dev,
+ "Couldn't get vadc rc=%d\n",
+ rc);
+ return rc;
+ }
+ }
+
+ vchg_vadc_dev = NULL;
+ if (of_find_property(pdev->dev.of_node, "qcom,vchg_sns-vadc", NULL)) {
+ vchg_vadc_dev = qpnp_get_vadc(&pdev->dev, "vchg_sns");
+ if (IS_ERR(vchg_vadc_dev)) {
+ rc = PTR_ERR(vchg_vadc_dev);
+ if (rc != -EPROBE_DEFER)
+ dev_err(&pdev->dev, "Couldn't get vadc 'vchg' rc=%d\n",
+ rc);
+ return rc;
+ }
+ }
+
+
+ chip = devm_kzalloc(&pdev->dev, sizeof(*chip), GFP_KERNEL);
+ if (!chip)
+ return -ENOMEM;
+
+ chip->regmap = dev_get_regmap(pdev->dev.parent, NULL);
+ if (!chip->regmap) {
+ dev_err(&pdev->dev, "Couldn't get parent's regmap\n");
+ return -EINVAL;
+ }
+
+ chip->fcc_votable = create_votable("BATT_FCC",
+ VOTE_MIN,
+ set_fastchg_current_vote_cb, chip);
+ if (IS_ERR(chip->fcc_votable)) {
+ rc = PTR_ERR(chip->fcc_votable);
+ goto votables_cleanup;
+ }
+
+ chip->usb_icl_votable = create_votable("USB_ICL",
+ VOTE_MIN,
+ set_usb_current_limit_vote_cb, chip);
+ if (IS_ERR(chip->usb_icl_votable)) {
+ rc = PTR_ERR(chip->usb_icl_votable);
+ goto votables_cleanup;
+ }
+
+ chip->dc_icl_votable = create_votable("DCIN_ICL",
+ VOTE_MIN,
+ set_dc_current_limit_vote_cb, chip);
+ if (IS_ERR(chip->dc_icl_votable)) {
+ rc = PTR_ERR(chip->dc_icl_votable);
+ goto votables_cleanup;
+ }
+
+ chip->usb_suspend_votable = create_votable("USB_SUSPEND",
+ VOTE_SET_ANY,
+ usb_suspend_vote_cb, chip);
+ if (IS_ERR(chip->usb_suspend_votable)) {
+ rc = PTR_ERR(chip->usb_suspend_votable);
+ goto votables_cleanup;
+ }
+
+ chip->dc_suspend_votable = create_votable("DC_SUSPEND",
+ VOTE_SET_ANY,
+ dc_suspend_vote_cb, chip);
+ if (IS_ERR(chip->dc_suspend_votable)) {
+ rc = PTR_ERR(chip->dc_suspend_votable);
+ goto votables_cleanup;
+ }
+
+ chip->battchg_suspend_votable = create_votable("BATTCHG_SUSPEND",
+ VOTE_SET_ANY,
+ charging_suspend_vote_cb, chip);
+ if (IS_ERR(chip->battchg_suspend_votable)) {
+ rc = PTR_ERR(chip->battchg_suspend_votable);
+ goto votables_cleanup;
+ }
+
+ chip->hw_aicl_rerun_disable_votable = create_votable("HWAICL_DISABLE",
+ VOTE_SET_ANY,
+ smbchg_hw_aicl_rerun_disable_cb, chip);
+ if (IS_ERR(chip->hw_aicl_rerun_disable_votable)) {
+ rc = PTR_ERR(chip->hw_aicl_rerun_disable_votable);
+ goto votables_cleanup;
+ }
+
+ chip->hw_aicl_rerun_enable_indirect_votable = create_votable(
+ "HWAICL_ENABLE_INDIRECT",
+ VOTE_SET_ANY,
+ smbchg_hw_aicl_rerun_enable_indirect_cb, chip);
+ if (IS_ERR(chip->hw_aicl_rerun_enable_indirect_votable)) {
+ rc = PTR_ERR(chip->hw_aicl_rerun_enable_indirect_votable);
+ goto votables_cleanup;
+ }
+
+ chip->aicl_deglitch_short_votable = create_votable(
+ "HWAICL_SHORT_DEGLITCH",
+ VOTE_SET_ANY,
+ smbchg_aicl_deglitch_config_cb, chip);
+ if (IS_ERR(chip->aicl_deglitch_short_votable)) {
+ rc = PTR_ERR(chip->aicl_deglitch_short_votable);
+ goto votables_cleanup;
+ }
+
+ INIT_WORK(&chip->usb_set_online_work, smbchg_usb_update_online_work);
+ INIT_DELAYED_WORK(&chip->parallel_en_work,
+ smbchg_parallel_usb_en_work);
+ INIT_DELAYED_WORK(&chip->vfloat_adjust_work, smbchg_vfloat_adjust_work);
+ INIT_DELAYED_WORK(&chip->hvdcp_det_work, smbchg_hvdcp_det_work);
+ init_completion(&chip->src_det_lowered);
+ init_completion(&chip->src_det_raised);
+ init_completion(&chip->usbin_uv_lowered);
+ init_completion(&chip->usbin_uv_raised);
+ chip->vadc_dev = vadc_dev;
+ chip->vchg_vadc_dev = vchg_vadc_dev;
+ chip->pdev = pdev;
+ chip->dev = &pdev->dev;
+
+ chip->typec_psy = typec_psy;
+ chip->fake_battery_soc = -EINVAL;
+ chip->usb_online = -EINVAL;
+ dev_set_drvdata(&pdev->dev, chip);
+
+ spin_lock_init(&chip->sec_access_lock);
+ mutex_init(&chip->therm_lvl_lock);
+ mutex_init(&chip->usb_set_online_lock);
+ mutex_init(&chip->parallel.lock);
+ mutex_init(&chip->taper_irq_lock);
+ mutex_init(&chip->pm_lock);
+ mutex_init(&chip->wipower_config);
+ mutex_init(&chip->usb_status_lock);
+ device_init_wakeup(chip->dev, true);
+
+ rc = smbchg_parse_peripherals(chip);
+ if (rc) {
+ dev_err(chip->dev, "Error parsing DT peripherals: %d\n", rc);
+ goto votables_cleanup;
+ }
+
+ rc = smbchg_check_chg_version(chip);
+ if (rc) {
+ pr_err("Unable to check schg version rc=%d\n", rc);
+ goto votables_cleanup;
+ }
+
+ rc = smb_parse_dt(chip);
+ if (rc < 0) {
+ dev_err(&pdev->dev, "Unable to parse DT nodes: %d\n", rc);
+ goto votables_cleanup;
+ }
+
+ rc = smbchg_regulator_init(chip);
+ if (rc) {
+ dev_err(&pdev->dev,
+ "Couldn't initialize regulator rc=%d\n", rc);
+ goto votables_cleanup;
+ }
+
+ chip->extcon = devm_extcon_dev_allocate(chip->dev, smbchg_extcon_cable);
+ if (IS_ERR(chip->extcon)) {
+ dev_err(chip->dev, "failed to allocate extcon device\n");
+ rc = PTR_ERR(chip->extcon);
+ goto votables_cleanup;
+ }
+
+ rc = devm_extcon_dev_register(chip->dev, chip->extcon);
+ if (rc) {
+ dev_err(chip->dev, "failed to register extcon device\n");
+ goto votables_cleanup;
+ }
+
+ chip->usb_psy_d.name = "usb";
+ chip->usb_psy_d.type = POWER_SUPPLY_TYPE_USB;
+ chip->usb_psy_d.get_property = smbchg_usb_get_property;
+ chip->usb_psy_d.set_property = smbchg_usb_set_property;
+ chip->usb_psy_d.properties = smbchg_usb_properties;
+ chip->usb_psy_d.num_properties = ARRAY_SIZE(smbchg_usb_properties);
+ chip->usb_psy_d.property_is_writeable = smbchg_usb_is_writeable;
+
+ usb_psy_cfg.drv_data = chip;
+ usb_psy_cfg.supplied_to = smbchg_usb_supplicants;
+ usb_psy_cfg.num_supplicants = ARRAY_SIZE(smbchg_usb_supplicants);
+
+ chip->usb_psy = devm_power_supply_register(chip->dev,
+ &chip->usb_psy_d, &usb_psy_cfg);
+ if (IS_ERR(chip->usb_psy)) {
+ dev_err(&pdev->dev, "Unable to register usb_psy rc = %ld\n",
+ PTR_ERR(chip->usb_psy));
+ rc = PTR_ERR(chip->usb_psy);
+ goto votables_cleanup;
+ }
+
+ if (of_find_property(chip->dev->of_node, "dpdm-supply", NULL)) {
+ chip->dpdm_reg = devm_regulator_get(chip->dev, "dpdm");
+ if (IS_ERR(chip->dpdm_reg)) {
+ rc = PTR_ERR(chip->dpdm_reg);
+ goto votables_cleanup;
+ }
+ }
+
+ rc = smbchg_hw_init(chip);
+ if (rc < 0) {
+ dev_err(&pdev->dev,
+ "Unable to initialize hardware rc = %d\n", rc);
+ goto out;
+ }
+
+ rc = determine_initial_status(chip);
+ if (rc < 0) {
+ dev_err(&pdev->dev,
+ "Unable to determine init status rc = %d\n", rc);
+ goto out;
+ }
+
+ chip->previous_soc = -EINVAL;
+ chip->batt_psy_d.name = chip->battery_psy_name;
+ chip->batt_psy_d.type = POWER_SUPPLY_TYPE_BATTERY;
+ chip->batt_psy_d.get_property = smbchg_battery_get_property;
+ chip->batt_psy_d.set_property = smbchg_battery_set_property;
+ chip->batt_psy_d.properties = smbchg_battery_properties;
+ chip->batt_psy_d.num_properties = ARRAY_SIZE(smbchg_battery_properties);
+ chip->batt_psy_d.external_power_changed = smbchg_external_power_changed;
+ chip->batt_psy_d.property_is_writeable = smbchg_battery_is_writeable;
+
+ batt_psy_cfg.drv_data = chip;
+ batt_psy_cfg.num_supplicants = 0;
+ chip->batt_psy = devm_power_supply_register(chip->dev,
+ &chip->batt_psy_d,
+ &batt_psy_cfg);
+ if (IS_ERR(chip->batt_psy)) {
+ dev_err(&pdev->dev,
+ "Unable to register batt_psy rc = %ld\n",
+ PTR_ERR(chip->batt_psy));
+ goto out;
+ }
+
+ if (chip->dc_psy_type != -EINVAL) {
+ chip->dc_psy_d.name = "dc";
+ chip->dc_psy_d.type = chip->dc_psy_type;
+ chip->dc_psy_d.get_property = smbchg_dc_get_property;
+ chip->dc_psy_d.set_property = smbchg_dc_set_property;
+ chip->dc_psy_d.property_is_writeable = smbchg_dc_is_writeable;
+ chip->dc_psy_d.properties = smbchg_dc_properties;
+ chip->dc_psy_d.num_properties
+ = ARRAY_SIZE(smbchg_dc_properties);
+
+ dc_psy_cfg.drv_data = chip;
+ dc_psy_cfg.num_supplicants
+ = ARRAY_SIZE(smbchg_dc_supplicants);
+ dc_psy_cfg.supplied_to = smbchg_dc_supplicants;
+
+ chip->dc_psy = devm_power_supply_register(chip->dev,
+ &chip->dc_psy_d,
+ &dc_psy_cfg);
+ if (IS_ERR(chip->dc_psy)) {
+ dev_err(&pdev->dev,
+ "Unable to register dc_psy rc = %ld\n",
+ PTR_ERR(chip->dc_psy));
+ goto out;
+ }
+ }
+
+ if (chip->cfg_chg_led_support &&
+ chip->schg_version == QPNP_SCHG_LITE) {
+ rc = smbchg_register_chg_led(chip);
+ if (rc) {
+ dev_err(chip->dev,
+ "Unable to register charger led: %d\n",
+ rc);
+ goto out;
+ }
+
+ rc = smbchg_chg_led_controls(chip);
+ if (rc) {
+ dev_err(chip->dev,
+ "Failed to set charger led controld bit: %d\n",
+ rc);
+ goto unregister_led_class;
+ }
+ }
+
+ rc = smbchg_request_irqs(chip);
+ if (rc < 0) {
+ dev_err(&pdev->dev, "Unable to request irqs rc = %d\n", rc);
+ goto unregister_led_class;
+ }
+
+ rerun_hvdcp_det_if_necessary(chip);
+
+ dump_regs(chip);
+ create_debugfs_entries(chip);
+ dev_info(chip->dev,
+ "SMBCHG successfully probe Charger version=%s Revision DIG:%d.%d ANA:%d.%d batt=%d dc=%d usb=%d\n",
+ version_str[chip->schg_version],
+ chip->revision[DIG_MAJOR], chip->revision[DIG_MINOR],
+ chip->revision[ANA_MAJOR], chip->revision[ANA_MINOR],
+ get_prop_batt_present(chip),
+ chip->dc_present, chip->usb_present);
+ return 0;
+
+unregister_led_class:
+ if (chip->cfg_chg_led_support && chip->schg_version == QPNP_SCHG_LITE)
+ led_classdev_unregister(&chip->led_cdev);
+out:
+ handle_usb_removal(chip);
+votables_cleanup:
+ if (chip->aicl_deglitch_short_votable)
+ destroy_votable(chip->aicl_deglitch_short_votable);
+ if (chip->hw_aicl_rerun_enable_indirect_votable)
+ destroy_votable(chip->hw_aicl_rerun_enable_indirect_votable);
+ if (chip->hw_aicl_rerun_disable_votable)
+ destroy_votable(chip->hw_aicl_rerun_disable_votable);
+ if (chip->battchg_suspend_votable)
+ destroy_votable(chip->battchg_suspend_votable);
+ if (chip->dc_suspend_votable)
+ destroy_votable(chip->dc_suspend_votable);
+ if (chip->usb_suspend_votable)
+ destroy_votable(chip->usb_suspend_votable);
+ if (chip->dc_icl_votable)
+ destroy_votable(chip->dc_icl_votable);
+ if (chip->usb_icl_votable)
+ destroy_votable(chip->usb_icl_votable);
+ if (chip->fcc_votable)
+ destroy_votable(chip->fcc_votable);
+ return rc;
+}
+
+static int smbchg_remove(struct platform_device *pdev)
+{
+ struct smbchg_chip *chip = dev_get_drvdata(&pdev->dev);
+
+ debugfs_remove_recursive(chip->debug_root);
+
+ destroy_votable(chip->aicl_deglitch_short_votable);
+ destroy_votable(chip->hw_aicl_rerun_enable_indirect_votable);
+ destroy_votable(chip->hw_aicl_rerun_disable_votable);
+ destroy_votable(chip->battchg_suspend_votable);
+ destroy_votable(chip->dc_suspend_votable);
+ destroy_votable(chip->usb_suspend_votable);
+ destroy_votable(chip->dc_icl_votable);
+ destroy_votable(chip->usb_icl_votable);
+ destroy_votable(chip->fcc_votable);
+
+ return 0;
+}
+
+static void smbchg_shutdown(struct platform_device *pdev)
+{
+ struct smbchg_chip *chip = dev_get_drvdata(&pdev->dev);
+ int rc;
+
+ if (!(chip->wa_flags & SMBCHG_RESTART_WA))
+ return;
+
+ if (!is_hvdcp_present(chip))
+ return;
+
+ pr_smb(PR_MISC, "Disable Parallel\n");
+ mutex_lock(&chip->parallel.lock);
+ smbchg_parallel_en = 0;
+ smbchg_parallel_usb_disable(chip);
+ mutex_unlock(&chip->parallel.lock);
+
+ pr_smb(PR_MISC, "Disable all interrupts\n");
+ disable_irq(chip->aicl_done_irq);
+ disable_irq(chip->batt_cold_irq);
+ disable_irq(chip->batt_cool_irq);
+ disable_irq(chip->batt_hot_irq);
+ disable_irq(chip->batt_missing_irq);
+ disable_irq(chip->batt_warm_irq);
+ disable_irq(chip->chg_error_irq);
+ disable_irq(chip->chg_hot_irq);
+ disable_irq(chip->chg_term_irq);
+ disable_irq(chip->dcin_uv_irq);
+ disable_irq(chip->fastchg_irq);
+ disable_irq(chip->otg_fail_irq);
+ disable_irq(chip->otg_oc_irq);
+ disable_irq(chip->power_ok_irq);
+ disable_irq(chip->recharge_irq);
+ disable_irq(chip->src_detect_irq);
+ disable_irq(chip->taper_irq);
+ disable_irq(chip->usbid_change_irq);
+ disable_irq(chip->usbin_ov_irq);
+ disable_irq(chip->usbin_uv_irq);
+ disable_irq(chip->vbat_low_irq);
+ disable_irq(chip->wdog_timeout_irq);
+
+ /* remove all votes for short deglitch */
+ vote(chip->aicl_deglitch_short_votable,
+ VARB_WORKAROUND_SHORT_DEGLITCH_VOTER, false, 0);
+ vote(chip->aicl_deglitch_short_votable,
+ HVDCP_SHORT_DEGLITCH_VOTER, false, 0);
+
+ /* vote to ensure AICL rerun is enabled */
+ rc = vote(chip->hw_aicl_rerun_enable_indirect_votable,
+ SHUTDOWN_WORKAROUND_VOTER, true, 0);
+ if (rc < 0)
+ pr_err("Couldn't vote to enable indirect AICL rerun\n");
+ rc = vote(chip->hw_aicl_rerun_disable_votable,
+ WEAK_CHARGER_HW_AICL_VOTER, false, 0);
+ if (rc < 0)
+ pr_err("Couldn't vote to enable AICL rerun\n");
+
+ /* switch to 5V HVDCP */
+ pr_smb(PR_MISC, "Switch to 5V HVDCP\n");
+ rc = smbchg_sec_masked_write(chip, chip->usb_chgpth_base + CHGPTH_CFG,
+ HVDCP_ADAPTER_SEL_MASK, HVDCP_5V);
+ if (rc < 0) {
+ pr_err("Couldn't configure HVDCP 5V rc=%d\n", rc);
+ return;
+ }
+
+ pr_smb(PR_MISC, "Wait 500mS to lower to 5V\n");
+ /* wait for HVDCP to lower to 5V */
+ msleep(500);
+ /*
+ * Check if the same hvdcp session is in progress. src_det should be
+ * high and that we are still in 5V hvdcp
+ */
+ if (!is_src_detect_high(chip)) {
+ pr_smb(PR_MISC, "src det low after 500mS sleep\n");
+ return;
+ }
+
+ /* disable HVDCP */
+ pr_smb(PR_MISC, "Disable HVDCP\n");
+ rc = smbchg_sec_masked_write(chip, chip->usb_chgpth_base + CHGPTH_CFG,
+ HVDCP_EN_BIT, 0);
+ if (rc < 0)
+ pr_err("Couldn't disable HVDCP rc=%d\n", rc);
+
+ chip->hvdcp_3_det_ignore_uv = true;
+ /* fake a removal */
+ pr_smb(PR_MISC, "Faking Removal\n");
+ rc = fake_insertion_removal(chip, false);
+ if (rc < 0)
+ pr_err("Couldn't fake removal HVDCP Removed rc=%d\n", rc);
+
+ /* fake an insertion */
+ pr_smb(PR_MISC, "Faking Insertion\n");
+ rc = fake_insertion_removal(chip, true);
+ if (rc < 0)
+ pr_err("Couldn't fake insertion rc=%d\n", rc);
+
+ pr_smb(PR_MISC, "Wait 1S to settle\n");
+ msleep(1000);
+ chip->hvdcp_3_det_ignore_uv = false;
+
+ pr_smb(PR_STATUS, "wrote power off configurations\n");
+}
+
+static const struct dev_pm_ops smbchg_pm_ops = {
+};
+
+MODULE_DEVICE_TABLE(spmi, smbchg_id);
+
+static struct platform_driver smbchg_driver = {
+ .driver = {
+ .name = "qpnp-smbcharger",
+ .owner = THIS_MODULE,
+ .of_match_table = smbchg_match_table,
+ .pm = &smbchg_pm_ops,
+ },
+ .probe = smbchg_probe,
+ .remove = smbchg_remove,
+ .shutdown = smbchg_shutdown,
+};
+
+static int __init smbchg_init(void)
+{
+ return platform_driver_register(&smbchg_driver);
+}
+
+static void __exit smbchg_exit(void)
+{
+ return platform_driver_unregister(&smbchg_driver);
+}
+
+module_init(smbchg_init);
+module_exit(smbchg_exit);
+
+MODULE_DESCRIPTION("QPNP SMB Charger");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:qpnp-smbcharger");
diff --git a/drivers/power/supply/qcom/smb-lib.c b/drivers/power/supply/qcom/smb-lib.c
index 52fcd7f..ddc8701 100644
--- a/drivers/power/supply/qcom/smb-lib.c
+++ b/drivers/power/supply/qcom/smb-lib.c
@@ -2740,17 +2740,13 @@
return rc;
}
-int smblib_set_prop_pd_active(struct smb_charger *chg,
- const union power_supply_propval *val)
+static int __smblib_set_prop_pd_active(struct smb_charger *chg, bool pd_active)
{
int rc;
bool orientation, sink_attached, hvdcp;
u8 stat;
- if (!get_effective_result(chg->pd_allowed_votable))
- return -EINVAL;
-
- chg->pd_active = val->intval;
+ chg->pd_active = pd_active;
if (chg->pd_active) {
vote(chg->apsd_disable_votable, PD_VOTER, true, 0);
vote(chg->pd_allowed_votable, PD_VOTER, true, 0);
@@ -2838,6 +2834,15 @@
return rc;
}
+int smblib_set_prop_pd_active(struct smb_charger *chg,
+ const union power_supply_propval *val)
+{
+ if (!get_effective_result(chg->pd_allowed_votable))
+ return -EINVAL;
+
+ return __smblib_set_prop_pd_active(chg, val->intval);
+}
+
int smblib_set_prop_ship_mode(struct smb_charger *chg,
const union power_supply_propval *val)
{
@@ -3059,6 +3064,11 @@
{
int rc, jeita_cc_delta_ua = 0;
+ if (chg->sw_jeita_enabled) {
+ val->intval = 0;
+ return 0;
+ }
+
rc = smblib_get_jeita_cc_delta(chg, &jeita_cc_delta_ua);
if (rc < 0) {
smblib_err(chg, "Couldn't get jeita cc delta rc=%d\n", rc);
@@ -3593,6 +3603,13 @@
/* enforce DCP ICL if specified */
vote(chg->usb_icl_votable, DCP_VOTER,
chg->dcp_icl_ua != -EINVAL, chg->dcp_icl_ua);
+
+ /*
+ * if pd is not allowed, then set pd_active = false right here,
+ * so that it starts the hvdcp engine
+ */
+ if (!get_effective_result(chg->pd_allowed_votable))
+ __smblib_set_prop_pd_active(chg, 0);
}
smblib_dbg(chg, PR_INTERRUPT, "IRQ: smblib_handle_hvdcp_check_timeout %s\n",
@@ -3822,7 +3839,7 @@
* give opportunity to the other side to be a SRC,
* for tDRPTRY + Tccdebounce time
*/
- msleep(100);
+ msleep(120);
rc = smblib_read(chg, TYPE_C_STATUS_4_REG, &stat);
if (rc < 0) {
@@ -4007,6 +4024,7 @@
vote(chg->usb_icl_votable, PL_USBIN_USBIN_VOTER, false, 0);
vote(chg->usb_icl_votable, SW_QC3_VOTER, false, 0);
vote(chg->usb_icl_votable, OTG_VOTER, false, 0);
+ vote(chg->usb_icl_votable, CTM_VOTER, false, 0);
/* reset hvdcp voters */
vote(chg->hvdcp_disable_votable_indirect, VBUS_CC_SHORT_VOTER, true, 0);
@@ -4255,7 +4273,7 @@
if (chg->cc2_detach_wa_active || chg->typec_en_dis_active ||
chg->try_sink_active) {
- smblib_dbg(chg, PR_INTERRUPT, "Ignoring since %s active\n",
+ smblib_dbg(chg, PR_MISC | PR_INTERRUPT, "Ignoring since %s active\n",
chg->cc2_detach_wa_active ?
"cc2_detach_wa" : "typec_en_dis");
return IRQ_HANDLED;
@@ -4780,7 +4798,9 @@
smblib_err(chg, "Couldn't disable type-c rc=%d\n", rc);
/* wait for the adapter to turn off VBUS */
- msleep(500);
+ msleep(1000);
+
+ smblib_dbg(chg, PR_MISC, "legacy workaround enabling typec\n");
rc = smblib_masked_write(chg,
TYPE_C_INTRPT_ENB_SOFTWARE_CTRL_REG,
@@ -4789,7 +4809,7 @@
smblib_err(chg, "Couldn't enable type-c rc=%d\n", rc);
/* wait for type-c detection to complete */
- msleep(100);
+ msleep(400);
rc = smblib_read(chg, TYPE_C_STATUS_5_REG, &stat);
if (rc < 0) {
@@ -4801,6 +4821,8 @@
vote(chg->usb_icl_votable, LEGACY_UNKNOWN_VOTER, false, 0);
legacy = stat & TYPEC_LEGACY_CABLE_STATUS_BIT;
rp_high = chg->typec_mode == POWER_SUPPLY_TYPEC_SOURCE_HIGH;
+ smblib_dbg(chg, PR_MISC, "legacy workaround done legacy = %d rp_high = %d\n",
+ legacy, rp_high);
if (!legacy || !rp_high)
vote(chg->hvdcp_disable_votable_indirect, VBUS_CC_SHORT_VOTER,
false, 0);
diff --git a/drivers/power/supply/qcom/smb-reg.h b/drivers/power/supply/qcom/smb-reg.h
index 80573fc..d40d6fd 100644
--- a/drivers/power/supply/qcom/smb-reg.h
+++ b/drivers/power/supply/qcom/smb-reg.h
@@ -624,6 +624,7 @@
#define TAPER_TIMER_SEL_CFG_REG (USBIN_BASE + 0x64)
#define TYPEC_SPARE_CFG_BIT BIT(7)
+#define TYPEC_DRP_DFP_TIME_CFG_BIT BIT(5)
#define TAPER_TIMER_SEL_MASK GENMASK(1, 0)
#define USBIN_LOAD_CFG_REG (USBIN_BASE + 0x65)
diff --git a/drivers/power/supply/qcom/smb1355-charger.c b/drivers/power/supply/qcom/smb1355-charger.c
index 4b42420..560bb92 100644
--- a/drivers/power/supply/qcom/smb1355-charger.c
+++ b/drivers/power/supply/qcom/smb1355-charger.c
@@ -62,6 +62,9 @@
#define CHGR_BATTOV_CFG_REG (CHGR_BASE + 0x70)
#define BATTOV_SETTING_MASK GENMASK(7, 0)
+#define CHGR_PRE_TO_FAST_THRESHOLD_CFG_REG (CHGR_BASE + 0x74)
+#define PRE_TO_FAST_CHARGE_THRESHOLD_MASK GENMASK(2, 0)
+
#define POWER_MODE_HICCUP_CFG (BATIF_BASE + 0x72)
#define MAX_HICCUP_DUETO_BATDIS_MASK GENMASK(5, 2)
#define HICCUP_TIMEOUT_CFG_MASK GENMASK(1, 0)
@@ -619,6 +622,15 @@
return rc;
}
+ /* set Pre-to-Fast Charging Threshold 2.6V */
+ rc = smb1355_masked_write(chip, CHGR_PRE_TO_FAST_THRESHOLD_CFG_REG,
+ PRE_TO_FAST_CHARGE_THRESHOLD_MASK, 0);
+ if (rc < 0) {
+ pr_err("Couldn't set PRE_TO_FAST_CHARGE_THRESHOLD rc=%d\n",
+ rc);
+ return rc;
+ }
+
return 0;
}
diff --git a/drivers/power/supply/qcom/step-chg-jeita.c b/drivers/power/supply/qcom/step-chg-jeita.c
index 06ecc7e..c759314 100644
--- a/drivers/power/supply/qcom/step-chg-jeita.c
+++ b/drivers/power/supply/qcom/step-chg-jeita.c
@@ -123,7 +123,7 @@
/* TEMP_LOW TEMP_HIGH FCC */
{0, 100, 600000},
{101, 200, 2000000},
- {201, 450, 3000000},
+ {201, 450, 3450000},
{451, 550, 600000},
},
};
@@ -135,7 +135,7 @@
.fv_cfg = {
/* TEMP_LOW TEMP_HIGH FCC */
{0, 100, 4200000},
- {101, 450, 4400000},
+ {101, 450, 4350000},
{451, 550, 4200000},
},
};
diff --git a/drivers/regulator/qpnp-labibb-regulator.c b/drivers/regulator/qpnp-labibb-regulator.c
index dbe2a08..6b1e480 100644
--- a/drivers/regulator/qpnp-labibb-regulator.c
+++ b/drivers/regulator/qpnp-labibb-regulator.c
@@ -1226,6 +1226,7 @@
return rc;
}
+/* For PMI8998 and earlier PMICs */
static const struct ibb_ver_ops ibb_ops_v1 = {
.set_default_voltage = qpnp_ibb_set_default_voltage_v1,
.set_voltage = qpnp_ibb_set_voltage_v1,
@@ -1237,6 +1238,7 @@
.voltage_at_one_pulse = qpnp_ibb_output_voltage_at_one_pulse_v1,
};
+/* For PM660A and later PMICs */
static const struct ibb_ver_ops ibb_ops_v2 = {
.set_default_voltage = qpnp_ibb_set_default_voltage_v2,
.set_voltage = qpnp_ibb_set_voltage_v2,
@@ -1346,8 +1348,9 @@
u32 thresh, bool enable)
{
int rc = 0;
- u8 val;
+ u8 val, mask;
+ mask = LAB_PS_CTL_EN;
if (enable) {
for (val = 0; val < ARRAY_SIZE(lab_ps_thresh_table_v2); val++)
if (lab_ps_thresh_table_v2[val] == thresh)
@@ -1359,13 +1362,13 @@
}
val |= LAB_PS_CTL_EN;
+ mask |= LAB_PS_THRESH_MASK;
} else {
val = 0;
}
- rc = qpnp_labibb_write(labibb, labibb->lab_base +
- REG_LAB_PS_CTL, &val, 1);
-
+ rc = qpnp_labibb_masked_write(labibb, labibb->lab_base +
+ REG_LAB_PS_CTL, mask, val);
if (rc < 0)
pr_err("write register %x failed rc = %d\n",
REG_LAB_PS_CTL, rc);
@@ -1373,12 +1376,18 @@
return rc;
}
+/* For PMI8996 and earlier PMICs */
static const struct lab_ver_ops lab_ops_v1 = {
.set_default_voltage = qpnp_lab_set_default_voltage_v1,
.ps_ctl = qpnp_lab_ps_ctl_v1,
};
-static const struct lab_ver_ops lab_ops_v2 = {
+static const struct lab_ver_ops pmi8998_lab_ops = {
+ .set_default_voltage = qpnp_lab_set_default_voltage_v1,
+ .ps_ctl = qpnp_lab_ps_ctl_v2,
+};
+
+static const struct lab_ver_ops pm660_lab_ops = {
.set_default_voltage = qpnp_lab_set_default_voltage_v2,
.ps_ctl = qpnp_lab_ps_ctl_v2,
};
@@ -3003,7 +3012,7 @@
struct device_node *of_node)
{
int rc = 0;
- u32 i, tmp = 0;
+ u32 i = 0, tmp = 0;
u8 val, mask;
/*
@@ -3037,37 +3046,48 @@
rc = of_property_read_u32(of_node,
"qcom,qpnp-ibb-lab-pwrdn-delay", &tmp);
if (!rc) {
- for (val = 0; val < ARRAY_SIZE(ibb_pwrdn_dly_table); val++)
- if (ibb_pwrdn_dly_table[val] == tmp)
- break;
+ if (tmp > 0) {
+ for (i = 0; i < ARRAY_SIZE(ibb_pwrdn_dly_table); i++) {
+ if (ibb_pwrdn_dly_table[i] == tmp)
+ break;
+ }
- if (val == ARRAY_SIZE(ibb_pwrdn_dly_table)) {
- pr_err("Invalid value in qcom,qpnp-ibb-lab-pwrdn-delay\n");
- return -EINVAL;
+ if (i == ARRAY_SIZE(ibb_pwrdn_dly_table)) {
+ pr_err("Invalid value in qcom,qpnp-ibb-lab-pwrdn-delay\n");
+ return -EINVAL;
+ }
}
labibb->ibb_vreg.pwrdn_dly = tmp;
- val |= IBB_PWRUP_PWRDN_CTL_1_EN_DLY2;
+
+ if (tmp > 0)
+ val = i | IBB_PWRUP_PWRDN_CTL_1_EN_DLY2;
+
mask |= IBB_PWRUP_PWRDN_CTL_1_EN_DLY2;
}
rc = of_property_read_u32(of_node,
"qcom,qpnp-ibb-lab-pwrup-delay", &tmp);
if (!rc) {
- for (i = 0; i < ARRAY_SIZE(ibb_pwrup_dly_table); i++)
- if (ibb_pwrup_dly_table[i] == tmp)
- break;
+ if (tmp > 0) {
+ for (i = 0; i < ARRAY_SIZE(ibb_pwrup_dly_table); i++) {
+ if (ibb_pwrup_dly_table[i] == tmp)
+ break;
+ }
- if (i == ARRAY_SIZE(ibb_pwrup_dly_table)) {
- pr_err("Invalid value in qcom,qpnp-ibb-lab-pwrup-delay\n");
- return -EINVAL;
+ if (i == ARRAY_SIZE(ibb_pwrup_dly_table)) {
+ pr_err("Invalid value in qcom,qpnp-ibb-lab-pwrup-delay\n");
+ return -EINVAL;
+ }
}
labibb->ibb_vreg.pwrup_dly = tmp;
+ if (tmp > 0)
+ val |= IBB_PWRUP_PWRDN_CTL_1_EN_DLY1;
+
val |= (i << IBB_PWRUP_PWRDN_CTL_1_DLY1_SHIFT);
- val |= (IBB_PWRUP_PWRDN_CTL_1_EN_DLY1 |
- IBB_PWRUP_PWRDN_CTL_1_LAB_VREG_OK);
+ val |= IBB_PWRUP_PWRDN_CTL_1_LAB_VREG_OK;
mask |= (IBB_PWRUP_PWRDN_CTL_1_EN_DLY1 |
IBB_PWRUP_PWRDN_CTL_1_DLY1_MASK |
IBB_PWRUP_PWRDN_CTL_1_LAB_VREG_OK);
@@ -3723,7 +3743,10 @@
if (labibb->pmic_rev_id->pmic_subtype == PM660L_SUBTYPE) {
labibb->ibb_ver_ops = &ibb_ops_v2;
- labibb->lab_ver_ops = &lab_ops_v2;
+ labibb->lab_ver_ops = &pm660_lab_ops;
+ } else if (labibb->pmic_rev_id->pmic_subtype == PMI8998_SUBTYPE) {
+ labibb->ibb_ver_ops = &ibb_ops_v1;
+ labibb->lab_ver_ops = &pmi8998_lab_ops;
} else {
labibb->ibb_ver_ops = &ibb_ops_v1;
labibb->lab_ver_ops = &lab_ops_v1;
diff --git a/drivers/scsi/ufs/ufs-qcom-debugfs.c b/drivers/scsi/ufs/ufs-qcom-debugfs.c
index 4547a6d..494ecd1 100644
--- a/drivers/scsi/ufs/ufs-qcom-debugfs.c
+++ b/drivers/scsi/ufs/ufs-qcom-debugfs.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2015, Linux Foundation. All rights reserved.
+ * Copyright (c) 2015,2017, 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
@@ -111,12 +111,15 @@
loff_t *ppos)
{
struct ufs_qcom_host *host = file->f_mapping->host->i_private;
- char configuration[TESTBUS_CFG_BUFF_LINE_SIZE] = {0};
+ char configuration[TESTBUS_CFG_BUFF_LINE_SIZE] = {'\0'};
loff_t buff_pos = 0;
char *comma;
int ret = 0;
int major;
int minor;
+ unsigned long flags;
+ struct ufs_hba *hba = host->hba;
+
ret = simple_write_to_buffer(configuration, TESTBUS_CFG_BUFF_LINE_SIZE,
&buff_pos, ubuf, cnt);
@@ -125,6 +128,7 @@
__func__);
goto out;
}
+ configuration[ret] = '\0';
comma = strnchr(configuration, TESTBUS_CFG_BUFF_LINE_SIZE, ',');
if (!comma || comma == configuration) {
@@ -142,8 +146,15 @@
goto out;
}
+ if (!ufs_qcom_testbus_cfg_is_ok(host, major, minor)) {
+ ret = -EPERM;
+ goto out;
+ }
+
+ spin_lock_irqsave(hba->host->host_lock, flags);
host->testbus.select_major = (u8)major;
host->testbus.select_minor = (u8)minor;
+ spin_unlock_irqrestore(hba->host->host_lock, flags);
/*
* Sanity check of the {major, minor} tuple is done in the
diff --git a/drivers/scsi/ufs/ufs-qcom.c b/drivers/scsi/ufs/ufs-qcom.c
index ee23fc7..1ad191e 100644
--- a/drivers/scsi/ufs/ufs-qcom.c
+++ b/drivers/scsi/ufs/ufs-qcom.c
@@ -1987,6 +1987,18 @@
}
#endif /* CONFIG_SMP */
+#define ANDROID_BOOT_DEV_MAX 30
+static char android_boot_dev[ANDROID_BOOT_DEV_MAX];
+
+#ifndef MODULE
+static int __init get_android_boot_dev(char *str)
+{
+ strlcpy(android_boot_dev, str, ANDROID_BOOT_DEV_MAX);
+ return 1;
+}
+__setup("androidboot.bootdevice=", get_android_boot_dev);
+#endif
+
/*
* ufs_qcom_parse_lpm - read from DTS whether LPM modes should be disabled.
*/
@@ -2454,12 +2466,13 @@
host->testbus.select_minor = 37;
}
-static bool ufs_qcom_testbus_cfg_is_ok(struct ufs_qcom_host *host)
+bool ufs_qcom_testbus_cfg_is_ok(struct ufs_qcom_host *host,
+ u8 select_major, u8 select_minor)
{
- if (host->testbus.select_major >= TSTBUS_MAX) {
+ if (select_major >= TSTBUS_MAX) {
dev_err(host->hba->dev,
"%s: UFS_CFG1[TEST_BUS_SEL} may not equal 0x%05X\n",
- __func__, host->testbus.select_major);
+ __func__, select_major);
return false;
}
@@ -2468,10 +2481,10 @@
* mappings of select_minor, since there is no harm in
* configuring a non-existent select_minor
*/
- if (host->testbus.select_minor > 0xFF) {
+ if (select_minor > 0xFF) {
dev_err(host->hba->dev,
"%s: 0x%05X is not a legal testbus option\n",
- __func__, host->testbus.select_minor);
+ __func__, select_minor);
return false;
}
@@ -2485,16 +2498,16 @@
*/
int ufs_qcom_testbus_config(struct ufs_qcom_host *host)
{
- int reg;
- int offset;
+ int reg = 0;
+ int offset, ret = 0, testbus_sel_offset = 19;
u32 mask = TEST_BUS_SUB_SEL_MASK;
+ unsigned long flags;
+ struct ufs_hba *hba;
if (!host)
return -EINVAL;
-
- if (!ufs_qcom_testbus_cfg_is_ok(host))
- return -EPERM;
-
+ hba = host->hba;
+ spin_lock_irqsave(hba->host->host_lock, flags);
switch (host->testbus.select_major) {
case TSTBUS_UAWM:
reg = UFS_TEST_BUS_CTRL_0;
@@ -2553,20 +2566,27 @@
}
mask <<= offset;
- ufshcd_rmwl(host->hba, TEST_BUS_SEL,
- (u32)host->testbus.select_major << 19,
+ spin_unlock_irqrestore(hba->host->host_lock, flags);
+ if (reg) {
+ ufshcd_rmwl(host->hba, TEST_BUS_SEL,
+ (u32)host->testbus.select_major << testbus_sel_offset,
REG_UFS_CFG1);
- ufshcd_rmwl(host->hba, mask,
+ ufshcd_rmwl(host->hba, mask,
(u32)host->testbus.select_minor << offset,
reg);
+ } else {
+ dev_err(hba->dev, "%s: Problem setting minor\n", __func__);
+ ret = -EINVAL;
+ goto out;
+ }
ufs_qcom_enable_test_bus(host);
/*
* Make sure the test bus configuration is
* committed before returning.
*/
mb();
-
- return 0;
+out:
+ return ret;
}
static void ufs_qcom_testbus_read(struct ufs_hba *hba)
@@ -2676,6 +2696,24 @@
{
int err;
struct device *dev = &pdev->dev;
+ struct device_node *np = dev->of_node;
+
+ /*
+ * On qcom platforms, bootdevice is the primary storage
+ * device. This device can either be eMMC or UFS.
+ * The type of device connected is detected at runtime.
+ * So, if an eMMC device is connected, and this function
+ * is invoked, it would turn-off the regulator if it detects
+ * that the storage device is not ufs.
+ * These regulators are turned ON by the bootloaders & turning
+ * them off without sending PON may damage the connected device.
+ * Hence, check for the connected device early-on & don't turn-off
+ * the regulators.
+ */
+ if (of_property_read_bool(np, "non-removable") &&
+ strlen(android_boot_dev) &&
+ strcmp(android_boot_dev, dev_name(dev)))
+ return -ENODEV;
/* Perform generic probe */
err = ufshcd_pltfrm_init(pdev, &ufs_hba_qcom_variant);
diff --git a/drivers/scsi/ufs/ufs-qcom.h b/drivers/scsi/ufs/ufs-qcom.h
index 32edd76..0ab656e 100644
--- a/drivers/scsi/ufs/ufs-qcom.h
+++ b/drivers/scsi/ufs/ufs-qcom.h
@@ -100,7 +100,7 @@
/* bit definitions for REG_UFS_CFG1 register */
#define QUNIPRO_SEL UFS_BIT(0)
#define TEST_BUS_EN BIT(18)
-#define TEST_BUS_SEL GENMASK(22, 19)
+#define TEST_BUS_SEL 0x780000
#define UFS_REG_TEST_BUS_EN BIT(30)
/* bit definitions for REG_UFS_CFG2 register */
@@ -390,6 +390,8 @@
#define ufs_qcom_is_link_active(hba) ufshcd_is_link_active(hba)
#define ufs_qcom_is_link_hibern8(hba) ufshcd_is_link_hibern8(hba)
+bool ufs_qcom_testbus_cfg_is_ok(struct ufs_qcom_host *host, u8 select_major,
+ u8 select_minor);
int ufs_qcom_testbus_config(struct ufs_qcom_host *host);
void ufs_qcom_print_hw_debug_reg_all(struct ufs_hba *hba, void *priv,
void (*print_fn)(struct ufs_hba *hba, int offset, int num_regs,
diff --git a/drivers/scsi/ufs/ufs_quirks.c b/drivers/scsi/ufs/ufs_quirks.c
index da2bfd5..a2b98fb 100644
--- a/drivers/scsi/ufs/ufs_quirks.c
+++ b/drivers/scsi/ufs/ufs_quirks.c
@@ -18,8 +18,6 @@
/* UFS cards deviations table */
UFS_FIX(UFS_VENDOR_SAMSUNG, UFS_ANY_MODEL, UFS_DEVICE_NO_VCCQ),
UFS_FIX(UFS_VENDOR_SAMSUNG, UFS_ANY_MODEL,
- UFS_DEVICE_QUIRK_RECOVERY_FROM_DL_NAC_ERRORS),
- UFS_FIX(UFS_VENDOR_SAMSUNG, UFS_ANY_MODEL,
UFS_DEVICE_NO_FASTAUTO),
UFS_FIX(UFS_VENDOR_SAMSUNG, UFS_ANY_MODEL,
UFS_DEVICE_QUIRK_HOST_PA_TACTIVATE),
diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c
index b6ba4c4..b98d2ae 100644
--- a/drivers/scsi/ufs/ufshcd.c
+++ b/drivers/scsi/ufs/ufshcd.c
@@ -172,6 +172,9 @@
}
#endif
+#define PWR_INFO_MASK 0xF
+#define PWR_RX_OFFSET 4
+
#define UFSHCD_REQ_SENSE_SIZE 18
#define UFSHCD_ENABLE_INTRS (UTP_TRANSFER_REQ_COMPL |\
@@ -408,8 +411,8 @@
#if IS_ENABLED(CONFIG_DEVFREQ_GOV_SIMPLE_ONDEMAND)
static struct devfreq_simple_ondemand_data ufshcd_ondemand_data = {
- .upthreshold = 35,
- .downdifferential = 30,
+ .upthreshold = 70,
+ .downdifferential = 65,
.simple_scaling = 1,
};
@@ -419,7 +422,7 @@
#endif
static struct devfreq_dev_profile ufs_devfreq_profile = {
- .polling_ms = 40,
+ .polling_ms = 60,
.target = ufshcd_devfreq_target,
.get_dev_status = ufshcd_devfreq_get_dev_status,
};
@@ -4802,8 +4805,9 @@
int ret = 0;
/* if already configured to the requested pwr_mode */
- if (pwr_mode->gear_rx == hba->pwr_info.gear_rx &&
- pwr_mode->gear_tx == hba->pwr_info.gear_tx &&
+ if (!hba->restore_needed &&
+ pwr_mode->gear_rx == hba->pwr_info.gear_rx &&
+ pwr_mode->gear_tx == hba->pwr_info.gear_tx &&
pwr_mode->lane_rx == hba->pwr_info.lane_rx &&
pwr_mode->lane_tx == hba->pwr_info.lane_tx &&
pwr_mode->pwr_rx == hba->pwr_info.pwr_rx &&
@@ -6471,6 +6475,52 @@
reg_hist->pos = (reg_hist->pos + 1) % UIC_ERR_REG_HIST_LENGTH;
}
+static void ufshcd_rls_handler(struct work_struct *work)
+{
+ struct ufs_hba *hba;
+ int ret = 0;
+ u32 mode;
+
+ hba = container_of(work, struct ufs_hba, rls_work);
+ ufshcd_scsi_block_requests(hba);
+ pm_runtime_get_sync(hba->dev);
+ ret = ufshcd_wait_for_doorbell_clr(hba, U64_MAX);
+ if (ret) {
+ dev_err(hba->dev,
+ "Timed out (%d) waiting for DB to clear\n",
+ ret);
+ goto out;
+ }
+
+ ufshcd_dme_get(hba, UIC_ARG_MIB(PA_PWRMODE), &mode);
+ if (hba->pwr_info.pwr_rx != ((mode >> PWR_RX_OFFSET) & PWR_INFO_MASK))
+ hba->restore_needed = true;
+
+ if (hba->pwr_info.pwr_tx != (mode & PWR_INFO_MASK))
+ hba->restore_needed = true;
+
+ ufshcd_dme_get(hba, UIC_ARG_MIB(PA_RXGEAR), &mode);
+ if (hba->pwr_info.gear_rx != mode)
+ hba->restore_needed = true;
+
+ ufshcd_dme_get(hba, UIC_ARG_MIB(PA_TXGEAR), &mode);
+ if (hba->pwr_info.gear_tx != mode)
+ hba->restore_needed = true;
+
+ if (hba->restore_needed)
+ ret = ufshcd_config_pwr_mode(hba, &(hba->pwr_info));
+
+ if (ret)
+ dev_err(hba->dev, "%s: Failed setting power mode, err = %d\n",
+ __func__, ret);
+ else
+ hba->restore_needed = false;
+
+out:
+ ufshcd_scsi_unblock_requests(hba);
+ pm_runtime_put_sync(hba->dev);
+}
+
/**
* ufshcd_update_uic_error - check and set fatal UIC error flags.
* @hba: per-adapter instance
@@ -6510,6 +6560,8 @@
hba->full_init_linereset = true;
}
}
+ if (!hba->full_init_linereset)
+ schedule_work(&hba->rls_work);
}
retval |= IRQ_HANDLED;
}
@@ -9208,6 +9260,7 @@
goto enable_gating;
}
+ flush_work(&hba->eeh_work);
ret = ufshcd_link_state_transition(hba, req_link_state, 1);
if (ret)
goto set_dev_active;
@@ -10449,6 +10502,7 @@
INIT_WORK(&hba->eh_work, ufshcd_err_handler);
INIT_WORK(&hba->eeh_work, ufshcd_exception_event_handler);
INIT_WORK(&hba->card_detect_work, ufshcd_card_detect_handler);
+ INIT_WORK(&hba->rls_work, ufshcd_rls_handler);
/* Initialize UIC command mutex */
mutex_init(&hba->uic_cmd_mutex);
diff --git a/drivers/scsi/ufs/ufshcd.h b/drivers/scsi/ufs/ufshcd.h
index 1b21238..1f5c404 100644
--- a/drivers/scsi/ufs/ufshcd.h
+++ b/drivers/scsi/ufs/ufshcd.h
@@ -873,6 +873,7 @@
/* Work Queues */
struct work_struct eh_work;
struct work_struct eeh_work;
+ struct work_struct rls_work;
/* HBA Errors */
u32 errors;
@@ -987,6 +988,7 @@
int latency_hist_enabled;
struct io_latency_state io_lat_s;
struct ufs_desc_size desc_size;
+ bool restore_needed;
};
static inline void ufshcd_mark_shutdown_ongoing(struct ufs_hba *hba)
diff --git a/drivers/slimbus/slim-msm-ngd.c b/drivers/slimbus/slim-msm-ngd.c
index ca56462..5fb0c29 100644
--- a/drivers/slimbus/slim-msm-ngd.c
+++ b/drivers/slimbus/slim-msm-ngd.c
@@ -422,9 +422,11 @@
u8 txn_mt;
u16 txn_mc = txn->mc;
u8 wbuf[SLIM_MSGQ_BUF_LEN];
+ const u8 *old_wbuf = NULL;
bool report_sat = false;
bool sync_wr = true;
+ memset(wbuf, 0, sizeof(wbuf));
if (txn->mc & SLIM_MSG_CLK_PAUSE_SEQ_FLG)
return -EPROTONOSUPPORT;
@@ -590,6 +592,7 @@
goto ngd_xfer_err;
}
txn->len = i;
+ old_wbuf = txn->wbuf;
txn->wbuf = wbuf;
txn->rl = txn->len + 4;
}
@@ -758,13 +761,18 @@
ctrl->txnt[txn->tid] = NULL;
spin_unlock_irqrestore(&ctrl->txn_lock, flags);
}
- return ret ? ret : dev->err;
+ goto ngd_xfer_ret;
}
ngd_xfer_err:
if (!report_sat) {
mutex_unlock(&dev->tx_lock);
msm_slim_put_ctrl(dev);
}
+ngd_xfer_ret:
+ if (txn->wbuf == wbuf)
+ txn->wbuf = old_wbuf;
+ if (txn->comp == &done)
+ txn->comp = NULL;
return ret ? ret : dev->err;
}
diff --git a/drivers/slimbus/slimbus.c b/drivers/slimbus/slimbus.c
index 3ae9a10..4c9210a 100644
--- a/drivers/slimbus/slimbus.c
+++ b/drivers/slimbus/slimbus.c
@@ -2512,7 +2512,7 @@
}
}
/* schedule 4k family channels */
- while (coeff1 < ctrl->sched.num_cc1 &&
+ while (coeff1 < ctrl->sched.num_cc1 && slc1 &&
curexp == (int)slc1->rootexp + expshft) {
/* searchorder effective when opensl valid */
static const int srcho[] = { 5, 2, 4, 1, 3, 0 };
diff --git a/drivers/soc/qcom/Kconfig b/drivers/soc/qcom/Kconfig
index ef55e58..62306bad 100644
--- a/drivers/soc/qcom/Kconfig
+++ b/drivers/soc/qcom/Kconfig
@@ -71,6 +71,16 @@
deadlock detection mode AMON will trigger an interrupt if some LLCC request
ages out.
+config QCOM_LLCC_PERFMON
+ tristate "Qualcomm Technologies, Inc. LLCC Perfmon driver"
+ depends on QCOM_LLCC
+ help
+ This option enables driver for LLCC Performance monitor block. Using
+ this various events in different LLCC sub block ports can be monitored.
+ This is used for performance and debug activity and exports SYSFS
+ interface. SYSFS interface used for configure and dump the LLCC
+ performance events.
+
config QCOM_PM
bool "Qualcomm Power Management"
depends on ARCH_QCOM && !ARM64
@@ -375,16 +385,6 @@
32-bit values by specifying a unique string and
remote processor ID.
-config MSM_SMP2P_TEST
- bool "SMSM Point-to-Point Test"
- depends on MSM_SMP2P
- help
- Enables loopback and unit testing support for
- SMP2P. Loopback support is used by other
- processors to do unit testing. Unit tests
- are used to verify the local and remote
- implementations.
-
config MSM_IPC_ROUTER_SMD_XPRT
depends on MSM_SMD
depends on IPC_ROUTER
@@ -590,13 +590,6 @@
trusted apps, unloading them and marshalling buffers to the
trusted fingerprint app.
-config APSS_CORE_EA
- depends on CPU_FREQ && PM_OPP
- bool "Qualcomm Technology Inc specific power aware driver"
- help
- Platform specific power aware driver to provide power
- and temperature information to the scheduler.
-
if MSM_PM
menuconfig MSM_IDLE_STATS
bool "Collect idle statistics"
diff --git a/drivers/soc/qcom/Makefile b/drivers/soc/qcom/Makefile
index 37b33e6..9a4e010 100644
--- a/drivers/soc/qcom/Makefile
+++ b/drivers/soc/qcom/Makefile
@@ -6,6 +6,7 @@
obj-$(CONFIG_QCOM_LLCC) += llcc-core.o llcc-slice.o
obj-$(CONFIG_QCOM_SDM845_LLCC) += llcc-sdm845.o
obj-$(CONFIG_QCOM_SDM670_LLCC) += llcc-sdm670.o
+obj-$(CONFIG_QCOM_LLCC_PERFMON) += llcc_perfmon.o
obj-$(CONFIG_QCOM_LLCC_AMON) += llcc-amon.o
obj-$(CONFIG_QPNP_PBS) += qpnp-pbs.o
obj-$(CONFIG_QCOM_PM) += spm.o
@@ -42,8 +43,7 @@
obj-$(CONFIG_QTI_SYSTEM_PM) += system_pm.o
obj-$(CONFIG_MSM_SERVICE_NOTIFIER) += service-notifier.o
obj-$(CONFIG_MSM_SERVICE_LOCATOR) += service-locator.o
-obj-$(CONFIG_MSM_SMP2P) += msm_smp2p.o smp2p_debug.o smp2p_sleepstate.o
-obj-$(CONFIG_MSM_SMP2P_TEST) += smp2p_loopback.o smp2p_test.o smp2p_spinlock_test.o
+obj-$(CONFIG_MSM_SMP2P) += msm_smp2p.o smp2p_loopback.o smp2p_debug.o smp2p_sleepstate.o
obj-$(CONFIG_MSM_IPC_ROUTER_SMD_XPRT) += ipc_router_smd_xprt.o
obj-$(CONFIG_MSM_IPC_ROUTER_HSIC_XPRT) += ipc_router_hsic_xprt.o
obj-$(CONFIG_MSM_IPC_ROUTER_MHI_XPRT) += ipc_router_mhi_xprt.o
@@ -73,7 +73,6 @@
obj-$(CONFIG_MSM_QBT1000) += qbt1000.o
obj-$(CONFIG_MSM_EVENT_TIMER) += event_timer.o
obj-$(CONFIG_MSM_IDLE_STATS) += lpm-stats.o
-obj-$(CONFIG_APSS_CORE_EA) += msm-core.o debug_core.o
obj-$(CONFIG_QCOM_DCC_V2) += dcc_v2.o
obj-$(CONFIG_QTI_RPM_STATS_LOG) += rpm_stats.o
obj-$(CONFIG_QCOM_SMCINVOKE) += smcinvoke.o
diff --git a/drivers/soc/qcom/icnss.c b/drivers/soc/qcom/icnss.c
index 6ce481b..04c611c 100644
--- a/drivers/soc/qcom/icnss.c
+++ b/drivers/soc/qcom/icnss.c
@@ -73,6 +73,8 @@
#define ICNSS_THRESHOLD_LOW 3450000
#define ICNSS_THRESHOLD_GUARD 20000
+#define ICNSS_MAX_PROBE_CNT 2
+
#define icnss_ipc_log_string(_x...) do { \
if (icnss_ipc_log_context) \
ipc_log_string(icnss_ipc_log_context, _x); \
@@ -2103,7 +2105,8 @@
static int icnss_call_driver_probe(struct icnss_priv *priv)
{
- int ret;
+ int ret = 0;
+ int probe_cnt = 0;
if (!priv->ops || !priv->ops->probe)
return 0;
@@ -2115,10 +2118,15 @@
icnss_hw_power_on(priv);
- ret = priv->ops->probe(&priv->pdev->dev);
+ while (probe_cnt < ICNSS_MAX_PROBE_CNT) {
+ ret = priv->ops->probe(&priv->pdev->dev);
+ probe_cnt++;
+ if (ret != -EPROBE_DEFER)
+ break;
+ }
if (ret < 0) {
- icnss_pr_err("Driver probe failed: %d, state: 0x%lx\n",
- ret, priv->state);
+ icnss_pr_err("Driver probe failed: %d, state: 0x%lx, probe_cnt: %d\n",
+ ret, priv->state, probe_cnt);
goto out;
}
@@ -2226,6 +2234,7 @@
static int icnss_driver_event_register_driver(void *data)
{
int ret = 0;
+ int probe_cnt = 0;
if (penv->ops)
return -EEXIST;
@@ -2245,11 +2254,15 @@
if (ret)
goto out;
- ret = penv->ops->probe(&penv->pdev->dev);
-
+ while (probe_cnt < ICNSS_MAX_PROBE_CNT) {
+ ret = penv->ops->probe(&penv->pdev->dev);
+ probe_cnt++;
+ if (ret != -EPROBE_DEFER)
+ break;
+ }
if (ret) {
- icnss_pr_err("Driver probe failed: %d, state: 0x%lx\n",
- ret, penv->state);
+ icnss_pr_err("Driver probe failed: %d, state: 0x%lx, probe_cnt: %d\n",
+ ret, penv->state, probe_cnt);
goto power_off;
}
@@ -2636,7 +2649,9 @@
clear_bit(ICNSS_HOST_TRIGGERED_PDR, &priv->state);
fw_down_data.crashed = event_data->crashed;
- icnss_call_driver_uevent(priv, ICNSS_UEVENT_FW_DOWN, &fw_down_data);
+ if (test_bit(ICNSS_FW_READY, &priv->state))
+ icnss_call_driver_uevent(priv, ICNSS_UEVENT_FW_DOWN,
+ &fw_down_data);
icnss_driver_event_post(ICNSS_DRIVER_EVENT_PD_SERVICE_DOWN,
ICNSS_EVENT_SYNC, event_data);
done:
diff --git a/drivers/soc/qcom/llcc-sdm670.c b/drivers/soc/qcom/llcc-sdm670.c
index 68ad755..494b93b 100644
--- a/drivers/soc/qcom/llcc-sdm670.c
+++ b/drivers/soc/qcom/llcc-sdm670.c
@@ -57,13 +57,14 @@
}
static struct llcc_slice_config sdm670_data[] = {
- SCT_ENTRY("cpuss", 1, 1, 512, 1, 1, 0xF, 0x0, 0, 0, 0, 1, 1),
- SCT_ENTRY("vidsc0", 2, 2, 64, 2, 1, 0xF, 0x0, 0, 0, 0, 1, 0),
- SCT_ENTRY("vidsc1", 3, 3, 64, 2, 1, 0xF, 0x0, 0, 0, 0, 1, 0),
- SCT_ENTRY("rotator", 4, 4, 384, 2, 1, 0xF, 0x0, 0, 0, 0, 1, 0),
- SCT_ENTRY("modem", 8, 8, 512, 1, 0, 0xF, 0x0, 0, 0, 0, 1, 0),
- SCT_ENTRY("gpuhtw", 11, 11, 128, 1, 1, 0xF, 0x0, 0, 0, 0, 1, 0),
- SCT_ENTRY("gpu", 12, 12, 384, 1, 1, 0xF, 0x0, 0, 0, 0, 1, 0),
+ SCT_ENTRY("cpuss", 1, 1, 512, 1, 0, 0xF, 0x0, 0, 0, 1, 1, 1),
+ SCT_ENTRY("rotator", 4, 4, 384, 2, 1, 0x0, 0xE, 2, 0, 1, 1, 0),
+ SCT_ENTRY("voice", 5, 5, 512, 1, 0, 0xF, 0x0, 0, 0, 1, 1, 0),
+ SCT_ENTRY("audio", 6, 6, 512, 1, 0, 0xF, 0x0, 0, 0, 1, 1, 0),
+ SCT_ENTRY("modem", 8, 8, 512, 1, 0, 0xF, 0x0, 0, 0, 1, 1, 0),
+ SCT_ENTRY("gpu", 12, 12, 384, 1, 1, 0x0, 0x0, 0, 0, 1, 1, 0),
+ SCT_ENTRY("mmuhwt", 13, 13, 512, 1, 0, 0x0, 0x8, 0, 0, 1, 0, 1),
+ SCT_ENTRY("audiohw", 22, 22, 512, 1, 1, 0xF, 0x0, 0, 0, 1, 1, 0),
};
static int sdm670_qcom_llcc_probe(struct platform_device *pdev)
diff --git a/drivers/soc/qcom/llcc_events.h b/drivers/soc/qcom/llcc_events.h
new file mode 100644
index 0000000..28f4594
--- /dev/null
+++ b/drivers/soc/qcom/llcc_events.h
@@ -0,0 +1,301 @@
+/* Copyright (c) 2017, 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 _SOC_QCOM_LLCC_EVENTS_H_
+#define _SOC_QCOM_LLCC_EVENTS_H_
+
+enum event_port_select {
+ EVENT_PORT_FEAC,
+ EVENT_PORT_FERC,
+ EVENT_PORT_FEWC,
+ EVENT_PORT_BEAC,
+ EVENT_PORT_BERC,
+ EVENT_PORT_TRP,
+ EVENT_PORT_DRP,
+ EVENT_PORT_PMGR,
+ EVENT_PORT_TENURE,
+ EVENT_PORT_TLAT,
+};
+
+enum feac_events {
+ FEAC_ANY_ACCESS,
+ FEAC_READ_INCR,
+ FEAC_WRITE_INCR,
+ FEAC_WRITE_ORDERED,
+ FEAC_READE_EXCL,
+ FEAC_WRITE_EXCL,
+ FEAC_CMO,
+ FEAC_CMO_CLEAN,
+ FEAC_CMO_INVAL,
+ FEAC_CMO_CLEANINVAL,
+ FEAC_CMO_DCPLD,
+ FEAC_READ_NOALLOC,
+ FEAC_WRITE_NOALLOC,
+ FEAC_PREFETCH,
+ FEAC_RD_BYTES,
+ FEAC_RD_BEATS,
+ FEAC_WR_BYTES,
+ FEAC_WR_BEATS,
+ FEAC_FC_READ,
+ FEAC_EWD_ACCESS,
+ FEAC_TCM_ACCESS,
+ FEAC_GM_HIT,
+ FEAC_GM_MISS,
+ FEAC_GM_UNAVAILABLE,
+ FEAC_XPU_ERROR,
+ FEAC_READ_HAZARD,
+ FEAC_WRITE_HAZARD,
+ FEAC_GRANULE_READ,
+ FEAC_GRANULE_WRITE,
+ FEAC_RIFB_ALLOC,
+ FEAC_WIFB_ALLOC,
+ FEAC_RIFB_DEALLOC,
+ FEAC_WIFB_DEALLOC,
+ FEAC_RESERVED,
+ FEAC_RESERVED1,
+ FEAC_FEAC2TRP_LP_TX,
+ FEAC_TRP_LP_BUSY,
+ FEAC_FEAC2TRP_HP_TX,
+ FEAC_TRP_HP_BUSY,
+ FEAC_FEAC2FEWC_TX,
+ FEAC_BEAC_LP_BUSY,
+ FEAC_BEAC_HP_BUSY,
+ FEAC_RIFB_FULL,
+ FEAC_WIFB_FULL,
+ FEAC_RD_CRDT_TX,
+ FEAC_WR_CRDT_TX,
+ FEAC_PROMOTION,
+ FEAC_FEAC2TRP_LP_PRESSURE,
+ FEAC_FEAC2TRP_HP_PRESSURE,
+ FEAC_FEAC2FEWC_PRESSURE,
+ FEAC_FEAC2BEAC_LP_PRESSURE,
+ FEAC_FEAC2BEAC_HP_PRESSURE,
+ FEAC_WR_THROUGH,
+};
+
+enum ferc_events {
+ FERC_BERC_CMD,
+ FERC_BERC_BEAT,
+ FERC_DRP_CMD,
+ FERC_DRP_BEAT,
+ FERC_RD_CTRL_RSP_TX,
+ FERC_WR_CTRL_RSP_TX,
+ FERC_RD_DATA_TX,
+ FERC_MISS_TRUMPS_HIT,
+ FERC_HIT_TRUMPS_WRSP,
+ FERC_RD_INTRA_RSP_IDLE,
+};
+
+enum fewc_events {
+ FEWC_WR_CMD,
+ FEWC_WR_DATA_BEAT,
+ FEWC_WR_LAST,
+ FEWC_WBUF_DEALLOC,
+ FEWC_WR_HIT,
+ FEWC_WR_MISS,
+ FEWC_NC_RMW,
+ FEWC_WR_DOWNGRADE,
+ FEWC_BEAC_WR_CMD,
+ FEWC_BEAC_WR_BEAT,
+ FEWC_BEAC_RD_CMD,
+ FEWC_BERC_FILL_BEAT,
+ FEWC_DRP_WR_CMD,
+ FEWC_DRP_WR_BEAT,
+ FEWC_DRP_RD_BEAT,
+ FEWC_TRP_TAG_LOOKUP,
+ FEWC_TRP_TAG_UPDATE,
+ FEWC_TRP_UNSTALL,
+ FEWC_WBUFFS_FULL,
+ FEWC_DRP_BUSY,
+ FEWC_BEAC_WR_BUSY,
+ FEWC_BEAC_RD_BUSY,
+ FEWC_TRP_TAG_LOOKUP_BUSY,
+ FEWC_TRP_TAG_UPDATE_BUSY,
+ FEWC_C_RMW,
+ FEWC_NC_ALLOC_RMW,
+ FEWC_NC_NO_ALLOC_RMW,
+ FEWC_NC_RMW_DEALLOC,
+ FEWC_C_RMW_DEALLOC,
+ FEWC_STALLED_BY_EVICT,
+};
+
+enum beac_events {
+ BEAC_RD_TX,
+ BEAC_WR_TX,
+ BEAC_RD_GRANULE,
+ BEAC_WR_GRANULE,
+ BEAC_WR_BEAT_TX,
+ BEAC_RD_CRDT_ZERO,
+ BEAC_WR_CRDT_ZERO,
+ BEAC_WDATA_CRDT_ZERO,
+ BEAC_IFCMD_CRDT_ZERO,
+ BEAC_IFWDATA_CRDT_ZERO,
+ BEAC_PCT_ENTRY_ALLOC,
+ BEAC_PCT_ENTRY_FREE,
+ BEAC_PCT_FULL,
+ BEAC_RD_PROMOTION_TX,
+ BEAC_WR_PROMOTION_TX,
+ BEAC_RD_PRESSURE_TX,
+ BEAC_WR_PRESSURE_TX,
+};
+
+enum berc_events {
+ BERC_RD_CMD,
+ BERC_ERROR_CMD,
+ BERC_PCT_ENTRY_DEALLOC,
+ BERC_RD_RSP_RX,
+ BERC_RD_RSP_BEAT_RX,
+ BERC_RD_LA_RX,
+ BERC_UNSTALL_RX,
+ BERC_TX_RD_CMD,
+ BERC_TX_ERR_CMD,
+ BERC_TX_RD_BEAT,
+ BERC_TX_ERR_BEAT,
+ BERC_RESERVED,
+ BERC_RESERVED1,
+ BERC_CMO_RX,
+ BERC_CMO_TX,
+ BERC_DRP_WR_TX,
+ BERC_DRP_WR_BEAT_TX,
+ BERC_FEWC_WR_TX,
+ BERC_FEWC_WR_BEAT_TX,
+ BERC_LBUFFS_FULL,
+ BERC_DRP_BUSY,
+ BERC_FEWC_BUSY,
+ BERC_LBUFF_STALLED,
+};
+
+enum trp_events {
+ TRP_ANY_ACCESS,
+ TRP_INCR_RD,
+ TRP_INCR_WR,
+ TRP_ANY_HIT,
+ TRP_RD_HIT,
+ TRP_WR_HIT,
+ TRP_RD_MISS,
+ TRP_WR_MISS,
+ TRP_RD_HIT_MISS,
+ TRP_WR_HIT_MISS,
+ TRP_EVICT,
+ TRP_GRANULE_EVICT,
+ TRP_RD_EVICT,
+ TRP_WR_EVICT,
+ TRP_LINE_FILL,
+ TRP_GRANULE_FILL,
+ TRP_WSC_WRITE,
+ TRP_WSC_EVICT,
+ TRP_SUBCACHE_ACT,
+ TRP_SUBCACHE_DEACT,
+ TRP_RD_DEACTIVE_SUBCACHE,
+ TRP_WR_DEACTIVE_SUBCACHE,
+ TRP_INVALID_LINE_ALLOC,
+ TRP_DEACTIVE_LINE_ALLOC,
+ TRP_SELF_EVICTION_ALLOC,
+ TRP_UC_SUBCACHE_ALLOC,
+ TRP_FC_SELF_EVICTION_ALLOC,
+ TRP_LP_SUBCACHE_VICTIM,
+ TRP_OC_SUBCACHE_VICTIM,
+ TRP_MRU_ROLLOVER,
+ TRP_NC_DOWNGRADE,
+ TRP_TAGRAM_CORR_ERR,
+ TRP_TAGRAM_UNCORR_ERR,
+ TRP_RD_MISS_FC,
+ TRP_CPU_WRITE_EWD_LINE,
+ TRP_CLIENT_WRITE_EWD_LINE,
+ TRP_CLIENT_READ_EWD_LINE,
+ TRP_CMO_I_EWD_LINE,
+ TRP_CMO_I_DIRTY_LINE,
+ TRP_DRP_RD_NOTIFICATION,
+ TRP_DRP_WR_NOTIFICATION,
+ TRP_LINEFILL_TAG_UPDATE,
+ TRP_FEWC_TAG_UPDATE,
+ TRP_ET_FULL,
+ TRP_NAWT_FULL,
+ TRP_HITQ_FULL,
+ TRP_ET_ALLOC,
+ TRP_ET_DEALLOC,
+ TRP_NAWT_ALLOC,
+ TRP_NAWT_DEALLOC,
+ TRP_RD_REPLAY,
+ TRP_WR_ECC_RD,
+ TRP_ET_LP_FULL,
+ TRP_ET_HP_FULL,
+ TRP_SOEH,
+};
+
+enum drp_events {
+ DRP_TRP_RD_NOTIFICATION,
+ DRP_TRP_WR_NOTIFICATION,
+ DRP_BIST_WR_NOTIFICATION,
+ DRP_DRIE_WR_NOTIFICATION,
+ DRP_ECC_CORR_ERR,
+ DRP_ECC_UNCORR_ERR,
+ DRP_FERC_RD_TX,
+ DRP_FEWC_RD_TX,
+ DRP_EVICT_LINE_TX,
+ DRP_EVICT_GRANULE_TX,
+ DRP_BIST_TX,
+ DRP_FERC_RD_BEAT,
+ DRP_FEWC_RD_BEAT,
+ DRP_BIST_RD_BEAT,
+ DRP_EVICT_RD_BEAT,
+ DRP_BERC_WR_BEAT,
+ DRP_FEWC_WR_BEAT,
+ DRP_BIST_WR_BEAT,
+ DRP_DRIE_WR_BEAT,
+ DRP_BERC_UNSTALL,
+ DRP_FEWC_UNSTALL,
+ DRP_LB_RD,
+ DRP_LB_WR,
+ DRP_BANK_CONFLICT,
+ DRP_FILL_TRUMPS_RD,
+ DRP_RD_TRUMPS_WR,
+ DRP_LB_SLP_RET,
+ DRP_LB_SLP_NRET,
+ DRP_LB_WAKEUP,
+ DRP_TRP_EARLY_WAKEUP,
+ DRP_PCB_IDLE,
+ DRP_EVICT_RDFIFO_FULL,
+ DRP_FEWC_RDFIFO_FULL,
+ DRP_FERC_RDFIFO_FULL,
+ DRP_FERC_RD,
+ DRP_FEWC_RD,
+ DRP_LINE_EVICT,
+ DRP_GRANULE_EVICT,
+ DRP_BIST_RD,
+ DRP_FEWC_WR,
+ DRP_LINE_FILL,
+ DRP_GRANULE_FILL,
+ DRP_BIST_WR,
+ DRP_DRIE_WR,
+};
+
+enum pmgr_events {
+ PMGR_Q_RUN_STATE,
+ PMGR_Q_DENIED_STATE,
+ PMGR_Q_STOPEED_TO_Q_RUN,
+ PMGR_Q_RUN_TO_Q_FENCED,
+ PMGR_Q_RUN_TO_Q_DENIED,
+ PMGR_Q_DENIED_TO_Q_RUN,
+ PMGR_Q_FENCED_TO_Q_STOPPED,
+ PMGR_Q_FENCED_TO_Q_DENIED,
+};
+
+enum filter_type {
+ SCID,
+ MID,
+ PROFILING_TAG,
+ WAY_ID,
+ UNKNOWN,
+};
+
+#endif /* _SOC_QCOM_LLCC_EVENTS_H_ */
diff --git a/drivers/soc/qcom/llcc_perfmon.c b/drivers/soc/qcom/llcc_perfmon.c
new file mode 100644
index 0000000..39276a9
--- /dev/null
+++ b/drivers/soc/qcom/llcc_perfmon.c
@@ -0,0 +1,1203 @@
+/* Copyright (c) 2017, 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) KBUILD_MODNAME ": " fmt
+
+#include <linux/kernel.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+#include <linux/mutex.h>
+#include <linux/bitmap.h>
+#include <linux/bitops.h>
+#include <linux/slab.h>
+#include <linux/io.h>
+#include <linux/err.h>
+#include <linux/hrtimer.h>
+#include <linux/mfd/syscon.h>
+#include <linux/regmap.h>
+#include <linux/soc/qcom/llcc-qcom.h>
+#include <linux/module.h>
+#include "llcc_events.h"
+#include "llcc_perfmon.h"
+
+#define LLCC_PERFMON_NAME "llcc_perfmon"
+#define LLCC_PERFMON_COUNTER_MAX 16
+#define MAX_NUMBER_OF_PORTS 8
+#define NUM_CHANNELS 16
+#define MAX_STRING_SIZE 20
+#define DELIM_CHAR " "
+
+/**
+ * struct llcc_perfmon_counter_map - llcc perfmon counter map info
+ * @port_sel: Port selected for configured counter
+ * @event_sel: Event selected for configured counter
+ * @counter_dump: Cumulative counter dump
+ */
+struct llcc_perfmon_counter_map {
+ unsigned int port_sel;
+ unsigned int event_sel;
+ unsigned long long counter_dump;
+};
+
+struct llcc_perfmon_private;
+/**
+ * struct event_port_ops - event port operation
+ * @event_config: Counter config support for port & event
+ * @event_enable: Counter enable support for port
+ * @event_filter_config: Port filter config support
+ */
+struct event_port_ops {
+ void (*event_config)(struct llcc_perfmon_private *,
+ unsigned int, unsigned int, bool);
+ void (*event_enable)(struct llcc_perfmon_private *, bool);
+ void (*event_filter_config)(struct llcc_perfmon_private *,
+ enum filter_type, unsigned long, bool);
+};
+
+/**
+ * struct llcc_perfmon_private - llcc perfmon private
+ * @llcc_map: llcc register address space map
+ * @broadcast_off: Offset of llcc broadcast address space
+ * @bank_off: Offset of llcc banks
+ * @num_banks: Number of banks supported
+ * @port_ops: struct event_port_ops
+ * @configured: Mapping of configured event counters
+ * @configured_counters:
+ * Count of configured counters.
+ * @enables_port: Port enabled for perfmon configuration
+ * @filtered_ports: Port filter enabled
+ * @port_configd: Number of perfmon port configuration supported
+ * @mutex: mutex to protect this structure
+ * @hrtimer: hrtimer instance for timer functionality
+ * @expires: timer expire time in nano seconds
+ */
+struct llcc_perfmon_private {
+ struct regmap *llcc_map;
+ unsigned int broadcast_off;
+ unsigned int bank_off[NUM_CHANNELS];
+ unsigned int num_banks;
+ struct event_port_ops *port_ops[MAX_NUMBER_OF_PORTS];
+ struct llcc_perfmon_counter_map configured[LLCC_PERFMON_COUNTER_MAX];
+ unsigned int configured_counters;
+ unsigned int enables_port;
+ unsigned int filtered_ports;
+ unsigned int port_configd;
+ struct mutex mutex;
+ struct hrtimer hrtimer;
+ ktime_t expires;
+};
+
+static inline void llcc_bcast_write(struct llcc_perfmon_private *llcc_priv,
+ unsigned int offset, uint32_t val)
+{
+ regmap_write(llcc_priv->llcc_map, llcc_priv->broadcast_off + offset,
+ val);
+}
+
+static inline void llcc_bcast_read(struct llcc_perfmon_private *llcc_priv,
+ unsigned int offset, uint32_t *val)
+{
+ regmap_read(llcc_priv->llcc_map, llcc_priv->broadcast_off + offset,
+ val);
+}
+
+static void llcc_bcast_modify(struct llcc_perfmon_private *llcc_priv,
+ unsigned int offset, uint32_t val, uint32_t mask)
+{
+ uint32_t readval;
+
+ llcc_bcast_read(llcc_priv, offset, &readval);
+ readval &= ~mask;
+ readval |= val & mask;
+ llcc_bcast_write(llcc_priv, offset, readval);
+}
+
+static void perfmon_counter_dump(struct llcc_perfmon_private *llcc_priv)
+{
+ uint32_t val;
+ unsigned int i, j;
+ unsigned long long total;
+
+ llcc_bcast_write(llcc_priv, PERFMON_DUMP, MONITOR_DUMP);
+ for (i = 0; i < llcc_priv->configured_counters - 1; i++) {
+ total = 0;
+ for (j = 0; j < llcc_priv->num_banks; j++) {
+ regmap_read(llcc_priv->llcc_map, llcc_priv->bank_off[j]
+ + LLCC_COUNTER_n_VALUE(i), &val);
+ total += val;
+ }
+
+ llcc_priv->configured[i].counter_dump += total;
+ }
+
+ total = 0;
+ for (j = 0; j < llcc_priv->num_banks; j++) {
+ regmap_read(llcc_priv->llcc_map, llcc_priv->bank_off[j] +
+ LLCC_COUNTER_n_VALUE(i), &val);
+ total += val;
+ }
+
+ llcc_priv->configured[i].counter_dump += total;
+}
+
+static ssize_t perfmon_counter_dump_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct llcc_perfmon_private *llcc_priv = dev_get_drvdata(dev);
+ uint32_t val;
+ unsigned int i, j;
+ unsigned long long total;
+ ssize_t cnt = 0, print;
+
+ if (llcc_priv->configured_counters == 0) {
+ pr_err("counters not configured\n");
+ return cnt;
+ }
+
+ if (llcc_priv->expires.tv64) {
+ perfmon_counter_dump(llcc_priv);
+ for (i = 0; i < llcc_priv->configured_counters - 1; i++) {
+ print = snprintf(buf, MAX_STRING_SIZE, "Port %02d,",
+ llcc_priv->configured[i].port_sel);
+ buf += print;
+ cnt += print;
+ print = snprintf(buf, MAX_STRING_SIZE, "Event %02d,",
+ llcc_priv->configured[i].event_sel);
+ buf += print;
+ cnt += print;
+
+ print = snprintf(buf, MAX_STRING_SIZE, "0x%016llx\n",
+ llcc_priv->configured[i].counter_dump);
+ buf += print;
+ cnt += print;
+ llcc_priv->configured[i].counter_dump = 0;
+ }
+
+ print = snprintf(buf, MAX_STRING_SIZE, "CYCLE COUNT, ,");
+ buf += print;
+ cnt += print;
+ print = snprintf(buf, MAX_STRING_SIZE, "0x%016llx\n",
+ llcc_priv->configured[i].counter_dump);
+ buf += print;
+ cnt += print;
+ llcc_priv->configured[i].counter_dump = 0;
+ hrtimer_forward_now(&llcc_priv->hrtimer, llcc_priv->expires);
+ } else {
+ llcc_bcast_write(llcc_priv, PERFMON_DUMP, MONITOR_DUMP);
+
+ for (i = 0; i < llcc_priv->configured_counters - 1; i++) {
+ print = snprintf(buf, MAX_STRING_SIZE, "Port %02d,",
+ llcc_priv->configured[i].port_sel);
+ buf += print;
+ cnt += print;
+ print = snprintf(buf, MAX_STRING_SIZE, "Event %02d,",
+ llcc_priv->configured[i].event_sel);
+ buf += print;
+ cnt += print;
+ total = 0;
+ for (j = 0; j < llcc_priv->num_banks; j++) {
+ regmap_read(llcc_priv->llcc_map,
+ llcc_priv->bank_off[j]
+ + LLCC_COUNTER_n_VALUE(i),
+ &val);
+ print = snprintf(buf, MAX_STRING_SIZE,
+ "0x%08x,", val);
+ buf += print;
+ cnt += print;
+ total += val;
+ }
+
+ print = snprintf(buf, MAX_STRING_SIZE, "0x%09llx\n",
+ total);
+ buf += print;
+ cnt += print;
+ }
+
+ print = snprintf(buf, MAX_STRING_SIZE, "CYCLE COUNT, ,");
+ buf += print;
+ cnt += print;
+ total = 0;
+ for (j = 0; j < llcc_priv->num_banks; j++) {
+ regmap_read(llcc_priv->llcc_map,
+ llcc_priv->bank_off[j] +
+ LLCC_COUNTER_n_VALUE(i), &val);
+ print = snprintf(buf, MAX_STRING_SIZE, "0x%08x,", val);
+ buf += print;
+ cnt += print;
+ total += val;
+ }
+
+ print = snprintf(buf, MAX_STRING_SIZE, "0x%09llx\n", total);
+ buf += print;
+ cnt += print;
+ }
+
+ return cnt;
+}
+
+static ssize_t perfmon_configure_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct llcc_perfmon_private *llcc_priv = dev_get_drvdata(dev);
+ struct event_port_ops *port_ops;
+ unsigned int j;
+ unsigned long port_sel, event_sel;
+ uint32_t val;
+ char *token, *delim = DELIM_CHAR;
+
+ mutex_lock(&llcc_priv->mutex);
+ if (llcc_priv->configured_counters) {
+ pr_err("Counters configured already, remove & try again\n");
+ mutex_unlock(&llcc_priv->mutex);
+ return -EINVAL;
+ }
+
+ llcc_priv->configured_counters = 0;
+ j = 0;
+ token = strsep((char **)&buf, delim);
+
+ while (token != NULL) {
+ if (kstrtoul(token, 10, &port_sel))
+ break;
+
+ if (port_sel >= llcc_priv->port_configd)
+ break;
+
+ token = strsep((char **)&buf, delim);
+ if (token == NULL)
+ break;
+
+ if (kstrtoul(token, 10, &event_sel))
+ break;
+
+ token = strsep((char **)&buf, delim);
+ if (event_sel >= EVENT_NUM_MAX) {
+ pr_err("unsupported event num %ld\n", event_sel);
+ continue;
+ }
+
+ llcc_priv->configured[j].port_sel = port_sel;
+ llcc_priv->configured[j].event_sel = event_sel;
+ port_ops = llcc_priv->port_ops[port_sel];
+ pr_info("configured event %ld counter %d on port %ld\n",
+ event_sel, j, port_sel);
+ port_ops->event_config(llcc_priv, event_sel, j++, true);
+ if (!(llcc_priv->enables_port & (1 << port_sel)))
+ if (port_ops->event_enable)
+ port_ops->event_enable(llcc_priv, true);
+
+ llcc_priv->enables_port |= (1 << port_sel);
+
+ /* Last perfmon counter for cycle counter */
+ if (llcc_priv->configured_counters++ ==
+ (LLCC_PERFMON_COUNTER_MAX - 2))
+ break;
+ }
+
+ /* configure clock event */
+ val = COUNT_CLOCK_EVENT | CLEAR_ON_ENABLE | CLEAR_ON_DUMP;
+ llcc_bcast_write(llcc_priv, PERFMON_COUNTER_n_CONFIG(j), val);
+
+ llcc_priv->configured_counters++;
+ mutex_unlock(&llcc_priv->mutex);
+ return count;
+}
+
+static ssize_t perfmon_remove_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct llcc_perfmon_private *llcc_priv = dev_get_drvdata(dev);
+ struct event_port_ops *port_ops;
+ unsigned int j, counter_remove = 0;
+ unsigned long port_sel, event_sel;
+ char *token, *delim = DELIM_CHAR;
+
+ mutex_lock(&llcc_priv->mutex);
+ if (!llcc_priv->configured_counters) {
+ pr_err("Counters not configured\n");
+ mutex_unlock(&llcc_priv->mutex);
+ return -EINVAL;
+ }
+
+ j = 0;
+ token = strsep((char **)&buf, delim);
+
+ while (token != NULL) {
+ if (kstrtoul(token, 10, &port_sel))
+ break;
+
+ if (port_sel >= llcc_priv->port_configd)
+ break;
+
+ token = strsep((char **)&buf, delim);
+ if (token == NULL)
+ break;
+
+ if (kstrtoul(token, 10, &event_sel))
+ break;
+
+ token = strsep((char **)&buf, delim);
+ if (event_sel >= EVENT_NUM_MAX) {
+ pr_err("unsupported event num %ld\n", event_sel);
+ continue;
+ }
+
+ /* put dummy values */
+ llcc_priv->configured[j].port_sel = MAX_NUMBER_OF_PORTS;
+ llcc_priv->configured[j].event_sel = 100;
+ port_ops = llcc_priv->port_ops[port_sel];
+ pr_info("Removed event %ld counter %d from port %ld\n",
+ event_sel, j, port_sel);
+
+ port_ops->event_config(llcc_priv, event_sel, j++, false);
+ if (llcc_priv->enables_port & (1 << port_sel))
+ if (port_ops->event_enable)
+ port_ops->event_enable(llcc_priv, false);
+
+ llcc_priv->enables_port &= ~(1 << port_sel);
+
+ /* Last perfmon counter for cycle counter */
+ if (counter_remove++ == (LLCC_PERFMON_COUNTER_MAX - 2))
+ break;
+ }
+
+ /* remove clock event */
+ llcc_bcast_write(llcc_priv, PERFMON_COUNTER_n_CONFIG(j), 0);
+
+ llcc_priv->configured_counters = 0;
+ mutex_unlock(&llcc_priv->mutex);
+ return count;
+}
+
+static enum filter_type find_filter_type(char *filter)
+{
+ enum filter_type ret = UNKNOWN;
+
+ if (!strcmp(filter, "SCID"))
+ ret = SCID;
+ else if (!strcmp(filter, "MID"))
+ ret = MID;
+ else if (!strcmp(filter, "PROFILING_TAG"))
+ ret = PROFILING_TAG;
+ else if (!strcmp(filter, "WAY_ID"))
+ ret = WAY_ID;
+
+ return ret;
+}
+
+static ssize_t perfmon_filter_config_store(struct device *dev,
+ struct device_attribute *attr, const char *buf,
+ size_t count)
+{
+ struct llcc_perfmon_private *llcc_priv = dev_get_drvdata(dev);
+ unsigned long port, val;
+ struct event_port_ops *port_ops;
+ char *token, *delim = DELIM_CHAR;
+ enum filter_type filter = UNKNOWN;
+
+ mutex_lock(&llcc_priv->mutex);
+
+ token = strsep((char **)&buf, delim);
+ if (token != NULL)
+ filter = find_filter_type(token);
+
+ if (filter == UNKNOWN) {
+ pr_err("filter configuration failed, Unsupported filter\n");
+ goto filter_config_free;
+ }
+
+ token = strsep((char **)&buf, delim);
+ if (token == NULL) {
+ pr_err("filter configuration failed, Wrong input\n");
+ goto filter_config_free;
+ }
+
+ if (kstrtoul(token, 10, &val)) {
+ pr_err("filter configuration failed, Wrong input\n");
+ goto filter_config_free;
+ }
+
+ if ((filter == SCID) && (val >= SCID_MAX)) {
+ pr_err("filter configuration failed, SCID above MAX value\n");
+ goto filter_config_free;
+ }
+
+
+ while (token != NULL) {
+ token = strsep((char **)&buf, delim);
+ if (token == NULL)
+ break;
+
+ if (kstrtoul(token, 10, &port))
+ break;
+
+ llcc_priv->filtered_ports |= 1 << port;
+ port_ops = llcc_priv->port_ops[port];
+ if (port_ops->event_filter_config)
+ port_ops->event_filter_config(llcc_priv, filter, val,
+ true);
+ }
+
+ mutex_unlock(&llcc_priv->mutex);
+ return count;
+
+filter_config_free:
+ mutex_unlock(&llcc_priv->mutex);
+ return -EINVAL;
+}
+
+static ssize_t perfmon_filter_remove_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct llcc_perfmon_private *llcc_priv = dev_get_drvdata(dev);
+ struct event_port_ops *port_ops;
+ unsigned long port, val;
+ char *token, *delim = DELIM_CHAR;
+ enum filter_type filter = UNKNOWN;
+
+ mutex_lock(&llcc_priv->mutex);
+ token = strsep((char **)&buf, delim);
+ if (token != NULL)
+ filter = find_filter_type(token);
+
+ if (filter == UNKNOWN) {
+ pr_err("filter configuration failed, Unsupported filter\n");
+ goto filter_remove_free;
+ }
+
+ token = strsep((char **)&buf, delim);
+ if (token == NULL) {
+ pr_err("filter configuration failed, Wrong input\n");
+ goto filter_remove_free;
+ }
+
+ if (kstrtoul(token, 10, &val)) {
+ pr_err("filter configuration failed, Wrong input\n");
+ goto filter_remove_free;
+ }
+
+ if ((filter == SCID) && (val >= SCID_MAX)) {
+ pr_err("filter configuration failed, SCID above MAX value\n");
+ goto filter_remove_free;
+ }
+
+ while (token != NULL) {
+ token = strsep((char **)&buf, delim);
+ if (token == NULL)
+ break;
+
+ if (kstrtoul(token, 10, &port))
+ break;
+
+ llcc_priv->filtered_ports &= ~(1 << port);
+ port_ops = llcc_priv->port_ops[port];
+ if (port_ops->event_filter_config)
+ port_ops->event_filter_config(llcc_priv, filter, val,
+ false);
+ }
+
+ mutex_unlock(&llcc_priv->mutex);
+ return count;
+
+filter_remove_free:
+ mutex_unlock(&llcc_priv->mutex);
+ return count;
+}
+
+static ssize_t perfmon_start_store(struct device *dev,
+ struct device_attribute *attr, const char *buf,
+ size_t count)
+{
+ struct llcc_perfmon_private *llcc_priv = dev_get_drvdata(dev);
+ uint32_t val = 0, mask;
+ unsigned long start;
+
+ if (kstrtoul(buf, 10, &start))
+ return -EINVAL;
+
+ mutex_lock(&llcc_priv->mutex);
+ if (start) {
+ if (!llcc_priv->configured_counters)
+ pr_err("start failed. perfmon not configured\n");
+
+ val = MANUAL_MODE | MONITOR_EN;
+ if (llcc_priv->expires.tv64) {
+ if (hrtimer_is_queued(&llcc_priv->hrtimer))
+ hrtimer_forward_now(&llcc_priv->hrtimer,
+ llcc_priv->expires);
+ else
+ hrtimer_start(&llcc_priv->hrtimer,
+ llcc_priv->expires,
+ HRTIMER_MODE_REL_PINNED);
+ }
+
+ } else {
+ if (llcc_priv->expires.tv64)
+ hrtimer_cancel(&llcc_priv->hrtimer);
+
+ if (!llcc_priv->configured_counters)
+ pr_err("stop failed. perfmon not configured\n");
+ }
+
+ mask = PERFMON_MODE_MONITOR_MODE_MASK | PERFMON_MODE_MONITOR_EN_MASK;
+ llcc_bcast_modify(llcc_priv, PERFMON_MODE, val, mask);
+
+
+ mutex_unlock(&llcc_priv->mutex);
+ return count;
+}
+
+static ssize_t perfmon_ns_periodic_dump_store(struct device *dev,
+ struct device_attribute *attr, const char *buf,
+ size_t count)
+{
+ struct llcc_perfmon_private *llcc_priv = dev_get_drvdata(dev);
+
+ if (kstrtos64(buf, 10, &llcc_priv->expires.tv64))
+ return -EINVAL;
+
+ mutex_lock(&llcc_priv->mutex);
+ if (!llcc_priv->expires.tv64) {
+ hrtimer_cancel(&llcc_priv->hrtimer);
+ mutex_unlock(&llcc_priv->mutex);
+ return count;
+ }
+
+ if (hrtimer_is_queued(&llcc_priv->hrtimer))
+ hrtimer_forward_now(&llcc_priv->hrtimer, llcc_priv->expires);
+ else
+ hrtimer_start(&llcc_priv->hrtimer, llcc_priv->expires,
+ HRTIMER_MODE_REL_PINNED);
+
+ mutex_unlock(&llcc_priv->mutex);
+ return count;
+}
+
+static ssize_t perfmon_scid_status_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct llcc_perfmon_private *llcc_priv = dev_get_drvdata(dev);
+ uint32_t val;
+ unsigned int i, offset;
+ ssize_t cnt = 0, print;
+
+ for (i = 0; i < SCID_MAX; i++) {
+ offset = TRP_SCID_n_STATUS(i);
+ llcc_bcast_read(llcc_priv, offset, &val);
+ if (val & TRP_SCID_STATUS_ACTIVE_MASK)
+ print = snprintf(buf, MAX_STRING_SIZE, "SCID %02d %10s",
+ i, "ACTIVE");
+ else
+ print = snprintf(buf, MAX_STRING_SIZE, "SCID %02d %10s",
+ i, "DEACTIVE");
+
+ buf += print;
+ cnt += print;
+
+ val = (val & TRP_SCID_STATUS_CURRENT_CAP_MASK)
+ >> TRP_SCID_STATUS_CURRENT_CAP_SHIFT;
+ print = snprintf(buf, MAX_STRING_SIZE, ",0x%08x\n", val);
+ buf += print;
+ cnt += print;
+ }
+
+ return cnt;
+}
+
+static DEVICE_ATTR_RO(perfmon_counter_dump);
+static DEVICE_ATTR_WO(perfmon_configure);
+static DEVICE_ATTR_WO(perfmon_remove);
+static DEVICE_ATTR_WO(perfmon_filter_config);
+static DEVICE_ATTR_WO(perfmon_filter_remove);
+static DEVICE_ATTR_WO(perfmon_start);
+static DEVICE_ATTR_RO(perfmon_scid_status);
+static DEVICE_ATTR_WO(perfmon_ns_periodic_dump);
+
+static struct attribute *llcc_perfmon_attrs[] = {
+ &dev_attr_perfmon_counter_dump.attr,
+ &dev_attr_perfmon_configure.attr,
+ &dev_attr_perfmon_remove.attr,
+ &dev_attr_perfmon_filter_config.attr,
+ &dev_attr_perfmon_filter_remove.attr,
+ &dev_attr_perfmon_start.attr,
+ &dev_attr_perfmon_scid_status.attr,
+ &dev_attr_perfmon_ns_periodic_dump.attr,
+ NULL,
+};
+
+static struct attribute_group llcc_perfmon_group = {
+ .attrs = llcc_perfmon_attrs,
+};
+
+static void perfmon_counter_config(struct llcc_perfmon_private *llcc_priv,
+ unsigned int port, unsigned int event_counter_num)
+{
+ uint32_t val;
+
+ val = (port & PERFMON_PORT_SELECT_MASK) |
+ ((event_counter_num << EVENT_SELECT_SHIFT) &
+ PERFMON_EVENT_SELECT_MASK) | CLEAR_ON_ENABLE | CLEAR_ON_DUMP;
+ llcc_bcast_write(llcc_priv, PERFMON_COUNTER_n_CONFIG(event_counter_num),
+ val);
+}
+
+static void feac_event_config(struct llcc_perfmon_private *llcc_priv,
+ unsigned int event_type, unsigned int event_counter_num,
+ bool enable)
+{
+ uint32_t val = 0, mask, counter_num = 0;
+
+ mask = EVENT_SEL_MASK;
+ if (llcc_priv->filtered_ports & (1 << EVENT_PORT_FEAC))
+ mask |= FILTER_SEL_MASK | FILTER_EN_MASK;
+
+ if (enable) {
+ val = (event_type << EVENT_SEL_SHIFT) & EVENT_SEL_MASK;
+ if (llcc_priv->filtered_ports & (1 << EVENT_PORT_FEAC))
+ val |= (FILTER_0 << FILTER_SEL_SHIFT) | FILTER_EN;
+
+ counter_num = event_counter_num;
+ }
+
+ llcc_bcast_modify(llcc_priv, FEAC_PROF_EVENT_n_CFG(event_counter_num),
+ val, mask);
+ perfmon_counter_config(llcc_priv, EVENT_PORT_FEAC, counter_num);
+}
+
+static void feac_event_enable(struct llcc_perfmon_private *llcc_priv,
+ bool enable)
+{
+ uint32_t val = 0, mask;
+
+ if (enable) {
+ val = (BYTE_SCALING << BYTE_SCALING_SHIFT) |
+ (BEAT_SCALING << BEAT_SCALING_SHIFT) | PROF_EN;
+
+ if (llcc_priv->filtered_ports & (1 << EVENT_PORT_FEAC))
+ val |= (FILTER_0 << FEAC_SCALING_FILTER_SEL_SHIFT) |
+ FEAC_SCALING_FILTER_EN;
+ }
+
+ mask = PROF_CFG_BEAT_SCALING_MASK | PROF_CFG_BYTE_SCALING_MASK
+ | PROF_CFG_EN_MASK;
+
+ if (llcc_priv->filtered_ports & (1 << EVENT_PORT_FEAC))
+ mask |= FEAC_SCALING_FILTER_SEL_MASK |
+ FEAC_SCALING_FILTER_EN_MASK;
+
+ llcc_bcast_modify(llcc_priv, FEAC_PROF_CFG, val, mask);
+}
+
+static void feac_event_filter_config(struct llcc_perfmon_private *llcc_priv,
+ enum filter_type filter, unsigned long match, bool enable)
+{
+ uint32_t val = 0, mask;
+
+ if (filter == SCID) {
+ if (enable)
+ val = (match << SCID_MATCH_SHIFT)
+ | SCID_MASK_MASK;
+
+ mask = SCID_MATCH_MASK | SCID_MASK_MASK;
+ llcc_bcast_modify(llcc_priv, FEAC_PROF_FILTER_0_CFG6, val,
+ mask);
+ } else if (filter == MID) {
+ if (enable)
+ val = (match << MID_MATCH_SHIFT)
+ | MID_MASK_MASK;
+
+ mask = MID_MATCH_MASK | MID_MASK_MASK;
+ llcc_bcast_modify(llcc_priv, FEAC_PROF_FILTER_0_CFG5, val,
+ mask);
+ } else {
+ pr_err("unknown filter/not supported\n");
+ }
+}
+
+static struct event_port_ops feac_port_ops = {
+ .event_config = feac_event_config,
+ .event_enable = feac_event_enable,
+ .event_filter_config = feac_event_filter_config,
+};
+
+static void ferc_event_config(struct llcc_perfmon_private *llcc_priv,
+ unsigned int event_type, unsigned int event_counter_num,
+ bool enable)
+{
+ uint32_t val = 0, mask, counter_num = 0;
+
+ mask = EVENT_SEL_MASK;
+ if (llcc_priv->filtered_ports & (1 << EVENT_PORT_FERC))
+ mask |= FILTER_SEL_MASK | FILTER_EN_MASK;
+
+ if (enable) {
+ val = event_type << EVENT_SEL_SHIFT;
+ if (llcc_priv->filtered_ports & (1 << EVENT_PORT_FERC))
+ val |= (FILTER_0 << FILTER_SEL_SHIFT) | FILTER_EN;
+
+ counter_num = event_counter_num;
+ }
+
+ llcc_bcast_modify(llcc_priv, FERC_PROF_EVENT_n_CFG(event_counter_num),
+ val, mask);
+ perfmon_counter_config(llcc_priv, EVENT_PORT_FERC, counter_num);
+}
+
+static void ferc_event_enable(struct llcc_perfmon_private *llcc_priv,
+ bool enable)
+{
+ uint32_t val = 0, mask;
+
+ if (enable)
+ val = (BYTE_SCALING << BYTE_SCALING_SHIFT) |
+ (BEAT_SCALING << BEAT_SCALING_SHIFT) | PROF_EN;
+
+ mask = PROF_CFG_BEAT_SCALING_MASK | PROF_CFG_BYTE_SCALING_MASK
+ | PROF_CFG_EN_MASK;
+ llcc_bcast_modify(llcc_priv, FERC_PROF_CFG, val, mask);
+}
+
+static void ferc_event_filter_config(struct llcc_perfmon_private *llcc_priv,
+ enum filter_type filter, unsigned long match, bool enable)
+{
+ uint32_t val = 0, mask;
+
+ if (filter != PROFILING_TAG) {
+ pr_err("unknown filter/not supported\n");
+ return;
+ }
+
+ if (enable)
+ val = (match << PROFTAG_MATCH_SHIFT) |
+ FILTER_0_MASK << PROFTAG_MASK_SHIFT;
+
+ mask = PROFTAG_MATCH_MASK | PROFTAG_MASK_MASK;
+ llcc_bcast_modify(llcc_priv, FERC_PROF_FILTER_0_CFG0, val, mask);
+}
+
+static struct event_port_ops ferc_port_ops = {
+ .event_config = ferc_event_config,
+ .event_enable = ferc_event_enable,
+ .event_filter_config = ferc_event_filter_config,
+};
+
+static void fewc_event_config(struct llcc_perfmon_private *llcc_priv,
+ unsigned int event_type, unsigned int event_counter_num,
+ bool enable)
+{
+ uint32_t val = 0, mask, counter_num = 0;
+
+ mask = EVENT_SEL_MASK;
+ if (llcc_priv->filtered_ports & (1 << EVENT_PORT_FEWC))
+ mask |= FILTER_SEL_MASK | FILTER_EN_MASK;
+
+ if (enable) {
+ val = (event_type << EVENT_SEL_SHIFT) & EVENT_SEL_MASK;
+ if (llcc_priv->filtered_ports & (1 << EVENT_PORT_FEWC))
+ val |= (FILTER_0 << FILTER_SEL_SHIFT) | FILTER_EN;
+
+ counter_num = event_counter_num;
+ }
+
+ llcc_bcast_modify(llcc_priv, FEWC_PROF_EVENT_n_CFG(event_counter_num),
+ val, mask);
+ perfmon_counter_config(llcc_priv, EVENT_PORT_FEWC, counter_num);
+}
+
+static void fewc_event_filter_config(struct llcc_perfmon_private *llcc_priv,
+ enum filter_type filter, unsigned long match, bool enable)
+{
+ uint32_t val = 0, mask;
+
+ if (filter != PROFILING_TAG) {
+ pr_err("unknown filter/not supported\n");
+ return;
+ }
+
+ if (enable)
+ val = (match << PROFTAG_MATCH_SHIFT) |
+ FILTER_0_MASK << PROFTAG_MASK_SHIFT;
+
+ mask = PROFTAG_MATCH_MASK | PROFTAG_MASK_MASK;
+ llcc_bcast_modify(llcc_priv, FEWC_PROF_FILTER_0_CFG0, val, mask);
+}
+
+static struct event_port_ops fewc_port_ops = {
+ .event_config = fewc_event_config,
+ .event_filter_config = fewc_event_filter_config,
+};
+
+static void beac_event_config(struct llcc_perfmon_private *llcc_priv,
+ unsigned int event_type, unsigned int event_counter_num,
+ bool enable)
+{
+ uint32_t val = 0, mask, counter_num = 0;
+
+ mask = EVENT_SEL_MASK;
+ if (llcc_priv->filtered_ports & (1 << EVENT_PORT_BEAC))
+ mask |= FILTER_SEL_MASK | FILTER_EN_MASK;
+
+ if (enable) {
+ val = (event_type << EVENT_SEL_SHIFT) & EVENT_SEL_MASK;
+ if (llcc_priv->filtered_ports & (1 << EVENT_PORT_BEAC))
+ val |= (FILTER_0 << FILTER_SEL_SHIFT) | FILTER_EN;
+
+ counter_num = event_counter_num;
+ }
+
+ llcc_bcast_modify(llcc_priv, BEAC_PROF_EVENT_n_CFG(event_counter_num),
+ val, mask);
+ perfmon_counter_config(llcc_priv, EVENT_PORT_BEAC, counter_num);
+}
+
+static void beac_event_enable(struct llcc_perfmon_private *llcc_priv,
+ bool enable)
+{
+ uint32_t val = 0, mask;
+
+ if (enable)
+ val = (BYTE_SCALING << BYTE_SCALING_SHIFT) |
+ (BEAT_SCALING << BEAT_SCALING_SHIFT) | PROF_EN;
+
+ mask = PROF_CFG_BEAT_SCALING_MASK | PROF_CFG_BYTE_SCALING_MASK
+ | PROF_CFG_EN_MASK;
+ llcc_bcast_modify(llcc_priv, BEAC_PROF_CFG, val, mask);
+}
+
+static void beac_event_filter_config(struct llcc_perfmon_private *llcc_priv,
+ enum filter_type filter, unsigned long match, bool enable)
+{
+ uint32_t val = 0, mask;
+
+ if (filter != PROFILING_TAG) {
+ pr_err("unknown filter/not supported\n");
+ return;
+ }
+
+ if (enable)
+ val = (match << BEAC_PROFTAG_MATCH_SHIFT)
+ | FILTER_0_MASK << BEAC_PROFTAG_MASK_SHIFT;
+
+ mask = BEAC_PROFTAG_MASK_MASK | BEAC_PROFTAG_MATCH_MASK;
+ llcc_bcast_modify(llcc_priv, BEAC_PROF_FILTER_0_CFG5, val, mask);
+
+ if (enable)
+ val = match << BEAC_MC_PROFTAG_SHIFT;
+
+ mask = BEAC_MC_PROFTAG_MASK;
+ llcc_bcast_modify(llcc_priv, BEAC_PROF_CFG, val, mask);
+}
+
+static struct event_port_ops beac_port_ops = {
+ .event_config = beac_event_config,
+ .event_enable = beac_event_enable,
+ .event_filter_config = beac_event_filter_config,
+};
+
+static void berc_event_config(struct llcc_perfmon_private *llcc_priv,
+ unsigned int event_type, unsigned int event_counter_num,
+ bool enable)
+{
+ uint32_t val = 0, mask, counter_num = 0;
+
+ mask = EVENT_SEL_MASK;
+ if (llcc_priv->filtered_ports & (1 << EVENT_PORT_BERC))
+ mask |= FILTER_SEL_MASK | FILTER_EN_MASK;
+
+ if (enable) {
+ val = (event_type << EVENT_SEL_SHIFT) & EVENT_SEL_MASK;
+ if (llcc_priv->filtered_ports & (1 << EVENT_PORT_BERC))
+ val |= (FILTER_0 << FILTER_SEL_SHIFT) | FILTER_EN;
+
+ counter_num = event_counter_num;
+ }
+
+ llcc_bcast_modify(llcc_priv, BERC_PROF_EVENT_n_CFG(event_counter_num),
+ val, mask);
+ perfmon_counter_config(llcc_priv, EVENT_PORT_BERC, counter_num);
+}
+
+static void berc_event_enable(struct llcc_perfmon_private *llcc_priv,
+ bool enable)
+{
+ uint32_t val = 0, mask;
+
+ if (enable)
+ val = (BYTE_SCALING << BYTE_SCALING_SHIFT) |
+ (BEAT_SCALING << BEAT_SCALING_SHIFT) | PROF_EN;
+
+ mask = PROF_CFG_BEAT_SCALING_MASK | PROF_CFG_BYTE_SCALING_MASK
+ | PROF_CFG_EN_MASK;
+ llcc_bcast_modify(llcc_priv, BERC_PROF_CFG, val, mask);
+}
+
+static void berc_event_filter_config(struct llcc_perfmon_private *llcc_priv,
+ enum filter_type filter, unsigned long match, bool enable)
+{
+ uint32_t val = 0, mask;
+
+ if (filter != PROFILING_TAG) {
+ pr_err("unknown filter/not supported\n");
+ return;
+ }
+
+ if (enable)
+ val = (match << PROFTAG_MATCH_SHIFT) |
+ FILTER_0_MASK << PROFTAG_MASK_SHIFT;
+
+ mask = PROFTAG_MATCH_MASK | PROFTAG_MASK_MASK;
+ llcc_bcast_modify(llcc_priv, BERC_PROF_FILTER_0_CFG0, val, mask);
+}
+
+static struct event_port_ops berc_port_ops = {
+ .event_config = berc_event_config,
+ .event_enable = berc_event_enable,
+ .event_filter_config = berc_event_filter_config,
+};
+
+static void trp_event_config(struct llcc_perfmon_private *llcc_priv,
+ unsigned int event_type, unsigned int event_counter_num,
+ bool enable)
+{
+ uint32_t val = 0, mask, counter_num = 0;
+
+ mask = EVENT_SEL_MASK;
+ if (llcc_priv->filtered_ports & (1 << EVENT_PORT_TRP))
+ mask |= FILTER_SEL_MASK | FILTER_EN_MASK;
+
+ if (enable) {
+ val = (event_type << EVENT_SEL_SHIFT) & EVENT_SEL_MASK;
+ if (llcc_priv->filtered_ports & (1 << EVENT_PORT_TRP))
+ val |= (FILTER_0 << FILTER_SEL_SHIFT) | FILTER_EN;
+
+ counter_num = event_counter_num;
+ }
+
+ llcc_bcast_modify(llcc_priv, TRP_PROF_EVENT_n_CFG(event_counter_num),
+ val, mask);
+ perfmon_counter_config(llcc_priv, EVENT_PORT_TRP, counter_num);
+}
+
+static void trp_event_filter_config(struct llcc_perfmon_private *llcc_priv,
+ enum filter_type filter, unsigned long match, bool enable)
+{
+ uint32_t val = 0, mask;
+
+ if (filter == SCID) {
+ if (enable)
+ val = (match << TRP_SCID_MATCH_SHIFT)
+ | TRP_SCID_MASK_MASK;
+
+ mask = TRP_SCID_MATCH_MASK | TRP_SCID_MASK_MASK;
+ } else if (filter == WAY_ID) {
+ if (enable)
+ val = (match << TRP_WAY_ID_MATCH_SHIFT)
+ | TRP_WAY_ID_MASK_MASK;
+
+ mask = TRP_WAY_ID_MATCH_MASK |
+ TRP_WAY_ID_MASK_MASK;
+ } else if (filter == PROFILING_TAG) {
+ if (enable)
+ val = (match << TRP_PROFTAG_MATCH_SHIFT)
+ | FILTER_0_MASK << TRP_PROFTAG_MASK_SHIFT;
+
+ mask = TRP_PROFTAG_MATCH_MASK | TRP_PROFTAG_MASK_MASK;
+ } else {
+ pr_err("unknown filter/not supported\n");
+ return;
+ }
+
+ llcc_bcast_modify(llcc_priv, TRP_PROF_FILTER_0_CFG1, val, mask);
+}
+
+static struct event_port_ops trp_port_ops = {
+ .event_config = trp_event_config,
+ .event_filter_config = trp_event_filter_config,
+};
+
+static void drp_event_config(struct llcc_perfmon_private *llcc_priv,
+ unsigned int event_type, unsigned int event_counter_num,
+ bool enable)
+{
+ uint32_t val = 0, mask, counter_num = 0;
+
+ mask = EVENT_SEL_MASK;
+ if (llcc_priv->filtered_ports & (1 << EVENT_PORT_DRP))
+ mask |= FILTER_SEL_MASK | FILTER_EN_MASK;
+
+ if (enable) {
+ val = (event_type << EVENT_SEL_SHIFT) & EVENT_SEL_MASK;
+ if (llcc_priv->filtered_ports & (1 << EVENT_PORT_DRP))
+ val |= (FILTER_0 << FILTER_SEL_SHIFT) | FILTER_EN;
+
+ counter_num = event_counter_num;
+ }
+
+ llcc_bcast_modify(llcc_priv, DRP_PROF_EVENT_n_CFG(event_counter_num),
+ val, mask);
+ perfmon_counter_config(llcc_priv, EVENT_PORT_DRP, counter_num);
+}
+
+static void drp_event_enable(struct llcc_perfmon_private *llcc_priv,
+ bool enable)
+{
+ uint32_t val = 0, mask;
+
+ if (enable)
+ val = (BEAT_SCALING << BEAT_SCALING_SHIFT) | PROF_EN;
+
+ mask = PROF_CFG_BEAT_SCALING_MASK | PROF_CFG_EN_MASK;
+ llcc_bcast_modify(llcc_priv, DRP_PROF_CFG, val, mask);
+}
+
+static struct event_port_ops drp_port_ops = {
+ .event_config = drp_event_config,
+ .event_enable = drp_event_enable,
+};
+
+static void pmgr_event_config(struct llcc_perfmon_private *llcc_priv,
+ unsigned int event_type, unsigned int event_counter_num,
+ bool enable)
+{
+ uint32_t val = 0, mask, counter_num = 0;
+
+ mask = EVENT_SEL_MASK;
+ if (llcc_priv->filtered_ports & (1 << EVENT_PORT_PMGR))
+ mask |= FILTER_SEL_MASK | FILTER_EN_MASK;
+
+ if (enable) {
+ val = (event_type << EVENT_SEL_SHIFT) & EVENT_SEL_MASK;
+ if (llcc_priv->filtered_ports & (1 << EVENT_PORT_PMGR))
+ val |= (FILTER_0 << FILTER_SEL_SHIFT) | FILTER_EN;
+
+ counter_num = event_counter_num;
+ }
+
+ llcc_bcast_modify(llcc_priv, PMGR_PROF_EVENT_n_CFG(event_counter_num),
+ val, mask);
+ perfmon_counter_config(llcc_priv, EVENT_PORT_PMGR, counter_num);
+}
+
+static struct event_port_ops pmgr_port_ops = {
+ .event_config = pmgr_event_config,
+};
+
+static void llcc_register_event_port(struct llcc_perfmon_private *llcc_priv,
+ struct event_port_ops *ops, unsigned int event_port_num)
+{
+ if (llcc_priv->port_configd >= MAX_NUMBER_OF_PORTS) {
+ pr_err("Register port Failure!");
+ return;
+ }
+
+ llcc_priv->port_configd = llcc_priv->port_configd + 1;
+ llcc_priv->port_ops[event_port_num] = ops;
+}
+
+static enum hrtimer_restart llcc_perfmon_timer_handler(struct hrtimer *hrtimer)
+{
+ struct llcc_perfmon_private *llcc_priv = container_of(hrtimer,
+ struct llcc_perfmon_private, hrtimer);
+
+ perfmon_counter_dump(llcc_priv);
+ hrtimer_forward_now(&llcc_priv->hrtimer, llcc_priv->expires);
+ return HRTIMER_RESTART;
+}
+
+static int llcc_perfmon_probe(struct platform_device *pdev)
+{
+ int result = 0;
+ struct llcc_perfmon_private *llcc_priv;
+ struct device *dev = &pdev->dev;
+ uint32_t val;
+
+ llcc_priv = devm_kzalloc(&pdev->dev, sizeof(*llcc_priv), GFP_KERNEL);
+ if (llcc_priv == NULL)
+ return -ENOMEM;
+
+ llcc_priv->llcc_map = syscon_node_to_regmap(dev->parent->of_node);
+ if (IS_ERR(llcc_priv->llcc_map))
+ return PTR_ERR(llcc_priv->llcc_map);
+
+ result = of_property_read_u32(pdev->dev.parent->of_node,
+ "qcom,llcc-broadcast-off", &llcc_priv->broadcast_off);
+ if (result) {
+ pr_err("Invalid qcom,broadcast-off entry\n");
+ return result;
+ }
+
+ llcc_bcast_read(llcc_priv, LLCC_COMMON_STATUS0, &val);
+
+ llcc_priv->num_banks = (val & LB_CNT_MASK) >> LB_CNT_SHIFT;
+ result = of_property_read_variable_u32_array(pdev->dev.parent->of_node,
+ "qcom,llcc-banks-off", (u32 *)&llcc_priv->bank_off,
+ 1, llcc_priv->num_banks);
+ if (result < 0) {
+ pr_err("Invalid qcom,llcc-banks-off entry\n");
+ return result;
+ }
+
+ result = sysfs_create_group(&pdev->dev.kobj, &llcc_perfmon_group);
+ if (result) {
+ pr_err("Unable to create sysfs version group\n");
+ return result;
+ }
+
+ mutex_init(&llcc_priv->mutex);
+ platform_set_drvdata(pdev, llcc_priv);
+ llcc_register_event_port(llcc_priv, &feac_port_ops, EVENT_PORT_FEAC);
+ llcc_register_event_port(llcc_priv, &ferc_port_ops, EVENT_PORT_FERC);
+ llcc_register_event_port(llcc_priv, &fewc_port_ops, EVENT_PORT_FEWC);
+ llcc_register_event_port(llcc_priv, &beac_port_ops, EVENT_PORT_BEAC);
+ llcc_register_event_port(llcc_priv, &berc_port_ops, EVENT_PORT_BERC);
+ llcc_register_event_port(llcc_priv, &trp_port_ops, EVENT_PORT_TRP);
+ llcc_register_event_port(llcc_priv, &drp_port_ops, EVENT_PORT_DRP);
+ llcc_register_event_port(llcc_priv, &pmgr_port_ops, EVENT_PORT_PMGR);
+ hrtimer_init(&llcc_priv->hrtimer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
+ llcc_priv->hrtimer.function = llcc_perfmon_timer_handler;
+ llcc_priv->expires.tv64 = 0;
+ return 0;
+}
+
+static int llcc_perfmon_remove(struct platform_device *pdev)
+{
+ struct llcc_perfmon_private *llcc_priv = platform_get_drvdata(pdev);
+
+ while (hrtimer_active(&llcc_priv->hrtimer))
+ hrtimer_cancel(&llcc_priv->hrtimer);
+
+ mutex_destroy(&llcc_priv->mutex);
+ sysfs_remove_group(&pdev->dev.kobj, &llcc_perfmon_group);
+ platform_set_drvdata(pdev, NULL);
+ return 0;
+}
+
+static const struct of_device_id of_match_llcc[] = {
+ {
+ .compatible = "qcom,llcc-perfmon",
+ },
+ {},
+};
+MODULE_DEVICE_TABLE(of, of_match_llcc);
+
+static struct platform_driver llcc_perfmon_driver = {
+ .probe = llcc_perfmon_probe,
+ .remove = llcc_perfmon_remove,
+ .driver = {
+ .name = LLCC_PERFMON_NAME,
+ .of_match_table = of_match_llcc,
+ }
+};
+module_platform_driver(llcc_perfmon_driver);
+
+MODULE_DESCRIPTION("QCOM LLCC PMU MONITOR");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/soc/qcom/llcc_perfmon.h b/drivers/soc/qcom/llcc_perfmon.h
new file mode 100644
index 0000000..11dd2f8
--- /dev/null
+++ b/drivers/soc/qcom/llcc_perfmon.h
@@ -0,0 +1,197 @@
+/* Copyright (c) 2017, 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 _SOC_QCOM_LLCC_PERFMON_H_
+#define _SOC_QCOM_LLCC_PERFMON_H_
+
+#define LLCC_COMMON_STATUS0 (0x3000C)
+/* FEAC */
+#define FEAC_PROF_FILTER_0_CFG5 (0x037014)
+#define FEAC_PROF_FILTER_0_CFG6 (0x037018)
+#define FEAC_PROF_EVENT_n_CFG(n) (0x037060 + 4 * n)
+#define FEAC_PROF_CFG (0x0370A0)
+
+/* FERC */
+#define FERC_PROF_FILTER_0_CFG0 (0x03B000)
+#define FERC_PROF_EVENT_n_CFG(n) (0x03B020 + 4 * n)
+#define FERC_PROF_CFG (0x03B060)
+
+/* FEWC */
+#define FEWC_PROF_FILTER_0_CFG0 (0x033000)
+#define FEWC_PROF_EVENT_n_CFG(n) (0x033020 + 4 * n)
+
+/* BEAC */
+#define BEAC_PROF_FILTER_0_CFG5 (0x049014)
+#define BEAC_PROF_EVENT_n_CFG(n) (0x049040 + 4 * n)
+#define BEAC_PROF_CFG (0x049080)
+
+/* BERC */
+#define BERC_PROF_FILTER_0_CFG0 (0x039000)
+#define BERC_PROF_EVENT_n_CFG(n) (0x039020 + 4 * n)
+#define BERC_PROF_CFG (0x039060)
+
+/* TRP */
+#define TRP_PROF_FILTER_0_CFG1 (0x024004)
+#define TRP_PROF_EVENT_n_CFG(n) (0x024020 + 4 * n)
+#define TRP_SCID_n_STATUS(n) (0x000004 + 0x1000 * n)
+
+/* DRP */
+#define DRP_PROF_EVENT_n_CFG(n) (0x044010 + 4 * n)
+#define DRP_PROF_CFG (0x044050)
+
+/* PMGR */
+#define PMGR_PROF_EVENT_n_CFG(n) (0x03F000 + 4 * n)
+
+#define PERFMON_COUNTER_n_CONFIG(n) (0x031020 + 4 * n)
+#define PERFMON_MODE (0x03100C)
+#define PERFMON_DUMP (0x031010)
+#define BROADCAST_COUNTER_n_VALUE(n) (0x031060 + 4 * n)
+
+#define LLCC_COUNTER_n_VALUE(n) (0x031060 + 4 * n)
+
+#define EVENT_NUM_MAX (64)
+#define SCID_MAX (32)
+
+/* Perfmon */
+#define CLEAR_ON_ENABLE BIT(31)
+#define CLEAR_ON_DUMP BIT(30)
+#define FREEZE_ON_SATURATE BIT(29)
+#define CHAINING_EN BIT(28)
+#define COUNT_CLOCK_EVENT BIT(24)
+
+#define EVENT_SELECT_SHIFT (16)
+#define PERFMON_EVENT_SELECT_MASK GENMASK(EVENT_SELECT_SHIFT + 4,\
+ EVENT_SELECT_SHIFT)
+#define PORT_SELECT_SHIFT (0)
+#define PERFMON_PORT_SELECT_MASK GENMASK(PORT_SELECT_SHIFT + 3,\
+ PORT_SELECT_SHIFT)
+
+#define MANUAL_MODE (0)
+#define TIMED_MODE (1)
+#define TRIGGER_MODE (2)
+#define MONITOR_EN_SHIFT (15)
+#define MONITOR_EN BIT(MONITOR_EN_SHIFT)
+#define PERFMON_MODE_MONITOR_EN_MASK GENMASK(MONITOR_EN_SHIFT + 0,\
+ MONITOR_EN_SHIFT)
+#define MONITOR_MODE_SHIFT (0)
+#define PERFMON_MODE_MONITOR_MODE_MASK GENMASK(MONITOR_MODE_SHIFT + 0,\
+ MONITOR_MODE_SHIFT)
+
+#define MONITOR_DUMP BIT(0)
+
+/* COMMON */
+#define BYTE_SCALING (1024)
+#define BEAT_SCALING (32)
+#define LB_CNT_SHIFT (28)
+#define LB_CNT_MASK GENMASK(LB_CNT_SHIFT + 3, \
+ LB_CNT_SHIFT)
+
+#define BYTE_SCALING_SHIFT (16)
+#define PROF_CFG_BYTE_SCALING_MASK GENMASK(BYTE_SCALING_SHIFT + 11,\
+ BYTE_SCALING_SHIFT)
+#define BEAT_SCALING_SHIFT (8)
+#define PROF_CFG_BEAT_SCALING_MASK GENMASK(BEAT_SCALING_SHIFT + 7,\
+ BEAT_SCALING_SHIFT)
+#define PROF_EN_SHIFT (0)
+#define PROF_EN BIT(PROF_EN_SHIFT)
+#define PROF_CFG_EN_MASK GENMASK(PROF_EN_SHIFT + 0,\
+ PROF_EN_SHIFT)
+
+#define FILTER_EN_SHIFT (31)
+#define FILTER_EN BIT(FILTER_EN_SHIFT)
+#define FILTER_EN_MASK GENMASK(FILTER_EN_SHIFT + 0,\
+ FILTER_EN_SHIFT)
+#define FILTER_0 (0)
+#define FILTER_0_MASK GENMASK(FILTER_0 + 0, \
+ FILTER_0)
+#define FILTER_1 (1)
+#define FILTER_1_MASK GENMASK(FILTER_1 + 0, \
+ FILTER_1)
+
+#define FILTER_SEL_SHIFT (16)
+#define FILTER_SEL_MASK GENMASK(FILTER_SEL_SHIFT + 0,\
+ FILTER_SEL_SHIFT)
+#define EVENT_SEL_SHIFT (0)
+#define EVENT_SEL_MASK GENMASK(EVENT_SEL_SHIFT + 5,\
+ EVENT_SEL_SHIFT)
+
+#define MID_MASK_SHIFT (16)
+#define MID_MASK_MASK GENMASK(MID_MASK_SHIFT + 15, \
+ MID_MASK_SHIFT)
+#define MID_MATCH_SHIFT (0)
+#define MID_MATCH_MASK GENMASK(MID_MATCH_SHIFT + 15, \
+ MID_MATCH_SHIFT)
+#define SCID_MASK_SHIFT (16)
+#define SCID_MASK_MASK GENMASK(SCID_MASK_SHIFT + 15, \
+ SCID_MASK_SHIFT)
+#define SCID_MATCH_SHIFT (0)
+#define SCID_MATCH_MASK GENMASK(SCID_MATCH_SHIFT + 15, \
+ SCID_MATCH_SHIFT)
+#define PROFTAG_MASK_SHIFT (2)
+#define PROFTAG_MASK_MASK GENMASK(PROFTAG_MASK_SHIFT + 1,\
+ PROFTAG_MASK_SHIFT)
+#define PROFTAG_MATCH_SHIFT (0)
+#define PROFTAG_MATCH_MASK GENMASK(PROFTAG_MATCH_SHIFT + 1,\
+ PROFTAG_MATCH_SHIFT)
+/* FEAC */
+#define FEAC_SCALING_FILTER_SEL_SHIFT (2)
+#define FEAC_SCALING_FILTER_SEL_MASK GENMASK(FEAC_SCALING_FILTER_SEL_SHIFT \
+ + 0, \
+ FEAC_SCALING_FILTER_SEL_SHIFT)
+#define FEAC_SCALING_FILTER_EN_SHIFT (1)
+#define FEAC_SCALING_FILTER_EN BIT(FEAC_SCALING_FILTER_EN_SHIFT)
+#define FEAC_SCALING_FILTER_EN_MASK GENMASK(FEAC_SCALING_FILTER_EN_SHIFT \
+ + 0, \
+ FEAC_SCALING_FILTER_EN_SHIFT)
+/* BEAC */
+#define BEAC_PROFTAG_MASK_SHIFT (14)
+#define BEAC_PROFTAG_MASK_MASK GENMASK(BEAC_PROFTAG_MASK_SHIFT + 1,\
+ BEAC_PROFTAG_MASK_SHIFT)
+#define BEAC_PROFTAG_MATCH_SHIFT (12)
+#define BEAC_PROFTAG_MATCH_MASK GENMASK(BEAC_PROFTAG_MATCH_SHIFT + 1,\
+ BEAC_PROFTAG_MATCH_SHIFT)
+#define BEAC_MC_PROFTAG_SHIFT (1)
+#define BEAC_MC_PROFTAG_MASK GENMASK(BEAC_MC_PROFTAG_SHIFT + 1,\
+ BEAC_MC_PROFTAG_SHIFT)
+/* TRP */
+#define TRP_SCID_MATCH_SHIFT (0)
+#define TRP_SCID_MATCH_MASK GENMASK(TRP_SCID_MATCH_SHIFT + 4,\
+ TRP_SCID_MATCH_SHIFT)
+#define TRP_SCID_MASK_SHIFT (8)
+#define TRP_SCID_MASK_MASK GENMASK(TRP_SCID_MASK_SHIFT + 4,\
+ TRP_SCID_MASK_SHIFT)
+#define TRP_WAY_ID_MATCH_SHIFT (16)
+#define TRP_WAY_ID_MATCH_MASK GENMASK(TRP_WAY_ID_MATCH_SHIFT + 3,\
+ TRP_WAY_ID_MATCH_SHIFT)
+#define TRP_WAY_ID_MASK_SHIFT (20)
+#define TRP_WAY_ID_MASK_MASK GENMASK(TRP_WAY_ID_MASK_SHIFT + 3,\
+ TRP_WAY_ID_MASK_SHIFT)
+#define TRP_PROFTAG_MATCH_SHIFT (24)
+#define TRP_PROFTAG_MATCH_MASK GENMASK(TRP_PROFTAG_MATCH_SHIFT + 1,\
+ TRP_PROFTAG_MATCH_SHIFT)
+#define TRP_PROFTAG_MASK_SHIFT (28)
+#define TRP_PROFTAG_MASK_MASK GENMASK(TRP_PROFTAG_MASK_SHIFT + 1,\
+ TRP_PROFTAG_MASK_SHIFT)
+
+#define TRP_SCID_STATUS_ACTIVE_SHIFT (0)
+#define TRP_SCID_STATUS_ACTIVE_MASK GENMASK( \
+ TRP_SCID_STATUS_ACTIVE_SHIFT \
+ + 0, \
+ TRP_SCID_STATUS_ACTIVE_SHIFT)
+#define TRP_SCID_STATUS_DEACTIVE_SHIFT (1)
+#define TRP_SCID_STATUS_CURRENT_CAP_SHIFT (16)
+#define TRP_SCID_STATUS_CURRENT_CAP_MASK GENMASK( \
+ TRP_SCID_STATUS_CURRENT_CAP_SHIFT \
+ + 13, \
+ TRP_SCID_STATUS_CURRENT_CAP_SHIFT)
+
+#endif /* _SOC_QCOM_LLCC_PERFMON_H_ */
diff --git a/drivers/soc/qcom/msm_bus/msm_bus_arb_rpmh.c b/drivers/soc/qcom/msm_bus/msm_bus_arb_rpmh.c
index a40efcb..437984c 100644
--- a/drivers/soc/qcom/msm_bus/msm_bus_arb_rpmh.c
+++ b/drivers/soc/qcom/msm_bus/msm_bus_arb_rpmh.c
@@ -1294,7 +1294,7 @@
src = pdata->usecase->vectors[i].src;
dest = pdata->usecase->vectors[i].dst;
- if ((src < 0) || (dest < 0)) {
+ if ((src < 0) || (dest < 0) || (src == dest)) {
MSM_BUS_ERR("%s:Invalid src/dst.src %d dest %d",
__func__, src, dest);
goto exit_invalid_data;
diff --git a/drivers/soc/qcom/msm_bus/msm_bus_noc_rpmh.c b/drivers/soc/qcom/msm_bus/msm_bus_noc_rpmh.c
index bdcdf29..282f2ac 100644
--- a/drivers/soc/qcom/msm_bus/msm_bus_noc_rpmh.c
+++ b/drivers/soc/qcom/msm_bus/msm_bus_noc_rpmh.c
@@ -33,6 +33,7 @@
#define MAX_SAT_FIELD (NOC_QOS_SATn_SAT_BMSK >> NOC_QOS_SATn_SAT_SHFT)
#define MIN_SAT_FIELD 1
#define MIN_BW_FIELD 1
+#define QM_BASE 0x010B8000
#define MSM_BUS_FAB_MEM_NOC 6152
#define NOC_QOS_REG_BASE(b, o) ((b) + (o))
@@ -107,6 +108,16 @@
NOC_QOS_SATn_SAT_SHFT = 0x0,
};
+#define QM_CLn_TH_LVL_MUX_ADDR(b, o, n, d) \
+ (NOC_QOS_REG_BASE(b, o) + 0x8C0 + (d) * (n))
+enum qm_cl_id_th_lvl_mux_cfg {
+ QM_CLn_TH_LVL_SW_OVERRD_BMSK = 0x80000000,
+ QM_CLn_TH_LVL_SW_OVERRD_SHFT = 0x1F,
+ QM_CLn_TH_LVL_SW_BMSK = 0x00000007,
+ QM_CLn_TH_LVL_SW_SHFT = 0x0,
+};
+
+static void __iomem *qm_base;
static void __iomem *memnoc_qos_base;
static int noc_div(uint64_t *a, uint32_t b)
@@ -159,6 +170,18 @@
}
#define MAX_WS(bw, timebase) noc_ws((bw), MAX_SAT_FIELD, (timebase))
+static void noc_set_qm_th_lvl_cfg(void __iomem *base, uint32_t off,
+ uint32_t n, uint32_t delta,
+ uint32_t override_val, uint32_t override)
+{
+ writel_relaxed(((override << QM_CLn_TH_LVL_SW_OVERRD_SHFT) |
+ (override_val & QM_CLn_TH_LVL_SW_BMSK)),
+ QM_CLn_TH_LVL_MUX_ADDR(base, off, n, delta));
+
+ /* Ensure QM CFG is set before exiting */
+ wmb();
+}
+
static void noc_set_qos_dflt_prio(void __iomem *base, uint32_t qos_off,
uint32_t mport, uint32_t qos_delta,
uint32_t prio)
@@ -355,6 +378,16 @@
goto err_qos_init;
}
+ if (!qm_base) {
+ qm_base = ioremap_nocache(QM_BASE, 0x4000);
+ if (!qm_base) {
+ MSM_BUS_ERR("%s: Error remapping address 0x%zx",
+ __func__, (size_t)QM_BASE);
+ ret = -ENOMEM;
+ goto err_qos_init;
+ }
+ }
+
spin_lock_irqsave(&noc_lock, flags);
if (fabdev->node_info->id == MSM_BUS_FAB_MEM_NOC)
@@ -395,45 +428,17 @@
spin_lock_irqsave(&noc_lock, flags);
- if (!memnoc_qos_base) {
- MSM_BUS_ERR("Memnoc QoS Base address not found!");
+ if (!qm_base) {
+ MSM_BUS_ERR("QM CFG base address not found!");
goto noc_throttle_exit;
}
if (enable) {
- noc_set_qos_limit_bw(memnoc_qos_base, 0x10000, 2,
- 0x1000, 0x1B);
- noc_set_qos_limit_bw(memnoc_qos_base, 0x10000, 3,
- 0x1000, 0x1B);
- noc_set_qos_limit_bw(memnoc_qos_base, 0x10000, 10,
- 0x1000, 0x30);
- noc_set_qos_limit_bw(memnoc_qos_base, 0x10000, 11,
- 0x1000, 0x30);
- noc_enable_qos_limiter(memnoc_qos_base, 0x10000, 10,
- 0x1000, 1);
- noc_enable_qos_limiter(memnoc_qos_base, 0x10000, 11,
- 0x1000, 1);
- noc_enable_qos_limiter(memnoc_qos_base, 0x10000, 2,
- 0x1000, 1);
- noc_enable_qos_limiter(memnoc_qos_base, 0x10000, 3,
- 0x1000, 1);
+ noc_set_qm_th_lvl_cfg(qm_base, 0x1000, 8, 0x4, 0x3, 0x1);
+ noc_set_qm_th_lvl_cfg(qm_base, 0x1000, 9, 0x4, 0x3, 0x1);
} else {
- noc_enable_qos_limiter(memnoc_qos_base, 0x10000, 2,
- 0x1000, 0);
- noc_enable_qos_limiter(memnoc_qos_base, 0x10000, 3,
- 0x1000, 0);
- noc_enable_qos_limiter(memnoc_qos_base, 0x10000, 10,
- 0x1000, 0);
- noc_enable_qos_limiter(memnoc_qos_base, 0x10000, 11,
- 0x1000, 0);
- noc_set_qos_limit_bw(memnoc_qos_base, 0x10000, 2,
- 0x1000, 0);
- noc_set_qos_limit_bw(memnoc_qos_base, 0x10000, 3,
- 0x1000, 0);
- noc_set_qos_limit_bw(memnoc_qos_base, 0x10000, 10,
- 0x1000, 0);
- noc_set_qos_limit_bw(memnoc_qos_base, 0x10000, 11,
- 0x1000, 0);
+ noc_set_qm_th_lvl_cfg(qm_base, 0x1000, 8, 0x4, 0, 0);
+ noc_set_qm_th_lvl_cfg(qm_base, 0x1000, 9, 0x4, 0, 0);
}
noc_throttle_exit:
diff --git a/drivers/soc/qcom/pil-msa.c b/drivers/soc/qcom/pil-msa.c
index ec3063e..a3eb551 100644
--- a/drivers/soc/qcom/pil-msa.c
+++ b/drivers/soc/qcom/pil-msa.c
@@ -124,13 +124,6 @@
int ret = 0;
u32 regval;
- if (drv->vreg) {
- ret = regulator_enable(drv->vreg);
- if (ret)
- dev_err(drv->desc.dev, "Failed to enable modem regulator(rc:%d)\n",
- ret);
- }
-
if (drv->cxrail_bhs) {
regval = readl_relaxed(drv->cxrail_bhs);
regval |= EXTERNAL_BHS_ON;
@@ -153,9 +146,6 @@
writel_relaxed(regval, drv->cxrail_bhs);
}
- if (drv->vreg)
- return regulator_disable(drv->vreg);
-
return 0;
}
@@ -456,7 +446,44 @@
return ret;
}
+ if (drv->vreg) {
+ ret = of_property_read_u32(pil->dev->of_node, "vdd_mss-uV",
+ &uv);
+ if (ret) {
+ dev_err(pil->dev,
+ "missing vdd_mss-uV property(rc:%d)\n", ret);
+ goto out;
+ }
+
+ ret = regulator_set_voltage(drv->vreg, uv,
+ INT_MAX);
+ if (ret) {
+ dev_err(pil->dev, "Failed to set vreg voltage(rc:%d)\n",
+ ret);
+ goto out;
+ }
+
+ ret = regulator_set_load(drv->vreg, 100000);
+ if (ret < 0) {
+ dev_err(pil->dev, "Failed to set vreg mode(rc:%d)\n",
+ ret);
+ goto out;
+ }
+ ret = regulator_enable(drv->vreg);
+ if (ret) {
+ dev_err(pil->dev, "Failed to enable vreg(rc:%d)\n",
+ ret);
+ regulator_set_voltage(drv->vreg, 0, INT_MAX);
+ goto out;
+ }
+ }
+
ret = pil_q6v5_make_proxy_votes(pil);
+ if (ret && drv->vreg) {
+ regulator_disable(drv->vreg);
+ regulator_set_voltage(drv->vreg, 0, INT_MAX);
+ }
+out:
if (ret) {
regulator_disable(drv->vreg_mx);
regulator_set_voltage(drv->vreg_mx, 0, INT_MAX);
@@ -472,6 +499,10 @@
pil_q6v5_remove_proxy_votes(pil);
regulator_disable(drv->vreg_mx);
regulator_set_voltage(drv->vreg_mx, 0, INT_MAX);
+ if (drv->vreg) {
+ regulator_disable(drv->vreg);
+ regulator_set_voltage(drv->vreg, 0, INT_MAX);
+ }
}
static int pil_mss_mem_setup(struct pil_desc *pil,
diff --git a/drivers/soc/qcom/pil-msa.h b/drivers/soc/qcom/pil-msa.h
index 7a6a3cc..0f1e75b 100644
--- a/drivers/soc/qcom/pil-msa.h
+++ b/drivers/soc/qcom/pil-msa.h
@@ -17,8 +17,6 @@
#include "peripheral-loader.h"
-#define VDD_MSS_UV 1000000
-
struct modem_data {
struct q6v5_data *q6;
struct subsys_device *subsys;
diff --git a/drivers/soc/qcom/pil-q6v5-mss.c b/drivers/soc/qcom/pil-q6v5-mss.c
index cf5f7e4..0477064 100644
--- a/drivers/soc/qcom/pil-q6v5-mss.c
+++ b/drivers/soc/qcom/pil-q6v5-mss.c
@@ -35,7 +35,6 @@
#include "pil-q6v5.h"
#include "pil-msa.h"
-#define MAX_VDD_MSS_UV 1150000
#define PROXY_TIMEOUT_MS 10000
#define MAX_SSR_REASON_LEN 256U
#define STOP_ACK_TIMEOUT_MS 1000
@@ -332,19 +331,6 @@
q6->vreg = devm_regulator_get(&pdev->dev, "vdd_mss");
if (IS_ERR(q6->vreg))
return PTR_ERR(q6->vreg);
-
- ret = regulator_set_voltage(q6->vreg, VDD_MSS_UV,
- MAX_VDD_MSS_UV);
- if (ret)
- dev_err(&pdev->dev, "Failed to set vreg voltage(rc:%d)\n",
- ret);
-
- ret = regulator_set_load(q6->vreg, 100000);
- if (ret < 0) {
- dev_err(&pdev->dev, "Failed to set vreg mode(rc:%d)\n",
- ret);
- return ret;
- }
}
q6->vreg_mx = devm_regulator_get(&pdev->dev, "vdd_mx");
diff --git a/drivers/soc/qcom/rpm_stats.c b/drivers/soc/qcom/rpm_stats.c
index a3fe7b3..e60a7ad 100644
--- a/drivers/soc/qcom/rpm_stats.c
+++ b/drivers/soc/qcom/rpm_stats.c
@@ -173,7 +173,7 @@
if (prvdata.read_idx < prvdata.num_records)
prvdata.len = msm_rpmstats_copy_stats(&prvdata);
- return snprintf(buf, prvdata.len, prvdata.buf);
+ return snprintf(buf, prvdata.len, "%s", prvdata.buf);
}
static int msm_rpmstats_create_sysfs(struct msm_rpmstats_platform_data *pd)
diff --git a/drivers/soc/qcom/smp2p_spinlock_test.c b/drivers/soc/qcom/smp2p_spinlock_test.c
deleted file mode 100644
index d1e5090..0000000
--- a/drivers/soc/qcom/smp2p_spinlock_test.c
+++ /dev/null
@@ -1,820 +0,0 @@
-/* drivers/soc/qcom/smp2p_spinlock_test.c
- *
- * Copyright (c) 2013-2017, The Linux Foundation. All rights reserved.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 and
- * only version 2 as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- */
-#include <linux/debugfs.h>
-#include <linux/ctype.h>
-#include <linux/jiffies.h>
-#include <linux/delay.h>
-#include <linux/completion.h>
-#include <linux/module.h>
-#include <linux/remote_spinlock.h>
-#include <soc/qcom/smem.h>
-#include "smem_private.h"
-#include "smp2p_private.h"
-#include "smp2p_test_common.h"
-
-#define RS_END_THIEF_PID_BIT 20
-#define RS_END_THIEF_MASK 0x00f00000
-
-/* Spinlock commands used for testing Apps<->RPM spinlocks. */
-enum RPM_SPINLOCK_CMDS {
- RPM_CMD_INVALID,
- RPM_CMD_START,
- RPM_CMD_LOCKED,
- RPM_CMD_UNLOCKED,
- RPM_CMD_END,
-};
-
-/* Shared structure for testing Apps<->RPM spinlocks. */
-struct rpm_spinlock_test {
- uint32_t apps_cmd;
- uint32_t apps_lock_count;
- uint32_t rpm_cmd;
- uint32_t rpm_lock_count;
-};
-
-static uint32_t ut_remote_spinlock_run_time = 1;
-
-/**
- * smp2p_ut_remote_spinlock_core - Verify remote spinlock.
- *
- * @s: Pointer to output file
- * @remote_pid: Remote processor to test
- * @use_trylock: Use trylock to prevent an Apps deadlock if the
- * remote spinlock fails.
- */
-static void smp2p_ut_remote_spinlock_core(struct seq_file *s, int remote_pid,
- bool use_trylock)
-{
- int failed = 0;
- unsigned int lock_count = 0;
- struct msm_smp2p_out *handle = NULL;
- int ret;
- uint32_t test_request;
- uint32_t test_response;
- struct mock_cb_data cb_out;
- struct mock_cb_data cb_in;
- unsigned long flags;
- unsigned int n;
- bool have_lock;
- bool timeout;
- int failed_tmp;
- int spinlock_owner;
- remote_spinlock_t *smem_spinlock;
- unsigned long end;
-
- seq_printf(s, "Running %s for '%s' remote pid %d\n",
- __func__, smp2p_pid_to_name(remote_pid), remote_pid);
-
- cb_out.initialized = false;
- cb_in.initialized = false;
- mock_cb_data_init(&cb_out);
- mock_cb_data_init(&cb_in);
- do {
- smem_spinlock = smem_get_remote_spinlock();
- UT_ASSERT_PTR(smem_spinlock, !=, NULL);
-
- /* Open output entry */
- ret = msm_smp2p_out_open(remote_pid, SMP2P_RLPB_ENTRY_NAME,
- &cb_out.nb, &handle);
- UT_ASSERT_INT(ret, ==, 0);
- UT_ASSERT_INT(
- (int)wait_for_completion_timeout(
- &cb_out.cb_completion, HZ * 2),
- >, 0);
- UT_ASSERT_INT(cb_out.cb_count, ==, 1);
- UT_ASSERT_INT(cb_out.event_open, ==, 1);
-
- /* Open inbound entry */
- ret = msm_smp2p_in_register(remote_pid, SMP2P_RLPB_ENTRY_NAME,
- &cb_in.nb);
- UT_ASSERT_INT(ret, ==, 0);
- UT_ASSERT_INT(
- (int)wait_for_completion_timeout(
- &cb_in.cb_completion, HZ * 2),
- >, 0);
- UT_ASSERT_INT(cb_in.cb_count, ==, 1);
- UT_ASSERT_INT(cb_in.event_open, ==, 1);
-
- /* Send start */
- mock_cb_data_reset(&cb_in);
- mock_cb_data_reset(&cb_out);
- test_request = 0x0;
- SMP2P_SET_RMT_CMD_TYPE_REQ(test_request);
- SMP2P_SET_RMT_CMD(test_request, SMP2P_LB_CMD_RSPIN_START);
- SMP2P_SET_RMT_DATA(test_request, 0x0);
- ret = msm_smp2p_out_write(handle, test_request);
- UT_ASSERT_INT(ret, ==, 0);
-
- UT_ASSERT_INT(
- (int)wait_for_completion_timeout(
- &cb_in.cb_completion, HZ * 2),
- >, 0);
- UT_ASSERT_INT(cb_in.cb_count, ==, 1);
- UT_ASSERT_INT(cb_in.event_entry_update, ==, 1);
- ret = msm_smp2p_in_read(remote_pid, SMP2P_RLPB_ENTRY_NAME,
- &test_response);
- UT_ASSERT_INT(ret, ==, 0);
-
- test_response = SMP2P_GET_RMT_CMD(test_response);
- if (test_response != SMP2P_LB_CMD_RSPIN_LOCKED &&
- test_response != SMP2P_LB_CMD_RSPIN_UNLOCKED) {
- /* invalid response from remote - abort test */
- test_request = 0x0;
- SMP2P_SET_RMT_CMD_TYPE(test_request, 1);
- SMP2P_SET_RMT_CMD(test_request, SMP2P_LB_CMD_RSPIN_END);
- SMP2P_SET_RMT_DATA(test_request, 0x0);
- ret = msm_smp2p_out_write(handle, test_request);
- UT_ASSERT_HEX(SMP2P_LB_CMD_RSPIN_LOCKED, ==,
- test_response);
- }
-
- /* Run spinlock test */
- if (use_trylock)
- seq_puts(s, "\tUsing remote_spin_trylock\n");
- else
- seq_puts(s, "\tUsing remote_spin_lock\n");
-
- flags = 0;
- have_lock = false;
- timeout = false;
- spinlock_owner = 0;
- test_request = 0x0;
- SMP2P_SET_RMT_CMD_TYPE_REQ(test_request);
- end = jiffies + (ut_remote_spinlock_run_time * HZ);
- if (ut_remote_spinlock_run_time < 300) {
- seq_printf(s, "\tRunning test for %u seconds; ",
- ut_remote_spinlock_run_time);
- seq_puts(s,
- "on physical hardware please run >= 300 seconds by doing 'echo 300 > ut_remote_spinlock_time'\n");
- }
- while (time_is_after_jiffies(end)) {
- /* try to acquire spinlock */
- if (use_trylock) {
- unsigned long j_start = jiffies;
-
- while (!remote_spin_trylock_irqsave(
- smem_spinlock, flags)) {
- if (jiffies_to_msecs(jiffies - j_start)
- > 1000) {
- seq_puts(s,
- "\tFail: Timeout trying to get the lock\n");
- timeout = true;
- break;
- }
- }
- if (timeout)
- break;
- } else {
- remote_spin_lock_irqsave(smem_spinlock, flags);
- }
- have_lock = true;
- ++lock_count;
-
- /* tell the remote side that we have the lock */
- SMP2P_SET_RMT_DATA(test_request, lock_count);
- SMP2P_SET_RMT_CMD(test_request,
- SMP2P_LB_CMD_RSPIN_LOCKED);
- ret = msm_smp2p_out_write(handle, test_request);
- UT_ASSERT_INT(ret, ==, 0);
-
- /* verify the other side doesn't say it has the lock */
- for (n = 0; n < 1000; ++n) {
- spinlock_owner =
- remote_spin_owner(smem_spinlock);
- if (spinlock_owner != SMEM_APPS) {
- /* lock stolen by remote side */
- seq_puts(s, "\tFail: Remote side: ");
- seq_printf(s, "%d stole lock pid: %d\n",
- remote_pid, spinlock_owner);
- failed = true;
- break;
- }
- spinlock_owner = 0;
-
- ret = msm_smp2p_in_read(remote_pid,
- SMP2P_RLPB_ENTRY_NAME, &test_response);
- UT_ASSERT_INT(ret, ==, 0);
- test_response =
- SMP2P_GET_RMT_CMD(test_response);
- UT_ASSERT_HEX(SMP2P_LB_CMD_RSPIN_UNLOCKED, ==,
- test_response);
- }
- if (failed)
- break;
-
- /* tell remote side we are unlocked and release lock */
- SMP2P_SET_RMT_CMD(test_request,
- SMP2P_LB_CMD_RSPIN_UNLOCKED);
- (void)msm_smp2p_out_write(handle, test_request);
- have_lock = false;
- remote_spin_unlock_irqrestore(smem_spinlock, flags);
- }
- if (have_lock)
- remote_spin_unlock_irqrestore(smem_spinlock, flags);
-
- /* End test */
- mock_cb_data_reset(&cb_in);
- SMP2P_SET_RMT_CMD(test_request, SMP2P_LB_CMD_RSPIN_END);
- SMP2P_SET_RMT_DATA(test_request, lock_count |
- (spinlock_owner << RS_END_THIEF_PID_BIT));
- (void)msm_smp2p_out_write(handle, test_request);
-
- failed_tmp = failed;
- failed = false;
- do {
- UT_ASSERT_INT(
- (int)wait_for_completion_timeout(
- &cb_in.cb_completion, HZ * 2),
- >, 0);
- reinit_completion(&cb_in.cb_completion);
- ret = msm_smp2p_in_read(remote_pid,
- SMP2P_RLPB_ENTRY_NAME, &test_response);
- UT_ASSERT_INT(ret, ==, 0);
- } while (!failed &&
- SMP2P_GET_RMT_CMD(test_response) !=
- SMP2P_LB_CMD_RSPIN_END);
- if (failed)
- break;
- failed = failed_tmp;
-
- test_response = SMP2P_GET_RMT_DATA(test_response);
- seq_puts(s, "\tLocked spinlock ");
- seq_printf(s, "local %u times; remote %u times",
- lock_count,
- test_response & ((1 << RS_END_THIEF_PID_BIT) - 1)
- );
- if (test_response & RS_END_THIEF_MASK) {
- seq_puts(s, "Remote side reporting lock stolen by ");
- seq_printf(s, "pid %d.\n",
- SMP2P_GET_BITS(test_response,
- RS_END_THIEF_MASK,
- RS_END_THIEF_PID_BIT));
- failed = 1;
- }
- seq_puts(s, "\n");
-
- /* Cleanup */
- ret = msm_smp2p_out_close(&handle);
- UT_ASSERT_INT(ret, ==, 0);
- UT_ASSERT_PTR(handle, ==, NULL);
- ret = msm_smp2p_in_unregister(remote_pid,
- SMP2P_RLPB_ENTRY_NAME, &cb_in.nb);
- UT_ASSERT_INT(ret, ==, 0);
-
- if (!failed && !timeout)
- seq_puts(s, "\tOK\n");
- } while (0);
-
- if (failed) {
- if (handle) {
- /* send end command */
- test_request = 0;
- SMP2P_SET_RMT_CMD(test_request, SMP2P_LB_CMD_RSPIN_END);
- SMP2P_SET_RMT_DATA(test_request, lock_count);
- (void)msm_smp2p_out_write(handle, test_request);
- (void)msm_smp2p_out_close(&handle);
- }
- (void)msm_smp2p_in_unregister(remote_pid,
- SMP2P_RLPB_ENTRY_NAME, &cb_in.nb);
-
- pr_err("%s: Failed\n", __func__);
- seq_puts(s, "\tFailed\n");
- }
-}
-
-/**
- * smp2p_ut_remote_spinlock_pid - Verify remote spinlock for a processor.
- *
- * @s: Pointer to output file
- * @pid: Processor to test
- * @use_trylock: Use trylock to prevent an Apps deadlock if the
- * remote spinlock fails.
- */
-static void smp2p_ut_remote_spinlock_pid(struct seq_file *s, int pid,
- bool use_trylock)
-{
- struct smp2p_interrupt_config *int_cfg;
-
- int_cfg = smp2p_get_interrupt_config();
- if (!int_cfg) {
- seq_puts(s, "Remote processor config unavailable\n");
- return;
- }
-
- if (pid >= SMP2P_NUM_PROCS || !int_cfg[pid].is_configured)
- return;
-
- msm_smp2p_deinit_rmt_lpb_proc(pid);
- smp2p_ut_remote_spinlock_core(s, pid, use_trylock);
- msm_smp2p_init_rmt_lpb_proc(pid);
-}
-
-/**
- * smp2p_ut_remote_spinlock - Verify remote spinlock for all processors.
- *
- * @s: pointer to output file
- */
-static void smp2p_ut_remote_spinlock(struct seq_file *s)
-{
- int pid;
-
- for (pid = 0; pid < SMP2P_NUM_PROCS; ++pid)
- smp2p_ut_remote_spinlock_pid(s, pid, false);
-}
-
-/**
- * smp2p_ut_remote_spin_trylock - Verify remote trylock for all processors.
- *
- * @s: Pointer to output file
- */
-static void smp2p_ut_remote_spin_trylock(struct seq_file *s)
-{
- int pid;
-
- for (pid = 0; pid < SMP2P_NUM_PROCS; ++pid)
- smp2p_ut_remote_spinlock_pid(s, pid, true);
-}
-
-/**
- * smp2p_ut_remote_spinlock - Verify remote spinlock for all processors.
- *
- * @s: pointer to output file
- *
- * This test verifies inbound and outbound functionality for all
- * configured remote processor.
- */
-static void smp2p_ut_remote_spinlock_modem(struct seq_file *s)
-{
- smp2p_ut_remote_spinlock_pid(s, SMP2P_MODEM_PROC, false);
-}
-
-static void smp2p_ut_remote_spinlock_adsp(struct seq_file *s)
-{
- smp2p_ut_remote_spinlock_pid(s, SMP2P_AUDIO_PROC, false);
-}
-
-static void smp2p_ut_remote_spinlock_dsps(struct seq_file *s)
-{
- smp2p_ut_remote_spinlock_pid(s, SMP2P_SENSOR_PROC, false);
-}
-
-static void smp2p_ut_remote_spinlock_wcnss(struct seq_file *s)
-{
- smp2p_ut_remote_spinlock_pid(s, SMP2P_WIRELESS_PROC, false);
-}
-
-static void smp2p_ut_remote_spinlock_cdsp(struct seq_file *s)
-{
- smp2p_ut_remote_spinlock_pid(s, SMP2P_CDSP_PROC, false);
-}
-
-static void smp2p_ut_remote_spinlock_tz(struct seq_file *s)
-{
- smp2p_ut_remote_spinlock_pid(s, SMP2P_TZ_PROC, false);
-}
-
-/**
- * smp2p_ut_remote_spinlock_rpm - Verify remote spinlock.
- *
- * @s: pointer to output file
- * @remote_pid: Remote processor to test
- */
-static void smp2p_ut_remote_spinlock_rpm(struct seq_file *s)
-{
- int failed = 0;
- unsigned long flags;
- unsigned int n;
- unsigned int test_num;
- struct rpm_spinlock_test *data_ptr;
- remote_spinlock_t *smem_spinlock;
- bool have_lock;
-
- seq_printf(s, "Running %s for Apps<->RPM Test\n",
- __func__);
- do {
- smem_spinlock = smem_get_remote_spinlock();
- UT_ASSERT_PTR(smem_spinlock, !=, NULL);
-
- data_ptr = smem_alloc(SMEM_ID_VENDOR0,
- sizeof(struct rpm_spinlock_test), 0,
- SMEM_ANY_HOST_FLAG);
- UT_ASSERT_PTR(0, !=, data_ptr);
-
- /* Send start */
- writel_relaxed(0, &data_ptr->apps_lock_count);
- writel_relaxed(RPM_CMD_START, &data_ptr->apps_cmd);
-
- seq_puts(s, "\tWaiting for RPM to start test\n");
- for (n = 0; n < 1000; ++n) {
- if (readl_relaxed(&data_ptr->rpm_cmd) !=
- RPM_CMD_INVALID)
- break;
- usleep_range(1000, 1200);
- }
- if (readl_relaxed(&data_ptr->rpm_cmd) == RPM_CMD_INVALID) {
- /* timeout waiting for RPM */
- writel_relaxed(RPM_CMD_INVALID, &data_ptr->apps_cmd);
- UT_ASSERT_INT(RPM_CMD_LOCKED, !=, RPM_CMD_INVALID);
- }
-
- /* Run spinlock test */
- flags = 0;
- have_lock = false;
- for (test_num = 0; !failed && test_num < 10000; ++test_num) {
- /* acquire spinlock */
- remote_spin_lock_irqsave(smem_spinlock, flags);
- have_lock = true;
- data_ptr->apps_lock_count++;
- writel_relaxed(data_ptr->apps_lock_count,
- &data_ptr->apps_lock_count);
- writel_relaxed(RPM_CMD_LOCKED, &data_ptr->apps_cmd);
- /*
- * Ensure that the remote side sees our lock has
- * been acquired before we start polling their status.
- */
- wmb();
-
- /* verify the other side doesn't say it has the lock */
- for (n = 0; n < 1000; ++n) {
- UT_ASSERT_HEX(RPM_CMD_UNLOCKED, ==,
- readl_relaxed(&data_ptr->rpm_cmd));
- }
- if (failed)
- break;
-
- /* release spinlock */
- have_lock = false;
- writel_relaxed(RPM_CMD_UNLOCKED, &data_ptr->apps_cmd);
- /*
- * Ensure that our status-update write was committed
- * before we unlock the spinlock.
- */
- wmb();
- remote_spin_unlock_irqrestore(smem_spinlock, flags);
- }
- if (have_lock)
- remote_spin_unlock_irqrestore(smem_spinlock, flags);
-
- /* End test */
- writel_relaxed(RPM_CMD_INVALID, &data_ptr->apps_cmd);
- seq_printf(s, "\tLocked spinlock local %u remote %u\n",
- readl_relaxed(&data_ptr->apps_lock_count),
- readl_relaxed(&data_ptr->rpm_lock_count));
-
- if (!failed)
- seq_puts(s, "\tOK\n");
- } while (0);
-
- if (failed) {
- pr_err("%s: Failed\n", __func__);
- seq_puts(s, "\tFailed\n");
- }
-}
-
-struct rmt_spinlock_work_item {
- struct work_struct work;
- struct completion try_lock;
- struct completion locked;
- bool has_locked;
-};
-
-static void ut_remote_spinlock_ssr_worker(struct work_struct *work)
-{
- remote_spinlock_t *smem_spinlock;
- unsigned long flags;
- struct rmt_spinlock_work_item *work_item =
- container_of(work, struct rmt_spinlock_work_item, work);
-
- work_item->has_locked = false;
- complete(&work_item->try_lock);
- smem_spinlock = smem_get_remote_spinlock();
- if (!smem_spinlock) {
- pr_err("%s Failed\n", __func__);
- return;
- }
-
- remote_spin_lock_irqsave(smem_spinlock, flags);
- remote_spin_unlock_irqrestore(smem_spinlock, flags);
- work_item->has_locked = true;
- complete(&work_item->locked);
-}
-
-/**
- * smp2p_ut_remote_spinlock_ssr - Verify remote spinlock.
- *
- * @s: pointer to output file
- */
-static void smp2p_ut_remote_spinlock_ssr(struct seq_file *s)
-{
- int failed = 0;
- unsigned long flags;
- remote_spinlock_t *smem_spinlock;
- int spinlock_owner = 0;
-
- struct workqueue_struct *ws = NULL;
- struct rmt_spinlock_work_item work_item = { .has_locked = false };
-
- seq_printf(s, " Running %s Test\n",
- __func__);
- do {
- smem_spinlock = smem_get_remote_spinlock();
- UT_ASSERT_PTR(smem_spinlock, !=, NULL);
-
- ws = create_singlethread_workqueue("ut_remote_spinlock_ssr");
- UT_ASSERT_PTR(ws, !=, NULL);
- INIT_WORK(&work_item.work, ut_remote_spinlock_ssr_worker);
- init_completion(&work_item.try_lock);
- init_completion(&work_item.locked);
-
- remote_spin_lock_irqsave(smem_spinlock, flags);
- /* Unlock local spin lock and hold HW spinlock */
- spin_unlock_irqrestore(&((smem_spinlock)->local), flags);
-
- queue_work(ws, &work_item.work);
- UT_ASSERT_INT(
- (int)wait_for_completion_timeout(
- &work_item.try_lock, HZ * 2), >, 0);
- UT_ASSERT_INT((int)work_item.has_locked, ==, 0);
- spinlock_owner = remote_spin_owner(smem_spinlock);
- UT_ASSERT_INT(spinlock_owner, ==, SMEM_APPS);
- remote_spin_release_all(SMEM_APPS);
-
- UT_ASSERT_INT(
- (int)wait_for_completion_timeout(
- &work_item.locked, HZ * 2), >, 0);
-
- if (!failed)
- seq_puts(s, "\tOK\n");
- } while (0);
-
- if (failed) {
- pr_err("%s: Failed\n", __func__);
- seq_puts(s, "\tFailed\n");
- }
-}
-
-/**
- * smp2p_ut_remote_spinlock_track_core - Verify remote spinlock.
- *
- * @s: Pointer to output file
- * @remote_pid: Remote processor to test
- *
- * This test has the remote subsystem grab the lock, and then has the local
- * subsystem attempt to grab the lock using the trylock() API. It then verifies
- * that the ID in the hw_spinlocks array matches the owner of the lock.
- */
-static void smp2p_ut_remote_spinlock_track_core(struct seq_file *s,
- int remote_pid)
-{
- int failed = 0;
- struct msm_smp2p_out *handle = NULL;
- int ret;
- uint32_t test_request;
- uint32_t test_response;
- struct mock_cb_data cb_out;
- struct mock_cb_data cb_in;
- unsigned long flags;
- int stored_value;
- remote_spinlock_t *smem_spinlock;
-
- seq_printf(s, "Running %s for '%s' remote pid %d\n",
- __func__, smp2p_pid_to_name(remote_pid), remote_pid);
-
- cb_out.initialized = false;
- cb_in.initialized = false;
- mock_cb_data_init(&cb_out);
- mock_cb_data_init(&cb_in);
- do {
- smem_spinlock = smem_get_remote_spinlock();
- UT_ASSERT_PTR(smem_spinlock, !=, NULL);
-
- /* Open output entry */
- ret = msm_smp2p_out_open(remote_pid, SMP2P_RLPB_ENTRY_NAME,
- &cb_out.nb, &handle);
- UT_ASSERT_INT(ret, ==, 0);
- UT_ASSERT_INT(
- (int)wait_for_completion_timeout(
- &cb_out.cb_completion, HZ * 2),
- >, 0);
- UT_ASSERT_INT(cb_out.cb_count, ==, 1);
- UT_ASSERT_INT(cb_out.event_open, ==, 1);
-
- /* Open inbound entry */
- ret = msm_smp2p_in_register(remote_pid, SMP2P_RLPB_ENTRY_NAME,
- &cb_in.nb);
- UT_ASSERT_INT(ret, ==, 0);
- UT_ASSERT_INT(
- (int)wait_for_completion_timeout(
- &cb_in.cb_completion, HZ * 2),
- >, 0);
- UT_ASSERT_INT(cb_in.cb_count, ==, 1);
- UT_ASSERT_INT(cb_in.event_open, ==, 1);
-
- /* Send start */
- mock_cb_data_reset(&cb_in);
- mock_cb_data_reset(&cb_out);
- test_request = 0x0;
- SMP2P_SET_RMT_CMD_TYPE_REQ(test_request);
- SMP2P_SET_RMT_CMD(test_request, SMP2P_LB_CMD_RSPIN_START);
- SMP2P_SET_RMT_DATA(test_request, 0x0);
- ret = msm_smp2p_out_write(handle, test_request);
- UT_ASSERT_INT(ret, ==, 0);
-
- UT_ASSERT_INT(
- (int)wait_for_completion_timeout(
- &cb_in.cb_completion, HZ * 2),
- >, 0);
- UT_ASSERT_INT(cb_in.cb_count, ==, 1);
- UT_ASSERT_INT(cb_in.event_entry_update, ==, 1);
- ret = msm_smp2p_in_read(remote_pid, SMP2P_RLPB_ENTRY_NAME,
- &test_response);
- UT_ASSERT_INT(ret, ==, 0);
-
- test_response = SMP2P_GET_RMT_CMD(test_response);
- if (test_response != SMP2P_LB_CMD_RSPIN_LOCKED &&
- test_response != SMP2P_LB_CMD_RSPIN_UNLOCKED) {
- /* invalid response from remote - abort test */
- test_request = 0x0;
- SMP2P_SET_RMT_CMD_TYPE(test_request, 1);
- SMP2P_SET_RMT_CMD(test_request, SMP2P_LB_CMD_RSPIN_END);
- SMP2P_SET_RMT_DATA(test_request, 0x0);
- ret = msm_smp2p_out_write(handle, test_request);
- UT_ASSERT_HEX(SMP2P_LB_CMD_RSPIN_LOCKED, ==,
- test_response);
- }
-
- /* Run spinlock test */
- flags = 0;
- test_request = 0x0;
- SMP2P_SET_RMT_CMD_TYPE_REQ(test_request);
-
- /* try to acquire spinlock */
- remote_spin_trylock_irqsave(smem_spinlock, flags);
- /*
- * Need to check against the locking token (PID + 1)
- * because the remote_spin_owner() API only returns the
- * PID.
- */
- stored_value = remote_spin_get_hw_spinlocks_element(
- smem_spinlock);
- UT_ASSERT_INT(stored_value, ==,
- remote_spin_owner(smem_spinlock) + 1);
- UT_ASSERT_INT(stored_value, ==, remote_pid + 1);
-
- /* End test */
- test_request = 0x0;
- SMP2P_SET_RMT_CMD(test_request, SMP2P_LB_CMD_RSPIN_END);
- SMP2P_SET_RMT_DATA(test_request, 0x0);
- (void)msm_smp2p_out_write(handle, test_request);
-
- /* Cleanup */
- ret = msm_smp2p_out_close(&handle);
- UT_ASSERT_INT(ret, ==, 0);
- UT_ASSERT_PTR(handle, ==, NULL);
- ret = msm_smp2p_in_unregister(remote_pid,
- SMP2P_RLPB_ENTRY_NAME, &cb_in.nb);
- UT_ASSERT_INT(ret, ==, 0);
-
- if (!failed)
- seq_puts(s, "\tOK\n");
- } while (0);
-
- if (failed) {
- if (handle) {
- /* send end command */
- test_request = 0x0;
- SMP2P_SET_RMT_CMD(test_request, SMP2P_LB_CMD_RSPIN_END);
- SMP2P_SET_RMT_DATA(test_request, 0x0);
- (void)msm_smp2p_out_write(handle, test_request);
- (void)msm_smp2p_out_close(&handle);
- }
- (void)msm_smp2p_in_unregister(remote_pid,
- SMP2P_RLPB_ENTRY_NAME, &cb_in.nb);
-
- pr_err("%s: Failed\n", __func__);
- seq_puts(s, "\tFailed\n");
- }
-}
-
-/**
- * smp2p_ut_remote_spinlock_track - Verify PID tracking for modem.
- *
- * @s: Pointer to output file
- * @pid: The processor to test
- */
-static void smp2p_ut_remote_spinlock_track(struct seq_file *s, int pid)
-{
- struct smp2p_interrupt_config *int_cfg;
-
- int_cfg = smp2p_get_interrupt_config();
- if (!int_cfg) {
- seq_puts(s, "Remote processor config unavailable\n");
- return;
- }
-
- if (pid >= SMP2P_NUM_PROCS || !int_cfg[pid].is_configured)
- return;
-
- msm_smp2p_deinit_rmt_lpb_proc(pid);
- smp2p_ut_remote_spinlock_track_core(s, pid);
- msm_smp2p_init_rmt_lpb_proc(pid);
-}
-
-/**
- * smp2p_ut_remote_spinlock_track - Verify PID tracking for all processors.
- *
- * @s: Pointer to output file
- *
- * This test verifies PID tracking for all configured remote processors.
- */
-static void smp2p_ut_remote_spinlock_track_modem(struct seq_file *s)
-{
- smp2p_ut_remote_spinlock_track(s, SMP2P_MODEM_PROC);
-}
-
-static void smp2p_ut_remote_spinlock_track_adsp(struct seq_file *s)
-{
- smp2p_ut_remote_spinlock_track(s, SMP2P_AUDIO_PROC);
-}
-
-static void smp2p_ut_remote_spinlock_track_dsps(struct seq_file *s)
-{
- smp2p_ut_remote_spinlock_track(s, SMP2P_SENSOR_PROC);
-}
-
-static void smp2p_ut_remote_spinlock_track_wcnss(struct seq_file *s)
-{
- smp2p_ut_remote_spinlock_track(s, SMP2P_WIRELESS_PROC);
-}
-
-static void smp2p_ut_remote_spinlock_track_cdsp(struct seq_file *s)
-{
- smp2p_ut_remote_spinlock_track(s, SMP2P_CDSP_PROC);
-}
-
-static void smp2p_ut_remote_spinlock_track_tz(struct seq_file *s)
-{
- smp2p_ut_remote_spinlock_track(s, SMP2P_TZ_PROC);
-}
-
-static int __init smp2p_debugfs_init(void)
-{
- /*
- * Add Unit Test entries.
- *
- * The idea with unit tests is that you can run all of them
- * from ADB shell by doing:
- * adb shell
- * cat ut*
- *
- * And if particular tests fail, you can then repeatedly run the
- * failing tests as you debug and resolve the failing test.
- */
- smp2p_debug_create("ut_remote_spinlock",
- smp2p_ut_remote_spinlock);
- smp2p_debug_create("ut_remote_spin_trylock",
- smp2p_ut_remote_spin_trylock);
- smp2p_debug_create("ut_remote_spinlock_modem",
- smp2p_ut_remote_spinlock_modem);
- smp2p_debug_create("ut_remote_spinlock_adsp",
- smp2p_ut_remote_spinlock_adsp);
- smp2p_debug_create("ut_remote_spinlock_dsps",
- smp2p_ut_remote_spinlock_dsps);
- smp2p_debug_create("ut_remote_spinlock_wcnss",
- smp2p_ut_remote_spinlock_wcnss);
- smp2p_debug_create("ut_remote_spinlock_cdsp",
- smp2p_ut_remote_spinlock_cdsp);
- smp2p_debug_create("ut_remote_spinlock_tz",
- smp2p_ut_remote_spinlock_tz);
- smp2p_debug_create("ut_remote_spinlock_rpm",
- smp2p_ut_remote_spinlock_rpm);
- smp2p_debug_create_u32("ut_remote_spinlock_time",
- &ut_remote_spinlock_run_time);
- smp2p_debug_create("ut_remote_spinlock_ssr",
- &smp2p_ut_remote_spinlock_ssr);
- smp2p_debug_create("ut_remote_spinlock_track_modem",
- &smp2p_ut_remote_spinlock_track_modem);
- smp2p_debug_create("ut_remote_spinlock_track_adsp",
- &smp2p_ut_remote_spinlock_track_adsp);
- smp2p_debug_create("ut_remote_spinlock_track_dsps",
- &smp2p_ut_remote_spinlock_track_dsps);
- smp2p_debug_create("ut_remote_spinlock_track_wcnss",
- &smp2p_ut_remote_spinlock_track_wcnss);
- smp2p_debug_create("ut_remote_spinlock_track_cdsp",
- &smp2p_ut_remote_spinlock_track_cdsp);
- smp2p_debug_create("ut_remote_spinlock_track_tz",
- &smp2p_ut_remote_spinlock_track_tz);
- return 0;
-}
-module_init(smp2p_debugfs_init);
diff --git a/drivers/soc/qcom/smp2p_test.c b/drivers/soc/qcom/smp2p_test.c
deleted file mode 100644
index aa8d0c8..0000000
--- a/drivers/soc/qcom/smp2p_test.c
+++ /dev/null
@@ -1,1324 +0,0 @@
-/* drivers/soc/qcom/smp2p_test.c
- *
- * Copyright (c) 2013-2016, The Linux Foundation. All rights reserved.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 and
- * only version 2 as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- */
-#include <linux/debugfs.h>
-#include <linux/ctype.h>
-#include <linux/jiffies.h>
-#include <linux/delay.h>
-#include <linux/completion.h>
-#include <linux/module.h>
-#include <soc/qcom/subsystem_restart.h>
-#include "smp2p_private.h"
-#include "smp2p_test_common.h"
-
-/**
- * smp2p_ut_local_basic - Basic sanity test using local loopback.
- *
- * @s: pointer to output file
- *
- * This test simulates a simple write and read
- * when remote processor does not exist.
- */
-static void smp2p_ut_local_basic(struct seq_file *s)
-{
- int failed = 0;
- struct msm_smp2p_out *smp2p_obj;
- struct msm_smp2p_remote_mock *rmp = NULL;
- int ret;
- uint32_t test_request;
- uint32_t test_response = 0;
- static struct mock_cb_data cb_data;
-
- seq_printf(s, "Running %s\n", __func__);
- mock_cb_data_init(&cb_data);
- do {
- /* initialize mock edge and start opening */
- ret = smp2p_reset_mock_edge();
- UT_ASSERT_INT(ret, ==, 0);
-
- rmp = msm_smp2p_get_remote_mock();
- UT_ASSERT_PTR(rmp, !=, NULL);
-
- rmp->rx_interrupt_count = 0;
- memset(&rmp->remote_item, 0,
- sizeof(struct smp2p_smem_item));
-
- msm_smp2p_set_remote_mock_exists(false);
-
- ret = msm_smp2p_out_open(SMP2P_REMOTE_MOCK_PROC, "smp2p",
- &cb_data.nb, &smp2p_obj);
- UT_ASSERT_INT(ret, ==, 0);
-
- UT_ASSERT_INT(rmp->rx_interrupt_count, ==, 1);
- UT_ASSERT_INT(cb_data.cb_count, ==, 0);
- rmp->rx_interrupt_count = 0;
-
- /* simulate response from remote side */
- rmp->remote_item.header.magic = SMP2P_MAGIC;
- SMP2P_SET_LOCAL_PID(
- rmp->remote_item.header.rem_loc_proc_id,
- SMP2P_REMOTE_MOCK_PROC);
- SMP2P_SET_REMOTE_PID(
- rmp->remote_item.header.rem_loc_proc_id,
- SMP2P_APPS_PROC);
- SMP2P_SET_VERSION(
- rmp->remote_item.header.feature_version, 1);
- SMP2P_SET_FEATURES(
- rmp->remote_item.header.feature_version, 0);
- SMP2P_SET_ENT_TOTAL(
- rmp->remote_item.header.valid_total_ent, SMP2P_MAX_ENTRY);
- SMP2P_SET_ENT_VALID(
- rmp->remote_item.header.valid_total_ent, 0);
- rmp->remote_item.header.flags = 0x0;
- msm_smp2p_set_remote_mock_exists(true);
- rmp->tx_interrupt();
-
- /* verify port was opened */
- UT_ASSERT_INT(
- (int)wait_for_completion_timeout(
- &cb_data.cb_completion, HZ / 2), >, 0);
- UT_ASSERT_INT(cb_data.cb_count, ==, 1);
- UT_ASSERT_INT(cb_data.event_open, ==, 1);
- UT_ASSERT_INT(rmp->rx_interrupt_count, ==, 2);
-
- /* do write (test outbound entries) */
- rmp->rx_interrupt_count = 0;
- test_request = 0xC0DE;
- ret = msm_smp2p_out_write(smp2p_obj, test_request);
- UT_ASSERT_INT(ret, ==, 0);
- UT_ASSERT_INT(rmp->rx_interrupt_count, ==, 1);
-
- /* do read (test inbound entries) */
- ret = msm_smp2p_out_read(smp2p_obj, &test_response);
- UT_ASSERT_INT(ret, ==, 0);
- UT_ASSERT_INT(test_request, ==, test_response);
-
- ret = msm_smp2p_out_close(&smp2p_obj);
- UT_ASSERT_INT(ret, ==, 0);
- UT_ASSERT_PTR(smp2p_obj, ==, 0);
-
- seq_puts(s, "\tOK\n");
- } while (0);
-
- if (failed) {
- pr_err("%s: Failed\n", __func__);
- seq_puts(s, "\tFailed\n");
- (void)msm_smp2p_out_close(&smp2p_obj);
- }
-}
-
-/**
- * smp2p_ut_local_late_open - Verify post-negotiation opening.
- *
- * @s: pointer to output file
- *
- * Verify entry creation for opening entries after negotiation is complete.
- */
-static void smp2p_ut_local_late_open(struct seq_file *s)
-{
- int failed = 0;
- struct msm_smp2p_out *smp2p_obj;
- struct msm_smp2p_remote_mock *rmp = NULL;
- int ret;
- uint32_t test_request;
- uint32_t test_response = 0;
- static struct mock_cb_data cb_data;
-
- seq_printf(s, "Running %s\n", __func__);
- mock_cb_data_init(&cb_data);
- do {
- /* initialize mock edge */
- ret = smp2p_reset_mock_edge();
- UT_ASSERT_INT(ret, ==, 0);
-
- rmp = msm_smp2p_get_remote_mock();
- UT_ASSERT_PTR(rmp, !=, NULL);
-
- rmp->rx_interrupt_count = 0;
- memset(&rmp->remote_item, 0,
- sizeof(struct smp2p_smem_item));
- rmp->remote_item.header.magic = SMP2P_MAGIC;
- SMP2P_SET_LOCAL_PID(
- rmp->remote_item.header.rem_loc_proc_id,
- SMP2P_REMOTE_MOCK_PROC);
- SMP2P_SET_REMOTE_PID(
- rmp->remote_item.header.rem_loc_proc_id,
- SMP2P_APPS_PROC);
- SMP2P_SET_VERSION(
- rmp->remote_item.header.feature_version, 1);
- SMP2P_SET_FEATURES(
- rmp->remote_item.header.feature_version, 0);
- SMP2P_SET_ENT_TOTAL(
- rmp->remote_item.header.valid_total_ent,
- SMP2P_MAX_ENTRY);
- SMP2P_SET_ENT_VALID(
- rmp->remote_item.header.valid_total_ent, 0);
- rmp->remote_item.header.flags = 0x0;
-
- msm_smp2p_set_remote_mock_exists(true);
-
- ret = msm_smp2p_out_open(SMP2P_REMOTE_MOCK_PROC, "smp2p",
- &cb_data.nb, &smp2p_obj);
- UT_ASSERT_INT(ret, ==, 0);
-
- /* verify port was opened */
- UT_ASSERT_INT(
- (int)wait_for_completion_timeout(
- &cb_data.cb_completion, HZ / 2),
- >, 0);
- UT_ASSERT_INT(cb_data.cb_count, ==, 1);
- UT_ASSERT_INT(cb_data.event_open, ==, 1);
- UT_ASSERT_INT(rmp->rx_interrupt_count, ==, 2);
-
- /* do write (test outbound entries) */
- rmp->rx_interrupt_count = 0;
- test_request = 0xC0DE;
- ret = msm_smp2p_out_write(smp2p_obj, test_request);
- UT_ASSERT_INT(ret, ==, 0);
- UT_ASSERT_INT(rmp->rx_interrupt_count, ==, 1);
-
- /* do read (test inbound entries) */
- ret = msm_smp2p_out_read(smp2p_obj, &test_response);
- UT_ASSERT_INT(ret, ==, 0);
- UT_ASSERT_INT(test_request, ==, test_response);
-
- ret = msm_smp2p_out_close(&smp2p_obj);
- UT_ASSERT_INT(ret, ==, 0);
- UT_ASSERT_PTR(smp2p_obj, ==, 0);
-
- seq_puts(s, "\tOK\n");
- } while (0);
-
- if (failed) {
- pr_err("%s: Failed\n", __func__);
- seq_puts(s, "\tFailed\n");
- (void)msm_smp2p_out_close(&smp2p_obj);
- }
-}
-
-/**
- * smp2p_ut_local_early_open - Verify pre-negotiation opening.
- *
- * @s: pointer to output file
- *
- * Verify entry creation for opening entries before negotiation is complete.
- */
-static void smp2p_ut_local_early_open(struct seq_file *s)
-{
- int failed = 0;
- struct msm_smp2p_out *smp2p_obj;
- struct msm_smp2p_remote_mock *rmp = NULL;
- struct smp2p_smem *outbound_item;
- int negotiation_state;
- int ret;
- uint32_t test_request;
- uint32_t test_response = 0;
- static struct mock_cb_data cb_data;
-
- seq_printf(s, "Running %s\n", __func__);
- mock_cb_data_init(&cb_data);
- do {
- /* initialize mock edge, but don't enable, yet */
- ret = smp2p_reset_mock_edge();
- UT_ASSERT_INT(ret, ==, 0);
-
- rmp = msm_smp2p_get_remote_mock();
- UT_ASSERT_PTR(rmp, !=, NULL);
-
- rmp->rx_interrupt_count = 0;
- memset(&rmp->remote_item, 0,
- sizeof(struct smp2p_smem_item));
- rmp->remote_item.header.magic = SMP2P_MAGIC;
- SMP2P_SET_LOCAL_PID(
- rmp->remote_item.header.rem_loc_proc_id,
- SMP2P_REMOTE_MOCK_PROC);
- SMP2P_SET_REMOTE_PID(
- rmp->remote_item.header.rem_loc_proc_id,
- SMP2P_APPS_PROC);
- SMP2P_SET_VERSION(
- rmp->remote_item.header.feature_version, 1);
- SMP2P_SET_FEATURES(
- rmp->remote_item.header.feature_version, 0);
- SMP2P_SET_ENT_TOTAL(
- rmp->remote_item.header.valid_total_ent, SMP2P_MAX_ENTRY);
- SMP2P_SET_ENT_VALID(
- rmp->remote_item.header.valid_total_ent, 0);
- rmp->remote_item.header.flags = 0x0;
-
- msm_smp2p_set_remote_mock_exists(false);
- UT_ASSERT_PTR(NULL, ==,
- smp2p_get_in_item(SMP2P_REMOTE_MOCK_PROC));
-
- /* initiate open, but verify it doesn't complete */
- ret = msm_smp2p_out_open(SMP2P_REMOTE_MOCK_PROC, "smp2p",
- &cb_data.nb, &smp2p_obj);
- UT_ASSERT_INT(ret, ==, 0);
-
- UT_ASSERT_INT(
- (int)wait_for_completion_timeout(
- &cb_data.cb_completion, HZ / 8),
- ==, 0);
- UT_ASSERT_INT(cb_data.cb_count, ==, 0);
- UT_ASSERT_INT(cb_data.event_open, ==, 0);
- UT_ASSERT_INT(rmp->rx_interrupt_count, ==, 1);
-
- outbound_item = smp2p_get_out_item(SMP2P_REMOTE_MOCK_PROC,
- &negotiation_state);
- UT_ASSERT_PTR(outbound_item, !=, NULL);
- UT_ASSERT_INT(negotiation_state, ==, SMP2P_EDGE_STATE_OPENING);
- UT_ASSERT_INT(0, ==,
- SMP2P_GET_ENT_VALID(outbound_item->valid_total_ent));
-
- /* verify that read/write don't work yet */
- rmp->rx_interrupt_count = 0;
- test_request = 0x0;
- ret = msm_smp2p_out_write(smp2p_obj, test_request);
- UT_ASSERT_INT(ret, ==, -ENODEV);
- UT_ASSERT_INT(rmp->rx_interrupt_count, ==, 0);
-
- ret = msm_smp2p_out_read(smp2p_obj, &test_response);
- UT_ASSERT_INT(ret, ==, -ENODEV);
-
- /* allocate remote entry and verify open */
- msm_smp2p_set_remote_mock_exists(true);
- rmp->tx_interrupt();
-
- UT_ASSERT_INT(
- (int)wait_for_completion_timeout(
- &cb_data.cb_completion, HZ / 2),
- >, 0);
- UT_ASSERT_INT(cb_data.cb_count, ==, 1);
- UT_ASSERT_INT(cb_data.event_open, ==, 1);
- UT_ASSERT_INT(rmp->rx_interrupt_count, ==, 2);
-
- /* do write (test outbound entries) */
- rmp->rx_interrupt_count = 0;
- test_request = 0xC0DE;
- ret = msm_smp2p_out_write(smp2p_obj, test_request);
- UT_ASSERT_INT(ret, ==, 0);
- UT_ASSERT_INT(rmp->rx_interrupt_count, ==, 1);
-
- /* do read (test inbound entries) */
- ret = msm_smp2p_out_read(smp2p_obj, &test_response);
- UT_ASSERT_INT(ret, ==, 0);
- UT_ASSERT_INT(test_request, ==, test_response);
-
- ret = msm_smp2p_out_close(&smp2p_obj);
- UT_ASSERT_INT(ret, ==, 0);
- UT_ASSERT_PTR(smp2p_obj, ==, 0);
-
- seq_puts(s, "\tOK\n");
- } while (0);
-
- if (failed) {
- pr_err("%s: Failed\n", __func__);
- seq_puts(s, "\tFailed\n");
- (void)msm_smp2p_out_close(&smp2p_obj);
- }
-}
-
-/**
- * smp2p_ut_mock_loopback - Exercise the remote loopback using remote mock.
- *
- * @s: pointer to output file
- *
- * This test exercises the remote loopback code using
- * remote mock object. The remote mock object simulates the remote
- * processor sending remote loopback commands to the local processor.
- */
-static void smp2p_ut_mock_loopback(struct seq_file *s)
-{
- int failed = 0;
- struct msm_smp2p_remote_mock *rmp = NULL;
- int ret;
- uint32_t test_request = 0;
- uint32_t test_response = 0;
- struct msm_smp2p_out *local;
-
- seq_printf(s, "Running %s\n", __func__);
- do {
- /* Initialize the mock edge */
- ret = smp2p_reset_mock_edge();
- UT_ASSERT_INT(ret, ==, 0);
-
- rmp = msm_smp2p_get_remote_mock();
- UT_ASSERT_PTR(rmp, !=, NULL);
-
- memset(&rmp->remote_item, 0,
- sizeof(struct smp2p_smem_item));
- rmp->remote_item.header.magic = SMP2P_MAGIC;
- SMP2P_SET_LOCAL_PID(
- rmp->remote_item.header.rem_loc_proc_id,
- SMP2P_REMOTE_MOCK_PROC);
- SMP2P_SET_REMOTE_PID(
- rmp->remote_item.header.rem_loc_proc_id,
- SMP2P_APPS_PROC);
- SMP2P_SET_VERSION(
- rmp->remote_item.header.feature_version, 1);
- SMP2P_SET_FEATURES(
- rmp->remote_item.header.feature_version, 0);
- SMP2P_SET_ENT_TOTAL(
- rmp->remote_item.header.valid_total_ent, SMP2P_MAX_ENTRY);
- SMP2P_SET_ENT_VALID(
- rmp->remote_item.header.valid_total_ent, 1);
- rmp->remote_item.header.flags = 0x0;
- msm_smp2p_set_remote_mock_exists(true);
-
- /* Create test entry and attach loopback server */
- rmp->rx_interrupt_count = 0;
- reinit_completion(&rmp->cb_completion);
- strlcpy(rmp->remote_item.entries[0].name, "smp2p",
- SMP2P_MAX_ENTRY_NAME);
- rmp->remote_item.entries[0].entry = 0;
- rmp->tx_interrupt();
-
- local = msm_smp2p_init_rmt_lpb_proc(SMP2P_REMOTE_MOCK_PROC);
- UT_ASSERT_INT(
- (int)wait_for_completion_timeout(
- &rmp->cb_completion, HZ / 2),
- >, 0);
- UT_ASSERT_INT(rmp->rx_interrupt_count, ==, 2);
-
- /* Send Echo Command */
- rmp->rx_interrupt_count = 0;
- reinit_completion(&rmp->cb_completion);
- SMP2P_SET_RMT_CMD_TYPE(test_request, 1);
- SMP2P_SET_RMT_CMD(test_request, SMP2P_LB_CMD_ECHO);
- SMP2P_SET_RMT_DATA(test_request, 10);
- rmp->remote_item.entries[0].entry = test_request;
- rmp->tx_interrupt();
- UT_ASSERT_INT(
- (int)wait_for_completion_timeout(
- &rmp->cb_completion, HZ / 2),
- >, 0);
-
- /* Verify Echo Response */
- UT_ASSERT_INT(rmp->rx_interrupt_count, ==, 1);
- ret = msm_smp2p_out_read(local,
- &test_response);
- UT_ASSERT_INT(ret, ==, 0);
- test_response = SMP2P_GET_RMT_DATA(test_response);
- UT_ASSERT_INT(test_response, ==, 10);
-
- /* Send PINGPONG command */
- test_request = 0;
- test_response = 0;
- rmp->rx_interrupt_count = 0;
- reinit_completion(&rmp->cb_completion);
- SMP2P_SET_RMT_CMD_TYPE(test_request, 1);
- SMP2P_SET_RMT_CMD(test_request, SMP2P_LB_CMD_PINGPONG);
- SMP2P_SET_RMT_DATA(test_request, 10);
- rmp->remote_item.entries[0].entry = test_request;
- rmp->tx_interrupt();
- UT_ASSERT_INT(
- (int)wait_for_completion_timeout(
- &rmp->cb_completion, HZ / 2),
- >, 0);
-
- /* Verify PINGPONG Response */
- UT_ASSERT_INT(rmp->rx_interrupt_count, ==, 1);
- ret = msm_smp2p_out_read(local, &test_response);
- UT_ASSERT_INT(ret, ==, 0);
- test_response = SMP2P_GET_RMT_DATA(test_response);
- UT_ASSERT_INT(test_response, ==, 9);
-
- /* Send CLEARALL command */
- test_request = 0;
- test_response = 0;
- rmp->rx_interrupt_count = 0;
- reinit_completion(&rmp->cb_completion);
- SMP2P_SET_RMT_CMD_TYPE(test_request, 1);
- SMP2P_SET_RMT_CMD(test_request, SMP2P_LB_CMD_CLEARALL);
- SMP2P_SET_RMT_DATA(test_request, 10);
- rmp->remote_item.entries[0].entry = test_request;
- rmp->tx_interrupt();
- UT_ASSERT_INT(
- (int)wait_for_completion_timeout(
- &rmp->cb_completion, HZ / 2),
- >, 0);
-
- /* Verify CLEARALL response */
- UT_ASSERT_INT(rmp->rx_interrupt_count, ==, 1);
- ret = msm_smp2p_out_read(local, &test_response);
- UT_ASSERT_INT(ret, ==, 0);
- test_response = SMP2P_GET_RMT_DATA(test_response);
- UT_ASSERT_INT(test_response, ==, 0);
-
- ret = msm_smp2p_deinit_rmt_lpb_proc(SMP2P_REMOTE_MOCK_PROC);
- UT_ASSERT_INT(ret, ==, 0);
- seq_puts(s, "\tOK\n");
- } while (0);
-
- if (failed) {
- pr_err("%s: Failed\n", __func__);
- seq_puts(s, "\tFailed\n");
- msm_smp2p_deinit_rmt_lpb_proc(SMP2P_REMOTE_MOCK_PROC);
- }
-}
-
-/**
- * smp2p_ut_remote_inout_core - Verify inbound/outbound functionality.
- *
- * @s: pointer to output file
- * @remote_pid: Remote processor to test
- *
- * This test verifies inbound/outbound functionality for the remote processor.
- */
-static void smp2p_ut_remote_inout_core(struct seq_file *s, int remote_pid)
-{
- int failed = 0;
- struct msm_smp2p_out *handle;
- int ret;
- uint32_t test_request;
- uint32_t test_response = 0;
- static struct mock_cb_data cb_out;
- static struct mock_cb_data cb_in;
-
- seq_printf(s, "Running %s for '%s' remote pid %d\n",
- __func__, smp2p_pid_to_name(remote_pid), remote_pid);
- mock_cb_data_init(&cb_out);
- mock_cb_data_init(&cb_in);
- do {
- /* Open output entry */
- ret = msm_smp2p_out_open(remote_pid, "smp2p",
- &cb_out.nb, &handle);
- UT_ASSERT_INT(ret, ==, 0);
- UT_ASSERT_INT(
- (int)wait_for_completion_timeout(
- &cb_out.cb_completion, HZ / 2),
- >, 0);
- UT_ASSERT_INT(cb_out.cb_count, ==, 1);
- UT_ASSERT_INT(cb_out.event_open, ==, 1);
-
- /* Open inbound entry */
- ret = msm_smp2p_in_register(remote_pid, "smp2p",
- &cb_in.nb);
- UT_ASSERT_INT(ret, ==, 0);
- UT_ASSERT_INT(
- (int)wait_for_completion_timeout(
- &cb_in.cb_completion, HZ / 2),
- >, 0);
- UT_ASSERT_INT(cb_in.cb_count, ==, 1);
- UT_ASSERT_INT(cb_in.event_open, ==, 1);
-
- /* Write an echo request */
- mock_cb_data_reset(&cb_out);
- mock_cb_data_reset(&cb_in);
- test_request = 0x0;
- SMP2P_SET_RMT_CMD_TYPE(test_request, 1);
- SMP2P_SET_RMT_CMD(test_request, SMP2P_LB_CMD_ECHO);
- SMP2P_SET_RMT_DATA(test_request, 0xAA55);
- ret = msm_smp2p_out_write(handle, test_request);
- UT_ASSERT_INT(ret, ==, 0);
-
- /* Verify inbound reply */
- UT_ASSERT_INT(
- (int)wait_for_completion_timeout(
- &cb_in.cb_completion, HZ / 2),
- >, 0);
- UT_ASSERT_INT(cb_in.cb_count, ==, 1);
- UT_ASSERT_INT(cb_in.event_entry_update, ==, 1);
- UT_ASSERT_INT(SMP2P_GET_RMT_DATA(
- cb_in.entry_data.current_value), ==, 0xAA55);
-
- ret = msm_smp2p_in_read(remote_pid, "smp2p", &test_response);
- UT_ASSERT_INT(ret, ==, 0);
- UT_ASSERT_INT(0, ==, SMP2P_GET_RMT_CMD_TYPE(test_response));
- UT_ASSERT_INT(SMP2P_LB_CMD_ECHO, ==,
- SMP2P_GET_RMT_CMD(test_response));
- UT_ASSERT_INT(0xAA55, ==, SMP2P_GET_RMT_DATA(test_response));
-
- /* Write a clear all request */
- mock_cb_data_reset(&cb_in);
- test_request = 0x0;
- SMP2P_SET_RMT_CMD_TYPE(test_request, 1);
- SMP2P_SET_RMT_CMD(test_request, SMP2P_LB_CMD_CLEARALL);
- SMP2P_SET_RMT_DATA(test_request, 0xAA55);
- ret = msm_smp2p_out_write(handle, test_request);
- UT_ASSERT_INT(ret, ==, 0);
-
- /* Verify inbound reply */
- UT_ASSERT_INT(
- (int)wait_for_completion_timeout(
- &cb_in.cb_completion, HZ / 2),
- >, 0);
- UT_ASSERT_INT(cb_in.cb_count, ==, 1);
- UT_ASSERT_INT(cb_in.event_entry_update, ==, 1);
- UT_ASSERT_INT(SMP2P_GET_RMT_DATA(
- cb_in.entry_data.current_value), ==, 0x0000);
-
- ret = msm_smp2p_in_read(remote_pid, "smp2p", &test_response);
- UT_ASSERT_INT(ret, ==, 0);
- UT_ASSERT_INT(0, ==, SMP2P_GET_RMT_CMD_TYPE(test_response));
- UT_ASSERT_INT(0x0000, ==, SMP2P_GET_RMT_DATA(test_response));
-
- /* Write a decrement request */
- mock_cb_data_reset(&cb_in);
- test_request = 0x0;
- SMP2P_SET_RMT_CMD_TYPE(test_request, 1);
- SMP2P_SET_RMT_CMD(test_request, SMP2P_LB_CMD_PINGPONG);
- SMP2P_SET_RMT_DATA(test_request, 0xAA55);
- ret = msm_smp2p_out_write(handle, test_request);
- UT_ASSERT_INT(ret, ==, 0);
-
- /* Verify inbound reply */
- UT_ASSERT_INT(
- (int)wait_for_completion_timeout(
- &cb_in.cb_completion, HZ / 2),
- >, 0);
- UT_ASSERT_INT(cb_in.cb_count, ==, 1);
- UT_ASSERT_INT(cb_in.event_entry_update, ==, 1);
- UT_ASSERT_INT(SMP2P_GET_RMT_DATA(
- cb_in.entry_data.current_value), ==, 0xAA54);
-
- ret = msm_smp2p_in_read(remote_pid, "smp2p", &test_response);
- UT_ASSERT_INT(ret, ==, 0);
- UT_ASSERT_INT(0, ==, SMP2P_GET_RMT_CMD_TYPE(test_response));
- UT_ASSERT_INT(SMP2P_LB_CMD_PINGPONG, ==,
- SMP2P_GET_RMT_CMD(test_response));
- UT_ASSERT_INT(0xAA54, ==, SMP2P_GET_RMT_DATA(test_response));
-
- /* Test the ignore flag */
- mock_cb_data_reset(&cb_in);
- test_request = 0x0;
- SMP2P_SET_RMT_CMD_TYPE(test_request, 1);
- SMP2P_SET_RMT_CMD(test_request, SMP2P_RLPB_IGNORE);
- SMP2P_SET_RMT_DATA(test_request, 0xAA55);
- ret = msm_smp2p_out_write(handle, test_request);
- UT_ASSERT_INT(ret, ==, 0);
-
- UT_ASSERT_INT(
- (int)wait_for_completion_timeout(
- &cb_in.cb_completion, HZ / 2),
- ==, 0);
- UT_ASSERT_INT(cb_in.cb_count, ==, 0);
- UT_ASSERT_INT(cb_in.event_entry_update, ==, 0);
- ret = msm_smp2p_in_read(remote_pid, "smp2p", &test_response);
- UT_ASSERT_INT(ret, ==, 0);
- UT_ASSERT_INT(0xAA54, ==, SMP2P_GET_RMT_DATA(test_response));
-
- /* Cleanup */
- ret = msm_smp2p_out_close(&handle);
- UT_ASSERT_INT(ret, ==, 0);
- UT_ASSERT_PTR(handle, ==, 0);
- ret = msm_smp2p_in_unregister(remote_pid, "smp2p", &cb_in.nb);
- UT_ASSERT_INT(ret, ==, 0);
-
- seq_puts(s, "\tOK\n");
- } while (0);
-
- if (failed) {
- if (handle)
- (void)msm_smp2p_out_close(&handle);
- (void)msm_smp2p_in_unregister(remote_pid, "smp2p", &cb_in.nb);
-
- pr_err("%s: Failed\n", __func__);
- seq_puts(s, "\tFailed\n");
- }
-}
-
-/**
- * smp2p_ut_remote_inout - Verify inbound/outbound functionality for all.
- *
- * @s: pointer to output file
- *
- * This test verifies inbound and outbound functionality for all
- * configured remote processor.
- */
-static void smp2p_ut_remote_inout(struct seq_file *s)
-{
- struct smp2p_interrupt_config *int_cfg;
- int pid;
-
- int_cfg = smp2p_get_interrupt_config();
- if (!int_cfg) {
- seq_puts(s, "Remote processor config unavailable\n");
- return;
- }
-
- for (pid = 0; pid < SMP2P_NUM_PROCS; ++pid) {
- if (!int_cfg[pid].is_configured)
- continue;
-
- msm_smp2p_deinit_rmt_lpb_proc(pid);
- smp2p_ut_remote_inout_core(s, pid);
- msm_smp2p_init_rmt_lpb_proc(pid);
- }
-}
-
-/**
- * smp2p_ut_remote_out_max_entries_core - Verify open functionality.
- *
- * @s: pointer to output file
- * @remote_pid: Remote processor for which the test is executed.
- *
- * This test verifies open functionality by creating maximum outbound entries.
- */
-static void smp2p_ut_remote_out_max_entries_core(struct seq_file *s,
- int remote_pid)
-{
- int j = 0;
- int failed = 0;
- struct msm_smp2p_out *handle[SMP2P_MAX_ENTRY];
- int ret;
- static struct mock_cb_data cb_out[SMP2P_MAX_ENTRY];
- char entry_name[SMP2P_MAX_ENTRY_NAME];
- int num_created;
-
- seq_printf(s, "Running %s for '%s' remote pid %d\n",
- __func__, smp2p_pid_to_name(remote_pid), remote_pid);
-
- for (j = 0; j < SMP2P_MAX_ENTRY; j++) {
- handle[j] = NULL;
- mock_cb_data_init(&cb_out[j]);
- }
-
- do {
- num_created = 0;
- for (j = 0; j < SMP2P_MAX_ENTRY; j++) {
- /* Open as many output entries as possible */
- scnprintf((char *)entry_name, SMP2P_MAX_ENTRY_NAME,
- "smp2p%d", j);
- ret = msm_smp2p_out_open(remote_pid, entry_name,
- &cb_out[j].nb, &handle[j]);
- if (ret == -ENOMEM)
- /* hit max number */
- break;
- UT_ASSERT_INT(ret, ==, 0);
- ++num_created;
- }
- if (failed)
- break;
-
- /* verify we created more than 1 entry */
- UT_ASSERT_INT(num_created, <=, SMP2P_MAX_ENTRY);
- UT_ASSERT_INT(num_created, >, 0);
-
- seq_puts(s, "\tOK\n");
- } while (0);
-
- if (failed) {
- pr_err("%s: Failed\n", __func__);
- seq_puts(s, "\tFailed\n");
- }
-
- /* cleanup */
- for (j = 0; j < SMP2P_MAX_ENTRY; j++)
- ret = msm_smp2p_out_close(&handle[j]);
-}
-
-/**
- * smp2p_ut_remote_out_max_entries - Verify open for all configured processors.
- *
- * @s: pointer to output file
- *
- * This test verifies creating max number of entries for
- * all configured remote processor.
- */
-static void smp2p_ut_remote_out_max_entries(struct seq_file *s)
-{
- struct smp2p_interrupt_config *int_cfg;
- int pid;
-
- int_cfg = smp2p_get_interrupt_config();
- if (!int_cfg) {
- seq_puts(s, "Remote processor config unavailable\n");
- return;
- }
-
- for (pid = 0; pid < SMP2P_NUM_PROCS; ++pid) {
- if (!int_cfg[pid].is_configured)
- continue;
-
- smp2p_ut_remote_out_max_entries_core(s, pid);
- }
-}
-
-/**
- * smp2p_ut_local_in_max_entries - Verify registering and unregistering.
- *
- * @s: pointer to output file
- *
- * This test verifies registering and unregistering for inbound entries using
- * the remote mock processor.
- */
-static void smp2p_ut_local_in_max_entries(struct seq_file *s)
-{
- int j = 0;
- int failed = 0;
- struct msm_smp2p_remote_mock *rmp = NULL;
- int ret;
- static struct mock_cb_data cb_in[SMP2P_MAX_ENTRY];
- static struct mock_cb_data cb_out;
-
- seq_printf(s, "Running %s\n", __func__);
-
- for (j = 0; j < SMP2P_MAX_ENTRY; j++)
- mock_cb_data_init(&cb_in[j]);
-
- mock_cb_data_init(&cb_out);
-
- do {
- /* Initialize mock edge */
- ret = smp2p_reset_mock_edge();
- UT_ASSERT_INT(ret, ==, 0);
-
- rmp = msm_smp2p_get_remote_mock();
- UT_ASSERT_PTR(rmp, !=, NULL);
-
- rmp->rx_interrupt_count = 0;
- memset(&rmp->remote_item, 0,
- sizeof(struct smp2p_smem_item));
- rmp->remote_item.header.magic = SMP2P_MAGIC;
- SMP2P_SET_LOCAL_PID(
- rmp->remote_item.header.rem_loc_proc_id,
- SMP2P_REMOTE_MOCK_PROC);
- SMP2P_SET_REMOTE_PID(
- rmp->remote_item.header.rem_loc_proc_id,
- SMP2P_APPS_PROC);
- SMP2P_SET_VERSION(
- rmp->remote_item.header.feature_version, 1);
- SMP2P_SET_FEATURES(
- rmp->remote_item.header.feature_version, 0);
- SMP2P_SET_ENT_TOTAL(
- rmp->remote_item.header.valid_total_ent, SMP2P_MAX_ENTRY);
- SMP2P_SET_ENT_VALID(
- rmp->remote_item.header.valid_total_ent, 0);
- rmp->remote_item.header.flags = 0x0;
- msm_smp2p_set_remote_mock_exists(true);
-
- /* Create Max Entries in the remote mock object */
- for (j = 0; j < SMP2P_MAX_ENTRY; j++) {
- scnprintf(rmp->remote_item.entries[j].name,
- SMP2P_MAX_ENTRY_NAME, "smp2p%d", j);
- rmp->remote_item.entries[j].entry = 0;
- rmp->tx_interrupt();
- }
-
- /* Register for in entries */
- for (j = 0; j < SMP2P_MAX_ENTRY; j++) {
- ret = msm_smp2p_in_register(SMP2P_REMOTE_MOCK_PROC,
- rmp->remote_item.entries[j].name,
- &(cb_in[j].nb));
- UT_ASSERT_INT(ret, ==, 0);
- UT_ASSERT_INT(
- (int)wait_for_completion_timeout(
- &(cb_in[j].cb_completion), HZ / 2),
- >, 0);
- UT_ASSERT_INT(cb_in[j].cb_count, ==, 1);
- UT_ASSERT_INT(cb_in[j].event_entry_update, ==, 0);
- }
- UT_ASSERT_INT(j, ==, SMP2P_MAX_ENTRY);
-
- /* Unregister */
- for (j = 0; j < SMP2P_MAX_ENTRY; j++) {
- ret = msm_smp2p_in_unregister(SMP2P_REMOTE_MOCK_PROC,
- rmp->remote_item.entries[j].name,
- &(cb_in[j].nb));
- UT_ASSERT_INT(ret, ==, 0);
- }
- UT_ASSERT_INT(j, ==, SMP2P_MAX_ENTRY);
-
- seq_puts(s, "\tOK\n");
- } while (0);
-
- if (failed) {
- pr_err("%s: Failed\n", __func__);
- seq_puts(s, "\tFailed\n");
-
- for (j = 0; j < SMP2P_MAX_ENTRY; j++)
- ret = msm_smp2p_in_unregister(SMP2P_REMOTE_MOCK_PROC,
- rmp->remote_item.entries[j].name,
- &(cb_in[j].nb));
- }
-}
-
-/**
- * smp2p_ut_local_in_multiple - Verify Multiple Inbound Registration.
- *
- * @s: pointer to output file
- *
- * This test verifies multiple clients registering for same inbound entries
- * using the remote mock processor.
- */
-static void smp2p_ut_local_in_multiple(struct seq_file *s)
-{
- int failed = 0;
- struct msm_smp2p_remote_mock *rmp = NULL;
- int ret;
- static struct mock_cb_data cb_in_1;
- static struct mock_cb_data cb_in_2;
- static struct mock_cb_data cb_out;
-
- seq_printf(s, "Running %s\n", __func__);
-
- mock_cb_data_init(&cb_in_1);
- mock_cb_data_init(&cb_in_2);
- mock_cb_data_init(&cb_out);
-
- do {
- /* Initialize mock edge */
- ret = smp2p_reset_mock_edge();
- UT_ASSERT_INT(ret, ==, 0);
-
- rmp = msm_smp2p_get_remote_mock();
- UT_ASSERT_PTR(rmp, !=, NULL);
-
- rmp->rx_interrupt_count = 0;
- memset(&rmp->remote_item, 0,
- sizeof(struct smp2p_smem_item));
- rmp->remote_item.header.magic = SMP2P_MAGIC;
- SMP2P_SET_LOCAL_PID(
- rmp->remote_item.header.rem_loc_proc_id,
- SMP2P_REMOTE_MOCK_PROC);
- SMP2P_SET_REMOTE_PID(
- rmp->remote_item.header.rem_loc_proc_id,
- SMP2P_APPS_PROC);
- SMP2P_SET_VERSION(
- rmp->remote_item.header.feature_version, 1);
- SMP2P_SET_FEATURES(
- rmp->remote_item.header.feature_version, 0);
- SMP2P_SET_ENT_TOTAL(
- rmp->remote_item.header.valid_total_ent, 1);
- SMP2P_SET_ENT_VALID(
- rmp->remote_item.header.valid_total_ent, 0);
- rmp->remote_item.header.flags = 0x0;
- msm_smp2p_set_remote_mock_exists(true);
-
- /* Create an Entry in the remote mock object */
- scnprintf(rmp->remote_item.entries[0].name,
- SMP2P_MAX_ENTRY_NAME, "smp2p%d", 1);
- rmp->remote_item.entries[0].entry = 0;
- rmp->tx_interrupt();
-
- /* Register multiple clients for the inbound entry */
- ret = msm_smp2p_in_register(SMP2P_REMOTE_MOCK_PROC,
- rmp->remote_item.entries[0].name,
- &cb_in_1.nb);
- UT_ASSERT_INT(ret, ==, 0);
- UT_ASSERT_INT(
- (int)wait_for_completion_timeout(
- &(cb_in_1.cb_completion), HZ / 2),
- >, 0);
- UT_ASSERT_INT(cb_in_1.cb_count, ==, 1);
- UT_ASSERT_INT(cb_in_1.event_entry_update, ==, 0);
-
- ret = msm_smp2p_in_register(SMP2P_REMOTE_MOCK_PROC,
- rmp->remote_item.entries[0].name,
- &cb_in_2.nb);
- UT_ASSERT_INT(ret, ==, 0);
- UT_ASSERT_INT(
- (int)wait_for_completion_timeout(
- &(cb_in_2.cb_completion), HZ / 2),
- >, 0);
- UT_ASSERT_INT(cb_in_2.cb_count, ==, 1);
- UT_ASSERT_INT(cb_in_2.event_entry_update, ==, 0);
-
-
- /* Unregister the clients */
- ret = msm_smp2p_in_unregister(SMP2P_REMOTE_MOCK_PROC,
- rmp->remote_item.entries[0].name,
- &(cb_in_1.nb));
- UT_ASSERT_INT(ret, ==, 0);
-
- ret = msm_smp2p_in_unregister(SMP2P_REMOTE_MOCK_PROC,
- rmp->remote_item.entries[0].name,
- &(cb_in_2.nb));
- UT_ASSERT_INT(ret, ==, 0);
-
- seq_puts(s, "\tOK\n");
- } while (0);
-
- if (failed) {
- pr_err("%s: Failed\n", __func__);
- seq_puts(s, "\tFailed\n");
-
- ret = msm_smp2p_in_unregister(SMP2P_REMOTE_MOCK_PROC,
- rmp->remote_item.entries[0].name,
- &(cb_in_1.nb));
-
- ret = msm_smp2p_in_unregister(SMP2P_REMOTE_MOCK_PROC,
- rmp->remote_item.entries[0].name,
- &(cb_in_2.nb));
- }
-}
-
-/**
- * smp2p_ut_local_ssr_ack - Verify SSR Done/ACK Feature
- *
- * @s: pointer to output file
- */
-static void smp2p_ut_local_ssr_ack(struct seq_file *s)
-{
- int failed = 0;
- struct msm_smp2p_remote_mock *rmp = NULL;
- int ret;
-
- seq_printf(s, "Running %s\n", __func__);
- do {
- struct smp2p_smem *rhdr;
- struct smp2p_smem *lhdr;
- int negotiation_state;
-
- /* initialize v1 without SMP2P_FEATURE_SSR_ACK enabled */
- ret = smp2p_reset_mock_edge();
- UT_ASSERT_INT(ret, ==, 0);
- rmp = msm_smp2p_get_remote_mock();
- UT_ASSERT_PTR(rmp, !=, NULL);
- rhdr = &rmp->remote_item.header;
-
- rmp->rx_interrupt_count = 0;
- memset(&rmp->remote_item, 0, sizeof(struct smp2p_smem_item));
- rhdr->magic = SMP2P_MAGIC;
- SMP2P_SET_LOCAL_PID(rhdr->rem_loc_proc_id,
- SMP2P_REMOTE_MOCK_PROC);
- SMP2P_SET_REMOTE_PID(rhdr->rem_loc_proc_id, SMP2P_APPS_PROC);
- SMP2P_SET_VERSION(rhdr->feature_version, 1);
- SMP2P_SET_FEATURES(rhdr->feature_version, 0);
- SMP2P_SET_ENT_TOTAL(rhdr->valid_total_ent, SMP2P_MAX_ENTRY);
- SMP2P_SET_ENT_VALID(rhdr->valid_total_ent, 0);
- rhdr->flags = 0x0;
- msm_smp2p_set_remote_mock_exists(true);
- rmp->tx_interrupt();
-
- /* verify edge is open */
- lhdr = smp2p_get_out_item(SMP2P_REMOTE_MOCK_PROC,
- &negotiation_state);
- UT_ASSERT_PTR(NULL, !=, lhdr);
- UT_ASSERT_INT(negotiation_state, ==, SMP2P_EDGE_STATE_OPENED);
- UT_ASSERT_INT(rmp->rx_interrupt_count, ==, 1);
-
- /* verify no response to ack feature */
- rmp->rx_interrupt_count = 0;
- SMP2P_SET_RESTART_DONE(rhdr->flags, 1);
- rmp->tx_interrupt();
- UT_ASSERT_INT(0, ==, SMP2P_GET_RESTART_DONE(lhdr->flags));
- UT_ASSERT_INT(0, ==, SMP2P_GET_RESTART_ACK(lhdr->flags));
- UT_ASSERT_INT(rmp->rx_interrupt_count, ==, 0);
-
- /* initialize v1 with SMP2P_FEATURE_SSR_ACK enabled */
- ret = smp2p_reset_mock_edge();
- UT_ASSERT_INT(ret, ==, 0);
- rmp = msm_smp2p_get_remote_mock();
- UT_ASSERT_PTR(rmp, !=, NULL);
- rhdr = &rmp->remote_item.header;
-
- rmp->rx_interrupt_count = 0;
- memset(&rmp->remote_item, 0, sizeof(struct smp2p_smem_item));
- rhdr->magic = SMP2P_MAGIC;
- SMP2P_SET_LOCAL_PID(rhdr->rem_loc_proc_id,
- SMP2P_REMOTE_MOCK_PROC);
- SMP2P_SET_REMOTE_PID(rhdr->rem_loc_proc_id, SMP2P_APPS_PROC);
- SMP2P_SET_VERSION(rhdr->feature_version, 1);
- SMP2P_SET_FEATURES(rhdr->feature_version,
- SMP2P_FEATURE_SSR_ACK);
- SMP2P_SET_ENT_TOTAL(rhdr->valid_total_ent, SMP2P_MAX_ENTRY);
- SMP2P_SET_ENT_VALID(rhdr->valid_total_ent, 0);
- rmp->rx_interrupt_count = 0;
- rhdr->flags = 0x0;
- msm_smp2p_set_remote_mock_exists(true);
- rmp->tx_interrupt();
-
- /* verify edge is open */
- lhdr = smp2p_get_out_item(SMP2P_REMOTE_MOCK_PROC,
- &negotiation_state);
- UT_ASSERT_PTR(NULL, !=, lhdr);
- UT_ASSERT_INT(negotiation_state, ==, SMP2P_EDGE_STATE_OPENED);
- UT_ASSERT_INT(rmp->rx_interrupt_count, ==, 1);
-
- /* verify response to ack feature */
- rmp->rx_interrupt_count = 0;
- SMP2P_SET_RESTART_DONE(rhdr->flags, 1);
- rmp->tx_interrupt();
- UT_ASSERT_INT(0, ==, SMP2P_GET_RESTART_DONE(lhdr->flags));
- UT_ASSERT_INT(1, ==, SMP2P_GET_RESTART_ACK(lhdr->flags));
- UT_ASSERT_INT(rmp->rx_interrupt_count, ==, 1);
-
- rmp->rx_interrupt_count = 0;
- SMP2P_SET_RESTART_DONE(rhdr->flags, 0);
- rmp->tx_interrupt();
- UT_ASSERT_INT(0, ==, SMP2P_GET_RESTART_DONE(lhdr->flags));
- UT_ASSERT_INT(0, ==, SMP2P_GET_RESTART_ACK(lhdr->flags));
- UT_ASSERT_INT(rmp->rx_interrupt_count, ==, 1);
-
- seq_puts(s, "\tOK\n");
- } while (0);
-
- if (failed) {
- pr_err("%s: Failed\n", __func__);
- seq_puts(s, "\tFailed\n");
- }
-}
-
-/**
- * get_ssr_name_for_proc - Retrieve an SSR name from the provided list
- *
- * @names: List of possible processor names
- * @name_len: The length of @names
- * @index: Index into @names
- *
- * Return: Pointer to the next processor name, NULL in error conditions
- */
-static char *get_ssr_name_for_proc(char * const names[], size_t name_len,
- int index)
-{
- if (index >= name_len) {
- pr_err("%s: SSR failed; check subsys name table\n",
- __func__);
- return NULL;
- }
-
- return names[index];
-}
-
-/**
- * smp2p_ut_local_ssr_ack - Verify SSR Done/ACK Feature
- *
- * @s: pointer to output file
- * @rpid: Remote processor ID
- * @int_cfg: Interrupt config
- */
-static void smp2p_ut_remotesubsys_ssr_ack(struct seq_file *s, uint32_t rpid,
- struct smp2p_interrupt_config *int_cfg)
-{
- int failed = 0;
-
- seq_printf(s, "Running %s\n", __func__);
- do {
- struct smp2p_smem *rhdr;
- struct smp2p_smem *lhdr;
- int negotiation_state;
- int name_index;
- int ret;
- uint32_t ssr_done_start;
- bool ssr_ack_enabled = false;
- bool ssr_success = false;
- char *name = NULL;
-
- static char * const mpss_names[] = {"modem", "mpss"};
- static char * const lpass_names[] = {"adsp", "lpass"};
- static char * const sensor_names[] = {"slpi", "dsps"};
- static char * const wcnss_names[] = {"wcnss"};
-
- lhdr = smp2p_get_out_item(rpid, &negotiation_state);
- UT_ASSERT_PTR(NULL, !=, lhdr);
- UT_ASSERT_INT(SMP2P_EDGE_STATE_OPENED, ==, negotiation_state);
-
- rhdr = smp2p_get_in_item(rpid);
- UT_ASSERT_PTR(NULL, !=, rhdr);
-
- /* get initial state of SSR flags */
- if (SMP2P_GET_FEATURES(rhdr->feature_version)
- & SMP2P_FEATURE_SSR_ACK)
- ssr_ack_enabled = true;
- else
- ssr_ack_enabled = false;
-
- ssr_done_start = SMP2P_GET_RESTART_DONE(rhdr->flags);
- UT_ASSERT_INT(ssr_done_start, ==,
- SMP2P_GET_RESTART_ACK(lhdr->flags));
-
- /* trigger restart */
- name_index = 0;
- while (!ssr_success) {
-
- switch (rpid) {
- case SMP2P_MODEM_PROC:
- name = get_ssr_name_for_proc(mpss_names,
- ARRAY_SIZE(mpss_names),
- name_index);
- break;
- case SMP2P_AUDIO_PROC:
- name = get_ssr_name_for_proc(lpass_names,
- ARRAY_SIZE(lpass_names),
- name_index);
- break;
- case SMP2P_SENSOR_PROC:
- name = get_ssr_name_for_proc(sensor_names,
- ARRAY_SIZE(sensor_names),
- name_index);
- break;
- case SMP2P_WIRELESS_PROC:
- name = get_ssr_name_for_proc(wcnss_names,
- ARRAY_SIZE(wcnss_names),
- name_index);
- break;
- default:
- pr_err("%s: Invalid proc ID %d given for ssr\n",
- __func__, rpid);
- }
-
- if (!name) {
- seq_puts(s, "\tSSR failed; check subsys name table\n");
- failed = true;
- break;
- }
-
- seq_printf(s, "Restarting '%s'\n", name);
- ret = subsystem_restart(name);
- if (ret == -ENODEV) {
- seq_puts(s, "\tSSR call failed\n");
- ++name_index;
- continue;
- }
- ssr_success = true;
- }
- if (failed)
- break;
-
- msleep(10*1000);
-
- /* verify ack signaling */
- if (ssr_ack_enabled) {
- ssr_done_start ^= 1;
- UT_ASSERT_INT(ssr_done_start, ==,
- SMP2P_GET_RESTART_ACK(lhdr->flags));
- UT_ASSERT_INT(ssr_done_start, ==,
- SMP2P_GET_RESTART_DONE(rhdr->flags));
- UT_ASSERT_INT(0, ==,
- SMP2P_GET_RESTART_DONE(lhdr->flags));
- seq_puts(s, "\tSSR ACK Enabled and Toggled\n");
- } else {
- UT_ASSERT_INT(0, ==,
- SMP2P_GET_RESTART_DONE(lhdr->flags));
- UT_ASSERT_INT(0, ==,
- SMP2P_GET_RESTART_ACK(lhdr->flags));
-
- UT_ASSERT_INT(0, ==,
- SMP2P_GET_RESTART_DONE(rhdr->flags));
- UT_ASSERT_INT(0, ==,
- SMP2P_GET_RESTART_ACK(rhdr->flags));
- seq_puts(s, "\tSSR ACK Disabled\n");
- }
-
- seq_puts(s, "\tOK\n");
- } while (0);
-
- if (failed) {
- pr_err("%s: Failed\n", __func__);
- seq_puts(s, "\tFailed\n");
- }
-}
-
-/**
- * smp2p_ut_remote_ssr_ack - Verify SSR Done/ACK Feature
- *
- * @s: pointer to output file
- *
- * Triggers SSR for each subsystem.
- */
-static void smp2p_ut_remote_ssr_ack(struct seq_file *s)
-{
- struct smp2p_interrupt_config *int_cfg;
- int pid;
-
- int_cfg = smp2p_get_interrupt_config();
- if (!int_cfg) {
- seq_puts(s,
- "Remote processor config unavailable\n");
- return;
- }
-
- for (pid = 0; pid < SMP2P_NUM_PROCS; ++pid) {
- if (!int_cfg[pid].is_configured)
- continue;
-
- msm_smp2p_deinit_rmt_lpb_proc(pid);
- smp2p_ut_remotesubsys_ssr_ack(s, pid, &int_cfg[pid]);
- msm_smp2p_init_rmt_lpb_proc(pid);
- }
-}
-
-static struct dentry *dent;
-
-static int debugfs_show(struct seq_file *s, void *data)
-{
- void (*show)(struct seq_file *) = s->private;
-
- show(s);
-
- return 0;
-}
-
-static int debug_open(struct inode *inode, struct file *file)
-{
- return single_open(file, debugfs_show, inode->i_private);
-}
-
-static const struct file_operations debug_ops = {
- .open = debug_open,
- .release = single_release,
- .read = seq_read,
- .llseek = seq_lseek,
-};
-
-void smp2p_debug_create(const char *name,
- void (*show)(struct seq_file *))
-{
- struct dentry *file;
-
- file = debugfs_create_file(name, 0444, dent, show, &debug_ops);
- if (!file)
- pr_err("%s: unable to create file '%s'\n", __func__, name);
-}
-
-void smp2p_debug_create_u32(const char *name, uint32_t *value)
-{
- struct dentry *file;
-
- file = debugfs_create_u32(name, 0644, dent, value);
- if (!file)
- pr_err("%s: unable to create file '%s'\n", __func__, name);
-}
-
-static int __init smp2p_debugfs_init(void)
-{
- dent = debugfs_create_dir("smp2p_test", 0);
- if (IS_ERR(dent))
- return PTR_ERR(dent);
-
- /*
- * Add Unit Test entries.
- *
- * The idea with unit tests is that you can run all of them
- * from ADB shell by doing:
- * adb shell
- * cat ut*
- *
- * And if particular tests fail, you can then repeatedly run the
- * failing tests as you debug and resolve the failing test.
- */
- smp2p_debug_create("ut_local_basic",
- smp2p_ut_local_basic);
- smp2p_debug_create("ut_local_late_open",
- smp2p_ut_local_late_open);
- smp2p_debug_create("ut_local_early_open",
- smp2p_ut_local_early_open);
- smp2p_debug_create("ut_mock_loopback",
- smp2p_ut_mock_loopback);
- smp2p_debug_create("ut_remote_inout",
- smp2p_ut_remote_inout);
- smp2p_debug_create("ut_local_in_max_entries",
- smp2p_ut_local_in_max_entries);
- smp2p_debug_create("ut_remote_out_max_entries",
- smp2p_ut_remote_out_max_entries);
- smp2p_debug_create("ut_local_in_multiple",
- smp2p_ut_local_in_multiple);
- smp2p_debug_create("ut_local_ssr_ack",
- smp2p_ut_local_ssr_ack);
- smp2p_debug_create("ut_remote_ssr_ack",
- smp2p_ut_remote_ssr_ack);
-
- return 0;
-}
-module_init(smp2p_debugfs_init);
diff --git a/drivers/soc/qcom/smp2p_test_common.h b/drivers/soc/qcom/smp2p_test_common.h
deleted file mode 100644
index 0d22fec..0000000
--- a/drivers/soc/qcom/smp2p_test_common.h
+++ /dev/null
@@ -1,214 +0,0 @@
-/* drivers/soc/qcom/smp2p_test_common.h
- *
- * Copyright (c) 2013-2014,2016 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 _SMP2P_TEST_COMMON_H_
-#define _SMP2P_TEST_COMMON_H_
-
-#include <linux/debugfs.h>
-
-/**
- * Unit test assertion for logging test cases.
- *
- * @a lval
- * @b rval
- * @cmp comparison operator
- *
- * Assertion fails if (@a cmp @b) is not true which then
- * logs the function and line number where the error occurred
- * along with the values of @a and @b.
- *
- * Assumes that the following local variables exist:
- * @s - sequential output file pointer
- * @failed - set to true if test fails
- */
-#define UT_ASSERT_INT(a, cmp, b) \
- { \
- int a_tmp = (a); \
- int b_tmp = (b); \
- if (!((a_tmp)cmp(b_tmp))) { \
- seq_printf(s, "%s:%d Fail: " #a "(%d) " #cmp " " #b "(%d)\n", \
- __func__, __LINE__, \
- a_tmp, b_tmp); \
- failed = 1; \
- break; \
- } \
- }
-
-#define UT_ASSERT_PTR(a, cmp, b) \
- { \
- void *a_tmp = (a); \
- void *b_tmp = (b); \
- if (!((a_tmp)cmp(b_tmp))) { \
- seq_printf(s, "%s:%d Fail: " #a "(%pK) " #cmp \
- " " #b "(%pK)\n", \
- __func__, __LINE__, \
- a_tmp, b_tmp); \
- failed = 1; \
- break; \
- } \
- }
-
-#define UT_ASSERT_UINT(a, cmp, b) \
- { \
- unsigned int a_tmp = (a); \
- unsigned int b_tmp = (b); \
- if (!((a_tmp)cmp(b_tmp))) { \
- seq_printf(s, "%s:%d Fail: " #a "(%u) " #cmp " " #b "(%u)\n", \
- __func__, __LINE__, \
- a_tmp, b_tmp); \
- failed = 1; \
- break; \
- } \
- }
-
-#define UT_ASSERT_HEX(a, cmp, b) \
- { \
- unsigned int a_tmp = (a); \
- unsigned int b_tmp = (b); \
- if (!((a_tmp)cmp(b_tmp))) { \
- seq_printf(s, "%s:%d Fail: " #a "(%x) " #cmp " " #b "(%x)\n", \
- __func__, __LINE__, \
- a_tmp, b_tmp); \
- failed = 1; \
- break; \
- } \
- }
-
-/**
- * In-range unit test assertion for test cases.
- *
- * @a lval
- * @minv Minimum value
- * @maxv Maximum value
- *
- * Assertion fails if @a is not on the exclusive range minv, maxv
- * ((@a < @minv) or (@a > @maxv)). In the failure case, the macro
- * logs the function and line number where the error occurred along
- * with the values of @a and @minv, @maxv.
- *
- * Assumes that the following local variables exist:
- * @s - sequential output file pointer
- * @failed - set to true if test fails
- */
-#define UT_ASSERT_INT_IN_RANGE(a, minv, maxv) \
- { \
- int a_tmp = (a); \
- int minv_tmp = (minv); \
- int maxv_tmp = (maxv); \
- if (((a_tmp) < (minv_tmp)) || ((a_tmp) > (maxv_tmp))) { \
- seq_printf(s, "%s:%d Fail: " #a "(%d) < " #minv "(%d) or " \
- #a "(%d) > " #maxv "(%d)\n", \
- __func__, __LINE__, \
- a_tmp, minv_tmp, a_tmp, maxv_tmp); \
- failed = 1; \
- break; \
- } \
- }
-
-/* Structure to track state changes for the notifier callback. */
-struct mock_cb_data {
- bool initialized;
- spinlock_t lock;
- struct notifier_block nb;
-
- /* events */
- struct completion cb_completion;
- int cb_count;
- int event_open;
- int event_entry_update;
- struct msm_smp2p_update_notif entry_data;
-};
-
-void smp2p_debug_create(const char *name, void (*show)(struct seq_file *));
-void smp2p_debug_create_u32(const char *name, uint32_t *value);
-static inline int smp2p_test_notify(struct notifier_block *self,
- unsigned long event, void *data);
-
-/**
- * Reset mock callback data to default values.
- *
- * @cb: Mock callback data
- */
-static inline void mock_cb_data_reset(struct mock_cb_data *cb)
-{
- reinit_completion(&cb->cb_completion);
- cb->cb_count = 0;
- cb->event_open = 0;
- cb->event_entry_update = 0;
- memset(&cb->entry_data, 0,
- sizeof(struct msm_smp2p_update_notif));
-}
-
-
-/**
- * Initialize mock callback data.
- *
- * @cb: Mock callback data
- */
-static inline void mock_cb_data_init(struct mock_cb_data *cb)
-{
- if (!cb->initialized) {
- init_completion(&cb->cb_completion);
- spin_lock_init(&cb->lock);
- cb->initialized = true;
- cb->nb.notifier_call = smp2p_test_notify;
- memset(&cb->entry_data, 0,
- sizeof(struct msm_smp2p_update_notif));
- }
- mock_cb_data_reset(cb);
-}
-
-/**
- * Notifier function passed into SMP2P for testing.
- *
- * @self: Pointer to calling notifier block
- * @event: Event
- * @data: Event-specific data
- * @returns: 0
- */
-static inline int smp2p_test_notify(struct notifier_block *self,
- unsigned long event, void *data)
-{
- struct mock_cb_data *cb_data_ptr;
- unsigned long flags;
-
- cb_data_ptr = container_of(self, struct mock_cb_data, nb);
-
- spin_lock_irqsave(&cb_data_ptr->lock, flags);
-
- switch (event) {
- case SMP2P_OPEN:
- ++cb_data_ptr->event_open;
- if (data) {
- cb_data_ptr->entry_data =
- *(struct msm_smp2p_update_notif *)(data);
- }
- break;
- case SMP2P_ENTRY_UPDATE:
- ++cb_data_ptr->event_entry_update;
- if (data) {
- cb_data_ptr->entry_data =
- *(struct msm_smp2p_update_notif *)(data);
- }
- break;
- default:
- pr_err("%s Unknown event\n", __func__);
- break;
- }
-
- ++cb_data_ptr->cb_count;
- complete(&cb_data_ptr->cb_completion);
- spin_unlock_irqrestore(&cb_data_ptr->lock, flags);
- return 0;
-}
-#endif /* _SMP2P_TEST_COMMON_H_ */
diff --git a/drivers/soc/qcom/tracer_pkt.c b/drivers/soc/qcom/tracer_pkt.c
index e233055..b699f2f 100644
--- a/drivers/soc/qcom/tracer_pkt.c
+++ b/drivers/soc/qcom/tracer_pkt.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2015-2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2015-2017, 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
@@ -100,7 +100,7 @@
pkt_hdr->reserved = 0;
pkt_hdr->id_valid = 0;
pkt_hdr->qdss_tracing = qdss_tracing ? true : false;
- if (pkt_priv_len > MAX_CC_WLEN * sizeof(uint32_t))
+ if (pkt_priv_len >= MAX_CC_WLEN * sizeof(uint32_t))
pkt_hdr->ccl = MAX_CC_WLEN;
else
pkt_hdr->ccl = pkt_priv_len/sizeof(uint32_t) +
diff --git a/drivers/soc/qcom/watchdog_v2.c b/drivers/soc/qcom/watchdog_v2.c
index 8bf5659..9aea6db 100644
--- a/drivers/soc/qcom/watchdog_v2.c
+++ b/drivers/soc/qcom/watchdog_v2.c
@@ -50,7 +50,7 @@
#define SCM_SET_REGSAVE_CMD 0x2
#define SCM_SVC_SEC_WDOG_DIS 0x7
#define MAX_CPU_CTX_SIZE 2048
-#define MAX_CPU_SCANDUMP_SIZE 0x10000
+#define MAX_CPU_SCANDUMP_SIZE 0x10100
static struct msm_watchdog_data *wdog_data;
diff --git a/drivers/spi/spi-geni-qcom.c b/drivers/spi/spi-geni-qcom.c
index 8dc42ac..7aaf08b 100644
--- a/drivers/spi/spi-geni-qcom.c
+++ b/drivers/spi/spi-geni-qcom.c
@@ -716,7 +716,7 @@
if (mas->cur_xfer_mode == FIFO_MODE) {
geni_se_select_mode(mas->base, FIFO_MODE);
reinit_completion(&mas->xfer_done);
- setup_fifo_params(spi_msg->spi, spi);
+ ret = setup_fifo_params(spi_msg->spi, spi);
} else if (mas->cur_xfer_mode == GSI_DMA) {
mas->num_tx_eot = 0;
mas->num_rx_eot = 0;
@@ -1199,6 +1199,7 @@
struct resource *res;
struct platform_device *wrapper_pdev;
struct device_node *wrapper_ph_node;
+ bool rt_pri;
spi = spi_alloc_master(&pdev->dev, sizeof(struct spi_geni_master));
if (!spi) {
@@ -1293,6 +1294,10 @@
goto spi_geni_probe_err;
}
+ rt_pri = of_property_read_bool(pdev->dev.of_node, "qcom,rt");
+ if (rt_pri)
+ spi->rt = true;
+
geni_mas->phys_addr = res->start;
geni_mas->size = resource_size(res);
geni_mas->base = devm_ioremap(&pdev->dev, res->start,
diff --git a/drivers/thermal/gov_low_limits.c b/drivers/thermal/gov_low_limits.c
index b75abf7..278869c 100644
--- a/drivers/thermal/gov_low_limits.c
+++ b/drivers/thermal/gov_low_limits.c
@@ -47,7 +47,7 @@
if ((tz->temperature <= trip_temp) ||
(instance->target != THERMAL_NO_TARGET
- && tz->temperature <= trip_hyst))
+ && tz->temperature < trip_hyst))
throttle = true;
else
throttle = false;
diff --git a/drivers/thermal/step_wise.c b/drivers/thermal/step_wise.c
index f6f30a0..1fff359 100644
--- a/drivers/thermal/step_wise.c
+++ b/drivers/thermal/step_wise.c
@@ -177,7 +177,7 @@
* temperature.
*/
if (tz->temperature >= trip_temp ||
- (tz->temperature >= hyst_temp &&
+ (tz->temperature > hyst_temp &&
old_target != THERMAL_NO_TARGET))
throttle = true;
else
diff --git a/drivers/thermal/thermal_core.c b/drivers/thermal/thermal_core.c
index 3cacc0d..7663e3c 100644
--- a/drivers/thermal/thermal_core.c
+++ b/drivers/thermal/thermal_core.c
@@ -522,6 +522,7 @@
if (!ret && *temp < crit_temp)
*temp = tz->emul_temperature;
}
+ trace_thermal_query_temp(tz, *temp);
mutex_unlock(&tz->lock);
exit:
return ret;
@@ -2586,9 +2587,11 @@
case PM_POST_SUSPEND:
atomic_set(&in_suspend, 0);
list_for_each_entry(tz, &thermal_tz_list, node) {
+ mutex_lock(&tz->lock);
thermal_zone_device_reset(tz);
- thermal_zone_device_update(tz,
- THERMAL_EVENT_UNSPECIFIED);
+ mod_delayed_work(system_freezable_power_efficient_wq,
+ &tz->poll_queue, 0);
+ mutex_unlock(&tz->lock);
}
break;
default:
diff --git a/drivers/tty/serial/msm_geni_serial.c b/drivers/tty/serial/msm_geni_serial.c
index 79788eb..9c819fb 100644
--- a/drivers/tty/serial/msm_geni_serial.c
+++ b/drivers/tty/serial/msm_geni_serial.c
@@ -117,7 +117,7 @@
#define WAIT_XFER_MAX_TIMEOUT_US (10000)
#define WAIT_XFER_MIN_TIMEOUT_US (9000)
#define IPC_LOG_PWR_PAGES (6)
-#define IPC_LOG_MISC_PAGES (6)
+#define IPC_LOG_MISC_PAGES (10)
#define IPC_LOG_TX_RX_PAGES (8)
#define DATA_BYTES_PER_LINE (32)
@@ -184,6 +184,7 @@
static void msm_geni_serial_power_off(struct uart_port *uport);
static int msm_geni_serial_poll_bit(struct uart_port *uport,
int offset, int bit_field, bool set);
+static void msm_geni_serial_stop_rx(struct uart_port *uport);
static atomic_t uart_line_id = ATOMIC_INIT(0);
@@ -241,26 +242,40 @@
(unsigned int)addr, size, buf);
}
+static bool device_pending_suspend(struct uart_port *uport)
+{
+ int usage_count = atomic_read(&uport->dev->power.usage_count);
+
+ return (pm_runtime_suspended(uport->dev) || !usage_count);
+}
+
static bool check_transfers_inflight(struct uart_port *uport)
{
bool xfer_on = false;
bool tx_active = false;
- bool tx_empty = false;
+ bool tx_fifo_status = false;
bool m_cmd_active = false;
bool rx_active = false;
u32 rx_fifo_status = 0;
+ struct msm_geni_serial_port *port = GET_DEV_PORT(uport);
u32 geni_status = geni_read_reg_nolog(uport->membase,
SE_GENI_STATUS);
+ struct circ_buf *xmit = &uport->state->xmit;
+
/* Possible stop tx is called multiple times. */
m_cmd_active = geni_status & M_GENI_CMD_ACTIVE;
- tx_empty = msm_geni_serial_tx_empty(uport);
- tx_active = m_cmd_active || !tx_empty;
+ if (port->xfer_mode == SE_DMA)
+ tx_fifo_status = port->tx_dma ? 1 : 0;
+ else
+ tx_fifo_status = geni_read_reg_nolog(uport->membase,
+ SE_GENI_TX_FIFO_STATUS);
+ tx_active = m_cmd_active || tx_fifo_status;
rx_fifo_status = geni_read_reg_nolog(uport->membase,
SE_GENI_RX_FIFO_STATUS);
if (rx_fifo_status)
rx_active = true;
- if (rx_active || tx_active)
+ if (rx_active || tx_active || !uart_circ_empty(xmit))
xfer_on = true;
return xfer_on;
@@ -329,6 +344,7 @@
__func__, port->ioctl_count);
return -EPERM;
}
+ wait_for_transfers_inflight(uport);
port->ioctl_count--;
msm_geni_serial_power_off(uport);
IPC_LOG_MSG(port->ipc_log_pwr, "%s%s ioctl %d\n", __func__,
@@ -362,8 +378,13 @@
static void msm_geni_serial_break_ctl(struct uart_port *uport, int ctl)
{
- if (!uart_console(uport) && pm_runtime_status_suspended(uport->dev))
+ struct msm_geni_serial_port *port = GET_DEV_PORT(uport);
+
+ if (!uart_console(uport) && device_pending_suspend(uport)) {
+ IPC_LOG_MSG(port->ipc_log_misc,
+ "%s.Device is suspended.\n", __func__);
return;
+ }
if (ctl) {
wait_for_transfers_inflight(uport);
@@ -384,9 +405,13 @@
{
u32 geni_ios = 0;
unsigned int mctrl = TIOCM_DSR | TIOCM_CAR;
+ struct msm_geni_serial_port *port = GET_DEV_PORT(uport);
- if (pm_runtime_status_suspended(uport->dev))
+ if (device_pending_suspend(uport)) {
+ IPC_LOG_MSG(port->ipc_log_misc,
+ "%s.Device is suspended.\n", __func__);
return TIOCM_DSR | TIOCM_CAR | TIOCM_CTS;
+ }
geni_ios = geni_read_reg_nolog(uport->membase, SE_GENI_IOS);
if (!(geni_ios & IO2_DATA_IN))
@@ -404,9 +429,13 @@
unsigned int mctrl)
{
u32 uart_manual_rfr = 0;
+ struct msm_geni_serial_port *port = GET_DEV_PORT(uport);
- if (pm_runtime_status_suspended(uport->dev))
+ if (device_pending_suspend(uport)) {
+ IPC_LOG_MSG(port->ipc_log_misc,
+ "%s.Device is suspended.\n", __func__);
return;
+ }
if (!(mctrl & TIOCM_RTS))
uart_manual_rfr |= (UART_MANUAL_RFR_EN | UART_RFR_NOT_READY);
geni_write_reg_nolog(uart_manual_rfr, uport->membase,
@@ -456,6 +485,14 @@
static void msm_geni_serial_power_off(struct uart_port *uport)
{
+ struct msm_geni_serial_port *port = GET_DEV_PORT(uport);
+ int usage_count = atomic_read(&uport->dev->power.usage_count);
+
+ if (!usage_count) {
+ IPC_LOG_MSG(port->ipc_log_pwr, "%s: Usage Count is already 0\n",
+ __func__);
+ return;
+ }
pm_runtime_mark_last_busy(uport->dev);
pm_runtime_put_autosuspend(uport->dev);
}
@@ -541,6 +578,7 @@
msm_geni_serial_poll_bit(uport, SE_GENI_S_CMD_CTRL_REG,
S_GENI_CMD_ABORT, false);
geni_write_reg_nolog(irq_clear, uport->membase, SE_GENI_S_IRQ_CLEAR);
+ geni_write_reg(FORCE_DEFAULT, uport->membase, GENI_FORCE_DEFAULT_REG);
}
#ifdef CONFIG_CONSOLE_POLL
@@ -789,13 +827,16 @@
unsigned int geni_status;
unsigned int geni_ios;
- if (!uart_console(uport) && pm_runtime_status_suspended(uport->dev)) {
- dev_err(uport->dev, "%s.Device is suspended.\n", __func__);
+ if (!uart_console(uport) && !pm_runtime_active(uport->dev)) {
IPC_LOG_MSG(msm_port->ipc_log_misc,
- "%s.Device is suspended.\n", __func__);
- return;
+ "%s.Putting in async RPM vote\n", __func__);
+ pm_runtime_get(uport->dev);
+ goto exit_start_tx;
}
+ if (!uart_console(uport))
+ pm_runtime_get(uport->dev);
+
if (msm_port->xfer_mode == FIFO_MODE) {
geni_status = geni_read_reg_nolog(uport->membase,
SE_GENI_STATUS);
@@ -828,6 +869,9 @@
if (!(geni_ios & IO2_DATA_IN))
IPC_LOG_MSG(msm_port->ipc_log_misc, "%s: ios: 0x%08x\n",
__func__, geni_ios);
+exit_start_tx:
+ if (!uart_console(uport))
+ msm_geni_serial_power_off(uport);
}
static void msm_geni_serial_tx_fsm_rst(struct uart_port *uport)
@@ -855,7 +899,7 @@
unsigned int geni_status;
struct msm_geni_serial_port *port = GET_DEV_PORT(uport);
- if (!uart_console(uport) && pm_runtime_status_suspended(uport->dev)) {
+ if (!uart_console(uport) && device_pending_suspend(uport)) {
dev_err(uport->dev, "%s.Device is suspended.\n", __func__);
IPC_LOG_MSG(port->ipc_log_misc,
"%s.Device is suspended.\n", __func__);
@@ -907,7 +951,7 @@
struct msm_geni_serial_port *port = GET_DEV_PORT(uport);
int ret;
- if (!uart_console(uport) && pm_runtime_status_suspended(uport->dev)) {
+ if (!uart_console(uport) && device_pending_suspend(uport)) {
dev_err(uport->dev, "%s.Device is suspended.\n", __func__);
IPC_LOG_MSG(port->ipc_log_misc,
"%s.Device is suspended.\n", __func__);
@@ -916,7 +960,8 @@
geni_status = geni_read_reg_nolog(uport->membase, SE_GENI_STATUS);
if (geni_status & S_GENI_CMD_ACTIVE)
- msm_geni_serial_abort_rx(uport);
+ msm_geni_serial_stop_rx(uport);
+
geni_setup_s_cmd(uport->membase, UART_START_READ, 0);
if (port->xfer_mode == FIFO_MODE) {
@@ -938,8 +983,8 @@
if (ret) {
dev_err(uport->dev, "%s: RX Prep dma failed %d\n",
__func__, ret);
- msm_geni_serial_abort_rx(uport);
- return;
+ msm_geni_serial_stop_rx(uport);
+ goto exit_geni_serial_start_rx;
}
}
/*
@@ -948,6 +993,7 @@
*/
mb();
geni_status = geni_read_reg_nolog(uport->membase, SE_GENI_STATUS);
+exit_geni_serial_start_rx:
IPC_LOG_MSG(port->ipc_log_misc, "%s 0x%x\n", __func__, geni_status);
}
@@ -976,9 +1022,9 @@
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;
- if (!uart_console(uport) && pm_runtime_status_suspended(uport->dev)) {
- dev_err(uport->dev, "%s.Device is suspended.\n", __func__);
+ if (!uart_console(uport) && device_pending_suspend(uport)) {
IPC_LOG_MSG(port->ipc_log_misc,
"%s.Device is suspended.\n", __func__);
return;
@@ -1007,7 +1053,17 @@
/* Possible stop rx is called multiple times. */
if (!(geni_status & S_GENI_CMD_ACTIVE))
return;
- msm_geni_serial_abort_rx(uport);
+ geni_cancel_s_cmd(uport->membase);
+ /*
+ * Ensure that the cancel goes through before polling for the
+ * cancel control bit.
+ */
+ mb();
+ msm_geni_serial_poll_bit(uport, SE_GENI_S_CMD_CTRL_REG,
+ S_GENI_CMD_CANCEL, false);
+ geni_write_reg_nolog(irq_clear, uport->membase, SE_GENI_S_IRQ_CLEAR);
+ if ((geni_status & S_GENI_CMD_ACTIVE))
+ msm_geni_serial_abort_rx(uport);
}
static int handle_rx_hs(struct uart_port *uport,
@@ -1096,6 +1152,15 @@
tx_fifo_status = geni_read_reg_nolog(uport->membase,
SE_GENI_TX_FIFO_STATUS);
if (uart_circ_empty(xmit) && !tx_fifo_status) {
+ /*
+ * This will balance out the power vote put in during start_tx
+ * allowing the device to suspend.
+ */
+ if (!uart_console(uport)) {
+ IPC_LOG_MSG(msm_port->ipc_log_misc,
+ "%s.Power Off.\n", __func__);
+ msm_geni_serial_power_off(uport);
+ }
msm_geni_serial_stop_tx(uport);
goto exit_handle_tx;
}
@@ -1152,8 +1217,7 @@
msm_port->xmit_size = xmit_size;
}
exit_handle_tx:
- if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
- uart_write_wakeup(uport);
+ uart_write_wakeup(uport);
return ret;
}
@@ -1220,8 +1284,18 @@
if (!uart_circ_empty(xmit))
msm_geni_serial_prep_dma_tx(uport);
- else
+ else {
+ /*
+ * This will balance out the power vote put in during start_tx
+ * allowing the device to suspend.
+ */
+ if (!uart_console(uport)) {
+ IPC_LOG_MSG(msm_port->ipc_log_misc,
+ "%s.Power Off.\n", __func__);
+ msm_geni_serial_power_off(uport);
+ }
uart_write_wakeup(uport);
+ }
return 0;
}
@@ -1241,7 +1315,7 @@
spin_lock_irqsave(&uport->lock, flags);
if (uart_console(uport) && uport->suspended)
goto exit_geni_serial_isr;
- if (!uart_console(uport) && pm_runtime_status_suspended(uport->dev)) {
+ if (!uart_console(uport) && pm_runtime_suspended(uport->dev)) {
dev_err(uport->dev, "%s.Device is suspended.\n", __func__);
IPC_LOG_MSG(msm_port->ipc_log_misc,
"%s.Device is suspended.\n", __func__);
@@ -1428,9 +1502,7 @@
msm_geni_serial_stop_rx(uport);
spin_unlock_irqrestore(&uport->lock, flags);
- if (uart_console(uport)) {
- se_geni_resources_off(&msm_port->serial_rsc);
- } else {
+ if (!uart_console(uport)) {
msm_geni_serial_power_off(uport);
if (msm_port->wakeup_irq > 0) {
irq_set_irq_wake(msm_port->wakeup_irq, 0);
@@ -1513,47 +1585,6 @@
* framework.
*/
mb();
- if (!uart_console(uport)) {
- char name[30];
-
- memset(name, 0, sizeof(name));
- if (!msm_port->ipc_log_rx) {
- scnprintf(name, sizeof(name), "%s%s",
- dev_name(uport->dev), "_rx");
- msm_port->ipc_log_rx = ipc_log_context_create(
- IPC_LOG_TX_RX_PAGES, name, 0);
- if (!msm_port->ipc_log_rx)
- dev_info(uport->dev, "Err in Rx IPC Log\n");
- }
- memset(name, 0, sizeof(name));
- if (!msm_port->ipc_log_tx) {
- scnprintf(name, sizeof(name), "%s%s",
- dev_name(uport->dev), "_tx");
- msm_port->ipc_log_tx = ipc_log_context_create(
- IPC_LOG_TX_RX_PAGES, name, 0);
- if (!msm_port->ipc_log_tx)
- dev_info(uport->dev, "Err in Tx IPC Log\n");
- }
- memset(name, 0, sizeof(name));
- if (!msm_port->ipc_log_pwr) {
- scnprintf(name, sizeof(name), "%s%s",
- dev_name(uport->dev), "_pwr");
- msm_port->ipc_log_pwr = ipc_log_context_create(
- IPC_LOG_PWR_PAGES, name, 0);
- if (!msm_port->ipc_log_pwr)
- dev_info(uport->dev, "Err in Pwr IPC Log\n");
- }
- memset(name, 0, sizeof(name));
- if (!msm_port->ipc_log_misc) {
- scnprintf(name, sizeof(name), "%s%s",
- dev_name(uport->dev), "_misc");
- msm_port->ipc_log_misc = ipc_log_context_create(
- IPC_LOG_MISC_PAGES, name, 0);
- if (!msm_port->ipc_log_misc)
- dev_info(uport->dev, "Err in Misc IPC Log\n");
- }
-
- }
exit_portsetup:
return ret;
}
@@ -1588,7 +1619,6 @@
goto exit_startup;
}
- msm_geni_serial_start_rx(uport);
/*
* Ensure that all the port configuration writes complete
* before returning to the framework.
@@ -1709,11 +1739,17 @@
struct msm_geni_serial_port *port = GET_DEV_PORT(uport);
unsigned long clk_rate;
- if (!uart_console(uport) && pm_runtime_status_suspended(uport->dev)) {
- IPC_LOG_MSG(port->ipc_log_pwr,
- "%s Device suspended,vote clocks on.\n", __func__);
- return;
+ if (!uart_console(uport)) {
+ int ret = msm_geni_serial_power_on(uport);
+
+ if (ret) {
+ IPC_LOG_MSG(port->ipc_log_misc,
+ "%s: Failed to vote clock on:%d\n",
+ __func__, ret);
+ return;
+ }
}
+ msm_geni_serial_stop_rx(uport);
/* baud rate */
baud = uart_get_baud_rate(uport, termios, old, 300, 4000000);
port->cur_baud = baud;
@@ -1802,6 +1838,9 @@
IPC_LOG_MSG(port->ipc_log_misc, "BitsChar%d stop bit%d\n",
bits_per_char, stop_bit_len);
exit_set_termios:
+ msm_geni_serial_start_rx(uport);
+ if (!uart_console(uport))
+ msm_geni_serial_power_off(uport);
return;
}
@@ -1812,10 +1851,10 @@
unsigned int is_tx_empty = 1;
struct msm_geni_serial_port *port = GET_DEV_PORT(uport);
- if (!uart_console(uport) && pm_runtime_status_suspended(uport->dev)) {
+ if (!uart_console(uport) && device_pending_suspend(uport)) {
IPC_LOG_MSG(port->ipc_log_pwr,
"%s Device suspended,vote clocks on.\n", __func__);
- return 1;
+ return 0;
}
if (port->xfer_mode == SE_DMA)
@@ -2071,13 +2110,69 @@
}
#endif /* defined(CONFIG_SERIAL_CORE_CONSOLE) || defined(CONFIG_CONSOLE_POLL) */
-static void msm_geni_serial_debug_init(struct uart_port *uport)
+static void msm_geni_serial_debug_init(struct uart_port *uport, bool console)
{
struct msm_geni_serial_port *msm_port = GET_DEV_PORT(uport);
msm_port->dbg = debugfs_create_dir(dev_name(uport->dev), NULL);
if (IS_ERR_OR_NULL(msm_port->dbg))
dev_err(uport->dev, "Failed to create dbg dir\n");
+
+ if (!console) {
+ char name[30];
+
+ memset(name, 0, sizeof(name));
+ if (!msm_port->ipc_log_rx) {
+ scnprintf(name, sizeof(name), "%s%s",
+ dev_name(uport->dev), "_rx");
+ msm_port->ipc_log_rx = ipc_log_context_create(
+ IPC_LOG_TX_RX_PAGES, name, 0);
+ if (!msm_port->ipc_log_rx)
+ dev_info(uport->dev, "Err in Rx IPC Log\n");
+ }
+ memset(name, 0, sizeof(name));
+ if (!msm_port->ipc_log_tx) {
+ scnprintf(name, sizeof(name), "%s%s",
+ dev_name(uport->dev), "_tx");
+ msm_port->ipc_log_tx = ipc_log_context_create(
+ IPC_LOG_TX_RX_PAGES, name, 0);
+ if (!msm_port->ipc_log_tx)
+ dev_info(uport->dev, "Err in Tx IPC Log\n");
+ }
+ memset(name, 0, sizeof(name));
+ if (!msm_port->ipc_log_pwr) {
+ scnprintf(name, sizeof(name), "%s%s",
+ dev_name(uport->dev), "_pwr");
+ msm_port->ipc_log_pwr = ipc_log_context_create(
+ IPC_LOG_PWR_PAGES, name, 0);
+ if (!msm_port->ipc_log_pwr)
+ dev_info(uport->dev, "Err in Pwr IPC Log\n");
+ }
+ memset(name, 0, sizeof(name));
+ if (!msm_port->ipc_log_misc) {
+ scnprintf(name, sizeof(name), "%s%s",
+ dev_name(uport->dev), "_misc");
+ msm_port->ipc_log_misc = ipc_log_context_create(
+ IPC_LOG_MISC_PAGES, name, 0);
+ if (!msm_port->ipc_log_misc)
+ dev_info(uport->dev, "Err in Misc IPC Log\n");
+ }
+ }
+}
+
+static void msm_geni_serial_cons_pm(struct uart_port *uport,
+ unsigned int new_state, unsigned int old_state)
+{
+ struct msm_geni_serial_port *msm_port = GET_DEV_PORT(uport);
+
+ if (unlikely(!uart_console(uport)))
+ return;
+
+ if (new_state == UART_PM_STATE_ON && old_state == UART_PM_STATE_OFF)
+ se_geni_resources_on(&msm_port->serial_rsc);
+ else if (new_state == UART_PM_STATE_OFF &&
+ old_state == UART_PM_STATE_ON)
+ se_geni_resources_off(&msm_port->serial_rsc);
}
static const struct uart_ops msm_geni_console_pops = {
@@ -2096,6 +2191,7 @@
.poll_get_char = msm_geni_serial_get_char,
.poll_put_char = msm_geni_serial_poll_put_char,
#endif
+ .pm = msm_geni_serial_cons_pm,
};
static const struct uart_ops msm_geni_serial_pops = {
@@ -2305,7 +2401,7 @@
line, uport->fifosize, is_console);
device_create_file(uport->dev, &dev_attr_loopback);
device_create_file(uport->dev, &dev_attr_xfer_mode);
- msm_geni_serial_debug_init(uport);
+ msm_geni_serial_debug_init(uport, is_console);
dev_port->port_setup = false;
return uart_add_one_port(drv, uport);
@@ -2333,12 +2429,12 @@
int ret = 0;
wait_for_transfers_inflight(&port->uport);
+ disable_irq(port->uport.irq);
ret = se_geni_resources_off(&port->serial_rsc);
if (ret) {
dev_err(dev, "%s: Error ret %d\n", __func__, ret);
goto exit_runtime_suspend;
}
- disable_irq(port->uport.irq);
if (port->wakeup_irq > 0) {
struct se_geni_rsc *rsc = &port->serial_rsc;
@@ -2420,7 +2516,6 @@
if (uart_console(uport) &&
console_suspend_enabled && uport->suspended) {
- se_geni_resources_on(&port->serial_rsc);
uart_resume_port((struct uart_driver *)uport->private_data,
uport);
disable_irq(uport->irq);
diff --git a/drivers/uio/msm_sharedmem/msm_sharedmem.c b/drivers/uio/msm_sharedmem/msm_sharedmem.c
index b25f55a..8be3e36 100644
--- a/drivers/uio/msm_sharedmem/msm_sharedmem.c
+++ b/drivers/uio/msm_sharedmem/msm_sharedmem.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2013-2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2013-2017, 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
@@ -107,10 +107,12 @@
struct resource *clnt_res = NULL;
u32 client_id = ((u32)~0U);
u32 shared_mem_size = 0;
+ u32 shared_mem_tot_sz = 0;
void *shared_mem = NULL;
phys_addr_t shared_mem_pyhsical = 0;
bool is_addr_dynamic = false;
struct sharemem_qmi_entry qmi_entry;
+ bool guard_memory = false;
/* Get the addresses from platform-data */
if (!pdev->dev.of_node) {
@@ -145,13 +147,30 @@
if (shared_mem_pyhsical == 0) {
is_addr_dynamic = true;
- shared_mem = dma_alloc_coherent(&pdev->dev, shared_mem_size,
+
+ /*
+ * If guard_memory is set, then the shared memory region
+ * will be guarded by SZ_4K at the start and at the end.
+ * This is needed to overcome the XPU limitation on few
+ * MSM HW, so as to make this memory not contiguous with
+ * other allocations that may possibly happen from other
+ * clients in the system.
+ */
+ guard_memory = of_property_read_bool(pdev->dev.of_node,
+ "qcom,guard-memory");
+
+ shared_mem_tot_sz = guard_memory ? shared_mem_size + SZ_8K :
+ shared_mem_size;
+
+ shared_mem = dma_alloc_coherent(&pdev->dev, shared_mem_tot_sz,
&shared_mem_pyhsical, GFP_KERNEL);
if (shared_mem == NULL) {
pr_err("Shared mem alloc client=%s, size=%u\n",
clnt_res->name, shared_mem_size);
return -ENOMEM;
}
+ if (guard_memory)
+ shared_mem_pyhsical += SZ_4K;
}
/* Set up the permissions for the shared ram that was allocated. */
diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c
index 3e459b0..5ca987a 100644
--- a/drivers/usb/dwc3/core.c
+++ b/drivers/usb/dwc3/core.c
@@ -428,6 +428,9 @@
{
struct dwc3_event_buffer *evt;
+ if (!dwc->ev_buf)
+ return;
+
evt = dwc->ev_buf;
evt->lpos = 0;
@@ -692,8 +695,6 @@
phy_exit(dwc->usb2_generic_phy);
phy_exit(dwc->usb3_generic_phy);
- usb_phy_set_suspend(dwc->usb2_phy, 1);
- usb_phy_set_suspend(dwc->usb3_phy, 1);
phy_power_off(dwc->usb2_generic_phy);
phy_power_off(dwc->usb3_generic_phy);
}
@@ -976,41 +977,14 @@
struct device *dev = dwc->dev;
int ret;
- switch (dwc->dr_mode) {
- case USB_DR_MODE_PERIPHERAL:
+ if (dwc->dr_mode == USB_DR_MODE_OTG ||
+ dwc->dr_mode == USB_DR_MODE_PERIPHERAL) {
ret = dwc3_gadget_init(dwc);
if (ret) {
if (ret != -EPROBE_DEFER)
dev_err(dev, "failed to initialize gadget\n");
return ret;
}
- break;
- case USB_DR_MODE_HOST:
- ret = dwc3_host_init(dwc);
- if (ret) {
- if (ret != -EPROBE_DEFER)
- dev_err(dev, "failed to initialize host\n");
- return ret;
- }
- break;
- case USB_DR_MODE_OTG:
- ret = dwc3_host_init(dwc);
- if (ret) {
- if (ret != -EPROBE_DEFER)
- dev_err(dev, "failed to initialize host\n");
- return ret;
- }
-
- ret = dwc3_gadget_init(dwc);
- if (ret) {
- if (ret != -EPROBE_DEFER)
- dev_err(dev, "failed to initialize gadget\n");
- return ret;
- }
- break;
- default:
- dev_err(dev, "Unsupported mode of operation %d\n", dwc->dr_mode);
- return -EINVAL;
}
return 0;
@@ -1018,21 +992,9 @@
static void dwc3_core_exit_mode(struct dwc3 *dwc)
{
- switch (dwc->dr_mode) {
- case USB_DR_MODE_PERIPHERAL:
+ if (dwc->dr_mode == USB_DR_MODE_PERIPHERAL ||
+ dwc->dr_mode == USB_DR_MODE_OTG)
dwc3_gadget_exit(dwc);
- break;
- case USB_DR_MODE_HOST:
- dwc3_host_exit(dwc);
- break;
- case USB_DR_MODE_OTG:
- dwc3_host_exit(dwc);
- dwc3_gadget_exit(dwc);
- break;
- default:
- /* do nothing */
- break;
- }
}
/* XHCI reset, resets other CORE registers as well, re-init those */
@@ -1209,6 +1171,10 @@
"snps,is-utmi-l1-suspend");
device_property_read_u8(dev, "snps,hird-threshold",
&hird_threshold);
+
+ device_property_read_u32(dev, "snps,xhci-imod-value",
+ &dwc->xhci_imod_value);
+
dwc->usb3_lpm_capable = device_property_read_bool(dev,
"snps,usb3_lpm_capable");
diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h
index b042152..63d0a3e 100644
--- a/drivers/usb/dwc3/core.h
+++ b/drivers/usb/dwc3/core.h
@@ -951,6 +951,8 @@
* @last_fifo_depth: total TXFIFO depth of all enabled USB IN/INT endpoints
* @imod_interval: set the interrupt moderation interval in 250ns
* increments or 0 to disable.
+ * @create_reg_debugfs: create debugfs entry to allow dwc3 register dump
+ * @xhci_imod_value: imod value to use with xhci
*/
struct dwc3 {
struct usb_ctrlrequest *ctrl_req;
@@ -1145,6 +1147,8 @@
void *dwc_ipc_log_ctxt;
int last_fifo_depth;
struct dwc3_gadget_events dbg_gadget_events;
+ bool create_reg_debugfs;
+ u32 xhci_imod_value;
};
/* -------------------------------------------------------------------------- */
diff --git a/drivers/usb/dwc3/debugfs.c b/drivers/usb/dwc3/debugfs.c
index 260092c..aebad09 100644
--- a/drivers/usb/dwc3/debugfs.c
+++ b/drivers/usb/dwc3/debugfs.c
@@ -291,6 +291,11 @@
unsigned long flags;
u32 reg;
+ if (atomic_read(&dwc->in_lpm)) {
+ seq_puts(s, "USB device is powered off\n");
+ return 0;
+ }
+
spin_lock_irqsave(&dwc->lock, flags);
reg = dwc3_readl(dwc->regs, DWC3_GCTL);
spin_unlock_irqrestore(&dwc->lock, flags);
@@ -326,6 +331,11 @@
u32 mode = 0;
char buf[32] = {};
+ if (atomic_read(&dwc->in_lpm)) {
+ dev_err(dwc->dev, "USB device is powered off\n");
+ return count;
+ }
+
if (copy_from_user(&buf, ubuf, min_t(size_t, sizeof(buf) - 1, count)))
return -EFAULT;
@@ -360,6 +370,12 @@
unsigned long flags;
u32 reg;
+
+ if (atomic_read(&dwc->in_lpm)) {
+ seq_puts(s, "USB device is powered off\n");
+ return 0;
+ }
+
spin_lock_irqsave(&dwc->lock, flags);
reg = dwc3_readl(dwc->regs, DWC3_DCTL);
reg &= DWC3_DCTL_TSTCTRL_MASK;
@@ -406,6 +422,11 @@
u32 testmode = 0;
char buf[32] = {};
+ if (atomic_read(&dwc->in_lpm)) {
+ seq_puts(s, "USB device is powered off\n");
+ return 0;
+ }
+
if (copy_from_user(&buf, ubuf, min_t(size_t, sizeof(buf) - 1, count)))
return -EFAULT;
@@ -444,6 +465,11 @@
enum dwc3_link_state state;
u32 reg;
+ if (atomic_read(&dwc->in_lpm)) {
+ seq_puts(s, "USB device is powered off\n");
+ return 0;
+ }
+
spin_lock_irqsave(&dwc->lock, flags);
reg = dwc3_readl(dwc->regs, DWC3_DSTS);
state = DWC3_DSTS_USBLNKST(reg);
@@ -513,6 +539,11 @@
enum dwc3_link_state state = 0;
char buf[32] = {};
+ if (atomic_read(&dwc->in_lpm)) {
+ seq_puts(s, "USB device is powered off\n");
+ return 0;
+ }
+
if (copy_from_user(&buf, ubuf, min_t(size_t, sizeof(buf) - 1, count)))
return -EFAULT;
@@ -558,6 +589,11 @@
unsigned long flags;
u32 val;
+ if (atomic_read(&dwc->in_lpm)) {
+ seq_puts(s, "USB device is powered off\n");
+ return 0;
+ }
+
spin_lock_irqsave(&dwc->lock, flags);
val = dwc3_core_fifo_space(dep, DWC3_TXFIFOQ);
seq_printf(s, "%u\n", val);
@@ -573,6 +609,11 @@
unsigned long flags;
u32 val;
+ if (atomic_read(&dwc->in_lpm)) {
+ seq_puts(s, "USB device is powered off\n");
+ return 0;
+ }
+
spin_lock_irqsave(&dwc->lock, flags);
val = dwc3_core_fifo_space(dep, DWC3_RXFIFOQ);
seq_printf(s, "%u\n", val);
@@ -588,6 +629,11 @@
unsigned long flags;
u32 val;
+ if (atomic_read(&dwc->in_lpm)) {
+ seq_puts(s, "USB device is powered off\n");
+ return 0;
+ }
+
spin_lock_irqsave(&dwc->lock, flags);
val = dwc3_core_fifo_space(dep, DWC3_TXREQQ);
seq_printf(s, "%u\n", val);
@@ -603,6 +649,11 @@
unsigned long flags;
u32 val;
+ if (atomic_read(&dwc->in_lpm)) {
+ seq_puts(s, "USB device is powered off\n");
+ return 0;
+ }
+
spin_lock_irqsave(&dwc->lock, flags);
val = dwc3_core_fifo_space(dep, DWC3_RXREQQ);
seq_printf(s, "%u\n", val);
@@ -618,6 +669,11 @@
unsigned long flags;
u32 val;
+ if (atomic_read(&dwc->in_lpm)) {
+ seq_puts(s, "USB device is powered off\n");
+ return 0;
+ }
+
spin_lock_irqsave(&dwc->lock, flags);
val = dwc3_core_fifo_space(dep, DWC3_RXINFOQ);
seq_printf(s, "%u\n", val);
@@ -633,6 +689,11 @@
unsigned long flags;
u32 val;
+ if (atomic_read(&dwc->in_lpm)) {
+ seq_puts(s, "USB device is powered off\n");
+ return 0;
+ }
+
spin_lock_irqsave(&dwc->lock, flags);
val = dwc3_core_fifo_space(dep, DWC3_DESCFETCHQ);
seq_printf(s, "%u\n", val);
@@ -648,6 +709,11 @@
unsigned long flags;
u32 val;
+ if (atomic_read(&dwc->in_lpm)) {
+ seq_puts(s, "USB device is powered off\n");
+ return 0;
+ }
+
spin_lock_irqsave(&dwc->lock, flags);
val = dwc3_core_fifo_space(dep, DWC3_EVENTQ);
seq_printf(s, "%u\n", val);
@@ -1044,9 +1110,12 @@
dwc->regset->nregs = ARRAY_SIZE(dwc3_regs);
dwc->regset->base = dwc->regs - DWC3_GLOBALS_REGS_START;
- file = debugfs_create_regset32("regdump", S_IRUGO, root, dwc->regset);
- if (!file)
- dev_dbg(dwc->dev, "Can't create debugfs regdump\n");
+ if (dwc->create_reg_debugfs) {
+ file = debugfs_create_regset32("regdump", 0444,
+ root, dwc->regset);
+ if (!file)
+ dev_dbg(dwc->dev, "Can't create debugfs regdump\n");
+ }
if (IS_ENABLED(CONFIG_USB_DWC3_DUAL_ROLE)) {
file = debugfs_create_file("mode", S_IRUGO | S_IWUSR, root,
diff --git a/drivers/usb/dwc3/dwc3-msm.c b/drivers/usb/dwc3/dwc3-msm.c
index d316821..d0fc511 100644
--- a/drivers/usb/dwc3/dwc3-msm.c
+++ b/drivers/usb/dwc3/dwc3-msm.c
@@ -1867,6 +1867,9 @@
dev_dbg(mdwc->dev, "DWC3_GSI_EVT_BUF_SETUP\n");
for (i = 0; i < mdwc->num_gsi_event_buffers; i++) {
evt = mdwc->gsi_ev_buff[i];
+ if (!evt)
+ break;
+
dev_dbg(mdwc->dev, "Evt buf %p dma %08llx length %d\n",
evt->buf, (unsigned long long) evt->dma,
evt->length);
@@ -1892,6 +1895,9 @@
break;
case DWC3_GSI_EVT_BUF_CLEANUP:
dev_dbg(mdwc->dev, "DWC3_GSI_EVT_BUF_CLEANUP\n");
+ if (!mdwc->gsi_ev_buff)
+ break;
+
for (i = 0; i < mdwc->num_gsi_event_buffers; i++) {
evt = mdwc->gsi_ev_buff[i];
evt->lpos = 0;
@@ -1911,6 +1917,9 @@
break;
case DWC3_GSI_EVT_BUF_FREE:
dev_dbg(mdwc->dev, "DWC3_GSI_EVT_BUF_FREE\n");
+ if (!mdwc->gsi_ev_buff)
+ break;
+
for (i = 0; i < mdwc->num_gsi_event_buffers; i++) {
evt = mdwc->gsi_ev_buff[i];
if (evt)
@@ -3145,7 +3154,7 @@
ret = dwc3_msm_get_clk_gdsc(mdwc);
if (ret) {
dev_err(&pdev->dev, "error getting clock or gdsc.\n");
- return ret;
+ goto err;
}
mdwc->id_state = DWC3_ID_FLOAT;
@@ -3441,24 +3450,20 @@
return 0;
put_dwc3:
- platform_device_put(mdwc->dwc3);
if (mdwc->bus_perf_client)
msm_bus_scale_unregister_client(mdwc->bus_perf_client);
+
uninit_iommu:
if (mdwc->iommu_map) {
arm_iommu_detach_device(mdwc->dev);
arm_iommu_release_mapping(mdwc->iommu_map);
}
+ of_platform_depopulate(&pdev->dev);
err:
+ destroy_workqueue(mdwc->dwc3_wq);
return ret;
}
-static int dwc3_msm_remove_children(struct device *dev, void *data)
-{
- device_unregister(dev);
- return 0;
-}
-
static int dwc3_msm_remove(struct platform_device *pdev)
{
struct dwc3_msm *mdwc = platform_get_drvdata(pdev);
@@ -3495,8 +3500,7 @@
if (mdwc->hs_phy)
mdwc->hs_phy->flags &= ~PHY_HOST_MODE;
- platform_device_put(mdwc->dwc3);
- device_for_each_child(&pdev->dev, NULL, dwc3_msm_remove_children);
+ of_platform_depopulate(&pdev->dev);
dbg_event(0xFF, "Remov put", 0);
pm_runtime_disable(mdwc->dev);
@@ -3663,9 +3667,6 @@
struct dwc3 *dwc = platform_get_drvdata(mdwc->dwc3);
int ret = 0;
- if (!dwc->xhci)
- return -EINVAL;
-
/*
* The vbus_reg pointer could have multiple values
* NULL: regulator_get() hasn't been called, or was previously deferred
@@ -3716,15 +3717,7 @@
mdwc->usbdev_nb.notifier_call = msm_dwc3_usbdev_notify;
usb_register_atomic_notify(&mdwc->usbdev_nb);
- /*
- * FIXME If micro A cable is disconnected during system suspend,
- * xhci platform device will be removed before runtime pm is
- * enabled for xhci device. Due to this, disable_depth becomes
- * greater than one and runtimepm is not enabled for next microA
- * connect. Fix this by calling pm_runtime_init for xhci device.
- */
- pm_runtime_init(&dwc->xhci->dev);
- ret = platform_device_add(dwc->xhci);
+ ret = dwc3_host_init(dwc);
if (ret) {
dev_err(mdwc->dev,
"%s: failed to add XHCI pdev ret=%d\n",
@@ -3792,7 +3785,7 @@
}
mdwc->hs_phy->flags &= ~PHY_HOST_MODE;
- platform_device_del(dwc->xhci);
+ dwc3_host_exit(dwc);
usb_unregister_notify(&mdwc->host_nb);
dwc3_usb3_phy_suspend(dwc, false);
@@ -3952,20 +3945,20 @@
union power_supply_propval pval = {0};
int ret, psy_type;
- if (mdwc->max_power == mA)
- return 0;
-
psy_type = get_psy_type(mdwc);
- if (psy_type == POWER_SUPPLY_TYPE_USB) {
- dev_info(mdwc->dev, "Avail curr from USB = %u\n", mA);
- /* Set max current limit in uA */
- pval.intval = 1000 * mA;
- } else if (psy_type == POWER_SUPPLY_TYPE_USB_FLOAT) {
+ if (psy_type == POWER_SUPPLY_TYPE_USB_FLOAT) {
pval.intval = -ETIMEDOUT;
- } else {
- return 0;
+ goto set_prop;
}
+ if (mdwc->max_power == mA || psy_type != POWER_SUPPLY_TYPE_USB)
+ return 0;
+
+ dev_info(mdwc->dev, "Avail curr from USB = %u\n", mA);
+ /* Set max current limit in uA */
+ pval.intval = 1000 * mA;
+
+set_prop:
ret = power_supply_set_property(mdwc->usb_psy,
POWER_SUPPLY_PROP_SDP_CURRENT_MAX, &pval);
if (ret) {
diff --git a/drivers/usb/dwc3/ep0.c b/drivers/usb/dwc3/ep0.c
index ec9ffc1..1c33051 100644
--- a/drivers/usb/dwc3/ep0.c
+++ b/drivers/usb/dwc3/ep0.c
@@ -878,10 +878,7 @@
dwc3_trace(trace_dwc3_ep0, "Setup Pending received");
- if (r)
- dwc3_gadget_giveback(ep0, r, -ECONNRESET);
-
- return;
+ dbg_event(0x0, "SETUPPEND", status);
}
ur = &r->request;
diff --git a/drivers/usb/dwc3/host.c b/drivers/usb/dwc3/host.c
index af3ce4f..3f79aa4 100644
--- a/drivers/usb/dwc3/host.c
+++ b/drivers/usb/dwc3/host.c
@@ -52,14 +52,16 @@
return irq;
}
+#define NUMBER_OF_PROPS 4
int dwc3_host_init(struct dwc3 *dwc)
{
- struct property_entry props[3];
+ struct property_entry props[NUMBER_OF_PROPS];
struct platform_device *xhci;
int ret, irq;
struct resource *res;
struct platform_device *dwc3_pdev = to_platform_device(dwc->dev);
int prop_idx = 0;
+ struct property_entry imod_prop;
irq = dwc3_host_get_irq(dwc);
if (irq < 0)
@@ -101,6 +103,15 @@
if (dwc->usb3_lpm_capable)
props[prop_idx++].name = "usb3-lpm-capable";
+ if (dwc->xhci_imod_value) {
+ imod_prop.name = "xhci-imod-value";
+ imod_prop.length = sizeof(u32);
+ imod_prop.is_string = false;
+ imod_prop.is_array = false;
+ imod_prop.value.u32_data = dwc->xhci_imod_value;
+ props[prop_idx++] = imod_prop;
+ }
+
/**
* WORKAROUND: dwc3 revisions <=3.00a have a limitation
* where Port Disable command doesn't work.
@@ -126,7 +137,11 @@
phy_create_lookup(dwc->usb3_generic_phy, "usb3-phy",
dev_name(dwc->dev));
- /* Platform device gets added as part of state machine */
+ ret = platform_device_add(xhci);
+ if (ret) {
+ dev_err(dwc->dev, "failed to register xHCI device\n");
+ goto err1;
+ }
return 0;
err1:
@@ -140,6 +155,5 @@
dev_name(dwc->dev));
phy_remove_lookup(dwc->usb3_generic_phy, "usb3-phy",
dev_name(dwc->dev));
- if (!dwc->is_drd)
- platform_device_unregister(dwc->xhci);
+ platform_device_unregister(dwc->xhci);
}
diff --git a/drivers/usb/gadget/composite.c b/drivers/usb/gadget/composite.c
index 02adc93..e32de9a 100644
--- a/drivers/usb/gadget/composite.c
+++ b/drivers/usb/gadget/composite.c
@@ -2013,8 +2013,9 @@
if (value < 0) {
DBG(cdev, "ep_queue --> %d\n", value);
req->status = 0;
- composite_setup_complete(gadget->ep0,
- req);
+ if (value != -ESHUTDOWN)
+ composite_setup_complete(
+ gadget->ep0, req);
}
}
return value;
@@ -2108,7 +2109,8 @@
if (value < 0) {
DBG(cdev, "ep_queue --> %d\n", value);
req->status = 0;
- composite_setup_complete(gadget->ep0, req);
+ if (value != -ESHUTDOWN)
+ composite_setup_complete(gadget->ep0, req);
}
} else if (value == USB_GADGET_DELAYED_STATUS && w_length != 0) {
WARN(cdev,
diff --git a/drivers/usb/gadget/function/f_ccid.c b/drivers/usb/gadget/function/f_ccid.c
index 7817f1c..dbf72ab 100644
--- a/drivers/usb/gadget/function/f_ccid.c
+++ b/drivers/usb/gadget/function/f_ccid.c
@@ -206,6 +206,71 @@
NULL,
};
+/* Super speed support: */
+static struct usb_endpoint_descriptor ccid_ss_notify_desc = {
+ .bLength = USB_DT_ENDPOINT_SIZE,
+ .bDescriptorType = USB_DT_ENDPOINT,
+ .bEndpointAddress = USB_DIR_IN,
+ .bmAttributes = USB_ENDPOINT_XFER_INT,
+ .wMaxPacketSize = cpu_to_le16(CCID_NOTIFY_MAXPACKET),
+ .bInterval = CCID_NOTIFY_INTERVAL + 4,
+};
+
+static struct usb_ss_ep_comp_descriptor ccid_ss_notify_comp_desc = {
+ .bLength = sizeof(ccid_ss_notify_comp_desc),
+ .bDescriptorType = USB_DT_SS_ENDPOINT_COMP,
+
+ /* the following 2 values can be tweaked if necessary */
+ /* .bMaxBurst = 0, */
+ /* .bmAttributes = 0, */
+};
+
+static struct usb_endpoint_descriptor ccid_ss_in_desc = {
+ .bLength = USB_DT_ENDPOINT_SIZE,
+ .bDescriptorType = USB_DT_ENDPOINT,
+ .bEndpointAddress = USB_DIR_IN,
+ .bmAttributes = USB_ENDPOINT_XFER_BULK,
+ .wMaxPacketSize = cpu_to_le16(1024),
+};
+
+static struct usb_ss_ep_comp_descriptor ccid_ss_in_comp_desc = {
+ .bLength = sizeof(ccid_ss_in_comp_desc),
+ .bDescriptorType = USB_DT_SS_ENDPOINT_COMP,
+
+ /* the following 2 values can be tweaked if necessary */
+ /* .bMaxBurst = 0, */
+ /* .bmAttributes = 0, */
+};
+
+static struct usb_endpoint_descriptor ccid_ss_out_desc = {
+ .bLength = USB_DT_ENDPOINT_SIZE,
+ .bDescriptorType = USB_DT_ENDPOINT,
+ .bEndpointAddress = USB_DIR_OUT,
+ .bmAttributes = USB_ENDPOINT_XFER_BULK,
+ .wMaxPacketSize = cpu_to_le16(1024),
+};
+
+static struct usb_ss_ep_comp_descriptor ccid_ss_out_comp_desc = {
+ .bLength = sizeof(ccid_ss_out_comp_desc),
+ .bDescriptorType = USB_DT_SS_ENDPOINT_COMP,
+
+ /* the following 2 values can be tweaked if necessary */
+ /* .bMaxBurst = 0, */
+ /* .bmAttributes = 0, */
+};
+
+static struct usb_descriptor_header *ccid_ss_descs[] = {
+ (struct usb_descriptor_header *) &ccid_interface_desc,
+ (struct usb_descriptor_header *) &ccid_class_desc,
+ (struct usb_descriptor_header *) &ccid_ss_notify_desc,
+ (struct usb_descriptor_header *) &ccid_ss_notify_comp_desc,
+ (struct usb_descriptor_header *) &ccid_ss_in_desc,
+ (struct usb_descriptor_header *) &ccid_ss_in_comp_desc,
+ (struct usb_descriptor_header *) &ccid_ss_out_desc,
+ (struct usb_descriptor_header *) &ccid_ss_out_comp_desc,
+ NULL,
+};
+
static inline struct f_ccid *func_to_ccid(struct usb_function *f)
{
return container_of(f, struct f_ccid, function);
@@ -503,10 +568,7 @@
static void ccid_function_unbind(struct usb_configuration *c,
struct usb_function *f)
{
- if (gadget_is_dualspeed(c->cdev->gadget))
- usb_free_descriptors(f->hs_descriptors);
- usb_free_descriptors(f->fs_descriptors);
-
+ usb_free_all_descriptors(f);
}
static int ccid_function_bind(struct usb_configuration *c,
@@ -551,24 +613,27 @@
ccid_dev->out = ep;
ep->driver_data = cdev;
- f->fs_descriptors = usb_copy_descriptors(ccid_fs_descs);
- if (!f->fs_descriptors)
+ /*
+ * support all relevant hardware speeds... we expect that when
+ * hardware is dual speed, all bulk-capable endpoints work at
+ * both speeds
+ */
+ ccid_hs_in_desc.bEndpointAddress = ccid_fs_in_desc.bEndpointAddress;
+ ccid_hs_out_desc.bEndpointAddress = ccid_fs_out_desc.bEndpointAddress;
+ ccid_hs_notify_desc.bEndpointAddress =
+ ccid_fs_notify_desc.bEndpointAddress;
+
+
+ ccid_ss_in_desc.bEndpointAddress = ccid_fs_in_desc.bEndpointAddress;
+ ccid_ss_out_desc.bEndpointAddress = ccid_fs_out_desc.bEndpointAddress;
+ ccid_ss_notify_desc.bEndpointAddress =
+ ccid_fs_notify_desc.bEndpointAddress;
+
+ ret = usb_assign_descriptors(f, ccid_fs_descs, ccid_hs_descs,
+ ccid_ss_descs, NULL);
+ if (ret)
goto ep_auto_out_fail;
- if (gadget_is_dualspeed(cdev->gadget)) {
- ccid_hs_in_desc.bEndpointAddress =
- ccid_fs_in_desc.bEndpointAddress;
- ccid_hs_out_desc.bEndpointAddress =
- ccid_fs_out_desc.bEndpointAddress;
- ccid_hs_notify_desc.bEndpointAddress =
- ccid_fs_notify_desc.bEndpointAddress;
-
- /* copy descriptors, and track endpoint copies */
- f->hs_descriptors = usb_copy_descriptors(ccid_hs_descs);
- if (!f->hs_descriptors)
- goto ep_auto_out_fail;
- }
-
pr_debug("%s: CCID %s Speed, IN:%s OUT:%s\n", __func__,
gadget_is_dualspeed(cdev->gadget) ? "dual" : "full",
ccid_dev->in->name, ccid_dev->out->name);
@@ -972,6 +1037,7 @@
ccid_dev->function.name = FUNCTION_NAME;
ccid_dev->function.fs_descriptors = ccid_fs_descs;
ccid_dev->function.hs_descriptors = ccid_hs_descs;
+ ccid_dev->function.ss_descriptors = ccid_ss_descs;
ccid_dev->function.bind = ccid_function_bind;
ccid_dev->function.unbind = ccid_function_unbind;
ccid_dev->function.set_alt = ccid_function_set_alt;
diff --git a/drivers/usb/gadget/function/f_gsi.c b/drivers/usb/gadget/function/f_gsi.c
index c11629d..a26d6df 100644
--- a/drivers/usb/gadget/function/f_gsi.c
+++ b/drivers/usb/gadget/function/f_gsi.c
@@ -19,7 +19,14 @@
MODULE_PARM_DESC(qti_packet_debug, "Print QTI Packet's Raw Data");
static struct workqueue_struct *ipa_usb_wq;
+static struct gsi_inst_status {
+ struct mutex gsi_lock;
+ bool inst_exist;
+ struct gsi_opts *opts;
+} inst_status[IPA_USB_MAX_TETH_PROT_SIZE];
+/* Deregister misc device and free instance structures */
+static void gsi_inst_clean(struct gsi_opts *opts);
static void gsi_rndis_ipa_reset_trigger(struct gsi_data_port *d_port);
static void ipa_disconnect_handler(struct gsi_data_port *d_port);
static int gsi_ctrl_send_notification(struct f_gsi *gsi);
@@ -919,40 +926,69 @@
struct gsi_ctrl_port *c_port = container_of(fp->private_data,
struct gsi_ctrl_port,
ctrl_device);
- struct f_gsi *gsi = c_port_to_gsi(c_port);
+ struct f_gsi *gsi;
+ struct gsi_inst_status *inst_cur;
if (!c_port) {
- log_event_err("%s: gsi ctrl port %p", __func__, c_port);
+ pr_err_ratelimited("%s: gsi ctrl port %p", __func__, c_port);
return -ENODEV;
}
+ gsi = container_of(c_port, struct f_gsi, c_port);
+ inst_cur = &inst_status[gsi->prot_id];
log_event_dbg("%s: open ctrl dev %s", __func__, c_port->name);
+ mutex_lock(&inst_cur->gsi_lock);
+
+ fp->private_data = &gsi->prot_id;
+
+ if (!inst_cur->inst_exist) {
+ mutex_unlock(&inst_cur->gsi_lock);
+ log_event_err("%s: [prot_id = %d], GSI instance freed already\n",
+ __func__, gsi->prot_id);
+ return -ENODEV;
+ }
+
if (c_port->is_open) {
- log_event_err("%s: Already opened", __func__);
+ mutex_unlock(&inst_cur->gsi_lock);
+ log_event_err("%s: Already opened\n", __func__);
return -EBUSY;
}
c_port->is_open = true;
+ mutex_unlock(&inst_cur->gsi_lock);
+
return 0;
}
static int gsi_ctrl_dev_release(struct inode *ip, struct file *fp)
{
- struct gsi_ctrl_port *c_port = container_of(fp->private_data,
- struct gsi_ctrl_port,
- ctrl_device);
- struct f_gsi *gsi = c_port_to_gsi(c_port);
+ enum ipa_usb_teth_prot prot_id =
+ *(enum ipa_usb_teth_prot *)(fp->private_data);
+ struct gsi_inst_status *inst_cur = &inst_status[prot_id];
+ struct f_gsi *gsi;
- if (!c_port) {
- log_event_err("%s: gsi ctrl port %p", __func__, c_port);
+ mutex_lock(&inst_cur->gsi_lock);
+
+ if (unlikely(inst_cur->inst_exist == false)) {
+ if (inst_cur->opts) {
+ /* GSI instance clean up */
+ gsi_inst_clean(inst_cur->opts);
+ inst_cur->opts = NULL;
+ }
+ mutex_unlock(&inst_cur->gsi_lock);
+ pr_err_ratelimited("%s: prot_id:%d: delayed free memory\n",
+ __func__, prot_id);
return -ENODEV;
}
- log_event_dbg("close ctrl dev %s", c_port->name);
+ inst_cur->opts->gsi->c_port.is_open = false;
+ gsi = inst_cur->opts->gsi;
+ mutex_unlock(&inst_cur->gsi_lock);
- c_port->is_open = false;
+ log_event_dbg("close ctrl dev %s\n",
+ inst_cur->opts->gsi->c_port.name);
return 0;
}
@@ -960,16 +996,28 @@
static ssize_t
gsi_ctrl_dev_read(struct file *fp, char __user *buf, size_t count, loff_t *pos)
{
- struct gsi_ctrl_port *c_port = container_of(fp->private_data,
- struct gsi_ctrl_port,
- ctrl_device);
- struct f_gsi *gsi = c_port_to_gsi(c_port);
+ struct gsi_ctrl_port *c_port;
struct gsi_ctrl_pkt *cpkt = NULL;
+ enum ipa_usb_teth_prot prot_id =
+ *(enum ipa_usb_teth_prot *)(fp->private_data);
+ struct gsi_inst_status *inst_cur = &inst_status[prot_id];
+ struct f_gsi *gsi;
unsigned long flags;
int ret = 0;
- log_event_dbg("%s: Enter %zu", __func__, count);
+ pr_debug("%s: Enter %zu", __func__, count);
+ mutex_lock(&inst_cur->gsi_lock);
+ if (unlikely(inst_cur->inst_exist == false)) {
+ mutex_unlock(&inst_cur->gsi_lock);
+ pr_err_ratelimited("%s: free_inst is called and being freed\n",
+ __func__);
+ return -ENODEV;
+ }
+ mutex_unlock(&inst_cur->gsi_lock);
+
+ gsi = inst_cur->opts->gsi;
+ c_port = &inst_cur->opts->gsi->c_port;
if (!c_port) {
log_event_err("%s: gsi ctrl port %p", __func__, c_port);
return -ENODEV;
@@ -1037,13 +1085,27 @@
int ret = 0;
unsigned long flags;
struct gsi_ctrl_pkt *cpkt;
- struct gsi_ctrl_port *c_port = container_of(fp->private_data,
- struct gsi_ctrl_port,
- ctrl_device);
- struct f_gsi *gsi = c_port_to_gsi(c_port);
- struct usb_request *req = c_port->notify_req;
+ struct gsi_ctrl_port *c_port;
+ struct usb_request *req;
+ enum ipa_usb_teth_prot prot_id =
+ *(enum ipa_usb_teth_prot *)(fp->private_data);
+ struct gsi_inst_status *inst_cur = &inst_status[prot_id];
+ struct f_gsi *gsi;
- log_event_dbg("Enter %zu", count);
+ pr_debug("Enter %zu", count);
+
+ mutex_lock(&inst_cur->gsi_lock);
+ if (unlikely(inst_cur->inst_exist == false)) {
+ mutex_unlock(&inst_cur->gsi_lock);
+ pr_err_ratelimited("%s: free_inst is called and being freed\n",
+ __func__);
+ return -ENODEV;
+ }
+ mutex_unlock(&inst_cur->gsi_lock);
+
+ gsi = inst_cur->opts->gsi;
+ c_port = &gsi->c_port;
+ req = c_port->notify_req;
if (!c_port || !req || !req->buf) {
log_event_err("%s: c_port %p req %p req->buf %p",
@@ -1101,15 +1163,28 @@
static long gsi_ctrl_dev_ioctl(struct file *fp, unsigned int cmd,
unsigned long arg)
{
- struct gsi_ctrl_port *c_port = container_of(fp->private_data,
- struct gsi_ctrl_port,
- ctrl_device);
- struct f_gsi *gsi = c_port_to_gsi(c_port);
+ struct gsi_ctrl_port *c_port;
+ struct f_gsi *gsi;
struct gsi_ctrl_pkt *cpkt;
struct ep_info info;
+ enum ipa_usb_teth_prot prot_id =
+ *(enum ipa_usb_teth_prot *)(fp->private_data);
+ struct gsi_inst_status *inst_cur = &inst_status[prot_id];
int val, ret = 0;
unsigned long flags;
+ mutex_lock(&inst_cur->gsi_lock);
+ if (unlikely(inst_cur->inst_exist == false)) {
+ mutex_unlock(&inst_cur->gsi_lock);
+ pr_err_ratelimited("%s: free_inst is called and being freed\n",
+ __func__);
+ return -ENODEV;
+ }
+ mutex_unlock(&inst_cur->gsi_lock);
+
+ gsi = inst_cur->opts->gsi;
+ c_port = &gsi->c_port;
+
if (!c_port) {
log_event_err("%s: gsi ctrl port %p", __func__, c_port);
return -ENODEV;
@@ -1230,13 +1305,25 @@
static unsigned int gsi_ctrl_dev_poll(struct file *fp, poll_table *wait)
{
- struct gsi_ctrl_port *c_port = container_of(fp->private_data,
- struct gsi_ctrl_port,
- ctrl_device);
- struct f_gsi *gsi = c_port_to_gsi(c_port);
+ struct gsi_ctrl_port *c_port;
+ enum ipa_usb_teth_prot prot_id =
+ *(enum ipa_usb_teth_prot *)(fp->private_data);
+ struct gsi_inst_status *inst_cur = &inst_status[prot_id];
+ struct f_gsi *gsi;
unsigned long flags;
unsigned int mask = 0;
+ mutex_lock(&inst_cur->gsi_lock);
+ if (unlikely(inst_cur->inst_exist == false)) {
+ mutex_unlock(&inst_cur->gsi_lock);
+ pr_err_ratelimited("%s: free_inst is called and being freed\n",
+ __func__);
+ return -ENODEV;
+ }
+ mutex_unlock(&inst_cur->gsi_lock);
+
+ gsi = inst_cur->opts->gsi;
+ c_port = &inst_cur->opts->gsi->c_port;
if (!c_port) {
log_event_err("%s: gsi ctrl port %p", __func__, c_port);
return -ENODEV;
@@ -2679,8 +2766,12 @@
f->name,
gadget_is_superspeed(c->cdev->gadget) ? "super" :
gadget_is_dualspeed(c->cdev->gadget) ? "dual" : "full",
- gsi->d_port.in_ep->name, gsi->d_port.out_ep->name,
- gsi->c_port.notify->name);
+ (gsi->d_port.in_ep == NULL ? "NULL" :
+ gsi->d_port.in_ep->name),
+ (gsi->d_port.out_ep == NULL ? "NULL" :
+ gsi->d_port.out_ep->name),
+ (gsi->c_port.notify == NULL ? "NULL" :
+ gsi->c_port.notify->name));
return 0;
dereg_rndis:
@@ -2827,7 +2918,11 @@
static void gsi_opts_release(struct config_item *item)
{
struct gsi_opts *opts = to_gsi_opts(item);
+ struct f_gsi *gsi;
+ gsi = opts->gsi;
+ log_event_dbg("%s: releasing %s instance\n",
+ __func__, gsi->function.name);
usb_put_function_instance(&opts->func_inst);
}
@@ -2991,26 +3086,52 @@
.ct_owner = THIS_MODULE,
};
+static void gsi_inst_clean(struct gsi_opts *opts)
+{
+ if (opts->gsi->c_port.ctrl_device.fops)
+ misc_deregister(&opts->gsi->c_port.ctrl_device);
+
+ kfree(opts->gsi);
+ kfree(opts);
+}
+
static int gsi_set_inst_name(struct usb_function_instance *fi,
const char *name)
{
- int ret, name_len;
+ int prot_id, name_len;
struct f_gsi *gsi;
char gsi_inst_name[MAX_INST_NAME_LEN + sizeof("gsi.") + 1];
- struct gsi_opts *opts = container_of(fi, struct gsi_opts, func_inst);
void *ipc_log_ctxt;
+ struct gsi_opts *opts, *opts_prev;
+
+ opts = container_of(fi, struct gsi_opts, func_inst);
name_len = strlen(name) + 1;
if (name_len > MAX_INST_NAME_LEN)
return -ENAMETOOLONG;
- ret = name_to_prot_id(name);
- if (ret < 0) {
+ prot_id = name_to_prot_id(name);
+ if (prot_id < 0) {
pr_err("%s: failed to find prot id for %s instance\n",
- __func__, name);
+ __func__, name);
return -EINVAL;
}
+ mutex_lock(&inst_status[prot_id].gsi_lock);
+ opts_prev = inst_status[prot_id].opts;
+ if (opts_prev) {
+ mutex_unlock(&inst_status[prot_id].gsi_lock);
+ pr_err("%s: prot_id = %d, prev inst do not freed yet\n",
+ __func__, prot_id);
+ return -EBUSY;
+ }
+ mutex_unlock(&inst_status[prot_id].gsi_lock);
+
+ gsi = gsi_function_init(prot_id);
+ if (IS_ERR(gsi))
+ return PTR_ERR(gsi);
+
+ opts->gsi = gsi;
/*
* create instance name with prefixing "gsi." to differentiate
* ipc log debugfs entry
@@ -3020,31 +3141,45 @@
if (!ipc_log_ctxt)
pr_err("%s: Err allocating ipc_log_ctxt for prot:%s\n",
__func__, gsi_inst_name);
-
- gsi = gsi_function_init(ret);
- if (IS_ERR(gsi)) {
- ipc_log_context_destroy(ipc_log_ctxt);
- return PTR_ERR(gsi);
- }
-
- opts->gsi = gsi;
opts->gsi->ipc_log_ctxt = ipc_log_ctxt;
+
+ /* Set instance status */
+ mutex_lock(&inst_status[prot_id].gsi_lock);
+ inst_status[prot_id].inst_exist = true;
+ inst_status[prot_id].opts = opts;
+ mutex_unlock(&inst_status[prot_id].gsi_lock);
+
return 0;
}
static void gsi_free_inst(struct usb_function_instance *f)
{
struct gsi_opts *opts = container_of(f, struct gsi_opts, func_inst);
+ enum ipa_usb_teth_prot prot_id;
+ struct f_gsi *gsi;
if (!opts->gsi)
return;
- if (opts->gsi->c_port.ctrl_device.fops)
- misc_deregister(&opts->gsi->c_port.ctrl_device);
+ prot_id = opts->gsi->prot_id;
+ gsi = opts->gsi;
+ mutex_lock(&inst_status[prot_id].gsi_lock);
+ if (opts->gsi->c_port.is_open) {
+ /* Mark instance exist as false */
+ inst_status[prot_id].inst_exist = false;
+ mutex_unlock(&inst_status[prot_id].gsi_lock);
+ log_event_err(
+ "%s: [prot_id = %d] Dev is open, free mem when dev close\n",
+ __func__, prot_id);
+ return;
+ }
ipc_log_context_destroy(opts->gsi->ipc_log_ctxt);
- kfree(opts->gsi);
- kfree(opts);
+ /* Clear instance status */
+ gsi_inst_clean(opts);
+ inst_status[prot_id].inst_exist = false;
+ inst_status[prot_id].opts = NULL;
+ mutex_unlock(&inst_status[prot_id].gsi_lock);
}
static struct usb_function_instance *gsi_alloc_inst(void)
@@ -3058,7 +3193,7 @@
opts->func_inst.set_inst_name = gsi_set_inst_name;
opts->func_inst.free_func_inst = gsi_free_inst;
config_group_init_type_name(&opts->func_inst.group, "",
- &gsi_func_type);
+ &gsi_func_type);
return &opts->func_inst;
}
@@ -3083,6 +3218,8 @@
static int fgsi_init(void)
{
+ int i;
+
ipa_usb_wq = alloc_workqueue("k_ipa_usb",
WQ_UNBOUND | WQ_MEM_RECLAIM | WQ_FREEZABLE, 1);
if (!ipa_usb_wq) {
@@ -3090,6 +3227,9 @@
return -ENOMEM;
}
+ for (i = 0; i < IPA_USB_MAX_TETH_PROT_SIZE; i++)
+ mutex_init(&inst_status[i].gsi_lock);
+
return usb_function_register(&gsiusb_func);
}
module_init(fgsi_init);
diff --git a/drivers/usb/host/xhci-plat.c b/drivers/usb/host/xhci-plat.c
index 14c0203..588546a 100644
--- a/drivers/usb/host/xhci-plat.c
+++ b/drivers/usb/host/xhci-plat.c
@@ -313,7 +313,7 @@
if (device_property_read_bool(&pdev->dev, "quirk-broken-port-ped"))
xhci->quirks |= XHCI_BROKEN_PORT_PED;
- if (device_property_read_u32(sysdev, "snps,xhci-imod-value", &imod))
+ if (device_property_read_u32(&pdev->dev, "xhci-imod-value", &imod))
imod = 0;
if (device_property_read_u32(sysdev, "usb-core-id", &xhci->core_id))
diff --git a/drivers/usb/misc/lvstest.c b/drivers/usb/misc/lvstest.c
index 2020009..934e10f 100644
--- a/drivers/usb/misc/lvstest.c
+++ b/drivers/usb/misc/lvstest.c
@@ -184,10 +184,13 @@
struct usb_interface *intf = to_usb_interface(dev);
struct usb_device *hdev = interface_to_usbdev(intf);
struct lvs_rh *lvs = usb_get_intfdata(intf);
+ int port;
int ret;
- ret = lvs_rh_set_port_feature(hdev, lvs->portnum,
- USB_PORT_FEAT_BH_PORT_RESET);
+ if (kstrtoint(buf, 0, &port) || port < 1 || port > 255)
+ port = lvs->portnum;
+
+ ret = lvs_rh_set_port_feature(hdev, port, USB_PORT_FEAT_BH_PORT_RESET);
if (ret < 0) {
dev_err(dev, "can't issue warm reset %d\n", ret);
return ret;
@@ -299,10 +302,14 @@
struct usb_interface *intf = to_usb_interface(dev);
struct usb_device *hdev = interface_to_usbdev(intf);
struct lvs_rh *lvs = usb_get_intfdata(intf);
+ int port;
int ret;
+ if (kstrtoint(buf, 0, &port) || port < 1 || port > 255)
+ port = lvs->portnum;
+
ret = lvs_rh_set_port_feature(hdev,
- lvs->portnum | USB_SS_PORT_LS_COMP_MOD << 3,
+ port | (USB_SS_PORT_LS_COMP_MOD << 3),
USB_PORT_FEAT_LINK_STATE);
if (ret < 0) {
dev_err(dev, "can't enable compliance mode %d\n", ret);
diff --git a/drivers/usb/pd/policy_engine.c b/drivers/usb/pd/policy_engine.c
index e8961b5..aabfb41 100644
--- a/drivers/usb/pd/policy_engine.c
+++ b/drivers/usb/pd/policy_engine.c
@@ -1038,6 +1038,16 @@
static void phy_shutdown(struct usbpd *pd)
{
usbpd_dbg(&pd->dev, "shutdown");
+
+ if (pd->vconn_enabled) {
+ regulator_disable(pd->vconn);
+ pd->vconn_enabled = false;
+ }
+
+ if (pd->vbus_enabled) {
+ regulator_disable(pd->vbus);
+ pd->vbus_enabled = false;
+ }
}
static enum hrtimer_restart pd_timeout(struct hrtimer *timer)
@@ -1211,14 +1221,13 @@
case PE_SRC_READY:
pd->in_explicit_contract = true;
- if (pd->current_dr == DR_DFP) {
- /* don't start USB host until after SVDM discovery */
- if (pd->vdm_state == VDM_NONE)
- usbpd_send_svdm(pd, USBPD_SID,
- USBPD_SVDM_DISCOVER_IDENTITY,
- SVDM_CMD_TYPE_INITIATOR, 0,
- NULL, 0);
- }
+
+ if (pd->vdm_tx)
+ kick_sm(pd, 0);
+ else if (pd->current_dr == DR_DFP && pd->vdm_state == VDM_NONE)
+ usbpd_send_svdm(pd, USBPD_SID,
+ USBPD_SVDM_DISCOVER_IDENTITY,
+ SVDM_CMD_TYPE_INITIATOR, 0, NULL, 0);
kobject_uevent(&pd->dev.kobj, KOBJ_CHANGE);
complete(&pd->is_ready);
@@ -1358,6 +1367,14 @@
case PE_SNK_READY:
pd->in_explicit_contract = true;
+
+ if (pd->vdm_tx)
+ kick_sm(pd, 0);
+ else if (pd->current_dr == DR_DFP && pd->vdm_state == VDM_NONE)
+ usbpd_send_svdm(pd, USBPD_SID,
+ USBPD_SVDM_DISCOVER_IDENTITY,
+ SVDM_CMD_TYPE_INITIATOR, 0, NULL, 0);
+
kobject_uevent(&pd->dev.kobj, KOBJ_CHANGE);
complete(&pd->is_ready);
dual_role_instance_changed(pd->dual_role);
@@ -2046,6 +2063,10 @@
switch (pd->current_state) {
case PE_UNKNOWN:
+ val.intval = 0;
+ power_supply_set_property(pd->usb_psy,
+ POWER_SUPPLY_PROP_PD_IN_HARD_RESET, &val);
+
if (pd->current_pr == PR_SINK) {
usbpd_set_state(pd, PE_SNK_STARTUP);
} else if (pd->current_pr == PR_SRC) {
@@ -2208,8 +2229,11 @@
case PE_SRC_TRANSITION_TO_DEFAULT:
if (pd->vconn_enabled)
regulator_disable(pd->vconn);
+ pd->vconn_enabled = false;
+
if (pd->vbus_enabled)
regulator_disable(pd->vbus);
+ pd->vbus_enabled = false;
if (pd->current_dr != DR_DFP) {
extcon_set_state_sync(pd->extcon, EXTCON_USB, 0);
@@ -2217,24 +2241,9 @@
pd_phy_update_roles(pd->current_dr, pd->current_pr);
}
- msleep(SRC_RECOVER_TIME);
-
- pd->vbus_enabled = false;
- enable_vbus(pd);
-
- if (pd->vconn_enabled) {
- ret = regulator_enable(pd->vconn);
- if (ret) {
- usbpd_err(&pd->dev, "Unable to enable vconn\n");
- pd->vconn_enabled = false;
- }
- }
-
- val.intval = 0;
- power_supply_set_property(pd->usb_psy,
- POWER_SUPPLY_PROP_PD_IN_HARD_RESET, &val);
-
- usbpd_set_state(pd, PE_SRC_STARTUP);
+ /* PE_UNKNOWN will turn on VBUS and go back to PE_SRC_STARTUP */
+ pd->current_state = PE_UNKNOWN;
+ kick_sm(pd, SRC_RECOVER_TIME);
break;
case PE_SRC_HARD_RESET:
diff --git a/drivers/usb/pd/qpnp-pdphy.c b/drivers/usb/pd/qpnp-pdphy.c
index 735774a..6395ca2 100644
--- a/drivers/usb/pd/qpnp-pdphy.c
+++ b/drivers/usb/pd/qpnp-pdphy.c
@@ -112,7 +112,6 @@
int tx_status;
u8 frame_filter_val;
bool in_test_data_mode;
- bool rx_busy;
enum data_role data_role;
enum power_role power_role;
@@ -490,7 +489,7 @@
}
ret = pdphy_reg_read(pdphy, &val, USB_PDPHY_RX_ACKNOWLEDGE, 1);
- if (ret || val || pdphy->rx_busy) {
+ if (ret || val) {
dev_err(pdphy->dev, "%s: RX message pending\n", __func__);
return -EBUSY;
}
@@ -668,15 +667,6 @@
BIST_MODE_MASK | BIST_ENABLE, bist_mode | BIST_ENABLE);
}
-static irqreturn_t pdphy_msg_rx_irq(int irq, void *data)
-{
- struct usb_pdphy *pdphy = data;
-
- pdphy->rx_busy = true;
-
- return IRQ_WAKE_THREAD;
-}
-
static irqreturn_t pdphy_msg_rx_irq_thread(int irq, void *data)
{
u8 size, rx_status, frame_type;
@@ -733,7 +723,6 @@
false);
pdphy->rx_bytes += size + 1;
done:
- pdphy->rx_busy = false;
return IRQ_HANDLED;
}
@@ -819,7 +808,7 @@
return ret;
ret = pdphy_request_irq(pdphy, pdev->dev.of_node,
- &pdphy->msg_rx_irq, "msg-rx", pdphy_msg_rx_irq,
+ &pdphy->msg_rx_irq, "msg-rx", NULL,
pdphy_msg_rx_irq_thread, (IRQF_TRIGGER_RISING | IRQF_ONESHOT));
if (ret < 0)
return ret;
diff --git a/drivers/usb/phy/phy-msm-qusb-v2.c b/drivers/usb/phy/phy-msm-qusb-v2.c
index 68bd576..1f80cde 100644
--- a/drivers/usb/phy/phy-msm-qusb-v2.c
+++ b/drivers/usb/phy/phy-msm-qusb-v2.c
@@ -507,17 +507,8 @@
dev_dbg(phy->dev, "%s\n", __func__);
- qusb_phy_enable_clocks(qphy, true);
+ qusb_phy_enable_power(qphy, false);
- /* Disable the PHY */
- writel_relaxed(readl_relaxed(qphy->base + qphy->phy_reg[PWR_CTRL1]) |
- PWR_CTRL1_POWR_DOWN,
- qphy->base + qphy->phy_reg[PWR_CTRL1]);
-
- /* Makes sure that above write goes through */
- wmb();
-
- qusb_phy_enable_clocks(qphy, false);
}
static u32 qusb_phy_get_linestate(struct qusb_phy *qphy)
@@ -1049,6 +1040,21 @@
if (ret)
return ret;
+ /* ldo24 is turned on and eud is pet irrespective of cable
+ * cable connection status by boot sw. Assume usb cable is not
+ * connected and perform detach pet. If usb cable is connected,
+ * eud hw will be pet in the dpdm callback.
+ */
+ if (qphy->eud_base) {
+ if (qphy->cfg_ahb_clk)
+ clk_prepare_enable(qphy->cfg_ahb_clk);
+
+ writel_relaxed(0, qphy->eud_base + EUD_SW_ATTACH_DET);
+
+ if (qphy->cfg_ahb_clk)
+ clk_disable_unprepare(qphy->cfg_ahb_clk);
+ }
+
ret = qusb_phy_regulator_init(qphy);
if (ret)
usb_remove_phy(&qphy->phy);
diff --git a/include/dt-bindings/clock/qcom,cpucc-sdm845.h b/include/dt-bindings/clock/qcom,cpucc-sdm845.h
index db3c940..f039284 100644
--- a/include/dt-bindings/clock/qcom,cpucc-sdm845.h
+++ b/include/dt-bindings/clock/qcom,cpucc-sdm845.h
@@ -27,5 +27,8 @@
#define CPU5_PERFCL_CLK 10
#define CPU6_PERFCL_CLK 11
#define CPU7_PERFCL_CLK 12
+#define L3_MISC_VOTE_CLK 13
+#define CPU4_PWRCL_CLK 14
+#define CPU5_PWRCL_CLK 15
#endif
diff --git a/include/dt-bindings/clock/qcom,gpucc-sdm845.h b/include/dt-bindings/clock/qcom,gpucc-sdm845.h
index a20d4f0..a82a630 100644
--- a/include/dt-bindings/clock/qcom,gpucc-sdm845.h
+++ b/include/dt-bindings/clock/qcom,gpucc-sdm845.h
@@ -17,33 +17,32 @@
/* GPUCC clock registers */
#define GPU_CC_ACD_AHB_CLK 0
#define GPU_CC_ACD_CXO_CLK 1
-#define GPU_CC_AHB_CLK 2
-#define GPU_CC_CRC_AHB_CLK 3
-#define GPU_CC_CX_APB_CLK 4
-#define GPU_CC_CX_GMU_CLK 5
-#define GPU_CC_CX_QDSS_AT_CLK 6
-#define GPU_CC_CX_QDSS_TRIG_CLK 7
-#define GPU_CC_CX_QDSS_TSCTR_CLK 8
-#define GPU_CC_CX_SNOC_DVM_CLK 9
-#define GPU_CC_CXO_AON_CLK 10
-#define GPU_CC_CXO_CLK 11
-#define GPU_CC_GX_GMU_CLK 12
-#define GPU_CC_GX_QDSS_TSCTR_CLK 13
-#define GPU_CC_GX_VSENSE_CLK 14
-#define GPU_CC_PLL0_OUT_MAIN 15
-#define GPU_CC_PLL0_OUT_ODD 16
-#define GPU_CC_PLL0_OUT_TEST 17
-#define GPU_CC_PLL1 18
-#define GPU_CC_PLL1_OUT_EVEN 19
-#define GPU_CC_PLL1_OUT_MAIN 20
-#define GPU_CC_PLL1_OUT_ODD 21
-#define GPU_CC_PLL1_OUT_TEST 22
-#define GPU_CC_PLL_TEST_CLK 23
-#define GPU_CC_SLEEP_CLK 24
-#define GPU_CC_GMU_CLK_SRC 25
-#define GPU_CC_CX_GFX3D_CLK 26
-#define GPU_CC_CX_GFX3D_SLV_CLK 27
-#define GPU_CC_PLL0 28
+#define GPU_CC_CRC_AHB_CLK 2
+#define GPU_CC_CX_APB_CLK 3
+#define GPU_CC_CX_GMU_CLK 4
+#define GPU_CC_CX_QDSS_AT_CLK 5
+#define GPU_CC_CX_QDSS_TRIG_CLK 6
+#define GPU_CC_CX_QDSS_TSCTR_CLK 7
+#define GPU_CC_CX_SNOC_DVM_CLK 8
+#define GPU_CC_CXO_AON_CLK 9
+#define GPU_CC_CXO_CLK 10
+#define GPU_CC_GX_GMU_CLK 11
+#define GPU_CC_GX_QDSS_TSCTR_CLK 12
+#define GPU_CC_GX_VSENSE_CLK 13
+#define GPU_CC_PLL0_OUT_MAIN 14
+#define GPU_CC_PLL0_OUT_ODD 15
+#define GPU_CC_PLL0_OUT_TEST 16
+#define GPU_CC_PLL1 17
+#define GPU_CC_PLL1_OUT_EVEN 18
+#define GPU_CC_PLL1_OUT_MAIN 19
+#define GPU_CC_PLL1_OUT_ODD 20
+#define GPU_CC_PLL1_OUT_TEST 21
+#define GPU_CC_PLL_TEST_CLK 22
+#define GPU_CC_SLEEP_CLK 23
+#define GPU_CC_GMU_CLK_SRC 24
+#define GPU_CC_CX_GFX3D_CLK 25
+#define GPU_CC_CX_GFX3D_SLV_CLK 26
+#define GPU_CC_PLL0 27
/* GPUCC reset clock registers */
#define GPUCC_GPU_CC_ACD_BCR 0
diff --git a/include/dt-bindings/msm/msm-bus-ids.h b/include/dt-bindings/msm/msm-bus-ids.h
index e20175d..ffa4bb0 100644
--- a/include/dt-bindings/msm/msm-bus-ids.h
+++ b/include/dt-bindings/msm/msm-bus-ids.h
@@ -89,6 +89,13 @@
#define MSM_BUS_BCM_ACV 7037
#define MSM_BUS_BCM_ALC 7038
#define MSM_BUS_BCM_QUP0 7039
+#define MSM_BUS_BCM_CE 7040
+#define MSM_BUS_BCM_PN0 7041
+#define MSM_BUS_BCM_PN1 7042
+#define MSM_BUS_BCM_PN2 7043
+#define MSM_BUS_BCM_PN3 7044
+#define MSM_BUS_BCM_PN4 7045
+#define MSM_BUS_BCM_PN5 7046
#define MSM_BUS_RSC_APPS 8000
#define MSM_BUS_RSC_DISP 8001
@@ -252,7 +259,10 @@
#define MSM_BUS_MASTER_CAMNOC_SF_UNCOMP 148
#define MSM_BUS_MASTER_GIC 149
#define MSM_BUS_MASTER_EMMC 150
-#define MSM_BUS_MASTER_MASTER_LAST 151
+#define MSM_BUS_MASTER_SPMI_FETCHER 151
+#define MSM_BUS_MASTER_ANOC_SNOC 152
+#define MSM_BUS_MASTER_ANOC_IPA 153
+#define MSM_BUS_MASTER_MASTER_LAST 154
#define MSM_BUS_MASTER_LLCC_DISPLAY 20000
#define MSM_BUS_MASTER_MNOC_HF_MEM_NOC_DISPLAY 20001
@@ -593,7 +603,11 @@
#define MSM_BUS_SLAVE_MEM_NOC_SNOC 776
#define MSM_BUS_SLAVE_IPA_CORE 777
#define MSM_BUS_SLAVE_CAMNOC_UNCOMP 778
-#define MSM_BUS_SLAVE_LAST 779
+#define MSM_BUS_SLAVE_ANOC_SNOC 779
+#define MSM_BUS_SLAVE_ANOC_IPA 780
+#define MSM_BUS_SLAVE_EMAC_CFG 781
+#define MSM_BUS_SLAVE_EMMC_CFG 782
+#define MSM_BUS_SLAVE_LAST 783
#define MSM_BUS_SLAVE_EBI_CH0_DISPLAY 20512
#define MSM_BUS_SLAVE_LLCC_DISPLAY 20513
diff --git a/include/linux/coresight.h b/include/linux/coresight.h
index a8003cc7..32c3d42 100644
--- a/include/linux/coresight.h
+++ b/include/linux/coresight.h
@@ -267,7 +267,7 @@
static inline void coresight_abort(void) {}
#endif
-#ifdef CONFIG_OF
+#if defined(CONFIG_OF) && defined(CONFIG_CORESIGHT)
extern struct coresight_platform_data *of_get_coresight_platform_data(
struct device *dev, struct device_node *node);
extern struct coresight_cti_data *of_get_coresight_cti_data(
@@ -275,7 +275,7 @@
#else
static inline struct coresight_platform_data *of_get_coresight_platform_data(
struct device *dev, struct device_node *node) { return NULL; }
-static inlint struct coresight_cti_data *of_get_coresight_cti_data(
+static inline struct coresight_cti_data *of_get_coresight_cti_data(
struct device *dev, struct device_node *node) { return NULL; }
#endif
diff --git a/include/linux/cpu.h b/include/linux/cpu.h
index 40b66f9..e0aa720 100644
--- a/include/linux/cpu.h
+++ b/include/linux/cpu.h
@@ -194,6 +194,7 @@
extern void cpu_hotplug_begin(void);
extern void cpu_hotplug_done(void);
extern void get_online_cpus(void);
+extern void cpu_hotplug_mutex_held(void);
extern void put_online_cpus(void);
extern void cpu_hotplug_disable(void);
extern void cpu_hotplug_enable(void);
@@ -216,6 +217,7 @@
#define cpu_hotplug_enable() do { } while (0)
#define hotcpu_notifier(fn, pri) do { (void)(fn); } while (0)
#define __hotcpu_notifier(fn, pri) do { (void)(fn); } while (0)
+#define cpu_hotplug_mutex_held() do { } while (0)
/* These aren't inline functions due to a GCC bug. */
#define register_hotcpu_notifier(nb) ({ (void)(nb); 0; })
#define __register_hotcpu_notifier(nb) ({ (void)(nb); 0; })
diff --git a/include/linux/cpuhotplug.h b/include/linux/cpuhotplug.h
index 6be0299..7a2ae2f 100644
--- a/include/linux/cpuhotplug.h
+++ b/include/linux/cpuhotplug.h
@@ -128,6 +128,7 @@
CPUHP_AP_WORKQUEUE_ONLINE,
CPUHP_AP_RCUTREE_ONLINE,
CPUHP_AP_NOTIFY_ONLINE,
+ CPUHP_AP_NOTIFY_PERF_ONLINE,
CPUHP_AP_ONLINE_DYN,
CPUHP_AP_ONLINE_DYN_END = CPUHP_AP_ONLINE_DYN + 30,
CPUHP_AP_X86_HPET_ONLINE,
diff --git a/include/linux/if_bridge.h b/include/linux/if_bridge.h
index c6587c0..6638a22 100644
--- a/include/linux/if_bridge.h
+++ b/include/linux/if_bridge.h
@@ -53,6 +53,8 @@
typedef int br_should_route_hook_t(struct sk_buff *skb);
extern br_should_route_hook_t __rcu *br_should_route_hook;
+extern struct net_device *br_port_dev_get(struct net_device *dev,
+ unsigned char *addr);
#if IS_ENABLED(CONFIG_BRIDGE) && IS_ENABLED(CONFIG_BRIDGE_IGMP_SNOOPING)
int br_multicast_list_adjacent(struct net_device *dev,
diff --git a/include/linux/ipa_wdi3.h b/include/linux/ipa_wdi3.h
new file mode 100644
index 0000000..aed8c59
--- /dev/null
+++ b/include/linux/ipa_wdi3.h
@@ -0,0 +1,254 @@
+/* Copyright (c) 2017, 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 _IPA_WDI3_H_
+#define _IPA_WDI3_H_
+
+#include <linux/ipa.h>
+
+#define IPA_HW_WDI3_TCL_DATA_CMD_ER_DESC_SIZE 32
+#define IPA_HW_WDI3_IPA2FW_ER_DESC_SIZE 8
+
+#define IPA_HW_WDI3_MAX_ER_DESC_SIZE \
+ (((IPA_HW_WDI3_TCL_DATA_CMD_ER_DESC_SIZE) > \
+ (IPA_HW_WDI3_IPA2FW_ER_DESC_SIZE)) ? \
+ (IPA_HW_WDI3_TCL_DATA_CMD_ER_DESC_SIZE) : \
+ (IPA_HW_WDI3_IPA2FW_ER_DESC_SIZE))
+
+/**
+ * struct ipa_wdi3_hdr_info - Header to install on IPA HW
+ *
+ * @hdr: header to install on IPA HW
+ * @hdr_len: length of header
+ * @dst_mac_addr_offset: destination mac address offset
+ * @hdr_type: layer two header type
+ */
+struct ipa_wdi3_hdr_info {
+ u8 *hdr;
+ u8 hdr_len;
+ u8 dst_mac_addr_offset;
+ enum ipa_hdr_l2_type hdr_type;
+};
+
+/**
+ * struct ipa_wdi3_reg_intf_in_params - parameters for uC offload
+ * interface registration
+ *
+ * @netdev_name: network interface name
+ * @hdr_info: header information
+ * @is_meta_data_valid: if meta data is valid
+ * @meta_data: meta data if any
+ * @meta_data_mask: meta data mask
+ */
+struct ipa_wdi3_reg_intf_in_params {
+ const char *netdev_name;
+ struct ipa_wdi3_hdr_info hdr_info[IPA_IP_MAX];
+ u8 is_meta_data_valid;
+ u32 meta_data;
+ u32 meta_data_mask;
+};
+
+/**
+ * struct ipa_wdi3_setup_info - WDI3 TX/Rx configuration
+ * @ipa_ep_cfg: ipa endpoint configuration
+ * @client: type of "client"
+ * @transfer_ring_base_pa: physical address of the base of the transfer ring
+ * @transfer_ring_size: size of the transfer ring
+ * @transfer_ring_doorbell_pa: physical address of the doorbell that
+ IPA uC will update the tailpointer of the transfer ring
+ * @event_ring_base_pa: physical address of the base of the event ring
+ * @event_ring_size: event ring size
+ * @event_ring_doorbell_pa: physical address of the doorbell that IPA uC
+ will update the headpointer of the event ring
+ * @num_pkt_buffers: Number of pkt buffers allocated. The size of the event
+ ring and the transfer ring has to be atleast ( num_pkt_buffers + 1)
+ * @pkt_offset: packet offset (wdi3 header length)
+ * @desc_format_template[IPA_HW_WDI3_MAX_ER_DESC_SIZE]: Holds a cached
+ template of the desc format
+ */
+struct ipa_wdi3_setup_info {
+ struct ipa_ep_cfg ipa_ep_cfg;
+ enum ipa_client_type client;
+ dma_addr_t transfer_ring_base_pa;
+ u32 transfer_ring_size;
+ dma_addr_t transfer_ring_doorbell_pa;
+
+ dma_addr_t event_ring_base_pa;
+ u32 event_ring_size;
+ dma_addr_t event_ring_doorbell_pa;
+ u16 num_pkt_buffers;
+
+ u16 pkt_offset;
+
+ u32 desc_format_template[IPA_HW_WDI3_MAX_ER_DESC_SIZE];
+};
+
+/**
+ * struct ipa_wdi3_conn_in_params - information provided by
+ * uC offload client
+ * @notify: client callback function
+ * @priv: client cookie
+ * @tx: parameters to connect TX pipe(from IPA to WLAN)
+ * @rx: parameters to connect RX pipe(from WLAN to IPA)
+ */
+struct ipa_wdi3_conn_in_params {
+ ipa_notify_cb notify;
+ void *priv;
+ struct ipa_wdi3_setup_info tx;
+ struct ipa_wdi3_setup_info rx;
+};
+
+/**
+ * struct ipa_wdi3_conn_out_params - information provided
+ * to WLAN driver
+ * @tx_uc_db_pa: physical address of IPA uC doorbell for TX
+ * @tx_uc_db_va: virtual address of IPA uC doorbell for TX
+ * @rx_uc_db_pa: physical address of IPA uC doorbell for RX
+ */
+struct ipa_wdi3_conn_out_params {
+ dma_addr_t tx_uc_db_pa;
+ void __iomem *tx_uc_db_va;
+ dma_addr_t rx_uc_db_pa;
+};
+
+/**
+ * struct ipa_wdi3_perf_profile - To set BandWidth profile
+ *
+ * @client: type of client
+ * @max_supported_bw_mbps: maximum bandwidth needed (in Mbps)
+ */
+struct ipa_wdi3_perf_profile {
+ enum ipa_client_type client;
+ u32 max_supported_bw_mbps;
+};
+
+#if defined CONFIG_IPA || defined CONFIG_IPA3
+
+/**
+ * ipa_wdi3_reg_intf - Client should call this function to
+ * init WDI3 IPA offload data path
+ *
+ * Note: Should not be called from atomic context and only
+ * after checking IPA readiness using ipa_register_ipa_ready_cb()
+ *
+ * @Return 0 on success, negative on failure
+ */
+int ipa_wdi3_reg_intf(
+ struct ipa_wdi3_reg_intf_in_params *in);
+
+/**
+ * ipa_wdi3_dereg_intf - Client Driver should call this
+ * function to deregister before unload and after disconnect
+ *
+ * @Return 0 on success, negative on failure
+ */
+int ipa_wdi3_dereg_intf(const char *netdev_name);
+
+/**
+ * ipa_wdi3_conn_pipes - Client should call this
+ * function to connect pipes
+ *
+ * @in: [in] input parameters from client
+ * @out: [out] output params to client
+ *
+ * Note: Should not be called from atomic context and only
+ * after checking IPA readiness using ipa_register_ipa_ready_cb()
+ *
+ * @Return 0 on success, negative on failure
+ */
+int ipa_wdi3_conn_pipes(struct ipa_wdi3_conn_in_params *in,
+ struct ipa_wdi3_conn_out_params *out);
+
+/**
+ * ipa_wdi3_disconn_pipes() - Client should call this
+ * function to disconnect pipes
+ *
+ * Note: Should not be called from atomic context
+ *
+ * Returns: 0 on success, negative on failure
+ */
+int ipa_wdi3_disconn_pipes(void);
+
+/**
+ * ipa_wdi3_enable_pipes() - Client should call this
+ * function to enable IPA offload data path
+ *
+ * Note: Should not be called from atomic context
+ *
+ * Returns: 0 on success, negative on failure
+ */
+int ipa_wdi3_enable_pipes(void);
+
+/**
+ * ipa_wdi3_disable_pipes() - Client should call this
+ * function to disable IPA offload data path
+ *
+ * Note: Should not be called from atomic context
+ *
+ * Returns: 0 on success, negative on failure
+ */
+int ipa_wdi3_disable_pipes(void);
+
+/**
+ * ipa_wdi3_set_perf_profile() - Client should call this function to
+ * set IPA clock bandwidth based on data rates
+ *
+ * @profile: [in] BandWidth profile to use
+ *
+ * Returns: 0 on success, negative on failure
+ */
+int ipa_wdi3_set_perf_profile(struct ipa_wdi3_perf_profile *profile);
+
+
+#else /* (CONFIG_IPA || CONFIG_IPA3) */
+
+static inline int ipa_wdi3_reg_intf(
+ struct ipa_wdi3_reg_intf_in_params *in)
+{
+ return -EPERM;
+}
+
+static inline int ipa_wdi3_dereg_intf(const char *netdev_name)
+{
+ return -EPERM;
+}
+
+static inline int ipa_wdi3_conn_pipes(struct ipa_wdi3_conn_in_params *in,
+ struct ipa_wdi3_conn_out_params *out)
+{
+ return -EPERM;
+}
+
+static inline int ipa_wdi3_disconn_pipes(void)
+{
+ return -EPERM;
+}
+
+static inline int ipa_wdi3_enable_pipes(void)
+{
+ return -EPERM;
+}
+
+static inline int ipa_wdi3_disable_pipes(void)
+{
+ return -EPERM;
+}
+
+static inline int ipa_wdi3_set_perf_profile(
+ struct ipa_wdi3_perf_profile *profile)
+{
+ return -EPERM;
+}
+
+#endif /* CONFIG_IPA3 */
+
+#endif /* _IPA_WDI3_H_ */
diff --git a/include/linux/mmc/card.h b/include/linux/mmc/card.h
index 72dd7ba..7425430 100644
--- a/include/linux/mmc/card.h
+++ b/include/linux/mmc/card.h
@@ -436,7 +436,6 @@
struct mmc_wr_pack_stats wr_pack_stats; /* packed commands stats*/
struct notifier_block reboot_notify;
enum mmc_pon_type pon_type;
- u8 *cached_ext_csd;
bool cmdq_init;
struct mmc_bkops_info bkops;
bool err_in_sdr104;
diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h
index 1e07ed2..089c18b 100644
--- a/include/linux/mmc/host.h
+++ b/include/linux/mmc/host.h
@@ -96,6 +96,23 @@
MMC_LOAD_LOW,
};
+enum {
+ MMC_ERR_CMD_TIMEOUT,
+ MMC_ERR_CMD_CRC,
+ MMC_ERR_DAT_TIMEOUT,
+ MMC_ERR_DAT_CRC,
+ MMC_ERR_AUTO_CMD,
+ MMC_ERR_ADMA,
+ MMC_ERR_TUNING,
+ MMC_ERR_CMDQ_RED,
+ MMC_ERR_CMDQ_GCE,
+ MMC_ERR_CMDQ_ICCE,
+ MMC_ERR_REQ_TIMEOUT,
+ MMC_ERR_CMDQ_REQ_TIMEOUT,
+ MMC_ERR_ICE_CFG,
+ MMC_ERR_MAX,
+};
+
struct mmc_cmdq_host_ops {
int (*init)(struct mmc_host *host);
int (*enable)(struct mmc_host *host);
@@ -563,6 +580,7 @@
struct dentry *debugfs_root;
bool err_occurred;
+ u32 err_stats[MMC_ERR_MAX];
struct mmc_async_req *areq; /* active async req */
struct mmc_context_info context_info; /* async synchronization info */
diff --git a/include/linux/msm_bcl.h b/include/linux/msm_bcl.h
new file mode 100644
index 0000000..3b84f37
--- /dev/null
+++ b/include/linux/msm_bcl.h
@@ -0,0 +1,104 @@
+/*
+ * Copyright (c) 2014, 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 __MSM_BCL_H
+#define __MSM_BCL_H
+
+#define BCL_NAME_MAX_LEN 20
+
+enum bcl_trip_type {
+ BCL_HIGH_TRIP,
+ BCL_LOW_TRIP,
+ BCL_TRIP_MAX,
+};
+
+enum bcl_param {
+ BCL_PARAM_VOLTAGE,
+ BCL_PARAM_CURRENT,
+ BCL_PARAM_MAX,
+};
+
+struct bcl_threshold {
+ int trip_value;
+ enum bcl_trip_type type;
+ void *trip_data;
+ void (*trip_notify) (enum bcl_trip_type, int, void *);
+};
+struct bcl_param_data;
+struct bcl_driver_ops {
+ int (*read) (int *);
+ int (*set_high_trip) (int);
+ int (*get_high_trip) (int *);
+ int (*set_low_trip) (int);
+ int (*get_low_trip) (int *);
+ int (*disable) (void);
+ int (*enable) (void);
+ int (*notify) (struct bcl_param_data *, int,
+ enum bcl_trip_type);
+};
+
+struct bcl_param_data {
+ char name[BCL_NAME_MAX_LEN];
+ struct device device;
+ struct bcl_driver_ops *ops;
+ int high_trip;
+ int low_trip;
+ int last_read_val;
+ bool registered;
+ struct kobj_attribute val_attr;
+ struct kobj_attribute high_trip_attr;
+ struct kobj_attribute low_trip_attr;
+ struct attribute_group bcl_attr_gp;
+ struct bcl_threshold *thresh[BCL_TRIP_MAX];
+};
+
+#ifdef CONFIG_MSM_BCL_CTL
+struct bcl_param_data *msm_bcl_register_param(enum bcl_param,
+ struct bcl_driver_ops *, char *);
+int msm_bcl_unregister_param(struct bcl_param_data *);
+int msm_bcl_enable(void);
+int msm_bcl_disable(void);
+int msm_bcl_set_threshold(enum bcl_param, enum bcl_trip_type,
+ struct bcl_threshold *);
+int msm_bcl_read(enum bcl_param, int *);
+#else
+static inline struct bcl_param_data *msm_bcl_register_param(
+ enum bcl_param param_type, struct bcl_driver_ops *ops, char *name)
+{
+ return NULL;
+}
+static inline int msm_bcl_unregister_param(struct bcl_param_data *data)
+{
+ return -ENOSYS;
+}
+static inline int msm_bcl_enable(void)
+{
+ return -ENOSYS;
+}
+static inline int msm_bcl_disable(void)
+{
+ return -ENOSYS;
+}
+static inline int msm_bcl_set_threshold(enum bcl_param param_type,
+ enum bcl_trip_type type,
+ struct bcl_threshold *inp_thresh)
+{
+ return -ENOSYS;
+}
+static inline int msm_bcl_read(enum bcl_param param_type, int *vbat_value)
+{
+ return -ENOSYS;
+}
+#endif
+
+#endif /*__MSM_BCL_H*/
diff --git a/include/linux/usb_bam.h b/include/linux/usb_bam.h
index 62fd4e4..1b0ca4a 100644
--- a/include/linux/usb_bam.h
+++ b/include/linux/usb_bam.h
@@ -198,7 +198,7 @@
};
/**
- * struct msm_usb_bam_platform_data: pipe connection information
+ * struct msm_usb_bam_data: pipe connection information
* between USB/HSIC BAM and another BAM. USB/HSIC BAM can be
* either src BAM or dst BAM
* @usb_bam_num_pipes: max number of pipes to use.
@@ -218,7 +218,7 @@
* can work at in bam2bam mode when connected to SS host.
* @enable_hsusb_bam_on_boot: Enable HSUSB BAM (non-NDP) on bootup itself
*/
-struct msm_usb_bam_platform_data {
+struct msm_usb_bam_data {
u8 max_connections;
int usb_bam_num_pipes;
phys_addr_t usb_bam_fifo_baseaddr;
diff --git a/include/net/neighbour.h b/include/net/neighbour.h
index 8b68384..ddfb742 100644
--- a/include/net/neighbour.h
+++ b/include/net/neighbour.h
@@ -62,6 +62,7 @@
NEIGH_VAR_GC_THRESH1,
NEIGH_VAR_GC_THRESH2,
NEIGH_VAR_GC_THRESH3,
+ NEIGH_VAR_PROBE,
NEIGH_VAR_MAX
};
diff --git a/include/net/netfilter/nf_conntrack.h b/include/net/netfilter/nf_conntrack.h
index d9d52c0..7815545 100644
--- a/include/net/netfilter/nf_conntrack.h
+++ b/include/net/netfilter/nf_conntrack.h
@@ -314,6 +314,7 @@
extern unsigned int nf_conntrack_htable_size;
extern seqcount_t nf_conntrack_generation;
extern unsigned int nf_conntrack_max;
+extern unsigned int nf_conntrack_pkt_threshold;
/* must be called with rcu read lock held */
static inline void
diff --git a/include/net/udp.h b/include/net/udp.h
index 4948790..a32c58e 100644
--- a/include/net/udp.h
+++ b/include/net/udp.h
@@ -247,6 +247,7 @@
/* net/ipv4/udp.c */
void udp_v4_early_demux(struct sk_buff *skb);
+bool udp_sk_rx_dst_set(struct sock *sk, struct dst_entry *dst);
int udp_get_port(struct sock *sk, unsigned short snum,
int (*saddr_cmp)(const struct sock *,
const struct sock *));
diff --git a/include/soc/qcom/spm.h b/include/soc/qcom/spm.h
index 1ed100b..d0f76d0 100644
--- a/include/soc/qcom/spm.h
+++ b/include/soc/qcom/spm.h
@@ -125,6 +125,27 @@
return false;
}
+static inline int msm_spm_is_avs_enabled(unsigned int cpu)
+{
+ return -ENODEV;
+}
+
+static inline int msm_spm_avs_enable(unsigned int cpu)
+{
+ return -ENODEV;
+}
+
+static inline int msm_spm_avs_disable(unsigned int cpu)
+{
+ return -ENODEV;
+}
+
+static inline int msm_spm_avs_set_limit(unsigned int cpu, uint32_t min_lvl,
+ uint32_t max_lvl)
+{
+ return -ENODEV;
+}
+
static inline int msm_spm_avs_enable_irq(unsigned int cpu,
enum msm_spm_avs_irq irq)
{
diff --git a/include/sound/pcm.h b/include/sound/pcm.h
index 8495824..348d958 100644
--- a/include/sound/pcm.h
+++ b/include/sound/pcm.h
@@ -106,7 +106,7 @@
#endif
#define SNDRV_PCM_IOCTL1_RESET 0
-#define SNDRV_PCM_IOCTL1_INFO 1
+/* 1 is absent slot. */
#define SNDRV_PCM_IOCTL1_CHANNEL_INFO 2
#define SNDRV_PCM_IOCTL1_GSTATE 3
#define SNDRV_PCM_IOCTL1_FIFO_SIZE 4
@@ -466,6 +466,7 @@
const struct snd_pcm_ops *ops;
/* -- runtime information -- */
struct snd_pcm_runtime *runtime;
+ spinlock_t runtime_lock;
/* -- timer section -- */
struct snd_timer *timer; /* timer */
unsigned timer_running: 1; /* time is running */
diff --git a/include/trace/events/cpuhp.h b/include/trace/events/cpuhp.h
index 996953d..7f4eba4 100644
--- a/include/trace/events/cpuhp.h
+++ b/include/trace/events/cpuhp.h
@@ -88,6 +88,34 @@
__entry->cpu, __entry->state, __entry->idx, __entry->ret)
);
+TRACE_EVENT(cpuhp_latency,
+
+ TP_PROTO(unsigned int cpu, unsigned int state,
+ u64 start_time, int ret),
+
+ TP_ARGS(cpu, state, start_time, ret),
+
+ TP_STRUCT__entry(
+ __field(unsigned int, cpu)
+ __field(unsigned int, state)
+ __field(u64, time)
+ __field(int, ret)
+ ),
+
+ TP_fast_assign(
+ __entry->cpu = cpu;
+ __entry->state = state;
+ __entry->time = div64_u64(sched_clock() - start_time, 1000);
+ __entry->ret = ret;
+ ),
+
+ TP_printk(" cpu:%d state:%s latency:%llu USEC ret: %d",
+ __entry->cpu, __entry->state ? "online" : "offline",
+ __entry->time, __entry->ret)
+);
+
+
+
#endif
/* This part must be outside protection */
diff --git a/include/trace/events/power.h b/include/trace/events/power.h
index cecfb8f..408fa57 100644
--- a/include/trace/events/power.h
+++ b/include/trace/events/power.h
@@ -702,9 +702,10 @@
TRACE_EVENT(memlat_dev_meas,
TP_PROTO(const char *name, unsigned int dev_id, unsigned long inst,
- unsigned long mem, unsigned long freq, unsigned int ratio),
+ unsigned long mem, unsigned long freq, unsigned int stall,
+ unsigned int ratio),
- TP_ARGS(name, dev_id, inst, mem, freq, ratio),
+ TP_ARGS(name, dev_id, inst, mem, freq, stall, ratio),
TP_STRUCT__entry(
__string(name, name)
@@ -712,6 +713,7 @@
__field(unsigned long, inst)
__field(unsigned long, mem)
__field(unsigned long, freq)
+ __field(unsigned int, stall)
__field(unsigned int, ratio)
),
@@ -721,15 +723,17 @@
__entry->inst = inst;
__entry->mem = mem;
__entry->freq = freq;
+ __entry->stall = stall;
__entry->ratio = ratio;
),
- TP_printk("dev: %s, id=%u, inst=%lu, mem=%lu, freq=%lu, ratio=%u",
+ TP_printk("dev: %s, id=%u, inst=%lu, mem=%lu, freq=%lu, stall=%u, ratio=%u",
__get_str(name),
__entry->dev_id,
__entry->inst,
__entry->mem,
__entry->freq,
+ __entry->stall,
__entry->ratio)
);
@@ -797,6 +801,29 @@
__entry->pl, __entry->flags)
);
+TRACE_EVENT(sugov_next_freq,
+ TP_PROTO(unsigned int cpu, unsigned long util, unsigned long max,
+ unsigned int freq),
+ TP_ARGS(cpu, util, max, freq),
+ TP_STRUCT__entry(
+ __field( unsigned int, cpu)
+ __field( unsigned long, util)
+ __field( unsigned long, max)
+ __field( unsigned int, freq)
+ ),
+ TP_fast_assign(
+ __entry->cpu = cpu;
+ __entry->util = util;
+ __entry->max = max;
+ __entry->freq = freq;
+ ),
+ TP_printk("cpu=%u util=%lu max=%lu freq=%u",
+ __entry->cpu,
+ __entry->util,
+ __entry->max,
+ __entry->freq)
+);
+
DECLARE_EVENT_CLASS(kpm_module,
TP_PROTO(unsigned int managed_cpus, unsigned int max_cpus),
diff --git a/include/trace/events/sched.h b/include/trace/events/sched.h
index cd61786..b4bcedf 100644
--- a/include/trace/events/sched.h
+++ b/include/trace/events/sched.h
@@ -1775,16 +1775,18 @@
__field(u32, busy)
__field(u32, old_is_busy)
__field(u32, is_busy)
+ __field(bool, high_irqload)
),
TP_fast_assign(
__entry->cpu = cpu;
__entry->busy = busy;
__entry->old_is_busy = old_is_busy;
__entry->is_busy = is_busy;
+ __entry->high_irqload = sched_cpu_high_irqload(cpu);
),
- TP_printk("cpu=%u, busy=%u, old_is_busy=%u, new_is_busy=%u",
+ TP_printk("cpu=%u, busy=%u, old_is_busy=%u, new_is_busy=%u high_irqload=%d",
__entry->cpu, __entry->busy, __entry->old_is_busy,
- __entry->is_busy)
+ __entry->is_busy, __entry->high_irqload)
);
TRACE_EVENT(core_ctl_set_boost,
diff --git a/include/trace/events/thermal.h b/include/trace/events/thermal.h
index c0475a2..94ee287 100644
--- a/include/trace/events/thermal.h
+++ b/include/trace/events/thermal.h
@@ -20,6 +20,29 @@
{ THERMAL_TRIP_PASSIVE, "PASSIVE"}, \
{ THERMAL_TRIP_ACTIVE, "ACTIVE"})
+TRACE_EVENT(thermal_query_temp,
+
+ TP_PROTO(struct thermal_zone_device *tz, int temp),
+
+ TP_ARGS(tz, temp),
+
+ TP_STRUCT__entry(
+ __string(thermal_zone, tz->type)
+ __field(int, id)
+ __field(int, temp)
+ ),
+
+ TP_fast_assign(
+ __assign_str(thermal_zone, tz->type);
+ __entry->id = tz->id;
+ __entry->temp = temp;
+ ),
+
+ TP_printk("thermal_zone=%s id=%d temp=%d",
+ __get_str(thermal_zone), __entry->id,
+ __entry->temp)
+);
+
TRACE_EVENT(thermal_temperature,
TP_PROTO(struct thermal_zone_device *tz),
diff --git a/include/uapi/drm/msm_drm_pp.h b/include/uapi/drm/msm_drm_pp.h
index 0765527..fcf84e3 100644
--- a/include/uapi/drm/msm_drm_pp.h
+++ b/include/uapi/drm/msm_drm_pp.h
@@ -69,7 +69,39 @@
__u32 val[PA_VLUT_SIZE];
};
-/* struct drm_msm_memcol - Memory color feature strucuture.
+#define PA_HSIC_HUE_ENABLE (1 << 0)
+#define PA_HSIC_SAT_ENABLE (1 << 1)
+#define PA_HSIC_VAL_ENABLE (1 << 2)
+#define PA_HSIC_CONT_ENABLE (1 << 3)
+/**
+ * struct drm_msm_pa_hsic - pa hsic feature structure
+ * @flags: flags for the feature customization, values can be:
+ * - PA_HSIC_HUE_ENABLE: Enable hue adjustment
+ * - PA_HSIC_SAT_ENABLE: Enable saturation adjustment
+ * - PA_HSIC_VAL_ENABLE: Enable value adjustment
+ * - PA_HSIC_CONT_ENABLE: Enable contrast adjustment
+ *
+ * @hue: hue setting
+ * @saturation: saturation setting
+ * @value: value setting
+ * @contrast: contrast setting
+ */
+#define DRM_MSM_PA_HSIC
+struct drm_msm_pa_hsic {
+ __u64 flags;
+ __u32 hue;
+ __u32 saturation;
+ __u32 value;
+ __u32 contrast;
+};
+
+#define MEMCOL_PROT_HUE (1 << 0)
+#define MEMCOL_PROT_SAT (1 << 1)
+#define MEMCOL_PROT_VAL (1 << 2)
+#define MEMCOL_PROT_CONT (1 << 3)
+#define MEMCOL_PROT_SIXZONE (1 << 4)
+#define MEMCOL_PROT_BLEND (1 << 5)
+/* struct drm_msm_memcol - Memory color feature structure.
* Skin, sky, foliage features are supported.
* @prot_flags: Bit mask for enabling protection feature.
* @color_adjust_p0: Adjustment curve.
@@ -96,6 +128,42 @@
__u32 val_region;
};
+#define DRM_MSM_SIXZONE
+#define SIXZONE_LUT_SIZE 384
+#define SIXZONE_HUE_ENABLE (1 << 0)
+#define SIXZONE_SAT_ENABLE (1 << 1)
+#define SIXZONE_VAL_ENABLE (1 << 2)
+/* struct drm_msm_sixzone_curve - Sixzone HSV adjustment curve structure.
+ * @p0: Hue adjustment.
+ * @p1: Saturation/Value adjustment.
+ */
+struct drm_msm_sixzone_curve {
+ __u32 p1;
+ __u32 p0;
+};
+
+/* struct drm_msm_sixzone - Sixzone feature structure.
+ * @flags: for feature customization, values can be:
+ * - SIXZONE_HUE_ENABLE: Enable hue adjustment
+ * - SIXZONE_SAT_ENABLE: Enable saturation adjustment
+ * - SIXZONE_VAL_ENABLE: Enable value adjustment
+ * @threshold: threshold qualifier.
+ * @adjust_p0: Adjustment curve.
+ * @adjust_p1: Adjustment curve.
+ * @sat_hold: Saturation hold info.
+ * @val_hold: Value hold info.
+ * @curve: HSV adjustment curve lut.
+ */
+struct drm_msm_sixzone {
+ __u64 flags;
+ __u32 threshold;
+ __u32 adjust_p0;
+ __u32 adjust_p1;
+ __u32 sat_hold;
+ __u32 val_hold;
+ struct drm_msm_sixzone_curve curve[SIXZONE_LUT_SIZE];
+};
+
#define GAMUT_3D_MODE_17 1
#define GAMUT_3D_MODE_5 2
#define GAMUT_3D_MODE_13 3
diff --git a/include/uapi/linux/netfilter/nf_conntrack_common.h b/include/uapi/linux/netfilter/nf_conntrack_common.h
index 6d074d1..1d7cd67 100644
--- a/include/uapi/linux/netfilter/nf_conntrack_common.h
+++ b/include/uapi/linux/netfilter/nf_conntrack_common.h
@@ -113,8 +113,11 @@
IPCT_NATSEQADJ = IPCT_SEQADJ,
IPCT_SECMARK, /* new security mark has been set */
IPCT_LABEL, /* new connlabel has been set */
+ IPCT_COUNTER, /* Packet counters have matched. */
};
+#define IPCT_COUNTER IPCT_COUNTER
+
enum ip_conntrack_expect_events {
IPEXP_NEW, /* new expectation */
IPEXP_DESTROY, /* destroyed expectation */
diff --git a/include/uapi/linux/pci_regs.h b/include/uapi/linux/pci_regs.h
index e5a2e68..2c5810a 100644
--- a/include/uapi/linux/pci_regs.h
+++ b/include/uapi/linux/pci_regs.h
@@ -674,6 +674,7 @@
#define PCI_EXT_CAP_ID_PMUX 0x1A /* Protocol Multiplexing */
#define PCI_EXT_CAP_ID_PASID 0x1B /* Process Address Space ID */
#define PCI_EXT_CAP_ID_DPC 0x1D /* Downstream Port Containment */
+#define PCI_EXT_CAP_ID_L1SS 0x1E /* L1 PM Substates */
#define PCI_EXT_CAP_ID_PTM 0x1F /* Precision Time Measurement */
#define PCI_EXT_CAP_ID_MAX PCI_EXT_CAP_ID_PTM
@@ -977,4 +978,19 @@
#define PCI_PTM_CTRL_ENABLE 0x00000001 /* PTM enable */
#define PCI_PTM_CTRL_ROOT 0x00000002 /* Root select */
+/* L1 PM Substates */
+#define PCI_L1SS_CAP 4 /* capability register */
+#define PCI_L1SS_CAP_PCIPM_L1_2 1 /* PCI PM L1.2 Support */
+#define PCI_L1SS_CAP_PCIPM_L1_1 2 /* PCI PM L1.1 Support */
+#define PCI_L1SS_CAP_ASPM_L1_2 4 /* ASPM L1.2 Support */
+#define PCI_L1SS_CAP_ASPM_L1_1 8 /* ASPM L1.1 Support */
+#define PCI_L1SS_CAP_L1_PM_SS 16 /* L1 PM Substates Support */
+#define PCI_L1SS_CTL1 8 /* Control Register 1 */
+#define PCI_L1SS_CTL1_PCIPM_L1_2 1 /* PCI PM L1.2 Enable */
+#define PCI_L1SS_CTL1_PCIPM_L1_1 2 /* PCI PM L1.1 Support */
+#define PCI_L1SS_CTL1_ASPM_L1_2 4 /* ASPM L1.2 Support */
+#define PCI_L1SS_CTL1_ASPM_L1_1 8 /* ASPM L1.1 Support */
+#define PCI_L1SS_CTL1_L1SS_MASK 0x0000000F
+#define PCI_L1SS_CTL2 0xC /* Control Register 2 */
+
#endif /* LINUX_PCI_REGS_H */
diff --git a/include/uapi/media/msm_vidc.h b/include/uapi/media/msm_vidc.h
index 4fe325d..63fd555 100644
--- a/include/uapi/media/msm_vidc.h
+++ b/include/uapi/media/msm_vidc.h
@@ -6,6 +6,7 @@
#define MSM_VIDC_HAL_INTERLACE_COLOR_FORMAT_NV12 0x2
#define MSM_VIDC_HAL_INTERLACE_COLOR_FORMAT_NV12_UBWC 0x8002
#define MSM_VIDC_4x_1 0x1
+#define MSM_VIDC_EXTRADATA_FRAME_QP_ADV 0x1
struct msm_vidc_extradata_header {
unsigned int size;
@@ -137,6 +138,10 @@
struct msm_vidc_frame_qp_payload {
unsigned int frame_qp;
+ unsigned int qp_sum;
+ unsigned int skip_qp_sum;
+ unsigned int skip_num_blocks;
+ unsigned int total_num_blocks;
};
struct msm_vidc_frame_bits_info_payload {
diff --git a/kernel/cpu.c b/kernel/cpu.c
index 69dc428..0d10ef5 100644
--- a/kernel/cpu.c
+++ b/kernel/cpu.c
@@ -239,6 +239,11 @@
#define cpuhp_lock_acquire() lock_map_acquire(&cpu_hotplug.dep_map)
#define cpuhp_lock_release() lock_map_release(&cpu_hotplug.dep_map)
+void cpu_hotplug_mutex_held(void)
+{
+ lockdep_assert_held(&cpu_hotplug.lock);
+}
+EXPORT_SYMBOL(cpu_hotplug_mutex_held);
void get_online_cpus(void)
{
@@ -893,6 +898,7 @@
struct cpuhp_cpu_state *st = per_cpu_ptr(&cpuhp_state, cpu);
int prev_state, ret = 0;
bool hasdied = false;
+ u64 start_time = 0;
if (num_online_cpus() == 1)
return -EBUSY;
@@ -904,6 +910,8 @@
return -EBUSY;
cpu_hotplug_begin();
+ if (trace_cpuhp_latency_enabled())
+ start_time = sched_clock();
cpuhp_tasks_frozen = tasks_frozen;
@@ -942,6 +950,7 @@
hasdied = prev_state != st->state && st->state == CPUHP_OFFLINE;
out:
+ trace_cpuhp_latency(cpu, 0, start_time, ret);
cpu_hotplug_done();
/* This post dead nonsense must die */
if (!ret && hasdied)
@@ -1015,8 +1024,11 @@
struct cpuhp_cpu_state *st = per_cpu_ptr(&cpuhp_state, cpu);
struct task_struct *idle;
int ret = 0;
+ u64 start_time = 0;
cpu_hotplug_begin();
+ if (trace_cpuhp_latency_enabled())
+ start_time = sched_clock();
if (!cpu_present(cpu)) {
ret = -EINVAL;
@@ -1064,6 +1076,7 @@
target = min((int)target, CPUHP_BRINGUP_CPU);
ret = cpuhp_up_callbacks(cpu, st, target);
out:
+ trace_cpuhp_latency(cpu, 1, start_time, ret);
cpu_hotplug_done();
return ret;
}
diff --git a/kernel/cpuset.c b/kernel/cpuset.c
index d3a7411..cbf240d 100644
--- a/kernel/cpuset.c
+++ b/kernel/cpuset.c
@@ -808,16 +808,15 @@
* 'cpus' is removed, then call this routine to rebuild the
* scheduler's dynamic sched domains.
*
- * Call with cpuset_mutex held. Takes get_online_cpus().
*/
-static void rebuild_sched_domains_locked(void)
+static void rebuild_sched_domains_unlocked(void)
{
struct sched_domain_attr *attr;
cpumask_var_t *doms;
int ndoms;
+ cpu_hotplug_mutex_held();
lockdep_assert_held(&cpuset_mutex);
- get_online_cpus();
/*
* We have raced with CPU hotplug. Don't do anything to avoid
@@ -825,27 +824,27 @@
* Anyways, hotplug work item will rebuild sched domains.
*/
if (!cpumask_equal(top_cpuset.effective_cpus, cpu_active_mask))
- goto out;
+ return;
/* Generate domain masks and attrs */
ndoms = generate_sched_domains(&doms, &attr);
/* Have scheduler rebuild the domains */
partition_sched_domains(ndoms, doms, attr);
-out:
- put_online_cpus();
}
#else /* !CONFIG_SMP */
-static void rebuild_sched_domains_locked(void)
+static void rebuild_sched_domains_unlocked(void)
{
}
#endif /* CONFIG_SMP */
void rebuild_sched_domains(void)
{
+ get_online_cpus();
mutex_lock(&cpuset_mutex);
- rebuild_sched_domains_locked();
+ rebuild_sched_domains_unlocked();
mutex_unlock(&cpuset_mutex);
+ put_online_cpus();
}
/**
@@ -877,7 +876,6 @@
*
* On legacy hierachy, effective_cpus will be the same with cpu_allowed.
*
- * Called with cpuset_mutex held
*/
static void update_cpumasks_hier(struct cpuset *cs, struct cpumask *new_cpus)
{
@@ -932,7 +930,7 @@
rcu_read_unlock();
if (need_rebuild_sched_domains)
- rebuild_sched_domains_locked();
+ rebuild_sched_domains_unlocked();
}
/**
@@ -1282,7 +1280,7 @@
cs->relax_domain_level = val;
if (!cpumask_empty(cs->cpus_allowed) &&
is_sched_load_balance(cs))
- rebuild_sched_domains_locked();
+ rebuild_sched_domains_unlocked();
}
return 0;
@@ -1313,7 +1311,6 @@
* cs: the cpuset to update
* turning_on: whether the flag is being set or cleared
*
- * Call with cpuset_mutex held.
*/
static int update_flag(cpuset_flagbits_t bit, struct cpuset *cs,
@@ -1348,7 +1345,7 @@
spin_unlock_irq(&callback_lock);
if (!cpumask_empty(trialcs->cpus_allowed) && balance_flag_changed)
- rebuild_sched_domains_locked();
+ rebuild_sched_domains_unlocked();
if (spread_flag_changed)
update_tasks_flags(cs);
@@ -1616,6 +1613,7 @@
cpuset_filetype_t type = cft->private;
int retval = 0;
+ get_online_cpus();
mutex_lock(&cpuset_mutex);
if (!is_cpuset_online(cs)) {
retval = -ENODEV;
@@ -1653,6 +1651,7 @@
}
out_unlock:
mutex_unlock(&cpuset_mutex);
+ put_online_cpus();
return retval;
}
@@ -1663,6 +1662,7 @@
cpuset_filetype_t type = cft->private;
int retval = -ENODEV;
+ get_online_cpus();
mutex_lock(&cpuset_mutex);
if (!is_cpuset_online(cs))
goto out_unlock;
@@ -1677,6 +1677,7 @@
}
out_unlock:
mutex_unlock(&cpuset_mutex);
+ put_online_cpus();
return retval;
}
@@ -1715,6 +1716,7 @@
kernfs_break_active_protection(of->kn);
flush_work(&cpuset_hotplug_work);
+ get_online_cpus();
mutex_lock(&cpuset_mutex);
if (!is_cpuset_online(cs))
goto out_unlock;
@@ -1740,6 +1742,7 @@
free_trial_cpuset(trialcs);
out_unlock:
mutex_unlock(&cpuset_mutex);
+ put_online_cpus();
kernfs_unbreak_active_protection(of->kn);
css_put(&cs->css);
flush_workqueue(cpuset_migrate_mm_wq);
@@ -2046,13 +2049,14 @@
/*
* If the cpuset being removed has its flag 'sched_load_balance'
* enabled, then simulate turning sched_load_balance off, which
- * will call rebuild_sched_domains_locked().
+ * will call rebuild_sched_domains_unlocked().
*/
static void cpuset_css_offline(struct cgroup_subsys_state *css)
{
struct cpuset *cs = css_cs(css);
+ get_online_cpus();
mutex_lock(&cpuset_mutex);
if (is_sched_load_balance(cs))
@@ -2062,6 +2066,7 @@
clear_bit(CS_ONLINE, &cs->flags);
mutex_unlock(&cpuset_mutex);
+ put_online_cpus();
}
static void cpuset_css_free(struct cgroup_subsys_state *css)
diff --git a/kernel/locking/osq_lock.c b/kernel/locking/osq_lock.c
index 05a3785..0befa20 100644
--- a/kernel/locking/osq_lock.c
+++ b/kernel/locking/osq_lock.c
@@ -1,6 +1,7 @@
#include <linux/percpu.h>
#include <linux/sched.h>
#include <linux/osq_lock.h>
+#include <linux/sched/rt.h>
/*
* An MCS like lock especially tailored for optimistic spinning for sleeping
@@ -85,6 +86,7 @@
{
struct optimistic_spin_node *node = this_cpu_ptr(&osq_node);
struct optimistic_spin_node *prev, *next;
+ struct task_struct *task = current;
int curr = encode_cpu(smp_processor_id());
int old;
@@ -104,6 +106,19 @@
prev = decode_cpu(old);
node->prev = prev;
+
+ /*
+ * osq_lock() unqueue
+ *
+ * node->prev = prev osq_wait_next()
+ * WMB MB
+ * prev->next = node next->prev = prev // unqueue-C
+ *
+ * Here 'node->prev' and 'next->prev' are the same variable and we need
+ * to ensure these stores happen in-order to avoid corrupting the list.
+ */
+ smp_wmb();
+
WRITE_ONCE(prev->next, node);
/*
@@ -118,8 +133,13 @@
while (!READ_ONCE(node->locked)) {
/*
* If we need to reschedule bail... so we can block.
+ * If a task spins on owner on a CPU after acquiring
+ * osq_lock while a RT task spins on another CPU to
+ * acquire osq_lock, it will starve the owner from
+ * completing if owner is to be scheduled on the same CPU.
+ * It will be a live lock.
*/
- if (need_resched())
+ if (need_resched() || rt_task(task))
goto unqueue;
cpu_relax_lowlatency();
diff --git a/kernel/locking/rwsem-xadd.c b/kernel/locking/rwsem-xadd.c
index 2337b4b..a4112df 100644
--- a/kernel/locking/rwsem-xadd.c
+++ b/kernel/locking/rwsem-xadd.c
@@ -574,6 +574,33 @@
WAKE_Q(wake_q);
/*
+ * __rwsem_down_write_failed_common(sem)
+ * rwsem_optimistic_spin(sem)
+ * osq_unlock(sem->osq)
+ * ...
+ * atomic_long_add_return(&sem->count)
+ *
+ * - VS -
+ *
+ * __up_write()
+ * if (atomic_long_sub_return_release(&sem->count) < 0)
+ * rwsem_wake(sem)
+ * osq_is_locked(&sem->osq)
+ *
+ * And __up_write() must observe !osq_is_locked() when it observes the
+ * atomic_long_add_return() in order to not miss a wakeup.
+ *
+ * This boils down to:
+ *
+ * [S.rel] X = 1 [RmW] r0 = (Y += 0)
+ * MB RMB
+ * [RmW] Y += 1 [L] r1 = X
+ *
+ * exists (r0=1 /\ r1=0)
+ */
+ smp_rmb();
+
+ /*
* If a spinner is present, it is not necessary to do the wakeup.
* Try to do wakeup only if the trylock succeeds to minimize
* spinlock contention which may introduce too much delay in the
diff --git a/kernel/sched/core.c b/kernel/sched/core.c
index d43ad69..58c4341 100644
--- a/kernel/sched/core.c
+++ b/kernel/sched/core.c
@@ -525,8 +525,7 @@
struct rq *rq = cpu_rq(cpu);
unsigned long flags;
- if (!raw_spin_trylock_irqsave(&rq->lock, flags))
- return;
+ raw_spin_lock_irqsave(&rq->lock, flags);
resched_curr(rq);
raw_spin_unlock_irqrestore(&rq->lock, flags);
}
@@ -1219,7 +1218,6 @@
if (cpumask_test_cpu(task_cpu(p), &allowed_mask))
goto out;
- dest_cpu = cpumask_any_and(cpu_valid_mask, new_mask);
if (task_running(rq, p) || p->state == TASK_WAKING) {
struct migration_arg arg = { p, dest_cpu };
/* Need help from migration thread: drop lock and wait. */
diff --git a/kernel/sched/core_ctl.c b/kernel/sched/core_ctl.c
index 772d97b..f95243b 100644
--- a/kernel/sched/core_ctl.c
+++ b/kernel/sched/core_ctl.c
@@ -22,6 +22,8 @@
#include <linux/syscore_ops.h>
#include <trace/events/sched.h>
+#include "sched.h"
+#include "walt.h"
#define MAX_CPUS_PER_CLUSTER 6
#define MAX_CLUSTERS 2
@@ -472,16 +474,19 @@
int max_nr, big_max_nr;
struct cluster_data *cluster;
unsigned int index = 0;
+ unsigned long flags;
sched_get_nr_running_avg(&avg, &iowait_avg, &big_avg,
&max_nr, &big_max_nr);
+ spin_lock_irqsave(&state_lock, flags);
for_each_cluster(cluster, index) {
if (!cluster->inited)
continue;
cluster->nrrun = cluster->is_big_cluster ? big_avg : avg;
cluster->max_nr = cluster->is_big_cluster ? big_max_nr : max_nr;
}
+ spin_unlock_irqrestore(&state_lock, flags);
}
#define MAX_NR_THRESHOLD 4
@@ -555,10 +560,16 @@
cluster->active_cpus = get_active_cpu_count(cluster);
thres_idx = cluster->active_cpus ? cluster->active_cpus - 1 : 0;
list_for_each_entry(c, &cluster->lru, sib) {
- if (c->busy >= cluster->busy_up_thres[thres_idx])
+ bool old_is_busy = c->is_busy;
+
+ if (c->busy >= cluster->busy_up_thres[thres_idx] ||
+ sched_cpu_high_irqload(c->cpu))
c->is_busy = true;
else if (c->busy < cluster->busy_down_thres[thres_idx])
c->is_busy = false;
+
+ trace_core_ctl_set_busy(c->cpu, c->busy, old_is_busy,
+ c->is_busy);
need_cpus += c->is_busy;
}
need_cpus = apply_task_need(cluster, need_cpus);
@@ -599,17 +610,6 @@
wake_up_core_ctl_thread(cluster);
}
-static void core_ctl_set_busy(struct cpu_data *c, unsigned int busy)
-{
- unsigned int old_is_busy = c->is_busy;
-
- if (c->busy == busy)
- return;
-
- c->busy = busy;
- trace_core_ctl_set_busy(c->cpu, busy, old_is_busy, c->is_busy);
-}
-
/* ========================= core count enforcement ==================== */
static void wake_up_core_ctl_thread(struct cluster_data *cluster)
@@ -637,26 +637,27 @@
spin_lock_irqsave(&state_lock, flags);
for_each_cluster(cluster, index) {
- if (cluster->is_big_cluster) {
- if (boost) {
- boost_state_changed = !cluster->boost;
- ++cluster->boost;
+ if (boost) {
+ boost_state_changed = !cluster->boost;
+ ++cluster->boost;
+ } else {
+ if (!cluster->boost) {
+ pr_err("Error turning off boost. Boost already turned off\n");
+ ret = -EINVAL;
+ break;
} else {
- if (!cluster->boost) {
- pr_err("Error turning off boost. Boost already turned off\n");
- ret = -EINVAL;
- } else {
- --cluster->boost;
- boost_state_changed = !cluster->boost;
- }
+ --cluster->boost;
+ boost_state_changed = !cluster->boost;
}
- break;
}
}
spin_unlock_irqrestore(&state_lock, flags);
- if (boost_state_changed)
- apply_need(cluster);
+ if (boost_state_changed) {
+ index = 0;
+ for_each_cluster(cluster, index)
+ apply_need(cluster);
+ }
trace_core_ctl_set_boost(cluster->boost, ret);
@@ -667,10 +668,10 @@
void core_ctl_check(u64 window_start)
{
int cpu;
- unsigned int busy;
struct cpu_data *c;
struct cluster_data *cluster;
unsigned int index = 0;
+ unsigned long flags;
if (unlikely(!initialized))
return;
@@ -680,6 +681,7 @@
core_ctl_check_timestamp = window_start;
+ spin_lock_irqsave(&state_lock, flags);
for_each_possible_cpu(cpu) {
c = &per_cpu(cpu_state, cpu);
@@ -688,9 +690,9 @@
if (!cluster || !cluster->inited)
continue;
- busy = sched_get_cpu_util(cpu);
- core_ctl_set_busy(c, busy);
+ c->busy = sched_get_cpu_util(cpu);
}
+ spin_unlock_irqrestore(&state_lock, flags);
update_running_avg();
diff --git a/kernel/sched/cpufreq_schedutil.c b/kernel/sched/cpufreq_schedutil.c
index 2eb966c..3192612 100644
--- a/kernel/sched/cpufreq_schedutil.c
+++ b/kernel/sched/cpufreq_schedutil.c
@@ -86,6 +86,7 @@
static DEFINE_PER_CPU(struct sugov_cpu, sugov_cpu);
static unsigned int stale_ns;
+static DEFINE_PER_CPU(struct sugov_tunables *, cached_tunables);
/************************ Governor internals ***********************/
@@ -162,6 +163,7 @@
policy->cpuinfo.max_freq : policy->cur;
freq = (freq + (freq >> 2)) * util / max;
+ trace_sugov_next_freq(policy->cpu, util, max, freq);
if (freq == sg_policy->cached_raw_freq && sg_policy->next_freq != UINT_MAX)
return sg_policy->next_freq;
@@ -313,10 +315,15 @@
struct sugov_cpu *sg_cpu = container_of(hook, struct sugov_cpu, update_util);
struct sugov_policy *sg_policy = sg_cpu->sg_policy;
struct cpufreq_policy *policy = sg_policy->policy;
- unsigned long util, max;
+ unsigned long util, max, hs_util;
unsigned int next_f;
bool busy;
+ flags &= ~SCHED_CPUFREQ_RT_DL;
+
+ if (!sg_policy->tunables->pl && flags & SCHED_CPUFREQ_PL)
+ return;
+
sugov_set_iowait_boost(sg_cpu, time, flags);
sg_cpu->last_update = time;
@@ -324,15 +331,30 @@
return;
busy = sugov_cpu_is_busy(sg_cpu);
- flags &= ~SCHED_CPUFREQ_RT_DL;
+ raw_spin_lock(&sg_policy->update_lock);
if (flags & SCHED_CPUFREQ_RT_DL) {
next_f = policy->cpuinfo.max_freq;
} else {
sugov_get_util(&util, &max, sg_cpu->cpu);
- sugov_iowait_boost(sg_cpu, &util, &max);
+ if (sg_policy->max != max) {
+ sg_policy->max = max;
+ hs_util = freq_to_util(sg_policy,
+ sg_policy->tunables->hispeed_freq);
+ hs_util = mult_frac(hs_util, TARGET_LOAD, 100);
+ sg_policy->hispeed_util = hs_util;
+ }
+
+ sg_cpu->util = util;
+ sg_cpu->max = max;
+ sg_cpu->flags = flags;
sugov_calc_avg_cap(sg_policy, sg_cpu->walt_load.ws,
sg_policy->policy->cur);
+ trace_sugov_util_update(sg_cpu->cpu, sg_cpu->util,
+ sg_policy->avg_cap,
+ max, sg_cpu->walt_load.nl,
+ sg_cpu->walt_load.pl, flags);
+ sugov_iowait_boost(sg_cpu, &util, &max);
sugov_walt_adjust(sg_cpu, &util, &max);
next_f = get_next_freq(sg_policy, util, max);
/*
@@ -343,6 +365,7 @@
next_f = sg_policy->next_freq;
}
sugov_update_commit(sg_policy, time, next_f);
+ raw_spin_unlock(&sg_policy->update_lock);
}
static unsigned int sugov_next_freq_shared(struct sugov_cpu *sg_cpu)
@@ -375,7 +398,7 @@
j_util = j_sg_cpu->util;
j_max = j_sg_cpu->max;
- if (j_util * max > j_max * util) {
+ if (j_util * max >= j_max * util) {
util = j_util;
max = j_max;
}
@@ -395,6 +418,9 @@
unsigned long util, max, hs_util;
unsigned int next_f;
+ if (!sg_policy->tunables->pl && flags & SCHED_CPUFREQ_PL)
+ return;
+
sugov_get_util(&util, &max, sg_cpu->cpu);
flags &= ~SCHED_CPUFREQ_RT_DL;
@@ -513,7 +539,7 @@
{
struct sugov_tunables *tunables = to_sugov_tunables(attr_set);
- return sprintf(buf, "%u\n", tunables->hispeed_load);
+ return scnprintf(buf, PAGE_SIZE, "%u\n", tunables->hispeed_load);
}
static ssize_t hispeed_load_store(struct gov_attr_set *attr_set,
@@ -533,7 +559,7 @@
{
struct sugov_tunables *tunables = to_sugov_tunables(attr_set);
- return sprintf(buf, "%u\n", tunables->hispeed_freq);
+ return scnprintf(buf, PAGE_SIZE, "%u\n", tunables->hispeed_freq);
}
static ssize_t hispeed_freq_store(struct gov_attr_set *attr_set,
@@ -565,7 +591,7 @@
{
struct sugov_tunables *tunables = to_sugov_tunables(attr_set);
- return sprintf(buf, "%u\n", tunables->pl);
+ return scnprintf(buf, PAGE_SIZE, "%u\n", tunables->pl);
}
static ssize_t pl_store(struct gov_attr_set *attr_set, const char *buf,
@@ -681,6 +707,31 @@
return tunables;
}
+static void sugov_tunables_save(struct cpufreq_policy *policy,
+ struct sugov_tunables *tunables)
+{
+ int cpu;
+ struct sugov_tunables *cached = per_cpu(cached_tunables, policy->cpu);
+
+ if (!have_governor_per_policy())
+ return;
+
+ if (!cached) {
+ cached = kzalloc(sizeof(*tunables), GFP_KERNEL);
+ if (!cached) {
+ pr_warn("Couldn't allocate tunables for caching\n");
+ return;
+ }
+ for_each_cpu(cpu, policy->related_cpus)
+ per_cpu(cached_tunables, cpu) = cached;
+ }
+
+ cached->pl = tunables->pl;
+ cached->hispeed_load = tunables->hispeed_load;
+ cached->hispeed_freq = tunables->hispeed_freq;
+ cached->rate_limit_us = tunables->rate_limit_us;
+}
+
static void sugov_tunables_free(struct sugov_tunables *tunables)
{
if (!have_governor_per_policy())
@@ -689,6 +740,23 @@
kfree(tunables);
}
+static void sugov_tunables_restore(struct cpufreq_policy *policy)
+{
+ struct sugov_policy *sg_policy = policy->governor_data;
+ struct sugov_tunables *tunables = sg_policy->tunables;
+ struct sugov_tunables *cached = per_cpu(cached_tunables, policy->cpu);
+
+ if (!cached)
+ return;
+
+ tunables->pl = cached->pl;
+ tunables->hispeed_load = cached->hispeed_load;
+ tunables->hispeed_freq = cached->hispeed_freq;
+ tunables->rate_limit_us = cached->rate_limit_us;
+ sg_policy->freq_update_delay_ns =
+ tunables->rate_limit_us * NSEC_PER_USEC;
+}
+
static int sugov_init(struct cpufreq_policy *policy)
{
struct sugov_policy *sg_policy;
@@ -743,6 +811,8 @@
sg_policy->tunables = tunables;
stale_ns = sched_ravg_window + (sched_ravg_window >> 3);
+ sugov_tunables_restore(policy);
+
ret = kobject_init_and_add(&tunables->attr_set.kobj, &sugov_tunables_ktype,
get_governor_parent_kobj(policy), "%s",
schedutil_gov.name);
@@ -782,8 +852,10 @@
count = gov_attr_set_put(&tunables->attr_set, &sg_policy->tunables_hook);
policy->governor_data = NULL;
- if (!count)
+ if (!count) {
+ sugov_tunables_save(policy, tunables);
sugov_tunables_free(tunables);
+ }
mutex_unlock(&global_tunables_lock);
diff --git a/kernel/sched/cputime.c b/kernel/sched/cputime.c
index 0d8cc06..e4f48f1 100644
--- a/kernel/sched/cputime.c
+++ b/kernel/sched/cputime.c
@@ -71,12 +71,12 @@
else
account = false;
+ u64_stats_update_end(&irqtime->sync);
+
if (account)
sched_account_irqtime(cpu, curr, delta, wallclock);
else if (curr != this_cpu_ksoftirqd())
sched_account_irqstart(cpu, curr, wallclock);
-
- u64_stats_update_end(&irqtime->sync);
}
EXPORT_SYMBOL_GPL(irqtime_account_irq);
diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c
index 7b459ca..2d92092 100755
--- a/kernel/sched/fair.c
+++ b/kernel/sched/fair.c
@@ -5563,6 +5563,13 @@
return DIV_ROUND_UP(util << SCHED_CAPACITY_SHIFT, capacity);
}
+static inline bool bias_to_waker_cpu_enabled(struct task_struct *wakee,
+ struct task_struct *waker)
+{
+ return task_util(waker) > sched_big_waker_task_load &&
+ task_util(wakee) < sched_small_wakee_task_load;
+}
+
static inline bool
bias_to_waker_cpu(struct task_struct *p, int cpu, struct cpumask *rtg_target)
{
@@ -6917,6 +6924,7 @@
struct related_thread_group *grp;
cpumask_t search_cpus;
int prev_cpu = task_cpu(p);
+ struct task_struct *curr = cpu_rq(cpu)->curr;
#ifdef CONFIG_SCHED_CORE_ROTATE
bool do_rotate = false;
bool avoid_prev_cpu = false;
@@ -6943,7 +6951,8 @@
if (grp && grp->preferred_cluster)
rtg_target = &grp->preferred_cluster->cpus;
- if (sync && bias_to_waker_cpu(p, cpu, rtg_target)) {
+ if (sync && bias_to_waker_cpu_enabled(p, curr) &&
+ bias_to_waker_cpu(p, cpu, rtg_target)) {
trace_sched_task_util_bias_to_waker(p, prev_cpu,
task_util(p), cpu, cpu, 0, need_idle);
return cpu;
diff --git a/kernel/sched/sched.h b/kernel/sched/sched.h
index 7ac731d..b5a271b 100644
--- a/kernel/sched/sched.h
+++ b/kernel/sched/sched.h
@@ -2349,7 +2349,6 @@
};
extern struct list_head cluster_head;
-extern int num_clusters;
extern struct sched_cluster *sched_cluster[NR_CPUS];
#define for_each_sched_cluster(cluster) \
diff --git a/kernel/sched/sched_avg.c b/kernel/sched/sched_avg.c
index 166c643..46480a7 100644
--- a/kernel/sched/sched_avg.c
+++ b/kernel/sched/sched_avg.c
@@ -123,7 +123,7 @@
EXPORT_SYMBOL(sched_get_nr_running_avg);
#define BUSY_NR_RUN 3
-#define BUSY_LOAD_FACTOR 2
+#define BUSY_LOAD_FACTOR 10
static inline void update_last_busy_time(int cpu, bool dequeue,
unsigned long prev_nr_run, u64 curr_time)
{
diff --git a/kernel/sched/walt.c b/kernel/sched/walt.c
index 316c276..1f5639c 100644
--- a/kernel/sched/walt.c
+++ b/kernel/sched/walt.c
@@ -162,6 +162,13 @@
*/
__read_mostly unsigned int sysctl_sched_freq_reporting_policy;
+
+#define SCHED_BIG_WAKER_TASK_LOAD_PCT 25UL
+#define SCHED_SMALL_WAKEE_TASK_LOAD_PCT 10UL
+
+__read_mostly unsigned int sched_big_waker_task_load;
+__read_mostly unsigned int sched_small_wakee_task_load;
+
static int __init set_sched_ravg_window(char *str)
{
unsigned int window_size;
@@ -2833,14 +2840,11 @@
rcu_read_unlock();
}
- if (cpu_max_table_freq[cpu] &&
- unlikely(thermal_max_freq && thermal_max_freq
- != cpu_max_table_freq[cpu])) {
+ if (cpu_max_table_freq[cpu])
return div64_ul(thermal_max_freq * max_cap[cpu],
cpu_max_table_freq[cpu]);
- } else {
+ else
return rq->cpu_capacity_orig;
- }
}
static DEFINE_SPINLOCK(cpu_freq_min_max_lock);
@@ -3116,4 +3120,9 @@
walt_cpu_util_freq_divisor =
(sched_ravg_window >> SCHED_CAPACITY_SHIFT) * 100;
+
+ sched_big_waker_task_load =
+ (SCHED_BIG_WAKER_TASK_LOAD_PCT << SCHED_CAPACITY_SHIFT) / 100;
+ sched_small_wakee_task_load =
+ (SCHED_SMALL_WAKEE_TASK_LOAD_PCT << SCHED_CAPACITY_SHIFT) / 100;
}
diff --git a/net/bridge/br_if.c b/net/bridge/br_if.c
index ed0dd33..9218931 100644
--- a/net/bridge/br_if.c
+++ b/net/bridge/br_if.c
@@ -655,3 +655,34 @@
if (mask & BR_AUTO_MASK)
nbp_update_port_count(br);
}
+
+/* br_port_dev_get()
+ * Using the given addr, identify the port to which it is reachable,
+ * returing a reference to the net device associated with that port.
+ *
+ * NOTE: Return NULL if given dev is not a bridge or
+ * the mac has no associated port
+ */
+struct net_device *br_port_dev_get(struct net_device *dev, unsigned char *addr)
+{
+ struct net_bridge_fdb_entry *fdbe;
+ struct net_bridge *br;
+ struct net_device *netdev = NULL;
+
+ /* Is this a bridge? */
+ if (!(dev->priv_flags & IFF_EBRIDGE))
+ return NULL;
+
+ br = netdev_priv(dev);
+
+ /* Lookup the fdb entry and get reference to the port dev */
+ rcu_read_lock();
+ fdbe = __br_fdb_get(br, addr, 0);
+ if (fdbe && fdbe->dst) {
+ netdev = fdbe->dst->dev; /* port device */
+ dev_hold(netdev);
+ }
+ rcu_read_unlock();
+ return netdev;
+}
+EXPORT_SYMBOL(br_port_dev_get);
diff --git a/net/core/neighbour.c b/net/core/neighbour.c
index 227c249..62893eb 100644
--- a/net/core/neighbour.c
+++ b/net/core/neighbour.c
@@ -56,6 +56,7 @@
static void neigh_update_notify(struct neighbour *neigh);
static int pneigh_ifdown(struct neigh_table *tbl, struct net_device *dev);
+static unsigned int neigh_probe_enable;
#ifdef CONFIG_PROC_FS
static const struct file_operations neigh_stat_seq_fops;
#endif
@@ -1258,9 +1259,20 @@
{
struct neighbour *neigh = __neigh_lookup(tbl, saddr, dev,
lladdr || !dev->addr_len);
- if (neigh)
- neigh_update(neigh, lladdr, NUD_STALE,
- NEIGH_UPDATE_F_OVERRIDE);
+ if (neigh) {
+ if (neigh_probe_enable) {
+ if (!(neigh->nud_state == NUD_REACHABLE)) {
+ neigh_update(neigh, lladdr, NUD_STALE,
+ NEIGH_UPDATE_F_OVERRIDE);
+ write_lock(&neigh->lock);
+ neigh_probe(neigh);
+ neigh_update_notify(neigh);
+ }
+ } else {
+ neigh_update(neigh, lladdr, NUD_STALE,
+ NEIGH_UPDATE_F_OVERRIDE);
+ }
+ }
return neigh;
}
EXPORT_SYMBOL(neigh_event_ns);
@@ -3107,6 +3119,12 @@
.extra2 = &int_max,
.proc_handler = proc_dointvec_minmax,
},
+ [NEIGH_VAR_PROBE] = {
+ .procname = "neigh_probe",
+ .maxlen = sizeof(int),
+ .mode = 0644,
+ .proc_handler = proc_dointvec,
+ },
{},
},
};
@@ -3142,6 +3160,7 @@
t->neigh_vars[NEIGH_VAR_GC_THRESH1].data = &tbl->gc_thresh1;
t->neigh_vars[NEIGH_VAR_GC_THRESH2].data = &tbl->gc_thresh2;
t->neigh_vars[NEIGH_VAR_GC_THRESH3].data = &tbl->gc_thresh3;
+ t->neigh_vars[NEIGH_VAR_PROBE].data = &neigh_probe_enable;
}
if (handler) {
diff --git a/net/ipc_router/ipc_router_core.c b/net/ipc_router/ipc_router_core.c
index a28b1af..d38157d 100644
--- a/net/ipc_router/ipc_router_core.c
+++ b/net/ipc_router/ipc_router_core.c
@@ -2925,6 +2925,10 @@
}
temp_skb = skb_peek_tail(pkt->pkt_fragment_q);
+ if (!temp_skb) {
+ IPC_RTR_ERR("%s: Empty skb\n", __func__);
+ return -EINVAL;
+ }
align_size = ALIGN_SIZE(pkt->length);
skb_put(temp_skb, align_size);
pkt->length += align_size;
@@ -3089,6 +3093,11 @@
}
temp_skb = skb_peek_tail(pkt->pkt_fragment_q);
+ if (!temp_skb) {
+ IPC_RTR_ERR("%s: Abort invalid pkt\n", __func__);
+ ret = -EINVAL;
+ goto out_write_pkt;
+ }
align_size = ALIGN_SIZE(pkt->length);
skb_put(temp_skb, align_size);
pkt->length += align_size;
@@ -3408,7 +3417,8 @@
align_size = ALIGN_SIZE(data_len);
if (align_size) {
temp_skb = skb_peek_tail((*pkt)->pkt_fragment_q);
- skb_trim(temp_skb, (temp_skb->len - align_size));
+ if (temp_skb)
+ skb_trim(temp_skb, (temp_skb->len - align_size));
}
return data_len;
}
diff --git a/net/ipc_router/ipc_router_socket.c b/net/ipc_router/ipc_router_socket.c
index 02242a1..a758a09 100644
--- a/net/ipc_router/ipc_router_socket.c
+++ b/net/ipc_router/ipc_router_socket.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2011-2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2011-2017, 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
@@ -141,6 +141,10 @@
hdr = &pkt->hdr;
if (addr && (hdr->type == IPC_ROUTER_CTRL_CMD_RESUME_TX)) {
temp = skb_peek(pkt->pkt_fragment_q);
+ if (!temp || !temp->data) {
+ IPC_RTR_ERR("%s: Invalid skb\n", __func__);
+ return -EINVAL;
+ }
ctl_msg = (union rr_control_msg *)(temp->data);
addr->family = AF_MSM_IPC;
addr->address.addrtype = MSM_IPC_ADDR_ID;
diff --git a/net/ipv4/udp.c b/net/ipv4/udp.c
index 7c1f5f6..200c9b6 100644
--- a/net/ipv4/udp.c
+++ b/net/ipv4/udp.c
@@ -1627,14 +1627,18 @@
/* For TCP sockets, sk_rx_dst is protected by socket lock
* For UDP, we use xchg() to guard against concurrent changes.
*/
-static void udp_sk_rx_dst_set(struct sock *sk, struct dst_entry *dst)
+bool udp_sk_rx_dst_set(struct sock *sk, struct dst_entry *dst)
{
struct dst_entry *old;
- dst_hold(dst);
- old = xchg(&sk->sk_rx_dst, dst);
- dst_release(old);
+ if (dst_hold_safe(dst)) {
+ old = xchg(&sk->sk_rx_dst, dst);
+ dst_release(old);
+ return old != dst;
+ }
+ return false;
}
+EXPORT_SYMBOL(udp_sk_rx_dst_set);
/*
* Multicasts and broadcasts go to each listener.
@@ -1957,13 +1961,11 @@
if (dst)
dst = dst_check(dst, 0);
if (dst) {
- /* DST_NOCACHE can not be used without taking a reference */
- if (dst->flags & DST_NOCACHE) {
- if (likely(atomic_inc_not_zero(&dst->__refcnt)))
- skb_dst_set(skb, dst);
- } else {
- skb_dst_set_noref(skb, dst);
- }
+ /* set noref for now.
+ * any place which wants to hold dst has to call
+ * dst_hold_safe()
+ */
+ skb_dst_set_noref(skb, dst);
}
}
diff --git a/net/ipv6/datagram.c b/net/ipv6/datagram.c
index edf15f0..1778af9 100644
--- a/net/ipv6/datagram.c
+++ b/net/ipv6/datagram.c
@@ -250,8 +250,14 @@
*/
err = ip6_datagram_dst_update(sk, true);
- if (err)
+ if (err) {
+ /* Reset daddr and dport so that udp_v6_early_demux()
+ * fails to find this socket
+ */
+ memset(&sk->sk_v6_daddr, 0, sizeof(sk->sk_v6_daddr));
+ inet->inet_dport = 0;
goto out;
+ }
sk->sk_state = TCP_ESTABLISHED;
sk_set_txhash(sk);
diff --git a/net/ipv6/netfilter/Kconfig b/net/ipv6/netfilter/Kconfig
index e10a04c..cf336d6 100644
--- a/net/ipv6/netfilter/Kconfig
+++ b/net/ipv6/netfilter/Kconfig
@@ -135,6 +135,18 @@
if IP6_NF_IPTABLES
+config IP6_NF_IPTABLES_128
+ tristate "128 bit arithmetic for iptables matching"
+ depends on IP6_NF_IPTABLES
+ help
+ This enables 128 bit matching in ip6tables to help optimize cases
+ where there is no match required. ip6tables matching for ipv6 always
+ has a mask if an address is specified for match. Adding a check for
+ mask prior to that helps to improve performance as it avoids the
+ masked comparison.
+
+ Note that this feature depends on the architecture. If unsure, say N.
+
# The simple matches.
config IP6_NF_MATCH_AH
tristate '"ah" match support'
diff --git a/net/ipv6/netfilter/ip6_tables.c b/net/ipv6/netfilter/ip6_tables.c
index 55aacea..1cee193 100644
--- a/net/ipv6/netfilter/ip6_tables.c
+++ b/net/ipv6/netfilter/ip6_tables.c
@@ -72,14 +72,23 @@
{
unsigned long ret;
const struct ipv6hdr *ipv6 = ipv6_hdr(skb);
+#if IS_ENABLED(IP6_NF_IPTABLES_128)
+ const __uint128_t *ulm1 = (const __uint128_t *)&ip6info->smsk;
+ const __uint128_t *ulm2 = (const __uint128_t *)&ip6info->dmsk;
+#endif
- if (NF_INVF(ip6info, IP6T_INV_SRCIP,
- ipv6_masked_addr_cmp(&ipv6->saddr, &ip6info->smsk,
- &ip6info->src)) ||
- NF_INVF(ip6info, IP6T_INV_DSTIP,
- ipv6_masked_addr_cmp(&ipv6->daddr, &ip6info->dmsk,
- &ip6info->dst)))
- return false;
+#if IS_ENABLED(IP6_NF_IPTABLES_128)
+ if (*ulm1 || *ulm2)
+#endif
+ {
+ if (NF_INVF(ip6info, IP6T_INV_SRCIP,
+ ipv6_masked_addr_cmp(&ipv6->saddr, &ip6info->smsk,
+ &ip6info->src)) ||
+ NF_INVF(ip6info, IP6T_INV_DSTIP,
+ ipv6_masked_addr_cmp(&ipv6->daddr, &ip6info->dmsk,
+ &ip6info->dst)))
+ return false;
+ }
ret = ifname_compare_aligned(indev, ip6info->iniface, ip6info->iniface_mask);
diff --git a/net/ipv6/udp.c b/net/ipv6/udp.c
index c43ef0c..2db6808 100644
--- a/net/ipv6/udp.c
+++ b/net/ipv6/udp.c
@@ -46,6 +46,7 @@
#include <net/tcp_states.h>
#include <net/ip6_checksum.h>
#include <net/xfrm.h>
+#include <net/inet_hashtables.h>
#include <net/inet6_hashtables.h>
#include <net/busy_poll.h>
#include <net/sock_reuseport.h>
@@ -277,11 +278,7 @@
struct udp_table *udptable)
{
const struct ipv6hdr *iph = ipv6_hdr(skb);
- struct sock *sk;
- sk = skb_steal_sock(skb);
- if (unlikely(sk))
- return sk;
return __udp6_lib_lookup(dev_net(skb->dev), &iph->saddr, sport,
&iph->daddr, dport, inet6_iif(skb),
udptable, skb);
@@ -757,6 +754,15 @@
return 0;
}
+static void udp6_sk_rx_dst_set(struct sock *sk, struct dst_entry *dst)
+{
+ if (udp_sk_rx_dst_set(sk, dst)) {
+ const struct rt6_info *rt = (const struct rt6_info *)dst;
+
+ inet6_sk(sk)->rx_dst_cookie = rt6_get_cookie(rt);
+ }
+}
+
int __udp6_lib_rcv(struct sk_buff *skb, struct udp_table *udptable,
int proto)
{
@@ -799,6 +805,24 @@
if (udp6_csum_init(skb, uh, proto))
goto csum_error;
+ /* Check if the socket is already available, e.g. due to early demux */
+ sk = skb_steal_sock(skb);
+ if (sk) {
+ struct dst_entry *dst = skb_dst(skb);
+ int ret;
+
+ if (unlikely(sk->sk_rx_dst != dst))
+ udp6_sk_rx_dst_set(sk, dst);
+
+ ret = udpv6_queue_rcv_skb(sk, skb);
+ sock_put(sk);
+
+ /* a return value > 0 means to resubmit the input */
+ if (ret > 0)
+ return ret;
+ return 0;
+ }
+
/*
* Multicast receive code
*/
@@ -807,11 +831,6 @@
saddr, daddr, udptable, proto);
/* Unicast */
-
- /*
- * check socket cache ... must talk to Alan about his plans
- * for sock caches... i'll skip this for now.
- */
sk = __udp6_lib_lookup_skb(skb, uh->source, uh->dest, udptable);
if (sk) {
int ret;
@@ -866,6 +885,71 @@
return 0;
}
+static struct sock *__udp6_lib_demux_lookup(struct net *net,
+ __be16 loc_port,
+ const struct in6_addr *loc_addr,
+ __be16 rmt_port,
+ const struct in6_addr *rmt_addr,
+ int dif)
+{
+ unsigned short hnum = ntohs(loc_port);
+ unsigned int hash2 = udp6_portaddr_hash(net, loc_addr, hnum);
+ unsigned int slot2 = hash2 & udp_table.mask;
+ struct udp_hslot *hslot2 = &udp_table.hash2[slot2];
+
+ const __portpair ports = INET_COMBINED_PORTS(rmt_port, hnum);
+ struct sock *sk;
+
+ udp_portaddr_for_each_entry_rcu(sk, &hslot2->head) {
+ if (sk->sk_state == TCP_ESTABLISHED &&
+ INET6_MATCH(sk, net, rmt_addr, loc_addr, ports, dif))
+ return sk;
+ /* Only check first socket in chain */
+ break;
+ }
+ return NULL;
+}
+
+static void udp_v6_early_demux(struct sk_buff *skb)
+{
+ struct net *net = dev_net(skb->dev);
+ const struct udphdr *uh;
+ struct sock *sk;
+ struct dst_entry *dst;
+ int dif = skb->dev->ifindex;
+
+ if (!pskb_may_pull(skb, skb_transport_offset(skb) +
+ sizeof(struct udphdr)))
+ return;
+
+ uh = udp_hdr(skb);
+
+ if (skb->pkt_type == PACKET_HOST)
+ sk = __udp6_lib_demux_lookup(net, uh->dest,
+ &ipv6_hdr(skb)->daddr,
+ uh->source, &ipv6_hdr(skb)->saddr,
+ dif);
+ else
+ return;
+
+ if (!sk || !atomic_inc_not_zero_hint(&sk->sk_refcnt, 2))
+ return;
+
+ skb->sk = sk;
+ skb->destructor = sock_efree;
+ dst = READ_ONCE(sk->sk_rx_dst);
+
+ if (dst)
+ dst = dst_check(dst, inet6_sk(sk)->rx_dst_cookie);
+ if (dst) {
+ /* set noref for now.
+ * any place which wants to hold dst has to call
+ * dst_hold_safe()
+ */
+ skb_dst_set_noref(skb, dst);
+ }
+}
+
static __inline__ int udpv6_rcv(struct sk_buff *skb)
{
return __udp6_lib_rcv(skb, &udp_table, IPPROTO_UDP);
@@ -1381,6 +1465,7 @@
#endif
static const struct inet6_protocol udpv6_protocol = {
+ .early_demux = udp_v6_early_demux,
.handler = udpv6_rcv,
.err_handler = udpv6_err,
.flags = INET6_PROTO_NOPOLICY|INET6_PROTO_FINAL,
diff --git a/net/netfilter/nf_conntrack_core.c b/net/netfilter/nf_conntrack_core.c
index ff2d32e..d5b49fc 100644
--- a/net/netfilter/nf_conntrack_core.c
+++ b/net/netfilter/nf_conntrack_core.c
@@ -181,6 +181,9 @@
unsigned int nf_conntrack_max __read_mostly;
seqcount_t nf_conntrack_generation __read_mostly;
+unsigned int nf_conntrack_pkt_threshold __read_mostly;
+EXPORT_SYMBOL(nf_conntrack_pkt_threshold);
+
DEFINE_PER_CPU(struct nf_conn, nf_conntrack_untracked);
EXPORT_PER_CPU_SYMBOL(nf_conntrack_untracked);
@@ -1434,6 +1437,9 @@
unsigned long extra_jiffies,
int do_acct)
{
+ struct nf_conn_acct *acct;
+ u64 pkts;
+
NF_CT_ASSERT(skb);
/* Only update if this is not a fixed timeout */
@@ -1446,8 +1452,27 @@
ct->timeout = extra_jiffies;
acct:
- if (do_acct)
- nf_ct_acct_update(ct, ctinfo, skb->len);
+ if (do_acct) {
+ acct = nf_conn_acct_find(ct);
+ if (acct) {
+ struct nf_conn_counter *counter = acct->counter;
+
+ atomic64_inc(&counter[CTINFO2DIR(ctinfo)].packets);
+ atomic64_add(skb->len, &counter
+ [CTINFO2DIR(ctinfo)].bytes);
+
+ pkts =
+ atomic64_read(&counter[CTINFO2DIR(ctinfo)].packets) +
+ atomic64_read(&counter[!CTINFO2DIR(ctinfo)].packets);
+ /* Report if the packet threshold is reached. */
+ if ((nf_conntrack_pkt_threshold > 0) &&
+ (pkts == nf_conntrack_pkt_threshold)) {
+ nf_conntrack_event_cache(IPCT_COUNTER, ct);
+ nf_conntrack_event_cache(IPCT_PROTOINFO, ct);
+ nf_ct_deliver_cached_events(ct);
+ }
+ }
+ }
}
EXPORT_SYMBOL_GPL(__nf_ct_refresh_acct);
diff --git a/net/netfilter/nf_conntrack_netlink.c b/net/netfilter/nf_conntrack_netlink.c
index 04111c1..08b24a9 100644
--- a/net/netfilter/nf_conntrack_netlink.c
+++ b/net/netfilter/nf_conntrack_netlink.c
@@ -729,6 +729,10 @@
if (events & (1 << IPCT_SEQADJ) &&
ctnetlink_dump_ct_seq_adj(skb, ct) < 0)
goto nla_put_failure;
+
+ if (events & (1 << IPCT_COUNTER) &&
+ ctnetlink_dump_acct(skb, ct, 0) < 0)
+ goto nla_put_failure;
}
#ifdef CONFIG_NF_CONNTRACK_MARK
diff --git a/net/netfilter/nf_conntrack_standalone.c b/net/netfilter/nf_conntrack_standalone.c
index 5f446cd..2f1014e 100644
--- a/net/netfilter/nf_conntrack_standalone.c
+++ b/net/netfilter/nf_conntrack_standalone.c
@@ -517,6 +517,14 @@
.mode = 0644,
.proc_handler = proc_dointvec,
},
+ {
+ .procname = "nf_conntrack_pkt_threshold",
+ .data = &nf_conntrack_pkt_threshold,
+ .maxlen = sizeof(int),
+ .mode = 0644,
+ .proc_handler = proc_dointvec,
+ },
+
{ }
};
diff --git a/net/netfilter/nf_nat_sip.c b/net/netfilter/nf_nat_sip.c
index 791fac4..2f2414e2 100644
--- a/net/netfilter/nf_nat_sip.c
+++ b/net/netfilter/nf_nat_sip.c
@@ -111,13 +111,26 @@
newaddr = ct->tuplehash[!dir].tuple.src.u3;
newport = ct_sip_info->forced_dport ? :
ct->tuplehash[!dir].tuple.src.u.udp.port;
+ } else if (nf_inet_addr_cmp(&ct->tuplehash[dir].tuple.src.u3, addr) &&
+ ct->tuplehash[dir].tuple.src.u.udp.port != port) {
+ newaddr = ct->tuplehash[!dir].tuple.dst.u3;
+ newport = 0;
+ } else if (nf_inet_addr_cmp(&ct->tuplehash[dir].tuple.dst.u3, addr) &&
+ ct->tuplehash[dir].tuple.dst.u.udp.port != port) {
+ newaddr = ct->tuplehash[!dir].tuple.src.u3;
+ newport = 0;
} else
return 1;
if (nf_inet_addr_cmp(&newaddr, addr) && newport == port)
return 1;
- buflen = sip_sprintf_addr_port(ct, buffer, &newaddr, ntohs(newport));
+ if (newport == 0)
+ buflen = sip_sprintf_addr(ct, buffer, &newaddr, false);
+ else
+ buflen = sip_sprintf_addr_port(ct, buffer, &newaddr,
+ ntohs(newport));
+
return mangle_packet(skb, protoff, dataoff, dptr, datalen,
matchoff, matchlen, buffer, buflen);
}
diff --git a/net/wireless/db.txt b/net/wireless/db.txt
index cad82a4..f323faf 100644
--- a/net/wireless/db.txt
+++ b/net/wireless/db.txt
@@ -737,7 +737,7 @@
(5170 - 5250 @ 80), (23)
(5735 - 5835 @ 80), (23)
# 60 gHz band channels 1-4, ref: Etsi En 302 567
- ((57000 - 66000 @ 2160), (40)
+ (57000 - 66000 @ 2160), (40)
country JP: DFS-JP
(2402 - 2482 @ 40), (20)
diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c
index adf7d03..3dd7b21 100644
--- a/net/wireless/nl80211.c
+++ b/net/wireless/nl80211.c
@@ -560,6 +560,14 @@
[NL80211_NAN_SRF_MAC_ADDRS] = { .type = NLA_NESTED },
};
+/* policy for packet pattern attributes */
+static const struct nla_policy
+nl80211_packet_pattern_policy[MAX_NL80211_PKTPAT + 1] = {
+ [NL80211_PKTPAT_MASK] = { .type = NLA_BINARY, },
+ [NL80211_PKTPAT_PATTERN] = { .type = NLA_BINARY, },
+ [NL80211_PKTPAT_OFFSET] = { .type = NLA_U32 },
+};
+
static int nl80211_prepare_wdev_dump(struct sk_buff *skb,
struct netlink_callback *cb,
struct cfg80211_registered_device **rdev,
@@ -10233,7 +10241,7 @@
u8 *mask_pat;
nla_parse(pat_tb, MAX_NL80211_PKTPAT, nla_data(pat),
- nla_len(pat), NULL);
+ nla_len(pat), nl80211_packet_pattern_policy);
err = -EINVAL;
if (!pat_tb[NL80211_PKTPAT_MASK] ||
!pat_tb[NL80211_PKTPAT_PATTERN])
@@ -10483,7 +10491,7 @@
u8 *mask_pat;
nla_parse(pat_tb, MAX_NL80211_PKTPAT, nla_data(pat),
- nla_len(pat), NULL);
+ nla_len(pat), nl80211_packet_pattern_policy);
if (!pat_tb[NL80211_PKTPAT_MASK] ||
!pat_tb[NL80211_PKTPAT_PATTERN])
return -EINVAL;
diff --git a/sound/core/pcm.c b/sound/core/pcm.c
index a2c2f06..4fc68b1 100644
--- a/sound/core/pcm.c
+++ b/sound/core/pcm.c
@@ -742,6 +742,7 @@
}
substream->group = &substream->self_group;
spin_lock_init(&substream->self_group.lock);
+ spin_lock_init(&substream->runtime_lock);
mutex_init(&substream->self_group.mutex);
INIT_LIST_HEAD(&substream->self_group.substreams);
list_add_tail(&substream->link_list, &substream->self_group.substreams);
@@ -1020,9 +1021,11 @@
void snd_pcm_detach_substream(struct snd_pcm_substream *substream)
{
struct snd_pcm_runtime *runtime;
+ unsigned long flags = 0;
if (PCM_RUNTIME_CHECK(substream))
return;
+ spin_lock_irqsave(&substream->runtime_lock, flags);
runtime = substream->runtime;
if (runtime->private_free != NULL)
runtime->private_free(runtime);
@@ -1036,6 +1039,7 @@
put_pid(substream->pid);
substream->pid = NULL;
substream->pstr->substream_opened--;
+ spin_unlock_irqrestore(&substream->runtime_lock, flags);
}
static ssize_t show_pcm_class(struct device *dev,
diff --git a/sound/core/pcm_lib.c b/sound/core/pcm_lib.c
index 6faddfb..f8d0bd8 100644
--- a/sound/core/pcm_lib.c
+++ b/sound/core/pcm_lib.c
@@ -1857,8 +1857,6 @@
unsigned int cmd, void *arg)
{
switch (cmd) {
- case SNDRV_PCM_IOCTL1_INFO:
- return 0;
case SNDRV_PCM_IOCTL1_RESET:
return snd_pcm_lib_ioctl_reset(substream, arg);
case SNDRV_PCM_IOCTL1_CHANNEL_INFO:
diff --git a/sound/core/pcm_native.c b/sound/core/pcm_native.c
index 3d9ff6d..8e5649a 100644
--- a/sound/core/pcm_native.c
+++ b/sound/core/pcm_native.c
@@ -197,7 +197,6 @@
int snd_pcm_info(struct snd_pcm_substream *substream, struct snd_pcm_info *info)
{
- struct snd_pcm_runtime *runtime;
struct snd_pcm *pcm = substream->pcm;
struct snd_pcm_str *pstr = substream->pstr;
@@ -213,12 +212,7 @@
info->subdevices_count = pstr->substream_count;
info->subdevices_avail = pstr->substream_count - pstr->substream_opened;
strlcpy(info->subname, substream->name, sizeof(info->subname));
- runtime = substream->runtime;
- /* AB: FIXME!!! This is definitely nonsense */
- if (runtime) {
- info->sync = runtime->sync;
- substream->ops->ioctl(substream, SNDRV_PCM_IOCTL1_INFO, info);
- }
+
return 0;
}
diff --git a/sound/core/pcm_timer.c b/sound/core/pcm_timer.c
index 20ecd8f..5258ecc 100644
--- a/sound/core/pcm_timer.c
+++ b/sound/core/pcm_timer.c
@@ -65,9 +65,16 @@
static unsigned long snd_pcm_timer_resolution(struct snd_timer * timer)
{
struct snd_pcm_substream *substream;
+ unsigned long ret = 0, flags = 0;
substream = timer->private_data;
- return substream->runtime ? substream->runtime->timer_resolution : 0;
+ spin_lock_irqsave(&substream->runtime_lock, flags);
+ if (substream->runtime)
+ ret = substream->runtime->timer_resolution;
+ else
+ ret = 0;
+ spin_unlock_irqrestore(&substream->runtime_lock, flags);
+ return ret;
}
static int snd_pcm_timer_start(struct snd_timer * timer)
diff --git a/sound/soc/soc-ops.c b/sound/soc/soc-ops.c
index 19d6af8..fc9c9b0 100644
--- a/sound/soc/soc-ops.c
+++ b/sound/soc/soc-ops.c
@@ -379,7 +379,7 @@
unsigned int rshift = mc->rshift;
int max = mc->max;
int min = mc->min;
- int mask = (1 << (fls(min + max) - 1)) - 1;
+ unsigned int mask = (1 << (fls(min + max) - 1)) - 1;
unsigned int val;
int ret;
@@ -424,7 +424,7 @@
unsigned int rshift = mc->rshift;
int max = mc->max;
int min = mc->min;
- int mask = (1 << (fls(min + max) - 1)) - 1;
+ unsigned int mask = (1 << (fls(min + max) - 1)) - 1;
int err = 0;
unsigned int val, val_mask, val2 = 0;
diff --git a/sound/usb/usb_audio_qmi_svc.c b/sound/usb/usb_audio_qmi_svc.c
index ebc081f..a7cda4a 100644
--- a/sound/usb/usb_audio_qmi_svc.c
+++ b/sound/usb/usb_audio_qmi_svc.c
@@ -80,6 +80,7 @@
/* audio control interface */
struct usb_host_interface *ctrl_intf;
unsigned int card_num;
+ unsigned int usb_core_id;
atomic_t in_use;
struct kref kref;
wait_queue_head_t disconnect_wq;
@@ -181,6 +182,8 @@
return USB_AUDIO_DEVICE_SPEED_HIGH_V01;
case USB_SPEED_SUPER:
return USB_AUDIO_DEVICE_SPEED_SUPER_V01;
+ case USB_SPEED_SUPER_PLUS:
+ return USB_AUDIO_DEVICE_SPEED_SUPER_PLUS_V01;
default:
pr_err("%s: udev speed %d\n", __func__, udev_speed);
return USB_AUDIO_DEVICE_SPEED_INVALID_V01;
@@ -690,6 +693,7 @@
}
uadev[card_num].card_num = card_num;
+ uadev[card_num].usb_core_id = resp->controller_num;
/* cache intf specific info to use it for unmap and free xfer buf */
uadev[card_num].info[info_idx].data_xfer_ring_va = tr_data_va;
@@ -811,6 +815,8 @@
pr_debug("%s: sending qmi indication disconnect\n", __func__);
disconnect_ind.dev_event = USB_AUDIO_DEV_DISCONNECT_V01;
disconnect_ind.slot_id = dev->udev->slot_id;
+ disconnect_ind.controller_num = dev->usb_core_id;
+ disconnect_ind.controller_num_valid = 1;
ret = qmi_send_ind(svc->uaudio_svc_hdl, svc->curr_conn,
&uaudio_stream_ind_desc, &disconnect_ind,
sizeof(disconnect_ind));
diff --git a/sound/usb/usb_audio_qmi_v01.c b/sound/usb/usb_audio_qmi_v01.c
index a93665c..4fa8445 100644
--- a/sound/usb/usb_audio_qmi_v01.c
+++ b/sound/usb/usb_audio_qmi_v01.c
@@ -866,6 +866,24 @@
interrupter_num),
},
{
+ .data_type = QMI_OPT_FLAG,
+ .elem_len = 1,
+ .elem_size = sizeof(uint8_t),
+ .is_array = NO_ARRAY,
+ .tlv_type = 0x19,
+ .offset = offsetof(struct qmi_uaudio_stream_ind_msg_v01,
+ controller_num_valid),
+ },
+ {
+ .data_type = QMI_UNSIGNED_1_BYTE,
+ .elem_len = 1,
+ .elem_size = sizeof(uint8_t),
+ .is_array = NO_ARRAY,
+ .tlv_type = 0x19,
+ .offset = offsetof(struct qmi_uaudio_stream_ind_msg_v01,
+ controller_num),
+ },
+ {
.data_type = QMI_EOTI,
.is_array = NO_ARRAY,
.is_array = QMI_COMMON_TLV_TYPE,
diff --git a/sound/usb/usb_audio_qmi_v01.h b/sound/usb/usb_audio_qmi_v01.h
index 9900764..addc0ed 100644
--- a/sound/usb/usb_audio_qmi_v01.h
+++ b/sound/usb/usb_audio_qmi_v01.h
@@ -84,6 +84,7 @@
USB_AUDIO_DEVICE_SPEED_FULL_V01 = 2,
USB_AUDIO_DEVICE_SPEED_HIGH_V01 = 3,
USB_AUDIO_DEVICE_SPEED_SUPER_V01 = 4,
+ USB_AUDIO_DEVICE_SPEED_SUPER_PLUS_V01 = 5,
USB_AUDIO_DEVICE_SPEED_ENUM_MAX_VAL_V01 = INT_MAX,
};
@@ -157,8 +158,10 @@
struct apps_mem_info_v01 xhci_mem_info;
uint8_t interrupter_num_valid;
uint8_t interrupter_num;
+ uint8_t controller_num_valid;
+ uint8_t controller_num;
};
-#define QMI_UAUDIO_STREAM_IND_MSG_V01_MAX_MSG_LEN 177
+#define QMI_UAUDIO_STREAM_IND_MSG_V01_MAX_MSG_LEN 181
extern struct elem_info qmi_uaudio_stream_ind_msg_v01_ei[];
#endif