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