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