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