diff mbox

i2c: riic: remove clock and frequency restrictions

Message ID 20171018140459.104351-1-chris.brandt@renesas.com (mailing list archive)
State Superseded
Delegated to: Geert Uytterhoeven
Headers show

Commit Message

Chris Brandt Oct. 18, 2017, 2:04 p.m. UTC
Remove the restriction that the parent clock has to be a specific frequency
and also allow any speed to be supported.

Signed-off-by: Chris Brandt <chris.brandt@renesas.com>
---
Tested on the RZ/A1H RSK board with the following DT:
&i2c3 {
	pinctrl-names = "default";
	pinctrl-0 = <&i2c3_pins>;
	status = "okay";
	clock-frequency = <400000>; /* tested values from 10k-400k */
	i2c-scl-rising-time-ns = <300>;
	i2c-scl-falling-time-ns = <250>;
};
---
 drivers/i2c/busses/i2c-riic.c | 117 ++++++++++++++++++++++++++++++------------
 1 file changed, 83 insertions(+), 34 deletions(-)

Comments

Geert Uytterhoeven Oct. 18, 2017, 2:25 p.m. UTC | #1
Hi Chris,

On Wed, Oct 18, 2017 at 4:04 PM, Chris Brandt <chris.brandt@renesas.com> wrote:
> Remove the restriction that the parent clock has to be a specific frequency
> and also allow any speed to be supported.
>
> Signed-off-by: Chris Brandt <chris.brandt@renesas.com>

Thanks for your patch!

> --- a/drivers/i2c/busses/i2c-riic.c
> +++ b/drivers/i2c/busses/i2c-riic.c
> @@ -288,48 +283,101 @@ static const struct i2c_algorithm riic_algo = {
>         .functionality  = riic_func,
>  };
>
> -static int riic_init_hw(struct riic_dev *riic, u32 spd)
> +static int riic_init_hw(struct riic_dev *riic, struct i2c_timings *t)
>  {

> +       /*
> +        * Determine reference clock rate. We must be able to get the desired
> +        * frequency with only 62 clock ticks max (31 high, 31 low).
> +        * Aim for a duty of 60% LOW, 40% HIGH.
> +        */
> +       total_ticks = rate / t->bus_freq_hz;
> +       if (rate % t->bus_freq_hz)              /* round up */
> +               total_ticks++;

DIV_ROUND_UP()

> +       /*
> +        * Remove clock ticks for rise and fall times. Convert ns to clock
> +        * ticks.
> +        */
> +       brl -= t->scl_fall_ns / (1000000000/rate);
> +       brh -= t->scl_rise_ns / (1000000000/rate);

To avoid losing too much precision by the division, you can rewrite this as
e.g.

    brl -= t->scl_fall_ns * rate / 1000000000;

("scl_fall_ns * rate" should not overflow).
Perhaps DIV_ROUND_UP(), too?

> +       pr_debug("i2c-riic: freq=%lu, duty=%d, fall=%lu, rise=%lu, cks=%d, brl=%d, brh=%d\n",
> +                rate/total_ticks, ((brl+3)*100)/(brl+brh+6),
> +                t->scl_fall_ns / (1000000000/rate),
> +                t->scl_rise_ns / (1000000000/rate), cks, brl, brh);

Likewise (or: reuse the values from above?).

Gr{oetje,eeting}s,

                        Geert

--
Geert Uytterhoeven -- There's lots of Linux beyond ia32 -- geert@linux-m68k.org

In personal conversations with technical people, I call myself a hacker. But
when I'm talking to journalists I just say "programmer" or something like that.
                                -- Linus Torvalds
Chris Brandt Oct. 18, 2017, 3:10 p.m. UTC | #2
Hi Geert,

Thank you for your review.

On Wednesday, October 18, 2017 1, Geert Uytterhoeven wrote:
> > +       /*

> > +        * Remove clock ticks for rise and fall times. Convert ns to

> clock

> > +        * ticks.

> > +        */

> > +       brl -= t->scl_fall_ns / (1000000000/rate);

> > +       brh -= t->scl_rise_ns / (1000000000/rate);

> 

> To avoid losing too much precision by the division, you can rewrite this

> as

> e.g.

> 

>     brl -= t->scl_fall_ns * rate / 1000000000;

> 

> ("scl_fall_ns * rate" should not overflow).

> Perhaps DIV_ROUND_UP(), too?


Unfortunately, I do not get the correct number every time.

Sometimes I get the correct number, but most of the time I get 0.

Note, if rate=33,330,000 and t->scl_fall_ns=300, then 

t->scl_fall_ns * rate  = 9,999,000,000 (0x253FCA1C0) a 34-bit number.

So, I'd rather just stick with the less precision because it's not that 
much of a bit deal.


Chris
Geert Uytterhoeven Oct. 18, 2017, 3:14 p.m. UTC | #3
Hi Chris,

On Wed, Oct 18, 2017 at 5:10 PM, Chris Brandt <Chris.Brandt@renesas.com> wrote:
> On Wednesday, October 18, 2017 1, Geert Uytterhoeven wrote:
>> > +       /*
>> > +        * Remove clock ticks for rise and fall times. Convert ns to
>> clock
>> > +        * ticks.
>> > +        */
>> > +       brl -= t->scl_fall_ns / (1000000000/rate);
>> > +       brh -= t->scl_rise_ns / (1000000000/rate);
>>
>> To avoid losing too much precision by the division, you can rewrite this
>> as
>> e.g.
>>
>>     brl -= t->scl_fall_ns * rate / 1000000000;
>>
>> ("scl_fall_ns * rate" should not overflow).
>> Perhaps DIV_ROUND_UP(), too?
>
> Unfortunately, I do not get the correct number every time.
>
> Sometimes I get the correct number, but most of the time I get 0.
>
> Note, if rate=33,330,000 and t->scl_fall_ns=300, then
>
> t->scl_fall_ns * rate  = 9,999,000,000 (0x253FCA1C0) a 34-bit number.
>
> So, I'd rather just stick with the less precision because it's not that
> much of a bit deal.

Oops, so the numbers are bigger than I had expected, naively.

Alternatively, you can use a 64-by-32 division, e.g.

    DIV_ROUND_UP_ULL((u64)t->scl_fall_ns * rate, 1000000000)

Gr{oetje,eeting}s,

                        Geert

--
Geert Uytterhoeven -- There's lots of Linux beyond ia32 -- geert@linux-m68k.org

In personal conversations with technical people, I call myself a hacker. But
when I'm talking to journalists I just say "programmer" or something like that.
                                -- Linus Torvalds
Chris Brandt Oct. 18, 2017, 3:47 p.m. UTC | #4
Hi Geert,

On Wednesday, October 18, 2017 1, Geert Uytterhoeven wrote:
> >> > +       /*

> >> > +        * Remove clock ticks for rise and fall times. Convert ns to

> >> clock

> >> > +        * ticks.

> >> > +        */

> >> > +       brl -= t->scl_fall_ns / (1000000000/rate);

> >> > +       brh -= t->scl_rise_ns / (1000000000/rate);

> >>

> >> To avoid losing too much precision by the division, you can rewrite

> this

> >> as

> >> e.g.

> >>

> >>     brl -= t->scl_fall_ns * rate / 1000000000;

> >>

> >> ("scl_fall_ns * rate" should not overflow).

> >> Perhaps DIV_ROUND_UP(), too?

> >

> > Unfortunately, I do not get the correct number every time.

> >

> > Sometimes I get the correct number, but most of the time I get 0.

> >

> > Note, if rate=33,330,000 and t->scl_fall_ns=300, then

> >

> > t->scl_fall_ns * rate  = 9,999,000,000 (0x253FCA1C0) a 34-bit number.

> >

> > So, I'd rather just stick with the less precision because it's not that

> > much of a bit deal.

> 

> Oops, so the numbers are bigger than I had expected, naively.

> 

> Alternatively, you can use a 64-by-32 division, e.g.

> 

>     DIV_ROUND_UP_ULL((u64)t->scl_fall_ns * rate, 1000000000)


That works better.

However, rounding up on this calculation means you may run faster than 
Specified (ie, you spend more time HIGH or LOW)


As a raw math vs code comparison:

t->scl_fall_ns = 300
rate = 16662500    (33325000/2)
REAL number = 4.99875
DIV_ROUND_UP_ULL produces = 5

t->scl_fall_ns = 250
rate = 16662500    (33325000/2)
REAL number = 4.165625
DIV_ROUND_UP_ULL produces = 5 (now I'm running higher faster than 400kHz)


So on this number, rounding down (truncating) is 'safer' from a speed 
stand point.

What do you think?

Chris
Wolfram Sang Oct. 18, 2017, 4:37 p.m. UTC | #5
> +	brl -= t->scl_fall_ns / (1000000000/rate);
> +	brh -= t->scl_rise_ns / (1000000000/rate);

However you solve the precision issue, please take care about the "space
around operators" rule afterwards. checkpatch should help you there.
Chris Brandt Oct. 18, 2017, 4:41 p.m. UTC | #6
On Wednesday, October 18, 2017 1, Wolfram Sang wrote:
> > +	brl -= t->scl_fall_ns / (1000000000/rate);
> > +	brh -= t->scl_rise_ns / (1000000000/rate);
> 
> However you solve the precision issue, please take care about the "space
> around operators" rule afterwards. checkpatch should help you there.

OK.

I ran checkpatch and fixed everything it found, but it didn't flag that one.


$ scripts/checkpatch.pl z_patches/riic/S4V1/*
total: 0 errors, 0 warnings, 169 lines checked

z_patches/riic/S4V1/0001-i2c-riic-remove-clock-and-frequency-restrictions.patch has no obvious style problems and is ready for submission.


Chris
Wolfram Sang Oct. 18, 2017, 4:44 p.m. UTC | #7
On Wed, Oct 18, 2017 at 04:41:57PM +0000, Chris Brandt wrote:
> On Wednesday, October 18, 2017 1, Wolfram Sang wrote:
> > > +	brl -= t->scl_fall_ns / (1000000000/rate);
> > > +	brh -= t->scl_rise_ns / (1000000000/rate);
> > 
> > However you solve the precision issue, please take care about the "space
> > around operators" rule afterwards. checkpatch should help you there.
> 
> OK.
> 
> I ran checkpatch and fixed everything it found, but it didn't flag that one.
> 
> 
> $ scripts/checkpatch.pl z_patches/riic/S4V1/*
> total: 0 errors, 0 warnings, 169 lines checked

I see. Yes, I recall that it missed it once for me, too.
Wolfram Sang Oct. 26, 2017, 8:36 p.m. UTC | #8
> I ran checkpatch and fixed everything it found, but it didn't flag that one.
> 
> 
> $ scripts/checkpatch.pl z_patches/riic/S4V1/*
> total: 0 errors, 0 warnings, 169 lines checked

with "--strict" it will find it.
Chris Brandt Oct. 27, 2017, 3:37 p.m. UTC | #9
On Thursday, October 26, 2017, linux-renesas-soc-owner@vger.kernel.org wrote:
> > I ran checkpatch and fixed everything it found, but it didn't flag that
> one.
> >
> >
> > $ scripts/checkpatch.pl z_patches/riic/S4V1/*
> > total: 0 errors, 0 warnings, 169 lines checked
> 
> with "--strict" it will find it.

Yes, then it finds other ones too that you missed.

So for my v2 submission, it passes with --strict.


Chris
diff mbox

Patch

diff --git a/drivers/i2c/busses/i2c-riic.c b/drivers/i2c/busses/i2c-riic.c
index c811af4c8d81..97fe204c812d 100644
--- a/drivers/i2c/busses/i2c-riic.c
+++ b/drivers/i2c/busses/i2c-riic.c
@@ -84,12 +84,7 @@ 
 
 #define ICSR2_NACKF	0x10
 
-/* ICBRx (@ PCLK 33MHz) */
 #define ICBR_RESERVED	0xe0 /* Should be 1 on writes */
-#define ICBRL_SP100K	(19 | ICBR_RESERVED)
-#define ICBRH_SP100K	(16 | ICBR_RESERVED)
-#define ICBRL_SP400K	(21 | ICBR_RESERVED)
-#define ICBRH_SP400K	(9 | ICBR_RESERVED)
 
 #define RIIC_INIT_MSG	-1
 
@@ -288,48 +283,101 @@  static const struct i2c_algorithm riic_algo = {
 	.functionality	= riic_func,
 };
 
-static int riic_init_hw(struct riic_dev *riic, u32 spd)
+static int riic_init_hw(struct riic_dev *riic, struct i2c_timings *t)
 {
 	int ret;
 	unsigned long rate;
+	int total_ticks, cks, brl, brh;
 
 	ret = clk_prepare_enable(riic->clk);
 	if (ret)
 		return ret;
 
+	if (t->bus_freq_hz > 400000) {
+		dev_err(&riic->adapter.dev,
+			"unsupported bus speed (%dHz). 400000 max\n",
+			t->bus_freq_hz);
+		clk_disable_unprepare(riic->clk);
+		return -EINVAL;
+	}
+
+	rate = clk_get_rate(riic->clk);
+
 	/*
-	 * TODO: Implement formula to calculate the timing values depending on
-	 * variable parent clock rate and arbitrary bus speed
+	 * Assume the default register settings:
+	 *  FER.SCLE = 1 (SCL sync circuit enabled, adds 2 or 3 cycles)
+	 *  FER.NFE = 1 (noise circuit enabled)
+	 *  MR3.NF = 0 (1 cycle of noise filtered out)
+	 *
+	 * Freq (CKS=000) = (I2CCLK + tr + tf)/ (BRH + 3 + 1) + (BRL + 3 + 1)
+	 * Freq (CKS!=000) = (I2CCLK + tr + tf)/ (BRH + 2 + 1) + (BRL + 2 + 1)
 	 */
-	rate = clk_get_rate(riic->clk);
-	if (rate != 33325000) {
-		dev_err(&riic->adapter.dev,
-			"invalid parent clk (%lu). Must be 33325000Hz\n", rate);
+
+	/*
+	 * Determine reference clock rate. We must be able to get the desired
+	 * frequency with only 62 clock ticks max (31 high, 31 low).
+	 * Aim for a duty of 60% LOW, 40% HIGH.
+	 */
+	total_ticks = rate / t->bus_freq_hz;
+	if (rate % t->bus_freq_hz)		/* round up */
+		total_ticks++;
+
+	for (cks = 0; cks < 7; cks++) {
+		/*
+		 * 60% low time must be less than BRL + 2 + 1
+		 * BRL max register value is 0x1F.
+		 */
+		brl = ((total_ticks * 6) / 10);
+		if (brl <= (0x1F + 3))
+			break;
+
+		total_ticks /= 2;
+		rate /= 2;
+	}
+
+	if (brl > (0x1F + 3)) {
+		dev_err(&riic->adapter.dev, "invalid speed (%lu). Too slow.\n",
+			(unsigned long)t->bus_freq_hz);
 		clk_disable_unprepare(riic->clk);
 		return -EINVAL;
 	}
 
+	brh = total_ticks - brl;
+
+	/* Remove automatic clock ticks for sync circuit and NF */
+	if (cks == 0) {
+		brl -= 4;
+		brh -= 4;
+	} else {
+		brl -= 3;
+		brh -= 3;
+	}
+
+	/*
+	 * Remove clock ticks for rise and fall times. Convert ns to clock
+	 * ticks.
+	 */
+	brl -= t->scl_fall_ns / (1000000000/rate);
+	brh -= t->scl_rise_ns / (1000000000/rate);
+
+	/* adjust for min register values for when SCLE=1 and NFE=1 */
+	if (brl < 1)
+		brl = 1;
+	if (brh < 1)
+		brh = 1;
+
+	pr_debug("i2c-riic: freq=%lu, duty=%d, fall=%lu, rise=%lu, cks=%d, brl=%d, brh=%d\n",
+		 rate/total_ticks, ((brl+3)*100)/(brl+brh+6),
+		 t->scl_fall_ns / (1000000000/rate),
+		 t->scl_rise_ns / (1000000000/rate), cks, brl, brh);
+
 	/* Changing the order of accessing IICRST and ICE may break things! */
 	writeb(ICCR1_IICRST | ICCR1_SOWP, riic->base + RIIC_ICCR1);
 	riic_clear_set_bit(riic, 0, ICCR1_ICE, RIIC_ICCR1);
 
-	switch (spd) {
-	case 100000:
-		writeb(ICMR1_CKS(3), riic->base + RIIC_ICMR1);
-		writeb(ICBRH_SP100K, riic->base + RIIC_ICBRH);
-		writeb(ICBRL_SP100K, riic->base + RIIC_ICBRL);
-		break;
-	case 400000:
-		writeb(ICMR1_CKS(1), riic->base + RIIC_ICMR1);
-		writeb(ICBRH_SP400K, riic->base + RIIC_ICBRH);
-		writeb(ICBRL_SP400K, riic->base + RIIC_ICBRL);
-		break;
-	default:
-		dev_err(&riic->adapter.dev,
-			"unsupported bus speed (%dHz). Use 100000 or 400000\n", spd);
-		clk_disable_unprepare(riic->clk);
-		return -EINVAL;
-	}
+	writeb(ICMR1_CKS(cks), riic->base + RIIC_ICMR1);
+	writeb(brh | ICBR_RESERVED, riic->base + RIIC_ICBRH);
+	writeb(brl | ICBR_RESERVED, riic->base + RIIC_ICBRL);
 
 	writeb(0, riic->base + RIIC_ICSER);
 	writeb(ICMR3_ACKWP | ICMR3_RDRFS, riic->base + RIIC_ICMR3);
@@ -351,11 +399,10 @@  static struct riic_irq_desc riic_irqs[] = {
 
 static int riic_i2c_probe(struct platform_device *pdev)
 {
-	struct device_node *np = pdev->dev.of_node;
 	struct riic_dev *riic;
 	struct i2c_adapter *adap;
 	struct resource *res;
-	u32 bus_rate = 0;
+	struct i2c_timings i2c_t;
 	int i, ret;
 
 	riic = devm_kzalloc(&pdev->dev, sizeof(*riic), GFP_KERNEL);
@@ -396,8 +443,9 @@  static int riic_i2c_probe(struct platform_device *pdev)
 
 	init_completion(&riic->msg_done);
 
-	of_property_read_u32(np, "clock-frequency", &bus_rate);
-	ret = riic_init_hw(riic, bus_rate);
+	i2c_parse_fw_timings(&pdev->dev, &i2c_t, true);
+
+	ret = riic_init_hw(riic, &i2c_t);
 	if (ret)
 		return ret;
 
@@ -408,7 +456,8 @@  static int riic_i2c_probe(struct platform_device *pdev)
 
 	platform_set_drvdata(pdev, riic);
 
-	dev_info(&pdev->dev, "registered with %dHz bus speed\n", bus_rate);
+	dev_info(&pdev->dev, "registered with %dHz bus speed\n",
+		 i2c_t.bus_freq_hz);
 	return 0;
 }