AC63_BT_SDK/cpu/bd19/adc_api.c
2025-02-18 15:40:42 +08:00

632 lines
15 KiB
C
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#include "typedef.h"
#include "asm/clock.h"
#include "asm/adc_api.h"
#include "timer.h"
#include "init.h"
#include "asm/efuse.h"
#include "irq.h"
#include "asm/power/p33.h"
#include "asm/power/p11.h"
#include "asm/power_interface.h"
#include "jiffies.h"
#include "app_config.h"
u32 adc_sample(u32 ch);
static volatile u16 _adc_res;
static volatile u16 cur_ch_value;
static u8 cur_ch = 0;
struct adc_info_t {
u32 ch;
u16 value;
u32 jiffies;
u32 sample_period;
};
#define ENABLE_OCCUPY_MODE 1
static struct adc_info_t adc_queue[ADC_MAX_CH + ENABLE_OCCUPY_MODE];
static u8 adc_sample_flag = 0;
static u16 dtemp_voltage = 0;
#define ADC_SRC_CLK clk_get("adc")
/*config adc clk according to sys_clk*/
static const u32 sys2adc_clk_info[] = {
128000000L,
96000000L,
72000000L,
48000000L,
24000000L,
12000000L,
6000000L,
1000000L,
};
u32 adc_add_sample_ch(u32 ch)
{
u32 i = 0;
for (i = 0; i < ADC_MAX_CH; i++) {
printf("%s() %d %x %x\n", __func__, i, ch, adc_queue[i].ch);
if (adc_queue[i].ch == ch) {
break;
} else if (adc_queue[i].ch == -1) {
adc_queue[i].ch = ch;
adc_queue[i].value = 1;
adc_queue[i].jiffies = 0;
adc_queue[i].sample_period = msecs_to_jiffies(0);
printf("add sample ch %x\n", ch);
break;
}
}
return i;
}
u32 adc_set_sample_freq(u32 ch, u32 ms)
{
u32 i;
for (i = 0; i < ADC_MAX_CH; i++) {
if (adc_queue[i].ch == ch) {
adc_queue[i].sample_period = msecs_to_jiffies(ms);
adc_queue[i].jiffies = msecs_to_jiffies(ms) + jiffies;
break;
}
}
return i;
}
u32 adc_remove_sample_ch(u32 ch)
{
u32 i = 0;
for (i = 0; i < ADC_MAX_CH; i++) {
if (adc_queue[i].ch == ch) {
adc_queue[i].ch = -1;
break;
}
}
return i;
}
static u32 adc_get_next_ch(u32 cur_ch)
{
for (int i = cur_ch + 1; i < ADC_MAX_CH; i++) {
if (adc_queue[i].ch != -1) {
return i;
}
}
return 0;
}
#define PMU_CH_SAMPLE_FREQ 500 //ms
#define PMU_CH_VALUE_ARRAY_SIZE (16 + 1)
static u16 vbat_value_array[PMU_CH_VALUE_ARRAY_SIZE];
static u16 vbg_value_array[PMU_CH_VALUE_ARRAY_SIZE];
static void adc_value_push(u16 *array, u16 adc_value)
{
u16 pos = array[0];
pos++;
if (pos >= PMU_CH_VALUE_ARRAY_SIZE) {
pos = 1;
}
array[pos] = adc_value;
array[0] = pos;
}
static u16 adc_value_avg(u16 *array)
{
u32 i, j, sum = 0;
for (i = 1, j = 0; i < PMU_CH_VALUE_ARRAY_SIZE; i++) {
if (array[i]) {
sum += array[i];
j += 1;
}
}
if (sum) {
return (sum / j);
}
return 1;
}
static void adc_value_array_reset(u16 *array)
{
for (int i = 0; i < PMU_CH_VALUE_ARRAY_SIZE; i++) {
array[i] = 0;
}
}
u32 adc_get_value(u32 ch)
{
if (ch == AD_CH_VBAT) {
return adc_value_avg(vbat_value_array);
} else if (ch == AD_CH_LDOREF) {
return adc_value_avg(vbg_value_array);
}
for (int i = 0; i < ADC_MAX_CH; i++) {
if (adc_queue[i].ch == ch) {
return adc_queue[i].value;
}
}
return 1;
}
#define VBG_CENTER 801
#define VBG_RES 3
u32 adc_value_to_voltage(u32 adc_vbg, u32 adc_ch_val)
{
u32 adc_res = adc_ch_val;
u32 adc_trim = get_vbg_trim();
u32 tmp, tmp1;
tmp1 = adc_trim & 0x0f;
tmp = (adc_trim & BIT(4)) ? VBG_CENTER - tmp1 * VBG_RES : VBG_CENTER + tmp1 * VBG_RES;
adc_res = adc_res * tmp / adc_vbg;
//printf("adc_res %d mv vbg:%d adc:%d adc_trim:%x\n", adc_res, adc_vbg, adc_ch_val, adc_trim);
return adc_res;
}
u32 adc_get_voltage(u32 ch)
{
#ifdef CONFIG_FPGA_ENABLE
return 1000;
#endif
if (ch == AD_CH_DTEMP) {
return dtemp_voltage;
}
u32 adc_vbg = adc_get_value(AD_CH_LDOREF);
u32 adc_res = adc_get_value(ch);
return adc_value_to_voltage(adc_vbg, adc_res);
}
/*
1、第一次上电: 电压小于2.4v不允许切dcdc模式
2、运行中当检测到电压小于2.4v切换为ldo模式
2、运行中当检测到电压大于2.4v,切换相应的电源模式
*/
void power_set_ldo_for_lowpower(u8 en);
u8 power_set_ldo_for_lowpower_check(u8 init)
{
static u8 ldo_lowpower = 0;
/*
电池电压小于2.4v时切换成ldo模式
*/
u32 vbat = adc_get_voltage(AD_CH_VBAT) * 4;
if (vbat < 2400) {
if (ldo_lowpower == 0) {
local_irq_disable();
power_set_ldo_for_lowpower(1);
power_set_mode(PWR_LDO15);
ldo_lowpower = 1;
local_irq_enable();
}
} else {
if (ldo_lowpower) {
local_irq_disable();
power_set_ldo_for_lowpower(0);
if (TCFG_LOWPOWER_POWER_SEL == PWR_DCDC15) {
if (get_charge_online_flag()) {
power_set_mode(PWR_DCDC15_FOR_CHARGE);
} else {
power_set_mode(PWR_DCDC15);
}
}
ldo_lowpower = 0;
local_irq_enable();
}
}
}
u32 adc_check_vbat_lowpower()
{
power_set_ldo_for_lowpower_check(0);
return 0;
}
void adc_audio_ch_select(u32 ch)
{
/* SFR(JL_ANA->DAA_CON0, 12, 4, ch); */
}
void adc_close()
{
JL_ADC->CON = 0;
JL_ADC->CON = 0;
}
void adc_suspend()
{
JL_ADC->CON &= ~BIT(4);
}
void adc_resume()
{
JL_ADC->CON |= BIT(4);
}
void adc_enter_occupy_mode(u32 ch)
{
if (JL_ADC->CON & BIT(4)) {
return;
}
adc_queue[ADC_MAX_CH].ch = ch;
cur_ch_value = adc_sample(ch);
}
void adc_exit_occupy_mode()
{
adc_queue[ADC_MAX_CH].ch = -1;
}
u32 adc_occupy_run()
{
if (adc_queue[ADC_MAX_CH].ch != -1) {
while (1) {
asm volatile("idle");//wait isr
if (_adc_res != (u16) - 1) {
break;
}
}
if (_adc_res == 0) {
_adc_res ++;
}
adc_queue[ADC_MAX_CH].value = _adc_res;
_adc_res = cur_ch_value;
return adc_queue[ADC_MAX_CH].value;
}
return 0;
}
u32 adc_get_occupy_value()
{
if (adc_queue[ADC_MAX_CH].ch != -1) {
return adc_queue[ADC_MAX_CH].value;
}
return 0;
}
u32 get_adc_div(u32 src_clk)
{
u32 adc_clk;
u32 adc_clk_idx;
u32 cnt;
adc_clk = src_clk;
cnt = ARRAY_SIZE(sys2adc_clk_info);
for (adc_clk_idx = 0; adc_clk_idx < cnt; adc_clk_idx ++) {
if (adc_clk > sys2adc_clk_info[adc_clk_idx]) {
break;
}
}
if (adc_clk_idx < cnt) {
adc_clk_idx = cnt - adc_clk_idx;
} else {
adc_clk_idx = cnt - 1;
}
return adc_clk_idx;
}
___interrupt
static void adc_isr()
{
_adc_res = JL_ADC->RES;
/* P33_CON_SET(P3_ANA_CON4, 5, 1, 0); */
local_irq_disable();
JL_ADC->CON = BIT(6);
local_irq_enable();
#if 1
if (adc_sample_flag > 1) {
extern void adc_scan(void *priv);
adc_scan(NULL);
}
#endif
}
u32 adc_sample(u32 ch)
{
const u32 tmp_adc_res = _adc_res;
_adc_res = (u16) - 1;
u32 adc_con = 0;
SFR(adc_con, 0, 3, 0b110);//div 96
adc_con |= (0xf << 12); //启动延时控制,实际启动延时为此数值*8个ADC时钟
adc_con |= BIT(3); //ana en
adc_con |= BIT(6); //en
adc_con |= BIT(5);//ie
SFR(adc_con, 8, 4, ch & 0xf);
if ((ch & 0xffff) == AD_CH_PMU) {
if (adc_sample_flag > 1) {
SFR(adc_con, 0, 3, 0b011);//div 24
adc_con &= ~(0xf << 12);
}
adc_pmu_ch_select(ch >> 16);
}
JL_ADC->CON = adc_con;
JL_ADC->CON |= BIT(4);//en
JL_ADC->CON |= BIT(6);//kistart
return tmp_adc_res;
}
void adc_scan(void *priv)
{
static u16 vbg_adc_res = 0;
static u16 dtemp_adc_res = 0;
if (adc_queue[ADC_MAX_CH].ch != -1) { //occupy mode
return;
}
if (JL_ADC->CON & BIT(4)) {
adc_sample_flag = 0;
return ;
}
if (adc_sample_flag) {
if (adc_sample_flag == 2) {
dtemp_adc_res = _adc_res;
adc_sample_flag = 3;
adc_sample(AD_CH_LDOREF);
return;
} else if (adc_sample_flag == 3) {
vbg_adc_res = _adc_res;
dtemp_voltage = adc_value_to_voltage(vbg_adc_res, dtemp_adc_res);
/* printf("vbg:%d dtemp:%d vol:%dmv\n", vbg_adc_res, dtemp_adc_res, dtemp_voltage); */
} else {
adc_queue[cur_ch].value = _adc_res;
if (adc_queue[cur_ch].ch == AD_CH_VBAT) {
adc_value_push(vbat_value_array, _adc_res);
/* printf("vbat %d",_adc_res); */
} else if (adc_queue[cur_ch].ch == AD_CH_LDOREF) {
adc_value_push(vbg_value_array, _adc_res);
/* printf("vbg %d",_adc_res); */
}
}
adc_sample_flag = 0;
}
u8 next_ch = adc_get_next_ch(cur_ch);
if (adc_queue[next_ch].sample_period) {
if (time_before(adc_queue[next_ch].jiffies, jiffies)) {
if (adc_queue[next_ch].ch == AD_CH_DTEMP) {
adc_sample_flag = 2;
} else {
adc_sample_flag = 1;
}
adc_sample(adc_queue[next_ch].ch);
adc_queue[next_ch].jiffies += adc_queue[next_ch].sample_period;
}
} else {
adc_sample(adc_queue[next_ch].ch);
adc_sample_flag = 1;
}
cur_ch = next_ch;
}
static u16 adc_wait_pnd()
{
while (!(JL_ADC->CON & BIT(7)));
u32 adc_res = JL_ADC->RES;
asm("nop");
JL_ADC->CON |= BIT(6);
return adc_res;
}
void _adc_init(u32 sys_lvd_en)
{
memset(adc_queue, 0xff, sizeof(adc_queue));
memset(vbg_value_array, 0x0, sizeof(vbg_value_array));
memset(vbat_value_array, 0x0, sizeof(vbat_value_array));
JL_ADC->CON = 0;
adc_add_sample_ch(AD_CH_LDOREF);
adc_set_sample_freq(AD_CH_LDOREF, PMU_CH_SAMPLE_FREQ);
adc_add_sample_ch(AD_CH_VBAT);
adc_set_sample_freq(AD_CH_VBAT, PMU_CH_SAMPLE_FREQ);
adc_add_sample_ch(AD_CH_DTEMP);
adc_set_sample_freq(AD_CH_DTEMP, PMU_CH_SAMPLE_FREQ);
u32 sum_ad = 0;
adc_sample(AD_CH_LDOREF);
for (int i = 0; i < PMU_CH_VALUE_ARRAY_SIZE; i ++) {
sum_ad += adc_wait_pnd();
}
sum_ad /= PMU_CH_VALUE_ARRAY_SIZE;
adc_value_push(vbg_value_array, sum_ad);
printf("vbg_adc_value = %d\n", adc_value_avg(vbg_value_array));
sum_ad = 0;
adc_sample(AD_CH_VBAT);
for (int i = 0; i < PMU_CH_VALUE_ARRAY_SIZE; i ++) {
sum_ad += adc_wait_pnd();
}
sum_ad /= PMU_CH_VALUE_ARRAY_SIZE;
adc_value_push(vbat_value_array, sum_ad);
printf("vbat_adc_value = %d\n", adc_value_avg(vbat_value_array));
printf("vbat = %d mv\n", adc_get_voltage(AD_CH_VBAT) * 4);
sum_ad = 0;
adc_sample(AD_CH_DTEMP);
for (int i = 0; i < PMU_CH_VALUE_ARRAY_SIZE; i ++) {
sum_ad += adc_wait_pnd();
}
sum_ad /= PMU_CH_VALUE_ARRAY_SIZE;
dtemp_voltage = adc_value_to_voltage(adc_value_avg(vbg_value_array), sum_ad);
printf("dtemp_adc_value = %d\n", sum_ad);
printf("dtemp = %d mv\n", dtemp_voltage);
request_irq(IRQ_SARADC_IDX, 0, adc_isr, 0);
usr_timer_add(NULL, adc_scan, 5, 0); //2ms
/* sys_timer_add(NULL, adc_scan, 10); //2ms */
/* void adc_test(); */
/* usr_timer_add(NULL, adc_test, 1000, 0); //2ms */
/* extern void wdt_close(); */
/* wdt_close(); */
/* */
/* while(1); */
}
static u32 get_vdd_voltage(u32 ch)
{
u32 vbg_value = 0;
u32 wvdd_value = 0;
adc_sample(AD_CH_LDOREF);
for (int i = 0; i < 10; i++) {
vbg_value += adc_wait_pnd();
}
adc_sample(ch);
for (int i = 0; i < 10; i++) {
wvdd_value += adc_wait_pnd();
}
u32 adc_vbg = vbg_value / 10;
u32 adc_res = wvdd_value / 10;
return adc_value_to_voltage(adc_vbg, adc_res);
}
u8 wvdd_efuse_level[4] = {WVDD_VOL_SEL_065V, WVDD_VOL_SEL_070V, WVDD_VOL_SEL_075V, WVDD_VOL_SEL_080V};
static u8 wvdd_trim(u8 trim)
{
u8 wvdd_lev = 0;
u8 wvdd_efuse_lev = ((p33_rd_page(1) >> 24) & 0x3);
if (trim) {
wvdd_lev = wvdd_efuse_level[wvdd_efuse_lev];
} else {
wvdd_lev = get_wvdd_trim_level();
}
if (wvdd_lev < WVDD_VOL_SEL_065V) {
wvdd_lev = WVDD_VOL_SEL_080V;
}
printf("trim: %d, wvdd_lev: %d\n", trim, wvdd_lev);
M2P_WDVDD = wvdd_lev;
return wvdd_lev;
}
static u8 pvdd_trim(u8 trim)
{
u32 v = 0;
u32 lev = 0xf;
u8 err = 0;
if (trim) {
for (; lev; lev--) {
P33_CON_SET(P3_PVDD1_AUTO, 0, 8, (lev | lev << 4));
delay(2000);//1ms
v = get_vdd_voltage(AD_CH_PVDD);
if (v < (PVDD_VOL_TRIM + PVDD_VOL_STEP)) {
break;
}
}
if (v < PVDD_VOL_TRIM) {
if (lev < PVDD_LEVEL_MAX) {
lev++;
}
}
//update_pvdd_trim_level(lev);
} else {
lev = get_pvdd_trim_level();
}
#if 0
printf("pvdd min: %d, max: %d, def_lev: %d\n", (PVDD_VOL_TRIM - PVDD_VOL_MIN) / PVDD_VOL_STEP - 2, \
(PVDD_VOL_TRIM - PVDD_VOL_MIN) / PVDD_VOL_STEP + 2, \
PVDD_LEVEL_DEFAULT);
#endif
printf("trim: %d, pvdd_lev: %d, pvdd_lev_l: %d\n", trim, lev, lev - PVDD_LEVEL_TRIM_LOW);
if (trim) {
u8 min = (PVDD_VOL_TRIM - PVDD_VOL_MIN) / PVDD_VOL_STEP - 2;
u8 max = (PVDD_VOL_TRIM - PVDD_VOL_MIN) / PVDD_VOL_STEP + 2;
if (!(lev >= min && lev <= max)) {
lev = PVDD_LEVEL_DEFAULT;
err = 1;
}
}
P33_CON_SET(P3_PVDD1_AUTO, 0, 8, (lev | lev << 4));
delay(2000);
P33_CON_SET(P3_PVDD0_AUTO, 0, 8, (7 << 4) | (lev - PVDD_LEVEL_TRIM_LOW));
if (err) {
return PVDD_LEVEL_ERR;
}
return lev;
}
void adc_init()
{
adc_pmu_detect_en(1);
//trim wvdd
u8 trim = check_wvdd_pvdd_trim(0);
u8 wvdd_lev = wvdd_trim(trim);
u8 pvdd_lev = pvdd_trim(trim);
if (trim) {
if ((wvdd_lev != WVDD_LEVEL_ERR) && (pvdd_lev != PVDD_LEVEL_ERR)) {
update_wvdd_pvdd_trim_level(wvdd_lev, pvdd_lev);
}
}
_adc_init(1);
//这个函数之后才能切电源模式
power_set_ldo_for_lowpower_check(1);
}
//late_initcall(adc_init);
void adc_test()
{
/* printf("\n\n%s() chip_id :%x\n", __func__, get_chip_id()); */
/* printf("%s() vbg trim:%x\n", __func__, get_vbg_trim()); */
/* printf("%s() vbat trim:%x\n", __func__, get_vbat_trim()); */
/* printf("\n\nWLA_CON0 %x\n", JL_ANA->WLA_CON0); */
/* printf("WLA_CON9 %x\n", JL_ANA->WLA_CON9); */
/* printf("WLA_CON10 %x\n", JL_ANA->WLA_CON10); */
/* printf("WLA_CON21 %x\n", JL_ANA->WLA_CON21); */
/* printf("ADA_CON %x\n", JL_ANA->ADA_CON3); */
/* printf("PLL_CON1 %x\n", JL_CLOCK->PLL_CON1); */
printf("\n%s() VBAT:%d %d mv\n\n", __func__,
adc_get_value(AD_CH_VBAT), adc_get_voltage(AD_CH_VBAT) * 4);
/* printf("\n%s() pa3:%d %d mv\n\n", __func__, */
/* adc_get_value(AD_CH_PA3), adc_get_voltage(AD_CH_PA3)); */
}
void adc_vbg_init()
{
return ;
}
//__initcall(adc_vbg_init);