linux/arch/arm/plat-omap/counter_32k.c
<<
>>
Prefs
   1/*
   2 * OMAP 32ksynctimer/counter_32k-related code
   3 *
   4 * Copyright (C) 2009 Texas Instruments
   5 * Copyright (C) 2010 Nokia Corporation
   6 * Tony Lindgren <tony@atomide.com>
   7 * Added OMAP4 support - Santosh Shilimkar <santosh.shilimkar@ti.com>
   8 *
   9 * This program is free software; you can redistribute it and/or modify
  10 * it under the terms of the GNU General Public License version 2 as
  11 * published by the Free Software Foundation.
  12 *
  13 * NOTE: This timer is not the same timer as the old OMAP1 MPU timer.
  14 */
  15#include <linux/kernel.h>
  16#include <linux/init.h>
  17#include <linux/clk.h>
  18#include <linux/err.h>
  19#include <linux/io.h>
  20#include <linux/sched.h>
  21
  22#include <asm/sched_clock.h>
  23
  24#include <plat/common.h>
  25#include <plat/board.h>
  26
  27#include <plat/clock.h>
  28
  29
  30/*
  31 * 32KHz clocksource ... always available, on pretty most chips except
  32 * OMAP 730 and 1510.  Other timers could be used as clocksources, with
  33 * higher resolution in free-running counter modes (e.g. 12 MHz xtal),
  34 * but systems won't necessarily want to spend resources that way.
  35 */
  36
  37#define OMAP16XX_TIMER_32K_SYNCHRONIZED         0xfffbc410
  38
  39#include <linux/clocksource.h>
  40
  41/*
  42 * offset_32k holds the init time counter value. It is then subtracted
  43 * from every counter read to achieve a counter that counts time from the
  44 * kernel boot (needed for sched_clock()).
  45 */
  46static u32 offset_32k __read_mostly;
  47
  48#ifdef CONFIG_ARCH_OMAP16XX
  49static cycle_t notrace omap16xx_32k_read(struct clocksource *cs)
  50{
  51        return omap_readl(OMAP16XX_TIMER_32K_SYNCHRONIZED) - offset_32k;
  52}
  53#else
  54#define omap16xx_32k_read       NULL
  55#endif
  56
  57#ifdef CONFIG_SOC_OMAP2420
  58static cycle_t notrace omap2420_32k_read(struct clocksource *cs)
  59{
  60        return omap_readl(OMAP2420_32KSYNCT_BASE + 0x10) - offset_32k;
  61}
  62#else
  63#define omap2420_32k_read       NULL
  64#endif
  65
  66#ifdef CONFIG_SOC_OMAP2430
  67static cycle_t notrace omap2430_32k_read(struct clocksource *cs)
  68{
  69        return omap_readl(OMAP2430_32KSYNCT_BASE + 0x10) - offset_32k;
  70}
  71#else
  72#define omap2430_32k_read       NULL
  73#endif
  74
  75#ifdef CONFIG_ARCH_OMAP3
  76static cycle_t notrace omap34xx_32k_read(struct clocksource *cs)
  77{
  78        return omap_readl(OMAP3430_32KSYNCT_BASE + 0x10) - offset_32k;
  79}
  80#else
  81#define omap34xx_32k_read       NULL
  82#endif
  83
  84#ifdef CONFIG_ARCH_OMAP4
  85static cycle_t notrace omap44xx_32k_read(struct clocksource *cs)
  86{
  87        return omap_readl(OMAP4430_32KSYNCT_BASE + 0x10) - offset_32k;
  88}
  89#else
  90#define omap44xx_32k_read       NULL
  91#endif
  92
  93/*
  94 * Kernel assumes that sched_clock can be called early but may not have
  95 * things ready yet.
  96 */
  97static cycle_t notrace omap_32k_read_dummy(struct clocksource *cs)
  98{
  99        return 0;
 100}
 101
 102static struct clocksource clocksource_32k = {
 103        .name           = "32k_counter",
 104        .rating         = 250,
 105        .read           = omap_32k_read_dummy,
 106        .mask           = CLOCKSOURCE_MASK(32),
 107        .flags          = CLOCK_SOURCE_IS_CONTINUOUS,
 108};
 109
 110/*
 111 * Returns current time from boot in nsecs. It's OK for this to wrap
 112 * around for now, as it's just a relative time stamp.
 113 */
 114static DEFINE_CLOCK_DATA(cd);
 115
 116/*
 117 * Constants generated by clocks_calc_mult_shift(m, s, 32768, NSEC_PER_SEC, 60).
 118 * This gives a resolution of about 30us and a wrap period of about 36hrs.
 119 */
 120#define SC_MULT         4000000000u
 121#define SC_SHIFT        17
 122
 123static inline unsigned long long notrace _omap_32k_sched_clock(void)
 124{
 125        u32 cyc = clocksource_32k.read(&clocksource_32k);
 126        return cyc_to_fixed_sched_clock(&cd, cyc, (u32)~0, SC_MULT, SC_SHIFT);
 127}
 128
 129#ifndef CONFIG_OMAP_MPU_TIMER
 130unsigned long long notrace sched_clock(void)
 131{
 132        return _omap_32k_sched_clock();
 133}
 134#else
 135unsigned long long notrace omap_32k_sched_clock(void)
 136{
 137        return _omap_32k_sched_clock();
 138}
 139#endif
 140
 141static void notrace omap_update_sched_clock(void)
 142{
 143        u32 cyc = clocksource_32k.read(&clocksource_32k);
 144        update_sched_clock(&cd, cyc, (u32)~0);
 145}
 146
 147/**
 148 * read_persistent_clock -  Return time from a persistent clock.
 149 *
 150 * Reads the time from a source which isn't disabled during PM, the
 151 * 32k sync timer.  Convert the cycles elapsed since last read into
 152 * nsecs and adds to a monotonically increasing timespec.
 153 */
 154static struct timespec persistent_ts;
 155static cycles_t cycles, last_cycles;
 156void read_persistent_clock(struct timespec *ts)
 157{
 158        unsigned long long nsecs;
 159        cycles_t delta;
 160        struct timespec *tsp = &persistent_ts;
 161
 162        last_cycles = cycles;
 163        cycles = clocksource_32k.read(&clocksource_32k);
 164        delta = cycles - last_cycles;
 165
 166        nsecs = clocksource_cyc2ns(delta,
 167                                   clocksource_32k.mult, clocksource_32k.shift);
 168
 169        timespec_add_ns(tsp, nsecs);
 170        *ts = *tsp;
 171}
 172
 173int __init omap_init_clocksource_32k(void)
 174{
 175        static char err[] __initdata = KERN_ERR
 176                        "%s: can't register clocksource!\n";
 177
 178        if (cpu_is_omap16xx() || cpu_class_is_omap2()) {
 179                struct clk *sync_32k_ick;
 180
 181                if (cpu_is_omap16xx())
 182                        clocksource_32k.read = omap16xx_32k_read;
 183                else if (cpu_is_omap2420())
 184                        clocksource_32k.read = omap2420_32k_read;
 185                else if (cpu_is_omap2430())
 186                        clocksource_32k.read = omap2430_32k_read;
 187                else if (cpu_is_omap34xx())
 188                        clocksource_32k.read = omap34xx_32k_read;
 189                else if (cpu_is_omap44xx())
 190                        clocksource_32k.read = omap44xx_32k_read;
 191                else
 192                        return -ENODEV;
 193
 194                sync_32k_ick = clk_get(NULL, "omap_32ksync_ick");
 195                if (!IS_ERR(sync_32k_ick))
 196                        clk_enable(sync_32k_ick);
 197
 198                offset_32k = clocksource_32k.read(&clocksource_32k);
 199
 200                if (clocksource_register_hz(&clocksource_32k, 32768))
 201                        printk(err, clocksource_32k.name);
 202
 203                init_fixed_sched_clock(&cd, omap_update_sched_clock, 32,
 204                                       32768, SC_MULT, SC_SHIFT);
 205        }
 206        return 0;
 207}
 208