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