linux/arch/arm/plat-omap/counter_32k.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-only
   2/*
   3 * OMAP 32ksynctimer/counter_32k-related code
   4 *
   5 * Copyright (C) 2009 Texas Instruments
   6 * Copyright (C) 2010 Nokia Corporation
   7 * Tony Lindgren <tony@atomide.com>
   8 * Added OMAP4 support - Santosh Shilimkar <santosh.shilimkar@ti.com>
   9 *
  10 * NOTE: This timer is not the same timer as the old OMAP1 MPU timer.
  11 */
  12#include <linux/kernel.h>
  13#include <linux/init.h>
  14#include <linux/clk.h>
  15#include <linux/err.h>
  16#include <linux/io.h>
  17#include <linux/clocksource.h>
  18#include <linux/sched_clock.h>
  19
  20#include <asm/mach/time.h>
  21
  22#include <plat/counter-32k.h>
  23
  24/* OMAP2_32KSYNCNT_CR_OFF: offset of 32ksync counter register */
  25#define OMAP2_32KSYNCNT_REV_OFF         0x0
  26#define OMAP2_32KSYNCNT_REV_SCHEME      (0x3 << 30)
  27#define OMAP2_32KSYNCNT_CR_OFF_LOW      0x10
  28#define OMAP2_32KSYNCNT_CR_OFF_HIGH     0x30
  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 */
  36static void __iomem *sync32k_cnt_reg;
  37
  38static u64 notrace omap_32k_read_sched_clock(void)
  39{
  40        return sync32k_cnt_reg ? readl_relaxed(sync32k_cnt_reg) : 0;
  41}
  42
  43/**
  44 * omap_read_persistent_clock64 -  Return time from a persistent clock.
  45 *
  46 * Reads the time from a source which isn't disabled during PM, the
  47 * 32k sync timer.  Convert the cycles elapsed since last read into
  48 * nsecs and adds to a monotonically increasing timespec64.
  49 */
  50static struct timespec64 persistent_ts;
  51static cycles_t cycles;
  52static unsigned int persistent_mult, persistent_shift;
  53
  54static void omap_read_persistent_clock64(struct timespec64 *ts)
  55{
  56        unsigned long long nsecs;
  57        cycles_t last_cycles;
  58
  59        last_cycles = cycles;
  60        cycles = sync32k_cnt_reg ? readl_relaxed(sync32k_cnt_reg) : 0;
  61
  62        nsecs = clocksource_cyc2ns(cycles - last_cycles,
  63                                        persistent_mult, persistent_shift);
  64
  65        timespec64_add_ns(&persistent_ts, nsecs);
  66
  67        *ts = persistent_ts;
  68}
  69
  70/**
  71 * omap_init_clocksource_32k - setup and register counter 32k as a
  72 * kernel clocksource
  73 * @pbase: base addr of counter_32k module
  74 * @size: size of counter_32k to map
  75 *
  76 * Returns 0 upon success or negative error code upon failure.
  77 *
  78 */
  79int __init omap_init_clocksource_32k(void __iomem *vbase)
  80{
  81        int ret;
  82
  83        /*
  84         * 32k sync Counter IP register offsets vary between the
  85         * highlander version and the legacy ones.
  86         * The 'SCHEME' bits(30-31) of the revision register is used
  87         * to identify the version.
  88         */
  89        if (readl_relaxed(vbase + OMAP2_32KSYNCNT_REV_OFF) &
  90                                                OMAP2_32KSYNCNT_REV_SCHEME)
  91                sync32k_cnt_reg = vbase + OMAP2_32KSYNCNT_CR_OFF_HIGH;
  92        else
  93                sync32k_cnt_reg = vbase + OMAP2_32KSYNCNT_CR_OFF_LOW;
  94
  95        /*
  96         * 120000 rough estimate from the calculations in
  97         * __clocksource_update_freq_scale.
  98         */
  99        clocks_calc_mult_shift(&persistent_mult, &persistent_shift,
 100                        32768, NSEC_PER_SEC, 120000);
 101
 102        ret = clocksource_mmio_init(sync32k_cnt_reg, "32k_counter", 32768,
 103                                250, 32, clocksource_mmio_readl_up);
 104        if (ret) {
 105                pr_err("32k_counter: can't register clocksource\n");
 106                return ret;
 107        }
 108
 109        sched_clock_register(omap_32k_read_sched_clock, 32, 32768);
 110        register_persistent_clock(omap_read_persistent_clock64);
 111        pr_info("OMAP clocksource: 32k_counter at 32768 Hz\n");
 112
 113        return 0;
 114}
 115