hc
2024-10-22 8ac6c7a54ed1b98d142dce24b11c6de6a1e239a5
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
// SPDX-License-Identifier: (GPL-2.0+ OR MIT)
/*
 * Copyright (c) 2023 Rockchip Electronics Co., Ltd.
 */
 
#include <linux/io.h>
 
#include "rkpm_helpers.h"
#include "rockchip_hptimer.h"
 
/* hp timer regs */
#define TIMER_HP_REVISION        0x0
#define TIMER_HP_CTRL            0x4
#define TIMER_HP_INT_EN            0x8
#define TIMER_HP_T24_GCD        0xc
#define TIMER_HP_T32_GCD        0x10
#define TIMER_HP_LOAD_COUNT0        0x14
#define TIMER_HP_LOAD_COUNT1        0x18
#define TIMER_HP_T24_DELAT_COUNT0    0x1c
#define TIMER_HP_T24_DELAT_COUNT1    0x20
#define TIMER_HP_CURR_32K_VALUE0    0x24
#define TIMER_HP_CURR_32K_VALUE1    0x28
#define TIMER_HP_CURR_TIMER_VALUE0    0x2c
#define TIMER_HP_CURR_TIMER_VALUE1    0x30
#define TIMER_HP_T24_32BEGIN0        0x34
#define TIMER_HP_T24_32BEGIN1        0x38
#define TIMER_HP_T32_24END0        0x3c
#define TIMER_HP_T32_24END1        0x40
#define TIMER_HP_BEGIN_END_VALID    0x44
#define TIMER_HP_SYNC_REQ        0x48
#define TIMER_HP_INTR_STATUS        0x4c
 
/* hptimer ctlr */
enum rk_hptimer_ctlr_reg {
   RK_HPTIMER_CTRL_EN = 0,
   RK_HPTIMER_CTRL_MODE = 1,
   RK_HPTIMER_CTRL_CNT_MODE = 3,
};
 
/* hptimer int */
enum rk_hptimer_int_id_t {
   RK_HPTIMER_INT_REACH = 0,
   RK_HPTIMER_INT_ADJ_DONE = 1,
   RK_HPTIMER_INT_SYNC = 2,
};
 
#define T24M_GCD        0xb71b
#define T32K_GCD        0x40
 
#define HPTIMER_WAIT_MAX_US    1000000
 
static void rk_hptimer_clear_int_st(void __iomem *base, enum rk_hptimer_int_id_t id)
{
   writel_relaxed(BIT(id), base + TIMER_HP_INTR_STATUS);
}
 
static int rk_hptimer_wait_int_st(void __iomem *base,
                 enum rk_hptimer_int_id_t id,
                 u64 wait_us)
{
   while (!(readl_relaxed(base + TIMER_HP_INTR_STATUS) & BIT(id)) &&
          --wait_us > 0)
       rkpm_raw_udelay(1);
   dsb();
 
   if (wait_us == 0) {
       rkpm_printstr("can't wait hptimer int:");
       rkpm_printdec(id);
       rkpm_printch('-');
       rkpm_printhex(readl_relaxed(base + TIMER_HP_INTR_STATUS));
       rkpm_printch('\n');
       return -1;
   } else {
       return 0;
   }
}
 
static int rk_hptimer_wait_begin_end_valid(void __iomem *base, u64 wait_us)
{
   while ((readl_relaxed(base + TIMER_HP_BEGIN_END_VALID) & 0x3) != 0x3 &&
          --wait_us > 0)
       rkpm_raw_udelay(1);
   dsb();
 
   if (wait_us == 0) {
       rkpm_printstr("can't wait hptimer begin_end valid:");
       rkpm_printhex(readl_relaxed(base + TIMER_HP_BEGIN_END_VALID));
       rkpm_printch('\n');
       return -1;
   } else {
       return 0;
   }
}
 
static u64 rk_hptimer_get_soft_adjust_delt_cnt(void __iomem *base)
{
   u64 begin, end, delt;
 
   if (rk_hptimer_wait_begin_end_valid(base, HPTIMER_WAIT_MAX_US))
       return 0;
 
   begin = (u64)readl_relaxed(base + TIMER_HP_T24_32BEGIN0) |
       (u64)readl_relaxed(base + TIMER_HP_T24_32BEGIN1) << 32;
   end = (u64)readl_relaxed(base + TIMER_HP_T32_24END0) |
         (u64)readl_relaxed(base + TIMER_HP_T32_24END1) << 32;
   delt = (end - begin) * T24M_GCD / T32K_GCD;
 
   writel_relaxed(0x3, base + TIMER_HP_BEGIN_END_VALID);
 
   return delt;
}
 
static void rk_hptimer_soft_adjust_req(void __iomem *base, u64 delt)
{
   if (delt == 0)
       return;
 
   writel_relaxed(delt & 0xffffffff, base + TIMER_HP_T24_DELAT_COUNT0);
   writel_relaxed((delt >> 32) & 0xffffffff, base + TIMER_HP_T24_DELAT_COUNT1);
   dsb();
 
   writel_relaxed(0x1, base + TIMER_HP_SYNC_REQ);
   dsb();
}
 
int rk_hptimer_is_enabled(void __iomem *base)
{
   return !!(readl_relaxed(base + TIMER_HP_CTRL) & BIT(RK_HPTIMER_CTRL_EN));
}
 
int rk_hptimer_get_mode(void __iomem *base)
{
   return (readl_relaxed(base + TIMER_HP_CTRL) >> RK_HPTIMER_CTRL_MODE) & 0x3;
}
 
u64 rk_hptimer_get_count(void __iomem *base)
{
   u64 cnt;
 
   cnt = (u64)readl_relaxed(base + TIMER_HP_CURR_TIMER_VALUE0) |
         (u64)readl_relaxed(base + TIMER_HP_CURR_TIMER_VALUE1) << 32;
 
   return cnt;
}
 
int rk_hptimer_wait_mode(void __iomem *base, enum rk_hptimer_mode_t mode)
{
   if (mode == RK_HPTIMER_NORM_MODE)
       return 0;
 
   if (mode == RK_HPTIMER_HARD_ADJUST_MODE) {
       /* wait adjust done if hard_adjust_mode */
       if (rk_hptimer_wait_int_st(base, RK_HPTIMER_INT_ADJ_DONE,
                      HPTIMER_WAIT_MAX_US))
           return -1;
 
       rk_hptimer_clear_int_st(base, RK_HPTIMER_INT_ADJ_DONE);
   } else if (mode == RK_HPTIMER_SOFT_ADJUST_MODE) {
       /* wait 32k sync done */
       if (rk_hptimer_wait_int_st(base, RK_HPTIMER_INT_SYNC,
                      HPTIMER_WAIT_MAX_US))
           return -1;
 
       rk_hptimer_clear_int_st(base, RK_HPTIMER_INT_SYNC);
   }
 
   return 0;
}
 
void rk_hptimer_do_soft_adjust(void __iomem *base)
{
   u64 delt = rk_hptimer_get_soft_adjust_delt_cnt(base);
 
   rk_hptimer_soft_adjust_req(base, delt);
 
   rk_hptimer_wait_mode(base, RK_HPTIMER_SOFT_ADJUST_MODE);
}
 
void rk_hptimer_do_soft_adjust_no_wait(void __iomem *base)
{
   u64 delt = rk_hptimer_get_soft_adjust_delt_cnt(base);
 
   rk_hptimer_soft_adjust_req(base, delt);
}
 
void rk_hptimer_mode_init(void __iomem *base, enum rk_hptimer_mode_t mode)
{
   u64 old_cnt = rk_hptimer_get_count(base);
   u32 val;
 
   writel_relaxed(0x0, base + TIMER_HP_CTRL);
   writel_relaxed(0x0, base + TIMER_HP_INT_EN);
   writel_relaxed(0x7, base + TIMER_HP_INTR_STATUS);
   writel_relaxed(0x3, base + TIMER_HP_BEGIN_END_VALID);
   writel_relaxed(0xffffffff, base + TIMER_HP_LOAD_COUNT0);
   writel_relaxed(0xffffffff, base + TIMER_HP_LOAD_COUNT1);
 
   /* config T24/T32 GCD if hard_adjust_mode */
   if (mode == RK_HPTIMER_HARD_ADJUST_MODE) {
       writel_relaxed(T24M_GCD, base + TIMER_HP_T24_GCD);
       writel_relaxed(T32K_GCD, base + TIMER_HP_T32_GCD);
   }
   dsb();
 
   if (mode != RK_HPTIMER_NORM_MODE) {
       writel_relaxed(0x7, base + TIMER_HP_INT_EN);
       writel_relaxed(mode << RK_HPTIMER_CTRL_MODE, base + TIMER_HP_CTRL);
       dsb();
   }
 
   val = readl_relaxed(base + TIMER_HP_CTRL);
   writel_relaxed(val | BIT(RK_HPTIMER_CTRL_EN), base + TIMER_HP_CTRL);
   dsb();
 
   /* compensate old_cnt to hptimer if soft_adjust_mode */
   if (mode == RK_HPTIMER_SOFT_ADJUST_MODE)
       rk_hptimer_soft_adjust_req(base, old_cnt);
 
   if (rk_hptimer_wait_mode(base, mode))
       pr_err("%s: can't wait hptimer mode:%d\n", __func__, mode);
}