base: soc: Introduce soc_device_match() interface
authorArnd Bergmann <arnd@arndb.de>
Wed, 21 Sep 2016 06:57:19 +0000 (14:57 +0800)
committerGeert Uytterhoeven <geert+renesas@glider.be>
Thu, 10 Nov 2016 09:10:29 +0000 (10:10 +0100)
We keep running into cases where device drivers want to know the exact
version of the a SoC they are currently running on. In the past, this has
usually been done through a vendor specific API that can be called by a
driver, or by directly accessing some kind of version register that is
not part of the device itself but that belongs to a global register area
of the chip.

Common reasons for doing this include:

- A machine is not using devicetree or similar for passing data about
  on-chip devices, but just announces their presence using boot-time
  platform devices, and the machine code itself does not care about the
  revision.

- There is existing firmware or boot loaders with existing DT binaries
  with generic compatible strings that do not identify the particular
  revision of each device, but the driver knows which SoC revisions
  include which part.

- A prerelease version of a chip has some quirks and we are using the same
  version of the bootloader and the DT blob on both the prerelease and the
  final version. An update of the DT binding seems inappropriate because
  that would involve maintaining multiple copies of the dts and/or
  bootloader.

This patch introduces the soc_device_match() interface that is meant to
work like of_match_node() but instead of identifying the version of a
device, it identifies the SoC itself using a vendor-agnostic interface.

Unlike of_match_node(), we do not do an exact string compare but instead
use glob_match() to allow wildcards in strings.

Signed-off-by: Arnd Bergmann <arnd@arndb.de>
Signed-off-by: Yangbo Lu <yangbo.lu@nxp.com>
Signed-off-by: Geert Uytterhoeven <geert+renesas@glider.be>
Acked-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
drivers/base/Kconfig
drivers/base/soc.c
include/linux/sys_soc.h

index fdf44cac08e6d0026dab6095f46b55924cd89b1d..991b21e1f89be5878b12b8d3fcf53fc76ac74e16 100644 (file)
@@ -235,6 +235,7 @@ config GENERIC_CPU_AUTOPROBE
 
 config SOC_BUS
        bool
+       select GLOB
 
 source "drivers/base/regmap/Kconfig"
 
index 028cef377fd49959aec641716531a5d14668d141..04ee597fc3a3fda00ff69cf0e14d54b5b871cb42 100644 (file)
@@ -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);
 
@@ -168,3 +169,68 @@ static int __init soc_bus_register(void)
        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 &&
+           !glob_match(match->machine, soc_dev->attr->machine))
+               return 0;
+
+       if (match->family &&
+           !glob_match(match->family, soc_dev->attr->family))
+               return 0;
+
+       if (match->revision &&
+           !glob_match(match->revision, soc_dev->attr->revision))
+               return 0;
+
+       if (match->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);
index 2739ccb69571e444265862bda4f5cc0f15636c2f..9f5eb06f9fd875653d4cd5ae679c4d7ca684cd17 100644 (file)
@@ -13,6 +13,7 @@ struct soc_device_attribute {
        const char *family;
        const char *revision;
        const char *soc_id;
+       const void *data;
 };
 
 /**
@@ -34,4 +35,6 @@ void soc_device_unregister(struct soc_device *soc_dev);
  */
 struct device *soc_device_to_device(struct soc_device *soc);
 
+const struct soc_device_attribute *soc_device_match(
+       const struct soc_device_attribute *matches);
 #endif /* __SOC_BUS_H */