From: Loic Poulain <loic.poulain@oss.qualcomm.com>
To: Robert Foss <rfoss@kernel.org>,
Andi Shyti <andi.shyti@kernel.org>,
Wolfram Sang <wsa+renesas@sang-engineering.com>,
Dmitry Baryshkov <dmitry.baryshkov@oss.qualcomm.com>,
Luca Weiss <luca@lucaweiss.eu>
Cc: linux-i2c@vger.kernel.org, linux-arm-msm@vger.kernel.org,
linux-kernel@vger.kernel.org, vladimir.zapolskiy@linaro.org,
konradybcio@kernel.org, stephan.gerhold@linaro.org,
Loic Poulain <loic.poulain@oss.qualcomm.com>
Subject: [PATCH v4 2/5] i2c: qcom-cci: Support per-mode CCI clock rates
Date: Sat, 01 Aug 2026 22:10:44 +0200 [thread overview]
Message-ID: <20260801-cci-clk-fix-v4-2-e1d80da54e01@oss.qualcomm.com> (raw)
In-Reply-To: <20260801-cci-clk-fix-v4-0-e1d80da54e01@oss.qualcomm.com>
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>
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 0a216c4d08114f267c8441925e8811ca64f1a909..1092ce0371429ef64c7cc44cc23a42ba4c135af5 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 const struct dev_pm_ops qcom_cci_pm = {
@@ -570,6 +609,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;
@@ -652,7 +696,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,
@@ -664,7 +708,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,
@@ -685,7 +729,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,
@@ -697,7 +741,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,
@@ -718,7 +762,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,
@@ -730,7 +774,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,
@@ -742,7 +786,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
next prev parent reply other threads:[~2026-08-01 20:10 UTC|newest]
Thread overview: 6+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-08-01 20:10 [PATCH v4 0/5] i2c: qcom-cci: Enforce the required CCI clock rate Loic Poulain
2026-08-01 20:10 ` [PATCH v4 1/5] i2c: qcom-cci: Switch msm8953 to the CCI v2 timing/rate config Loic Poulain
2026-08-01 20:10 ` Loic Poulain [this message]
2026-08-01 20:10 ` [PATCH v4 3/5] i2c: qcom-cci: Add 19.2 MHz timings for the v2 CCI Loic Poulain
2026-08-01 20:10 ` [PATCH v4 4/5] i2c: qcom-cci: Share the timing table across CCI revisions Loic Poulain
2026-08-01 20:10 ` [PATCH v4 5/5] i2c: qcom-cci: Enforce the required CCI clock rate Loic Poulain
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20260801-cci-clk-fix-v4-2-e1d80da54e01@oss.qualcomm.com \
--to=loic.poulain@oss.qualcomm.com \
--cc=andi.shyti@kernel.org \
--cc=dmitry.baryshkov@oss.qualcomm.com \
--cc=konradybcio@kernel.org \
--cc=linux-arm-msm@vger.kernel.org \
--cc=linux-i2c@vger.kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=luca@lucaweiss.eu \
--cc=rfoss@kernel.org \
--cc=stephan.gerhold@linaro.org \
--cc=vladimir.zapolskiy@linaro.org \
--cc=wsa+renesas@sang-engineering.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox