diff mbox series

[4/4] syscon: add Linux-compatible syscon API

Message ID 1524019125-26287-5-git-send-email-yamada.masahiro@socionext.com
State Superseded
Headers show
Series Add Linux-compatible syscon_to_regmap API | expand

Commit Message

Masahiro Yamada April 18, 2018, 2:38 a.m. UTC
The syscon implementation in U-Boot is different from that in Linux.
Thus, DT files imported from Linux do not work for U-Boot.

In U-Boot driver model, each node is bound to a dedicated driver
that is the most compatible to it.  This design gets along with the
concept of DT, and the syscon in Linux originally worked like that.

However, Linux commit bdb0066df96e ("mfd: syscon: Decouple syscon
interface from platform devices") changed the behavior because it is
useful to let a device bind to another driver, but still works as a
syscon provider.

That change had happened before U-Boot initially supported the syscon
driver by commit 6f98b7504f70 ("dm: Add support for generic system
controllers (syscon)").  So, the U-Boot's syscon works differently
from the beginning.  I'd say this is mis-implementation given that
DT is not oriented to a particular project, but Linux is the canon
of DT in practice.

The problem typically arises in the combination of "syscon" and
"simple-mfd" compatibles.

In Linux, they are orthogonal, i.e., the order between "syscon" and
"simple-mfd" does not matter at all.

Assume the following compatible.

   compatible = "foo,bar-syscon", "syscon", "simple-mfd";

In U-Boot, this device node is bound to the syscon driver
(driver/core/syscon-uclass.c) since the "syscon" is found to be the
most compatible.  Then, syscon_get_regmap() succeeds.

However,

   compatible = "foo,bar-syscon", "simple-mfd", "syscon";

does not work because this node is bound to the simple-bus driver
(drivers/core/simple-bus.c) in the favor of "simple-mfd" compatible.
The compatible string "syscon" is just dismissed.

Moreover,

   compatible = "foo,bar-syscon", "syscon";

works like the first case because the syscon driver populates the
child devices.  This is wrong because populating children is the job
of "simple-mfd" (or "simple-bus").

This commit ports syscon_node_to_regmap() from Linux.  This API
does not require the given node to be bound to a driver in any way.

Reported-by: Kunihiko Hayashi <hayashi.kunihiko@socionext.com>
Signed-off-by: Masahiro Yamada <yamada.masahiro@socionext.com>
---

 drivers/core/syscon-uclass.c | 63 ++++++++++++++++++++++++++++++++++++++++++++
 include/syscon.h             |  8 ++++++
 2 files changed, 71 insertions(+)

Comments

Simon Glass April 22, 2018, 8:11 p.m. UTC | #1
Hi Masahiro,

On 17 April 2018 at 20:38, Masahiro Yamada <yamada.masahiro@socionext.com>
wrote:
> The syscon implementation in U-Boot is different from that in Linux.
> Thus, DT files imported from Linux do not work for U-Boot.
>
> In U-Boot driver model, each node is bound to a dedicated driver
> that is the most compatible to it.  This design gets along with the
> concept of DT, and the syscon in Linux originally worked like that.
>
> However, Linux commit bdb0066df96e ("mfd: syscon: Decouple syscon
> interface from platform devices") changed the behavior because it is
> useful to let a device bind to another driver, but still works as a
> syscon provider.
>
> That change had happened before U-Boot initially supported the syscon
> driver by commit 6f98b7504f70 ("dm: Add support for generic system
> controllers (syscon)").  So, the U-Boot's syscon works differently
> from the beginning.  I'd say this is mis-implementation given that
> DT is not oriented to a particular project, but Linux is the canon
> of DT in practice.
>
> The problem typically arises in the combination of "syscon" and
> "simple-mfd" compatibles.
>
> In Linux, they are orthogonal, i.e., the order between "syscon" and
> "simple-mfd" does not matter at all.
>
> Assume the following compatible.
>
>    compatible = "foo,bar-syscon", "syscon", "simple-mfd";
>
> In U-Boot, this device node is bound to the syscon driver
> (driver/core/syscon-uclass.c) since the "syscon" is found to be the
> most compatible.  Then, syscon_get_regmap() succeeds.
>
> However,
>
>    compatible = "foo,bar-syscon", "simple-mfd", "syscon";
>
> does not work because this node is bound to the simple-bus driver
> (drivers/core/simple-bus.c) in the favor of "simple-mfd" compatible.
> The compatible string "syscon" is just dismissed.
>
> Moreover,
>
>    compatible = "foo,bar-syscon", "syscon";
>
> works like the first case because the syscon driver populates the
> child devices.  This is wrong because populating children is the job
> of "simple-mfd" (or "simple-bus").
>
> This commit ports syscon_node_to_regmap() from Linux.  This API
> does not require the given node to be bound to a driver in any way.
>
> Reported-by: Kunihiko Hayashi <hayashi.kunihiko@socionext.com>
> Signed-off-by: Masahiro Yamada <yamada.masahiro@socionext.com>
> ---
>
>  drivers/core/syscon-uclass.c | 63
++++++++++++++++++++++++++++++++++++++++++++
>  include/syscon.h             |  8 ++++++
>  2 files changed, 71 insertions(+)

I was not aware of this change in how Linux worked, but it makes sense.

Please can you add a test for this?

Regards,
Simon
Masahiro Yamada April 23, 2018, 4:58 a.m. UTC | #2
Hi Simon,


2018-04-23 5:11 GMT+09:00 Simon Glass <sjg@chromium.org>:
> Hi Masahiro,
>
> On 17 April 2018 at 20:38, Masahiro Yamada <yamada.masahiro@socionext.com>
> wrote:
>> The syscon implementation in U-Boot is different from that in Linux.
>> Thus, DT files imported from Linux do not work for U-Boot.
>>
>> In U-Boot driver model, each node is bound to a dedicated driver
>> that is the most compatible to it.  This design gets along with the
>> concept of DT, and the syscon in Linux originally worked like that.
>>
>> However, Linux commit bdb0066df96e ("mfd: syscon: Decouple syscon
>> interface from platform devices") changed the behavior because it is
>> useful to let a device bind to another driver, but still works as a
>> syscon provider.
>>
>> That change had happened before U-Boot initially supported the syscon
>> driver by commit 6f98b7504f70 ("dm: Add support for generic system
>> controllers (syscon)").  So, the U-Boot's syscon works differently
>> from the beginning.  I'd say this is mis-implementation given that
>> DT is not oriented to a particular project, but Linux is the canon
>> of DT in practice.
>>
>> The problem typically arises in the combination of "syscon" and
>> "simple-mfd" compatibles.
>>
>> In Linux, they are orthogonal, i.e., the order between "syscon" and
>> "simple-mfd" does not matter at all.
>>
>> Assume the following compatible.
>>
>>    compatible = "foo,bar-syscon", "syscon", "simple-mfd";
>>
>> In U-Boot, this device node is bound to the syscon driver
>> (driver/core/syscon-uclass.c) since the "syscon" is found to be the
>> most compatible.  Then, syscon_get_regmap() succeeds.
>>
>> However,
>>
>>    compatible = "foo,bar-syscon", "simple-mfd", "syscon";
>>
>> does not work because this node is bound to the simple-bus driver
>> (drivers/core/simple-bus.c) in the favor of "simple-mfd" compatible.
>> The compatible string "syscon" is just dismissed.
>>
>> Moreover,
>>
>>    compatible = "foo,bar-syscon", "syscon";
>>
>> works like the first case because the syscon driver populates the
>> child devices.  This is wrong because populating children is the job
>> of "simple-mfd" (or "simple-bus").
>>
>> This commit ports syscon_node_to_regmap() from Linux.  This API
>> does not require the given node to be bound to a driver in any way.
>>
>> Reported-by: Kunihiko Hayashi <hayashi.kunihiko@socionext.com>
>> Signed-off-by: Masahiro Yamada <yamada.masahiro@socionext.com>
>> ---
>>
>>  drivers/core/syscon-uclass.c | 63
> ++++++++++++++++++++++++++++++++++++++++++++
>>  include/syscon.h             |  8 ++++++
>>  2 files changed, 71 insertions(+)
>
> I was not aware of this change in how Linux worked, but it makes sense.
>
> Please can you add a test for this?


I do not believe a missing test would block this patch,
but I volunteered to add it (as a separate patch).

http://patchwork.ozlabs.org/patch/902733/
Simon Glass April 25, 2018, 5:01 a.m. UTC | #3
On 22 April 2018 at 22:58, Masahiro Yamada <yamada.masahiro@socionext.com>
wrote:
> Hi Simon,
>
>
> 2018-04-23 5:11 GMT+09:00 Simon Glass <sjg@chromium.org>:
>> Hi Masahiro,
>>
>> On 17 April 2018 at 20:38, Masahiro Yamada <yamada.masahiro@socionext.com
>
>> wrote:
>>> The syscon implementation in U-Boot is different from that in Linux.
>>> Thus, DT files imported from Linux do not work for U-Boot.
>>>
>>> In U-Boot driver model, each node is bound to a dedicated driver
>>> that is the most compatible to it.  This design gets along with the
>>> concept of DT, and the syscon in Linux originally worked like that.
>>>
>>> However, Linux commit bdb0066df96e ("mfd: syscon: Decouple syscon
>>> interface from platform devices") changed the behavior because it is
>>> useful to let a device bind to another driver, but still works as a
>>> syscon provider.
>>>
>>> That change had happened before U-Boot initially supported the syscon
>>> driver by commit 6f98b7504f70 ("dm: Add support for generic system
>>> controllers (syscon)").  So, the U-Boot's syscon works differently
>>> from the beginning.  I'd say this is mis-implementation given that
>>> DT is not oriented to a particular project, but Linux is the canon
>>> of DT in practice.
>>>
>>> The problem typically arises in the combination of "syscon" and
>>> "simple-mfd" compatibles.
>>>
>>> In Linux, they are orthogonal, i.e., the order between "syscon" and
>>> "simple-mfd" does not matter at all.
>>>
>>> Assume the following compatible.
>>>
>>>    compatible = "foo,bar-syscon", "syscon", "simple-mfd";
>>>
>>> In U-Boot, this device node is bound to the syscon driver
>>> (driver/core/syscon-uclass.c) since the "syscon" is found to be the
>>> most compatible.  Then, syscon_get_regmap() succeeds.
>>>
>>> However,
>>>
>>>    compatible = "foo,bar-syscon", "simple-mfd", "syscon";
>>>
>>> does not work because this node is bound to the simple-bus driver
>>> (drivers/core/simple-bus.c) in the favor of "simple-mfd" compatible.
>>> The compatible string "syscon" is just dismissed.
>>>
>>> Moreover,
>>>
>>>    compatible = "foo,bar-syscon", "syscon";
>>>
>>> works like the first case because the syscon driver populates the
>>> child devices.  This is wrong because populating children is the job
>>> of "simple-mfd" (or "simple-bus").
>>>
>>> This commit ports syscon_node_to_regmap() from Linux.  This API
>>> does not require the given node to be bound to a driver in any way.
>>>
>>> Reported-by: Kunihiko Hayashi <hayashi.kunihiko@socionext.com>
>>> Signed-off-by: Masahiro Yamada <yamada.masahiro@socionext.com>
>>> ---
>>>
>>>  drivers/core/syscon-uclass.c | 63
>> ++++++++++++++++++++++++++++++++++++++++++++
>>>  include/syscon.h             |  8 ++++++
>>>  2 files changed, 71 insertions(+)
>>
>> I was not aware of this change in how Linux worked, but it makes sense.
>>
>> Please can you add a test for this?
>
>
> I do not believe a missing test would block this patch,
> but I volunteered to add it (as a separate patch).
>
> http://patchwork.ozlabs.org/patch/902733/

Thanks!
diff mbox series

Patch

diff --git a/drivers/core/syscon-uclass.c b/drivers/core/syscon-uclass.c
index c99409b..bd2f681 100644
--- a/drivers/core/syscon-uclass.c
+++ b/drivers/core/syscon-uclass.c
@@ -15,6 +15,14 @@ 
 #include <dm/root.h>
 #include <linux/err.h>
 
+/*
+ * Caution:
+ * This API requires the given node has already been bound to "syscon".
+ *    compatible = "syscon", "simple-mfd";
+ * works, but
+ *    compatible = "simple-mfd", "syscon";
+ * does not.  This behavior is different from Linux.
+ */
 struct regmap *syscon_get_regmap(struct udevice *dev)
 {
 	struct syscon_uc_info *priv;
@@ -109,3 +117,58 @@  U_BOOT_DRIVER(generic_syscon) = {
 #endif
 	.of_match = generic_syscon_ids,
 };
+
+/*
+ * Linux-compatible syscon-to-regmap
+ * The syscon node can be bound to another driver, but still works
+ * as a syscon provider.
+ */
+static LIST_HEAD(syscon_list);
+
+struct syscon {
+	ofnode node;
+	struct regmap *regmap;
+	struct list_head list;
+};
+
+static struct syscon *of_syscon_register(ofnode node)
+{
+	struct syscon *syscon;
+	int ret;
+
+	if (!ofnode_device_is_compatible(node, "syscon"))
+		return ERR_PTR(-EINVAL);
+
+	syscon = malloc(sizeof(*syscon));
+	if (!syscon)
+		return ERR_PTR(-ENOMEM);
+
+	ret = regmap_init_mem(node, &syscon->regmap);
+	if (ret) {
+		free(syscon);
+		return ERR_PTR(ret);
+	}
+
+	list_add_tail(&syscon->list, &syscon_list);
+
+	return syscon;
+}
+
+struct regmap *syscon_node_to_regmap(ofnode node)
+{
+	struct syscon *entry, *syscon = NULL;
+
+	list_for_each_entry(entry, &syscon_list, list)
+		if (ofnode_equal(entry->node, node)) {
+			syscon = entry;
+			break;
+		}
+
+	if (!syscon)
+		syscon = of_syscon_register(node);
+
+	if (IS_ERR(syscon))
+		return ERR_CAST(syscon);
+
+	return syscon->regmap;
+}
diff --git a/include/syscon.h b/include/syscon.h
index 5d52b1c..a019345 100644
--- a/include/syscon.h
+++ b/include/syscon.h
@@ -8,6 +8,7 @@ 
 #ifndef __SYSCON_H
 #define __SYSCON_H
 
+#include <dm/ofnode.h>
 #include <fdtdec.h>
 
 /**
@@ -82,4 +83,11 @@  struct regmap *syscon_get_regmap_by_driver_data(ulong driver_data);
  */
 void *syscon_get_first_range(ulong driver_data);
 
+/**
+ * syscon_node_to_regmap - Linux-compat syscon lookup
+ *
+ * @node:		Device node of syscon
+ */
+struct regmap *syscon_node_to_regmap(ofnode node);
+
 #endif