Re: [RFC] 16C950 custom baudrate settings

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

 



On Thu, Apr 9, 2015 at 6:13 PM, Greg KH <gregkh@xxxxxxxxxxxxxxxxxxx> wrote:
> 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.

Will do. Below some more details, but of course the patch would show more.

I was just thinking how to handle TCR and CPR values. The simplest
solution seems to just  extend serial8250_(g/s)et_divisor, so that is
gets one more parameter cpr and "frac" will be tcr.

The max baudrate value for 16C950 (uart_get_baud_rate) should be also
changed to port->uartclk and not port->uartclk / 16, as the clock
divider can be changed via cpr.

Yegor
--
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