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