Skip to content

Commit bdb0066

Browse files
pankajkdubeyLee Jones
authored and
Lee Jones
committed
mfd: syscon: Decouple syscon interface from platform devices
Currently a syscon entity can be only registered directly through a platform device that binds to a dedicated syscon driver. However in certain use cases it is desirable to make a device used with another driver a syscon interface provider. For example, certain SoCs (e.g. Exynos) contain system controller blocks which perform various functions such as power domain control, CPU power management, low power mode control, but in addition contain certain IP integration glue, such as various signal masks, coprocessor power control, etc. In such case, there is a need to have a dedicated driver for such system controller but also share registers with other drivers. The latter is where the syscon interface is helpful. In case of DT based platforms, this patch decouples syscon object from syscon platform driver, and allows to create syscon objects first time when it is required by calling of syscon_regmap_lookup_by APIs and keep a list of such syscon objects along with syscon provider device_nodes and regmap handles. For non-DT based platforms, this patch keeps syscon platform driver structure so that syscon can be probed and such non-DT based drivers can use syscon_regmap_lookup_by_pdev API and access regmap handles. Once all users of "syscon_regmap_lookup_by_pdev" migrated to DT based, we can completely remove platform driver of syscon, and keep only helper functions to get regmap handles. Suggested-by: Arnd Bergmann <[email protected]> Suggested-by: Tomasz Figa <[email protected]> Tested-by: Vivek Gautam <[email protected]> Tested-by: Javier Martinez Canillas <[email protected]> Signed-off-by: Pankaj Dubey <[email protected]> Reviewed-by: Arnd Bergmann <[email protected]> Tested-by: Heiko Stuebner <[email protected]> Reviewed-by: Heiko Stuebner <[email protected]> Signed-off-by: Lee Jones <[email protected]>
1 parent ee828d0 commit bdb0066

File tree

1 file changed

+74
-22
lines changed

1 file changed

+74
-22
lines changed

drivers/mfd/syscon.c

Lines changed: 74 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -15,38 +15,102 @@
1515
#include <linux/err.h>
1616
#include <linux/io.h>
1717
#include <linux/module.h>
18+
#include <linux/list.h>
1819
#include <linux/of.h>
1920
#include <linux/of_address.h>
2021
#include <linux/of_platform.h>
2122
#include <linux/platform_data/syscon.h>
2223
#include <linux/platform_device.h>
2324
#include <linux/regmap.h>
2425
#include <linux/mfd/syscon.h>
26+
#include <linux/slab.h>
2527

2628
static struct platform_driver syscon_driver;
2729

30+
static DEFINE_SPINLOCK(syscon_list_slock);
31+
static LIST_HEAD(syscon_list);
32+
2833
struct syscon {
34+
struct device_node *np;
2935
struct regmap *regmap;
36+
struct list_head list;
37+
};
38+
39+
static struct regmap_config syscon_regmap_config = {
40+
.reg_bits = 32,
41+
.val_bits = 32,
42+
.reg_stride = 4,
3043
};
3144

32-
static int syscon_match_node(struct device *dev, void *data)
45+
static struct syscon *of_syscon_register(struct device_node *np)
3346
{
34-
struct device_node *dn = data;
47+
struct syscon *syscon;
48+
struct regmap *regmap;
49+
void __iomem *base;
50+
int ret;
51+
struct regmap_config syscon_config = syscon_regmap_config;
52+
53+
if (!of_device_is_compatible(np, "syscon"))
54+
return ERR_PTR(-EINVAL);
55+
56+
syscon = kzalloc(sizeof(*syscon), GFP_KERNEL);
57+
if (!syscon)
58+
return ERR_PTR(-ENOMEM);
59+
60+
base = of_iomap(np, 0);
61+
if (!base) {
62+
ret = -ENOMEM;
63+
goto err_map;
64+
}
65+
66+
/* Parse the device's DT node for an endianness specification */
67+
if (of_property_read_bool(np, "big-endian"))
68+
syscon_config.val_format_endian = REGMAP_ENDIAN_BIG;
69+
else if (of_property_read_bool(np, "little-endian"))
70+
syscon_config.val_format_endian = REGMAP_ENDIAN_LITTLE;
71+
72+
regmap = regmap_init_mmio(NULL, base, &syscon_config);
73+
if (IS_ERR(regmap)) {
74+
pr_err("regmap init failed\n");
75+
ret = PTR_ERR(regmap);
76+
goto err_regmap;
77+
}
78+
79+
syscon->regmap = regmap;
80+
syscon->np = np;
81+
82+
spin_lock(&syscon_list_slock);
83+
list_add_tail(&syscon->list, &syscon_list);
84+
spin_unlock(&syscon_list_slock);
3585

36-
return (dev->of_node == dn) ? 1 : 0;
86+
return syscon;
87+
88+
err_regmap:
89+
iounmap(base);
90+
err_map:
91+
kfree(syscon);
92+
return ERR_PTR(ret);
3793
}
3894

3995
struct regmap *syscon_node_to_regmap(struct device_node *np)
4096
{
41-
struct syscon *syscon;
42-
struct device *dev;
97+
struct syscon *entry, *syscon = NULL;
4398

44-
dev = driver_find_device(&syscon_driver.driver, NULL, np,
45-
syscon_match_node);
46-
if (!dev)
47-
return ERR_PTR(-EPROBE_DEFER);
99+
spin_lock(&syscon_list_slock);
48100

49-
syscon = dev_get_drvdata(dev);
101+
list_for_each_entry(entry, &syscon_list, list)
102+
if (entry->np == np) {
103+
syscon = entry;
104+
break;
105+
}
106+
107+
spin_unlock(&syscon_list_slock);
108+
109+
if (!syscon)
110+
syscon = of_syscon_register(np);
111+
112+
if (IS_ERR(syscon))
113+
return ERR_CAST(syscon);
50114

51115
return syscon->regmap;
52116
}
@@ -110,17 +174,6 @@ struct regmap *syscon_regmap_lookup_by_phandle(struct device_node *np,
110174
}
111175
EXPORT_SYMBOL_GPL(syscon_regmap_lookup_by_phandle);
112176

113-
static const struct of_device_id of_syscon_match[] = {
114-
{ .compatible = "syscon", },
115-
{ },
116-
};
117-
118-
static struct regmap_config syscon_regmap_config = {
119-
.reg_bits = 32,
120-
.val_bits = 32,
121-
.reg_stride = 4,
122-
};
123-
124177
static int syscon_probe(struct platform_device *pdev)
125178
{
126179
struct device *dev = &pdev->dev;
@@ -167,7 +220,6 @@ static struct platform_driver syscon_driver = {
167220
.driver = {
168221
.name = "syscon",
169222
.owner = THIS_MODULE,
170-
.of_match_table = of_syscon_match,
171223
},
172224
.probe = syscon_probe,
173225
.id_table = syscon_ids,

0 commit comments

Comments
 (0)