summaryrefslogtreecommitdiffstats
path: root/drivers/rtc
diff options
context:
space:
mode:
authorOndrej Jirman <megous@megous.com>2019-08-20 11:19:33 -0400
committerAlexandre Belloni <alexandre.belloni@bootlin.com>2019-08-23 10:20:50 -0400
commitb60ff2cfb598ddf03d8dcb43ed53c35aa563e04c (patch)
treed1b0404a5dfd18f0af6b1e57878cdea9beb41d9c /drivers/rtc
parent903e259f9caf036cd9046884e5de9aa64447755c (diff)
rtc: sun6i: Add support for H6 RTC
RTC on H6 is mostly the same as on H5 and H3. It has slight differences mostly in features that are not yet supported by this driver. Some differences are already stated in the comments in existing code. One other difference is that H6 has extra bit in LOSC_CTRL_REG, called EXT_LOSC_EN to enable/disable external low speed crystal oscillator. It also has bit EXT_LOSC_STA in LOSC_AUTO_SWT_STA_REG, to check whether external low speed oscillator is working correctly. This patch adds support for enabling LOSC when necessary: - during reparenting - when probing the clock H6 also has capacbility to automatically reparent RTC clock from external crystal oscillator, to internal RC oscillator, if external oscillator fails. This is enabled by default. Disable it during probe. Signed-off-by: Ondrej Jirman <megous@megous.com> Reviewed-by: Chen-Yu Tsai <wens@csie.org> Link: https://lore.kernel.org/r/20190820151934.3860-3-megous@megous.com Signed-off-by: Alexandre Belloni <alexandre.belloni@bootlin.com>
Diffstat (limited to 'drivers/rtc')
-rw-r--r--drivers/rtc/rtc-sun6i.c40
1 files changed, 38 insertions, 2 deletions
diff --git a/drivers/rtc/rtc-sun6i.c b/drivers/rtc/rtc-sun6i.c
index dbd676db431e..956c0846201f 100644
--- a/drivers/rtc/rtc-sun6i.c
+++ b/drivers/rtc/rtc-sun6i.c
@@ -32,9 +32,11 @@
32/* Control register */ 32/* Control register */
33#define SUN6I_LOSC_CTRL 0x0000 33#define SUN6I_LOSC_CTRL 0x0000
34#define SUN6I_LOSC_CTRL_KEY (0x16aa << 16) 34#define SUN6I_LOSC_CTRL_KEY (0x16aa << 16)
35#define SUN6I_LOSC_CTRL_AUTO_SWT_BYPASS BIT(15)
35#define SUN6I_LOSC_CTRL_ALM_DHMS_ACC BIT(9) 36#define SUN6I_LOSC_CTRL_ALM_DHMS_ACC BIT(9)
36#define SUN6I_LOSC_CTRL_RTC_HMS_ACC BIT(8) 37#define SUN6I_LOSC_CTRL_RTC_HMS_ACC BIT(8)
37#define SUN6I_LOSC_CTRL_RTC_YMD_ACC BIT(7) 38#define SUN6I_LOSC_CTRL_RTC_YMD_ACC BIT(7)
39#define SUN6I_LOSC_CTRL_EXT_LOSC_EN BIT(4)
38#define SUN6I_LOSC_CTRL_EXT_OSC BIT(0) 40#define SUN6I_LOSC_CTRL_EXT_OSC BIT(0)
39#define SUN6I_LOSC_CTRL_ACC_MASK GENMASK(9, 7) 41#define SUN6I_LOSC_CTRL_ACC_MASK GENMASK(9, 7)
40 42
@@ -128,6 +130,8 @@ struct sun6i_rtc_clk_data {
128 unsigned int has_prescaler : 1; 130 unsigned int has_prescaler : 1;
129 unsigned int has_out_clk : 1; 131 unsigned int has_out_clk : 1;
130 unsigned int export_iosc : 1; 132 unsigned int export_iosc : 1;
133 unsigned int has_losc_en : 1;
134 unsigned int has_auto_swt : 1;
131}; 135};
132 136
133struct sun6i_rtc_dev { 137struct sun6i_rtc_dev {
@@ -190,6 +194,10 @@ static int sun6i_rtc_osc_set_parent(struct clk_hw *hw, u8 index)
190 val &= ~SUN6I_LOSC_CTRL_EXT_OSC; 194 val &= ~SUN6I_LOSC_CTRL_EXT_OSC;
191 val |= SUN6I_LOSC_CTRL_KEY; 195 val |= SUN6I_LOSC_CTRL_KEY;
192 val |= index ? SUN6I_LOSC_CTRL_EXT_OSC : 0; 196 val |= index ? SUN6I_LOSC_CTRL_EXT_OSC : 0;
197 if (rtc->data->has_losc_en) {
198 val &= ~SUN6I_LOSC_CTRL_EXT_LOSC_EN;
199 val |= index ? SUN6I_LOSC_CTRL_EXT_LOSC_EN : 0;
200 }
193 writel(val, rtc->base + SUN6I_LOSC_CTRL); 201 writel(val, rtc->base + SUN6I_LOSC_CTRL);
194 spin_unlock_irqrestore(&rtc->lock, flags); 202 spin_unlock_irqrestore(&rtc->lock, flags);
195 203
@@ -215,6 +223,7 @@ static void __init sun6i_rtc_clk_init(struct device_node *node,
215 const char *iosc_name = "rtc-int-osc"; 223 const char *iosc_name = "rtc-int-osc";
216 const char *clkout_name = "osc32k-out"; 224 const char *clkout_name = "osc32k-out";
217 const char *parents[2]; 225 const char *parents[2];
226 u32 reg;
218 227
219 rtc = kzalloc(sizeof(*rtc), GFP_KERNEL); 228 rtc = kzalloc(sizeof(*rtc), GFP_KERNEL);
220 if (!rtc) 229 if (!rtc)
@@ -235,9 +244,18 @@ static void __init sun6i_rtc_clk_init(struct device_node *node,
235 goto err; 244 goto err;
236 } 245 }
237 246
247 reg = SUN6I_LOSC_CTRL_KEY;
248 if (rtc->data->has_auto_swt) {
249 /* Bypass auto-switch to int osc, on ext losc failure */
250 reg |= SUN6I_LOSC_CTRL_AUTO_SWT_BYPASS;
251 writel(reg, rtc->base + SUN6I_LOSC_CTRL);
252 }
253
238 /* Switch to the external, more precise, oscillator */ 254 /* Switch to the external, more precise, oscillator */
239 writel(SUN6I_LOSC_CTRL_KEY | SUN6I_LOSC_CTRL_EXT_OSC, 255 reg |= SUN6I_LOSC_CTRL_EXT_OSC;
240 rtc->base + SUN6I_LOSC_CTRL); 256 if (rtc->data->has_losc_en)
257 reg |= SUN6I_LOSC_CTRL_EXT_LOSC_EN;
258 writel(reg, rtc->base + SUN6I_LOSC_CTRL);
241 259
242 /* Yes, I know, this is ugly. */ 260 /* Yes, I know, this is ugly. */
243 sun6i_rtc = rtc; 261 sun6i_rtc = rtc;
@@ -345,6 +363,23 @@ CLK_OF_DECLARE_DRIVER(sun8i_h3_rtc_clk, "allwinner,sun8i-h3-rtc",
345CLK_OF_DECLARE_DRIVER(sun50i_h5_rtc_clk, "allwinner,sun50i-h5-rtc", 363CLK_OF_DECLARE_DRIVER(sun50i_h5_rtc_clk, "allwinner,sun50i-h5-rtc",
346 sun8i_h3_rtc_clk_init); 364 sun8i_h3_rtc_clk_init);
347 365
366static const struct sun6i_rtc_clk_data sun50i_h6_rtc_data = {
367 .rc_osc_rate = 16000000,
368 .fixed_prescaler = 32,
369 .has_prescaler = 1,
370 .has_out_clk = 1,
371 .export_iosc = 1,
372 .has_losc_en = 1,
373 .has_auto_swt = 1,
374};
375
376static void __init sun50i_h6_rtc_clk_init(struct device_node *node)
377{
378 sun6i_rtc_clk_init(node, &sun50i_h6_rtc_data);
379}
380CLK_OF_DECLARE_DRIVER(sun50i_h6_rtc_clk, "allwinner,sun50i-h6-rtc",
381 sun50i_h6_rtc_clk_init);
382
348static const struct sun6i_rtc_clk_data sun8i_v3_rtc_data = { 383static const struct sun6i_rtc_clk_data sun8i_v3_rtc_data = {
349 .rc_osc_rate = 32000, 384 .rc_osc_rate = 32000,
350 .has_out_clk = 1, 385 .has_out_clk = 1,
@@ -673,6 +708,7 @@ static const struct of_device_id sun6i_rtc_dt_ids[] = {
673 { .compatible = "allwinner,sun8i-r40-rtc" }, 708 { .compatible = "allwinner,sun8i-r40-rtc" },
674 { .compatible = "allwinner,sun8i-v3-rtc" }, 709 { .compatible = "allwinner,sun8i-v3-rtc" },
675 { .compatible = "allwinner,sun50i-h5-rtc" }, 710 { .compatible = "allwinner,sun50i-h5-rtc" },
711 { .compatible = "allwinner,sun50i-h6-rtc" },
676 { /* sentinel */ }, 712 { /* sentinel */ },
677}; 713};
678MODULE_DEVICE_TABLE(of, sun6i_rtc_dt_ids); 714MODULE_DEVICE_TABLE(of, sun6i_rtc_dt_ids);