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