1/* 2 * arch/arm/mach-netx/time.c 3 * 4 * Copyright (c) 2005 Sascha Hauer <s.hauer@pengutronix.de>, Pengutronix 5 * 6 * This program is free software; you can redistribute it and/or modify 7 * it under the terms of the GNU General Public License version 2 8 * as published by the Free Software Foundation. 9 * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 18 */ 19 20#include <linux/init.h> 21#include <linux/interrupt.h> 22#include <linux/irq.h> 23#include <linux/clocksource.h> 24#include <linux/clockchips.h> 25#include <linux/io.h> 26 27#include <mach/hardware.h> 28#include <asm/mach/time.h> 29#include <mach/netx-regs.h> 30 31#define TIMER_CLOCKEVENT 0 32#define TIMER_CLOCKSOURCE 1 33 34static void netx_set_mode(enum clock_event_mode mode, 35 struct clock_event_device *clk) 36{ 37 u32 tmode; 38 39 /* disable timer */ 40 writel(0, NETX_GPIO_COUNTER_CTRL(TIMER_CLOCKEVENT)); 41 42 switch (mode) { 43 case CLOCK_EVT_MODE_PERIODIC: 44 writel(LATCH, NETX_GPIO_COUNTER_MAX(TIMER_CLOCKEVENT)); 45 tmode = NETX_GPIO_COUNTER_CTRL_RST_EN | 46 NETX_GPIO_COUNTER_CTRL_IRQ_EN | 47 NETX_GPIO_COUNTER_CTRL_RUN; 48 break; 49 50 case CLOCK_EVT_MODE_ONESHOT: 51 writel(0, NETX_GPIO_COUNTER_MAX(TIMER_CLOCKEVENT)); 52 tmode = NETX_GPIO_COUNTER_CTRL_IRQ_EN | 53 NETX_GPIO_COUNTER_CTRL_RUN; 54 break; 55 56 default: 57 WARN(1, "%s: unhandled mode %d\n", __func__, mode); 58 /* fall through */ 59 60 case CLOCK_EVT_MODE_SHUTDOWN: 61 case CLOCK_EVT_MODE_UNUSED: 62 case CLOCK_EVT_MODE_RESUME: 63 tmode = 0; 64 break; 65 } 66 67 writel(tmode, NETX_GPIO_COUNTER_CTRL(TIMER_CLOCKEVENT)); 68} 69 70static int netx_set_next_event(unsigned long evt, 71 struct clock_event_device *clk) 72{ 73 writel(0 - evt, NETX_GPIO_COUNTER_CURRENT(TIMER_CLOCKEVENT)); 74 return 0; 75} 76 77static struct clock_event_device netx_clockevent = { 78 .name = "netx-timer" __stringify(TIMER_CLOCKEVENT), 79 .shift = 32, 80 .features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT, 81 .set_next_event = netx_set_next_event, 82 .set_mode = netx_set_mode, 83}; 84 85/* 86 * IRQ handler for the timer 87 */ 88static irqreturn_t 89netx_timer_interrupt(int irq, void *dev_id) 90{ 91 struct clock_event_device *evt = &netx_clockevent; 92 93 /* acknowledge interrupt */ 94 writel(COUNTER_BIT(0), NETX_GPIO_IRQ); 95 96 evt->event_handler(evt); 97 98 return IRQ_HANDLED; 99} 100 101static struct irqaction netx_timer_irq = { 102 .name = "NetX Timer Tick", 103 .flags = IRQF_DISABLED | IRQF_TIMER | IRQF_IRQPOLL, 104 .handler = netx_timer_interrupt, 105}; 106 107cycle_t netx_get_cycles(struct clocksource *cs) 108{ 109 return readl(NETX_GPIO_COUNTER_CURRENT(TIMER_CLOCKSOURCE)); 110} 111 112static struct clocksource clocksource_netx = { 113 .name = "netx_timer", 114 .rating = 200, 115 .read = netx_get_cycles, 116 .mask = CLOCKSOURCE_MASK(32), 117 .shift = 20, 118 .flags = CLOCK_SOURCE_IS_CONTINUOUS, 119}; 120 121/* 122 * Set up timer interrupt 123 */ 124static void __init netx_timer_init(void) 125{ 126 /* disable timer initially */ 127 writel(0, NETX_GPIO_COUNTER_CTRL(0)); 128 129 /* Reset the timer value to zero */ 130 writel(0, NETX_GPIO_COUNTER_CURRENT(0)); 131 132 writel(LATCH, NETX_GPIO_COUNTER_MAX(0)); 133 134 /* acknowledge interrupt */ 135 writel(COUNTER_BIT(0), NETX_GPIO_IRQ); 136 137 /* Enable the interrupt in the specific timer 138 * register and start timer 139 */ 140 writel(COUNTER_BIT(0), NETX_GPIO_IRQ_ENABLE); 141 writel(NETX_GPIO_COUNTER_CTRL_IRQ_EN | NETX_GPIO_COUNTER_CTRL_RUN, 142 NETX_GPIO_COUNTER_CTRL(0)); 143 144 setup_irq(NETX_IRQ_TIMER0, &netx_timer_irq); 145 146 /* Setup timer one for clocksource */ 147 writel(0, NETX_GPIO_COUNTER_CTRL(TIMER_CLOCKSOURCE)); 148 writel(0, NETX_GPIO_COUNTER_CURRENT(TIMER_CLOCKSOURCE)); 149 writel(0xffffffff, NETX_GPIO_COUNTER_MAX(TIMER_CLOCKSOURCE)); 150 151 writel(NETX_GPIO_COUNTER_CTRL_RUN, 152 NETX_GPIO_COUNTER_CTRL(TIMER_CLOCKSOURCE)); 153 154 clocksource_netx.mult = 155 clocksource_hz2mult(CLOCK_TICK_RATE, clocksource_netx.shift); 156 clocksource_register(&clocksource_netx); 157 158 netx_clockevent.mult = div_sc(CLOCK_TICK_RATE, NSEC_PER_SEC, 159 netx_clockevent.shift); 160 netx_clockevent.max_delta_ns = 161 clockevent_delta2ns(0xfffffffe, &netx_clockevent); 162 /* with max_delta_ns >= delta2ns(0x800) the system currently runs fine. 163 * Adding some safety ... */ 164 netx_clockevent.min_delta_ns = 165 clockevent_delta2ns(0xa00, &netx_clockevent); 166 netx_clockevent.cpumask = cpumask_of(0); 167 clockevents_register_device(&netx_clockevent); 168} 169 170struct sys_timer netx_timer = { 171 .init = netx_timer_init, 172}; 173