linux/arch/mips/jz4740/time.c
<<
>>
Prefs
   1/*
   2 *  Copyright (C) 2010, Lars-Peter Clausen <lars@metafoo.de>
   3 *  JZ4740 platform time support
   4 *
   5 *  This program is free software; you can redistribute it and/or modify it
   6 *  under  the terms of the GNU General  Public License as published by the
   7 *  Free Software Foundation;  either version 2 of the License, or (at your
   8 *  option) any later version.
   9 *
  10 *  You should have received a copy of the GNU General Public License along
  11 *  with this program; if not, write to the Free Software Foundation, Inc.,
  12 *  675 Mass Ave, Cambridge, MA 02139, USA.
  13 *
  14 */
  15
  16#include <linux/clk.h>
  17#include <linux/clk-provider.h>
  18#include <linux/interrupt.h>
  19#include <linux/kernel.h>
  20#include <linux/time.h>
  21
  22#include <linux/clockchips.h>
  23#include <linux/sched_clock.h>
  24
  25#include <asm/mach-jz4740/clock.h>
  26#include <asm/mach-jz4740/irq.h>
  27#include <asm/mach-jz4740/timer.h>
  28#include <asm/time.h>
  29
  30#include "clock.h"
  31
  32#define TIMER_CLOCKEVENT 0
  33#define TIMER_CLOCKSOURCE 1
  34
  35static uint16_t jz4740_jiffies_per_tick;
  36
  37static u64 jz4740_clocksource_read(struct clocksource *cs)
  38{
  39        return jz4740_timer_get_count(TIMER_CLOCKSOURCE);
  40}
  41
  42static struct clocksource jz4740_clocksource = {
  43        .name = "jz4740-timer",
  44        .rating = 200,
  45        .read = jz4740_clocksource_read,
  46        .mask = CLOCKSOURCE_MASK(16),
  47        .flags = CLOCK_SOURCE_IS_CONTINUOUS,
  48};
  49
  50static u64 notrace jz4740_read_sched_clock(void)
  51{
  52        return jz4740_timer_get_count(TIMER_CLOCKSOURCE);
  53}
  54
  55static irqreturn_t jz4740_clockevent_irq(int irq, void *devid)
  56{
  57        struct clock_event_device *cd = devid;
  58
  59        jz4740_timer_ack_full(TIMER_CLOCKEVENT);
  60
  61        if (!clockevent_state_periodic(cd))
  62                jz4740_timer_disable(TIMER_CLOCKEVENT);
  63
  64        cd->event_handler(cd);
  65
  66        return IRQ_HANDLED;
  67}
  68
  69static int jz4740_clockevent_set_periodic(struct clock_event_device *evt)
  70{
  71        jz4740_timer_set_count(TIMER_CLOCKEVENT, 0);
  72        jz4740_timer_set_period(TIMER_CLOCKEVENT, jz4740_jiffies_per_tick);
  73        jz4740_timer_irq_full_enable(TIMER_CLOCKEVENT);
  74        jz4740_timer_enable(TIMER_CLOCKEVENT);
  75
  76        return 0;
  77}
  78
  79static int jz4740_clockevent_resume(struct clock_event_device *evt)
  80{
  81        jz4740_timer_irq_full_enable(TIMER_CLOCKEVENT);
  82        jz4740_timer_enable(TIMER_CLOCKEVENT);
  83
  84        return 0;
  85}
  86
  87static int jz4740_clockevent_shutdown(struct clock_event_device *evt)
  88{
  89        jz4740_timer_disable(TIMER_CLOCKEVENT);
  90
  91        return 0;
  92}
  93
  94static int jz4740_clockevent_set_next(unsigned long evt,
  95        struct clock_event_device *cd)
  96{
  97        jz4740_timer_set_count(TIMER_CLOCKEVENT, 0);
  98        jz4740_timer_set_period(TIMER_CLOCKEVENT, evt);
  99        jz4740_timer_enable(TIMER_CLOCKEVENT);
 100
 101        return 0;
 102}
 103
 104static struct clock_event_device jz4740_clockevent = {
 105        .name = "jz4740-timer",
 106        .features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT,
 107        .set_next_event = jz4740_clockevent_set_next,
 108        .set_state_shutdown = jz4740_clockevent_shutdown,
 109        .set_state_periodic = jz4740_clockevent_set_periodic,
 110        .set_state_oneshot = jz4740_clockevent_shutdown,
 111        .tick_resume = jz4740_clockevent_resume,
 112        .rating = 200,
 113#ifdef CONFIG_MACH_JZ4740
 114        .irq = JZ4740_IRQ_TCU0,
 115#endif
 116#if defined(CONFIG_MACH_JZ4770) || defined(CONFIG_MACH_JZ4780)
 117        .irq = JZ4780_IRQ_TCU2,
 118#endif
 119};
 120
 121static struct irqaction timer_irqaction = {
 122        .handler        = jz4740_clockevent_irq,
 123        .flags          = IRQF_PERCPU | IRQF_TIMER,
 124        .name           = "jz4740-timerirq",
 125        .dev_id         = &jz4740_clockevent,
 126};
 127
 128void __init plat_time_init(void)
 129{
 130        int ret;
 131        uint32_t clk_rate;
 132        uint16_t ctrl;
 133        struct clk *ext_clk;
 134
 135        of_clk_init(NULL);
 136        jz4740_timer_init();
 137
 138        ext_clk = clk_get(NULL, "ext");
 139        if (IS_ERR(ext_clk))
 140                panic("unable to get ext clock");
 141        clk_rate = clk_get_rate(ext_clk) >> 4;
 142        clk_put(ext_clk);
 143
 144        jz4740_jiffies_per_tick = DIV_ROUND_CLOSEST(clk_rate, HZ);
 145
 146        clockevent_set_clock(&jz4740_clockevent, clk_rate);
 147        jz4740_clockevent.min_delta_ns = clockevent_delta2ns(100, &jz4740_clockevent);
 148        jz4740_clockevent.min_delta_ticks = 100;
 149        jz4740_clockevent.max_delta_ns = clockevent_delta2ns(0xffff, &jz4740_clockevent);
 150        jz4740_clockevent.max_delta_ticks = 0xffff;
 151        jz4740_clockevent.cpumask = cpumask_of(0);
 152
 153        clockevents_register_device(&jz4740_clockevent);
 154
 155        ret = clocksource_register_hz(&jz4740_clocksource, clk_rate);
 156
 157        if (ret)
 158                printk(KERN_ERR "Failed to register clocksource: %d\n", ret);
 159
 160        sched_clock_register(jz4740_read_sched_clock, 16, clk_rate);
 161
 162        setup_irq(jz4740_clockevent.irq, &timer_irqaction);
 163
 164        ctrl = JZ_TIMER_CTRL_PRESCALE_16 | JZ_TIMER_CTRL_SRC_EXT;
 165
 166        jz4740_timer_set_ctrl(TIMER_CLOCKEVENT, ctrl);
 167        jz4740_timer_set_ctrl(TIMER_CLOCKSOURCE, ctrl);
 168
 169        jz4740_timer_set_period(TIMER_CLOCKEVENT, jz4740_jiffies_per_tick);
 170        jz4740_timer_irq_full_enable(TIMER_CLOCKEVENT);
 171
 172        jz4740_timer_set_period(TIMER_CLOCKSOURCE, 0xffff);
 173
 174        jz4740_timer_enable(TIMER_CLOCKEVENT);
 175        jz4740_timer_enable(TIMER_CLOCKSOURCE);
 176}
 177