syscon.c 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284
  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. #include <linux/err.h>
  15. #include <linux/hwspinlock.h>
  16. #include <linux/io.h>
  17. #include <linux/module.h>
  18. #include <linux/list.h>
  19. #include <linux/of.h>
  20. #include <linux/of_address.h>
  21. #include <linux/of_platform.h>
  22. #include <linux/platform_data/syscon.h>
  23. #include <linux/platform_device.h>
  24. #include <linux/regmap.h>
  25. #include <linux/mfd/syscon.h>
  26. #include <linux/slab.h>
  27. static struct platform_driver syscon_driver;
  28. static DEFINE_SPINLOCK(syscon_list_slock);
  29. static LIST_HEAD(syscon_list);
  30. struct syscon {
  31. struct device_node *np;
  32. struct regmap *regmap;
  33. struct list_head list;
  34. };
  35. static const struct regmap_config syscon_regmap_config = {
  36. .reg_bits = 32,
  37. .val_bits = 32,
  38. .reg_stride = 4,
  39. };
  40. static struct syscon *of_syscon_register(struct device_node *np)
  41. {
  42. struct syscon *syscon;
  43. struct regmap *regmap;
  44. void __iomem *base;
  45. u32 reg_io_width;
  46. int ret;
  47. struct regmap_config syscon_config = syscon_regmap_config;
  48. struct resource res;
  49. if (!of_device_is_compatible(np, "syscon"))
  50. return ERR_PTR(-EINVAL);
  51. syscon = kzalloc(sizeof(*syscon), GFP_KERNEL);
  52. if (!syscon)
  53. return ERR_PTR(-ENOMEM);
  54. if (of_address_to_resource(np, 0, &res)) {
  55. ret = -ENOMEM;
  56. goto err_map;
  57. }
  58. base = ioremap(res.start, resource_size(&res));
  59. if (!base) {
  60. ret = -ENOMEM;
  61. goto err_map;
  62. }
  63. /* Parse the device's DT node for an endianness specification */
  64. if (of_property_read_bool(np, "big-endian"))
  65. syscon_config.val_format_endian = REGMAP_ENDIAN_BIG;
  66. else if (of_property_read_bool(np, "little-endian"))
  67. syscon_config.val_format_endian = REGMAP_ENDIAN_LITTLE;
  68. else if (of_property_read_bool(np, "native-endian"))
  69. syscon_config.val_format_endian = REGMAP_ENDIAN_NATIVE;
  70. /*
  71. * search for reg-io-width property in DT. If it is not provided,
  72. * default to 4 bytes. regmap_init_mmio will return an error if values
  73. * are invalid so there is no need to check them here.
  74. */
  75. ret = of_property_read_u32(np, "reg-io-width", &reg_io_width);
  76. if (ret)
  77. reg_io_width = 4;
  78. ret = of_hwspin_lock_get_id(np, 0);
  79. if (ret > 0 || (IS_ENABLED(CONFIG_HWSPINLOCK) && ret == 0)) {
  80. syscon_config.use_hwlock = true;
  81. syscon_config.hwlock_id = ret;
  82. syscon_config.hwlock_mode = HWLOCK_IRQSTATE;
  83. } else if (ret < 0) {
  84. switch (ret) {
  85. case -ENOENT:
  86. /* Ignore missing hwlock, it's optional. */
  87. break;
  88. default:
  89. pr_err("Failed to retrieve valid hwlock: %d\n", ret);
  90. /* fall-through */
  91. case -EPROBE_DEFER:
  92. goto err_regmap;
  93. }
  94. }
  95. syscon_config.name = of_node_full_name(np);
  96. syscon_config.reg_stride = reg_io_width;
  97. syscon_config.val_bits = reg_io_width * 8;
  98. syscon_config.max_register = resource_size(&res) - reg_io_width;
  99. syscon_config.name = of_node_full_name(np);
  100. regmap = regmap_init_mmio(NULL, base, &syscon_config);
  101. if (IS_ERR(regmap)) {
  102. pr_err("regmap init failed\n");
  103. ret = PTR_ERR(regmap);
  104. goto err_regmap;
  105. }
  106. syscon->regmap = regmap;
  107. syscon->np = np;
  108. spin_lock(&syscon_list_slock);
  109. list_add_tail(&syscon->list, &syscon_list);
  110. spin_unlock(&syscon_list_slock);
  111. return syscon;
  112. err_regmap:
  113. iounmap(base);
  114. err_map:
  115. kfree(syscon);
  116. return ERR_PTR(ret);
  117. }
  118. struct regmap *syscon_node_to_regmap(struct device_node *np)
  119. {
  120. struct syscon *entry, *syscon = NULL;
  121. spin_lock(&syscon_list_slock);
  122. list_for_each_entry(entry, &syscon_list, list)
  123. if (entry->np == np) {
  124. syscon = entry;
  125. break;
  126. }
  127. spin_unlock(&syscon_list_slock);
  128. if (!syscon)
  129. syscon = of_syscon_register(np);
  130. if (IS_ERR(syscon))
  131. return ERR_CAST(syscon);
  132. return syscon->regmap;
  133. }
  134. EXPORT_SYMBOL_GPL(syscon_node_to_regmap);
  135. struct regmap *syscon_regmap_lookup_by_compatible(const char *s)
  136. {
  137. struct device_node *syscon_np;
  138. struct regmap *regmap;
  139. syscon_np = of_find_compatible_node(NULL, NULL, s);
  140. if (!syscon_np)
  141. return ERR_PTR(-ENODEV);
  142. regmap = syscon_node_to_regmap(syscon_np);
  143. of_node_put(syscon_np);
  144. return regmap;
  145. }
  146. EXPORT_SYMBOL_GPL(syscon_regmap_lookup_by_compatible);
  147. static int syscon_match_pdevname(struct device *dev, void *data)
  148. {
  149. return !strcmp(dev_name(dev), (const char *)data);
  150. }
  151. struct regmap *syscon_regmap_lookup_by_pdevname(const char *s)
  152. {
  153. struct device *dev;
  154. struct syscon *syscon;
  155. dev = driver_find_device(&syscon_driver.driver, NULL, (void *)s,
  156. syscon_match_pdevname);
  157. if (!dev)
  158. return ERR_PTR(-EPROBE_DEFER);
  159. syscon = dev_get_drvdata(dev);
  160. return syscon->regmap;
  161. }
  162. EXPORT_SYMBOL_GPL(syscon_regmap_lookup_by_pdevname);
  163. struct regmap *syscon_regmap_lookup_by_phandle(struct device_node *np,
  164. const char *property)
  165. {
  166. struct device_node *syscon_np;
  167. struct regmap *regmap;
  168. if (property)
  169. syscon_np = of_parse_phandle(np, property, 0);
  170. else
  171. syscon_np = np;
  172. if (!syscon_np)
  173. return ERR_PTR(-ENODEV);
  174. regmap = syscon_node_to_regmap(syscon_np);
  175. of_node_put(syscon_np);
  176. return regmap;
  177. }
  178. EXPORT_SYMBOL_GPL(syscon_regmap_lookup_by_phandle);
  179. static int syscon_probe(struct platform_device *pdev)
  180. {
  181. struct device *dev = &pdev->dev;
  182. struct syscon_platform_data *pdata = dev_get_platdata(dev);
  183. struct syscon *syscon;
  184. struct regmap_config syscon_config = syscon_regmap_config;
  185. struct resource *res;
  186. void __iomem *base;
  187. syscon = devm_kzalloc(dev, sizeof(*syscon), GFP_KERNEL);
  188. if (!syscon)
  189. return -ENOMEM;
  190. res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
  191. if (!res)
  192. return -ENOENT;
  193. base = devm_ioremap(dev, res->start, resource_size(res));
  194. if (!base)
  195. return -ENOMEM;
  196. syscon_config.max_register = res->end - res->start - 3;
  197. if (pdata)
  198. syscon_config.name = pdata->label;
  199. syscon->regmap = devm_regmap_init_mmio(dev, base, &syscon_config);
  200. if (IS_ERR(syscon->regmap)) {
  201. dev_err(dev, "regmap init failed\n");
  202. return PTR_ERR(syscon->regmap);
  203. }
  204. platform_set_drvdata(pdev, syscon);
  205. dev_dbg(dev, "regmap %pR registered\n", res);
  206. return 0;
  207. }
  208. static const struct platform_device_id syscon_ids[] = {
  209. { "syscon", },
  210. { }
  211. };
  212. static struct platform_driver syscon_driver = {
  213. .driver = {
  214. .name = "syscon",
  215. },
  216. .probe = syscon_probe,
  217. .id_table = syscon_ids,
  218. };
  219. static int __init syscon_init(void)
  220. {
  221. return platform_driver_register(&syscon_driver);
  222. }
  223. postcore_initcall(syscon_init);
  224. static void __exit syscon_exit(void)
  225. {
  226. platform_driver_unregister(&syscon_driver);
  227. }
  228. module_exit(syscon_exit);
  229. MODULE_AUTHOR("Dong Aisheng <dong.aisheng@linaro.org>");
  230. MODULE_DESCRIPTION("System Control driver");
  231. MODULE_LICENSE("GPL v2");