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