diff options
author | Albrecht Dreß <albrecht.dress@arcor.de> | 2010-04-26 07:18:12 -0400 |
---|---|---|
committer | Grant Likely <grant.likely@secretlab.ca> | 2010-07-24 20:27:57 -0400 |
commit | 0d1f22e4907fec330ef0e475cb0dad48419498f2 (patch) | |
tree | 85a5da2e1c59ddba4f186fcf8b8eae6605f87729 /drivers | |
parent | b37fa16e78d6f9790462b3181602a26b5af36260 (diff) |
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ß <albrecht.dress@arcor.de>
[agust: fixed mpc5121 prescaler comment]
Signed-off-by: Anatolij Gustschin <agust@denx.de>
Signed-off-by: Grant Likely <grant.likely@secretlab.ca>
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/serial/mpc52xx_uart.c | 145 |
1 files changed, 116 insertions, 29 deletions
diff --git a/drivers/serial/mpc52xx_uart.c b/drivers/serial/mpc52xx_uart.c index 84a35f699016..1a88b363005c 100644 --- a/drivers/serial/mpc52xx_uart.c +++ b/drivers/serial/mpc52xx_uart.c | |||
@@ -113,7 +113,9 @@ struct psc_ops { | |||
113 | unsigned char (*read_char)(struct uart_port *port); | 113 | unsigned char (*read_char)(struct uart_port *port); |
114 | void (*cw_disable_ints)(struct uart_port *port); | 114 | void (*cw_disable_ints)(struct uart_port *port); |
115 | void (*cw_restore_ints)(struct uart_port *port); | 115 | 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); | ||
117 | int (*clock)(struct uart_port *port, int enable); | 119 | int (*clock)(struct uart_port *port, int enable); |
118 | int (*fifoc_init)(void); | 120 | int (*fifoc_init)(void); |
119 | void (*fifoc_uninit)(void); | 121 | void (*fifoc_uninit)(void); |
@@ -121,6 +123,16 @@ struct psc_ops { | |||
121 | irqreturn_t (*handle_irq)(struct uart_port *port); | 123 | irqreturn_t (*handle_irq)(struct uart_port *port); |
122 | }; | 124 | }; |
123 | 125 | ||
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 | |||
124 | #ifdef CONFIG_PPC_MPC52xx | 136 | #ifdef CONFIG_PPC_MPC52xx |
125 | #define FIFO_52xx(port) ((struct mpc52xx_psc_fifo __iomem *)(PSC(port)+1)) | 137 | #define FIFO_52xx(port) ((struct mpc52xx_psc_fifo __iomem *)(PSC(port)+1)) |
126 | static void mpc52xx_psc_fifo_init(struct uart_port *port) | 138 | static void mpc52xx_psc_fifo_init(struct uart_port *port) |
@@ -128,9 +140,6 @@ static void mpc52xx_psc_fifo_init(struct uart_port *port) | |||
128 | struct mpc52xx_psc __iomem *psc = PSC(port); | 140 | struct mpc52xx_psc __iomem *psc = PSC(port); |
129 | struct mpc52xx_psc_fifo __iomem *fifo = FIFO_52xx(port); | 141 | struct mpc52xx_psc_fifo __iomem *fifo = FIFO_52xx(port); |
130 | 142 | ||
131 | /* /32 prescaler */ | ||
132 | out_be16(&psc->mpc52xx_psc_clock_select, 0xdd00); | ||
133 | |||
134 | out_8(&fifo->rfcntl, 0x00); | 143 | out_8(&fifo->rfcntl, 0x00); |
135 | out_be16(&fifo->rfalarm, 0x1ff); | 144 | out_be16(&fifo->rfalarm, 0x1ff); |
136 | out_8(&fifo->tfcntl, 0x07); | 145 | out_8(&fifo->tfcntl, 0x07); |
@@ -219,15 +228,47 @@ static void mpc52xx_psc_cw_restore_ints(struct uart_port *port) | |||
219 | out_be16(&PSC(port)->mpc52xx_psc_imr, port->read_status_mask); | 228 | out_be16(&PSC(port)->mpc52xx_psc_imr, port->read_status_mask); |
220 | } | 229 | } |
221 | 230 | ||
222 | /* Search for bus-frequency property in this node or a parent */ | 231 | static unsigned int mpc5200_psc_set_baudrate(struct uart_port *port, |
223 | static unsigned long mpc52xx_getuartclk(void *p) | 232 | struct ktermios *new, |
233 | struct ktermios *old) | ||
224 | { | 234 | { |
225 | /* | 235 | unsigned int baud; |
226 | * 5200 UARTs have a / 32 prescaler | 236 | unsigned int divisor; |
227 | * but the generic serial code assumes 16 | 237 | |
228 | * so return ipb freq / 2 | 238 | /* The 5200 has a fixed /32 prescaler, uartclk contains the ipb freq */ |
229 | */ | 239 | baud = uart_get_baud_rate(port, new, old, |
230 | return mpc5xxx_get_bus_frequency(p) / 2; | 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; | ||
231 | } | 272 | } |
232 | 273 | ||
233 | static void mpc52xx_psc_get_irq(struct uart_port *port, struct device_node *np) | 274 | 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 = { | |||
258 | .read_char = mpc52xx_psc_read_char, | 299 | .read_char = mpc52xx_psc_read_char, |
259 | .cw_disable_ints = mpc52xx_psc_cw_disable_ints, | 300 | .cw_disable_ints = mpc52xx_psc_cw_disable_ints, |
260 | .cw_restore_ints = mpc52xx_psc_cw_restore_ints, | 301 | .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, | ||
262 | .get_irq = mpc52xx_psc_get_irq, | 324 | .get_irq = mpc52xx_psc_get_irq, |
263 | .handle_irq = mpc52xx_psc_handle_irq, | 325 | .handle_irq = mpc52xx_psc_handle_irq, |
264 | }; | 326 | }; |
@@ -392,9 +454,35 @@ static void mpc512x_psc_cw_restore_ints(struct uart_port *port) | |||
392 | out_be32(&FIFO_512x(port)->rximr, port->read_status_mask & 0x7f); | 454 | out_be32(&FIFO_512x(port)->rximr, port->read_status_mask & 0x7f); |
393 | } | 455 | } |
394 | 456 | ||
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) | ||
396 | { | 460 | { |
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; | ||
398 | } | 486 | } |
399 | 487 | ||
400 | /* Init PSC FIFO Controller */ | 488 | /* Init PSC FIFO Controller */ |
@@ -498,7 +586,7 @@ static struct psc_ops mpc512x_psc_ops = { | |||
498 | .read_char = mpc512x_psc_read_char, | 586 | .read_char = mpc512x_psc_read_char, |
499 | .cw_disable_ints = mpc512x_psc_cw_disable_ints, | 587 | .cw_disable_ints = mpc512x_psc_cw_disable_ints, |
500 | .cw_restore_ints = mpc512x_psc_cw_restore_ints, | 588 | .cw_restore_ints = mpc512x_psc_cw_restore_ints, |
501 | .getuartclk = mpc512x_getuartclk, | 589 | .set_baudrate = mpc512x_psc_set_baudrate, |
502 | .clock = mpc512x_psc_clock, | 590 | .clock = mpc512x_psc_clock, |
503 | .fifoc_init = mpc512x_psc_fifoc_init, | 591 | .fifoc_init = mpc512x_psc_fifoc_init, |
504 | .fifoc_uninit = mpc512x_psc_fifoc_uninit, | 592 | .fifoc_uninit = mpc512x_psc_fifoc_uninit, |
@@ -666,8 +754,8 @@ mpc52xx_uart_set_termios(struct uart_port *port, struct ktermios *new, | |||
666 | struct mpc52xx_psc __iomem *psc = PSC(port); | 754 | struct mpc52xx_psc __iomem *psc = PSC(port); |
667 | unsigned long flags; | 755 | unsigned long flags; |
668 | unsigned char mr1, mr2; | 756 | unsigned char mr1, mr2; |
669 | unsigned short ctr; | 757 | unsigned int j; |
670 | unsigned int j, baud, quot; | 758 | unsigned int baud; |
671 | 759 | ||
672 | /* Prepare what we're gonna write */ | 760 | /* Prepare what we're gonna write */ |
673 | mr1 = 0; | 761 | mr1 = 0; |
@@ -704,16 +792,9 @@ mpc52xx_uart_set_termios(struct uart_port *port, struct ktermios *new, | |||
704 | mr2 |= MPC52xx_PSC_MODE_TXCTS; | 792 | mr2 |= MPC52xx_PSC_MODE_TXCTS; |
705 | } | 793 | } |
706 | 794 | ||
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 | |||
711 | /* Get the lock */ | 795 | /* Get the lock */ |
712 | spin_lock_irqsave(&port->lock, flags); | 796 | spin_lock_irqsave(&port->lock, flags); |
713 | 797 | ||
714 | /* Update the per-port timeout */ | ||
715 | uart_update_timeout(port, new->c_cflag, baud); | ||
716 | |||
717 | /* Do our best to flush TX & RX, so we don't lose anything */ | 798 | /* Do our best to flush TX & RX, so we don't lose anything */ |
718 | /* But we don't wait indefinitely ! */ | 799 | /* But we don't wait indefinitely ! */ |
719 | j = 5000000; /* Maximum wait */ | 800 | j = 5000000; /* Maximum wait */ |
@@ -737,8 +818,10 @@ mpc52xx_uart_set_termios(struct uart_port *port, struct ktermios *new, | |||
737 | out_8(&psc->command, MPC52xx_PSC_SEL_MODE_REG_1); | 818 | out_8(&psc->command, MPC52xx_PSC_SEL_MODE_REG_1); |
738 | out_8(&psc->mode, mr1); | 819 | out_8(&psc->mode, mr1); |
739 | out_8(&psc->mode, mr2); | 820 | out_8(&psc->mode, mr2); |
740 | out_8(&psc->ctur, ctr >> 8); | 821 | baud = psc_ops->set_baudrate(port, new, old); |
741 | out_8(&psc->ctlr, ctr & 0xff); | 822 | |
823 | /* Update the per-port timeout */ | ||
824 | uart_update_timeout(port, new->c_cflag, baud); | ||
742 | 825 | ||
743 | if (UART_ENABLE_MS(port, new->c_cflag)) | 826 | if (UART_ENABLE_MS(port, new->c_cflag)) |
744 | mpc52xx_uart_enable_ms(port); | 827 | mpc52xx_uart_enable_ms(port); |
@@ -1118,7 +1201,7 @@ mpc52xx_console_setup(struct console *co, char *options) | |||
1118 | return ret; | 1201 | return ret; |
1119 | } | 1202 | } |
1120 | 1203 | ||
1121 | uartclk = psc_ops->getuartclk(np); | 1204 | uartclk = mpc5xxx_get_bus_frequency(np); |
1122 | if (uartclk == 0) { | 1205 | if (uartclk == 0) { |
1123 | pr_debug("Could not find uart clock frequency!\n"); | 1206 | pr_debug("Could not find uart clock frequency!\n"); |
1124 | return -EINVAL; | 1207 | return -EINVAL; |
@@ -1201,6 +1284,7 @@ static struct uart_driver mpc52xx_uart_driver = { | |||
1201 | 1284 | ||
1202 | static struct of_device_id mpc52xx_uart_of_match[] = { | 1285 | static struct of_device_id mpc52xx_uart_of_match[] = { |
1203 | #ifdef CONFIG_PPC_MPC52xx | 1286 | #ifdef CONFIG_PPC_MPC52xx |
1287 | { .compatible = "fsl,mpc5200b-psc-uart", .data = &mpc5200b_psc_ops, }, | ||
1204 | { .compatible = "fsl,mpc5200-psc-uart", .data = &mpc52xx_psc_ops, }, | 1288 | { .compatible = "fsl,mpc5200-psc-uart", .data = &mpc52xx_psc_ops, }, |
1205 | /* binding used by old lite5200 device trees: */ | 1289 | /* binding used by old lite5200 device trees: */ |
1206 | { .compatible = "mpc5200-psc-uart", .data = &mpc52xx_psc_ops, }, | 1290 | { .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) | |||
1233 | pr_debug("Found %s assigned to ttyPSC%x\n", | 1317 | pr_debug("Found %s assigned to ttyPSC%x\n", |
1234 | mpc52xx_uart_nodes[idx]->full_name, idx); | 1318 | mpc52xx_uart_nodes[idx]->full_name, idx); |
1235 | 1319 | ||
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); | ||
1237 | if (uartclk == 0) { | 1324 | if (uartclk == 0) { |
1238 | dev_dbg(&op->dev, "Could not find uart clock frequency!\n"); | 1325 | dev_dbg(&op->dev, "Could not find uart clock frequency!\n"); |
1239 | return -EINVAL; | 1326 | return -EINVAL; |