164db8bb1SStefan Wahren // SPDX-License-Identifier: GPL-2.0+
2ee4af569SSimon Arlott /*
3ee4af569SSimon Arlott  * Copyright 2012 Simon Arlott
4ee4af569SSimon Arlott  */
5ee4af569SSimon Arlott 
6ee4af569SSimon Arlott #include <linux/bitops.h>
7ee4af569SSimon Arlott #include <linux/clockchips.h>
8ee4af569SSimon Arlott #include <linux/clocksource.h>
9ee4af569SSimon Arlott #include <linux/interrupt.h>
10ee4af569SSimon Arlott #include <linux/irqreturn.h>
11ee4af569SSimon Arlott #include <linux/kernel.h>
12ee4af569SSimon Arlott #include <linux/module.h>
13*6303d069SRob Herring #include <linux/of.h>
14ee4af569SSimon Arlott #include <linux/of_address.h>
15ee4af569SSimon Arlott #include <linux/of_irq.h>
16ee4af569SSimon Arlott #include <linux/slab.h>
17ee4af569SSimon Arlott #include <linux/string.h>
1838ff87f7SStephen Boyd #include <linux/sched_clock.h>
19ee4af569SSimon Arlott 
20ee4af569SSimon Arlott #include <asm/irq.h>
21ee4af569SSimon Arlott 
22ee4af569SSimon Arlott #define REG_CONTROL	0x00
23ee4af569SSimon Arlott #define REG_COUNTER_LO	0x04
24ee4af569SSimon Arlott #define REG_COUNTER_HI	0x08
25ee4af569SSimon Arlott #define REG_COMPARE(n)	(0x0c + (n) * 4)
26ee4af569SSimon Arlott #define MAX_TIMER	3
27ee4af569SSimon Arlott #define DEFAULT_TIMER	3
28ee4af569SSimon Arlott 
29ee4af569SSimon Arlott struct bcm2835_timer {
30ee4af569SSimon Arlott 	void __iomem *control;
31ee4af569SSimon Arlott 	void __iomem *compare;
32ee4af569SSimon Arlott 	int match_mask;
33ee4af569SSimon Arlott 	struct clock_event_device evt;
34ee4af569SSimon Arlott };
35ee4af569SSimon Arlott 
36ee4af569SSimon Arlott static void __iomem *system_clock __read_mostly;
37ee4af569SSimon Arlott 
bcm2835_sched_read(void)3818952f20SStephen Boyd static u64 notrace bcm2835_sched_read(void)
39ee4af569SSimon Arlott {
40ee4af569SSimon Arlott 	return readl_relaxed(system_clock);
41ee4af569SSimon Arlott }
42ee4af569SSimon Arlott 
bcm2835_time_set_next_event(unsigned long event,struct clock_event_device * evt_dev)43ee4af569SSimon Arlott static int bcm2835_time_set_next_event(unsigned long event,
44ee4af569SSimon Arlott 	struct clock_event_device *evt_dev)
45ee4af569SSimon Arlott {
46ee4af569SSimon Arlott 	struct bcm2835_timer *timer = container_of(evt_dev,
47ee4af569SSimon Arlott 		struct bcm2835_timer, evt);
48ee4af569SSimon Arlott 	writel_relaxed(readl_relaxed(system_clock) + event,
49ee4af569SSimon Arlott 		timer->compare);
50ee4af569SSimon Arlott 	return 0;
51ee4af569SSimon Arlott }
52ee4af569SSimon Arlott 
bcm2835_time_interrupt(int irq,void * dev_id)53ee4af569SSimon Arlott static irqreturn_t bcm2835_time_interrupt(int irq, void *dev_id)
54ee4af569SSimon Arlott {
55ee4af569SSimon Arlott 	struct bcm2835_timer *timer = dev_id;
56ee4af569SSimon Arlott 	void (*event_handler)(struct clock_event_device *);
57ee4af569SSimon Arlott 	if (readl_relaxed(timer->control) & timer->match_mask) {
58ee4af569SSimon Arlott 		writel_relaxed(timer->match_mask, timer->control);
59ee4af569SSimon Arlott 
606aa7de05SMark Rutland 		event_handler = READ_ONCE(timer->evt.event_handler);
61ee4af569SSimon Arlott 		if (event_handler)
62ee4af569SSimon Arlott 			event_handler(&timer->evt);
63ee4af569SSimon Arlott 		return IRQ_HANDLED;
64ee4af569SSimon Arlott 	} else {
65ee4af569SSimon Arlott 		return IRQ_NONE;
66ee4af569SSimon Arlott 	}
67ee4af569SSimon Arlott }
68ee4af569SSimon Arlott 
bcm2835_timer_init(struct device_node * node)69524a7f08SDaniel Lezcano static int __init bcm2835_timer_init(struct device_node *node)
70ee4af569SSimon Arlott {
71ee4af569SSimon Arlott 	void __iomem *base;
72ee4af569SSimon Arlott 	u32 freq;
73524a7f08SDaniel Lezcano 	int irq, ret;
74ee4af569SSimon Arlott 	struct bcm2835_timer *timer;
75ee4af569SSimon Arlott 
76ee4af569SSimon Arlott 	base = of_iomap(node, 0);
77524a7f08SDaniel Lezcano 	if (!base) {
78ac9ce6d1SRafał Miłecki 		pr_err("Can't remap registers\n");
79524a7f08SDaniel Lezcano 		return -ENXIO;
80524a7f08SDaniel Lezcano 	}
81ee4af569SSimon Arlott 
82524a7f08SDaniel Lezcano 	ret = of_property_read_u32(node, "clock-frequency", &freq);
83524a7f08SDaniel Lezcano 	if (ret) {
84ac9ce6d1SRafał Miłecki 		pr_err("Can't read clock-frequency\n");
8584c39b8bSArvind Yadav 		goto err_iounmap;
86524a7f08SDaniel Lezcano 	}
87ee4af569SSimon Arlott 
88ee4af569SSimon Arlott 	system_clock = base + REG_COUNTER_LO;
8918952f20SStephen Boyd 	sched_clock_register(bcm2835_sched_read, 32, freq);
90ee4af569SSimon Arlott 
91ee4af569SSimon Arlott 	clocksource_mmio_init(base + REG_COUNTER_LO, node->name,
92ee4af569SSimon Arlott 		freq, 300, 32, clocksource_mmio_readl_up);
93ee4af569SSimon Arlott 
94ee4af569SSimon Arlott 	irq = irq_of_parse_and_map(node, DEFAULT_TIMER);
95524a7f08SDaniel Lezcano 	if (irq <= 0) {
96ac9ce6d1SRafał Miłecki 		pr_err("Can't parse IRQ\n");
9784c39b8bSArvind Yadav 		ret = -EINVAL;
9884c39b8bSArvind Yadav 		goto err_iounmap;
99524a7f08SDaniel Lezcano 	}
100ee4af569SSimon Arlott 
101ee4af569SSimon Arlott 	timer = kzalloc(sizeof(*timer), GFP_KERNEL);
102524a7f08SDaniel Lezcano 	if (!timer) {
10384c39b8bSArvind Yadav 		ret = -ENOMEM;
10484c39b8bSArvind Yadav 		goto err_iounmap;
105524a7f08SDaniel Lezcano 	}
106ee4af569SSimon Arlott 
107ee4af569SSimon Arlott 	timer->control = base + REG_CONTROL;
108ee4af569SSimon Arlott 	timer->compare = base + REG_COMPARE(DEFAULT_TIMER);
109ee4af569SSimon Arlott 	timer->match_mask = BIT(DEFAULT_TIMER);
110ee4af569SSimon Arlott 	timer->evt.name = node->name;
111ee4af569SSimon Arlott 	timer->evt.rating = 300;
112ee4af569SSimon Arlott 	timer->evt.features = CLOCK_EVT_FEAT_ONESHOT;
113ee4af569SSimon Arlott 	timer->evt.set_next_event = bcm2835_time_set_next_event;
114ee4af569SSimon Arlott 	timer->evt.cpumask = cpumask_of(0);
115ee4af569SSimon Arlott 
116cc2550b4Safzal mohammed 	ret = request_irq(irq, bcm2835_time_interrupt, IRQF_TIMER | IRQF_SHARED,
117cc2550b4Safzal mohammed 			  node->name, timer);
118524a7f08SDaniel Lezcano 	if (ret) {
119524a7f08SDaniel Lezcano 		pr_err("Can't set up timer IRQ\n");
1202052d032SColin Ian King 		goto err_timer_free;
121524a7f08SDaniel Lezcano 	}
122ee4af569SSimon Arlott 
123ee4af569SSimon Arlott 	clockevents_config_and_register(&timer->evt, freq, 0xf, 0xffffffff);
124ee4af569SSimon Arlott 
125ee4af569SSimon Arlott 	pr_info("bcm2835: system timer (irq = %d)\n", irq);
126524a7f08SDaniel Lezcano 
127524a7f08SDaniel Lezcano 	return 0;
12884c39b8bSArvind Yadav 
1292052d032SColin Ian King err_timer_free:
1302052d032SColin Ian King 	kfree(timer);
1312052d032SColin Ian King 
13284c39b8bSArvind Yadav err_iounmap:
13384c39b8bSArvind Yadav 	iounmap(base);
13484c39b8bSArvind Yadav 	return ret;
135ee4af569SSimon Arlott }
13617273395SDaniel Lezcano TIMER_OF_DECLARE(bcm2835, "brcm,bcm2835-system-timer",
137c1b724f6SStephen Warren 			bcm2835_timer_init);
138