static void cdns_i3c_master_upd_i3c_scl_lim()

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);
}