#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;
#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;
+}