/* * linux/arch/arm/mach-omap2/timer.c * * OMAP2 GP timer support. * * Copyright (C) 2009 Nokia Corporation * * Update to use new clocksource/clockevent layers * Author: Kevin Hilman, MontaVista Software, Inc. <source@mvista.com> * Copyright (C) 2007 MontaVista Software, Inc. * * Original driver: * Copyright (C) 2005 Nokia Corporation * Author: Paul Mundt <paul.mundt@nokia.com> * Juha Yrjölä <juha.yrjola@nokia.com> * OMAP Dual-mode timer framework support by Timo Teras * * Some parts based off of TI's 24xx code: * * Copyright (C) 2004-2009 Texas Instruments, Inc. * * Roughly modelled after the OMAP1 MPU timer code. * Added OMAP4 support - Santosh Shilimkar <santosh.shilimkar@ti.com> * * This file is subject to the terms and conditions of the GNU General Public * License. See the file "COPYING" in the main directory of this archive * for more details. */ #include <linux/clk.h> #include <linux/clocksource.h> #include "soc.h" #include "common.h" #include "control.h" #include "omap-secure.h" #define REALTIME_COUNTER_BASE 0x48243200 #define INCREMENTER_NUMERATOR_OFFSET 0x10 #define INCREMENTER_DENUMERATOR_RELOAD_OFFSET 0x14 #define NUMERATOR_DENUMERATOR_MASK 0xfffff000 static unsigned long arch_timer_freq; void set_cntfreq(void) { omap_smc1(OMAP5_DRA7_MON_SET_CNTFRQ_INDEX, arch_timer_freq); } /* * The realtime counter also called master counter, is a free-running * counter, which is related to real time. It produces the count used * by the CPU local timer peripherals in the MPU cluster. The timer counts * at a rate of 6.144 MHz. Because the device operates on different clocks * in different power modes, the master counter shifts operation between * clocks, adjusting the increment per clock in hardware accordingly to * maintain a constant count rate. */ static void __init realtime_counter_init(void) { void __iomem *base; static struct clk *sys_clk; unsigned long rate; unsigned int reg; unsigned long long num, den; base = ioremap(REALTIME_COUNTER_BASE, SZ_32); if (!base) { pr_err("%s: ioremap failed\n", __func__); return; } sys_clk = clk_get(NULL, "sys_clkin"); if (IS_ERR(sys_clk)) { pr_err("%s: failed to get system clock handle\n", __func__); iounmap(base); return; } rate = clk_get_rate(sys_clk); clk_put(sys_clk); if (soc_is_dra7xx()) { /* * Errata i856 says the 32.768KHz crystal does not start at * power on, so the CPU falls back to an emulated 32KHz clock * based on sysclk / 610 instead. This causes the master counter * frequency to not be 6.144MHz but at sysclk / 610 * 375 / 2 * (OR sysclk * 75 / 244) * * This affects at least the DRA7/AM572x 1.0, 1.1 revisions. * Of course any board built without a populated 32.768KHz * crystal would also need this fix even if the CPU is fixed * later. * * Either case can be detected by using the two speedselect bits * If they are not 0, then the 32.768KHz clock driving the * coarse counter that corrects the fine counter every time it * ticks is actually rate/610 rather than 32.768KHz and we * should compensate to avoid the 570ppm (at 20MHz, much worse * at other rates) too fast system time. */ reg = omap_ctrl_readl(DRA7_CTRL_CORE_BOOTSTRAP); if (reg & DRA7_SPEEDSELECT_MASK) { num = 75; den = 244; goto sysclk1_based; } } /* Numerator/denumerator values refer TRM Realtime Counter section */ switch (rate) { case 12000000: num = 64; den = 125; break; case 13000000: num = 768; den = 1625; break; case 19200000: num = 8; den = 25; break; case 20000000: num = 192; den = 625; break; case 26000000: num = 384; den = 1625; break; case 27000000: num = 256; den = 1125; break; case 38400000: default: /* Program it for 38.4 MHz */ num = 4; den = 25; break; } sysclk1_based: /* Program numerator and denumerator registers */ reg = readl_relaxed(base + INCREMENTER_NUMERATOR_OFFSET) & NUMERATOR_DENUMERATOR_MASK; reg |= num; writel_relaxed(reg, base + INCREMENTER_NUMERATOR_OFFSET); reg = readl_relaxed(base + INCREMENTER_DENUMERATOR_RELOAD_OFFSET) & NUMERATOR_DENUMERATOR_MASK; reg |= den; writel_relaxed(reg, base + INCREMENTER_DENUMERATOR_RELOAD_OFFSET); arch_timer_freq = DIV_ROUND_UP_ULL(rate * num, den); set_cntfreq(); iounmap(base); } void __init omap5_realtime_timer_init(void) { omap_clk_init(); realtime_counter_init(); timer_probe(); }