1/* 2 * linux/arch/arm/plat-versatile/timer-sp.c 3 * 4 * Copyright (C) 1999 - 2003 ARM Limited 5 * Copyright (C) 2000 Deep Blue Solutions Ltd 6 * 7 * This program is free software; you can redistribute it and/or modify 8 * it under the terms of the GNU General Public License as published by 9 * the Free Software Foundation; either version 2 of the License, or 10 * (at your option) any later version. 11 * 12 * This program is distributed in the hope that it will be useful, 13 * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 * GNU General Public License for more details. 16 * 17 * You should have received a copy of the GNU General Public License 18 * along with this program; if not, write to the Free Software 19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 20 */ 21#include <linux/clocksource.h> 22#include <linux/clockchips.h> 23#include <linux/interrupt.h> 24#include <linux/irq.h> 25#include <linux/io.h> 26 27#include <asm/hardware/arm_timer.h> 28 29#include <plat/timer-sp.h> 30 31/* 32 * These timers are currently always setup to be clocked at 1MHz. 33 */ 34#define TIMER_FREQ_KHZ (1000) 35#define TIMER_RELOAD (TIMER_FREQ_KHZ * 1000 / HZ) 36 37static void __iomem *clksrc_base; 38 39static cycle_t sp804_read(struct clocksource *cs) 40{ 41 return ~readl(clksrc_base + TIMER_VALUE); 42} 43 44static struct clocksource clocksource_sp804 = { 45 .name = "timer3", 46 .rating = 200, 47 .read = sp804_read, 48 .mask = CLOCKSOURCE_MASK(32), 49 .shift = 20, 50 .flags = CLOCK_SOURCE_IS_CONTINUOUS, 51}; 52 53void __init sp804_clocksource_init(void __iomem *base) 54{ 55 struct clocksource *cs = &clocksource_sp804; 56 57 clksrc_base = base; 58 59 /* setup timer 0 as free-running clocksource */ 60 writel(0, clksrc_base + TIMER_CTRL); 61 writel(0xffffffff, clksrc_base + TIMER_LOAD); 62 writel(0xffffffff, clksrc_base + TIMER_VALUE); 63 writel(TIMER_CTRL_32BIT | TIMER_CTRL_ENABLE | TIMER_CTRL_PERIODIC, 64 clksrc_base + TIMER_CTRL); 65 66 cs->mult = clocksource_khz2mult(TIMER_FREQ_KHZ, cs->shift); 67 clocksource_register(cs); 68} 69 70 71static void __iomem *clkevt_base; 72 73/* 74 * IRQ handler for the timer 75 */ 76static irqreturn_t sp804_timer_interrupt(int irq, void *dev_id) 77{ 78 struct clock_event_device *evt = dev_id; 79 80 /* clear the interrupt */ 81 writel(1, clkevt_base + TIMER_INTCLR); 82 83 evt->event_handler(evt); 84 85 return IRQ_HANDLED; 86} 87 88static void sp804_set_mode(enum clock_event_mode mode, 89 struct clock_event_device *evt) 90{ 91 unsigned long ctrl = TIMER_CTRL_32BIT | TIMER_CTRL_IE; 92 93 writel(ctrl, clkevt_base + TIMER_CTRL); 94 95 switch (mode) { 96 case CLOCK_EVT_MODE_PERIODIC: 97 writel(TIMER_RELOAD, clkevt_base + TIMER_LOAD); 98 ctrl |= TIMER_CTRL_PERIODIC | TIMER_CTRL_ENABLE; 99 break; 100 101 case CLOCK_EVT_MODE_ONESHOT: 102 /* period set, and timer enabled in 'next_event' hook */ 103 ctrl |= TIMER_CTRL_ONESHOT; 104 break; 105 106 case CLOCK_EVT_MODE_UNUSED: 107 case CLOCK_EVT_MODE_SHUTDOWN: 108 default: 109 break; 110 } 111 112 writel(ctrl, clkevt_base + TIMER_CTRL); 113} 114 115static int sp804_set_next_event(unsigned long next, 116 struct clock_event_device *evt) 117{ 118 unsigned long ctrl = readl(clkevt_base + TIMER_CTRL); 119 120 writel(next, clkevt_base + TIMER_LOAD); 121 writel(ctrl | TIMER_CTRL_ENABLE, clkevt_base + TIMER_CTRL); 122 123 return 0; 124} 125 126static struct clock_event_device sp804_clockevent = { 127 .name = "timer0", 128 .shift = 32, 129 .features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT, 130 .set_mode = sp804_set_mode, 131 .set_next_event = sp804_set_next_event, 132 .rating = 300, 133 .cpumask = cpu_all_mask, 134}; 135 136static struct irqaction sp804_timer_irq = { 137 .name = "timer", 138 .flags = IRQF_DISABLED | IRQF_TIMER | IRQF_IRQPOLL, 139 .handler = sp804_timer_interrupt, 140 .dev_id = &sp804_clockevent, 141}; 142 143void __init sp804_clockevents_init(void __iomem *base, unsigned int timer_irq) 144{ 145 struct clock_event_device *evt = &sp804_clockevent; 146 147 clkevt_base = base; 148 149 evt->irq = timer_irq; 150 evt->mult = div_sc(TIMER_FREQ_KHZ, NSEC_PER_MSEC, evt->shift); 151 evt->max_delta_ns = clockevent_delta2ns(0xffffffff, evt); 152 evt->min_delta_ns = clockevent_delta2ns(0xf, evt); 153 154 setup_irq(timer_irq, &sp804_timer_irq); 155 clockevents_register_device(evt); 156} 157