ccu-sun6i-rtc.c 9.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382
  1. // SPDX-License-Identifier: GPL-2.0-only
  2. //
  3. // Copyright (c) 2021 Samuel Holland <[email protected]>
  4. //
  5. #include <linux/clk.h>
  6. #include <linux/clk-provider.h>
  7. #include <linux/io.h>
  8. #include <linux/module.h>
  9. #include <linux/of_device.h>
  10. #include <linux/clk/sunxi-ng.h>
  11. #include "ccu_common.h"
  12. #include "ccu_div.h"
  13. #include "ccu_gate.h"
  14. #include "ccu_mux.h"
  15. #include "ccu-sun6i-rtc.h"
  16. #define IOSC_ACCURACY 300000000 /* 30% */
  17. #define IOSC_RATE 16000000
  18. #define LOSC_RATE 32768
  19. #define LOSC_RATE_SHIFT 15
  20. #define LOSC_CTRL_REG 0x0
  21. #define LOSC_CTRL_KEY 0x16aa0000
  22. #define IOSC_32K_CLK_DIV_REG 0x8
  23. #define IOSC_32K_CLK_DIV GENMASK(4, 0)
  24. #define IOSC_32K_PRE_DIV 32
  25. #define IOSC_CLK_CALI_REG 0xc
  26. #define IOSC_CLK_CALI_DIV_ONES 22
  27. #define IOSC_CLK_CALI_EN BIT(1)
  28. #define IOSC_CLK_CALI_SRC_SEL BIT(0)
  29. #define LOSC_OUT_GATING_REG 0x60
  30. #define DCXO_CTRL_REG 0x160
  31. #define DCXO_CTRL_CLK16M_RC_EN BIT(0)
  32. struct sun6i_rtc_match_data {
  33. bool have_ext_osc32k : 1;
  34. bool have_iosc_calibration : 1;
  35. bool rtc_32k_single_parent : 1;
  36. const struct clk_parent_data *osc32k_fanout_parents;
  37. u8 osc32k_fanout_nparents;
  38. };
  39. static bool have_iosc_calibration;
  40. static int ccu_iosc_enable(struct clk_hw *hw)
  41. {
  42. struct ccu_common *cm = hw_to_ccu_common(hw);
  43. return ccu_gate_helper_enable(cm, DCXO_CTRL_CLK16M_RC_EN);
  44. }
  45. static void ccu_iosc_disable(struct clk_hw *hw)
  46. {
  47. struct ccu_common *cm = hw_to_ccu_common(hw);
  48. return ccu_gate_helper_disable(cm, DCXO_CTRL_CLK16M_RC_EN);
  49. }
  50. static int ccu_iosc_is_enabled(struct clk_hw *hw)
  51. {
  52. struct ccu_common *cm = hw_to_ccu_common(hw);
  53. return ccu_gate_helper_is_enabled(cm, DCXO_CTRL_CLK16M_RC_EN);
  54. }
  55. static unsigned long ccu_iosc_recalc_rate(struct clk_hw *hw,
  56. unsigned long parent_rate)
  57. {
  58. struct ccu_common *cm = hw_to_ccu_common(hw);
  59. if (have_iosc_calibration) {
  60. u32 reg = readl(cm->base + IOSC_CLK_CALI_REG);
  61. /*
  62. * Recover the IOSC frequency by shifting the ones place of
  63. * (fixed-point divider * 32768) into bit zero.
  64. */
  65. if (reg & IOSC_CLK_CALI_EN)
  66. return reg >> (IOSC_CLK_CALI_DIV_ONES - LOSC_RATE_SHIFT);
  67. }
  68. return IOSC_RATE;
  69. }
  70. static unsigned long ccu_iosc_recalc_accuracy(struct clk_hw *hw,
  71. unsigned long parent_accuracy)
  72. {
  73. return IOSC_ACCURACY;
  74. }
  75. static const struct clk_ops ccu_iosc_ops = {
  76. .enable = ccu_iosc_enable,
  77. .disable = ccu_iosc_disable,
  78. .is_enabled = ccu_iosc_is_enabled,
  79. .recalc_rate = ccu_iosc_recalc_rate,
  80. .recalc_accuracy = ccu_iosc_recalc_accuracy,
  81. };
  82. static struct ccu_common iosc_clk = {
  83. .reg = DCXO_CTRL_REG,
  84. .hw.init = CLK_HW_INIT_NO_PARENT("iosc", &ccu_iosc_ops,
  85. CLK_GET_RATE_NOCACHE),
  86. };
  87. static int ccu_iosc_32k_prepare(struct clk_hw *hw)
  88. {
  89. struct ccu_common *cm = hw_to_ccu_common(hw);
  90. u32 val;
  91. if (!have_iosc_calibration)
  92. return 0;
  93. val = readl(cm->base + IOSC_CLK_CALI_REG);
  94. writel(val | IOSC_CLK_CALI_EN | IOSC_CLK_CALI_SRC_SEL,
  95. cm->base + IOSC_CLK_CALI_REG);
  96. return 0;
  97. }
  98. static void ccu_iosc_32k_unprepare(struct clk_hw *hw)
  99. {
  100. struct ccu_common *cm = hw_to_ccu_common(hw);
  101. u32 val;
  102. if (!have_iosc_calibration)
  103. return;
  104. val = readl(cm->base + IOSC_CLK_CALI_REG);
  105. writel(val & ~(IOSC_CLK_CALI_EN | IOSC_CLK_CALI_SRC_SEL),
  106. cm->base + IOSC_CLK_CALI_REG);
  107. }
  108. static unsigned long ccu_iosc_32k_recalc_rate(struct clk_hw *hw,
  109. unsigned long parent_rate)
  110. {
  111. struct ccu_common *cm = hw_to_ccu_common(hw);
  112. u32 val;
  113. if (have_iosc_calibration) {
  114. val = readl(cm->base + IOSC_CLK_CALI_REG);
  115. /* Assume the calibrated 32k clock is accurate. */
  116. if (val & IOSC_CLK_CALI_SRC_SEL)
  117. return LOSC_RATE;
  118. }
  119. val = readl(cm->base + IOSC_32K_CLK_DIV_REG) & IOSC_32K_CLK_DIV;
  120. return parent_rate / IOSC_32K_PRE_DIV / (val + 1);
  121. }
  122. static unsigned long ccu_iosc_32k_recalc_accuracy(struct clk_hw *hw,
  123. unsigned long parent_accuracy)
  124. {
  125. struct ccu_common *cm = hw_to_ccu_common(hw);
  126. u32 val;
  127. if (have_iosc_calibration) {
  128. val = readl(cm->base + IOSC_CLK_CALI_REG);
  129. /* Assume the calibrated 32k clock is accurate. */
  130. if (val & IOSC_CLK_CALI_SRC_SEL)
  131. return 0;
  132. }
  133. return parent_accuracy;
  134. }
  135. static const struct clk_ops ccu_iosc_32k_ops = {
  136. .prepare = ccu_iosc_32k_prepare,
  137. .unprepare = ccu_iosc_32k_unprepare,
  138. .recalc_rate = ccu_iosc_32k_recalc_rate,
  139. .recalc_accuracy = ccu_iosc_32k_recalc_accuracy,
  140. };
  141. static struct ccu_common iosc_32k_clk = {
  142. .hw.init = CLK_HW_INIT_HW("iosc-32k", &iosc_clk.hw,
  143. &ccu_iosc_32k_ops,
  144. CLK_GET_RATE_NOCACHE),
  145. };
  146. static const struct clk_hw *ext_osc32k[] = { NULL }; /* updated during probe */
  147. static SUNXI_CCU_GATE_HWS(ext_osc32k_gate_clk, "ext-osc32k-gate",
  148. ext_osc32k, 0x0, BIT(4), 0);
  149. static const struct clk_hw *osc32k_parents[] = {
  150. &iosc_32k_clk.hw,
  151. &ext_osc32k_gate_clk.common.hw
  152. };
  153. static struct clk_init_data osc32k_init_data = {
  154. .name = "osc32k",
  155. .ops = &ccu_mux_ops,
  156. .parent_hws = osc32k_parents,
  157. .num_parents = ARRAY_SIZE(osc32k_parents), /* updated during probe */
  158. };
  159. static struct ccu_mux osc32k_clk = {
  160. .mux = _SUNXI_CCU_MUX(0, 1),
  161. .common = {
  162. .reg = LOSC_CTRL_REG,
  163. .features = CCU_FEATURE_KEY_FIELD,
  164. .hw.init = &osc32k_init_data,
  165. },
  166. };
  167. /* This falls back to the global name for fwnodes without a named reference. */
  168. static const struct clk_parent_data osc24M[] = {
  169. { .fw_name = "hosc", .name = "osc24M" }
  170. };
  171. static struct ccu_gate osc24M_32k_clk = {
  172. .enable = BIT(16),
  173. .common = {
  174. .reg = LOSC_OUT_GATING_REG,
  175. .prediv = 750,
  176. .features = CCU_FEATURE_ALL_PREDIV,
  177. .hw.init = CLK_HW_INIT_PARENTS_DATA("osc24M-32k", osc24M,
  178. &ccu_gate_ops, 0),
  179. },
  180. };
  181. static const struct clk_hw *rtc_32k_parents[] = {
  182. &osc32k_clk.common.hw,
  183. &osc24M_32k_clk.common.hw
  184. };
  185. static struct clk_init_data rtc_32k_init_data = {
  186. .name = "rtc-32k",
  187. .ops = &ccu_mux_ops,
  188. .parent_hws = rtc_32k_parents,
  189. .num_parents = ARRAY_SIZE(rtc_32k_parents), /* updated during probe */
  190. .flags = CLK_IS_CRITICAL,
  191. };
  192. static struct ccu_mux rtc_32k_clk = {
  193. .mux = _SUNXI_CCU_MUX(1, 1),
  194. .common = {
  195. .reg = LOSC_CTRL_REG,
  196. .features = CCU_FEATURE_KEY_FIELD,
  197. .hw.init = &rtc_32k_init_data,
  198. },
  199. };
  200. static struct clk_init_data osc32k_fanout_init_data = {
  201. .name = "osc32k-fanout",
  202. .ops = &ccu_mux_ops,
  203. /* parents are set during probe */
  204. };
  205. static struct ccu_mux osc32k_fanout_clk = {
  206. .enable = BIT(0),
  207. .mux = _SUNXI_CCU_MUX(1, 2),
  208. .common = {
  209. .reg = LOSC_OUT_GATING_REG,
  210. .hw.init = &osc32k_fanout_init_data,
  211. },
  212. };
  213. static struct ccu_common *sun6i_rtc_ccu_clks[] = {
  214. &iosc_clk,
  215. &iosc_32k_clk,
  216. &ext_osc32k_gate_clk.common,
  217. &osc32k_clk.common,
  218. &osc24M_32k_clk.common,
  219. &rtc_32k_clk.common,
  220. &osc32k_fanout_clk.common,
  221. };
  222. static struct clk_hw_onecell_data sun6i_rtc_ccu_hw_clks = {
  223. .num = CLK_NUMBER,
  224. .hws = {
  225. [CLK_OSC32K] = &osc32k_clk.common.hw,
  226. [CLK_OSC32K_FANOUT] = &osc32k_fanout_clk.common.hw,
  227. [CLK_IOSC] = &iosc_clk.hw,
  228. [CLK_IOSC_32K] = &iosc_32k_clk.hw,
  229. [CLK_EXT_OSC32K_GATE] = &ext_osc32k_gate_clk.common.hw,
  230. [CLK_OSC24M_32K] = &osc24M_32k_clk.common.hw,
  231. [CLK_RTC_32K] = &rtc_32k_clk.common.hw,
  232. },
  233. };
  234. static const struct sunxi_ccu_desc sun6i_rtc_ccu_desc = {
  235. .ccu_clks = sun6i_rtc_ccu_clks,
  236. .num_ccu_clks = ARRAY_SIZE(sun6i_rtc_ccu_clks),
  237. .hw_clks = &sun6i_rtc_ccu_hw_clks,
  238. };
  239. static const struct clk_parent_data sun50i_h616_osc32k_fanout_parents[] = {
  240. { .hw = &osc32k_clk.common.hw },
  241. { .fw_name = "pll-32k" },
  242. { .hw = &osc24M_32k_clk.common.hw }
  243. };
  244. static const struct clk_parent_data sun50i_r329_osc32k_fanout_parents[] = {
  245. { .hw = &osc32k_clk.common.hw },
  246. { .hw = &ext_osc32k_gate_clk.common.hw },
  247. { .hw = &osc24M_32k_clk.common.hw }
  248. };
  249. static const struct sun6i_rtc_match_data sun50i_h616_rtc_ccu_data = {
  250. .have_iosc_calibration = true,
  251. .rtc_32k_single_parent = true,
  252. .osc32k_fanout_parents = sun50i_h616_osc32k_fanout_parents,
  253. .osc32k_fanout_nparents = ARRAY_SIZE(sun50i_h616_osc32k_fanout_parents),
  254. };
  255. static const struct sun6i_rtc_match_data sun50i_r329_rtc_ccu_data = {
  256. .have_ext_osc32k = true,
  257. .osc32k_fanout_parents = sun50i_r329_osc32k_fanout_parents,
  258. .osc32k_fanout_nparents = ARRAY_SIZE(sun50i_r329_osc32k_fanout_parents),
  259. };
  260. static const struct of_device_id sun6i_rtc_ccu_match[] = {
  261. {
  262. .compatible = "allwinner,sun50i-h616-rtc",
  263. .data = &sun50i_h616_rtc_ccu_data,
  264. },
  265. {
  266. .compatible = "allwinner,sun50i-r329-rtc",
  267. .data = &sun50i_r329_rtc_ccu_data,
  268. },
  269. {},
  270. };
  271. int sun6i_rtc_ccu_probe(struct device *dev, void __iomem *reg)
  272. {
  273. const struct sun6i_rtc_match_data *data;
  274. struct clk *ext_osc32k_clk = NULL;
  275. const struct of_device_id *match;
  276. /* This driver is only used for newer variants of the hardware. */
  277. match = of_match_device(sun6i_rtc_ccu_match, dev);
  278. if (!match)
  279. return 0;
  280. data = match->data;
  281. have_iosc_calibration = data->have_iosc_calibration;
  282. if (data->have_ext_osc32k) {
  283. const char *fw_name;
  284. /* ext-osc32k was the only input clock in the old binding. */
  285. fw_name = of_property_read_bool(dev->of_node, "clock-names")
  286. ? "ext-osc32k" : NULL;
  287. ext_osc32k_clk = devm_clk_get_optional(dev, fw_name);
  288. if (IS_ERR(ext_osc32k_clk))
  289. return PTR_ERR(ext_osc32k_clk);
  290. }
  291. if (ext_osc32k_clk) {
  292. /* Link ext-osc32k-gate to its parent. */
  293. *ext_osc32k = __clk_get_hw(ext_osc32k_clk);
  294. } else {
  295. /* ext-osc32k-gate is an orphan, so do not register it. */
  296. sun6i_rtc_ccu_hw_clks.hws[CLK_EXT_OSC32K_GATE] = NULL;
  297. osc32k_init_data.num_parents = 1;
  298. }
  299. if (data->rtc_32k_single_parent)
  300. rtc_32k_init_data.num_parents = 1;
  301. osc32k_fanout_init_data.parent_data = data->osc32k_fanout_parents;
  302. osc32k_fanout_init_data.num_parents = data->osc32k_fanout_nparents;
  303. return devm_sunxi_ccu_probe(dev, reg, &sun6i_rtc_ccu_desc);
  304. }
  305. MODULE_IMPORT_NS(SUNXI_CCU);
  306. MODULE_LICENSE("GPL");