xref: /kernel/linux/linux-6.6/arch/m68k/coldfire/pit.c (revision 62306a36)
162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/***************************************************************************/
362306a36Sopenharmony_ci
462306a36Sopenharmony_ci/*
562306a36Sopenharmony_ci *	pit.c -- Freescale ColdFire PIT timer. Currently this type of
662306a36Sopenharmony_ci *	         hardware timer only exists in the Freescale ColdFire
762306a36Sopenharmony_ci *		 5270/5271, 5282 and 5208 CPUs. No doubt newer ColdFire
862306a36Sopenharmony_ci *		 family members will probably use it too.
962306a36Sopenharmony_ci *
1062306a36Sopenharmony_ci *	Copyright (C) 1999-2008, Greg Ungerer (gerg@snapgear.com)
1162306a36Sopenharmony_ci *	Copyright (C) 2001-2004, SnapGear Inc. (www.snapgear.com)
1262306a36Sopenharmony_ci */
1362306a36Sopenharmony_ci
1462306a36Sopenharmony_ci/***************************************************************************/
1562306a36Sopenharmony_ci
1662306a36Sopenharmony_ci#include <linux/kernel.h>
1762306a36Sopenharmony_ci#include <linux/sched.h>
1862306a36Sopenharmony_ci#include <linux/param.h>
1962306a36Sopenharmony_ci#include <linux/init.h>
2062306a36Sopenharmony_ci#include <linux/interrupt.h>
2162306a36Sopenharmony_ci#include <linux/irq.h>
2262306a36Sopenharmony_ci#include <linux/clockchips.h>
2362306a36Sopenharmony_ci#include <asm/machdep.h>
2462306a36Sopenharmony_ci#include <asm/io.h>
2562306a36Sopenharmony_ci#include <asm/coldfire.h>
2662306a36Sopenharmony_ci#include <asm/mcfpit.h>
2762306a36Sopenharmony_ci#include <asm/mcfsim.h>
2862306a36Sopenharmony_ci
2962306a36Sopenharmony_ci/***************************************************************************/
3062306a36Sopenharmony_ci
3162306a36Sopenharmony_ci/*
3262306a36Sopenharmony_ci *	By default use timer1 as the system clock timer.
3362306a36Sopenharmony_ci */
3462306a36Sopenharmony_ci#define	FREQ	((MCF_CLK / 2) / 64)
3562306a36Sopenharmony_ci#define	TA(a)	(MCFPIT_BASE1 + (a))
3662306a36Sopenharmony_ci#define PIT_CYCLES_PER_JIFFY (FREQ / HZ)
3762306a36Sopenharmony_ci
3862306a36Sopenharmony_cistatic u32 pit_cnt;
3962306a36Sopenharmony_ci
4062306a36Sopenharmony_ci/*
4162306a36Sopenharmony_ci * Initialize the PIT timer.
4262306a36Sopenharmony_ci *
4362306a36Sopenharmony_ci * This is also called after resume to bring the PIT into operation again.
4462306a36Sopenharmony_ci */
4562306a36Sopenharmony_ci
4662306a36Sopenharmony_cistatic int cf_pit_set_periodic(struct clock_event_device *evt)
4762306a36Sopenharmony_ci{
4862306a36Sopenharmony_ci	__raw_writew(MCFPIT_PCSR_DISABLE, TA(MCFPIT_PCSR));
4962306a36Sopenharmony_ci	__raw_writew(PIT_CYCLES_PER_JIFFY, TA(MCFPIT_PMR));
5062306a36Sopenharmony_ci	__raw_writew(MCFPIT_PCSR_EN | MCFPIT_PCSR_PIE |
5162306a36Sopenharmony_ci		     MCFPIT_PCSR_OVW | MCFPIT_PCSR_RLD |
5262306a36Sopenharmony_ci		     MCFPIT_PCSR_CLK64, TA(MCFPIT_PCSR));
5362306a36Sopenharmony_ci	return 0;
5462306a36Sopenharmony_ci}
5562306a36Sopenharmony_ci
5662306a36Sopenharmony_cistatic int cf_pit_set_oneshot(struct clock_event_device *evt)
5762306a36Sopenharmony_ci{
5862306a36Sopenharmony_ci	__raw_writew(MCFPIT_PCSR_DISABLE, TA(MCFPIT_PCSR));
5962306a36Sopenharmony_ci	__raw_writew(MCFPIT_PCSR_EN | MCFPIT_PCSR_PIE |
6062306a36Sopenharmony_ci		     MCFPIT_PCSR_OVW | MCFPIT_PCSR_CLK64, TA(MCFPIT_PCSR));
6162306a36Sopenharmony_ci	return 0;
6262306a36Sopenharmony_ci}
6362306a36Sopenharmony_ci
6462306a36Sopenharmony_cistatic int cf_pit_shutdown(struct clock_event_device *evt)
6562306a36Sopenharmony_ci{
6662306a36Sopenharmony_ci	__raw_writew(MCFPIT_PCSR_DISABLE, TA(MCFPIT_PCSR));
6762306a36Sopenharmony_ci	return 0;
6862306a36Sopenharmony_ci}
6962306a36Sopenharmony_ci
7062306a36Sopenharmony_ci/*
7162306a36Sopenharmony_ci * Program the next event in oneshot mode
7262306a36Sopenharmony_ci *
7362306a36Sopenharmony_ci * Delta is given in PIT ticks
7462306a36Sopenharmony_ci */
7562306a36Sopenharmony_cistatic int cf_pit_next_event(unsigned long delta,
7662306a36Sopenharmony_ci		struct clock_event_device *evt)
7762306a36Sopenharmony_ci{
7862306a36Sopenharmony_ci	__raw_writew(delta, TA(MCFPIT_PMR));
7962306a36Sopenharmony_ci	return 0;
8062306a36Sopenharmony_ci}
8162306a36Sopenharmony_ci
8262306a36Sopenharmony_cistruct clock_event_device cf_pit_clockevent = {
8362306a36Sopenharmony_ci	.name			= "pit",
8462306a36Sopenharmony_ci	.features		= CLOCK_EVT_FEAT_PERIODIC |
8562306a36Sopenharmony_ci				  CLOCK_EVT_FEAT_ONESHOT,
8662306a36Sopenharmony_ci	.set_state_shutdown	= cf_pit_shutdown,
8762306a36Sopenharmony_ci	.set_state_periodic	= cf_pit_set_periodic,
8862306a36Sopenharmony_ci	.set_state_oneshot	= cf_pit_set_oneshot,
8962306a36Sopenharmony_ci	.set_next_event		= cf_pit_next_event,
9062306a36Sopenharmony_ci	.shift			= 32,
9162306a36Sopenharmony_ci	.irq			= MCF_IRQ_PIT1,
9262306a36Sopenharmony_ci};
9362306a36Sopenharmony_ci
9462306a36Sopenharmony_ci
9562306a36Sopenharmony_ci
9662306a36Sopenharmony_ci/***************************************************************************/
9762306a36Sopenharmony_ci
9862306a36Sopenharmony_cistatic irqreturn_t pit_tick(int irq, void *dummy)
9962306a36Sopenharmony_ci{
10062306a36Sopenharmony_ci	struct clock_event_device *evt = &cf_pit_clockevent;
10162306a36Sopenharmony_ci	u16 pcsr;
10262306a36Sopenharmony_ci
10362306a36Sopenharmony_ci	/* Reset the ColdFire timer */
10462306a36Sopenharmony_ci	pcsr = __raw_readw(TA(MCFPIT_PCSR));
10562306a36Sopenharmony_ci	__raw_writew(pcsr | MCFPIT_PCSR_PIF, TA(MCFPIT_PCSR));
10662306a36Sopenharmony_ci
10762306a36Sopenharmony_ci	pit_cnt += PIT_CYCLES_PER_JIFFY;
10862306a36Sopenharmony_ci	evt->event_handler(evt);
10962306a36Sopenharmony_ci	return IRQ_HANDLED;
11062306a36Sopenharmony_ci}
11162306a36Sopenharmony_ci
11262306a36Sopenharmony_ci/***************************************************************************/
11362306a36Sopenharmony_ci
11462306a36Sopenharmony_cistatic u64 pit_read_clk(struct clocksource *cs)
11562306a36Sopenharmony_ci{
11662306a36Sopenharmony_ci	unsigned long flags;
11762306a36Sopenharmony_ci	u32 cycles;
11862306a36Sopenharmony_ci	u16 pcntr;
11962306a36Sopenharmony_ci
12062306a36Sopenharmony_ci	local_irq_save(flags);
12162306a36Sopenharmony_ci	pcntr = __raw_readw(TA(MCFPIT_PCNTR));
12262306a36Sopenharmony_ci	cycles = pit_cnt;
12362306a36Sopenharmony_ci	local_irq_restore(flags);
12462306a36Sopenharmony_ci
12562306a36Sopenharmony_ci	return cycles + PIT_CYCLES_PER_JIFFY - pcntr;
12662306a36Sopenharmony_ci}
12762306a36Sopenharmony_ci
12862306a36Sopenharmony_ci/***************************************************************************/
12962306a36Sopenharmony_ci
13062306a36Sopenharmony_cistatic struct clocksource pit_clk = {
13162306a36Sopenharmony_ci	.name	= "pit",
13262306a36Sopenharmony_ci	.rating	= 100,
13362306a36Sopenharmony_ci	.read	= pit_read_clk,
13462306a36Sopenharmony_ci	.mask	= CLOCKSOURCE_MASK(32),
13562306a36Sopenharmony_ci};
13662306a36Sopenharmony_ci
13762306a36Sopenharmony_ci/***************************************************************************/
13862306a36Sopenharmony_ci
13962306a36Sopenharmony_civoid hw_timer_init(void)
14062306a36Sopenharmony_ci{
14162306a36Sopenharmony_ci	int ret;
14262306a36Sopenharmony_ci
14362306a36Sopenharmony_ci	cf_pit_clockevent.cpumask = cpumask_of(smp_processor_id());
14462306a36Sopenharmony_ci	cf_pit_clockevent.mult = div_sc(FREQ, NSEC_PER_SEC, 32);
14562306a36Sopenharmony_ci	cf_pit_clockevent.max_delta_ns =
14662306a36Sopenharmony_ci		clockevent_delta2ns(0xFFFF, &cf_pit_clockevent);
14762306a36Sopenharmony_ci	cf_pit_clockevent.max_delta_ticks = 0xFFFF;
14862306a36Sopenharmony_ci	cf_pit_clockevent.min_delta_ns =
14962306a36Sopenharmony_ci		clockevent_delta2ns(0x3f, &cf_pit_clockevent);
15062306a36Sopenharmony_ci	cf_pit_clockevent.min_delta_ticks = 0x3f;
15162306a36Sopenharmony_ci	clockevents_register_device(&cf_pit_clockevent);
15262306a36Sopenharmony_ci
15362306a36Sopenharmony_ci	ret = request_irq(MCF_IRQ_PIT1, pit_tick, IRQF_TIMER, "timer", NULL);
15462306a36Sopenharmony_ci	if (ret) {
15562306a36Sopenharmony_ci		pr_err("Failed to request irq %d (timer): %pe\n", MCF_IRQ_PIT1,
15662306a36Sopenharmony_ci		       ERR_PTR(ret));
15762306a36Sopenharmony_ci	}
15862306a36Sopenharmony_ci
15962306a36Sopenharmony_ci	clocksource_register_hz(&pit_clk, FREQ);
16062306a36Sopenharmony_ci}
16162306a36Sopenharmony_ci
16262306a36Sopenharmony_ci/***************************************************************************/
163