diff --git a/drivers/i3c/master/svc-i3c-master.c b/drivers/i3c/master/svc-i3c-master.c index 8ad0421e7a2efab68166c13c1151eea0240fa7be..423db3dca257b06ad05b7fbacf54704a98d8d791 100644 --- a/drivers/i3c/master/svc-i3c-master.c +++ b/drivers/i3c/master/svc-i3c-master.c @@ -127,6 +127,8 @@ /* 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: