Commit 5a5b614b authored by Marek Szyprowski's avatar Marek Szyprowski Committed by Alexandre Belloni

rtc: s3c: Rewrite clock handling

s3c_rtc_enable/disable_clk() functions were designed to be called multiple
times without reference counting, because they were initially only used in
alarm setting/clearing functions, which can be called both when alarm is
already set or not. Later however, calls to those functions have been added to
other places in the driver - like time and /proc reading callbacks, what
results in broken alarm if any of such events happens after the alarm has
been set. Fix this by simplifying s3c_rtc_enable/disable_clk() functions
to rely on proper reference counting in clock core and move alarm enable
counter to s3c_rtc_setaie() function.
Signed-off-by: default avatarMarek Szyprowski <m.szyprowski@samsung.com>
Reviewed-by: default avatarKrzysztof Kozlowski <krzk@kernel.org>
Signed-off-by: default avatarAlexandre Belloni <alexandre.belloni@bootlin.com>
parent f724c6be
...@@ -39,7 +39,7 @@ struct s3c_rtc { ...@@ -39,7 +39,7 @@ struct s3c_rtc {
void __iomem *base; void __iomem *base;
struct clk *rtc_clk; struct clk *rtc_clk;
struct clk *rtc_src_clk; struct clk *rtc_src_clk;
bool clk_disabled; bool alarm_enabled;
const struct s3c_rtc_data *data; const struct s3c_rtc_data *data;
...@@ -47,7 +47,7 @@ struct s3c_rtc { ...@@ -47,7 +47,7 @@ struct s3c_rtc {
int irq_tick; int irq_tick;
spinlock_t pie_lock; spinlock_t pie_lock;
spinlock_t alarm_clk_lock; spinlock_t alarm_lock;
int ticnt_save; int ticnt_save;
int ticnt_en_save; int ticnt_en_save;
...@@ -70,44 +70,27 @@ struct s3c_rtc_data { ...@@ -70,44 +70,27 @@ struct s3c_rtc_data {
static int s3c_rtc_enable_clk(struct s3c_rtc *info) static int s3c_rtc_enable_clk(struct s3c_rtc *info)
{ {
unsigned long irq_flags; int ret;
int ret = 0;
spin_lock_irqsave(&info->alarm_clk_lock, irq_flags); ret = clk_enable(info->rtc_clk);
if (ret)
return ret;
if (info->clk_disabled) { if (info->data->needs_src_clk) {
ret = clk_enable(info->rtc_clk); ret = clk_enable(info->rtc_src_clk);
if (ret) if (ret) {
goto out; clk_disable(info->rtc_clk);
return ret;
if (info->data->needs_src_clk) {
ret = clk_enable(info->rtc_src_clk);
if (ret) {
clk_disable(info->rtc_clk);
goto out;
}
} }
info->clk_disabled = false;
} }
return 0;
out:
spin_unlock_irqrestore(&info->alarm_clk_lock, irq_flags);
return ret;
} }
static void s3c_rtc_disable_clk(struct s3c_rtc *info) static void s3c_rtc_disable_clk(struct s3c_rtc *info)
{ {
unsigned long irq_flags; if (info->data->needs_src_clk)
clk_disable(info->rtc_src_clk);
spin_lock_irqsave(&info->alarm_clk_lock, irq_flags); clk_disable(info->rtc_clk);
if (!info->clk_disabled) {
if (info->data->needs_src_clk)
clk_disable(info->rtc_src_clk);
clk_disable(info->rtc_clk);
info->clk_disabled = true;
}
spin_unlock_irqrestore(&info->alarm_clk_lock, irq_flags);
} }
/* IRQ Handlers */ /* IRQ Handlers */
...@@ -135,6 +118,7 @@ static irqreturn_t s3c_rtc_alarmirq(int irq, void *id) ...@@ -135,6 +118,7 @@ static irqreturn_t s3c_rtc_alarmirq(int irq, void *id)
static int s3c_rtc_setaie(struct device *dev, unsigned int enabled) static int s3c_rtc_setaie(struct device *dev, unsigned int enabled)
{ {
struct s3c_rtc *info = dev_get_drvdata(dev); struct s3c_rtc *info = dev_get_drvdata(dev);
unsigned long flags;
unsigned int tmp; unsigned int tmp;
int ret; int ret;
...@@ -151,17 +135,19 @@ static int s3c_rtc_setaie(struct device *dev, unsigned int enabled) ...@@ -151,17 +135,19 @@ static int s3c_rtc_setaie(struct device *dev, unsigned int enabled)
writeb(tmp, info->base + S3C2410_RTCALM); writeb(tmp, info->base + S3C2410_RTCALM);
s3c_rtc_disable_clk(info); spin_lock_irqsave(&info->alarm_lock, flags);
if (enabled) { if (info->alarm_enabled && !enabled)
ret = s3c_rtc_enable_clk(info);
if (ret)
return ret;
} else {
s3c_rtc_disable_clk(info); s3c_rtc_disable_clk(info);
} else if (!info->alarm_enabled && enabled)
ret = s3c_rtc_enable_clk(info);
return 0; info->alarm_enabled = enabled;
spin_unlock_irqrestore(&info->alarm_lock, flags);
s3c_rtc_disable_clk(info);
return ret;
} }
/* Set RTC frequency */ /* Set RTC frequency */
...@@ -357,10 +343,10 @@ static int s3c_rtc_setalarm(struct device *dev, struct rtc_wkalrm *alrm) ...@@ -357,10 +343,10 @@ static int s3c_rtc_setalarm(struct device *dev, struct rtc_wkalrm *alrm)
writeb(alrm_en, info->base + S3C2410_RTCALM); writeb(alrm_en, info->base + S3C2410_RTCALM);
s3c_rtc_disable_clk(info);
s3c_rtc_setaie(dev, alrm->enabled); s3c_rtc_setaie(dev, alrm->enabled);
s3c_rtc_disable_clk(info);
return 0; return 0;
} }
...@@ -491,7 +477,7 @@ static int s3c_rtc_probe(struct platform_device *pdev) ...@@ -491,7 +477,7 @@ static int s3c_rtc_probe(struct platform_device *pdev)
return -EINVAL; return -EINVAL;
} }
spin_lock_init(&info->pie_lock); spin_lock_init(&info->pie_lock);
spin_lock_init(&info->alarm_clk_lock); spin_lock_init(&info->alarm_lock);
platform_set_drvdata(pdev, info); platform_set_drvdata(pdev, info);
...@@ -591,6 +577,8 @@ static int s3c_rtc_probe(struct platform_device *pdev) ...@@ -591,6 +577,8 @@ static int s3c_rtc_probe(struct platform_device *pdev)
s3c_rtc_setfreq(info, 1); s3c_rtc_setfreq(info, 1);
s3c_rtc_disable_clk(info);
return 0; return 0;
err_nortc: err_nortc:
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment