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