162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci//
362306a36Sopenharmony_ci// GPIO Aggregator
462306a36Sopenharmony_ci//
562306a36Sopenharmony_ci// Copyright (C) 2019-2020 Glider bv
662306a36Sopenharmony_ci
762306a36Sopenharmony_ci#define DRV_NAME       "gpio-aggregator"
862306a36Sopenharmony_ci#define pr_fmt(fmt)	DRV_NAME ": " fmt
962306a36Sopenharmony_ci
1062306a36Sopenharmony_ci#include <linux/bitmap.h>
1162306a36Sopenharmony_ci#include <linux/bitops.h>
1262306a36Sopenharmony_ci#include <linux/ctype.h>
1362306a36Sopenharmony_ci#include <linux/delay.h>
1462306a36Sopenharmony_ci#include <linux/idr.h>
1562306a36Sopenharmony_ci#include <linux/kernel.h>
1662306a36Sopenharmony_ci#include <linux/mod_devicetable.h>
1762306a36Sopenharmony_ci#include <linux/module.h>
1862306a36Sopenharmony_ci#include <linux/mutex.h>
1962306a36Sopenharmony_ci#include <linux/overflow.h>
2062306a36Sopenharmony_ci#include <linux/platform_device.h>
2162306a36Sopenharmony_ci#include <linux/property.h>
2262306a36Sopenharmony_ci#include <linux/slab.h>
2362306a36Sopenharmony_ci#include <linux/spinlock.h>
2462306a36Sopenharmony_ci#include <linux/string.h>
2562306a36Sopenharmony_ci
2662306a36Sopenharmony_ci#include <linux/gpio/consumer.h>
2762306a36Sopenharmony_ci#include <linux/gpio/driver.h>
2862306a36Sopenharmony_ci#include <linux/gpio/machine.h>
2962306a36Sopenharmony_ci
3062306a36Sopenharmony_ci#define AGGREGATOR_MAX_GPIOS 512
3162306a36Sopenharmony_ci
3262306a36Sopenharmony_ci/*
3362306a36Sopenharmony_ci * GPIO Aggregator sysfs interface
3462306a36Sopenharmony_ci */
3562306a36Sopenharmony_ci
3662306a36Sopenharmony_cistruct gpio_aggregator {
3762306a36Sopenharmony_ci	struct gpiod_lookup_table *lookups;
3862306a36Sopenharmony_ci	struct platform_device *pdev;
3962306a36Sopenharmony_ci	char args[];
4062306a36Sopenharmony_ci};
4162306a36Sopenharmony_ci
4262306a36Sopenharmony_cistatic DEFINE_MUTEX(gpio_aggregator_lock);	/* protects idr */
4362306a36Sopenharmony_cistatic DEFINE_IDR(gpio_aggregator_idr);
4462306a36Sopenharmony_ci
4562306a36Sopenharmony_cistatic int aggr_add_gpio(struct gpio_aggregator *aggr, const char *key,
4662306a36Sopenharmony_ci			 int hwnum, unsigned int *n)
4762306a36Sopenharmony_ci{
4862306a36Sopenharmony_ci	struct gpiod_lookup_table *lookups;
4962306a36Sopenharmony_ci
5062306a36Sopenharmony_ci	lookups = krealloc(aggr->lookups, struct_size(lookups, table, *n + 2),
5162306a36Sopenharmony_ci			   GFP_KERNEL);
5262306a36Sopenharmony_ci	if (!lookups)
5362306a36Sopenharmony_ci		return -ENOMEM;
5462306a36Sopenharmony_ci
5562306a36Sopenharmony_ci	lookups->table[*n] = GPIO_LOOKUP_IDX(key, hwnum, NULL, *n, 0);
5662306a36Sopenharmony_ci
5762306a36Sopenharmony_ci	(*n)++;
5862306a36Sopenharmony_ci	memset(&lookups->table[*n], 0, sizeof(lookups->table[*n]));
5962306a36Sopenharmony_ci
6062306a36Sopenharmony_ci	aggr->lookups = lookups;
6162306a36Sopenharmony_ci	return 0;
6262306a36Sopenharmony_ci}
6362306a36Sopenharmony_ci
6462306a36Sopenharmony_cistatic int aggr_parse(struct gpio_aggregator *aggr)
6562306a36Sopenharmony_ci{
6662306a36Sopenharmony_ci	char *args = skip_spaces(aggr->args);
6762306a36Sopenharmony_ci	char *name, *offsets, *p;
6862306a36Sopenharmony_ci	unsigned long *bitmap;
6962306a36Sopenharmony_ci	unsigned int i, n = 0;
7062306a36Sopenharmony_ci	int error = 0;
7162306a36Sopenharmony_ci
7262306a36Sopenharmony_ci	bitmap = bitmap_alloc(AGGREGATOR_MAX_GPIOS, GFP_KERNEL);
7362306a36Sopenharmony_ci	if (!bitmap)
7462306a36Sopenharmony_ci		return -ENOMEM;
7562306a36Sopenharmony_ci
7662306a36Sopenharmony_ci	args = next_arg(args, &name, &p);
7762306a36Sopenharmony_ci	while (*args) {
7862306a36Sopenharmony_ci		args = next_arg(args, &offsets, &p);
7962306a36Sopenharmony_ci
8062306a36Sopenharmony_ci		p = get_options(offsets, 0, &error);
8162306a36Sopenharmony_ci		if (error == 0 || *p) {
8262306a36Sopenharmony_ci			/* Named GPIO line */
8362306a36Sopenharmony_ci			error = aggr_add_gpio(aggr, name, U16_MAX, &n);
8462306a36Sopenharmony_ci			if (error)
8562306a36Sopenharmony_ci				goto free_bitmap;
8662306a36Sopenharmony_ci
8762306a36Sopenharmony_ci			name = offsets;
8862306a36Sopenharmony_ci			continue;
8962306a36Sopenharmony_ci		}
9062306a36Sopenharmony_ci
9162306a36Sopenharmony_ci		/* GPIO chip + offset(s) */
9262306a36Sopenharmony_ci		error = bitmap_parselist(offsets, bitmap, AGGREGATOR_MAX_GPIOS);
9362306a36Sopenharmony_ci		if (error) {
9462306a36Sopenharmony_ci			pr_err("Cannot parse %s: %d\n", offsets, error);
9562306a36Sopenharmony_ci			goto free_bitmap;
9662306a36Sopenharmony_ci		}
9762306a36Sopenharmony_ci
9862306a36Sopenharmony_ci		for_each_set_bit(i, bitmap, AGGREGATOR_MAX_GPIOS) {
9962306a36Sopenharmony_ci			error = aggr_add_gpio(aggr, name, i, &n);
10062306a36Sopenharmony_ci			if (error)
10162306a36Sopenharmony_ci				goto free_bitmap;
10262306a36Sopenharmony_ci		}
10362306a36Sopenharmony_ci
10462306a36Sopenharmony_ci		args = next_arg(args, &name, &p);
10562306a36Sopenharmony_ci	}
10662306a36Sopenharmony_ci
10762306a36Sopenharmony_ci	if (!n) {
10862306a36Sopenharmony_ci		pr_err("No GPIOs specified\n");
10962306a36Sopenharmony_ci		error = -EINVAL;
11062306a36Sopenharmony_ci	}
11162306a36Sopenharmony_ci
11262306a36Sopenharmony_cifree_bitmap:
11362306a36Sopenharmony_ci	bitmap_free(bitmap);
11462306a36Sopenharmony_ci	return error;
11562306a36Sopenharmony_ci}
11662306a36Sopenharmony_ci
11762306a36Sopenharmony_cistatic ssize_t new_device_store(struct device_driver *driver, const char *buf,
11862306a36Sopenharmony_ci				size_t count)
11962306a36Sopenharmony_ci{
12062306a36Sopenharmony_ci	struct gpio_aggregator *aggr;
12162306a36Sopenharmony_ci	struct platform_device *pdev;
12262306a36Sopenharmony_ci	int res, id;
12362306a36Sopenharmony_ci
12462306a36Sopenharmony_ci	/* kernfs guarantees string termination, so count + 1 is safe */
12562306a36Sopenharmony_ci	aggr = kzalloc(sizeof(*aggr) + count + 1, GFP_KERNEL);
12662306a36Sopenharmony_ci	if (!aggr)
12762306a36Sopenharmony_ci		return -ENOMEM;
12862306a36Sopenharmony_ci
12962306a36Sopenharmony_ci	memcpy(aggr->args, buf, count + 1);
13062306a36Sopenharmony_ci
13162306a36Sopenharmony_ci	aggr->lookups = kzalloc(struct_size(aggr->lookups, table, 1),
13262306a36Sopenharmony_ci				GFP_KERNEL);
13362306a36Sopenharmony_ci	if (!aggr->lookups) {
13462306a36Sopenharmony_ci		res = -ENOMEM;
13562306a36Sopenharmony_ci		goto free_ga;
13662306a36Sopenharmony_ci	}
13762306a36Sopenharmony_ci
13862306a36Sopenharmony_ci	mutex_lock(&gpio_aggregator_lock);
13962306a36Sopenharmony_ci	id = idr_alloc(&gpio_aggregator_idr, aggr, 0, 0, GFP_KERNEL);
14062306a36Sopenharmony_ci	mutex_unlock(&gpio_aggregator_lock);
14162306a36Sopenharmony_ci
14262306a36Sopenharmony_ci	if (id < 0) {
14362306a36Sopenharmony_ci		res = id;
14462306a36Sopenharmony_ci		goto free_table;
14562306a36Sopenharmony_ci	}
14662306a36Sopenharmony_ci
14762306a36Sopenharmony_ci	aggr->lookups->dev_id = kasprintf(GFP_KERNEL, "%s.%d", DRV_NAME, id);
14862306a36Sopenharmony_ci	if (!aggr->lookups->dev_id) {
14962306a36Sopenharmony_ci		res = -ENOMEM;
15062306a36Sopenharmony_ci		goto remove_idr;
15162306a36Sopenharmony_ci	}
15262306a36Sopenharmony_ci
15362306a36Sopenharmony_ci	res = aggr_parse(aggr);
15462306a36Sopenharmony_ci	if (res)
15562306a36Sopenharmony_ci		goto free_dev_id;
15662306a36Sopenharmony_ci
15762306a36Sopenharmony_ci	gpiod_add_lookup_table(aggr->lookups);
15862306a36Sopenharmony_ci
15962306a36Sopenharmony_ci	pdev = platform_device_register_simple(DRV_NAME, id, NULL, 0);
16062306a36Sopenharmony_ci	if (IS_ERR(pdev)) {
16162306a36Sopenharmony_ci		res = PTR_ERR(pdev);
16262306a36Sopenharmony_ci		goto remove_table;
16362306a36Sopenharmony_ci	}
16462306a36Sopenharmony_ci
16562306a36Sopenharmony_ci	aggr->pdev = pdev;
16662306a36Sopenharmony_ci	return count;
16762306a36Sopenharmony_ci
16862306a36Sopenharmony_ciremove_table:
16962306a36Sopenharmony_ci	gpiod_remove_lookup_table(aggr->lookups);
17062306a36Sopenharmony_cifree_dev_id:
17162306a36Sopenharmony_ci	kfree(aggr->lookups->dev_id);
17262306a36Sopenharmony_ciremove_idr:
17362306a36Sopenharmony_ci	mutex_lock(&gpio_aggregator_lock);
17462306a36Sopenharmony_ci	idr_remove(&gpio_aggregator_idr, id);
17562306a36Sopenharmony_ci	mutex_unlock(&gpio_aggregator_lock);
17662306a36Sopenharmony_cifree_table:
17762306a36Sopenharmony_ci	kfree(aggr->lookups);
17862306a36Sopenharmony_cifree_ga:
17962306a36Sopenharmony_ci	kfree(aggr);
18062306a36Sopenharmony_ci	return res;
18162306a36Sopenharmony_ci}
18262306a36Sopenharmony_ci
18362306a36Sopenharmony_cistatic DRIVER_ATTR_WO(new_device);
18462306a36Sopenharmony_ci
18562306a36Sopenharmony_cistatic void gpio_aggregator_free(struct gpio_aggregator *aggr)
18662306a36Sopenharmony_ci{
18762306a36Sopenharmony_ci	platform_device_unregister(aggr->pdev);
18862306a36Sopenharmony_ci	gpiod_remove_lookup_table(aggr->lookups);
18962306a36Sopenharmony_ci	kfree(aggr->lookups->dev_id);
19062306a36Sopenharmony_ci	kfree(aggr->lookups);
19162306a36Sopenharmony_ci	kfree(aggr);
19262306a36Sopenharmony_ci}
19362306a36Sopenharmony_ci
19462306a36Sopenharmony_cistatic ssize_t delete_device_store(struct device_driver *driver,
19562306a36Sopenharmony_ci				   const char *buf, size_t count)
19662306a36Sopenharmony_ci{
19762306a36Sopenharmony_ci	struct gpio_aggregator *aggr;
19862306a36Sopenharmony_ci	unsigned int id;
19962306a36Sopenharmony_ci	int error;
20062306a36Sopenharmony_ci
20162306a36Sopenharmony_ci	if (!str_has_prefix(buf, DRV_NAME "."))
20262306a36Sopenharmony_ci		return -EINVAL;
20362306a36Sopenharmony_ci
20462306a36Sopenharmony_ci	error = kstrtouint(buf + strlen(DRV_NAME "."), 10, &id);
20562306a36Sopenharmony_ci	if (error)
20662306a36Sopenharmony_ci		return error;
20762306a36Sopenharmony_ci
20862306a36Sopenharmony_ci	mutex_lock(&gpio_aggregator_lock);
20962306a36Sopenharmony_ci	aggr = idr_remove(&gpio_aggregator_idr, id);
21062306a36Sopenharmony_ci	mutex_unlock(&gpio_aggregator_lock);
21162306a36Sopenharmony_ci	if (!aggr)
21262306a36Sopenharmony_ci		return -ENOENT;
21362306a36Sopenharmony_ci
21462306a36Sopenharmony_ci	gpio_aggregator_free(aggr);
21562306a36Sopenharmony_ci	return count;
21662306a36Sopenharmony_ci}
21762306a36Sopenharmony_cistatic DRIVER_ATTR_WO(delete_device);
21862306a36Sopenharmony_ci
21962306a36Sopenharmony_cistatic struct attribute *gpio_aggregator_attrs[] = {
22062306a36Sopenharmony_ci	&driver_attr_new_device.attr,
22162306a36Sopenharmony_ci	&driver_attr_delete_device.attr,
22262306a36Sopenharmony_ci	NULL
22362306a36Sopenharmony_ci};
22462306a36Sopenharmony_ciATTRIBUTE_GROUPS(gpio_aggregator);
22562306a36Sopenharmony_ci
22662306a36Sopenharmony_cistatic int __exit gpio_aggregator_idr_remove(int id, void *p, void *data)
22762306a36Sopenharmony_ci{
22862306a36Sopenharmony_ci	gpio_aggregator_free(p);
22962306a36Sopenharmony_ci	return 0;
23062306a36Sopenharmony_ci}
23162306a36Sopenharmony_ci
23262306a36Sopenharmony_cistatic void __exit gpio_aggregator_remove_all(void)
23362306a36Sopenharmony_ci{
23462306a36Sopenharmony_ci	mutex_lock(&gpio_aggregator_lock);
23562306a36Sopenharmony_ci	idr_for_each(&gpio_aggregator_idr, gpio_aggregator_idr_remove, NULL);
23662306a36Sopenharmony_ci	idr_destroy(&gpio_aggregator_idr);
23762306a36Sopenharmony_ci	mutex_unlock(&gpio_aggregator_lock);
23862306a36Sopenharmony_ci}
23962306a36Sopenharmony_ci
24062306a36Sopenharmony_ci
24162306a36Sopenharmony_ci/*
24262306a36Sopenharmony_ci *  GPIO Forwarder
24362306a36Sopenharmony_ci */
24462306a36Sopenharmony_ci
24562306a36Sopenharmony_cistruct gpiochip_fwd_timing {
24662306a36Sopenharmony_ci	u32 ramp_up_us;
24762306a36Sopenharmony_ci	u32 ramp_down_us;
24862306a36Sopenharmony_ci};
24962306a36Sopenharmony_ci
25062306a36Sopenharmony_cistruct gpiochip_fwd {
25162306a36Sopenharmony_ci	struct gpio_chip chip;
25262306a36Sopenharmony_ci	struct gpio_desc **descs;
25362306a36Sopenharmony_ci	union {
25462306a36Sopenharmony_ci		struct mutex mlock;	/* protects tmp[] if can_sleep */
25562306a36Sopenharmony_ci		spinlock_t slock;	/* protects tmp[] if !can_sleep */
25662306a36Sopenharmony_ci	};
25762306a36Sopenharmony_ci	struct gpiochip_fwd_timing *delay_timings;
25862306a36Sopenharmony_ci	unsigned long tmp[];		/* values and descs for multiple ops */
25962306a36Sopenharmony_ci};
26062306a36Sopenharmony_ci
26162306a36Sopenharmony_ci#define fwd_tmp_values(fwd)	&(fwd)->tmp[0]
26262306a36Sopenharmony_ci#define fwd_tmp_descs(fwd)	(void *)&(fwd)->tmp[BITS_TO_LONGS((fwd)->chip.ngpio)]
26362306a36Sopenharmony_ci
26462306a36Sopenharmony_ci#define fwd_tmp_size(ngpios)	(BITS_TO_LONGS((ngpios)) + (ngpios))
26562306a36Sopenharmony_ci
26662306a36Sopenharmony_cistatic int gpio_fwd_get_direction(struct gpio_chip *chip, unsigned int offset)
26762306a36Sopenharmony_ci{
26862306a36Sopenharmony_ci	struct gpiochip_fwd *fwd = gpiochip_get_data(chip);
26962306a36Sopenharmony_ci
27062306a36Sopenharmony_ci	return gpiod_get_direction(fwd->descs[offset]);
27162306a36Sopenharmony_ci}
27262306a36Sopenharmony_ci
27362306a36Sopenharmony_cistatic int gpio_fwd_direction_input(struct gpio_chip *chip, unsigned int offset)
27462306a36Sopenharmony_ci{
27562306a36Sopenharmony_ci	struct gpiochip_fwd *fwd = gpiochip_get_data(chip);
27662306a36Sopenharmony_ci
27762306a36Sopenharmony_ci	return gpiod_direction_input(fwd->descs[offset]);
27862306a36Sopenharmony_ci}
27962306a36Sopenharmony_ci
28062306a36Sopenharmony_cistatic int gpio_fwd_direction_output(struct gpio_chip *chip,
28162306a36Sopenharmony_ci				     unsigned int offset, int value)
28262306a36Sopenharmony_ci{
28362306a36Sopenharmony_ci	struct gpiochip_fwd *fwd = gpiochip_get_data(chip);
28462306a36Sopenharmony_ci
28562306a36Sopenharmony_ci	return gpiod_direction_output(fwd->descs[offset], value);
28662306a36Sopenharmony_ci}
28762306a36Sopenharmony_ci
28862306a36Sopenharmony_cistatic int gpio_fwd_get(struct gpio_chip *chip, unsigned int offset)
28962306a36Sopenharmony_ci{
29062306a36Sopenharmony_ci	struct gpiochip_fwd *fwd = gpiochip_get_data(chip);
29162306a36Sopenharmony_ci
29262306a36Sopenharmony_ci	return chip->can_sleep ? gpiod_get_value_cansleep(fwd->descs[offset])
29362306a36Sopenharmony_ci			       : gpiod_get_value(fwd->descs[offset]);
29462306a36Sopenharmony_ci}
29562306a36Sopenharmony_ci
29662306a36Sopenharmony_cistatic int gpio_fwd_get_multiple(struct gpiochip_fwd *fwd, unsigned long *mask,
29762306a36Sopenharmony_ci				 unsigned long *bits)
29862306a36Sopenharmony_ci{
29962306a36Sopenharmony_ci	struct gpio_desc **descs = fwd_tmp_descs(fwd);
30062306a36Sopenharmony_ci	unsigned long *values = fwd_tmp_values(fwd);
30162306a36Sopenharmony_ci	unsigned int i, j = 0;
30262306a36Sopenharmony_ci	int error;
30362306a36Sopenharmony_ci
30462306a36Sopenharmony_ci	bitmap_clear(values, 0, fwd->chip.ngpio);
30562306a36Sopenharmony_ci	for_each_set_bit(i, mask, fwd->chip.ngpio)
30662306a36Sopenharmony_ci		descs[j++] = fwd->descs[i];
30762306a36Sopenharmony_ci
30862306a36Sopenharmony_ci	if (fwd->chip.can_sleep)
30962306a36Sopenharmony_ci		error = gpiod_get_array_value_cansleep(j, descs, NULL, values);
31062306a36Sopenharmony_ci	else
31162306a36Sopenharmony_ci		error = gpiod_get_array_value(j, descs, NULL, values);
31262306a36Sopenharmony_ci	if (error)
31362306a36Sopenharmony_ci		return error;
31462306a36Sopenharmony_ci
31562306a36Sopenharmony_ci	j = 0;
31662306a36Sopenharmony_ci	for_each_set_bit(i, mask, fwd->chip.ngpio)
31762306a36Sopenharmony_ci		__assign_bit(i, bits, test_bit(j++, values));
31862306a36Sopenharmony_ci
31962306a36Sopenharmony_ci	return 0;
32062306a36Sopenharmony_ci}
32162306a36Sopenharmony_ci
32262306a36Sopenharmony_cistatic int gpio_fwd_get_multiple_locked(struct gpio_chip *chip,
32362306a36Sopenharmony_ci					unsigned long *mask, unsigned long *bits)
32462306a36Sopenharmony_ci{
32562306a36Sopenharmony_ci	struct gpiochip_fwd *fwd = gpiochip_get_data(chip);
32662306a36Sopenharmony_ci	unsigned long flags;
32762306a36Sopenharmony_ci	int error;
32862306a36Sopenharmony_ci
32962306a36Sopenharmony_ci	if (chip->can_sleep) {
33062306a36Sopenharmony_ci		mutex_lock(&fwd->mlock);
33162306a36Sopenharmony_ci		error = gpio_fwd_get_multiple(fwd, mask, bits);
33262306a36Sopenharmony_ci		mutex_unlock(&fwd->mlock);
33362306a36Sopenharmony_ci	} else {
33462306a36Sopenharmony_ci		spin_lock_irqsave(&fwd->slock, flags);
33562306a36Sopenharmony_ci		error = gpio_fwd_get_multiple(fwd, mask, bits);
33662306a36Sopenharmony_ci		spin_unlock_irqrestore(&fwd->slock, flags);
33762306a36Sopenharmony_ci	}
33862306a36Sopenharmony_ci
33962306a36Sopenharmony_ci	return error;
34062306a36Sopenharmony_ci}
34162306a36Sopenharmony_ci
34262306a36Sopenharmony_cistatic void gpio_fwd_delay(struct gpio_chip *chip, unsigned int offset, int value)
34362306a36Sopenharmony_ci{
34462306a36Sopenharmony_ci	struct gpiochip_fwd *fwd = gpiochip_get_data(chip);
34562306a36Sopenharmony_ci	const struct gpiochip_fwd_timing *delay_timings;
34662306a36Sopenharmony_ci	bool is_active_low = gpiod_is_active_low(fwd->descs[offset]);
34762306a36Sopenharmony_ci	u32 delay_us;
34862306a36Sopenharmony_ci
34962306a36Sopenharmony_ci	delay_timings = &fwd->delay_timings[offset];
35062306a36Sopenharmony_ci	if ((!is_active_low && value) || (is_active_low && !value))
35162306a36Sopenharmony_ci		delay_us = delay_timings->ramp_up_us;
35262306a36Sopenharmony_ci	else
35362306a36Sopenharmony_ci		delay_us = delay_timings->ramp_down_us;
35462306a36Sopenharmony_ci	if (!delay_us)
35562306a36Sopenharmony_ci		return;
35662306a36Sopenharmony_ci
35762306a36Sopenharmony_ci	if (chip->can_sleep)
35862306a36Sopenharmony_ci		fsleep(delay_us);
35962306a36Sopenharmony_ci	else
36062306a36Sopenharmony_ci		udelay(delay_us);
36162306a36Sopenharmony_ci}
36262306a36Sopenharmony_ci
36362306a36Sopenharmony_cistatic void gpio_fwd_set(struct gpio_chip *chip, unsigned int offset, int value)
36462306a36Sopenharmony_ci{
36562306a36Sopenharmony_ci	struct gpiochip_fwd *fwd = gpiochip_get_data(chip);
36662306a36Sopenharmony_ci
36762306a36Sopenharmony_ci	if (chip->can_sleep)
36862306a36Sopenharmony_ci		gpiod_set_value_cansleep(fwd->descs[offset], value);
36962306a36Sopenharmony_ci	else
37062306a36Sopenharmony_ci		gpiod_set_value(fwd->descs[offset], value);
37162306a36Sopenharmony_ci
37262306a36Sopenharmony_ci	if (fwd->delay_timings)
37362306a36Sopenharmony_ci		gpio_fwd_delay(chip, offset, value);
37462306a36Sopenharmony_ci}
37562306a36Sopenharmony_ci
37662306a36Sopenharmony_cistatic void gpio_fwd_set_multiple(struct gpiochip_fwd *fwd, unsigned long *mask,
37762306a36Sopenharmony_ci				  unsigned long *bits)
37862306a36Sopenharmony_ci{
37962306a36Sopenharmony_ci	struct gpio_desc **descs = fwd_tmp_descs(fwd);
38062306a36Sopenharmony_ci	unsigned long *values = fwd_tmp_values(fwd);
38162306a36Sopenharmony_ci	unsigned int i, j = 0;
38262306a36Sopenharmony_ci
38362306a36Sopenharmony_ci	for_each_set_bit(i, mask, fwd->chip.ngpio) {
38462306a36Sopenharmony_ci		__assign_bit(j, values, test_bit(i, bits));
38562306a36Sopenharmony_ci		descs[j++] = fwd->descs[i];
38662306a36Sopenharmony_ci	}
38762306a36Sopenharmony_ci
38862306a36Sopenharmony_ci	if (fwd->chip.can_sleep)
38962306a36Sopenharmony_ci		gpiod_set_array_value_cansleep(j, descs, NULL, values);
39062306a36Sopenharmony_ci	else
39162306a36Sopenharmony_ci		gpiod_set_array_value(j, descs, NULL, values);
39262306a36Sopenharmony_ci}
39362306a36Sopenharmony_ci
39462306a36Sopenharmony_cistatic void gpio_fwd_set_multiple_locked(struct gpio_chip *chip,
39562306a36Sopenharmony_ci					 unsigned long *mask, unsigned long *bits)
39662306a36Sopenharmony_ci{
39762306a36Sopenharmony_ci	struct gpiochip_fwd *fwd = gpiochip_get_data(chip);
39862306a36Sopenharmony_ci	unsigned long flags;
39962306a36Sopenharmony_ci
40062306a36Sopenharmony_ci	if (chip->can_sleep) {
40162306a36Sopenharmony_ci		mutex_lock(&fwd->mlock);
40262306a36Sopenharmony_ci		gpio_fwd_set_multiple(fwd, mask, bits);
40362306a36Sopenharmony_ci		mutex_unlock(&fwd->mlock);
40462306a36Sopenharmony_ci	} else {
40562306a36Sopenharmony_ci		spin_lock_irqsave(&fwd->slock, flags);
40662306a36Sopenharmony_ci		gpio_fwd_set_multiple(fwd, mask, bits);
40762306a36Sopenharmony_ci		spin_unlock_irqrestore(&fwd->slock, flags);
40862306a36Sopenharmony_ci	}
40962306a36Sopenharmony_ci}
41062306a36Sopenharmony_ci
41162306a36Sopenharmony_cistatic int gpio_fwd_set_config(struct gpio_chip *chip, unsigned int offset,
41262306a36Sopenharmony_ci			       unsigned long config)
41362306a36Sopenharmony_ci{
41462306a36Sopenharmony_ci	struct gpiochip_fwd *fwd = gpiochip_get_data(chip);
41562306a36Sopenharmony_ci
41662306a36Sopenharmony_ci	return gpiod_set_config(fwd->descs[offset], config);
41762306a36Sopenharmony_ci}
41862306a36Sopenharmony_ci
41962306a36Sopenharmony_cistatic int gpio_fwd_to_irq(struct gpio_chip *chip, unsigned int offset)
42062306a36Sopenharmony_ci{
42162306a36Sopenharmony_ci	struct gpiochip_fwd *fwd = gpiochip_get_data(chip);
42262306a36Sopenharmony_ci
42362306a36Sopenharmony_ci	return gpiod_to_irq(fwd->descs[offset]);
42462306a36Sopenharmony_ci}
42562306a36Sopenharmony_ci
42662306a36Sopenharmony_ci/*
42762306a36Sopenharmony_ci * The GPIO delay provides a way to configure platform specific delays
42862306a36Sopenharmony_ci * for the GPIO ramp-up or ramp-down delays. This can serve the following
42962306a36Sopenharmony_ci * purposes:
43062306a36Sopenharmony_ci *   - Open-drain output using an RC filter
43162306a36Sopenharmony_ci */
43262306a36Sopenharmony_ci#define FWD_FEATURE_DELAY		BIT(0)
43362306a36Sopenharmony_ci
43462306a36Sopenharmony_ci#ifdef CONFIG_OF_GPIO
43562306a36Sopenharmony_cistatic int gpiochip_fwd_delay_of_xlate(struct gpio_chip *chip,
43662306a36Sopenharmony_ci				       const struct of_phandle_args *gpiospec,
43762306a36Sopenharmony_ci				       u32 *flags)
43862306a36Sopenharmony_ci{
43962306a36Sopenharmony_ci	struct gpiochip_fwd *fwd = gpiochip_get_data(chip);
44062306a36Sopenharmony_ci	struct gpiochip_fwd_timing *timings;
44162306a36Sopenharmony_ci	u32 line;
44262306a36Sopenharmony_ci
44362306a36Sopenharmony_ci	if (gpiospec->args_count != chip->of_gpio_n_cells)
44462306a36Sopenharmony_ci		return -EINVAL;
44562306a36Sopenharmony_ci
44662306a36Sopenharmony_ci	line = gpiospec->args[0];
44762306a36Sopenharmony_ci	if (line >= chip->ngpio)
44862306a36Sopenharmony_ci		return -EINVAL;
44962306a36Sopenharmony_ci
45062306a36Sopenharmony_ci	timings = &fwd->delay_timings[line];
45162306a36Sopenharmony_ci	timings->ramp_up_us = gpiospec->args[1];
45262306a36Sopenharmony_ci	timings->ramp_down_us = gpiospec->args[2];
45362306a36Sopenharmony_ci
45462306a36Sopenharmony_ci	return line;
45562306a36Sopenharmony_ci}
45662306a36Sopenharmony_ci
45762306a36Sopenharmony_cistatic int gpiochip_fwd_setup_delay_line(struct device *dev, struct gpio_chip *chip,
45862306a36Sopenharmony_ci					 struct gpiochip_fwd *fwd)
45962306a36Sopenharmony_ci{
46062306a36Sopenharmony_ci	fwd->delay_timings = devm_kcalloc(dev, chip->ngpio,
46162306a36Sopenharmony_ci					  sizeof(*fwd->delay_timings),
46262306a36Sopenharmony_ci					  GFP_KERNEL);
46362306a36Sopenharmony_ci	if (!fwd->delay_timings)
46462306a36Sopenharmony_ci		return -ENOMEM;
46562306a36Sopenharmony_ci
46662306a36Sopenharmony_ci	chip->of_xlate = gpiochip_fwd_delay_of_xlate;
46762306a36Sopenharmony_ci	chip->of_gpio_n_cells = 3;
46862306a36Sopenharmony_ci
46962306a36Sopenharmony_ci	return 0;
47062306a36Sopenharmony_ci}
47162306a36Sopenharmony_ci#else
47262306a36Sopenharmony_cistatic int gpiochip_fwd_setup_delay_line(struct device *dev, struct gpio_chip *chip,
47362306a36Sopenharmony_ci					 struct gpiochip_fwd *fwd)
47462306a36Sopenharmony_ci{
47562306a36Sopenharmony_ci	return 0;
47662306a36Sopenharmony_ci}
47762306a36Sopenharmony_ci#endif	/* !CONFIG_OF_GPIO */
47862306a36Sopenharmony_ci
47962306a36Sopenharmony_ci/**
48062306a36Sopenharmony_ci * gpiochip_fwd_create() - Create a new GPIO forwarder
48162306a36Sopenharmony_ci * @dev: Parent device pointer
48262306a36Sopenharmony_ci * @ngpios: Number of GPIOs in the forwarder.
48362306a36Sopenharmony_ci * @descs: Array containing the GPIO descriptors to forward to.
48462306a36Sopenharmony_ci *         This array must contain @ngpios entries, and must not be deallocated
48562306a36Sopenharmony_ci *         before the forwarder has been destroyed again.
48662306a36Sopenharmony_ci * @features: Bitwise ORed features as defined with FWD_FEATURE_*.
48762306a36Sopenharmony_ci *
48862306a36Sopenharmony_ci * This function creates a new gpiochip, which forwards all GPIO operations to
48962306a36Sopenharmony_ci * the passed GPIO descriptors.
49062306a36Sopenharmony_ci *
49162306a36Sopenharmony_ci * Return: An opaque object pointer, or an ERR_PTR()-encoded negative error
49262306a36Sopenharmony_ci *         code on failure.
49362306a36Sopenharmony_ci */
49462306a36Sopenharmony_cistatic struct gpiochip_fwd *gpiochip_fwd_create(struct device *dev,
49562306a36Sopenharmony_ci						unsigned int ngpios,
49662306a36Sopenharmony_ci						struct gpio_desc *descs[],
49762306a36Sopenharmony_ci						unsigned long features)
49862306a36Sopenharmony_ci{
49962306a36Sopenharmony_ci	const char *label = dev_name(dev);
50062306a36Sopenharmony_ci	struct gpiochip_fwd *fwd;
50162306a36Sopenharmony_ci	struct gpio_chip *chip;
50262306a36Sopenharmony_ci	unsigned int i;
50362306a36Sopenharmony_ci	int error;
50462306a36Sopenharmony_ci
50562306a36Sopenharmony_ci	fwd = devm_kzalloc(dev, struct_size(fwd, tmp, fwd_tmp_size(ngpios)),
50662306a36Sopenharmony_ci			   GFP_KERNEL);
50762306a36Sopenharmony_ci	if (!fwd)
50862306a36Sopenharmony_ci		return ERR_PTR(-ENOMEM);
50962306a36Sopenharmony_ci
51062306a36Sopenharmony_ci	chip = &fwd->chip;
51162306a36Sopenharmony_ci
51262306a36Sopenharmony_ci	/*
51362306a36Sopenharmony_ci	 * If any of the GPIO lines are sleeping, then the entire forwarder
51462306a36Sopenharmony_ci	 * will be sleeping.
51562306a36Sopenharmony_ci	 * If any of the chips support .set_config(), then the forwarder will
51662306a36Sopenharmony_ci	 * support setting configs.
51762306a36Sopenharmony_ci	 */
51862306a36Sopenharmony_ci	for (i = 0; i < ngpios; i++) {
51962306a36Sopenharmony_ci		struct gpio_chip *parent = gpiod_to_chip(descs[i]);
52062306a36Sopenharmony_ci
52162306a36Sopenharmony_ci		dev_dbg(dev, "%u => gpio %d irq %d\n", i,
52262306a36Sopenharmony_ci			desc_to_gpio(descs[i]), gpiod_to_irq(descs[i]));
52362306a36Sopenharmony_ci
52462306a36Sopenharmony_ci		if (gpiod_cansleep(descs[i]))
52562306a36Sopenharmony_ci			chip->can_sleep = true;
52662306a36Sopenharmony_ci		if (parent && parent->set_config)
52762306a36Sopenharmony_ci			chip->set_config = gpio_fwd_set_config;
52862306a36Sopenharmony_ci	}
52962306a36Sopenharmony_ci
53062306a36Sopenharmony_ci	chip->label = label;
53162306a36Sopenharmony_ci	chip->parent = dev;
53262306a36Sopenharmony_ci	chip->owner = THIS_MODULE;
53362306a36Sopenharmony_ci	chip->get_direction = gpio_fwd_get_direction;
53462306a36Sopenharmony_ci	chip->direction_input = gpio_fwd_direction_input;
53562306a36Sopenharmony_ci	chip->direction_output = gpio_fwd_direction_output;
53662306a36Sopenharmony_ci	chip->get = gpio_fwd_get;
53762306a36Sopenharmony_ci	chip->get_multiple = gpio_fwd_get_multiple_locked;
53862306a36Sopenharmony_ci	chip->set = gpio_fwd_set;
53962306a36Sopenharmony_ci	chip->set_multiple = gpio_fwd_set_multiple_locked;
54062306a36Sopenharmony_ci	chip->to_irq = gpio_fwd_to_irq;
54162306a36Sopenharmony_ci	chip->base = -1;
54262306a36Sopenharmony_ci	chip->ngpio = ngpios;
54362306a36Sopenharmony_ci	fwd->descs = descs;
54462306a36Sopenharmony_ci
54562306a36Sopenharmony_ci	if (chip->can_sleep)
54662306a36Sopenharmony_ci		mutex_init(&fwd->mlock);
54762306a36Sopenharmony_ci	else
54862306a36Sopenharmony_ci		spin_lock_init(&fwd->slock);
54962306a36Sopenharmony_ci
55062306a36Sopenharmony_ci	if (features & FWD_FEATURE_DELAY) {
55162306a36Sopenharmony_ci		error = gpiochip_fwd_setup_delay_line(dev, chip, fwd);
55262306a36Sopenharmony_ci		if (error)
55362306a36Sopenharmony_ci			return ERR_PTR(error);
55462306a36Sopenharmony_ci	}
55562306a36Sopenharmony_ci
55662306a36Sopenharmony_ci	error = devm_gpiochip_add_data(dev, chip, fwd);
55762306a36Sopenharmony_ci	if (error)
55862306a36Sopenharmony_ci		return ERR_PTR(error);
55962306a36Sopenharmony_ci
56062306a36Sopenharmony_ci	return fwd;
56162306a36Sopenharmony_ci}
56262306a36Sopenharmony_ci
56362306a36Sopenharmony_ci
56462306a36Sopenharmony_ci/*
56562306a36Sopenharmony_ci *  GPIO Aggregator platform device
56662306a36Sopenharmony_ci */
56762306a36Sopenharmony_ci
56862306a36Sopenharmony_cistatic int gpio_aggregator_probe(struct platform_device *pdev)
56962306a36Sopenharmony_ci{
57062306a36Sopenharmony_ci	struct device *dev = &pdev->dev;
57162306a36Sopenharmony_ci	struct gpio_desc **descs;
57262306a36Sopenharmony_ci	struct gpiochip_fwd *fwd;
57362306a36Sopenharmony_ci	unsigned long features;
57462306a36Sopenharmony_ci	int i, n;
57562306a36Sopenharmony_ci
57662306a36Sopenharmony_ci	n = gpiod_count(dev, NULL);
57762306a36Sopenharmony_ci	if (n < 0)
57862306a36Sopenharmony_ci		return n;
57962306a36Sopenharmony_ci
58062306a36Sopenharmony_ci	descs = devm_kmalloc_array(dev, n, sizeof(*descs), GFP_KERNEL);
58162306a36Sopenharmony_ci	if (!descs)
58262306a36Sopenharmony_ci		return -ENOMEM;
58362306a36Sopenharmony_ci
58462306a36Sopenharmony_ci	for (i = 0; i < n; i++) {
58562306a36Sopenharmony_ci		descs[i] = devm_gpiod_get_index(dev, NULL, i, GPIOD_ASIS);
58662306a36Sopenharmony_ci		if (IS_ERR(descs[i]))
58762306a36Sopenharmony_ci			return PTR_ERR(descs[i]);
58862306a36Sopenharmony_ci	}
58962306a36Sopenharmony_ci
59062306a36Sopenharmony_ci	features = (uintptr_t)device_get_match_data(dev);
59162306a36Sopenharmony_ci	fwd = gpiochip_fwd_create(dev, n, descs, features);
59262306a36Sopenharmony_ci	if (IS_ERR(fwd))
59362306a36Sopenharmony_ci		return PTR_ERR(fwd);
59462306a36Sopenharmony_ci
59562306a36Sopenharmony_ci	platform_set_drvdata(pdev, fwd);
59662306a36Sopenharmony_ci	return 0;
59762306a36Sopenharmony_ci}
59862306a36Sopenharmony_ci
59962306a36Sopenharmony_cistatic const struct of_device_id gpio_aggregator_dt_ids[] = {
60062306a36Sopenharmony_ci	{
60162306a36Sopenharmony_ci		.compatible = "gpio-delay",
60262306a36Sopenharmony_ci		.data = (void *)FWD_FEATURE_DELAY,
60362306a36Sopenharmony_ci	},
60462306a36Sopenharmony_ci	/*
60562306a36Sopenharmony_ci	 * Add GPIO-operated devices controlled from userspace below,
60662306a36Sopenharmony_ci	 * or use "driver_override" in sysfs.
60762306a36Sopenharmony_ci	 */
60862306a36Sopenharmony_ci	{}
60962306a36Sopenharmony_ci};
61062306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, gpio_aggregator_dt_ids);
61162306a36Sopenharmony_ci
61262306a36Sopenharmony_cistatic struct platform_driver gpio_aggregator_driver = {
61362306a36Sopenharmony_ci	.probe = gpio_aggregator_probe,
61462306a36Sopenharmony_ci	.driver = {
61562306a36Sopenharmony_ci		.name = DRV_NAME,
61662306a36Sopenharmony_ci		.groups = gpio_aggregator_groups,
61762306a36Sopenharmony_ci		.of_match_table = gpio_aggregator_dt_ids,
61862306a36Sopenharmony_ci	},
61962306a36Sopenharmony_ci};
62062306a36Sopenharmony_ci
62162306a36Sopenharmony_cistatic int __init gpio_aggregator_init(void)
62262306a36Sopenharmony_ci{
62362306a36Sopenharmony_ci	return platform_driver_register(&gpio_aggregator_driver);
62462306a36Sopenharmony_ci}
62562306a36Sopenharmony_cimodule_init(gpio_aggregator_init);
62662306a36Sopenharmony_ci
62762306a36Sopenharmony_cistatic void __exit gpio_aggregator_exit(void)
62862306a36Sopenharmony_ci{
62962306a36Sopenharmony_ci	gpio_aggregator_remove_all();
63062306a36Sopenharmony_ci	platform_driver_unregister(&gpio_aggregator_driver);
63162306a36Sopenharmony_ci}
63262306a36Sopenharmony_cimodule_exit(gpio_aggregator_exit);
63362306a36Sopenharmony_ci
63462306a36Sopenharmony_ciMODULE_AUTHOR("Geert Uytterhoeven <geert+renesas@glider.be>");
63562306a36Sopenharmony_ciMODULE_DESCRIPTION("GPIO Aggregator");
63662306a36Sopenharmony_ciMODULE_LICENSE("GPL v2");
637