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