162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Copyright (C) 1999, 2000, 05, 06 Ralf Baechle (ralf@linux-mips.org)
462306a36Sopenharmony_ci * Copyright (C) 1999, 2000 Silicon Graphics, Inc.
562306a36Sopenharmony_ci */
662306a36Sopenharmony_ci#include <linux/bcd.h>
762306a36Sopenharmony_ci#include <linux/clockchips.h>
862306a36Sopenharmony_ci#include <linux/init.h>
962306a36Sopenharmony_ci#include <linux/kernel.h>
1062306a36Sopenharmony_ci#include <linux/sched.h>
1162306a36Sopenharmony_ci#include <linux/sched_clock.h>
1262306a36Sopenharmony_ci#include <linux/interrupt.h>
1362306a36Sopenharmony_ci#include <linux/kernel_stat.h>
1462306a36Sopenharmony_ci#include <linux/param.h>
1562306a36Sopenharmony_ci#include <linux/smp.h>
1662306a36Sopenharmony_ci#include <linux/time.h>
1762306a36Sopenharmony_ci#include <linux/timex.h>
1862306a36Sopenharmony_ci#include <linux/mm.h>
1962306a36Sopenharmony_ci#include <linux/platform_device.h>
2062306a36Sopenharmony_ci
2162306a36Sopenharmony_ci#include <asm/time.h>
2262306a36Sopenharmony_ci#include <asm/sgialib.h>
2362306a36Sopenharmony_ci#include <asm/sn/klconfig.h>
2462306a36Sopenharmony_ci#include <asm/sn/arch.h>
2562306a36Sopenharmony_ci#include <asm/sn/addrs.h>
2662306a36Sopenharmony_ci#include <asm/sn/agent.h>
2762306a36Sopenharmony_ci
2862306a36Sopenharmony_ci#include "ip27-common.h"
2962306a36Sopenharmony_ci
3062306a36Sopenharmony_cistatic int rt_next_event(unsigned long delta, struct clock_event_device *evt)
3162306a36Sopenharmony_ci{
3262306a36Sopenharmony_ci	unsigned int cpu = smp_processor_id();
3362306a36Sopenharmony_ci	int slice = cputoslice(cpu);
3462306a36Sopenharmony_ci	unsigned long cnt;
3562306a36Sopenharmony_ci
3662306a36Sopenharmony_ci	cnt = LOCAL_HUB_L(PI_RT_COUNT);
3762306a36Sopenharmony_ci	cnt += delta;
3862306a36Sopenharmony_ci	LOCAL_HUB_S(PI_RT_COMPARE_A + PI_COUNT_OFFSET * slice, cnt);
3962306a36Sopenharmony_ci
4062306a36Sopenharmony_ci	return LOCAL_HUB_L(PI_RT_COUNT) >= cnt ? -ETIME : 0;
4162306a36Sopenharmony_ci}
4262306a36Sopenharmony_ci
4362306a36Sopenharmony_cistatic DEFINE_PER_CPU(struct clock_event_device, hub_rt_clockevent);
4462306a36Sopenharmony_cistatic DEFINE_PER_CPU(char [11], hub_rt_name);
4562306a36Sopenharmony_ci
4662306a36Sopenharmony_cistatic irqreturn_t hub_rt_counter_handler(int irq, void *dev_id)
4762306a36Sopenharmony_ci{
4862306a36Sopenharmony_ci	unsigned int cpu = smp_processor_id();
4962306a36Sopenharmony_ci	struct clock_event_device *cd = &per_cpu(hub_rt_clockevent, cpu);
5062306a36Sopenharmony_ci	int slice = cputoslice(cpu);
5162306a36Sopenharmony_ci
5262306a36Sopenharmony_ci	/*
5362306a36Sopenharmony_ci	 * Ack
5462306a36Sopenharmony_ci	 */
5562306a36Sopenharmony_ci	LOCAL_HUB_S(PI_RT_PEND_A + PI_COUNT_OFFSET * slice, 0);
5662306a36Sopenharmony_ci	cd->event_handler(cd);
5762306a36Sopenharmony_ci
5862306a36Sopenharmony_ci	return IRQ_HANDLED;
5962306a36Sopenharmony_ci}
6062306a36Sopenharmony_ci
6162306a36Sopenharmony_cistruct irqaction hub_rt_irqaction = {
6262306a36Sopenharmony_ci	.handler	= hub_rt_counter_handler,
6362306a36Sopenharmony_ci	.percpu_dev_id	= &hub_rt_clockevent,
6462306a36Sopenharmony_ci	.flags		= IRQF_PERCPU | IRQF_TIMER,
6562306a36Sopenharmony_ci	.name		= "hub-rt",
6662306a36Sopenharmony_ci};
6762306a36Sopenharmony_ci
6862306a36Sopenharmony_ci/*
6962306a36Sopenharmony_ci * This is a hack; we really need to figure these values out dynamically
7062306a36Sopenharmony_ci *
7162306a36Sopenharmony_ci * Since 800 ns works very well with various HUB frequencies, such as
7262306a36Sopenharmony_ci * 360, 380, 390 and 400 MHZ, we use 800 ns rtc cycle time.
7362306a36Sopenharmony_ci *
7462306a36Sopenharmony_ci * Ralf: which clock rate is used to feed the counter?
7562306a36Sopenharmony_ci */
7662306a36Sopenharmony_ci#define NSEC_PER_CYCLE		800
7762306a36Sopenharmony_ci#define CYCLES_PER_SEC		(NSEC_PER_SEC / NSEC_PER_CYCLE)
7862306a36Sopenharmony_ci
7962306a36Sopenharmony_civoid hub_rt_clock_event_init(void)
8062306a36Sopenharmony_ci{
8162306a36Sopenharmony_ci	unsigned int cpu = smp_processor_id();
8262306a36Sopenharmony_ci	struct clock_event_device *cd = &per_cpu(hub_rt_clockevent, cpu);
8362306a36Sopenharmony_ci	unsigned char *name = per_cpu(hub_rt_name, cpu);
8462306a36Sopenharmony_ci
8562306a36Sopenharmony_ci	sprintf(name, "hub-rt %d", cpu);
8662306a36Sopenharmony_ci	cd->name		= name;
8762306a36Sopenharmony_ci	cd->features		= CLOCK_EVT_FEAT_ONESHOT;
8862306a36Sopenharmony_ci	clockevent_set_clock(cd, CYCLES_PER_SEC);
8962306a36Sopenharmony_ci	cd->max_delta_ns	= clockevent_delta2ns(0xfffffffffffff, cd);
9062306a36Sopenharmony_ci	cd->max_delta_ticks	= 0xfffffffffffff;
9162306a36Sopenharmony_ci	cd->min_delta_ns	= clockevent_delta2ns(0x300, cd);
9262306a36Sopenharmony_ci	cd->min_delta_ticks	= 0x300;
9362306a36Sopenharmony_ci	cd->rating		= 200;
9462306a36Sopenharmony_ci	cd->irq			= IP27_RT_TIMER_IRQ;
9562306a36Sopenharmony_ci	cd->cpumask		= cpumask_of(cpu);
9662306a36Sopenharmony_ci	cd->set_next_event	= rt_next_event;
9762306a36Sopenharmony_ci	clockevents_register_device(cd);
9862306a36Sopenharmony_ci
9962306a36Sopenharmony_ci	enable_percpu_irq(IP27_RT_TIMER_IRQ, IRQ_TYPE_NONE);
10062306a36Sopenharmony_ci}
10162306a36Sopenharmony_ci
10262306a36Sopenharmony_cistatic void __init hub_rt_clock_event_global_init(void)
10362306a36Sopenharmony_ci{
10462306a36Sopenharmony_ci	irq_set_handler(IP27_RT_TIMER_IRQ, handle_percpu_devid_irq);
10562306a36Sopenharmony_ci	irq_set_percpu_devid(IP27_RT_TIMER_IRQ);
10662306a36Sopenharmony_ci	setup_percpu_irq(IP27_RT_TIMER_IRQ, &hub_rt_irqaction);
10762306a36Sopenharmony_ci}
10862306a36Sopenharmony_ci
10962306a36Sopenharmony_cistatic u64 hub_rt_read(struct clocksource *cs)
11062306a36Sopenharmony_ci{
11162306a36Sopenharmony_ci	return REMOTE_HUB_L(cputonasid(0), PI_RT_COUNT);
11262306a36Sopenharmony_ci}
11362306a36Sopenharmony_ci
11462306a36Sopenharmony_cistruct clocksource hub_rt_clocksource = {
11562306a36Sopenharmony_ci	.name	= "HUB-RT",
11662306a36Sopenharmony_ci	.rating = 200,
11762306a36Sopenharmony_ci	.read	= hub_rt_read,
11862306a36Sopenharmony_ci	.mask	= CLOCKSOURCE_MASK(52),
11962306a36Sopenharmony_ci	.flags	= CLOCK_SOURCE_IS_CONTINUOUS,
12062306a36Sopenharmony_ci};
12162306a36Sopenharmony_ci
12262306a36Sopenharmony_cistatic u64 notrace hub_rt_read_sched_clock(void)
12362306a36Sopenharmony_ci{
12462306a36Sopenharmony_ci	return REMOTE_HUB_L(cputonasid(0), PI_RT_COUNT);
12562306a36Sopenharmony_ci}
12662306a36Sopenharmony_ci
12762306a36Sopenharmony_cistatic void __init hub_rt_clocksource_init(void)
12862306a36Sopenharmony_ci{
12962306a36Sopenharmony_ci	struct clocksource *cs = &hub_rt_clocksource;
13062306a36Sopenharmony_ci
13162306a36Sopenharmony_ci	clocksource_register_hz(cs, CYCLES_PER_SEC);
13262306a36Sopenharmony_ci
13362306a36Sopenharmony_ci	sched_clock_register(hub_rt_read_sched_clock, 52, CYCLES_PER_SEC);
13462306a36Sopenharmony_ci}
13562306a36Sopenharmony_ci
13662306a36Sopenharmony_civoid __init plat_time_init(void)
13762306a36Sopenharmony_ci{
13862306a36Sopenharmony_ci	hub_rt_clocksource_init();
13962306a36Sopenharmony_ci	hub_rt_clock_event_global_init();
14062306a36Sopenharmony_ci	hub_rt_clock_event_init();
14162306a36Sopenharmony_ci}
14262306a36Sopenharmony_ci
14362306a36Sopenharmony_civoid hub_rtc_init(nasid_t nasid)
14462306a36Sopenharmony_ci{
14562306a36Sopenharmony_ci
14662306a36Sopenharmony_ci	/*
14762306a36Sopenharmony_ci	 * We only need to initialize the current node.
14862306a36Sopenharmony_ci	 * If this is not the current node then it is a cpuless
14962306a36Sopenharmony_ci	 * node and timeouts will not happen there.
15062306a36Sopenharmony_ci	 */
15162306a36Sopenharmony_ci	if (get_nasid() == nasid) {
15262306a36Sopenharmony_ci		LOCAL_HUB_S(PI_RT_EN_A, 1);
15362306a36Sopenharmony_ci		LOCAL_HUB_S(PI_RT_EN_B, 1);
15462306a36Sopenharmony_ci		LOCAL_HUB_S(PI_PROF_EN_A, 0);
15562306a36Sopenharmony_ci		LOCAL_HUB_S(PI_PROF_EN_B, 0);
15662306a36Sopenharmony_ci		LOCAL_HUB_S(PI_RT_COUNT, 0);
15762306a36Sopenharmony_ci		LOCAL_HUB_S(PI_RT_PEND_A, 0);
15862306a36Sopenharmony_ci		LOCAL_HUB_S(PI_RT_PEND_B, 0);
15962306a36Sopenharmony_ci	}
16062306a36Sopenharmony_ci}
161