xref: /openbmc/linux/drivers/clocksource/timer-cadence-ttc.c (revision c900529f3d9161bfde5cca0754f83b4d3c3e0220)
19c92ab61SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
29d8d47eaSDaniel Lezcano /*
39d8d47eaSDaniel Lezcano  * This file contains driver for the Cadence Triple Timer Counter Rev 06
49d8d47eaSDaniel Lezcano  *
59d8d47eaSDaniel Lezcano  *  Copyright (C) 2011-2013 Xilinx
69d8d47eaSDaniel Lezcano  *
79d8d47eaSDaniel Lezcano  * based on arch/mips/kernel/time.c timer driver
89d8d47eaSDaniel Lezcano  */
99d8d47eaSDaniel Lezcano 
109d8d47eaSDaniel Lezcano #include <linux/clk.h>
119d8d47eaSDaniel Lezcano #include <linux/interrupt.h>
129d8d47eaSDaniel Lezcano #include <linux/clockchips.h>
139d8d47eaSDaniel Lezcano #include <linux/clocksource.h>
149d8d47eaSDaniel Lezcano #include <linux/of_address.h>
159d8d47eaSDaniel Lezcano #include <linux/of_irq.h>
16*6303d069SRob Herring #include <linux/platform_device.h>
179d8d47eaSDaniel Lezcano #include <linux/slab.h>
189d8d47eaSDaniel Lezcano #include <linux/sched_clock.h>
19f5ac896bSRajan Vaja #include <linux/module.h>
20f5ac896bSRajan Vaja #include <linux/of_platform.h>
219d8d47eaSDaniel Lezcano 
229d8d47eaSDaniel Lezcano /*
239d8d47eaSDaniel Lezcano  * This driver configures the 2 16/32-bit count-up timers as follows:
249d8d47eaSDaniel Lezcano  *
259d8d47eaSDaniel Lezcano  * T1: Timer 1, clocksource for generic timekeeping
269d8d47eaSDaniel Lezcano  * T2: Timer 2, clockevent source for hrtimers
279d8d47eaSDaniel Lezcano  * T3: Timer 3, <unused>
289d8d47eaSDaniel Lezcano  *
299d8d47eaSDaniel Lezcano  * The input frequency to the timer module for emulation is 2.5MHz which is
309d8d47eaSDaniel Lezcano  * common to all the timer channels (T1, T2, and T3). With a pre-scaler of 32,
319d8d47eaSDaniel Lezcano  * the timers are clocked at 78.125KHz (12.8 us resolution).
329d8d47eaSDaniel Lezcano 
339d8d47eaSDaniel Lezcano  * The input frequency to the timer module in silicon is configurable and
349d8d47eaSDaniel Lezcano  * obtained from device tree. The pre-scaler of 32 is used.
359d8d47eaSDaniel Lezcano  */
369d8d47eaSDaniel Lezcano 
379d8d47eaSDaniel Lezcano /*
389d8d47eaSDaniel Lezcano  * Timer Register Offset Definitions of Timer 1, Increment base address by 4
399d8d47eaSDaniel Lezcano  * and use same offsets for Timer 2
409d8d47eaSDaniel Lezcano  */
419d8d47eaSDaniel Lezcano #define TTC_CLK_CNTRL_OFFSET		0x00 /* Clock Control Reg, RW */
429d8d47eaSDaniel Lezcano #define TTC_CNT_CNTRL_OFFSET		0x0C /* Counter Control Reg, RW */
439d8d47eaSDaniel Lezcano #define TTC_COUNT_VAL_OFFSET		0x18 /* Counter Value Reg, RO */
449d8d47eaSDaniel Lezcano #define TTC_INTR_VAL_OFFSET		0x24 /* Interval Count Reg, RW */
459d8d47eaSDaniel Lezcano #define TTC_ISR_OFFSET		0x54 /* Interrupt Status Reg, RO */
469d8d47eaSDaniel Lezcano #define TTC_IER_OFFSET		0x60 /* Interrupt Enable Reg, RW */
479d8d47eaSDaniel Lezcano 
489d8d47eaSDaniel Lezcano #define TTC_CNT_CNTRL_DISABLE_MASK	0x1
499d8d47eaSDaniel Lezcano 
509d8d47eaSDaniel Lezcano #define TTC_CLK_CNTRL_CSRC_MASK		(1 << 5)	/* clock source */
519d8d47eaSDaniel Lezcano #define TTC_CLK_CNTRL_PSV_MASK		0x1e
529d8d47eaSDaniel Lezcano #define TTC_CLK_CNTRL_PSV_SHIFT		1
539d8d47eaSDaniel Lezcano 
549d8d47eaSDaniel Lezcano /*
559d8d47eaSDaniel Lezcano  * Setup the timers to use pre-scaling, using a fixed value for now that will
569d8d47eaSDaniel Lezcano  * work across most input frequency, but it may need to be more dynamic
579d8d47eaSDaniel Lezcano  */
589d8d47eaSDaniel Lezcano #define PRESCALE_EXPONENT	11	/* 2 ^ PRESCALE_EXPONENT = PRESCALE */
599d8d47eaSDaniel Lezcano #define PRESCALE		2048	/* The exponent must match this */
609d8d47eaSDaniel Lezcano #define CLK_CNTRL_PRESCALE	((PRESCALE_EXPONENT - 1) << 1)
619d8d47eaSDaniel Lezcano #define CLK_CNTRL_PRESCALE_EN	1
629d8d47eaSDaniel Lezcano #define CNT_CNTRL_RESET		(1 << 4)
639d8d47eaSDaniel Lezcano 
649d8d47eaSDaniel Lezcano #define MAX_F_ERR 50
659d8d47eaSDaniel Lezcano 
669d8d47eaSDaniel Lezcano /**
679d8d47eaSDaniel Lezcano  * struct ttc_timer - This definition defines local timer structure
689d8d47eaSDaniel Lezcano  *
699d8d47eaSDaniel Lezcano  * @base_addr:	Base address of timer
709d8d47eaSDaniel Lezcano  * @freq:	Timer input clock frequency
719d8d47eaSDaniel Lezcano  * @clk:	Associated clock source
729d8d47eaSDaniel Lezcano  * @clk_rate_change_nb	Notifier block for clock rate changes
739d8d47eaSDaniel Lezcano  */
749d8d47eaSDaniel Lezcano struct ttc_timer {
759d8d47eaSDaniel Lezcano 	void __iomem *base_addr;
769d8d47eaSDaniel Lezcano 	unsigned long freq;
779d8d47eaSDaniel Lezcano 	struct clk *clk;
789d8d47eaSDaniel Lezcano 	struct notifier_block clk_rate_change_nb;
799d8d47eaSDaniel Lezcano };
809d8d47eaSDaniel Lezcano 
819d8d47eaSDaniel Lezcano #define to_ttc_timer(x) \
829d8d47eaSDaniel Lezcano 		container_of(x, struct ttc_timer, clk_rate_change_nb)
839d8d47eaSDaniel Lezcano 
849d8d47eaSDaniel Lezcano struct ttc_timer_clocksource {
859d8d47eaSDaniel Lezcano 	u32			scale_clk_ctrl_reg_old;
869d8d47eaSDaniel Lezcano 	u32			scale_clk_ctrl_reg_new;
879d8d47eaSDaniel Lezcano 	struct ttc_timer	ttc;
889d8d47eaSDaniel Lezcano 	struct clocksource	cs;
899d8d47eaSDaniel Lezcano };
909d8d47eaSDaniel Lezcano 
919d8d47eaSDaniel Lezcano #define to_ttc_timer_clksrc(x) \
929d8d47eaSDaniel Lezcano 		container_of(x, struct ttc_timer_clocksource, cs)
939d8d47eaSDaniel Lezcano 
949d8d47eaSDaniel Lezcano struct ttc_timer_clockevent {
959d8d47eaSDaniel Lezcano 	struct ttc_timer		ttc;
969d8d47eaSDaniel Lezcano 	struct clock_event_device	ce;
979d8d47eaSDaniel Lezcano };
989d8d47eaSDaniel Lezcano 
999d8d47eaSDaniel Lezcano #define to_ttc_timer_clkevent(x) \
1009d8d47eaSDaniel Lezcano 		container_of(x, struct ttc_timer_clockevent, ce)
1019d8d47eaSDaniel Lezcano 
1029d8d47eaSDaniel Lezcano static void __iomem *ttc_sched_clock_val_reg;
1039d8d47eaSDaniel Lezcano 
1049d8d47eaSDaniel Lezcano /**
1059d8d47eaSDaniel Lezcano  * ttc_set_interval - Set the timer interval value
1069d8d47eaSDaniel Lezcano  *
1079d8d47eaSDaniel Lezcano  * @timer:	Pointer to the timer instance
1089d8d47eaSDaniel Lezcano  * @cycles:	Timer interval ticks
1099d8d47eaSDaniel Lezcano  **/
ttc_set_interval(struct ttc_timer * timer,unsigned long cycles)1109d8d47eaSDaniel Lezcano static void ttc_set_interval(struct ttc_timer *timer,
1119d8d47eaSDaniel Lezcano 					unsigned long cycles)
1129d8d47eaSDaniel Lezcano {
1139d8d47eaSDaniel Lezcano 	u32 ctrl_reg;
1149d8d47eaSDaniel Lezcano 
1159d8d47eaSDaniel Lezcano 	/* Disable the counter, set the counter value  and re-enable counter */
1169d8d47eaSDaniel Lezcano 	ctrl_reg = readl_relaxed(timer->base_addr + TTC_CNT_CNTRL_OFFSET);
1179d8d47eaSDaniel Lezcano 	ctrl_reg |= TTC_CNT_CNTRL_DISABLE_MASK;
1189d8d47eaSDaniel Lezcano 	writel_relaxed(ctrl_reg, timer->base_addr + TTC_CNT_CNTRL_OFFSET);
1199d8d47eaSDaniel Lezcano 
1209d8d47eaSDaniel Lezcano 	writel_relaxed(cycles, timer->base_addr + TTC_INTR_VAL_OFFSET);
1219d8d47eaSDaniel Lezcano 
1229d8d47eaSDaniel Lezcano 	/*
1239d8d47eaSDaniel Lezcano 	 * Reset the counter (0x10) so that it starts from 0, one-shot
1249d8d47eaSDaniel Lezcano 	 * mode makes this needed for timing to be right.
1259d8d47eaSDaniel Lezcano 	 */
1269d8d47eaSDaniel Lezcano 	ctrl_reg |= CNT_CNTRL_RESET;
1279d8d47eaSDaniel Lezcano 	ctrl_reg &= ~TTC_CNT_CNTRL_DISABLE_MASK;
1289d8d47eaSDaniel Lezcano 	writel_relaxed(ctrl_reg, timer->base_addr + TTC_CNT_CNTRL_OFFSET);
1299d8d47eaSDaniel Lezcano }
1309d8d47eaSDaniel Lezcano 
1319d8d47eaSDaniel Lezcano /**
1329d8d47eaSDaniel Lezcano  * ttc_clock_event_interrupt - Clock event timer interrupt handler
1339d8d47eaSDaniel Lezcano  *
1349d8d47eaSDaniel Lezcano  * @irq:	IRQ number of the Timer
1359d8d47eaSDaniel Lezcano  * @dev_id:	void pointer to the ttc_timer instance
1369d8d47eaSDaniel Lezcano  *
1379d8d47eaSDaniel Lezcano  * returns: Always IRQ_HANDLED - success
1389d8d47eaSDaniel Lezcano  **/
ttc_clock_event_interrupt(int irq,void * dev_id)1399d8d47eaSDaniel Lezcano static irqreturn_t ttc_clock_event_interrupt(int irq, void *dev_id)
1409d8d47eaSDaniel Lezcano {
1419d8d47eaSDaniel Lezcano 	struct ttc_timer_clockevent *ttce = dev_id;
1429d8d47eaSDaniel Lezcano 	struct ttc_timer *timer = &ttce->ttc;
1439d8d47eaSDaniel Lezcano 
1449d8d47eaSDaniel Lezcano 	/* Acknowledge the interrupt and call event handler */
1459d8d47eaSDaniel Lezcano 	readl_relaxed(timer->base_addr + TTC_ISR_OFFSET);
1469d8d47eaSDaniel Lezcano 
1479d8d47eaSDaniel Lezcano 	ttce->ce.event_handler(&ttce->ce);
1489d8d47eaSDaniel Lezcano 
1499d8d47eaSDaniel Lezcano 	return IRQ_HANDLED;
1509d8d47eaSDaniel Lezcano }
1519d8d47eaSDaniel Lezcano 
1529d8d47eaSDaniel Lezcano /**
1539d8d47eaSDaniel Lezcano  * __ttc_clocksource_read - Reads the timer counter register
1549d8d47eaSDaniel Lezcano  *
1559d8d47eaSDaniel Lezcano  * returns: Current timer counter register value
1569d8d47eaSDaniel Lezcano  **/
__ttc_clocksource_read(struct clocksource * cs)1579d8d47eaSDaniel Lezcano static u64 __ttc_clocksource_read(struct clocksource *cs)
1589d8d47eaSDaniel Lezcano {
1599d8d47eaSDaniel Lezcano 	struct ttc_timer *timer = &to_ttc_timer_clksrc(cs)->ttc;
1609d8d47eaSDaniel Lezcano 
1619d8d47eaSDaniel Lezcano 	return (u64)readl_relaxed(timer->base_addr +
1629d8d47eaSDaniel Lezcano 				TTC_COUNT_VAL_OFFSET);
1639d8d47eaSDaniel Lezcano }
1649d8d47eaSDaniel Lezcano 
ttc_sched_clock_read(void)1659d8d47eaSDaniel Lezcano static u64 notrace ttc_sched_clock_read(void)
1669d8d47eaSDaniel Lezcano {
1679d8d47eaSDaniel Lezcano 	return readl_relaxed(ttc_sched_clock_val_reg);
1689d8d47eaSDaniel Lezcano }
1699d8d47eaSDaniel Lezcano 
1709d8d47eaSDaniel Lezcano /**
1719d8d47eaSDaniel Lezcano  * ttc_set_next_event - Sets the time interval for next event
1729d8d47eaSDaniel Lezcano  *
1739d8d47eaSDaniel Lezcano  * @cycles:	Timer interval ticks
1749d8d47eaSDaniel Lezcano  * @evt:	Address of clock event instance
1759d8d47eaSDaniel Lezcano  *
1769d8d47eaSDaniel Lezcano  * returns: Always 0 - success
1779d8d47eaSDaniel Lezcano  **/
ttc_set_next_event(unsigned long cycles,struct clock_event_device * evt)1789d8d47eaSDaniel Lezcano static int ttc_set_next_event(unsigned long cycles,
1799d8d47eaSDaniel Lezcano 					struct clock_event_device *evt)
1809d8d47eaSDaniel Lezcano {
1819d8d47eaSDaniel Lezcano 	struct ttc_timer_clockevent *ttce = to_ttc_timer_clkevent(evt);
1829d8d47eaSDaniel Lezcano 	struct ttc_timer *timer = &ttce->ttc;
1839d8d47eaSDaniel Lezcano 
1849d8d47eaSDaniel Lezcano 	ttc_set_interval(timer, cycles);
1859d8d47eaSDaniel Lezcano 	return 0;
1869d8d47eaSDaniel Lezcano }
1879d8d47eaSDaniel Lezcano 
1889d8d47eaSDaniel Lezcano /**
1899d8d47eaSDaniel Lezcano  * ttc_set_{shutdown|oneshot|periodic} - Sets the state of timer
1909d8d47eaSDaniel Lezcano  *
1919d8d47eaSDaniel Lezcano  * @evt:	Address of clock event instance
1929d8d47eaSDaniel Lezcano  **/
ttc_shutdown(struct clock_event_device * evt)1939d8d47eaSDaniel Lezcano static int ttc_shutdown(struct clock_event_device *evt)
1949d8d47eaSDaniel Lezcano {
1959d8d47eaSDaniel Lezcano 	struct ttc_timer_clockevent *ttce = to_ttc_timer_clkevent(evt);
1969d8d47eaSDaniel Lezcano 	struct ttc_timer *timer = &ttce->ttc;
1979d8d47eaSDaniel Lezcano 	u32 ctrl_reg;
1989d8d47eaSDaniel Lezcano 
1999d8d47eaSDaniel Lezcano 	ctrl_reg = readl_relaxed(timer->base_addr + TTC_CNT_CNTRL_OFFSET);
2009d8d47eaSDaniel Lezcano 	ctrl_reg |= TTC_CNT_CNTRL_DISABLE_MASK;
2019d8d47eaSDaniel Lezcano 	writel_relaxed(ctrl_reg, timer->base_addr + TTC_CNT_CNTRL_OFFSET);
2029d8d47eaSDaniel Lezcano 	return 0;
2039d8d47eaSDaniel Lezcano }
2049d8d47eaSDaniel Lezcano 
ttc_set_periodic(struct clock_event_device * evt)2059d8d47eaSDaniel Lezcano static int ttc_set_periodic(struct clock_event_device *evt)
2069d8d47eaSDaniel Lezcano {
2079d8d47eaSDaniel Lezcano 	struct ttc_timer_clockevent *ttce = to_ttc_timer_clkevent(evt);
2089d8d47eaSDaniel Lezcano 	struct ttc_timer *timer = &ttce->ttc;
2099d8d47eaSDaniel Lezcano 
2109d8d47eaSDaniel Lezcano 	ttc_set_interval(timer,
2119d8d47eaSDaniel Lezcano 			 DIV_ROUND_CLOSEST(ttce->ttc.freq, PRESCALE * HZ));
2129d8d47eaSDaniel Lezcano 	return 0;
2139d8d47eaSDaniel Lezcano }
2149d8d47eaSDaniel Lezcano 
ttc_resume(struct clock_event_device * evt)2159d8d47eaSDaniel Lezcano static int ttc_resume(struct clock_event_device *evt)
2169d8d47eaSDaniel Lezcano {
2179d8d47eaSDaniel Lezcano 	struct ttc_timer_clockevent *ttce = to_ttc_timer_clkevent(evt);
2189d8d47eaSDaniel Lezcano 	struct ttc_timer *timer = &ttce->ttc;
2199d8d47eaSDaniel Lezcano 	u32 ctrl_reg;
2209d8d47eaSDaniel Lezcano 
2219d8d47eaSDaniel Lezcano 	ctrl_reg = readl_relaxed(timer->base_addr + TTC_CNT_CNTRL_OFFSET);
2229d8d47eaSDaniel Lezcano 	ctrl_reg &= ~TTC_CNT_CNTRL_DISABLE_MASK;
2239d8d47eaSDaniel Lezcano 	writel_relaxed(ctrl_reg, timer->base_addr + TTC_CNT_CNTRL_OFFSET);
2249d8d47eaSDaniel Lezcano 	return 0;
2259d8d47eaSDaniel Lezcano }
2269d8d47eaSDaniel Lezcano 
ttc_rate_change_clocksource_cb(struct notifier_block * nb,unsigned long event,void * data)2279d8d47eaSDaniel Lezcano static int ttc_rate_change_clocksource_cb(struct notifier_block *nb,
2289d8d47eaSDaniel Lezcano 		unsigned long event, void *data)
2299d8d47eaSDaniel Lezcano {
2309d8d47eaSDaniel Lezcano 	struct clk_notifier_data *ndata = data;
2319d8d47eaSDaniel Lezcano 	struct ttc_timer *ttc = to_ttc_timer(nb);
2329d8d47eaSDaniel Lezcano 	struct ttc_timer_clocksource *ttccs = container_of(ttc,
2339d8d47eaSDaniel Lezcano 			struct ttc_timer_clocksource, ttc);
2349d8d47eaSDaniel Lezcano 
2359d8d47eaSDaniel Lezcano 	switch (event) {
2369d8d47eaSDaniel Lezcano 	case PRE_RATE_CHANGE:
2379d8d47eaSDaniel Lezcano 	{
2389d8d47eaSDaniel Lezcano 		u32 psv;
2399d8d47eaSDaniel Lezcano 		unsigned long factor, rate_low, rate_high;
2409d8d47eaSDaniel Lezcano 
2419d8d47eaSDaniel Lezcano 		if (ndata->new_rate > ndata->old_rate) {
2429d8d47eaSDaniel Lezcano 			factor = DIV_ROUND_CLOSEST(ndata->new_rate,
2439d8d47eaSDaniel Lezcano 					ndata->old_rate);
2449d8d47eaSDaniel Lezcano 			rate_low = ndata->old_rate;
2459d8d47eaSDaniel Lezcano 			rate_high = ndata->new_rate;
2469d8d47eaSDaniel Lezcano 		} else {
2479d8d47eaSDaniel Lezcano 			factor = DIV_ROUND_CLOSEST(ndata->old_rate,
2489d8d47eaSDaniel Lezcano 					ndata->new_rate);
2499d8d47eaSDaniel Lezcano 			rate_low = ndata->new_rate;
2509d8d47eaSDaniel Lezcano 			rate_high = ndata->old_rate;
2519d8d47eaSDaniel Lezcano 		}
2529d8d47eaSDaniel Lezcano 
2539d8d47eaSDaniel Lezcano 		if (!is_power_of_2(factor))
2549d8d47eaSDaniel Lezcano 				return NOTIFY_BAD;
2559d8d47eaSDaniel Lezcano 
2569d8d47eaSDaniel Lezcano 		if (abs(rate_high - (factor * rate_low)) > MAX_F_ERR)
2579d8d47eaSDaniel Lezcano 			return NOTIFY_BAD;
2589d8d47eaSDaniel Lezcano 
2599d8d47eaSDaniel Lezcano 		factor = __ilog2_u32(factor);
2609d8d47eaSDaniel Lezcano 
2619d8d47eaSDaniel Lezcano 		/*
2629d8d47eaSDaniel Lezcano 		 * store timer clock ctrl register so we can restore it in case
2639d8d47eaSDaniel Lezcano 		 * of an abort.
2649d8d47eaSDaniel Lezcano 		 */
2659d8d47eaSDaniel Lezcano 		ttccs->scale_clk_ctrl_reg_old =
2669d8d47eaSDaniel Lezcano 			readl_relaxed(ttccs->ttc.base_addr +
2679d8d47eaSDaniel Lezcano 			TTC_CLK_CNTRL_OFFSET);
2689d8d47eaSDaniel Lezcano 
2699d8d47eaSDaniel Lezcano 		psv = (ttccs->scale_clk_ctrl_reg_old &
2709d8d47eaSDaniel Lezcano 				TTC_CLK_CNTRL_PSV_MASK) >>
2719d8d47eaSDaniel Lezcano 				TTC_CLK_CNTRL_PSV_SHIFT;
2729d8d47eaSDaniel Lezcano 		if (ndata->new_rate < ndata->old_rate)
2739d8d47eaSDaniel Lezcano 			psv -= factor;
2749d8d47eaSDaniel Lezcano 		else
2759d8d47eaSDaniel Lezcano 			psv += factor;
2769d8d47eaSDaniel Lezcano 
2779d8d47eaSDaniel Lezcano 		/* prescaler within legal range? */
2789d8d47eaSDaniel Lezcano 		if (psv & ~(TTC_CLK_CNTRL_PSV_MASK >> TTC_CLK_CNTRL_PSV_SHIFT))
2799d8d47eaSDaniel Lezcano 			return NOTIFY_BAD;
2809d8d47eaSDaniel Lezcano 
2819d8d47eaSDaniel Lezcano 		ttccs->scale_clk_ctrl_reg_new = ttccs->scale_clk_ctrl_reg_old &
2829d8d47eaSDaniel Lezcano 			~TTC_CLK_CNTRL_PSV_MASK;
2839d8d47eaSDaniel Lezcano 		ttccs->scale_clk_ctrl_reg_new |= psv << TTC_CLK_CNTRL_PSV_SHIFT;
2849d8d47eaSDaniel Lezcano 
2859d8d47eaSDaniel Lezcano 
2869d8d47eaSDaniel Lezcano 		/* scale down: adjust divider in post-change notification */
2879d8d47eaSDaniel Lezcano 		if (ndata->new_rate < ndata->old_rate)
2889d8d47eaSDaniel Lezcano 			return NOTIFY_DONE;
2899d8d47eaSDaniel Lezcano 
2909d8d47eaSDaniel Lezcano 		/* scale up: adjust divider now - before frequency change */
2919d8d47eaSDaniel Lezcano 		writel_relaxed(ttccs->scale_clk_ctrl_reg_new,
2929d8d47eaSDaniel Lezcano 			       ttccs->ttc.base_addr + TTC_CLK_CNTRL_OFFSET);
2939d8d47eaSDaniel Lezcano 		break;
2949d8d47eaSDaniel Lezcano 	}
2959d8d47eaSDaniel Lezcano 	case POST_RATE_CHANGE:
2969d8d47eaSDaniel Lezcano 		/* scale up: pre-change notification did the adjustment */
2979d8d47eaSDaniel Lezcano 		if (ndata->new_rate > ndata->old_rate)
2989d8d47eaSDaniel Lezcano 			return NOTIFY_OK;
2999d8d47eaSDaniel Lezcano 
3009d8d47eaSDaniel Lezcano 		/* scale down: adjust divider now - after frequency change */
3019d8d47eaSDaniel Lezcano 		writel_relaxed(ttccs->scale_clk_ctrl_reg_new,
3029d8d47eaSDaniel Lezcano 			       ttccs->ttc.base_addr + TTC_CLK_CNTRL_OFFSET);
3039d8d47eaSDaniel Lezcano 		break;
3049d8d47eaSDaniel Lezcano 
3059d8d47eaSDaniel Lezcano 	case ABORT_RATE_CHANGE:
3069d8d47eaSDaniel Lezcano 		/* we have to undo the adjustment in case we scale up */
3079d8d47eaSDaniel Lezcano 		if (ndata->new_rate < ndata->old_rate)
3089d8d47eaSDaniel Lezcano 			return NOTIFY_OK;
3099d8d47eaSDaniel Lezcano 
3109d8d47eaSDaniel Lezcano 		/* restore original register value */
3119d8d47eaSDaniel Lezcano 		writel_relaxed(ttccs->scale_clk_ctrl_reg_old,
3129d8d47eaSDaniel Lezcano 			       ttccs->ttc.base_addr + TTC_CLK_CNTRL_OFFSET);
313df561f66SGustavo A. R. Silva 		fallthrough;
3149d8d47eaSDaniel Lezcano 	default:
3159d8d47eaSDaniel Lezcano 		return NOTIFY_DONE;
3169d8d47eaSDaniel Lezcano 	}
3179d8d47eaSDaniel Lezcano 
3189d8d47eaSDaniel Lezcano 	return NOTIFY_DONE;
3199d8d47eaSDaniel Lezcano }
3209d8d47eaSDaniel Lezcano 
ttc_setup_clocksource(struct clk * clk,void __iomem * base,u32 timer_width)3219d8d47eaSDaniel Lezcano static int __init ttc_setup_clocksource(struct clk *clk, void __iomem *base,
3229d8d47eaSDaniel Lezcano 					 u32 timer_width)
3239d8d47eaSDaniel Lezcano {
3249d8d47eaSDaniel Lezcano 	struct ttc_timer_clocksource *ttccs;
3259d8d47eaSDaniel Lezcano 	int err;
3269d8d47eaSDaniel Lezcano 
3279d8d47eaSDaniel Lezcano 	ttccs = kzalloc(sizeof(*ttccs), GFP_KERNEL);
3289d8d47eaSDaniel Lezcano 	if (!ttccs)
3299d8d47eaSDaniel Lezcano 		return -ENOMEM;
3309d8d47eaSDaniel Lezcano 
3319d8d47eaSDaniel Lezcano 	ttccs->ttc.clk = clk;
3329d8d47eaSDaniel Lezcano 
3339d8d47eaSDaniel Lezcano 	err = clk_prepare_enable(ttccs->ttc.clk);
3349d8d47eaSDaniel Lezcano 	if (err) {
3359d8d47eaSDaniel Lezcano 		kfree(ttccs);
3369d8d47eaSDaniel Lezcano 		return err;
3379d8d47eaSDaniel Lezcano 	}
3389d8d47eaSDaniel Lezcano 
3399d8d47eaSDaniel Lezcano 	ttccs->ttc.freq = clk_get_rate(ttccs->ttc.clk);
3409d8d47eaSDaniel Lezcano 
3419d8d47eaSDaniel Lezcano 	ttccs->ttc.clk_rate_change_nb.notifier_call =
3429d8d47eaSDaniel Lezcano 		ttc_rate_change_clocksource_cb;
3439d8d47eaSDaniel Lezcano 	ttccs->ttc.clk_rate_change_nb.next = NULL;
3449d8d47eaSDaniel Lezcano 
3459d8d47eaSDaniel Lezcano 	err = clk_notifier_register(ttccs->ttc.clk,
3469d8d47eaSDaniel Lezcano 				    &ttccs->ttc.clk_rate_change_nb);
3479d8d47eaSDaniel Lezcano 	if (err)
3489d8d47eaSDaniel Lezcano 		pr_warn("Unable to register clock notifier.\n");
3499d8d47eaSDaniel Lezcano 
3509d8d47eaSDaniel Lezcano 	ttccs->ttc.base_addr = base;
3519d8d47eaSDaniel Lezcano 	ttccs->cs.name = "ttc_clocksource";
3529d8d47eaSDaniel Lezcano 	ttccs->cs.rating = 200;
3539d8d47eaSDaniel Lezcano 	ttccs->cs.read = __ttc_clocksource_read;
3549d8d47eaSDaniel Lezcano 	ttccs->cs.mask = CLOCKSOURCE_MASK(timer_width);
3559d8d47eaSDaniel Lezcano 	ttccs->cs.flags = CLOCK_SOURCE_IS_CONTINUOUS;
3569d8d47eaSDaniel Lezcano 
3579d8d47eaSDaniel Lezcano 	/*
3589d8d47eaSDaniel Lezcano 	 * Setup the clock source counter to be an incrementing counter
3599d8d47eaSDaniel Lezcano 	 * with no interrupt and it rolls over at 0xFFFF. Pre-scale
3609d8d47eaSDaniel Lezcano 	 * it by 32 also. Let it start running now.
3619d8d47eaSDaniel Lezcano 	 */
3629d8d47eaSDaniel Lezcano 	writel_relaxed(0x0,  ttccs->ttc.base_addr + TTC_IER_OFFSET);
3639d8d47eaSDaniel Lezcano 	writel_relaxed(CLK_CNTRL_PRESCALE | CLK_CNTRL_PRESCALE_EN,
3649d8d47eaSDaniel Lezcano 		     ttccs->ttc.base_addr + TTC_CLK_CNTRL_OFFSET);
3659d8d47eaSDaniel Lezcano 	writel_relaxed(CNT_CNTRL_RESET,
3669d8d47eaSDaniel Lezcano 		     ttccs->ttc.base_addr + TTC_CNT_CNTRL_OFFSET);
3679d8d47eaSDaniel Lezcano 
3689d8d47eaSDaniel Lezcano 	err = clocksource_register_hz(&ttccs->cs, ttccs->ttc.freq / PRESCALE);
3699d8d47eaSDaniel Lezcano 	if (err) {
3709d8d47eaSDaniel Lezcano 		kfree(ttccs);
3719d8d47eaSDaniel Lezcano 		return err;
3729d8d47eaSDaniel Lezcano 	}
3739d8d47eaSDaniel Lezcano 
3749d8d47eaSDaniel Lezcano 	ttc_sched_clock_val_reg = base + TTC_COUNT_VAL_OFFSET;
3759d8d47eaSDaniel Lezcano 	sched_clock_register(ttc_sched_clock_read, timer_width,
3769d8d47eaSDaniel Lezcano 			     ttccs->ttc.freq / PRESCALE);
3779d8d47eaSDaniel Lezcano 
3789d8d47eaSDaniel Lezcano 	return 0;
3799d8d47eaSDaniel Lezcano }
3809d8d47eaSDaniel Lezcano 
ttc_rate_change_clockevent_cb(struct notifier_block * nb,unsigned long event,void * data)3819d8d47eaSDaniel Lezcano static int ttc_rate_change_clockevent_cb(struct notifier_block *nb,
3829d8d47eaSDaniel Lezcano 		unsigned long event, void *data)
3839d8d47eaSDaniel Lezcano {
3849d8d47eaSDaniel Lezcano 	struct clk_notifier_data *ndata = data;
3859d8d47eaSDaniel Lezcano 	struct ttc_timer *ttc = to_ttc_timer(nb);
3869d8d47eaSDaniel Lezcano 	struct ttc_timer_clockevent *ttcce = container_of(ttc,
3879d8d47eaSDaniel Lezcano 			struct ttc_timer_clockevent, ttc);
3889d8d47eaSDaniel Lezcano 
3899d8d47eaSDaniel Lezcano 	switch (event) {
3909d8d47eaSDaniel Lezcano 	case POST_RATE_CHANGE:
3919d8d47eaSDaniel Lezcano 		/* update cached frequency */
3929d8d47eaSDaniel Lezcano 		ttc->freq = ndata->new_rate;
3939d8d47eaSDaniel Lezcano 
3949d8d47eaSDaniel Lezcano 		clockevents_update_freq(&ttcce->ce, ndata->new_rate / PRESCALE);
3959d8d47eaSDaniel Lezcano 
396df561f66SGustavo A. R. Silva 		fallthrough;
3979d8d47eaSDaniel Lezcano 	case PRE_RATE_CHANGE:
3989d8d47eaSDaniel Lezcano 	case ABORT_RATE_CHANGE:
3999d8d47eaSDaniel Lezcano 	default:
4009d8d47eaSDaniel Lezcano 		return NOTIFY_DONE;
4019d8d47eaSDaniel Lezcano 	}
4029d8d47eaSDaniel Lezcano }
4039d8d47eaSDaniel Lezcano 
ttc_setup_clockevent(struct clk * clk,void __iomem * base,u32 irq)4049d8d47eaSDaniel Lezcano static int __init ttc_setup_clockevent(struct clk *clk,
4059d8d47eaSDaniel Lezcano 				       void __iomem *base, u32 irq)
4069d8d47eaSDaniel Lezcano {
4079d8d47eaSDaniel Lezcano 	struct ttc_timer_clockevent *ttcce;
4089d8d47eaSDaniel Lezcano 	int err;
4099d8d47eaSDaniel Lezcano 
4109d8d47eaSDaniel Lezcano 	ttcce = kzalloc(sizeof(*ttcce), GFP_KERNEL);
4119d8d47eaSDaniel Lezcano 	if (!ttcce)
4129d8d47eaSDaniel Lezcano 		return -ENOMEM;
4139d8d47eaSDaniel Lezcano 
4149d8d47eaSDaniel Lezcano 	ttcce->ttc.clk = clk;
4159d8d47eaSDaniel Lezcano 
4169d8d47eaSDaniel Lezcano 	err = clk_prepare_enable(ttcce->ttc.clk);
417eee422c4SYu Kuai 	if (err)
418eee422c4SYu Kuai 		goto out_kfree;
4199d8d47eaSDaniel Lezcano 
4209d8d47eaSDaniel Lezcano 	ttcce->ttc.clk_rate_change_nb.notifier_call =
4219d8d47eaSDaniel Lezcano 		ttc_rate_change_clockevent_cb;
4229d8d47eaSDaniel Lezcano 	ttcce->ttc.clk_rate_change_nb.next = NULL;
4239d8d47eaSDaniel Lezcano 
4249d8d47eaSDaniel Lezcano 	err = clk_notifier_register(ttcce->ttc.clk,
4259d8d47eaSDaniel Lezcano 				    &ttcce->ttc.clk_rate_change_nb);
4269d8d47eaSDaniel Lezcano 	if (err) {
4279d8d47eaSDaniel Lezcano 		pr_warn("Unable to register clock notifier.\n");
428eee422c4SYu Kuai 		goto out_kfree;
4299d8d47eaSDaniel Lezcano 	}
4309d8d47eaSDaniel Lezcano 
4319d8d47eaSDaniel Lezcano 	ttcce->ttc.freq = clk_get_rate(ttcce->ttc.clk);
4329d8d47eaSDaniel Lezcano 
4339d8d47eaSDaniel Lezcano 	ttcce->ttc.base_addr = base;
4349d8d47eaSDaniel Lezcano 	ttcce->ce.name = "ttc_clockevent";
4359d8d47eaSDaniel Lezcano 	ttcce->ce.features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT;
4369d8d47eaSDaniel Lezcano 	ttcce->ce.set_next_event = ttc_set_next_event;
4379d8d47eaSDaniel Lezcano 	ttcce->ce.set_state_shutdown = ttc_shutdown;
4389d8d47eaSDaniel Lezcano 	ttcce->ce.set_state_periodic = ttc_set_periodic;
4399d8d47eaSDaniel Lezcano 	ttcce->ce.set_state_oneshot = ttc_shutdown;
4409d8d47eaSDaniel Lezcano 	ttcce->ce.tick_resume = ttc_resume;
4419d8d47eaSDaniel Lezcano 	ttcce->ce.rating = 200;
4429d8d47eaSDaniel Lezcano 	ttcce->ce.irq = irq;
4439d8d47eaSDaniel Lezcano 	ttcce->ce.cpumask = cpu_possible_mask;
4449d8d47eaSDaniel Lezcano 
4459d8d47eaSDaniel Lezcano 	/*
4469d8d47eaSDaniel Lezcano 	 * Setup the clock event timer to be an interval timer which
4479d8d47eaSDaniel Lezcano 	 * is prescaled by 32 using the interval interrupt. Leave it
4489d8d47eaSDaniel Lezcano 	 * disabled for now.
4499d8d47eaSDaniel Lezcano 	 */
4509d8d47eaSDaniel Lezcano 	writel_relaxed(0x23, ttcce->ttc.base_addr + TTC_CNT_CNTRL_OFFSET);
4519d8d47eaSDaniel Lezcano 	writel_relaxed(CLK_CNTRL_PRESCALE | CLK_CNTRL_PRESCALE_EN,
4529d8d47eaSDaniel Lezcano 		     ttcce->ttc.base_addr + TTC_CLK_CNTRL_OFFSET);
4539d8d47eaSDaniel Lezcano 	writel_relaxed(0x1,  ttcce->ttc.base_addr + TTC_IER_OFFSET);
4549d8d47eaSDaniel Lezcano 
4559d8d47eaSDaniel Lezcano 	err = request_irq(irq, ttc_clock_event_interrupt,
4569d8d47eaSDaniel Lezcano 			  IRQF_TIMER, ttcce->ce.name, ttcce);
457eee422c4SYu Kuai 	if (err)
458eee422c4SYu Kuai 		goto out_kfree;
4599d8d47eaSDaniel Lezcano 
4609d8d47eaSDaniel Lezcano 	clockevents_config_and_register(&ttcce->ce,
4619d8d47eaSDaniel Lezcano 			ttcce->ttc.freq / PRESCALE, 1, 0xfffe);
4629d8d47eaSDaniel Lezcano 
4639d8d47eaSDaniel Lezcano 	return 0;
464eee422c4SYu Kuai 
465eee422c4SYu Kuai out_kfree:
466eee422c4SYu Kuai 	kfree(ttcce);
467eee422c4SYu Kuai 	return err;
4689d8d47eaSDaniel Lezcano }
4699d8d47eaSDaniel Lezcano 
ttc_timer_probe(struct platform_device * pdev)470f5ac896bSRajan Vaja static int __init ttc_timer_probe(struct platform_device *pdev)
4719d8d47eaSDaniel Lezcano {
4729d8d47eaSDaniel Lezcano 	unsigned int irq;
4739d8d47eaSDaniel Lezcano 	void __iomem *timer_baseaddr;
4749d8d47eaSDaniel Lezcano 	struct clk *clk_cs, *clk_ce;
4759d8d47eaSDaniel Lezcano 	static int initialized;
4769d8d47eaSDaniel Lezcano 	int clksel, ret;
4779d8d47eaSDaniel Lezcano 	u32 timer_width = 16;
478f5ac896bSRajan Vaja 	struct device_node *timer = pdev->dev.of_node;
4799d8d47eaSDaniel Lezcano 
4809d8d47eaSDaniel Lezcano 	if (initialized)
4819d8d47eaSDaniel Lezcano 		return 0;
4829d8d47eaSDaniel Lezcano 
4839d8d47eaSDaniel Lezcano 	initialized = 1;
4849d8d47eaSDaniel Lezcano 
4859d8d47eaSDaniel Lezcano 	/*
4869d8d47eaSDaniel Lezcano 	 * Get the 1st Triple Timer Counter (TTC) block from the device tree
4879d8d47eaSDaniel Lezcano 	 * and use it. Note that the event timer uses the interrupt and it's the
4889d8d47eaSDaniel Lezcano 	 * 2nd TTC hence the irq_of_parse_and_map(,1)
4899d8d47eaSDaniel Lezcano 	 */
4908b5bf64cSFeng Mingxi 	timer_baseaddr = devm_of_iomap(&pdev->dev, timer, 0, NULL);
4918b5bf64cSFeng Mingxi 	if (IS_ERR(timer_baseaddr)) {
4929d8d47eaSDaniel Lezcano 		pr_err("ERROR: invalid timer base address\n");
4938b5bf64cSFeng Mingxi 		return PTR_ERR(timer_baseaddr);
4949d8d47eaSDaniel Lezcano 	}
4959d8d47eaSDaniel Lezcano 
4969d8d47eaSDaniel Lezcano 	irq = irq_of_parse_and_map(timer, 1);
4979d8d47eaSDaniel Lezcano 	if (irq <= 0) {
4989d8d47eaSDaniel Lezcano 		pr_err("ERROR: invalid interrupt number\n");
4999d8d47eaSDaniel Lezcano 		return -EINVAL;
5009d8d47eaSDaniel Lezcano 	}
5019d8d47eaSDaniel Lezcano 
5029d8d47eaSDaniel Lezcano 	of_property_read_u32(timer, "timer-width", &timer_width);
5039d8d47eaSDaniel Lezcano 
5049d8d47eaSDaniel Lezcano 	clksel = readl_relaxed(timer_baseaddr + TTC_CLK_CNTRL_OFFSET);
5059d8d47eaSDaniel Lezcano 	clksel = !!(clksel & TTC_CLK_CNTRL_CSRC_MASK);
5069d8d47eaSDaniel Lezcano 	clk_cs = of_clk_get(timer, clksel);
5079d8d47eaSDaniel Lezcano 	if (IS_ERR(clk_cs)) {
5089d8d47eaSDaniel Lezcano 		pr_err("ERROR: timer input clock not found\n");
5099d8d47eaSDaniel Lezcano 		return PTR_ERR(clk_cs);
5109d8d47eaSDaniel Lezcano 	}
5119d8d47eaSDaniel Lezcano 
5129d8d47eaSDaniel Lezcano 	clksel = readl_relaxed(timer_baseaddr + 4 + TTC_CLK_CNTRL_OFFSET);
5139d8d47eaSDaniel Lezcano 	clksel = !!(clksel & TTC_CLK_CNTRL_CSRC_MASK);
5149d8d47eaSDaniel Lezcano 	clk_ce = of_clk_get(timer, clksel);
5159d8d47eaSDaniel Lezcano 	if (IS_ERR(clk_ce)) {
5169d8d47eaSDaniel Lezcano 		pr_err("ERROR: timer input clock not found\n");
5178b5bf64cSFeng Mingxi 		ret = PTR_ERR(clk_ce);
5188b5bf64cSFeng Mingxi 		goto put_clk_cs;
5199d8d47eaSDaniel Lezcano 	}
5209d8d47eaSDaniel Lezcano 
5219d8d47eaSDaniel Lezcano 	ret = ttc_setup_clocksource(clk_cs, timer_baseaddr, timer_width);
5229d8d47eaSDaniel Lezcano 	if (ret)
5238b5bf64cSFeng Mingxi 		goto put_clk_ce;
5249d8d47eaSDaniel Lezcano 
5259d8d47eaSDaniel Lezcano 	ret = ttc_setup_clockevent(clk_ce, timer_baseaddr + 4, irq);
5269d8d47eaSDaniel Lezcano 	if (ret)
5278b5bf64cSFeng Mingxi 		goto put_clk_ce;
5289d8d47eaSDaniel Lezcano 
5299d8d47eaSDaniel Lezcano 	pr_info("%pOFn #0 at %p, irq=%d\n", timer, timer_baseaddr, irq);
5309d8d47eaSDaniel Lezcano 
5319d8d47eaSDaniel Lezcano 	return 0;
5328b5bf64cSFeng Mingxi 
5338b5bf64cSFeng Mingxi put_clk_ce:
5348b5bf64cSFeng Mingxi 	clk_put(clk_ce);
5358b5bf64cSFeng Mingxi put_clk_cs:
5368b5bf64cSFeng Mingxi 	clk_put(clk_cs);
5378b5bf64cSFeng Mingxi 	return ret;
5389d8d47eaSDaniel Lezcano }
5399d8d47eaSDaniel Lezcano 
540f5ac896bSRajan Vaja static const struct of_device_id ttc_timer_of_match[] = {
541f5ac896bSRajan Vaja 	{.compatible = "cdns,ttc"},
542f5ac896bSRajan Vaja 	{},
543f5ac896bSRajan Vaja };
544f5ac896bSRajan Vaja 
545f5ac896bSRajan Vaja MODULE_DEVICE_TABLE(of, ttc_timer_of_match);
546f5ac896bSRajan Vaja 
547f5ac896bSRajan Vaja static struct platform_driver ttc_timer_driver = {
548f5ac896bSRajan Vaja 	.driver = {
549f5ac896bSRajan Vaja 		.name	= "cdns_ttc_timer",
550f5ac896bSRajan Vaja 		.of_match_table = ttc_timer_of_match,
551f5ac896bSRajan Vaja 	},
552f5ac896bSRajan Vaja };
553f5ac896bSRajan Vaja builtin_platform_driver_probe(ttc_timer_driver, ttc_timer_probe);
554