diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2009-04-07 11:45:12 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2009-04-07 11:45:12 -0400 |
commit | 0e26da0f2200a2fb51844aaa43e365ea9dd5a93d (patch) | |
tree | 73409a696901934da5f6df976c6521d3e50e8749 /drivers/i2c/busses/i2c-s3c2410.c | |
parent | 10a0d912898ac2fe49094acf2c1339d0fb4c2bc6 (diff) | |
parent | 65de394df21f8ccc61525f77b0e4ee6940a0932e (diff) |
Merge branch 'i2c-for-2630-v2' of git://aeryn.fluff.org.uk/bjdooks/linux
* 'i2c-for-2630-v2' of git://aeryn.fluff.org.uk/bjdooks/linux:
i2c: imx: Make disable_delay a per-device variable
i2c: xtensa s6000 i2c driver
powerpc/85xx: i2c-mpc: use new I2C bindings for the Socates board
i2c: i2c-mpc: make I2C bus speed configurable
i2c: i2c-mpc: use dev based printout function
i2c: i2c-mpc: various coding style fixes
i2c: imx: Add missing request_mem_region in probe()
i2c: i2c-s3c2410: Initialise Samsung I2C controller early
i2c-s3c2410: Simplify bus frequency calculation
i2c-s3c2410: sda_delay should be in ns, not clock ticks
i2c: iMX/MXC support
Diffstat (limited to 'drivers/i2c/busses/i2c-s3c2410.c')
-rw-r--r-- | drivers/i2c/busses/i2c-s3c2410.c | 77 |
1 files changed, 31 insertions, 46 deletions
diff --git a/drivers/i2c/busses/i2c-s3c2410.c b/drivers/i2c/busses/i2c-s3c2410.c index 5b7f95641ba4..1691ef0f1ee1 100644 --- a/drivers/i2c/busses/i2c-s3c2410.c +++ b/drivers/i2c/busses/i2c-s3c2410.c | |||
@@ -1,6 +1,6 @@ | |||
1 | /* linux/drivers/i2c/busses/i2c-s3c2410.c | 1 | /* linux/drivers/i2c/busses/i2c-s3c2410.c |
2 | * | 2 | * |
3 | * Copyright (C) 2004,2005 Simtec Electronics | 3 | * Copyright (C) 2004,2005,2009 Simtec Electronics |
4 | * Ben Dooks <ben@simtec.co.uk> | 4 | * Ben Dooks <ben@simtec.co.uk> |
5 | * | 5 | * |
6 | * S3C2410 I2C Controller | 6 | * S3C2410 I2C Controller |
@@ -590,18 +590,6 @@ static int s3c24xx_i2c_calcdivisor(unsigned long clkin, unsigned int wanted, | |||
590 | return clkin / (calc_divs * calc_div1); | 590 | return clkin / (calc_divs * calc_div1); |
591 | } | 591 | } |
592 | 592 | ||
593 | /* freq_acceptable | ||
594 | * | ||
595 | * test wether a frequency is within the acceptable range of error | ||
596 | */ | ||
597 | |||
598 | static inline int freq_acceptable(unsigned int freq, unsigned int wanted) | ||
599 | { | ||
600 | int diff = freq - wanted; | ||
601 | |||
602 | return diff >= -2 && diff <= 2; | ||
603 | } | ||
604 | |||
605 | /* s3c24xx_i2c_clockrate | 593 | /* s3c24xx_i2c_clockrate |
606 | * | 594 | * |
607 | * work out a divisor for the user requested frequency setting, | 595 | * work out a divisor for the user requested frequency setting, |
@@ -614,44 +602,28 @@ static int s3c24xx_i2c_clockrate(struct s3c24xx_i2c *i2c, unsigned int *got) | |||
614 | struct s3c2410_platform_i2c *pdata = i2c->dev->platform_data; | 602 | struct s3c2410_platform_i2c *pdata = i2c->dev->platform_data; |
615 | unsigned long clkin = clk_get_rate(i2c->clk); | 603 | unsigned long clkin = clk_get_rate(i2c->clk); |
616 | unsigned int divs, div1; | 604 | unsigned int divs, div1; |
605 | unsigned long target_frequency; | ||
617 | u32 iiccon; | 606 | u32 iiccon; |
618 | int freq; | 607 | int freq; |
619 | int start, end; | ||
620 | 608 | ||
621 | i2c->clkrate = clkin; | 609 | i2c->clkrate = clkin; |
622 | clkin /= 1000; /* clkin now in KHz */ | 610 | clkin /= 1000; /* clkin now in KHz */ |
623 | 611 | ||
624 | dev_dbg(i2c->dev, "pdata %p, freq %lu %lu..%lu\n", | 612 | dev_dbg(i2c->dev, "pdata desired frequency %lu\n", pdata->frequency); |
625 | pdata, pdata->bus_freq, pdata->min_freq, pdata->max_freq); | ||
626 | |||
627 | if (pdata->bus_freq != 0) { | ||
628 | freq = s3c24xx_i2c_calcdivisor(clkin, pdata->bus_freq/1000, | ||
629 | &div1, &divs); | ||
630 | if (freq_acceptable(freq, pdata->bus_freq/1000)) | ||
631 | goto found; | ||
632 | } | ||
633 | |||
634 | /* ok, we may have to search for something suitable... */ | ||
635 | 613 | ||
636 | start = (pdata->max_freq == 0) ? pdata->bus_freq : pdata->max_freq; | 614 | target_frequency = pdata->frequency ? pdata->frequency : 100000; |
637 | end = pdata->min_freq; | ||
638 | 615 | ||
639 | start /= 1000; | 616 | target_frequency /= 1000; /* Target frequency now in KHz */ |
640 | end /= 1000; | ||
641 | 617 | ||
642 | /* search loop... */ | 618 | freq = s3c24xx_i2c_calcdivisor(clkin, target_frequency, &div1, &divs); |
643 | 619 | ||
644 | for (; start > end; start--) { | 620 | if (freq > target_frequency) { |
645 | freq = s3c24xx_i2c_calcdivisor(clkin, start, &div1, &divs); | 621 | dev_err(i2c->dev, |
646 | if (freq_acceptable(freq, start)) | 622 | "Unable to achieve desired frequency %luKHz." \ |
647 | goto found; | 623 | " Lowest achievable %dKHz\n", target_frequency, freq); |
624 | return -EINVAL; | ||
648 | } | 625 | } |
649 | 626 | ||
650 | /* cannot find frequency spec */ | ||
651 | |||
652 | return -EINVAL; | ||
653 | |||
654 | found: | ||
655 | *got = freq; | 627 | *got = freq; |
656 | 628 | ||
657 | iiccon = readl(i2c->regs + S3C2410_IICCON); | 629 | iiccon = readl(i2c->regs + S3C2410_IICCON); |
@@ -663,6 +635,23 @@ static int s3c24xx_i2c_clockrate(struct s3c24xx_i2c *i2c, unsigned int *got) | |||
663 | 635 | ||
664 | writel(iiccon, i2c->regs + S3C2410_IICCON); | 636 | writel(iiccon, i2c->regs + S3C2410_IICCON); |
665 | 637 | ||
638 | if (s3c24xx_i2c_is2440(i2c)) { | ||
639 | unsigned long sda_delay; | ||
640 | |||
641 | if (pdata->sda_delay) { | ||
642 | sda_delay = (freq / 1000) * pdata->sda_delay; | ||
643 | sda_delay /= 1000000; | ||
644 | sda_delay = DIV_ROUND_UP(sda_delay, 5); | ||
645 | if (sda_delay > 3) | ||
646 | sda_delay = 3; | ||
647 | sda_delay |= S3C2410_IICLC_FILTER_ON; | ||
648 | } else | ||
649 | sda_delay = 0; | ||
650 | |||
651 | dev_dbg(i2c->dev, "IICLC=%08lx\n", sda_delay); | ||
652 | writel(sda_delay, i2c->regs + S3C2440_IICLC); | ||
653 | } | ||
654 | |||
666 | return 0; | 655 | return 0; |
667 | } | 656 | } |
668 | 657 | ||
@@ -769,11 +758,8 @@ static int s3c24xx_i2c_init(struct s3c24xx_i2c *i2c) | |||
769 | 758 | ||
770 | /* check for s3c2440 i2c controller */ | 759 | /* check for s3c2440 i2c controller */ |
771 | 760 | ||
772 | if (s3c24xx_i2c_is2440(i2c)) { | 761 | if (s3c24xx_i2c_is2440(i2c)) |
773 | dev_dbg(i2c->dev, "S3C2440_IICLC=%08x\n", pdata->sda_delay); | 762 | writel(0x0, i2c->regs + S3C2440_IICLC); |
774 | |||
775 | writel(pdata->sda_delay, i2c->regs + S3C2440_IICLC); | ||
776 | } | ||
777 | 763 | ||
778 | return 0; | 764 | return 0; |
779 | } | 765 | } |
@@ -1018,14 +1004,13 @@ static int __init i2c_adap_s3c_init(void) | |||
1018 | 1004 | ||
1019 | return ret; | 1005 | return ret; |
1020 | } | 1006 | } |
1007 | subsys_initcall(i2c_adap_s3c_init); | ||
1021 | 1008 | ||
1022 | static void __exit i2c_adap_s3c_exit(void) | 1009 | static void __exit i2c_adap_s3c_exit(void) |
1023 | { | 1010 | { |
1024 | platform_driver_unregister(&s3c2410_i2c_driver); | 1011 | platform_driver_unregister(&s3c2410_i2c_driver); |
1025 | platform_driver_unregister(&s3c2440_i2c_driver); | 1012 | platform_driver_unregister(&s3c2440_i2c_driver); |
1026 | } | 1013 | } |
1027 | |||
1028 | module_init(i2c_adap_s3c_init); | ||
1029 | module_exit(i2c_adap_s3c_exit); | 1014 | module_exit(i2c_adap_s3c_exit); |
1030 | 1015 | ||
1031 | MODULE_DESCRIPTION("S3C24XX I2C Bus driver"); | 1016 | MODULE_DESCRIPTION("S3C24XX I2C Bus driver"); |