diff options
author | Soren Brinkmann <soren.brinkmann@xilinx.com> | 2013-10-17 17:08:11 -0400 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2013-10-19 22:47:38 -0400 |
commit | c4b0510cc1571ff44e1d6024d92683d49a8bcfde (patch) | |
tree | b82057687f759d1f64664a177a7df96cc62b92bb | |
parent | e6b39bfd0db207d2e9f3f78468d18f529f3b7901 (diff) |
tty: xuartps: Dynamically adjust to input frequency changes
Add a clock notifier to dynamically handle frequency changes of the
input clock by reprogramming the UART in order to keep the baud rate
constant.
Signed-off-by: Soren Brinkmann <soren.brinkmann@xilinx.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
-rw-r--r-- | drivers/tty/serial/xilinx_uartps.c | 123 |
1 files changed, 119 insertions, 4 deletions
diff --git a/drivers/tty/serial/xilinx_uartps.c b/drivers/tty/serial/xilinx_uartps.c index 95e12c216984..82195040e906 100644 --- a/drivers/tty/serial/xilinx_uartps.c +++ b/drivers/tty/serial/xilinx_uartps.c | |||
@@ -163,13 +163,20 @@ MODULE_PARM_DESC(rx_timeout, "Rx timeout, 1-255"); | |||
163 | 163 | ||
164 | /** | 164 | /** |
165 | * struct xuartps - device data | 165 | * struct xuartps - device data |
166 | * @refclk Reference clock | 166 | * @port Pointer to the UART port |
167 | * @aperclk APB clock | 167 | * @refclk Reference clock |
168 | * @aperclk APB clock | ||
169 | * @baud Current baud rate | ||
170 | * @clk_rate_change_nb Notifier block for clock changes | ||
168 | */ | 171 | */ |
169 | struct xuartps { | 172 | struct xuartps { |
173 | struct uart_port *port; | ||
170 | struct clk *refclk; | 174 | struct clk *refclk; |
171 | struct clk *aperclk; | 175 | struct clk *aperclk; |
176 | unsigned int baud; | ||
177 | struct notifier_block clk_rate_change_nb; | ||
172 | }; | 178 | }; |
179 | #define to_xuartps(_nb) container_of(_nb, struct xuartps, clk_rate_change_nb); | ||
173 | 180 | ||
174 | /** | 181 | /** |
175 | * xuartps_isr - Interrupt handler | 182 | * xuartps_isr - Interrupt handler |
@@ -385,6 +392,7 @@ static unsigned int xuartps_set_baud_rate(struct uart_port *port, | |||
385 | u32 cd, bdiv; | 392 | u32 cd, bdiv; |
386 | u32 mreg; | 393 | u32 mreg; |
387 | int div8; | 394 | int div8; |
395 | struct xuartps *xuartps = port->private_data; | ||
388 | 396 | ||
389 | calc_baud = xuartps_calc_baud_divs(port->uartclk, baud, &bdiv, &cd, | 397 | calc_baud = xuartps_calc_baud_divs(port->uartclk, baud, &bdiv, &cd, |
390 | &div8); | 398 | &div8); |
@@ -398,10 +406,105 @@ static unsigned int xuartps_set_baud_rate(struct uart_port *port, | |||
398 | xuartps_writel(mreg, XUARTPS_MR_OFFSET); | 406 | xuartps_writel(mreg, XUARTPS_MR_OFFSET); |
399 | xuartps_writel(cd, XUARTPS_BAUDGEN_OFFSET); | 407 | xuartps_writel(cd, XUARTPS_BAUDGEN_OFFSET); |
400 | xuartps_writel(bdiv, XUARTPS_BAUDDIV_OFFSET); | 408 | xuartps_writel(bdiv, XUARTPS_BAUDDIV_OFFSET); |
409 | xuartps->baud = baud; | ||
401 | 410 | ||
402 | return calc_baud; | 411 | return calc_baud; |
403 | } | 412 | } |
404 | 413 | ||
414 | /** | ||
415 | * xuartps_clk_notitifer_cb - Clock notifier callback | ||
416 | * @nb: Notifier block | ||
417 | * @event: Notify event | ||
418 | * @data: Notifier data | ||
419 | * Returns NOTIFY_OK on success, NOTIFY_BAD on error. | ||
420 | */ | ||
421 | static int xuartps_clk_notifier_cb(struct notifier_block *nb, | ||
422 | unsigned long event, void *data) | ||
423 | { | ||
424 | u32 ctrl_reg; | ||
425 | struct uart_port *port; | ||
426 | int locked = 0; | ||
427 | struct clk_notifier_data *ndata = data; | ||
428 | unsigned long flags = 0; | ||
429 | struct xuartps *xuartps = to_xuartps(nb); | ||
430 | |||
431 | port = xuartps->port; | ||
432 | if (port->suspended) | ||
433 | return NOTIFY_OK; | ||
434 | |||
435 | switch (event) { | ||
436 | case PRE_RATE_CHANGE: | ||
437 | { | ||
438 | u32 bdiv; | ||
439 | u32 cd; | ||
440 | int div8; | ||
441 | |||
442 | /* | ||
443 | * Find out if current baud-rate can be achieved with new clock | ||
444 | * frequency. | ||
445 | */ | ||
446 | if (!xuartps_calc_baud_divs(ndata->new_rate, xuartps->baud, | ||
447 | &bdiv, &cd, &div8)) | ||
448 | return NOTIFY_BAD; | ||
449 | |||
450 | spin_lock_irqsave(&xuartps->port->lock, flags); | ||
451 | |||
452 | /* Disable the TX and RX to set baud rate */ | ||
453 | xuartps_writel(xuartps_readl(XUARTPS_CR_OFFSET) | | ||
454 | (XUARTPS_CR_TX_DIS | XUARTPS_CR_RX_DIS), | ||
455 | XUARTPS_CR_OFFSET); | ||
456 | |||
457 | spin_unlock_irqrestore(&xuartps->port->lock, flags); | ||
458 | |||
459 | return NOTIFY_OK; | ||
460 | } | ||
461 | case POST_RATE_CHANGE: | ||
462 | /* | ||
463 | * Set clk dividers to generate correct baud with new clock | ||
464 | * frequency. | ||
465 | */ | ||
466 | |||
467 | spin_lock_irqsave(&xuartps->port->lock, flags); | ||
468 | |||
469 | locked = 1; | ||
470 | port->uartclk = ndata->new_rate; | ||
471 | |||
472 | xuartps->baud = xuartps_set_baud_rate(xuartps->port, | ||
473 | xuartps->baud); | ||
474 | /* fall through */ | ||
475 | case ABORT_RATE_CHANGE: | ||
476 | if (!locked) | ||
477 | spin_lock_irqsave(&xuartps->port->lock, flags); | ||
478 | |||
479 | /* Set TX/RX Reset */ | ||
480 | xuartps_writel(xuartps_readl(XUARTPS_CR_OFFSET) | | ||
481 | (XUARTPS_CR_TXRST | XUARTPS_CR_RXRST), | ||
482 | XUARTPS_CR_OFFSET); | ||
483 | |||
484 | while (xuartps_readl(XUARTPS_CR_OFFSET) & | ||
485 | (XUARTPS_CR_TXRST | XUARTPS_CR_RXRST)) | ||
486 | cpu_relax(); | ||
487 | |||
488 | /* | ||
489 | * Clear the RX disable and TX disable bits and then set the TX | ||
490 | * enable bit and RX enable bit to enable the transmitter and | ||
491 | * receiver. | ||
492 | */ | ||
493 | xuartps_writel(rx_timeout, XUARTPS_RXTOUT_OFFSET); | ||
494 | ctrl_reg = xuartps_readl(XUARTPS_CR_OFFSET); | ||
495 | xuartps_writel( | ||
496 | (ctrl_reg & ~(XUARTPS_CR_TX_DIS | XUARTPS_CR_RX_DIS)) | | ||
497 | (XUARTPS_CR_TX_EN | XUARTPS_CR_RX_EN), | ||
498 | XUARTPS_CR_OFFSET); | ||
499 | |||
500 | spin_unlock_irqrestore(&xuartps->port->lock, flags); | ||
501 | |||
502 | return NOTIFY_OK; | ||
503 | default: | ||
504 | return NOTIFY_DONE; | ||
505 | } | ||
506 | } | ||
507 | |||
405 | /*----------------------Uart Operations---------------------------*/ | 508 | /*----------------------Uart Operations---------------------------*/ |
406 | 509 | ||
407 | /** | 510 | /** |
@@ -1164,13 +1267,19 @@ static int xuartps_probe(struct platform_device *pdev) | |||
1164 | goto err_out_clk_disable; | 1267 | goto err_out_clk_disable; |
1165 | } | 1268 | } |
1166 | 1269 | ||
1270 | xuartps_data->clk_rate_change_nb.notifier_call = | ||
1271 | xuartps_clk_notifier_cb; | ||
1272 | if (clk_notifier_register(xuartps_data->refclk, | ||
1273 | &xuartps_data->clk_rate_change_nb)) | ||
1274 | dev_warn(&pdev->dev, "Unable to register clock notifier.\n"); | ||
1275 | |||
1167 | /* Initialize the port structure */ | 1276 | /* Initialize the port structure */ |
1168 | port = xuartps_get_port(); | 1277 | port = xuartps_get_port(); |
1169 | 1278 | ||
1170 | if (!port) { | 1279 | if (!port) { |
1171 | dev_err(&pdev->dev, "Cannot get uart_port structure\n"); | 1280 | dev_err(&pdev->dev, "Cannot get uart_port structure\n"); |
1172 | rc = -ENODEV; | 1281 | rc = -ENODEV; |
1173 | goto err_out_clk_disable; | 1282 | goto err_out_notif_unreg; |
1174 | } else { | 1283 | } else { |
1175 | /* Register the port. | 1284 | /* Register the port. |
1176 | * This function also registers this device with the tty layer | 1285 | * This function also registers this device with the tty layer |
@@ -1181,16 +1290,20 @@ static int xuartps_probe(struct platform_device *pdev) | |||
1181 | port->dev = &pdev->dev; | 1290 | port->dev = &pdev->dev; |
1182 | port->uartclk = clk_get_rate(xuartps_data->refclk); | 1291 | port->uartclk = clk_get_rate(xuartps_data->refclk); |
1183 | port->private_data = xuartps_data; | 1292 | port->private_data = xuartps_data; |
1293 | xuartps_data->port = port; | ||
1184 | platform_set_drvdata(pdev, port); | 1294 | platform_set_drvdata(pdev, port); |
1185 | rc = uart_add_one_port(&xuartps_uart_driver, port); | 1295 | rc = uart_add_one_port(&xuartps_uart_driver, port); |
1186 | if (rc) { | 1296 | if (rc) { |
1187 | dev_err(&pdev->dev, | 1297 | dev_err(&pdev->dev, |
1188 | "uart_add_one_port() failed; err=%i\n", rc); | 1298 | "uart_add_one_port() failed; err=%i\n", rc); |
1189 | goto err_out_clk_disable; | 1299 | goto err_out_notif_unreg; |
1190 | } | 1300 | } |
1191 | return 0; | 1301 | return 0; |
1192 | } | 1302 | } |
1193 | 1303 | ||
1304 | err_out_notif_unreg: | ||
1305 | clk_notifier_unregister(xuartps_data->refclk, | ||
1306 | &xuartps_data->clk_rate_change_nb); | ||
1194 | err_out_clk_disable: | 1307 | err_out_clk_disable: |
1195 | clk_disable_unprepare(xuartps_data->refclk); | 1308 | clk_disable_unprepare(xuartps_data->refclk); |
1196 | err_out_clk_dis_aper: | 1309 | err_out_clk_dis_aper: |
@@ -1212,6 +1325,8 @@ static int xuartps_remove(struct platform_device *pdev) | |||
1212 | int rc; | 1325 | int rc; |
1213 | 1326 | ||
1214 | /* Remove the xuartps port from the serial core */ | 1327 | /* Remove the xuartps port from the serial core */ |
1328 | clk_notifier_unregister(xuartps_data->refclk, | ||
1329 | &xuartps_data->clk_rate_change_nb); | ||
1215 | rc = uart_remove_one_port(&xuartps_uart_driver, port); | 1330 | rc = uart_remove_one_port(&xuartps_uart_driver, port); |
1216 | port->mapbase = 0; | 1331 | port->mapbase = 0; |
1217 | clk_disable_unprepare(xuartps_data->refclk); | 1332 | clk_disable_unprepare(xuartps_data->refclk); |