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