in master/i3c-master-cdns.c [1062:1130]
static void cdns_i3c_master_upd_i3c_scl_lim(struct cdns_i3c_master *master)
{
struct i3c_master_controller *m = &master->base;
unsigned long i3c_lim_period, pres_step, ncycles;
struct i3c_bus *bus = i3c_master_get_bus(m);
unsigned long new_i3c_scl_lim = 0;
struct i3c_dev_desc *dev;
u32 prescl1, ctrl;
i3c_bus_for_each_i3cdev(bus, dev) {
unsigned long max_fscl;
max_fscl = max(I3C_CCC_MAX_SDR_FSCL(dev->info.max_read_ds),
I3C_CCC_MAX_SDR_FSCL(dev->info.max_write_ds));
switch (max_fscl) {
case I3C_SDR1_FSCL_8MHZ:
max_fscl = 8000000;
break;
case I3C_SDR2_FSCL_6MHZ:
max_fscl = 6000000;
break;
case I3C_SDR3_FSCL_4MHZ:
max_fscl = 4000000;
break;
case I3C_SDR4_FSCL_2MHZ:
max_fscl = 2000000;
break;
case I3C_SDR0_FSCL_MAX:
default:
max_fscl = 0;
break;
}
if (max_fscl &&
(new_i3c_scl_lim > max_fscl || !new_i3c_scl_lim))
new_i3c_scl_lim = max_fscl;
}
/* Only update PRESCL_CTRL1 if the I3C SCL limitation has changed. */
if (new_i3c_scl_lim == master->i3c_scl_lim)
return;
master->i3c_scl_lim = new_i3c_scl_lim;
if (!new_i3c_scl_lim)
return;
pres_step = 1000000000UL / (bus->scl_rate.i3c * 4);
/* Configure PP_LOW to meet I3C slave limitations. */
prescl1 = readl(master->regs + PRESCL_CTRL1) &
~PRESCL_CTRL1_PP_LOW_MASK;
ctrl = readl(master->regs + CTRL);
i3c_lim_period = DIV_ROUND_UP(1000000000, master->i3c_scl_lim);
ncycles = DIV_ROUND_UP(i3c_lim_period, pres_step);
if (ncycles < 4)
ncycles = 0;
else
ncycles -= 4;
prescl1 |= PRESCL_CTRL1_PP_LOW(ncycles);
/* Disable I3C master before updating PRESCL_CTRL1. */
if (ctrl & CTRL_DEV_EN)
cdns_i3c_master_disable(master);
writel(prescl1, master->regs + PRESCL_CTRL1);
if (ctrl & CTRL_DEV_EN)
cdns_i3c_master_enable(master);
}