Merge "drm/msm: update debug support in msm drm driver" into msm-4.9
diff --git a/Documentation/devicetree/bindings/mmc/sdhci-msm.txt b/Documentation/devicetree/bindings/mmc/sdhci-msm.txt
index 6111c88..0db9970 100644
--- a/Documentation/devicetree/bindings/mmc/sdhci-msm.txt
+++ b/Documentation/devicetree/bindings/mmc/sdhci-msm.txt
@@ -44,6 +44,9 @@
- gpios - specifies gpios assigned for sdhc slot.
- qcom,gpio-names - a list of strings that map in order to the list of gpios
+ Tlmm pins are specified as <clk cmd data> and starting with eMMC5.0 as
+ <clk cmd data rclk>
+
Example:
aliases {
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 9638888..addb0a6 100644
--- a/Documentation/devicetree/bindings/power/supply/qcom/qpnp-fg-gen3.txt
+++ b/Documentation/devicetree/bindings/power/supply/qcom/qpnp-fg-gen3.txt
@@ -154,6 +154,20 @@
asleep and the battery is discharging. This option requires
qcom,fg-esr-timer-awake to be defined.
+- qcom,fg-esr-pulse-thresh-ma
+ Usage: optional
+ Value type: <u32>
+ Definition: ESR pulse qualification threshold in mA. If this is not
+ specified, a default value of 110 mA will be configured.
+ Allowed values are from 1 to 997.
+
+- qcom,fg-esr-meas-curr-ma
+ Usage: optional
+ Value type: <u32>
+ Definition: ESR measurement current in mA. If this is not specified,
+ a default value of 120 mA will be configured. Allowed
+ values are 60, 120, 180 and 240.
+
- qcom,cycle-counter-en
Usage: optional
Value type: <empty>
diff --git a/Documentation/devicetree/bindings/power/supply/qcom/smb138x-charger.txt b/Documentation/devicetree/bindings/power/supply/qcom/smb138x-charger.txt
index c8f2a5a..92ef23c 100644
--- a/Documentation/devicetree/bindings/power/supply/qcom/smb138x-charger.txt
+++ b/Documentation/devicetree/bindings/power/supply/qcom/smb138x-charger.txt
@@ -31,6 +31,12 @@
revid module. This is used to identify
the SMB subtype.
+- qcom,parallel-mode
+ Usage: optional
+ Value type: <u32>
+ Definition: Specifies parallel charging mode. If not specified, MID-MID
+ option is selected by default.
+
- qcom,suspend-input
Usage: optional
Value type: <empty>
diff --git a/arch/arm64/boot/dts/qcom/sdm845-cdp.dtsi b/arch/arm64/boot/dts/qcom/sdm845-cdp.dtsi
index 27a95ae..38d7d38 100644
--- a/arch/arm64/boot/dts/qcom/sdm845-cdp.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm845-cdp.dtsi
@@ -119,13 +119,7 @@
status = "ok";
};
-&pmi8998_flash2 {
- pinctrl-names = "led_enable", "led_disable";
- pinctrl-0 = <&flash_led3_front_en>;
- pinctrl-1 = <&flash_led3_front_dis>;
-};
-
-&pmi8998_torch2 {
+&pmi8998_switch1 {
pinctrl-names = "led_enable", "led_disable";
pinctrl-0 = <&flash_led3_front_en>;
pinctrl-1 = <&flash_led3_front_dis>;
diff --git a/arch/arm64/boot/dts/qcom/sdm845-mtp.dtsi b/arch/arm64/boot/dts/qcom/sdm845-mtp.dtsi
index 28afd55..e1eacb19 100644
--- a/arch/arm64/boot/dts/qcom/sdm845-mtp.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm845-mtp.dtsi
@@ -104,13 +104,7 @@
status = "ok";
};
-&pmi8998_flash2 {
- pinctrl-names = "led_enable", "led_disable";
- pinctrl-0 = <&flash_led3_front_en>;
- pinctrl-1 = <&flash_led3_front_dis>;
-};
-
-&pmi8998_torch2 {
+&pmi8998_switch1 {
pinctrl-names = "led_enable", "led_disable";
pinctrl-0 = <&flash_led3_front_en>;
pinctrl-1 = <&flash_led3_front_dis>;
diff --git a/arch/arm64/boot/dts/qcom/sdm845-pinctrl.dtsi b/arch/arm64/boot/dts/qcom/sdm845-pinctrl.dtsi
index f300684..aa96cec 100644
--- a/arch/arm64/boot/dts/qcom/sdm845-pinctrl.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm845-pinctrl.dtsi
@@ -70,16 +70,28 @@
flash_led3_front_en: flash_led3_front_en {
mux {
pins = "gpio21";
+ function = "gpio";
+ };
+
+ config {
+ pins = "gpio21";
drive_strength = <2>;
output-high;
+ bias-disable;
};
};
flash_led3_front_dis: flash_led3_front_dis {
mux {
pins = "gpio21";
+ function = "gpio";
+ };
+
+ config {
+ pins = "gpio21";
drive_strength = <2>;
output-low;
+ bias-disable;
};
};
};
diff --git a/arch/arm64/boot/dts/qcom/sdm845-regulator.dtsi b/arch/arm64/boot/dts/qcom/sdm845-regulator.dtsi
index 3a6e18e..eba6d77 100644
--- a/arch/arm64/boot/dts/qcom/sdm845-regulator.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm845-regulator.dtsi
@@ -559,7 +559,7 @@
regulator-name = "pm8998_l8";
qcom,set = <RPMH_REGULATOR_SET_ALL>;
regulator-min-microvolt = <1200000>;
- regulator-max-microvolt = <1200000>;
+ regulator-max-microvolt = <1248000>;
qcom,init-voltage = <1200000>;
qcom,init-mode = <RPMH_REGULATOR_MODE_LDO_LPM>;
};
@@ -576,9 +576,9 @@
pm8998_l9: regulator-l9 {
regulator-name = "pm8998_l9";
qcom,set = <RPMH_REGULATOR_SET_ALL>;
- regulator-min-microvolt = <1808000>;
- regulator-max-microvolt = <2960000>;
- qcom,init-voltage = <1808000>;
+ regulator-min-microvolt = <1704000>;
+ regulator-max-microvolt = <2928000>;
+ qcom,init-voltage = <1704000>;
qcom,init-mode = <RPMH_REGULATOR_MODE_LDO_LPM>;
};
};
@@ -594,9 +594,9 @@
pm8998_l10: regulator-l10 {
regulator-name = "pm8998_l10";
qcom,set = <RPMH_REGULATOR_SET_ALL>;
- regulator-min-microvolt = <1808000>;
- regulator-max-microvolt = <2960000>;
- qcom,init-voltage = <1808000>;
+ regulator-min-microvolt = <1704000>;
+ regulator-max-microvolt = <2928000>;
+ qcom,init-voltage = <1704000>;
qcom,init-mode = <RPMH_REGULATOR_MODE_LDO_LPM>;
};
};
@@ -613,7 +613,7 @@
regulator-name = "pm8998_l11";
qcom,set = <RPMH_REGULATOR_SET_ALL>;
regulator-min-microvolt = <1000000>;
- regulator-max-microvolt = <1000000>;
+ regulator-max-microvolt = <1048000>;
qcom,init-voltage = <1000000>;
qcom,init-mode = <RPMH_REGULATOR_MODE_LDO_LPM>;
};
@@ -648,9 +648,9 @@
pm8998_l13: regulator-l13 {
regulator-name = "pm8998_l13";
qcom,set = <RPMH_REGULATOR_SET_ALL>;
- regulator-min-microvolt = <1808000>;
+ regulator-min-microvolt = <1800000>;
regulator-max-microvolt = <2960000>;
- qcom,init-voltage = <1808000>;
+ qcom,init-voltage = <1800000>;
qcom,init-mode = <RPMH_REGULATOR_MODE_LDO_LPM>;
};
};
@@ -667,7 +667,7 @@
regulator-name = "pm8998_l14";
qcom,set = <RPMH_REGULATOR_SET_ALL>;
regulator-min-microvolt = <1800000>;
- regulator-max-microvolt = <1800000>;
+ regulator-max-microvolt = <1880000>;
qcom,init-voltage = <1800000>;
qcom,init-mode = <RPMH_REGULATOR_MODE_LDO_LPM>;
};
@@ -739,7 +739,7 @@
regulator-name = "pm8998_l18";
qcom,set = <RPMH_REGULATOR_SET_ALL>;
regulator-min-microvolt = <2704000>;
- regulator-max-microvolt = <2704000>;
+ regulator-max-microvolt = <2960000>;
qcom,init-voltage = <2704000>;
qcom,init-mode = <RPMH_REGULATOR_MODE_LDO_LPM>;
};
@@ -756,9 +756,9 @@
pm8998_l19: regulator-l19 {
regulator-name = "pm8998_l19";
qcom,set = <RPMH_REGULATOR_SET_ALL>;
- regulator-min-microvolt = <3008000>;
- regulator-max-microvolt = <3008000>;
- qcom,init-voltage = <3008000>;
+ regulator-min-microvolt = <2856000>;
+ regulator-max-microvolt = <3104000>;
+ qcom,init-voltage = <2856000>;
qcom,init-mode = <RPMH_REGULATOR_MODE_LDO_LPM>;
};
};
@@ -774,9 +774,9 @@
pm8998_l20: regulator-l20 {
regulator-name = "pm8998_l20";
qcom,set = <RPMH_REGULATOR_SET_ALL>;
- regulator-min-microvolt = <2960000>;
+ regulator-min-microvolt = <2704000>;
regulator-max-microvolt = <2960000>;
- qcom,init-voltage = <2960000>;
+ qcom,init-voltage = <2704000>;
qcom,init-mode = <RPMH_REGULATOR_MODE_LDO_LPM>;
};
};
@@ -792,9 +792,9 @@
pm8998_l21: regulator-l21 {
regulator-name = "pm8998_l21";
qcom,set = <RPMH_REGULATOR_SET_ALL>;
- regulator-min-microvolt = <2960000>;
+ regulator-min-microvolt = <2704000>;
regulator-max-microvolt = <2960000>;
- qcom,init-voltage = <2960000>;
+ qcom,init-voltage = <2704000>;
qcom,init-mode = <RPMH_REGULATOR_MODE_LDO_LPM>;
};
};
@@ -811,7 +811,7 @@
regulator-name = "pm8998_l22";
qcom,set = <RPMH_REGULATOR_SET_ALL>;
regulator-min-microvolt = <2864000>;
- regulator-max-microvolt = <2864000>;
+ regulator-max-microvolt = <3312000>;
qcom,init-voltage = <2864000>;
qcom,init-mode = <RPMH_REGULATOR_MODE_LDO_LPM>;
};
@@ -828,9 +828,9 @@
pm8998_l23: regulator-l23 {
regulator-name = "pm8998_l23";
qcom,set = <RPMH_REGULATOR_SET_ALL>;
- regulator-min-microvolt = <3312000>;
+ regulator-min-microvolt = <3000000>;
regulator-max-microvolt = <3312000>;
- qcom,init-voltage = <3312000>;
+ qcom,init-voltage = <3000000>;
qcom,init-mode = <RPMH_REGULATOR_MODE_LDO_LPM>;
};
};
@@ -865,9 +865,9 @@
pm8998_l25: regulator-l25 {
regulator-name = "pm8998_l25";
qcom,set = <RPMH_REGULATOR_SET_ALL>;
- regulator-min-microvolt = <3104000>;
- regulator-max-microvolt = <3104000>;
- qcom,init-voltage = <3104000>;
+ regulator-min-microvolt = <3000000>;
+ regulator-max-microvolt = <3312000>;
+ qcom,init-voltage = <3000000>;
qcom,init-mode = <RPMH_REGULATOR_MODE_LDO_LPM>;
};
};
@@ -914,9 +914,9 @@
pm8998_l28: regulator-l28 {
regulator-name = "pm8998_l28";
qcom,set = <RPMH_REGULATOR_SET_ALL>;
- regulator-min-microvolt = <3008000>;
+ regulator-min-microvolt = <2856000>;
regulator-max-microvolt = <3008000>;
- qcom,init-voltage = <3008000>;
+ qcom,init-voltage = <2856000>;
qcom,init-mode = <RPMH_REGULATOR_MODE_LDO_LPM>;
};
};
diff --git a/arch/arm64/boot/dts/qcom/sdm845-rumi.dts b/arch/arm64/boot/dts/qcom/sdm845-rumi.dts
index 0f31c0a..be41858 100644
--- a/arch/arm64/boot/dts/qcom/sdm845-rumi.dts
+++ b/arch/arm64/boot/dts/qcom/sdm845-rumi.dts
@@ -16,7 +16,6 @@
#include "sdm845.dtsi"
#include "sdm845-rumi.dtsi"
-#include "sdm845-usb.dtsi"
/ {
model = "Qualcomm Technologies, Inc. SDM845 RUMI";
compatible = "qcom,sdm845-rumi", "qcom,sdm845", "qcom,rumi";
@@ -28,32 +27,3 @@
status = "disabled";
};
};
-
-&usb0 {
- /delete-property/ qcom,usb-dbm;
- qcom,charging-disabled;
- dwc3@a600000 {
- maximum-speed = "high-speed";
- };
-};
-
-&qusb_phy0 {
- reg = <0x088e2000 0x4>,
- <0x0a720000 0x9500>;
- reg-names = "qusb_phy_base",
- "emu_phy_base";
- qcom,emulation;
- qcom,emu-init-seq = <0x19 0x1404
- 0x20 0x1414
- 0x79 0x1410
- 0x00 0x1418
- 0x99 0x1404
- 0x04 0x1408
- 0xd9 0x1404>;
-
- qcom,emu-dcm-reset-seq = <0x5 0x14 /* 0x1 0x14 for E1.2 */
- 0x100000 0x20
- 0x0 0x20
- 0x1a0 0x20 /* 0x220 0x20 for E1.2 */
- 0x80 0x28>;
-};
diff --git a/arch/arm64/boot/dts/qcom/sdm845-rumi.dtsi b/arch/arm64/boot/dts/qcom/sdm845-rumi.dtsi
index 5625531..3ec83f5 100644
--- a/arch/arm64/boot/dts/qcom/sdm845-rumi.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm845-rumi.dtsi
@@ -151,3 +151,33 @@
&pmi8998_charger {
qcom,suspend-input;
};
+
+&usb0 {
+ /delete-property/ qcom,usb-dbm;
+ extcon = <0>, <0>, <&eud>;
+ qcom,charging-disabled;
+ dwc3@a600000 {
+ maximum-speed = "high-speed";
+ };
+};
+
+&qusb_phy0 {
+ reg = <0x088e2000 0x4>,
+ <0x0a720000 0x9500>;
+ reg-names = "qusb_phy_base",
+ "emu_phy_base";
+ qcom,emulation;
+ qcom,emu-init-seq = <0x19 0x1404
+ 0x20 0x1414
+ 0x79 0x1410
+ 0x00 0x1418
+ 0x99 0x1404
+ 0x04 0x1408
+ 0xd9 0x1404>;
+
+ qcom,emu-dcm-reset-seq = <0x5 0x14 /* 0x1 0x14 for E1.2 */
+ 0x100000 0x20
+ 0x0 0x20
+ 0x1a0 0x20 /* 0x220 0x20 for E1.2 */
+ 0x80 0x28>;
+};
diff --git a/arch/arm64/boot/dts/qcom/sdm845-sde.dtsi b/arch/arm64/boot/dts/qcom/sdm845-sde.dtsi
index d99e6de..3e00577 100644
--- a/arch/arm64/boot/dts/qcom/sdm845-sde.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm845-sde.dtsi
@@ -18,16 +18,22 @@
reg-names = "mdp_phys",
"vbif_phys";
- clocks = <&clock_dispcc DISP_CC_MDSS_AHB_CLK>,
+ clocks =
+ <&clock_gcc GCC_DISP_AHB_CLK>,
+ <&clock_gcc GCC_DISP_AXI_CLK>,
+ <&clock_dispcc DISP_CC_MDSS_AHB_CLK>,
<&clock_dispcc DISP_CC_MDSS_AXI_CLK>,
<&clock_dispcc DISP_CC_MDSS_MDP_CLK_SRC>,
- <&clock_dispcc DISP_CC_MDSS_MDP_CLK>;
- clock-names = "iface_clk", "bus_clk",
- "core_clk_src", "core_clk";
- clock-rate = <0 0 300000000 300000000>;
- clock-max-rate = <0 0 430000000 430000000>;
+ <&clock_dispcc DISP_CC_MDSS_VSYNC_CLK_SRC>,
+ <&clock_dispcc DISP_CC_MDSS_MDP_CLK>,
+ <&clock_dispcc DISP_CC_MDSS_VSYNC_CLK>;
+ clock-names = "gcc_iface", "gcc_bus",
+ "iface_clk", "bus_clk", "core_clk_src",
+ "vsync_clk_src", "core_clk", "vsync_clk";
+ clock-rate = <0 0 0 0 300000000 0 300000000 0 0>;
+ clock-max-rate = <0 0 0 0 430000000 0 430000000 0 0>;
- mdp-vdd-supply = <&mdss_core_gdsc>;
+ sde-vdd-supply = <&mdss_core_gdsc>;
/* interrupt config */
interrupt-parent = <&intc>;
@@ -142,7 +148,7 @@
qcom,platform-supply-entry@0 {
reg = <0>;
- qcom,supply-name = "mdp-vdd";
+ qcom,supply-name = "sde-vdd";
qcom,supply-min-voltage = <0>;
qcom,supply-max-voltage = <0>;
qcom,supply-enable-load = <0>;
@@ -184,6 +190,11 @@
qcom,sde-rsc-version = <1>;
vdd-supply = <&mdss_core_gdsc>;
+ clocks = <&clock_dispcc DISP_CC_MDSS_RSCC_AHB_CLK>,
+ <&clock_dispcc DISP_CC_MDSS_RSCC_VSYNC_CLK>;
+ clock-names = "iface_clk", "vsync_clk";
+ clock-rate = <0 0>;
+
qcom,sde-dram-channels = <2>;
/* data and reg bus scale settings */
diff --git a/arch/arm64/boot/dts/qcom/sdm845-usb.dtsi b/arch/arm64/boot/dts/qcom/sdm845-usb.dtsi
index c5e7fe6..7c310cd 100644
--- a/arch/arm64/boot/dts/qcom/sdm845-usb.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm845-usb.dtsi
@@ -19,8 +19,6 @@
reg = <0x0a600000 0xf8c00>,
<0x088ee000 0x400>;
reg-names = "core_base", "ahb2phy_base";
- iommus = <&apps_smmu 0x740>;
- qcom,smmu-s1-bypass;
#address-cells = <1>;
#size-cells = <1>;
ranges;
@@ -284,9 +282,9 @@
status = "disabled";
};
- dbm_1p5: dbm@a8f8000 {
+ dbm_1p5: dbm@a6f8000 {
compatible = "qcom,usb-dbm-1p5";
- reg = <0xa8f8000 0x400>;
+ reg = <0xa6f8000 0x400>;
qcom,reset-ep-after-lpm-resume;
};
@@ -300,8 +298,6 @@
reg = <0x0a800000 0xf8c00>,
<0x088ee000 0x400>;
reg-names = "core_base", "ahb2phy_base";
- iommus = <&apps_smmu 0x760>;
- qcom,smmu-s1-bypass;
#address-cells = <1>;
#size-cells = <1>;
ranges;
diff --git a/arch/arm64/boot/dts/qcom/sdm845.dtsi b/arch/arm64/boot/dts/qcom/sdm845.dtsi
index 5a41ab7..415ff03 100644
--- a/arch/arm64/boot/dts/qcom/sdm845.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm845.dtsi
@@ -2206,3 +2206,4 @@
#include "sdm845-pinctrl.dtsi"
#include "sdm845-audio.dtsi"
#include "sdm845-gpu.dtsi"
+#include "sdm845-usb.dtsi"
diff --git a/drivers/clk/qcom/debugcc-sdm845.c b/drivers/clk/qcom/debugcc-sdm845.c
index d74db61..d30675c 100644
--- a/drivers/clk/qcom/debugcc-sdm845.c
+++ b/drivers/clk/qcom/debugcc-sdm845.c
@@ -113,6 +113,10 @@ static const char *const debug_mux_parent_names[] = {
"disp_cc_mdss_spdm_pclk1_clk",
"disp_cc_mdss_spdm_rot_clk",
"disp_cc_mdss_vsync_clk",
+ "measure_only_snoc_clk",
+ "measure_only_cnoc_clk",
+ "measure_only_bimc_clk",
+ "measure_only_ipa_2x_clk",
"gcc_aggre_noc_pcie_tbu_clk",
"gcc_aggre_ufs_card_axi_clk",
"gcc_aggre_ufs_phy_axi_clk",
@@ -444,6 +448,14 @@ static struct clk_debug_mux gcc_debug_mux = {
0x1C, 0xFF, 0, 0x3, 0, 1, 0x6000, 0x6008, 0x600C },
{ "disp_cc_mdss_vsync_clk", 0x47, 4, DISP_CC,
0x6, 0xFF, 0, 0x3, 0, 1, 0x6000, 0x6008, 0x600C },
+ { "measure_only_snoc_clk", 0x7, 4, GCC,
+ 0x7, 0x3FFF, 0, 0xF, 0, 4, 0x62008, 0x62000, 0x62004 },
+ { "measure_only_cnoc_clk", 0x15, 4, GCC,
+ 0x7, 0x3FFF, 0, 0xF, 0, 4, 0x62008, 0x62000, 0x62004 },
+ { "measure_only_bimc_clk", 0xc2, 4, GCC,
+ 0x7, 0x3FFF, 0, 0xF, 0, 4, 0x62008, 0x62000, 0x62004 },
+ { "measure_only_ipa_2x_clk", 0x128, 4, GCC,
+ 0x7, 0x3FFF, 0, 0xF, 0, 4, 0x62008, 0x62000, 0x62004 },
{ "gcc_aggre_noc_pcie_tbu_clk", 0x2D, 4, GCC,
0x2D, 0x3FF, 0, 0xF, 0, 4, 0x62008, 0x62000, 0x62004 },
{ "gcc_aggre_ufs_card_axi_clk", 0x11E, 4, GCC,
diff --git a/drivers/clk/qcom/dispcc-sdm845.c b/drivers/clk/qcom/dispcc-sdm845.c
index 6b1eca8..cb073a8 100644
--- a/drivers/clk/qcom/dispcc-sdm845.c
+++ b/drivers/clk/qcom/dispcc-sdm845.c
@@ -106,8 +106,8 @@ static const struct parent_map disp_cc_parent_map_3[] = {
static const char * const disp_cc_parent_names_3[] = {
"bi_tcxo",
"disp_cc_pll0",
- "gpll0",
- "gpll0",
+ "gcc_disp_gpll0_clk_src",
+ "gcc_disp_gpll0_div_clk_src",
"core_bi_pll_test_se",
};
diff --git a/drivers/clk/qcom/gcc-sdm845.c b/drivers/clk/qcom/gcc-sdm845.c
index 08dce3f..f2dcc82 100644
--- a/drivers/clk/qcom/gcc-sdm845.c
+++ b/drivers/clk/qcom/gcc-sdm845.c
@@ -36,6 +36,7 @@
#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
@@ -150,6 +151,38 @@ static const char * const gcc_parent_names_6[] = {
"core_bi_pll_test_se",
};
+static struct clk_dummy measure_only_snoc_clk = {
+ .rrate = 1000,
+ .hw.init = &(struct clk_init_data){
+ .name = "measure_only_snoc_clk",
+ .ops = &clk_dummy_ops,
+ },
+};
+
+static struct clk_dummy measure_only_cnoc_clk = {
+ .rrate = 1000,
+ .hw.init = &(struct clk_init_data){
+ .name = "measure_only_cnoc_clk",
+ .ops = &clk_dummy_ops,
+ },
+};
+
+static struct clk_dummy measure_only_bimc_clk = {
+ .rrate = 1000,
+ .hw.init = &(struct clk_init_data){
+ .name = "measure_only_bimc_clk",
+ .ops = &clk_dummy_ops,
+ },
+};
+
+static struct clk_dummy measure_only_ipa_2x_clk = {
+ .rrate = 1000,
+ .hw.init = &(struct clk_init_data){
+ .name = "measure_only_ipa_2x_clk",
+ .ops = &clk_dummy_ops,
+ },
+};
+
static struct pll_vco fabia_vco[] = {
{ 250000000, 2000000000, 0 },
{ 125000000, 1000000000, 1 },
@@ -3146,6 +3179,13 @@ static struct clk_branch gcc_video_xo_clk = {
},
};
+struct clk_hw *gcc_sdm845_hws[] = {
+ [MEASURE_ONLY_SNOC_CLK] = &measure_only_snoc_clk.hw,
+ [MEASURE_ONLY_CNOC_CLK] = &measure_only_cnoc_clk.hw,
+ [MEASURE_ONLY_BIMC_CLK] = &measure_only_bimc_clk.hw,
+ [MEASURE_ONLY_IPA_2X_CLK] = &measure_only_ipa_2x_clk.hw,
+};
+
static struct clk_regmap *gcc_sdm845_clocks[] = {
[GCC_AGGRE_NOC_PCIE_TBU_CLK] = &gcc_aggre_noc_pcie_tbu_clk.clkr,
[GCC_AGGRE_UFS_CARD_AXI_CLK] = &gcc_aggre_ufs_card_axi_clk.clkr,
@@ -3384,19 +3424,21 @@ MODULE_DEVICE_TABLE(of, gcc_sdm845_match_table);
static int gcc_sdm845_probe(struct platform_device *pdev)
{
+ struct clk *clk;
struct regmap *regmap;
- int ret = 0;
+ int i, ret = 0;
regmap = qcom_cc_map(pdev, &gcc_sdm845_desc);
if (IS_ERR(regmap))
return PTR_ERR(regmap);
/*
- * Set the CPUSS_AHB_CLK_SLEEP_ENA bit to allow the cpuss_ahb_clk to be
+ * 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, CPUSS_AHB_CLK_SLEEP_ENA);
+ 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])) {
@@ -3414,6 +3456,13 @@ static int gcc_sdm845_probe(struct platform_device *pdev)
return PTR_ERR(vdd_cx_ao.regulator[0]);
}
+ /* Register the dummy measurement clocks */
+ for (i = 0; i < ARRAY_SIZE(gcc_sdm845_hws); i++) {
+ clk = devm_clk_register(&pdev->dev, gcc_sdm845_hws[i]);
+ if (IS_ERR(clk))
+ return PTR_ERR(clk);
+ }
+
ret = qcom_cc_really_probe(pdev, &gcc_sdm845_desc, regmap);
if (ret) {
dev_err(&pdev->dev, "Failed to register GCC clocks\n");
@@ -3424,8 +3473,9 @@ static int gcc_sdm845_probe(struct platform_device *pdev)
regmap_update_bits(regmap, GCC_MMSS_MISC, 0x3, 0x3);
regmap_update_bits(regmap, GCC_GPU_MISC, 0x3, 0x3);
- /* Keep these HMSS clocks enabled always */
+ /* Keep these CPUSS clocks enabled always */
clk_prepare_enable(gcc_cpuss_ahb_clk.clkr.hw.clk);
+ clk_prepare_enable(gcc_sys_noc_cpuss_ahb_clk.clkr.hw.clk);
clk_prepare_enable(gcc_cpuss_dvm_bus_clk.clkr.hw.clk);
clk_prepare_enable(gcc_cpuss_gnoc_clk.clkr.hw.clk);
diff --git a/drivers/clk/qcom/gpucc-sdm845.c b/drivers/clk/qcom/gpucc-sdm845.c
index a95deff..d9a626e 100644
--- a/drivers/clk/qcom/gpucc-sdm845.c
+++ b/drivers/clk/qcom/gpucc-sdm845.c
@@ -80,8 +80,8 @@ static const char * const gpu_cc_parent_names_0[] = {
"bi_tcxo",
"gpu_cc_pll0",
"gpu_cc_pll1",
- "gpll0",
- "gpll0_out_even",
+ "gcc_gpu_gpll0_clk_src",
+ "gcc_gpu_gpll0_div_clk_src",
"core_bi_pll_test_se",
};
@@ -101,7 +101,7 @@ static const char * const gpu_cc_parent_names_1[] = {
"gpu_cc_pll0_out_odd",
"gpu_cc_pll1_out_even",
"gpu_cc_pll1_out_odd",
- "gpll0",
+ "gcc_gpu_gpll0_clk_src",
"core_bi_pll_test_se",
};
@@ -114,8 +114,8 @@ static const struct parent_map gpu_cc_parent_map_2[] = {
static const char * const gpu_cc_parent_names_2[] = {
"bi_tcxo",
- "gpll0",
- "gpll0",
+ "gcc_gpu_gpll0_clk_src",
+ "gcc_gpu_gpll0_div_clk_src",
"core_bi_pll_test_se",
};
diff --git a/drivers/gpu/drm/msm/msm_drv.c b/drivers/gpu/drm/msm/msm_drv.c
index 7246d84..ced015f 100644
--- a/drivers/gpu/drm/msm/msm_drv.c
+++ b/drivers/gpu/drm/msm/msm_drv.c
@@ -1706,6 +1706,13 @@ static const struct of_device_id msm_gpu_match[] = {
{ },
};
+#ifdef CONFIG_QCOM_KGSL
+static int add_gpu_components(struct device *dev,
+ struct component_match **matchptr)
+{
+ return 0;
+}
+#else
static int add_gpu_components(struct device *dev,
struct component_match **matchptr)
{
@@ -1721,6 +1728,7 @@ static int add_gpu_components(struct device *dev,
return 0;
}
+#endif
static int msm_drm_bind(struct device *dev)
{
diff --git a/drivers/gpu/drm/msm/sde/sde_hw_cdm.c b/drivers/gpu/drm/msm/sde/sde_hw_cdm.c
index 18893af..ad2910e 100644
--- a/drivers/gpu/drm/msm/sde/sde_hw_cdm.c
+++ b/drivers/gpu/drm/msm/sde/sde_hw_cdm.c
@@ -56,6 +56,19 @@ static u32 cosite_v_coeff[] = {0x00080004};
*/
static u32 offsite_v_coeff[] = {0x00060002};
+/* Limited Range rgb2yuv coeff with clamp and bias values for CSC 10 module */
+static struct sde_csc_cfg rgb2yuv_cfg = {
+ {
+ 0x0083, 0x0102, 0x0032,
+ 0x1fb5, 0x1f6c, 0x00e1,
+ 0x00e1, 0x1f45, 0x1fdc
+ },
+ { 0x00, 0x00, 0x00 },
+ { 0x0040, 0x0200, 0x0200 },
+ { 0x000, 0x3ff, 0x000, 0x3ff, 0x000, 0x3ff },
+ { 0x040, 0x3ac, 0x040, 0x3c0, 0x040, 0x3c0 },
+};
+
static struct sde_cdm_cfg *_cdm_offset(enum sde_cdm cdm,
struct sde_mdss_cfg *m,
void __iomem *addr,
@@ -279,6 +292,11 @@ struct sde_hw_cdm *sde_hw_cdm_init(enum sde_cdm idx,
sde_dbg_reg_register_dump_range(SDE_DBG_NAME, cfg->name, c->hw.blk_off,
c->hw.blk_off + c->hw.length, c->hw.xin_id);
+ /*
+ * Perform any default initialization for the chroma down module
+ * @setup default csc coefficients
+ */
+ sde_hw_cdm_setup_csc_10bit(c, &rgb2yuv_cfg);
return c;
}
diff --git a/drivers/gpu/drm/msm/sde/sde_hw_interrupts.c b/drivers/gpu/drm/msm/sde/sde_hw_interrupts.c
index 47fb07f..d5289c0 100644
--- a/drivers/gpu/drm/msm/sde/sde_hw_interrupts.c
+++ b/drivers/gpu/drm/msm/sde/sde_hw_interrupts.c
@@ -844,11 +844,21 @@ static int sde_hw_intr_disable_irq(struct sde_hw_intr *intr, int irq_idx)
static int sde_hw_intr_clear_irqs(struct sde_hw_intr *intr)
{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(sde_intr_set); i++)
+ SDE_REG_WRITE(&intr->hw, sde_intr_set[i].clr_off, 0xffffffff);
+
return 0;
}
static int sde_hw_intr_disable_irqs(struct sde_hw_intr *intr)
{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(sde_intr_set); i++)
+ SDE_REG_WRITE(&intr->hw, sde_intr_set[i].en_off, 0x00000000);
+
return 0;
}
diff --git a/drivers/gpu/drm/msm/sde/sde_kms.c b/drivers/gpu/drm/msm/sde/sde_kms.c
index 4114d1b..7e18a0e 100644
--- a/drivers/gpu/drm/msm/sde/sde_kms.c
+++ b/drivers/gpu/drm/msm/sde/sde_kms.c
@@ -1232,7 +1232,7 @@ static const struct msm_kms_funcs kms_funcs = {
/* the caller api needs to turn on clock before calling it */
static inline void _sde_kms_core_hw_rev_init(struct sde_kms *sde_kms)
{
- return;
+ sde_kms->core_rev = readl_relaxed(sde_kms->mmio + 0x0);
}
static int _sde_kms_mmu_destroy(struct sde_kms *sde_kms)
diff --git a/drivers/gpu/drm/msm/sde_rsc.c b/drivers/gpu/drm/msm/sde_rsc.c
index a9a7d4f..c1b812a 100644
--- a/drivers/gpu/drm/msm/sde_rsc.c
+++ b/drivers/gpu/drm/msm/sde_rsc.c
@@ -413,12 +413,6 @@ static int sde_rsc_switch_to_cmd(struct sde_rsc_priv *rsc,
if (client->current_state == SDE_RSC_VID_STATE)
goto end;
- /* no need to enable solver again */
- if (rsc->current_state == SDE_RSC_CLK_STATE) {
- rc = 0;
- goto end;
- }
-
if (rsc->hw_ops.state_update)
rc = rsc->hw_ops.state_update(rsc, SDE_RSC_CMD_STATE);
@@ -440,14 +434,8 @@ static bool sde_rsc_switch_to_clk(struct sde_rsc_priv *rsc)
(client->current_state == SDE_RSC_CMD_STATE))
goto end;
- /* no need to enable the solver again */
- if (rsc->current_state == SDE_RSC_CMD_STATE) {
- rc = 0;
- goto end;
- }
-
if (rsc->hw_ops.state_update)
- rc = rsc->hw_ops.state_update(rsc, SDE_RSC_CMD_STATE);
+ rc = rsc->hw_ops.state_update(rsc, SDE_RSC_CLK_STATE);
end:
return rc;
}
@@ -1086,6 +1074,7 @@ static int sde_rsc_probe(struct platform_device *pdev)
sde_rsc_clk_enable(&rsc->phandle, rsc->pclient, false);
INIT_LIST_HEAD(&rsc->client_list);
+ INIT_LIST_HEAD(&rsc->event_list);
mutex_init(&rsc->client_lock);
pr_info("sde rsc index:%d probed successfully\n",
@@ -1095,6 +1084,7 @@ static int sde_rsc_probe(struct platform_device *pdev)
snprintf(name, MAX_RSC_CLIENT_NAME_LEN, "%s%d", "sde_rsc", counter);
_sde_rsc_init_debugfs(rsc, name);
counter++;
+ rsc->power_collapse = true;
ret = component_add(&pdev->dev, &sde_rsc_comp_ops);
if (ret)
diff --git a/drivers/gpu/drm/msm/sde_rsc_hw.c b/drivers/gpu/drm/msm/sde_rsc_hw.c
index fb963ee..de579c1 100644
--- a/drivers/gpu/drm/msm/sde_rsc_hw.c
+++ b/drivers/gpu/drm/msm/sde_rsc_hw.c
@@ -314,6 +314,20 @@ int sde_rsc_mode2_entry(struct sde_rsc_priv *rsc)
wrapper_status |= BIT(0);
dss_reg_w(&rsc->wrapper_io, SDE_RSCC_WRAPPER_CTRL,
wrapper_status, rsc->debug_mode);
+
+ /**
+ * force busy and idle during clk & video mode state because it
+ * is trying to entry in mode-2 without turning on the vysnc.
+ */
+ if ((rsc->current_state == SDE_RSC_VID_STATE) ||
+ (rsc->current_state == SDE_RSC_CLK_STATE)) {
+ dss_reg_w(&rsc->wrapper_io, SDE_RSCC_WRAPPER_OVERRIDE_CTRL,
+ BIT(0) | BIT(1), rsc->debug_mode);
+ wmb(); /* force busy gurantee */
+ dss_reg_w(&rsc->wrapper_io, SDE_RSCC_WRAPPER_OVERRIDE_CTRL,
+ BIT(0) | BIT(9), rsc->debug_mode);
+ }
+
/* make sure that mode-2 is triggered before wait*/
wmb();
@@ -331,6 +345,13 @@ int sde_rsc_mode2_entry(struct sde_rsc_priv *rsc)
goto end;
}
+ if ((rsc->current_state == SDE_RSC_VID_STATE) ||
+ (rsc->current_state == SDE_RSC_CLK_STATE)) {
+ dss_reg_w(&rsc->wrapper_io, SDE_RSCC_WRAPPER_OVERRIDE_CTRL,
+ BIT(0) | BIT(8), rsc->debug_mode);
+ wmb(); /* force busy on vsync */
+ }
+
rsc_event_trigger(rsc, SDE_RSC_EVENT_POST_CORE_PC);
return 0;
@@ -343,13 +364,26 @@ int sde_rsc_mode2_entry(struct sde_rsc_priv *rsc)
return rc;
}
-int sde_rsc_mode2_exit(struct sde_rsc_priv *rsc)
+int sde_rsc_mode2_exit(struct sde_rsc_priv *rsc, enum sde_rsc_state state)
{
int rc = -EBUSY;
int count, reg;
rsc_event_trigger(rsc, SDE_RSC_EVENT_PRE_CORE_RESTORE);
+ /**
+ * force busy and idle during clk & video mode state because it
+ * is trying to entry in mode-2 without turning on the vysnc.
+ */
+ if ((state == SDE_RSC_VID_STATE) || (state == SDE_RSC_CLK_STATE)) {
+ reg = dss_reg_r(&rsc->wrapper_io,
+ SDE_RSCC_WRAPPER_OVERRIDE_CTRL, rsc->debug_mode);
+ reg |= BIT(8);
+ reg &= ~(BIT(1) | BIT(0));
+ dss_reg_w(&rsc->wrapper_io, SDE_RSCC_WRAPPER_OVERRIDE_CTRL,
+ reg, rsc->debug_mode);
+ }
+
// needs review with HPG sequence
dss_reg_w(&rsc->wrapper_io, SDE_RSCC_F1_QTMR_V1_CNTP_CVAL_LO,
0x0, rsc->debug_mode);
@@ -405,7 +439,7 @@ static int sde_rsc_state_update(struct sde_rsc_priv *rsc,
int reg;
if (rsc->power_collapse) {
- rc = sde_rsc_mode2_exit(rsc);
+ rc = sde_rsc_mode2_exit(rsc, state);
if (rc)
pr_err("power collapse: mode2 exit failed\n");
else
@@ -451,6 +485,10 @@ static int sde_rsc_state_update(struct sde_rsc_priv *rsc,
rsc_event_trigger(rsc, SDE_RSC_EVENT_SOLVER_DISABLED);
break;
+ case SDE_RSC_CLK_STATE:
+ pr_debug("clk state handling\n");
+ break;
+
case SDE_RSC_IDLE_STATE:
rc = sde_rsc_mode2_entry(rsc);
if (rc)
@@ -694,9 +732,6 @@ int sde_rsc_hw_register(struct sde_rsc_priv *rsc)
rsc->hw_ops.tcs_use_ok = rsc_hw_tcs_use_ok;
rsc->hw_ops.is_amc_mode = rsc_hw_is_amc_mode;
- rsc->hw_ops.mode2_entry = sde_rsc_mode2_entry;
- rsc->hw_ops.mode2_exit = sde_rsc_mode2_exit;
-
rsc->hw_ops.hw_vsync = rsc_hw_vsync;
rsc->hw_ops.state_update = sde_rsc_state_update;
rsc->hw_ops.debug_show = sde_rsc_debug_show;
diff --git a/drivers/gpu/drm/msm/sde_rsc_priv.h b/drivers/gpu/drm/msm/sde_rsc_priv.h
index 2563c85..30810fe 100644
--- a/drivers/gpu/drm/msm/sde_rsc_priv.h
+++ b/drivers/gpu/drm/msm/sde_rsc_priv.h
@@ -61,10 +61,6 @@ enum rsc_vsync_req {
* TCS command.
* @hw_vsync: Enables the vsync on RSC block.
* @tcs_use_ok: set TCS set to high to allow RSC to use it.
- * @mode2_entry: Request to entry mode2 when all clients are
- * requesting power collapse.
- * @mode2_exit: Request to exit mode2 when one of the client
- * is requesting against the power collapse
* @is_amc_mode: Check current amc mode status
* @state_update: Enable/override the solver based on rsc state
* status (command/video)
@@ -78,8 +74,6 @@ struct sde_rsc_hw_ops {
int (*hw_vsync)(struct sde_rsc_priv *rsc, enum rsc_vsync_req request,
char *buffer, int buffer_size, u32 mode);
int (*tcs_use_ok)(struct sde_rsc_priv *rsc);
- int (*mode2_entry)(struct sde_rsc_priv *rsc);
- int (*mode2_exit)(struct sde_rsc_priv *rsc);
bool (*is_amc_mode)(struct sde_rsc_priv *rsc);
int (*state_update)(struct sde_rsc_priv *rsc, enum sde_rsc_state state);
int (*debug_show)(struct seq_file *s, struct sde_rsc_priv *rsc);
diff --git a/drivers/gpu/msm/kgsl.c b/drivers/gpu/msm/kgsl.c
index d1c84f1..fa4ca39 100644
--- a/drivers/gpu/msm/kgsl.c
+++ b/drivers/gpu/msm/kgsl.c
@@ -1775,9 +1775,9 @@ long kgsl_ioctl_drawctxt_create(struct kgsl_device_private *dev_priv,
/* Commit the pointer to the context in context_idr */
write_lock(&device->context_lock);
idr_replace(&device->context_idr, context, context->id);
+ param->drawctxt_id = context->id;
write_unlock(&device->context_lock);
- param->drawctxt_id = context->id;
done:
return result;
}
diff --git a/drivers/iio/adc/qcom-rradc.c b/drivers/iio/adc/qcom-rradc.c
index e412230..b521df6 100644
--- a/drivers/iio/adc/qcom-rradc.c
+++ b/drivers/iio/adc/qcom-rradc.c
@@ -38,6 +38,7 @@
#define FG_ADC_RR_FAKE_BATT_HIGH_MSB 0x5B
#define FG_ADC_RR_BATT_ID_CTRL 0x60
+#define FG_ADC_RR_BATT_ID_CTRL_CHANNEL_CONV BIT(0)
#define FG_ADC_RR_BATT_ID_TRIGGER 0x61
#define FG_ADC_RR_BATT_ID_TRIGGER_CTL BIT(0)
#define FG_ADC_RR_BATT_ID_STS 0x62
@@ -748,6 +749,75 @@ static int rradc_read_channel_with_continuous_mode(struct rradc_chip *chip,
return rc;
}
+static int rradc_enable_batt_id_channel(struct rradc_chip *chip, bool enable)
+{
+ int rc = 0;
+
+ if (enable) {
+ rc = rradc_masked_write(chip, FG_ADC_RR_BATT_ID_CTRL,
+ FG_ADC_RR_BATT_ID_CTRL_CHANNEL_CONV,
+ FG_ADC_RR_BATT_ID_CTRL_CHANNEL_CONV);
+ if (rc < 0) {
+ pr_err("Enabling BATT ID channel failed:%d\n", rc);
+ return rc;
+ }
+ } else {
+ rc = rradc_masked_write(chip, FG_ADC_RR_BATT_ID_CTRL,
+ FG_ADC_RR_BATT_ID_CTRL_CHANNEL_CONV, 0);
+ if (rc < 0) {
+ pr_err("Disabling BATT ID channel failed:%d\n", rc);
+ return rc;
+ }
+ }
+
+ return rc;
+}
+
+static int rradc_do_batt_id_conversion(struct rradc_chip *chip,
+ struct rradc_chan_prop *prop, u16 *data, u8 *buf)
+{
+ int rc = 0, ret = 0;
+
+ rc = rradc_enable_batt_id_channel(chip, true);
+ if (rc < 0) {
+ pr_err("Enabling BATT ID channel failed:%d\n", rc);
+ return rc;
+ }
+
+ rc = rradc_masked_write(chip, FG_ADC_RR_BATT_ID_TRIGGER,
+ FG_ADC_RR_BATT_ID_TRIGGER_CTL,
+ FG_ADC_RR_BATT_ID_TRIGGER_CTL);
+ if (rc < 0) {
+ pr_err("BATT_ID trigger set failed:%d\n", rc);
+ ret = rc;
+ rc = rradc_enable_batt_id_channel(chip, false);
+ if (rc < 0)
+ pr_err("Disabling BATT ID channel failed:%d\n", rc);
+ return ret;
+ }
+
+ rc = rradc_read_channel_with_continuous_mode(chip, prop, buf);
+ if (rc < 0) {
+ pr_err("Error reading in continuous mode:%d\n", rc);
+ ret = rc;
+ }
+
+ rc = rradc_masked_write(chip, FG_ADC_RR_BATT_ID_TRIGGER,
+ FG_ADC_RR_BATT_ID_TRIGGER_CTL, 0);
+ if (rc < 0) {
+ pr_err("BATT_ID trigger re-set failed:%d\n", rc);
+ ret = rc;
+ }
+
+ rc = rradc_enable_batt_id_channel(chip, false);
+ if (rc < 0) {
+ pr_err("Disabling BATT ID channel failed:%d\n", rc);
+ ret = rc;
+ }
+
+ return ret;
+}
+
static int rradc_do_conversion(struct rradc_chip *chip,
struct rradc_chan_prop *prop, u16 *data)
{
@@ -760,24 +830,9 @@ static int rradc_do_conversion(struct rradc_chip *chip,
switch (prop->channel) {
case RR_ADC_BATT_ID:
- rc = rradc_masked_write(chip, FG_ADC_RR_BATT_ID_TRIGGER,
- FG_ADC_RR_BATT_ID_TRIGGER_CTL,
- FG_ADC_RR_BATT_ID_TRIGGER_CTL);
+ rc = rradc_do_batt_id_conversion(chip, prop, data, buf);
if (rc < 0) {
- pr_err("BATT_ID trigger set failed:%d\n", rc);
- goto fail;
- }
-
- rc = rradc_read_channel_with_continuous_mode(chip, prop, buf);
- if (rc < 0) {
- pr_err("Error reading in continuous mode:%d\n", rc);
- goto fail;
- }
-
- rc = rradc_masked_write(chip, FG_ADC_RR_BATT_ID_TRIGGER,
- FG_ADC_RR_BATT_ID_TRIGGER_CTL, 0);
- if (rc < 0) {
- pr_err("BATT_ID trigger re-set failed:%d\n", rc);
+ pr_err("Battery ID conversion failed:%d\n", rc);
goto fail;
}
break;
diff --git a/drivers/iio/adc/qcom-tadc.c b/drivers/iio/adc/qcom-tadc.c
index 9241288..05b1985 100644
--- a/drivers/iio/adc/qcom-tadc.c
+++ b/drivers/iio/adc/qcom-tadc.c
@@ -18,7 +18,12 @@
#include <linux/of_irq.h>
#include <linux/platform_device.h>
#include <linux/regmap.h>
+#include <linux/power_supply.h>
+#include <linux/pmic-voter.h>
+#define USB_PRESENT_VOTER "USB_PRESENT_VOTER"
+#define SLEEP_VOTER "SLEEP_VOTER"
+#define SHUTDOWN_VOTER "SHUTDOWN_VOTER"
#define TADC_REVISION1_REG 0x00
#define TADC_REVISION2_REG 0x01
#define TADC_REVISION3_REG 0x02
@@ -54,6 +59,7 @@
#define TADC_CH7_ADC_HI_REG(chip) (chip->tadc_base + 0x73)
#define TADC_CH8_ADC_LO_REG(chip) (chip->tadc_base + 0x74)
#define TADC_CH8_ADC_HI_REG(chip) (chip->tadc_base + 0x75)
+#define TADC_ADC_DIRECT_TST(chip) (chip->tadc_base + 0xE7)
/* TADC_CMP register definitions */
#define TADC_CMP_THR1_CMP_REG(chip) (chip->tadc_cmp_base + 0x51)
@@ -217,6 +223,12 @@ struct tadc_chip {
struct tadc_chan_data chans[TADC_NUM_CH];
struct completion eoc_complete;
struct mutex write_lock;
+ struct mutex conv_lock;
+ struct power_supply *usb_psy;
+ struct votable *tadc_disable_votable;
+ struct work_struct status_change_work;
+ struct notifier_block nb;
+ u8 hwtrig_conv;
};
struct tadc_pt {
@@ -274,7 +286,7 @@ static bool tadc_is_reg_locked(struct tadc_chip *chip, u16 reg)
if ((reg & 0xFF00) == chip->tadc_cmp_base)
return true;
- if (reg == TADC_HWTRIG_CONV_CH_EN_REG(chip))
+ if (reg >= TADC_HWTRIG_CONV_CH_EN_REG(chip))
return true;
return false;
@@ -345,6 +357,26 @@ static int tadc_bulk_write(struct tadc_chip *chip, u16 reg, u8 *data,
return rc;
}
+static int tadc_masked_write(struct tadc_chip *chip, u16 reg, u8 mask, u8 data)
+{
+ int rc = 0;
+
+ mutex_lock(&chip->write_lock);
+ if (tadc_is_reg_locked(chip, reg)) {
+ rc = regmap_write(chip->regmap, (reg & 0xFF00) | 0xD0, 0xA5);
+ if (rc < 0) {
+ pr_err("Couldn't unlock secure register rc=%d\n", rc);
+ goto unlock;
+ }
+ }
+
+ rc = regmap_update_bits(chip->regmap, reg, mask, data);
+
+unlock:
+ mutex_unlock(&chip->write_lock);
+ return rc;
+}
+
static int tadc_lerp(const struct tadc_pt *pts, size_t size, bool inv,
s32 input, s32 *output)
{
@@ -480,12 +512,22 @@ static int tadc_do_conversion(struct tadc_chip *chip, u8 channels, s16 *adc)
{
unsigned long timeout, timeleft;
u8 val[TADC_NUM_CH * 2];
- int rc, i;
+ int rc = 0, i;
+ mutex_lock(&chip->conv_lock);
rc = tadc_read(chip, TADC_MBG_ERR_REG(chip), val, 1);
if (rc < 0) {
pr_err("Couldn't read mbg error status rc=%d\n", rc);
- return rc;
+ goto unlock;
+ }
+
+ reinit_completion(&chip->eoc_complete);
+
+ if (get_effective_result(chip->tadc_disable_votable)) {
+ /* leave it back in completed state */
+ complete_all(&chip->eoc_complete);
+ rc = -ENODATA;
+ goto unlock;
}
if (val[0] != 0) {
@@ -496,7 +538,7 @@ static int tadc_do_conversion(struct tadc_chip *chip, u8 channels, s16 *adc)
rc = tadc_write(chip, TADC_CONV_REQ_REG(chip), channels);
if (rc < 0) {
pr_err("Couldn't write conversion request rc=%d\n", rc);
- return rc;
+ goto unlock;
}
timeout = msecs_to_jiffies(CONVERSION_TIMEOUT_MS);
@@ -506,25 +548,34 @@ static int tadc_do_conversion(struct tadc_chip *chip, u8 channels, s16 *adc)
rc = tadc_read(chip, TADC_SW_CH_CONV_REG(chip), val, 1);
if (rc < 0) {
pr_err("Couldn't read conversion status rc=%d\n", rc);
- return rc;
+ goto unlock;
}
+ /*
+ * check one last time if the channel we are requesting
+ * has completed conversion
+ */
if (val[0] != channels) {
- pr_err("Conversion timed out\n");
- return -ETIMEDOUT;
+ rc = -ETIMEDOUT;
+ goto unlock;
}
}
rc = tadc_read(chip, TADC_CH1_ADC_LO_REG(chip), val, ARRAY_SIZE(val));
if (rc < 0) {
pr_err("Couldn't read adc channels rc=%d\n", rc);
- return rc;
+ goto unlock;
}
for (i = 0; i < TADC_NUM_CH; i++)
adc[i] = (s16)(val[i * 2] | (u16)val[i * 2 + 1] << 8);
- return jiffies_to_msecs(timeout - timeleft);
+ pr_debug("Conversion time for channels 0x%x = %dms\n", channels,
+ jiffies_to_msecs(timeout - timeleft));
+
+unlock:
+ mutex_unlock(&chip->conv_lock);
+ return rc;
}
static int tadc_read_raw(struct iio_dev *indio_dev,
@@ -593,12 +644,17 @@ static int tadc_read_raw(struct iio_dev *indio_dev,
break;
default:
rc = tadc_do_conversion(chip, BIT(chan->channel), adc);
- if (rc >= 0)
- *val = adc[chan->channel];
+ if (rc < 0) {
+ if (rc != -ENODATA)
+ pr_err("Couldn't read battery current and voltage channels rc=%d\n",
+ rc);
+ return rc;
+ }
+ *val = adc[chan->channel];
break;
}
- if (rc < 0) {
+ if (rc < 0 && rc != -ENODATA) {
pr_err("Couldn't read channel %d\n", chan->channel);
return rc;
}
@@ -630,7 +686,7 @@ static int tadc_read_raw(struct iio_dev *indio_dev,
case TADC_BATT_P:
rc = tadc_do_conversion(chip,
BIT(TADC_BATT_I) | BIT(TADC_BATT_V), adc);
- if (rc < 0) {
+ if (rc < 0 && rc != -ENODATA) {
pr_err("Couldn't read battery current and voltage channels rc=%d\n",
rc);
return rc;
@@ -641,7 +697,7 @@ static int tadc_read_raw(struct iio_dev *indio_dev,
case TADC_INPUT_P:
rc = tadc_do_conversion(chip,
BIT(TADC_INPUT_I) | BIT(TADC_INPUT_V), adc);
- if (rc < 0) {
+ if (rc < 0 && rc != -ENODATA) {
pr_err("Couldn't read input current and voltage channels rc=%d\n",
rc);
return rc;
@@ -683,6 +739,7 @@ static int tadc_read_raw(struct iio_dev *indio_dev,
case TADC_DIE_TEMP:
case TADC_DIE_TEMP_THR1:
case TADC_DIE_TEMP_THR2:
+ case TADC_DIE_TEMP_THR3:
*val = chan_data->scale;
return IIO_VAL_INT;
case TADC_BATT_I:
@@ -821,15 +878,137 @@ static int tadc_write_raw(struct iio_dev *indio_dev,
return 0;
}
-
static irqreturn_t handle_eoc(int irq, void *dev_id)
{
struct tadc_chip *chip = dev_id;
- complete(&chip->eoc_complete);
+ complete_all(&chip->eoc_complete);
return IRQ_HANDLED;
}
+static int tadc_disable_vote_callback(struct votable *votable,
+ void *data, int disable, const char *client)
+{
+ struct tadc_chip *chip = data;
+ int rc;
+ int timeout;
+ unsigned long timeleft;
+
+ if (disable) {
+ timeout = msecs_to_jiffies(CONVERSION_TIMEOUT_MS);
+ timeleft = wait_for_completion_timeout(&chip->eoc_complete,
+ timeout);
+ if (timeleft == 0)
+ pr_err("Timed out waiting for eoc, disabling hw conversions regardless\n");
+
+ rc = tadc_read(chip, TADC_HWTRIG_CONV_CH_EN_REG(chip),
+ &chip->hwtrig_conv, 1);
+ if (rc < 0) {
+ pr_err("Couldn't save hw conversions rc=%d\n", rc);
+ return rc;
+ }
+ rc = tadc_write(chip, TADC_HWTRIG_CONV_CH_EN_REG(chip), 0x00);
+ if (rc < 0) {
+ pr_err("Couldn't disable hw conversions rc=%d\n", rc);
+ return rc;
+ }
+ rc = tadc_write(chip, TADC_ADC_DIRECT_TST(chip), 0x80);
+ if (rc < 0) {
+ pr_err("Couldn't enable direct test mode rc=%d\n", rc);
+ return rc;
+ }
+ } else {
+ rc = tadc_write(chip, TADC_ADC_DIRECT_TST(chip), 0x00);
+ if (rc < 0) {
+ pr_err("Couldn't disable direct test mode rc=%d\n", rc);
+ return rc;
+ }
+ rc = tadc_write(chip, TADC_HWTRIG_CONV_CH_EN_REG(chip),
+ chip->hwtrig_conv);
+ if (rc < 0) {
+ pr_err("Couldn't restore hw conversions rc=%d\n", rc);
+ return rc;
+ }
+ }
+
+ pr_debug("client: %s disable: %d\n", client, disable);
+ return 0;
+}
+
+static void status_change_work(struct work_struct *work)
+{
+ struct tadc_chip *chip = container_of(work,
+ struct tadc_chip, status_change_work);
+ union power_supply_propval pval = {0, };
+ int rc;
+
+ if (!chip->usb_psy)
+ chip->usb_psy = power_supply_get_by_name("usb");
+
+ if (!chip->usb_psy) {
+ /* treat usb is not present */
+ vote(chip->tadc_disable_votable, USB_PRESENT_VOTER, true, 0);
+ return;
+ }
+
+ rc = power_supply_get_property(chip->usb_psy,
+ POWER_SUPPLY_PROP_PRESENT, &pval);
+ if (rc < 0) {
+ pr_err("Couldn't get present status rc=%d\n", rc);
+ /* treat usb is not present */
+ vote(chip->tadc_disable_votable, USB_PRESENT_VOTER, true, 0);
+ return;
+ }
+
+ /* disable if usb is not present */
+ vote(chip->tadc_disable_votable, USB_PRESENT_VOTER, !pval.intval, 0);
+}
+
+static int tadc_notifier_call(struct notifier_block *nb,
+ unsigned long ev, void *v)
+{
+ struct power_supply *psy = v;
+ struct tadc_chip *chip = container_of(nb, struct tadc_chip, nb);
+
+ if (ev != PSY_EVENT_PROP_CHANGED)
+ return NOTIFY_OK;
+
+ if ((strcmp(psy->desc->name, "usb") == 0))
+ schedule_work(&chip->status_change_work);
+
+ return NOTIFY_OK;
+}
+
+static int tadc_register_notifier(struct tadc_chip *chip)
+{
+ int rc;
+
+ chip->nb.notifier_call = tadc_notifier_call;
+ rc = power_supply_reg_notifier(&chip->nb);
+ if (rc < 0) {
+ pr_err("Couldn't register psy notifier rc = %d\n", rc);
+ return rc;
+ }
+
+ return 0;
+}
+
+static int tadc_suspend(struct device *dev)
+{
+ struct tadc_chip *chip = dev_get_drvdata(dev);
+
+ vote(chip->tadc_disable_votable, SLEEP_VOTER, true, 0);
+ return 0;
+}
+
+static int tadc_resume(struct device *dev)
+{
+ struct tadc_chip *chip = dev_get_drvdata(dev);
+
+ vote(chip->tadc_disable_votable, SLEEP_VOTER, false, 0);
+ return 0;
+}
+
static int tadc_set_therm_table(struct tadc_chan_data *chan_data, u32 beta,
u32 rtherm)
{
@@ -975,16 +1154,23 @@ static int tadc_init_hw(struct tadc_chip *chip)
return rc;
}
- /* enable all temperature hardware triggers */
- rc = tadc_write(chip, TADC_HWTRIG_CONV_CH_EN_REG(chip),
- BIT(TADC_THERM1) |
- BIT(TADC_THERM2) |
- BIT(TADC_DIE_TEMP));
+ /* enable connector and die temp hardware triggers */
+ rc = tadc_masked_write(chip, TADC_HWTRIG_CONV_CH_EN_REG(chip),
+ BIT(TADC_THERM2) | BIT(TADC_DIE_TEMP),
+ BIT(TADC_THERM2) | BIT(TADC_DIE_TEMP));
if (rc < 0) {
pr_err("Couldn't enable hardware triggers rc=%d\n", rc);
return rc;
}
+ /* save hw triggered conversion configuration */
+ rc = tadc_read(chip, TADC_HWTRIG_CONV_CH_EN_REG(chip),
+ &chip->hwtrig_conv, 1);
+ if (rc < 0) {
+ pr_err("Couldn't save hw conversions rc=%d\n", rc);
+ return rc;
+ }
+
return 0;
}
@@ -1009,6 +1195,12 @@ static int tadc_probe(struct platform_device *pdev)
chip->dev = &pdev->dev;
init_completion(&chip->eoc_complete);
+ /*
+ * set the completion in "completed" state so disable of the tadc
+ * can progress
+ */
+ complete_all(&chip->eoc_complete);
+
rc = of_property_read_u32(node, "reg", &chip->tadc_base);
if (rc < 0) {
pr_err("Couldn't read base address rc=%d\n", rc);
@@ -1017,6 +1209,8 @@ static int tadc_probe(struct platform_device *pdev)
chip->tadc_cmp_base = chip->tadc_base + 0x100;
mutex_init(&chip->write_lock);
+ mutex_init(&chip->conv_lock);
+ INIT_WORK(&chip->status_change_work, status_change_work);
chip->regmap = dev_get_regmap(chip->dev->parent, NULL);
if (!chip->regmap) {
pr_err("Couldn't get regmap\n");
@@ -1035,17 +1229,36 @@ static int tadc_probe(struct platform_device *pdev)
return rc;
}
+ chip->tadc_disable_votable = create_votable("SMB_TADC_DISABLE",
+ VOTE_SET_ANY,
+ tadc_disable_vote_callback,
+ chip);
+ if (IS_ERR(chip->tadc_disable_votable)) {
+ rc = PTR_ERR(chip->tadc_disable_votable);
+ return rc;
+ }
+ /* assume usb is not present */
+ vote(chip->tadc_disable_votable, USB_PRESENT_VOTER, true, 0);
+ vote(chip->tadc_disable_votable, SHUTDOWN_VOTER, false, 0);
+ vote(chip->tadc_disable_votable, SLEEP_VOTER, false, 0);
+
+ rc = tadc_register_notifier(chip);
+ if (rc < 0) {
+ pr_err("Couldn't register notifier=%d\n", rc);
+ goto destroy_votable;
+ }
+
irq = of_irq_get_byname(node, "eoc");
if (irq < 0) {
pr_err("Couldn't get eoc irq rc=%d\n", irq);
- return irq;
+ goto destroy_votable;
}
rc = devm_request_threaded_irq(chip->dev, irq, NULL, handle_eoc,
IRQF_ONESHOT, "eoc", chip);
if (rc < 0) {
pr_err("Couldn't request irq %d rc=%d\n", irq, rc);
- return rc;
+ goto destroy_votable;
}
indio_dev->dev.parent = chip->dev;
@@ -1058,17 +1271,37 @@ static int tadc_probe(struct platform_device *pdev)
rc = devm_iio_device_register(chip->dev, indio_dev);
if (rc < 0) {
pr_err("Couldn't register IIO device rc=%d\n", rc);
- return rc;
+ goto destroy_votable;
}
+ platform_set_drvdata(pdev, chip);
return 0;
+
+destroy_votable:
+ destroy_votable(chip->tadc_disable_votable);
+ return rc;
}
static int tadc_remove(struct platform_device *pdev)
{
+ struct tadc_chip *chip = platform_get_drvdata(pdev);
+
+ destroy_votable(chip->tadc_disable_votable);
return 0;
}
+static void tadc_shutdown(struct platform_device *pdev)
+{
+ struct tadc_chip *chip = platform_get_drvdata(pdev);
+
+ vote(chip->tadc_disable_votable, SHUTDOWN_VOTER, true, 0);
+}
+
+static const struct dev_pm_ops tadc_pm_ops = {
+ .resume = tadc_resume,
+ .suspend = tadc_suspend,
+};
+
static const struct of_device_id tadc_match_table[] = {
{ .compatible = "qcom,tadc" },
{ }
@@ -1076,12 +1309,14 @@ static const struct of_device_id tadc_match_table[] = {
MODULE_DEVICE_TABLE(of, tadc_match_table);
static struct platform_driver tadc_driver = {
- .driver = {
+ .driver = {
.name = "qcom-tadc",
.of_match_table = tadc_match_table,
+ .pm = &tadc_pm_ops,
},
- .probe = tadc_probe,
- .remove = tadc_remove,
+ .probe = tadc_probe,
+ .remove = tadc_remove,
+ .shutdown = tadc_shutdown,
};
module_platform_driver(tadc_driver);
diff --git a/drivers/leds/leds-qpnp-flash-v2.c b/drivers/leds/leds-qpnp-flash-v2.c
index da1cb99..fdc4b30 100644
--- a/drivers/leds/leds-qpnp-flash-v2.c
+++ b/drivers/leds/leds-qpnp-flash-v2.c
@@ -175,9 +175,7 @@ enum {
struct flash_node_data {
struct platform_device *pdev;
struct led_classdev cdev;
- struct pinctrl *pinctrl;
- struct pinctrl_state *gpio_state_active;
- struct pinctrl_state *gpio_state_suspend;
+ struct pinctrl *strobe_pinctrl;
struct pinctrl_state *hw_strobe_state_active;
struct pinctrl_state *hw_strobe_state_suspend;
int hw_strobe_gpio;
@@ -198,6 +196,9 @@ struct flash_node_data {
struct flash_switch_data {
struct platform_device *pdev;
struct regulator *vreg;
+ struct pinctrl *led_en_pinctrl;
+ struct pinctrl_state *gpio_state_active;
+ struct pinctrl_state *gpio_state_suspend;
struct led_classdev cdev;
int led_mask;
bool regulator_on;
@@ -570,9 +571,9 @@ static int qpnp_flash_led_hw_strobe_enable(struct flash_node_data *fnode,
if (gpio_is_valid(fnode->hw_strobe_gpio)) {
gpio_set_value(fnode->hw_strobe_gpio, on ? 1 : 0);
- } else if (fnode->hw_strobe_state_active &&
+ } else if (fnode->strobe_pinctrl && fnode->hw_strobe_state_active &&
fnode->hw_strobe_state_suspend) {
- rc = pinctrl_select_state(fnode->pinctrl,
+ rc = pinctrl_select_state(fnode->strobe_pinctrl,
on ? fnode->hw_strobe_state_active :
fnode->hw_strobe_state_suspend);
if (rc < 0) {
@@ -949,15 +950,6 @@ static int qpnp_flash_led_switch_disable(struct flash_switch_data *snode)
led->fnode[i].led_on = false;
- if (led->fnode[i].pinctrl) {
- rc = pinctrl_select_state(led->fnode[i].pinctrl,
- led->fnode[i].gpio_state_suspend);
- if (rc < 0) {
- pr_err("failed to disable GPIO, rc=%d\n", rc);
- return rc;
- }
- }
-
if (led->fnode[i].trigger & FLASH_LED_HW_SW_STROBE_SEL_BIT) {
rc = qpnp_flash_led_hw_strobe_enable(&led->fnode[i],
led->pdata->hw_strobe_option, false);
@@ -969,6 +961,17 @@ static int qpnp_flash_led_switch_disable(struct flash_switch_data *snode)
}
}
+ if (snode->led_en_pinctrl) {
+ pr_debug("Selecting suspend state for %s\n", snode->cdev.name);
+ rc = pinctrl_select_state(snode->led_en_pinctrl,
+ snode->gpio_state_suspend);
+ if (rc < 0) {
+ pr_err("failed to select pinctrl suspend state rc=%d\n",
+ rc);
+ return rc;
+ }
+ }
+
snode->enabled = false;
return 0;
}
@@ -1039,15 +1042,6 @@ static int qpnp_flash_led_switch_set(struct flash_switch_data *snode, bool on)
val |= FLASH_LED_ENABLE << led->fnode[i].id;
- if (led->fnode[i].pinctrl) {
- rc = pinctrl_select_state(led->fnode[i].pinctrl,
- led->fnode[i].gpio_state_active);
- if (rc < 0) {
- pr_err("failed to enable GPIO rc=%d\n", rc);
- return rc;
- }
- }
-
if (led->fnode[i].trigger & FLASH_LED_HW_SW_STROBE_SEL_BIT) {
rc = qpnp_flash_led_hw_strobe_enable(&led->fnode[i],
led->pdata->hw_strobe_option, true);
@@ -1059,6 +1053,17 @@ static int qpnp_flash_led_switch_set(struct flash_switch_data *snode, bool on)
}
}
+ if (snode->led_en_pinctrl) {
+ pr_debug("Selecting active state for %s\n", snode->cdev.name);
+ rc = pinctrl_select_state(snode->led_en_pinctrl,
+ snode->gpio_state_active);
+ if (rc < 0) {
+ pr_err("failed to select pinctrl active state rc=%d\n",
+ rc);
+ return rc;
+ }
+ }
+
if (led->enable == 0) {
rc = qpnp_flash_led_masked_write(led,
FLASH_LED_REG_MOD_CTRL(led->base),
@@ -1461,6 +1466,20 @@ static int qpnp_flash_led_parse_each_led_dt(struct qpnp_flash_led *led,
}
fnode->trigger = (strobe_sel << 2) | (edge_trigger << 1) | active_high;
+ rc = led_classdev_register(&led->pdev->dev, &fnode->cdev);
+ if (rc < 0) {
+ pr_err("Unable to register led node %d\n", fnode->id);
+ return rc;
+ }
+
+ fnode->cdev.dev->of_node = node;
+ fnode->strobe_pinctrl = devm_pinctrl_get(fnode->cdev.dev);
+ if (IS_ERR_OR_NULL(fnode->strobe_pinctrl)) {
+ pr_debug("No pinctrl defined for %s, err=%ld\n",
+ fnode->cdev.name, PTR_ERR(fnode->strobe_pinctrl));
+ fnode->strobe_pinctrl = NULL;
+ }
+
if (fnode->trigger & FLASH_LED_HW_SW_STROBE_SEL_BIT) {
if (of_find_property(node, "qcom,hw-strobe-gpio", NULL)) {
fnode->hw_strobe_gpio = of_get_named_gpio(node,
@@ -1470,11 +1489,11 @@ static int qpnp_flash_led_parse_each_led_dt(struct qpnp_flash_led *led,
return fnode->hw_strobe_gpio;
}
gpio_direction_output(fnode->hw_strobe_gpio, 0);
- } else {
+ } else if (fnode->strobe_pinctrl) {
fnode->hw_strobe_gpio = -1;
fnode->hw_strobe_state_active =
- pinctrl_lookup_state(fnode->pinctrl,
- "strobe_enable");
+ pinctrl_lookup_state(fnode->strobe_pinctrl,
+ "strobe_enable");
if (IS_ERR_OR_NULL(fnode->hw_strobe_state_active)) {
pr_err("No active pin for hardware strobe, rc=%ld\n",
PTR_ERR(fnode->hw_strobe_state_active));
@@ -1482,8 +1501,8 @@ static int qpnp_flash_led_parse_each_led_dt(struct qpnp_flash_led *led,
}
fnode->hw_strobe_state_suspend =
- pinctrl_lookup_state(fnode->pinctrl,
- "strobe_disable");
+ pinctrl_lookup_state(fnode->strobe_pinctrl,
+ "strobe_disable");
if (IS_ERR_OR_NULL(fnode->hw_strobe_state_suspend)) {
pr_err("No suspend pin for hardware strobe, rc=%ld\n",
PTR_ERR(fnode->hw_strobe_state_suspend)
@@ -1493,38 +1512,6 @@ static int qpnp_flash_led_parse_each_led_dt(struct qpnp_flash_led *led,
}
}
- rc = led_classdev_register(&led->pdev->dev, &fnode->cdev);
- if (rc < 0) {
- pr_err("Unable to register led node %d\n", fnode->id);
- return rc;
- }
-
- fnode->cdev.dev->of_node = node;
-
- fnode->pinctrl = devm_pinctrl_get(fnode->cdev.dev);
- if (IS_ERR_OR_NULL(fnode->pinctrl)) {
- pr_debug("No pinctrl defined\n");
- fnode->pinctrl = NULL;
- } else {
- fnode->gpio_state_active =
- pinctrl_lookup_state(fnode->pinctrl, "led_enable");
- if (IS_ERR_OR_NULL(fnode->gpio_state_active)) {
- pr_err("Cannot lookup LED active state\n");
- devm_pinctrl_put(fnode->pinctrl);
- fnode->pinctrl = NULL;
- return PTR_ERR(fnode->gpio_state_active);
- }
-
- fnode->gpio_state_suspend =
- pinctrl_lookup_state(fnode->pinctrl, "led_disable");
- if (IS_ERR_OR_NULL(fnode->gpio_state_suspend)) {
- pr_err("Cannot lookup LED disable state\n");
- devm_pinctrl_put(fnode->pinctrl);
- fnode->pinctrl = NULL;
- return PTR_ERR(fnode->gpio_state_suspend);
- }
- }
-
return 0;
}
@@ -1589,6 +1576,36 @@ static int qpnp_flash_led_parse_and_register_switch(struct qpnp_flash_led *led,
}
snode->cdev.dev->of_node = node;
+
+ snode->led_en_pinctrl = devm_pinctrl_get(snode->cdev.dev);
+ if (IS_ERR_OR_NULL(snode->led_en_pinctrl)) {
+ pr_debug("No pinctrl defined for %s, err=%ld\n",
+ snode->cdev.name, PTR_ERR(snode->led_en_pinctrl));
+ snode->led_en_pinctrl = NULL;
+ }
+
+ if (snode->led_en_pinctrl) {
+ snode->gpio_state_active =
+ pinctrl_lookup_state(snode->led_en_pinctrl,
+ "led_enable");
+ if (IS_ERR_OR_NULL(snode->gpio_state_active)) {
+ pr_err("Cannot lookup LED active state\n");
+ devm_pinctrl_put(snode->led_en_pinctrl);
+ snode->led_en_pinctrl = NULL;
+ return PTR_ERR(snode->gpio_state_active);
+ }
+
+ snode->gpio_state_suspend =
+ pinctrl_lookup_state(snode->led_en_pinctrl,
+ "led_disable");
+ if (IS_ERR_OR_NULL(snode->gpio_state_suspend)) {
+ pr_err("Cannot lookup LED disable state\n");
+ devm_pinctrl_put(snode->led_en_pinctrl);
+ snode->led_en_pinctrl = NULL;
+ return PTR_ERR(snode->gpio_state_suspend);
+ }
+ }
+
return 0;
}
diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c
index e19d912..9551238 100644
--- a/drivers/mmc/core/core.c
+++ b/drivers/mmc/core/core.c
@@ -1823,6 +1823,7 @@ int mmc_set_signal_voltage(struct mmc_host *host, int signal_voltage, u32 ocr)
* During a signal voltage level switch, the clock must be gated
* for 5 ms according to the SD spec
*/
+ host->card_clock_off = true;
clock = host->ios.clock;
host->ios.clock = 0;
mmc_set_ios(host);
@@ -1833,6 +1834,9 @@ int mmc_set_signal_voltage(struct mmc_host *host, int signal_voltage, u32 ocr)
* sent CMD11, so a power cycle is required anyway
*/
err = -EAGAIN;
+ host->ios.clock = clock;
+ mmc_set_ios(host);
+ host->card_clock_off = false;
goto power_cycle;
}
@@ -1841,6 +1845,7 @@ int mmc_set_signal_voltage(struct mmc_host *host, int signal_voltage, u32 ocr)
host->ios.clock = clock;
mmc_set_ios(host);
+ host->card_clock_off = false;
/* Wait for at least 1 ms according to spec */
mmc_delay(1);
diff --git a/drivers/mmc/core/host.c b/drivers/mmc/core/host.c
index f18105f..e9f74a2 100644
--- a/drivers/mmc/core/host.c
+++ b/drivers/mmc/core/host.c
@@ -296,6 +296,10 @@ static inline void mmc_host_clk_sysfs_init(struct mmc_host *host)
{
}
+bool mmc_host_may_gate_card(struct mmc_card *card)
+{
+ return false;
+}
#endif
void mmc_retune_enable(struct mmc_host *host)
diff --git a/drivers/mmc/host/sdhci-msm.c b/drivers/mmc/host/sdhci-msm.c
index b2fdb19..cf48a04 100644
--- a/drivers/mmc/host/sdhci-msm.c
+++ b/drivers/mmc/host/sdhci-msm.c
@@ -2,7 +2,7 @@
* drivers/mmc/host/sdhci-msm.c - Qualcomm Technologies, Inc. MSM SDHCI Platform
* driver source file
*
- * Copyright (c) 2012-2013, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2012-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
@@ -32,17 +32,33 @@
#include <linux/scatterlist.h>
#include <linux/slab.h>
#include <linux/mmc/mmc.h>
+#include <linux/mmc/slot-gpio.h>
+#include <linux/dma-mapping.h>
+#include <linux/iopoll.h>
#include <linux/msm-bus.h>
#include "sdhci-pltfm.h"
#define SDHCI_VER_100 0x2B
+
+#define CORE_VERSION_MAJOR_MASK 0xF0000000
+#define CORE_VERSION_MAJOR_SHIFT 28
+
#define CORE_HC_MODE 0x78
#define HC_MODE_EN 0x1
+#define FF_CLK_SW_RST_DIS (1 << 13)
+
+#define CORE_GENERICS 0x70
+#define SWITCHABLE_SIGNALLING_VOL (1 << 29)
#define CORE_POWER 0x0
#define CORE_SW_RST (1 << 7)
+#define CORE_MCI_VERSION 0x050
+#define CORE_TESTBUS_CONFIG 0x0CC
+#define CORE_TESTBUS_ENA (1 << 3)
+#define CORE_SDCC_DEBUG_REG 0x124
+
#define CORE_PWRCTL_STATUS 0xDC
#define CORE_PWRCTL_MASK 0xE0
#define CORE_PWRCTL_CLEAR 0xE4
@@ -61,25 +77,83 @@
#define INT_MASK 0xF
#define MAX_PHASES 16
-#define CORE_DLL_LOCK (1 << 7)
+#define CORE_DLL_CONFIG 0x100
+#define CORE_CMD_DAT_TRACK_SEL (1 << 0)
#define CORE_DLL_EN (1 << 16)
#define CORE_CDR_EN (1 << 17)
#define CORE_CK_OUT_EN (1 << 18)
#define CORE_CDR_EXT_EN (1 << 19)
#define CORE_DLL_PDN (1 << 29)
#define CORE_DLL_RST (1 << 30)
-#define CORE_DLL_CONFIG 0x100
-#define CORE_DLL_TEST_CTL 0x104
+
#define CORE_DLL_STATUS 0x108
+#define CORE_DLL_LOCK (1 << 7)
#define CORE_VENDOR_SPEC 0x10C
#define CORE_CLK_PWRSAVE (1 << 1)
+#define CORE_HC_MCLK_SEL_DFLT (2 << 8)
+#define CORE_HC_MCLK_SEL_HS400 (3 << 8)
+#define CORE_HC_MCLK_SEL_MASK (3 << 8)
+#define CORE_HC_AUTO_CMD21_EN (1 << 6)
#define CORE_IO_PAD_PWR_SWITCH (1 << 16)
+#define CORE_HC_SELECT_IN_EN (1 << 18)
+#define CORE_HC_SELECT_IN_HS400 (6 << 19)
+#define CORE_HC_SELECT_IN_MASK (7 << 19)
+
+#define CORE_VENDOR_SPEC_CAPABILITIES0 0x11C
+#define CORE_8_BIT_SUPPORT (1 << 18)
+#define CORE_3_3V_SUPPORT (1 << 24)
+#define CORE_3_0V_SUPPORT (1 << 25)
+#define CORE_1_8V_SUPPORT (1 << 26)
+#define CORE_SYS_BUS_SUPPORT_64_BIT 28
+
+#define CORE_VENDOR_SPEC_ADMA_ERR_ADDR0 0x114
+#define CORE_VENDOR_SPEC_ADMA_ERR_ADDR1 0x118
+
+#define CORE_CSR_CDC_CTLR_CFG0 0x130
+#define CORE_SW_TRIG_FULL_CALIB (1 << 16)
+#define CORE_HW_AUTOCAL_ENA (1 << 17)
+
+#define CORE_CSR_CDC_CTLR_CFG1 0x134
+#define CORE_CSR_CDC_CAL_TIMER_CFG0 0x138
+#define CORE_TIMER_ENA (1 << 16)
+
+#define CORE_CSR_CDC_CAL_TIMER_CFG1 0x13C
+#define CORE_CSR_CDC_REFCOUNT_CFG 0x140
+#define CORE_CSR_CDC_COARSE_CAL_CFG 0x144
+#define CORE_CDC_OFFSET_CFG 0x14C
+#define CORE_CSR_CDC_DELAY_CFG 0x150
+#define CORE_CDC_SLAVE_DDA_CFG 0x160
+#define CORE_CSR_CDC_STATUS0 0x164
+#define CORE_CALIBRATION_DONE (1 << 0)
+
+#define CORE_CDC_ERROR_CODE_MASK 0x7000000
+
+#define CORE_CSR_CDC_GEN_CFG 0x178
+#define CORE_CDC_SWITCH_BYPASS_OFF (1 << 0)
+#define CORE_CDC_SWITCH_RC_EN (1 << 1)
+
+#define CORE_DDR_200_CFG 0x184
+#define CORE_CDC_T4_DLY_SEL (1 << 0)
+#define CORE_START_CDC_TRAFFIC (1 << 6)
+
+#define CORE_MCI_DATA_CNT 0x30
+#define CORE_MCI_STATUS 0x34
+#define CORE_MCI_FIFO_CNT 0x44
+
+#define CORE_TESTBUS_SEL2_BIT 4
+#define CORE_TESTBUS_SEL2 (1 << CORE_TESTBUS_SEL2_BIT)
/* 8KB descriptors */
#define SDHCI_MSM_MAX_SEGMENTS (1 << 13)
#define SDHCI_MSM_MMC_CLK_GATE_DELAY 200 /* msecs */
+#define CORE_FREQ_100MHZ (100 * 1000 * 1000)
+
+#define INVALID_TUNING_PHASE -1
+
+#define CORE_VERSION_TARGET_MASK 0x000000FF
+
static const u32 tuning_block_64[] = {
0x00FF0FFF, 0xCCC3CCFF, 0xFFCC3CC3, 0xEFFEFFFE,
0xDDFFDFFF, 0xFBFFFBFF, 0xFF7FFFBF, 0xEFBDF777,
@@ -98,6 +172,10 @@ static const u32 tuning_block_128[] = {
0xFFFFBBBB, 0xFFFF77FF, 0xFF7777FF, 0xEEDDBB77
};
+static int disable_slots;
+/* root can write, others read */
+module_param(disable_slots, int, S_IRUGO|S_IWUSR);
+
/* This structure keeps information per regulator */
struct sdhci_msm_reg_data {
/* voltage regulator handle */
@@ -165,12 +243,14 @@ struct sdhci_msm_pltfm_data {
u32 caps2;
unsigned long mmc_bus_width;
- u32 max_clk;
struct sdhci_msm_slot_reg_data *vreg_data;
bool nonremovable;
struct sdhci_msm_pin_data *pin_data;
u32 cpu_dma_latency_us;
+ int status_gpio; /* card detection GPIO that is configured as IRQ */
struct sdhci_msm_bus_voting_data *voting_data;
+ u32 *sup_clk_table;
+ unsigned char sup_clk_cnt;
};
struct sdhci_msm_bus_vote {
@@ -189,12 +269,24 @@ struct sdhci_msm_host {
struct clk *clk; /* main SD/MMC bus clock */
struct clk *pclk; /* SDHC peripheral bus clock */
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 */
atomic_t clks_on; /* Set if clocks are enabled */
struct sdhci_msm_pltfm_data *pdata;
struct mmc_host *mmc;
struct sdhci_pltfm_data sdhci_msm_pdata;
- wait_queue_head_t pwr_irq_wait;
+ u32 curr_pwr_state;
+ u32 curr_io_level;
+ struct completion pwr_irq_completion;
struct sdhci_msm_bus_vote msm_bus_vote;
+ struct device_attribute polling;
+ u32 clk_rate; /* Keeps track of current clock rate that is set */
+ bool tuning_done;
+ bool calibration_done;
+ u8 saved_tuning_phase;
+ bool en_auto_cmd21;
+ struct device_attribute auto_cmd21_attr;
+ atomic_t controller_clock;
};
enum vdd_io_level {
@@ -238,6 +330,93 @@ static inline int msm_dll_poll_ck_out_en(struct sdhci_host *host,
return rc;
}
+/*
+ * Enable CDR to track changes of DAT lines and adjust sampling
+ * point according to voltage/temperature variations
+ */
+static int msm_enable_cdr_cm_sdc4_dll(struct sdhci_host *host)
+{
+ int rc = 0;
+ u32 config;
+
+ config = readl_relaxed(host->ioaddr + CORE_DLL_CONFIG);
+ config |= CORE_CDR_EN;
+ config &= ~(CORE_CDR_EXT_EN | CORE_CK_OUT_EN);
+ writel_relaxed(config, host->ioaddr + CORE_DLL_CONFIG);
+
+ rc = msm_dll_poll_ck_out_en(host, 0);
+ if (rc)
+ goto err;
+
+ writel_relaxed((readl_relaxed(host->ioaddr + CORE_DLL_CONFIG) |
+ CORE_CK_OUT_EN), host->ioaddr + CORE_DLL_CONFIG);
+
+ rc = msm_dll_poll_ck_out_en(host, 1);
+ if (rc)
+ goto err;
+ goto out;
+err:
+ pr_err("%s: %s: failed\n", mmc_hostname(host->mmc), __func__);
+out:
+ return rc;
+}
+
+static ssize_t store_auto_cmd21(struct device *dev, struct device_attribute
+ *attr, const char *buf, size_t count)
+{
+ struct sdhci_host *host = dev_get_drvdata(dev);
+ struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+ struct sdhci_msm_host *msm_host = pltfm_host->priv;
+ u32 tmp;
+ unsigned long flags;
+
+ if (!kstrtou32(buf, 0, &tmp)) {
+ spin_lock_irqsave(&host->lock, flags);
+ msm_host->en_auto_cmd21 = !!tmp;
+ spin_unlock_irqrestore(&host->lock, flags);
+ }
+ return count;
+}
+
+static ssize_t show_auto_cmd21(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct sdhci_host *host = dev_get_drvdata(dev);
+ struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+ struct sdhci_msm_host *msm_host = pltfm_host->priv;
+
+ return snprintf(buf, PAGE_SIZE, "%d\n", msm_host->en_auto_cmd21);
+}
+
+/* MSM auto-tuning handler */
+static int sdhci_msm_config_auto_tuning_cmd(struct sdhci_host *host,
+ bool enable,
+ u32 type)
+{
+ int rc = 0;
+ struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+ struct sdhci_msm_host *msm_host = pltfm_host->priv;
+ u32 val = 0;
+
+ if (!msm_host->en_auto_cmd21)
+ return 0;
+
+ if (type == MMC_SEND_TUNING_BLOCK_HS200)
+ val = CORE_HC_AUTO_CMD21_EN;
+ else
+ return 0;
+
+ if (enable) {
+ rc = msm_enable_cdr_cm_sdc4_dll(host);
+ writel_relaxed(readl_relaxed(host->ioaddr + CORE_VENDOR_SPEC) |
+ val, host->ioaddr + CORE_VENDOR_SPEC);
+ } else {
+ writel_relaxed(readl_relaxed(host->ioaddr + CORE_VENDOR_SPEC) &
+ ~val, host->ioaddr + CORE_VENDOR_SPEC);
+ }
+ return rc;
+}
+
static int msm_config_cm_dll_phase(struct sdhci_host *host, u8 phase)
{
int rc = 0;
@@ -298,8 +477,8 @@ static int msm_config_cm_dll_phase(struct sdhci_host *host, u8 phase)
* Find out the greatest range of consecuitive selected
* DLL clock output phases that can be used as sampling
* setting for SD3.0 UHS-I card read operation (in SDR104
- * timing mode) or for eMMC4.5 card read operation (in HS200
- * timing mode).
+ * timing mode) or for eMMC4.5 card read operation (in
+ * HS400/HS200 timing mode).
* Select the 3/4 of the range and configure the DLL with the
* selected DLL clock output phase.
*/
@@ -442,19 +621,25 @@ static int msm_init_cm_dll(struct sdhci_host *host)
int rc = 0;
unsigned long flags;
u32 wait_cnt;
+ bool prev_pwrsave, curr_pwrsave;
pr_debug("%s: Enter %s\n", mmc_hostname(mmc), __func__);
spin_lock_irqsave(&host->lock, flags);
-
+ prev_pwrsave = !!(readl_relaxed(host->ioaddr + CORE_VENDOR_SPEC) &
+ CORE_CLK_PWRSAVE);
+ curr_pwrsave = prev_pwrsave;
/*
* Make sure that clock is always enabled when DLL
* tuning is in progress. Keeping PWRSAVE ON may
* turn off the clock. So let's disable the PWRSAVE
* here and re-enable it once tuning is completed.
*/
- writel_relaxed((readl_relaxed(host->ioaddr + CORE_VENDOR_SPEC)
- & ~CORE_CLK_PWRSAVE),
- host->ioaddr + CORE_VENDOR_SPEC);
+ if (prev_pwrsave) {
+ writel_relaxed((readl_relaxed(host->ioaddr + CORE_VENDOR_SPEC)
+ & ~CORE_CLK_PWRSAVE),
+ host->ioaddr + CORE_VENDOR_SPEC);
+ curr_pwrsave = false;
+ }
/* Write 1 to DLL_RST bit of DLL_CONFIG register */
writel_relaxed((readl_relaxed(host->ioaddr + CORE_DLL_CONFIG)
@@ -497,26 +682,190 @@ static int msm_init_cm_dll(struct sdhci_host *host)
}
out:
- /* re-enable PWRSAVE */
- writel_relaxed((readl_relaxed(host->ioaddr + CORE_VENDOR_SPEC) |
- CORE_CLK_PWRSAVE),
- host->ioaddr + CORE_VENDOR_SPEC);
+ /* Restore the correct PWRSAVE state */
+ if (prev_pwrsave ^ curr_pwrsave) {
+ u32 reg = readl_relaxed(host->ioaddr + CORE_VENDOR_SPEC);
+
+ if (prev_pwrsave)
+ reg |= CORE_CLK_PWRSAVE;
+ else
+ reg &= ~CORE_CLK_PWRSAVE;
+
+ writel_relaxed(reg, host->ioaddr + CORE_VENDOR_SPEC);
+ }
+
spin_unlock_irqrestore(&host->lock, flags);
pr_debug("%s: Exit %s\n", mmc_hostname(mmc), __func__);
return rc;
}
+static int sdhci_msm_cdclp533_calibration(struct sdhci_host *host)
+{
+ u32 wait_cnt;
+ int ret = 0;
+ int cdc_err = 0;
+ struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+ struct sdhci_msm_host *msm_host = pltfm_host->priv;
+
+ pr_debug("%s: Enter %s\n", mmc_hostname(host->mmc), __func__);
+
+ /*
+ * Retuning in HS400 (DDR mode) will fail, just reset the
+ * tuning block and restore the saved tuning phase.
+ */
+ ret = msm_init_cm_dll(host);
+ if (ret)
+ goto out;
+
+ /* Set the selected phase in delay line hw block */
+ ret = msm_config_cm_dll_phase(host, msm_host->saved_tuning_phase);
+ if (ret)
+ goto out;
+
+ /* Write 1 to CMD_DAT_TRACK_SEL field in DLL_CONFIG */
+ writel_relaxed((readl_relaxed(host->ioaddr + CORE_DLL_CONFIG)
+ | CORE_CMD_DAT_TRACK_SEL),
+ host->ioaddr + CORE_DLL_CONFIG);
+
+ /* Write 0 to CDC_T4_DLY_SEL field in VENDOR_SPEC_DDR200_CFG */
+ writel_relaxed((readl_relaxed(host->ioaddr + CORE_DDR_200_CFG)
+ & ~CORE_CDC_T4_DLY_SEL),
+ host->ioaddr + CORE_DDR_200_CFG);
+
+ /* Write 0 to CDC_SWITCH_BYPASS_OFF field in CORE_CSR_CDC_GEN_CFG */
+ writel_relaxed((readl_relaxed(host->ioaddr + CORE_CSR_CDC_GEN_CFG)
+ & ~CORE_CDC_SWITCH_BYPASS_OFF),
+ host->ioaddr + CORE_CSR_CDC_GEN_CFG);
+
+ /* Write 1 to CDC_SWITCH_RC_EN field in CORE_CSR_CDC_GEN_CFG */
+ writel_relaxed((readl_relaxed(host->ioaddr + CORE_CSR_CDC_GEN_CFG)
+ | CORE_CDC_SWITCH_RC_EN),
+ host->ioaddr + CORE_CSR_CDC_GEN_CFG);
+
+ /* Write 0 to START_CDC_TRAFFIC field in CORE_DDR200_CFG */
+ writel_relaxed((readl_relaxed(host->ioaddr + CORE_DDR_200_CFG)
+ & ~CORE_START_CDC_TRAFFIC),
+ host->ioaddr + CORE_DDR_200_CFG);
+
+ /*
+ * Perform CDC Register Initialization Sequence
+ *
+ * CORE_CSR_CDC_CTLR_CFG0 0x11800EC
+ * CORE_CSR_CDC_CTLR_CFG1 0x3011111
+ * CORE_CSR_CDC_CAL_TIMER_CFG0 0x1201000
+ * CORE_CSR_CDC_CAL_TIMER_CFG1 0x4
+ * CORE_CSR_CDC_REFCOUNT_CFG 0xCB732020
+ * CORE_CSR_CDC_COARSE_CAL_CFG 0xB19
+ * CORE_CSR_CDC_DELAY_CFG 0x3AC
+ * CORE_CDC_OFFSET_CFG 0x0
+ * CORE_CDC_SLAVE_DDA_CFG 0x16334
+ */
+
+ writel_relaxed(0x11800EC, host->ioaddr + CORE_CSR_CDC_CTLR_CFG0);
+ writel_relaxed(0x3011111, host->ioaddr + CORE_CSR_CDC_CTLR_CFG1);
+ writel_relaxed(0x1201000, host->ioaddr + CORE_CSR_CDC_CAL_TIMER_CFG0);
+ writel_relaxed(0x4, host->ioaddr + CORE_CSR_CDC_CAL_TIMER_CFG1);
+ writel_relaxed(0xCB732020, host->ioaddr + CORE_CSR_CDC_REFCOUNT_CFG);
+ writel_relaxed(0xB19, host->ioaddr + CORE_CSR_CDC_COARSE_CAL_CFG);
+ writel_relaxed(0x3AC, host->ioaddr + CORE_CSR_CDC_DELAY_CFG);
+ writel_relaxed(0x0, host->ioaddr + CORE_CDC_OFFSET_CFG);
+ writel_relaxed(0x16334, host->ioaddr + CORE_CDC_SLAVE_DDA_CFG);
+
+ /* CDC HW Calibration */
+
+ /* Write 1 to SW_TRIG_FULL_CALIB field in CORE_CSR_CDC_CTLR_CFG0 */
+ writel_relaxed((readl_relaxed(host->ioaddr + CORE_CSR_CDC_CTLR_CFG0)
+ | CORE_SW_TRIG_FULL_CALIB),
+ host->ioaddr + CORE_CSR_CDC_CTLR_CFG0);
+
+ /* Write 0 to SW_TRIG_FULL_CALIB field in CORE_CSR_CDC_CTLR_CFG0 */
+ writel_relaxed((readl_relaxed(host->ioaddr + CORE_CSR_CDC_CTLR_CFG0)
+ & ~CORE_SW_TRIG_FULL_CALIB),
+ host->ioaddr + CORE_CSR_CDC_CTLR_CFG0);
+
+ /* Write 1 to HW_AUTOCAL_ENA field in CORE_CSR_CDC_CTLR_CFG0 */
+ writel_relaxed((readl_relaxed(host->ioaddr + CORE_CSR_CDC_CTLR_CFG0)
+ | CORE_HW_AUTOCAL_ENA),
+ host->ioaddr + CORE_CSR_CDC_CTLR_CFG0);
+
+ /* Write 1 to TIMER_ENA field in CORE_CSR_CDC_CAL_TIMER_CFG0 */
+ writel_relaxed((readl_relaxed(host->ioaddr +
+ CORE_CSR_CDC_CAL_TIMER_CFG0) | CORE_TIMER_ENA),
+ host->ioaddr + CORE_CSR_CDC_CAL_TIMER_CFG0);
+
+ mb();
+
+ /* Poll on CALIBRATION_DONE field in CORE_CSR_CDC_STATUS0 to be 1 */
+ wait_cnt = 50;
+ while (!(readl_relaxed(host->ioaddr + CORE_CSR_CDC_STATUS0)
+ & CORE_CALIBRATION_DONE)) {
+ /* max. wait for 50us sec for CALIBRATION_DONE bit to be set */
+ if (--wait_cnt == 0) {
+ pr_err("%s: %s: CDC Calibration was not completed\n",
+ mmc_hostname(host->mmc), __func__);
+ ret = -ETIMEDOUT;
+ goto out;
+ }
+ /* wait for 1us before polling again */
+ udelay(1);
+ }
+
+ /* Verify CDC_ERROR_CODE field in CORE_CSR_CDC_STATUS0 is 0 */
+ cdc_err = readl_relaxed(host->ioaddr + CORE_CSR_CDC_STATUS0)
+ & CORE_CDC_ERROR_CODE_MASK;
+ if (cdc_err) {
+ pr_err("%s: %s: CDC Error Code %d\n",
+ mmc_hostname(host->mmc), __func__, cdc_err);
+ ret = -EINVAL;
+ goto out;
+ }
+
+ /* Write 1 to START_CDC_TRAFFIC field in CORE_DDR200_CFG */
+ writel_relaxed((readl_relaxed(host->ioaddr + CORE_DDR_200_CFG)
+ | CORE_START_CDC_TRAFFIC),
+ host->ioaddr + CORE_DDR_200_CFG);
+out:
+ pr_debug("%s: Exit %s, ret:%d\n", mmc_hostname(host->mmc),
+ __func__, ret);
+ return ret;
+}
+
int sdhci_msm_execute_tuning(struct sdhci_host *host, u32 opcode)
{
unsigned long flags;
+ int tuning_seq_cnt = 3;
u8 phase, *data_buf, tuned_phases[16], tuned_phase_cnt = 0;
const u32 *tuning_block_pattern = tuning_block_64;
int size = sizeof(tuning_block_64); /* Tuning pattern size in bytes */
int rc;
struct mmc_host *mmc = host->mmc;
+ struct mmc_ios ios = host->mmc->ios;
+ struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+ struct sdhci_msm_host *msm_host = pltfm_host->priv;
+
+ /*
+ * Tuning is required for SDR104, HS200 and HS400 cards and
+ * if clock frequency is greater than 100MHz in these modes.
+ */
+ if (host->clock <= CORE_FREQ_100MHZ ||
+ !((ios.timing == MMC_TIMING_MMC_HS400) ||
+ (ios.timing == MMC_TIMING_MMC_HS200) ||
+ (ios.timing == MMC_TIMING_UHS_SDR104)))
+ return 0;
pr_debug("%s: Enter %s\n", mmc_hostname(mmc), __func__);
- /* Tuning is only required for SDR104 modes */
+
+ /* CDCLP533 HW calibration is only required for HS400 mode*/
+ if (msm_host->tuning_done && !msm_host->calibration_done &&
+ (mmc->ios.timing == MMC_TIMING_MMC_HS400)) {
+ rc = sdhci_msm_cdclp533_calibration(host);
+ spin_lock_irqsave(&host->lock, flags);
+ if (!rc)
+ msm_host->calibration_done = true;
+ spin_unlock_irqrestore(&host->lock, flags);
+ goto out;
+ }
+
spin_lock_irqsave(&host->lock, flags);
if ((opcode == MMC_SEND_TUNING_BLOCK_HS200) &&
@@ -526,17 +875,18 @@ int sdhci_msm_execute_tuning(struct sdhci_host *host, u32 opcode)
}
spin_unlock_irqrestore(&host->lock, flags);
- /* first of all reset the tuning block */
- rc = msm_init_cm_dll(host);
- if (rc)
- goto out;
-
data_buf = kmalloc(size, GFP_KERNEL);
if (!data_buf) {
rc = -ENOMEM;
goto out;
}
+retry:
+ /* first of all reset the tuning block */
+ rc = msm_init_cm_dll(host);
+ if (rc)
+ goto kfree;
+
phase = 0;
do {
struct mmc_command cmd = {0};
@@ -590,19 +940,26 @@ int sdhci_msm_execute_tuning(struct sdhci_host *host, u32 opcode)
rc = msm_config_cm_dll_phase(host, phase);
if (rc)
goto kfree;
+ msm_host->saved_tuning_phase = phase;
pr_debug("%s: %s: finally setting the tuning phase to %d\n",
mmc_hostname(mmc), __func__, phase);
} else {
+ if (--tuning_seq_cnt)
+ goto retry;
/* tuning failed */
pr_err("%s: %s: no tuning point found\n",
mmc_hostname(mmc), __func__);
- rc = -EAGAIN;
+ rc = -EIO;
}
kfree:
kfree(data_buf);
out:
- pr_debug("%s: Exit %s\n", mmc_hostname(mmc), __func__);
+ spin_lock_irqsave(&host->lock, flags);
+ if (!rc)
+ msm_host->tuning_done = true;
+ spin_unlock_irqrestore(&host->lock, flags);
+ pr_debug("%s: Exit %s, err(%d)\n", mmc_hostname(mmc), __func__, rc);
return rc;
}
@@ -671,7 +1028,7 @@ static int sdhci_msm_dt_get_array(struct device *dev, const char *prop_name,
goto out;
}
sz = *len = *len / sizeof(*arr);
- if (sz <= 0 || (size > 0 && (sz != size))) {
+ if (sz <= 0 || (size > 0 && (sz > size))) {
dev_err(dev, "%s invalid size\n", prop_name);
ret = -EINVAL;
goto out;
@@ -708,8 +1065,7 @@ static int sdhci_msm_dt_parse_vreg_info(struct device *dev,
snprintf(prop_name, MAX_PROP_SIZE, "%s-supply", vreg_name);
if (!of_parse_phandle(np, prop_name, 0)) {
- dev_err(dev, "No vreg data found for %s\n", vreg_name);
- ret = -EINVAL;
+ dev_info(dev, "No vreg data found for %s\n", vreg_name);
return ret;
}
@@ -828,6 +1184,9 @@ static struct sdhci_msm_pltfm_data *sdhci_msm_populate_pdata(struct device *dev)
u32 bus_width = 0;
u32 cpu_dma_latency;
int len, i;
+ int clk_table_len;
+ u32 *clk_table = NULL;
+ enum of_gpio_flags flags = OF_GPIO_ACTIVE_LOW;
pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL);
if (!pdata) {
@@ -835,6 +1194,10 @@ static struct sdhci_msm_pltfm_data *sdhci_msm_populate_pdata(struct device *dev)
goto out;
}
+ pdata->status_gpio = of_get_named_gpio_flags(np, "cd-gpios", 0, &flags);
+ if (gpio_is_valid(pdata->status_gpio) & !(flags & OF_GPIO_ACTIVE_LOW))
+ pdata->caps2 |= MMC_CAP2_CD_ACTIVE_HIGH;
+
of_property_read_u32(np, "qcom,bus-width", &bus_width);
if (bus_width == 8)
pdata->mmc_bus_width = MMC_CAP_8_BIT_DATA;
@@ -849,6 +1212,18 @@ static struct sdhci_msm_pltfm_data *sdhci_msm_populate_pdata(struct device *dev)
&cpu_dma_latency))
pdata->cpu_dma_latency_us = cpu_dma_latency;
+ if (sdhci_msm_dt_get_array(dev, "qcom,clk-rates",
+ &clk_table, &clk_table_len, 0)) {
+ dev_err(dev, "failed parsing supported clock rates\n");
+ goto out;
+ }
+ if (!clk_table || !clk_table_len) {
+ dev_err(dev, "Invalid clock table\n");
+ goto out;
+ }
+ pdata->sup_clk_table = clk_table;
+ pdata->sup_clk_cnt = clk_table_len;
+
pdata->vreg_data = devm_kzalloc(dev, sizeof(struct
sdhci_msm_slot_reg_data),
GFP_KERNEL);
@@ -874,8 +1249,6 @@ static struct sdhci_msm_pltfm_data *sdhci_msm_populate_pdata(struct device *dev)
goto out;
}
- of_property_read_u32(np, "qcom,max-clk-rate", &pdata->max_clk);
-
len = of_property_count_strings(np, "qcom,bus-speed-mode");
for (i = 0; i < len; i++) {
@@ -886,7 +1259,11 @@ static struct sdhci_msm_pltfm_data *sdhci_msm_populate_pdata(struct device *dev)
if (!name)
continue;
- if (!strncmp(name, "HS200_1p8v", sizeof("HS200_1p8v")))
+ if (!strncmp(name, "HS400_1p8v", sizeof("HS400_1p8v")))
+ pdata->caps2 |= MMC_CAP2_HS400_1_8V;
+ else if (!strncmp(name, "HS400_1p2v", sizeof("HS400_1p2v")))
+ pdata->caps2 |= MMC_CAP2_HS400_1_2V;
+ else if (!strncmp(name, "HS200_1p8v", sizeof("HS200_1p8v")))
pdata->caps2 |= MMC_CAP2_HS200_1_8V_SDR;
else if (!strncmp(name, "HS200_1p2v", sizeof("HS200_1p2v")))
pdata->caps2 |= MMC_CAP2_HS200_1_2V_SDR;
@@ -910,9 +1287,12 @@ static struct sdhci_msm_pltfm_data *sdhci_msm_populate_pdata(struct device *dev)
static unsigned int sdhci_get_bw_required(struct sdhci_host *host,
struct mmc_ios *ios)
{
+ struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+ struct sdhci_msm_host *msm_host = pltfm_host->priv;
+
unsigned int bw;
- bw = host->clock;
+ bw = msm_host->clk_rate;
/*
* For DDR mode, SDCC controller clock will be at
* the double rate than the actual clock that goes to card.
@@ -953,16 +1333,18 @@ static int sdhci_msm_bus_get_vote_for_bw(struct sdhci_msm_host *host,
*/
static inline int sdhci_msm_bus_set_vote(struct sdhci_msm_host *msm_host,
int vote,
- unsigned long flags)
+ unsigned long *flags)
{
struct sdhci_host *host = platform_get_drvdata(msm_host->pdev);
int rc = 0;
+ BUG_ON(!flags);
+
if (vote != msm_host->msm_bus_vote.curr_vote) {
- spin_unlock_irqrestore(&host->lock, flags);
+ spin_unlock_irqrestore(&host->lock, *flags);
rc = msm_bus_scale_client_update_request(
msm_host->msm_bus_vote.client_handle, vote);
- spin_lock_irqsave(&host->lock, flags);
+ spin_lock_irqsave(&host->lock, *flags);
if (rc) {
pr_err("%s: msm_bus_scale_client_update_request() failed: bus_client_handle=0x%x, vote=%d, err=%d\n",
mmc_hostname(host->mmc),
@@ -995,7 +1377,7 @@ static void sdhci_msm_bus_work(struct work_struct *work)
/* don't vote for 0 bandwidth if any request is in progress */
if (!host->mrq) {
sdhci_msm_bus_set_vote(msm_host,
- msm_host->msm_bus_vote.min_bw_vote, flags);
+ msm_host->msm_bus_vote.min_bw_vote, &flags);
} else
pr_warning("%s: %s: Transfer in progress. skipping bus voting to 0 bandwidth\n",
mmc_hostname(host->mmc), __func__);
@@ -1017,7 +1399,7 @@ static void sdhci_msm_bus_cancel_work_and_set_vote(struct sdhci_host *host,
cancel_delayed_work_sync(&msm_host->msm_bus_vote.vote_work);
spin_lock_irqsave(&host->lock, flags);
vote = sdhci_msm_bus_get_vote_for_bw(msm_host, bw);
- sdhci_msm_bus_set_vote(msm_host, vote, flags);
+ sdhci_msm_bus_set_vote(msm_host, vote, &flags);
spin_unlock_irqrestore(&host->lock, flags);
}
@@ -1112,10 +1494,22 @@ static void sdhci_msm_bus_voting(struct sdhci_host *host, u32 enable)
return;
bw = sdhci_get_bw_required(host, ios);
- if (enable)
+ if (enable) {
sdhci_msm_bus_cancel_work_and_set_vote(host, bw);
- else
- sdhci_msm_bus_queue_work(host);
+ } else {
+ /*
+ * If clock gating is enabled, then remove the vote
+ * immediately because clocks will be disabled only
+ * after SDHCI_MSM_MMC_CLK_GATE_DELAY and thus no
+ * additional delay is required to remove the bus vote.
+ */
+#ifdef CONFIG_MMC_CLKGATE
+ if (host->mmc->clkgate_delay)
+ sdhci_msm_bus_cancel_work_and_set_vote(host, 0);
+ else
+#endif
+ sdhci_msm_bus_queue_work(host);
+ }
}
/* Regulator utility functions */
@@ -1137,11 +1531,14 @@ static int sdhci_msm_vreg_init_reg(struct device *dev,
goto out;
}
- /* sanity check */
- if (!vreg->high_vol_level || !vreg->hpm_uA) {
- pr_err("%s: %s invalid constraints specified\n",
- __func__, vreg->name);
- ret = -EINVAL;
+ if (regulator_count_voltages(vreg->reg) > 0) {
+ vreg->set_voltage_sup = true;
+ /* sanity check */
+ if (!vreg->high_vol_level || !vreg->hpm_uA) {
+ pr_err("%s: %s invalid constraints specified\n",
+ __func__, vreg->name);
+ ret = -EINVAL;
+ }
}
out:
@@ -1182,12 +1579,13 @@ static int sdhci_msm_vreg_set_voltage(struct sdhci_msm_reg_data *vreg,
int min_uV, int max_uV)
{
int ret = 0;
-
- ret = regulator_set_voltage(vreg->reg, min_uV, max_uV);
- if (ret) {
- pr_err("%s: regulator_set_voltage(%s)failed. min_uV=%d,max_uV=%d,ret=%d\n",
+ if (vreg->set_voltage_sup) {
+ ret = regulator_set_voltage(vreg->reg, min_uV, max_uV);
+ if (ret) {
+ pr_err("%s: regulator_set_voltage(%s)failed. min_uV=%d,max_uV=%d,ret=%d\n",
__func__, vreg->name, min_uV, max_uV, ret);
}
+ }
return ret;
}
@@ -1392,6 +1790,8 @@ static irqreturn_t sdhci_msm_pwr_irq(int irq, void *data)
u8 irq_status = 0;
u8 irq_ack = 0;
int ret = 0;
+ int pwr_state = 0, io_level = 0;
+ unsigned long flags;
irq_status = readb_relaxed(msm_host->core_mem + CORE_PWRCTL_STATUS);
pr_debug("%s: Received IRQ(%d), status=0x%x\n",
@@ -1410,21 +1810,33 @@ static irqreturn_t sdhci_msm_pwr_irq(int irq, void *data)
/* Handle BUS ON/OFF*/
if (irq_status & CORE_PWRCTL_BUS_ON) {
ret = sdhci_msm_setup_vreg(msm_host->pdata, true, false);
- if (!ret)
+ if (!ret) {
ret = sdhci_msm_setup_pins(msm_host->pdata, true);
+ ret |= sdhci_msm_set_vdd_io_vol(msm_host->pdata,
+ VDD_IO_HIGH, 0);
+ }
if (ret)
irq_ack |= CORE_PWRCTL_BUS_FAIL;
else
irq_ack |= CORE_PWRCTL_BUS_SUCCESS;
+
+ pwr_state = REQ_BUS_ON;
+ io_level = REQ_IO_HIGH;
}
if (irq_status & CORE_PWRCTL_BUS_OFF) {
ret = sdhci_msm_setup_vreg(msm_host->pdata, false, false);
- if (!ret)
+ if (!ret) {
ret = sdhci_msm_setup_pins(msm_host->pdata, false);
+ ret |= sdhci_msm_set_vdd_io_vol(msm_host->pdata,
+ VDD_IO_LOW, 0);
+ }
if (ret)
irq_ack |= CORE_PWRCTL_BUS_FAIL;
else
irq_ack |= CORE_PWRCTL_BUS_SUCCESS;
+
+ pwr_state = REQ_BUS_OFF;
+ io_level = REQ_IO_LOW;
}
/* Handle IO LOW/HIGH */
if (irq_status & CORE_PWRCTL_IO_LOW) {
@@ -1434,6 +1846,8 @@ static irqreturn_t sdhci_msm_pwr_irq(int irq, void *data)
irq_ack |= CORE_PWRCTL_IO_FAIL;
else
irq_ack |= CORE_PWRCTL_IO_SUCCESS;
+
+ io_level = REQ_IO_LOW;
}
if (irq_status & CORE_PWRCTL_IO_HIGH) {
/* Switch voltage High */
@@ -1442,6 +1856,8 @@ static irqreturn_t sdhci_msm_pwr_irq(int irq, void *data)
irq_ack |= CORE_PWRCTL_IO_FAIL;
else
irq_ack |= CORE_PWRCTL_IO_SUCCESS;
+
+ io_level = REQ_IO_HIGH;
}
/* ACK status to the core */
@@ -1454,11 +1870,11 @@ static irqreturn_t sdhci_msm_pwr_irq(int irq, void *data)
*/
mb();
- if (irq_status & CORE_PWRCTL_IO_HIGH)
+ if (io_level & REQ_IO_HIGH)
writel_relaxed((readl_relaxed(host->ioaddr + CORE_VENDOR_SPEC) &
~CORE_IO_PAD_PWR_SWITCH),
host->ioaddr + CORE_VENDOR_SPEC);
- if (irq_status & CORE_PWRCTL_IO_LOW)
+ else if (io_level & REQ_IO_LOW)
writel_relaxed((readl_relaxed(host->ioaddr + CORE_VENDOR_SPEC) |
CORE_IO_PAD_PWR_SWITCH),
host->ioaddr + CORE_VENDOR_SPEC);
@@ -1466,11 +1882,53 @@ static irqreturn_t sdhci_msm_pwr_irq(int irq, void *data)
pr_debug("%s: Handled IRQ(%d), ret=%d, ack=0x%x\n",
mmc_hostname(msm_host->mmc), irq, ret, irq_ack);
- wake_up_interruptible(&msm_host->pwr_irq_wait);
+ spin_lock_irqsave(&host->lock, flags);
+ if (pwr_state)
+ msm_host->curr_pwr_state = pwr_state;
+ if (io_level)
+ msm_host->curr_io_level = io_level;
+ complete(&msm_host->pwr_irq_completion);
+ spin_unlock_irqrestore(&host->lock, flags);
+
return IRQ_HANDLED;
}
static ssize_t
+show_polling(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct sdhci_host *host = dev_get_drvdata(dev);
+ int poll;
+ unsigned long flags;
+
+ spin_lock_irqsave(&host->lock, flags);
+ poll = !!(host->mmc->caps & MMC_CAP_NEEDS_POLL);
+ spin_unlock_irqrestore(&host->lock, flags);
+
+ return snprintf(buf, PAGE_SIZE, "%d\n", poll);
+}
+
+static ssize_t
+store_polling(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct sdhci_host *host = dev_get_drvdata(dev);
+ int value;
+ unsigned long flags;
+
+ if (!kstrtou32(buf, 0, &value)) {
+ spin_lock_irqsave(&host->lock, flags);
+ if (value) {
+ host->mmc->caps |= MMC_CAP_NEEDS_POLL;
+ mmc_detect_change(host->mmc, 0);
+ } else {
+ host->mmc->caps &= ~MMC_CAP_NEEDS_POLL;
+ }
+ spin_unlock_irqrestore(&host->lock, flags);
+ }
+ return count;
+}
+
+static ssize_t
show_sdhci_max_bus_bw(struct device *dev, struct device_attribute *attr,
char *buf)
{
@@ -1500,25 +1958,61 @@ store_sdhci_max_bus_bw(struct device *dev, struct device_attribute *attr,
return count;
}
-static void sdhci_msm_check_power_status(struct sdhci_host *host)
+static void sdhci_msm_check_power_status(struct sdhci_host *host, u32 req_type)
{
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
struct sdhci_msm_host *msm_host = pltfm_host->priv;
- int ret = 0;
+ unsigned long flags;
+ bool done = false;
+ u32 io_sig_sts;
- pr_debug("%s: %s: power status before waiting 0x%x\n",
- mmc_hostname(host->mmc), __func__,
- readb_relaxed(msm_host->core_mem + CORE_PWRCTL_CTL));
+ spin_lock_irqsave(&host->lock, flags);
+ pr_debug("%s: %s: request %d curr_pwr_state %x curr_io_level %x\n",
+ mmc_hostname(host->mmc), __func__, req_type,
+ msm_host->curr_pwr_state, msm_host->curr_io_level);
+ io_sig_sts = readl_relaxed(msm_host->core_mem + CORE_GENERICS);
+ /*
+ * The IRQ for request type IO High/Low will be generated when -
+ * 1. SWITCHABLE_SIGNALLING_VOL is enabled in HW.
+ * 2. If 1 is true and when there is a state change in 1.8V enable
+ * bit (bit 3) of SDHCI_HOST_CONTROL2 register. The reset state of
+ * that bit is 0 which indicates 3.3V IO voltage. So, when MMC core
+ * layer tries to set it to 3.3V before card detection happens, the
+ * IRQ doesn't get triggered as there is no state change in this bit.
+ * The driver already handles this case by changing the IO voltage
+ * level to high as part of controller power up sequence. Hence, check
+ * for host->pwr to handle a case where IO voltage high request is
+ * issued even before controller power up.
+ */
+ if (req_type & (REQ_IO_HIGH | REQ_IO_LOW)) {
+ if (!(io_sig_sts & SWITCHABLE_SIGNALLING_VOL) ||
+ ((req_type & REQ_IO_HIGH) && !host->pwr)) {
+ pr_debug("%s: do not wait for power IRQ that never comes\n",
+ mmc_hostname(host->mmc));
+ spin_unlock_irqrestore(&host->lock, flags);
+ return;
+ }
+ }
- ret = wait_event_interruptible(msm_host->pwr_irq_wait,
- (readb_relaxed(msm_host->core_mem +
- CORE_PWRCTL_CTL)) != 0x0);
- if (ret)
- pr_warning("%s: %s: returned due to error %d\n",
- mmc_hostname(host->mmc), __func__, ret);
- pr_debug("%s: %s: ret %d power status after handling power IRQ 0x%x\n",
- mmc_hostname(host->mmc), __func__, ret,
- readb_relaxed(msm_host->core_mem + CORE_PWRCTL_CTL));
+ if ((req_type & msm_host->curr_pwr_state) ||
+ (req_type & msm_host->curr_io_level))
+ done = true;
+ spin_unlock_irqrestore(&host->lock, flags);
+
+ /*
+ * This is needed here to hanlde a case where IRQ gets
+ * triggered even before this function is called so that
+ * x->done counter of completion gets reset. Otherwise,
+ * next call to wait_for_completion returns immediately
+ * without actually waiting for the IRQ to be handled.
+ */
+ if (done)
+ init_completion(&msm_host->pwr_irq_completion);
+ else
+ wait_for_completion(&msm_host->pwr_irq_completion);
+
+ pr_debug("%s: %s: request %d done\n", mmc_hostname(host->mmc),
+ __func__, req_type);
}
static void sdhci_msm_toggle_cdr(struct sdhci_host *host, bool enable)
@@ -1538,76 +2032,485 @@ static unsigned int sdhci_msm_max_segs(void)
return SDHCI_MSM_MAX_SEGMENTS;
}
-void sdhci_msm_set_clock(struct sdhci_host *host, unsigned int clock)
+static unsigned int sdhci_msm_get_min_clock(struct sdhci_host *host)
{
- int rc;
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
struct sdhci_msm_host *msm_host = pltfm_host->priv;
- unsigned long flags;
- if (clock && !atomic_read(&msm_host->clks_on)) {
- pr_debug("%s: request to enable clock at rate %u\n",
- mmc_hostname(host->mmc), clock);
+ return msm_host->pdata->sup_clk_table[0];
+}
+
+static unsigned int sdhci_msm_get_max_clock(struct sdhci_host *host)
+{
+ struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+ struct sdhci_msm_host *msm_host = pltfm_host->priv;
+ int max_clk_index = msm_host->pdata->sup_clk_cnt;
+
+ return msm_host->pdata->sup_clk_table[max_clk_index - 1];
+}
+
+static unsigned int sdhci_msm_get_sup_clk_rate(struct sdhci_host *host,
+ u32 req_clk)
+{
+ struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+ struct sdhci_msm_host *msm_host = pltfm_host->priv;
+ unsigned int sel_clk = -1;
+ unsigned char cnt;
+
+ if (req_clk < sdhci_msm_get_min_clock(host)) {
+ sel_clk = sdhci_msm_get_min_clock(host);
+ return sel_clk;
+ }
+
+ for (cnt = 0; cnt < msm_host->pdata->sup_clk_cnt; cnt++) {
+ if (msm_host->pdata->sup_clk_table[cnt] > req_clk) {
+ break;
+ } else if (msm_host->pdata->sup_clk_table[cnt] == req_clk) {
+ sel_clk = msm_host->pdata->sup_clk_table[cnt];
+ break;
+ } else {
+ sel_clk = msm_host->pdata->sup_clk_table[cnt];
+ }
+ }
+ return sel_clk;
+}
+
+static int sdhci_msm_enable_controller_clock(struct sdhci_host *host)
+{
+ struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+ struct sdhci_msm_host *msm_host = pltfm_host->priv;
+ int rc = 0;
+
+ if (atomic_read(&msm_host->controller_clock))
+ return 0;
+
+ sdhci_msm_bus_voting(host, 1);
+
+ if (!IS_ERR(msm_host->pclk)) {
+ rc = clk_prepare_enable(msm_host->pclk);
+ if (rc) {
+ pr_err("%s: %s: failed to enable the pclk with error %d\n",
+ mmc_hostname(host->mmc), __func__, rc);
+ goto remove_vote;
+ }
+ }
+
+ 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;
+ }
+
+ atomic_set(&msm_host->controller_clock, 1);
+ pr_debug("%s: %s: enabled controller clock\n",
+ mmc_hostname(host->mmc), __func__);
+ goto out;
+
+disable_pclk:
+ if (!IS_ERR(msm_host->pclk))
+ clk_disable_unprepare(msm_host->pclk);
+remove_vote:
+ if (msm_host->msm_bus_vote.client_handle)
+ sdhci_msm_bus_cancel_work_and_set_vote(host, 0);
+out:
+ return rc;
+}
+
+
+
+static int sdhci_msm_prepare_clocks(struct sdhci_host *host, bool enable)
+{
+ struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+ struct sdhci_msm_host *msm_host = pltfm_host->priv;
+ int rc = 0;
+
+ if (enable && !atomic_read(&msm_host->clks_on)) {
+ pr_debug("%s: request to enable clocks\n",
+ mmc_hostname(host->mmc));
+
+ /*
+ * The bus-width or the clock rate might have changed
+ * after controller clocks are enbaled, update bus vote
+ * in such case.
+ */
+ if (atomic_read(&msm_host->controller_clock))
+ sdhci_msm_bus_voting(host, 1);
+
+ rc = sdhci_msm_enable_controller_clock(host);
+ if (rc)
+ goto remove_vote;
+
if (!IS_ERR_OR_NULL(msm_host->bus_clk)) {
rc = clk_prepare_enable(msm_host->bus_clk);
if (rc) {
pr_err("%s: %s: failed to enable the bus-clock with error %d\n",
mmc_hostname(host->mmc), __func__, rc);
- goto out;
+ goto disable_controller_clk;
}
}
- if (!IS_ERR(msm_host->pclk)) {
- rc = clk_prepare_enable(msm_host->pclk);
+ if (!IS_ERR(msm_host->ff_clk)) {
+ rc = clk_prepare_enable(msm_host->ff_clk);
if (rc) {
- pr_err("%s: %s: failed to enable the pclk with error %d\n",
+ pr_err("%s: %s: failed to enable the ff_clk with error %d\n",
mmc_hostname(host->mmc), __func__, rc);
goto disable_bus_clk;
}
}
- 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;
+ if (!IS_ERR(msm_host->sleep_clk)) {
+ rc = clk_prepare_enable(msm_host->sleep_clk);
+ if (rc) {
+ pr_err("%s: %s: failed to enable the sleep_clk with error %d\n",
+ mmc_hostname(host->mmc), __func__, rc);
+ goto disable_ff_clk;
+ }
}
mb();
- atomic_set(&msm_host->clks_on, 1);
- } else if (!clock && atomic_read(&msm_host->clks_on)) {
- pr_debug("%s: request to disable clocks\n",
- mmc_hostname(host->mmc));
+ } else if (!enable && atomic_read(&msm_host->clks_on)) {
sdhci_writew(host, 0, SDHCI_CLOCK_CONTROL);
mb();
+ /*
+ * During 1.8V signal switching the clock source must
+ * still be ON as it requires accessing SDHC
+ * registers (SDHCi host control2 register bit 3 must
+ * be written and polled after stopping the SDCLK).
+ */
+ if (host->mmc->card_clock_off)
+ return 0;
+ pr_debug("%s: request to disable clocks\n",
+ mmc_hostname(host->mmc));
+ if (!IS_ERR_OR_NULL(msm_host->sleep_clk))
+ clk_disable_unprepare(msm_host->sleep_clk);
+ if (!IS_ERR_OR_NULL(msm_host->ff_clk))
+ clk_disable_unprepare(msm_host->ff_clk);
clk_disable_unprepare(msm_host->clk);
if (!IS_ERR(msm_host->pclk))
clk_disable_unprepare(msm_host->pclk);
if (!IS_ERR_OR_NULL(msm_host->bus_clk))
clk_disable_unprepare(msm_host->bus_clk);
- atomic_set(&msm_host->clks_on, 0);
+
+ atomic_set(&msm_host->controller_clock, 0);
+ sdhci_msm_bus_voting(host, 0);
}
- spin_lock_irqsave(&host->lock, flags);
- host->clock = clock;
- spin_unlock_irqrestore(&host->lock, flags);
+ atomic_set(&msm_host->clks_on, enable);
goto out;
-disable_pclk:
- if (!IS_ERR_OR_NULL(msm_host->pclk))
- clk_disable_unprepare(msm_host->pclk);
+disable_ff_clk:
+ if (!IS_ERR_OR_NULL(msm_host->ff_clk))
+ clk_disable_unprepare(msm_host->ff_clk);
disable_bus_clk:
if (!IS_ERR_OR_NULL(msm_host->bus_clk))
clk_disable_unprepare(msm_host->bus_clk);
+disable_controller_clk:
+ if (!IS_ERR_OR_NULL(msm_host->clk))
+ clk_disable_unprepare(msm_host->clk);
+ if (!IS_ERR_OR_NULL(msm_host->pclk))
+ clk_disable_unprepare(msm_host->pclk);
+ atomic_set(&msm_host->controller_clock, 0);
+remove_vote:
+ if (msm_host->msm_bus_vote.client_handle)
+ sdhci_msm_bus_cancel_work_and_set_vote(host, 0);
out:
- return;
+ return rc;
+}
+
+static void sdhci_msm_set_clock(struct sdhci_host *host, unsigned int clock)
+{
+ int rc;
+ struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+ struct sdhci_msm_host *msm_host = pltfm_host->priv;
+ struct mmc_ios curr_ios = host->mmc->ios;
+ u32 sup_clock, ddr_clock;
+ bool curr_pwrsave;
+
+ if (!clock) {
+ /*
+ * disable pwrsave to ensure clock is not auto-gated until
+ * the rate is >400KHz (initialization complete).
+ */
+ writel_relaxed(readl_relaxed(host->ioaddr + CORE_VENDOR_SPEC) &
+ ~CORE_CLK_PWRSAVE, host->ioaddr + CORE_VENDOR_SPEC);
+ sdhci_msm_prepare_clocks(host, false);
+ host->clock = clock;
+ goto out;
+ }
+
+ rc = sdhci_msm_prepare_clocks(host, true);
+ if (rc)
+ goto out;
+
+ curr_pwrsave = !!(readl_relaxed(host->ioaddr + CORE_VENDOR_SPEC) &
+ CORE_CLK_PWRSAVE);
+ if ((clock > 400000) &&
+ !curr_pwrsave && mmc_host_may_gate_card(host->mmc->card))
+ writel_relaxed(readl_relaxed(host->ioaddr + CORE_VENDOR_SPEC)
+ | CORE_CLK_PWRSAVE,
+ host->ioaddr + CORE_VENDOR_SPEC);
+ /*
+ * Disable pwrsave for a newly added card if doesn't allow clock
+ * gating.
+ */
+ else if (curr_pwrsave && !mmc_host_may_gate_card(host->mmc->card))
+ writel_relaxed(readl_relaxed(host->ioaddr + CORE_VENDOR_SPEC)
+ & ~CORE_CLK_PWRSAVE,
+ host->ioaddr + CORE_VENDOR_SPEC);
+
+ sup_clock = sdhci_msm_get_sup_clk_rate(host, clock);
+ if ((curr_ios.timing == MMC_TIMING_UHS_DDR50) ||
+ (curr_ios.timing == MMC_TIMING_MMC_HS400)) {
+ /*
+ * The SDHC requires internal clock frequency to be double the
+ * actual clock that will be set for DDR mode. The controller
+ * uses the faster clock(100/400MHz) for some of its parts and
+ * send the actual required clock (50/200MHz) to the card.
+ */
+ ddr_clock = clock * 2;
+ sup_clock = sdhci_msm_get_sup_clk_rate(host,
+ ddr_clock);
+ }
+
+ /*
+ * In general all timing modes are controlled via UHS mode select in
+ * Host Control2 register. eMMC specific HS200/HS400 doesn't have
+ * their respective modes defined here, hence we use these values.
+ *
+ * HS200 - SDR104 (Since they both are equivalent in functionality)
+ * HS400 - This involves multiple configurations
+ * Initially SDR104 - when tuning is required as HS200
+ * Then when switching to DDR @ 400MHz (HS400) we use
+ * the vendor specific HC_SELECT_IN to control the mode.
+ *
+ * In addition to controlling the modes we also need to select the
+ * correct input clock for DLL depending on the mode.
+ *
+ * HS400 - divided clock (free running MCLK/2)
+ * All other modes - default (free running MCLK)
+ */
+ if (curr_ios.timing == MMC_TIMING_MMC_HS400) {
+ /* Select the divided clock (free running MCLK/2) */
+ writel_relaxed(((readl_relaxed(host->ioaddr + CORE_VENDOR_SPEC)
+ & ~CORE_HC_MCLK_SEL_MASK)
+ | CORE_HC_MCLK_SEL_HS400),
+ host->ioaddr + CORE_VENDOR_SPEC);
+ /*
+ * Select HS400 mode using the HC_SELECT_IN from VENDOR SPEC
+ * register
+ */
+ if (msm_host->tuning_done && !msm_host->calibration_done) {
+ /*
+ * Write 0x6 to HC_SELECT_IN and 1 to HC_SELECT_IN_EN
+ * field in VENDOR_SPEC_FUNC
+ */
+ writel_relaxed((readl_relaxed(host->ioaddr + \
+ CORE_VENDOR_SPEC)
+ | CORE_HC_SELECT_IN_HS400
+ | CORE_HC_SELECT_IN_EN),
+ host->ioaddr + CORE_VENDOR_SPEC);
+ }
+ } else {
+ /* Select the default clock (free running MCLK) */
+ writel_relaxed(((readl_relaxed(host->ioaddr + CORE_VENDOR_SPEC)
+ & ~CORE_HC_MCLK_SEL_MASK)
+ | CORE_HC_MCLK_SEL_DFLT),
+ host->ioaddr + CORE_VENDOR_SPEC);
+
+ /*
+ * Disable HC_SELECT_IN to be able to use the UHS mode select
+ * configuration from Host Control2 register for all other
+ * modes.
+ *
+ * Write 0 to HC_SELECT_IN and HC_SELECT_IN_EN field
+ * in VENDOR_SPEC_FUNC
+ */
+ writel_relaxed((readl_relaxed(host->ioaddr + CORE_VENDOR_SPEC)
+ & ~CORE_HC_SELECT_IN_EN
+ & ~CORE_HC_SELECT_IN_MASK),
+ host->ioaddr + CORE_VENDOR_SPEC);
+ }
+ mb();
+
+ if (sup_clock != msm_host->clk_rate) {
+ pr_debug("%s: %s: setting clk rate to %u\n",
+ mmc_hostname(host->mmc), __func__, sup_clock);
+ rc = clk_set_rate(msm_host->clk, sup_clock);
+ if (rc) {
+ pr_err("%s: %s: Failed to set rate %u for host-clk : %d\n",
+ mmc_hostname(host->mmc), __func__,
+ sup_clock, rc);
+ goto out;
+ }
+ msm_host->clk_rate = sup_clock;
+ host->clock = clock;
+ /*
+ * Update the bus vote in case of frequency change due to
+ * clock scaling.
+ */
+ sdhci_msm_bus_voting(host, 1);
+ }
+out:
+ sdhci_set_clock(host, clock);
+}
+
+static void sdhci_msm_set_uhs_signaling(struct sdhci_host *host,
+ unsigned int uhs)
+{
+ struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+ struct sdhci_msm_host *msm_host = pltfm_host->priv;
+ u16 ctrl_2;
+
+ ctrl_2 = sdhci_readw(host, SDHCI_HOST_CONTROL2);
+ /* Select Bus Speed Mode for host */
+ ctrl_2 &= ~SDHCI_CTRL_UHS_MASK;
+ if (uhs == MMC_TIMING_MMC_HS400)
+ ctrl_2 |= SDHCI_CTRL_UHS_SDR104;
+ else if (uhs == MMC_TIMING_MMC_HS200)
+ ctrl_2 |= SDHCI_CTRL_UHS_SDR104;
+ else if (uhs == MMC_TIMING_UHS_SDR12)
+ ctrl_2 |= SDHCI_CTRL_UHS_SDR12;
+ else if (uhs == MMC_TIMING_UHS_SDR25)
+ ctrl_2 |= SDHCI_CTRL_UHS_SDR25;
+ else if (uhs == MMC_TIMING_UHS_SDR50)
+ ctrl_2 |= SDHCI_CTRL_UHS_SDR50;
+ else if (uhs == MMC_TIMING_UHS_SDR104)
+ ctrl_2 |= SDHCI_CTRL_UHS_SDR104;
+ else if (uhs == MMC_TIMING_UHS_DDR50)
+ ctrl_2 |= SDHCI_CTRL_UHS_DDR50;
+ /*
+ * When clock frquency is less than 100MHz, the feedback clock must be
+ * provided and DLL must not be used so that tuning can be skipped. To
+ * provide feedback clock, the mode selection can be any value less
+ * than 3'b011 in bits [2:0] of HOST CONTROL2 register.
+ */
+ if (host->clock <= CORE_FREQ_100MHZ) {
+ if ((uhs == MMC_TIMING_MMC_HS400) ||
+ (uhs == MMC_TIMING_MMC_HS200) ||
+ (uhs == MMC_TIMING_UHS_SDR104))
+ ctrl_2 &= ~SDHCI_CTRL_UHS_MASK;
+
+ /*
+ * Make sure DLL is disabled when not required
+ *
+ * Write 1 to DLL_RST bit of DLL_CONFIG register
+ */
+ writel_relaxed((readl_relaxed(host->ioaddr + CORE_DLL_CONFIG)
+ | CORE_DLL_RST),
+ host->ioaddr + CORE_DLL_CONFIG);
+
+ /* Write 1 to DLL_PDN bit of DLL_CONFIG register */
+ writel_relaxed((readl_relaxed(host->ioaddr + CORE_DLL_CONFIG)
+ | CORE_DLL_PDN),
+ host->ioaddr + CORE_DLL_CONFIG);
+ mb();
+
+ /*
+ * The DLL needs to be restored and CDCLP533 recalibrated
+ * when the clock frequency is set back to 400MHz.
+ */
+ msm_host->calibration_done = false;
+ }
+
+ pr_debug("%s: %s-clock:%u uhs mode:%u ctrl_2:0x%x\n",
+ mmc_hostname(host->mmc), __func__, host->clock, uhs, ctrl_2);
+ sdhci_writew(host, ctrl_2, SDHCI_HOST_CONTROL2);
+
+}
+
+#define MAX_TEST_BUS 20
+
+void sdhci_msm_dump_vendor_regs(struct sdhci_host *host)
+{
+ struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+ struct sdhci_msm_host *msm_host = pltfm_host->priv;
+ int tbsel, tbsel2;
+ int i, index = 0;
+ u32 test_bus_val = 0;
+ u32 debug_reg[MAX_TEST_BUS] = {0};
+
+ pr_info("----------- VENDOR REGISTER DUMP -----------\n");
+ pr_info("Data cnt: 0x%08x | Fifo cnt: 0x%08x | Int sts: 0x%08x\n",
+ readl_relaxed(msm_host->core_mem + CORE_MCI_DATA_CNT),
+ readl_relaxed(msm_host->core_mem + CORE_MCI_FIFO_CNT),
+ readl_relaxed(msm_host->core_mem + CORE_MCI_STATUS));
+ pr_info("DLL cfg: 0x%08x | DLL sts: 0x%08x | SDCC ver: 0x%08x\n",
+ readl_relaxed(host->ioaddr + CORE_DLL_CONFIG),
+ readl_relaxed(host->ioaddr + CORE_DLL_STATUS),
+ readl_relaxed(msm_host->core_mem + CORE_MCI_VERSION));
+ pr_info("Vndr func: 0x%08x | Vndr adma err : addr0: 0x%08x addr1: 0x%08x\n",
+ readl_relaxed(host->ioaddr + CORE_VENDOR_SPEC),
+ readl_relaxed(host->ioaddr + CORE_VENDOR_SPEC_ADMA_ERR_ADDR0),
+ readl_relaxed(host->ioaddr + CORE_VENDOR_SPEC_ADMA_ERR_ADDR1));
+
+ /*
+ * tbsel indicates [2:0] bits and tbsel2 indicates [7:4] bits
+ * of CORE_TESTBUS_CONFIG register.
+ *
+ * To select test bus 0 to 7 use tbsel and to select any test bus
+ * above 7 use (tbsel2 | tbsel) to get the test bus number. For eg,
+ * to select test bus 14, write 0x1E to CORE_TESTBUS_CONFIG register
+ * i.e., tbsel2[7:4] = 0001, tbsel[2:0] = 110.
+ */
+ for (tbsel2 = 0; tbsel2 < 3; tbsel2++) {
+ for (tbsel = 0; tbsel < 8; tbsel++) {
+ if (index >= MAX_TEST_BUS)
+ break;
+ test_bus_val = (tbsel2 << CORE_TESTBUS_SEL2_BIT) |
+ tbsel | CORE_TESTBUS_ENA;
+ writel_relaxed(test_bus_val,
+ msm_host->core_mem + CORE_TESTBUS_CONFIG);
+ debug_reg[index++] = readl_relaxed(msm_host->core_mem +
+ CORE_SDCC_DEBUG_REG);
+ }
+ }
+ for (i = 0; i < MAX_TEST_BUS; i = i + 4)
+ pr_info(" Test bus[%d to %d]: 0x%08x 0x%08x 0x%08x 0x%08x\n",
+ i, i + 3, debug_reg[i], debug_reg[i+1],
+ debug_reg[i+2], debug_reg[i+3]);
+ /* Disable test bus */
+ writel_relaxed(~CORE_TESTBUS_ENA, msm_host->core_mem +
+ CORE_TESTBUS_CONFIG);
}
static struct sdhci_ops sdhci_msm_ops = {
+ .set_uhs_signaling = sdhci_msm_set_uhs_signaling,
.check_power_status = sdhci_msm_check_power_status,
.platform_execute_tuning = sdhci_msm_execute_tuning,
.toggle_cdr = sdhci_msm_toggle_cdr,
.get_max_segments = sdhci_msm_max_segs,
.set_clock = sdhci_msm_set_clock,
- .platform_bus_voting = sdhci_msm_bus_voting,
+ .get_min_clock = sdhci_msm_get_min_clock,
+ .get_max_clock = sdhci_msm_get_max_clock,
+ .dump_vendor_regs = sdhci_msm_dump_vendor_regs,
+ .config_auto_tuning_cmd = sdhci_msm_config_auto_tuning_cmd,
+ .enable_controller_clock = sdhci_msm_enable_controller_clock,
};
+static void sdhci_set_default_hw_caps(struct sdhci_msm_host *msm_host,
+ struct sdhci_host *host)
+{
+ u32 version, caps;
+ u16 minor;
+ u8 major;
+
+ version = readl_relaxed(msm_host->core_mem + CORE_MCI_VERSION);
+ major = (version & CORE_VERSION_MAJOR_MASK) >>
+ CORE_VERSION_MAJOR_SHIFT;
+ minor = version & CORE_VERSION_TARGET_MASK;
+
+ /*
+ * Starting with SDCC 5 controller (core major version = 1)
+ * controller won't advertise 3.0v and 8-bit features except for
+ * some targets.
+ */
+ if (major >= 1 && minor != 0x11 && minor != 0x12) {
+ caps = CORE_3_0V_SUPPORT;
+ if (msm_host->pdata->mmc_bus_width == MMC_CAP_8_BIT_DATA)
+ caps |= CORE_8_BIT_SUPPORT;
+ writel_relaxed(
+ (readl_relaxed(host->ioaddr + SDHCI_CAPABILITIES) |
+ caps), host->ioaddr + CORE_VENDOR_SPEC_CAPABILITIES0);
+ }
+}
+
static int sdhci_msm_probe(struct platform_device *pdev)
{
struct sdhci_host *host;
@@ -1615,7 +2518,8 @@ static int sdhci_msm_probe(struct platform_device *pdev)
struct sdhci_msm_host *msm_host;
struct resource *core_memres = NULL;
int ret = 0, pwr_irq = 0, dead = 0;
- u32 host_version;
+ u16 host_version;
+ u32 pwr, irq_status, irq_ctl;
pr_debug("%s: Enter %s\n", dev_name(&pdev->dev), __func__);
msm_host = devm_kzalloc(&pdev->dev, sizeof(struct sdhci_msm_host),
@@ -1624,7 +2528,6 @@ static int sdhci_msm_probe(struct platform_device *pdev)
ret = -ENOMEM;
goto out;
}
- init_waitqueue_head(&msm_host->pwr_irq_wait);
msm_host->sdhci_msm_pdata.ops = &sdhci_msm_ops;
host = sdhci_pltfm_init(pdev, &msm_host->sdhci_msm_pdata, 0);
@@ -1640,6 +2543,19 @@ static int sdhci_msm_probe(struct platform_device *pdev)
/* Extract platform data */
if (pdev->dev.of_node) {
+ ret = of_alias_get_id(pdev->dev.of_node, "sdhc");
+ if (ret < 0) {
+ dev_err(&pdev->dev, "Failed to get slot index %d\n",
+ ret);
+ goto pltfm_free;
+ }
+ if (disable_slots & (1 << (ret - 1))) {
+ dev_info(&pdev->dev, "%s: Slot %d disabled\n", __func__,
+ ret);
+ ret = -ENODEV;
+ goto pltfm_free;
+ }
+
msm_host->pdata = sdhci_msm_populate_pdata(&pdev->dev);
if (!msm_host->pdata) {
dev_err(&pdev->dev, "DT parsing error\n");
@@ -1671,6 +2587,7 @@ static int sdhci_msm_probe(struct platform_device *pdev)
if (ret)
goto bus_clk_disable;
}
+ atomic_set(&msm_host->controller_clock, 1);
/* Setup SDC MMC clock */
msm_host->clk = devm_clk_get(&pdev->dev, "core_clk");
@@ -1679,16 +2596,51 @@ static int sdhci_msm_probe(struct platform_device *pdev)
goto pclk_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;
+ }
ret = clk_prepare_enable(msm_host->clk);
if (ret)
goto pclk_disable;
+ msm_host->clk_rate = sdhci_msm_get_min_clock(host);
atomic_set(&msm_host->clks_on, 1);
+
+ /* Setup CDC calibration fixed feedback clock */
+ msm_host->ff_clk = devm_clk_get(&pdev->dev, "cal_clk");
+ if (!IS_ERR(msm_host->ff_clk)) {
+ ret = clk_prepare_enable(msm_host->ff_clk);
+ if (ret)
+ goto clk_disable;
+ }
+
+ /* Setup CDC calibration sleep clock */
+ msm_host->sleep_clk = devm_clk_get(&pdev->dev, "sleep_clk");
+ if (!IS_ERR(msm_host->sleep_clk)) {
+ ret = clk_prepare_enable(msm_host->sleep_clk);
+ if (ret)
+ goto ff_clk_disable;
+ }
+
+ msm_host->saved_tuning_phase = INVALID_TUNING_PHASE;
+
+ ret = sdhci_msm_bus_register(msm_host, pdev);
+ if (ret)
+ goto sleep_clk_disable;
+
+ if (msm_host->msm_bus_vote.client_handle)
+ INIT_DELAYED_WORK(&msm_host->msm_bus_vote.vote_work,
+ sdhci_msm_bus_work);
+ sdhci_msm_bus_voting(host, 1);
+
/* Setup regulators */
ret = sdhci_msm_vreg_init(&pdev->dev, msm_host->pdata, true);
if (ret) {
dev_err(&pdev->dev, "Regulator setup failed (%d)\n", ret);
- goto clk_disable;
+ goto bus_unregister;
}
/* Reset the core and Enable SDHC mode */
@@ -1703,11 +2655,54 @@ static int sdhci_msm_probe(struct platform_device *pdev)
goto vreg_deinit;
}
+ /* Unset HC_MODE_EN bit in HC_MODE register */
+ writel_relaxed(0, (msm_host->core_mem + CORE_HC_MODE));
+
/* Set SW_RST bit in POWER register (Offset 0x0) */
- writel_relaxed(CORE_SW_RST, msm_host->core_mem + CORE_POWER);
+ writel_relaxed(readl_relaxed(msm_host->core_mem + CORE_POWER) |
+ CORE_SW_RST, msm_host->core_mem + CORE_POWER);
+ /*
+ * SW reset can take upto 10HCLK + 15MCLK cycles.
+ * Calculating based on min clk rates (hclk = 27MHz,
+ * mclk = 400KHz) it comes to ~40us. Let's poll for
+ * max. 1ms for reset completion.
+ */
+ ret = readl_poll_timeout(msm_host->core_mem + CORE_POWER,
+ pwr, !(pwr & CORE_SW_RST), 100, 10);
+
+ if (ret) {
+ dev_err(&pdev->dev, "reset failed (%d)\n", ret);
+ goto vreg_deinit;
+ }
/* Set HC_MODE_EN bit in HC_MODE register */
writel_relaxed(HC_MODE_EN, (msm_host->core_mem + CORE_HC_MODE));
+ /* Set FF_CLK_SW_RST_DIS bit in HC_MODE register */
+ writel_relaxed(readl_relaxed(msm_host->core_mem + CORE_HC_MODE) |
+ FF_CLK_SW_RST_DIS, msm_host->core_mem + CORE_HC_MODE);
+
+ sdhci_set_default_hw_caps(msm_host, host);
+ /*
+ * CORE_SW_RST above may trigger power irq if previous status of PWRCTL
+ * was either BUS_ON or IO_HIGH_V. So before we enable the power irq
+ * interrupt in GIC (by registering the interrupt handler), we need to
+ * ensure that any pending power irq interrupt status is acknowledged
+ * otherwise power irq interrupt handler would be fired prematurely.
+ */
+ irq_status = readl_relaxed(msm_host->core_mem + CORE_PWRCTL_STATUS);
+ writel_relaxed(irq_status, (msm_host->core_mem + CORE_PWRCTL_CLEAR));
+ irq_ctl = readl_relaxed(msm_host->core_mem + CORE_PWRCTL_CTL);
+ if (irq_status & (CORE_PWRCTL_BUS_ON | CORE_PWRCTL_BUS_OFF))
+ irq_ctl |= CORE_PWRCTL_BUS_SUCCESS;
+ if (irq_status & (CORE_PWRCTL_IO_HIGH | CORE_PWRCTL_IO_LOW))
+ irq_ctl |= CORE_PWRCTL_IO_SUCCESS;
+ writel_relaxed(irq_ctl, (msm_host->core_mem + CORE_PWRCTL_CTL));
+ /*
+ * Ensure that above writes are propogated before interrupt enablement
+ * in GIC.
+ */
+ mb();
+
/*
* Following are the deviations from SDHC spec v3.0 -
* 1. Card detection is handled using separate GPIO.
@@ -1715,8 +2710,16 @@ static int sdhci_msm_probe(struct platform_device *pdev)
*/
host->quirks |= SDHCI_QUIRK_BROKEN_CARD_DETECTION;
host->quirks |= SDHCI_QUIRK_SINGLE_POWER_WRITE;
+ host->quirks |= SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN;
+ host->quirks2 |= SDHCI_QUIRK2_ALWAYS_USE_BASE_CLOCK;
+ host->quirks2 |= SDHCI_QUIRK2_IGNORE_DATATOUT_FOR_R1BCMD;
+ host->quirks2 |= SDHCI_QUIRK2_BROKEN_PRESET_VALUE;
+ host->quirks2 |= SDHCI_QUIRK2_USE_RESERVED_MAX_TIMEOUT;
- host_version = readl_relaxed((host->ioaddr + SDHCI_HOST_VERSION));
+ if (host->quirks2 & SDHCI_QUIRK2_ALWAYS_USE_BASE_CLOCK)
+ host->quirks2 |= SDHCI_QUIRK2_DIVIDE_TOUT_BY_4;
+
+ host_version = readw_relaxed((host->ioaddr + SDHCI_HOST_VERSION));
dev_dbg(&pdev->dev, "Host Version: 0x%x Vendor Version 0x%x\n",
host_version, ((host_version & SDHCI_VENDOR_VER_MASK) >>
SDHCI_VENDOR_VER_SHIFT));
@@ -1734,6 +2737,8 @@ static int sdhci_msm_probe(struct platform_device *pdev)
host->quirks2 |= SDHCI_QUIRK2_RDWR_TX_ACTIVE_EOT;
}
+ host->quirks2 |= SDHCI_QUIRK2_IGN_DATA_END_BIT_ERROR;
+
/* Setup PWRCTL irq */
pwr_irq = platform_get_irq_byname(pdev, "pwr_irq");
if (pwr_irq < 0) {
@@ -1761,37 +2766,45 @@ static int sdhci_msm_probe(struct platform_device *pdev)
/* Set host capabilities */
msm_host->mmc->caps |= msm_host->pdata->mmc_bus_width;
msm_host->mmc->caps |= msm_host->pdata->caps;
- msm_host->mmc->caps |= MMC_CAP_HW_RESET;
msm_host->mmc->caps2 |= msm_host->pdata->caps2;
msm_host->mmc->caps2 |= MMC_CAP2_PACKED_WR;
msm_host->mmc->caps2 |= MMC_CAP2_PACKED_WR_CONTROL;
+ msm_host->mmc->caps2 |= MMC_CAP2_CLK_SCALE;
+ msm_host->mmc->caps2 |= MMC_CAP2_ASYNC_SDIO_IRQ_4BIT_MODE;
+ msm_host->mmc->pm_caps |= MMC_PM_KEEP_POWER;
if (msm_host->pdata->nonremovable)
msm_host->mmc->caps |= MMC_CAP_NONREMOVABLE;
host->cpu_dma_latency_us = msm_host->pdata->cpu_dma_latency_us;
- ret = sdhci_msm_bus_register(msm_host, pdev);
- if (ret)
- goto vreg_deinit;
+ init_completion(&msm_host->pwr_irq_completion);
- if (msm_host->msm_bus_vote.client_handle)
- INIT_DELAYED_WORK(&msm_host->msm_bus_vote.vote_work,
- sdhci_msm_bus_work);
+ if (gpio_is_valid(msm_host->pdata->status_gpio)) {
+ ret = mmc_gpio_request_cd(msm_host->mmc,
+ msm_host->pdata->status_gpio, 0);
+ if (ret) {
+ dev_err(&pdev->dev, "%s: Failed to request card detection IRQ %d\n",
+ __func__, ret);
+ goto vreg_deinit;
+ }
+ }
+
+ if ((sdhci_readl(host, SDHCI_CAPABILITIES) & SDHCI_CAN_64BIT) &&
+ (dma_supported(mmc_dev(host->mmc), DMA_BIT_MASK(64)))) {
+ host->dma_mask = DMA_BIT_MASK(64);
+ mmc_dev(host->mmc)->dma_mask = &host->dma_mask;
+ } else if (dma_supported(mmc_dev(host->mmc), DMA_BIT_MASK(32))) {
+ host->dma_mask = DMA_BIT_MASK(32);
+ mmc_dev(host->mmc)->dma_mask = &host->dma_mask;
+ } else {
+ dev_err(&pdev->dev, "%s: Failed to set dma mask\n", __func__);
+ }
ret = sdhci_add_host(host);
if (ret) {
dev_err(&pdev->dev, "Add host failed (%d)\n", ret);
- goto bus_unregister;
- }
-
- /* Set core clk rate, optionally override from dts */
- if (msm_host->pdata->max_clk)
- host->max_clk = msm_host->pdata->max_clk;
- ret = clk_set_rate(msm_host->clk, host->max_clk);
- if (ret) {
- dev_err(&pdev->dev, "MClk rate set failed (%d)\n", ret);
- goto remove_host;
+ goto vreg_deinit;
}
msm_host->msm_bus_vote.max_bus_bw.show = show_sdhci_max_bus_bw;
@@ -1804,16 +2817,49 @@ static int sdhci_msm_probe(struct platform_device *pdev)
if (ret)
goto remove_host;
+ if (!gpio_is_valid(msm_host->pdata->status_gpio)) {
+ msm_host->polling.show = show_polling;
+ msm_host->polling.store = store_polling;
+ sysfs_attr_init(&msm_host->polling.attr);
+ msm_host->polling.attr.name = "polling";
+ msm_host->polling.attr.mode = S_IRUGO | S_IWUSR;
+ ret = device_create_file(&pdev->dev, &msm_host->polling);
+ if (ret)
+ goto remove_max_bus_bw_file;
+ }
+
+ msm_host->auto_cmd21_attr.show = show_auto_cmd21;
+ msm_host->auto_cmd21_attr.store = store_auto_cmd21;
+ sysfs_attr_init(&msm_host->auto_cmd21_attr.attr);
+ msm_host->auto_cmd21_attr.attr.name = "enable_auto_cmd21";
+ msm_host->auto_cmd21_attr.attr.mode = S_IRUGO | S_IWUSR;
+ ret = device_create_file(&pdev->dev, &msm_host->auto_cmd21_attr);
+ if (ret) {
+ pr_err("%s: %s: failed creating auto-cmd21 attr: %d\n",
+ mmc_hostname(host->mmc), __func__, ret);
+ device_remove_file(&pdev->dev, &msm_host->auto_cmd21_attr);
+ }
+
/* Successful initialization */
goto out;
+remove_max_bus_bw_file:
+ device_remove_file(&pdev->dev, &msm_host->msm_bus_vote.max_bus_bw);
remove_host:
dead = (readl_relaxed(host->ioaddr + SDHCI_INT_STATUS) == 0xffffffff);
sdhci_remove_host(host, dead);
-bus_unregister:
- sdhci_msm_bus_unregister(msm_host);
vreg_deinit:
sdhci_msm_vreg_init(&pdev->dev, msm_host->pdata, false);
+bus_unregister:
+ if (msm_host->msm_bus_vote.client_handle)
+ sdhci_msm_bus_cancel_work_and_set_vote(host, 0);
+ sdhci_msm_bus_unregister(msm_host);
+sleep_clk_disable:
+ if (!IS_ERR(msm_host->sleep_clk))
+ clk_disable_unprepare(msm_host->sleep_clk);
+ff_clk_disable:
+ if (!IS_ERR(msm_host->ff_clk))
+ clk_disable_unprepare(msm_host->ff_clk);
clk_disable:
if (!IS_ERR(msm_host->clk))
clk_disable_unprepare(msm_host->clk);
@@ -1840,9 +2886,12 @@ static int sdhci_msm_remove(struct platform_device *pdev)
0xffffffff);
pr_debug("%s: %s\n", dev_name(&pdev->dev), __func__);
+ if (!gpio_is_valid(msm_host->pdata->status_gpio))
+ device_remove_file(&pdev->dev, &msm_host->polling);
device_remove_file(&pdev->dev, &msm_host->msm_bus_vote.max_bus_bw);
sdhci_remove_host(host, dead);
sdhci_pltfm_free(pdev);
+
sdhci_msm_vreg_init(&pdev->dev, msm_host->pdata, false);
if (pdata->pin_data)
diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c
index bb8e95b..60be7c5 100644
--- a/drivers/mmc/host/sdhci.c
+++ b/drivers/mmc/host/sdhci.c
@@ -30,6 +30,7 @@
#include <linux/mmc/card.h>
#include <linux/mmc/sdio.h>
#include <linux/mmc/slot-gpio.h>
+#include <linux/mmc/sdio.h>
#include "sdhci.h"
@@ -45,62 +46,119 @@ static unsigned int debug_quirks2;
static void sdhci_finish_data(struct sdhci_host *);
+static bool sdhci_check_state(struct sdhci_host *);
+
static void sdhci_enable_preset_value(struct sdhci_host *host, bool enable);
+static void sdhci_dump_state(struct sdhci_host *host)
+{
+ struct mmc_host *mmc = host->mmc;
+
+ #ifdef CONFIG_MMC_CLKGATE
+ pr_info("%s: clk: %d clk-gated: %d claimer: %s pwr: %d\n",
+ mmc_hostname(mmc), host->clock, mmc->clk_gated,
+ mmc->claimer->comm, host->pwr);
+ #else
+ pr_info("%s: clk: %d claimer: %s pwr: %d\n",
+ mmc_hostname(mmc), host->clock,
+ mmc->claimer->comm, host->pwr);
+ #endif
+ pr_info("%s: rpmstatus[pltfm](runtime-suspend:usage_count:disable_depth)(%d:%d:%d)\n",
+ mmc_hostname(mmc), mmc->parent->power.runtime_status,
+ atomic_read(&mmc->parent->power.usage_count),
+ mmc->parent->power.disable_depth);
+}
+
static void sdhci_dumpregs(struct sdhci_host *host)
{
- pr_err(DRIVER_NAME ": =========== REGISTER DUMP (%s)===========\n",
- mmc_hostname(host->mmc));
+ pr_info(DRIVER_NAME ": =========== REGISTER DUMP (%s)===========\n",
+ mmc_hostname(host->mmc));
- pr_err(DRIVER_NAME ": Sys addr: 0x%08x | Version: 0x%08x\n",
+ pr_info(DRIVER_NAME ": Sys addr: 0x%08x | Version: 0x%08x\n",
sdhci_readl(host, SDHCI_DMA_ADDRESS),
sdhci_readw(host, SDHCI_HOST_VERSION));
- pr_err(DRIVER_NAME ": Blk size: 0x%08x | Blk cnt: 0x%08x\n",
+ pr_info(DRIVER_NAME ": Blk size: 0x%08x | Blk cnt: 0x%08x\n",
sdhci_readw(host, SDHCI_BLOCK_SIZE),
sdhci_readw(host, SDHCI_BLOCK_COUNT));
- pr_err(DRIVER_NAME ": Argument: 0x%08x | Trn mode: 0x%08x\n",
+ pr_info(DRIVER_NAME ": Argument: 0x%08x | Trn mode: 0x%08x\n",
sdhci_readl(host, SDHCI_ARGUMENT),
sdhci_readw(host, SDHCI_TRANSFER_MODE));
- pr_err(DRIVER_NAME ": Present: 0x%08x | Host ctl: 0x%08x\n",
+ pr_info(DRIVER_NAME ": Present: 0x%08x | Host ctl: 0x%08x\n",
sdhci_readl(host, SDHCI_PRESENT_STATE),
sdhci_readb(host, SDHCI_HOST_CONTROL));
- pr_err(DRIVER_NAME ": Power: 0x%08x | Blk gap: 0x%08x\n",
+ pr_info(DRIVER_NAME ": Power: 0x%08x | Blk gap: 0x%08x\n",
sdhci_readb(host, SDHCI_POWER_CONTROL),
sdhci_readb(host, SDHCI_BLOCK_GAP_CONTROL));
- pr_err(DRIVER_NAME ": Wake-up: 0x%08x | Clock: 0x%08x\n",
+ pr_info(DRIVER_NAME ": Wake-up: 0x%08x | Clock: 0x%08x\n",
sdhci_readb(host, SDHCI_WAKE_UP_CONTROL),
sdhci_readw(host, SDHCI_CLOCK_CONTROL));
- pr_err(DRIVER_NAME ": Timeout: 0x%08x | Int stat: 0x%08x\n",
+ pr_info(DRIVER_NAME ": Timeout: 0x%08x | Int stat: 0x%08x\n",
sdhci_readb(host, SDHCI_TIMEOUT_CONTROL),
sdhci_readl(host, SDHCI_INT_STATUS));
- pr_err(DRIVER_NAME ": Int enab: 0x%08x | Sig enab: 0x%08x\n",
+ pr_info(DRIVER_NAME ": Int enab: 0x%08x | Sig enab: 0x%08x\n",
sdhci_readl(host, SDHCI_INT_ENABLE),
sdhci_readl(host, SDHCI_SIGNAL_ENABLE));
- pr_err(DRIVER_NAME ": AC12 err: 0x%08x | Slot int: 0x%08x\n",
- sdhci_readw(host, SDHCI_ACMD12_ERR),
+ pr_info(DRIVER_NAME ": AC12 err: 0x%08x | Slot int: 0x%08x\n",
+ host->auto_cmd_err_sts,
sdhci_readw(host, SDHCI_SLOT_INT_STATUS));
- pr_err(DRIVER_NAME ": Caps: 0x%08x | Caps_1: 0x%08x\n",
+ pr_info(DRIVER_NAME ": Caps: 0x%08x | Caps_1: 0x%08x\n",
sdhci_readl(host, SDHCI_CAPABILITIES),
sdhci_readl(host, SDHCI_CAPABILITIES_1));
- pr_err(DRIVER_NAME ": Cmd: 0x%08x | Max curr: 0x%08x\n",
+ pr_info(DRIVER_NAME ": Cmd: 0x%08x | Max curr: 0x%08x\n",
sdhci_readw(host, SDHCI_COMMAND),
sdhci_readl(host, SDHCI_MAX_CURRENT));
- pr_err(DRIVER_NAME ": Host ctl2: 0x%08x\n",
+ pr_info(DRIVER_NAME ": Resp 1: 0x%08x | Resp 0: 0x%08x\n",
+ sdhci_readl(host, SDHCI_RESPONSE + 0x4),
+ sdhci_readl(host, SDHCI_RESPONSE));
+ pr_info(DRIVER_NAME ": Resp 3: 0x%08x | Resp 2: 0x%08x\n",
+ sdhci_readl(host, SDHCI_RESPONSE + 0xC),
+ sdhci_readl(host, SDHCI_RESPONSE + 0x8));
+ pr_info(DRIVER_NAME ": Host ctl2: 0x%08x\n",
sdhci_readw(host, SDHCI_HOST_CONTROL2));
if (host->flags & SDHCI_USE_ADMA) {
if (host->flags & SDHCI_USE_64_BIT_DMA)
- pr_err(DRIVER_NAME ": ADMA Err: 0x%08x | ADMA Ptr: 0x%08x%08x\n",
+ pr_info(DRIVER_NAME ": ADMA Err: 0x%08x | ADMA Ptr: 0x%08x%08x\n",
readl(host->ioaddr + SDHCI_ADMA_ERROR),
readl(host->ioaddr + SDHCI_ADMA_ADDRESS_HI),
readl(host->ioaddr + SDHCI_ADMA_ADDRESS));
else
- pr_err(DRIVER_NAME ": ADMA Err: 0x%08x | ADMA Ptr: 0x%08x\n",
+ pr_info(DRIVER_NAME ": ADMA Err: 0x%08x | ADMA Ptr: 0x%08x\n",
readl(host->ioaddr + SDHCI_ADMA_ERROR),
readl(host->ioaddr + SDHCI_ADMA_ADDRESS));
}
- pr_err(DRIVER_NAME ": ===========================================\n");
+ if (host->ops->dump_vendor_regs)
+ host->ops->dump_vendor_regs(host);
+ sdhci_dump_state(host);
+ pr_info(DRIVER_NAME ": ===========================================\n");
+}
+
+#define MAX_PM_QOS_TIMEOUT_VALUE 100000 /* 100 ms */
+static ssize_t
+show_sdhci_pm_qos_tout(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct sdhci_host *host = dev_get_drvdata(dev);
+
+ return snprintf(buf, PAGE_SIZE, "%d us\n", host->pm_qos_timeout_us);
+}
+
+static ssize_t
+store_sdhci_pm_qos_tout(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct sdhci_host *host = dev_get_drvdata(dev);
+ uint32_t value;
+ unsigned long flags;
+
+ if (!kstrtou32(buf, 0, &value)) {
+ spin_lock_irqsave(&host->lock, flags);
+ if (value <= MAX_PM_QOS_TIMEOUT_VALUE)
+ host->pm_qos_timeout_us = value;
+ spin_unlock_irqrestore(&host->lock, flags);
+ }
+ return count;
}
/*****************************************************************************\
@@ -178,6 +236,10 @@ void sdhci_reset(struct sdhci_host *host, u8 mask)
/* Wait max 100 ms */
timeout = 100;
+ if (host->ops->check_power_status && host->pwr &&
+ (mask & SDHCI_RESET_ALL))
+ host->ops->check_power_status(host, REQ_BUS_OFF);
+
/* hw clears the bit when it's done */
while (sdhci_readb(host, SDHCI_SOFTWARE_RESET) & mask) {
if (timeout == 0) {
@@ -227,7 +289,7 @@ static void sdhci_init(struct sdhci_host *host, int soft)
SDHCI_INT_DATA_CRC | SDHCI_INT_DATA_TIMEOUT |
SDHCI_INT_INDEX | SDHCI_INT_END_BIT | SDHCI_INT_CRC |
SDHCI_INT_TIMEOUT | SDHCI_INT_DATA_END |
- SDHCI_INT_RESPONSE;
+ SDHCI_INT_RESPONSE | SDHCI_INT_AUTO_CMD_ERR;
if (host->tuning_mode == SDHCI_TUNING_MODE_2 ||
host->tuning_mode == SDHCI_TUNING_MODE_3)
@@ -276,7 +338,7 @@ static void sdhci_led_control(struct led_classdev *led,
spin_lock_irqsave(&host->lock, flags);
- if (host->runtime_suspended)
+ if (host->runtime_suspended || sdhci_check_state(host))
goto out;
if (brightness == LED_OFF)
@@ -660,6 +722,7 @@ static u8 sdhci_calc_timeout(struct sdhci_host *host, struct mmc_command *cmd)
u8 count;
struct mmc_data *data = cmd->data;
unsigned target_timeout, current_timeout;
+ u32 curr_clk = 0; /* In KHz */
/*
* If the host controller provides us with an incorrect timeout
@@ -705,7 +768,14 @@ static u8 sdhci_calc_timeout(struct sdhci_host *host, struct mmc_command *cmd)
* (1) / (2) > 2^6
*/
count = 0;
- current_timeout = (1 << 13) * 1000 / host->timeout_clk;
+ if (host->quirks2 & SDHCI_QUIRK2_ALWAYS_USE_BASE_CLOCK) {
+ curr_clk = host->clock / 1000;
+ if (host->quirks2 & SDHCI_QUIRK2_DIVIDE_TOUT_BY_4)
+ curr_clk /= 4;
+ current_timeout = (1 << 13) * 1000 / curr_clk;
+ } else {
+ current_timeout = (1 << 13) * 1000 / host->timeout_clk;
+ }
while (current_timeout < target_timeout) {
count++;
current_timeout <<= 1;
@@ -713,10 +783,12 @@ static u8 sdhci_calc_timeout(struct sdhci_host *host, struct mmc_command *cmd)
break;
}
- if (count >= 0xF) {
- DBG("%s: Too large timeout 0x%x requested for CMD%d!\n",
- mmc_hostname(host->mmc), count, cmd->opcode);
- count = 0xE;
+ if (!(host->quirks2 & SDHCI_QUIRK2_USE_RESERVED_MAX_TIMEOUT)) {
+ if (count >= 0xF) {
+ DBG("%s: Too large timeout 0x%x requested for CMD%d!\n",
+ mmc_hostname(host->mmc), count, cmd->opcode);
+ count = 0xE;
+ }
}
return count;
@@ -773,7 +845,7 @@ static void sdhci_prepare_data(struct sdhci_host *host, struct mmc_command *cmd)
WARN_ON(host->data);
/* Sanity checks */
- BUG_ON(data->blksz * data->blocks > 524288);
+ BUG_ON(data->blksz * data->blocks > host->mmc->max_req_size);
BUG_ON(data->blksz > host->mmc->max_blk_size);
BUG_ON(data->blocks > 65535);
@@ -941,8 +1013,13 @@ static void sdhci_set_transfer_mode(struct sdhci_host *host,
}
}
- if (data->flags & MMC_DATA_READ)
+ if (data->flags & MMC_DATA_READ) {
mode |= SDHCI_TRNS_READ;
+ if (host->ops->toggle_cdr)
+ host->ops->toggle_cdr(host, true);
+ }
+ if (host->ops->toggle_cdr && (data->flags & MMC_DATA_WRITE))
+ host->ops->toggle_cdr(host, false);
if (host->flags & SDHCI_REQ_USE_DMA)
mode |= SDHCI_TRNS_DMA;
@@ -1162,6 +1239,8 @@ void sdhci_send_command(struct sdhci_host *host, struct mmc_command *cmd)
cmd->opcode == MMC_SEND_TUNING_BLOCK_HS200)
flags |= SDHCI_CMD_DATA;
+ if (cmd->data)
+ host->data_start_time = ktime_get();
sdhci_writew(host, SDHCI_MAKE_CMD(cmd->opcode, flags), SDHCI_COMMAND);
}
EXPORT_SYMBOL_GPL(sdhci_send_command);
@@ -1345,6 +1424,10 @@ u16 sdhci_calc_clk(struct sdhci_host *host, unsigned int clock,
clock_set:
if (real_div)
*actual_clock = (host->max_clk * clk_mul) / real_div;
+
+ if (host->quirks2 & SDHCI_QUIRK2_ALWAYS_USE_BASE_CLOCK)
+ div = 0;
+
clk |= (div & SDHCI_DIV_MASK) << SDHCI_DIVIDER_SHIFT;
clk |= ((div & SDHCI_DIV_HI_MASK) >> SDHCI_DIV_MASK_LEN)
<< SDHCI_DIVIDER_HI_SHIFT;
@@ -1439,6 +1522,8 @@ void sdhci_set_power_noreg(struct sdhci_host *host, unsigned char mode,
if (pwr == 0) {
sdhci_writeb(host, 0, SDHCI_POWER_CONTROL);
+ if (host->ops->check_power_status)
+ host->ops->check_power_status(host, REQ_BUS_OFF);
if (host->quirks2 & SDHCI_QUIRK2_CARD_ON_NEEDS_BUS_ON)
sdhci_runtime_pm_bus_off(host);
} else {
@@ -1446,20 +1531,27 @@ void sdhci_set_power_noreg(struct sdhci_host *host, unsigned char mode,
* Spec says that we should clear the power reg before setting
* a new value. Some controllers don't seem to like this though.
*/
- if (!(host->quirks & SDHCI_QUIRK_SINGLE_POWER_WRITE))
+ if (!(host->quirks & SDHCI_QUIRK_SINGLE_POWER_WRITE)) {
sdhci_writeb(host, 0, SDHCI_POWER_CONTROL);
-
+ if (host->ops->check_power_status)
+ host->ops->check_power_status(host, REQ_BUS_OFF);
+ }
/*
* At least the Marvell CaFe chip gets confused if we set the
* voltage and set turn on power at the same time, so set the
* voltage first.
*/
- if (host->quirks & SDHCI_QUIRK_NO_SIMULT_VDD_AND_POWER)
+ if (host->quirks & SDHCI_QUIRK_NO_SIMULT_VDD_AND_POWER) {
sdhci_writeb(host, pwr, SDHCI_POWER_CONTROL);
+ if (host->ops->check_power_status)
+ host->ops->check_power_status(host, REQ_BUS_ON);
+ }
pwr |= SDHCI_POWER_ON;
sdhci_writeb(host, pwr, SDHCI_POWER_CONTROL);
+ if (host->ops->check_power_status)
+ host->ops->check_power_status(host, REQ_BUS_ON);
if (host->quirks2 & SDHCI_QUIRK2_CARD_ON_NEEDS_BUS_ON)
sdhci_runtime_pm_bus_on(host);
@@ -1507,15 +1599,91 @@ static int sdhci_disable(struct mmc_host *mmc)
{
struct sdhci_host *host = mmc_priv(mmc);
- if (host->cpu_dma_latency_us)
- pm_qos_update_request(&host->pm_qos_req_dma,
+ if (host->cpu_dma_latency_us) {
+ /*
+ * In performance mode, release QoS vote after a timeout to
+ * make sure back-to-back requests don't suffer from latencies
+ * that are involved to wake CPU from low power modes in cases
+ * where the CPU goes into low power mode as soon as QoS vote is
+ * released.
+ */
+ if (host->power_policy == SDHCI_PERFORMANCE_MODE)
+ pm_qos_update_request_timeout(&host->pm_qos_req_dma,
+ host->cpu_dma_latency_us,
+ host->pm_qos_timeout_us);
+ else
+ pm_qos_update_request(&host->pm_qos_req_dma,
PM_QOS_DEFAULT_VALUE);
+ }
+
if (host->ops->platform_bus_voting)
host->ops->platform_bus_voting(host, 0);
return 0;
}
+static inline void sdhci_update_power_policy(struct sdhci_host *host,
+ enum sdhci_power_policy policy)
+{
+ host->power_policy = policy;
+}
+
+static int sdhci_notify_load(struct mmc_host *mmc, enum mmc_load state)
+{
+ int err = 0;
+ struct sdhci_host *host = mmc_priv(mmc);
+
+ switch (state) {
+ case MMC_LOAD_HIGH:
+ sdhci_update_power_policy(host, SDHCI_PERFORMANCE_MODE);
+ break;
+ case MMC_LOAD_LOW:
+ sdhci_update_power_policy(host, SDHCI_POWER_SAVE_MODE);
+ break;
+ default:
+ err = -EINVAL;
+ break;
+ }
+
+ return err;
+}
+
+static bool sdhci_check_state(struct sdhci_host *host)
+{
+ if (!host->clock || !host->pwr)
+ return true;
+ else
+ return false;
+}
+
+static bool sdhci_check_auto_tuning(struct sdhci_host *host,
+ struct mmc_command *cmd)
+{
+ if (((cmd->opcode != MMC_READ_SINGLE_BLOCK) &&
+ (cmd->opcode != MMC_READ_MULTIPLE_BLOCK) &&
+ (cmd->opcode != SD_IO_RW_EXTENDED)) || (host->clock < 100000000))
+ return false;
+ else if (host->mmc->ios.timing == MMC_TIMING_MMC_HS200 ||
+ host->mmc->ios.timing == MMC_TIMING_UHS_SDR104)
+ return true;
+ else
+ return false;
+}
+
+static int sdhci_get_tuning_cmd(struct sdhci_host *host)
+{
+ if (!host->mmc || !host->mmc->card)
+ return 0;
+ /*
+ * If we are here, all conditions have already been true
+ * and the card can either be an eMMC or SD/SDIO
+ */
+ if (mmc_card_mmc(host->mmc->card))
+ return MMC_SEND_TUNING_BLOCK_HS200;
+ else
+ return MMC_SEND_TUNING_BLOCK;
+}
+
static void sdhci_request(struct mmc_host *mmc, struct mmc_request *mrq)
{
struct sdhci_host *host;
@@ -1524,8 +1692,32 @@ static void sdhci_request(struct mmc_host *mmc, struct mmc_request *mrq)
host = mmc_priv(mmc);
- /* Firstly check card presence */
+ if (sdhci_check_state(host)) {
+ sdhci_dump_state(host);
+ WARN(1, "sdhci in bad state");
+ mrq->cmd->error = -EIO;
+ if (mrq->data)
+ mrq->data->error = -EIO;
+ tasklet_schedule(&host->finish_tasklet);
+ return;
+ }
+
+ /*
+ * Firstly check card presence from cd-gpio. The return could
+ * be one of the following possibilities:
+ * negative: cd-gpio is not available
+ * zero: cd-gpio is used, and card is removed
+ * one: cd-gpio is used, and card is present
+ */
present = mmc->ops->get_cd(mmc);
+ if (present < 0) {
+ /* If polling, assume that the card is always present. */
+ if (host->quirks & SDHCI_QUIRK_BROKEN_CARD_DETECTION)
+ present = 1;
+ else
+ present = sdhci_readl(host, SDHCI_PRESENT_STATE) &
+ SDHCI_CARD_PRESENT;
+ }
spin_lock_irqsave(&host->lock, flags);
@@ -1546,6 +1738,15 @@ static void sdhci_request(struct mmc_host *mmc, struct mmc_request *mrq)
mrq->cmd->error = -ENOMEDIUM;
sdhci_finish_mrq(host, mrq);
} else {
+ if (host->ops->config_auto_tuning_cmd) {
+ if (sdhci_check_auto_tuning(host, mrq->cmd))
+ host->ops->config_auto_tuning_cmd(host, true,
+ sdhci_get_tuning_cmd(host));
+ else
+ host->ops->config_auto_tuning_cmd(host, false,
+ sdhci_get_tuning_cmd(host));
+ }
+
if (mrq->sbc && !(host->flags & SDHCI_AUTO_CMD23))
sdhci_send_command(host, mrq->sbc);
else
@@ -1607,11 +1808,9 @@ static void sdhci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
struct sdhci_host *host = mmc_priv(mmc);
unsigned long flags;
u8 ctrl;
-
- spin_lock_irqsave(&host->lock, flags);
+ int ret;
if (host->flags & SDHCI_DEVICE_DEAD) {
- spin_unlock_irqrestore(&host->lock, flags);
if (!IS_ERR(mmc->supply.vmmc) &&
ios->power_mode == MMC_POWER_OFF)
mmc_regulator_set_ocr(mmc, mmc->supply.vmmc, 0);
@@ -1623,6 +1822,25 @@ static void sdhci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
!(host->quirks2 & SDHCI_QUIRK2_PRESET_VALUE_BROKEN))
sdhci_enable_preset_value(host, false);
+ /*
+ * The controller clocks may be off during power-up and we may end up
+ * enabling card clock before giving power to the card. Hence, during
+ * MMC_POWER_UP enable the controller clock and turn-on the regulators.
+ * The mmc_power_up would provide the necessary delay before turning on
+ * the clocks to the card.
+ */
+ if (ios->power_mode & MMC_POWER_UP) {
+ if (host->ops->enable_controller_clock) {
+ ret = host->ops->enable_controller_clock(host);
+ if (ret) {
+ pr_err("%s: enabling controller clock: failed: %d\n",
+ mmc_hostname(host->mmc), ret);
+ } else {
+ sdhci_set_power(host, ios->power_mode, ios->vdd);
+ }
+ }
+ }
+
spin_lock_irqsave(&host->lock, flags);
if (!ios->clock || ios->clock != host->clock) {
spin_unlock_irqrestore(&host->lock, flags);
@@ -1647,9 +1865,13 @@ static void sdhci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
if (host->ops->set_power)
host->ops->set_power(host, ios->power_mode, ios->vdd);
else
- if (ios->power_mode & (MMC_POWER_UP | MMC_POWER_ON))
+ if (!host->ops->enable_controller_clock && (ios->power_mode &
+ (MMC_POWER_UP |
+ MMC_POWER_ON)))
sdhci_set_power(host, ios->power_mode, ios->vdd);
+ spin_lock_irqsave(&host->lock, flags);
+
if (host->ops->platform_send_init_74_clocks)
host->ops->platform_send_init_74_clocks(host, ios->power_mode);
@@ -1716,7 +1938,8 @@ static void sdhci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
sdhci_writeb(host, ctrl, SDHCI_HOST_CONTROL);
/* Re-enable SD Clock */
- host->ops->set_clock(host, host->clock);
+ if (ios->clock)
+ host->ops->set_clock(host, host->clock);
}
/* Reset SD Clock Enable */
@@ -1743,7 +1966,8 @@ static void sdhci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
}
/* Re-enable SD Clock */
- host->ops->set_clock(host, host->clock);
+ if (ios->clock)
+ host->ops->set_clock(host, host->clock);
} else
sdhci_writeb(host, ctrl, SDHCI_HOST_CONTROL);
@@ -1907,6 +2131,8 @@ static int sdhci_start_signal_voltage_switch(struct mmc_host *mmc,
/* Set 1.8V Signal Enable in the Host Control2 register to 0 */
ctrl &= ~SDHCI_CTRL_VDD_180;
sdhci_writew(host, ctrl, SDHCI_HOST_CONTROL2);
+ if (host->ops->check_power_status)
+ host->ops->check_power_status(host, REQ_IO_HIGH);
if (!IS_ERR(mmc->supply.vqmmc)) {
ret = mmc_regulator_set_vqmmc(mmc, ios);
@@ -1946,6 +2172,8 @@ static int sdhci_start_signal_voltage_switch(struct mmc_host *mmc,
*/
ctrl |= SDHCI_CTRL_VDD_180;
sdhci_writew(host, ctrl, SDHCI_HOST_CONTROL2);
+ if (host->ops->check_power_status)
+ host->ops->check_power_status(host, REQ_IO_LOW);
/* Some controller need to do more when switching */
if (host->ops->voltage_switch)
@@ -2227,6 +2455,9 @@ static void sdhci_enable_preset_value(struct sdhci_host *host, bool enable)
if (host->version < SDHCI_SPEC_300)
return;
+ if (host->quirks2 & SDHCI_QUIRK2_BROKEN_PRESET_VALUE)
+ return;
+
/*
* We only enable or disable Preset Value if they are not already
* enabled or disabled respectively. Otherwise, we bail out.
@@ -2340,6 +2571,7 @@ static const struct mmc_host_ops sdhci_ops = {
.card_busy = sdhci_card_busy,
.enable = sdhci_enable,
.disable = sdhci_disable,
+ .notify_load = sdhci_notify_load,
};
/*****************************************************************************\
@@ -2421,6 +2653,7 @@ static bool sdhci_request_done(struct sdhci_host *host)
sdhci_led_deactivate(host);
host->mrqs_done[i] = NULL;
+ host->auto_cmd_err_sts = 0;
mmiowb();
spin_unlock_irqrestore(&host->lock, flags);
@@ -2476,6 +2709,11 @@ static void sdhci_timeout_data_timer(unsigned long data)
sdhci_dumpregs(host);
if (host->data) {
+ pr_info("%s: bytes to transfer: %d transferred: %d\n",
+ mmc_hostname(host->mmc),
+ (host->data->blksz * host->data->blocks),
+ (sdhci_readw(host, SDHCI_BLOCK_SIZE) & 0xFFF) *
+ sdhci_readw(host, SDHCI_BLOCK_COUNT));
host->data->error = -ETIMEDOUT;
sdhci_finish_data(host);
} else if (host->data_cmd) {
@@ -2499,6 +2737,7 @@ static void sdhci_timeout_data_timer(unsigned long data)
static void sdhci_cmd_irq(struct sdhci_host *host, u32 intmask)
{
+ u16 auto_cmd_status;
if (!host->cmd) {
/*
* SDHCI recovers from errors by resetting the cmd and data
@@ -2514,12 +2753,27 @@ static void sdhci_cmd_irq(struct sdhci_host *host, u32 intmask)
}
if (intmask & (SDHCI_INT_TIMEOUT | SDHCI_INT_CRC |
- SDHCI_INT_END_BIT | SDHCI_INT_INDEX)) {
+ SDHCI_INT_END_BIT | SDHCI_INT_INDEX |
+ SDHCI_INT_AUTO_CMD_ERR)) {
if (intmask & SDHCI_INT_TIMEOUT)
host->cmd->error = -ETIMEDOUT;
else
host->cmd->error = -EILSEQ;
+ if (intmask & SDHCI_INT_AUTO_CMD_ERR) {
+ auto_cmd_status = host->auto_cmd_err_sts;
+ pr_err("%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 |
+ SDHCI_AUTO_CMD_INDEX_ERR |
+ SDHCI_AUTO_CMD_ENDBIT_ERR))
+ host->cmd->error = -EIO;
+ else if (auto_cmd_status & SDHCI_AUTO_CMD_TIMEOUT_ERR)
+ host->cmd->error = -ETIMEDOUT;
+ else if (auto_cmd_status & SDHCI_AUTO_CMD_CRC_ERR)
+ host->cmd->error = -EILSEQ;
+ }
+
/*
* If this command initiates a data phase and a response
* CRC error is signalled, the card can start transferring
@@ -2581,6 +2835,7 @@ static void sdhci_adma_show_error(struct sdhci_host *host) { }
static void sdhci_data_irq(struct sdhci_host *host, u32 intmask)
{
u32 command;
+ bool pr_msg = false;
/* CMD19 generates _only_ Buffer Read Ready interrupt */
if (intmask & SDHCI_INT_DATA_AVAIL) {
@@ -2621,6 +2876,9 @@ static void sdhci_data_irq(struct sdhci_host *host, u32 intmask)
sdhci_finish_mrq(host, data_cmd->mrq);
return;
}
+ if (host->quirks2 &
+ SDHCI_QUIRK2_IGNORE_DATATOUT_FOR_R1BCMD)
+ return;
}
/*
@@ -2653,10 +2911,25 @@ static void sdhci_data_irq(struct sdhci_host *host, u32 intmask)
if (host->ops->adma_workaround)
host->ops->adma_workaround(host, intmask);
}
-
- if (host->data->error)
+ if (host->data->error) {
+ if (intmask & (SDHCI_INT_DATA_CRC | SDHCI_INT_DATA_TIMEOUT)) {
+ command = SDHCI_GET_CMD(sdhci_readw(host,
+ SDHCI_COMMAND));
+ if ((command != MMC_SEND_TUNING_BLOCK_HS200) &&
+ (command != MMC_SEND_TUNING_BLOCK))
+ pr_msg = true;
+ } else {
+ pr_msg = true;
+ }
+ if (pr_msg) {
+ pr_err("%s: data txfr (0x%08x) error: %d after %lld ms\n",
+ mmc_hostname(host->mmc), intmask,
+ host->data->error, ktime_to_ms(ktime_sub(
+ ktime_get(), host->data_start_time)));
+ sdhci_dumpregs(host);
+ }
sdhci_finish_data(host);
- else {
+ } else {
if (intmask & (SDHCI_INT_DATA_AVAIL | SDHCI_INT_SPACE_AVAIL))
sdhci_transfer_pio(host);
@@ -2723,6 +2996,9 @@ static irqreturn_t sdhci_irq(int irq, void *dev_id)
}
do {
+ if (intmask & SDHCI_INT_AUTO_CMD_ERR)
+ host->auto_cmd_err_sts = sdhci_readw(host,
+ SDHCI_AUTO_CMD_ERR);
/* Clear selected interrupts. */
mask = intmask & (SDHCI_INT_CMD_MASK | SDHCI_INT_DATA_MASK |
SDHCI_INT_BUS_POWER);
@@ -3052,6 +3328,8 @@ struct sdhci_host *sdhci_alloc_host(struct device *dev,
host->flags = SDHCI_SIGNALING_330;
+ spin_lock_init(&host->lock);
+
return host;
}
@@ -3542,8 +3820,6 @@ int sdhci_setup_host(struct sdhci_host *host)
if (mmc->caps2 & MMC_CAP2_HSX00_1_2V)
host->flags |= SDHCI_SIGNALING_120;
- spin_lock_init(&host->lock);
-
/*
* Maximum number of segments. Depends on if the hardware
* can do scatter/gather or not.
@@ -3656,14 +3932,33 @@ int __sdhci_add_host(struct sdhci_host *host)
mmiowb();
- if (host->cpu_dma_latency_us)
+ if (host->cpu_dma_latency_us) {
+ host->pm_qos_timeout_us = 10000; /* default value */
pm_qos_add_request(&host->pm_qos_req_dma,
PM_QOS_CPU_DMA_LATENCY, PM_QOS_DEFAULT_VALUE);
+ host->pm_qos_tout.show = show_sdhci_pm_qos_tout;
+ host->pm_qos_tout.store = store_sdhci_pm_qos_tout;
+ sysfs_attr_init(&host->pm_qos_tout.attr);
+ host->pm_qos_tout.attr.name = "pm_qos_unvote_delay";
+ host->pm_qos_tout.attr.mode = S_IRUGO | S_IWUSR;
+ ret = device_create_file(mmc_dev(mmc), &host->pm_qos_tout);
+ if (ret)
+ pr_err("%s: cannot create pm_qos_unvote_delay %d\n",
+ mmc_hostname(mmc), ret);
+
+ }
+
ret = mmc_add_host(mmc);
if (ret)
goto unled;
+ if (host->quirks2 & SDHCI_QUIRK2_IGN_DATA_END_BIT_ERROR) {
+ host->ier = (host->ier & ~SDHCI_INT_DATA_END_BIT);
+ sdhci_writel(host, host->ier, SDHCI_INT_ENABLE);
+ sdhci_writel(host, host->ier, SDHCI_SIGNAL_ENABLE);
+ }
+
pr_info("%s: SDHCI controller on %s [%s] using %s\n",
mmc_hostname(mmc), host->hw_name, dev_name(mmc_dev(mmc)),
(host->flags & SDHCI_USE_ADMA) ?
diff --git a/drivers/mmc/host/sdhci.h b/drivers/mmc/host/sdhci.h
index a8d4cfa..5e809d4 100644
--- a/drivers/mmc/host/sdhci.h
+++ b/drivers/mmc/host/sdhci.h
@@ -142,14 +142,16 @@
#define SDHCI_INT_DATA_CRC 0x00200000
#define SDHCI_INT_DATA_END_BIT 0x00400000
#define SDHCI_INT_BUS_POWER 0x00800000
-#define SDHCI_INT_ACMD12ERR 0x01000000
+#define SDHCI_INT_AUTO_CMD_ERR 0x01000000
#define SDHCI_INT_ADMA_ERROR 0x02000000
#define SDHCI_INT_NORMAL_MASK 0x00007FFF
#define SDHCI_INT_ERROR_MASK 0xFFFF8000
#define SDHCI_INT_CMD_MASK (SDHCI_INT_RESPONSE | SDHCI_INT_TIMEOUT | \
- SDHCI_INT_CRC | SDHCI_INT_END_BIT | SDHCI_INT_INDEX)
+ SDHCI_INT_CRC | SDHCI_INT_END_BIT | SDHCI_INT_INDEX | \
+ SDHCI_INT_AUTO_CMD_ERR)
+
#define SDHCI_INT_DATA_MASK (SDHCI_INT_DATA_END | SDHCI_INT_DMA_END | \
SDHCI_INT_DATA_AVAIL | SDHCI_INT_SPACE_AVAIL | \
SDHCI_INT_DATA_TIMEOUT | SDHCI_INT_DATA_CRC | \
@@ -157,7 +159,13 @@
SDHCI_INT_BLK_GAP)
#define SDHCI_INT_ALL_MASK ((unsigned int)-1)
-#define SDHCI_ACMD12_ERR 0x3C
+#define SDHCI_AUTO_CMD_ERR 0x3C
+#define SDHCI_AUTO_CMD12_NOT_EXEC 0x0001
+#define SDHCI_AUTO_CMD_TIMEOUT_ERR 0x0002
+#define SDHCI_AUTO_CMD_CRC_ERR 0x0004
+#define SDHCI_AUTO_CMD_ENDBIT_ERR 0x0008
+#define SDHCI_AUTO_CMD_INDEX_ERR 0x0010
+#define SDHCI_AUTO_CMD12_NOT_ISSUED 0x0080
#define SDHCI_HOST_CONTROL2 0x3E
#define SDHCI_CTRL_UHS_MASK 0x0007
@@ -329,6 +337,11 @@ enum sdhci_cookie {
COOKIE_MAPPED, /* mapped by sdhci_prepare_data() */
};
+enum sdhci_power_policy {
+ SDHCI_PERFORMANCE_MODE,
+ SDHCI_POWER_SAVE_MODE,
+};
+
struct sdhci_host {
/* Data set by hardware interface driver */
const char *hw_name; /* Hardware bus name */
@@ -438,6 +451,47 @@ struct sdhci_host {
*/
#define SDHCI_QUIRK2_SLOW_INT_CLR (1<<18)
+#define SDHCI_QUIRK2_ALWAYS_USE_BASE_CLOCK (1<<19)
+
+/*
+ * Ignore data timeout error for R1B commands as there will be no
+ * data associated and the busy timeout value for these commands
+ * could be lager than the maximum timeout value that controller
+ * can handle.
+ */
+#define SDHCI_QUIRK2_IGNORE_DATATOUT_FOR_R1BCMD (1<<21)
+
+/*
+ * The preset value registers are not properly initialized by
+ * some hardware and hence preset value must not be enabled for
+ * such controllers.
+ */
+#define SDHCI_QUIRK2_BROKEN_PRESET_VALUE (1<<22)
+/*
+ * Some controllers define the usage of 0xF in data timeout counter
+ * register (0x2E) which is actually a reserved bit as per
+ * specification.
+ */
+#define SDHCI_QUIRK2_USE_RESERVED_MAX_TIMEOUT (1<<23)
+/*
+ * This is applicable for controllers that advertize timeout clock
+ * value in capabilities register (bit 5-0) as just 50MHz whereas the
+ * base clock frequency is 200MHz. So, the controller internally
+ * multiplies the value in timeout control register by 4 with the
+ * assumption that driver always uses fixed timeout clock value from
+ * capabilities register to calculate the timeout. But when the driver
+ * uses SDHCI_QUIRK2_ALWAYS_USE_BASE_CLOCK base clock frequency is directly
+ * controller by driver and it's rate varies upto max. 200MHz. This new quirk
+ * will be used in such cases to avoid controller mulplication when timeout is
+ * calculated based on the base clock.
+ */
+#define SDHCI_QUIRK2_DIVIDE_TOUT_BY_4 (1 << 23)
+
+/*
+ * Some SDHC controllers are unable to handle data-end bit error in
+ * 1-bit mode of SDIO.
+ */
+#define SDHCI_QUIRK2_IGN_DATA_END_BIT_ERROR (1<<24)
int irq; /* Device IRQ */
void __iomem *ioaddr; /* Mapped address */
@@ -503,6 +557,10 @@ struct sdhci_host {
size_t adma_table_sz; /* ADMA descriptor table size */
size_t align_buffer_sz; /* Bounce buffer size */
+ unsigned int adma_desc_sz; /* ADMA descriptor table size */
+ unsigned int align_buf_sz; /* Bounce buffer size */
+ unsigned int adma_max_desc; /* Max ADMA descriptos (max sg segments) */
+
dma_addr_t adma_addr; /* Mapped ADMA descr. table */
dma_addr_t align_addr; /* Mapped bounce buffer */
@@ -540,6 +598,14 @@ struct sdhci_host {
unsigned int cpu_dma_latency_us;
struct pm_qos_request pm_qos_req_dma;
+ ktime_t data_start_time;
+
+ unsigned int pm_qos_timeout_us; /* timeout for PM QoS request */
+ struct device_attribute pm_qos_tout;
+
+ enum sdhci_power_policy power_policy;
+
+ u32 auto_cmd_err_sts;
unsigned long private[0] ____cacheline_aligned;
};
@@ -575,10 +641,19 @@ struct sdhci_ops {
void (*hw_reset)(struct sdhci_host *host);
void (*adma_workaround)(struct sdhci_host *host, u32 intmask);
unsigned int (*get_max_segments)(void);
+#define REQ_BUS_OFF (1 << 0)
+#define REQ_BUS_ON (1 << 1)
+#define REQ_IO_LOW (1 << 2)
+#define REQ_IO_HIGH (1 << 3)
void (*card_event)(struct sdhci_host *host);
void (*platform_bus_voting)(struct sdhci_host *host, u32 enable);
void (*toggle_cdr)(struct sdhci_host *host, bool enable);
- void (*check_power_status)(struct sdhci_host *host);
+ void (*check_power_status)(struct sdhci_host *host, u32 req_type);
+ int (*config_auto_tuning_cmd)(struct sdhci_host *host,
+ bool enable,
+ u32 type);
+ int (*enable_controller_clock)(struct sdhci_host *host);
+ void (*dump_vendor_regs)(struct sdhci_host *host);
void (*voltage_switch)(struct sdhci_host *host);
int (*select_drive_strength)(struct sdhci_host *host,
struct mmc_card *card,
diff --git a/drivers/pci/host/pci-msm.c b/drivers/pci/host/pci-msm.c
index 34764de..1e45c73 100644
--- a/drivers/pci/host/pci-msm.c
+++ b/drivers/pci/host/pci-msm.c
@@ -5577,7 +5577,7 @@ static irqreturn_t handle_global_irq(int irq, void *data)
handle_aer_irq(irq, data);
break;
default:
- PCIE_ERR(dev,
+ PCIE_DUMP(dev,
"PCIe: RC%d: Unexpected event %d is caught!\n",
dev->rc_idx, i);
}
diff --git a/drivers/power/supply/power_supply_sysfs.c b/drivers/power/supply/power_supply_sysfs.c
index f6fa78f..46dc148 100644
--- a/drivers/power/supply/power_supply_sysfs.c
+++ b/drivers/power/supply/power_supply_sysfs.c
@@ -278,6 +278,7 @@ static struct device_attribute power_supply_attrs[] = {
POWER_SUPPLY_ATTR(dp_dm),
POWER_SUPPLY_ATTR(input_current_limited),
POWER_SUPPLY_ATTR(input_current_now),
+ POWER_SUPPLY_ATTR(charge_qnovo_enable),
POWER_SUPPLY_ATTR(current_qnovo),
POWER_SUPPLY_ATTR(voltage_qnovo),
POWER_SUPPLY_ATTR(rerun_aicl),
@@ -306,6 +307,7 @@ static struct device_attribute power_supply_attrs[] = {
POWER_SUPPLY_ATTR(die_health),
POWER_SUPPLY_ATTR(connector_health),
POWER_SUPPLY_ATTR(ctm_current_max),
+ POWER_SUPPLY_ATTR(hw_current_max),
/* Local extensions of type int64_t */
POWER_SUPPLY_ATTR(charge_counter_ext),
/* Properties of type `const char *' */
diff --git a/drivers/power/supply/qcom/battery.c b/drivers/power/supply/qcom/battery.c
index 3659b92..b985ecd 100644
--- a/drivers/power/supply/qcom/battery.c
+++ b/drivers/power/supply/qcom/battery.c
@@ -13,6 +13,7 @@
#define pr_fmt(fmt) "QCOM-BATT: %s: " fmt, __func__
#include <linux/device.h>
+#include <linux/delay.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/regmap.h>
@@ -24,7 +25,7 @@
#include <linux/printk.h>
#include <linux/pm_wakeup.h>
#include <linux/slab.h>
-#include "pmic-voter.h"
+#include <linux/pmic-voter.h>
#define DRV_MAJOR_VERSION 1
#define DRV_MINOR_VERSION 0
@@ -36,6 +37,7 @@
#define PL_HW_ABSENT_VOTER "PL_HW_ABSENT_VOTER"
#define PL_VOTER "PL_VOTER"
#define RESTRICT_CHG_VOTER "RESTRICT_CHG_VOTER"
+#define ICL_CHANGE_VOTER "ICL_CHANGE_VOTER"
struct pl_data {
int pl_mode;
@@ -49,14 +51,16 @@ struct pl_data {
struct votable *pl_disable_votable;
struct votable *pl_awake_votable;
struct votable *hvdcp_hw_inov_dis_votable;
+ struct votable *usb_icl_votable;
struct work_struct status_change_work;
struct work_struct pl_disable_forever_work;
struct delayed_work pl_taper_work;
struct power_supply *main_psy;
struct power_supply *pl_psy;
struct power_supply *batt_psy;
+ struct power_supply *usb_psy;
int charge_type;
- int main_settled_ua;
+ int total_settled_ua;
int pl_settled_ua;
struct class qcom_batt_class;
struct wakeup_source *pl_ws;
@@ -92,15 +96,10 @@ enum {
********/
static void split_settled(struct pl_data *chip)
{
- int slave_icl_pct;
+ int slave_icl_pct, total_current_ua;
int slave_ua = 0, main_settled_ua = 0;
union power_supply_propval pval = {0, };
- int rc;
-
- /* TODO some parallel chargers do not have a fine ICL resolution. For
- * them implement a psy interface which returns the closest lower ICL
- * for desired split
- */
+ int rc, total_settled_ua = 0;
if ((chip->pl_mode != POWER_SUPPLY_PL_USBIN_USBIN)
&& (chip->pl_mode != POWER_SUPPLY_PL_USBIN_USBIN_EXT))
@@ -122,12 +121,31 @@ static void split_settled(struct pl_data *chip)
slave_icl_pct = max(0, chip->slave_pct - 10);
slave_ua = ((main_settled_ua + chip->pl_settled_ua)
* slave_icl_pct) / 100;
+ total_settled_ua = main_settled_ua + chip->pl_settled_ua;
}
- /* ICL_REDUCTION on main could be 0mA when pl is disabled */
- pval.intval = slave_ua;
+ total_current_ua = get_effective_result_locked(chip->usb_icl_votable);
+ if (total_current_ua < 0) {
+ if (!chip->usb_psy)
+ chip->usb_psy = power_supply_get_by_name("usb");
+ if (!chip->usb_psy) {
+ pr_err("Couldn't get usbpsy while splitting settled\n");
+ return;
+ }
+ /* no client is voting, so get the total current from charger */
+ rc = power_supply_get_property(chip->usb_psy,
+ POWER_SUPPLY_PROP_HW_CURRENT_MAX, &pval);
+ if (rc < 0) {
+ pr_err("Couldn't get max current rc=%d\n", rc);
+ return;
+ }
+ total_current_ua = pval.intval;
+ }
+
+ pval.intval = total_current_ua - slave_ua;
+ /* Set ICL on main charger */
rc = power_supply_set_property(chip->main_psy,
- POWER_SUPPLY_PROP_ICL_REDUCTION, &pval);
+ POWER_SUPPLY_PROP_CURRENT_MAX, &pval);
if (rc < 0) {
pr_err("Couldn't change slave suspend state rc=%d\n", rc);
return;
@@ -142,10 +160,12 @@ static void split_settled(struct pl_data *chip)
return;
}
- /* main_settled_ua represents the total capability of adapter */
- if (!chip->main_settled_ua)
- chip->main_settled_ua = main_settled_ua;
+ chip->total_settled_ua = total_settled_ua;
chip->pl_settled_ua = slave_ua;
+
+ pl_dbg(chip, PR_PARALLEL,
+ "Split total_current_ua=%d main_settled_ua=%d slave_ua=%d\n",
+ total_current_ua, main_settled_ua, slave_ua);
}
static ssize_t version_show(struct class *c, struct class_attribute *attr,
@@ -213,6 +233,10 @@ static ssize_t restrict_chg_store(struct class *c, struct class_attribute *attr,
chip->restricted_charging_enabled = !!val;
+ /* disable parallel charger in case of restricted charging */
+ vote(chip->pl_disable_votable, RESTRICT_CHG_VOTER,
+ chip->restricted_charging_enabled, 0);
+
vote(chip->fcc_votable, RESTRICT_CHG_VOTER,
chip->restricted_charging_enabled,
chip->restricted_current);
@@ -487,6 +511,59 @@ static int pl_fv_vote_callback(struct votable *votable, void *data,
return 0;
}
+#define ICL_STEP_UV 25000
+static int usb_icl_vote_callback(struct votable *votable, void *data,
+ int icl_ua, const char *client)
+{
+ int rc;
+ struct pl_data *chip = data;
+ union power_supply_propval pval = {0, };
+
+ if (!chip->main_psy)
+ return 0;
+
+ if (client == NULL)
+ icl_ua = INT_MAX;
+
+ /*
+ * Disable parallel for new ICL vote - the call to split_settled will
+ * ensure that all the input current limit gets assigned to the main
+ * charger.
+ */
+ vote(chip->pl_disable_votable, ICL_CHANGE_VOTER, true, 0);
+
+ /* rerun AICL */
+ /* get the settled current */
+ rc = power_supply_get_property(chip->main_psy,
+ POWER_SUPPLY_PROP_INPUT_CURRENT_SETTLED,
+ &pval);
+ if (rc < 0) {
+ pr_err("Couldn't get aicl settled value rc=%d\n", rc);
+ return rc;
+ }
+
+ /* rerun AICL if new ICL is above settled ICL */
+ if (icl_ua > pval.intval) {
+ /* set a lower ICL */
+ pval.intval = max(pval.intval - ICL_STEP_UV, ICL_STEP_UV);
+ power_supply_set_property(chip->main_psy,
+ POWER_SUPPLY_PROP_CURRENT_MAX,
+ &pval);
+ /* wait for ICL change */
+ msleep(100);
+
+ pval.intval = icl_ua;
+ power_supply_set_property(chip->main_psy,
+ POWER_SUPPLY_PROP_CURRENT_MAX,
+ &pval);
+ /* wait for ICL change */
+ msleep(100);
+ }
+ vote(chip->pl_disable_votable, ICL_CHANGE_VOTER, false, 0);
+
+ return 0;
+}
+
static void pl_disable_forever_work(struct work_struct *work)
{
struct pl_data *chip = container_of(work,
@@ -508,7 +585,7 @@ static int pl_disable_vote_callback(struct votable *votable,
int rc;
chip->taper_pct = 100;
- chip->main_settled_ua = 0;
+ chip->total_settled_ua = 0;
chip->pl_settled_ua = 0;
if (!pl_disable) { /* enable */
@@ -596,13 +673,15 @@ static int pl_awake_vote_callback(struct votable *votable,
static bool is_main_available(struct pl_data *chip)
{
- if (!chip->main_psy)
- chip->main_psy = power_supply_get_by_name("main");
+ if (chip->main_psy)
+ return true;
- if (!chip->main_psy)
- return false;
+ chip->main_psy = power_supply_get_by_name("main");
- return true;
+ if (chip->main_psy)
+ rerun_election(chip->usb_icl_votable);
+
+ return !!chip->main_psy;
}
static bool is_batt_available(struct pl_data *chip)
@@ -711,6 +790,7 @@ static void handle_main_charge_type(struct pl_data *chip)
static void handle_settled_icl_change(struct pl_data *chip)
{
union power_supply_propval pval = {0, };
+ int new_total_settled_ua;
int rc;
if (get_effective_result(chip->pl_disable_votable))
@@ -730,9 +810,15 @@ static void handle_settled_icl_change(struct pl_data *chip)
return;
}
+ new_total_settled_ua = pval.intval + chip->pl_settled_ua;
+ pl_dbg(chip, PR_PARALLEL,
+ "total_settled_ua=%d settled_ua=%d new_total_settled_ua=%d\n",
+ chip->total_settled_ua, pval.intval,
+ new_total_settled_ua);
+
/* If ICL change is small skip splitting */
- if (abs((chip->main_settled_ua - chip->pl_settled_ua)
- - pval.intval) > MIN_ICL_CHANGE_DELTA_UA)
+ if (abs(new_total_settled_ua - chip->total_settled_ua)
+ > MIN_ICL_CHANGE_DELTA_UA)
split_settled(chip);
} else {
rerun_election(chip->fcc_votable);
@@ -855,6 +941,14 @@ static int pl_init(void)
goto destroy_votable;
}
+ chip->usb_icl_votable = create_votable("USB_ICL", VOTE_MIN,
+ usb_icl_vote_callback,
+ chip);
+ if (IS_ERR(chip->usb_icl_votable)) {
+ rc = PTR_ERR(chip->usb_icl_votable);
+ goto destroy_votable;
+ }
+
chip->pl_disable_votable = create_votable("PL_DISABLE", VOTE_SET_ANY,
pl_disable_vote_callback,
chip);
@@ -909,6 +1003,7 @@ static int pl_init(void)
destroy_votable(chip->pl_disable_votable);
destroy_votable(chip->fv_votable);
destroy_votable(chip->fcc_votable);
+ destroy_votable(chip->usb_icl_votable);
release_wakeup_source:
wakeup_source_unregister(chip->pl_ws);
cleanup:
diff --git a/drivers/power/supply/qcom/fg-core.h b/drivers/power/supply/qcom/fg-core.h
index c0ba5a9..48fe04f 100644
--- a/drivers/power/supply/qcom/fg-core.h
+++ b/drivers/power/supply/qcom/fg-core.h
@@ -29,7 +29,7 @@
#include <linux/string_helpers.h>
#include <linux/types.h>
#include <linux/uaccess.h>
-#include "pmic-voter.h"
+#include <linux/pmic-voter.h>
#define fg_dbg(chip, reason, fmt, ...) \
do { \
@@ -46,10 +46,13 @@
&& (value) <= (right)))
/* Awake votable reasons */
-#define SRAM_READ "fg_sram_read"
-#define SRAM_WRITE "fg_sram_write"
-#define PROFILE_LOAD "fg_profile_load"
-#define DELTA_SOC "fg_delta_soc"
+#define SRAM_READ "fg_sram_read"
+#define SRAM_WRITE "fg_sram_write"
+#define PROFILE_LOAD "fg_profile_load"
+#define DELTA_SOC "fg_delta_soc"
+
+/* Delta BSOC votable reasons */
+#define DELTA_BSOC_IRQ_VOTER "fg_delta_bsoc_irq"
#define DEBUG_PRINT_BUFFER_SIZE 64
/* 3 byte address + 1 space character */
@@ -159,6 +162,7 @@ enum fg_sram_param_id {
FG_SRAM_ESR_TIMER_DISCHG_INIT,
FG_SRAM_ESR_TIMER_CHG_MAX,
FG_SRAM_ESR_TIMER_CHG_INIT,
+ FG_SRAM_ESR_PULSE_THRESH,
FG_SRAM_SYS_TERM_CURR,
FG_SRAM_CHG_TERM_CURR,
FG_SRAM_DELTA_MSOC_THR,
@@ -250,6 +254,8 @@ struct fg_dt_props {
int esr_tight_lt_flt_upct;
int esr_broad_lt_flt_upct;
int slope_limit_temp;
+ int esr_pulse_thresh_ma;
+ int esr_meas_curr_ma;
int jeita_thresholds[NUM_JEITA_LEVELS];
int ki_coeff_soc[KI_COEFF_SOC_LEVELS];
int ki_coeff_med_dischg[KI_COEFF_SOC_LEVELS];
@@ -330,6 +336,7 @@ struct fg_chip {
struct fg_memif *sram;
struct fg_irq_info *irqs;
struct votable *awake_votable;
+ struct votable *delta_bsoc_irq_en_votable;
struct fg_sram_param *sp;
struct fg_alg_flag *alg_flags;
int *debug_mask;
@@ -370,8 +377,8 @@ struct fg_chip {
bool esr_fcc_ctrl_en;
bool soc_reporting_ready;
bool esr_flt_cold_temp_en;
- bool bsoc_delta_irq_en;
bool slope_limit_en;
+ bool use_ima_single_mode;
struct completion soc_update;
struct completion soc_ready;
struct delayed_work profile_load_work;
diff --git a/drivers/power/supply/qcom/fg-memif.c b/drivers/power/supply/qcom/fg-memif.c
index 2dc7618..8a949bf 100644
--- a/drivers/power/supply/qcom/fg-memif.c
+++ b/drivers/power/supply/qcom/fg-memif.c
@@ -48,6 +48,10 @@ static int fg_config_access_mode(struct fg_chip *chip, bool access, bool burst)
int rc;
u8 intf_ctl = 0;
+ fg_dbg(chip, FG_SRAM_READ | FG_SRAM_WRITE, "access: %d burst: %d\n",
+ access, burst);
+
+ WARN_ON(burst && chip->use_ima_single_mode);
intf_ctl = ((access == FG_WRITE) ? IMA_WR_EN_BIT : 0) |
(burst ? MEM_ACS_BURST_BIT : 0);
@@ -175,6 +179,7 @@ int fg_clear_dma_errors_if_any(struct fg_chip *chip)
{
int rc;
u8 dma_sts;
+ bool error_present;
rc = fg_read(chip, MEM_IF_DMA_STS(chip), &dma_sts, 1);
if (rc < 0) {
@@ -184,14 +189,13 @@ int fg_clear_dma_errors_if_any(struct fg_chip *chip)
}
fg_dbg(chip, FG_STATUS, "dma_sts: %x\n", dma_sts);
- if (dma_sts & (DMA_WRITE_ERROR_BIT | DMA_READ_ERROR_BIT)) {
- rc = fg_masked_write(chip, MEM_IF_DMA_CTL(chip),
- DMA_CLEAR_LOG_BIT, DMA_CLEAR_LOG_BIT);
- if (rc < 0) {
- pr_err("failed to write addr=0x%04x, rc=%d\n",
- MEM_IF_DMA_CTL(chip), rc);
- return rc;
- }
+ error_present = dma_sts & (DMA_WRITE_ERROR_BIT | DMA_READ_ERROR_BIT);
+ rc = fg_masked_write(chip, MEM_IF_DMA_CTL(chip), DMA_CLEAR_LOG_BIT,
+ error_present ? DMA_CLEAR_LOG_BIT : 0);
+ if (rc < 0) {
+ pr_err("failed to write addr=0x%04x, rc=%d\n",
+ MEM_IF_DMA_CTL(chip), rc);
+ return rc;
}
return 0;
@@ -293,7 +297,9 @@ static int fg_check_iacs_ready(struct fg_chip *chip)
/* check for error condition */
rc = fg_clear_ima_errors_if_any(chip, false);
if (rc < 0) {
- pr_err("Failed to check for ima errors rc=%d\n", rc);
+ if (rc != -EAGAIN)
+ pr_err("Failed to check for ima errors rc=%d\n",
+ rc);
return rc;
}
@@ -357,7 +363,12 @@ static int __fg_interleaved_mem_write(struct fg_chip *chip, u16 address,
/* check for error condition */
rc = fg_clear_ima_errors_if_any(chip, false);
if (rc < 0) {
- pr_err("Failed to check for ima errors rc=%d\n", rc);
+ if (rc == -EAGAIN)
+ pr_err("IMA error cleared, address [%d %d] len %d\n",
+ address, offset, len);
+ else
+ pr_err("Failed to check for ima errors rc=%d\n",
+ rc);
return rc;
}
@@ -365,6 +376,15 @@ static int __fg_interleaved_mem_write(struct fg_chip *chip, u16 address,
len -= num_bytes;
offset = byte_enable = 0;
+ if (chip->use_ima_single_mode && len) {
+ address++;
+ rc = fg_set_address(chip, address);
+ if (rc < 0) {
+ pr_err("failed to set address rc = %d\n", rc);
+ return rc;
+ }
+ }
+
rc = fg_check_iacs_ready(chip);
if (rc < 0) {
pr_debug("IACS_RDY failed rc=%d\n", rc);
@@ -403,22 +423,40 @@ static int __fg_interleaved_mem_read(struct fg_chip *chip, u16 address,
/* check for error condition */
rc = fg_clear_ima_errors_if_any(chip, false);
if (rc < 0) {
- pr_err("Failed to check for ima errors rc=%d\n", rc);
+ if (rc == -EAGAIN)
+ pr_err("IMA error cleared, address [%d %d] len %d\n",
+ address, offset, len);
+ else
+ pr_err("Failed to check for ima errors rc=%d\n",
+ rc);
return rc;
}
- if (len && len < BYTES_PER_SRAM_WORD) {
- /*
- * Move to single mode. Changing address is not
- * required here as it must be in burst mode. Address
- * will get incremented internally by FG HW once the MSB
- * of RD_DATA is read.
- */
- rc = fg_config_access_mode(chip, FG_READ, 0);
- if (rc < 0) {
- pr_err("failed to move to single mode rc=%d\n",
- rc);
- return -EIO;
+ if (chip->use_ima_single_mode) {
+ if (len) {
+ address++;
+ rc = fg_set_address(chip, address);
+ if (rc < 0) {
+ pr_err("failed to set address rc = %d\n",
+ rc);
+ return rc;
+ }
+ }
+ } else {
+ if (len && len < BYTES_PER_SRAM_WORD) {
+ /*
+ * Move to single mode. Changing address is not
+ * required here as it must be in burst mode.
+ * Address will get incremented internally by FG
+ * HW once the MSB of RD_DATA is read.
+ */
+ rc = fg_config_access_mode(chip, FG_READ,
+ false);
+ if (rc < 0) {
+ pr_err("failed to move to single mode rc=%d\n",
+ rc);
+ return -EIO;
+ }
}
}
@@ -489,6 +527,7 @@ static int fg_interleaved_mem_config(struct fg_chip *chip, u8 *val,
u16 address, int offset, int len, bool access)
{
int rc = 0;
+ bool burst_mode = false;
if (!is_mem_access_available(chip, access))
return -EBUSY;
@@ -503,7 +542,8 @@ static int fg_interleaved_mem_config(struct fg_chip *chip, u8 *val,
}
/* configure for the read/write, single/burst mode */
- rc = fg_config_access_mode(chip, access, (offset + len) > 4);
+ burst_mode = chip->use_ima_single_mode ? false : ((offset + len) > 4);
+ rc = fg_config_access_mode(chip, access, burst_mode);
if (rc < 0) {
pr_err("failed to set memory access rc = %d\n", rc);
return rc;
@@ -583,7 +623,7 @@ int fg_interleaved_mem_read(struct fg_chip *chip, u16 address, u8 offset,
if (rc < 0) {
count++;
if (rc == -EAGAIN) {
- pr_err("IMA access failed retry_count = %d\n", count);
+ pr_err("IMA read failed retry_count = %d\n", count);
goto retry;
}
pr_err("failed to read SRAM address rc = %d\n", rc);
@@ -667,8 +707,8 @@ int fg_interleaved_mem_write(struct fg_chip *chip, u16 address, u8 offset,
rc = __fg_interleaved_mem_write(chip, address, offset, val, len);
if (rc < 0) {
count++;
- if ((rc == -EAGAIN) && (count < RETRY_COUNT)) {
- pr_err("IMA access failed retry_count = %d\n", count);
+ if (rc == -EAGAIN) {
+ pr_err("IMA write failed retry_count = %d\n", count);
goto retry;
}
pr_err("failed to write SRAM address rc = %d\n", rc);
diff --git a/drivers/power/supply/qcom/fg-reg.h b/drivers/power/supply/qcom/fg-reg.h
index bf2827f..cd0b2fb 100644
--- a/drivers/power/supply/qcom/fg-reg.h
+++ b/drivers/power/supply/qcom/fg-reg.h
@@ -167,6 +167,7 @@
/* BATT_INFO_ESR_PULL_DN_CFG */
#define ESR_PULL_DOWN_IVAL_MASK GENMASK(3, 2)
+#define ESR_PULL_DOWN_IVAL_SHIFT 2
#define ESR_MEAS_CUR_60MA 0x0
#define ESR_MEAS_CUR_120MA 0x1
#define ESR_MEAS_CUR_180MA 0x2
diff --git a/drivers/power/supply/qcom/pmic-voter.c b/drivers/power/supply/qcom/pmic-voter.c
index 39a0dcb..10a1c54 100644
--- a/drivers/power/supply/qcom/pmic-voter.c
+++ b/drivers/power/supply/qcom/pmic-voter.c
@@ -18,9 +18,9 @@
#include <linux/slab.h>
#include <linux/string.h>
-#include "pmic-voter.h"
+#include <linux/pmic-voter.h>
-#define NUM_MAX_CLIENTS 8
+#define NUM_MAX_CLIENTS 16
#define DEBUG_FORCE_CLIENT "DEBUG_FORCE_CLIENT"
static DEFINE_SPINLOCK(votable_list_slock);
@@ -188,6 +188,38 @@ void unlock_votable(struct votable *votable)
}
/**
+ * is_client_vote_enabled() -
+ * is_client_vote_enabled_locked() -
+ * The unlocked and locked variants of getting whether a client's
+ vote is enabled.
+ * @votable: the votable object
+ * @client_str: client of interest
+ *
+ * Returns:
+ * True if the client's vote is enabled; false otherwise.
+ */
+bool is_client_vote_enabled_locked(struct votable *votable,
+ const char *client_str)
+{
+ int client_id = get_client_id(votable, client_str);
+
+ if (client_id < 0)
+ return false;
+
+ return votable->votes[client_id].enabled;
+}
+
+bool is_client_vote_enabled(struct votable *votable, const char *client_str)
+{
+ bool enabled;
+
+ lock_votable(votable);
+ enabled = is_client_vote_enabled_locked(votable, client_str);
+ unlock_votable(votable);
+ return enabled;
+}
+
+/**
* get_client_vote() -
* get_client_vote_locked() -
* The unlocked and locked variants of getting a client's voted
diff --git a/drivers/power/supply/qcom/qpnp-fg-gen3.c b/drivers/power/supply/qcom/qpnp-fg-gen3.c
index 304d0cf..7ab5b31 100644
--- a/drivers/power/supply/qcom/qpnp-fg-gen3.c
+++ b/drivers/power/supply/qcom/qpnp-fg-gen3.c
@@ -31,6 +31,8 @@
#define FG_MEM_INFO_PMI8998 0x0D
/* SRAM address and offset in ascending order */
+#define ESR_PULSE_THRESH_WORD 2
+#define ESR_PULSE_THRESH_OFFSET 3
#define SLOPE_LIMIT_WORD 3
#define SLOPE_LIMIT_OFFSET 0
#define CUTOFF_VOLT_WORD 5
@@ -216,6 +218,8 @@ static struct fg_sram_param pmi8998_v1_sram_params[] = {
ESR_TIMER_CHG_MAX_OFFSET, 2, 1, 1, 0, fg_encode_default, NULL),
PARAM(ESR_TIMER_CHG_INIT, ESR_TIMER_CHG_INIT_WORD,
ESR_TIMER_CHG_INIT_OFFSET, 2, 1, 1, 0, fg_encode_default, NULL),
+ PARAM(ESR_PULSE_THRESH, ESR_PULSE_THRESH_WORD, ESR_PULSE_THRESH_OFFSET,
+ 1, 100000, 390625, 0, fg_encode_default, NULL),
PARAM(KI_COEFF_MED_DISCHG, KI_COEFF_MED_DISCHG_WORD,
KI_COEFF_MED_DISCHG_OFFSET, 1, 1000, 244141, 0,
fg_encode_default, NULL),
@@ -286,6 +290,8 @@ static struct fg_sram_param pmi8998_v2_sram_params[] = {
ESR_TIMER_CHG_MAX_OFFSET, 2, 1, 1, 0, fg_encode_default, NULL),
PARAM(ESR_TIMER_CHG_INIT, ESR_TIMER_CHG_INIT_WORD,
ESR_TIMER_CHG_INIT_OFFSET, 2, 1, 1, 0, fg_encode_default, NULL),
+ PARAM(ESR_PULSE_THRESH, ESR_PULSE_THRESH_WORD, ESR_PULSE_THRESH_OFFSET,
+ 1, 100000, 390625, 0, fg_encode_default, NULL),
PARAM(KI_COEFF_MED_DISCHG, KI_COEFF_MED_DISCHG_v2_WORD,
KI_COEFF_MED_DISCHG_v2_OFFSET, 1, 1000, 244141, 0,
fg_encode_default, NULL),
@@ -525,7 +531,7 @@ static int fg_get_sram_prop(struct fg_chip *chip, enum fg_sram_param_id id,
}
#define CC_SOC_30BIT GENMASK(29, 0)
-static int fg_get_cc_soc(struct fg_chip *chip, int *val)
+static int fg_get_charge_raw(struct fg_chip *chip, int *val)
{
int rc, cc_soc;
@@ -539,7 +545,7 @@ static int fg_get_cc_soc(struct fg_chip *chip, int *val)
return 0;
}
-static int fg_get_cc_soc_sw(struct fg_chip *chip, int *val)
+static int fg_get_charge_counter(struct fg_chip *chip, int *val)
{
int rc, cc_soc;
@@ -981,6 +987,29 @@ static inline void get_batt_temp_delta(int delta, u8 *val)
};
}
+static inline void get_esr_meas_current(int curr_ma, u8 *val)
+{
+ switch (curr_ma) {
+ case 60:
+ *val = ESR_MEAS_CUR_60MA;
+ break;
+ case 120:
+ *val = ESR_MEAS_CUR_120MA;
+ break;
+ case 180:
+ *val = ESR_MEAS_CUR_180MA;
+ break;
+ case 240:
+ *val = ESR_MEAS_CUR_240MA;
+ break;
+ default:
+ *val = ESR_MEAS_CUR_120MA;
+ break;
+ };
+
+ *val <<= ESR_PULL_DOWN_IVAL_SHIFT;
+}
+
static int fg_set_esr_timer(struct fg_chip *chip, int cycles, bool charging,
int flags)
{
@@ -1054,6 +1083,25 @@ static void fg_notify_charger(struct fg_chip *chip)
fg_dbg(chip, FG_STATUS, "Notified charger on float voltage and FCC\n");
}
+static int fg_delta_bsoc_irq_en_cb(struct votable *votable, void *data,
+ int enable, const char *client)
+{
+ struct fg_chip *chip = data;
+
+ if (!chip->irqs[BSOC_DELTA_IRQ].irq)
+ return 0;
+
+ if (enable) {
+ enable_irq(chip->irqs[BSOC_DELTA_IRQ].irq);
+ 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);
+ }
+
+ return 0;
+}
+
static int fg_awake_cb(struct votable *votable, void *data, int awake,
const char *client)
{
@@ -1241,7 +1289,7 @@ static void fg_cap_learning_post_process(struct fg_chip *chip)
chip->cl.final_cc_uah, old_cap, chip->cl.learned_cc_uah);
}
-static int fg_cap_learning_process_full_data(struct fg_chip *chip)
+static int fg_cap_learning_process_full_data(struct fg_chip *chip)
{
int rc, cc_soc_sw, cc_soc_delta_pct;
int64_t delta_cc_uah;
@@ -1263,30 +1311,39 @@ static int fg_cap_learning_process_full_data(struct fg_chip *chip)
return 0;
}
-static int fg_cap_learning_begin(struct fg_chip *chip, int batt_soc)
+#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;
+ int rc, cc_soc_sw, batt_soc_msb;
- if (DIV_ROUND_CLOSEST(batt_soc * 100, FULL_SOC_RAW) >
+ batt_soc_msb = batt_soc >> 24;
+ if (DIV_ROUND_CLOSEST(batt_soc_msb * 100, FULL_SOC_RAW) >
chip->dt.cl_start_soc) {
fg_dbg(chip, FG_CAP_LEARN, "Battery SOC %d is high!, not starting\n",
- batt_soc);
+ batt_soc_msb);
return -EINVAL;
}
- chip->cl.init_cc_uah = div64_s64(chip->cl.learned_cc_uah * batt_soc,
+ chip->cl.init_cc_uah = div64_s64(chip->cl.learned_cc_uah * batt_soc_msb,
FULL_SOC_RAW);
- rc = fg_get_sram_prop(chip, FG_SRAM_CC_SOC_SW, &cc_soc_sw);
+
+ /* 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);
if (rc < 0) {
- pr_err("Error in getting CC_SOC_SW, rc=%d\n", rc);
- return rc;
+ 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, chip->cl.init_cc_soc_sw);
- return 0;
+ batt_soc_msb, chip->cl.init_cc_soc_sw);
+out:
+ return rc;
}
static int fg_cap_learning_done(struct fg_chip *chip)
@@ -1318,7 +1375,7 @@ static int fg_cap_learning_done(struct fg_chip *chip)
#define FULL_SOC_RAW 255
static void fg_cap_learning_update(struct fg_chip *chip)
{
- int rc, batt_soc;
+ int rc, batt_soc, batt_soc_msb;
mutex_lock(&chip->cl.lock);
@@ -1337,11 +1394,9 @@ static void fg_cap_learning_update(struct fg_chip *chip)
goto out;
}
- /* We need only the most significant byte here */
- batt_soc = (u32)batt_soc >> 24;
-
+ batt_soc_msb = (u32)batt_soc >> 24;
fg_dbg(chip, FG_CAP_LEARN, "Chg_status: %d cl_active: %d batt_soc: %d\n",
- chip->charge_status, chip->cl.active, batt_soc);
+ chip->charge_status, chip->cl.active, batt_soc_msb);
/* Initialize the starting point of learning capacity */
if (!chip->cl.active) {
@@ -1363,7 +1418,7 @@ static void fg_cap_learning_update(struct fg_chip *chip)
if (chip->charge_status == POWER_SUPPLY_STATUS_NOT_CHARGING) {
fg_dbg(chip, FG_CAP_LEARN, "Capacity learning aborted @ battery SOC %d\n",
- batt_soc);
+ batt_soc_msb);
chip->cl.active = false;
chip->cl.init_cc_uah = 0;
}
@@ -1470,16 +1525,8 @@ static int fg_charge_full_update(struct fg_chip *chip)
return 0;
mutex_lock(&chip->charge_full_lock);
- if (!chip->charge_done && chip->bsoc_delta_irq_en) {
- disable_irq_wake(fg_irqs[BSOC_DELTA_IRQ].irq);
- disable_irq_nosync(fg_irqs[BSOC_DELTA_IRQ].irq);
- chip->bsoc_delta_irq_en = false;
- } else if (chip->charge_done && !chip->bsoc_delta_irq_en) {
- enable_irq(fg_irqs[BSOC_DELTA_IRQ].irq);
- enable_irq_wake(fg_irqs[BSOC_DELTA_IRQ].irq);
- chip->bsoc_delta_irq_en = true;
- }
-
+ vote(chip->delta_bsoc_irq_en_votable, DELTA_BSOC_IRQ_VOTER,
+ chip->charge_done, 0);
rc = power_supply_get_property(chip->batt_psy, POWER_SUPPLY_PROP_HEALTH,
&prop);
if (rc < 0) {
@@ -1598,6 +1645,9 @@ static int fg_rconn_config(struct fg_chip *chip)
u64 scaling_factor;
u32 val = 0;
+ if (!chip->dt.rconn_mohms)
+ return 0;
+
rc = fg_sram_read(chip, PROFILE_INTEGRITY_WORD,
SW_CONFIG_OFFSET, (u8 *)&val, 1, FG_IMA_DEFAULT);
if (rc < 0) {
@@ -1696,6 +1746,9 @@ static int fg_set_recharge_soc(struct fg_chip *chip, int recharge_soc)
if (!chip->dt.auto_recharge_soc)
return 0;
+ if (recharge_soc < 0 || recharge_soc > FULL_CAPACITY)
+ return 0;
+
fg_encode(chip->sp, FG_SRAM_RECHARGE_SOC_THR, recharge_soc, &buf);
rc = fg_sram_write(chip,
chip->sp[FG_SRAM_RECHARGE_SOC_THR].addr_word,
@@ -1712,46 +1765,55 @@ static int fg_set_recharge_soc(struct fg_chip *chip, int recharge_soc)
static int fg_adjust_recharge_soc(struct fg_chip *chip)
{
int rc, msoc, recharge_soc, new_recharge_soc = 0;
+ bool recharge_soc_status;
if (!chip->dt.auto_recharge_soc)
return 0;
recharge_soc = chip->dt.recharge_soc_thr;
+ recharge_soc_status = chip->recharge_soc_adjusted;
/*
* If the input is present and charging had been terminated, adjust
* the recharge SOC threshold based on the monotonic SOC at which
* the charge termination had happened.
*/
- if (is_input_present(chip) && !chip->recharge_soc_adjusted
- && chip->charge_done) {
- /* Get raw monotonic SOC for calculation */
- rc = fg_get_msoc(chip, &msoc);
- if (rc < 0) {
- pr_err("Error in getting msoc, rc=%d\n", rc);
- return rc;
- }
+ if (is_input_present(chip)) {
+ if (chip->charge_done) {
+ if (!chip->recharge_soc_adjusted) {
+ /* Get raw monotonic SOC for calculation */
+ rc = fg_get_msoc(chip, &msoc);
+ if (rc < 0) {
+ pr_err("Error in getting msoc, rc=%d\n",
+ rc);
+ return rc;
+ }
- /* Adjust the recharge_soc threshold */
- new_recharge_soc = msoc - (FULL_CAPACITY - recharge_soc);
- } else if (chip->recharge_soc_adjusted && (!is_input_present(chip)
- || chip->health == POWER_SUPPLY_HEALTH_GOOD)) {
+ /* Adjust the recharge_soc threshold */
+ new_recharge_soc = msoc - (FULL_CAPACITY -
+ recharge_soc);
+ chip->recharge_soc_adjusted = true;
+ } else {
+ /* adjusted already, do nothing */
+ return 0;
+ }
+ } else {
+ /* Charging, do nothing */
+ return 0;
+ }
+ } else {
/* Restore the default value */
new_recharge_soc = recharge_soc;
+ chip->recharge_soc_adjusted = false;
}
- if (new_recharge_soc > 0 && new_recharge_soc < FULL_CAPACITY) {
- rc = fg_set_recharge_soc(chip, new_recharge_soc);
- if (rc) {
- pr_err("Couldn't set resume SOC for FG, rc=%d\n", rc);
- return rc;
- }
-
- chip->recharge_soc_adjusted = (new_recharge_soc !=
- recharge_soc);
- fg_dbg(chip, FG_STATUS, "resume soc set to %d\n",
- new_recharge_soc);
+ rc = fg_set_recharge_soc(chip, new_recharge_soc);
+ if (rc < 0) {
+ chip->recharge_soc_adjusted = recharge_soc_status;
+ pr_err("Couldn't set resume SOC for FG, rc=%d\n", rc);
+ return rc;
}
+ fg_dbg(chip, FG_STATUS, "resume soc set to %d\n", new_recharge_soc);
return 0;
}
@@ -2156,6 +2218,35 @@ static int fg_get_cycle_count(struct fg_chip *chip)
return count;
}
+static int fg_bp_params_config(struct fg_chip *chip)
+{
+ int rc = 0;
+ u8 buf;
+
+ /* This SRAM register is only present in v2.0 and above */
+ if (!(chip->wa_flags & PMI8998_V1_REV_WA) &&
+ chip->bp.float_volt_uv > 0) {
+ fg_encode(chip->sp, FG_SRAM_FLOAT_VOLT,
+ chip->bp.float_volt_uv / 1000, &buf);
+ rc = fg_sram_write(chip, chip->sp[FG_SRAM_FLOAT_VOLT].addr_word,
+ chip->sp[FG_SRAM_FLOAT_VOLT].addr_byte, &buf,
+ chip->sp[FG_SRAM_FLOAT_VOLT].len, FG_IMA_DEFAULT);
+ if (rc < 0) {
+ pr_err("Error in writing float_volt, rc=%d\n", rc);
+ return rc;
+ }
+ }
+
+ if (chip->bp.vbatt_full_mv > 0) {
+ rc = fg_set_constant_chg_voltage(chip,
+ chip->bp.vbatt_full_mv * 1000);
+ if (rc < 0)
+ return rc;
+ }
+
+ return rc;
+}
+
#define PROFILE_LOAD_BIT BIT(0)
#define BOOTLOADER_LOAD_BIT BIT(1)
#define BOOTLOADER_RESTART_BIT BIT(2)
@@ -2176,6 +2267,17 @@ static bool is_profile_load_required(struct fg_chip *chip)
/* Check if integrity bit is set */
if (val & PROFILE_LOAD_BIT) {
fg_dbg(chip, FG_STATUS, "Battery profile integrity bit is set\n");
+
+ /* Whitelist the values */
+ val &= ~PROFILE_LOAD_BIT;
+ if (val != HLOS_RESTART_BIT && val != BOOTLOADER_LOAD_BIT &&
+ val != (BOOTLOADER_LOAD_BIT | BOOTLOADER_RESTART_BIT)) {
+ val |= PROFILE_LOAD_BIT;
+ pr_warn("Garbage value in profile integrity word: 0x%x\n",
+ val);
+ return true;
+ }
+
rc = fg_sram_read(chip, PROFILE_LOAD_WORD, PROFILE_LOAD_OFFSET,
buf, PROFILE_COMP_LEN, FG_IMA_DEFAULT);
if (rc < 0) {
@@ -2323,6 +2425,11 @@ static void profile_load_work(struct work_struct *work)
}
done:
+ rc = fg_bp_params_config(chip);
+ if (rc < 0)
+ pr_err("Error in configuring battery profile params, rc:%d\n",
+ rc);
+
rc = fg_sram_read(chip, NOM_CAP_WORD, NOM_CAP_OFFSET, buf, 2,
FG_IMA_DEFAULT);
if (rc < 0) {
@@ -2806,7 +2913,7 @@ static int fg_psy_get_property(struct power_supply *psy,
pval->intval = chip->cyc_ctr.id;
break;
case POWER_SUPPLY_PROP_CHARGE_NOW_RAW:
- rc = fg_get_cc_soc(chip, &pval->intval);
+ rc = fg_get_charge_raw(chip, &pval->intval);
break;
case POWER_SUPPLY_PROP_CHARGE_NOW:
pval->intval = chip->cl.init_cc_uah;
@@ -2815,7 +2922,7 @@ static int fg_psy_get_property(struct power_supply *psy,
pval->intval = chip->cl.learned_cc_uah;
break;
case POWER_SUPPLY_PROP_CHARGE_COUNTER:
- rc = fg_get_cc_soc_sw(chip, &pval->intval);
+ rc = fg_get_charge_counter(chip, &pval->intval);
break;
case POWER_SUPPLY_PROP_TIME_TO_FULL_AVG:
rc = fg_get_time_to_full(chip, &pval->intval);
@@ -2974,27 +3081,6 @@ static int fg_hw_init(struct fg_chip *chip)
return rc;
}
- /* This SRAM register is only present in v2.0 and above */
- if (!(chip->wa_flags & PMI8998_V1_REV_WA) &&
- chip->bp.float_volt_uv > 0) {
- fg_encode(chip->sp, FG_SRAM_FLOAT_VOLT,
- chip->bp.float_volt_uv / 1000, buf);
- rc = fg_sram_write(chip, chip->sp[FG_SRAM_FLOAT_VOLT].addr_word,
- chip->sp[FG_SRAM_FLOAT_VOLT].addr_byte, buf,
- chip->sp[FG_SRAM_FLOAT_VOLT].len, FG_IMA_DEFAULT);
- if (rc < 0) {
- pr_err("Error in writing float_volt, rc=%d\n", rc);
- return rc;
- }
- }
-
- if (chip->bp.vbatt_full_mv > 0) {
- rc = fg_set_constant_chg_voltage(chip,
- chip->bp.vbatt_full_mv * 1000);
- if (rc < 0)
- return rc;
- }
-
fg_encode(chip->sp, FG_SRAM_CHG_TERM_CURR, chip->dt.chg_term_curr_ma,
buf);
rc = fg_sram_write(chip, chip->sp[FG_SRAM_CHG_TERM_CURR].addr_word,
@@ -3164,12 +3250,10 @@ static int fg_hw_init(struct fg_chip *chip)
return rc;
}
- if (chip->dt.rconn_mohms > 0) {
- rc = fg_rconn_config(chip);
- if (rc < 0) {
- pr_err("Error in configuring Rconn, rc=%d\n", rc);
- return rc;
- }
+ rc = fg_rconn_config(chip);
+ if (rc < 0) {
+ pr_err("Error in configuring Rconn, rc=%d\n", rc);
+ return rc;
}
fg_encode(chip->sp, FG_SRAM_ESR_TIGHT_FILTER,
@@ -3192,6 +3276,24 @@ static int fg_hw_init(struct fg_chip *chip)
return rc;
}
+ fg_encode(chip->sp, FG_SRAM_ESR_PULSE_THRESH,
+ chip->dt.esr_pulse_thresh_ma, buf);
+ rc = fg_sram_write(chip, chip->sp[FG_SRAM_ESR_PULSE_THRESH].addr_word,
+ chip->sp[FG_SRAM_ESR_PULSE_THRESH].addr_byte, buf,
+ chip->sp[FG_SRAM_ESR_PULSE_THRESH].len, FG_IMA_DEFAULT);
+ if (rc < 0) {
+ pr_err("Error in writing esr_pulse_thresh_ma, rc=%d\n", rc);
+ return rc;
+ }
+
+ get_esr_meas_current(chip->dt.esr_meas_curr_ma, &val);
+ rc = fg_masked_write(chip, BATT_INFO_ESR_PULL_DN_CFG(chip),
+ ESR_PULL_DOWN_IVAL_MASK, val);
+ if (rc < 0) {
+ pr_err("Error in writing esr_meas_curr_ma, rc=%d\n", rc);
+ return rc;
+ }
+
return 0;
}
@@ -3216,20 +3318,19 @@ static irqreturn_t fg_mem_xcp_irq_handler(int irq, void *data)
}
fg_dbg(chip, FG_IRQ, "irq %d triggered, status:%d\n", irq, status);
- if (status & MEM_XCP_BIT) {
- rc = fg_clear_dma_errors_if_any(chip);
- if (rc < 0) {
- pr_err("Error in clearing DMA error, rc=%d\n", rc);
- return IRQ_HANDLED;
- }
- mutex_lock(&chip->sram_rw_lock);
+ mutex_lock(&chip->sram_rw_lock);
+ rc = fg_clear_dma_errors_if_any(chip);
+ if (rc < 0)
+ pr_err("Error in clearing DMA error, rc=%d\n", rc);
+
+ if (status & MEM_XCP_BIT) {
rc = fg_clear_ima_errors_if_any(chip, true);
if (rc < 0 && rc != -EAGAIN)
pr_err("Error in checking IMA errors rc:%d\n", rc);
- mutex_unlock(&chip->sram_rw_lock);
}
+ mutex_unlock(&chip->sram_rw_lock);
return IRQ_HANDLED;
}
@@ -3676,6 +3777,8 @@ static int fg_parse_ki_coefficients(struct fg_chip *chip)
#define DEFAULT_ESR_TIGHT_LT_FLT_UPCT 48829
#define DEFAULT_ESR_BROAD_LT_FLT_UPCT 148438
#define DEFAULT_ESR_CLAMP_MOHMS 20
+#define DEFAULT_ESR_PULSE_THRESH_MA 110
+#define DEFAULT_ESR_MEAS_CURR_MA 120
static int fg_parse_dt(struct fg_chip *chip)
{
struct device_node *child, *revid_node, *node = chip->dev->of_node;
@@ -3725,6 +3828,7 @@ static int fg_parse_dt(struct fg_chip *chip)
case PM660_SUBTYPE:
chip->sp = pmi8998_v2_sram_params;
chip->alg_flags = pmi8998_v2_alg_flags;
+ chip->use_ima_single_mode = true;
break;
default:
return -EINVAL;
@@ -3945,9 +4049,7 @@ static int fg_parse_dt(struct fg_chip *chip)
pr_err("Error in parsing Ki coefficients, rc=%d\n", rc);
rc = of_property_read_u32(node, "qcom,fg-rconn-mohms", &temp);
- if (rc < 0)
- chip->dt.rconn_mohms = -EINVAL;
- else
+ if (!rc)
chip->dt.rconn_mohms = temp;
rc = of_property_read_u32(node, "qcom,fg-esr-filter-switch-temp",
@@ -3995,6 +4097,22 @@ static int fg_parse_dt(struct fg_chip *chip)
else
chip->dt.esr_clamp_mohms = temp;
+ chip->dt.esr_pulse_thresh_ma = DEFAULT_ESR_PULSE_THRESH_MA;
+ rc = of_property_read_u32(node, "qcom,fg-esr-pulse-thresh-ma", &temp);
+ if (!rc) {
+ /* ESR pulse qualification threshold range is 1-997 mA */
+ if (temp > 0 && temp < 997)
+ chip->dt.esr_pulse_thresh_ma = temp;
+ }
+
+ chip->dt.esr_meas_curr_ma = DEFAULT_ESR_MEAS_CURR_MA;
+ rc = of_property_read_u32(node, "qcom,fg-esr-meas-curr-ma", &temp);
+ if (!rc) {
+ /* ESR measurement current range is 60-240 mA */
+ if (temp >= 60 || temp <= 240)
+ chip->dt.esr_meas_curr_ma = temp;
+ }
+
return 0;
}
@@ -4005,6 +4123,9 @@ static void fg_cleanup(struct fg_chip *chip)
if (chip->awake_votable)
destroy_votable(chip->awake_votable);
+ if (chip->delta_bsoc_irq_en_votable)
+ destroy_votable(chip->delta_bsoc_irq_en_votable);
+
if (chip->batt_id_chan)
iio_channel_release(chip->batt_id_chan);
@@ -4046,7 +4167,15 @@ static int fg_gen3_probe(struct platform_device *pdev)
chip);
if (IS_ERR(chip->awake_votable)) {
rc = PTR_ERR(chip->awake_votable);
- return rc;
+ goto exit;
+ }
+
+ chip->delta_bsoc_irq_en_votable = create_votable("FG_DELTA_BSOC_IRQ",
+ VOTE_SET_ANY,
+ fg_delta_bsoc_irq_en_cb, chip);
+ if (IS_ERR(chip->delta_bsoc_irq_en_votable)) {
+ rc = PTR_ERR(chip->delta_bsoc_irq_en_votable);
+ goto exit;
}
rc = fg_parse_dt(chip);
@@ -4073,7 +4202,7 @@ static int fg_gen3_probe(struct platform_device *pdev)
rc = fg_get_batt_id(chip);
if (rc < 0) {
pr_err("Error in getting battery id, rc:%d\n", rc);
- return rc;
+ goto exit;
}
rc = fg_get_batt_profile(chip);
@@ -4131,11 +4260,7 @@ static int fg_gen3_probe(struct platform_device *pdev)
disable_irq_nosync(fg_irqs[SOC_UPDATE_IRQ].irq);
/* Keep BSOC_DELTA_IRQ irq disabled until we require it */
- if (fg_irqs[BSOC_DELTA_IRQ].irq) {
- disable_irq_wake(fg_irqs[BSOC_DELTA_IRQ].irq);
- disable_irq_nosync(fg_irqs[BSOC_DELTA_IRQ].irq);
- chip->bsoc_delta_irq_en = false;
- }
+ rerun_election(chip->delta_bsoc_irq_en_votable);
rc = fg_debugfs_create(chip);
if (rc < 0) {
diff --git a/drivers/power/supply/qcom/qpnp-qnovo.c b/drivers/power/supply/qcom/qpnp-qnovo.c
index cbfab30..c74dc89 100644
--- a/drivers/power/supply/qcom/qpnp-qnovo.c
+++ b/drivers/power/supply/qcom/qpnp-qnovo.c
@@ -19,7 +19,7 @@
#include <linux/of.h>
#include <linux/of_irq.h>
#include <linux/qpnp/qpnp-revid.h>
-#include "pmic-voter.h"
+#include <linux/pmic-voter.h>
#define QNOVO_REVISION1 0x00
#define QNOVO_REVISION2 0x01
@@ -29,6 +29,8 @@
#define QNOVO_PTRAIN_STS 0x08
#define QNOVO_ERROR_STS 0x09
#define QNOVO_ERROR_BIT BIT(0)
+#define QNOVO_ERROR_STS2 0x0A
+#define QNOVO_ERROR_CHARGING_DISABLED BIT(1)
#define QNOVO_INT_RT_STS 0x10
#define QNOVO_INT_SET_TYPE 0x11
#define QNOVO_INT_POLARITY_HIGH 0x12
@@ -109,20 +111,6 @@ struct qnovo_dt_props {
struct device_node *revid_dev_node;
};
-enum {
- QNOVO_NO_ERR_STS_BIT = BIT(0),
-};
-
-struct chg_props {
- bool charging;
- bool usb_online;
- bool dc_online;
-};
-
-struct chg_status {
- bool ok_to_qnovo;
-};
-
struct qnovo {
int base;
struct mutex write_lock;
@@ -141,13 +129,10 @@ struct qnovo {
s64 v_gain_mega;
struct notifier_block nb;
struct power_supply *batt_psy;
- struct power_supply *usb_psy;
- struct power_supply *dc_psy;
- struct chg_props cp;
- struct chg_status cs;
struct work_struct status_change_work;
int fv_uV_request;
int fcc_uA_request;
+ bool ok_to_qnovo;
};
static int debug_mask;
@@ -272,28 +257,22 @@ static int qnovo_disable_cb(struct votable *votable, void *data, int disable,
const char *client)
{
struct qnovo *chip = data;
- int rc = 0;
+ union power_supply_propval pval = {0};
+ int rc;
- if (disable) {
- rc = qnovo_batt_psy_update(chip, true);
- if (rc < 0)
- return rc;
- }
+ if (!is_batt_available(chip))
+ return -EINVAL;
- rc = qnovo_masked_write(chip, QNOVO_PTRAIN_EN, QNOVO_PTRAIN_EN_BIT,
- disable ? 0 : QNOVO_PTRAIN_EN_BIT);
+ pval.intval = !disable;
+ rc = power_supply_set_property(chip->batt_psy,
+ POWER_SUPPLY_PROP_CHARGE_QNOVO_ENABLE,
+ &pval);
if (rc < 0) {
- dev_err(chip->dev, "Couldn't %s pulse train rc=%d\n",
- disable ? "disable" : "enable", rc);
- return rc;
+ pr_err("Couldn't set prop qnovo_enable rc = %d\n", rc);
+ return -EINVAL;
}
- if (!disable) {
- rc = qnovo_batt_psy_update(chip, false);
- if (rc < 0)
- return rc;
- }
-
+ rc = qnovo_batt_psy_update(chip, disable);
return rc;
}
@@ -325,36 +304,18 @@ static int qnovo_parse_dt(struct qnovo *chip)
return 0;
}
-static int qnovo_check_chg_version(struct qnovo *chip)
-{
- int rc;
-
- chip->pmic_rev_id = get_revid_data(chip->dt.revid_dev_node);
- if (IS_ERR(chip->pmic_rev_id)) {
- rc = PTR_ERR(chip->pmic_rev_id);
- if (rc != -EPROBE_DEFER)
- pr_err("Unable to get pmic_revid rc=%d\n", rc);
- return rc;
- }
-
- if ((chip->pmic_rev_id->pmic_subtype == PMI8998_SUBTYPE)
- && (chip->pmic_rev_id->rev4 < PMI8998_V2P0_REV4)) {
- chip->wa_flags |= QNOVO_NO_ERR_STS_BIT;
- }
-
- return 0;
-}
-
enum {
VER = 0,
OK_TO_QNOVO,
- ENABLE,
+ QNOVO_ENABLE,
+ PT_ENABLE,
FV_REQUEST,
FCC_REQUEST,
PE_CTRL_REG,
PE_CTRL2_REG,
PTRAIN_STS_REG,
INT_RT_STS_REG,
+ ERR_STS2_REG,
PREST1,
PPULS1,
NREST1,
@@ -394,6 +355,12 @@ struct param_info {
};
static struct param_info params[] = {
+ [PT_ENABLE] = {
+ .name = "PT_ENABLE",
+ .start_addr = QNOVO_PTRAIN_EN,
+ .num_regs = 1,
+ .units_str = "",
+ },
[FV_REQUEST] = {
.units_str = "uV",
},
@@ -424,6 +391,12 @@ static struct param_info params[] = {
.num_regs = 1,
.units_str = "",
},
+ [ERR_STS2_REG] = {
+ .name = "RAW_CHGR_ERR",
+ .start_addr = QNOVO_ERROR_STS2,
+ .num_regs = 1,
+ .units_str = "",
+ },
[PREST1] = {
.name = "PREST1",
.start_addr = QNOVO_PREST1_CTRL,
@@ -431,7 +404,7 @@ static struct param_info params[] = {
.reg_to_unit_multiplier = 5,
.reg_to_unit_divider = 1,
.min_val = 5,
- .max_val = 1275,
+ .max_val = 255,
.units_str = "mS",
},
[PPULS1] = {
@@ -440,8 +413,8 @@ static struct param_info params[] = {
.num_regs = 2,
.reg_to_unit_multiplier = 1600, /* converts to uC */
.reg_to_unit_divider = 1,
- .min_val = 0,
- .max_val = 104856000,
+ .min_val = 30000,
+ .max_val = 65535000,
.units_str = "uC",
},
[NREST1] = {
@@ -451,7 +424,7 @@ static struct param_info params[] = {
.reg_to_unit_multiplier = 5,
.reg_to_unit_divider = 1,
.min_val = 5,
- .max_val = 1275,
+ .max_val = 255,
.units_str = "mS",
},
[NPULS1] = {
@@ -460,8 +433,8 @@ static struct param_info params[] = {
.num_regs = 1,
.reg_to_unit_multiplier = 5,
.reg_to_unit_divider = 1,
- .min_val = 5,
- .max_val = 1275,
+ .min_val = 0,
+ .max_val = 255,
.units_str = "mS",
},
[PPCNT] = {
@@ -470,7 +443,7 @@ static struct param_info params[] = {
.num_regs = 1,
.reg_to_unit_multiplier = 1,
.reg_to_unit_divider = 1,
- .min_val = 0,
+ .min_val = 1,
.max_val = 255,
.units_str = "pulses",
},
@@ -480,8 +453,8 @@ static struct param_info params[] = {
.num_regs = 2,
.reg_to_unit_multiplier = 610350, /* converts to nV */
.reg_to_unit_divider = 1,
- .min_val = 0,
- .max_val = 5000000,
+ .min_val = 2200000,
+ .max_val = 4500000,
.units_str = "uV",
},
[PVOLT1] = {
@@ -506,8 +479,6 @@ static struct param_info params[] = {
.num_regs = 1,
.reg_to_unit_multiplier = 2,
.reg_to_unit_divider = 1,
- .min_val = 5,
- .max_val = 1275,
.units_str = "S",
},
[PREST2] = {
@@ -517,7 +488,7 @@ static struct param_info params[] = {
.reg_to_unit_multiplier = 5,
.reg_to_unit_divider = 1,
.min_val = 5,
- .max_val = 327675,
+ .max_val = 65535,
.units_str = "mS",
},
[PPULS2] = {
@@ -526,8 +497,8 @@ static struct param_info params[] = {
.num_regs = 2,
.reg_to_unit_multiplier = 1600, /* converts to uC */
.reg_to_unit_divider = 1,
- .min_val = 0,
- .max_val = 104856000,
+ .min_val = 30000,
+ .max_val = 65535000,
.units_str = "uC",
},
[NREST2] = {
@@ -538,7 +509,7 @@ static struct param_info params[] = {
.reg_to_unit_divider = 1,
.reg_to_unit_offset = -5,
.min_val = 5,
- .max_val = 1280,
+ .max_val = 255,
.units_str = "mS",
},
[NPULS2] = {
@@ -547,18 +518,18 @@ static struct param_info params[] = {
.num_regs = 1,
.reg_to_unit_multiplier = 5,
.reg_to_unit_divider = 1,
- .min_val = 5,
- .max_val = 1275,
+ .min_val = 0,
+ .max_val = 255,
.units_str = "mS",
},
[VLIM2] = {
- .name = "VLIM1",
+ .name = "VLIM2",
.start_addr = QNOVO_VLIM2_LSB_CTRL,
.num_regs = 2,
.reg_to_unit_multiplier = 610350, /* converts to nV */
.reg_to_unit_divider = 1,
- .min_val = 0,
- .max_val = 5000000,
+ .min_val = 2200000,
+ .max_val = 4500000,
.units_str = "uV",
},
[PVOLT2] = {
@@ -591,6 +562,8 @@ static struct param_info params[] = {
.num_regs = 1,
.reg_to_unit_multiplier = 1,
.reg_to_unit_divider = 1,
+ .min_val = 0,
+ .max_val = 255,
.units_str = "pulses",
},
[VMAX] = {
@@ -645,33 +618,73 @@ static ssize_t ok_to_qnovo_show(struct class *c, struct class_attribute *attr,
{
struct qnovo *chip = container_of(c, struct qnovo, qnovo_class);
- return snprintf(buf, PAGE_SIZE, "%d\n", chip->cs.ok_to_qnovo);
+ return snprintf(buf, PAGE_SIZE, "%d\n", chip->ok_to_qnovo);
}
-static ssize_t enable_show(struct class *c, struct class_attribute *attr,
+static ssize_t qnovo_enable_show(struct class *c, struct class_attribute *attr,
char *ubuf)
{
struct qnovo *chip = container_of(c, struct qnovo, qnovo_class);
- int val;
+ int val = get_effective_result(chip->disable_votable);
- val = get_client_vote(chip->disable_votable, USER_VOTER);
- val = !val;
- return snprintf(ubuf, PAGE_SIZE, "%d\n", val);
+ return snprintf(ubuf, PAGE_SIZE, "%d\n", !val);
}
-static ssize_t enable_store(struct class *c, struct class_attribute *attr,
+static ssize_t qnovo_enable_store(struct class *c, struct class_attribute *attr,
const char *ubuf, size_t count)
{
struct qnovo *chip = container_of(c, struct qnovo, qnovo_class);
unsigned long val;
- bool disable;
- if (kstrtoul(ubuf, 10, &val))
+ if (kstrtoul(ubuf, 0, &val))
return -EINVAL;
- disable = !val;
+ vote(chip->disable_votable, USER_VOTER, !val, 0);
- vote(chip->disable_votable, USER_VOTER, disable, 0);
+ return count;
+}
+
+static ssize_t pt_enable_show(struct class *c, struct class_attribute *attr,
+ char *ubuf)
+{
+ int i = attr - qnovo_attributes;
+ struct qnovo *chip = container_of(c, struct qnovo, qnovo_class);
+ u8 buf[2] = {0, 0};
+ u16 regval;
+ int rc;
+
+ rc = qnovo_read(chip, params[i].start_addr, buf, params[i].num_regs);
+ if (rc < 0) {
+ pr_err("Couldn't read %s rc = %d\n", params[i].name, rc);
+ return -EINVAL;
+ }
+ regval = buf[1] << 8 | buf[0];
+
+ return snprintf(ubuf, PAGE_SIZE, "%d\n",
+ (int)(regval & QNOVO_PTRAIN_EN_BIT));
+}
+
+static ssize_t pt_enable_store(struct class *c, struct class_attribute *attr,
+ const char *ubuf, size_t count)
+{
+ struct qnovo *chip = container_of(c, struct qnovo, qnovo_class);
+ unsigned long val;
+ int rc = 0;
+
+ if (get_effective_result(chip->disable_votable))
+ return -EINVAL;
+
+ if (kstrtoul(ubuf, 0, &val))
+ return -EINVAL;
+
+ rc = qnovo_masked_write(chip, QNOVO_PTRAIN_EN, QNOVO_PTRAIN_EN_BIT,
+ (bool)val ? QNOVO_PTRAIN_EN_BIT : 0);
+ if (rc < 0) {
+ dev_err(chip->dev, "Couldn't %s pulse train rc=%d\n",
+ (bool)val ? "enable" : "disable", rc);
+ return rc;
+ }
+
return count;
}
@@ -688,7 +701,7 @@ static ssize_t val_show(struct class *c, struct class_attribute *attr,
if (i == FCC_REQUEST)
val = chip->fcc_uA_request;
- return snprintf(ubuf, PAGE_SIZE, "%d%s\n", val, params[i].units_str);
+ return snprintf(ubuf, PAGE_SIZE, "%d\n", val);
}
static ssize_t val_store(struct class *c, struct class_attribute *attr,
@@ -698,7 +711,7 @@ static ssize_t val_store(struct class *c, struct class_attribute *attr,
int i = attr - qnovo_attributes;
unsigned long val;
- if (kstrtoul(ubuf, 10, &val))
+ if (kstrtoul(ubuf, 0, &val))
return -EINVAL;
if (i == FV_REQUEST)
@@ -707,6 +720,9 @@ static ssize_t val_store(struct class *c, struct class_attribute *attr,
if (i == FCC_REQUEST)
chip->fcc_uA_request = val;
+ if (!get_effective_result(chip->disable_votable))
+ qnovo_batt_psy_update(chip, false);
+
return count;
}
@@ -726,8 +742,7 @@ static ssize_t reg_show(struct class *c, struct class_attribute *attr,
}
regval = buf[1] << 8 | buf[0];
- return snprintf(ubuf, PAGE_SIZE, "0x%04x%s\n",
- regval, params[i].units_str);
+ return snprintf(ubuf, PAGE_SIZE, "0x%04x\n", regval);
}
static ssize_t reg_store(struct class *c, struct class_attribute *attr,
@@ -739,7 +754,7 @@ static ssize_t reg_store(struct class *c, struct class_attribute *attr,
unsigned long val;
int rc;
- if (kstrtoul(ubuf, 16, &val))
+ if (kstrtoul(ubuf, 0, &val))
return -EINVAL;
buf[0] = val & 0xFF;
@@ -774,7 +789,7 @@ static ssize_t time_show(struct class *c, struct class_attribute *attr,
/ params[i].reg_to_unit_divider)
- params[i].reg_to_unit_offset;
- return snprintf(ubuf, PAGE_SIZE, "%d%s\n", val, params[i].units_str);
+ return snprintf(ubuf, PAGE_SIZE, "%d\n", val);
}
static ssize_t time_store(struct class *c, struct class_attribute *attr,
@@ -787,7 +802,7 @@ static ssize_t time_store(struct class *c, struct class_attribute *attr,
unsigned long val;
int rc;
- if (kstrtoul(ubuf, 10, &val))
+ if (kstrtoul(ubuf, 0, &val))
return -EINVAL;
if (val < params[i].min_val || val > params[i].max_val) {
@@ -828,7 +843,11 @@ static ssize_t current_show(struct class *c, struct class_attribute *attr,
pr_err("Couldn't read %s rc = %d\n", params[i].name, rc);
return -EINVAL;
}
- regval_nA = buf[1] << 8 | buf[0];
+
+ if (buf[1] & BIT(5))
+ buf[1] |= GENMASK(7, 6);
+
+ regval_nA = (s16)(buf[1] << 8 | buf[0]);
regval_nA = div_s64(regval_nA * params[i].reg_to_unit_multiplier,
params[i].reg_to_unit_divider)
- params[i].reg_to_unit_offset;
@@ -841,11 +860,10 @@ static ssize_t current_show(struct class *c, struct class_attribute *attr,
gain = chip->internal_i_gain_mega;
}
- comp_val_nA = div_s64(regval_nA * gain, 1000000) + offset_nA;
+ comp_val_nA = div_s64(regval_nA * gain, 1000000) - offset_nA;
comp_val_uA = div_s64(comp_val_nA, 1000);
- return snprintf(ubuf, PAGE_SIZE, "%d%s\n",
- comp_val_uA, params[i].units_str);
+ return snprintf(ubuf, PAGE_SIZE, "%d\n", comp_val_uA);
}
static ssize_t voltage_show(struct class *c, struct class_attribute *attr,
@@ -875,8 +893,7 @@ static ssize_t voltage_show(struct class *c, struct class_attribute *attr,
comp_val_nV = div_s64(regval_nV * gain, 1000000) + offset_nV;
comp_val_uV = div_s64(comp_val_nV, 1000);
- return snprintf(ubuf, PAGE_SIZE, "%d%s\n",
- comp_val_uV, params[i].units_str);
+ return snprintf(ubuf, PAGE_SIZE, "%d\n", comp_val_uV);
}
static ssize_t voltage_store(struct class *c, struct class_attribute *attr,
@@ -890,7 +907,7 @@ static ssize_t voltage_store(struct class *c, struct class_attribute *attr,
s64 regval_nV;
s64 gain, offset_nV;
- if (kstrtoul(ubuf, 10, &val_uV))
+ if (kstrtoul(ubuf, 0, &val_uV))
return -EINVAL;
if (val_uV < params[i].min_val || val_uV > params[i].max_val) {
@@ -947,8 +964,7 @@ static ssize_t coulomb_show(struct class *c, struct class_attribute *attr,
gain = chip->internal_i_gain_mega;
comp_val_uC = div_s64(regval_uC * gain, 1000000);
- return snprintf(ubuf, PAGE_SIZE, "%d%s\n",
- comp_val_uC, params[i].units_str);
+ return snprintf(ubuf, PAGE_SIZE, "%d\n", comp_val_uC);
}
static ssize_t coulomb_store(struct class *c, struct class_attribute *attr,
@@ -962,7 +978,7 @@ static ssize_t coulomb_store(struct class *c, struct class_attribute *attr,
s64 regval;
s64 gain;
- if (kstrtoul(ubuf, 10, &val_uC))
+ if (kstrtoul(ubuf, 0, &val_uC))
return -EINVAL;
if (val_uC < params[i].min_val || val_uC > params[i].max_val) {
@@ -1014,15 +1030,14 @@ static ssize_t batt_prop_show(struct class *c, struct class_attribute *attr,
return -EINVAL;
}
- return snprintf(ubuf, PAGE_SIZE, "%d%s\n",
- pval.intval, params[i].units_str);
+ return snprintf(ubuf, PAGE_SIZE, "%d\n", pval.intval);
}
static struct class_attribute qnovo_attributes[] = {
[VER] = __ATTR_RO(version),
[OK_TO_QNOVO] = __ATTR_RO(ok_to_qnovo),
- [ENABLE] = __ATTR(enable, 0644,
- enable_show, enable_store),
+ [QNOVO_ENABLE] = __ATTR_RW(qnovo_enable),
+ [PT_ENABLE] = __ATTR_RW(pt_enable),
[FV_REQUEST] = __ATTR(fv_uV_request, 0644,
val_show, val_store),
[FCC_REQUEST] = __ATTR(fcc_uA_request, 0644,
@@ -1031,10 +1046,12 @@ static struct class_attribute qnovo_attributes[] = {
reg_show, reg_store),
[PE_CTRL2_REG] = __ATTR(PE_CTRL2_REG, 0644,
reg_show, reg_store),
- [PTRAIN_STS_REG] = __ATTR(PTRAIN_STS_REG, 0644,
- reg_show, reg_store),
- [INT_RT_STS_REG] = __ATTR(INT_RT_STS_REG, 0644,
- reg_show, reg_store),
+ [PTRAIN_STS_REG] = __ATTR(PTRAIN_STS_REG, 0444,
+ reg_show, NULL),
+ [INT_RT_STS_REG] = __ATTR(INT_RT_STS_REG, 0444,
+ reg_show, NULL),
+ [ERR_STS2_REG] = __ATTR(ERR_STS2_REG, 0444,
+ reg_show, NULL),
[PREST1] = __ATTR(PREST1_mS, 0644,
time_show, time_store),
[PPULS1] = __ATTR(PPULS1_uC, 0644,
@@ -1055,7 +1072,7 @@ static struct class_attribute qnovo_attributes[] = {
time_show, NULL),
[PREST2] = __ATTR(PREST2_mS, 0644,
time_show, time_store),
- [PPULS2] = __ATTR(PPULS2_mS, 0644,
+ [PPULS2] = __ATTR(PPULS2_uC, 0644,
coulomb_show, coulomb_store),
[NREST2] = __ATTR(NREST2_mS, 0644,
time_show, time_store),
@@ -1073,8 +1090,8 @@ static struct class_attribute qnovo_attributes[] = {
time_show, time_store),
[VMAX] = __ATTR(VMAX_uV, 0444,
voltage_show, NULL),
- [SNUM] = __ATTR(SNUM, 0644,
- time_show, time_store),
+ [SNUM] = __ATTR(SNUM, 0444,
+ time_show, NULL),
[VBATT] = __ATTR(VBATT_uV, 0444,
batt_prop_show, NULL),
[IBATT] = __ATTR(IBATT_uA, 0444,
@@ -1086,95 +1103,40 @@ static struct class_attribute qnovo_attributes[] = {
__ATTR_NULL,
};
-static void get_chg_props(struct qnovo *chip, struct chg_props *cp)
+static int qnovo_update_status(struct qnovo *chip)
{
- union power_supply_propval pval;
u8 val = 0;
int rc;
+ bool charging;
+ bool changed = false;
- cp->charging = true;
- rc = qnovo_read(chip, QNOVO_ERROR_STS, &val, 1);
+ rc = qnovo_read(chip, QNOVO_ERROR_STS2, &val, 1);
if (rc < 0) {
pr_err("Couldn't read error sts rc = %d\n", rc);
- cp->charging = false;
+ charging = false;
} else {
- cp->charging = (!(val & QNOVO_ERROR_BIT));
+ charging = !(val & QNOVO_ERROR_CHARGING_DISABLED);
}
- if (chip->wa_flags & QNOVO_NO_ERR_STS_BIT) {
- /*
- * on v1.0 and v1.1 pmic's force charging to true
- * if things are not good to charge s/w gets a PTRAIN_DONE
- * interrupt
- */
- cp->charging = true;
+ if (chip->ok_to_qnovo ^ charging) {
+
+ vote(chip->disable_votable, OK_TO_QNOVO_VOTER, !charging, 0);
+ if (!charging)
+ vote(chip->disable_votable, USER_VOTER, true, 0);
+
+ chip->ok_to_qnovo = charging;
+ changed = true;
}
- cp->usb_online = false;
- if (!chip->usb_psy)
- chip->usb_psy = power_supply_get_by_name("usb");
- if (chip->usb_psy) {
- rc = power_supply_get_property(chip->usb_psy,
- POWER_SUPPLY_PROP_ONLINE, &pval);
- if (rc < 0)
- pr_err("Couldn't read usb online rc = %d\n", rc);
- else
- cp->usb_online = (bool)pval.intval;
- }
-
- cp->dc_online = false;
- if (!chip->dc_psy)
- chip->dc_psy = power_supply_get_by_name("dc");
- if (chip->dc_psy) {
- rc = power_supply_get_property(chip->dc_psy,
- POWER_SUPPLY_PROP_ONLINE, &pval);
- if (rc < 0)
- pr_err("Couldn't read dc online rc = %d\n", rc);
- else
- cp->dc_online = (bool)pval.intval;
- }
-}
-
-static void get_chg_status(struct qnovo *chip, const struct chg_props *cp,
- struct chg_status *cs)
-{
- cs->ok_to_qnovo = false;
-
- if (cp->charging &&
- (cp->usb_online || cp->dc_online))
- cs->ok_to_qnovo = true;
+ return changed;
}
static void status_change_work(struct work_struct *work)
{
struct qnovo *chip = container_of(work,
struct qnovo, status_change_work);
- bool notify_uevent = false;
- struct chg_props cp;
- struct chg_status cs;
- get_chg_props(chip, &cp);
- get_chg_status(chip, &cp, &cs);
-
- if (cs.ok_to_qnovo ^ chip->cs.ok_to_qnovo) {
- /*
- * when it is not okay to Qnovo charge, disable both voters,
- * so that when it becomes okay to Qnovo charge the user voter
- * has to specifically enable its vote to being Qnovo charging
- */
- if (!cs.ok_to_qnovo) {
- vote(chip->disable_votable, OK_TO_QNOVO_VOTER, 1, 0);
- vote(chip->disable_votable, USER_VOTER, 1, 0);
- } else {
- vote(chip->disable_votable, OK_TO_QNOVO_VOTER, 0, 0);
- }
- notify_uevent = true;
- }
-
- memcpy(&chip->cp, &cp, sizeof(struct chg_props));
- memcpy(&chip->cs, &cs, sizeof(struct chg_status));
-
- if (notify_uevent)
+ if (qnovo_update_status(chip))
kobject_uevent(&chip->dev->kobj, KOBJ_CHANGE);
}
@@ -1186,8 +1148,8 @@ static int qnovo_notifier_call(struct notifier_block *nb,
if (ev != PSY_EVENT_PROP_CHANGED)
return NOTIFY_OK;
- if ((strcmp(psy->desc->name, "battery") == 0)
- || (strcmp(psy->desc->name, "usb") == 0))
+
+ if (strcmp(psy->desc->name, "battery") == 0)
schedule_work(&chip->status_change_work);
return NOTIFY_OK;
@@ -1197,8 +1159,7 @@ static irqreturn_t handle_ptrain_done(int irq, void *data)
{
struct qnovo *chip = data;
- /* disable user voter here */
- vote(chip->disable_votable, USER_VOTER, 0, 0);
+ qnovo_update_status(chip);
kobject_uevent(&chip->dev->kobj, KOBJ_CHANGE);
return IRQ_HANDLED;
}
@@ -1211,7 +1172,14 @@ static int qnovo_hw_init(struct qnovo *chip)
u8 vadc_offset, vadc_gain;
u8 val;
- vote(chip->disable_votable, USER_VOTER, 1, 0);
+ vote(chip->disable_votable, USER_VOTER, true, 0);
+
+ val = 0;
+ rc = qnovo_write(chip, QNOVO_STRM_CTRL, &val, 1);
+ if (rc < 0) {
+ pr_err("Couldn't write iadc bitstream control rc = %d\n", rc);
+ return rc;
+ }
rc = qnovo_read(chip, QNOVO_IADC_OFFSET_0, &iadc_offset_external, 1);
if (rc < 0) {
@@ -1219,12 +1187,28 @@ static int qnovo_hw_init(struct qnovo *chip)
return rc;
}
+ /* stored as an 8 bit 2's complement signed integer */
+ val = -1 * iadc_offset_external;
+ rc = qnovo_write(chip, QNOVO_TR_IADC_OFFSET_0, &val, 1);
+ if (rc < 0) {
+ pr_err("Couldn't write iadc offset rc = %d\n", rc);
+ return rc;
+ }
+
rc = qnovo_read(chip, QNOVO_IADC_OFFSET_1, &iadc_offset_internal, 1);
if (rc < 0) {
pr_err("Couldn't read iadc internal offset rc = %d\n", rc);
return rc;
}
+ /* stored as an 8 bit 2's complement signed integer */
+ val = -1 * iadc_offset_internal;
+ rc = qnovo_write(chip, QNOVO_TR_IADC_OFFSET_1, &val, 1);
+ if (rc < 0) {
+ pr_err("Couldn't write iadc offset rc = %d\n", rc);
+ return rc;
+ }
+
rc = qnovo_read(chip, QNOVO_IADC_GAIN_0, &iadc_gain_external, 1);
if (rc < 0) {
pr_err("Couldn't read iadc external gain rc = %d\n", rc);
@@ -1249,53 +1233,20 @@ static int qnovo_hw_init(struct qnovo *chip)
return rc;
}
- chip->external_offset_nA = (s64)iadc_offset_external * IADC_LSB_NA;
- chip->internal_offset_nA = (s64)iadc_offset_internal * IADC_LSB_NA;
- chip->offset_nV = (s64)vadc_offset * VADC_LSB_NA;
+ chip->external_offset_nA = (s64)(s8)iadc_offset_external * IADC_LSB_NA;
+ chip->internal_offset_nA = (s64)(s8)iadc_offset_internal * IADC_LSB_NA;
+ chip->offset_nV = (s64)(s8)vadc_offset * VADC_LSB_NA;
chip->external_i_gain_mega
- = 1000000000 + (s64)iadc_gain_external * GAIN_LSB_FACTOR;
+ = 1000000000 + (s64)(s8)iadc_gain_external * GAIN_LSB_FACTOR;
chip->external_i_gain_mega
= div_s64(chip->external_i_gain_mega, 1000);
chip->internal_i_gain_mega
- = 1000000000 + (s64)iadc_gain_internal * GAIN_LSB_FACTOR;
+ = 1000000000 + (s64)(s8)iadc_gain_internal * GAIN_LSB_FACTOR;
chip->internal_i_gain_mega
= div_s64(chip->internal_i_gain_mega, 1000);
- chip->v_gain_mega = 1000000000 + (s64)vadc_gain * GAIN_LSB_FACTOR;
+ chip->v_gain_mega = 1000000000 + (s64)(s8)vadc_gain * GAIN_LSB_FACTOR;
chip->v_gain_mega = div_s64(chip->v_gain_mega, 1000);
- val = 0;
- rc = qnovo_write(chip, QNOVO_STRM_CTRL, &val, 1);
- if (rc < 0) {
- pr_err("Couldn't write iadc bitsteam control rc = %d\n", rc);
- return rc;
- }
-
- rc = qnovo_read(chip, QNOVO_TR_IADC_OFFSET_0, &val, 1);
- if (rc < 0) {
- pr_err("Couldn't read iadc offset rc = %d\n", rc);
- return rc;
- }
-
- val *= -1;
- rc = qnovo_write(chip, QNOVO_TR_IADC_OFFSET_0, &val, 1);
- if (rc < 0) {
- pr_err("Couldn't write iadc offset rc = %d\n", rc);
- return rc;
- }
-
- rc = qnovo_read(chip, QNOVO_TR_IADC_OFFSET_1, &val, 1);
- if (rc < 0) {
- pr_err("Couldn't read iadc offset rc = %d\n", rc);
- return rc;
- }
-
- val *= -1;
- rc = qnovo_write(chip, QNOVO_TR_IADC_OFFSET_1, &val, 1);
- if (rc < 0) {
- pr_err("Couldn't write iadc offset rc = %d\n", rc);
- return rc;
- }
-
return 0;
}
@@ -1333,6 +1284,9 @@ static int qnovo_request_interrupts(struct qnovo *chip)
irq_ptrain_done, rc);
return rc;
}
+
+ enable_irq_wake(irq_ptrain_done);
+
return rc;
}
@@ -1362,13 +1316,6 @@ static int qnovo_probe(struct platform_device *pdev)
return rc;
}
- rc = qnovo_check_chg_version(chip);
- if (rc < 0) {
- if (rc != -EPROBE_DEFER)
- pr_err("Couldn't check version rc=%d\n", rc);
- return rc;
- }
-
/* set driver data before resources request it */
platform_set_drvdata(pdev, chip);
@@ -1414,6 +1361,8 @@ static int qnovo_probe(struct platform_device *pdev)
goto unreg_notifier;
}
+ device_init_wakeup(chip->dev, true);
+
return rc;
unreg_notifier:
diff --git a/drivers/power/supply/qcom/qpnp-smb2.c b/drivers/power/supply/qcom/qpnp-smb2.c
index dab7888..8fd45f18 100644
--- a/drivers/power/supply/qcom/qpnp-smb2.c
+++ b/drivers/power/supply/qcom/qpnp-smb2.c
@@ -11,6 +11,7 @@
*/
#include <linux/debugfs.h>
+#include <linux/delay.h>
#include <linux/device.h>
#include <linux/module.h>
#include <linux/platform_device.h>
@@ -25,7 +26,7 @@
#include "smb-reg.h"
#include "smb-lib.h"
#include "storm-watch.h"
-#include "pmic-voter.h"
+#include <linux/pmic-voter.h>
#define SMB2_DEFAULT_WPWR_UW 8000000
@@ -239,7 +240,6 @@ static struct smb_params pm660_params = {
struct smb_dt_props {
int fcc_ua;
int usb_icl_ua;
- int otg_cl_ua;
int dc_icl_ua;
int boost_threshold_ua;
int fv_uv;
@@ -323,9 +323,9 @@ static int smb2_parse_dt(struct smb2 *chip)
chip->dt.usb_icl_ua = -EINVAL;
rc = of_property_read_u32(node,
- "qcom,otg-cl-ua", &chip->dt.otg_cl_ua);
+ "qcom,otg-cl-ua", &chg->otg_cl_ua);
if (rc < 0)
- chip->dt.otg_cl_ua = MICRO_1P5A;
+ chg->otg_cl_ua = MICRO_1P5A;
rc = of_property_read_u32(node,
"qcom,dc-icl-ua", &chip->dt.dc_icl_ua);
@@ -414,6 +414,7 @@ static enum power_supply_property smb2_usb_props[] = {
POWER_SUPPLY_PROP_BOOST_CURRENT,
POWER_SUPPLY_PROP_PE_START,
POWER_SUPPLY_PROP_CTM_CURRENT_MAX,
+ POWER_SUPPLY_PROP_HW_CURRENT_MAX,
};
static int smb2_usb_get_prop(struct power_supply *psy,
@@ -502,6 +503,9 @@ static int smb2_usb_get_prop(struct power_supply *psy,
case POWER_SUPPLY_PROP_CTM_CURRENT_MAX:
val->intval = get_client_vote(chg->usb_icl_votable, CTM_VOTER);
break;
+ case POWER_SUPPLY_PROP_HW_CURRENT_MAX:
+ rc = smblib_get_charge_current(chg, &val->intval);
+ break;
default:
pr_err("get prop %d is not supported in usb\n", psp);
rc = -EINVAL;
@@ -610,12 +614,12 @@ static int smb2_init_usb_psy(struct smb2 *chip)
static enum power_supply_property smb2_usb_main_props[] = {
POWER_SUPPLY_PROP_VOLTAGE_MAX,
- POWER_SUPPLY_PROP_ICL_REDUCTION,
POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX,
POWER_SUPPLY_PROP_TYPE,
POWER_SUPPLY_PROP_INPUT_CURRENT_SETTLED,
POWER_SUPPLY_PROP_INPUT_VOLTAGE_SETTLED,
POWER_SUPPLY_PROP_FCC_DELTA,
+ POWER_SUPPLY_PROP_CURRENT_MAX,
/*
* TODO move the TEMP and TEMP_MAX properties here,
* and update the thermal balancer to look here
@@ -634,9 +638,6 @@ static int smb2_usb_main_get_prop(struct power_supply *psy,
case POWER_SUPPLY_PROP_VOLTAGE_MAX:
rc = smblib_get_charge_param(chg, &chg->param.fv, &val->intval);
break;
- case POWER_SUPPLY_PROP_ICL_REDUCTION:
- val->intval = chg->icl_reduction_ua;
- break;
case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX:
rc = smblib_get_charge_param(chg, &chg->param.fcc,
&val->intval);
@@ -653,6 +654,9 @@ static int smb2_usb_main_get_prop(struct power_supply *psy,
case POWER_SUPPLY_PROP_FCC_DELTA:
rc = smblib_get_prop_fcc_delta(chg, val);
break;
+ case POWER_SUPPLY_PROP_CURRENT_MAX:
+ val->intval = get_effective_result(chg->usb_icl_votable);
+ break;
default:
pr_debug("get prop %d is not supported in usb-main\n", psp);
rc = -EINVAL;
@@ -677,12 +681,12 @@ static int smb2_usb_main_set_prop(struct power_supply *psy,
case POWER_SUPPLY_PROP_VOLTAGE_MAX:
rc = smblib_set_charge_param(chg, &chg->param.fv, val->intval);
break;
- case POWER_SUPPLY_PROP_ICL_REDUCTION:
- rc = smblib_set_icl_reduction(chg, val->intval);
- break;
case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX:
rc = smblib_set_charge_param(chg, &chg->param.fcc, val->intval);
break;
+ case POWER_SUPPLY_PROP_CURRENT_MAX:
+ rc = smblib_set_icl_current(chg, val->intval);
+ break;
default:
pr_err("set prop %d is not supported\n", psp);
rc = -EINVAL;
@@ -838,7 +842,9 @@ static enum power_supply_property smb2_batt_props[] = {
POWER_SUPPLY_PROP_INPUT_CURRENT_LIMITED,
POWER_SUPPLY_PROP_VOLTAGE_NOW,
POWER_SUPPLY_PROP_VOLTAGE_MAX,
+ POWER_SUPPLY_PROP_VOLTAGE_QNOVO,
POWER_SUPPLY_PROP_CURRENT_NOW,
+ POWER_SUPPLY_PROP_CURRENT_QNOVO,
POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX,
POWER_SUPPLY_PROP_TEMP,
POWER_SUPPLY_PROP_TECHNOLOGY,
@@ -858,6 +864,7 @@ static int smb2_batt_get_prop(struct power_supply *psy,
{
struct smb_charger *chg = power_supply_get_drvdata(psy);
int rc = 0;
+ union power_supply_propval pval = {0, };
switch (psp) {
case POWER_SUPPLY_PROP_STATUS:
@@ -882,7 +889,14 @@ static int smb2_batt_get_prop(struct power_supply *psy,
rc = smblib_get_prop_system_temp_level(chg, val);
break;
case POWER_SUPPLY_PROP_CHARGER_TEMP:
- rc = smblib_get_prop_charger_temp(chg, val);
+ /* do not query RRADC if charger is not present */
+ rc = smblib_get_prop_usb_present(chg, &pval);
+ if (rc < 0)
+ pr_err("Couldn't get usb present rc=%d\n", rc);
+
+ rc = -ENODATA;
+ if (pval.intval)
+ rc = smblib_get_prop_charger_temp(chg, val);
break;
case POWER_SUPPLY_PROP_CHARGER_TEMP_MAX:
rc = smblib_get_prop_charger_temp_max(chg, val);
@@ -902,6 +916,9 @@ static int smb2_batt_get_prop(struct power_supply *psy,
case POWER_SUPPLY_PROP_VOLTAGE_MAX:
val->intval = get_client_vote(chg->fv_votable, DEFAULT_VOTER);
break;
+ case POWER_SUPPLY_PROP_CHARGE_QNOVO_ENABLE:
+ rc = smblib_get_prop_charge_qnovo_enable(chg, val);
+ break;
case POWER_SUPPLY_PROP_VOLTAGE_QNOVO:
val->intval = chg->qnovo_fv_uv;
break;
@@ -977,12 +994,17 @@ static int smb2_batt_set_prop(struct power_supply *psy,
case POWER_SUPPLY_PROP_VOLTAGE_MAX:
vote(chg->fv_votable, DEFAULT_VOTER, true, val->intval);
break;
+ case POWER_SUPPLY_PROP_CHARGE_QNOVO_ENABLE:
+ rc = smblib_set_prop_charge_qnovo_enable(chg, val);
+ break;
case POWER_SUPPLY_PROP_VOLTAGE_QNOVO:
chg->qnovo_fv_uv = val->intval;
rc = rerun_election(chg->fv_votable);
break;
case POWER_SUPPLY_PROP_CURRENT_QNOVO:
chg->qnovo_fcc_ua = val->intval;
+ vote(chg->pl_disable_votable, PL_QNOVO_VOTER,
+ val->intval != -EINVAL && val->intval < 2000000, 0);
rc = rerun_election(chg->fcc_votable);
break;
case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX:
@@ -1115,6 +1137,9 @@ static int smb2_init_vconn_regulator(struct smb2 *chip)
struct regulator_config cfg = {};
int rc = 0;
+ if (chg->micro_usb_mode)
+ return 0;
+
chg->vconn_vreg = devm_kzalloc(chg->dev, sizeof(*chg->vconn_vreg),
GFP_KERNEL);
if (!chg->vconn_vreg)
@@ -1325,6 +1350,39 @@ static int smb2_disable_typec(struct smb_charger *chg)
{
int rc;
+ /* Move to typeC mode */
+ /* configure FSM in idle state and disable UFP_ENABLE bit */
+ rc = smblib_masked_write(chg, TYPE_C_INTRPT_ENB_SOFTWARE_CTRL_REG,
+ TYPEC_DISABLE_CMD_BIT | UFP_EN_CMD_BIT,
+ TYPEC_DISABLE_CMD_BIT);
+ if (rc < 0) {
+ dev_err(chg->dev, "Couldn't put FSM in idle rc=%d\n", rc);
+ return rc;
+ }
+
+ /* wait for FSM to enter idle state */
+ msleep(200);
+ /* configure TypeC mode */
+ rc = smblib_masked_write(chg, TYPE_C_CFG_REG,
+ TYPE_C_OR_U_USB_BIT, 0);
+ if (rc < 0) {
+ dev_err(chg->dev, "Couldn't enable micro USB mode rc=%d\n", rc);
+ return rc;
+ }
+
+ /* wait for mode change before enabling FSM */
+ usleep_range(10000, 11000);
+ /* release FSM from idle state */
+ rc = smblib_masked_write(chg, TYPE_C_INTRPT_ENB_SOFTWARE_CTRL_REG,
+ TYPEC_DISABLE_CMD_BIT, 0);
+ if (rc < 0) {
+ dev_err(chg->dev, "Couldn't release FSM rc=%d\n", rc);
+ return rc;
+ }
+
+ /* wait for FSM to start */
+ msleep(100);
+ /* move to uUSB mode */
/* configure FSM in idle state */
rc = smblib_masked_write(chg, TYPE_C_INTRPT_ENB_SOFTWARE_CTRL_REG,
TYPEC_DISABLE_CMD_BIT, TYPEC_DISABLE_CMD_BIT);
@@ -1333,6 +1391,8 @@ static int smb2_disable_typec(struct smb_charger *chg)
return rc;
}
+ /* wait for FSM to enter idle state */
+ msleep(200);
/* configure micro USB mode */
rc = smblib_masked_write(chg, TYPE_C_CFG_REG,
TYPE_C_OR_U_USB_BIT, TYPE_C_OR_U_USB_BIT);
@@ -1341,6 +1401,8 @@ static int smb2_disable_typec(struct smb_charger *chg)
return rc;
}
+ /* wait for mode change before enabling FSM */
+ usleep_range(10000, 11000);
/* release FSM from idle state */
rc = smblib_masked_write(chg, TYPE_C_INTRPT_ENB_SOFTWARE_CTRL_REG,
TYPEC_DISABLE_CMD_BIT, 0);
@@ -1386,7 +1448,8 @@ static int smb2_init_hw(struct smb2 *chip)
/* set OTG current limit */
rc = smblib_set_charge_param(chg, &chg->param.otg_cl,
- chip->dt.otg_cl_ua);
+ (chg->wa_flags & OTG_WA) ?
+ chg->param.otg_cl.min_u : chg->otg_cl_ua);
if (rc < 0) {
pr_err("Couldn't set otg current limit rc=%d\n", rc);
return rc;
@@ -1420,10 +1483,10 @@ static int smb2_init_hw(struct smb2 *chip)
DEFAULT_VOTER, true, chip->dt.fv_uv);
vote(chg->dc_icl_votable,
DEFAULT_VOTER, true, chip->dt.dc_icl_ua);
- vote(chg->hvdcp_disable_votable_indirect, DEFAULT_VOTER,
- chip->dt.hvdcp_disable, 0);
vote(chg->hvdcp_disable_votable_indirect, PD_INACTIVE_VOTER,
true, 0);
+ vote(chg->hvdcp_disable_votable_indirect, DEFAULT_VOTER,
+ chip->dt.hvdcp_disable, 0);
vote(chg->pd_disallowed_votable_indirect, CC_DETACHED_VOTER,
true, 0);
vote(chg->pd_disallowed_votable_indirect, HVDCP_TIMEOUT_VOTER,
@@ -1489,13 +1552,6 @@ static int smb2_init_hw(struct smb2 *chip)
return rc;
}
- rc = smblib_masked_write(chg, QNOVO_PT_ENABLE_CMD_REG,
- QNOVO_PT_ENABLE_CMD_BIT, QNOVO_PT_ENABLE_CMD_BIT);
- if (rc < 0) {
- dev_err(chg->dev, "Couldn't enable qnovo rc=%d\n", rc);
- return rc;
- }
-
/* configure step charging */
rc = smb2_config_step_charging(chip);
if (rc < 0) {
@@ -1520,6 +1576,16 @@ static int smb2_init_hw(struct smb2 *chip)
return rc;
}
+ /* disable h/w autonomous parallel charging control */
+ rc = smblib_masked_write(chg, MISC_CFG_REG,
+ STAT_PARALLEL_1400MA_EN_CFG_BIT, 0);
+ if (rc < 0) {
+ dev_err(chg->dev,
+ "Couldn't disable h/w autonomous parallel control rc=%d\n",
+ rc);
+ return rc;
+ }
+
/* configure float charger options */
switch (chip->dt.float_option) {
case 1:
@@ -1608,6 +1674,15 @@ static int smb2_init_hw(struct smb2 *chip)
return rc;
}
+static int smb2_post_init(struct smb2 *chip)
+{
+ struct smb_charger *chg = &chip->chg;
+
+ rerun_election(chg->usb_irq_enable_votable);
+
+ return 0;
+}
+
static int smb2_chg_config_init(struct smb2 *chip)
{
struct smb_charger *chg = &chip->chg;
@@ -1649,7 +1724,7 @@ static int smb2_chg_config_init(struct smb2 *chip)
break;
case PM660_SUBTYPE:
chip->chg.smb_version = PM660_SUBTYPE;
- chip->chg.wa_flags |= BOOST_BACK_WA;
+ chip->chg.wa_flags |= BOOST_BACK_WA | OTG_WA;
chg->param.freq_buck = pm660_params.freq_buck;
chg->param.freq_boost = pm660_params.freq_boost;
chg->chg_freq.freq_5V = 600;
@@ -2074,7 +2149,7 @@ static int smb2_probe(struct platform_device *pdev)
rc = smb2_init_vconn_regulator(chip);
if (rc < 0) {
pr_err("Couldn't initialize vconn regulator rc=%d\n",
- rc);
+ rc);
goto cleanup;
}
@@ -2137,6 +2212,8 @@ static int smb2_probe(struct platform_device *pdev)
goto cleanup;
}
+ smb2_post_init(chip);
+
smb2_create_debugfs(chip);
rc = smblib_get_prop_usb_present(chg, &val);
@@ -2167,6 +2244,8 @@ static int smb2_probe(struct platform_device *pdev)
}
batt_charge_type = val.intval;
+ device_init_wakeup(chg->dev, true);
+
pr_info("QPNP SMB2 probed successfully usb:present=%d type=%d batt:present = %d health = %d charge = %d\n",
usb_present, chg->usb_psy_desc.type,
batt_present, batt_health, batt_charge_type);
diff --git a/drivers/power/supply/qcom/smb-lib.c b/drivers/power/supply/qcom/smb-lib.c
index 6b4c214..c8deedd 100644
--- a/drivers/power/supply/qcom/smb-lib.c
+++ b/drivers/power/supply/qcom/smb-lib.c
@@ -21,7 +21,7 @@
#include "smb-lib.h"
#include "smb-reg.h"
#include "storm-watch.h"
-#include "pmic-voter.h"
+#include <linux/pmic-voter.h>
#define smblib_err(chg, fmt, ...) \
pr_err("%s: %s: " fmt, chg->name, \
@@ -160,39 +160,14 @@ static int smblib_get_jeita_cc_delta(struct smb_charger *chg, int *cc_delta_ua)
int smblib_icl_override(struct smb_charger *chg, bool override)
{
int rc;
- bool override_status;
- u8 stat;
- u16 reg;
- switch (chg->smb_version) {
- case PMI8998_SUBTYPE:
- reg = APSD_RESULT_STATUS_REG;
- break;
- case PM660_SUBTYPE:
- reg = AICL_STATUS_REG;
- break;
- default:
- smblib_dbg(chg, PR_MISC, "Unknown chip version=%x\n",
- chg->smb_version);
- return -EINVAL;
- }
+ rc = smblib_masked_write(chg, USBIN_LOAD_CFG_REG,
+ ICL_OVERRIDE_AFTER_APSD_BIT,
+ override ? ICL_OVERRIDE_AFTER_APSD_BIT : 0);
+ if (rc < 0)
+ smblib_err(chg, "Couldn't override ICL rc=%d\n", rc);
- rc = smblib_read(chg, reg, &stat);
- if (rc < 0) {
- smblib_err(chg, "Couldn't read reg=%x rc=%d\n", reg, rc);
- return rc;
- }
- override_status = (bool)(stat & ICL_OVERRIDE_LATCH_BIT);
-
- if (override != override_status) {
- rc = smblib_masked_write(chg, CMD_APSD_REG,
- ICL_OVERRIDE_BIT, ICL_OVERRIDE_BIT);
- if (rc < 0) {
- smblib_err(chg, "Couldn't override ICL rc=%d\n", rc);
- return rc;
- }
- }
- return 0;
+ return rc;
}
/********************
@@ -547,6 +522,26 @@ static int smblib_set_usb_pd_allowed_voltage(struct smb_charger *chg,
* HELPER FUNCTIONS *
********************/
+static void smblib_rerun_apsd(struct smb_charger *chg)
+{
+ int rc;
+
+ smblib_dbg(chg, PR_MISC, "re-running APSD\n");
+ if (chg->wa_flags & QC_AUTH_INTERRUPT_WA_BIT) {
+ rc = smblib_masked_write(chg,
+ USBIN_SOURCE_CHANGE_INTRPT_ENB_REG,
+ AUTH_IRQ_EN_CFG_BIT, AUTH_IRQ_EN_CFG_BIT);
+ if (rc < 0)
+ smblib_err(chg, "Couldn't enable HVDCP auth IRQ rc=%d\n",
+ rc);
+ }
+
+ rc = smblib_masked_write(chg, CMD_APSD_REG,
+ APSD_RERUN_BIT, APSD_RERUN_BIT);
+ if (rc < 0)
+ smblib_err(chg, "Couldn't re-run APSD rc=%d\n", rc);
+}
+
static int try_rerun_apsd_for_hvdcp(struct smb_charger *chg)
{
const struct apsd_result *apsd_result;
@@ -564,11 +559,7 @@ static int try_rerun_apsd_for_hvdcp(struct smb_charger *chg)
chg->hvdcp_disable_votable_indirect)) {
apsd_result = smblib_get_apsd_result(chg);
if (apsd_result->bit & (QC_2P0_BIT | QC_3P0_BIT)) {
- /* rerun APSD */
- smblib_dbg(chg, PR_MISC, "rerun APSD\n");
- smblib_masked_write(chg, CMD_APSD_REG,
- APSD_RERUN_BIT,
- APSD_RERUN_BIT);
+ smblib_rerun_apsd(chg);
}
}
}
@@ -580,12 +571,13 @@ static const struct apsd_result *smblib_update_usb_type(struct smb_charger *chg)
const struct apsd_result *apsd_result = smblib_get_apsd_result(chg);
/* if PD is active, APSD is disabled so won't have a valid result */
- if (chg->pd_active) {
+ if (chg->pd_active)
chg->usb_psy_desc.type = POWER_SUPPLY_TYPE_USB_PD;
- return apsd_result;
- }
+ else
+ chg->usb_psy_desc.type = apsd_result->pst;
- chg->usb_psy_desc.type = apsd_result->pst;
+ smblib_dbg(chg, PR_MISC, "APSD=%s PD=%d\n",
+ apsd_result->name, chg->pd_active);
return apsd_result;
}
@@ -661,10 +653,13 @@ static void smblib_uusb_removal(struct smb_charger *chg)
{
int rc;
+ cancel_delayed_work_sync(&chg->pl_enable_work);
+ vote(chg->pl_disable_votable, PL_DELAY_VOTER, true, 0);
+ vote(chg->awake_votable, PL_DELAY_VOTER, false, 0);
+
/* reset both usbin current and voltage votes */
vote(chg->pl_enable_votable_indirect, USBIN_I_VOTER, false, 0);
vote(chg->pl_enable_votable_indirect, USBIN_V_VOTER, false, 0);
- vote(chg->pl_disable_votable, PL_DELAY_HVDCP_VOTER, true, 0);
cancel_delayed_work_sync(&chg->hvdcp_detect_work);
@@ -700,28 +695,6 @@ static void smblib_uusb_removal(struct smb_charger *chg)
if (rc < 0)
smblib_err(chg,
"Couldn't un-vote DCP from USB ICL rc=%d\n", rc);
-
- /* clear USB ICL vote for PL_USBIN_USBIN_VOTER */
- rc = vote(chg->usb_icl_votable, PL_USBIN_USBIN_VOTER, false, 0);
- if (rc < 0)
- smblib_err(chg,
- "Couldn't un-vote PL_USBIN_USBIN from USB ICL rc=%d\n",
- rc);
-}
-
-static bool smblib_sysok_reason_usbin(struct smb_charger *chg)
-{
- int rc;
- u8 stat;
-
- rc = smblib_read(chg, SYSOK_REASON_STATUS_REG, &stat);
- if (rc < 0) {
- smblib_err(chg, "Couldn't get SYSOK_REASON_STATUS rc=%d\n", rc);
- /* assuming 'not usbin' in case of read failure */
- return false;
- }
-
- return stat & SYSOK_REASON_USBIN_BIT;
}
void smblib_suspend_on_debug_battery(struct smb_charger *chg)
@@ -747,7 +720,6 @@ void smblib_suspend_on_debug_battery(struct smb_charger *chg)
int smblib_rerun_apsd_if_required(struct smb_charger *chg)
{
- const struct apsd_result *apsd_result;
union power_supply_propval val;
int rc;
@@ -760,21 +732,27 @@ int smblib_rerun_apsd_if_required(struct smb_charger *chg)
if (!val.intval)
return 0;
- apsd_result = smblib_get_apsd_result(chg);
- if ((apsd_result->pst == POWER_SUPPLY_TYPE_UNKNOWN)
- || (apsd_result->pst == POWER_SUPPLY_TYPE_USB)) {
- /* rerun APSD */
- pr_info("Reruning APSD type = %s at bootup\n",
- apsd_result->name);
- rc = smblib_masked_write(chg, CMD_APSD_REG,
- APSD_RERUN_BIT,
- APSD_RERUN_BIT);
- if (rc < 0) {
- smblib_err(chg, "Couldn't rerun APSD rc = %d\n", rc);
- return rc;
+ /* fetch the DPDM regulator */
+ if (!chg->dpdm_reg && of_get_property(chg->dev->of_node,
+ "dpdm-supply", NULL)) {
+ chg->dpdm_reg = devm_regulator_get(chg->dev, "dpdm");
+ if (IS_ERR(chg->dpdm_reg)) {
+ smblib_err(chg, "Couldn't get dpdm regulator rc=%ld\n",
+ PTR_ERR(chg->dpdm_reg));
+ chg->dpdm_reg = NULL;
}
}
+ if (chg->dpdm_reg && !regulator_is_enabled(chg->dpdm_reg)) {
+ smblib_dbg(chg, PR_MISC, "enabling DPDM regulator\n");
+ rc = regulator_enable(chg->dpdm_reg);
+ if (rc < 0)
+ smblib_err(chg, "Couldn't enable dpdm regulator rc=%d\n",
+ rc);
+ }
+
+ smblib_rerun_apsd(chg);
+
return 0;
}
@@ -812,29 +790,12 @@ static int smblib_get_pulse_cnt(struct smb_charger *chg, int *count)
return 0;
}
-/*********************
- * VOTABLE CALLBACKS *
- *********************/
-
-static int smblib_dc_suspend_vote_callback(struct votable *votable, void *data,
- int suspend, const char *client)
-{
- struct smb_charger *chg = data;
-
- /* resume input if suspend is invalid */
- if (suspend < 0)
- suspend = 0;
-
- return smblib_set_dc_suspend(chg, (bool)suspend);
-}
-
#define USBIN_25MA 25000
#define USBIN_100MA 100000
#define USBIN_150MA 150000
#define USBIN_500MA 500000
#define USBIN_900MA 900000
-
static int set_sdp_current(struct smb_charger *chg, int icl_ua)
{
int rc;
@@ -873,20 +834,18 @@ static int set_sdp_current(struct smb_charger *chg, int icl_ua)
return rc;
}
-static int smblib_usb_icl_vote_callback(struct votable *votable, void *data,
- int icl_ua, const char *client)
+int smblib_set_icl_current(struct smb_charger *chg, int icl_ua)
{
- struct smb_charger *chg = data;
int rc = 0;
bool override;
union power_supply_propval pval;
/* suspend and return if 25mA or less is requested */
- if (client && (icl_ua < USBIN_25MA))
+ if (icl_ua < USBIN_25MA)
return smblib_set_usb_suspend(chg, true);
disable_irq_nosync(chg->irq_info[USBIN_ICL_CHANGE_IRQ].irq);
- if (!client)
+ if (icl_ua == INT_MAX)
goto override_suspend_config;
rc = smblib_get_prop_typec_mode(chg, &pval);
@@ -904,8 +863,7 @@ static int smblib_usb_icl_vote_callback(struct votable *votable, void *data,
goto enable_icl_changed_interrupt;
}
} else {
- rc = smblib_set_charge_param(chg, &chg->param.usb_icl,
- icl_ua - chg->icl_reduction_ua);
+ rc = smblib_set_charge_param(chg, &chg->param.usb_icl, icl_ua);
if (rc < 0) {
smblib_err(chg, "Couldn't set HC ICL rc=%d\n", rc);
goto enable_icl_changed_interrupt;
@@ -915,7 +873,7 @@ static int smblib_usb_icl_vote_callback(struct votable *votable, void *data,
override_suspend_config:
/* determine if override needs to be enforced */
override = true;
- if (client == NULL) {
+ if (icl_ua == INT_MAX) {
/* remove override if no voters - hw defaults is desired */
override = false;
} else if (pval.intval == POWER_SUPPLY_TYPEC_SOURCE_DEFAULT) {
@@ -923,7 +881,7 @@ static int smblib_usb_icl_vote_callback(struct votable *votable, void *data,
/* For std cable with type = SDP never override */
override = false;
else if (chg->usb_psy_desc.type == POWER_SUPPLY_TYPE_USB_CDP
- && icl_ua - chg->icl_reduction_ua == 1500000)
+ && icl_ua == 1500000)
/*
* For std cable with type = CDP override only if
* current is not 1500mA
@@ -953,6 +911,22 @@ static int smblib_usb_icl_vote_callback(struct votable *votable, void *data,
return rc;
}
+/*********************
+ * VOTABLE CALLBACKS *
+ *********************/
+
+static int smblib_dc_suspend_vote_callback(struct votable *votable, void *data,
+ int suspend, const char *client)
+{
+ struct smb_charger *chg = data;
+
+ /* resume input if suspend is invalid */
+ if (suspend < 0)
+ suspend = 0;
+
+ return smblib_set_dc_suspend(chg, (bool)suspend);
+}
+
static int smblib_dc_icl_vote_callback(struct votable *votable, void *data,
int icl_ua, const char *client)
{
@@ -1089,16 +1063,6 @@ static int smblib_apsd_disable_vote_callback(struct votable *votable,
int rc;
if (apsd_disable) {
- /* Don't run APSD on CC debounce when APSD is disabled */
- rc = smblib_masked_write(chg, TYPE_C_CFG_REG,
- APSD_START_ON_CC_BIT,
- 0);
- if (rc < 0) {
- smblib_err(chg, "Couldn't disable APSD_START_ON_CC rc=%d\n",
- rc);
- return rc;
- }
-
rc = smblib_masked_write(chg, USBIN_OPTIONS_1_CFG_REG,
AUTO_SRC_DETECT_BIT,
0);
@@ -1114,15 +1078,6 @@ static int smblib_apsd_disable_vote_callback(struct votable *votable,
smblib_err(chg, "Couldn't enable APSD rc=%d\n", rc);
return rc;
}
-
- rc = smblib_masked_write(chg, TYPE_C_CFG_REG,
- APSD_START_ON_CC_BIT,
- APSD_START_ON_CC_BIT);
- if (rc < 0) {
- smblib_err(chg, "Couldn't enable APSD_START_ON_CC rc=%d\n",
- rc);
- return rc;
- }
}
return 0;
@@ -1159,6 +1114,26 @@ static int smblib_hvdcp_hw_inov_dis_vote_callback(struct votable *votable,
return rc;
}
+static int smblib_usb_irq_enable_vote_callback(struct votable *votable,
+ void *data, int enable, const char *client)
+{
+ struct smb_charger *chg = data;
+
+ if (!chg->irq_info[INPUT_CURRENT_LIMIT_IRQ].irq ||
+ !chg->irq_info[HIGH_DUTY_CYCLE_IRQ].irq)
+ return 0;
+
+ if (enable) {
+ enable_irq(chg->irq_info[INPUT_CURRENT_LIMIT_IRQ].irq);
+ enable_irq(chg->irq_info[HIGH_DUTY_CYCLE_IRQ].irq);
+ } else {
+ disable_irq(chg->irq_info[INPUT_CURRENT_LIMIT_IRQ].irq);
+ disable_irq(chg->irq_info[HIGH_DUTY_CYCLE_IRQ].irq);
+ }
+
+ return 0;
+}
+
/*******************
* VCONN REGULATOR *
* *****************/
@@ -1281,11 +1256,14 @@ int smblib_vconn_regulator_is_enabled(struct regulator_dev *rdev)
/*****************
* OTG REGULATOR *
*****************/
-
+#define MAX_RETRY 15
+#define MIN_DELAY_US 2000
+#define MAX_DELAY_US 9000
static int _smblib_vbus_regulator_enable(struct regulator_dev *rdev)
{
struct smb_charger *chg = rdev_get_drvdata(rdev);
- int rc;
+ int rc, retry_count = 0, min_delay = MIN_DELAY_US;
+ u8 stat;
smblib_dbg(chg, PR_OTG, "halt 1 in 8 mode\n");
rc = smblib_masked_write(chg, OTG_ENG_OTG_CFG_REG,
@@ -1304,6 +1282,42 @@ static int _smblib_vbus_regulator_enable(struct regulator_dev *rdev)
return rc;
}
+ if (chg->wa_flags & OTG_WA) {
+ /* check for softstart */
+ do {
+ usleep_range(min_delay, min_delay + 100);
+ rc = smblib_read(chg, OTG_STATUS_REG, &stat);
+ if (rc < 0) {
+ smblib_err(chg,
+ "Couldn't read OTG status rc=%d\n",
+ rc);
+ goto out;
+ }
+
+ if (stat & BOOST_SOFTSTART_DONE_BIT) {
+ rc = smblib_set_charge_param(chg,
+ &chg->param.otg_cl, chg->otg_cl_ua);
+ if (rc < 0)
+ smblib_err(chg,
+ "Couldn't set otg limit\n");
+ break;
+ }
+
+ /* increase the delay for following iterations */
+ if (retry_count > 5)
+ min_delay = MAX_DELAY_US;
+ } while (retry_count++ < MAX_RETRY);
+
+ if (retry_count >= MAX_RETRY) {
+ smblib_dbg(chg, PR_OTG, "Boost Softstart not done\n");
+ goto out;
+ }
+ }
+
+ return 0;
+out:
+ /* disable OTG if softstart failed */
+ smblib_write(chg, CMD_OTG_REG, 0);
return rc;
}
@@ -1316,6 +1330,14 @@ int smblib_vbus_regulator_enable(struct regulator_dev *rdev)
if (chg->otg_en)
goto unlock;
+ if (!chg->usb_icl_votable) {
+ chg->usb_icl_votable = find_votable("USB_ICL");
+
+ if (!chg->usb_icl_votable)
+ return -EINVAL;
+ }
+ vote(chg->usb_icl_votable, USBIN_USBIN_BOOST_VOTER, true, 0);
+
rc = _smblib_vbus_regulator_enable(rdev);
if (rc >= 0)
chg->otg_en = true;
@@ -1337,6 +1359,17 @@ static int _smblib_vbus_regulator_disable(struct regulator_dev *rdev)
smblib_err(chg, "Couldn't disable VCONN rc=%d\n", rc);
}
+ if (chg->wa_flags & OTG_WA) {
+ /* set OTG current limit to minimum value */
+ rc = smblib_set_charge_param(chg, &chg->param.otg_cl,
+ chg->param.otg_cl.min_u);
+ if (rc < 0) {
+ smblib_err(chg,
+ "Couldn't set otg current limit rc=%d\n", rc);
+ return rc;
+ }
+ }
+
smblib_dbg(chg, PR_OTG, "disabling OTG\n");
rc = smblib_write(chg, CMD_OTG_REG, 0);
if (rc < 0) {
@@ -1345,7 +1378,6 @@ static int _smblib_vbus_regulator_disable(struct regulator_dev *rdev)
}
smblib_dbg(chg, PR_OTG, "start 1 in 8 mode\n");
- rc = smblib_write(chg, CMD_OTG_REG, 0);
rc = smblib_masked_write(chg, OTG_ENG_OTG_CFG_REG,
ENG_BUCKBOOST_HALT1_8_MODE_BIT, 0);
if (rc < 0) {
@@ -1369,6 +1401,8 @@ int smblib_vbus_regulator_disable(struct regulator_dev *rdev)
if (rc >= 0)
chg->otg_en = false;
+ if (chg->usb_icl_votable)
+ vote(chg->usb_icl_votable, USBIN_USBIN_BOOST_VOTER, false, 0);
unlock:
mutex_unlock(&chg->otg_oc_lock);
return rc;
@@ -1682,6 +1716,23 @@ int smblib_get_prop_batt_charge_done(struct smb_charger *chg,
return 0;
}
+int smblib_get_prop_charge_qnovo_enable(struct smb_charger *chg,
+ union power_supply_propval *val)
+{
+ int rc;
+ u8 stat;
+
+ rc = smblib_read(chg, QNOVO_PT_ENABLE_CMD_REG, &stat);
+ if (rc < 0) {
+ smblib_err(chg, "Couldn't read QNOVO_PT_ENABLE_CMD rc=%d\n",
+ rc);
+ return rc;
+ }
+
+ val->intval = (bool)(stat & QNOVO_PT_ENABLE_CMD_BIT);
+ return 0;
+}
+
/***********************
* BATTERY PSY SETTERS *
***********************/
@@ -1733,6 +1784,10 @@ int smblib_set_prop_system_temp_level(struct smb_charger *chg,
return -EINVAL;
chg->system_temp_level = val->intval;
+ /* disable parallel charge in case of system temp level */
+ vote(chg->pl_disable_votable, THERMAL_DAEMON_VOTER,
+ chg->system_temp_level ? true : false, 0);
+
if (chg->system_temp_level == chg->thermal_levels)
return vote(chg->chg_disable_votable,
THERMAL_DAEMON_VOTER, true, 0);
@@ -1746,6 +1801,22 @@ int smblib_set_prop_system_temp_level(struct smb_charger *chg,
return 0;
}
+int smblib_set_prop_charge_qnovo_enable(struct smb_charger *chg,
+ const union power_supply_propval *val)
+{
+ int rc = 0;
+
+ rc = smblib_masked_write(chg, QNOVO_PT_ENABLE_CMD_REG,
+ QNOVO_PT_ENABLE_CMD_BIT,
+ val->intval ? QNOVO_PT_ENABLE_CMD_BIT : 0);
+ if (rc < 0) {
+ dev_err(chg->dev, "Couldn't enable qnovo rc=%d\n", rc);
+ return rc;
+ }
+
+ return rc;
+}
+
int smblib_rerun_aicl(struct smb_charger *chg)
{
int rc, settled_icl_ua;
@@ -1951,7 +2022,7 @@ int smblib_get_prop_usb_online(struct smb_charger *chg,
int rc = 0;
u8 stat;
- if (get_client_vote(chg->usb_icl_votable, USER_VOTER) == 0) {
+ if (get_client_vote_locked(chg->usb_icl_votable, USER_VOTER) == 0) {
val->intval = false;
return rc;
}
@@ -2445,10 +2516,6 @@ int smblib_set_prop_usb_voltage_min(struct smb_charger *chg,
return rc;
}
- if (chg->mode == PARALLEL_MASTER)
- vote(chg->pl_enable_votable_indirect, USBIN_V_VOTER,
- min_uv > MICRO_5V, 0);
-
chg->voltage_min_uv = min_uv;
return rc;
}
@@ -2468,10 +2535,6 @@ int smblib_set_prop_usb_voltage_max(struct smb_charger *chg,
}
chg->voltage_max_uv = max_uv;
- rc = smblib_rerun_aicl(chg);
- if (rc < 0)
- smblib_err(chg, "Couldn't re-run AICL rc=%d\n", rc);
-
return rc;
}
@@ -2491,6 +2554,7 @@ int smblib_set_prop_pd_active(struct smb_charger *chg,
vote(chg->apsd_disable_votable, PD_VOTER, pd_active, 0);
vote(chg->pd_allowed_votable, PD_VOTER, pd_active, 0);
+ vote(chg->usb_irq_enable_votable, PD_VOTER, pd_active, 0);
/*
* VCONN_EN_ORIENTATION_BIT controls whether to use CC1 or CC2 line
@@ -2526,6 +2590,9 @@ int smblib_set_prop_pd_active(struct smb_charger *chg,
return rc;
}
+ /* since PD was found the cable must be non-legacy */
+ vote(chg->usb_icl_votable, LEGACY_UNKNOWN_VOTER, false, 0);
+
/* clear USB ICL vote for DCP_VOTER */
rc = vote(chg->usb_icl_votable, DCP_VOTER, false, 0);
if (rc < 0)
@@ -2533,29 +2600,12 @@ int smblib_set_prop_pd_active(struct smb_charger *chg,
"Couldn't un-vote DCP from USB ICL rc=%d\n",
rc);
- /* clear USB ICL vote for PL_USBIN_USBIN_VOTER */
- rc = vote(chg->usb_icl_votable, PL_USBIN_USBIN_VOTER, false, 0);
- if (rc < 0)
- smblib_err(chg,
- "Couldn't un-vote PL_USBIN_USBIN from USB ICL rc=%d\n",
- rc);
-
/* remove USB_PSY_VOTER */
rc = vote(chg->usb_icl_votable, USB_PSY_VOTER, false, 0);
if (rc < 0) {
smblib_err(chg, "Couldn't unvote USB_PSY rc=%d\n", rc);
return rc;
}
-
- /* pd active set, parallel charger can be enabled now */
- rc = vote(chg->pl_disable_votable, PL_DELAY_HVDCP_VOTER,
- false, 0);
- if (rc < 0) {
- smblib_err(chg,
- "Couldn't unvote PL_DELAY_HVDCP_VOTER rc=%d\n",
- rc);
- return rc;
- }
}
/* CC pin selection s/w override in PD session; h/w otherwise. */
@@ -2643,12 +2693,6 @@ int smblib_reg_block_restore(struct smb_charger *chg,
static struct reg_info cc2_detach_settings[] = {
{
- .reg = TYPE_C_CFG_REG,
- .mask = APSD_START_ON_CC_BIT,
- .val = 0,
- .desc = "TYPE_C_CFG_REG",
- },
- {
.reg = TYPE_C_CFG_2_REG,
.mask = TYPE_C_UFP_MODE_BIT | EN_TRY_SOURCE_MODE_BIT,
.val = TYPE_C_UFP_MODE_BIT,
@@ -2810,15 +2854,21 @@ int smblib_get_prop_fcc_delta(struct smb_charger *chg,
#define TYPEC_DEFAULT_CURRENT_MA 900000
#define TYPEC_MEDIUM_CURRENT_MA 1500000
#define TYPEC_HIGH_CURRENT_MA 3000000
-static int smblib_get_charge_current(struct smb_charger *chg,
+int smblib_get_charge_current(struct smb_charger *chg,
int *total_current_ua)
{
const struct apsd_result *apsd_result = smblib_update_usb_type(chg);
union power_supply_propval val = {0, };
- int rc, typec_source_rd, current_ua;
+ int rc = 0, typec_source_rd, current_ua;
bool non_compliant;
u8 stat5;
+ if (chg->pd_active) {
+ *total_current_ua =
+ get_client_vote_locked(chg->usb_icl_votable, PD_VOTER);
+ return rc;
+ }
+
rc = smblib_read(chg, TYPE_C_STATUS_5_REG, &stat5);
if (rc < 0) {
smblib_err(chg, "Couldn't read TYPE_C_STATUS_5 rc=%d\n", rc);
@@ -2893,39 +2943,12 @@ static int smblib_get_charge_current(struct smb_charger *chg,
return 0;
}
-int smblib_set_icl_reduction(struct smb_charger *chg, int reduction_ua)
-{
- int current_ua, rc;
-
- if (reduction_ua == 0) {
- vote(chg->usb_icl_votable, PL_USBIN_USBIN_VOTER, false, 0);
- } else {
- /*
- * No usb_icl voter means we are defaulting to hw chosen
- * max limit. We need a vote from s/w to enforce the reduction.
- */
- if (get_effective_result(chg->usb_icl_votable) == -EINVAL) {
- rc = smblib_get_charge_current(chg, ¤t_ua);
- if (rc < 0) {
- pr_err("Failed to get ICL rc=%d\n", rc);
- return rc;
- }
- vote(chg->usb_icl_votable, PL_USBIN_USBIN_VOTER, true,
- current_ua);
- }
- }
-
- chg->icl_reduction_ua = reduction_ua;
-
- return rerun_election(chg->usb_icl_votable);
-}
-
/************************
* PARALLEL PSY GETTERS *
************************/
int smblib_get_prop_slave_current_now(struct smb_charger *chg,
- union power_supply_propval *pval)
+ union power_supply_propval *pval)
{
if (IS_ERR_OR_NULL(chg->iio.batt_i_chan))
chg->iio.batt_i_chan = iio_channel_get(chg->dev, "batt_i");
@@ -2962,6 +2985,14 @@ irqreturn_t smblib_handle_otg_overcurrent(int irq, void *data)
return IRQ_HANDLED;
}
+ if (chg->wa_flags & OTG_WA) {
+ if (stat & OTG_OC_DIS_SW_STS_RT_STS_BIT)
+ smblib_err(chg, "OTG disabled by hw\n");
+
+ /* not handling software based hiccups for PM660 */
+ return IRQ_HANDLED;
+ }
+
if (stat & OTG_OVERCURRENT_RT_STS_BIT)
schedule_work(&chg->otg_oc_work);
@@ -2980,7 +3011,7 @@ irqreturn_t smblib_handle_chg_state_change(int irq, void *data)
rc = smblib_read(chg, BATTERY_CHARGER_STATUS_1_REG, &stat);
if (rc < 0) {
smblib_err(chg, "Couldn't read BATTERY_CHARGER_STATUS_1 rc=%d\n",
- rc);
+ rc);
return IRQ_HANDLED;
}
@@ -3085,6 +3116,7 @@ irqreturn_t smblib_handle_usbin_uv(int irq, void *data)
return IRQ_HANDLED;
}
+#define PL_DELAY_MS 30000
irqreturn_t smblib_handle_usb_plugin(int irq, void *data)
{
struct smb_irq_data *irq_data = data;
@@ -3123,6 +3155,11 @@ irqreturn_t smblib_handle_usb_plugin(int irq, void *data)
smblib_err(chg, "Couldn't enable dpdm regulator rc=%d\n",
rc);
}
+
+ /* Schedule work to enable parallel charger */
+ vote(chg->awake_votable, PL_DELAY_VOTER, true, 0);
+ schedule_delayed_work(&chg->pl_enable_work,
+ msecs_to_jiffies(PL_DELAY_MS));
} else {
if (chg->wa_flags & BOOST_BACK_WA)
vote(chg->usb_icl_votable, BOOST_BACK_VOTER, false, 0);
@@ -3177,6 +3214,7 @@ irqreturn_t smblib_handle_icl_change(int irq, void *data)
|| (stat & AICL_DONE_BIT))
delay = 0;
+ cancel_delayed_work_sync(&chg->icl_change_work);
schedule_delayed_work(&chg->icl_change_work,
msecs_to_jiffies(delay));
}
@@ -3286,11 +3324,19 @@ static void smblib_handle_hvdcp_3p0_auth_done(struct smb_charger *chg,
if (chg->mode == PARALLEL_MASTER)
vote(chg->pl_enable_votable_indirect, USBIN_V_VOTER, true, 0);
- /* QC authentication done, parallel charger can be enabled now */
- vote(chg->pl_disable_votable, PL_DELAY_HVDCP_VOTER, false, 0);
-
/* the APSD done handler will set the USB supply type */
apsd_result = smblib_get_apsd_result(chg);
+ if (get_effective_result(chg->hvdcp_hw_inov_dis_votable)) {
+ if (apsd_result->pst == POWER_SUPPLY_TYPE_USB_HVDCP) {
+ /* force HVDCP2 to 9V if INOV is disabled */
+ rc = smblib_masked_write(chg, CMD_HVDCP_2_REG,
+ FORCE_9V_BIT, FORCE_9V_BIT);
+ if (rc < 0)
+ smblib_err(chg,
+ "Couldn't force 9V HVDCP rc=%d\n", rc);
+ }
+ }
+
smblib_dbg(chg, PR_INTERRUPT, "IRQ: hvdcp-3p0-auth-done rising; %s detected\n",
apsd_result->name);
}
@@ -3308,6 +3354,10 @@ static void smblib_handle_hvdcp_check_timeout(struct smb_charger *chg,
/* could be a legacy cable, try doing hvdcp */
try_rerun_apsd_for_hvdcp(chg);
+ /* enable HDC and ICL irq for QC2/3 charger */
+ if (qc_charger)
+ vote(chg->usb_irq_enable_votable, QC_VOTER, true, 0);
+
/*
* HVDCP detection timeout done
* If adapter is not QC2.0/QC3.0 - it is a plain old DCP.
@@ -3316,15 +3366,6 @@ static void smblib_handle_hvdcp_check_timeout(struct smb_charger *chg,
/* enforce DCP ICL if specified */
vote(chg->usb_icl_votable, DCP_VOTER,
chg->dcp_icl_ua != -EINVAL, chg->dcp_icl_ua);
- /*
- * If adapter is not QC2.0/QC3.0 remove vote for parallel
- * disable.
- * Otherwise if adapter is QC2.0/QC3.0 wait for authentication
- * to complete.
- */
- if (!qc_charger)
- vote(chg->pl_disable_votable, PL_DELAY_HVDCP_VOTER,
- false, 0);
}
smblib_dbg(chg, PR_INTERRUPT, "IRQ: smblib_handle_hvdcp_check_timeout %s\n",
@@ -3344,6 +3385,37 @@ static void smblib_handle_hvdcp_detect_done(struct smb_charger *chg,
rising ? "rising" : "falling");
}
+static void smblib_force_legacy_icl(struct smb_charger *chg, int pst)
+{
+ switch (pst) {
+ case POWER_SUPPLY_TYPE_USB:
+ /*
+ * USB_PSY will vote to increase the current to 500/900mA once
+ * enumeration is done. Ensure that USB_PSY has at least voted
+ * for 100mA before releasing the LEGACY_UNKNOWN vote
+ */
+ if (!is_client_vote_enabled(chg->usb_icl_votable,
+ USB_PSY_VOTER))
+ vote(chg->usb_icl_votable, USB_PSY_VOTER, true, 100000);
+ vote(chg->usb_icl_votable, LEGACY_UNKNOWN_VOTER, false, 0);
+ break;
+ case POWER_SUPPLY_TYPE_USB_CDP:
+ vote(chg->usb_icl_votable, LEGACY_UNKNOWN_VOTER, true, 1500000);
+ break;
+ case POWER_SUPPLY_TYPE_USB_DCP:
+ vote(chg->usb_icl_votable, LEGACY_UNKNOWN_VOTER, true, 1500000);
+ break;
+ case POWER_SUPPLY_TYPE_USB_HVDCP:
+ case POWER_SUPPLY_TYPE_USB_HVDCP_3:
+ vote(chg->usb_icl_votable, LEGACY_UNKNOWN_VOTER, true, 3000000);
+ break;
+ default:
+ smblib_err(chg, "Unknown APSD %d; forcing 500mA\n", pst);
+ vote(chg->usb_icl_votable, LEGACY_UNKNOWN_VOTER, true, 500000);
+ break;
+ }
+}
+
#define HVDCP_DET_MS 2500
static void smblib_handle_apsd_done(struct smb_charger *chg, bool rising)
{
@@ -3353,6 +3425,10 @@ static void smblib_handle_apsd_done(struct smb_charger *chg, bool rising)
return;
apsd_result = smblib_update_usb_type(chg);
+
+ if (!chg->pd_active)
+ smblib_force_legacy_icl(chg, apsd_result->pst);
+
switch (apsd_result->bit) {
case SDP_CHARGER_BIT:
case CDP_CHARGER_BIT:
@@ -3365,13 +3441,9 @@ static void smblib_handle_apsd_done(struct smb_charger *chg, bool rising)
break;
case OCP_CHARGER_BIT:
case FLOAT_CHARGER_BIT:
- /*
- * if not DCP then no hvdcp timeout happens. Enable
- * pd/parallel here.
- */
+ /* if not DCP then no hvdcp timeout happens, Enable pd here. */
vote(chg->pd_disallowed_votable_indirect, HVDCP_TIMEOUT_VOTER,
false, 0);
- vote(chg->pl_disable_votable, PL_DELAY_HVDCP_VOTER, false, 0);
break;
case DCP_CHARGER_BIT:
if (chg->wa_flags & QC_CHARGER_DETECTION_WA_BIT)
@@ -3437,6 +3509,9 @@ static void typec_source_removal(struct smb_charger *chg)
{
int rc;
+ /* reset legacy unknown vote */
+ vote(chg->usb_icl_votable, LEGACY_UNKNOWN_VOTER, false, 0);
+
/* reset both usbin current and voltage votes */
vote(chg->pl_enable_votable_indirect, USBIN_I_VOTER, false, 0);
vote(chg->pl_enable_votable_indirect, USBIN_V_VOTER, false, 0);
@@ -3480,16 +3555,19 @@ static void typec_source_removal(struct smb_charger *chg)
smblib_err(chg,
"Couldn't un-vote DCP from USB ICL rc=%d\n", rc);
- /* clear USB ICL vote for PL_USBIN_USBIN_VOTER */
- rc = vote(chg->usb_icl_votable, PL_USBIN_USBIN_VOTER, false, 0);
- if (rc < 0)
- smblib_err(chg,
- "Couldn't un-vote PL_USBIN_USBIN from USB ICL rc=%d\n",
- rc);
}
static void typec_source_insertion(struct smb_charger *chg)
{
+ /*
+ * at any time we want LEGACY_UNKNOWN, PD, or USB_PSY to be voting for
+ * ICL, so vote LEGACY_UNKNOWN here if none of the above three have
+ * casted their votes
+ */
+ if (!is_client_vote_enabled(chg->usb_icl_votable, LEGACY_UNKNOWN_VOTER)
+ && !is_client_vote_enabled(chg->usb_icl_votable, PD_VOTER)
+ && !is_client_vote_enabled(chg->usb_icl_votable, USB_PSY_VOTER))
+ vote(chg->usb_icl_votable, LEGACY_UNKNOWN_VOTER, true, 100000);
}
static void typec_sink_insertion(struct smb_charger *chg)
@@ -3510,11 +3588,16 @@ static void typec_sink_removal(struct smb_charger *chg)
static void smblib_handle_typec_removal(struct smb_charger *chg)
{
+ int rc;
+
+ cancel_delayed_work_sync(&chg->pl_enable_work);
+ vote(chg->pl_disable_votable, PL_DELAY_VOTER, true, 0);
+ vote(chg->awake_votable, PL_DELAY_VOTER, false, 0);
+
vote(chg->pd_disallowed_votable_indirect, CC_DETACHED_VOTER, true, 0);
vote(chg->pd_disallowed_votable_indirect, HVDCP_TIMEOUT_VOTER, true, 0);
- vote(chg->pd_disallowed_votable_indirect, LEGACY_CABLE_VOTER, true, 0);
- vote(chg->pd_disallowed_votable_indirect, VBUS_CC_SHORT_VOTER, true, 0);
- vote(chg->pl_disable_votable, PL_DELAY_HVDCP_VOTER, true, 0);
+ vote(chg->usb_irq_enable_votable, PD_VOTER, false, 0);
+ vote(chg->usb_irq_enable_votable, QC_VOTER, false, 0);
/* reset votes from vbus_cc_short */
vote(chg->hvdcp_disable_votable_indirect, VBUS_CC_SHORT_VOTER,
@@ -3532,10 +3615,13 @@ static void smblib_handle_typec_removal(struct smb_charger *chg)
chg->pulse_cnt = 0;
chg->usb_icl_delta_ua = 0;
- chg->usb_ever_removed = true;
+ /* enable APSD CC trigger for next insertion */
+ rc = smblib_masked_write(chg, TYPE_C_CFG_REG,
+ APSD_START_ON_CC_BIT, APSD_START_ON_CC_BIT);
+ if (rc < 0)
+ smblib_err(chg, "Couldn't enable APSD_START_ON_CC rc=%d\n", rc);
smblib_update_usb_type(chg);
-
typec_source_removal(chg);
typec_sink_removal(chg);
}
@@ -3543,12 +3629,16 @@ static void smblib_handle_typec_removal(struct smb_charger *chg)
static void smblib_handle_typec_insertion(struct smb_charger *chg,
bool sink_attached, bool legacy_cable)
{
- int rp;
- bool vbus_cc_short = false;
- bool valid_legacy_cable;
+ int rp, rc;
vote(chg->pd_disallowed_votable_indirect, CC_DETACHED_VOTER, false, 0);
+ /* disable APSD CC trigger since CC is attached */
+ rc = smblib_masked_write(chg, TYPE_C_CFG_REG, APSD_START_ON_CC_BIT, 0);
+ if (rc < 0)
+ smblib_err(chg, "Couldn't disable APSD_START_ON_CC rc=%d\n",
+ rc);
+
if (sink_attached) {
typec_source_removal(chg);
typec_sink_insertion(chg);
@@ -3557,25 +3647,16 @@ static void smblib_handle_typec_insertion(struct smb_charger *chg,
typec_sink_removal(chg);
}
- valid_legacy_cable = legacy_cable &&
- (chg->usb_ever_removed || !smblib_sysok_reason_usbin(chg));
- vote(chg->pd_disallowed_votable_indirect, LEGACY_CABLE_VOTER,
- valid_legacy_cable, 0);
-
- if (valid_legacy_cable) {
- rp = smblib_get_prop_ufp_mode(chg);
- if (rp == POWER_SUPPLY_TYPEC_SOURCE_HIGH
- || rp == POWER_SUPPLY_TYPEC_NON_COMPLIANT) {
- vbus_cc_short = true;
- smblib_err(chg, "Disabling PD and HVDCP, VBUS-CC shorted, rp = %d found\n",
- rp);
- }
+ rp = smblib_get_prop_ufp_mode(chg);
+ if (rp == POWER_SUPPLY_TYPEC_SOURCE_HIGH
+ || rp == POWER_SUPPLY_TYPEC_NON_COMPLIANT) {
+ smblib_dbg(chg, PR_MISC, "VBUS & CC could be shorted; keeping HVDCP disabled\n");
+ vote(chg->hvdcp_disable_votable_indirect, VBUS_CC_SHORT_VOTER,
+ true, 0);
+ } else {
+ vote(chg->hvdcp_disable_votable_indirect, VBUS_CC_SHORT_VOTER,
+ false, 0);
}
-
- vote(chg->hvdcp_disable_votable_indirect, VBUS_CC_SHORT_VOTER,
- vbus_cc_short, 0);
- vote(chg->pd_disallowed_votable_indirect, VBUS_CC_SHORT_VOTER,
- vbus_cc_short, 0);
}
static void smblib_handle_typec_debounce_done(struct smb_charger *chg,
@@ -3978,6 +4059,9 @@ static void smblib_vconn_oc_work(struct work_struct *work)
int rc, i;
u8 stat;
+ if (chg->micro_usb_mode)
+ return;
+
smblib_err(chg, "over-current detected on VCONN\n");
if (!chg->vconn_vreg || !chg->vconn_vreg->rdev)
return;
@@ -4072,6 +4156,16 @@ static void smblib_icl_change_work(struct work_struct *work)
smblib_dbg(chg, PR_INTERRUPT, "icl_settled=%d\n", settled_ua);
}
+static void smblib_pl_enable_work(struct work_struct *work)
+{
+ struct smb_charger *chg = container_of(work, struct smb_charger,
+ pl_enable_work.work);
+
+ smblib_dbg(chg, PR_PARALLEL, "timer expired, enabling parallel\n");
+ vote(chg->pl_disable_votable, PL_DELAY_VOTER, false, 0);
+ vote(chg->awake_votable, PL_DELAY_VOTER, false, 0);
+}
+
static int smblib_create_votables(struct smb_charger *chg)
{
int rc = 0;
@@ -4088,13 +4182,19 @@ static int smblib_create_votables(struct smb_charger *chg)
return rc;
}
+ chg->usb_icl_votable = find_votable("USB_ICL");
+ if (!chg->usb_icl_votable) {
+ rc = -EPROBE_DEFER;
+ return rc;
+ }
+
chg->pl_disable_votable = find_votable("PL_DISABLE");
if (!chg->pl_disable_votable) {
rc = -EPROBE_DEFER;
return rc;
}
vote(chg->pl_disable_votable, PL_INDIRECT_VOTER, true, 0);
- vote(chg->pl_disable_votable, PL_DELAY_HVDCP_VOTER, true, 0);
+ vote(chg->pl_disable_votable, PL_DELAY_VOTER, true, 0);
chg->dc_suspend_votable = create_votable("DC_SUSPEND", VOTE_SET_ANY,
smblib_dc_suspend_vote_callback,
@@ -4104,14 +4204,6 @@ static int smblib_create_votables(struct smb_charger *chg)
return rc;
}
- chg->usb_icl_votable = create_votable("USB_ICL", VOTE_MIN,
- smblib_usb_icl_vote_callback,
- chg);
- if (IS_ERR(chg->usb_icl_votable)) {
- rc = PTR_ERR(chg->usb_icl_votable);
- return rc;
- }
-
chg->dc_icl_votable = create_votable("DC_ICL", VOTE_MIN,
smblib_dc_icl_vote_callback,
chg);
@@ -4197,6 +4289,15 @@ static int smblib_create_votables(struct smb_charger *chg)
return rc;
}
+ chg->usb_irq_enable_votable = create_votable("USB_IRQ_DISABLE",
+ VOTE_SET_ANY,
+ smblib_usb_irq_enable_vote_callback,
+ chg);
+ if (IS_ERR(chg->usb_irq_enable_votable)) {
+ rc = PTR_ERR(chg->usb_irq_enable_votable);
+ return rc;
+ }
+
return rc;
}
@@ -4253,6 +4354,7 @@ int smblib_init(struct smb_charger *chg)
INIT_WORK(&chg->vconn_oc_work, smblib_vconn_oc_work);
INIT_DELAYED_WORK(&chg->otg_ss_done_work, smblib_otg_ss_done_work);
INIT_DELAYED_WORK(&chg->icl_change_work, smblib_icl_change_work);
+ INIT_DELAYED_WORK(&chg->pl_enable_work, smblib_pl_enable_work);
chg->fake_capacity = -EINVAL;
switch (chg->mode) {
diff --git a/drivers/power/supply/qcom/smb-lib.h b/drivers/power/supply/qcom/smb-lib.h
index 21ccd3c..49b9d3d 100644
--- a/drivers/power/supply/qcom/smb-lib.h
+++ b/drivers/power/supply/qcom/smb-lib.h
@@ -32,10 +32,12 @@ enum print_reason {
#define USER_VOTER "USER_VOTER"
#define PD_VOTER "PD_VOTER"
#define DCP_VOTER "DCP_VOTER"
+#define QC_VOTER "QC_VOTER"
#define PL_USBIN_USBIN_VOTER "PL_USBIN_USBIN_VOTER"
#define USB_PSY_VOTER "USB_PSY_VOTER"
#define PL_TAPER_WORK_RUNNING_VOTER "PL_TAPER_WORK_RUNNING_VOTER"
#define PL_INDIRECT_VOTER "PL_INDIRECT_VOTER"
+#define PL_QNOVO_VOTER "PL_QNOVO_VOTER"
#define USBIN_I_VOTER "USBIN_I_VOTER"
#define USBIN_V_VOTER "USBIN_V_VOTER"
#define CHG_STATE_VOTER "CHG_STATE_VOTER"
@@ -47,17 +49,18 @@ enum print_reason {
#define PD_DISALLOWED_INDIRECT_VOTER "PD_DISALLOWED_INDIRECT_VOTER"
#define PD_HARD_RESET_VOTER "PD_HARD_RESET_VOTER"
#define VBUS_CC_SHORT_VOTER "VBUS_CC_SHORT_VOTER"
-#define LEGACY_CABLE_VOTER "LEGACY_CABLE_VOTER"
#define PD_INACTIVE_VOTER "PD_INACTIVE_VOTER"
#define BOOST_BACK_VOTER "BOOST_BACK_VOTER"
+#define USBIN_USBIN_BOOST_VOTER "USBIN_USBIN_BOOST_VOTER"
#define HVDCP_INDIRECT_VOTER "HVDCP_INDIRECT_VOTER"
#define MICRO_USB_VOTER "MICRO_USB_VOTER"
#define DEBUG_BOARD_VOTER "DEBUG_BOARD_VOTER"
#define PD_SUSPEND_SUPPORTED_VOTER "PD_SUSPEND_SUPPORTED_VOTER"
-#define PL_DELAY_HVDCP_VOTER "PL_DELAY_HVDCP_VOTER"
+#define PL_DELAY_VOTER "PL_DELAY_VOTER"
#define CTM_VOTER "CTM_VOTER"
#define SW_QC3_VOTER "SW_QC3_VOTER"
#define AICL_RERUN_VOTER "AICL_RERUN_VOTER"
+#define LEGACY_UNKNOWN_VOTER "LEGACY_UNKNOWN_VOTER"
#define VCONN_MAX_ATTEMPTS 3
#define OTG_MAX_ATTEMPTS 3
@@ -80,6 +83,7 @@ enum {
BOOST_BACK_WA = BIT(1),
TYPEC_CC2_REMOVAL_WA_BIT = BIT(2),
QC_AUTH_INTERRUPT_WA_BIT = BIT(3),
+ OTG_WA = BIT(4),
};
enum smb_irq_index {
@@ -271,6 +275,7 @@ struct smb_charger {
struct votable *hvdcp_enable_votable;
struct votable *apsd_disable_votable;
struct votable *hvdcp_hw_inov_dis_votable;
+ struct votable *usb_irq_enable_votable;
/* work */
struct work_struct bms_update_work;
@@ -283,6 +288,7 @@ struct smb_charger {
struct work_struct vconn_oc_work;
struct delayed_work otg_ss_done_work;
struct delayed_work icl_change_work;
+ struct delayed_work pl_enable_work;
/* cached status */
int voltage_min_uv;
@@ -305,17 +311,16 @@ struct smb_charger {
int otg_attempts;
int vconn_attempts;
int default_icl_ua;
+ int otg_cl_ua;
/* workaround flag */
u32 wa_flags;
enum cc2_sink_type cc2_sink_detach_flag;
int boost_current_ua;
+ int temp_speed_reading_count;
/* extcon for VBUS / ID notification to USB for uUSB */
struct extcon_dev *extcon;
- bool usb_ever_removed;
-
- int icl_reduction_ua;
/* qnovo */
int qnovo_fcc_ua;
@@ -453,6 +458,8 @@ int smblib_get_prop_charger_temp_max(struct smb_charger *chg,
union power_supply_propval *val);
int smblib_get_prop_die_health(struct smb_charger *chg,
union power_supply_propval *val);
+int smblib_get_prop_charge_qnovo_enable(struct smb_charger *chg,
+ union power_supply_propval *val);
int smblib_set_prop_pd_current_max(struct smb_charger *chg,
const union power_supply_propval *val);
int smblib_set_prop_usb_current_max(struct smb_charger *chg,
@@ -473,14 +480,17 @@ int smblib_get_prop_slave_current_now(struct smb_charger *chg,
union power_supply_propval *val);
int smblib_set_prop_ship_mode(struct smb_charger *chg,
const union power_supply_propval *val);
+int smblib_set_prop_charge_qnovo_enable(struct smb_charger *chg,
+ const union power_supply_propval *val);
void smblib_suspend_on_debug_battery(struct smb_charger *chg);
int smblib_rerun_apsd_if_required(struct smb_charger *chg);
int smblib_get_prop_fcc_delta(struct smb_charger *chg,
union power_supply_propval *val);
int smblib_icl_override(struct smb_charger *chg, bool override);
-int smblib_set_icl_reduction(struct smb_charger *chg, int reduction_ua);
int smblib_dp_dm(struct smb_charger *chg, int val);
int smblib_rerun_aicl(struct smb_charger *chg);
+int smblib_set_icl_current(struct smb_charger *chg, int icl_ua);
+int smblib_get_charge_current(struct smb_charger *chg, int *total_current_ua);
int smblib_init(struct smb_charger *chg);
int smblib_deinit(struct smb_charger *chg);
diff --git a/drivers/power/supply/qcom/smb-reg.h b/drivers/power/supply/qcom/smb-reg.h
index f4afb80..167666a 100644
--- a/drivers/power/supply/qcom/smb-reg.h
+++ b/drivers/power/supply/qcom/smb-reg.h
@@ -628,6 +628,7 @@ enum {
#define USBIN_LOAD_CFG_REG (USBIN_BASE + 0x65)
#define USBIN_OV_CH_LOAD_OPTION_BIT BIT(7)
+#define ICL_OVERRIDE_AFTER_APSD_BIT BIT(4)
#define USBIN_ICL_OPTIONS_REG (USBIN_BASE + 0x66)
#define CFG_USB3P0_SEL_BIT BIT(2)
@@ -918,6 +919,7 @@ enum {
#define MISC_CFG_REG (MISC_BASE + 0x52)
#define GSM_PA_ON_ADJ_SEL_BIT BIT(0)
+#define STAT_PARALLEL_1400MA_EN_CFG_BIT BIT(3)
#define TCC_DEBOUNCE_20MS_BIT BIT(5)
#define SNARL_BARK_BITE_WD_CFG_REG (MISC_BASE + 0x53)
diff --git a/drivers/power/supply/qcom/smb138x-charger.c b/drivers/power/supply/qcom/smb138x-charger.c
index 1e89a721..83374bb 100644
--- a/drivers/power/supply/qcom/smb138x-charger.c
+++ b/drivers/power/supply/qcom/smb138x-charger.c
@@ -28,7 +28,7 @@
#include "smb-reg.h"
#include "smb-lib.h"
#include "storm-watch.h"
-#include "pmic-voter.h"
+#include <linux/pmic-voter.h>
#define SMB138X_DEFAULT_FCC_UA 1000000
#define SMB138X_DEFAULT_ICL_UA 1500000
@@ -45,6 +45,7 @@
#define STACKED_DIODE_EN_BIT BIT(2)
#define TDIE_AVG_COUNT 10
+#define MAX_SPEED_READING_TIMES 5
enum {
OOB_COMP_WA_BIT = BIT(0),
@@ -95,6 +96,7 @@ struct smb_dt_props {
int dc_icl_ua;
int chg_temp_max_mdegc;
int connector_temp_max_mdegc;
+ int pl_mode;
};
struct smb138x {
@@ -126,8 +128,16 @@ static int smb138x_get_prop_charger_temp(struct smb138x *chip,
union power_supply_propval pval;
int rc = 0, avg = 0, i;
struct smb_charger *chg = &chip->chg;
+ int die_avg_count;
- for (i = 0; i < TDIE_AVG_COUNT; i++) {
+ if (chg->temp_speed_reading_count < MAX_SPEED_READING_TIMES) {
+ chg->temp_speed_reading_count++;
+ die_avg_count = 1;
+ } else {
+ die_avg_count = TDIE_AVG_COUNT;
+ }
+
+ for (i = 0; i < die_avg_count; i++) {
pval.intval = 0;
rc = smblib_get_prop_charger_temp(chg, &pval);
if (rc < 0) {
@@ -137,7 +147,7 @@ static int smb138x_get_prop_charger_temp(struct smb138x *chip,
}
avg += pval.intval;
}
- val->intval = avg / TDIE_AVG_COUNT;
+ val->intval = avg / die_avg_count;
return rc;
}
@@ -152,6 +162,11 @@ static int smb138x_parse_dt(struct smb138x *chip)
return -EINVAL;
}
+ rc = of_property_read_u32(node,
+ "qcom,parallel-mode", &chip->dt.pl_mode);
+ if (rc < 0)
+ chip->dt.pl_mode = POWER_SUPPLY_PL_USBMID_USBMID;
+
chip->dt.suspend_input = of_property_read_bool(node,
"qcom,suspend-input");
@@ -520,6 +535,8 @@ static enum power_supply_property smb138x_parallel_props[] = {
POWER_SUPPLY_PROP_CHARGING_ENABLED,
POWER_SUPPLY_PROP_PIN_ENABLED,
POWER_SUPPLY_PROP_INPUT_SUSPEND,
+ POWER_SUPPLY_PROP_INPUT_CURRENT_LIMITED,
+ POWER_SUPPLY_PROP_CURRENT_MAX,
POWER_SUPPLY_PROP_VOLTAGE_MAX,
POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX,
POWER_SUPPLY_PROP_CURRENT_NOW,
@@ -559,6 +576,21 @@ static int smb138x_parallel_get_prop(struct power_supply *psy,
case POWER_SUPPLY_PROP_INPUT_SUSPEND:
rc = smblib_get_usb_suspend(chg, &val->intval);
break;
+ case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMITED:
+ if ((chip->dt.pl_mode == POWER_SUPPLY_PL_USBIN_USBIN)
+ || (chip->dt.pl_mode == POWER_SUPPLY_PL_USBIN_USBIN_EXT))
+ rc = smblib_get_prop_input_current_limited(chg, val);
+ else
+ val->intval = 0;
+ break;
+ case POWER_SUPPLY_PROP_CURRENT_MAX:
+ if ((chip->dt.pl_mode == POWER_SUPPLY_PL_USBIN_USBIN)
+ || (chip->dt.pl_mode == POWER_SUPPLY_PL_USBIN_USBIN_EXT))
+ rc = smblib_get_charge_param(chg, &chg->param.usb_icl,
+ &val->intval);
+ else
+ val->intval = 0;
+ break;
case POWER_SUPPLY_PROP_VOLTAGE_MAX:
rc = smblib_get_charge_param(chg, &chg->param.fv, &val->intval);
break;
@@ -579,7 +611,7 @@ static int smb138x_parallel_get_prop(struct power_supply *psy,
val->strval = "smb138x";
break;
case POWER_SUPPLY_PROP_PARALLEL_MODE:
- val->intval = POWER_SUPPLY_PL_USBMID_USBMID;
+ val->intval = chip->dt.pl_mode;
break;
case POWER_SUPPLY_PROP_CONNECTOR_HEALTH:
val->intval = smb138x_get_prop_connector_health(chip);
@@ -638,6 +670,12 @@ static int smb138x_parallel_set_prop(struct power_supply *psy,
case POWER_SUPPLY_PROP_INPUT_SUSPEND:
rc = smb138x_set_parallel_suspend(chip, (bool)val->intval);
break;
+ case POWER_SUPPLY_PROP_CURRENT_MAX:
+ if ((chip->dt.pl_mode == POWER_SUPPLY_PL_USBIN_USBIN)
+ || (chip->dt.pl_mode == POWER_SUPPLY_PL_USBIN_USBIN_EXT))
+ rc = smblib_set_charge_param(chg, &chg->param.usb_icl,
+ val->intval);
+ break;
case POWER_SUPPLY_PROP_VOLTAGE_MAX:
rc = smblib_set_charge_param(chg, &chg->param.fv, val->intval);
break;
@@ -1449,6 +1487,16 @@ static int smb138x_slave_probe(struct smb138x *chip)
goto cleanup;
}
+ if ((chip->dt.pl_mode == POWER_SUPPLY_PL_USBIN_USBIN)
+ || (chip->dt.pl_mode == POWER_SUPPLY_PL_USBIN_USBIN_EXT)) {
+ rc = smb138x_init_vbus_regulator(chip);
+ if (rc < 0) {
+ pr_err("Couldn't initialize vbus regulator rc=%d\n",
+ rc);
+ return rc;
+ }
+ }
+
rc = smb138x_init_parallel_psy(chip);
if (rc < 0) {
pr_err("Couldn't initialize parallel psy rc=%d\n", rc);
@@ -1473,6 +1521,8 @@ static int smb138x_slave_probe(struct smb138x *chip)
smblib_deinit(chg);
if (chip->parallel_psy)
power_supply_unregister(chip->parallel_psy);
+ if (chg->vbus_vreg && chg->vbus_vreg->rdev)
+ regulator_unregister(chg->vbus_vreg->rdev);
return rc;
}
diff --git a/drivers/scsi/ufs/ufs-qcom.c b/drivers/scsi/ufs/ufs-qcom.c
index d326b80..ee23fc7 100644
--- a/drivers/scsi/ufs/ufs-qcom.c
+++ b/drivers/scsi/ufs/ufs-qcom.c
@@ -32,6 +32,7 @@
#include "ufs_quirks.h"
#include "ufs-qcom-ice.h"
#include "ufs-qcom-debugfs.h"
+#include <linux/clk/qcom.h>
#define MAX_PROP_SIZE 32
#define VDDP_REF_CLK_MIN_UV 1200000
@@ -356,6 +357,28 @@ static int ufs_qcom_enable_hw_clk_gating(struct ufs_hba *hba)
return err;
}
+static void ufs_qcom_force_mem_config(struct ufs_hba *hba)
+{
+ struct ufs_clk_info *clki;
+
+ /*
+ * Configure the behavior of ufs clocks core and peripheral
+ * memory state when they are turned off.
+ * This configuration is required to allow retaining
+ * ICE crypto configuration (including keys) when
+ * core_clk_ice is turned off, and powering down
+ * non-ICE RAMs of host controller.
+ */
+ list_for_each_entry(clki, &hba->clk_list_head, list) {
+ if (!strcmp(clki->name, "core_clk_ice"))
+ clk_set_flags(clki->clk, CLKFLAG_RETAIN_MEM);
+ else
+ clk_set_flags(clki->clk, CLKFLAG_NORETAIN_MEM);
+ clk_set_flags(clki->clk, CLKFLAG_NORETAIN_PERIPH);
+ clk_set_flags(clki->clk, CLKFLAG_PERIPH_OFF_CLEAR);
+ }
+}
+
static int ufs_qcom_hce_enable_notify(struct ufs_hba *hba,
enum ufs_notify_change_status status)
{
@@ -364,6 +387,7 @@ static int ufs_qcom_hce_enable_notify(struct ufs_hba *hba,
switch (status) {
case PRE_CHANGE:
+ ufs_qcom_force_mem_config(hba);
ufs_qcom_power_up_sequence(hba);
/*
* The PHY PLL output is the source of tx/rx lane symbol
diff --git a/drivers/soc/qcom/glink_loopback_server.c b/drivers/soc/qcom/glink_loopback_server.c
index 0aeb0e8..3b540f3 100644
--- a/drivers/soc/qcom/glink_loopback_server.c
+++ b/drivers/soc/qcom/glink_loopback_server.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2014-2016, The Linux Foundation. All rights reserved.
+/* 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
@@ -141,6 +141,7 @@ static struct ctl_ch_info ctl_ch_tbl[] = {
{"LOOPBACK_CTL_APSS", "lpass", "smem"},
{"LOOPBACK_CTL_APSS", "dsps", "smem"},
{"LOOPBACK_CTL_APSS", "spss", "mailbox"},
+ {"LOOPBACK_CTL_APSS", "wdsp", "spi"},
};
static DEFINE_MUTEX(ctl_ch_list_lock);
diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c
index 33e3d9f..3e459b0 100644
--- a/drivers/usb/dwc3/core.c
+++ b/drivers/usb/dwc3/core.c
@@ -1064,56 +1064,17 @@ EXPORT_SYMBOL(dwc3_notify_event);
int dwc3_core_pre_init(struct dwc3 *dwc)
{
- int ret;
+ int ret = 0;
dwc3_cache_hwparams(dwc);
-
- ret = dwc3_phy_setup(dwc);
- if (ret)
- goto err0;
-
if (!dwc->ev_buf) {
ret = dwc3_alloc_event_buffers(dwc, DWC3_EVENT_BUFFERS_SIZE);
if (ret) {
dev_err(dwc->dev, "failed to allocate event buffers\n");
ret = -ENOMEM;
- goto err1;
}
}
- ret = dwc3_core_init(dwc);
- if (ret) {
- dev_err(dwc->dev, "failed to initialize core\n");
- goto err2;
- }
-
- ret = phy_power_on(dwc->usb2_generic_phy);
- if (ret < 0)
- goto err3;
-
- ret = phy_power_on(dwc->usb3_generic_phy);
- if (ret < 0)
- goto err4;
-
- ret = dwc3_event_buffers_setup(dwc);
- if (ret) {
- dev_err(dwc->dev, "failed to setup event buffers\n");
- goto err5;
- }
-
- return ret;
-
-err5:
- phy_power_off(dwc->usb3_generic_phy);
-err4:
- phy_power_off(dwc->usb2_generic_phy);
-err3:
- dwc3_core_exit(dwc);
-err2:
- dwc3_free_event_buffers(dwc);
-err1:
- dwc3_ulpi_exit(dwc);
-err0:
return ret;
}
diff --git a/include/dt-bindings/clock/qcom,gcc-sdm845.h b/include/dt-bindings/clock/qcom,gcc-sdm845.h
index d52e335..f020039 100644
--- a/include/dt-bindings/clock/qcom,gcc-sdm845.h
+++ b/include/dt-bindings/clock/qcom,gcc-sdm845.h
@@ -224,4 +224,10 @@
#define GCC_USB3_DP_PHY_SEC_BCR 23
#define GCC_USB_PHY_CFG_AHB2PHY_BCR 24
+/* Dummy clocks for rate measurement */
+#define MEASURE_ONLY_SNOC_CLK 0
+#define MEASURE_ONLY_CNOC_CLK 1
+#define MEASURE_ONLY_BIMC_CLK 2
+#define MEASURE_ONLY_IPA_2X_CLK 3
+
#endif
diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h
index 6dd1547..9403bbe 100644
--- a/include/linux/mmc/host.h
+++ b/include/linux/mmc/host.h
@@ -83,6 +83,12 @@ struct mmc_ios {
bool enhanced_strobe; /* hs400es selection */
};
+/* states to represent load on the host */
+enum mmc_load {
+ MMC_LOAD_HIGH,
+ MMC_LOAD_LOW,
+};
+
struct mmc_host_ops {
/*
* 'enable' is called when the host is claimed and 'disable' is called
@@ -168,6 +174,7 @@ struct mmc_host_ops {
*/
int (*multi_io_quirk)(struct mmc_card *card,
unsigned int direction, int blk_size);
+ int (*notify_load)(struct mmc_host *, enum mmc_load);
};
struct mmc_card;
@@ -320,6 +327,8 @@ struct mmc_host {
#define MMC_CAP2_NO_SD (1 << 21) /* Do not send SD commands during initialization */
#define MMC_CAP2_NO_MMC (1 << 22) /* Do not send (e)MMC commands during initialization */
#define MMC_CAP2_PACKED_WR_CONTROL (1 << 23) /* Allow write packing control */
+#define MMC_CAP2_CLK_SCALE (1 << 24) /* Allow dynamic clk scaling */
+#define MMC_CAP2_ASYNC_SDIO_IRQ_4BIT_MODE (1 << 25) /* Allows Asynchronous SDIO irq while card is in 4-bit mode */
mmc_pm_flag_t pm_caps; /* supported pm features */
@@ -431,10 +440,16 @@ struct mmc_host {
struct io_latency_state io_lat_s;
#endif
+ /*
+ * Set to 1 to just stop the SDCLK to the card without
+ * actually disabling the clock from it's source.
+ */
+ bool card_clock_off;
unsigned long private[0] ____cacheline_aligned;
};
struct mmc_host *mmc_alloc_host(int extra, struct device *);
+extern bool mmc_host_may_gate_card(struct mmc_card *);
int mmc_add_host(struct mmc_host *);
void mmc_remove_host(struct mmc_host *);
void mmc_free_host(struct mmc_host *);
diff --git a/drivers/power/supply/qcom/pmic-voter.h b/include/linux/pmic-voter.h
similarity index 90%
rename from drivers/power/supply/qcom/pmic-voter.h
rename to include/linux/pmic-voter.h
index 031b9a0..f202bf7 100644
--- a/drivers/power/supply/qcom/pmic-voter.h
+++ b/include/linux/pmic-voter.h
@@ -24,6 +24,9 @@ enum votable_type {
NUM_VOTABLE_TYPES,
};
+bool is_client_vote_enabled(struct votable *votable, const char *client_str);
+bool is_client_vote_enabled_locked(struct votable *votable,
+ const char *client_str);
int get_client_vote(struct votable *votable, const char *client_str);
int get_client_vote_locked(struct votable *votable, const char *client_str);
int get_effective_result(struct votable *votable);
diff --git a/include/linux/power_supply.h b/include/linux/power_supply.h
index b46d6a8..77912a1 100644
--- a/include/linux/power_supply.h
+++ b/include/linux/power_supply.h
@@ -217,6 +217,7 @@ enum power_supply_property {
POWER_SUPPLY_PROP_DP_DM,
POWER_SUPPLY_PROP_INPUT_CURRENT_LIMITED,
POWER_SUPPLY_PROP_INPUT_CURRENT_NOW,
+ POWER_SUPPLY_PROP_CHARGE_QNOVO_ENABLE,
POWER_SUPPLY_PROP_CURRENT_QNOVO,
POWER_SUPPLY_PROP_VOLTAGE_QNOVO,
POWER_SUPPLY_PROP_RERUN_AICL,
@@ -245,6 +246,7 @@ enum power_supply_property {
POWER_SUPPLY_PROP_DIE_HEALTH,
POWER_SUPPLY_PROP_CONNECTOR_HEALTH,
POWER_SUPPLY_PROP_CTM_CURRENT_MAX,
+ POWER_SUPPLY_PROP_HW_CURRENT_MAX,
/* Local extensions of type int64_t */
POWER_SUPPLY_PROP_CHARGE_COUNTER_EXT,
/* Properties of type `const char *' */
diff --git a/kernel/cpu.c b/kernel/cpu.c
index 7c23144..69df75d 100644
--- a/kernel/cpu.c
+++ b/kernel/cpu.c
@@ -1060,9 +1060,37 @@ static int _cpu_up(unsigned int cpu, int tasks_frozen, enum cpuhp_state target)
return ret;
}
+static int switch_to_rt_policy(void)
+{
+ struct sched_param param = { .sched_priority = MAX_RT_PRIO - 1 };
+ unsigned int policy = current->policy;
+ int err;
+
+ /* Nobody should be attempting hotplug from these policy contexts. */
+ if (policy == SCHED_BATCH || policy == SCHED_IDLE ||
+ policy == SCHED_DEADLINE)
+ return -EPERM;
+
+ if (policy == SCHED_FIFO || policy == SCHED_RR)
+ return 1;
+
+ /* Only SCHED_NORMAL left. */
+ err = sched_setscheduler_nocheck(current, SCHED_FIFO, ¶m);
+ return err;
+
+}
+
+static int switch_to_fair_policy(void)
+{
+ struct sched_param param = { .sched_priority = 0 };
+
+ return sched_setscheduler_nocheck(current, SCHED_NORMAL, ¶m);
+}
+
static int do_cpu_up(unsigned int cpu, enum cpuhp_state target)
{
int err = 0;
+ int switch_err = 0;
if (!cpu_possible(cpu)) {
pr_err("can't online cpu %d because it is not configured as may-hotadd at boot time\n",
@@ -1073,6 +1101,10 @@ static int do_cpu_up(unsigned int cpu, enum cpuhp_state target)
return -EINVAL;
}
+ switch_err = switch_to_rt_policy();
+ if (switch_err < 0)
+ return switch_err;
+
err = try_online_node(cpu_to_node(cpu));
if (err)
return err;
@@ -1087,6 +1119,13 @@ static int do_cpu_up(unsigned int cpu, enum cpuhp_state target)
err = _cpu_up(cpu, 0, target);
out:
cpu_maps_update_done();
+
+ if (!switch_err) {
+ switch_err = switch_to_fair_policy();
+ pr_err("Hotplug policy switch err. Task %s pid=%d\n",
+ current->comm, current->pid);
+ }
+
return err;
}
diff --git a/sound/soc/msm/msm8996.c b/sound/soc/msm/msm8996.c
index bc5f7e5..45c5479 100644
--- a/sound/soc/msm/msm8996.c
+++ b/sound/soc/msm/msm8996.c
@@ -351,7 +351,7 @@ static int msm8996_get_spk(struct snd_kcontrol *kcontrol,
static int msm8996_set_spk(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
pr_debug("%s() ucontrol->value.integer.value[0] = %ld\n",
__func__, ucontrol->value.integer.value[0]);