diff options
| -rw-r--r-- | arch/arm/mach-realview/localtimer.c | 94 | ||||
| -rw-r--r-- | include/asm-arm/hardware/arm_twd.h | 7 |
2 files changed, 71 insertions, 30 deletions
diff --git a/arch/arm/mach-realview/localtimer.c b/arch/arm/mach-realview/localtimer.c index 529eb6979e61..4f87b4d09c7d 100644 --- a/arch/arm/mach-realview/localtimer.c +++ b/arch/arm/mach-realview/localtimer.c | |||
| @@ -16,8 +16,8 @@ | |||
| 16 | #include <linux/jiffies.h> | 16 | #include <linux/jiffies.h> |
| 17 | #include <linux/percpu.h> | 17 | #include <linux/percpu.h> |
| 18 | #include <linux/clockchips.h> | 18 | #include <linux/clockchips.h> |
| 19 | #include <linux/irq.h> | ||
| 19 | 20 | ||
| 20 | #include <asm/mach/time.h> | ||
| 21 | #include <asm/hardware/arm_twd.h> | 21 | #include <asm/hardware/arm_twd.h> |
| 22 | #include <asm/hardware/gic.h> | 22 | #include <asm/hardware/gic.h> |
| 23 | #include <asm/hardware.h> | 23 | #include <asm/hardware.h> |
| @@ -43,6 +43,43 @@ void local_timer_interrupt(void) | |||
| 43 | 43 | ||
| 44 | static unsigned long mpcore_timer_rate; | 44 | static unsigned long mpcore_timer_rate; |
| 45 | 45 | ||
| 46 | static void local_timer_set_mode(enum clock_event_mode mode, | ||
| 47 | struct clock_event_device *clk) | ||
| 48 | { | ||
| 49 | void __iomem *base = TWD_BASE(smp_processor_id()); | ||
| 50 | unsigned long ctrl; | ||
| 51 | |||
| 52 | switch(mode) { | ||
| 53 | case CLOCK_EVT_MODE_PERIODIC: | ||
| 54 | /* timer load already set up */ | ||
| 55 | ctrl = TWD_TIMER_CONTROL_ENABLE | TWD_TIMER_CONTROL_IT_ENABLE | ||
| 56 | | TWD_TIMER_CONTROL_PERIODIC; | ||
| 57 | break; | ||
| 58 | case CLOCK_EVT_MODE_ONESHOT: | ||
| 59 | /* period set, and timer enabled in 'next_event' hook */ | ||
| 60 | ctrl = TWD_TIMER_CONTROL_IT_ENABLE | TWD_TIMER_CONTROL_ONESHOT; | ||
| 61 | break; | ||
| 62 | case CLOCK_EVT_MODE_UNUSED: | ||
| 63 | case CLOCK_EVT_MODE_SHUTDOWN: | ||
| 64 | default: | ||
| 65 | ctrl = 0; | ||
| 66 | } | ||
| 67 | |||
| 68 | __raw_writel(ctrl, base + TWD_TIMER_CONTROL); | ||
| 69 | } | ||
| 70 | |||
| 71 | static int local_timer_set_next_event(unsigned long evt, | ||
| 72 | struct clock_event_device *unused) | ||
| 73 | { | ||
| 74 | void __iomem *base = TWD_BASE(smp_processor_id()); | ||
| 75 | unsigned long ctrl = __raw_readl(base + TWD_TIMER_CONTROL); | ||
| 76 | |||
| 77 | __raw_writel(evt, base + TWD_TIMER_COUNTER); | ||
| 78 | __raw_writel(ctrl | TWD_TIMER_CONTROL_ENABLE, base + TWD_TIMER_CONTROL); | ||
| 79 | |||
| 80 | return 0; | ||
| 81 | } | ||
| 82 | |||
| 46 | /* | 83 | /* |
| 47 | * local_timer_ack: checks for a local timer interrupt. | 84 | * local_timer_ack: checks for a local timer interrupt. |
| 48 | * | 85 | * |
| @@ -61,12 +98,11 @@ int local_timer_ack(void) | |||
| 61 | return 0; | 98 | return 0; |
| 62 | } | 99 | } |
| 63 | 100 | ||
| 64 | void __cpuinit local_timer_setup(unsigned int cpu) | 101 | static void __cpuinit twd_calibrate_rate(unsigned int cpu) |
| 65 | { | 102 | { |
| 66 | void __iomem *base = TWD_BASE(cpu); | 103 | void __iomem *base = TWD_BASE(cpu); |
| 67 | unsigned int load, offset; | 104 | unsigned long load, count; |
| 68 | u64 waitjiffies; | 105 | u64 waitjiffies; |
| 69 | unsigned int count; | ||
| 70 | 106 | ||
| 71 | /* | 107 | /* |
| 72 | * If this is the first time round, we need to work out how fast | 108 | * If this is the first time round, we need to work out how fast |
| @@ -104,36 +140,36 @@ void __cpuinit local_timer_setup(unsigned int cpu) | |||
| 104 | load = mpcore_timer_rate / HZ; | 140 | load = mpcore_timer_rate / HZ; |
| 105 | 141 | ||
| 106 | __raw_writel(load, base + TWD_TIMER_LOAD); | 142 | __raw_writel(load, base + TWD_TIMER_LOAD); |
| 107 | __raw_writel(0x7, base + TWD_TIMER_CONTROL); | 143 | } |
| 108 | |||
| 109 | /* | ||
| 110 | * Now maneuver our local tick into the right part of the jiffy. | ||
| 111 | * Start by working out where within the tick our local timer | ||
| 112 | * interrupt should go. | ||
| 113 | */ | ||
| 114 | offset = ((mpcore_timer_rate / HZ) / (NR_CPUS + 1)) * (cpu + 1); | ||
| 115 | 144 | ||
| 116 | /* | 145 | /* |
| 117 | * gettimeoffset() will return a number of us since the last tick. | 146 | * Setup the local clock events for a CPU. |
| 118 | * Convert this number of us to a local timer tick count. | 147 | */ |
| 119 | * Be careful of integer overflow whilst keeping maximum precision. | 148 | void __cpuinit local_timer_setup(unsigned int cpu) |
| 120 | * | 149 | { |
| 121 | * with HZ=100 and 1MHz (fpga) ~ 1GHz processor: | 150 | struct clock_event_device *clk = &per_cpu(local_clockevent, cpu); |
| 122 | * load = 1 ~ 10,000 | 151 | unsigned long flags; |
| 123 | * mpcore_timer_rate/10000 = 100 ~ 100,000 | ||
| 124 | * | ||
| 125 | * so the multiply value will be less than 10^9 always. | ||
| 126 | */ | ||
| 127 | load = (system_timer->offset() * (mpcore_timer_rate / 10000)) / 100; | ||
| 128 | 152 | ||
| 129 | /* Add on our offset to get the load value */ | 153 | twd_calibrate_rate(cpu); |
| 130 | load = (load + offset) % (mpcore_timer_rate / HZ); | ||
| 131 | 154 | ||
| 132 | __raw_writel(load, base + TWD_TIMER_COUNTER); | 155 | clk->name = "local_timer"; |
| 156 | clk->features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT; | ||
| 157 | clk->rating = 350; | ||
| 158 | clk->set_mode = local_timer_set_mode; | ||
| 159 | clk->set_next_event = local_timer_set_next_event; | ||
| 160 | clk->irq = IRQ_LOCALTIMER; | ||
| 161 | clk->cpumask = cpumask_of_cpu(cpu); | ||
| 162 | clk->shift = 20; | ||
| 163 | clk->mult = div_sc(mpcore_timer_rate, NSEC_PER_SEC, clk->shift); | ||
| 164 | clk->max_delta_ns = clockevent_delta2ns(0xffffffff, clk); | ||
| 165 | clk->min_delta_ns = clockevent_delta2ns(0xf, clk); | ||
| 133 | 166 | ||
| 134 | /* Make sure our local interrupt controller has this enabled */ | 167 | /* Make sure our local interrupt controller has this enabled */ |
| 135 | __raw_writel(1 << IRQ_LOCALTIMER, | 168 | local_irq_save(flags); |
| 136 | __io_address(REALVIEW_GIC_DIST_BASE) + GIC_DIST_ENABLE_SET); | 169 | get_irq_chip(IRQ_LOCALTIMER)->unmask(IRQ_LOCALTIMER); |
| 170 | local_irq_restore(flags); | ||
| 171 | |||
| 172 | clockevents_register_device(clk); | ||
| 137 | } | 173 | } |
| 138 | 174 | ||
| 139 | /* | 175 | /* |
diff --git a/include/asm-arm/hardware/arm_twd.h b/include/asm-arm/hardware/arm_twd.h index 131d5b40e072..e521b70713c8 100644 --- a/include/asm-arm/hardware/arm_twd.h +++ b/include/asm-arm/hardware/arm_twd.h | |||
| @@ -1,7 +1,7 @@ | |||
| 1 | #ifndef __ASM_HARDWARE_TWD_H | 1 | #ifndef __ASM_HARDWARE_TWD_H |
| 2 | #define __ASM_HARDWARE_TWD_H | 2 | #define __ASM_HARDWARE_TWD_H |
| 3 | 3 | ||
| 4 | #define TWD_TIMER_LOAD 0x00 | 4 | #define TWD_TIMER_LOAD 0x00 |
| 5 | #define TWD_TIMER_COUNTER 0x04 | 5 | #define TWD_TIMER_COUNTER 0x04 |
| 6 | #define TWD_TIMER_CONTROL 0x08 | 6 | #define TWD_TIMER_CONTROL 0x08 |
| 7 | #define TWD_TIMER_INTSTAT 0x0C | 7 | #define TWD_TIMER_INTSTAT 0x0C |
| @@ -13,4 +13,9 @@ | |||
| 13 | #define TWD_WDOG_RESETSTAT 0x30 | 13 | #define TWD_WDOG_RESETSTAT 0x30 |
| 14 | #define TWD_WDOG_DISABLE 0x34 | 14 | #define TWD_WDOG_DISABLE 0x34 |
| 15 | 15 | ||
| 16 | #define TWD_TIMER_CONTROL_ENABLE (1 << 0) | ||
| 17 | #define TWD_TIMER_CONTROL_ONESHOT (0 << 1) | ||
| 18 | #define TWD_TIMER_CONTROL_PERIODIC (1 << 1) | ||
| 19 | #define TWD_TIMER_CONTROL_IT_ENABLE (1 << 2) | ||
| 20 | |||
| 16 | #endif | 21 | #endif |
