• Home
  • History
  • Annotate
  • Line#
  • Navigate
  • Raw
  • Download
  • only in /asuswrt-rt-n18u-9.0.0.4.380.2695/release/src-rt-6.x.4708/linux/linux-2.6.36/arch/mips/brcm-boards/bcm947xx/
1/*
2 * Copyright (C) 2013, Broadcom Corporation. All Rights Reserved.
3 *
4 * Permission to use, copy, modify, and/or distribute this software for any
5 * purpose with or without fee is hereby granted, provided that the above
6 * copyright notice and this permission notice appear in all copies.
7 *
8 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
11 * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
13 * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
14 * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15 *
16 * $Id: time.c,v 1.9 2009-07-17 06:23:12 $
17 */
18#include <linux/version.h>
19#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,36)
20#include <linux/config.h>
21#endif
22#include <linux/init.h>
23#include <linux/kernel.h>
24#include <linux/sched.h>
25#include <linux/serial_reg.h>
26#include <linux/interrupt.h>
27#include <asm/addrspace.h>
28#include <asm/io.h>
29#include <asm/time.h>
30
31#include <typedefs.h>
32#include <osl.h>
33#include <bcmutils.h>
34#include <bcmnvram.h>
35#include <hndsoc.h>
36#include <sbchipc.h>
37#include <siutils.h>
38#include <hndmips.h>
39#include <mipsinc.h>
40#include <hndcpu.h>
41#include <bcmdevs.h>
42
43/* Global SB handle */
44extern si_t *bcm947xx_sih;
45extern spinlock_t bcm947xx_sih_lock;
46
47/* Convenience */
48#define sih bcm947xx_sih
49#define sih_lock bcm947xx_sih_lock
50
51#define WATCHDOG_MIN	3000	/* milliseconds */
52extern int panic_timeout;
53extern int panic_on_oops;
54static int watchdog = 0;
55
56#ifndef	CONFIG_HWSIM
57static u8 *mcr = NULL;
58#endif /* CONFIG_HWSIM */
59
60static void __init
61bcm947xx_time_init(void)
62{
63	unsigned int hz;
64	char cn[8];
65
66	/*
67	 * Use deterministic values for initial counter interrupt
68	 * so that calibrate delay avoids encountering a counter wrap.
69	 */
70	write_c0_count(0);
71	write_c0_compare(0xffff);
72
73	if (!(hz = si_cpu_clock(sih)))
74		hz = 100000000;
75
76	bcm_chipname(sih->chip, cn, 8);
77	printk(KERN_INFO "CPU: BCM%s rev %d at %d MHz\n", cn, sih->chiprev,
78	       (hz + 500000) / 1000000);
79
80	/* Set MIPS counter frequency for fixed_rate_gettimeoffset() */
81	mips_hpt_frequency = hz / 2;
82
83	/* Set watchdog interval in ms */
84	watchdog = simple_strtoul(nvram_safe_get("watchdog"), NULL, 0);
85
86	/* Ensure at least WATCHDOG_MIN */
87	if ((watchdog > 0) && (watchdog < WATCHDOG_MIN))
88		watchdog = WATCHDOG_MIN;
89
90	/* Set panic timeout in seconds */
91	panic_timeout = watchdog / 1000;
92	panic_on_oops = watchdog / 1000;
93}
94
95#ifdef CONFIG_HND_BMIPS3300_PROF
96extern bool hndprofiling;
97#ifdef CONFIG_MIPS64
98typedef u_int64_t sbprof_pc;
99#else
100typedef u_int32_t sbprof_pc;
101#endif
102extern void sbprof_cpu_intr(sbprof_pc restartpc);
103#endif	/* CONFIG_HND_BMIPS3300_PROF */
104
105static irqreturn_t
106bcm947xx_timer_interrupt(int irq, void *dev_id)
107{
108#ifdef CONFIG_HND_BMIPS3300_PROF
109	/*
110	 * Are there any ExcCode or other mean(s) to determine what has caused
111	 * the timer interrupt? For now simply stop the normal timer proc if
112	 * count register is less than compare register.
113	 */
114	if (hndprofiling) {
115		sbprof_cpu_intr(read_c0_epc() +
116		                ((read_c0_cause() >> (CAUSEB_BD - 2)) & 4));
117		if (read_c0_count() < read_c0_compare())
118			return (IRQ_HANDLED);
119	}
120#endif	/* CONFIG_HND_BMIPS3300_PROF */
121
122#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,36)
123	/* Generic MIPS timer code */
124	timer_interrupt(irq, dev_id);
125#else	/* 2.6.36 and up */
126	{
127	/* There is no more a MIPS generic timer ISR */
128	struct clock_event_device *cd = dev_id;
129	BUG_ON( ! cd );
130	cd->event_handler(cd);
131	/* Clear Count/Compare Interrupt */
132	write_c0_compare(read_c0_count() + mips_hpt_frequency / HZ);
133	}
134#endif
135
136	/* Set the watchdog timer to reset after the specified number of ms */
137	if (watchdog > 0)
138		si_watchdog_ms(sih, watchdog);
139
140#ifdef	CONFIG_HWSIM
141	(*((int *)0xa0000f1c))++;
142#else
143	/* Blink one of the LEDs in the external UART */
144	if (mcr && !(jiffies % (HZ/2)))
145		writeb(readb(mcr) ^ UART_MCR_OUT2, mcr);
146#endif
147
148	return (IRQ_HANDLED);
149}
150
151
152#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,36)
153static void bcm947xx_clockevent_set_mode(enum clock_event_mode mode,
154	struct clock_event_device *cd)
155{
156	printk( KERN_CRIT "bcm947xx_clockevent_set_mode: %d\n", mode );
157	/* Need to add mode switch to support both
158	periodic and one-shot operation here */
159}
160#ifdef BRCM_TIMER_ONESHOT
161/* This is used in one-shot operation mode */
162static int bcm947xx_clockevent_set_next(unsigned long delta,
163	struct clock_event_device *cd)
164{
165        unsigned int cnt;
166        int res;
167
168	printk( KERN_CRIT "bcm947xx_clockevent_set_next: %#lx\n", delta );
169
170        cnt = read_c0_count();
171        cnt += delta;
172        write_c0_compare(cnt);
173        res = ((int)(read_c0_count() - cnt) >= 0) ? -ETIME : 0;
174        return res;
175}
176#endif
177
178struct clock_event_device bcm947xx_clockevent = {
179	.name		= "bcm947xx",
180	.features	= CLOCK_EVT_FEAT_PERIODIC,
181	.rating		= 300,
182	.irq		= 7,
183	.set_mode 	= bcm947xx_clockevent_set_mode,
184#ifdef BRCM_TIMER_ONESHOT
185	.set_next_event = bcm947xx_clockevent_set_next,
186#endif
187};
188#endif
189
190/* named initialization should work on earlier 2.6 too */
191static struct irqaction bcm947xx_timer_irqaction = {
192	.handler	= bcm947xx_timer_interrupt,
193	.flags		= IRQF_DISABLED | IRQF_TIMER,
194	.name		= "bcm947xx timer",
195	.dev_id		= &bcm947xx_clockevent,
196};
197
198#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,36)
199void __init plat_timer_setup(struct irqaction *irq)
200{
201	/* Enable the timer interrupt */
202	setup_irq(7, &bcm947xx_timer_irqaction);
203}
204#else
205void __init plat_time_init(void)
206{
207	struct clock_event_device *cd = &bcm947xx_clockevent;
208	const u8 irq = 7;
209
210	/* Initialize the timer */
211	bcm947xx_time_init();
212
213	cd->cpumask = cpumask_of(smp_processor_id());
214
215        clockevent_set_clock(cd, mips_hpt_frequency);
216#ifdef BRCM_TIMER_ONESHOT
217        /* Calculate the min / max delta */
218        cd->max_delta_ns        = clockevent_delta2ns(0x7fffffff, cd);
219        cd->min_delta_ns        = clockevent_delta2ns(0x300, cd);
220#endif
221	clockevents_register_device(cd);
222
223	/* Enable the timer interrupt */
224	setup_irq(irq, &bcm947xx_timer_irqaction);
225}
226#endif
227