drivers: adc: add bflb adc driver

Adds a driver for the GPADC

Signed-off-by: Camille BAUD <mail@massdriver.space>
This commit is contained in:
Camille BAUD
2025-10-31 02:06:58 +01:00
committed by Fabio Baltieri
parent 33196ff3cd
commit bddaa08afd
4 changed files with 714 additions and 0 deletions

View File

@@ -22,6 +22,7 @@ zephyr_library_sources_ifdef(CONFIG_ADC_ADS1X1X adc_ads1x1x.c)
zephyr_library_sources_ifdef(CONFIG_ADC_ADS1X4S0X adc_ads1x4s0x.c)
zephyr_library_sources_ifdef(CONFIG_ADC_ADS7052 adc_ads7052.c)
zephyr_library_sources_ifdef(CONFIG_ADC_AMBIQ adc_ambiq.c)
zephyr_library_sources_ifdef(CONFIG_ADC_BFLB adc_bflb.c)
zephyr_library_sources_ifdef(CONFIG_ADC_CC13XX_CC26XX adc_cc13xx_cc26xx.c)
zephyr_library_sources_ifdef(CONFIG_ADC_CC23X0 adc_cc23x0.c)
zephyr_library_sources_ifdef(CONFIG_ADC_CC32XX adc_cc32xx.c)

View File

@@ -88,6 +88,7 @@ source "drivers/adc/Kconfig.ads1x4s0x"
source "drivers/adc/Kconfig.ads7052"
source "drivers/adc/Kconfig.ambiq"
source "drivers/adc/Kconfig.b91"
source "drivers/adc/Kconfig.bflb"
source "drivers/adc/Kconfig.cc13xx_cc26xx"
source "drivers/adc/Kconfig.cc23x0"
source "drivers/adc/Kconfig.cc32xx"

10
drivers/adc/Kconfig.bflb Normal file
View File

@@ -0,0 +1,10 @@
# Copyright (c) 2024-2025 MASSDRIVER EI
# SPDX-License-Identifier: Apache-2.0
config ADC_BFLB
bool "Bouffalolab ADC driver"
default y
depends on DT_HAS_BFLB_ADC_ENABLED
select ADC_CONFIGURABLE_INPUTS
help
Enables the Bouffalolab ADC driver.

702
drivers/adc/adc_bflb.c Normal file
View File

@@ -0,0 +1,702 @@
/*
* Copyright (c) 2024-2025 MASSDRIVER EI (massdriver.space)
*
* SPDX-License-Identifier: Apache-2.0
*/
#define DT_DRV_COMPAT bflb_adc
#include <zephyr/irq.h>
#include <zephyr/drivers/adc.h>
#include <zephyr/drivers/pinctrl.h>
#include <zephyr/drivers/syscon.h>
#include <zephyr/logging/log.h>
LOG_MODULE_REGISTER(adc_bflb, CONFIG_ADC_LOG_LEVEL);
#include <bflb_soc.h>
#include <glb_reg.h>
#include <hbn_reg.h>
#include <common_defines.h>
#include <bouffalolab/common/adc_reg.h>
#include <zephyr/drivers/clock_control/clock_control_bflb_common.h>
#define ADC_CHAN_SELECT_PER_SCN 6
#define ADC_CHAN_SELECT_SIZE_SCN 5
#define ADC_CHAN_SELECT_MSK_SCN 0x1f
#define ADC_CHAN_COUNT 12
#define ADC_CHAN_INPUT_COUNT 0x1f
#define ADC_GAIN_1_ID 1
#define ADC_GAIN_2_ID 2
#define ADC_GAIN_4_ID 3
#define ADC_GAIN_8_ID 4
#define ADC_GAIN_16_ID 5
#define ADC_GAIN_32_ID 6
#define ADC_GAIN_UNSET ADC_GAIN_128
#define ADC_RESOLUTION_12B_ID 0
#define ADC_RESOLUTION_14B_ID 2
#define ADC_RESOLUTION_16B_ID 4
#define ADC_INPUT_ID_HALF_VBAT 18
#define ADC_INPUT_ID_GND 23
#define ADC_RESULT_POSITIVE_INPUT 0x3E00000
#define ADC_RESULT_POSITIVE_INPUT_POS 21
#define ADC_RESULT_NEGATIVE_INPUT 0x1F0000
#define ADC_RESULT_NEGATIVE_INPUT_POS 16
#define ADC_RESULT 0xFFFF
#define ADC_WAIT_TIMEOUT_MS 500
#define ADC_CLK_DIV_32 7
struct adc_bflb_config {
uint32_t reg_GPIP;
uint32_t reg_AON;
const struct pinctrl_dev_config *pcfg;
void (*irq_config_func)(const struct device *dev);
};
struct adc_bflb_data {
uint8_t channel_count;
uint8_t channel_p[12];
uint8_t channel_n[12];
enum adc_gain gain;
bool differential;
float cal_coe;
uint16_t cal_off;
};
static void adc_bflb_channel_set_channel(const struct device *dev, uint8_t id,
uint8_t channel_number_n, uint8_t channel_number_p)
{
const struct adc_bflb_config *const cfg = dev->config;
uint32_t offset_p = AON_GPADC_REG_SCN_POS1_OFFSET;
uint32_t offset_n = AON_GPADC_REG_SCN_NEG1_OFFSET;
uint32_t tmp;
if (id >= ADC_CHAN_SELECT_PER_SCN) {
offset_p = AON_GPADC_REG_SCN_POS2_OFFSET;
offset_n = AON_GPADC_REG_SCN_NEG2_OFFSET;
}
tmp = sys_read32(cfg->reg_AON + offset_p);
tmp &= ~(ADC_CHAN_SELECT_MSK_SCN
<< ((id % ADC_CHAN_SELECT_PER_SCN) * ADC_CHAN_SELECT_SIZE_SCN));
tmp |= channel_number_p << ((id % ADC_CHAN_SELECT_PER_SCN) * ADC_CHAN_SELECT_SIZE_SCN);
sys_write32(tmp, cfg->reg_AON + offset_p);
tmp = sys_read32(cfg->reg_AON + offset_n);
tmp &= ~(ADC_CHAN_SELECT_MSK_SCN
<< ((id % ADC_CHAN_SELECT_PER_SCN) * ADC_CHAN_SELECT_SIZE_SCN));
tmp |= channel_number_n << ((id % ADC_CHAN_SELECT_PER_SCN) * ADC_CHAN_SELECT_SIZE_SCN);
sys_write32(tmp, cfg->reg_AON + offset_n);
}
static int adc_bflb_channel_setup(const struct device *dev,
const struct adc_channel_cfg *channel_cfg)
{
const struct adc_bflb_config *const cfg = dev->config;
struct adc_bflb_data *data = dev->data;
uint32_t tmp;
uint8_t channel_id = channel_cfg->channel_id;
uint8_t gain = ADC_GAIN_1_ID;
if (data->channel_count > ADC_CHAN_COUNT) {
LOG_ERR("Too many channels");
return -ENOTSUP;
}
if (channel_cfg->input_negative > ADC_CHAN_INPUT_COUNT
|| channel_cfg->input_positive > ADC_CHAN_INPUT_COUNT) {
LOG_ERR("Bad channel number(s)");
return -EINVAL;
}
if (channel_id >= ADC_CHAN_COUNT) {
LOG_ERR("Bad channel ID");
return -EINVAL;
}
switch (channel_cfg->gain) {
case ADC_GAIN_1:
gain = ADC_GAIN_1_ID;
break;
case ADC_GAIN_2:
gain = ADC_GAIN_2_ID;
break;
case ADC_GAIN_4:
gain = ADC_GAIN_4_ID;
break;
case ADC_GAIN_8:
gain = ADC_GAIN_8_ID;
break;
case ADC_GAIN_16:
gain = ADC_GAIN_16_ID;
break;
case ADC_GAIN_32:
gain = ADC_GAIN_32_ID;
break;
default:
LOG_ERR("Gain must be between 1 and 32 (included), cannot be 3, 6, 12, 24");
return -EINVAL;
}
if (data->gain != ADC_GAIN_UNSET && data->gain != channel_cfg->gain) {
LOG_WRN("Gain does not match previously set gain, gain is global for this adc");
}
data->gain = channel_cfg->gain;
if (data->gain != ADC_GAIN_UNSET && data->differential != channel_cfg->differential) {
LOG_WRN("Differential mode does not match previously set mode, it is global");
}
if (channel_cfg->differential) {
data->channel_n[channel_id] = channel_cfg->input_negative;
} else {
data->channel_n[channel_id] = ADC_INPUT_ID_GND;
}
data->channel_p[channel_id] = channel_cfg->input_positive;
if (data->channel_count == 0) {
tmp = sys_read32(cfg->reg_AON + AON_GPADC_REG_CONFIG1_OFFSET);
tmp |= AON_GPADC_CONT_CONV_EN;
tmp &= ~AON_GPADC_SCAN_EN;
tmp &= ~AON_GPADC_CLK_ANA_INV;
sys_write32(tmp, cfg->reg_AON + AON_GPADC_REG_CONFIG1_OFFSET);
tmp = sys_read32(cfg->reg_AON + AON_GPADC_REG_CMD_OFFSET);
tmp &= ~AON_GPADC_POS_SEL_MASK;
tmp &= ~AON_GPADC_NEG_SEL_MASK;
if (channel_cfg->differential) {
tmp &= ~AON_GPADC_NEG_GND;
tmp |= channel_cfg->input_negative << AON_GPADC_NEG_SEL_SHIFT;
} else {
tmp |= AON_GPADC_NEG_GND;
/* GND channel */
tmp |= ADC_INPUT_ID_GND << AON_GPADC_NEG_SEL_SHIFT;
}
tmp |= channel_cfg->input_positive << AON_GPADC_POS_SEL_SHIFT;
sys_write32(tmp, cfg->reg_AON + AON_GPADC_REG_CMD_OFFSET);
adc_bflb_channel_set_channel(dev, 0, data->channel_n[channel_id],
data->channel_p[channel_id]);
} else {
tmp = sys_read32(cfg->reg_AON + AON_GPADC_REG_CONFIG1_OFFSET);
tmp &= ~AON_GPADC_CONT_CONV_EN;
tmp |= AON_GPADC_SCAN_EN;
tmp |= AON_GPADC_CLK_ANA_INV;
tmp &= ~AON_GPADC_SCAN_LENGTH_MASK;
tmp |= data->channel_count << AON_GPADC_SCAN_LENGTH_SHIFT;
sys_write32(tmp, cfg->reg_AON + AON_GPADC_REG_CONFIG1_OFFSET);
tmp = sys_read32(cfg->reg_AON + AON_GPADC_REG_CMD_OFFSET);
tmp &= ~AON_GPADC_POS_SEL_MASK;
tmp &= ~AON_GPADC_NEG_SEL_MASK;
tmp |= AON_GPADC_NEG_GND;
sys_write32(tmp, cfg->reg_AON + AON_GPADC_REG_CMD_OFFSET);
adc_bflb_channel_set_channel(dev, data->channel_count, data->channel_n[channel_id],
data->channel_p[channel_id]);
}
tmp = sys_read32(cfg->reg_AON + AON_GPADC_REG_CONFIG2_OFFSET);
tmp |= (gain << AON_GPADC_PGA1_GAIN_SHIFT);
tmp |= (gain << AON_GPADC_PGA2_GAIN_SHIFT);
if (channel_cfg->differential) {
tmp |= AON_GPADC_DIFF_MODE;
} else {
tmp &= ~AON_GPADC_DIFF_MODE;
}
sys_write32(tmp, cfg->reg_AON + AON_GPADC_REG_CONFIG2_OFFSET);
data->channel_count++;
return 0;
}
static uint32_t adc_bflb_read_one(const struct device *dev)
{
const struct adc_bflb_config *const cfg = dev->config;
while ((sys_read32(cfg->reg_GPIP + GPIP_GPADC_CONFIG_OFFSET) &
GPIP_GPADC_FIFO_DATA_COUNT_MASK) == 0) {
clock_bflb_settle();
}
return sys_read32(cfg->reg_GPIP + GPIP_GPADC_DMA_RDATA_OFFSET) & GPIP_GPADC_DMA_RDATA_MASK;
}
static void adc_bflb_trigger(const struct device *dev)
{
const struct adc_bflb_config *const cfg = dev->config;
uint32_t tmp;
tmp = sys_read32(cfg->reg_AON + AON_GPADC_REG_CMD_OFFSET);
tmp |= AON_GPADC_CONV_START;
sys_write32(tmp, cfg->reg_AON + AON_GPADC_REG_CMD_OFFSET);
}
static void adc_bflb_detrigger(const struct device *dev)
{
const struct adc_bflb_config *const cfg = dev->config;
uint32_t tmp;
tmp = sys_read32(cfg->reg_AON + AON_GPADC_REG_CMD_OFFSET);
tmp &= ~AON_GPADC_CONV_START;
sys_write32(tmp, cfg->reg_AON + AON_GPADC_REG_CMD_OFFSET);
}
static int adc_bflb_read(const struct device *dev,
const struct adc_sequence *sequence)
{
struct adc_bflb_data *data = dev->data;
const struct adc_bflb_config *const cfg = dev->config;
uint32_t tmp;
uint8_t chan_nb = 0;
uint32_t nb_samples = 0;
uint8_t sample_chans[ADC_CHAN_COUNT] = {0};
k_timepoint_t end_timeout = sys_timepoint_calc(K_MSEC(ADC_WAIT_TIMEOUT_MS));
for (uint8_t i = 0; i < ADC_CHAN_COUNT; i++) {
if ((sequence->channels >> i) & 0x1) {
sample_chans[chan_nb] = i;
chan_nb += 1;
}
}
nb_samples = sequence->buffer_size / 2 / chan_nb;
if (nb_samples < 1) {
LOG_ERR("resolution 12 to 16 bits, buffer size invalid");
return -EINVAL;
}
tmp = sys_read32(cfg->reg_AON + AON_GPADC_REG_CONFIG1_OFFSET);
tmp &= ~AON_GPADC_RES_SEL_MASK;
switch (sequence->resolution) {
case 12:
tmp |= ADC_RESOLUTION_12B_ID << AON_GPADC_RES_SEL_SHIFT;
break;
case 14:
tmp |= ADC_RESOLUTION_14B_ID << AON_GPADC_RES_SEL_SHIFT;
break;
case 16:
tmp |= ADC_RESOLUTION_16B_ID << AON_GPADC_RES_SEL_SHIFT;
break;
default:
LOG_ERR("resolution 12, 14 or 16 bits, resolution invalid");
return -EINVAL;
}
sys_write32(tmp, cfg->reg_AON + AON_GPADC_REG_CONFIG1_OFFSET);
tmp = sys_read32(cfg->reg_GPIP + GPIP_GPADC_CONFIG_OFFSET);
tmp |= GPIP_GPADC_FIFO_CLR;
sys_write32(tmp, cfg->reg_GPIP + GPIP_GPADC_CONFIG_OFFSET);
adc_bflb_trigger(dev);
for (int i = 0; i < nb_samples; i++) {
for (int j = 0; j < chan_nb; j++) {
tmp = adc_bflb_read_one(dev);
while (((tmp & ADC_RESULT_POSITIVE_INPUT)
>> ADC_RESULT_POSITIVE_INPUT_POS
!= data->channel_p[sample_chans[j]]
|| (tmp & ADC_RESULT_NEGATIVE_INPUT)
>> ADC_RESULT_NEGATIVE_INPUT_POS
!= data->channel_n[sample_chans[j]])
&& !sys_timepoint_expired(end_timeout)) {
tmp = adc_bflb_read_one(dev);
}
((uint16_t *)sequence->buffer)[i * chan_nb + j] = ((tmp & ADC_RESULT)
>> (16 - sequence->resolution)) / data->cal_coe - data->cal_off;
}
}
if (sys_timepoint_expired(end_timeout)) {
return -ETIMEDOUT;
}
adc_bflb_detrigger(dev);
return 0;
}
static void adc_bflb_isr(const struct device *dev)
{
/* Do nothing */
}
#if defined(CONFIG_SOC_SERIES_BL60X)
static void adc_bflb_calibrate_dynamic(const struct device *dev)
{
struct adc_bflb_data *data = dev->data;
const struct adc_bflb_config *const cfg = dev->config;
volatile uint32_t tmp;
volatile uint32_t offset = 0;
bool negative = false;
tmp = sys_read32(cfg->reg_AON + AON_GPADC_REG_CONFIG1_OFFSET);
/* resolution 16-bits */
tmp |= (ADC_RESOLUTION_16B_ID << AON_GPADC_RES_SEL_SHIFT);
/* continuous mode */
tmp |= AON_GPADC_CONT_CONV_EN;
sys_write32(tmp, cfg->reg_AON + AON_GPADC_REG_CONFIG1_OFFSET);
tmp = sys_read32(cfg->reg_AON + AON_GPADC_REG_CONFIG2_OFFSET);
tmp |= AON_GPADC_DIFF_MODE;
tmp |= AON_GPADC_VBAT_EN;
tmp &= ~AON_GPADC_VREF_SEL;
sys_write32(tmp, cfg->reg_AON + AON_GPADC_REG_CONFIG2_OFFSET);
tmp = sys_read32(cfg->reg_AON + AON_GPADC_REG_CMD_OFFSET);
tmp &= ~AON_GPADC_NEG_GND;
tmp &= ~AON_GPADC_POS_SEL_MASK;
tmp &= ~AON_GPADC_NEG_SEL_MASK;
tmp |= (ADC_INPUT_ID_HALF_VBAT << AON_GPADC_POS_SEL_SHIFT);
tmp |= (ADC_INPUT_ID_HALF_VBAT << AON_GPADC_NEG_SEL_SHIFT);
sys_write32(tmp, cfg->reg_AON + AON_GPADC_REG_CMD_OFFSET);
tmp = sys_read32(cfg->reg_GPIP + GPIP_GPADC_CONFIG_OFFSET);
tmp |= GPIP_GPADC_FIFO_CLR;
sys_write32(tmp, cfg->reg_GPIP + GPIP_GPADC_CONFIG_OFFSET);
clock_bflb_settle();
clock_bflb_settle();
clock_bflb_settle();
adc_bflb_trigger(dev);
/* 10 samplings */
for (uint8_t i = 0; i < 10; i++) {
tmp = adc_bflb_read_one(dev);
/* only consider samples after the first 5 */
if (i > 4) {
if (tmp & 0x8000) {
negative = true;
tmp = ~tmp;
tmp += 1;
}
offset += (tmp & 0xffff);
}
}
adc_bflb_detrigger(dev);
offset = offset / 5;
if (negative) {
data->cal_coe += (float)offset / 2048.0f;
} else {
data->cal_coe -= (float)offset / 2048.0f;
}
}
#endif
static void adc_bflb_calibrate_gnd_offset(const struct device *dev)
{
struct adc_bflb_data *data = dev->data;
const struct adc_bflb_config *const cfg = dev->config;
volatile uint32_t tmp;
volatile uint32_t offset = 0;
tmp = sys_read32(cfg->reg_AON + AON_GPADC_REG_CONFIG1_OFFSET);
tmp |= (ADC_RESOLUTION_16B_ID << AON_GPADC_RES_SEL_SHIFT);
tmp |= AON_GPADC_CONT_CONV_EN;
tmp &= ~AON_GPADC_SCAN_EN;
tmp &= ~AON_GPADC_CLK_ANA_INV;
sys_write32(tmp, cfg->reg_AON + AON_GPADC_REG_CONFIG1_OFFSET);
tmp = sys_read32(cfg->reg_AON + AON_GPADC_REG_CONFIG2_OFFSET);
tmp &= ~AON_GPADC_DIFF_MODE;
tmp &= ~AON_GPADC_VBAT_EN;
tmp &= ~AON_GPADC_VREF_SEL;
sys_write32(tmp, cfg->reg_AON + AON_GPADC_REG_CONFIG2_OFFSET);
tmp = sys_read32(cfg->reg_AON + AON_GPADC_REG_CMD_OFFSET);
tmp |= AON_GPADC_NEG_GND;
tmp &= ~AON_GPADC_POS_SEL_MASK;
tmp &= ~AON_GPADC_NEG_SEL_MASK;
tmp |= (ADC_INPUT_ID_GND << AON_GPADC_POS_SEL_SHIFT);
tmp |= (ADC_INPUT_ID_GND << AON_GPADC_NEG_SEL_SHIFT);
sys_write32(tmp, cfg->reg_AON + AON_GPADC_REG_CMD_OFFSET);
tmp = sys_read32(cfg->reg_GPIP + GPIP_GPADC_CONFIG_OFFSET);
tmp |= GPIP_GPADC_FIFO_CLR;
sys_write32(tmp, cfg->reg_GPIP + GPIP_GPADC_CONFIG_OFFSET);
clock_bflb_settle();
clock_bflb_settle();
clock_bflb_settle();
adc_bflb_trigger(dev);
/* 10 samplings */
for (uint8_t i = 0; i < 10; i++) {
tmp = adc_bflb_read_one(dev);
/* only consider samples after the first 5 */
if (i > 4) {
offset += (tmp & ADC_RESULT);
}
}
adc_bflb_detrigger(dev);
data->cal_off = offset / 5;
}
#if defined(CONFIG_SOC_SERIES_BL70X)
static int adc_bflb_calibrate_efuse(const struct device *dev)
{
struct adc_bflb_data *data = dev->data;
const struct device *efuse = DEVICE_DT_GET_ONE(bflb_efuse);
int ret;
uint32_t trim;
ret = syscon_read_reg(efuse, 0x78, &trim);
if (ret < 0) {
LOG_ERR("Error: Couldn't read efuses: err: %d.\n", ret);
return -EINVAL;
}
if ((trim & 0x4000) == 0) {
LOG_ERR("Error: ADC calibration data not present");
return -EINVAL;
}
trim = (trim & 0x1FFE) >> 1;
if (trim & 0x800) {
trim = ~trim;
trim += 1;
trim = trim & 0xfff;
data->cal_coe = ((float)1.0 + ((float)trim / (float)2048.0));
} else {
data->cal_coe = ((float)1.0 - ((float)trim / (float)2048.0));
}
return 0;
}
#elif defined(CONFIG_SOC_SERIES_BL61X)
static int adc_bflb_calibrate_efuse(const struct device *dev)
{
struct adc_bflb_data *data = dev->data;
const struct device *efuse = DEVICE_DT_GET_ONE(bflb_efuse);
int ret;
uint32_t trim;
ret = syscon_read_reg(efuse, 0xF0, &trim);
if (ret < 0) {
LOG_ERR("Error: Couldn't read efuses: err: %d.\n", ret);
return -EINVAL;
}
if ((trim & 0x4000000) == 0) {
LOG_ERR("Error: ADC calibration data not present");
return -EINVAL;
}
trim = (trim & 0x3FFC000) >> 14;
if (trim & 0x800) {
trim = ~trim;
trim += 1;
trim = trim & 0xfff;
data->cal_coe = ((float)1.0 + ((float)trim / (float)2048.0));
} else {
data->cal_coe = ((float)1.0 - ((float)trim / (float)2048.0));
}
return 0;
}
#endif
#if defined(CONFIG_SOC_SERIES_BL60X) || defined(CONFIG_SOC_SERIES_BL70X)
static void adc_bflb_init_clock(const struct device *dev)
{
uint32_t tmp;
/* clock pathing*/
tmp = sys_read32(GLB_BASE + GLB_GPADC_32M_SRC_CTRL_OFFSET);
/* clock = XTAL or RC32M (32M) */
tmp |= GLB_GPADC_32M_CLK_SEL_MSK;
/* div = 1 so ADC gets 32Mhz */
tmp &= ~GLB_GPADC_32M_CLK_DIV_MSK;
/* enable */
tmp |= GLB_GPADC_32M_DIV_EN_MSK;
sys_write32(tmp, GLB_BASE + GLB_GPADC_32M_SRC_CTRL_OFFSET);
}
#elif defined(CONFIG_SOC_SERIES_BL61X)
static void adc_bflb_init_clock(const struct device *dev)
{
uint32_t tmp;
/* clock pathing*/
tmp = sys_read32(GLB_BASE + GLB_ADC_CFG0_OFFSET);
/* clock = XTAL or RC32M (32M) */
tmp |= GLB_GPADC_32M_CLK_SEL_MSK;
/* div = 1 so ADC gets 32Mhz */
tmp &= ~GLB_GPADC_32M_CLK_DIV_MSK;
/* enable */
tmp |= GLB_GPADC_32M_DIV_EN_MSK;
sys_write32(tmp, GLB_BASE + GLB_ADC_CFG0_OFFSET);
}
#else
#error Unsupported Platform
#endif
static int adc_bflb_init(const struct device *dev)
{
const struct adc_bflb_config *const cfg = dev->config;
uint32_t tmp;
int ret;
ret = pinctrl_apply_state(cfg->pcfg, PINCTRL_STATE_DEFAULT);
if (ret < 0) {
return ret;
}
adc_bflb_init_clock(dev);
/* peripheral reset sequence */
tmp = sys_read32(cfg->reg_AON + AON_GPADC_REG_CMD_OFFSET);
tmp &= ~AON_GPADC_GLOBAL_EN;
sys_write32(tmp, cfg->reg_AON + AON_GPADC_REG_CMD_OFFSET);
tmp = sys_read32(cfg->reg_AON + AON_GPADC_REG_CMD_OFFSET);
tmp |= AON_GPADC_GLOBAL_EN;
sys_write32(tmp, cfg->reg_AON + AON_GPADC_REG_CMD_OFFSET);
tmp = sys_read32(cfg->reg_AON + AON_GPADC_REG_CMD_OFFSET);
tmp |= AON_GPADC_SOFT_RST;
sys_write32(tmp, cfg->reg_AON + AON_GPADC_REG_CMD_OFFSET);
clock_bflb_settle();
clock_bflb_settle();
clock_bflb_settle();
tmp = sys_read32(cfg->reg_AON + AON_GPADC_REG_CMD_OFFSET);
tmp &= ~AON_GPADC_CONV_START;
sys_write32(tmp, cfg->reg_AON + AON_GPADC_REG_CMD_OFFSET);
tmp = sys_read32(cfg->reg_AON + AON_GPADC_REG_CMD_OFFSET);
tmp &= ~AON_GPADC_SOFT_RST;
sys_write32(tmp, cfg->reg_AON + AON_GPADC_REG_CMD_OFFSET);
tmp = 0;
/* enable power to adc? */
tmp |= (2 << AON_GPADC_V18_SEL_SHIFT);
tmp |= (1 << AON_GPADC_V11_SEL_SHIFT);
/* set internal clock divider to 32 */
tmp |= (ADC_CLK_DIV_32 << AON_GPADC_CLK_DIV_RATIO_SHIFT);
/* default resolution (12-bits) */
tmp |= (ADC_RESOLUTION_12B_ID << AON_GPADC_RES_SEL_SHIFT);
tmp &= ~AON_GPADC_CONT_CONV_EN;
tmp &= ~AON_GPADC_SCAN_EN;
sys_write32(tmp, cfg->reg_AON + AON_GPADC_REG_CONFIG1_OFFSET);
clock_bflb_settle();
clock_bflb_settle();
clock_bflb_settle();
tmp = 0;
/* ""conversion speed"" */
tmp |= (2 << AON_GPADC_DLY_SEL_SHIFT);
/* ""Vref AZ and chop on"" */
tmp |= (2 << AON_GPADC_CHOP_MODE_SHIFT);
/* "gain 1" is 1 */
tmp |= (1 << AON_GPADC_PGA1_GAIN_SHIFT);
/* "gain 2" is 1 */
tmp |= (1 << AON_GPADC_PGA2_GAIN_SHIFT);
/* enable gain */
tmp |= AON_GPADC_PGA_EN;
/* "offset calibration" value */
tmp |= (8 << AON_GPADC_PGA_OS_CAL_SHIFT);
/* "VCM" is 1.2v */
tmp |= (1 << AON_GPADC_PGA_VCM_SHIFT);
/* ADC reference (VREF channel) is 3v3 */
tmp &= ~AON_GPADC_VREF_SEL;
sys_write32(tmp, cfg->reg_AON + AON_GPADC_REG_CONFIG2_OFFSET);
tmp = sys_read32(cfg->reg_AON + AON_GPADC_REG_CMD_OFFSET);
/* "MIC2" differential mode enable */
tmp |= AON_GPADC_MIC2_DIFF;
/* single ended mode is achieved by setting differential other end to ground */
tmp |= AON_GPADC_NEG_GND;
sys_write32(tmp, cfg->reg_AON + AON_GPADC_REG_CMD_OFFSET);
/* clear calibration */
tmp = sys_read32(cfg->reg_AON + AON_GPADC_REG_DEFINE_OFFSET);
tmp &= ~AON_GPADC_OS_CAL_DATA_MASK;
sys_write32(tmp, cfg->reg_AON + AON_GPADC_REG_DEFINE_OFFSET);
/* interrupts and status setup */
tmp = sys_read32(cfg->reg_GPIP + GPIP_GPADC_CONFIG_OFFSET);
tmp |= (GPIP_GPADC_FIFO_UNDERRUN_MASK
| GPIP_GPADC_FIFO_OVERRUN_MASK
| GPIP_GPADC_RDY_MASK
| GPIP_GPADC_FIFO_UNDERRUN_CLR
| GPIP_GPADC_FIFO_OVERRUN_CLR
| GPIP_GPADC_RDY_CLR);
#ifdef CONFIG_SOC_SERIES_BL70X
tmp |= (GPIP_GPADC_FIFO_RDY_MASK | GPIP_GPADC_FIFO_RDY);
#endif
tmp |= GPIP_GPADC_FIFO_CLR;
tmp &= ~GPIP_GPADC_FIFO_THL_MASK;
tmp &= ~GPIP_GPADC_DMA_EN;
sys_write32(tmp, cfg->reg_GPIP + GPIP_GPADC_CONFIG_OFFSET);
clock_bflb_settle();
tmp = sys_read32(cfg->reg_GPIP + GPIP_GPADC_CONFIG_OFFSET);
tmp &= ~(GPIP_GPADC_FIFO_UNDERRUN_CLR
| GPIP_GPADC_FIFO_OVERRUN_CLR
| GPIP_GPADC_RDY_CLR
| GPIP_GPADC_FIFO_CLR);
sys_write32(tmp, cfg->reg_GPIP + GPIP_GPADC_CONFIG_OFFSET);
tmp = sys_read32(cfg->reg_AON + AON_GPADC_REG_ISR_OFFSET);
tmp |= AON_GPADC_NEG_SATUR_MASK;
tmp |= AON_GPADC_POS_SATUR_MASK;
sys_write32(tmp, cfg->reg_AON + AON_GPADC_REG_ISR_OFFSET);
#if defined(CONFIG_SOC_SERIES_BL60X)
adc_bflb_calibrate_dynamic(dev);
adc_bflb_calibrate_gnd_offset(dev);
#else
ret = adc_bflb_calibrate_efuse(dev);
if (ret < 0) {
LOG_ERR("Couldn't calibrate via efuses");
return ret;
}
adc_bflb_calibrate_gnd_offset(dev);
#endif
cfg->irq_config_func(dev);
return 0;
}
static DEVICE_API(adc, adc_bflb_api) = {
.channel_setup = adc_bflb_channel_setup,
.read = adc_bflb_read,
.ref_internal = 3200,
};
#define ADC_BFLB_DEVICE(n) \
PINCTRL_DT_INST_DEFINE(n); \
static void adc_bflb_irq_config_##n(const struct device *dev) \
{ \
IRQ_CONNECT(DT_INST_IRQN(n), \
DT_INST_IRQ(n, priority), \
adc_bflb_isr, \
DEVICE_DT_INST_GET(n), 0); \
irq_enable(DT_INST_IRQN(n)); \
} \
static const struct adc_bflb_config adc_bflb_config_##n = { \
.reg_GPIP = DT_INST_REG_ADDR_BY_IDX(n, 0), \
.reg_AON = DT_INST_REG_ADDR_BY_IDX(n, 1), \
.irq_config_func = &adc_bflb_irq_config_##n, \
.pcfg = PINCTRL_DT_INST_DEV_CONFIG_GET(n), \
}; \
static struct adc_bflb_data adc_bflb_data_##n = { \
.channel_count = 0, \
.gain = ADC_GAIN_UNSET, \
.cal_off = 0, \
.cal_coe = 1.0f, \
}; \
DEVICE_DT_INST_DEFINE(n, adc_bflb_init, NULL, \
&adc_bflb_data_##n, \
&adc_bflb_config_##n, POST_KERNEL, \
CONFIG_ADC_INIT_PRIORITY, \
&adc_bflb_api);
DT_INST_FOREACH_STATUS_OKAY(ADC_BFLB_DEVICE)