1/* 2 * linux/arch/arm/plat-mxc/epit.c 3 * 4 * Copyright (C) 2010 Sascha Hauer <s.hauer@pengutronix.de> 5 * 6 * This program is free software; you can redistribute it and/or 7 * modify it under the terms of the GNU General Public License 8 * as published by the Free Software Foundation; either version 2 9 * of the License, or (at your option) any later version. 10 * This program is distributed in the hope that it will be useful, 11 * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 * GNU General Public License for more details. 14 * 15 * You should have received a copy of the GNU General Public License 16 * along with this program; if not, write to the Free Software 17 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, 18 * MA 02110-1301, USA. 19 */ 20 21#define EPITCR 0x00 22#define EPITSR 0x04 23#define EPITLR 0x08 24#define EPITCMPR 0x0c 25#define EPITCNR 0x10 26 27#define EPITCR_EN (1 << 0) 28#define EPITCR_ENMOD (1 << 1) 29#define EPITCR_OCIEN (1 << 2) 30#define EPITCR_RLD (1 << 3) 31#define EPITCR_PRESC(x) (((x) & 0xfff) << 4) 32#define EPITCR_SWR (1 << 16) 33#define EPITCR_IOVW (1 << 17) 34#define EPITCR_DBGEN (1 << 18) 35#define EPITCR_WAITEN (1 << 19) 36#define EPITCR_RES (1 << 20) 37#define EPITCR_STOPEN (1 << 21) 38#define EPITCR_OM_DISCON (0 << 22) 39#define EPITCR_OM_TOGGLE (1 << 22) 40#define EPITCR_OM_CLEAR (2 << 22) 41#define EPITCR_OM_SET (3 << 22) 42#define EPITCR_CLKSRC_OFF (0 << 24) 43#define EPITCR_CLKSRC_PERIPHERAL (1 << 24) 44#define EPITCR_CLKSRC_REF_HIGH (1 << 24) 45#define EPITCR_CLKSRC_REF_LOW (3 << 24) 46 47#define EPITSR_OCIF (1 << 0) 48 49#include <linux/interrupt.h> 50#include <linux/irq.h> 51#include <linux/clockchips.h> 52#include <linux/clk.h> 53#include <linux/err.h> 54#include <asm/mach/time.h> 55 56#include "common.h" 57#include "hardware.h" 58 59static struct clock_event_device clockevent_epit; 60static enum clock_event_mode clockevent_mode = CLOCK_EVT_MODE_UNUSED; 61 62static void __iomem *timer_base; 63 64static inline void epit_irq_disable(void) 65{ 66 u32 val; 67 68 val = __raw_readl(timer_base + EPITCR); 69 val &= ~EPITCR_OCIEN; 70 __raw_writel(val, timer_base + EPITCR); 71} 72 73static inline void epit_irq_enable(void) 74{ 75 u32 val; 76 77 val = __raw_readl(timer_base + EPITCR); 78 val |= EPITCR_OCIEN; 79 __raw_writel(val, timer_base + EPITCR); 80} 81 82static void epit_irq_acknowledge(void) 83{ 84 __raw_writel(EPITSR_OCIF, timer_base + EPITSR); 85} 86 87static int __init epit_clocksource_init(struct clk *timer_clk) 88{ 89 unsigned int c = clk_get_rate(timer_clk); 90 91 return clocksource_mmio_init(timer_base + EPITCNR, "epit", c, 200, 32, 92 clocksource_mmio_readl_down); 93} 94 95/* clock event */ 96 97static int epit_set_next_event(unsigned long evt, 98 struct clock_event_device *unused) 99{ 100 unsigned long tcmp; 101 102 tcmp = __raw_readl(timer_base + EPITCNR); 103 104 __raw_writel(tcmp - evt, timer_base + EPITCMPR); 105 106 return 0; 107} 108 109static void epit_set_mode(enum clock_event_mode mode, 110 struct clock_event_device *evt) 111{ 112 unsigned long flags; 113 114 /* 115 * The timer interrupt generation is disabled at least 116 * for enough time to call epit_set_next_event() 117 */ 118 local_irq_save(flags); 119 120 /* Disable interrupt in GPT module */ 121 epit_irq_disable(); 122 123 if (mode != clockevent_mode) { 124 /* Set event time into far-far future */ 125 126 /* Clear pending interrupt */ 127 epit_irq_acknowledge(); 128 } 129 130 /* Remember timer mode */ 131 clockevent_mode = mode; 132 local_irq_restore(flags); 133 134 switch (mode) { 135 case CLOCK_EVT_MODE_PERIODIC: 136 printk(KERN_ERR "epit_set_mode: Periodic mode is not " 137 "supported for i.MX EPIT\n"); 138 break; 139 case CLOCK_EVT_MODE_ONESHOT: 140 /* 141 * Do not put overhead of interrupt enable/disable into 142 * epit_set_next_event(), the core has about 4 minutes 143 * to call epit_set_next_event() or shutdown clock after 144 * mode switching 145 */ 146 local_irq_save(flags); 147 epit_irq_enable(); 148 local_irq_restore(flags); 149 break; 150 case CLOCK_EVT_MODE_SHUTDOWN: 151 case CLOCK_EVT_MODE_UNUSED: 152 case CLOCK_EVT_MODE_RESUME: 153 /* Left event sources disabled, no more interrupts appear */ 154 break; 155 } 156} 157 158/* 159 * IRQ handler for the timer 160 */ 161static irqreturn_t epit_timer_interrupt(int irq, void *dev_id) 162{ 163 struct clock_event_device *evt = &clockevent_epit; 164 165 epit_irq_acknowledge(); 166 167 evt->event_handler(evt); 168 169 return IRQ_HANDLED; 170} 171 172static struct irqaction epit_timer_irq = { 173 .name = "i.MX EPIT Timer Tick", 174 .flags = IRQF_TIMER | IRQF_IRQPOLL, 175 .handler = epit_timer_interrupt, 176}; 177 178static struct clock_event_device clockevent_epit = { 179 .name = "epit", 180 .features = CLOCK_EVT_FEAT_ONESHOT, 181 .set_mode = epit_set_mode, 182 .set_next_event = epit_set_next_event, 183 .rating = 200, 184}; 185 186static int __init epit_clockevent_init(struct clk *timer_clk) 187{ 188 clockevent_epit.cpumask = cpumask_of(0); 189 clockevents_config_and_register(&clockevent_epit, 190 clk_get_rate(timer_clk), 191 0x800, 0xfffffffe); 192 193 return 0; 194} 195 196void __init epit_timer_init(void __iomem *base, int irq) 197{ 198 struct clk *timer_clk; 199 200 timer_clk = clk_get_sys("imx-epit.0", NULL); 201 if (IS_ERR(timer_clk)) { 202 pr_err("i.MX epit: unable to get clk\n"); 203 return; 204 } 205 206 clk_prepare_enable(timer_clk); 207 208 timer_base = base; 209 210 /* 211 * Initialise to a known state (all timers off, and timing reset) 212 */ 213 __raw_writel(0x0, timer_base + EPITCR); 214 215 __raw_writel(0xffffffff, timer_base + EPITLR); 216 __raw_writel(EPITCR_EN | EPITCR_CLKSRC_REF_HIGH | EPITCR_WAITEN, 217 timer_base + EPITCR); 218 219 /* init and register the timer to the framework */ 220 epit_clocksource_init(timer_clk); 221 epit_clockevent_init(timer_clk); 222 223 /* Make irqs happen */ 224 setup_irq(irq, &epit_timer_irq); 225} 226