18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci *  arch/arm/mach-vt8500/timer.c
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci *  Copyright (C) 2012 Tony Prisk <linux@prisktech.co.nz>
68c2ecf20Sopenharmony_ci *  Copyright (C) 2010 Alexey Charkov <alchark@gmail.com>
78c2ecf20Sopenharmony_ci */
88c2ecf20Sopenharmony_ci
98c2ecf20Sopenharmony_ci/*
108c2ecf20Sopenharmony_ci * This file is copied and modified from the original timer.c provided by
118c2ecf20Sopenharmony_ci * Alexey Charkov. Minor changes have been made for Device Tree Support.
128c2ecf20Sopenharmony_ci */
138c2ecf20Sopenharmony_ci
148c2ecf20Sopenharmony_ci#include <linux/io.h>
158c2ecf20Sopenharmony_ci#include <linux/irq.h>
168c2ecf20Sopenharmony_ci#include <linux/interrupt.h>
178c2ecf20Sopenharmony_ci#include <linux/clocksource.h>
188c2ecf20Sopenharmony_ci#include <linux/clockchips.h>
198c2ecf20Sopenharmony_ci#include <linux/delay.h>
208c2ecf20Sopenharmony_ci
218c2ecf20Sopenharmony_ci#include <linux/of.h>
228c2ecf20Sopenharmony_ci#include <linux/of_address.h>
238c2ecf20Sopenharmony_ci#include <linux/of_irq.h>
248c2ecf20Sopenharmony_ci
258c2ecf20Sopenharmony_ci#define VT8500_TIMER_OFFSET	0x0100
268c2ecf20Sopenharmony_ci#define VT8500_TIMER_HZ		3000000
278c2ecf20Sopenharmony_ci#define TIMER_MATCH_VAL		0x0000
288c2ecf20Sopenharmony_ci#define TIMER_COUNT_VAL		0x0010
298c2ecf20Sopenharmony_ci#define TIMER_STATUS_VAL	0x0014
308c2ecf20Sopenharmony_ci#define TIMER_IER_VAL		0x001c		/* interrupt enable */
318c2ecf20Sopenharmony_ci#define TIMER_CTRL_VAL		0x0020
328c2ecf20Sopenharmony_ci#define TIMER_AS_VAL		0x0024		/* access status */
338c2ecf20Sopenharmony_ci#define TIMER_COUNT_R_ACTIVE	(1 << 5)	/* not ready for read */
348c2ecf20Sopenharmony_ci#define TIMER_COUNT_W_ACTIVE	(1 << 4)	/* not ready for write */
358c2ecf20Sopenharmony_ci#define TIMER_MATCH_W_ACTIVE	(1 << 0)	/* not ready for write */
368c2ecf20Sopenharmony_ci
378c2ecf20Sopenharmony_ci#define msecs_to_loops(t) (loops_per_jiffy / 1000 * HZ * t)
388c2ecf20Sopenharmony_ci
398c2ecf20Sopenharmony_ci#define MIN_OSCR_DELTA		16
408c2ecf20Sopenharmony_ci
418c2ecf20Sopenharmony_cistatic void __iomem *regbase;
428c2ecf20Sopenharmony_ci
438c2ecf20Sopenharmony_cistatic u64 vt8500_timer_read(struct clocksource *cs)
448c2ecf20Sopenharmony_ci{
458c2ecf20Sopenharmony_ci	int loops = msecs_to_loops(10);
468c2ecf20Sopenharmony_ci	writel(3, regbase + TIMER_CTRL_VAL);
478c2ecf20Sopenharmony_ci	while ((readl((regbase + TIMER_AS_VAL)) & TIMER_COUNT_R_ACTIVE)
488c2ecf20Sopenharmony_ci						&& --loops)
498c2ecf20Sopenharmony_ci		cpu_relax();
508c2ecf20Sopenharmony_ci	return readl(regbase + TIMER_COUNT_VAL);
518c2ecf20Sopenharmony_ci}
528c2ecf20Sopenharmony_ci
538c2ecf20Sopenharmony_cistatic struct clocksource clocksource = {
548c2ecf20Sopenharmony_ci	.name           = "vt8500_timer",
558c2ecf20Sopenharmony_ci	.rating         = 200,
568c2ecf20Sopenharmony_ci	.read           = vt8500_timer_read,
578c2ecf20Sopenharmony_ci	.mask           = CLOCKSOURCE_MASK(32),
588c2ecf20Sopenharmony_ci	.flags          = CLOCK_SOURCE_IS_CONTINUOUS,
598c2ecf20Sopenharmony_ci};
608c2ecf20Sopenharmony_ci
618c2ecf20Sopenharmony_cistatic int vt8500_timer_set_next_event(unsigned long cycles,
628c2ecf20Sopenharmony_ci				    struct clock_event_device *evt)
638c2ecf20Sopenharmony_ci{
648c2ecf20Sopenharmony_ci	int loops = msecs_to_loops(10);
658c2ecf20Sopenharmony_ci	u64 alarm = clocksource.read(&clocksource) + cycles;
668c2ecf20Sopenharmony_ci	while ((readl(regbase + TIMER_AS_VAL) & TIMER_MATCH_W_ACTIVE)
678c2ecf20Sopenharmony_ci						&& --loops)
688c2ecf20Sopenharmony_ci		cpu_relax();
698c2ecf20Sopenharmony_ci	writel((unsigned long)alarm, regbase + TIMER_MATCH_VAL);
708c2ecf20Sopenharmony_ci
718c2ecf20Sopenharmony_ci	if ((signed)(alarm - clocksource.read(&clocksource)) <= MIN_OSCR_DELTA)
728c2ecf20Sopenharmony_ci		return -ETIME;
738c2ecf20Sopenharmony_ci
748c2ecf20Sopenharmony_ci	writel(1, regbase + TIMER_IER_VAL);
758c2ecf20Sopenharmony_ci
768c2ecf20Sopenharmony_ci	return 0;
778c2ecf20Sopenharmony_ci}
788c2ecf20Sopenharmony_ci
798c2ecf20Sopenharmony_cistatic int vt8500_shutdown(struct clock_event_device *evt)
808c2ecf20Sopenharmony_ci{
818c2ecf20Sopenharmony_ci	writel(readl(regbase + TIMER_CTRL_VAL) | 1, regbase + TIMER_CTRL_VAL);
828c2ecf20Sopenharmony_ci	writel(0, regbase + TIMER_IER_VAL);
838c2ecf20Sopenharmony_ci	return 0;
848c2ecf20Sopenharmony_ci}
858c2ecf20Sopenharmony_ci
868c2ecf20Sopenharmony_cistatic struct clock_event_device clockevent = {
878c2ecf20Sopenharmony_ci	.name			= "vt8500_timer",
888c2ecf20Sopenharmony_ci	.features		= CLOCK_EVT_FEAT_ONESHOT,
898c2ecf20Sopenharmony_ci	.rating			= 200,
908c2ecf20Sopenharmony_ci	.set_next_event		= vt8500_timer_set_next_event,
918c2ecf20Sopenharmony_ci	.set_state_shutdown	= vt8500_shutdown,
928c2ecf20Sopenharmony_ci	.set_state_oneshot	= vt8500_shutdown,
938c2ecf20Sopenharmony_ci};
948c2ecf20Sopenharmony_ci
958c2ecf20Sopenharmony_cistatic irqreturn_t vt8500_timer_interrupt(int irq, void *dev_id)
968c2ecf20Sopenharmony_ci{
978c2ecf20Sopenharmony_ci	struct clock_event_device *evt = dev_id;
988c2ecf20Sopenharmony_ci	writel(0xf, regbase + TIMER_STATUS_VAL);
998c2ecf20Sopenharmony_ci	evt->event_handler(evt);
1008c2ecf20Sopenharmony_ci
1018c2ecf20Sopenharmony_ci	return IRQ_HANDLED;
1028c2ecf20Sopenharmony_ci}
1038c2ecf20Sopenharmony_ci
1048c2ecf20Sopenharmony_cistatic int __init vt8500_timer_init(struct device_node *np)
1058c2ecf20Sopenharmony_ci{
1068c2ecf20Sopenharmony_ci	int timer_irq, ret;
1078c2ecf20Sopenharmony_ci
1088c2ecf20Sopenharmony_ci	regbase = of_iomap(np, 0);
1098c2ecf20Sopenharmony_ci	if (!regbase) {
1108c2ecf20Sopenharmony_ci		pr_err("%s: Missing iobase description in Device Tree\n",
1118c2ecf20Sopenharmony_ci								__func__);
1128c2ecf20Sopenharmony_ci		return -ENXIO;
1138c2ecf20Sopenharmony_ci	}
1148c2ecf20Sopenharmony_ci
1158c2ecf20Sopenharmony_ci	timer_irq = irq_of_parse_and_map(np, 0);
1168c2ecf20Sopenharmony_ci	if (!timer_irq) {
1178c2ecf20Sopenharmony_ci		pr_err("%s: Missing irq description in Device Tree\n",
1188c2ecf20Sopenharmony_ci								__func__);
1198c2ecf20Sopenharmony_ci		return -EINVAL;
1208c2ecf20Sopenharmony_ci	}
1218c2ecf20Sopenharmony_ci
1228c2ecf20Sopenharmony_ci	writel(1, regbase + TIMER_CTRL_VAL);
1238c2ecf20Sopenharmony_ci	writel(0xf, regbase + TIMER_STATUS_VAL);
1248c2ecf20Sopenharmony_ci	writel(~0, regbase + TIMER_MATCH_VAL);
1258c2ecf20Sopenharmony_ci
1268c2ecf20Sopenharmony_ci	ret = clocksource_register_hz(&clocksource, VT8500_TIMER_HZ);
1278c2ecf20Sopenharmony_ci	if (ret) {
1288c2ecf20Sopenharmony_ci		pr_err("%s: clocksource_register failed for %s\n",
1298c2ecf20Sopenharmony_ci		       __func__, clocksource.name);
1308c2ecf20Sopenharmony_ci		return ret;
1318c2ecf20Sopenharmony_ci	}
1328c2ecf20Sopenharmony_ci
1338c2ecf20Sopenharmony_ci	clockevent.cpumask = cpumask_of(0);
1348c2ecf20Sopenharmony_ci
1358c2ecf20Sopenharmony_ci	ret = request_irq(timer_irq, vt8500_timer_interrupt,
1368c2ecf20Sopenharmony_ci			  IRQF_TIMER | IRQF_IRQPOLL, "vt8500_timer",
1378c2ecf20Sopenharmony_ci			  &clockevent);
1388c2ecf20Sopenharmony_ci	if (ret) {
1398c2ecf20Sopenharmony_ci		pr_err("%s: setup_irq failed for %s\n", __func__,
1408c2ecf20Sopenharmony_ci							clockevent.name);
1418c2ecf20Sopenharmony_ci		return ret;
1428c2ecf20Sopenharmony_ci	}
1438c2ecf20Sopenharmony_ci
1448c2ecf20Sopenharmony_ci	clockevents_config_and_register(&clockevent, VT8500_TIMER_HZ,
1458c2ecf20Sopenharmony_ci					MIN_OSCR_DELTA * 2, 0xf0000000);
1468c2ecf20Sopenharmony_ci
1478c2ecf20Sopenharmony_ci	return 0;
1488c2ecf20Sopenharmony_ci}
1498c2ecf20Sopenharmony_ci
1508c2ecf20Sopenharmony_ciTIMER_OF_DECLARE(vt8500, "via,vt8500-timer", vt8500_timer_init);
151