Skip to content

Commit 567b8e9

Browse files
lpovlsenJiri Kosina
authored andcommitted
HID: mcp2221: Fix GPIO output handling
The mcp2221 driver GPIO output handling has has several issues. * A wrong value is used for the GPIO direction. * Wrong offsets are calculated for some GPIO set value/set direction operations, when offset is larger than 0. This has been fixed by introducing proper manifest constants for the direction encoding, and using 'offsetof' when calculating GPIO register offsets. The updated driver has been tested with the Sparx5 pcb134/pcb135 board, which has the mcp2221 device with several (output) GPIO's. Fixes: 328de1c ("HID: mcp2221: add GPIO functionality support") Reviewed-by: Rishi Gupta <gupt21@gmail.com> Signed-off-by: Lars Povlsen <lars.povlsen@microchip.com> Signed-off-by: Jiri Kosina <jkosina@suse.cz>
1 parent 34a9fa2 commit 567b8e9

1 file changed

Lines changed: 39 additions & 9 deletions

File tree

drivers/hid/hid-mcp2221.c

Lines changed: 39 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,36 @@ enum {
4949
MCP2221_ALT_F_NOT_GPIOD = 0xEF,
5050
};
5151

52+
/* MCP GPIO direction encoding */
53+
enum {
54+
MCP2221_DIR_OUT = 0x00,
55+
MCP2221_DIR_IN = 0x01,
56+
};
57+
58+
#define MCP_NGPIO 4
59+
60+
/* MCP GPIO set command layout */
61+
struct mcp_set_gpio {
62+
u8 cmd;
63+
u8 dummy;
64+
struct {
65+
u8 change_value;
66+
u8 value;
67+
u8 change_direction;
68+
u8 direction;
69+
} gpio[MCP_NGPIO];
70+
} __packed;
71+
72+
/* MCP GPIO get command layout */
73+
struct mcp_get_gpio {
74+
u8 cmd;
75+
u8 dummy;
76+
struct {
77+
u8 direction;
78+
u8 value;
79+
} gpio[MCP_NGPIO];
80+
} __packed;
81+
5282
/*
5383
* There is no way to distinguish responses. Therefore next command
5484
* is sent only after response to previous has been received. Mutex
@@ -542,7 +572,7 @@ static int mcp_gpio_get(struct gpio_chip *gc,
542572

543573
mcp->txbuf[0] = MCP2221_GPIO_GET;
544574

545-
mcp->gp_idx = (offset + 1) * 2;
575+
mcp->gp_idx = offsetof(struct mcp_get_gpio, gpio[offset].value);
546576

547577
mutex_lock(&mcp->lock);
548578
ret = mcp_send_data_req_status(mcp, mcp->txbuf, 1);
@@ -559,7 +589,7 @@ static void mcp_gpio_set(struct gpio_chip *gc,
559589
memset(mcp->txbuf, 0, 18);
560590
mcp->txbuf[0] = MCP2221_GPIO_SET;
561591

562-
mcp->gp_idx = ((offset + 1) * 4) - 1;
592+
mcp->gp_idx = offsetof(struct mcp_set_gpio, gpio[offset].value);
563593

564594
mcp->txbuf[mcp->gp_idx - 1] = 1;
565595
mcp->txbuf[mcp->gp_idx] = !!value;
@@ -575,7 +605,7 @@ static int mcp_gpio_dir_set(struct mcp2221 *mcp,
575605
memset(mcp->txbuf, 0, 18);
576606
mcp->txbuf[0] = MCP2221_GPIO_SET;
577607

578-
mcp->gp_idx = (offset + 1) * 5;
608+
mcp->gp_idx = offsetof(struct mcp_set_gpio, gpio[offset].direction);
579609

580610
mcp->txbuf[mcp->gp_idx - 1] = 1;
581611
mcp->txbuf[mcp->gp_idx] = val;
@@ -590,7 +620,7 @@ static int mcp_gpio_direction_input(struct gpio_chip *gc,
590620
struct mcp2221 *mcp = gpiochip_get_data(gc);
591621

592622
mutex_lock(&mcp->lock);
593-
ret = mcp_gpio_dir_set(mcp, offset, 0);
623+
ret = mcp_gpio_dir_set(mcp, offset, MCP2221_DIR_IN);
594624
mutex_unlock(&mcp->lock);
595625

596626
return ret;
@@ -603,7 +633,7 @@ static int mcp_gpio_direction_output(struct gpio_chip *gc,
603633
struct mcp2221 *mcp = gpiochip_get_data(gc);
604634

605635
mutex_lock(&mcp->lock);
606-
ret = mcp_gpio_dir_set(mcp, offset, 1);
636+
ret = mcp_gpio_dir_set(mcp, offset, MCP2221_DIR_OUT);
607637
mutex_unlock(&mcp->lock);
608638

609639
/* Can't configure as output, bailout early */
@@ -623,7 +653,7 @@ static int mcp_gpio_get_direction(struct gpio_chip *gc,
623653

624654
mcp->txbuf[0] = MCP2221_GPIO_GET;
625655

626-
mcp->gp_idx = (offset + 1) * 2;
656+
mcp->gp_idx = offsetof(struct mcp_get_gpio, gpio[offset].direction);
627657

628658
mutex_lock(&mcp->lock);
629659
ret = mcp_send_data_req_status(mcp, mcp->txbuf, 1);
@@ -632,7 +662,7 @@ static int mcp_gpio_get_direction(struct gpio_chip *gc,
632662
if (ret)
633663
return ret;
634664

635-
if (mcp->gpio_dir)
665+
if (mcp->gpio_dir == MCP2221_DIR_IN)
636666
return GPIO_LINE_DIRECTION_IN;
637667

638668
return GPIO_LINE_DIRECTION_OUT;
@@ -758,7 +788,7 @@ static int mcp2221_raw_event(struct hid_device *hdev,
758788
mcp->status = -ENOENT;
759789
} else {
760790
mcp->status = !!data[mcp->gp_idx];
761-
mcp->gpio_dir = !!data[mcp->gp_idx + 1];
791+
mcp->gpio_dir = data[mcp->gp_idx + 1];
762792
}
763793
break;
764794
default:
@@ -860,7 +890,7 @@ static int mcp2221_probe(struct hid_device *hdev,
860890
mcp->gc->get_direction = mcp_gpio_get_direction;
861891
mcp->gc->set = mcp_gpio_set;
862892
mcp->gc->get = mcp_gpio_get;
863-
mcp->gc->ngpio = 4;
893+
mcp->gc->ngpio = MCP_NGPIO;
864894
mcp->gc->base = -1;
865895
mcp->gc->can_sleep = 1;
866896
mcp->gc->parent = &hdev->dev;

0 commit comments

Comments
 (0)