clk-s2mps11.c 8.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353
  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. return regmap_update_bits(s2mps11->iodev->regmap_pmic,
  55. s2mps11->reg,
  56. s2mps11->mask, s2mps11->mask);
  57. }
  58. static void s2mps11_clk_unprepare(struct clk_hw *hw)
  59. {
  60. struct s2mps11_clk *s2mps11 = to_s2mps11_clk(hw);
  61. regmap_update_bits(s2mps11->iodev->regmap_pmic, s2mps11->reg,
  62. s2mps11->mask, ~s2mps11->mask);
  63. }
  64. static int s2mps11_clk_is_prepared(struct clk_hw *hw)
  65. {
  66. int ret;
  67. u32 val;
  68. struct s2mps11_clk *s2mps11 = to_s2mps11_clk(hw);
  69. ret = regmap_read(s2mps11->iodev->regmap_pmic,
  70. s2mps11->reg, &val);
  71. if (ret < 0)
  72. return -EINVAL;
  73. return val & s2mps11->mask;
  74. }
  75. static unsigned long s2mps11_clk_recalc_rate(struct clk_hw *hw,
  76. unsigned long parent_rate)
  77. {
  78. return 32768;
  79. }
  80. static struct clk_ops s2mps11_clk_ops = {
  81. .prepare = s2mps11_clk_prepare,
  82. .unprepare = s2mps11_clk_unprepare,
  83. .is_prepared = s2mps11_clk_is_prepared,
  84. .recalc_rate = s2mps11_clk_recalc_rate,
  85. };
  86. static struct clk_init_data s2mps11_clks_init[S2MPS11_CLKS_NUM] = {
  87. [S2MPS11_CLK_AP] = {
  88. .name = "s2mps11_ap",
  89. .ops = &s2mps11_clk_ops,
  90. .flags = CLK_IS_ROOT,
  91. },
  92. [S2MPS11_CLK_CP] = {
  93. .name = "s2mps11_cp",
  94. .ops = &s2mps11_clk_ops,
  95. .flags = CLK_IS_ROOT,
  96. },
  97. [S2MPS11_CLK_BT] = {
  98. .name = "s2mps11_bt",
  99. .ops = &s2mps11_clk_ops,
  100. .flags = CLK_IS_ROOT,
  101. },
  102. };
  103. static struct clk_init_data s2mps13_clks_init[S2MPS11_CLKS_NUM] = {
  104. [S2MPS11_CLK_AP] = {
  105. .name = "s2mps13_ap",
  106. .ops = &s2mps11_clk_ops,
  107. .flags = CLK_IS_ROOT,
  108. },
  109. [S2MPS11_CLK_CP] = {
  110. .name = "s2mps13_cp",
  111. .ops = &s2mps11_clk_ops,
  112. .flags = CLK_IS_ROOT,
  113. },
  114. [S2MPS11_CLK_BT] = {
  115. .name = "s2mps13_bt",
  116. .ops = &s2mps11_clk_ops,
  117. .flags = CLK_IS_ROOT,
  118. },
  119. };
  120. static struct clk_init_data s2mps14_clks_init[S2MPS11_CLKS_NUM] = {
  121. [S2MPS11_CLK_AP] = {
  122. .name = "s2mps14_ap",
  123. .ops = &s2mps11_clk_ops,
  124. .flags = CLK_IS_ROOT,
  125. },
  126. [S2MPS11_CLK_BT] = {
  127. .name = "s2mps14_bt",
  128. .ops = &s2mps11_clk_ops,
  129. .flags = CLK_IS_ROOT,
  130. },
  131. };
  132. static struct device_node *s2mps11_clk_parse_dt(struct platform_device *pdev,
  133. struct clk_init_data *clks_init)
  134. {
  135. struct sec_pmic_dev *iodev = dev_get_drvdata(pdev->dev.parent);
  136. struct device_node *clk_np;
  137. int i;
  138. if (!iodev->dev->of_node)
  139. return ERR_PTR(-EINVAL);
  140. clk_np = of_get_child_by_name(iodev->dev->of_node, "clocks");
  141. if (!clk_np) {
  142. dev_err(&pdev->dev, "could not find clock sub-node\n");
  143. return ERR_PTR(-EINVAL);
  144. }
  145. for (i = 0; i < S2MPS11_CLKS_NUM; i++) {
  146. if (!clks_init[i].name)
  147. continue; /* Skip clocks not present in some devices */
  148. of_property_read_string_index(clk_np, "clock-output-names", i,
  149. &clks_init[i].name);
  150. }
  151. return clk_np;
  152. }
  153. static int s2mps11_clk_probe(struct platform_device *pdev)
  154. {
  155. struct sec_pmic_dev *iodev = dev_get_drvdata(pdev->dev.parent);
  156. struct s2mps11_clk *s2mps11_clks, *s2mps11_clk;
  157. unsigned int s2mps11_reg;
  158. struct clk_init_data *clks_init;
  159. int i, ret = 0;
  160. s2mps11_clks = devm_kcalloc(&pdev->dev, S2MPS11_CLKS_NUM,
  161. sizeof(*s2mps11_clk), GFP_KERNEL);
  162. if (!s2mps11_clks)
  163. return -ENOMEM;
  164. s2mps11_clk = s2mps11_clks;
  165. clk_table = devm_kcalloc(&pdev->dev, S2MPS11_CLKS_NUM,
  166. sizeof(struct clk *), GFP_KERNEL);
  167. if (!clk_table)
  168. return -ENOMEM;
  169. switch(platform_get_device_id(pdev)->driver_data) {
  170. case S2MPS11X:
  171. s2mps11_reg = S2MPS11_REG_RTC_CTRL;
  172. clks_init = s2mps11_clks_init;
  173. break;
  174. case S2MPS13X:
  175. s2mps11_reg = S2MPS13_REG_RTCCTRL;
  176. clks_init = s2mps13_clks_init;
  177. break;
  178. case S2MPS14X:
  179. s2mps11_reg = S2MPS14_REG_RTCCTRL;
  180. clks_init = s2mps14_clks_init;
  181. break;
  182. case S5M8767X:
  183. s2mps11_reg = S5M8767_REG_CTRL1;
  184. clks_init = s2mps11_clks_init;
  185. break;
  186. default:
  187. dev_err(&pdev->dev, "Invalid device type\n");
  188. return -EINVAL;
  189. }
  190. /* Store clocks of_node in first element of s2mps11_clks array */
  191. s2mps11_clks->clk_np = s2mps11_clk_parse_dt(pdev, clks_init);
  192. if (IS_ERR(s2mps11_clks->clk_np))
  193. return PTR_ERR(s2mps11_clks->clk_np);
  194. for (i = 0; i < S2MPS11_CLKS_NUM; i++, s2mps11_clk++) {
  195. if (!clks_init[i].name)
  196. continue; /* Skip clocks not present in some devices */
  197. s2mps11_clk->iodev = iodev;
  198. s2mps11_clk->hw.init = &clks_init[i];
  199. s2mps11_clk->mask = 1 << i;
  200. s2mps11_clk->reg = s2mps11_reg;
  201. s2mps11_clk->clk = devm_clk_register(&pdev->dev,
  202. &s2mps11_clk->hw);
  203. if (IS_ERR(s2mps11_clk->clk)) {
  204. dev_err(&pdev->dev, "Fail to register : %s\n",
  205. s2mps11_name(s2mps11_clk));
  206. ret = PTR_ERR(s2mps11_clk->clk);
  207. goto err_reg;
  208. }
  209. s2mps11_clk->lookup = clkdev_create(s2mps11_clk->clk,
  210. s2mps11_name(s2mps11_clk), NULL);
  211. if (!s2mps11_clk->lookup) {
  212. ret = -ENOMEM;
  213. goto err_reg;
  214. }
  215. }
  216. for (i = 0; i < S2MPS11_CLKS_NUM; i++) {
  217. /* Skip clocks not present on S2MPS14 */
  218. if (!clks_init[i].name)
  219. continue;
  220. clk_table[i] = s2mps11_clks[i].clk;
  221. }
  222. clk_data.clks = clk_table;
  223. clk_data.clk_num = S2MPS11_CLKS_NUM;
  224. of_clk_add_provider(s2mps11_clks->clk_np, of_clk_src_onecell_get,
  225. &clk_data);
  226. platform_set_drvdata(pdev, s2mps11_clks);
  227. return ret;
  228. err_reg:
  229. while (--i >= 0)
  230. clkdev_drop(s2mps11_clks[i].lookup);
  231. return ret;
  232. }
  233. static int s2mps11_clk_remove(struct platform_device *pdev)
  234. {
  235. struct s2mps11_clk *s2mps11_clks = platform_get_drvdata(pdev);
  236. int i;
  237. of_clk_del_provider(s2mps11_clks[0].clk_np);
  238. /* Drop the reference obtained in s2mps11_clk_parse_dt */
  239. of_node_put(s2mps11_clks[0].clk_np);
  240. for (i = 0; i < S2MPS11_CLKS_NUM; i++) {
  241. /* Skip clocks not present on S2MPS14 */
  242. if (!s2mps11_clks[i].lookup)
  243. continue;
  244. clkdev_drop(s2mps11_clks[i].lookup);
  245. }
  246. return 0;
  247. }
  248. static const struct platform_device_id s2mps11_clk_id[] = {
  249. { "s2mps11-clk", S2MPS11X},
  250. { "s2mps13-clk", S2MPS13X},
  251. { "s2mps14-clk", S2MPS14X},
  252. { "s5m8767-clk", S5M8767X},
  253. { },
  254. };
  255. MODULE_DEVICE_TABLE(platform, s2mps11_clk_id);
  256. #ifdef CONFIG_OF
  257. /*
  258. * Device is instantiated through parent MFD device and device matching is done
  259. * through platform_device_id.
  260. *
  261. * However if device's DT node contains proper clock compatible and driver is
  262. * built as a module, then the *module* matching will be done trough DT aliases.
  263. * This requires of_device_id table. In the same time this will not change the
  264. * actual *device* matching so do not add .of_match_table.
  265. */
  266. static const struct of_device_id s2mps11_dt_match[] = {
  267. {
  268. .compatible = "samsung,s2mps11-clk",
  269. .data = (void *)S2MPS11X,
  270. }, {
  271. .compatible = "samsung,s2mps13-clk",
  272. .data = (void *)S2MPS13X,
  273. }, {
  274. .compatible = "samsung,s2mps14-clk",
  275. .data = (void *)S2MPS14X,
  276. }, {
  277. .compatible = "samsung,s5m8767-clk",
  278. .data = (void *)S5M8767X,
  279. }, {
  280. /* Sentinel */
  281. },
  282. };
  283. MODULE_DEVICE_TABLE(of, s2mps11_dt_match);
  284. #endif
  285. static struct platform_driver s2mps11_clk_driver = {
  286. .driver = {
  287. .name = "s2mps11-clk",
  288. },
  289. .probe = s2mps11_clk_probe,
  290. .remove = s2mps11_clk_remove,
  291. .id_table = s2mps11_clk_id,
  292. };
  293. static int __init s2mps11_clk_init(void)
  294. {
  295. return platform_driver_register(&s2mps11_clk_driver);
  296. }
  297. subsys_initcall(s2mps11_clk_init);
  298. static void __exit s2mps11_clk_cleanup(void)
  299. {
  300. platform_driver_unregister(&s2mps11_clk_driver);
  301. }
  302. module_exit(s2mps11_clk_cleanup);
  303. MODULE_DESCRIPTION("S2MPS11 Clock Driver");
  304. MODULE_AUTHOR("Yadwinder Singh Brar <yadi.brar@samsung.com>");
  305. MODULE_LICENSE("GPL");