static int img_i2c_init()

in busses/i2c-img-scb.c [1153:1327]


static int img_i2c_init(struct img_i2c *i2c)
{
	unsigned int clk_khz, bitrate_khz, clk_period, tckh, tckl, tsdh;
	unsigned int i, data, prescale, inc, int_bitrate, filt;
	struct img_i2c_timings timing;
	u32 rev;
	int ret;

	ret = pm_runtime_resume_and_get(i2c->adap.dev.parent);
	if (ret < 0)
		return ret;

	rev = img_i2c_readl(i2c, SCB_CORE_REV_REG);
	if ((rev & 0x00ffffff) < 0x00020200) {
		dev_info(i2c->adap.dev.parent,
			 "Unknown hardware revision (%d.%d.%d.%d)\n",
			 (rev >> 24) & 0xff, (rev >> 16) & 0xff,
			 (rev >> 8) & 0xff, rev & 0xff);
		pm_runtime_mark_last_busy(i2c->adap.dev.parent);
		pm_runtime_put_autosuspend(i2c->adap.dev.parent);
		return -EINVAL;
	}

	/* Fencing enabled by default. */
	i2c->need_wr_rd_fence = true;

	/* Determine what mode we're in from the bitrate */
	timing = timings[0];
	for (i = 0; i < ARRAY_SIZE(timings); i++) {
		if (i2c->bitrate <= timings[i].max_bitrate) {
			timing = timings[i];
			break;
		}
	}
	if (i2c->bitrate > timings[ARRAY_SIZE(timings) - 1].max_bitrate) {
		dev_warn(i2c->adap.dev.parent,
			 "requested bitrate (%u) is higher than the max bitrate supported (%u)\n",
			 i2c->bitrate,
			 timings[ARRAY_SIZE(timings) - 1].max_bitrate);
		timing = timings[ARRAY_SIZE(timings) - 1];
		i2c->bitrate = timing.max_bitrate;
	}

	bitrate_khz = i2c->bitrate / 1000;
	clk_khz = clk_get_rate(i2c->scb_clk) / 1000;

	/* Find the prescale that would give us that inc (approx delay = 0) */
	prescale = SCB_OPT_INC * clk_khz / (256 * 16 * bitrate_khz);
	prescale = clamp_t(unsigned int, prescale, 1, 8);
	clk_khz /= prescale;

	/* Setup the clock increment value */
	inc = (256 * 16 * bitrate_khz) / clk_khz;

	/*
	 * The clock generation logic allows to filter glitches on the bus.
	 * This filter is able to remove bus glitches shorter than 50ns.
	 * If the clock enable rate is greater than 20 MHz, no filtering
	 * is required, so we need to disable it.
	 * If it's between the 20-40 MHz range, there's no need to divide
	 * the clock to get a filter.
	 */
	if (clk_khz < 20000) {
		filt = SCB_FILT_DISABLE;
	} else if (clk_khz < 40000) {
		filt = SCB_FILT_BYPASS;
	} else {
		/* Calculate filter clock */
		filt = (64000 / ((clk_khz / 1000) * SCB_FILT_GLITCH));

		/* Scale up if needed */
		if (64000 % ((clk_khz / 1000) * SCB_FILT_GLITCH))
			inc++;

		if (filt > SCB_FILT_INC_MASK)
			filt = SCB_FILT_INC_MASK;

		filt = (filt & SCB_FILT_INC_MASK) << SCB_FILT_INC_SHIFT;
	}
	data = filt | ((inc & SCB_INC_MASK) << SCB_INC_SHIFT) | (prescale - 1);
	img_i2c_writel(i2c, SCB_CLK_SET_REG, data);

	/* Obtain the clock period of the fx16 clock in ns */
	clk_period = (256 * 1000000) / (clk_khz * inc);

	/* Calculate the bitrate in terms of internal clock pulses */
	int_bitrate = 1000000 / (bitrate_khz * clk_period);
	if ((1000000 % (bitrate_khz * clk_period)) >=
	    ((bitrate_khz * clk_period) / 2))
		int_bitrate++;

	/*
	 * Setup clock duty cycle, start with 50% and adjust TCKH and TCKL
	 * values from there if they don't meet minimum timing requirements
	 */
	tckh = int_bitrate / 2;
	tckl = int_bitrate - tckh;

	/* Adjust TCKH and TCKL values */
	data = DIV_ROUND_UP(timing.tckl, clk_period);

	if (tckl < data) {
		tckl = data;
		tckh = int_bitrate - tckl;
	}

	if (tckh > 0)
		--tckh;

	if (tckl > 0)
		--tckl;

	img_i2c_writel(i2c, SCB_TIME_TCKH_REG, tckh);
	img_i2c_writel(i2c, SCB_TIME_TCKL_REG, tckl);

	/* Setup TSDH value */
	tsdh = DIV_ROUND_UP(timing.tsdh, clk_period);

	if (tsdh > 1)
		data = tsdh - 1;
	else
		data = 0x01;
	img_i2c_writel(i2c, SCB_TIME_TSDH_REG, data);

	/* This value is used later */
	tsdh = data;

	/* Setup TPL value */
	data = timing.tpl / clk_period;
	if (data > 0)
		--data;
	img_i2c_writel(i2c, SCB_TIME_TPL_REG, data);

	/* Setup TPH value */
	data = timing.tph / clk_period;
	if (data > 0)
		--data;
	img_i2c_writel(i2c, SCB_TIME_TPH_REG, data);

	/* Setup TSDL value to TPL + TSDH + 2 */
	img_i2c_writel(i2c, SCB_TIME_TSDL_REG, data + tsdh + 2);

	/* Setup TP2S value */
	data = timing.tp2s / clk_period;
	if (data > 0)
		--data;
	img_i2c_writel(i2c, SCB_TIME_TP2S_REG, data);

	img_i2c_writel(i2c, SCB_TIME_TBI_REG, TIMEOUT_TBI);
	img_i2c_writel(i2c, SCB_TIME_TSL_REG, TIMEOUT_TSL);
	img_i2c_writel(i2c, SCB_TIME_TDL_REG, TIMEOUT_TDL);

	/* Take module out of soft reset and enable clocks */
	img_i2c_soft_reset(i2c);

	/* Disable all interrupts */
	img_i2c_writel(i2c, SCB_INT_MASK_REG, 0);

	/* Clear all interrupts */
	img_i2c_writel(i2c, SCB_INT_CLEAR_REG, ~0);

	/* Clear the scb_line_status events */
	img_i2c_writel(i2c, SCB_CLEAR_REG, ~0);

	/* Enable interrupts */
	img_i2c_writel(i2c, SCB_INT_MASK_REG, i2c->int_enable);

	/* Perform a synchronous sequence to reset the bus */
	ret = img_i2c_reset_bus(i2c);

	pm_runtime_mark_last_busy(i2c->adap.dev.parent);
	pm_runtime_put_autosuspend(i2c->adap.dev.parent);

	return ret;
}