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;