linux-input.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
From: Tobias Junghans <tobias.junghans@inhub.de>
To: linux-input@vger.kernel.org, Rishi Gupta <gupt21@gmail.com>,
	Jiri Kosina <jikos@kernel.org>,
	Benjamin Tissoires <benjamin.tissoires@redhat.com>
Cc: Tobias Junghans <tobias.junghans@inhub.de>
Subject: [PATCH 2/2] HID: mcp2221: configure GP pins for GPIO function
Date: Wed, 18 Aug 2021 17:27:43 +0200	[thread overview]
Message-ID: <20210818152743.163929-2-tobias.junghans@inhub.de> (raw)
In-Reply-To: <20210818152743.163929-1-tobias.junghans@inhub.de>

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 <tobias.junghans@inhub.de>
---
 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


  reply	other threads:[~2021-08-18 15:28 UTC|newest]

Thread overview: 12+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2021-08-18 15:27 [PATCH 1/2] HID: mcp2221: enable HID I/O during probe Tobias Junghans
2021-08-18 15:27 ` Tobias Junghans [this message]
     [not found]   ` <CALUj-gtmN70HwzvtsO7rss0LXboFC0h9Dgfakt6UEvC_6FdAwA@mail.gmail.com>
2021-08-20  7:18     ` [PATCH 2/2] HID: mcp2221: configure GP pins for GPIO function Tobias Junghans
2021-08-30  9:11       ` rishi gupta
2021-08-30 13:09         ` Tobias Junghans
2021-08-30 13:17           ` rishi gupta
2021-08-30 13:30             ` Tobias Junghans
2021-09-02  7:13               ` rishi gupta
2021-09-16 23:13   ` Linus Walleij
2021-09-19 10:42     ` rishi gupta
2021-09-24 17:36       ` Jim Posen
2022-04-06 15:54 ` [PATCH 1/2] HID: mcp2221: enable HID I/O during probe rishi gupta

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20210818152743.163929-2-tobias.junghans@inhub.de \
    --to=tobias.junghans@inhub.de \
    --cc=benjamin.tissoires@redhat.com \
    --cc=gupt21@gmail.com \
    --cc=jikos@kernel.org \
    --cc=linux-input@vger.kernel.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).