xref: /linux/arch/mips/sgi-ip27/ip27-timer.c (revision 8be98d2f2a0a262f8bf8a0bc1fdf522b3c7aab17)
1b2441318SGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0
21da177e4SLinus Torvalds /*
3*ee5e12e7SLukas Bulwahn  * Copyright (C) 1999, 2000, 05, 06 Ralf Baechle (ralf@linux-mips.org)
4*ee5e12e7SLukas Bulwahn  * Copyright (C) 1999, 2000 Silicon Graphics, Inc.
51da177e4SLinus Torvalds  */
61da177e4SLinus Torvalds #include <linux/bcd.h>
7e887b245SRalf Baechle #include <linux/clockchips.h>
81da177e4SLinus Torvalds #include <linux/init.h>
91da177e4SLinus Torvalds #include <linux/kernel.h>
101da177e4SLinus Torvalds #include <linux/sched.h>
11c41cef36SDeng-Cheng Zhu #include <linux/sched_clock.h>
121da177e4SLinus Torvalds #include <linux/interrupt.h>
131da177e4SLinus Torvalds #include <linux/kernel_stat.h>
141da177e4SLinus Torvalds #include <linux/param.h>
15631330f5SRalf Baechle #include <linux/smp.h>
161da177e4SLinus Torvalds #include <linux/time.h>
171da177e4SLinus Torvalds #include <linux/timex.h>
181da177e4SLinus Torvalds #include <linux/mm.h>
193ec066cdSThomas Bogendoerfer #include <linux/platform_device.h>
201da177e4SLinus Torvalds 
211da177e4SLinus Torvalds #include <asm/time.h>
221da177e4SLinus Torvalds #include <asm/sgialib.h>
231da177e4SLinus Torvalds #include <asm/sn/klconfig.h>
241da177e4SLinus Torvalds #include <asm/sn/arch.h>
251da177e4SLinus Torvalds #include <asm/sn/addrs.h>
26b78e9d63SThomas Bogendoerfer #include <asm/sn/agent.h>
279d0aaf98SThomas Bogendoerfer 
289d0aaf98SThomas Bogendoerfer #include "ip27-common.h"
291da177e4SLinus Torvalds 
rt_next_event(unsigned long delta,struct clock_event_device * evt)3006d428d7SRalf Baechle static int rt_next_event(unsigned long delta, struct clock_event_device *evt)
3106d428d7SRalf Baechle {
3206d428d7SRalf Baechle 	unsigned int cpu = smp_processor_id();
33c8925297SRalf Baechle 	int slice = cputoslice(cpu);
3406d428d7SRalf Baechle 	unsigned long cnt;
3506d428d7SRalf Baechle 
3606d428d7SRalf Baechle 	cnt = LOCAL_HUB_L(PI_RT_COUNT);
3706d428d7SRalf Baechle 	cnt += delta;
38725d7b36SRalf Baechle 	LOCAL_HUB_S(PI_RT_COMPARE_A + PI_COUNT_OFFSET * slice, cnt);
3906d428d7SRalf Baechle 
4006d428d7SRalf Baechle 	return LOCAL_HUB_L(PI_RT_COUNT) >= cnt ? -ETIME : 0;
4106d428d7SRalf Baechle }
4206d428d7SRalf Baechle 
43b32bb803SThomas Bogendoerfer static DEFINE_PER_CPU(struct clock_event_device, hub_rt_clockevent);
44b32bb803SThomas Bogendoerfer static DEFINE_PER_CPU(char [11], hub_rt_name);
45b32bb803SThomas Bogendoerfer 
hub_rt_counter_handler(int irq,void * dev_id)4606d428d7SRalf Baechle static irqreturn_t hub_rt_counter_handler(int irq, void *dev_id)
47e887b245SRalf Baechle {
48e887b245SRalf Baechle 	unsigned int cpu = smp_processor_id();
49b32bb803SThomas Bogendoerfer 	struct clock_event_device *cd = &per_cpu(hub_rt_clockevent, cpu);
50725d7b36SRalf Baechle 	int slice = cputoslice(cpu);
51e887b245SRalf Baechle 
52725d7b36SRalf Baechle 	/*
53725d7b36SRalf Baechle 	 * Ack
54725d7b36SRalf Baechle 	 */
55c8925297SRalf Baechle 	LOCAL_HUB_S(PI_RT_PEND_A + PI_COUNT_OFFSET * slice, 0);
56e887b245SRalf Baechle 	cd->event_handler(cd);
57e887b245SRalf Baechle 
58e887b245SRalf Baechle 	return IRQ_HANDLED;
59e887b245SRalf Baechle }
60e887b245SRalf Baechle 
6106d428d7SRalf Baechle struct irqaction hub_rt_irqaction = {
6206d428d7SRalf Baechle 	.handler	= hub_rt_counter_handler,
6369a07a41SThomas Bogendoerfer 	.percpu_dev_id	= &hub_rt_clockevent,
648b5690f8SYong Zhang 	.flags		= IRQF_PERCPU | IRQF_TIMER,
6506d428d7SRalf Baechle 	.name		= "hub-rt",
663c009442SRalf Baechle };
673c009442SRalf Baechle 
68e887b245SRalf Baechle /*
69e887b245SRalf Baechle  * This is a hack; we really need to figure these values out dynamically
70e887b245SRalf Baechle  *
71e887b245SRalf Baechle  * Since 800 ns works very well with various HUB frequencies, such as
72e887b245SRalf Baechle  * 360, 380, 390 and 400 MHZ, we use 800 ns rtc cycle time.
73e887b245SRalf Baechle  *
74e887b245SRalf Baechle  * Ralf: which clock rate is used to feed the counter?
75e887b245SRalf Baechle  */
76e887b245SRalf Baechle #define NSEC_PER_CYCLE		800
77e887b245SRalf Baechle #define CYCLES_PER_SEC		(NSEC_PER_SEC / NSEC_PER_CYCLE)
783c009442SRalf Baechle 
hub_rt_clock_event_init(void)79078a55fcSPaul Gortmaker void hub_rt_clock_event_init(void)
80e887b245SRalf Baechle {
81e887b245SRalf Baechle 	unsigned int cpu = smp_processor_id();
8206d428d7SRalf Baechle 	struct clock_event_device *cd = &per_cpu(hub_rt_clockevent, cpu);
8306d428d7SRalf Baechle 	unsigned char *name = per_cpu(hub_rt_name, cpu);
84e887b245SRalf Baechle 
8506d428d7SRalf Baechle 	sprintf(name, "hub-rt %d", cpu);
86b32bb803SThomas Bogendoerfer 	cd->name		= name;
87b32bb803SThomas Bogendoerfer 	cd->features		= CLOCK_EVT_FEAT_ONESHOT;
8806d428d7SRalf Baechle 	clockevent_set_clock(cd, CYCLES_PER_SEC);
8906d428d7SRalf Baechle 	cd->max_delta_ns	= clockevent_delta2ns(0xfffffffffffff, cd);
90e4db9253SNicolai Stange 	cd->max_delta_ticks	= 0xfffffffffffff;
9106d428d7SRalf Baechle 	cd->min_delta_ns	= clockevent_delta2ns(0x300, cd);
92e4db9253SNicolai Stange 	cd->min_delta_ticks	= 0x300;
93b32bb803SThomas Bogendoerfer 	cd->rating		= 200;
9469a07a41SThomas Bogendoerfer 	cd->irq			= IP27_RT_TIMER_IRQ;
95320ab2b0SRusty Russell 	cd->cpumask		= cpumask_of(cpu);
96b32bb803SThomas Bogendoerfer 	cd->set_next_event	= rt_next_event;
97e887b245SRalf Baechle 	clockevents_register_device(cd);
9869a07a41SThomas Bogendoerfer 
9969a07a41SThomas Bogendoerfer 	enable_percpu_irq(IP27_RT_TIMER_IRQ, IRQ_TYPE_NONE);
10006d428d7SRalf Baechle }
10106d428d7SRalf Baechle 
hub_rt_clock_event_global_init(void)10206d428d7SRalf Baechle static void __init hub_rt_clock_event_global_init(void)
10306d428d7SRalf Baechle {
10469a07a41SThomas Bogendoerfer 	irq_set_handler(IP27_RT_TIMER_IRQ, handle_percpu_devid_irq);
10569a07a41SThomas Bogendoerfer 	irq_set_percpu_devid(IP27_RT_TIMER_IRQ);
10669a07a41SThomas Bogendoerfer 	setup_percpu_irq(IP27_RT_TIMER_IRQ, &hub_rt_irqaction);
1071da177e4SLinus Torvalds }
1081da177e4SLinus Torvalds 
hub_rt_read(struct clocksource * cs)109a5a1d1c2SThomas Gleixner static u64 hub_rt_read(struct clocksource *cs)
11016b7b2acSAtsushi Nemoto {
11116b7b2acSAtsushi Nemoto 	return REMOTE_HUB_L(cputonasid(0), PI_RT_COUNT);
11216b7b2acSAtsushi Nemoto }
11316b7b2acSAtsushi Nemoto 
11406d428d7SRalf Baechle struct clocksource hub_rt_clocksource = {
115e887b245SRalf Baechle 	.name	= "HUB-RT",
11687b2335dSRalf Baechle 	.rating = 200,
11787b2335dSRalf Baechle 	.read	= hub_rt_read,
11887b2335dSRalf Baechle 	.mask	= CLOCKSOURCE_MASK(52),
11987b2335dSRalf Baechle 	.flags	= CLOCK_SOURCE_IS_CONTINUOUS,
12087b2335dSRalf Baechle };
12187b2335dSRalf Baechle 
hub_rt_read_sched_clock(void)122c41cef36SDeng-Cheng Zhu static u64 notrace hub_rt_read_sched_clock(void)
123c41cef36SDeng-Cheng Zhu {
124c41cef36SDeng-Cheng Zhu 	return REMOTE_HUB_L(cputonasid(0), PI_RT_COUNT);
125c41cef36SDeng-Cheng Zhu }
126c41cef36SDeng-Cheng Zhu 
hub_rt_clocksource_init(void)12706d428d7SRalf Baechle static void __init hub_rt_clocksource_init(void)
1281da177e4SLinus Torvalds {
12906d428d7SRalf Baechle 	struct clocksource *cs = &hub_rt_clocksource;
13006d428d7SRalf Baechle 
13175c4fd8cSJohn Stultz 	clocksource_register_hz(cs, CYCLES_PER_SEC);
132c41cef36SDeng-Cheng Zhu 
133c41cef36SDeng-Cheng Zhu 	sched_clock_register(hub_rt_read_sched_clock, 52, CYCLES_PER_SEC);
1341da177e4SLinus Torvalds }
1351da177e4SLinus Torvalds 
plat_time_init(void)136e887b245SRalf Baechle void __init plat_time_init(void)
137e887b245SRalf Baechle {
13806d428d7SRalf Baechle 	hub_rt_clocksource_init();
13906d428d7SRalf Baechle 	hub_rt_clock_event_global_init();
140b32bb803SThomas Bogendoerfer 	hub_rt_clock_event_init();
141e887b245SRalf Baechle }
142e887b245SRalf Baechle 
hub_rtc_init(nasid_t nasid)1434bf841ebSThomas Bogendoerfer void hub_rtc_init(nasid_t nasid)
1441da177e4SLinus Torvalds {
1453ec066cdSThomas Bogendoerfer 
1461da177e4SLinus Torvalds 	/*
1471da177e4SLinus Torvalds 	 * We only need to initialize the current node.
1481da177e4SLinus Torvalds 	 * If this is not the current node then it is a cpuless
1491da177e4SLinus Torvalds 	 * node and timeouts will not happen there.
1501da177e4SLinus Torvalds 	 */
1514bf841ebSThomas Bogendoerfer 	if (get_nasid() == nasid) {
1521da177e4SLinus Torvalds 		LOCAL_HUB_S(PI_RT_EN_A, 1);
1531da177e4SLinus Torvalds 		LOCAL_HUB_S(PI_RT_EN_B, 1);
1541da177e4SLinus Torvalds 		LOCAL_HUB_S(PI_PROF_EN_A, 0);
1551da177e4SLinus Torvalds 		LOCAL_HUB_S(PI_PROF_EN_B, 0);
1561da177e4SLinus Torvalds 		LOCAL_HUB_S(PI_RT_COUNT, 0);
1571da177e4SLinus Torvalds 		LOCAL_HUB_S(PI_RT_PEND_A, 0);
1581da177e4SLinus Torvalds 		LOCAL_HUB_S(PI_RT_PEND_B, 0);
1591da177e4SLinus Torvalds 	}
1601da177e4SLinus Torvalds }
161