Commit e9d29f4f authored by Jiri Kosina's avatar Jiri Kosina
Browse files

Merge branch 'for-6.8/mcp2221' into for-linus

- several assorted functional fixes for mcp2221 driver (Hamish Martin)
parents 4dc8c87a 26824686
Loading
Loading
Loading
Loading
+50 −22
Original line number Diff line number Diff line
@@ -49,6 +49,7 @@ enum {
	MCP2221_I2C_MASK_ADDR_NACK = 0x40,
	MCP2221_I2C_WRADDRL_SEND = 0x21,
	MCP2221_I2C_ADDR_NACK = 0x25,
	MCP2221_I2C_READ_PARTIAL = 0x54,
	MCP2221_I2C_READ_COMPL = 0x55,
	MCP2221_ALT_F_NOT_GPIOV = 0xEE,
	MCP2221_ALT_F_NOT_GPIOD = 0xEF,
@@ -187,6 +188,25 @@ static int mcp_cancel_last_cmd(struct mcp2221 *mcp)
	return mcp_send_data_req_status(mcp, mcp->txbuf, 8);
}

/* Check if the last command succeeded or failed and return the result.
 * If the command did fail, cancel that command which will free the i2c bus.
 */
static int mcp_chk_last_cmd_status_free_bus(struct mcp2221 *mcp)
{
	int ret;

	ret = mcp_chk_last_cmd_status(mcp);
	if (ret) {
		/* The last command was a failure.
		 * Send a cancel which will also free the bus.
		 */
		usleep_range(980, 1000);
		mcp_cancel_last_cmd(mcp);
	}

	return ret;
}

static int mcp_set_i2c_speed(struct mcp2221 *mcp)
{
	int ret;
@@ -241,7 +261,7 @@ static int mcp_i2c_write(struct mcp2221 *mcp,
		usleep_range(980, 1000);

		if (last_status) {
			ret = mcp_chk_last_cmd_status(mcp);
			ret = mcp_chk_last_cmd_status_free_bus(mcp);
			if (ret)
				return ret;
		}
@@ -278,6 +298,7 @@ static int mcp_i2c_smbus_read(struct mcp2221 *mcp,
{
	int ret;
	u16 total_len;
	int retries = 0;

	mcp->txbuf[0] = type;
	if (msg) {
@@ -301,19 +322,30 @@ static int mcp_i2c_smbus_read(struct mcp2221 *mcp,
	mcp->rxbuf_idx = 0;

	do {
		/* Wait for the data to be read by the device */
		usleep_range(980, 1000);

		memset(mcp->txbuf, 0, 4);
		mcp->txbuf[0] = MCP2221_I2C_GET_DATA;

		ret = mcp_send_data_req_status(mcp, mcp->txbuf, 1);
		if (ret)
			return ret;

		ret = mcp_chk_last_cmd_status(mcp);
		if (ret)
		if (ret) {
			if (retries < 5) {
				/* The data wasn't ready to read.
				 * Wait a bit longer and try again.
				 */
				usleep_range(90, 100);
				retries++;
			} else {
				return ret;
			}
		} else {
			retries = 0;
		}
	} while (mcp->rxbuf_idx < total_len);

	usleep_range(980, 1000);
	} while (mcp->rxbuf_idx < total_len);
	ret = mcp_chk_last_cmd_status_free_bus(mcp);

	return ret;
}
@@ -328,11 +360,6 @@ static int mcp_i2c_xfer(struct i2c_adapter *adapter,

	mutex_lock(&mcp->lock);

	/* Setting speed before every transaction is required for mcp2221 */
	ret = mcp_set_i2c_speed(mcp);
	if (ret)
		goto exit;

	if (num == 1) {
		if (msgs->flags & I2C_M_RD) {
			ret = mcp_i2c_smbus_read(mcp, msgs, MCP2221_I2C_RD_DATA,
@@ -417,9 +444,7 @@ static int mcp_smbus_write(struct mcp2221 *mcp, u16 addr,
	if (last_status) {
		usleep_range(980, 1000);

		ret = mcp_chk_last_cmd_status(mcp);
		if (ret)
			return ret;
		ret = mcp_chk_last_cmd_status_free_bus(mcp);
	}

	return ret;
@@ -437,10 +462,6 @@ static int mcp_smbus_xfer(struct i2c_adapter *adapter, u16 addr,

	mutex_lock(&mcp->lock);

	ret = mcp_set_i2c_speed(mcp);
	if (ret)
		goto exit;

	switch (size) {

	case I2C_SMBUS_QUICK:
@@ -791,7 +812,8 @@ static int mcp2221_raw_event(struct hid_device *hdev,
				mcp->status = -EIO;
				break;
			}
			if (data[2] == MCP2221_I2C_READ_COMPL) {
			if (data[2] == MCP2221_I2C_READ_COMPL ||
			    data[2] == MCP2221_I2C_READ_PARTIAL) {
				buf = mcp->rxbuf;
				memcpy(&buf[mcp->rxbuf_idx], &data[4], data[3]);
				mcp->rxbuf_idx = mcp->rxbuf_idx + data[3];
@@ -1150,12 +1172,18 @@ static int mcp2221_probe(struct hid_device *hdev,
	if (i2c_clk_freq < 50)
		i2c_clk_freq = 50;
	mcp->cur_i2c_clk_div = (12000000 / (i2c_clk_freq * 1000)) - 3;
	ret = mcp_set_i2c_speed(mcp);
	if (ret) {
		hid_err(hdev, "can't set i2c speed: %d\n", ret);
		return ret;
	}

	mcp->adapter.owner = THIS_MODULE;
	mcp->adapter.class = I2C_CLASS_HWMON;
	mcp->adapter.algo = &mcp_i2c_algo;
	mcp->adapter.retries = 1;
	mcp->adapter.dev.parent = &hdev->dev;
	ACPI_COMPANION_SET(&mcp->adapter.dev, ACPI_COMPANION(hdev->dev.parent));
	snprintf(mcp->adapter.name, sizeof(mcp->adapter.name),
			"MCP2221 usb-i2c bridge");