162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Copyright 2012 Simon Arlott
462306a36Sopenharmony_ci */
562306a36Sopenharmony_ci
662306a36Sopenharmony_ci#include <linux/bitops.h>
762306a36Sopenharmony_ci#include <linux/clockchips.h>
862306a36Sopenharmony_ci#include <linux/clocksource.h>
962306a36Sopenharmony_ci#include <linux/interrupt.h>
1062306a36Sopenharmony_ci#include <linux/irqreturn.h>
1162306a36Sopenharmony_ci#include <linux/kernel.h>
1262306a36Sopenharmony_ci#include <linux/module.h>
1362306a36Sopenharmony_ci#include <linux/of.h>
1462306a36Sopenharmony_ci#include <linux/of_address.h>
1562306a36Sopenharmony_ci#include <linux/of_irq.h>
1662306a36Sopenharmony_ci#include <linux/slab.h>
1762306a36Sopenharmony_ci#include <linux/string.h>
1862306a36Sopenharmony_ci#include <linux/sched_clock.h>
1962306a36Sopenharmony_ci
2062306a36Sopenharmony_ci#include <asm/irq.h>
2162306a36Sopenharmony_ci
2262306a36Sopenharmony_ci#define REG_CONTROL	0x00
2362306a36Sopenharmony_ci#define REG_COUNTER_LO	0x04
2462306a36Sopenharmony_ci#define REG_COUNTER_HI	0x08
2562306a36Sopenharmony_ci#define REG_COMPARE(n)	(0x0c + (n) * 4)
2662306a36Sopenharmony_ci#define MAX_TIMER	3
2762306a36Sopenharmony_ci#define DEFAULT_TIMER	3
2862306a36Sopenharmony_ci
2962306a36Sopenharmony_cistruct bcm2835_timer {
3062306a36Sopenharmony_ci	void __iomem *control;
3162306a36Sopenharmony_ci	void __iomem *compare;
3262306a36Sopenharmony_ci	int match_mask;
3362306a36Sopenharmony_ci	struct clock_event_device evt;
3462306a36Sopenharmony_ci};
3562306a36Sopenharmony_ci
3662306a36Sopenharmony_cistatic void __iomem *system_clock __read_mostly;
3762306a36Sopenharmony_ci
3862306a36Sopenharmony_cistatic u64 notrace bcm2835_sched_read(void)
3962306a36Sopenharmony_ci{
4062306a36Sopenharmony_ci	return readl_relaxed(system_clock);
4162306a36Sopenharmony_ci}
4262306a36Sopenharmony_ci
4362306a36Sopenharmony_cistatic int bcm2835_time_set_next_event(unsigned long event,
4462306a36Sopenharmony_ci	struct clock_event_device *evt_dev)
4562306a36Sopenharmony_ci{
4662306a36Sopenharmony_ci	struct bcm2835_timer *timer = container_of(evt_dev,
4762306a36Sopenharmony_ci		struct bcm2835_timer, evt);
4862306a36Sopenharmony_ci	writel_relaxed(readl_relaxed(system_clock) + event,
4962306a36Sopenharmony_ci		timer->compare);
5062306a36Sopenharmony_ci	return 0;
5162306a36Sopenharmony_ci}
5262306a36Sopenharmony_ci
5362306a36Sopenharmony_cistatic irqreturn_t bcm2835_time_interrupt(int irq, void *dev_id)
5462306a36Sopenharmony_ci{
5562306a36Sopenharmony_ci	struct bcm2835_timer *timer = dev_id;
5662306a36Sopenharmony_ci	void (*event_handler)(struct clock_event_device *);
5762306a36Sopenharmony_ci	if (readl_relaxed(timer->control) & timer->match_mask) {
5862306a36Sopenharmony_ci		writel_relaxed(timer->match_mask, timer->control);
5962306a36Sopenharmony_ci
6062306a36Sopenharmony_ci		event_handler = READ_ONCE(timer->evt.event_handler);
6162306a36Sopenharmony_ci		if (event_handler)
6262306a36Sopenharmony_ci			event_handler(&timer->evt);
6362306a36Sopenharmony_ci		return IRQ_HANDLED;
6462306a36Sopenharmony_ci	} else {
6562306a36Sopenharmony_ci		return IRQ_NONE;
6662306a36Sopenharmony_ci	}
6762306a36Sopenharmony_ci}
6862306a36Sopenharmony_ci
6962306a36Sopenharmony_cistatic int __init bcm2835_timer_init(struct device_node *node)
7062306a36Sopenharmony_ci{
7162306a36Sopenharmony_ci	void __iomem *base;
7262306a36Sopenharmony_ci	u32 freq;
7362306a36Sopenharmony_ci	int irq, ret;
7462306a36Sopenharmony_ci	struct bcm2835_timer *timer;
7562306a36Sopenharmony_ci
7662306a36Sopenharmony_ci	base = of_iomap(node, 0);
7762306a36Sopenharmony_ci	if (!base) {
7862306a36Sopenharmony_ci		pr_err("Can't remap registers\n");
7962306a36Sopenharmony_ci		return -ENXIO;
8062306a36Sopenharmony_ci	}
8162306a36Sopenharmony_ci
8262306a36Sopenharmony_ci	ret = of_property_read_u32(node, "clock-frequency", &freq);
8362306a36Sopenharmony_ci	if (ret) {
8462306a36Sopenharmony_ci		pr_err("Can't read clock-frequency\n");
8562306a36Sopenharmony_ci		goto err_iounmap;
8662306a36Sopenharmony_ci	}
8762306a36Sopenharmony_ci
8862306a36Sopenharmony_ci	system_clock = base + REG_COUNTER_LO;
8962306a36Sopenharmony_ci	sched_clock_register(bcm2835_sched_read, 32, freq);
9062306a36Sopenharmony_ci
9162306a36Sopenharmony_ci	clocksource_mmio_init(base + REG_COUNTER_LO, node->name,
9262306a36Sopenharmony_ci		freq, 300, 32, clocksource_mmio_readl_up);
9362306a36Sopenharmony_ci
9462306a36Sopenharmony_ci	irq = irq_of_parse_and_map(node, DEFAULT_TIMER);
9562306a36Sopenharmony_ci	if (irq <= 0) {
9662306a36Sopenharmony_ci		pr_err("Can't parse IRQ\n");
9762306a36Sopenharmony_ci		ret = -EINVAL;
9862306a36Sopenharmony_ci		goto err_iounmap;
9962306a36Sopenharmony_ci	}
10062306a36Sopenharmony_ci
10162306a36Sopenharmony_ci	timer = kzalloc(sizeof(*timer), GFP_KERNEL);
10262306a36Sopenharmony_ci	if (!timer) {
10362306a36Sopenharmony_ci		ret = -ENOMEM;
10462306a36Sopenharmony_ci		goto err_iounmap;
10562306a36Sopenharmony_ci	}
10662306a36Sopenharmony_ci
10762306a36Sopenharmony_ci	timer->control = base + REG_CONTROL;
10862306a36Sopenharmony_ci	timer->compare = base + REG_COMPARE(DEFAULT_TIMER);
10962306a36Sopenharmony_ci	timer->match_mask = BIT(DEFAULT_TIMER);
11062306a36Sopenharmony_ci	timer->evt.name = node->name;
11162306a36Sopenharmony_ci	timer->evt.rating = 300;
11262306a36Sopenharmony_ci	timer->evt.features = CLOCK_EVT_FEAT_ONESHOT;
11362306a36Sopenharmony_ci	timer->evt.set_next_event = bcm2835_time_set_next_event;
11462306a36Sopenharmony_ci	timer->evt.cpumask = cpumask_of(0);
11562306a36Sopenharmony_ci
11662306a36Sopenharmony_ci	ret = request_irq(irq, bcm2835_time_interrupt, IRQF_TIMER | IRQF_SHARED,
11762306a36Sopenharmony_ci			  node->name, timer);
11862306a36Sopenharmony_ci	if (ret) {
11962306a36Sopenharmony_ci		pr_err("Can't set up timer IRQ\n");
12062306a36Sopenharmony_ci		goto err_timer_free;
12162306a36Sopenharmony_ci	}
12262306a36Sopenharmony_ci
12362306a36Sopenharmony_ci	clockevents_config_and_register(&timer->evt, freq, 0xf, 0xffffffff);
12462306a36Sopenharmony_ci
12562306a36Sopenharmony_ci	pr_info("bcm2835: system timer (irq = %d)\n", irq);
12662306a36Sopenharmony_ci
12762306a36Sopenharmony_ci	return 0;
12862306a36Sopenharmony_ci
12962306a36Sopenharmony_cierr_timer_free:
13062306a36Sopenharmony_ci	kfree(timer);
13162306a36Sopenharmony_ci
13262306a36Sopenharmony_cierr_iounmap:
13362306a36Sopenharmony_ci	iounmap(base);
13462306a36Sopenharmony_ci	return ret;
13562306a36Sopenharmony_ci}
13662306a36Sopenharmony_ciTIMER_OF_DECLARE(bcm2835, "brcm,bcm2835-system-timer",
13762306a36Sopenharmony_ci			bcm2835_timer_init);
138