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