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