X-Git-Url: https://git.sur5r.net/?a=blobdiff_plain;f=drivers%2Fcore%2Fsyscon-uclass.c;h=303e166a69c1b769f5006712c06dc3d855e1c2c4;hb=9f9ce3c369b7fbcc47496331ff28fad80302a42c;hp=e03f46af5739d7156ad0270a438e4744496e535f;hpb=532f2435cfe94e54e01ba68572daa853d7752afa;p=u-boot diff --git a/drivers/core/syscon-uclass.c b/drivers/core/syscon-uclass.c index e03f46af57..303e166a69 100644 --- a/drivers/core/syscon-uclass.c +++ b/drivers/core/syscon-uclass.c @@ -1,8 +1,7 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * Copyright (C) 2015 Google, Inc * Written by Simon Glass - * - * SPDX-License-Identifier: GPL-2.0+ */ #include @@ -15,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; @@ -29,7 +37,20 @@ static int syscon_pre_probe(struct udevice *dev) { struct syscon_uc_info *priv = dev_get_uclass_priv(dev); - return regmap_init_mem(dev, &priv->regmap); + /* + * With OF_PLATDATA we really have no way of knowing the format of + * the device-specific platform data. So we assume that it starts with + * a 'reg' member, and this holds a single address and size. Drivers + * using OF_PLATDATA will need to ensure that this is true. + */ +#if CONFIG_IS_ENABLED(OF_PLATDATA) + struct syscon_base_platdata *plat = dev_get_platdata(dev); + + return regmap_init_mem_platdata(dev, plat->reg, ARRAY_SIZE(plat->reg), + &priv->regmap); +#else + return regmap_init_mem(dev_ofnode(dev), &priv->regmap); +#endif } int syscon_get_by_driver_data(ulong driver_data, struct udevice **devp) @@ -82,3 +103,72 @@ UCLASS_DRIVER(syscon) = { .per_device_auto_alloc_size = sizeof(struct syscon_uc_info), .pre_probe = syscon_pre_probe, }; + +static const struct udevice_id generic_syscon_ids[] = { + { .compatible = "syscon" }, + { } +}; + +U_BOOT_DRIVER(generic_syscon) = { + .name = "syscon", + .id = UCLASS_SYSCON, +#if !CONFIG_IS_ENABLED(OF_PLATDATA) + .bind = dm_scan_fdt_dev, +#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; +}