xref: /linux/arch/mips/sgi-ip27/ip27-timer.c (revision f8aeb85f158fa98f8c2c9a24effb00adc06c0e85)
11da177e4SLinus Torvalds /*
254d0a216SRalf Baechle  * Copytight (C) 1999, 2000, 05, 06 Ralf Baechle (ralf@linux-mips.org)
31da177e4SLinus Torvalds  * Copytight (C) 1999, 2000 Silicon Graphics, Inc.
41da177e4SLinus Torvalds  */
51da177e4SLinus Torvalds #include <linux/bcd.h>
61da177e4SLinus Torvalds #include <linux/init.h>
71da177e4SLinus Torvalds #include <linux/kernel.h>
81da177e4SLinus Torvalds #include <linux/sched.h>
91da177e4SLinus Torvalds #include <linux/interrupt.h>
101da177e4SLinus Torvalds #include <linux/kernel_stat.h>
111da177e4SLinus Torvalds #include <linux/param.h>
121da177e4SLinus Torvalds #include <linux/time.h>
131da177e4SLinus Torvalds #include <linux/timex.h>
141da177e4SLinus Torvalds #include <linux/mm.h>
151da177e4SLinus Torvalds 
161da177e4SLinus Torvalds #include <asm/time.h>
171da177e4SLinus Torvalds #include <asm/pgtable.h>
181da177e4SLinus Torvalds #include <asm/sgialib.h>
191da177e4SLinus Torvalds #include <asm/sn/ioc3.h>
201da177e4SLinus Torvalds #include <asm/m48t35.h>
211da177e4SLinus Torvalds #include <asm/sn/klconfig.h>
221da177e4SLinus Torvalds #include <asm/sn/arch.h>
231da177e4SLinus Torvalds #include <asm/sn/addrs.h>
241da177e4SLinus Torvalds #include <asm/sn/sn_private.h>
251da177e4SLinus Torvalds #include <asm/sn/sn0/ip27.h>
261da177e4SLinus Torvalds #include <asm/sn/sn0/hub.h>
271da177e4SLinus Torvalds 
281da177e4SLinus Torvalds /*
291da177e4SLinus Torvalds  * This is a hack; we really need to figure these values out dynamically
301da177e4SLinus Torvalds  *
311da177e4SLinus Torvalds  * Since 800 ns works very well with various HUB frequencies, such as
321da177e4SLinus Torvalds  * 360, 380, 390 and 400 MHZ, we use 800 ns rtc cycle time.
331da177e4SLinus Torvalds  *
341da177e4SLinus Torvalds  * Ralf: which clock rate is used to feed the counter?
351da177e4SLinus Torvalds  */
361da177e4SLinus Torvalds #define NSEC_PER_CYCLE		800
371da177e4SLinus Torvalds #define CYCLES_PER_SEC		(NSEC_PER_SEC/NSEC_PER_CYCLE)
381da177e4SLinus Torvalds #define CYCLES_PER_JIFFY	(CYCLES_PER_SEC/HZ)
391da177e4SLinus Torvalds 
401da177e4SLinus Torvalds #define TICK_SIZE (tick_nsec / 1000)
411da177e4SLinus Torvalds 
421da177e4SLinus Torvalds static unsigned long ct_cur[NR_CPUS];	/* What counter should be at next timer irq */
431da177e4SLinus Torvalds static long last_rtc_update;		/* Last time the rtc clock got updated */
441da177e4SLinus Torvalds 
451da177e4SLinus Torvalds #if 0
461da177e4SLinus Torvalds static int set_rtc_mmss(unsigned long nowtime)
471da177e4SLinus Torvalds {
481da177e4SLinus Torvalds 	int retval = 0;
491da177e4SLinus Torvalds 	int real_seconds, real_minutes, cmos_minutes;
501da177e4SLinus Torvalds 	struct m48t35_rtc *rtc;
511da177e4SLinus Torvalds 	nasid_t nid;
521da177e4SLinus Torvalds 
531da177e4SLinus Torvalds 	nid = get_nasid();
541da177e4SLinus Torvalds 	rtc = (struct m48t35_rtc *)(KL_CONFIG_CH_CONS_INFO(nid)->memory_base +
551da177e4SLinus Torvalds 							IOC3_BYTEBUS_DEV0);
561da177e4SLinus Torvalds 
571da177e4SLinus Torvalds 	rtc->control |= M48T35_RTC_READ;
581da177e4SLinus Torvalds 	cmos_minutes = BCD2BIN(rtc->min);
591da177e4SLinus Torvalds 	rtc->control &= ~M48T35_RTC_READ;
601da177e4SLinus Torvalds 
611da177e4SLinus Torvalds 	/*
621da177e4SLinus Torvalds 	 * Since we're only adjusting minutes and seconds, don't interfere with
631da177e4SLinus Torvalds 	 * hour overflow. This avoids messing with unknown time zones but
641da177e4SLinus Torvalds 	 * requires your RTC not to be off by more than 15 minutes
651da177e4SLinus Torvalds 	 */
661da177e4SLinus Torvalds 	real_seconds = nowtime % 60;
671da177e4SLinus Torvalds 	real_minutes = nowtime / 60;
681da177e4SLinus Torvalds 	if (((abs(real_minutes - cmos_minutes) + 15)/30) & 1)
691da177e4SLinus Torvalds 		real_minutes += 30;	/* correct for half hour time zone */
701da177e4SLinus Torvalds 	real_minutes %= 60;
711da177e4SLinus Torvalds 
721da177e4SLinus Torvalds 	if (abs(real_minutes - cmos_minutes) < 30) {
731da177e4SLinus Torvalds 		real_seconds = BIN2BCD(real_seconds);
741da177e4SLinus Torvalds 		real_minutes = BIN2BCD(real_minutes);
751da177e4SLinus Torvalds 		rtc->control |= M48T35_RTC_SET;
761da177e4SLinus Torvalds 		rtc->sec = real_seconds;
771da177e4SLinus Torvalds 		rtc->min = real_minutes;
781da177e4SLinus Torvalds 		rtc->control &= ~M48T35_RTC_SET;
791da177e4SLinus Torvalds 	} else {
801da177e4SLinus Torvalds 		printk(KERN_WARNING
811da177e4SLinus Torvalds 		       "set_rtc_mmss: can't update from %d to %d\n",
821da177e4SLinus Torvalds 		       cmos_minutes, real_minutes);
831da177e4SLinus Torvalds 		retval = -1;
841da177e4SLinus Torvalds 	}
851da177e4SLinus Torvalds 
861da177e4SLinus Torvalds 	return retval;
871da177e4SLinus Torvalds }
881da177e4SLinus Torvalds #endif
891da177e4SLinus Torvalds 
903c009442SRalf Baechle static unsigned int rt_timer_irq;
913c009442SRalf Baechle 
92937a8015SRalf Baechle void ip27_rt_timer_interrupt(void)
931da177e4SLinus Torvalds {
941da177e4SLinus Torvalds 	int cpu = smp_processor_id();
951da177e4SLinus Torvalds 	int cpuA = cputoslice(cpu) == 0;
963c009442SRalf Baechle 	unsigned int irq = rt_timer_irq;
971da177e4SLinus Torvalds 
981da177e4SLinus Torvalds 	irq_enter();
991da177e4SLinus Torvalds 	write_seqlock(&xtime_lock);
1001da177e4SLinus Torvalds 
1011da177e4SLinus Torvalds again:
1021da177e4SLinus Torvalds 	LOCAL_HUB_S(cpuA ? PI_RT_PEND_A : PI_RT_PEND_B, 0);	/* Ack  */
1031da177e4SLinus Torvalds 	ct_cur[cpu] += CYCLES_PER_JIFFY;
1041da177e4SLinus Torvalds 	LOCAL_HUB_S(cpuA ? PI_RT_COMPARE_A : PI_RT_COMPARE_B, ct_cur[cpu]);
1051da177e4SLinus Torvalds 
1061da177e4SLinus Torvalds 	if (LOCAL_HUB_L(PI_RT_COUNT) >= ct_cur[cpu])
1071da177e4SLinus Torvalds 		goto again;
1081da177e4SLinus Torvalds 
1091da177e4SLinus Torvalds 	kstat_this_cpu.irqs[irq]++;		/* kstat only for bootcpu? */
1101da177e4SLinus Torvalds 
1111da177e4SLinus Torvalds 	if (cpu == 0)
1123171a030SAtsushi Nemoto 		do_timer(1);
1131da177e4SLinus Torvalds 
114937a8015SRalf Baechle 	update_process_times(user_mode(get_irq_regs()));
1151da177e4SLinus Torvalds 
1161da177e4SLinus Torvalds 	/*
1171da177e4SLinus Torvalds 	 * If we have an externally synchronized Linux clock, then update
1181da177e4SLinus Torvalds 	 * RTC clock accordingly every ~11 minutes. Set_rtc_mmss() has to be
1191da177e4SLinus Torvalds 	 * called as close as possible to when a second starts.
1201da177e4SLinus Torvalds 	 */
121b149ee22Sjohn stultz 	if (ntp_synced() &&
1221da177e4SLinus Torvalds 	    xtime.tv_sec > last_rtc_update + 660 &&
1231da177e4SLinus Torvalds 	    (xtime.tv_nsec / 1000) >= 500000 - ((unsigned) TICK_SIZE) / 2 &&
1241da177e4SLinus Torvalds 	    (xtime.tv_nsec / 1000) <= 500000 + ((unsigned) TICK_SIZE) / 2) {
125c0858d82SAtsushi Nemoto 		if (rtc_mips_set_time(xtime.tv_sec) == 0) {
1261da177e4SLinus Torvalds 			last_rtc_update = xtime.tv_sec;
1271da177e4SLinus Torvalds 		} else {
1281da177e4SLinus Torvalds 			last_rtc_update = xtime.tv_sec - 600;
1291da177e4SLinus Torvalds 			/* do it again in 60 s */
1301da177e4SLinus Torvalds 		}
1311da177e4SLinus Torvalds 	}
1321da177e4SLinus Torvalds 
1331da177e4SLinus Torvalds 	write_sequnlock(&xtime_lock);
1341da177e4SLinus Torvalds 	irq_exit();
1351da177e4SLinus Torvalds }
1361da177e4SLinus Torvalds 
1371da177e4SLinus Torvalds /* Includes for ioc3_init().  */
1381da177e4SLinus Torvalds #include <asm/sn/types.h>
1391da177e4SLinus Torvalds #include <asm/sn/sn0/addrs.h>
1401da177e4SLinus Torvalds #include <asm/sn/sn0/hubni.h>
1411da177e4SLinus Torvalds #include <asm/sn/sn0/hubio.h>
1421da177e4SLinus Torvalds #include <asm/pci/bridge.h>
1431da177e4SLinus Torvalds 
1441da177e4SLinus Torvalds static __init unsigned long get_m48t35_time(void)
1451da177e4SLinus Torvalds {
1461da177e4SLinus Torvalds         unsigned int year, month, date, hour, min, sec;
1471da177e4SLinus Torvalds 	struct m48t35_rtc *rtc;
1481da177e4SLinus Torvalds 	nasid_t nid;
1491da177e4SLinus Torvalds 
1501da177e4SLinus Torvalds 	nid = get_nasid();
1511da177e4SLinus Torvalds 	rtc = (struct m48t35_rtc *)(KL_CONFIG_CH_CONS_INFO(nid)->memory_base +
1521da177e4SLinus Torvalds 							IOC3_BYTEBUS_DEV0);
1531da177e4SLinus Torvalds 
1541da177e4SLinus Torvalds 	rtc->control |= M48T35_RTC_READ;
1551da177e4SLinus Torvalds 	sec = rtc->sec;
1561da177e4SLinus Torvalds 	min = rtc->min;
1571da177e4SLinus Torvalds 	hour = rtc->hour;
1581da177e4SLinus Torvalds 	date = rtc->date;
1591da177e4SLinus Torvalds 	month = rtc->month;
1601da177e4SLinus Torvalds 	year = rtc->year;
1611da177e4SLinus Torvalds 	rtc->control &= ~M48T35_RTC_READ;
1621da177e4SLinus Torvalds 
1631da177e4SLinus Torvalds         sec = BCD2BIN(sec);
1641da177e4SLinus Torvalds         min = BCD2BIN(min);
1651da177e4SLinus Torvalds         hour = BCD2BIN(hour);
1661da177e4SLinus Torvalds         date = BCD2BIN(date);
1671da177e4SLinus Torvalds         month = BCD2BIN(month);
1681da177e4SLinus Torvalds         year = BCD2BIN(year);
1691da177e4SLinus Torvalds 
1701da177e4SLinus Torvalds         year += 1970;
1711da177e4SLinus Torvalds 
1721da177e4SLinus Torvalds         return mktime(year, month, date, hour, min, sec);
1731da177e4SLinus Torvalds }
1741da177e4SLinus Torvalds 
1753c009442SRalf Baechle static void enable_rt_irq(unsigned int irq)
1763c009442SRalf Baechle {
1773c009442SRalf Baechle }
1783c009442SRalf Baechle 
1793c009442SRalf Baechle static void disable_rt_irq(unsigned int irq)
1803c009442SRalf Baechle {
1813c009442SRalf Baechle }
1823c009442SRalf Baechle 
18394dee171SRalf Baechle static struct irq_chip rt_irq_type = {
18470d21cdeSAtsushi Nemoto 	.name		= "SN HUB RT timer",
1851603b5acSAtsushi Nemoto 	.ack		= disable_rt_irq,
1861603b5acSAtsushi Nemoto 	.mask		= disable_rt_irq,
1871603b5acSAtsushi Nemoto 	.mask_ack	= disable_rt_irq,
1881603b5acSAtsushi Nemoto 	.unmask		= enable_rt_irq,
1891417836eSAtsushi Nemoto 	.eoi		= enable_rt_irq,
1903c009442SRalf Baechle };
1913c009442SRalf Baechle 
1923c009442SRalf Baechle static struct irqaction rt_irqaction = {
193*f8aeb85fSRalf Baechle 	.handler	= (irq_handler_t) ip27_rt_timer_interrupt,
194f40298fdSThomas Gleixner 	.flags		= IRQF_DISABLED,
1953c009442SRalf Baechle 	.mask		= CPU_MASK_NONE,
1963c009442SRalf Baechle 	.name		= "timer"
1973c009442SRalf Baechle };
1983c009442SRalf Baechle 
19954d0a216SRalf Baechle void __init plat_timer_setup(struct irqaction *irq)
2001da177e4SLinus Torvalds {
2013c009442SRalf Baechle 	int irqno  = allocate_irqno();
2023c009442SRalf Baechle 
2033c009442SRalf Baechle 	if (irqno < 0)
2043c009442SRalf Baechle 		panic("Can't allocate interrupt number for timer interrupt");
2053c009442SRalf Baechle 
2061417836eSAtsushi Nemoto 	set_irq_chip_and_handler(irqno, &rt_irq_type, handle_percpu_irq);
2073c009442SRalf Baechle 
2081da177e4SLinus Torvalds 	/* over-write the handler, we use our own way */
2091da177e4SLinus Torvalds 	irq->handler = no_action;
2101da177e4SLinus Torvalds 
2111da177e4SLinus Torvalds 	/* setup irqaction */
2123c009442SRalf Baechle 	irq_desc[irqno].status |= IRQ_PER_CPU;
2133c009442SRalf Baechle 
2143c009442SRalf Baechle 	rt_timer_irq = irqno;
215bf283630SRalf Baechle 	/*
216bf283630SRalf Baechle 	 * Only needed to get /proc/interrupt to display timer irq stats
217bf283630SRalf Baechle 	 */
218bf283630SRalf Baechle 	setup_irq(irqno, &rt_irqaction);
2191da177e4SLinus Torvalds }
2201da177e4SLinus Torvalds 
22100598560SAtsushi Nemoto static cycle_t ip27_hpt_read(void)
22216b7b2acSAtsushi Nemoto {
22316b7b2acSAtsushi Nemoto 	return REMOTE_HUB_L(cputonasid(0), PI_RT_COUNT);
22416b7b2acSAtsushi Nemoto }
22516b7b2acSAtsushi Nemoto 
2261da177e4SLinus Torvalds void __init ip27_time_init(void)
2271da177e4SLinus Torvalds {
22800598560SAtsushi Nemoto 	clocksource_mips.read = ip27_hpt_read;
22916b7b2acSAtsushi Nemoto 	mips_hpt_frequency = CYCLES_PER_SEC;
2301da177e4SLinus Torvalds 	xtime.tv_sec = get_m48t35_time();
2311da177e4SLinus Torvalds 	xtime.tv_nsec = 0;
2321da177e4SLinus Torvalds }
2331da177e4SLinus Torvalds 
2341da177e4SLinus Torvalds void __init cpu_time_init(void)
2351da177e4SLinus Torvalds {
2361da177e4SLinus Torvalds 	lboard_t *board;
2371da177e4SLinus Torvalds 	klcpu_t *cpu;
2381da177e4SLinus Torvalds 	int cpuid;
2391da177e4SLinus Torvalds 
2401da177e4SLinus Torvalds 	/* Don't use ARCS.  ARCS is fragile.  Klconfig is simple and sane.  */
2411da177e4SLinus Torvalds 	board = find_lboard(KL_CONFIG_INFO(get_nasid()), KLTYPE_IP27);
2421da177e4SLinus Torvalds 	if (!board)
2431da177e4SLinus Torvalds 		panic("Can't find board info for myself.");
2441da177e4SLinus Torvalds 
2451da177e4SLinus Torvalds 	cpuid = LOCAL_HUB_L(PI_CPU_NUM) ? IP27_CPU0_INDEX : IP27_CPU1_INDEX;
2461da177e4SLinus Torvalds 	cpu = (klcpu_t *) KLCF_COMP(board, cpuid);
2471da177e4SLinus Torvalds 	if (!cpu)
2481da177e4SLinus Torvalds 		panic("No information about myself?");
2491da177e4SLinus Torvalds 
2501da177e4SLinus Torvalds 	printk("CPU %d clock is %dMHz.\n", smp_processor_id(), cpu->cpu_speed);
2511da177e4SLinus Torvalds 
2521da177e4SLinus Torvalds 	set_c0_status(SRB_TIMOCLK);
2531da177e4SLinus Torvalds }
2541da177e4SLinus Torvalds 
2551da177e4SLinus Torvalds void __init hub_rtc_init(cnodeid_t cnode)
2561da177e4SLinus Torvalds {
2571da177e4SLinus Torvalds 	/*
2581da177e4SLinus Torvalds 	 * We only need to initialize the current node.
2591da177e4SLinus Torvalds 	 * If this is not the current node then it is a cpuless
2601da177e4SLinus Torvalds 	 * node and timeouts will not happen there.
2611da177e4SLinus Torvalds 	 */
2621da177e4SLinus Torvalds 	if (get_compact_nodeid() == cnode) {
2631da177e4SLinus Torvalds 		int cpu = smp_processor_id();
2641da177e4SLinus Torvalds 		LOCAL_HUB_S(PI_RT_EN_A, 1);
2651da177e4SLinus Torvalds 		LOCAL_HUB_S(PI_RT_EN_B, 1);
2661da177e4SLinus Torvalds 		LOCAL_HUB_S(PI_PROF_EN_A, 0);
2671da177e4SLinus Torvalds 		LOCAL_HUB_S(PI_PROF_EN_B, 0);
2681da177e4SLinus Torvalds 		ct_cur[cpu] = CYCLES_PER_JIFFY;
2691da177e4SLinus Torvalds 		LOCAL_HUB_S(PI_RT_COMPARE_A, ct_cur[cpu]);
2701da177e4SLinus Torvalds 		LOCAL_HUB_S(PI_RT_COUNT, 0);
2711da177e4SLinus Torvalds 		LOCAL_HUB_S(PI_RT_PEND_A, 0);
2721da177e4SLinus Torvalds 		LOCAL_HUB_S(PI_RT_COMPARE_B, ct_cur[cpu]);
2731da177e4SLinus Torvalds 		LOCAL_HUB_S(PI_RT_COUNT, 0);
2741da177e4SLinus Torvalds 		LOCAL_HUB_S(PI_RT_PEND_B, 0);
2751da177e4SLinus Torvalds 	}
2761da177e4SLinus Torvalds }
277