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;
|
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 {
|
||||||
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 {
|
||||||
|
|
||||||
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)
|
||||||
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)
|
||||||
|
|
||||||
writeb(tmp, info->base + S3C2410_RTCALM);
|
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);
|
s3c_rtc_disable_clk(info);
|
||||||
|
|
||||||
if (enabled) {
|
return ret;
|
||||||
ret = s3c_rtc_enable_clk(info);
|
|
||||||
if (ret)
|
|
||||||
return ret;
|
|
||||||
} else {
|
|
||||||
s3c_rtc_disable_clk(info);
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Set RTC frequency */
|
/* 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);
|
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)
|
||||||
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)
|
||||||
|
|
||||||
s3c_rtc_setfreq(info, 1);
|
s3c_rtc_setfreq(info, 1);
|
||||||
|
|
||||||
|
s3c_rtc_disable_clk(info);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
err_nortc:
|
err_nortc:
|
||||||
|
|
Loading…
Reference in New Issue