timer-mtu2.c 4.6 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92
/*
 * arch/sh/kernel/timers/timer-mtu2.c - MTU2 Timer Support
 *
 *  Copyright (C) 2005  Paul Mundt
 *
 * Based off of arch/sh/kernel/timers/timer-tmu.c
 *
 * This file is subject to the terms and conditions of the GNU General Public
 * License.  See the file "COPYING" in the main directory of this archive
 * for more details.
 */
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/interrupt.h>
#include <linux/seqlock.h>
#include <asm/timer.h>
#include <asm/io.h>
#include <asm/irq.h>
#include <asm/clock.h>

/*
 * We use channel 1 for our lowly system timer. Channel 2 would be the other
 * likely candidate, but we leave it alone as it has higher divisors that
 * would be of more use to other more interesting applications.
 *
 * TODO: Presently we only implement a 16-bit single-channel system timer.
 * However, we can implement channel cascade if we go the overflow route and
 * get away with using 2 MTU2 channels as a 32-bit timer.
 */
#define MTU2_TSTR	0xfffe4280
#define MTU2_TCR_1	0xfffe4380
#define MTU2_TMDR_1	0xfffe4381
#define MTU2_TIOR_1	0xfffe4382
#define MTU2_TIER_1	0xfffe4384
#define MTU2_TSR_1	0xfffe4385
#define MTU2_TCNT_1	0xfffe4386	/* 16-bit counter */
#define MTU2_TGRA_1	0xfffe438a

#define STBCR3		0xfffe0408

#define MTU2_TSTR_CST1	(1 << 1)	/* Counter Start 1 */

#define MTU2_TSR_TGFA	(1 << 0)	/* GRA compare match */

#define MTU2_TIER_TGIEA	(1 << 0)	/* GRA compare match  interrupt enable */

#define MTU2_TCR_INIT	0x22

#define MTU2_TCR_CALIB  0x00

static unsigned long mtu2_timer_get_offset(void)
{
	int count;
	static int count_p = 0x7fff;	/* for the first call after boot */
	static unsigned long jiffies_p = 0;

	/*
	 * cache volatile jiffies temporarily; we have IRQs turned off.
	 */
	unsigned long jiffies_t;

	/* timer count may underflow right here */
	count = ctrl_inw(MTU2_TCNT_1);	/* read the latched count */

	jiffies_t = jiffies;

	/*
	 * avoiding timer inconsistencies (they are rare, but they happen)...
	 * there is one kind of problem that must be avoided here:
	 *  1. the timer counter underflows
	 */

	if (jiffies_t == jiffies_p) {
		if (count > count_p) {
			if (ctrl_inb(MTU2_TSR_1) & MTU2_TSR_TGFA) {
				count -= LATCH;
			} else {
				printk("%s (): hardware timer problem?\n",
				       __FUNCTION__);
			}
		}
	} else
		jiffies_p = jiffies_t;

	count_p = count;

	count = ((LATCH-1) - count) * TICK_SIZE;
	count = (count + LATCH/2) / LATCH;

	return count;
}

P
Paul Mundt 已提交
93
static irqreturn_t mtu2_timer_interrupt(int irq, void *dev_id)
94 95 96 97 98 99 100 101 102
{
	unsigned long timer_status;

	/* Clear TGFA bit */
	timer_status = ctrl_inb(MTU2_TSR_1);
	timer_status &= ~MTU2_TSR_TGFA;
	ctrl_outb(timer_status, MTU2_TSR_1);

	/* Do timer tick */
P
Paul Mundt 已提交
103
	handle_timer_tick();
104 105 106 107 108 109 110

	return IRQ_HANDLED;
}

static struct irqaction mtu2_irq = {
	.name		= "timer",
	.handler	= mtu2_timer_interrupt,
B
Bernhard Walle 已提交
111
	.flags		= IRQF_DISABLED | IRQF_TIMER | IRQF_IRQPOLL,
112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159
	.mask		= CPU_MASK_NONE,
};

static unsigned int divisors[] = { 1, 4, 16, 64, 1, 1, 256 };

static void mtu2_clk_init(struct clk *clk)
{
	u8 idx = MTU2_TCR_INIT & 0x7;

	clk->rate = clk->parent->rate / divisors[idx];
	/* Start TCNT counting */
	ctrl_outb(ctrl_inb(MTU2_TSTR) | MTU2_TSTR_CST1, MTU2_TSTR);

}

static void mtu2_clk_recalc(struct clk *clk)
{
	u8 idx = ctrl_inb(MTU2_TCR_1) & 0x7;
	clk->rate = clk->parent->rate / divisors[idx];
}

static struct clk_ops mtu2_clk_ops = {
	.init		= mtu2_clk_init,
	.recalc		= mtu2_clk_recalc,
};

static struct clk mtu2_clk1 = {
	.name		= "mtu2_clk1",
	.ops		= &mtu2_clk_ops,
};

static int mtu2_timer_start(void)
{
	ctrl_outb(ctrl_inb(MTU2_TSTR) | MTU2_TSTR_CST1, MTU2_TSTR);
	return 0;
}

static int mtu2_timer_stop(void)
{
	ctrl_outb(ctrl_inb(MTU2_TSTR) & ~MTU2_TSTR_CST1, MTU2_TSTR);
	return 0;
}

static int mtu2_timer_init(void)
{
	u8 tmp;
	unsigned long interval;

P
Paul Mundt 已提交
160
	setup_irq(CONFIG_SH_TIMER_IRQ, &mtu2_irq);
161

P
Paul Mundt 已提交
162
	mtu2_clk1.parent = clk_get(NULL, "module_clk");
163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189

	ctrl_outb(ctrl_inb(STBCR3) & (~0x20), STBCR3);

	/* Normal operation */
	ctrl_outb(0, MTU2_TMDR_1);
	ctrl_outb(MTU2_TCR_INIT, MTU2_TCR_1);
	ctrl_outb(0x01, MTU2_TIOR_1);

	/* Enable underflow interrupt */
	ctrl_outb(ctrl_inb(MTU2_TIER_1) | MTU2_TIER_TGIEA, MTU2_TIER_1);

	interval = CONFIG_SH_PCLK_FREQ / 16 / HZ;
	printk(KERN_INFO "Interval = %ld\n", interval);

	ctrl_outw(interval, MTU2_TGRA_1);
	ctrl_outw(0, MTU2_TCNT_1);

	clk_register(&mtu2_clk1);
	clk_enable(&mtu2_clk1);

	return 0;
}

struct sys_timer_ops mtu2_timer_ops = {
	.init		= mtu2_timer_init,
	.start		= mtu2_timer_start,
	.stop		= mtu2_timer_stop,
P
Paul Mundt 已提交
190
#ifndef CONFIG_GENERIC_TIME
191
	.get_offset	= mtu2_timer_get_offset,
P
Paul Mundt 已提交
192
#endif
193 194 195 196 197 198
};

struct sys_timer mtu2_timer = {
	.name	= "mtu2",
	.ops	= &mtu2_timer_ops,
};