18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * arch/arm/plat-iop/time.c 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Timer code for IOP32x and IOP33x based systems 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * Author: Deepak Saxena <dsaxena@mvista.com> 88c2ecf20Sopenharmony_ci * 98c2ecf20Sopenharmony_ci * Copyright 2002-2003 MontaVista Software Inc. 108c2ecf20Sopenharmony_ci */ 118c2ecf20Sopenharmony_ci 128c2ecf20Sopenharmony_ci#include <linux/kernel.h> 138c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 148c2ecf20Sopenharmony_ci#include <linux/time.h> 158c2ecf20Sopenharmony_ci#include <linux/init.h> 168c2ecf20Sopenharmony_ci#include <linux/timex.h> 178c2ecf20Sopenharmony_ci#include <linux/io.h> 188c2ecf20Sopenharmony_ci#include <linux/clocksource.h> 198c2ecf20Sopenharmony_ci#include <linux/clockchips.h> 208c2ecf20Sopenharmony_ci#include <linux/export.h> 218c2ecf20Sopenharmony_ci#include <linux/sched_clock.h> 228c2ecf20Sopenharmony_ci#include <asm/irq.h> 238c2ecf20Sopenharmony_ci#include <linux/uaccess.h> 248c2ecf20Sopenharmony_ci#include <asm/mach/irq.h> 258c2ecf20Sopenharmony_ci#include <asm/mach/time.h> 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_ci#include "hardware.h" 288c2ecf20Sopenharmony_ci#include "irqs.h" 298c2ecf20Sopenharmony_ci 308c2ecf20Sopenharmony_ci/* 318c2ecf20Sopenharmony_ci * Minimum clocksource/clockevent timer range in seconds 328c2ecf20Sopenharmony_ci */ 338c2ecf20Sopenharmony_ci#define IOP_MIN_RANGE 4 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_ci/* 368c2ecf20Sopenharmony_ci * IOP clocksource (free-running timer 1). 378c2ecf20Sopenharmony_ci */ 388c2ecf20Sopenharmony_cistatic u64 notrace iop_clocksource_read(struct clocksource *unused) 398c2ecf20Sopenharmony_ci{ 408c2ecf20Sopenharmony_ci return 0xffffffffu - read_tcr1(); 418c2ecf20Sopenharmony_ci} 428c2ecf20Sopenharmony_ci 438c2ecf20Sopenharmony_cistatic struct clocksource iop_clocksource = { 448c2ecf20Sopenharmony_ci .name = "iop_timer1", 458c2ecf20Sopenharmony_ci .rating = 300, 468c2ecf20Sopenharmony_ci .read = iop_clocksource_read, 478c2ecf20Sopenharmony_ci .mask = CLOCKSOURCE_MASK(32), 488c2ecf20Sopenharmony_ci .flags = CLOCK_SOURCE_IS_CONTINUOUS, 498c2ecf20Sopenharmony_ci}; 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_ci/* 528c2ecf20Sopenharmony_ci * IOP sched_clock() implementation via its clocksource. 538c2ecf20Sopenharmony_ci */ 548c2ecf20Sopenharmony_cistatic u64 notrace iop_read_sched_clock(void) 558c2ecf20Sopenharmony_ci{ 568c2ecf20Sopenharmony_ci return 0xffffffffu - read_tcr1(); 578c2ecf20Sopenharmony_ci} 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_ci/* 608c2ecf20Sopenharmony_ci * IOP clockevents (interrupting timer 0). 618c2ecf20Sopenharmony_ci */ 628c2ecf20Sopenharmony_cistatic int iop_set_next_event(unsigned long delta, 638c2ecf20Sopenharmony_ci struct clock_event_device *unused) 648c2ecf20Sopenharmony_ci{ 658c2ecf20Sopenharmony_ci u32 tmr = IOP_TMR_PRIVILEGED | IOP_TMR_RATIO_1_1; 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_ci BUG_ON(delta == 0); 688c2ecf20Sopenharmony_ci write_tmr0(tmr & ~(IOP_TMR_EN | IOP_TMR_RELOAD)); 698c2ecf20Sopenharmony_ci write_tcr0(delta); 708c2ecf20Sopenharmony_ci write_tmr0((tmr & ~IOP_TMR_RELOAD) | IOP_TMR_EN); 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_ci return 0; 738c2ecf20Sopenharmony_ci} 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_cistatic unsigned long ticks_per_jiffy; 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_cistatic int iop_set_periodic(struct clock_event_device *evt) 788c2ecf20Sopenharmony_ci{ 798c2ecf20Sopenharmony_ci u32 tmr = read_tmr0(); 808c2ecf20Sopenharmony_ci 818c2ecf20Sopenharmony_ci write_tmr0(tmr & ~IOP_TMR_EN); 828c2ecf20Sopenharmony_ci write_tcr0(ticks_per_jiffy - 1); 838c2ecf20Sopenharmony_ci write_trr0(ticks_per_jiffy - 1); 848c2ecf20Sopenharmony_ci tmr |= (IOP_TMR_RELOAD | IOP_TMR_EN); 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_ci write_tmr0(tmr); 878c2ecf20Sopenharmony_ci return 0; 888c2ecf20Sopenharmony_ci} 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_cistatic int iop_set_oneshot(struct clock_event_device *evt) 918c2ecf20Sopenharmony_ci{ 928c2ecf20Sopenharmony_ci u32 tmr = read_tmr0(); 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_ci /* ->set_next_event sets period and enables timer */ 958c2ecf20Sopenharmony_ci tmr &= ~(IOP_TMR_RELOAD | IOP_TMR_EN); 968c2ecf20Sopenharmony_ci write_tmr0(tmr); 978c2ecf20Sopenharmony_ci return 0; 988c2ecf20Sopenharmony_ci} 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_cistatic int iop_shutdown(struct clock_event_device *evt) 1018c2ecf20Sopenharmony_ci{ 1028c2ecf20Sopenharmony_ci u32 tmr = read_tmr0(); 1038c2ecf20Sopenharmony_ci 1048c2ecf20Sopenharmony_ci tmr &= ~IOP_TMR_EN; 1058c2ecf20Sopenharmony_ci write_tmr0(tmr); 1068c2ecf20Sopenharmony_ci return 0; 1078c2ecf20Sopenharmony_ci} 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_cistatic int iop_resume(struct clock_event_device *evt) 1108c2ecf20Sopenharmony_ci{ 1118c2ecf20Sopenharmony_ci u32 tmr = read_tmr0(); 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_ci tmr |= IOP_TMR_EN; 1148c2ecf20Sopenharmony_ci write_tmr0(tmr); 1158c2ecf20Sopenharmony_ci return 0; 1168c2ecf20Sopenharmony_ci} 1178c2ecf20Sopenharmony_ci 1188c2ecf20Sopenharmony_cistatic struct clock_event_device iop_clockevent = { 1198c2ecf20Sopenharmony_ci .name = "iop_timer0", 1208c2ecf20Sopenharmony_ci .features = CLOCK_EVT_FEAT_PERIODIC | 1218c2ecf20Sopenharmony_ci CLOCK_EVT_FEAT_ONESHOT, 1228c2ecf20Sopenharmony_ci .rating = 300, 1238c2ecf20Sopenharmony_ci .set_next_event = iop_set_next_event, 1248c2ecf20Sopenharmony_ci .set_state_shutdown = iop_shutdown, 1258c2ecf20Sopenharmony_ci .set_state_periodic = iop_set_periodic, 1268c2ecf20Sopenharmony_ci .tick_resume = iop_resume, 1278c2ecf20Sopenharmony_ci .set_state_oneshot = iop_set_oneshot, 1288c2ecf20Sopenharmony_ci}; 1298c2ecf20Sopenharmony_ci 1308c2ecf20Sopenharmony_cistatic irqreturn_t 1318c2ecf20Sopenharmony_ciiop_timer_interrupt(int irq, void *dev_id) 1328c2ecf20Sopenharmony_ci{ 1338c2ecf20Sopenharmony_ci struct clock_event_device *evt = dev_id; 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_ci write_tisr(1); 1368c2ecf20Sopenharmony_ci evt->event_handler(evt); 1378c2ecf20Sopenharmony_ci return IRQ_HANDLED; 1388c2ecf20Sopenharmony_ci} 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_cistatic unsigned long iop_tick_rate; 1418c2ecf20Sopenharmony_ciunsigned long get_iop_tick_rate(void) 1428c2ecf20Sopenharmony_ci{ 1438c2ecf20Sopenharmony_ci return iop_tick_rate; 1448c2ecf20Sopenharmony_ci} 1458c2ecf20Sopenharmony_ciEXPORT_SYMBOL(get_iop_tick_rate); 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_civoid __init iop_init_time(unsigned long tick_rate) 1488c2ecf20Sopenharmony_ci{ 1498c2ecf20Sopenharmony_ci u32 timer_ctl; 1508c2ecf20Sopenharmony_ci int irq = IRQ_IOP32X_TIMER0; 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_ci sched_clock_register(iop_read_sched_clock, 32, tick_rate); 1538c2ecf20Sopenharmony_ci 1548c2ecf20Sopenharmony_ci ticks_per_jiffy = DIV_ROUND_CLOSEST(tick_rate, HZ); 1558c2ecf20Sopenharmony_ci iop_tick_rate = tick_rate; 1568c2ecf20Sopenharmony_ci 1578c2ecf20Sopenharmony_ci timer_ctl = IOP_TMR_EN | IOP_TMR_PRIVILEGED | 1588c2ecf20Sopenharmony_ci IOP_TMR_RELOAD | IOP_TMR_RATIO_1_1; 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_ci /* 1618c2ecf20Sopenharmony_ci * Set up interrupting clockevent timer 0. 1628c2ecf20Sopenharmony_ci */ 1638c2ecf20Sopenharmony_ci write_tmr0(timer_ctl & ~IOP_TMR_EN); 1648c2ecf20Sopenharmony_ci write_tisr(1); 1658c2ecf20Sopenharmony_ci if (request_irq(irq, iop_timer_interrupt, IRQF_TIMER | IRQF_IRQPOLL, 1668c2ecf20Sopenharmony_ci "IOP Timer Tick", &iop_clockevent)) 1678c2ecf20Sopenharmony_ci pr_err("Failed to request irq() %d (IOP Timer Tick)\n", irq); 1688c2ecf20Sopenharmony_ci iop_clockevent.cpumask = cpumask_of(0); 1698c2ecf20Sopenharmony_ci clockevents_config_and_register(&iop_clockevent, tick_rate, 1708c2ecf20Sopenharmony_ci 0xf, 0xfffffffe); 1718c2ecf20Sopenharmony_ci 1728c2ecf20Sopenharmony_ci /* 1738c2ecf20Sopenharmony_ci * Set up free-running clocksource timer 1. 1748c2ecf20Sopenharmony_ci */ 1758c2ecf20Sopenharmony_ci write_trr1(0xffffffff); 1768c2ecf20Sopenharmony_ci write_tcr1(0xffffffff); 1778c2ecf20Sopenharmony_ci write_tmr1(timer_ctl); 1788c2ecf20Sopenharmony_ci clocksource_register_hz(&iop_clocksource, tick_rate); 1798c2ecf20Sopenharmony_ci} 180