18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
28c2ecf20Sopenharmony_ci// Copyright (C) 2018 Hangzhou C-SKY Microsystems co.,ltd.
38c2ecf20Sopenharmony_ci
48c2ecf20Sopenharmony_ci#include <linux/init.h>
58c2ecf20Sopenharmony_ci#include <linux/interrupt.h>
68c2ecf20Sopenharmony_ci#include <linux/sched_clock.h>
78c2ecf20Sopenharmony_ci
88c2ecf20Sopenharmony_ci#include "timer-of.h"
98c2ecf20Sopenharmony_ci
108c2ecf20Sopenharmony_ci#define CLKSRC_OFFSET	0x40
118c2ecf20Sopenharmony_ci
128c2ecf20Sopenharmony_ci#define TIMER_STATUS	0x00
138c2ecf20Sopenharmony_ci#define TIMER_VALUE	0x04
148c2ecf20Sopenharmony_ci#define TIMER_CONTRL	0x10
158c2ecf20Sopenharmony_ci#define TIMER_CONFIG	0x20
168c2ecf20Sopenharmony_ci#define TIMER_DIV	0x24
178c2ecf20Sopenharmony_ci#define TIMER_INI	0x28
188c2ecf20Sopenharmony_ci
198c2ecf20Sopenharmony_ci#define GX6605S_STATUS_CLR	BIT(0)
208c2ecf20Sopenharmony_ci#define GX6605S_CONTRL_RST	BIT(0)
218c2ecf20Sopenharmony_ci#define GX6605S_CONTRL_START	BIT(1)
228c2ecf20Sopenharmony_ci#define GX6605S_CONFIG_EN	BIT(0)
238c2ecf20Sopenharmony_ci#define GX6605S_CONFIG_IRQ_EN	BIT(1)
248c2ecf20Sopenharmony_ci
258c2ecf20Sopenharmony_cistatic irqreturn_t gx6605s_timer_interrupt(int irq, void *dev)
268c2ecf20Sopenharmony_ci{
278c2ecf20Sopenharmony_ci	struct clock_event_device *ce = dev;
288c2ecf20Sopenharmony_ci	void __iomem *base = timer_of_base(to_timer_of(ce));
298c2ecf20Sopenharmony_ci
308c2ecf20Sopenharmony_ci	writel_relaxed(GX6605S_STATUS_CLR, base + TIMER_STATUS);
318c2ecf20Sopenharmony_ci	writel_relaxed(0, base + TIMER_INI);
328c2ecf20Sopenharmony_ci
338c2ecf20Sopenharmony_ci	ce->event_handler(ce);
348c2ecf20Sopenharmony_ci
358c2ecf20Sopenharmony_ci	return IRQ_HANDLED;
368c2ecf20Sopenharmony_ci}
378c2ecf20Sopenharmony_ci
388c2ecf20Sopenharmony_cistatic int gx6605s_timer_set_oneshot(struct clock_event_device *ce)
398c2ecf20Sopenharmony_ci{
408c2ecf20Sopenharmony_ci	void __iomem *base = timer_of_base(to_timer_of(ce));
418c2ecf20Sopenharmony_ci
428c2ecf20Sopenharmony_ci	/* reset and stop counter */
438c2ecf20Sopenharmony_ci	writel_relaxed(GX6605S_CONTRL_RST, base + TIMER_CONTRL);
448c2ecf20Sopenharmony_ci
458c2ecf20Sopenharmony_ci	/* enable with irq and start */
468c2ecf20Sopenharmony_ci	writel_relaxed(GX6605S_CONFIG_EN | GX6605S_CONFIG_IRQ_EN,
478c2ecf20Sopenharmony_ci		       base + TIMER_CONFIG);
488c2ecf20Sopenharmony_ci
498c2ecf20Sopenharmony_ci	return 0;
508c2ecf20Sopenharmony_ci}
518c2ecf20Sopenharmony_ci
528c2ecf20Sopenharmony_cistatic int gx6605s_timer_set_next_event(unsigned long delta,
538c2ecf20Sopenharmony_ci					struct clock_event_device *ce)
548c2ecf20Sopenharmony_ci{
558c2ecf20Sopenharmony_ci	void __iomem *base = timer_of_base(to_timer_of(ce));
568c2ecf20Sopenharmony_ci
578c2ecf20Sopenharmony_ci	/* use reset to pause timer */
588c2ecf20Sopenharmony_ci	writel_relaxed(GX6605S_CONTRL_RST, base + TIMER_CONTRL);
598c2ecf20Sopenharmony_ci
608c2ecf20Sopenharmony_ci	/* config next timeout value */
618c2ecf20Sopenharmony_ci	writel_relaxed(ULONG_MAX - delta, base + TIMER_INI);
628c2ecf20Sopenharmony_ci	writel_relaxed(GX6605S_CONTRL_START, base + TIMER_CONTRL);
638c2ecf20Sopenharmony_ci
648c2ecf20Sopenharmony_ci	return 0;
658c2ecf20Sopenharmony_ci}
668c2ecf20Sopenharmony_ci
678c2ecf20Sopenharmony_cistatic int gx6605s_timer_shutdown(struct clock_event_device *ce)
688c2ecf20Sopenharmony_ci{
698c2ecf20Sopenharmony_ci	void __iomem *base = timer_of_base(to_timer_of(ce));
708c2ecf20Sopenharmony_ci
718c2ecf20Sopenharmony_ci	writel_relaxed(0, base + TIMER_CONTRL);
728c2ecf20Sopenharmony_ci	writel_relaxed(0, base + TIMER_CONFIG);
738c2ecf20Sopenharmony_ci
748c2ecf20Sopenharmony_ci	return 0;
758c2ecf20Sopenharmony_ci}
768c2ecf20Sopenharmony_ci
778c2ecf20Sopenharmony_cistatic struct timer_of to = {
788c2ecf20Sopenharmony_ci	.flags = TIMER_OF_IRQ | TIMER_OF_BASE | TIMER_OF_CLOCK,
798c2ecf20Sopenharmony_ci	.clkevt = {
808c2ecf20Sopenharmony_ci		.rating			= 300,
818c2ecf20Sopenharmony_ci		.features		= CLOCK_EVT_FEAT_DYNIRQ |
828c2ecf20Sopenharmony_ci					  CLOCK_EVT_FEAT_ONESHOT,
838c2ecf20Sopenharmony_ci		.set_state_shutdown	= gx6605s_timer_shutdown,
848c2ecf20Sopenharmony_ci		.set_state_oneshot	= gx6605s_timer_set_oneshot,
858c2ecf20Sopenharmony_ci		.set_next_event		= gx6605s_timer_set_next_event,
868c2ecf20Sopenharmony_ci		.cpumask		= cpu_possible_mask,
878c2ecf20Sopenharmony_ci	},
888c2ecf20Sopenharmony_ci	.of_irq = {
898c2ecf20Sopenharmony_ci		.handler		= gx6605s_timer_interrupt,
908c2ecf20Sopenharmony_ci		.flags			= IRQF_TIMER | IRQF_IRQPOLL,
918c2ecf20Sopenharmony_ci	},
928c2ecf20Sopenharmony_ci};
938c2ecf20Sopenharmony_ci
948c2ecf20Sopenharmony_cistatic u64 notrace gx6605s_sched_clock_read(void)
958c2ecf20Sopenharmony_ci{
968c2ecf20Sopenharmony_ci	void __iomem *base;
978c2ecf20Sopenharmony_ci
988c2ecf20Sopenharmony_ci	base = timer_of_base(&to) + CLKSRC_OFFSET;
998c2ecf20Sopenharmony_ci
1008c2ecf20Sopenharmony_ci	return (u64)readl_relaxed(base + TIMER_VALUE);
1018c2ecf20Sopenharmony_ci}
1028c2ecf20Sopenharmony_ci
1038c2ecf20Sopenharmony_cistatic void gx6605s_clkevt_init(void __iomem *base)
1048c2ecf20Sopenharmony_ci{
1058c2ecf20Sopenharmony_ci	writel_relaxed(0, base + TIMER_DIV);
1068c2ecf20Sopenharmony_ci	writel_relaxed(0, base + TIMER_CONFIG);
1078c2ecf20Sopenharmony_ci
1088c2ecf20Sopenharmony_ci	clockevents_config_and_register(&to.clkevt, timer_of_rate(&to), 2,
1098c2ecf20Sopenharmony_ci					ULONG_MAX);
1108c2ecf20Sopenharmony_ci}
1118c2ecf20Sopenharmony_ci
1128c2ecf20Sopenharmony_cistatic int gx6605s_clksrc_init(void __iomem *base)
1138c2ecf20Sopenharmony_ci{
1148c2ecf20Sopenharmony_ci	writel_relaxed(0, base + TIMER_DIV);
1158c2ecf20Sopenharmony_ci	writel_relaxed(0, base + TIMER_INI);
1168c2ecf20Sopenharmony_ci
1178c2ecf20Sopenharmony_ci	writel_relaxed(GX6605S_CONTRL_RST, base + TIMER_CONTRL);
1188c2ecf20Sopenharmony_ci
1198c2ecf20Sopenharmony_ci	writel_relaxed(GX6605S_CONFIG_EN, base + TIMER_CONFIG);
1208c2ecf20Sopenharmony_ci
1218c2ecf20Sopenharmony_ci	writel_relaxed(GX6605S_CONTRL_START, base + TIMER_CONTRL);
1228c2ecf20Sopenharmony_ci
1238c2ecf20Sopenharmony_ci	sched_clock_register(gx6605s_sched_clock_read, 32, timer_of_rate(&to));
1248c2ecf20Sopenharmony_ci
1258c2ecf20Sopenharmony_ci	return clocksource_mmio_init(base + TIMER_VALUE, "gx6605s",
1268c2ecf20Sopenharmony_ci			timer_of_rate(&to), 200, 32, clocksource_mmio_readl_up);
1278c2ecf20Sopenharmony_ci}
1288c2ecf20Sopenharmony_ci
1298c2ecf20Sopenharmony_cistatic int __init gx6605s_timer_init(struct device_node *np)
1308c2ecf20Sopenharmony_ci{
1318c2ecf20Sopenharmony_ci	int ret;
1328c2ecf20Sopenharmony_ci
1338c2ecf20Sopenharmony_ci	/*
1348c2ecf20Sopenharmony_ci	 * The timer driver is for nationalchip gx6605s SOC and there are two
1358c2ecf20Sopenharmony_ci	 * same timer in gx6605s. We use one for clkevt and another for clksrc.
1368c2ecf20Sopenharmony_ci	 *
1378c2ecf20Sopenharmony_ci	 * The timer is mmio map to access, so we need give mmio address in dts.
1388c2ecf20Sopenharmony_ci	 *
1398c2ecf20Sopenharmony_ci	 * It provides a 32bit countup timer and interrupt will be caused by
1408c2ecf20Sopenharmony_ci	 * count-overflow.
1418c2ecf20Sopenharmony_ci	 * So we need set-next-event by ULONG_MAX - delta in TIMER_INI reg.
1428c2ecf20Sopenharmony_ci	 *
1438c2ecf20Sopenharmony_ci	 * The counter at 0x0  offset is clock event.
1448c2ecf20Sopenharmony_ci	 * The counter at 0x40 offset is clock source.
1458c2ecf20Sopenharmony_ci	 * They are the same in hardware, just different used by driver.
1468c2ecf20Sopenharmony_ci	 */
1478c2ecf20Sopenharmony_ci	ret = timer_of_init(np, &to);
1488c2ecf20Sopenharmony_ci	if (ret)
1498c2ecf20Sopenharmony_ci		return ret;
1508c2ecf20Sopenharmony_ci
1518c2ecf20Sopenharmony_ci	gx6605s_clkevt_init(timer_of_base(&to));
1528c2ecf20Sopenharmony_ci
1538c2ecf20Sopenharmony_ci	return gx6605s_clksrc_init(timer_of_base(&to) + CLKSRC_OFFSET);
1548c2ecf20Sopenharmony_ci}
1558c2ecf20Sopenharmony_ciTIMER_OF_DECLARE(csky_gx6605s_timer, "csky,gx6605s-timer", gx6605s_timer_init);
156