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