diff options
Diffstat (limited to 'arch/arm/mach-omap2/timer.c')
| -rw-r--r-- | arch/arm/mach-omap2/timer.c | 342 |
1 files changed, 342 insertions, 0 deletions
diff --git a/arch/arm/mach-omap2/timer.c b/arch/arm/mach-omap2/timer.c new file mode 100644 index 000000000000..e9640728239b --- /dev/null +++ b/arch/arm/mach-omap2/timer.c | |||
| @@ -0,0 +1,342 @@ | |||
| 1 | /* | ||
| 2 | * linux/arch/arm/mach-omap2/timer.c | ||
| 3 | * | ||
| 4 | * OMAP2 GP timer support. | ||
| 5 | * | ||
| 6 | * Copyright (C) 2009 Nokia Corporation | ||
| 7 | * | ||
| 8 | * Update to use new clocksource/clockevent layers | ||
| 9 | * Author: Kevin Hilman, MontaVista Software, Inc. <source@mvista.com> | ||
| 10 | * Copyright (C) 2007 MontaVista Software, Inc. | ||
| 11 | * | ||
| 12 | * Original driver: | ||
| 13 | * Copyright (C) 2005 Nokia Corporation | ||
| 14 | * Author: Paul Mundt <paul.mundt@nokia.com> | ||
| 15 | * Juha Yrjölä <juha.yrjola@nokia.com> | ||
| 16 | * OMAP Dual-mode timer framework support by Timo Teras | ||
| 17 | * | ||
| 18 | * Some parts based off of TI's 24xx code: | ||
| 19 | * | ||
| 20 | * Copyright (C) 2004-2009 Texas Instruments, Inc. | ||
| 21 | * | ||
| 22 | * Roughly modelled after the OMAP1 MPU timer code. | ||
| 23 | * Added OMAP4 support - Santosh Shilimkar <santosh.shilimkar@ti.com> | ||
| 24 | * | ||
| 25 | * This file is subject to the terms and conditions of the GNU General Public | ||
| 26 | * License. See the file "COPYING" in the main directory of this archive | ||
| 27 | * for more details. | ||
| 28 | */ | ||
| 29 | #include <linux/init.h> | ||
| 30 | #include <linux/time.h> | ||
| 31 | #include <linux/interrupt.h> | ||
| 32 | #include <linux/err.h> | ||
| 33 | #include <linux/clk.h> | ||
| 34 | #include <linux/delay.h> | ||
| 35 | #include <linux/irq.h> | ||
| 36 | #include <linux/clocksource.h> | ||
| 37 | #include <linux/clockchips.h> | ||
| 38 | |||
| 39 | #include <asm/mach/time.h> | ||
| 40 | #include <plat/dmtimer.h> | ||
| 41 | #include <asm/localtimer.h> | ||
| 42 | #include <asm/sched_clock.h> | ||
| 43 | #include <plat/common.h> | ||
| 44 | #include <plat/omap_hwmod.h> | ||
| 45 | |||
| 46 | /* Parent clocks, eventually these will come from the clock framework */ | ||
| 47 | |||
| 48 | #define OMAP2_MPU_SOURCE "sys_ck" | ||
| 49 | #define OMAP3_MPU_SOURCE OMAP2_MPU_SOURCE | ||
| 50 | #define OMAP4_MPU_SOURCE "sys_clkin_ck" | ||
| 51 | #define OMAP2_32K_SOURCE "func_32k_ck" | ||
| 52 | #define OMAP3_32K_SOURCE "omap_32k_fck" | ||
| 53 | #define OMAP4_32K_SOURCE "sys_32k_ck" | ||
| 54 | |||
| 55 | #ifdef CONFIG_OMAP_32K_TIMER | ||
| 56 | #define OMAP2_CLKEV_SOURCE OMAP2_32K_SOURCE | ||
| 57 | #define OMAP3_CLKEV_SOURCE OMAP3_32K_SOURCE | ||
| 58 | #define OMAP4_CLKEV_SOURCE OMAP4_32K_SOURCE | ||
| 59 | #define OMAP3_SECURE_TIMER 12 | ||
| 60 | #else | ||
| 61 | #define OMAP2_CLKEV_SOURCE OMAP2_MPU_SOURCE | ||
| 62 | #define OMAP3_CLKEV_SOURCE OMAP3_MPU_SOURCE | ||
| 63 | #define OMAP4_CLKEV_SOURCE OMAP4_MPU_SOURCE | ||
| 64 | #define OMAP3_SECURE_TIMER 1 | ||
| 65 | #endif | ||
| 66 | |||
| 67 | /* MAX_GPTIMER_ID: number of GPTIMERs on the chip */ | ||
| 68 | #define MAX_GPTIMER_ID 12 | ||
| 69 | |||
| 70 | u32 sys_timer_reserved; | ||
| 71 | |||
| 72 | /* Clockevent code */ | ||
| 73 | |||
| 74 | static struct omap_dm_timer clkev; | ||
| 75 | static struct clock_event_device clockevent_gpt; | ||
| 76 | |||
| 77 | static irqreturn_t omap2_gp_timer_interrupt(int irq, void *dev_id) | ||
| 78 | { | ||
| 79 | struct clock_event_device *evt = &clockevent_gpt; | ||
| 80 | |||
| 81 | __omap_dm_timer_write_status(clkev.io_base, OMAP_TIMER_INT_OVERFLOW); | ||
| 82 | |||
| 83 | evt->event_handler(evt); | ||
| 84 | return IRQ_HANDLED; | ||
| 85 | } | ||
| 86 | |||
| 87 | static struct irqaction omap2_gp_timer_irq = { | ||
| 88 | .name = "gp timer", | ||
| 89 | .flags = IRQF_DISABLED | IRQF_TIMER | IRQF_IRQPOLL, | ||
| 90 | .handler = omap2_gp_timer_interrupt, | ||
| 91 | }; | ||
| 92 | |||
| 93 | static int omap2_gp_timer_set_next_event(unsigned long cycles, | ||
| 94 | struct clock_event_device *evt) | ||
| 95 | { | ||
| 96 | __omap_dm_timer_load_start(clkev.io_base, OMAP_TIMER_CTRL_ST, | ||
| 97 | 0xffffffff - cycles, 1); | ||
| 98 | |||
| 99 | return 0; | ||
| 100 | } | ||
| 101 | |||
| 102 | static void omap2_gp_timer_set_mode(enum clock_event_mode mode, | ||
| 103 | struct clock_event_device *evt) | ||
| 104 | { | ||
| 105 | u32 period; | ||
| 106 | |||
| 107 | __omap_dm_timer_stop(clkev.io_base, 1, clkev.rate); | ||
| 108 | |||
| 109 | switch (mode) { | ||
| 110 | case CLOCK_EVT_MODE_PERIODIC: | ||
| 111 | period = clkev.rate / HZ; | ||
| 112 | period -= 1; | ||
| 113 | /* Looks like we need to first set the load value separately */ | ||
| 114 | __omap_dm_timer_write(clkev.io_base, OMAP_TIMER_LOAD_REG, | ||
| 115 | 0xffffffff - period, 1); | ||
| 116 | __omap_dm_timer_load_start(clkev.io_base, | ||
| 117 | OMAP_TIMER_CTRL_AR | OMAP_TIMER_CTRL_ST, | ||
| 118 | 0xffffffff - period, 1); | ||
| 119 | break; | ||
| 120 | case CLOCK_EVT_MODE_ONESHOT: | ||
| 121 | break; | ||
| 122 | case CLOCK_EVT_MODE_UNUSED: | ||
| 123 | case CLOCK_EVT_MODE_SHUTDOWN: | ||
| 124 | case CLOCK_EVT_MODE_RESUME: | ||
| 125 | break; | ||
| 126 | } | ||
| 127 | } | ||
| 128 | |||
| 129 | static struct clock_event_device clockevent_gpt = { | ||
| 130 | .name = "gp timer", | ||
| 131 | .features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT, | ||
| 132 | .shift = 32, | ||
| 133 | .set_next_event = omap2_gp_timer_set_next_event, | ||
| 134 | .set_mode = omap2_gp_timer_set_mode, | ||
| 135 | }; | ||
| 136 | |||
| 137 | static int __init omap_dm_timer_init_one(struct omap_dm_timer *timer, | ||
| 138 | int gptimer_id, | ||
| 139 | const char *fck_source) | ||
| 140 | { | ||
| 141 | char name[10]; /* 10 = sizeof("gptXX_Xck0") */ | ||
| 142 | struct omap_hwmod *oh; | ||
| 143 | size_t size; | ||
| 144 | int res = 0; | ||
| 145 | |||
| 146 | sprintf(name, "timer%d", gptimer_id); | ||
| 147 | omap_hwmod_setup_one(name); | ||
| 148 | oh = omap_hwmod_lookup(name); | ||
| 149 | if (!oh) | ||
| 150 | return -ENODEV; | ||
| 151 | |||
| 152 | timer->irq = oh->mpu_irqs[0].irq; | ||
| 153 | timer->phys_base = oh->slaves[0]->addr->pa_start; | ||
| 154 | size = oh->slaves[0]->addr->pa_end - timer->phys_base; | ||
| 155 | |||
| 156 | /* Static mapping, never released */ | ||
| 157 | timer->io_base = ioremap(timer->phys_base, size); | ||
| 158 | if (!timer->io_base) | ||
| 159 | return -ENXIO; | ||
| 160 | |||
| 161 | /* After the dmtimer is using hwmod these clocks won't be needed */ | ||
| 162 | sprintf(name, "gpt%d_fck", gptimer_id); | ||
| 163 | timer->fclk = clk_get(NULL, name); | ||
| 164 | if (IS_ERR(timer->fclk)) | ||
| 165 | return -ENODEV; | ||
| 166 | |||
| 167 | sprintf(name, "gpt%d_ick", gptimer_id); | ||
| 168 | timer->iclk = clk_get(NULL, name); | ||
| 169 | if (IS_ERR(timer->iclk)) { | ||
| 170 | clk_put(timer->fclk); | ||
| 171 | return -ENODEV; | ||
| 172 | } | ||
| 173 | |||
| 174 | omap_hwmod_enable(oh); | ||
| 175 | |||
| 176 | sys_timer_reserved |= (1 << (gptimer_id - 1)); | ||
| 177 | |||
| 178 | if (gptimer_id != 12) { | ||
| 179 | struct clk *src; | ||
| 180 | |||
| 181 | src = clk_get(NULL, fck_source); | ||
| 182 | if (IS_ERR(src)) { | ||
| 183 | res = -EINVAL; | ||
| 184 | } else { | ||
| 185 | res = __omap_dm_timer_set_source(timer->fclk, src); | ||
| 186 | if (IS_ERR_VALUE(res)) | ||
| 187 | pr_warning("%s: timer%i cannot set source\n", | ||
| 188 | __func__, gptimer_id); | ||
| 189 | clk_put(src); | ||
| 190 | } | ||
| 191 | } | ||
| 192 | __omap_dm_timer_reset(timer->io_base, 1, 1); | ||
| 193 | timer->posted = 1; | ||
| 194 | |||
| 195 | timer->rate = clk_get_rate(timer->fclk); | ||
| 196 | |||
| 197 | timer->reserved = 1; | ||
| 198 | |||
| 199 | return res; | ||
| 200 | } | ||
| 201 | |||
| 202 | static void __init omap2_gp_clockevent_init(int gptimer_id, | ||
| 203 | const char *fck_source) | ||
| 204 | { | ||
| 205 | int res; | ||
| 206 | |||
| 207 | res = omap_dm_timer_init_one(&clkev, gptimer_id, fck_source); | ||
| 208 | BUG_ON(res); | ||
| 209 | |||
| 210 | omap2_gp_timer_irq.dev_id = (void *)&clkev; | ||
| 211 | setup_irq(clkev.irq, &omap2_gp_timer_irq); | ||
| 212 | |||
| 213 | __omap_dm_timer_int_enable(clkev.io_base, OMAP_TIMER_INT_OVERFLOW); | ||
| 214 | |||
| 215 | clockevent_gpt.mult = div_sc(clkev.rate, NSEC_PER_SEC, | ||
| 216 | clockevent_gpt.shift); | ||
| 217 | clockevent_gpt.max_delta_ns = | ||
| 218 | clockevent_delta2ns(0xffffffff, &clockevent_gpt); | ||
| 219 | clockevent_gpt.min_delta_ns = | ||
| 220 | clockevent_delta2ns(3, &clockevent_gpt); | ||
| 221 | /* Timer internal resynch latency. */ | ||
| 222 | |||
| 223 | clockevent_gpt.cpumask = cpumask_of(0); | ||
| 224 | clockevents_register_device(&clockevent_gpt); | ||
| 225 | |||
| 226 | pr_info("OMAP clockevent source: GPTIMER%d at %lu Hz\n", | ||
| 227 | gptimer_id, clkev.rate); | ||
| 228 | } | ||
| 229 | |||
| 230 | /* Clocksource code */ | ||
| 231 | |||
| 232 | #ifdef CONFIG_OMAP_32K_TIMER | ||
| 233 | /* | ||
| 234 | * When 32k-timer is enabled, don't use GPTimer for clocksource | ||
| 235 | * instead, just leave default clocksource which uses the 32k | ||
| 236 | * sync counter. See clocksource setup in plat-omap/counter_32k.c | ||
| 237 | */ | ||
| 238 | |||
| 239 | static void __init omap2_gp_clocksource_init(int unused, const char *dummy) | ||
| 240 | { | ||
| 241 | omap_init_clocksource_32k(); | ||
| 242 | } | ||
| 243 | |||
| 244 | #else | ||
| 245 | |||
| 246 | static struct omap_dm_timer clksrc; | ||
| 247 | |||
| 248 | /* | ||
| 249 | * clocksource | ||
| 250 | */ | ||
| 251 | static DEFINE_CLOCK_DATA(cd); | ||
| 252 | static cycle_t clocksource_read_cycles(struct clocksource *cs) | ||
| 253 | { | ||
| 254 | return (cycle_t)__omap_dm_timer_read_counter(clksrc.io_base, 1); | ||
| 255 | } | ||
| 256 | |||
| 257 | static struct clocksource clocksource_gpt = { | ||
| 258 | .name = "gp timer", | ||
| 259 | .rating = 300, | ||
| 260 | .read = clocksource_read_cycles, | ||
| 261 | .mask = CLOCKSOURCE_MASK(32), | ||
| 262 | .flags = CLOCK_SOURCE_IS_CONTINUOUS, | ||
| 263 | }; | ||
| 264 | |||
| 265 | static void notrace dmtimer_update_sched_clock(void) | ||
| 266 | { | ||
| 267 | u32 cyc; | ||
| 268 | |||
| 269 | cyc = __omap_dm_timer_read_counter(clksrc.io_base, 1); | ||
| 270 | |||
| 271 | update_sched_clock(&cd, cyc, (u32)~0); | ||
| 272 | } | ||
| 273 | |||
| 274 | unsigned long long notrace sched_clock(void) | ||
| 275 | { | ||
| 276 | u32 cyc = 0; | ||
| 277 | |||
| 278 | if (clksrc.reserved) | ||
| 279 | cyc = __omap_dm_timer_read_counter(clksrc.io_base, 1); | ||
| 280 | |||
| 281 | return cyc_to_sched_clock(&cd, cyc, (u32)~0); | ||
| 282 | } | ||
| 283 | |||
| 284 | /* Setup free-running counter for clocksource */ | ||
| 285 | static void __init omap2_gp_clocksource_init(int gptimer_id, | ||
| 286 | const char *fck_source) | ||
| 287 | { | ||
| 288 | int res; | ||
| 289 | |||
| 290 | res = omap_dm_timer_init_one(&clksrc, gptimer_id, fck_source); | ||
| 291 | BUG_ON(res); | ||
| 292 | |||
| 293 | pr_info("OMAP clocksource: GPTIMER%d at %lu Hz\n", | ||
| 294 | gptimer_id, clksrc.rate); | ||
| 295 | |||
| 296 | __omap_dm_timer_load_start(clksrc.io_base, OMAP_TIMER_CTRL_ST, 0, 1); | ||
| 297 | init_sched_clock(&cd, dmtimer_update_sched_clock, 32, clksrc.rate); | ||
| 298 | |||
| 299 | if (clocksource_register_hz(&clocksource_gpt, clksrc.rate)) | ||
| 300 | pr_err("Could not register clocksource %s\n", | ||
| 301 | clocksource_gpt.name); | ||
| 302 | } | ||
| 303 | #endif | ||
| 304 | |||
| 305 | #define OMAP_SYS_TIMER_INIT(name, clkev_nr, clkev_src, \ | ||
| 306 | clksrc_nr, clksrc_src) \ | ||
| 307 | static void __init omap##name##_timer_init(void) \ | ||
| 308 | { \ | ||
| 309 | omap2_gp_clockevent_init((clkev_nr), clkev_src); \ | ||
| 310 | omap2_gp_clocksource_init((clksrc_nr), clksrc_src); \ | ||
| 311 | } | ||
| 312 | |||
| 313 | #define OMAP_SYS_TIMER(name) \ | ||
| 314 | struct sys_timer omap##name##_timer = { \ | ||
| 315 | .init = omap##name##_timer_init, \ | ||
| 316 | }; | ||
| 317 | |||
| 318 | #ifdef CONFIG_ARCH_OMAP2 | ||
| 319 | OMAP_SYS_TIMER_INIT(2, 1, OMAP2_CLKEV_SOURCE, 2, OMAP2_MPU_SOURCE) | ||
| 320 | OMAP_SYS_TIMER(2) | ||
| 321 | #endif | ||
| 322 | |||
| 323 | #ifdef CONFIG_ARCH_OMAP3 | ||
| 324 | OMAP_SYS_TIMER_INIT(3, 1, OMAP3_CLKEV_SOURCE, 2, OMAP3_MPU_SOURCE) | ||
| 325 | OMAP_SYS_TIMER(3) | ||
| 326 | OMAP_SYS_TIMER_INIT(3_secure, OMAP3_SECURE_TIMER, OMAP3_CLKEV_SOURCE, | ||
| 327 | 2, OMAP3_MPU_SOURCE) | ||
| 328 | OMAP_SYS_TIMER(3_secure) | ||
| 329 | #endif | ||
| 330 | |||
| 331 | #ifdef CONFIG_ARCH_OMAP4 | ||
| 332 | static void __init omap4_timer_init(void) | ||
| 333 | { | ||
| 334 | #ifdef CONFIG_LOCAL_TIMERS | ||
| 335 | twd_base = ioremap(OMAP44XX_LOCAL_TWD_BASE, SZ_256); | ||
| 336 | BUG_ON(!twd_base); | ||
| 337 | #endif | ||
| 338 | omap2_gp_clockevent_init(1, OMAP4_CLKEV_SOURCE); | ||
| 339 | omap2_gp_clocksource_init(2, OMAP4_MPU_SOURCE); | ||
| 340 | } | ||
| 341 | OMAP_SYS_TIMER(4) | ||
| 342 | #endif | ||
