Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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>

authored by

Wolfram Sang and committed by
Alexandre Belloni
eea7791e 0a243de9

+91 -17
+91 -17
drivers/rtc/rtc-rzn1.c
··· 19 19 #include <linux/platform_device.h> 20 20 #include <linux/pm_runtime.h> 21 21 #include <linux/rtc.h> 22 + #include <linux/spinlock.h> 22 23 23 24 #define RZN1_RTC_CTL0 0x00 24 25 #define RZN1_RTC_CTL0_SLSB_SUBU 0 ··· 28 27 #define RZN1_RTC_CTL0_CE BIT(7) 29 28 30 29 #define RZN1_RTC_CTL1 0x04 30 + #define RZN1_RTC_CTL1_1SE BIT(3) 31 31 #define RZN1_RTC_CTL1_ALME BIT(4) 32 32 33 33 #define RZN1_RTC_CTL2 0x08 ··· 60 58 struct rzn1_rtc { 61 59 struct rtc_device *rtcdev; 62 60 void __iomem *base; 61 + /* 62 + * Protects access to RZN1_RTC_CTL1 reg. rtc_lock with threaded_irqs 63 + * would introduce race conditions when switching interrupts because 64 + * of potential sleeps 65 + */ 66 + spinlock_t ctl1_access_lock; 67 + struct rtc_time tm_alarm; 63 68 }; 64 69 65 70 static void rzn1_rtc_get_time_snapshot(struct rzn1_rtc *rtc, struct rtc_time *tm) ··· 144 135 static irqreturn_t rzn1_rtc_alarm_irq(int irq, void *dev_id) 145 136 { 146 137 struct rzn1_rtc *rtc = dev_id; 138 + u32 ctl1, set_irq_bits = 0; 147 139 148 - rtc_update_irq(rtc->rtcdev, 1, RTC_AF | RTC_IRQF); 140 + if (rtc->tm_alarm.tm_sec == 0) 141 + rtc_update_irq(rtc->rtcdev, 1, RTC_AF | RTC_IRQF); 142 + else 143 + /* Switch to 1s interrupts */ 144 + set_irq_bits = RZN1_RTC_CTL1_1SE; 145 + 146 + guard(spinlock)(&rtc->ctl1_access_lock); 147 + 148 + ctl1 = readl(rtc->base + RZN1_RTC_CTL1); 149 + ctl1 &= ~RZN1_RTC_CTL1_ALME; 150 + ctl1 |= set_irq_bits; 151 + writel(ctl1, rtc->base + RZN1_RTC_CTL1); 152 + 153 + return IRQ_HANDLED; 154 + } 155 + 156 + static irqreturn_t rzn1_rtc_1s_irq(int irq, void *dev_id) 157 + { 158 + struct rzn1_rtc *rtc = dev_id; 159 + u32 ctl1; 160 + 161 + if (readl(rtc->base + RZN1_RTC_SECC) == bin2bcd(rtc->tm_alarm.tm_sec)) { 162 + guard(spinlock)(&rtc->ctl1_access_lock); 163 + 164 + ctl1 = readl(rtc->base + RZN1_RTC_CTL1); 165 + ctl1 &= ~RZN1_RTC_CTL1_1SE; 166 + writel(ctl1, rtc->base + RZN1_RTC_CTL1); 167 + 168 + rtc_update_irq(rtc->rtcdev, 1, RTC_AF | RTC_IRQF); 169 + } 149 170 150 171 return IRQ_HANDLED; 151 172 } ··· 183 144 static int rzn1_rtc_alarm_irq_enable(struct device *dev, unsigned int enable) 184 145 { 185 146 struct rzn1_rtc *rtc = dev_get_drvdata(dev); 186 - u32 ctl1 = readl(rtc->base + RZN1_RTC_CTL1); 147 + struct rtc_time *tm = &rtc->tm_alarm, tm_now; 148 + u32 ctl1; 149 + int ret; 187 150 188 - if (enable) 189 - ctl1 |= RZN1_RTC_CTL1_ALME; 190 - else 191 - ctl1 &= ~RZN1_RTC_CTL1_ALME; 151 + guard(spinlock_irqsave)(&rtc->ctl1_access_lock); 192 152 193 - writel(ctl1, rtc->base + RZN1_RTC_CTL1); 153 + ctl1 = readl(rtc->base + RZN1_RTC_CTL1); 154 + 155 + if (enable) { 156 + /* 157 + * Use alarm interrupt if alarm time is at least a minute away 158 + * or less than a minute but in the next minute. Otherwise use 159 + * 1 second interrupt to wait for the proper second 160 + */ 161 + do { 162 + ctl1 &= ~(RZN1_RTC_CTL1_ALME | RZN1_RTC_CTL1_1SE); 163 + 164 + ret = rzn1_rtc_read_time(dev, &tm_now); 165 + if (ret) 166 + return ret; 167 + 168 + if (rtc_tm_sub(tm, &tm_now) > 59 || tm->tm_min != tm_now.tm_min) 169 + ctl1 |= RZN1_RTC_CTL1_ALME; 170 + else 171 + ctl1 |= RZN1_RTC_CTL1_1SE; 172 + 173 + writel(ctl1, rtc->base + RZN1_RTC_CTL1); 174 + } while (readl(rtc->base + RZN1_RTC_SECC) != bin2bcd(tm_now.tm_sec)); 175 + } else { 176 + ctl1 &= ~(RZN1_RTC_CTL1_ALME | RZN1_RTC_CTL1_1SE); 177 + writel(ctl1, rtc->base + RZN1_RTC_CTL1); 178 + } 194 179 195 180 return 0; 196 181 } ··· 248 185 } 249 186 250 187 ctl1 = readl(rtc->base + RZN1_RTC_CTL1); 251 - alrm->enabled = !!(ctl1 & RZN1_RTC_CTL1_ALME); 188 + alrm->enabled = !!(ctl1 & (RZN1_RTC_CTL1_ALME | RZN1_RTC_CTL1_1SE)); 252 189 253 190 return 0; 254 191 } ··· 278 215 writel(bin2bcd(tm->tm_min), rtc->base + RZN1_RTC_ALM); 279 216 writel(bin2bcd(tm->tm_hour), rtc->base + RZN1_RTC_ALH); 280 217 writel(BIT(wday), rtc->base + RZN1_RTC_ALW); 218 + 219 + rtc->tm_alarm = alrm->time; 281 220 282 221 rzn1_rtc_alarm_irq_enable(dev, alrm->enabled); 283 222 ··· 369 304 static int rzn1_rtc_probe(struct platform_device *pdev) 370 305 { 371 306 struct rzn1_rtc *rtc; 372 - int alarm_irq; 307 + int irq; 373 308 int ret; 374 309 375 310 rtc = devm_kzalloc(&pdev->dev, sizeof(*rtc), GFP_KERNEL); ··· 382 317 if (IS_ERR(rtc->base)) 383 318 return dev_err_probe(&pdev->dev, PTR_ERR(rtc->base), "Missing reg\n"); 384 319 385 - alarm_irq = platform_get_irq(pdev, 0); 386 - if (alarm_irq < 0) 387 - return alarm_irq; 320 + irq = platform_get_irq_byname(pdev, "alarm"); 321 + if (irq < 0) 322 + return irq; 388 323 389 324 rtc->rtcdev = devm_rtc_allocate_device(&pdev->dev); 390 325 if (IS_ERR(rtc->rtcdev)) ··· 394 329 rtc->rtcdev->range_max = RTC_TIMESTAMP_END_2099; 395 330 rtc->rtcdev->alarm_offset_max = 7 * 86400; 396 331 rtc->rtcdev->ops = &rzn1_rtc_ops; 397 - set_bit(RTC_FEATURE_ALARM_RES_MINUTE, rtc->rtcdev->features); 398 - clear_bit(RTC_FEATURE_UPDATE_INTERRUPT, rtc->rtcdev->features); 399 332 400 333 ret = devm_pm_runtime_enable(&pdev->dev); 401 334 if (ret < 0) ··· 412 349 /* Disable all interrupts */ 413 350 writel(0, rtc->base + RZN1_RTC_CTL1); 414 351 415 - ret = devm_request_irq(&pdev->dev, alarm_irq, rzn1_rtc_alarm_irq, 0, 416 - dev_name(&pdev->dev), rtc); 352 + spin_lock_init(&rtc->ctl1_access_lock); 353 + 354 + ret = devm_request_irq(&pdev->dev, irq, rzn1_rtc_alarm_irq, 0, "RZN1 RTC Alarm", rtc); 417 355 if (ret) { 418 - dev_err(&pdev->dev, "RTC timer interrupt not available\n"); 356 + dev_err(&pdev->dev, "RTC alarm interrupt not available\n"); 419 357 goto dis_runtime_pm; 358 + } 359 + 360 + irq = platform_get_irq_byname_optional(pdev, "pps"); 361 + if (irq >= 0) 362 + ret = devm_request_irq(&pdev->dev, irq, rzn1_rtc_1s_irq, 0, "RZN1 RTC 1s", rtc); 363 + 364 + if (irq < 0 || ret) { 365 + set_bit(RTC_FEATURE_ALARM_RES_MINUTE, rtc->rtcdev->features); 366 + clear_bit(RTC_FEATURE_UPDATE_INTERRUPT, rtc->rtcdev->features); 367 + dev_warn(&pdev->dev, "RTC pps interrupt not available. Alarm has only minute accuracy\n"); 420 368 } 421 369 422 370 ret = devm_rtc_register_device(rtc->rtcdev);