Linux-RISC-V Archive on lore.kernel.org
 help / color / mirror / Atom feed
From: "Troy Mitchell" <troy.mitchell@linux.spacemit.com>
To: "Alex Elder" <elder@riscstar.com>,
	"Troy Mitchell" <troy.mitchell@linux.spacemit.com>,
	"Andi Shyti" <andi.shyti@kernel.org>,
	"Yixun Lan" <dlan@kernel.org>
Cc: <linux-i2c@vger.kernel.org>, <linux-kernel@vger.kernel.org>,
	<linux-riscv@lists.infradead.org>, <spacemit@lists.linux.dev>
Subject: Re: [PATCH v6 1/2] i2c: spacemit: configure ILCR/IWCR for accurate SCL frequency
Date: Fri, 08 May 2026 14:37:14 +0800	[thread overview]
Message-ID: <DID36992GF2G.229YAG9BSQ3VZ@linux.spacemit.com> (raw)
In-Reply-To: <01278362-ae7c-424a-838b-4add1718e7f6@riscstar.com>

On Wed Apr 29, 2026 at 8:40 PM CST, Alex Elder wrote:
> On 4/29/26 2:48 AM, Troy Mitchell wrote:

[...]

>>   #define SPACEMIT_ISR		 0x4		/* Status register */
>>   #define SPACEMIT_IDBR		 0xc		/* Data buffer register */
>>   #define SPACEMIT_IRCR		 0x18		/* Reset cycle counter */
>> +#define SPACEMIT_ILCR		 0x10		/* Load Count Register */
>> +#define SPACEMIT_IWCR		 0x14		/* Wait Count Register */
>>   #define SPACEMIT_IBMR		 0x1c		/* Bus monitor register */
>>   
>>   /* SPACEMIT_ICR register fields */
>> @@ -88,6 +92,12 @@
>>   #define SPACEMIT_BMR_SDA         BIT(0)		/* SDA line level */
>>   #define SPACEMIT_BMR_SCL         BIT(1)		/* SCL line level */
>>   
>> +#define SPACEMIT_LCR_LV_STANDARD_MASK		GENMASK(8, 0)
>> +#define SPACEMIT_LCR_LV_FAST_MASK		GENMASK(17, 9)
>> +
>> +/* Required by I2C IP for correct SCL timing */
>> +#define SPACEMIT_IWCR_INIT_VALUE		0x142A
>
> Even if it's just used to "do the right thing", I still think it's
> useful to break this numeric value into what it *represents*.  The
> register is defined this way:
> COUNT		GENMASK(4:0)
> HS_COUNT1	GENMASK(9:5)
> HS_COUNT2	GENMASK(14:10)
> (the rest is reserved)
>
> So 0x142a represents:
> COUNT		10	33 MHz I2C functional clock (versus 66 MHz)
> HS_COUNT1	2	Count defining start bit setup/hold times
> HS_COUNT2	3	Count defining stop bit setup/hold times
>
> The first one in particular *could* be managed in your clock
> to support even more accurate clock rates.  But even if you
> don't change its value I think it's meaningful to know what
> the value represents.
>
> That said--what I suggest won't change what the code does,
> it just helps the reader understand what it's doing.
Thanks for the suggestion.
I've broken SPACEMIT_IWCR_INIT_VALUE into its constituent fields
using FIELD_PREP. One small correction: 0x142A actually decodes
to COUNT=10, HS_COUNT1=1, HS_COUNT2=5 (not 2 and 3).
>
>> +
>>   /* i2c bus recover timeout: us */
>>   #define SPACEMIT_I2C_BUS_BUSY_TIMEOUT		100000
>>   
>>
[...]

>> +
>> +static int spacemit_i2c_clk_determine_rate(struct clk_hw *hw,
>> +					   struct clk_rate_request *req)
>> +{
>> +	struct spacemit_i2c_dev *i2c = container_of(hw, struct spacemit_i2c_dev, scl_clk_hw);
>> +	u32 lv, denom;
>
> Can you have clk_set_rate() call clk_determine_rate(), or at
> least have them both share a helper that does these (duplicated)
> calculations?
Done. Extracted spacemit_i2c_calc_lv() as a shared helper used
by both set_rate and determine_rate.

>
>> +
>> +	denom = DIV_ROUND_CLOSEST(req->best_parent_rate, req->rate);
>> +
>> +	if (i2c->mode == SPACEMIT_MODE_STANDARD) {
>> +		lv = (denom <= 8) ? 0 : DIV_ROUND_CLOSEST(denom - 8, 2);
>> +		req->rate = DIV_ROUND_CLOSEST(req->best_parent_rate, lv * 2 + 8);
>> +	} else {
>> +		lv = (denom <= 10) ? 0 : DIV_ROUND_CLOSEST(denom - 10, 2);
>> +		req->rate = DIV_ROUND_CLOSEST(req->best_parent_rate, lv * 2 + 10);
>> +	}
>> +
>> +	return 0;
>> +}
>> +

[...]

>>   
>> +static struct clk *spacemit_i2c_register_scl_clk(struct spacemit_i2c_dev *i2c)
>> +{
>> +	struct clk_init_data init = {};
>> +	char name[64];
>> +	int ret;
>> +
>> +	ret = snprintf(name, sizeof(name), "%s_scl_clk", dev_name(i2c->dev));
>> +	if (ret >= ARRAY_SIZE(name))
>> +		dev_warn(i2c->dev, "scl clock name truncated");
>> +
>> +	init.name = name;
>> +	init.ops = &spacemit_i2c_clk_ops;
>> +	init.parent_data = (struct clk_parent_data[]) {
>> +		{ .fw_name = "func" },
>> +	};
>> +	init.num_parents = 1;
>> +
>> +	i2c->scl_clk_hw.init = &init;
>> +
>> +	return devm_clk_register(i2c->dev, &i2c->scl_clk_hw);
>
> The comments above clk_register() indicate that this is a deprecated
> interface, and you should use a form of clk_hw_register() instead.
> devm_clk_hw_register() exists and takes the same arguments, but
> returns an int rather than the clock pointer.  But I think then you
> can call devm_clk_get_enabled() in the caller (spacemit_i2c_probe()).
> That would allow you to get rid of your clk_disable_unprepare()
> wrapper too.
I've switched to devm_clk_hw_register() as suggested. However,
devm_clk_get_enabled() isn't applicable here because the SCL clock is
registered internally by the driver rather than being described in the
Device Tree. Since devm_clk_get_enabled() relies on clk_get(),
it would fail to find the clock without a DT entry or a clkdev lookup.

Instead, I'm using devm_clk_hw_get_clk() to retrieve the struct clk
directly from the clk_hw, followed by a manual clk_prepare_enable()
with a devm action for automated cleanup.

                            - Troy

_______________________________________________
linux-riscv mailing list
linux-riscv@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-riscv

  reply	other threads:[~2026-05-08  6:38 UTC|newest]

Thread overview: 5+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-04-29  7:48 [PATCH v6 0/2] i2c: spacemit: improve clock handling and cleanups Troy Mitchell
2026-04-29  7:48 ` [PATCH v6 1/2] i2c: spacemit: configure ILCR/IWCR for accurate SCL frequency Troy Mitchell
2026-04-29 12:40   ` Alex Elder
2026-05-08  6:37     ` Troy Mitchell [this message]
2026-04-29  7:48 ` [PATCH v6 2/2] i2c: spacemit: drop warning when clock-frequency property is absent Troy Mitchell

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=DID36992GF2G.229YAG9BSQ3VZ@linux.spacemit.com \
    --to=troy.mitchell@linux.spacemit.com \
    --cc=andi.shyti@kernel.org \
    --cc=dlan@kernel.org \
    --cc=elder@riscstar.com \
    --cc=linux-i2c@vger.kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-riscv@lists.infradead.org \
    --cc=spacemit@lists.linux.dev \
    /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