| .. | .. |
|---|
| 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"); |
|---|