Skip to content

Commit 489405f

Browse files
gclementtorvalds
authored andcommitted
rtc: armada38x: fix concurrency access in armada38x_rtc_set_time
While setting the time, the RTC TIME register should not be accessed. However due to hardware constraints, setting the RTC time involves sleeping during 100ms. This sleep was done outside the critical section protected by the spinlock, so it was possible to read the RTC TIME register and get an incorrect value. This patch introduces a mutex for protecting the RTC TIME access, unlike the spinlock it is allowed to sleep in a critical section protected by a mutex. The RTC STATUS register can still be used from the interrupt handler but it has no effect on setting the time. Signed-off-by: Gregory CLEMENT <gregory.clement@free-electrons.com> Acked-by: Alexandre Belloni <alexandre.belloni@free-electrons.com> Acked-by: Andrew Lunn <andrew@lunn.ch> Cc: Alessandro Zummo <a.zummo@towertech.it> Cc: <stable@vger.kernel.org> [4.0] Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
1 parent b1432a2 commit 489405f

File tree

1 file changed

+12
-12
lines changed

1 file changed

+12
-12
lines changed

drivers/rtc/rtc-armada38x.c

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,13 @@ struct armada38x_rtc {
4040
void __iomem *regs;
4141
void __iomem *regs_soc;
4242
spinlock_t lock;
43+
/*
44+
* While setting the time, the RTC TIME register should not be
45+
* accessed. Setting the RTC time involves sleeping during
46+
* 100ms, so a mutex instead of a spinlock is used to protect
47+
* it
48+
*/
49+
struct mutex mutex_time;
4350
int irq;
4451
};
4552

@@ -59,8 +66,7 @@ static int armada38x_rtc_read_time(struct device *dev, struct rtc_time *tm)
5966
struct armada38x_rtc *rtc = dev_get_drvdata(dev);
6067
unsigned long time, time_check, flags;
6168

62-
spin_lock_irqsave(&rtc->lock, flags);
63-
69+
mutex_lock(&rtc->mutex_time);
6470
time = readl(rtc->regs + RTC_TIME);
6571
/*
6672
* WA for failing time set attempts. As stated in HW ERRATA if
@@ -71,7 +77,7 @@ static int armada38x_rtc_read_time(struct device *dev, struct rtc_time *tm)
7177
if ((time_check - time) > 1)
7278
time_check = readl(rtc->regs + RTC_TIME);
7379

74-
spin_unlock_irqrestore(&rtc->lock, flags);
80+
mutex_unlock(&rtc->mutex_time);
7581

7682
rtc_time_to_tm(time_check, tm);
7783

@@ -94,19 +100,12 @@ static int armada38x_rtc_set_time(struct device *dev, struct rtc_time *tm)
94100
* then wait for 100ms before writing to the time register to be
95101
* sure that the data will be taken into account.
96102
*/
97-
spin_lock_irqsave(&rtc->lock, flags);
98-
103+
mutex_lock(&rtc->mutex_time);
99104
rtc_delayed_write(0, rtc, RTC_STATUS);
100-
101-
spin_unlock_irqrestore(&rtc->lock, flags);
102-
103105
msleep(100);
104-
105-
spin_lock_irqsave(&rtc->lock, flags);
106-
107106
rtc_delayed_write(time, rtc, RTC_TIME);
107+
mutex_unlock(&rtc->mutex_time);
108108

109-
spin_unlock_irqrestore(&rtc->lock, flags);
110109
out:
111110
return ret;
112111
}
@@ -230,6 +229,7 @@ static __init int armada38x_rtc_probe(struct platform_device *pdev)
230229
return -ENOMEM;
231230

232231
spin_lock_init(&rtc->lock);
232+
mutex_init(&rtc->mutex_time);
233233

234234
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "rtc");
235235
rtc->regs = devm_ioremap_resource(&pdev->dev, res);

0 commit comments

Comments
 (0)