Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0+
0002 /*
0003  * Copyright 2012 Simon Arlott
0004  */
0005 
0006 #include <linux/bitops.h>
0007 #include <linux/clockchips.h>
0008 #include <linux/clocksource.h>
0009 #include <linux/interrupt.h>
0010 #include <linux/irqreturn.h>
0011 #include <linux/kernel.h>
0012 #include <linux/module.h>
0013 #include <linux/of_address.h>
0014 #include <linux/of_irq.h>
0015 #include <linux/of_platform.h>
0016 #include <linux/slab.h>
0017 #include <linux/string.h>
0018 #include <linux/sched_clock.h>
0019 
0020 #include <asm/irq.h>
0021 
0022 #define REG_CONTROL 0x00
0023 #define REG_COUNTER_LO  0x04
0024 #define REG_COUNTER_HI  0x08
0025 #define REG_COMPARE(n)  (0x0c + (n) * 4)
0026 #define MAX_TIMER   3
0027 #define DEFAULT_TIMER   3
0028 
0029 struct bcm2835_timer {
0030     void __iomem *control;
0031     void __iomem *compare;
0032     int match_mask;
0033     struct clock_event_device evt;
0034 };
0035 
0036 static void __iomem *system_clock __read_mostly;
0037 
0038 static u64 notrace bcm2835_sched_read(void)
0039 {
0040     return readl_relaxed(system_clock);
0041 }
0042 
0043 static int bcm2835_time_set_next_event(unsigned long event,
0044     struct clock_event_device *evt_dev)
0045 {
0046     struct bcm2835_timer *timer = container_of(evt_dev,
0047         struct bcm2835_timer, evt);
0048     writel_relaxed(readl_relaxed(system_clock) + event,
0049         timer->compare);
0050     return 0;
0051 }
0052 
0053 static irqreturn_t bcm2835_time_interrupt(int irq, void *dev_id)
0054 {
0055     struct bcm2835_timer *timer = dev_id;
0056     void (*event_handler)(struct clock_event_device *);
0057     if (readl_relaxed(timer->control) & timer->match_mask) {
0058         writel_relaxed(timer->match_mask, timer->control);
0059 
0060         event_handler = READ_ONCE(timer->evt.event_handler);
0061         if (event_handler)
0062             event_handler(&timer->evt);
0063         return IRQ_HANDLED;
0064     } else {
0065         return IRQ_NONE;
0066     }
0067 }
0068 
0069 static int __init bcm2835_timer_init(struct device_node *node)
0070 {
0071     void __iomem *base;
0072     u32 freq;
0073     int irq, ret;
0074     struct bcm2835_timer *timer;
0075 
0076     base = of_iomap(node, 0);
0077     if (!base) {
0078         pr_err("Can't remap registers\n");
0079         return -ENXIO;
0080     }
0081 
0082     ret = of_property_read_u32(node, "clock-frequency", &freq);
0083     if (ret) {
0084         pr_err("Can't read clock-frequency\n");
0085         goto err_iounmap;
0086     }
0087 
0088     system_clock = base + REG_COUNTER_LO;
0089     sched_clock_register(bcm2835_sched_read, 32, freq);
0090 
0091     clocksource_mmio_init(base + REG_COUNTER_LO, node->name,
0092         freq, 300, 32, clocksource_mmio_readl_up);
0093 
0094     irq = irq_of_parse_and_map(node, DEFAULT_TIMER);
0095     if (irq <= 0) {
0096         pr_err("Can't parse IRQ\n");
0097         ret = -EINVAL;
0098         goto err_iounmap;
0099     }
0100 
0101     timer = kzalloc(sizeof(*timer), GFP_KERNEL);
0102     if (!timer) {
0103         ret = -ENOMEM;
0104         goto err_iounmap;
0105     }
0106 
0107     timer->control = base + REG_CONTROL;
0108     timer->compare = base + REG_COMPARE(DEFAULT_TIMER);
0109     timer->match_mask = BIT(DEFAULT_TIMER);
0110     timer->evt.name = node->name;
0111     timer->evt.rating = 300;
0112     timer->evt.features = CLOCK_EVT_FEAT_ONESHOT;
0113     timer->evt.set_next_event = bcm2835_time_set_next_event;
0114     timer->evt.cpumask = cpumask_of(0);
0115 
0116     ret = request_irq(irq, bcm2835_time_interrupt, IRQF_TIMER | IRQF_SHARED,
0117               node->name, timer);
0118     if (ret) {
0119         pr_err("Can't set up timer IRQ\n");
0120         goto err_timer_free;
0121     }
0122 
0123     clockevents_config_and_register(&timer->evt, freq, 0xf, 0xffffffff);
0124 
0125     pr_info("bcm2835: system timer (irq = %d)\n", irq);
0126 
0127     return 0;
0128 
0129 err_timer_free:
0130     kfree(timer);
0131 
0132 err_iounmap:
0133     iounmap(base);
0134     return ret;
0135 }
0136 TIMER_OF_DECLARE(bcm2835, "brcm,bcm2835-system-timer",
0137             bcm2835_timer_init);