1*9d8d47eaSDaniel Lezcano /* 2*9d8d47eaSDaniel Lezcano * Marvell Orion SoC timer handling. 3*9d8d47eaSDaniel Lezcano * 4*9d8d47eaSDaniel Lezcano * Sebastian Hesselbarth <sebastian.hesselbarth@gmail.com> 5*9d8d47eaSDaniel Lezcano * 6*9d8d47eaSDaniel Lezcano * This file is licensed under the terms of the GNU General Public 7*9d8d47eaSDaniel Lezcano * License version 2. This program is licensed "as is" without any 8*9d8d47eaSDaniel Lezcano * warranty of any kind, whether express or implied. 9*9d8d47eaSDaniel Lezcano * 10*9d8d47eaSDaniel Lezcano * Timer 0 is used as free-running clocksource, while timer 1 is 11*9d8d47eaSDaniel Lezcano * used as clock_event_device. 12*9d8d47eaSDaniel Lezcano */ 13*9d8d47eaSDaniel Lezcano 14*9d8d47eaSDaniel Lezcano #include <linux/kernel.h> 15*9d8d47eaSDaniel Lezcano #include <linux/bitops.h> 16*9d8d47eaSDaniel Lezcano #include <linux/clk.h> 17*9d8d47eaSDaniel Lezcano #include <linux/clockchips.h> 18*9d8d47eaSDaniel Lezcano #include <linux/delay.h> 19*9d8d47eaSDaniel Lezcano #include <linux/interrupt.h> 20*9d8d47eaSDaniel Lezcano #include <linux/of_address.h> 21*9d8d47eaSDaniel Lezcano #include <linux/of_irq.h> 22*9d8d47eaSDaniel Lezcano #include <linux/spinlock.h> 23*9d8d47eaSDaniel Lezcano #include <linux/sched_clock.h> 24*9d8d47eaSDaniel Lezcano 25*9d8d47eaSDaniel Lezcano #define TIMER_CTRL 0x00 26*9d8d47eaSDaniel Lezcano #define TIMER0_EN BIT(0) 27*9d8d47eaSDaniel Lezcano #define TIMER0_RELOAD_EN BIT(1) 28*9d8d47eaSDaniel Lezcano #define TIMER1_EN BIT(2) 29*9d8d47eaSDaniel Lezcano #define TIMER1_RELOAD_EN BIT(3) 30*9d8d47eaSDaniel Lezcano #define TIMER0_RELOAD 0x10 31*9d8d47eaSDaniel Lezcano #define TIMER0_VAL 0x14 32*9d8d47eaSDaniel Lezcano #define TIMER1_RELOAD 0x18 33*9d8d47eaSDaniel Lezcano #define TIMER1_VAL 0x1c 34*9d8d47eaSDaniel Lezcano 35*9d8d47eaSDaniel Lezcano #define ORION_ONESHOT_MIN 1 36*9d8d47eaSDaniel Lezcano #define ORION_ONESHOT_MAX 0xfffffffe 37*9d8d47eaSDaniel Lezcano 38*9d8d47eaSDaniel Lezcano static void __iomem *timer_base; 39*9d8d47eaSDaniel Lezcano 40*9d8d47eaSDaniel Lezcano static unsigned long notrace orion_read_timer(void) 41*9d8d47eaSDaniel Lezcano { 42*9d8d47eaSDaniel Lezcano return ~readl(timer_base + TIMER0_VAL); 43*9d8d47eaSDaniel Lezcano } 44*9d8d47eaSDaniel Lezcano 45*9d8d47eaSDaniel Lezcano static struct delay_timer orion_delay_timer = { 46*9d8d47eaSDaniel Lezcano .read_current_timer = orion_read_timer, 47*9d8d47eaSDaniel Lezcano }; 48*9d8d47eaSDaniel Lezcano 49*9d8d47eaSDaniel Lezcano static void orion_delay_timer_init(unsigned long rate) 50*9d8d47eaSDaniel Lezcano { 51*9d8d47eaSDaniel Lezcano orion_delay_timer.freq = rate; 52*9d8d47eaSDaniel Lezcano register_current_timer_delay(&orion_delay_timer); 53*9d8d47eaSDaniel Lezcano } 54*9d8d47eaSDaniel Lezcano 55*9d8d47eaSDaniel Lezcano /* 56*9d8d47eaSDaniel Lezcano * Free-running clocksource handling. 57*9d8d47eaSDaniel Lezcano */ 58*9d8d47eaSDaniel Lezcano static u64 notrace orion_read_sched_clock(void) 59*9d8d47eaSDaniel Lezcano { 60*9d8d47eaSDaniel Lezcano return ~readl(timer_base + TIMER0_VAL); 61*9d8d47eaSDaniel Lezcano } 62*9d8d47eaSDaniel Lezcano 63*9d8d47eaSDaniel Lezcano /* 64*9d8d47eaSDaniel Lezcano * Clockevent handling. 65*9d8d47eaSDaniel Lezcano */ 66*9d8d47eaSDaniel Lezcano static u32 ticks_per_jiffy; 67*9d8d47eaSDaniel Lezcano 68*9d8d47eaSDaniel Lezcano static int orion_clkevt_next_event(unsigned long delta, 69*9d8d47eaSDaniel Lezcano struct clock_event_device *dev) 70*9d8d47eaSDaniel Lezcano { 71*9d8d47eaSDaniel Lezcano /* setup and enable one-shot timer */ 72*9d8d47eaSDaniel Lezcano writel(delta, timer_base + TIMER1_VAL); 73*9d8d47eaSDaniel Lezcano atomic_io_modify(timer_base + TIMER_CTRL, 74*9d8d47eaSDaniel Lezcano TIMER1_RELOAD_EN | TIMER1_EN, TIMER1_EN); 75*9d8d47eaSDaniel Lezcano 76*9d8d47eaSDaniel Lezcano return 0; 77*9d8d47eaSDaniel Lezcano } 78*9d8d47eaSDaniel Lezcano 79*9d8d47eaSDaniel Lezcano static int orion_clkevt_shutdown(struct clock_event_device *dev) 80*9d8d47eaSDaniel Lezcano { 81*9d8d47eaSDaniel Lezcano /* disable timer */ 82*9d8d47eaSDaniel Lezcano atomic_io_modify(timer_base + TIMER_CTRL, 83*9d8d47eaSDaniel Lezcano TIMER1_RELOAD_EN | TIMER1_EN, 0); 84*9d8d47eaSDaniel Lezcano return 0; 85*9d8d47eaSDaniel Lezcano } 86*9d8d47eaSDaniel Lezcano 87*9d8d47eaSDaniel Lezcano static int orion_clkevt_set_periodic(struct clock_event_device *dev) 88*9d8d47eaSDaniel Lezcano { 89*9d8d47eaSDaniel Lezcano /* setup and enable periodic timer at 1/HZ intervals */ 90*9d8d47eaSDaniel Lezcano writel(ticks_per_jiffy - 1, timer_base + TIMER1_RELOAD); 91*9d8d47eaSDaniel Lezcano writel(ticks_per_jiffy - 1, timer_base + TIMER1_VAL); 92*9d8d47eaSDaniel Lezcano atomic_io_modify(timer_base + TIMER_CTRL, 93*9d8d47eaSDaniel Lezcano TIMER1_RELOAD_EN | TIMER1_EN, 94*9d8d47eaSDaniel Lezcano TIMER1_RELOAD_EN | TIMER1_EN); 95*9d8d47eaSDaniel Lezcano return 0; 96*9d8d47eaSDaniel Lezcano } 97*9d8d47eaSDaniel Lezcano 98*9d8d47eaSDaniel Lezcano static struct clock_event_device orion_clkevt = { 99*9d8d47eaSDaniel Lezcano .name = "orion_event", 100*9d8d47eaSDaniel Lezcano .features = CLOCK_EVT_FEAT_ONESHOT | 101*9d8d47eaSDaniel Lezcano CLOCK_EVT_FEAT_PERIODIC, 102*9d8d47eaSDaniel Lezcano .shift = 32, 103*9d8d47eaSDaniel Lezcano .rating = 300, 104*9d8d47eaSDaniel Lezcano .set_next_event = orion_clkevt_next_event, 105*9d8d47eaSDaniel Lezcano .set_state_shutdown = orion_clkevt_shutdown, 106*9d8d47eaSDaniel Lezcano .set_state_periodic = orion_clkevt_set_periodic, 107*9d8d47eaSDaniel Lezcano .set_state_oneshot = orion_clkevt_shutdown, 108*9d8d47eaSDaniel Lezcano .tick_resume = orion_clkevt_shutdown, 109*9d8d47eaSDaniel Lezcano }; 110*9d8d47eaSDaniel Lezcano 111*9d8d47eaSDaniel Lezcano static irqreturn_t orion_clkevt_irq_handler(int irq, void *dev_id) 112*9d8d47eaSDaniel Lezcano { 113*9d8d47eaSDaniel Lezcano orion_clkevt.event_handler(&orion_clkevt); 114*9d8d47eaSDaniel Lezcano return IRQ_HANDLED; 115*9d8d47eaSDaniel Lezcano } 116*9d8d47eaSDaniel Lezcano 117*9d8d47eaSDaniel Lezcano static struct irqaction orion_clkevt_irq = { 118*9d8d47eaSDaniel Lezcano .name = "orion_event", 119*9d8d47eaSDaniel Lezcano .flags = IRQF_TIMER, 120*9d8d47eaSDaniel Lezcano .handler = orion_clkevt_irq_handler, 121*9d8d47eaSDaniel Lezcano }; 122*9d8d47eaSDaniel Lezcano 123*9d8d47eaSDaniel Lezcano static int __init orion_timer_init(struct device_node *np) 124*9d8d47eaSDaniel Lezcano { 125*9d8d47eaSDaniel Lezcano unsigned long rate; 126*9d8d47eaSDaniel Lezcano struct clk *clk; 127*9d8d47eaSDaniel Lezcano int irq, ret; 128*9d8d47eaSDaniel Lezcano 129*9d8d47eaSDaniel Lezcano /* timer registers are shared with watchdog timer */ 130*9d8d47eaSDaniel Lezcano timer_base = of_iomap(np, 0); 131*9d8d47eaSDaniel Lezcano if (!timer_base) { 132*9d8d47eaSDaniel Lezcano pr_err("%pOFn: unable to map resource\n", np); 133*9d8d47eaSDaniel Lezcano return -ENXIO; 134*9d8d47eaSDaniel Lezcano } 135*9d8d47eaSDaniel Lezcano 136*9d8d47eaSDaniel Lezcano clk = of_clk_get(np, 0); 137*9d8d47eaSDaniel Lezcano if (IS_ERR(clk)) { 138*9d8d47eaSDaniel Lezcano pr_err("%pOFn: unable to get clk\n", np); 139*9d8d47eaSDaniel Lezcano return PTR_ERR(clk); 140*9d8d47eaSDaniel Lezcano } 141*9d8d47eaSDaniel Lezcano 142*9d8d47eaSDaniel Lezcano ret = clk_prepare_enable(clk); 143*9d8d47eaSDaniel Lezcano if (ret) { 144*9d8d47eaSDaniel Lezcano pr_err("Failed to prepare clock\n"); 145*9d8d47eaSDaniel Lezcano return ret; 146*9d8d47eaSDaniel Lezcano } 147*9d8d47eaSDaniel Lezcano 148*9d8d47eaSDaniel Lezcano /* we are only interested in timer1 irq */ 149*9d8d47eaSDaniel Lezcano irq = irq_of_parse_and_map(np, 1); 150*9d8d47eaSDaniel Lezcano if (irq <= 0) { 151*9d8d47eaSDaniel Lezcano pr_err("%pOFn: unable to parse timer1 irq\n", np); 152*9d8d47eaSDaniel Lezcano return -EINVAL; 153*9d8d47eaSDaniel Lezcano } 154*9d8d47eaSDaniel Lezcano 155*9d8d47eaSDaniel Lezcano rate = clk_get_rate(clk); 156*9d8d47eaSDaniel Lezcano 157*9d8d47eaSDaniel Lezcano /* setup timer0 as free-running clocksource */ 158*9d8d47eaSDaniel Lezcano writel(~0, timer_base + TIMER0_VAL); 159*9d8d47eaSDaniel Lezcano writel(~0, timer_base + TIMER0_RELOAD); 160*9d8d47eaSDaniel Lezcano atomic_io_modify(timer_base + TIMER_CTRL, 161*9d8d47eaSDaniel Lezcano TIMER0_RELOAD_EN | TIMER0_EN, 162*9d8d47eaSDaniel Lezcano TIMER0_RELOAD_EN | TIMER0_EN); 163*9d8d47eaSDaniel Lezcano 164*9d8d47eaSDaniel Lezcano ret = clocksource_mmio_init(timer_base + TIMER0_VAL, 165*9d8d47eaSDaniel Lezcano "orion_clocksource", rate, 300, 32, 166*9d8d47eaSDaniel Lezcano clocksource_mmio_readl_down); 167*9d8d47eaSDaniel Lezcano if (ret) { 168*9d8d47eaSDaniel Lezcano pr_err("Failed to initialize mmio timer\n"); 169*9d8d47eaSDaniel Lezcano return ret; 170*9d8d47eaSDaniel Lezcano } 171*9d8d47eaSDaniel Lezcano 172*9d8d47eaSDaniel Lezcano sched_clock_register(orion_read_sched_clock, 32, rate); 173*9d8d47eaSDaniel Lezcano 174*9d8d47eaSDaniel Lezcano /* setup timer1 as clockevent timer */ 175*9d8d47eaSDaniel Lezcano ret = setup_irq(irq, &orion_clkevt_irq); 176*9d8d47eaSDaniel Lezcano if (ret) { 177*9d8d47eaSDaniel Lezcano pr_err("%pOFn: unable to setup irq\n", np); 178*9d8d47eaSDaniel Lezcano return ret; 179*9d8d47eaSDaniel Lezcano } 180*9d8d47eaSDaniel Lezcano 181*9d8d47eaSDaniel Lezcano ticks_per_jiffy = (clk_get_rate(clk) + HZ/2) / HZ; 182*9d8d47eaSDaniel Lezcano orion_clkevt.cpumask = cpumask_of(0); 183*9d8d47eaSDaniel Lezcano orion_clkevt.irq = irq; 184*9d8d47eaSDaniel Lezcano clockevents_config_and_register(&orion_clkevt, rate, 185*9d8d47eaSDaniel Lezcano ORION_ONESHOT_MIN, ORION_ONESHOT_MAX); 186*9d8d47eaSDaniel Lezcano 187*9d8d47eaSDaniel Lezcano 188*9d8d47eaSDaniel Lezcano orion_delay_timer_init(rate); 189*9d8d47eaSDaniel Lezcano 190*9d8d47eaSDaniel Lezcano return 0; 191*9d8d47eaSDaniel Lezcano } 192*9d8d47eaSDaniel Lezcano TIMER_OF_DECLARE(orion_timer, "marvell,orion-timer", orion_timer_init); 193