137-clk-qcom-Add-support-for-High-Frequency-PLLs-HFPLLs.patch 9.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351
  1. Content-Type: text/plain; charset="utf-8"
  2. MIME-Version: 1.0
  3. Content-Transfer-Encoding: 7bit
  4. Subject: [v3,05/13] clk: qcom: Add support for High-Frequency PLLs (HFPLLs)
  5. From: Stephen Boyd <sboyd@codeaurora.org>
  6. X-Patchwork-Id: 6063261
  7. Message-Id: <1426920332-9340-6-git-send-email-sboyd@codeaurora.org>
  8. To: Mike Turquette <mturquette@linaro.org>, Stephen Boyd <sboyd@codeaurora.org>
  9. Cc: linux-kernel@vger.kernel.org, linux-arm-msm@vger.kernel.org,
  10. linux-pm@vger.kernel.org, linux-arm-kernel@lists.infradead.org,
  11. Viresh Kumar <viresh.kumar@linaro.org>
  12. Date: Fri, 20 Mar 2015 23:45:24 -0700
  13. HFPLLs are the main frequency source for Krait CPU clocks. Add
  14. support for changing the rate of these PLLs.
  15. Signed-off-by: Stephen Boyd <sboyd@codeaurora.org>
  16. ---
  17. I'd really like to get rid of __clk_hfpll_init_once() if possible...
  18. drivers/clk/qcom/Makefile | 1 +
  19. drivers/clk/qcom/clk-hfpll.c | 253 +++++++++++++++++++++++++++++++++++++++++++
  20. drivers/clk/qcom/clk-hfpll.h | 54 +++++++++
  21. 3 files changed, 308 insertions(+)
  22. create mode 100644 drivers/clk/qcom/clk-hfpll.c
  23. create mode 100644 drivers/clk/qcom/clk-hfpll.h
  24. --- a/drivers/clk/qcom/Makefile
  25. +++ b/drivers/clk/qcom/Makefile
  26. @@ -6,6 +6,7 @@ clk-qcom-y += clk-pll.o
  27. clk-qcom-y += clk-rcg.o
  28. clk-qcom-y += clk-rcg2.o
  29. clk-qcom-y += clk-branch.o
  30. +clk-qcom-y += clk-hfpll.o
  31. clk-qcom-y += reset.o
  32. obj-$(CONFIG_APQ_GCC_8084) += gcc-apq8084.o
  33. --- /dev/null
  34. +++ b/drivers/clk/qcom/clk-hfpll.c
  35. @@ -0,0 +1,253 @@
  36. +/*
  37. + * Copyright (c) 2013-2014, The Linux Foundation. All rights reserved.
  38. + *
  39. + * This program is free software; you can redistribute it and/or modify
  40. + * it under the terms of the GNU General Public License version 2 and
  41. + * only version 2 as published by the Free Software Foundation.
  42. + *
  43. + * This program is distributed in the hope that it will be useful,
  44. + * but WITHOUT ANY WARRANTY; without even the implied warranty of
  45. + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  46. + * GNU General Public License for more details.
  47. + */
  48. +#include <linux/kernel.h>
  49. +#include <linux/export.h>
  50. +#include <linux/regmap.h>
  51. +#include <linux/delay.h>
  52. +#include <linux/err.h>
  53. +#include <linux/clk-provider.h>
  54. +#include <linux/spinlock.h>
  55. +
  56. +#include "clk-regmap.h"
  57. +#include "clk-hfpll.h"
  58. +
  59. +#define PLL_OUTCTRL BIT(0)
  60. +#define PLL_BYPASSNL BIT(1)
  61. +#define PLL_RESET_N BIT(2)
  62. +
  63. +/* Initialize a HFPLL at a given rate and enable it. */
  64. +static void __clk_hfpll_init_once(struct clk_hw *hw)
  65. +{
  66. + struct clk_hfpll *h = to_clk_hfpll(hw);
  67. + struct hfpll_data const *hd = h->d;
  68. + struct regmap *regmap = h->clkr.regmap;
  69. +
  70. + if (likely(h->init_done))
  71. + return;
  72. +
  73. + /* Configure PLL parameters for integer mode. */
  74. + if (hd->config_val)
  75. + regmap_write(regmap, hd->config_reg, hd->config_val);
  76. + regmap_write(regmap, hd->m_reg, 0);
  77. + regmap_write(regmap, hd->n_reg, 1);
  78. +
  79. + if (hd->user_reg) {
  80. + u32 regval = hd->user_val;
  81. + unsigned long rate;
  82. +
  83. + rate = __clk_get_rate(hw->clk);
  84. +
  85. + /* Pick the right VCO. */
  86. + if (hd->user_vco_mask && rate > hd->low_vco_max_rate)
  87. + regval |= hd->user_vco_mask;
  88. + regmap_write(regmap, hd->user_reg, regval);
  89. + }
  90. +
  91. + if (hd->droop_reg)
  92. + regmap_write(regmap, hd->droop_reg, hd->droop_val);
  93. +
  94. + h->init_done = true;
  95. +}
  96. +
  97. +static void __clk_hfpll_enable(struct clk_hw *hw)
  98. +{
  99. + struct clk_hfpll *h = to_clk_hfpll(hw);
  100. + struct hfpll_data const *hd = h->d;
  101. + struct regmap *regmap = h->clkr.regmap;
  102. + u32 val;
  103. +
  104. + __clk_hfpll_init_once(hw);
  105. +
  106. + /* Disable PLL bypass mode. */
  107. + regmap_update_bits(regmap, hd->mode_reg, PLL_BYPASSNL, PLL_BYPASSNL);
  108. +
  109. + /*
  110. + * H/W requires a 5us delay between disabling the bypass and
  111. + * de-asserting the reset. Delay 10us just to be safe.
  112. + */
  113. + udelay(10);
  114. +
  115. + /* De-assert active-low PLL reset. */
  116. + regmap_update_bits(regmap, hd->mode_reg, PLL_RESET_N, PLL_RESET_N);
  117. +
  118. + /* Wait for PLL to lock. */
  119. + if (hd->status_reg) {
  120. + do {
  121. + regmap_read(regmap, hd->status_reg, &val);
  122. + } while (!(val & BIT(hd->lock_bit)));
  123. + } else {
  124. + udelay(60);
  125. + }
  126. +
  127. + /* Enable PLL output. */
  128. + regmap_update_bits(regmap, hd->mode_reg, PLL_OUTCTRL, PLL_OUTCTRL);
  129. +}
  130. +
  131. +/* Enable an already-configured HFPLL. */
  132. +static int clk_hfpll_enable(struct clk_hw *hw)
  133. +{
  134. + unsigned long flags;
  135. + struct clk_hfpll *h = to_clk_hfpll(hw);
  136. + struct hfpll_data const *hd = h->d;
  137. + struct regmap *regmap = h->clkr.regmap;
  138. + u32 mode;
  139. +
  140. + spin_lock_irqsave(&h->lock, flags);
  141. + regmap_read(regmap, hd->mode_reg, &mode);
  142. + if (!(mode & (PLL_BYPASSNL | PLL_RESET_N | PLL_OUTCTRL)))
  143. + __clk_hfpll_enable(hw);
  144. + spin_unlock_irqrestore(&h->lock, flags);
  145. +
  146. + return 0;
  147. +}
  148. +
  149. +static void __clk_hfpll_disable(struct clk_hfpll *h)
  150. +{
  151. + struct hfpll_data const *hd = h->d;
  152. + struct regmap *regmap = h->clkr.regmap;
  153. +
  154. + /*
  155. + * Disable the PLL output, disable test mode, enable the bypass mode,
  156. + * and assert the reset.
  157. + */
  158. + regmap_update_bits(regmap, hd->mode_reg,
  159. + PLL_BYPASSNL | PLL_RESET_N | PLL_OUTCTRL, 0);
  160. +}
  161. +
  162. +static void clk_hfpll_disable(struct clk_hw *hw)
  163. +{
  164. + struct clk_hfpll *h = to_clk_hfpll(hw);
  165. + unsigned long flags;
  166. +
  167. + spin_lock_irqsave(&h->lock, flags);
  168. + __clk_hfpll_disable(h);
  169. + spin_unlock_irqrestore(&h->lock, flags);
  170. +}
  171. +
  172. +static long clk_hfpll_round_rate(struct clk_hw *hw, unsigned long rate,
  173. + unsigned long *parent_rate)
  174. +{
  175. + struct clk_hfpll *h = to_clk_hfpll(hw);
  176. + struct hfpll_data const *hd = h->d;
  177. + unsigned long rrate;
  178. +
  179. + rate = clamp(rate, hd->min_rate, hd->max_rate);
  180. +
  181. + rrate = DIV_ROUND_UP(rate, *parent_rate) * *parent_rate;
  182. + if (rrate > hd->max_rate)
  183. + rrate -= *parent_rate;
  184. +
  185. + return rrate;
  186. +}
  187. +
  188. +/*
  189. + * For optimization reasons, assumes no downstream clocks are actively using
  190. + * it.
  191. + */
  192. +static int clk_hfpll_set_rate(struct clk_hw *hw, unsigned long rate,
  193. + unsigned long parent_rate)
  194. +{
  195. + struct clk_hfpll *h = to_clk_hfpll(hw);
  196. + struct hfpll_data const *hd = h->d;
  197. + struct regmap *regmap = h->clkr.regmap;
  198. + unsigned long flags;
  199. + u32 l_val, val;
  200. + bool enabled;
  201. +
  202. + l_val = rate / parent_rate;
  203. +
  204. + spin_lock_irqsave(&h->lock, flags);
  205. +
  206. + enabled = __clk_is_enabled(hw->clk);
  207. + if (enabled)
  208. + __clk_hfpll_disable(h);
  209. +
  210. + /* Pick the right VCO. */
  211. + if (hd->user_reg && hd->user_vco_mask) {
  212. + regmap_read(regmap, hd->user_reg, &val);
  213. + if (rate <= hd->low_vco_max_rate)
  214. + val &= ~hd->user_vco_mask;
  215. + else
  216. + val |= hd->user_vco_mask;
  217. + regmap_write(regmap, hd->user_reg, val);
  218. + }
  219. +
  220. + regmap_write(regmap, hd->l_reg, l_val);
  221. +
  222. + if (enabled)
  223. + __clk_hfpll_enable(hw);
  224. +
  225. + spin_unlock_irqrestore(&h->lock, flags);
  226. +
  227. + return 0;
  228. +}
  229. +
  230. +static unsigned long clk_hfpll_recalc_rate(struct clk_hw *hw,
  231. + unsigned long parent_rate)
  232. +{
  233. + struct clk_hfpll *h = to_clk_hfpll(hw);
  234. + struct hfpll_data const *hd = h->d;
  235. + struct regmap *regmap = h->clkr.regmap;
  236. + u32 l_val;
  237. +
  238. + regmap_read(regmap, hd->l_reg, &l_val);
  239. +
  240. + return l_val * parent_rate;
  241. +}
  242. +
  243. +static void clk_hfpll_init(struct clk_hw *hw)
  244. +{
  245. + struct clk_hfpll *h = to_clk_hfpll(hw);
  246. + struct hfpll_data const *hd = h->d;
  247. + struct regmap *regmap = h->clkr.regmap;
  248. + u32 mode, status;
  249. +
  250. + regmap_read(regmap, hd->mode_reg, &mode);
  251. + if (mode != (PLL_BYPASSNL | PLL_RESET_N | PLL_OUTCTRL)) {
  252. + __clk_hfpll_init_once(hw);
  253. + return;
  254. + }
  255. +
  256. + if (hd->status_reg) {
  257. + regmap_read(regmap, hd->status_reg, &status);
  258. + if (!(status & BIT(hd->lock_bit))) {
  259. + WARN(1, "HFPLL %s is ON, but not locked!\n",
  260. + __clk_get_name(hw->clk));
  261. + clk_hfpll_disable(hw);
  262. + __clk_hfpll_init_once(hw);
  263. + }
  264. + }
  265. +}
  266. +
  267. +static int hfpll_is_enabled(struct clk_hw *hw)
  268. +{
  269. + struct clk_hfpll *h = to_clk_hfpll(hw);
  270. + struct hfpll_data const *hd = h->d;
  271. + struct regmap *regmap = h->clkr.regmap;
  272. + u32 mode;
  273. +
  274. + regmap_read(regmap, hd->mode_reg, &mode);
  275. + mode &= 0x7;
  276. + return mode == (PLL_BYPASSNL | PLL_RESET_N | PLL_OUTCTRL);
  277. +}
  278. +
  279. +const struct clk_ops clk_ops_hfpll = {
  280. + .enable = clk_hfpll_enable,
  281. + .disable = clk_hfpll_disable,
  282. + .is_enabled = hfpll_is_enabled,
  283. + .round_rate = clk_hfpll_round_rate,
  284. + .set_rate = clk_hfpll_set_rate,
  285. + .recalc_rate = clk_hfpll_recalc_rate,
  286. + .init = clk_hfpll_init,
  287. +};
  288. +EXPORT_SYMBOL_GPL(clk_ops_hfpll);
  289. --- /dev/null
  290. +++ b/drivers/clk/qcom/clk-hfpll.h
  291. @@ -0,0 +1,54 @@
  292. +/*
  293. + * Copyright (c) 2013-2014, The Linux Foundation. All rights reserved.
  294. + *
  295. + * This program is free software; you can redistribute it and/or modify
  296. + * it under the terms of the GNU General Public License version 2 and
  297. + * only version 2 as published by the Free Software Foundation.
  298. + *
  299. + * This program is distributed in the hope that it will be useful,
  300. + * but WITHOUT ANY WARRANTY; without even the implied warranty of
  301. + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  302. + * GNU General Public License for more details.
  303. + */
  304. +#ifndef __QCOM_CLK_HFPLL_H__
  305. +#define __QCOM_CLK_HFPLL_H__
  306. +
  307. +#include <linux/clk-provider.h>
  308. +#include <linux/spinlock.h>
  309. +#include "clk-regmap.h"
  310. +
  311. +struct hfpll_data {
  312. + u32 mode_reg;
  313. + u32 l_reg;
  314. + u32 m_reg;
  315. + u32 n_reg;
  316. + u32 user_reg;
  317. + u32 droop_reg;
  318. + u32 config_reg;
  319. + u32 status_reg;
  320. + u8 lock_bit;
  321. +
  322. + u32 droop_val;
  323. + u32 config_val;
  324. + u32 user_val;
  325. + u32 user_vco_mask;
  326. + unsigned long low_vco_max_rate;
  327. +
  328. + unsigned long min_rate;
  329. + unsigned long max_rate;
  330. +};
  331. +
  332. +struct clk_hfpll {
  333. + struct hfpll_data const *d;
  334. + int init_done;
  335. +
  336. + struct clk_regmap clkr;
  337. + spinlock_t lock;
  338. +};
  339. +
  340. +#define to_clk_hfpll(_hw) \
  341. + container_of(to_clk_regmap(_hw), struct clk_hfpll, clkr)
  342. +
  343. +extern const struct clk_ops clk_ops_hfpll;
  344. +
  345. +#endif