diff --git a/drivers/core/syscon-uclass.c b/drivers/core/syscon-uclass.c index 62ba462..303e166 100644 --- a/drivers/core/syscon-uclass.c +++ b/drivers/core/syscon-uclass.c @@ -14,6 +14,15 @@ #include #include +/* + * 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; +} diff --git a/include/syscon.h b/include/syscon.h index d3261aa..2aa73e5 100644 --- a/include/syscon.h +++ b/include/syscon.h @@ -7,6 +7,7 @@ #ifndef __SYSCON_H #define __SYSCON_H +#include #include /** @@ -81,4 +82,11 @@ struct regmap *syscon_get_regmap_by_driver_data(ulong driver_data); */ void *syscon_get_first_range(ulong driver_data); +/** + * syscon_node_to_regmap - get regmap from syscon + * + * @node: Device node of syscon + */ +struct regmap *syscon_node_to_regmap(ofnode node); + #endif