18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * This file contains driver for the Cadence Triple Timer Counter Rev 06
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci *  Copyright (C) 2011-2013 Xilinx
68c2ecf20Sopenharmony_ci *
78c2ecf20Sopenharmony_ci * based on arch/mips/kernel/time.c timer driver
88c2ecf20Sopenharmony_ci */
98c2ecf20Sopenharmony_ci
108c2ecf20Sopenharmony_ci#include <linux/clk.h>
118c2ecf20Sopenharmony_ci#include <linux/interrupt.h>
128c2ecf20Sopenharmony_ci#include <linux/clockchips.h>
138c2ecf20Sopenharmony_ci#include <linux/clocksource.h>
148c2ecf20Sopenharmony_ci#include <linux/of_address.h>
158c2ecf20Sopenharmony_ci#include <linux/of_irq.h>
168c2ecf20Sopenharmony_ci#include <linux/slab.h>
178c2ecf20Sopenharmony_ci#include <linux/sched_clock.h>
188c2ecf20Sopenharmony_ci#include <linux/module.h>
198c2ecf20Sopenharmony_ci#include <linux/of_platform.h>
208c2ecf20Sopenharmony_ci
218c2ecf20Sopenharmony_ci/*
228c2ecf20Sopenharmony_ci * This driver configures the 2 16/32-bit count-up timers as follows:
238c2ecf20Sopenharmony_ci *
248c2ecf20Sopenharmony_ci * T1: Timer 1, clocksource for generic timekeeping
258c2ecf20Sopenharmony_ci * T2: Timer 2, clockevent source for hrtimers
268c2ecf20Sopenharmony_ci * T3: Timer 3, <unused>
278c2ecf20Sopenharmony_ci *
288c2ecf20Sopenharmony_ci * The input frequency to the timer module for emulation is 2.5MHz which is
298c2ecf20Sopenharmony_ci * common to all the timer channels (T1, T2, and T3). With a pre-scaler of 32,
308c2ecf20Sopenharmony_ci * the timers are clocked at 78.125KHz (12.8 us resolution).
318c2ecf20Sopenharmony_ci
328c2ecf20Sopenharmony_ci * The input frequency to the timer module in silicon is configurable and
338c2ecf20Sopenharmony_ci * obtained from device tree. The pre-scaler of 32 is used.
348c2ecf20Sopenharmony_ci */
358c2ecf20Sopenharmony_ci
368c2ecf20Sopenharmony_ci/*
378c2ecf20Sopenharmony_ci * Timer Register Offset Definitions of Timer 1, Increment base address by 4
388c2ecf20Sopenharmony_ci * and use same offsets for Timer 2
398c2ecf20Sopenharmony_ci */
408c2ecf20Sopenharmony_ci#define TTC_CLK_CNTRL_OFFSET		0x00 /* Clock Control Reg, RW */
418c2ecf20Sopenharmony_ci#define TTC_CNT_CNTRL_OFFSET		0x0C /* Counter Control Reg, RW */
428c2ecf20Sopenharmony_ci#define TTC_COUNT_VAL_OFFSET		0x18 /* Counter Value Reg, RO */
438c2ecf20Sopenharmony_ci#define TTC_INTR_VAL_OFFSET		0x24 /* Interval Count Reg, RW */
448c2ecf20Sopenharmony_ci#define TTC_ISR_OFFSET		0x54 /* Interrupt Status Reg, RO */
458c2ecf20Sopenharmony_ci#define TTC_IER_OFFSET		0x60 /* Interrupt Enable Reg, RW */
468c2ecf20Sopenharmony_ci
478c2ecf20Sopenharmony_ci#define TTC_CNT_CNTRL_DISABLE_MASK	0x1
488c2ecf20Sopenharmony_ci
498c2ecf20Sopenharmony_ci#define TTC_CLK_CNTRL_CSRC_MASK		(1 << 5)	/* clock source */
508c2ecf20Sopenharmony_ci#define TTC_CLK_CNTRL_PSV_MASK		0x1e
518c2ecf20Sopenharmony_ci#define TTC_CLK_CNTRL_PSV_SHIFT		1
528c2ecf20Sopenharmony_ci
538c2ecf20Sopenharmony_ci/*
548c2ecf20Sopenharmony_ci * Setup the timers to use pre-scaling, using a fixed value for now that will
558c2ecf20Sopenharmony_ci * work across most input frequency, but it may need to be more dynamic
568c2ecf20Sopenharmony_ci */
578c2ecf20Sopenharmony_ci#define PRESCALE_EXPONENT	11	/* 2 ^ PRESCALE_EXPONENT = PRESCALE */
588c2ecf20Sopenharmony_ci#define PRESCALE		2048	/* The exponent must match this */
598c2ecf20Sopenharmony_ci#define CLK_CNTRL_PRESCALE	((PRESCALE_EXPONENT - 1) << 1)
608c2ecf20Sopenharmony_ci#define CLK_CNTRL_PRESCALE_EN	1
618c2ecf20Sopenharmony_ci#define CNT_CNTRL_RESET		(1 << 4)
628c2ecf20Sopenharmony_ci
638c2ecf20Sopenharmony_ci#define MAX_F_ERR 50
648c2ecf20Sopenharmony_ci
658c2ecf20Sopenharmony_ci/**
668c2ecf20Sopenharmony_ci * struct ttc_timer - This definition defines local timer structure
678c2ecf20Sopenharmony_ci *
688c2ecf20Sopenharmony_ci * @base_addr:	Base address of timer
698c2ecf20Sopenharmony_ci * @freq:	Timer input clock frequency
708c2ecf20Sopenharmony_ci * @clk:	Associated clock source
718c2ecf20Sopenharmony_ci * @clk_rate_change_nb	Notifier block for clock rate changes
728c2ecf20Sopenharmony_ci */
738c2ecf20Sopenharmony_cistruct ttc_timer {
748c2ecf20Sopenharmony_ci	void __iomem *base_addr;
758c2ecf20Sopenharmony_ci	unsigned long freq;
768c2ecf20Sopenharmony_ci	struct clk *clk;
778c2ecf20Sopenharmony_ci	struct notifier_block clk_rate_change_nb;
788c2ecf20Sopenharmony_ci};
798c2ecf20Sopenharmony_ci
808c2ecf20Sopenharmony_ci#define to_ttc_timer(x) \
818c2ecf20Sopenharmony_ci		container_of(x, struct ttc_timer, clk_rate_change_nb)
828c2ecf20Sopenharmony_ci
838c2ecf20Sopenharmony_cistruct ttc_timer_clocksource {
848c2ecf20Sopenharmony_ci	u32			scale_clk_ctrl_reg_old;
858c2ecf20Sopenharmony_ci	u32			scale_clk_ctrl_reg_new;
868c2ecf20Sopenharmony_ci	struct ttc_timer	ttc;
878c2ecf20Sopenharmony_ci	struct clocksource	cs;
888c2ecf20Sopenharmony_ci};
898c2ecf20Sopenharmony_ci
908c2ecf20Sopenharmony_ci#define to_ttc_timer_clksrc(x) \
918c2ecf20Sopenharmony_ci		container_of(x, struct ttc_timer_clocksource, cs)
928c2ecf20Sopenharmony_ci
938c2ecf20Sopenharmony_cistruct ttc_timer_clockevent {
948c2ecf20Sopenharmony_ci	struct ttc_timer		ttc;
958c2ecf20Sopenharmony_ci	struct clock_event_device	ce;
968c2ecf20Sopenharmony_ci};
978c2ecf20Sopenharmony_ci
988c2ecf20Sopenharmony_ci#define to_ttc_timer_clkevent(x) \
998c2ecf20Sopenharmony_ci		container_of(x, struct ttc_timer_clockevent, ce)
1008c2ecf20Sopenharmony_ci
1018c2ecf20Sopenharmony_cistatic void __iomem *ttc_sched_clock_val_reg;
1028c2ecf20Sopenharmony_ci
1038c2ecf20Sopenharmony_ci/**
1048c2ecf20Sopenharmony_ci * ttc_set_interval - Set the timer interval value
1058c2ecf20Sopenharmony_ci *
1068c2ecf20Sopenharmony_ci * @timer:	Pointer to the timer instance
1078c2ecf20Sopenharmony_ci * @cycles:	Timer interval ticks
1088c2ecf20Sopenharmony_ci **/
1098c2ecf20Sopenharmony_cistatic void ttc_set_interval(struct ttc_timer *timer,
1108c2ecf20Sopenharmony_ci					unsigned long cycles)
1118c2ecf20Sopenharmony_ci{
1128c2ecf20Sopenharmony_ci	u32 ctrl_reg;
1138c2ecf20Sopenharmony_ci
1148c2ecf20Sopenharmony_ci	/* Disable the counter, set the counter value  and re-enable counter */
1158c2ecf20Sopenharmony_ci	ctrl_reg = readl_relaxed(timer->base_addr + TTC_CNT_CNTRL_OFFSET);
1168c2ecf20Sopenharmony_ci	ctrl_reg |= TTC_CNT_CNTRL_DISABLE_MASK;
1178c2ecf20Sopenharmony_ci	writel_relaxed(ctrl_reg, timer->base_addr + TTC_CNT_CNTRL_OFFSET);
1188c2ecf20Sopenharmony_ci
1198c2ecf20Sopenharmony_ci	writel_relaxed(cycles, timer->base_addr + TTC_INTR_VAL_OFFSET);
1208c2ecf20Sopenharmony_ci
1218c2ecf20Sopenharmony_ci	/*
1228c2ecf20Sopenharmony_ci	 * Reset the counter (0x10) so that it starts from 0, one-shot
1238c2ecf20Sopenharmony_ci	 * mode makes this needed for timing to be right.
1248c2ecf20Sopenharmony_ci	 */
1258c2ecf20Sopenharmony_ci	ctrl_reg |= CNT_CNTRL_RESET;
1268c2ecf20Sopenharmony_ci	ctrl_reg &= ~TTC_CNT_CNTRL_DISABLE_MASK;
1278c2ecf20Sopenharmony_ci	writel_relaxed(ctrl_reg, timer->base_addr + TTC_CNT_CNTRL_OFFSET);
1288c2ecf20Sopenharmony_ci}
1298c2ecf20Sopenharmony_ci
1308c2ecf20Sopenharmony_ci/**
1318c2ecf20Sopenharmony_ci * ttc_clock_event_interrupt - Clock event timer interrupt handler
1328c2ecf20Sopenharmony_ci *
1338c2ecf20Sopenharmony_ci * @irq:	IRQ number of the Timer
1348c2ecf20Sopenharmony_ci * @dev_id:	void pointer to the ttc_timer instance
1358c2ecf20Sopenharmony_ci *
1368c2ecf20Sopenharmony_ci * returns: Always IRQ_HANDLED - success
1378c2ecf20Sopenharmony_ci **/
1388c2ecf20Sopenharmony_cistatic irqreturn_t ttc_clock_event_interrupt(int irq, void *dev_id)
1398c2ecf20Sopenharmony_ci{
1408c2ecf20Sopenharmony_ci	struct ttc_timer_clockevent *ttce = dev_id;
1418c2ecf20Sopenharmony_ci	struct ttc_timer *timer = &ttce->ttc;
1428c2ecf20Sopenharmony_ci
1438c2ecf20Sopenharmony_ci	/* Acknowledge the interrupt and call event handler */
1448c2ecf20Sopenharmony_ci	readl_relaxed(timer->base_addr + TTC_ISR_OFFSET);
1458c2ecf20Sopenharmony_ci
1468c2ecf20Sopenharmony_ci	ttce->ce.event_handler(&ttce->ce);
1478c2ecf20Sopenharmony_ci
1488c2ecf20Sopenharmony_ci	return IRQ_HANDLED;
1498c2ecf20Sopenharmony_ci}
1508c2ecf20Sopenharmony_ci
1518c2ecf20Sopenharmony_ci/**
1528c2ecf20Sopenharmony_ci * __ttc_clocksource_read - Reads the timer counter register
1538c2ecf20Sopenharmony_ci *
1548c2ecf20Sopenharmony_ci * returns: Current timer counter register value
1558c2ecf20Sopenharmony_ci **/
1568c2ecf20Sopenharmony_cistatic u64 __ttc_clocksource_read(struct clocksource *cs)
1578c2ecf20Sopenharmony_ci{
1588c2ecf20Sopenharmony_ci	struct ttc_timer *timer = &to_ttc_timer_clksrc(cs)->ttc;
1598c2ecf20Sopenharmony_ci
1608c2ecf20Sopenharmony_ci	return (u64)readl_relaxed(timer->base_addr +
1618c2ecf20Sopenharmony_ci				TTC_COUNT_VAL_OFFSET);
1628c2ecf20Sopenharmony_ci}
1638c2ecf20Sopenharmony_ci
1648c2ecf20Sopenharmony_cistatic u64 notrace ttc_sched_clock_read(void)
1658c2ecf20Sopenharmony_ci{
1668c2ecf20Sopenharmony_ci	return readl_relaxed(ttc_sched_clock_val_reg);
1678c2ecf20Sopenharmony_ci}
1688c2ecf20Sopenharmony_ci
1698c2ecf20Sopenharmony_ci/**
1708c2ecf20Sopenharmony_ci * ttc_set_next_event - Sets the time interval for next event
1718c2ecf20Sopenharmony_ci *
1728c2ecf20Sopenharmony_ci * @cycles:	Timer interval ticks
1738c2ecf20Sopenharmony_ci * @evt:	Address of clock event instance
1748c2ecf20Sopenharmony_ci *
1758c2ecf20Sopenharmony_ci * returns: Always 0 - success
1768c2ecf20Sopenharmony_ci **/
1778c2ecf20Sopenharmony_cistatic int ttc_set_next_event(unsigned long cycles,
1788c2ecf20Sopenharmony_ci					struct clock_event_device *evt)
1798c2ecf20Sopenharmony_ci{
1808c2ecf20Sopenharmony_ci	struct ttc_timer_clockevent *ttce = to_ttc_timer_clkevent(evt);
1818c2ecf20Sopenharmony_ci	struct ttc_timer *timer = &ttce->ttc;
1828c2ecf20Sopenharmony_ci
1838c2ecf20Sopenharmony_ci	ttc_set_interval(timer, cycles);
1848c2ecf20Sopenharmony_ci	return 0;
1858c2ecf20Sopenharmony_ci}
1868c2ecf20Sopenharmony_ci
1878c2ecf20Sopenharmony_ci/**
1888c2ecf20Sopenharmony_ci * ttc_set_{shutdown|oneshot|periodic} - Sets the state of timer
1898c2ecf20Sopenharmony_ci *
1908c2ecf20Sopenharmony_ci * @evt:	Address of clock event instance
1918c2ecf20Sopenharmony_ci **/
1928c2ecf20Sopenharmony_cistatic int ttc_shutdown(struct clock_event_device *evt)
1938c2ecf20Sopenharmony_ci{
1948c2ecf20Sopenharmony_ci	struct ttc_timer_clockevent *ttce = to_ttc_timer_clkevent(evt);
1958c2ecf20Sopenharmony_ci	struct ttc_timer *timer = &ttce->ttc;
1968c2ecf20Sopenharmony_ci	u32 ctrl_reg;
1978c2ecf20Sopenharmony_ci
1988c2ecf20Sopenharmony_ci	ctrl_reg = readl_relaxed(timer->base_addr + TTC_CNT_CNTRL_OFFSET);
1998c2ecf20Sopenharmony_ci	ctrl_reg |= TTC_CNT_CNTRL_DISABLE_MASK;
2008c2ecf20Sopenharmony_ci	writel_relaxed(ctrl_reg, timer->base_addr + TTC_CNT_CNTRL_OFFSET);
2018c2ecf20Sopenharmony_ci	return 0;
2028c2ecf20Sopenharmony_ci}
2038c2ecf20Sopenharmony_ci
2048c2ecf20Sopenharmony_cistatic int ttc_set_periodic(struct clock_event_device *evt)
2058c2ecf20Sopenharmony_ci{
2068c2ecf20Sopenharmony_ci	struct ttc_timer_clockevent *ttce = to_ttc_timer_clkevent(evt);
2078c2ecf20Sopenharmony_ci	struct ttc_timer *timer = &ttce->ttc;
2088c2ecf20Sopenharmony_ci
2098c2ecf20Sopenharmony_ci	ttc_set_interval(timer,
2108c2ecf20Sopenharmony_ci			 DIV_ROUND_CLOSEST(ttce->ttc.freq, PRESCALE * HZ));
2118c2ecf20Sopenharmony_ci	return 0;
2128c2ecf20Sopenharmony_ci}
2138c2ecf20Sopenharmony_ci
2148c2ecf20Sopenharmony_cistatic int ttc_resume(struct clock_event_device *evt)
2158c2ecf20Sopenharmony_ci{
2168c2ecf20Sopenharmony_ci	struct ttc_timer_clockevent *ttce = to_ttc_timer_clkevent(evt);
2178c2ecf20Sopenharmony_ci	struct ttc_timer *timer = &ttce->ttc;
2188c2ecf20Sopenharmony_ci	u32 ctrl_reg;
2198c2ecf20Sopenharmony_ci
2208c2ecf20Sopenharmony_ci	ctrl_reg = readl_relaxed(timer->base_addr + TTC_CNT_CNTRL_OFFSET);
2218c2ecf20Sopenharmony_ci	ctrl_reg &= ~TTC_CNT_CNTRL_DISABLE_MASK;
2228c2ecf20Sopenharmony_ci	writel_relaxed(ctrl_reg, timer->base_addr + TTC_CNT_CNTRL_OFFSET);
2238c2ecf20Sopenharmony_ci	return 0;
2248c2ecf20Sopenharmony_ci}
2258c2ecf20Sopenharmony_ci
2268c2ecf20Sopenharmony_cistatic int ttc_rate_change_clocksource_cb(struct notifier_block *nb,
2278c2ecf20Sopenharmony_ci		unsigned long event, void *data)
2288c2ecf20Sopenharmony_ci{
2298c2ecf20Sopenharmony_ci	struct clk_notifier_data *ndata = data;
2308c2ecf20Sopenharmony_ci	struct ttc_timer *ttc = to_ttc_timer(nb);
2318c2ecf20Sopenharmony_ci	struct ttc_timer_clocksource *ttccs = container_of(ttc,
2328c2ecf20Sopenharmony_ci			struct ttc_timer_clocksource, ttc);
2338c2ecf20Sopenharmony_ci
2348c2ecf20Sopenharmony_ci	switch (event) {
2358c2ecf20Sopenharmony_ci	case PRE_RATE_CHANGE:
2368c2ecf20Sopenharmony_ci	{
2378c2ecf20Sopenharmony_ci		u32 psv;
2388c2ecf20Sopenharmony_ci		unsigned long factor, rate_low, rate_high;
2398c2ecf20Sopenharmony_ci
2408c2ecf20Sopenharmony_ci		if (ndata->new_rate > ndata->old_rate) {
2418c2ecf20Sopenharmony_ci			factor = DIV_ROUND_CLOSEST(ndata->new_rate,
2428c2ecf20Sopenharmony_ci					ndata->old_rate);
2438c2ecf20Sopenharmony_ci			rate_low = ndata->old_rate;
2448c2ecf20Sopenharmony_ci			rate_high = ndata->new_rate;
2458c2ecf20Sopenharmony_ci		} else {
2468c2ecf20Sopenharmony_ci			factor = DIV_ROUND_CLOSEST(ndata->old_rate,
2478c2ecf20Sopenharmony_ci					ndata->new_rate);
2488c2ecf20Sopenharmony_ci			rate_low = ndata->new_rate;
2498c2ecf20Sopenharmony_ci			rate_high = ndata->old_rate;
2508c2ecf20Sopenharmony_ci		}
2518c2ecf20Sopenharmony_ci
2528c2ecf20Sopenharmony_ci		if (!is_power_of_2(factor))
2538c2ecf20Sopenharmony_ci				return NOTIFY_BAD;
2548c2ecf20Sopenharmony_ci
2558c2ecf20Sopenharmony_ci		if (abs(rate_high - (factor * rate_low)) > MAX_F_ERR)
2568c2ecf20Sopenharmony_ci			return NOTIFY_BAD;
2578c2ecf20Sopenharmony_ci
2588c2ecf20Sopenharmony_ci		factor = __ilog2_u32(factor);
2598c2ecf20Sopenharmony_ci
2608c2ecf20Sopenharmony_ci		/*
2618c2ecf20Sopenharmony_ci		 * store timer clock ctrl register so we can restore it in case
2628c2ecf20Sopenharmony_ci		 * of an abort.
2638c2ecf20Sopenharmony_ci		 */
2648c2ecf20Sopenharmony_ci		ttccs->scale_clk_ctrl_reg_old =
2658c2ecf20Sopenharmony_ci			readl_relaxed(ttccs->ttc.base_addr +
2668c2ecf20Sopenharmony_ci			TTC_CLK_CNTRL_OFFSET);
2678c2ecf20Sopenharmony_ci
2688c2ecf20Sopenharmony_ci		psv = (ttccs->scale_clk_ctrl_reg_old &
2698c2ecf20Sopenharmony_ci				TTC_CLK_CNTRL_PSV_MASK) >>
2708c2ecf20Sopenharmony_ci				TTC_CLK_CNTRL_PSV_SHIFT;
2718c2ecf20Sopenharmony_ci		if (ndata->new_rate < ndata->old_rate)
2728c2ecf20Sopenharmony_ci			psv -= factor;
2738c2ecf20Sopenharmony_ci		else
2748c2ecf20Sopenharmony_ci			psv += factor;
2758c2ecf20Sopenharmony_ci
2768c2ecf20Sopenharmony_ci		/* prescaler within legal range? */
2778c2ecf20Sopenharmony_ci		if (psv & ~(TTC_CLK_CNTRL_PSV_MASK >> TTC_CLK_CNTRL_PSV_SHIFT))
2788c2ecf20Sopenharmony_ci			return NOTIFY_BAD;
2798c2ecf20Sopenharmony_ci
2808c2ecf20Sopenharmony_ci		ttccs->scale_clk_ctrl_reg_new = ttccs->scale_clk_ctrl_reg_old &
2818c2ecf20Sopenharmony_ci			~TTC_CLK_CNTRL_PSV_MASK;
2828c2ecf20Sopenharmony_ci		ttccs->scale_clk_ctrl_reg_new |= psv << TTC_CLK_CNTRL_PSV_SHIFT;
2838c2ecf20Sopenharmony_ci
2848c2ecf20Sopenharmony_ci
2858c2ecf20Sopenharmony_ci		/* scale down: adjust divider in post-change notification */
2868c2ecf20Sopenharmony_ci		if (ndata->new_rate < ndata->old_rate)
2878c2ecf20Sopenharmony_ci			return NOTIFY_DONE;
2888c2ecf20Sopenharmony_ci
2898c2ecf20Sopenharmony_ci		/* scale up: adjust divider now - before frequency change */
2908c2ecf20Sopenharmony_ci		writel_relaxed(ttccs->scale_clk_ctrl_reg_new,
2918c2ecf20Sopenharmony_ci			       ttccs->ttc.base_addr + TTC_CLK_CNTRL_OFFSET);
2928c2ecf20Sopenharmony_ci		break;
2938c2ecf20Sopenharmony_ci	}
2948c2ecf20Sopenharmony_ci	case POST_RATE_CHANGE:
2958c2ecf20Sopenharmony_ci		/* scale up: pre-change notification did the adjustment */
2968c2ecf20Sopenharmony_ci		if (ndata->new_rate > ndata->old_rate)
2978c2ecf20Sopenharmony_ci			return NOTIFY_OK;
2988c2ecf20Sopenharmony_ci
2998c2ecf20Sopenharmony_ci		/* scale down: adjust divider now - after frequency change */
3008c2ecf20Sopenharmony_ci		writel_relaxed(ttccs->scale_clk_ctrl_reg_new,
3018c2ecf20Sopenharmony_ci			       ttccs->ttc.base_addr + TTC_CLK_CNTRL_OFFSET);
3028c2ecf20Sopenharmony_ci		break;
3038c2ecf20Sopenharmony_ci
3048c2ecf20Sopenharmony_ci	case ABORT_RATE_CHANGE:
3058c2ecf20Sopenharmony_ci		/* we have to undo the adjustment in case we scale up */
3068c2ecf20Sopenharmony_ci		if (ndata->new_rate < ndata->old_rate)
3078c2ecf20Sopenharmony_ci			return NOTIFY_OK;
3088c2ecf20Sopenharmony_ci
3098c2ecf20Sopenharmony_ci		/* restore original register value */
3108c2ecf20Sopenharmony_ci		writel_relaxed(ttccs->scale_clk_ctrl_reg_old,
3118c2ecf20Sopenharmony_ci			       ttccs->ttc.base_addr + TTC_CLK_CNTRL_OFFSET);
3128c2ecf20Sopenharmony_ci		fallthrough;
3138c2ecf20Sopenharmony_ci	default:
3148c2ecf20Sopenharmony_ci		return NOTIFY_DONE;
3158c2ecf20Sopenharmony_ci	}
3168c2ecf20Sopenharmony_ci
3178c2ecf20Sopenharmony_ci	return NOTIFY_DONE;
3188c2ecf20Sopenharmony_ci}
3198c2ecf20Sopenharmony_ci
3208c2ecf20Sopenharmony_cistatic int __init ttc_setup_clocksource(struct clk *clk, void __iomem *base,
3218c2ecf20Sopenharmony_ci					 u32 timer_width)
3228c2ecf20Sopenharmony_ci{
3238c2ecf20Sopenharmony_ci	struct ttc_timer_clocksource *ttccs;
3248c2ecf20Sopenharmony_ci	int err;
3258c2ecf20Sopenharmony_ci
3268c2ecf20Sopenharmony_ci	ttccs = kzalloc(sizeof(*ttccs), GFP_KERNEL);
3278c2ecf20Sopenharmony_ci	if (!ttccs)
3288c2ecf20Sopenharmony_ci		return -ENOMEM;
3298c2ecf20Sopenharmony_ci
3308c2ecf20Sopenharmony_ci	ttccs->ttc.clk = clk;
3318c2ecf20Sopenharmony_ci
3328c2ecf20Sopenharmony_ci	err = clk_prepare_enable(ttccs->ttc.clk);
3338c2ecf20Sopenharmony_ci	if (err) {
3348c2ecf20Sopenharmony_ci		kfree(ttccs);
3358c2ecf20Sopenharmony_ci		return err;
3368c2ecf20Sopenharmony_ci	}
3378c2ecf20Sopenharmony_ci
3388c2ecf20Sopenharmony_ci	ttccs->ttc.freq = clk_get_rate(ttccs->ttc.clk);
3398c2ecf20Sopenharmony_ci
3408c2ecf20Sopenharmony_ci	ttccs->ttc.clk_rate_change_nb.notifier_call =
3418c2ecf20Sopenharmony_ci		ttc_rate_change_clocksource_cb;
3428c2ecf20Sopenharmony_ci	ttccs->ttc.clk_rate_change_nb.next = NULL;
3438c2ecf20Sopenharmony_ci
3448c2ecf20Sopenharmony_ci	err = clk_notifier_register(ttccs->ttc.clk,
3458c2ecf20Sopenharmony_ci				    &ttccs->ttc.clk_rate_change_nb);
3468c2ecf20Sopenharmony_ci	if (err)
3478c2ecf20Sopenharmony_ci		pr_warn("Unable to register clock notifier.\n");
3488c2ecf20Sopenharmony_ci
3498c2ecf20Sopenharmony_ci	ttccs->ttc.base_addr = base;
3508c2ecf20Sopenharmony_ci	ttccs->cs.name = "ttc_clocksource";
3518c2ecf20Sopenharmony_ci	ttccs->cs.rating = 200;
3528c2ecf20Sopenharmony_ci	ttccs->cs.read = __ttc_clocksource_read;
3538c2ecf20Sopenharmony_ci	ttccs->cs.mask = CLOCKSOURCE_MASK(timer_width);
3548c2ecf20Sopenharmony_ci	ttccs->cs.flags = CLOCK_SOURCE_IS_CONTINUOUS;
3558c2ecf20Sopenharmony_ci
3568c2ecf20Sopenharmony_ci	/*
3578c2ecf20Sopenharmony_ci	 * Setup the clock source counter to be an incrementing counter
3588c2ecf20Sopenharmony_ci	 * with no interrupt and it rolls over at 0xFFFF. Pre-scale
3598c2ecf20Sopenharmony_ci	 * it by 32 also. Let it start running now.
3608c2ecf20Sopenharmony_ci	 */
3618c2ecf20Sopenharmony_ci	writel_relaxed(0x0,  ttccs->ttc.base_addr + TTC_IER_OFFSET);
3628c2ecf20Sopenharmony_ci	writel_relaxed(CLK_CNTRL_PRESCALE | CLK_CNTRL_PRESCALE_EN,
3638c2ecf20Sopenharmony_ci		     ttccs->ttc.base_addr + TTC_CLK_CNTRL_OFFSET);
3648c2ecf20Sopenharmony_ci	writel_relaxed(CNT_CNTRL_RESET,
3658c2ecf20Sopenharmony_ci		     ttccs->ttc.base_addr + TTC_CNT_CNTRL_OFFSET);
3668c2ecf20Sopenharmony_ci
3678c2ecf20Sopenharmony_ci	err = clocksource_register_hz(&ttccs->cs, ttccs->ttc.freq / PRESCALE);
3688c2ecf20Sopenharmony_ci	if (err) {
3698c2ecf20Sopenharmony_ci		kfree(ttccs);
3708c2ecf20Sopenharmony_ci		return err;
3718c2ecf20Sopenharmony_ci	}
3728c2ecf20Sopenharmony_ci
3738c2ecf20Sopenharmony_ci	ttc_sched_clock_val_reg = base + TTC_COUNT_VAL_OFFSET;
3748c2ecf20Sopenharmony_ci	sched_clock_register(ttc_sched_clock_read, timer_width,
3758c2ecf20Sopenharmony_ci			     ttccs->ttc.freq / PRESCALE);
3768c2ecf20Sopenharmony_ci
3778c2ecf20Sopenharmony_ci	return 0;
3788c2ecf20Sopenharmony_ci}
3798c2ecf20Sopenharmony_ci
3808c2ecf20Sopenharmony_cistatic int ttc_rate_change_clockevent_cb(struct notifier_block *nb,
3818c2ecf20Sopenharmony_ci		unsigned long event, void *data)
3828c2ecf20Sopenharmony_ci{
3838c2ecf20Sopenharmony_ci	struct clk_notifier_data *ndata = data;
3848c2ecf20Sopenharmony_ci	struct ttc_timer *ttc = to_ttc_timer(nb);
3858c2ecf20Sopenharmony_ci	struct ttc_timer_clockevent *ttcce = container_of(ttc,
3868c2ecf20Sopenharmony_ci			struct ttc_timer_clockevent, ttc);
3878c2ecf20Sopenharmony_ci
3888c2ecf20Sopenharmony_ci	switch (event) {
3898c2ecf20Sopenharmony_ci	case POST_RATE_CHANGE:
3908c2ecf20Sopenharmony_ci		/* update cached frequency */
3918c2ecf20Sopenharmony_ci		ttc->freq = ndata->new_rate;
3928c2ecf20Sopenharmony_ci
3938c2ecf20Sopenharmony_ci		clockevents_update_freq(&ttcce->ce, ndata->new_rate / PRESCALE);
3948c2ecf20Sopenharmony_ci
3958c2ecf20Sopenharmony_ci		fallthrough;
3968c2ecf20Sopenharmony_ci	case PRE_RATE_CHANGE:
3978c2ecf20Sopenharmony_ci	case ABORT_RATE_CHANGE:
3988c2ecf20Sopenharmony_ci	default:
3998c2ecf20Sopenharmony_ci		return NOTIFY_DONE;
4008c2ecf20Sopenharmony_ci	}
4018c2ecf20Sopenharmony_ci}
4028c2ecf20Sopenharmony_ci
4038c2ecf20Sopenharmony_cistatic int __init ttc_setup_clockevent(struct clk *clk,
4048c2ecf20Sopenharmony_ci				       void __iomem *base, u32 irq)
4058c2ecf20Sopenharmony_ci{
4068c2ecf20Sopenharmony_ci	struct ttc_timer_clockevent *ttcce;
4078c2ecf20Sopenharmony_ci	int err;
4088c2ecf20Sopenharmony_ci
4098c2ecf20Sopenharmony_ci	ttcce = kzalloc(sizeof(*ttcce), GFP_KERNEL);
4108c2ecf20Sopenharmony_ci	if (!ttcce)
4118c2ecf20Sopenharmony_ci		return -ENOMEM;
4128c2ecf20Sopenharmony_ci
4138c2ecf20Sopenharmony_ci	ttcce->ttc.clk = clk;
4148c2ecf20Sopenharmony_ci
4158c2ecf20Sopenharmony_ci	err = clk_prepare_enable(ttcce->ttc.clk);
4168c2ecf20Sopenharmony_ci	if (err)
4178c2ecf20Sopenharmony_ci		goto out_kfree;
4188c2ecf20Sopenharmony_ci
4198c2ecf20Sopenharmony_ci	ttcce->ttc.clk_rate_change_nb.notifier_call =
4208c2ecf20Sopenharmony_ci		ttc_rate_change_clockevent_cb;
4218c2ecf20Sopenharmony_ci	ttcce->ttc.clk_rate_change_nb.next = NULL;
4228c2ecf20Sopenharmony_ci
4238c2ecf20Sopenharmony_ci	err = clk_notifier_register(ttcce->ttc.clk,
4248c2ecf20Sopenharmony_ci				    &ttcce->ttc.clk_rate_change_nb);
4258c2ecf20Sopenharmony_ci	if (err) {
4268c2ecf20Sopenharmony_ci		pr_warn("Unable to register clock notifier.\n");
4278c2ecf20Sopenharmony_ci		goto out_kfree;
4288c2ecf20Sopenharmony_ci	}
4298c2ecf20Sopenharmony_ci
4308c2ecf20Sopenharmony_ci	ttcce->ttc.freq = clk_get_rate(ttcce->ttc.clk);
4318c2ecf20Sopenharmony_ci
4328c2ecf20Sopenharmony_ci	ttcce->ttc.base_addr = base;
4338c2ecf20Sopenharmony_ci	ttcce->ce.name = "ttc_clockevent";
4348c2ecf20Sopenharmony_ci	ttcce->ce.features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT;
4358c2ecf20Sopenharmony_ci	ttcce->ce.set_next_event = ttc_set_next_event;
4368c2ecf20Sopenharmony_ci	ttcce->ce.set_state_shutdown = ttc_shutdown;
4378c2ecf20Sopenharmony_ci	ttcce->ce.set_state_periodic = ttc_set_periodic;
4388c2ecf20Sopenharmony_ci	ttcce->ce.set_state_oneshot = ttc_shutdown;
4398c2ecf20Sopenharmony_ci	ttcce->ce.tick_resume = ttc_resume;
4408c2ecf20Sopenharmony_ci	ttcce->ce.rating = 200;
4418c2ecf20Sopenharmony_ci	ttcce->ce.irq = irq;
4428c2ecf20Sopenharmony_ci	ttcce->ce.cpumask = cpu_possible_mask;
4438c2ecf20Sopenharmony_ci
4448c2ecf20Sopenharmony_ci	/*
4458c2ecf20Sopenharmony_ci	 * Setup the clock event timer to be an interval timer which
4468c2ecf20Sopenharmony_ci	 * is prescaled by 32 using the interval interrupt. Leave it
4478c2ecf20Sopenharmony_ci	 * disabled for now.
4488c2ecf20Sopenharmony_ci	 */
4498c2ecf20Sopenharmony_ci	writel_relaxed(0x23, ttcce->ttc.base_addr + TTC_CNT_CNTRL_OFFSET);
4508c2ecf20Sopenharmony_ci	writel_relaxed(CLK_CNTRL_PRESCALE | CLK_CNTRL_PRESCALE_EN,
4518c2ecf20Sopenharmony_ci		     ttcce->ttc.base_addr + TTC_CLK_CNTRL_OFFSET);
4528c2ecf20Sopenharmony_ci	writel_relaxed(0x1,  ttcce->ttc.base_addr + TTC_IER_OFFSET);
4538c2ecf20Sopenharmony_ci
4548c2ecf20Sopenharmony_ci	err = request_irq(irq, ttc_clock_event_interrupt,
4558c2ecf20Sopenharmony_ci			  IRQF_TIMER, ttcce->ce.name, ttcce);
4568c2ecf20Sopenharmony_ci	if (err)
4578c2ecf20Sopenharmony_ci		goto out_kfree;
4588c2ecf20Sopenharmony_ci
4598c2ecf20Sopenharmony_ci	clockevents_config_and_register(&ttcce->ce,
4608c2ecf20Sopenharmony_ci			ttcce->ttc.freq / PRESCALE, 1, 0xfffe);
4618c2ecf20Sopenharmony_ci
4628c2ecf20Sopenharmony_ci	return 0;
4638c2ecf20Sopenharmony_ci
4648c2ecf20Sopenharmony_ciout_kfree:
4658c2ecf20Sopenharmony_ci	kfree(ttcce);
4668c2ecf20Sopenharmony_ci	return err;
4678c2ecf20Sopenharmony_ci}
4688c2ecf20Sopenharmony_ci
4698c2ecf20Sopenharmony_cistatic int __init ttc_timer_probe(struct platform_device *pdev)
4708c2ecf20Sopenharmony_ci{
4718c2ecf20Sopenharmony_ci	unsigned int irq;
4728c2ecf20Sopenharmony_ci	void __iomem *timer_baseaddr;
4738c2ecf20Sopenharmony_ci	struct clk *clk_cs, *clk_ce;
4748c2ecf20Sopenharmony_ci	static int initialized;
4758c2ecf20Sopenharmony_ci	int clksel, ret;
4768c2ecf20Sopenharmony_ci	u32 timer_width = 16;
4778c2ecf20Sopenharmony_ci	struct device_node *timer = pdev->dev.of_node;
4788c2ecf20Sopenharmony_ci
4798c2ecf20Sopenharmony_ci	if (initialized)
4808c2ecf20Sopenharmony_ci		return 0;
4818c2ecf20Sopenharmony_ci
4828c2ecf20Sopenharmony_ci	initialized = 1;
4838c2ecf20Sopenharmony_ci
4848c2ecf20Sopenharmony_ci	/*
4858c2ecf20Sopenharmony_ci	 * Get the 1st Triple Timer Counter (TTC) block from the device tree
4868c2ecf20Sopenharmony_ci	 * and use it. Note that the event timer uses the interrupt and it's the
4878c2ecf20Sopenharmony_ci	 * 2nd TTC hence the irq_of_parse_and_map(,1)
4888c2ecf20Sopenharmony_ci	 */
4898c2ecf20Sopenharmony_ci	timer_baseaddr = devm_of_iomap(&pdev->dev, timer, 0, NULL);
4908c2ecf20Sopenharmony_ci	if (IS_ERR(timer_baseaddr)) {
4918c2ecf20Sopenharmony_ci		pr_err("ERROR: invalid timer base address\n");
4928c2ecf20Sopenharmony_ci		return PTR_ERR(timer_baseaddr);
4938c2ecf20Sopenharmony_ci	}
4948c2ecf20Sopenharmony_ci
4958c2ecf20Sopenharmony_ci	irq = irq_of_parse_and_map(timer, 1);
4968c2ecf20Sopenharmony_ci	if (irq <= 0) {
4978c2ecf20Sopenharmony_ci		pr_err("ERROR: invalid interrupt number\n");
4988c2ecf20Sopenharmony_ci		return -EINVAL;
4998c2ecf20Sopenharmony_ci	}
5008c2ecf20Sopenharmony_ci
5018c2ecf20Sopenharmony_ci	of_property_read_u32(timer, "timer-width", &timer_width);
5028c2ecf20Sopenharmony_ci
5038c2ecf20Sopenharmony_ci	clksel = readl_relaxed(timer_baseaddr + TTC_CLK_CNTRL_OFFSET);
5048c2ecf20Sopenharmony_ci	clksel = !!(clksel & TTC_CLK_CNTRL_CSRC_MASK);
5058c2ecf20Sopenharmony_ci	clk_cs = of_clk_get(timer, clksel);
5068c2ecf20Sopenharmony_ci	if (IS_ERR(clk_cs)) {
5078c2ecf20Sopenharmony_ci		pr_err("ERROR: timer input clock not found\n");
5088c2ecf20Sopenharmony_ci		return PTR_ERR(clk_cs);
5098c2ecf20Sopenharmony_ci	}
5108c2ecf20Sopenharmony_ci
5118c2ecf20Sopenharmony_ci	clksel = readl_relaxed(timer_baseaddr + 4 + TTC_CLK_CNTRL_OFFSET);
5128c2ecf20Sopenharmony_ci	clksel = !!(clksel & TTC_CLK_CNTRL_CSRC_MASK);
5138c2ecf20Sopenharmony_ci	clk_ce = of_clk_get(timer, clksel);
5148c2ecf20Sopenharmony_ci	if (IS_ERR(clk_ce)) {
5158c2ecf20Sopenharmony_ci		pr_err("ERROR: timer input clock not found\n");
5168c2ecf20Sopenharmony_ci		ret = PTR_ERR(clk_ce);
5178c2ecf20Sopenharmony_ci		goto put_clk_cs;
5188c2ecf20Sopenharmony_ci	}
5198c2ecf20Sopenharmony_ci
5208c2ecf20Sopenharmony_ci	ret = ttc_setup_clocksource(clk_cs, timer_baseaddr, timer_width);
5218c2ecf20Sopenharmony_ci	if (ret)
5228c2ecf20Sopenharmony_ci		goto put_clk_ce;
5238c2ecf20Sopenharmony_ci
5248c2ecf20Sopenharmony_ci	ret = ttc_setup_clockevent(clk_ce, timer_baseaddr + 4, irq);
5258c2ecf20Sopenharmony_ci	if (ret)
5268c2ecf20Sopenharmony_ci		goto put_clk_ce;
5278c2ecf20Sopenharmony_ci
5288c2ecf20Sopenharmony_ci	pr_info("%pOFn #0 at %p, irq=%d\n", timer, timer_baseaddr, irq);
5298c2ecf20Sopenharmony_ci
5308c2ecf20Sopenharmony_ci	return 0;
5318c2ecf20Sopenharmony_ci
5328c2ecf20Sopenharmony_ciput_clk_ce:
5338c2ecf20Sopenharmony_ci	clk_put(clk_ce);
5348c2ecf20Sopenharmony_ciput_clk_cs:
5358c2ecf20Sopenharmony_ci	clk_put(clk_cs);
5368c2ecf20Sopenharmony_ci	return ret;
5378c2ecf20Sopenharmony_ci}
5388c2ecf20Sopenharmony_ci
5398c2ecf20Sopenharmony_cistatic const struct of_device_id ttc_timer_of_match[] = {
5408c2ecf20Sopenharmony_ci	{.compatible = "cdns,ttc"},
5418c2ecf20Sopenharmony_ci	{},
5428c2ecf20Sopenharmony_ci};
5438c2ecf20Sopenharmony_ci
5448c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, ttc_timer_of_match);
5458c2ecf20Sopenharmony_ci
5468c2ecf20Sopenharmony_cistatic struct platform_driver ttc_timer_driver = {
5478c2ecf20Sopenharmony_ci	.driver = {
5488c2ecf20Sopenharmony_ci		.name	= "cdns_ttc_timer",
5498c2ecf20Sopenharmony_ci		.of_match_table = ttc_timer_of_match,
5508c2ecf20Sopenharmony_ci	},
5518c2ecf20Sopenharmony_ci};
5528c2ecf20Sopenharmony_cibuiltin_platform_driver_probe(ttc_timer_driver, ttc_timer_probe);
553