| From b1b6e5ce1a71e21b47cc9c229eba2db35cc25f35 Mon Sep 17 00:00:00 2001 |
| From: Arnd Bergmann <arnd@arndb.de> |
| Date: Wed, 21 Sep 2016 14:57:19 +0800 |
| Subject: [PATCH 022/299] base: soc: Introduce soc_device_match() interface |
| |
| 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> |
| (cherry picked from commit c97db7cc7778e34a53b42d58c766f0ec0e30d580) |
| Signed-off-by: Simon Horman <horms+renesas@verge.net.au> |
| --- |
| drivers/base/Kconfig | 1 |
| drivers/base/soc.c | 66 ++++++++++++++++++++++++++++++++++++++++++++++++ |
| include/linux/sys_soc.h | 3 ++ |
| 3 files changed, 70 insertions(+) |
| |
| --- a/drivers/base/Kconfig |
| +++ b/drivers/base/Kconfig |
| @@ -240,6 +240,7 @@ config GENERIC_CPU_VULNERABILITIES |
| |
| config SOC_BUS |
| bool |
| + select GLOB |
| |
| source "drivers/base/regmap/Kconfig" |
| |
| --- 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); |
| |
| @@ -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); |
| --- 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,6 @@ void soc_device_unregister(struct soc_de |
| */ |
| 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 */ |