| Chris Zankel | 5a0015d | 2005-06-23 22:01:16 -0700 | [diff] [blame] | 1 | /* | 
|  | 2 | * arch/xtensa/kernel/time.c | 
|  | 3 | * | 
|  | 4 | * Timer and clock support. | 
|  | 5 | * | 
|  | 6 | * This file is subject to the terms and conditions of the GNU General Public | 
|  | 7 | * License.  See the file "COPYING" in the main directory of this archive | 
|  | 8 | * for more details. | 
|  | 9 | * | 
|  | 10 | * Copyright (C) 2005 Tensilica Inc. | 
|  | 11 | * | 
|  | 12 | * Chris Zankel <chris@zankel.net> | 
|  | 13 | */ | 
|  | 14 |  | 
| Chris Zankel | 5a0015d | 2005-06-23 22:01:16 -0700 | [diff] [blame] | 15 | #include <linux/errno.h> | 
| Alexey Dobriyan | d43c36d | 2009-10-07 17:09:06 +0400 | [diff] [blame] | 16 | #include <linux/sched.h> | 
| Chris Zankel | 5a0015d | 2005-06-23 22:01:16 -0700 | [diff] [blame] | 17 | #include <linux/time.h> | 
| Johannes Weiner | fcc8f0f | 2009-03-04 21:39:12 +0100 | [diff] [blame] | 18 | #include <linux/clocksource.h> | 
| Chris Zankel | 5a0015d | 2005-06-23 22:01:16 -0700 | [diff] [blame] | 19 | #include <linux/interrupt.h> | 
|  | 20 | #include <linux/module.h> | 
|  | 21 | #include <linux/init.h> | 
|  | 22 | #include <linux/irq.h> | 
|  | 23 | #include <linux/profile.h> | 
|  | 24 | #include <linux/delay.h> | 
|  | 25 |  | 
|  | 26 | #include <asm/timex.h> | 
|  | 27 | #include <asm/platform.h> | 
|  | 28 |  | 
| Chris Zankel | 5a0015d | 2005-06-23 22:01:16 -0700 | [diff] [blame] | 29 | #ifdef CONFIG_XTENSA_CALIBRATE_CCOUNT | 
|  | 30 | unsigned long ccount_per_jiffy;		/* per 1/HZ */ | 
| Chris Zankel | 2b8aea7 | 2007-08-05 10:26:30 -0700 | [diff] [blame] | 31 | unsigned long nsec_per_ccount;		/* nsec per ccount increment */ | 
| Chris Zankel | 5a0015d | 2005-06-23 22:01:16 -0700 | [diff] [blame] | 32 | #endif | 
|  | 33 |  | 
| Johannes Weiner | fcc8f0f | 2009-03-04 21:39:12 +0100 | [diff] [blame] | 34 | static cycle_t ccount_read(void) | 
|  | 35 | { | 
|  | 36 | return (cycle_t)get_ccount(); | 
|  | 37 | } | 
|  | 38 |  | 
|  | 39 | static struct clocksource ccount_clocksource = { | 
|  | 40 | .name = "ccount", | 
|  | 41 | .rating = 200, | 
|  | 42 | .read = ccount_read, | 
|  | 43 | .mask = CLOCKSOURCE_MASK(32), | 
|  | 44 | /* | 
|  | 45 | * With a shift of 22 the lower limit of the cpu clock is | 
|  | 46 | * 1MHz, where NSEC_PER_CCOUNT is 1000 or a bit less than | 
|  | 47 | * 2^10: Since we have 32 bits and the multiplicator can | 
|  | 48 | * already take up as much as 10 bits, this leaves us with | 
|  | 49 | * remaining upper 22 bits. | 
|  | 50 | */ | 
|  | 51 | .shift = 22, | 
|  | 52 | }; | 
|  | 53 |  | 
| Chris Zankel | fd43fe1 | 2006-12-10 02:18:47 -0800 | [diff] [blame] | 54 | static irqreturn_t timer_interrupt(int irq, void *dev_id); | 
| Chris Zankel | 5a0015d | 2005-06-23 22:01:16 -0700 | [diff] [blame] | 55 | static struct irqaction timer_irqaction = { | 
|  | 56 | .handler =	timer_interrupt, | 
| Thomas Gleixner | 85ac3ab | 2006-07-01 19:29:31 -0700 | [diff] [blame] | 57 | .flags =	IRQF_DISABLED, | 
| Chris Zankel | 5a0015d | 2005-06-23 22:01:16 -0700 | [diff] [blame] | 58 | .name =		"timer", | 
|  | 59 | }; | 
|  | 60 |  | 
|  | 61 | void __init time_init(void) | 
|  | 62 | { | 
| Chris Zankel | 288a60c | 2005-09-22 21:44:23 -0700 | [diff] [blame] | 63 | #ifdef CONFIG_XTENSA_CALIBRATE_CCOUNT | 
| Chris Zankel | 5a0015d | 2005-06-23 22:01:16 -0700 | [diff] [blame] | 64 | printk("Calibrating CPU frequency "); | 
|  | 65 | platform_calibrate_ccount(); | 
|  | 66 | printk("%d.%02d MHz\n", (int)ccount_per_jiffy/(1000000/HZ), | 
|  | 67 | (int)(ccount_per_jiffy/(10000/HZ))%100); | 
|  | 68 | #endif | 
| Johannes Weiner | fcc8f0f | 2009-03-04 21:39:12 +0100 | [diff] [blame] | 69 | ccount_clocksource.mult = | 
|  | 70 | clocksource_hz2mult(CCOUNT_PER_JIFFY * HZ, | 
|  | 71 | ccount_clocksource.shift); | 
|  | 72 | clocksource_register(&ccount_clocksource); | 
| Chris Zankel | 5a0015d | 2005-06-23 22:01:16 -0700 | [diff] [blame] | 73 |  | 
|  | 74 | /* Initialize the linux timer interrupt. */ | 
|  | 75 |  | 
|  | 76 | setup_irq(LINUX_TIMER_INT, &timer_irqaction); | 
|  | 77 | set_linux_timer(get_ccount() + CCOUNT_PER_JIFFY); | 
|  | 78 | } | 
|  | 79 |  | 
| Chris Zankel | 5a0015d | 2005-06-23 22:01:16 -0700 | [diff] [blame] | 80 | /* | 
|  | 81 | * The timer interrupt is called HZ times per second. | 
|  | 82 | */ | 
|  | 83 |  | 
| Chris Zankel | fd43fe1 | 2006-12-10 02:18:47 -0800 | [diff] [blame] | 84 | irqreturn_t timer_interrupt (int irq, void *dev_id) | 
| Chris Zankel | 5a0015d | 2005-06-23 22:01:16 -0700 | [diff] [blame] | 85 | { | 
|  | 86 |  | 
|  | 87 | unsigned long next; | 
|  | 88 |  | 
|  | 89 | next = get_linux_timer(); | 
|  | 90 |  | 
|  | 91 | again: | 
|  | 92 | while ((signed long)(get_ccount() - next) > 0) { | 
|  | 93 |  | 
| Chris Zankel | fd43fe1 | 2006-12-10 02:18:47 -0800 | [diff] [blame] | 94 | profile_tick(CPU_PROFILING); | 
| Chris Zankel | 5a0015d | 2005-06-23 22:01:16 -0700 | [diff] [blame] | 95 | #ifndef CONFIG_SMP | 
| Chris Zankel | fd43fe1 | 2006-12-10 02:18:47 -0800 | [diff] [blame] | 96 | update_process_times(user_mode(get_irq_regs())); | 
| Chris Zankel | 5a0015d | 2005-06-23 22:01:16 -0700 | [diff] [blame] | 97 | #endif | 
|  | 98 |  | 
| Torben Hohn | d12b0e2 | 2011-01-27 16:00:27 +0100 | [diff] [blame] | 99 | xtime_update(1); /* Linux handler in kernel/time/timekeeping */ | 
| Chris Zankel | 2b8aea7 | 2007-08-05 10:26:30 -0700 | [diff] [blame] | 100 |  | 
|  | 101 | /* Note that writing CCOMPARE clears the interrupt. */ | 
|  | 102 |  | 
| Chris Zankel | 5a0015d | 2005-06-23 22:01:16 -0700 | [diff] [blame] | 103 | next += CCOUNT_PER_JIFFY; | 
| Chris Zankel | 2b8aea7 | 2007-08-05 10:26:30 -0700 | [diff] [blame] | 104 | set_linux_timer(next); | 
| Chris Zankel | 5a0015d | 2005-06-23 22:01:16 -0700 | [diff] [blame] | 105 | } | 
|  | 106 |  | 
| Chris Zankel | 2b8aea7 | 2007-08-05 10:26:30 -0700 | [diff] [blame] | 107 | /* Allow platform to do something useful (Wdog). */ | 
| Chris Zankel | 5a0015d | 2005-06-23 22:01:16 -0700 | [diff] [blame] | 108 |  | 
| Chris Zankel | 2b8aea7 | 2007-08-05 10:26:30 -0700 | [diff] [blame] | 109 | platform_heartbeat(); | 
| Chris Zankel | 5a0015d | 2005-06-23 22:01:16 -0700 | [diff] [blame] | 110 |  | 
|  | 111 | /* Make sure we didn't miss any tick... */ | 
|  | 112 |  | 
|  | 113 | if ((signed long)(get_ccount() - next) > 0) | 
|  | 114 | goto again; | 
|  | 115 |  | 
| Chris Zankel | 5a0015d | 2005-06-23 22:01:16 -0700 | [diff] [blame] | 116 | return IRQ_HANDLED; | 
|  | 117 | } | 
|  | 118 |  | 
|  | 119 | #ifndef CONFIG_GENERIC_CALIBRATE_DELAY | 
| Adrian Bunk | 6c81c32 | 2008-02-06 01:37:51 -0800 | [diff] [blame] | 120 | void __cpuinit calibrate_delay(void) | 
| Chris Zankel | 5a0015d | 2005-06-23 22:01:16 -0700 | [diff] [blame] | 121 | { | 
|  | 122 | loops_per_jiffy = CCOUNT_PER_JIFFY; | 
|  | 123 | printk("Calibrating delay loop (skipped)... " | 
|  | 124 | "%lu.%02lu BogoMIPS preset\n", | 
|  | 125 | loops_per_jiffy/(1000000/HZ), | 
|  | 126 | (loops_per_jiffy/(10000/HZ)) % 100); | 
|  | 127 | } | 
|  | 128 | #endif |