1828546e2SGeert Uytterhoeven // SPDX-License-Identifier: GPL-2.0-only
2828546e2SGeert Uytterhoeven //
3828546e2SGeert Uytterhoeven // GPIO Aggregator
4828546e2SGeert Uytterhoeven //
5828546e2SGeert Uytterhoeven // Copyright (C) 2019-2020 Glider bv
6828546e2SGeert Uytterhoeven
7828546e2SGeert Uytterhoeven #define DRV_NAME "gpio-aggregator"
8828546e2SGeert Uytterhoeven #define pr_fmt(fmt) DRV_NAME ": " fmt
9828546e2SGeert Uytterhoeven
10828546e2SGeert Uytterhoeven #include <linux/bitmap.h>
11828546e2SGeert Uytterhoeven #include <linux/bitops.h>
12828546e2SGeert Uytterhoeven #include <linux/ctype.h>
13b466622cSAndy Shevchenko #include <linux/delay.h>
14828546e2SGeert Uytterhoeven #include <linux/idr.h>
15828546e2SGeert Uytterhoeven #include <linux/kernel.h>
16b89a9e98SAndy Shevchenko #include <linux/mod_devicetable.h>
17828546e2SGeert Uytterhoeven #include <linux/module.h>
18828546e2SGeert Uytterhoeven #include <linux/mutex.h>
19828546e2SGeert Uytterhoeven #include <linux/overflow.h>
20828546e2SGeert Uytterhoeven #include <linux/platform_device.h>
21*81674beaSAndy Shevchenko #include <linux/property.h>
2239ebbd52SAndy Shevchenko #include <linux/slab.h>
23828546e2SGeert Uytterhoeven #include <linux/spinlock.h>
24828546e2SGeert Uytterhoeven #include <linux/string.h>
25828546e2SGeert Uytterhoeven
2639ebbd52SAndy Shevchenko #include <linux/gpio/consumer.h>
2739ebbd52SAndy Shevchenko #include <linux/gpio/driver.h>
2839ebbd52SAndy Shevchenko #include <linux/gpio/machine.h>
2939ebbd52SAndy Shevchenko
3095b39792SChristophe Leroy #define AGGREGATOR_MAX_GPIOS 512
31828546e2SGeert Uytterhoeven
32828546e2SGeert Uytterhoeven /*
33828546e2SGeert Uytterhoeven * GPIO Aggregator sysfs interface
34828546e2SGeert Uytterhoeven */
35828546e2SGeert Uytterhoeven
36828546e2SGeert Uytterhoeven struct gpio_aggregator {
37828546e2SGeert Uytterhoeven struct gpiod_lookup_table *lookups;
38828546e2SGeert Uytterhoeven struct platform_device *pdev;
39828546e2SGeert Uytterhoeven char args[];
40828546e2SGeert Uytterhoeven };
41828546e2SGeert Uytterhoeven
42828546e2SGeert Uytterhoeven static DEFINE_MUTEX(gpio_aggregator_lock); /* protects idr */
43828546e2SGeert Uytterhoeven static DEFINE_IDR(gpio_aggregator_idr);
44828546e2SGeert Uytterhoeven
aggr_add_gpio(struct gpio_aggregator * aggr,const char * key,int hwnum,unsigned int * n)45828546e2SGeert Uytterhoeven static int aggr_add_gpio(struct gpio_aggregator *aggr, const char *key,
46828546e2SGeert Uytterhoeven int hwnum, unsigned int *n)
47828546e2SGeert Uytterhoeven {
48828546e2SGeert Uytterhoeven struct gpiod_lookup_table *lookups;
49828546e2SGeert Uytterhoeven
50828546e2SGeert Uytterhoeven lookups = krealloc(aggr->lookups, struct_size(lookups, table, *n + 2),
51828546e2SGeert Uytterhoeven GFP_KERNEL);
52828546e2SGeert Uytterhoeven if (!lookups)
53828546e2SGeert Uytterhoeven return -ENOMEM;
54828546e2SGeert Uytterhoeven
55b2498cb8SAndy Shevchenko lookups->table[*n] = GPIO_LOOKUP_IDX(key, hwnum, NULL, *n, 0);
56828546e2SGeert Uytterhoeven
57828546e2SGeert Uytterhoeven (*n)++;
58828546e2SGeert Uytterhoeven memset(&lookups->table[*n], 0, sizeof(lookups->table[*n]));
59828546e2SGeert Uytterhoeven
60828546e2SGeert Uytterhoeven aggr->lookups = lookups;
61828546e2SGeert Uytterhoeven return 0;
62828546e2SGeert Uytterhoeven }
63828546e2SGeert Uytterhoeven
aggr_parse(struct gpio_aggregator * aggr)64828546e2SGeert Uytterhoeven static int aggr_parse(struct gpio_aggregator *aggr)
65828546e2SGeert Uytterhoeven {
66ac505b6fSAndy Shevchenko char *args = skip_spaces(aggr->args);
67deb631c4SAndy Shevchenko char *name, *offsets, *p;
68ec75039dSGeert Uytterhoeven unsigned long *bitmap;
69ec75039dSGeert Uytterhoeven unsigned int i, n = 0;
70ec75039dSGeert Uytterhoeven int error = 0;
71ec75039dSGeert Uytterhoeven
7295b39792SChristophe Leroy bitmap = bitmap_alloc(AGGREGATOR_MAX_GPIOS, GFP_KERNEL);
73ec75039dSGeert Uytterhoeven if (!bitmap)
74ec75039dSGeert Uytterhoeven return -ENOMEM;
75828546e2SGeert Uytterhoeven
76ac505b6fSAndy Shevchenko args = next_arg(args, &name, &p);
77ac505b6fSAndy Shevchenko while (*args) {
78ac505b6fSAndy Shevchenko args = next_arg(args, &offsets, &p);
79828546e2SGeert Uytterhoeven
80deb631c4SAndy Shevchenko p = get_options(offsets, 0, &error);
81deb631c4SAndy Shevchenko if (error == 0 || *p) {
82828546e2SGeert Uytterhoeven /* Named GPIO line */
83828546e2SGeert Uytterhoeven error = aggr_add_gpio(aggr, name, U16_MAX, &n);
84828546e2SGeert Uytterhoeven if (error)
85ec75039dSGeert Uytterhoeven goto free_bitmap;
86828546e2SGeert Uytterhoeven
87828546e2SGeert Uytterhoeven name = offsets;
88828546e2SGeert Uytterhoeven continue;
89828546e2SGeert Uytterhoeven }
90828546e2SGeert Uytterhoeven
91828546e2SGeert Uytterhoeven /* GPIO chip + offset(s) */
9295b39792SChristophe Leroy error = bitmap_parselist(offsets, bitmap, AGGREGATOR_MAX_GPIOS);
93ec75039dSGeert Uytterhoeven if (error) {
94ec75039dSGeert Uytterhoeven pr_err("Cannot parse %s: %d\n", offsets, error);
95ec75039dSGeert Uytterhoeven goto free_bitmap;
96828546e2SGeert Uytterhoeven }
97828546e2SGeert Uytterhoeven
9895b39792SChristophe Leroy for_each_set_bit(i, bitmap, AGGREGATOR_MAX_GPIOS) {
99828546e2SGeert Uytterhoeven error = aggr_add_gpio(aggr, name, i, &n);
100828546e2SGeert Uytterhoeven if (error)
101ec75039dSGeert Uytterhoeven goto free_bitmap;
102828546e2SGeert Uytterhoeven }
103828546e2SGeert Uytterhoeven
104ac505b6fSAndy Shevchenko args = next_arg(args, &name, &p);
105828546e2SGeert Uytterhoeven }
106828546e2SGeert Uytterhoeven
107828546e2SGeert Uytterhoeven if (!n) {
108828546e2SGeert Uytterhoeven pr_err("No GPIOs specified\n");
109ec75039dSGeert Uytterhoeven error = -EINVAL;
110828546e2SGeert Uytterhoeven }
111828546e2SGeert Uytterhoeven
112ec75039dSGeert Uytterhoeven free_bitmap:
113ec75039dSGeert Uytterhoeven bitmap_free(bitmap);
114ec75039dSGeert Uytterhoeven return error;
115828546e2SGeert Uytterhoeven }
116828546e2SGeert Uytterhoeven
new_device_store(struct device_driver * driver,const char * buf,size_t count)117828546e2SGeert Uytterhoeven static ssize_t new_device_store(struct device_driver *driver, const char *buf,
118828546e2SGeert Uytterhoeven size_t count)
119828546e2SGeert Uytterhoeven {
120828546e2SGeert Uytterhoeven struct gpio_aggregator *aggr;
121828546e2SGeert Uytterhoeven struct platform_device *pdev;
122828546e2SGeert Uytterhoeven int res, id;
123828546e2SGeert Uytterhoeven
124828546e2SGeert Uytterhoeven /* kernfs guarantees string termination, so count + 1 is safe */
125828546e2SGeert Uytterhoeven aggr = kzalloc(sizeof(*aggr) + count + 1, GFP_KERNEL);
126828546e2SGeert Uytterhoeven if (!aggr)
127828546e2SGeert Uytterhoeven return -ENOMEM;
128828546e2SGeert Uytterhoeven
129828546e2SGeert Uytterhoeven memcpy(aggr->args, buf, count + 1);
130828546e2SGeert Uytterhoeven
131828546e2SGeert Uytterhoeven aggr->lookups = kzalloc(struct_size(aggr->lookups, table, 1),
132828546e2SGeert Uytterhoeven GFP_KERNEL);
133828546e2SGeert Uytterhoeven if (!aggr->lookups) {
134828546e2SGeert Uytterhoeven res = -ENOMEM;
135828546e2SGeert Uytterhoeven goto free_ga;
136828546e2SGeert Uytterhoeven }
137828546e2SGeert Uytterhoeven
138828546e2SGeert Uytterhoeven mutex_lock(&gpio_aggregator_lock);
139828546e2SGeert Uytterhoeven id = idr_alloc(&gpio_aggregator_idr, aggr, 0, 0, GFP_KERNEL);
140828546e2SGeert Uytterhoeven mutex_unlock(&gpio_aggregator_lock);
141828546e2SGeert Uytterhoeven
142828546e2SGeert Uytterhoeven if (id < 0) {
143828546e2SGeert Uytterhoeven res = id;
144828546e2SGeert Uytterhoeven goto free_table;
145828546e2SGeert Uytterhoeven }
146828546e2SGeert Uytterhoeven
147828546e2SGeert Uytterhoeven aggr->lookups->dev_id = kasprintf(GFP_KERNEL, "%s.%d", DRV_NAME, id);
148828546e2SGeert Uytterhoeven if (!aggr->lookups->dev_id) {
149828546e2SGeert Uytterhoeven res = -ENOMEM;
150828546e2SGeert Uytterhoeven goto remove_idr;
151828546e2SGeert Uytterhoeven }
152828546e2SGeert Uytterhoeven
153828546e2SGeert Uytterhoeven res = aggr_parse(aggr);
154828546e2SGeert Uytterhoeven if (res)
155828546e2SGeert Uytterhoeven goto free_dev_id;
156828546e2SGeert Uytterhoeven
157828546e2SGeert Uytterhoeven gpiod_add_lookup_table(aggr->lookups);
158828546e2SGeert Uytterhoeven
159828546e2SGeert Uytterhoeven pdev = platform_device_register_simple(DRV_NAME, id, NULL, 0);
160828546e2SGeert Uytterhoeven if (IS_ERR(pdev)) {
161828546e2SGeert Uytterhoeven res = PTR_ERR(pdev);
162828546e2SGeert Uytterhoeven goto remove_table;
163828546e2SGeert Uytterhoeven }
164828546e2SGeert Uytterhoeven
165828546e2SGeert Uytterhoeven aggr->pdev = pdev;
166828546e2SGeert Uytterhoeven return count;
167828546e2SGeert Uytterhoeven
168828546e2SGeert Uytterhoeven remove_table:
169828546e2SGeert Uytterhoeven gpiod_remove_lookup_table(aggr->lookups);
170828546e2SGeert Uytterhoeven free_dev_id:
171828546e2SGeert Uytterhoeven kfree(aggr->lookups->dev_id);
172828546e2SGeert Uytterhoeven remove_idr:
173828546e2SGeert Uytterhoeven mutex_lock(&gpio_aggregator_lock);
174828546e2SGeert Uytterhoeven idr_remove(&gpio_aggregator_idr, id);
175828546e2SGeert Uytterhoeven mutex_unlock(&gpio_aggregator_lock);
176828546e2SGeert Uytterhoeven free_table:
177828546e2SGeert Uytterhoeven kfree(aggr->lookups);
178828546e2SGeert Uytterhoeven free_ga:
179828546e2SGeert Uytterhoeven kfree(aggr);
180828546e2SGeert Uytterhoeven return res;
181828546e2SGeert Uytterhoeven }
182828546e2SGeert Uytterhoeven
183828546e2SGeert Uytterhoeven static DRIVER_ATTR_WO(new_device);
184828546e2SGeert Uytterhoeven
gpio_aggregator_free(struct gpio_aggregator * aggr)185828546e2SGeert Uytterhoeven static void gpio_aggregator_free(struct gpio_aggregator *aggr)
186828546e2SGeert Uytterhoeven {
187828546e2SGeert Uytterhoeven platform_device_unregister(aggr->pdev);
188828546e2SGeert Uytterhoeven gpiod_remove_lookup_table(aggr->lookups);
189828546e2SGeert Uytterhoeven kfree(aggr->lookups->dev_id);
190828546e2SGeert Uytterhoeven kfree(aggr->lookups);
191828546e2SGeert Uytterhoeven kfree(aggr);
192828546e2SGeert Uytterhoeven }
193828546e2SGeert Uytterhoeven
delete_device_store(struct device_driver * driver,const char * buf,size_t count)194828546e2SGeert Uytterhoeven static ssize_t delete_device_store(struct device_driver *driver,
195828546e2SGeert Uytterhoeven const char *buf, size_t count)
196828546e2SGeert Uytterhoeven {
197828546e2SGeert Uytterhoeven struct gpio_aggregator *aggr;
198828546e2SGeert Uytterhoeven unsigned int id;
199828546e2SGeert Uytterhoeven int error;
200828546e2SGeert Uytterhoeven
201828546e2SGeert Uytterhoeven if (!str_has_prefix(buf, DRV_NAME "."))
202828546e2SGeert Uytterhoeven return -EINVAL;
203828546e2SGeert Uytterhoeven
204828546e2SGeert Uytterhoeven error = kstrtouint(buf + strlen(DRV_NAME "."), 10, &id);
205828546e2SGeert Uytterhoeven if (error)
206828546e2SGeert Uytterhoeven return error;
207828546e2SGeert Uytterhoeven
208828546e2SGeert Uytterhoeven mutex_lock(&gpio_aggregator_lock);
209828546e2SGeert Uytterhoeven aggr = idr_remove(&gpio_aggregator_idr, id);
210828546e2SGeert Uytterhoeven mutex_unlock(&gpio_aggregator_lock);
211828546e2SGeert Uytterhoeven if (!aggr)
212828546e2SGeert Uytterhoeven return -ENOENT;
213828546e2SGeert Uytterhoeven
214828546e2SGeert Uytterhoeven gpio_aggregator_free(aggr);
215828546e2SGeert Uytterhoeven return count;
216828546e2SGeert Uytterhoeven }
217828546e2SGeert Uytterhoeven static DRIVER_ATTR_WO(delete_device);
218828546e2SGeert Uytterhoeven
219828546e2SGeert Uytterhoeven static struct attribute *gpio_aggregator_attrs[] = {
220828546e2SGeert Uytterhoeven &driver_attr_new_device.attr,
221828546e2SGeert Uytterhoeven &driver_attr_delete_device.attr,
2226e004a98SAndy Shevchenko NULL
223828546e2SGeert Uytterhoeven };
224828546e2SGeert Uytterhoeven ATTRIBUTE_GROUPS(gpio_aggregator);
225828546e2SGeert Uytterhoeven
gpio_aggregator_idr_remove(int id,void * p,void * data)226828546e2SGeert Uytterhoeven static int __exit gpio_aggregator_idr_remove(int id, void *p, void *data)
227828546e2SGeert Uytterhoeven {
228828546e2SGeert Uytterhoeven gpio_aggregator_free(p);
229828546e2SGeert Uytterhoeven return 0;
230828546e2SGeert Uytterhoeven }
231828546e2SGeert Uytterhoeven
gpio_aggregator_remove_all(void)232828546e2SGeert Uytterhoeven static void __exit gpio_aggregator_remove_all(void)
233828546e2SGeert Uytterhoeven {
234828546e2SGeert Uytterhoeven mutex_lock(&gpio_aggregator_lock);
235828546e2SGeert Uytterhoeven idr_for_each(&gpio_aggregator_idr, gpio_aggregator_idr_remove, NULL);
236828546e2SGeert Uytterhoeven idr_destroy(&gpio_aggregator_idr);
237828546e2SGeert Uytterhoeven mutex_unlock(&gpio_aggregator_lock);
238828546e2SGeert Uytterhoeven }
239828546e2SGeert Uytterhoeven
240828546e2SGeert Uytterhoeven
241828546e2SGeert Uytterhoeven /*
242828546e2SGeert Uytterhoeven * GPIO Forwarder
243828546e2SGeert Uytterhoeven */
244828546e2SGeert Uytterhoeven
245b466622cSAndy Shevchenko struct gpiochip_fwd_timing {
246b466622cSAndy Shevchenko u32 ramp_up_us;
247b466622cSAndy Shevchenko u32 ramp_down_us;
248b466622cSAndy Shevchenko };
249b466622cSAndy Shevchenko
250828546e2SGeert Uytterhoeven struct gpiochip_fwd {
251828546e2SGeert Uytterhoeven struct gpio_chip chip;
252828546e2SGeert Uytterhoeven struct gpio_desc **descs;
253828546e2SGeert Uytterhoeven union {
254828546e2SGeert Uytterhoeven struct mutex mlock; /* protects tmp[] if can_sleep */
255828546e2SGeert Uytterhoeven spinlock_t slock; /* protects tmp[] if !can_sleep */
256828546e2SGeert Uytterhoeven };
257b466622cSAndy Shevchenko struct gpiochip_fwd_timing *delay_timings;
258828546e2SGeert Uytterhoeven unsigned long tmp[]; /* values and descs for multiple ops */
259828546e2SGeert Uytterhoeven };
260828546e2SGeert Uytterhoeven
261aa4858ebSGeert Uytterhoeven #define fwd_tmp_values(fwd) &(fwd)->tmp[0]
262aa4858ebSGeert Uytterhoeven #define fwd_tmp_descs(fwd) (void *)&(fwd)->tmp[BITS_TO_LONGS((fwd)->chip.ngpio)]
263aa4858ebSGeert Uytterhoeven
264aa4858ebSGeert Uytterhoeven #define fwd_tmp_size(ngpios) (BITS_TO_LONGS((ngpios)) + (ngpios))
265aa4858ebSGeert Uytterhoeven
gpio_fwd_get_direction(struct gpio_chip * chip,unsigned int offset)266828546e2SGeert Uytterhoeven static int gpio_fwd_get_direction(struct gpio_chip *chip, unsigned int offset)
267828546e2SGeert Uytterhoeven {
268828546e2SGeert Uytterhoeven struct gpiochip_fwd *fwd = gpiochip_get_data(chip);
269828546e2SGeert Uytterhoeven
270828546e2SGeert Uytterhoeven return gpiod_get_direction(fwd->descs[offset]);
271828546e2SGeert Uytterhoeven }
272828546e2SGeert Uytterhoeven
gpio_fwd_direction_input(struct gpio_chip * chip,unsigned int offset)273828546e2SGeert Uytterhoeven static int gpio_fwd_direction_input(struct gpio_chip *chip, unsigned int offset)
274828546e2SGeert Uytterhoeven {
275828546e2SGeert Uytterhoeven struct gpiochip_fwd *fwd = gpiochip_get_data(chip);
276828546e2SGeert Uytterhoeven
277828546e2SGeert Uytterhoeven return gpiod_direction_input(fwd->descs[offset]);
278828546e2SGeert Uytterhoeven }
279828546e2SGeert Uytterhoeven
gpio_fwd_direction_output(struct gpio_chip * chip,unsigned int offset,int value)280828546e2SGeert Uytterhoeven static int gpio_fwd_direction_output(struct gpio_chip *chip,
281828546e2SGeert Uytterhoeven unsigned int offset, int value)
282828546e2SGeert Uytterhoeven {
283828546e2SGeert Uytterhoeven struct gpiochip_fwd *fwd = gpiochip_get_data(chip);
284828546e2SGeert Uytterhoeven
285828546e2SGeert Uytterhoeven return gpiod_direction_output(fwd->descs[offset], value);
286828546e2SGeert Uytterhoeven }
287828546e2SGeert Uytterhoeven
gpio_fwd_get(struct gpio_chip * chip,unsigned int offset)288828546e2SGeert Uytterhoeven static int gpio_fwd_get(struct gpio_chip *chip, unsigned int offset)
289828546e2SGeert Uytterhoeven {
290828546e2SGeert Uytterhoeven struct gpiochip_fwd *fwd = gpiochip_get_data(chip);
291828546e2SGeert Uytterhoeven
2922cba0545SGeert Uytterhoeven return chip->can_sleep ? gpiod_get_value_cansleep(fwd->descs[offset])
2932cba0545SGeert Uytterhoeven : gpiod_get_value(fwd->descs[offset]);
294828546e2SGeert Uytterhoeven }
295828546e2SGeert Uytterhoeven
gpio_fwd_get_multiple(struct gpiochip_fwd * fwd,unsigned long * mask,unsigned long * bits)29601e8d85bSAndy Shevchenko static int gpio_fwd_get_multiple(struct gpiochip_fwd *fwd, unsigned long *mask,
297828546e2SGeert Uytterhoeven unsigned long *bits)
298828546e2SGeert Uytterhoeven {
299aa4858ebSGeert Uytterhoeven struct gpio_desc **descs = fwd_tmp_descs(fwd);
300aa4858ebSGeert Uytterhoeven unsigned long *values = fwd_tmp_values(fwd);
301828546e2SGeert Uytterhoeven unsigned int i, j = 0;
302828546e2SGeert Uytterhoeven int error;
303828546e2SGeert Uytterhoeven
304828546e2SGeert Uytterhoeven bitmap_clear(values, 0, fwd->chip.ngpio);
305828546e2SGeert Uytterhoeven for_each_set_bit(i, mask, fwd->chip.ngpio)
306828546e2SGeert Uytterhoeven descs[j++] = fwd->descs[i];
307828546e2SGeert Uytterhoeven
3082cba0545SGeert Uytterhoeven if (fwd->chip.can_sleep)
3092cba0545SGeert Uytterhoeven error = gpiod_get_array_value_cansleep(j, descs, NULL, values);
3102cba0545SGeert Uytterhoeven else
311828546e2SGeert Uytterhoeven error = gpiod_get_array_value(j, descs, NULL, values);
31201e8d85bSAndy Shevchenko if (error)
31301e8d85bSAndy Shevchenko return error;
31401e8d85bSAndy Shevchenko
315828546e2SGeert Uytterhoeven j = 0;
316828546e2SGeert Uytterhoeven for_each_set_bit(i, mask, fwd->chip.ngpio)
317828546e2SGeert Uytterhoeven __assign_bit(i, bits, test_bit(j++, values));
31801e8d85bSAndy Shevchenko
31901e8d85bSAndy Shevchenko return 0;
320828546e2SGeert Uytterhoeven }
321828546e2SGeert Uytterhoeven
gpio_fwd_get_multiple_locked(struct gpio_chip * chip,unsigned long * mask,unsigned long * bits)32201e8d85bSAndy Shevchenko static int gpio_fwd_get_multiple_locked(struct gpio_chip *chip,
32301e8d85bSAndy Shevchenko unsigned long *mask, unsigned long *bits)
32401e8d85bSAndy Shevchenko {
32501e8d85bSAndy Shevchenko struct gpiochip_fwd *fwd = gpiochip_get_data(chip);
32601e8d85bSAndy Shevchenko unsigned long flags;
32701e8d85bSAndy Shevchenko int error;
32801e8d85bSAndy Shevchenko
32901e8d85bSAndy Shevchenko if (chip->can_sleep) {
33001e8d85bSAndy Shevchenko mutex_lock(&fwd->mlock);
33101e8d85bSAndy Shevchenko error = gpio_fwd_get_multiple(fwd, mask, bits);
332828546e2SGeert Uytterhoeven mutex_unlock(&fwd->mlock);
33301e8d85bSAndy Shevchenko } else {
33401e8d85bSAndy Shevchenko spin_lock_irqsave(&fwd->slock, flags);
33501e8d85bSAndy Shevchenko error = gpio_fwd_get_multiple(fwd, mask, bits);
336828546e2SGeert Uytterhoeven spin_unlock_irqrestore(&fwd->slock, flags);
33701e8d85bSAndy Shevchenko }
338828546e2SGeert Uytterhoeven
339828546e2SGeert Uytterhoeven return error;
340828546e2SGeert Uytterhoeven }
341828546e2SGeert Uytterhoeven
gpio_fwd_delay(struct gpio_chip * chip,unsigned int offset,int value)342b466622cSAndy Shevchenko static void gpio_fwd_delay(struct gpio_chip *chip, unsigned int offset, int value)
343b466622cSAndy Shevchenko {
344b466622cSAndy Shevchenko struct gpiochip_fwd *fwd = gpiochip_get_data(chip);
345b466622cSAndy Shevchenko const struct gpiochip_fwd_timing *delay_timings;
346b466622cSAndy Shevchenko bool is_active_low = gpiod_is_active_low(fwd->descs[offset]);
347b466622cSAndy Shevchenko u32 delay_us;
348b466622cSAndy Shevchenko
349b466622cSAndy Shevchenko delay_timings = &fwd->delay_timings[offset];
350b466622cSAndy Shevchenko if ((!is_active_low && value) || (is_active_low && !value))
351b466622cSAndy Shevchenko delay_us = delay_timings->ramp_up_us;
352b466622cSAndy Shevchenko else
353b466622cSAndy Shevchenko delay_us = delay_timings->ramp_down_us;
354b466622cSAndy Shevchenko if (!delay_us)
355b466622cSAndy Shevchenko return;
356b466622cSAndy Shevchenko
357b466622cSAndy Shevchenko if (chip->can_sleep)
358b466622cSAndy Shevchenko fsleep(delay_us);
359b466622cSAndy Shevchenko else
360b466622cSAndy Shevchenko udelay(delay_us);
361b466622cSAndy Shevchenko }
362b466622cSAndy Shevchenko
gpio_fwd_set(struct gpio_chip * chip,unsigned int offset,int value)363828546e2SGeert Uytterhoeven static void gpio_fwd_set(struct gpio_chip *chip, unsigned int offset, int value)
364828546e2SGeert Uytterhoeven {
365828546e2SGeert Uytterhoeven struct gpiochip_fwd *fwd = gpiochip_get_data(chip);
366828546e2SGeert Uytterhoeven
3672cba0545SGeert Uytterhoeven if (chip->can_sleep)
3682cba0545SGeert Uytterhoeven gpiod_set_value_cansleep(fwd->descs[offset], value);
3692cba0545SGeert Uytterhoeven else
370828546e2SGeert Uytterhoeven gpiod_set_value(fwd->descs[offset], value);
371b466622cSAndy Shevchenko
372b466622cSAndy Shevchenko if (fwd->delay_timings)
373b466622cSAndy Shevchenko gpio_fwd_delay(chip, offset, value);
374828546e2SGeert Uytterhoeven }
375828546e2SGeert Uytterhoeven
gpio_fwd_set_multiple(struct gpiochip_fwd * fwd,unsigned long * mask,unsigned long * bits)37601e8d85bSAndy Shevchenko static void gpio_fwd_set_multiple(struct gpiochip_fwd *fwd, unsigned long *mask,
377828546e2SGeert Uytterhoeven unsigned long *bits)
378828546e2SGeert Uytterhoeven {
379aa4858ebSGeert Uytterhoeven struct gpio_desc **descs = fwd_tmp_descs(fwd);
380aa4858ebSGeert Uytterhoeven unsigned long *values = fwd_tmp_values(fwd);
381828546e2SGeert Uytterhoeven unsigned int i, j = 0;
382828546e2SGeert Uytterhoeven
383828546e2SGeert Uytterhoeven for_each_set_bit(i, mask, fwd->chip.ngpio) {
384828546e2SGeert Uytterhoeven __assign_bit(j, values, test_bit(i, bits));
385828546e2SGeert Uytterhoeven descs[j++] = fwd->descs[i];
386828546e2SGeert Uytterhoeven }
387828546e2SGeert Uytterhoeven
3882cba0545SGeert Uytterhoeven if (fwd->chip.can_sleep)
3892cba0545SGeert Uytterhoeven gpiod_set_array_value_cansleep(j, descs, NULL, values);
3902cba0545SGeert Uytterhoeven else
391828546e2SGeert Uytterhoeven gpiod_set_array_value(j, descs, NULL, values);
39201e8d85bSAndy Shevchenko }
393828546e2SGeert Uytterhoeven
gpio_fwd_set_multiple_locked(struct gpio_chip * chip,unsigned long * mask,unsigned long * bits)39401e8d85bSAndy Shevchenko static void gpio_fwd_set_multiple_locked(struct gpio_chip *chip,
39501e8d85bSAndy Shevchenko unsigned long *mask, unsigned long *bits)
39601e8d85bSAndy Shevchenko {
39701e8d85bSAndy Shevchenko struct gpiochip_fwd *fwd = gpiochip_get_data(chip);
39801e8d85bSAndy Shevchenko unsigned long flags;
39901e8d85bSAndy Shevchenko
40001e8d85bSAndy Shevchenko if (chip->can_sleep) {
40101e8d85bSAndy Shevchenko mutex_lock(&fwd->mlock);
40201e8d85bSAndy Shevchenko gpio_fwd_set_multiple(fwd, mask, bits);
403828546e2SGeert Uytterhoeven mutex_unlock(&fwd->mlock);
40401e8d85bSAndy Shevchenko } else {
40501e8d85bSAndy Shevchenko spin_lock_irqsave(&fwd->slock, flags);
40601e8d85bSAndy Shevchenko gpio_fwd_set_multiple(fwd, mask, bits);
407828546e2SGeert Uytterhoeven spin_unlock_irqrestore(&fwd->slock, flags);
408828546e2SGeert Uytterhoeven }
40901e8d85bSAndy Shevchenko }
410828546e2SGeert Uytterhoeven
gpio_fwd_set_config(struct gpio_chip * chip,unsigned int offset,unsigned long config)411828546e2SGeert Uytterhoeven static int gpio_fwd_set_config(struct gpio_chip *chip, unsigned int offset,
412828546e2SGeert Uytterhoeven unsigned long config)
413828546e2SGeert Uytterhoeven {
414828546e2SGeert Uytterhoeven struct gpiochip_fwd *fwd = gpiochip_get_data(chip);
415828546e2SGeert Uytterhoeven
416828546e2SGeert Uytterhoeven return gpiod_set_config(fwd->descs[offset], config);
417828546e2SGeert Uytterhoeven }
418828546e2SGeert Uytterhoeven
gpio_fwd_to_irq(struct gpio_chip * chip,unsigned int offset)419a00128dfSGeert Uytterhoeven static int gpio_fwd_to_irq(struct gpio_chip *chip, unsigned int offset)
420a00128dfSGeert Uytterhoeven {
421a00128dfSGeert Uytterhoeven struct gpiochip_fwd *fwd = gpiochip_get_data(chip);
422a00128dfSGeert Uytterhoeven
423a00128dfSGeert Uytterhoeven return gpiod_to_irq(fwd->descs[offset]);
424a00128dfSGeert Uytterhoeven }
425a00128dfSGeert Uytterhoeven
426*81674beaSAndy Shevchenko /*
427*81674beaSAndy Shevchenko * The GPIO delay provides a way to configure platform specific delays
428*81674beaSAndy Shevchenko * for the GPIO ramp-up or ramp-down delays. This can serve the following
429*81674beaSAndy Shevchenko * purposes:
430*81674beaSAndy Shevchenko * - Open-drain output using an RC filter
431*81674beaSAndy Shevchenko */
432*81674beaSAndy Shevchenko #define FWD_FEATURE_DELAY BIT(0)
433*81674beaSAndy Shevchenko
434*81674beaSAndy Shevchenko #ifdef CONFIG_OF_GPIO
gpiochip_fwd_delay_of_xlate(struct gpio_chip * chip,const struct of_phandle_args * gpiospec,u32 * flags)435*81674beaSAndy Shevchenko static int gpiochip_fwd_delay_of_xlate(struct gpio_chip *chip,
436*81674beaSAndy Shevchenko const struct of_phandle_args *gpiospec,
437*81674beaSAndy Shevchenko u32 *flags)
438*81674beaSAndy Shevchenko {
439*81674beaSAndy Shevchenko struct gpiochip_fwd *fwd = gpiochip_get_data(chip);
440*81674beaSAndy Shevchenko struct gpiochip_fwd_timing *timings;
441*81674beaSAndy Shevchenko u32 line;
442*81674beaSAndy Shevchenko
443*81674beaSAndy Shevchenko if (gpiospec->args_count != chip->of_gpio_n_cells)
444*81674beaSAndy Shevchenko return -EINVAL;
445*81674beaSAndy Shevchenko
446*81674beaSAndy Shevchenko line = gpiospec->args[0];
447*81674beaSAndy Shevchenko if (line >= chip->ngpio)
448*81674beaSAndy Shevchenko return -EINVAL;
449*81674beaSAndy Shevchenko
450*81674beaSAndy Shevchenko timings = &fwd->delay_timings[line];
451*81674beaSAndy Shevchenko timings->ramp_up_us = gpiospec->args[1];
452*81674beaSAndy Shevchenko timings->ramp_down_us = gpiospec->args[2];
453*81674beaSAndy Shevchenko
454*81674beaSAndy Shevchenko return line;
455*81674beaSAndy Shevchenko }
456*81674beaSAndy Shevchenko
gpiochip_fwd_setup_delay_line(struct device * dev,struct gpio_chip * chip,struct gpiochip_fwd * fwd)457*81674beaSAndy Shevchenko static int gpiochip_fwd_setup_delay_line(struct device *dev, struct gpio_chip *chip,
458*81674beaSAndy Shevchenko struct gpiochip_fwd *fwd)
459*81674beaSAndy Shevchenko {
460*81674beaSAndy Shevchenko fwd->delay_timings = devm_kcalloc(dev, chip->ngpio,
461*81674beaSAndy Shevchenko sizeof(*fwd->delay_timings),
462*81674beaSAndy Shevchenko GFP_KERNEL);
463*81674beaSAndy Shevchenko if (!fwd->delay_timings)
464*81674beaSAndy Shevchenko return -ENOMEM;
465*81674beaSAndy Shevchenko
466*81674beaSAndy Shevchenko chip->of_xlate = gpiochip_fwd_delay_of_xlate;
467*81674beaSAndy Shevchenko chip->of_gpio_n_cells = 3;
468*81674beaSAndy Shevchenko
469*81674beaSAndy Shevchenko return 0;
470*81674beaSAndy Shevchenko }
471*81674beaSAndy Shevchenko #else
gpiochip_fwd_setup_delay_line(struct device * dev,struct gpio_chip * chip,struct gpiochip_fwd * fwd)472*81674beaSAndy Shevchenko static int gpiochip_fwd_setup_delay_line(struct device *dev, struct gpio_chip *chip,
473*81674beaSAndy Shevchenko struct gpiochip_fwd *fwd)
474*81674beaSAndy Shevchenko {
475*81674beaSAndy Shevchenko return 0;
476*81674beaSAndy Shevchenko }
477*81674beaSAndy Shevchenko #endif /* !CONFIG_OF_GPIO */
478*81674beaSAndy Shevchenko
479828546e2SGeert Uytterhoeven /**
480828546e2SGeert Uytterhoeven * gpiochip_fwd_create() - Create a new GPIO forwarder
481828546e2SGeert Uytterhoeven * @dev: Parent device pointer
482828546e2SGeert Uytterhoeven * @ngpios: Number of GPIOs in the forwarder.
483828546e2SGeert Uytterhoeven * @descs: Array containing the GPIO descriptors to forward to.
484828546e2SGeert Uytterhoeven * This array must contain @ngpios entries, and must not be deallocated
485828546e2SGeert Uytterhoeven * before the forwarder has been destroyed again.
486*81674beaSAndy Shevchenko * @features: Bitwise ORed features as defined with FWD_FEATURE_*.
487828546e2SGeert Uytterhoeven *
488828546e2SGeert Uytterhoeven * This function creates a new gpiochip, which forwards all GPIO operations to
489828546e2SGeert Uytterhoeven * the passed GPIO descriptors.
490828546e2SGeert Uytterhoeven *
491828546e2SGeert Uytterhoeven * Return: An opaque object pointer, or an ERR_PTR()-encoded negative error
492828546e2SGeert Uytterhoeven * code on failure.
493828546e2SGeert Uytterhoeven */
gpiochip_fwd_create(struct device * dev,unsigned int ngpios,struct gpio_desc * descs[],unsigned long features)494828546e2SGeert Uytterhoeven static struct gpiochip_fwd *gpiochip_fwd_create(struct device *dev,
495828546e2SGeert Uytterhoeven unsigned int ngpios,
496*81674beaSAndy Shevchenko struct gpio_desc *descs[],
497*81674beaSAndy Shevchenko unsigned long features)
498828546e2SGeert Uytterhoeven {
499828546e2SGeert Uytterhoeven const char *label = dev_name(dev);
500828546e2SGeert Uytterhoeven struct gpiochip_fwd *fwd;
501828546e2SGeert Uytterhoeven struct gpio_chip *chip;
502828546e2SGeert Uytterhoeven unsigned int i;
503828546e2SGeert Uytterhoeven int error;
504828546e2SGeert Uytterhoeven
505aa4858ebSGeert Uytterhoeven fwd = devm_kzalloc(dev, struct_size(fwd, tmp, fwd_tmp_size(ngpios)),
506aa4858ebSGeert Uytterhoeven GFP_KERNEL);
507828546e2SGeert Uytterhoeven if (!fwd)
508828546e2SGeert Uytterhoeven return ERR_PTR(-ENOMEM);
509828546e2SGeert Uytterhoeven
510828546e2SGeert Uytterhoeven chip = &fwd->chip;
511828546e2SGeert Uytterhoeven
512828546e2SGeert Uytterhoeven /*
513828546e2SGeert Uytterhoeven * If any of the GPIO lines are sleeping, then the entire forwarder
514828546e2SGeert Uytterhoeven * will be sleeping.
515828546e2SGeert Uytterhoeven * If any of the chips support .set_config(), then the forwarder will
516828546e2SGeert Uytterhoeven * support setting configs.
517828546e2SGeert Uytterhoeven */
518828546e2SGeert Uytterhoeven for (i = 0; i < ngpios; i++) {
519828546e2SGeert Uytterhoeven struct gpio_chip *parent = gpiod_to_chip(descs[i]);
520828546e2SGeert Uytterhoeven
521a00128dfSGeert Uytterhoeven dev_dbg(dev, "%u => gpio %d irq %d\n", i,
522a00128dfSGeert Uytterhoeven desc_to_gpio(descs[i]), gpiod_to_irq(descs[i]));
523828546e2SGeert Uytterhoeven
524828546e2SGeert Uytterhoeven if (gpiod_cansleep(descs[i]))
525828546e2SGeert Uytterhoeven chip->can_sleep = true;
526828546e2SGeert Uytterhoeven if (parent && parent->set_config)
527828546e2SGeert Uytterhoeven chip->set_config = gpio_fwd_set_config;
528828546e2SGeert Uytterhoeven }
529828546e2SGeert Uytterhoeven
530828546e2SGeert Uytterhoeven chip->label = label;
531828546e2SGeert Uytterhoeven chip->parent = dev;
532828546e2SGeert Uytterhoeven chip->owner = THIS_MODULE;
533828546e2SGeert Uytterhoeven chip->get_direction = gpio_fwd_get_direction;
534828546e2SGeert Uytterhoeven chip->direction_input = gpio_fwd_direction_input;
535828546e2SGeert Uytterhoeven chip->direction_output = gpio_fwd_direction_output;
536828546e2SGeert Uytterhoeven chip->get = gpio_fwd_get;
53701e8d85bSAndy Shevchenko chip->get_multiple = gpio_fwd_get_multiple_locked;
538828546e2SGeert Uytterhoeven chip->set = gpio_fwd_set;
53901e8d85bSAndy Shevchenko chip->set_multiple = gpio_fwd_set_multiple_locked;
540a00128dfSGeert Uytterhoeven chip->to_irq = gpio_fwd_to_irq;
541828546e2SGeert Uytterhoeven chip->base = -1;
542828546e2SGeert Uytterhoeven chip->ngpio = ngpios;
543828546e2SGeert Uytterhoeven fwd->descs = descs;
544828546e2SGeert Uytterhoeven
545828546e2SGeert Uytterhoeven if (chip->can_sleep)
546828546e2SGeert Uytterhoeven mutex_init(&fwd->mlock);
547828546e2SGeert Uytterhoeven else
548828546e2SGeert Uytterhoeven spin_lock_init(&fwd->slock);
549828546e2SGeert Uytterhoeven
550*81674beaSAndy Shevchenko if (features & FWD_FEATURE_DELAY) {
551*81674beaSAndy Shevchenko error = gpiochip_fwd_setup_delay_line(dev, chip, fwd);
552*81674beaSAndy Shevchenko if (error)
553*81674beaSAndy Shevchenko return ERR_PTR(error);
554*81674beaSAndy Shevchenko }
555*81674beaSAndy Shevchenko
556828546e2SGeert Uytterhoeven error = devm_gpiochip_add_data(dev, chip, fwd);
557828546e2SGeert Uytterhoeven if (error)
558828546e2SGeert Uytterhoeven return ERR_PTR(error);
559828546e2SGeert Uytterhoeven
560828546e2SGeert Uytterhoeven return fwd;
561828546e2SGeert Uytterhoeven }
562828546e2SGeert Uytterhoeven
563828546e2SGeert Uytterhoeven
564828546e2SGeert Uytterhoeven /*
565828546e2SGeert Uytterhoeven * GPIO Aggregator platform device
566828546e2SGeert Uytterhoeven */
567828546e2SGeert Uytterhoeven
gpio_aggregator_probe(struct platform_device * pdev)568828546e2SGeert Uytterhoeven static int gpio_aggregator_probe(struct platform_device *pdev)
569828546e2SGeert Uytterhoeven {
570828546e2SGeert Uytterhoeven struct device *dev = &pdev->dev;
571828546e2SGeert Uytterhoeven struct gpio_desc **descs;
572828546e2SGeert Uytterhoeven struct gpiochip_fwd *fwd;
573*81674beaSAndy Shevchenko unsigned long features;
574828546e2SGeert Uytterhoeven int i, n;
575828546e2SGeert Uytterhoeven
576828546e2SGeert Uytterhoeven n = gpiod_count(dev, NULL);
577828546e2SGeert Uytterhoeven if (n < 0)
578828546e2SGeert Uytterhoeven return n;
579828546e2SGeert Uytterhoeven
580828546e2SGeert Uytterhoeven descs = devm_kmalloc_array(dev, n, sizeof(*descs), GFP_KERNEL);
581828546e2SGeert Uytterhoeven if (!descs)
582828546e2SGeert Uytterhoeven return -ENOMEM;
583828546e2SGeert Uytterhoeven
584828546e2SGeert Uytterhoeven for (i = 0; i < n; i++) {
585828546e2SGeert Uytterhoeven descs[i] = devm_gpiod_get_index(dev, NULL, i, GPIOD_ASIS);
586828546e2SGeert Uytterhoeven if (IS_ERR(descs[i]))
587828546e2SGeert Uytterhoeven return PTR_ERR(descs[i]);
588828546e2SGeert Uytterhoeven }
589828546e2SGeert Uytterhoeven
590*81674beaSAndy Shevchenko features = (uintptr_t)device_get_match_data(dev);
591*81674beaSAndy Shevchenko fwd = gpiochip_fwd_create(dev, n, descs, features);
592828546e2SGeert Uytterhoeven if (IS_ERR(fwd))
593828546e2SGeert Uytterhoeven return PTR_ERR(fwd);
594828546e2SGeert Uytterhoeven
595828546e2SGeert Uytterhoeven platform_set_drvdata(pdev, fwd);
596828546e2SGeert Uytterhoeven return 0;
597828546e2SGeert Uytterhoeven }
598828546e2SGeert Uytterhoeven
599828546e2SGeert Uytterhoeven static const struct of_device_id gpio_aggregator_dt_ids[] = {
600*81674beaSAndy Shevchenko {
601*81674beaSAndy Shevchenko .compatible = "gpio-delay",
602*81674beaSAndy Shevchenko .data = (void *)FWD_FEATURE_DELAY,
603*81674beaSAndy Shevchenko },
604828546e2SGeert Uytterhoeven /*
605828546e2SGeert Uytterhoeven * Add GPIO-operated devices controlled from userspace below,
606b89a9e98SAndy Shevchenko * or use "driver_override" in sysfs.
607828546e2SGeert Uytterhoeven */
6086e004a98SAndy Shevchenko {}
609828546e2SGeert Uytterhoeven };
610828546e2SGeert Uytterhoeven MODULE_DEVICE_TABLE(of, gpio_aggregator_dt_ids);
611828546e2SGeert Uytterhoeven
612828546e2SGeert Uytterhoeven static struct platform_driver gpio_aggregator_driver = {
613828546e2SGeert Uytterhoeven .probe = gpio_aggregator_probe,
614828546e2SGeert Uytterhoeven .driver = {
615828546e2SGeert Uytterhoeven .name = DRV_NAME,
616828546e2SGeert Uytterhoeven .groups = gpio_aggregator_groups,
617b89a9e98SAndy Shevchenko .of_match_table = gpio_aggregator_dt_ids,
618828546e2SGeert Uytterhoeven },
619828546e2SGeert Uytterhoeven };
620828546e2SGeert Uytterhoeven
gpio_aggregator_init(void)621828546e2SGeert Uytterhoeven static int __init gpio_aggregator_init(void)
622828546e2SGeert Uytterhoeven {
623828546e2SGeert Uytterhoeven return platform_driver_register(&gpio_aggregator_driver);
624828546e2SGeert Uytterhoeven }
625828546e2SGeert Uytterhoeven module_init(gpio_aggregator_init);
626828546e2SGeert Uytterhoeven
gpio_aggregator_exit(void)627828546e2SGeert Uytterhoeven static void __exit gpio_aggregator_exit(void)
628828546e2SGeert Uytterhoeven {
629828546e2SGeert Uytterhoeven gpio_aggregator_remove_all();
630828546e2SGeert Uytterhoeven platform_driver_unregister(&gpio_aggregator_driver);
631828546e2SGeert Uytterhoeven }
632828546e2SGeert Uytterhoeven module_exit(gpio_aggregator_exit);
633828546e2SGeert Uytterhoeven
634828546e2SGeert Uytterhoeven MODULE_AUTHOR("Geert Uytterhoeven <geert+renesas@glider.be>");
635828546e2SGeert Uytterhoeven MODULE_DESCRIPTION("GPIO Aggregator");
636828546e2SGeert Uytterhoeven MODULE_LICENSE("GPL v2");
637