diff options
Diffstat (limited to 'drivers')
| -rw-r--r-- | drivers/core/syscon-uclass.c | 64 |
1 files changed, 64 insertions, 0 deletions
diff --git a/drivers/core/syscon-uclass.c b/drivers/core/syscon-uclass.c index 62ba462158..303e166a69 100644 --- a/drivers/core/syscon-uclass.c +++ b/drivers/core/syscon-uclass.c @@ -14,6 +14,15 @@ #include <dm/root.h> #include <linux/err.h> +/* + * Caution: + * This API requires the given device has alerady been bound to syscon driver. + * For example, + * compatible = "syscon", "simple-mfd"; + * works, but + * compatible = "simple-mfd", "syscon"; + * does not. The behavior is different from Linux. + */ struct regmap *syscon_get_regmap(struct udevice *dev) { struct syscon_uc_info *priv; @@ -108,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; +} |
