diff options
-rw-r--r-- | arch/arm/plat-samsung/include/plat/regs-serial.h | 5 | ||||
-rw-r--r-- | drivers/tty/serial/samsung.c | 207 | ||||
-rw-r--r-- | drivers/tty/serial/samsung.h | 4 |
3 files changed, 95 insertions, 121 deletions
diff --git a/arch/arm/plat-samsung/include/plat/regs-serial.h b/arch/arm/plat-samsung/include/plat/regs-serial.h index 720734847027..b493d8d0cc0c 100644 --- a/arch/arm/plat-samsung/include/plat/regs-serial.h +++ b/arch/arm/plat-samsung/include/plat/regs-serial.h | |||
@@ -222,6 +222,10 @@ | |||
222 | #define S5PV210_UFSTAT_RXSHIFT (0) | 222 | #define S5PV210_UFSTAT_RXSHIFT (0) |
223 | 223 | ||
224 | #define NO_NEED_CHECK_CLKSRC 1 | 224 | #define NO_NEED_CHECK_CLKSRC 1 |
225 | #define S3C2410_UCON_CLKSEL0 (1 << 0) | ||
226 | #define S3C2410_UCON_CLKSEL1 (1 << 1) | ||
227 | #define S3C2410_UCON_CLKSEL2 (1 << 2) | ||
228 | #define S3C2410_UCON_CLKSEL3 (1 << 3) | ||
225 | 229 | ||
226 | #ifndef __ASSEMBLY__ | 230 | #ifndef __ASSEMBLY__ |
227 | 231 | ||
@@ -257,6 +261,7 @@ struct s3c2410_uartcfg { | |||
257 | unsigned char unused; | 261 | unsigned char unused; |
258 | unsigned short flags; | 262 | unsigned short flags; |
259 | upf_t uart_flags; /* default uart flags */ | 263 | upf_t uart_flags; /* default uart flags */ |
264 | unsigned int clk_sel; | ||
260 | 265 | ||
261 | unsigned int has_fracval; | 266 | unsigned int has_fracval; |
262 | 267 | ||
diff --git a/drivers/tty/serial/samsung.c b/drivers/tty/serial/samsung.c index fc242b2fd368..dc5a4edbc450 100644 --- a/drivers/tty/serial/samsung.c +++ b/drivers/tty/serial/samsung.c | |||
@@ -49,6 +49,7 @@ | |||
49 | #include <mach/map.h> | 49 | #include <mach/map.h> |
50 | 50 | ||
51 | #include <plat/regs-serial.h> | 51 | #include <plat/regs-serial.h> |
52 | #include <plat/clock.h> | ||
52 | 53 | ||
53 | #include "samsung.h" | 54 | #include "samsung.h" |
54 | 55 | ||
@@ -558,133 +559,98 @@ static void s3c24xx_serial_pm(struct uart_port *port, unsigned int level, | |||
558 | * | 559 | * |
559 | */ | 560 | */ |
560 | 561 | ||
562 | #define MAX_CLK_NAME_LENGTH 15 | ||
561 | 563 | ||
562 | #define MAX_CLKS (8) | 564 | static inline int s3c24xx_serial_getsource(struct uart_port *port) |
563 | |||
564 | static struct s3c24xx_uart_clksrc tmp_clksrc = { | ||
565 | .name = "pclk", | ||
566 | .min_baud = 0, | ||
567 | .max_baud = 0, | ||
568 | .divisor = 1, | ||
569 | }; | ||
570 | |||
571 | static inline int | ||
572 | s3c24xx_serial_getsource(struct uart_port *port, struct s3c24xx_uart_clksrc *c) | ||
573 | { | 565 | { |
574 | struct s3c24xx_uart_info *info = s3c24xx_port_to_info(port); | 566 | struct s3c24xx_uart_info *info = s3c24xx_port_to_info(port); |
567 | unsigned int ucon; | ||
575 | 568 | ||
576 | return (info->get_clksrc)(port, c); | 569 | if (info->num_clks == 1) |
577 | } | 570 | return 0; |
578 | |||
579 | static inline int | ||
580 | s3c24xx_serial_setsource(struct uart_port *port, struct s3c24xx_uart_clksrc *c) | ||
581 | { | ||
582 | struct s3c24xx_uart_info *info = s3c24xx_port_to_info(port); | ||
583 | 571 | ||
584 | return (info->set_clksrc)(port, c); | 572 | ucon = rd_regl(port, S3C2410_UCON); |
573 | ucon &= info->clksel_mask; | ||
574 | return ucon >> info->clksel_shift; | ||
585 | } | 575 | } |
586 | 576 | ||
587 | struct baud_calc { | 577 | static void s3c24xx_serial_setsource(struct uart_port *port, |
588 | struct s3c24xx_uart_clksrc *clksrc; | 578 | unsigned int clk_sel) |
589 | unsigned int calc; | ||
590 | unsigned int divslot; | ||
591 | unsigned int quot; | ||
592 | struct clk *src; | ||
593 | }; | ||
594 | |||
595 | static int s3c24xx_serial_calcbaud(struct baud_calc *calc, | ||
596 | struct uart_port *port, | ||
597 | struct s3c24xx_uart_clksrc *clksrc, | ||
598 | unsigned int baud) | ||
599 | { | 579 | { |
600 | struct s3c24xx_uart_port *ourport = to_ourport(port); | 580 | struct s3c24xx_uart_info *info = s3c24xx_port_to_info(port); |
601 | unsigned long rate; | 581 | unsigned int ucon; |
602 | |||
603 | calc->src = clk_get(port->dev, clksrc->name); | ||
604 | if (calc->src == NULL || IS_ERR(calc->src)) | ||
605 | return 0; | ||
606 | |||
607 | rate = clk_get_rate(calc->src); | ||
608 | rate /= clksrc->divisor; | ||
609 | 582 | ||
610 | calc->clksrc = clksrc; | 583 | if (info->num_clks == 1) |
584 | return; | ||
611 | 585 | ||
612 | if (ourport->info->has_divslot) { | 586 | ucon = rd_regl(port, S3C2410_UCON); |
613 | unsigned long div = rate / baud; | 587 | if ((ucon & info->clksel_mask) >> info->clksel_shift == clk_sel) |
614 | 588 | return; | |
615 | /* The UDIVSLOT register on the newer UARTs allows us to | ||
616 | * get a divisor adjustment of 1/16th on the baud clock. | ||
617 | * | ||
618 | * We don't keep the UDIVSLOT value (the 16ths we calculated | ||
619 | * by not multiplying the baud by 16) as it is easy enough | ||
620 | * to recalculate. | ||
621 | */ | ||
622 | |||
623 | calc->quot = div / 16; | ||
624 | calc->calc = rate / div; | ||
625 | } else { | ||
626 | calc->quot = (rate + (8 * baud)) / (16 * baud); | ||
627 | calc->calc = (rate / (calc->quot * 16)); | ||
628 | } | ||
629 | 589 | ||
630 | calc->quot--; | 590 | ucon &= ~info->clksel_mask; |
631 | return 1; | 591 | ucon |= clk_sel << info->clksel_shift; |
592 | wr_regl(port, S3C2410_UCON, ucon); | ||
632 | } | 593 | } |
633 | 594 | ||
634 | static unsigned int s3c24xx_serial_getclk(struct uart_port *port, | 595 | static unsigned int s3c24xx_serial_getclk(struct s3c24xx_uart_port *ourport, |
635 | struct s3c24xx_uart_clksrc **clksrc, | 596 | unsigned int req_baud, struct clk **best_clk, |
636 | struct clk **clk, | 597 | unsigned int *clk_num) |
637 | unsigned int baud) | ||
638 | { | 598 | { |
639 | struct s3c2410_uartcfg *cfg = s3c24xx_port_to_cfg(port); | 599 | struct s3c24xx_uart_info *info = ourport->info; |
640 | struct s3c24xx_uart_clksrc *clkp; | 600 | struct clk *clk; |
641 | struct baud_calc res[MAX_CLKS]; | 601 | unsigned long rate; |
642 | struct baud_calc *resptr, *best, *sptr; | 602 | unsigned int cnt, baud, quot, clk_sel, best_quot = 0; |
643 | int i; | 603 | char clkname[MAX_CLK_NAME_LENGTH]; |
644 | 604 | int calc_deviation, deviation = (1 << 30) - 1; | |
645 | clkp = cfg->clocks; | 605 | |
646 | best = NULL; | 606 | *best_clk = NULL; |
647 | 607 | clk_sel = (ourport->cfg->clk_sel) ? ourport->cfg->clk_sel : | |
648 | if (cfg->clocks_size < 2) { | 608 | ourport->info->def_clk_sel; |
649 | if (cfg->clocks_size == 0) | 609 | for (cnt = 0; cnt < info->num_clks; cnt++) { |
650 | clkp = &tmp_clksrc; | 610 | if (!(clk_sel & (1 << cnt))) |
651 | 611 | continue; | |
652 | s3c24xx_serial_calcbaud(res, port, clkp, baud); | 612 | |
653 | best = res; | 613 | sprintf(clkname, "clk_uart_baud%d", cnt); |
654 | resptr = best + 1; | 614 | clk = clk_get(ourport->port.dev, clkname); |
655 | } else { | 615 | if (IS_ERR_OR_NULL(clk)) |
656 | resptr = res; | 616 | continue; |
657 | 617 | ||
658 | for (i = 0; i < cfg->clocks_size; i++, clkp++) { | 618 | rate = clk_get_rate(clk); |
659 | if (s3c24xx_serial_calcbaud(resptr, port, clkp, baud)) | 619 | if (!rate) |
660 | resptr++; | 620 | continue; |
621 | |||
622 | if (ourport->info->has_divslot) { | ||
623 | unsigned long div = rate / req_baud; | ||
624 | |||
625 | /* The UDIVSLOT register on the newer UARTs allows us to | ||
626 | * get a divisor adjustment of 1/16th on the baud clock. | ||
627 | * | ||
628 | * We don't keep the UDIVSLOT value (the 16ths we | ||
629 | * calculated by not multiplying the baud by 16) as it | ||
630 | * is easy enough to recalculate. | ||
631 | */ | ||
632 | |||
633 | quot = div / 16; | ||
634 | baud = rate / div; | ||
635 | } else { | ||
636 | quot = (rate + (8 * req_baud)) / (16 * req_baud); | ||
637 | baud = rate / (quot * 16); | ||
661 | } | 638 | } |
662 | } | 639 | quot--; |
663 | |||
664 | /* ok, we now need to select the best clock we found */ | ||
665 | |||
666 | if (!best) { | ||
667 | unsigned int deviation = (1<<30)|((1<<30)-1); | ||
668 | int calc_deviation; | ||
669 | 640 | ||
670 | for (sptr = res; sptr < resptr; sptr++) { | 641 | calc_deviation = req_baud - baud; |
671 | calc_deviation = baud - sptr->calc; | 642 | if (calc_deviation < 0) |
672 | if (calc_deviation < 0) | 643 | calc_deviation = -calc_deviation; |
673 | calc_deviation = -calc_deviation; | ||
674 | 644 | ||
675 | if (calc_deviation < deviation) { | 645 | if (calc_deviation < deviation) { |
676 | best = sptr; | 646 | *best_clk = clk; |
677 | deviation = calc_deviation; | 647 | best_quot = quot; |
678 | } | 648 | *clk_num = cnt; |
649 | deviation = calc_deviation; | ||
679 | } | 650 | } |
680 | } | 651 | } |
681 | 652 | ||
682 | /* store results to pass back */ | 653 | return best_quot; |
683 | |||
684 | *clksrc = best->clksrc; | ||
685 | *clk = best->src; | ||
686 | |||
687 | return best->quot; | ||
688 | } | 654 | } |
689 | 655 | ||
690 | /* udivslot_table[] | 656 | /* udivslot_table[] |
@@ -717,10 +683,9 @@ static void s3c24xx_serial_set_termios(struct uart_port *port, | |||
717 | { | 683 | { |
718 | struct s3c2410_uartcfg *cfg = s3c24xx_port_to_cfg(port); | 684 | struct s3c2410_uartcfg *cfg = s3c24xx_port_to_cfg(port); |
719 | struct s3c24xx_uart_port *ourport = to_ourport(port); | 685 | struct s3c24xx_uart_port *ourport = to_ourport(port); |
720 | struct s3c24xx_uart_clksrc *clksrc = NULL; | ||
721 | struct clk *clk = NULL; | 686 | struct clk *clk = NULL; |
722 | unsigned long flags; | 687 | unsigned long flags; |
723 | unsigned int baud, quot; | 688 | unsigned int baud, quot, clk_sel = 0; |
724 | unsigned int ulcon; | 689 | unsigned int ulcon; |
725 | unsigned int umcon; | 690 | unsigned int umcon; |
726 | unsigned int udivslot = 0; | 691 | unsigned int udivslot = 0; |
@@ -736,17 +701,16 @@ static void s3c24xx_serial_set_termios(struct uart_port *port, | |||
736 | */ | 701 | */ |
737 | 702 | ||
738 | baud = uart_get_baud_rate(port, termios, old, 0, 115200*8); | 703 | baud = uart_get_baud_rate(port, termios, old, 0, 115200*8); |
739 | 704 | quot = s3c24xx_serial_getclk(ourport, baud, &clk, &clk_sel); | |
740 | if (baud == 38400 && (port->flags & UPF_SPD_MASK) == UPF_SPD_CUST) | 705 | if (baud == 38400 && (port->flags & UPF_SPD_MASK) == UPF_SPD_CUST) |
741 | quot = port->custom_divisor; | 706 | quot = port->custom_divisor; |
742 | else | 707 | if (!clk) |
743 | quot = s3c24xx_serial_getclk(port, &clksrc, &clk, baud); | 708 | return; |
744 | 709 | ||
745 | /* check to see if we need to change clock source */ | 710 | /* check to see if we need to change clock source */ |
746 | 711 | ||
747 | if (ourport->clksrc != clksrc || ourport->baudclk != clk) { | 712 | if (ourport->baudclk != clk) { |
748 | dbg("selecting clock %p\n", clk); | 713 | s3c24xx_serial_setsource(port, clk_sel); |
749 | s3c24xx_serial_setsource(port, clksrc); | ||
750 | 714 | ||
751 | if (ourport->baudclk != NULL && !IS_ERR(ourport->baudclk)) { | 715 | if (ourport->baudclk != NULL && !IS_ERR(ourport->baudclk)) { |
752 | clk_disable(ourport->baudclk); | 716 | clk_disable(ourport->baudclk); |
@@ -755,7 +719,6 @@ static void s3c24xx_serial_set_termios(struct uart_port *port, | |||
755 | 719 | ||
756 | clk_enable(clk); | 720 | clk_enable(clk); |
757 | 721 | ||
758 | ourport->clksrc = clksrc; | ||
759 | ourport->baudclk = clk; | 722 | ourport->baudclk = clk; |
760 | ourport->baudclk_rate = clk ? clk_get_rate(clk) : 0; | 723 | ourport->baudclk_rate = clk ? clk_get_rate(clk) : 0; |
761 | } | 724 | } |
@@ -1202,7 +1165,7 @@ static ssize_t s3c24xx_serial_show_clksrc(struct device *dev, | |||
1202 | struct uart_port *port = s3c24xx_dev_to_port(dev); | 1165 | struct uart_port *port = s3c24xx_dev_to_port(dev); |
1203 | struct s3c24xx_uart_port *ourport = to_ourport(port); | 1166 | struct s3c24xx_uart_port *ourport = to_ourport(port); |
1204 | 1167 | ||
1205 | return snprintf(buf, PAGE_SIZE, "* %s\n", ourport->clksrc->name); | 1168 | return snprintf(buf, PAGE_SIZE, "* %s\n", ourport->baudclk->name); |
1206 | } | 1169 | } |
1207 | 1170 | ||
1208 | static DEVICE_ATTR(clock_source, S_IRUGO, s3c24xx_serial_show_clksrc, NULL); | 1171 | static DEVICE_ATTR(clock_source, S_IRUGO, s3c24xx_serial_show_clksrc, NULL); |
@@ -1382,12 +1345,13 @@ static void __init | |||
1382 | s3c24xx_serial_get_options(struct uart_port *port, int *baud, | 1345 | s3c24xx_serial_get_options(struct uart_port *port, int *baud, |
1383 | int *parity, int *bits) | 1346 | int *parity, int *bits) |
1384 | { | 1347 | { |
1385 | struct s3c24xx_uart_clksrc clksrc; | ||
1386 | struct clk *clk; | 1348 | struct clk *clk; |
1387 | unsigned int ulcon; | 1349 | unsigned int ulcon; |
1388 | unsigned int ucon; | 1350 | unsigned int ucon; |
1389 | unsigned int ubrdiv; | 1351 | unsigned int ubrdiv; |
1390 | unsigned long rate; | 1352 | unsigned long rate; |
1353 | unsigned int clk_sel; | ||
1354 | char clk_name[MAX_CLK_NAME_LENGTH]; | ||
1391 | 1355 | ||
1392 | ulcon = rd_regl(port, S3C2410_ULCON); | 1356 | ulcon = rd_regl(port, S3C2410_ULCON); |
1393 | ucon = rd_regl(port, S3C2410_UCON); | 1357 | ucon = rd_regl(port, S3C2410_UCON); |
@@ -1432,11 +1396,12 @@ s3c24xx_serial_get_options(struct uart_port *port, int *baud, | |||
1432 | 1396 | ||
1433 | /* now calculate the baud rate */ | 1397 | /* now calculate the baud rate */ |
1434 | 1398 | ||
1435 | s3c24xx_serial_getsource(port, &clksrc); | 1399 | clk_sel = s3c24xx_serial_getsource(port); |
1400 | sprintf(clk_name, "clk_uart_baud%d", clk_sel); | ||
1436 | 1401 | ||
1437 | clk = clk_get(port->dev, clksrc.name); | 1402 | clk = clk_get(port->dev, clk_name); |
1438 | if (!IS_ERR(clk) && clk != NULL) | 1403 | if (!IS_ERR(clk) && clk != NULL) |
1439 | rate = clk_get_rate(clk) / clksrc.divisor; | 1404 | rate = clk_get_rate(clk); |
1440 | else | 1405 | else |
1441 | rate = 1; | 1406 | rate = 1; |
1442 | 1407 | ||
diff --git a/drivers/tty/serial/samsung.h b/drivers/tty/serial/samsung.h index 6c9cb9d5ccdb..11369f3102c1 100644 --- a/drivers/tty/serial/samsung.h +++ b/drivers/tty/serial/samsung.h | |||
@@ -19,6 +19,10 @@ struct s3c24xx_uart_info { | |||
19 | unsigned long tx_fifomask; | 19 | unsigned long tx_fifomask; |
20 | unsigned long tx_fifoshift; | 20 | unsigned long tx_fifoshift; |
21 | unsigned long tx_fifofull; | 21 | unsigned long tx_fifofull; |
22 | unsigned int def_clk_sel; | ||
23 | unsigned long num_clks; | ||
24 | unsigned long clksel_mask; | ||
25 | unsigned long clksel_shift; | ||
22 | 26 | ||
23 | /* uart port features */ | 27 | /* uart port features */ |
24 | 28 | ||