1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3  * Copyright (C) 2022 Svyatoslav Ryhel <clamor95@gmail.com>
4  */
5 
6 #include <common.h>
7 #include <dm.h>
8 #include <errno.h>
9 #include <timer.h>
10 
11 #include <asm/io.h>
12 #include <asm/arch/clock.h>
13 #include <asm/arch/tegra.h>
14 
15 #define TEGRA_OSC_CLK_ENB_L_SET		(NV_PA_CLK_RST_BASE + 0x320)
16 #define TEGRA_OSC_SET_CLK_ENB_TMR	BIT(5)
17 
18 #define TEGRA_TIMER_USEC_CNTR		(NV_PA_TMRUS_BASE + 0)
19 #define TEGRA_TIMER_USEC_CFG		(NV_PA_TMRUS_BASE + 4)
20 
21 #define TEGRA_TIMER_RATE		1000000 /* 1 MHz */
22 
23 /*
24  * On pre-DM stage timer should be left configured by
25  * previous bootloader for correct 1MHz clock.
26  * In the case of reset default value is set to 1/13 of
27  * CLK_M which should be decent enough to safely
28  * get to DM stage.
29  */
timer_early_get_count(void)30 u64 notrace timer_early_get_count(void)
31 {
32 	/* At this stage raw timer is used */
33 	return readl(TEGRA_TIMER_USEC_CNTR);
34 }
35 
timer_early_get_rate(void)36 unsigned long notrace timer_early_get_rate(void)
37 {
38 	return TEGRA_TIMER_RATE;
39 }
40 
timer_get_boot_us(void)41 ulong timer_get_boot_us(void)
42 {
43 	return timer_early_get_count();
44 }
45 
46 /*
47  * At moment of calling get_count, timer driver is already
48  * probed and is configured to have precise 1MHz clock.
49  * Tegra timer has a step of 1 microsecond which removes
50  * need of using adjusments involving uc_priv->clock_rate.
51  */
tegra_timer_get_count(struct udevice * dev)52 static notrace u64 tegra_timer_get_count(struct udevice *dev)
53 {
54 	u32 val = timer_early_get_count();
55 	return timer_conv_64(val);
56 }
57 
tegra_timer_probe(struct udevice * dev)58 static int tegra_timer_probe(struct udevice *dev)
59 {
60 	struct timer_dev_priv *uc_priv = dev_get_uclass_priv(dev);
61 	u32 usec_config, value;
62 
63 	/* Timer rate has to be set unconditionally */
64 	uc_priv->clock_rate = TEGRA_TIMER_RATE;
65 
66 	/*
67 	 * Configure microsecond timers to have 1MHz clock
68 	 * Config register is 0xqqww, where qq is "dividend", ww is "divisor"
69 	 * Uses n+1 scheme
70 	 */
71 	switch (clock_get_rate(CLOCK_ID_CLK_M)) {
72 	case 12000000:
73 		usec_config = 0x000b; /* (11+1)/(0+1) */
74 		break;
75 	case 12800000:
76 		usec_config = 0x043f; /* (63+1)/(4+1) */
77 		break;
78 	case 13000000:
79 		usec_config = 0x000c; /* (12+1)/(0+1) */
80 		break;
81 	case 16800000:
82 		usec_config = 0x0453; /* (83+1)/(4+1) */
83 		break;
84 	case 19200000:
85 		usec_config = 0x045f; /* (95+1)/(4+1) */
86 		break;
87 	case 26000000:
88 		usec_config = 0x0019; /* (25+1)/(0+1) */
89 		break;
90 	case 38400000:
91 		usec_config = 0x04bf; /* (191+1)/(4+1) */
92 		break;
93 	case 48000000:
94 		usec_config = 0x002f; /* (47+1)/(0+1) */
95 		break;
96 	default:
97 		return -EINVAL;
98 	}
99 
100 	/* Enable clock to timer hardware */
101 	value = readl_relaxed(TEGRA_OSC_CLK_ENB_L_SET);
102 	writel_relaxed(value | TEGRA_OSC_SET_CLK_ENB_TMR,
103 		       TEGRA_OSC_CLK_ENB_L_SET);
104 
105 	writel_relaxed(usec_config, TEGRA_TIMER_USEC_CFG);
106 
107 	return 0;
108 }
109 
110 static const struct timer_ops tegra_timer_ops = {
111 	.get_count = tegra_timer_get_count,
112 };
113 
114 static const struct udevice_id tegra_timer_ids[] = {
115 	{ .compatible = "nvidia,tegra20-timer" },
116 	{ .compatible = "nvidia,tegra30-timer" },
117 	{ .compatible = "nvidia,tegra114-timer" },
118 	{ .compatible = "nvidia,tegra124-timer" },
119 	{ .compatible = "nvidia,tegra210-timer" },
120 	{ }
121 };
122 
123 U_BOOT_DRIVER(tegra_timer) = {
124 	.name		= "tegra_timer",
125 	.id		= UCLASS_TIMER,
126 	.of_match	= tegra_timer_ids,
127 	.probe		= tegra_timer_probe,
128 	.ops		= &tegra_timer_ops,
129 	.flags		= DM_FLAG_PRE_RELOC,
130 };
131