Skip to content

Commit 328de1c

Browse files
RishiGupta12Jiri Kosina
authored andcommitted
HID: mcp2221: add GPIO functionality support
MCP2221 has 4 pins that can be used as GPIO or configured for alternate functionality such as clock generation and IRQ detection. This patch adds support for GPIO functionality. To set direction of a pin or to toggle its state after it has been configured as GPIO, driver sends command to mcp2221 and parses response received from mcp2221. Based on this response either 0 or appropriate error code is returned to GPIO framework. To get the direction or current state of a pin, driver sends command and read response from the device. Based on the response received from device direction or value is sent to the GPIO framework. Command from driver to mcp2221 device are output report. Response received from mcp2221 is input report. Datasheet (page 45-48) contains details about how to decode the response received from device: http://ww1.microchip.com/downloads/en/DeviceDoc/20005565B.pdf Signed-off-by: Rishi Gupta <[email protected]> Reviewed-by: Linus Walleij <[email protected]> Signed-off-by: Jiri Kosina <[email protected]>
1 parent c101e9b commit 328de1c

File tree

1 file changed

+169
-0
lines changed

1 file changed

+169
-0
lines changed

drivers/hid/hid-mcp2221.c

Lines changed: 169 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
#include <linux/hid.h>
1616
#include <linux/hidraw.h>
1717
#include <linux/i2c.h>
18+
#include <linux/gpio/driver.h>
1819
#include "hid-ids.h"
1920

2021
/* Commands codes in a raw output report */
@@ -27,6 +28,8 @@ enum {
2728
MCP2221_I2C_PARAM_OR_STATUS = 0x10,
2829
MCP2221_I2C_SET_SPEED = 0x20,
2930
MCP2221_I2C_CANCEL = 0x10,
31+
MCP2221_GPIO_SET = 0x50,
32+
MCP2221_GPIO_GET = 0x51,
3033
};
3134

3235
/* Response codes in a raw input report */
@@ -42,6 +45,8 @@ enum {
4245
MCP2221_I2C_WRADDRL_SEND = 0x21,
4346
MCP2221_I2C_ADDR_NACK = 0x25,
4447
MCP2221_I2C_READ_COMPL = 0x55,
48+
MCP2221_ALT_F_NOT_GPIOV = 0xEE,
49+
MCP2221_ALT_F_NOT_GPIOD = 0xEF,
4550
};
4651

4752
/*
@@ -59,6 +64,9 @@ struct mcp2221 {
5964
int rxbuf_idx;
6065
int status;
6166
u8 cur_i2c_clk_div;
67+
struct gpio_chip *gc;
68+
u8 gp_idx;
69+
u8 gpio_dir;
6270
};
6371

6472
/*
@@ -526,6 +534,110 @@ static const struct i2c_algorithm mcp_i2c_algo = {
526534
.functionality = mcp_i2c_func,
527535
};
528536

537+
static int mcp_gpio_get(struct gpio_chip *gc,
538+
unsigned int offset)
539+
{
540+
int ret;
541+
struct mcp2221 *mcp = gpiochip_get_data(gc);
542+
543+
mcp->txbuf[0] = MCP2221_GPIO_GET;
544+
545+
mcp->gp_idx = (offset + 1) * 2;
546+
547+
mutex_lock(&mcp->lock);
548+
ret = mcp_send_data_req_status(mcp, mcp->txbuf, 1);
549+
mutex_unlock(&mcp->lock);
550+
551+
return ret;
552+
}
553+
554+
static void mcp_gpio_set(struct gpio_chip *gc,
555+
unsigned int offset, int value)
556+
{
557+
struct mcp2221 *mcp = gpiochip_get_data(gc);
558+
559+
memset(mcp->txbuf, 0, 18);
560+
mcp->txbuf[0] = MCP2221_GPIO_SET;
561+
562+
mcp->gp_idx = ((offset + 1) * 4) - 1;
563+
564+
mcp->txbuf[mcp->gp_idx - 1] = 1;
565+
mcp->txbuf[mcp->gp_idx] = !!value;
566+
567+
mutex_lock(&mcp->lock);
568+
mcp_send_data_req_status(mcp, mcp->txbuf, 18);
569+
mutex_unlock(&mcp->lock);
570+
}
571+
572+
static int mcp_gpio_dir_set(struct mcp2221 *mcp,
573+
unsigned int offset, u8 val)
574+
{
575+
memset(mcp->txbuf, 0, 18);
576+
mcp->txbuf[0] = MCP2221_GPIO_SET;
577+
578+
mcp->gp_idx = (offset + 1) * 5;
579+
580+
mcp->txbuf[mcp->gp_idx - 1] = 1;
581+
mcp->txbuf[mcp->gp_idx] = val;
582+
583+
return mcp_send_data_req_status(mcp, mcp->txbuf, 18);
584+
}
585+
586+
static int mcp_gpio_direction_input(struct gpio_chip *gc,
587+
unsigned int offset)
588+
{
589+
int ret;
590+
struct mcp2221 *mcp = gpiochip_get_data(gc);
591+
592+
mutex_lock(&mcp->lock);
593+
ret = mcp_gpio_dir_set(mcp, offset, 0);
594+
mutex_unlock(&mcp->lock);
595+
596+
return ret;
597+
}
598+
599+
static int mcp_gpio_direction_output(struct gpio_chip *gc,
600+
unsigned int offset, int value)
601+
{
602+
int ret;
603+
struct mcp2221 *mcp = gpiochip_get_data(gc);
604+
605+
mutex_lock(&mcp->lock);
606+
ret = mcp_gpio_dir_set(mcp, offset, 1);
607+
mutex_unlock(&mcp->lock);
608+
609+
/* Can't configure as output, bailout early */
610+
if (ret)
611+
return ret;
612+
613+
mcp_gpio_set(gc, offset, value);
614+
615+
return 0;
616+
}
617+
618+
static int mcp_gpio_get_direction(struct gpio_chip *gc,
619+
unsigned int offset)
620+
{
621+
int ret;
622+
struct mcp2221 *mcp = gpiochip_get_data(gc);
623+
624+
mcp->txbuf[0] = MCP2221_GPIO_GET;
625+
626+
mcp->gp_idx = (offset + 1) * 2;
627+
628+
mutex_lock(&mcp->lock);
629+
ret = mcp_send_data_req_status(mcp, mcp->txbuf, 1);
630+
mutex_unlock(&mcp->lock);
631+
632+
if (ret)
633+
return ret;
634+
635+
if (mcp->gpio_dir)
636+
return GPIO_LINE_DIRECTION_IN;
637+
638+
return GPIO_LINE_DIRECTION_OUT;
639+
}
640+
529641
/* Gives current state of i2c engine inside mcp2221 */
530642
static int mcp_get_i2c_eng_state(struct mcp2221 *mcp,
531643
u8 *data, u8 idx)
@@ -638,6 +750,39 @@ static int mcp2221_raw_event(struct hid_device *hdev,
638750
complete(&mcp->wait_in_report);
639751
break;
640752

753+
case MCP2221_GPIO_GET:
754+
switch (data[1]) {
755+
case MCP2221_SUCCESS:
756+
if ((data[mcp->gp_idx] == MCP2221_ALT_F_NOT_GPIOV) ||
757+
(data[mcp->gp_idx + 1] == MCP2221_ALT_F_NOT_GPIOD)) {
758+
mcp->status = -ENOENT;
759+
} else {
760+
mcp->status = !!data[mcp->gp_idx];
761+
mcp->gpio_dir = !!data[mcp->gp_idx + 1];
762+
}
763+
break;
764+
default:
765+
mcp->status = -EAGAIN;
766+
}
767+
complete(&mcp->wait_in_report);
768+
break;
769+
770+
case MCP2221_GPIO_SET:
771+
switch (data[1]) {
772+
case MCP2221_SUCCESS:
773+
if ((data[mcp->gp_idx] == MCP2221_ALT_F_NOT_GPIOV) ||
774+
(data[mcp->gp_idx - 1] == MCP2221_ALT_F_NOT_GPIOV)) {
775+
mcp->status = -ENOENT;
776+
} else {
777+
mcp->status = 0;
778+
}
779+
break;
780+
default:
781+
mcp->status = -EAGAIN;
782+
}
783+
complete(&mcp->wait_in_report);
784+
break;
785+
641786
default:
642787
mcp->status = -EIO;
643788
complete(&mcp->wait_in_report);
@@ -702,8 +847,32 @@ static int mcp2221_probe(struct hid_device *hdev,
702847
}
703848
i2c_set_adapdata(&mcp->adapter, mcp);
704849

850+
/* Setup GPIO chip */
851+
mcp->gc = devm_kzalloc(&hdev->dev, sizeof(*mcp->gc), GFP_KERNEL);
852+
if (!mcp->gc) {
853+
ret = -ENOMEM;
854+
goto err_gc;
855+
}
856+
857+
mcp->gc->label = "mcp2221_gpio";
858+
mcp->gc->direction_input = mcp_gpio_direction_input;
859+
mcp->gc->direction_output = mcp_gpio_direction_output;
860+
mcp->gc->get_direction = mcp_gpio_get_direction;
861+
mcp->gc->set = mcp_gpio_set;
862+
mcp->gc->get = mcp_gpio_get;
863+
mcp->gc->ngpio = 4;
864+
mcp->gc->base = -1;
865+
mcp->gc->can_sleep = 1;
866+
mcp->gc->parent = &hdev->dev;
867+
868+
ret = devm_gpiochip_add_data(&hdev->dev, mcp->gc, mcp);
869+
if (ret)
870+
goto err_gc;
871+
705872
return 0;
706873

874+
err_gc:
875+
i2c_del_adapter(&mcp->adapter);
707876
err_i2c:
708877
hid_hw_close(mcp->hdev);
709878
err_hstop:

0 commit comments

Comments
 (0)