summaryrefslogtreecommitdiff
path: root/drivers/i2c/busses/i2c-octeon-core.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/i2c/busses/i2c-octeon-core.c')
-rw-r--r--drivers/i2c/busses/i2c-octeon-core.c39
1 files changed, 35 insertions, 4 deletions
diff --git a/drivers/i2c/busses/i2c-octeon-core.c b/drivers/i2c/busses/i2c-octeon-core.c
index 845eda70b8ca..75efb375d8e4 100644
--- a/drivers/i2c/busses/i2c-octeon-core.c
+++ b/drivers/i2c/busses/i2c-octeon-core.c
@@ -17,9 +17,14 @@
#include <linux/interrupt.h>
#include <linux/kernel.h>
#include <linux/module.h>
+#include <linux/pci.h>
#include "i2c-octeon-core.h"
+#define INITIAL_DELTA_HZ 1000000
+#define TWSI_MASTER_CLK_REG_DEF_VAL 0x18
+#define TWSI_MASTER_CLK_REG_OTX2_VAL 0x3
+
/* interrupt service routine */
irqreturn_t octeon_i2c_isr(int irq, void *dev_id)
{
@@ -658,31 +663,57 @@ out:
void octeon_i2c_set_clock(struct octeon_i2c *i2c)
{
int tclk, thp_base, inc, thp_idx, mdiv_idx, ndiv_idx, foscl, diff;
- int thp = 0x18, mdiv = 2, ndiv = 0, delta_hz = 1000000;
+ bool is_plat_otx2;
+ unsigned int mdiv_min = 2;
+ /*
+ * Find divisors to produce target frequency, start with large delta
+ * to cover wider range of divisors, note thp = TCLK half period.
+ */
+ unsigned int thp = TWSI_MASTER_CLK_REG_DEF_VAL, mdiv = 2, ndiv = 0;
+ unsigned int delta_hz = INITIAL_DELTA_HZ;
+
+ is_plat_otx2 = octeon_i2c_is_otx2(to_pci_dev(i2c->dev));
+
+ if (is_plat_otx2) {
+ thp = TWSI_MASTER_CLK_REG_OTX2_VAL;
+ mdiv_min = 0;
+ }
for (ndiv_idx = 0; ndiv_idx < 8 && delta_hz != 0; ndiv_idx++) {
/*
* An mdiv value of less than 2 seems to not work well
* with ds1337 RTCs, so we constrain it to larger values.
*/
- for (mdiv_idx = 15; mdiv_idx >= 2 && delta_hz != 0; mdiv_idx--) {
+ for (mdiv_idx = 15; mdiv_idx >= mdiv_min && delta_hz != 0; mdiv_idx--) {
/*
* For given ndiv and mdiv values check the
* two closest thp values.
*/
tclk = i2c->twsi_freq * (mdiv_idx + 1) * 10;
tclk *= (1 << ndiv_idx);
- thp_base = (i2c->sys_freq / (tclk * 2)) - 1;
+ if (is_plat_otx2)
+ thp_base = (i2c->sys_freq / tclk) - 2;
+ else
+ thp_base = (i2c->sys_freq / (tclk * 2)) - 1;
for (inc = 0; inc <= 1; inc++) {
thp_idx = thp_base + inc;
if (thp_idx < 5 || thp_idx > 0xff)
continue;
- foscl = i2c->sys_freq / (2 * (thp_idx + 1));
+ if (is_plat_otx2)
+ foscl = i2c->sys_freq / (thp_idx + 2);
+ else
+ foscl = i2c->sys_freq /
+ (2 * (thp_idx + 1));
foscl = foscl / (1 << ndiv_idx);
foscl = foscl / (mdiv_idx + 1) / 10;
diff = abs(foscl - i2c->twsi_freq);
+ /*
+ * Diff holds difference between calculated frequency
+ * value vs desired frequency.
+ * Delta_hz is updated with last minimum diff.
+ */
if (diff < delta_hz) {
delta_hz = diff;
thp = thp_idx;