Skip to content

Commit 0d1f22e

Browse files
albrechtdglikely
authored andcommitted
powerpc/5200: improve uart baud rate calculation (reach high baud rates, better accuracy)
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 in a new set_baudrate method. Notes: only "fsl,mpc5200b-psc-uart" compatible devices benefit from these improvements. Tested on a custom 5200B based board, from 110 baud up to 3 MBaud, and with both "fsl,mpc5200b-psc-uart" and "fsl,mpc5200-psc-uart" devices. Also tested on the mpc5121ads board. Signed-off-by: Albrecht Dreß <[email protected]> [agust: fixed mpc5121 prescaler comment] Signed-off-by: Anatolij Gustschin <[email protected]> Signed-off-by: Grant Likely <[email protected]>
1 parent b37fa16 commit 0d1f22e

File tree

1 file changed

+116
-29
lines changed

1 file changed

+116
-29
lines changed

drivers/serial/mpc52xx_uart.c

Lines changed: 116 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -113,24 +113,33 @@ struct psc_ops {
113113
unsigned char (*read_char)(struct uart_port *port);
114114
void (*cw_disable_ints)(struct uart_port *port);
115115
void (*cw_restore_ints)(struct uart_port *port);
116-
unsigned long (*getuartclk)(void *p);
116+
unsigned int (*set_baudrate)(struct uart_port *port,
117+
struct ktermios *new,
118+
struct ktermios *old);
117119
int (*clock)(struct uart_port *port, int enable);
118120
int (*fifoc_init)(void);
119121
void (*fifoc_uninit)(void);
120122
void (*get_irq)(struct uart_port *, struct device_node *);
121123
irqreturn_t (*handle_irq)(struct uart_port *port);
122124
};
123125

126+
/* setting the prescaler and divisor reg is common for all chips */
127+
static inline void mpc52xx_set_divisor(struct mpc52xx_psc __iomem *psc,
128+
u16 prescaler, unsigned int divisor)
129+
{
130+
/* select prescaler */
131+
out_be16(&psc->mpc52xx_psc_clock_select, prescaler);
132+
out_8(&psc->ctur, divisor >> 8);
133+
out_8(&psc->ctlr, divisor & 0xff);
134+
}
135+
124136
#ifdef CONFIG_PPC_MPC52xx
125137
#define FIFO_52xx(port) ((struct mpc52xx_psc_fifo __iomem *)(PSC(port)+1))
126138
static void mpc52xx_psc_fifo_init(struct uart_port *port)
127139
{
128140
struct mpc52xx_psc __iomem *psc = PSC(port);
129141
struct mpc52xx_psc_fifo __iomem *fifo = FIFO_52xx(port);
130142

131-
/* /32 prescaler */
132-
out_be16(&psc->mpc52xx_psc_clock_select, 0xdd00);
133-
134143
out_8(&fifo->rfcntl, 0x00);
135144
out_be16(&fifo->rfalarm, 0x1ff);
136145
out_8(&fifo->tfcntl, 0x07);
@@ -219,15 +228,47 @@ static void mpc52xx_psc_cw_restore_ints(struct uart_port *port)
219228
out_be16(&PSC(port)->mpc52xx_psc_imr, port->read_status_mask);
220229
}
221230

222-
/* Search for bus-frequency property in this node or a parent */
223-
static unsigned long mpc52xx_getuartclk(void *p)
231+
static unsigned int mpc5200_psc_set_baudrate(struct uart_port *port,
232+
struct ktermios *new,
233+
struct ktermios *old)
224234
{
225-
/*
226-
* 5200 UARTs have a / 32 prescaler
227-
* but the generic serial code assumes 16
228-
* so return ipb freq / 2
229-
*/
230-
return mpc5xxx_get_bus_frequency(p) / 2;
235+
unsigned int baud;
236+
unsigned int divisor;
237+
238+
/* The 5200 has a fixed /32 prescaler, uartclk contains the ipb freq */
239+
baud = uart_get_baud_rate(port, new, old,
240+
port->uartclk / (32 * 0xffff) + 1,
241+
port->uartclk / 32);
242+
divisor = (port->uartclk + 16 * baud) / (32 * baud);
243+
244+
/* enable the /32 prescaler and set the divisor */
245+
mpc52xx_set_divisor(PSC(port), 0xdd00, divisor);
246+
return baud;
247+
}
248+
249+
static unsigned int mpc5200b_psc_set_baudrate(struct uart_port *port,
250+
struct ktermios *new,
251+
struct ktermios *old)
252+
{
253+
unsigned int baud;
254+
unsigned int divisor;
255+
u16 prescaler;
256+
257+
/* The 5200B has a selectable /4 or /32 prescaler, uartclk contains the
258+
* ipb freq */
259+
baud = uart_get_baud_rate(port, new, old,
260+
port->uartclk / (32 * 0xffff) + 1,
261+
port->uartclk / 4);
262+
divisor = (port->uartclk + 2 * baud) / (4 * baud);
263+
264+
/* select the proper prescaler and set the divisor */
265+
if (divisor > 0xffff) {
266+
divisor = (divisor + 4) / 8;
267+
prescaler = 0xdd00; /* /32 */
268+
} else
269+
prescaler = 0xff00; /* /4 */
270+
mpc52xx_set_divisor(PSC(port), prescaler, divisor);
271+
return baud;
231272
}
232273

233274
static void mpc52xx_psc_get_irq(struct uart_port *port, struct device_node *np)
@@ -258,7 +299,28 @@ static struct psc_ops mpc52xx_psc_ops = {
258299
.read_char = mpc52xx_psc_read_char,
259300
.cw_disable_ints = mpc52xx_psc_cw_disable_ints,
260301
.cw_restore_ints = mpc52xx_psc_cw_restore_ints,
261-
.getuartclk = mpc52xx_getuartclk,
302+
.set_baudrate = mpc5200_psc_set_baudrate,
303+
.get_irq = mpc52xx_psc_get_irq,
304+
.handle_irq = mpc52xx_psc_handle_irq,
305+
};
306+
307+
static struct psc_ops mpc5200b_psc_ops = {
308+
.fifo_init = mpc52xx_psc_fifo_init,
309+
.raw_rx_rdy = mpc52xx_psc_raw_rx_rdy,
310+
.raw_tx_rdy = mpc52xx_psc_raw_tx_rdy,
311+
.rx_rdy = mpc52xx_psc_rx_rdy,
312+
.tx_rdy = mpc52xx_psc_tx_rdy,
313+
.tx_empty = mpc52xx_psc_tx_empty,
314+
.stop_rx = mpc52xx_psc_stop_rx,
315+
.start_tx = mpc52xx_psc_start_tx,
316+
.stop_tx = mpc52xx_psc_stop_tx,
317+
.rx_clr_irq = mpc52xx_psc_rx_clr_irq,
318+
.tx_clr_irq = mpc52xx_psc_tx_clr_irq,
319+
.write_char = mpc52xx_psc_write_char,
320+
.read_char = mpc52xx_psc_read_char,
321+
.cw_disable_ints = mpc52xx_psc_cw_disable_ints,
322+
.cw_restore_ints = mpc52xx_psc_cw_restore_ints,
323+
.set_baudrate = mpc5200b_psc_set_baudrate,
262324
.get_irq = mpc52xx_psc_get_irq,
263325
.handle_irq = mpc52xx_psc_handle_irq,
264326
};
@@ -392,9 +454,35 @@ static void mpc512x_psc_cw_restore_ints(struct uart_port *port)
392454
out_be32(&FIFO_512x(port)->rximr, port->read_status_mask & 0x7f);
393455
}
394456

395-
static unsigned long mpc512x_getuartclk(void *p)
457+
static unsigned int mpc512x_psc_set_baudrate(struct uart_port *port,
458+
struct ktermios *new,
459+
struct ktermios *old)
396460
{
397-
return mpc5xxx_get_bus_frequency(p);
461+
unsigned int baud;
462+
unsigned int divisor;
463+
464+
/*
465+
* The "MPC5121e Microcontroller Reference Manual, Rev. 3" says on
466+
* pg. 30-10 that the chip supports a /32 and a /10 prescaler.
467+
* Furthermore, it states that "After reset, the prescaler by 10
468+
* for the UART mode is selected", but the reset register value is
469+
* 0x0000 which means a /32 prescaler. This is wrong.
470+
*
471+
* In reality using /32 prescaler doesn't work, as it is not supported!
472+
* Use /16 or /10 prescaler, see "MPC5121e Hardware Design Guide",
473+
* Chapter 4.1 PSC in UART Mode.
474+
* Calculate with a /16 prescaler here.
475+
*/
476+
477+
/* uartclk contains the ips freq */
478+
baud = uart_get_baud_rate(port, new, old,
479+
port->uartclk / (16 * 0xffff) + 1,
480+
port->uartclk / 16);
481+
divisor = (port->uartclk + 8 * baud) / (16 * baud);
482+
483+
/* enable the /16 prescaler and set the divisor */
484+
mpc52xx_set_divisor(PSC(port), 0xdd00, divisor);
485+
return baud;
398486
}
399487

400488
/* Init PSC FIFO Controller */
@@ -498,7 +586,7 @@ static struct psc_ops mpc512x_psc_ops = {
498586
.read_char = mpc512x_psc_read_char,
499587
.cw_disable_ints = mpc512x_psc_cw_disable_ints,
500588
.cw_restore_ints = mpc512x_psc_cw_restore_ints,
501-
.getuartclk = mpc512x_getuartclk,
589+
.set_baudrate = mpc512x_psc_set_baudrate,
502590
.clock = mpc512x_psc_clock,
503591
.fifoc_init = mpc512x_psc_fifoc_init,
504592
.fifoc_uninit = mpc512x_psc_fifoc_uninit,
@@ -666,8 +754,8 @@ mpc52xx_uart_set_termios(struct uart_port *port, struct ktermios *new,
666754
struct mpc52xx_psc __iomem *psc = PSC(port);
667755
unsigned long flags;
668756
unsigned char mr1, mr2;
669-
unsigned short ctr;
670-
unsigned int j, baud, quot;
757+
unsigned int j;
758+
unsigned int baud;
671759

672760
/* Prepare what we're gonna write */
673761
mr1 = 0;
@@ -704,16 +792,9 @@ mpc52xx_uart_set_termios(struct uart_port *port, struct ktermios *new,
704792
mr2 |= MPC52xx_PSC_MODE_TXCTS;
705793
}
706794

707-
baud = uart_get_baud_rate(port, new, old, 0, port->uartclk/16);
708-
quot = uart_get_divisor(port, baud);
709-
ctr = quot & 0xffff;
710-
711795
/* Get the lock */
712796
spin_lock_irqsave(&port->lock, flags);
713797

714-
/* Update the per-port timeout */
715-
uart_update_timeout(port, new->c_cflag, baud);
716-
717798
/* Do our best to flush TX & RX, so we don't lose anything */
718799
/* But we don't wait indefinitely ! */
719800
j = 5000000; /* Maximum wait */
@@ -737,8 +818,10 @@ mpc52xx_uart_set_termios(struct uart_port *port, struct ktermios *new,
737818
out_8(&psc->command, MPC52xx_PSC_SEL_MODE_REG_1);
738819
out_8(&psc->mode, mr1);
739820
out_8(&psc->mode, mr2);
740-
out_8(&psc->ctur, ctr >> 8);
741-
out_8(&psc->ctlr, ctr & 0xff);
821+
baud = psc_ops->set_baudrate(port, new, old);
822+
823+
/* Update the per-port timeout */
824+
uart_update_timeout(port, new->c_cflag, baud);
742825

743826
if (UART_ENABLE_MS(port, new->c_cflag))
744827
mpc52xx_uart_enable_ms(port);
@@ -1118,7 +1201,7 @@ mpc52xx_console_setup(struct console *co, char *options)
11181201
return ret;
11191202
}
11201203

1121-
uartclk = psc_ops->getuartclk(np);
1204+
uartclk = mpc5xxx_get_bus_frequency(np);
11221205
if (uartclk == 0) {
11231206
pr_debug("Could not find uart clock frequency!\n");
11241207
return -EINVAL;
@@ -1201,6 +1284,7 @@ static struct uart_driver mpc52xx_uart_driver = {
12011284

12021285
static struct of_device_id mpc52xx_uart_of_match[] = {
12031286
#ifdef CONFIG_PPC_MPC52xx
1287+
{ .compatible = "fsl,mpc5200b-psc-uart", .data = &mpc5200b_psc_ops, },
12041288
{ .compatible = "fsl,mpc5200-psc-uart", .data = &mpc52xx_psc_ops, },
12051289
/* binding used by old lite5200 device trees: */
12061290
{ .compatible = "mpc5200-psc-uart", .data = &mpc52xx_psc_ops, },
@@ -1233,7 +1317,10 @@ mpc52xx_uart_of_probe(struct of_device *op, const struct of_device_id *match)
12331317
pr_debug("Found %s assigned to ttyPSC%x\n",
12341318
mpc52xx_uart_nodes[idx]->full_name, idx);
12351319

1236-
uartclk = psc_ops->getuartclk(op->dev.of_node);
1320+
/* set the uart clock to the input clock of the psc, the different
1321+
* prescalers are taken into account in the set_baudrate() methods
1322+
* of the respective chip */
1323+
uartclk = mpc5xxx_get_bus_frequency(op->dev.of_node);
12371324
if (uartclk == 0) {
12381325
dev_dbg(&op->dev, "Could not find uart clock frequency!\n");
12391326
return -EINVAL;

0 commit comments

Comments
 (0)