162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci *  linux/arch/arm/common/time-acorn.c
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci *  Copyright (c) 1996-2000 Russell King.
662306a36Sopenharmony_ci *
762306a36Sopenharmony_ci *  Changelog:
862306a36Sopenharmony_ci *   24-Sep-1996	RMK	Created
962306a36Sopenharmony_ci *   10-Oct-1996	RMK	Brought up to date with arch-sa110eval
1062306a36Sopenharmony_ci *   04-Dec-1997	RMK	Updated for new arch/arm/time.c
1162306a36Sopenharmony_ci *   13=Jun-2004	DS	Moved to arch/arm/common b/c shared w/CLPS7500
1262306a36Sopenharmony_ci */
1362306a36Sopenharmony_ci#include <linux/clocksource.h>
1462306a36Sopenharmony_ci#include <linux/init.h>
1562306a36Sopenharmony_ci#include <linux/interrupt.h>
1662306a36Sopenharmony_ci#include <linux/irq.h>
1762306a36Sopenharmony_ci#include <linux/io.h>
1862306a36Sopenharmony_ci
1962306a36Sopenharmony_ci#include <mach/hardware.h>
2062306a36Sopenharmony_ci#include <asm/hardware/ioc.h>
2162306a36Sopenharmony_ci
2262306a36Sopenharmony_ci#include <asm/mach/time.h>
2362306a36Sopenharmony_ci
2462306a36Sopenharmony_ci#define RPC_CLOCK_FREQ 2000000
2562306a36Sopenharmony_ci#define RPC_LATCH DIV_ROUND_CLOSEST(RPC_CLOCK_FREQ, HZ)
2662306a36Sopenharmony_ci
2762306a36Sopenharmony_cistatic u32 ioc_time;
2862306a36Sopenharmony_ci
2962306a36Sopenharmony_cistatic u64 ioc_timer_read(struct clocksource *cs)
3062306a36Sopenharmony_ci{
3162306a36Sopenharmony_ci	unsigned int count1, count2, status;
3262306a36Sopenharmony_ci	unsigned long flags;
3362306a36Sopenharmony_ci	u32 ticks;
3462306a36Sopenharmony_ci
3562306a36Sopenharmony_ci	local_irq_save(flags);
3662306a36Sopenharmony_ci	ioc_writeb (0, IOC_T0LATCH);
3762306a36Sopenharmony_ci	barrier ();
3862306a36Sopenharmony_ci	count1 = ioc_readb(IOC_T0CNTL) | (ioc_readb(IOC_T0CNTH) << 8);
3962306a36Sopenharmony_ci	barrier ();
4062306a36Sopenharmony_ci	status = ioc_readb(IOC_IRQREQA);
4162306a36Sopenharmony_ci	barrier ();
4262306a36Sopenharmony_ci	ioc_writeb (0, IOC_T0LATCH);
4362306a36Sopenharmony_ci	barrier ();
4462306a36Sopenharmony_ci	count2 = ioc_readb(IOC_T0CNTL) | (ioc_readb(IOC_T0CNTH) << 8);
4562306a36Sopenharmony_ci	ticks = ioc_time + RPC_LATCH - count2;
4662306a36Sopenharmony_ci	local_irq_restore(flags);
4762306a36Sopenharmony_ci
4862306a36Sopenharmony_ci	if (count2 < count1) {
4962306a36Sopenharmony_ci		/*
5062306a36Sopenharmony_ci		 * The timer has not reloaded between reading count1 and
5162306a36Sopenharmony_ci		 * count2, check whether an interrupt was actually pending.
5262306a36Sopenharmony_ci		 */
5362306a36Sopenharmony_ci		if (status & (1 << 5))
5462306a36Sopenharmony_ci			ticks += RPC_LATCH;
5562306a36Sopenharmony_ci	} else if (count2 > count1) {
5662306a36Sopenharmony_ci		/*
5762306a36Sopenharmony_ci		 * The timer has reloaded, so count2 indicates the new
5862306a36Sopenharmony_ci		 * count since the wrap.  The interrupt would not have
5962306a36Sopenharmony_ci		 * been processed, so add the missed ticks.
6062306a36Sopenharmony_ci		 */
6162306a36Sopenharmony_ci		ticks += RPC_LATCH;
6262306a36Sopenharmony_ci	}
6362306a36Sopenharmony_ci
6462306a36Sopenharmony_ci	return ticks;
6562306a36Sopenharmony_ci}
6662306a36Sopenharmony_ci
6762306a36Sopenharmony_cistatic struct clocksource ioctime_clocksource = {
6862306a36Sopenharmony_ci	.read = ioc_timer_read,
6962306a36Sopenharmony_ci	.mask = CLOCKSOURCE_MASK(32),
7062306a36Sopenharmony_ci	.rating = 100,
7162306a36Sopenharmony_ci};
7262306a36Sopenharmony_ci
7362306a36Sopenharmony_civoid __init ioctime_init(void)
7462306a36Sopenharmony_ci{
7562306a36Sopenharmony_ci	ioc_writeb(RPC_LATCH & 255, IOC_T0LTCHL);
7662306a36Sopenharmony_ci	ioc_writeb(RPC_LATCH >> 8, IOC_T0LTCHH);
7762306a36Sopenharmony_ci	ioc_writeb(0, IOC_T0GO);
7862306a36Sopenharmony_ci}
7962306a36Sopenharmony_ci
8062306a36Sopenharmony_cistatic irqreturn_t
8162306a36Sopenharmony_ciioc_timer_interrupt(int irq, void *dev_id)
8262306a36Sopenharmony_ci{
8362306a36Sopenharmony_ci	ioc_time += RPC_LATCH;
8462306a36Sopenharmony_ci	legacy_timer_tick(1);
8562306a36Sopenharmony_ci	return IRQ_HANDLED;
8662306a36Sopenharmony_ci}
8762306a36Sopenharmony_ci
8862306a36Sopenharmony_ci/*
8962306a36Sopenharmony_ci * Set up timer interrupt.
9062306a36Sopenharmony_ci */
9162306a36Sopenharmony_civoid __init ioc_timer_init(void)
9262306a36Sopenharmony_ci{
9362306a36Sopenharmony_ci	WARN_ON(clocksource_register_hz(&ioctime_clocksource, RPC_CLOCK_FREQ));
9462306a36Sopenharmony_ci	ioctime_init();
9562306a36Sopenharmony_ci	if (request_irq(IRQ_TIMER0, ioc_timer_interrupt, 0, "timer", NULL))
9662306a36Sopenharmony_ci		pr_err("Failed to request irq %d (timer)\n", IRQ_TIMER0);
9762306a36Sopenharmony_ci}
98