[PATCH] Fix wrong irq enable via rtc_control()
rtc_control() may be called in the interrupt context in ALSA rtc-timer driver. The patch fixes the wrong irq enable in rtc.c, and also fixes the possible race of bit flags. Signed-off-by: Takashi Iwai <tiwai@suse.de>
This commit is contained in:
parent
1d4ae4a119
commit
c3348760aa
@ -149,8 +149,22 @@ static void get_rtc_alm_time (struct rtc_time *alm_tm);
|
|||||||
#ifdef RTC_IRQ
|
#ifdef RTC_IRQ
|
||||||
static void rtc_dropped_irq(unsigned long data);
|
static void rtc_dropped_irq(unsigned long data);
|
||||||
|
|
||||||
static void set_rtc_irq_bit(unsigned char bit);
|
static void set_rtc_irq_bit_locked(unsigned char bit);
|
||||||
static void mask_rtc_irq_bit(unsigned char bit);
|
static void mask_rtc_irq_bit_locked(unsigned char bit);
|
||||||
|
|
||||||
|
static inline void set_rtc_irq_bit(unsigned char bit)
|
||||||
|
{
|
||||||
|
spin_lock_irq(&rtc_lock);
|
||||||
|
set_rtc_irq_bit_locked(bit);
|
||||||
|
spin_unlock_irq(&rtc_lock);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void mask_rtc_irq_bit(unsigned char bit)
|
||||||
|
{
|
||||||
|
spin_lock_irq(&rtc_lock);
|
||||||
|
mask_rtc_irq_bit_locked(bit);
|
||||||
|
spin_unlock_irq(&rtc_lock);
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
static int rtc_proc_open(struct inode *inode, struct file *file);
|
static int rtc_proc_open(struct inode *inode, struct file *file);
|
||||||
@ -401,18 +415,19 @@ static int rtc_do_ioctl(unsigned int cmd, unsigned long arg, int kernel)
|
|||||||
}
|
}
|
||||||
case RTC_PIE_OFF: /* Mask periodic int. enab. bit */
|
case RTC_PIE_OFF: /* Mask periodic int. enab. bit */
|
||||||
{
|
{
|
||||||
mask_rtc_irq_bit(RTC_PIE);
|
unsigned long flags; /* can be called from isr via rtc_control() */
|
||||||
|
spin_lock_irqsave (&rtc_lock, flags);
|
||||||
|
mask_rtc_irq_bit_locked(RTC_PIE);
|
||||||
if (rtc_status & RTC_TIMER_ON) {
|
if (rtc_status & RTC_TIMER_ON) {
|
||||||
spin_lock_irq (&rtc_lock);
|
|
||||||
rtc_status &= ~RTC_TIMER_ON;
|
rtc_status &= ~RTC_TIMER_ON;
|
||||||
del_timer(&rtc_irq_timer);
|
del_timer(&rtc_irq_timer);
|
||||||
spin_unlock_irq (&rtc_lock);
|
|
||||||
}
|
}
|
||||||
|
spin_unlock_irqrestore (&rtc_lock, flags);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
case RTC_PIE_ON: /* Allow periodic ints */
|
case RTC_PIE_ON: /* Allow periodic ints */
|
||||||
{
|
{
|
||||||
|
unsigned long flags; /* can be called from isr via rtc_control() */
|
||||||
/*
|
/*
|
||||||
* We don't really want Joe User enabling more
|
* We don't really want Joe User enabling more
|
||||||
* than 64Hz of interrupts on a multi-user machine.
|
* than 64Hz of interrupts on a multi-user machine.
|
||||||
@ -421,14 +436,14 @@ static int rtc_do_ioctl(unsigned int cmd, unsigned long arg, int kernel)
|
|||||||
(!capable(CAP_SYS_RESOURCE)))
|
(!capable(CAP_SYS_RESOURCE)))
|
||||||
return -EACCES;
|
return -EACCES;
|
||||||
|
|
||||||
|
spin_lock_irqsave (&rtc_lock, flags);
|
||||||
if (!(rtc_status & RTC_TIMER_ON)) {
|
if (!(rtc_status & RTC_TIMER_ON)) {
|
||||||
spin_lock_irq (&rtc_lock);
|
|
||||||
rtc_irq_timer.expires = jiffies + HZ/rtc_freq + 2*HZ/100;
|
rtc_irq_timer.expires = jiffies + HZ/rtc_freq + 2*HZ/100;
|
||||||
add_timer(&rtc_irq_timer);
|
add_timer(&rtc_irq_timer);
|
||||||
rtc_status |= RTC_TIMER_ON;
|
rtc_status |= RTC_TIMER_ON;
|
||||||
spin_unlock_irq (&rtc_lock);
|
|
||||||
}
|
}
|
||||||
set_rtc_irq_bit(RTC_PIE);
|
set_rtc_irq_bit_locked(RTC_PIE);
|
||||||
|
spin_unlock_irqrestore (&rtc_lock, flags);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
case RTC_UIE_OFF: /* Mask ints from RTC updates. */
|
case RTC_UIE_OFF: /* Mask ints from RTC updates. */
|
||||||
@ -609,6 +624,7 @@ static int rtc_do_ioctl(unsigned int cmd, unsigned long arg, int kernel)
|
|||||||
{
|
{
|
||||||
int tmp = 0;
|
int tmp = 0;
|
||||||
unsigned char val;
|
unsigned char val;
|
||||||
|
unsigned long flags; /* can be called from isr via rtc_control() */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* The max we can do is 8192Hz.
|
* The max we can do is 8192Hz.
|
||||||
@ -631,9 +647,9 @@ static int rtc_do_ioctl(unsigned int cmd, unsigned long arg, int kernel)
|
|||||||
if (arg != (1<<tmp))
|
if (arg != (1<<tmp))
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
spin_lock_irq(&rtc_lock);
|
spin_lock_irqsave(&rtc_lock, flags);
|
||||||
if (hpet_set_periodic_freq(arg)) {
|
if (hpet_set_periodic_freq(arg)) {
|
||||||
spin_unlock_irq(&rtc_lock);
|
spin_unlock_irqrestore(&rtc_lock, flags);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
rtc_freq = arg;
|
rtc_freq = arg;
|
||||||
@ -641,7 +657,7 @@ static int rtc_do_ioctl(unsigned int cmd, unsigned long arg, int kernel)
|
|||||||
val = CMOS_READ(RTC_FREQ_SELECT) & 0xf0;
|
val = CMOS_READ(RTC_FREQ_SELECT) & 0xf0;
|
||||||
val |= (16 - tmp);
|
val |= (16 - tmp);
|
||||||
CMOS_WRITE(val, RTC_FREQ_SELECT);
|
CMOS_WRITE(val, RTC_FREQ_SELECT);
|
||||||
spin_unlock_irq(&rtc_lock);
|
spin_unlock_irqrestore(&rtc_lock, flags);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
@ -844,12 +860,15 @@ int rtc_control(rtc_task_t *task, unsigned int cmd, unsigned long arg)
|
|||||||
#ifndef RTC_IRQ
|
#ifndef RTC_IRQ
|
||||||
return -EIO;
|
return -EIO;
|
||||||
#else
|
#else
|
||||||
spin_lock_irq(&rtc_task_lock);
|
unsigned long flags;
|
||||||
|
if (cmd != RTC_PIE_ON && cmd != RTC_PIE_OFF && cmd != RTC_IRQP_SET)
|
||||||
|
return -EINVAL;
|
||||||
|
spin_lock_irqsave(&rtc_task_lock, flags);
|
||||||
if (rtc_callback != task) {
|
if (rtc_callback != task) {
|
||||||
spin_unlock_irq(&rtc_task_lock);
|
spin_unlock_irqrestore(&rtc_task_lock, flags);
|
||||||
return -ENXIO;
|
return -ENXIO;
|
||||||
}
|
}
|
||||||
spin_unlock_irq(&rtc_task_lock);
|
spin_unlock_irqrestore(&rtc_task_lock, flags);
|
||||||
return rtc_do_ioctl(cmd, arg, 1);
|
return rtc_do_ioctl(cmd, arg, 1);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
@ -1306,40 +1325,32 @@ static void get_rtc_alm_time(struct rtc_time *alm_tm)
|
|||||||
* meddles with the interrupt enable/disable bits.
|
* meddles with the interrupt enable/disable bits.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
static void mask_rtc_irq_bit(unsigned char bit)
|
static void mask_rtc_irq_bit_locked(unsigned char bit)
|
||||||
{
|
{
|
||||||
unsigned char val;
|
unsigned char val;
|
||||||
|
|
||||||
spin_lock_irq(&rtc_lock);
|
if (hpet_mask_rtc_irq_bit(bit))
|
||||||
if (hpet_mask_rtc_irq_bit(bit)) {
|
|
||||||
spin_unlock_irq(&rtc_lock);
|
|
||||||
return;
|
return;
|
||||||
}
|
|
||||||
val = CMOS_READ(RTC_CONTROL);
|
val = CMOS_READ(RTC_CONTROL);
|
||||||
val &= ~bit;
|
val &= ~bit;
|
||||||
CMOS_WRITE(val, RTC_CONTROL);
|
CMOS_WRITE(val, RTC_CONTROL);
|
||||||
CMOS_READ(RTC_INTR_FLAGS);
|
CMOS_READ(RTC_INTR_FLAGS);
|
||||||
|
|
||||||
rtc_irq_data = 0;
|
rtc_irq_data = 0;
|
||||||
spin_unlock_irq(&rtc_lock);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void set_rtc_irq_bit(unsigned char bit)
|
static void set_rtc_irq_bit_locked(unsigned char bit)
|
||||||
{
|
{
|
||||||
unsigned char val;
|
unsigned char val;
|
||||||
|
|
||||||
spin_lock_irq(&rtc_lock);
|
if (hpet_set_rtc_irq_bit(bit))
|
||||||
if (hpet_set_rtc_irq_bit(bit)) {
|
|
||||||
spin_unlock_irq(&rtc_lock);
|
|
||||||
return;
|
return;
|
||||||
}
|
|
||||||
val = CMOS_READ(RTC_CONTROL);
|
val = CMOS_READ(RTC_CONTROL);
|
||||||
val |= bit;
|
val |= bit;
|
||||||
CMOS_WRITE(val, RTC_CONTROL);
|
CMOS_WRITE(val, RTC_CONTROL);
|
||||||
CMOS_READ(RTC_INTR_FLAGS);
|
CMOS_READ(RTC_INTR_FLAGS);
|
||||||
|
|
||||||
rtc_irq_data = 0;
|
rtc_irq_data = 0;
|
||||||
spin_unlock_irq(&rtc_lock);
|
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user