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/clocksource.h>
  21#include <linux/sched_clock.h>
  22
  23#include <asm/mach/time.h>
  24
  25#include <plat/counter-32k.h>
  26
  27/* OMAP2_32KSYNCNT_CR_OFF: offset of 32ksync counter register */
  28#define OMAP2_32KSYNCNT_REV_OFF         0x0
  29#define OMAP2_32KSYNCNT_REV_SCHEME      (0x3 << 30)
  30#define OMAP2_32KSYNCNT_CR_OFF_LOW      0x10
  31#define OMAP2_32KSYNCNT_CR_OFF_HIGH     0x30
  32
  33/*
  34 * 32KHz clocksource ... always available, on pretty most chips except
  35 * OMAP 730 and 1510.  Other timers could be used as clocksources, with
  36 * higher resolution in free-running counter modes (e.g. 12 MHz xtal),
  37 * but systems won't necessarily want to spend resources that way.
  38 */
  39static void __iomem *sync32k_cnt_reg;
  40
  41static u64 notrace omap_32k_read_sched_clock(void)
  42{
  43        return sync32k_cnt_reg ? readl_relaxed(sync32k_cnt_reg) : 0;
  44}
  45
  46/**
  47 * omap_read_persistent_clock64 -  Return time from a persistent clock.
  48 *
  49 * Reads the time from a source which isn't disabled during PM, the
  50 * 32k sync timer.  Convert the cycles elapsed since last read into
  51 * nsecs and adds to a monotonically increasing timespec64.
  52 */
  53static struct timespec64 persistent_ts;
  54static cycles_t cycles;
  55static unsigned int persistent_mult, persistent_shift;
  56
  57static void omap_read_persistent_clock64(struct timespec64 *ts)
  58{
  59        unsigned long long nsecs;
  60        cycles_t last_cycles;
  61
  62        last_cycles = cycles;
  63        cycles = sync32k_cnt_reg ? readl_relaxed(sync32k_cnt_reg) : 0;
  64
  65        nsecs = clocksource_cyc2ns(cycles - last_cycles,
  66                                        persistent_mult, persistent_shift);
  67
  68        timespec64_add_ns(&persistent_ts, nsecs);
  69
  70        *ts = persistent_ts;
  71}
  72
  73/**
  74 * omap_init_clocksource_32k - setup and register counter 32k as a
  75 * kernel clocksource
  76 * @pbase: base addr of counter_32k module
  77 * @size: size of counter_32k to map
  78 *
  79 * Returns 0 upon success or negative error code upon failure.
  80 *
  81 */
  82int __init omap_init_clocksource_32k(void __iomem *vbase)
  83{
  84        int ret;
  85
  86        /*
  87         * 32k sync Counter IP register offsets vary between the
  88         * highlander version and the legacy ones.
  89         * The 'SCHEME' bits(30-31) of the revision register is used
  90         * to identify the version.
  91         */
  92        if (readl_relaxed(vbase + OMAP2_32KSYNCNT_REV_OFF) &
  93                                                OMAP2_32KSYNCNT_REV_SCHEME)
  94                sync32k_cnt_reg = vbase + OMAP2_32KSYNCNT_CR_OFF_HIGH;
  95        else
  96                sync32k_cnt_reg = vbase + OMAP2_32KSYNCNT_CR_OFF_LOW;
  97
  98        /*
  99         * 120000 rough estimate from the calculations in
 100         * __clocksource_update_freq_scale.
 101         */
 102        clocks_calc_mult_shift(&persistent_mult, &persistent_shift,
 103                        32768, NSEC_PER_SEC, 120000);
 104
 105        ret = clocksource_mmio_init(sync32k_cnt_reg, "32k_counter", 32768,
 106                                250, 32, clocksource_mmio_readl_up);
 107        if (ret) {
 108                pr_err("32k_counter: can't register clocksource\n");
 109                return ret;
 110        }
 111
 112        sched_clock_register(omap_32k_read_sched_clock, 32, 32768);
 113        register_persistent_clock(NULL, omap_read_persistent_clock64);
 114        pr_info("OMAP clocksource: 32k_counter at 32768 Hz\n");
 115
 116        return 0;
 117}
 118