1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3  * ARM PrimeCell Dual-Timer Module (SP804) driver
4  * Copyright (C) 2022 Arm Ltd.
5  */
6 
7 #include <common.h>
8 #include <clk.h>
9 #include <dm.h>
10 #include <init.h>
11 #include <log.h>
12 #include <asm/global_data.h>
13 #include <dm/ofnode.h>
14 #include <mapmem.h>
15 #include <dt-structs.h>
16 #include <timer.h>
17 #include <asm/io.h>
18 
19 DECLARE_GLOBAL_DATA_PTR;
20 
21 #define SP804_TIMERX_LOAD		0x00
22 #define SP804_TIMERX_VALUE		0x04
23 #define SP804_TIMERX_CONTROL		0x08
24 
25 #define SP804_CTRL_TIMER_ENABLE		(1U << 7)
26 #define SP804_CTRL_TIMER_PERIODIC	(1U << 6)
27 #define SP804_CTRL_INT_ENABLE		(1U << 5)
28 #define SP804_CTRL_TIMER_PRESCALE_SHIFT	2
29 #define SP804_CTRL_TIMER_PRESCALE_MASK	(3U << SP804_CTRL_TIMER_PRESCALE_SHIFT)
30 #define SP804_CTRL_TIMER_32BIT		(1U << 1)
31 #define SP804_CTRL_ONESHOT		(1U << 0)
32 
33 
34 struct sp804_timer_plat {
35 	uintptr_t base;
36 };
37 
sp804_timer_get_count(struct udevice * dev)38 static u64 sp804_timer_get_count(struct udevice *dev)
39 {
40 	struct sp804_timer_plat *plat = dev_get_plat(dev);
41 	uint32_t cntr = readl(plat->base + SP804_TIMERX_VALUE);
42 
43 	/* timers are down-counting */
44 	return ~0u - cntr;
45 }
46 
sp804_clk_of_to_plat(struct udevice * dev)47 static int sp804_clk_of_to_plat(struct udevice *dev)
48 {
49 	struct sp804_timer_plat *plat = dev_get_plat(dev);
50 
51 	plat->base = dev_read_addr(dev);
52 	if (!plat->base)
53 		return -ENOENT;
54 
55 	return 0;
56 }
57 
sp804_timer_probe(struct udevice * dev)58 static int sp804_timer_probe(struct udevice *dev)
59 {
60 	struct sp804_timer_plat *plat = dev_get_plat(dev);
61 	struct timer_dev_priv *uc_priv = dev_get_uclass_priv(dev);
62 	struct clk base_clk;
63 	unsigned int divider = 1;
64 	uint32_t ctlr;
65 	int ret;
66 
67 	ctlr = readl(plat->base + SP804_TIMERX_CONTROL);
68 	ctlr &= SP804_CTRL_TIMER_PRESCALE_MASK;
69 	switch (ctlr >> SP804_CTRL_TIMER_PRESCALE_SHIFT) {
70 	case 0x0: divider = 1; break;
71 	case 0x1: divider = 16; break;
72 	case 0x2: divider = 256; break;
73 	case 0x3: printf("illegal!\n"); break;
74 	}
75 
76 	ret = clk_get_by_index(dev, 0, &base_clk);
77 	if (ret) {
78 		printf("could not find SP804 timer base clock in DT\n");
79 		return ret;
80 	}
81 
82 	uc_priv->clock_rate = clk_get_rate(&base_clk) / divider;
83 
84 	/* keep divider, free-running, wrapping, no IRQs, 32-bit mode */
85 	ctlr |= SP804_CTRL_TIMER_32BIT | SP804_CTRL_TIMER_ENABLE;
86 	writel(ctlr, plat->base + SP804_TIMERX_CONTROL);
87 
88 	return 0;
89 }
90 
91 static const struct timer_ops sp804_timer_ops = {
92 	.get_count = sp804_timer_get_count,
93 };
94 
95 static const struct udevice_id sp804_timer_ids[] = {
96 	{ .compatible = "arm,sp804" },
97 	{}
98 };
99 
100 U_BOOT_DRIVER(arm_sp804_timer) = {
101 	.name		= "arm_sp804_timer",
102 	.id		= UCLASS_TIMER,
103 	.of_match 	= sp804_timer_ids,
104 	.probe		= sp804_timer_probe,
105 	.ops		= &sp804_timer_ops,
106 	.plat_auto	= sizeof(struct sp804_timer_plat),
107 	.of_to_plat 	= sp804_clk_of_to_plat,
108 };
109