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/clk.h> 16 #include <linux/err.h> 17 #include <linux/hwspinlock.h> 18 #include <linux/io.h> 19 #include <linux/init.h> 20 #include <linux/list.h> 21 #include <linux/of.h> 22 #include <linux/of_address.h> 23 #include <linux/of_platform.h> 24 #include <linux/platform_data/syscon.h> 25 #include <linux/platform_device.h> 26 #include <linux/regmap.h> 27 #include <linux/mfd/syscon.h> 28 #include <linux/slab.h> 29 30 static struct platform_driver syscon_driver; 31 32 static DEFINE_SPINLOCK(syscon_list_slock); 33 static LIST_HEAD(syscon_list); 34 35 struct syscon { 36 struct device_node *np; 37 struct regmap *regmap; 38 struct list_head list; 39 }; 40 41 static const struct regmap_config syscon_regmap_config = { 42 .reg_bits = 32, 43 .val_bits = 32, 44 .reg_stride = 4, 45 }; 46 47 static struct syscon *of_syscon_register(struct device_node *np) 48 { 49 struct clk *clk; 50 struct syscon *syscon; 51 struct regmap *regmap; 52 void __iomem *base; 53 u32 reg_io_width; 54 int ret; 55 struct regmap_config syscon_config = syscon_regmap_config; 56 struct resource res; 57 58 if (!of_device_is_compatible(np, "syscon")) 59 return ERR_PTR(-EINVAL); 60 61 syscon = kzalloc(sizeof(*syscon), GFP_KERNEL); 62 if (!syscon) 63 return ERR_PTR(-ENOMEM); 64 65 if (of_address_to_resource(np, 0, &res)) { 66 ret = -ENOMEM; 67 goto err_map; 68 } 69 70 base = ioremap(res.start, resource_size(&res)); 71 if (!base) { 72 ret = -ENOMEM; 73 goto err_map; 74 } 75 76 /* Parse the device's DT node for an endianness specification */ 77 if (of_property_read_bool(np, "big-endian")) 78 syscon_config.val_format_endian = REGMAP_ENDIAN_BIG; 79 else if (of_property_read_bool(np, "little-endian")) 80 syscon_config.val_format_endian = REGMAP_ENDIAN_LITTLE; 81 else if (of_property_read_bool(np, "native-endian")) 82 syscon_config.val_format_endian = REGMAP_ENDIAN_NATIVE; 83 84 /* 85 * search for reg-io-width property in DT. If it is not provided, 86 * default to 4 bytes. regmap_init_mmio will return an error if values 87 * are invalid so there is no need to check them here. 88 */ 89 ret = of_property_read_u32(np, "reg-io-width", ®_io_width); 90 if (ret) 91 reg_io_width = 4; 92 93 ret = of_hwspin_lock_get_id(np, 0); 94 if (ret > 0 || (IS_ENABLED(CONFIG_HWSPINLOCK) && ret == 0)) { 95 syscon_config.use_hwlock = true; 96 syscon_config.hwlock_id = ret; 97 syscon_config.hwlock_mode = HWLOCK_IRQSTATE; 98 } else if (ret < 0) { 99 switch (ret) { 100 case -ENOENT: 101 /* Ignore missing hwlock, it's optional. */ 102 break; 103 default: 104 pr_err("Failed to retrieve valid hwlock: %d\n", ret); 105 /* fall-through */ 106 case -EPROBE_DEFER: 107 goto err_regmap; 108 } 109 } 110 111 syscon_config.name = of_node_full_name(np); 112 syscon_config.reg_stride = reg_io_width; 113 syscon_config.val_bits = reg_io_width * 8; 114 syscon_config.max_register = resource_size(&res) - reg_io_width; 115 syscon_config.name = of_node_full_name(np); 116 117 regmap = regmap_init_mmio(NULL, base, &syscon_config); 118 if (IS_ERR(regmap)) { 119 pr_err("regmap init failed\n"); 120 ret = PTR_ERR(regmap); 121 goto err_regmap; 122 } 123 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; 134 } 135 136 syscon->regmap = regmap; 137 syscon->np = np; 138 139 spin_lock(&syscon_list_slock); 140 list_add_tail(&syscon->list, &syscon_list); 141 spin_unlock(&syscon_list_slock); 142 143 return syscon; 144 145 err_attach: 146 if (!IS_ERR(clk)) 147 clk_put(clk); 148 err_clk: 149 regmap_exit(regmap); 150 err_regmap: 151 iounmap(base); 152 err_map: 153 kfree(syscon); 154 return ERR_PTR(ret); 155 } 156 157 struct regmap *syscon_node_to_regmap(struct device_node *np) 158 { 159 struct syscon *entry, *syscon = NULL; 160 161 spin_lock(&syscon_list_slock); 162 163 list_for_each_entry(entry, &syscon_list, list) 164 if (entry->np == np) { 165 syscon = entry; 166 break; 167 } 168 169 spin_unlock(&syscon_list_slock); 170 171 if (!syscon) 172 syscon = of_syscon_register(np); 173 174 if (IS_ERR(syscon)) 175 return ERR_CAST(syscon); 176 177 return syscon->regmap; 178 } 179 EXPORT_SYMBOL_GPL(syscon_node_to_regmap); 180 181 struct regmap *syscon_regmap_lookup_by_compatible(const char *s) 182 { 183 struct device_node *syscon_np; 184 struct regmap *regmap; 185 186 syscon_np = of_find_compatible_node(NULL, NULL, s); 187 if (!syscon_np) 188 return ERR_PTR(-ENODEV); 189 190 regmap = syscon_node_to_regmap(syscon_np); 191 of_node_put(syscon_np); 192 193 return regmap; 194 } 195 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 218 struct regmap *syscon_regmap_lookup_by_phandle(struct device_node *np, 219 const char *property) 220 { 221 struct device_node *syscon_np; 222 struct regmap *regmap; 223 224 if (property) 225 syscon_np = of_parse_phandle(np, property, 0); 226 else 227 syscon_np = np; 228 229 if (!syscon_np) 230 return ERR_PTR(-ENODEV); 231 232 regmap = syscon_node_to_regmap(syscon_np); 233 of_node_put(syscon_np); 234 235 return regmap; 236 } 237 EXPORT_SYMBOL_GPL(syscon_regmap_lookup_by_phandle); 238 239 static int syscon_probe(struct platform_device *pdev) 240 { 241 struct device *dev = &pdev->dev; 242 struct syscon_platform_data *pdata = dev_get_platdata(dev); 243 struct syscon *syscon; 244 struct regmap_config syscon_config = syscon_regmap_config; 245 struct resource *res; 246 void __iomem *base; 247 248 syscon = devm_kzalloc(dev, sizeof(*syscon), GFP_KERNEL); 249 if (!syscon) 250 return -ENOMEM; 251 252 res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 253 if (!res) 254 return -ENOENT; 255 256 base = devm_ioremap(dev, res->start, resource_size(res)); 257 if (!base) 258 return -ENOMEM; 259 260 syscon_config.max_register = res->end - res->start - 3; 261 if (pdata) 262 syscon_config.name = pdata->label; 263 syscon->regmap = devm_regmap_init_mmio(dev, base, &syscon_config); 264 if (IS_ERR(syscon->regmap)) { 265 dev_err(dev, "regmap init failed\n"); 266 return PTR_ERR(syscon->regmap); 267 } 268 269 platform_set_drvdata(pdev, syscon); 270 271 dev_dbg(dev, "regmap %pR registered\n", res); 272 273 return 0; 274 } 275 276 static const struct platform_device_id syscon_ids[] = { 277 { "syscon", }, 278 { } 279 }; 280 281 static struct platform_driver syscon_driver = { 282 .driver = { 283 .name = "syscon", 284 }, 285 .probe = syscon_probe, 286 .id_table = syscon_ids, 287 }; 288 289 static int __init syscon_init(void) 290 { 291 return platform_driver_register(&syscon_driver); 292 } 293 postcore_initcall(syscon_init); 294