Files
kernel-tenderloin-3.0/arch/arm/kernel/arch_timer.c
Trilok Soni eecb28c590 ARM: gic: Consolidate PPI handling with request_percpu_irq() API
The commit 292b293 creates the MSM boot failures, so squash
the commit 28af690 with it to avoid such failures. The commit ddd847
and 0c1991 are required to keep the watchdog and Copper targets working.

commit 292b293ceef2eda1f96f0c90b96e954d7bdabd1c
Author: Marc Zyngier <marc.zyngier@arm.com>
Date:   Wed Jul 20 16:24:14 2011 +0100

    ARM: gic: consolidate PPI handling

    PPI handling is a bit of an odd beast. It uses its own low level
    handling code and is hardwired to the local timers (hence lacking
    a registration interface).

    Instead, switch the low handling to the normal SPI handling code.
    PPIs are handled by the handle_percpu_devid_irq flow.

    This also allows the removal of some duplicated code.

    Cc: Kukjin Kim <kgene.kim@samsung.com>
    Cc: David Brown <davidb@codeaurora.org>
    Cc: Bryan Huntsman <bryanh@codeaurora.org>
    Cc: Tony Lindgren <tony@atomide.com>
    Cc: Paul Mundt <lethal@linux-sh.org>
    Cc: Magnus Damm <magnus.damm@gmail.com>
    Cc: Thomas Gleixner <tglx@linutronix.de>
    Acked-by: David Brown <davidb@codeaurora.org>
    Tested-by: David Brown <davidb@codeaurora.org>
    Tested-by: Shawn Guo <shawn.guo@linaro.org>
    Signed-off-by: Marc Zyngier <marc.zyngier@arm.com>

commit 28af690a284dfcb627bd69d0963db1c0f412cb8c
Author: Marc Zyngier <marc.zyngier@arm.com>
Date:   Fri Jul 22 12:52:37 2011 +0100

    ARM: gic, local timers: use the request_percpu_irq() interface

    This patch remove the hardcoded link between local timers and PPIs,
    and convert the PPI users (TWD, MCT and MSM timers) to the new
    *_percpu_irq interface. Also some collateral cleanup
    (local_timer_ack() is gone, and the interrupt handler is strictly
    private to each driver).

    PPIs are now useable for more than just the local timers.

    Additional testing by David Brown (msm8250 and msm8660) and
    Shawn Guo (imx6q).

    Cc: David Brown <davidb@codeaurora.org>
    Cc: Thomas Gleixner <tglx@linutronix.de>
    Acked-by: David Brown <davidb@codeaurora.org>
    Tested-by: David Brown <davidb@codeaurora.org>
    Tested-by: Shawn Guo <shawn.guo@linaro.org>
    Signed-off-by: Marc Zyngier <marc.zyngier@arm.com>

commit ddd8478d68f8cf75ee9771667c0cbe2a9d1caeb9
Author: Trilok Soni <tsoni@codeaurora.org>
Date:   Tue Dec 6 00:56:01 2011 +0530

    msm: watchdog: Use request_percpu_irq() interface

    Change-Id: I7c319344f6a7f7a7c70682ac87f5c385e56d130c
    Signed-off-by: Trilok Soni <tsoni@codeaurora.org>
    Signed-off-by: Rohit Vaswani <rvaswani@codeaurora.org>

commit 0c19915e092214a4c17a9920c4c1f3d78610217d
Author: Sathish Ambley <sambley@codeaurora.org>
Date:   Fri Dec 9 17:07:37 2011 +0530

    arm: arch_timer: Use request_percpu_irq() API

    Change-Id: Iee9b218d538f315cd884a47d95bcc0dcc49b0fe1
    Signed-off-by: Sathish Ambley <sambley@codeaurora.org>

Change-Id: I7bbba706b1f2e55814be5891ed76063725c2bfb1
Signed-off-by: Ravi Kumar <kumarrav@codeaurora.org>
[tsoni@codeaurora.org: MSM specific fixes]
Signed-off-by: Trilok Soni <tsoni@codeaurora.org>
2011-12-23 18:05:10 -07:00

340 lines
7.7 KiB
C

/*
* linux/arch/arm/kernel/arch_timer.c
*
* Copyright (C) 2011 ARM Ltd.
* All Rights Reserved
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/timex.h>
#include <linux/device.h>
#include <linux/smp.h>
#include <linux/cpu.h>
#include <linux/jiffies.h>
#include <linux/clockchips.h>
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/irq.h>
#include <asm/cputype.h>
#include <asm/sched_clock.h>
#include <asm/hardware/gic.h>
static unsigned long arch_timer_rate;
static int arch_timer_ppi;
static int arch_timer_ppi2;
static DEFINE_CLOCK_DATA(cd);
static struct clock_event_device __percpu *arch_timer_evt;
/*
* Architected system timer support.
*/
#define ARCH_TIMER_CTRL_ENABLE (1 << 0)
#define ARCH_TIMER_CTRL_IT_MASK (1 << 1)
#define ARCH_TIMER_REG_CTRL 0
#define ARCH_TIMER_REG_FREQ 1
#define ARCH_TIMER_REG_TVAL 2
static void arch_timer_reg_write(int reg, u32 val)
{
switch (reg) {
case ARCH_TIMER_REG_CTRL:
asm volatile("mcr p15, 0, %0, c14, c2, 1" : : "r" (val));
break;
case ARCH_TIMER_REG_TVAL:
asm volatile("mcr p15, 0, %0, c14, c2, 0" : : "r" (val));
break;
}
isb();
}
static u32 arch_timer_reg_read(int reg)
{
u32 val;
switch (reg) {
case ARCH_TIMER_REG_CTRL:
asm volatile("mrc p15, 0, %0, c14, c2, 1" : "=r" (val));
break;
case ARCH_TIMER_REG_FREQ:
asm volatile("mrc p15, 0, %0, c14, c0, 0" : "=r" (val));
break;
case ARCH_TIMER_REG_TVAL:
asm volatile("mrc p15, 0, %0, c14, c2, 0" : "=r" (val));
break;
default:
BUG();
}
return val;
}
static irqreturn_t arch_timer_handler(int irq, void *dev_id)
{
struct clock_event_device *evt;
unsigned long ctrl;
ctrl = arch_timer_reg_read(ARCH_TIMER_REG_CTRL);
if (ctrl & 0x4) {
ctrl |= ARCH_TIMER_CTRL_IT_MASK;
arch_timer_reg_write(ARCH_TIMER_REG_CTRL, ctrl);
evt = per_cpu_ptr(arch_timer_evt, smp_processor_id());
evt->event_handler(evt);
return IRQ_HANDLED;
}
return IRQ_NONE;
}
static void arch_timer_stop(void)
{
unsigned long ctrl;
ctrl = arch_timer_reg_read(ARCH_TIMER_REG_CTRL);
ctrl &= ~ARCH_TIMER_CTRL_ENABLE;
arch_timer_reg_write(ARCH_TIMER_REG_CTRL, ctrl);
}
static void arch_timer_set_mode(enum clock_event_mode mode,
struct clock_event_device *clk)
{
switch (mode) {
case CLOCK_EVT_MODE_UNUSED:
case CLOCK_EVT_MODE_SHUTDOWN:
arch_timer_stop();
break;
default:
break;
}
}
static int arch_timer_set_next_event(unsigned long evt,
struct clock_event_device *unused)
{
unsigned long ctrl;
ctrl = arch_timer_reg_read(ARCH_TIMER_REG_CTRL);
ctrl |= ARCH_TIMER_CTRL_ENABLE;
ctrl &= ~ARCH_TIMER_CTRL_IT_MASK;
arch_timer_reg_write(ARCH_TIMER_REG_CTRL, ctrl);
arch_timer_reg_write(ARCH_TIMER_REG_TVAL, evt);
return 0;
}
static void __cpuinit arch_timer_setup(void *data)
{
struct clock_event_device *clk = data;
/* Be safe... */
arch_timer_stop();
clk->features = CLOCK_EVT_FEAT_ONESHOT;
clk->name = "arch_sys_timer";
clk->rating = 450;
clk->set_mode = arch_timer_set_mode;
clk->set_next_event = arch_timer_set_next_event;
clk->irq = arch_timer_ppi;
clk->cpumask = cpumask_of(smp_processor_id());
clockevents_config_and_register(clk, arch_timer_rate,
0xf, 0x7fffffff);
enable_percpu_irq(arch_timer_ppi, 0);
if (arch_timer_ppi2 > 0)
enable_percpu_irq(arch_timer_ppi2, 0);
}
/* Is the optional system timer available? */
static int local_timer_is_architected(void)
{
return (cpu_architecture() >= CPU_ARCH_ARMv7) &&
((read_cpuid_ext(CPUID_EXT_PFR1) >> 16) & 0xf) == 1;
}
static int arch_timer_available(void)
{
unsigned long freq;
if (!local_timer_is_architected())
return -ENXIO;
if (arch_timer_rate == 0) {
arch_timer_reg_write(ARCH_TIMER_REG_CTRL, 0);
freq = arch_timer_reg_read(ARCH_TIMER_REG_FREQ);
/* Check the timer frequency. */
if (freq == 0) {
pr_warn("Architected timer frequency not available\n");
return -EINVAL;
}
arch_timer_rate = freq;
pr_info("Architected local timer running at %lu.%02luMHz.\n",
arch_timer_rate / 1000000, (arch_timer_rate % 100000) / 100);
}
return 0;
}
static inline cycle_t arch_counter_get_cntpct(void)
{
u32 cvall, cvalh;
asm volatile("mrrc p15, 0, %0, %1, c14" : "=r" (cvall), "=r" (cvalh));
return ((u64) cvalh << 32) | cvall;
}
static inline cycle_t arch_counter_get_cntvct(void)
{
u32 cvall, cvalh;
asm volatile("mrrc p15, 1, %0, %1, c14" : "=r" (cvall), "=r" (cvalh));
return ((u64) cvalh << 32) | cvall;
}
static cycle_t arch_counter_read(struct clocksource *cs)
{
return arch_counter_get_cntpct();
}
#ifdef ARCH_HAS_READ_CURRENT_TIMER
int read_current_timer(unsigned long *timer_val)
{
*timer_val = (unsigned long)arch_counter_get_cntpct();
return 0;
}
#endif
static struct clocksource clocksource_counter = {
.name = "arch_sys_counter",
.rating = 400,
.read = arch_counter_read,
.mask = CLOCKSOURCE_MASK(56),
.flags = CLOCK_SOURCE_IS_CONTINUOUS,
};
static u32 arch_counter_get_cntvct32(void)
{
cycle_t cntvct;
cntvct = arch_counter_get_cntvct();
/*
* The sched_clock infrastructure only knows about counters
* with at most 32bits. Forget about the upper 24 bits for the
* time being...
*/
return (u32)(cntvct & (u32)~0);
}
unsigned long long notrace sched_clock(void)
{
return cyc_to_sched_clock(&cd, arch_counter_get_cntvct32(), (u32)~0);
}
static void notrace arch_timer_update_sched_clock(void)
{
update_sched_clock(&cd, arch_counter_get_cntvct32(), (u32)~0);
}
static void __cpuinit arch_timer_teardown(void *data)
{
struct clock_event_device *clk = data;
pr_debug("arch_timer_teardown disable IRQ%d cpu #%d\n",
clk->irq, smp_processor_id());
disable_percpu_irq(arch_timer_ppi);
if (arch_timer_ppi2 > 0)
disable_percpu_irq(arch_timer_ppi2);
arch_timer_set_mode(CLOCK_EVT_MODE_UNUSED, clk);
}
static int __cpuinit arch_timer_cpu_notify(struct notifier_block *self,
unsigned long action, void *data)
{
int cpu = (int)data;
struct clock_event_device *clk = per_cpu_ptr(arch_timer_evt, cpu);
switch(action) {
case CPU_ONLINE:
case CPU_ONLINE_FROZEN:
smp_call_function_single(cpu, arch_timer_setup, clk, 1);
break;
case CPU_DOWN_PREPARE:
case CPU_DOWN_PREPARE_FROZEN:
smp_call_function_single(cpu, arch_timer_teardown, clk, 1);
break;
}
return NOTIFY_OK;
}
static struct notifier_block __cpuinitdata arch_timer_cpu_nb = {
.notifier_call = arch_timer_cpu_notify,
};
int __init arch_timer_register(struct resource *res, int res_nr)
{
int err;
if (!res_nr || res[0].start < 0 || !(res[0].flags & IORESOURCE_IRQ))
return -EINVAL;
err = arch_timer_available();
if (err)
return err;
arch_timer_evt = alloc_percpu(struct clock_event_device);
if (!arch_timer_evt)
return -ENOMEM;
arch_timer_ppi = res[0].start;
if (res_nr > 1 && (res[1].flags & IORESOURCE_IRQ))
arch_timer_ppi2 = res[1].start;
clocksource_register_hz(&clocksource_counter, arch_timer_rate);
init_sched_clock(&cd, arch_timer_update_sched_clock, 32,
arch_timer_rate);
#ifdef ARCH_HAS_READ_CURRENT_TIMER
set_delay_fn(read_current_timer_delay_loop);
#endif
err = request_percpu_irq(arch_timer_ppi, arch_timer_handler,
"arch_sys_timer", arch_timer_evt);
if (err) {
pr_err("%s: can't register interrupt %d (%d)\n",
"arch_sys_timer", arch_timer_ppi, err);
return err;
}
if (arch_timer_ppi2 > 0) {
err = request_percpu_irq(arch_timer_ppi2, arch_timer_handler,
"arch_sys_timer", arch_timer_evt);
if (err)
pr_warn("%s: can't register interrupt %d (%d)\n",
"arch_sys_timer", arch_timer_ppi2, err);
}
/* Immediately configure the timer on the boot CPU */
arch_timer_setup(per_cpu_ptr(arch_timer_evt, smp_processor_id()));
register_cpu_notifier(&arch_timer_cpu_nb);
return 0;
}