/* * linux/arch/arm/mach-omap2/timer.c * * OMAP2 GP timer support. * * Copyright (C) 2009 Nokia Corporation * * Update to use new clocksource/clockevent layers * Author: Kevin Hilman, MontaVista Software, Inc. * Copyright (C) 2007 MontaVista Software, Inc. * * Original driver: * Copyright (C) 2005 Nokia Corporation * Author: Paul Mundt * Juha Yrjölä * OMAP Dual-mode timer framework support by Timo Teras * * Some parts based off of TI's 24xx code: * * Copyright (C) 2004-2009 Texas Instruments, Inc. * * Roughly modelled after the OMAP1 MPU timer code. * Added OMAP4 support - Santosh Shilimkar * * This file is subject to the terms and conditions of the GNU General Public * License. See the file "COPYING" in the main directory of this archive * for more details. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "omap_hwmod.h" #include "omap_device.h" #include #include #include "omap-pm.h" #include "soc.h" #include "common.h" #include "powerdomain.h" /* Parent clocks, eventually these will come from the clock framework */ #define OMAP2_MPU_SOURCE "sys_ck" #define OMAP3_MPU_SOURCE OMAP2_MPU_SOURCE #define OMAP4_MPU_SOURCE "sys_clkin_ck" #define OMAP2_32K_SOURCE "func_32k_ck" #define OMAP3_32K_SOURCE "omap_32k_fck" #define OMAP4_32K_SOURCE "sys_32k_ck" #define REALTIME_COUNTER_BASE 0x48243200 #define INCREMENTER_NUMERATOR_OFFSET 0x10 #define INCREMENTER_DENUMERATOR_RELOAD_OFFSET 0x14 #define NUMERATOR_DENUMERATOR_MASK 0xfffff000 /* Clockevent code */ static struct omap_dm_timer clkev; static struct clock_event_device clockevent_gpt; static irqreturn_t omap2_gp_timer_interrupt(int irq, void *dev_id) { struct clock_event_device *evt = &clockevent_gpt; __omap_dm_timer_write_status(&clkev, OMAP_TIMER_INT_OVERFLOW); evt->event_handler(evt); return IRQ_HANDLED; } static struct irqaction omap2_gp_timer_irq = { .name = "gp_timer", .flags = IRQF_DISABLED | IRQF_TIMER | IRQF_IRQPOLL, .handler = omap2_gp_timer_interrupt, }; static int omap2_gp_timer_set_next_event(unsigned long cycles, struct clock_event_device *evt) { __omap_dm_timer_load_start(&clkev, OMAP_TIMER_CTRL_ST, 0xffffffff - cycles, OMAP_TIMER_POSTED); return 0; } static void omap2_gp_timer_set_mode(enum clock_event_mode mode, struct clock_event_device *evt) { u32 period; __omap_dm_timer_stop(&clkev, OMAP_TIMER_POSTED, clkev.rate); switch (mode) { case CLOCK_EVT_MODE_PERIODIC: period = clkev.rate / HZ; period -= 1; /* Looks like we need to first set the load value separately */ __omap_dm_timer_write(&clkev, OMAP_TIMER_LOAD_REG, 0xffffffff - period, OMAP_TIMER_POSTED); __omap_dm_timer_load_start(&clkev, OMAP_TIMER_CTRL_AR | OMAP_TIMER_CTRL_ST, 0xffffffff - period, OMAP_TIMER_POSTED); break; case CLOCK_EVT_MODE_ONESHOT: break; case CLOCK_EVT_MODE_UNUSED: case CLOCK_EVT_MODE_SHUTDOWN: case CLOCK_EVT_MODE_RESUME: break; } } static struct clock_event_device clockevent_gpt = { .name = "gp_timer", .features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT, .shift = 32, .rating = 300, .set_next_event = omap2_gp_timer_set_next_event, .set_mode = omap2_gp_timer_set_mode, }; static struct property device_disabled = { .name = "status", .length = sizeof("disabled"), .value = "disabled", }; static struct of_device_id omap_timer_match[] __initdata = { { .compatible = "ti,omap2-timer", }, { } }; /** * omap_get_timer_dt - get a timer using device-tree * @match - device-tree match structure for matching a device type * @property - optional timer property to match * * Helper function to get a timer during early boot using device-tree for use * as kernel system timer. Optionally, the property argument can be used to * select a timer with a specific property. Once a timer is found then mark * the timer node in device-tree as disabled, to prevent the kernel from * registering this timer as a platform device and so no one else can use it. */ static struct device_node * __init omap_get_timer_dt(struct of_device_id *match,