Skip to content

Commit

Permalink
---
Browse files Browse the repository at this point in the history
yaml
---
r: 83538
b: refs/heads/master
c: 9d8af78
h: refs/heads/master
v: v3
  • Loading branch information
Bernhard Walle authored and Linus Torvalds committed Feb 6, 2008
1 parent ecaef3a commit ecb4cb7
Show file tree
Hide file tree
Showing 3 changed files with 71 additions and 15 deletions.
2 changes: 1 addition & 1 deletion [refs]
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
---
refs/heads/master: 095b9d546f8fdac335989bd3d60405ff3af40ee9
refs/heads/master: 9d8af78b07976d4d84e0df491abd4e9db848d0ad
2 changes: 1 addition & 1 deletion trunk/arch/x86/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -415,7 +415,7 @@ config HPET_TIMER

config HPET_EMULATE_RTC
def_bool y
depends on HPET_TIMER && (RTC=y || RTC=m)
depends on HPET_TIMER && (RTC=y || RTC=m || RTC_DRV_CMOS=m || RTC_DRV_CMOS=y)

# Mark as embedded because too many people got it wrong.
# The code disables itself when not needed.
Expand Down
82 changes: 69 additions & 13 deletions trunk/drivers/rtc/rtc-cmos.c
Original file line number Diff line number Diff line change
Expand Up @@ -36,9 +36,24 @@
#include <linux/platform_device.h>
#include <linux/mod_devicetable.h>

#ifdef CONFIG_HPET_EMULATE_RTC
#include <asm/hpet.h>
#endif

/* this is for "generic access to PC-style RTC" using CMOS_READ/CMOS_WRITE */
#include <asm-generic/rtc.h>

#ifndef CONFIG_HPET_EMULATE_RTC
#define is_hpet_enabled() 0
#define hpet_set_alarm_time(hrs, min, sec) do { } while (0)
#define hpet_set_periodic_freq(arg) 0
#define hpet_mask_rtc_irq_bit(arg) do { } while (0)
#define hpet_set_rtc_irq_bit(arg) do { } while (0)
#define hpet_rtc_timer_init() do { } while (0)
#define hpet_register_irq_handler(h) 0
#define hpet_unregister_irq_handler(h) do { } while (0)
extern irqreturn_t hpet_rtc_interrupt(int irq, void *dev_id);
#endif

struct cmos_rtc {
struct rtc_device *rtc;
Expand Down Expand Up @@ -199,6 +214,7 @@ static int cmos_set_alarm(struct device *dev, struct rtc_wkalrm *t)
sec = t->time.tm_sec;
sec = (sec < 60) ? BIN2BCD(sec) : 0xff;

hpet_set_alarm_time(t->time.tm_hour, t->time.tm_min, t->time.tm_sec);
spin_lock_irq(&rtc_lock);

/* next rtc irq must not be from previous alarm setting */
Expand Down Expand Up @@ -252,7 +268,8 @@ static int cmos_irq_set_freq(struct device *dev, int freq)
f = 16 - f;

spin_lock_irqsave(&rtc_lock, flags);
CMOS_WRITE(RTC_REF_CLCK_32KHZ | f, RTC_FREQ_SELECT);
if (!hpet_set_periodic_freq(freq))
CMOS_WRITE(RTC_REF_CLCK_32KHZ | f, RTC_FREQ_SELECT);
spin_unlock_irqrestore(&rtc_lock, flags);

return 0;
Expand Down Expand Up @@ -314,28 +331,37 @@ cmos_rtc_ioctl(struct device *dev, unsigned int cmd, unsigned long arg)
switch (cmd) {
case RTC_AIE_OFF: /* alarm off */
rtc_control &= ~RTC_AIE;
hpet_mask_rtc_irq_bit(RTC_AIE);
break;
case RTC_AIE_ON: /* alarm on */
rtc_control |= RTC_AIE;
hpet_set_rtc_irq_bit(RTC_AIE);
break;
case RTC_UIE_OFF: /* update off */
rtc_control &= ~RTC_UIE;
hpet_mask_rtc_irq_bit(RTC_UIE);
break;
case RTC_UIE_ON: /* update on */
rtc_control |= RTC_UIE;
hpet_set_rtc_irq_bit(RTC_UIE);
break;
case RTC_PIE_OFF: /* periodic off */
rtc_control &= ~RTC_PIE;
hpet_mask_rtc_irq_bit(RTC_PIE);
break;
case RTC_PIE_ON: /* periodic on */
rtc_control |= RTC_PIE;
hpet_set_rtc_irq_bit(RTC_PIE);
break;
}
CMOS_WRITE(rtc_control, RTC_CONTROL);
if (!is_hpet_enabled())
CMOS_WRITE(rtc_control, RTC_CONTROL);

rtc_intr = CMOS_READ(RTC_INTR_FLAGS);
rtc_intr &= (rtc_control & RTC_IRQMASK) | RTC_IRQF;
if (is_intr(rtc_intr))
rtc_update_irq(cmos->rtc, 1, rtc_intr);

spin_unlock_irqrestore(&rtc_lock, flags);
return 0;
}
Expand Down Expand Up @@ -475,15 +501,25 @@ static irqreturn_t cmos_interrupt(int irq, void *p)
u8 rtc_control;

spin_lock(&rtc_lock);
irqstat = CMOS_READ(RTC_INTR_FLAGS);
rtc_control = CMOS_READ(RTC_CONTROL);
irqstat &= (rtc_control & RTC_IRQMASK) | RTC_IRQF;
/*
* In this case it is HPET RTC interrupt handler
* calling us, with the interrupt information
* passed as arg1, instead of irq.
*/
if (is_hpet_enabled())
irqstat = (unsigned long)irq & 0xF0;
else {
irqstat = CMOS_READ(RTC_INTR_FLAGS);
rtc_control = CMOS_READ(RTC_CONTROL);
irqstat &= (rtc_control & RTC_IRQMASK) | RTC_IRQF;
}

/* All Linux RTC alarms should be treated as if they were oneshot.
* Similar code may be needed in system wakeup paths, in case the
* alarm woke the system.
*/
if (irqstat & RTC_AIE) {
rtc_control = CMOS_READ(RTC_CONTROL);
rtc_control &= ~RTC_AIE;
CMOS_WRITE(rtc_control, RTC_CONTROL);
CMOS_READ(RTC_INTR_FLAGS);
Expand Down Expand Up @@ -591,8 +627,9 @@ cmos_do_probe(struct device *dev, struct resource *ports, int rtc_irq)
* doesn't use 32KHz here ... for portability we might need to
* do something about other clock frequencies.
*/
CMOS_WRITE(RTC_REF_CLCK_32KHZ | 0x06, RTC_FREQ_SELECT);
cmos_rtc.rtc->irq_freq = 1024;
if (!hpet_set_periodic_freq(cmos_rtc.rtc->irq_freq))
CMOS_WRITE(RTC_REF_CLCK_32KHZ | 0x06, RTC_FREQ_SELECT);

/* disable irqs.
*
Expand All @@ -615,14 +652,31 @@ cmos_do_probe(struct device *dev, struct resource *ports, int rtc_irq)
goto cleanup1;
}

if (is_valid_irq(rtc_irq))
retval = request_irq(rtc_irq, cmos_interrupt, IRQF_DISABLED,
cmos_rtc.rtc->dev.bus_id,
if (is_valid_irq(rtc_irq)) {
irq_handler_t rtc_cmos_int_handler;

if (is_hpet_enabled()) {
int err;

rtc_cmos_int_handler = hpet_rtc_interrupt;
err = hpet_register_irq_handler(cmos_interrupt);
if (err != 0) {
printk(KERN_WARNING "hpet_register_irq_handler "
" failed in rtc_init().");
goto cleanup1;
}
} else
rtc_cmos_int_handler = cmos_interrupt;

retval = request_irq(rtc_irq, rtc_cmos_int_handler,
IRQF_DISABLED, cmos_rtc.rtc->dev.bus_id,
cmos_rtc.rtc);
if (retval < 0) {
dev_dbg(dev, "IRQ %d is already in use\n", rtc_irq);
goto cleanup1;
if (retval < 0) {
dev_dbg(dev, "IRQ %d is already in use\n", rtc_irq);
goto cleanup1;
}
}
hpet_rtc_timer_init();

/* export at least the first block of NVRAM */
nvram.size = address_space - NVRAM_OFFSET;
Expand Down Expand Up @@ -677,8 +731,10 @@ static void __exit cmos_do_remove(struct device *dev)

sysfs_remove_bin_file(&dev->kobj, &nvram);

if (is_valid_irq(cmos->irq))
if (is_valid_irq(cmos->irq)) {
free_irq(cmos->irq, cmos->rtc);
hpet_unregister_irq_handler(cmos_interrupt);
}

rtc_device_unregister(cmos->rtc);
cmos->rtc = NULL;
Expand Down

0 comments on commit ecb4cb7

Please sign in to comment.