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