diff options
Diffstat (limited to 'arch/arm/mach-mmp/time.c')
| -rw-r--r-- | arch/arm/mach-mmp/time.c | 163 |
1 files changed, 104 insertions, 59 deletions
diff --git a/arch/arm/mach-mmp/time.c b/arch/arm/mach-mmp/time.c index 66528193f93..2756351dbb3 100644 --- a/arch/arm/mach-mmp/time.c +++ b/arch/arm/mach-mmp/time.c @@ -9,7 +9,7 @@ * 2008-04-11: Jason Chagas <Jason.chagas@marvell.com> * 2008-10-08: Bin Yang <bin.yang@marvell.com> * - * The timers module actually includes three timers, each timer with upto + * The timers module actually includes three timers, each timer with up to * three match comparators. Timer #0 is used here in free-running mode as * the clock source, and match comparator #1 used as clock event device. * @@ -25,8 +25,10 @@ #include <linux/io.h> #include <linux/irq.h> -#include <linux/sched.h> -#include <linux/cnt32_to_63.h> +#include <linux/of.h> +#include <linux/of_address.h> +#include <linux/of_irq.h> +#include <linux/sched_clock.h> #include <mach/addr-map.h> #include <mach/regs-timers.h> @@ -37,28 +39,18 @@ #include "clock.h" +#ifdef CONFIG_CPU_MMP2 +#define MMP_CLOCK_FREQ 6500000 +#else +#define MMP_CLOCK_FREQ 3250000 +#endif + #define TIMERS_VIRT_BASE TIMERS1_VIRT_BASE #define MAX_DELTA (0xfffffffe) #define MIN_DELTA (16) -#define TCR2NS_SCALE_FACTOR 10 - -static unsigned long tcr2ns_scale; - -static void __init set_tcr2ns_scale(unsigned long tcr_rate) -{ - unsigned long long v = 1000000000ULL << TCR2NS_SCALE_FACTOR; - do_div(v, tcr_rate); - tcr2ns_scale = v; - /* - * We want an even value to automatically clear the top bit - * returned by cnt32_to_63() without an additional run time - * instruction. So if the LSB is 1 then round it up. - */ - if (tcr2ns_scale & 1) - tcr2ns_scale++; -} +static void __iomem *mmp_timer_base = TIMERS_VIRT_BASE; /* * FIXME: the timer needs some delay to stablize the counter capture @@ -67,46 +59,68 @@ static inline uint32_t timer_read(void) { int delay = 100; - __raw_writel(1, TIMERS_VIRT_BASE + TMR_CVWR(0)); + __raw_writel(1, mmp_timer_base + TMR_CVWR(1)); while (delay--) cpu_relax(); - return __raw_readl(TIMERS_VIRT_BASE + TMR_CVWR(0)); + return __raw_readl(mmp_timer_base + TMR_CVWR(1)); } -unsigned long long sched_clock(void) +static u64 notrace mmp_read_sched_clock(void) { - unsigned long long v = cnt32_to_63(timer_read()); - return (v * tcr2ns_scale) >> TCR2NS_SCALE_FACTOR; + return timer_read(); } static irqreturn_t timer_interrupt(int irq, void *dev_id) { struct clock_event_device *c = dev_id; - /* disable and clear pending interrupt status */ - __raw_writel(0x0, TIMERS_VIRT_BASE + TMR_IER(0)); - __raw_writel(0x1, TIMERS_VIRT_BASE + TMR_ICR(0)); + /* + * Clear pending interrupt status. + */ + __raw_writel(0x01, mmp_timer_base + TMR_ICR(0)); + + /* + * Disable timer 0. + */ + __raw_writel(0x02, mmp_timer_base + TMR_CER); + c->event_handler(c); + return IRQ_HANDLED; } static int timer_set_next_event(unsigned long delta, struct clock_event_device *dev) { - unsigned long flags, next; + unsigned long flags; local_irq_save(flags); - /* clear pending interrupt status and enable */ - __raw_writel(0x01, TIMERS_VIRT_BASE + TMR_ICR(0)); - __raw_writel(0x01, TIMERS_VIRT_BASE + TMR_IER(0)); + /* + * Disable timer 0. + */ + __raw_writel(0x02, mmp_timer_base + TMR_CER); - next = timer_read() + delta; - __raw_writel(next, TIMERS_VIRT_BASE + TMR_TN_MM(0, 0)); + /* + * Clear and enable timer match 0 interrupt. + */ + __raw_writel(0x01, mmp_timer_base + TMR_ICR(0)); + __raw_writel(0x01, mmp_timer_base + TMR_IER(0)); + + /* + * Setup new clockevent timer value. + */ + __raw_writel(delta - 1, mmp_timer_base + TMR_TN_MM(0, 0)); + + /* + * Enable timer 0. + */ + __raw_writel(0x03, mmp_timer_base + TMR_CER); local_irq_restore(flags); + return 0; } @@ -121,7 +135,7 @@ static void timer_set_mode(enum clock_event_mode mode, case CLOCK_EVT_MODE_UNUSED: case CLOCK_EVT_MODE_SHUTDOWN: /* disable the matching interrupt */ - __raw_writel(0x00, TIMERS_VIRT_BASE + TMR_IER(0)); + __raw_writel(0x00, mmp_timer_base + TMR_IER(0)); break; case CLOCK_EVT_MODE_RESUME: case CLOCK_EVT_MODE_PERIODIC: @@ -133,7 +147,6 @@ static void timer_set_mode(enum clock_event_mode mode, static struct clock_event_device ckevt = { .name = "clockevent", .features = CLOCK_EVT_FEAT_ONESHOT, - .shift = 32, .rating = 200, .set_next_event = timer_set_next_event, .set_mode = timer_set_mode, @@ -146,7 +159,6 @@ static cycle_t clksrc_read(struct clocksource *cs) static struct clocksource cksrc = { .name = "clocksource", - .shift = 20, .rating = 200, .read = clksrc_read, .mask = CLOCKSOURCE_MASK(32), @@ -155,29 +167,32 @@ static struct clocksource cksrc = { static void __init timer_config(void) { - uint32_t ccr = __raw_readl(TIMERS_VIRT_BASE + TMR_CCR); - uint32_t cer = __raw_readl(TIMERS_VIRT_BASE + TMR_CER); - uint32_t cmr = __raw_readl(TIMERS_VIRT_BASE + TMR_CMR); + uint32_t ccr = __raw_readl(mmp_timer_base + TMR_CCR); + + __raw_writel(0x0, mmp_timer_base + TMR_CER); /* disable */ - __raw_writel(cer & ~0x1, TIMERS_VIRT_BASE + TMR_CER); /* disable */ + ccr &= (cpu_is_mmp2()) ? (TMR_CCR_CS_0(0) | TMR_CCR_CS_1(0)) : + (TMR_CCR_CS_0(3) | TMR_CCR_CS_1(3)); + __raw_writel(ccr, mmp_timer_base + TMR_CCR); - ccr &= (cpu_is_mmp2()) ? TMR_CCR_CS_0(0) : TMR_CCR_CS_0(3); - __raw_writel(ccr, TIMERS_VIRT_BASE + TMR_CCR); + /* set timer 0 to periodic mode, and timer 1 to free-running mode */ + __raw_writel(0x2, mmp_timer_base + TMR_CMR); - /* free-running mode */ - __raw_writel(cmr | 0x01, TIMERS_VIRT_BASE + TMR_CMR); + __raw_writel(0x1, mmp_timer_base + TMR_PLCR(0)); /* periodic */ + __raw_writel(0x7, mmp_timer_base + TMR_ICR(0)); /* clear status */ + __raw_writel(0x0, mmp_timer_base + TMR_IER(0)); - __raw_writel(0x0, TIMERS_VIRT_BASE + TMR_PLCR(0)); /* free-running */ - __raw_writel(0x7, TIMERS_VIRT_BASE + TMR_ICR(0)); /* clear status */ - __raw_writel(0x0, TIMERS_VIRT_BASE + TMR_IER(0)); + __raw_writel(0x0, mmp_timer_base + TMR_PLCR(1)); /* free-running */ + __raw_writel(0x7, mmp_timer_base + TMR_ICR(1)); /* clear status */ + __raw_writel(0x0, mmp_timer_base + TMR_IER(1)); - /* enable timer counter */ - __raw_writel(cer | 0x01, TIMERS_VIRT_BASE + TMR_CER); + /* enable timer 1 counter */ + __raw_writel(0x2, mmp_timer_base + TMR_CER); } static struct irqaction timer_irq = { .name = "timer", - .flags = IRQF_DISABLED | IRQF_TIMER | IRQF_IRQPOLL, + .flags = IRQF_TIMER | IRQF_IRQPOLL, .handler = timer_interrupt, .dev_id = &ckevt, }; @@ -186,17 +201,47 @@ void __init timer_init(int irq) { timer_config(); - set_tcr2ns_scale(CLOCK_TICK_RATE); + sched_clock_register(mmp_read_sched_clock, 32, MMP_CLOCK_FREQ); - ckevt.mult = div_sc(CLOCK_TICK_RATE, NSEC_PER_SEC, ckevt.shift); - ckevt.max_delta_ns = clockevent_delta2ns(MAX_DELTA, &ckevt); - ckevt.min_delta_ns = clockevent_delta2ns(MIN_DELTA, &ckevt); ckevt.cpumask = cpumask_of(0); - cksrc.mult = clocksource_hz2mult(CLOCK_TICK_RATE, cksrc.shift); - setup_irq(irq, &timer_irq); - clocksource_register(&cksrc); - clockevents_register_device(&ckevt); + clocksource_register_hz(&cksrc, MMP_CLOCK_FREQ); + clockevents_config_and_register(&ckevt, MMP_CLOCK_FREQ, + MIN_DELTA, MAX_DELTA); +} + +#ifdef CONFIG_OF +static struct of_device_id mmp_timer_dt_ids[] = { + { .compatible = "mrvl,mmp-timer", }, + {} +}; + +void __init mmp_dt_init_timer(void) +{ + struct device_node *np; + int irq, ret; + + np = of_find_matching_node(NULL, mmp_timer_dt_ids); + if (!np) { + ret = -ENODEV; + goto out; + } + + irq = irq_of_parse_and_map(np, 0); + if (!irq) { + ret = -EINVAL; + goto out; + } + mmp_timer_base = of_iomap(np, 0); + if (!mmp_timer_base) { + ret = -ENOMEM; + goto out; + } + timer_init(irq); + return; +out: + pr_err("Failed to get timer from device tree with error:%d\n", ret); } +#endif |
