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
|
/*
* SPDX-License-Identifier: BSD-2-Clause
*
* Copyright (c) 2019 Western Digital Corporation or its affiliates.
*
* Authors:
* Anup Patel <[email protected]>
*/
#ifndef __SBI_TIMER_H__
#define __SBI_TIMER_H__
#include <sbi/sbi_list.h>
/** Timer event re-start details */
struct sbi_timer_event_restart {
/** Flag indicating whether event re-start is required */
bool required;
/** Next time stamp for event if re-start is required */
u64 next_event;
};
/** Timer event abstraction */
struct sbi_timer_event {
/** List head for per-HART event list (Internal) */
struct sbi_dlist head;
/** Hart on which the event is started / running (Internal) */
int hart_index;
/** Time stamp when the event expires (Internal) */
u64 time_stamp;
/**
* Event callback to be called upon expiry.
*
* If the callback wants to re-start the event then
* it must update the event re-start details.
*
* NOTE: This will be called with the per-HART timer
* event list lock held.
*/
void (*callback)(struct sbi_timer_event *ev,
struct sbi_timer_event_restart *restart);
/**
* Event cleanup to be called upon sbi_timer_exit()
*
* NOTE: This will be called with per-HART timer
* event list lock held.
*/
void (*cleanup)(struct sbi_timer_event *ev);
/** Event specific private data */
void *priv;
};
#define SBI_INIT_TIMER_EVENT(__ptr, __callback, __cleanup, __priv) \
do { \
SBI_INIT_LIST_HEAD(&(__ptr)->head); \
(__ptr)->hart_index = -1; \
(__ptr)->time_stamp = 0; \
(__ptr)->callback = (__callback); \
(__ptr)->cleanup = (__cleanup); \
(__ptr)->priv = (__priv); \
} while (0)
/** Timer hardware device */
struct sbi_timer_device {
/** Name of the timer operations */
char name[32];
/** Frequency of timer in HZ */
unsigned long timer_freq;
/** Get free-running timer value */
u64 (*timer_value)(void);
/** Start timer event for current HART */
void (*timer_event_start)(u64 next_event);
/** Stop timer event for current HART */
void (*timer_event_stop)(void);
/** Initialize timer device for current HART */
int (*warm_init)(void);
};
struct sbi_scratch;
/** Compute timer value delta based on arbitary units */
u64 sbi_timer_compute_delta(ulong units, u64 unit_freq);
/** Compute timer value delta from milliseconds */
static inline u64 sbi_timer_compute_mdelta(ulong msecs)
{
return sbi_timer_compute_delta(msecs, 1000);
}
/** Compute timer value delta from microseconds */
static inline u64 sbi_timer_compute_udelta(ulong usecs)
{
return sbi_timer_compute_delta(usecs, 1000000);
}
/** Generic delay loop of desired granularity */
void sbi_timer_delay_loop(ulong units, u64 unit_freq,
void (*delay_fn)(void *), void *opaque);
/** Provide delay in terms of milliseconds */
static inline void sbi_timer_mdelay(ulong msecs)
{
sbi_timer_delay_loop(msecs, 1000, NULL, NULL);
}
/** Provide delay in terms of microseconds */
static inline void sbi_timer_udelay(ulong usecs)
{
sbi_timer_delay_loop(usecs, 1000000, NULL, NULL);
}
/**
* A blocking function that will wait until @p predicate returns true or
* @p timeout_ms milliseconds elapsed. @p arg will be passed as argument to
* @p predicate function.
*
* @param predicate Pointer to a function that returns true if certain
* condition is met. It shouldn't block the code execution.
* @param arg Argument to pass to @p predicate.
* @param timeout_ms Timeout value in milliseconds. The function will return
* false if @p timeout_ms time period elapsed but still @p predicate doesn't
* return true.
*
* @return true if @p predicate returns true within @p timeout_ms, false
* otherwise.
*/
bool sbi_timer_waitms_until(bool (*predicate)(void *), void *arg,
uint64_t timeout_ms);
/** Get timer value for current HART */
u64 sbi_timer_value(void);
/** Compute timer value after specified milliseconds */
static inline u64 sbi_timer_value_after_msecs(ulong msecs)
{
return sbi_timer_value() + sbi_timer_compute_mdelta(msecs);
}
/** Compute timer value after specified microseconds */
static inline u64 sbi_timer_value_after_usecs(ulong usecs)
{
return sbi_timer_value() + sbi_timer_compute_udelta(usecs);
}
/** Get virtualized timer value for current HART */
u64 sbi_timer_virt_value(void);
/** Get timer delta value for current HART */
u64 sbi_timer_get_delta(void);
/** Set timer delta value for current HART */
void sbi_timer_set_delta(ulong delta);
#if __riscv_xlen == 32
/** Set upper 32-bits of timer delta value for current HART */
void sbi_timer_set_delta_upper(ulong delta_upper);
#endif
/** Start timer event on current HART */
void sbi_timer_event_start(struct sbi_timer_event *ev, u64 next_event);
/** Stop timer event on current HART */
void sbi_timer_event_stop(struct sbi_timer_event *ev);
/** Start supervisor timer event on current HART */
void sbi_timer_smode_event_start(u64 next_event);
/** Process timer event for current HART */
void sbi_timer_process(void);
/** Get current timer device */
const struct sbi_timer_device *sbi_timer_get_device(void);
/** Register timer device */
void sbi_timer_set_device(const struct sbi_timer_device *dev);
/* Initialize timer */
int sbi_timer_init(struct sbi_scratch *scratch, bool cold_boot);
/* Exit timer */
void sbi_timer_exit(struct sbi_scratch *scratch);
#endif
|