Skip to contentSkip to conversation

arm/dt: Add SoC detection macros

PATCH New Allen Martin 5 participants 11 messages Sep 9, 2011 – Sep 19, 2011

Subsystems (2)
lore
Share & tools
lore
#0 lore raw

PATCH 1/1 arm/dt: Add SoC detection macros

These macros allow runtime query of SoC family and version via soc_is_*() If the corresponding SoC is not configured the macro will evaluate to 0. If the corresponding SoC is the only architecure configured, the macro will evaluate to 1. If multiple architecures are configured the macro will evaluate to a runtime call to soc_get_version().

I've added tegra2 and tegra3 SoCs as a starting point.

---
 arch/arm/include/asm/soc.h |   76 ++++++++++++++++++++++++++++++++++++++++++++
 arch/arm/kernel/Makefile   |    1 +
 arch/arm/kernel/setup.c    |    2 +
 arch/arm/kernel/soc.c      |   37 +++++++++++++++++++++
 4 files changed, 116 insertions(+), 0 deletions(-)
 create mode 100644 arch/arm/include/asm/soc.h
 create mode 100644 arch/arm/kernel/soc.c
arch/arm/include/asm/soc.h new file +76 -0
new file mode 100644
index 0000000..d95f643
@@ -0,0 +1,76 @@
+/*
+ * arch/arm/include/asm/soc.h
+ *
+ * Copyright (C) 2011 NVIDIA, Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+/* determine if multiple SoCs are enabled */
+#undef MULTI_SOC
+#undef SOC_NAME
+
+#ifdef CONFIG_ARCH_TEGRA_2x_SOC
+# ifdef SOC_NAME
+#  undef MULTI_SOC
+#  define MULTI_SOC
+# else
+#   define SOC_NAME tegra2
+# endif
+#endif
+#ifdef CONFIG_ARCH_TEGRA_3x_SOC
+# ifdef SOC_NAME
+#  undef MULTI_SOC
+#  define MULTI_SOC
+# else
+#   define SOC_NAME tegra3
+# endif
+#endif
+
+#define soc_is_tegra2()			0
+#define soc_is_tegra3()			0
+
+#if defined(MULTI_SOC)
+# if defined(CONFIG_ARCH_TEGRA_2x_SOC)
+#  undef soc_is_tegra2
+#  define soc_is_tegra2()		is_tegra2()
+# endif
+# if defined(CONFIG_ARCH_TEGRA_3x_SOC)
+#  undef soc_is_tegra3
+#  define soc_is_tegra3()		is_tegra3()
+# endif
+#else /* non-multi, only one architecture is on */
+# if defined(CONFIG_ARCH_TEGRA_2x_SOC)
+#  undef soc_is_tegra2
+#  define soc_is_tegra2()		1
+# elif defined(CONFIG_ARCH_TEGRA_3x_SOC)
+#  undef soc_is_tegra3
+#  define soc_is_tegra3()		1
+# endif
+#endif
+
+enum soc_version {
+	SOC_UNKNOWN = 0,
+	TEGRA_T20,
+	TEGRA_T30,
+};
+
+void soc_init_version(void);
+enum soc_version soc_get_version(void);
+
+static inline int is_tegra2(void)
+{
+	return soc_get_version() == TEGRA_T20;
+}
+
+static inline int is_tegra3(void)
+{
+	return soc_get_version() == TEGRA_T30;
+}
arch/arm/kernel/Makefile +1 -0
index c687bce..de29477 100644
@@ -50,6 +50,7 @@ obj-$(CONFIG_KGDB)		+= kgdb.o
 obj-$(CONFIG_ARM_UNWIND)	+= unwind.o
 obj-$(CONFIG_HAVE_TCM)		+= tcm.o
 obj-$(CONFIG_OF)		+= devtree.o
+obj-$(CONFIG_OF)		+= soc.o
 obj-$(CONFIG_CRASH_DUMP)	+= crash_dump.o
 obj-$(CONFIG_SWP_EMULATE)	+= swp_emulate.o
 CFLAGS_swp_emulate.o		:= -Wa,-march=armv7-a
arch/arm/kernel/setup.c +2 -0
index 0ca06f7..63ad6de 100644
@@ -52,6 +52,7 @@
 #include <asm/mach/time.h>
 #include <asm/traps.h>
 #include <asm/unwind.h>
+#include <asm/soc.h>
 
 #if defined(CONFIG_DEPRECATED_PARAM_STRUCT)
 #include "compat.h"
@@ -922,6 +923,7 @@ void __init setup_arch(char **cmdline_p)
 	request_standard_resources(mdesc);
 
 	unflatten_device_tree();
+	soc_init_version();
 
 #ifdef CONFIG_SMP
 	if (is_smp())
arch/arm/kernel/soc.c new file +37 -0
new file mode 100644
index 0000000..8686468
@@ -0,0 +1,37 @@
+/*
+ * arch/arm/kernel/soc.c
+ *
+ * Copyright (C) 2011 NVIDIA, Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/of.h>
+
+#include <asm/soc.h>
+
+static enum soc_version soc_version;
+
+enum soc_version soc_get_version(void)
+{
+	return soc_version;
+}
+
+void soc_init_version(void)
+{
+	if (of_machine_is_compatible("nvidia,tegra20"))
+		soc_version = TEGRA_T20;
+	else if (of_machine_is_compatible("nvidia,tegra30"))
+		soc_version = TEGRA_T30;
+	else
+		panic("Unknown SoC");
+}
Signature · 1 line
1.7.4.1
#1 lore raw

PATCH 1/1 arm/dt: Add SoC detection macros

Quoted 7 lines · Allen Martin

These macros allow runtime query of SoC family and version via soc_is_*() ?If the corresponding SoC is not configured the macro will evaluate to 0. ?If the corresponding SoC is the only architecure configured, the macro will evaluate to 1. ?If multiple architecures are configured the macro will evaluate to a runtime call to soc_get_version().

Review

Quoted 9 lines
(quoted diff) +9 -0
+void soc_init_version(void)
+{
+ ? ? ? if (of_machine_is_compatible("nvidia,tegra20"))
+ ? ? ? ? ? ? ? soc_version = TEGRA_T20;
+ ? ? ? else if (of_machine_is_compatible("nvidia,tegra30"))
+ ? ? ? ? ? ? ? soc_version = TEGRA_T30;
+ ? ? ? else
+ ? ? ? ? ? ? ? panic("Unknown SoC");
+}

The above should be enough for your local code, no need to wrap it in layers of indirection.

Sure, doing the of_machine_is...() calls are slow, but you shouldn't be doing runtime decisions about what SoC you are on during critical path anyway -- it should be done at setup/probe time, much of the rest should be possible to handle with setting appropriate function pointers, etc.

Also, panic:ing is unacceptable.

-Olof

#2 lore raw

PATCH 1/1 arm/dt: Add SoC detection macros

In reply to Allen Martin
Quoted 28 lines · Allen Martin
arch/arm/include/asm/soc.h new file +28 -0
+#ifdef CONFIG_ARCH_TEGRA_2x_SOC
+# ifdef SOC_NAME
+#  undef MULTI_SOC
+#  define MULTI_SOC
+# else
+#   define SOC_NAME tegra2
+# endif
+#endif
+#ifdef CONFIG_ARCH_TEGRA_3x_SOC
+# ifdef SOC_NAME
+#  undef MULTI_SOC
+#  define MULTI_SOC
+# else
+#   define SOC_NAME tegra3
+# endif
+#endif
+
+#define soc_is_tegra2()			0
+#define soc_is_tegra3()			0
+
+#if defined(MULTI_SOC)
+# if defined(CONFIG_ARCH_TEGRA_2x_SOC)
+#  undef soc_is_tegra2
+#  define soc_is_tegra2()		is_tegra2()
+# endif
+# if defined(CONFIG_ARCH_TEGRA_3x_SOC)
+#  undef soc_is_tegra3
+#  define soc_is_tegra3()		is_tegra3()

arch/arm/include/asm/soc.h line 57

Quoted 10 lines
arch/arm/include/asm/soc.h new file +10 -0
+# endif
+#else /* non-multi, only one architecture is on */
+# if defined(CONFIG_ARCH_TEGRA_2x_SOC)
+#  undef soc_is_tegra2
+#  define soc_is_tegra2()		1
+# elif defined(CONFIG_ARCH_TEGRA_3x_SOC)
+#  undef soc_is_tegra3
+#  define soc_is_tegra3()		1
+# endif
+#endif

This is not the way to do this, especially for a file in asm/*.h. Look at the way machine_is_xxx() is dealt with in include/generated/mach-types.h.

#define MULTI_SOC 0
#undef SOC_SELECTED

#ifdef CONFIG_ARCH_TEGRA_2x_SOC

# ifdef SOC_SELECTED
#  undef MULTI_SOC
#  define MULTI_SOC			1
# else
#  define SOC_SELECTED
# endif
# define soc_is_tegra2()		(!MULTI_SOC || is_tegra2())
#else
# define soc_is_tegra2()		0
#endif

#ifdef CONFIG_ARCH_TEGRA_3x_SOC

# ifdef SOC_SELECTED
#  undef MULTI_SOC
#  define MULTI_SOC			1
# else
#  define SOC_SELECTED
# endif
# define soc_is_tegra3()		(!MULTI_SOC || is_tegra3())
#else
# define soc_is_tegra3()		0
#endif

#undef SOC_SELECTED

The above is nicely extensible - if other SoCs need to extend this they just need to add another outer ifdef..endif section to the file.

arch/arm/include/asm/soc.h line 62

Quoted 5 lines
arch/arm/include/asm/soc.h new file +5 -0
+
+enum soc_version {
+	SOC_UNKNOWN = 0,
+	TEGRA_T20,
+	TEGRA_T30,

I'd suggest prefixing these with SOC_ to avoid any namespace problems.

Quoted 4 lines
arch/arm/include/asm/soc.h new file +4 -0
+};
+
+void soc_init_version(void);
+enum soc_version soc_get_version(void);

arch/arm/include/asm/soc.h line 76

Quoted 10 lines
arch/arm/include/asm/soc.h new file +10 -0
+
+static inline int is_tegra2(void)
+{
+	return soc_get_version() == TEGRA_T20;
+}
+
+static inline int is_tegra3(void)
+{
+	return soc_get_version() == TEGRA_T30;
+}

If we require all SoCs to provide a value in soc_version, then we can use exactly the same method as mach-types.h uses - and while at this, please get rid of soc_get_version(). It's far cheaper to access the variable directly rather than indirect through a function, just like we do with __machine_arch_type. Mark it __read_mostly too.

One last point to raise here is - and it's quite a fundamental one - do we really want this? If we're making decisions based on the SoC type, that suggests to me that the hardware description in DT is incomplete, and we're hiding stuff in the kernel behind the SoC type. That doesn't sound particularly appealing given the point of DT is to encode the hardware specific information outside the kernel code.

#3 lore raw

PATCH 1/1 arm/dt: Add SoC detection macros

Quoted 104 lines · Russell King - ARM Linux
Quoted 28 lines · Allen Martin
arch/arm/include/asm/soc.h new file +28 -0
+#ifdef CONFIG_ARCH_TEGRA_2x_SOC
+# ifdef SOC_NAME
+#  undef MULTI_SOC
+#  define MULTI_SOC
+# else
+#   define SOC_NAME tegra2
+# endif
+#endif
+#ifdef CONFIG_ARCH_TEGRA_3x_SOC
+# ifdef SOC_NAME
+#  undef MULTI_SOC
+#  define MULTI_SOC
+# else
+#   define SOC_NAME tegra3
+# endif
+#endif
+
+#define soc_is_tegra2()			0
+#define soc_is_tegra3()			0
+
+#if defined(MULTI_SOC)
+# if defined(CONFIG_ARCH_TEGRA_2x_SOC)
+#  undef soc_is_tegra2
+#  define soc_is_tegra2()		is_tegra2()
+# endif
+# if defined(CONFIG_ARCH_TEGRA_3x_SOC)
+#  undef soc_is_tegra3
+#  define soc_is_tegra3()		is_tegra3()

arch/arm/include/asm/soc.h line 57

Quoted 10 lines
arch/arm/include/asm/soc.h new file +10 -0
+# endif
+#else /* non-multi, only one architecture is on */
+# if defined(CONFIG_ARCH_TEGRA_2x_SOC)
+#  undef soc_is_tegra2
+#  define soc_is_tegra2()		1
+# elif defined(CONFIG_ARCH_TEGRA_3x_SOC)
+#  undef soc_is_tegra3
+#  define soc_is_tegra3()		1
+# endif
+#endif

This is not the way to do this, especially for a file in asm/*.h. Look at the way machine_is_xxx() is dealt with in include/generated/mach-types.h.

#define MULTI_SOC 0
#undef SOC_SELECTED

#ifdef CONFIG_ARCH_TEGRA_2x_SOC

# ifdef SOC_SELECTED
#  undef MULTI_SOC
#  define MULTI_SOC			1
# else
#  define SOC_SELECTED
# endif
# define soc_is_tegra2()		(!MULTI_SOC || is_tegra2())
#else
# define soc_is_tegra2()		0
#endif

#ifdef CONFIG_ARCH_TEGRA_3x_SOC

# ifdef SOC_SELECTED
#  undef MULTI_SOC
#  define MULTI_SOC			1
# else
#  define SOC_SELECTED
# endif
# define soc_is_tegra3()		(!MULTI_SOC || is_tegra3())
#else
# define soc_is_tegra3()		0
#endif

#undef SOC_SELECTED

The above is nicely extensible - if other SoCs need to extend this they just need to add another outer ifdef..endif section to the file.

arch/arm/include/asm/soc.h line 62

Quoted 5 lines
arch/arm/include/asm/soc.h new file +5 -0
+
+enum soc_version {
+	SOC_UNKNOWN = 0,
+	TEGRA_T20,
+	TEGRA_T30,

I'd suggest prefixing these with SOC_ to avoid any namespace problems.

Quoted 4 lines
arch/arm/include/asm/soc.h new file +4 -0
+};
+
+void soc_init_version(void);
+enum soc_version soc_get_version(void);

arch/arm/include/asm/soc.h line 76

Quoted 10 lines
arch/arm/include/asm/soc.h new file +10 -0
+
+static inline int is_tegra2(void)
+{
+	return soc_get_version() == TEGRA_T20;
+}
+
+static inline int is_tegra3(void)
+{
+	return soc_get_version() == TEGRA_T30;
+}

If we require all SoCs to provide a value in soc_version, then we can use exactly the same method as mach-types.h uses - and while at this, please get rid of soc_get_version(). It's far cheaper to access the variable directly rather than indirect through a function, just like we do with __machine_arch_type. Mark it __read_mostly too.

Quoted 6 lines

One last point to raise here is - and it's quite a fundamental one - do we really want this? If we're making decisions based on the SoC type, that suggests to me that the hardware description in DT is incomplete, and we're hiding stuff in the kernel behind the SoC type. That doesn't sound particularly appealing given the point of DT is to encode the hardware specific information outside the kernel code.

except if a machine can run on 2 soc so detect it will avoid to have 2 Device Tree

Best Regards,
J.

#4 lore raw

PATCH 1/1 arm/dt: Add SoC detection macros

Quoted 10 lines · Jean-Christophe PLAGNIOL-VILLARD
Quoted 6 lines · Russell King - ARM Linux

One last point to raise here is - and it's quite a fundamental one - do we really want this? If we're making decisions based on the SoC type, that suggests to me that the hardware description in DT is incomplete, and we're hiding stuff in the kernel behind the SoC type. That doesn't sound particularly appealing given the point of DT is to encode the hardware specific information outside the kernel code.

except if a machine can run on 2 soc so detect it will avoid to have 2 Device Tree

This code is structured to match the SoC based upon an entry in the DT, so for tegra2 vs tegra3 it's already having to have two different DTs to distinguish between them.

However, I still go back to my original point: the point of DT is to provide a description of the hardware which the kernel is running on - not only for current hardware but possibly future hardware as well. Eg, if Tegra4 comes along with more peripherals than Tegra3 but has basic hardware which the kernel already supports, just wired up differently, then Tegra4 should just work with a new DT file and no code changes.

What I'm saying is that in that scenario it should not be necessary to edit the kernel to invent new SoC types, and then teach it that Tegra4 is mostly the same as Tegra3. That information should all be encoded into the DT rather than the C code in the kernel.

So, I think adding this SoC type stuff is the wrong approach to the problem.

#5 lore raw

PATCH 1/1 arm/dt: Add SoC detection macros

Quoted 23 lines · Russell King - ARM Linux
Quoted 10 lines · Jean-Christophe PLAGNIOL-VILLARD
Quoted 6 lines · Russell King - ARM Linux

One last point to raise here is - and it's quite a fundamental one - do we really want this? If we're making decisions based on the SoC type, that suggests to me that the hardware description in DT is incomplete, and we're hiding stuff in the kernel behind the SoC type. That doesn't sound particularly appealing given the point of DT is to encode the hardware specific information outside the kernel code.

except if a machine can run on 2 soc so detect it will avoid to have 2 Device Tree

This code is structured to match the SoC based upon an entry in the DT, so for tegra2 vs tegra3 it's already having to have two different DTs to distinguish between them.

However, I still go back to my original point: the point of DT is to provide a description of the hardware which the kernel is running on - not only for current hardware but possibly future hardware as well. Eg, if Tegra4 comes along with more peripherals than Tegra3 but has basic hardware which the kernel already supports, just wired up differently, then Tegra4 should just work with a new DT file and no code changes.

Quoted 7 lines

What I'm saying is that in that scenario it should not be necessary to edit the kernel to invent new SoC types, and then teach it that Tegra4 is mostly the same as Tegra3. That information should all be encoded into the DT rather than the C code in the kernel.

So, I think adding this SoC type stuff is the wrong approach to the problem.

I agree about it I just mean that if you have the same board with 2 SoC which are pin to pin compatible detect the soc type will be usefull as the soc resource may not be the same

Best Regards,
J.

#6 lore raw

PATCH 1/1 arm/dt: Add SoC detection macros

Quoted 4 lines · On Saturday 17 September 2011 20:19:07 Jean-Christophe PLAGNIOL-VILLARD wrote:

I agree about it I just mean that if you have the same board with 2 SoC which are pin to pin compatible detect the soc type will be usefull as the soc resource may not be the same

In that scenario, you would put all SOC specific data into one .dtsi file and all board data into another .dtsi file and then create a description for the combination using a trivial .dts file with two include statements. This is all solved outside of the kernel already, so there is no need for infrastructure in the kernel.

Arnd

#7 lore raw

PATCH 1/1 arm/dt: Add SoC detection macros

Quoted 11 lines · Arnd Bergmann
Quoted 4 lines · On Saturday 17 September 2011 20:19:07 Jean-Christophe PLAGNIOL-VILLARD wrote:

I agree about it I just mean that if you have the same board with 2 SoC which are pin to pin compatible detect the soc type will be usefull as the soc resource may not be the same

In that scenario, you would put all SOC specific data into one .dtsi file and all board data into another .dtsi file and then create a description for the combination using a trivial .dts file with two include statements. This is all solved outside of the kernel already, so there is no need for infrastructure in the kernel.

except in this case you must have 2 machine id which can be avoided if you can detect the soc at kernel level

Best Regards,
J.

#8 lore raw

PATCH 1/1 arm/dt: Add SoC detection macros

Quoted 7 lines · On Sunday 18 September 2011 02:46:15 Jean-Christophe PLAGNIOL-VILLARD wrote:
Quoted 6 lines · Arnd Bergmann
Quoted 4 lines · On Saturday 17 September 2011 20:19:07 Jean-Christophe PLAGNIOL-VILLARD wrote:

I agree about it I just mean that if you have the same board with 2 SoC which are pin to pin compatible detect the soc type will be usefull as the soc resource may not be the same

Quoted 7 lines
Quoted 5 lines

In that scenario, you would put all SOC specific data into one .dtsi file and all board data into another .dtsi file and then create a description for the combination using a trivial .dts file with two include statements. This is all solved outside of the kernel already, so there is no need for infrastructure in the kernel.

except in this case you must have 2 machine id which can be avoided if you can detect the soc at kernel level

But when you move to device tree based probing, you don't use machine numbers at all, so it doesn't matter.

Detecting the soc in the kernel is pointless, since you describe it in the device tree anyway. Ideally, the kernel should not need to know about the possible SoCs at all, it just needs to know the components on it.

While we first want to get to the point where the device tree only needs to describe the off-chip devices, in the long run it's much better to describe all of the hardware in the device tree, as we do with new platforms (zynq, prima2, ...) already. I don't think we need to introduce infrastructure now when we know that we want to get rid of it later.

Arnd

#9 lore raw

PATCH 1/1 arm/dt: Add SoC detection macros

In reply to Russell King - ARM Linux
Quoted 6 lines

What I'm saying is that in that scenario it should not be necessary to edit the kernel to invent new SoC types, and then teach it that Tegra4 is mostly the same as Tegra3. That information should all be encoded into the DT rather than the C code in the kernel.

So, I think adding this SoC type stuff is the wrong approach to the problem.

What ends up happening in practice for a lot of hw blocks inside the SoC, is that tegra4 is mostly the same as tegra3 with a few new registers and bug fixes that slightly change the programming model. So either we have to add device quirks to teach device tree about the differences, and pass those in as flags to the driver, or we can do SoC detection at runtime in the driver. It sounds like the consensus from you and Olof is that the first is preferable.

-Allen

#10 lore raw

PATCH 1/1 arm/dt: Add SoC detection macros

Quoted 8 lines · Allen Martin
Quoted 6 lines

What I'm saying is that in that scenario it should not be necessary to edit the kernel to invent new SoC types, and then teach it that Tegra4 is mostly the same as Tegra3. ?That information should all be encoded into the DT rather than the C code in the kernel.

So, I think adding this SoC type stuff is the wrong approach to the problem.

What ends up happening in practice for a lot of hw blocks inside the SoC, is that tegra4 is mostly the same as tegra3 with a few new registers and bug fixes that slightly change the programming model. ?So either we have to add device quirks to teach device tree about the differences, and pass those in as flags to the driver, or we can do SoC detection at runtime in the driver. ?It sounds like the consensus from you and Olof is that the first is preferable.

Well, my fear of making a performance-optimized implementation of this is that it will be overused at runtime. As an example on how it should not look when starting out on a new driver today, look at the gpio driver for omap. It looks the way it does because they merged the 3 separate implementations together, but they have a bunch of functions in there that have three completely different code paths depending on which platform they are on. For those, having three different functions, and a function pointer to reach it through, makes more sense.

But for to-be-upstreamed SoC support for, for example, tegra3 -- a platform that has device-tree support -- it would be better to do a new compatible field in the device tree for it, and thus at the time of probing of the device (in the driver) you will know if it's a tegra2 or tegra3 gpio controller you are configuring. Based on that, you can setup your driver to behave appropriately -- some of that might of course still be runtime checks, but hopefully not too much of it.

The SoC detection-at-runtime doesn't scale and map to drivers all that cleanly either. Today you have a linear roadmap of devices where development happens on the "family number" field. What if there is a future T32 that is an evolution of T30 but with tegra4's gpio controller, for example [assuming numbering is similar to today's
tegra2 t20/t25/etc]? It's better to do the versioning per
IP/device/driver, than trying to map a global version/product number per-driver to different behavior.

-Olof