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