Commit 7a56efea authored by Koichiro Den's avatar Koichiro Den Committed by Bartosz Golaszewski
Browse files

gpio: aggregator: reorder functions to prepare for configfs introduction



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: default avatarGeert Uytterhoeven <geert+renesas@glider.be>
Signed-off-by: default avatarKoichiro Den <koichiro.den@canonical.com>
Link: https://lore.kernel.org/r/20250407043019.4105613-2-koichiro.den@canonical.com


Signed-off-by: default avatarBartosz Golaszewski <bartosz.golaszewski@linaro.org>
parent dad3386b
Loading
Loading
Loading
Loading
+192 −188
Original line number Diff line number Diff line
@@ -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);