From 7093f030929f50e2bd7a165b2914aaf2d53ff72b Mon Sep 17 00:00:00 2001 From: Mike Tipton Date: Mon, 6 Jun 2022 16:37:22 -0700 Subject: [PATCH] clk: qcom: gdsc-regulator: Add BW voting support When certain GDSCs are enabled they cause their core to permanently assert active signals to RPMh. These active signals will prevent NOCs from powering down. So if BW votes are removed prior to disabling the GDSC, then BCM will get stuck in the power down sequence and rpmh driver timeouts. Prevent this by placing minimum BW votes whenever the GDSC is enabled. Change-Id: Iadd0bac37a6d04fd87c507fc7eee23e203891a6c Signed-off-by: Mike Tipton --- drivers/clk/qcom/gdsc-regulator.c | 49 +++++++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) diff --git a/drivers/clk/qcom/gdsc-regulator.c b/drivers/clk/qcom/gdsc-regulator.c index d1f2130ab049..7c9d80dacf2c 100644 --- a/drivers/clk/qcom/gdsc-regulator.c +++ b/drivers/clk/qcom/gdsc-regulator.c @@ -1,6 +1,7 @@ // SPDX-License-Identifier: GPL-2.0-only /* * Copyright (c) 2016-2021, The Linux Foundation. All rights reserved. + * Copyright (c) 2022, Qualcomm Innovation Center, Inc. All rights reserved. */ #include @@ -23,6 +24,7 @@ #include #include #include +#include #include "../../regulator/internal.h" #include "gdsc-debug.h" @@ -74,6 +76,7 @@ struct gdsc { struct mbox_client mbox_client; struct mbox_chan *mbox; struct reset_control **reset_clocks; + struct icc_path **paths; bool toggle_logic; bool retain_ff_enable; bool resets_asserted; @@ -88,6 +91,7 @@ struct gdsc { int reset_count; int root_clk_idx; int sw_reset_count; + int path_count; u32 gds_timeout; bool skip_disable_before_enable; bool skip_disable; @@ -258,6 +262,14 @@ static int gdsc_enable(struct regulator_dev *rdev) } if (sc->toggle_logic) { + for (i = 0; i < sc->path_count; i++) { + ret = icc_set_bw(sc->paths[i], 1, 1); + if (ret) { + dev_err(&rdev->dev, "Failed to vote BW for %d, ret=%d\n", i, ret); + return ret; + } + } + if (sc->sw_reset_count) { for (i = 0; i < sc->sw_reset_count; i++) regmap_set_bits(sc->sw_resets[i], REG_OFFSET, @@ -471,6 +483,13 @@ static int gdsc_disable(struct regulator_dev *rdev) regmap_write(sc->domain_addr, REG_OFFSET, regval); } + for (i = 0; i < sc->path_count; i++) { + ret = icc_set_bw(sc->paths[i], 0, 0); + if (ret) { + dev_err(&rdev->dev, "Failed to unvote BW for %d: %d\n", i, ret); + return ret; + } + } } else { for (i = sc->reset_count - 1; i >= 0; i--) reset_control_assert(sc->reset_clocks[i]); @@ -747,6 +766,16 @@ static int gdsc_parse_dt_data(struct gdsc *sc, struct device *dev, return sc->clock_count; } + sc->path_count = of_property_count_strings(dev->of_node, + "interconnect-names"); + if (sc->path_count == -EINVAL) { + sc->path_count = 0; + } else if (sc->path_count < 0) { + dev_err(dev, "Failed to get interconnect names, ret=%d\n", + sc->path_count); + return sc->path_count; + } + sc->root_en = of_property_read_bool(dev->of_node, "qcom,enable-root-clk"); sc->force_root_en = of_property_read_bool(dev->of_node, @@ -872,6 +901,26 @@ static int gdsc_get_resources(struct gdsc *sc, struct platform_device *pdev) sc->root_clk_idx = i; } + sc->paths = devm_kcalloc(dev, sc->path_count, sizeof(*sc->paths), GFP_KERNEL); + if (sc->path_count && !sc->paths) + return -ENOMEM; + + for (i = 0; i < sc->path_count; i++) { + const char *name; + + of_property_read_string_index(dev->of_node, "interconnect-names", i, + &name); + + sc->paths[i] = of_icc_get(dev, name); + if (IS_ERR(sc->paths[i])) { + ret = PTR_ERR(sc->paths[i]); + if (ret != -EPROBE_DEFER) + dev_err(dev, "Failed to get path %s, ret=%d\n", + name, ret); + return ret; + } + } + if ((sc->root_en || sc->force_root_en) && (sc->root_clk_idx == -1)) { dev_err(dev, "Failed to get root clock name\n"); return -EINVAL;