at v3.1 172 lines 4.3 kB view raw
1/* 2 * linux/arch/arm/common/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/clk.h> 22#include <linux/clocksource.h> 23#include <linux/clockchips.h> 24#include <linux/err.h> 25#include <linux/interrupt.h> 26#include <linux/irq.h> 27#include <linux/io.h> 28 29#include <asm/hardware/arm_timer.h> 30 31static long __init sp804_get_clock_rate(const char *name) 32{ 33 struct clk *clk; 34 long rate; 35 int err; 36 37 clk = clk_get_sys("sp804", name); 38 if (IS_ERR(clk)) { 39 pr_err("sp804: %s clock not found: %d\n", name, 40 (int)PTR_ERR(clk)); 41 return PTR_ERR(clk); 42 } 43 44 err = clk_enable(clk); 45 if (err) { 46 pr_err("sp804: %s clock failed to enable: %d\n", name, err); 47 clk_put(clk); 48 return err; 49 } 50 51 rate = clk_get_rate(clk); 52 if (rate < 0) { 53 pr_err("sp804: %s clock failed to get rate: %ld\n", name, rate); 54 clk_disable(clk); 55 clk_put(clk); 56 } 57 58 return rate; 59} 60 61void __init sp804_clocksource_init(void __iomem *base, const char *name) 62{ 63 long rate = sp804_get_clock_rate(name); 64 65 if (rate < 0) 66 return; 67 68 /* setup timer 0 as free-running clocksource */ 69 writel(0, base + TIMER_CTRL); 70 writel(0xffffffff, base + TIMER_LOAD); 71 writel(0xffffffff, base + TIMER_VALUE); 72 writel(TIMER_CTRL_32BIT | TIMER_CTRL_ENABLE | TIMER_CTRL_PERIODIC, 73 base + TIMER_CTRL); 74 75 clocksource_mmio_init(base + TIMER_VALUE, name, 76 rate, 200, 32, clocksource_mmio_readl_down); 77} 78 79 80static void __iomem *clkevt_base; 81static unsigned long clkevt_reload; 82 83/* 84 * IRQ handler for the timer 85 */ 86static irqreturn_t sp804_timer_interrupt(int irq, void *dev_id) 87{ 88 struct clock_event_device *evt = dev_id; 89 90 /* clear the interrupt */ 91 writel(1, clkevt_base + TIMER_INTCLR); 92 93 evt->event_handler(evt); 94 95 return IRQ_HANDLED; 96} 97 98static void sp804_set_mode(enum clock_event_mode mode, 99 struct clock_event_device *evt) 100{ 101 unsigned long ctrl = TIMER_CTRL_32BIT | TIMER_CTRL_IE; 102 103 writel(ctrl, clkevt_base + TIMER_CTRL); 104 105 switch (mode) { 106 case CLOCK_EVT_MODE_PERIODIC: 107 writel(clkevt_reload, clkevt_base + TIMER_LOAD); 108 ctrl |= TIMER_CTRL_PERIODIC | TIMER_CTRL_ENABLE; 109 break; 110 111 case CLOCK_EVT_MODE_ONESHOT: 112 /* period set, and timer enabled in 'next_event' hook */ 113 ctrl |= TIMER_CTRL_ONESHOT; 114 break; 115 116 case CLOCK_EVT_MODE_UNUSED: 117 case CLOCK_EVT_MODE_SHUTDOWN: 118 default: 119 break; 120 } 121 122 writel(ctrl, clkevt_base + TIMER_CTRL); 123} 124 125static int sp804_set_next_event(unsigned long next, 126 struct clock_event_device *evt) 127{ 128 unsigned long ctrl = readl(clkevt_base + TIMER_CTRL); 129 130 writel(next, clkevt_base + TIMER_LOAD); 131 writel(ctrl | TIMER_CTRL_ENABLE, clkevt_base + TIMER_CTRL); 132 133 return 0; 134} 135 136static struct clock_event_device sp804_clockevent = { 137 .shift = 32, 138 .features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT, 139 .set_mode = sp804_set_mode, 140 .set_next_event = sp804_set_next_event, 141 .rating = 300, 142 .cpumask = cpu_all_mask, 143}; 144 145static struct irqaction sp804_timer_irq = { 146 .name = "timer", 147 .flags = IRQF_DISABLED | IRQF_TIMER | IRQF_IRQPOLL, 148 .handler = sp804_timer_interrupt, 149 .dev_id = &sp804_clockevent, 150}; 151 152void __init sp804_clockevents_init(void __iomem *base, unsigned int irq, 153 const char *name) 154{ 155 struct clock_event_device *evt = &sp804_clockevent; 156 long rate = sp804_get_clock_rate(name); 157 158 if (rate < 0) 159 return; 160 161 clkevt_base = base; 162 clkevt_reload = DIV_ROUND_CLOSEST(rate, HZ); 163 164 evt->name = name; 165 evt->irq = irq; 166 evt->mult = div_sc(rate, NSEC_PER_SEC, evt->shift); 167 evt->max_delta_ns = clockevent_delta2ns(0xffffffff, evt); 168 evt->min_delta_ns = clockevent_delta2ns(0xf, evt); 169 170 setup_irq(irq, &sp804_timer_irq); 171 clockevents_register_device(evt); 172}