| 1 | --- /dev/null |
| 2 | +++ b/arch/arm/plat-s3c24xx/time.c |
| 3 | @@ -0,0 +1,480 @@ |
| 4 | +/* linux/arch/arm/plat-s3c24xx/time.c |
| 5 | + * |
| 6 | + * Copyright (C) 2003-2005 Simtec Electronics |
| 7 | + * Ben Dooks, <ben@simtec.co.uk> |
| 8 | + * |
| 9 | + * dyn_tick support by Andrzej Zaborowski based on omap_dyn_tick_timer. |
| 10 | + * |
| 11 | + * This program is free software; you can redistribute it and/or modify |
| 12 | + * it under the terms of the GNU General Public License as published by |
| 13 | + * the Free Software Foundation; either version 2 of the License, or |
| 14 | + * (at your option) any later version. |
| 15 | + * |
| 16 | + * This program is distributed in the hope that it will be useful, |
| 17 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 18 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| 19 | + * GNU General Public License for more details. |
| 20 | + * |
| 21 | + * You should have received a copy of the GNU General Public License |
| 22 | + * along with this program; if not, write to the Free Software |
| 23 | + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
| 24 | + */ |
| 25 | + |
| 26 | +#include <linux/kernel.h> |
| 27 | +#include <linux/sched.h> |
| 28 | +#include <linux/init.h> |
| 29 | +#include <linux/interrupt.h> |
| 30 | +#include <linux/irq.h> |
| 31 | +#include <linux/err.h> |
| 32 | +#include <linux/clk.h> |
| 33 | + |
| 34 | +#include <asm/system.h> |
| 35 | +#include <asm/leds.h> |
| 36 | +#include <asm/mach-types.h> |
| 37 | + |
| 38 | +#include <asm/io.h> |
| 39 | +#include <asm/irq.h> |
| 40 | +#include <mach/map.h> |
| 41 | +#include <asm/plat-s3c/regs-timer.h> |
| 42 | +#include <mach/regs-irq.h> |
| 43 | +#include <asm/mach/time.h> |
| 44 | + |
| 45 | +#include <asm/plat-s3c24xx/clock.h> |
| 46 | +#include <asm/plat-s3c24xx/cpu.h> |
| 47 | + |
| 48 | +static unsigned long timer_startval; |
| 49 | +static unsigned long timer_usec_ticks; |
| 50 | +static struct work_struct resume_work; |
| 51 | + |
| 52 | +unsigned long pclk; |
| 53 | +struct clk *clk; |
| 54 | + |
| 55 | +#define TIMER_USEC_SHIFT 16 |
| 56 | + |
| 57 | +/* we use the shifted arithmetic to work out the ratio of timer ticks |
| 58 | + * to usecs, as often the peripheral clock is not a nice even multiple |
| 59 | + * of 1MHz. |
| 60 | + * |
| 61 | + * shift of 14 and 15 are too low for the 12MHz, 16 seems to be ok |
| 62 | + * for the current HZ value of 200 without producing overflows. |
| 63 | + * |
| 64 | + * Original patch by Dimitry Andric, updated by Ben Dooks |
| 65 | +*/ |
| 66 | + |
| 67 | + |
| 68 | +/* timer_mask_usec_ticks |
| 69 | + * |
| 70 | + * given a clock and divisor, make the value to pass into timer_ticks_to_usec |
| 71 | + * to scale the ticks into usecs |
| 72 | +*/ |
| 73 | + |
| 74 | +static inline unsigned long |
| 75 | +timer_mask_usec_ticks(unsigned long scaler, unsigned long pclk) |
| 76 | +{ |
| 77 | + unsigned long den = pclk / 1000; |
| 78 | + |
| 79 | + return ((1000 << TIMER_USEC_SHIFT) * scaler + (den >> 1)) / den; |
| 80 | +} |
| 81 | + |
| 82 | +/* timer_ticks_to_usec |
| 83 | + * |
| 84 | + * convert timer ticks to usec. |
| 85 | +*/ |
| 86 | + |
| 87 | +static inline unsigned long timer_ticks_to_usec(unsigned long ticks) |
| 88 | +{ |
| 89 | + unsigned long res; |
| 90 | + |
| 91 | + res = ticks * timer_usec_ticks; |
| 92 | + res += 1 << (TIMER_USEC_SHIFT - 4); /* round up slightly */ |
| 93 | + |
| 94 | + return res >> TIMER_USEC_SHIFT; |
| 95 | +} |
| 96 | + |
| 97 | +/*** |
| 98 | + * Returns microsecond since last clock interrupt. Note that interrupts |
| 99 | + * will have been disabled by do_gettimeoffset() |
| 100 | + * IRQs are disabled before entering here from do_gettimeofday() |
| 101 | + */ |
| 102 | + |
| 103 | +#define SRCPND_TIMER4 (1<<(IRQ_TIMER4 - IRQ_EINT0)) |
| 104 | + |
| 105 | +unsigned long s3c2410_gettimeoffset (void) |
| 106 | +{ |
| 107 | + unsigned long tdone; |
| 108 | + unsigned long irqpend; |
| 109 | + unsigned long tval; |
| 110 | + |
| 111 | + /* work out how many ticks have gone since last timer interrupt */ |
| 112 | + |
| 113 | + tval = __raw_readl(S3C2410_TCNTO(4)); |
| 114 | + tdone = timer_startval - tval; |
| 115 | + |
| 116 | + /* check to see if there is an interrupt pending */ |
| 117 | + |
| 118 | + irqpend = __raw_readl(S3C2410_SRCPND); |
| 119 | + if (irqpend & SRCPND_TIMER4) { |
| 120 | + /* re-read the timer, and try and fix up for the missed |
| 121 | + * interrupt. Note, the interrupt may go off before the |
| 122 | + * timer has re-loaded from wrapping. |
| 123 | + */ |
| 124 | + |
| 125 | + tval = __raw_readl(S3C2410_TCNTO(4)); |
| 126 | + tdone = timer_startval - tval; |
| 127 | + |
| 128 | + if (tval != 0) |
| 129 | + tdone += timer_startval; |
| 130 | + } |
| 131 | + |
| 132 | + return timer_ticks_to_usec(tdone); |
| 133 | +} |
| 134 | + |
| 135 | + |
| 136 | +/* |
| 137 | + * IRQ handler for the timer |
| 138 | + */ |
| 139 | +static irqreturn_t |
| 140 | +s3c2410_timer_interrupt(int irq, void *dev_id) |
| 141 | +{ |
| 142 | + timer_tick(); |
| 143 | + return IRQ_HANDLED; |
| 144 | +} |
| 145 | + |
| 146 | +static struct irqaction s3c2410_timer_irq = { |
| 147 | + .name = "S3C2410 Timer Tick", |
| 148 | + .flags = IRQF_DISABLED | IRQF_TIMER | IRQF_IRQPOLL, |
| 149 | + .handler = s3c2410_timer_interrupt, |
| 150 | +}; |
| 151 | + |
| 152 | +#define use_tclk1_12() ( \ |
| 153 | + machine_is_bast() || \ |
| 154 | + machine_is_vr1000() || \ |
| 155 | + machine_is_anubis() || \ |
| 156 | + machine_is_osiris() ) |
| 157 | + |
| 158 | +/* |
| 159 | + * Set up timer interrupt, and return the current time in seconds. |
| 160 | + * |
| 161 | + * Currently we only use timer4, as it is the only timer which has no |
| 162 | + * other function that can be exploited externally |
| 163 | + */ |
| 164 | +static void s3c2410_timer_setup (void) |
| 165 | +{ |
| 166 | + unsigned long tcon; |
| 167 | + unsigned long tcnt; |
| 168 | + unsigned long tcfg1; |
| 169 | + unsigned long tcfg0; |
| 170 | + |
| 171 | + tcnt = 0xffff; /* default value for tcnt */ |
| 172 | + |
| 173 | + /* read the current timer configuration bits */ |
| 174 | + |
| 175 | + tcon = __raw_readl(S3C2410_TCON); |
| 176 | + tcfg1 = __raw_readl(S3C2410_TCFG1); |
| 177 | + tcfg0 = __raw_readl(S3C2410_TCFG0); |
| 178 | + |
| 179 | + /* configure the system for whichever machine is in use */ |
| 180 | + |
| 181 | + if (use_tclk1_12()) { |
| 182 | + /* timer is at 12MHz, scaler is 1 */ |
| 183 | + timer_usec_ticks = timer_mask_usec_ticks(1, 12000000); |
| 184 | + tcnt = 12000000 / HZ; |
| 185 | + |
| 186 | + tcfg1 &= ~S3C2410_TCFG1_MUX4_MASK; |
| 187 | + tcfg1 |= S3C2410_TCFG1_MUX4_TCLK1; |
| 188 | + } else { |
| 189 | + /* since values around 50 to |
| 190 | + * 70MHz are not values we can directly generate the timer |
| 191 | + * value from, we need to pre-scale and divide before using it. |
| 192 | + * |
| 193 | + * for instance, using 50.7MHz and dividing by 6 gives 8.45MHz |
| 194 | + * (8.45 ticks per usec) |
| 195 | + */ |
| 196 | + |
| 197 | + /* configure clock tick */ |
| 198 | + timer_usec_ticks = timer_mask_usec_ticks(6, pclk); |
| 199 | + printk("timer_usec_ticks = %lu\n", timer_usec_ticks); |
| 200 | + |
| 201 | + tcfg1 &= ~S3C2410_TCFG1_MUX4_MASK; |
| 202 | + tcfg1 |= S3C2410_TCFG1_MUX4_DIV2; |
| 203 | + |
| 204 | + tcfg0 &= ~S3C2410_TCFG_PRESCALER1_MASK; |
| 205 | + tcfg0 |= ((6 - 1) / 2) << S3C2410_TCFG_PRESCALER1_SHIFT; |
| 206 | + |
| 207 | + tcnt = (pclk / 6) / HZ; |
| 208 | + } |
| 209 | + |
| 210 | + /* timers reload after counting zero, so reduce the count by 1 */ |
| 211 | + |
| 212 | + tcnt--; |
| 213 | + |
| 214 | + printk("timer tcon=%08lx, tcnt %04lx, tcfg %08lx,%08lx, usec %08lx\n", |
| 215 | + tcon, tcnt, tcfg0, tcfg1, timer_usec_ticks); |
| 216 | + |
| 217 | + /* check to see if timer is within 16bit range... */ |
| 218 | + if (tcnt > 0xffff) { |
| 219 | + panic("setup_timer: HZ is too small, cannot configure timer!"); |
| 220 | + return; |
| 221 | + } |
| 222 | + |
| 223 | + __raw_writel(tcfg1, S3C2410_TCFG1); |
| 224 | + __raw_writel(tcfg0, S3C2410_TCFG0); |
| 225 | + |
| 226 | + timer_startval = tcnt; |
| 227 | + __raw_writel(tcnt, S3C2410_TCNTB(4)); |
| 228 | + |
| 229 | + /* ensure timer is stopped... */ |
| 230 | + |
| 231 | + tcon &= ~(7<<20); |
| 232 | + tcon |= S3C2410_TCON_T4RELOAD; |
| 233 | + tcon |= S3C2410_TCON_T4MANUALUPD; |
| 234 | + |
| 235 | + __raw_writel(tcon, S3C2410_TCON); |
| 236 | + __raw_writel(tcnt, S3C2410_TCNTB(4)); |
| 237 | + __raw_writel(tcnt, S3C2410_TCMPB(4)); |
| 238 | + |
| 239 | + /* start the timer running */ |
| 240 | + tcon |= S3C2410_TCON_T4START; |
| 241 | + tcon &= ~S3C2410_TCON_T4MANUALUPD; |
| 242 | + __raw_writel(tcon, S3C2410_TCON); |
| 243 | + |
| 244 | + __raw_writel(__raw_readl(S3C2410_INTMSK) & (~(1UL << 14)), |
| 245 | + S3C2410_INTMSK); |
| 246 | + |
| 247 | +} |
| 248 | + |
| 249 | +struct sys_timer s3c24xx_timer; |
| 250 | +static void timer_resume_work(struct work_struct *work) |
| 251 | +{ |
| 252 | + clk_enable(clk); |
| 253 | + |
| 254 | +#ifdef CONFIG_NO_IDLE_HZ |
| 255 | + if (s3c24xx_timer.dyn_tick->state & DYN_TICK_ENABLED) |
| 256 | + s3c24xx_timer.dyn_tick->enable(); |
| 257 | + else |
| 258 | +#endif |
| 259 | + s3c2410_timer_setup(); |
| 260 | +} |
| 261 | + |
| 262 | +static void __init s3c2410_timer_init (void) |
| 263 | +{ |
| 264 | + if (!use_tclk1_12()) { |
| 265 | + /* for the h1940 (and others), we use the pclk from the core |
| 266 | + * to generate the timer values. |
| 267 | + */ |
| 268 | + |
| 269 | + /* this is used as default if no other timer can be found */ |
| 270 | + clk = clk_get(NULL, "timers"); |
| 271 | + if (IS_ERR(clk)) |
| 272 | + panic("failed to get clock for system timer"); |
| 273 | + |
| 274 | + clk_enable(clk); |
| 275 | + |
| 276 | + pclk = clk_get_rate(clk); |
| 277 | + printk("pclk = %lu\n", pclk); |
| 278 | + } |
| 279 | + |
| 280 | + INIT_WORK(&resume_work, timer_resume_work); |
| 281 | + s3c2410_timer_setup(); |
| 282 | + setup_irq(IRQ_TIMER4, &s3c2410_timer_irq); |
| 283 | +} |
| 284 | + |
| 285 | +static void s3c2410_timer_resume_work(struct work_struct *work) |
| 286 | +{ |
| 287 | + s3c2410_timer_setup(); |
| 288 | +} |
| 289 | + |
| 290 | +static void s3c2410_timer_resume(void) |
| 291 | +{ |
| 292 | + static DECLARE_WORK(work, s3c2410_timer_resume_work); |
| 293 | + int res; |
| 294 | + |
| 295 | + res = schedule_work(&work); |
| 296 | + if (!res) |
| 297 | + printk(KERN_ERR |
| 298 | + "s3c2410_timer_resume_work already queued ???\n"); |
| 299 | +} |
| 300 | + |
| 301 | +#ifdef CONFIG_NO_IDLE_HZ |
| 302 | +/* |
| 303 | + * We'll set a constant prescaler so we don't have to bother setting it |
| 304 | + * when reprogramming and so that we avoid costly divisions. |
| 305 | + * |
| 306 | + * (2 * HZ) << INPUT_FREQ_SHIFT is the desired frequency after prescaler. |
| 307 | + * At HZ == 200, HZ * 1024 should work for PCLKs of up to ~53.5 MHz. |
| 308 | + */ |
| 309 | +#define INPUT_FREQ_SHIFT 9 |
| 310 | + |
| 311 | +static int ticks_last; |
| 312 | +static int ticks_left; |
| 313 | +static uint32_t tcnto_last; |
| 314 | + |
| 315 | +static inline int s3c24xx_timer_read(void) |
| 316 | +{ |
| 317 | + uint32_t tcnto = __raw_readl(S3C2410_TCNTO(4)); |
| 318 | + |
| 319 | + /* |
| 320 | + * WARNING: sometimes we get called before TCNTB has been |
| 321 | + * loaded into the counter and TCNTO then returns its previous |
| 322 | + * value and kill us, so don't do anything before counter is |
| 323 | + * reloaded. |
| 324 | + */ |
| 325 | + if (unlikely(tcnto == tcnto_last)) |
| 326 | + return ticks_last; |
| 327 | + |
| 328 | + tcnto_last = -1; |
| 329 | + return tcnto << |
| 330 | + ((__raw_readl(S3C2410_TCFG1) >> S3C2410_TCFG1_MUX4_SHIFT) & 3); |
| 331 | +} |
| 332 | + |
| 333 | +static inline void s3c24xx_timer_program(int ticks) |
| 334 | +{ |
| 335 | + uint32_t tcon = __raw_readl(S3C2410_TCON) & ~(7 << 20); |
| 336 | + uint32_t tcfg1 = __raw_readl(S3C2410_TCFG1) & ~S3C2410_TCFG1_MUX4_MASK; |
| 337 | + |
| 338 | + /* Just make sure the timer is stopped. */ |
| 339 | + __raw_writel(tcon, S3C2410_TCON); |
| 340 | + |
| 341 | + /* TODO: add likely()ies / unlikely()ies */ |
| 342 | + if (ticks >> 18) { |
| 343 | + ticks_last = min(ticks, 0xffff << 3); |
| 344 | + ticks_left = ticks - ticks_last; |
| 345 | + __raw_writel(tcfg1 | S3C2410_TCFG1_MUX4_DIV16, S3C2410_TCFG1); |
| 346 | + __raw_writel(ticks_last >> 3, S3C2410_TCNTB(4)); |
| 347 | + } else if (ticks >> 17) { |
| 348 | + ticks_last = ticks; |
| 349 | + ticks_left = 0; |
| 350 | + __raw_writel(tcfg1 | S3C2410_TCFG1_MUX4_DIV8, S3C2410_TCFG1); |
| 351 | + __raw_writel(ticks_last >> 2, S3C2410_TCNTB(4)); |
| 352 | + } else if (ticks >> 16) { |
| 353 | + ticks_last = ticks; |
| 354 | + ticks_left = 0; |
| 355 | + __raw_writel(tcfg1 | S3C2410_TCFG1_MUX4_DIV4, S3C2410_TCFG1); |
| 356 | + __raw_writel(ticks_last >> 1, S3C2410_TCNTB(4)); |
| 357 | + } else { |
| 358 | + ticks_last = ticks; |
| 359 | + ticks_left = 0; |
| 360 | + __raw_writel(tcfg1 | S3C2410_TCFG1_MUX4_DIV2, S3C2410_TCFG1); |
| 361 | + __raw_writel(ticks_last >> 0, S3C2410_TCNTB(4)); |
| 362 | + } |
| 363 | + |
| 364 | + tcnto_last = __raw_readl(S3C2410_TCNTO(4)); |
| 365 | + __raw_writel(tcon | S3C2410_TCON_T4MANUALUPD, |
| 366 | + S3C2410_TCON); |
| 367 | + __raw_writel(tcon | S3C2410_TCON_T4START, |
| 368 | + S3C2410_TCON); |
| 369 | +} |
| 370 | + |
| 371 | +/* |
| 372 | + * If we have already waited all the time we were supposed to wait, |
| 373 | + * kick the timer, setting the longest allowed timeout value just |
| 374 | + * for time-keeping. |
| 375 | + */ |
| 376 | +static inline void s3c24xx_timer_program_idle(void) |
| 377 | +{ |
| 378 | + s3c24xx_timer_program(0xffff << 3); |
| 379 | +} |
| 380 | + |
| 381 | +static inline void s3c24xx_timer_update(int restart) |
| 382 | +{ |
| 383 | + int ticks_cur = s3c24xx_timer_read(); |
| 384 | + int jiffies_elapsed = (ticks_last - ticks_cur) >> INPUT_FREQ_SHIFT; |
| 385 | + int subjiffy = ticks_last - (jiffies_elapsed << INPUT_FREQ_SHIFT); |
| 386 | + |
| 387 | + if (restart) { |
| 388 | + if (ticks_left >= (1 << INPUT_FREQ_SHIFT)) |
| 389 | + s3c24xx_timer_program(ticks_left); |
| 390 | + else |
| 391 | + s3c24xx_timer_program_idle(); |
| 392 | + ticks_last += subjiffy; |
| 393 | + } else |
| 394 | + ticks_last = subjiffy; |
| 395 | + |
| 396 | + while (jiffies_elapsed --) |
| 397 | + timer_tick(); |
| 398 | +} |
| 399 | + |
| 400 | +/* Called when the timer expires. */ |
| 401 | +static irqreturn_t s3c24xx_timer_handler(int irq, void *dev_id) |
| 402 | +{ |
| 403 | + tcnto_last = -1; |
| 404 | + s3c24xx_timer_update(1); |
| 405 | + |
| 406 | + return IRQ_HANDLED; |
| 407 | +} |
| 408 | + |
| 409 | +/* Called to update jiffies with time elapsed. */ |
| 410 | +static irqreturn_t s3c24xx_timer_handler_dyn_tick(int irq, void *dev_id) |
| 411 | +{ |
| 412 | + s3c24xx_timer_update(0); |
| 413 | + |
| 414 | + return IRQ_HANDLED; |
| 415 | +} |
| 416 | + |
| 417 | +/* |
| 418 | + * Programs the next timer interrupt needed. Called when dynamic tick is |
| 419 | + * enabled, and to reprogram the ticks to skip from pm_idle. The CPU goes |
| 420 | + * to sleep directly after this. |
| 421 | + */ |
| 422 | +static void s3c24xx_timer_reprogram_dyn_tick(unsigned long next_jiffies) |
| 423 | +{ |
| 424 | + int subjiffy_left = ticks_last - s3c24xx_timer_read(); |
| 425 | + |
| 426 | + s3c24xx_timer_program(max((int) next_jiffies, 1) << INPUT_FREQ_SHIFT); |
| 427 | + ticks_last += subjiffy_left; |
| 428 | +} |
| 429 | + |
| 430 | +static unsigned long s3c24xx_timer_offset_dyn_tick(void) |
| 431 | +{ |
| 432 | + /* TODO */ |
| 433 | + return 0; |
| 434 | +} |
| 435 | + |
| 436 | +static int s3c24xx_timer_enable_dyn_tick(void) |
| 437 | +{ |
| 438 | + /* Set our constant prescaler. */ |
| 439 | + uint32_t tcfg0 = __raw_readl(S3C2410_TCFG0); |
| 440 | + int prescaler = |
| 441 | + max(min(256, (int) pclk / (HZ << (INPUT_FREQ_SHIFT + 1))), 1); |
| 442 | + |
| 443 | + tcfg0 &= ~S3C2410_TCFG_PRESCALER1_MASK; |
| 444 | + tcfg0 |= (prescaler - 1) << S3C2410_TCFG_PRESCALER1_SHIFT; |
| 445 | + __raw_writel(tcfg0, S3C2410_TCFG0); |
| 446 | + |
| 447 | + /* Override handlers. */ |
| 448 | + s3c2410_timer_irq.handler = s3c24xx_timer_handler; |
| 449 | + s3c24xx_timer.offset = s3c24xx_timer_offset_dyn_tick; |
| 450 | + |
| 451 | + printk(KERN_INFO "dyn_tick enabled on s3c24xx timer 4, " |
| 452 | + "%li Hz pclk with prescaler %i\n", pclk, prescaler); |
| 453 | + |
| 454 | + s3c24xx_timer_program_idle(); |
| 455 | + |
| 456 | + return 0; |
| 457 | +} |
| 458 | + |
| 459 | +static int s3c24xx_timer_disable_dyn_tick(void) |
| 460 | +{ |
| 461 | + s3c2410_timer_irq.handler = s3c2410_timer_interrupt; |
| 462 | + s3c24xx_timer.offset = s3c2410_gettimeoffset; |
| 463 | + s3c2410_timer_setup(); |
| 464 | + |
| 465 | + return 0; |
| 466 | +} |
| 467 | + |
| 468 | +static struct dyn_tick_timer s3c24xx_dyn_tick_timer = { |
| 469 | + .enable = s3c24xx_timer_enable_dyn_tick, |
| 470 | + .disable = s3c24xx_timer_disable_dyn_tick, |
| 471 | + .reprogram = s3c24xx_timer_reprogram_dyn_tick, |
| 472 | + .handler = s3c24xx_timer_handler_dyn_tick, |
| 473 | +}; |
| 474 | +#endif /* CONFIG_NO_IDLE_HZ */ |
| 475 | + |
| 476 | +struct sys_timer s3c24xx_timer = { |
| 477 | + .init = s3c2410_timer_init, |
| 478 | + .offset = s3c2410_gettimeoffset, |
| 479 | + .resume = s3c2410_timer_resume, |
| 480 | +#ifdef CONFIG_NO_IDLE_HZ |
| 481 | + .dyn_tick = &s3c24xx_dyn_tick_timer, |
| 482 | +#endif |
| 483 | +}; |
| 484 | |