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

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
Loading
Loading
Loading
Loading
+31 −43
Original line number Diff line number Diff line
@@ -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;

	spin_lock_irqsave(&info->alarm_clk_lock, irq_flags);
	int ret;

	if (info->clk_disabled) {
	ret = clk_enable(info->rtc_clk);
	if (ret)
			goto out;
		return ret;

	if (info->data->needs_src_clk) {
		ret = clk_enable(info->rtc_src_clk);
		if (ret) {
			clk_disable(info->rtc_clk);
				goto out;
			}
			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);
}

/* 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);

	s3c_rtc_disable_clk(info);
	spin_lock_irqsave(&info->alarm_lock, flags);

	if (enabled) {
	if (info->alarm_enabled && !enabled)
		s3c_rtc_disable_clk(info);
	else if (!info->alarm_enabled && enabled)
		ret = s3c_rtc_enable_clk(info);
		if (ret)
			return ret;
	} else {

	info->alarm_enabled = enabled;
	spin_unlock_irqrestore(&info->alarm_lock, flags);

	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: