.. | .. |
---|
| 1 | +// SPDX-License-Identifier: GPL-2.0-or-later |
---|
1 | 2 | /* |
---|
2 | 3 | * System Control Driver |
---|
3 | 4 | * |
---|
.. | .. |
---|
5 | 6 | * Copyright (C) 2012 Linaro Ltd. |
---|
6 | 7 | * |
---|
7 | 8 | * 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 | 9 | */ |
---|
14 | 10 | |
---|
15 | 11 | #include <linux/clk.h> |
---|
16 | 12 | #include <linux/err.h> |
---|
17 | 13 | #include <linux/hwspinlock.h> |
---|
18 | 14 | #include <linux/io.h> |
---|
19 | | -#include <linux/module.h> |
---|
| 15 | +#include <linux/init.h> |
---|
20 | 16 | #include <linux/list.h> |
---|
21 | 17 | #include <linux/of.h> |
---|
22 | 18 | #include <linux/of_address.h> |
---|
.. | .. |
---|
44 | 40 | .reg_stride = 4, |
---|
45 | 41 | }; |
---|
46 | 42 | |
---|
47 | | -static struct syscon *of_syscon_register(struct device_node *np) |
---|
| 43 | +static struct syscon *of_syscon_register(struct device_node *np, bool check_clk) |
---|
48 | 44 | { |
---|
49 | 45 | struct clk *clk; |
---|
50 | 46 | struct syscon *syscon; |
---|
.. | .. |
---|
54 | 50 | int ret; |
---|
55 | 51 | struct regmap_config syscon_config = syscon_regmap_config; |
---|
56 | 52 | struct resource res; |
---|
57 | | - |
---|
58 | | - if (!of_device_is_compatible(np, "syscon")) |
---|
59 | | - return ERR_PTR(-EINVAL); |
---|
60 | 53 | |
---|
61 | 54 | syscon = kzalloc(sizeof(*syscon), GFP_KERNEL); |
---|
62 | 55 | if (!syscon) |
---|
.. | .. |
---|
102 | 95 | break; |
---|
103 | 96 | default: |
---|
104 | 97 | pr_err("Failed to retrieve valid hwlock: %d\n", ret); |
---|
105 | | - /* fall-through */ |
---|
| 98 | + fallthrough; |
---|
106 | 99 | case -EPROBE_DEFER: |
---|
107 | 100 | goto err_regmap; |
---|
108 | 101 | } |
---|
109 | 102 | } |
---|
110 | 103 | |
---|
111 | | - syscon_config.name = of_node_full_name(np); |
---|
| 104 | + syscon_config.name = kasprintf(GFP_KERNEL, "%pOFn@%llx", np, |
---|
| 105 | + (u64)res.start); |
---|
112 | 106 | syscon_config.reg_stride = reg_io_width; |
---|
113 | 107 | syscon_config.val_bits = reg_io_width * 8; |
---|
114 | 108 | syscon_config.max_register = resource_size(&res) - reg_io_width; |
---|
115 | | - syscon_config.name = of_node_full_name(np); |
---|
116 | 109 | |
---|
117 | 110 | regmap = regmap_init_mmio(NULL, base, &syscon_config); |
---|
| 111 | + kfree(syscon_config.name); |
---|
118 | 112 | if (IS_ERR(regmap)) { |
---|
119 | 113 | pr_err("regmap init failed\n"); |
---|
120 | 114 | ret = PTR_ERR(regmap); |
---|
121 | 115 | goto err_regmap; |
---|
122 | 116 | } |
---|
123 | 117 | |
---|
124 | | - clk = of_clk_get(np, 0); |
---|
125 | | - if (IS_ERR(clk)) { |
---|
126 | | - ret = PTR_ERR(clk); |
---|
127 | | - /* clock is optional */ |
---|
128 | | - if (ret != -ENOENT) |
---|
129 | | - goto err_clk; |
---|
130 | | - } else { |
---|
131 | | - ret = regmap_mmio_attach_clk(regmap, clk); |
---|
132 | | - if (ret) |
---|
133 | | - goto err_attach; |
---|
| 118 | + if (check_clk) { |
---|
| 119 | + clk = of_clk_get(np, 0); |
---|
| 120 | + if (IS_ERR(clk)) { |
---|
| 121 | + ret = PTR_ERR(clk); |
---|
| 122 | + /* clock is optional */ |
---|
| 123 | + if (ret != -ENOENT) |
---|
| 124 | + goto err_clk; |
---|
| 125 | + } else { |
---|
| 126 | + ret = regmap_mmio_attach_clk(regmap, clk); |
---|
| 127 | + if (ret) |
---|
| 128 | + goto err_attach; |
---|
| 129 | + } |
---|
134 | 130 | } |
---|
135 | 131 | |
---|
136 | 132 | syscon->regmap = regmap; |
---|
.. | .. |
---|
154 | 150 | return ERR_PTR(ret); |
---|
155 | 151 | } |
---|
156 | 152 | |
---|
157 | | -struct regmap *syscon_node_to_regmap(struct device_node *np) |
---|
| 153 | +static struct regmap *device_node_get_regmap(struct device_node *np, |
---|
| 154 | + bool check_clk) |
---|
158 | 155 | { |
---|
159 | 156 | struct syscon *entry, *syscon = NULL; |
---|
160 | 157 | |
---|
.. | .. |
---|
169 | 166 | spin_unlock(&syscon_list_slock); |
---|
170 | 167 | |
---|
171 | 168 | if (!syscon) |
---|
172 | | - syscon = of_syscon_register(np); |
---|
| 169 | + syscon = of_syscon_register(np, check_clk); |
---|
173 | 170 | |
---|
174 | 171 | if (IS_ERR(syscon)) |
---|
175 | 172 | return ERR_CAST(syscon); |
---|
176 | 173 | |
---|
177 | 174 | return syscon->regmap; |
---|
| 175 | +} |
---|
| 176 | + |
---|
| 177 | +struct regmap *device_node_to_regmap(struct device_node *np) |
---|
| 178 | +{ |
---|
| 179 | + return device_node_get_regmap(np, false); |
---|
| 180 | +} |
---|
| 181 | +EXPORT_SYMBOL_GPL(device_node_to_regmap); |
---|
| 182 | + |
---|
| 183 | +struct regmap *syscon_node_to_regmap(struct device_node *np) |
---|
| 184 | +{ |
---|
| 185 | + if (!of_device_is_compatible(np, "syscon")) |
---|
| 186 | + return ERR_PTR(-EINVAL); |
---|
| 187 | + |
---|
| 188 | + return device_node_get_regmap(np, true); |
---|
178 | 189 | } |
---|
179 | 190 | EXPORT_SYMBOL_GPL(syscon_node_to_regmap); |
---|
180 | 191 | |
---|
.. | .. |
---|
193 | 204 | return regmap; |
---|
194 | 205 | } |
---|
195 | 206 | EXPORT_SYMBOL_GPL(syscon_regmap_lookup_by_compatible); |
---|
196 | | - |
---|
197 | | -static int syscon_match_pdevname(struct device *dev, void *data) |
---|
198 | | -{ |
---|
199 | | - return !strcmp(dev_name(dev), (const char *)data); |
---|
200 | | -} |
---|
201 | | - |
---|
202 | | -struct regmap *syscon_regmap_lookup_by_pdevname(const char *s) |
---|
203 | | -{ |
---|
204 | | - struct device *dev; |
---|
205 | | - struct syscon *syscon; |
---|
206 | | - |
---|
207 | | - dev = driver_find_device(&syscon_driver.driver, NULL, (void *)s, |
---|
208 | | - syscon_match_pdevname); |
---|
209 | | - if (!dev) |
---|
210 | | - return ERR_PTR(-EPROBE_DEFER); |
---|
211 | | - |
---|
212 | | - syscon = dev_get_drvdata(dev); |
---|
213 | | - |
---|
214 | | - return syscon->regmap; |
---|
215 | | -} |
---|
216 | | -EXPORT_SYMBOL_GPL(syscon_regmap_lookup_by_pdevname); |
---|
217 | 207 | |
---|
218 | 208 | struct regmap *syscon_regmap_lookup_by_phandle(struct device_node *np, |
---|
219 | 209 | const char *property) |
---|
.. | .. |
---|
236 | 226 | } |
---|
237 | 227 | EXPORT_SYMBOL_GPL(syscon_regmap_lookup_by_phandle); |
---|
238 | 228 | |
---|
| 229 | +struct regmap *syscon_regmap_lookup_by_phandle_args(struct device_node *np, |
---|
| 230 | + const char *property, |
---|
| 231 | + int arg_count, |
---|
| 232 | + unsigned int *out_args) |
---|
| 233 | +{ |
---|
| 234 | + struct device_node *syscon_np; |
---|
| 235 | + struct of_phandle_args args; |
---|
| 236 | + struct regmap *regmap; |
---|
| 237 | + unsigned int index; |
---|
| 238 | + int rc; |
---|
| 239 | + |
---|
| 240 | + rc = of_parse_phandle_with_fixed_args(np, property, arg_count, |
---|
| 241 | + 0, &args); |
---|
| 242 | + if (rc) |
---|
| 243 | + return ERR_PTR(rc); |
---|
| 244 | + |
---|
| 245 | + syscon_np = args.np; |
---|
| 246 | + if (!syscon_np) |
---|
| 247 | + return ERR_PTR(-ENODEV); |
---|
| 248 | + |
---|
| 249 | + regmap = syscon_node_to_regmap(syscon_np); |
---|
| 250 | + for (index = 0; index < arg_count; index++) |
---|
| 251 | + out_args[index] = args.args[index]; |
---|
| 252 | + of_node_put(syscon_np); |
---|
| 253 | + |
---|
| 254 | + return regmap; |
---|
| 255 | +} |
---|
| 256 | +EXPORT_SYMBOL_GPL(syscon_regmap_lookup_by_phandle_args); |
---|
| 257 | + |
---|
239 | 258 | static int syscon_probe(struct platform_device *pdev) |
---|
240 | 259 | { |
---|
241 | 260 | struct device *dev = &pdev->dev; |
---|
.. | .. |
---|
257 | 276 | if (!base) |
---|
258 | 277 | return -ENOMEM; |
---|
259 | 278 | |
---|
260 | | - syscon_config.max_register = res->end - res->start - 3; |
---|
| 279 | + syscon_config.max_register = resource_size(res) - 4; |
---|
261 | 280 | if (pdata) |
---|
262 | 281 | syscon_config.name = pdata->label; |
---|
263 | 282 | syscon->regmap = devm_regmap_init_mmio(dev, base, &syscon_config); |
---|
.. | .. |
---|
291 | 310 | return platform_driver_register(&syscon_driver); |
---|
292 | 311 | } |
---|
293 | 312 | postcore_initcall(syscon_init); |
---|
294 | | - |
---|
295 | | -static void __exit syscon_exit(void) |
---|
296 | | -{ |
---|
297 | | - platform_driver_unregister(&syscon_driver); |
---|
298 | | -} |
---|
299 | | -module_exit(syscon_exit); |
---|
300 | | - |
---|
301 | | -MODULE_AUTHOR("Dong Aisheng <dong.aisheng@linaro.org>"); |
---|
302 | | -MODULE_DESCRIPTION("System Control driver"); |
---|
303 | | -MODULE_LICENSE("GPL v2"); |
---|