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

Re: Pcmcia card speed



On Fri, Oct 26, 2001 at 10:13:42AM -0700, jt wrote:
> Ulrich Müller wrote :
>
> > has no effect, as the driver has no support for the 16950's CPR
> > register. The strange thing is that according to the 16950 specification
> > the CPR register should be disabled after a chip reset, so the baud rate
> > should be the maximum possible.

	I've added proper CPR support for the 16C950 (see attached
patch). I've even added a special hack for the BrainBoxes Pcmcia
card. No go...
	Now, I'm thinking that Brainboxes uses the default 1.8432MHz
clock by default, and that you need to play with 'CKS' to get what you
need. I would like those friends at Brainboxes to tell us the trick ;-)
	Have fun...

	Jean
diff -u -p linux/include/linux/serial_reg.orig.h linux/include/linux/serial_reg.h
--- linux/include/linux/serial_reg.orig.h	Fri Oct 26 10:50:17 2001
+++ linux/include/linux/serial_reg.h	Fri Oct 26 11:35:39 2001
@@ -126,6 +126,12 @@
 #define UART_MCR_OUT1	0x04	/* Out1 complement */
 #define UART_MCR_RTS	0x02	/* RTS complement */
 #define UART_MCR_DTR	0x01	/* DTR complement */
+/*
+ * Extra MCR bit for 16C950.
+ * Those bits are only available when the 950 mode is enable (EFR bit 4 = 1)
+ */
+#define UART_MCR_IRDA		0x40	/* Enable IrDA mode (SIR) */
+#define UART_MCR_PRESCALE	0x80	/* Enable clock prescaler */
 
 /*
  * These are the definitions for the Modem Status Register
@@ -163,7 +169,7 @@
 /* The 16950 ICR registers */
 #define UART_ACR	0x00	/* Additional Control Register */
 #define UART_CPR	0x01	/* Clock Prescalar Register */
-#define UART_TCR	0x02	/* Times Clock Register */
+#define UART_TCR	0x02	/* Times Clock Register (overclock) */
 #define UART_CKS	0x03	/* Clock Select Register */
 #define UART_TTL	0x04	/* Transmitter Interrupt Trigger Level */
 #define UART_RTL	0x05	/* Receiver Interrupt Trigger Level */
diff -u -p linux/include/linux/serialP.orig.h linux/include/linux/serialP.h
--- linux/include/linux/serialP.orig.h	Fri Oct 26 10:50:07 2001
+++ linux/include/linux/serialP.h	Fri Oct 26 14:04:43 2001
@@ -75,6 +75,7 @@ struct async_struct {
 	int			MCR; 	/* Modem control register */
 	int			LCR; 	/* Line control register */
 	int			ACR;	 /* 16950 Additional Control Reg. */
+	int			CPR;	/* 16950 Clock prescaler. */
 	unsigned long		event;
 	unsigned long		last_active;
 	int			line;
diff -u -p linux/drivers/char/serial.orig.c linux/drivers/char/serial.c
--- linux/drivers/char/serial.orig.c	Fri Oct 26 10:32:58 2001
+++ linux/drivers/char/serial.c	Fri Oct 26 16:02:54 2001
@@ -1268,6 +1268,36 @@ static int startup(struct async_struct *
 		serial_outp(info, UART_LCR, 0xBF);
 		serial_outp(info, UART_EFR, UART_EFR_ECB);
 		serial_outp(info, UART_LCR, 0);
+
+		/* If we don't have a value for CPR, try to guess one...
+		 * The 16C950 can use non standard UART clock frequencies,
+		 * the CPR register allow to convert this non standard clock
+		 * to the standard clock, allowing us to generate the proper
+		 * serial bit rate.
+		 * The UART clock is known only to the board designer, so we
+		 * can only attempt to guess the proper CPR value (see below).
+		 * The user can "fix" CPR by setting baud_base with setserial.
+		 * Jean II */
+		if(info->CPR == 0) {
+			/* Spec says : After reset, MCR[7] is set to the
+			 * complement of the value of pin CLKSEL.
+			 * To be compatible with 16C550, either use 1.8432MHz
+			 * clock with CLKSEL connected to VDD, or use
+			 * 7.3728MHz with CLKSEL connected to GND. */
+			if(serial_inp(info, UART_MCR) & UART_MCR_PRESCALE) {
+				/* Assume 7.3728MHz UART clock.
+				 * CPR will contain 0x20 == Divide by 4 */
+				info->CPR = serial_icr_read(info, UART_CPR);
+				state->baud_base = (115200 * info->CPR) / 0x8;
+			} else {
+				/* Assume standard 1.8432MHz UART clock */
+				info->CPR = 0x08;	/* Divide by 1 */
+				state->baud_base = 115200;
+			}
+#ifdef DEBUG_16C950_CPR
+			printk(KERN_DEBUG "16C950 baud_base = %d, CPR = 0x%X - 0x%X, MCR = 0x%X\n", state->baud_base, info->CPR, serial_icr_read(info, UART_CPR), serial_inp(info, UART_MCR));
+#endif
+		}
 	}
 
 #ifdef CONFIG_SERIAL_RSA
@@ -1370,8 +1400,12 @@ static int startup(struct async_struct *
 	serial_outp(info, UART_LCR, UART_LCR_WLEN8);	/* reset DLAB */
 
 	info->MCR = 0;
+	if (state->type == PORT_16C950)
+		/* For the 16C950, always enable the clock prescaler
+		 * it will be properly set in set_extra_speed_950() */
+		info->MCR |= UART_MCR_PRESCALE;
 	if (info->tty->termios->c_cflag & CBAUD)
-		info->MCR = UART_MCR_DTR | UART_MCR_RTS;
+		info->MCR |= UART_MCR_DTR | UART_MCR_RTS;
 #ifdef CONFIG_SERIAL_MANY_PORTS
 	if (info->flags & ASYNC_FOURPORT) {
 		if (state->irq == 0)
@@ -1601,6 +1635,110 @@ static int tty_get_baud_rate(struct tty_
 #endif
 
 /*
+ * Set the appropriate extra speed control register of the 16C950.
+ * Basically, we set the CPR prescaler register to match the assumed
+ * UART clock speed, and we set the TCR overclock register to enable
+ * to reach higher speed.
+ * We return the new apparent overclocked baud_base that will be used
+ * to calculate the proper divisor (quot).
+ * Jean II
+ */
+static int set_extra_speed_950(struct async_struct *info, int baud)
+{
+	/* baud_base need to be at least 25 bits to avoid overflow. */
+	long	baud_base = info->state->baud_base;
+	int	current_tcr;
+
+	/* Setting CPR (prescaler register)
+	 *
+	 * baud_base indicates by how much the present UART clock is faster
+	 * than the standard UART clock. In other words, if the UART clock
+	 * is 4 time faster than standard (7.3728MHz), you should set
+	 * baud_base to 4*115200 = 460800 (which is the max baud rate
+	 * with CPR=1 and TCR=16 mentionned in table 17 at page 30 of the
+	 * OX16C950 rev B spec sheet).
+	 * It seems that the prescaler must be greater than 1 (CPR >= 0x8).
+	 *
+	 * For now, we are pretty dumb in setting CPR. We just use the proper
+	 * prescaler value when we need to do standard speeds (up to 115200)
+	 * and we use no prescaler to reach higher speeds.
+	 * Remember that on top of that we can be overclocked up to 4 times
+	 * through TCR.
+	 * We could be much more clever, as in theory the CPR allow us
+	 * to generate any baud_base we want, so we can generate any
+	 * serial speed we want. I don't really know how well it works
+	 * in practice...
+	 */
+	if(baud > 115200) {
+		/* No prescaler - allow higher speeds */
+		info->CPR = 0x8;
+		/* No need to fudge baud_rate, it's the proper one */
+	} else {
+		/* Calculate prescaler - allow 550 compatible speeds */
+		info->CPR = (baud_base * 0x8) / 115200;
+		/* Check boundaries */
+		if(info->CPR < 8) {
+			info->CPR = 8;
+			printk(KERN_DEBUG "16C950 : Baud_base is to low (min 115200)\n");
+		}
+		if(info->CPR > 0xFF) {
+			info->CPR = 0xFF;
+			printk(KERN_DEBUG "16C950 : Baud_base is to high (max 3672000)\n");
+		}
+		/* Our apparent baud_base is now 550 compatible */
+		baud_base = 115200;
+	}
+	serial_icr_write(info, UART_CPR, info->CPR);
+#ifdef DEBUG_16C950_CPR
+	printk(KERN_DEBUG "16C950 new baud_base = %ld, CPR = 0x%X\n", baud_base, info->CPR);
+#endif
+
+	/* Setting TCR (overclock register)
+	 *
+	 * We try to minimise the use of overclocking to only when
+	 * it's really necessary : only when we can't reach
+	 * the speed through the prescaler.
+	 *
+	 * The 4 lowest bit of TCR represent the value of the clock
+	 * sampling. 0x0 in fact mean 0x10 = 16, which is the default
+	 * (550 compatible), and only values in the range 4 to 16 are
+	 * supported (0x4 -> 0xF + 0x0).
+	 * Moreover, it seems that only 0x4, 0x8 and 0x0 are safe with
+	 * older revisions of the 16C950.
+	 */
+	if (baud <= baud_base)
+		current_tcr = 0x0;	/* Sampling clock = 16 = standard */
+	else if (baud <= 2*baud_base) {
+		current_tcr = 0x8;	/* Sampling clock = 8 = 2x overclock */
+		baud_base = baud_base * 2;
+	} else if (baud <= 4*baud_base) {
+		current_tcr = 0x4;	/* Sampling clock = 4 = 4x overclock */
+		baud_base = baud_base * 4;
+	} else {
+		/* Should do better ??? */
+		printk(KERN_DEBUG "16C950 : Can't reach desired speed %d\n",
+		       baud);
+		current_tcr = 0x0;	/* Sampling clock = 16 = standard */
+
+		/* Hack for Brainbox BlueTooth Pcmcia cards - don't tell */
+		if((baud == 921600) && (baud_base == 115200)) {
+			printk(KERN_DEBUG "16C950 : Brainbox kludge enabled - yuck !\n");
+			info->CPR = 0x4;	/* Out of spec ??? */
+			serial_icr_write(info, UART_CPR, info->CPR);
+			current_tcr = 0x4;
+			baud_base = baud_base * 8;
+		}
+	}
+	serial_icr_write(info, UART_TCR, current_tcr);
+#ifdef DEBUG_16C950_CPR
+	printk(KERN_DEBUG "16C950 new baud_base = %ld, TCR = 0x%X\n", baud_base, current_tcr);
+#endif
+
+	/* Return the aparent baud_base */
+	return(baud_base);
+}
+
+/*
  * This routine is called to set the UART divisor registers to match
  * the specified baud rate for a serial port.
  */
@@ -1654,16 +1792,8 @@ static void change_speed(struct async_st
 #endif
 	baud_base = info->state->baud_base;
 	if (info->state->type == PORT_16C950) {
-		if (baud <= baud_base)
-			serial_icr_write(info, UART_TCR, 0);
-		else if (baud <= 2*baud_base) {
-			serial_icr_write(info, UART_TCR, 0x8);
-			baud_base = baud_base * 2;
-		} else if (baud <= 4*baud_base) {
-			serial_icr_write(info, UART_TCR, 0x4);
-			baud_base = baud_base * 4;
-		} else
-			serial_icr_write(info, UART_TCR, 0);
+		/* Special processing for 16C950 */
+		baud_base = set_extra_speed_950(info, baud);
 	}
 	if (baud == 38400 &&
 	    ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_CUST))