162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (C) 2008-2009 Manuel Lauss <manuel.lauss@gmail.com> 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Previous incarnations were: 662306a36Sopenharmony_ci * Copyright (C) 2001, 2006, 2008 MontaVista Software, <source@mvista.com> 762306a36Sopenharmony_ci * Copied and modified Carsten Langgaard's time.c 862306a36Sopenharmony_ci * 962306a36Sopenharmony_ci * Carsten Langgaard, carstenl@mips.com 1062306a36Sopenharmony_ci * Copyright (C) 1999,2000 MIPS Technologies, Inc. All rights reserved. 1162306a36Sopenharmony_ci * 1262306a36Sopenharmony_ci * ######################################################################## 1362306a36Sopenharmony_ci * 1462306a36Sopenharmony_ci * ######################################################################## 1562306a36Sopenharmony_ci * 1662306a36Sopenharmony_ci * Clocksource/event using the 32.768kHz-clocked Counter1 ('RTC' in the 1762306a36Sopenharmony_ci * databooks). Firmware/Board init code must enable the counters in the 1862306a36Sopenharmony_ci * counter control register, otherwise the CP0 counter clocksource/event 1962306a36Sopenharmony_ci * will be installed instead (and use of 'wait' instruction is prohibited). 2062306a36Sopenharmony_ci */ 2162306a36Sopenharmony_ci 2262306a36Sopenharmony_ci#include <linux/clockchips.h> 2362306a36Sopenharmony_ci#include <linux/clocksource.h> 2462306a36Sopenharmony_ci#include <linux/interrupt.h> 2562306a36Sopenharmony_ci#include <linux/spinlock.h> 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_ci#include <asm/idle.h> 2862306a36Sopenharmony_ci#include <asm/processor.h> 2962306a36Sopenharmony_ci#include <asm/time.h> 3062306a36Sopenharmony_ci#include <asm/mach-au1x00/au1000.h> 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_ci/* 32kHz clock enabled and detected */ 3362306a36Sopenharmony_ci#define CNTR_OK (SYS_CNTRL_E0 | SYS_CNTRL_32S) 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_cistatic u64 au1x_counter1_read(struct clocksource *cs) 3662306a36Sopenharmony_ci{ 3762306a36Sopenharmony_ci return alchemy_rdsys(AU1000_SYS_RTCREAD); 3862306a36Sopenharmony_ci} 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_cistatic struct clocksource au1x_counter1_clocksource = { 4162306a36Sopenharmony_ci .name = "alchemy-counter1", 4262306a36Sopenharmony_ci .read = au1x_counter1_read, 4362306a36Sopenharmony_ci .mask = CLOCKSOURCE_MASK(32), 4462306a36Sopenharmony_ci .flags = CLOCK_SOURCE_IS_CONTINUOUS, 4562306a36Sopenharmony_ci .rating = 1500, 4662306a36Sopenharmony_ci}; 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_cistatic int au1x_rtcmatch2_set_next_event(unsigned long delta, 4962306a36Sopenharmony_ci struct clock_event_device *cd) 5062306a36Sopenharmony_ci{ 5162306a36Sopenharmony_ci delta += alchemy_rdsys(AU1000_SYS_RTCREAD); 5262306a36Sopenharmony_ci /* wait for register access */ 5362306a36Sopenharmony_ci while (alchemy_rdsys(AU1000_SYS_CNTRCTRL) & SYS_CNTRL_M21) 5462306a36Sopenharmony_ci ; 5562306a36Sopenharmony_ci alchemy_wrsys(delta, AU1000_SYS_RTCMATCH2); 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_ci return 0; 5862306a36Sopenharmony_ci} 5962306a36Sopenharmony_ci 6062306a36Sopenharmony_cistatic irqreturn_t au1x_rtcmatch2_irq(int irq, void *dev_id) 6162306a36Sopenharmony_ci{ 6262306a36Sopenharmony_ci struct clock_event_device *cd = dev_id; 6362306a36Sopenharmony_ci cd->event_handler(cd); 6462306a36Sopenharmony_ci return IRQ_HANDLED; 6562306a36Sopenharmony_ci} 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_cistatic struct clock_event_device au1x_rtcmatch2_clockdev = { 6862306a36Sopenharmony_ci .name = "rtcmatch2", 6962306a36Sopenharmony_ci .features = CLOCK_EVT_FEAT_ONESHOT, 7062306a36Sopenharmony_ci .rating = 1500, 7162306a36Sopenharmony_ci .set_next_event = au1x_rtcmatch2_set_next_event, 7262306a36Sopenharmony_ci .cpumask = cpu_possible_mask, 7362306a36Sopenharmony_ci}; 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_cistatic int __init alchemy_time_init(unsigned int m2int) 7662306a36Sopenharmony_ci{ 7762306a36Sopenharmony_ci struct clock_event_device *cd = &au1x_rtcmatch2_clockdev; 7862306a36Sopenharmony_ci unsigned long t; 7962306a36Sopenharmony_ci 8062306a36Sopenharmony_ci au1x_rtcmatch2_clockdev.irq = m2int; 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_ci /* Check if firmware (YAMON, ...) has enabled 32kHz and clock 8362306a36Sopenharmony_ci * has been detected. If so install the rtcmatch2 clocksource, 8462306a36Sopenharmony_ci * otherwise don't bother. Note that both bits being set is by 8562306a36Sopenharmony_ci * no means a definite guarantee that the counters actually work 8662306a36Sopenharmony_ci * (the 32S bit seems to be stuck set to 1 once a single clock- 8762306a36Sopenharmony_ci * edge is detected, hence the timeouts). 8862306a36Sopenharmony_ci */ 8962306a36Sopenharmony_ci if (CNTR_OK != (alchemy_rdsys(AU1000_SYS_CNTRCTRL) & CNTR_OK)) 9062306a36Sopenharmony_ci goto cntr_err; 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_ci /* 9362306a36Sopenharmony_ci * setup counter 1 (RTC) to tick at full speed 9462306a36Sopenharmony_ci */ 9562306a36Sopenharmony_ci t = 0xffffff; 9662306a36Sopenharmony_ci while ((alchemy_rdsys(AU1000_SYS_CNTRCTRL) & SYS_CNTRL_T1S) && --t) 9762306a36Sopenharmony_ci asm volatile ("nop"); 9862306a36Sopenharmony_ci if (!t) 9962306a36Sopenharmony_ci goto cntr_err; 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_ci alchemy_wrsys(0, AU1000_SYS_RTCTRIM); /* 32.768 kHz */ 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_ci t = 0xffffff; 10462306a36Sopenharmony_ci while ((alchemy_rdsys(AU1000_SYS_CNTRCTRL) & SYS_CNTRL_C1S) && --t) 10562306a36Sopenharmony_ci asm volatile ("nop"); 10662306a36Sopenharmony_ci if (!t) 10762306a36Sopenharmony_ci goto cntr_err; 10862306a36Sopenharmony_ci alchemy_wrsys(0, AU1000_SYS_RTCWRITE); 10962306a36Sopenharmony_ci 11062306a36Sopenharmony_ci t = 0xffffff; 11162306a36Sopenharmony_ci while ((alchemy_rdsys(AU1000_SYS_CNTRCTRL) & SYS_CNTRL_C1S) && --t) 11262306a36Sopenharmony_ci asm volatile ("nop"); 11362306a36Sopenharmony_ci if (!t) 11462306a36Sopenharmony_ci goto cntr_err; 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_ci /* register counter1 clocksource and event device */ 11762306a36Sopenharmony_ci clocksource_register_hz(&au1x_counter1_clocksource, 32768); 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_ci cd->shift = 32; 12062306a36Sopenharmony_ci cd->mult = div_sc(32768, NSEC_PER_SEC, cd->shift); 12162306a36Sopenharmony_ci cd->max_delta_ns = clockevent_delta2ns(0xffffffff, cd); 12262306a36Sopenharmony_ci cd->max_delta_ticks = 0xffffffff; 12362306a36Sopenharmony_ci cd->min_delta_ns = clockevent_delta2ns(9, cd); 12462306a36Sopenharmony_ci cd->min_delta_ticks = 9; /* ~0.28ms */ 12562306a36Sopenharmony_ci clockevents_register_device(cd); 12662306a36Sopenharmony_ci if (request_irq(m2int, au1x_rtcmatch2_irq, IRQF_TIMER, "timer", 12762306a36Sopenharmony_ci &au1x_rtcmatch2_clockdev)) 12862306a36Sopenharmony_ci pr_err("Failed to register timer interrupt\n"); 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_ci printk(KERN_INFO "Alchemy clocksource installed\n"); 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_ci return 0; 13362306a36Sopenharmony_ci 13462306a36Sopenharmony_cicntr_err: 13562306a36Sopenharmony_ci return -1; 13662306a36Sopenharmony_ci} 13762306a36Sopenharmony_ci 13862306a36Sopenharmony_cistatic int alchemy_m2inttab[] __initdata = { 13962306a36Sopenharmony_ci AU1000_RTC_MATCH2_INT, 14062306a36Sopenharmony_ci AU1500_RTC_MATCH2_INT, 14162306a36Sopenharmony_ci AU1100_RTC_MATCH2_INT, 14262306a36Sopenharmony_ci AU1550_RTC_MATCH2_INT, 14362306a36Sopenharmony_ci AU1200_RTC_MATCH2_INT, 14462306a36Sopenharmony_ci AU1300_RTC_MATCH2_INT, 14562306a36Sopenharmony_ci}; 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_civoid __init plat_time_init(void) 14862306a36Sopenharmony_ci{ 14962306a36Sopenharmony_ci int t; 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_ci t = alchemy_get_cputype(); 15262306a36Sopenharmony_ci if (t == ALCHEMY_CPU_UNKNOWN || 15362306a36Sopenharmony_ci alchemy_time_init(alchemy_m2inttab[t])) 15462306a36Sopenharmony_ci cpu_wait = NULL; /* wait doesn't work with r4k timer */ 15562306a36Sopenharmony_ci} 156