diff mbox

[PATCHv3,4/5] rtc: s3c: Add support for RTC of Exynos3250 SoC

Message ID 1409216559-22402-5-git-send-email-cw00.choi@samsung.com
State Accepted
Headers show

Commit Message

Chanwoo Choi Aug. 28, 2014, 9:02 a.m. UTC
This patch add support for RTC of Exynos3250 SoC. The Exynos3250 needs source
clock(32.768KHz) for RTC block. If source clock of RTC is registerd on clock
list of common clk framework, Exynos RTC drvier have to control this clock.

Clock list for s3c-rtc device:
- rtc : CLK_RTC of CLK_GATE_IP_PERIR is gate clock for RTC.
- rtc_src : XrtcXTI is 32.768.kHz source clock for RTC.
 (XRTCXTI: Specifies a clock from 32.768 kHz crystal pad with XRTCXTI and
 XRTCXTO pins. RTC uses this clock as the source of a real-time clock.)

Signed-off-by: Chanwoo Choi <cw00.choi@samsung.com>
Acked-by: Kyungmin Park <kyungmin.park@samsung.com>
Cc: Alessandro Zummo <a.zummo@towertech.it>
Cc: Kukjin Kim <kgene.kim@samsung.com>
Cc: Andrew Morton <akpm@linux-foundation.org>
---
 Documentation/devicetree/bindings/rtc/s3c-rtc.txt |  1 +
 drivers/rtc/rtc-s3c.c                             | 93 ++++++++++++++++++++++-
 2 files changed, 93 insertions(+), 1 deletion(-)

Comments

Javier Martinez Canillas Sept. 16, 2014, 1:48 p.m. UTC | #1
Hello Chanwoo,

On Thu, Aug 28, 2014 at 11:02 AM, Chanwoo Choi <cw00.choi@samsung.com> wrote:
> This patch add support for RTC of Exynos3250 SoC. The Exynos3250 needs source
> clock(32.768KHz) for RTC block. If source clock of RTC is registerd on clock
> list of common clk framework, Exynos RTC drvier have to control this clock.
>
> Clock list for s3c-rtc device:
> - rtc : CLK_RTC of CLK_GATE_IP_PERIR is gate clock for RTC.
> - rtc_src : XrtcXTI is 32.768.kHz source clock for RTC.

Is this RTC source clock needed for all Exynos SoCs?

> @@ -480,11 +530,19 @@ static int s3c_rtc_probe(struct platform_device *pdev)
>
>         info->rtc_clk = devm_clk_get(&pdev->dev, "rtc");
>         if (IS_ERR(info->rtc_clk)) {
> -               dev_err(&pdev->dev, "failed to find rtc clock source\n");
> +               dev_err(&pdev->dev, "failed to find rtc clock\n");
>                 return PTR_ERR(info->rtc_clk);
>         }
>         clk_prepare_enable(info->rtc_clk);
>
> +       info->rtc_src_clk = devm_clk_get(&pdev->dev, "rtc_src");
> +       if (IS_ERR(info->rtc_src_clk)) {
> +               dev_err(&pdev->dev, "failed to find rtc source clock\n");
> +               return PTR_ERR(info->rtc_src_clk);

On an Exynos5420 Peach Pit machine I'm having the following error and
the driver fails to probe:

[    2.095700] s3c-rtc: probe of 101e0000.rtc failed with error -2

Reverting this patch fixes the issue and the s3c RTC works again on
this machine.

Best regards,
Javier
Daniel Drake Sept. 16, 2014, 3:03 p.m. UTC | #2
On Tue, Sep 16, 2014 at 7:48 AM, Javier Martinez Canillas
<javier@dowhile0.org> wrote:
>> Clock list for s3c-rtc device:
>> - rtc : CLK_RTC of CLK_GATE_IP_PERIR is gate clock for RTC.
>> - rtc_src : XrtcXTI is 32.768.kHz source clock for RTC.
>
> Is this RTC source clock needed for all Exynos SoCs?

It is at least needed on Exynos4412, which has the XrtcXTI thing
exactly as you describe. However the very standard setup there is to
hook it up to the CP clock output of the MAX76686 PMIC. This CP clock
is on by default, so you can potentially live without that detail
being present in the DT.

However... one small issue with this setup is that when you enable
CONFIG_COMMON_CLK_MAX77686, the CP clock gets exposed in Linux's
common clock framework, and Linux then turns it off because it
believes it is unused. Then the RTC stops ticking.

So the rtc_src idea would also be good for Exynos4412. Maybe it would
make sense to drop the needs_src_clk flag, and simply require/enable
the src clock whenever it is present in the DT.

Also, are you sure about the way you are treating this clock, all
those enable/disable calls? You only seem to enable it when doing some
particular driver operations e.g. reading the time, leaving it
disabled at all other times. However I believe on Exynos4412 that if
you disable this clock then the RTC will not tick.

Daniel
Javier Martinez Canillas Sept. 16, 2014, 3:20 p.m. UTC | #3
Hello Daniel,

On Tue, Sep 16, 2014 at 5:03 PM, Daniel Drake <drake@endlessm.com> wrote:
> On Tue, Sep 16, 2014 at 7:48 AM, Javier Martinez Canillas
> <javier@dowhile0.org> wrote:
>>> Clock list for s3c-rtc device:
>>> - rtc : CLK_RTC of CLK_GATE_IP_PERIR is gate clock for RTC.
>>> - rtc_src : XrtcXTI is 32.768.kHz source clock for RTC.
>>
>> Is this RTC source clock needed for all Exynos SoCs?
>
> It is at least needed on Exynos4412, which has the XrtcXTI thing
> exactly as you describe. However the very standard setup there is to
> hook it up to the CP clock output of the MAX76686 PMIC. This CP clock
> is on by default, so you can potentially live without that detail
> being present in the DT.
>

Thanks for confirming for Exynos4412, I just answered my own email
saying that I found to be needed on Exynos5420 as well and as you
said, it was just working because the Maxim clocks were left on by
default.

> However... one small issue with this setup is that when you enable
> CONFIG_COMMON_CLK_MAX77686, the CP clock gets exposed in Linux's
> common clock framework, and Linux then turns it off because it
> believes it is unused. Then the RTC stops ticking.
>

Indeed, this is an issue about relying on default state. We had a
quite long discussion a couple of weeks ago about simplefb relying on
clocks and regulators left enabled by the bootloader but once these
were know to the kernel, the frameworks disable them because were
unused making simplefb to fail.

> So the rtc_src idea would also be good for Exynos4412. Maybe it would
> make sense to drop the needs_src_clk flag, and simply require/enable
> the src clock whenever it is present in the DT.
>

That sounds more sensible to me as well. I wonder what should happen
in this case with DT backward compatibility though. But as you said,
the external clock is required and the kernel will disable this clock
once is know to the CCF since is not used so maybe will be hard to
maintain DT backward compatibility in this case.

> Also, are you sure about the way you are treating this clock, all
> those enable/disable calls? You only seem to enable it when doing some
> particular driver operations e.g. reading the time, leaving it
> disabled at all other times. However I believe on Exynos4412 that if
> you disable this clock then the RTC will not tick.
>
> Daniel

Best regards,
Javier
Doug Anderson Sept. 16, 2014, 10:15 p.m. UTC | #4
Hi,

On Tue, Sep 16, 2014 at 8:20 AM, Javier Martinez Canillas
<javier@dowhile0.org> wrote:
> Hello Daniel,
>
> On Tue, Sep 16, 2014 at 5:03 PM, Daniel Drake <drake@endlessm.com> wrote:
>> On Tue, Sep 16, 2014 at 7:48 AM, Javier Martinez Canillas
>> <javier@dowhile0.org> wrote:
>>>> Clock list for s3c-rtc device:
>>>> - rtc : CLK_RTC of CLK_GATE_IP_PERIR is gate clock for RTC.
>>>> - rtc_src : XrtcXTI is 32.768.kHz source clock for RTC.
>>>
>>> Is this RTC source clock needed for all Exynos SoCs?
>>
>> It is at least needed on Exynos4412, which has the XrtcXTI thing
>> exactly as you describe. However the very standard setup there is to
>> hook it up to the CP clock output of the MAX76686 PMIC. This CP clock
>> is on by default, so you can potentially live without that detail
>> being present in the DT.
>>
>
> Thanks for confirming for Exynos4412, I just answered my own email
> saying that I found to be needed on Exynos5420 as well and as you
> said, it was just working because the Maxim clocks were left on by
> default.
>
>> However... one small issue with this setup is that when you enable
>> CONFIG_COMMON_CLK_MAX77686, the CP clock gets exposed in Linux's
>> common clock framework, and Linux then turns it off because it
>> believes it is unused. Then the RTC stops ticking.
>>
>
> Indeed, this is an issue about relying on default state. We had a
> quite long discussion a couple of weeks ago about simplefb relying on
> clocks and regulators left enabled by the bootloader but once these
> were know to the kernel, the frameworks disable them because were
> unused making simplefb to fail.
>
>> So the rtc_src idea would also be good for Exynos4412. Maybe it would
>> make sense to drop the needs_src_clk flag, and simply require/enable
>> the src clock whenever it is present in the DT.
>>
>
> That sounds more sensible to me as well. I wonder what should happen
> in this case with DT backward compatibility though. But as you said,
> the external clock is required and the kernel will disable this clock
> once is know to the CCF since is not used so maybe will be hard to
> maintain DT backward compatibility in this case.

I think you can turn off CONFIG_COMMON_CLK_MAX77686 and then this
clock will be left at whatever the bootloader set it to, right?  Then
there will be no auto-disabling by the CCF and the RTC will work.
That's one argument for making the clock "optional".

NOTE: I don't think that the builtin RTC is terribly important for any
exynos-based Chromebooks that I'm aware of.  We rely on the RTC that's
part of the Maxim PMIC itself and pretty much ignore the one built-in
to the exynos.  I think there are some cases it was used (as a
fallback wakeup source in certain test scripts), but nothing very
important.

-Doug
Javier Martinez Canillas Sept. 17, 2014, 8:39 a.m. UTC | #5
Hello Doug,

On Wed, Sep 17, 2014 at 12:15 AM, Doug Anderson <dianders@chromium.org> wrote:
>
> I think you can turn off CONFIG_COMMON_CLK_MAX77686 and then this
> clock will be left at whatever the bootloader set it to, right?  Then
> there will be no auto-disabling by the CCF and the RTC will work.

Yes, that's how Daniel was working around the issue.

> That's one argument for making the clock "optional".
>

Indeed.

> NOTE: I don't think that the builtin RTC is terribly important for any
> exynos-based Chromebooks that I'm aware of.  We rely on the RTC that's
> part of the Maxim PMIC itself and pretty much ignore the one built-in
> to the exynos.  I think there are some cases it was used (as a
> fallback wakeup source in certain test scripts), but nothing very
> important.
>

Ok, I'll post the patch I shared before that makes the rtc to claim
the max77802 32kHz clock as "rtc_src" anyways since that is the right
thing to do even if the "rtc_src" ends being optional due DT backward
compat.

> -Doug

Best regards,
Javier
Daniel Drake Sept. 17, 2014, 4:49 p.m. UTC | #6
On Tue, Sep 16, 2014 at 4:15 PM, Doug Anderson <dianders@chromium.org> wrote:
> NOTE: I don't think that the builtin RTC is terribly important for any
> exynos-based Chromebooks that I'm aware of.  We rely on the RTC that's
> part of the Maxim PMIC itself and pretty much ignore the one built-in
> to the exynos.  I think there are some cases it was used (as a
> fallback wakeup source in certain test scripts), but nothing very
> important.

That's not true for all hardware though, at least the board I'm
working on now has the SoC RTC as battery-backed and the PMIC one with
no battery. So in this case at least, the interesting RTC is the SoC
one.

Daniel
Doug Anderson Sept. 17, 2014, 4:51 p.m. UTC | #7
Daniel,

On Wed, Sep 17, 2014 at 9:49 AM, Daniel Drake <drake@endlessm.com> wrote:
> On Tue, Sep 16, 2014 at 4:15 PM, Doug Anderson <dianders@chromium.org> wrote:
>> NOTE: I don't think that the builtin RTC is terribly important for any
>> exynos-based Chromebooks that I'm aware of.  We rely on the RTC that's
>> part of the Maxim PMIC itself and pretty much ignore the one built-in
>> to the exynos.  I think there are some cases it was used (as a
>> fallback wakeup source in certain test scripts), but nothing very
>> important.
>
> That's not true for all hardware though, at least the board I'm
> working on now has the SoC RTC as battery-backed and the PMIC one with
> no battery. So in this case at least, the interesting RTC is the SoC
> one.

Yup, I can totally believe that.  My statement was meant only to apply
to the boards I knew about firsthand...

-Doug
diff mbox

Patch

diff --git a/Documentation/devicetree/bindings/rtc/s3c-rtc.txt b/Documentation/devicetree/bindings/rtc/s3c-rtc.txt
index 06db446..ab757b84 100644
--- a/Documentation/devicetree/bindings/rtc/s3c-rtc.txt
+++ b/Documentation/devicetree/bindings/rtc/s3c-rtc.txt
@@ -6,6 +6,7 @@  Required properties:
     * "samsung,s3c2416-rtc" - for controllers compatible with s3c2416 rtc.
     * "samsung,s3c2443-rtc" - for controllers compatible with s3c2443 rtc.
     * "samsung,s3c6410-rtc" - for controllers compatible with s3c6410 rtc.
+    * "samsung,exynos3250-rtc" - for controllers compatible with exynos3250 rtc.
 - reg: physical base address of the controller and length of memory mapped
   region.
 - interrupts: Two interrupt numbers to the cpu should be specified. First
diff --git a/drivers/rtc/rtc-s3c.c b/drivers/rtc/rtc-s3c.c
index 0d90892..a6b1252 100644
--- a/drivers/rtc/rtc-s3c.c
+++ b/drivers/rtc/rtc-s3c.c
@@ -38,6 +38,7 @@  struct s3c_rtc {
 
 	void __iomem *base;
 	struct clk *rtc_clk;
+	struct clk *rtc_src_clk;
 	bool enabled;
 
 	struct s3c_rtc_data *data;
@@ -54,6 +55,7 @@  struct s3c_rtc {
 
 struct s3c_rtc_data {
 	int max_user_freq;
+	bool needs_src_clk;
 
 	void (*irq_handler) (struct s3c_rtc *info, int mask);
 	void (*set_freq) (struct s3c_rtc *info, int freq);
@@ -73,10 +75,14 @@  static void s3c_rtc_alarm_clk_enable(struct s3c_rtc *info, bool enable)
 	if (enable) {
 		if (!info->enabled) {
 			clk_enable(info->rtc_clk);
+			if (info->data->needs_src_clk)
+				clk_enable(info->rtc_src_clk);
 			info->enabled = true;
 		}
 	} else {
 		if (info->enabled) {
+			if (info->data->needs_src_clk)
+				clk_disable(info->rtc_src_clk);
 			clk_disable(info->rtc_clk);
 			info->enabled = false;
 		}
@@ -114,12 +120,16 @@  static int s3c_rtc_setaie(struct device *dev, unsigned int enabled)
 	dev_dbg(info->dev, "%s: aie=%d\n", __func__, enabled);
 
 	clk_enable(info->rtc_clk);
+	if (info->data->needs_src_clk)
+		clk_enable(info->rtc_src_clk);
 	tmp = readb(info->base + S3C2410_RTCALM) & ~S3C2410_RTCALM_ALMEN;
 
 	if (enabled)
 		tmp |= S3C2410_RTCALM_ALMEN;
 
 	writeb(tmp, info->base + S3C2410_RTCALM);
+	if (info->data->needs_src_clk)
+		clk_disable(info->rtc_src_clk);
 	clk_disable(info->rtc_clk);
 
 	s3c_rtc_alarm_clk_enable(info, enabled);
@@ -134,12 +144,16 @@  static int s3c_rtc_setfreq(struct s3c_rtc *info, int freq)
 		return -EINVAL;
 
 	clk_enable(info->rtc_clk);
+	if (info->data->needs_src_clk)
+		clk_enable(info->rtc_src_clk);
 	spin_lock_irq(&info->pie_lock);
 
 	if (info->data->set_freq)
 		info->data->set_freq(info, freq);
 
 	spin_unlock_irq(&info->pie_lock);
+	if (info->data->needs_src_clk)
+		clk_disable(info->rtc_src_clk);
 	clk_disable(info->rtc_clk);
 
 	return 0;
@@ -152,6 +166,9 @@  static int s3c_rtc_gettime(struct device *dev, struct rtc_time *rtc_tm)
 	unsigned int have_retried = 0;
 
 	clk_enable(info->rtc_clk);
+	if (info->data->needs_src_clk)
+		clk_enable(info->rtc_src_clk);
+
  retry_get_time:
 	rtc_tm->tm_min  = readb(info->base + S3C2410_RTCMIN);
 	rtc_tm->tm_hour = readb(info->base + S3C2410_RTCHOUR);
@@ -185,6 +202,8 @@  static int s3c_rtc_gettime(struct device *dev, struct rtc_time *rtc_tm)
 
 	rtc_tm->tm_mon -= 1;
 
+	if (info->data->needs_src_clk)
+		clk_disable(info->rtc_src_clk);
 	clk_disable(info->rtc_clk);
 
 	return rtc_valid_tm(rtc_tm);
@@ -207,6 +226,8 @@  static int s3c_rtc_settime(struct device *dev, struct rtc_time *tm)
 	}
 
 	clk_enable(info->rtc_clk);
+	if (info->data->needs_src_clk)
+		clk_enable(info->rtc_src_clk);
 
 	writeb(bin2bcd(tm->tm_sec),  info->base + S3C2410_RTCSEC);
 	writeb(bin2bcd(tm->tm_min),  info->base + S3C2410_RTCMIN);
@@ -215,6 +236,8 @@  static int s3c_rtc_settime(struct device *dev, struct rtc_time *tm)
 	writeb(bin2bcd(tm->tm_mon + 1), info->base + S3C2410_RTCMON);
 	writeb(bin2bcd(year), info->base + S3C2410_RTCYEAR);
 
+	if (info->data->needs_src_clk)
+		clk_disable(info->rtc_src_clk);
 	clk_disable(info->rtc_clk);
 
 	return 0;
@@ -227,6 +250,9 @@  static int s3c_rtc_getalarm(struct device *dev, struct rtc_wkalrm *alrm)
 	unsigned int alm_en;
 
 	clk_enable(info->rtc_clk);
+	if (info->data->needs_src_clk)
+		clk_enable(info->rtc_src_clk);
+
 	alm_tm->tm_sec  = readb(info->base + S3C2410_ALMSEC);
 	alm_tm->tm_min  = readb(info->base + S3C2410_ALMMIN);
 	alm_tm->tm_hour = readb(info->base + S3C2410_ALMHOUR);
@@ -278,7 +304,10 @@  static int s3c_rtc_getalarm(struct device *dev, struct rtc_wkalrm *alrm)
 	else
 		alm_tm->tm_year = -1;
 
+	if (info->data->needs_src_clk)
+		clk_disable(info->rtc_src_clk);
 	clk_disable(info->rtc_clk);
+
 	return 0;
 }
 
@@ -289,6 +318,9 @@  static int s3c_rtc_setalarm(struct device *dev, struct rtc_wkalrm *alrm)
 	unsigned int alrm_en;
 
 	clk_enable(info->rtc_clk);
+	if (info->data->needs_src_clk)
+		clk_enable(info->rtc_src_clk);
+
 	dev_dbg(dev, "s3c_rtc_setalarm: %d, %04d.%02d.%02d %02d:%02d:%02d\n",
 		 alrm->enabled,
 		 1900 + tm->tm_year, tm->tm_mon + 1, tm->tm_mday,
@@ -318,6 +350,8 @@  static int s3c_rtc_setalarm(struct device *dev, struct rtc_wkalrm *alrm)
 
 	s3c_rtc_setaie(dev, alrm->enabled);
 
+	if (info->data->needs_src_clk)
+		clk_disable(info->rtc_src_clk);
 	clk_disable(info->rtc_clk);
 
 	return 0;
@@ -328,10 +362,14 @@  static int s3c_rtc_proc(struct device *dev, struct seq_file *seq)
 	struct s3c_rtc *info = dev_get_drvdata(dev);
 
 	clk_enable(info->rtc_clk);
+	if (info->data->needs_src_clk)
+		clk_enable(info->rtc_src_clk);
 
 	if (info->data->enable_tick)
 		info->data->enable_tick(info, seq);
 
+	if (info->data->needs_src_clk)
+		clk_disable(info->rtc_src_clk);
 	clk_disable(info->rtc_clk);
 
 	return 0;
@@ -351,6 +389,8 @@  static void s3c24xx_rtc_enable(struct s3c_rtc *info)
 	unsigned int con, tmp;
 
 	clk_enable(info->rtc_clk);
+	if (info->data->needs_src_clk)
+		clk_enable(info->rtc_src_clk);
 
 	con = readw(info->base + S3C2410_RTCCON);
 	/* re-enable the device, and check it is ok */
@@ -378,6 +418,8 @@  static void s3c24xx_rtc_enable(struct s3c_rtc *info)
 			info->base + S3C2410_RTCCON);
 	}
 
+	if (info->data->needs_src_clk)
+		clk_disable(info->rtc_src_clk);
 	clk_disable(info->rtc_clk);
 }
 
@@ -386,6 +428,8 @@  static void s3c24xx_rtc_disable(struct s3c_rtc *info)
 	unsigned int con;
 
 	clk_enable(info->rtc_clk);
+	if (info->data->needs_src_clk)
+		clk_enable(info->rtc_src_clk);
 
 	con = readw(info->base + S3C2410_RTCCON);
 	con &= ~S3C2410_RTCCON_RTCEN;
@@ -395,6 +439,8 @@  static void s3c24xx_rtc_disable(struct s3c_rtc *info)
 	con &= ~S3C2410_TICNT_ENABLE;
 	writeb(con, info->base + S3C2410_TICNT);
 
+	if (info->data->needs_src_clk)
+		clk_disable(info->rtc_src_clk);
 	clk_disable(info->rtc_clk);
 }
 
@@ -403,12 +449,16 @@  static void s3c6410_rtc_disable(struct s3c_rtc *info)
 	unsigned int con;
 
 	clk_enable(info->rtc_clk);
+	if (info->data->needs_src_clk)
+		clk_enable(info->rtc_src_clk);
 
 	con = readw(info->base + S3C2410_RTCCON);
 	con &= ~S3C64XX_RTCCON_TICEN;
 	con &= ~S3C2410_RTCCON_RTCEN;
 	writew(con, info->base + S3C2410_RTCCON);
 
+	if (info->data->needs_src_clk)
+		clk_disable(info->rtc_src_clk);
 	clk_disable(info->rtc_clk);
 }
 
@@ -480,11 +530,19 @@  static int s3c_rtc_probe(struct platform_device *pdev)
 
 	info->rtc_clk = devm_clk_get(&pdev->dev, "rtc");
 	if (IS_ERR(info->rtc_clk)) {
-		dev_err(&pdev->dev, "failed to find rtc clock source\n");
+		dev_err(&pdev->dev, "failed to find rtc clock\n");
 		return PTR_ERR(info->rtc_clk);
 	}
 	clk_prepare_enable(info->rtc_clk);
 
+	info->rtc_src_clk = devm_clk_get(&pdev->dev, "rtc_src");
+	if (IS_ERR(info->rtc_src_clk)) {
+		dev_err(&pdev->dev, "failed to find rtc source clock\n");
+		return PTR_ERR(info->rtc_src_clk);
+	}
+	clk_prepare_enable(info->rtc_src_clk);
+
+
 	/* check to see if everything is setup correctly */
 	if (info->data->enable)
 		info->data->enable(info);
@@ -538,6 +596,8 @@  static int s3c_rtc_probe(struct platform_device *pdev)
 
 	s3c_rtc_setfreq(info, 1);
 
+	if (info->data->needs_src_clk)
+		clk_disable(info->rtc_src_clk);
 	clk_disable(info->rtc_clk);
 
 	return 0;
@@ -557,6 +617,8 @@  static int s3c_rtc_suspend(struct device *dev)
 	struct s3c_rtc *info = dev_get_drvdata(dev);
 
 	clk_enable(info->rtc_clk);
+	if (info->data->needs_src_clk)
+		clk_enable(info->rtc_src_clk);
 
 	/* save TICNT for anyone using periodic interrupts */
 	if (info->data->save_tick_cnt)
@@ -572,6 +634,8 @@  static int s3c_rtc_suspend(struct device *dev)
 			dev_err(dev, "enable_irq_wake failed\n");
 	}
 
+	if (info->data->needs_src_clk)
+		clk_disable(info->rtc_src_clk);
 	clk_disable(info->rtc_clk);
 
 	return 0;
@@ -582,6 +646,8 @@  static int s3c_rtc_resume(struct device *dev)
 	struct s3c_rtc *info = dev_get_drvdata(dev);
 
 	clk_enable(info->rtc_clk);
+	if (info->data->needs_src_clk)
+		clk_enable(info->rtc_src_clk);
 
 	if (info->data->enable)
 		info->data->enable(info);
@@ -594,6 +660,8 @@  static int s3c_rtc_resume(struct device *dev)
 		info->wake_en = false;
 	}
 
+	if (info->data->needs_src_clk)
+		clk_disable(info->rtc_src_clk);
 	clk_disable(info->rtc_clk);
 
 	return 0;
@@ -604,7 +672,11 @@  static SIMPLE_DEV_PM_OPS(s3c_rtc_pm_ops, s3c_rtc_suspend, s3c_rtc_resume);
 static void s3c24xx_rtc_irq(struct s3c_rtc *info, int mask)
 {
 	clk_enable(info->rtc_clk);
+	if (info->data->needs_src_clk)
+		clk_enable(info->rtc_src_clk);
 	rtc_update_irq(info->rtc, 1, RTC_AF | RTC_IRQF);
+	if (info->data->needs_src_clk)
+		clk_disable(info->rtc_src_clk);
 	clk_disable(info->rtc_clk);
 
 	s3c_rtc_alarm_clk_enable(info, false);
@@ -613,8 +685,12 @@  static void s3c24xx_rtc_irq(struct s3c_rtc *info, int mask)
 static void s3c6410_rtc_irq(struct s3c_rtc *info, int mask)
 {
 	clk_enable(info->rtc_clk);
+	if (info->data->needs_src_clk)
+		clk_enable(info->rtc_src_clk);
 	rtc_update_irq(info->rtc, 1, RTC_AF | RTC_IRQF);
 	writeb(mask, info->base + S3C2410_INTP);
+	if (info->data->needs_src_clk)
+		clk_disable(info->rtc_src_clk);
 	clk_disable(info->rtc_clk);
 
 	s3c_rtc_alarm_clk_enable(info, false);
@@ -780,6 +856,18 @@  static struct s3c_rtc_data const s3c6410_rtc_data = {
 	.disable		= s3c6410_rtc_disable,
 };
 
+static struct s3c_rtc_data const exynos3250_rtc_data = {
+	.max_user_freq		= 32768,
+	.needs_src_clk		= true,
+	.irq_handler		= s3c6410_rtc_irq,
+	.set_freq		= s3c6410_rtc_setfreq,
+	.enable_tick		= s3c6410_rtc_enable_tick,
+	.save_tick_cnt		= s3c6410_rtc_save_tick_cnt,
+	.restore_tick_cnt	= s3c6410_rtc_restore_tick_cnt,
+	.enable			= s3c24xx_rtc_enable,
+	.disable		= s3c6410_rtc_disable,
+};
+
 static const struct of_device_id s3c_rtc_dt_match[] = {
 	{
 		.compatible = "samsung,s3c2410-rtc",
@@ -793,6 +881,9 @@  static const struct of_device_id s3c_rtc_dt_match[] = {
 	}, {
 		.compatible = "samsung,s3c6410-rtc",
 		.data = (void *)&s3c6410_rtc_data,
+	}, {
+		.compatible = "samsung,exynos3250-rtc",
+		.data = (void *)&exynos3250_rtc_data,
 	},
 	{ /* sentinel */ },
 };