Skip to content

Commit b5ad1bf

Browse files
miquelraynalalexandrebelloni
authored andcommitted
rtc: rzn1: Add alarm support
The RZN1 RTC can trigger an interrupt when reaching a particular date up to 7 days ahead. Bring support for this alarm. One drawback though, the granularity is about a minute. Signed-off-by: Miquel Raynal <[email protected]> Signed-off-by: Alexandre Belloni <[email protected]> Link: https://lore.kernel.org/r/[email protected]
1 parent deeb4b5 commit b5ad1bf

File tree

1 file changed

+105
-1
lines changed

1 file changed

+105
-1
lines changed

drivers/rtc/rtc-rzn1.c

Lines changed: 105 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -156,14 +156,107 @@ static int rzn1_rtc_set_time(struct device *dev, struct rtc_time *tm)
156156
return 0;
157157
}
158158

159+
static irqreturn_t rzn1_rtc_alarm_irq(int irq, void *dev_id)
160+
{
161+
struct rzn1_rtc *rtc = dev_id;
162+
163+
rtc_update_irq(rtc->rtcdev, 1, RTC_AF | RTC_IRQF);
164+
165+
return IRQ_HANDLED;
166+
}
167+
168+
static int rzn1_rtc_alarm_irq_enable(struct device *dev, unsigned int enable)
169+
{
170+
struct rzn1_rtc *rtc = dev_get_drvdata(dev);
171+
u32 ctl1 = readl(rtc->base + RZN1_RTC_CTL1);
172+
173+
if (enable)
174+
ctl1 |= RZN1_RTC_CTL1_ALME;
175+
else
176+
ctl1 &= ~RZN1_RTC_CTL1_ALME;
177+
178+
writel(ctl1, rtc->base + RZN1_RTC_CTL1);
179+
180+
return 0;
181+
}
182+
183+
static int rzn1_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm)
184+
{
185+
struct rzn1_rtc *rtc = dev_get_drvdata(dev);
186+
struct rtc_time *tm = &alrm->time;
187+
unsigned int min, hour, wday, delta_days;
188+
time64_t alarm;
189+
u32 ctl1;
190+
int ret;
191+
192+
ret = rzn1_rtc_read_time(dev, tm);
193+
if (ret)
194+
return ret;
195+
196+
min = readl(rtc->base + RZN1_RTC_ALM);
197+
hour = readl(rtc->base + RZN1_RTC_ALH);
198+
wday = readl(rtc->base + RZN1_RTC_ALW);
199+
200+
tm->tm_sec = 0;
201+
tm->tm_min = bcd2bin(min);
202+
tm->tm_hour = bcd2bin(hour);
203+
delta_days = ((fls(wday) - 1) - tm->tm_wday + 7) % 7;
204+
tm->tm_wday = fls(wday) - 1;
205+
206+
if (delta_days) {
207+
alarm = rtc_tm_to_time64(tm) + (delta_days * 86400);
208+
rtc_time64_to_tm(alarm, tm);
209+
}
210+
211+
ctl1 = readl(rtc->base + RZN1_RTC_CTL1);
212+
alrm->enabled = !!(ctl1 & RZN1_RTC_CTL1_ALME);
213+
214+
return 0;
215+
}
216+
217+
static int rzn1_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm)
218+
{
219+
struct rzn1_rtc *rtc = dev_get_drvdata(dev);
220+
struct rtc_time *tm = &alrm->time, tm_now;
221+
unsigned long alarm, farest;
222+
unsigned int days_ahead, wday;
223+
int ret;
224+
225+
ret = rzn1_rtc_read_time(dev, &tm_now);
226+
if (ret)
227+
return ret;
228+
229+
/* We cannot set alarms more than one week ahead */
230+
farest = rtc_tm_to_time64(&tm_now) + (7 * 86400);
231+
alarm = rtc_tm_to_time64(tm);
232+
if (time_after(alarm, farest))
233+
return -ERANGE;
234+
235+
/* Convert alarm day into week day */
236+
days_ahead = tm->tm_mday - tm_now.tm_mday;
237+
wday = (tm_now.tm_wday + days_ahead) % 7;
238+
239+
writel(bin2bcd(tm->tm_min), rtc->base + RZN1_RTC_ALM);
240+
writel(bin2bcd(tm->tm_hour), rtc->base + RZN1_RTC_ALH);
241+
writel(BIT(wday), rtc->base + RZN1_RTC_ALW);
242+
243+
rzn1_rtc_alarm_irq_enable(dev, alrm->enabled);
244+
245+
return 0;
246+
}
247+
159248
static const struct rtc_class_ops rzn1_rtc_ops = {
160249
.read_time = rzn1_rtc_read_time,
161250
.set_time = rzn1_rtc_set_time,
251+
.read_alarm = rzn1_rtc_read_alarm,
252+
.set_alarm = rzn1_rtc_set_alarm,
253+
.alarm_irq_enable = rzn1_rtc_alarm_irq_enable,
162254
};
163255

164256
static int rzn1_rtc_probe(struct platform_device *pdev)
165257
{
166258
struct rzn1_rtc *rtc;
259+
int alarm_irq;
167260
int ret;
168261

169262
rtc = devm_kzalloc(&pdev->dev, sizeof(*rtc), GFP_KERNEL);
@@ -176,14 +269,18 @@ static int rzn1_rtc_probe(struct platform_device *pdev)
176269
if (IS_ERR(rtc->base))
177270
return dev_err_probe(&pdev->dev, PTR_ERR(rtc->base), "Missing reg\n");
178271

272+
alarm_irq = platform_get_irq(pdev, 0);
273+
if (alarm_irq < 0)
274+
return alarm_irq;
275+
179276
rtc->rtcdev = devm_rtc_allocate_device(&pdev->dev);
180277
if (IS_ERR(rtc->rtcdev))
181278
return PTR_ERR(rtc);
182279

183280
rtc->rtcdev->range_min = RTC_TIMESTAMP_BEGIN_2000;
184281
rtc->rtcdev->range_max = RTC_TIMESTAMP_END_2099;
185282
rtc->rtcdev->ops = &rzn1_rtc_ops;
186-
clear_bit(RTC_FEATURE_ALARM, rtc->rtcdev->features);
283+
set_bit(RTC_FEATURE_ALARM_RES_MINUTE, rtc->rtcdev->features);
187284
clear_bit(RTC_FEATURE_UPDATE_INTERRUPT, rtc->rtcdev->features);
188285

189286
devm_pm_runtime_enable(&pdev->dev);
@@ -201,6 +298,13 @@ static int rzn1_rtc_probe(struct platform_device *pdev)
201298
/* Disable all interrupts */
202299
writel(0, rtc->base + RZN1_RTC_CTL1);
203300

301+
ret = devm_request_irq(&pdev->dev, alarm_irq, rzn1_rtc_alarm_irq, 0,
302+
dev_name(&pdev->dev), rtc);
303+
if (ret) {
304+
dev_err(&pdev->dev, "RTC timer interrupt not available\n");
305+
goto dis_runtime_pm;
306+
}
307+
204308
ret = devm_rtc_register_device(rtc->rtcdev);
205309
if (ret)
206310
goto dis_runtime_pm;

0 commit comments

Comments
 (0)