[PATCH 2/3] soc: Add VIA/WonderMedia SoC identification driver

Alexey Charkov posted 3 patches 9 months, 3 weeks ago
There is a newer version of this series
[PATCH 2/3] soc: Add VIA/WonderMedia SoC identification driver
Posted by Alexey Charkov 9 months, 3 weeks ago
Add a small SOC bus driver to parse the chip ID and revision made
available on VIA/WonderMedia SoCs via their system configuration
controller's SCC_ID register.

This is intended to select appropriate sets of on-chip device quirks
at runtime, as it has been found that even within the same SoC
version there can be register-incompatible differences, such as
with the SDMMC controller on WM8505 rev. A0-A1 vs. rev. A2.

The list of SoC versions is compiled from various vendor source dumps
and not all of them have corresponding mainline driver support.
Some of them also have been seen with varying on-chip markings while
sharing the same hardware chip ID's (as is the case with e.g. WM8850
vs. WM8950). In such cases the selection of names to use here among
those seen in various source dumps and chip markings was arbitrary.

Suggested by Krzysztof at [1] - thanks a lot!

[1] https://lore.kernel.org/all/14de236b-e2a7-4bde-986d-1e5ffddd01b4@kernel.org/

Signed-off-by: Alexey Charkov <alchark@gmail.com>
---
 drivers/soc/Kconfig              |   1 +
 drivers/soc/Makefile             |   1 +
 drivers/soc/vt8500/Kconfig       |  20 +++++++
 drivers/soc/vt8500/Makefile      |   2 +
 drivers/soc/vt8500/wmt-socinfo.c | 121 +++++++++++++++++++++++++++++++++++++++
 5 files changed, 145 insertions(+)

diff --git a/drivers/soc/Kconfig b/drivers/soc/Kconfig
index 6a8daeb8c4b96cd29d56343b338a423140b89896..37ca3f094f8994c7e9c7c99c3ba47d168d41ce30 100644
--- a/drivers/soc/Kconfig
+++ b/drivers/soc/Kconfig
@@ -28,6 +28,7 @@ source "drivers/soc/tegra/Kconfig"
 source "drivers/soc/ti/Kconfig"
 source "drivers/soc/ux500/Kconfig"
 source "drivers/soc/versatile/Kconfig"
+source "drivers/soc/vt8500/Kconfig"
 source "drivers/soc/xilinx/Kconfig"
 
 endmenu
diff --git a/drivers/soc/Makefile b/drivers/soc/Makefile
index 2037a8695cb2898659a434803dcdfa2d95b1dbd6..777255401252eab554f56bded7ff8ea5611704bf 100644
--- a/drivers/soc/Makefile
+++ b/drivers/soc/Makefile
@@ -34,4 +34,5 @@ obj-$(CONFIG_ARCH_TEGRA)	+= tegra/
 obj-y				+= ti/
 obj-$(CONFIG_ARCH_U8500)	+= ux500/
 obj-y				+= versatile/
+obj-y				+= vt8500/
 obj-y				+= xilinx/
diff --git a/drivers/soc/vt8500/Kconfig b/drivers/soc/vt8500/Kconfig
new file mode 100644
index 0000000000000000000000000000000000000000..2b2350678c3f70297c51f94eb77674c01be773d8
--- /dev/null
+++ b/drivers/soc/vt8500/Kconfig
@@ -0,0 +1,20 @@
+# SPDX-License-Identifier: GPL-2.0-only
+
+if ARCH_VT8500 || COMPILE_TEST
+
+menu "VIA/WonderMedia SoC drivers"
+
+config WMT_SOCINFO
+	bool "VIA/WonderMedia SoC Information driver"
+	default ARCH_VT8500
+	select SOC_BUS
+	default ARCH_VT8500
+	help
+	  Say yes to support decoding of VIA/WonderMedia system configuration
+	  register information. This currently includes just the chip ID register
+	  which helps identify the exact hardware revision of the SoC the kernel
+	  is running on (to know if any revision-specific quirks are required)
+
+endmenu
+
+endif
diff --git a/drivers/soc/vt8500/Makefile b/drivers/soc/vt8500/Makefile
new file mode 100644
index 0000000000000000000000000000000000000000..05964c5f2890989c1d794af4f5af10f849a497bc
--- /dev/null
+++ b/drivers/soc/vt8500/Makefile
@@ -0,0 +1,2 @@
+# SPDX-License-Identifier: GPL-2.0-only
+obj-$(CONFIG_WMT_SOCINFO)	+= wmt-socinfo.o
diff --git a/drivers/soc/vt8500/wmt-socinfo.c b/drivers/soc/vt8500/wmt-socinfo.c
new file mode 100644
index 0000000000000000000000000000000000000000..81dd6f722ede35150289a0d87cf09b95d9446948
--- /dev/null
+++ b/drivers/soc/vt8500/wmt-socinfo.c
@@ -0,0 +1,121 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright 2025 Alexey Charkov <alchark@gmail.com>
+ * Based on aspeed-socinfo.c
+ */
+
+#include <linux/io.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/of_platform.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/sys_soc.h>
+
+static struct {
+	const char *name;
+	const unsigned long id;
+} const chip_id_table[] = {
+	/* VIA */
+	{ "VT8420", 0x3300 },
+	{ "VT8430", 0x3357 },
+	{ "VT8500", 0x3400 },
+
+	/* WonderMedia */
+	{ "WM8425", 0x3429 },
+	{ "WM8435", 0x3437 },
+	{ "WM8440", 0x3451 },
+	{ "WM8505", 0x3426 },
+	{ "WM8650", 0x3465 },
+	{ "WM8750", 0x3445 },
+	{ "WM8850", 0x3481 },
+	{ "WM8880", 0x3498 },
+};
+
+static const char *sccid_to_name(unsigned long sccid)
+{
+	unsigned long id = sccid >> 16;
+	unsigned int i;
+
+	for (i = 0 ; i < ARRAY_SIZE(chip_id_table) ; ++i) {
+		if (chip_id_table[i].id == id)
+			return chip_id_table[i].name;
+	}
+
+	return "Unknown";
+}
+
+static const char *sccid_to_rev(unsigned long sccid)
+{
+	char letter, digit;
+
+	letter = (sccid >> 8) & 0xf;
+	letter = (letter - 1) + 'A';
+
+	digit = sccid & 0xff;
+	digit = (digit - 1) + '0';
+
+	return kasprintf(GFP_KERNEL, "%c%c", letter, digit);
+}
+
+static int __init wmt_socinfo_init(void)
+{
+	struct soc_device_attribute *attrs;
+	struct soc_device *soc_dev;
+	struct device_node *np;
+	void __iomem *reg;
+	unsigned long sccid;
+	const char *machine = NULL;
+
+	np = of_find_compatible_node(NULL, NULL, "via,scc-id");
+	if (!of_device_is_available(np)) {
+		of_node_put(np);
+		return -ENODEV;
+	}
+
+	reg = of_iomap(np, 0);
+	if (!reg) {
+		of_node_put(np);
+		return -ENODEV;
+	}
+	sccid = readl(reg);
+	iounmap(reg);
+
+	attrs = kzalloc(sizeof(*attrs), GFP_KERNEL);
+	if (!attrs)
+		return -ENODEV;
+
+	/*
+	 * Machine: VIA APC Rock
+	 * Family: WM8850
+	 * Revision: A2
+	 * SoC ID: raw silicon revision id (0x34810103)
+	 */
+
+	np = of_find_node_by_path("/");
+	of_property_read_string(np, "model", &machine);
+	if (machine)
+		attrs->machine = kstrdup(machine, GFP_KERNEL);
+	of_node_put(np);
+
+	attrs->family = sccid_to_name(sccid);
+	attrs->revision = sccid_to_rev(sccid);
+	attrs->soc_id = kasprintf(GFP_KERNEL, "%08lx", sccid);
+
+	soc_dev = soc_device_register(attrs);
+	if (IS_ERR(soc_dev)) {
+		kfree(attrs->machine);
+		kfree(attrs->soc_id);
+		kfree(attrs->revision);
+		kfree(attrs);
+		return PTR_ERR(soc_dev);
+	}
+
+	pr_info("VIA/WonderMedia %s rev %s (%s)\n",
+			attrs->family,
+			attrs->revision,
+			attrs->soc_id);
+
+	return 0;
+}
+early_initcall(wmt_socinfo_init);

-- 
2.49.0
Re: [PATCH 2/3] soc: Add VIA/WonderMedia SoC identification driver
Posted by Krzysztof Kozlowski 9 months, 2 weeks ago
On 23/04/2025 21:18, Alexey Charkov wrote:
> Add a small SOC bus driver to parse the chip ID and revision made
> available on VIA/WonderMedia SoCs via their system configuration
> controller's SCC_ID register.


...

> +#include <linux/io.h>
> +#include <linux/of.h>
> +#include <linux/of_address.h>
> +#include <linux/of_platform.h>
> +#include <linux/platform_device.h>
> +#include <linux/slab.h>
> +#include <linux/sys_soc.h>
> +
> +static struct {

I think const by convention is placed here - right after static. It
should be equivalent, just convention.

> +	const char *name;
> +	const unsigned long id;
> +} const chip_id_table[] = {
> +	/* VIA */
> +	{ "VT8420", 0x3300 },
> +	{ "VT8430", 0x3357 },
> +	{ "VT8500", 0x3400 },
> +
> +	/* WonderMedia */
> +	{ "WM8425", 0x3429 },
> +	{ "WM8435", 0x3437 },
> +	{ "WM8440", 0x3451 },
> +	{ "WM8505", 0x3426 },
> +	{ "WM8650", 0x3465 },
> +	{ "WM8750", 0x3445 },
> +	{ "WM8850", 0x3481 },
> +	{ "WM8880", 0x3498 },
> +};
> +
> +static const char *sccid_to_name(unsigned long sccid)
> +{
> +	unsigned long id = sccid >> 16;
> +	unsigned int i;
> +
> +	for (i = 0 ; i < ARRAY_SIZE(chip_id_table) ; ++i) {
> +		if (chip_id_table[i].id == id)
> +			return chip_id_table[i].name;
> +	}
> +
> +	return "Unknown";
> +}
> +
> +static const char *sccid_to_rev(unsigned long sccid)
> +{
> +	char letter, digit;
> +
> +	letter = (sccid >> 8) & 0xf;
> +	letter = (letter - 1) + 'A';
> +
> +	digit = sccid & 0xff;
> +	digit = (digit - 1) + '0';
> +
> +	return kasprintf(GFP_KERNEL, "%c%c", letter, digit);
> +}
> +
> +static int __init wmt_socinfo_init(void)
> +{
> +	struct soc_device_attribute *attrs;
> +	struct soc_device *soc_dev;
> +	struct device_node *np;
> +	void __iomem *reg;
> +	unsigned long sccid;
> +	const char *machine = NULL;
> +
> +	np = of_find_compatible_node(NULL, NULL, "via,scc-id");
> +	if (!of_device_is_available(np)) {
> +		of_node_put(np);
> +		return -ENODEV;
> +	}
> +
> +	reg = of_iomap(np, 0);

of_node_put(np) here... although this will be dropped (see below)


> +	if (!reg) {
> +		of_node_put(np);
> +		return -ENODEV;
> +	}
> +	sccid = readl(reg);
> +	iounmap(reg);
> +
> +	attrs = kzalloc(sizeof(*attrs), GFP_KERNEL);
> +	if (!attrs)
> +		return -ENODEV;
> +
> +	/*
> +	 * Machine: VIA APC Rock
> +	 * Family: WM8850
> +	 * Revision: A2
> +	 * SoC ID: raw silicon revision id (0x34810103)
> +	 */
> +
> +	np = of_find_node_by_path("/");
> +	of_property_read_string(np, "model", &machine);
> +	if (machine)
> +		attrs->machine = kstrdup(machine, GFP_KERNEL);
> +	of_node_put(np);
> +
> +	attrs->family = sccid_to_name(sccid);
> +	attrs->revision = sccid_to_rev(sccid);
> +	attrs->soc_id = kasprintf(GFP_KERNEL, "%08lx", sccid);
> +
> +	soc_dev = soc_device_register(attrs);
> +	if (IS_ERR(soc_dev)) {
> +		kfree(attrs->machine);
> +		kfree(attrs->soc_id);
> +		kfree(attrs->revision);
> +		kfree(attrs);
> +		return PTR_ERR(soc_dev);
> +	}
> +
> +	pr_info("VIA/WonderMedia %s rev %s (%s)\n",
> +			attrs->family,
> +			attrs->revision,
> +			attrs->soc_id);
> +
> +	return 0;
> +}
> +early_initcall(wmt_socinfo_init);

No, this does not scale. This is supposed to be module_platform_driver
instead of manually re-ordering code. Then all your memory allocations
become devm, printks become dev_xxx and you can simplify it.


> 


Best regards,
Krzysztof
Re: [PATCH 2/3] soc: Add VIA/WonderMedia SoC identification driver
Posted by Alexey Charkov 9 months, 2 weeks ago
On Fri, Apr 25, 2025 at 2:24 PM Krzysztof Kozlowski <krzk@kernel.org> wrote:
>
> On 23/04/2025 21:18, Alexey Charkov wrote:
> > Add a small SOC bus driver to parse the chip ID and revision made
> > available on VIA/WonderMedia SoCs via their system configuration
> > controller's SCC_ID register.
>
>
> ...
>
> > +#include <linux/io.h>
> > +#include <linux/of.h>
> > +#include <linux/of_address.h>
> > +#include <linux/of_platform.h>
> > +#include <linux/platform_device.h>
> > +#include <linux/slab.h>
> > +#include <linux/sys_soc.h>
> > +
> > +static struct {
>
> I think const by convention is placed here - right after static. It
> should be equivalent, just convention.

Makes sense, thank you. Will adjust in v2.

> > +     const char *name;
> > +     const unsigned long id;
> > +} const chip_id_table[] = {
> > +     /* VIA */
> > +     { "VT8420", 0x3300 },
> > +     { "VT8430", 0x3357 },
> > +     { "VT8500", 0x3400 },
> > +
> > +     /* WonderMedia */
> > +     { "WM8425", 0x3429 },
> > +     { "WM8435", 0x3437 },
> > +     { "WM8440", 0x3451 },
> > +     { "WM8505", 0x3426 },
> > +     { "WM8650", 0x3465 },
> > +     { "WM8750", 0x3445 },
> > +     { "WM8850", 0x3481 },
> > +     { "WM8880", 0x3498 },
> > +};
> > +
> > +static const char *sccid_to_name(unsigned long sccid)
> > +{
> > +     unsigned long id = sccid >> 16;
> > +     unsigned int i;
> > +
> > +     for (i = 0 ; i < ARRAY_SIZE(chip_id_table) ; ++i) {
> > +             if (chip_id_table[i].id == id)
> > +                     return chip_id_table[i].name;
> > +     }
> > +
> > +     return "Unknown";
> > +}
> > +
> > +static const char *sccid_to_rev(unsigned long sccid)
> > +{
> > +     char letter, digit;
> > +
> > +     letter = (sccid >> 8) & 0xf;
> > +     letter = (letter - 1) + 'A';
> > +
> > +     digit = sccid & 0xff;
> > +     digit = (digit - 1) + '0';
> > +
> > +     return kasprintf(GFP_KERNEL, "%c%c", letter, digit);
> > +}
> > +
> > +static int __init wmt_socinfo_init(void)
> > +{
> > +     struct soc_device_attribute *attrs;
> > +     struct soc_device *soc_dev;
> > +     struct device_node *np;
> > +     void __iomem *reg;
> > +     unsigned long sccid;
> > +     const char *machine = NULL;
> > +
> > +     np = of_find_compatible_node(NULL, NULL, "via,scc-id");
> > +     if (!of_device_is_available(np)) {
> > +             of_node_put(np);
> > +             return -ENODEV;
> > +     }
> > +
> > +     reg = of_iomap(np, 0);
>
> of_node_put(np) here... although this will be dropped (see below)
>
>
> > +     if (!reg) {
> > +             of_node_put(np);
> > +             return -ENODEV;
> > +     }
> > +     sccid = readl(reg);
> > +     iounmap(reg);
> > +
> > +     attrs = kzalloc(sizeof(*attrs), GFP_KERNEL);
> > +     if (!attrs)
> > +             return -ENODEV;
> > +
> > +     /*
> > +      * Machine: VIA APC Rock
> > +      * Family: WM8850
> > +      * Revision: A2
> > +      * SoC ID: raw silicon revision id (0x34810103)
> > +      */
> > +
> > +     np = of_find_node_by_path("/");
> > +     of_property_read_string(np, "model", &machine);
> > +     if (machine)
> > +             attrs->machine = kstrdup(machine, GFP_KERNEL);
> > +     of_node_put(np);
> > +
> > +     attrs->family = sccid_to_name(sccid);
> > +     attrs->revision = sccid_to_rev(sccid);
> > +     attrs->soc_id = kasprintf(GFP_KERNEL, "%08lx", sccid);
> > +
> > +     soc_dev = soc_device_register(attrs);
> > +     if (IS_ERR(soc_dev)) {
> > +             kfree(attrs->machine);
> > +             kfree(attrs->soc_id);
> > +             kfree(attrs->revision);
> > +             kfree(attrs);
> > +             return PTR_ERR(soc_dev);
> > +     }
> > +
> > +     pr_info("VIA/WonderMedia %s rev %s (%s)\n",
> > +                     attrs->family,
> > +                     attrs->revision,
> > +                     attrs->soc_id);
> > +
> > +     return 0;
> > +}
> > +early_initcall(wmt_socinfo_init);
>
> No, this does not scale. This is supposed to be module_platform_driver
> instead of manually re-ordering code. Then all your memory allocations
> become devm, printks become dev_xxx and you can simplify it.

Fair enough. Will convert into a platform driver and use managed
functions. Thanks for pointing this out!

Best regards,
Alexey