Skip to content

Commit 7882637

Browse files
Mstrodlgregkh
authored andcommitted
gpio: mpsse: add quirk support
[ Upstream commit f13b0f7 ] Builds out a facility for specifying compatible lines directions and labels for MPSSE-based devices. * dir_in/out are bitmask of lines that can go in/out. 1 means compatible, 0 means incompatible. * names is an array of line names which will be exposed to userspace. Also changes the chip label format to include some more useful information about the device to help identify it from userspace. Signed-off-by: Mary Strodl <mstrodl@csh.rit.edu> Reviewed-by: Dan Carpenter <dan.carpenter@linaro.org> Reviewed-by: Linus Walleij <linus.walleij@linaro.org> Link: https://lore.kernel.org/r/20251014133530.3592716-4-mstrodl@csh.rit.edu Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@linaro.org> Stable-dep-of: 1e876e5 ("gpio: mpsse: fix reference leak in gpio_mpsse_probe() error paths") Signed-off-by: Sasha Levin <sashal@kernel.org> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
1 parent 472d900 commit 7882637

1 file changed

Lines changed: 106 additions & 3 deletions

File tree

drivers/gpio/gpio-mpsse.c

Lines changed: 106 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,9 @@ struct mpsse_priv {
2929
u8 gpio_outputs[2]; /* Output states for GPIOs [L, H] */
3030
u8 gpio_dir[2]; /* Directions for GPIOs [L, H] */
3131

32+
unsigned long dir_in; /* Bitmask of valid input pins */
33+
unsigned long dir_out; /* Bitmask of valid output pins */
34+
3235
u8 *bulk_in_buf; /* Extra recv buffer to grab status bytes */
3336

3437
struct usb_endpoint_descriptor *bulk_in;
@@ -54,6 +57,14 @@ struct bulk_desc {
5457
int timeout;
5558
};
5659

60+
#define MPSSE_NGPIO 16
61+
62+
struct mpsse_quirk {
63+
const char *names[MPSSE_NGPIO]; /* Pin names, if applicable */
64+
unsigned long dir_in; /* Bitmask of valid input pins */
65+
unsigned long dir_out; /* Bitmask of valid output pins */
66+
};
67+
5768
static const struct usb_device_id gpio_mpsse_table[] = {
5869
{ USB_DEVICE(0x0c52, 0xa064) }, /* SeaLevel Systems, Inc. */
5970
{ } /* Terminating entry */
@@ -171,13 +182,43 @@ static int gpio_mpsse_get_bank(struct mpsse_priv *priv, u8 bank)
171182
return buf;
172183
}
173184

185+
static int mpsse_ensure_supported(struct gpio_chip *chip,
186+
unsigned long mask, int direction)
187+
{
188+
unsigned long supported, unsupported;
189+
char *type = "input";
190+
struct mpsse_priv *priv = gpiochip_get_data(chip);
191+
192+
supported = priv->dir_in;
193+
if (direction == GPIO_LINE_DIRECTION_OUT) {
194+
supported = priv->dir_out;
195+
type = "output";
196+
}
197+
198+
/* An invalid bit was in the provided mask */
199+
unsupported = mask & ~supported;
200+
if (unsupported) {
201+
dev_err(&priv->udev->dev,
202+
"mpsse: GPIO %lu doesn't support %s\n",
203+
find_first_bit(&unsupported, sizeof(unsupported) * 8),
204+
type);
205+
return -EOPNOTSUPP;
206+
}
207+
208+
return 0;
209+
}
210+
174211
static int gpio_mpsse_set_multiple(struct gpio_chip *chip, unsigned long *mask,
175212
unsigned long *bits)
176213
{
177214
unsigned long i, bank, bank_mask, bank_bits;
178215
int ret;
179216
struct mpsse_priv *priv = gpiochip_get_data(chip);
180217

218+
ret = mpsse_ensure_supported(chip, *mask, GPIO_LINE_DIRECTION_OUT);
219+
if (ret)
220+
return ret;
221+
181222
guard(mutex)(&priv->io_mutex);
182223
for_each_set_clump8(i, bank_mask, mask, chip->ngpio) {
183224
bank = i / 8;
@@ -205,6 +246,10 @@ static int gpio_mpsse_get_multiple(struct gpio_chip *chip, unsigned long *mask,
205246
int ret;
206247
struct mpsse_priv *priv = gpiochip_get_data(chip);
207248

249+
ret = mpsse_ensure_supported(chip, *mask, GPIO_LINE_DIRECTION_IN);
250+
if (ret)
251+
return ret;
252+
208253
guard(mutex)(&priv->io_mutex);
209254
for_each_set_clump8(i, bank_mask, mask, chip->ngpio) {
210255
bank = i / 8;
@@ -253,10 +298,15 @@ static int gpio_mpsse_gpio_set(struct gpio_chip *chip, unsigned int offset,
253298
static int gpio_mpsse_direction_output(struct gpio_chip *chip,
254299
unsigned int offset, int value)
255300
{
301+
int ret;
256302
struct mpsse_priv *priv = gpiochip_get_data(chip);
257303
int bank = (offset & 8) >> 3;
258304
int bank_offset = offset & 7;
259305

306+
ret = mpsse_ensure_supported(chip, BIT(offset), GPIO_LINE_DIRECTION_OUT);
307+
if (ret)
308+
return ret;
309+
260310
scoped_guard(mutex, &priv->io_mutex)
261311
priv->gpio_dir[bank] |= BIT(bank_offset);
262312

@@ -266,10 +316,15 @@ static int gpio_mpsse_direction_output(struct gpio_chip *chip,
266316
static int gpio_mpsse_direction_input(struct gpio_chip *chip,
267317
unsigned int offset)
268318
{
319+
int ret;
269320
struct mpsse_priv *priv = gpiochip_get_data(chip);
270321
int bank = (offset & 8) >> 3;
271322
int bank_offset = offset & 7;
272323

324+
ret = mpsse_ensure_supported(chip, BIT(offset), GPIO_LINE_DIRECTION_IN);
325+
if (ret)
326+
return ret;
327+
273328
guard(mutex)(&priv->io_mutex);
274329
priv->gpio_dir[bank] &= ~BIT(bank_offset);
275330
gpio_mpsse_set_bank(priv, bank);
@@ -483,18 +538,49 @@ static void gpio_mpsse_ida_remove(void *data)
483538
ida_free(&gpio_mpsse_ida, priv->id);
484539
}
485540

541+
static int mpsse_init_valid_mask(struct gpio_chip *chip,
542+
unsigned long *valid_mask,
543+
unsigned int ngpios)
544+
{
545+
struct mpsse_priv *priv = gpiochip_get_data(chip);
546+
547+
if (WARN_ON(priv == NULL))
548+
return -ENODEV;
549+
550+
*valid_mask = priv->dir_in | priv->dir_out;
551+
552+
return 0;
553+
}
554+
555+
static void mpsse_irq_init_valid_mask(struct gpio_chip *chip,
556+
unsigned long *valid_mask,
557+
unsigned int ngpios)
558+
{
559+
struct mpsse_priv *priv = gpiochip_get_data(chip);
560+
561+
if (WARN_ON(priv == NULL))
562+
return;
563+
564+
/* Can only use IRQ on input capable pins */
565+
*valid_mask = priv->dir_in;
566+
}
567+
486568
static int gpio_mpsse_probe(struct usb_interface *interface,
487569
const struct usb_device_id *id)
488570
{
489571
struct mpsse_priv *priv;
490572
struct device *dev;
573+
char *serial;
491574
int err;
575+
struct mpsse_quirk *quirk = (void *)id->driver_info;
492576

493577
dev = &interface->dev;
494578
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
495579
if (!priv)
496580
return -ENOMEM;
497581

582+
INIT_LIST_HEAD(&priv->workers);
583+
498584
priv->udev = usb_get_dev(interface_to_usbdev(interface));
499585
priv->intf = interface;
500586
priv->intf_id = interface->cur_altsetting->desc.bInterfaceNumber;
@@ -521,9 +607,15 @@ static int gpio_mpsse_probe(struct usb_interface *interface,
521607

522608
raw_spin_lock_init(&priv->irq_spin);
523609

610+
serial = priv->udev->serial;
611+
if (!serial)
612+
serial = "NONE";
613+
524614
priv->gpio.label = devm_kasprintf(dev, GFP_KERNEL,
525-
"gpio-mpsse.%d.%d",
526-
priv->id, priv->intf_id);
615+
"MPSSE%04x:%04x.%d.%d.%s",
616+
id->idVendor, id->idProduct,
617+
priv->intf_id, priv->id,
618+
serial);
527619
if (!priv->gpio.label)
528620
return -ENOMEM;
529621

@@ -537,10 +629,20 @@ static int gpio_mpsse_probe(struct usb_interface *interface,
537629
priv->gpio.get_multiple = gpio_mpsse_get_multiple;
538630
priv->gpio.set_multiple = gpio_mpsse_set_multiple;
539631
priv->gpio.base = -1;
540-
priv->gpio.ngpio = 16;
632+
priv->gpio.ngpio = MPSSE_NGPIO;
541633
priv->gpio.offset = priv->intf_id * priv->gpio.ngpio;
542634
priv->gpio.can_sleep = 1;
543635

636+
if (quirk) {
637+
priv->dir_out = quirk->dir_out;
638+
priv->dir_in = quirk->dir_in;
639+
priv->gpio.names = quirk->names;
640+
priv->gpio.init_valid_mask = mpsse_init_valid_mask;
641+
} else {
642+
priv->dir_in = U16_MAX;
643+
priv->dir_out = U16_MAX;
644+
}
645+
544646
err = usb_find_common_endpoints(interface->cur_altsetting,
545647
&priv->bulk_in, &priv->bulk_out,
546648
NULL, NULL);
@@ -579,6 +681,7 @@ static int gpio_mpsse_probe(struct usb_interface *interface,
579681
priv->gpio.irq.parents = NULL;
580682
priv->gpio.irq.default_type = IRQ_TYPE_NONE;
581683
priv->gpio.irq.handler = handle_simple_irq;
684+
priv->gpio.irq.init_valid_mask = mpsse_irq_init_valid_mask;
582685

583686
err = devm_gpiochip_add_data(dev, &priv->gpio, priv);
584687
if (err)

0 commit comments

Comments
 (0)