0001
0002
0003
0004
0005
0006 #include <linux/clk.h>
0007 #include <linux/clk-provider.h>
0008 #include <linux/io.h>
0009 #include <linux/module.h>
0010 #include <linux/of_device.h>
0011
0012 #include <linux/clk/sunxi-ng.h>
0013
0014 #include "ccu_common.h"
0015
0016 #include "ccu_div.h"
0017 #include "ccu_gate.h"
0018 #include "ccu_mux.h"
0019
0020 #include "ccu-sun6i-rtc.h"
0021
0022 #define IOSC_ACCURACY 300000000
0023 #define IOSC_RATE 16000000
0024
0025 #define LOSC_RATE 32768
0026 #define LOSC_RATE_SHIFT 15
0027
0028 #define LOSC_CTRL_REG 0x0
0029 #define LOSC_CTRL_KEY 0x16aa0000
0030
0031 #define IOSC_32K_CLK_DIV_REG 0x8
0032 #define IOSC_32K_CLK_DIV GENMASK(4, 0)
0033 #define IOSC_32K_PRE_DIV 32
0034
0035 #define IOSC_CLK_CALI_REG 0xc
0036 #define IOSC_CLK_CALI_DIV_ONES 22
0037 #define IOSC_CLK_CALI_EN BIT(1)
0038 #define IOSC_CLK_CALI_SRC_SEL BIT(0)
0039
0040 #define LOSC_OUT_GATING_REG 0x60
0041
0042 #define DCXO_CTRL_REG 0x160
0043 #define DCXO_CTRL_CLK16M_RC_EN BIT(0)
0044
0045 struct sun6i_rtc_match_data {
0046 bool have_ext_osc32k : 1;
0047 bool have_iosc_calibration : 1;
0048 bool rtc_32k_single_parent : 1;
0049 const struct clk_parent_data *osc32k_fanout_parents;
0050 u8 osc32k_fanout_nparents;
0051 };
0052
0053 static bool have_iosc_calibration;
0054
0055 static int ccu_iosc_enable(struct clk_hw *hw)
0056 {
0057 struct ccu_common *cm = hw_to_ccu_common(hw);
0058
0059 return ccu_gate_helper_enable(cm, DCXO_CTRL_CLK16M_RC_EN);
0060 }
0061
0062 static void ccu_iosc_disable(struct clk_hw *hw)
0063 {
0064 struct ccu_common *cm = hw_to_ccu_common(hw);
0065
0066 return ccu_gate_helper_disable(cm, DCXO_CTRL_CLK16M_RC_EN);
0067 }
0068
0069 static int ccu_iosc_is_enabled(struct clk_hw *hw)
0070 {
0071 struct ccu_common *cm = hw_to_ccu_common(hw);
0072
0073 return ccu_gate_helper_is_enabled(cm, DCXO_CTRL_CLK16M_RC_EN);
0074 }
0075
0076 static unsigned long ccu_iosc_recalc_rate(struct clk_hw *hw,
0077 unsigned long parent_rate)
0078 {
0079 struct ccu_common *cm = hw_to_ccu_common(hw);
0080
0081 if (have_iosc_calibration) {
0082 u32 reg = readl(cm->base + IOSC_CLK_CALI_REG);
0083
0084
0085
0086
0087
0088 if (reg & IOSC_CLK_CALI_EN)
0089 return reg >> (IOSC_CLK_CALI_DIV_ONES - LOSC_RATE_SHIFT);
0090 }
0091
0092 return IOSC_RATE;
0093 }
0094
0095 static unsigned long ccu_iosc_recalc_accuracy(struct clk_hw *hw,
0096 unsigned long parent_accuracy)
0097 {
0098 return IOSC_ACCURACY;
0099 }
0100
0101 static const struct clk_ops ccu_iosc_ops = {
0102 .enable = ccu_iosc_enable,
0103 .disable = ccu_iosc_disable,
0104 .is_enabled = ccu_iosc_is_enabled,
0105 .recalc_rate = ccu_iosc_recalc_rate,
0106 .recalc_accuracy = ccu_iosc_recalc_accuracy,
0107 };
0108
0109 static struct ccu_common iosc_clk = {
0110 .reg = DCXO_CTRL_REG,
0111 .hw.init = CLK_HW_INIT_NO_PARENT("iosc", &ccu_iosc_ops,
0112 CLK_GET_RATE_NOCACHE),
0113 };
0114
0115 static int ccu_iosc_32k_prepare(struct clk_hw *hw)
0116 {
0117 struct ccu_common *cm = hw_to_ccu_common(hw);
0118 u32 val;
0119
0120 if (!have_iosc_calibration)
0121 return 0;
0122
0123 val = readl(cm->base + IOSC_CLK_CALI_REG);
0124 writel(val | IOSC_CLK_CALI_EN | IOSC_CLK_CALI_SRC_SEL,
0125 cm->base + IOSC_CLK_CALI_REG);
0126
0127 return 0;
0128 }
0129
0130 static void ccu_iosc_32k_unprepare(struct clk_hw *hw)
0131 {
0132 struct ccu_common *cm = hw_to_ccu_common(hw);
0133 u32 val;
0134
0135 if (!have_iosc_calibration)
0136 return;
0137
0138 val = readl(cm->base + IOSC_CLK_CALI_REG);
0139 writel(val & ~(IOSC_CLK_CALI_EN | IOSC_CLK_CALI_SRC_SEL),
0140 cm->base + IOSC_CLK_CALI_REG);
0141 }
0142
0143 static unsigned long ccu_iosc_32k_recalc_rate(struct clk_hw *hw,
0144 unsigned long parent_rate)
0145 {
0146 struct ccu_common *cm = hw_to_ccu_common(hw);
0147 u32 val;
0148
0149 if (have_iosc_calibration) {
0150 val = readl(cm->base + IOSC_CLK_CALI_REG);
0151
0152
0153 if (val & IOSC_CLK_CALI_SRC_SEL)
0154 return LOSC_RATE;
0155 }
0156
0157 val = readl(cm->base + IOSC_32K_CLK_DIV_REG) & IOSC_32K_CLK_DIV;
0158
0159 return parent_rate / IOSC_32K_PRE_DIV / (val + 1);
0160 }
0161
0162 static unsigned long ccu_iosc_32k_recalc_accuracy(struct clk_hw *hw,
0163 unsigned long parent_accuracy)
0164 {
0165 struct ccu_common *cm = hw_to_ccu_common(hw);
0166 u32 val;
0167
0168 if (have_iosc_calibration) {
0169 val = readl(cm->base + IOSC_CLK_CALI_REG);
0170
0171
0172 if (val & IOSC_CLK_CALI_SRC_SEL)
0173 return 0;
0174 }
0175
0176 return parent_accuracy;
0177 }
0178
0179 static const struct clk_ops ccu_iosc_32k_ops = {
0180 .prepare = ccu_iosc_32k_prepare,
0181 .unprepare = ccu_iosc_32k_unprepare,
0182 .recalc_rate = ccu_iosc_32k_recalc_rate,
0183 .recalc_accuracy = ccu_iosc_32k_recalc_accuracy,
0184 };
0185
0186 static struct ccu_common iosc_32k_clk = {
0187 .hw.init = CLK_HW_INIT_HW("iosc-32k", &iosc_clk.hw,
0188 &ccu_iosc_32k_ops,
0189 CLK_GET_RATE_NOCACHE),
0190 };
0191
0192 static const struct clk_hw *ext_osc32k[] = { NULL };
0193
0194 static SUNXI_CCU_GATE_HWS(ext_osc32k_gate_clk, "ext-osc32k-gate",
0195 ext_osc32k, 0x0, BIT(4), 0);
0196
0197 static const struct clk_hw *osc32k_parents[] = {
0198 &iosc_32k_clk.hw,
0199 &ext_osc32k_gate_clk.common.hw
0200 };
0201
0202 static struct clk_init_data osc32k_init_data = {
0203 .name = "osc32k",
0204 .ops = &ccu_mux_ops,
0205 .parent_hws = osc32k_parents,
0206 .num_parents = ARRAY_SIZE(osc32k_parents),
0207 };
0208
0209 static struct ccu_mux osc32k_clk = {
0210 .mux = _SUNXI_CCU_MUX(0, 1),
0211 .common = {
0212 .reg = LOSC_CTRL_REG,
0213 .features = CCU_FEATURE_KEY_FIELD,
0214 .hw.init = &osc32k_init_data,
0215 },
0216 };
0217
0218
0219 static const struct clk_parent_data osc24M[] = {
0220 { .fw_name = "hosc", .name = "osc24M" }
0221 };
0222
0223 static struct ccu_gate osc24M_32k_clk = {
0224 .enable = BIT(16),
0225 .common = {
0226 .reg = LOSC_OUT_GATING_REG,
0227 .prediv = 750,
0228 .features = CCU_FEATURE_ALL_PREDIV,
0229 .hw.init = CLK_HW_INIT_PARENTS_DATA("osc24M-32k", osc24M,
0230 &ccu_gate_ops, 0),
0231 },
0232 };
0233
0234 static const struct clk_hw *rtc_32k_parents[] = {
0235 &osc32k_clk.common.hw,
0236 &osc24M_32k_clk.common.hw
0237 };
0238
0239 static struct clk_init_data rtc_32k_init_data = {
0240 .name = "rtc-32k",
0241 .ops = &ccu_mux_ops,
0242 .parent_hws = rtc_32k_parents,
0243 .num_parents = ARRAY_SIZE(rtc_32k_parents),
0244 .flags = CLK_IS_CRITICAL,
0245 };
0246
0247 static struct ccu_mux rtc_32k_clk = {
0248 .mux = _SUNXI_CCU_MUX(1, 1),
0249 .common = {
0250 .reg = LOSC_CTRL_REG,
0251 .features = CCU_FEATURE_KEY_FIELD,
0252 .hw.init = &rtc_32k_init_data,
0253 },
0254 };
0255
0256 static struct clk_init_data osc32k_fanout_init_data = {
0257 .name = "osc32k-fanout",
0258 .ops = &ccu_mux_ops,
0259
0260 };
0261
0262 static struct ccu_mux osc32k_fanout_clk = {
0263 .enable = BIT(0),
0264 .mux = _SUNXI_CCU_MUX(1, 2),
0265 .common = {
0266 .reg = LOSC_OUT_GATING_REG,
0267 .hw.init = &osc32k_fanout_init_data,
0268 },
0269 };
0270
0271 static struct ccu_common *sun6i_rtc_ccu_clks[] = {
0272 &iosc_clk,
0273 &iosc_32k_clk,
0274 &ext_osc32k_gate_clk.common,
0275 &osc32k_clk.common,
0276 &osc24M_32k_clk.common,
0277 &rtc_32k_clk.common,
0278 &osc32k_fanout_clk.common,
0279 };
0280
0281 static struct clk_hw_onecell_data sun6i_rtc_ccu_hw_clks = {
0282 .num = CLK_NUMBER,
0283 .hws = {
0284 [CLK_OSC32K] = &osc32k_clk.common.hw,
0285 [CLK_OSC32K_FANOUT] = &osc32k_fanout_clk.common.hw,
0286 [CLK_IOSC] = &iosc_clk.hw,
0287 [CLK_IOSC_32K] = &iosc_32k_clk.hw,
0288 [CLK_EXT_OSC32K_GATE] = &ext_osc32k_gate_clk.common.hw,
0289 [CLK_OSC24M_32K] = &osc24M_32k_clk.common.hw,
0290 [CLK_RTC_32K] = &rtc_32k_clk.common.hw,
0291 },
0292 };
0293
0294 static const struct sunxi_ccu_desc sun6i_rtc_ccu_desc = {
0295 .ccu_clks = sun6i_rtc_ccu_clks,
0296 .num_ccu_clks = ARRAY_SIZE(sun6i_rtc_ccu_clks),
0297
0298 .hw_clks = &sun6i_rtc_ccu_hw_clks,
0299 };
0300
0301 static const struct clk_parent_data sun50i_h616_osc32k_fanout_parents[] = {
0302 { .hw = &osc32k_clk.common.hw },
0303 { .fw_name = "pll-32k" },
0304 { .hw = &osc24M_32k_clk.common.hw }
0305 };
0306
0307 static const struct clk_parent_data sun50i_r329_osc32k_fanout_parents[] = {
0308 { .hw = &osc32k_clk.common.hw },
0309 { .hw = &ext_osc32k_gate_clk.common.hw },
0310 { .hw = &osc24M_32k_clk.common.hw }
0311 };
0312
0313 static const struct sun6i_rtc_match_data sun50i_h616_rtc_ccu_data = {
0314 .have_iosc_calibration = true,
0315 .rtc_32k_single_parent = true,
0316 .osc32k_fanout_parents = sun50i_h616_osc32k_fanout_parents,
0317 .osc32k_fanout_nparents = ARRAY_SIZE(sun50i_h616_osc32k_fanout_parents),
0318 };
0319
0320 static const struct sun6i_rtc_match_data sun50i_r329_rtc_ccu_data = {
0321 .have_ext_osc32k = true,
0322 .osc32k_fanout_parents = sun50i_r329_osc32k_fanout_parents,
0323 .osc32k_fanout_nparents = ARRAY_SIZE(sun50i_r329_osc32k_fanout_parents),
0324 };
0325
0326 static const struct of_device_id sun6i_rtc_ccu_match[] = {
0327 {
0328 .compatible = "allwinner,sun50i-h616-rtc",
0329 .data = &sun50i_h616_rtc_ccu_data,
0330 },
0331 {
0332 .compatible = "allwinner,sun50i-r329-rtc",
0333 .data = &sun50i_r329_rtc_ccu_data,
0334 },
0335 {},
0336 };
0337
0338 int sun6i_rtc_ccu_probe(struct device *dev, void __iomem *reg)
0339 {
0340 const struct sun6i_rtc_match_data *data;
0341 struct clk *ext_osc32k_clk = NULL;
0342 const struct of_device_id *match;
0343
0344
0345 match = of_match_device(sun6i_rtc_ccu_match, dev);
0346 if (!match)
0347 return 0;
0348
0349 data = match->data;
0350 have_iosc_calibration = data->have_iosc_calibration;
0351
0352 if (data->have_ext_osc32k) {
0353 const char *fw_name;
0354
0355
0356 fw_name = of_property_read_bool(dev->of_node, "clock-names")
0357 ? "ext-osc32k" : NULL;
0358 ext_osc32k_clk = devm_clk_get_optional(dev, fw_name);
0359 if (IS_ERR(ext_osc32k_clk))
0360 return PTR_ERR(ext_osc32k_clk);
0361 }
0362
0363 if (ext_osc32k_clk) {
0364
0365 *ext_osc32k = __clk_get_hw(ext_osc32k_clk);
0366 } else {
0367
0368 sun6i_rtc_ccu_hw_clks.hws[CLK_EXT_OSC32K_GATE] = NULL;
0369 osc32k_init_data.num_parents = 1;
0370 }
0371
0372 if (data->rtc_32k_single_parent)
0373 rtc_32k_init_data.num_parents = 1;
0374
0375 osc32k_fanout_init_data.parent_data = data->osc32k_fanout_parents;
0376 osc32k_fanout_init_data.num_parents = data->osc32k_fanout_nparents;
0377
0378 return devm_sunxi_ccu_probe(dev, reg, &sun6i_rtc_ccu_desc);
0379 }
0380
0381 MODULE_IMPORT_NS(SUNXI_CCU);
0382 MODULE_LICENSE("GPL");