A notify-device is a synchronization facility that allows to query "readiness" across drivers, without creating a direct dependency between the driver modules. The notify-device can also be used to trigger deferred probes. Signed-off-by: Matthias Schiffer <matthias.schiffer@xxxxxxxxxxxxxxx> --- drivers/misc/Kconfig | 4 ++ drivers/misc/Makefile | 1 + drivers/misc/notify-device.c | 109 ++++++++++++++++++++++++++++++++++ include/linux/notify-device.h | 33 ++++++++++ 4 files changed, 147 insertions(+) create mode 100644 drivers/misc/notify-device.c create mode 100644 include/linux/notify-device.h diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig index 358ad56f6524..63559e9f854c 100644 --- a/drivers/misc/Kconfig +++ b/drivers/misc/Kconfig @@ -496,6 +496,10 @@ config VCPU_STALL_DETECTOR If you do not intend to run this kernel as a guest, say N. +config NOTIFY_DEVICE + tristate "Notify device" + depends on OF + source "drivers/misc/c2port/Kconfig" source "drivers/misc/eeprom/Kconfig" source "drivers/misc/cb710/Kconfig" diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile index ac9b3e757ba1..1e8012112b43 100644 --- a/drivers/misc/Makefile +++ b/drivers/misc/Makefile @@ -62,3 +62,4 @@ obj-$(CONFIG_HI6421V600_IRQ) += hi6421v600-irq.o obj-$(CONFIG_OPEN_DICE) += open-dice.o obj-$(CONFIG_GP_PCI1XXXX) += mchp_pci1xxxx/ obj-$(CONFIG_VCPU_STALL_DETECTOR) += vcpu_stall_detector.o +obj-$(CONFIG_NOTIFY_DEVICE) += notify-device.o diff --git a/drivers/misc/notify-device.c b/drivers/misc/notify-device.c new file mode 100644 index 000000000000..42e0980394ea --- /dev/null +++ b/drivers/misc/notify-device.c @@ -0,0 +1,109 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +#include <linux/device/class.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/notify-device.h> +#include <linux/platform_device.h> +#include <linux/slab.h> + +static void notify_device_release(struct device *dev) +{ + of_node_put(dev->of_node); + kfree(dev); +} + +static struct class notify_device_class = { + .name = "notify-device", + .owner = THIS_MODULE, + .dev_release = notify_device_release, +}; + +static struct platform_driver notify_device_driver = { + .driver = { + .name = "notify-device", + }, +}; + +struct device *notify_device_create(struct device *parent, const char *child) +{ + struct device_node *node; + struct device *dev; + int err; + + if (!parent->of_node) + return ERR_PTR(-EINVAL); + + node = of_get_child_by_name(parent->of_node, child); + if (!node) + return NULL; + + dev = kzalloc(sizeof(*dev), GFP_KERNEL); + if (!dev) { + of_node_put(node); + return ERR_PTR(-ENOMEM); + } + + dev_set_name(dev, "%s:%s", dev_name(parent), child); + dev->class = ¬ify_device_class; + dev->parent = parent; + dev->of_node = node; + err = device_register(dev); + if (err) { + put_device(dev); + return ERR_PTR(err); + } + + dev->driver = ¬ify_device_driver.driver; + err = device_bind_driver(dev); + if (err) { + device_unregister(dev); + return ERR_PTR(err); + } + + return dev; +} +EXPORT_SYMBOL_GPL(notify_device_create); + +void notify_device_destroy(struct device *dev) +{ + if (!dev) + return; + + device_release_driver(dev); + device_unregister(dev); +} +EXPORT_SYMBOL_GPL(notify_device_destroy); + +struct device *notify_device_find_by_of_node(struct device_node *node) +{ + return class_find_device_by_of_node(¬ify_device_class, node); +} +EXPORT_SYMBOL_GPL(notify_device_find_by_of_node); + +static int __init notify_device_init(void) +{ + int err; + + err = class_register(¬ify_device_class); + if (err) + return err; + + err = platform_driver_register(¬ify_device_driver); + if (err) { + class_unregister(¬ify_device_class); + return err; + } + + return 0; +} + +static void __exit notify_device_exit(void) +{ + platform_driver_unregister(¬ify_device_driver); + class_unregister(¬ify_device_class); +} + +module_init(notify_device_init); +module_exit(notify_device_exit); +MODULE_LICENSE("GPL"); diff --git a/include/linux/notify-device.h b/include/linux/notify-device.h new file mode 100644 index 000000000000..f8c3e15d3b8f --- /dev/null +++ b/include/linux/notify-device.h @@ -0,0 +1,33 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +#ifndef _LINUX_NOTIFY_DEVICE_H +#define _LINUX_NOTIFY_DEVICE_H +#include <linux/device.h> +#include <linux/of.h> + +#ifdef CONFIG_NOTIFY_DEVICE + +struct device *notify_device_create(struct device *parent, const char *child); +void notify_device_destroy(struct device *dev); +struct device *notify_device_find_by_of_node(struct device_node *node); + +#else + +static inline struct device *notify_device_create(struct device *parent, + const char *child) +{ + return NULL; +} + +static inline void notify_device_destroy(struct device *dev) +{ +} + +static inline struct device *notify_device_find_by_of_node(struct device_node *node) +{ + return NULL; +} + +#endif + +#endif /* _LINUX_NOTIFY_DEVICE_H */ -- 2.25.1