[PATCH 4/5] i2c: qcom-cci: Add OPP table support and enforce FAST_PLUS requirements

Konrad Dybcio
2025-09-04T14:31:44Z

From: Konrad Dybcio <konrad.dybcio@oss.qualcomm.com>

The CCI clock has voltage requirements, which need to be described
through an OPP table.

The 1 MHz FAST_PLUS mode requires the CCI core clock runs at 37,5 MHz
(which is a value common across all SoCs), since it's not possible to
reach the required timings with the default 19.2 MHz rate.

Address both issues by introducing an OPP table and using it to vote
for the faster rate.

Signed-off-by: Konrad Dybcio <konrad.dybcio@oss.qualcomm.com>
---
drivers/i2c/busses/i2c-qcom-cci.c | 33 +++++++++++++++++++++++++++++++++
1 file changed, 33 insertions(+)

diff --git a/drivers/i2c/busses/i2c-qcom-cci.c b/drivers/i2c/busses/i2c-qcom-cci.c
index 74fedfdec3ae4e034ec4d946179e963c783b5923..d6192e2a5e3bc4d908cba594d1910a41f3a41e9c 100644
--- a/drivers/i2c/busses/i2c-qcom-cci.c
+++ b/drivers/i2c/busses/i2c-qcom-cci.c
@@ -10,6 +10,7 @@
#include <linux/module.h>
#include <linux/of.h>
#include <linux/platform_device.h>
+#include <linux/pm_opp.h>
#include <linux/pm_runtime.h>

#define CCI_HW_VERSION 0x0
@@ -121,6 +122,7 @@ struct cci_data {
struct i2c_adapter_quirks quirks;
u16 queue_size[NUM_QUEUES];
struct hw_params params[3];
+ bool fast_mode_plus_supported;
};

struct cci {
@@ -466,9 +468,22 @@ static const struct i2c_algorithm cci_algo = {
.functionality = cci_func,
};

+static unsigned long cci_desired_clk_rate(struct cci *cci)
+{
+ if (cci->data->fast_mode_plus_supported)
+ return 37500000ULL;
+
+ return 19200000ULL;
+}
+
static int __maybe_unused cci_suspend_runtime(struct device *dev)
{
struct cci *cci = dev_get_drvdata(dev);
+ int ret;
+
+ ret = dev_pm_opp_set_rate(dev, 0);
+ if (ret)
+ return ret;

clk_bulk_disable_unprepare(cci->nclocks, cci->clocks);

@@ -484,6 +499,10 @@ static int __maybe_unused cci_resume_runtime(struct device *dev)
if (ret)
return ret;

+ ret = dev_pm_opp_set_rate(dev, cci_desired_clk_rate(cci));
+ if (ret)
+ return ret;
+
cci_init(cci);

return 0;
@@ -588,6 +607,19 @@ static int cci_probe(struct platform_device *pdev)
if (ret < 0)
return ret;

+ ret = devm_pm_opp_set_clkname(dev, "cci");
+ if (ret)
+ return ret;
+
+ /* OPP table is optional */
+ ret = devm_pm_opp_of_add_table(dev);
+ if (ret && ret != -ENODEV)
+ return dev_err_probe(dev, ret, "invalid OPP table in device tree\n");
+
+ ret = dev_pm_opp_set_rate(dev, cci_desired_clk_rate(cci));
+ if (ret)
+ return ret;
+
/* Interrupt */

ret = platform_get_irq(pdev, 0);
@@ -775,6 +807,7 @@ static const struct cci_data cci_v2_data = {
.trdhld = 3,
.tsp = 3
},
+ .fast_mode_plus_supported = true,
};

static const struct of_device_id cci_dt_match[] = {

--
2.51.0