linux/arch/mips/cavium-octeon/csrc-octeon.c
<<
>>
Prefs
   1/*
   2 * This file is subject to the terms and conditions of the GNU General Public
   3 * License.  See the file "COPYING" in the main directory of this archive
   4 * for more details.
   5 *
   6 * Copyright (C) 2007 by Ralf Baechle
   7 * Copyright (C) 2009, 2010 Cavium Networks, Inc.
   8 */
   9#include <linux/clocksource.h>
  10#include <linux/init.h>
  11#include <linux/smp.h>
  12
  13#include <asm/cpu-info.h>
  14#include <asm/time.h>
  15
  16#include <asm/octeon/octeon.h>
  17#include <asm/octeon/cvmx-ipd-defs.h>
  18#include <asm/octeon/cvmx-mio-defs.h>
  19
  20/*
  21 * Set the current core's cvmcount counter to the value of the
  22 * IPD_CLK_COUNT.  We do this on all cores as they are brought
  23 * on-line.  This allows for a read from a local cpu register to
  24 * access a synchronized counter.
  25 *
  26 * On CPU_CAVIUM_OCTEON2 the IPD_CLK_COUNT is scaled by rdiv/sdiv.
  27 */
  28void octeon_init_cvmcount(void)
  29{
  30        unsigned long flags;
  31        unsigned loops = 2;
  32        u64 f = 0;
  33        u64 rdiv = 0;
  34        u64 sdiv = 0;
  35        if (current_cpu_type() == CPU_CAVIUM_OCTEON2) {
  36                union cvmx_mio_rst_boot rst_boot;
  37                rst_boot.u64 = cvmx_read_csr(CVMX_MIO_RST_BOOT);
  38                rdiv = rst_boot.s.c_mul;        /* CPU clock */
  39                sdiv = rst_boot.s.pnr_mul;      /* I/O clock */
  40                f = (0x8000000000000000ull / sdiv) * 2;
  41        }
  42
  43
  44        /* Clobber loops so GCC will not unroll the following while loop. */
  45        asm("" : "+r" (loops));
  46
  47        local_irq_save(flags);
  48        /*
  49         * Loop several times so we are executing from the cache,
  50         * which should give more deterministic timing.
  51         */
  52        while (loops--) {
  53                u64 ipd_clk_count = cvmx_read_csr(CVMX_IPD_CLK_COUNT);
  54                if (rdiv != 0) {
  55                        ipd_clk_count *= rdiv;
  56                        if (f != 0) {
  57                                asm("dmultu\t%[cnt],%[f]\n\t"
  58                                    "mfhi\t%[cnt]"
  59                                    : [cnt] "+r" (ipd_clk_count),
  60                                      [f] "=r" (f)
  61                                    : : "hi", "lo");
  62                        }
  63                }
  64                write_c0_cvmcount(ipd_clk_count);
  65        }
  66        local_irq_restore(flags);
  67}
  68
  69static cycle_t octeon_cvmcount_read(struct clocksource *cs)
  70{
  71        return read_c0_cvmcount();
  72}
  73
  74static struct clocksource clocksource_mips = {
  75        .name           = "OCTEON_CVMCOUNT",
  76        .read           = octeon_cvmcount_read,
  77        .mask           = CLOCKSOURCE_MASK(64),
  78        .flags          = CLOCK_SOURCE_IS_CONTINUOUS,
  79};
  80
  81unsigned long long notrace sched_clock(void)
  82{
  83        /* 64-bit arithmatic can overflow, so use 128-bit.  */
  84        u64 t1, t2, t3;
  85        unsigned long long rv;
  86        u64 mult = clocksource_mips.mult;
  87        u64 shift = clocksource_mips.shift;
  88        u64 cnt = read_c0_cvmcount();
  89
  90        asm (
  91                "dmultu\t%[cnt],%[mult]\n\t"
  92                "nor\t%[t1],$0,%[shift]\n\t"
  93                "mfhi\t%[t2]\n\t"
  94                "mflo\t%[t3]\n\t"
  95                "dsll\t%[t2],%[t2],1\n\t"
  96                "dsrlv\t%[rv],%[t3],%[shift]\n\t"
  97                "dsllv\t%[t1],%[t2],%[t1]\n\t"
  98                "or\t%[rv],%[t1],%[rv]\n\t"
  99                : [rv] "=&r" (rv), [t1] "=&r" (t1), [t2] "=&r" (t2), [t3] "=&r" (t3)
 100                : [cnt] "r" (cnt), [mult] "r" (mult), [shift] "r" (shift)
 101                : "hi", "lo");
 102        return rv;
 103}
 104
 105void __init plat_time_init(void)
 106{
 107        clocksource_mips.rating = 300;
 108        clocksource_set_clock(&clocksource_mips, octeon_get_clock_rate());
 109        clocksource_register(&clocksource_mips);
 110}
 111
 112static u64 octeon_udelay_factor;
 113static u64 octeon_ndelay_factor;
 114
 115void __init octeon_setup_delays(void)
 116{
 117        octeon_udelay_factor = octeon_get_clock_rate() / 1000000;
 118        /*
 119         * For __ndelay we divide by 2^16, so the factor is multiplied
 120         * by the same amount.
 121         */
 122        octeon_ndelay_factor = (octeon_udelay_factor * 0x10000ull) / 1000ull;
 123
 124        preset_lpj = octeon_get_clock_rate() / HZ;
 125}
 126
 127void __udelay(unsigned long us)
 128{
 129        u64 cur, end, inc;
 130
 131        cur = read_c0_cvmcount();
 132
 133        inc = us * octeon_udelay_factor;
 134        end = cur + inc;
 135
 136        while (end > cur)
 137                cur = read_c0_cvmcount();
 138}
 139EXPORT_SYMBOL(__udelay);
 140
 141void __ndelay(unsigned long ns)
 142{
 143        u64 cur, end, inc;
 144
 145        cur = read_c0_cvmcount();
 146
 147        inc = ((ns * octeon_ndelay_factor) >> 16);
 148        end = cur + inc;
 149
 150        while (end > cur)
 151                cur = read_c0_cvmcount();
 152}
 153EXPORT_SYMBOL(__ndelay);
 154
 155void __delay(unsigned long loops)
 156{
 157        u64 cur, end;
 158
 159        cur = read_c0_cvmcount();
 160        end = cur + loops;
 161
 162        while (end > cur)
 163                cur = read_c0_cvmcount();
 164}
 165EXPORT_SYMBOL(__delay);
 166