758 lines
21 KiB
C
758 lines
21 KiB
C
/**
|
||
* @file sdrv_acmp.c
|
||
* @brief semidrive acmp driver
|
||
*
|
||
* @copyright Copyright (c) 2022 Semidrive Semiconductor.
|
||
* All rights reserved.
|
||
*/
|
||
#include <types.h>
|
||
#include <bits.h>
|
||
#include <debug.h>
|
||
#include <string.h>
|
||
#include <reg.h>
|
||
#include <irq.h>
|
||
#include <udelay/udelay.h>
|
||
#include "sdrv_acmp.h"
|
||
|
||
/* Define the register offsets of acmp component */
|
||
#define ACMP_MAX_CSEL_NUM 4
|
||
#define ACMP_MAX_INDEX_NUM 32
|
||
#define ACMP_INIT_POLLs 200000
|
||
|
||
#define ACMP_SOFT (0x00)
|
||
#define ACMP_SOFT_RST (0)
|
||
#define ACMP_SOFT_RC_TMR_RST (1)
|
||
|
||
#define ACMP_MODE (0x04)
|
||
|
||
#define ACMP_IT_MODE_CFG (0x08)
|
||
#define ACMP_IT_MODE_CFG_TG_START (0)
|
||
#define ACMP_IT_MODE_CFG_QSTART (8)
|
||
#define ACMP_IT_MODE_CFG_QEND (16)
|
||
#define ACMP_IT_MODE_CFG_CUR_ENTRY_PT (24)
|
||
|
||
#define ACMP_TIMER (0x0C)
|
||
#define ACMP_TIMER_COMPARE_VAL (0)
|
||
#define ACMP_TIMER_TERMINAL_VAL (16)
|
||
|
||
#define ACMP_CFG_INDEX(n) (0x10 + n * 4)
|
||
#define ACMP_CFG_INDEX_AMSEL (0)
|
||
#define ACMP_CFG_INDEX_CSEL (12)
|
||
#define ACMP_CFG_INDEX_IE (16)
|
||
#define ACMP_CFG_INDEX_UIE (17)
|
||
|
||
#define ACMP_ANA_CFG(n) (0x90 + n * 4)
|
||
#define ACMP_ANA_CFG_REFSEL (0)
|
||
#define ACMP_ANA_CFG_SDSEL (1)
|
||
#define ACMP_ANA_CFG_HCFG (4)
|
||
#define ACMP_ANA_CFG_DAIN (8)
|
||
|
||
#define ACMP_CTRL_CFG(n) (0xA0 + n * 4)
|
||
#define ACMP_CTRL_CFG_AMUX_SETUP (0)
|
||
#define ACMP_CTRL_CFG_PROP_DLY (8)
|
||
#define ACMP_CTRL_CFG_RBW (16)
|
||
#define ACMP_CTRL_CFG_FBW (20)
|
||
#define ACMP_CTRL_CFG_SAMPLE_EN (24)
|
||
#define ACMP_CTRL_CFG_MASK_EN (25)
|
||
#define ACMP_CTRL_CFG_POL_ADJ (26)
|
||
#define ACMP_CTRL_CFG_SYNC_EN (27)
|
||
|
||
#define ACMP_IRQ (0xB0)
|
||
|
||
#define ACMP_IRQ_UNCOR (0xB4)
|
||
|
||
#define ANA_EXT_MUX_ERR (0xB8)
|
||
#define ANA_EXT_MUX_ERR_STATUS (0)
|
||
#define ANA_EXT_MUX_ERR_SIG_EN (16)
|
||
|
||
#define ACMP_PD_CTRL (0xBC)
|
||
#define ACMP_PD_CTRL_CMP (0)
|
||
#define ACMP_PD_CTRL_DAC (1)
|
||
#define ACMP_PD_CTRL_Q_CH_CMP (8)
|
||
#define ACMP_PD_CTRL_Q_CH_DAC (9)
|
||
|
||
#define ACMP_INIT (0xC0)
|
||
#define ACMP_INIT_CNT_THRD (0)
|
||
#define ACMP_INIT_DONE (16)
|
||
|
||
#define ACMP_LOW_POWER (0xC4)
|
||
|
||
#define ACMP_SCH (0xC8)
|
||
#define ACMP_SCH_ACMP_EN (0)
|
||
#define ACMP_SCH_ANA_INSEL_ON (1)
|
||
|
||
#define REG_PARITY_ERR_INT_STAT (0xD0)
|
||
|
||
#define REG_PARITY_ERR_INT_STAT_EN (0xD4)
|
||
|
||
#define REG_PARITY_ERR_INT_SIG_EN (0xD8)
|
||
|
||
#define FUSA_COR_ERR_INT_STAT (0xE0)
|
||
|
||
#define FUSA_COR_ERR_INT_STAT_EN (0xE4)
|
||
|
||
#define FUSA_COR_ERR_INT_SIG_EN (0xE8)
|
||
|
||
#define FUSA_UNCOR_ERR_INT_STAT (0xF0)
|
||
#define FUSA_UNCOR_ERR_INT_STAT_PWDATA_UNCOR_ERR (0)
|
||
#define FUSA_UNCOR_ERR_INT_STAT_PWDATA_DATAL_ERR (1)
|
||
#define FUSA_UNCOR_ERR_INT_STAT_PADDR_UNCOR_ERR (2)
|
||
#define FUSA_UNCOR_ERR_INT_STAT_PCTL_UNCOR_ERR (3)
|
||
#define FUSA_UNCOR_ERR_INT_STAT_INPUT_ERR (4)
|
||
|
||
#define FUSA_UNCOR_ERR_INT_STAT_EN (0xF4)
|
||
#define FUSA_UNCOR_ERR_INT_STAT_EN_PWDATA_UNCOR_ERR (0)
|
||
#define FUSA_UNCOR_ERR_INT_STAT_EN_PWDATA_DATAL_ERR (1)
|
||
#define FUSA_UNCOR_ERR_INT_STAT_EN_PADDR_UNCOR_ERR (2)
|
||
#define FUSA_UNCOR_ERR_INT_STAT_EN_PCTL_UNCOR_ERR (3)
|
||
#define FUSA_UNCOR_ERR_INT_STAT_EN_INPUT_ERR (4)
|
||
|
||
#define FUSA_UNCOR_ERR_INT_SIG_EN (0xF8)
|
||
#define FUSA_UNCOR_ERR_INT_SIG_EN_PWDATA_UNCOR_ERR (0)
|
||
#define FUSA_UNCOR_ERR_INT_SIG_EN_PWDATA_DATAL_ERR (1)
|
||
#define FUSA_UNCOR_ERR_INT_SIG_EN_PADDR_UNCOR_ERR (2)
|
||
#define FUSA_UNCOR_ERR_INT_SIG_EN_PCTL_UNCOR_ERR (3)
|
||
#define FUSA_UNCOR_ERR_INT_SIG_EN_INPUT_ERR (4)
|
||
|
||
#define PWDATA_INJ (0x100)
|
||
|
||
#define PWECC_INJ (0x104)
|
||
|
||
#define SELFTEST_MODE (0x108)
|
||
|
||
#define PRDATAINJ (0x10C)
|
||
|
||
#define INT_ERR_INJ (0x110)
|
||
#define INT_ERR_INJ_IE_ERR (0)
|
||
#define INT_ERR_INJ_COR_ERR (1)
|
||
#define INT_ERR_INJ_UNC_ERR (2)
|
||
|
||
/**
|
||
* @brief sdrv acmp soft reset
|
||
*
|
||
* @param[in] base sdrv acmp address
|
||
*/
|
||
static void sdrv_acmp_soft_reset(paddr_t base)
|
||
{
|
||
RMWREG32(base + ACMP_SOFT, ACMP_SOFT_RST, 1, 1);
|
||
|
||
RMWREG32(base + ACMP_SOFT, ACMP_SOFT_RST, 1, 0);
|
||
}
|
||
|
||
/**
|
||
* @brief sdrv acmp config mode.
|
||
*
|
||
* @param[in] base sdrv acmp address
|
||
* @param[in] mode sdrv acmp mode
|
||
*/
|
||
static void sdrv_acmp_config_mode(paddr_t base, sdrv_acmp_mode_t mode)
|
||
{
|
||
writel(mode, base + ACMP_MODE);
|
||
}
|
||
|
||
/**
|
||
* @brief sdrv acmp get mode.
|
||
*
|
||
* @param[in] base sdrv acmp address
|
||
* @param[in] mode sdrv acmp mode
|
||
*/
|
||
static sdrv_acmp_mode_t sdrv_acmp_get_mode(paddr_t base)
|
||
{
|
||
return (sdrv_acmp_mode_t)(readl(base + ACMP_MODE) & 0x1U);
|
||
}
|
||
|
||
/**
|
||
* @brief sdrv acmp config it mode.
|
||
*
|
||
* @param[in] base sdrv acmp address
|
||
* @param[in] qstart sdrv acmp trigger start id number for loop
|
||
* @param[in] qend sdrv acmp trigger end id number for loop
|
||
* @param[in] cur_entry_pt sdrv acmp trigger first access id number
|
||
* @return SDRV_STATUS_OK represents success or other error code
|
||
*/
|
||
static status_t sdrv_acmp_config_it_mode(paddr_t base, uint8_t qstart,
|
||
uint8_t qend, uint8_t cur_entry_pt)
|
||
{
|
||
if ((qstart <= cur_entry_pt) && (cur_entry_pt <= qend)) {
|
||
RMWREG32(base + ACMP_IT_MODE_CFG, ACMP_IT_MODE_CFG_QSTART, 5, qstart & 0x1F);
|
||
|
||
RMWREG32(base + ACMP_IT_MODE_CFG, ACMP_IT_MODE_CFG_QEND, 5, qend & 0x1F);
|
||
|
||
RMWREG32(base + ACMP_IT_MODE_CFG, ACMP_IT_MODE_CFG_CUR_ENTRY_PT, 5,
|
||
cur_entry_pt & 0x1F);
|
||
|
||
return SDRV_STATUS_OK;
|
||
}
|
||
else {
|
||
return SDRV_ACMP_STATUS_INVALID_PARAM;
|
||
}
|
||
}
|
||
|
||
/**
|
||
* @brief sdrv acmp get it mode config.
|
||
*
|
||
* @param[in] base sdrv acmp address
|
||
* @param[in] qstart sdrv acmp trigger start id number for loop
|
||
* @param[in] qend sdrv acmp trigger end id number for loop
|
||
* @param[in] cur_entry_pt sdrv acmp trigger first access id number
|
||
*/
|
||
static void sdrv_acmp_get_it_mode_config(paddr_t base, uint8_t *qstart,
|
||
uint8_t *qend, uint8_t *cur_entry_pt)
|
||
{
|
||
uint32_t it_cfg = readl(base + ACMP_IT_MODE_CFG);
|
||
|
||
*qstart = (it_cfg >> ACMP_IT_MODE_CFG_QSTART) & 0x1F;
|
||
|
||
*qend = (it_cfg >> ACMP_IT_MODE_CFG_QEND) & 0x1F;
|
||
|
||
*cur_entry_pt = (it_cfg >> ACMP_IT_MODE_CFG_CUR_ENTRY_PT) & 0x1F;
|
||
}
|
||
|
||
/**
|
||
* @brief sdrv acmp config timer.
|
||
*
|
||
* @param[in] base sdrv acmp address
|
||
* @param[in] compare_val acmp timer compare value
|
||
* @param[in] terminal_val acmp timer terminal value
|
||
* @return SDRV_STATUS_OK represents success or other error code
|
||
*/
|
||
static status_t sdrv_acmp_config_timer(paddr_t base, uint16_t compare_val,
|
||
uint16_t terminal_val)
|
||
{
|
||
RMWREG32(base + ACMP_TIMER, ACMP_TIMER_COMPARE_VAL, 16, compare_val & 0xFFFF);
|
||
|
||
if (terminal_val > compare_val) {
|
||
RMWREG32(base + ACMP_TIMER, ACMP_TIMER_TERMINAL_VAL, 16, terminal_val & 0xFFFF);
|
||
}
|
||
else {
|
||
return SDRV_ACMP_STATUS_INVALID_PARAM;
|
||
}
|
||
|
||
return SDRV_STATUS_OK;
|
||
}
|
||
|
||
/**
|
||
* @brief sdrv acmp start it generate.
|
||
*
|
||
* @param[in] base sdrv acmp address
|
||
* @param[in] trigger_start acmp it generate start
|
||
*/
|
||
static void sdrv_acmp_it_gen_start(paddr_t base, bool trigger_start)
|
||
{
|
||
RMWREG32(base + ACMP_IT_MODE_CFG, ACMP_IT_MODE_CFG_TG_START, 1, trigger_start);
|
||
}
|
||
|
||
/**
|
||
* @brief sdrv acmp config csel
|
||
*
|
||
* @param[in] base sdrv acmp address
|
||
* @param[in] csel_cfg sdrv acmp csel cfg
|
||
*/
|
||
static void sdrv_acmp_config_csel(paddr_t base, sdrv_acmp_csel_cfg_t *csel_cfg)
|
||
{
|
||
uint32_t val = 0;
|
||
|
||
/* acmp ctrl cfg*/
|
||
val = (csel_cfg->sync_en << ACMP_CTRL_CFG_SYNC_EN) |
|
||
(csel_cfg->pol_adj << ACMP_CTRL_CFG_POL_ADJ) |
|
||
(csel_cfg->mask_en << ACMP_CTRL_CFG_MASK_EN) |
|
||
(csel_cfg->sample_en << ACMP_CTRL_CFG_SAMPLE_EN) |
|
||
(csel_cfg->fbw << ACMP_CTRL_CFG_FBW) |
|
||
(csel_cfg->rbw << ACMP_CTRL_CFG_RBW) |
|
||
(csel_cfg->prop_delay << ACMP_CTRL_CFG_PROP_DLY) |
|
||
(csel_cfg->amux_setup << ACMP_CTRL_CFG_AMUX_SETUP);
|
||
|
||
writel(val, base + ACMP_CTRL_CFG(csel_cfg->csel_idx));
|
||
|
||
/* acmp ana cfg*/
|
||
val = (csel_cfg->dain << ACMP_ANA_CFG_DAIN) |
|
||
(csel_cfg->hcfg << ACMP_ANA_CFG_HCFG) |
|
||
(csel_cfg->sdsel << ACMP_ANA_CFG_SDSEL) |
|
||
(csel_cfg->refsel << ACMP_ANA_CFG_REFSEL);
|
||
|
||
writel(val, base + ACMP_ANA_CFG(csel_cfg->csel_idx));
|
||
}
|
||
|
||
/**
|
||
* @brief sdrv acmp config index
|
||
*
|
||
* @param[in] base sdrv acmp address
|
||
* @param[in] index_cfg sdrv acmp index cfg
|
||
*/
|
||
static void sdrv_acmp_config_index(paddr_t base,
|
||
sdrv_acmp_index_cfg_t *index_cfg)
|
||
{
|
||
uint32_t val = 0;
|
||
|
||
val = (index_cfg->uie << ACMP_CFG_INDEX_UIE) |
|
||
(index_cfg->ie << ACMP_CFG_INDEX_IE) |
|
||
(index_cfg->csel << ACMP_CFG_INDEX_CSEL) |
|
||
(index_cfg->amsel << ACMP_CFG_INDEX_AMSEL);
|
||
|
||
writel(val, base + ACMP_CFG_INDEX(index_cfg->idx));
|
||
}
|
||
|
||
/**
|
||
* @brief sdrv acmp get irq state.
|
||
*
|
||
* @param[in] base sdrv acmp address
|
||
* @return irq status
|
||
*/
|
||
static uint32_t sdrv_acmp_get_irq_state(paddr_t base)
|
||
{
|
||
return readl(base + ACMP_IRQ);
|
||
}
|
||
|
||
/**
|
||
* @brief sdrv acmp clear irq.
|
||
*
|
||
* @param[in] base sdrv acmp address
|
||
* @param[in] state irq state
|
||
*/
|
||
static void sdrv_acmp_clear_irq(paddr_t base, uint32_t state)
|
||
{
|
||
writel(state, base + ACMP_IRQ);
|
||
}
|
||
|
||
/**
|
||
* @brief sdrv acmp get irq status.
|
||
*
|
||
* @param[in] base sdrv acmp address
|
||
* @return init done
|
||
*/
|
||
static bool sdrv_acmp_lld_get_init_done(paddr_t base)
|
||
{
|
||
return ((readl(base + ACMP_INIT) >> ACMP_INIT_DONE) & 0x1);
|
||
}
|
||
|
||
/**
|
||
* @brief sdrv acmp set low power req_n delay num.
|
||
*
|
||
* @param[in] base sdrv acmp address
|
||
* @param[in] req_n_dly req_n delay number
|
||
*/
|
||
static void sdrv_acmp_set_low_power(paddr_t base, uint8_t req_n_dly)
|
||
{
|
||
writel(req_n_dly & 0xFFFF, base + ACMP_LOW_POWER);
|
||
}
|
||
|
||
/**
|
||
* @brief sdrv acmp dac recieve q channel low power requset.
|
||
*
|
||
* @param[in] base sdrv acmp address
|
||
* @param[in] enable cmp recieve q channel low power requset
|
||
*/
|
||
static void sdrv_acmp_rcv_qch_cmp(paddr_t base, bool enable)
|
||
{
|
||
RMWREG32(base + ACMP_PD_CTRL, ACMP_PD_CTRL_CMP, 1, enable);
|
||
}
|
||
|
||
/**
|
||
* @brief sdrv acmp comperitor recieve q channel low power requset.
|
||
*
|
||
* @param[in] base sdrv acmp address
|
||
* @param[in] enable dac recieve q channel low power requset
|
||
*/
|
||
static void sdrv_acmp_rcv_qch_dac(paddr_t base, bool enable)
|
||
{
|
||
RMWREG32(base + ACMP_PD_CTRL, ACMP_PD_CTRL_DAC, 1, enable);
|
||
}
|
||
|
||
/**
|
||
* @brief sdrv acmp irq handler
|
||
*
|
||
* @param[in] irq irq number
|
||
* @param[in] arg irq arg
|
||
* return irq handler result
|
||
*/
|
||
static int sdrv_acmp_irq_handler(uint32_t irq, void *arg)
|
||
{
|
||
sdrv_acmp_t *dev = arg;
|
||
|
||
uint32_t irq_state;
|
||
int ret = 0;
|
||
|
||
ASSERT(dev);
|
||
|
||
irq_state = sdrv_acmp_get_irq_state(dev->base);
|
||
|
||
if (irq_state) {
|
||
if (dev->callback) {
|
||
ret = dev->callback(dev, irq_state);
|
||
}
|
||
|
||
sdrv_acmp_clear_irq(dev->base, irq_state);
|
||
}
|
||
|
||
return ret;
|
||
}
|
||
|
||
/**
|
||
* @brief sdrv acmp init
|
||
*
|
||
* This function initializes acmp device and attach callback function.
|
||
*
|
||
* @param[in] dev sdrv acmp device
|
||
* @param[in] cfg sdrv acmp configuration parameters
|
||
* @param[in] callback sdrv acmp callback function
|
||
* @return SDRV_STATUS_OK represents success or other error code
|
||
*/
|
||
status_t sdrv_acmp_init(sdrv_acmp_t *dev, sdrv_acmp_config_t *cfg,
|
||
sdrv_acmp_callback callback)
|
||
{
|
||
status_t ret;
|
||
uint32_t i = 0;
|
||
uint32_t tms = 0;
|
||
|
||
if (cfg->base == 0) {
|
||
return SDRV_ACMP_STATUS_INVALID_PARAM;
|
||
}
|
||
|
||
dev->base = cfg->base;
|
||
dev->irq = cfg->irq;
|
||
dev->callback = callback;
|
||
|
||
#if (CONFIG_E3 || CONFIG_D3)
|
||
|
||
if (IS_P0) {
|
||
/* close ADC self-test to avoid disturbing the ACMP reference voltage */
|
||
#if APB_ADC1_BASE
|
||
writel(0xFFFF, APB_ADC1_BASE + 0x244);
|
||
#endif
|
||
#if APB_ADC2_BASE
|
||
writel(0xFFFF, APB_ADC2_BASE + 0x244);
|
||
#endif
|
||
#if APB_ADC3_BASE
|
||
writel(0xFFFF, APB_ADC3_BASE + 0x244);
|
||
#endif
|
||
}
|
||
|
||
#endif
|
||
|
||
/* soft reset */
|
||
sdrv_acmp_soft_reset(dev->base);
|
||
|
||
/* wait init done */
|
||
while (!sdrv_acmp_lld_get_init_done(dev->base)) {
|
||
if (tms++ > ACMP_INIT_POLLs) {
|
||
return SDRV_STATUS_TIMEOUT;
|
||
}
|
||
}
|
||
|
||
/* config mode */
|
||
sdrv_acmp_config_mode(dev->base, cfg->mode);
|
||
|
||
if (cfg->mode == ACMP_IT_MODE) {
|
||
ret = sdrv_acmp_config_it_mode(dev->base, cfg->qstart, cfg->qend,
|
||
cfg->cur_entry_pt);
|
||
|
||
if (ret != SDRV_STATUS_OK) {
|
||
return ret;
|
||
}
|
||
|
||
ret = sdrv_acmp_config_timer(dev->base, cfg->timer_compare_val,
|
||
cfg->timer_terminal_val);
|
||
|
||
if (ret != SDRV_STATUS_OK) {
|
||
return ret;
|
||
}
|
||
}
|
||
|
||
/* config csel */
|
||
if ((cfg->csel_num > 0) && (cfg->csel_num <= ACMP_MAX_CSEL_NUM)) {
|
||
for (i = 0; i < cfg->csel_num; i++) {
|
||
sdrv_acmp_config_csel(dev->base, &cfg->csel_cfg[i]);
|
||
}
|
||
}
|
||
else {
|
||
return SDRV_ACMP_STATUS_INVALID_PARAM;
|
||
}
|
||
|
||
/* config index */
|
||
if ((cfg->index_num > 0) && (cfg->index_num <= ACMP_MAX_INDEX_NUM)) {
|
||
for (i = 0; i < cfg->index_num; i++) {
|
||
sdrv_acmp_config_index(dev->base, &cfg->index_cfg[i]);
|
||
}
|
||
}
|
||
else {
|
||
return SDRV_ACMP_STATUS_INVALID_PARAM;
|
||
}
|
||
|
||
/* set low power req_n delay */
|
||
if (cfg->req_n_dly > 0) {
|
||
sdrv_acmp_set_low_power(dev->base, cfg->req_n_dly);
|
||
}
|
||
|
||
/* competitor recv q channel low power request */
|
||
sdrv_acmp_rcv_qch_cmp(dev->base, cfg->qch_cmp_en);
|
||
|
||
/* dac recv q channel low power request */
|
||
sdrv_acmp_rcv_qch_dac(dev->base, cfg->qch_dac_en);
|
||
|
||
/* start it generate */
|
||
if (cfg->mode == ACMP_IT_MODE) {
|
||
sdrv_acmp_it_gen_start(dev->base, true);
|
||
}
|
||
else {
|
||
sdrv_acmp_it_gen_start(dev->base, false);
|
||
}
|
||
|
||
if (dev->irq > 0) {
|
||
irq_attach(dev->irq, sdrv_acmp_irq_handler, dev);
|
||
irq_enable(dev->irq);
|
||
}
|
||
else {
|
||
return SDRV_ACMP_STATUS_INVALID_PARAM;
|
||
}
|
||
|
||
return SDRV_STATUS_OK;
|
||
}
|
||
|
||
/**
|
||
* @brief sdrv acmp start
|
||
*
|
||
* This function is used to start or stop acmp comparation.
|
||
* All the parameters are remained when acmp stop, and the previous comparation will restart when acmp start.
|
||
*
|
||
* @param[in] dev sdrv acmp dev
|
||
* @param[in] start sdrv acmp start
|
||
* @return SDRV_STATUS_OK represents success
|
||
*/
|
||
status_t sdrv_acmp_start(sdrv_acmp_t *dev, bool start)
|
||
{
|
||
if (dev->base == 0) {
|
||
return SDRV_ACMP_STATUS_INVALID_PARAM;
|
||
}
|
||
|
||
RMWREG32(dev->base + ACMP_SCH, ACMP_SCH_ANA_INSEL_ON, 1, 0);
|
||
|
||
RMWREG32(dev->base + ACMP_SCH, ACMP_SCH_ACMP_EN, 1, start);
|
||
|
||
return SDRV_STATUS_OK;
|
||
}
|
||
|
||
/**
|
||
* @brief sdrv acmp IT mode trigger start
|
||
*
|
||
* This function is used to stop or restart trigger with differnet id numbers in IT mode.
|
||
* The new comparation will use the same comparation parameters with the former comparation.
|
||
* If the parameters need to be modified, acmp should be stopped and reinitialized.
|
||
*
|
||
* @param[in] dev sdrv acmp device
|
||
* @param[in] qstart sdrv acmp trigger start id number for loop
|
||
* @param[in] qend sdrv acmp trigger end id number for loop
|
||
* @param[in] cur_entry_pt sdrv acmp trigger first access id number
|
||
* @param[in] trigger_start acmp IT mode trigger start
|
||
* @return SDRV_STATUS_OK represents success
|
||
*/
|
||
status_t sdrv_acmp_it_trigger_start(sdrv_acmp_t *dev, uint8_t qstart, uint8_t qend, uint8_t cur_entry_pt,
|
||
bool trigger_start)
|
||
{
|
||
if (dev->base == 0) {
|
||
return SDRV_ACMP_STATUS_INVALID_PARAM;
|
||
}
|
||
|
||
/* check ACMP_IT_MODE */
|
||
if (sdrv_acmp_get_mode(dev->base) == ACMP_HT_MODE) {
|
||
return SDRV_ACMP_STATUS_WRONG_MODE;
|
||
}
|
||
|
||
/* trigger stop */
|
||
sdrv_acmp_it_gen_start(dev->base, false);
|
||
|
||
/* trigger restart */
|
||
if (trigger_start) {
|
||
sdrv_acmp_config_it_mode(dev->base, qstart, qend, cur_entry_pt);
|
||
|
||
sdrv_acmp_it_gen_start(dev->base, trigger_start);
|
||
}
|
||
|
||
return SDRV_STATUS_OK;
|
||
}
|
||
|
||
/**
|
||
* @brief sdrv acmp IT mode update valid irq state
|
||
*
|
||
* This function is used to workaround for multiple trigger if interrupt is required in IT mode.
|
||
* Occurrence scenario:
|
||
* Only appers in multiple triggers of IT mode using interrupt, but this is not a problem
|
||
* if not changing the configuration(index and analog config) at next trigger.
|
||
* At the end of each trigger, if acmp voltage comparision result is pulled up one clock cycle
|
||
* before next trigger, the interrupt generated by this trigger will update to the register ID
|
||
* of the next trigger. Other functions of ACMP are not affected.
|
||
*
|
||
* How to use this function:
|
||
* Call the function at beginning of acmp callback function.
|
||
*
|
||
* @param[in] dev sdrv acmp device
|
||
* @param[in] state sdrv acmp irq state
|
||
* @param[in] wait_time check irq valid wait time (unit: μs),
|
||
* the period should be a little longer than terminal time.
|
||
* @return SDRV_STATUS_OK represents success
|
||
*/
|
||
status_t sdrv_acmp_it_update_valid_irq(sdrv_acmp_t *dev, uint32_t *state, uint32_t wait_time)
|
||
{
|
||
uint8_t save_qstart = 0;
|
||
uint8_t save_qend = 0;
|
||
uint8_t save_cur_entry_pt = 0;
|
||
uint8_t irq_bit = 0;
|
||
|
||
if (dev->base == 0) {
|
||
return SDRV_ACMP_STATUS_INVALID_PARAM;
|
||
}
|
||
|
||
/* check ACMP_IT_MODE */
|
||
if (sdrv_acmp_get_mode(dev->base) == ACMP_HT_MODE) {
|
||
return SDRV_ACMP_STATUS_WRONG_MODE;
|
||
}
|
||
|
||
/* save IT mode qstart/qend/cur_entry_pt */
|
||
sdrv_acmp_get_it_mode_config(dev->base, &save_qstart, &save_qend, &save_cur_entry_pt);
|
||
|
||
/* check state, and clear invalid bits */
|
||
while (*state >> irq_bit) {
|
||
/* check irq_bit */
|
||
if (BIT(*state, irq_bit)) {
|
||
/* clear irq_bit */
|
||
sdrv_acmp_clear_irq(dev->base, 1 << irq_bit);
|
||
|
||
/* stop tg_start */
|
||
sdrv_acmp_it_gen_start(dev->base, false);
|
||
|
||
/* only trigger irq_bit */
|
||
sdrv_acmp_config_it_mode(dev->base, irq_bit, irq_bit, irq_bit);
|
||
|
||
/* restart tg_start */
|
||
sdrv_acmp_it_gen_start(dev->base, true);//
|
||
|
||
/* wait more than terminal time */
|
||
udelay(wait_time);
|
||
}
|
||
|
||
/* check next bit */
|
||
irq_bit ++;
|
||
if (irq_bit > 31) {
|
||
break;
|
||
}
|
||
}
|
||
|
||
/* update irq state */
|
||
*state = sdrv_acmp_get_irq_state(dev->base);
|
||
|
||
/* restore IT mode qstart/qend/cur_entry_pt */
|
||
if (save_cur_entry_pt >= save_qend) {
|
||
save_cur_entry_pt = save_qstart;
|
||
}
|
||
else {
|
||
save_cur_entry_pt = save_cur_entry_pt + 1;
|
||
}
|
||
return sdrv_acmp_config_it_mode(dev->base, save_qstart, save_qend, save_cur_entry_pt);
|
||
}
|
||
|
||
/**
|
||
* @brief sdrv acmp power ctrl
|
||
*
|
||
* This function sets the sdrv acmp power mode.
|
||
* The status is default power on after acmp initilization.
|
||
* If the acmp comparation is finished, the power mode can be set to power down to reduce power consumption.
|
||
*
|
||
* @param[in] dev sdrv acmp device
|
||
* @param[in] down 0 power on (default), 1 power down
|
||
* @return SDRV_STATUS_OK represents success
|
||
*/
|
||
status_t sdrv_acmp_power_ctrl(sdrv_acmp_t *dev, bool down)
|
||
{
|
||
if (dev->base == 0) {
|
||
return SDRV_ACMP_STATUS_INVALID_PARAM;
|
||
}
|
||
|
||
/* power down analog competitor */
|
||
RMWREG32(dev->base + ACMP_PD_CTRL, ACMP_PD_CTRL_CMP, 1, down);
|
||
|
||
/* power down analog dac */
|
||
RMWREG32(dev->base + ACMP_PD_CTRL, ACMP_PD_CTRL_DAC, 1, down);
|
||
|
||
return SDRV_STATUS_OK;
|
||
}
|
||
|
||
/**
|
||
* @brief sdrv acmp allows analog competitor and dac enter lowpower mode
|
||
*
|
||
* This function allows competitor and dac recv q channel low power request.
|
||
* And than if the lowpower request is received, the competitor and dac will enter
|
||
* low power mode after req_n_dly clock cycle.
|
||
*
|
||
* @param[in] dev sdrv acmp device
|
||
* @param[in] cmp_en competitor enable or disable
|
||
* @param[in] dac_en dac enable or disable
|
||
* @param[in] req_n_dly enter lowpower mode delay number
|
||
* @return SDRV_STATUS_OK represents success
|
||
*/
|
||
status_t sdrv_acmp_enable_low_power(sdrv_acmp_t *dev, bool cmp_en, bool dac_en,
|
||
uint8_t req_n_dly)
|
||
{
|
||
if (dev->base == 0) {
|
||
return SDRV_ACMP_STATUS_INVALID_PARAM;
|
||
}
|
||
|
||
/* set low power req_n delay */
|
||
sdrv_acmp_set_low_power(dev->base, req_n_dly);
|
||
|
||
/* competitor recv q channel low power request */
|
||
sdrv_acmp_rcv_qch_cmp(dev->base, cmp_en);
|
||
|
||
/* dac recv q channel low power request */
|
||
sdrv_acmp_rcv_qch_dac(dev->base, dac_en);
|
||
|
||
return SDRV_STATUS_OK;
|
||
}
|
||
|
||
/**
|
||
* @brief sdrv acmp set callback
|
||
*
|
||
* This function is used to set the sdrv acmp callback function separately, after acmp initilization.
|
||
*
|
||
* @param[in] dev sdrv acmp device
|
||
* @param[in] callback acmp callback function
|
||
* @return SDRV_STATUS_OK represents success
|
||
*/
|
||
status_t sdrv_acmp_set_callback(sdrv_acmp_t *dev, sdrv_acmp_callback callback)
|
||
{
|
||
dev->callback = callback;
|
||
|
||
return SDRV_STATUS_OK;
|
||
}
|
||
|
||
/**
|
||
* @brief sdrv acmp deinit
|
||
*
|
||
* This function stops acmp and deletes acmp initilization.
|
||
* Acmp must be de-initiated before entering lowpower mode.
|
||
*
|
||
* @param[in] dev sdrv acmp device
|
||
* @return SDRV_STATUS_OK represents success
|
||
*/
|
||
status_t sdrv_acmp_deinit(sdrv_acmp_t *dev)
|
||
{
|
||
status_t ret;
|
||
|
||
if (dev->irq > 0) {
|
||
irq_disable(dev->irq);
|
||
irq_detach(dev->irq);
|
||
}
|
||
|
||
/* stop acmp */
|
||
ret = sdrv_acmp_start(dev, false);
|
||
|
||
/* clear ACMP IT mode */
|
||
sdrv_acmp_config_mode(dev->base, ACMP_HT_MODE);
|
||
|
||
/* soft reset */
|
||
sdrv_acmp_soft_reset(dev->base);
|
||
|
||
/* clear the acmp controller. */
|
||
(void)memset(dev, 0, sizeof(sdrv_acmp_t));
|
||
|
||
return ret;
|
||
}
|