From nobody Wed Dec 17 17:28:51 2025 Received: from smtp-relay-internal-0.canonical.com (smtp-relay-internal-0.canonical.com [185.125.188.122]) (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 E16DB1922F5 for ; Mon, 7 Apr 2025 04:30:51 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=185.125.188.122 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1744000254; cv=none; b=keVUDsbTh997Uimaujid+15PQhLdUNeiXb2eCBQ8WtNJccoTyWiPPuoiVxwQulpIMoujVVWEVc0G7IcvUX+vKQWyszrLYxHSLqWfXB8sBx+LNvnKvmhQACmXj3LB5BykPanraCVGh1afCTgPw8+VH5LmVANRypRCTqW88oj2bsA= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1744000254; c=relaxed/simple; bh=QKzGBY006K35i0VUre3m/YXkXfWXlIbWjW5345Q28Sw=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=GplSkOyvSOnuLOZuaEwt1jt2TanMWvyDrdRKnKhsV8WoyXiTFnjGZ1luv8DcODOIUC/Onrm5zLzJMif9VJ19F898l2+6smMhn9SY5nZFcr/qABgX7TdluQRmeaDXBQXL8JIsVShs6W6TNI2X+1YUxy+sw55wYVTH3VMJHh0pVME= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=canonical.com; spf=pass smtp.mailfrom=canonical.com; dkim=pass (2048-bit key) header.d=canonical.com header.i=@canonical.com header.b=fMzQk41s; arc=none smtp.client-ip=185.125.188.122 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=canonical.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=canonical.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=canonical.com header.i=@canonical.com header.b="fMzQk41s" Received: from mail-pg1-f199.google.com (mail-pg1-f199.google.com [209.85.215.199]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by smtp-relay-internal-0.canonical.com (Postfix) with ESMTPS id 2AC4E3F2C2 for ; Mon, 7 Apr 2025 04:30:50 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=canonical.com; s=20210705; t=1744000250; bh=gYD77nW/waFXgRcL7RZiNMF3p8f4KzCk7t7Ss3jccwc=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=fMzQk41swGRDZD0MT87hwrqv1i54OXC/JJllHQjlkze4HE84L8ygzSgyQrI8S7eAE w8os78ofofQfec13yMiCqlLI239LFTb9OuJinjD+2ct7PkadCoUIl99nQy9wA81Zy5 pO7u6NJ+WamF+BNgyDmK2GWQC3S1x/D/hIaqw4hFTzgmkeGrDiZEvYl027fWOE7sca VIbEccG9Kc1nuPP1l7lyFpGR3F0PiwpD2gPXGnz4wDLems4W79DpDl9ZoLgmMB+XAs 3noBlKl2TCxLMfmGpiZKx8UINdAqbvFy0TNh/dXuCxfgAJ37TSA+bnUYyOcn+sh49B PFtlWKoNYt+wQ== Received: by mail-pg1-f199.google.com with SMTP id 41be03b00d2f7-af8a4410816so3164740a12.1 for ; Sun, 06 Apr 2025 21:30:50 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1744000249; x=1744605049; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=gYD77nW/waFXgRcL7RZiNMF3p8f4KzCk7t7Ss3jccwc=; b=DUumhijiFiPTveUubpBbVlrIcjXmSTQsCQwh7PU6NAxmBVJ3rEeVM8ghz1e/aZdtZ8 YL+17d6X0wCGdworkUtgsStI25Rktw7wGzz8ipz9qxht37HwtZd2c0ShsBMT3VMOHKQG FaZIIobnIEgGhkn2h++iebYzEzerv4Fyce3gA3askXlghmQyNH6CYPDNASImKuIN2vWk 4/aMyybTkTI+BW73DUMB30xLQhG2fnpf5TPg0xqeggdWq8RnkXnmaP9vtcnHr5CPSCAI xF26D4WkyEmIihJgFcIsPpTZ7vYhKIZ1efQs/Crh5IwqYu5F9D6a+iJVMlh1gWdkMtyt t12g== X-Forwarded-Encrypted: i=1; AJvYcCUPpiTYEUykoNPHfl8L63Q0FG6ntoIvW3lhxcA1IwQov7bs6FCSCt5COKXTUuPApkcaxhbVxrEpUISz9BU=@vger.kernel.org X-Gm-Message-State: AOJu0YwwWSsaPVe5oTpfZYbnJV2n1vXRSR7ozQ64WAbmfZ3mvjRmFmcq 5i6m1qKpUVK1pgkITZTHUeE11vqiLN2XQ1LnAYFlXpCmkDddvBS6hxXzImasfFHzaTgq3mLxott R3CzFBUEg9GgxE8PzN5VL+I23R1UrpR6GBzan3qPSXzh/H81Wl4IQE3Saw+PVh+8z1OrtbWuZf+ btZA== X-Gm-Gg: ASbGncuLsLA+Xz7YHGQdXYXvC701MKMONQ2+nT4FetOXv3LsVVaR/VnbjjB4TbKG27K BtaHjwixlPyzk/OEnudZ2sFVDYI4mSH1iDCOGkyfM/2ogq+3lzXBqxGWDDLeD+ko8oXcLPRhrj+ HWGrX8Y78fjuD7C6aBKdWxFgj7ii4mvvlIwFKtEEVMOZ5/VOroYIERWNhl4f7WH3nA1JU5ARj1/ 6i1i0N0dByyO9J60rQs1aHGB7vF3FJ66DHpFiXfoTxkfE1WitnFsGspLLW99pqxbqUUpV97GT8u Ak5Z0MLbV3bjA4DNHQq0BIc55XrCbC5LPA== X-Received: by 2002:a17:902:d491:b0:220:ff3f:6cba with SMTP id d9443c01a7336-22a95573902mr122824825ad.38.1744000248624; Sun, 06 Apr 2025 21:30:48 -0700 (PDT) X-Google-Smtp-Source: AGHT+IFRt1TZZiQKo9HcnEyiDo2yBUyjoUApiYat4HpGVsubxyL9kQHP3eAQ5W3KlqnK1ubECzk5qg== X-Received: by 2002:a17:902:d491:b0:220:ff3f:6cba with SMTP id d9443c01a7336-22a95573902mr122824555ad.38.1744000248309; Sun, 06 Apr 2025 21:30:48 -0700 (PDT) Received: from localhost.localdomain ([240f:74:7be:1:5985:1f8b:863f:3722]) by smtp.gmail.com with ESMTPSA id d9443c01a7336-22978670dbbsm70839525ad.209.2025.04.06.21.30.46 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sun, 06 Apr 2025 21:30:48 -0700 (PDT) From: Koichiro Den To: linux-gpio@vger.kernel.org Cc: brgl@bgdev.pl, geert+renesas@glider.be, linus.walleij@linaro.org, maciej.borzecki@canonical.com, linux-kernel@vger.kernel.org Subject: [PATCH v7 1/9] gpio: aggregator: reorder functions to prepare for configfs introduction Date: Mon, 7 Apr 2025 13:30:11 +0900 Message-ID: <20250407043019.4105613-2-koichiro.den@canonical.com> X-Mailer: git-send-email 2.45.2 In-Reply-To: <20250407043019.4105613-1-koichiro.den@canonical.com> References: <20250407043019.4105613-1-koichiro.den@canonical.com> 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" Reorder functions in drivers/gpio/gpio-aggregator.c to prepare for the configfs-based interface additions in subsequent commits. Arrange the code so that the configfs implementations will appear above the existing sysfs-specific code, since the latter will partly depend on the configfs interface implementations when it starts to expose the settings to configfs. The order in drivers/gpio/gpio-aggregator.c will be as follows: * Basic gpio_aggregator/gpio_aggregator_line representations * Common utility functions * GPIO Forwarder implementations * Configfs interface implementations * Sysfs interface implementations * Platform device implementations * Module init/exit implementations This separate commit ensures a clean diff for the subsequent commits. No functional change. Reviewed-by: Geert Uytterhoeven Signed-off-by: Koichiro Den --- drivers/gpio/gpio-aggregator.c | 380 +++++++++++++++++---------------- 1 file changed, 192 insertions(+), 188 deletions(-) diff --git a/drivers/gpio/gpio-aggregator.c b/drivers/gpio/gpio-aggregator.c index d232ea865356..e026deb4ac64 100644 --- a/drivers/gpio/gpio-aggregator.c +++ b/drivers/gpio/gpio-aggregator.c @@ -61,194 +61,6 @@ static int aggr_add_gpio(struct gpio_aggregator *aggr, = const char *key, return 0; } =20 -static int aggr_parse(struct gpio_aggregator *aggr) -{ - char *args =3D skip_spaces(aggr->args); - char *name, *offsets, *p; - unsigned int i, n =3D 0; - int error =3D 0; - - unsigned long *bitmap __free(bitmap) =3D - bitmap_alloc(AGGREGATOR_MAX_GPIOS, GFP_KERNEL); - if (!bitmap) - return -ENOMEM; - - args =3D next_arg(args, &name, &p); - while (*args) { - args =3D next_arg(args, &offsets, &p); - - p =3D get_options(offsets, 0, &error); - if (error =3D=3D 0 || *p) { - /* Named GPIO line */ - error =3D aggr_add_gpio(aggr, name, U16_MAX, &n); - if (error) - return error; - - name =3D offsets; - continue; - } - - /* GPIO chip + offset(s) */ - error =3D bitmap_parselist(offsets, bitmap, AGGREGATOR_MAX_GPIOS); - if (error) { - pr_err("Cannot parse %s: %d\n", offsets, error); - return error; - } - - for_each_set_bit(i, bitmap, AGGREGATOR_MAX_GPIOS) { - error =3D aggr_add_gpio(aggr, name, i, &n); - if (error) - return error; - } - - args =3D next_arg(args, &name, &p); - } - - if (!n) { - pr_err("No GPIOs specified\n"); - return -EINVAL; - } - - return 0; -} - -static ssize_t new_device_store(struct device_driver *driver, const char *= buf, - size_t count) -{ - struct gpio_aggregator *aggr; - struct platform_device *pdev; - int res, id; - - if (!try_module_get(THIS_MODULE)) - return -ENOENT; - - /* kernfs guarantees string termination, so count + 1 is safe */ - aggr =3D kzalloc(sizeof(*aggr) + count + 1, GFP_KERNEL); - if (!aggr) { - res =3D -ENOMEM; - goto put_module; - } - - memcpy(aggr->args, buf, count + 1); - - aggr->lookups =3D kzalloc(struct_size(aggr->lookups, table, 1), - GFP_KERNEL); - if (!aggr->lookups) { - res =3D -ENOMEM; - goto free_ga; - } - - mutex_lock(&gpio_aggregator_lock); - id =3D idr_alloc(&gpio_aggregator_idr, aggr, 0, 0, GFP_KERNEL); - mutex_unlock(&gpio_aggregator_lock); - - if (id < 0) { - res =3D id; - goto free_table; - } - - aggr->lookups->dev_id =3D kasprintf(GFP_KERNEL, "%s.%d", DRV_NAME, id); - if (!aggr->lookups->dev_id) { - res =3D -ENOMEM; - goto remove_idr; - } - - res =3D aggr_parse(aggr); - if (res) - goto free_dev_id; - - gpiod_add_lookup_table(aggr->lookups); - - pdev =3D platform_device_register_simple(DRV_NAME, id, NULL, 0); - if (IS_ERR(pdev)) { - res =3D PTR_ERR(pdev); - goto remove_table; - } - - aggr->pdev =3D pdev; - module_put(THIS_MODULE); - return count; - -remove_table: - gpiod_remove_lookup_table(aggr->lookups); -free_dev_id: - kfree(aggr->lookups->dev_id); -remove_idr: - mutex_lock(&gpio_aggregator_lock); - idr_remove(&gpio_aggregator_idr, id); - mutex_unlock(&gpio_aggregator_lock); -free_table: - kfree(aggr->lookups); -free_ga: - kfree(aggr); -put_module: - module_put(THIS_MODULE); - return res; -} - -static DRIVER_ATTR_WO(new_device); - -static void gpio_aggregator_free(struct gpio_aggregator *aggr) -{ - platform_device_unregister(aggr->pdev); - gpiod_remove_lookup_table(aggr->lookups); - kfree(aggr->lookups->dev_id); - kfree(aggr->lookups); - kfree(aggr); -} - -static ssize_t delete_device_store(struct device_driver *driver, - const char *buf, size_t count) -{ - struct gpio_aggregator *aggr; - unsigned int id; - int error; - - if (!str_has_prefix(buf, DRV_NAME ".")) - return -EINVAL; - - error =3D kstrtouint(buf + strlen(DRV_NAME "."), 10, &id); - if (error) - return error; - - if (!try_module_get(THIS_MODULE)) - return -ENOENT; - - mutex_lock(&gpio_aggregator_lock); - aggr =3D idr_remove(&gpio_aggregator_idr, id); - mutex_unlock(&gpio_aggregator_lock); - if (!aggr) { - module_put(THIS_MODULE); - return -ENOENT; - } - - gpio_aggregator_free(aggr); - module_put(THIS_MODULE); - return count; -} -static DRIVER_ATTR_WO(delete_device); - -static struct attribute *gpio_aggregator_attrs[] =3D { - &driver_attr_new_device.attr, - &driver_attr_delete_device.attr, - NULL -}; -ATTRIBUTE_GROUPS(gpio_aggregator); - -static int __exit gpio_aggregator_idr_remove(int id, void *p, void *data) -{ - gpio_aggregator_free(p); - return 0; -} - -static void __exit gpio_aggregator_remove_all(void) -{ - mutex_lock(&gpio_aggregator_lock); - idr_for_each(&gpio_aggregator_idr, gpio_aggregator_idr_remove, NULL); - idr_destroy(&gpio_aggregator_idr); - mutex_unlock(&gpio_aggregator_lock); -} - =20 /* * GPIO Forwarder @@ -583,6 +395,184 @@ static struct gpiochip_fwd *gpiochip_fwd_create(struc= t device *dev, } =20 =20 +/* + * Sysfs interface + */ +static int aggr_parse(struct gpio_aggregator *aggr) +{ + char *args =3D skip_spaces(aggr->args); + char *name, *offsets, *p; + unsigned int i, n =3D 0; + int error =3D 0; + + unsigned long *bitmap __free(bitmap) =3D + bitmap_alloc(AGGREGATOR_MAX_GPIOS, GFP_KERNEL); + if (!bitmap) + return -ENOMEM; + + args =3D next_arg(args, &name, &p); + while (*args) { + args =3D next_arg(args, &offsets, &p); + + p =3D get_options(offsets, 0, &error); + if (error =3D=3D 0 || *p) { + /* Named GPIO line */ + error =3D aggr_add_gpio(aggr, name, U16_MAX, &n); + if (error) + return error; + + name =3D offsets; + continue; + } + + /* GPIO chip + offset(s) */ + error =3D bitmap_parselist(offsets, bitmap, AGGREGATOR_MAX_GPIOS); + if (error) { + pr_err("Cannot parse %s: %d\n", offsets, error); + return error; + } + + for_each_set_bit(i, bitmap, AGGREGATOR_MAX_GPIOS) { + error =3D aggr_add_gpio(aggr, name, i, &n); + if (error) + return error; + } + + args =3D next_arg(args, &name, &p); + } + + if (!n) { + pr_err("No GPIOs specified\n"); + return -EINVAL; + } + + return 0; +} + +static ssize_t new_device_store(struct device_driver *driver, const char *= buf, + size_t count) +{ + struct gpio_aggregator *aggr; + struct platform_device *pdev; + int res, id; + + if (!try_module_get(THIS_MODULE)) + return -ENOENT; + + /* kernfs guarantees string termination, so count + 1 is safe */ + aggr =3D kzalloc(sizeof(*aggr) + count + 1, GFP_KERNEL); + if (!aggr) { + res =3D -ENOMEM; + goto put_module; + } + + memcpy(aggr->args, buf, count + 1); + + aggr->lookups =3D kzalloc(struct_size(aggr->lookups, table, 1), + GFP_KERNEL); + if (!aggr->lookups) { + res =3D -ENOMEM; + goto free_ga; + } + + mutex_lock(&gpio_aggregator_lock); + id =3D idr_alloc(&gpio_aggregator_idr, aggr, 0, 0, GFP_KERNEL); + mutex_unlock(&gpio_aggregator_lock); + + if (id < 0) { + res =3D id; + goto free_table; + } + + aggr->lookups->dev_id =3D kasprintf(GFP_KERNEL, "%s.%d", DRV_NAME, id); + if (!aggr->lookups->dev_id) { + res =3D -ENOMEM; + goto remove_idr; + } + + res =3D aggr_parse(aggr); + if (res) + goto free_dev_id; + + gpiod_add_lookup_table(aggr->lookups); + + pdev =3D platform_device_register_simple(DRV_NAME, id, NULL, 0); + if (IS_ERR(pdev)) { + res =3D PTR_ERR(pdev); + goto remove_table; + } + + aggr->pdev =3D pdev; + module_put(THIS_MODULE); + return count; + +remove_table: + gpiod_remove_lookup_table(aggr->lookups); +free_dev_id: + kfree(aggr->lookups->dev_id); +remove_idr: + mutex_lock(&gpio_aggregator_lock); + idr_remove(&gpio_aggregator_idr, id); + mutex_unlock(&gpio_aggregator_lock); +free_table: + kfree(aggr->lookups); +free_ga: + kfree(aggr); +put_module: + module_put(THIS_MODULE); + return res; +} + +static DRIVER_ATTR_WO(new_device); + +static void gpio_aggregator_free(struct gpio_aggregator *aggr) +{ + platform_device_unregister(aggr->pdev); + gpiod_remove_lookup_table(aggr->lookups); + kfree(aggr->lookups->dev_id); + kfree(aggr->lookups); + kfree(aggr); +} + +static ssize_t delete_device_store(struct device_driver *driver, + const char *buf, size_t count) +{ + struct gpio_aggregator *aggr; + unsigned int id; + int error; + + if (!str_has_prefix(buf, DRV_NAME ".")) + return -EINVAL; + + error =3D kstrtouint(buf + strlen(DRV_NAME "."), 10, &id); + if (error) + return error; + + if (!try_module_get(THIS_MODULE)) + return -ENOENT; + + mutex_lock(&gpio_aggregator_lock); + aggr =3D idr_remove(&gpio_aggregator_idr, id); + mutex_unlock(&gpio_aggregator_lock); + if (!aggr) { + module_put(THIS_MODULE); + return -ENOENT; + } + + gpio_aggregator_free(aggr); + module_put(THIS_MODULE); + return count; +} +static DRIVER_ATTR_WO(delete_device); + +static struct attribute *gpio_aggregator_attrs[] =3D { + &driver_attr_new_device.attr, + &driver_attr_delete_device.attr, + NULL +}; +ATTRIBUTE_GROUPS(gpio_aggregator); + + /* * GPIO Aggregator platform device */ @@ -640,6 +630,20 @@ static struct platform_driver gpio_aggregator_driver = =3D { }, }; =20 +static int __exit gpio_aggregator_idr_remove(int id, void *p, void *data) +{ + gpio_aggregator_free(p); + return 0; +} + +static void __exit gpio_aggregator_remove_all(void) +{ + mutex_lock(&gpio_aggregator_lock); + idr_for_each(&gpio_aggregator_idr, gpio_aggregator_idr_remove, NULL); + idr_destroy(&gpio_aggregator_idr); + mutex_unlock(&gpio_aggregator_lock); +} + static int __init gpio_aggregator_init(void) { return platform_driver_register(&gpio_aggregator_driver); --=20 2.45.2 From nobody Wed Dec 17 17:28:51 2025 Received: from smtp-relay-internal-0.canonical.com (smtp-relay-internal-0.canonical.com [185.125.188.122]) (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 C3121213252 for ; Mon, 7 Apr 2025 04:30:53 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=185.125.188.122 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1744000255; cv=none; b=IdhKTdht3Jj8S8MbaUfKOVZpDyoV3rn/o9lZMtDrC33SrCVNA6NjgjMAVxqP6vAEe5fIYw7H4AyWsROTYbrLztIZi5GAi/+msW1RiRVl7WdWC3/H5VB8tBci1/L8y9c5lLjTQ3aisFpSBm31U0JvpmpY+paDzdV9sDAuaCDMTk4= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1744000255; c=relaxed/simple; bh=PF2bzENyixS725vuRFQYOE0EHw2Blz1UgA3yYSWEqBc=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=mdTbbJU6De1ElX7sru+liPtIfWxNjl1KJ42iuis+7VRkM0xMoB+oaAIOusfI+30SQjLPffhmxbfkv6QwtT+rolEysBsNfgRFRMBRdj5fIHVguIQf1Jwlq8Dxkr0gSOETH+9CvASROeNid5yCkvq/nGeSfFf7sdNOGKIOnXD8fr4= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=canonical.com; spf=pass smtp.mailfrom=canonical.com; dkim=pass (2048-bit key) header.d=canonical.com header.i=@canonical.com header.b=k3y/qy4p; arc=none smtp.client-ip=185.125.188.122 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=canonical.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=canonical.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=canonical.com header.i=@canonical.com header.b="k3y/qy4p" Received: from mail-pl1-f199.google.com (mail-pl1-f199.google.com [209.85.214.199]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by smtp-relay-internal-0.canonical.com (Postfix) with ESMTPS id 4DCB43F342 for ; Mon, 7 Apr 2025 04:30:52 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=canonical.com; s=20210705; t=1744000252; bh=qMmf8wsuaV2g3eTzKbfqc83zqOBY3dNWVYTFEe1i+UA=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=k3y/qy4pg9PYawuAqdOjsS8SAPqy7VZMLM62doww/RyNLngIFMAA7Uxvlb8Sk7Eus /fNEC/pT5nuYJ25EGthFw4f+B+oMS1281p2DvNXMgr7ME2tHBY5uSukLfoHr2qhjfm 7aOKxOXXPTBYar8SwRSQHRnKy/dfTQuqlwPSirrffzAVdJSf+TEi54TXxf7buyILbv s+WhhouNHGb7Z5YTGs98tQYtOWSLbJDyOf98LSDbeJ0Yaqb11kWyT5r1gMBzDojLgt AC9vXeF1uVid0+rs8ohFx3KkzlaxC+JS9gZqI9qc5c9BqFVx9mvC3WKlivPLTTD1oi fF2OykIYkc5uQ== Received: by mail-pl1-f199.google.com with SMTP id d9443c01a7336-22647ff3cf5so33698675ad.0 for ; Sun, 06 Apr 2025 21:30:52 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1744000251; x=1744605051; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=qMmf8wsuaV2g3eTzKbfqc83zqOBY3dNWVYTFEe1i+UA=; b=rMYUbdCq0c6gLjJK41xzNyX7wqrnOY3t4BIV+chtdyhBbXwUw4I2G8KDK9w7V9JqSw f3ViNEgp/p02FTFSvL14bpNs+jd4/WBDelZPWv0ZRWYmJjen+u1MSDcmZmw5KLiaKy97 1xNxHZz6rgcLSC5mf2aVn4ew0r7Ja6gfQLgAEph95RVsmhqb9gqzmbadpMJ9EX+CpFV0 07bUtBWuG/F3bW61Kc/pOJqvTSZx9rKd8R/rm0BXFiy4tYjXTS3Zj47dfJ/Ua8nZayy4 wPwMafPuKo4soYoDXFkezQBwwg2LyX/5tTeh+e/fFwLPqi+Jjg6urAuYxbETGyxurxW2 xWjg== X-Forwarded-Encrypted: i=1; AJvYcCU1Pvt4/d7QtPhviDrAVGLIMFsTF+DVU2n3K0qmK0fiW8ISz29tuEZ4Dp4BfWoa2LAPnv8gy2Zf5GVbeJw=@vger.kernel.org X-Gm-Message-State: AOJu0Yyq/q8Ypf23tlrTJS1N4bVhYfY3aVuTliAMmXYvrAbVOpSAvCUE 1IwNfV9jxPmbIdkBWsFxgEI06gJpm9Ydd925giXTDFiCYA4xN86b7LxTRnTuBdkD1ahfW+W11ph uEZEMT2JsLgbGcS/FnHgpSbmNcKgEIJw3NBUFuT2z+bgtkGJRVJct6YvSJWZgNI7iqQzGMd8j98 jrZw== X-Gm-Gg: ASbGncu06vpUNzjo4kT7wduwpDQo0BAfHKk+WNrWCCemrF0xaCCk1aP70xjQL4AsUW9 CcKGC+oQX4I6UqZrosStNtfHB/8tvZYjYww5Y++3KjgvfWrcWSUb6+VS5IOBEKkOqZ6RUagSRr2 EaOeYzKoo1lwwSNhhJq/jA+5y6QcanT0nEIxj7rW+j4jYAk0KVx5BiOV822pklqafXwzzY5PZr8 k19KXHafOc/2TdisRhvA+J1BtR1dZ5O9mK0L2cpkrjXaMKFnfi+KgR9/XF1lDh2FdhXD6LdZ6uc 0ZsHnF8eawyzq4R4WqGlHcS9ldFB87h+SA== X-Received: by 2002:a17:902:e842:b0:224:c46:d167 with SMTP id d9443c01a7336-22a9552acdemr101661825ad.16.1744000250898; Sun, 06 Apr 2025 21:30:50 -0700 (PDT) X-Google-Smtp-Source: AGHT+IEiLDy5RBiDIAPrY/hDRGYzyILeWDPk5r2DHuhwaOj5ikPWcDHSqAyhxm5S1PqpppxhOfeQgQ== X-Received: by 2002:a17:902:e842:b0:224:c46:d167 with SMTP id d9443c01a7336-22a9552acdemr101661535ad.16.1744000250532; Sun, 06 Apr 2025 21:30:50 -0700 (PDT) Received: from localhost.localdomain ([240f:74:7be:1:5985:1f8b:863f:3722]) by smtp.gmail.com with ESMTPSA id d9443c01a7336-22978670dbbsm70839525ad.209.2025.04.06.21.30.48 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sun, 06 Apr 2025 21:30:50 -0700 (PDT) From: Koichiro Den To: linux-gpio@vger.kernel.org Cc: brgl@bgdev.pl, geert+renesas@glider.be, linus.walleij@linaro.org, maciej.borzecki@canonical.com, linux-kernel@vger.kernel.org Subject: [PATCH v7 2/9] gpio: aggregator: unify function naming Date: Mon, 7 Apr 2025 13:30:12 +0900 Message-ID: <20250407043019.4105613-3-koichiro.den@canonical.com> X-Mailer: git-send-email 2.45.2 In-Reply-To: <20250407043019.4105613-1-koichiro.den@canonical.com> References: <20250407043019.4105613-1-koichiro.den@canonical.com> 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" Unify function names to use gpio_aggregator_ prefix (except GPIO forwarder implementations, which remain unchanged in subsequent commits). While at it, rename the pre-existing gpio_aggregator_free() to gpio_aggregator_destory(), since that name will be used by new alloc/free functions introduced in the next commit, for which the name is more appropriate. No functional change. Signed-off-by: Koichiro Den --- drivers/gpio/gpio-aggregator.c | 37 ++++++++++++++++++---------------- 1 file changed, 20 insertions(+), 17 deletions(-) diff --git a/drivers/gpio/gpio-aggregator.c b/drivers/gpio/gpio-aggregator.c index e026deb4ac64..ff5cd5eaa550 100644 --- a/drivers/gpio/gpio-aggregator.c +++ b/drivers/gpio/gpio-aggregator.c @@ -42,8 +42,8 @@ struct gpio_aggregator { static DEFINE_MUTEX(gpio_aggregator_lock); /* protects idr */ static DEFINE_IDR(gpio_aggregator_idr); =20 -static int aggr_add_gpio(struct gpio_aggregator *aggr, const char *key, - int hwnum, unsigned int *n) +static int gpio_aggregator_add_gpio(struct gpio_aggregator *aggr, + const char *key, int hwnum, unsigned int *n) { struct gpiod_lookup_table *lookups; =20 @@ -398,7 +398,7 @@ static struct gpiochip_fwd *gpiochip_fwd_create(struct = device *dev, /* * Sysfs interface */ -static int aggr_parse(struct gpio_aggregator *aggr) +static int gpio_aggregator_parse(struct gpio_aggregator *aggr) { char *args =3D skip_spaces(aggr->args); char *name, *offsets, *p; @@ -417,7 +417,7 @@ static int aggr_parse(struct gpio_aggregator *aggr) p =3D get_options(offsets, 0, &error); if (error =3D=3D 0 || *p) { /* Named GPIO line */ - error =3D aggr_add_gpio(aggr, name, U16_MAX, &n); + error =3D gpio_aggregator_add_gpio(aggr, name, U16_MAX, &n); if (error) return error; =20 @@ -433,7 +433,7 @@ static int aggr_parse(struct gpio_aggregator *aggr) } =20 for_each_set_bit(i, bitmap, AGGREGATOR_MAX_GPIOS) { - error =3D aggr_add_gpio(aggr, name, i, &n); + error =3D gpio_aggregator_add_gpio(aggr, name, i, &n); if (error) return error; } @@ -449,8 +449,8 @@ static int aggr_parse(struct gpio_aggregator *aggr) return 0; } =20 -static ssize_t new_device_store(struct device_driver *driver, const char *= buf, - size_t count) +static ssize_t gpio_aggregator_new_device_store(struct device_driver *driv= er, + const char *buf, size_t count) { struct gpio_aggregator *aggr; struct platform_device *pdev; @@ -490,7 +490,7 @@ static ssize_t new_device_store(struct device_driver *d= river, const char *buf, goto remove_idr; } =20 - res =3D aggr_parse(aggr); + res =3D gpio_aggregator_parse(aggr); if (res) goto free_dev_id; =20 @@ -523,9 +523,10 @@ static ssize_t new_device_store(struct device_driver *= driver, const char *buf, return res; } =20 -static DRIVER_ATTR_WO(new_device); +static struct driver_attribute driver_attr_gpio_aggregator_new_device =3D + __ATTR(new_device, 0200, NULL, gpio_aggregator_new_device_store); =20 -static void gpio_aggregator_free(struct gpio_aggregator *aggr) +static void gpio_aggregator_destroy(struct gpio_aggregator *aggr) { platform_device_unregister(aggr->pdev); gpiod_remove_lookup_table(aggr->lookups); @@ -534,8 +535,8 @@ static void gpio_aggregator_free(struct gpio_aggregator= *aggr) kfree(aggr); } =20 -static ssize_t delete_device_store(struct device_driver *driver, - const char *buf, size_t count) +static ssize_t gpio_aggregator_delete_device_store(struct device_driver *d= river, + const char *buf, size_t count) { struct gpio_aggregator *aggr; unsigned int id; @@ -559,15 +560,17 @@ static ssize_t delete_device_store(struct device_driv= er *driver, return -ENOENT; } =20 - gpio_aggregator_free(aggr); + gpio_aggregator_destroy(aggr); module_put(THIS_MODULE); return count; } -static DRIVER_ATTR_WO(delete_device); + +static struct driver_attribute driver_attr_gpio_aggregator_delete_device = =3D + __ATTR(delete_device, 0200, NULL, gpio_aggregator_delete_device_store); =20 static struct attribute *gpio_aggregator_attrs[] =3D { - &driver_attr_new_device.attr, - &driver_attr_delete_device.attr, + &driver_attr_gpio_aggregator_new_device.attr, + &driver_attr_gpio_aggregator_delete_device.attr, NULL }; ATTRIBUTE_GROUPS(gpio_aggregator); @@ -632,7 +635,7 @@ static struct platform_driver gpio_aggregator_driver = =3D { =20 static int __exit gpio_aggregator_idr_remove(int id, void *p, void *data) { - gpio_aggregator_free(p); + gpio_aggregator_destroy(p); return 0; } =20 --=20 2.45.2 From nobody Wed Dec 17 17:28:51 2025 Received: from smtp-relay-internal-1.canonical.com (smtp-relay-internal-1.canonical.com [185.125.188.123]) (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 EB7182222C3 for ; Mon, 7 Apr 2025 04:30:56 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=185.125.188.123 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1744000258; cv=none; b=M0aya/2cB1PjgzghV+3+vtyWgfGscVu3TFb9ImiXBLGMj6nb/xMQyAH+E295xs2Sm/VS3DH0akh7j8sTjLhCFclLTOk95Wtnpd9QRpXhcO7p8Knag/x4xxaoRjnYQSbqUM8hqVkk7NWBbtiNz4KH6qa2O1pzj3NLRBzuagEEXNM= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1744000258; c=relaxed/simple; bh=TbAKJXmX6g7xhJYJocuFyn/R1DEc55FoQ9fLzC8oylM=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=shVFdYAgZlNt0xOvreeAATRC7MeaNjtWnjbcB4QIslo2QZJrCO8/67RkS/flqvYn1oxqgfoZmkB15kz0+46R3ZhmDcAebfvrt8yZ65dRbZFjNpCVLvMie4nrYphBpKDxgOcUd6i5yKMKE2jA5jtN4cTKhqREFrxRQ4RMpZxmM7U= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=canonical.com; spf=pass smtp.mailfrom=canonical.com; dkim=pass (2048-bit key) header.d=canonical.com header.i=@canonical.com header.b=PKBQcJdw; arc=none smtp.client-ip=185.125.188.123 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=canonical.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=canonical.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=canonical.com header.i=@canonical.com header.b="PKBQcJdw" Received: from mail-pl1-f199.google.com (mail-pl1-f199.google.com [209.85.214.199]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by smtp-relay-internal-1.canonical.com (Postfix) with ESMTPS id AED9A3F181 for ; Mon, 7 Apr 2025 04:30:54 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=canonical.com; s=20210705; t=1744000254; bh=olpAErLRuaUJTw8reFG3lTyrFxmkOqkGE5UK7pclqas=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=PKBQcJdw0tkRa5gzKWAzX19sW7ScmuyC21FnmjfBE47Bv1WGZTw0Z0Q5XUR1ePGP6 7EUL0gYNwINUo95RqKjdpumrczRLl8AEj1/DIVTRwZql3UBW2dUe22KxBopn56MqyH BsmcaJKwp24kPB2Ig48h63X40Hgh2TlDrcTdswScJcPdNa+1sx/f18FfFDiQ8AOueA 4H2mCfuvhXqsOItP8T7XzrncKoPxR+CJG0dnAtVdT2til4wSzCC4cgfI/6uB0VRb66 16DeWTmQnD2cNTqktFMi2W1HAASV4i+34ZyIg68f4mfqYwa9jt+5FRVO4T70BT7EaM OzEswivOCf42w== Received: by mail-pl1-f199.google.com with SMTP id d9443c01a7336-22651aca434so37647665ad.1 for ; Sun, 06 Apr 2025 21:30:54 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1744000253; x=1744605053; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=olpAErLRuaUJTw8reFG3lTyrFxmkOqkGE5UK7pclqas=; b=ArQFeTeQMngOdciUb+b5+J07b9V91o0IJMX/p5PxAlss0zxrLIqukVPbwy8Okgil4E gNz7Tn15mi7xMRKxg8C6WkOuLlEPbMcAP5xcAhtpLeH4l7D5MKG9gGzMMS5OfHjOV5+Z ixWqGlKgi6bIOeVJcelI42JxhbRCqbtIV9eQP7TgchKF6gDe20nL/7WoBx8PXdK/8sce SpPbuScLFVocjl/cEIt62kAHt+LXLRpWXOPk9aJE+aIx7z0M+Ht+1ZLaiqZOxy/ONZ2o rSwCQAHGN+MCpJ80Y3H8IdmxK4GvaXkWzUUKWRmuJ5aBbIjGOYMfjSgycCytlvlseG2y Bq9g== X-Forwarded-Encrypted: i=1; AJvYcCUGJIYXSvDbmdD/b/fskebIOLT9b3u3mN8KFKnyZrNNdU84DvC5/H7ZWr9ru6Z0dLi+EzMSQ1aw0Y5ECEg=@vger.kernel.org X-Gm-Message-State: AOJu0Yyv6wOBFSoggbQgTrl8l4OQ1O6VgaaqVqd6QY+oVroYr3kOAB18 3xzhzXTAFb031/Lw2A7y7qhmQxypLpqcS6gByXV01pBTrxzF+HJspN+KeVXbb6fhADL8guCxkZ+ yWS8Pd+mdT+IqZfppoxdYySM5VEPaQ3h8NbIIRlWNkucQh9WWnfFjjMxoRS5dIFgdQnIU+MNBKB xONw== X-Gm-Gg: ASbGncsVoSeACRKMszbVDWJYS2jVxsX9X4qiWbjmT95qo+1i4Anu0Z0BDaFvvrEQFpQ llRusONTnRKVhs2Eqn4cA9xcq1sdXpd7ZFOlGrCI78Ko/vCHMm7Y2sbaL2k73waGvrtQjoTIZJx U+InDCKa1QkMIJRek9ALDlRYdR61CD4bePcb4T55YbpIEAQfRnZck2Vz5j60f4rVMirWbqqtk1U 3zykXdLvx5TbgtVTaRVDg4JtKF34oBAf615CP7bvyFTPgrazecfF9Vub3wKZpuqUDvuu8b4Y3nc NpO2DlkIYQPAJCJMwr68lrjr/cmheZjoHQ== X-Received: by 2002:a17:903:1b2d:b0:216:7926:8d69 with SMTP id d9443c01a7336-22a8a8dd69emr130201335ad.47.1744000253112; Sun, 06 Apr 2025 21:30:53 -0700 (PDT) X-Google-Smtp-Source: AGHT+IEeDelhsi3pljdEQEJi4TRSqlIHVRt8P+++1nYrhtJHgIUj2n3n/8C4lyr1PQisWmgPh4rxog== X-Received: by 2002:a17:903:1b2d:b0:216:7926:8d69 with SMTP id d9443c01a7336-22a8a8dd69emr130201035ad.47.1744000252685; Sun, 06 Apr 2025 21:30:52 -0700 (PDT) Received: from localhost.localdomain ([240f:74:7be:1:5985:1f8b:863f:3722]) by smtp.gmail.com with ESMTPSA id d9443c01a7336-22978670dbbsm70839525ad.209.2025.04.06.21.30.50 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sun, 06 Apr 2025 21:30:52 -0700 (PDT) From: Koichiro Den To: linux-gpio@vger.kernel.org Cc: brgl@bgdev.pl, geert+renesas@glider.be, linus.walleij@linaro.org, maciej.borzecki@canonical.com, linux-kernel@vger.kernel.org Subject: [PATCH v7 3/9] gpio: aggregator: add gpio_aggregator_{alloc,free}() Date: Mon, 7 Apr 2025 13:30:13 +0900 Message-ID: <20250407043019.4105613-4-koichiro.den@canonical.com> X-Mailer: git-send-email 2.45.2 In-Reply-To: <20250407043019.4105613-1-koichiro.den@canonical.com> References: <20250407043019.4105613-1-koichiro.den@canonical.com> 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" Prepare for the upcoming configfs interface. These functions will be used by both the existing sysfs interface and the new configfs interface, reducing code duplication. No functional change. Signed-off-by: Koichiro Den --- drivers/gpio/gpio-aggregator.c | 58 +++++++++++++++++++++------------- 1 file changed, 36 insertions(+), 22 deletions(-) diff --git a/drivers/gpio/gpio-aggregator.c b/drivers/gpio/gpio-aggregator.c index ff5cd5eaa550..6f933a76b2b9 100644 --- a/drivers/gpio/gpio-aggregator.c +++ b/drivers/gpio/gpio-aggregator.c @@ -36,12 +36,41 @@ struct gpio_aggregator { struct gpiod_lookup_table *lookups; struct platform_device *pdev; + int id; char args[]; }; =20 static DEFINE_MUTEX(gpio_aggregator_lock); /* protects idr */ static DEFINE_IDR(gpio_aggregator_idr); =20 +static int gpio_aggregator_alloc(struct gpio_aggregator **aggr, size_t arg= _size) +{ + int ret; + + struct gpio_aggregator *new __free(kfree) =3D kzalloc( + sizeof(*new) + arg_size, GFP_KERNEL); + if (!new) + return -ENOMEM; + + scoped_guard(mutex, &gpio_aggregator_lock) + ret =3D idr_alloc(&gpio_aggregator_idr, new, 0, 0, GFP_KERNEL); + + if (ret < 0) + return ret; + + new->id =3D ret; + *aggr =3D no_free_ptr(new); + return 0; +} + +static void gpio_aggregator_free(struct gpio_aggregator *aggr) +{ + scoped_guard(mutex, &gpio_aggregator_lock) + idr_remove(&gpio_aggregator_idr, aggr->id); + + kfree(aggr); +} + static int gpio_aggregator_add_gpio(struct gpio_aggregator *aggr, const char *key, int hwnum, unsigned int *n) { @@ -454,17 +483,15 @@ static ssize_t gpio_aggregator_new_device_store(struc= t device_driver *driver, { struct gpio_aggregator *aggr; struct platform_device *pdev; - int res, id; + int res; =20 if (!try_module_get(THIS_MODULE)) return -ENOENT; =20 /* kernfs guarantees string termination, so count + 1 is safe */ - aggr =3D kzalloc(sizeof(*aggr) + count + 1, GFP_KERNEL); - if (!aggr) { - res =3D -ENOMEM; + res =3D gpio_aggregator_alloc(&aggr, count + 1); + if (res) goto put_module; - } =20 memcpy(aggr->args, buf, count + 1); =20 @@ -475,19 +502,10 @@ static ssize_t gpio_aggregator_new_device_store(struc= t device_driver *driver, goto free_ga; } =20 - mutex_lock(&gpio_aggregator_lock); - id =3D idr_alloc(&gpio_aggregator_idr, aggr, 0, 0, GFP_KERNEL); - mutex_unlock(&gpio_aggregator_lock); - - if (id < 0) { - res =3D id; - goto free_table; - } - - aggr->lookups->dev_id =3D kasprintf(GFP_KERNEL, "%s.%d", DRV_NAME, id); + aggr->lookups->dev_id =3D kasprintf(GFP_KERNEL, "%s.%d", DRV_NAME, aggr->= id); if (!aggr->lookups->dev_id) { res =3D -ENOMEM; - goto remove_idr; + goto free_table; } =20 res =3D gpio_aggregator_parse(aggr); @@ -496,7 +514,7 @@ static ssize_t gpio_aggregator_new_device_store(struct = device_driver *driver, =20 gpiod_add_lookup_table(aggr->lookups); =20 - pdev =3D platform_device_register_simple(DRV_NAME, id, NULL, 0); + pdev =3D platform_device_register_simple(DRV_NAME, aggr->id, NULL, 0); if (IS_ERR(pdev)) { res =3D PTR_ERR(pdev); goto remove_table; @@ -510,14 +528,10 @@ static ssize_t gpio_aggregator_new_device_store(struc= t device_driver *driver, gpiod_remove_lookup_table(aggr->lookups); free_dev_id: kfree(aggr->lookups->dev_id); -remove_idr: - mutex_lock(&gpio_aggregator_lock); - idr_remove(&gpio_aggregator_idr, id); - mutex_unlock(&gpio_aggregator_lock); free_table: kfree(aggr->lookups); free_ga: - kfree(aggr); + gpio_aggregator_free(aggr); put_module: module_put(THIS_MODULE); return res; --=20 2.45.2 From nobody Wed Dec 17 17:28:51 2025 Received: from smtp-relay-internal-0.canonical.com (smtp-relay-internal-0.canonical.com [185.125.188.122]) (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 0CC8722256F for ; Mon, 7 Apr 2025 04:30:59 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=185.125.188.122 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1744000261; cv=none; b=Jyw43BMU7DpPuA65iSaiVMtlNR81sYCrROwFwEkzxQx/JWkuXhiYdiDBh0h2iz4tlm4CSj1Ez30vcokJgsoXN5GqzouADOIgHsgyXCMpVqoSeeA5YuWdKlCULtHB437JBK37IlMIUC6T+fSr2Ud0bZF72aMWWxM9ydDLPRLCx/c= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1744000261; c=relaxed/simple; bh=lB984tfDAhyeELYOhlZPQDtI7dyD54w4UolKF76kaYU=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=dHHwzhMFeI3UBHt9YAwJwO81TBn1f8KCCyIjVEnwrPGc2J2r8NCdNQcAU7YDpq2IaiNP5Hyu9EDvWGsoMYDojcZVmvK6vhxGMjHUZTK4ccA4FOtLscUxnJwg/424eXmr70VGZvr2ymwdNqKUkh81K3i47sLQZmQ4ESdpzR3TnNA= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=canonical.com; spf=pass smtp.mailfrom=canonical.com; dkim=pass (2048-bit key) header.d=canonical.com header.i=@canonical.com header.b=H6zaf1jH; arc=none smtp.client-ip=185.125.188.122 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=canonical.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=canonical.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=canonical.com header.i=@canonical.com header.b="H6zaf1jH" Received: from mail-pl1-f198.google.com (mail-pl1-f198.google.com [209.85.214.198]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by smtp-relay-internal-0.canonical.com (Postfix) with ESMTPS id 5FC6B3F471 for ; Mon, 7 Apr 2025 04:30:57 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=canonical.com; s=20210705; t=1744000257; bh=nUAokXMdm3M+aYaYztUvZ9QujIN2UHQZPTxR18O4/do=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=H6zaf1jHyq9whs08wO0vS6ZyIIJlRUTk+CTkd703WRBxJ48xWpPDbo+1ob4VH1PRA 0pVKUUhjMvBOcuGJuRJVWKnGXeVF9scg+DNpJGIAghJJoXk2u7pzFDbhHZlpbn+da0 cRidHZ7YjmIVZayUDdz8ZsnTIM18W0WDsx/yAlUOlTwEewOcMmTIingvQJ3/yajWeh RED5i4eIm4oVJ7NKQ94KpZgfF/5Y55TCli+BO0r/w9neBIl1RrUloct/BOBGCU9NKJ FWaxdt3Mvr74LF7G0EH4JssSOKpgcfMfA4HrEzLZ0NfdmYrim3KJjpIVjYY4+W/AQW poHc0h/ibDHTA== Received: by mail-pl1-f198.google.com with SMTP id d9443c01a7336-223fd6e9408so37211395ad.1 for ; Sun, 06 Apr 2025 21:30:57 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1744000256; x=1744605056; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=nUAokXMdm3M+aYaYztUvZ9QujIN2UHQZPTxR18O4/do=; b=bhmeXBQEZEemQODMdTOCmKLTC6+MPDxHglUGxkFB/NHUZ35ah/RsFrGtmXN8O+42qv IvJUQ4IyBC9AkXjMh8Lp2So919wFvQx09eEwd1Kx8zG7otzS9zaWSVOQ1SFTeqguhMUV jFg9ODriWH2i6HObxkjGIT514qPDaBq6jssqRaHYkBYPJ2YDD6R9BaLkfkoENa3kaMT3 eXax02IXDgWATvfl9KgwS8aSyMaVh5GkjGzOtJbOVGCchDlRb+d+7Urwy7mTSegEFQq4 yVqxFMtwYDOCxJavSwAa0mv6GmEfQg1Q/r2VaDNy/2dub2LVOEp7MtPPHf5a1sy++oJZ 6zwA== X-Forwarded-Encrypted: i=1; AJvYcCVtIdZDgTFknLzL1lnFnoKjYzsggO3pFphUUaBBsb5EjPduBQhsshshWaQ85N9dVZMLFuclYwnYpcVV9Hw=@vger.kernel.org X-Gm-Message-State: AOJu0YzplSPXbXk6l8fn6bFRDSI3LJOYCpqHsTQzWlb6fj+Gb8njcrZX IOFAFiX9zeyJAofrCZNkPhxbYetPskWUAuS7xURym8FnutonG/ccXjkKeR87DxHRpgMtRqY/uD5 JblTPKcJmi55lqlAow1GPg1dBtxn/mMZfdTBUDgs1S1HTkaA+JVsndQVeIILSxUb3T40zRPpNl5 PUQw== X-Gm-Gg: ASbGnctedh29n42NNCHlna/GR2K08eiQ9Ul+iModtLIjT04bR/sbuXLU/Rlo5FsdBQe frTX4+DzhL0y7FY5ihZO8I5fIM+3LjjOPYu30rHcWXJSksYDlFKQyh0OecQvi+2s03l3P6xf2l5 N8HRQK489/ha1+dvQtoi9jaLPZFupXWeKVAWd6ZhtKJULc1kFF3UduCMwN1JwUKHCsIhjADqoHZ +mruiey1yWZuDEJWUrp6MN893UnUGNF5Yy77C20N9kQc+1amZYBfiOR6l5Q04gALyyM9oHHMoBe LAEa4UMkJKiQQKSgLnMgvGVbiMybTgzmxQ== X-Received: by 2002:a17:903:3ba8:b0:215:a56f:1e50 with SMTP id d9443c01a7336-22a89a3fa0dmr171385825ad.8.1744000255816; Sun, 06 Apr 2025 21:30:55 -0700 (PDT) X-Google-Smtp-Source: AGHT+IGSj9kmZOKANGNLr/EkSCa2AVcQH1/OMyViFWwuu0o2MUob2tqc4aQFfvW1NHpOX1PZBBouGA== X-Received: by 2002:a17:903:3ba8:b0:215:a56f:1e50 with SMTP id d9443c01a7336-22a89a3fa0dmr171385315ad.8.1744000255036; Sun, 06 Apr 2025 21:30:55 -0700 (PDT) Received: from localhost.localdomain ([240f:74:7be:1:5985:1f8b:863f:3722]) by smtp.gmail.com with ESMTPSA id d9443c01a7336-22978670dbbsm70839525ad.209.2025.04.06.21.30.53 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sun, 06 Apr 2025 21:30:54 -0700 (PDT) From: Koichiro Den To: linux-gpio@vger.kernel.org Cc: brgl@bgdev.pl, geert+renesas@glider.be, linus.walleij@linaro.org, maciej.borzecki@canonical.com, linux-kernel@vger.kernel.org Subject: [PATCH v7 4/9] gpio: aggregator: introduce basic configfs interface Date: Mon, 7 Apr 2025 13:30:14 +0900 Message-ID: <20250407043019.4105613-5-koichiro.den@canonical.com> X-Mailer: git-send-email 2.45.2 In-Reply-To: <20250407043019.4105613-1-koichiro.den@canonical.com> References: <20250407043019.4105613-1-koichiro.den@canonical.com> 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" The existing sysfs 'new_device' interface has several limitations: * No way to determine when GPIO aggregator creation is complete. * No way to retrieve errors when creating a GPIO aggregator. * No way to trace a GPIO line of an aggregator back to its corresponding physical device. * The 'new_device' echo does not indicate which virtual gpiochip was created. * No way to assign names to GPIO lines exported through an aggregator. Introduce the new configfs interface for gpio-aggregator to address these limitations. It provides a more streamlined, modern, and extensible configuration method. For backward compatibility, the 'new_device' interface and its behavior is retained for now. This commit implements basic functionalities: /config/gpio-aggregator// /config/gpio-aggregator//live /config/gpio-aggregator//dev_name /config/gpio-aggregator/// /config/gpio-aggregator///key /config/gpio-aggregator///offset /config/gpio-aggregator///name Basic setup flow is: 1. Create a directory for a GPIO aggregator. 2. Create subdirectories for each line you want to instantiate. 3. In each line directory, configure the key and offset. The key/offset semantics are as follows: * If offset is >=3D 0: - key specifies the name of the chip this GPIO belongs to - offset specifies the line offset within that chip. * If offset is <0: - key needs to specify the GPIO line name. 4. Return to the aggregator's root directory and write '1' to the live attribute. For example, the command in the existing kernel doc: echo 'e6052000.gpio 19 e6050000.gpio 20-21' > new_device is equivalent to: mkdir /sys/kernel/config/gpio-aggregator/ # Change to name of your choice (e.g. "aggr0") cd /sys/kernel/config/gpio-aggregator/ mkdir line0 line1 line2 # Only "line" naming allowed. echo e6052000.gpio > line0/key echo 19 > line0/offset echo e6050000.gpio > line1/key echo 20 > line1/offset echo e6050000.gpio > line2/key echo 21 > line2/offset echo 1 > live The corresponding gpio_device id can be identified as follows: cd /sys/kernel/config/gpio-aggregator/ ls -d /sys/devices/platform/`cat dev_name`/gpiochip* Also, via configfs, custom GPIO line name can be set like this: cd /sys/kernel/config/gpio-aggregator/ echo "abc" > line1/name Signed-off-by: Koichiro Den --- drivers/gpio/Kconfig | 2 + drivers/gpio/gpio-aggregator.c | 638 ++++++++++++++++++++++++++++++++- 2 files changed, 628 insertions(+), 12 deletions(-) diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index f2c39bbff83a..12b29866f8fd 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -1869,6 +1869,8 @@ menu "Virtual GPIO drivers" =20 config GPIO_AGGREGATOR tristate "GPIO Aggregator" + select CONFIGFS_FS + select DEV_SYNC_PROBE help Say yes here to enable the GPIO Aggregator, which provides a way to aggregate existing GPIO lines into a new virtual GPIO chip. diff --git a/drivers/gpio/gpio-aggregator.c b/drivers/gpio/gpio-aggregator.c index 6f933a76b2b9..d1c837f00ffe 100644 --- a/drivers/gpio/gpio-aggregator.c +++ b/drivers/gpio/gpio-aggregator.c @@ -9,10 +9,13 @@ =20 #include #include +#include #include #include #include #include +#include +#include #include #include #include @@ -27,6 +30,8 @@ #include #include =20 +#include "dev-sync-probe.h" + #define AGGREGATOR_MAX_GPIOS 512 =20 /* @@ -34,12 +39,38 @@ */ =20 struct gpio_aggregator { + struct dev_sync_probe_data probe_data; + struct config_group group; struct gpiod_lookup_table *lookups; - struct platform_device *pdev; + struct mutex lock; int id; + + /* List of gpio_aggregator_line. Always added in order */ + struct list_head list_head; + + /* used by legacy sysfs interface only */ + bool init_via_sysfs; char args[]; }; =20 +struct gpio_aggregator_line { + struct config_group group; + struct gpio_aggregator *parent; + struct list_head entry; + + /* Line index within the aggregator device */ + unsigned int idx; + + /* Custom name for the virtual line */ + const char *name; + /* GPIO chip label or line name */ + const char *key; + /* Can be negative to indicate lookup by line name */ + int offset; + + enum gpio_lookup_flags flags; +}; + static DEFINE_MUTEX(gpio_aggregator_lock); /* protects idr */ static DEFINE_IDR(gpio_aggregator_idr); =20 @@ -59,6 +90,8 @@ static int gpio_aggregator_alloc(struct gpio_aggregator *= *aggr, size_t arg_size) return ret; =20 new->id =3D ret; + INIT_LIST_HEAD(&new->list_head); + mutex_init(&new->lock); *aggr =3D no_free_ptr(new); return 0; } @@ -68,6 +101,7 @@ static void gpio_aggregator_free(struct gpio_aggregator = *aggr) scoped_guard(mutex, &gpio_aggregator_lock) idr_remove(&gpio_aggregator_idr, aggr->id); =20 + mutex_destroy(&aggr->lock); kfree(aggr); } =20 @@ -90,6 +124,71 @@ static int gpio_aggregator_add_gpio(struct gpio_aggrega= tor *aggr, return 0; } =20 +static bool gpio_aggregator_is_active(struct gpio_aggregator *aggr) +{ + lockdep_assert_held(&aggr->lock); + + return aggr->probe_data.pdev && platform_get_drvdata(aggr->probe_data.pde= v); +} + +static size_t gpio_aggregator_count_lines(struct gpio_aggregator *aggr) +{ + lockdep_assert_held(&aggr->lock); + + return list_count_nodes(&aggr->list_head); +} + +static struct gpio_aggregator_line * +gpio_aggregator_line_alloc(struct gpio_aggregator *parent, unsigned int id= x, + char *key, int offset) +{ + struct gpio_aggregator_line *line; + + line =3D kzalloc(sizeof(*line), GFP_KERNEL); + if (!line) + return ERR_PTR(-ENOMEM); + + if (key) { + line->key =3D kstrdup(key, GFP_KERNEL); + if (!line->key) { + kfree(line); + return ERR_PTR(-ENOMEM); + } + } + + line->flags =3D GPIO_LOOKUP_FLAGS_DEFAULT; + line->parent =3D parent; + line->idx =3D idx; + line->offset =3D offset; + INIT_LIST_HEAD(&line->entry); + + return line; +} + +static void gpio_aggregator_line_add(struct gpio_aggregator *aggr, + struct gpio_aggregator_line *line) +{ + struct gpio_aggregator_line *tmp; + + lockdep_assert_held(&aggr->lock); + + list_for_each_entry(tmp, &aggr->list_head, entry) { + if (tmp->idx > line->idx) { + list_add_tail(&line->entry, &tmp->entry); + return; + } + } + list_add_tail(&line->entry, &aggr->list_head); +} + +static void gpio_aggregator_line_del(struct gpio_aggregator *aggr, + struct gpio_aggregator_line *line) +{ + lockdep_assert_held(&aggr->lock); + + list_del(&line->entry); +} + =20 /* * GPIO Forwarder @@ -424,6 +523,475 @@ static struct gpiochip_fwd *gpiochip_fwd_create(struc= t device *dev, } =20 =20 +/* + * Configfs interface + */ + +static struct gpio_aggregator * +to_gpio_aggregator(struct config_item *item) +{ + struct config_group *group =3D to_config_group(item); + + return container_of(group, struct gpio_aggregator, group); +} + +static struct gpio_aggregator_line * +to_gpio_aggregator_line(struct config_item *item) +{ + struct config_group *group =3D to_config_group(item); + + return container_of(group, struct gpio_aggregator_line, group); +} + +static struct fwnode_handle * +gpio_aggregator_make_device_sw_node(struct gpio_aggregator *aggr) +{ + struct property_entry properties[2]; + struct gpio_aggregator_line *line; + size_t num_lines; + int n =3D 0; + + memset(properties, 0, sizeof(properties)); + + num_lines =3D gpio_aggregator_count_lines(aggr); + if (num_lines =3D=3D 0) + return NULL; + + const char **line_names __free(kfree) =3D kcalloc( + num_lines, sizeof(*line_names), GFP_KERNEL); + if (!line_names) + return ERR_PTR(-ENOMEM); + + /* The list is always sorted as new elements are inserted in order. */ + list_for_each_entry(line, &aggr->list_head, entry) + line_names[n++] =3D line->name ?: ""; + + properties[0] =3D PROPERTY_ENTRY_STRING_ARRAY_LEN( + "gpio-line-names", + line_names, num_lines); + + return fwnode_create_software_node(properties, NULL); +} + +static int gpio_aggregator_activate(struct gpio_aggregator *aggr) +{ + struct platform_device_info pdevinfo; + struct gpio_aggregator_line *line; + struct fwnode_handle *swnode; + unsigned int n =3D 0; + int ret =3D 0; + + if (gpio_aggregator_count_lines(aggr) =3D=3D 0) + return -EINVAL; + + aggr->lookups =3D kzalloc(struct_size(aggr->lookups, table, 1), + GFP_KERNEL); + if (!aggr->lookups) + return -ENOMEM; + + swnode =3D gpio_aggregator_make_device_sw_node(aggr); + if (IS_ERR(swnode)) + goto err_remove_lookups; + + memset(&pdevinfo, 0, sizeof(pdevinfo)); + pdevinfo.name =3D DRV_NAME; + pdevinfo.id =3D aggr->id; + pdevinfo.fwnode =3D swnode; + + /* The list is always sorted as new elements are inserted in order. */ + list_for_each_entry(line, &aggr->list_head, entry) { + /* + * - Either GPIO chip label or line name must be configured + * (i.e. line->key must be non-NULL) + * - Line directories must be named with sequential numeric + * suffixes starting from 0. (i.e. ./line0, ./line1, ...) + */ + if (!line->key || line->idx !=3D n) { + ret =3D -EINVAL; + goto err_remove_swnode; + } + + if (line->offset < 0) + ret =3D gpio_aggregator_add_gpio(aggr, line->key, + U16_MAX, &n); + else + ret =3D gpio_aggregator_add_gpio(aggr, line->key, + line->offset, &n); + if (ret) + goto err_remove_swnode; + } + + aggr->lookups->dev_id =3D kasprintf(GFP_KERNEL, "%s.%d", DRV_NAME, aggr->= id); + if (!aggr->lookups->dev_id) { + ret =3D -ENOMEM; + goto err_remove_swnode; + } + + gpiod_add_lookup_table(aggr->lookups); + + ret =3D dev_sync_probe_register(&aggr->probe_data, &pdevinfo); + if (ret) + goto err_remove_lookup_table; + + return 0; + +err_remove_lookup_table: + kfree(aggr->lookups->dev_id); + gpiod_remove_lookup_table(aggr->lookups); +err_remove_swnode: + fwnode_remove_software_node(swnode); +err_remove_lookups: + kfree(aggr->lookups); + + return ret; +} + +static void gpio_aggregator_deactivate(struct gpio_aggregator *aggr) +{ + dev_sync_probe_unregister(&aggr->probe_data); + gpiod_remove_lookup_table(aggr->lookups); + kfree(aggr->lookups->dev_id); + kfree(aggr->lookups); +} + +static void gpio_aggregator_lockup_configfs(struct gpio_aggregator *aggr, + bool lock) +{ + struct configfs_subsystem *subsys =3D aggr->group.cg_subsys; + struct gpio_aggregator_line *line; + + /* + * The device only needs to depend on leaf lines. This is + * sufficient to lock up all the configfs entries that the + * instantiated, alive device depends on. + */ + list_for_each_entry(line, &aggr->list_head, entry) { + if (lock) + configfs_depend_item_unlocked( + subsys, &line->group.cg_item); + else + configfs_undepend_item_unlocked( + &line->group.cg_item); + } +} + +static ssize_t +gpio_aggregator_line_key_show(struct config_item *item, char *page) +{ + struct gpio_aggregator_line *line =3D to_gpio_aggregator_line(item); + struct gpio_aggregator *aggr =3D line->parent; + + guard(mutex)(&aggr->lock); + + return sysfs_emit(page, "%s\n", line->key ?: ""); +} + +static ssize_t +gpio_aggregator_line_key_store(struct config_item *item, const char *page, + size_t count) +{ + struct gpio_aggregator_line *line =3D to_gpio_aggregator_line(item); + struct gpio_aggregator *aggr =3D line->parent; + + char *key __free(kfree) =3D kstrndup(skip_spaces(page), count, + GFP_KERNEL); + if (!key) + return -ENOMEM; + + strim(key); + + guard(mutex)(&aggr->lock); + + if (gpio_aggregator_is_active(aggr)) + return -EBUSY; + + kfree(line->key); + line->key =3D no_free_ptr(key); + + return count; +} +CONFIGFS_ATTR(gpio_aggregator_line_, key); + +static ssize_t +gpio_aggregator_line_name_show(struct config_item *item, char *page) +{ + struct gpio_aggregator_line *line =3D to_gpio_aggregator_line(item); + struct gpio_aggregator *aggr =3D line->parent; + + guard(mutex)(&aggr->lock); + + return sysfs_emit(page, "%s\n", line->name ?: ""); +} + +static ssize_t +gpio_aggregator_line_name_store(struct config_item *item, const char *page, + size_t count) +{ + struct gpio_aggregator_line *line =3D to_gpio_aggregator_line(item); + struct gpio_aggregator *aggr =3D line->parent; + + char *name __free(kfree) =3D kstrndup(skip_spaces(page), count, + GFP_KERNEL); + if (!name) + return -ENOMEM; + + strim(name); + + guard(mutex)(&aggr->lock); + + if (gpio_aggregator_is_active(aggr)) + return -EBUSY; + + kfree(line->name); + line->name =3D no_free_ptr(name); + + return count; +} +CONFIGFS_ATTR(gpio_aggregator_line_, name); + +static ssize_t +gpio_aggregator_line_offset_show(struct config_item *item, char *page) +{ + struct gpio_aggregator_line *line =3D to_gpio_aggregator_line(item); + struct gpio_aggregator *aggr =3D line->parent; + + guard(mutex)(&aggr->lock); + + return sysfs_emit(page, "%d\n", line->offset); +} + +static ssize_t +gpio_aggregator_line_offset_store(struct config_item *item, const char *pa= ge, + size_t count) +{ + struct gpio_aggregator_line *line =3D to_gpio_aggregator_line(item); + struct gpio_aggregator *aggr =3D line->parent; + int offset, ret; + + ret =3D kstrtoint(page, 0, &offset); + if (ret) + return ret; + + /* + * When offset =3D=3D -1, 'key' represents a line name to lookup. + * When 0 <=3D offset < 65535, 'key' represents the label of the chip with + * the 'offset' value representing the line within that chip. + * + * GPIOLIB uses the U16_MAX value to indicate lookup by line name so + * the greatest offset we can accept is (U16_MAX - 1). + */ + if (offset > (U16_MAX - 1) || offset < -1) + return -EINVAL; + + guard(mutex)(&aggr->lock); + + if (gpio_aggregator_is_active(aggr)) + return -EBUSY; + + line->offset =3D offset; + + return count; +} +CONFIGFS_ATTR(gpio_aggregator_line_, offset); + +static struct configfs_attribute *gpio_aggregator_line_attrs[] =3D { + &gpio_aggregator_line_attr_key, + &gpio_aggregator_line_attr_name, + &gpio_aggregator_line_attr_offset, + NULL +}; + +static ssize_t +gpio_aggregator_device_dev_name_show(struct config_item *item, char *page) +{ + struct gpio_aggregator *aggr =3D to_gpio_aggregator(item); + struct platform_device *pdev; + + guard(mutex)(&aggr->lock); + + pdev =3D aggr->probe_data.pdev; + if (pdev) + return sysfs_emit(page, "%s\n", dev_name(&pdev->dev)); + + return sysfs_emit(page, "%s.%d\n", DRV_NAME, aggr->id); +} +CONFIGFS_ATTR_RO(gpio_aggregator_device_, dev_name); + +static ssize_t +gpio_aggregator_device_live_show(struct config_item *item, char *page) +{ + struct gpio_aggregator *aggr =3D to_gpio_aggregator(item); + + guard(mutex)(&aggr->lock); + + return sysfs_emit(page, "%c\n", + gpio_aggregator_is_active(aggr) ? '1' : '0'); +} + +static ssize_t +gpio_aggregator_device_live_store(struct config_item *item, const char *pa= ge, + size_t count) +{ + struct gpio_aggregator *aggr =3D to_gpio_aggregator(item); + int ret =3D 0; + bool live; + + ret =3D kstrtobool(page, &live); + if (ret) + return ret; + + if (!try_module_get(THIS_MODULE)) + return -ENOENT; + + if (live) + gpio_aggregator_lockup_configfs(aggr, true); + + scoped_guard(mutex, &aggr->lock) { + if (live =3D=3D gpio_aggregator_is_active(aggr)) + ret =3D -EPERM; + else if (live) + ret =3D gpio_aggregator_activate(aggr); + else + gpio_aggregator_deactivate(aggr); + } + + /* + * Undepend is required only if device disablement (live =3D=3D 0) + * succeeds or if device enablement (live =3D=3D 1) fails. + */ + if (live =3D=3D !!ret) + gpio_aggregator_lockup_configfs(aggr, false); + + module_put(THIS_MODULE); + + return ret ?: count; +} +CONFIGFS_ATTR(gpio_aggregator_device_, live); + +static struct configfs_attribute *gpio_aggregator_device_attrs[] =3D { + &gpio_aggregator_device_attr_dev_name, + &gpio_aggregator_device_attr_live, + NULL +}; + +static void +gpio_aggregator_line_release(struct config_item *item) +{ + struct gpio_aggregator_line *line =3D to_gpio_aggregator_line(item); + struct gpio_aggregator *aggr =3D line->parent; + + guard(mutex)(&aggr->lock); + + gpio_aggregator_line_del(aggr, line); + kfree(line->key); + kfree(line->name); + kfree(line); +} + +static struct configfs_item_operations gpio_aggregator_line_item_ops =3D { + .release =3D gpio_aggregator_line_release, +}; + +static const struct config_item_type gpio_aggregator_line_type =3D { + .ct_item_ops =3D &gpio_aggregator_line_item_ops, + .ct_attrs =3D gpio_aggregator_line_attrs, + .ct_owner =3D THIS_MODULE, +}; + +static void gpio_aggregator_device_release(struct config_item *item) +{ + struct gpio_aggregator *aggr =3D to_gpio_aggregator(item); + + /* + * If the aggregator is active, this code wouldn't be reached, + * so calling gpio_aggregator_deactivate() is always unnecessary. + */ + gpio_aggregator_free(aggr); +} + +static struct configfs_item_operations gpio_aggregator_device_item_ops =3D= { + .release =3D gpio_aggregator_device_release, +}; + +static struct config_group * +gpio_aggregator_device_make_group(struct config_group *group, const char *= name) +{ + struct gpio_aggregator *aggr =3D to_gpio_aggregator(&group->cg_item); + struct gpio_aggregator_line *line; + unsigned int idx; + int ret, nchar; + + ret =3D sscanf(name, "line%u%n", &idx, &nchar); + if (ret !=3D 1 || nchar !=3D strlen(name)) + return ERR_PTR(-EINVAL); + + guard(mutex)(&aggr->lock); + + if (gpio_aggregator_is_active(aggr)) + return ERR_PTR(-EBUSY); + + list_for_each_entry(line, &aggr->list_head, entry) + if (line->idx =3D=3D idx) + return ERR_PTR(-EINVAL); + + line =3D gpio_aggregator_line_alloc(aggr, idx, NULL, -1); + if (!line) + return ERR_PTR(-ENOMEM); + + config_group_init_type_name(&line->group, name, &gpio_aggregator_line_typ= e); + + gpio_aggregator_line_add(aggr, line); + + return &line->group; +} + +static struct configfs_group_operations gpio_aggregator_device_group_ops = =3D { + .make_group =3D gpio_aggregator_device_make_group, +}; + +static const struct config_item_type gpio_aggregator_device_type =3D { + .ct_group_ops =3D &gpio_aggregator_device_group_ops, + .ct_item_ops =3D &gpio_aggregator_device_item_ops, + .ct_attrs =3D gpio_aggregator_device_attrs, + .ct_owner =3D THIS_MODULE, +}; + +static struct config_group * +gpio_aggregator_make_group(struct config_group *group, const char *name) +{ + struct gpio_aggregator *aggr; + int ret; + + /* arg space is unneeded */ + ret =3D gpio_aggregator_alloc(&aggr, 0); + if (ret) + return ERR_PTR(ret); + + config_group_init_type_name(&aggr->group, name, &gpio_aggregator_device_t= ype); + dev_sync_probe_init(&aggr->probe_data); + + return &aggr->group; +} + +static struct configfs_group_operations gpio_aggregator_group_ops =3D { + .make_group =3D gpio_aggregator_make_group, +}; + +static const struct config_item_type gpio_aggregator_type =3D { + .ct_group_ops =3D &gpio_aggregator_group_ops, + .ct_owner =3D THIS_MODULE, +}; + +static struct configfs_subsystem gpio_aggregator_subsys =3D { + .su_group =3D { + .cg_item =3D { + .ci_namebuf =3D DRV_NAME, + .ci_type =3D &gpio_aggregator_type, + }, + }, +}; + + /* * Sysfs interface */ @@ -495,6 +1063,7 @@ static ssize_t gpio_aggregator_new_device_store(struct= device_driver *driver, =20 memcpy(aggr->args, buf, count + 1); =20 + aggr->init_via_sysfs =3D true; aggr->lookups =3D kzalloc(struct_size(aggr->lookups, table, 1), GFP_KERNEL); if (!aggr->lookups) { @@ -520,7 +1089,7 @@ static ssize_t gpio_aggregator_new_device_store(struct= device_driver *driver, goto remove_table; } =20 - aggr->pdev =3D pdev; + aggr->probe_data.pdev =3D pdev; module_put(THIS_MODULE); return count; =20 @@ -542,10 +1111,7 @@ static struct driver_attribute driver_attr_gpio_aggre= gator_new_device =3D =20 static void gpio_aggregator_destroy(struct gpio_aggregator *aggr) { - platform_device_unregister(aggr->pdev); - gpiod_remove_lookup_table(aggr->lookups); - kfree(aggr->lookups->dev_id); - kfree(aggr->lookups); + gpio_aggregator_deactivate(aggr); kfree(aggr); } =20 @@ -567,12 +1133,19 @@ static ssize_t gpio_aggregator_delete_device_store(s= truct device_driver *driver, return -ENOENT; =20 mutex_lock(&gpio_aggregator_lock); - aggr =3D idr_remove(&gpio_aggregator_idr, id); - mutex_unlock(&gpio_aggregator_lock); - if (!aggr) { + aggr =3D idr_find(&gpio_aggregator_idr, id); + /* + * For simplicity, devices created via configfs cannot be deleted + * via sysfs. + */ + if (aggr && aggr->init_via_sysfs) + idr_remove(&gpio_aggregator_idr, id); + else { + mutex_unlock(&gpio_aggregator_lock); module_put(THIS_MODULE); return -ENOENT; } + mutex_unlock(&gpio_aggregator_lock); =20 gpio_aggregator_destroy(aggr); module_put(THIS_MODULE); @@ -649,21 +1222,61 @@ static struct platform_driver gpio_aggregator_driver= =3D { =20 static int __exit gpio_aggregator_idr_remove(int id, void *p, void *data) { + /* + * There should be no aggregator created via configfs, as their + * presence would prevent module unloading. + */ gpio_aggregator_destroy(p); return 0; } =20 static void __exit gpio_aggregator_remove_all(void) { - mutex_lock(&gpio_aggregator_lock); + /* + * Configfs callbacks acquire gpio_aggregator_lock when accessing + * gpio_aggregator_idr, so to prevent lock inversion deadlock, we + * cannot protect idr_for_each invocation here with + * gpio_aggregator_lock, as gpio_aggregator_idr_remove() accesses + * configfs groups. Fortunately, the new_device/delete_device path + * and the module unload path are mutually exclusive, thanks to an + * explicit try_module_get inside of those driver attr handlers. + * Also, when we reach here, no configfs entries present or being + * created. Therefore, no need to protect with gpio_aggregator_lock + * below. + */ idr_for_each(&gpio_aggregator_idr, gpio_aggregator_idr_remove, NULL); idr_destroy(&gpio_aggregator_idr); - mutex_unlock(&gpio_aggregator_lock); } =20 static int __init gpio_aggregator_init(void) { - return platform_driver_register(&gpio_aggregator_driver); + int ret =3D 0; + + config_group_init(&gpio_aggregator_subsys.su_group); + mutex_init(&gpio_aggregator_subsys.su_mutex); + ret =3D configfs_register_subsystem(&gpio_aggregator_subsys); + if (ret) { + pr_err("Failed to register the '%s' configfs subsystem: %d\n", + gpio_aggregator_subsys.su_group.cg_item.ci_namebuf, ret); + mutex_destroy(&gpio_aggregator_subsys.su_mutex); + return ret; + } + + /* + * CAVEAT: This must occur after configfs registration. Otherwise, + * a race condition could arise: driver attribute groups might be + * exposed and accessed by users before configfs registration + * completes. new_device_store() does not expect a partially + * initialized configfs state. + */ + ret =3D platform_driver_register(&gpio_aggregator_driver); + if (ret) { + pr_err("Failed to register the platform driver: %d\n", ret); + mutex_destroy(&gpio_aggregator_subsys.su_mutex); + configfs_unregister_subsystem(&gpio_aggregator_subsys); + } + + return ret; } module_init(gpio_aggregator_init); =20 @@ -671,6 +1284,7 @@ static void __exit gpio_aggregator_exit(void) { gpio_aggregator_remove_all(); platform_driver_unregister(&gpio_aggregator_driver); + configfs_unregister_subsystem(&gpio_aggregator_subsys); } module_exit(gpio_aggregator_exit); =20 --=20 2.45.2 From nobody Wed Dec 17 17:28:51 2025 Received: from smtp-relay-internal-1.canonical.com (smtp-relay-internal-1.canonical.com [185.125.188.123]) (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 6DA7922258C for ; Mon, 7 Apr 2025 04:31:01 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=185.125.188.123 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1744000263; cv=none; b=espxNuwXom88n9e8WY/dgdIRqFKbxYDVRq8GtR+1Or6xR0VMY3tJ5UPc09ToAySHyMCKK7C3+xV6mocBn83Blv4fb3vt8uk312dtQGqE9t14+ujYEem+6t0Jv7WgymXKbSLsqVSD4/euATroJrGTwRMPw9LWBu+LrR2CoUo0dUs= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1744000263; c=relaxed/simple; bh=topjyXl8cfmVC2wwnmz9rv1A6nyhahSaPg2JtNji6lQ=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=tpk5L5iJPHJoXX0W/SAhhhuQZzDvvTWsafP1tVwA2a/K8ecR01d3L7ItNrMklHbeRZEMDNAlqmoopMCHBsXpLC+6q/XsyR6Wby1YNqi6i2q1CUybH9OOVOzOuTR4tBdk8DtD5Doqq2K1gEIxkrOMIWiTq2MDO7J1kQ6RqVTB+yg= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=canonical.com; spf=pass smtp.mailfrom=canonical.com; dkim=pass (2048-bit key) header.d=canonical.com header.i=@canonical.com header.b=LBPY3WHJ; arc=none smtp.client-ip=185.125.188.123 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=canonical.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=canonical.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=canonical.com header.i=@canonical.com header.b="LBPY3WHJ" Received: from mail-pl1-f197.google.com (mail-pl1-f197.google.com [209.85.214.197]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by smtp-relay-internal-1.canonical.com (Postfix) with ESMTPS id 5C2E43F287 for ; Mon, 7 Apr 2025 04:30:59 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=canonical.com; s=20210705; t=1744000259; bh=JN2InJzY9fip1rzot+r5SY6hkXxBlsffh5kl0Mfv5JI=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=LBPY3WHJdVkXWqtvLgTGv4McIMBqZg3tQiVla209NiMLeT/CBJnBAVAga8HoARSad zlQ/GDfc2lr+ji11vzXC3AW5rWRVAlnhP/dkB6mD8+gnypWUgqwyQTUNfEz0AKvege fBRP7dY3HY0PbFCZdht2QRlB0CZvE1jyJwU7hWqsKOG6vifiAGUwR1DcjjUIQwdlJn vVghKLALleMc+1Jgdz9lMmWcsphwKP1WFhNWOx6CA2sW0wq50hByiaidXey8MkEivm MJEcRAaxbYyUMe9wSP7Z3QPfIrkXOPfEZI0m6+MhJZAzYXUzfqL4tdPP6KTyFCss6i PVeMeqx1oJ5jw== Received: by mail-pl1-f197.google.com with SMTP id d9443c01a7336-225107fbdc7so33203375ad.0 for ; Sun, 06 Apr 2025 21:30:59 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1744000257; x=1744605057; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=JN2InJzY9fip1rzot+r5SY6hkXxBlsffh5kl0Mfv5JI=; b=JVYDJ1qS0HwWAHdVU7+oumSVvkU6yPA8iIJ4e/Q+VZt3S6gyFKEKpBaqcTdw2N42YN LToWnu8MPQ6VsfOzeJuGSCmnnAu31cD0xRIB2wZVMhqgsxDZl7C9HGuPSr4RAJSL6Miw ue2Buc6dJcs2Rnbs6HzP+YIqr8p10R2jG10DBB/UUhgjj0Za7TE3Mjpf88Zxbi0CMm2m 74KlC+j2361s/dEA/s2YCaqp65C1AKy7qrvw0O2OZJ1VC3f/UCUZmj0knWSbOJA0jNzu qUI9C+izRjZHpGKhBTNZOF68Dp2EW4t61kG7GAmcQnTJOwnrSA2Dise10HVy1geJiBmy LtLg== X-Forwarded-Encrypted: i=1; AJvYcCVAEED1lSbQoCn73kVUteNnpTURmKDzPaPkBy3UxKqqWyMQSqw0w1z5vx4cy2ptaZ2wJIFJO5olPrc7jkM=@vger.kernel.org X-Gm-Message-State: AOJu0YxXPHxTuoTO0T/YB64HgAy+6InA++3ykIfotj+q1LWEjzIC33yd D22OOHyz65Bsi9TkJjVfyrTVNOGBE+5ZYr8IJYrGtGuvisGFjPnqci09cd0f1Y2FCssqkATDes1 cgb1+vKTb18LFxkc2eQlpG7mLAtSs/OQ/cCuWcZYx/i6HvJtm5mp53C0ohwSxcPyo5hAjXmS5so VOXg== X-Gm-Gg: ASbGncvbGr3IB09wF1C5LVmPDyCdsd7hcpLE3lunSW11ivtOmCJiaxXGPJsn91SlqQO S3yc5pVlvZY096TzJ06E13XnTT3fS9bG5CjfEmBdRsMK2LZPPyCCWJ7QbE7bAmTfXh0BBXIRezr pVGWn8N96kOWwJBIilCU9TD+RtsSR/NT1M7KdrL3jbE8eSp0zMfGeJhoANpP89mqSHzB02NRpNk Dfvin5qOe0LnSX20k3YQSEP33ogQfV5R/LXggFJqt/k8X1zCfI0fr0o/QVogYN+GwL+Cje/AtTT 6WP6MAdH+vIgAFGN2shZW9S053HFfrUrQQ== X-Received: by 2002:a17:903:1948:b0:224:a74:28cd with SMTP id d9443c01a7336-22a8a87d03fmr152760885ad.31.1744000257304; Sun, 06 Apr 2025 21:30:57 -0700 (PDT) X-Google-Smtp-Source: AGHT+IHMJeKHzvUbt+AOJlfEZJCfM9kHhfstRNImHDDwMKHZqjXIMCC7cuk6cmuwqUav1kWPUQTDcQ== X-Received: by 2002:a17:903:1948:b0:224:a74:28cd with SMTP id d9443c01a7336-22a8a87d03fmr152760695ad.31.1744000257046; Sun, 06 Apr 2025 21:30:57 -0700 (PDT) Received: from localhost.localdomain ([240f:74:7be:1:5985:1f8b:863f:3722]) by smtp.gmail.com with ESMTPSA id d9443c01a7336-22978670dbbsm70839525ad.209.2025.04.06.21.30.55 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sun, 06 Apr 2025 21:30:56 -0700 (PDT) From: Koichiro Den To: linux-gpio@vger.kernel.org Cc: brgl@bgdev.pl, geert+renesas@glider.be, linus.walleij@linaro.org, maciej.borzecki@canonical.com, linux-kernel@vger.kernel.org Subject: [PATCH v7 5/9] gpio: aggregator: rename 'name' to 'key' in gpio_aggregator_parse() Date: Mon, 7 Apr 2025 13:30:15 +0900 Message-ID: <20250407043019.4105613-6-koichiro.den@canonical.com> X-Mailer: git-send-email 2.45.2 In-Reply-To: <20250407043019.4105613-1-koichiro.den@canonical.com> References: <20250407043019.4105613-1-koichiro.den@canonical.com> 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" Rename the local variable 'name' in gpio_aggregator_parse() to 'key' because struct gpio_aggregator_line now uses the 'name' field for the custom line name and the local variable actually represents a 'key'. This change prepares for the next but one commit. No functional change. Reviewed-by: Geert Uytterhoeven Signed-off-by: Koichiro Den --- drivers/gpio/gpio-aggregator.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/drivers/gpio/gpio-aggregator.c b/drivers/gpio/gpio-aggregator.c index d1c837f00ffe..2d8a7019b75e 100644 --- a/drivers/gpio/gpio-aggregator.c +++ b/drivers/gpio/gpio-aggregator.c @@ -998,7 +998,7 @@ static struct configfs_subsystem gpio_aggregator_subsys= =3D { static int gpio_aggregator_parse(struct gpio_aggregator *aggr) { char *args =3D skip_spaces(aggr->args); - char *name, *offsets, *p; + char *key, *offsets, *p; unsigned int i, n =3D 0; int error =3D 0; =20 @@ -1007,18 +1007,18 @@ static int gpio_aggregator_parse(struct gpio_aggreg= ator *aggr) if (!bitmap) return -ENOMEM; =20 - args =3D next_arg(args, &name, &p); + args =3D next_arg(args, &key, &p); while (*args) { args =3D next_arg(args, &offsets, &p); =20 p =3D get_options(offsets, 0, &error); if (error =3D=3D 0 || *p) { /* Named GPIO line */ - error =3D gpio_aggregator_add_gpio(aggr, name, U16_MAX, &n); + error =3D gpio_aggregator_add_gpio(aggr, key, U16_MAX, &n); if (error) return error; =20 - name =3D offsets; + key =3D offsets; continue; } =20 @@ -1030,12 +1030,12 @@ static int gpio_aggregator_parse(struct gpio_aggreg= ator *aggr) } =20 for_each_set_bit(i, bitmap, AGGREGATOR_MAX_GPIOS) { - error =3D gpio_aggregator_add_gpio(aggr, name, i, &n); + error =3D gpio_aggregator_add_gpio(aggr, key, i, &n); if (error) return error; } =20 - args =3D next_arg(args, &name, &p); + args =3D next_arg(args, &key, &p); } =20 if (!n) { --=20 2.45.2 From nobody Wed Dec 17 17:28:51 2025 Received: from smtp-relay-internal-1.canonical.com (smtp-relay-internal-1.canonical.com [185.125.188.123]) (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 0893D22259C for ; Mon, 7 Apr 2025 04:31:02 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=185.125.188.123 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1744000265; cv=none; b=TC5AXn/xG+mog8of0bfTyx37k50JvdsVsLHrnYR3yg7YOtfUWu/XIpfZHGW9zH8KTj6IDdu86yX3Phqrk1ETP4lcpr7E9qstYAqLSyU9xGY+1EAPqrsN7SASWz/GUq/ZJlBAz/a4/ivRPIum23w2kvq7LW6ECkG8V53kDuYkejg= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1744000265; c=relaxed/simple; bh=YRfwgBPjWgBBKoPJSbVVBZYu3/kI4nztcwIUXOqhvgU=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=HT9Yf/4a4w21F3DwGasLQvsMwbz3+x+lAYQdhmqrtiwUiFMaQ2cCVwCEBzaceINdBPJK5NIxRJ9WntnNF4ghDVH0yH+TcxoVHfHaGyWO/AvV9RdZR+a2xSRRQV84P6VO4h5D4PxbzaabHQ3uWLkkyTaAeN7Q0EXup7I1FyLS4xQ= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=canonical.com; spf=pass smtp.mailfrom=canonical.com; dkim=pass (2048-bit key) header.d=canonical.com header.i=@canonical.com header.b=Y2R5CTRR; arc=none smtp.client-ip=185.125.188.123 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=canonical.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=canonical.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=canonical.com header.i=@canonical.com header.b="Y2R5CTRR" Received: from mail-pl1-f198.google.com (mail-pl1-f198.google.com [209.85.214.198]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by smtp-relay-internal-1.canonical.com (Postfix) with ESMTPS id 607323F181 for ; Mon, 7 Apr 2025 04:31:01 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=canonical.com; s=20210705; t=1744000261; bh=pkRong3R1WAVT/Zk9oY39vsyRup91MiW4yFLIt9P1U4=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=Y2R5CTRRuE0j4SPZfgMa+y5bF8mb1EXgiSYwuTo07ni6JQ35mraGecCQLw2L+vFkD Qs1sw25cxIEacb4dgNHkHtNLfPkOS9SRpTNZzuRs+UBS0VPgsAqCsi679bXCBzlLD8 64248XJTnVpyTE1vzxpPwf5EYyMfzFTgErUKckdFUt6/6f5U2FMYJfcmGepct/hkll AfURNKE5CkNpFMP3WoagzNcDbuuT/bBJHWe2ylN81G+lU/S/lC560sQ9G02CkIRiM/ r4SFDHKJG/BJtMCwLiF1Ky7sZduCqqWmeg9CvwkiMJSa6pYSLlu+WmndzpBcPovD22 0nWe4WF7yv/Ww== Received: by mail-pl1-f198.google.com with SMTP id d9443c01a7336-22410053005so64347105ad.1 for ; Sun, 06 Apr 2025 21:31:01 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1744000260; x=1744605060; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=pkRong3R1WAVT/Zk9oY39vsyRup91MiW4yFLIt9P1U4=; b=rPUwKETlPLrAg6p15FeRWaBmlNEd4JOW5zMpwDlglBx/DPWN7ZjiiWF9t2ZJyDYDQf f7LJPklEuVgjOEYCpN08fqG9clyN89ViTJYT+rp6AxXSpdxm2sanDO6tjL/+RJ7bgycj cSzfursiyYx6EaDHAaLvddPKPsOdgODoETHAm7QL+rUQ8SQZYzzG5PEMp7cs7O1s0zKp 1wj7Fc7Ak5DT7xx1xby2ZYvqRXz9qOi7itQ9FjCXH7R3AYvt1qvMAizEa91CA3suN9n+ Abm2DWmsgsXOJ05m8u8kakToJ+mUSmvmfYTo0/yXL8gHsh38cMV0vgDegQzCAVLueuDn 8MoA== X-Forwarded-Encrypted: i=1; AJvYcCVx5YSnLUluxA+YC89VcLk9YgsHnORlamxmI1TFFR/9cm7EhdPkbpeszJYIVlDoxqKEvqAeAU23fOH4KKQ=@vger.kernel.org X-Gm-Message-State: AOJu0Yyl7mx25UTSnujYfHQ+jxZEDbQFe99LSNgihmVfWflk0Y1oMSN9 ZsagRIw2Svhj8SoOz3mYZRcI56dSIe32EgoaXmHevfWUIpAVsSfUWrEXAkCggx05LZEqVm12eYH okJaUF7nA2NKSGZiP1f89LJT0w0phZmpkRnNZ5QVkTTtgxz/3y6PslaIAFY6DZ+aADQTwie8ZFs nHbQ== X-Gm-Gg: ASbGncugthSTXYJSh0FgepjGafcyeN7sYoZZ6wSDnuPhUZI9nuYw+ryI/rNfbmUgw0B yJgEUWpwEkAF0BIsE+XxNatUvS79uVGMlsu2LHcZ9BE1CF747ba4rjGj3nFas5ioCFE0C1zjOH2 uPzCLRXGw8+U4u24k4Qx3UOjxO/aJFYCk6yKQOMC+7vQWuTX0NY3u4DEILG8vfeVZjUGVnckOEX PjRRdN9JVBYe6qMVvnzT74+do0/oUARFcllrMsvfwMdzrZzKrvm7Nhgn82p8lXpY+aUCyW2365l fFb7f1X4sI5uwyl36VbTcam5IDn+EZNbTw== X-Received: by 2002:a17:902:f64a:b0:224:194c:6942 with SMTP id d9443c01a7336-22a8a8b828amr164483975ad.34.1744000259685; Sun, 06 Apr 2025 21:30:59 -0700 (PDT) X-Google-Smtp-Source: AGHT+IFoTBtjgGYyMo8U+tHKN7qfhJYW+OLGU/Ad2psdFbZ7K7/CabhVgATMiQwhPW0bUE1Ii37gcA== X-Received: by 2002:a17:902:f64a:b0:224:194c:6942 with SMTP id d9443c01a7336-22a8a8b828amr164483705ad.34.1744000259250; Sun, 06 Apr 2025 21:30:59 -0700 (PDT) Received: from localhost.localdomain ([240f:74:7be:1:5985:1f8b:863f:3722]) by smtp.gmail.com with ESMTPSA id d9443c01a7336-22978670dbbsm70839525ad.209.2025.04.06.21.30.57 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sun, 06 Apr 2025 21:30:58 -0700 (PDT) From: Koichiro Den To: linux-gpio@vger.kernel.org Cc: brgl@bgdev.pl, geert+renesas@glider.be, linus.walleij@linaro.org, maciej.borzecki@canonical.com, linux-kernel@vger.kernel.org Subject: [PATCH v7 6/9] gpio: aggregator: expose aggregator created via legacy sysfs to configfs Date: Mon, 7 Apr 2025 13:30:16 +0900 Message-ID: <20250407043019.4105613-7-koichiro.den@canonical.com> X-Mailer: git-send-email 2.45.2 In-Reply-To: <20250407043019.4105613-1-koichiro.den@canonical.com> References: <20250407043019.4105613-1-koichiro.den@canonical.com> 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" Expose settings for aggregators created using the sysfs 'new_device' interface to configfs. Once written to 'new_device', an "_sysfs." path appears in the configfs regardless of whether the probe succeeds. Consequently, users can no longer use that prefix for custom GPIO aggregator names. The 'live' attribute changes to 1 when the probe succeeds and the GPIO forwarder is instantiated. Note that the aggregator device created via sysfs is asynchronous, i.e. writing into 'new_device' returns without waiting for probe completion, and the probe may succeed, fail, or eventually succeed via deferred probe. Thus, the 'live' attribute may change from 0 to 1 asynchronously without notice. So, editing key/offset/name while it's waiting for deferred probe is prohibited. The configfs auto-generation relies on create_default_group(), which inherently prohibits rmdir(2). To align with the limitation, this commit also prohibits mkdir(2) for them. When users want to change the number of lines for an aggregator initialized via 'new_device', they need to tear down the device using 'delete_device' and reconfigure it from scratch. This does not break previous behavior; users of legacy sysfs interface simply gain additional almost read-only configfs exposure. Still, users can write to the 'live' attribute to toggle the device unless it's waiting for deferred probe. So once probe succeeds, they can deactivate it in the same manner as the devices initialized via configfs. Signed-off-by: Koichiro Den --- drivers/gpio/gpio-aggregator.c | 138 ++++++++++++++++++++++++++++++--- 1 file changed, 126 insertions(+), 12 deletions(-) diff --git a/drivers/gpio/gpio-aggregator.c b/drivers/gpio/gpio-aggregator.c index 2d8a7019b75e..bea01ebe8cda 100644 --- a/drivers/gpio/gpio-aggregator.c +++ b/drivers/gpio/gpio-aggregator.c @@ -33,6 +33,7 @@ #include "dev-sync-probe.h" =20 #define AGGREGATOR_MAX_GPIOS 512 +#define AGGREGATOR_LEGACY_PREFIX "_sysfs" =20 /* * GPIO Aggregator sysfs interface @@ -131,6 +132,14 @@ static bool gpio_aggregator_is_active(struct gpio_aggr= egator *aggr) return aggr->probe_data.pdev && platform_get_drvdata(aggr->probe_data.pde= v); } =20 +/* Only aggregators created via legacy sysfs can be "activating". */ +static bool gpio_aggregator_is_activating(struct gpio_aggregator *aggr) +{ + lockdep_assert_held(&aggr->lock); + + return aggr->probe_data.pdev && !platform_get_drvdata(aggr->probe_data.pd= ev); +} + static size_t gpio_aggregator_count_lines(struct gpio_aggregator *aggr) { lockdep_assert_held(&aggr->lock); @@ -189,6 +198,30 @@ static void gpio_aggregator_line_del(struct gpio_aggre= gator *aggr, list_del(&line->entry); } =20 +static void gpio_aggregator_free_lines(struct gpio_aggregator *aggr) +{ + struct gpio_aggregator_line *line, *tmp; + + list_for_each_entry_safe(line, tmp, &aggr->list_head, entry) { + configfs_unregister_group(&line->group); + /* + * Normally, we acquire aggr->lock within the configfs + * callback. However, in the legacy sysfs interface case, + * calling configfs_(un)register_group while holding + * aggr->lock could cause a deadlock. Fortunately, this is + * unnecessary because the new_device/delete_device path + * and the module unload path are mutually exclusive, + * thanks to an explicit try_module_get. That's why this + * minimal scoped_guard suffices. + */ + scoped_guard(mutex, &aggr->lock) + gpio_aggregator_line_del(aggr, line); + kfree(line->key); + kfree(line->name); + kfree(line); + } +} + =20 /* * GPIO Forwarder @@ -702,7 +735,8 @@ gpio_aggregator_line_key_store(struct config_item *item= , const char *page, =20 guard(mutex)(&aggr->lock); =20 - if (gpio_aggregator_is_active(aggr)) + if (gpio_aggregator_is_activating(aggr) || + gpio_aggregator_is_active(aggr)) return -EBUSY; =20 kfree(line->key); @@ -739,7 +773,8 @@ gpio_aggregator_line_name_store(struct config_item *ite= m, const char *page, =20 guard(mutex)(&aggr->lock); =20 - if (gpio_aggregator_is_active(aggr)) + if (gpio_aggregator_is_activating(aggr) || + gpio_aggregator_is_active(aggr)) return -EBUSY; =20 kfree(line->name); @@ -785,7 +820,8 @@ gpio_aggregator_line_offset_store(struct config_item *i= tem, const char *page, =20 guard(mutex)(&aggr->lock); =20 - if (gpio_aggregator_is_active(aggr)) + if (gpio_aggregator_is_activating(aggr) || + gpio_aggregator_is_active(aggr)) return -EBUSY; =20 line->offset =3D offset; @@ -843,11 +879,12 @@ gpio_aggregator_device_live_store(struct config_item = *item, const char *page, if (!try_module_get(THIS_MODULE)) return -ENOENT; =20 - if (live) + if (live && !aggr->init_via_sysfs) gpio_aggregator_lockup_configfs(aggr, true); =20 scoped_guard(mutex, &aggr->lock) { - if (live =3D=3D gpio_aggregator_is_active(aggr)) + if (gpio_aggregator_is_activating(aggr) || + (live =3D=3D gpio_aggregator_is_active(aggr))) ret =3D -EPERM; else if (live) ret =3D gpio_aggregator_activate(aggr); @@ -859,7 +896,7 @@ gpio_aggregator_device_live_store(struct config_item *i= tem, const char *page, * Undepend is required only if device disablement (live =3D=3D 0) * succeeds or if device enablement (live =3D=3D 1) fails. */ - if (live =3D=3D !!ret) + if (live =3D=3D !!ret && !aggr->init_via_sysfs) gpio_aggregator_lockup_configfs(aggr, false); =20 module_put(THIS_MODULE); @@ -903,7 +940,7 @@ static void gpio_aggregator_device_release(struct confi= g_item *item) struct gpio_aggregator *aggr =3D to_gpio_aggregator(item); =20 /* - * If the aggregator is active, this code wouldn't be reached, + * At this point, aggr is neither active nor activating, * so calling gpio_aggregator_deactivate() is always unnecessary. */ gpio_aggregator_free(aggr); @@ -925,6 +962,15 @@ gpio_aggregator_device_make_group(struct config_group = *group, const char *name) if (ret !=3D 1 || nchar !=3D strlen(name)) return ERR_PTR(-EINVAL); =20 + if (aggr->init_via_sysfs) + /* + * Aggregators created via legacy sysfs interface are exposed as + * default groups, which means rmdir(2) is prohibited for them. + * For simplicity, and to avoid confusion, we also prohibit + * mkdir(2). + */ + return ERR_PTR(-EPERM); + guard(mutex)(&aggr->lock); =20 if (gpio_aggregator_is_active(aggr)) @@ -962,6 +1008,14 @@ gpio_aggregator_make_group(struct config_group *group= , const char *name) struct gpio_aggregator *aggr; int ret; =20 + /* + * "_sysfs" prefix is reserved for auto-generated config group + * for devices create via legacy sysfs interface. + */ + if (strncmp(name, AGGREGATOR_LEGACY_PREFIX, + sizeof(AGGREGATOR_LEGACY_PREFIX)) =3D=3D 0) + return ERR_PTR(-EINVAL); + /* arg space is unneeded */ ret =3D gpio_aggregator_alloc(&aggr, 0); if (ret) @@ -998,6 +1052,8 @@ static struct configfs_subsystem gpio_aggregator_subsy= s =3D { static int gpio_aggregator_parse(struct gpio_aggregator *aggr) { char *args =3D skip_spaces(aggr->args); + struct gpio_aggregator_line *line; + char name[CONFIGFS_ITEM_NAME_LEN]; char *key, *offsets, *p; unsigned int i, n =3D 0; int error =3D 0; @@ -1014,9 +1070,24 @@ static int gpio_aggregator_parse(struct gpio_aggrega= tor *aggr) p =3D get_options(offsets, 0, &error); if (error =3D=3D 0 || *p) { /* Named GPIO line */ + scnprintf(name, sizeof(name), "line%u", n); + line =3D gpio_aggregator_line_alloc(aggr, n, key, -1); + if (!line) { + error =3D -ENOMEM; + goto err; + } + config_group_init_type_name(&line->group, name, + &gpio_aggregator_line_type); + error =3D configfs_register_group(&aggr->group, + &line->group); + if (error) + goto err; + scoped_guard(mutex, &aggr->lock) + gpio_aggregator_line_add(aggr, line); + error =3D gpio_aggregator_add_gpio(aggr, key, U16_MAX, &n); if (error) - return error; + goto err; =20 key =3D offsets; continue; @@ -1030,9 +1101,24 @@ static int gpio_aggregator_parse(struct gpio_aggrega= tor *aggr) } =20 for_each_set_bit(i, bitmap, AGGREGATOR_MAX_GPIOS) { + scnprintf(name, sizeof(name), "line%u", n); + line =3D gpio_aggregator_line_alloc(aggr, n, key, i); + if (!line) { + error =3D -ENOMEM; + goto err; + } + config_group_init_type_name(&line->group, name, + &gpio_aggregator_line_type); + error =3D configfs_register_group(&aggr->group, + &line->group); + if (error) + goto err; + scoped_guard(mutex, &aggr->lock) + gpio_aggregator_line_add(aggr, line); + error =3D gpio_aggregator_add_gpio(aggr, key, i, &n); if (error) - return error; + goto err; } =20 args =3D next_arg(args, &key, &p); @@ -1040,15 +1126,20 @@ static int gpio_aggregator_parse(struct gpio_aggreg= ator *aggr) =20 if (!n) { pr_err("No GPIOs specified\n"); - return -EINVAL; + goto err; } =20 return 0; + +err: + gpio_aggregator_free_lines(aggr); + return error; } =20 static ssize_t gpio_aggregator_new_device_store(struct device_driver *driv= er, const char *buf, size_t count) { + char name[CONFIGFS_ITEM_NAME_LEN]; struct gpio_aggregator *aggr; struct platform_device *pdev; int res; @@ -1077,10 +1168,25 @@ static ssize_t gpio_aggregator_new_device_store(str= uct device_driver *driver, goto free_table; } =20 - res =3D gpio_aggregator_parse(aggr); + scnprintf(name, sizeof(name), "%s.%d", AGGREGATOR_LEGACY_PREFIX, aggr->id= ); + config_group_init_type_name(&aggr->group, name, &gpio_aggregator_device_t= ype); + + /* + * Since the device created by sysfs might be toggled via configfs + * 'live' attribute later, this initialization is needed. + */ + dev_sync_probe_init(&aggr->probe_data); + + /* Expose to configfs */ + res =3D configfs_register_group(&gpio_aggregator_subsys.su_group, + &aggr->group); if (res) goto free_dev_id; =20 + res =3D gpio_aggregator_parse(aggr); + if (res) + goto unregister_group; + gpiod_add_lookup_table(aggr->lookups); =20 pdev =3D platform_device_register_simple(DRV_NAME, aggr->id, NULL, 0); @@ -1095,6 +1201,8 @@ static ssize_t gpio_aggregator_new_device_store(struc= t device_driver *driver, =20 remove_table: gpiod_remove_lookup_table(aggr->lookups); +unregister_group: + configfs_unregister_group(&aggr->group); free_dev_id: kfree(aggr->lookups->dev_id); free_table: @@ -1111,7 +1219,13 @@ static struct driver_attribute driver_attr_gpio_aggr= egator_new_device =3D =20 static void gpio_aggregator_destroy(struct gpio_aggregator *aggr) { - gpio_aggregator_deactivate(aggr); + scoped_guard(mutex, &aggr->lock) { + if (gpio_aggregator_is_activating(aggr) || + gpio_aggregator_is_active(aggr)) + gpio_aggregator_deactivate(aggr); + } + gpio_aggregator_free_lines(aggr); + configfs_unregister_group(&aggr->group); kfree(aggr); } =20 --=20 2.45.2 From nobody Wed Dec 17 17:28:51 2025 Received: from smtp-relay-internal-0.canonical.com (smtp-relay-internal-0.canonical.com [185.125.188.122]) (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 81A6A221F21 for ; Mon, 7 Apr 2025 04:31:05 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=185.125.188.122 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1744000267; cv=none; b=VvIQvkpjjE1w/7RUaH+dOYTAITd+vt8/jL5UvuCFMFTo4PpxDqL6QJZ8ZtZvYX38/UOXwKvY1nI0IQ1znyLmpS6JSi3MyY0auqVzjttLmXRvKPRD7y+v4Gna9hoIv3ZdgTgboNTUbLY0X3JvR4Nq+h+SP9cOwUgW+AR69KT7zZo= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1744000267; c=relaxed/simple; bh=5hW1x0h0BI/1kFl0MGB9PENf/p3rDflku5UUTYWUgwg=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=DMZ/O0a9JA6lAcA6lHXB0RcPjOD4gNaVl2CzsdoJuXQx6Y/6FwOG73HJqX5Z8YRA0Y2Ue1YgC5n1lcmfapyRU/Xhgi8Xbf6KAy5ujq6jSdENrpShgZ7YDAaTbx7ZcVFFMZr8bW0bvft8GfwwQnM/i7bpsNgloibO5RvC2VLuv6A= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=canonical.com; spf=pass smtp.mailfrom=canonical.com; dkim=pass (2048-bit key) header.d=canonical.com header.i=@canonical.com header.b=iLwcuMku; arc=none smtp.client-ip=185.125.188.122 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=canonical.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=canonical.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=canonical.com header.i=@canonical.com header.b="iLwcuMku" Received: from mail-pl1-f199.google.com (mail-pl1-f199.google.com [209.85.214.199]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by smtp-relay-internal-0.canonical.com (Postfix) with ESMTPS id 476363F2C2 for ; Mon, 7 Apr 2025 04:31:03 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=canonical.com; s=20210705; t=1744000263; bh=ibKGlo6h6Vsh/r+V4NevFZFzzB1WsTKuG5wixnUQ0uE=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=iLwcuMkuRGmEOtrOs+pPpW7Ow7Gw3T/yG7UDuotDIyCFbd2R4STBmO/Eh4sF3LFyQ CzWqaH6HczmBXFnv47EA/4DIfVeSuBOss7uN7vyQ9AgybxkYqXJsJtf9Sz/JWD7ODc PLBk+jYD9W42hXMOnILksNIijJM7TAGxBLqYj/9MSKIl9pb1SDhE/EqHv9N0VP23N3 wrk7/zHrCDRYmU4HKvWyx4jCNsbuJaPNSId0vEs1lNRtEbh+wg2DPi2OOmrJZ5jpVQ vaWMhFGFGpsvYyPNX4v4gH2v/LBpSNzQb7eZa6c4sFgbzd91Dgc5uRDv87dFl8H31b b/52zchmDgxnw== Received: by mail-pl1-f199.google.com with SMTP id d9443c01a7336-2241ae15dcbso47634785ad.0 for ; Sun, 06 Apr 2025 21:31:03 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1744000262; x=1744605062; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=ibKGlo6h6Vsh/r+V4NevFZFzzB1WsTKuG5wixnUQ0uE=; b=r+ZdYhrpLybjeXvPHrReXAm6DH7AD5nZZZzCmVWgKzGCTTwArlzGeEoepXuoA2gtH8 D3Xlx5/dTHWYMrgcHV+pBRrdF7GGelcWxKM6hSnJFd1J71AS3WFm570qslWOe5N2lNNQ OziyYhXX6Xq/4Ecp4Bx162T4D6UkdE/iIgUxZNQoqDgl4cOoZkAA5B0ZX9pb3A+4yh/k s43NJrviurX+DxnUfnal+e4I9AQfKdcu8aU0cbR7tfLS5MgtW93keIbkrgfLYuXQBSCe UtN+GohzySMkHKzA662BV4xoZrhppXXpwQ64NJ0fohP7BoQ7WNQxw/SUP0rIH/YqkBQ7 d7LA== X-Forwarded-Encrypted: i=1; AJvYcCXVWAgC6QY+wo8Ef2M4lNTwlqIFDWk1Ot4wF+VXuVnvIhCC9RD67/chgZS2etEFFlGaetPN5Vc4c0g09Jc=@vger.kernel.org X-Gm-Message-State: AOJu0Ywxr30PbEUExQQSeroRPrKESJmXcB5qDG3BLjnK3E6i3zZ7q8la ZOrScONPrPzcm2MJzau+I508E4R3kFpzzeQQyHkJXcM0BncuNapSg/+i7jtSpx6Nb9a4jTyEN0e sPlUwrHnAswMsg66fhE0fUXpxZ2OhX1CuJlCmiEl8lOeFkvTP8sMgvcCxia3h5ULkYmeujlFdNb SQDw== X-Gm-Gg: ASbGnctTGCUxzG9ji8N39Cz6NK5x89E5tm+nWj62ezaXmR7oAwaSard7qc4JFJVW65S 4ochj1VHPhqYB99UU5kDJjq0d9oSHjdwy4H+ooJdoSVlHn1Q/MRumIeFKNCkvnKCqVvNgZOpkkJ RayS11/UBjFnEAlbsY5qyn603jheldQiJ60ze0iYceU4ydDKQtZgA7kcNeCZB3X03nwIyFh2nap GGxwQx5vAmuRmTXK0S9SZ0LGLY/qCZ1YeSDPO+nfS5OKvk2ZNaJfzGicw0CYobVlX9OCrTRvGYV SLuNXONH2Mj6lBsitUvi/saq2h3NzKy62A== X-Received: by 2002:a17:902:d50e:b0:21f:85ee:f2df with SMTP id d9443c01a7336-22a8a05524fmr151180775ad.15.1744000261814; Sun, 06 Apr 2025 21:31:01 -0700 (PDT) X-Google-Smtp-Source: AGHT+IH+bu3wJwF3+o72OVyEJz8tYU76qPXcw9JGAY3si2m0Y9uGwevcahFZ2tnrq1WiHt6WtsfzhQ== X-Received: by 2002:a17:902:d50e:b0:21f:85ee:f2df with SMTP id d9443c01a7336-22a8a05524fmr151180585ad.15.1744000261512; Sun, 06 Apr 2025 21:31:01 -0700 (PDT) Received: from localhost.localdomain ([240f:74:7be:1:5985:1f8b:863f:3722]) by smtp.gmail.com with ESMTPSA id d9443c01a7336-22978670dbbsm70839525ad.209.2025.04.06.21.30.59 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sun, 06 Apr 2025 21:31:01 -0700 (PDT) From: Koichiro Den To: linux-gpio@vger.kernel.org Cc: brgl@bgdev.pl, geert+renesas@glider.be, linus.walleij@linaro.org, maciej.borzecki@canonical.com, linux-kernel@vger.kernel.org Subject: [PATCH v7 7/9] gpio: aggregator: cancel deferred probe for devices created via configfs Date: Mon, 7 Apr 2025 13:30:17 +0900 Message-ID: <20250407043019.4105613-8-koichiro.den@canonical.com> X-Mailer: git-send-email 2.45.2 In-Reply-To: <20250407043019.4105613-1-koichiro.den@canonical.com> References: <20250407043019.4105613-1-koichiro.den@canonical.com> 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" For aggregators initialized via configfs, write 1 to 'live' waits for probe completion and returns an error if the probe fails, unlike the legacy sysfs interface, which is asynchronous. Since users control the liveness of the aggregator device and might be editing configurations while 'live' is 0, deferred probing is both unnatural and unsafe. Cancel deferred probe for purely configfs-based aggregators when probe fails. Signed-off-by: Koichiro Den --- drivers/gpio/gpio-aggregator.c | 30 +++++++++++++++++++++++++++--- 1 file changed, 27 insertions(+), 3 deletions(-) diff --git a/drivers/gpio/gpio-aggregator.c b/drivers/gpio/gpio-aggregator.c index bea01ebe8cda..d6010331a4c0 100644 --- a/drivers/gpio/gpio-aggregator.c +++ b/drivers/gpio/gpio-aggregator.c @@ -72,6 +72,10 @@ struct gpio_aggregator_line { enum gpio_lookup_flags flags; }; =20 +struct gpio_aggregator_pdev_meta { + bool init_via_sysfs; +}; + static DEFINE_MUTEX(gpio_aggregator_lock); /* protects idr */ static DEFINE_IDR(gpio_aggregator_idr); =20 @@ -1139,6 +1143,7 @@ static int gpio_aggregator_parse(struct gpio_aggregat= or *aggr) static ssize_t gpio_aggregator_new_device_store(struct device_driver *driv= er, const char *buf, size_t count) { + struct gpio_aggregator_pdev_meta meta =3D { .init_via_sysfs =3D true }; char name[CONFIGFS_ITEM_NAME_LEN]; struct gpio_aggregator *aggr; struct platform_device *pdev; @@ -1189,7 +1194,7 @@ static ssize_t gpio_aggregator_new_device_store(struc= t device_driver *driver, =20 gpiod_add_lookup_table(aggr->lookups); =20 - pdev =3D platform_device_register_simple(DRV_NAME, aggr->id, NULL, 0); + pdev =3D platform_device_register_data(NULL, DRV_NAME, aggr->id, &meta, s= izeof(meta)); if (IS_ERR(pdev)) { res =3D PTR_ERR(pdev); goto remove_table; @@ -1276,14 +1281,15 @@ static struct attribute *gpio_aggregator_attrs[] = =3D { }; ATTRIBUTE_GROUPS(gpio_aggregator); =20 - /* * GPIO Aggregator platform device */ =20 static int gpio_aggregator_probe(struct platform_device *pdev) { + struct gpio_aggregator_pdev_meta *meta; struct device *dev =3D &pdev->dev; + bool init_via_sysfs =3D false; struct gpio_desc **descs; struct gpiochip_fwd *fwd; unsigned long features; @@ -1297,10 +1303,28 @@ static int gpio_aggregator_probe(struct platform_de= vice *pdev) if (!descs) return -ENOMEM; =20 + meta =3D dev_get_platdata(&pdev->dev); + if (meta && meta->init_via_sysfs) + init_via_sysfs =3D true; + for (i =3D 0; i < n; i++) { descs[i] =3D devm_gpiod_get_index(dev, NULL, i, GPIOD_ASIS); - if (IS_ERR(descs[i])) + if (IS_ERR(descs[i])) { + /* + * Deferred probing is not suitable when the aggregator + * is created via configfs. They should just retry later + * whenever they like. For device creation via sysfs, + * error is propagated without overriding for backward + * compatibility. .prevent_deferred_probe is kept unset + * for other cases. + */ + if (!init_via_sysfs && !dev_of_node(dev) && + descs[i] =3D=3D ERR_PTR(-EPROBE_DEFER)) { + pr_warn("Deferred probe canceled for creation via configfs.\n"); + return -ENODEV; + } return PTR_ERR(descs[i]); + } } =20 features =3D (uintptr_t)device_get_match_data(dev); --=20 2.45.2 From nobody Wed Dec 17 17:28:51 2025 Received: from smtp-relay-internal-1.canonical.com (smtp-relay-internal-1.canonical.com [185.125.188.123]) (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 6BAAF2236FF for ; Mon, 7 Apr 2025 04:31:07 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=185.125.188.123 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1744000269; cv=none; b=Yw4Dp8WtQ02zbLf8L27IpLZqFjGynL8HrVPe9tq0ZoLyn7Kgbr1FDmvulrpeeNwdupAR8U9jkmm1bWenQD0ttIhMvfvayE0p0qmtrdNGtRBVaGHsaG4fyG/Jpe9rjB14HL3yIgzkOZfv0nDPu3Rx8fYCN4I5XyfDZkrQNgubSYw= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1744000269; c=relaxed/simple; bh=5z75klJulPBK3euVFo5jCw9QXY35/qZ4NCunDCR/9Jo=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=SDfgsH5ltdX+pO4epscd7XLmg4TNuqupwLwAklxUYf3Ant+Ft8hObcNgasmHzSg7PtYEe3X+0J5ZKk+uDhMQs0fownEsqdyg/vLYy5cKTRk3J91bD/hQiRUV9vwoh48mNL8aTIYLZyfggbos777pJKes54B7ebNPYVOt/n/wgmQ= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=canonical.com; spf=pass smtp.mailfrom=canonical.com; dkim=pass (2048-bit key) header.d=canonical.com header.i=@canonical.com header.b=jWjvI6Rb; arc=none smtp.client-ip=185.125.188.123 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=canonical.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=canonical.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=canonical.com header.i=@canonical.com header.b="jWjvI6Rb" Received: from mail-pl1-f198.google.com (mail-pl1-f198.google.com [209.85.214.198]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by smtp-relay-internal-1.canonical.com (Postfix) with ESMTPS id A2ECB3F181 for ; Mon, 7 Apr 2025 04:31:05 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=canonical.com; s=20210705; t=1744000265; bh=VbN0B6Oenuw6+py3+N6wdefqtqSm9CuUWAW6b8DJ97M=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=jWjvI6RbueLLP7tt7e9kkBC5C/DIf6NbM47Ra31ueuYluJIzQCI5MZurPZ/ZLEbsg YrFh0sXkj2fztmjUUDLuye9xjo3ULMvkKIE9TOB1fxVIOmEVC0jksPTz7sKJ2qu82J ZII1undzNIAhD51yByOcdptH09vTvn6Kni0FGqVG5hFjHLKmhHje4qsbfaBWzPy4hz 2/0KtwE0x/4UQJ+11kWbnQFV6t2sZ322oXOmIZOjNoLvb367qIseTulDHSBGAheeeH C6Y52RsbpDqrxH4IoA/srBtUh30cx4P7qwFTipOTOeI+VPTkFvtQkRz/SmCxPqWvjy TxSa/12mIrNFA== Received: by mail-pl1-f198.google.com with SMTP id d9443c01a7336-22aa75e6653so3323235ad.0 for ; Sun, 06 Apr 2025 21:31:05 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1744000264; x=1744605064; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=VbN0B6Oenuw6+py3+N6wdefqtqSm9CuUWAW6b8DJ97M=; b=q1fpWeDui3MSTS2V0FKhi84OjXoe0s36V/9cTlLnVYHTrld4tp43XIKjMqMSgYXuBu unASxyHuZMMKEL2vVHTI/eWq4TSBkhXtTrM3mEdO+K9gyFAXJCyDgbeGdp5D0G/BSjP+ wul9wHARHseAM6yMvPu+1kihB8IyJy/8ZDWf4WGM+IZZDIVe0r86iczDZD/nWfU8SBhj Xvm18ON597fH8Z4ip361WwTCVLVQenLjaBxL7j1fAo1CaQg8kS9lHDA1EW5rbPyA0e6m 6THkts52G0uoTL0wGpcNrxciMpQdR8xR1FIXd1vGA0/5xndvn2PmHz7CV4gS6/92lQUB hBAg== X-Forwarded-Encrypted: i=1; AJvYcCUfBE16L5fc4U2oiYnuRFNGuRZnXIwtKb8udfzcXBbvfvNhXZQSAkO8eRjXCqEalOKfMDLT4wOINivLzkg=@vger.kernel.org X-Gm-Message-State: AOJu0YwrcnD3MZtO26Gwws69fe56yMmhbEWq4lZL4fCv+rpJuNKFNZB6 Zufu6pJT8JWgDwxkA7IuA3qWHbHOyP0/kp2IvRP4lHU81BnZM9MDker7dqTnbJdRBd+xa6/RG5L be6D4gXN8z+34ZkRP5btdfuStyVbB8l+t07remYa2fQqHBT8f+ifn2iRVNXj80k5GrQt74fltwr 0MIQ== X-Gm-Gg: ASbGncv3pf/EjuuzALGXQZ0ZtJ8avPWZvcZH0mTA7SJfrJiwbRVVsoicikOTaU7Y4cn LKRD28kc+4LrUP2lg1NdhEDzl8V2HrFF1pQEzTuw9YemfZ9ztkRrIYk5yc4dVVfoXOAJyUP7DyQ Rs9VWPM+OZWuI0yzACxxOn1CNdWitlRG2V9gGacYkqxCFlopq+jjDdaIP98R7OCMqAqaDQAKryK ZhoLwubfBd7hVtIA2zSaw2h1IRimuYMyTn8WWeDCRujfEBM+WKpJbFbzmXJ/h3NoFcvY2P6/kf+ J3QNCx6OVwPJyfskuM7c7omBWPV0iiThUQ== X-Received: by 2002:a17:902:ce02:b0:224:160d:3f54 with SMTP id d9443c01a7336-22a8a87949cmr149795555ad.31.1744000264106; Sun, 06 Apr 2025 21:31:04 -0700 (PDT) X-Google-Smtp-Source: AGHT+IFy98Am/whrbbqIZNo+BR5j4Z0JXTKszZD/vODdfyxs6+TUEIaW5R6ZQESYqR01WAMU2ROHLg== X-Received: by 2002:a17:902:ce02:b0:224:160d:3f54 with SMTP id d9443c01a7336-22a8a87949cmr149795415ad.31.1744000263751; Sun, 06 Apr 2025 21:31:03 -0700 (PDT) Received: from localhost.localdomain ([240f:74:7be:1:5985:1f8b:863f:3722]) by smtp.gmail.com with ESMTPSA id d9443c01a7336-22978670dbbsm70839525ad.209.2025.04.06.21.31.01 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sun, 06 Apr 2025 21:31:03 -0700 (PDT) From: Koichiro Den To: linux-gpio@vger.kernel.org Cc: brgl@bgdev.pl, geert+renesas@glider.be, linus.walleij@linaro.org, maciej.borzecki@canonical.com, linux-kernel@vger.kernel.org Subject: [PATCH v7 8/9] Documentation: gpio: document configfs interface for gpio-aggregator Date: Mon, 7 Apr 2025 13:30:18 +0900 Message-ID: <20250407043019.4105613-9-koichiro.den@canonical.com> X-Mailer: git-send-email 2.45.2 In-Reply-To: <20250407043019.4105613-1-koichiro.den@canonical.com> References: <20250407043019.4105613-1-koichiro.den@canonical.com> 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" Add documentation for the newly added configfs-based interface for GPIO aggregator. Signed-off-by: Koichiro Den --- .../admin-guide/gpio/gpio-aggregator.rst | 107 ++++++++++++++++++ 1 file changed, 107 insertions(+) diff --git a/Documentation/admin-guide/gpio/gpio-aggregator.rst b/Documenta= tion/admin-guide/gpio/gpio-aggregator.rst index 5cd1e7221756..8374a9df9105 100644 --- a/Documentation/admin-guide/gpio/gpio-aggregator.rst +++ b/Documentation/admin-guide/gpio/gpio-aggregator.rst @@ -69,6 +69,113 @@ write-only attribute files in sysfs. $ echo gpio-aggregator.0 > delete_device =20 =20 +Aggregating GPIOs using Configfs +-------------------------------- + +**Group:** ``/config/gpio-aggregator`` + + This is the root directory of the gpio-aggregator configfs tree. + +**Group:** ``/config/gpio-aggregator/`` + + This directory represents a GPIO aggregator device. You can assign any + name to ```` (e.g. ``agg0``), except names starting with + ``_sysfs`` prefix, which are reserved for auto-generated configfs + entries corresponding to devices created via Sysfs. + +**Attribute:** ``/config/gpio-aggregator//live`` + + The ``live`` attribute allows to trigger the actual creation of the de= vice + once it's fully configured. Accepted values are: + + * ``1``, ``yes``, ``true`` : enable the virtual device + * ``0``, ``no``, ``false`` : disable the virtual device + +**Attribute:** ``/config/gpio-aggregator//dev_name`` + + The read-only ``dev_name`` attribute exposes the name of the device as= it + will appear in the system on the platform bus (e.g. ``gpio-aggregator.= 0``). + This is useful for identifying a character device for the newly created + aggregator. If it's ``gpio-aggregator.0``, + ``/sys/devices/platform/gpio-aggregator.0/gpiochipX`` path tells you t= hat the + GPIO device id is ``X``. + +You must create subdirectories for each virtual line you want to +instantiate, named exactly as ``line0``, ``line1``, ..., ``lineY``, when +you want to instantiate ``Y+1`` (Y >=3D 0) lines. Configure all lines bef= ore +activating the device by setting ``live`` to 1. + +**Group:** ``/config/gpio-aggregator///`` + + This directory represents a GPIO line to include in the aggregator. + +**Attribute:** ``/config/gpio-aggregator///key`` + +**Attribute:** ``/config/gpio-aggregator///offset`` + + The default values after creating the ```` directory are: + + * ``key`` : + * ``offset`` : -1 + + ``key`` must always be explicitly configured, while ``offset`` depends. + Two configuration patterns exist for each ````: + + (a). For lookup by GPIO line name: + + * Set ``key`` to the line name. + * Ensure ``offset`` remains -1 (the default). + + (b). For lookup by GPIO chip name and the line offset within the chip: + + * Set ``key`` to the chip name. + * Set ``offset`` to the line offset (0 <=3D ``offset`` < 65535). + +**Attribute:** ``/config/gpio-aggregator///name`` + + The ``name`` attribute sets a custom name for lineY. If left unset, the + line will remain unnamed. + +Once the configuration is done, the ``'live'`` attribute must be set to 1 +in order to instantiate the aggregator device. It can be set back to 0 to +destroy the virtual device. The module will synchronously wait for the new +aggregator device to be successfully probed and if this doesn't happen, wr= iting +to ``'live'`` will result in an error. This is a different behaviour from = the +case when you create it using sysfs ``new_device`` interface. + +.. note:: + + For aggregators created via Sysfs, the configfs entries are + auto-generated and appear as ``/config/gpio-aggregator/_sysfs./``. Y= ou + cannot add or remove line directories with mkdir(2)/rmdir(2). To modify + lines, you must use the "delete_device" interface to tear down the + existing device and reconfigure it from scratch. However, you can still + toggle the aggregator with the ``live`` attribute and adjust the + ``key``, ``offset``, and ``name`` attributes for each line when ``live`` + is set to 0 by hand (i.e. it's not waiting for deferred probe). + +Sample configuration commands +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. code-block:: sh + + # Create a directory for an aggregator device + $ mkdir /sys/kernel/config/gpio-aggregator/agg0 + + # Configure each line + $ mkdir /sys/kernel/config/gpio-aggregator/agg0/line0 + $ echo gpiochip0 > /sys/kernel/config/gpio-aggregator/agg0/line0/key + $ echo 6 > /sys/kernel/config/gpio-aggregator/agg0/line0/offset + $ echo test0 > /sys/kernel/config/gpio-aggregator/agg0/line0/name + $ mkdir /sys/kernel/config/gpio-aggregator/agg0/line1 + $ echo gpiochip0 > /sys/kernel/config/gpio-aggregator/agg0/line1/key + $ echo 7 > /sys/kernel/config/gpio-aggregator/agg0/line1/offset + $ echo test1 > /sys/kernel/config/gpio-aggregator/agg0/line1/name + + # Activate the aggregator device + $ echo 1 > /sys/kernel/config/gpio-aggregator/agg0/live + + Generic GPIO Driver ------------------- =20 --=20 2.45.2 From nobody Wed Dec 17 17:28:51 2025 Received: from smtp-relay-internal-1.canonical.com (smtp-relay-internal-1.canonical.com [185.125.188.123]) (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 8B20B22370D for ; Mon, 7 Apr 2025 04:31:10 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=185.125.188.123 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1744000273; cv=none; b=VXkHqjnSjQ2/7E0ocC1YATPsV16jxTo1y49nj/bxv1GK2Kn3tMverQ2191WHSQ/r+ZUdkjF3fAnDIOY2CdJndiNI+ex/KzqQN1nn+YZFJCAfg5mEpJBr6YeijnLXKQ1CrnDuU5GJkcDaBN2Jk81xyQThRBSijARg4xOaquVz/Fw= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1744000273; c=relaxed/simple; bh=vrVAGSgxNANrEDlAUI4hvwiL+VnhDEgL6Y/zsKBMygU=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=iXTEED/a03MmHp0pTzxtwV7hJH83kkJrcYBXmkzW9tSyKm7z+P9mu62PPkDs1x4USZAQ+a2G6vjwwzWfjYp9zQXM0GlVTuOac1aCuXzgP2GDHIw9n3QxxnnN6MssjZ2hE7KWABkbww/ZDuLeC0Lr85oUdeqj4zIHAvumhpFtK8U= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=canonical.com; spf=pass smtp.mailfrom=canonical.com; dkim=pass (2048-bit key) header.d=canonical.com header.i=@canonical.com header.b=p7x+iyIq; arc=none smtp.client-ip=185.125.188.123 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=canonical.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=canonical.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=canonical.com header.i=@canonical.com header.b="p7x+iyIq" Received: from mail-pj1-f69.google.com (mail-pj1-f69.google.com [209.85.216.69]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by smtp-relay-internal-1.canonical.com (Postfix) with ESMTPS id 6D1A23F0F8 for ; Mon, 7 Apr 2025 04:31:08 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=canonical.com; s=20210705; t=1744000268; bh=unE8r5bz7y+LtS0Ck6ChgHm8lSL9zhWWp92j38DBeMA=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=p7x+iyIqNcJc+ic60JggXLbKFS8uFulC+FkHcVyExX0BGR/NfX8gLZW2ORVtlWqIn LaBP/OZ9NNJQJmtmDO+lLynAjnBmEEtzTT3sXVTeAfQVJ3oTt9CAFxuTQd+ibuKVHy dUGl3iB9kFka+mhFQ7neu2ece0IZeSqjxnSfjxmSbzOCONV0dojPxkeStRFx/bGUqH TGXSlNS0v4rSwdxhogg1iKNwFcEEepo8Cu9Z47pQnLQcxZrpGJwPucl3ndbA4wxWKV 1sCCTA6vAnSUM7Ma8W3FsKFbfWIkmMWcR/Zzmj+dwUMe8seI/2Pp5GB9YXIEyvqnlR jbSZiRJB2R5rg== Received: by mail-pj1-f69.google.com with SMTP id 98e67ed59e1d1-2ff8119b436so3451962a91.0 for ; Sun, 06 Apr 2025 21:31:08 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1744000267; x=1744605067; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=unE8r5bz7y+LtS0Ck6ChgHm8lSL9zhWWp92j38DBeMA=; b=ABJZYoxABXCr6kTkuZzyuhj+ktay3ueKxYbbk0CifaQ2emhz441n8luWNjfz12IQOq 8nMMHyIdodLsy6HR+4fKGWJ4AzQVmSCNghn/MT1Kvtu8gpK6S8s2vfbaWZv1Hx8WxE0r 5/bmNdTp/OgPtu1SKC6bO4JXT3+h8JM5x/45zko0mlAthSRKwOYnbAaJRgcXhtRxfMqR czUeFA4SxzukST8+7yzw3KxKSrZyLr5Xa72Sg1Wo1SigYwiWj7x6lco+hmQFVQTLKjWJ knPsRBrOZoZr66DZ0llFsxHxYZx4iQVrPrvohZEmoPp20DKqUObdHTy81B4QlpQ3v8u4 +p6g== X-Forwarded-Encrypted: i=1; AJvYcCVPLU+FIGC7mRbEJ3zRtDSfJ00gzR9DD5HEeQpjzk6bkdbU0MboKXsR+DmeCMYpGovN/wa766NsMEl4Ei8=@vger.kernel.org X-Gm-Message-State: AOJu0YyioOQiWPzsvUDIEKJWQ1XigG6ATCynQZEfpvC2d3roViz+yw2u YvB0SMivXOyLvsVNyXBx4nWX9X3dCPt27L6/bbYqUKhRr4To1v3PP6PE7kDecskhcys50afQ4/B KgvqASy3Iwhlw2jR+MgzXySHLLmiuDYbOHFTM5vuCcmumUYRPEECUU1O57E1djwSjCY43/dwCVI eWyg== X-Gm-Gg: ASbGnctCD1VtMqk31JgmAlZVbXS1biyT1eJKNHLjcjZojuZ75RsF3mYLQIR4Q/8GPEV tqwEoa7EBDsId/f23GFx0cSc5ZG3PRhIila20TjcTql9Fnb1hWBc5RvydEHEIqlpPUz88m3A6N+ 874g4syfgkhkFzGc8/rFdPHTp2wOfvOe4lMDJQLGs/f0nscaJmT6bEVtkZad/6LhBJ9ZY1Jr/Eg L6B+OZeZ8yv3PK/mEtWANkdSGzuXdAg4Kz5pD4kMv7qk+O1JIoUMWR6e7a/pZEjjWFHAaHM61q/ BaiTu16AUCX2XI1eqNGIhQHEacl/nJFXSA== X-Received: by 2002:a17:90b:1f8f:b0:2ee:44ec:e524 with SMTP id 98e67ed59e1d1-306a624e6acmr15655819a91.35.1744000266625; Sun, 06 Apr 2025 21:31:06 -0700 (PDT) X-Google-Smtp-Source: AGHT+IHEgs0KnqOjxukQ18dmsRcRvwFdAw1k9aXdiRO9fMIMKFZdu9Y6ZVlXHlZw4+dMT8bdYxevYQ== X-Received: by 2002:a17:90b:1f8f:b0:2ee:44ec:e524 with SMTP id 98e67ed59e1d1-306a624e6acmr15655787a91.35.1744000265952; Sun, 06 Apr 2025 21:31:05 -0700 (PDT) Received: from localhost.localdomain ([240f:74:7be:1:5985:1f8b:863f:3722]) by smtp.gmail.com with ESMTPSA id d9443c01a7336-22978670dbbsm70839525ad.209.2025.04.06.21.31.04 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sun, 06 Apr 2025 21:31:05 -0700 (PDT) From: Koichiro Den To: linux-gpio@vger.kernel.org Cc: brgl@bgdev.pl, geert+renesas@glider.be, linus.walleij@linaro.org, maciej.borzecki@canonical.com, linux-kernel@vger.kernel.org Subject: [PATCH v7 9/9] selftests: gpio: add test cases for gpio-aggregator Date: Mon, 7 Apr 2025 13:30:19 +0900 Message-ID: <20250407043019.4105613-10-koichiro.den@canonical.com> X-Mailer: git-send-email 2.45.2 In-Reply-To: <20250407043019.4105613-1-koichiro.den@canonical.com> References: <20250407043019.4105613-1-koichiro.den@canonical.com> 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" Add a set of tests for gpio-aggregator module. This test covers both pre-existing new_device/delete_device interface and new configfs-based interface. Signed-off-by: Koichiro Den --- tools/testing/selftests/gpio/Makefile | 2 +- tools/testing/selftests/gpio/config | 1 + .../testing/selftests/gpio/gpio-aggregator.sh | 723 ++++++++++++++++++ 3 files changed, 725 insertions(+), 1 deletion(-) create mode 100755 tools/testing/selftests/gpio/gpio-aggregator.sh diff --git a/tools/testing/selftests/gpio/Makefile b/tools/testing/selftest= s/gpio/Makefile index e0884390447d..7bfe315f7001 100644 --- a/tools/testing/selftests/gpio/Makefile +++ b/tools/testing/selftests/gpio/Makefile @@ -1,6 +1,6 @@ # SPDX-License-Identifier: GPL-2.0 =20 -TEST_PROGS :=3D gpio-mockup.sh gpio-sim.sh +TEST_PROGS :=3D gpio-mockup.sh gpio-sim.sh gpio-aggregator.sh TEST_FILES :=3D gpio-mockup-sysfs.sh TEST_GEN_PROGS_EXTENDED :=3D gpio-mockup-cdev gpio-chip-info gpio-line-name CFLAGS +=3D -O2 -g -Wall $(KHDR_INCLUDES) diff --git a/tools/testing/selftests/gpio/config b/tools/testing/selftests/= gpio/config index 409a8532facc..1287abeaac7e 100644 --- a/tools/testing/selftests/gpio/config +++ b/tools/testing/selftests/gpio/config @@ -2,3 +2,4 @@ CONFIG_GPIOLIB=3Dy CONFIG_GPIO_CDEV=3Dy CONFIG_GPIO_MOCKUP=3Dm CONFIG_GPIO_SIM=3Dm +CONFIG_GPIO_AGGREGATOR=3Dm diff --git a/tools/testing/selftests/gpio/gpio-aggregator.sh b/tools/testin= g/selftests/gpio/gpio-aggregator.sh new file mode 100755 index 000000000000..f1bab62c4cd2 --- /dev/null +++ b/tools/testing/selftests/gpio/gpio-aggregator.sh @@ -0,0 +1,723 @@ +#!/bin/sh +# SPDX-License-Identifier: GPL-2.0 +# Copyright (C) 2025 Bartosz Golaszewski +# Copyright (C) 2025 Koichiro Den + +BASE_DIR=3D$(dirname "$0") +CONFIGFS_SIM_DIR=3D"/sys/kernel/config/gpio-sim" +CONFIGFS_AGG_DIR=3D"/sys/kernel/config/gpio-aggregator" +SYSFS_AGG_DIR=3D"/sys/bus/platform/drivers/gpio-aggregator" +MODULE=3D"gpio-aggregator" + +fail() { + echo "$*" >&2 + echo "GPIO $MODULE test FAIL" + exit 1 +} + +skip() { + echo "$*" >&2 + echo "GPIO $MODULE test SKIP" + exit 4 +} + +# gpio-sim +sim_enable_chip() { + local CHIP=3D$1 + + echo 1 > "$CONFIGFS_SIM_DIR/$CHIP/live" || fail "Unable to enable the chi= p" +} + +sim_disable_chip() { + local CHIP=3D$1 + + echo 0 > "$CONFIGFS_SIM_DIR/$CHIP/live" || fail "Unable to disable the ch= ip" +} + +sim_configfs_cleanup() { + local NOCHECK=3D${1:-0} + + for CHIP_DIR in "$CONFIGFS_SIM_DIR"/*; do + [ -d "$CHIP_DIR" ] || continue + echo 0 > "$CHIP_DIR/live" + find "$CHIP_DIR" -depth -type d -exec rmdir {} \; + done + [ "$NOCHECK" -eq 1 ] && return; + remaining=3D$(find "$CONFIGFS_SIM_DIR" -mindepth 1 -type d 2> /dev/null) + if [ -n "$remaining" ]; then + fail "Directories remain in $CONFIGFS_SIM_DIR: $remaining" + fi +} + +sim_get_chip_label() { + local CHIP=3D$1 + local BANK=3D$2 + local CHIP_NAME=3D$(cat "$CONFIGFS_SIM_DIR/$CHIP/$BANK/chip_name" 2> /dev= /null) || \ + fail "Unable to read the chip name from configfs" + + $BASE_DIR/gpio-chip-info "/dev/$CHIP_NAME" label || \ + fail "Unable to read the chip label from the character device" +} + +# gpio-aggregator +agg_create_chip() { + local CHIP=3D$1 + + mkdir "$CONFIGFS_AGG_DIR/$CHIP" +} + +agg_remove_chip() { + local CHIP=3D$1 + + find "$CONFIGFS_AGG_DIR/$CHIP/" -depth -type d -exec rmdir {} \; || \ + fail "Unable to remove $CONFIGFS_AGG_DIR/$CHIP" +} + +agg_create_line() { + local CHIP=3D$1 + local LINE=3D$2 + + mkdir "$CONFIGFS_AGG_DIR/$CHIP/$LINE" +} + +agg_remove_line() { + local CHIP=3D$1 + local LINE=3D$2 + + rmdir "$CONFIGFS_AGG_DIR/$CHIP/$LINE" +} + +agg_set_key() { + local CHIP=3D$1 + local LINE=3D$2 + local KEY=3D$3 + + echo "$KEY" > "$CONFIGFS_AGG_DIR/$CHIP/$LINE/key" || fail "Unable to set = the lookup key" +} + +agg_set_offset() { + local CHIP=3D$1 + local LINE=3D$2 + local OFFSET=3D$3 + + echo "$OFFSET" > "$CONFIGFS_AGG_DIR/$CHIP/$LINE/offset" || \ + fail "Unable to set the lookup offset" +} + +agg_set_line_name() { + local CHIP=3D$1 + local LINE=3D$2 + local NAME=3D$3 + + echo "$NAME" > "$CONFIGFS_AGG_DIR/$CHIP/$LINE/name" || fail "Unable to se= t the line name" +} + +agg_enable_chip() { + local CHIP=3D$1 + + echo 1 > "$CONFIGFS_AGG_DIR/$CHIP/live" || fail "Unable to enable the chi= p" +} + +agg_disable_chip() { + local CHIP=3D$1 + + echo 0 > "$CONFIGFS_AGG_DIR/$CHIP/live" || fail "Unable to disable the ch= ip" +} + +agg_configfs_cleanup() { + local NOCHECK=3D${1:-0} + + for CHIP_DIR in "$CONFIGFS_AGG_DIR"/*; do + [ -d "$CHIP_DIR" ] || continue + echo 0 > "$CHIP_DIR/live" 2> /dev/null + find "$CHIP_DIR" -depth -type d -exec rmdir {} \; + done + [ "$NOCHECK" -eq 1 ] && return; + remaining=3D$(find "$CONFIGFS_AGG_DIR" -mindepth 1 -type d 2> /dev/null) + if [ -n "$remaining" ]; then + fail "Directories remain in $CONFIGFS_AGG_DIR: $remaining" + fi +} + +agg_configfs_dev_name() { + local CHIP=3D$1 + + cat "$CONFIGFS_AGG_DIR/$CHIP/dev_name" 2> /dev/null || \ + fail "Unable to read the device name from configfs" +} + +agg_configfs_chip_name() { + local CHIP=3D$1 + local DEV_NAME=3D$(agg_configfs_dev_name "$CHIP") + local CHIP_LIST=3D$(find "/sys/devices/platform/$DEV_NAME" \ + -maxdepth 1 -type d -name "gpiochip[0-9]*" 2> /dev/null) + local CHIP_COUNT=3D$(echo "$CHIP_LIST" | wc -l) + + if [ -z "$CHIP_LIST" ]; then + fail "No gpiochip in /sys/devices/platform/$DEV_NAME/" + elif [ "$CHIP_COUNT" -ne 1 ]; then + fail "Multiple gpiochips unexpectedly found: $CHIP_LIST" + fi + basename "$CHIP_LIST" +} + +agg_get_chip_num_lines() { + local CHIP=3D$1 + local N_DIR=3D$(ls -d $CONFIGFS_AGG_DIR/$CHIP/line[0-9]* 2> /dev/null | w= c -l) + local N_LINES + + if [ "$(cat $CONFIGFS_AGG_DIR/$CHIP/live)" =3D 0 ]; then + echo "$N_DIR" + else + N_LINES=3D$( + $BASE_DIR/gpio-chip-info \ + "/dev/$(agg_configfs_chip_name "$CHIP")" num-lines + ) || fail "Unable to read the number of lines from the character device" + if [ $N_DIR !=3D $N_LINES ]; then + fail "Discrepancy between two sources for the number of lines" + fi + echo "$N_LINES" + fi +} + +agg_get_chip_label() { + local CHIP=3D$1 + + $BASE_DIR/gpio-chip-info "/dev/$(agg_configfs_chip_name "$CHIP")" label |= | \ + fail "Unable to read the chip label from the character device" +} + +agg_get_line_name() { + local CHIP=3D$1 + local OFFSET=3D$2 + local NAME_CONFIGFS=3D$(cat "$CONFIGFS_AGG_DIR/$CHIP/line${OFFSET}/name") + local NAME_CDEV + + if [ "$(cat "$CONFIGFS_AGG_DIR/$CHIP/live")" =3D 0 ]; then + echo "$NAME_CONFIGFS" + else + NAME_CDEV=3D$( + $BASE_DIR/gpio-line-name \ + "/dev/$(agg_configfs_chip_name "$CHIP")" "$OFFSET" + ) || fail "Unable to read the line name from the character device" + if [ "$NAME_CONFIGFS" !=3D "$NAME_CDEV" ]; then + fail "Discrepancy between two sources for the name of line" + fi + echo "$NAME_CDEV" + fi +} + + +# Load the modules. This will pull in configfs if needed too. +modprobe gpio-sim || skip "unable to load the gpio-sim module" +modprobe gpio-aggregator || skip "unable to load the gpio-aggregator modul= e" + +# Make sure configfs is mounted at /sys/kernel/config. Wait a bit if neede= d. +for IDX in $(seq 5); do + if [ "$IDX" -eq "5" ]; then + skip "configfs not mounted at /sys/kernel/config" + fi + + mountpoint -q /sys/kernel/config && break + sleep 0.1 +done + +# If the module was already loaded: remove all previous chips +agg_configfs_cleanup +sim_configfs_cleanup + +trap "exit 1" SIGTERM SIGINT +trap "agg_configfs_cleanup 1; sim_configfs_cleanup 1" EXIT + +# Use gpio-sim chips as the test backend +for CHIP in $(seq -f "chip%g" 0 1); do + mkdir $CONFIGFS_SIM_DIR/$CHIP + for BANK in $(seq -f "bank%g" 0 1); do + mkdir -p "$CONFIGFS_SIM_DIR/$CHIP/$BANK" + echo "${CHIP}_${BANK}" > "$CONFIGFS_SIM_DIR/$CHIP/$BANK/label" || \ + fail "unable to set the chip label" + echo 16 > "$CONFIGFS_SIM_DIR/$CHIP/$BANK/num_lines" || \ + fail "unable to set the number of lines" + for IDX in $(seq 0 15); do + LINE_NAME=3D"${CHIP}${BANK}_${IDX}" + LINE_DIR=3D"$CONFIGFS_SIM_DIR/$CHIP/$BANK/line$IDX" + mkdir -p $LINE_DIR + echo "$LINE_NAME" > "$LINE_DIR/name" || fail "unable to set the line na= me" + done + done + sim_enable_chip "$CHIP" +done + +echo "1. GPIO aggregator creation/deletion" + +echo "1.1. Creation/deletion via configfs" + +echo "1.1.1. Minimum creation/deletion" +agg_create_chip agg0 +agg_create_line agg0 line0 +agg_set_key agg0 line0 "$(sim_get_chip_label chip0 bank0)" +agg_set_offset agg0 line0 5 +agg_set_line_name agg0 line0 test0 +agg_enable_chip agg0 +test "$(cat "$CONFIGFS_AGG_DIR/agg0/live")" =3D 1 || fail "chip unexpected= ly dead" +test "$(agg_get_chip_label agg0)" =3D "$(agg_configfs_dev_name agg0)" || \ + fail "label is inconsistent" +test "$(agg_get_chip_num_lines agg0)" =3D "1" || fail "number of lines is = not 1" +test "$(agg_get_line_name agg0 0)" =3D "test0" || fail "line name is unset" +agg_disable_chip agg0 +agg_remove_line agg0 line0 +agg_remove_chip agg0 + +echo "1.1.2. Complex creation/deletion" +agg_create_chip agg0 +agg_create_line agg0 line0 +agg_create_line agg0 line1 +agg_create_line agg0 line2 +agg_create_line agg0 line3 +agg_set_key agg0 line0 "$(sim_get_chip_label chip0 bank0)" +agg_set_key agg0 line1 "$(sim_get_chip_label chip0 bank1)" +agg_set_key agg0 line2 "$(sim_get_chip_label chip1 bank0)" +agg_set_key agg0 line3 "$(sim_get_chip_label chip1 bank1)" +agg_set_offset agg0 line0 1 +agg_set_offset agg0 line1 3 +agg_set_offset agg0 line2 5 +agg_set_offset agg0 line3 7 +agg_set_line_name agg0 line0 test0 +agg_set_line_name agg0 line1 test1 +agg_set_line_name agg0 line2 test2 +agg_set_line_name agg0 line3 test3 +agg_enable_chip agg0 +test "$(cat "$CONFIGFS_AGG_DIR/agg0/live")" =3D 1 || fail "chip unexpected= ly dead" +test "$(agg_get_chip_label agg0)" =3D "$(agg_configfs_dev_name agg0)" || \ + fail "label is inconsistent" +test "$(agg_get_chip_num_lines agg0)" =3D "4" || fail "number of lines is = not 1" +test "$(agg_get_line_name agg0 0)" =3D "test0" || fail "line name is unset" +test "$(agg_get_line_name agg0 1)" =3D "test1" || fail "line name is unset" +test "$(agg_get_line_name agg0 2)" =3D "test2" || fail "line name is unset" +test "$(agg_get_line_name agg0 3)" =3D "test3" || fail "line name is unset" +agg_disable_chip agg0 +agg_remove_line agg0 line0 +agg_remove_line agg0 line1 +agg_remove_line agg0 line2 +agg_remove_line agg0 line3 +agg_remove_chip agg0 + +echo "1.1.3. Can't instantiate a chip without any line" +agg_create_chip agg0 +echo 1 > "$CONFIGFS_AGG_DIR/agg0/live" 2> /dev/null && fail "chip unexpect= edly enabled" +test "$(cat "$CONFIGFS_AGG_DIR/agg0/live")" =3D 0 || fail "chip unexpected= ly alive" +agg_remove_chip agg0 + +echo "1.1.4. Can't instantiate a chip with invalid configuration" +agg_create_chip agg0 +agg_create_line agg0 line0 +agg_set_key agg0 line0 "chipX_bankX" +agg_set_offset agg0 line0 99 +agg_set_line_name agg0 line0 test0 +echo 1 > "$CONFIGFS_AGG_DIR/agg0/live" 2> /dev/null && fail "chip unexpect= edly enabled" +test "$(cat "$CONFIGFS_AGG_DIR/agg0/live")" =3D 0 || fail "chip unexpected= ly alive" +agg_remove_line agg0 line0 +agg_remove_chip agg0 + +echo "1.1.5. Can't instantiate a chip asynchronously via deferred probe" +agg_create_chip agg0 +agg_create_line agg0 line0 +agg_set_key agg0 line0 "chip0_bank0" +agg_set_offset agg0 line0 5 +agg_set_line_name agg0 line0 test0 +sim_disable_chip chip0 +echo 1 > "$CONFIGFS_AGG_DIR/agg0/live" 2> /dev/null && fail "chip unexpect= edly enabled" +test "$(cat "$CONFIGFS_AGG_DIR/agg0/live")" =3D 0 || fail "chip unexpected= ly alive" +sim_enable_chip chip0 +sleep 1 +test "$(cat "$CONFIGFS_AGG_DIR/agg0/live")" =3D 0 || \ + fail "chip unexpectedly transitioned to 'live' state" +agg_remove_line agg0 line0 +agg_remove_chip agg0 + +echo "1.2. Creation/deletion via sysfs" + +echo "1.2.1. Minimum creation/deletion" +echo "chip0_bank0 0" > "$SYSFS_AGG_DIR/new_device" +CHIPNAME=3D$(agg_configfs_chip_name _sysfs.0) +test "$(cat "$CONFIGFS_AGG_DIR/_sysfs.0/live")" =3D 1 || fail "chip unexpe= ctedly dead" +test "$(agg_get_chip_label _sysfs.0)" =3D "$(agg_configfs_dev_name _sysfs.= 0)" || \ + fail "label is inconsistent" +test "$(agg_get_chip_num_lines _sysfs.0)" =3D "1" || fail "number of lines= is not 1" +test "$(agg_get_line_name _sysfs.0 0)" =3D "" || fail "line name is unset" +echo "$(agg_configfs_dev_name _sysfs.0)" > "$SYSFS_AGG_DIR/delete_device" +test -d $CONFIGFS_AGG_DIR/_sysfs.0 && fail "_sysfs.0 unexpectedly remains" +test -d /dev/${CHIPNAME} && fail "/dev/${CHIPNAME} unexpectedly remains" + +echo "1.2.2. Complex creation/deletion" +echo "chip0bank0_0 chip1_bank1 10-11" > "$SYSFS_AGG_DIR/new_device" +CHIPNAME=3D$(agg_configfs_chip_name _sysfs.0) +test "$(cat "$CONFIGFS_AGG_DIR/_sysfs.0/live")" =3D 1 || fail "chip unexpe= ctedly dead" +test "$(agg_get_chip_label _sysfs.0)" =3D "$(agg_configfs_dev_name _sysfs.= 0)" || \ + fail "label is inconsistent" +test "$(agg_get_chip_num_lines _sysfs.0)" =3D "3" || fail "number of lines= is not 3" +test "$(agg_get_line_name _sysfs.0 0)" =3D "" || fail "line name is unset" +test "$(agg_get_line_name _sysfs.0 1)" =3D "" || fail "line name is unset" +test "$(agg_get_line_name _sysfs.0 2)" =3D "" || fail "line name is unset" +echo "$(agg_configfs_dev_name _sysfs.0)" > "$SYSFS_AGG_DIR/delete_device" +test -d $CONFIGFS_AGG_DIR/_sysfs.0 && fail "_sysfs.0 unexpectedly remains" +test -d /dev/${CHIPNAME} && fail "/dev/${CHIPNAME} unexpectedly remains" + +echo "1.2.3. Asynchronous creation with deferred probe" +sim_disable_chip chip0 +echo 'chip0_bank0 0' > $SYSFS_AGG_DIR/new_device +sleep 1 +test "$(cat "$CONFIGFS_AGG_DIR/_sysfs.0/live")" =3D 0 || fail "chip unexpe= ctedly alive" +sim_enable_chip chip0 +sleep 1 +CHIPNAME=3D$(agg_configfs_chip_name _sysfs.0) +test "$(cat "$CONFIGFS_AGG_DIR/_sysfs.0/live")" =3D 1 || fail "chip unexpe= ctedly remains dead" +test "$(agg_get_chip_label _sysfs.0)" =3D "$(agg_configfs_dev_name _sysfs.= 0)" || \ + fail "label is inconsistent" +test "$(agg_get_chip_num_lines _sysfs.0)" =3D "1" || fail "number of lines= is not 1" +test "$(agg_get_line_name _sysfs.0 0)" =3D "" || fail "line name unexpecte= dly set" +echo "$(agg_configfs_dev_name _sysfs.0)" > "$SYSFS_AGG_DIR/delete_device" +test -d $CONFIGFS_AGG_DIR/_sysfs.0 && fail "_sysfs.0 unexpectedly remains" +test -d /dev/${CHIPNAME} && fail "/dev/${CHIPNAME} unexpectedly remains" + +echo "1.2.4. Can't instantiate a chip with invalid configuration" +echo "xyz 0" > "$SYSFS_AGG_DIR/new_device" +test "$(cat $CONFIGFS_AGG_DIR/_sysfs.0/live)" =3D 0 || fail "chip unexpect= edly alive" +echo "$(agg_configfs_dev_name _sysfs.0)" > "$SYSFS_AGG_DIR/delete_device" + +echo "2. GPIO aggregator configuration" + +echo "2.1. Configuring aggregators instantiated via configfs" +setup_2_1() { + agg_create_chip agg0 + agg_create_line agg0 line0 + agg_create_line agg0 line1 + agg_set_key agg0 line0 "$(sim_get_chip_label chip0 bank0)" + agg_set_key agg0 line1 "$(sim_get_chip_label chip1 bank0)" + agg_set_offset agg0 line0 1 + agg_set_offset agg0 line1 3 + agg_set_line_name agg0 line0 test0 + agg_set_line_name agg0 line1 test1 + agg_enable_chip agg0 +} +teardown_2_1() { + agg_configfs_cleanup +} + +echo "2.1.1. While offline" + +echo "2.1.1.1. Line can be added/removed" +setup_2_1 +agg_disable_chip agg0 +agg_create_line agg0 line2 +agg_set_key agg0 line2 "$(sim_get_chip_label chip0 bank1)" +agg_set_offset agg0 line2 5 +agg_enable_chip agg0 +test "$(agg_get_chip_num_lines agg0)" =3D "3" || fail "number of lines is = not 1" +teardown_2_1 + +echo "2.1.1.2. Line key can be modified" +setup_2_1 +agg_disable_chip agg0 +agg_set_key agg0 line0 "$(sim_get_chip_label chip0 bank1)" +agg_set_key agg0 line1 "$(sim_get_chip_label chip1 bank1)" +agg_enable_chip agg0 +teardown_2_1 + +echo "2.1.1.3. Line name can be modified" +setup_2_1 +agg_disable_chip agg0 +agg_set_line_name agg0 line0 new0 +agg_set_line_name agg0 line1 new1 +agg_enable_chip agg0 +test "$(agg_get_line_name agg0 0)" =3D "new0" || fail "line name is unset" +test "$(agg_get_line_name agg0 1)" =3D "new1" || fail "line name is unset" +teardown_2_1 + +echo "2.1.1.4. Line offset can be modified" +setup_2_1 +agg_disable_chip agg0 +agg_set_offset agg0 line0 5 +agg_set_offset agg0 line1 7 +agg_enable_chip agg0 +teardown_2_1 + +echo "2.1.1.5. Can re-enable a chip after valid reconfiguration" +setup_2_1 +agg_disable_chip agg0 +agg_set_key agg0 line0 "$(sim_get_chip_label chip1 bank1)" +agg_set_offset agg0 line0 15 +agg_set_key agg0 line1 "$(sim_get_chip_label chip0 bank1)" +agg_set_offset agg0 line0 14 +agg_create_line agg0 line2 +agg_set_key agg0 line2 "$(sim_get_chip_label chip0 bank1)" +agg_set_offset agg0 line2 13 +agg_enable_chip agg0 +test "$(agg_get_chip_num_lines agg0)" =3D "3" || fail "number of lines is = not 1" +teardown_2_1 + +echo "2.1.1.7. Can't re-enable a chip with invalid reconfiguration" +setup_2_1 +agg_disable_chip agg0 +agg_set_key agg0 line0 invalidkey +echo 1 > "$CONFIGFS_AGG_DIR/agg0/live" 2> /dev/null && fail "chip unexpect= edly enabled" +teardown_2_1 +setup_2_1 +agg_disable_chip agg0 +agg_set_offset agg0 line0 99 +echo 1 > "$CONFIGFS_AGG_DIR/agg0/live" 2> /dev/null && fail "chip unexpect= edly enabled" +teardown_2_1 + +echo "2.1.2. While online" + +echo "2.1.2.1. Can't add/remove line" +setup_2_1 +mkdir "$CONFIGFS_AGG_DIR/agg0/line2" 2> /dev/null && fail "line unexpected= ly added" +rmdir "$CONFIGFS_AGG_DIR/agg0/line1" 2> /dev/null && fail "line unexpected= ly removed" +teardown_2_1 + +echo "2.1.2.2. Can't modify line key" +setup_2_1 +echo "chip1_bank1" > "$CONFIGFS_AGG_DIR/agg0/line0/key" 2> /dev/null && \ + fail "lookup key unexpectedly updated" +teardown_2_1 + +echo "2.1.2.3. Can't modify line name" +setup_2_1 +echo "new0" > "$CONFIGFS_AGG_DIR/agg0/line0/name" 2> /dev/null && \ + fail "name unexpectedly updated" +teardown_2_1 + +echo "2.1.2.4. Can't modify line offset" +setup_2_1 +echo "5" > "$CONFIGFS_AGG_DIR/agg0/line0/offset" 2> /dev/null && \ + fail "offset unexpectedly updated" +teardown_2_1 + +echo "2.2. Configuring aggregators instantiated via sysfs" +setup_2_2() { + echo "chip0_bank0 1 chip1_bank0 3" > "$SYSFS_AGG_DIR/new_device" +} +teardown_2_2() { + echo "$(agg_configfs_dev_name _sysfs.0)" > "$SYSFS_AGG_DIR/delete_device" +} + +echo "2.2.1. While online" + +echo "2.2.1.1. Can toggle live" +setup_2_2 +agg_disable_chip _sysfs.0 +agg_enable_chip _sysfs.0 +teardown_2_2 + +echo "2.2.1.2. Can't add/remove line" +setup_2_2 +mkdir "$CONFIGFS_AGG_DIR/_sysfs.0/line2" 2> /dev/null && fail "line unexpe= ctedly added" +rmdir "$CONFIGFS_AGG_DIR/_sysfs.0/line1" 2> /dev/null && fail "line unexpe= ctedly removed" +teardown_2_2 + +echo "2.2.1.3. Can't modify line key" +setup_2_2 +echo "chip1_bank1" > "$CONFIGFS_AGG_DIR/_sysfs.0/line0/key" 2> /dev/null &= & \ + fail "lookup key unexpectedly updated" +teardown_2_2 + +echo "2.2.1.4. Can't modify line name" +setup_2_2 +echo "new0" > "$CONFIGFS_AGG_DIR/_sysfs.0/line0/name" 2> /dev/null && \ + fail "name unexpectedly updated" +teardown_2_2 + +echo "2.2.1.5. Can't modify line offset" +setup_2_2 +echo "5" > "$CONFIGFS_AGG_DIR/_sysfs.0/line0/offset" 2> /dev/null && \ + fail "offset unexpectedly updated" +teardown_2_2 + +echo "2.2.2. While waiting for deferred probe" + +echo "2.2.2.1. Can't add/remove line despite live =3D 0" +sim_disable_chip chip0 +setup_2_2 +mkdir "$CONFIGFS_AGG_DIR/_sysfs.0/line2" 2> /dev/null && fail "line unexpe= ctedly added" +rmdir "$CONFIGFS_AGG_DIR/_sysfs.0/line1" 2> /dev/null && fail "line unexpe= ctedly removed" +teardown_2_2 +sim_enable_chip chip0 + +echo "2.2.2.2. Can't modify line key" +sim_disable_chip chip0 +setup_2_2 +echo "chip1_bank1" > "$CONFIGFS_AGG_DIR/_sysfs.0/line0/key" 2> /dev/null &= & \ + fail "lookup key unexpectedly updated" +teardown_2_2 +sim_enable_chip chip0 + +echo "2.2.2.3. Can't modify line name" +sim_disable_chip chip0 +setup_2_2 +echo "new0" > "$CONFIGFS_AGG_DIR/_sysfs.0/line0/name" 2> /dev/null && \ + fail "name unexpectedly updated" +teardown_2_2 +sim_enable_chip chip0 + +echo "2.2.2.4. Can't modify line offset" +sim_disable_chip chip0 +setup_2_2 +echo 5 > "$CONFIGFS_AGG_DIR/_sysfs.0/line0/offset" 2> /dev/null && \ + fail "offset unexpectedly updated" +teardown_2_2 +sim_enable_chip chip0 + +echo "2.2.2.5. Can't toggle live" +sim_disable_chip chip0 +setup_2_2 +test "$(cat "$CONFIGFS_AGG_DIR/_sysfs.0/live")" =3D 0 || fail "chip unexpe= ctedly alive" +echo 1 > "$CONFIGFS_AGG_DIR/_sysfs.0/live" 2> /dev/null && fail "chip unex= pectedly enabled" +teardown_2_2 +sim_enable_chip chip0 + +echo "2.2.3. While offline" + +echo "2.2.3.1. Can't add/remove line despite live =3D 0" +setup_2_2 +agg_disable_chip _sysfs.0 +mkdir "$CONFIGFS_AGG_DIR/_sysfs.0/line2" 2> /dev/null && fail "line unexpe= ctedly added" +rmdir "$CONFIGFS_AGG_DIR/_sysfs.0/line1" 2> /dev/null && fail "line unexpe= ctedly removed" +teardown_2_2 + +echo "2.2.3.2. Line key can be modified" +setup_2_2 +agg_disable_chip _sysfs.0 +agg_set_key _sysfs.0 line0 "$(sim_get_chip_label chip0 bank1)" +agg_set_key _sysfs.0 line1 "$(sim_get_chip_label chip1 bank1)" +agg_enable_chip _sysfs.0 +teardown_2_2 + +echo "2.2.3.3. Line name can be modified" +setup_2_2 +agg_disable_chip _sysfs.0 +agg_set_line_name _sysfs.0 line0 new0 +agg_set_line_name _sysfs.0 line1 new1 +agg_enable_chip _sysfs.0 +test "$(agg_get_line_name _sysfs.0 0)" =3D "new0" || fail "line name is un= set" +test "$(agg_get_line_name _sysfs.0 1)" =3D "new1" || fail "line name is un= set" +teardown_2_2 + +echo "2.2.3.4. Line offset can be modified" +setup_2_2 +agg_disable_chip _sysfs.0 +agg_set_offset _sysfs.0 line0 5 +agg_set_offset _sysfs.0 line1 7 +agg_enable_chip _sysfs.0 +teardown_2_2 + +echo "2.2.3.5. Can re-enable a chip with valid reconfiguration" +setup_2_2 +agg_disable_chip _sysfs.0 +agg_set_key _sysfs.0 line0 "$(sim_get_chip_label chip1 bank1)" +agg_set_offset _sysfs.0 line0 15 +agg_set_key _sysfs.0 line1 "$(sim_get_chip_label chip0 bank1)" +agg_set_offset _sysfs.0 line0 14 +agg_enable_chip _sysfs.0 +teardown_2_2 + +echo "2.2.3.6. Can't re-enable a chip with invalid reconfiguration" +setup_2_2 +agg_disable_chip _sysfs.0 +agg_set_key _sysfs.0 line0 invalidkey +echo 1 > "$CONFIGFS_AGG_DIR/_sysfs.0/live" 2> /dev/null && fail "chip unex= pectedly enabled" +teardown_2_2 +setup_2_2 +agg_disable_chip _sysfs.0 +agg_set_offset _sysfs.0 line0 99 +echo 1 > "$CONFIGFS_AGG_DIR/_sysfs.0/live" 2> /dev/null && fail "chip unex= pectedly enabled" +teardown_2_2 + +echo "3. Module unload" + +echo "3.1. Can't unload module if there is at least one device created via= configfs" +agg_create_chip agg0 +modprobe -r gpio-aggregator 2> /dev/null +test -d /sys/module/gpio_aggregator || fail "module unexpectedly unloaded" +agg_remove_chip agg0 + +echo "3.2. Can unload module if there is no device created via configfs" +echo "chip0_bank0 1 chip1_bank0 3" > "$SYSFS_AGG_DIR/new_device" +modprobe -r gpio-aggregator 2> /dev/null +test -d /sys/module/gpio_aggregator && fail "module unexpectedly remains t= o be loaded" +modprobe gpio-aggregator 2> /dev/null + +echo "4. GPIO forwarder functional" +SETTINGS=3D"chip0:bank0:2 chip0:bank1:4 chip1:bank0:6 chip1:bank1:8" +setup_4() { + local OFFSET=3D0 + agg_create_chip agg0 + for SETTING in $SETTINGS; do + CHIP=3D$(echo "$SETTING" | cut -d: -f1) + BANK=3D$(echo "$SETTING" | cut -d: -f2) + LINE=3D$(echo "$SETTING" | cut -d: -f3) + agg_create_line agg0 "line${OFFSET}" + agg_set_key agg0 "line${OFFSET}" "$(sim_get_chip_label "$CHIP" "$BAN= K")" + agg_set_offset agg0 "line${OFFSET}" "$LINE" + OFFSET=3D$(expr $OFFSET + 1) + done + agg_enable_chip agg0 +} +teardown_4() { + agg_configfs_cleanup +} + +echo "4.1. Forwarding set values" +setup_4 +OFFSET=3D0 +for SETTING in $SETTINGS; do + CHIP=3D$(echo "$SETTING" | cut -d: -f1) + BANK=3D$(echo "$SETTING" | cut -d: -f2) + LINE=3D$(echo "$SETTING" | cut -d: -f3) + DEVNAME=3D$(cat "$CONFIGFS_SIM_DIR/$CHIP/dev_name") + CHIPNAME=3D$(cat "$CONFIGFS_SIM_DIR/$CHIP/$BANK/chip_name") + VAL_PATH=3D"/sys/devices/platform/$DEVNAME/$CHIPNAME/sim_gpio${LINE}/valu= e" + test $(cat $VAL_PATH) =3D "0" || fail "incorrect value read from sysfs" + $BASE_DIR/gpio-mockup-cdev -s 1 "/dev/$(agg_configfs_chip_name agg0)" "$O= FFSET" & + mock_pid=3D$! + sleep 0.1 # FIXME Any better way? + test "$(cat $VAL_PATH)" =3D "1" || fail "incorrect value read from sysfs" + kill "$mock_pid" + OFFSET=3D$(expr $OFFSET + 1) +done +teardown_4 + +echo "4.2. Forwarding set config" +setup_4 +OFFSET=3D0 +for SETTING in $SETTINGS; do + CHIP=3D$(echo "$SETTING" | cut -d: -f1) + BANK=3D$(echo "$SETTING" | cut -d: -f2) + LINE=3D$(echo "$SETTING" | cut -d: -f3) + DEVNAME=3D$(cat "$CONFIGFS_SIM_DIR/$CHIP/dev_name") + CHIPNAME=3D$(cat "$CONFIGFS_SIM_DIR/$CHIP/$BANK/chip_name") + VAL_PATH=3D"/sys/devices/platform/$DEVNAME/$CHIPNAME/sim_gpio${LINE}/valu= e" + $BASE_DIR/gpio-mockup-cdev -b pull-up "/dev/$(agg_configfs_chip_name agg0= )" "$OFFSET" + test $(cat "$VAL_PATH") =3D "1" || fail "incorrect value read from sysfs" + OFFSET=3D$(expr $OFFSET + 1) +done +teardown_4 + +echo "5. Race condition verification" + +echo "5.1. Stress test of new_device/delete_device and module load/unload" +for _ in $(seq 1000); do + { + echo "dummy 0" > "$SYSFS_AGG_DIR/new_device" + cat "$CONFIGFS_AGG_DIR/_sysfs.0/dev_name" > "$SYSFS_AGG_DIR/delete_devic= e" + } 2> /dev/null +done & +writer_pid=3D$! +while kill -0 "$writer_pid" 2> /dev/null; do + { + modprobe gpio-aggregator + modprobe -r gpio-aggregator + } 2> /dev/null +done + +echo "GPIO $MODULE test PASS" --=20 2.45.2