1*493d09b7SDaniel Lezcano /* 2*493d09b7SDaniel Lezcano * Allwinner A1X SoCs timer handling. 3*493d09b7SDaniel Lezcano * 4*493d09b7SDaniel Lezcano * Copyright (C) 2012 Maxime Ripard 5*493d09b7SDaniel Lezcano * 6*493d09b7SDaniel Lezcano * Maxime Ripard <[email protected]> 7*493d09b7SDaniel Lezcano * 8*493d09b7SDaniel Lezcano * Based on code from 9*493d09b7SDaniel Lezcano * Allwinner Technology Co., Ltd. <www.allwinnertech.com> 10*493d09b7SDaniel Lezcano * Benn Huang <[email protected]> 11*493d09b7SDaniel Lezcano * 12*493d09b7SDaniel Lezcano * This file is licensed under the terms of the GNU General Public 13*493d09b7SDaniel Lezcano * License version 2. This program is licensed "as is" without any 14*493d09b7SDaniel Lezcano * warranty of any kind, whether express or implied. 15*493d09b7SDaniel Lezcano */ 16*493d09b7SDaniel Lezcano 17*493d09b7SDaniel Lezcano #include <linux/clk.h> 18*493d09b7SDaniel Lezcano #include <linux/clockchips.h> 19*493d09b7SDaniel Lezcano #include <linux/interrupt.h> 20*493d09b7SDaniel Lezcano #include <linux/irq.h> 21*493d09b7SDaniel Lezcano #include <linux/irqreturn.h> 22*493d09b7SDaniel Lezcano #include <linux/sched_clock.h> 23*493d09b7SDaniel Lezcano #include <linux/of.h> 24*493d09b7SDaniel Lezcano #include <linux/of_address.h> 25*493d09b7SDaniel Lezcano #include <linux/of_irq.h> 26*493d09b7SDaniel Lezcano 27*493d09b7SDaniel Lezcano #include "timer-of.h" 28*493d09b7SDaniel Lezcano 29*493d09b7SDaniel Lezcano #define TIMER_IRQ_EN_REG 0x00 30*493d09b7SDaniel Lezcano #define TIMER_IRQ_EN(val) BIT(val) 31*493d09b7SDaniel Lezcano #define TIMER_IRQ_ST_REG 0x04 32*493d09b7SDaniel Lezcano #define TIMER_CTL_REG(val) (0x10 * val + 0x10) 33*493d09b7SDaniel Lezcano #define TIMER_CTL_ENABLE BIT(0) 34*493d09b7SDaniel Lezcano #define TIMER_CTL_RELOAD BIT(1) 35*493d09b7SDaniel Lezcano #define TIMER_CTL_CLK_SRC(val) (((val) & 0x3) << 2) 36*493d09b7SDaniel Lezcano #define TIMER_CTL_CLK_SRC_OSC24M (1) 37*493d09b7SDaniel Lezcano #define TIMER_CTL_CLK_PRES(val) (((val) & 0x7) << 4) 38*493d09b7SDaniel Lezcano #define TIMER_CTL_ONESHOT BIT(7) 39*493d09b7SDaniel Lezcano #define TIMER_INTVAL_REG(val) (0x10 * (val) + 0x14) 40*493d09b7SDaniel Lezcano #define TIMER_CNTVAL_REG(val) (0x10 * (val) + 0x18) 41*493d09b7SDaniel Lezcano 42*493d09b7SDaniel Lezcano #define TIMER_SYNC_TICKS 3 43*493d09b7SDaniel Lezcano 44*493d09b7SDaniel Lezcano /* 45*493d09b7SDaniel Lezcano * When we disable a timer, we need to wait at least for 2 cycles of 46*493d09b7SDaniel Lezcano * the timer source clock. We will use for that the clocksource timer 47*493d09b7SDaniel Lezcano * that is already setup and runs at the same frequency than the other 48*493d09b7SDaniel Lezcano * timers, and we never will be disabled. 49*493d09b7SDaniel Lezcano */ 50*493d09b7SDaniel Lezcano static void sun4i_clkevt_sync(void __iomem *base) 51*493d09b7SDaniel Lezcano { 52*493d09b7SDaniel Lezcano u32 old = readl(base + TIMER_CNTVAL_REG(1)); 53*493d09b7SDaniel Lezcano 54*493d09b7SDaniel Lezcano while ((old - readl(base + TIMER_CNTVAL_REG(1))) < TIMER_SYNC_TICKS) 55*493d09b7SDaniel Lezcano cpu_relax(); 56*493d09b7SDaniel Lezcano } 57*493d09b7SDaniel Lezcano 58*493d09b7SDaniel Lezcano static void sun4i_clkevt_time_stop(void __iomem *base, u8 timer) 59*493d09b7SDaniel Lezcano { 60*493d09b7SDaniel Lezcano u32 val = readl(base + TIMER_CTL_REG(timer)); 61*493d09b7SDaniel Lezcano writel(val & ~TIMER_CTL_ENABLE, base + TIMER_CTL_REG(timer)); 62*493d09b7SDaniel Lezcano sun4i_clkevt_sync(base); 63*493d09b7SDaniel Lezcano } 64*493d09b7SDaniel Lezcano 65*493d09b7SDaniel Lezcano static void sun4i_clkevt_time_setup(void __iomem *base, u8 timer, 66*493d09b7SDaniel Lezcano unsigned long delay) 67*493d09b7SDaniel Lezcano { 68*493d09b7SDaniel Lezcano writel(delay, base + TIMER_INTVAL_REG(timer)); 69*493d09b7SDaniel Lezcano } 70*493d09b7SDaniel Lezcano 71*493d09b7SDaniel Lezcano static void sun4i_clkevt_time_start(void __iomem *base, u8 timer, 72*493d09b7SDaniel Lezcano bool periodic) 73*493d09b7SDaniel Lezcano { 74*493d09b7SDaniel Lezcano u32 val = readl(base + TIMER_CTL_REG(timer)); 75*493d09b7SDaniel Lezcano 76*493d09b7SDaniel Lezcano if (periodic) 77*493d09b7SDaniel Lezcano val &= ~TIMER_CTL_ONESHOT; 78*493d09b7SDaniel Lezcano else 79*493d09b7SDaniel Lezcano val |= TIMER_CTL_ONESHOT; 80*493d09b7SDaniel Lezcano 81*493d09b7SDaniel Lezcano writel(val | TIMER_CTL_ENABLE | TIMER_CTL_RELOAD, 82*493d09b7SDaniel Lezcano base + TIMER_CTL_REG(timer)); 83*493d09b7SDaniel Lezcano } 84*493d09b7SDaniel Lezcano 85*493d09b7SDaniel Lezcano static int sun4i_clkevt_shutdown(struct clock_event_device *evt) 86*493d09b7SDaniel Lezcano { 87*493d09b7SDaniel Lezcano struct timer_of *to = to_timer_of(evt); 88*493d09b7SDaniel Lezcano 89*493d09b7SDaniel Lezcano sun4i_clkevt_time_stop(timer_of_base(to), 0); 90*493d09b7SDaniel Lezcano 91*493d09b7SDaniel Lezcano return 0; 92*493d09b7SDaniel Lezcano } 93*493d09b7SDaniel Lezcano 94*493d09b7SDaniel Lezcano static int sun4i_clkevt_set_oneshot(struct clock_event_device *evt) 95*493d09b7SDaniel Lezcano { 96*493d09b7SDaniel Lezcano struct timer_of *to = to_timer_of(evt); 97*493d09b7SDaniel Lezcano 98*493d09b7SDaniel Lezcano sun4i_clkevt_time_stop(timer_of_base(to), 0); 99*493d09b7SDaniel Lezcano sun4i_clkevt_time_start(timer_of_base(to), 0, false); 100*493d09b7SDaniel Lezcano 101*493d09b7SDaniel Lezcano return 0; 102*493d09b7SDaniel Lezcano } 103*493d09b7SDaniel Lezcano 104*493d09b7SDaniel Lezcano static int sun4i_clkevt_set_periodic(struct clock_event_device *evt) 105*493d09b7SDaniel Lezcano { 106*493d09b7SDaniel Lezcano struct timer_of *to = to_timer_of(evt); 107*493d09b7SDaniel Lezcano 108*493d09b7SDaniel Lezcano sun4i_clkevt_time_stop(timer_of_base(to), 0); 109*493d09b7SDaniel Lezcano sun4i_clkevt_time_setup(timer_of_base(to), 0, timer_of_period(to)); 110*493d09b7SDaniel Lezcano sun4i_clkevt_time_start(timer_of_base(to), 0, true); 111*493d09b7SDaniel Lezcano 112*493d09b7SDaniel Lezcano return 0; 113*493d09b7SDaniel Lezcano } 114*493d09b7SDaniel Lezcano 115*493d09b7SDaniel Lezcano static int sun4i_clkevt_next_event(unsigned long evt, 116*493d09b7SDaniel Lezcano struct clock_event_device *clkevt) 117*493d09b7SDaniel Lezcano { 118*493d09b7SDaniel Lezcano struct timer_of *to = to_timer_of(clkevt); 119*493d09b7SDaniel Lezcano 120*493d09b7SDaniel Lezcano sun4i_clkevt_time_stop(timer_of_base(to), 0); 121*493d09b7SDaniel Lezcano sun4i_clkevt_time_setup(timer_of_base(to), 0, evt - TIMER_SYNC_TICKS); 122*493d09b7SDaniel Lezcano sun4i_clkevt_time_start(timer_of_base(to), 0, false); 123*493d09b7SDaniel Lezcano 124*493d09b7SDaniel Lezcano return 0; 125*493d09b7SDaniel Lezcano } 126*493d09b7SDaniel Lezcano 127*493d09b7SDaniel Lezcano static void sun4i_timer_clear_interrupt(void __iomem *base) 128*493d09b7SDaniel Lezcano { 129*493d09b7SDaniel Lezcano writel(TIMER_IRQ_EN(0), base + TIMER_IRQ_ST_REG); 130*493d09b7SDaniel Lezcano } 131*493d09b7SDaniel Lezcano 132*493d09b7SDaniel Lezcano static irqreturn_t sun4i_timer_interrupt(int irq, void *dev_id) 133*493d09b7SDaniel Lezcano { 134*493d09b7SDaniel Lezcano struct clock_event_device *evt = (struct clock_event_device *)dev_id; 135*493d09b7SDaniel Lezcano struct timer_of *to = to_timer_of(evt); 136*493d09b7SDaniel Lezcano 137*493d09b7SDaniel Lezcano sun4i_timer_clear_interrupt(timer_of_base(to)); 138*493d09b7SDaniel Lezcano evt->event_handler(evt); 139*493d09b7SDaniel Lezcano 140*493d09b7SDaniel Lezcano return IRQ_HANDLED; 141*493d09b7SDaniel Lezcano } 142*493d09b7SDaniel Lezcano 143*493d09b7SDaniel Lezcano static struct timer_of to = { 144*493d09b7SDaniel Lezcano .flags = TIMER_OF_IRQ | TIMER_OF_CLOCK | TIMER_OF_BASE, 145*493d09b7SDaniel Lezcano 146*493d09b7SDaniel Lezcano .clkevt = { 147*493d09b7SDaniel Lezcano .name = "sun4i_tick", 148*493d09b7SDaniel Lezcano .rating = 350, 149*493d09b7SDaniel Lezcano .features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT, 150*493d09b7SDaniel Lezcano .set_state_shutdown = sun4i_clkevt_shutdown, 151*493d09b7SDaniel Lezcano .set_state_periodic = sun4i_clkevt_set_periodic, 152*493d09b7SDaniel Lezcano .set_state_oneshot = sun4i_clkevt_set_oneshot, 153*493d09b7SDaniel Lezcano .tick_resume = sun4i_clkevt_shutdown, 154*493d09b7SDaniel Lezcano .set_next_event = sun4i_clkevt_next_event, 155*493d09b7SDaniel Lezcano .cpumask = cpu_possible_mask, 156*493d09b7SDaniel Lezcano }, 157*493d09b7SDaniel Lezcano 158*493d09b7SDaniel Lezcano .of_irq = { 159*493d09b7SDaniel Lezcano .handler = sun4i_timer_interrupt, 160*493d09b7SDaniel Lezcano .flags = IRQF_TIMER | IRQF_IRQPOLL, 161*493d09b7SDaniel Lezcano }, 162*493d09b7SDaniel Lezcano }; 163*493d09b7SDaniel Lezcano 164*493d09b7SDaniel Lezcano static u64 notrace sun4i_timer_sched_read(void) 165*493d09b7SDaniel Lezcano { 166*493d09b7SDaniel Lezcano return ~readl(timer_of_base(&to) + TIMER_CNTVAL_REG(1)); 167*493d09b7SDaniel Lezcano } 168*493d09b7SDaniel Lezcano 169*493d09b7SDaniel Lezcano static int __init sun4i_timer_init(struct device_node *node) 170*493d09b7SDaniel Lezcano { 171*493d09b7SDaniel Lezcano int ret; 172*493d09b7SDaniel Lezcano u32 val; 173*493d09b7SDaniel Lezcano 174*493d09b7SDaniel Lezcano ret = timer_of_init(node, &to); 175*493d09b7SDaniel Lezcano if (ret) 176*493d09b7SDaniel Lezcano return ret; 177*493d09b7SDaniel Lezcano 178*493d09b7SDaniel Lezcano writel(~0, timer_of_base(&to) + TIMER_INTVAL_REG(1)); 179*493d09b7SDaniel Lezcano writel(TIMER_CTL_ENABLE | TIMER_CTL_RELOAD | 180*493d09b7SDaniel Lezcano TIMER_CTL_CLK_SRC(TIMER_CTL_CLK_SRC_OSC24M), 181*493d09b7SDaniel Lezcano timer_of_base(&to) + TIMER_CTL_REG(1)); 182*493d09b7SDaniel Lezcano 183*493d09b7SDaniel Lezcano /* 184*493d09b7SDaniel Lezcano * sched_clock_register does not have priorities, and on sun6i and 185*493d09b7SDaniel Lezcano * later there is a better sched_clock registered by arm_arch_timer.c 186*493d09b7SDaniel Lezcano */ 187*493d09b7SDaniel Lezcano if (of_machine_is_compatible("allwinner,sun4i-a10") || 188*493d09b7SDaniel Lezcano of_machine_is_compatible("allwinner,sun5i-a13") || 189*493d09b7SDaniel Lezcano of_machine_is_compatible("allwinner,sun5i-a10s")) 190*493d09b7SDaniel Lezcano sched_clock_register(sun4i_timer_sched_read, 32, 191*493d09b7SDaniel Lezcano timer_of_rate(&to)); 192*493d09b7SDaniel Lezcano 193*493d09b7SDaniel Lezcano ret = clocksource_mmio_init(timer_of_base(&to) + TIMER_CNTVAL_REG(1), 194*493d09b7SDaniel Lezcano node->name, timer_of_rate(&to), 350, 32, 195*493d09b7SDaniel Lezcano clocksource_mmio_readl_down); 196*493d09b7SDaniel Lezcano if (ret) { 197*493d09b7SDaniel Lezcano pr_err("Failed to register clocksource\n"); 198*493d09b7SDaniel Lezcano return ret; 199*493d09b7SDaniel Lezcano } 200*493d09b7SDaniel Lezcano 201*493d09b7SDaniel Lezcano writel(TIMER_CTL_CLK_SRC(TIMER_CTL_CLK_SRC_OSC24M), 202*493d09b7SDaniel Lezcano timer_of_base(&to) + TIMER_CTL_REG(0)); 203*493d09b7SDaniel Lezcano 204*493d09b7SDaniel Lezcano /* Make sure timer is stopped before playing with interrupts */ 205*493d09b7SDaniel Lezcano sun4i_clkevt_time_stop(timer_of_base(&to), 0); 206*493d09b7SDaniel Lezcano 207*493d09b7SDaniel Lezcano /* clear timer0 interrupt */ 208*493d09b7SDaniel Lezcano sun4i_timer_clear_interrupt(timer_of_base(&to)); 209*493d09b7SDaniel Lezcano 210*493d09b7SDaniel Lezcano clockevents_config_and_register(&to.clkevt, timer_of_rate(&to), 211*493d09b7SDaniel Lezcano TIMER_SYNC_TICKS, 0xffffffff); 212*493d09b7SDaniel Lezcano 213*493d09b7SDaniel Lezcano /* Enable timer0 interrupt */ 214*493d09b7SDaniel Lezcano val = readl(timer_of_base(&to) + TIMER_IRQ_EN_REG); 215*493d09b7SDaniel Lezcano writel(val | TIMER_IRQ_EN(0), timer_of_base(&to) + TIMER_IRQ_EN_REG); 216*493d09b7SDaniel Lezcano 217*493d09b7SDaniel Lezcano return ret; 218*493d09b7SDaniel Lezcano } 219*493d09b7SDaniel Lezcano TIMER_OF_DECLARE(sun4i, "allwinner,sun4i-a10-timer", 220*493d09b7SDaniel Lezcano sun4i_timer_init); 221