0001
0002
0003
0004
0005
0006
0007
0008
0009
0010
0011 #include <linux/clk.h>
0012 #include <linux/err.h>
0013 #include <linux/hwspinlock.h>
0014 #include <linux/io.h>
0015 #include <linux/init.h>
0016 #include <linux/list.h>
0017 #include <linux/of.h>
0018 #include <linux/of_address.h>
0019 #include <linux/of_platform.h>
0020 #include <linux/platform_data/syscon.h>
0021 #include <linux/platform_device.h>
0022 #include <linux/regmap.h>
0023 #include <linux/mfd/syscon.h>
0024 #include <linux/slab.h>
0025
0026 static struct platform_driver syscon_driver;
0027
0028 static DEFINE_SPINLOCK(syscon_list_slock);
0029 static LIST_HEAD(syscon_list);
0030
0031 struct syscon {
0032 struct device_node *np;
0033 struct regmap *regmap;
0034 struct list_head list;
0035 };
0036
0037 static const struct regmap_config syscon_regmap_config = {
0038 .reg_bits = 32,
0039 .val_bits = 32,
0040 .reg_stride = 4,
0041 };
0042
0043 static struct syscon *of_syscon_register(struct device_node *np, bool check_clk)
0044 {
0045 struct clk *clk;
0046 struct syscon *syscon;
0047 struct regmap *regmap;
0048 void __iomem *base;
0049 u32 reg_io_width;
0050 int ret;
0051 struct regmap_config syscon_config = syscon_regmap_config;
0052 struct resource res;
0053
0054 syscon = kzalloc(sizeof(*syscon), GFP_KERNEL);
0055 if (!syscon)
0056 return ERR_PTR(-ENOMEM);
0057
0058 if (of_address_to_resource(np, 0, &res)) {
0059 ret = -ENOMEM;
0060 goto err_map;
0061 }
0062
0063 base = of_iomap(np, 0);
0064 if (!base) {
0065 ret = -ENOMEM;
0066 goto err_map;
0067 }
0068
0069
0070 if (of_property_read_bool(np, "big-endian"))
0071 syscon_config.val_format_endian = REGMAP_ENDIAN_BIG;
0072 else if (of_property_read_bool(np, "little-endian"))
0073 syscon_config.val_format_endian = REGMAP_ENDIAN_LITTLE;
0074 else if (of_property_read_bool(np, "native-endian"))
0075 syscon_config.val_format_endian = REGMAP_ENDIAN_NATIVE;
0076
0077
0078
0079
0080
0081
0082 ret = of_property_read_u32(np, "reg-io-width", ®_io_width);
0083 if (ret)
0084 reg_io_width = 4;
0085
0086 ret = of_hwspin_lock_get_id(np, 0);
0087 if (ret > 0 || (IS_ENABLED(CONFIG_HWSPINLOCK) && ret == 0)) {
0088 syscon_config.use_hwlock = true;
0089 syscon_config.hwlock_id = ret;
0090 syscon_config.hwlock_mode = HWLOCK_IRQSTATE;
0091 } else if (ret < 0) {
0092 switch (ret) {
0093 case -ENOENT:
0094
0095 break;
0096 default:
0097 pr_err("Failed to retrieve valid hwlock: %d\n", ret);
0098 fallthrough;
0099 case -EPROBE_DEFER:
0100 goto err_regmap;
0101 }
0102 }
0103
0104 syscon_config.name = kasprintf(GFP_KERNEL, "%pOFn@%pa", np, &res.start);
0105 syscon_config.reg_stride = reg_io_width;
0106 syscon_config.val_bits = reg_io_width * 8;
0107 syscon_config.max_register = resource_size(&res) - reg_io_width;
0108
0109 regmap = regmap_init_mmio(NULL, base, &syscon_config);
0110 kfree(syscon_config.name);
0111 if (IS_ERR(regmap)) {
0112 pr_err("regmap init failed\n");
0113 ret = PTR_ERR(regmap);
0114 goto err_regmap;
0115 }
0116
0117 if (check_clk) {
0118 clk = of_clk_get(np, 0);
0119 if (IS_ERR(clk)) {
0120 ret = PTR_ERR(clk);
0121
0122 if (ret != -ENOENT)
0123 goto err_clk;
0124 } else {
0125 ret = regmap_mmio_attach_clk(regmap, clk);
0126 if (ret)
0127 goto err_attach;
0128 }
0129 }
0130
0131 syscon->regmap = regmap;
0132 syscon->np = np;
0133
0134 spin_lock(&syscon_list_slock);
0135 list_add_tail(&syscon->list, &syscon_list);
0136 spin_unlock(&syscon_list_slock);
0137
0138 return syscon;
0139
0140 err_attach:
0141 if (!IS_ERR(clk))
0142 clk_put(clk);
0143 err_clk:
0144 regmap_exit(regmap);
0145 err_regmap:
0146 iounmap(base);
0147 err_map:
0148 kfree(syscon);
0149 return ERR_PTR(ret);
0150 }
0151
0152 static struct regmap *device_node_get_regmap(struct device_node *np,
0153 bool check_clk)
0154 {
0155 struct syscon *entry, *syscon = NULL;
0156
0157 spin_lock(&syscon_list_slock);
0158
0159 list_for_each_entry(entry, &syscon_list, list)
0160 if (entry->np == np) {
0161 syscon = entry;
0162 break;
0163 }
0164
0165 spin_unlock(&syscon_list_slock);
0166
0167 if (!syscon)
0168 syscon = of_syscon_register(np, check_clk);
0169
0170 if (IS_ERR(syscon))
0171 return ERR_CAST(syscon);
0172
0173 return syscon->regmap;
0174 }
0175
0176 struct regmap *device_node_to_regmap(struct device_node *np)
0177 {
0178 return device_node_get_regmap(np, false);
0179 }
0180 EXPORT_SYMBOL_GPL(device_node_to_regmap);
0181
0182 struct regmap *syscon_node_to_regmap(struct device_node *np)
0183 {
0184 if (!of_device_is_compatible(np, "syscon"))
0185 return ERR_PTR(-EINVAL);
0186
0187 return device_node_get_regmap(np, true);
0188 }
0189 EXPORT_SYMBOL_GPL(syscon_node_to_regmap);
0190
0191 struct regmap *syscon_regmap_lookup_by_compatible(const char *s)
0192 {
0193 struct device_node *syscon_np;
0194 struct regmap *regmap;
0195
0196 syscon_np = of_find_compatible_node(NULL, NULL, s);
0197 if (!syscon_np)
0198 return ERR_PTR(-ENODEV);
0199
0200 regmap = syscon_node_to_regmap(syscon_np);
0201 of_node_put(syscon_np);
0202
0203 return regmap;
0204 }
0205 EXPORT_SYMBOL_GPL(syscon_regmap_lookup_by_compatible);
0206
0207 struct regmap *syscon_regmap_lookup_by_phandle(struct device_node *np,
0208 const char *property)
0209 {
0210 struct device_node *syscon_np;
0211 struct regmap *regmap;
0212
0213 if (property)
0214 syscon_np = of_parse_phandle(np, property, 0);
0215 else
0216 syscon_np = np;
0217
0218 if (!syscon_np)
0219 return ERR_PTR(-ENODEV);
0220
0221 regmap = syscon_node_to_regmap(syscon_np);
0222 of_node_put(syscon_np);
0223
0224 return regmap;
0225 }
0226 EXPORT_SYMBOL_GPL(syscon_regmap_lookup_by_phandle);
0227
0228 struct regmap *syscon_regmap_lookup_by_phandle_args(struct device_node *np,
0229 const char *property,
0230 int arg_count,
0231 unsigned int *out_args)
0232 {
0233 struct device_node *syscon_np;
0234 struct of_phandle_args args;
0235 struct regmap *regmap;
0236 unsigned int index;
0237 int rc;
0238
0239 rc = of_parse_phandle_with_fixed_args(np, property, arg_count,
0240 0, &args);
0241 if (rc)
0242 return ERR_PTR(rc);
0243
0244 syscon_np = args.np;
0245 if (!syscon_np)
0246 return ERR_PTR(-ENODEV);
0247
0248 regmap = syscon_node_to_regmap(syscon_np);
0249 for (index = 0; index < arg_count; index++)
0250 out_args[index] = args.args[index];
0251 of_node_put(syscon_np);
0252
0253 return regmap;
0254 }
0255 EXPORT_SYMBOL_GPL(syscon_regmap_lookup_by_phandle_args);
0256
0257
0258
0259
0260
0261
0262 struct regmap *syscon_regmap_lookup_by_phandle_optional(struct device_node *np,
0263 const char *property)
0264 {
0265 struct regmap *regmap;
0266
0267 regmap = syscon_regmap_lookup_by_phandle(np, property);
0268 if (IS_ERR(regmap) && PTR_ERR(regmap) == -ENODEV)
0269 return NULL;
0270
0271 return regmap;
0272 }
0273 EXPORT_SYMBOL_GPL(syscon_regmap_lookup_by_phandle_optional);
0274
0275 static int syscon_probe(struct platform_device *pdev)
0276 {
0277 struct device *dev = &pdev->dev;
0278 struct syscon_platform_data *pdata = dev_get_platdata(dev);
0279 struct syscon *syscon;
0280 struct regmap_config syscon_config = syscon_regmap_config;
0281 struct resource *res;
0282 void __iomem *base;
0283
0284 syscon = devm_kzalloc(dev, sizeof(*syscon), GFP_KERNEL);
0285 if (!syscon)
0286 return -ENOMEM;
0287
0288 res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
0289 if (!res)
0290 return -ENOENT;
0291
0292 base = devm_ioremap(dev, res->start, resource_size(res));
0293 if (!base)
0294 return -ENOMEM;
0295
0296 syscon_config.max_register = resource_size(res) - 4;
0297 if (pdata)
0298 syscon_config.name = pdata->label;
0299 syscon->regmap = devm_regmap_init_mmio(dev, base, &syscon_config);
0300 if (IS_ERR(syscon->regmap)) {
0301 dev_err(dev, "regmap init failed\n");
0302 return PTR_ERR(syscon->regmap);
0303 }
0304
0305 platform_set_drvdata(pdev, syscon);
0306
0307 dev_dbg(dev, "regmap %pR registered\n", res);
0308
0309 return 0;
0310 }
0311
0312 static const struct platform_device_id syscon_ids[] = {
0313 { "syscon", },
0314 { }
0315 };
0316
0317 static struct platform_driver syscon_driver = {
0318 .driver = {
0319 .name = "syscon",
0320 },
0321 .probe = syscon_probe,
0322 .id_table = syscon_ids,
0323 };
0324
0325 static int __init syscon_init(void)
0326 {
0327 return platform_driver_register(&syscon_driver);
0328 }
0329 postcore_initcall(syscon_init);