static void process_channel_reply(
	struct i2c_engine *engine,
	struct i2c_reply_transaction_data *reply)
{
	uint32_t length = reply->length;
	uint8_t *buffer = reply->data;

	struct i2c_hw_engine_dce110 *hw_engine =
		FROM_I2C_ENGINE(engine);


	REG_SET_3(DC_I2C_DATA, 0,
			DC_I2C_INDEX, hw_engine->buffer_used_write,
			DC_I2C_DATA_RW, 1,
			DC_I2C_INDEX_WRITE, 1);

	while (length) {
		/* after reading the status,
		 * if the I2C operation executed successfully
		 * (i.e. DC_I2C_STATUS_DONE = 1) then the I2C controller
		 * should read data bytes from I2C circular data buffer */

		uint32_t i2c_data;

		REG_GET(DC_I2C_DATA, DC_I2C_DATA, &i2c_data);
		*buffer++ = i2c_data;

		--length;
	}
}
static enum i2c_channel_operation_result get_channel_status(
	struct i2c_engine *i2c_engine,
	uint8_t *returned_bytes)
{
	uint32_t i2c_sw_status = 0;
	struct i2c_hw_engine_dce110 *hw_engine = FROM_I2C_ENGINE(i2c_engine);
	uint32_t value =
			REG_GET(DC_I2C_SW_STATUS, DC_I2C_SW_STATUS, &i2c_sw_status);

	if (i2c_sw_status == DC_I2C_STATUS__DC_I2C_STATUS_USED_BY_SW)
		return I2C_CHANNEL_OPERATION_ENGINE_BUSY;
	else if (value & hw_engine->i2c_mask->DC_I2C_SW_STOPPED_ON_NACK)
		return I2C_CHANNEL_OPERATION_NO_RESPONSE;
	else if (value & hw_engine->i2c_mask->DC_I2C_SW_TIMEOUT)
		return I2C_CHANNEL_OPERATION_TIMEOUT;
	else if (value & hw_engine->i2c_mask->DC_I2C_SW_ABORTED)
		return I2C_CHANNEL_OPERATION_FAILED;
	else if (value & hw_engine->i2c_mask->DC_I2C_SW_DONE)
		return I2C_CHANNEL_OPERATION_SUCCEEDED;

	/*
	 * this is the case when HW used for communication, I2C_SW_STATUS
	 * could be zero
	 */
	return I2C_CHANNEL_OPERATION_SUCCEEDED;
}
static bool setup_engine(
	struct i2c_engine *i2c_engine)
{
	struct i2c_hw_engine_dce110 *hw_engine = FROM_I2C_ENGINE(i2c_engine);

	/* Program pin select */
	REG_UPDATE_6(
			DC_I2C_CONTROL,
			DC_I2C_GO, 0,
			DC_I2C_SOFT_RESET, 0,
			DC_I2C_SEND_RESET, 0,
			DC_I2C_SW_STATUS_RESET, 1,
			DC_I2C_TRANSACTION_COUNT, 0,
			DC_I2C_DDC_SELECT, hw_engine->engine_id);

	/* Program time limit */
	REG_UPDATE_N(
			SETUP, 2,
			FN(DC_I2C_DDC1_SETUP, DC_I2C_DDC1_TIME_LIMIT), I2C_SETUP_TIME_LIMIT,
			FN(DC_I2C_DDC1_SETUP, DC_I2C_DDC1_ENABLE), 1);

	/* Program HW priority
	 * set to High - interrupt software I2C at any time
	 * Enable restart of SW I2C that was interrupted by HW
	 * disable queuing of software while I2C is in use by HW */
	REG_UPDATE_2(
			DC_I2C_ARBITRATION,
			DC_I2C_NO_QUEUED_SW_GO, 0,
			DC_I2C_SW_PRIORITY, DC_I2C_ARBITRATION__DC_I2C_SW_PRIORITY_NORMAL);

	return true;
}
static void submit_channel_request(
	struct i2c_engine *engine,
	struct i2c_request_transaction_data *request)
{
	request->status = I2C_CHANNEL_OPERATION_SUCCEEDED;

	if (!process_transaction(FROM_I2C_ENGINE(engine), request))
		return;

	if (is_hw_busy(&engine->base)) {
		request->status = I2C_CHANNEL_OPERATION_ENGINE_BUSY;
		return;
	}

	execute_transaction(FROM_I2C_ENGINE(engine));
}
static void destroy(
	struct i2c_engine **ptr)
{
	dal_i2c_sw_engine_destruct(FROM_I2C_ENGINE(*ptr));

	dm_free(*ptr);
	*ptr = NULL;
}
static void destroy(
	struct i2c_engine **engine)
{
	struct i2c_sw_engine_dce110 *sw_engine = FROM_I2C_ENGINE(*engine);

	destruct(sw_engine);

	dm_free(sw_engine);

	*engine = NULL;
}
static void destroy(
	struct i2c_engine **i2c_engine)
{
	struct i2c_hw_engine_dce110 *engine_dce110 =
			FROM_I2C_ENGINE(*i2c_engine);

	dal_i2c_hw_engine_destruct(&engine_dce110->base);

	kfree(engine_dce110);

	*i2c_engine = NULL;
}
static uint32_t get_speed(
	const struct i2c_engine *i2c_engine)
{
	const struct i2c_hw_engine_dce110 *hw_engine = FROM_I2C_ENGINE(i2c_engine);
	uint32_t pre_scale = 0;

	REG_GET(SPEED, DC_I2C_DDC1_PRESCALE, &pre_scale);

	/* [anaumov] it seems following is unnecessary */
	/*ASSERT(value.bits.DC_I2C_DDC1_PRESCALE);*/
	return pre_scale ?
		hw_engine->reference_frequency / pre_scale :
		hw_engine->base.default_speed;
}
void dal_i2c_sw_engine_set_speed(
	struct i2c_engine *engine,
	uint32_t speed)
{
	struct i2c_sw_engine *sw_engine = FROM_I2C_ENGINE(engine);

	ASSERT(speed);

	sw_engine->speed = speed ? speed : I2CAUX_DEFAULT_I2C_SW_SPEED;

	sw_engine->clock_delay = 1000 / sw_engine->speed;

	if (sw_engine->clock_delay < 12)
		sw_engine->clock_delay = 12;
}
static void process_channel_reply(
	struct i2c_engine *engine,
	struct i2c_reply_transaction_data *reply)
{
	uint8_t length = reply->length;
	uint8_t *buffer = reply->data;

	struct i2c_hw_engine_dce110 *i2c_hw_engine_dce110 =
		FROM_I2C_ENGINE(engine);
	uint32_t value = dal_read_reg(engine->base.ctx, mmDC_I2C_DATA);

	/*set index*/
	set_reg_field_value(
		value,
		i2c_hw_engine_dce110->buffer_used_write,
		DC_I2C_DATA,
		DC_I2C_INDEX);

	set_reg_field_value(
		value,
		1,
		DC_I2C_DATA,
		DC_I2C_DATA_RW);

	set_reg_field_value(
		value,
		1,
		DC_I2C_DATA,
		DC_I2C_INDEX_WRITE);

	dal_write_reg(engine->base.ctx, mmDC_I2C_DATA, value);

	while (length) {
		/* after reading the status,
		 * if the I2C operation executed successfully
		 * (i.e. DC_I2C_STATUS_DONE = 1) then the I2C controller
		 * should read data bytes from I2C circular data buffer */

		value = dal_read_reg(engine->base.ctx, mmDC_I2C_DATA);

		*buffer++ = get_reg_field_value(
				value,
				DC_I2C_DATA,
				DC_I2C_DATA);

		--length;
	}
}
void dal_i2c_sw_engine_submit_channel_request(
	struct i2c_engine *engine,
	struct i2c_request_transaction_data *req)
{
	struct i2c_sw_engine *sw_engine = FROM_I2C_ENGINE(engine);

	struct ddc *ddc = engine->base.ddc;
	uint16_t clock_delay_div_4 = sw_engine->clock_delay >> 2;

	/* send sync (start / repeated start) */

	bool result = start_sync(engine->base.ctx, ddc, clock_delay_div_4);

	/* process payload */

	if (result) {
		switch (req->action) {
		case I2CAUX_TRANSACTION_ACTION_I2C_WRITE:
		case I2CAUX_TRANSACTION_ACTION_I2C_WRITE_MOT:
			result = i2c_write(engine->base.ctx, ddc, clock_delay_div_4,
				req->address, req->length, req->data);
		break;
		case I2CAUX_TRANSACTION_ACTION_I2C_READ:
		case I2CAUX_TRANSACTION_ACTION_I2C_READ_MOT:
			result = i2c_read(engine->base.ctx, ddc, clock_delay_div_4,
				req->address, req->length, req->data);
		break;
		default:
			result = false;
		break;
		}
	}

	/* send stop if not 'mot' or operation failed */

	if (!result ||
		(req->action == I2CAUX_TRANSACTION_ACTION_I2C_WRITE) ||
		(req->action == I2CAUX_TRANSACTION_ACTION_I2C_READ))
		if (!stop_sync(engine->base.ctx, ddc, clock_delay_div_4))
			result = false;

	req->status = result ?
		I2C_CHANNEL_OPERATION_SUCCEEDED :
		I2C_CHANNEL_OPERATION_FAILED;
}
Example #12
0
static void set_speed(
	struct i2c_engine *i2c_engine,
	uint32_t speed)
{
	struct i2c_hw_engine_dce110 *hw_engine = FROM_I2C_ENGINE(i2c_engine);

	if (speed) {
		if (hw_engine->i2c_mask->DC_I2C_DDC1_START_STOP_TIMING_CNTL)
			REG_UPDATE_N(
				SPEED, 3,
				FN(DC_I2C_DDC1_SPEED, DC_I2C_DDC1_PRESCALE), hw_engine->reference_frequency / speed,
				FN(DC_I2C_DDC1_SPEED, DC_I2C_DDC1_THRESHOLD), 2,
				FN(DC_I2C_DDC1_SPEED, DC_I2C_DDC1_START_STOP_TIMING_CNTL), speed > 50 ? 2:1);
		else
			REG_UPDATE_N(
				SPEED, 2,
				FN(DC_I2C_DDC1_SPEED, DC_I2C_DDC1_PRESCALE), hw_engine->reference_frequency / speed,
				FN(DC_I2C_DDC1_SPEED, DC_I2C_DDC1_THRESHOLD), 2);
	}
}
static void set_speed(
	struct i2c_engine *i2c_engine,
	uint32_t speed)
{
	struct i2c_hw_engine_dce110 *engine = FROM_I2C_ENGINE(i2c_engine);

	if (speed) {
		const uint32_t addr = engine->addr.DC_I2C_DDCX_SPEED;

		uint32_t value = dal_read_reg(i2c_engine->base.ctx, addr);

		set_reg_field_value(
			value,
			engine->reference_frequency / speed,
			DC_I2C_DDC1_SPEED,
			DC_I2C_DDC1_PRESCALE);

		set_reg_field_value(
			value,
			2,
			DC_I2C_DDC1_SPEED,
			DC_I2C_DDC1_THRESHOLD);

		/*DCE11, HW add 100Khz support for I2c*/
		if (speed > 50) {
			set_reg_field_value(
				value,
				2,
				DC_I2C_DDC1_SPEED,
				DC_I2C_DDC1_START_STOP_TIMING_CNTL);
		} else {
			set_reg_field_value(
				value,
				1,
				DC_I2C_DDC1_SPEED,
				DC_I2C_DDC1_START_STOP_TIMING_CNTL);
		}

		dal_write_reg(i2c_engine->base.ctx, addr, value);
	}
}
static uint32_t get_speed(
	const struct i2c_engine *i2c_engine)
{
	const struct i2c_hw_engine_dce110 *engine = FROM_I2C_ENGINE(i2c_engine);

	const uint32_t addr = engine->addr.DC_I2C_DDCX_SPEED;

	uint32_t pre_scale = 0;

	uint32_t value = dal_read_reg(i2c_engine->base.ctx, addr);

	pre_scale = get_reg_field_value(
			value,
			DC_I2C_DDC1_SPEED,
			DC_I2C_DDC1_PRESCALE);

	/* [anaumov] it seems following is unnecessary */
	/*ASSERT(value.bits.DC_I2C_DDC1_PRESCALE);*/

	return pre_scale ?
		engine->reference_frequency / pre_scale :
		engine->base.default_speed;
}
uint32_t dal_i2c_sw_engine_get_speed(
	const struct i2c_engine *engine)
{
	return FROM_I2C_ENGINE(engine)->speed;
}
static bool setup_engine(
	struct i2c_engine *i2c_engine)
{
	uint32_t value = 0;
	struct i2c_hw_engine_dce110 *engine = FROM_I2C_ENGINE(i2c_engine);

	/* Program pin select */
	{
		const uint32_t addr = mmDC_I2C_CONTROL;

		value = dal_read_reg(i2c_engine->base.ctx, addr);

		set_reg_field_value(
			value,
			0,
			DC_I2C_CONTROL,
			DC_I2C_GO);

		set_reg_field_value(
			value,
			0,
			DC_I2C_CONTROL,
			DC_I2C_SOFT_RESET);

		set_reg_field_value(
			value,
			0,
			DC_I2C_CONTROL,
			DC_I2C_SEND_RESET);

		set_reg_field_value(
			value,
			0,
			DC_I2C_CONTROL,
			DC_I2C_SW_STATUS_RESET);

		set_reg_field_value(
			value,
			0,
			DC_I2C_CONTROL,
			DC_I2C_TRANSACTION_COUNT);

		set_reg_field_value(
			value,
			engine->engine_id,
			DC_I2C_CONTROL,
			DC_I2C_DDC_SELECT);


		dal_write_reg(i2c_engine->base.ctx, addr, value);
	}

	/* Program time limit */
	{
		const uint32_t addr = engine->addr.DC_I2C_DDCX_SETUP;

		value = dal_read_reg(i2c_engine->base.ctx, addr);

		set_reg_field_value(
			value,
			I2C_SETUP_TIME_LIMIT,
			DC_I2C_DDC1_SETUP,
			DC_I2C_DDC1_TIME_LIMIT);

		set_reg_field_value(
			value,
			1,
			DC_I2C_DDC1_SETUP,
			DC_I2C_DDC1_ENABLE);

		dal_write_reg(i2c_engine->base.ctx, addr, value);
	}

	/* Program HW priority
	 * set to High - interrupt software I2C at any time
	 * Enable restart of SW I2C that was interrupted by HW
	 * disable queuing of software while I2C is in use by HW */
	{
		value = dal_read_reg(i2c_engine->base.ctx,
				mmDC_I2C_ARBITRATION);

		set_reg_field_value(
			value,
			0,
			DC_I2C_ARBITRATION,
			DC_I2C_NO_QUEUED_SW_GO);

		set_reg_field_value(
			value,
			DC_I2C_ARBITRATION__DC_I2C_SW_PRIORITY_NORMAL,
			DC_I2C_ARBITRATION,
			DC_I2C_SW_PRIORITY);

		dal_write_reg(i2c_engine->base.ctx,
				mmDC_I2C_ARBITRATION, value);
	}

	return true;
}