RTC: Revert UIE emulation removal

Uwe pointed out that my alarm based UIE emulation is not sufficient
to replace the older timer/polling based UIE emulation on devices
where there is no alarm irq. This causes rtc devices without alarms
to return -EINVAL to UIE ioctls. The fix is to re-instate the old
timer/polling method for devices without alarm irqs.

This patch reverts the following commits:
042620a018 - Remove UIE emulation
1daeddd596 - Cleanup removed UIE emulation declaration
b5cc8ca1c9 - Remove Kconfig symbol for UIE emulation

The emulation mode will still need to be wired-in with a following
patch before it will work.

CC: Uwe Kleine-König <u.kleine-koenig@pengutronix.de>
CC: Thomas Gleixner <tglx@linutronix.de>
Reported-by: Uwe Kleine-König <u.kleine-koenig@pengutronix.de>
Signed-off-by: John Stultz <john.stultz@linaro.org>
This commit is contained in:
John Stultz 2011-02-11 17:45:40 -08:00
parent 516373b8b6
commit 6e57b1d6a8
3 changed files with 130 additions and 0 deletions

View file

@ -97,6 +97,18 @@ config RTC_INTF_DEV
If unsure, say Y.
config RTC_INTF_DEV_UIE_EMUL
bool "RTC UIE emulation on dev interface"
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
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

View file

@ -46,6 +46,105 @@ static int rtc_dev_open(struct inode *inode, struct file *file)
return err;
}
#ifdef CONFIG_RTC_INTF_DEV_UIE_EMUL
/*
* Routine to poll RTC seconds field for change as often as possible,
* after first RTC_UIE use timer to reduce polling
*/
static void rtc_uie_task(struct work_struct *work)
{
struct rtc_device *rtc =
container_of(work, struct rtc_device, uie_task);
struct rtc_time tm;
int num = 0;
int err;
err = rtc_read_time(rtc, &tm);
spin_lock_irq(&rtc->irq_lock);
if (rtc->stop_uie_polling || err) {
rtc->uie_task_active = 0;
} else if (rtc->oldsecs != tm.tm_sec) {
num = (tm.tm_sec + 60 - rtc->oldsecs) % 60;
rtc->oldsecs = tm.tm_sec;
rtc->uie_timer.expires = jiffies + HZ - (HZ/10);
rtc->uie_timer_active = 1;
rtc->uie_task_active = 0;
add_timer(&rtc->uie_timer);
} else if (schedule_work(&rtc->uie_task) == 0) {
rtc->uie_task_active = 0;
}
spin_unlock_irq(&rtc->irq_lock);
if (num)
rtc_update_irq(rtc, num, RTC_UF | RTC_IRQF);
}
static void rtc_uie_timer(unsigned long data)
{
struct rtc_device *rtc = (struct rtc_device *)data;
unsigned long flags;
spin_lock_irqsave(&rtc->irq_lock, flags);
rtc->uie_timer_active = 0;
rtc->uie_task_active = 1;
if ((schedule_work(&rtc->uie_task) == 0))
rtc->uie_task_active = 0;
spin_unlock_irqrestore(&rtc->irq_lock, flags);
}
static int clear_uie(struct rtc_device *rtc)
{
spin_lock_irq(&rtc->irq_lock);
if (rtc->uie_irq_active) {
rtc->stop_uie_polling = 1;
if (rtc->uie_timer_active) {
spin_unlock_irq(&rtc->irq_lock);
del_timer_sync(&rtc->uie_timer);
spin_lock_irq(&rtc->irq_lock);
rtc->uie_timer_active = 0;
}
if (rtc->uie_task_active) {
spin_unlock_irq(&rtc->irq_lock);
flush_scheduled_work();
spin_lock_irq(&rtc->irq_lock);
}
rtc->uie_irq_active = 0;
}
spin_unlock_irq(&rtc->irq_lock);
return 0;
}
static int set_uie(struct rtc_device *rtc)
{
struct rtc_time tm;
int err;
err = rtc_read_time(rtc, &tm);
if (err)
return err;
spin_lock_irq(&rtc->irq_lock);
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;
if (schedule_work(&rtc->uie_task) == 0)
rtc->uie_task_active = 0;
}
rtc->irq_data = 0;
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
rtc_dev_read(struct file *file, char __user *buf, size_t count, loff_t *ppos)
@ -387,6 +486,11 @@ void rtc_dev_prepare(struct rtc_device *rtc)
rtc->dev.devt = MKDEV(MAJOR(rtc_devt), rtc->id);
#ifdef CONFIG_RTC_INTF_DEV_UIE_EMUL
INIT_WORK(&rtc->uie_task, rtc_uie_task);
setup_timer(&rtc->uie_timer, rtc_uie_timer, (unsigned long)rtc);
#endif
cdev_init(&rtc->char_dev, &rtc_dev_fops);
rtc->char_dev.owner = rtc->owner;
}

View file

@ -203,6 +203,18 @@ struct rtc_device
struct hrtimer pie_timer; /* sub second exp, so needs hrtimer */
int pie_enabled;
struct work_struct irqwork;
#ifdef CONFIG_RTC_INTF_DEV_UIE_EMUL
struct work_struct uie_task;
struct timer_list uie_timer;
/* Those fields are protected by rtc->irq_lock */
unsigned int oldsecs;
unsigned int uie_irq_active:1;
unsigned int stop_uie_polling:1;
unsigned int uie_task_active:1;
unsigned int uie_timer_active:1;
#endif
};
#define to_rtc_device(d) container_of(d, struct rtc_device, dev)
@ -235,6 +247,8 @@ 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);
void rtc_aie_update_irq(void *private);
void rtc_uie_update_irq(void *private);