xref: /openbmc/linux/drivers/rtc/interface.c (revision 96c8f06a0fb359a9a89701a7afab6d837e466ab0)
10c86edc0SAlessandro Zummo /*
20c86edc0SAlessandro Zummo  * RTC subsystem, interface functions
30c86edc0SAlessandro Zummo  *
40c86edc0SAlessandro Zummo  * Copyright (C) 2005 Tower Technologies
50c86edc0SAlessandro Zummo  * Author: Alessandro Zummo <a.zummo@towertech.it>
60c86edc0SAlessandro Zummo  *
70c86edc0SAlessandro Zummo  * based on arch/arm/common/rtctime.c
80c86edc0SAlessandro Zummo  *
90c86edc0SAlessandro Zummo  * This program is free software; you can redistribute it and/or modify
100c86edc0SAlessandro Zummo  * it under the terms of the GNU General Public License version 2 as
110c86edc0SAlessandro Zummo  * published by the Free Software Foundation.
120c86edc0SAlessandro Zummo */
130c86edc0SAlessandro Zummo 
140c86edc0SAlessandro Zummo #include <linux/rtc.h>
15d43c36dcSAlexey Dobriyan #include <linux/sched.h>
1697144c67SDavid Brownell #include <linux/log2.h>
176610e089SJohn Stultz #include <linux/workqueue.h>
186610e089SJohn Stultz 
196610e089SJohn Stultz static int __rtc_read_time(struct rtc_device *rtc, struct rtc_time *tm)
206610e089SJohn Stultz {
216610e089SJohn Stultz 	int err;
226610e089SJohn Stultz 	if (!rtc->ops)
236610e089SJohn Stultz 		err = -ENODEV;
246610e089SJohn Stultz 	else if (!rtc->ops->read_time)
256610e089SJohn Stultz 		err = -EINVAL;
266610e089SJohn Stultz 	else {
276610e089SJohn Stultz 		memset(tm, 0, sizeof(struct rtc_time));
286610e089SJohn Stultz 		err = rtc->ops->read_time(rtc->dev.parent, tm);
296610e089SJohn Stultz 	}
306610e089SJohn Stultz 	return err;
316610e089SJohn Stultz }
320c86edc0SAlessandro Zummo 
33ab6a2d70SDavid Brownell int rtc_read_time(struct rtc_device *rtc, struct rtc_time *tm)
340c86edc0SAlessandro Zummo {
350c86edc0SAlessandro Zummo 	int err;
360c86edc0SAlessandro Zummo 
370c86edc0SAlessandro Zummo 	err = mutex_lock_interruptible(&rtc->ops_lock);
380c86edc0SAlessandro Zummo 	if (err)
39b68bb263SDavid Brownell 		return err;
400c86edc0SAlessandro Zummo 
416610e089SJohn Stultz 	err = __rtc_read_time(rtc, tm);
420c86edc0SAlessandro Zummo 	mutex_unlock(&rtc->ops_lock);
430c86edc0SAlessandro Zummo 	return err;
440c86edc0SAlessandro Zummo }
450c86edc0SAlessandro Zummo EXPORT_SYMBOL_GPL(rtc_read_time);
460c86edc0SAlessandro Zummo 
47ab6a2d70SDavid Brownell int rtc_set_time(struct rtc_device *rtc, struct rtc_time *tm)
480c86edc0SAlessandro Zummo {
490c86edc0SAlessandro Zummo 	int err;
500c86edc0SAlessandro Zummo 
510c86edc0SAlessandro Zummo 	err = rtc_valid_tm(tm);
520c86edc0SAlessandro Zummo 	if (err != 0)
530c86edc0SAlessandro Zummo 		return err;
540c86edc0SAlessandro Zummo 
550c86edc0SAlessandro Zummo 	err = mutex_lock_interruptible(&rtc->ops_lock);
560c86edc0SAlessandro Zummo 	if (err)
57b68bb263SDavid Brownell 		return err;
580c86edc0SAlessandro Zummo 
590c86edc0SAlessandro Zummo 	if (!rtc->ops)
600c86edc0SAlessandro Zummo 		err = -ENODEV;
61bbccf83fSAlessandro Zummo 	else if (rtc->ops->set_time)
62cd966209SDavid Brownell 		err = rtc->ops->set_time(rtc->dev.parent, tm);
63bbccf83fSAlessandro Zummo 	else if (rtc->ops->set_mmss) {
64bbccf83fSAlessandro Zummo 		unsigned long secs;
65bbccf83fSAlessandro Zummo 		err = rtc_tm_to_time(tm, &secs);
66bbccf83fSAlessandro Zummo 		if (err == 0)
67bbccf83fSAlessandro Zummo 			err = rtc->ops->set_mmss(rtc->dev.parent, secs);
68bbccf83fSAlessandro Zummo 	} else
69bbccf83fSAlessandro Zummo 		err = -EINVAL;
700c86edc0SAlessandro Zummo 
710c86edc0SAlessandro Zummo 	mutex_unlock(&rtc->ops_lock);
720c86edc0SAlessandro Zummo 	return err;
730c86edc0SAlessandro Zummo }
740c86edc0SAlessandro Zummo EXPORT_SYMBOL_GPL(rtc_set_time);
750c86edc0SAlessandro Zummo 
76ab6a2d70SDavid Brownell int rtc_set_mmss(struct rtc_device *rtc, unsigned long secs)
770c86edc0SAlessandro Zummo {
780c86edc0SAlessandro Zummo 	int err;
790c86edc0SAlessandro Zummo 
800c86edc0SAlessandro Zummo 	err = mutex_lock_interruptible(&rtc->ops_lock);
810c86edc0SAlessandro Zummo 	if (err)
82b68bb263SDavid Brownell 		return err;
830c86edc0SAlessandro Zummo 
840c86edc0SAlessandro Zummo 	if (!rtc->ops)
850c86edc0SAlessandro Zummo 		err = -ENODEV;
860c86edc0SAlessandro Zummo 	else if (rtc->ops->set_mmss)
87cd966209SDavid Brownell 		err = rtc->ops->set_mmss(rtc->dev.parent, secs);
880c86edc0SAlessandro Zummo 	else if (rtc->ops->read_time && rtc->ops->set_time) {
890c86edc0SAlessandro Zummo 		struct rtc_time new, old;
900c86edc0SAlessandro Zummo 
91cd966209SDavid Brownell 		err = rtc->ops->read_time(rtc->dev.parent, &old);
920c86edc0SAlessandro Zummo 		if (err == 0) {
930c86edc0SAlessandro Zummo 			rtc_time_to_tm(secs, &new);
940c86edc0SAlessandro Zummo 
950c86edc0SAlessandro Zummo 			/*
960c86edc0SAlessandro Zummo 			 * avoid writing when we're going to change the day of
970c86edc0SAlessandro Zummo 			 * the month. We will retry in the next minute. This
980c86edc0SAlessandro Zummo 			 * basically means that if the RTC must not drift
990c86edc0SAlessandro Zummo 			 * by more than 1 minute in 11 minutes.
1000c86edc0SAlessandro Zummo 			 */
1010c86edc0SAlessandro Zummo 			if (!((old.tm_hour == 23 && old.tm_min == 59) ||
1020c86edc0SAlessandro Zummo 				(new.tm_hour == 23 && new.tm_min == 59)))
103cd966209SDavid Brownell 				err = rtc->ops->set_time(rtc->dev.parent,
104ab6a2d70SDavid Brownell 						&new);
1050c86edc0SAlessandro Zummo 		}
1060c86edc0SAlessandro Zummo 	}
1070c86edc0SAlessandro Zummo 	else
1080c86edc0SAlessandro Zummo 		err = -EINVAL;
1090c86edc0SAlessandro Zummo 
1100c86edc0SAlessandro Zummo 	mutex_unlock(&rtc->ops_lock);
1110c86edc0SAlessandro Zummo 
1120c86edc0SAlessandro Zummo 	return err;
1130c86edc0SAlessandro Zummo }
1140c86edc0SAlessandro Zummo EXPORT_SYMBOL_GPL(rtc_set_mmss);
1150c86edc0SAlessandro Zummo 
1166610e089SJohn Stultz int rtc_read_alarm(struct rtc_device *rtc, struct rtc_wkalrm *alarm)
1170c86edc0SAlessandro Zummo {
1180c86edc0SAlessandro Zummo 	int err;
1190c86edc0SAlessandro Zummo 
1200c86edc0SAlessandro Zummo 	err = mutex_lock_interruptible(&rtc->ops_lock);
1210c86edc0SAlessandro Zummo 	if (err)
122b68bb263SDavid Brownell 		return err;
1236610e089SJohn Stultz 	alarm->enabled = rtc->aie_timer.enabled;
1246610e089SJohn Stultz 	if (alarm->enabled)
1256610e089SJohn Stultz 		alarm->time = rtc_ktime_to_tm(rtc->aie_timer.node.expires);
1260c86edc0SAlessandro Zummo 	mutex_unlock(&rtc->ops_lock);
1270e36a9a4SMark Lord 
1280e36a9a4SMark Lord 	return 0;
1290e36a9a4SMark Lord }
1300c86edc0SAlessandro Zummo EXPORT_SYMBOL_GPL(rtc_read_alarm);
1310c86edc0SAlessandro Zummo 
1326610e089SJohn Stultz int __rtc_set_alarm(struct rtc_device *rtc, struct rtc_wkalrm *alarm)
1336610e089SJohn Stultz {
1346610e089SJohn Stultz 	struct rtc_time tm;
1356610e089SJohn Stultz 	long now, scheduled;
1366610e089SJohn Stultz 	int err;
1376610e089SJohn Stultz 
1386610e089SJohn Stultz 	err = rtc_valid_tm(&alarm->time);
1396610e089SJohn Stultz 	if (err)
1406610e089SJohn Stultz 		return err;
1416610e089SJohn Stultz 	rtc_tm_to_time(&alarm->time, &scheduled);
1426610e089SJohn Stultz 
1436610e089SJohn Stultz 	/* Make sure we're not setting alarms in the past */
1446610e089SJohn Stultz 	err = __rtc_read_time(rtc, &tm);
1456610e089SJohn Stultz 	rtc_tm_to_time(&tm, &now);
1466610e089SJohn Stultz 	if (scheduled <= now)
1476610e089SJohn Stultz 		return -ETIME;
1486610e089SJohn Stultz 	/*
1496610e089SJohn Stultz 	 * XXX - We just checked to make sure the alarm time is not
1506610e089SJohn Stultz 	 * in the past, but there is still a race window where if
1516610e089SJohn Stultz 	 * the is alarm set for the next second and the second ticks
1526610e089SJohn Stultz 	 * over right here, before we set the alarm.
1536610e089SJohn Stultz 	 */
1546610e089SJohn Stultz 
1556610e089SJohn Stultz 	if (!rtc->ops)
1566610e089SJohn Stultz 		err = -ENODEV;
1576610e089SJohn Stultz 	else if (!rtc->ops->set_alarm)
1586610e089SJohn Stultz 		err = -EINVAL;
1596610e089SJohn Stultz 	else
1606610e089SJohn Stultz 		err = rtc->ops->set_alarm(rtc->dev.parent, alarm);
1616610e089SJohn Stultz 
1626610e089SJohn Stultz 	return err;
1636610e089SJohn Stultz }
1646610e089SJohn Stultz 
165ab6a2d70SDavid Brownell int rtc_set_alarm(struct rtc_device *rtc, struct rtc_wkalrm *alarm)
1660c86edc0SAlessandro Zummo {
1670c86edc0SAlessandro Zummo 	int err;
1680c86edc0SAlessandro Zummo 
169f8245c26SDavid Brownell 	err = rtc_valid_tm(&alarm->time);
170f8245c26SDavid Brownell 	if (err != 0)
171f8245c26SDavid Brownell 		return err;
172f8245c26SDavid Brownell 
1730c86edc0SAlessandro Zummo 	err = mutex_lock_interruptible(&rtc->ops_lock);
1740c86edc0SAlessandro Zummo 	if (err)
175b68bb263SDavid Brownell 		return err;
1766610e089SJohn Stultz 	if (rtc->aie_timer.enabled) {
177*96c8f06aSThomas Gleixner 		rtc_timer_remove(rtc, &rtc->aie_timer);
1786610e089SJohn Stultz 		rtc->aie_timer.enabled = 0;
1796610e089SJohn Stultz 	}
1806610e089SJohn Stultz 	rtc->aie_timer.node.expires = rtc_tm_to_ktime(alarm->time);
1816610e089SJohn Stultz 	rtc->aie_timer.period = ktime_set(0, 0);
1826610e089SJohn Stultz 	if (alarm->enabled) {
1836610e089SJohn Stultz 		rtc->aie_timer.enabled = 1;
184*96c8f06aSThomas Gleixner 		rtc_timer_enqueue(rtc, &rtc->aie_timer);
1856610e089SJohn Stultz 	}
1860c86edc0SAlessandro Zummo 	mutex_unlock(&rtc->ops_lock);
1876610e089SJohn Stultz 	return 0;
1880c86edc0SAlessandro Zummo }
1890c86edc0SAlessandro Zummo EXPORT_SYMBOL_GPL(rtc_set_alarm);
1900c86edc0SAlessandro Zummo 
191099e6576SAlessandro Zummo int rtc_alarm_irq_enable(struct rtc_device *rtc, unsigned int enabled)
192099e6576SAlessandro Zummo {
193099e6576SAlessandro Zummo 	int err = mutex_lock_interruptible(&rtc->ops_lock);
194099e6576SAlessandro Zummo 	if (err)
195099e6576SAlessandro Zummo 		return err;
196099e6576SAlessandro Zummo 
1976610e089SJohn Stultz 	if (rtc->aie_timer.enabled != enabled) {
1986610e089SJohn Stultz 		if (enabled) {
1996610e089SJohn Stultz 			rtc->aie_timer.enabled = 1;
200*96c8f06aSThomas Gleixner 			rtc_timer_enqueue(rtc, &rtc->aie_timer);
2016610e089SJohn Stultz 		} else {
202*96c8f06aSThomas Gleixner 			rtc_timer_remove(rtc, &rtc->aie_timer);
2036610e089SJohn Stultz 			rtc->aie_timer.enabled = 0;
2046610e089SJohn Stultz 		}
2056610e089SJohn Stultz 	}
2066610e089SJohn Stultz 
207099e6576SAlessandro Zummo 	if (!rtc->ops)
208099e6576SAlessandro Zummo 		err = -ENODEV;
209099e6576SAlessandro Zummo 	else if (!rtc->ops->alarm_irq_enable)
210099e6576SAlessandro Zummo 		err = -EINVAL;
211099e6576SAlessandro Zummo 	else
212099e6576SAlessandro Zummo 		err = rtc->ops->alarm_irq_enable(rtc->dev.parent, enabled);
213099e6576SAlessandro Zummo 
214099e6576SAlessandro Zummo 	mutex_unlock(&rtc->ops_lock);
215099e6576SAlessandro Zummo 	return err;
216099e6576SAlessandro Zummo }
217099e6576SAlessandro Zummo EXPORT_SYMBOL_GPL(rtc_alarm_irq_enable);
218099e6576SAlessandro Zummo 
219099e6576SAlessandro Zummo int rtc_update_irq_enable(struct rtc_device *rtc, unsigned int enabled)
220099e6576SAlessandro Zummo {
221099e6576SAlessandro Zummo 	int err = mutex_lock_interruptible(&rtc->ops_lock);
222099e6576SAlessandro Zummo 	if (err)
223099e6576SAlessandro Zummo 		return err;
224099e6576SAlessandro Zummo 
2256610e089SJohn Stultz 	/* make sure we're changing state */
2266610e089SJohn Stultz 	if (rtc->uie_rtctimer.enabled == enabled)
2276610e089SJohn Stultz 		goto out;
2286610e089SJohn Stultz 
2296610e089SJohn Stultz 	if (enabled) {
2306610e089SJohn Stultz 		struct rtc_time tm;
2316610e089SJohn Stultz 		ktime_t now, onesec;
2326610e089SJohn Stultz 
2336610e089SJohn Stultz 		__rtc_read_time(rtc, &tm);
2346610e089SJohn Stultz 		onesec = ktime_set(1, 0);
2356610e089SJohn Stultz 		now = rtc_tm_to_ktime(tm);
2366610e089SJohn Stultz 		rtc->uie_rtctimer.node.expires = ktime_add(now, onesec);
2376610e089SJohn Stultz 		rtc->uie_rtctimer.period = ktime_set(1, 0);
2386610e089SJohn Stultz 		rtc->uie_rtctimer.enabled = 1;
239*96c8f06aSThomas Gleixner 		rtc_timer_enqueue(rtc, &rtc->uie_rtctimer);
2406610e089SJohn Stultz 	} else {
241*96c8f06aSThomas Gleixner 		rtc_timer_remove(rtc, &rtc->uie_rtctimer);
2426610e089SJohn Stultz 		rtc->uie_rtctimer.enabled = 0;
243099e6576SAlessandro Zummo 	}
244099e6576SAlessandro Zummo 
2456610e089SJohn Stultz out:
246099e6576SAlessandro Zummo 	mutex_unlock(&rtc->ops_lock);
247099e6576SAlessandro Zummo 	return err;
2486610e089SJohn Stultz 
249099e6576SAlessandro Zummo }
250099e6576SAlessandro Zummo EXPORT_SYMBOL_GPL(rtc_update_irq_enable);
251099e6576SAlessandro Zummo 
2526610e089SJohn Stultz 
253d728b1e6SDavid Brownell /**
2546610e089SJohn Stultz  * rtc_handle_legacy_irq - AIE, UIE and PIE event hook
2556610e089SJohn Stultz  * @rtc: pointer to the rtc device
2566610e089SJohn Stultz  *
2576610e089SJohn Stultz  * This function is called when an AIE, UIE or PIE mode interrupt
2586610e089SJohn Stultz  * has occured (or been emulated).
2596610e089SJohn Stultz  *
2606610e089SJohn Stultz  * Triggers the registered irq_task function callback.
2616610e089SJohn Stultz  */
2626610e089SJohn Stultz static void rtc_handle_legacy_irq(struct rtc_device *rtc, int num, int mode)
2636610e089SJohn Stultz {
2646610e089SJohn Stultz 	unsigned long flags;
2656610e089SJohn Stultz 
2666610e089SJohn Stultz 	/* mark one irq of the appropriate mode */
2676610e089SJohn Stultz 	spin_lock_irqsave(&rtc->irq_lock, flags);
2686610e089SJohn Stultz 	rtc->irq_data = (rtc->irq_data + (num << 8)) | (RTC_IRQF|mode);
2696610e089SJohn Stultz 	spin_unlock_irqrestore(&rtc->irq_lock, flags);
2706610e089SJohn Stultz 
2716610e089SJohn Stultz 	/* call the task func */
2726610e089SJohn Stultz 	spin_lock_irqsave(&rtc->irq_task_lock, flags);
2736610e089SJohn Stultz 	if (rtc->irq_task)
2746610e089SJohn Stultz 		rtc->irq_task->func(rtc->irq_task->private_data);
2756610e089SJohn Stultz 	spin_unlock_irqrestore(&rtc->irq_task_lock, flags);
2766610e089SJohn Stultz 
2776610e089SJohn Stultz 	wake_up_interruptible(&rtc->irq_queue);
2786610e089SJohn Stultz 	kill_fasync(&rtc->async_queue, SIGIO, POLL_IN);
2796610e089SJohn Stultz }
2806610e089SJohn Stultz 
2816610e089SJohn Stultz 
2826610e089SJohn Stultz /**
2836610e089SJohn Stultz  * rtc_aie_update_irq - AIE mode rtctimer hook
2846610e089SJohn Stultz  * @private: pointer to the rtc_device
2856610e089SJohn Stultz  *
2866610e089SJohn Stultz  * This functions is called when the aie_timer expires.
2876610e089SJohn Stultz  */
2886610e089SJohn Stultz void rtc_aie_update_irq(void *private)
2896610e089SJohn Stultz {
2906610e089SJohn Stultz 	struct rtc_device *rtc = (struct rtc_device *)private;
2916610e089SJohn Stultz 	rtc_handle_legacy_irq(rtc, 1, RTC_AF);
2926610e089SJohn Stultz }
2936610e089SJohn Stultz 
2946610e089SJohn Stultz 
2956610e089SJohn Stultz /**
2966610e089SJohn Stultz  * rtc_uie_update_irq - UIE mode rtctimer hook
2976610e089SJohn Stultz  * @private: pointer to the rtc_device
2986610e089SJohn Stultz  *
2996610e089SJohn Stultz  * This functions is called when the uie_timer expires.
3006610e089SJohn Stultz  */
3016610e089SJohn Stultz void rtc_uie_update_irq(void *private)
3026610e089SJohn Stultz {
3036610e089SJohn Stultz 	struct rtc_device *rtc = (struct rtc_device *)private;
3046610e089SJohn Stultz 	rtc_handle_legacy_irq(rtc, 1,  RTC_UF);
3056610e089SJohn Stultz }
3066610e089SJohn Stultz 
3076610e089SJohn Stultz 
3086610e089SJohn Stultz /**
3096610e089SJohn Stultz  * rtc_pie_update_irq - PIE mode hrtimer hook
3106610e089SJohn Stultz  * @timer: pointer to the pie mode hrtimer
3116610e089SJohn Stultz  *
3126610e089SJohn Stultz  * This function is used to emulate PIE mode interrupts
3136610e089SJohn Stultz  * using an hrtimer. This function is called when the periodic
3146610e089SJohn Stultz  * hrtimer expires.
3156610e089SJohn Stultz  */
3166610e089SJohn Stultz enum hrtimer_restart rtc_pie_update_irq(struct hrtimer *timer)
3176610e089SJohn Stultz {
3186610e089SJohn Stultz 	struct rtc_device *rtc;
3196610e089SJohn Stultz 	ktime_t period;
3206610e089SJohn Stultz 	int count;
3216610e089SJohn Stultz 	rtc = container_of(timer, struct rtc_device, pie_timer);
3226610e089SJohn Stultz 
3236610e089SJohn Stultz 	period = ktime_set(0, NSEC_PER_SEC/rtc->irq_freq);
3246610e089SJohn Stultz 	count = hrtimer_forward_now(timer, period);
3256610e089SJohn Stultz 
3266610e089SJohn Stultz 	rtc_handle_legacy_irq(rtc, count, RTC_PF);
3276610e089SJohn Stultz 
3286610e089SJohn Stultz 	return HRTIMER_RESTART;
3296610e089SJohn Stultz }
3306610e089SJohn Stultz 
3316610e089SJohn Stultz /**
3326610e089SJohn Stultz  * rtc_update_irq - Triggered when a RTC interrupt occurs.
333ab6a2d70SDavid Brownell  * @rtc: the rtc device
334d728b1e6SDavid Brownell  * @num: how many irqs are being reported (usually one)
335d728b1e6SDavid Brownell  * @events: mask of RTC_IRQF with one or more of RTC_PF, RTC_AF, RTC_UF
336e6229becSAtsushi Nemoto  * Context: any
337d728b1e6SDavid Brownell  */
338ab6a2d70SDavid Brownell void rtc_update_irq(struct rtc_device *rtc,
3390c86edc0SAlessandro Zummo 		unsigned long num, unsigned long events)
3400c86edc0SAlessandro Zummo {
3416610e089SJohn Stultz 	schedule_work(&rtc->irqwork);
3420c86edc0SAlessandro Zummo }
3430c86edc0SAlessandro Zummo EXPORT_SYMBOL_GPL(rtc_update_irq);
3440c86edc0SAlessandro Zummo 
34571da8905SDave Young static int __rtc_match(struct device *dev, void *data)
34671da8905SDave Young {
34771da8905SDave Young 	char *name = (char *)data;
34871da8905SDave Young 
349d4afc76cSKay Sievers 	if (strcmp(dev_name(dev), name) == 0)
35071da8905SDave Young 		return 1;
35171da8905SDave Young 	return 0;
35271da8905SDave Young }
35371da8905SDave Young 
354ab6a2d70SDavid Brownell struct rtc_device *rtc_class_open(char *name)
3550c86edc0SAlessandro Zummo {
356cd966209SDavid Brownell 	struct device *dev;
357ab6a2d70SDavid Brownell 	struct rtc_device *rtc = NULL;
3580c86edc0SAlessandro Zummo 
359695794aeSGreg Kroah-Hartman 	dev = class_find_device(rtc_class, NULL, name, __rtc_match);
360cd966209SDavid Brownell 	if (dev)
361cd966209SDavid Brownell 		rtc = to_rtc_device(dev);
3620c86edc0SAlessandro Zummo 
363ab6a2d70SDavid Brownell 	if (rtc) {
364ab6a2d70SDavid Brownell 		if (!try_module_get(rtc->owner)) {
365cd966209SDavid Brownell 			put_device(dev);
366ab6a2d70SDavid Brownell 			rtc = NULL;
367ab6a2d70SDavid Brownell 		}
3680c86edc0SAlessandro Zummo 	}
3690c86edc0SAlessandro Zummo 
370ab6a2d70SDavid Brownell 	return rtc;
3710c86edc0SAlessandro Zummo }
3720c86edc0SAlessandro Zummo EXPORT_SYMBOL_GPL(rtc_class_open);
3730c86edc0SAlessandro Zummo 
374ab6a2d70SDavid Brownell void rtc_class_close(struct rtc_device *rtc)
3750c86edc0SAlessandro Zummo {
376ab6a2d70SDavid Brownell 	module_put(rtc->owner);
377cd966209SDavid Brownell 	put_device(&rtc->dev);
3780c86edc0SAlessandro Zummo }
3790c86edc0SAlessandro Zummo EXPORT_SYMBOL_GPL(rtc_class_close);
3800c86edc0SAlessandro Zummo 
381ab6a2d70SDavid Brownell int rtc_irq_register(struct rtc_device *rtc, struct rtc_task *task)
3820c86edc0SAlessandro Zummo {
3830c86edc0SAlessandro Zummo 	int retval = -EBUSY;
3840c86edc0SAlessandro Zummo 
3850c86edc0SAlessandro Zummo 	if (task == NULL || task->func == NULL)
3860c86edc0SAlessandro Zummo 		return -EINVAL;
3870c86edc0SAlessandro Zummo 
388d691eb90SAlessandro Zummo 	/* Cannot register while the char dev is in use */
389372a302eSJiri Kosina 	if (test_and_set_bit_lock(RTC_DEV_BUSY, &rtc->flags))
390d691eb90SAlessandro Zummo 		return -EBUSY;
391d691eb90SAlessandro Zummo 
392d728b1e6SDavid Brownell 	spin_lock_irq(&rtc->irq_task_lock);
3930c86edc0SAlessandro Zummo 	if (rtc->irq_task == NULL) {
3940c86edc0SAlessandro Zummo 		rtc->irq_task = task;
3950c86edc0SAlessandro Zummo 		retval = 0;
3960c86edc0SAlessandro Zummo 	}
397d728b1e6SDavid Brownell 	spin_unlock_irq(&rtc->irq_task_lock);
3980c86edc0SAlessandro Zummo 
399372a302eSJiri Kosina 	clear_bit_unlock(RTC_DEV_BUSY, &rtc->flags);
400d691eb90SAlessandro Zummo 
4010c86edc0SAlessandro Zummo 	return retval;
4020c86edc0SAlessandro Zummo }
4030c86edc0SAlessandro Zummo EXPORT_SYMBOL_GPL(rtc_irq_register);
4040c86edc0SAlessandro Zummo 
405ab6a2d70SDavid Brownell void rtc_irq_unregister(struct rtc_device *rtc, struct rtc_task *task)
4060c86edc0SAlessandro Zummo {
407d728b1e6SDavid Brownell 	spin_lock_irq(&rtc->irq_task_lock);
4080c86edc0SAlessandro Zummo 	if (rtc->irq_task == task)
4090c86edc0SAlessandro Zummo 		rtc->irq_task = NULL;
410d728b1e6SDavid Brownell 	spin_unlock_irq(&rtc->irq_task_lock);
4110c86edc0SAlessandro Zummo }
4120c86edc0SAlessandro Zummo EXPORT_SYMBOL_GPL(rtc_irq_unregister);
4130c86edc0SAlessandro Zummo 
41497144c67SDavid Brownell /**
41597144c67SDavid Brownell  * rtc_irq_set_state - enable/disable 2^N Hz periodic IRQs
41697144c67SDavid Brownell  * @rtc: the rtc device
41797144c67SDavid Brownell  * @task: currently registered with rtc_irq_register()
41897144c67SDavid Brownell  * @enabled: true to enable periodic IRQs
41997144c67SDavid Brownell  * Context: any
42097144c67SDavid Brownell  *
42197144c67SDavid Brownell  * Note that rtc_irq_set_freq() should previously have been used to
42297144c67SDavid Brownell  * specify the desired frequency of periodic IRQ task->func() callbacks.
42397144c67SDavid Brownell  */
424ab6a2d70SDavid Brownell int rtc_irq_set_state(struct rtc_device *rtc, struct rtc_task *task, int enabled)
4250c86edc0SAlessandro Zummo {
4260c86edc0SAlessandro Zummo 	int err = 0;
4270c86edc0SAlessandro Zummo 	unsigned long flags;
4280c86edc0SAlessandro Zummo 
4290c86edc0SAlessandro Zummo 	spin_lock_irqsave(&rtc->irq_task_lock, flags);
430d691eb90SAlessandro Zummo 	if (rtc->irq_task != NULL && task == NULL)
431d691eb90SAlessandro Zummo 		err = -EBUSY;
4320c86edc0SAlessandro Zummo 	if (rtc->irq_task != task)
433d691eb90SAlessandro Zummo 		err = -EACCES;
4340c86edc0SAlessandro Zummo 
4356610e089SJohn Stultz 	if (enabled) {
4366610e089SJohn Stultz 		ktime_t period = ktime_set(0, NSEC_PER_SEC/rtc->irq_freq);
4376610e089SJohn Stultz 		hrtimer_start(&rtc->pie_timer, period, HRTIMER_MODE_REL);
4386610e089SJohn Stultz 	} else {
4396610e089SJohn Stultz 		hrtimer_cancel(&rtc->pie_timer);
4406610e089SJohn Stultz 	}
4416610e089SJohn Stultz 	rtc->pie_enabled = enabled;
4426610e089SJohn Stultz 	spin_unlock_irqrestore(&rtc->irq_task_lock, flags);
4430c86edc0SAlessandro Zummo 
4440c86edc0SAlessandro Zummo 	return err;
4450c86edc0SAlessandro Zummo }
4460c86edc0SAlessandro Zummo EXPORT_SYMBOL_GPL(rtc_irq_set_state);
4470c86edc0SAlessandro Zummo 
44897144c67SDavid Brownell /**
44997144c67SDavid Brownell  * rtc_irq_set_freq - set 2^N Hz periodic IRQ frequency for IRQ
45097144c67SDavid Brownell  * @rtc: the rtc device
45197144c67SDavid Brownell  * @task: currently registered with rtc_irq_register()
45297144c67SDavid Brownell  * @freq: positive frequency with which task->func() will be called
45397144c67SDavid Brownell  * Context: any
45497144c67SDavid Brownell  *
45597144c67SDavid Brownell  * Note that rtc_irq_set_state() is used to enable or disable the
45697144c67SDavid Brownell  * periodic IRQs.
45797144c67SDavid Brownell  */
458ab6a2d70SDavid Brownell int rtc_irq_set_freq(struct rtc_device *rtc, struct rtc_task *task, int freq)
4590c86edc0SAlessandro Zummo {
46056f10c63SAlessandro Zummo 	int err = 0;
4610c86edc0SAlessandro Zummo 	unsigned long flags;
4620c86edc0SAlessandro Zummo 
4630c86edc0SAlessandro Zummo 	spin_lock_irqsave(&rtc->irq_task_lock, flags);
464d691eb90SAlessandro Zummo 	if (rtc->irq_task != NULL && task == NULL)
465d691eb90SAlessandro Zummo 		err = -EBUSY;
4660c86edc0SAlessandro Zummo 	if (rtc->irq_task != task)
467d691eb90SAlessandro Zummo 		err = -EACCES;
4680c86edc0SAlessandro Zummo 	if (err == 0) {
4690c86edc0SAlessandro Zummo 		rtc->irq_freq = freq;
4706610e089SJohn Stultz 		if (rtc->pie_enabled) {
4716610e089SJohn Stultz 			ktime_t period;
4726610e089SJohn Stultz 			hrtimer_cancel(&rtc->pie_timer);
4736610e089SJohn Stultz 			period = ktime_set(0, NSEC_PER_SEC/rtc->irq_freq);
4746610e089SJohn Stultz 			hrtimer_start(&rtc->pie_timer, period,
4756610e089SJohn Stultz 					HRTIMER_MODE_REL);
4760c86edc0SAlessandro Zummo 		}
4776610e089SJohn Stultz 	}
4786610e089SJohn Stultz 	spin_unlock_irqrestore(&rtc->irq_task_lock, flags);
4790c86edc0SAlessandro Zummo 	return err;
4800c86edc0SAlessandro Zummo }
4812601a464SDavid Brownell EXPORT_SYMBOL_GPL(rtc_irq_set_freq);
4826610e089SJohn Stultz 
4836610e089SJohn Stultz /**
484*96c8f06aSThomas Gleixner  * rtc_timer_enqueue - Adds a rtc_timer to the rtc_device timerqueue
4856610e089SJohn Stultz  * @rtc rtc device
4866610e089SJohn Stultz  * @timer timer being added.
4876610e089SJohn Stultz  *
4886610e089SJohn Stultz  * Enqueues a timer onto the rtc devices timerqueue and sets
4896610e089SJohn Stultz  * the next alarm event appropriately.
4906610e089SJohn Stultz  *
4916610e089SJohn Stultz  * Must hold ops_lock for proper serialization of timerqueue
4926610e089SJohn Stultz  */
493*96c8f06aSThomas Gleixner void rtc_timer_enqueue(struct rtc_device *rtc, struct rtc_timer *timer)
4946610e089SJohn Stultz {
4956610e089SJohn Stultz 	timerqueue_add(&rtc->timerqueue, &timer->node);
4966610e089SJohn Stultz 	if (&timer->node == timerqueue_getnext(&rtc->timerqueue)) {
4976610e089SJohn Stultz 		struct rtc_wkalrm alarm;
4986610e089SJohn Stultz 		int err;
4996610e089SJohn Stultz 		alarm.time = rtc_ktime_to_tm(timer->node.expires);
5006610e089SJohn Stultz 		alarm.enabled = 1;
5016610e089SJohn Stultz 		err = __rtc_set_alarm(rtc, &alarm);
5026610e089SJohn Stultz 		if (err == -ETIME)
5036610e089SJohn Stultz 			schedule_work(&rtc->irqwork);
5046610e089SJohn Stultz 	}
5056610e089SJohn Stultz }
5066610e089SJohn Stultz 
5076610e089SJohn Stultz /**
508*96c8f06aSThomas Gleixner  * rtc_timer_remove - Removes a rtc_timer from the rtc_device timerqueue
5096610e089SJohn Stultz  * @rtc rtc device
5106610e089SJohn Stultz  * @timer timer being removed.
5116610e089SJohn Stultz  *
5126610e089SJohn Stultz  * Removes a timer onto the rtc devices timerqueue and sets
5136610e089SJohn Stultz  * the next alarm event appropriately.
5146610e089SJohn Stultz  *
5156610e089SJohn Stultz  * Must hold ops_lock for proper serialization of timerqueue
5166610e089SJohn Stultz  */
517*96c8f06aSThomas Gleixner void rtc_timer_remove(struct rtc_device *rtc, struct rtc_timer *timer)
5186610e089SJohn Stultz {
5196610e089SJohn Stultz 	struct timerqueue_node *next = timerqueue_getnext(&rtc->timerqueue);
5206610e089SJohn Stultz 	timerqueue_del(&rtc->timerqueue, &timer->node);
5216610e089SJohn Stultz 
5226610e089SJohn Stultz 	if (next == &timer->node) {
5236610e089SJohn Stultz 		struct rtc_wkalrm alarm;
5246610e089SJohn Stultz 		int err;
5256610e089SJohn Stultz 		next = timerqueue_getnext(&rtc->timerqueue);
5266610e089SJohn Stultz 		if (!next)
5276610e089SJohn Stultz 			return;
5286610e089SJohn Stultz 		alarm.time = rtc_ktime_to_tm(next->expires);
5296610e089SJohn Stultz 		alarm.enabled = 1;
5306610e089SJohn Stultz 		err = __rtc_set_alarm(rtc, &alarm);
5316610e089SJohn Stultz 		if (err == -ETIME)
5326610e089SJohn Stultz 			schedule_work(&rtc->irqwork);
5336610e089SJohn Stultz 	}
5346610e089SJohn Stultz }
5356610e089SJohn Stultz 
5366610e089SJohn Stultz /**
537*96c8f06aSThomas Gleixner  * rtc_timer_do_work - Expires rtc timers
5386610e089SJohn Stultz  * @rtc rtc device
5396610e089SJohn Stultz  * @timer timer being removed.
5406610e089SJohn Stultz  *
5416610e089SJohn Stultz  * Expires rtc timers. Reprograms next alarm event if needed.
5426610e089SJohn Stultz  * Called via worktask.
5436610e089SJohn Stultz  *
5446610e089SJohn Stultz  * Serializes access to timerqueue via ops_lock mutex
5456610e089SJohn Stultz  */
546*96c8f06aSThomas Gleixner void rtc_timer_do_work(struct work_struct *work)
5476610e089SJohn Stultz {
5486610e089SJohn Stultz 	struct rtc_timer *timer;
5496610e089SJohn Stultz 	struct timerqueue_node *next;
5506610e089SJohn Stultz 	ktime_t now;
5516610e089SJohn Stultz 	struct rtc_time tm;
5526610e089SJohn Stultz 
5536610e089SJohn Stultz 	struct rtc_device *rtc =
5546610e089SJohn Stultz 		container_of(work, struct rtc_device, irqwork);
5556610e089SJohn Stultz 
5566610e089SJohn Stultz 	mutex_lock(&rtc->ops_lock);
5576610e089SJohn Stultz again:
5586610e089SJohn Stultz 	__rtc_read_time(rtc, &tm);
5596610e089SJohn Stultz 	now = rtc_tm_to_ktime(tm);
5606610e089SJohn Stultz 	while ((next = timerqueue_getnext(&rtc->timerqueue))) {
5616610e089SJohn Stultz 		if (next->expires.tv64 > now.tv64)
5626610e089SJohn Stultz 			break;
5636610e089SJohn Stultz 
5646610e089SJohn Stultz 		/* expire timer */
5656610e089SJohn Stultz 		timer = container_of(next, struct rtc_timer, node);
5666610e089SJohn Stultz 		timerqueue_del(&rtc->timerqueue, &timer->node);
5676610e089SJohn Stultz 		timer->enabled = 0;
5686610e089SJohn Stultz 		if (timer->task.func)
5696610e089SJohn Stultz 			timer->task.func(timer->task.private_data);
5706610e089SJohn Stultz 
5716610e089SJohn Stultz 		/* Re-add/fwd periodic timers */
5726610e089SJohn Stultz 		if (ktime_to_ns(timer->period)) {
5736610e089SJohn Stultz 			timer->node.expires = ktime_add(timer->node.expires,
5746610e089SJohn Stultz 							timer->period);
5756610e089SJohn Stultz 			timer->enabled = 1;
5766610e089SJohn Stultz 			timerqueue_add(&rtc->timerqueue, &timer->node);
5776610e089SJohn Stultz 		}
5786610e089SJohn Stultz 	}
5796610e089SJohn Stultz 
5806610e089SJohn Stultz 	/* Set next alarm */
5816610e089SJohn Stultz 	if (next) {
5826610e089SJohn Stultz 		struct rtc_wkalrm alarm;
5836610e089SJohn Stultz 		int err;
5846610e089SJohn Stultz 		alarm.time = rtc_ktime_to_tm(next->expires);
5856610e089SJohn Stultz 		alarm.enabled = 1;
5866610e089SJohn Stultz 		err = __rtc_set_alarm(rtc, &alarm);
5876610e089SJohn Stultz 		if (err == -ETIME)
5886610e089SJohn Stultz 			goto again;
5896610e089SJohn Stultz 	}
5906610e089SJohn Stultz 
5916610e089SJohn Stultz 	mutex_unlock(&rtc->ops_lock);
5926610e089SJohn Stultz }
5936610e089SJohn Stultz 
5946610e089SJohn Stultz 
595*96c8f06aSThomas Gleixner /* rtc_timer_init - Initializes an rtc_timer
5966610e089SJohn Stultz  * @timer: timer to be intiialized
5976610e089SJohn Stultz  * @f: function pointer to be called when timer fires
5986610e089SJohn Stultz  * @data: private data passed to function pointer
5996610e089SJohn Stultz  *
6006610e089SJohn Stultz  * Kernel interface to initializing an rtc_timer.
6016610e089SJohn Stultz  */
602*96c8f06aSThomas Gleixner void rtc_timer_init(struct rtc_timer *timer, void (*f)(void* p), void* data)
6036610e089SJohn Stultz {
6046610e089SJohn Stultz 	timerqueue_init(&timer->node);
6056610e089SJohn Stultz 	timer->enabled = 0;
6066610e089SJohn Stultz 	timer->task.func = f;
6076610e089SJohn Stultz 	timer->task.private_data = data;
6086610e089SJohn Stultz }
6096610e089SJohn Stultz 
610*96c8f06aSThomas Gleixner /* rtc_timer_start - Sets an rtc_timer to fire in the future
6116610e089SJohn Stultz  * @ rtc: rtc device to be used
6126610e089SJohn Stultz  * @ timer: timer being set
6136610e089SJohn Stultz  * @ expires: time at which to expire the timer
6146610e089SJohn Stultz  * @ period: period that the timer will recur
6156610e089SJohn Stultz  *
6166610e089SJohn Stultz  * Kernel interface to set an rtc_timer
6176610e089SJohn Stultz  */
618*96c8f06aSThomas Gleixner int rtc_timer_start(struct rtc_device *rtc, struct rtc_timer* timer,
6196610e089SJohn Stultz 			ktime_t expires, ktime_t period)
6206610e089SJohn Stultz {
6216610e089SJohn Stultz 	int ret = 0;
6226610e089SJohn Stultz 	mutex_lock(&rtc->ops_lock);
6236610e089SJohn Stultz 	if (timer->enabled)
624*96c8f06aSThomas Gleixner 		rtc_timer_remove(rtc, timer);
6256610e089SJohn Stultz 
6266610e089SJohn Stultz 	timer->node.expires = expires;
6276610e089SJohn Stultz 	timer->period = period;
6286610e089SJohn Stultz 
6296610e089SJohn Stultz 	timer->enabled = 1;
630*96c8f06aSThomas Gleixner 	rtc_timer_enqueue(rtc, timer);
6316610e089SJohn Stultz 
6326610e089SJohn Stultz 	mutex_unlock(&rtc->ops_lock);
6336610e089SJohn Stultz 	return ret;
6346610e089SJohn Stultz }
6356610e089SJohn Stultz 
636*96c8f06aSThomas Gleixner /* rtc_timer_cancel - Stops an rtc_timer
6376610e089SJohn Stultz  * @ rtc: rtc device to be used
6386610e089SJohn Stultz  * @ timer: timer being set
6396610e089SJohn Stultz  *
6406610e089SJohn Stultz  * Kernel interface to cancel an rtc_timer
6416610e089SJohn Stultz  */
642*96c8f06aSThomas Gleixner int rtc_timer_cancel(struct rtc_device *rtc, struct rtc_timer* timer)
6436610e089SJohn Stultz {
6446610e089SJohn Stultz 	int ret = 0;
6456610e089SJohn Stultz 	mutex_lock(&rtc->ops_lock);
6466610e089SJohn Stultz 	if (timer->enabled)
647*96c8f06aSThomas Gleixner 		rtc_timer_remove(rtc, timer);
6486610e089SJohn Stultz 	timer->enabled = 0;
6496610e089SJohn Stultz 	mutex_unlock(&rtc->ops_lock);
6506610e089SJohn Stultz 	return ret;
6516610e089SJohn Stultz }
6526610e089SJohn Stultz 
6536610e089SJohn Stultz 
654