aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-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 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) 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 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