linuxppc-dev.lists.ozlabs.org archive mirror
 help / color / mirror / Atom feed
* [Patch] mpc5200b: improve baud rate calculation (reach high baud rates, better accuracy)
@ 2010-03-01 18:11 Albrecht Dreß
  2010-03-02  0:32 ` Wolfram Sang
  2010-03-02 20:22 ` Grant Likely
  0 siblings, 2 replies; 9+ messages in thread
From: Albrecht Dreß @ 2010-03-01 18:11 UTC (permalink / raw)
  To: Linux PPC Development, Likely, Grant

On the MPC5200B, select the baud rate prescaler as /4 by default to make ve=
ry
high baud rates (e.g. 3 MBaud) accessible and to achieve a higher precision
for high baud rates in general. For baud rates below ~500 Baud, the code wi=
ll
automatically fall back to the /32 prescaler.  The original MPC5200 does on=
ly
have a /32 prescaler which is detected only once and stored in a global.  A
new chip-dependent method is used to set the divisor.

Tested on a custom 5200B based board, with up to 3 MBaud.

Signed-off-by: Albrecht Dre=DF <albrecht.dress@arcor.de>

---

--- linux-2.6.33/drivers/serial/mpc52xx_uart.c.orig	2010-02-24 19:52:17.000=
000000 +0100
+++ linux-2.6.33/drivers/serial/mpc52xx_uart.c	2010-02-26 21:12:51.00000000=
0 +0100
@@ -144,9 +144,17 @@ struct psc_ops {
 	unsigned char	(*read_char)(struct uart_port *port);
 	void		(*cw_disable_ints)(struct uart_port *port);
 	void		(*cw_restore_ints)(struct uart_port *port);
+	void		(*set_divisor)(struct uart_port *port,
+				       unsigned int divisor);
 	unsigned long	(*getuartclk)(void *p);
 };
=20
+/* We need to distinguish between the MPC5200 which has only a /32 prescal=
er,
+ * and the MPC5200B which has a /32 and a /4 prescaler.  The global is fin=
e,
+ * as the chip can be only either a 5200B or not. */
+static int is_mpc5200b =3D -1;
+
+
 #ifdef CONFIG_PPC_MPC52xx
 #define FIFO_52xx(port) ((struct mpc52xx_psc_fifo __iomem *)(PSC(port)+1))
 static void mpc52xx_psc_fifo_init(struct uart_port *port)
@@ -154,9 +162,6 @@ static void mpc52xx_psc_fifo_init(struct
 	struct mpc52xx_psc __iomem *psc =3D PSC(port);
 	struct mpc52xx_psc_fifo __iomem *fifo =3D FIFO_52xx(port);
=20
-	/* /32 prescaler */
-	out_be16(&psc->mpc52xx_psc_clock_select, 0xdd00);
-
 	out_8(&fifo->rfcntl, 0x00);
 	out_be16(&fifo->rfalarm, 0x1ff);
 	out_8(&fifo->tfcntl, 0x07);
@@ -245,15 +250,40 @@ static void mpc52xx_psc_cw_restore_ints(
 	out_be16(&PSC(port)->mpc52xx_psc_imr, port->read_status_mask);
 }
=20
+static void mpc52xx_psc_set_divisor(struct uart_port *port,
+				    unsigned int divisor)
+{
+	struct mpc52xx_psc __iomem *psc =3D PSC(port);
+
+	/* prescaler */
+	if (is_mpc5200b !=3D 1)
+		out_be16(&psc->mpc52xx_psc_clock_select, 0xdd00); /* /32 */
+	else if (divisor > 0xffff) {
+		out_be16(&psc->mpc52xx_psc_clock_select, 0xdd00); /* /32 */
+		divisor =3D (divisor + 4) / 8;
+	} else
+		out_be16(&psc->mpc52xx_psc_clock_select, 0xff00); /* /4 */
+
+	/* ctr */
+	divisor &=3D 0xffff;
+	out_8(&psc->ctur, divisor >> 8);
+	out_8(&psc->ctlr, divisor & 0xff);
+}
+
 /* Search for bus-frequency property in this node or a parent */
 static unsigned long mpc52xx_getuartclk(void *p)
 {
 	/*
-	 * 5200 UARTs have a / 32 prescaler
-	 * but the generic serial code assumes 16
-	 * so return ipb freq / 2
+	 * The 5200 has only /32 prescalers.
+	 * 5200B UARTs have a /4 or a /32 prescaler.  For higher accuracy, we
+	 * do all calculations using the /4 prescaler for this chip.
+	 * The generic serial code assumes /16 so return ipb freq / 2 (5200)
+	 * or ipb freq * 4 (5200B).
 	 */
-	return mpc5xxx_get_bus_frequency(p) / 2;
+	if (is_mpc5200b =3D=3D 1)
+		return mpc5xxx_get_bus_frequency(p) * 4;
+	else
+		return mpc5xxx_get_bus_frequency(p) / 2;
 }
=20
 static struct psc_ops mpc52xx_psc_ops =3D {
@@ -272,6 +302,7 @@ static struct psc_ops mpc52xx_psc_ops =3D=20
 	.read_char =3D mpc52xx_psc_read_char,
 	.cw_disable_ints =3D mpc52xx_psc_cw_disable_ints,
 	.cw_restore_ints =3D mpc52xx_psc_cw_restore_ints,
+	.set_divisor =3D mpc52xx_psc_set_divisor,
 	.getuartclk =3D mpc52xx_getuartclk,
 };
=20
@@ -388,6 +419,16 @@ static void mpc512x_psc_cw_restore_ints(
 	out_be32(&FIFO_512x(port)->rximr, port->read_status_mask & 0x7f);
 }
=20
+static void mpc512x_psc_set_divisor(struct uart_port *port,
+				    unsigned int divisor)
+{
+	struct mpc52xx_psc __iomem *psc =3D PSC(port);
+
+	divisor &=3D 0xffff;
+	out_8(&psc->ctur, divisor >> 8);
+	out_8(&psc->ctlr, divisor & 0xff);
+}
+
 static unsigned long mpc512x_getuartclk(void *p)
 {
 	return mpc5xxx_get_bus_frequency(p);
@@ -409,6 +450,7 @@ static struct psc_ops mpc512x_psc_ops =3D=20
 	.read_char =3D mpc512x_psc_read_char,
 	.cw_disable_ints =3D mpc512x_psc_cw_disable_ints,
 	.cw_restore_ints =3D mpc512x_psc_cw_restore_ints,
+	.set_divisor =3D mpc512x_psc_set_divisor,
 	.getuartclk =3D mpc512x_getuartclk,
 };
 #endif
@@ -564,7 +606,6 @@ mpc52xx_uart_set_termios(struct uart_por
 	struct mpc52xx_psc __iomem *psc =3D PSC(port);
 	unsigned long flags;
 	unsigned char mr1, mr2;
-	unsigned short ctr;
 	unsigned int j, baud, quot;
=20
 	/* Prepare what we're gonna write */
@@ -604,7 +645,6 @@ mpc52xx_uart_set_termios(struct uart_por
=20
 	baud =3D uart_get_baud_rate(port, new, old, 0, port->uartclk/16);
 	quot =3D uart_get_divisor(port, baud);
-	ctr =3D quot & 0xffff;
=20
 	/* Get the lock */
 	spin_lock_irqsave(&port->lock, flags);
@@ -635,8 +675,7 @@ mpc52xx_uart_set_termios(struct uart_por
 	out_8(&psc->command, MPC52xx_PSC_SEL_MODE_REG_1);
 	out_8(&psc->mode, mr1);
 	out_8(&psc->mode, mr2);
-	out_8(&psc->ctur, ctr >> 8);
-	out_8(&psc->ctlr, ctr & 0xff);
+	psc_ops->set_divisor(port, quot);
=20
 	if (UART_ENABLE_MS(port, new->c_cflag))
 		mpc52xx_uart_enable_ms(port);
@@ -1113,6 +1152,19 @@ mpc52xx_uart_of_probe(struct of_device *
=20
 	dev_dbg(&op->dev, "mpc52xx_uart_probe(op=3D%p, match=3D%p)\n", op, match)=
;
=20
+	/* Check only once if we are running on a mpc5200b or not */
+	if (is_mpc5200b =3D=3D -1) {
+		struct device_node *np;
+
+		np =3D of_find_compatible_node(NULL, NULL, "fsl,mpc5200b-immr");
+		if (np) {
+			is_mpc5200b =3D 1;
+			dev_dbg(&op->dev, "mpc5200b: using /4 prescaler\n");
+			of_node_put(np);
+		} else
+			is_mpc5200b =3D 0;
+	}
+
 	/* Check validity & presence */
 	for (idx =3D 0; idx < MPC52xx_PSC_MAXNUM; idx++)
 		if (mpc52xx_uart_nodes[idx] =3D=3D op->node)

^ permalink raw reply	[flat|nested] 9+ messages in thread
* Re: [Patch] mpc5200b: improve baud rate calculation (reach high baud rates, better accuracy)
@ 2010-03-02  8:09 Albrecht Dreß
  2010-03-02  8:28 ` Wolfram Sang
                   ` (2 more replies)
  0 siblings, 3 replies; 9+ messages in thread
From: Albrecht Dreß @ 2010-03-02  8:09 UTC (permalink / raw)
  To: w.sang; +Cc: linuxppc-dev, devicetree-discuss

Hi Wolfram!

Thanks a lot for your comments!

[snip]
> > + * as the chip can be only either a 5200B or not. */
> > +static int is_mpc5200b =3D -1;
> > +
> > +
>=20
> One empty line too much. Maybe we can also get rid of the static later in
> the
> process, but first things first.

Ooops....

[snip]
> > +=09if (is_mpc5200b =3D=3D 1)
> > +=09=09return mpc5xxx_get_bus_frequency(p) * 4;
> > +=09else
> > +=09=09return mpc5xxx_get_bus_frequency(p) / 2;
>=20
> Isn't this wrong? You can also have /32 on the 5200B (the fallback).

Yes, but I do all /calculations/ with the /4 prescaler for higher accuracy.=
  If the divisor exceeds the available 16 bits of the counter reg, I round =
(divisor / 8) to use the /32 prescaler.  Think of a 19-bit counter value, w=
here I can choose to use either the lower or the higher 16 bits for the cou=
nter reg.  Remember also that using the higher 16 bits (/32 prescaler) is p=
robably the exceptional case - with an IPB frequency of 132 MHz this will h=
appen only for standard baud rates B300 and slower.

[snip]
> > +=09/* Check only once if we are running on a mpc5200b or not */
> > +=09if (is_mpc5200b =3D=3D -1) {
> > +=09=09struct device_node *np;
> > +
> > +=09=09np =3D of_find_compatible_node(NULL, NULL, "fsl,mpc5200b-immr");
>=20
> This should be handled using a new compatible-entry
> "fsl,mpc5200b-psc-uart".

I agree that this would be a lot cleaner, but it's also a lot more intrusiv=
e.  CC'ing the device tree discussion list here... comments, please!!

> > +=09=09if (np) {
> > +=09=09=09is_mpc5200b =3D 1;
> > +=09=09=09dev_dbg(&op->dev, "mpc5200b: using /4 prescaler\n");
>=20
> Does this message respect the fallback case?

See comment above...

> You could also have a set_divisor-function for 5200 and 5200B and set it
> here
> in the function struct (one reason less for the static ;))

Hmmm, but then I would need a 'static struct psc_ops mpc5200b_psc_ops', whe=
re only two functions differ from the generic 52xx struct as it is implemen=
ted now.  Using the static int needs less space.  However, in combination w=
ith the new compatible entry, it would of course make sense.

Again, any insight from the device tree gurus would be appreciated!

Thanks, Albrecht.

Tolle Dekollet=E9s oder scharfe Tatoos? Vote jetzt ... oder mach selbst mit=
 und zeige Deine Schokoladenseite
bei Topp oder Hopp von Arcor: http://www.arcor.de/rd/footer.toh

^ permalink raw reply	[flat|nested] 9+ messages in thread

end of thread, other threads:[~2010-03-02 20:22 UTC | newest]

Thread overview: 9+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2010-03-01 18:11 [Patch] mpc5200b: improve baud rate calculation (reach high baud rates, better accuracy) Albrecht Dreß
2010-03-02  0:32 ` Wolfram Sang
2010-03-02 20:22 ` Grant Likely
  -- strict thread matches above, loose matches on Subject: below --
2010-03-02  8:09 Albrecht Dreß
2010-03-02  8:28 ` Wolfram Sang
2010-03-02  8:56 ` Albrecht Dreß
2010-03-02 15:27   ` Wolfram Sang
2010-03-02 20:12   ` Grant Likely
2010-03-02 20:06 ` Grant Likely

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).