--- serial-old.c Wed Feb 20 17:21:45 2002 +++ serial.c Wed Feb 20 17:26:16 2002 @@ -57,6 +57,9 @@ * 10/00: add in optional software flow control for serial console. * Kanoj Sarcar (Modified by Theodore Ts'o) * + * 02/02: 16C950 automatic clock frequency detection. + * Fabrizio Gennari + * */ static char *serial_version = "5.05c"; @@ -3510,6 +3513,64 @@ return count; } +/* The algorithm for detecting the clock frequency in 16C950 UARTs + was suggested by Anna Trett of Oxford Semiconductor */ + +static int autodetect_16c950_clock(struct async_struct *info, + struct serial_state *state) +{ + unsigned char old_fcr, old_mcr, old_dll, old_dlm, old_tcr; + int nchar=0,num=0,result; + struct timeval before,after; + int husec_elapsed; + + old_fcr = serial_inp(info, UART_FCR); + old_mcr = serial_inp(info, UART_MCR); + serial_outp(info, UART_FCR, + UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT); + serial_outp(info, UART_MCR, UART_MCR_LOOP); + serial_outp(info, UART_LCR, UART_LCR_DLAB); + old_dll = serial_inp(info, UART_DLL); + old_dlm = serial_inp(info, UART_DLM); + old_tcr = serial_inp(info, UART_TCR); + serial_outp(info, UART_DLL, 0x01); + serial_outp(info, UART_DLM, 0x00); + serial_outp(info, UART_TCR, 0x00); + serial_outp(info, UART_LCR, 0x03); + printk(KERN_INFO "Autodetecting...\n"); + + /* The best thing to do would be to loop for + a set number of jiffies. But the variable jiffies does + not increment here. So, we just loop until a set number + of characters is received (it should take 1 sec if a standard + clock is used) and measure how many hundreds of usecs it took, + storing it in husec_elapsed */ + + do_gettimeofday(&before); + do{ + serial_outp(info, UART_TX, (num++)&0xFF); + if (serial_inp(info, UART_LSR) & UART_LSR_DR){ + nchar++; + serial_inp(info, UART_RX); + } + }while(nchar<11520 && num < 1000000); + do_gettimeofday(&after); + husec_elapsed=(after.tv_sec-before.tv_sec)*10000+(after.tv_usec-before.tv_usec)/100; + printk(KERN_INFO "num %u nchar %u elapsed %u\n",num,nchar,husec_elapsed); + serial_outp(info, UART_FCR, old_fcr); + serial_outp(info, UART_MCR, old_mcr); + serial_outp(info, UART_LCR, UART_LCR_DLAB); + serial_outp(info, UART_DLL, old_dll); + serial_outp(info, UART_DLM, old_dlm); + serial_outp(info, UART_TCR, old_tcr); + + nchar = nchar*10*10000; + printk(KERN_INFO "nchar %u\n",nchar); + result = nchar/husec_elapsed; + printk(KERN_INFO "res %u\n",result); + return result; +} + /* * This is a helper routine to autodetect StarTech/Exar/Oxsemi UART's. * When this function is called we know it is at least a StarTech @@ -3556,6 +3617,7 @@ state->type = PORT_16C950; state->revision = serial_icr_read(info, UART_REV) | (scratch3 << 8); + state->baud_base=autodetect_16c950_clock(info,state); return; } }