]> www.infradead.org Git - users/jedix/linux-maple.git/commitdiff
i3c: master: svc: adjust SDR according to i3c spec
authorCarlos Song <carlos.song@nxp.com>
Fri, 19 Jul 2024 08:02:33 +0000 (16:02 +0800)
committerAlexandre Belloni <alexandre.belloni@bootlin.com>
Tue, 17 Sep 2024 14:48:40 +0000 (16:48 +0200)
According to I3C Specification(Version 1.1) 5.1.2.4 "Use of Clock
Speed to Prevent Legacy I2C Devices From Seeing I3C traffic", when
slow i2c devices(FM/FM+ rate i2c frequency without 50ns filter)
works on i3c bus, i3c SDR should work at FM/FM+ rate.

Adjust timing for difference mode.

Signed-off-by: Clark Wang <xiaoning.wang@nxp.com>
Signed-off-by: Carlos Song <carlos.song@nxp.com>
Signed-off-by: Frank Li <frank.li@nxp.com>
Acked-by: Miquel Raynal <miquel.raynal@bootlin.com>
Link: https://lore.kernel.org/r/20240719080233.842771-1-carlos.song@nxp.com
Signed-off-by: Alexandre Belloni <alexandre.belloni@bootlin.com>
drivers/i3c/master/svc-i3c-master.c

index 8ad0421e7a2efab68166c13c1151eea0240fa7be..423db3dca257b06ad05b7fbacf54704a98d8d791 100644 (file)
 
 /* This parameter depends on the implementation and may be tuned */
 #define SVC_I3C_FIFO_SIZE 16
+#define SVC_I3C_PPBAUD_MAX 15
+#define SVC_I3C_QUICK_I2C_CLK 4170000
 
 #define SVC_I3C_EVENT_IBI      BIT(0)
 #define SVC_I3C_EVENT_HOTJOIN  BIT(1)
@@ -585,6 +587,7 @@ static int svc_i3c_master_bus_init(struct i3c_master_controller *m)
        struct i3c_bus *bus = i3c_master_get_bus(m);
        struct i3c_device_info info = {};
        unsigned long fclk_rate, fclk_period_ns;
+       unsigned long i2c_period_ns, i2c_scl_rate, i3c_scl_rate;
        unsigned int high_period_ns, od_low_period_ns;
        u32 ppbaud, pplow, odhpp, odbaud, odstop, i2cbaud, reg;
        int ret;
@@ -605,12 +608,15 @@ static int svc_i3c_master_bus_init(struct i3c_master_controller *m)
        }
 
        fclk_period_ns = DIV_ROUND_UP(1000000000, fclk_rate);
+       i2c_period_ns = DIV_ROUND_UP(1000000000, bus->scl_rate.i2c);
+       i2c_scl_rate = bus->scl_rate.i2c;
+       i3c_scl_rate = bus->scl_rate.i3c;
 
        /*
         * Using I3C Push-Pull mode, target is 12.5MHz/80ns period.
         * Simplest configuration is using a 50% duty-cycle of 40ns.
         */
-       ppbaud = DIV_ROUND_UP(40, fclk_period_ns) - 1;
+       ppbaud = DIV_ROUND_UP(fclk_rate / 2, i3c_scl_rate) - 1;
        pplow = 0;
 
        /*
@@ -620,7 +626,7 @@ static int svc_i3c_master_bus_init(struct i3c_master_controller *m)
         */
        odhpp = 1;
        high_period_ns = (ppbaud + 1) * fclk_period_ns;
-       odbaud = DIV_ROUND_UP(240 - high_period_ns, high_period_ns) - 1;
+       odbaud = DIV_ROUND_UP(fclk_rate, SVC_I3C_QUICK_I2C_CLK * (1 + ppbaud)) - 2;
        od_low_period_ns = (odbaud + 1) * high_period_ns;
 
        switch (bus->mode) {
@@ -629,20 +635,27 @@ static int svc_i3c_master_bus_init(struct i3c_master_controller *m)
                odstop = 0;
                break;
        case I3C_BUS_MODE_MIXED_FAST:
-       case I3C_BUS_MODE_MIXED_LIMITED:
                /*
                 * Using I2C Fm+ mode, target is 1MHz/1000ns, the difference
                 * between the high and low period does not really matter.
                 */
-               i2cbaud = DIV_ROUND_UP(1000, od_low_period_ns) - 2;
+               i2cbaud = DIV_ROUND_UP(i2c_period_ns, od_low_period_ns) - 2;
                odstop = 1;
                break;
+       case I3C_BUS_MODE_MIXED_LIMITED:
        case I3C_BUS_MODE_MIXED_SLOW:
-               /*
-                * Using I2C Fm mode, target is 0.4MHz/2500ns, with the same
-                * constraints as the FM+ mode.
-                */
-               i2cbaud = DIV_ROUND_UP(2500, od_low_period_ns) - 2;
+               /* I3C PP + I3C OP + I2C OP both use i2c clk rate */
+               if (ppbaud > SVC_I3C_PPBAUD_MAX) {
+                       ppbaud = SVC_I3C_PPBAUD_MAX;
+                       pplow =  DIV_ROUND_UP(fclk_rate, i3c_scl_rate) - (2 + 2 * ppbaud);
+               }
+
+               high_period_ns = (ppbaud + 1) * fclk_period_ns;
+               odhpp = 0;
+               odbaud = DIV_ROUND_UP(fclk_rate, i2c_scl_rate * (2 + 2 * ppbaud)) - 1;
+
+               od_low_period_ns = (odbaud + 1) * high_period_ns;
+               i2cbaud = DIV_ROUND_UP(i2c_period_ns, od_low_period_ns) - 2;
                odstop = 1;
                break;
        default: