diff options
| author | Linus Torvalds <torvalds@linux-foundation.org> | 2011-01-10 17:50:08 -0500 |
|---|---|---|
| committer | Linus Torvalds <torvalds@linux-foundation.org> | 2011-01-10 17:50:08 -0500 |
| commit | abf8792d0e1b203e303ed1c02437e0e10a39dcda (patch) | |
| tree | 8f0a9c4db37c6e776adf98e1a066f8dcb889e22d /arch/arm/mach-msm/timer.c | |
| parent | e0e736fc0d33861335e2a132e4f688f7fd380c61 (diff) | |
| parent | e14411da420bad7bdaae65cccd8787674e6c565e (diff) | |
Merge branch 'msm-smp' of git://codeaurora.org/quic/kernel/davidb/linux-msm
* 'msm-smp' of git://codeaurora.org/quic/kernel/davidb/linux-msm:
msm: add SMP support for msm
msm: hotplug: support cpu hotplug on msm
msm: timer: SMP timer support for msm
msm: scm-boot: Support for setting cold/warm boot addresses
msm: Secure Channel Manager (SCM) support
Diffstat (limited to 'arch/arm/mach-msm/timer.c')
| -rw-r--r-- | arch/arm/mach-msm/timer.c | 125 |
1 files changed, 102 insertions, 23 deletions
diff --git a/arch/arm/mach-msm/timer.c b/arch/arm/mach-msm/timer.c index 595be7fea31a..c105d28b53e3 100644 --- a/arch/arm/mach-msm/timer.c +++ b/arch/arm/mach-msm/timer.c | |||
| @@ -47,6 +47,19 @@ enum { | |||
| 47 | 47 | ||
| 48 | #define GPT_HZ 32768 | 48 | #define GPT_HZ 32768 |
| 49 | 49 | ||
| 50 | enum timer_location { | ||
| 51 | LOCAL_TIMER = 0, | ||
| 52 | GLOBAL_TIMER = 1, | ||
| 53 | }; | ||
| 54 | |||
| 55 | #ifdef MSM_TMR0_BASE | ||
| 56 | #define MSM_TMR_GLOBAL (MSM_TMR0_BASE - MSM_TMR_BASE) | ||
| 57 | #else | ||
| 58 | #define MSM_TMR_GLOBAL 0 | ||
| 59 | #endif | ||
| 60 | |||
| 61 | #define MSM_GLOBAL_TIMER MSM_CLOCK_DGT | ||
| 62 | |||
| 50 | #if defined(CONFIG_ARCH_QSD8X50) | 63 | #if defined(CONFIG_ARCH_QSD8X50) |
| 51 | #define DGT_HZ (19200000 / 4) /* 19.2 MHz / 4 by default */ | 64 | #define DGT_HZ (19200000 / 4) /* 19.2 MHz / 4 by default */ |
| 52 | #define MSM_DGT_SHIFT (0) | 65 | #define MSM_DGT_SHIFT (0) |
| @@ -65,49 +78,67 @@ struct msm_clock { | |||
| 65 | void __iomem *regbase; | 78 | void __iomem *regbase; |
| 66 | uint32_t freq; | 79 | uint32_t freq; |
| 67 | uint32_t shift; | 80 | uint32_t shift; |
| 81 | void __iomem *global_counter; | ||
| 82 | void __iomem *local_counter; | ||
| 83 | }; | ||
| 84 | |||
| 85 | enum { | ||
| 86 | MSM_CLOCK_GPT, | ||
| 87 | MSM_CLOCK_DGT, | ||
| 88 | NR_TIMERS, | ||
| 68 | }; | 89 | }; |
| 69 | 90 | ||
| 91 | |||
| 92 | static struct msm_clock msm_clocks[]; | ||
| 93 | static struct clock_event_device *local_clock_event; | ||
| 94 | |||
| 70 | static irqreturn_t msm_timer_interrupt(int irq, void *dev_id) | 95 | static irqreturn_t msm_timer_interrupt(int irq, void *dev_id) |
| 71 | { | 96 | { |
| 72 | struct clock_event_device *evt = dev_id; | 97 | struct clock_event_device *evt = dev_id; |
| 98 | if (smp_processor_id() != 0) | ||
| 99 | evt = local_clock_event; | ||
| 100 | if (evt->event_handler == NULL) | ||
| 101 | return IRQ_HANDLED; | ||
| 73 | evt->event_handler(evt); | 102 | evt->event_handler(evt); |
| 74 | return IRQ_HANDLED; | 103 | return IRQ_HANDLED; |
| 75 | } | 104 | } |
| 76 | 105 | ||
| 77 | static cycle_t msm_gpt_read(struct clocksource *cs) | 106 | static cycle_t msm_read_timer_count(struct clocksource *cs) |
| 78 | { | 107 | { |
| 79 | return readl(MSM_GPT_BASE + TIMER_COUNT_VAL); | 108 | struct msm_clock *clk = container_of(cs, struct msm_clock, clocksource); |
| 109 | |||
| 110 | return readl(clk->global_counter); | ||
| 80 | } | 111 | } |
| 81 | 112 | ||
| 82 | static cycle_t msm_dgt_read(struct clocksource *cs) | 113 | static struct msm_clock *clockevent_to_clock(struct clock_event_device *evt) |
| 83 | { | 114 | { |
| 84 | return readl(MSM_DGT_BASE + TIMER_COUNT_VAL) >> MSM_DGT_SHIFT; | 115 | #ifdef CONFIG_SMP |
| 116 | int i; | ||
| 117 | for (i = 0; i < NR_TIMERS; i++) | ||
| 118 | if (evt == &(msm_clocks[i].clockevent)) | ||
| 119 | return &msm_clocks[i]; | ||
| 120 | return &msm_clocks[MSM_GLOBAL_TIMER]; | ||
| 121 | #else | ||
| 122 | return container_of(evt, struct msm_clock, clockevent); | ||
| 123 | #endif | ||
| 85 | } | 124 | } |
| 86 | 125 | ||
| 87 | static int msm_timer_set_next_event(unsigned long cycles, | 126 | static int msm_timer_set_next_event(unsigned long cycles, |
| 88 | struct clock_event_device *evt) | 127 | struct clock_event_device *evt) |
| 89 | { | 128 | { |
| 90 | struct msm_clock *clock = container_of(evt, struct msm_clock, clockevent); | 129 | struct msm_clock *clock = clockevent_to_clock(evt); |
| 91 | uint32_t now = readl(clock->regbase + TIMER_COUNT_VAL); | 130 | uint32_t now = readl(clock->local_counter); |
| 92 | uint32_t alarm = now + (cycles << clock->shift); | 131 | uint32_t alarm = now + (cycles << clock->shift); |
| 93 | int late; | ||
| 94 | 132 | ||
| 95 | writel(alarm, clock->regbase + TIMER_MATCH_VAL); | 133 | writel(alarm, clock->regbase + TIMER_MATCH_VAL); |
| 96 | now = readl(clock->regbase + TIMER_COUNT_VAL); | ||
| 97 | late = now - alarm; | ||
| 98 | if (late >= (-2 << clock->shift) && late < DGT_HZ*5) { | ||
| 99 | printk(KERN_NOTICE "msm_timer_set_next_event(%lu) clock %s, " | ||
| 100 | "alarm already expired, now %x, alarm %x, late %d\n", | ||
| 101 | cycles, clock->clockevent.name, now, alarm, late); | ||
| 102 | return -ETIME; | ||
| 103 | } | ||
| 104 | return 0; | 134 | return 0; |
| 105 | } | 135 | } |
| 106 | 136 | ||
| 107 | static void msm_timer_set_mode(enum clock_event_mode mode, | 137 | static void msm_timer_set_mode(enum clock_event_mode mode, |
| 108 | struct clock_event_device *evt) | 138 | struct clock_event_device *evt) |
| 109 | { | 139 | { |
| 110 | struct msm_clock *clock = container_of(evt, struct msm_clock, clockevent); | 140 | struct msm_clock *clock = clockevent_to_clock(evt); |
| 141 | |||
| 111 | switch (mode) { | 142 | switch (mode) { |
| 112 | case CLOCK_EVT_MODE_RESUME: | 143 | case CLOCK_EVT_MODE_RESUME: |
| 113 | case CLOCK_EVT_MODE_PERIODIC: | 144 | case CLOCK_EVT_MODE_PERIODIC: |
| @@ -123,7 +154,7 @@ static void msm_timer_set_mode(enum clock_event_mode mode, | |||
| 123 | } | 154 | } |
| 124 | 155 | ||
| 125 | static struct msm_clock msm_clocks[] = { | 156 | static struct msm_clock msm_clocks[] = { |
| 126 | { | 157 | [MSM_CLOCK_GPT] = { |
| 127 | .clockevent = { | 158 | .clockevent = { |
| 128 | .name = "gp_timer", | 159 | .name = "gp_timer", |
| 129 | .features = CLOCK_EVT_FEAT_ONESHOT, | 160 | .features = CLOCK_EVT_FEAT_ONESHOT, |
| @@ -135,7 +166,7 @@ static struct msm_clock msm_clocks[] = { | |||
| 135 | .clocksource = { | 166 | .clocksource = { |
| 136 | .name = "gp_timer", | 167 | .name = "gp_timer", |
| 137 | .rating = 200, | 168 | .rating = 200, |
| 138 | .read = msm_gpt_read, | 169 | .read = msm_read_timer_count, |
| 139 | .mask = CLOCKSOURCE_MASK(32), | 170 | .mask = CLOCKSOURCE_MASK(32), |
| 140 | .flags = CLOCK_SOURCE_IS_CONTINUOUS, | 171 | .flags = CLOCK_SOURCE_IS_CONTINUOUS, |
| 141 | }, | 172 | }, |
| @@ -147,9 +178,12 @@ static struct msm_clock msm_clocks[] = { | |||
| 147 | .irq = INT_GP_TIMER_EXP | 178 | .irq = INT_GP_TIMER_EXP |
| 148 | }, | 179 | }, |
| 149 | .regbase = MSM_GPT_BASE, | 180 | .regbase = MSM_GPT_BASE, |
| 150 | .freq = GPT_HZ | 181 | .freq = GPT_HZ, |
| 182 | .local_counter = MSM_GPT_BASE + TIMER_COUNT_VAL, | ||
| 183 | .global_counter = MSM_GPT_BASE + TIMER_COUNT_VAL + | ||
| 184 | MSM_TMR_GLOBAL, | ||
| 151 | }, | 185 | }, |
| 152 | { | 186 | [MSM_CLOCK_DGT] = { |
| 153 | .clockevent = { | 187 | .clockevent = { |
| 154 | .name = "dg_timer", | 188 | .name = "dg_timer", |
| 155 | .features = CLOCK_EVT_FEAT_ONESHOT, | 189 | .features = CLOCK_EVT_FEAT_ONESHOT, |
| @@ -161,7 +195,7 @@ static struct msm_clock msm_clocks[] = { | |||
| 161 | .clocksource = { | 195 | .clocksource = { |
| 162 | .name = "dg_timer", | 196 | .name = "dg_timer", |
| 163 | .rating = 300, | 197 | .rating = 300, |
| 164 | .read = msm_dgt_read, | 198 | .read = msm_read_timer_count, |
| 165 | .mask = CLOCKSOURCE_MASK((32 - MSM_DGT_SHIFT)), | 199 | .mask = CLOCKSOURCE_MASK((32 - MSM_DGT_SHIFT)), |
| 166 | .flags = CLOCK_SOURCE_IS_CONTINUOUS, | 200 | .flags = CLOCK_SOURCE_IS_CONTINUOUS, |
| 167 | }, | 201 | }, |
| @@ -174,7 +208,10 @@ static struct msm_clock msm_clocks[] = { | |||
| 174 | }, | 208 | }, |
| 175 | .regbase = MSM_DGT_BASE, | 209 | .regbase = MSM_DGT_BASE, |
| 176 | .freq = DGT_HZ >> MSM_DGT_SHIFT, | 210 | .freq = DGT_HZ >> MSM_DGT_SHIFT, |
| 177 | .shift = MSM_DGT_SHIFT | 211 | .shift = MSM_DGT_SHIFT, |
| 212 | .local_counter = MSM_DGT_BASE + TIMER_COUNT_VAL, | ||
| 213 | .global_counter = MSM_DGT_BASE + TIMER_COUNT_VAL + | ||
| 214 | MSM_TMR_GLOBAL, | ||
| 178 | } | 215 | } |
| 179 | }; | 216 | }; |
| 180 | 217 | ||
| @@ -183,7 +220,7 @@ static void __init msm_timer_init(void) | |||
| 183 | int i; | 220 | int i; |
| 184 | int res; | 221 | int res; |
| 185 | 222 | ||
| 186 | #ifdef CONFIG_ARCH_MSM8X60 | 223 | #ifdef CONFIG_ARCH_MSM_SCORPIONMP |
| 187 | writel(DGT_CLK_CTL_DIV_4, MSM_TMR_BASE + DGT_CLK_CTL); | 224 | writel(DGT_CLK_CTL_DIV_4, MSM_TMR_BASE + DGT_CLK_CTL); |
| 188 | #endif | 225 | #endif |
| 189 | 226 | ||
| @@ -217,6 +254,48 @@ static void __init msm_timer_init(void) | |||
| 217 | } | 254 | } |
| 218 | } | 255 | } |
| 219 | 256 | ||
| 257 | #ifdef CONFIG_SMP | ||
| 258 | void __cpuinit local_timer_setup(struct clock_event_device *evt) | ||
| 259 | { | ||
| 260 | struct msm_clock *clock = &msm_clocks[MSM_GLOBAL_TIMER]; | ||
| 261 | |||
| 262 | /* Use existing clock_event for cpu 0 */ | ||
| 263 | if (!smp_processor_id()) | ||
| 264 | return; | ||
| 265 | |||
| 266 | writel(DGT_CLK_CTL_DIV_4, MSM_TMR_BASE + DGT_CLK_CTL); | ||
| 267 | |||
| 268 | if (!local_clock_event) { | ||
| 269 | writel(0, clock->regbase + TIMER_ENABLE); | ||
| 270 | writel(0, clock->regbase + TIMER_CLEAR); | ||
| 271 | writel(~0, clock->regbase + TIMER_MATCH_VAL); | ||
| 272 | } | ||
| 273 | evt->irq = clock->irq.irq; | ||
| 274 | evt->name = "local_timer"; | ||
| 275 | evt->features = CLOCK_EVT_FEAT_ONESHOT; | ||
| 276 | evt->rating = clock->clockevent.rating; | ||
| 277 | evt->set_mode = msm_timer_set_mode; | ||
| 278 | evt->set_next_event = msm_timer_set_next_event; | ||
| 279 | evt->shift = clock->clockevent.shift; | ||
| 280 | evt->mult = div_sc(clock->freq, NSEC_PER_SEC, evt->shift); | ||
| 281 | evt->max_delta_ns = | ||
| 282 | clockevent_delta2ns(0xf0000000 >> clock->shift, evt); | ||
| 283 | evt->min_delta_ns = clockevent_delta2ns(4, evt); | ||
| 284 | |||
| 285 | local_clock_event = evt; | ||
| 286 | |||
| 287 | gic_enable_ppi(clock->irq.irq); | ||
| 288 | |||
| 289 | clockevents_register_device(evt); | ||
| 290 | } | ||
| 291 | |||
| 292 | inline int local_timer_ack(void) | ||
| 293 | { | ||
| 294 | return 1; | ||
| 295 | } | ||
| 296 | |||
| 297 | #endif | ||
| 298 | |||
| 220 | struct sys_timer msm_timer = { | 299 | struct sys_timer msm_timer = { |
| 221 | .init = msm_timer_init | 300 | .init = msm_timer_init |
| 222 | }; | 301 | }; |
