i2c: thunderx: Clock divisor logic changes
authorSuneel Garapati <sgarapati@marvell.com>
Tue, 23 Apr 2024 07:46:04 +0000 (00:46 -0700)
committerAndi Shyti <andi.shyti@kernel.org>
Sun, 5 May 2024 22:56:38 +0000 (00:56 +0200)
Handle changes to clock divisor logic for OcteonTX2 SoC family using
subsystem ID and using default reference clock source as 100MHz.

Signed-off-by: Suneel Garapati <sgarapati@marvell.com>
Signed-off-by: Piyush Malgujar <pmalgujar@marvell.com>
Acked-by: Andi Shyti <andi.shyti@kernel.org>
Signed-off-by: Andi Shyti <andi.shyti@kernel.org>
drivers/i2c/busses/i2c-octeon-core.c
drivers/i2c/busses/i2c-octeon-core.h
drivers/i2c/busses/i2c-thunderx-pcidrv.c

index 845eda70b8cab52a0453c9f4cb545010fba4305d..75efb375d8e49479267e214772d4df48352be358 100644 (file)
 #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;
index 9bb9f64fdda0392364638ecbaafe3fab5612baf6..69bd62940f99eb786877026380cd1d02a4eaadb9 100644 (file)
@@ -1,5 +1,6 @@
 /* SPDX-License-Identifier: GPL-2.0 */
 #include <linux/atomic.h>
+#include <linux/bitfield.h>
 #include <linux/clk.h>
 #include <linux/delay.h>
 #include <linux/device.h>
@@ -7,6 +8,7 @@
 #include <linux/i2c-smbus.h>
 #include <linux/io.h>
 #include <linux/kernel.h>
+#include <linux/pci.h>
 
 /* Controller command patterns */
 #define SW_TWSI_V              BIT_ULL(63)     /* Valid bit */
@@ -211,6 +213,21 @@ static inline void octeon_i2c_write_int(struct octeon_i2c *i2c, u64 data)
        octeon_i2c_writeq_flush(data, i2c->twsi_base + TWSI_INT(i2c));
 }
 
+#define PCI_SUBSYS_DEVID_9XXX  0xB
+#define PCI_SUBSYS_MASK                GENMASK(15, 12)
+/**
+ * octeon_i2c_is_otx2 - check for chip ID
+ * @pdev: PCI dev structure
+ *
+ * Returns true if the device is an OcteonTX2, false otherwise.
+ */
+static inline bool octeon_i2c_is_otx2(struct pci_dev *pdev)
+{
+       u32 chip_id = FIELD_GET(PCI_SUBSYS_MASK, pdev->subsystem_device);
+
+       return (chip_id == PCI_SUBSYS_DEVID_9XXX);
+}
+
 /* Prototypes */
 irqreturn_t octeon_i2c_isr(int irq, void *dev_id);
 int octeon_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num);
index a77cd86fe75ed7401bc041b27c651b9fedf67285..75569774003857dc984e8540ef8f4d1bb084cfb0 100644 (file)
@@ -28,6 +28,7 @@
 #define PCI_DEVICE_ID_THUNDER_TWSI     0xa012
 
 #define SYS_FREQ_DEFAULT               700000000
+#define OTX2_REF_FREQ_DEFAULT          100000000
 
 #define TWSI_INT_ENA_W1C               0x1028
 #define TWSI_INT_ENA_W1S               0x1030
@@ -205,6 +206,12 @@ static int thunder_i2c_probe_pci(struct pci_dev *pdev,
        if (ret)
                goto error;
 
+       /*
+        * For OcteonTX2 chips, set reference frequency to 100MHz
+        * as refclk_src in TWSI_MODE register defaults to 100MHz.
+        */
+       if (octeon_i2c_is_otx2(pdev))
+               i2c->sys_freq = OTX2_REF_FREQ_DEFAULT;
        octeon_i2c_set_clock(i2c);
 
        i2c->adap = thunderx_i2c_ops;