160bff9f8SThomas Gleixner // SPDX-License-Identifier: GPL-2.0 2493d09b7SDaniel Lezcano /* 3493d09b7SDaniel Lezcano * Allwinner A1X SoCs timer handling. 4493d09b7SDaniel Lezcano * 5493d09b7SDaniel Lezcano * Copyright (C) 2012 Maxime Ripard 6493d09b7SDaniel Lezcano * 7493d09b7SDaniel Lezcano * Maxime Ripard <[email protected]> 8493d09b7SDaniel Lezcano * 9493d09b7SDaniel Lezcano * Based on code from 10493d09b7SDaniel Lezcano * Allwinner Technology Co., Ltd. <www.allwinnertech.com> 11493d09b7SDaniel Lezcano * Benn Huang <[email protected]> 12493d09b7SDaniel Lezcano */ 13493d09b7SDaniel Lezcano 14493d09b7SDaniel Lezcano #include <linux/clk.h> 15493d09b7SDaniel Lezcano #include <linux/clockchips.h> 16493d09b7SDaniel Lezcano #include <linux/interrupt.h> 17493d09b7SDaniel Lezcano #include <linux/irq.h> 18493d09b7SDaniel Lezcano #include <linux/irqreturn.h> 19493d09b7SDaniel Lezcano #include <linux/sched_clock.h> 20493d09b7SDaniel Lezcano #include <linux/of.h> 21493d09b7SDaniel Lezcano #include <linux/of_address.h> 22493d09b7SDaniel Lezcano #include <linux/of_irq.h> 23493d09b7SDaniel Lezcano 24493d09b7SDaniel Lezcano #include "timer-of.h" 25493d09b7SDaniel Lezcano 26493d09b7SDaniel Lezcano #define TIMER_IRQ_EN_REG 0x00 27493d09b7SDaniel Lezcano #define TIMER_IRQ_EN(val) BIT(val) 28493d09b7SDaniel Lezcano #define TIMER_IRQ_ST_REG 0x04 29493d09b7SDaniel Lezcano #define TIMER_CTL_REG(val) (0x10 * val + 0x10) 30493d09b7SDaniel Lezcano #define TIMER_CTL_ENABLE BIT(0) 31493d09b7SDaniel Lezcano #define TIMER_CTL_RELOAD BIT(1) 32493d09b7SDaniel Lezcano #define TIMER_CTL_CLK_SRC(val) (((val) & 0x3) << 2) 33493d09b7SDaniel Lezcano #define TIMER_CTL_CLK_SRC_OSC24M (1) 34493d09b7SDaniel Lezcano #define TIMER_CTL_CLK_PRES(val) (((val) & 0x7) << 4) 35493d09b7SDaniel Lezcano #define TIMER_CTL_ONESHOT BIT(7) 36493d09b7SDaniel Lezcano #define TIMER_INTVAL_REG(val) (0x10 * (val) + 0x14) 37493d09b7SDaniel Lezcano #define TIMER_CNTVAL_REG(val) (0x10 * (val) + 0x18) 38493d09b7SDaniel Lezcano 39493d09b7SDaniel Lezcano #define TIMER_SYNC_TICKS 3 40493d09b7SDaniel Lezcano 41493d09b7SDaniel Lezcano /* 42493d09b7SDaniel Lezcano * When we disable a timer, we need to wait at least for 2 cycles of 43493d09b7SDaniel Lezcano * the timer source clock. We will use for that the clocksource timer 44493d09b7SDaniel Lezcano * that is already setup and runs at the same frequency than the other 45493d09b7SDaniel Lezcano * timers, and we never will be disabled. 46493d09b7SDaniel Lezcano */ 47493d09b7SDaniel Lezcano static void sun4i_clkevt_sync(void __iomem *base) 48493d09b7SDaniel Lezcano { 49493d09b7SDaniel Lezcano u32 old = readl(base + TIMER_CNTVAL_REG(1)); 50493d09b7SDaniel Lezcano 51493d09b7SDaniel Lezcano while ((old - readl(base + TIMER_CNTVAL_REG(1))) < TIMER_SYNC_TICKS) 52493d09b7SDaniel Lezcano cpu_relax(); 53493d09b7SDaniel Lezcano } 54493d09b7SDaniel Lezcano 55493d09b7SDaniel Lezcano static void sun4i_clkevt_time_stop(void __iomem *base, u8 timer) 56493d09b7SDaniel Lezcano { 57493d09b7SDaniel Lezcano u32 val = readl(base + TIMER_CTL_REG(timer)); 58493d09b7SDaniel Lezcano writel(val & ~TIMER_CTL_ENABLE, base + TIMER_CTL_REG(timer)); 59493d09b7SDaniel Lezcano sun4i_clkevt_sync(base); 60493d09b7SDaniel Lezcano } 61493d09b7SDaniel Lezcano 62493d09b7SDaniel Lezcano static void sun4i_clkevt_time_setup(void __iomem *base, u8 timer, 63493d09b7SDaniel Lezcano unsigned long delay) 64493d09b7SDaniel Lezcano { 65493d09b7SDaniel Lezcano writel(delay, base + TIMER_INTVAL_REG(timer)); 66493d09b7SDaniel Lezcano } 67493d09b7SDaniel Lezcano 68493d09b7SDaniel Lezcano static void sun4i_clkevt_time_start(void __iomem *base, u8 timer, 69493d09b7SDaniel Lezcano bool periodic) 70493d09b7SDaniel Lezcano { 71493d09b7SDaniel Lezcano u32 val = readl(base + TIMER_CTL_REG(timer)); 72493d09b7SDaniel Lezcano 73493d09b7SDaniel Lezcano if (periodic) 74493d09b7SDaniel Lezcano val &= ~TIMER_CTL_ONESHOT; 75493d09b7SDaniel Lezcano else 76493d09b7SDaniel Lezcano val |= TIMER_CTL_ONESHOT; 77493d09b7SDaniel Lezcano 78493d09b7SDaniel Lezcano writel(val | TIMER_CTL_ENABLE | TIMER_CTL_RELOAD, 79493d09b7SDaniel Lezcano base + TIMER_CTL_REG(timer)); 80493d09b7SDaniel Lezcano } 81493d09b7SDaniel Lezcano 82493d09b7SDaniel Lezcano static int sun4i_clkevt_shutdown(struct clock_event_device *evt) 83493d09b7SDaniel Lezcano { 84493d09b7SDaniel Lezcano struct timer_of *to = to_timer_of(evt); 85493d09b7SDaniel Lezcano 86493d09b7SDaniel Lezcano sun4i_clkevt_time_stop(timer_of_base(to), 0); 87493d09b7SDaniel Lezcano 88493d09b7SDaniel Lezcano return 0; 89493d09b7SDaniel Lezcano } 90493d09b7SDaniel Lezcano 91493d09b7SDaniel Lezcano static int sun4i_clkevt_set_oneshot(struct clock_event_device *evt) 92493d09b7SDaniel Lezcano { 93493d09b7SDaniel Lezcano struct timer_of *to = to_timer_of(evt); 94493d09b7SDaniel Lezcano 95493d09b7SDaniel Lezcano sun4i_clkevt_time_stop(timer_of_base(to), 0); 96493d09b7SDaniel Lezcano sun4i_clkevt_time_start(timer_of_base(to), 0, false); 97493d09b7SDaniel Lezcano 98493d09b7SDaniel Lezcano return 0; 99493d09b7SDaniel Lezcano } 100493d09b7SDaniel Lezcano 101493d09b7SDaniel Lezcano static int sun4i_clkevt_set_periodic(struct clock_event_device *evt) 102493d09b7SDaniel Lezcano { 103493d09b7SDaniel Lezcano struct timer_of *to = to_timer_of(evt); 104493d09b7SDaniel Lezcano 105493d09b7SDaniel Lezcano sun4i_clkevt_time_stop(timer_of_base(to), 0); 106493d09b7SDaniel Lezcano sun4i_clkevt_time_setup(timer_of_base(to), 0, timer_of_period(to)); 107493d09b7SDaniel Lezcano sun4i_clkevt_time_start(timer_of_base(to), 0, true); 108493d09b7SDaniel Lezcano 109493d09b7SDaniel Lezcano return 0; 110493d09b7SDaniel Lezcano } 111493d09b7SDaniel Lezcano 112493d09b7SDaniel Lezcano static int sun4i_clkevt_next_event(unsigned long evt, 113493d09b7SDaniel Lezcano struct clock_event_device *clkevt) 114493d09b7SDaniel Lezcano { 115493d09b7SDaniel Lezcano struct timer_of *to = to_timer_of(clkevt); 116493d09b7SDaniel Lezcano 117493d09b7SDaniel Lezcano sun4i_clkevt_time_stop(timer_of_base(to), 0); 118493d09b7SDaniel Lezcano sun4i_clkevt_time_setup(timer_of_base(to), 0, evt - TIMER_SYNC_TICKS); 119493d09b7SDaniel Lezcano sun4i_clkevt_time_start(timer_of_base(to), 0, false); 120493d09b7SDaniel Lezcano 121493d09b7SDaniel Lezcano return 0; 122493d09b7SDaniel Lezcano } 123493d09b7SDaniel Lezcano 124493d09b7SDaniel Lezcano static void sun4i_timer_clear_interrupt(void __iomem *base) 125493d09b7SDaniel Lezcano { 126493d09b7SDaniel Lezcano writel(TIMER_IRQ_EN(0), base + TIMER_IRQ_ST_REG); 127493d09b7SDaniel Lezcano } 128493d09b7SDaniel Lezcano 129493d09b7SDaniel Lezcano static irqreturn_t sun4i_timer_interrupt(int irq, void *dev_id) 130493d09b7SDaniel Lezcano { 131*7a93d490SXU pengfei struct clock_event_device *evt = dev_id; 132493d09b7SDaniel Lezcano struct timer_of *to = to_timer_of(evt); 133493d09b7SDaniel Lezcano 134493d09b7SDaniel Lezcano sun4i_timer_clear_interrupt(timer_of_base(to)); 135493d09b7SDaniel Lezcano evt->event_handler(evt); 136493d09b7SDaniel Lezcano 137493d09b7SDaniel Lezcano return IRQ_HANDLED; 138493d09b7SDaniel Lezcano } 139493d09b7SDaniel Lezcano 140493d09b7SDaniel Lezcano static struct timer_of to = { 141493d09b7SDaniel Lezcano .flags = TIMER_OF_IRQ | TIMER_OF_CLOCK | TIMER_OF_BASE, 142493d09b7SDaniel Lezcano 143493d09b7SDaniel Lezcano .clkevt = { 144493d09b7SDaniel Lezcano .name = "sun4i_tick", 145493d09b7SDaniel Lezcano .rating = 350, 146493d09b7SDaniel Lezcano .features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT, 147493d09b7SDaniel Lezcano .set_state_shutdown = sun4i_clkevt_shutdown, 148493d09b7SDaniel Lezcano .set_state_periodic = sun4i_clkevt_set_periodic, 149493d09b7SDaniel Lezcano .set_state_oneshot = sun4i_clkevt_set_oneshot, 150493d09b7SDaniel Lezcano .tick_resume = sun4i_clkevt_shutdown, 151493d09b7SDaniel Lezcano .set_next_event = sun4i_clkevt_next_event, 152493d09b7SDaniel Lezcano .cpumask = cpu_possible_mask, 153493d09b7SDaniel Lezcano }, 154493d09b7SDaniel Lezcano 155493d09b7SDaniel Lezcano .of_irq = { 156493d09b7SDaniel Lezcano .handler = sun4i_timer_interrupt, 157493d09b7SDaniel Lezcano .flags = IRQF_TIMER | IRQF_IRQPOLL, 158493d09b7SDaniel Lezcano }, 159493d09b7SDaniel Lezcano }; 160493d09b7SDaniel Lezcano 161493d09b7SDaniel Lezcano static u64 notrace sun4i_timer_sched_read(void) 162493d09b7SDaniel Lezcano { 163493d09b7SDaniel Lezcano return ~readl(timer_of_base(&to) + TIMER_CNTVAL_REG(1)); 164493d09b7SDaniel Lezcano } 165493d09b7SDaniel Lezcano 166493d09b7SDaniel Lezcano static int __init sun4i_timer_init(struct device_node *node) 167493d09b7SDaniel Lezcano { 168493d09b7SDaniel Lezcano int ret; 169493d09b7SDaniel Lezcano u32 val; 170493d09b7SDaniel Lezcano 171493d09b7SDaniel Lezcano ret = timer_of_init(node, &to); 172493d09b7SDaniel Lezcano if (ret) 173493d09b7SDaniel Lezcano return ret; 174493d09b7SDaniel Lezcano 175493d09b7SDaniel Lezcano writel(~0, timer_of_base(&to) + TIMER_INTVAL_REG(1)); 176493d09b7SDaniel Lezcano writel(TIMER_CTL_ENABLE | TIMER_CTL_RELOAD | 177493d09b7SDaniel Lezcano TIMER_CTL_CLK_SRC(TIMER_CTL_CLK_SRC_OSC24M), 178493d09b7SDaniel Lezcano timer_of_base(&to) + TIMER_CTL_REG(1)); 179493d09b7SDaniel Lezcano 180493d09b7SDaniel Lezcano /* 181493d09b7SDaniel Lezcano * sched_clock_register does not have priorities, and on sun6i and 182493d09b7SDaniel Lezcano * later there is a better sched_clock registered by arm_arch_timer.c 183493d09b7SDaniel Lezcano */ 184493d09b7SDaniel Lezcano if (of_machine_is_compatible("allwinner,sun4i-a10") || 185493d09b7SDaniel Lezcano of_machine_is_compatible("allwinner,sun5i-a13") || 1860113ab80SMesih Kilinc of_machine_is_compatible("allwinner,sun5i-a10s") || 1870113ab80SMesih Kilinc of_machine_is_compatible("allwinner,suniv-f1c100s")) 188493d09b7SDaniel Lezcano sched_clock_register(sun4i_timer_sched_read, 32, 189493d09b7SDaniel Lezcano timer_of_rate(&to)); 190493d09b7SDaniel Lezcano 191493d09b7SDaniel Lezcano ret = clocksource_mmio_init(timer_of_base(&to) + TIMER_CNTVAL_REG(1), 192493d09b7SDaniel Lezcano node->name, timer_of_rate(&to), 350, 32, 193493d09b7SDaniel Lezcano clocksource_mmio_readl_down); 194493d09b7SDaniel Lezcano if (ret) { 195493d09b7SDaniel Lezcano pr_err("Failed to register clocksource\n"); 196493d09b7SDaniel Lezcano return ret; 197493d09b7SDaniel Lezcano } 198493d09b7SDaniel Lezcano 199493d09b7SDaniel Lezcano writel(TIMER_CTL_CLK_SRC(TIMER_CTL_CLK_SRC_OSC24M), 200493d09b7SDaniel Lezcano timer_of_base(&to) + TIMER_CTL_REG(0)); 201493d09b7SDaniel Lezcano 202493d09b7SDaniel Lezcano /* Make sure timer is stopped before playing with interrupts */ 203493d09b7SDaniel Lezcano sun4i_clkevt_time_stop(timer_of_base(&to), 0); 204493d09b7SDaniel Lezcano 205493d09b7SDaniel Lezcano /* clear timer0 interrupt */ 206493d09b7SDaniel Lezcano sun4i_timer_clear_interrupt(timer_of_base(&to)); 207493d09b7SDaniel Lezcano 208493d09b7SDaniel Lezcano clockevents_config_and_register(&to.clkevt, timer_of_rate(&to), 209493d09b7SDaniel Lezcano TIMER_SYNC_TICKS, 0xffffffff); 210493d09b7SDaniel Lezcano 211493d09b7SDaniel Lezcano /* Enable timer0 interrupt */ 212493d09b7SDaniel Lezcano val = readl(timer_of_base(&to) + TIMER_IRQ_EN_REG); 213493d09b7SDaniel Lezcano writel(val | TIMER_IRQ_EN(0), timer_of_base(&to) + TIMER_IRQ_EN_REG); 214493d09b7SDaniel Lezcano 215493d09b7SDaniel Lezcano return ret; 216493d09b7SDaniel Lezcano } 217493d09b7SDaniel Lezcano TIMER_OF_DECLARE(sun4i, "allwinner,sun4i-a10-timer", 218493d09b7SDaniel Lezcano sun4i_timer_init); 219bca4e084SMaxime Ripard TIMER_OF_DECLARE(sun8i_a23, "allwinner,sun8i-a23-timer", 220bca4e084SMaxime Ripard sun4i_timer_init); 221bca4e084SMaxime Ripard TIMER_OF_DECLARE(sun8i_v3s, "allwinner,sun8i-v3s-timer", 222bca4e084SMaxime Ripard sun4i_timer_init); 2230113ab80SMesih Kilinc TIMER_OF_DECLARE(suniv, "allwinner,suniv-f1c100s-timer", 2240113ab80SMesih Kilinc sun4i_timer_init); 225