Skip to content

Commit

Permalink
[ARM] 3427/1: ARM: OMAP: 2/8 Update timers
Browse files Browse the repository at this point in the history
Patch from Tony Lindgren

Update OMAP timers from linux-omap tree. The highlights of the
patch are:

- Move timer32k code from mach-omap1 to plat-omap and make it
  work also on omap24xx by Tony Lindgren
- Add support for dmtimer idle check for PM by Tuukka Tikkanen

Signed-off-by: Tony Lindgren <tony@atomide.com>
Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
  • Loading branch information
Tony Lindgren authored and Russell King committed Apr 2, 2006
1 parent b824efa commit a569c6e
Show file tree
Hide file tree
Showing 6 changed files with 362 additions and 199 deletions.
197 changes: 0 additions & 197 deletions arch/arm/mach-omap1/time.c
Original file line number Diff line number Diff line change
Expand Up @@ -51,8 +51,6 @@

struct sys_timer omap_timer;

#ifdef CONFIG_OMAP_MPU_TIMER

/*
* ---------------------------------------------------------------------------
* MPU timer
Expand Down Expand Up @@ -222,195 +220,6 @@ unsigned long long sched_clock(void)

return cycles_2_ns(ticks64);
}
#endif /* CONFIG_OMAP_MPU_TIMER */

#ifdef CONFIG_OMAP_32K_TIMER

#ifdef CONFIG_ARCH_OMAP15XX
#error OMAP 32KHz timer does not currently work on 15XX!
#endif

/*
* ---------------------------------------------------------------------------
* 32KHz OS timer
*
* This currently works only on 16xx, as 1510 does not have the continuous
* 32KHz synchronous timer. The 32KHz synchronous timer is used to keep track
* of time in addition to the 32KHz OS timer. Using only the 32KHz OS timer
* on 1510 would be possible, but the timer would not be as accurate as
* with the 32KHz synchronized timer.
* ---------------------------------------------------------------------------
*/
#define OMAP_32K_TIMER_BASE 0xfffb9000
#define OMAP_32K_TIMER_CR 0x08
#define OMAP_32K_TIMER_TVR 0x00
#define OMAP_32K_TIMER_TCR 0x04

#define OMAP_32K_TICKS_PER_HZ (32768 / HZ)

/*
* TRM says 1 / HZ = ( TVR + 1) / 32768, so TRV = (32768 / HZ) - 1
* so with HZ = 100, TVR = 327.68.
*/
#define OMAP_32K_TIMER_TICK_PERIOD ((32768 / HZ) - 1)
#define TIMER_32K_SYNCHRONIZED 0xfffbc410

#define JIFFIES_TO_HW_TICKS(nr_jiffies, clock_rate) \
(((nr_jiffies) * (clock_rate)) / HZ)

static inline void omap_32k_timer_write(int val, int reg)
{
omap_writew(val, reg + OMAP_32K_TIMER_BASE);
}

static inline unsigned long omap_32k_timer_read(int reg)
{
return omap_readl(reg + OMAP_32K_TIMER_BASE) & 0xffffff;
}

/*
* The 32KHz synchronized timer is an additional timer on 16xx.
* It is always running.
*/
static inline unsigned long omap_32k_sync_timer_read(void)
{
return omap_readl(TIMER_32K_SYNCHRONIZED);
}

static inline void omap_32k_timer_start(unsigned long load_val)
{
omap_32k_timer_write(load_val, OMAP_32K_TIMER_TVR);
omap_32k_timer_write(0x0f, OMAP_32K_TIMER_CR);
}

static inline void omap_32k_timer_stop(void)
{
omap_32k_timer_write(0x0, OMAP_32K_TIMER_CR);
}

/*
* Rounds down to nearest usec. Note that this will overflow for larger values.
*/
static inline unsigned long omap_32k_ticks_to_usecs(unsigned long ticks_32k)
{
return (ticks_32k * 5*5*5*5*5*5) >> 9;
}

/*
* Rounds down to nearest nsec.
*/
static inline unsigned long long
omap_32k_ticks_to_nsecs(unsigned long ticks_32k)
{
return (unsigned long long) ticks_32k * 1000 * 5*5*5*5*5*5 >> 9;
}

static unsigned long omap_32k_last_tick = 0;

/*
* Returns elapsed usecs since last 32k timer interrupt
*/
static unsigned long omap_32k_timer_gettimeoffset(void)
{
unsigned long now = omap_32k_sync_timer_read();
return omap_32k_ticks_to_usecs(now - omap_32k_last_tick);
}

/*
* Returns current time from boot in nsecs. It's OK for this to wrap
* around for now, as it's just a relative time stamp.
*/
unsigned long long sched_clock(void)
{
return omap_32k_ticks_to_nsecs(omap_32k_sync_timer_read());
}

/*
* Timer interrupt for 32KHz timer. When dynamic tick is enabled, this
* function is also called from other interrupts to remove latency
* issues with dynamic tick. In the dynamic tick case, we need to lock
* with irqsave.
*/
static irqreturn_t omap_32k_timer_interrupt(int irq, void *dev_id,
struct pt_regs *regs)
{
unsigned long flags;
unsigned long now;

write_seqlock_irqsave(&xtime_lock, flags);
now = omap_32k_sync_timer_read();

while (now - omap_32k_last_tick >= OMAP_32K_TICKS_PER_HZ) {
omap_32k_last_tick += OMAP_32K_TICKS_PER_HZ;
timer_tick(regs);
}

/* Restart timer so we don't drift off due to modulo or dynamic tick.
* By default we program the next timer to be continuous to avoid
* latencies during high system load. During dynamic tick operation the
* continuous timer can be overridden from pm_idle to be longer.
*/
omap_32k_timer_start(omap_32k_last_tick + OMAP_32K_TICKS_PER_HZ - now);
write_sequnlock_irqrestore(&xtime_lock, flags);

return IRQ_HANDLED;
}

#ifdef CONFIG_NO_IDLE_HZ
/*
* Programs the next timer interrupt needed. Called when dynamic tick is
* enabled, and to reprogram the ticks to skip from pm_idle. Note that
* we can keep the timer continuous, and don't need to set it to run in
* one-shot mode. This is because the timer will get reprogrammed again
* after next interrupt.
*/
void omap_32k_timer_reprogram(unsigned long next_tick)
{
omap_32k_timer_start(JIFFIES_TO_HW_TICKS(next_tick, 32768) + 1);
}

static struct irqaction omap_32k_timer_irq;
extern struct timer_update_handler timer_update;

static int omap_32k_timer_enable_dyn_tick(void)
{
/* No need to reprogram timer, just use the next interrupt */
return 0;
}

static int omap_32k_timer_disable_dyn_tick(void)
{
omap_32k_timer_start(OMAP_32K_TIMER_TICK_PERIOD);
return 0;
}

static struct dyn_tick_timer omap_dyn_tick_timer = {
.enable = omap_32k_timer_enable_dyn_tick,
.disable = omap_32k_timer_disable_dyn_tick,
.reprogram = omap_32k_timer_reprogram,
.handler = omap_32k_timer_interrupt,
};
#endif /* CONFIG_NO_IDLE_HZ */

static struct irqaction omap_32k_timer_irq = {
.name = "32KHz timer",
.flags = SA_INTERRUPT | SA_TIMER,
.handler = omap_32k_timer_interrupt,
};

static __init void omap_init_32k_timer(void)
{

#ifdef CONFIG_NO_IDLE_HZ
omap_timer.dyn_tick = &omap_dyn_tick_timer;
#endif

setup_irq(INT_OS_TIMER, &omap_32k_timer_irq);
omap_timer.offset = omap_32k_timer_gettimeoffset;
omap_32k_last_tick = omap_32k_sync_timer_read();
omap_32k_timer_start(OMAP_32K_TIMER_TICK_PERIOD);
}
#endif /* CONFIG_OMAP_32K_TIMER */

/*
* ---------------------------------------------------------------------------
Expand All @@ -419,13 +228,7 @@ static __init void omap_init_32k_timer(void)
*/
static void __init omap_timer_init(void)
{
#if defined(CONFIG_OMAP_MPU_TIMER)
omap_init_mpu_timer();
#elif defined(CONFIG_OMAP_32K_TIMER)
omap_init_32k_timer();
#else
#error No system timer selected in Kconfig!
#endif
}

struct sys_timer omap_timer = {
Expand Down
4 changes: 2 additions & 2 deletions arch/arm/plat-omap/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -70,13 +70,13 @@ config OMAP_MPU_TIMER

config OMAP_32K_TIMER
bool "Use 32KHz timer"
depends on ARCH_OMAP16XX
depends on ARCH_OMAP16XX || ARCH_OMAP24XX
help
Select this option if you want to enable the OMAP 32KHz timer.
This timer saves power compared to the OMAP_MPU_TIMER, and has
support for no tick during idle. The 32KHz timer provides less
intra-tick resolution than OMAP_MPU_TIMER. The 32KHz timer is
currently only available for OMAP-16xx.
currently only available for OMAP16XX and 24XX.

endchoice

Expand Down
26 changes: 26 additions & 0 deletions arch/arm/plat-omap/dmtimer.c
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,32 @@ int omap_dm_timers_active(void)
}


/**
* omap_dm_timer_modify_idlect_mask - Check if any running timers use ARMXOR
* @inputmask: current value of idlect mask
*/
__u32 omap_dm_timer_modify_idlect_mask(__u32 inputmask)
{
int n;

/* If ARMXOR cannot be idled this function call is unnecessary */
if (!(inputmask & (1 << 1)))
return inputmask;

/* If any active timer is using ARMXOR return modified mask */
for (n = 0; dm_timers[n].base; ++n)
if (omap_dm_timer_read_reg(&dm_timers[n], OMAP_TIMER_CTRL_REG)&
OMAP_TIMER_CTRL_ST) {
if (((omap_readl(MOD_CONF_CTRL_1)>>(n*2)) & 0x03) == 0)
inputmask &= ~(1 << 1);
else
inputmask &= ~(1 << 2);
}

return inputmask;
}


void omap_dm_timer_set_source(struct omap_dm_timer *timer, int source)
{
int n = (timer - dm_timers) << 1;
Expand Down
Loading

0 comments on commit a569c6e

Please sign in to comment.