diff mbox

[6/6] tty: serial: lpuart: add a more accurate baud rate calculation method

Message ID 1494316248-24052-7-git-send-email-aisheng.dong@nxp.com
State New
Headers show

Commit Message

Dong Aisheng May 9, 2017, 7:50 a.m. UTC
On new LPUART versions, the oversampling ratio for the receiver can be
changed from 4x (00011) to 32x (11111) which could help us get a more
accurate baud rate divider.

The idea is to use the best OSR (over-sampling rate) possible.
Note, OSR is typically hard-set to 16 in other LPUART instantiations.
Loop to find the best OSR value possible, one that generates minimum
baud diff iterate through the rest of the supported values of OSR.

Currently only i.MX7ULP is using it.

Cc: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Cc: Jiri Slaby <jslaby@suse.com>
Cc: Fugang Duan <fugang.duan@nxp.com>
Cc: Stefan Agner <stefan@agner.ch>
Cc: Mingkai Hu <Mingkai.Hu@nxp.com>
Cc: Yangbo Lu <yangbo.lu@nxp.com>
Signed-off-by: Dong Aisheng <aisheng.dong@nxp.com>
---
 drivers/tty/serial/fsl_lpuart.c | 85 ++++++++++++++++++++++++++++++++++++++---
 1 file changed, 79 insertions(+), 6 deletions(-)

Comments

Andy Shevchenko May 28, 2017, 12:04 a.m. UTC | #1
On Tue, May 9, 2017 at 10:50 AM, Dong Aisheng <aisheng.dong@nxp.com> wrote:
> On new LPUART versions, the oversampling ratio for the receiver can be
> changed from 4x (00011) to 32x (11111) which could help us get a more
> accurate baud rate divider.
>
> The idea is to use the best OSR (over-sampling rate) possible.
> Note, OSR is typically hard-set to 16 in other LPUART instantiations.
> Loop to find the best OSR value possible, one that generates minimum
> baud diff iterate through the rest of the supported values of OSR.

> +lpuart32_serial_setbrg(struct lpuart_port *sport, unsigned int baudrate)
> +{
> +       u32 sbr, osr, baud_diff, tmp_osr, tmp_sbr, tmp_diff, tmp;
> +       u32 clk = sport->port.uartclk;
> +
> +       /*
> +        * The idea is to use the best OSR (over-sampling rate) possible.
> +        * Note, OSR is typically hard-set to 16 in other LPUART instantiations.
> +        * Loop to find the best OSR value possible, one that generates minimum
> +        * baud_diff iterate through the rest of the supported values of OSR.
> +        *
> +        * Calculation Formula:
> +        *  Baud Rate = baud clock / ((OSR+1) × SBR)
> +        */
> +       baud_diff = baudrate;
> +       osr = 0;
> +       sbr = 0;
> +

> +       for (tmp_osr = 4; tmp_osr <= 32; tmp_osr++) {

I _think_ you may simplify this and avoid for-loop if you reconsider approach.

> +               /* calculate the temporary sbr value  */
> +               tmp_sbr = (clk / (baudrate * tmp_osr));
> +               if (tmp_sbr == 0)
> +                       tmp_sbr = 1;
> +
> +               /*
> +                * calculate the baud rate difference based on the temporary
> +                * osr and sbr values
> +                */

> +               tmp_diff = clk / (tmp_osr * tmp_sbr) - baudrate;

(32 - 4 + 1) times division is called...

> +
> +               /* select best values between sbr and sbr+1 */
> +               tmp = clk / (tmp_osr * (tmp_sbr + 1));
> +               if (tmp_diff > (baudrate - tmp)) {
> +                       tmp_diff = baudrate - tmp;
> +                       tmp_sbr++;
> +               }
> +
> +               if (tmp_diff <= baud_diff) {
> +                       baud_diff = tmp_diff;
> +                       osr = tmp_osr;
> +                       sbr = tmp_sbr;
> +               }
> +       }

> +       /* handle buadrate outside acceptable rate */
> +       if (baud_diff > ((baudrate / 100) * 3))
> +               dev_warn(sport->port.dev,
> +                        "unacceptable baud rate difference of more than 3%%\n");

Shouldn't you fall back to previous setting?

> +
> +       tmp = lpuart32_read(sport->port.membase + UARTBAUD);
> +

> +       if ((osr > 3) && (osr < 8))

Isn't it

if (osr ^ BIT(2) < BIT(2))

?

> +               tmp |= UARTBAUD_BOTHEDGE;

> +}

> +       if (of_device_is_compatible(port->dev->of_node, "fsl,imx7ulp-lpuart")) {
> +               lpuart32_serial_setbrg(sport, baud);

> +       } else {
> +               sbr = sport->port.uartclk / (16 * baud);
> +               bd &= ~UARTBAUD_SBR_MASK;
> +               bd |= sbr & UARTBAUD_SBR_MASK;
> +               bd |= UARTBAUD_BOTHEDGE;
> +               bd &= ~(UARTBAUD_TDMAE | UARTBAUD_RDMAE);
> +               lpuart32_write(bd, sport->port.membase + UARTBAUD);
> +       }

Perhaps it makes sense to split this to a helper function as well (in
a separate patch).
Dong Aisheng May 31, 2017, 2:18 p.m. UTC | #2
Hi Andy,

> -----Original Message-----

> From: Andy Shevchenko [mailto:andy.shevchenko@gmail.com]

> Sent: Sunday, May 28, 2017 8:04 AM

> To: A.S. Dong

> Cc: linux-serial@vger.kernel.org; linux-kernel@vger.kernel.org; linux-arm

> Mailing List; Greg Kroah-Hartman; Jiri Slaby; Andy Duan; Stefan Agner;

> Mingkai Hu; Y.B. Lu

> Subject: Re: [PATCH 6/6] tty: serial: lpuart: add a more accurate baud

> rate calculation method

> 

> On Tue, May 9, 2017 at 10:50 AM, Dong Aisheng <aisheng.dong@nxp.com> wrote:

> > On new LPUART versions, the oversampling ratio for the receiver can be

> > changed from 4x (00011) to 32x (11111) which could help us get a more

> > accurate baud rate divider.

> >

> > The idea is to use the best OSR (over-sampling rate) possible.

> > Note, OSR is typically hard-set to 16 in other LPUART instantiations.

> > Loop to find the best OSR value possible, one that generates minimum

> > baud diff iterate through the rest of the supported values of OSR.

> 

> > +lpuart32_serial_setbrg(struct lpuart_port *sport, unsigned int

> > +baudrate) {

> > +       u32 sbr, osr, baud_diff, tmp_osr, tmp_sbr, tmp_diff, tmp;

> > +       u32 clk = sport->port.uartclk;

> > +

> > +       /*

> > +        * The idea is to use the best OSR (over-sampling rate) possible.

> > +        * Note, OSR is typically hard-set to 16 in other LPUART

> instantiations.

> > +        * Loop to find the best OSR value possible, one that generates

> minimum

> > +        * baud_diff iterate through the rest of the supported values of

> OSR.

> > +        *

> > +        * Calculation Formula:

> > +        *  Baud Rate = baud clock / ((OSR+1) × SBR)

> > +        */

> > +       baud_diff = baudrate;

> > +       osr = 0;

> > +       sbr = 0;

> > +

> 

> > +       for (tmp_osr = 4; tmp_osr <= 32; tmp_osr++) {

> 

> I _think_ you may simplify this and avoid for-loop if you reconsider

> approach.

> 


The algorithm is that we have to iterate all possible OSCs and find
the one with minimum baud_diff.

I'm not sure what alternative approach did you mean?

But there is indeed a optimization way, see below.

> > +               /* calculate the temporary sbr value  */

> > +               tmp_sbr = (clk / (baudrate * tmp_osr));

> > +               if (tmp_sbr == 0)

> > +                       tmp_sbr = 1;

> > +

> > +               /*

> > +                * calculate the baud rate difference based on the

> temporary

> > +                * osr and sbr values

> > +                */

> 

> > +               tmp_diff = clk / (tmp_osr * tmp_sbr) - baudrate;

> 

> (32 - 4 + 1) times division is called...

> 


Yes.

> > +

> > +               /* select best values between sbr and sbr+1 */

> > +               tmp = clk / (tmp_osr * (tmp_sbr + 1));

> > +               if (tmp_diff > (baudrate - tmp)) {

> > +                       tmp_diff = baudrate - tmp;

> > +                       tmp_sbr++;

> > +               }

> > +

> > +               if (tmp_diff <= baud_diff) {

> > +                       baud_diff = tmp_diff;

> > +                       osr = tmp_osr;

> > +                       sbr = tmp_sbr;


To optimize the looping, we probably could do:
If (!daud_diff)
	Break;

> > +               }

> > +       }

> 

> > +       /* handle buadrate outside acceptable rate */

> > +       if (baud_diff > ((baudrate / 100) * 3))

> > +               dev_warn(sport->port.dev,

> > +                        "unacceptable baud rate difference of more

> > + than 3%%\n");

> 

> Shouldn't you fall back to previous setting?

> 


Hmmm.. Is there defined semantic to do that or is there any other ones
doing that way?

I see most drivers not doing that.

> > +

> > +       tmp = lpuart32_read(sport->port.membase + UARTBAUD);

> > +

> 

> > +       if ((osr > 3) && (osr < 8))

> 

> Isn't it

> 

> if (osr ^ BIT(2) < BIT(2))

> 

> ?

> 


That is obvious hard to understand and I'd rather keep a more explicit way.

> > +               tmp |= UARTBAUD_BOTHEDGE;

> 

> > +}

> 

> > +       if (of_device_is_compatible(port->dev->of_node, "fsl,imx7ulp-

> lpuart")) {

> > +               lpuart32_serial_setbrg(sport, baud);

> 

> > +       } else {

> > +               sbr = sport->port.uartclk / (16 * baud);

> > +               bd &= ~UARTBAUD_SBR_MASK;

> > +               bd |= sbr & UARTBAUD_SBR_MASK;

> > +               bd |= UARTBAUD_BOTHEDGE;

> > +               bd &= ~(UARTBAUD_TDMAE | UARTBAUD_RDMAE);

> > +               lpuart32_write(bd, sport->port.membase + UARTBAUD);

> > +       }

> 

> Perhaps it makes sense to split this to a helper function as well (in a

> separate patch).

> 


That will be removed according to Stefan's suggestion to get LS platforms
Start to test.

Regards
Dong Aisheng
Andy Shevchenko June 2, 2017, 5:11 p.m. UTC | #3
On Wed, May 31, 2017 at 5:18 PM, A.S. Dong <aisheng.dong@nxp.com> wrote:
>> On Tue, May 9, 2017 at 10:50 AM, Dong Aisheng <aisheng.dong@nxp.com> wrote:

By some reason my previous message went privately.
It didn't have anything major anyway and here I'm suggesting
optimization of finding factors of the formula in use. See below.

>> > +       u32 sbr, osr, baud_diff, tmp_osr, tmp_sbr, tmp_diff, tmp;
>> > +       u32 clk = sport->port.uartclk;
>> > +
>> > +       /*
>> > +        * The idea is to use the best OSR (over-sampling rate) possible.
>> > +        * Note, OSR is typically hard-set to 16 in other LPUART
>> instantiations.
>> > +        * Loop to find the best OSR value possible, one that generates
>> minimum
>> > +        * baud_diff iterate through the rest of the supported values of
>> OSR.
>> > +        *
>> > +        * Calculation Formula:
>> > +        *  Baud Rate = baud clock / ((OSR+1) × SBR)
>> > +        */
>> > +       baud_diff = baudrate;
>> > +       osr = 0;
>> > +       sbr = 0;
>> > +
>>
>> > +       for (tmp_osr = 4; tmp_osr <= 32; tmp_osr++) {

I missed one thing, what happened by default to OSR? What is the value in use?

>> I _think_ you may simplify this and avoid for-loop if you reconsider
>> approach.

> But there is indeed a optimization way, see below.

> To optimize the looping, we probably could do:
> If (!baud_diff)
>         Break;

It's a small one, we may have more interesting approach.

So, the algo is the following:

Assume the ranges like this:
OSR = [4 ... 32]
SBR = [2 ... 8192]

Then:

1. Get ratio factor as
      ratio = CLK / desired baud rate
2. If ratio < 8192 * 9 / 2, just use (ratio / 4, 4) as (OSR, SBR)
setting. (Needs clarification on OSR < 4)
3. if ratio >= 8192 * 31, just use those two numbers (8192, 31). You
can't do anything better there.
4. Otherwise, get a minimum required factor of OSR
      osr_min = ratio / 8192
5. Start your loop from osr_min + 1 to 31.

6 (optional). Of course you may not consider baud_diff > osr_min, it's
I suppose obvious

P.S. Note, all divisions by 2^n are just simple right shifts. Diffs
are calculated as multiplication of OSR and SBR in comparison to
ratio. One division so far.
Dong Aisheng June 9, 2017, 8:01 a.m. UTC | #4
Hi Andy,

> -----Original Message-----

> From: Andy Shevchenko [mailto:andy.shevchenko@gmail.com]

> Sent: Saturday, June 03, 2017 1:11 AM

> To: A.S. Dong

> Cc: linux-serial@vger.kernel.org; linux-kernel@vger.kernel.org; linux-arm

> Mailing List; Greg Kroah-Hartman; Jiri Slaby; Andy Duan; Stefan Agner;

> Mingkai Hu; Y.B. Lu

> Subject: Re: [PATCH 6/6] tty: serial: lpuart: add a more accurate baud

> rate calculation method

> 

> On Wed, May 31, 2017 at 5:18 PM, A.S. Dong <aisheng.dong@nxp.com> wrote:

> >> On Tue, May 9, 2017 at 10:50 AM, Dong Aisheng <aisheng.dong@nxp.com>

> wrote:

> 

> By some reason my previous message went privately.

> It didn't have anything major anyway and here I'm suggesting optimization

> of finding factors of the formula in use. See below.

> 

> >> > +       u32 sbr, osr, baud_diff, tmp_osr, tmp_sbr, tmp_diff, tmp;

> >> > +       u32 clk = sport->port.uartclk;

> >> > +

> >> > +       /*

> >> > +        * The idea is to use the best OSR (over-sampling rate)

> possible.

> >> > +        * Note, OSR is typically hard-set to 16 in other LPUART

> >> instantiations.

> >> > +        * Loop to find the best OSR value possible, one that

> >> > + generates

> >> minimum

> >> > +        * baud_diff iterate through the rest of the supported

> >> > + values of

> >> OSR.

> >> > +        *

> >> > +        * Calculation Formula:

> >> > +        *  Baud Rate = baud clock / ((OSR+1) × SBR)

> >> > +        */

> >> > +       baud_diff = baudrate;

> >> > +       osr = 0;

> >> > +       sbr = 0;

> >> > +

> >>

> >> > +       for (tmp_osr = 4; tmp_osr <= 32; tmp_osr++) {

> 

> I missed one thing, what happened by default to OSR? What is the value in

> use?

> 


No valid default value. (osc/sbr are 0 by default)
If no proper osc and sbr calculated, a WARNING will show.

> >> I _think_ you may simplify this and avoid for-loop if you reconsider

> >> approach.

> 

> > But there is indeed a optimization way, see below.

> 

> > To optimize the looping, we probably could do:

> > If (!baud_diff)

> >         Break;

> 

> It's a small one, we may have more interesting approach.

> 

> So, the algo is the following:

> 

> Assume the ranges like this:

> OSR = [4 ... 32]

> SBR = [2 ... 8192]

> 


Baud Rate = baud clock / ((OSR+1) × SBR)

In HW:
OSR range : 3 – 31
SBR range: 1 – 8191

> Then:

> 

> 1. Get ratio factor as

>       ratio = CLK / desired baud rate

> 2. If ratio < 8192 * 9 / 2, just use (ratio / 4, 4) as (OSR, SBR) setting.

> (Needs clarification on OSR < 4) 


Sorry that I'm a bit mess here.
What is 8192 * 9 /2 meaning?

And for (ratio / 4, 4) as (OSR,SBR), take 115200 as an example:
Assuming baud clock 24Mhz.

Ratio = 24000000 / 115200 = 208
OSR = Ratio / 4 = 52 
Then OSR is out of range which seems wrong.

> 3. if ratio >= 8192 * 31, just use those

> two numbers (8192, 31). You can't do anything better there.


This actually may not happen.
Even take a 9600 as example, the clk becomes:
8191 * 31 * 9600 = 2.4GHz
Which is theoretically not exist.

> 4. Otherwise, get a minimum required factor of OSR

>       osr_min = ratio / 8192

> 5. Start your loop from osr_min + 1 to 31.

> 

> 6 (optional). Of course you may not consider baud_diff > osr_min, it's I

> suppose obvious

> 

> P.S. Note, all divisions by 2^n are just simple right shifts. Diffs are

> calculated as multiplication of OSR and SBR in comparison to ratio. One

> division so far.

> 


I'm not quite understand the approach.

How about you send a separate baud algorithm improvement patch later?
Then it first can provide us a good patch history and also better to
understand for review.

Last, very appreciate for your kind and professional review.

Regards
Dong Aisheng
Andy Shevchenko June 9, 2017, 9:26 a.m. UTC | #5
On Fri, Jun 9, 2017 at 11:01 AM, A.S. Dong <aisheng.dong@nxp.com> wrote:

>> >> > +       u32 sbr, osr, baud_diff, tmp_osr, tmp_sbr, tmp_diff, tmp;
>> >> > +       u32 clk = sport->port.uartclk;
>> >> > +
>> >> > +       /*
>> >> > +        * The idea is to use the best OSR (over-sampling rate)
>> possible.
>> >> > +        * Note, OSR is typically hard-set to 16 in other LPUART
>> >> instantiations.
>> >> > +        * Loop to find the best OSR value possible, one that
>> >> > + generates
>> >> minimum
>> >> > +        * baud_diff iterate through the rest of the supported
>> >> > + values of
>> >> OSR.
>> >> > +        *
>> >> > +        * Calculation Formula:
>> >> > +        *  Baud Rate = baud clock / ((OSR+1) × SBR)
>> >> > +        */
>> >> > +       baud_diff = baudrate;
>> >> > +       osr = 0;
>> >> > +       sbr = 0;
>> >> > +
>> >>
>> >> > +       for (tmp_osr = 4; tmp_osr <= 32; tmp_osr++) {
>>
>> I missed one thing, what happened by default to OSR? What is the value in
>> use?
>>
>
> No valid default value. (osc/sbr are 0 by default)
> If no proper osc and sbr calculated, a WARNING will show.

Okay, so, it means the maximum supported speed is UART clock / 4. Correct?

>> So, the algo is the following:
>>
>> Assume the ranges like this:
>> OSR = [4 ... 32]
>> SBR = [2 ... 8192]
>>
>
> Baud Rate = baud clock / ((OSR+1) × SBR)
>
> In HW:
> OSR range : 3 – 31
> SBR range: 1 – 8191

I've read that, but think outside the box.

>> Then:
>>
>> 1. Get ratio factor as
>>       ratio = CLK / desired baud rate
>> 2. If ratio < 8192 * 9 / 2, just use (ratio / 4, 4) as (OSR, SBR) setting.
>> (Needs clarification on OSR < 4)
>
> Sorry that I'm a bit mess here.
> What is 8192 * 9 /2 meaning?

I forgot the details...

> And for (ratio / 4, 4) as (OSR,SBR), take 115200 as an example:
> Assuming baud clock 24Mhz.
>
> Ratio = 24000000 / 115200 = 208
> OSR = Ratio / 4 = 52
> Then OSR is out of range which seems wrong.

...yes...

>> 3. if ratio >= 8192 * 31, just use those
>> two numbers (8192, 31). You can't do anything better there.
>
> This actually may not happen.
> Even take a 9600 as example, the clk becomes:
> 8191 * 31 * 9600 = 2.4GHz
> Which is theoretically not exist.
>
>> 4. Otherwise, get a minimum required factor of OSR
>>       osr_min = ratio / 8192
>> 5. Start your loop from osr_min + 1 to 31.
>>
>> 6 (optional). Of course you may not consider baud_diff > osr_min, it's I
>> suppose obvious
>>
>> P.S. Note, all divisions by 2^n are just simple right shifts. Diffs are
>> calculated as multiplication of OSR and SBR in comparison to ratio. One
>> division so far.

> I'm not quite understand the approach.

...lemme prepare a python script demonstrating it.

> How about you send a separate baud algorithm improvement patch later?

Why not to do it right a way?

Just describe it in a comment if you afraid of reader can't understand
from the code.
Dong Aisheng June 9, 2017, 2:20 p.m. UTC | #6
> -----Original Message-----

> From: Andy Shevchenko [mailto:andy.shevchenko@gmail.com]

> Sent: Friday, June 09, 2017 5:26 PM

> To: A.S. Dong

> Cc: linux-serial@vger.kernel.org; linux-kernel@vger.kernel.org; linux-arm

> Mailing List; Greg Kroah-Hartman; Jiri Slaby; Andy Duan; Stefan Agner;

> Mingkai Hu; Y.B. Lu; Dong Aisheng

> Subject: Re: [PATCH 6/6] tty: serial: lpuart: add a more accurate baud

> rate calculation method

> 

> On Fri, Jun 9, 2017 at 11:01 AM, A.S. Dong <aisheng.dong@nxp.com> wrote:

> 

> >> >> > +       u32 sbr, osr, baud_diff, tmp_osr, tmp_sbr, tmp_diff, tmp;

> >> >> > +       u32 clk = sport->port.uartclk;

> >> >> > +

> >> >> > +       /*

> >> >> > +        * The idea is to use the best OSR (over-sampling rate)

> >> possible.

> >> >> > +        * Note, OSR is typically hard-set to 16 in other LPUART

> >> >> instantiations.

> >> >> > +        * Loop to find the best OSR value possible, one that

> >> >> > + generates

> >> >> minimum

> >> >> > +        * baud_diff iterate through the rest of the supported

> >> >> > + values of

> >> >> OSR.

> >> >> > +        *

> >> >> > +        * Calculation Formula:

> >> >> > +        *  Baud Rate = baud clock / ((OSR+1) × SBR)

> >> >> > +        */

> >> >> > +       baud_diff = baudrate;

> >> >> > +       osr = 0;

> >> >> > +       sbr = 0;

> >> >> > +

> >> >>

> >> >> > +       for (tmp_osr = 4; tmp_osr <= 32; tmp_osr++) {

> >>

> >> I missed one thing, what happened by default to OSR? What is the

> >> value in use?

> >>

> >

> > No valid default value. (osc/sbr are 0 by default) If no proper osc

> > and sbr calculated, a WARNING will show.

> 

> Okay, so, it means the maximum supported speed is UART clock / 4. Correct?

> 


Yes.

> >> So, the algo is the following:

> >>

> >> Assume the ranges like this:

> >> OSR = [4 ... 32]

> >> SBR = [2 ... 8192]

> >>

> >

> > Baud Rate = baud clock / ((OSR+1) × SBR)

> >

> > In HW:

> > OSR range : 3 – 31

> > SBR range: 1 – 8191

> 

> I've read that, but think outside the box.

> 

> >> Then:

> >>

> >> 1. Get ratio factor as

> >>       ratio = CLK / desired baud rate 2. If ratio < 8192 * 9 / 2,

> >> just use (ratio / 4, 4) as (OSR, SBR) setting.

> >> (Needs clarification on OSR < 4)

> >

> > Sorry that I'm a bit mess here.

> > What is 8192 * 9 /2 meaning?

> 

> I forgot the details...

> 

> > And for (ratio / 4, 4) as (OSR,SBR), take 115200 as an example:

> > Assuming baud clock 24Mhz.

> >

> > Ratio = 24000000 / 115200 = 208

> > OSR = Ratio / 4 = 52

> > Then OSR is out of range which seems wrong.

> 

> ...yes...

> 

> >> 3. if ratio >= 8192 * 31, just use those two numbers (8192, 31). You

> >> can't do anything better there.

> >

> > This actually may not happen.

> > Even take a 9600 as example, the clk becomes:

> > 8191 * 31 * 9600 = 2.4GHz

> > Which is theoretically not exist.

> >

> >> 4. Otherwise, get a minimum required factor of OSR

> >>       osr_min = ratio / 8192

> >> 5. Start your loop from osr_min + 1 to 31.

> >>

> >> 6 (optional). Of course you may not consider baud_diff > osr_min,

> >> it's I suppose obvious

> >>

> >> P.S. Note, all divisions by 2^n are just simple right shifts. Diffs

> >> are calculated as multiplication of OSR and SBR in comparison to

> >> ratio. One division so far.

> 

> > I'm not quite understand the approach.

> 

> ...lemme prepare a python script demonstrating it.

> 


Great, thanks

> > How about you send a separate baud algorithm improvement patch later?

> 

> Why not to do it right a way?

> 


Because I thought that could be a separate patch which is doing algorithm
improvement, then we can have the full history and a clear comparison.

And also we are still not sure whether it works, we don't want to block on it
too long.

But if you're pretty sure about it, I would wait for some more time.

However, personally I would still rather keep them in two separate Patches
for clearer history and comparison.

> Just describe it in a comment if you afraid of reader can't understand

> from the code.

> 


That is good.

Regards
Dong Aisheng

> --

> With Best Regards,

> Andy Shevchenko
Andy Shevchenko June 9, 2017, 3:48 p.m. UTC | #7
On Fri, Jun 9, 2017 at 5:20 PM, A.S. Dong <aisheng.dong@nxp.com> wrote:
>> > How about you send a separate baud algorithm improvement patch later?
>>
>> Why not to do it right a way?
>>
>
> Because I thought that could be a separate patch which is doing algorithm
> improvement, then we can have the full history and a clear comparison.
>
> And also we are still not sure whether it works, we don't want to block on it
> too long.
>
> But if you're pretty sure about it, I would wait for some more time.
>
> However, personally I would still rather keep them in two separate Patches
> for clearer history and comparison.

Since we already near to -rc5, I would rather agree with you.
So, please proceed with your approach and we can modify it in relaxing
mode later on.

Thanks, and sorry for the delay!
Dong Aisheng June 12, 2017, 2:23 p.m. UTC | #8
> -----Original Message-----
> From: Andy Shevchenko [mailto:andy.shevchenko@gmail.com]
> Sent: Friday, June 09, 2017 11:49 PM
> To: A.S. Dong
> Cc: linux-serial@vger.kernel.org; linux-kernel@vger.kernel.org; linux-arm
> Mailing List; Greg Kroah-Hartman; Jiri Slaby; Andy Duan; Stefan Agner;
> Mingkai Hu; Y.B. Lu; Dong Aisheng
> Subject: Re: [PATCH 6/6] tty: serial: lpuart: add a more accurate baud
> rate calculation method
> 
> On Fri, Jun 9, 2017 at 5:20 PM, A.S. Dong <aisheng.dong@nxp.com> wrote:
> >> > How about you send a separate baud algorithm improvement patch later?
> >>
> >> Why not to do it right a way?
> >>
> >
> > Because I thought that could be a separate patch which is doing
> > algorithm improvement, then we can have the full history and a clear
> comparison.
> >
> > And also we are still not sure whether it works, we don't want to
> > block on it too long.
> >
> > But if you're pretty sure about it, I would wait for some more time.
> >
> > However, personally I would still rather keep them in two separate
> > Patches for clearer history and comparison.
> 
> Since we already near to -rc5, I would rather agree with you.
> So, please proceed with your approach and we can modify it in relaxing
> mode later on.
> 
> Thanks, and sorry for the delay!

Never mind.
A professional review is always respectable and appreciated.

Thanks

Regards
Dong Aisheng
diff mbox

Patch

diff --git a/drivers/tty/serial/fsl_lpuart.c b/drivers/tty/serial/fsl_lpuart.c
index 5b485e8..6b2abb7 100644
--- a/drivers/tty/serial/fsl_lpuart.c
+++ b/drivers/tty/serial/fsl_lpuart.c
@@ -140,6 +140,8 @@ 
 #define UARTBAUD_SBNS		0x00002000
 #define UARTBAUD_SBR		0x00000000
 #define UARTBAUD_SBR_MASK	0x1fff
+#define UARTBAUD_OSR_MASK       0x1f
+#define UARTBAUD_OSR_SHIFT      24
 
 #define UARTSTAT_LBKDIF		0x80000000
 #define UARTSTAT_RXEDGIF	0x40000000
@@ -1508,6 +1510,72 @@  lpuart_set_termios(struct uart_port *port, struct ktermios *termios,
 }
 
 static void
+lpuart32_serial_setbrg(struct lpuart_port *sport, unsigned int baudrate)
+{
+	u32 sbr, osr, baud_diff, tmp_osr, tmp_sbr, tmp_diff, tmp;
+	u32 clk = sport->port.uartclk;
+
+	/*
+	 * The idea is to use the best OSR (over-sampling rate) possible.
+	 * Note, OSR is typically hard-set to 16 in other LPUART instantiations.
+	 * Loop to find the best OSR value possible, one that generates minimum
+	 * baud_diff iterate through the rest of the supported values of OSR.
+	 *
+	 * Calculation Formula:
+	 *  Baud Rate = baud clock / ((OSR+1) × SBR)
+	 */
+	baud_diff = baudrate;
+	osr = 0;
+	sbr = 0;
+
+	for (tmp_osr = 4; tmp_osr <= 32; tmp_osr++) {
+		/* calculate the temporary sbr value  */
+		tmp_sbr = (clk / (baudrate * tmp_osr));
+		if (tmp_sbr == 0)
+			tmp_sbr = 1;
+
+		/*
+		 * calculate the baud rate difference based on the temporary
+		 * osr and sbr values
+		 */
+		tmp_diff = clk / (tmp_osr * tmp_sbr) - baudrate;
+
+		/* select best values between sbr and sbr+1 */
+		tmp = clk / (tmp_osr * (tmp_sbr + 1));
+		if (tmp_diff > (baudrate - tmp)) {
+			tmp_diff = baudrate - tmp;
+			tmp_sbr++;
+		}
+
+		if (tmp_diff <= baud_diff) {
+			baud_diff = tmp_diff;
+			osr = tmp_osr;
+			sbr = tmp_sbr;
+		}
+	}
+
+	/* handle buadrate outside acceptable rate */
+	if (baud_diff > ((baudrate / 100) * 3))
+		dev_warn(sport->port.dev,
+			 "unacceptable baud rate difference of more than 3%%\n");
+
+	tmp = lpuart32_read(sport->port.membase + UARTBAUD);
+
+	if ((osr > 3) && (osr < 8))
+		tmp |= UARTBAUD_BOTHEDGE;
+
+	tmp &= ~(UARTBAUD_OSR_MASK << UARTBAUD_OSR_SHIFT);
+	tmp |= (((osr-1) & UARTBAUD_OSR_MASK) << UARTBAUD_OSR_SHIFT);
+
+	tmp &= ~UARTBAUD_SBR_MASK;
+	tmp |= sbr & UARTBAUD_SBR_MASK;
+
+	tmp &= ~(UARTBAUD_TDMAE | UARTBAUD_RDMAE);
+
+	lpuart32_write(tmp, sport->port.membase + UARTBAUD);
+}
+
+static void
 lpuart32_set_termios(struct uart_port *port, struct ktermios *termios,
 		   struct ktermios *old)
 {
@@ -1613,12 +1681,17 @@  lpuart32_set_termios(struct uart_port *port, struct ktermios *termios,
 	lpuart32_write(old_ctrl & ~(UARTCTRL_TE | UARTCTRL_RE),
 			sport->port.membase + UARTCTRL);
 
-	sbr = sport->port.uartclk / (16 * baud);
-	bd &= ~UARTBAUD_SBR_MASK;
-	bd |= sbr & UARTBAUD_SBR_MASK;
-	bd |= UARTBAUD_BOTHEDGE;
-	bd &= ~(UARTBAUD_TDMAE | UARTBAUD_RDMAE);
-	lpuart32_write(bd, sport->port.membase + UARTBAUD);
+	if (of_device_is_compatible(port->dev->of_node, "fsl,imx7ulp-lpuart")) {
+		lpuart32_serial_setbrg(sport, baud);
+	} else {
+		sbr = sport->port.uartclk / (16 * baud);
+		bd &= ~UARTBAUD_SBR_MASK;
+		bd |= sbr & UARTBAUD_SBR_MASK;
+		bd |= UARTBAUD_BOTHEDGE;
+		bd &= ~(UARTBAUD_TDMAE | UARTBAUD_RDMAE);
+		lpuart32_write(bd, sport->port.membase + UARTBAUD);
+	}
+
 	lpuart32_write(modem, sport->port.membase + UARTMODIR);
 	lpuart32_write(ctrl, sport->port.membase + UARTCTRL);
 	/* restore control register */