1
linux/arch/arm/mach-omap2/timer-gp.c

262 lines
6.6 KiB
C
Raw Normal View History

/*
* linux/arch/arm/mach-omap2/timer-gp.c
*
* OMAP2 GP timer support.
*
* Copyright (C) 2009 Nokia Corporation
*
* Update to use new clocksource/clockevent layers
* Author: Kevin Hilman, MontaVista Software, Inc. <source@mvista.com>
* Copyright (C) 2007 MontaVista Software, Inc.
*
* Original driver:
* Copyright (C) 2005 Nokia Corporation
* Author: Paul Mundt <paul.mundt@nokia.com>
* Juha Yrjölä <juha.yrjola@nokia.com>
* 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 <santosh.shilimkar@ti.com>
*
* 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 <linux/init.h>
#include <linux/time.h>
#include <linux/interrupt.h>
#include <linux/err.h>
#include <linux/clk.h>
#include <linux/delay.h>
#include <linux/irq.h>
#include <linux/clocksource.h>
#include <linux/clockchips.h>
#include <asm/mach/time.h>
#include <plat/dmtimer.h>
#include <asm/localtimer.h>
#include <asm/sched_clock.h>
OMAP2plus: Fix static function warnings This patch fixes sparse warnings due non declarations of static functions. arch/arm/mach-omap2/timer-gp.c:115:12: warning: symbol 'omap2_gp_clockevent_set_gptimer' was not declared. Should it be static? arch/arm/mach-omap2/powerdomain.c:993:5: warning: symbol 'pwrdm_set_lowpwrstchange' was not declared. Should it be static? arch/arm/mach-omap2/board-flash.c:141:8: warning: symbol 'board_nand_init' was not declared. Should it be static? arch/arm/mach-omap2/board-n8x0.c:416:6: warning: symbol 'n8x0_mmc_slot1_cover_handler' was not declared. Should it be static? arch/arm/mach-omap2/board-n8x0.c:544:13: warning: symbol 'n8x0_mmc_init' was not declared. Should it be static? arch/arm/mach-omap2/board-rx51-peripherals.c:902:13: warning: symbol 'rx51_peripherals_init' was not declared. Should it be static? arch/arm/mach-omap2/board-rx51-video.c:107:13: warning: symbol 'rx51_video_mem_init' was not declared. Should it be static? arch/arm/mach-omap2/board-zoom-debugboard.c:155:12: warning: symbol 'zoom_debugboard_init' was not declared. Should it be static? arch/arm/mach-omap2/board-zoom-peripherals.c:280:13: warning: symbol 'zoom_peripherals_init' was not declared. Should it be static? arch/arm/mach-omap2/board-igep0020.c:110:13: warning: symbol 'igep2_flash_init' was not declared. Should it be static? arch/arm/mach-omap2/board-am3517evm.c:109:6: warning: symbol 'am3517_evm_ethernet_init' was not declared. Should it be static? drivers/mtd/onenand/omap2.c:577:5: warning: symbol 'omap2_onenand_rephase' was not declared. Should it be static? Signed-off-by: Manjunath Kondaiah G <manjugk@ti.com> Cc: linux-arm-kernel@lists.infradead.org Cc: Nishanth Menon <nm@ti.com> Signed-off-by: Tony Lindgren <tony@atomide.com>
2010-10-08 09:58:35 -07:00
#include "timer-gp.h"
OMAP: counter_32k: init clocksource as part of machine timer init After commit dc548fbbd2ecd0fc3b02301d551e5f8e19ae58fd ("ARM: omap: convert sched_clock() to use new infrastructure"), OMAPs that use the 32KiHz "synchronization timer" as their clocksource crash during boot: [ 0.000000] OMAP clockevent source: GPTIMER1 at 32768 Hz [ 0.000000] Unable to handle kernel NULL pointer dereference at virtual address 00000000 [ 0.000000] pgd = c0004000 [ 0.000000] [00000000] *pgd=00000000 [ 0.000000] Internal error: Oops: 80000005 [#1] SMP [ 0.000000] last sysfs file: [ 0.000000] Modules linked in: [ 0.000000] CPU: 0 Tainted: G W (2.6.37-07734-g2467802 #7) [ 0.000000] PC is at 0x0 [ 0.000000] LR is at sched_clock_poll+0x2c/0x3c [ 0.000000] pc : [<00000000>] lr : [<c0060b74>] psr: 600001d3 [ 0.000000] sp : c058bfd0 ip : c058a000 fp : 00000000 [ 0.000000] r10: 00000000 r9 : 411fc092 r8 : 800330c8 [ 0.000000] r7 : c05a08e0 r6 : c0034c48 r5 : c05ffc40 r4 : c0034c4c [ 0.000000] r3 : c05ffe6c r2 : c05a0bc0 r1 : c059f098 r0 : 00000000 [ 0.000000] Flags: nZCv IRQs off FIQs off Mode SVC_32 ISA ARM Segment kernel [ 0.000000] Control: 10c53c7f Table: 8000404a DAC: 00000017 This is due to the recent ARM init_sched_clock() changes and the late initialization of the counter_32k clock source. More information here: http://marc.info/?l=linux-omap&m=129513468605208&w=2 Fix by initializing the counter_32k clocksource during the machine timer initialization. Reported-by: Russell King <rmk+kernel@arm.linux.org.uk> Tested-by: Thomas Weber <weber@corscience.de> Signed-off-by: Paul Walmsley <paul@pwsan.com>
2011-01-15 21:32:01 -07:00
#include <plat/common.h>
/* MAX_GPTIMER_ID: number of GPTIMERs on the chip */
#define MAX_GPTIMER_ID 12
static struct omap_dm_timer *gptimer;
static struct clock_event_device clockevent_gpt;
static u8 __initdata gptimer_id = 1;
static u8 __initdata inited;
struct omap_dm_timer *gptimer_wakeup;
static irqreturn_t omap2_gp_timer_interrupt(int irq, void *dev_id)
{
struct omap_dm_timer *gpt = (struct omap_dm_timer *)dev_id;
struct clock_event_device *evt = &clockevent_gpt;
omap_dm_timer_write_status(gpt, 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_set_load_start(gptimer, 0, 0xffffffff - cycles);
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(gptimer);
switch (mode) {
case CLOCK_EVT_MODE_PERIODIC:
period = clk_get_rate(omap_dm_timer_get_fclk(gptimer)) / HZ;
period -= 1;
omap_dm_timer_set_load_start(gptimer, 1, 0xffffffff - period);
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,
.set_next_event = omap2_gp_timer_set_next_event,
.set_mode = omap2_gp_timer_set_mode,
};
/**
* omap2_gp_clockevent_set_gptimer - set which GPTIMER is used for clockevents
* @id: GPTIMER to use (1..MAX_GPTIMER_ID)
*
* Define the GPTIMER that the system should use for the tick timer.
* Meant to be called from board-*.c files in the event that GPTIMER1, the
* default, is unsuitable. Returns -EINVAL on error or 0 on success.
*/
int __init omap2_gp_clockevent_set_gptimer(u8 id)
{
if (id < 1 || id > MAX_GPTIMER_ID)
return -EINVAL;
BUG_ON(inited);
gptimer_id = id;
return 0;
}
static void __init omap2_gp_clockevent_init(void)
{
u32 tick_rate;
int src;
inited = 1;
gptimer = omap_dm_timer_request_specific(gptimer_id);
BUG_ON(gptimer == NULL);
gptimer_wakeup = gptimer;
#if defined(CONFIG_OMAP_32K_TIMER)
src = OMAP_TIMER_SRC_32_KHZ;
#else
src = OMAP_TIMER_SRC_SYS_CLK;
WARN(gptimer_id == 12, "WARNING: GPTIMER12 can only use the "
"secure 32KiHz clock source\n");
#endif
if (gptimer_id != 12)
WARN(IS_ERR_VALUE(omap_dm_timer_set_source(gptimer, src)),
"timer-gp: omap_dm_timer_set_source() failed\n");
tick_rate = clk_get_rate(omap_dm_timer_get_fclk(gptimer));
pr_info("OMAP clockevent source: GPTIMER%d at %u Hz\n",
gptimer_id, tick_rate);
omap2_gp_timer_irq.dev_id = (void *)gptimer;
setup_irq(omap_dm_timer_get_irq(gptimer), &omap2_gp_timer_irq);
omap_dm_timer_set_int_enable(gptimer, OMAP_TIMER_INT_OVERFLOW);
clockevent_gpt.mult = div_sc(tick_rate, NSEC_PER_SEC,
clockevent_gpt.shift);
clockevent_gpt.max_delta_ns =
clockevent_delta2ns(0xffffffff, &clockevent_gpt);
clockevent_gpt.min_delta_ns =
clockevent_delta2ns(3, &clockevent_gpt);
/* Timer internal resynch latency. */
clockevent_gpt.cpumask = cpumask_of(0);
clockevents_register_device(&clockevent_gpt);
}
/* Clocksource code */
#ifdef CONFIG_OMAP_32K_TIMER
/*
* When 32k-timer is enabled, don't use GPTimer for clocksource
* instead, just leave default clocksource which uses the 32k
OMAP: counter_32k: init clocksource as part of machine timer init After commit dc548fbbd2ecd0fc3b02301d551e5f8e19ae58fd ("ARM: omap: convert sched_clock() to use new infrastructure"), OMAPs that use the 32KiHz "synchronization timer" as their clocksource crash during boot: [ 0.000000] OMAP clockevent source: GPTIMER1 at 32768 Hz [ 0.000000] Unable to handle kernel NULL pointer dereference at virtual address 00000000 [ 0.000000] pgd = c0004000 [ 0.000000] [00000000] *pgd=00000000 [ 0.000000] Internal error: Oops: 80000005 [#1] SMP [ 0.000000] last sysfs file: [ 0.000000] Modules linked in: [ 0.000000] CPU: 0 Tainted: G W (2.6.37-07734-g2467802 #7) [ 0.000000] PC is at 0x0 [ 0.000000] LR is at sched_clock_poll+0x2c/0x3c [ 0.000000] pc : [<00000000>] lr : [<c0060b74>] psr: 600001d3 [ 0.000000] sp : c058bfd0 ip : c058a000 fp : 00000000 [ 0.000000] r10: 00000000 r9 : 411fc092 r8 : 800330c8 [ 0.000000] r7 : c05a08e0 r6 : c0034c48 r5 : c05ffc40 r4 : c0034c4c [ 0.000000] r3 : c05ffe6c r2 : c05a0bc0 r1 : c059f098 r0 : 00000000 [ 0.000000] Flags: nZCv IRQs off FIQs off Mode SVC_32 ISA ARM Segment kernel [ 0.000000] Control: 10c53c7f Table: 8000404a DAC: 00000017 This is due to the recent ARM init_sched_clock() changes and the late initialization of the counter_32k clock source. More information here: http://marc.info/?l=linux-omap&m=129513468605208&w=2 Fix by initializing the counter_32k clocksource during the machine timer initialization. Reported-by: Russell King <rmk+kernel@arm.linux.org.uk> Tested-by: Thomas Weber <weber@corscience.de> Signed-off-by: Paul Walmsley <paul@pwsan.com>
2011-01-15 21:32:01 -07:00
* sync counter. See clocksource setup in plat-omap/counter_32k.c
*/
OMAP: counter_32k: init clocksource as part of machine timer init After commit dc548fbbd2ecd0fc3b02301d551e5f8e19ae58fd ("ARM: omap: convert sched_clock() to use new infrastructure"), OMAPs that use the 32KiHz "synchronization timer" as their clocksource crash during boot: [ 0.000000] OMAP clockevent source: GPTIMER1 at 32768 Hz [ 0.000000] Unable to handle kernel NULL pointer dereference at virtual address 00000000 [ 0.000000] pgd = c0004000 [ 0.000000] [00000000] *pgd=00000000 [ 0.000000] Internal error: Oops: 80000005 [#1] SMP [ 0.000000] last sysfs file: [ 0.000000] Modules linked in: [ 0.000000] CPU: 0 Tainted: G W (2.6.37-07734-g2467802 #7) [ 0.000000] PC is at 0x0 [ 0.000000] LR is at sched_clock_poll+0x2c/0x3c [ 0.000000] pc : [<00000000>] lr : [<c0060b74>] psr: 600001d3 [ 0.000000] sp : c058bfd0 ip : c058a000 fp : 00000000 [ 0.000000] r10: 00000000 r9 : 411fc092 r8 : 800330c8 [ 0.000000] r7 : c05a08e0 r6 : c0034c48 r5 : c05ffc40 r4 : c0034c4c [ 0.000000] r3 : c05ffe6c r2 : c05a0bc0 r1 : c059f098 r0 : 00000000 [ 0.000000] Flags: nZCv IRQs off FIQs off Mode SVC_32 ISA ARM Segment kernel [ 0.000000] Control: 10c53c7f Table: 8000404a DAC: 00000017 This is due to the recent ARM init_sched_clock() changes and the late initialization of the counter_32k clock source. More information here: http://marc.info/?l=linux-omap&m=129513468605208&w=2 Fix by initializing the counter_32k clocksource during the machine timer initialization. Reported-by: Russell King <rmk+kernel@arm.linux.org.uk> Tested-by: Thomas Weber <weber@corscience.de> Signed-off-by: Paul Walmsley <paul@pwsan.com>
2011-01-15 21:32:01 -07:00
static void __init omap2_gp_clocksource_init(void)
{
omap_init_clocksource_32k();
}
#else
/*
* clocksource
*/
static DEFINE_CLOCK_DATA(cd);
static struct omap_dm_timer *gpt_clocksource;
static cycle_t clocksource_read_cycles(struct clocksource *cs)
{
return (cycle_t)omap_dm_timer_read_counter(gpt_clocksource);
}
static struct clocksource clocksource_gpt = {
.name = "gp timer",
.rating = 300,
.read = clocksource_read_cycles,
.mask = CLOCKSOURCE_MASK(32),
.flags = CLOCK_SOURCE_IS_CONTINUOUS,
};
static void notrace dmtimer_update_sched_clock(void)
{
u32 cyc;
cyc = omap_dm_timer_read_counter(gpt_clocksource);
update_sched_clock(&cd, cyc, (u32)~0);
}
/* Setup free-running counter for clocksource */
static void __init omap2_gp_clocksource_init(void)
{
static struct omap_dm_timer *gpt;
u32 tick_rate;
static char err1[] __initdata = KERN_ERR
"%s: failed to request dm-timer\n";
static char err2[] __initdata = KERN_ERR
"%s: can't register clocksource!\n";
gpt = omap_dm_timer_request();
if (!gpt)
printk(err1, clocksource_gpt.name);
gpt_clocksource = gpt;
omap_dm_timer_set_source(gpt, OMAP_TIMER_SRC_SYS_CLK);
tick_rate = clk_get_rate(omap_dm_timer_get_fclk(gpt));
omap_dm_timer_set_load_start(gpt, 1, 0);
init_sched_clock(&cd, dmtimer_update_sched_clock, 32, tick_rate);
if (clocksource_register_hz(&clocksource_gpt, tick_rate))
printk(err2, clocksource_gpt.name);
}
#endif
static void __init omap2_gp_timer_init(void)
{
#ifdef CONFIG_LOCAL_TIMERS
if (cpu_is_omap44xx()) {
twd_base = ioremap(OMAP44XX_LOCAL_TWD_BASE, SZ_256);
BUG_ON(!twd_base);
}
#endif
omap_dm_timer_init();
omap2_gp_clockevent_init();
omap2_gp_clocksource_init();
}
struct sys_timer omap_timer = {
.init = omap2_gp_timer_init,
};