summaryrefslogtreecommitdiff
path: root/bsp/rockchip/common/rk_hal/lib/hal/src/hal_base.c
blob: cd68f1d2cfa44cf781fe5ba51af57d91d014cf4d (plain)
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
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
/* SPDX-License-Identifier: BSD-3-Clause */
/*
 * Copyright (c) 2020-2021 Rockchip Electronics Co., Ltd.
 */

#include "hal_base.h"

/** @addtogroup RK_HAL_Driver
 *  @{
 */

/** @addtogroup HAL_BASE
 *  @{
 */

/** @defgroup HAL_BASE_How_To_Use How To Use
 *  @{

 HAL system support is including delay system, HAL tick system and global system clock,

 HAL system tick setting:

 - Attach HAL_IncTick() to system tick interrupt handler;
 - Notify the HAL system the system's tick frequency by calling HAL_SetTickFreq() unless
    it is the same as default value HAL_TICK_FREQ_1KHZ;
 - If you need a more accurate delay system, specify SYS_TIMER in hal_conf.h.

 Init HAL system:

 - Initialize the HAL system by calling HAL_Init():

 Reset when SOC system is changed:

 - Update system with new core clock and new SysTick clock source by calling HAL_SystemCoreClockUpdate();

 APIs:

 - Get system time by calling HAL_GetTick();
 - Delay for a certain length of time, HAL_DelayMs(), HAL_DelayUs(), and HAL_CPUDelayUs().
 - Blocking for a certain period of time to continuously query HW status, use HAL_GetTick()
 to do timeout, this will be more accurate.

 @} */

/** @defgroup HAL_BASE_Private_Definition Private Definition
 *  @{
 */
/********************* Private MACRO Definition ******************************/

#define HAL_TICK_FREQ_DEFAULT HAL_TICK_FREQ_1KHZ

/********************* Private Structure Definition **************************/

/********************* Private Variable Definition ***************************/

static __IO uint32_t uwTick;
static eHAL_tickFreq uwTickFreq = HAL_TICK_FREQ_DEFAULT;

/********************* Private Function Definition ***************************/
#if defined(__CORTEX_A) || defined(__CORTEX_M)
#if __CORTEX_M == 0U || !defined(__GNUC__)
static void CPUCycleLoop(uint32_t cycles)
{
    uint32_t count;

    if (cycles < 100U) {
        return;
    }

    count = cycles / 3;
    while (count-- > 0) {
        __asm volatile ("nop");
    }
}
#else
static void CPUCycleLoop(uint32_t cycles)
{
    __ASM volatile (
        "mov  r0, %0\n\t"
        "adds r0, r0, #2\n\t"   //    1    2    Round to the nearest multiple of 4.
        "lsrs r0, r0, #2\n\t"   //    1    2    Divide by 4 and set flags.
        "beq  2f\n\t"           //    2    2    Skip if 0.
        ".align 4\n\t"
        "1:\n\t"
        "adds r0, r0, #1\n\t"   //    1    2    Increment the counter.
        "subs r0, r0, #2\n\t"   //    1    2    Decrement the counter by 2.
        "bne  1b\n\t"           //   (1)2  2    2 CPU cycles (if branch is taken).
        "nop\n\t"               //    1    2    Loop alignment padding.
        "2:"
        : : "r" (cycles)
        );
}
#endif
#elif defined(__RISC_V)
static void CPUCycleLoop(uint32_t cycles)
{
    asm volatile (
        "mv   a0, %0\n\t"
        "addi a0, a0, 2\n\t"   //    1    2    Round to the nearest multiple of 4.
        "li   a1, 4\n\t"
        "div  a0, a0, a1\n\t"  //    1    2    Divide by 4 and set flags.
        "li   a1, 2\n\t"
        "bnez a0, 1f\n\t"      //    2    2    Skip if 0.
        "j    2f\n\t"
        ".align 6\n\t"
        "1:\n\t"
        "addi a0, a0, 1\n\t"   //    1    2    Increment the counter.
        "sub  a0, a0, a1\n\t"  //    1    2    Decrement the counter by 2.
        "bnez a0, 1b\n\t"      //   (1)2  2    2 CPU cycles (if branch is taken).
        "nop\n\t"              //    1    2    Loop alignment padding.
        "2:"
        : : "r" (cycles)
        );
}
#endif

#if defined(SYS_TIMER) && defined(HAL_TIMER_MODULE_ENABLED)
__STATIC_FORCEINLINE HAL_Status TimerDelayUs(uint32_t us)
{
    uint64_t count, from, now, pass;

    from = HAL_TIMER_GetCount(SYS_TIMER);
    count = PLL_INPUT_OSC_RATE / 1000000 * us;

    do {
        now = HAL_TIMER_GetCount(SYS_TIMER);
        pass = now > from ? now - from : from - now;
    } while (pass < count);

    return HAL_OK;
}
#endif

/** @} */
/********************* Public Function Definition ***************************/
/** @defgroup HAL_BASE_Exported_Functions_Group4 Init and DeInit Functions

 This section provides functions allowing to init and deinit the module:

 *  @{
 */

/**
 * @brief  Init HAL driver basic code.
 * @return HAL_OK.
 */
HAL_Status HAL_Init(void)
{
#ifdef __CORTEX_M
#ifdef HAL_NVIC_MODULE_ENABLED
    /* Set Interrupt Group Priority */
    HAL_NVIC_Init();

    /* Set Interrupt Group Priority */
    HAL_NVIC_SetPriorityGrouping(NVIC_PRIORITYGROUP_DEFAULT);
#endif
#endif

#if defined(SYS_TIMER) && defined(HAL_TIMER_MODULE_ENABLED)
    HAL_TIMER_SysTimerInit(SYS_TIMER);
#endif

#ifdef RK_BSP_TEMP
#ifdef HAL_PINCTRL_MODULE_ENABLED
    HAL_PINCTRL_Init();
#endif
#endif

    return HAL_OK;
}

/**
 * @brief  HAL system update with new core clock and systick clock source.
 * @param  hz: new core clock.
 * @param  clkSource: new systick clock source.
 * @return HAL_OK.
 */
HAL_Status HAL_SystemCoreClockUpdate(uint32_t hz, eHAL_systickClkSource clkSource)
{
    uint32_t rate = hz;
    HAL_Status ret = HAL_OK;

#if defined(__CORTEX_M) && defined(HAL_SYSTICK_MODULE_ENABLED)
    ret = HAL_SYSTICK_CLKSourceConfig(clkSource);
    if (ret == HAL_OK && clkSource == HAL_SYSTICK_CLKSRC_EXT) {
        rate = PLL_INPUT_OSC_RATE;
    }
    HAL_SYSTICK_Config(rate / (1000 / HAL_GetTickFreq()));
    ret = HAL_OK;
#endif

    if (ret == HAL_OK) {
        SystemCoreClock = rate;   /* Update global SystemCoreClock */
    }

    return ret;
}

/**
 * @brief  HAL deinit.
 * @return HAL_Status: HAL_OK.
 */
HAL_Status HAL_DeInit(void)
{
    /* TO-DO */

    return HAL_OK;
}

/** @} */

/** @defgroup HAL_BASE_Exported_Functions_Group5 Other Functions
 *  @{
 */

/**
 * @brief  Count plus tickFreq when interrupt occurs.
 * @return HAL_Status: HAL_OK.
 */
HAL_Status HAL_IncTick(void)
{
    uwTick += uwTickFreq;

    return HAL_OK;
}

/**
 * @brief  Provides tick value in millisecond.
 * @return uint32_t: tick value in millisecond.
 * @attention this API allow direct use in the HAL layer.
 */
uint32_t HAL_GetTick(void)
{
#if defined(SYS_TIMER) && defined(HAL_TIMER_MODULE_ENABLED)
    uint64_t tick = HAL_TIMER_GetCount(SYS_TIMER);
    uint32_t base = PLL_INPUT_OSC_RATE / 1000;

    if (tick >> 62) {
        tick = ~tick;
    }

    return (uint32_t)HAL_DivU64(tick, base);
#else

    return uwTick;
#endif
}

/**
 * @brief  Provides system timer count.
 * @return uint64_t: timer count.
 * @attention this API allow direct use in the HAL layer.
 */
uint64_t HAL_GetSysTimerCount(void)
{
#if defined(SYS_TIMER) && defined(HAL_TIMER_MODULE_ENABLED)
    uint64_t count = HAL_TIMER_GetCount(SYS_TIMER);
    if (count >> 62) {
        count = ~count;
    }

    return count;
#else

    return 0LLU;
#endif
}

/**
  * @brief Set new tick frequency.
  * @return HAL_Status.
  */
HAL_Status HAL_SetTickFreq(eHAL_tickFreq freq)
{
    HAL_ASSERT(IS_TICKFREQ(freq));

    uwTickFreq = freq;

    return HAL_OK;
}

/**
  * @brief Return tick frequency.
  * @return uint32_t: tick period in Hz.
  * @attention this API allow direct use in the HAL layer.
  */
eHAL_tickFreq HAL_GetTickFreq(void)
{
    return uwTickFreq;
}

/**
 * @brief  SysTick mdelay.
 * @param  ms: mdelay count.
 * @return HAL_Status: HAL_OK.
 * @attention this API allow direct use in the HAL layer. Blocking for a
 *  certain period of time to continuously query HW status, use HAL_GetTick
 *  to do timeout, that will be more accurate.
 */
__WEAK HAL_Status HAL_DelayMs(uint32_t ms)
{
    for (uint32_t i = 0; i < ms; i++) {
        HAL_DelayUs(1000);
    }

    return HAL_OK;
}

/**
 * @brief  SysTick udelay.
 * @param  us: udelay count.
 * @return HAL_Status: HAL_OK.
 * @attention this API allow direct use in the HAL layer. The longer the delay,
 *  the more accurate. Actual delay is greater than the parameter.
 */
HAL_Status HAL_DelayUs(uint32_t us)
{
#if defined(SYS_TIMER) && defined(HAL_TIMER_MODULE_ENABLED)

    return TimerDelayUs(us);
#else

    return HAL_CPUDelayUs(us);
#endif
}

/**
 * @brief  CPU loop udelay.
 * @param  us: udelay count.
 * @return HAL_Status: HAL_OK.
 * @attention this API allow direct use in the HAL layer. The longer the delay,
 *  the more accurate. Actual delay is greater than the parameter.
 *  During delay, CPU rate change result in delay imprecise, so call it in
 *  following case:
 *    1.IRQ disable
 *    2.CRU code
 */
HAL_Status HAL_CPUDelayUs(uint32_t us)
{
    volatile uint32_t cycles;

#if (__CORTEX_M == 0)
    cycles = (uint32_t)HAL_DivU64((uint64_t)SystemCoreClock, 1000000) * us; /* Add few cycles penalty */
#else
    cycles = SystemCoreClock / 1000000 * us; /* Add few cycles penalty */
#endif

    CPUCycleLoop(cycles);

    return HAL_OK;
}

/** @} */

/** @} */

/** @} */