Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-only
0002 //
0003 // Copyright (c) 2021 Samuel Holland <samuel@sholland.org>
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 /* 30% */
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          * Recover the IOSC frequency by shifting the ones place of
0086          * (fixed-point divider * 32768) into bit zero.
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         /* Assume the calibrated 32k clock is accurate. */
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         /* Assume the calibrated 32k clock is accurate. */
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 }; /* updated during probe */
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), /* updated during probe */
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 /* This falls back to the global name for fwnodes without a named reference. */
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), /* updated during probe */
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     /* parents are set during probe */
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     /* This driver is only used for newer variants of the hardware. */
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         /* ext-osc32k was the only input clock in the old binding. */
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         /* Link ext-osc32k-gate to its parent. */
0365         *ext_osc32k = __clk_get_hw(ext_osc32k_clk);
0366     } else {
0367         /* ext-osc32k-gate is an orphan, so do not register it. */
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");