|
@@ -2317,6 +2317,86 @@ EXPORT_SYMBOL_GPL(spi_write_then_read);
|
|
|
|
|
|
/*-------------------------------------------------------------------------*/
|
|
|
|
|
|
+#if IS_ENABLED(CONFIG_OF_DYNAMIC)
|
|
|
+static int __spi_of_device_match(struct device *dev, void *data)
|
|
|
+{
|
|
|
+ return dev->of_node == data;
|
|
|
+}
|
|
|
+
|
|
|
+/* must call put_device() when done with returned spi_device device */
|
|
|
+static struct spi_device *of_find_spi_device_by_node(struct device_node *node)
|
|
|
+{
|
|
|
+ struct device *dev = bus_find_device(&spi_bus_type, NULL, node,
|
|
|
+ __spi_of_device_match);
|
|
|
+ return dev ? to_spi_device(dev) : NULL;
|
|
|
+}
|
|
|
+
|
|
|
+static int __spi_of_master_match(struct device *dev, const void *data)
|
|
|
+{
|
|
|
+ return dev->of_node == data;
|
|
|
+}
|
|
|
+
|
|
|
+/* the spi masters are not using spi_bus, so we find it with another way */
|
|
|
+static struct spi_master *of_find_spi_master_by_node(struct device_node *node)
|
|
|
+{
|
|
|
+ struct device *dev;
|
|
|
+
|
|
|
+ dev = class_find_device(&spi_master_class, NULL, node,
|
|
|
+ __spi_of_master_match);
|
|
|
+ if (!dev)
|
|
|
+ return NULL;
|
|
|
+
|
|
|
+ /* reference got in class_find_device */
|
|
|
+ return container_of(dev, struct spi_master, dev);
|
|
|
+}
|
|
|
+
|
|
|
+static int of_spi_notify(struct notifier_block *nb, unsigned long action,
|
|
|
+ void *arg)
|
|
|
+{
|
|
|
+ struct of_reconfig_data *rd = arg;
|
|
|
+ struct spi_master *master;
|
|
|
+ struct spi_device *spi;
|
|
|
+
|
|
|
+ switch (of_reconfig_get_state_change(action, arg)) {
|
|
|
+ case OF_RECONFIG_CHANGE_ADD:
|
|
|
+ master = of_find_spi_master_by_node(rd->dn->parent);
|
|
|
+ if (master == NULL)
|
|
|
+ return NOTIFY_OK; /* not for us */
|
|
|
+
|
|
|
+ spi = of_register_spi_device(master, rd->dn);
|
|
|
+ put_device(&master->dev);
|
|
|
+
|
|
|
+ if (IS_ERR(spi)) {
|
|
|
+ pr_err("%s: failed to create for '%s'\n",
|
|
|
+ __func__, rd->dn->full_name);
|
|
|
+ return notifier_from_errno(PTR_ERR(spi));
|
|
|
+ }
|
|
|
+ break;
|
|
|
+
|
|
|
+ case OF_RECONFIG_CHANGE_REMOVE:
|
|
|
+ /* find our device by node */
|
|
|
+ spi = of_find_spi_device_by_node(rd->dn);
|
|
|
+ if (spi == NULL)
|
|
|
+ return NOTIFY_OK; /* no? not meant for us */
|
|
|
+
|
|
|
+ /* unregister takes one ref away */
|
|
|
+ spi_unregister_device(spi);
|
|
|
+
|
|
|
+ /* and put the reference of the find */
|
|
|
+ put_device(&spi->dev);
|
|
|
+ break;
|
|
|
+ }
|
|
|
+
|
|
|
+ return NOTIFY_OK;
|
|
|
+}
|
|
|
+
|
|
|
+static struct notifier_block spi_of_notifier = {
|
|
|
+ .notifier_call = of_spi_notify,
|
|
|
+};
|
|
|
+#else /* IS_ENABLED(CONFIG_OF_DYNAMIC) */
|
|
|
+extern struct notifier_block spi_of_notifier;
|
|
|
+#endif /* IS_ENABLED(CONFIG_OF_DYNAMIC) */
|
|
|
+
|
|
|
static int __init spi_init(void)
|
|
|
{
|
|
|
int status;
|
|
@@ -2334,6 +2414,10 @@ static int __init spi_init(void)
|
|
|
status = class_register(&spi_master_class);
|
|
|
if (status < 0)
|
|
|
goto err2;
|
|
|
+
|
|
|
+ if (IS_ENABLED(CONFIG_OF))
|
|
|
+ WARN_ON(of_reconfig_notifier_register(&spi_of_notifier));
|
|
|
+
|
|
|
return 0;
|
|
|
|
|
|
err2:
|