1 /* 2 * linux/arch/arm/mach-omap1/time.c 3 * 4 * OMAP Timers 5 * 6 * Copyright (C) 2004 Nokia Corporation 7 * Partial timer rewrite and additional dynamic tick timer support by 8 * Tony Lindgen <tony@atomide.com> and 9 * Tuukka Tikkanen <tuukka.tikkanen@elektrobit.com> 10 * 11 * MPU timer code based on the older MPU timer code for OMAP 12 * Copyright (C) 2000 RidgeRun, Inc. 13 * Author: Greg Lonnon <glonnon@ridgerun.com> 14 * 15 * This program is free software; you can redistribute it and/or modify it 16 * under the terms of the GNU General Public License as published by the 17 * Free Software Foundation; either version 2 of the License, or (at your 18 * option) any later version. 19 * 20 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED 21 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 22 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN 23 * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 24 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 25 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF 26 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 27 * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 28 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 29 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 * 31 * You should have received a copy of the GNU General Public License along 32 * with this program; if not, write to the Free Software Foundation, Inc., 33 * 675 Mass Ave, Cambridge, MA 02139, USA. 34 */ 35 36 #include <linux/kernel.h> 37 #include <linux/init.h> 38 #include <linux/delay.h> 39 #include <linux/interrupt.h> 40 #include <linux/spinlock.h> 41 #include <linux/clk.h> 42 #include <linux/err.h> 43 #include <linux/clocksource.h> 44 #include <linux/clockchips.h> 45 #include <linux/io.h> 46 #include <linux/sched_clock.h> 47 48 #include <asm/irq.h> 49 50 #include <mach/hardware.h> 51 #include <asm/mach/irq.h> 52 #include <asm/mach/time.h> 53 54 #include "iomap.h" 55 #include "common.h" 56 57 #ifdef CONFIG_OMAP_MPU_TIMER 58 59 #define OMAP_MPU_TIMER_BASE OMAP_MPU_TIMER1_BASE 60 #define OMAP_MPU_TIMER_OFFSET 0x100 61 62 typedef struct { 63 u32 cntl; /* CNTL_TIMER, R/W */ 64 u32 load_tim; /* LOAD_TIM, W */ 65 u32 read_tim; /* READ_TIM, R */ 66 } omap_mpu_timer_regs_t; 67 68 #define omap_mpu_timer_base(n) \ 69 ((omap_mpu_timer_regs_t __iomem *)OMAP1_IO_ADDRESS(OMAP_MPU_TIMER_BASE + \ 70 (n)*OMAP_MPU_TIMER_OFFSET)) 71 72 static inline unsigned long notrace omap_mpu_timer_read(int nr) 73 { 74 omap_mpu_timer_regs_t __iomem *timer = omap_mpu_timer_base(nr); 75 return readl(&timer->read_tim); 76 } 77 78 static inline void omap_mpu_set_autoreset(int nr) 79 { 80 omap_mpu_timer_regs_t __iomem *timer = omap_mpu_timer_base(nr); 81 82 writel(readl(&timer->cntl) | MPU_TIMER_AR, &timer->cntl); 83 } 84 85 static inline void omap_mpu_remove_autoreset(int nr) 86 { 87 omap_mpu_timer_regs_t __iomem *timer = omap_mpu_timer_base(nr); 88 89 writel(readl(&timer->cntl) & ~MPU_TIMER_AR, &timer->cntl); 90 } 91 92 static inline void omap_mpu_timer_start(int nr, unsigned long load_val, 93 int autoreset) 94 { 95 omap_mpu_timer_regs_t __iomem *timer = omap_mpu_timer_base(nr); 96 unsigned int timerflags = MPU_TIMER_CLOCK_ENABLE | MPU_TIMER_ST; 97 98 if (autoreset) 99 timerflags |= MPU_TIMER_AR; 100 101 writel(MPU_TIMER_CLOCK_ENABLE, &timer->cntl); 102 udelay(1); 103 writel(load_val, &timer->load_tim); 104 udelay(1); 105 writel(timerflags, &timer->cntl); 106 } 107 108 static inline void omap_mpu_timer_stop(int nr) 109 { 110 omap_mpu_timer_regs_t __iomem *timer = omap_mpu_timer_base(nr); 111 112 writel(readl(&timer->cntl) & ~MPU_TIMER_ST, &timer->cntl); 113 } 114 115 /* 116 * --------------------------------------------------------------------------- 117 * MPU timer 1 ... count down to zero, interrupt, reload 118 * --------------------------------------------------------------------------- 119 */ 120 static int omap_mpu_set_next_event(unsigned long cycles, 121 struct clock_event_device *evt) 122 { 123 omap_mpu_timer_start(0, cycles, 0); 124 return 0; 125 } 126 127 static int omap_mpu_set_oneshot(struct clock_event_device *evt) 128 { 129 omap_mpu_timer_stop(0); 130 omap_mpu_remove_autoreset(0); 131 return 0; 132 } 133 134 static int omap_mpu_set_periodic(struct clock_event_device *evt) 135 { 136 omap_mpu_set_autoreset(0); 137 return 0; 138 } 139 140 static struct clock_event_device clockevent_mpu_timer1 = { 141 .name = "mpu_timer1", 142 .features = CLOCK_EVT_FEAT_PERIODIC | 143 CLOCK_EVT_FEAT_ONESHOT, 144 .set_next_event = omap_mpu_set_next_event, 145 .set_state_periodic = omap_mpu_set_periodic, 146 .set_state_oneshot = omap_mpu_set_oneshot, 147 }; 148 149 static irqreturn_t omap_mpu_timer1_interrupt(int irq, void *dev_id) 150 { 151 struct clock_event_device *evt = &clockevent_mpu_timer1; 152 153 evt->event_handler(evt); 154 155 return IRQ_HANDLED; 156 } 157 158 static __init void omap_init_mpu_timer(unsigned long rate) 159 { 160 if (request_irq(INT_TIMER1, omap_mpu_timer1_interrupt, 161 IRQF_TIMER | IRQF_IRQPOLL, "mpu_timer1", NULL)) 162 pr_err("Failed to request irq %d (mpu_timer1)\n", INT_TIMER1); 163 omap_mpu_timer_start(0, (rate / HZ) - 1, 1); 164 165 clockevent_mpu_timer1.cpumask = cpumask_of(0); 166 clockevents_config_and_register(&clockevent_mpu_timer1, rate, 167 1, -1); 168 } 169 170 171 /* 172 * --------------------------------------------------------------------------- 173 * MPU timer 2 ... free running 32-bit clock source and scheduler clock 174 * --------------------------------------------------------------------------- 175 */ 176 177 static u64 notrace omap_mpu_read_sched_clock(void) 178 { 179 return ~omap_mpu_timer_read(1); 180 } 181 182 static void __init omap_init_clocksource(unsigned long rate) 183 { 184 omap_mpu_timer_regs_t __iomem *timer = omap_mpu_timer_base(1); 185 static char err[] __initdata = KERN_ERR 186 "%s: can't register clocksource!\n"; 187 188 omap_mpu_timer_start(1, ~0, 1); 189 sched_clock_register(omap_mpu_read_sched_clock, 32, rate); 190 191 if (clocksource_mmio_init(&timer->read_tim, "mpu_timer2", rate, 192 300, 32, clocksource_mmio_readl_down)) 193 printk(err, "mpu_timer2"); 194 } 195 196 static void __init omap_mpu_timer_init(void) 197 { 198 struct clk *ck_ref = clk_get(NULL, "ck_ref"); 199 unsigned long rate; 200 201 BUG_ON(IS_ERR(ck_ref)); 202 203 rate = clk_get_rate(ck_ref); 204 clk_put(ck_ref); 205 206 /* PTV = 0 */ 207 rate /= 2; 208 209 omap_init_mpu_timer(rate); 210 omap_init_clocksource(rate); 211 } 212 213 #else 214 static inline void omap_mpu_timer_init(void) 215 { 216 pr_err("Bogus timer, should not happen\n"); 217 } 218 #endif /* CONFIG_OMAP_MPU_TIMER */ 219 220 /* 221 * --------------------------------------------------------------------------- 222 * Timer initialization 223 * --------------------------------------------------------------------------- 224 */ 225 void __init omap1_timer_init(void) 226 { 227 if (omap_32k_timer_init() != 0) 228 omap_mpu_timer_init(); 229 } 230