aboutsummaryrefslogtreecommitdiffstats
path: root/drivers
diff options
context:
space:
mode:
authorDong Aisheng <dong.aisheng@linaro.org>2012-09-04 22:57:13 -0400
committerSamuel Ortiz <sameo@linux.intel.com>2012-09-17 09:24:14 -0400
commit87d687301f380729ec320619f100f3ba39f3693d (patch)
tree3f6f274a01552da3b5f7af4dc1eaaad04a11c1b4 /drivers
parenta435ae1d51e2f18414f2a87219fdbe068231e692 (diff)
mfd: Add syscon driver based on regmap
Add regmap based syscon driver. This is usually used for access misc bits in registers which does not belong to a specific module, for example, IMX IOMUXC GPR and ANATOP. With this driver, client can use generic regmap API to access registers which are registered into syscon. Reviewed-by: Mark Brown <broonie@opensource.wolfsonmicro.com> Acked-by: Stephen Warren <swarren@wwwdotorg.org> Signed-off-by: Dong Aisheng <dong.aisheng@linaro.org> Signed-off-by: Samuel Ortiz <sameo@linux.intel.com>
Diffstat (limited to 'drivers')
-rw-r--r--drivers/mfd/Kconfig8
-rw-r--r--drivers/mfd/Makefile1
-rw-r--r--drivers/mfd/syscon.c176
3 files changed, 185 insertions, 0 deletions
diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
index 856ec00ab787..909d84b1f5c1 100644
--- a/drivers/mfd/Kconfig
+++ b/drivers/mfd/Kconfig
@@ -1004,6 +1004,14 @@ config MFD_ANATOP
1004 MFD controller. This controller embeds regulator and 1004 MFD controller. This controller embeds regulator and
1005 thermal devices for Freescale i.MX platforms. 1005 thermal devices for Freescale i.MX platforms.
1006 1006
1007config MFD_SYSCON
1008 bool "System Controller Register R/W Based on Regmap"
1009 depends on OF
1010 select REGMAP_MMIO
1011 help
1012 Select this option to enable accessing system control registers
1013 via regmap.
1014
1007config MFD_PALMAS 1015config MFD_PALMAS
1008 bool "Support for the TI Palmas series chips" 1016 bool "Support for the TI Palmas series chips"
1009 select MFD_CORE 1017 select MFD_CORE
diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile
index 2a4108d98442..07b66aff30b4 100644
--- a/drivers/mfd/Makefile
+++ b/drivers/mfd/Makefile
@@ -132,4 +132,5 @@ obj-$(CONFIG_MFD_PALMAS) += palmas.o
132obj-$(CONFIG_MFD_RC5T583) += rc5t583.o rc5t583-irq.o 132obj-$(CONFIG_MFD_RC5T583) += rc5t583.o rc5t583-irq.o
133obj-$(CONFIG_MFD_SEC_CORE) += sec-core.o sec-irq.o 133obj-$(CONFIG_MFD_SEC_CORE) += sec-core.o sec-irq.o
134obj-$(CONFIG_MFD_ANATOP) += anatop-mfd.o 134obj-$(CONFIG_MFD_ANATOP) += anatop-mfd.o
135obj-$(CONFIG_MFD_SYSCON) += syscon.o
135obj-$(CONFIG_MFD_LM3533) += lm3533-core.o lm3533-ctrlbank.o 136obj-$(CONFIG_MFD_LM3533) += lm3533-core.o lm3533-ctrlbank.o
diff --git a/drivers/mfd/syscon.c b/drivers/mfd/syscon.c
new file mode 100644
index 000000000000..65fe609026cc
--- /dev/null
+++ b/drivers/mfd/syscon.c
@@ -0,0 +1,176 @@
1/*
2 * System Control Driver
3 *
4 * Copyright (C) 2012 Freescale Semiconductor, Inc.
5 * Copyright (C) 2012 Linaro Ltd.
6 *
7 * Author: Dong Aisheng <dong.aisheng@linaro.org>
8 *
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
13 */
14
15#include <linux/err.h>
16#include <linux/io.h>
17#include <linux/module.h>
18#include <linux/of.h>
19#include <linux/of_address.h>
20#include <linux/of_platform.h>
21#include <linux/platform_device.h>
22#include <linux/regmap.h>
23
24static struct platform_driver syscon_driver;
25
26struct syscon {
27 struct device *dev;
28 void __iomem *base;
29 struct regmap *regmap;
30};
31
32static int syscon_match(struct device *dev, void *data)
33{
34 struct syscon *syscon = dev_get_drvdata(dev);
35 struct device_node *dn = data;
36
37 return (syscon->dev->of_node == dn) ? 1 : 0;
38}
39
40struct regmap *syscon_node_to_regmap(struct device_node *np)
41{
42 struct syscon *syscon;
43 struct device *dev;
44
45 dev = driver_find_device(&syscon_driver.driver, NULL, np,
46 syscon_match);
47 if (!dev)
48 return ERR_PTR(-EPROBE_DEFER);
49
50 syscon = dev_get_drvdata(dev);
51
52 return syscon->regmap;
53}
54EXPORT_SYMBOL_GPL(syscon_node_to_regmap);
55
56struct regmap *syscon_regmap_lookup_by_compatible(const char *s)
57{
58 struct device_node *syscon_np;
59 struct regmap *regmap;
60
61 syscon_np = of_find_compatible_node(NULL, NULL, s);
62 if (!syscon_np)
63 return ERR_PTR(-ENODEV);
64
65 regmap = syscon_node_to_regmap(syscon_np);
66 of_node_put(syscon_np);
67
68 return regmap;
69}
70EXPORT_SYMBOL_GPL(syscon_regmap_lookup_by_compatible);
71
72struct regmap *syscon_regmap_lookup_by_phandle(struct device_node *np,
73 const char *property)
74{
75 struct device_node *syscon_np;
76 struct regmap *regmap;
77
78 syscon_np = of_parse_phandle(np, property, 0);
79 if (!syscon_np)
80 return ERR_PTR(-ENODEV);
81
82 regmap = syscon_node_to_regmap(syscon_np);
83 of_node_put(syscon_np);
84
85 return regmap;
86}
87EXPORT_SYMBOL_GPL(syscon_regmap_lookup_by_phandle);
88
89static const struct of_device_id of_syscon_match[] = {
90 { .compatible = "syscon", },
91 { },
92};
93
94static struct regmap_config syscon_regmap_config = {
95 .reg_bits = 32,
96 .val_bits = 32,
97 .reg_stride = 4,
98};
99
100static int __devinit syscon_probe(struct platform_device *pdev)
101{
102 struct device *dev = &pdev->dev;
103 struct device_node *np = dev->of_node;
104 struct syscon *syscon;
105 struct resource res;
106 int ret;
107
108 if (!np)
109 return -ENOENT;
110
111 syscon = devm_kzalloc(dev, sizeof(struct syscon),
112 GFP_KERNEL);
113 if (!syscon)
114 return -ENOMEM;
115
116 syscon->base = of_iomap(np, 0);
117 if (!syscon->base)
118 return -EADDRNOTAVAIL;
119
120 ret = of_address_to_resource(np, 0, &res);
121 if (ret)
122 return ret;
123
124 syscon_regmap_config.max_register = res.end - res.start - 3;
125 syscon->regmap = devm_regmap_init_mmio(dev, syscon->base,
126 &syscon_regmap_config);
127 if (IS_ERR(syscon->regmap)) {
128 dev_err(dev, "regmap init failed\n");
129 return PTR_ERR(syscon->regmap);
130 }
131
132 syscon->dev = dev;
133 platform_set_drvdata(pdev, syscon);
134
135 dev_info(dev, "syscon regmap start 0x%x end 0x%x registered\n",
136 res.start, res.end);
137
138 return 0;
139}
140
141static int __devexit syscon_remove(struct platform_device *pdev)
142{
143 struct syscon *syscon;
144
145 syscon = platform_get_drvdata(pdev);
146 iounmap(syscon->base);
147 platform_set_drvdata(pdev, NULL);
148
149 return 0;
150}
151
152static struct platform_driver syscon_driver = {
153 .driver = {
154 .name = "syscon",
155 .owner = THIS_MODULE,
156 .of_match_table = of_syscon_match,
157 },
158 .probe = syscon_probe,
159 .remove = __devexit_p(syscon_remove),
160};
161
162static int __init syscon_init(void)
163{
164 return platform_driver_register(&syscon_driver);
165}
166postcore_initcall(syscon_init);
167
168static void __exit syscon_exit(void)
169{
170 platform_driver_unregister(&syscon_driver);
171}
172module_exit(syscon_exit);
173
174MODULE_AUTHOR("Dong Aisheng <dong.aisheng@linaro.org>");
175MODULE_DESCRIPTION("System Control driver");
176MODULE_LICENSE("GPL v2");