Loading Documentation/devicetree/bindings/regulator/gdsc-regulator.txt 0 → 100644 +52 −0 Original line number Original line Diff line number Diff line Qualcomm Global Distributed Switch Controller (GDSC) Regulator Driver The GDSC driver, implemented under the regulator framework, is responsible for safely collapsing and restoring power to peripheral cores on chipsets like msm8974 for power savings. Required properties: - compatible: Must be "qcom,gdsc" - regulator-name: A string used as a descriptive name for regulator outputs - reg: The address of the GDSCR register Optional properties: - parent-supply: phandle to the parent supply/regulator node - clock-names: List of string names for core clocks - qcom,retain-mem: Presence denotes a hardware requirement to leave the forced core memory retention signals in the core's clock branch control registers asserted. - qcom,retain-periph: Presence denotes a hardware requirement to leave the forced periph memory retention signal in the core's clock branch control registers asserted. - qcom,skip-logic-collapse: Presence denotes a requirement to leave power to the core's logic enabled. - qcom,support-hw-trigger: Presence denotes a hardware feature to switch on/off this regulator based on internal HW signals to save more power. - qcom,enable-root-clk: Presence denotes that the clocks in the "clocks" property are required to be enabled before gdsc is turned on and disabled before turning off gdsc. This will be used in subsystems where reset is synchronous and root clk is active without sw being aware of its state. The clock-name which denotes the root clock should be named as "core_root_clk". - qcom,force-enable-root-clk: If set, denotes that the root clock should be force enabled before turning on the GDSC and then be immediately force disabled. Likewise for GDSC disable. This is used in cases where the core root clock needs to be force-enabled prior to turning on the core. The clock-name which denotes the root clock should be "core_root_clk". - reg-names: Names of the bases for the above "reg" registers. Ex. "base", "domain_addr". - qcom,no-status-check-on-disable: Do not poll the status bit when GDSC is disabled. Example: gdsc_oxili_gx: qcom,gdsc@fd8c4024 { compatible = "qcom,gdsc"; regulator-name = "gdsc_oxili_gx"; parent-supply = <&pm8841_s4>; reg = <0xfd8c4024 0x4>; clock-names = "core_clk"; }; arch/arm/boot/dts/qcom/msm8996.dtsi +1 −1 Original line number Original line Diff line number Diff line Loading @@ -3639,7 +3639,7 @@ clocks = <&clock_gcc clk_gcc_mmss_bimc_gfx_clk>, clocks = <&clock_gcc clk_gcc_mmss_bimc_gfx_clk>, <&clock_gpu clk_gpu_gx_gfx3d_clk>, <&clock_gpu clk_gpu_gx_gfx3d_clk>, <&clock_gpu clk_gfx3d_clk_src>; <&clock_gpu clk_gfx3d_clk_src>; qcom,enable-root-clk; qcom,force-enable-root-clk; parent-supply = <&gfx_vreg>; parent-supply = <&gfx_vreg>; status = "ok"; status = "ok"; }; }; Loading drivers/clk/msm/gdsc.c +18 −4 Original line number Original line Diff line number Diff line Loading @@ -53,6 +53,7 @@ struct gdsc { bool toggle_logic; bool toggle_logic; bool resets_asserted; bool resets_asserted; bool root_en; bool root_en; bool force_root_en; int root_clk_idx; int root_clk_idx; bool no_status_check_on_disable; bool no_status_check_on_disable; void __iomem *domain_addr; void __iomem *domain_addr; Loading Loading @@ -85,7 +86,7 @@ static int gdsc_enable(struct regulator_dev *rdev) uint32_t regval; uint32_t regval; int i, ret; int i, ret; if (sc->root_en) if (sc->root_en || sc->force_root_en) clk_prepare_enable(sc->clocks[sc->root_clk_idx]); clk_prepare_enable(sc->clocks[sc->root_clk_idx]); if (sc->toggle_logic) { if (sc->toggle_logic) { Loading Loading @@ -147,6 +148,12 @@ static int gdsc_enable(struct regulator_dev *rdev) */ */ udelay(1); udelay(1); /* Delay to account for staggered memory powerup. */ udelay(1); if (sc->force_root_en) clk_disable_unprepare(sc->clocks[sc->root_clk_idx]); return 0; return 0; } } Loading @@ -156,6 +163,9 @@ static int gdsc_disable(struct regulator_dev *rdev) uint32_t regval; uint32_t regval; int i, ret = 0; int i, ret = 0; if (sc->force_root_en) clk_prepare_enable(sc->clocks[sc->root_clk_idx]); for (i = sc->clock_count-1; i >= 0; i--) { for (i = sc->clock_count-1; i >= 0; i--) { if (unlikely(i == sc->root_clk_idx)) if (unlikely(i == sc->root_clk_idx)) continue; continue; Loading @@ -165,6 +175,9 @@ static int gdsc_disable(struct regulator_dev *rdev) clk_set_flags(sc->clocks[i], CLKFLAG_NORETAIN_PERIPH); clk_set_flags(sc->clocks[i], CLKFLAG_NORETAIN_PERIPH); } } /* Delay to account for staggered memory powerdown. */ udelay(1); if (sc->toggle_logic) { if (sc->toggle_logic) { regval = readl_relaxed(sc->gdscr); regval = readl_relaxed(sc->gdscr); if (regval & HW_CONTROL_MASK) { if (regval & HW_CONTROL_MASK) { Loading Loading @@ -207,7 +220,7 @@ static int gdsc_disable(struct regulator_dev *rdev) sc->resets_asserted = true; sc->resets_asserted = true; } } if (sc->root_en) if (sc->root_en || sc->force_root_en) clk_disable_unprepare(sc->clocks[sc->root_clk_idx]); clk_disable_unprepare(sc->clocks[sc->root_clk_idx]); return ret; return ret; Loading Loading @@ -356,7 +369,8 @@ static int gdsc_probe(struct platform_device *pdev) sc->root_en = of_property_read_bool(pdev->dev.of_node, sc->root_en = of_property_read_bool(pdev->dev.of_node, "qcom,enable-root-clk"); "qcom,enable-root-clk"); sc->force_root_en = of_property_read_bool(pdev->dev.of_node, "qcom,force-enable-root-clk"); for (i = 0; i < sc->clock_count; i++) { for (i = 0; i < sc->clock_count; i++) { const char *clock_name; const char *clock_name; of_property_read_string_index(pdev->dev.of_node, "clock-names", of_property_read_string_index(pdev->dev.of_node, "clock-names", Loading @@ -374,7 +388,7 @@ static int gdsc_probe(struct platform_device *pdev) sc->root_clk_idx = i; sc->root_clk_idx = i; } } if (sc->root_en && (sc->root_clk_idx == -1)) { if ((sc->root_en || sc->force_root_en) && (sc->root_clk_idx == -1)) { dev_err(&pdev->dev, "Failed to get root clock name\n"); dev_err(&pdev->dev, "Failed to get root clock name\n"); return -EINVAL; return -EINVAL; } } Loading Loading
Documentation/devicetree/bindings/regulator/gdsc-regulator.txt 0 → 100644 +52 −0 Original line number Original line Diff line number Diff line Qualcomm Global Distributed Switch Controller (GDSC) Regulator Driver The GDSC driver, implemented under the regulator framework, is responsible for safely collapsing and restoring power to peripheral cores on chipsets like msm8974 for power savings. Required properties: - compatible: Must be "qcom,gdsc" - regulator-name: A string used as a descriptive name for regulator outputs - reg: The address of the GDSCR register Optional properties: - parent-supply: phandle to the parent supply/regulator node - clock-names: List of string names for core clocks - qcom,retain-mem: Presence denotes a hardware requirement to leave the forced core memory retention signals in the core's clock branch control registers asserted. - qcom,retain-periph: Presence denotes a hardware requirement to leave the forced periph memory retention signal in the core's clock branch control registers asserted. - qcom,skip-logic-collapse: Presence denotes a requirement to leave power to the core's logic enabled. - qcom,support-hw-trigger: Presence denotes a hardware feature to switch on/off this regulator based on internal HW signals to save more power. - qcom,enable-root-clk: Presence denotes that the clocks in the "clocks" property are required to be enabled before gdsc is turned on and disabled before turning off gdsc. This will be used in subsystems where reset is synchronous and root clk is active without sw being aware of its state. The clock-name which denotes the root clock should be named as "core_root_clk". - qcom,force-enable-root-clk: If set, denotes that the root clock should be force enabled before turning on the GDSC and then be immediately force disabled. Likewise for GDSC disable. This is used in cases where the core root clock needs to be force-enabled prior to turning on the core. The clock-name which denotes the root clock should be "core_root_clk". - reg-names: Names of the bases for the above "reg" registers. Ex. "base", "domain_addr". - qcom,no-status-check-on-disable: Do not poll the status bit when GDSC is disabled. Example: gdsc_oxili_gx: qcom,gdsc@fd8c4024 { compatible = "qcom,gdsc"; regulator-name = "gdsc_oxili_gx"; parent-supply = <&pm8841_s4>; reg = <0xfd8c4024 0x4>; clock-names = "core_clk"; };
arch/arm/boot/dts/qcom/msm8996.dtsi +1 −1 Original line number Original line Diff line number Diff line Loading @@ -3639,7 +3639,7 @@ clocks = <&clock_gcc clk_gcc_mmss_bimc_gfx_clk>, clocks = <&clock_gcc clk_gcc_mmss_bimc_gfx_clk>, <&clock_gpu clk_gpu_gx_gfx3d_clk>, <&clock_gpu clk_gpu_gx_gfx3d_clk>, <&clock_gpu clk_gfx3d_clk_src>; <&clock_gpu clk_gfx3d_clk_src>; qcom,enable-root-clk; qcom,force-enable-root-clk; parent-supply = <&gfx_vreg>; parent-supply = <&gfx_vreg>; status = "ok"; status = "ok"; }; }; Loading
drivers/clk/msm/gdsc.c +18 −4 Original line number Original line Diff line number Diff line Loading @@ -53,6 +53,7 @@ struct gdsc { bool toggle_logic; bool toggle_logic; bool resets_asserted; bool resets_asserted; bool root_en; bool root_en; bool force_root_en; int root_clk_idx; int root_clk_idx; bool no_status_check_on_disable; bool no_status_check_on_disable; void __iomem *domain_addr; void __iomem *domain_addr; Loading Loading @@ -85,7 +86,7 @@ static int gdsc_enable(struct regulator_dev *rdev) uint32_t regval; uint32_t regval; int i, ret; int i, ret; if (sc->root_en) if (sc->root_en || sc->force_root_en) clk_prepare_enable(sc->clocks[sc->root_clk_idx]); clk_prepare_enable(sc->clocks[sc->root_clk_idx]); if (sc->toggle_logic) { if (sc->toggle_logic) { Loading Loading @@ -147,6 +148,12 @@ static int gdsc_enable(struct regulator_dev *rdev) */ */ udelay(1); udelay(1); /* Delay to account for staggered memory powerup. */ udelay(1); if (sc->force_root_en) clk_disable_unprepare(sc->clocks[sc->root_clk_idx]); return 0; return 0; } } Loading @@ -156,6 +163,9 @@ static int gdsc_disable(struct regulator_dev *rdev) uint32_t regval; uint32_t regval; int i, ret = 0; int i, ret = 0; if (sc->force_root_en) clk_prepare_enable(sc->clocks[sc->root_clk_idx]); for (i = sc->clock_count-1; i >= 0; i--) { for (i = sc->clock_count-1; i >= 0; i--) { if (unlikely(i == sc->root_clk_idx)) if (unlikely(i == sc->root_clk_idx)) continue; continue; Loading @@ -165,6 +175,9 @@ static int gdsc_disable(struct regulator_dev *rdev) clk_set_flags(sc->clocks[i], CLKFLAG_NORETAIN_PERIPH); clk_set_flags(sc->clocks[i], CLKFLAG_NORETAIN_PERIPH); } } /* Delay to account for staggered memory powerdown. */ udelay(1); if (sc->toggle_logic) { if (sc->toggle_logic) { regval = readl_relaxed(sc->gdscr); regval = readl_relaxed(sc->gdscr); if (regval & HW_CONTROL_MASK) { if (regval & HW_CONTROL_MASK) { Loading Loading @@ -207,7 +220,7 @@ static int gdsc_disable(struct regulator_dev *rdev) sc->resets_asserted = true; sc->resets_asserted = true; } } if (sc->root_en) if (sc->root_en || sc->force_root_en) clk_disable_unprepare(sc->clocks[sc->root_clk_idx]); clk_disable_unprepare(sc->clocks[sc->root_clk_idx]); return ret; return ret; Loading Loading @@ -356,7 +369,8 @@ static int gdsc_probe(struct platform_device *pdev) sc->root_en = of_property_read_bool(pdev->dev.of_node, sc->root_en = of_property_read_bool(pdev->dev.of_node, "qcom,enable-root-clk"); "qcom,enable-root-clk"); sc->force_root_en = of_property_read_bool(pdev->dev.of_node, "qcom,force-enable-root-clk"); for (i = 0; i < sc->clock_count; i++) { for (i = 0; i < sc->clock_count; i++) { const char *clock_name; const char *clock_name; of_property_read_string_index(pdev->dev.of_node, "clock-names", of_property_read_string_index(pdev->dev.of_node, "clock-names", Loading @@ -374,7 +388,7 @@ static int gdsc_probe(struct platform_device *pdev) sc->root_clk_idx = i; sc->root_clk_idx = i; } } if (sc->root_en && (sc->root_clk_idx == -1)) { if ((sc->root_en || sc->force_root_en) && (sc->root_clk_idx == -1)) { dev_err(&pdev->dev, "Failed to get root clock name\n"); dev_err(&pdev->dev, "Failed to get root clock name\n"); return -EINVAL; return -EINVAL; } } Loading