| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1 | /* | 
|  | 2 | *  linux/arch/arm/mach-imx/time.c | 
|  | 3 | * | 
|  | 4 | *  Copyright (C) 2000-2001 Deep Blue Solutions | 
|  | 5 | *  Copyright (C) 2002 Shane Nay (shane@minirl.com) | 
|  | 6 | * | 
|  | 7 | * This program is free software; you can redistribute it and/or modify | 
|  | 8 | * it under the terms of the GNU General Public License version 2 as | 
|  | 9 | * published by the Free Software Foundation. | 
|  | 10 | */ | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 11 | #include <linux/kernel.h> | 
|  | 12 | #include <linux/sched.h> | 
|  | 13 | #include <linux/init.h> | 
|  | 14 | #include <linux/interrupt.h> | 
| Thomas Gleixner | a6284ac | 2006-07-01 22:32:34 +0100 | [diff] [blame] | 15 | #include <linux/irq.h> | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 16 | #include <linux/time.h> | 
| Pavel Pisa | 86987d5 | 2006-12-06 17:19:44 +0100 | [diff] [blame] | 17 | #include <linux/clocksource.h> | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 18 |  | 
|  | 19 | #include <asm/hardware.h> | 
|  | 20 | #include <asm/io.h> | 
|  | 21 | #include <asm/leds.h> | 
|  | 22 | #include <asm/irq.h> | 
|  | 23 | #include <asm/mach/time.h> | 
|  | 24 |  | 
|  | 25 | /* Use timer 1 as system timer */ | 
|  | 26 | #define TIMER_BASE IMX_TIM1_BASE | 
|  | 27 |  | 
| Pavel Pisa | 86987d5 | 2006-12-06 17:19:44 +0100 | [diff] [blame] | 28 | static unsigned long evt_diff; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 29 |  | 
|  | 30 | /* | 
|  | 31 | * IRQ handler for the timer | 
|  | 32 | */ | 
|  | 33 | static irqreturn_t | 
| Linus Torvalds | 0cd61b6 | 2006-10-06 10:53:39 -0700 | [diff] [blame] | 34 | imx_timer_interrupt(int irq, void *dev_id) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 35 | { | 
| Pavel Pisa | 86987d5 | 2006-12-06 17:19:44 +0100 | [diff] [blame] | 36 | uint32_t tstat; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 37 |  | 
|  | 38 | /* clear the interrupt */ | 
| Pavel Pisa | 86987d5 | 2006-12-06 17:19:44 +0100 | [diff] [blame] | 39 | tstat = IMX_TSTAT(TIMER_BASE); | 
|  | 40 | IMX_TSTAT(TIMER_BASE) = 0; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 41 |  | 
| Pavel Pisa | 86987d5 | 2006-12-06 17:19:44 +0100 | [diff] [blame] | 42 | if (tstat & TSTAT_COMP) { | 
|  | 43 | do { | 
|  | 44 |  | 
|  | 45 | write_seqlock(&xtime_lock); | 
|  | 46 | timer_tick(); | 
|  | 47 | write_sequnlock(&xtime_lock); | 
|  | 48 | IMX_TCMP(TIMER_BASE) += evt_diff; | 
|  | 49 |  | 
|  | 50 | } while (unlikely((int32_t)(IMX_TCMP(TIMER_BASE) | 
|  | 51 | - IMX_TCN(TIMER_BASE)) < 0)); | 
|  | 52 | } | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 53 |  | 
|  | 54 | return IRQ_HANDLED; | 
|  | 55 | } | 
|  | 56 |  | 
|  | 57 | static struct irqaction imx_timer_irq = { | 
|  | 58 | .name		= "i.MX Timer Tick", | 
| Thomas Gleixner | 52e405e | 2006-07-03 02:20:05 +0200 | [diff] [blame] | 59 | .flags		= IRQF_DISABLED | IRQF_TIMER, | 
| Russell King | 09b8b5f | 2005-06-26 17:06:36 +0100 | [diff] [blame] | 60 | .handler	= imx_timer_interrupt, | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 61 | }; | 
|  | 62 |  | 
|  | 63 | /* | 
| Pavel Pisa | 86987d5 | 2006-12-06 17:19:44 +0100 | [diff] [blame] | 64 | * Set up timer hardware into expected mode and state. | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 65 | */ | 
| Pavel Pisa | 86987d5 | 2006-12-06 17:19:44 +0100 | [diff] [blame] | 66 | static void __init imx_timer_hardware_init(void) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 67 | { | 
|  | 68 | /* | 
|  | 69 | * Initialise to a known state (all timers off, and timing reset) | 
|  | 70 | */ | 
|  | 71 | IMX_TCTL(TIMER_BASE) = 0; | 
|  | 72 | IMX_TPRER(TIMER_BASE) = 0; | 
|  | 73 | IMX_TCMP(TIMER_BASE) = LATCH - 1; | 
| Pavel Pisa | 86987d5 | 2006-12-06 17:19:44 +0100 | [diff] [blame] | 74 |  | 
|  | 75 | IMX_TCTL(TIMER_BASE) = TCTL_FRR | TCTL_CLK_PCLK1 | TCTL_IRQEN | TCTL_TEN; | 
|  | 76 | evt_diff = LATCH; | 
|  | 77 | } | 
|  | 78 |  | 
|  | 79 | cycle_t imx_get_cycles(void) | 
|  | 80 | { | 
|  | 81 | return IMX_TCN(TIMER_BASE); | 
|  | 82 | } | 
|  | 83 |  | 
|  | 84 | static struct clocksource clocksource_imx = { | 
|  | 85 | .name 		= "imx_timer1", | 
|  | 86 | .rating		= 200, | 
|  | 87 | .read		= imx_get_cycles, | 
|  | 88 | .mask		= 0xFFFFFFFF, | 
|  | 89 | .shift 		= 20, | 
| Thomas Gleixner | c66699a | 2007-02-16 01:27:37 -0800 | [diff] [blame] | 90 | .flags		= CLOCK_SOURCE_IS_CONTINUOUS, | 
| Pavel Pisa | 86987d5 | 2006-12-06 17:19:44 +0100 | [diff] [blame] | 91 | }; | 
|  | 92 |  | 
|  | 93 | static int __init imx_clocksource_init(void) | 
|  | 94 | { | 
|  | 95 | clocksource_imx.mult = | 
|  | 96 | clocksource_hz2mult(imx_get_perclk1(), clocksource_imx.shift); | 
|  | 97 | clocksource_register(&clocksource_imx); | 
|  | 98 |  | 
|  | 99 | return 0; | 
|  | 100 | } | 
|  | 101 |  | 
|  | 102 | static void __init imx_timer_init(void) | 
|  | 103 | { | 
|  | 104 | imx_timer_hardware_init(); | 
|  | 105 | imx_clocksource_init(); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 106 |  | 
|  | 107 | /* | 
|  | 108 | * Make irqs happen for the system timer | 
|  | 109 | */ | 
|  | 110 | setup_irq(TIM1_INT, &imx_timer_irq); | 
|  | 111 | } | 
|  | 112 |  | 
|  | 113 | struct sys_timer imx_timer = { | 
|  | 114 | .init		= imx_timer_init, | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 115 | }; |