diff options
Diffstat (limited to 'drivers/gpio/gpio-sim.c')
-rw-r--r-- | drivers/gpio/gpio-sim.c | 201 |
1 files changed, 94 insertions, 107 deletions
diff --git a/drivers/gpio/gpio-sim.c b/drivers/gpio/gpio-sim.c index 2ed5cbe7c8a8..f638219a7c4f 100644 --- a/drivers/gpio/gpio-sim.c +++ b/drivers/gpio/gpio-sim.c @@ -7,9 +7,9 @@ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt +#include <linux/array_size.h> #include <linux/bitmap.h> #include <linux/cleanup.h> -#include <linux/completion.h> #include <linux/configfs.h> #include <linux/device.h> #include <linux/err.h> @@ -20,7 +20,6 @@ #include <linux/interrupt.h> #include <linux/irq.h> #include <linux/irq_sim.h> -#include <linux/kernel.h> #include <linux/list.h> #include <linux/lockdep.h> #include <linux/minmax.h> @@ -37,6 +36,8 @@ #include <linux/sysfs.h> #include <linux/types.h> +#include "dev-sync-probe.h" + #define GPIO_SIM_NGPIO_MAX 1024 #define GPIO_SIM_PROP_MAX 4 /* Max 3 properties + sentinel. */ #define GPIO_SIM_NUM_ATTRS 3 /* value, pull and sentinel */ @@ -119,12 +120,14 @@ static int gpio_sim_get(struct gpio_chip *gc, unsigned int offset) return !!test_bit(offset, chip->value_map); } -static void gpio_sim_set(struct gpio_chip *gc, unsigned int offset, int value) +static int gpio_sim_set(struct gpio_chip *gc, unsigned int offset, int value) { struct gpio_sim_chip *chip = gpiochip_get_data(gc); scoped_guard(mutex, &chip->lock) __assign_bit(offset, chip->value_map, value); + + return 0; } static int gpio_sim_get_multiple(struct gpio_chip *gc, @@ -138,14 +141,16 @@ static int gpio_sim_get_multiple(struct gpio_chip *gc, return 0; } -static void gpio_sim_set_multiple(struct gpio_chip *gc, - unsigned long *mask, unsigned long *bits) +static int gpio_sim_set_multiple(struct gpio_chip *gc, + unsigned long *mask, unsigned long *bits) { struct gpio_sim_chip *chip = gpiochip_get_data(gc); scoped_guard(mutex, &chip->lock) bitmap_replace(chip->value_map, chip->value_map, bits, mask, gc->ngpio); + + return 0; } static int gpio_sim_direction_output(struct gpio_chip *gc, @@ -227,6 +232,27 @@ static void gpio_sim_free(struct gpio_chip *gc, unsigned int offset) } } +static int gpio_sim_irq_requested(struct irq_domain *domain, + irq_hw_number_t hwirq, void *data) +{ + struct gpio_sim_chip *chip = data; + + return gpiochip_lock_as_irq(&chip->gc, hwirq); +} + +static void gpio_sim_irq_released(struct irq_domain *domain, + irq_hw_number_t hwirq, void *data) +{ + struct gpio_sim_chip *chip = data; + + gpiochip_unlock_as_irq(&chip->gc, hwirq); +} + +static const struct irq_sim_ops gpio_sim_irq_sim_ops = { + .irq_sim_irq_requested = gpio_sim_irq_requested, + .irq_sim_irq_released = gpio_sim_irq_released, +}; + static void gpio_sim_dbg_show(struct seq_file *seq, struct gpio_chip *gc) { struct gpio_sim_chip *chip = gpiochip_get_data(gc); @@ -308,13 +334,6 @@ static ssize_t gpio_sim_sysfs_pull_store(struct device *dev, return len; } -static void gpio_sim_mutex_destroy(void *data) -{ - struct mutex *lock = data; - - mutex_destroy(lock); -} - static void gpio_sim_put_device(void *data) { struct device *dev = data; @@ -399,11 +418,6 @@ static int gpio_sim_setup_sysfs(struct gpio_sim_chip *chip) return devm_add_action_or_reset(dev, gpio_sim_sysfs_remove, chip); } -static int gpio_sim_dev_match_fwnode(struct device *dev, void *data) -{ - return device_match_fwnode(dev, data); -} - static int gpio_sim_add_bank(struct fwnode_handle *swnode, struct device *dev) { struct gpio_sim_chip *chip; @@ -450,7 +464,9 @@ static int gpio_sim_add_bank(struct fwnode_handle *swnode, struct device *dev) if (!chip->pull_map) return -ENOMEM; - chip->irq_sim = devm_irq_domain_create_sim(dev, swnode, num_lines); + chip->irq_sim = devm_irq_domain_create_sim_full(dev, swnode, num_lines, + &gpio_sim_irq_sim_ops, + chip); if (IS_ERR(chip->irq_sim)) return PTR_ERR(chip->irq_sim); @@ -458,9 +474,7 @@ static int gpio_sim_add_bank(struct fwnode_handle *swnode, struct device *dev) if (ret) return ret; - mutex_init(&chip->lock); - ret = devm_add_action_or_reset(dev, gpio_sim_mutex_destroy, - &chip->lock); + ret = devm_mutex_init(dev, &chip->lock); if (ret) return ret; @@ -472,9 +486,9 @@ static int gpio_sim_add_bank(struct fwnode_handle *swnode, struct device *dev) gc->parent = dev; gc->fwnode = swnode; gc->get = gpio_sim_get; - gc->set = gpio_sim_set; + gc->set_rv = gpio_sim_set; gc->get_multiple = gpio_sim_get_multiple; - gc->set_multiple = gpio_sim_set_multiple; + gc->set_multiple_rv = gpio_sim_set_multiple; gc->direction_output = gpio_sim_direction_output; gc->direction_input = gpio_sim_direction_input; gc->get_direction = gpio_sim_get_direction; @@ -489,7 +503,7 @@ static int gpio_sim_add_bank(struct fwnode_handle *swnode, struct device *dev) if (ret) return ret; - chip->dev = device_find_child(dev, swnode, gpio_sim_dev_match_fwnode); + chip->dev = device_find_child(dev, swnode, device_match_fwnode); if (!chip->dev) return -ENODEV; @@ -506,15 +520,12 @@ static int gpio_sim_add_bank(struct fwnode_handle *swnode, struct device *dev) static int gpio_sim_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; - struct fwnode_handle *swnode; int ret; - device_for_each_child_node(dev, swnode) { + device_for_each_child_node_scoped(dev, swnode) { ret = gpio_sim_add_bank(swnode, dev); - if (ret) { - fwnode_handle_put(swnode); + if (ret) return ret; - } } return 0; @@ -535,14 +546,9 @@ static struct platform_driver gpio_sim_driver = { }; struct gpio_sim_device { + struct dev_sync_probe_data probe_data; struct config_group group; - /* - * If pdev is NULL, the device is 'pending' (waiting for configuration). - * Once the pointer is assigned, the device has been created and the - * item is 'live'. - */ - struct platform_device *pdev; int id; /* @@ -556,46 +562,11 @@ struct gpio_sim_device { */ struct mutex lock; - /* - * This is used to synchronously wait for the driver's probe to complete - * and notify the user-space about any errors. - */ - struct notifier_block bus_notifier; - struct completion probe_completion; - bool driver_bound; - struct gpiod_hog *hogs; struct list_head bank_list; }; -/* This is called with dev->lock already taken. */ -static int gpio_sim_bus_notifier_call(struct notifier_block *nb, - unsigned long action, void *data) -{ - struct gpio_sim_device *simdev = container_of(nb, - struct gpio_sim_device, - bus_notifier); - struct device *dev = data; - char devname[32]; - - snprintf(devname, sizeof(devname), "gpio-sim.%u", simdev->id); - - if (strcmp(dev_name(dev), devname) == 0) { - if (action == BUS_NOTIFY_BOUND_DRIVER) - simdev->driver_bound = true; - else if (action == BUS_NOTIFY_DRIVER_NOT_BOUND) - simdev->driver_bound = false; - else - return NOTIFY_DONE; - - complete(&simdev->probe_completion); - return NOTIFY_OK; - } - - return NOTIFY_DONE; -} - static struct gpio_sim_device *to_gpio_sim_device(struct config_item *item) { struct config_group *group = to_config_group(item); @@ -702,7 +673,7 @@ static bool gpio_sim_device_is_live(struct gpio_sim_device *dev) { lockdep_assert_held(&dev->lock); - return !!dev->pdev; + return !!dev->probe_data.pdev; } static char *gpio_sim_strdup_trimmed(const char *str, size_t count) @@ -724,7 +695,7 @@ static ssize_t gpio_sim_device_config_dev_name_show(struct config_item *item, guard(mutex)(&dev->lock); - pdev = dev->pdev; + pdev = dev->probe_data.pdev; if (pdev) return sprintf(page, "%s\n", dev_name(&pdev->dev)); @@ -933,7 +904,6 @@ static int gpio_sim_device_activate(struct gpio_sim_device *dev) { struct platform_device_info pdevinfo; struct fwnode_handle *swnode; - struct platform_device *pdev; struct gpio_sim_bank *bank; int ret; @@ -975,31 +945,13 @@ static int gpio_sim_device_activate(struct gpio_sim_device *dev) pdevinfo.fwnode = swnode; pdevinfo.id = dev->id; - reinit_completion(&dev->probe_completion); - dev->driver_bound = false; - bus_register_notifier(&platform_bus_type, &dev->bus_notifier); - - pdev = platform_device_register_full(&pdevinfo); - if (IS_ERR(pdev)) { - bus_unregister_notifier(&platform_bus_type, &dev->bus_notifier); - gpio_sim_remove_hogs(dev); - gpio_sim_remove_swnode_recursive(swnode); - return PTR_ERR(pdev); - } - - wait_for_completion(&dev->probe_completion); - bus_unregister_notifier(&platform_bus_type, &dev->bus_notifier); - - if (!dev->driver_bound) { - /* Probe failed, check kernel log. */ - platform_device_unregister(pdev); + ret = dev_sync_probe_register(&dev->probe_data, &pdevinfo); + if (ret) { gpio_sim_remove_hogs(dev); gpio_sim_remove_swnode_recursive(swnode); - return -ENXIO; + return ret; } - dev->pdev = pdev; - return 0; } @@ -1009,11 +961,37 @@ static void gpio_sim_device_deactivate(struct gpio_sim_device *dev) lockdep_assert_held(&dev->lock); - swnode = dev_fwnode(&dev->pdev->dev); - platform_device_unregister(dev->pdev); + swnode = dev_fwnode(&dev->probe_data.pdev->dev); + dev_sync_probe_unregister(&dev->probe_data); gpio_sim_remove_hogs(dev); gpio_sim_remove_swnode_recursive(swnode); - dev->pdev = NULL; +} + +static void +gpio_sim_device_lockup_configfs(struct gpio_sim_device *dev, bool lock) +{ + struct configfs_subsystem *subsys = dev->group.cg_subsys; + struct gpio_sim_bank *bank; + struct gpio_sim_line *line; + struct config_item *item; + + /* + * The device only needs to depend on leaf entries. This is + * sufficient to lock up all the configfs entries that the + * instantiated, alive device depends on. + */ + list_for_each_entry(bank, &dev->bank_list, siblings) { + list_for_each_entry(line, &bank->line_list, siblings) { + item = line->hog ? &line->hog->item + : &line->group.cg_item; + + if (lock) + WARN_ON(configfs_depend_item_unlocked(subsys, + item)); + else + configfs_undepend_item_unlocked(item); + } + } } static ssize_t @@ -1028,14 +1006,24 @@ gpio_sim_device_config_live_store(struct config_item *item, if (ret) return ret; - guard(mutex)(&dev->lock); + if (live) + gpio_sim_device_lockup_configfs(dev, true); - if (live == gpio_sim_device_is_live(dev)) - ret = -EPERM; - else if (live) - ret = gpio_sim_device_activate(dev); - else - gpio_sim_device_deactivate(dev); + scoped_guard(mutex, &dev->lock) { + if (live == gpio_sim_device_is_live(dev)) + ret = -EPERM; + else if (live) + ret = gpio_sim_device_activate(dev); + else + gpio_sim_device_deactivate(dev); + } + + /* + * Undepend is required only if device disablement (live == 0) + * succeeds or if device enablement (live == 1) fails. + */ + if (live == !!ret) + gpio_sim_device_lockup_configfs(dev, false); return ret ?: count; } @@ -1077,7 +1065,7 @@ static ssize_t gpio_sim_bank_config_chip_name_show(struct config_item *item, guard(mutex)(&dev->lock); if (gpio_sim_device_is_live(dev)) - return device_for_each_child(&dev->pdev->dev, &ctx, + return device_for_each_child(&dev->probe_data.pdev->dev, &ctx, gpio_sim_emit_chip_name); return sprintf(page, "none\n"); @@ -1518,8 +1506,7 @@ gpio_sim_config_make_device_group(struct config_group *group, const char *name) mutex_init(&dev->lock); INIT_LIST_HEAD(&dev->bank_list); - dev->bus_notifier.notifier_call = gpio_sim_bus_notifier_call; - init_completion(&dev->probe_completion); + dev_sync_probe_init(&dev->probe_data); return &no_free_ptr(dev)->group; } |