18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright (C) 2008-2009 Manuel Lauss <manuel.lauss@gmail.com> 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Previous incarnations were: 68c2ecf20Sopenharmony_ci * Copyright (C) 2001, 2006, 2008 MontaVista Software, <source@mvista.com> 78c2ecf20Sopenharmony_ci * Copied and modified Carsten Langgaard's time.c 88c2ecf20Sopenharmony_ci * 98c2ecf20Sopenharmony_ci * Carsten Langgaard, carstenl@mips.com 108c2ecf20Sopenharmony_ci * Copyright (C) 1999,2000 MIPS Technologies, Inc. All rights reserved. 118c2ecf20Sopenharmony_ci * 128c2ecf20Sopenharmony_ci * ######################################################################## 138c2ecf20Sopenharmony_ci * 148c2ecf20Sopenharmony_ci * ######################################################################## 158c2ecf20Sopenharmony_ci * 168c2ecf20Sopenharmony_ci * Clocksource/event using the 32.768kHz-clocked Counter1 ('RTC' in the 178c2ecf20Sopenharmony_ci * databooks). Firmware/Board init code must enable the counters in the 188c2ecf20Sopenharmony_ci * counter control register, otherwise the CP0 counter clocksource/event 198c2ecf20Sopenharmony_ci * will be installed instead (and use of 'wait' instruction is prohibited). 208c2ecf20Sopenharmony_ci */ 218c2ecf20Sopenharmony_ci 228c2ecf20Sopenharmony_ci#include <linux/clockchips.h> 238c2ecf20Sopenharmony_ci#include <linux/clocksource.h> 248c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 258c2ecf20Sopenharmony_ci#include <linux/spinlock.h> 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_ci#include <asm/idle.h> 288c2ecf20Sopenharmony_ci#include <asm/processor.h> 298c2ecf20Sopenharmony_ci#include <asm/time.h> 308c2ecf20Sopenharmony_ci#include <asm/mach-au1x00/au1000.h> 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_ci/* 32kHz clock enabled and detected */ 338c2ecf20Sopenharmony_ci#define CNTR_OK (SYS_CNTRL_E0 | SYS_CNTRL_32S) 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_cistatic u64 au1x_counter1_read(struct clocksource *cs) 368c2ecf20Sopenharmony_ci{ 378c2ecf20Sopenharmony_ci return alchemy_rdsys(AU1000_SYS_RTCREAD); 388c2ecf20Sopenharmony_ci} 398c2ecf20Sopenharmony_ci 408c2ecf20Sopenharmony_cistatic struct clocksource au1x_counter1_clocksource = { 418c2ecf20Sopenharmony_ci .name = "alchemy-counter1", 428c2ecf20Sopenharmony_ci .read = au1x_counter1_read, 438c2ecf20Sopenharmony_ci .mask = CLOCKSOURCE_MASK(32), 448c2ecf20Sopenharmony_ci .flags = CLOCK_SOURCE_IS_CONTINUOUS, 458c2ecf20Sopenharmony_ci .rating = 1500, 468c2ecf20Sopenharmony_ci}; 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_cistatic int au1x_rtcmatch2_set_next_event(unsigned long delta, 498c2ecf20Sopenharmony_ci struct clock_event_device *cd) 508c2ecf20Sopenharmony_ci{ 518c2ecf20Sopenharmony_ci delta += alchemy_rdsys(AU1000_SYS_RTCREAD); 528c2ecf20Sopenharmony_ci /* wait for register access */ 538c2ecf20Sopenharmony_ci while (alchemy_rdsys(AU1000_SYS_CNTRCTRL) & SYS_CNTRL_M21) 548c2ecf20Sopenharmony_ci ; 558c2ecf20Sopenharmony_ci alchemy_wrsys(delta, AU1000_SYS_RTCMATCH2); 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_ci return 0; 588c2ecf20Sopenharmony_ci} 598c2ecf20Sopenharmony_ci 608c2ecf20Sopenharmony_cistatic irqreturn_t au1x_rtcmatch2_irq(int irq, void *dev_id) 618c2ecf20Sopenharmony_ci{ 628c2ecf20Sopenharmony_ci struct clock_event_device *cd = dev_id; 638c2ecf20Sopenharmony_ci cd->event_handler(cd); 648c2ecf20Sopenharmony_ci return IRQ_HANDLED; 658c2ecf20Sopenharmony_ci} 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_cistatic struct clock_event_device au1x_rtcmatch2_clockdev = { 688c2ecf20Sopenharmony_ci .name = "rtcmatch2", 698c2ecf20Sopenharmony_ci .features = CLOCK_EVT_FEAT_ONESHOT, 708c2ecf20Sopenharmony_ci .rating = 1500, 718c2ecf20Sopenharmony_ci .set_next_event = au1x_rtcmatch2_set_next_event, 728c2ecf20Sopenharmony_ci .cpumask = cpu_possible_mask, 738c2ecf20Sopenharmony_ci}; 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_cistatic int __init alchemy_time_init(unsigned int m2int) 768c2ecf20Sopenharmony_ci{ 778c2ecf20Sopenharmony_ci struct clock_event_device *cd = &au1x_rtcmatch2_clockdev; 788c2ecf20Sopenharmony_ci unsigned long t; 798c2ecf20Sopenharmony_ci 808c2ecf20Sopenharmony_ci au1x_rtcmatch2_clockdev.irq = m2int; 818c2ecf20Sopenharmony_ci 828c2ecf20Sopenharmony_ci /* Check if firmware (YAMON, ...) has enabled 32kHz and clock 838c2ecf20Sopenharmony_ci * has been detected. If so install the rtcmatch2 clocksource, 848c2ecf20Sopenharmony_ci * otherwise don't bother. Note that both bits being set is by 858c2ecf20Sopenharmony_ci * no means a definite guarantee that the counters actually work 868c2ecf20Sopenharmony_ci * (the 32S bit seems to be stuck set to 1 once a single clock- 878c2ecf20Sopenharmony_ci * edge is detected, hence the timeouts). 888c2ecf20Sopenharmony_ci */ 898c2ecf20Sopenharmony_ci if (CNTR_OK != (alchemy_rdsys(AU1000_SYS_CNTRCTRL) & CNTR_OK)) 908c2ecf20Sopenharmony_ci goto cntr_err; 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_ci /* 938c2ecf20Sopenharmony_ci * setup counter 1 (RTC) to tick at full speed 948c2ecf20Sopenharmony_ci */ 958c2ecf20Sopenharmony_ci t = 0xffffff; 968c2ecf20Sopenharmony_ci while ((alchemy_rdsys(AU1000_SYS_CNTRCTRL) & SYS_CNTRL_T1S) && --t) 978c2ecf20Sopenharmony_ci asm volatile ("nop"); 988c2ecf20Sopenharmony_ci if (!t) 998c2ecf20Sopenharmony_ci goto cntr_err; 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_ci alchemy_wrsys(0, AU1000_SYS_RTCTRIM); /* 32.768 kHz */ 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_ci t = 0xffffff; 1048c2ecf20Sopenharmony_ci while ((alchemy_rdsys(AU1000_SYS_CNTRCTRL) & SYS_CNTRL_C1S) && --t) 1058c2ecf20Sopenharmony_ci asm volatile ("nop"); 1068c2ecf20Sopenharmony_ci if (!t) 1078c2ecf20Sopenharmony_ci goto cntr_err; 1088c2ecf20Sopenharmony_ci alchemy_wrsys(0, AU1000_SYS_RTCWRITE); 1098c2ecf20Sopenharmony_ci 1108c2ecf20Sopenharmony_ci t = 0xffffff; 1118c2ecf20Sopenharmony_ci while ((alchemy_rdsys(AU1000_SYS_CNTRCTRL) & SYS_CNTRL_C1S) && --t) 1128c2ecf20Sopenharmony_ci asm volatile ("nop"); 1138c2ecf20Sopenharmony_ci if (!t) 1148c2ecf20Sopenharmony_ci goto cntr_err; 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_ci /* register counter1 clocksource and event device */ 1178c2ecf20Sopenharmony_ci clocksource_register_hz(&au1x_counter1_clocksource, 32768); 1188c2ecf20Sopenharmony_ci 1198c2ecf20Sopenharmony_ci cd->shift = 32; 1208c2ecf20Sopenharmony_ci cd->mult = div_sc(32768, NSEC_PER_SEC, cd->shift); 1218c2ecf20Sopenharmony_ci cd->max_delta_ns = clockevent_delta2ns(0xffffffff, cd); 1228c2ecf20Sopenharmony_ci cd->max_delta_ticks = 0xffffffff; 1238c2ecf20Sopenharmony_ci cd->min_delta_ns = clockevent_delta2ns(9, cd); 1248c2ecf20Sopenharmony_ci cd->min_delta_ticks = 9; /* ~0.28ms */ 1258c2ecf20Sopenharmony_ci clockevents_register_device(cd); 1268c2ecf20Sopenharmony_ci if (request_irq(m2int, au1x_rtcmatch2_irq, IRQF_TIMER, "timer", 1278c2ecf20Sopenharmony_ci &au1x_rtcmatch2_clockdev)) 1288c2ecf20Sopenharmony_ci pr_err("Failed to register timer interrupt\n"); 1298c2ecf20Sopenharmony_ci 1308c2ecf20Sopenharmony_ci printk(KERN_INFO "Alchemy clocksource installed\n"); 1318c2ecf20Sopenharmony_ci 1328c2ecf20Sopenharmony_ci return 0; 1338c2ecf20Sopenharmony_ci 1348c2ecf20Sopenharmony_cicntr_err: 1358c2ecf20Sopenharmony_ci return -1; 1368c2ecf20Sopenharmony_ci} 1378c2ecf20Sopenharmony_ci 1388c2ecf20Sopenharmony_cistatic int alchemy_m2inttab[] __initdata = { 1398c2ecf20Sopenharmony_ci AU1000_RTC_MATCH2_INT, 1408c2ecf20Sopenharmony_ci AU1500_RTC_MATCH2_INT, 1418c2ecf20Sopenharmony_ci AU1100_RTC_MATCH2_INT, 1428c2ecf20Sopenharmony_ci AU1550_RTC_MATCH2_INT, 1438c2ecf20Sopenharmony_ci AU1200_RTC_MATCH2_INT, 1448c2ecf20Sopenharmony_ci AU1300_RTC_MATCH2_INT, 1458c2ecf20Sopenharmony_ci}; 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_civoid __init plat_time_init(void) 1488c2ecf20Sopenharmony_ci{ 1498c2ecf20Sopenharmony_ci int t; 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_ci t = alchemy_get_cputype(); 1528c2ecf20Sopenharmony_ci if (t == ALCHEMY_CPU_UNKNOWN || 1538c2ecf20Sopenharmony_ci alchemy_time_init(alchemy_m2inttab[t])) 1548c2ecf20Sopenharmony_ci cpu_wait = NULL; /* wait doesn't work with r4k timer */ 1558c2ecf20Sopenharmony_ci} 156