From patchwork Mon Apr 7 04:30:11 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Koichiro Den X-Patchwork-Id: 878831 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 4D19319341F 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 4E0A43F471 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-af51d92201fso4560404a12.0 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=wsUk5KiCW/Gzd+EWbdeRwf6UGNB+7nd33m7z++0lOBoYxF1e+zpe5QBlzRn3SuDuVR xoif6RSqXzAwf473RNHaxqzBTkFSf/ogQoohXCx/helXZaCL0byRrPNbE6baAWc5yqK3 hi96dQh9Og9PegUsdulkz1TTJXIw6yqMBGMNISJ69CZjBjtL/tbqQKga0Gx3pMR4canQ 8j/3j3nLAtceo1Ggm8z7flcnFGCRP5oZYEmFoi+xr02gkOQAMBAKksnyqPismm+RfTjA m+VnyxSPyS6oBjjPF/Uw4o98kwLlwUe07jlomhOr3KQiVT7e/VYEpM8v/QTDTvD0Es2X zxXw== X-Gm-Message-State: AOJu0YwYg9/UgKkGIVA4W7xEis7+/Xy5iW3N0bOpZ6z5RVJkTILSLvm5 Tbw+Fb3G22x5I5tpfhFquVAPTJYYBlHwHSUy2S2nCCX0nu7NWJQeQ3buroI50cRAVHliUIVqbpO l4IHmZ5bcl75RJ+2NCQnJw4KnG1+ofD37zcjW6c/4k9SIiOEy+NVjitDUC3OG79fQckL6s6/79i jXfPHIcdE= X-Gm-Gg: ASbGncu78gnQWFaOoSIspX3G/3R1NgqmUtUvKey8owTAOdOyt7fxd22osOaPLdeNic6 h0O0+lUcE9RY75AsZhFwv5ZeCgwIqHJ1UhQ0kKF190UnBXuQbaBIdnKCDtotRFlS0+bQxwNJIxY mqLXdostaUaWLdJ6A4dCKJ9YZSqc5jk3uGDSDRUEU4m1/8Y3XkY9XCIp1CmFSISuKcfdJ9rYVRe MU7XB8cucN8HeqpdTXRzo87AsEHkfmtamEEHACDfIIP6RZlCqOjS2RsZelHxJONlH/iWrZ9xPE0 pm7dw4g1PwqCBegjoIGWeXLTw3j2luzCFw== X-Received: by 2002:a17:902:d491:b0:220:ff3f:6cba with SMTP id d9443c01a7336-22a95573902mr122824845ad.38.1744000248659; 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-gpio@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 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; } -static int aggr_parse(struct gpio_aggregator *aggr) -{ - char *args = skip_spaces(aggr->args); - char *name, *offsets, *p; - unsigned int i, n = 0; - int error = 0; - - unsigned long *bitmap __free(bitmap) = - bitmap_alloc(AGGREGATOR_MAX_GPIOS, GFP_KERNEL); - if (!bitmap) - return -ENOMEM; - - args = next_arg(args, &name, &p); - while (*args) { - args = next_arg(args, &offsets, &p); - - p = get_options(offsets, 0, &error); - if (error == 0 || *p) { - /* Named GPIO line */ - error = aggr_add_gpio(aggr, name, U16_MAX, &n); - if (error) - return error; - - name = offsets; - continue; - } - - /* GPIO chip + offset(s) */ - error = 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 = aggr_add_gpio(aggr, name, i, &n); - if (error) - return error; - } - - args = 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 = kzalloc(sizeof(*aggr) + count + 1, GFP_KERNEL); - if (!aggr) { - res = -ENOMEM; - goto put_module; - } - - memcpy(aggr->args, buf, count + 1); - - aggr->lookups = kzalloc(struct_size(aggr->lookups, table, 1), - GFP_KERNEL); - if (!aggr->lookups) { - res = -ENOMEM; - goto free_ga; - } - - mutex_lock(&gpio_aggregator_lock); - id = idr_alloc(&gpio_aggregator_idr, aggr, 0, 0, GFP_KERNEL); - mutex_unlock(&gpio_aggregator_lock); - - if (id < 0) { - res = id; - goto free_table; - } - - aggr->lookups->dev_id = kasprintf(GFP_KERNEL, "%s.%d", DRV_NAME, id); - if (!aggr->lookups->dev_id) { - res = -ENOMEM; - goto remove_idr; - } - - res = aggr_parse(aggr); - if (res) - goto free_dev_id; - - gpiod_add_lookup_table(aggr->lookups); - - pdev = platform_device_register_simple(DRV_NAME, id, NULL, 0); - if (IS_ERR(pdev)) { - res = PTR_ERR(pdev); - goto remove_table; - } - - aggr->pdev = 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 = 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 = 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[] = { - &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); -} - /* * GPIO Forwarder @@ -583,6 +395,184 @@ static struct gpiochip_fwd *gpiochip_fwd_create(struct device *dev, } +/* + * Sysfs interface + */ +static int aggr_parse(struct gpio_aggregator *aggr) +{ + char *args = skip_spaces(aggr->args); + char *name, *offsets, *p; + unsigned int i, n = 0; + int error = 0; + + unsigned long *bitmap __free(bitmap) = + bitmap_alloc(AGGREGATOR_MAX_GPIOS, GFP_KERNEL); + if (!bitmap) + return -ENOMEM; + + args = next_arg(args, &name, &p); + while (*args) { + args = next_arg(args, &offsets, &p); + + p = get_options(offsets, 0, &error); + if (error == 0 || *p) { + /* Named GPIO line */ + error = aggr_add_gpio(aggr, name, U16_MAX, &n); + if (error) + return error; + + name = offsets; + continue; + } + + /* GPIO chip + offset(s) */ + error = 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 = aggr_add_gpio(aggr, name, i, &n); + if (error) + return error; + } + + args = 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 = kzalloc(sizeof(*aggr) + count + 1, GFP_KERNEL); + if (!aggr) { + res = -ENOMEM; + goto put_module; + } + + memcpy(aggr->args, buf, count + 1); + + aggr->lookups = kzalloc(struct_size(aggr->lookups, table, 1), + GFP_KERNEL); + if (!aggr->lookups) { + res = -ENOMEM; + goto free_ga; + } + + mutex_lock(&gpio_aggregator_lock); + id = idr_alloc(&gpio_aggregator_idr, aggr, 0, 0, GFP_KERNEL); + mutex_unlock(&gpio_aggregator_lock); + + if (id < 0) { + res = id; + goto free_table; + } + + aggr->lookups->dev_id = kasprintf(GFP_KERNEL, "%s.%d", DRV_NAME, id); + if (!aggr->lookups->dev_id) { + res = -ENOMEM; + goto remove_idr; + } + + res = aggr_parse(aggr); + if (res) + goto free_dev_id; + + gpiod_add_lookup_table(aggr->lookups); + + pdev = platform_device_register_simple(DRV_NAME, id, NULL, 0); + if (IS_ERR(pdev)) { + res = PTR_ERR(pdev); + goto remove_table; + } + + aggr->pdev = 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 = 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 = 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[] = { + &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 = { }, }; +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); From patchwork Mon Apr 7 04:30:12 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Koichiro Den X-Patchwork-Id: 879195 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 97CDE221F26 for ; Mon, 7 Apr 2025 04:30:54 +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=1744000256; cv=none; b=JX745g/A8+tYXpFzUcRNWDl5RAxSb5ahTwjRe7FFnTfRXuH0DYHRaCHNOIfUn4Cy6Mz8Ve9xuhwtY7+nRfgZphCDrU+QG43bu9sEv3PVbAzziSelExx4AQyvQrb2fGX1tfIQc5nnEyBNpbpjrv2FlbfP0R2M3JTbVkMQE0j9Ees= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1744000256; c=relaxed/simple; bh=PF2bzENyixS725vuRFQYOE0EHw2Blz1UgA3yYSWEqBc=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=C3XvPPw3ukAZM4hR7/aZEV1m1YNM11RwolI/FijNSfI1fiW1EANlfAgH5sovQsD8FtRsgZvtnE2gUcRHY3783GPMyKPGfi70P5e6LeD4zYFBhux/rJJNweE1fojy5NNUiMLxfugvLSxYj8A+ir9BQXmgOtmJb5DRlpV+6GG3Pg8= 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-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 7C8FB3F476 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-pg1-f199.google.com with SMTP id 41be03b00d2f7-af53a6bc0b6so1456400a12.1 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=PbooEcv2V5a04lqk9HGWWsc9zxzQoy7xBjwcC5iMCoXl0J/SLFvaEkhbW86BdajcRe oF2SDJdp9kv2EhYH9NltJR/zTCjkwhh507CcH8sd15jEy3/FjbdW2h7fN7aUD38U5KXF kuuaLddlocLcoQktaJ6f8qsgiWRUO79OY6ufmEws7Jo+9c2XPFbcpBnMq5DjLLZ+KdeJ Vg22Zs/q2H7ADES7ssWwt128uig54ido2TYp7NGHrFYfszF6pjw4YTFaNVRUYW5496Sb 1BJfjyCBY8xu7h65TE/BorGGed2jHokWHaMC8VCBdrDZ3jFwxkHr3Lv64YlG2y44p4mi A8fA== X-Gm-Message-State: AOJu0Yw/Kdh0xjHA7LHMRqgTQPUkkSoRmo2yA8pQFAaKJUXCg8rXgmXx Z/TxnxX3/jC3bc3GEiFwVR9gj5sNAuT6K72du+E3HShubEh0XZbvHOJcw4AtAFVSXcNrLvclhXK ++qnaAUKCrkcttRp4UMmvNTSEmLVbK6kTXLhdCULSJkoXUisoukSRtflYQo6VsHNq+2JuSepkNl 0R+mO4U5w= X-Gm-Gg: ASbGncuUil7xEuTdTOFiIbvIfA6fW1asWxfl9DbgdQGZThZH09T0ccmzA1MlL3+U5UF RLUv9R/aIoE+URAQbe8qp3Acj/bz8vsEMrBD7D27WOF2XJyvDuEGGOBvxppv/7pX44s5nt0JKTo DOPkwdf0V5I/IfeHJQ10xdVJMztUu5rRhvfPSOMzcuNxNMzFS0SW11iX5XOt6XQ7nBBZOfhgVX7 0OEXXl4QxQjPXTZyqf8IBg5lOFIo8DEsicFB7xWMpgo41LAMiLOhX15mUBIdtH1DYrWj6lHesJj /SiNMD6cAp8MENL9EAt5gEmx5sMRZRWlGg== X-Received: by 2002:a17:902:e842:b0:224:c46:d167 with SMTP id d9443c01a7336-22a9552acdemr101661835ad.16.1744000250899; 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-gpio@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 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); -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; @@ -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 = skip_spaces(aggr->args); char *name, *offsets, *p; @@ -417,7 +417,7 @@ static int aggr_parse(struct gpio_aggregator *aggr) p = get_options(offsets, 0, &error); if (error == 0 || *p) { /* Named GPIO line */ - error = aggr_add_gpio(aggr, name, U16_MAX, &n); + error = gpio_aggregator_add_gpio(aggr, name, U16_MAX, &n); if (error) return error; @@ -433,7 +433,7 @@ static int aggr_parse(struct gpio_aggregator *aggr) } for_each_set_bit(i, bitmap, AGGREGATOR_MAX_GPIOS) { - error = aggr_add_gpio(aggr, name, i, &n); + error = 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; } -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 *driver, + 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 *driver, const char *buf, goto remove_idr; } - res = aggr_parse(aggr); + res = gpio_aggregator_parse(aggr); if (res) goto free_dev_id; @@ -523,9 +523,10 @@ static ssize_t new_device_store(struct device_driver *driver, const char *buf, return res; } -static DRIVER_ATTR_WO(new_device); +static struct driver_attribute driver_attr_gpio_aggregator_new_device = + __ATTR(new_device, 0200, NULL, gpio_aggregator_new_device_store); -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); } -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 *driver, + 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_driver *driver, return -ENOENT; } - 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 = + __ATTR(delete_device, 0200, NULL, gpio_aggregator_delete_device_store); static struct attribute *gpio_aggregator_attrs[] = { - &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 = { static int __exit gpio_aggregator_idr_remove(int id, void *p, void *data) { - gpio_aggregator_free(p); + gpio_aggregator_destroy(p); return 0; } From patchwork Mon Apr 7 04:30:13 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Koichiro Den X-Patchwork-Id: 878830 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 58E532222CF for ; Mon, 7 Apr 2025 04:30:56 +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=1744000259; cv=none; b=fRIDvhZRyR6+P1jh/sOuNJUhhI1jVZcCJsc20YYgmbfbLQXvJz5+DUqlwACZpDIAI8t2BmRRxiT/wjCVSDKNrVYZlhG3Auyb4Fvvj5uwovhHwL/aRHMFMluG0Z1y5k6jQVPjgchV0WM3XvPPW3a/hBuYJkHulgbF2a7OGYbrU+Q= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1744000259; c=relaxed/simple; bh=TbAKJXmX6g7xhJYJocuFyn/R1DEc55FoQ9fLzC8oylM=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=MKfA8kzVIKU/NMd5VkidiUlYwR9OJxNM+5t6zDxJIiANPBCR6l/m2hNuu1UzE9aVxMKU+3FmQFbMiHOwcfG+RpU29XdCytjtLbuu5Ac56rQGk9lIsfnhjrRXyVZl/c4UkM1VcKIYCdSCdgE6jG3rbg+uCGEXGI1aBvNSHpswIYc= 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.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="PKBQcJdw" 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-0.canonical.com (Postfix) with ESMTPS id A58723F2C2 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-f197.google.com with SMTP id d9443c01a7336-225107fbdc7so33202495ad.0 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=eErB509fpH2F8KM/wFZDcgQpZJ2LkSNsaUwTjHxCHW5aIfMgXlJmsPtI/DMdHJSZyl auUVF1ht3o5o80an/x/8GEb45EOemjZPCpE3cc7BXOG6E0REQ7qFHnee9DmyHaJfwW8+ 7dW1VC1/fHJpoNTCWCFunedy3OpeOmAXfEvp8XSfW2qN4ZR0vIZ7+IKgwoAtxYSM2PQo nJSVo0QQtJkYfnrsHsFSQzajZKJ2aeQTe0xYu3yPzKXAitFr3q2MOyMOvcILw/ONrmid tFOeio/97BBbwBicCqaE1QEdI23rG8ZE5LwOGPRNg4H2HNjy8cdFVRsh33GOa3lm9baQ mXXQ== X-Gm-Message-State: AOJu0Yxa3z9ZpoN9Bo8Q9OMy/PL3FpARdXhru9aK48mfAhoIRZI2EL/a l4cf7MiBHRUzCkYz0JpYvyV91NaBQjMTPuTES3WPUkro4vzkQHZBgAJwqdCFfMhFT8H6IPP+J1m raj3AbZNAGMGrR169ZGpgOtzIW/5ZYsAzQdtxB8WZUbjGAVjIExmaX3zkxubsl6T1yKF3n70Su6 Szgt+MWaQ= X-Gm-Gg: ASbGncu51ygQtIzpk9bM/mB1imibRK41RWwzQQGyrnDDYlaidHUFWt+LGpntgWhTZ/I 6pSNMzTWahT+u3F/sbsWI//ryTsh1R/D9bfDpCbsP2LC9GcG01WSItkogtE6V1iMrJ4Q2NwJgiO Y214Gx3D/Z5ORaelkRUbUMl4mB2q27Bf09uG8x1FKTnX0hk37DLXTSV0TwihCw64sR9Qhsf/chd AawxghQuinMUwuGPFn5PiqtmPCbHyhx1cJTG9E+DT//f2gAvasBQBvAXzNNCDDwMYDEQrqd34uA SUsh/nVyxJnU9CEQ4kbi6NPvZ/TjZynPuw== X-Received: by 2002:a17:903:1b2d:b0:216:7926:8d69 with SMTP id d9443c01a7336-22a8a8dd69emr130201305ad.47.1744000253107; 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-gpio@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 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[]; }; static DEFINE_MUTEX(gpio_aggregator_lock); /* protects idr */ static DEFINE_IDR(gpio_aggregator_idr); +static int gpio_aggregator_alloc(struct gpio_aggregator **aggr, size_t arg_size) +{ + int ret; + + struct gpio_aggregator *new __free(kfree) = kzalloc( + sizeof(*new) + arg_size, GFP_KERNEL); + if (!new) + return -ENOMEM; + + scoped_guard(mutex, &gpio_aggregator_lock) + ret = idr_alloc(&gpio_aggregator_idr, new, 0, 0, GFP_KERNEL); + + if (ret < 0) + return ret; + + new->id = ret; + *aggr = 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(struct device_driver *driver, { struct gpio_aggregator *aggr; struct platform_device *pdev; - int res, id; + int res; if (!try_module_get(THIS_MODULE)) return -ENOENT; /* kernfs guarantees string termination, so count + 1 is safe */ - aggr = kzalloc(sizeof(*aggr) + count + 1, GFP_KERNEL); - if (!aggr) { - res = -ENOMEM; + res = gpio_aggregator_alloc(&aggr, count + 1); + if (res) goto put_module; - } memcpy(aggr->args, buf, count + 1); @@ -475,19 +502,10 @@ static ssize_t gpio_aggregator_new_device_store(struct device_driver *driver, goto free_ga; } - mutex_lock(&gpio_aggregator_lock); - id = idr_alloc(&gpio_aggregator_idr, aggr, 0, 0, GFP_KERNEL); - mutex_unlock(&gpio_aggregator_lock); - - if (id < 0) { - res = id; - goto free_table; - } - - aggr->lookups->dev_id = kasprintf(GFP_KERNEL, "%s.%d", DRV_NAME, id); + aggr->lookups->dev_id = kasprintf(GFP_KERNEL, "%s.%d", DRV_NAME, aggr->id); if (!aggr->lookups->dev_id) { res = -ENOMEM; - goto remove_idr; + goto free_table; } res = gpio_aggregator_parse(aggr); @@ -496,7 +514,7 @@ static ssize_t gpio_aggregator_new_device_store(struct device_driver *driver, gpiod_add_lookup_table(aggr->lookups); - pdev = platform_device_register_simple(DRV_NAME, id, NULL, 0); + pdev = platform_device_register_simple(DRV_NAME, aggr->id, NULL, 0); if (IS_ERR(pdev)) { res = PTR_ERR(pdev); goto remove_table; @@ -510,14 +528,10 @@ static ssize_t gpio_aggregator_new_device_store(struct 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; From patchwork Mon Apr 7 04:30:14 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Koichiro Den X-Patchwork-Id: 878829 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 84FD9222585 for ; Mon, 7 Apr 2025 04:31:00 +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=CmVzCqr3FHrU+r/odR5V7+2qwO04VsBpgmkeJp7TuhLu5sxG7qdmR4f+tCi1wCArHxjNtcGraDq+h9YsJ/YgZsqMfSqrfK2/j6s65CLpOa1Z3IE4Gjs0tdQdzXYL9jsS7Jl1cq1r3qoQdeEu6dfiuaV65wdQsdtcGZMWPvvskUs= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1744000263; c=relaxed/simple; bh=lB984tfDAhyeELYOhlZPQDtI7dyD54w4UolKF76kaYU=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=OnCyPqQKdzuZn+iI5BVlXEQzbiYpxvaljzNnafaO6xElc0pd88VjRlrkwLE/aIqqD3mh4XsJ0mlodQO5vwZ1bpAnPuDBzoQYyFlnvoyUu3lVNNpTwhDI4vpyeVaflk1Plwm7agaW3Y7eH9SDMUI0unqWMvvvCVUniogEQYgf24M= 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.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="H6zaf1jH" Received: from mail-pl1-f200.google.com (mail-pl1-f200.google.com [209.85.214.200]) (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 A34A83F0F8 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-f200.google.com with SMTP id d9443c01a7336-2255ae39f8fso53103735ad.0 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=fuACgS4hBhVLVdBm3B6QuJW/m1aTs/gowz6QilmuLGgF3mezT4XMX9Y6jM8fsxE4CD TPF1/+33N7YuG9UT1soy2j+lUmXpzz3vt5pvIO4GVj6nykiDzgGDVyc/cIQMzcvc+iXz tgMXa+T6FktqkLwrDil6ekhqVm3Ioh/Wk00OYRnEMqbBdLSlcfcWGkYzSn2KYvu/Ml0C i4BV26jkfXzP//MY6weBSRqfvOsnswV3ZDQOTLGFibF7G3Fmh8Oqoi658jE59YqPT1eA 1y6VYqfFB8V9QhXTISCoZ3OAwVyngu/n4m5imveVKktNI2/4sLtfdW9JAjkymB10Qfft PD1w== X-Gm-Message-State: AOJu0YzLodWvCr7iH+hYSxItdaBrpMKcaSpwnHlQUARqM5+Yi5Lz5S6j HgAol7DKoOF5d1H3Se28FWc4GQLPc5w83wvK7AuxDnOD0XxH9Tl7+uO7DvvXrVMokI82O/KT2DZ h9aOhIEovTMr6onsD8r/6hmIXN98lJNy5wv4OzJiufLEzNmwXoMrunnDvffcPcmMuXiM6iWX26X jxnFp0o7o= X-Gm-Gg: ASbGncsO8ZPD3Wk8/0MDXVR6GrZksOGMjFKpDJ/aBsW3nfBlf9Zs5B0ScYX/szY3Hcn FYOuR4I8P4IHPbwuQ3FxVa/pPzGP+ejDM2MxhfzuNbvSdj6p4xJL7tWOqT56E4N+Drkwk8++gYx WoVV4HcAzMpN/RjNtAcLSoBQrR7lrxc7RC7Y2avchHNScQD+u92/fCC+iqBl2RcvxaOpUpQTBHH WS+7faeoS1mueKQwCCTPB7q4EthdC8ArsBRejFOnPdgVxEFF2LFvHm1a8qvZ+KHjUmrEk3zucyY wDm4PamSF+DWbvIJTu1IpVPbziVm3yUUWw== X-Received: by 2002:a17:903:3ba8:b0:215:a56f:1e50 with SMTP id d9443c01a7336-22a89a3fa0dmr171385795ad.8.1744000255814; 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-gpio@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 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 >= 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" 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 @@ #include #include +#include #include #include #include #include +#include +#include #include #include #include @@ -27,6 +30,8 @@ #include #include +#include "dev-sync-probe.h" + #define AGGREGATOR_MAX_GPIOS 512 /* @@ -34,12 +39,38 @@ */ 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[]; }; +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); @@ -59,6 +90,8 @@ static int gpio_aggregator_alloc(struct gpio_aggregator **aggr, size_t arg_size) return ret; new->id = ret; + INIT_LIST_HEAD(&new->list_head); + mutex_init(&new->lock); *aggr = 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); + mutex_destroy(&aggr->lock); kfree(aggr); } @@ -90,6 +124,71 @@ static int gpio_aggregator_add_gpio(struct gpio_aggregator *aggr, return 0; } +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.pdev); +} + +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 idx, + char *key, int offset) +{ + struct gpio_aggregator_line *line; + + line = kzalloc(sizeof(*line), GFP_KERNEL); + if (!line) + return ERR_PTR(-ENOMEM); + + if (key) { + line->key = kstrdup(key, GFP_KERNEL); + if (!line->key) { + kfree(line); + return ERR_PTR(-ENOMEM); + } + } + + line->flags = GPIO_LOOKUP_FLAGS_DEFAULT; + line->parent = parent; + line->idx = idx; + line->offset = 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); +} + /* * GPIO Forwarder @@ -424,6 +523,475 @@ static struct gpiochip_fwd *gpiochip_fwd_create(struct device *dev, } +/* + * Configfs interface + */ + +static struct gpio_aggregator * +to_gpio_aggregator(struct config_item *item) +{ + struct config_group *group = 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 = 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 = 0; + + memset(properties, 0, sizeof(properties)); + + num_lines = gpio_aggregator_count_lines(aggr); + if (num_lines == 0) + return NULL; + + const char **line_names __free(kfree) = 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++] = line->name ?: ""; + + properties[0] = 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 = 0; + int ret = 0; + + if (gpio_aggregator_count_lines(aggr) == 0) + return -EINVAL; + + aggr->lookups = kzalloc(struct_size(aggr->lookups, table, 1), + GFP_KERNEL); + if (!aggr->lookups) + return -ENOMEM; + + swnode = gpio_aggregator_make_device_sw_node(aggr); + if (IS_ERR(swnode)) + goto err_remove_lookups; + + memset(&pdevinfo, 0, sizeof(pdevinfo)); + pdevinfo.name = DRV_NAME; + pdevinfo.id = aggr->id; + pdevinfo.fwnode = 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 != n) { + ret = -EINVAL; + goto err_remove_swnode; + } + + if (line->offset < 0) + ret = gpio_aggregator_add_gpio(aggr, line->key, + U16_MAX, &n); + else + ret = gpio_aggregator_add_gpio(aggr, line->key, + line->offset, &n); + if (ret) + goto err_remove_swnode; + } + + aggr->lookups->dev_id = kasprintf(GFP_KERNEL, "%s.%d", DRV_NAME, aggr->id); + if (!aggr->lookups->dev_id) { + ret = -ENOMEM; + goto err_remove_swnode; + } + + gpiod_add_lookup_table(aggr->lookups); + + ret = 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 = 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 = to_gpio_aggregator_line(item); + struct gpio_aggregator *aggr = 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 = to_gpio_aggregator_line(item); + struct gpio_aggregator *aggr = line->parent; + + char *key __free(kfree) = 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 = 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 = to_gpio_aggregator_line(item); + struct gpio_aggregator *aggr = 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 = to_gpio_aggregator_line(item); + struct gpio_aggregator *aggr = line->parent; + + char *name __free(kfree) = 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 = 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 = to_gpio_aggregator_line(item); + struct gpio_aggregator *aggr = 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 *page, + size_t count) +{ + struct gpio_aggregator_line *line = to_gpio_aggregator_line(item); + struct gpio_aggregator *aggr = line->parent; + int offset, ret; + + ret = kstrtoint(page, 0, &offset); + if (ret) + return ret; + + /* + * When offset == -1, 'key' represents a line name to lookup. + * When 0 <= 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 = offset; + + return count; +} +CONFIGFS_ATTR(gpio_aggregator_line_, offset); + +static struct configfs_attribute *gpio_aggregator_line_attrs[] = { + &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 = to_gpio_aggregator(item); + struct platform_device *pdev; + + guard(mutex)(&aggr->lock); + + pdev = 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 = 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 *page, + size_t count) +{ + struct gpio_aggregator *aggr = to_gpio_aggregator(item); + int ret = 0; + bool live; + + ret = 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 == gpio_aggregator_is_active(aggr)) + ret = -EPERM; + else if (live) + ret = gpio_aggregator_activate(aggr); + else + gpio_aggregator_deactivate(aggr); + } + + /* + * Undepend is required only if device disablement (live == 0) + * succeeds or if device enablement (live == 1) fails. + */ + if (live == !!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[] = { + &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 = to_gpio_aggregator_line(item); + struct gpio_aggregator *aggr = 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 = { + .release = gpio_aggregator_line_release, +}; + +static const struct config_item_type gpio_aggregator_line_type = { + .ct_item_ops = &gpio_aggregator_line_item_ops, + .ct_attrs = gpio_aggregator_line_attrs, + .ct_owner = THIS_MODULE, +}; + +static void gpio_aggregator_device_release(struct config_item *item) +{ + struct gpio_aggregator *aggr = 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 = { + .release = gpio_aggregator_device_release, +}; + +static struct config_group * +gpio_aggregator_device_make_group(struct config_group *group, const char *name) +{ + struct gpio_aggregator *aggr = to_gpio_aggregator(&group->cg_item); + struct gpio_aggregator_line *line; + unsigned int idx; + int ret, nchar; + + ret = sscanf(name, "line%u%n", &idx, &nchar); + if (ret != 1 || nchar != 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 == idx) + return ERR_PTR(-EINVAL); + + line = 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_type); + + gpio_aggregator_line_add(aggr, line); + + return &line->group; +} + +static struct configfs_group_operations gpio_aggregator_device_group_ops = { + .make_group = gpio_aggregator_device_make_group, +}; + +static const struct config_item_type gpio_aggregator_device_type = { + .ct_group_ops = &gpio_aggregator_device_group_ops, + .ct_item_ops = &gpio_aggregator_device_item_ops, + .ct_attrs = gpio_aggregator_device_attrs, + .ct_owner = 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 = gpio_aggregator_alloc(&aggr, 0); + if (ret) + return ERR_PTR(ret); + + config_group_init_type_name(&aggr->group, name, &gpio_aggregator_device_type); + dev_sync_probe_init(&aggr->probe_data); + + return &aggr->group; +} + +static struct configfs_group_operations gpio_aggregator_group_ops = { + .make_group = gpio_aggregator_make_group, +}; + +static const struct config_item_type gpio_aggregator_type = { + .ct_group_ops = &gpio_aggregator_group_ops, + .ct_owner = THIS_MODULE, +}; + +static struct configfs_subsystem gpio_aggregator_subsys = { + .su_group = { + .cg_item = { + .ci_namebuf = DRV_NAME, + .ci_type = &gpio_aggregator_type, + }, + }, +}; + + /* * Sysfs interface */ @@ -495,6 +1063,7 @@ static ssize_t gpio_aggregator_new_device_store(struct device_driver *driver, memcpy(aggr->args, buf, count + 1); + aggr->init_via_sysfs = true; aggr->lookups = 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; } - aggr->pdev = pdev; + aggr->probe_data.pdev = pdev; module_put(THIS_MODULE); return count; @@ -542,10 +1111,7 @@ static struct driver_attribute driver_attr_gpio_aggregator_new_device = 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); } @@ -567,12 +1133,19 @@ static ssize_t gpio_aggregator_delete_device_store(struct device_driver *driver, return -ENOENT; mutex_lock(&gpio_aggregator_lock); - aggr = idr_remove(&gpio_aggregator_idr, id); - mutex_unlock(&gpio_aggregator_lock); - if (!aggr) { + aggr = 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); gpio_aggregator_destroy(aggr); module_put(THIS_MODULE); @@ -649,21 +1222,61 @@ static struct platform_driver gpio_aggregator_driver = { 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; } 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); } static int __init gpio_aggregator_init(void) { - return platform_driver_register(&gpio_aggregator_driver); + int ret = 0; + + config_group_init(&gpio_aggregator_subsys.su_group); + mutex_init(&gpio_aggregator_subsys.su_mutex); + ret = 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 = 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); @@ -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); From patchwork Mon Apr 7 04:30:15 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Koichiro Den X-Patchwork-Id: 879194 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 2E40A19342F for ; Mon, 7 Apr 2025 04:31:00 +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=1744000262; cv=none; b=UhlGSQhgGxXoVOH1Nj+PxCYBJ1F1+WmjqYMk4XOBV3IUPD215mKnSepyrxyGZiVqxtvQoUC2aYLaYmF/szNYWldQlCX7bm5kbt9OCU6brL008WVThD3+IVCToH0rqUCAc7uTKwBaJt3OcsJU50uQt7GQQ18BP4TPb+BaluKQnas= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1744000262; c=relaxed/simple; bh=topjyXl8cfmVC2wwnmz9rv1A6nyhahSaPg2JtNji6lQ=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=rKSAbpO+nbeJ8sB/LLRqBZiVM1HIN8ySSa0I9YV9xnu9XuwClAxNcX8TpbTr/jgS0A6E9zzqfHO+fmPE1SaFYvbnY+LB97ngEytH2eNsdm8gEssIMMESe+T8JyrSnAipvTQdLJJmvPT+PwLBD+tdEjdI3M3QRc21BfFKo2sLiQ8= 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-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 603DF3F29A 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-f198.google.com with SMTP id d9443c01a7336-229170fbe74so31708145ad.2 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=tt1dmmZLQT0C+nH6KIlmeMDZvui8sSP3BGugt50vdgYFki3P1H1XRQDfdeD12cfdt5 7FlONx3+KK/Ej3Q/EScUuzn4F3vzYWtOz+s/H6ERKTnvvR/JpdabHlhtwx0Nt+70c3Ub 2TnSuPK7ofw7+ceMtqpvmdKIw9L3W7b0unDcghdTFlYKvIVzCy95awwrctVNAuGdxTYN CUs8ON089a/JkKRcPg7UCGrY17UD2RlAwDf8LoX/YPqBL11qZNI+G+ilJ96XAAbZMYbn tgLoBzlGMod0mOv1uBM1DEifm5DK7BUV0QgaAz8cYsH6k5VGVBbdxC0xEfxs0fHiIJHq HL2A== X-Gm-Message-State: AOJu0YyeVXJ97GjGkey2R41G+iVePaseVbQ4WoRUbtsB20dhTpg8W7jW 4UnT7WxT7ogmJSl4Dogh6u/xy2OZ2ecik2Ce7cm1bkxwLPLMHvjbhaHsJBYnl3clpefEWP/iaHU n1HeoRLus4fEoaOHkUww6OLgzVNQl/L4V8DFzB9ZB7NzNnbESfgirWcHPYT+Qz2xOt01jkJGpc3 9L12Rmc6Y= X-Gm-Gg: ASbGncsHI4WfPZAyxpqwOrYEsvPmf2e1jygso5R5ICI2RIgJ+8diiEiUi4ytxaJ6Klx tUyuuZxH2FWWlY7BqT5SmZcVlDwWUyKxTw3XYxQdn0oiR38WGvKQPqm9k6XunLyrEhPAJrhDy95 6rqU30w9+zzgXNdTE4ZFyqMzbzUnyhHpsrjjCUlvylqqOar5tIrdZUmrMel/5zULMKfpZaMZl3z c2ObAK6+tidbBvFhR3GF5LI0ZqHgY7YQ+VwD8r3YlSLJUZ24dQ0P2+dXxw/fK8Kd0h/gWxZovbw Ma9pG+hlJhdvpFiQpecf+VVM3CD+bpClUA== X-Received: by 2002:a17:903:1948:b0:224:a74:28cd with SMTP id d9443c01a7336-22a8a87d03fmr152760915ad.31.1744000257308; 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-gpio@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 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 = { static int gpio_aggregator_parse(struct gpio_aggregator *aggr) { char *args = skip_spaces(aggr->args); - char *name, *offsets, *p; + char *key, *offsets, *p; unsigned int i, n = 0; int error = 0; @@ -1007,18 +1007,18 @@ static int gpio_aggregator_parse(struct gpio_aggregator *aggr) if (!bitmap) return -ENOMEM; - args = next_arg(args, &name, &p); + args = next_arg(args, &key, &p); while (*args) { args = next_arg(args, &offsets, &p); p = get_options(offsets, 0, &error); if (error == 0 || *p) { /* Named GPIO line */ - error = gpio_aggregator_add_gpio(aggr, name, U16_MAX, &n); + error = gpio_aggregator_add_gpio(aggr, key, U16_MAX, &n); if (error) return error; - name = offsets; + key = offsets; continue; } @@ -1030,12 +1030,12 @@ static int gpio_aggregator_parse(struct gpio_aggregator *aggr) } for_each_set_bit(i, bitmap, AGGREGATOR_MAX_GPIOS) { - error = gpio_aggregator_add_gpio(aggr, name, i, &n); + error = gpio_aggregator_add_gpio(aggr, key, i, &n); if (error) return error; } - args = next_arg(args, &name, &p); + args = next_arg(args, &key, &p); } if (!n) { From patchwork Mon Apr 7 04:30:16 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Koichiro Den X-Patchwork-Id: 879193 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 64C6E222585 for ; Mon, 7 Apr 2025 04:31:03 +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-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 8EAE73F193 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-f197.google.com with SMTP id d9443c01a7336-2242ade807fso64457155ad.2 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=Wuzby29RbJkCFG2kbpCjQHIwYU6g0EkSQ+vXspU1Aue64C/MxUH8EvnoTXzlikuFXI /+EzyvGHW4lUwQXk5O8IiGPA34DijgJvB3CTrecBS0+5wN80V5iQXxx6wGS4R8LC+v1l TYxZc4Rg02z6P20ZbCcS/GScxCiAn39P/wjhwCUAFVZ0moXa7NUl6WZ6YbxKpq+YZGP9 Rf0s5w7nO6etQVRCVd5Vwaz9R5L7Jn23fKuP6fnLcWiFaKuMEqcRxZ2ntYtLyXS8sUi/ Vfcu61s68m8ee0qC/cS5Q0xwJZCOF+DRsRDj6RmdGp9iGDX/twhQrEjTI4zjrG69tcJ2 CHzQ== X-Gm-Message-State: AOJu0YyJEzrv0D/+RSqssGmRRk9ls+//FquhkX1sdL+OWmnoG1pvTbGU sdv8iID0AQanjacAAr8sW7/P5xlYl201tgzXgXewHWOtsLPfAMoPL23peSKIJt7z6NpUU5leiAY iqfaAfBY6xh6E7dY50kCBCgWozp9kJ1DBDfprGFY+NicECkwfJ+7fDLJ6B0AqH/7qOKr5L3CaZT vOQPs+jZU= X-Gm-Gg: ASbGncubMCrDPkENWO8mZzhooAkyyULcRZnT08ExFpOpjOCw0SKs/YZWdxUmcF9+dv3 RuUtyuSwr23zqaQSSQz6CRdWrAzldjHVjzuNmczBbeKQwIgam/iN62qd/ndLrm90/FFdCSFNcSS zfX0qpCzr2Q235sEsp53H3RZyHfUurSefAu8op0yBJxWzklLE3O9wZfWkzy5QFAVAIbZBrDPZ8x H3Jz5yqxSFEUVG9VUkH/l9EYZKB+Msy9dYYDCI0n8oTjIEvfMRgj4ot7Qe5INFa0sveLoQiodHV zo7zxEY6lOWaUSNTR8jjevh3GoDqsQKx/A== X-Received: by 2002:a17:902:f64a:b0:224:194c:6942 with SMTP id d9443c01a7336-22a8a8b828amr164483965ad.34.1744000259684; 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-gpio@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 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" #define AGGREGATOR_MAX_GPIOS 512 +#define AGGREGATOR_LEGACY_PREFIX "_sysfs" /* * GPIO Aggregator sysfs interface @@ -131,6 +132,14 @@ static bool gpio_aggregator_is_active(struct gpio_aggregator *aggr) return aggr->probe_data.pdev && platform_get_drvdata(aggr->probe_data.pdev); } +/* 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.pdev); +} + 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_aggregator *aggr, list_del(&line->entry); } +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); + } +} + /* * GPIO Forwarder @@ -702,7 +735,8 @@ gpio_aggregator_line_key_store(struct config_item *item, const char *page, guard(mutex)(&aggr->lock); - if (gpio_aggregator_is_active(aggr)) + if (gpio_aggregator_is_activating(aggr) || + gpio_aggregator_is_active(aggr)) return -EBUSY; kfree(line->key); @@ -739,7 +773,8 @@ gpio_aggregator_line_name_store(struct config_item *item, const char *page, guard(mutex)(&aggr->lock); - if (gpio_aggregator_is_active(aggr)) + if (gpio_aggregator_is_activating(aggr) || + gpio_aggregator_is_active(aggr)) return -EBUSY; kfree(line->name); @@ -785,7 +820,8 @@ gpio_aggregator_line_offset_store(struct config_item *item, const char *page, guard(mutex)(&aggr->lock); - if (gpio_aggregator_is_active(aggr)) + if (gpio_aggregator_is_activating(aggr) || + gpio_aggregator_is_active(aggr)) return -EBUSY; line->offset = 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; - if (live) + if (live && !aggr->init_via_sysfs) gpio_aggregator_lockup_configfs(aggr, true); scoped_guard(mutex, &aggr->lock) { - if (live == gpio_aggregator_is_active(aggr)) + if (gpio_aggregator_is_activating(aggr) || + (live == gpio_aggregator_is_active(aggr))) ret = -EPERM; else if (live) ret = gpio_aggregator_activate(aggr); @@ -859,7 +896,7 @@ gpio_aggregator_device_live_store(struct config_item *item, const char *page, * Undepend is required only if device disablement (live == 0) * succeeds or if device enablement (live == 1) fails. */ - if (live == !!ret) + if (live == !!ret && !aggr->init_via_sysfs) gpio_aggregator_lockup_configfs(aggr, false); module_put(THIS_MODULE); @@ -903,7 +940,7 @@ static void gpio_aggregator_device_release(struct config_item *item) struct gpio_aggregator *aggr = to_gpio_aggregator(item); /* - * 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 != 1 || nchar != strlen(name)) return ERR_PTR(-EINVAL); + 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); 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; + /* + * "_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)) == 0) + return ERR_PTR(-EINVAL); + /* arg space is unneeded */ ret = gpio_aggregator_alloc(&aggr, 0); if (ret) @@ -998,6 +1052,8 @@ static struct configfs_subsystem gpio_aggregator_subsys = { static int gpio_aggregator_parse(struct gpio_aggregator *aggr) { char *args = skip_spaces(aggr->args); + struct gpio_aggregator_line *line; + char name[CONFIGFS_ITEM_NAME_LEN]; char *key, *offsets, *p; unsigned int i, n = 0; int error = 0; @@ -1014,9 +1070,24 @@ static int gpio_aggregator_parse(struct gpio_aggregator *aggr) p = get_options(offsets, 0, &error); if (error == 0 || *p) { /* Named GPIO line */ + scnprintf(name, sizeof(name), "line%u", n); + line = gpio_aggregator_line_alloc(aggr, n, key, -1); + if (!line) { + error = -ENOMEM; + goto err; + } + config_group_init_type_name(&line->group, name, + &gpio_aggregator_line_type); + error = configfs_register_group(&aggr->group, + &line->group); + if (error) + goto err; + scoped_guard(mutex, &aggr->lock) + gpio_aggregator_line_add(aggr, line); + error = gpio_aggregator_add_gpio(aggr, key, U16_MAX, &n); if (error) - return error; + goto err; key = offsets; continue; @@ -1030,9 +1101,24 @@ static int gpio_aggregator_parse(struct gpio_aggregator *aggr) } for_each_set_bit(i, bitmap, AGGREGATOR_MAX_GPIOS) { + scnprintf(name, sizeof(name), "line%u", n); + line = gpio_aggregator_line_alloc(aggr, n, key, i); + if (!line) { + error = -ENOMEM; + goto err; + } + config_group_init_type_name(&line->group, name, + &gpio_aggregator_line_type); + error = configfs_register_group(&aggr->group, + &line->group); + if (error) + goto err; + scoped_guard(mutex, &aggr->lock) + gpio_aggregator_line_add(aggr, line); + error = gpio_aggregator_add_gpio(aggr, key, i, &n); if (error) - return error; + goto err; } args = next_arg(args, &key, &p); @@ -1040,15 +1126,20 @@ static int gpio_aggregator_parse(struct gpio_aggregator *aggr) if (!n) { pr_err("No GPIOs specified\n"); - return -EINVAL; + goto err; } return 0; + +err: + gpio_aggregator_free_lines(aggr); + return error; } static ssize_t gpio_aggregator_new_device_store(struct device_driver *driver, 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(struct device_driver *driver, goto free_table; } - res = 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_type); + + /* + * 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 = configfs_register_group(&gpio_aggregator_subsys.su_group, + &aggr->group); if (res) goto free_dev_id; + res = gpio_aggregator_parse(aggr); + if (res) + goto unregister_group; + gpiod_add_lookup_table(aggr->lookups); pdev = platform_device_register_simple(DRV_NAME, aggr->id, NULL, 0); @@ -1095,6 +1201,8 @@ static ssize_t gpio_aggregator_new_device_store(struct device_driver *driver, 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_aggregator_new_device = 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); } From patchwork Mon Apr 7 04:30:17 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Koichiro Den X-Patchwork-Id: 878828 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 35536223329 for ; Mon, 7 Apr 2025 04:31:04 +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=1744000266; cv=none; b=ADWTEh7v/Wkqt50biIAtyMbQqPaE5w6IX4CiWSnARmapZPAyQ/+AMoloJUsHMGcP6JZU0n59+UVemenMapAMLGDF9fRvsxlK3JvdC7VKVrzJBZAqEbnvox+mlCG/TP+RcWJFkcI97K+sowfLjvaPkttAa/LB3LkAB/kaO1Es8Bs= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1744000266; c=relaxed/simple; bh=5hW1x0h0BI/1kFl0MGB9PENf/p3rDflku5UUTYWUgwg=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=gSGdi39w5OF0maYLPYrylqvLDfK7rpqJYkCaitLhzRicjcc3A417zRvhF69Jyndr1CYM8bouTjygKew3fE9tUShOGpbU8LLq4ob66Z5bUgNVXQ9O0qGHL3Ef9S5TuZSVVNbgKXvs5+Fx8HmKxQ/6hcslYw3UsBf8FW1qhto8RV4= 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.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="iLwcuMku" 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 61BEB3F13F 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-f198.google.com with SMTP id d9443c01a7336-227ea16b03dso56751675ad.3 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=ccwSS4vtrLBQbcmIShxXaTSi6N/9NzqC6QCC0b+EYJ3OcI0g3bpi76842VoKH44j7E 6/PFvJ3sRc+5TN3lGcAQL5D0tQ7iJf5qPm02kkaGLHSx0DVdrE2OaCW1aBtu+dEiG/f0 NRciYRPP9v59F0gowGCr9lUPKBJ2Jm3LT8PqOdB5zq8tH2hLBqNcpemjYyZe/bsiDjkh BQub+3aE8I1nuuPJB9u39jSRiOPmZL3BXljKe1er2VpVjXwrwP5kk4lZ+6B1lFgQfRsp iX5+6nQ12FyI3SnWOwNNP3jsIwv1zVFarJObNK7L9rhRomqxXzKsliy9mdhCuOXyPaV+ EycQ== X-Gm-Message-State: AOJu0YwROr8Jq8FnxzneurPNkPrdd1B0LIIRCS9jWHViWmhyxnbtsuUt TGHFv1k8AijqKTYdP5DG8uvqmbaEFRMlwwHXucHx81Pv+aWCe0KeFeSU8u2ih7zdOE4p0cyqwYW q7grisi4xacE8NhQSn6vgQy0UKLOUc9Cphynz4mtvy/fA7DkLHK90nG0og/fTcCiZNBxPNVQY8m TcrhxQMEo= X-Gm-Gg: ASbGnctfftm1GI4QMYGT+AnKyNhW6Nky3ojz3WHkxh6SZ1XHlLE1TSINlp/rk+Xiubv 5zAfkd/nT+nXNSLYSJ7Z4/69Olpai658lj8LR/TMU7dxxKi37BS0BWjFi7ScT6um4g5sdKmZc5Q E4q+vJxQ/bA1GEvW9oDqbNWGy7aHDbaFBB7BzQGQn+L40OuGvfMb12uC4axFiDcHxtenqie3psB lJbuJ9/uQQWv7HDOpawjhgQkDQfK+xgOTn3rWYt5xI6HSpHOxL2wZlaytHEHdeunPlVYnutJUU2 nVWAaQLVWmmgCOye8YkZpSvt/ygWPWPyGA== X-Received: by 2002:a17:902:d50e:b0:21f:85ee:f2df with SMTP id d9443c01a7336-22a8a05524fmr151180785ad.15.1744000261817; 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-gpio@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 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; }; +struct gpio_aggregator_pdev_meta { + bool init_via_sysfs; +}; + static DEFINE_MUTEX(gpio_aggregator_lock); /* protects idr */ static DEFINE_IDR(gpio_aggregator_idr); @@ -1139,6 +1143,7 @@ static int gpio_aggregator_parse(struct gpio_aggregator *aggr) static ssize_t gpio_aggregator_new_device_store(struct device_driver *driver, const char *buf, size_t count) { + struct gpio_aggregator_pdev_meta meta = { .init_via_sysfs = 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(struct device_driver *driver, gpiod_add_lookup_table(aggr->lookups); - pdev = platform_device_register_simple(DRV_NAME, aggr->id, NULL, 0); + pdev = platform_device_register_data(NULL, DRV_NAME, aggr->id, &meta, sizeof(meta)); if (IS_ERR(pdev)) { res = PTR_ERR(pdev); goto remove_table; @@ -1276,14 +1281,15 @@ static struct attribute *gpio_aggregator_attrs[] = { }; ATTRIBUTE_GROUPS(gpio_aggregator); - /* * GPIO Aggregator platform device */ static int gpio_aggregator_probe(struct platform_device *pdev) { + struct gpio_aggregator_pdev_meta *meta; struct device *dev = &pdev->dev; + bool init_via_sysfs = false; struct gpio_desc **descs; struct gpiochip_fwd *fwd; unsigned long features; @@ -1297,10 +1303,28 @@ static int gpio_aggregator_probe(struct platform_device *pdev) if (!descs) return -ENOMEM; + meta = dev_get_platdata(&pdev->dev); + if (meta && meta->init_via_sysfs) + init_via_sysfs = true; + for (i = 0; i < n; i++) { descs[i] = 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] == ERR_PTR(-EPROBE_DEFER)) { + pr_warn("Deferred probe canceled for creation via configfs.\n"); + return -ENODEV; + } return PTR_ERR(descs[i]); + } } features = (uintptr_t)device_get_match_data(dev); From patchwork Mon Apr 7 04:30:18 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Koichiro Den X-Patchwork-Id: 879192 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 9AEA1223719 for ; Mon, 7 Apr 2025 04:31:08 +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=1744000270; cv=none; b=cpSV81CZZVLM5ou0aLPs1mu+uHUZ17H2/P+FlzZmX3B4DULWdS7fcbiXvPPQHI0Zj0pmw+GEq7BE7tGGRzxDkJnofNv+BLbevXeNt3stkSZMejENl2+FKDvfa+BaHTOnF8HRKkZ9iw6YktBM6X6fxG0QW09YA10BgV0Q9hSWV1k= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1744000270; c=relaxed/simple; bh=5z75klJulPBK3euVFo5jCw9QXY35/qZ4NCunDCR/9Jo=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=W1GO5sjCah22frmR8rZtWPGlszo3h8P098pDX9GLaYiijgCtktUdBGHFHJXq27DI53WG9WfopegdQV3xdydGOtB0yXpvy0lbkY0kO1Zq8bzw5BAGeqiz1GWh5yz9RzE4EblzN2N80JvCOocOeDnsaKwxEifB+UTgZYop0sa2Tws= 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.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="jWjvI6Rb" 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 A6EFC3F342 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-f199.google.com with SMTP id d9443c01a7336-22403329f9eso32410605ad.3 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=PENFS+rNYJuhJ9BOomcMQgcRb8gZzGDiwMcP1fa0Key0GRw/tlXJhQ82Sylho4Q73X 8UVeamQ420ENoe5pJ1XxbJz0s+Cgw8o+zMLs3yAFJLUB9Ae5nj7pwXVTdTByGHiCNQvf tHbH4LCp6M7kaXxO5Q9SRQa1ei8yoSM6k4A9fvDRpbsl+UuwcBnBV0+ioD6rC4iGG3Yl OLbmmyKD4gae6yv+PsE09q4TKgwtrQ+LBlTWgKzgjBm/YNwe2YttSbTDZrNwBfI0IZxH 6sLPbD2QLTELm+tOUKJeIn+7Jvx5R7H5vzyKuibNN2VTM62aK9AxCsyGZCIQVhfzTxDs KCIw== X-Gm-Message-State: AOJu0YxkIb09IBZC+DohE15JHL5h1GxM76JZKdQdVYkxdzEgfVg45ZWJ HFiOGOV0H2rAJ/lztdKnuDY6M9dP8XzP3z7j0o6VdilvXP2US8Fa3wri8uGjmfxBbgH59Grg5Ui oyg04xif0rdOk0et44+2rhxIWe5JjYjqcipm+HccZP2RZ8dMu8ITO4sJSDPNq1YEdTFc6+EKCFS XkxuitphI= X-Gm-Gg: ASbGncvMoewzvA3dyYwyX21A7HHLPxuP4pya3kOrWIJHJAaucLrH3Dohl2wqwa8tmmD 8mFSepHROe9p5+B+u2AlsfZASS4H8cmz21S6ynHDvwlNF7CjFP2sfbF4aOs/py4+OSHclllMF9l Uz1xm7kA0gdncA/vYHLx943PsHWrGZsR/ciY09QM5al2aWAVfkVmv8WoF6GhaPVdJ656se6PjJp CxSEcq8/mfS7EeuKWR/kjzcAbTZBi+M3saOH4EAZU25IJqWIJbnFuezPauBMPCIHysosSvdUqnI jOweVFvqNgsVs4xQorfvMRrEUyPyirbazg== X-Received: by 2002:a17:902:ce02:b0:224:160d:3f54 with SMTP id d9443c01a7336-22a8a87949cmr149795585ad.31.1744000264125; 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-gpio@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 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/Documentation/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 +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 device + 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 that 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 >= 0) lines. Configure all lines before +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 <= ``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, writing +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./``. You + 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 ------------------- From patchwork Mon Apr 7 04:30:19 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Koichiro Den X-Patchwork-Id: 878827 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 A3E33224241 for ; Mon, 7 Apr 2025 04:31:10 +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=1744000273; cv=none; b=EBtms7JzjzgoCBSodC4eCcpyWmozvzScFif3mOZ3bhWNbeWUR9qNYzerWaodFWYTy8esqw88oilvU4lvUHKdEPDpEkYEDHaMaciynJ+yOj+5y+eK+huZS4QVMlCva2cgNTLfGLaRuSwZRaAbDA/4zQ7PtoCYgV2AiPyD0nU+OrQ= 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=aJdgR7+j; 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="aJdgR7+j" 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 D87713F471 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=1744000269; bh=unE8r5bz7y+LtS0Ck6ChgHm8lSL9zhWWp92j38DBeMA=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=aJdgR7+jsJE03lmGRxhoDEl7/O9nuXpM9TZrBcwTSutPPVYk3DkNftMViAa1Lx3O4 wTkF7XLm8upDpTHc0jIixLpIawKhsjki0ChdDcI13/bL4XgjWAKNWX24UgrKodC6BX ICAYTd926a1ui2tWKIxEN6DMprja3dQwvjvuiA5aRd0bLjuSGTOaN1eS3VevIBMoYT Dnp6zwIqOfo7AthzANR2mFJnCjhonMujfQmuhUVbbtfC6lKSAMvetzqEOlh140aI2Z oxoRRKOMqA6ojJG7KY2n7fMdvb1WvtRqVkaJ5pCBJUzKiImYHPIC6q0H24VQDOM7G+ 6WRHbzzte9Ceg== Received: by mail-pg1-f199.google.com with SMTP id 41be03b00d2f7-af5268afde2so1354615a12.2 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=f1SnnXqkqZW+7fbDjzYvMlG/7afyPTtvwZXw8qAHgGSvwIcUDcmCXTxcE5eJa9IshA tNWJ5SLukwathZjoLDnr7U2UD8T8Ro41fhULgDDdYKD44bHSuGoj7X0OM3ojxvdjs5vm BTquE1bCcnn2DWyNKjOCj5XjFP3JErprnBPu/pps1lIe64By//QPeG7rPVFzbr3g1zSI n//YsQYdwubdW3DGo2zh1wLnbHdo4T7iE6kONP4e6nzO8m8s9IwVnwXt8JAi4Dk4Eo+I AxK3JehZ/ILwa3d7xBsjOYbsmq0pDD7ShpRwYUiQSmg1aJyDZG1ODKcPsYCARm1ZD7f3 wINw== X-Gm-Message-State: AOJu0YwTobLrIAP4sypx1MNdqJohWg8oNZRnu/suCT5sgBALMM04pSRC vMcnxOjHGi2hinNfNjZV77dxaKtiSpZai4oB9uS+RcCvLa7EQAcfsayYZhtdwiI9EddqkVRe8Pc iawxRWtvu0+aI0p3IstbDVPv29pVALrPG/2k6t6qZoKZfiqVLXYDCJObbEXvSVCQDb7RYrcmBEE u1nzyJqj0= X-Gm-Gg: ASbGncvuGWpqEH2YzU8sBgNa/eN+VBg/swdmHJN2kF4onbygQCTY3aC4RbWj9M/DVM+ TY2JKLmCprca3W1QsxlVJG0DYUGkCPYV1VM0jFJtjxF4paHr2/HdQ7jq7MLS4L9msDClah/+okl UCQDcJUhIQrTh9ufNTR0jgo6tCIYXWgG2F6g7FD/TMzBfiUxHuqwxYhgN2RsrwZJVZPhuvjvHfg srQzGJhrrYOdPGLN8EYe0zhRPB4Ic68NYyHAnsFNSBYKqBDR7bK1b7MYnTDp14ZhZNG/7jpYH3g u1O/WkPoxSfHG+dgUOZJqConBgBtZX0sUw== X-Received: by 2002:a17:90b:1f8f:b0:2ee:44ec:e524 with SMTP id 98e67ed59e1d1-306a624e6acmr15655831a91.35.1744000266929; 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-gpio@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 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/selftests/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 -TEST_PROGS := gpio-mockup.sh gpio-sim.sh +TEST_PROGS := gpio-mockup.sh gpio-sim.sh gpio-aggregator.sh TEST_FILES := gpio-mockup-sysfs.sh TEST_GEN_PROGS_EXTENDED := gpio-mockup-cdev gpio-chip-info gpio-line-name CFLAGS += -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=y CONFIG_GPIO_CDEV=y CONFIG_GPIO_MOCKUP=m CONFIG_GPIO_SIM=m +CONFIG_GPIO_AGGREGATOR=m diff --git a/tools/testing/selftests/gpio/gpio-aggregator.sh b/tools/testing/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=$(dirname "$0") +CONFIGFS_SIM_DIR="/sys/kernel/config/gpio-sim" +CONFIGFS_AGG_DIR="/sys/kernel/config/gpio-aggregator" +SYSFS_AGG_DIR="/sys/bus/platform/drivers/gpio-aggregator" +MODULE="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=$1 + + echo 1 > "$CONFIGFS_SIM_DIR/$CHIP/live" || fail "Unable to enable the chip" +} + +sim_disable_chip() { + local CHIP=$1 + + echo 0 > "$CONFIGFS_SIM_DIR/$CHIP/live" || fail "Unable to disable the chip" +} + +sim_configfs_cleanup() { + local NOCHECK=${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=$(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=$1 + local BANK=$2 + local CHIP_NAME=$(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=$1 + + mkdir "$CONFIGFS_AGG_DIR/$CHIP" +} + +agg_remove_chip() { + local CHIP=$1 + + find "$CONFIGFS_AGG_DIR/$CHIP/" -depth -type d -exec rmdir {} \; || \ + fail "Unable to remove $CONFIGFS_AGG_DIR/$CHIP" +} + +agg_create_line() { + local CHIP=$1 + local LINE=$2 + + mkdir "$CONFIGFS_AGG_DIR/$CHIP/$LINE" +} + +agg_remove_line() { + local CHIP=$1 + local LINE=$2 + + rmdir "$CONFIGFS_AGG_DIR/$CHIP/$LINE" +} + +agg_set_key() { + local CHIP=$1 + local LINE=$2 + local KEY=$3 + + echo "$KEY" > "$CONFIGFS_AGG_DIR/$CHIP/$LINE/key" || fail "Unable to set the lookup key" +} + +agg_set_offset() { + local CHIP=$1 + local LINE=$2 + local OFFSET=$3 + + echo "$OFFSET" > "$CONFIGFS_AGG_DIR/$CHIP/$LINE/offset" || \ + fail "Unable to set the lookup offset" +} + +agg_set_line_name() { + local CHIP=$1 + local LINE=$2 + local NAME=$3 + + echo "$NAME" > "$CONFIGFS_AGG_DIR/$CHIP/$LINE/name" || fail "Unable to set the line name" +} + +agg_enable_chip() { + local CHIP=$1 + + echo 1 > "$CONFIGFS_AGG_DIR/$CHIP/live" || fail "Unable to enable the chip" +} + +agg_disable_chip() { + local CHIP=$1 + + echo 0 > "$CONFIGFS_AGG_DIR/$CHIP/live" || fail "Unable to disable the chip" +} + +agg_configfs_cleanup() { + local NOCHECK=${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=$(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=$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=$1 + local DEV_NAME=$(agg_configfs_dev_name "$CHIP") + local CHIP_LIST=$(find "/sys/devices/platform/$DEV_NAME" \ + -maxdepth 1 -type d -name "gpiochip[0-9]*" 2> /dev/null) + local CHIP_COUNT=$(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=$1 + local N_DIR=$(ls -d $CONFIGFS_AGG_DIR/$CHIP/line[0-9]* 2> /dev/null | wc -l) + local N_LINES + + if [ "$(cat $CONFIGFS_AGG_DIR/$CHIP/live)" = 0 ]; then + echo "$N_DIR" + else + N_LINES=$( + $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 != $N_LINES ]; then + fail "Discrepancy between two sources for the number of lines" + fi + echo "$N_LINES" + fi +} + +agg_get_chip_label() { + local CHIP=$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=$1 + local OFFSET=$2 + local NAME_CONFIGFS=$(cat "$CONFIGFS_AGG_DIR/$CHIP/line${OFFSET}/name") + local NAME_CDEV + + if [ "$(cat "$CONFIGFS_AGG_DIR/$CHIP/live")" = 0 ]; then + echo "$NAME_CONFIGFS" + else + NAME_CDEV=$( + $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" != "$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 module" + +# Make sure configfs is mounted at /sys/kernel/config. Wait a bit if needed. +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="${CHIP}${BANK}_${IDX}" + LINE_DIR="$CONFIGFS_SIM_DIR/$CHIP/$BANK/line$IDX" + mkdir -p $LINE_DIR + echo "$LINE_NAME" > "$LINE_DIR/name" || fail "unable to set the line name" + 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")" = 1 || fail "chip unexpectedly dead" +test "$(agg_get_chip_label agg0)" = "$(agg_configfs_dev_name agg0)" || \ + fail "label is inconsistent" +test "$(agg_get_chip_num_lines agg0)" = "1" || fail "number of lines is not 1" +test "$(agg_get_line_name agg0 0)" = "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")" = 1 || fail "chip unexpectedly dead" +test "$(agg_get_chip_label agg0)" = "$(agg_configfs_dev_name agg0)" || \ + fail "label is inconsistent" +test "$(agg_get_chip_num_lines agg0)" = "4" || fail "number of lines is not 1" +test "$(agg_get_line_name agg0 0)" = "test0" || fail "line name is unset" +test "$(agg_get_line_name agg0 1)" = "test1" || fail "line name is unset" +test "$(agg_get_line_name agg0 2)" = "test2" || fail "line name is unset" +test "$(agg_get_line_name agg0 3)" = "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 unexpectedly enabled" +test "$(cat "$CONFIGFS_AGG_DIR/agg0/live")" = 0 || fail "chip unexpectedly 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 unexpectedly enabled" +test "$(cat "$CONFIGFS_AGG_DIR/agg0/live")" = 0 || fail "chip unexpectedly 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 unexpectedly enabled" +test "$(cat "$CONFIGFS_AGG_DIR/agg0/live")" = 0 || fail "chip unexpectedly alive" +sim_enable_chip chip0 +sleep 1 +test "$(cat "$CONFIGFS_AGG_DIR/agg0/live")" = 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=$(agg_configfs_chip_name _sysfs.0) +test "$(cat "$CONFIGFS_AGG_DIR/_sysfs.0/live")" = 1 || fail "chip unexpectedly dead" +test "$(agg_get_chip_label _sysfs.0)" = "$(agg_configfs_dev_name _sysfs.0)" || \ + fail "label is inconsistent" +test "$(agg_get_chip_num_lines _sysfs.0)" = "1" || fail "number of lines is not 1" +test "$(agg_get_line_name _sysfs.0 0)" = "" || 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=$(agg_configfs_chip_name _sysfs.0) +test "$(cat "$CONFIGFS_AGG_DIR/_sysfs.0/live")" = 1 || fail "chip unexpectedly dead" +test "$(agg_get_chip_label _sysfs.0)" = "$(agg_configfs_dev_name _sysfs.0)" || \ + fail "label is inconsistent" +test "$(agg_get_chip_num_lines _sysfs.0)" = "3" || fail "number of lines is not 3" +test "$(agg_get_line_name _sysfs.0 0)" = "" || fail "line name is unset" +test "$(agg_get_line_name _sysfs.0 1)" = "" || fail "line name is unset" +test "$(agg_get_line_name _sysfs.0 2)" = "" || 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")" = 0 || fail "chip unexpectedly alive" +sim_enable_chip chip0 +sleep 1 +CHIPNAME=$(agg_configfs_chip_name _sysfs.0) +test "$(cat "$CONFIGFS_AGG_DIR/_sysfs.0/live")" = 1 || fail "chip unexpectedly remains dead" +test "$(agg_get_chip_label _sysfs.0)" = "$(agg_configfs_dev_name _sysfs.0)" || \ + fail "label is inconsistent" +test "$(agg_get_chip_num_lines _sysfs.0)" = "1" || fail "number of lines is not 1" +test "$(agg_get_line_name _sysfs.0 0)" = "" || fail "line name unexpectedly 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)" = 0 || fail "chip unexpectedly 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)" = "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)" = "new0" || fail "line name is unset" +test "$(agg_get_line_name agg0 1)" = "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)" = "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 unexpectedly 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 unexpectedly 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 unexpectedly added" +rmdir "$CONFIGFS_AGG_DIR/agg0/line1" 2> /dev/null && fail "line unexpectedly 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 unexpectedly added" +rmdir "$CONFIGFS_AGG_DIR/_sysfs.0/line1" 2> /dev/null && fail "line unexpectedly 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 = 0" +sim_disable_chip chip0 +setup_2_2 +mkdir "$CONFIGFS_AGG_DIR/_sysfs.0/line2" 2> /dev/null && fail "line unexpectedly added" +rmdir "$CONFIGFS_AGG_DIR/_sysfs.0/line1" 2> /dev/null && fail "line unexpectedly 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")" = 0 || fail "chip unexpectedly alive" +echo 1 > "$CONFIGFS_AGG_DIR/_sysfs.0/live" 2> /dev/null && fail "chip unexpectedly 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 = 0" +setup_2_2 +agg_disable_chip _sysfs.0 +mkdir "$CONFIGFS_AGG_DIR/_sysfs.0/line2" 2> /dev/null && fail "line unexpectedly added" +rmdir "$CONFIGFS_AGG_DIR/_sysfs.0/line1" 2> /dev/null && fail "line unexpectedly 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)" = "new0" || fail "line name is unset" +test "$(agg_get_line_name _sysfs.0 1)" = "new1" || fail "line name is unset" +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 unexpectedly 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 unexpectedly 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 to be loaded" +modprobe gpio-aggregator 2> /dev/null + +echo "4. GPIO forwarder functional" +SETTINGS="chip0:bank0:2 chip0:bank1:4 chip1:bank0:6 chip1:bank1:8" +setup_4() { + local OFFSET=0 + agg_create_chip agg0 + for SETTING in $SETTINGS; do + CHIP=$(echo "$SETTING" | cut -d: -f1) + BANK=$(echo "$SETTING" | cut -d: -f2) + LINE=$(echo "$SETTING" | cut -d: -f3) + agg_create_line agg0 "line${OFFSET}" + agg_set_key agg0 "line${OFFSET}" "$(sim_get_chip_label "$CHIP" "$BANK")" + agg_set_offset agg0 "line${OFFSET}" "$LINE" + OFFSET=$(expr $OFFSET + 1) + done + agg_enable_chip agg0 +} +teardown_4() { + agg_configfs_cleanup +} + +echo "4.1. Forwarding set values" +setup_4 +OFFSET=0 +for SETTING in $SETTINGS; do + CHIP=$(echo "$SETTING" | cut -d: -f1) + BANK=$(echo "$SETTING" | cut -d: -f2) + LINE=$(echo "$SETTING" | cut -d: -f3) + DEVNAME=$(cat "$CONFIGFS_SIM_DIR/$CHIP/dev_name") + CHIPNAME=$(cat "$CONFIGFS_SIM_DIR/$CHIP/$BANK/chip_name") + VAL_PATH="/sys/devices/platform/$DEVNAME/$CHIPNAME/sim_gpio${LINE}/value" + test $(cat $VAL_PATH) = "0" || fail "incorrect value read from sysfs" + $BASE_DIR/gpio-mockup-cdev -s 1 "/dev/$(agg_configfs_chip_name agg0)" "$OFFSET" & + mock_pid=$! + sleep 0.1 # FIXME Any better way? + test "$(cat $VAL_PATH)" = "1" || fail "incorrect value read from sysfs" + kill "$mock_pid" + OFFSET=$(expr $OFFSET + 1) +done +teardown_4 + +echo "4.2. Forwarding set config" +setup_4 +OFFSET=0 +for SETTING in $SETTINGS; do + CHIP=$(echo "$SETTING" | cut -d: -f1) + BANK=$(echo "$SETTING" | cut -d: -f2) + LINE=$(echo "$SETTING" | cut -d: -f3) + DEVNAME=$(cat "$CONFIGFS_SIM_DIR/$CHIP/dev_name") + CHIPNAME=$(cat "$CONFIGFS_SIM_DIR/$CHIP/$BANK/chip_name") + VAL_PATH="/sys/devices/platform/$DEVNAME/$CHIPNAME/sim_gpio${LINE}/value" + $BASE_DIR/gpio-mockup-cdev -b pull-up "/dev/$(agg_configfs_chip_name agg0)" "$OFFSET" + test $(cat "$VAL_PATH") = "1" || fail "incorrect value read from sysfs" + OFFSET=$(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_device" + } 2> /dev/null +done & +writer_pid=$! +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"