1/* 2 * linux/arch/arm/mach-omap2/timer-gp.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 43/* MAX_GPTIMER_ID: number of GPTIMERs on the chip */ 44#define MAX_GPTIMER_ID 12 45 46static struct omap_dm_timer *gptimer; 47static struct clock_event_device clockevent_gpt; 48static u8 __initdata gptimer_id = 1; 49static u8 __initdata inited; 50struct omap_dm_timer *gptimer_wakeup; 51 52static irqreturn_t omap2_gp_timer_interrupt(int irq, void *dev_id) 53{ 54 struct omap_dm_timer *gpt = (struct omap_dm_timer *)dev_id; 55 struct clock_event_device *evt = &clockevent_gpt; 56 57 omap_dm_timer_write_status(gpt, OMAP_TIMER_INT_OVERFLOW); 58 59 evt->event_handler(evt); 60 return IRQ_HANDLED; 61} 62 63static struct irqaction omap2_gp_timer_irq = { 64 .name = "gp timer", 65 .flags = IRQF_DISABLED | IRQF_TIMER | IRQF_IRQPOLL, 66 .handler = omap2_gp_timer_interrupt, 67}; 68 69static int omap2_gp_timer_set_next_event(unsigned long cycles, 70 struct clock_event_device *evt) 71{ 72 omap_dm_timer_set_load_start(gptimer, 0, 0xffffffff - cycles); 73 74 return 0; 75} 76 77static void omap2_gp_timer_set_mode(enum clock_event_mode mode, 78 struct clock_event_device *evt) 79{ 80 u32 period; 81 82 omap_dm_timer_stop(gptimer); 83 84 switch (mode) { 85 case CLOCK_EVT_MODE_PERIODIC: 86 period = clk_get_rate(omap_dm_timer_get_fclk(gptimer)) / HZ; 87 period -= 1; 88 omap_dm_timer_set_load_start(gptimer, 1, 0xffffffff - period); 89 break; 90 case CLOCK_EVT_MODE_ONESHOT: 91 break; 92 case CLOCK_EVT_MODE_UNUSED: 93 case CLOCK_EVT_MODE_SHUTDOWN: 94 case CLOCK_EVT_MODE_RESUME: 95 break; 96 } 97} 98 99static struct clock_event_device clockevent_gpt = { 100 .name = "gp timer", 101 .features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT, 102 .shift = 32, 103 .set_next_event = omap2_gp_timer_set_next_event, 104 .set_mode = omap2_gp_timer_set_mode, 105}; 106 107/** 108 * omap2_gp_clockevent_set_gptimer - set which GPTIMER is used for clockevents 109 * @id: GPTIMER to use (1..MAX_GPTIMER_ID) 110 * 111 * Define the GPTIMER that the system should use for the tick timer. 112 * Meant to be called from board-*.c files in the event that GPTIMER1, the 113 * default, is unsuitable. Returns -EINVAL on error or 0 on success. 114 */ 115int __init omap2_gp_clockevent_set_gptimer(u8 id) 116{ 117 if (id < 1 || id > MAX_GPTIMER_ID) 118 return -EINVAL; 119 120 BUG_ON(inited); 121 122 gptimer_id = id; 123 124 return 0; 125} 126 127static void __init omap2_gp_clockevent_init(void) 128{ 129 u32 tick_rate; 130 int src; 131 132 inited = 1; 133 134 gptimer = omap_dm_timer_request_specific(gptimer_id); 135 BUG_ON(gptimer == NULL); 136 gptimer_wakeup = gptimer; 137 138#if defined(CONFIG_OMAP_32K_TIMER) 139 src = OMAP_TIMER_SRC_32_KHZ; 140#else 141 src = OMAP_TIMER_SRC_SYS_CLK; 142 WARN(gptimer_id == 12, "WARNING: GPTIMER12 can only use the " 143 "secure 32KiHz clock source\n"); 144#endif 145 146 if (gptimer_id != 12) 147 WARN(IS_ERR_VALUE(omap_dm_timer_set_source(gptimer, src)), 148 "timer-gp: omap_dm_timer_set_source() failed\n"); 149 150 tick_rate = clk_get_rate(omap_dm_timer_get_fclk(gptimer)); 151 152 pr_info("OMAP clockevent source: GPTIMER%d at %u Hz\n", 153 gptimer_id, tick_rate); 154 155 omap2_gp_timer_irq.dev_id = (void *)gptimer; 156 setup_irq(omap_dm_timer_get_irq(gptimer), &omap2_gp_timer_irq); 157 omap_dm_timer_set_int_enable(gptimer, OMAP_TIMER_INT_OVERFLOW); 158 159 clockevent_gpt.mult = div_sc(tick_rate, NSEC_PER_SEC, 160 clockevent_gpt.shift); 161 clockevent_gpt.max_delta_ns = 162 clockevent_delta2ns(0xffffffff, &clockevent_gpt); 163 clockevent_gpt.min_delta_ns = 164 clockevent_delta2ns(3, &clockevent_gpt); 165 /* Timer internal resynch latency. */ 166 167 clockevent_gpt.cpumask = cpumask_of(0); 168 clockevents_register_device(&clockevent_gpt); 169} 170 171/* Clocksource code */ 172 173#ifdef CONFIG_OMAP_32K_TIMER 174/* 175 * When 32k-timer is enabled, don't use GPTimer for clocksource 176 * instead, just leave default clocksource which uses the 32k 177 * sync counter. See clocksource setup in see plat-omap/common.c. 178 */ 179 180static inline void __init omap2_gp_clocksource_init(void) {} 181#else 182/* 183 * clocksource 184 */ 185static struct omap_dm_timer *gpt_clocksource; 186static cycle_t clocksource_read_cycles(struct clocksource *cs) 187{ 188 return (cycle_t)omap_dm_timer_read_counter(gpt_clocksource); 189} 190 191static struct clocksource clocksource_gpt = { 192 .name = "gp timer", 193 .rating = 300, 194 .read = clocksource_read_cycles, 195 .mask = CLOCKSOURCE_MASK(32), 196 .shift = 24, 197 .flags = CLOCK_SOURCE_IS_CONTINUOUS, 198}; 199 200/* Setup free-running counter for clocksource */ 201static void __init omap2_gp_clocksource_init(void) 202{ 203 static struct omap_dm_timer *gpt; 204 u32 tick_rate, tick_period; 205 static char err1[] __initdata = KERN_ERR 206 "%s: failed to request dm-timer\n"; 207 static char err2[] __initdata = KERN_ERR 208 "%s: can't register clocksource!\n"; 209 210 gpt = omap_dm_timer_request(); 211 if (!gpt) 212 printk(err1, clocksource_gpt.name); 213 gpt_clocksource = gpt; 214 215 omap_dm_timer_set_source(gpt, OMAP_TIMER_SRC_SYS_CLK); 216 tick_rate = clk_get_rate(omap_dm_timer_get_fclk(gpt)); 217 tick_period = (tick_rate / HZ) - 1; 218 219 omap_dm_timer_set_load_start(gpt, 1, 0); 220 221 clocksource_gpt.mult = 222 clocksource_khz2mult(tick_rate/1000, clocksource_gpt.shift); 223 if (clocksource_register(&clocksource_gpt)) 224 printk(err2, clocksource_gpt.name); 225} 226#endif 227 228static void __init omap2_gp_timer_init(void) 229{ 230#ifdef CONFIG_LOCAL_TIMERS 231 twd_base = ioremap(OMAP44XX_LOCAL_TWD_BASE, SZ_256); 232 BUG_ON(!twd_base); 233#endif 234 omap_dm_timer_init(); 235 236 omap2_gp_clockevent_init(); 237 omap2_gp_clocksource_init(); 238} 239 240struct sys_timer omap_timer = { 241 .init = omap2_gp_timer_init, 242}; 243