Skip to content

Commit eea7791

Browse files
Wolfram Sangalexandrebelloni
authored andcommitted
rtc: rzn1: implement one-second accuracy for alarms
The hardware alarm only supports one-minute accuracy which is coarse and disables UIE usage. Use the 1-second interrupt to achieve per-second accuracy. It is activated once we hit the per-minute alarm. The new feature is optional. When there is no 1-second interrupt, old behaviour with per-minute accuracy is used as before. With this feature, all tests of 'rtctest' are successfully passed. Signed-off-by: Wolfram Sang <wsa+renesas@sang-engineering.com> Link: https://lore.kernel.org/r/20250305101038.9933-2-wsa+renesas@sang-engineering.com Signed-off-by: Alexandre Belloni <alexandre.belloni@bootlin.com>
1 parent 0a243de commit eea7791

File tree

1 file changed

+91
-17
lines changed

1 file changed

+91
-17
lines changed

drivers/rtc/rtc-rzn1.c

Lines changed: 91 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
#include <linux/platform_device.h>
2020
#include <linux/pm_runtime.h>
2121
#include <linux/rtc.h>
22+
#include <linux/spinlock.h>
2223

2324
#define RZN1_RTC_CTL0 0x00
2425
#define RZN1_RTC_CTL0_SLSB_SUBU 0
@@ -27,6 +28,7 @@
2728
#define RZN1_RTC_CTL0_CE BIT(7)
2829

2930
#define RZN1_RTC_CTL1 0x04
31+
#define RZN1_RTC_CTL1_1SE BIT(3)
3032
#define RZN1_RTC_CTL1_ALME BIT(4)
3133

3234
#define RZN1_RTC_CTL2 0x08
@@ -58,6 +60,13 @@
5860
struct rzn1_rtc {
5961
struct rtc_device *rtcdev;
6062
void __iomem *base;
63+
/*
64+
* Protects access to RZN1_RTC_CTL1 reg. rtc_lock with threaded_irqs
65+
* would introduce race conditions when switching interrupts because
66+
* of potential sleeps
67+
*/
68+
spinlock_t ctl1_access_lock;
69+
struct rtc_time tm_alarm;
6170
};
6271

6372
static void rzn1_rtc_get_time_snapshot(struct rzn1_rtc *rtc, struct rtc_time *tm)
@@ -135,23 +144,77 @@ static int rzn1_rtc_set_time(struct device *dev, struct rtc_time *tm)
135144
static irqreturn_t rzn1_rtc_alarm_irq(int irq, void *dev_id)
136145
{
137146
struct rzn1_rtc *rtc = dev_id;
147+
u32 ctl1, set_irq_bits = 0;
148+
149+
if (rtc->tm_alarm.tm_sec == 0)
150+
rtc_update_irq(rtc->rtcdev, 1, RTC_AF | RTC_IRQF);
151+
else
152+
/* Switch to 1s interrupts */
153+
set_irq_bits = RZN1_RTC_CTL1_1SE;
138154

139-
rtc_update_irq(rtc->rtcdev, 1, RTC_AF | RTC_IRQF);
155+
guard(spinlock)(&rtc->ctl1_access_lock);
156+
157+
ctl1 = readl(rtc->base + RZN1_RTC_CTL1);
158+
ctl1 &= ~RZN1_RTC_CTL1_ALME;
159+
ctl1 |= set_irq_bits;
160+
writel(ctl1, rtc->base + RZN1_RTC_CTL1);
161+
162+
return IRQ_HANDLED;
163+
}
164+
165+
static irqreturn_t rzn1_rtc_1s_irq(int irq, void *dev_id)
166+
{
167+
struct rzn1_rtc *rtc = dev_id;
168+
u32 ctl1;
169+
170+
if (readl(rtc->base + RZN1_RTC_SECC) == bin2bcd(rtc->tm_alarm.tm_sec)) {
171+
guard(spinlock)(&rtc->ctl1_access_lock);
172+
173+
ctl1 = readl(rtc->base + RZN1_RTC_CTL1);
174+
ctl1 &= ~RZN1_RTC_CTL1_1SE;
175+
writel(ctl1, rtc->base + RZN1_RTC_CTL1);
176+
177+
rtc_update_irq(rtc->rtcdev, 1, RTC_AF | RTC_IRQF);
178+
}
140179

141180
return IRQ_HANDLED;
142181
}
143182

144183
static int rzn1_rtc_alarm_irq_enable(struct device *dev, unsigned int enable)
145184
{
146185
struct rzn1_rtc *rtc = dev_get_drvdata(dev);
147-
u32 ctl1 = readl(rtc->base + RZN1_RTC_CTL1);
186+
struct rtc_time *tm = &rtc->tm_alarm, tm_now;
187+
u32 ctl1;
188+
int ret;
148189

149-
if (enable)
150-
ctl1 |= RZN1_RTC_CTL1_ALME;
151-
else
152-
ctl1 &= ~RZN1_RTC_CTL1_ALME;
190+
guard(spinlock_irqsave)(&rtc->ctl1_access_lock);
153191

154-
writel(ctl1, rtc->base + RZN1_RTC_CTL1);
192+
ctl1 = readl(rtc->base + RZN1_RTC_CTL1);
193+
194+
if (enable) {
195+
/*
196+
* Use alarm interrupt if alarm time is at least a minute away
197+
* or less than a minute but in the next minute. Otherwise use
198+
* 1 second interrupt to wait for the proper second
199+
*/
200+
do {
201+
ctl1 &= ~(RZN1_RTC_CTL1_ALME | RZN1_RTC_CTL1_1SE);
202+
203+
ret = rzn1_rtc_read_time(dev, &tm_now);
204+
if (ret)
205+
return ret;
206+
207+
if (rtc_tm_sub(tm, &tm_now) > 59 || tm->tm_min != tm_now.tm_min)
208+
ctl1 |= RZN1_RTC_CTL1_ALME;
209+
else
210+
ctl1 |= RZN1_RTC_CTL1_1SE;
211+
212+
writel(ctl1, rtc->base + RZN1_RTC_CTL1);
213+
} while (readl(rtc->base + RZN1_RTC_SECC) != bin2bcd(tm_now.tm_sec));
214+
} else {
215+
ctl1 &= ~(RZN1_RTC_CTL1_ALME | RZN1_RTC_CTL1_1SE);
216+
writel(ctl1, rtc->base + RZN1_RTC_CTL1);
217+
}
155218

156219
return 0;
157220
}
@@ -185,7 +248,7 @@ static int rzn1_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm)
185248
}
186249

187250
ctl1 = readl(rtc->base + RZN1_RTC_CTL1);
188-
alrm->enabled = !!(ctl1 & RZN1_RTC_CTL1_ALME);
251+
alrm->enabled = !!(ctl1 & (RZN1_RTC_CTL1_ALME | RZN1_RTC_CTL1_1SE));
189252

190253
return 0;
191254
}
@@ -216,6 +279,8 @@ static int rzn1_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm)
216279
writel(bin2bcd(tm->tm_hour), rtc->base + RZN1_RTC_ALH);
217280
writel(BIT(wday), rtc->base + RZN1_RTC_ALW);
218281

282+
rtc->tm_alarm = alrm->time;
283+
219284
rzn1_rtc_alarm_irq_enable(dev, alrm->enabled);
220285

221286
return 0;
@@ -304,7 +369,7 @@ static const struct rtc_class_ops rzn1_rtc_ops = {
304369
static int rzn1_rtc_probe(struct platform_device *pdev)
305370
{
306371
struct rzn1_rtc *rtc;
307-
int alarm_irq;
372+
int irq;
308373
int ret;
309374

310375
rtc = devm_kzalloc(&pdev->dev, sizeof(*rtc), GFP_KERNEL);
@@ -317,9 +382,9 @@ static int rzn1_rtc_probe(struct platform_device *pdev)
317382
if (IS_ERR(rtc->base))
318383
return dev_err_probe(&pdev->dev, PTR_ERR(rtc->base), "Missing reg\n");
319384

320-
alarm_irq = platform_get_irq(pdev, 0);
321-
if (alarm_irq < 0)
322-
return alarm_irq;
385+
irq = platform_get_irq_byname(pdev, "alarm");
386+
if (irq < 0)
387+
return irq;
323388

324389
rtc->rtcdev = devm_rtc_allocate_device(&pdev->dev);
325390
if (IS_ERR(rtc->rtcdev))
@@ -329,8 +394,6 @@ static int rzn1_rtc_probe(struct platform_device *pdev)
329394
rtc->rtcdev->range_max = RTC_TIMESTAMP_END_2099;
330395
rtc->rtcdev->alarm_offset_max = 7 * 86400;
331396
rtc->rtcdev->ops = &rzn1_rtc_ops;
332-
set_bit(RTC_FEATURE_ALARM_RES_MINUTE, rtc->rtcdev->features);
333-
clear_bit(RTC_FEATURE_UPDATE_INTERRUPT, rtc->rtcdev->features);
334397

335398
ret = devm_pm_runtime_enable(&pdev->dev);
336399
if (ret < 0)
@@ -349,13 +412,24 @@ static int rzn1_rtc_probe(struct platform_device *pdev)
349412
/* Disable all interrupts */
350413
writel(0, rtc->base + RZN1_RTC_CTL1);
351414

352-
ret = devm_request_irq(&pdev->dev, alarm_irq, rzn1_rtc_alarm_irq, 0,
353-
dev_name(&pdev->dev), rtc);
415+
spin_lock_init(&rtc->ctl1_access_lock);
416+
417+
ret = devm_request_irq(&pdev->dev, irq, rzn1_rtc_alarm_irq, 0, "RZN1 RTC Alarm", rtc);
354418
if (ret) {
355-
dev_err(&pdev->dev, "RTC timer interrupt not available\n");
419+
dev_err(&pdev->dev, "RTC alarm interrupt not available\n");
356420
goto dis_runtime_pm;
357421
}
358422

423+
irq = platform_get_irq_byname_optional(pdev, "pps");
424+
if (irq >= 0)
425+
ret = devm_request_irq(&pdev->dev, irq, rzn1_rtc_1s_irq, 0, "RZN1 RTC 1s", rtc);
426+
427+
if (irq < 0 || ret) {
428+
set_bit(RTC_FEATURE_ALARM_RES_MINUTE, rtc->rtcdev->features);
429+
clear_bit(RTC_FEATURE_UPDATE_INTERRUPT, rtc->rtcdev->features);
430+
dev_warn(&pdev->dev, "RTC pps interrupt not available. Alarm has only minute accuracy\n");
431+
}
432+
359433
ret = devm_rtc_register_device(rtc->rtcdev);
360434
if (ret)
361435
goto dis_runtime_pm;

0 commit comments

Comments
 (0)