clk-palmas.c 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307
  1. /*
  2. * Clock driver for Palmas device.
  3. *
  4. * Copyright (c) 2013, NVIDIA Corporation.
  5. * Copyright (c) 2013-2014 Texas Instruments, Inc.
  6. *
  7. * Author: Laxman Dewangan <ldewangan@nvidia.com>
  8. * Peter Ujfalusi <peter.ujfalusi@ti.com>
  9. *
  10. * This program is free software; you can redistribute it and/or
  11. * modify it under the terms of the GNU General Public License as
  12. * published by the Free Software Foundation version 2.
  13. *
  14. * This program is distributed "as is" WITHOUT ANY WARRANTY of any kind,
  15. * whether express or implied; without even the implied warranty of
  16. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  17. * General Public License for more details.
  18. */
  19. #include <linux/clk.h>
  20. #include <linux/clkdev.h>
  21. #include <linux/clk-provider.h>
  22. #include <linux/mfd/palmas.h>
  23. #include <linux/module.h>
  24. #include <linux/of.h>
  25. #include <linux/of_device.h>
  26. #include <linux/platform_device.h>
  27. #include <linux/slab.h>
  28. #define PALMAS_CLOCK_DT_EXT_CONTROL_ENABLE1 1
  29. #define PALMAS_CLOCK_DT_EXT_CONTROL_ENABLE2 2
  30. #define PALMAS_CLOCK_DT_EXT_CONTROL_NSLEEP 3
  31. struct palmas_clk32k_desc {
  32. const char *clk_name;
  33. unsigned int control_reg;
  34. unsigned int enable_mask;
  35. unsigned int sleep_mask;
  36. unsigned int sleep_reqstr_id;
  37. int delay;
  38. };
  39. struct palmas_clock_info {
  40. struct device *dev;
  41. struct clk *clk;
  42. struct clk_hw hw;
  43. struct palmas *palmas;
  44. struct palmas_clk32k_desc *clk_desc;
  45. int ext_control_pin;
  46. };
  47. static inline struct palmas_clock_info *to_palmas_clks_info(struct clk_hw *hw)
  48. {
  49. return container_of(hw, struct palmas_clock_info, hw);
  50. }
  51. static unsigned long palmas_clks_recalc_rate(struct clk_hw *hw,
  52. unsigned long parent_rate)
  53. {
  54. return 32768;
  55. }
  56. static int palmas_clks_prepare(struct clk_hw *hw)
  57. {
  58. struct palmas_clock_info *cinfo = to_palmas_clks_info(hw);
  59. int ret;
  60. ret = palmas_update_bits(cinfo->palmas, PALMAS_RESOURCE_BASE,
  61. cinfo->clk_desc->control_reg,
  62. cinfo->clk_desc->enable_mask,
  63. cinfo->clk_desc->enable_mask);
  64. if (ret < 0)
  65. dev_err(cinfo->dev, "Reg 0x%02x update failed, %d\n",
  66. cinfo->clk_desc->control_reg, ret);
  67. else if (cinfo->clk_desc->delay)
  68. udelay(cinfo->clk_desc->delay);
  69. return ret;
  70. }
  71. static void palmas_clks_unprepare(struct clk_hw *hw)
  72. {
  73. struct palmas_clock_info *cinfo = to_palmas_clks_info(hw);
  74. int ret;
  75. /*
  76. * Clock can be disabled through external pin if it is externally
  77. * controlled.
  78. */
  79. if (cinfo->ext_control_pin)
  80. return;
  81. ret = palmas_update_bits(cinfo->palmas, PALMAS_RESOURCE_BASE,
  82. cinfo->clk_desc->control_reg,
  83. cinfo->clk_desc->enable_mask, 0);
  84. if (ret < 0)
  85. dev_err(cinfo->dev, "Reg 0x%02x update failed, %d\n",
  86. cinfo->clk_desc->control_reg, ret);
  87. }
  88. static int palmas_clks_is_prepared(struct clk_hw *hw)
  89. {
  90. struct palmas_clock_info *cinfo = to_palmas_clks_info(hw);
  91. int ret;
  92. u32 val;
  93. if (cinfo->ext_control_pin)
  94. return 1;
  95. ret = palmas_read(cinfo->palmas, PALMAS_RESOURCE_BASE,
  96. cinfo->clk_desc->control_reg, &val);
  97. if (ret < 0) {
  98. dev_err(cinfo->dev, "Reg 0x%02x read failed, %d\n",
  99. cinfo->clk_desc->control_reg, ret);
  100. return ret;
  101. }
  102. return !!(val & cinfo->clk_desc->enable_mask);
  103. }
  104. static struct clk_ops palmas_clks_ops = {
  105. .prepare = palmas_clks_prepare,
  106. .unprepare = palmas_clks_unprepare,
  107. .is_prepared = palmas_clks_is_prepared,
  108. .recalc_rate = palmas_clks_recalc_rate,
  109. };
  110. struct palmas_clks_of_match_data {
  111. struct clk_init_data init;
  112. struct palmas_clk32k_desc desc;
  113. };
  114. static struct palmas_clks_of_match_data palmas_of_clk32kg = {
  115. .init = {
  116. .name = "clk32kg",
  117. .ops = &palmas_clks_ops,
  118. .flags = CLK_IS_ROOT | CLK_IGNORE_UNUSED,
  119. },
  120. .desc = {
  121. .clk_name = "clk32kg",
  122. .control_reg = PALMAS_CLK32KG_CTRL,
  123. .enable_mask = PALMAS_CLK32KG_CTRL_MODE_ACTIVE,
  124. .sleep_mask = PALMAS_CLK32KG_CTRL_MODE_SLEEP,
  125. .sleep_reqstr_id = PALMAS_EXTERNAL_REQSTR_ID_CLK32KG,
  126. .delay = 200,
  127. },
  128. };
  129. static struct palmas_clks_of_match_data palmas_of_clk32kgaudio = {
  130. .init = {
  131. .name = "clk32kgaudio",
  132. .ops = &palmas_clks_ops,
  133. .flags = CLK_IS_ROOT | CLK_IGNORE_UNUSED,
  134. },
  135. .desc = {
  136. .clk_name = "clk32kgaudio",
  137. .control_reg = PALMAS_CLK32KGAUDIO_CTRL,
  138. .enable_mask = PALMAS_CLK32KG_CTRL_MODE_ACTIVE,
  139. .sleep_mask = PALMAS_CLK32KG_CTRL_MODE_SLEEP,
  140. .sleep_reqstr_id = PALMAS_EXTERNAL_REQSTR_ID_CLK32KGAUDIO,
  141. .delay = 200,
  142. },
  143. };
  144. static const struct of_device_id palmas_clks_of_match[] = {
  145. {
  146. .compatible = "ti,palmas-clk32kg",
  147. .data = &palmas_of_clk32kg,
  148. },
  149. {
  150. .compatible = "ti,palmas-clk32kgaudio",
  151. .data = &palmas_of_clk32kgaudio,
  152. },
  153. { },
  154. };
  155. MODULE_DEVICE_TABLE(of, palmas_clks_of_match);
  156. static void palmas_clks_get_clk_data(struct platform_device *pdev,
  157. struct palmas_clock_info *cinfo)
  158. {
  159. struct device_node *node = pdev->dev.of_node;
  160. unsigned int prop;
  161. int ret;
  162. ret = of_property_read_u32(node, "ti,external-sleep-control",
  163. &prop);
  164. if (ret)
  165. return;
  166. switch (prop) {
  167. case PALMAS_CLOCK_DT_EXT_CONTROL_ENABLE1:
  168. prop = PALMAS_EXT_CONTROL_ENABLE1;
  169. break;
  170. case PALMAS_CLOCK_DT_EXT_CONTROL_ENABLE2:
  171. prop = PALMAS_EXT_CONTROL_ENABLE2;
  172. break;
  173. case PALMAS_CLOCK_DT_EXT_CONTROL_NSLEEP:
  174. prop = PALMAS_EXT_CONTROL_NSLEEP;
  175. break;
  176. default:
  177. dev_warn(&pdev->dev, "%s: Invalid ext control option: %u\n",
  178. node->name, prop);
  179. prop = 0;
  180. break;
  181. }
  182. cinfo->ext_control_pin = prop;
  183. }
  184. static int palmas_clks_init_configure(struct palmas_clock_info *cinfo)
  185. {
  186. int ret;
  187. ret = palmas_update_bits(cinfo->palmas, PALMAS_RESOURCE_BASE,
  188. cinfo->clk_desc->control_reg,
  189. cinfo->clk_desc->sleep_mask, 0);
  190. if (ret < 0) {
  191. dev_err(cinfo->dev, "Reg 0x%02x update failed, %d\n",
  192. cinfo->clk_desc->control_reg, ret);
  193. return ret;
  194. }
  195. if (cinfo->ext_control_pin) {
  196. ret = clk_prepare(cinfo->clk);
  197. if (ret < 0) {
  198. dev_err(cinfo->dev, "Clock prep failed, %d\n", ret);
  199. return ret;
  200. }
  201. ret = palmas_ext_control_req_config(cinfo->palmas,
  202. cinfo->clk_desc->sleep_reqstr_id,
  203. cinfo->ext_control_pin, true);
  204. if (ret < 0) {
  205. dev_err(cinfo->dev, "Ext config for %s failed, %d\n",
  206. cinfo->clk_desc->clk_name, ret);
  207. return ret;
  208. }
  209. }
  210. return ret;
  211. }
  212. static int palmas_clks_probe(struct platform_device *pdev)
  213. {
  214. struct palmas *palmas = dev_get_drvdata(pdev->dev.parent);
  215. struct device_node *node = pdev->dev.of_node;
  216. struct palmas_clks_of_match_data *match_data;
  217. const struct of_device_id *match;
  218. struct palmas_clock_info *cinfo;
  219. struct clk *clk;
  220. int ret;
  221. match = of_match_device(palmas_clks_of_match, &pdev->dev);
  222. match_data = (struct palmas_clks_of_match_data *)match->data;
  223. cinfo = devm_kzalloc(&pdev->dev, sizeof(*cinfo), GFP_KERNEL);
  224. if (!cinfo)
  225. return -ENOMEM;
  226. palmas_clks_get_clk_data(pdev, cinfo);
  227. platform_set_drvdata(pdev, cinfo);
  228. cinfo->dev = &pdev->dev;
  229. cinfo->palmas = palmas;
  230. cinfo->clk_desc = &match_data->desc;
  231. cinfo->hw.init = &match_data->init;
  232. clk = devm_clk_register(&pdev->dev, &cinfo->hw);
  233. if (IS_ERR(clk)) {
  234. ret = PTR_ERR(clk);
  235. dev_err(&pdev->dev, "Fail to register clock %s, %d\n",
  236. match_data->desc.clk_name, ret);
  237. return ret;
  238. }
  239. cinfo->clk = clk;
  240. ret = palmas_clks_init_configure(cinfo);
  241. if (ret < 0) {
  242. dev_err(&pdev->dev, "Clock config failed, %d\n", ret);
  243. return ret;
  244. }
  245. ret = of_clk_add_provider(node, of_clk_src_simple_get, cinfo->clk);
  246. if (ret < 0)
  247. dev_err(&pdev->dev, "Fail to add clock driver, %d\n", ret);
  248. return ret;
  249. }
  250. static int palmas_clks_remove(struct platform_device *pdev)
  251. {
  252. of_clk_del_provider(pdev->dev.of_node);
  253. return 0;
  254. }
  255. static struct platform_driver palmas_clks_driver = {
  256. .driver = {
  257. .name = "palmas-clk",
  258. .of_match_table = palmas_clks_of_match,
  259. },
  260. .probe = palmas_clks_probe,
  261. .remove = palmas_clks_remove,
  262. };
  263. module_platform_driver(palmas_clks_driver);
  264. MODULE_DESCRIPTION("Clock driver for Palmas Series Devices");
  265. MODULE_ALIAS("platform:palmas-clk");
  266. MODULE_AUTHOR("Peter Ujfalusi <peter.ujfalusi@ti.com>");
  267. MODULE_LICENSE("GPL v2");