Commit | Line | Data |
---|---|---|
cb8c474e BG |
1 | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | /* | |
3 | * GPIO testing driver based on configfs. | |
4 | * | |
5 | * Copyright (C) 2021 Bartosz Golaszewski <brgl@bgdev.pl> | |
6 | */ | |
7 | ||
8 | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt | |
9 | ||
10 | #include <linux/bitmap.h> | |
3faf89f2 | 11 | #include <linux/cleanup.h> |
cb8c474e BG |
12 | #include <linux/completion.h> |
13 | #include <linux/configfs.h> | |
14 | #include <linux/device.h> | |
15 | #include <linux/gpio/driver.h> | |
16 | #include <linux/gpio/machine.h> | |
17 | #include <linux/idr.h> | |
18 | #include <linux/interrupt.h> | |
19 | #include <linux/irq.h> | |
20 | #include <linux/irq_sim.h> | |
21 | #include <linux/list.h> | |
22 | #include <linux/mod_devicetable.h> | |
23 | #include <linux/module.h> | |
24 | #include <linux/mutex.h> | |
25 | #include <linux/notifier.h> | |
26 | #include <linux/platform_device.h> | |
27 | #include <linux/property.h> | |
28 | #include <linux/slab.h> | |
29 | #include <linux/string.h> | |
30 | #include <linux/string_helpers.h> | |
31 | #include <linux/sysfs.h> | |
32 | ||
33 | #include "gpiolib.h" | |
34 | ||
11e47bbd | 35 | #define GPIO_SIM_NGPIO_MAX 1024 |
cb8c474e BG |
36 | #define GPIO_SIM_PROP_MAX 4 /* Max 3 properties + sentinel. */ |
37 | #define GPIO_SIM_NUM_ATTRS 3 /* value, pull and sentinel */ | |
38 | ||
39 | static DEFINE_IDA(gpio_sim_ida); | |
40 | ||
41 | struct gpio_sim_chip { | |
42 | struct gpio_chip gc; | |
43 | unsigned long *direction_map; | |
44 | unsigned long *value_map; | |
45 | unsigned long *pull_map; | |
46 | struct irq_domain *irq_sim; | |
47 | struct mutex lock; | |
48 | const struct attribute_group **attr_groups; | |
49 | }; | |
50 | ||
51 | struct gpio_sim_attribute { | |
52 | struct device_attribute dev_attr; | |
53 | unsigned int offset; | |
54 | }; | |
55 | ||
56 | static struct gpio_sim_attribute * | |
57 | to_gpio_sim_attr(struct device_attribute *dev_attr) | |
58 | { | |
59 | return container_of(dev_attr, struct gpio_sim_attribute, dev_attr); | |
60 | } | |
61 | ||
62 | static int gpio_sim_apply_pull(struct gpio_sim_chip *chip, | |
63 | unsigned int offset, int value) | |
64 | { | |
65 | int irq, irq_type, ret; | |
66 | struct gpio_desc *desc; | |
67 | struct gpio_chip *gc; | |
68 | ||
69 | gc = &chip->gc; | |
70 | desc = &gc->gpiodev->descs[offset]; | |
71 | ||
3faf89f2 | 72 | guard(mutex)(&chip->lock); |
cb8c474e BG |
73 | |
74 | if (test_bit(FLAG_REQUESTED, &desc->flags) && | |
75 | !test_bit(FLAG_IS_OUT, &desc->flags)) { | |
76 | if (value == !!test_bit(offset, chip->value_map)) | |
77 | goto set_pull; | |
78 | ||
79 | /* | |
80 | * This is fine - it just means, nobody is listening | |
81 | * for interrupts on this line, otherwise | |
82 | * irq_create_mapping() would have been called from | |
83 | * the to_irq() callback. | |
84 | */ | |
85 | irq = irq_find_mapping(chip->irq_sim, offset); | |
86 | if (!irq) | |
87 | goto set_value; | |
88 | ||
89 | irq_type = irq_get_trigger_type(irq); | |
90 | ||
91 | if ((value && (irq_type & IRQ_TYPE_EDGE_RISING)) || | |
92 | (!value && (irq_type & IRQ_TYPE_EDGE_FALLING))) { | |
93 | ret = irq_set_irqchip_state(irq, IRQCHIP_STATE_PENDING, | |
94 | true); | |
95 | if (ret) | |
96 | goto set_pull; | |
97 | } | |
98 | } | |
99 | ||
100 | set_value: | |
101 | /* Change the value unless we're actively driving the line. */ | |
102 | if (!test_bit(FLAG_REQUESTED, &desc->flags) || | |
103 | !test_bit(FLAG_IS_OUT, &desc->flags)) | |
104 | __assign_bit(offset, chip->value_map, value); | |
105 | ||
106 | set_pull: | |
107 | __assign_bit(offset, chip->pull_map, value); | |
cb8c474e BG |
108 | return 0; |
109 | } | |
110 | ||
111 | static int gpio_sim_get(struct gpio_chip *gc, unsigned int offset) | |
112 | { | |
113 | struct gpio_sim_chip *chip = gpiochip_get_data(gc); | |
cb8c474e | 114 | |
3faf89f2 | 115 | guard(mutex)(&chip->lock); |
cb8c474e | 116 | |
3faf89f2 | 117 | return !!test_bit(offset, chip->value_map); |
cb8c474e BG |
118 | } |
119 | ||
120 | static void gpio_sim_set(struct gpio_chip *gc, unsigned int offset, int value) | |
121 | { | |
122 | struct gpio_sim_chip *chip = gpiochip_get_data(gc); | |
123 | ||
3faf89f2 BG |
124 | scoped_guard(mutex, &chip->lock) |
125 | __assign_bit(offset, chip->value_map, value); | |
cb8c474e BG |
126 | } |
127 | ||
128 | static int gpio_sim_get_multiple(struct gpio_chip *gc, | |
129 | unsigned long *mask, unsigned long *bits) | |
130 | { | |
131 | struct gpio_sim_chip *chip = gpiochip_get_data(gc); | |
132 | ||
3faf89f2 BG |
133 | scoped_guard(mutex, &chip->lock) |
134 | bitmap_replace(bits, bits, chip->value_map, mask, gc->ngpio); | |
cb8c474e BG |
135 | |
136 | return 0; | |
137 | } | |
138 | ||
139 | static void gpio_sim_set_multiple(struct gpio_chip *gc, | |
140 | unsigned long *mask, unsigned long *bits) | |
141 | { | |
142 | struct gpio_sim_chip *chip = gpiochip_get_data(gc); | |
143 | ||
3faf89f2 BG |
144 | scoped_guard(mutex, &chip->lock) |
145 | bitmap_replace(chip->value_map, chip->value_map, bits, mask, | |
146 | gc->ngpio); | |
cb8c474e BG |
147 | } |
148 | ||
149 | static int gpio_sim_direction_output(struct gpio_chip *gc, | |
150 | unsigned int offset, int value) | |
151 | { | |
152 | struct gpio_sim_chip *chip = gpiochip_get_data(gc); | |
153 | ||
3faf89f2 BG |
154 | scoped_guard(mutex, &chip->lock) { |
155 | __clear_bit(offset, chip->direction_map); | |
156 | __assign_bit(offset, chip->value_map, value); | |
157 | } | |
cb8c474e BG |
158 | |
159 | return 0; | |
160 | } | |
161 | ||
162 | static int gpio_sim_direction_input(struct gpio_chip *gc, unsigned int offset) | |
163 | { | |
164 | struct gpio_sim_chip *chip = gpiochip_get_data(gc); | |
165 | ||
3faf89f2 BG |
166 | scoped_guard(mutex, &chip->lock) |
167 | __set_bit(offset, chip->direction_map); | |
cb8c474e BG |
168 | |
169 | return 0; | |
170 | } | |
171 | ||
172 | static int gpio_sim_get_direction(struct gpio_chip *gc, unsigned int offset) | |
173 | { | |
174 | struct gpio_sim_chip *chip = gpiochip_get_data(gc); | |
175 | int direction; | |
176 | ||
3faf89f2 BG |
177 | scoped_guard(mutex, &chip->lock) |
178 | direction = !!test_bit(offset, chip->direction_map); | |
cb8c474e BG |
179 | |
180 | return direction ? GPIO_LINE_DIRECTION_IN : GPIO_LINE_DIRECTION_OUT; | |
181 | } | |
182 | ||
183 | static int gpio_sim_set_config(struct gpio_chip *gc, | |
184 | unsigned int offset, unsigned long config) | |
185 | { | |
186 | struct gpio_sim_chip *chip = gpiochip_get_data(gc); | |
187 | ||
188 | switch (pinconf_to_config_param(config)) { | |
189 | case PIN_CONFIG_BIAS_PULL_UP: | |
190 | return gpio_sim_apply_pull(chip, offset, 1); | |
191 | case PIN_CONFIG_BIAS_PULL_DOWN: | |
192 | return gpio_sim_apply_pull(chip, offset, 0); | |
193 | default: | |
194 | break; | |
195 | } | |
196 | ||
197 | return -ENOTSUPP; | |
198 | } | |
199 | ||
200 | static int gpio_sim_to_irq(struct gpio_chip *gc, unsigned int offset) | |
201 | { | |
202 | struct gpio_sim_chip *chip = gpiochip_get_data(gc); | |
203 | ||
204 | return irq_create_mapping(chip->irq_sim, offset); | |
205 | } | |
206 | ||
207 | static void gpio_sim_free(struct gpio_chip *gc, unsigned int offset) | |
208 | { | |
209 | struct gpio_sim_chip *chip = gpiochip_get_data(gc); | |
210 | ||
3faf89f2 BG |
211 | scoped_guard(mutex, &chip->lock) |
212 | __assign_bit(offset, chip->value_map, | |
213 | !!test_bit(offset, chip->pull_map)); | |
cb8c474e BG |
214 | } |
215 | ||
216 | static ssize_t gpio_sim_sysfs_val_show(struct device *dev, | |
217 | struct device_attribute *attr, char *buf) | |
218 | { | |
219 | struct gpio_sim_attribute *line_attr = to_gpio_sim_attr(attr); | |
220 | struct gpio_sim_chip *chip = dev_get_drvdata(dev); | |
221 | int val; | |
222 | ||
3faf89f2 BG |
223 | scoped_guard(mutex, &chip->lock) |
224 | val = !!test_bit(line_attr->offset, chip->value_map); | |
cb8c474e BG |
225 | |
226 | return sysfs_emit(buf, "%d\n", val); | |
227 | } | |
228 | ||
229 | static ssize_t gpio_sim_sysfs_val_store(struct device *dev, | |
230 | struct device_attribute *attr, | |
231 | const char *buf, size_t count) | |
232 | { | |
233 | /* | |
234 | * Not assigning this function will result in write() returning -EIO | |
235 | * which is confusing. Return -EPERM explicitly. | |
236 | */ | |
237 | return -EPERM; | |
238 | } | |
239 | ||
240 | static const char *const gpio_sim_sysfs_pull_strings[] = { | |
241 | [0] = "pull-down", | |
242 | [1] = "pull-up", | |
243 | }; | |
244 | ||
245 | static ssize_t gpio_sim_sysfs_pull_show(struct device *dev, | |
246 | struct device_attribute *attr, | |
247 | char *buf) | |
248 | { | |
249 | struct gpio_sim_attribute *line_attr = to_gpio_sim_attr(attr); | |
250 | struct gpio_sim_chip *chip = dev_get_drvdata(dev); | |
251 | int pull; | |
252 | ||
3faf89f2 BG |
253 | scoped_guard(mutex, &chip->lock) |
254 | pull = !!test_bit(line_attr->offset, chip->pull_map); | |
cb8c474e BG |
255 | |
256 | return sysfs_emit(buf, "%s\n", gpio_sim_sysfs_pull_strings[pull]); | |
257 | } | |
258 | ||
259 | static ssize_t gpio_sim_sysfs_pull_store(struct device *dev, | |
260 | struct device_attribute *attr, | |
261 | const char *buf, size_t len) | |
262 | { | |
263 | struct gpio_sim_attribute *line_attr = to_gpio_sim_attr(attr); | |
264 | struct gpio_sim_chip *chip = dev_get_drvdata(dev); | |
265 | int ret, pull; | |
266 | ||
267 | pull = sysfs_match_string(gpio_sim_sysfs_pull_strings, buf); | |
268 | if (pull < 0) | |
269 | return pull; | |
270 | ||
271 | ret = gpio_sim_apply_pull(chip, line_attr->offset, pull); | |
272 | if (ret) | |
273 | return ret; | |
274 | ||
275 | return len; | |
276 | } | |
277 | ||
278 | static void gpio_sim_mutex_destroy(void *data) | |
279 | { | |
280 | struct mutex *lock = data; | |
281 | ||
282 | mutex_destroy(lock); | |
283 | } | |
284 | ||
ab4109f9 BG |
285 | static void gpio_sim_dispose_mappings(void *data) |
286 | { | |
287 | struct gpio_sim_chip *chip = data; | |
288 | unsigned int i; | |
289 | ||
290 | for (i = 0; i < chip->gc.ngpio; i++) | |
291 | irq_dispose_mapping(irq_find_mapping(chip->irq_sim, i)); | |
292 | } | |
293 | ||
cb8c474e BG |
294 | static void gpio_sim_sysfs_remove(void *data) |
295 | { | |
296 | struct gpio_sim_chip *chip = data; | |
297 | ||
298 | sysfs_remove_groups(&chip->gc.gpiodev->dev.kobj, chip->attr_groups); | |
299 | } | |
300 | ||
301 | static int gpio_sim_setup_sysfs(struct gpio_sim_chip *chip) | |
302 | { | |
303 | struct device_attribute *val_dev_attr, *pull_dev_attr; | |
304 | struct gpio_sim_attribute *val_attr, *pull_attr; | |
305 | unsigned int num_lines = chip->gc.ngpio; | |
306 | struct device *dev = chip->gc.parent; | |
307 | struct attribute_group *attr_group; | |
308 | struct attribute **attrs; | |
309 | int i, ret; | |
310 | ||
311 | chip->attr_groups = devm_kcalloc(dev, sizeof(*chip->attr_groups), | |
312 | num_lines + 1, GFP_KERNEL); | |
313 | if (!chip->attr_groups) | |
314 | return -ENOMEM; | |
315 | ||
316 | for (i = 0; i < num_lines; i++) { | |
317 | attr_group = devm_kzalloc(dev, sizeof(*attr_group), GFP_KERNEL); | |
c680c6a8 CJ |
318 | attrs = devm_kcalloc(dev, GPIO_SIM_NUM_ATTRS, sizeof(*attrs), |
319 | GFP_KERNEL); | |
cb8c474e BG |
320 | val_attr = devm_kzalloc(dev, sizeof(*val_attr), GFP_KERNEL); |
321 | pull_attr = devm_kzalloc(dev, sizeof(*pull_attr), GFP_KERNEL); | |
322 | if (!attr_group || !attrs || !val_attr || !pull_attr) | |
323 | return -ENOMEM; | |
324 | ||
325 | attr_group->name = devm_kasprintf(dev, GFP_KERNEL, | |
326 | "sim_gpio%u", i); | |
327 | if (!attr_group->name) | |
328 | return -ENOMEM; | |
329 | ||
330 | val_attr->offset = pull_attr->offset = i; | |
331 | ||
332 | val_dev_attr = &val_attr->dev_attr; | |
333 | pull_dev_attr = &pull_attr->dev_attr; | |
334 | ||
335 | sysfs_attr_init(&val_dev_attr->attr); | |
336 | sysfs_attr_init(&pull_dev_attr->attr); | |
337 | ||
338 | val_dev_attr->attr.name = "value"; | |
339 | pull_dev_attr->attr.name = "pull"; | |
340 | ||
341 | val_dev_attr->attr.mode = pull_dev_attr->attr.mode = 0644; | |
342 | ||
343 | val_dev_attr->show = gpio_sim_sysfs_val_show; | |
344 | val_dev_attr->store = gpio_sim_sysfs_val_store; | |
345 | pull_dev_attr->show = gpio_sim_sysfs_pull_show; | |
346 | pull_dev_attr->store = gpio_sim_sysfs_pull_store; | |
347 | ||
348 | attrs[0] = &val_dev_attr->attr; | |
349 | attrs[1] = &pull_dev_attr->attr; | |
350 | ||
351 | attr_group->attrs = attrs; | |
352 | chip->attr_groups[i] = attr_group; | |
353 | } | |
354 | ||
355 | ret = sysfs_create_groups(&chip->gc.gpiodev->dev.kobj, | |
356 | chip->attr_groups); | |
357 | if (ret) | |
358 | return ret; | |
359 | ||
360 | return devm_add_action_or_reset(dev, gpio_sim_sysfs_remove, chip); | |
361 | } | |
362 | ||
363 | static int gpio_sim_add_bank(struct fwnode_handle *swnode, struct device *dev) | |
364 | { | |
365 | struct gpio_sim_chip *chip; | |
366 | struct gpio_chip *gc; | |
367 | const char *label; | |
368 | u32 num_lines; | |
369 | int ret; | |
370 | ||
371 | ret = fwnode_property_read_u32(swnode, "ngpios", &num_lines); | |
372 | if (ret) | |
373 | return ret; | |
374 | ||
11e47bbd BG |
375 | if (num_lines > GPIO_SIM_NGPIO_MAX) |
376 | return -ERANGE; | |
377 | ||
cb8c474e BG |
378 | ret = fwnode_property_read_string(swnode, "gpio-sim,label", &label); |
379 | if (ret) { | |
4827aae0 AS |
380 | label = devm_kasprintf(dev, GFP_KERNEL, "%s-%pfwP", |
381 | dev_name(dev), swnode); | |
cb8c474e BG |
382 | if (!label) |
383 | return -ENOMEM; | |
384 | } | |
385 | ||
386 | chip = devm_kzalloc(dev, sizeof(*chip), GFP_KERNEL); | |
387 | if (!chip) | |
388 | return -ENOMEM; | |
389 | ||
390 | chip->direction_map = devm_bitmap_alloc(dev, num_lines, GFP_KERNEL); | |
391 | if (!chip->direction_map) | |
392 | return -ENOMEM; | |
393 | ||
394 | /* Default to input mode. */ | |
395 | bitmap_fill(chip->direction_map, num_lines); | |
396 | ||
397 | chip->value_map = devm_bitmap_zalloc(dev, num_lines, GFP_KERNEL); | |
398 | if (!chip->value_map) | |
399 | return -ENOMEM; | |
400 | ||
401 | chip->pull_map = devm_bitmap_zalloc(dev, num_lines, GFP_KERNEL); | |
402 | if (!chip->pull_map) | |
403 | return -ENOMEM; | |
404 | ||
6e39c1ac | 405 | chip->irq_sim = devm_irq_domain_create_sim(dev, swnode, num_lines); |
cb8c474e BG |
406 | if (IS_ERR(chip->irq_sim)) |
407 | return PTR_ERR(chip->irq_sim); | |
408 | ||
ab4109f9 BG |
409 | ret = devm_add_action_or_reset(dev, gpio_sim_dispose_mappings, chip); |
410 | if (ret) | |
411 | return ret; | |
412 | ||
cb8c474e BG |
413 | mutex_init(&chip->lock); |
414 | ret = devm_add_action_or_reset(dev, gpio_sim_mutex_destroy, | |
415 | &chip->lock); | |
416 | if (ret) | |
417 | return ret; | |
418 | ||
419 | gc = &chip->gc; | |
420 | gc->base = -1; | |
421 | gc->ngpio = num_lines; | |
422 | gc->label = label; | |
423 | gc->owner = THIS_MODULE; | |
424 | gc->parent = dev; | |
425 | gc->fwnode = swnode; | |
426 | gc->get = gpio_sim_get; | |
427 | gc->set = gpio_sim_set; | |
428 | gc->get_multiple = gpio_sim_get_multiple; | |
429 | gc->set_multiple = gpio_sim_set_multiple; | |
430 | gc->direction_output = gpio_sim_direction_output; | |
431 | gc->direction_input = gpio_sim_direction_input; | |
432 | gc->get_direction = gpio_sim_get_direction; | |
433 | gc->set_config = gpio_sim_set_config; | |
434 | gc->to_irq = gpio_sim_to_irq; | |
435 | gc->free = gpio_sim_free; | |
5a78d5db | 436 | gc->can_sleep = true; |
cb8c474e BG |
437 | |
438 | ret = devm_gpiochip_add_data(dev, gc, chip); | |
439 | if (ret) | |
440 | return ret; | |
441 | ||
442 | /* Used by sysfs and configfs callbacks. */ | |
443 | dev_set_drvdata(&gc->gpiodev->dev, chip); | |
444 | ||
445 | return gpio_sim_setup_sysfs(chip); | |
446 | } | |
447 | ||
448 | static int gpio_sim_probe(struct platform_device *pdev) | |
449 | { | |
450 | struct device *dev = &pdev->dev; | |
451 | struct fwnode_handle *swnode; | |
452 | int ret; | |
453 | ||
454 | device_for_each_child_node(dev, swnode) { | |
455 | ret = gpio_sim_add_bank(swnode, dev); | |
a2d05fb7 YY |
456 | if (ret) { |
457 | fwnode_handle_put(swnode); | |
cb8c474e | 458 | return ret; |
a2d05fb7 | 459 | } |
cb8c474e BG |
460 | } |
461 | ||
462 | return 0; | |
463 | } | |
464 | ||
465 | static const struct of_device_id gpio_sim_of_match[] = { | |
466 | { .compatible = "gpio-simulator" }, | |
467 | { } | |
468 | }; | |
469 | MODULE_DEVICE_TABLE(of, gpio_sim_of_match); | |
470 | ||
471 | static struct platform_driver gpio_sim_driver = { | |
472 | .driver = { | |
473 | .name = "gpio-sim", | |
474 | .of_match_table = gpio_sim_of_match, | |
475 | }, | |
476 | .probe = gpio_sim_probe, | |
477 | }; | |
478 | ||
479 | struct gpio_sim_device { | |
480 | struct config_group group; | |
481 | ||
482 | /* | |
483 | * If pdev is NULL, the device is 'pending' (waiting for configuration). | |
484 | * Once the pointer is assigned, the device has been created and the | |
485 | * item is 'live'. | |
486 | */ | |
487 | struct platform_device *pdev; | |
488 | int id; | |
489 | ||
490 | /* | |
491 | * Each configfs filesystem operation is protected with the subsystem | |
492 | * mutex. Each separate attribute is protected with the buffer mutex. | |
493 | * This structure however can be modified by callbacks of different | |
494 | * attributes so we need another lock. | |
495 | * | |
43818a4b | 496 | * We use this lock for protecting all data structures owned by this |
cb8c474e BG |
497 | * object too. |
498 | */ | |
499 | struct mutex lock; | |
500 | ||
501 | /* | |
502 | * This is used to synchronously wait for the driver's probe to complete | |
503 | * and notify the user-space about any errors. | |
504 | */ | |
505 | struct notifier_block bus_notifier; | |
506 | struct completion probe_completion; | |
507 | bool driver_bound; | |
508 | ||
509 | struct gpiod_hog *hogs; | |
510 | ||
511 | struct list_head bank_list; | |
512 | }; | |
513 | ||
514 | /* This is called with dev->lock already taken. */ | |
515 | static int gpio_sim_bus_notifier_call(struct notifier_block *nb, | |
516 | unsigned long action, void *data) | |
517 | { | |
518 | struct gpio_sim_device *simdev = container_of(nb, | |
519 | struct gpio_sim_device, | |
520 | bus_notifier); | |
521 | struct device *dev = data; | |
522 | char devname[32]; | |
523 | ||
524 | snprintf(devname, sizeof(devname), "gpio-sim.%u", simdev->id); | |
525 | ||
526 | if (strcmp(dev_name(dev), devname) == 0) { | |
527 | if (action == BUS_NOTIFY_BOUND_DRIVER) | |
528 | simdev->driver_bound = true; | |
529 | else if (action == BUS_NOTIFY_DRIVER_NOT_BOUND) | |
530 | simdev->driver_bound = false; | |
531 | else | |
532 | return NOTIFY_DONE; | |
533 | ||
534 | complete(&simdev->probe_completion); | |
535 | return NOTIFY_OK; | |
536 | } | |
537 | ||
538 | return NOTIFY_DONE; | |
539 | } | |
540 | ||
541 | static struct gpio_sim_device *to_gpio_sim_device(struct config_item *item) | |
542 | { | |
543 | struct config_group *group = to_config_group(item); | |
544 | ||
545 | return container_of(group, struct gpio_sim_device, group); | |
546 | } | |
547 | ||
548 | struct gpio_sim_bank { | |
549 | struct config_group group; | |
550 | ||
551 | /* | |
552 | * We could have used the ci_parent field of the config_item but | |
553 | * configfs is stupid and calls the item's release callback after | |
554 | * already having cleared the parent pointer even though the parent | |
555 | * is guaranteed to survive the child... | |
556 | * | |
557 | * So we need to store the pointer to the parent struct here. We can | |
558 | * dereference it anywhere we need with no checks and no locking as | |
55d01c98 | 559 | * it's guaranteed to survive the children and protected by configfs |
cb8c474e BG |
560 | * locks. |
561 | * | |
562 | * Same for other structures. | |
563 | */ | |
564 | struct gpio_sim_device *parent; | |
565 | struct list_head siblings; | |
566 | ||
567 | char *label; | |
568 | unsigned int num_lines; | |
569 | ||
570 | struct list_head line_list; | |
571 | ||
572 | struct fwnode_handle *swnode; | |
573 | }; | |
574 | ||
575 | static struct gpio_sim_bank *to_gpio_sim_bank(struct config_item *item) | |
576 | { | |
577 | struct config_group *group = to_config_group(item); | |
578 | ||
579 | return container_of(group, struct gpio_sim_bank, group); | |
580 | } | |
581 | ||
c162ca0b BG |
582 | static bool gpio_sim_bank_has_label(struct gpio_sim_bank *bank) |
583 | { | |
584 | return bank->label && *bank->label; | |
585 | } | |
586 | ||
cb8c474e BG |
587 | static struct gpio_sim_device * |
588 | gpio_sim_bank_get_device(struct gpio_sim_bank *bank) | |
589 | { | |
590 | return bank->parent; | |
591 | } | |
592 | ||
593 | struct gpio_sim_hog; | |
594 | ||
595 | struct gpio_sim_line { | |
596 | struct config_group group; | |
597 | ||
598 | struct gpio_sim_bank *parent; | |
599 | struct list_head siblings; | |
600 | ||
601 | unsigned int offset; | |
602 | char *name; | |
603 | ||
604 | /* There can only be one hog per line. */ | |
605 | struct gpio_sim_hog *hog; | |
606 | }; | |
607 | ||
608 | static struct gpio_sim_line *to_gpio_sim_line(struct config_item *item) | |
609 | { | |
610 | struct config_group *group = to_config_group(item); | |
611 | ||
612 | return container_of(group, struct gpio_sim_line, group); | |
613 | } | |
614 | ||
615 | static struct gpio_sim_device * | |
616 | gpio_sim_line_get_device(struct gpio_sim_line *line) | |
617 | { | |
618 | struct gpio_sim_bank *bank = line->parent; | |
619 | ||
620 | return gpio_sim_bank_get_device(bank); | |
621 | } | |
622 | ||
623 | struct gpio_sim_hog { | |
624 | struct config_item item; | |
625 | struct gpio_sim_line *parent; | |
626 | ||
627 | char *name; | |
628 | int dir; | |
629 | }; | |
630 | ||
631 | static struct gpio_sim_hog *to_gpio_sim_hog(struct config_item *item) | |
632 | { | |
633 | return container_of(item, struct gpio_sim_hog, item); | |
634 | } | |
635 | ||
636 | static struct gpio_sim_device *gpio_sim_hog_get_device(struct gpio_sim_hog *hog) | |
637 | { | |
638 | struct gpio_sim_line *line = hog->parent; | |
639 | ||
640 | return gpio_sim_line_get_device(line); | |
641 | } | |
642 | ||
643 | static bool gpio_sim_device_is_live_unlocked(struct gpio_sim_device *dev) | |
644 | { | |
645 | return !!dev->pdev; | |
646 | } | |
647 | ||
648 | static char *gpio_sim_strdup_trimmed(const char *str, size_t count) | |
649 | { | |
ba0294df | 650 | char *trimmed; |
cb8c474e | 651 | |
ba0294df BG |
652 | trimmed = kstrndup(skip_spaces(str), count, GFP_KERNEL); |
653 | if (!trimmed) | |
cb8c474e BG |
654 | return NULL; |
655 | ||
ba0294df | 656 | return strim(trimmed); |
cb8c474e BG |
657 | } |
658 | ||
659 | static ssize_t gpio_sim_device_config_dev_name_show(struct config_item *item, | |
660 | char *page) | |
661 | { | |
662 | struct gpio_sim_device *dev = to_gpio_sim_device(item); | |
663 | struct platform_device *pdev; | |
cb8c474e | 664 | |
3faf89f2 BG |
665 | guard(mutex)(&dev->lock); |
666 | ||
cb8c474e BG |
667 | pdev = dev->pdev; |
668 | if (pdev) | |
3faf89f2 | 669 | return sprintf(page, "%s\n", dev_name(&pdev->dev)); |
cb8c474e | 670 | |
3faf89f2 | 671 | return sprintf(page, "gpio-sim.%d\n", dev->id); |
cb8c474e BG |
672 | } |
673 | ||
674 | CONFIGFS_ATTR_RO(gpio_sim_device_config_, dev_name); | |
675 | ||
676 | static ssize_t | |
677 | gpio_sim_device_config_live_show(struct config_item *item, char *page) | |
678 | { | |
679 | struct gpio_sim_device *dev = to_gpio_sim_device(item); | |
680 | bool live; | |
681 | ||
3faf89f2 BG |
682 | scoped_guard(mutex, &dev->lock) |
683 | live = gpio_sim_device_is_live_unlocked(dev); | |
cb8c474e BG |
684 | |
685 | return sprintf(page, "%c\n", live ? '1' : '0'); | |
686 | } | |
687 | ||
688 | static char **gpio_sim_make_line_names(struct gpio_sim_bank *bank, | |
689 | unsigned int *line_names_size) | |
690 | { | |
691 | unsigned int max_offset = 0; | |
692 | bool has_line_names = false; | |
693 | struct gpio_sim_line *line; | |
694 | char **line_names; | |
695 | ||
696 | list_for_each_entry(line, &bank->line_list, siblings) { | |
d7459efc KG |
697 | if (line->offset >= bank->num_lines) |
698 | continue; | |
699 | ||
cb8c474e BG |
700 | if (line->name) { |
701 | if (line->offset > max_offset) | |
702 | max_offset = line->offset; | |
703 | ||
704 | /* | |
705 | * max_offset can stay at 0 so it's not an indicator | |
706 | * of whether line names were configured at all. | |
707 | */ | |
708 | has_line_names = true; | |
709 | } | |
710 | } | |
711 | ||
712 | if (!has_line_names) | |
713 | /* | |
714 | * This is not an error - NULL means, there are no line | |
715 | * names configured. | |
716 | */ | |
717 | return NULL; | |
718 | ||
719 | *line_names_size = max_offset + 1; | |
720 | ||
721 | line_names = kcalloc(*line_names_size, sizeof(*line_names), GFP_KERNEL); | |
722 | if (!line_names) | |
723 | return ERR_PTR(-ENOMEM); | |
724 | ||
95ae9979 | 725 | list_for_each_entry(line, &bank->line_list, siblings) { |
d7459efc KG |
726 | if (line->offset >= bank->num_lines) |
727 | continue; | |
728 | ||
95ae9979 KG |
729 | if (line->name && (line->offset <= max_offset)) |
730 | line_names[line->offset] = line->name; | |
731 | } | |
cb8c474e BG |
732 | |
733 | return line_names; | |
734 | } | |
735 | ||
736 | static void gpio_sim_remove_hogs(struct gpio_sim_device *dev) | |
737 | { | |
738 | struct gpiod_hog *hog; | |
739 | ||
740 | if (!dev->hogs) | |
741 | return; | |
742 | ||
743 | gpiod_remove_hogs(dev->hogs); | |
744 | ||
79eeab1d | 745 | for (hog = dev->hogs; hog->chip_label; hog++) { |
cb8c474e BG |
746 | kfree(hog->chip_label); |
747 | kfree(hog->line_name); | |
748 | } | |
749 | ||
750 | kfree(dev->hogs); | |
751 | dev->hogs = NULL; | |
752 | } | |
753 | ||
754 | static int gpio_sim_add_hogs(struct gpio_sim_device *dev) | |
755 | { | |
756 | unsigned int num_hogs = 0, idx = 0; | |
757 | struct gpio_sim_bank *bank; | |
758 | struct gpio_sim_line *line; | |
759 | struct gpiod_hog *hog; | |
760 | ||
761 | list_for_each_entry(bank, &dev->bank_list, siblings) { | |
762 | list_for_each_entry(line, &bank->line_list, siblings) { | |
d7459efc KG |
763 | if (line->offset >= bank->num_lines) |
764 | continue; | |
765 | ||
cb8c474e BG |
766 | if (line->hog) |
767 | num_hogs++; | |
768 | } | |
769 | } | |
770 | ||
771 | if (!num_hogs) | |
772 | return 0; | |
773 | ||
774 | /* Allocate one more for the sentinel. */ | |
775 | dev->hogs = kcalloc(num_hogs + 1, sizeof(*dev->hogs), GFP_KERNEL); | |
776 | if (!dev->hogs) | |
777 | return -ENOMEM; | |
778 | ||
779 | list_for_each_entry(bank, &dev->bank_list, siblings) { | |
780 | list_for_each_entry(line, &bank->line_list, siblings) { | |
d7459efc KG |
781 | if (line->offset >= bank->num_lines) |
782 | continue; | |
783 | ||
cb8c474e BG |
784 | if (!line->hog) |
785 | continue; | |
786 | ||
787 | hog = &dev->hogs[idx++]; | |
788 | ||
789 | /* | |
790 | * We need to make this string manually because at this | |
791 | * point the device doesn't exist yet and so dev_name() | |
792 | * is not available. | |
793 | */ | |
c162ca0b BG |
794 | if (gpio_sim_bank_has_label(bank)) |
795 | hog->chip_label = kstrdup(bank->label, | |
796 | GFP_KERNEL); | |
797 | else | |
798 | hog->chip_label = kasprintf(GFP_KERNEL, | |
4827aae0 | 799 | "gpio-sim.%u-%pfwP", |
c162ca0b | 800 | dev->id, |
4827aae0 | 801 | bank->swnode); |
cb8c474e BG |
802 | if (!hog->chip_label) { |
803 | gpio_sim_remove_hogs(dev); | |
804 | return -ENOMEM; | |
805 | } | |
806 | ||
807 | /* | |
808 | * We need to duplicate this because the hog config | |
809 | * item can be removed at any time (and we can't block | |
810 | * it) and gpiolib doesn't make a deep copy of the hog | |
811 | * data. | |
812 | */ | |
813 | if (line->hog->name) { | |
814 | hog->line_name = kstrdup(line->hog->name, | |
815 | GFP_KERNEL); | |
816 | if (!hog->line_name) { | |
817 | gpio_sim_remove_hogs(dev); | |
818 | return -ENOMEM; | |
819 | } | |
820 | } | |
821 | ||
822 | hog->chip_hwnum = line->offset; | |
823 | hog->dflags = line->hog->dir; | |
824 | } | |
825 | } | |
826 | ||
827 | gpiod_add_hogs(dev->hogs); | |
828 | ||
829 | return 0; | |
830 | } | |
831 | ||
832 | static struct fwnode_handle * | |
833 | gpio_sim_make_bank_swnode(struct gpio_sim_bank *bank, | |
834 | struct fwnode_handle *parent) | |
835 | { | |
836 | struct property_entry properties[GPIO_SIM_PROP_MAX]; | |
837 | unsigned int prop_idx = 0, line_names_size = 0; | |
3faf89f2 | 838 | char **line_names __free(kfree) = NULL; |
cb8c474e BG |
839 | |
840 | memset(properties, 0, sizeof(properties)); | |
841 | ||
842 | properties[prop_idx++] = PROPERTY_ENTRY_U32("ngpios", bank->num_lines); | |
843 | ||
c162ca0b | 844 | if (gpio_sim_bank_has_label(bank)) |
cb8c474e BG |
845 | properties[prop_idx++] = PROPERTY_ENTRY_STRING("gpio-sim,label", |
846 | bank->label); | |
847 | ||
848 | line_names = gpio_sim_make_line_names(bank, &line_names_size); | |
849 | if (IS_ERR(line_names)) | |
850 | return ERR_CAST(line_names); | |
851 | ||
852 | if (line_names) | |
853 | properties[prop_idx++] = PROPERTY_ENTRY_STRING_ARRAY_LEN( | |
854 | "gpio-line-names", | |
855 | line_names, line_names_size); | |
856 | ||
3faf89f2 | 857 | return fwnode_create_software_node(properties, parent); |
cb8c474e BG |
858 | } |
859 | ||
860 | static void gpio_sim_remove_swnode_recursive(struct fwnode_handle *swnode) | |
861 | { | |
862 | struct fwnode_handle *child; | |
863 | ||
864 | fwnode_for_each_child_node(swnode, child) | |
865 | fwnode_remove_software_node(child); | |
866 | ||
867 | fwnode_remove_software_node(swnode); | |
868 | } | |
869 | ||
870 | static bool gpio_sim_bank_labels_non_unique(struct gpio_sim_device *dev) | |
871 | { | |
872 | struct gpio_sim_bank *this, *pos; | |
873 | ||
874 | list_for_each_entry(this, &dev->bank_list, siblings) { | |
875 | list_for_each_entry(pos, &dev->bank_list, siblings) { | |
876 | if (this == pos || (!this->label || !pos->label)) | |
877 | continue; | |
878 | ||
879 | if (strcmp(this->label, pos->label) == 0) | |
880 | return true; | |
881 | } | |
882 | } | |
883 | ||
884 | return false; | |
885 | } | |
886 | ||
887 | static int gpio_sim_device_activate_unlocked(struct gpio_sim_device *dev) | |
888 | { | |
889 | struct platform_device_info pdevinfo; | |
890 | struct fwnode_handle *swnode; | |
891 | struct platform_device *pdev; | |
892 | struct gpio_sim_bank *bank; | |
893 | int ret; | |
894 | ||
895 | if (list_empty(&dev->bank_list)) | |
896 | return -ENODATA; | |
897 | ||
898 | /* | |
899 | * Non-unique GPIO device labels are a corner-case we don't support | |
900 | * as it would interfere with machine hogging mechanism and has little | |
901 | * use in real life. | |
902 | */ | |
903 | if (gpio_sim_bank_labels_non_unique(dev)) | |
904 | return -EINVAL; | |
905 | ||
906 | memset(&pdevinfo, 0, sizeof(pdevinfo)); | |
907 | ||
908 | swnode = fwnode_create_software_node(NULL, NULL); | |
909 | if (IS_ERR(swnode)) | |
910 | return PTR_ERR(swnode); | |
911 | ||
912 | list_for_each_entry(bank, &dev->bank_list, siblings) { | |
913 | bank->swnode = gpio_sim_make_bank_swnode(bank, swnode); | |
c08995bf TR |
914 | if (IS_ERR(bank->swnode)) { |
915 | ret = PTR_ERR(bank->swnode); | |
cb8c474e BG |
916 | gpio_sim_remove_swnode_recursive(swnode); |
917 | return ret; | |
918 | } | |
919 | } | |
920 | ||
921 | ret = gpio_sim_add_hogs(dev); | |
922 | if (ret) { | |
923 | gpio_sim_remove_swnode_recursive(swnode); | |
924 | return ret; | |
925 | } | |
926 | ||
927 | pdevinfo.name = "gpio-sim"; | |
928 | pdevinfo.fwnode = swnode; | |
929 | pdevinfo.id = dev->id; | |
930 | ||
931 | reinit_completion(&dev->probe_completion); | |
932 | dev->driver_bound = false; | |
933 | bus_register_notifier(&platform_bus_type, &dev->bus_notifier); | |
934 | ||
935 | pdev = platform_device_register_full(&pdevinfo); | |
936 | if (IS_ERR(pdev)) { | |
937 | bus_unregister_notifier(&platform_bus_type, &dev->bus_notifier); | |
938 | gpio_sim_remove_hogs(dev); | |
939 | gpio_sim_remove_swnode_recursive(swnode); | |
940 | return PTR_ERR(pdev); | |
941 | } | |
942 | ||
943 | wait_for_completion(&dev->probe_completion); | |
944 | bus_unregister_notifier(&platform_bus_type, &dev->bus_notifier); | |
945 | ||
946 | if (!dev->driver_bound) { | |
947 | /* Probe failed, check kernel log. */ | |
948 | platform_device_unregister(pdev); | |
949 | gpio_sim_remove_hogs(dev); | |
950 | gpio_sim_remove_swnode_recursive(swnode); | |
951 | return -ENXIO; | |
952 | } | |
953 | ||
954 | dev->pdev = pdev; | |
955 | ||
956 | return 0; | |
957 | } | |
958 | ||
959 | static void gpio_sim_device_deactivate_unlocked(struct gpio_sim_device *dev) | |
960 | { | |
961 | struct fwnode_handle *swnode; | |
962 | ||
963 | swnode = dev_fwnode(&dev->pdev->dev); | |
964 | platform_device_unregister(dev->pdev); | |
0c14f3aa | 965 | gpio_sim_remove_hogs(dev); |
cb8c474e BG |
966 | gpio_sim_remove_swnode_recursive(swnode); |
967 | dev->pdev = NULL; | |
cb8c474e BG |
968 | } |
969 | ||
970 | static ssize_t | |
971 | gpio_sim_device_config_live_store(struct config_item *item, | |
972 | const char *page, size_t count) | |
973 | { | |
974 | struct gpio_sim_device *dev = to_gpio_sim_device(item); | |
975 | bool live; | |
976 | int ret; | |
977 | ||
978 | ret = kstrtobool(page, &live); | |
979 | if (ret) | |
980 | return ret; | |
981 | ||
3faf89f2 | 982 | guard(mutex)(&dev->lock); |
cb8c474e | 983 | |
a40fe1ff | 984 | if (live == gpio_sim_device_is_live_unlocked(dev)) |
cb8c474e BG |
985 | ret = -EPERM; |
986 | else if (live) | |
987 | ret = gpio_sim_device_activate_unlocked(dev); | |
988 | else | |
989 | gpio_sim_device_deactivate_unlocked(dev); | |
990 | ||
cb8c474e BG |
991 | return ret ?: count; |
992 | } | |
993 | ||
994 | CONFIGFS_ATTR(gpio_sim_device_config_, live); | |
995 | ||
996 | static struct configfs_attribute *gpio_sim_device_config_attrs[] = { | |
997 | &gpio_sim_device_config_attr_dev_name, | |
998 | &gpio_sim_device_config_attr_live, | |
999 | NULL | |
1000 | }; | |
1001 | ||
1002 | struct gpio_sim_chip_name_ctx { | |
7329b071 | 1003 | struct fwnode_handle *swnode; |
cb8c474e BG |
1004 | char *page; |
1005 | }; | |
1006 | ||
1007 | static int gpio_sim_emit_chip_name(struct device *dev, void *data) | |
1008 | { | |
1009 | struct gpio_sim_chip_name_ctx *ctx = data; | |
cb8c474e BG |
1010 | |
1011 | /* This would be the sysfs device exported in /sys/class/gpio. */ | |
1012 | if (dev->class) | |
1013 | return 0; | |
1014 | ||
7329b071 BG |
1015 | if (device_match_fwnode(dev, ctx->swnode)) |
1016 | return sprintf(ctx->page, "%s\n", dev_name(dev)); | |
cb8c474e | 1017 | |
7329b071 | 1018 | return 0; |
cb8c474e BG |
1019 | } |
1020 | ||
1021 | static ssize_t gpio_sim_bank_config_chip_name_show(struct config_item *item, | |
1022 | char *page) | |
1023 | { | |
1024 | struct gpio_sim_bank *bank = to_gpio_sim_bank(item); | |
1025 | struct gpio_sim_device *dev = gpio_sim_bank_get_device(bank); | |
7329b071 | 1026 | struct gpio_sim_chip_name_ctx ctx = { bank->swnode, page }; |
cb8c474e | 1027 | |
3faf89f2 BG |
1028 | guard(mutex)(&dev->lock); |
1029 | ||
cb8c474e | 1030 | if (gpio_sim_device_is_live_unlocked(dev)) |
3faf89f2 BG |
1031 | return device_for_each_child(&dev->pdev->dev, &ctx, |
1032 | gpio_sim_emit_chip_name); | |
cb8c474e | 1033 | |
3faf89f2 | 1034 | return sprintf(page, "none\n"); |
cb8c474e BG |
1035 | } |
1036 | ||
1037 | CONFIGFS_ATTR_RO(gpio_sim_bank_config_, chip_name); | |
1038 | ||
1039 | static ssize_t | |
1040 | gpio_sim_bank_config_label_show(struct config_item *item, char *page) | |
1041 | { | |
1042 | struct gpio_sim_bank *bank = to_gpio_sim_bank(item); | |
1043 | struct gpio_sim_device *dev = gpio_sim_bank_get_device(bank); | |
cb8c474e | 1044 | |
3faf89f2 | 1045 | guard(mutex)(&dev->lock); |
cb8c474e | 1046 | |
3faf89f2 | 1047 | return sprintf(page, "%s\n", bank->label ?: ""); |
cb8c474e BG |
1048 | } |
1049 | ||
1050 | static ssize_t gpio_sim_bank_config_label_store(struct config_item *item, | |
1051 | const char *page, size_t count) | |
1052 | { | |
1053 | struct gpio_sim_bank *bank = to_gpio_sim_bank(item); | |
1054 | struct gpio_sim_device *dev = gpio_sim_bank_get_device(bank); | |
1055 | char *trimmed; | |
1056 | ||
3faf89f2 | 1057 | guard(mutex)(&dev->lock); |
cb8c474e | 1058 | |
3faf89f2 | 1059 | if (gpio_sim_device_is_live_unlocked(dev)) |
cb8c474e | 1060 | return -EBUSY; |
cb8c474e BG |
1061 | |
1062 | trimmed = gpio_sim_strdup_trimmed(page, count); | |
3faf89f2 | 1063 | if (!trimmed) |
cb8c474e | 1064 | return -ENOMEM; |
cb8c474e BG |
1065 | |
1066 | kfree(bank->label); | |
1067 | bank->label = trimmed; | |
1068 | ||
cb8c474e BG |
1069 | return count; |
1070 | } | |
1071 | ||
1072 | CONFIGFS_ATTR(gpio_sim_bank_config_, label); | |
1073 | ||
1074 | static ssize_t | |
1075 | gpio_sim_bank_config_num_lines_show(struct config_item *item, char *page) | |
1076 | { | |
1077 | struct gpio_sim_bank *bank = to_gpio_sim_bank(item); | |
1078 | struct gpio_sim_device *dev = gpio_sim_bank_get_device(bank); | |
cb8c474e | 1079 | |
3faf89f2 | 1080 | guard(mutex)(&dev->lock); |
cb8c474e | 1081 | |
3faf89f2 | 1082 | return sprintf(page, "%u\n", bank->num_lines); |
cb8c474e BG |
1083 | } |
1084 | ||
1085 | static ssize_t | |
1086 | gpio_sim_bank_config_num_lines_store(struct config_item *item, | |
1087 | const char *page, size_t count) | |
1088 | { | |
1089 | struct gpio_sim_bank *bank = to_gpio_sim_bank(item); | |
1090 | struct gpio_sim_device *dev = gpio_sim_bank_get_device(bank); | |
1091 | unsigned int num_lines; | |
1092 | int ret; | |
1093 | ||
1094 | ret = kstrtouint(page, 0, &num_lines); | |
1095 | if (ret) | |
1096 | return ret; | |
1097 | ||
1098 | if (num_lines == 0) | |
1099 | return -EINVAL; | |
1100 | ||
3faf89f2 | 1101 | guard(mutex)(&dev->lock); |
cb8c474e | 1102 | |
3faf89f2 | 1103 | if (gpio_sim_device_is_live_unlocked(dev)) |
cb8c474e | 1104 | return -EBUSY; |
cb8c474e BG |
1105 | |
1106 | bank->num_lines = num_lines; | |
1107 | ||
cb8c474e BG |
1108 | return count; |
1109 | } | |
1110 | ||
1111 | CONFIGFS_ATTR(gpio_sim_bank_config_, num_lines); | |
1112 | ||
1113 | static struct configfs_attribute *gpio_sim_bank_config_attrs[] = { | |
1114 | &gpio_sim_bank_config_attr_chip_name, | |
1115 | &gpio_sim_bank_config_attr_label, | |
1116 | &gpio_sim_bank_config_attr_num_lines, | |
1117 | NULL | |
1118 | }; | |
1119 | ||
1120 | static ssize_t | |
1121 | gpio_sim_line_config_name_show(struct config_item *item, char *page) | |
1122 | { | |
1123 | struct gpio_sim_line *line = to_gpio_sim_line(item); | |
1124 | struct gpio_sim_device *dev = gpio_sim_line_get_device(line); | |
cb8c474e | 1125 | |
3faf89f2 | 1126 | guard(mutex)(&dev->lock); |
cb8c474e | 1127 | |
3faf89f2 | 1128 | return sprintf(page, "%s\n", line->name ?: ""); |
cb8c474e BG |
1129 | } |
1130 | ||
1131 | static ssize_t gpio_sim_line_config_name_store(struct config_item *item, | |
1132 | const char *page, size_t count) | |
1133 | { | |
1134 | struct gpio_sim_line *line = to_gpio_sim_line(item); | |
1135 | struct gpio_sim_device *dev = gpio_sim_line_get_device(line); | |
1136 | char *trimmed; | |
1137 | ||
3faf89f2 | 1138 | guard(mutex)(&dev->lock); |
cb8c474e | 1139 | |
3faf89f2 | 1140 | if (gpio_sim_device_is_live_unlocked(dev)) |
cb8c474e | 1141 | return -EBUSY; |
cb8c474e BG |
1142 | |
1143 | trimmed = gpio_sim_strdup_trimmed(page, count); | |
3faf89f2 | 1144 | if (!trimmed) |
cb8c474e | 1145 | return -ENOMEM; |
cb8c474e BG |
1146 | |
1147 | kfree(line->name); | |
1148 | line->name = trimmed; | |
1149 | ||
cb8c474e BG |
1150 | return count; |
1151 | } | |
1152 | ||
1153 | CONFIGFS_ATTR(gpio_sim_line_config_, name); | |
1154 | ||
1155 | static struct configfs_attribute *gpio_sim_line_config_attrs[] = { | |
1156 | &gpio_sim_line_config_attr_name, | |
1157 | NULL | |
1158 | }; | |
1159 | ||
1160 | static ssize_t gpio_sim_hog_config_name_show(struct config_item *item, | |
1161 | char *page) | |
1162 | { | |
1163 | struct gpio_sim_hog *hog = to_gpio_sim_hog(item); | |
1164 | struct gpio_sim_device *dev = gpio_sim_hog_get_device(hog); | |
cb8c474e | 1165 | |
3faf89f2 | 1166 | guard(mutex)(&dev->lock); |
cb8c474e | 1167 | |
3faf89f2 | 1168 | return sprintf(page, "%s\n", hog->name ?: ""); |
cb8c474e BG |
1169 | } |
1170 | ||
1171 | static ssize_t gpio_sim_hog_config_name_store(struct config_item *item, | |
1172 | const char *page, size_t count) | |
1173 | { | |
1174 | struct gpio_sim_hog *hog = to_gpio_sim_hog(item); | |
1175 | struct gpio_sim_device *dev = gpio_sim_hog_get_device(hog); | |
1176 | char *trimmed; | |
1177 | ||
3faf89f2 | 1178 | guard(mutex)(&dev->lock); |
cb8c474e | 1179 | |
3faf89f2 | 1180 | if (gpio_sim_device_is_live_unlocked(dev)) |
cb8c474e | 1181 | return -EBUSY; |
cb8c474e BG |
1182 | |
1183 | trimmed = gpio_sim_strdup_trimmed(page, count); | |
3faf89f2 | 1184 | if (!trimmed) |
cb8c474e | 1185 | return -ENOMEM; |
cb8c474e BG |
1186 | |
1187 | kfree(hog->name); | |
1188 | hog->name = trimmed; | |
1189 | ||
cb8c474e BG |
1190 | return count; |
1191 | } | |
1192 | ||
1193 | CONFIGFS_ATTR(gpio_sim_hog_config_, name); | |
1194 | ||
1195 | static ssize_t gpio_sim_hog_config_direction_show(struct config_item *item, | |
1196 | char *page) | |
1197 | { | |
1198 | struct gpio_sim_hog *hog = to_gpio_sim_hog(item); | |
1199 | struct gpio_sim_device *dev = gpio_sim_hog_get_device(hog); | |
1200 | char *repr; | |
1201 | int dir; | |
1202 | ||
3faf89f2 BG |
1203 | scoped_guard(mutex, &dev->lock) |
1204 | dir = hog->dir; | |
cb8c474e BG |
1205 | |
1206 | switch (dir) { | |
1207 | case GPIOD_IN: | |
1208 | repr = "input"; | |
1209 | break; | |
1210 | case GPIOD_OUT_HIGH: | |
1211 | repr = "output-high"; | |
1212 | break; | |
1213 | case GPIOD_OUT_LOW: | |
1214 | repr = "output-low"; | |
1215 | break; | |
1216 | default: | |
1217 | /* This would be a programmer bug. */ | |
1218 | WARN(1, "Unexpected hog direction value: %d", dir); | |
1219 | return -EINVAL; | |
1220 | } | |
1221 | ||
1222 | return sprintf(page, "%s\n", repr); | |
1223 | } | |
1224 | ||
1225 | static ssize_t | |
1226 | gpio_sim_hog_config_direction_store(struct config_item *item, | |
1227 | const char *page, size_t count) | |
1228 | { | |
1229 | struct gpio_sim_hog *hog = to_gpio_sim_hog(item); | |
1230 | struct gpio_sim_device *dev = gpio_sim_hog_get_device(hog); | |
cb8c474e BG |
1231 | int dir; |
1232 | ||
3faf89f2 | 1233 | guard(mutex)(&dev->lock); |
cb8c474e | 1234 | |
3faf89f2 | 1235 | if (gpio_sim_device_is_live_unlocked(dev)) |
cb8c474e | 1236 | return -EBUSY; |
cb8c474e | 1237 | |
39df52dd | 1238 | if (sysfs_streq(page, "input")) |
cb8c474e | 1239 | dir = GPIOD_IN; |
39df52dd | 1240 | else if (sysfs_streq(page, "output-high")) |
cb8c474e | 1241 | dir = GPIOD_OUT_HIGH; |
39df52dd | 1242 | else if (sysfs_streq(page, "output-low")) |
cb8c474e BG |
1243 | dir = GPIOD_OUT_LOW; |
1244 | else | |
3faf89f2 | 1245 | return -EINVAL; |
cb8c474e BG |
1246 | |
1247 | hog->dir = dir; | |
1248 | ||
cb8c474e BG |
1249 | return count; |
1250 | } | |
1251 | ||
1252 | CONFIGFS_ATTR(gpio_sim_hog_config_, direction); | |
1253 | ||
1254 | static struct configfs_attribute *gpio_sim_hog_config_attrs[] = { | |
1255 | &gpio_sim_hog_config_attr_name, | |
1256 | &gpio_sim_hog_config_attr_direction, | |
1257 | NULL | |
1258 | }; | |
1259 | ||
1260 | static void gpio_sim_hog_config_item_release(struct config_item *item) | |
1261 | { | |
1262 | struct gpio_sim_hog *hog = to_gpio_sim_hog(item); | |
1263 | struct gpio_sim_line *line = hog->parent; | |
1264 | struct gpio_sim_device *dev = gpio_sim_hog_get_device(hog); | |
1265 | ||
3faf89f2 BG |
1266 | scoped_guard(mutex, &dev->lock) |
1267 | line->hog = NULL; | |
cb8c474e BG |
1268 | |
1269 | kfree(hog->name); | |
1270 | kfree(hog); | |
1271 | } | |
1272 | ||
a9a5b720 | 1273 | static struct configfs_item_operations gpio_sim_hog_config_item_ops = { |
cb8c474e BG |
1274 | .release = gpio_sim_hog_config_item_release, |
1275 | }; | |
1276 | ||
1277 | static const struct config_item_type gpio_sim_hog_config_type = { | |
1278 | .ct_item_ops = &gpio_sim_hog_config_item_ops, | |
1279 | .ct_attrs = gpio_sim_hog_config_attrs, | |
1280 | .ct_owner = THIS_MODULE, | |
1281 | }; | |
1282 | ||
1283 | static struct config_item * | |
1284 | gpio_sim_line_config_make_hog_item(struct config_group *group, const char *name) | |
1285 | { | |
1286 | struct gpio_sim_line *line = to_gpio_sim_line(&group->cg_item); | |
1287 | struct gpio_sim_device *dev = gpio_sim_line_get_device(line); | |
1288 | struct gpio_sim_hog *hog; | |
1289 | ||
1290 | if (strcmp(name, "hog") != 0) | |
1291 | return ERR_PTR(-EINVAL); | |
1292 | ||
3faf89f2 | 1293 | guard(mutex)(&dev->lock); |
cb8c474e BG |
1294 | |
1295 | hog = kzalloc(sizeof(*hog), GFP_KERNEL); | |
3faf89f2 | 1296 | if (!hog) |
cb8c474e | 1297 | return ERR_PTR(-ENOMEM); |
cb8c474e BG |
1298 | |
1299 | config_item_init_type_name(&hog->item, name, | |
1300 | &gpio_sim_hog_config_type); | |
1301 | ||
1302 | hog->dir = GPIOD_IN; | |
1303 | hog->name = NULL; | |
1304 | hog->parent = line; | |
1305 | line->hog = hog; | |
1306 | ||
cb8c474e BG |
1307 | return &hog->item; |
1308 | } | |
1309 | ||
1310 | static void gpio_sim_line_config_group_release(struct config_item *item) | |
1311 | { | |
1312 | struct gpio_sim_line *line = to_gpio_sim_line(item); | |
1313 | struct gpio_sim_device *dev = gpio_sim_line_get_device(line); | |
1314 | ||
3faf89f2 BG |
1315 | scoped_guard(mutex, &dev->lock) |
1316 | list_del(&line->siblings); | |
cb8c474e BG |
1317 | |
1318 | kfree(line->name); | |
1319 | kfree(line); | |
1320 | } | |
1321 | ||
1322 | static struct configfs_item_operations gpio_sim_line_config_item_ops = { | |
1323 | .release = gpio_sim_line_config_group_release, | |
1324 | }; | |
1325 | ||
1326 | static struct configfs_group_operations gpio_sim_line_config_group_ops = { | |
1327 | .make_item = gpio_sim_line_config_make_hog_item, | |
1328 | }; | |
1329 | ||
1330 | static const struct config_item_type gpio_sim_line_config_type = { | |
1331 | .ct_item_ops = &gpio_sim_line_config_item_ops, | |
1332 | .ct_group_ops = &gpio_sim_line_config_group_ops, | |
1333 | .ct_attrs = gpio_sim_line_config_attrs, | |
1334 | .ct_owner = THIS_MODULE, | |
1335 | }; | |
1336 | ||
1337 | static struct config_group * | |
1338 | gpio_sim_bank_config_make_line_group(struct config_group *group, | |
1339 | const char *name) | |
1340 | { | |
1341 | struct gpio_sim_bank *bank = to_gpio_sim_bank(&group->cg_item); | |
1342 | struct gpio_sim_device *dev = gpio_sim_bank_get_device(bank); | |
1343 | struct gpio_sim_line *line; | |
1344 | unsigned int offset; | |
1345 | int ret, nchar; | |
1346 | ||
1347 | ret = sscanf(name, "line%u%n", &offset, &nchar); | |
1348 | if (ret != 1 || nchar != strlen(name)) | |
1349 | return ERR_PTR(-EINVAL); | |
1350 | ||
3faf89f2 | 1351 | guard(mutex)(&dev->lock); |
cb8c474e | 1352 | |
3faf89f2 | 1353 | if (gpio_sim_device_is_live_unlocked(dev)) |
cb8c474e | 1354 | return ERR_PTR(-EBUSY); |
cb8c474e BG |
1355 | |
1356 | line = kzalloc(sizeof(*line), GFP_KERNEL); | |
3faf89f2 | 1357 | if (!line) |
cb8c474e | 1358 | return ERR_PTR(-ENOMEM); |
cb8c474e BG |
1359 | |
1360 | config_group_init_type_name(&line->group, name, | |
1361 | &gpio_sim_line_config_type); | |
1362 | ||
1363 | line->parent = bank; | |
1364 | line->offset = offset; | |
1365 | list_add_tail(&line->siblings, &bank->line_list); | |
1366 | ||
cb8c474e BG |
1367 | return &line->group; |
1368 | } | |
1369 | ||
1370 | static void gpio_sim_bank_config_group_release(struct config_item *item) | |
1371 | { | |
1372 | struct gpio_sim_bank *bank = to_gpio_sim_bank(item); | |
1373 | struct gpio_sim_device *dev = gpio_sim_bank_get_device(bank); | |
1374 | ||
3faf89f2 BG |
1375 | scoped_guard(mutex, &dev->lock) |
1376 | list_del(&bank->siblings); | |
cb8c474e BG |
1377 | |
1378 | kfree(bank->label); | |
1379 | kfree(bank); | |
1380 | } | |
1381 | ||
1382 | static struct configfs_item_operations gpio_sim_bank_config_item_ops = { | |
1383 | .release = gpio_sim_bank_config_group_release, | |
1384 | }; | |
1385 | ||
1386 | static struct configfs_group_operations gpio_sim_bank_config_group_ops = { | |
1387 | .make_group = gpio_sim_bank_config_make_line_group, | |
1388 | }; | |
1389 | ||
1390 | static const struct config_item_type gpio_sim_bank_config_group_type = { | |
1391 | .ct_item_ops = &gpio_sim_bank_config_item_ops, | |
1392 | .ct_group_ops = &gpio_sim_bank_config_group_ops, | |
1393 | .ct_attrs = gpio_sim_bank_config_attrs, | |
1394 | .ct_owner = THIS_MODULE, | |
1395 | }; | |
1396 | ||
1397 | static struct config_group * | |
1398 | gpio_sim_device_config_make_bank_group(struct config_group *group, | |
1399 | const char *name) | |
1400 | { | |
1401 | struct gpio_sim_device *dev = to_gpio_sim_device(&group->cg_item); | |
1402 | struct gpio_sim_bank *bank; | |
1403 | ||
3faf89f2 | 1404 | guard(mutex)(&dev->lock); |
cb8c474e | 1405 | |
3faf89f2 | 1406 | if (gpio_sim_device_is_live_unlocked(dev)) |
cb8c474e | 1407 | return ERR_PTR(-EBUSY); |
cb8c474e BG |
1408 | |
1409 | bank = kzalloc(sizeof(*bank), GFP_KERNEL); | |
3faf89f2 | 1410 | if (!bank) |
cb8c474e | 1411 | return ERR_PTR(-ENOMEM); |
cb8c474e BG |
1412 | |
1413 | config_group_init_type_name(&bank->group, name, | |
1414 | &gpio_sim_bank_config_group_type); | |
1415 | bank->num_lines = 1; | |
1416 | bank->parent = dev; | |
1417 | INIT_LIST_HEAD(&bank->line_list); | |
1418 | list_add_tail(&bank->siblings, &dev->bank_list); | |
1419 | ||
cb8c474e BG |
1420 | return &bank->group; |
1421 | } | |
1422 | ||
1423 | static void gpio_sim_device_config_group_release(struct config_item *item) | |
1424 | { | |
1425 | struct gpio_sim_device *dev = to_gpio_sim_device(item); | |
1426 | ||
3faf89f2 BG |
1427 | scoped_guard(mutex, &dev->lock) { |
1428 | if (gpio_sim_device_is_live_unlocked(dev)) | |
1429 | gpio_sim_device_deactivate_unlocked(dev); | |
1430 | } | |
cb8c474e BG |
1431 | |
1432 | mutex_destroy(&dev->lock); | |
1433 | ida_free(&gpio_sim_ida, dev->id); | |
1434 | kfree(dev); | |
1435 | } | |
1436 | ||
1437 | static struct configfs_item_operations gpio_sim_device_config_item_ops = { | |
1438 | .release = gpio_sim_device_config_group_release, | |
1439 | }; | |
1440 | ||
1441 | static struct configfs_group_operations gpio_sim_device_config_group_ops = { | |
1442 | .make_group = gpio_sim_device_config_make_bank_group, | |
1443 | }; | |
1444 | ||
1445 | static const struct config_item_type gpio_sim_device_config_group_type = { | |
1446 | .ct_item_ops = &gpio_sim_device_config_item_ops, | |
1447 | .ct_group_ops = &gpio_sim_device_config_group_ops, | |
1448 | .ct_attrs = gpio_sim_device_config_attrs, | |
1449 | .ct_owner = THIS_MODULE, | |
1450 | }; | |
1451 | ||
1452 | static struct config_group * | |
1453 | gpio_sim_config_make_device_group(struct config_group *group, const char *name) | |
1454 | { | |
3faf89f2 | 1455 | struct gpio_sim_device *dev __free(kfree) = NULL; |
cb8c474e BG |
1456 | int id; |
1457 | ||
1458 | dev = kzalloc(sizeof(*dev), GFP_KERNEL); | |
1459 | if (!dev) | |
1460 | return ERR_PTR(-ENOMEM); | |
1461 | ||
1462 | id = ida_alloc(&gpio_sim_ida, GFP_KERNEL); | |
3faf89f2 | 1463 | if (id < 0) |
cb8c474e | 1464 | return ERR_PTR(id); |
cb8c474e BG |
1465 | |
1466 | config_group_init_type_name(&dev->group, name, | |
1467 | &gpio_sim_device_config_group_type); | |
1468 | dev->id = id; | |
1469 | mutex_init(&dev->lock); | |
1470 | INIT_LIST_HEAD(&dev->bank_list); | |
1471 | ||
1472 | dev->bus_notifier.notifier_call = gpio_sim_bus_notifier_call; | |
1473 | init_completion(&dev->probe_completion); | |
1474 | ||
3faf89f2 | 1475 | return &no_free_ptr(dev)->group; |
cb8c474e BG |
1476 | } |
1477 | ||
1478 | static struct configfs_group_operations gpio_sim_config_group_ops = { | |
1479 | .make_group = gpio_sim_config_make_device_group, | |
1480 | }; | |
1481 | ||
1482 | static const struct config_item_type gpio_sim_config_type = { | |
1483 | .ct_group_ops = &gpio_sim_config_group_ops, | |
1484 | .ct_owner = THIS_MODULE, | |
1485 | }; | |
1486 | ||
1487 | static struct configfs_subsystem gpio_sim_config_subsys = { | |
1488 | .su_group = { | |
1489 | .cg_item = { | |
1490 | .ci_namebuf = "gpio-sim", | |
1491 | .ci_type = &gpio_sim_config_type, | |
1492 | }, | |
1493 | }, | |
1494 | }; | |
1495 | ||
1496 | static int __init gpio_sim_init(void) | |
1497 | { | |
1498 | int ret; | |
1499 | ||
1500 | ret = platform_driver_register(&gpio_sim_driver); | |
1501 | if (ret) { | |
1502 | pr_err("Error %d while registering the platform driver\n", ret); | |
1503 | return ret; | |
1504 | } | |
1505 | ||
1506 | config_group_init(&gpio_sim_config_subsys.su_group); | |
1507 | mutex_init(&gpio_sim_config_subsys.su_mutex); | |
1508 | ret = configfs_register_subsystem(&gpio_sim_config_subsys); | |
1509 | if (ret) { | |
1510 | pr_err("Error %d while registering the configfs subsystem %s\n", | |
1511 | ret, gpio_sim_config_subsys.su_group.cg_item.ci_namebuf); | |
1512 | mutex_destroy(&gpio_sim_config_subsys.su_mutex); | |
1513 | platform_driver_unregister(&gpio_sim_driver); | |
1514 | return ret; | |
1515 | } | |
1516 | ||
1517 | return 0; | |
1518 | } | |
1519 | module_init(gpio_sim_init); | |
1520 | ||
1521 | static void __exit gpio_sim_exit(void) | |
1522 | { | |
1523 | configfs_unregister_subsystem(&gpio_sim_config_subsys); | |
1524 | mutex_destroy(&gpio_sim_config_subsys.su_mutex); | |
1525 | platform_driver_unregister(&gpio_sim_driver); | |
1526 | } | |
1527 | module_exit(gpio_sim_exit); | |
1528 | ||
1529 | MODULE_AUTHOR("Bartosz Golaszewski <brgl@bgdev.pl"); | |
1530 | MODULE_DESCRIPTION("GPIO Simulator Module"); | |
1531 | MODULE_LICENSE("GPL"); |