Skip to content

Commit

Permalink
---
Browse files Browse the repository at this point in the history
yaml
---
r: 53998
b: refs/heads/master
c: 075192a
h: refs/heads/master
v: v3
  • Loading branch information
Kevin Hilman authored and Russell King committed Apr 21, 2007
1 parent a84daf3 commit 08b855b
Show file tree
Hide file tree
Showing 6 changed files with 225 additions and 174 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: 89df127246f23add865f4a8f719c990e41151843
refs/heads/master: 075192ae807579448afcc0833bd349ccce057825
1 change: 1 addition & 0 deletions trunk/arch/arm/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -370,6 +370,7 @@ config ARCH_LH7A40X
config ARCH_OMAP
bool "TI OMAP"
select GENERIC_GPIO
select GENERIC_TIME
help
Support for TI's OMAP platform (OMAP1 and OMAP2).

Expand Down
206 changes: 133 additions & 73 deletions trunk/arch/arm/mach-omap1/time.c
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,10 @@
#include <linux/interrupt.h>
#include <linux/sched.h>
#include <linux/spinlock.h>
#include <linux/clk.h>
#include <linux/err.h>
#include <linux/clocksource.h>
#include <linux/clockchips.h>

#include <asm/system.h>
#include <asm/hardware.h>
Expand All @@ -48,13 +52,7 @@
#include <asm/mach/irq.h>
#include <asm/mach/time.h>

struct sys_timer omap_timer;

/*
* ---------------------------------------------------------------------------
* MPU timer
* ---------------------------------------------------------------------------
*/
#define OMAP_MPU_TIMER_BASE OMAP_MPU_TIMER1_BASE
#define OMAP_MPU_TIMER_OFFSET 0x100

Expand Down Expand Up @@ -88,21 +86,6 @@ static inline unsigned long long cycles_2_ns(unsigned long long cyc)
return (cyc * cyc2ns_scale) >> CYC2NS_SCALE_FACTOR;
}

/*
* MPU_TICKS_PER_SEC must be an even number, otherwise machinecycles_to_usecs
* will break. On P2, the timer count rate is 6.5 MHz after programming PTV
* with 0. This divides the 13MHz input by 2, and is undocumented.
*/
#if defined(CONFIG_MACH_OMAP_PERSEUS2) || defined(CONFIG_MACH_OMAP_FSAMPLE)
/* REVISIT: This ifdef construct should be replaced by a query to clock
* framework to see if timer base frequency is 12.0, 13.0 or 19.2 MHz.
*/
#define MPU_TICKS_PER_SEC (13000000 / 2)
#else
#define MPU_TICKS_PER_SEC (12000000 / 2)
#endif

#define MPU_TIMER_TICK_PERIOD ((MPU_TICKS_PER_SEC / HZ) - 1)

typedef struct {
u32 cntl; /* CNTL_TIMER, R/W */
Expand All @@ -120,98 +103,164 @@ static inline unsigned long omap_mpu_timer_read(int nr)
return timer->read_tim;
}

static inline void omap_mpu_timer_start(int nr, unsigned long load_val)
static inline void omap_mpu_set_autoreset(int nr)
{
volatile omap_mpu_timer_regs_t* timer = omap_mpu_timer_base(nr);

timer->cntl = MPU_TIMER_CLOCK_ENABLE;
udelay(1);
timer->load_tim = load_val;
udelay(1);
timer->cntl = (MPU_TIMER_CLOCK_ENABLE | MPU_TIMER_AR | MPU_TIMER_ST);
timer->cntl = timer->cntl | MPU_TIMER_AR;
}

unsigned long omap_mpu_timer_ticks_to_usecs(unsigned long nr_ticks)
static inline void omap_mpu_remove_autoreset(int nr)
{
unsigned long long nsec;
volatile omap_mpu_timer_regs_t* timer = omap_mpu_timer_base(nr);

nsec = cycles_2_ns((unsigned long long)nr_ticks);
return (unsigned long)nsec / 1000;
timer->cntl = timer->cntl & ~MPU_TIMER_AR;
}

/*
* Last processed system timer interrupt
*/
static unsigned long omap_mpu_timer_last = 0;
static inline void omap_mpu_timer_start(int nr, unsigned long load_val,
int autoreset)
{
volatile omap_mpu_timer_regs_t* timer = omap_mpu_timer_base(nr);
unsigned int timerflags = (MPU_TIMER_CLOCK_ENABLE | MPU_TIMER_ST);

if (autoreset) timerflags |= MPU_TIMER_AR;

timer->cntl = MPU_TIMER_CLOCK_ENABLE;
udelay(1);
timer->load_tim = load_val;
udelay(1);
timer->cntl = timerflags;
}

/*
* Returns elapsed usecs since last system timer interrupt
* ---------------------------------------------------------------------------
* MPU timer 1 ... count down to zero, interrupt, reload
* ---------------------------------------------------------------------------
*/
static unsigned long omap_mpu_timer_gettimeoffset(void)
static int omap_mpu_set_next_event(unsigned long cycles,
struct clock_event_device *evt)
{
unsigned long now = 0 - omap_mpu_timer_read(0);
unsigned long elapsed = now - omap_mpu_timer_last;
omap_mpu_timer_start(0, cycles, 0);
return 0;
}

return omap_mpu_timer_ticks_to_usecs(elapsed);
static void omap_mpu_set_mode(enum clock_event_mode mode,
struct clock_event_device *evt)
{
switch (mode) {
case CLOCK_EVT_MODE_PERIODIC:
omap_mpu_set_autoreset(0);
break;
case CLOCK_EVT_MODE_ONESHOT:
omap_mpu_remove_autoreset(0);
break;
case CLOCK_EVT_MODE_UNUSED:
case CLOCK_EVT_MODE_SHUTDOWN:
break;
}
}

/*
* Elapsed time between interrupts is calculated using timer0.
* Latency during the interrupt is calculated using timer1.
* Both timer0 and timer1 are counting at 6MHz (P2 6.5MHz).
*/
static irqreturn_t omap_mpu_timer_interrupt(int irq, void *dev_id)
static struct clock_event_device clockevent_mpu_timer1 = {
.name = "mpu_timer1",
.features = CLOCK_EVT_FEAT_PERIODIC, CLOCK_EVT_FEAT_ONESHOT,
.shift = 32,
.set_next_event = omap_mpu_set_next_event,
.set_mode = omap_mpu_set_mode,
};

static irqreturn_t omap_mpu_timer1_interrupt(int irq, void *dev_id)
{
unsigned long now, latency;
struct clock_event_device *evt = &clockevent_mpu_timer1;

write_seqlock(&xtime_lock);
now = 0 - omap_mpu_timer_read(0);
latency = MPU_TICKS_PER_SEC / HZ - omap_mpu_timer_read(1);
omap_mpu_timer_last = now - latency;
timer_tick();
write_sequnlock(&xtime_lock);
evt->event_handler(evt);

return IRQ_HANDLED;
}

static struct irqaction omap_mpu_timer_irq = {
.name = "mpu timer",
static struct irqaction omap_mpu_timer1_irq = {
.name = "mpu_timer1",
.flags = IRQF_DISABLED | IRQF_TIMER,
.handler = omap_mpu_timer_interrupt,
.handler = omap_mpu_timer1_interrupt,
};

static unsigned long omap_mpu_timer1_overflows;
static irqreturn_t omap_mpu_timer1_interrupt(int irq, void *dev_id)
static __init void omap_init_mpu_timer(unsigned long rate)
{
set_cyc2ns_scale(rate / 1000);

setup_irq(INT_TIMER1, &omap_mpu_timer1_irq);
omap_mpu_timer_start(0, (rate / HZ) - 1, 1);

clockevent_mpu_timer1.mult = div_sc(rate, NSEC_PER_SEC,
clockevent_mpu_timer1.shift);
clockevent_mpu_timer1.max_delta_ns =
clockevent_delta2ns(-1, &clockevent_mpu_timer1);
clockevent_mpu_timer1.min_delta_ns =
clockevent_delta2ns(1, &clockevent_mpu_timer1);

clockevent_mpu_timer1.cpumask = cpumask_of_cpu(0);
clockevents_register_device(&clockevent_mpu_timer1);
}


/*
* ---------------------------------------------------------------------------
* MPU timer 2 ... free running 32-bit clock source and scheduler clock
* ---------------------------------------------------------------------------
*/

static unsigned long omap_mpu_timer2_overflows;

static irqreturn_t omap_mpu_timer2_interrupt(int irq, void *dev_id)
{
omap_mpu_timer1_overflows++;
omap_mpu_timer2_overflows++;
return IRQ_HANDLED;
}

static struct irqaction omap_mpu_timer1_irq = {
.name = "mpu timer1 overflow",
static struct irqaction omap_mpu_timer2_irq = {
.name = "mpu_timer2",
.flags = IRQF_DISABLED,
.handler = omap_mpu_timer1_interrupt,
.handler = omap_mpu_timer2_interrupt,
};

static __init void omap_init_mpu_timer(void)
static cycle_t mpu_read(void)
{
set_cyc2ns_scale(MPU_TICKS_PER_SEC / 1000);
omap_timer.offset = omap_mpu_timer_gettimeoffset;
setup_irq(INT_TIMER1, &omap_mpu_timer1_irq);
setup_irq(INT_TIMER2, &omap_mpu_timer_irq);
omap_mpu_timer_start(0, 0xffffffff);
omap_mpu_timer_start(1, MPU_TIMER_TICK_PERIOD);
return ~omap_mpu_timer_read(1);
}

static struct clocksource clocksource_mpu = {
.name = "mpu_timer2",
.rating = 300,
.read = mpu_read,
.mask = CLOCKSOURCE_MASK(32),
.shift = 24,
.flags = CLOCK_SOURCE_IS_CONTINUOUS,
};

static void __init omap_init_clocksource(unsigned long rate)
{
static char err[] __initdata = KERN_ERR
"%s: can't register clocksource!\n";

clocksource_mpu.mult
= clocksource_khz2mult(rate/1000, clocksource_mpu.shift);

setup_irq(INT_TIMER2, &omap_mpu_timer2_irq);
omap_mpu_timer_start(1, ~0, 1);

if (clocksource_register(&clocksource_mpu))
printk(err, clocksource_mpu.name);
}


/*
* Scheduler clock - returns current time in nanosec units.
*/
unsigned long long sched_clock(void)
{
unsigned long ticks = 0 - omap_mpu_timer_read(0);
unsigned long ticks = 0 - omap_mpu_timer_read(1);
unsigned long long ticks64;

ticks64 = omap_mpu_timer1_overflows;
ticks64 = omap_mpu_timer2_overflows;
ticks64 <<= 32;
ticks64 |= ticks;

Expand All @@ -225,10 +274,21 @@ unsigned long long sched_clock(void)
*/
static void __init omap_timer_init(void)
{
omap_init_mpu_timer();
struct clk *ck_ref = clk_get(NULL, "ck_ref");
unsigned long rate;

BUG_ON(IS_ERR(ck_ref));

rate = clk_get_rate(ck_ref);
clk_put(ck_ref);

/* PTV = 0 */
rate /= 2;

omap_init_mpu_timer(rate);
omap_init_clocksource(rate);
}

struct sys_timer omap_timer = {
.init = omap_timer_init,
.offset = NULL, /* Initialized later */
};
1 change: 1 addition & 0 deletions trunk/arch/arm/plat-omap/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ choice

config ARCH_OMAP1
bool "TI OMAP1"
select GENERIC_CLOCKEVENTS

config ARCH_OMAP2
bool "TI OMAP2"
Expand Down
50 changes: 50 additions & 0 deletions trunk/arch/arm/plat-omap/common.c
Original file line number Diff line number Diff line change
Expand Up @@ -156,3 +156,53 @@ static int __init omap_add_serial_console(void)
return add_preferred_console("ttyS", line, opt);
}
console_initcall(omap_add_serial_console);


/*
* 32KHz clocksource ... always available, on pretty most chips except
* OMAP 730 and 1510. Other timers could be used as clocksources, with
* higher resolution in free-running counter modes (e.g. 12 MHz xtal),
* but systems won't necessarily want to spend resources that way.
*/

#if defined(CONFIG_ARCH_OMAP16XX)
#define TIMER_32K_SYNCHRONIZED 0xfffbc410
#elif defined(CONFIG_ARCH_OMAP24XX)
#define TIMER_32K_SYNCHRONIZED 0x48004010
#endif

#ifdef TIMER_32K_SYNCHRONIZED

#include <linux/clocksource.h>

static cycle_t omap_32k_read(void)
{
return omap_readl(TIMER_32K_SYNCHRONIZED);
}

static struct clocksource clocksource_32k = {
.name = "32k_counter",
.rating = 250,
.read = omap_32k_read,
.mask = CLOCKSOURCE_MASK(32),
.shift = 10,
.flags = CLOCK_SOURCE_IS_CONTINUOUS,
};

static int __init omap_init_clocksource_32k(void)
{
static char err[] __initdata = KERN_ERR
"%s: can't register clocksource!\n";

if (cpu_is_omap16xx() || cpu_is_omap24xx()) {
clocksource_32k.mult = clocksource_hz2mult(32768,
clocksource_32k.shift);

if (clocksource_register(&clocksource_32k))
printk(err, clocksource_32k.name);
}
return 0;
}
arch_initcall(omap_init_clocksource_32k);

#endif /* TIMER_32K_SYNCHRONIZED */
Loading

0 comments on commit 08b855b

Please sign in to comment.