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 <quic_mdtipton@quicinc.com>
This commit is contained in:
Mike Tipton 2022-06-06 16:37:22 -07:00
parent 79703e7f64
commit 7093f03092

View File

@ -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 <linux/kernel.h>
@ -23,6 +24,7 @@
#include <linux/mailbox_client.h>
#include <linux/mailbox_controller.h>
#include <linux/mailbox/qmp.h>
#include <linux/interconnect.h>
#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;