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