1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3 * Copyright (C) 2020, Sean Anderson <seanga2@gmail.com>
4 * Copyright (C) 2018, Bin Meng <bmeng.cn@gmail.com>
5 */
6
7 #include <config.h>
8 #include <clk.h>
9 #include <div64.h>
10 #include <dm.h>
11 #include <timer.h>
12 #include <asm/io.h>
13 #include <dm/device-internal.h>
14 #include <linux/err.h>
15
16 #define CLINT_MTIME_OFFSET 0xbff8
17 #define ACLINT_MTIME_OFFSET 0
18
19 /* mtime register */
20 #define MTIME_REG(base, offset) ((ulong)(base) + (offset))
21
riscv_aclint_timer_get_count(struct udevice * dev)22 static u64 notrace riscv_aclint_timer_get_count(struct udevice *dev)
23 {
24 return readq((void __iomem *)MTIME_REG(dev_get_priv(dev),
25 dev_get_driver_data(dev)));
26 }
27
28 #if CONFIG_IS_ENABLED(RISCV_MMODE) && IS_ENABLED(CONFIG_TIMER_EARLY)
29 /**
30 * timer_early_get_rate() - Get the timer rate before driver model
31 */
timer_early_get_rate(void)32 unsigned long notrace timer_early_get_rate(void)
33 {
34 return RISCV_MMODE_TIMER_FREQ;
35 }
36
37 /**
38 * timer_early_get_count() - Get the timer count before driver model
39 *
40 */
timer_early_get_count(void)41 u64 notrace timer_early_get_count(void)
42 {
43 return readq((void __iomem *)MTIME_REG(RISCV_MMODE_TIMERBASE,
44 RISCV_MMODE_TIMEROFF));
45 }
46 #endif
47
48 #if CONFIG_IS_ENABLED(RISCV_MMODE) && CONFIG_IS_ENABLED(BOOTSTAGE)
timer_get_boot_us(void)49 ulong timer_get_boot_us(void)
50 {
51 int ret;
52 u64 ticks = 0;
53 u32 rate;
54
55 ret = dm_timer_init();
56 if (!ret) {
57 rate = timer_get_rate(gd->timer);
58 timer_get_count(gd->timer, &ticks);
59 } else {
60 rate = RISCV_MMODE_TIMER_FREQ;
61 ticks = readq((void __iomem *)MTIME_REG(RISCV_MMODE_TIMERBASE,
62 RISCV_MMODE_TIMEROFF));
63 }
64
65 /* Below is converted from time(us) = (tick / rate) * 10000000 */
66 return lldiv(ticks * 1000, (rate / 1000));
67 }
68 #endif
69
70 static const struct timer_ops riscv_aclint_timer_ops = {
71 .get_count = riscv_aclint_timer_get_count,
72 };
73
riscv_aclint_timer_probe(struct udevice * dev)74 static int riscv_aclint_timer_probe(struct udevice *dev)
75 {
76 dev_set_priv(dev, dev_read_addr_ptr(dev));
77 if (!dev_get_priv(dev))
78 return -EINVAL;
79
80 return timer_timebase_fallback(dev);
81 }
82
83 static const struct udevice_id riscv_aclint_timer_ids[] = {
84 { .compatible = "riscv,clint0", .data = CLINT_MTIME_OFFSET },
85 { .compatible = "sifive,clint0", .data = CLINT_MTIME_OFFSET },
86 { .compatible = "riscv,aclint-mtimer", .data = ACLINT_MTIME_OFFSET },
87 { }
88 };
89
90 U_BOOT_DRIVER(riscv_aclint_timer) = {
91 .name = "riscv_aclint_timer",
92 .id = UCLASS_TIMER,
93 .of_match = riscv_aclint_timer_ids,
94 .probe = riscv_aclint_timer_probe,
95 .ops = &riscv_aclint_timer_ops,
96 .flags = DM_FLAG_PRE_RELOC,
97 };
98