aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorThomas Abraham <thomas.abraham@linaro.org>2011-10-24 05:47:46 -0400
committerKukjin Kim <kgene.kim@samsung.com>2011-12-22 20:06:56 -0500
commit5f5a7a5578c5885201cf9c85856f023fe8b81765 (patch)
tree2145b345f6980d7ce94a6c7145b83be79921fb93
parent046c217c65a7670b4ee1aecdb9854284e32b2d6c (diff)
serial: samsung: switch to clkdev based clock lookup
Instead of using clock names supplied in platform data, use a generic clock name 'clk_uart_baud' to look up clocks. The platform code should register clocks with the name 'clk_uart_baud' which can be used by the baud rate generator. The clock lookup and selection of the best clock as baud rate clock is reworked. Platform code can specify the clocks that can be used as source for the baud clock (as supported previously by passing names of clocks). A new member is added to the platform data 'clk_sel' which holds a bit-field value with each bit representing a baud source clock. If a bit at any bit position is set, that clock is looked up to participate in the selection of the baud clock source. Cc: Ben Dooks <ben-linux@fluff.org> Signed-off-by: Thomas Abraham <thomas.abraham@linaro.org> Signed-off-by: Kukjin Kim <kgene.kim@samsung.com>
-rw-r--r--arch/arm/plat-samsung/include/plat/regs-serial.h5
-rw-r--r--drivers/tty/serial/samsung.c207
-rw-r--r--drivers/tty/serial/samsung.h4
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 72073484702..b493d8d0cc0 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 fc242b2fd36..dc5a4edbc45 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) 564static inline int s3c24xx_serial_getsource(struct uart_port *port)
563
564static struct s3c24xx_uart_clksrc tmp_clksrc = {
565 .name = "pclk",
566 .min_baud = 0,
567 .max_baud = 0,
568 .divisor = 1,
569};
570
571static inline int
572s3c24xx_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
579static inline int
580s3c24xx_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
587struct baud_calc { 577static 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
595static 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
634static unsigned int s3c24xx_serial_getclk(struct uart_port *port, 595static 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
1208static DEVICE_ATTR(clock_source, S_IRUGO, s3c24xx_serial_show_clksrc, NULL); 1171static DEVICE_ATTR(clock_source, S_IRUGO, s3c24xx_serial_show_clksrc, NULL);
@@ -1382,12 +1345,13 @@ static void __init
1382s3c24xx_serial_get_options(struct uart_port *port, int *baud, 1345s3c24xx_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 6c9cb9d5ccd..11369f3102c 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