clk-s2mps11.c 8.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335
  1. /*
  2. * clk-s2mps11.c - Clock driver for S2MPS11.
  3. *
  4. * Copyright (C) 2013,2014 Samsung Electornics
  5. *
  6. * This program is free software; you can redistribute it and/or modify it
  7. * under the terms of the GNU General Public License as published by the
  8. * Free Software Foundation; either version 2 of the License, or (at your
  9. * option) any later version.
  10. *
  11. * This program is distributed in the hope that it will be useful,
  12. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  14. * GNU General Public License for more details.
  15. *
  16. */
  17. #include <linux/module.h>
  18. #include <linux/err.h>
  19. #include <linux/of.h>
  20. #include <linux/clkdev.h>
  21. #include <linux/regmap.h>
  22. #include <linux/clk-provider.h>
  23. #include <linux/platform_device.h>
  24. #include <linux/mfd/samsung/s2mps11.h>
  25. #include <linux/mfd/samsung/s2mps13.h>
  26. #include <linux/mfd/samsung/s2mps14.h>
  27. #include <linux/mfd/samsung/s5m8767.h>
  28. #include <linux/mfd/samsung/core.h>
  29. #define s2mps11_name(a) (a->hw.init->name)
  30. static struct clk **clk_table;
  31. static struct clk_onecell_data clk_data;
  32. enum {
  33. S2MPS11_CLK_AP = 0,
  34. S2MPS11_CLK_CP,
  35. S2MPS11_CLK_BT,
  36. S2MPS11_CLKS_NUM,
  37. };
  38. struct s2mps11_clk {
  39. struct sec_pmic_dev *iodev;
  40. struct device_node *clk_np;
  41. struct clk_hw hw;
  42. struct clk *clk;
  43. struct clk_lookup *lookup;
  44. u32 mask;
  45. unsigned int reg;
  46. };
  47. static struct s2mps11_clk *to_s2mps11_clk(struct clk_hw *hw)
  48. {
  49. return container_of(hw, struct s2mps11_clk, hw);
  50. }
  51. static int s2mps11_clk_prepare(struct clk_hw *hw)
  52. {
  53. struct s2mps11_clk *s2mps11 = to_s2mps11_clk(hw);
  54. int ret;
  55. ret = regmap_update_bits(s2mps11->iodev->regmap_pmic,
  56. s2mps11->reg,
  57. s2mps11->mask, s2mps11->mask);
  58. return ret;
  59. }
  60. static void s2mps11_clk_unprepare(struct clk_hw *hw)
  61. {
  62. struct s2mps11_clk *s2mps11 = to_s2mps11_clk(hw);
  63. int ret;
  64. ret = regmap_update_bits(s2mps11->iodev->regmap_pmic, s2mps11->reg,
  65. s2mps11->mask, ~s2mps11->mask);
  66. }
  67. static int s2mps11_clk_is_prepared(struct clk_hw *hw)
  68. {
  69. int ret;
  70. u32 val;
  71. struct s2mps11_clk *s2mps11 = to_s2mps11_clk(hw);
  72. ret = regmap_read(s2mps11->iodev->regmap_pmic,
  73. s2mps11->reg, &val);
  74. if (ret < 0)
  75. return -EINVAL;
  76. return val & s2mps11->mask;
  77. }
  78. static unsigned long s2mps11_clk_recalc_rate(struct clk_hw *hw,
  79. unsigned long parent_rate)
  80. {
  81. return 32768;
  82. }
  83. static struct clk_ops s2mps11_clk_ops = {
  84. .prepare = s2mps11_clk_prepare,
  85. .unprepare = s2mps11_clk_unprepare,
  86. .is_prepared = s2mps11_clk_is_prepared,
  87. .recalc_rate = s2mps11_clk_recalc_rate,
  88. };
  89. static struct clk_init_data s2mps11_clks_init[S2MPS11_CLKS_NUM] = {
  90. [S2MPS11_CLK_AP] = {
  91. .name = "s2mps11_ap",
  92. .ops = &s2mps11_clk_ops,
  93. .flags = CLK_IS_ROOT,
  94. },
  95. [S2MPS11_CLK_CP] = {
  96. .name = "s2mps11_cp",
  97. .ops = &s2mps11_clk_ops,
  98. .flags = CLK_IS_ROOT,
  99. },
  100. [S2MPS11_CLK_BT] = {
  101. .name = "s2mps11_bt",
  102. .ops = &s2mps11_clk_ops,
  103. .flags = CLK_IS_ROOT,
  104. },
  105. };
  106. static struct clk_init_data s2mps13_clks_init[S2MPS11_CLKS_NUM] = {
  107. [S2MPS11_CLK_AP] = {
  108. .name = "s2mps13_ap",
  109. .ops = &s2mps11_clk_ops,
  110. .flags = CLK_IS_ROOT,
  111. },
  112. [S2MPS11_CLK_CP] = {
  113. .name = "s2mps13_cp",
  114. .ops = &s2mps11_clk_ops,
  115. .flags = CLK_IS_ROOT,
  116. },
  117. [S2MPS11_CLK_BT] = {
  118. .name = "s2mps13_bt",
  119. .ops = &s2mps11_clk_ops,
  120. .flags = CLK_IS_ROOT,
  121. },
  122. };
  123. static struct clk_init_data s2mps14_clks_init[S2MPS11_CLKS_NUM] = {
  124. [S2MPS11_CLK_AP] = {
  125. .name = "s2mps14_ap",
  126. .ops = &s2mps11_clk_ops,
  127. .flags = CLK_IS_ROOT,
  128. },
  129. [S2MPS11_CLK_BT] = {
  130. .name = "s2mps14_bt",
  131. .ops = &s2mps11_clk_ops,
  132. .flags = CLK_IS_ROOT,
  133. },
  134. };
  135. static struct device_node *s2mps11_clk_parse_dt(struct platform_device *pdev,
  136. struct clk_init_data *clks_init)
  137. {
  138. struct sec_pmic_dev *iodev = dev_get_drvdata(pdev->dev.parent);
  139. struct device_node *clk_np;
  140. int i;
  141. if (!iodev->dev->of_node)
  142. return ERR_PTR(-EINVAL);
  143. clk_np = of_get_child_by_name(iodev->dev->of_node, "clocks");
  144. if (!clk_np) {
  145. dev_err(&pdev->dev, "could not find clock sub-node\n");
  146. return ERR_PTR(-EINVAL);
  147. }
  148. for (i = 0; i < S2MPS11_CLKS_NUM; i++) {
  149. if (!clks_init[i].name)
  150. continue; /* Skip clocks not present in some devices */
  151. of_property_read_string_index(clk_np, "clock-output-names", i,
  152. &clks_init[i].name);
  153. }
  154. return clk_np;
  155. }
  156. static int s2mps11_clk_probe(struct platform_device *pdev)
  157. {
  158. struct sec_pmic_dev *iodev = dev_get_drvdata(pdev->dev.parent);
  159. struct s2mps11_clk *s2mps11_clks, *s2mps11_clk;
  160. unsigned int s2mps11_reg;
  161. struct clk_init_data *clks_init;
  162. int i, ret = 0;
  163. s2mps11_clks = devm_kzalloc(&pdev->dev, sizeof(*s2mps11_clk) *
  164. S2MPS11_CLKS_NUM, GFP_KERNEL);
  165. if (!s2mps11_clks)
  166. return -ENOMEM;
  167. s2mps11_clk = s2mps11_clks;
  168. clk_table = devm_kzalloc(&pdev->dev, sizeof(struct clk *) *
  169. S2MPS11_CLKS_NUM, GFP_KERNEL);
  170. if (!clk_table)
  171. return -ENOMEM;
  172. switch(platform_get_device_id(pdev)->driver_data) {
  173. case S2MPS11X:
  174. s2mps11_reg = S2MPS11_REG_RTC_CTRL;
  175. clks_init = s2mps11_clks_init;
  176. break;
  177. case S2MPS13X:
  178. s2mps11_reg = S2MPS13_REG_RTCCTRL;
  179. clks_init = s2mps13_clks_init;
  180. break;
  181. case S2MPS14X:
  182. s2mps11_reg = S2MPS14_REG_RTCCTRL;
  183. clks_init = s2mps14_clks_init;
  184. break;
  185. case S5M8767X:
  186. s2mps11_reg = S5M8767_REG_CTRL1;
  187. clks_init = s2mps11_clks_init;
  188. break;
  189. default:
  190. dev_err(&pdev->dev, "Invalid device type\n");
  191. return -EINVAL;
  192. }
  193. /* Store clocks of_node in first element of s2mps11_clks array */
  194. s2mps11_clks->clk_np = s2mps11_clk_parse_dt(pdev, clks_init);
  195. if (IS_ERR(s2mps11_clks->clk_np))
  196. return PTR_ERR(s2mps11_clks->clk_np);
  197. for (i = 0; i < S2MPS11_CLKS_NUM; i++, s2mps11_clk++) {
  198. if (!clks_init[i].name)
  199. continue; /* Skip clocks not present in some devices */
  200. s2mps11_clk->iodev = iodev;
  201. s2mps11_clk->hw.init = &clks_init[i];
  202. s2mps11_clk->mask = 1 << i;
  203. s2mps11_clk->reg = s2mps11_reg;
  204. s2mps11_clk->clk = devm_clk_register(&pdev->dev,
  205. &s2mps11_clk->hw);
  206. if (IS_ERR(s2mps11_clk->clk)) {
  207. dev_err(&pdev->dev, "Fail to register : %s\n",
  208. s2mps11_name(s2mps11_clk));
  209. ret = PTR_ERR(s2mps11_clk->clk);
  210. goto err_reg;
  211. }
  212. s2mps11_clk->lookup = clkdev_alloc(s2mps11_clk->clk,
  213. s2mps11_name(s2mps11_clk), NULL);
  214. if (!s2mps11_clk->lookup) {
  215. ret = -ENOMEM;
  216. goto err_lup;
  217. }
  218. clkdev_add(s2mps11_clk->lookup);
  219. }
  220. for (i = 0; i < S2MPS11_CLKS_NUM; i++) {
  221. /* Skip clocks not present on S2MPS14 */
  222. if (!clks_init[i].name)
  223. continue;
  224. clk_table[i] = s2mps11_clks[i].clk;
  225. }
  226. clk_data.clks = clk_table;
  227. clk_data.clk_num = S2MPS11_CLKS_NUM;
  228. of_clk_add_provider(s2mps11_clks->clk_np, of_clk_src_onecell_get,
  229. &clk_data);
  230. platform_set_drvdata(pdev, s2mps11_clks);
  231. return ret;
  232. err_lup:
  233. devm_clk_unregister(&pdev->dev, s2mps11_clk->clk);
  234. err_reg:
  235. while (s2mps11_clk > s2mps11_clks) {
  236. if (s2mps11_clk->lookup) {
  237. clkdev_drop(s2mps11_clk->lookup);
  238. devm_clk_unregister(&pdev->dev, s2mps11_clk->clk);
  239. }
  240. s2mps11_clk--;
  241. }
  242. return ret;
  243. }
  244. static int s2mps11_clk_remove(struct platform_device *pdev)
  245. {
  246. struct s2mps11_clk *s2mps11_clks = platform_get_drvdata(pdev);
  247. int i;
  248. of_clk_del_provider(s2mps11_clks[0].clk_np);
  249. /* Drop the reference obtained in s2mps11_clk_parse_dt */
  250. of_node_put(s2mps11_clks[0].clk_np);
  251. for (i = 0; i < S2MPS11_CLKS_NUM; i++) {
  252. /* Skip clocks not present on S2MPS14 */
  253. if (!s2mps11_clks[i].lookup)
  254. continue;
  255. clkdev_drop(s2mps11_clks[i].lookup);
  256. }
  257. return 0;
  258. }
  259. static const struct platform_device_id s2mps11_clk_id[] = {
  260. { "s2mps11-clk", S2MPS11X},
  261. { "s2mps13-clk", S2MPS13X},
  262. { "s2mps14-clk", S2MPS14X},
  263. { "s5m8767-clk", S5M8767X},
  264. { },
  265. };
  266. MODULE_DEVICE_TABLE(platform, s2mps11_clk_id);
  267. static struct platform_driver s2mps11_clk_driver = {
  268. .driver = {
  269. .name = "s2mps11-clk",
  270. },
  271. .probe = s2mps11_clk_probe,
  272. .remove = s2mps11_clk_remove,
  273. .id_table = s2mps11_clk_id,
  274. };
  275. static int __init s2mps11_clk_init(void)
  276. {
  277. return platform_driver_register(&s2mps11_clk_driver);
  278. }
  279. subsys_initcall(s2mps11_clk_init);
  280. static void __init s2mps11_clk_cleanup(void)
  281. {
  282. platform_driver_unregister(&s2mps11_clk_driver);
  283. }
  284. module_exit(s2mps11_clk_cleanup);
  285. MODULE_DESCRIPTION("S2MPS11 Clock Driver");
  286. MODULE_AUTHOR("Yadwinder Singh Brar <yadi.brar@samsung.com>");
  287. MODULE_LICENSE("GPL");