[RFC 1/5] misc: introduce notify-device driver

From: Matthias Schiffer
Date: Wed Oct 26 2022 - 09:16:50 EST


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 = &notify_device_class;
+ dev->parent = parent;
+ dev->of_node = node;
+ err = device_register(dev);
+ if (err) {
+ put_device(dev);
+ return ERR_PTR(err);
+ }
+
+ dev->driver = &notify_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(&notify_device_class, node);
+}
+EXPORT_SYMBOL_GPL(notify_device_find_by_of_node);
+
+static int __init notify_device_init(void)
+{
+ int err;
+
+ err = class_register(&notify_device_class);
+ if (err)
+ return err;
+
+ err = platform_driver_register(&notify_device_driver);
+ if (err) {
+ class_unregister(&notify_device_class);
+ return err;
+ }
+
+ return 0;
+}
+
+static void __exit notify_device_exit(void)
+{
+ platform_driver_unregister(&notify_device_driver);
+ class_unregister(&notify_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