[RFC] 16C950 custom baudrate settings

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

 



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?

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