Skip to content

Commit be4a11c

Browse files
miquelraynalalexandrebelloni
authored andcommitted
rtc: rzn1: Add oscillator offset support
The RZN1 RTC can compensate the imprecision of the oscillator up to approximately 190ppm. Seconds can last slightly shorter or longer depending on the configuration. Below ~65ppm of correction, we can change the time spent in a second every minute, which is the most accurate compensation that the RTC can offer. Above, the compensation will be active every 20s. Signed-off-by: Miquel Raynal <[email protected]> Signed-off-by: Alexandre Belloni <[email protected]> Link: https://lore.kernel.org/r/[email protected]
1 parent b5ad1bf commit be4a11c

File tree

1 file changed

+73
-0
lines changed

1 file changed

+73
-0
lines changed

drivers/rtc/rtc-rzn1.c

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -245,12 +245,85 @@ static int rzn1_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm)
245245
return 0;
246246
}
247247

248+
static int rzn1_rtc_read_offset(struct device *dev, long *offset)
249+
{
250+
struct rzn1_rtc *rtc = dev_get_drvdata(dev);
251+
unsigned int ppb_per_step;
252+
bool subtract;
253+
u32 val;
254+
255+
val = readl(rtc->base + RZN1_RTC_SUBU);
256+
ppb_per_step = val & RZN1_RTC_SUBU_DEV ? 1017 : 3051;
257+
subtract = val & RZN1_RTC_SUBU_DECR;
258+
val &= 0x3F;
259+
260+
if (!val)
261+
*offset = 0;
262+
else if (subtract)
263+
*offset = -(((~val) & 0x3F) + 1) * ppb_per_step;
264+
else
265+
*offset = (val - 1) * ppb_per_step;
266+
267+
return 0;
268+
}
269+
270+
static int rzn1_rtc_set_offset(struct device *dev, long offset)
271+
{
272+
struct rzn1_rtc *rtc = dev_get_drvdata(dev);
273+
unsigned int steps;
274+
int stepsh, stepsl;
275+
u32 val;
276+
int ret;
277+
278+
/*
279+
* Check which resolution mode (every 20 or 60s) can be used.
280+
* Between 2 and 124 clock pulses can be added or substracted.
281+
*
282+
* In 20s mode, the minimum resolution is 2 / (32768 * 20) which is
283+
* close to 3051 ppb. In 60s mode, the resolution is closer to 1017.
284+
*/
285+
stepsh = DIV_ROUND_CLOSEST(offset, 1017);
286+
stepsl = DIV_ROUND_CLOSEST(offset, 3051);
287+
288+
if (stepsh >= -0x3E && stepsh <= 0x3E) {
289+
/* 1017 ppb per step */
290+
steps = stepsh;
291+
val |= RZN1_RTC_SUBU_DEV;
292+
} else if (stepsl >= -0x3E && stepsl <= 0x3E) {
293+
/* 3051 ppb per step */
294+
steps = stepsl;
295+
} else {
296+
return -ERANGE;
297+
}
298+
299+
if (!steps)
300+
return 0;
301+
302+
if (steps > 0) {
303+
val |= steps + 1;
304+
} else {
305+
val |= RZN1_RTC_SUBU_DECR;
306+
val |= (~(-steps - 1)) & 0x3F;
307+
}
308+
309+
ret = readl_poll_timeout(rtc->base + RZN1_RTC_CTL2, val,
310+
!(val & RZN1_RTC_CTL2_WUST), 100, 2000000);
311+
if (ret)
312+
return ret;
313+
314+
writel(val, rtc->base + RZN1_RTC_SUBU);
315+
316+
return 0;
317+
}
318+
248319
static const struct rtc_class_ops rzn1_rtc_ops = {
249320
.read_time = rzn1_rtc_read_time,
250321
.set_time = rzn1_rtc_set_time,
251322
.read_alarm = rzn1_rtc_read_alarm,
252323
.set_alarm = rzn1_rtc_set_alarm,
253324
.alarm_irq_enable = rzn1_rtc_alarm_irq_enable,
325+
.read_offset = rzn1_rtc_read_offset,
326+
.set_offset = rzn1_rtc_set_offset,
254327
};
255328

256329
static int rzn1_rtc_probe(struct platform_device *pdev)

0 commit comments

Comments
 (0)