* [PATCH v3 1/5] i2c: qcom-cci: Switch msm8953 to the CCI v2 timing/rate config
2026-07-29 12:25 [PATCH v3 0/5] i2c: qcom-cci: Enforce the required CCI clock rate Loic Poulain
@ 2026-07-29 12:25 ` Loic Poulain
2026-07-31 14:06 ` Konrad Dybcio
2026-07-29 12:25 ` [PATCH v3 2/5] i2c: qcom-cci: Support per-mode CCI clock rates Loic Poulain
` (4 subsequent siblings)
5 siblings, 1 reply; 12+ messages in thread
From: Loic Poulain @ 2026-07-29 12:25 UTC (permalink / raw)
To: Robert Foss, Andi Shyti, Wolfram Sang, Dmitry Baryshkov,
Luca Weiss
Cc: linux-i2c, linux-arm-msm, linux-kernel, vladimir.zapolskiy,
konradybcio, stephan.gerhold, Loic Poulain, Wenmeng Liu
The msm8953 CCI timing table is internally inconsistent. Its Standard
and Fast timings match v1/v1.5, which are calibrated for a 19.2 MHz CCI
clock, but its Fast+ timings are essentially the v2 values, which are
calibrated for 37.5 MHz. Since all masters share a single CCI clock,
no single rate can satisfy all three modes with the current table, and
the DT assigns 19.2 MHz, so Fast+ timings are wrong.
The msm8953 CCI is the same hardware version as msm8996/sdm630, which
already use the cci_v2_data config (37.5 MHz). 37.5 MHz is supported by
the msm8953 CCI RCG, so reuse cci_v2_data for msm8953 as well and drop
the redundant, inconsistent standalone table. This makes all three I2C
modes self-consistent under a single clock rate.
Note this requires the CCI clock to run at 37.5 MHz, the driver selects
and enforces the proper rate in following patches.
Tested-by: Wenmeng Liu <wenmeng.liu@oss.qualcomm.com>
Signed-off-by: Loic Poulain <loic.poulain@oss.qualcomm.com>
---
drivers/i2c/busses/i2c-qcom-cci.c | 47 +--------------------------------------
1 file changed, 1 insertion(+), 46 deletions(-)
diff --git a/drivers/i2c/busses/i2c-qcom-cci.c b/drivers/i2c/busses/i2c-qcom-cci.c
index 4d64895a9e9e4e0bd5e0ccb5c3cc04b282b1e4d5..23cef922351ce44d71f1691cd7e4aab274fdd196 100644
--- a/drivers/i2c/busses/i2c-qcom-cci.c
+++ b/drivers/i2c/busses/i2c-qcom-cci.c
@@ -772,54 +772,9 @@ static const struct cci_data cci_v2_data = {
},
};
-static const struct cci_data cci_msm8953_data = {
- .num_masters = 2,
- .queue_size = { 64, 16 },
- .quirks = {
- .max_write_len = 11,
- .max_read_len = 12,
- },
- .params[I2C_MODE_STANDARD] = {
- .thigh = 78,
- .tlow = 114,
- .tsu_sto = 28,
- .tsu_sta = 28,
- .thd_dat = 10,
- .thd_sta = 77,
- .tbuf = 118,
- .scl_stretch_en = 0,
- .trdhld = 6,
- .tsp = 1
- },
- .params[I2C_MODE_FAST] = {
- .thigh = 20,
- .tlow = 28,
- .tsu_sto = 21,
- .tsu_sta = 21,
- .thd_dat = 13,
- .thd_sta = 18,
- .tbuf = 32,
- .scl_stretch_en = 0,
- .trdhld = 6,
- .tsp = 3
- },
- .params[I2C_MODE_FAST_PLUS] = {
- .thigh = 16,
- .tlow = 22,
- .tsu_sto = 17,
- .tsu_sta = 18,
- .thd_dat = 16,
- .thd_sta = 15,
- .tbuf = 19,
- .scl_stretch_en = 1,
- .trdhld = 3,
- .tsp = 3
- },
-};
-
static const struct of_device_id cci_dt_match[] = {
{ .compatible = "qcom,msm8226-cci", .data = &cci_v1_data},
- { .compatible = "qcom,msm8953-cci", .data = &cci_msm8953_data},
+ { .compatible = "qcom,msm8953-cci", .data = &cci_v2_data},
{ .compatible = "qcom,msm8974-cci", .data = &cci_v1_5_data},
{ .compatible = "qcom,msm8996-cci", .data = &cci_v2_data},
--
2.34.1
^ permalink raw reply related [flat|nested] 12+ messages in thread* Re: [PATCH v3 1/5] i2c: qcom-cci: Switch msm8953 to the CCI v2 timing/rate config
2026-07-29 12:25 ` [PATCH v3 1/5] i2c: qcom-cci: Switch msm8953 to the CCI v2 timing/rate config Loic Poulain
@ 2026-07-31 14:06 ` Konrad Dybcio
2026-07-31 14:25 ` Loic Poulain
2026-08-03 2:28 ` Wenmeng Liu
0 siblings, 2 replies; 12+ messages in thread
From: Konrad Dybcio @ 2026-07-31 14:06 UTC (permalink / raw)
To: Loic Poulain, Robert Foss, Andi Shyti, Wolfram Sang,
Dmitry Baryshkov, Luca Weiss
Cc: linux-i2c, linux-arm-msm, linux-kernel, vladimir.zapolskiy,
konradybcio, stephan.gerhold, Wenmeng Liu
On 7/29/26 2:25 PM, Loic Poulain wrote:
> The msm8953 CCI timing table is internally inconsistent. Its Standard
> and Fast timings match v1/v1.5, which are calibrated for a 19.2 MHz CCI
> clock, but its Fast+ timings are essentially the v2 values, which are
> calibrated for 37.5 MHz. Since all masters share a single CCI clock,
> no single rate can satisfy all three modes with the current table, and
> the DT assigns 19.2 MHz, so Fast+ timings are wrong.
>
> The msm8953 CCI is the same hardware version as msm8996/sdm630, which
> already use the cci_v2_data config (37.5 MHz). 37.5 MHz is supported by
> the msm8953 CCI RCG, so reuse cci_v2_data for msm8953 as well and drop
> the redundant, inconsistent standalone table. This makes all three I2C
> modes self-consistent under a single clock rate.
>
> Note this requires the CCI clock to run at 37.5 MHz, the driver selects
> and enforces the proper rate in following patches.
>
> Tested-by: Wenmeng Liu <wenmeng.liu@oss.qualcomm.com>
Wenmeng, have you actually tested this on MSM8953 device?
Konrad
^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [PATCH v3 1/5] i2c: qcom-cci: Switch msm8953 to the CCI v2 timing/rate config
2026-07-31 14:06 ` Konrad Dybcio
@ 2026-07-31 14:25 ` Loic Poulain
2026-08-03 2:28 ` Wenmeng Liu
1 sibling, 0 replies; 12+ messages in thread
From: Loic Poulain @ 2026-07-31 14:25 UTC (permalink / raw)
To: Konrad Dybcio
Cc: Robert Foss, Andi Shyti, Wolfram Sang, Dmitry Baryshkov,
Luca Weiss, linux-i2c, linux-arm-msm, linux-kernel,
vladimir.zapolskiy, konradybcio, stephan.gerhold, Wenmeng Liu
On Fri, Jul 31, 2026 at 4:07 PM Konrad Dybcio
<konrad.dybcio@oss.qualcomm.com> wrote:
>
> On 7/29/26 2:25 PM, Loic Poulain wrote:
> > The msm8953 CCI timing table is internally inconsistent. Its Standard
> > and Fast timings match v1/v1.5, which are calibrated for a 19.2 MHz CCI
> > clock, but its Fast+ timings are essentially the v2 values, which are
> > calibrated for 37.5 MHz. Since all masters share a single CCI clock,
> > no single rate can satisfy all three modes with the current table, and
> > the DT assigns 19.2 MHz, so Fast+ timings are wrong.
> >
> > The msm8953 CCI is the same hardware version as msm8996/sdm630, which
> > already use the cci_v2_data config (37.5 MHz). 37.5 MHz is supported by
> > the msm8953 CCI RCG, so reuse cci_v2_data for msm8953 as well and drop
> > the redundant, inconsistent standalone table. This makes all three I2C
> > modes self-consistent under a single clock rate.
> >
> > Note this requires the CCI clock to run at 37.5 MHz, the driver selects
> > and enforces the proper rate in following patches.
> >
> > Tested-by: Wenmeng Liu <wenmeng.liu@oss.qualcomm.com>
>
> Wenmeng, have you actually tested this on MSM8953 device?
Hmm, No, the b4 trailers got confused after the series was reworked
and incorrectly carried over the tag to this patch.
Unless Wenmeng provides it, I'll remove it in the next revision once
I've collected a bit more review feedback.
^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [PATCH v3 1/5] i2c: qcom-cci: Switch msm8953 to the CCI v2 timing/rate config
2026-07-31 14:06 ` Konrad Dybcio
2026-07-31 14:25 ` Loic Poulain
@ 2026-08-03 2:28 ` Wenmeng Liu
1 sibling, 0 replies; 12+ messages in thread
From: Wenmeng Liu @ 2026-08-03 2:28 UTC (permalink / raw)
To: Konrad Dybcio, Loic Poulain, Robert Foss, Andi Shyti,
Wolfram Sang, Dmitry Baryshkov, Luca Weiss
Cc: linux-i2c, linux-arm-msm, linux-kernel, vladimir.zapolskiy,
konradybcio, stephan.gerhold
Hi Konrad,
On 7/31/2026 10:06 PM, Konrad Dybcio wrote:
> On 7/29/26 2:25 PM, Loic Poulain wrote:
>> The msm8953 CCI timing table is internally inconsistent. Its Standard
>> and Fast timings match v1/v1.5, which are calibrated for a 19.2 MHz CCI
>> clock, but its Fast+ timings are essentially the v2 values, which are
>> calibrated for 37.5 MHz. Since all masters share a single CCI clock,
>> no single rate can satisfy all three modes with the current table, and
>> the DT assigns 19.2 MHz, so Fast+ timings are wrong.
>>
>> The msm8953 CCI is the same hardware version as msm8996/sdm630, which
>> already use the cci_v2_data config (37.5 MHz). 37.5 MHz is supported by
>> the msm8953 CCI RCG, so reuse cci_v2_data for msm8953 as well and drop
>> the redundant, inconsistent standalone table. This makes all three I2C
>> modes self-consistent under a single clock rate.
>>
>> Note this requires the CCI clock to run at 37.5 MHz, the driver selects
>> and enforces the proper rate in following patches.
>>
>> Tested-by: Wenmeng Liu <wenmeng.liu@oss.qualcomm.com>
>
> Wenmeng, have you actually tested this on MSM8953 device?
No, that's not the case. My testing was done on Hamoa & Koidak, which
uses the common msm8996 compatible.
Thanks,
Wenmeng
^ permalink raw reply [flat|nested] 12+ messages in thread
* [PATCH v3 2/5] i2c: qcom-cci: Support per-mode CCI clock rates
2026-07-29 12:25 [PATCH v3 0/5] i2c: qcom-cci: Enforce the required CCI clock rate Loic Poulain
2026-07-29 12:25 ` [PATCH v3 1/5] i2c: qcom-cci: Switch msm8953 to the CCI v2 timing/rate config Loic Poulain
@ 2026-07-29 12:25 ` Loic Poulain
2026-07-29 12:25 ` [PATCH v3 3/5] i2c: qcom-cci: Add 19.2 MHz timings for the v2 CCI Loic Poulain
` (3 subsequent siblings)
5 siblings, 0 replies; 12+ messages in thread
From: Loic Poulain @ 2026-07-29 12:25 UTC (permalink / raw)
To: Robert Foss, Andi Shyti, Wolfram Sang, Dmitry Baryshkov,
Luca Weiss
Cc: linux-i2c, linux-arm-msm, linux-kernel, vladimir.zapolskiy,
konradybcio, stephan.gerhold, Loic Poulain, Wenmeng Liu
The CCI hw_params timing values (thigh, tlow, etc.) are expressed in
clock ticks and are only valid at the specific CCI clock rate they were
calibrated for. Different I2C modes may be calibrated for different
rates, and the single CCI clock is shared by all masters.
Turn the timing table into a two-dimensional [rate][mode] matrix so a
given rate can carry timing sets for each mode, and select the entry
matching the currently running clock rate at init time. The existing
per-variant values are moved under their calibrated rate (19.2 MHz for
v1/v1.5, 37.5 MHz for v2), no timing values are changed.
At this stage the driver only validates the running rate against the
table, the timings are only valid at the exact rate they were calibrated
for, so if the current rate has no matching entry for a master's mode,
fail initialization rather than program incorrect timings. A following
patch actively enforces the required rate so this becomes a safety net.
Suggested-by: Vladimir Zapolskiy <vladimir.zapolskiy@linaro.org>
Tested-by: Wenmeng Liu <wenmeng.liu@oss.qualcomm.com>
Signed-off-by: Loic Poulain <loic.poulain@oss.qualcomm.com>
---
drivers/i2c/busses/i2c-qcom-cci.c | 74 +++++++++++++++++++++++++++++++--------
1 file changed, 59 insertions(+), 15 deletions(-)
diff --git a/drivers/i2c/busses/i2c-qcom-cci.c b/drivers/i2c/busses/i2c-qcom-cci.c
index 23cef922351ce44d71f1691cd7e4aab274fdd196..6fc2327db44acf016d008d0e268099494572774b 100644
--- a/drivers/i2c/busses/i2c-qcom-cci.c
+++ b/drivers/i2c/busses/i2c-qcom-cci.c
@@ -82,6 +82,13 @@ enum {
I2C_MODE_STANDARD,
I2C_MODE_FAST,
I2C_MODE_FAST_PLUS,
+ NUM_I2C_MODES,
+};
+
+enum {
+ CCI_CLK_RATE_19_2MHZ,
+ CCI_CLK_RATE_37_5MHZ,
+ NUM_CCI_CLK_RATES,
};
enum cci_i2c_queue_t {
@@ -117,7 +124,7 @@ struct cci_data {
unsigned int num_masters;
struct i2c_adapter_quirks quirks;
u16 queue_size[NUM_QUEUES];
- struct hw_params params[3];
+ struct hw_params params[NUM_CCI_CLK_RATES][NUM_I2C_MODES];
};
struct cci {
@@ -127,6 +134,7 @@ struct cci {
const struct cci_data *data;
struct clk_bulk_data *clocks;
int nclocks;
+ struct clk *cci_clk;
struct cci_master master[NUM_MASTERS];
};
@@ -225,7 +233,34 @@ static int cci_halt(struct cci *cci, u8 master_num)
return 0;
}
-static void cci_init(struct cci *cci)
+static const unsigned long cci_clk_rates[NUM_CCI_CLK_RATES] = {
+ [CCI_CLK_RATE_19_2MHZ] = 19200000,
+ [CCI_CLK_RATE_37_5MHZ] = 37500000,
+};
+
+static int cci_clk_rate_idx(unsigned long rate)
+{
+ int i;
+
+ for (i = 0; i < NUM_CCI_CLK_RATES; i++)
+ if (cci_clk_rates[i] == rate)
+ return i;
+
+ return -EINVAL;
+}
+
+static const struct hw_params *cci_get_hw_params(struct cci *cci, int mode)
+{
+ unsigned long rate = clk_get_rate(cci->cci_clk);
+ int ri = cci_clk_rate_idx(rate);
+
+ if (ri >= 0 && cci->data->params[ri][mode].thigh)
+ return &cci->data->params[ri][mode];
+
+ return NULL;
+}
+
+static int cci_init(struct cci *cci)
{
u32 val = CCI_IRQ_MASK_0_I2C_M0_RD_DONE |
CCI_IRQ_MASK_0_I2C_M0_Q0_REPORT |
@@ -249,7 +284,12 @@ static void cci_init(struct cci *cci)
if (!cci->master[i].cci)
continue;
- hw = &cci->data->params[mode];
+ hw = cci_get_hw_params(cci, mode);
+ if (!hw) {
+ dev_err(cci->dev, "no timing for mode %d at CCI clock %lu Hz\n",
+ mode, clk_get_rate(cci->cci_clk));
+ return -EOPNOTSUPP;
+ }
val = hw->thigh << 16 | hw->tlow;
writel(val, cci->base + CCI_I2C_Mm_SCL_CTL(i));
@@ -266,6 +306,8 @@ static void cci_init(struct cci *cci)
val = hw->scl_stretch_en << 8 | hw->trdhld << 4 | hw->tsp;
writel(val, cci->base + CCI_I2C_Mm_MISC_CTL(i));
}
+
+ return 0;
}
static int cci_reset(struct cci *cci)
@@ -283,9 +325,7 @@ static int cci_reset(struct cci *cci)
return -ETIMEDOUT;
}
- cci_init(cci);
-
- return 0;
+ return cci_init(cci);
}
static int cci_run_queue(struct cci *cci, u8 master, u8 queue)
@@ -488,8 +528,7 @@ static int __maybe_unused cci_resume_runtime(struct device *dev)
if (ret)
return ret;
- cci_init(cci);
- return 0;
+ return cci_init(cci);
}
static int __maybe_unused cci_suspend(struct device *dev)
@@ -586,6 +625,11 @@ static int cci_probe(struct platform_device *pdev)
return dev_err_probe(dev, -EINVAL, "not enough clocks in DT\n");
cci->nclocks = ret;
+ cci->cci_clk = devm_clk_get(dev, "cci");
+ if (IS_ERR(cci->cci_clk))
+ return dev_err_probe(dev, PTR_ERR(cci->cci_clk),
+ "failed to get CCI clock\n");
+
ret = cci_enable_clocks(cci);
if (ret < 0)
return ret;
@@ -668,7 +712,7 @@ static const struct cci_data cci_v1_data = {
.max_write_len = 10,
.max_read_len = 12,
},
- .params[I2C_MODE_STANDARD] = {
+ .params[CCI_CLK_RATE_19_2MHZ][I2C_MODE_STANDARD] = {
.thigh = 78,
.tlow = 114,
.tsu_sto = 28,
@@ -680,7 +724,7 @@ static const struct cci_data cci_v1_data = {
.trdhld = 6,
.tsp = 1
},
- .params[I2C_MODE_FAST] = {
+ .params[CCI_CLK_RATE_19_2MHZ][I2C_MODE_FAST] = {
.thigh = 20,
.tlow = 28,
.tsu_sto = 21,
@@ -701,7 +745,7 @@ static const struct cci_data cci_v1_5_data = {
.max_write_len = 10,
.max_read_len = 12,
},
- .params[I2C_MODE_STANDARD] = {
+ .params[CCI_CLK_RATE_19_2MHZ][I2C_MODE_STANDARD] = {
.thigh = 78,
.tlow = 114,
.tsu_sto = 28,
@@ -713,7 +757,7 @@ static const struct cci_data cci_v1_5_data = {
.trdhld = 6,
.tsp = 1
},
- .params[I2C_MODE_FAST] = {
+ .params[CCI_CLK_RATE_19_2MHZ][I2C_MODE_FAST] = {
.thigh = 20,
.tlow = 28,
.tsu_sto = 21,
@@ -734,7 +778,7 @@ static const struct cci_data cci_v2_data = {
.max_write_len = 11,
.max_read_len = 12,
},
- .params[I2C_MODE_STANDARD] = {
+ .params[CCI_CLK_RATE_37_5MHZ][I2C_MODE_STANDARD] = {
.thigh = 201,
.tlow = 174,
.tsu_sto = 204,
@@ -746,7 +790,7 @@ static const struct cci_data cci_v2_data = {
.trdhld = 6,
.tsp = 3
},
- .params[I2C_MODE_FAST] = {
+ .params[CCI_CLK_RATE_37_5MHZ][I2C_MODE_FAST] = {
.thigh = 38,
.tlow = 56,
.tsu_sto = 40,
@@ -758,7 +802,7 @@ static const struct cci_data cci_v2_data = {
.trdhld = 6,
.tsp = 3
},
- .params[I2C_MODE_FAST_PLUS] = {
+ .params[CCI_CLK_RATE_37_5MHZ][I2C_MODE_FAST_PLUS] = {
.thigh = 16,
.tlow = 22,
.tsu_sto = 17,
--
2.34.1
^ permalink raw reply related [flat|nested] 12+ messages in thread* [PATCH v3 3/5] i2c: qcom-cci: Add 19.2 MHz timings for the v2 CCI
2026-07-29 12:25 [PATCH v3 0/5] i2c: qcom-cci: Enforce the required CCI clock rate Loic Poulain
2026-07-29 12:25 ` [PATCH v3 1/5] i2c: qcom-cci: Switch msm8953 to the CCI v2 timing/rate config Loic Poulain
2026-07-29 12:25 ` [PATCH v3 2/5] i2c: qcom-cci: Support per-mode CCI clock rates Loic Poulain
@ 2026-07-29 12:25 ` Loic Poulain
2026-07-29 12:25 ` [PATCH v3 4/5] i2c: qcom-cci: Share the timing table across CCI revisions Loic Poulain
` (2 subsequent siblings)
5 siblings, 0 replies; 12+ messages in thread
From: Loic Poulain @ 2026-07-29 12:25 UTC (permalink / raw)
To: Robert Foss, Andi Shyti, Wolfram Sang, Dmitry Baryshkov,
Luca Weiss
Cc: linux-i2c, linux-arm-msm, linux-kernel, vladimir.zapolskiy,
konradybcio, stephan.gerhold, Loic Poulain
The v2 CCI (msm8996/sdm630/msm8953/...) is normally clocked at 37.5 MHz,
but its CCI clock can also be configured and run at 19.2 MHz. Add the
Standard and Fast timing sets calibrated for 19.2 MHz so that a v2
controller left at that rate still has valid timings for those modes
(Fast+ requires 37.5 MHz).
The values mirror the v1/v1.5 19.2 MHz Standard/Fast timings, which
share the same CCI clock rate.
Signed-off-by: Loic Poulain <loic.poulain@oss.qualcomm.com>
---
drivers/i2c/busses/i2c-qcom-cci.c | 24 ++++++++++++++++++++++++
1 file changed, 24 insertions(+)
diff --git a/drivers/i2c/busses/i2c-qcom-cci.c b/drivers/i2c/busses/i2c-qcom-cci.c
index 6fc2327db44acf016d008d0e268099494572774b..a7b9a8d43e39a6bdeb47250c34ef7474bd7138f8 100644
--- a/drivers/i2c/busses/i2c-qcom-cci.c
+++ b/drivers/i2c/busses/i2c-qcom-cci.c
@@ -778,6 +778,30 @@ static const struct cci_data cci_v2_data = {
.max_write_len = 11,
.max_read_len = 12,
},
+ .params[CCI_CLK_RATE_19_2MHZ][I2C_MODE_STANDARD] = {
+ .thigh = 78,
+ .tlow = 114,
+ .tsu_sto = 28,
+ .tsu_sta = 28,
+ .thd_dat = 10,
+ .thd_sta = 77,
+ .tbuf = 118,
+ .scl_stretch_en = 0,
+ .trdhld = 6,
+ .tsp = 1
+ },
+ .params[CCI_CLK_RATE_19_2MHZ][I2C_MODE_FAST] = {
+ .thigh = 20,
+ .tlow = 28,
+ .tsu_sto = 21,
+ .tsu_sta = 21,
+ .thd_dat = 13,
+ .thd_sta = 18,
+ .tbuf = 32,
+ .scl_stretch_en = 0,
+ .trdhld = 6,
+ .tsp = 3
+ },
.params[CCI_CLK_RATE_37_5MHZ][I2C_MODE_STANDARD] = {
.thigh = 201,
.tlow = 174,
--
2.34.1
^ permalink raw reply related [flat|nested] 12+ messages in thread* [PATCH v3 4/5] i2c: qcom-cci: Share the timing table across CCI revisions
2026-07-29 12:25 [PATCH v3 0/5] i2c: qcom-cci: Enforce the required CCI clock rate Loic Poulain
` (2 preceding siblings ...)
2026-07-29 12:25 ` [PATCH v3 3/5] i2c: qcom-cci: Add 19.2 MHz timings for the v2 CCI Loic Poulain
@ 2026-07-29 12:25 ` Loic Poulain
2026-07-29 12:25 ` [PATCH v3 5/5] i2c: qcom-cci: Enforce the required CCI clock rate Loic Poulain
2026-07-31 20:54 ` [PATCH v3 0/5] " Andi Shyti
5 siblings, 0 replies; 12+ messages in thread
From: Loic Poulain @ 2026-07-29 12:25 UTC (permalink / raw)
To: Robert Foss, Andi Shyti, Wolfram Sang, Dmitry Baryshkov,
Luca Weiss
Cc: linux-i2c, linux-arm-msm, linux-kernel, vladimir.zapolskiy,
konradybcio, stephan.gerhold, Loic Poulain
The hw_params timing values only depend on the CCI clock rate and the
I2C mode, not on the hardware revision: every per-variant table used
identical values for a given [rate][mode]. Only the set of supported
modes differs between revisions.
Move the timings into a single shared cci_hw_params[rate][mode] table
and describe each variant's highest supported mode in cci_data with
max_mode instead of duplicating the timing values. This removes the
per-variant timing tables without any functional change.
Suggested-by: Vladimir Zapolskiy <vladimir.zapolskiy@linaro.org>
Signed-off-by: Loic Poulain <loic.poulain@oss.qualcomm.com>
---
drivers/i2c/busses/i2c-qcom-cci.c | 181 +++++++++++++++-----------------------
1 file changed, 70 insertions(+), 111 deletions(-)
diff --git a/drivers/i2c/busses/i2c-qcom-cci.c b/drivers/i2c/busses/i2c-qcom-cci.c
index a7b9a8d43e39a6bdeb47250c34ef7474bd7138f8..8e4641de049e56da5afbddbab122946bcdd4f60d 100644
--- a/drivers/i2c/busses/i2c-qcom-cci.c
+++ b/drivers/i2c/busses/i2c-qcom-cci.c
@@ -124,7 +124,8 @@ struct cci_data {
unsigned int num_masters;
struct i2c_adapter_quirks quirks;
u16 queue_size[NUM_QUEUES];
- struct hw_params params[NUM_CCI_CLK_RATES][NUM_I2C_MODES];
+ /* Highest I2C mode supported by this variant. */
+ u8 max_mode;
};
struct cci {
@@ -249,13 +250,76 @@ static int cci_clk_rate_idx(unsigned long rate)
return -EINVAL;
}
+static const struct hw_params cci_hw_params[NUM_CCI_CLK_RATES][NUM_I2C_MODES] = {
+ [CCI_CLK_RATE_19_2MHZ][I2C_MODE_STANDARD] = {
+ .thigh = 78,
+ .tlow = 114,
+ .tsu_sto = 28,
+ .tsu_sta = 28,
+ .thd_dat = 10,
+ .thd_sta = 77,
+ .tbuf = 118,
+ .scl_stretch_en = 0,
+ .trdhld = 6,
+ .tsp = 1
+ },
+ [CCI_CLK_RATE_19_2MHZ][I2C_MODE_FAST] = {
+ .thigh = 20,
+ .tlow = 28,
+ .tsu_sto = 21,
+ .tsu_sta = 21,
+ .thd_dat = 13,
+ .thd_sta = 18,
+ .tbuf = 32,
+ .scl_stretch_en = 0,
+ .trdhld = 6,
+ .tsp = 3
+ },
+ [CCI_CLK_RATE_37_5MHZ][I2C_MODE_STANDARD] = {
+ .thigh = 201,
+ .tlow = 174,
+ .tsu_sto = 204,
+ .tsu_sta = 231,
+ .thd_dat = 22,
+ .thd_sta = 162,
+ .tbuf = 227,
+ .scl_stretch_en = 0,
+ .trdhld = 6,
+ .tsp = 3
+ },
+ [CCI_CLK_RATE_37_5MHZ][I2C_MODE_FAST] = {
+ .thigh = 38,
+ .tlow = 56,
+ .tsu_sto = 40,
+ .tsu_sta = 40,
+ .thd_dat = 22,
+ .thd_sta = 35,
+ .tbuf = 62,
+ .scl_stretch_en = 0,
+ .trdhld = 6,
+ .tsp = 3
+ },
+ [CCI_CLK_RATE_37_5MHZ][I2C_MODE_FAST_PLUS] = {
+ .thigh = 16,
+ .tlow = 22,
+ .tsu_sto = 17,
+ .tsu_sta = 18,
+ .thd_dat = 16,
+ .thd_sta = 15,
+ .tbuf = 24,
+ .scl_stretch_en = 0,
+ .trdhld = 3,
+ .tsp = 3
+ },
+};
+
static const struct hw_params *cci_get_hw_params(struct cci *cci, int mode)
{
unsigned long rate = clk_get_rate(cci->cci_clk);
int ri = cci_clk_rate_idx(rate);
- if (ri >= 0 && cci->data->params[ri][mode].thigh)
- return &cci->data->params[ri][mode];
+ if (ri >= 0 && mode <= cci->data->max_mode && cci_hw_params[ri][mode].thigh)
+ return &cci_hw_params[ri][mode];
return NULL;
}
@@ -712,30 +776,7 @@ static const struct cci_data cci_v1_data = {
.max_write_len = 10,
.max_read_len = 12,
},
- .params[CCI_CLK_RATE_19_2MHZ][I2C_MODE_STANDARD] = {
- .thigh = 78,
- .tlow = 114,
- .tsu_sto = 28,
- .tsu_sta = 28,
- .thd_dat = 10,
- .thd_sta = 77,
- .tbuf = 118,
- .scl_stretch_en = 0,
- .trdhld = 6,
- .tsp = 1
- },
- .params[CCI_CLK_RATE_19_2MHZ][I2C_MODE_FAST] = {
- .thigh = 20,
- .tlow = 28,
- .tsu_sto = 21,
- .tsu_sta = 21,
- .thd_dat = 13,
- .thd_sta = 18,
- .tbuf = 32,
- .scl_stretch_en = 0,
- .trdhld = 6,
- .tsp = 3
- },
+ .max_mode = I2C_MODE_FAST,
};
static const struct cci_data cci_v1_5_data = {
@@ -745,30 +786,7 @@ static const struct cci_data cci_v1_5_data = {
.max_write_len = 10,
.max_read_len = 12,
},
- .params[CCI_CLK_RATE_19_2MHZ][I2C_MODE_STANDARD] = {
- .thigh = 78,
- .tlow = 114,
- .tsu_sto = 28,
- .tsu_sta = 28,
- .thd_dat = 10,
- .thd_sta = 77,
- .tbuf = 118,
- .scl_stretch_en = 0,
- .trdhld = 6,
- .tsp = 1
- },
- .params[CCI_CLK_RATE_19_2MHZ][I2C_MODE_FAST] = {
- .thigh = 20,
- .tlow = 28,
- .tsu_sto = 21,
- .tsu_sta = 21,
- .thd_dat = 13,
- .thd_sta = 18,
- .tbuf = 32,
- .scl_stretch_en = 0,
- .trdhld = 6,
- .tsp = 3
- },
+ .max_mode = I2C_MODE_FAST,
};
static const struct cci_data cci_v2_data = {
@@ -778,66 +796,7 @@ static const struct cci_data cci_v2_data = {
.max_write_len = 11,
.max_read_len = 12,
},
- .params[CCI_CLK_RATE_19_2MHZ][I2C_MODE_STANDARD] = {
- .thigh = 78,
- .tlow = 114,
- .tsu_sto = 28,
- .tsu_sta = 28,
- .thd_dat = 10,
- .thd_sta = 77,
- .tbuf = 118,
- .scl_stretch_en = 0,
- .trdhld = 6,
- .tsp = 1
- },
- .params[CCI_CLK_RATE_19_2MHZ][I2C_MODE_FAST] = {
- .thigh = 20,
- .tlow = 28,
- .tsu_sto = 21,
- .tsu_sta = 21,
- .thd_dat = 13,
- .thd_sta = 18,
- .tbuf = 32,
- .scl_stretch_en = 0,
- .trdhld = 6,
- .tsp = 3
- },
- .params[CCI_CLK_RATE_37_5MHZ][I2C_MODE_STANDARD] = {
- .thigh = 201,
- .tlow = 174,
- .tsu_sto = 204,
- .tsu_sta = 231,
- .thd_dat = 22,
- .thd_sta = 162,
- .tbuf = 227,
- .scl_stretch_en = 0,
- .trdhld = 6,
- .tsp = 3
- },
- .params[CCI_CLK_RATE_37_5MHZ][I2C_MODE_FAST] = {
- .thigh = 38,
- .tlow = 56,
- .tsu_sto = 40,
- .tsu_sta = 40,
- .thd_dat = 22,
- .thd_sta = 35,
- .tbuf = 62,
- .scl_stretch_en = 0,
- .trdhld = 6,
- .tsp = 3
- },
- .params[CCI_CLK_RATE_37_5MHZ][I2C_MODE_FAST_PLUS] = {
- .thigh = 16,
- .tlow = 22,
- .tsu_sto = 17,
- .tsu_sta = 18,
- .thd_dat = 16,
- .thd_sta = 15,
- .tbuf = 24,
- .scl_stretch_en = 0,
- .trdhld = 3,
- .tsp = 3
- },
+ .max_mode = I2C_MODE_FAST_PLUS,
};
static const struct of_device_id cci_dt_match[] = {
--
2.34.1
^ permalink raw reply related [flat|nested] 12+ messages in thread* [PATCH v3 5/5] i2c: qcom-cci: Enforce the required CCI clock rate
2026-07-29 12:25 [PATCH v3 0/5] i2c: qcom-cci: Enforce the required CCI clock rate Loic Poulain
` (3 preceding siblings ...)
2026-07-29 12:25 ` [PATCH v3 4/5] i2c: qcom-cci: Share the timing table across CCI revisions Loic Poulain
@ 2026-07-29 12:25 ` Loic Poulain
2026-07-31 20:55 ` Andi Shyti
2026-07-31 20:54 ` [PATCH v3 0/5] " Andi Shyti
5 siblings, 1 reply; 12+ messages in thread
From: Loic Poulain @ 2026-07-29 12:25 UTC (permalink / raw)
To: Robert Foss, Andi Shyti, Wolfram Sang, Dmitry Baryshkov,
Luca Weiss
Cc: linux-i2c, linux-arm-msm, linux-kernel, vladimir.zapolskiy,
konradybcio, stephan.gerhold, Loic Poulain, Konrad Dybcio,
Wenmeng Liu
The CCI hw_params timing values are only valid at the specific clock
rate they were calibrated for. A previous change made the driver select
the timing set matching the currently running clock rate, but the rate
itself was still left to the DT (assigned-clock-rates) or the bootloader,
which is fragile: if no rate is enforced the timings may not match and
violate the I2C specification.
Actively drive the CCI clock to the rate required by the configured
modes. The single CCI clock is shared by all masters, which may run in
different modes, so cci_get_required_rate() picks the lowest rate that
has a valid timing set for every active master's mode. This avoids
clocking the bus faster than necessary while still satisfying every
master (e.g. a Fast+ master forces 37.5 MHz).
Apply the rate through the OPP framework so that boards describing an
opp table also get the required power-domain/regulator votes for that rate.
Boards without an OPP table simply fall back to plain clk_set_rate()
behavior, so existing DTs keep working.
Suggested-by: Konrad Dybcio <konrad.dybcio@oss.qualcomm.com>
Tested-by: Wenmeng Liu <wenmeng.liu@oss.qualcomm.com>
Signed-off-by: Loic Poulain <loic.poulain@oss.qualcomm.com>
---
drivers/i2c/busses/i2c-qcom-cci.c | 75 +++++++++++++++++++++++++++++++++++++++
1 file changed, 75 insertions(+)
diff --git a/drivers/i2c/busses/i2c-qcom-cci.c b/drivers/i2c/busses/i2c-qcom-cci.c
index 8e4641de049e56da5afbddbab122946bcdd4f60d..fbe3fef1b49fb3d0f1b77ed9677f2d5e0eef4df5 100644
--- a/drivers/i2c/busses/i2c-qcom-cci.c
+++ b/drivers/i2c/busses/i2c-qcom-cci.c
@@ -11,6 +11,7 @@
#include <linux/of.h>
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
+#include <linux/pm_opp.h>
#define CCI_HW_VERSION 0x0
#define CCI_RESET_CMD 0x004
@@ -575,10 +576,67 @@ static void cci_disable_clocks(struct cci *cci)
clk_bulk_disable_unprepare(cci->nclocks, cci->clocks);
}
+/*
+ * The single CCI clock is shared by all masters, which may run in different
+ * modes. Pick the lowest rate that has a valid timing set for every active
+ * master's mode.
+ */
+static unsigned long cci_get_required_rate(struct cci *cci)
+{
+ int ri, i;
+
+ for (ri = 0; ri < NUM_CCI_CLK_RATES; ri++) {
+ bool supported = true;
+
+ for (i = 0; i < cci->data->num_masters; i++) {
+ int mode = cci->master[i].mode;
+
+ if (!cci->master[i].cci)
+ continue;
+
+ if (mode > cci->data->max_mode ||
+ !cci_hw_params[ri][mode].thigh) {
+ supported = false;
+ break;
+ }
+ }
+
+ if (supported)
+ return cci_clk_rates[ri];
+ }
+
+ return 0;
+}
+
+static int cci_set_core_rate(struct cci *cci, unsigned long rate)
+{
+ struct device *dev = cci->dev;
+ int ret;
+
+ ret = dev_pm_opp_set_rate(dev, rate);
+ if (ret) {
+ dev_warn(dev, "CCI clock could not be set to %lu Hz\n", rate);
+ return ret;
+ }
+
+ if (!rate)
+ return 0;
+
+ /*
+ * Sanity: The hw_params timings are only valid at the exact
+ * expected rate, verify what landed on the hardware.
+ */
+ if (clk_get_rate(cci->cci_clk) != rate)
+ dev_warn(dev, "CCI clock is not at expected %lu Hz\n", rate);
+
+ return 0;
+}
+
static int __maybe_unused cci_suspend_runtime(struct device *dev)
{
struct cci *cci = dev_get_drvdata(dev);
+ cci_set_core_rate(cci, 0);
cci_disable_clocks(cci);
return 0;
}
@@ -588,6 +646,10 @@ static int __maybe_unused cci_resume_runtime(struct device *dev)
struct cci *cci = dev_get_drvdata(dev);
int ret;
+ ret = cci_set_core_rate(cci, cci_get_required_rate(cci));
+ if (ret)
+ return ret;
+
ret = cci_enable_clocks(cci);
if (ret)
return ret;
@@ -694,6 +756,19 @@ static int cci_probe(struct platform_device *pdev)
return dev_err_probe(dev, PTR_ERR(cci->cci_clk),
"failed to get CCI clock\n");
+ ret = devm_pm_opp_set_clkname(dev, "cci");
+ if (ret)
+ return dev_err_probe(dev, ret, "failed to set CCI OPP clk\n");
+
+ /* OPP table is optional */
+ ret = devm_pm_opp_of_add_table(dev);
+ if (ret && ret != -ENODEV)
+ return dev_err_probe(dev, ret, "failed to add OPP table\n");
+
+ ret = cci_set_core_rate(cci, cci_get_required_rate(cci));
+ if (ret)
+ return dev_err_probe(dev, ret, "failed to set CCI clock rate\n");
+
ret = cci_enable_clocks(cci);
if (ret < 0)
return ret;
--
2.34.1
^ permalink raw reply related [flat|nested] 12+ messages in thread* Re: [PATCH v3 5/5] i2c: qcom-cci: Enforce the required CCI clock rate
2026-07-29 12:25 ` [PATCH v3 5/5] i2c: qcom-cci: Enforce the required CCI clock rate Loic Poulain
@ 2026-07-31 20:55 ` Andi Shyti
2026-08-01 7:14 ` Loic Poulain
0 siblings, 1 reply; 12+ messages in thread
From: Andi Shyti @ 2026-07-31 20:55 UTC (permalink / raw)
To: Loic Poulain
Cc: Robert Foss, Wolfram Sang, Dmitry Baryshkov, Luca Weiss,
linux-i2c, linux-arm-msm, linux-kernel, vladimir.zapolskiy,
konradybcio, stephan.gerhold, Konrad Dybcio, Wenmeng Liu
Hi Loic,
...
> @@ -588,6 +646,10 @@ static int __maybe_unused cci_resume_runtime(struct device *dev)
> struct cci *cci = dev_get_drvdata(dev);
> int ret;
>
> + ret = cci_set_core_rate(cci, cci_get_required_rate(cci));
> + if (ret)
> + return ret;
> +
> ret = cci_enable_clocks(cci);
> if (ret)
> return ret;
> @@ -694,6 +756,19 @@ static int cci_probe(struct platform_device *pdev)
> return dev_err_probe(dev, PTR_ERR(cci->cci_clk),
> "failed to get CCI clock\n");
>
> + ret = devm_pm_opp_set_clkname(dev, "cci");
> + if (ret)
> + return dev_err_probe(dev, ret, "failed to set CCI OPP clk\n");
what if CONFIG_PM_OPP is not configured in?
Thanks,
Andi
> +
> + /* OPP table is optional */
> + ret = devm_pm_opp_of_add_table(dev);
> + if (ret && ret != -ENODEV)
> + return dev_err_probe(dev, ret, "failed to add OPP table\n");
^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [PATCH v3 5/5] i2c: qcom-cci: Enforce the required CCI clock rate
2026-07-31 20:55 ` Andi Shyti
@ 2026-08-01 7:14 ` Loic Poulain
0 siblings, 0 replies; 12+ messages in thread
From: Loic Poulain @ 2026-08-01 7:14 UTC (permalink / raw)
To: Andi Shyti
Cc: Robert Foss, Wolfram Sang, Dmitry Baryshkov, Luca Weiss,
linux-i2c, linux-arm-msm, linux-kernel, vladimir.zapolskiy,
konradybcio, stephan.gerhold, Konrad Dybcio, Wenmeng Liu
On Fri, Jul 31, 2026 at 10:55 PM Andi Shyti <andi.shyti@kernel.org> wrote:
>
> Hi Loic,
>
> ...
>
> > @@ -588,6 +646,10 @@ static int __maybe_unused cci_resume_runtime(struct device *dev)
> > struct cci *cci = dev_get_drvdata(dev);
> > int ret;
> >
> > + ret = cci_set_core_rate(cci, cci_get_required_rate(cci));
> > + if (ret)
> > + return ret;
> > +
> > ret = cci_enable_clocks(cci);
> > if (ret)
> > return ret;
> > @@ -694,6 +756,19 @@ static int cci_probe(struct platform_device *pdev)
> > return dev_err_probe(dev, PTR_ERR(cci->cci_clk),
> > "failed to get CCI clock\n");
> >
> > + ret = devm_pm_opp_set_clkname(dev, "cci");
> > + if (ret)
> > + return dev_err_probe(dev, ret, "failed to set CCI OPP clk\n");
>
> what if CONFIG_PM_OPP is not configured in?
In that case we will 'gracefully' fail the probe as the operation will
return -EOPNOTSUPP. Actually, this mirrors spi-qup and spi-qcom-qspi.
On a qcom arm64 build, several always-present symbols pull it in.
That said, it could be better to make the dependency explicit rather
than rely on transitive selects and add select PM_OPP to the qcom-cci
Kconfig entry?
>
> Thanks,
> Andi
>
> > +
> > + /* OPP table is optional */
> > + ret = devm_pm_opp_of_add_table(dev);
> > + if (ret && ret != -ENODEV)
> > + return dev_err_probe(dev, ret, "failed to add OPP table\n");
^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [PATCH v3 0/5] i2c: qcom-cci: Enforce the required CCI clock rate
2026-07-29 12:25 [PATCH v3 0/5] i2c: qcom-cci: Enforce the required CCI clock rate Loic Poulain
` (4 preceding siblings ...)
2026-07-29 12:25 ` [PATCH v3 5/5] i2c: qcom-cci: Enforce the required CCI clock rate Loic Poulain
@ 2026-07-31 20:54 ` Andi Shyti
5 siblings, 0 replies; 12+ messages in thread
From: Andi Shyti @ 2026-07-31 20:54 UTC (permalink / raw)
To: Loic Poulain
Cc: Robert Foss, Wolfram Sang, Dmitry Baryshkov, Luca Weiss,
linux-i2c, linux-arm-msm, linux-kernel, vladimir.zapolskiy,
konradybcio, stephan.gerhold, Wenmeng Liu, Konrad Dybcio
> Loic Poulain (5):
> i2c: qcom-cci: Switch msm8953 to the CCI v2 timing/rate config
> i2c: qcom-cci: Support per-mode CCI clock rates
> i2c: qcom-cci: Add 19.2 MHz timings for the v2 CCI
> i2c: qcom-cci: Share the timing table across CCI revisions
> i2c: qcom-cci: Enforce the required CCI clock rate
Can you please rebase the series on top of next/master or
i2c/i2c-next? Patch 2 doesn't apply.
Andi
^ permalink raw reply [flat|nested] 12+ messages in thread