xref: /kernel/linux/linux-5.10/arch/arm/mach-rpc/time.c (revision 8c2ecf20)
18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci *  linux/arch/arm/common/time-acorn.c
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci *  Copyright (c) 1996-2000 Russell King.
68c2ecf20Sopenharmony_ci *
78c2ecf20Sopenharmony_ci *  Changelog:
88c2ecf20Sopenharmony_ci *   24-Sep-1996	RMK	Created
98c2ecf20Sopenharmony_ci *   10-Oct-1996	RMK	Brought up to date with arch-sa110eval
108c2ecf20Sopenharmony_ci *   04-Dec-1997	RMK	Updated for new arch/arm/time.c
118c2ecf20Sopenharmony_ci *   13=Jun-2004	DS	Moved to arch/arm/common b/c shared w/CLPS7500
128c2ecf20Sopenharmony_ci */
138c2ecf20Sopenharmony_ci#include <linux/clocksource.h>
148c2ecf20Sopenharmony_ci#include <linux/init.h>
158c2ecf20Sopenharmony_ci#include <linux/interrupt.h>
168c2ecf20Sopenharmony_ci#include <linux/irq.h>
178c2ecf20Sopenharmony_ci#include <linux/io.h>
188c2ecf20Sopenharmony_ci
198c2ecf20Sopenharmony_ci#include <mach/hardware.h>
208c2ecf20Sopenharmony_ci#include <asm/hardware/ioc.h>
218c2ecf20Sopenharmony_ci
228c2ecf20Sopenharmony_ci#include <asm/mach/time.h>
238c2ecf20Sopenharmony_ci
248c2ecf20Sopenharmony_ci#define RPC_CLOCK_FREQ 2000000
258c2ecf20Sopenharmony_ci#define RPC_LATCH DIV_ROUND_CLOSEST(RPC_CLOCK_FREQ, HZ)
268c2ecf20Sopenharmony_ci
278c2ecf20Sopenharmony_cistatic u32 ioc_time;
288c2ecf20Sopenharmony_ci
298c2ecf20Sopenharmony_cistatic u64 ioc_timer_read(struct clocksource *cs)
308c2ecf20Sopenharmony_ci{
318c2ecf20Sopenharmony_ci	unsigned int count1, count2, status;
328c2ecf20Sopenharmony_ci	unsigned long flags;
338c2ecf20Sopenharmony_ci	u32 ticks;
348c2ecf20Sopenharmony_ci
358c2ecf20Sopenharmony_ci	local_irq_save(flags);
368c2ecf20Sopenharmony_ci	ioc_writeb (0, IOC_T0LATCH);
378c2ecf20Sopenharmony_ci	barrier ();
388c2ecf20Sopenharmony_ci	count1 = ioc_readb(IOC_T0CNTL) | (ioc_readb(IOC_T0CNTH) << 8);
398c2ecf20Sopenharmony_ci	barrier ();
408c2ecf20Sopenharmony_ci	status = ioc_readb(IOC_IRQREQA);
418c2ecf20Sopenharmony_ci	barrier ();
428c2ecf20Sopenharmony_ci	ioc_writeb (0, IOC_T0LATCH);
438c2ecf20Sopenharmony_ci	barrier ();
448c2ecf20Sopenharmony_ci	count2 = ioc_readb(IOC_T0CNTL) | (ioc_readb(IOC_T0CNTH) << 8);
458c2ecf20Sopenharmony_ci	ticks = ioc_time + RPC_LATCH - count2;
468c2ecf20Sopenharmony_ci	local_irq_restore(flags);
478c2ecf20Sopenharmony_ci
488c2ecf20Sopenharmony_ci	if (count2 < count1) {
498c2ecf20Sopenharmony_ci		/*
508c2ecf20Sopenharmony_ci		 * The timer has not reloaded between reading count1 and
518c2ecf20Sopenharmony_ci		 * count2, check whether an interrupt was actually pending.
528c2ecf20Sopenharmony_ci		 */
538c2ecf20Sopenharmony_ci		if (status & (1 << 5))
548c2ecf20Sopenharmony_ci			ticks += RPC_LATCH;
558c2ecf20Sopenharmony_ci	} else if (count2 > count1) {
568c2ecf20Sopenharmony_ci		/*
578c2ecf20Sopenharmony_ci		 * The timer has reloaded, so count2 indicates the new
588c2ecf20Sopenharmony_ci		 * count since the wrap.  The interrupt would not have
598c2ecf20Sopenharmony_ci		 * been processed, so add the missed ticks.
608c2ecf20Sopenharmony_ci		 */
618c2ecf20Sopenharmony_ci		ticks += RPC_LATCH;
628c2ecf20Sopenharmony_ci	}
638c2ecf20Sopenharmony_ci
648c2ecf20Sopenharmony_ci	return ticks;
658c2ecf20Sopenharmony_ci}
668c2ecf20Sopenharmony_ci
678c2ecf20Sopenharmony_cistatic struct clocksource ioctime_clocksource = {
688c2ecf20Sopenharmony_ci	.read = ioc_timer_read,
698c2ecf20Sopenharmony_ci	.mask = CLOCKSOURCE_MASK(32),
708c2ecf20Sopenharmony_ci	.rating = 100,
718c2ecf20Sopenharmony_ci};
728c2ecf20Sopenharmony_ci
738c2ecf20Sopenharmony_civoid __init ioctime_init(void)
748c2ecf20Sopenharmony_ci{
758c2ecf20Sopenharmony_ci	ioc_writeb(RPC_LATCH & 255, IOC_T0LTCHL);
768c2ecf20Sopenharmony_ci	ioc_writeb(RPC_LATCH >> 8, IOC_T0LTCHH);
778c2ecf20Sopenharmony_ci	ioc_writeb(0, IOC_T0GO);
788c2ecf20Sopenharmony_ci}
798c2ecf20Sopenharmony_ci
808c2ecf20Sopenharmony_cistatic irqreturn_t
818c2ecf20Sopenharmony_ciioc_timer_interrupt(int irq, void *dev_id)
828c2ecf20Sopenharmony_ci{
838c2ecf20Sopenharmony_ci	ioc_time += RPC_LATCH;
848c2ecf20Sopenharmony_ci	timer_tick();
858c2ecf20Sopenharmony_ci	return IRQ_HANDLED;
868c2ecf20Sopenharmony_ci}
878c2ecf20Sopenharmony_ci
888c2ecf20Sopenharmony_ci/*
898c2ecf20Sopenharmony_ci * Set up timer interrupt.
908c2ecf20Sopenharmony_ci */
918c2ecf20Sopenharmony_civoid __init ioc_timer_init(void)
928c2ecf20Sopenharmony_ci{
938c2ecf20Sopenharmony_ci	WARN_ON(clocksource_register_hz(&ioctime_clocksource, RPC_CLOCK_FREQ));
948c2ecf20Sopenharmony_ci	ioctime_init();
958c2ecf20Sopenharmony_ci	if (request_irq(IRQ_TIMER0, ioc_timer_interrupt, 0, "timer", NULL))
968c2ecf20Sopenharmony_ci		pr_err("Failed to request irq %d (timer)\n", IRQ_TIMER0);
978c2ecf20Sopenharmony_ci}
98