From nobody Mon Feb 9 03:16:09 2026 Received: from greygoose-centos7.csh.rit.edu (greygoose-centos7.csh.rit.edu [129.21.49.170]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 916D1277C9A; Thu, 2 Oct 2025 18:12:07 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=129.21.49.170 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1759428731; cv=none; b=apylqkJQMNPYPtWm7WK1UWXV0sUxbmM8j8pQRLJqGCIFl7kyw8VCp8xBPrkT17yK0wYoizH+ZFgyg6Z8zxnHTSpmTUzWcc3fWcbgyS/EtU4uRbAZ9uU4Puy3OR7Ts5+t2Rw52WZbko3YFSrjr6Ptu+GDGZ2SnbZXWBIzd52UArY= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1759428731; c=relaxed/simple; bh=1iIEJf9upLApf9F3IDmVG2M2QC5J9/5aCBY2pJ/2ECU=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=XoRFodkMumyrw7O9vLu0wTKv9gFZ9EdYaQZgBNdL1QQqbpUomfNbf8h/regF7POOP0LmyixFM/rIOGFyUkcxWUhHUkwR4XKaBx2fOrNYh3BEmtTDLN4xzewqfliCzGihwe6U6gaqZZVQhx1+pyvmlhgRunkEHjq2yLvfVQPDLpg= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=csh.rit.edu; spf=pass smtp.mailfrom=csh.rit.edu; dkim=pass (1024-bit key) header.d=csh.rit.edu header.i=@csh.rit.edu header.b=tg7YvEb7; arc=none smtp.client-ip=129.21.49.170 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=csh.rit.edu Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=csh.rit.edu Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=csh.rit.edu header.i=@csh.rit.edu header.b="tg7YvEb7" Received: from localhost (localhost [127.0.0.1]) by greygoose-centos7.csh.rit.edu (Postfix) with ESMTP id 24A0745735E9; Thu, 2 Oct 2025 14:12:00 -0400 (EDT) Authentication-Results: mail.csh.rit.edu (amavisd-new); dkim=pass (1024-bit key) reason="pass (just generated, assumed good)" header.d=csh.rit.edu DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=csh.rit.edu; h= content-transfer-encoding:mime-version:references:in-reply-to :x-mailer:message-id:date:date:subject:subject:from:from :received:received; s=mail; t=1759428715; x=1761243116; bh=1iIEJ f9upLApf9F3IDmVG2M2QC5J9/5aCBY2pJ/2ECU=; b=tg7YvEb7/lUPv8B95j+Q2 F5brS+ZYh+eAmPEhel+4X7a881YrhITWAaS5jfo/Wbgfie7Nf0PMO0Fetcozh2lL 6ceKBVcy8y85bX2TXuFtoc/h9hN+f/eV8tisYsGlmuRCDrJIzhfBOUs8wPiAikli CD/xXNANBIdqM1b1Lk6wiA= X-Virus-Scanned: amavisd-new at csh.rit.edu Received: from greygoose-centos7.csh.rit.edu ([127.0.0.1]) by localhost (mail.csh.rit.edu [127.0.0.1]) (amavisd-new, port 10026) with ESMTP id jH_Yq4lpUznU; Thu, 2 Oct 2025 14:11:55 -0400 (EDT) Received: from ada.csh.rit.edu (ada.csh.rit.edu [129.21.49.156]) by greygoose-centos7.csh.rit.edu (Postfix) with ESMTP id 62B1240EA0BF; Thu, 2 Oct 2025 14:11:52 -0400 (EDT) From: Mary Strodl To: linux-kernel@vger.kernel.org Cc: tzungbi@kernel.org, dan.carpenter@linaro.org, linus.walleij@linaro.org, brgl@bgdev.pl, linux-gpio@vger.kernel.org, Mary Strodl Subject: [PATCH v3 1/4] gpio: mpsse: propagate error from direction_input Date: Thu, 2 Oct 2025 14:11:33 -0400 Message-ID: <20251002181136.3546798-2-mstrodl@csh.rit.edu> X-Mailer: git-send-email 2.47.0 In-Reply-To: <20251002181136.3546798-1-mstrodl@csh.rit.edu> References: <20251002181136.3546798-1-mstrodl@csh.rit.edu> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" Not sure how I missed this, but errors encountered when setting the direction to input weren't being propagated to the caller. Signed-off-by: Mary Strodl --- drivers/gpio/gpio-mpsse.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/gpio/gpio-mpsse.c b/drivers/gpio/gpio-mpsse.c index 9f42bb30b4ec..c508d9e33054 100644 --- a/drivers/gpio/gpio-mpsse.c +++ b/drivers/gpio/gpio-mpsse.c @@ -261,9 +261,8 @@ static int gpio_mpsse_direction_input(struct gpio_chip = *chip, =20 guard(mutex)(&priv->io_mutex); priv->gpio_dir[bank] &=3D ~BIT(bank_offset); - gpio_mpsse_set_bank(priv, bank); =20 - return 0; + return gpio_mpsse_set_bank(priv, bank); } =20 static int gpio_mpsse_get_direction(struct gpio_chip *chip, --=20 2.47.0 From nobody Mon Feb 9 03:16:09 2026 Received: from greygoose-centos7.csh.rit.edu (greygoose-centos7.csh.rit.edu [129.21.49.170]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 1DC3A25C6F9; Thu, 2 Oct 2025 18:12:05 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=129.21.49.170 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1759428728; cv=none; b=iOpAQNEOMjfxw2SI2sYbhfit0twc4H20K+FEpAG1Znl/91sV4J8kCkorCuM+7fTAqGzDYPi+YpjC0tyzDbMLwswg4XiZFBuwLzsC8D30r/gmn+FWoZLfb3dwJYGheo/79H7x1nc6STEjn7qAisqwWXR2qn3GEYOPyQqdZxTZ6P4= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1759428728; c=relaxed/simple; bh=COl7OlCM3knwtFd2u1lRtK6/6wOjU5PGf+/hmhe/Vfg=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=LhUmqpbq/1FlhFap9b1E17Sw+da0k34uZF5yGCk0gCjneDO68zu2WUUbtr3/+Pie8tQCvURDYAAO4YGGm8jRLZkd2sYBzVQ7ED7lncuB9FgprlCurpJPk/fwDFpXWpqDQ6byY8WKniL8xdyd+Hwv7kzFhdnrjyQKa1IRwF+bnME= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=csh.rit.edu; spf=pass smtp.mailfrom=csh.rit.edu; dkim=pass (1024-bit key) header.d=csh.rit.edu header.i=@csh.rit.edu header.b=EcYISDS/; arc=none smtp.client-ip=129.21.49.170 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=csh.rit.edu Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=csh.rit.edu Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=csh.rit.edu header.i=@csh.rit.edu header.b="EcYISDS/" Received: from localhost (localhost [127.0.0.1]) by greygoose-centos7.csh.rit.edu (Postfix) with ESMTP id 28A68401CF12; Thu, 2 Oct 2025 14:12:05 -0400 (EDT) Authentication-Results: mail.csh.rit.edu (amavisd-new); dkim=pass (1024-bit key) reason="pass (just generated, assumed good)" header.d=csh.rit.edu DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=csh.rit.edu; h= content-transfer-encoding:mime-version:references:in-reply-to :x-mailer:message-id:date:date:subject:subject:from:from :received:received; s=mail; t=1759428720; x=1761243121; bh=COl7O lCM3knwtFd2u1lRtK6/6wOjU5PGf+/hmhe/Vfg=; b=EcYISDS/40MPABHTUvMgb EmqnyvBZ7C331QMvzypL4h37vOYIycMp1aRAEQmIDOxtZ70GCWysUHZk50/r/y9E 9StESQxzJEFMV28R7OmLggg923hX+qQSC7lZbpkRKtbLyF3RGjJcdYCGuWNxXadP hSH/bsuCvbw2s/kAX0QvH4= X-Virus-Scanned: amavisd-new at csh.rit.edu Received: from greygoose-centos7.csh.rit.edu ([127.0.0.1]) by localhost (mail.csh.rit.edu [127.0.0.1]) (amavisd-new, port 10026) with ESMTP id 9o7KEQloIbLl; Thu, 2 Oct 2025 14:12:00 -0400 (EDT) Received: from ada.csh.rit.edu (ada.csh.rit.edu [129.21.49.156]) by greygoose-centos7.csh.rit.edu (Postfix) with ESMTP id 812674133835; Thu, 2 Oct 2025 14:11:52 -0400 (EDT) From: Mary Strodl To: linux-kernel@vger.kernel.org Cc: tzungbi@kernel.org, dan.carpenter@linaro.org, linus.walleij@linaro.org, brgl@bgdev.pl, linux-gpio@vger.kernel.org, Mary Strodl Subject: [PATCH v3 2/4] gpio: mpsse: ensure worker is torn down Date: Thu, 2 Oct 2025 14:11:34 -0400 Message-ID: <20251002181136.3546798-3-mstrodl@csh.rit.edu> X-Mailer: git-send-email 2.47.0 In-Reply-To: <20251002181136.3546798-1-mstrodl@csh.rit.edu> References: <20251002181136.3546798-1-mstrodl@csh.rit.edu> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" When an IRQ worker is running, unplugging the device would cause a crash. The sealevel hardware this driver was written for was not hotpluggable, so I never realized it. This change uses a spinlock to protect a list of workers, which it tears down on disconnect. Signed-off-by: Mary Strodl --- drivers/gpio/gpio-mpsse.c | 106 +++++++++++++++++++++++++++++++++++--- 1 file changed, 99 insertions(+), 7 deletions(-) diff --git a/drivers/gpio/gpio-mpsse.c b/drivers/gpio/gpio-mpsse.c index c508d9e33054..6ec940f6b371 100644 --- a/drivers/gpio/gpio-mpsse.c +++ b/drivers/gpio/gpio-mpsse.c @@ -10,6 +10,7 @@ #include #include #include +#include #include =20 struct mpsse_priv { @@ -17,8 +18,10 @@ struct mpsse_priv { struct usb_device *udev; /* USB device encompassing all MPSSEs */ struct usb_interface *intf; /* USB interface for this MPSSE */ u8 intf_id; /* USB interface number for this MPSSE */ - struct work_struct irq_work; /* polling work thread */ + struct list_head workers; /* polling work threads */ struct mutex irq_mutex; /* lock over irq_data */ + struct mutex irq_race; /* race for polling worker teardown */ + raw_spinlock_t irq_spin; /* protects worker list */ atomic_t irq_type[16]; /* pin -> edge detection type */ atomic_t irq_enabled; int id; @@ -34,6 +37,14 @@ struct mpsse_priv { struct mutex io_mutex; /* sync I/O with disconnect */ }; =20 +struct mpsse_worker { + struct mpsse_priv *priv; + struct work_struct work; + atomic_t cancelled; + struct list_head list; /* linked list */ + struct list_head destroy; /* teardown linked list */ +}; + struct bulk_desc { bool tx; /* direction of bulk transfer */ u8 *data; /* input (tx) or output (rx) */ @@ -283,18 +294,62 @@ static int gpio_mpsse_get_direction(struct gpio_chip = *chip, return ret; } =20 -static void gpio_mpsse_poll(struct work_struct *work) +/* + * Stops all workers except `my_worker`. + * Safe to call only when `irq_race` is held. + */ +static void gpio_mpsse_stop_all_except(struct mpsse_priv *priv, + struct mpsse_worker *my_worker) +{ + struct mpsse_worker *worker, *worker_tmp; + struct list_head destructors =3D LIST_HEAD_INIT(destructors); + + scoped_guard(raw_spinlock_irqsave, &priv->irq_spin) { + list_for_each_entry_safe(worker, worker_tmp, + &priv->workers, list) { + /* Don't stop ourselves */ + if (worker =3D=3D my_worker) + continue; + + list_del(&worker->list); + + /* Give worker a chance to terminate itself */ + atomic_set(&worker->cancelled, 1); + /* Keep track of stuff to cancel */ + INIT_LIST_HEAD(&worker->destroy); + list_add(&worker->destroy, &destructors); + } + } + + list_for_each_entry_safe(worker, worker_tmp, + &destructors, destroy) { + list_del(&worker->destroy); + cancel_work_sync(&worker->work); + kfree(worker); + } +} + +static void gpio_mpsse_poll(struct work_struct *my_work) { unsigned long pin_mask, pin_states, flags; int irq_enabled, offset, err, value, fire_irq, irq, old_value[16], irq_type[16]; - struct mpsse_priv *priv =3D container_of(work, struct mpsse_priv, - irq_work); + struct mpsse_worker *my_worker =3D container_of(my_work, struct mpsse_wor= ker, work); + struct mpsse_priv *priv =3D my_worker->priv; =20 for (offset =3D 0; offset < priv->gpio.ngpio; ++offset) old_value[offset] =3D -1; =20 - while ((irq_enabled =3D atomic_read(&priv->irq_enabled))) { + /* + * We only want one worker. Workers race to acquire irq_race and tear + * down all other workers. This is a cond guard so that we don't deadlock + * trying to cancel a worker. + */ + scoped_cond_guard(mutex_try, return, &priv->irq_race) + gpio_mpsse_stop_all_except(priv, my_worker); + + while ((irq_enabled =3D atomic_read(&priv->irq_enabled)) && + !atomic_read(&my_worker->cancelled)) { usleep_range(MPSSE_POLL_INTERVAL, MPSSE_POLL_INTERVAL + 1000); /* Cleanup will trigger at the end of the loop */ guard(mutex)(&priv->irq_mutex); @@ -369,21 +424,45 @@ static int gpio_mpsse_set_irq_type(struct irq_data *i= rqd, unsigned int type) =20 static void gpio_mpsse_irq_disable(struct irq_data *irqd) { + struct mpsse_worker *worker; struct mpsse_priv *priv =3D irq_data_get_irq_chip_data(irqd); =20 atomic_and(~BIT(irqd->hwirq), &priv->irq_enabled); gpiochip_disable_irq(&priv->gpio, irqd->hwirq); + + /* + * Can't actually do teardown in IRQ context (it blocks). + * As a result, these workers will stick around until irq is reenabled + * or device gets disconnected + */ + scoped_guard(raw_spinlock_irqsave, &priv->irq_spin) + list_for_each_entry(worker, &priv->workers, list) + atomic_set(&worker->cancelled, 1); } =20 static void gpio_mpsse_irq_enable(struct irq_data *irqd) { + struct mpsse_worker *worker; struct mpsse_priv *priv =3D irq_data_get_irq_chip_data(irqd); =20 gpiochip_enable_irq(&priv->gpio, irqd->hwirq); /* If no-one else was using the IRQ, enable it */ if (!atomic_fetch_or(BIT(irqd->hwirq), &priv->irq_enabled)) { - INIT_WORK(&priv->irq_work, gpio_mpsse_poll); - schedule_work(&priv->irq_work); + /* + * Can't be devm because it uses a non-raw spinlock (illegal in + * this context, where a raw spinlock is held by our caller) + */ + worker =3D kzalloc(sizeof(*worker), GFP_NOWAIT); + if (!worker) + return; + + worker->priv =3D priv; + INIT_LIST_HEAD(&worker->list); + INIT_WORK(&worker->work, gpio_mpsse_poll); + schedule_work(&worker->work); + + scoped_guard(raw_spinlock_irqsave, &priv->irq_spin) + list_add(&worker->list, &priv->workers); } } =20 @@ -435,6 +514,12 @@ static int gpio_mpsse_probe(struct usb_interface *inte= rface, if (err) return err; =20 + err =3D devm_mutex_init(dev, &priv->irq_race); + if (err) + return err; + + raw_spin_lock_init(&priv->irq_spin); + priv->gpio.label =3D devm_kasprintf(dev, GFP_KERNEL, "gpio-mpsse.%d.%d", priv->id, priv->intf_id); @@ -505,6 +590,13 @@ static void gpio_mpsse_disconnect(struct usb_interface= *intf) { struct mpsse_priv *priv =3D usb_get_intfdata(intf); =20 + /* + * Lock prevents double-free of worker from here and the teardown + * step at the beginning of gpio_mpsse_poll + */ + scoped_guard(mutex, &priv->irq_race) + gpio_mpsse_stop_all_except(priv, NULL); + priv->intf =3D NULL; usb_set_intfdata(intf, NULL); usb_put_dev(priv->udev); --=20 2.47.0 From nobody Mon Feb 9 03:16:09 2026 Received: from greygoose-centos7.csh.rit.edu (greygoose-centos7.csh.rit.edu [129.21.49.170]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 9440C27978C; Thu, 2 Oct 2025 18:12:10 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=129.21.49.170 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1759428732; cv=none; b=IaQfQKNuwXU/9KXy0RlxzwOPZU2Q9mh6x/lqNHFVOK/tnqBm5F6y384nKBcqH64x3WUF0OjQ5YkiiKkmqMZZ6iN6msiVdMXyaZiWay6HI6IEwyj8DVxdIngenz6J/cnuaQLnYoUeYaF8ZQcVQXV8FSvqKyUWNaNh99dQ9i/d1J8= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1759428732; c=relaxed/simple; bh=ljpreaDWjm26bFkwGysv9tT8k5bxV0YipsYpo6RPX0I=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=HvyYZu3IeN9MpDcJn/hc+ZoazbXBIpgJrwT6PH8wahmbmVR3oRMWm5gYP4ByTU9G8tZytgRHykOmtRbdZAi2P7ecIO/Onmzonc3a6AFwUbxJUGUkB6Cc2Ml4uVe+gMPw+3TqpzI5/jdVH/3Lnqb6414hy4EEw/7ieqi3g4eSkco= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=csh.rit.edu; spf=pass smtp.mailfrom=csh.rit.edu; dkim=pass (1024-bit key) header.d=csh.rit.edu header.i=@csh.rit.edu header.b=nriZkzj/; arc=none smtp.client-ip=129.21.49.170 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=csh.rit.edu Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=csh.rit.edu Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=csh.rit.edu header.i=@csh.rit.edu header.b="nriZkzj/" Received: from localhost (localhost [127.0.0.1]) by greygoose-centos7.csh.rit.edu (Postfix) with ESMTP id 8FD63401CF12; Thu, 2 Oct 2025 14:12:09 -0400 (EDT) Authentication-Results: mail.csh.rit.edu (amavisd-new); dkim=pass (1024-bit key) reason="pass (just generated, assumed good)" header.d=csh.rit.edu DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=csh.rit.edu; h= content-transfer-encoding:mime-version:references:in-reply-to :x-mailer:message-id:date:date:subject:subject:from:from :received:received; s=mail; t=1759428724; x=1761243125; bh=ljpre aDWjm26bFkwGysv9tT8k5bxV0YipsYpo6RPX0I=; b=nriZkzj/Zkg4RUvO22xGA SWu63UQ6c2diPruJMqHC/MtwL5wKKy0+FJO+XmU4LoO3s+SsPzghldKNLH40csjU 7vA78SqYLM9nPyKBf3R/SNap7JzHIFPwK4rRxUkGkkmMIXivm7oYunpwmQynDlbS V98fOcoB6i7+gD/xq76wm0= X-Virus-Scanned: amavisd-new at csh.rit.edu Received: from greygoose-centos7.csh.rit.edu ([127.0.0.1]) by localhost (mail.csh.rit.edu [127.0.0.1]) (amavisd-new, port 10026) with ESMTP id K9K5sjzGkuuI; Thu, 2 Oct 2025 14:12:04 -0400 (EDT) Received: from ada.csh.rit.edu (ada.csh.rit.edu [129.21.49.156]) by greygoose-centos7.csh.rit.edu (Postfix) with ESMTP id A0F8D456DF5D; Thu, 2 Oct 2025 14:11:52 -0400 (EDT) From: Mary Strodl To: linux-kernel@vger.kernel.org Cc: tzungbi@kernel.org, dan.carpenter@linaro.org, linus.walleij@linaro.org, brgl@bgdev.pl, linux-gpio@vger.kernel.org, Mary Strodl Subject: [PATCH v3 3/4] gpio: mpsse: add quirk support Date: Thu, 2 Oct 2025 14:11:35 -0400 Message-ID: <20251002181136.3546798-4-mstrodl@csh.rit.edu> X-Mailer: git-send-email 2.47.0 In-Reply-To: <20251002181136.3546798-1-mstrodl@csh.rit.edu> References: <20251002181136.3546798-1-mstrodl@csh.rit.edu> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" 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. 0 means compatible, 1 means incompatible. This is convenient, because it means if the struct is zeroed out, it supports all pins. * 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 --- drivers/gpio/gpio-mpsse.c | 109 ++++++++++++++++++++++++++++++++++++-- 1 file changed, 106 insertions(+), 3 deletions(-) diff --git a/drivers/gpio/gpio-mpsse.c b/drivers/gpio/gpio-mpsse.c index 6ec940f6b371..c2a344488a23 100644 --- a/drivers/gpio/gpio-mpsse.c +++ b/drivers/gpio/gpio-mpsse.c @@ -29,6 +29,9 @@ struct mpsse_priv { u8 gpio_outputs[2]; /* Output states for GPIOs [L, H] */ u8 gpio_dir[2]; /* Directions for GPIOs [L, H] */ =20 + unsigned long dir_in; /* Bitmask of valid input pins */ + unsigned long dir_out; /* Bitmask of valid output pins */ + u8 *bulk_in_buf; /* Extra recv buffer to grab status bytes */ =20 struct usb_endpoint_descriptor *bulk_in; @@ -54,6 +57,14 @@ struct bulk_desc { int timeout; }; =20 +#define MPSSE_NGPIO 16 + +struct mpsse_quirk { + const char *names[MPSSE_NGPIO]; /* Pin names, if applicable */ + unsigned long dir_in; /* Bitmask of valid input pins */ + unsigned long dir_out; /* Bitmask of valid output pins */ +}; + static const struct usb_device_id gpio_mpsse_table[] =3D { { USB_DEVICE(0x0c52, 0xa064) }, /* SeaLevel Systems, Inc. */ { } /* Terminating entry */ @@ -171,6 +182,32 @@ static int gpio_mpsse_get_bank(struct mpsse_priv *priv= , u8 bank) return buf; } =20 +static int mpsse_ensure_supported(struct gpio_chip *chip, + unsigned long *mask, int direction) +{ + unsigned long supported, unsupported; + char *type =3D "input"; + struct mpsse_priv *priv =3D gpiochip_get_data(chip); + + supported =3D priv->dir_in; + if (direction =3D=3D GPIO_LINE_DIRECTION_OUT) { + supported =3D priv->dir_out; + type =3D "output"; + } + + /* An invalid bit was in the provided mask */ + unsupported =3D *mask & supported; + if (unsupported) { + dev_err(&priv->udev->dev, + "mpsse: GPIO %d doesn't support %s\n", + (int)find_first_bit(&unsupported, sizeof(unsupported) * 8), + type); + return -EOPNOTSUPP; + } + + return 0; +} + static int gpio_mpsse_set_multiple(struct gpio_chip *chip, unsigned long *= mask, unsigned long *bits) { @@ -178,6 +215,10 @@ static int gpio_mpsse_set_multiple(struct gpio_chip *c= hip, unsigned long *mask, int ret; struct mpsse_priv *priv =3D gpiochip_get_data(chip); =20 + ret =3D mpsse_ensure_supported(chip, mask, GPIO_LINE_DIRECTION_OUT); + if (ret) + return ret; + guard(mutex)(&priv->io_mutex); for_each_set_clump8(i, bank_mask, mask, chip->ngpio) { bank =3D i / 8; @@ -205,6 +246,10 @@ static int gpio_mpsse_get_multiple(struct gpio_chip *c= hip, unsigned long *mask, int ret; struct mpsse_priv *priv =3D gpiochip_get_data(chip); =20 + ret =3D mpsse_ensure_supported(chip, mask, GPIO_LINE_DIRECTION_IN); + if (ret) + return ret; + guard(mutex)(&priv->io_mutex); for_each_set_clump8(i, bank_mask, mask, chip->ngpio) { bank =3D i / 8; @@ -253,9 +298,15 @@ static int gpio_mpsse_gpio_set(struct gpio_chip *chip,= unsigned int offset, static int gpio_mpsse_direction_output(struct gpio_chip *chip, unsigned int offset, int value) { + int ret; struct mpsse_priv *priv =3D gpiochip_get_data(chip); int bank =3D (offset & 8) >> 3; int bank_offset =3D offset & 7; + unsigned long mask =3D BIT(offset); + + ret =3D mpsse_ensure_supported(chip, &mask, GPIO_LINE_DIRECTION_OUT); + if (ret) + return ret; =20 scoped_guard(mutex, &priv->io_mutex) priv->gpio_dir[bank] |=3D BIT(bank_offset); @@ -266,9 +317,15 @@ static int gpio_mpsse_direction_output(struct gpio_chi= p *chip, static int gpio_mpsse_direction_input(struct gpio_chip *chip, unsigned int offset) { + int ret; struct mpsse_priv *priv =3D gpiochip_get_data(chip); int bank =3D (offset & 8) >> 3; int bank_offset =3D offset & 7; + unsigned long mask =3D BIT(offset); + + ret =3D mpsse_ensure_supported(chip, &mask, GPIO_LINE_DIRECTION_IN); + if (ret) + return ret; =20 guard(mutex)(&priv->io_mutex); priv->gpio_dir[bank] &=3D ~BIT(bank_offset); @@ -482,18 +539,50 @@ static void gpio_mpsse_ida_remove(void *data) ida_free(&gpio_mpsse_ida, priv->id); } =20 +static int mpsse_init_valid_mask(struct gpio_chip *chip, + unsigned long *valid_mask, + unsigned int ngpios) +{ + struct mpsse_priv *priv =3D gpiochip_get_data(chip); + + if (WARN_ON(priv =3D=3D NULL)) + return -ENODEV; + + /* If bit is set in both, set to 0 (NAND) */ + *valid_mask =3D ~priv->dir_in | ~priv->dir_out; + + return 0; +} + +static void mpsse_irq_init_valid_mask(struct gpio_chip *chip, + unsigned long *valid_mask, + unsigned int ngpios) +{ + struct mpsse_priv *priv =3D gpiochip_get_data(chip); + + if (WARN_ON(priv =3D=3D NULL)) + return; + + /* Can only use IRQ on input capable pins */ + *valid_mask =3D ~priv->dir_in; +} + static int gpio_mpsse_probe(struct usb_interface *interface, const struct usb_device_id *id) { struct mpsse_priv *priv; struct device *dev; + char *serial; int err; + struct mpsse_quirk *quirk =3D (void *)id->driver_info; =20 dev =3D &interface->dev; priv =3D devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); if (!priv) return -ENOMEM; =20 + INIT_LIST_HEAD(&priv->workers); + priv->udev =3D usb_get_dev(interface_to_usbdev(interface)); priv->intf =3D interface; priv->intf_id =3D interface->cur_altsetting->desc.bInterfaceNumber; @@ -520,9 +609,15 @@ static int gpio_mpsse_probe(struct usb_interface *inte= rface, =20 raw_spin_lock_init(&priv->irq_spin); =20 + serial =3D priv->udev->serial; + if (!serial) + serial =3D "NONE"; + priv->gpio.label =3D devm_kasprintf(dev, GFP_KERNEL, - "gpio-mpsse.%d.%d", - priv->id, priv->intf_id); + "MPSSE%04x:%04x.%d.%d.%s", + id->idVendor, id->idProduct, + priv->intf_id, priv->id, + serial); if (!priv->gpio.label) return -ENOMEM; =20 @@ -536,10 +631,17 @@ static int gpio_mpsse_probe(struct usb_interface *int= erface, priv->gpio.get_multiple =3D gpio_mpsse_get_multiple; priv->gpio.set_multiple =3D gpio_mpsse_set_multiple; priv->gpio.base =3D -1; - priv->gpio.ngpio =3D 16; + priv->gpio.ngpio =3D MPSSE_NGPIO; priv->gpio.offset =3D priv->intf_id * priv->gpio.ngpio; priv->gpio.can_sleep =3D 1; =20 + if (quirk) { + priv->dir_out =3D quirk->dir_out; + priv->dir_in =3D quirk->dir_in; + priv->gpio.names =3D quirk->names; + priv->gpio.init_valid_mask =3D mpsse_init_valid_mask; + } + err =3D usb_find_common_endpoints(interface->cur_altsetting, &priv->bulk_in, &priv->bulk_out, NULL, NULL); @@ -578,6 +680,7 @@ static int gpio_mpsse_probe(struct usb_interface *inter= face, priv->gpio.irq.parents =3D NULL; priv->gpio.irq.default_type =3D IRQ_TYPE_NONE; priv->gpio.irq.handler =3D handle_simple_irq; + priv->gpio.irq.init_valid_mask =3D mpsse_irq_init_valid_mask; =20 err =3D devm_gpiochip_add_data(dev, &priv->gpio, priv); if (err) --=20 2.47.0 From nobody Mon Feb 9 03:16:09 2026 Received: from greygoose-centos7.csh.rit.edu (greygoose-centos7.csh.rit.edu [129.21.49.170]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 1DD5027A44A; Thu, 2 Oct 2025 18:12:10 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=129.21.49.170 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1759428733; cv=none; b=nr+D8kFWHDDmhaqK/CFUmbFfL253JX97zGdKImsmVosO8jardOA7PjL+4WzZV8iZjngMH3VYdU4EjQhLS57LSVYwYqj8qhf9yehxV7wtdJS7mC1PJcDdN1KwWDbNHp65ekowMSyE3nnankvu7q8La0oGpzo/z4AWj09Lk5R2lWs= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1759428733; c=relaxed/simple; bh=W2m7Cd/qmNXM1k0QykgSUzIUJ6rontCrfMafYILgWx8=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=ABvII+eInxj+K47GGVRh9Z65L9Mjo1wXpl9pKi/BfXVv4nFoARDtmAuPOHt4CgJjSV4zSpDMlaPxgl8Gu8j4aq2+Niq8BGxAzbnCQcvxhU8OM0RpLMExNk/VDmXkkWTOxnSD4cTNRxXcUelm3LAlvpFoSEvUExhRh9lrNLVw5HI= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=csh.rit.edu; spf=pass smtp.mailfrom=csh.rit.edu; dkim=pass (1024-bit key) header.d=csh.rit.edu header.i=@csh.rit.edu header.b=nPJiCYx1; arc=none smtp.client-ip=129.21.49.170 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=csh.rit.edu Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=csh.rit.edu Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=csh.rit.edu header.i=@csh.rit.edu header.b="nPJiCYx1" Received: from localhost (localhost [127.0.0.1]) by greygoose-centos7.csh.rit.edu (Postfix) with ESMTP id 29FA440EA0BF; Thu, 2 Oct 2025 14:12:10 -0400 (EDT) Authentication-Results: mail.csh.rit.edu (amavisd-new); dkim=pass (1024-bit key) reason="pass (just generated, assumed good)" header.d=csh.rit.edu DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=csh.rit.edu; h= content-transfer-encoding:mime-version:references:in-reply-to :x-mailer:message-id:date:date:subject:subject:from:from :received:received; s=mail; t=1759428725; x=1761243126; bh=W2m7C d/qmNXM1k0QykgSUzIUJ6rontCrfMafYILgWx8=; b=nPJiCYx1fjzxynWYuC4yK +PBoOeMfeskM6IYqp1Nlppirs3Z0ryXsXMxqfIj/0rH3I+FDil1uJgp2zbizCyki iXtQYbpgKoRQOQVbbynjGqV3wQLIbO9ECze5Liu/HaXLwFV7NZQHpX6JdioiEOC+ 8+lSwOJbXfVjvn2XAAT5Pc= X-Virus-Scanned: amavisd-new at csh.rit.edu Received: from greygoose-centos7.csh.rit.edu ([127.0.0.1]) by localhost (mail.csh.rit.edu [127.0.0.1]) (amavisd-new, port 10026) with ESMTP id 9J5Lvaxx6d3d; Thu, 2 Oct 2025 14:12:05 -0400 (EDT) Received: from ada.csh.rit.edu (ada.csh.rit.edu [129.21.49.156]) by greygoose-centos7.csh.rit.edu (Postfix) with ESMTP id BB1D6456D905; Thu, 2 Oct 2025 14:11:52 -0400 (EDT) From: Mary Strodl To: linux-kernel@vger.kernel.org Cc: tzungbi@kernel.org, dan.carpenter@linaro.org, linus.walleij@linaro.org, brgl@bgdev.pl, linux-gpio@vger.kernel.org, Mary Strodl Subject: [PATCH v3 4/4] gpio: mpsse: support bryx radio interface kit Date: Thu, 2 Oct 2025 14:11:36 -0400 Message-ID: <20251002181136.3546798-5-mstrodl@csh.rit.edu> X-Mailer: git-send-email 2.47.0 In-Reply-To: <20251002181136.3546798-1-mstrodl@csh.rit.edu> References: <20251002181136.3546798-1-mstrodl@csh.rit.edu> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" This device is powered by an FT232H, which is very similar to the FT2232H this driver was written for. The key difference is it has only one MPSSE instead of two. As a result, it presents only one USB interface to the system, which conveniently "just works" out of the box with this driver. The brik exposes only two GPIO lines which are hardware limited to only be useful in one direction. As a result, I've restricted things on the driver side to refuse to configure any other lines. This device, unlike the sealevel device I wrote this driver for originally, is hotpluggable, which makes for all sorts of weird edgecases. I've tried my best to stress-test the parts that could go wrong, but given the new usecase, more heads taking a critical look at the teardown and synchronization bits on the driver as a whole would be appreciated. Signed-off-by: Mary Strodl --- drivers/gpio/gpio-mpsse.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/drivers/gpio/gpio-mpsse.c b/drivers/gpio/gpio-mpsse.c index c2a344488a23..3c0645374ba7 100644 --- a/drivers/gpio/gpio-mpsse.c +++ b/drivers/gpio/gpio-mpsse.c @@ -65,8 +65,19 @@ struct mpsse_quirk { unsigned long dir_out; /* Bitmask of valid output pins */ }; =20 +static struct mpsse_quirk bryx_brik_quirk =3D { + .names =3D { + [3] =3D "Push to Talk", + [5] =3D "Channel Activity", + }, + .dir_out =3D ~BIT(3), /* Push to Talk */ + .dir_in =3D ~BIT(5), /* Channel Activity */ +}; + static const struct usb_device_id gpio_mpsse_table[] =3D { { USB_DEVICE(0x0c52, 0xa064) }, /* SeaLevel Systems, Inc. */ + { USB_DEVICE(0x0403, 0x6988), /* FTDI, assigned to Bryx */ + .driver_info =3D (kernel_ulong_t)&bryx_brik_quirk}, { } /* Terminating entry */ }; =20 --=20 2.47.0