Blame view
drivers/mfd/syscon.c
6.04 KB
87d687301 mfd: Add syscon d... |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
/* * System Control Driver * * Copyright (C) 2012 Freescale Semiconductor, Inc. * Copyright (C) 2012 Linaro Ltd. * * Author: Dong Aisheng <dong.aisheng@linaro.org> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. */ #include <linux/err.h> #include <linux/io.h> #include <linux/module.h> |
bdb0066df mfd: syscon: Deco... |
18 |
#include <linux/list.h> |
87d687301 mfd: Add syscon d... |
19 20 21 |
#include <linux/of.h> #include <linux/of_address.h> #include <linux/of_platform.h> |
29f9b6cf7 mfd: syscon: Add ... |
22 |
#include <linux/platform_data/syscon.h> |
87d687301 mfd: Add syscon d... |
23 24 |
#include <linux/platform_device.h> #include <linux/regmap.h> |
75177deee mfd: syscon: Fix ... |
25 |
#include <linux/mfd/syscon.h> |
bdb0066df mfd: syscon: Deco... |
26 |
#include <linux/slab.h> |
87d687301 mfd: Add syscon d... |
27 28 |
static struct platform_driver syscon_driver; |
bdb0066df mfd: syscon: Deco... |
29 30 |
static DEFINE_SPINLOCK(syscon_list_slock); static LIST_HEAD(syscon_list); |
87d687301 mfd: Add syscon d... |
31 |
struct syscon { |
bdb0066df mfd: syscon: Deco... |
32 |
struct device_node *np; |
87d687301 mfd: Add syscon d... |
33 |
struct regmap *regmap; |
bdb0066df mfd: syscon: Deco... |
34 35 |
struct list_head list; }; |
c131045d5 mfd: syscon: Make... |
36 |
static const struct regmap_config syscon_regmap_config = { |
bdb0066df mfd: syscon: Deco... |
37 38 39 |
.reg_bits = 32, .val_bits = 32, .reg_stride = 4, |
87d687301 mfd: Add syscon d... |
40 |
}; |
bdb0066df mfd: syscon: Deco... |
41 |
static struct syscon *of_syscon_register(struct device_node *np) |
87d687301 mfd: Add syscon d... |
42 |
{ |
bdb0066df mfd: syscon: Deco... |
43 44 45 |
struct syscon *syscon; struct regmap *regmap; void __iomem *base; |
db2fb60cd mfd: syscon: Add ... |
46 |
u32 reg_io_width; |
bdb0066df mfd: syscon: Deco... |
47 48 |
int ret; struct regmap_config syscon_config = syscon_regmap_config; |
ca668f0ed mfd: syscon: Set ... |
49 |
struct resource res; |
bdb0066df mfd: syscon: Deco... |
50 51 52 53 54 55 56 |
if (!of_device_is_compatible(np, "syscon")) return ERR_PTR(-EINVAL); syscon = kzalloc(sizeof(*syscon), GFP_KERNEL); if (!syscon) return ERR_PTR(-ENOMEM); |
ca668f0ed mfd: syscon: Set ... |
57 58 59 60 61 62 |
if (of_address_to_resource(np, 0, &res)) { ret = -ENOMEM; goto err_map; } base = ioremap(res.start, resource_size(&res)); |
bdb0066df mfd: syscon: Deco... |
63 64 65 66 67 68 69 70 |
if (!base) { ret = -ENOMEM; goto err_map; } /* Parse the device's DT node for an endianness specification */ if (of_property_read_bool(np, "big-endian")) syscon_config.val_format_endian = REGMAP_ENDIAN_BIG; |
d29ccdb3f mfd: syscon: Supp... |
71 |
else if (of_property_read_bool(np, "little-endian")) |
bdb0066df mfd: syscon: Deco... |
72 |
syscon_config.val_format_endian = REGMAP_ENDIAN_LITTLE; |
d29ccdb3f mfd: syscon: Supp... |
73 74 |
else if (of_property_read_bool(np, "native-endian")) syscon_config.val_format_endian = REGMAP_ENDIAN_NATIVE; |
bdb0066df mfd: syscon: Deco... |
75 |
|
db2fb60cd mfd: syscon: Add ... |
76 77 78 79 80 81 82 83 84 85 86 |
/* * search for reg-io-width property in DT. If it is not provided, * default to 4 bytes. regmap_init_mmio will return an error if values * are invalid so there is no need to check them here. */ ret = of_property_read_u32(np, "reg-io-width", ®_io_width); if (ret) reg_io_width = 4; syscon_config.reg_stride = reg_io_width; syscon_config.val_bits = reg_io_width * 8; |
ca668f0ed mfd: syscon: Set ... |
87 |
syscon_config.max_register = resource_size(&res) - reg_io_width; |
db2fb60cd mfd: syscon: Add ... |
88 |
|
bdb0066df mfd: syscon: Deco... |
89 90 91 92 93 94 95 96 97 98 99 100 101 102 |
regmap = regmap_init_mmio(NULL, base, &syscon_config); if (IS_ERR(regmap)) { pr_err("regmap init failed "); ret = PTR_ERR(regmap); goto err_regmap; } syscon->regmap = regmap; syscon->np = np; spin_lock(&syscon_list_slock); list_add_tail(&syscon->list, &syscon_list); spin_unlock(&syscon_list_slock); |
87d687301 mfd: Add syscon d... |
103 |
|
bdb0066df mfd: syscon: Deco... |
104 105 106 107 108 109 110 |
return syscon; err_regmap: iounmap(base); err_map: kfree(syscon); return ERR_PTR(ret); |
87d687301 mfd: Add syscon d... |
111 112 113 114 |
} struct regmap *syscon_node_to_regmap(struct device_node *np) { |
bdb0066df mfd: syscon: Deco... |
115 |
struct syscon *entry, *syscon = NULL; |
87d687301 mfd: Add syscon d... |
116 |
|
bdb0066df mfd: syscon: Deco... |
117 |
spin_lock(&syscon_list_slock); |
87d687301 mfd: Add syscon d... |
118 |
|
bdb0066df mfd: syscon: Deco... |
119 120 121 122 123 124 125 126 127 128 129 130 131 |
list_for_each_entry(entry, &syscon_list, list) if (entry->np == np) { syscon = entry; break; } spin_unlock(&syscon_list_slock); if (!syscon) syscon = of_syscon_register(np); if (IS_ERR(syscon)) return ERR_CAST(syscon); |
87d687301 mfd: Add syscon d... |
132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 |
return syscon->regmap; } EXPORT_SYMBOL_GPL(syscon_node_to_regmap); struct regmap *syscon_regmap_lookup_by_compatible(const char *s) { struct device_node *syscon_np; struct regmap *regmap; syscon_np = of_find_compatible_node(NULL, NULL, s); if (!syscon_np) return ERR_PTR(-ENODEV); regmap = syscon_node_to_regmap(syscon_np); of_node_put(syscon_np); return regmap; } EXPORT_SYMBOL_GPL(syscon_regmap_lookup_by_compatible); |
5ab3a89a7 mfd: syscon: Add ... |
152 153 |
static int syscon_match_pdevname(struct device *dev, void *data) { |
5ab3a89a7 mfd: syscon: Add ... |
154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 |
return !strcmp(dev_name(dev), (const char *)data); } struct regmap *syscon_regmap_lookup_by_pdevname(const char *s) { struct device *dev; struct syscon *syscon; dev = driver_find_device(&syscon_driver.driver, NULL, (void *)s, syscon_match_pdevname); if (!dev) return ERR_PTR(-EPROBE_DEFER); syscon = dev_get_drvdata(dev); return syscon->regmap; } EXPORT_SYMBOL_GPL(syscon_regmap_lookup_by_pdevname); |
87d687301 mfd: Add syscon d... |
172 173 174 175 176 |
struct regmap *syscon_regmap_lookup_by_phandle(struct device_node *np, const char *property) { struct device_node *syscon_np; struct regmap *regmap; |
45330bb43 mfd: syscon: Allo... |
177 178 179 180 |
if (property) syscon_np = of_parse_phandle(np, property, 0); else syscon_np = np; |
87d687301 mfd: Add syscon d... |
181 182 183 184 185 186 187 188 189 |
if (!syscon_np) return ERR_PTR(-ENODEV); regmap = syscon_node_to_regmap(syscon_np); of_node_put(syscon_np); return regmap; } EXPORT_SYMBOL_GPL(syscon_regmap_lookup_by_phandle); |
f791be492 mfd: remove use o... |
190 |
static int syscon_probe(struct platform_device *pdev) |
87d687301 mfd: Add syscon d... |
191 192 |
{ struct device *dev = &pdev->dev; |
29f9b6cf7 mfd: syscon: Add ... |
193 |
struct syscon_platform_data *pdata = dev_get_platdata(dev); |
87d687301 mfd: Add syscon d... |
194 |
struct syscon *syscon; |
c131045d5 mfd: syscon: Make... |
195 |
struct regmap_config syscon_config = syscon_regmap_config; |
5ab3a89a7 mfd: syscon: Add ... |
196 |
struct resource *res; |
f10111cc8 mfd: syscon: Remo... |
197 |
void __iomem *base; |
87d687301 mfd: Add syscon d... |
198 |
|
5ab3a89a7 mfd: syscon: Add ... |
199 |
syscon = devm_kzalloc(dev, sizeof(*syscon), GFP_KERNEL); |
87d687301 mfd: Add syscon d... |
200 201 |
if (!syscon) return -ENOMEM; |
5ab3a89a7 mfd: syscon: Add ... |
202 203 204 |
res = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (!res) return -ENOENT; |
87d687301 mfd: Add syscon d... |
205 |
|
f10111cc8 mfd: syscon: Remo... |
206 207 |
base = devm_ioremap(dev, res->start, resource_size(res)); if (!base) |
5ab3a89a7 mfd: syscon: Add ... |
208 |
return -ENOMEM; |
87d687301 mfd: Add syscon d... |
209 |
|
c131045d5 mfd: syscon: Make... |
210 |
syscon_config.max_register = res->end - res->start - 3; |
29f9b6cf7 mfd: syscon: Add ... |
211 |
if (pdata) |
c131045d5 mfd: syscon: Make... |
212 213 |
syscon_config.name = pdata->label; syscon->regmap = devm_regmap_init_mmio(dev, base, &syscon_config); |
87d687301 mfd: Add syscon d... |
214 215 216 217 218 |
if (IS_ERR(syscon->regmap)) { dev_err(dev, "regmap init failed "); return PTR_ERR(syscon->regmap); } |
87d687301 mfd: Add syscon d... |
219 |
platform_set_drvdata(pdev, syscon); |
38d8974e3 mfd: syscon: Move... |
220 221 |
dev_dbg(dev, "regmap %pR registered ", res); |
87d687301 mfd: Add syscon d... |
222 223 224 |
return 0; } |
5ab3a89a7 mfd: syscon: Add ... |
225 226 227 228 |
static const struct platform_device_id syscon_ids[] = { { "syscon", }, { } }; |
87d687301 mfd: Add syscon d... |
229 230 231 232 |
static struct platform_driver syscon_driver = { .driver = { .name = "syscon", |
87d687301 mfd: Add syscon d... |
233 234 |
}, .probe = syscon_probe, |
5ab3a89a7 mfd: syscon: Add ... |
235 |
.id_table = syscon_ids, |
87d687301 mfd: Add syscon d... |
236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 |
}; static int __init syscon_init(void) { return platform_driver_register(&syscon_driver); } postcore_initcall(syscon_init); static void __exit syscon_exit(void) { platform_driver_unregister(&syscon_driver); } module_exit(syscon_exit); MODULE_AUTHOR("Dong Aisheng <dong.aisheng@linaro.org>"); MODULE_DESCRIPTION("System Control driver"); MODULE_LICENSE("GPL v2"); |