diff --git a/arch/arm/mach-tegra/timer.c b/arch/arm/mach-tegra/timer.c index 363217308285..cc9ec7020533 100644 --- a/arch/arm/mach-tegra/timer.c +++ b/arch/arm/mach-tegra/timer.c @@ -38,6 +38,10 @@ #include "board.h" #include "clock.h" +#define RTC_SECONDS 0x08 +#define RTC_SHADOW_SECONDS 0x0c +#define RTC_MILLISECONDS 0x10 + #define TIMERUS_CNTR_1US 0x10 #define TIMERUS_USEC_CFG 0x14 #define TIMERUS_CNTR_FREEZE 0x4c @@ -50,14 +54,13 @@ #define TIMER_PTV 0x0 #define TIMER_PCR 0x4 -struct tegra_timer; - -static void __iomem *timer_reg_base = IO_ADDRESS(TEGRA_TMR1_BASE); +static void __iomem *timer_base = IO_ADDRESS(TEGRA_TMR1_BASE); +static void __iomem *rtc_base = IO_ADDRESS(TEGRA_RTC_BASE); #define timer_writel(value, reg) \ - __raw_writel(value, (u32)timer_reg_base + (reg)) + __raw_writel(value, (u32)timer_base + (reg)) #define timer_readl(reg) \ - __raw_readl((u32)timer_reg_base + (reg)) + __raw_readl((u32)timer_base + (reg)) static int tegra_timer_set_next_event(unsigned long cycles, struct clock_event_device *evt) @@ -91,9 +94,29 @@ static void tegra_timer_set_mode(enum clock_event_mode mode, } } -static cycle_t tegra_clocksource_read(struct clocksource *cs) +static u64 tegra_us_clocksource_offset; +static u64 tegra_us_resume_offset; +static cycle_t tegra_clocksource_us_read(struct clocksource *cs) { - return cnt32_to_63(timer_readl(TIMERUS_CNTR_1US)); + return tegra_us_clocksource_offset + + cnt32_to_63(timer_readl(TIMERUS_CNTR_1US)); +} + +void tegra_clocksource_us_suspend(struct clocksource *cs) +{ + tegra_us_resume_offset = tegra_clocksource_us_read(cs); +} + +void tegra_clocksource_us_resume(struct clocksource *cs) +{ + tegra_us_clocksource_offset = tegra_us_resume_offset; +} + +static cycle_t tegra_clocksource_32k_read(struct clocksource *cs) +{ + u32 ms = readl(rtc_base + RTC_MILLISECONDS); + u32 s = readl(rtc_base + RTC_SHADOW_SECONDS); + return (u64)s * 1000 + ms; } static struct clock_event_device tegra_clockevent = { @@ -104,18 +127,54 @@ static struct clock_event_device tegra_clockevent = { .set_mode = tegra_timer_set_mode, }; -static struct clocksource tegra_clocksource = { +static struct clocksource tegra_clocksource_us = { .name = "timer_us", .rating = 300, - .read = tegra_clocksource_read, + .read = tegra_clocksource_us_read, + .suspend= tegra_clocksource_us_suspend, + .resume = tegra_clocksource_us_resume, + .mask = 0x7FFFFFFFFFFFFFFFULL, + .flags = CLOCK_SOURCE_IS_CONTINUOUS, +}; + +static struct clocksource tegra_clocksource_32k = { + .name = "rtc_32k", + .rating = 100, + .read = tegra_clocksource_32k_read, .mask = 0x7FFFFFFFFFFFFFFFULL, .flags = CLOCK_SOURCE_IS_CONTINUOUS, }; unsigned long long sched_clock(void) { - return clocksource_cyc2ns(tegra_clocksource.read(&tegra_clocksource), - tegra_clocksource.mult, tegra_clocksource.shift); + return tegra_clocksource_us.read(&tegra_clocksource_us) * 1000; +} + +/** + * read_persistent_clock - Return time from a persistent clock. + * + * Reads the time from a source which isn't disabled during PM, the + * 32k sync timer. Convert the cycles elapsed since last read into + * nsecs and adds to a monotonically increasing timespec. + */ +static struct timespec persistent_ts; +static cycles_t cycles, last_cycles; +void read_persistent_clock(struct timespec *ts) +{ + unsigned long long nsecs; + cycles_t delta; + struct timespec *tsp = &persistent_ts; + + last_cycles = cycles; + cycles = tegra_clocksource_32k.read(&tegra_clocksource_32k); + delta = cycles - last_cycles; + + nsecs = clocksource_cyc2ns(delta, + tegra_clocksource_32k.mult, + tegra_clocksource_32k.shift); + + timespec_add_ns(tsp, nsecs); + *ts = *tsp; } static irqreturn_t tegra_timer_interrupt(int irq, void *dev_id) @@ -134,6 +193,20 @@ static struct irqaction tegra_timer_irq = { .irq = INT_TMR3, }; +static irqreturn_t tegra_lp2wake_interrupt(int irq, void *dev_id) +{ + timer_writel(1<<30, TIMER4_BASE + TIMER_PCR); + return IRQ_HANDLED; +} + +static struct irqaction tegra_lp2wake_irq = { + .name = "timer_lp2wake", + .flags = IRQF_DISABLED, + .handler = tegra_lp2wake_interrupt, + .dev_id = NULL, + .irq = INT_TMR4, +}; + static void __init tegra_init_timer(void) { unsigned long rate = clk_measure_input_freq(); @@ -160,8 +233,13 @@ static void __init tegra_init_timer(void) WARN(1, "Unknown clock rate"); } - if (clocksource_register_hz(&tegra_clocksource, 1000000)) { - printk(KERN_ERR "Failed to register clocksource\n"); + if (clocksource_register_hz(&tegra_clocksource_us, 1000000)) { + printk(KERN_ERR "Failed to register us clocksource\n"); + BUG(); + } + + if (clocksource_register_hz(&tegra_clocksource_32k, 1000)) { + printk(KERN_ERR "Failed to register 32k clocksource\n"); BUG(); } @@ -171,6 +249,12 @@ static void __init tegra_init_timer(void) BUG(); } + ret = setup_irq(tegra_lp2wake_irq.irq, &tegra_lp2wake_irq); + if (ret) { + printk(KERN_ERR "Failed to register LP2 timer IRQ: %d\n", ret); + BUG(); + } + clockevents_calc_mult_shift(&tegra_clockevent, 1000000, 5); tegra_clockevent.max_delta_ns = clockevent_delta2ns(0x1fffffff, &tegra_clockevent); @@ -186,3 +270,29 @@ static void __init tegra_init_timer(void) struct sys_timer tegra_timer = { .init = tegra_init_timer, }; + +void tegra_lp2_set_trigger(unsigned long cycles) +{ + timer_writel(0, TIMER4_BASE + TIMER_PTV); + if (cycles) { + u32 reg = 0x80000000ul | min(0x1ffffffful, cycles); + timer_writel(reg, TIMER4_BASE + TIMER_PTV); + } +} +EXPORT_SYMBOL(tegra_lp2_set_trigger); + +unsigned long tegra_lp2_timer_remain(void) +{ + return timer_readl(TIMER4_BASE + TIMER_PCR) & 0x1ffffffful; +} + +static u32 usec_config; +void tegra_timer_suspend(void) +{ + usec_config = timer_readl(TIMERUS_USEC_CFG); +} + +void tegra_timer_resume(void) +{ + timer_writel(usec_config, TIMERUS_USEC_CFG); +}