From 584cd27f0bda9b26eca24beff4bb8c9339ae24e8 Mon Sep 17 00:00:00 2001 From: Deepak Katragadda Date: Wed, 25 Apr 2018 10:10:16 -0700 Subject: [PATCH 1/2] clk: qcom: gdsc-regulator: Add MMCX specific logic When clients call the regulator_is_enabled() API for GDSCs that are sourced off of the MMCX rail, any register accesses that will be made as part of the callback rely on the presumption that the MMCX rail is enabled. When the rail is disabled, the corresponding ahb clocks that are needed for these registers to be accessible will also be turned off, thus leading to an unclocked access. Check to make sure that the MMCX rail is enabled prior to reading or writing to the MM registers. Along similar lines, add logic to enable the parent rail for the GDSC prior to reading and writing to the GDSC registers during the set_mode and get_mode callbacks. Change-Id: I8265bc147efd2acd1d6638638eb78935762feee8 Signed-off-by: Deepak Katragadda --- drivers/clk/qcom/gdsc-regulator.c | 73 +++++++++++++++++++++++++++++-- 1 file changed, 70 insertions(+), 3 deletions(-) diff --git a/drivers/clk/qcom/gdsc-regulator.c b/drivers/clk/qcom/gdsc-regulator.c index 4a19a6723fad..95609e64ea6d 100644 --- a/drivers/clk/qcom/gdsc-regulator.c +++ b/drivers/clk/qcom/gdsc-regulator.c @@ -111,7 +111,7 @@ static int poll_gdsc_status(struct gdsc *sc, enum gdscr_status status) * bit in the GDSCR to be set or reset after the GDSC state * changes. Hence, keep on checking for a reasonable number * of times until the bit is set with the least possible delay - * between succeessive tries. + * between successive tries. */ udelay(1); } @@ -127,6 +127,30 @@ static int gdsc_is_enabled(struct regulator_dev *rdev) if (!sc->toggle_logic) return !sc->resets_asserted; + if (sc->parent_regulator) { + /* + * The parent regulator for the GDSC is required to be on to + * make any register accesses to the GDSC base. Return false + * if the parent supply is disabled. + */ + if (regulator_is_enabled(sc->parent_regulator) <= 0) + return false; + + /* + * Place an explicit vote on the parent rail to cover cases when + * it might be disabled between this point and reading the GDSC + * registers. + */ + if (regulator_set_voltage(sc->parent_regulator, + RPMH_REGULATOR_LEVEL_LOW_SVS, INT_MAX)) + return false; + + if (regulator_enable(sc->parent_regulator)) { + regulator_set_voltage(sc->parent_regulator, 0, INT_MAX); + return false; + } + } + regmap_read(sc->regmap, REG_OFFSET, ®val); if (regval & PWR_ON_MASK) { @@ -135,10 +159,20 @@ static int gdsc_is_enabled(struct regulator_dev *rdev) * votable GDS registers. Check the SW_COLLAPSE_MASK to * determine if HLOS has voted for it. */ - if (!(regval & SW_COLLAPSE_MASK)) + if (!(regval & SW_COLLAPSE_MASK)) { + if (sc->parent_regulator) { + regulator_disable(sc->parent_regulator); + regulator_set_voltage(sc->parent_regulator, 0, + INT_MAX); + } return true; + } } + if (sc->parent_regulator) { + regulator_disable(sc->parent_regulator); + regulator_set_voltage(sc->parent_regulator, 0, INT_MAX); + } return false; } @@ -374,9 +408,33 @@ static unsigned int gdsc_get_mode(struct regulator_dev *rdev) { struct gdsc *sc = rdev_get_drvdata(rdev); uint32_t regval; + int ret; mutex_lock(&gdsc_seq_lock); + + if (sc->parent_regulator) { + ret = regulator_set_voltage(sc->parent_regulator, + RPMH_REGULATOR_LEVEL_LOW_SVS, INT_MAX); + if (ret) { + mutex_unlock(&gdsc_seq_lock); + return ret; + } + + ret = regulator_enable(sc->parent_regulator); + if (ret) { + regulator_set_voltage(sc->parent_regulator, 0, INT_MAX); + mutex_unlock(&gdsc_seq_lock); + return ret; + } + } + regmap_read(sc->regmap, REG_OFFSET, ®val); + + if (sc->parent_regulator) { + regulator_disable(sc->parent_regulator); + regulator_set_voltage(sc->parent_regulator, 0, INT_MAX); + } + mutex_unlock(&gdsc_seq_lock); if (regval & HW_CONTROL_MASK) @@ -400,6 +458,13 @@ static int gdsc_set_mode(struct regulator_dev *rdev, unsigned int mode) mutex_unlock(&gdsc_seq_lock); return ret; } + + ret = regulator_enable(sc->parent_regulator); + if (ret) { + regulator_set_voltage(sc->parent_regulator, 0, INT_MAX); + mutex_unlock(&gdsc_seq_lock); + return ret; + } } regmap_read(sc->regmap, REG_OFFSET, ®val); @@ -445,8 +510,10 @@ static int gdsc_set_mode(struct regulator_dev *rdev, unsigned int mode) break; } - if (sc->parent_regulator) + if (sc->parent_regulator) { + regulator_disable(sc->parent_regulator); regulator_set_voltage(sc->parent_regulator, 0, INT_MAX); + } mutex_unlock(&gdsc_seq_lock); From 8051a4930bf382b33efb0aae4af9ebfe7e1922f7 Mon Sep 17 00:00:00 2001 From: David Collins Date: Mon, 23 Apr 2018 18:00:50 -0700 Subject: [PATCH 2/2] clk: qcom: gdsc-regulator: add support for enabling retention registers Add support for the RETAIN_FF_ENABLE feature which enables the usage of retention registers. These registers maintain their state after disabling and re-enabling a GDSC. Change-Id: Iad19bfb2cb73fa76aad230bad6372d68b447b4e2 Signed-off-by: David Collins --- drivers/clk/qcom/gdsc-regulator.c | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/drivers/clk/qcom/gdsc-regulator.c b/drivers/clk/qcom/gdsc-regulator.c index 95609e64ea6d..377d2ba5a817 100644 --- a/drivers/clk/qcom/gdsc-regulator.c +++ b/drivers/clk/qcom/gdsc-regulator.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-only /* - * Copyright (c) 2016-2018, The Linux Foundation. All rights reserved. + * Copyright (c) 2016-2019, The Linux Foundation. All rights reserved. */ #include @@ -25,6 +25,7 @@ #define PWR_ON_MASK BIT(31) #define CLK_DIS_WAIT_MASK (0xF << 12) #define CLK_DIS_WAIT_SHIFT (12) +#define RETAIN_FF_ENABLE_MASK BIT(11) #define SW_OVERRIDE_MASK BIT(2) #define HW_CONTROL_MASK BIT(1) #define SW_COLLAPSE_MASK BIT(0) @@ -54,6 +55,7 @@ struct gdsc { struct regulator *parent_regulator; struct reset_control **reset_clocks; bool toggle_logic; + bool retain_ff_enable; bool resets_asserted; bool root_en; bool force_root_en; @@ -299,6 +301,11 @@ static int gdsc_enable(struct regulator_dev *rdev) goto end; } } + + if (sc->retain_ff_enable && !(regval & RETAIN_FF_ENABLE_MASK)) { + regval |= RETAIN_FF_ENABLE_MASK; + regmap_write(sc->regmap, REG_OFFSET, regval); + } } else { for (i = 0; i < sc->reset_count; i++) reset_control_deassert(sc->reset_clocks[i]); @@ -699,6 +706,8 @@ static int gdsc_probe(struct platform_device *pdev) sc->no_status_check_on_disable = of_property_read_bool(pdev->dev.of_node, "qcom,no-status-check-on-disable"); + sc->retain_ff_enable = of_property_read_bool(pdev->dev.of_node, + "qcom,retain-regs"); sc->toggle_logic = !of_property_read_bool(pdev->dev.of_node, "qcom,skip-logic-collapse"); support_hw_trigger = of_property_read_bool(pdev->dev.of_node,