]>
Commit | Line | Data |
---|---|---|
adf86904 SR |
1 | // SPDX-License-Identifier: GPL-2.0+ |
2 | /* | |
3 | * Copyright (C) 2022 Svyatoslav Ryhel <[email protected]> | |
4 | */ | |
5 | ||
adf86904 SR |
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 | */ | |
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 | ||
35 | unsigned long notrace timer_early_get_rate(void) | |
36 | { | |
37 | return TEGRA_TIMER_RATE; | |
38 | } | |
39 | ||
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 | */ | |
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 | ||
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 | }; |