pinebuds/apps/common/app_utils.c

273 lines
7.1 KiB
C

/***************************************************************************
*
* Copyright 2015-2019 BES.
* All rights reserved. All unpublished rights reserved.
*
* No part of this work may be used or reproduced in any form or by any
* means, or stored in a database or retrieval system, without prior written
* permission of BES.
*
* Use of this work is governed by a license granted by BES.
* This work contains confidential and proprietary information of
* BES. which is protected by copyright, trade secret,
* trademark and other intellectual property rights.
*
****************************************************************************/
#include "app_utils.h"
#include "analog.h"
#include "cmsis.h"
#include "hal_timer.h"
#include "hal_trace.h"
#include "hal_wdt.h"
#include "pmu.h"
#ifdef RTOS
#include "cmsis_os.h"
#endif
#define FREQ_FREE 0UL
#define FREQ_26M 26UL
#define FREQ_52M 52UL
#define FREQ_78M 78UL
#define FREQ_104M 104UL
#define FREQ_208M 208UL
/*
* qos_users, quality of services users, this kind of user must run with the
* minist frequency they requiested, if there are more users, the frequency
* should be boost for these users
* e.g. the key word recorgnithion function request 26M Hz, and the music
* also need 26M, if both of them are running, the cpu freq should boost to
* 26M + 26M = 52M Hz
*/
/*
* NOTE:
* The macro QOS_USERS works only when the APP_SYSFREQ_USER_APP_XXX is not large
* than 32, currently this works, but if the are more user, another way needed
*/
#define QOS_USERS \
((1 << (APP_SYSFREQ_USER_AI_VOICE)) | (1 << (APP_SYSFREQ_USER_BT_A2DP)))
static const uint32_t freq_map[] = {
[HAL_CMU_FREQ_32K] = FREQ_FREE, [HAL_CMU_FREQ_26M] = FREQ_26M,
[HAL_CMU_FREQ_52M] = FREQ_52M, [HAL_CMU_FREQ_78M] = FREQ_78M,
[HAL_CMU_FREQ_104M] = FREQ_104M, [HAL_CMU_FREQ_208M] = FREQ_208M,
};
static const uint32_t user_map[] = {
[0] = APP_SYSFREQ_USER_AI_VOICE,
[1] = APP_SYSFREQ_USER_BT_A2DP,
};
/*
* qos_freqs_map
* filled with user's freq, one user's freq occupy 4bits,
* that limit the frequecy number is not large than 16
*
* bit field structure:
-----------------------------------------------------
|user7 | user6 |..................|user1 | user 0|
-----------------------------------------------------
|31~28 | 27~24 |..................|7~4 | 3~0 |
-----------------------------------------------------
*
* Ok, this is ugly, but there is not so much frequecy level,
* and maybe work for a long time
*/
static uint32_t qos_freqs_map;
/*
* qos_users_map
* filled with user's number, one user's occupy 1 bit,
*
bit field structure:
-----------------------------------------------------
|user31~7(reseverd)| user7 |........| user 1| user 0|
-----------------------------------------------------
*
*/
static uint32_t qos_users_map;
static int app_qosfreq_req(enum APP_SYSFREQ_USER_T user,
enum APP_SYSFREQ_FREQ_T freq) {
int ret;
int qos_freq_num = 0;
uint32_t max_qos_freq = 0;
int user_idx;
int i;
uint32_t lock;
if (freq >= APP_SYSFREQ_FREQ_QTY)
return -1;
lock = int_lock();
for (i = 0; i < ARRAY_SIZE(user_map); i++) {
if (user == user_map[i]) {
break;
}
}
if (i >= ARRAY_SIZE(user_map)) {
int_unlock(lock);
ASSERT(0, "can not find qos user");
return 0;
}
user_idx = i;
if ((int)freq != (int)HAL_CMU_FREQ_32K) { // require freq
qos_freqs_map &= ~(0xf << (4 * i));
qos_freqs_map |= freq << (4 * i);
qos_users_map |= 1 << user_idx;
} else { // release freq
qos_freqs_map &= ~(0xf << (4 * i));
qos_users_map &= ~(1 << user_idx);
}
// scan the qos_user_map and sum every user's request freq
for (i = 0; i < ARRAY_SIZE(user_map); i++) {
if ((qos_users_map >> i) & 0x1) {
uint32_t real_freq;
int freq_num;
freq_num = (qos_freqs_map >> (4 * i)) & 0xf;
real_freq = freq_map[freq_num];
max_qos_freq += real_freq;
}
}
for (i = 0; i < ARRAY_SIZE(freq_map); i++) {
if (i) {
if ((max_qos_freq > freq_map[i - 1]) && (max_qos_freq <= freq_map[i])) {
qos_freq_num = i;
break;
}
} else {
if (max_qos_freq == freq_map[i]) {
qos_freq_num = i;
break;
}
}
}
if (i >= ARRAY_SIZE(freq_map)) {
qos_freq_num = (HAL_CMU_FREQ_QTY - 1);
int_unlock(lock);
TRACE(0, "WARNING: required sysfreq exceed");
// ASSERT(0, "can not find actual freq");
return 0;
}
user = APP_SYSFREQ_USER_QOS;
TRACE(2, "User %d require sysfreq %d", user, qos_freq_num);
ret = hal_sysfreq_req((enum HAL_SYSFREQ_USER_T)user,
(enum HAL_CMU_FREQ_T)qos_freq_num);
int_unlock(lock);
return ret;
}
int app_sysfreq_req(enum APP_SYSFREQ_USER_T user,
enum APP_SYSFREQ_FREQ_T freq) {
int ret;
// if user is qos user
if ((1 << user) & QOS_USERS) {
ret = app_qosfreq_req(user, freq);
} else { // if user is NOT qos user
ret = hal_sysfreq_req((enum HAL_SYSFREQ_USER_T)user,
(enum HAL_CMU_FREQ_T)freq);
}
return ret;
}
#ifdef RTOS
extern int rtx_task_idle_health_check(void);
static void watchdog_ping_handler(void const *n);
static osTimerId wdt_ping_timer_id;
osTimerDef(wdt_ping_timer, watchdog_ping_handler);
static uint32_t wdt_ping_period;
static void watchdog_ping(void) {
hal_wdt_ping(HAL_WDT_ID_0);
#ifndef CHIP_BEST2000
pmu_wdt_feed();
#endif
}
static void app_wdt_irq_handle(enum HAL_WDT_ID_T id, uint32_t status) {
analog_aud_codec_mute();
ASSERT(0, "%s id:%d status:%d", __func__, id, status);
}
static void pmu_wdt_irq_handle(void) {
analog_aud_codec_mute();
ASSERT(1, "%s", __func__);
}
static void watchdog_ping_handler(void const *unused) {
int ret;
watchdog_ping();
ret = rtx_task_idle_health_check();
if (ret < 0) {
ASSERT(0, "System soft lockup");
}
osTimerStart(wdt_ping_timer_id, wdt_ping_period);
}
int app_wdt_open(int seconds) {
uint32_t lock = int_lock();
hal_wdt_set_irq_callback(HAL_WDT_ID_0, app_wdt_irq_handle);
hal_wdt_set_timeout(HAL_WDT_ID_0, seconds);
hal_wdt_start(HAL_WDT_ID_0);
pmu_wdt_set_irq_handler(pmu_wdt_irq_handle);
#ifndef CHIP_BEST2000
pmu_wdt_config(seconds * 1100, seconds * 1100);
pmu_wdt_start();
#endif
int_unlock(lock);
wdt_ping_timer_id = osTimerCreate(osTimer(wdt_ping_timer), osTimerOnce, NULL);
if (!wdt_ping_timer_id) {
TRACE(0, "Warning: can not create watchdog ping timer");
return -1;
}
wdt_ping_period = seconds * 1000 / 4;
osTimerStart(wdt_ping_timer_id, wdt_ping_period);
return 0;
}
int app_wdt_reopen(int seconds) {
uint32_t lock = int_lock();
hal_wdt_stop(HAL_WDT_ID_0);
hal_wdt_set_timeout(HAL_WDT_ID_0, seconds);
hal_wdt_start(HAL_WDT_ID_0);
#ifndef CHIP_BEST2000
pmu_wdt_config(seconds * 1000, seconds * 1000);
pmu_wdt_start();
#endif
int_unlock(lock);
osTimerStart(wdt_ping_timer_id, wdt_ping_period);
return 0;
}
int app_wdt_close(void) {
uint32_t lock;
osTimerStop(wdt_ping_timer_id);
lock = int_lock();
hal_wdt_stop(HAL_WDT_ID_0);
#ifndef CHIP_BEST2000
pmu_wdt_stop();
#endif
int_unlock(lock);
return 0;
}
#endif