[PATCH v4 11/12] interconnect: qcom: icc-rpm: Introduce keep_alive
From: Konrad Dybcio
Date: Tue Feb 14 2023 - 09:38:44 EST
The downstream kernel employs the concept of "keeping the bus alive"
by voting for the minimum (XO/19.2MHz) rate at all times on certain
(well, most) buses. This is a very important thing to have, as if we
either have a lackluster/wrong DT that doesn't specify a (high enough)
vote on a certain bus, we may lose access to the entire bus altogether.
This is very apparent when we only start introducing interconnect
support on a given platform and haven't yet introduced voting on all
peripherals.
The same can happen if we only have a single driver casting a vote on
a certain bus and that driver exits/crashes/suspends.
The keepalive vote is limited to the ACTIVE bucket, as keeping a
permanent vote on the SLEEP one could prevent the platform from properly
entering low power mode states.
Introduce the very same concept, with a slight twist: the vendor
kernel checks whether the rate is zero before setting the minimum
vote, but that's rather silly, as in doing so we're at the mercy
of CCF. Instead, explicitly clamp the rates to always be >= 19.2 MHz
for providers with keep_alive=true.
Signed-off-by: Konrad Dybcio <konrad.dybcio@xxxxxxxxxx>
---
drivers/interconnect/qcom/icc-rpm.c | 10 ++++++++++
drivers/interconnect/qcom/icc-rpm.h | 3 +++
2 files changed, 13 insertions(+)
diff --git a/drivers/interconnect/qcom/icc-rpm.c b/drivers/interconnect/qcom/icc-rpm.c
index c1954584d6f0..88cad71688ec 100644
--- a/drivers/interconnect/qcom/icc-rpm.c
+++ b/drivers/interconnect/qcom/icc-rpm.c
@@ -51,6 +51,8 @@
#define NOC_QOS_MODE_FIXED_VAL 0x0
#define NOC_QOS_MODE_BYPASS_VAL 0x2
+#define ICC_BUS_CLK_MIN_RATE 19200000ULL
+
static int qcom_icc_set_qnoc_qos(struct icc_node *src, u64 max_bw)
{
struct icc_provider *provider = src->provider;
@@ -397,6 +399,13 @@ static int qcom_icc_set(struct icc_node *src, struct icc_node *dst)
do_div(rate, src_qn->buswidth);
rate = min_t(u64, rate, LONG_MAX);
+ /*
+ * Downstream checks whether the requested rate is zero, but it makes little sense
+ * to vote for a value that's below the lower threshold, so let's not do so.
+ */
+ if (bucket == QCOM_ICC_BUCKET_WAKE && qp->keep_alive)
+ qp->bus_clk_rate[i] = max(ICC_BUS_CLK_MIN_RATE, qp->bus_clk_rate[i]);
+
if (qp->bus_clk_rate[i] == rate)
continue;
@@ -482,6 +491,7 @@ int qnoc_probe(struct platform_device *pdev)
qp->bus_clks[i].id = cds[i];
qp->num_bus_clks = cd_num;
+ qp->keep_alive = desc->keep_alive;
qp->type = desc->type;
qp->qos_offset = desc->qos_offset;
diff --git a/drivers/interconnect/qcom/icc-rpm.h b/drivers/interconnect/qcom/icc-rpm.h
index 9dd631964b8c..77e263b93c27 100644
--- a/drivers/interconnect/qcom/icc-rpm.h
+++ b/drivers/interconnect/qcom/icc-rpm.h
@@ -29,6 +29,7 @@ enum qcom_icc_type {
* @regmap: regmap for QoS registers read/write access
* @qos_offset: offset to QoS registers
* @bus_clk_rate: bus clock rate in Hz
+ * @keep_alive: whether to always keep a minimum vote on the bus clocks
* @bus_clks: the clk_bulk_data table of bus clocks
* @intf_clks: the clk_bulk_data table of interface clocks
*/
@@ -40,6 +41,7 @@ struct qcom_icc_provider {
struct regmap *regmap;
unsigned int qos_offset;
u64 bus_clk_rate[2];
+ bool keep_alive;
struct clk_bulk_data bus_clks[2];
struct clk_bulk_data intf_clks[];
};
@@ -100,6 +102,7 @@ struct qcom_icc_desc {
const char * const *intf_clocks;
size_t num_intf_clocks;
bool has_bus_pd;
+ bool keep_alive;
enum qcom_icc_type type;
const struct regmap_config *regmap_cfg;
unsigned int qos_offset;
--
2.39.1