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