rtc: add alarm/update irq interfaces

Add 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>
Cc: Atsushi Nemoto <anemo@mba.ocn.ne.jp>
Cc: Ralf Baechle <ralf@linux-mips.org>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
This commit is contained in:
Alessandro Zummo 2009-01-04 12:00:54 -08:00 committed by Linus Torvalds
parent 54566b2c15
commit 099e657625
4 changed files with 101 additions and 18 deletions

View File

@ -102,9 +102,13 @@ config RTC_INTF_DEV_UIE_EMUL
depends on RTC_INTF_DEV depends on RTC_INTF_DEV
help help
Provides an emulation for RTC_UIE if the underlying rtc chip 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. 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 config RTC_DRV_TEST
tristate "Test driver/device" tristate "Test driver/device"
help help

View File

@ -307,6 +307,60 @@ int rtc_set_alarm(struct rtc_device *rtc, struct rtc_wkalrm *alarm)
} }
EXPORT_SYMBOL_GPL(rtc_set_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) {
mutex_unlock(&rtc->ops_lock);
return rtc_dev_update_irq_enable_emul(rtc, enabled);
}
#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);
mutex_unlock(&rtc->ops_lock);
#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
return err;
}
EXPORT_SYMBOL_GPL(rtc_update_irq_enable);
/** /**
* rtc_update_irq - report RTC periodic, alarm, and/or update irqs * rtc_update_irq - report RTC periodic, alarm, and/or update irqs
* @rtc: the rtc device * @rtc: the rtc device

View File

@ -92,10 +92,10 @@ static void rtc_uie_timer(unsigned long data)
spin_unlock_irqrestore(&rtc->irq_lock, flags); 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); spin_lock_irq(&rtc->irq_lock);
if (rtc->irq_active) { if (rtc->uie_irq_active) {
rtc->stop_uie_polling = 1; rtc->stop_uie_polling = 1;
if (rtc->uie_timer_active) { if (rtc->uie_timer_active) {
spin_unlock_irq(&rtc->irq_lock); spin_unlock_irq(&rtc->irq_lock);
@ -108,9 +108,10 @@ static void clear_uie(struct rtc_device *rtc)
flush_scheduled_work(); flush_scheduled_work();
spin_lock_irq(&rtc->irq_lock); spin_lock_irq(&rtc->irq_lock);
} }
rtc->irq_active = 0; rtc->uie_irq_active = 0;
} }
spin_unlock_irq(&rtc->irq_lock); spin_unlock_irq(&rtc->irq_lock);
return 0;
} }
static int set_uie(struct rtc_device *rtc) static int set_uie(struct rtc_device *rtc)
@ -122,8 +123,8 @@ static int set_uie(struct rtc_device *rtc)
if (err) if (err)
return err; return err;
spin_lock_irq(&rtc->irq_lock); spin_lock_irq(&rtc->irq_lock);
if (!rtc->irq_active) { if (!rtc->uie_irq_active) {
rtc->irq_active = 1; rtc->uie_irq_active = 1;
rtc->stop_uie_polling = 0; rtc->stop_uie_polling = 0;
rtc->oldsecs = tm.tm_sec; rtc->oldsecs = tm.tm_sec;
rtc->uie_task_active = 1; rtc->uie_task_active = 1;
@ -134,6 +135,16 @@ static int set_uie(struct rtc_device *rtc)
spin_unlock_irq(&rtc->irq_lock); spin_unlock_irq(&rtc->irq_lock);
return 0; 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 */ #endif /* CONFIG_RTC_INTF_DEV_UIE_EMUL */
static ssize_t static ssize_t
@ -357,6 +368,22 @@ static long rtc_dev_ioctl(struct file *file,
err = rtc_irq_set_state(rtc, NULL, 0); err = rtc_irq_set_state(rtc, NULL, 0);
break; 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: case RTC_IRQP_SET:
err = rtc_irq_set_freq(rtc, NULL, arg); err = rtc_irq_set_freq(rtc, NULL, arg);
break; break;
@ -401,17 +428,6 @@ static long rtc_dev_ioctl(struct file *file,
err = -EFAULT; err = -EFAULT;
return err; 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: default:
err = -ENOTTY; err = -ENOTTY;
break; 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 * 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. * 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_dev_ioctl(file, RTC_UIE_OFF, 0);
rtc_update_irq_enable(rtc, 0);
rtc_irq_set_state(rtc, NULL, 0); rtc_irq_set_state(rtc, NULL, 0);
if (rtc->ops->release) if (rtc->ops->release)

View File

@ -145,6 +145,8 @@ struct rtc_class_ops {
int (*irq_set_state)(struct device *, int enabled); int (*irq_set_state)(struct device *, int enabled);
int (*irq_set_freq)(struct device *, int freq); int (*irq_set_freq)(struct device *, int freq);
int (*read_callback)(struct device *, int data); 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 #define RTC_DEVICE_NAME_SIZE 20
@ -181,7 +183,7 @@ struct rtc_device
struct timer_list uie_timer; struct timer_list uie_timer;
/* Those fields are protected by rtc->irq_lock */ /* Those fields are protected by rtc->irq_lock */
unsigned int oldsecs; unsigned int oldsecs;
unsigned int irq_active:1; unsigned int uie_irq_active:1;
unsigned int stop_uie_polling:1; unsigned int stop_uie_polling:1;
unsigned int uie_task_active:1; unsigned int uie_task_active:1;
unsigned int uie_timer_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); struct rtc_task *task, int enabled);
extern int rtc_irq_set_freq(struct rtc_device *rtc, extern int rtc_irq_set_freq(struct rtc_device *rtc,
struct rtc_task *task, int freq); 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 { typedef struct rtc_task {
void (*func)(void *private_data); void (*func)(void *private_data);