Message ID | 1494316248-24052-7-git-send-email-aisheng.dong@nxp.com |
---|---|
State | New |
Headers | show |
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).
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
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.
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
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.
> -----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
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!
> -----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 --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 */
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(-)