Skip to content

Commit

Permalink
ARM: 6455/2: Better use of the RTC framework for sa11xx.
Browse files Browse the repository at this point in the history
This patch uses the RTC framework to treat some common ioctl.

In particular, it fixes the behaviour of rtc_irq_set_freq(), which did
not work as expected because the timer was not beeing retriggered.

Signed-off-by: Marcelo Roberto Jimenez <mroberto@cpti.cetuc.puc-rio.br>
Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
  • Loading branch information
Marcelo Roberto Jimenez authored and Russell King committed Dec 21, 2010
1 parent 47bb3b3 commit d2ccb52
Showing 1 changed file with 74 additions and 24 deletions.
98 changes: 74 additions & 24 deletions drivers/rtc/rtc-sa1100.c
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@
#define RTC_DEF_DIVIDER (32768 - 1)
#define RTC_DEF_TRIM 0

static unsigned long rtc_freq = 1024;
static const unsigned long RTC_FREQ = 1024;
static unsigned long timer_freq;
static struct rtc_time rtc_alarm;
static DEFINE_SPINLOCK(sa1100_rtc_lock);
Expand Down Expand Up @@ -156,8 +156,58 @@ static irqreturn_t sa1100_rtc_interrupt(int irq, void *dev_id)
return IRQ_HANDLED;
}

static int sa1100_irq_set_freq(struct device *dev, int freq)
{
if (freq < 1 || freq > timer_freq) {
return -EINVAL;
} else {
struct rtc_device *rtc = (struct rtc_device *)dev;

rtc->irq_freq = freq;

return 0;
}
}

static int rtc_timer1_count;

static int sa1100_irq_set_state(struct device *dev, int enabled)
{
spin_lock_irq(&sa1100_rtc_lock);
if (enabled) {
struct rtc_device *rtc = (struct rtc_device *)dev;

OSMR1 = timer_freq / rtc->irq_freq + OSCR;
OIER |= OIER_E1;
rtc_timer1_count = 1;
} else {
OIER &= ~OIER_E1;
}
spin_unlock_irq(&sa1100_rtc_lock);

return 0;
}

static inline int sa1100_timer1_retrigger(struct rtc_device *rtc)
{
unsigned long diff;
unsigned long period = timer_freq / rtc->irq_freq;

spin_lock_irq(&sa1100_rtc_lock);

do {
OSMR1 += period;
diff = OSMR1 - OSCR;
/* If OSCR > OSMR1, diff is a very large number (unsigned
* math). This means we have a lost interrupt. */
} while (diff > period);
OIER |= OIER_E1;

spin_unlock_irq(&sa1100_rtc_lock);

return 0;
}

static irqreturn_t timer1_interrupt(int irq, void *dev_id)
{
struct platform_device *pdev = to_platform_device(dev_id);
Expand All @@ -175,16 +225,22 @@ static irqreturn_t timer1_interrupt(int irq, void *dev_id)
rtc_update_irq(rtc, rtc_timer1_count, RTC_PF | RTC_IRQF);

if (rtc_timer1_count == 1)
rtc_timer1_count = (rtc_freq * ((1 << 30) / (timer_freq >> 2)));
rtc_timer1_count =
(rtc->irq_freq * ((1 << 30) / (timer_freq >> 2)));

/* retrigger. */
sa1100_timer1_retrigger(rtc);

return IRQ_HANDLED;
}

static int sa1100_rtc_read_callback(struct device *dev, int data)
{
if (data & RTC_PF) {
struct rtc_device *rtc = (struct rtc_device *)dev;

/* interpolate missed periods and set match for the next */
unsigned long period = timer_freq / rtc_freq;
unsigned long period = timer_freq / rtc->irq_freq;
unsigned long oscr = OSCR;
unsigned long osmr1 = OSMR1;
unsigned long missed = (oscr - osmr1)/period;
Expand All @@ -207,6 +263,7 @@ static int sa1100_rtc_read_callback(struct device *dev, int data)
static int sa1100_rtc_open(struct device *dev)
{
int ret;
struct rtc_device *rtc = (struct rtc_device *)dev;

ret = request_irq(IRQ_RTC1Hz, sa1100_rtc_interrupt, IRQF_DISABLED,
"rtc 1Hz", dev);
Expand All @@ -226,6 +283,9 @@ static int sa1100_rtc_open(struct device *dev)
dev_err(dev, "IRQ %d already in use.\n", IRQ_OST1);
goto fail_pi;
}
rtc->max_user_freq = RTC_FREQ;
sa1100_irq_set_freq(dev, RTC_FREQ);

return 0;

fail_pi:
Expand Down Expand Up @@ -274,25 +334,6 @@ static int sa1100_rtc_ioctl(struct device *dev, unsigned int cmd,
RTSR |= RTSR_HZE;
spin_unlock_irq(&sa1100_rtc_lock);
return 0;
case RTC_PIE_OFF:
spin_lock_irq(&sa1100_rtc_lock);
OIER &= ~OIER_E1;
spin_unlock_irq(&sa1100_rtc_lock);
return 0;
case RTC_PIE_ON:
spin_lock_irq(&sa1100_rtc_lock);
OSMR1 = timer_freq / rtc_freq + OSCR;
OIER |= OIER_E1;
rtc_timer1_count = 1;
spin_unlock_irq(&sa1100_rtc_lock);
return 0;
case RTC_IRQP_READ:
return put_user(rtc_freq, (unsigned long *)arg);
case RTC_IRQP_SET:
if (arg < 1 || arg > timer_freq)
return -EINVAL;
rtc_freq = arg;
return 0;
}
return -ENOIOCTLCMD;
}
Expand Down Expand Up @@ -344,12 +385,14 @@ static int sa1100_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm)

static int sa1100_rtc_proc(struct device *dev, struct seq_file *seq)
{
struct rtc_device *rtc = (struct rtc_device *)dev;

seq_printf(seq, "trim/divider\t: 0x%08x\n", (u32) RTTR);
seq_printf(seq, "update_IRQ\t: %s\n",
(RTSR & RTSR_HZE) ? "yes" : "no");
seq_printf(seq, "periodic_IRQ\t: %s\n",
(OIER & OIER_E1) ? "yes" : "no");
seq_printf(seq, "periodic_freq\t: %ld\n", rtc_freq);
seq_printf(seq, "periodic_freq\t: %d\n", rtc->irq_freq);
seq_printf(seq, "RTSR\t\t: 0x%08x\n", (u32)RTSR);

return 0;
Expand All @@ -365,6 +408,8 @@ static const struct rtc_class_ops sa1100_rtc_ops = {
.read_alarm = sa1100_rtc_read_alarm,
.set_alarm = sa1100_rtc_set_alarm,
.proc = sa1100_rtc_proc,
.irq_set_freq = sa1100_irq_set_freq,
.irq_set_state = sa1100_irq_set_state,
};

static int sa1100_rtc_probe(struct platform_device *pdev)
Expand All @@ -391,13 +436,18 @@ static int sa1100_rtc_probe(struct platform_device *pdev)
device_init_wakeup(&pdev->dev, 1);

rtc = rtc_device_register(pdev->name, &pdev->dev, &sa1100_rtc_ops,
THIS_MODULE);
THIS_MODULE);

if (IS_ERR(rtc))
return PTR_ERR(rtc);

platform_set_drvdata(pdev, rtc);

/* Set the irq_freq */
/*TODO: Find out who is messing with this value after we initialize
* it here.*/
rtc->irq_freq = RTC_FREQ;

/* Fix for a nasty initialization problem the in SA11xx RTSR register.
* See also the comments in sa1100_rtc_interrupt().
*
Expand Down

0 comments on commit d2ccb52

Please sign in to comment.