[PATCH v3 6/8] i2c: qcom-geni: Vote for SE clock rate using OPP
From: Praveen Talari
Date: Thu Oct 15 2026 - 09:37:29 EST
The GENI I2C driver selects a source clock based on the requested bus
frequency and programs the corresponding clock selector. However, it
does not request the selected source clock rate when OPP-based clock
scaling is used.
Store the matched source clock frequency and vote for it through
dev_pm_opp_set_rate() before programming the clock selector. Also remove
and restore the vote during runtime suspend and resume.
Signed-off-by: Praveen Talari <praveen.talari@xxxxxxxxxxxxxxxx>
---
drivers/i2c/busses/i2c-qcom-geni.c | 23 ++++++++++++++++++-----
1 file changed, 18 insertions(+), 5 deletions(-)
diff --git a/drivers/i2c/busses/i2c-qcom-geni.c b/drivers/i2c/busses/i2c-qcom-geni.c
index 5f3d260a3913..a6fe89d0934f 100644
--- a/drivers/i2c/busses/i2c-qcom-geni.c
+++ b/drivers/i2c/busses/i2c-qcom-geni.c
@@ -16,6 +16,7 @@
#include <linux/module.h>
#include <linux/of.h>
#include <linux/platform_device.h>
+#include <linux/pm_opp.h>
#include <linux/pm_runtime.h>
#include <linux/soc/qcom/geni-se.h>
#include <linux/spinlock.h>
@@ -136,6 +137,7 @@ struct geni_i2c_dev {
u32 clk_freq_out;
const struct geni_i2c_clk_fld *clk_fld;
u32 clk_idx;
+ unsigned long src_freq;
void *dma_buf;
size_t xfer_len;
dma_addr_t dma_addr;
@@ -206,7 +208,7 @@ static const struct geni_i2c_clk_fld geni_i2c_clk_map_32mhz[] = {
static int geni_i2c_clk_map_idx(struct geni_i2c_dev *gi2c)
{
const struct geni_i2c_clk_fld *itr;
- unsigned long req_freq, res_freq;
+ unsigned long req_freq;
int ret;
/*
@@ -223,14 +225,14 @@ static int geni_i2c_clk_map_idx(struct geni_i2c_dev *gi2c)
}
ret = geni_se_clk_freq_match(&gi2c->se, req_freq, &gi2c->clk_idx,
- &res_freq, true);
+ &gi2c->src_freq, true);
if (ret)
return dev_err_probe(gi2c->se.dev, ret,
"Failed to find SE source clock %lu Hz\n",
req_freq);
- if (res_freq != req_freq) {
+ if (gi2c->src_freq != req_freq) {
dev_err(gi2c->se.dev, "Unsupported SE source clock %lu Hz\n",
- res_freq);
+ gi2c->src_freq);
return -EINVAL;
}
@@ -255,6 +257,11 @@ static int qcom_geni_i2c_conf(struct geni_se *se, unsigned long freq)
struct geni_i2c_dev *gi2c = dev_get_drvdata(se->dev);
const struct geni_i2c_clk_fld *itr = gi2c->clk_fld;
u32 val;
+ int ret;
+
+ ret = dev_pm_opp_set_rate(se->dev, gi2c->src_freq);
+ if (ret)
+ return ret;
writel_relaxed(gi2c->clk_idx, gi2c->se.base + SE_GENI_CLK_SEL);
@@ -1286,7 +1293,7 @@ static int geni_i2c_runtime_suspend(struct device *dev)
}
}
- return 0;
+ return dev_pm_opp_set_rate(dev, 0);
}
static int geni_i2c_runtime_resume(struct device *dev)
@@ -1300,6 +1307,12 @@ static int geni_i2c_runtime_resume(struct device *dev)
return ret;
}
+ if (gi2c->src_freq) {
+ ret = dev_pm_opp_set_rate(dev, gi2c->src_freq);
+ if (ret)
+ return ret;
+ }
+
enable_irq(gi2c->irq);
return 0;
--
2.34.1