@@ -102,9 +102,13 @@ config RTC_INTF_DEV_UIE_EMUL
depends on RTC_INTF_DEV
help
Provides an emulation for RTC_UIE if the underlying rtc chip
- driver does not expose RTC_UIE ioctls. Those requests generate
+ driver does not expose RTC_UIE ioctls. Those requests generate
once-per-second update interrupts, used for synchronization.
+ The emulation code will read the time from the hardware
+ clock several times per second, please enable this option
+ only if you know that you really need it.
+
config RTC_DRV_TEST
tristate "Test driver/device"
help
@@ -307,6 +307,61 @@ int rtc_set_alarm(struct rtc_device *rtc, struct rtc_wkalrm *alarm)
}
EXPORT_SYMBOL_GPL(rtc_set_alarm);
+int rtc_alarm_irq_enable(struct rtc_device *rtc, unsigned int enabled)
+{
+ int err = mutex_lock_interruptible(&rtc->ops_lock);
+ if (err)
+ return err;
+
+ if (!rtc->ops)
+ err = -ENODEV;
+ else if (!rtc->ops->alarm_irq_enable)
+ err = -EINVAL;
+ else
+ err = rtc->ops->alarm_irq_enable(rtc->dev.parent, enabled);
+
+ mutex_unlock(&rtc->ops_lock);
+ return err;
+}
+EXPORT_SYMBOL_GPL(rtc_alarm_irq_enable);
+
+int rtc_update_irq_enable(struct rtc_device *rtc, unsigned int enabled)
+{
+ int err = mutex_lock_interruptible(&rtc->ops_lock);
+ if (err)
+ return err;
+
+#ifdef CONFIG_RTC_INTF_DEV_UIE_EMUL
+ if (enabled == 0 && rtc->uie_irq_active) {
+ err = rtc_dev_update_irq_enable_emul(rtc, enabled);
+ mutex_unlock(&rtc->ops_lock);
+ return err;
+ }
+#endif
+
+ if (!rtc->ops)
+ err = -ENODEV;
+ else if (!rtc->ops->update_irq_enable)
+ err = -EINVAL;
+ else
+ err = rtc->ops->update_irq_enable(rtc->dev.parent, enabled);
+
+#ifdef CONFIG_RTC_INTF_DEV_UIE_EMUL
+ /*
+ * Enable emulation if the driver did not provide
+ * the update_irq_enable function pointer or if returned
+ * -EINVAL to signal that it has been configured without
+ * interrupts or that are not available at the moment.
+ */
+ if (err == -EINVAL)
+ err = rtc_dev_update_irq_enable_emul(rtc, enabled);
+#endif
+
+ mutex_unlock(&rtc->ops_lock);
+ return err;
+}
+EXPORT_SYMBOL_GPL(rtc_update_irq_enable);
+
/**
* rtc_update_irq - report RTC periodic, alarm, and/or update irqs
* @rtc: the rtc device
@@ -92,10 +92,10 @@ static void rtc_uie_timer(unsigned long data)
spin_unlock_irqrestore(&rtc->irq_lock, flags);
}
-static void clear_uie(struct rtc_device *rtc)
+static int clear_uie(struct rtc_device *rtc)
{
spin_lock_irq(&rtc->irq_lock);
- if (rtc->irq_active) {
+ if (rtc->uie_irq_active) {
rtc->stop_uie_polling = 1;
if (rtc->uie_timer_active) {
spin_unlock_irq(&rtc->irq_lock);
@@ -108,9 +108,10 @@ static void clear_uie(struct rtc_device *rtc)
flush_scheduled_work();
spin_lock_irq(&rtc->irq_lock);
}
- rtc->irq_active = 0;
+ rtc->uie_irq_active = 0;
}
spin_unlock_irq(&rtc->irq_lock);
+ return 0;
}
static int set_uie(struct rtc_device *rtc)
@@ -122,8 +123,8 @@ static int set_uie(struct rtc_device *rtc)
if (err)
return err;
spin_lock_irq(&rtc->irq_lock);
- if (!rtc->irq_active) {
- rtc->irq_active = 1;
+ if (!rtc->uie_irq_active) {
+ rtc->uie_irq_active = 1;
rtc->stop_uie_polling = 0;
rtc->oldsecs = tm.tm_sec;
rtc->uie_task_active = 1;
@@ -134,6 +135,16 @@ static int set_uie(struct rtc_device *rtc)
spin_unlock_irq(&rtc->irq_lock);
return 0;
}
+
+int rtc_dev_update_irq_enable_emul(struct rtc_device *rtc, unsigned int enabled)
+{
+ if (enabled)
+ return set_uie(rtc);
+ else
+ return clear_uie(rtc);
+}
+EXPORT_SYMBOL(rtc_dev_update_irq_enable_emul);
+
#endif /* CONFIG_RTC_INTF_DEV_UIE_EMUL */
static ssize_t
@@ -357,6 +368,22 @@ static long rtc_dev_ioctl(struct file *file,
err = rtc_irq_set_state(rtc, NULL, 0);
break;
+ case RTC_AIE_ON:
+ mutex_unlock(&rtc->ops_lock);
+ return rtc_alarm_irq_enable(rtc, 1);
+
+ case RTC_AIE_OFF:
+ mutex_unlock(&rtc->ops_lock);
+ return rtc_alarm_irq_enable(rtc, 0);
+
+ case RTC_UIE_ON:
+ mutex_unlock(&rtc->ops_lock);
+ return rtc_update_irq_enable(rtc, 1);
+
+ case RTC_UIE_OFF:
+ mutex_unlock(&rtc->ops_lock);
+ return rtc_update_irq_enable(rtc, 0);
+
case RTC_IRQP_SET:
err = rtc_irq_set_freq(rtc, NULL, arg);
break;
@@ -401,17 +428,6 @@ static long rtc_dev_ioctl(struct file *file,
err = -EFAULT;
return err;
-#ifdef CONFIG_RTC_INTF_DEV_UIE_EMUL
- case RTC_UIE_OFF:
- mutex_unlock(&rtc->ops_lock);
- clear_uie(rtc);
- return 0;
-
- case RTC_UIE_ON:
- mutex_unlock(&rtc->ops_lock);
- err = set_uie(rtc);
- return err;
-#endif
default:
err = -ENOTTY;
break;
@@ -440,7 +456,10 @@ static int rtc_dev_release(struct inode *inode, struct file *file)
* Leave the alarm alone; it may be set to trigger a system wakeup
* later, or be used by kernel code, and is a one-shot event anyway.
*/
+
+ /* Keep ioctl until all drivers are converted */
rtc_dev_ioctl(file, RTC_UIE_OFF, 0);
+ rtc_update_irq_enable(rtc, 0);
rtc_irq_set_state(rtc, NULL, 0);
if (rtc->ops->release)
@@ -145,6 +145,8 @@ struct rtc_class_ops {
int (*irq_set_state)(struct device *, int enabled);
int (*irq_set_freq)(struct device *, int freq);
int (*read_callback)(struct device *, int data);
+ int (*alarm_irq_enable)(struct device *, unsigned int enabled);
+ int (*update_irq_enable)(struct device *, unsigned int enabled);
};
#define RTC_DEVICE_NAME_SIZE 20
@@ -181,7 +183,7 @@ struct rtc_device
struct timer_list uie_timer;
/* Those fields are protected by rtc->irq_lock */
unsigned int oldsecs;
- unsigned int irq_active:1;
+ unsigned int uie_irq_active:1;
unsigned int stop_uie_polling:1;
unsigned int uie_task_active:1;
unsigned int uie_timer_active:1;
@@ -216,6 +218,10 @@ extern int rtc_irq_set_state(struct rtc_device *rtc,
struct rtc_task *task, int enabled);
extern int rtc_irq_set_freq(struct rtc_device *rtc,
struct rtc_task *task, int freq);
+extern int rtc_update_irq_enable(struct rtc_device *rtc, unsigned int enabled);
+extern int rtc_alarm_irq_enable(struct rtc_device *rtc, unsigned int enabled);
+extern int rtc_dev_update_irq_enable_emul(struct rtc_device *rtc,
+ unsigned int enabled);
typedef struct rtc_task {
void (*func)(void *private_data);
This patch adds standard interfaces for alarm/update irqs enabling. Drivers are no more required to implement equivalent ioctl code as rtc-dev will provide it. UIE emulation should now be handled correctly and will work even for those RTC drivers who cannot be configured to do both UIE and AIE. Signed-off-by: Alessandro Zummo <a.zummo@towertech.it> Cc: David Brownell <david-b@pacbell.net> --- drivers/rtc/Kconfig | 6 ++++- drivers/rtc/interface.c | 55 +++++++++++++++++++++++++++++++++++++++++++++++ drivers/rtc/rtc-dev.c | 51 ++++++++++++++++++++++++++++++-------------- include/linux/rtc.h | 8 ++++++- 4 files changed, 102 insertions(+), 18 deletions(-) --~--~---------~--~----~------------~-------~--~----~ You received this message because you are subscribed to "rtc-linux". Membership options at http://groups.google.com/group/rtc-linux . Please read http://groups.google.com/group/rtc-linux/web/checklist before submitting a driver. -~----------~----~----~----~------~----~------~--~---