1/* 2 * arch/sh/kernel/timers/timer-tmu.c - TMU Timer Support 3 * 4 * Copyright (C) 2005 - 2007 Paul Mundt 5 * 6 * TMU handling code hacked out of arch/sh/kernel/time.c 7 * 8 * Copyright (C) 1999 Tetsuya Okada & Niibe Yutaka 9 * Copyright (C) 2000 Philipp Rumpf <prumpf@tux.org> 10 * Copyright (C) 2002, 2003, 2004 Paul Mundt 11 * Copyright (C) 2002 M. R. Brown <mrbrown@linux-sh.org> 12 * 13 * This file is subject to the terms and conditions of the GNU General Public 14 * License. See the file "COPYING" in the main directory of this archive 15 * for more details. 16 */ 17#include <linux/init.h> 18#include <linux/kernel.h> 19#include <linux/interrupt.h> 20#include <linux/seqlock.h> 21#include <linux/clockchips.h> 22#include <asm/timer.h> 23#include <asm/rtc.h> 24#include <asm/io.h> 25#include <asm/irq.h> 26#include <asm/clock.h> 27 28#define TMU_TOCR_INIT 0x00 29#define TMU_TCR_INIT 0x0020 30 31static int tmu_timer_start(void) 32{ 33 ctrl_outb(ctrl_inb(TMU_TSTR) | 0x3, TMU_TSTR); 34 return 0; 35} 36 37static void tmu0_timer_set_interval(unsigned long interval, unsigned int reload) 38{ 39 ctrl_outl(interval, TMU0_TCNT); 40 41 /* 42 * TCNT reloads from TCOR on underflow, clear it if we don't 43 * intend to auto-reload 44 */ 45 if (reload) 46 ctrl_outl(interval, TMU0_TCOR); 47 else 48 ctrl_outl(0, TMU0_TCOR); 49 50 tmu_timer_start(); 51} 52 53static int tmu_timer_stop(void) 54{ 55 ctrl_outb(ctrl_inb(TMU_TSTR) & ~0x3, TMU_TSTR); 56 return 0; 57} 58 59static cycle_t tmu_timer_read(void) 60{ 61 return ~ctrl_inl(TMU1_TCNT); 62} 63 64static int tmu_set_next_event(unsigned long cycles, 65 struct clock_event_device *evt) 66{ 67 tmu0_timer_set_interval(cycles, 1); 68 return 0; 69} 70 71static void tmu_set_mode(enum clock_event_mode mode, 72 struct clock_event_device *evt) 73{ 74 switch (mode) { 75 case CLOCK_EVT_MODE_PERIODIC: 76 ctrl_outl(ctrl_inl(TMU0_TCNT), TMU0_TCOR); 77 break; 78 case CLOCK_EVT_MODE_ONESHOT: 79 ctrl_outl(0, TMU0_TCOR); 80 break; 81 case CLOCK_EVT_MODE_UNUSED: 82 case CLOCK_EVT_MODE_SHUTDOWN: 83 break; 84 } 85} 86 87static struct clock_event_device tmu0_clockevent = { 88 .name = "tmu0", 89 .shift = 32, 90 .features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT, 91 .set_mode = tmu_set_mode, 92 .set_next_event = tmu_set_next_event, 93}; 94 95static irqreturn_t tmu_timer_interrupt(int irq, void *dummy) 96{ 97 struct clock_event_device *evt = &tmu0_clockevent; 98 unsigned long timer_status; 99 100 /* Clear UNF bit */ 101 timer_status = ctrl_inw(TMU0_TCR); 102 timer_status &= ~0x100; 103 ctrl_outw(timer_status, TMU0_TCR); 104 105 evt->event_handler(evt); 106 107 return IRQ_HANDLED; 108} 109 110static struct irqaction tmu0_irq = { 111 .name = "periodic timer", 112 .handler = tmu_timer_interrupt, 113 .flags = IRQF_DISABLED | IRQF_TIMER | IRQF_IRQPOLL, 114 .mask = CPU_MASK_NONE, 115}; 116 117static void tmu0_clk_init(struct clk *clk) 118{ 119 u8 divisor = TMU_TCR_INIT & 0x7; 120 ctrl_outw(TMU_TCR_INIT, TMU0_TCR); 121 clk->rate = clk->parent->rate / (4 << (divisor << 1)); 122} 123 124static void tmu0_clk_recalc(struct clk *clk) 125{ 126 u8 divisor = ctrl_inw(TMU0_TCR) & 0x7; 127 clk->rate = clk->parent->rate / (4 << (divisor << 1)); 128} 129 130static struct clk_ops tmu0_clk_ops = { 131 .init = tmu0_clk_init, 132 .recalc = tmu0_clk_recalc, 133}; 134 135static struct clk tmu0_clk = { 136 .name = "tmu0_clk", 137 .ops = &tmu0_clk_ops, 138}; 139 140static void tmu1_clk_init(struct clk *clk) 141{ 142 u8 divisor = TMU_TCR_INIT & 0x7; 143 ctrl_outw(divisor, TMU1_TCR); 144 clk->rate = clk->parent->rate / (4 << (divisor << 1)); 145} 146 147static void tmu1_clk_recalc(struct clk *clk) 148{ 149 u8 divisor = ctrl_inw(TMU1_TCR) & 0x7; 150 clk->rate = clk->parent->rate / (4 << (divisor << 1)); 151} 152 153static struct clk_ops tmu1_clk_ops = { 154 .init = tmu1_clk_init, 155 .recalc = tmu1_clk_recalc, 156}; 157 158static struct clk tmu1_clk = { 159 .name = "tmu1_clk", 160 .ops = &tmu1_clk_ops, 161}; 162 163static int tmu_timer_init(void) 164{ 165 unsigned long interval; 166 unsigned long frequency; 167 168 setup_irq(CONFIG_SH_TIMER_IRQ, &tmu0_irq); 169 170 tmu0_clk.parent = clk_get(NULL, "module_clk"); 171 tmu1_clk.parent = clk_get(NULL, "module_clk"); 172 173 tmu_timer_stop(); 174 175#if !defined(CONFIG_CPU_SUBTYPE_SH7300) && !defined(CONFIG_CPU_SUBTYPE_SH7760) && \ 176 !defined(CONFIG_CPU_SUBTYPE_SH7785) 177 ctrl_outb(TMU_TOCR_INIT, TMU_TOCR); 178#endif 179 180 clk_register(&tmu0_clk); 181 clk_register(&tmu1_clk); 182 clk_enable(&tmu0_clk); 183 clk_enable(&tmu1_clk); 184 185 frequency = clk_get_rate(&tmu0_clk); 186 interval = (frequency + HZ / 2) / HZ; 187 188 sh_hpt_frequency = clk_get_rate(&tmu1_clk); 189 ctrl_outl(~0, TMU1_TCNT); 190 ctrl_outl(~0, TMU1_TCOR); 191 192 tmu0_timer_set_interval(interval, 1); 193 194 tmu0_clockevent.mult = div_sc(frequency, NSEC_PER_SEC, 195 tmu0_clockevent.shift); 196 tmu0_clockevent.max_delta_ns = 197 clockevent_delta2ns(-1, &tmu0_clockevent); 198 tmu0_clockevent.min_delta_ns = 199 clockevent_delta2ns(1, &tmu0_clockevent); 200 201 tmu0_clockevent.cpumask = cpumask_of_cpu(0); 202 203 clockevents_register_device(&tmu0_clockevent); 204 205 return 0; 206} 207 208struct sys_timer_ops tmu_timer_ops = { 209 .init = tmu_timer_init, 210 .start = tmu_timer_start, 211 .stop = tmu_timer_stop, 212 .read = tmu_timer_read, 213}; 214 215struct sys_timer tmu_timer = { 216 .name = "tmu", 217 .ops = &tmu_timer_ops, 218}; 219