1e6b7f580SIvan Khoronzhuk /* 2e6b7f580SIvan Khoronzhuk * Keystone broadcast clock-event 3e6b7f580SIvan Khoronzhuk * 4e6b7f580SIvan Khoronzhuk * Copyright 2013 Texas Instruments, Inc. 5e6b7f580SIvan Khoronzhuk * 6e6b7f580SIvan Khoronzhuk * Author: Ivan Khoronzhuk <ivan.khoronzhuk@ti.com> 7e6b7f580SIvan Khoronzhuk * 8e6b7f580SIvan Khoronzhuk * This program is free software; you can redistribute it and/or modify 9e6b7f580SIvan Khoronzhuk * it under the terms of the GNU General Public License version 2 as 10e6b7f580SIvan Khoronzhuk * published by the Free Software Foundation. 11e6b7f580SIvan Khoronzhuk * 12e6b7f580SIvan Khoronzhuk */ 13e6b7f580SIvan Khoronzhuk 14e6b7f580SIvan Khoronzhuk #include <linux/clk.h> 15e6b7f580SIvan Khoronzhuk #include <linux/clockchips.h> 16e6b7f580SIvan Khoronzhuk #include <linux/clocksource.h> 17e6b7f580SIvan Khoronzhuk #include <linux/interrupt.h> 18e6b7f580SIvan Khoronzhuk #include <linux/of_address.h> 19e6b7f580SIvan Khoronzhuk #include <linux/of_irq.h> 20e6b7f580SIvan Khoronzhuk 21e6b7f580SIvan Khoronzhuk #define TIMER_NAME "timer-keystone" 22e6b7f580SIvan Khoronzhuk 23e6b7f580SIvan Khoronzhuk /* Timer register offsets */ 24e6b7f580SIvan Khoronzhuk #define TIM12 0x10 25e6b7f580SIvan Khoronzhuk #define TIM34 0x14 26e6b7f580SIvan Khoronzhuk #define PRD12 0x18 27e6b7f580SIvan Khoronzhuk #define PRD34 0x1c 28e6b7f580SIvan Khoronzhuk #define TCR 0x20 29e6b7f580SIvan Khoronzhuk #define TGCR 0x24 30e6b7f580SIvan Khoronzhuk #define INTCTLSTAT 0x44 31e6b7f580SIvan Khoronzhuk 32e6b7f580SIvan Khoronzhuk /* Timer register bitfields */ 33e6b7f580SIvan Khoronzhuk #define TCR_ENAMODE_MASK 0xC0 34e6b7f580SIvan Khoronzhuk #define TCR_ENAMODE_ONESHOT_MASK 0x40 35e6b7f580SIvan Khoronzhuk #define TCR_ENAMODE_PERIODIC_MASK 0x80 36e6b7f580SIvan Khoronzhuk 37e6b7f580SIvan Khoronzhuk #define TGCR_TIM_UNRESET_MASK 0x03 38e6b7f580SIvan Khoronzhuk #define INTCTLSTAT_ENINT_MASK 0x01 39e6b7f580SIvan Khoronzhuk 40e6b7f580SIvan Khoronzhuk /** 41e6b7f580SIvan Khoronzhuk * struct keystone_timer: holds timer's data 42e6b7f580SIvan Khoronzhuk * @base: timer memory base address 43e6b7f580SIvan Khoronzhuk * @hz_period: cycles per HZ period 44e6b7f580SIvan Khoronzhuk * @event_dev: event device based on timer 45e6b7f580SIvan Khoronzhuk */ 46e6b7f580SIvan Khoronzhuk static struct keystone_timer { 47e6b7f580SIvan Khoronzhuk void __iomem *base; 48e6b7f580SIvan Khoronzhuk unsigned long hz_period; 49e6b7f580SIvan Khoronzhuk struct clock_event_device event_dev; 50e6b7f580SIvan Khoronzhuk } timer; 51e6b7f580SIvan Khoronzhuk 52e6b7f580SIvan Khoronzhuk static inline u32 keystone_timer_readl(unsigned long rg) 53e6b7f580SIvan Khoronzhuk { 54e6b7f580SIvan Khoronzhuk return readl_relaxed(timer.base + rg); 55e6b7f580SIvan Khoronzhuk } 56e6b7f580SIvan Khoronzhuk 57e6b7f580SIvan Khoronzhuk static inline void keystone_timer_writel(u32 val, unsigned long rg) 58e6b7f580SIvan Khoronzhuk { 59e6b7f580SIvan Khoronzhuk writel_relaxed(val, timer.base + rg); 60e6b7f580SIvan Khoronzhuk } 61e6b7f580SIvan Khoronzhuk 62e6b7f580SIvan Khoronzhuk /** 63e6b7f580SIvan Khoronzhuk * keystone_timer_barrier: write memory barrier 64e6b7f580SIvan Khoronzhuk * use explicit barrier to avoid using readl/writel non relaxed function 65e6b7f580SIvan Khoronzhuk * variants, because in our case non relaxed variants hide the true places 66e6b7f580SIvan Khoronzhuk * where barrier is needed. 67e6b7f580SIvan Khoronzhuk */ 68e6b7f580SIvan Khoronzhuk static inline void keystone_timer_barrier(void) 69e6b7f580SIvan Khoronzhuk { 70e6b7f580SIvan Khoronzhuk __iowmb(); 71e6b7f580SIvan Khoronzhuk } 72e6b7f580SIvan Khoronzhuk 73e6b7f580SIvan Khoronzhuk /** 74e6b7f580SIvan Khoronzhuk * keystone_timer_config: configures timer to work in oneshot/periodic modes. 75634eb0ecSViresh Kumar * @ mask: mask of the mode to configure 76e6b7f580SIvan Khoronzhuk * @ period: cycles number to configure for 77e6b7f580SIvan Khoronzhuk */ 78634eb0ecSViresh Kumar static int keystone_timer_config(u64 period, int mask) 79e6b7f580SIvan Khoronzhuk { 80e6b7f580SIvan Khoronzhuk u32 tcr; 81e6b7f580SIvan Khoronzhuk u32 off; 82e6b7f580SIvan Khoronzhuk 83e6b7f580SIvan Khoronzhuk tcr = keystone_timer_readl(TCR); 84e6b7f580SIvan Khoronzhuk off = tcr & ~(TCR_ENAMODE_MASK); 85e6b7f580SIvan Khoronzhuk 86e6b7f580SIvan Khoronzhuk /* set enable mode */ 87634eb0ecSViresh Kumar tcr |= mask; 88e6b7f580SIvan Khoronzhuk 89e6b7f580SIvan Khoronzhuk /* disable timer */ 90e6b7f580SIvan Khoronzhuk keystone_timer_writel(off, TCR); 91e6b7f580SIvan Khoronzhuk /* here we have to be sure the timer has been disabled */ 92e6b7f580SIvan Khoronzhuk keystone_timer_barrier(); 93e6b7f580SIvan Khoronzhuk 94e6b7f580SIvan Khoronzhuk /* reset counter to zero, set new period */ 95e6b7f580SIvan Khoronzhuk keystone_timer_writel(0, TIM12); 96e6b7f580SIvan Khoronzhuk keystone_timer_writel(0, TIM34); 97e6b7f580SIvan Khoronzhuk keystone_timer_writel(period & 0xffffffff, PRD12); 98e6b7f580SIvan Khoronzhuk keystone_timer_writel(period >> 32, PRD34); 99e6b7f580SIvan Khoronzhuk 100e6b7f580SIvan Khoronzhuk /* 101e6b7f580SIvan Khoronzhuk * enable timer 102e6b7f580SIvan Khoronzhuk * here we have to be sure that CNTLO, CNTHI, PRDLO, PRDHI registers 103e6b7f580SIvan Khoronzhuk * have been written. 104e6b7f580SIvan Khoronzhuk */ 105e6b7f580SIvan Khoronzhuk keystone_timer_barrier(); 106e6b7f580SIvan Khoronzhuk keystone_timer_writel(tcr, TCR); 107e6b7f580SIvan Khoronzhuk return 0; 108e6b7f580SIvan Khoronzhuk } 109e6b7f580SIvan Khoronzhuk 110e6b7f580SIvan Khoronzhuk static void keystone_timer_disable(void) 111e6b7f580SIvan Khoronzhuk { 112e6b7f580SIvan Khoronzhuk u32 tcr; 113e6b7f580SIvan Khoronzhuk 114e6b7f580SIvan Khoronzhuk tcr = keystone_timer_readl(TCR); 115e6b7f580SIvan Khoronzhuk 116e6b7f580SIvan Khoronzhuk /* disable timer */ 117e6b7f580SIvan Khoronzhuk tcr &= ~(TCR_ENAMODE_MASK); 118e6b7f580SIvan Khoronzhuk keystone_timer_writel(tcr, TCR); 119e6b7f580SIvan Khoronzhuk } 120e6b7f580SIvan Khoronzhuk 121e6b7f580SIvan Khoronzhuk static irqreturn_t keystone_timer_interrupt(int irq, void *dev_id) 122e6b7f580SIvan Khoronzhuk { 123e6b7f580SIvan Khoronzhuk struct clock_event_device *evt = dev_id; 124e6b7f580SIvan Khoronzhuk 125e6b7f580SIvan Khoronzhuk evt->event_handler(evt); 126e6b7f580SIvan Khoronzhuk return IRQ_HANDLED; 127e6b7f580SIvan Khoronzhuk } 128e6b7f580SIvan Khoronzhuk 129e6b7f580SIvan Khoronzhuk static int keystone_set_next_event(unsigned long cycles, 130e6b7f580SIvan Khoronzhuk struct clock_event_device *evt) 131e6b7f580SIvan Khoronzhuk { 132634eb0ecSViresh Kumar return keystone_timer_config(cycles, TCR_ENAMODE_ONESHOT_MASK); 133e6b7f580SIvan Khoronzhuk } 134e6b7f580SIvan Khoronzhuk 135634eb0ecSViresh Kumar static int keystone_shutdown(struct clock_event_device *evt) 136e6b7f580SIvan Khoronzhuk { 137e6b7f580SIvan Khoronzhuk keystone_timer_disable(); 138634eb0ecSViresh Kumar return 0; 139e6b7f580SIvan Khoronzhuk } 140634eb0ecSViresh Kumar 141634eb0ecSViresh Kumar static int keystone_set_periodic(struct clock_event_device *evt) 142634eb0ecSViresh Kumar { 143634eb0ecSViresh Kumar keystone_timer_config(timer.hz_period, TCR_ENAMODE_PERIODIC_MASK); 144634eb0ecSViresh Kumar return 0; 145e6b7f580SIvan Khoronzhuk } 146e6b7f580SIvan Khoronzhuk 14753186505SDaniel Lezcano static int __init keystone_timer_init(struct device_node *np) 148e6b7f580SIvan Khoronzhuk { 149e6b7f580SIvan Khoronzhuk struct clock_event_device *event_dev = &timer.event_dev; 150e6b7f580SIvan Khoronzhuk unsigned long rate; 151e6b7f580SIvan Khoronzhuk struct clk *clk; 152e6b7f580SIvan Khoronzhuk int irq, error; 153e6b7f580SIvan Khoronzhuk 154e6b7f580SIvan Khoronzhuk irq = irq_of_parse_and_map(np, 0); 155bdf7344eSDaniel Lezcano if (!irq) { 156e6b7f580SIvan Khoronzhuk pr_err("%s: failed to map interrupts\n", __func__); 15753186505SDaniel Lezcano return -EINVAL; 158e6b7f580SIvan Khoronzhuk } 159e6b7f580SIvan Khoronzhuk 160e6b7f580SIvan Khoronzhuk timer.base = of_iomap(np, 0); 161e6b7f580SIvan Khoronzhuk if (!timer.base) { 162e6b7f580SIvan Khoronzhuk pr_err("%s: failed to map registers\n", __func__); 16353186505SDaniel Lezcano return -ENXIO; 164e6b7f580SIvan Khoronzhuk } 165e6b7f580SIvan Khoronzhuk 166e6b7f580SIvan Khoronzhuk clk = of_clk_get(np, 0); 167e6b7f580SIvan Khoronzhuk if (IS_ERR(clk)) { 168e6b7f580SIvan Khoronzhuk pr_err("%s: failed to get clock\n", __func__); 169e6b7f580SIvan Khoronzhuk iounmap(timer.base); 17053186505SDaniel Lezcano return PTR_ERR(clk); 171e6b7f580SIvan Khoronzhuk } 172e6b7f580SIvan Khoronzhuk 173e6b7f580SIvan Khoronzhuk error = clk_prepare_enable(clk); 174e6b7f580SIvan Khoronzhuk if (error) { 175e6b7f580SIvan Khoronzhuk pr_err("%s: failed to enable clock\n", __func__); 176e6b7f580SIvan Khoronzhuk goto err; 177e6b7f580SIvan Khoronzhuk } 178e6b7f580SIvan Khoronzhuk 179e6b7f580SIvan Khoronzhuk rate = clk_get_rate(clk); 180e6b7f580SIvan Khoronzhuk 181e6b7f580SIvan Khoronzhuk /* disable, use internal clock source */ 182e6b7f580SIvan Khoronzhuk keystone_timer_writel(0, TCR); 183e6b7f580SIvan Khoronzhuk /* here we have to be sure the timer has been disabled */ 184e6b7f580SIvan Khoronzhuk keystone_timer_barrier(); 185e6b7f580SIvan Khoronzhuk 186e6b7f580SIvan Khoronzhuk /* reset timer as 64-bit, no pre-scaler, plus features are disabled */ 187e6b7f580SIvan Khoronzhuk keystone_timer_writel(0, TGCR); 188e6b7f580SIvan Khoronzhuk 189e6b7f580SIvan Khoronzhuk /* unreset timer */ 190e099d01eSMatthias Brugger keystone_timer_writel(TGCR_TIM_UNRESET_MASK, TGCR); 191e6b7f580SIvan Khoronzhuk 192e6b7f580SIvan Khoronzhuk /* init counter to zero */ 193e6b7f580SIvan Khoronzhuk keystone_timer_writel(0, TIM12); 194e6b7f580SIvan Khoronzhuk keystone_timer_writel(0, TIM34); 195e6b7f580SIvan Khoronzhuk 196e6b7f580SIvan Khoronzhuk timer.hz_period = DIV_ROUND_UP(rate, HZ); 197e6b7f580SIvan Khoronzhuk 198e6b7f580SIvan Khoronzhuk /* enable timer interrupts */ 199e6b7f580SIvan Khoronzhuk keystone_timer_writel(INTCTLSTAT_ENINT_MASK, INTCTLSTAT); 200e6b7f580SIvan Khoronzhuk 201e6b7f580SIvan Khoronzhuk error = request_irq(irq, keystone_timer_interrupt, IRQF_TIMER, 202e6b7f580SIvan Khoronzhuk TIMER_NAME, event_dev); 203e6b7f580SIvan Khoronzhuk if (error) { 204e6b7f580SIvan Khoronzhuk pr_err("%s: failed to setup irq\n", __func__); 205e6b7f580SIvan Khoronzhuk goto err; 206e6b7f580SIvan Khoronzhuk } 207e6b7f580SIvan Khoronzhuk 208e6b7f580SIvan Khoronzhuk /* setup clockevent */ 209e6b7f580SIvan Khoronzhuk event_dev->features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT; 210e6b7f580SIvan Khoronzhuk event_dev->set_next_event = keystone_set_next_event; 211634eb0ecSViresh Kumar event_dev->set_state_shutdown = keystone_shutdown; 212634eb0ecSViresh Kumar event_dev->set_state_periodic = keystone_set_periodic; 213634eb0ecSViresh Kumar event_dev->set_state_oneshot = keystone_shutdown; 214*f8f5fe86SSudeep Holla event_dev->cpumask = cpu_possible_mask; 215e6b7f580SIvan Khoronzhuk event_dev->owner = THIS_MODULE; 216e6b7f580SIvan Khoronzhuk event_dev->name = TIMER_NAME; 217e6b7f580SIvan Khoronzhuk event_dev->irq = irq; 218e6b7f580SIvan Khoronzhuk 219e6b7f580SIvan Khoronzhuk clockevents_config_and_register(event_dev, rate, 1, ULONG_MAX); 220e6b7f580SIvan Khoronzhuk 221e6b7f580SIvan Khoronzhuk pr_info("keystone timer clock @%lu Hz\n", rate); 22253186505SDaniel Lezcano return 0; 223e6b7f580SIvan Khoronzhuk err: 224e6b7f580SIvan Khoronzhuk clk_put(clk); 225e6b7f580SIvan Khoronzhuk iounmap(timer.base); 22653186505SDaniel Lezcano return error; 227e6b7f580SIvan Khoronzhuk } 228e6b7f580SIvan Khoronzhuk 22917273395SDaniel Lezcano TIMER_OF_DECLARE(keystone_timer, "ti,keystone-timer", 230e6b7f580SIvan Khoronzhuk keystone_timer_init); 231