18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * System timer for CSR SiRFprimaII
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Copyright (c) 2011 Cambridge Silicon Radio Limited, a CSR plc group company.
68c2ecf20Sopenharmony_ci */
78c2ecf20Sopenharmony_ci
88c2ecf20Sopenharmony_ci#include <linux/kernel.h>
98c2ecf20Sopenharmony_ci#include <linux/interrupt.h>
108c2ecf20Sopenharmony_ci#include <linux/clockchips.h>
118c2ecf20Sopenharmony_ci#include <linux/clocksource.h>
128c2ecf20Sopenharmony_ci#include <linux/bitops.h>
138c2ecf20Sopenharmony_ci#include <linux/irq.h>
148c2ecf20Sopenharmony_ci#include <linux/clk.h>
158c2ecf20Sopenharmony_ci#include <linux/err.h>
168c2ecf20Sopenharmony_ci#include <linux/slab.h>
178c2ecf20Sopenharmony_ci#include <linux/of.h>
188c2ecf20Sopenharmony_ci#include <linux/of_irq.h>
198c2ecf20Sopenharmony_ci#include <linux/of_address.h>
208c2ecf20Sopenharmony_ci#include <linux/sched_clock.h>
218c2ecf20Sopenharmony_ci
228c2ecf20Sopenharmony_ci#define PRIMA2_CLOCK_FREQ 1000000
238c2ecf20Sopenharmony_ci
248c2ecf20Sopenharmony_ci#define SIRFSOC_TIMER_COUNTER_LO	0x0000
258c2ecf20Sopenharmony_ci#define SIRFSOC_TIMER_COUNTER_HI	0x0004
268c2ecf20Sopenharmony_ci#define SIRFSOC_TIMER_MATCH_0		0x0008
278c2ecf20Sopenharmony_ci#define SIRFSOC_TIMER_MATCH_1		0x000C
288c2ecf20Sopenharmony_ci#define SIRFSOC_TIMER_MATCH_2		0x0010
298c2ecf20Sopenharmony_ci#define SIRFSOC_TIMER_MATCH_3		0x0014
308c2ecf20Sopenharmony_ci#define SIRFSOC_TIMER_MATCH_4		0x0018
318c2ecf20Sopenharmony_ci#define SIRFSOC_TIMER_MATCH_5		0x001C
328c2ecf20Sopenharmony_ci#define SIRFSOC_TIMER_STATUS		0x0020
338c2ecf20Sopenharmony_ci#define SIRFSOC_TIMER_INT_EN		0x0024
348c2ecf20Sopenharmony_ci#define SIRFSOC_TIMER_WATCHDOG_EN	0x0028
358c2ecf20Sopenharmony_ci#define SIRFSOC_TIMER_DIV		0x002C
368c2ecf20Sopenharmony_ci#define SIRFSOC_TIMER_LATCH		0x0030
378c2ecf20Sopenharmony_ci#define SIRFSOC_TIMER_LATCHED_LO	0x0034
388c2ecf20Sopenharmony_ci#define SIRFSOC_TIMER_LATCHED_HI	0x0038
398c2ecf20Sopenharmony_ci
408c2ecf20Sopenharmony_ci#define SIRFSOC_TIMER_WDT_INDEX		5
418c2ecf20Sopenharmony_ci
428c2ecf20Sopenharmony_ci#define SIRFSOC_TIMER_LATCH_BIT	 BIT(0)
438c2ecf20Sopenharmony_ci
448c2ecf20Sopenharmony_ci#define SIRFSOC_TIMER_REG_CNT 11
458c2ecf20Sopenharmony_ci
468c2ecf20Sopenharmony_cistatic const u32 sirfsoc_timer_reg_list[SIRFSOC_TIMER_REG_CNT] = {
478c2ecf20Sopenharmony_ci	SIRFSOC_TIMER_MATCH_0, SIRFSOC_TIMER_MATCH_1, SIRFSOC_TIMER_MATCH_2,
488c2ecf20Sopenharmony_ci	SIRFSOC_TIMER_MATCH_3, SIRFSOC_TIMER_MATCH_4, SIRFSOC_TIMER_MATCH_5,
498c2ecf20Sopenharmony_ci	SIRFSOC_TIMER_INT_EN, SIRFSOC_TIMER_WATCHDOG_EN, SIRFSOC_TIMER_DIV,
508c2ecf20Sopenharmony_ci	SIRFSOC_TIMER_LATCHED_LO, SIRFSOC_TIMER_LATCHED_HI,
518c2ecf20Sopenharmony_ci};
528c2ecf20Sopenharmony_ci
538c2ecf20Sopenharmony_cistatic u32 sirfsoc_timer_reg_val[SIRFSOC_TIMER_REG_CNT];
548c2ecf20Sopenharmony_ci
558c2ecf20Sopenharmony_cistatic void __iomem *sirfsoc_timer_base;
568c2ecf20Sopenharmony_ci
578c2ecf20Sopenharmony_ci/* timer0 interrupt handler */
588c2ecf20Sopenharmony_cistatic irqreturn_t sirfsoc_timer_interrupt(int irq, void *dev_id)
598c2ecf20Sopenharmony_ci{
608c2ecf20Sopenharmony_ci	struct clock_event_device *ce = dev_id;
618c2ecf20Sopenharmony_ci
628c2ecf20Sopenharmony_ci	WARN_ON(!(readl_relaxed(sirfsoc_timer_base + SIRFSOC_TIMER_STATUS) &
638c2ecf20Sopenharmony_ci		BIT(0)));
648c2ecf20Sopenharmony_ci
658c2ecf20Sopenharmony_ci	/* clear timer0 interrupt */
668c2ecf20Sopenharmony_ci	writel_relaxed(BIT(0), sirfsoc_timer_base + SIRFSOC_TIMER_STATUS);
678c2ecf20Sopenharmony_ci
688c2ecf20Sopenharmony_ci	ce->event_handler(ce);
698c2ecf20Sopenharmony_ci
708c2ecf20Sopenharmony_ci	return IRQ_HANDLED;
718c2ecf20Sopenharmony_ci}
728c2ecf20Sopenharmony_ci
738c2ecf20Sopenharmony_ci/* read 64-bit timer counter */
748c2ecf20Sopenharmony_cistatic u64 notrace sirfsoc_timer_read(struct clocksource *cs)
758c2ecf20Sopenharmony_ci{
768c2ecf20Sopenharmony_ci	u64 cycles;
778c2ecf20Sopenharmony_ci
788c2ecf20Sopenharmony_ci	/* latch the 64-bit timer counter */
798c2ecf20Sopenharmony_ci	writel_relaxed(SIRFSOC_TIMER_LATCH_BIT,
808c2ecf20Sopenharmony_ci		sirfsoc_timer_base + SIRFSOC_TIMER_LATCH);
818c2ecf20Sopenharmony_ci	cycles = readl_relaxed(sirfsoc_timer_base + SIRFSOC_TIMER_LATCHED_HI);
828c2ecf20Sopenharmony_ci	cycles = (cycles << 32) |
838c2ecf20Sopenharmony_ci		readl_relaxed(sirfsoc_timer_base + SIRFSOC_TIMER_LATCHED_LO);
848c2ecf20Sopenharmony_ci
858c2ecf20Sopenharmony_ci	return cycles;
868c2ecf20Sopenharmony_ci}
878c2ecf20Sopenharmony_ci
888c2ecf20Sopenharmony_cistatic int sirfsoc_timer_set_next_event(unsigned long delta,
898c2ecf20Sopenharmony_ci	struct clock_event_device *ce)
908c2ecf20Sopenharmony_ci{
918c2ecf20Sopenharmony_ci	unsigned long now, next;
928c2ecf20Sopenharmony_ci
938c2ecf20Sopenharmony_ci	writel_relaxed(SIRFSOC_TIMER_LATCH_BIT,
948c2ecf20Sopenharmony_ci		sirfsoc_timer_base + SIRFSOC_TIMER_LATCH);
958c2ecf20Sopenharmony_ci	now = readl_relaxed(sirfsoc_timer_base + SIRFSOC_TIMER_LATCHED_LO);
968c2ecf20Sopenharmony_ci	next = now + delta;
978c2ecf20Sopenharmony_ci	writel_relaxed(next, sirfsoc_timer_base + SIRFSOC_TIMER_MATCH_0);
988c2ecf20Sopenharmony_ci	writel_relaxed(SIRFSOC_TIMER_LATCH_BIT,
998c2ecf20Sopenharmony_ci		sirfsoc_timer_base + SIRFSOC_TIMER_LATCH);
1008c2ecf20Sopenharmony_ci	now = readl_relaxed(sirfsoc_timer_base + SIRFSOC_TIMER_LATCHED_LO);
1018c2ecf20Sopenharmony_ci
1028c2ecf20Sopenharmony_ci	return next - now > delta ? -ETIME : 0;
1038c2ecf20Sopenharmony_ci}
1048c2ecf20Sopenharmony_ci
1058c2ecf20Sopenharmony_cistatic int sirfsoc_timer_shutdown(struct clock_event_device *evt)
1068c2ecf20Sopenharmony_ci{
1078c2ecf20Sopenharmony_ci	u32 val = readl_relaxed(sirfsoc_timer_base + SIRFSOC_TIMER_INT_EN);
1088c2ecf20Sopenharmony_ci
1098c2ecf20Sopenharmony_ci	writel_relaxed(val & ~BIT(0),
1108c2ecf20Sopenharmony_ci		       sirfsoc_timer_base + SIRFSOC_TIMER_INT_EN);
1118c2ecf20Sopenharmony_ci	return 0;
1128c2ecf20Sopenharmony_ci}
1138c2ecf20Sopenharmony_ci
1148c2ecf20Sopenharmony_cistatic int sirfsoc_timer_set_oneshot(struct clock_event_device *evt)
1158c2ecf20Sopenharmony_ci{
1168c2ecf20Sopenharmony_ci	u32 val = readl_relaxed(sirfsoc_timer_base + SIRFSOC_TIMER_INT_EN);
1178c2ecf20Sopenharmony_ci
1188c2ecf20Sopenharmony_ci	writel_relaxed(val | BIT(0), sirfsoc_timer_base + SIRFSOC_TIMER_INT_EN);
1198c2ecf20Sopenharmony_ci	return 0;
1208c2ecf20Sopenharmony_ci}
1218c2ecf20Sopenharmony_ci
1228c2ecf20Sopenharmony_cistatic void sirfsoc_clocksource_suspend(struct clocksource *cs)
1238c2ecf20Sopenharmony_ci{
1248c2ecf20Sopenharmony_ci	int i;
1258c2ecf20Sopenharmony_ci
1268c2ecf20Sopenharmony_ci	writel_relaxed(SIRFSOC_TIMER_LATCH_BIT,
1278c2ecf20Sopenharmony_ci		sirfsoc_timer_base + SIRFSOC_TIMER_LATCH);
1288c2ecf20Sopenharmony_ci
1298c2ecf20Sopenharmony_ci	for (i = 0; i < SIRFSOC_TIMER_REG_CNT; i++)
1308c2ecf20Sopenharmony_ci		sirfsoc_timer_reg_val[i] =
1318c2ecf20Sopenharmony_ci			readl_relaxed(sirfsoc_timer_base +
1328c2ecf20Sopenharmony_ci				sirfsoc_timer_reg_list[i]);
1338c2ecf20Sopenharmony_ci}
1348c2ecf20Sopenharmony_ci
1358c2ecf20Sopenharmony_cistatic void sirfsoc_clocksource_resume(struct clocksource *cs)
1368c2ecf20Sopenharmony_ci{
1378c2ecf20Sopenharmony_ci	int i;
1388c2ecf20Sopenharmony_ci
1398c2ecf20Sopenharmony_ci	for (i = 0; i < SIRFSOC_TIMER_REG_CNT - 2; i++)
1408c2ecf20Sopenharmony_ci		writel_relaxed(sirfsoc_timer_reg_val[i],
1418c2ecf20Sopenharmony_ci			sirfsoc_timer_base + sirfsoc_timer_reg_list[i]);
1428c2ecf20Sopenharmony_ci
1438c2ecf20Sopenharmony_ci	writel_relaxed(sirfsoc_timer_reg_val[SIRFSOC_TIMER_REG_CNT - 2],
1448c2ecf20Sopenharmony_ci		sirfsoc_timer_base + SIRFSOC_TIMER_COUNTER_LO);
1458c2ecf20Sopenharmony_ci	writel_relaxed(sirfsoc_timer_reg_val[SIRFSOC_TIMER_REG_CNT - 1],
1468c2ecf20Sopenharmony_ci		sirfsoc_timer_base + SIRFSOC_TIMER_COUNTER_HI);
1478c2ecf20Sopenharmony_ci}
1488c2ecf20Sopenharmony_ci
1498c2ecf20Sopenharmony_cistatic struct clock_event_device sirfsoc_clockevent = {
1508c2ecf20Sopenharmony_ci	.name = "sirfsoc_clockevent",
1518c2ecf20Sopenharmony_ci	.rating = 200,
1528c2ecf20Sopenharmony_ci	.features = CLOCK_EVT_FEAT_ONESHOT,
1538c2ecf20Sopenharmony_ci	.set_state_shutdown = sirfsoc_timer_shutdown,
1548c2ecf20Sopenharmony_ci	.set_state_oneshot = sirfsoc_timer_set_oneshot,
1558c2ecf20Sopenharmony_ci	.set_next_event = sirfsoc_timer_set_next_event,
1568c2ecf20Sopenharmony_ci};
1578c2ecf20Sopenharmony_ci
1588c2ecf20Sopenharmony_cistatic struct clocksource sirfsoc_clocksource = {
1598c2ecf20Sopenharmony_ci	.name = "sirfsoc_clocksource",
1608c2ecf20Sopenharmony_ci	.rating = 200,
1618c2ecf20Sopenharmony_ci	.mask = CLOCKSOURCE_MASK(64),
1628c2ecf20Sopenharmony_ci	.flags = CLOCK_SOURCE_IS_CONTINUOUS,
1638c2ecf20Sopenharmony_ci	.read = sirfsoc_timer_read,
1648c2ecf20Sopenharmony_ci	.suspend = sirfsoc_clocksource_suspend,
1658c2ecf20Sopenharmony_ci	.resume = sirfsoc_clocksource_resume,
1668c2ecf20Sopenharmony_ci};
1678c2ecf20Sopenharmony_ci
1688c2ecf20Sopenharmony_ci/* Overwrite weak default sched_clock with more precise one */
1698c2ecf20Sopenharmony_cistatic u64 notrace sirfsoc_read_sched_clock(void)
1708c2ecf20Sopenharmony_ci{
1718c2ecf20Sopenharmony_ci	return sirfsoc_timer_read(NULL);
1728c2ecf20Sopenharmony_ci}
1738c2ecf20Sopenharmony_ci
1748c2ecf20Sopenharmony_cistatic void __init sirfsoc_clockevent_init(void)
1758c2ecf20Sopenharmony_ci{
1768c2ecf20Sopenharmony_ci	sirfsoc_clockevent.cpumask = cpumask_of(0);
1778c2ecf20Sopenharmony_ci	clockevents_config_and_register(&sirfsoc_clockevent, PRIMA2_CLOCK_FREQ,
1788c2ecf20Sopenharmony_ci					2, -2);
1798c2ecf20Sopenharmony_ci}
1808c2ecf20Sopenharmony_ci
1818c2ecf20Sopenharmony_ci/* initialize the kernel jiffy timer source */
1828c2ecf20Sopenharmony_cistatic int __init sirfsoc_prima2_timer_init(struct device_node *np)
1838c2ecf20Sopenharmony_ci{
1848c2ecf20Sopenharmony_ci	unsigned long rate;
1858c2ecf20Sopenharmony_ci	unsigned int irq;
1868c2ecf20Sopenharmony_ci	struct clk *clk;
1878c2ecf20Sopenharmony_ci	int ret;
1888c2ecf20Sopenharmony_ci
1898c2ecf20Sopenharmony_ci	clk = of_clk_get(np, 0);
1908c2ecf20Sopenharmony_ci	if (IS_ERR(clk)) {
1918c2ecf20Sopenharmony_ci		pr_err("Failed to get clock\n");
1928c2ecf20Sopenharmony_ci		return PTR_ERR(clk);
1938c2ecf20Sopenharmony_ci	}
1948c2ecf20Sopenharmony_ci
1958c2ecf20Sopenharmony_ci	ret = clk_prepare_enable(clk);
1968c2ecf20Sopenharmony_ci	if (ret) {
1978c2ecf20Sopenharmony_ci		pr_err("Failed to enable clock\n");
1988c2ecf20Sopenharmony_ci		return ret;
1998c2ecf20Sopenharmony_ci	}
2008c2ecf20Sopenharmony_ci
2018c2ecf20Sopenharmony_ci	rate = clk_get_rate(clk);
2028c2ecf20Sopenharmony_ci
2038c2ecf20Sopenharmony_ci	if (rate < PRIMA2_CLOCK_FREQ || rate % PRIMA2_CLOCK_FREQ) {
2048c2ecf20Sopenharmony_ci		pr_err("Invalid clock rate\n");
2058c2ecf20Sopenharmony_ci		return -EINVAL;
2068c2ecf20Sopenharmony_ci	}
2078c2ecf20Sopenharmony_ci
2088c2ecf20Sopenharmony_ci	sirfsoc_timer_base = of_iomap(np, 0);
2098c2ecf20Sopenharmony_ci	if (!sirfsoc_timer_base) {
2108c2ecf20Sopenharmony_ci		pr_err("unable to map timer cpu registers\n");
2118c2ecf20Sopenharmony_ci		return -ENXIO;
2128c2ecf20Sopenharmony_ci	}
2138c2ecf20Sopenharmony_ci
2148c2ecf20Sopenharmony_ci	irq = irq_of_parse_and_map(np, 0);
2158c2ecf20Sopenharmony_ci
2168c2ecf20Sopenharmony_ci	writel_relaxed(rate / PRIMA2_CLOCK_FREQ / 2 - 1,
2178c2ecf20Sopenharmony_ci		sirfsoc_timer_base + SIRFSOC_TIMER_DIV);
2188c2ecf20Sopenharmony_ci	writel_relaxed(0, sirfsoc_timer_base + SIRFSOC_TIMER_COUNTER_LO);
2198c2ecf20Sopenharmony_ci	writel_relaxed(0, sirfsoc_timer_base + SIRFSOC_TIMER_COUNTER_HI);
2208c2ecf20Sopenharmony_ci	writel_relaxed(BIT(0), sirfsoc_timer_base + SIRFSOC_TIMER_STATUS);
2218c2ecf20Sopenharmony_ci
2228c2ecf20Sopenharmony_ci	ret = clocksource_register_hz(&sirfsoc_clocksource, PRIMA2_CLOCK_FREQ);
2238c2ecf20Sopenharmony_ci	if (ret) {
2248c2ecf20Sopenharmony_ci		pr_err("Failed to register clocksource\n");
2258c2ecf20Sopenharmony_ci		return ret;
2268c2ecf20Sopenharmony_ci	}
2278c2ecf20Sopenharmony_ci
2288c2ecf20Sopenharmony_ci	sched_clock_register(sirfsoc_read_sched_clock, 64, PRIMA2_CLOCK_FREQ);
2298c2ecf20Sopenharmony_ci
2308c2ecf20Sopenharmony_ci	ret = request_irq(irq, sirfsoc_timer_interrupt, IRQF_TIMER,
2318c2ecf20Sopenharmony_ci			  "sirfsoc_timer0", &sirfsoc_clockevent);
2328c2ecf20Sopenharmony_ci	if (ret) {
2338c2ecf20Sopenharmony_ci		pr_err("Failed to setup irq\n");
2348c2ecf20Sopenharmony_ci		return ret;
2358c2ecf20Sopenharmony_ci	}
2368c2ecf20Sopenharmony_ci
2378c2ecf20Sopenharmony_ci	sirfsoc_clockevent_init();
2388c2ecf20Sopenharmony_ci
2398c2ecf20Sopenharmony_ci	return 0;
2408c2ecf20Sopenharmony_ci}
2418c2ecf20Sopenharmony_ciTIMER_OF_DECLARE(sirfsoc_prima2_timer,
2428c2ecf20Sopenharmony_ci	"sirf,prima2-tick", sirfsoc_prima2_timer_init);
243