aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorArnd Bergmann <arnd@arndb.de>2016-11-30 16:48:06 +0100
committerArnd Bergmann <arnd@arndb.de>2016-11-30 16:48:06 +0100
commitae1d0a466ef76e1ff5058e2b316c14f46af83675 (patch)
tree063eb63ec7231970cd6fd87adce612d0507c99e5
parentd319fc6dc1bfb616d4d90e04967f9ec8f08d52d8 (diff)
parentda65a1589dacc7ec44ea0557a14d70a39d991f32 (diff)
Merge tag 'soc-device-match-tag1' of git://git.kernel.org/pub/scm/linux/kernel/git/geert/renesas-drivers into next/drivers
Pull "soc_device_match() interface for matching against soc_bus attributes" from Geert Uytterhoeven: This provides core infrastructure as a dependency for several users (Freescale/NXP, Samsung, Renesas). Its core parts have been acked by Greg, and the fixes by Arnd and/or Greg (the last fix only received an informal ack, that's why I hadn't added the ack). This has already been pulled by Ulf, and is present in mmc/next, as a dependency for a Freescale/NXP driver update. * tag 'soc-device-match-tag1' of git://git.kernel.org/pub/scm/linux/kernel/git/geert/renesas-drivers: base: soc: Provide a dummy implementation of soc_device_match() base: soc: Check for NULL SoC device attributes base: soc: Introduce soc_device_match() interface base: soc: Early register bus when needed
-rw-r--r--drivers/base/Kconfig1
-rw-r--r--drivers/base/soc.c79
-rw-r--r--include/linux/sys_soc.h9
3 files changed, 89 insertions, 0 deletions
diff --git a/drivers/base/Kconfig b/drivers/base/Kconfig
index fdf44cac08e6..991b21e1f89b 100644
--- a/drivers/base/Kconfig
+++ b/drivers/base/Kconfig
@@ -235,6 +235,7 @@ config GENERIC_CPU_AUTOPROBE
config SOC_BUS
bool
+ select GLOB
source "drivers/base/regmap/Kconfig"
diff --git a/drivers/base/soc.c b/drivers/base/soc.c
index b63f23e6ad61..dc26e5949a32 100644
--- a/drivers/base/soc.c
+++ b/drivers/base/soc.c
@@ -13,6 +13,7 @@
#include <linux/spinlock.h>
#include <linux/sys_soc.h>
#include <linux/err.h>
+#include <linux/glob.h>
static DEFINE_IDA(soc_ida);
@@ -113,6 +114,12 @@ struct soc_device *soc_device_register(struct soc_device_attribute *soc_dev_attr
struct soc_device *soc_dev;
int ret;
+ if (!soc_bus_type.p) {
+ ret = bus_register(&soc_bus_type);
+ if (ret)
+ goto out1;
+ }
+
soc_dev = kzalloc(sizeof(*soc_dev), GFP_KERNEL);
if (!soc_dev) {
ret = -ENOMEM;
@@ -156,6 +163,78 @@ void soc_device_unregister(struct soc_device *soc_dev)
static int __init soc_bus_register(void)
{
+ if (soc_bus_type.p)
+ return 0;
+
return bus_register(&soc_bus_type);
}
core_initcall(soc_bus_register);
+
+static int soc_device_match_one(struct device *dev, void *arg)
+{
+ struct soc_device *soc_dev = container_of(dev, struct soc_device, dev);
+ const struct soc_device_attribute *match = arg;
+
+ if (match->machine &&
+ (!soc_dev->attr->machine ||
+ !glob_match(match->machine, soc_dev->attr->machine)))
+ return 0;
+
+ if (match->family &&
+ (!soc_dev->attr->family ||
+ !glob_match(match->family, soc_dev->attr->family)))
+ return 0;
+
+ if (match->revision &&
+ (!soc_dev->attr->revision ||
+ !glob_match(match->revision, soc_dev->attr->revision)))
+ return 0;
+
+ if (match->soc_id &&
+ (!soc_dev->attr->soc_id ||
+ !glob_match(match->soc_id, soc_dev->attr->soc_id)))
+ return 0;
+
+ return 1;
+}
+
+/*
+ * soc_device_match - identify the SoC in the machine
+ * @matches: zero-terminated array of possible matches
+ *
+ * returns the first matching entry of the argument array, or NULL
+ * if none of them match.
+ *
+ * This function is meant as a helper in place of of_match_node()
+ * in cases where either no device tree is available or the information
+ * in a device node is insufficient to identify a particular variant
+ * by its compatible strings or other properties. For new devices,
+ * the DT binding should always provide unique compatible strings
+ * that allow the use of of_match_node() instead.
+ *
+ * The calling function can use the .data entry of the
+ * soc_device_attribute to pass a structure or function pointer for
+ * each entry.
+ */
+const struct soc_device_attribute *soc_device_match(
+ const struct soc_device_attribute *matches)
+{
+ int ret = 0;
+
+ if (!matches)
+ return NULL;
+
+ while (!ret) {
+ if (!(matches->machine || matches->family ||
+ matches->revision || matches->soc_id))
+ break;
+ ret = bus_for_each_dev(&soc_bus_type, NULL, (void *)matches,
+ soc_device_match_one);
+ if (!ret)
+ matches++;
+ else
+ return matches;
+ }
+ return NULL;
+}
+EXPORT_SYMBOL_GPL(soc_device_match);
diff --git a/include/linux/sys_soc.h b/include/linux/sys_soc.h
index 2739ccb69571..bed223b70217 100644
--- a/include/linux/sys_soc.h
+++ b/include/linux/sys_soc.h
@@ -13,6 +13,7 @@ struct soc_device_attribute {
const char *family;
const char *revision;
const char *soc_id;
+ const void *data;
};
/**
@@ -34,4 +35,12 @@ void soc_device_unregister(struct soc_device *soc_dev);
*/
struct device *soc_device_to_device(struct soc_device *soc);
+#ifdef CONFIG_SOC_BUS
+const struct soc_device_attribute *soc_device_match(
+ const struct soc_device_attribute *matches);
+#else
+static inline const struct soc_device_attribute *soc_device_match(
+ const struct soc_device_attribute *matches) { return NULL; }
+#endif
+
#endif /* __SOC_BUS_H */