mirror of https://gitee.com/openkylin/linux.git
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: Marek Szyprowski <m.szyprowski@samsung.com> Reviewed-by: Krzysztof Kozlowski <krzk@kernel.org> Signed-off-by: Alexandre Belloni <alexandre.belloni@bootlin.com>
This commit is contained in:
parent
f724c6bee1
commit
5a5b614ba6
|
@ -39,7 +39,7 @@ struct s3c_rtc {
|
|||
void __iomem *base;
|
||||
struct clk *rtc_clk;
|
||||
struct clk *rtc_src_clk;
|
||||
bool clk_disabled;
|
||||
bool alarm_enabled;
|
||||
|
||||
const struct s3c_rtc_data *data;
|
||||
|
||||
|
@ -47,7 +47,7 @@ struct s3c_rtc {
|
|||
int irq_tick;
|
||||
|
||||
spinlock_t pie_lock;
|
||||
spinlock_t alarm_clk_lock;
|
||||
spinlock_t alarm_lock;
|
||||
|
||||
int ticnt_save;
|
||||
int ticnt_en_save;
|
||||
|
@ -70,44 +70,27 @@ struct s3c_rtc_data {
|
|||
|
||||
static int s3c_rtc_enable_clk(struct s3c_rtc *info)
|
||||
{
|
||||
unsigned long irq_flags;
|
||||
int ret = 0;
|
||||
int ret;
|
||||
|
||||
spin_lock_irqsave(&info->alarm_clk_lock, irq_flags);
|
||||
ret = clk_enable(info->rtc_clk);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (info->clk_disabled) {
|
||||
ret = clk_enable(info->rtc_clk);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
if (info->data->needs_src_clk) {
|
||||
ret = clk_enable(info->rtc_src_clk);
|
||||
if (ret) {
|
||||
clk_disable(info->rtc_clk);
|
||||
goto out;
|
||||
}
|
||||
if (info->data->needs_src_clk) {
|
||||
ret = clk_enable(info->rtc_src_clk);
|
||||
if (ret) {
|
||||
clk_disable(info->rtc_clk);
|
||||
return ret;
|
||||
}
|
||||
info->clk_disabled = false;
|
||||
}
|
||||
|
||||
out:
|
||||
spin_unlock_irqrestore(&info->alarm_clk_lock, irq_flags);
|
||||
|
||||
return ret;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void s3c_rtc_disable_clk(struct s3c_rtc *info)
|
||||
{
|
||||
unsigned long irq_flags;
|
||||
|
||||
spin_lock_irqsave(&info->alarm_clk_lock, irq_flags);
|
||||
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);
|
||||
if (info->data->needs_src_clk)
|
||||
clk_disable(info->rtc_src_clk);
|
||||
clk_disable(info->rtc_clk);
|
||||
}
|
||||
|
||||
/* IRQ Handlers */
|
||||
|
@ -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)
|
||||
{
|
||||
struct s3c_rtc *info = dev_get_drvdata(dev);
|
||||
unsigned long flags;
|
||||
unsigned int tmp;
|
||||
int ret;
|
||||
|
||||
|
@ -151,17 +135,19 @@ static int s3c_rtc_setaie(struct device *dev, unsigned int enabled)
|
|||
|
||||
writeb(tmp, info->base + S3C2410_RTCALM);
|
||||
|
||||
spin_lock_irqsave(&info->alarm_lock, flags);
|
||||
|
||||
if (info->alarm_enabled && !enabled)
|
||||
s3c_rtc_disable_clk(info);
|
||||
else if (!info->alarm_enabled && enabled)
|
||||
ret = s3c_rtc_enable_clk(info);
|
||||
|
||||
info->alarm_enabled = enabled;
|
||||
spin_unlock_irqrestore(&info->alarm_lock, flags);
|
||||
|
||||
s3c_rtc_disable_clk(info);
|
||||
|
||||
if (enabled) {
|
||||
ret = s3c_rtc_enable_clk(info);
|
||||
if (ret)
|
||||
return ret;
|
||||
} else {
|
||||
s3c_rtc_disable_clk(info);
|
||||
}
|
||||
|
||||
return 0;
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Set RTC frequency */
|
||||
|
@ -357,10 +343,10 @@ static int s3c_rtc_setalarm(struct device *dev, struct rtc_wkalrm *alrm)
|
|||
|
||||
writeb(alrm_en, info->base + S3C2410_RTCALM);
|
||||
|
||||
s3c_rtc_disable_clk(info);
|
||||
|
||||
s3c_rtc_setaie(dev, alrm->enabled);
|
||||
|
||||
s3c_rtc_disable_clk(info);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -491,7 +477,7 @@ static int s3c_rtc_probe(struct platform_device *pdev)
|
|||
return -EINVAL;
|
||||
}
|
||||
spin_lock_init(&info->pie_lock);
|
||||
spin_lock_init(&info->alarm_clk_lock);
|
||||
spin_lock_init(&info->alarm_lock);
|
||||
|
||||
platform_set_drvdata(pdev, info);
|
||||
|
||||
|
@ -591,6 +577,8 @@ static int s3c_rtc_probe(struct platform_device *pdev)
|
|||
|
||||
s3c_rtc_setfreq(info, 1);
|
||||
|
||||
s3c_rtc_disable_clk(info);
|
||||
|
||||
return 0;
|
||||
|
||||
err_nortc:
|
||||
|
|
Loading…
Reference in New Issue