Re: [RFC] 16C950 custom baudrate settings

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On Thu, Apr 09, 2015 at 01:04:26PM +0200, Yegor Yefremov wrote:
> 16C950 UART has 3 register, that will be used for baudrate setting:
> divisor, TCR and CPR. Below you can see our calculation routine
> (http://svn.visionsystems.de/cgi-bin/viewvc.cgi/trunk/drivers/serial/serial_core.c?view=markup&root=linux-2.6.33-OnRISC&sortby=log&pathrev=10):
> 
> /**
>  * serial16C950_get_divisors - return TCR, CPR and divisor values for
> 16C950 UART
>  * @baudbase: baud base
>  * @baud: desired baud rate in decimal
>  * @TCR: times clock register value
>  * @CPR: clock prescaler register value
>  * @Divisor: 16-bit divisor value for DLL and DLM registers
>  *
>  * Search for the valid values of the TCR, CPR and divisor. According
> to the datasheet
>  * the crystal clock applied to the 16C950 can't be greater than 60MHz.
>  *
>  * The baud rates smaller than 1000bit/s can be calculated
> immediately. For the greater baud rates
>  * TCR, CPR and divisor values will be tried to find the first
> combination generating the
>  * baud rate with an error smaller than 3 per mil, but we also accept
> the baudr rates with an error
>  * smaller than 2%.
>  *
>  * If no suitable combination is found, 0 is returned. Otherwise 1
> will be returned.
>  */
> int serial16C950_get_divisors(unsigned long baudbase, unsigned long
> baud, unsigned *TCR, unsigned *CPR, unsigned short *Divisor)
> {
>         unsigned long crystal8, Error, maxerr, e03p, e20p, div = 0;
>         long long tmp;
>         int iCPR, iTCR, ret;
> 
>         ret = 0;
>         crystal8 = baudbase * 16 * 8; // crystal speed (max = 60MHz * 8)
>         *TCR = 0;
>         *CPR = 8;
>         *Divisor = 96;
>         iTCR = 16;
>         if (baud > baudbase * (16 / 4))
>                 return ret;
> 
>         if(baud < 1000)
>         {
>                 iCPR = ((baudbase*8)/65535) + 1;
>                 *Divisor = ((baudbase*8) / iCPR)/baud;
>                 *CPR = iCPR;
>                 *TCR = iTCR;
>                 ret = 1;
>                 goto DoubleBreak;
>         }
> 
>         e03p = (baud * 3) / 1000; // 3 per mil
>         e20p = (baud * 2) / 100; // 2%
>         maxerr = baud;
> 
>         do
>         {
>                 iCPR = 8;
> 
>                 do
>                 {
>                         tmp = iTCR*iCPR*baud;
> 
>                         // check if we get division overflow
>                         if (tmp < 2* crystal8)
>                         {
>                                 // calculate actual divisor
>                                 div = (crystal8 + ((unsigned long)tmp/2))
>                                                 / (unsigned long)(tmp);
> 
>                                 // divisor must fit into 16 bit
>                                 if (div < 0x10000)
>                                 {
>                                         // calculate possible error
>                                         Error = abs(baud - (crystal8)
> / (iTCR*iCPR*div));
>                                         if (Error < maxerr)
>                                         {
>                                                 maxerr = Error;
>                                                 *Divisor = div;
>                                                 *CPR = iCPR;
>                                                 *TCR = iTCR;
>                                         }
>                                 }
>                         }
> 
>                         // if error is smaller than 2% we have a valid result
>                         if (maxerr <= e20p)
>                                 ret = 1;
>                         // exact result
>                         if (maxerr == 0)
>                                 goto DoubleBreak;
>                         if ((iTCR >= 14) && (maxerr <= e03p))
>                                 goto DoubleBreak;
>                         if ((iTCR < 14) && (maxerr <= e20p))
>                                 goto DoubleBreak;
>                         if (div == 0)
>                                 break;
>                         else
>                                 iCPR++;
>                 } while (iCPR <= 255);
>                 iTCR--;
>         } while (iTCR >= 4);
> 
> DoubleBreak:
>         *TCR &= 0x0F;
>         return ret;
> }
> 
> It would be great, if such  functionality would be available in
> mainline kernel. What do you think about this?

I have no idea what you are asking for here, please create a patch if
you think it should be in the kernel, otherwise there's really nothing
we can do.

thanks,

greg k-h
--
To unsubscribe from this list: send the line "unsubscribe linux-serial" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html




[Index of Archives]     [Kernel Newbies]     [Security]     [Netfilter]     [Bugtraq]     [Linux PPP]     [Linux FS]     [Yosemite News]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux RAID]     [Samba]     [Video 4 Linux]     [Linmodem]     [Device Mapper]     [Linux Kernel for ARM]

  Powered by Linux