1/* linux/arch/arm/mach-msm/timer.c 2 * 3 * Copyright (C) 2007 Google, Inc. 4 * 5 * This software is licensed under the terms of the GNU General Public 6 * License version 2, as published by the Free Software Foundation, and 7 * may be copied, distributed, and modified under those terms. 8 * 9 * This program is distributed in the hope that it will be useful, 10 * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 * GNU General Public License for more details. 13 * 14 */ 15 16#include <linux/init.h> 17#include <linux/time.h> 18#include <linux/interrupt.h> 19#include <linux/irq.h> 20#include <linux/clk.h> 21#include <linux/clockchips.h> 22#include <linux/delay.h> 23#include <linux/io.h> 24 25#include <asm/mach/time.h> 26#include <mach/msm_iomap.h> 27 28#ifndef MSM_DGT_BASE 29#define MSM_DGT_BASE (MSM_GPT_BASE + 0x10) 30#endif 31#define MSM_DGT_SHIFT (5) 32 33#define TIMER_MATCH_VAL 0x0000 34#define TIMER_COUNT_VAL 0x0004 35#define TIMER_ENABLE 0x0008 36#define TIMER_ENABLE_CLR_ON_MATCH_EN 2 37#define TIMER_ENABLE_EN 1 38#define TIMER_CLEAR 0x000C 39 40#define CSR_PROTECTION 0x0020 41#define CSR_PROTECTION_EN 1 42 43#define GPT_HZ 32768 44#define DGT_HZ 19200000 /* 19.2 MHz or 600 KHz after shift */ 45 46struct msm_clock { 47 struct clock_event_device clockevent; 48 struct clocksource clocksource; 49 struct irqaction irq; 50 void __iomem *regbase; 51 uint32_t freq; 52 uint32_t shift; 53}; 54 55static irqreturn_t msm_timer_interrupt(int irq, void *dev_id) 56{ 57 struct clock_event_device *evt = dev_id; 58 evt->event_handler(evt); 59 return IRQ_HANDLED; 60} 61 62static cycle_t msm_gpt_read(struct clocksource *cs) 63{ 64 return readl(MSM_GPT_BASE + TIMER_COUNT_VAL); 65} 66 67static cycle_t msm_dgt_read(struct clocksource *cs) 68{ 69 return readl(MSM_DGT_BASE + TIMER_COUNT_VAL) >> MSM_DGT_SHIFT; 70} 71 72static int msm_timer_set_next_event(unsigned long cycles, 73 struct clock_event_device *evt) 74{ 75 struct msm_clock *clock = container_of(evt, struct msm_clock, clockevent); 76 uint32_t now = readl(clock->regbase + TIMER_COUNT_VAL); 77 uint32_t alarm = now + (cycles << clock->shift); 78 int late; 79 80 writel(alarm, clock->regbase + TIMER_MATCH_VAL); 81 now = readl(clock->regbase + TIMER_COUNT_VAL); 82 late = now - alarm; 83 if (late >= (-2 << clock->shift) && late < DGT_HZ*5) { 84 printk(KERN_NOTICE "msm_timer_set_next_event(%lu) clock %s, " 85 "alarm already expired, now %x, alarm %x, late %d\n", 86 cycles, clock->clockevent.name, now, alarm, late); 87 return -ETIME; 88 } 89 return 0; 90} 91 92static void msm_timer_set_mode(enum clock_event_mode mode, 93 struct clock_event_device *evt) 94{ 95 struct msm_clock *clock = container_of(evt, struct msm_clock, clockevent); 96 switch (mode) { 97 case CLOCK_EVT_MODE_RESUME: 98 case CLOCK_EVT_MODE_PERIODIC: 99 break; 100 case CLOCK_EVT_MODE_ONESHOT: 101 writel(TIMER_ENABLE_EN, clock->regbase + TIMER_ENABLE); 102 break; 103 case CLOCK_EVT_MODE_UNUSED: 104 case CLOCK_EVT_MODE_SHUTDOWN: 105 writel(0, clock->regbase + TIMER_ENABLE); 106 break; 107 } 108} 109 110static struct msm_clock msm_clocks[] = { 111 { 112 .clockevent = { 113 .name = "gp_timer", 114 .features = CLOCK_EVT_FEAT_ONESHOT, 115 .shift = 32, 116 .rating = 200, 117 .set_next_event = msm_timer_set_next_event, 118 .set_mode = msm_timer_set_mode, 119 }, 120 .clocksource = { 121 .name = "gp_timer", 122 .rating = 200, 123 .read = msm_gpt_read, 124 .mask = CLOCKSOURCE_MASK(32), 125 .shift = 24, 126 .flags = CLOCK_SOURCE_IS_CONTINUOUS, 127 }, 128 .irq = { 129 .name = "gp_timer", 130 .flags = IRQF_DISABLED | IRQF_TIMER | IRQF_TRIGGER_RISING, 131 .handler = msm_timer_interrupt, 132 .dev_id = &msm_clocks[0].clockevent, 133 .irq = INT_GP_TIMER_EXP 134 }, 135 .regbase = MSM_GPT_BASE, 136 .freq = GPT_HZ 137 }, 138 { 139 .clockevent = { 140 .name = "dg_timer", 141 .features = CLOCK_EVT_FEAT_ONESHOT, 142 .shift = 32 + MSM_DGT_SHIFT, 143 .rating = 300, 144 .set_next_event = msm_timer_set_next_event, 145 .set_mode = msm_timer_set_mode, 146 }, 147 .clocksource = { 148 .name = "dg_timer", 149 .rating = 300, 150 .read = msm_dgt_read, 151 .mask = CLOCKSOURCE_MASK((32 - MSM_DGT_SHIFT)), 152 .shift = 24 - MSM_DGT_SHIFT, 153 .flags = CLOCK_SOURCE_IS_CONTINUOUS, 154 }, 155 .irq = { 156 .name = "dg_timer", 157 .flags = IRQF_DISABLED | IRQF_TIMER | IRQF_TRIGGER_RISING, 158 .handler = msm_timer_interrupt, 159 .dev_id = &msm_clocks[1].clockevent, 160 .irq = INT_DEBUG_TIMER_EXP 161 }, 162 .regbase = MSM_DGT_BASE, 163 .freq = DGT_HZ >> MSM_DGT_SHIFT, 164 .shift = MSM_DGT_SHIFT 165 } 166}; 167 168static void __init msm_timer_init(void) 169{ 170 int i; 171 int res; 172 173 for (i = 0; i < ARRAY_SIZE(msm_clocks); i++) { 174 struct msm_clock *clock = &msm_clocks[i]; 175 struct clock_event_device *ce = &clock->clockevent; 176 struct clocksource *cs = &clock->clocksource; 177 writel(0, clock->regbase + TIMER_ENABLE); 178 writel(0, clock->regbase + TIMER_CLEAR); 179 writel(~0, clock->regbase + TIMER_MATCH_VAL); 180 181 ce->mult = div_sc(clock->freq, NSEC_PER_SEC, ce->shift); 182 /* allow at least 10 seconds to notice that the timer wrapped */ 183 ce->max_delta_ns = 184 clockevent_delta2ns(0xf0000000 >> clock->shift, ce); 185 /* 4 gets rounded down to 3 */ 186 ce->min_delta_ns = clockevent_delta2ns(4, ce); 187 ce->cpumask = cpumask_of(0); 188 189 cs->mult = clocksource_hz2mult(clock->freq, cs->shift); 190 res = clocksource_register(cs); 191 if (res) 192 printk(KERN_ERR "msm_timer_init: clocksource_register " 193 "failed for %s\n", cs->name); 194 195 res = setup_irq(clock->irq.irq, &clock->irq); 196 if (res) 197 printk(KERN_ERR "msm_timer_init: setup_irq " 198 "failed for %s\n", cs->name); 199 200 clockevents_register_device(ce); 201 } 202} 203 204struct sys_timer msm_timer = { 205 .init = msm_timer_init 206}; 207