linux/arch/mips/loongson2ef/common/cs5536/cs5536_mfgpt.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-or-later
   2/*
   3 * CS5536 General timer functions
   4 *
   5 * Copyright (C) 2007 Lemote Inc. & Institute of Computing Technology
   6 * Author: Yanhua, yanh@lemote.com
   7 *
   8 * Copyright (C) 2009 Lemote Inc.
   9 * Author: Wu zhangjin, wuzhangjin@gmail.com
  10 *
  11 * Reference: AMD Geode(TM) CS5536 Companion Device Data Book
  12 */
  13
  14#include <linux/io.h>
  15#include <linux/init.h>
  16#include <linux/export.h>
  17#include <linux/jiffies.h>
  18#include <linux/spinlock.h>
  19#include <linux/interrupt.h>
  20#include <linux/clockchips.h>
  21
  22#include <asm/time.h>
  23
  24#include <cs5536/cs5536_mfgpt.h>
  25
  26static DEFINE_RAW_SPINLOCK(mfgpt_lock);
  27
  28static u32 mfgpt_base;
  29
  30/*
  31 * Initialize the MFGPT timer.
  32 *
  33 * This is also called after resume to bring the MFGPT into operation again.
  34 */
  35
  36/* disable counter */
  37void disable_mfgpt0_counter(void)
  38{
  39        outw(inw(MFGPT0_SETUP) & 0x7fff, MFGPT0_SETUP);
  40}
  41EXPORT_SYMBOL(disable_mfgpt0_counter);
  42
  43/* enable counter, comparator2 to event mode, 14.318MHz clock */
  44void enable_mfgpt0_counter(void)
  45{
  46        outw(0xe310, MFGPT0_SETUP);
  47}
  48EXPORT_SYMBOL(enable_mfgpt0_counter);
  49
  50static int mfgpt_timer_set_periodic(struct clock_event_device *evt)
  51{
  52        raw_spin_lock(&mfgpt_lock);
  53
  54        outw(COMPARE, MFGPT0_CMP2);     /* set comparator2 */
  55        outw(0, MFGPT0_CNT);            /* set counter to 0 */
  56        enable_mfgpt0_counter();
  57
  58        raw_spin_unlock(&mfgpt_lock);
  59        return 0;
  60}
  61
  62static int mfgpt_timer_shutdown(struct clock_event_device *evt)
  63{
  64        if (clockevent_state_periodic(evt) || clockevent_state_oneshot(evt)) {
  65                raw_spin_lock(&mfgpt_lock);
  66                disable_mfgpt0_counter();
  67                raw_spin_unlock(&mfgpt_lock);
  68        }
  69
  70        return 0;
  71}
  72
  73static struct clock_event_device mfgpt_clockevent = {
  74        .name = "mfgpt",
  75        .features = CLOCK_EVT_FEAT_PERIODIC,
  76
  77        /* The oneshot mode have very high deviation, don't use it! */
  78        .set_state_shutdown = mfgpt_timer_shutdown,
  79        .set_state_periodic = mfgpt_timer_set_periodic,
  80        .irq = CS5536_MFGPT_INTR,
  81};
  82
  83static irqreturn_t timer_interrupt(int irq, void *dev_id)
  84{
  85        u32 basehi;
  86
  87        /*
  88         * get MFGPT base address
  89         *
  90         * NOTE: do not remove me, it's need for the value of mfgpt_base is
  91         * variable
  92         */
  93        _rdmsr(DIVIL_MSR_REG(DIVIL_LBAR_MFGPT), &basehi, &mfgpt_base);
  94
  95        /* ack */
  96        outw(inw(MFGPT0_SETUP) | 0x4000, MFGPT0_SETUP);
  97
  98        mfgpt_clockevent.event_handler(&mfgpt_clockevent);
  99
 100        return IRQ_HANDLED;
 101}
 102
 103/*
 104 * Initialize the conversion factor and the min/max deltas of the clock event
 105 * structure and register the clock event source with the framework.
 106 */
 107void __init setup_mfgpt0_timer(void)
 108{
 109        u32 basehi;
 110        struct clock_event_device *cd = &mfgpt_clockevent;
 111        unsigned int cpu = smp_processor_id();
 112
 113        cd->cpumask = cpumask_of(cpu);
 114        clockevent_set_clock(cd, MFGPT_TICK_RATE);
 115        cd->max_delta_ns = clockevent_delta2ns(0xffff, cd);
 116        cd->max_delta_ticks = 0xffff;
 117        cd->min_delta_ns = clockevent_delta2ns(0xf, cd);
 118        cd->min_delta_ticks = 0xf;
 119
 120        /* Enable MFGPT0 Comparator 2 Output to the Interrupt Mapper */
 121        _wrmsr(DIVIL_MSR_REG(MFGPT_IRQ), 0, 0x100);
 122
 123        /* Enable Interrupt Gate 5 */
 124        _wrmsr(DIVIL_MSR_REG(PIC_ZSEL_LOW), 0, 0x50000);
 125
 126        /* get MFGPT base address */
 127        _rdmsr(DIVIL_MSR_REG(DIVIL_LBAR_MFGPT), &basehi, &mfgpt_base);
 128
 129        clockevents_register_device(cd);
 130
 131        if (request_irq(CS5536_MFGPT_INTR, timer_interrupt,
 132                        IRQF_NOBALANCING | IRQF_TIMER, "timer", NULL))
 133                pr_err("Failed to register timer interrupt\n");
 134}
 135
 136/*
 137 * Since the MFGPT overflows every tick, its not very useful
 138 * to just read by itself. So use jiffies to emulate a free
 139 * running counter:
 140 */
 141static u64 mfgpt_read(struct clocksource *cs)
 142{
 143        unsigned long flags;
 144        int count;
 145        u32 jifs;
 146        static int old_count;
 147        static u32 old_jifs;
 148
 149        raw_spin_lock_irqsave(&mfgpt_lock, flags);
 150        /*
 151         * Although our caller may have the read side of xtime_lock,
 152         * this is now a seqlock, and we are cheating in this routine
 153         * by having side effects on state that we cannot undo if
 154         * there is a collision on the seqlock and our caller has to
 155         * retry.  (Namely, old_jifs and old_count.)  So we must treat
 156         * jiffies as volatile despite the lock.  We read jiffies
 157         * before latching the timer count to guarantee that although
 158         * the jiffies value might be older than the count (that is,
 159         * the counter may underflow between the last point where
 160         * jiffies was incremented and the point where we latch the
 161         * count), it cannot be newer.
 162         */
 163        jifs = jiffies;
 164        /* read the count */
 165        count = inw(MFGPT0_CNT);
 166
 167        /*
 168         * It's possible for count to appear to go the wrong way for this
 169         * reason:
 170         *
 171         *  The timer counter underflows, but we haven't handled the resulting
 172         *  interrupt and incremented jiffies yet.
 173         *
 174         * Previous attempts to handle these cases intelligently were buggy, so
 175         * we just do the simple thing now.
 176         */
 177        if (count < old_count && jifs == old_jifs)
 178                count = old_count;
 179
 180        old_count = count;
 181        old_jifs = jifs;
 182
 183        raw_spin_unlock_irqrestore(&mfgpt_lock, flags);
 184
 185        return (u64) (jifs * COMPARE) + count;
 186}
 187
 188static struct clocksource clocksource_mfgpt = {
 189        .name = "mfgpt",
 190        .rating = 120, /* Functional for real use, but not desired */
 191        .read = mfgpt_read,
 192        .mask = CLOCKSOURCE_MASK(32),
 193};
 194
 195int __init init_mfgpt_clocksource(void)
 196{
 197        if (num_possible_cpus() > 1)    /* MFGPT does not scale! */
 198                return 0;
 199
 200        return clocksource_register_hz(&clocksource_mfgpt, MFGPT_TICK_RATE);
 201}
 202
 203arch_initcall(init_mfgpt_clocksource);
 204