syscon.c 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177
  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/io.h>
  16. #include <linux/module.h>
  17. #include <linux/of.h>
  18. #include <linux/of_address.h>
  19. #include <linux/of_platform.h>
  20. #include <linux/platform_device.h>
  21. #include <linux/regmap.h>
  22. #include <linux/mfd/syscon.h>
  23. static struct platform_driver syscon_driver;
  24. struct syscon {
  25. struct device *dev;
  26. void __iomem *base;
  27. struct regmap *regmap;
  28. };
  29. static int syscon_match(struct device *dev, void *data)
  30. {
  31. struct syscon *syscon = dev_get_drvdata(dev);
  32. struct device_node *dn = data;
  33. return (syscon->dev->of_node == dn) ? 1 : 0;
  34. }
  35. struct regmap *syscon_node_to_regmap(struct device_node *np)
  36. {
  37. struct syscon *syscon;
  38. struct device *dev;
  39. dev = driver_find_device(&syscon_driver.driver, NULL, np,
  40. syscon_match);
  41. if (!dev)
  42. return ERR_PTR(-EPROBE_DEFER);
  43. syscon = dev_get_drvdata(dev);
  44. return syscon->regmap;
  45. }
  46. EXPORT_SYMBOL_GPL(syscon_node_to_regmap);
  47. struct regmap *syscon_regmap_lookup_by_compatible(const char *s)
  48. {
  49. struct device_node *syscon_np;
  50. struct regmap *regmap;
  51. syscon_np = of_find_compatible_node(NULL, NULL, s);
  52. if (!syscon_np)
  53. return ERR_PTR(-ENODEV);
  54. regmap = syscon_node_to_regmap(syscon_np);
  55. of_node_put(syscon_np);
  56. return regmap;
  57. }
  58. EXPORT_SYMBOL_GPL(syscon_regmap_lookup_by_compatible);
  59. struct regmap *syscon_regmap_lookup_by_phandle(struct device_node *np,
  60. const char *property)
  61. {
  62. struct device_node *syscon_np;
  63. struct regmap *regmap;
  64. syscon_np = of_parse_phandle(np, property, 0);
  65. if (!syscon_np)
  66. return ERR_PTR(-ENODEV);
  67. regmap = syscon_node_to_regmap(syscon_np);
  68. of_node_put(syscon_np);
  69. return regmap;
  70. }
  71. EXPORT_SYMBOL_GPL(syscon_regmap_lookup_by_phandle);
  72. static const struct of_device_id of_syscon_match[] = {
  73. { .compatible = "syscon", },
  74. { },
  75. };
  76. static struct regmap_config syscon_regmap_config = {
  77. .reg_bits = 32,
  78. .val_bits = 32,
  79. .reg_stride = 4,
  80. };
  81. static int syscon_probe(struct platform_device *pdev)
  82. {
  83. struct device *dev = &pdev->dev;
  84. struct device_node *np = dev->of_node;
  85. struct syscon *syscon;
  86. struct resource res;
  87. int ret;
  88. if (!np)
  89. return -ENOENT;
  90. syscon = devm_kzalloc(dev, sizeof(struct syscon),
  91. GFP_KERNEL);
  92. if (!syscon)
  93. return -ENOMEM;
  94. syscon->base = of_iomap(np, 0);
  95. if (!syscon->base)
  96. return -EADDRNOTAVAIL;
  97. ret = of_address_to_resource(np, 0, &res);
  98. if (ret)
  99. return ret;
  100. syscon_regmap_config.max_register = res.end - res.start - 3;
  101. syscon->regmap = devm_regmap_init_mmio(dev, syscon->base,
  102. &syscon_regmap_config);
  103. if (IS_ERR(syscon->regmap)) {
  104. dev_err(dev, "regmap init failed\n");
  105. return PTR_ERR(syscon->regmap);
  106. }
  107. syscon->dev = dev;
  108. platform_set_drvdata(pdev, syscon);
  109. dev_info(dev, "syscon regmap start 0x%x end 0x%x registered\n",
  110. res.start, res.end);
  111. return 0;
  112. }
  113. static int syscon_remove(struct platform_device *pdev)
  114. {
  115. struct syscon *syscon;
  116. syscon = platform_get_drvdata(pdev);
  117. iounmap(syscon->base);
  118. platform_set_drvdata(pdev, NULL);
  119. return 0;
  120. }
  121. static struct platform_driver syscon_driver = {
  122. .driver = {
  123. .name = "syscon",
  124. .owner = THIS_MODULE,
  125. .of_match_table = of_syscon_match,
  126. },
  127. .probe = syscon_probe,
  128. .remove = syscon_remove,
  129. };
  130. static int __init syscon_init(void)
  131. {
  132. return platform_driver_register(&syscon_driver);
  133. }
  134. postcore_initcall(syscon_init);
  135. static void __exit syscon_exit(void)
  136. {
  137. platform_driver_unregister(&syscon_driver);
  138. }
  139. module_exit(syscon_exit);
  140. MODULE_AUTHOR("Dong Aisheng <dong.aisheng@linaro.org>");
  141. MODULE_DESCRIPTION("System Control driver");
  142. MODULE_LICENSE("GPL v2");