linux/drivers/clocksource/arm_generic.c
<<
>>
Prefs
   1/*
   2 * Generic timers support
   3 *
   4 * Copyright (C) 2012 ARM Ltd.
   5 * Author: Marc Zyngier <marc.zyngier@arm.com>
   6 *
   7 * This program is free software; you can redistribute it and/or modify
   8 * it under the terms of the GNU General Public License version 2 as
   9 * published by the Free Software Foundation.
  10 *
  11 * This program is distributed in the hope that it will be useful,
  12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  14 * GNU General Public License for more details.
  15 *
  16 * You should have received a copy of the GNU General Public License
  17 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  18 */
  19
  20#include <linux/init.h>
  21#include <linux/kernel.h>
  22#include <linux/delay.h>
  23#include <linux/device.h>
  24#include <linux/smp.h>
  25#include <linux/cpu.h>
  26#include <linux/jiffies.h>
  27#include <linux/interrupt.h>
  28#include <linux/clockchips.h>
  29#include <linux/of_irq.h>
  30#include <linux/io.h>
  31
  32#include <clocksource/arm_generic.h>
  33
  34#include <asm/arm_generic.h>
  35
  36static u32 arch_timer_rate;
  37static u64 sched_clock_mult __read_mostly;
  38static DEFINE_PER_CPU(struct clock_event_device, arch_timer_evt);
  39static int arch_timer_ppi;
  40
  41static irqreturn_t arch_timer_handle_irq(int irq, void *dev_id)
  42{
  43        struct clock_event_device *evt = dev_id;
  44        unsigned long ctrl;
  45
  46        ctrl = arch_timer_reg_read(ARCH_TIMER_REG_CTRL);
  47        if (ctrl & ARCH_TIMER_CTRL_ISTATUS) {
  48                ctrl |= ARCH_TIMER_CTRL_IMASK;
  49                arch_timer_reg_write(ARCH_TIMER_REG_CTRL, ctrl);
  50                evt->event_handler(evt);
  51                return IRQ_HANDLED;
  52        }
  53
  54        return IRQ_NONE;
  55}
  56
  57static void arch_timer_stop(void)
  58{
  59        unsigned long ctrl;
  60
  61        ctrl = arch_timer_reg_read(ARCH_TIMER_REG_CTRL);
  62        ctrl &= ~ARCH_TIMER_CTRL_ENABLE;
  63        arch_timer_reg_write(ARCH_TIMER_REG_CTRL, ctrl);
  64}
  65
  66static void arch_timer_set_mode(enum clock_event_mode mode,
  67                                struct clock_event_device *clk)
  68{
  69        switch (mode) {
  70        case CLOCK_EVT_MODE_UNUSED:
  71        case CLOCK_EVT_MODE_SHUTDOWN:
  72                arch_timer_stop();
  73                break;
  74        default:
  75                break;
  76        }
  77}
  78
  79static int arch_timer_set_next_event(unsigned long evt,
  80                                     struct clock_event_device *unused)
  81{
  82        unsigned long ctrl;
  83
  84        ctrl = arch_timer_reg_read(ARCH_TIMER_REG_CTRL);
  85        ctrl |= ARCH_TIMER_CTRL_ENABLE;
  86        ctrl &= ~ARCH_TIMER_CTRL_IMASK;
  87
  88        arch_timer_reg_write(ARCH_TIMER_REG_TVAL, evt);
  89        arch_timer_reg_write(ARCH_TIMER_REG_CTRL, ctrl);
  90
  91        return 0;
  92}
  93
  94static void __cpuinit arch_timer_setup(struct clock_event_device *clk)
  95{
  96        /* Let's make sure the timer is off before doing anything else */
  97        arch_timer_stop();
  98
  99        clk->features = CLOCK_EVT_FEAT_ONESHOT | CLOCK_EVT_FEAT_C3STOP;
 100        clk->name = "arch_sys_timer";
 101        clk->rating = 400;
 102        clk->set_mode = arch_timer_set_mode;
 103        clk->set_next_event = arch_timer_set_next_event;
 104        clk->irq = arch_timer_ppi;
 105        clk->cpumask = cpumask_of(smp_processor_id());
 106
 107        clockevents_config_and_register(clk, arch_timer_rate,
 108                                        0xf, 0x7fffffff);
 109
 110        enable_percpu_irq(clk->irq, 0);
 111
 112        /* Ensure the physical counter is visible to userspace for the vDSO. */
 113        arch_counter_enable_user_access();
 114}
 115
 116static void __init arch_timer_calibrate(void)
 117{
 118        if (arch_timer_rate == 0) {
 119                arch_timer_reg_write(ARCH_TIMER_REG_CTRL, 0);
 120                arch_timer_rate = arch_timer_reg_read(ARCH_TIMER_REG_FREQ);
 121
 122                /* Check the timer frequency. */
 123                if (arch_timer_rate == 0)
 124                        panic("Architected timer frequency is set to zero.\n"
 125                              "You must set this in your .dts file\n");
 126        }
 127
 128        /* Cache the sched_clock multiplier to save a divide in the hot path. */
 129
 130        sched_clock_mult = NSEC_PER_SEC / arch_timer_rate;
 131
 132        pr_info("Architected local timer running at %u.%02uMHz.\n",
 133                 arch_timer_rate / 1000000, (arch_timer_rate / 10000) % 100);
 134}
 135
 136static cycle_t arch_counter_read(struct clocksource *cs)
 137{
 138        return arch_counter_get_cntpct();
 139}
 140
 141static struct clocksource clocksource_counter = {
 142        .name   = "arch_sys_counter",
 143        .rating = 400,
 144        .read   = arch_counter_read,
 145        .mask   = CLOCKSOURCE_MASK(56),
 146        .flags  = (CLOCK_SOURCE_IS_CONTINUOUS | CLOCK_SOURCE_VALID_FOR_HRES),
 147};
 148
 149int read_current_timer(unsigned long *timer_value)
 150{
 151        *timer_value = arch_counter_get_cntpct();
 152        return 0;
 153}
 154
 155unsigned long long notrace sched_clock(void)
 156{
 157        return arch_counter_get_cntvct() * sched_clock_mult;
 158}
 159
 160static int __cpuinit arch_timer_cpu_notify(struct notifier_block *self,
 161                                           unsigned long action, void *hcpu)
 162{
 163        int cpu = (long)hcpu;
 164        struct clock_event_device *clk = per_cpu_ptr(&arch_timer_evt, cpu);
 165
 166        switch(action) {
 167        case CPU_STARTING:
 168        case CPU_STARTING_FROZEN:
 169                arch_timer_setup(clk);
 170                break;
 171
 172        case CPU_DYING:
 173        case CPU_DYING_FROZEN:
 174                pr_debug("arch_timer_teardown disable IRQ%d cpu #%d\n",
 175                         clk->irq, cpu);
 176                disable_percpu_irq(clk->irq);
 177                arch_timer_set_mode(CLOCK_EVT_MODE_UNUSED, clk);
 178                break;
 179        }
 180
 181        return NOTIFY_OK;
 182}
 183
 184static struct notifier_block __cpuinitdata arch_timer_cpu_nb = {
 185        .notifier_call = arch_timer_cpu_notify,
 186};
 187
 188static const struct of_device_id arch_timer_of_match[] __initconst = {
 189        { .compatible = "arm,armv8-timer" },
 190        {},
 191};
 192
 193int __init arm_generic_timer_init(void)
 194{
 195        struct device_node *np;
 196        int err;
 197        u32 freq;
 198
 199        np = of_find_matching_node(NULL, arch_timer_of_match);
 200        if (!np) {
 201                pr_err("arch_timer: can't find DT node\n");
 202                return -ENODEV;
 203        }
 204
 205        /* Try to determine the frequency from the device tree or CNTFRQ */
 206        if (!of_property_read_u32(np, "clock-frequency", &freq))
 207                arch_timer_rate = freq;
 208        arch_timer_calibrate();
 209
 210        arch_timer_ppi = irq_of_parse_and_map(np, 0);
 211        pr_info("arch_timer: found %s irq %d\n", np->name, arch_timer_ppi);
 212
 213        err = request_percpu_irq(arch_timer_ppi, arch_timer_handle_irq,
 214                                 np->name, &arch_timer_evt);
 215        if (err) {
 216                pr_err("arch_timer: can't register interrupt %d (%d)\n",
 217                       arch_timer_ppi, err);
 218                return err;
 219        }
 220
 221        clocksource_register_hz(&clocksource_counter, arch_timer_rate);
 222
 223        /* Calibrate the delay loop directly */
 224        lpj_fine = arch_timer_rate / HZ;
 225
 226        /* Immediately configure the timer on the boot CPU */
 227        arch_timer_setup(per_cpu_ptr(&arch_timer_evt, smp_processor_id()));
 228
 229        register_cpu_notifier(&arch_timer_cpu_nb);
 230
 231        return 0;
 232}
 233
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.