162306a36Sopenharmony_ci/* 262306a36Sopenharmony_ci * linux/arch/arm/mach-omap1/timer32k.c 362306a36Sopenharmony_ci * 462306a36Sopenharmony_ci * OMAP 32K Timer 562306a36Sopenharmony_ci * 662306a36Sopenharmony_ci * Copyright (C) 2004 - 2005 Nokia Corporation 762306a36Sopenharmony_ci * Partial timer rewrite and additional dynamic tick timer support by 862306a36Sopenharmony_ci * Tony Lindgen <tony@atomide.com> and 962306a36Sopenharmony_ci * Tuukka Tikkanen <tuukka.tikkanen@elektrobit.com> 1062306a36Sopenharmony_ci * OMAP Dual-mode timer framework support by Timo Teras 1162306a36Sopenharmony_ci * 1262306a36Sopenharmony_ci * MPU timer code based on the older MPU timer code for OMAP 1362306a36Sopenharmony_ci * Copyright (C) 2000 RidgeRun, Inc. 1462306a36Sopenharmony_ci * Author: Greg Lonnon <glonnon@ridgerun.com> 1562306a36Sopenharmony_ci * 1662306a36Sopenharmony_ci * This program is free software; you can redistribute it and/or modify it 1762306a36Sopenharmony_ci * under the terms of the GNU General Public License as published by the 1862306a36Sopenharmony_ci * Free Software Foundation; either version 2 of the License, or (at your 1962306a36Sopenharmony_ci * option) any later version. 2062306a36Sopenharmony_ci * 2162306a36Sopenharmony_ci * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED 2262306a36Sopenharmony_ci * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 2362306a36Sopenharmony_ci * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN 2462306a36Sopenharmony_ci * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 2562306a36Sopenharmony_ci * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 2662306a36Sopenharmony_ci * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF 2762306a36Sopenharmony_ci * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 2862306a36Sopenharmony_ci * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 2962306a36Sopenharmony_ci * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 3062306a36Sopenharmony_ci * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 3162306a36Sopenharmony_ci * 3262306a36Sopenharmony_ci * You should have received a copy of the GNU General Public License along 3362306a36Sopenharmony_ci * with this program; if not, write to the Free Software Foundation, Inc., 3462306a36Sopenharmony_ci * 675 Mass Ave, Cambridge, MA 02139, USA. 3562306a36Sopenharmony_ci */ 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_ci#include <linux/kernel.h> 3862306a36Sopenharmony_ci#include <linux/init.h> 3962306a36Sopenharmony_ci#include <linux/delay.h> 4062306a36Sopenharmony_ci#include <linux/interrupt.h> 4162306a36Sopenharmony_ci#include <linux/sched.h> 4262306a36Sopenharmony_ci#include <linux/spinlock.h> 4362306a36Sopenharmony_ci#include <linux/err.h> 4462306a36Sopenharmony_ci#include <linux/clk.h> 4562306a36Sopenharmony_ci#include <linux/clocksource.h> 4662306a36Sopenharmony_ci#include <linux/clockchips.h> 4762306a36Sopenharmony_ci#include <linux/io.h> 4862306a36Sopenharmony_ci#include <linux/sched_clock.h> 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_ci#include <asm/irq.h> 5162306a36Sopenharmony_ci#include <asm/mach/irq.h> 5262306a36Sopenharmony_ci#include <asm/mach/time.h> 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_ci#include "hardware.h" 5562306a36Sopenharmony_ci#include "common.h" 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_ci/* 5862306a36Sopenharmony_ci * --------------------------------------------------------------------------- 5962306a36Sopenharmony_ci * 32KHz OS timer 6062306a36Sopenharmony_ci * 6162306a36Sopenharmony_ci * This currently works only on 16xx, as 1510 does not have the continuous 6262306a36Sopenharmony_ci * 32KHz synchronous timer. The 32KHz synchronous timer is used to keep track 6362306a36Sopenharmony_ci * of time in addition to the 32KHz OS timer. Using only the 32KHz OS timer 6462306a36Sopenharmony_ci * on 1510 would be possible, but the timer would not be as accurate as 6562306a36Sopenharmony_ci * with the 32KHz synchronized timer. 6662306a36Sopenharmony_ci * --------------------------------------------------------------------------- 6762306a36Sopenharmony_ci */ 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_ci/* 16xx specific defines */ 7062306a36Sopenharmony_ci#define OMAP1_32K_TIMER_BASE 0xfffb9000 7162306a36Sopenharmony_ci#define OMAP1_32KSYNC_TIMER_BASE 0xfffbc400 7262306a36Sopenharmony_ci#define OMAP1_32K_TIMER_CR 0x08 7362306a36Sopenharmony_ci#define OMAP1_32K_TIMER_TVR 0x00 7462306a36Sopenharmony_ci#define OMAP1_32K_TIMER_TCR 0x04 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_ci#define OMAP_32K_TICKS_PER_SEC (32768) 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_ci/* 7962306a36Sopenharmony_ci * TRM says 1 / HZ = ( TVR + 1) / 32768, so TRV = (32768 / HZ) - 1 8062306a36Sopenharmony_ci * so with HZ = 128, TVR = 255. 8162306a36Sopenharmony_ci */ 8262306a36Sopenharmony_ci#define OMAP_32K_TIMER_TICK_PERIOD ((OMAP_32K_TICKS_PER_SEC / HZ) - 1) 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_ci#define JIFFIES_TO_HW_TICKS(nr_jiffies, clock_rate) \ 8562306a36Sopenharmony_ci (((nr_jiffies) * (clock_rate)) / HZ) 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_cistatic inline void omap_32k_timer_write(int val, int reg) 8862306a36Sopenharmony_ci{ 8962306a36Sopenharmony_ci omap_writew(val, OMAP1_32K_TIMER_BASE + reg); 9062306a36Sopenharmony_ci} 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_cistatic inline void omap_32k_timer_start(unsigned long load_val) 9362306a36Sopenharmony_ci{ 9462306a36Sopenharmony_ci if (!load_val) 9562306a36Sopenharmony_ci load_val = 1; 9662306a36Sopenharmony_ci omap_32k_timer_write(load_val, OMAP1_32K_TIMER_TVR); 9762306a36Sopenharmony_ci omap_32k_timer_write(0x0f, OMAP1_32K_TIMER_CR); 9862306a36Sopenharmony_ci} 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_cistatic inline void omap_32k_timer_stop(void) 10162306a36Sopenharmony_ci{ 10262306a36Sopenharmony_ci omap_32k_timer_write(0x0, OMAP1_32K_TIMER_CR); 10362306a36Sopenharmony_ci} 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_ci#define omap_32k_timer_ack_irq() 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_cistatic int omap_32k_timer_set_next_event(unsigned long delta, 10862306a36Sopenharmony_ci struct clock_event_device *dev) 10962306a36Sopenharmony_ci{ 11062306a36Sopenharmony_ci omap_32k_timer_start(delta); 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_ci return 0; 11362306a36Sopenharmony_ci} 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_cistatic int omap_32k_timer_shutdown(struct clock_event_device *evt) 11662306a36Sopenharmony_ci{ 11762306a36Sopenharmony_ci omap_32k_timer_stop(); 11862306a36Sopenharmony_ci return 0; 11962306a36Sopenharmony_ci} 12062306a36Sopenharmony_ci 12162306a36Sopenharmony_cistatic int omap_32k_timer_set_periodic(struct clock_event_device *evt) 12262306a36Sopenharmony_ci{ 12362306a36Sopenharmony_ci omap_32k_timer_stop(); 12462306a36Sopenharmony_ci omap_32k_timer_start(OMAP_32K_TIMER_TICK_PERIOD); 12562306a36Sopenharmony_ci return 0; 12662306a36Sopenharmony_ci} 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_cistatic struct clock_event_device clockevent_32k_timer = { 12962306a36Sopenharmony_ci .name = "32k-timer", 13062306a36Sopenharmony_ci .features = CLOCK_EVT_FEAT_PERIODIC | 13162306a36Sopenharmony_ci CLOCK_EVT_FEAT_ONESHOT, 13262306a36Sopenharmony_ci .set_next_event = omap_32k_timer_set_next_event, 13362306a36Sopenharmony_ci .set_state_shutdown = omap_32k_timer_shutdown, 13462306a36Sopenharmony_ci .set_state_periodic = omap_32k_timer_set_periodic, 13562306a36Sopenharmony_ci .set_state_oneshot = omap_32k_timer_shutdown, 13662306a36Sopenharmony_ci .tick_resume = omap_32k_timer_shutdown, 13762306a36Sopenharmony_ci}; 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_cistatic irqreturn_t omap_32k_timer_interrupt(int irq, void *dev_id) 14062306a36Sopenharmony_ci{ 14162306a36Sopenharmony_ci struct clock_event_device *evt = &clockevent_32k_timer; 14262306a36Sopenharmony_ci omap_32k_timer_ack_irq(); 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_ci evt->event_handler(evt); 14562306a36Sopenharmony_ci 14662306a36Sopenharmony_ci return IRQ_HANDLED; 14762306a36Sopenharmony_ci} 14862306a36Sopenharmony_ci 14962306a36Sopenharmony_cistatic __init void omap_init_32k_timer(void) 15062306a36Sopenharmony_ci{ 15162306a36Sopenharmony_ci if (request_irq(INT_OS_TIMER, omap_32k_timer_interrupt, 15262306a36Sopenharmony_ci IRQF_TIMER | IRQF_IRQPOLL, "32KHz timer", NULL)) 15362306a36Sopenharmony_ci pr_err("Failed to request irq %d(32KHz timer)\n", INT_OS_TIMER); 15462306a36Sopenharmony_ci 15562306a36Sopenharmony_ci clockevent_32k_timer.cpumask = cpumask_of(0); 15662306a36Sopenharmony_ci clockevents_config_and_register(&clockevent_32k_timer, 15762306a36Sopenharmony_ci OMAP_32K_TICKS_PER_SEC, 1, 0xfffffffe); 15862306a36Sopenharmony_ci} 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_ci/* OMAP2_32KSYNCNT_CR_OFF: offset of 32ksync counter register */ 16162306a36Sopenharmony_ci#define OMAP2_32KSYNCNT_REV_OFF 0x0 16262306a36Sopenharmony_ci#define OMAP2_32KSYNCNT_REV_SCHEME (0x3 << 30) 16362306a36Sopenharmony_ci#define OMAP2_32KSYNCNT_CR_OFF_LOW 0x10 16462306a36Sopenharmony_ci#define OMAP2_32KSYNCNT_CR_OFF_HIGH 0x30 16562306a36Sopenharmony_ci 16662306a36Sopenharmony_ci/* 16762306a36Sopenharmony_ci * 32KHz clocksource ... always available, on pretty most chips except 16862306a36Sopenharmony_ci * OMAP 730 and 1510. Other timers could be used as clocksources, with 16962306a36Sopenharmony_ci * higher resolution in free-running counter modes (e.g. 12 MHz xtal), 17062306a36Sopenharmony_ci * but systems won't necessarily want to spend resources that way. 17162306a36Sopenharmony_ci */ 17262306a36Sopenharmony_cistatic void __iomem *sync32k_cnt_reg; 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_cistatic u64 notrace omap_32k_read_sched_clock(void) 17562306a36Sopenharmony_ci{ 17662306a36Sopenharmony_ci return sync32k_cnt_reg ? readl_relaxed(sync32k_cnt_reg) : 0; 17762306a36Sopenharmony_ci} 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_cistatic struct timespec64 persistent_ts; 18062306a36Sopenharmony_cistatic cycles_t cycles; 18162306a36Sopenharmony_cistatic unsigned int persistent_mult, persistent_shift; 18262306a36Sopenharmony_ci 18362306a36Sopenharmony_ci/** 18462306a36Sopenharmony_ci * omap_read_persistent_clock64 - Return time from a persistent clock. 18562306a36Sopenharmony_ci * @ts: &struct timespec64 for the returned time 18662306a36Sopenharmony_ci * 18762306a36Sopenharmony_ci * Reads the time from a source which isn't disabled during PM, the 18862306a36Sopenharmony_ci * 32k sync timer. Convert the cycles elapsed since last read into 18962306a36Sopenharmony_ci * nsecs and adds to a monotonically increasing timespec64. 19062306a36Sopenharmony_ci */ 19162306a36Sopenharmony_cistatic void omap_read_persistent_clock64(struct timespec64 *ts) 19262306a36Sopenharmony_ci{ 19362306a36Sopenharmony_ci unsigned long long nsecs; 19462306a36Sopenharmony_ci cycles_t last_cycles; 19562306a36Sopenharmony_ci 19662306a36Sopenharmony_ci last_cycles = cycles; 19762306a36Sopenharmony_ci cycles = sync32k_cnt_reg ? readl_relaxed(sync32k_cnt_reg) : 0; 19862306a36Sopenharmony_ci 19962306a36Sopenharmony_ci nsecs = clocksource_cyc2ns(cycles - last_cycles, 20062306a36Sopenharmony_ci persistent_mult, persistent_shift); 20162306a36Sopenharmony_ci 20262306a36Sopenharmony_ci timespec64_add_ns(&persistent_ts, nsecs); 20362306a36Sopenharmony_ci 20462306a36Sopenharmony_ci *ts = persistent_ts; 20562306a36Sopenharmony_ci} 20662306a36Sopenharmony_ci 20762306a36Sopenharmony_ci/** 20862306a36Sopenharmony_ci * omap_init_clocksource_32k - setup and register counter 32k as a 20962306a36Sopenharmony_ci * kernel clocksource 21062306a36Sopenharmony_ci * @vbase: base addr of counter_32k module 21162306a36Sopenharmony_ci * 21262306a36Sopenharmony_ci * Returns: %0 upon success or negative error code upon failure. 21362306a36Sopenharmony_ci * 21462306a36Sopenharmony_ci */ 21562306a36Sopenharmony_cistatic int __init omap_init_clocksource_32k(void __iomem *vbase) 21662306a36Sopenharmony_ci{ 21762306a36Sopenharmony_ci int ret; 21862306a36Sopenharmony_ci 21962306a36Sopenharmony_ci /* 22062306a36Sopenharmony_ci * 32k sync Counter IP register offsets vary between the 22162306a36Sopenharmony_ci * highlander version and the legacy ones. 22262306a36Sopenharmony_ci * The 'SCHEME' bits(30-31) of the revision register is used 22362306a36Sopenharmony_ci * to identify the version. 22462306a36Sopenharmony_ci */ 22562306a36Sopenharmony_ci if (readl_relaxed(vbase + OMAP2_32KSYNCNT_REV_OFF) & 22662306a36Sopenharmony_ci OMAP2_32KSYNCNT_REV_SCHEME) 22762306a36Sopenharmony_ci sync32k_cnt_reg = vbase + OMAP2_32KSYNCNT_CR_OFF_HIGH; 22862306a36Sopenharmony_ci else 22962306a36Sopenharmony_ci sync32k_cnt_reg = vbase + OMAP2_32KSYNCNT_CR_OFF_LOW; 23062306a36Sopenharmony_ci 23162306a36Sopenharmony_ci /* 23262306a36Sopenharmony_ci * 120000 rough estimate from the calculations in 23362306a36Sopenharmony_ci * __clocksource_update_freq_scale. 23462306a36Sopenharmony_ci */ 23562306a36Sopenharmony_ci clocks_calc_mult_shift(&persistent_mult, &persistent_shift, 23662306a36Sopenharmony_ci 32768, NSEC_PER_SEC, 120000); 23762306a36Sopenharmony_ci 23862306a36Sopenharmony_ci ret = clocksource_mmio_init(sync32k_cnt_reg, "32k_counter", 32768, 23962306a36Sopenharmony_ci 250, 32, clocksource_mmio_readl_up); 24062306a36Sopenharmony_ci if (ret) { 24162306a36Sopenharmony_ci pr_err("32k_counter: can't register clocksource\n"); 24262306a36Sopenharmony_ci return ret; 24362306a36Sopenharmony_ci } 24462306a36Sopenharmony_ci 24562306a36Sopenharmony_ci sched_clock_register(omap_32k_read_sched_clock, 32, 32768); 24662306a36Sopenharmony_ci register_persistent_clock(omap_read_persistent_clock64); 24762306a36Sopenharmony_ci pr_info("OMAP clocksource: 32k_counter at 32768 Hz\n"); 24862306a36Sopenharmony_ci 24962306a36Sopenharmony_ci return 0; 25062306a36Sopenharmony_ci} 25162306a36Sopenharmony_ci 25262306a36Sopenharmony_ci/* 25362306a36Sopenharmony_ci * --------------------------------------------------------------------------- 25462306a36Sopenharmony_ci * Timer initialization 25562306a36Sopenharmony_ci * --------------------------------------------------------------------------- 25662306a36Sopenharmony_ci */ 25762306a36Sopenharmony_ciint __init omap_32k_timer_init(void) 25862306a36Sopenharmony_ci{ 25962306a36Sopenharmony_ci int ret = -ENODEV; 26062306a36Sopenharmony_ci 26162306a36Sopenharmony_ci if (cpu_is_omap16xx()) { 26262306a36Sopenharmony_ci void __iomem *base; 26362306a36Sopenharmony_ci struct clk *sync32k_ick; 26462306a36Sopenharmony_ci 26562306a36Sopenharmony_ci base = ioremap(OMAP1_32KSYNC_TIMER_BASE, SZ_1K); 26662306a36Sopenharmony_ci if (!base) { 26762306a36Sopenharmony_ci pr_err("32k_counter: failed to map base addr\n"); 26862306a36Sopenharmony_ci return -ENODEV; 26962306a36Sopenharmony_ci } 27062306a36Sopenharmony_ci 27162306a36Sopenharmony_ci sync32k_ick = clk_get(NULL, "omap_32ksync_ick"); 27262306a36Sopenharmony_ci if (!IS_ERR(sync32k_ick)) 27362306a36Sopenharmony_ci clk_prepare_enable(sync32k_ick); 27462306a36Sopenharmony_ci 27562306a36Sopenharmony_ci ret = omap_init_clocksource_32k(base); 27662306a36Sopenharmony_ci } 27762306a36Sopenharmony_ci 27862306a36Sopenharmony_ci if (!ret) 27962306a36Sopenharmony_ci omap_init_32k_timer(); 28062306a36Sopenharmony_ci 28162306a36Sopenharmony_ci return ret; 28262306a36Sopenharmony_ci} 283