[PATCH 1/2] HID: mcp2221: enable HID I/O during probe

Subsystems: hid core layer, mcp2221a microchip usb-hid to i2c bridge driver, the rest

STALE1648d

6 messages, 4 authors, 2022-04-06 · open the first message on its own page

[PATCH 1/2] HID: mcp2221: enable HID I/O during probe

From: Tobias Junghans <hidden>
Date: 2021-08-18 15:28:35

devm_gpiochip_add_data() calls the gpio_chip->get_direction handler
for each line, resulting in device I/O in mcp_gpio_get_direction().
However unless hid_device_io_start() is called, mcp2221_raw_event()
is not called during probe, causing mcp_gpio_get_direction() to time
out. This fixes that probing takes 12 seconds to complete.

Signed-off-by: Tobias Junghans <redacted>
---
 drivers/hid/hid-mcp2221.c | 3 +++
 1 file changed, 3 insertions(+)
diff --git a/drivers/hid/hid-mcp2221.c b/drivers/hid/hid-mcp2221.c
index 4211b9839209..8e54173b195c 100644
--- a/drivers/hid/hid-mcp2221.c
+++ b/drivers/hid/hid-mcp2221.c
@@ -895,7 +895,10 @@ static int mcp2221_probe(struct hid_device *hdev,
 	mcp->gc->can_sleep = 1;
 	mcp->gc->parent = &hdev->dev;
 
+	hid_device_io_start(hdev);
 	ret = devm_gpiochip_add_data(&hdev->dev, mcp->gc, mcp);
+	hid_device_io_stop(hdev);
+
 	if (ret)
 		goto err_gc;
 
-- 
2.25.1

[PATCH 2/2] HID: mcp2221: configure GP pins for GPIO function

From: Tobias Junghans <hidden>
Date: 2021-08-18 15:28:45

Per default the GP pins of an MCP2221 are designated to a certain
dedicated or alternate function. While it's possible to change these
defaults by manually updating the internal flash, it's much more
convenient and safe to configure the GP pins as GPIOs automatically
at runtime whenever the corresponding GPIO line is requested. The
previous setting is restored as soon as the GPIO line is freed again.

Signed-off-by: Tobias Junghans <redacted>
---
 drivers/hid/hid-mcp2221.c | 142 ++++++++++++++++++++++++++++++++++++++
 1 file changed, 142 insertions(+)
diff --git a/drivers/hid/hid-mcp2221.c b/drivers/hid/hid-mcp2221.c
index 8e54173b195c..0f2345bd065f 100644
--- a/drivers/hid/hid-mcp2221.c
+++ b/drivers/hid/hid-mcp2221.c
@@ -30,6 +30,8 @@ enum {
 	MCP2221_I2C_CANCEL = 0x10,
 	MCP2221_GPIO_SET = 0x50,
 	MCP2221_GPIO_GET = 0x51,
+	MCP2221_SRAM_SETTINGS_SET = 0x60,
+	MCP2221_SRAM_SETTINGS_GET = 0x61,
 };
 
 /* Response codes in a raw input report */
@@ -56,6 +58,7 @@ enum {
 };
 
 #define MCP_NGPIO	4
+#define MCP_PASSWD_LEN	8
 
 /* MCP GPIO set command layout */
 struct mcp_set_gpio {
@@ -79,6 +82,49 @@ struct mcp_get_gpio {
 	} gpio[MCP_NGPIO];
 } __packed;
 
+/* MCP GP settings */
+enum {
+	MCP2221_GP_FUNC_GPIO = 0x00, /* GPIO operation */
+	MCP2221_GP_FUNC_DEDICATED = 0x01, /* dedicated function operation */
+	MCP2221_GP_FUNC_ALT0 = 0x02, /* alternate function 0 */
+	MCP2221_GP_FUNC_ALT1 = 0x03, /* alternate function 1 */
+	MCP2221_GP_FUNC_ALT2 = 0x04, /* alternate function 2 */
+	MCP2221_GP_GPIO_DIR_IN = 0x08, /* GPIO input mode */
+	MCP2221_GP_GPIO_OUT_VALUE = 0x10, /* GPIO output value */
+};
+
+/* MCP SRAM settings set command layout */
+struct mcp_set_sram_settings {
+	u8 cmd;
+	u8 dummy;
+	u8 clk_out_div;
+	u8 dac_voltage_ref;
+	u8 dac_output_value;
+	u8 adc_voltage_ref;
+	u8 interrupt_detection;
+	u8 alter_gp_settings;
+	u8 gp_settings[MCP_NGPIO];
+} __packed;
+
+/* MCP SRAM settings set command layout */
+struct mcp_get_sram_settings {
+	u8 cmd;
+	u8 dummy;
+	u8 len_chip_settings;
+	u8 len_gp_settings;
+	u8 init_values;
+	u8 clk_out_div;
+	u8 dac_settings;
+	u8 interrupt_adc_settings;
+	u16 usb_vid;
+	u16 usb_pid;
+	u8 usb_pwr_attrs;
+	u8 usb_req_current;
+	u8 password[MCP_PASSWD_LEN];
+	u8 gp_settings[MCP_NGPIO];
+} __packed;
+
+
 /*
  * There is no way to distinguish responses. Therefore next command
  * is sent only after response to previous has been received. Mutex
@@ -97,6 +143,8 @@ struct mcp2221 {
 	struct gpio_chip *gc;
 	u8 gp_idx;
 	u8 gpio_dir;
+	u8 gp_default_settings[MCP_NGPIO];
+	u8 gp_runtime_settings[MCP_NGPIO];
 };
 
 /*
@@ -668,6 +716,63 @@ static int mcp_gpio_get_direction(struct gpio_chip *gc,
 	return GPIO_LINE_DIRECTION_OUT;
 }
 
+static int mcp_get_gp_default_settings(struct mcp2221 *mcp)
+{
+	int ret;
+
+	mcp->txbuf[0] = MCP2221_SRAM_SETTINGS_GET;
+
+	mutex_lock(&mcp->lock);
+	ret = mcp_send_data_req_status(mcp, mcp->txbuf, 1);
+	mutex_unlock(&mcp->lock);
+
+	return ret;
+}
+
+static int mcp_configure_gp(struct mcp2221 *mcp, unsigned int offset, u8 val)
+{
+	int ret;
+
+	memset(mcp->txbuf, 0, sizeof(struct mcp_set_sram_settings));
+
+	mcp->txbuf[0] = MCP2221_SRAM_SETTINGS_SET;
+	mcp->txbuf[offsetof(struct mcp_set_sram_settings, alter_gp_settings)] = 0x80;
+
+	mcp->gp_runtime_settings[offset] = val;
+	mcp->gp_idx = offsetof(struct mcp_set_sram_settings, gp_settings[0]);
+	memcpy(&mcp->txbuf[mcp->gp_idx], mcp->gp_runtime_settings,
+			sizeof(mcp->gp_runtime_settings));
+
+	mutex_lock(&mcp->lock);
+	ret = mcp_send_data_req_status(mcp, mcp->txbuf, sizeof(struct mcp_set_sram_settings));
+	mutex_unlock(&mcp->lock);
+
+	return ret;
+}
+
+static int mcp_gpio_request(struct gpio_chip *gc, unsigned int offset)
+{
+	int ret;
+	struct mcp2221 *mcp = gpiochip_get_data(gc);
+
+	ret = mcp_configure_gp(mcp, offset, MCP2221_GP_FUNC_GPIO |
+					MCP2221_GP_GPIO_DIR_IN);
+	if (ret) {
+		hid_err(mcp->hdev, "failed to set GP function\n");
+		return ret;
+	}
+
+	return ret;
+}
+
+static void mcp_gpio_free(struct gpio_chip *gc, unsigned int offset)
+{
+	struct mcp2221 *mcp = gpiochip_get_data(gc);
+
+	if (mcp_configure_gp(mcp, offset, mcp->gp_default_settings[offset]))
+		hid_warn(mcp->hdev, "failed to restore GP function\n");
+}
+
 /* Gives current state of i2c engine inside mcp2221 */
 static int mcp_get_i2c_eng_state(struct mcp2221 *mcp,
 				u8 *data, u8 idx)
@@ -813,6 +918,28 @@ static int mcp2221_raw_event(struct hid_device *hdev,
 		complete(&mcp->wait_in_report);
 		break;
 
+	case MCP2221_SRAM_SETTINGS_GET:
+		if (data[1] == MCP2221_SUCCESS) {
+			int offset = offsetof(struct mcp_get_sram_settings, gp_settings[0]);
+
+			memcpy(mcp->gp_default_settings, &data[offset],
+							sizeof(mcp->gp_default_settings));
+			mcp->status = 0;
+		} else {
+			mcp->status = -EIO;
+		}
+		complete(&mcp->wait_in_report);
+		break;
+
+	case MCP2221_SRAM_SETTINGS_SET:
+		if (data[1] == MCP2221_SUCCESS)
+			mcp->status = 0;
+		else
+			mcp->status = -EIO;
+
+		complete(&mcp->wait_in_report);
+		break;
+
 	default:
 		mcp->status = -EIO;
 		complete(&mcp->wait_in_report);
@@ -890,6 +1017,8 @@ static int mcp2221_probe(struct hid_device *hdev,
 	mcp->gc->get_direction = mcp_gpio_get_direction;
 	mcp->gc->set = mcp_gpio_set;
 	mcp->gc->get = mcp_gpio_get;
+	mcp->gc->request = mcp_gpio_request;
+	mcp->gc->free = mcp_gpio_free;
 	mcp->gc->ngpio = MCP_NGPIO;
 	mcp->gc->base = -1;
 	mcp->gc->can_sleep = 1;
@@ -902,6 +1031,19 @@ static int mcp2221_probe(struct hid_device *hdev,
 	if (ret)
 		goto err_gc;
 
+	hid_device_io_start(hdev);
+	ret = mcp_get_gp_default_settings(mcp);
+	hid_device_io_stop(hdev);
+
+	if (ret) {
+		hid_err(mcp->hdev, "failed to get GP default settings\n");
+		ret = -EIO;
+		goto err_gc;
+	}
+
+	memcpy(mcp->gp_runtime_settings, mcp->gp_default_settings,
+					sizeof(mcp->gp_default_settings));
+
 	return 0;
 
 err_gc:
-- 
2.25.1

Re: [PATCH 2/2] HID: mcp2221: configure GP pins for GPIO function

From: Linus Walleij <hidden>
Date: 2021-09-16 23:14:36

On Wed, Aug 18, 2021 at 5:28 PM Tobias Junghans
[off-list ref] wrote:
Per default the GP pins of an MCP2221 are designated to a certain
dedicated or alternate function. While it's possible to change these
defaults by manually updating the internal flash, it's much more
convenient and safe to configure the GP pins as GPIOs automatically
at runtime whenever the corresponding GPIO line is requested. The
previous setting is restored as soon as the GPIO line is freed again.

Signed-off-by: Tobias Junghans <redacted>
My sympathies are usually on the side of users trying to make
use of their hardware and they should be able to.

For other wrong configured GPIO expanders such as FTDI
a publicly available firmware reflash tool exists, and if that does
not exist for this hardware, I think this approach is legitimate.

Acked-by: Linus Walleij <redacted>

Yours,
Linus Walleij

Re: [PATCH 2/2] HID: mcp2221: configure GP pins for GPIO function

From: rishi gupta <gupt21@gmail.com>
Date: 2021-09-19 10:43:09

Thanks Linus.

Hi Tobias, few observations on code:
1. Copy-paste error; please change set to get in comments for struct
mcp_get_sram_settings.
2. If mcp_configure_gp() fails, we have invalid copy of
mcp->gp_runtime_settings (new value is not set actually but we have
updated this array with new value)..

Regards,
Rishi

On Fri, Sep 17, 2021 at 4:44 AM Linus Walleij [off-list ref] wrote:
On Wed, Aug 18, 2021 at 5:28 PM Tobias Junghans
[off-list ref] wrote:
quoted
Per default the GP pins of an MCP2221 are designated to a certain
dedicated or alternate function. While it's possible to change these
defaults by manually updating the internal flash, it's much more
convenient and safe to configure the GP pins as GPIOs automatically
at runtime whenever the corresponding GPIO line is requested. The
previous setting is restored as soon as the GPIO line is freed again.

Signed-off-by: Tobias Junghans <redacted>
My sympathies are usually on the side of users trying to make
use of their hardware and they should be able to.

For other wrong configured GPIO expanders such as FTDI
a publicly available firmware reflash tool exists, and if that does
not exist for this hardware, I think this approach is legitimate.

Acked-by: Linus Walleij <redacted>

Yours,
Linus Walleij

Re: [PATCH 2/2] HID: mcp2221: configure GP pins for GPIO function

From: Jim Posen <hidden>
Date: 2021-09-24 17:37:02

Thanks for submitting this patch Tobias, I ran into the same issue using the GPIO pins on the MCP2221.

I don't have prior experience with Linux driver dev, but I'm going to add some thoughts anyway.

The approach of configuring the pins for GPIO operation on request/free makes sense to me as opposed to assuming they were already configured somehow beforehand. Aside from being inconvenient, assuming correct configuration seems like a recipe for accidentally shorting the pin, which Rishi is concerned about.

With regards to your patch, I find it odd that the handler for the MCP2221_SRAM_SETTINGS_GET event sets gp_default_settings. Instead, I suggest it set gp_runtime_settings then memcpys from gp_runtime_settings to gp_default_settings at the bottom of probe. And rename mcp_get_gp_default_settings to mcp_get_gp_settings.

I ran into another bug in this driver which I think makes sense to fix in this patch set: direction and value are flipped in the mcp_get_gpio struct. According to the data sheet, value comes before direction for each pin.

With this patch and that last bug fixed, I have tested the driver and GPIO operations are now working for me.

Re: [PATCH 1/2] HID: mcp2221: enable HID I/O during probe

From: rishi gupta <gupt21@gmail.com>
Date: 2022-04-06 17:43:44

Thanks Tobias for the patch!

Reviewed-by: Rishi Gupta <gupt21@gmail.com>


On Wed, Aug 18, 2021 at 8:28 AM Tobias Junghans
[off-list ref] wrote:
quoted hunk
devm_gpiochip_add_data() calls the gpio_chip->get_direction handler
for each line, resulting in device I/O in mcp_gpio_get_direction().
However unless hid_device_io_start() is called, mcp2221_raw_event()
is not called during probe, causing mcp_gpio_get_direction() to time
out. This fixes that probing takes 12 seconds to complete.

Signed-off-by: Tobias Junghans <redacted>
---
 drivers/hid/hid-mcp2221.c | 3 +++
 1 file changed, 3 insertions(+)
diff --git a/drivers/hid/hid-mcp2221.c b/drivers/hid/hid-mcp2221.c
index 4211b9839209..8e54173b195c 100644
--- a/drivers/hid/hid-mcp2221.c
+++ b/drivers/hid/hid-mcp2221.c
@@ -895,7 +895,10 @@ static int mcp2221_probe(struct hid_device *hdev,
        mcp->gc->can_sleep = 1;
        mcp->gc->parent = &hdev->dev;

+       hid_device_io_start(hdev);
        ret = devm_gpiochip_add_data(&hdev->dev, mcp->gc, mcp);
+       hid_device_io_stop(hdev);
+
        if (ret)
                goto err_gc;

--
2.25.1
Keyboard shortcuts
hback out one level
jnext message in thread
kprevious message in thread
ldrill in
Escclose help / fold thread tree
?toggle this help