From: "Albrecht Dreß" <albrecht.dress@arcor.de>
To: Linux PPC Development <linuxppc-dev@ozlabs.org>,
"Likely, Grant" <grant.likely@secretlab.ca>
Subject: [Patch v.2] mpc5200b/uart: improve baud rate calculation (reach high baud rates, better accuracy)
Date: Wed, 03 Mar 2010 19:23:03 +0100 [thread overview]
Message-ID: <1267640583.4760.0@antares> (raw)
On the MPC5200B, make very high baud rates (e.g. 3 MBaud) accessible and
achieve a higher precision for high baud rates in general. This is done by
selecting the appropriate prescaler (/4 or /32). As to keep the code clean=
,
the getuartclk method has been dropped, and all calculations are done with =
a
"virtual" /4 prescaler for all chips for maximum accuracy. A new set_divis=
or
method scales down the divisor values found by the generic serial code
appropriately.
Note: only "fsl,mpc5200b-psc-uart" compatible devices benefit from these
improvements.
Tested on a custom 5200B based board, with up to 3 MBaud, and with both
"fsl,mpc5200b-psc-uart" and "fsl,mpc5200-psc-uart" devices.
Signed-off-by: Albrecht Dre=DF <albrecht.dress@arcor.de>
---
Changes vs. v.1: include improvements suggested by Wolfram and Grant (thank=
s a
lot for your helpful input!!): drop getuartclk method and use the highest
possible frequency for calculation, use new psc_ops for the 5200b, let the
set_divisor method do all the dirty work, emit warnings if bad divisor valu=
es
have been selected.
=20
--- linux-2.6.33-orig/drivers/serial/mpc52xx_uart.c 2010-02-24 19:52:17.000=
000000 +0100
+++ linux-2.6.33/drivers/serial/mpc52xx_uart.c 2010-03-03 10:52:04.00000000=
0 +0100
@@ -144,7 +144,8 @@ 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);
- unsigned long (*getuartclk)(void *p);
+ void (*set_divisor)(struct uart_port *port,
+ unsigned int divisor);
};
=20
#ifdef CONFIG_PPC_MPC52xx
@@ -154,9 +155,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 +243,55 @@ static void mpc52xx_psc_cw_restore_ints(
out_be16(&PSC(port)->mpc52xx_psc_imr, port->read_status_mask);
}
=20
-/* Search for bus-frequency property in this node or a parent */
-static unsigned long mpc52xx_getuartclk(void *p)
+static void mpc52xx_psc_set_divisor(struct uart_port *port,
+ unsigned int divisor)
{
- /*
- * 5200 UARTs have a / 32 prescaler
- * but the generic serial code assumes 16
- * so return ipb freq / 2
- */
- return mpc5xxx_get_bus_frequency(p) / 2;
+ struct mpc52xx_psc __iomem *psc =3D PSC(port);
+
+ /* adjust divisor for a /32 prescaler; see note in
+ * mpc52xx_uart_of_probe() */
+ divisor =3D (divisor + 4) / 8;
+ if (divisor > 0xffff) {
+ pr_warning("%s: divisor overflow (%x), use 0xffff\n", __func__,
+ divisor);
+ divisor =3D 0xffff;
+ } else if (divisor =3D=3D 0) {
+ pr_warning("%s: divisor 0, use 1\n", __func__);
+ divisor =3D 1;
+ }
+
+ /* prescaler */
+ out_be16(&psc->mpc52xx_psc_clock_select, 0xdd00); /* /32 */
+
+ /* ctr */
+ out_8(&psc->ctur, divisor >> 8);
+ out_8(&psc->ctlr, divisor & 0xff);
+}
+
+static void mpc5200b_psc_set_divisor(struct uart_port *port,
+ unsigned int divisor)
+{
+ struct mpc52xx_psc __iomem *psc =3D PSC(port);
+
+ /* set prescaler; see note in mpc52xx_uart_of_probe() */
+ if (divisor > 0xffff) {
+ divisor =3D (divisor + 4) / 8;
+ out_be16(&psc->mpc52xx_psc_clock_select, 0xdd00); /* /32 */
+ } else
+ out_be16(&psc->mpc52xx_psc_clock_select, 0xff00); /* /4 */
+
+ if (divisor > 0xffff) {
+ pr_warning("%s: divisor overflow (%x), use 0xffff\n", __func__,
+ divisor);
+ divisor =3D 0xffff;
+ } else if (divisor =3D=3D 0) {
+ pr_warning("%s: divisor 0, use 1\n", __func__);
+ divisor =3D 1;
+ }
+
+ /* ctr */
+ out_8(&psc->ctur, divisor >> 8);
+ out_8(&psc->ctlr, divisor & 0xff);
}
=20
static struct psc_ops mpc52xx_psc_ops =3D {
@@ -272,7 +310,26 @@ 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,
- .getuartclk =3D mpc52xx_getuartclk,
+ .set_divisor =3D mpc52xx_psc_set_divisor,
+};
+
+static struct psc_ops mpc5200b_psc_ops =3D {
+ .fifo_init =3D mpc52xx_psc_fifo_init,
+ .raw_rx_rdy =3D mpc52xx_psc_raw_rx_rdy,
+ .raw_tx_rdy =3D mpc52xx_psc_raw_tx_rdy,
+ .rx_rdy =3D mpc52xx_psc_rx_rdy,
+ .tx_rdy =3D mpc52xx_psc_tx_rdy,
+ .tx_empty =3D mpc52xx_psc_tx_empty,
+ .stop_rx =3D mpc52xx_psc_stop_rx,
+ .start_tx =3D mpc52xx_psc_start_tx,
+ .stop_tx =3D mpc52xx_psc_stop_tx,
+ .rx_clr_irq =3D mpc52xx_psc_rx_clr_irq,
+ .tx_clr_irq =3D mpc52xx_psc_tx_clr_irq,
+ .write_char =3D mpc52xx_psc_write_char,
+ .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 mpc5200b_psc_set_divisor,
};
=20
#endif /* CONFIG_MPC52xx */
@@ -388,9 +445,25 @@ static void mpc512x_psc_cw_restore_ints(
out_be32(&FIFO_512x(port)->rximr, port->read_status_mask & 0x7f);
}
=20
-static unsigned long mpc512x_getuartclk(void *p)
+static void mpc512x_psc_set_divisor(struct uart_port *port,
+ unsigned int divisor)
{
- return mpc5xxx_get_bus_frequency(p);
+ struct mpc52xx_psc __iomem *psc =3D PSC(port);
+
+ /* adjust divisor for a /16 prescaler; see note in
+ * mpc52xx_uart_of_probe() */
+ divisor =3D (divisor + 2) / 4;
+ if (divisor > 0xffff) {
+ pr_warning("%s: divisor overflow (%x), use 0xffff\n", __func__,
+ divisor);
+ divisor =3D 0xffff;
+ } else if (divisor =3D=3D 0) {
+ pr_warning("%s: divisor 0, use 1\n", __func__);
+ divisor =3D 1;
+ }
+
+ out_8(&psc->ctur, divisor >> 8);
+ out_8(&psc->ctlr, divisor & 0xff);
}
=20
static struct psc_ops mpc512x_psc_ops =3D {
@@ -409,7 +482,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,
- .getuartclk =3D mpc512x_getuartclk,
+ .set_divisor =3D mpc512x_psc_set_divisor,
};
#endif
=20
@@ -564,7 +637,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 +676,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 +706,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);
@@ -1007,7 +1077,8 @@ mpc52xx_console_setup(struct console *co
return ret;
}
=20
- uartclk =3D psc_ops->getuartclk(np);
+ /* see remarks about the uart clock in mpc52xx_uart_of_probe() */
+ uartclk =3D mpc5xxx_get_bus_frequency(np) * 4;
if (uartclk =3D=3D 0) {
pr_debug("Could not find uart clock frequency!\n");
return -EINVAL;
@@ -1090,6 +1161,7 @@ static struct uart_driver mpc52xx_uart_d
=20
static struct of_device_id mpc52xx_uart_of_match[] =3D {
#ifdef CONFIG_PPC_MPC52xx
+ { .compatible =3D "fsl,mpc5200b-psc-uart", .data =3D &mpc5200b_psc_ops, }=
,
{ .compatible =3D "fsl,mpc5200-psc-uart", .data =3D &mpc52xx_psc_ops, },
/* binding used by old lite5200 device trees: */
{ .compatible =3D "mpc5200-psc-uart", .data =3D &mpc52xx_psc_ops, },
@@ -1122,7 +1194,24 @@ mpc52xx_uart_of_probe(struct of_device *
pr_debug("Found %s assigned to ttyPSC%x\n",
mpc52xx_uart_nodes[idx]->full_name, idx);
=20
- uartclk =3D psc_ops->getuartclk(op->node);
+ /*
+ * Note about the uart clock:
+ * This series of processors use the ipb clock frequency for the clock
+ * generation scaled down by prescalers and a 16-bit counter register:
+ * - the 5200 has a /32 prescaler
+ * - the 5200B has selectable /4 or /32 prescalers (i.e. the counter
+ * reg can be viewed as a 19-bit value, of which we can use either
+ * the upper or the lower 16 bits - in the latter case the three
+ * MSB's must of course be 0)
+ * - the 512x has a /16 prescaler
+ * The generic serial code assumes a prescaler of /16. As we want to
+ * achieve the maximum accuracy possible, we let the generic serial
+ * code perform all calculations with the /4 prescaler, i.e. we have
+ * to set the uart clock to ipb freq * 4 here. The set_divisor methods
+ * for the different chips are responsible for scaling down the divisor
+ * value appropriately.
+ */
+ uartclk =3D mpc5xxx_get_bus_frequency(op->node) * 4;
if (uartclk =3D=3D 0) {
dev_dbg(&op->dev, "Could not find uart clock frequency!\n");
return -EINVAL;
next reply other threads:[~2010-03-03 18:23 UTC|newest]
Thread overview: 4+ messages / expand[flat|nested] mbox.gz Atom feed top
2010-03-03 18:23 Albrecht Dreß [this message]
2010-03-03 21:07 ` [Patch v.2] mpc5200b/uart: improve baud rate calculation (reach high baud rates, better accuracy) Grant Likely
2010-03-04 9:56 ` Albrecht Dreß
2010-03-04 13:27 ` Grant Likely
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=1267640583.4760.0@antares \
--to=albrecht.dress@arcor.de \
--cc=grant.likely@secretlab.ca \
--cc=linuxppc-dev@ozlabs.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.