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