* Re: [PATCH v3 2/4] i2c: rk3x: add ops to caculate i2c clocks
[not found] ` <CAHp75VdR01_suo3nTWuuT2OEusy6z6KGSg29LKD8T3kY9eGMPA@mail.gmail.com>
@ 2016-01-15 8:34 ` David.Wu
0 siblings, 0 replies; 2+ messages in thread
From: David.Wu @ 2016-01-15 8:34 UTC (permalink / raw)
To: Andy Shevchenko, David Wu
Cc: Heiko Stübner, Wolfram Sang, Douglas Anderson, Tao Huang,
Chris Zhong, cf, Jianqun Xu, Lin Huang, linux-arm Mailing List,
linux-rockchip, linux-i2c, linux-kernel@vger.kernel.org
Hi Andy,
在 2016/1/14 21:19, Andy Shevchenko 写道:
> On Thu, Jan 14, 2016 at 2:31 PM, David Wu <david.wu@rock-chips.com> wrote:
>> From: David Wu <wdc@rock-chips.com>
>>
>> I2c Controller of rk3x is updated for the rules to caculate clocks.
>> So it need a new method to caculate i2c clock timing information
>> for new version. The current method is defined as v0, and new is
>> v1, next maybe v2......
> Thanks for an update. My comments below.
>
>> --- a/drivers/i2c/busses/i2c-rk3x.c
>> +++ b/drivers/i2c/busses/i2c-rk3x.c
>> @@ -90,10 +95,27 @@ struct rk3x_i2c_soc_data {
>> int grf_offset;
>> };
>>
>> +/**
>> + * struct rk3x_priv_i2c_timings - rk3x I2C timing information
>> + * @div_low: Divider output for low
>> + * @div_high: Divider output for high
>> + */
>> +struct rk3x_priv_i2c_timings {
>> + unsigned long div_low;
>> + unsigned long div_high;
>> +};
>> +
>> +struct rk3x_i2c_ops {
>> + int (*calc_clock)(unsigned long,
>> + struct i2c_timings *,
>> + struct rk3x_priv_i2c_timings *);
>> +};
>> +
>> struct rk3x_i2c {
>> struct i2c_adapter adap;
>> struct device *dev;
>> struct rk3x_i2c_soc_data *soc_data;
>> + struct rk3x_i2c_ops ops;
> Not much sense for now to have a struct for one member.
Yes, it looks ok not to do change here.
>> /* Hardware resources */
>> void __iomem *regs;
>> @@ -102,6 +124,7 @@ struct rk3x_i2c {
>>
> What about
> /* I2C timing settings */
> struct i2c_timings t;
>
> /* Divider settings */
> int (*calc_divs)(unsigned long,
> struct i2c_timings *,
> unsigned long *div_low,
> unsigned long *div_high);
> unsigned long div_low;
> unsigned long div_high;
> ?
>
> As far as I understand you still calculate divider values.
>
>> /* Synchronization & notification */
>> spinlock_t lock;
>> @@ -431,21 +454,20 @@ out:
>> }
>>
>> /**
>> - * Calculate divider values for desired SCL frequency
>> + * Calculate timing clock info values for desired SCL frequency
>> *
>> * @clk_rate: I2C input clock rate
>> - * @t_input: Known I2C timing information.
>> - * @div_low: Divider output for low
>> - * @div_high: Divider output for high
> For now seems better to leave this prototype.
>
>> + * @t_input: Known I2C timing information
>> + * @t_output: Caculated rk3x private timing information that would
>> + * be written into regs
>> *
>> * Returns: 0 on success, -EINVAL if the goal SCL rate is too slow. In that case
>> * a best-effort divider value is returned in divs. If the target rate is
>> * too high, we silently use the highest possible rate.
>> */
>> -static int rk3x_i2c_calc_divs(unsigned long clk_rate,
>> - struct i2c_timings *t_input,
>> - unsigned long *div_low,
>> - unsigned long *div_high)
>> +static int rk3x_i2c_v0_calc_clock(unsigned long clk_rate,
>> + struct i2c_timings *t_input,
>> + struct rk3x_priv_i2c_timings *t_output)
>> {
>> unsigned long spec_min_low_ns, spec_min_high_ns;
>> unsigned long spec_setup_start, spec_max_data_hold_ns;
>
>> @@ -583,25 +605,25 @@ static int rk3x_i2c_calc_divs(unsigned long clk_rate,
>>
>> /* Give low the "ideal" and give high whatever extra is left */
>> extra_low_div = ideal_low_div - min_low_div;
>> - *div_low = ideal_low_div;
>> - *div_high = min_high_div + (extra_div - extra_low_div);
>> + t_output->div_low = ideal_low_div;
>> + t_output->div_high = min_high_div + (extra_div - extra_low_div);
>> }
>>
>> /*
>> * Adjust to the fact that the hardware has an implicit "+1".
>> * NOTE: Above calculations always produce div_low > 0 and div_high > 0.
>> */
>> - *div_low = *div_low - 1;
>> - *div_high = *div_high - 1;
>> + t_output->div_low = t_output->div_low - 1;
> -= 1
>
>> + t_output->div_high = t_output->div_high - 1;
> Ditto.
>
> But without change of prototype those no needed.
>
>> /* Maximum divider supported by hw is 0xffff */
>> - if (*div_low > 0xffff) {
>> - *div_low = 0xffff;
>> + if (t_output->div_low > 0xffff) {
>> + t_output->div_low = 0xffff;
>> ret = -EINVAL;
>> }
>>
>> - if (*div_high > 0xffff) {
>> - *div_high = 0xffff;
>> + if (t_output->div_high > 0xffff) {
>> + t_output->div_high = 0xffff;
>> ret = -EINVAL;
>> }
>>
>> @@ -610,19 +632,21 @@ static int rk3x_i2c_calc_divs(unsigned long clk_rate,
>>
>> static void rk3x_i2c_adapt_div(struct rk3x_i2c *i2c, unsigned long clk_rate)
>> {
>> - unsigned long div_low, div_high;
>> u64 t_low_ns, t_high_ns;
>> int ret;
>>
>> - ret = rk3x_i2c_calc_divs(clk_rate, &i2c->t, &div_low, &div_high);
>> + ret = i2c->ops.calc_clock(clk_rate, &i2c->t, &i2c->t_priv);
>> WARN_ONCE(ret != 0, "Could not reach SCL freq %u", i2c->t.bus_freq_hz);
>>
>> clk_enable(i2c->clk);
>> - i2c_writel(i2c, (div_high << 16) | (div_low & 0xffff), REG_CLKDIV);
>> + i2c_writel(i2c, (i2c->t_priv.div_high << 16) |
>> + (i2c->t_priv.div_low & 0xffff), REG_CLKDIV);
>> clk_disable(i2c->clk);
>>
>> - t_low_ns = div_u64(((u64)div_low + 1) * 8 * 1000000000, clk_rate);
>> - t_high_ns = div_u64(((u64)div_high + 1) * 8 * 1000000000, clk_rate);
>> + t_low_ns = div_u64(((u64)i2c->t_priv.div_low + 1) * 8 * 1000000000,
>> + clk_rate);
>> + t_high_ns = div_u64(((u64)i2c->t_priv.div_high + 1) * 8 * 1000000000,
>> + clk_rate);
>> dev_dbg(i2c->dev,
>> "CLK %lukhz, Req %uns, Act low %lluns high %lluns\n",
>> clk_rate / 1000,
>> @@ -652,12 +676,11 @@ static int rk3x_i2c_clk_notifier_cb(struct notifier_block *nb, unsigned long
>> {
>> struct clk_notifier_data *ndata = data;
>> struct rk3x_i2c *i2c = container_of(nb, struct rk3x_i2c, clk_rate_nb);
>> - unsigned long div_low, div_high;
>>
>> switch (event) {
>> case PRE_RATE_CHANGE:
>> - if (rk3x_i2c_calc_divs(ndata->new_rate, &i2c->t,
>> - &div_low, &div_high) != 0)
>> + if (i2c->ops.calc_clock(ndata->new_rate, &i2c->t,
>> + &i2c->t_priv) != 0)
>> return NOTIFY_STOP;
>>
>> /* scale up */
>> @@ -861,6 +884,7 @@ static int rk3x_i2c_probe(struct platform_device *pdev)
>> u32 value;
>> int irq;
>> unsigned long clk_rate;
>> + unsigned int version;
> No need to have a separate variable…
>
>> + version = (readl(i2c->regs + REG_CON) & VERSION_MASK) >> VERSION_SHIFT;
>> + if (version == RK3X_I2C_V0)
>> + i2c->ops.calc_clock = rk3x_i2c_v0_calc_clock;
> value = readl(i2c->regs + REG_CON);
> if ((value & VERSION_MASK) >> VERSION_SHIFT) == RK3X_I2C_V0)
> i2c->ops.calc_clock = rk3x_i2c_v0_calc_clock;
>
^ permalink raw reply [flat|nested] 2+ messages in thread
* Re: [PATCH v3 3/4] i2c: rk3x: new method to calculate i2c clocks
[not found] ` <CAD=FV=UXMzEQiKcMa7VjDx+arcbRo1e-0HPHBnG8GP7k6uFt1g@mail.gmail.com>
@ 2016-01-15 9:39 ` David.Wu
0 siblings, 0 replies; 2+ messages in thread
From: David.Wu @ 2016-01-15 9:39 UTC (permalink / raw)
To: Doug Anderson, David Wu
Cc: Heiko Stübner, Wolfram Sang, andy.shevchenko, Tao Huang,
Chris, Eddie Cai, Jianqun Xu, Lin Huang,
linux-arm-kernel@lists.infradead.org,
open list:ARM/Rockchip SoC..., linux-i2c,
linux-kernel@vger.kernel.org
Hi Doug,
在 2016/1/15 0:12, Doug Anderson 写道:
> Hi,
>
> On Thu, Jan 14, 2016 at 4:31 AM, David Wu <david.wu@rock-chips.com> wrote:
>> There was an timing issue about "repeated start" time at the I2C
>> controller of version0, controller appears to drop SDA at .875x (7/8)
>> programmed clk high. The rule(.875x) isn't enough to meet tSU;STA
>> requirements on 100k's Standard-mode. To resolve this issue,
>> data_upd_st, start_setup_cnt and stop_setup_cnt configs for I2C
>> timing information are added, new rules are designed to calculate
>> the timing information at new v1.
> I'm curious: does new hardware behave differently and that's why you
> need v1?
Yes , i didn't describe clearly about difference between old and new.
Old and new hardware behave the same except some timing rules.
v0 is the same with the v1 for tHigh and tLow :
tHigh = 8 * divh * pclk_cycle;
tLow = 8 * divl * pclk_cycle;
v0 rules' difference:
start setup: 7/8 * tHigh + 1 pclk cycle
start hold : 2 * (7/8 * tHigh) - 1 pclk cycle
stop setup : 7/8 * tHigh + 1 pclk cycle
data setup: 1/2 tLow - 1 pclk cycle
data hold : 1/2 tLow + 1 pclk cycle
For 100k's example:
spec_min_low_ns = 4700;
spec_min_high_ns = 4000;
spec_min_setup_start_ns = 4700;
We could calculate the timing info by the rules of v0, and ignore effect
of the pclk cycle here.
tSU;sta >=4700;
tHigh_min = tSU;sta * 8/7 >= 5372ns;
tHigh_min = max(tHigh_min, spec_min_high_ns) = 5372ns;
We get the final scl clk rate is 99k(1000000000 / (5372ns + 4700ns)),
it looks ok for the 100k
Standard mode. But the timing point of repeat start and low time is very
critical, it is dangerous
to some slave devices which are perhaps not so specified.
So need to give some time margin for them, that would increase the cycle
time and reduce
the clk rate, the final rate we get may be 97k or 96k.
In fact, we must think about scl rise time and scl fall time, and get
final clk may be 93k or less.
After that, we get about 7 percent lost at clk rate, it would reduce the
efficiency of i2c transfer.
In other words, we could say there is a timing issue about "repeat
start" time when we want
accurate 100k's rate, it is short to meet I2C spec requirements.
3.4M clk rate also has the same issue.
The start_setup count is added to fix this issue, tHigh is not need to
considered of "repeat start" time.
After divs calculated, the count would be calculated to meet i2c spec.
v1 rule:
start setup: tHigh + 1 pclk cycle
start hold: [8h * (u + 1) - 1] * T;
tSU;sto = (8h * p + 1) * T;
---------------------------------------------------------------------------------------------------------------
The data_upd_st is added for the Data set-up time and Data hold time on
Highspeed mode.
For 1.7M's example on v0:
tHD;DAT = 1/2 tLow;
tHD;DAT <= spec_max_data_hold_ns = 150ns
tLow <= 2 * tHD;DAT <= 300ns;
tLow >= spec_min_low_ns = 320ns;
According to these, we could not get a value for tLow, need changes for
the rule: tHD;DAT = 1/2 tLow.
Cut the tLow into eight euqal parts,the range of data_upd_st is 1 ~ 7.
It seems that v1 rule could resolve the issue.
v1 rule:
tHD;sda = n/8 * tLow;
tSU;sda = [(8 - n)/8 * tLow;
3.4M clk rate also has the same issue.
> ...or does old and new hardware behave the same and you're
> just introducing v1 so you don't mess with how old boards are working?
The registers of counts added would not effect old boards.
No matter what values of count was written in regs, that old i2c
controller didn't care,
it worked by original timing rule.
After picking this patch, pmic-rk818 and touchscreen-gt911 worked well
on the rk3368 sdk
board which use old method.
>
> >From the description it sounds as if the old code had problems as 100k too...
>
> If the new controller is different, I'd probably reword like the
> following (you'd have to re-wordwrap):
>
> There was an timing issue about "repeated start" time at the I2C
> controller of version0, controller appears to drop SDA at .875x (7/8)
> programmed clk high. On version 1 of the controller, the rule(.875x)
> isn't enough to meet tSU;STA
> requirements on 100k's Standard-mode. To resolve this issue,
> data_upd_st, start_setup_cnt and stop_setup_cnt configs for I2C
> timing information are added, new rules are designed to calculate
> the timing information at new v1.
There was an timing issue about "repeated start" time at the I2C
controller of version0, controller appears to drop SDA at .875x (7/8)
programmed clk high. On version 1 of the controller, the rule(.875x)
isn't enough to meet tSU;STA requirements on 100k's Standard-mode.
To resolve this issue,data_upd_st, start_setup_cnt and stop_setup_cnt
configs for I2C timing information are added, new rules are designed
to calculate the timing information at new v1.
> -Doug
>
>
>
^ permalink raw reply [flat|nested] 2+ messages in thread
end of thread, other threads:[~2016-01-15 9:39 UTC | newest]
Thread overview: 2+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
[not found] <1452774699-57455-1-git-send-email-david.wu@rock-chips.com>
[not found] ` <1452774699-57455-3-git-send-email-david.wu@rock-chips.com>
[not found] ` <CAHp75VdR01_suo3nTWuuT2OEusy6z6KGSg29LKD8T3kY9eGMPA@mail.gmail.com>
2016-01-15 8:34 ` [PATCH v3 2/4] i2c: rk3x: add ops to caculate i2c clocks David.Wu
[not found] ` <1452774699-57455-4-git-send-email-david.wu@rock-chips.com>
[not found] ` <CAD=FV=UXMzEQiKcMa7VjDx+arcbRo1e-0HPHBnG8GP7k6uFt1g@mail.gmail.com>
2016-01-15 9:39 ` [PATCH v3 3/4] i2c: rk3x: new method to calculate " David.Wu
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).