/* * Copyright (c) 2021 Actions Semiconductor Co., Ltd * * SPDX-License-Identifier: Apache-2.0 */ /** * @file * @brief Actions PMU ADC implementation */ #include #include #include #include #include #include #include #include #include #include #include LOG_MODULE_REGISTER(pmuadc0, CONFIG_ADC_LOG_LEVEL); /*************************************************************************************************** * PMUADC_CTL */ #define PMUADC_CTL_REG_SENSOR_EFUSE_SHIFT (26) #define PMUADC_CTL_REG_SENSOR_EFUSE_MASK (0x3F << PMUADC_CTL_REG_SENSOR_EFUSE_SHIFT) #define PMUADC_CTL_EN_TEST_SEN_CURR BIT(25) #define ADC_SEL BIT(24) #define PMUADC_CTL_PMUADC_EN BIT(23) #define PMUADC_CTL_REG_IBIAS_BUF_SHIFT (21) #define PMUADC_CTL_REG_IBIAS_BUF_MASK (0x3 << PMUADC_CTL_REG_IBIAS_BUF_SHIFT) #define PMUADC_CTL_REG_IBIAS_BUF(x) ((x) << PMUADC_CTL_REG_IBIAS_BUF_SHIFT) #define PMUADC_CTL_REG_IBIAS_ADC_SHIFT (19) #define PMUADC_CTL_REG_IBIAS_ADC_MASK (0x3 << PMUADC_CTL_REG_IBIAS_ADC_SHIFT) #define PMUADC_CTL_REG_IBIAS_ADC(x) ((x) << PMUADC_CTL_REG_IBIAS_ADC_SHIFT) #define PMUADC_CTL_TEST_SARAD BIT(18) #define PMUADC_CTL_LRADC6_SCAL BIT(17) #define PMUADC_CTL_LRADC5_SCAL BIT(16) #define PMUADC_CTL_LRADC4_SCAL BIT(15) #define PMUADC_CTL_LRADC3_SCAL BIT(14) #define PMUADC_CTL_LRADC2_SCAL BIT(13) #define PMUADC_CTL_SCAN_MODE BIT(12) #define PMUADC_CTL_LRADC6_CHEN BIT(11) #define PMUADC_CTL_LRADC5_CHEN BIT(10) #define PMUADC_CTL_LRADC4_CHEN BIT(9) #define PMUADC_CTL_LRADC3_CHEN BIT(8) #define PMUADC_CTL_LRADC2_CHEN BIT(7) #define PMUADC_CTL_VCCI_CHEN BIT(6) #define PMUADC_CTL_LRADC1_CHEN BIT(5) #define PMUADC_CTL_SVCC_CHEN BIT(4) #define PMUADC_CTL_SENSOR_CHEN BIT(3) #define PMUADC_CTL_DC5V_CHEN BIT(2) #define PMUADC_CTL_BATV_CHEN BIT(1) #define PMUADC_CTL_CHARGI_CHEN BIT(0) /*************************************************************************************************** * PMUADC_INTMASK */ #define PMUADC_INTMASK_LRADC6_INTEN BIT(11) #define PMUADC_INTMASK_LRADC5_INTEN BIT(10) #define PMUADC_INTMASK_LRADC4_INTEN BIT(9) #define PMUADC_INTMASK_LRADC3_INTEN BIT(8) #define PMUADC_INTMASK_LRADC2_INTEN BIT(7) #define PMUADC_INTMASK_VCCI_INTEN BIT(6) #define PMUADC_INTMASK_LRADC1_INTEN BIT(5) #define PMUADC_INTMASK_SVCC_INTEN BIT(4) #define PMUADC_INTMASK_SENSOR_INTEN BIT(3) #define PMUADC_INTMASK_DC5V_INTEN BIT(2) #define PMUADC_INTMASK_BATV_INTEN BIT(1) #define PMUADC_INTMASK_CHARGI_INTEN BIT(0) /*************************************************************************************************** * PMUADC_PD */ #define PMUADC_PD_LRADC6_PD BIT(11) #define PMUADC_PD_LRADC5_PD BIT(10) #define PMUADC_PD_LRADC4_PD BIT(9) #define PMUADC_PD_LRADC3_PD BIT(8) #define PMUADC_PD_LRADC2_PD BIT(7) #define PMUADC_PD_VCCI_PD BIT(6) #define PMUADC_PD_LRADC1_PD BIT(5) #define PMUADC_PD_SVCC_PD BIT(4) #define PMUADC_PD_SENSOR_PD BIT(3) #define PMUADC_PD_DC5V_PD BIT(2) #define PMUADC_PD_BATV_PD BIT(1) #define PMUADC_PD_CHARGI_PD BIT(0) /*************************************************************************************************** * CHARGI_DATA */ #define CHARGI_DATA_CHARGI_SHIFT (0) #define CHARGI_DATA_CHARGI_MASK (0x1FFF << CHARGI_DATA_CHARGI_SHIFT) /*************************************************************************************************** * BATADC_DATA */ #define BATADC_DATA_BATV_SHIFT (0) #define BATADC_DATA_BATV_MASK (0x3FFF << BATADC_DATA_BATV_SHIFT) /*************************************************************************************************** * DC5VADC_DATA */ #define DC5VADC_DATA_DC5V_SHIFT (0) #define DC5VADC_DATA_DC5V_MASK (0xFFF << DC5VADC_DATA_DC5V_SHIFT) /*************************************************************************************************** * SENSADC_DATA */ #define SENSADC_DATA_SENSOR_SHIFT (0) #define SENSADC_DATA_SENSOR_MASK (0xFFF << SENSADC_DATA_SENSOR_SHIFT) /*************************************************************************************************** * SVCCADC_DATA */ #define SVCCADC_DATA_SVCC_SHIFT (0) #define SVCCADC_DATA_SVCC_MASK (0xFFF << SVCCADC_DATA_SVCC_SHIFT) /*************************************************************************************************** * VCCI_DATA */ #define VCCIADC_DATA_VCCI_SHIFT (0) #define VCCIADC_DATA_VCCI_MASK (0x1FFF << VCCIADC_DATA_VCCI_SHIFT) /*************************************************************************************************** * LRADC_DATA */ #define LRADC_DATA_LRADC_SHIFT (0) #define LRADC_DATA_LRADC_MASK (0x3FFF << LRADC_DATA_LRADC_SHIFT) /*************************************************************************************************** * PMUADCDIG_CTL */ #define PMUADCDIG_CTL_LRADC6_AVG_SHIFT (30) #define PMUADCDIG_CTL_LRADC6_AVG_MASK (0x3 << PMUADCDIG_CTL_LRADC6_AVG_SHIFT) #define PMUADCDIG_CTL_LRADC6_AVG(x) ((x) << PMUADCDIG_CTL_LRADC6_AVG_SHIFT) #define PMUADCDIG_CTL_LRADC5_AVG_SHIFT (28) #define PMUADCDIG_CTL_LRADC5_AVG_MASK (0x3 << PMUADCDIG_CTL_LRADC5_AVG_SHIFT) #define PMUADCDIG_CTL_LRADC5_AVG(x) ((x) << PMUADCDIG_CTL_LRADC5_AVG_SHIFT) #define PMUADCDIG_CTL_LRADC4_AVG_SHIFT (26) #define PMUADCDIG_CTL_LRADC4_AVG_MASK (0x3 << PMUADCDIG_CTL_LRADC4_AVG_SHIFT) #define PMUADCDIG_CTL_LRADC4_AVG(x) ((x) << PMUADCDIG_CTL_LRADC4_AVG_SHIFT) #define PMUADCDIG_CTL_LRADC3_AVG_SHIFT (24) #define PMUADCDIG_CTL_LRADC3_AVG_MASK (0x3 << PMUADCDIG_CTL_LRADC3_AVG_SHIFT) #define PMUADCDIG_CTL_LRADC3_AVG(x) ((x) << PMUADCDIG_CTL_LRADC3_AVG_SHIFT) #define PMUADCDIG_CTL_LRADC2_AVG_SHIFT (22) #define PMUADCDIG_CTL_LRADC2_AVG_MASK (0x3 << PMUADCDIG_CTL_LRADC2_AVG_SHIFT) #define PMUADCDIG_CTL_LRADC2_AVG(x) ((x) << PMUADCDIG_CTL_LRADC2_AVG_SHIFT) #define PMUADCDIG_CTL_VCCI_AVG_SHIFT (20) #define PMUADCDIG_CTL_VCCI_AVG_MASK (0x3 << PMUADCDIG_CTL_VCCI_AVG_SHIFT) #define PMUADCDIG_CTL_VCCI_AVG(x) ((x) << PMUADCDIG_CTL_VCCI_AVG_SHIFT) #define PMUADCDIG_CTL_LRADC1_AVG_SHIFT (18) #define PMUADCDIG_CTL_LRADC1_AVG_MASK (0x3 << PMUADCDIG_CTL_LRADC1_AVG_SHIFT) #define PMUADCDIG_CTL_LRADC1_AVG(x) ((x) << PMUADCDIG_CTL_LRADC1_AVG_SHIFT) #define PMUADCDIG_CTL_SVCCADC_AVG_SHIFT (16) #define PMUADCDIG_CTL_SVCCADC_AVG_MASK (0x3 << PMUADCDIG_CTL_SVCCADC_AVG_SHIFT) #define PMUADCDIG_CTL_SVCCADC_AVG(x) ((x) << PMUADCDIG_CTL_SVCCADC_AVG_SHIFT) #define PMUADCDIG_CTL_SESADC_AVG_SHIFT (14) #define PMUADCDIG_CTL_SESADC_AVG_MASK (0x3 << PMUADCDIG_CTL_SESADC_AVG_SHIFT) #define PMUADCDIG_CTL_SESADC_AVG(x) ((x) << PMUADCDIG_CTL_SESADC_AVG_SHIFT) #define PMUADCDIG_CTL_DC5VADC_AVG_SHIFT (12) #define PMUADCDIG_CTL_DC5VADC_AVG_MASK (0x3 << PMUADCDIG_CTL_DC5VADC_AVG_SHIFT) #define PMUADCDIG_CTL_DC5VADC_AVG(x) ((x) << PMUADCDIG_CTL_DC5VADC_AVG_SHIFT) #define PMUADCDIG_CTL_BAT_AVG_SHIFT (10) #define PMUADCDIG_CTL_BAT_AVG_MASK (0x3 << PMUADCDIG_CTL_BAT_AVG_SHIFT) #define PMUADCDIG_CTL_BAT_AVG(x) ((x) << PMUADCDIG_CTL_BAT_AVG_SHIFT) #define PMUADCDIG_CTL_CHARGI_AVG_SHIFT (8) #define PMUADCDIG_CTL_CHARGI_AVG_MASK (0x3 << PMUADCDIG_CTL_CHARGI_AVG_SHIFT) #define PMUADCDIG_CTL_CHARGI_AVG(x) ((x) << PMUADCDIG_CTL_CHARGI_AVG_SHIFT) #define PMUADCDIG_CTL_LRADC2_R_SEL BIT(5) #define PMUADCDIG_CTL_RVALUE_SEL_SHIFT (0) #define PMUADCDIG_CTL_RVALUE_SEL_MASK (0x1f << PMUADCDIG_CTL_RVALUE_SEL_SHIFT) #define PMUADCDIG_CTL_RVALUE_SEL(x) ((x) << PMUADCDIG_CTL_RVALUE_SEL_SHIFT) #define PMUADC_MAX_CHANNEL_ID (11) /* MAX valid channel ID */ #define PMUADC_WAIT_TIMEOUT_MS (50) /* Timeout to wait for PMU ADC sampling */ #define PMUADC_WAIT_OVER_SAMPLE_US (3000) //(1000) #if (CONFIG_PMUADC_LRADC1_AVG == 0) #define LRADCx_PENDING(ch, n) \ if ((ch) & PMUADC_PD_LRADC##n##_PD) { \ data->mdata.v.lradc##n##_voltage = pmuadc_reg->lradc##n##_data & LRADC_DATA_LRADC_MASK; \ if (n == 1) \ data->channels &= ~(BIT(n + 4)); \ else \ data->channels &= ~(BIT(n + 5)); \ LOG_DBG("New LRADC%d voltage: 0x%x", n, data->mdata.v.lradc##n##_voltage); \ } #else #define LRADCx_PENDING(ch, n) \ if ((ch) & PMUADC_PD_LRADC##n##_PD) { \ ret = pmuadc_wait_sample_complete(dev, ch); \ if (ret) \ return ret; \ data->mdata.v.lradc##n##_voltage = pmuadc_reg->lradc##n##_data & LRADC_DATA_LRADC_MASK; \ if (n == 1) \ data->channels &= ~(BIT(n + 4)); \ else \ data->channels &= ~(BIT(n + 5)); \ LOG_DBG("New LRADC%d voltage: 0x%x", n, data->mdata.v.lradc##n##_voltage); \ } #endif #define LRADCx_PENDING_ALL(x) \ { \ LRADCx_PENDING(x, 1); \ LRADCx_PENDING(x, 2); \ LRADCx_PENDING(x, 3); \ LRADCx_PENDING(x, 4); \ LRADCx_PENDING(x, 5); \ LRADCx_PENDING(x, 6); \ } /* * @struct acts_pmu_adc * @brief Actions PMU ADC controller hardware register */ struct acts_pmu_adc { volatile uint32_t ctl; /* PMUADC control */ volatile uint32_t intmask; /* PMUADC sample interrupt set */ volatile uint32_t pending; /* PMUADC sample pending */ volatile uint32_t dig_ctl; /* PMUADC digital control */ volatile uint32_t charge_data; /* Measure charging current */ volatile uint32_t bat_data; /* Measure battery voltage */ volatile uint32_t dc5v_data; /* Measure DC5V voltage */ volatile uint32_t sensor_data; /* IC temperature sensor */ volatile uint32_t svcc_data; /* Measure SVCC voltage */ volatile uint32_t lradc1_data; /* Measure LRADC1 IO voltage */ volatile uint32_t vcci_data; /* Measure BAT to VCC current voltage */ volatile uint32_t lradc2_data; /* Measure LRADC2 IO voltage */ volatile uint32_t lradc3_data; /* Measure LRADC3 IO voltage */ volatile uint32_t lradc4_data; /* Measure LRADC4 IO voltage */ volatile uint32_t lradc5_data; /* Measure LRADC5 IO voltage */ volatile uint32_t lradc6_data; /* Measure LRADC6 IO voltage */ }; /** * struct pmuadc_measure_data * @brief The measure result data from PMU ADC sampling. */ union pmuadc_measure_data { uint16_t data[PMUADC_MAX_CHANNEL_ID + 1]; struct { uint16_t charging_current; uint16_t battery_voltage; //uint16_t charging_current; uint16_t dc5v_voltage; uint16_t sensor_temperature; uint16_t svcc_voltage; uint16_t lradc1_voltage; uint16_t vcci_voltage; uint16_t lradc2_voltage; uint16_t lradc3_voltage; uint16_t lradc4_voltage; uint16_t lradc5_voltage; uint16_t lradc6_voltage; } v; }; /** * struct pmuadc_drv_data * @brief The meta data which related to Actions PMU ADC. */ struct pmuadc_drv_data { uint16_t channel_bitmap; //#ifndef CONFIG_ADC_ACTS_ALWAYS_ON bool b_always_on; struct k_sem completion; /* ADC sample synchronization completion semaphare */ //#endif struct k_sem lock; /* ADC read lock */ union pmuadc_measure_data mdata; /* measuared data */ uint16_t channels; /* active channels */ uint16_t sample_cnt; /* sample counter */ }; /** * struct pmuadc_config_data * @brief The hardware data that related to Actions PMU ADC */ struct pmuadc_config_data { uint32_t reg_base; /* PMU ADC controller register base address */ uint8_t clk_id; /* LRADC devclk id */ uint8_t clk_src; /* LRADC clock source */ uint8_t clk_div; /* LRADC clock divisor */ uint8_t debounce; /* PMU ADC debounce as the first sample data is not correct */ void (*irq_config)(void); /* IRQ configuration function */ }; /* @brief get the base address of PMU ADC register */ static inline struct acts_pmu_adc *get_pmuadc_reg_base(const struct device *dev) { const struct pmuadc_config_data *cfg = dev->config; return (struct acts_pmu_adc *)cfg->reg_base; } /* @brief dump pmu dac controller register */ void pmudac_dump_register(const struct device *dev) { struct acts_pmu_adc *pmuadc_reg = get_pmuadc_reg_base(dev); LOG_INF("** pmudac contoller regster **"); LOG_INF(" BASE: %08x", (uint32_t)pmuadc_reg); LOG_INF(" CTL: %08x", pmuadc_reg->ctl); LOG_INF(" INTMASK: %08x", pmuadc_reg->intmask); LOG_INF(" PENDING: %08x", pmuadc_reg->pending); LOG_INF(" DIG_CTL: %08x", pmuadc_reg->dig_ctl); LOG_INF(" CHARGE_DATA: %08x", pmuadc_reg->charge_data); LOG_INF(" BAT_DATA: %08x", pmuadc_reg->bat_data); LOG_INF(" DC5V_DATA: %08x", pmuadc_reg->dc5v_data); LOG_INF(" SENSOR_DATA: %08x", pmuadc_reg->sensor_data); LOG_INF(" SVCC_DATA: %08x", pmuadc_reg->svcc_data); LOG_INF(" VCCI_DATA: %08x", pmuadc_reg->vcci_data); LOG_INF(" LRADC1_DATA: %08x", pmuadc_reg->lradc1_data); LOG_INF(" LRADC2_DATA: %08x", pmuadc_reg->lradc2_data); LOG_INF(" LRADC3_DATA: %08x", pmuadc_reg->lradc3_data); LOG_INF(" LRADC4_DATA: %08x", pmuadc_reg->lradc4_data); LOG_INF(" LRADC5_DATA: %08x", pmuadc_reg->lradc5_data); LOG_INF(" LRADC6_DATA: %08x", pmuadc_reg->lradc6_data); LOG_INF(" CMU_LRADCCLK: %08x", sys_read32(CMU_LRADCCLK)); } static s16_t adc_sensor_offset, adc_dc5v_offset, adc_bat_offset; /* @brief PMU ADC channel lock */ static inline void pmuadc_lock(const struct device *dev) { struct pmuadc_drv_data *data = dev->data; k_sem_take(&data->lock, K_FOREVER); } /* @brief PMU ADC channel unlock */ static inline void pmuadc_unlock(const struct device *dev) { struct pmuadc_drv_data *data = dev->data; k_sem_give(&data->lock); } //#ifndef CONFIG_ADC_ACTS_ALWAYS_ON /* @brief wait for PMU ADC sample IRQ completion */ static inline int pmuadc_wait_for_completion(const struct device *dev) { struct pmuadc_drv_data *data = dev->data; return k_sem_take(&data->completion, K_MSEC(PMUADC_WAIT_TIMEOUT_MS)); } /* @brief signal that PMU ADC sample data completly */ static inline void pmuadc_complete(const struct device *dev) { struct pmuadc_drv_data *data = dev->data; k_sem_give(&data->completion); } //#endif /* @brief check the buffer size */ static int check_buffer_size(const struct adc_sequence *sequence, uint8_t active_channels) { uint32_t needed_buffer_size; needed_buffer_size = active_channels * sizeof(uint16_t); if (sequence->buffer_size < needed_buffer_size) { LOG_ERR("Provided buffer is too small (%u/%u)", sequence->buffer_size, needed_buffer_size); return -ENOMEM; } return 0; } /* @brief validate the selected PMU ADC channels */ static int pmuadc_check_channels(const struct device *dev, const struct adc_sequence *sequence) { struct pmuadc_drv_data *data = dev->data; uint32_t channels = sequence->channels; uint8_t i, active_channels = 0; if (!channels) { LOG_ERR("null channels"); return -EINVAL; } for (i = 0; i <= PMUADC_MAX_CHANNEL_ID; i++) { if (channels & BIT(i)) { if (!(data->channel_bitmap & BIT(i))) { LOG_ERR("ADC channel@%d has not setuped yet", i); return -ENXIO; } else { ++active_channels; } } } return check_buffer_size(sequence, active_channels); } /* @brief enable specified PMU ADC channels to sample data */ static int pmuadc_enable_channels(const struct device *dev, uint16_t channels) { struct pmuadc_drv_data *data = dev->data; struct acts_pmu_adc *pmuadc_reg = get_pmuadc_reg_base(dev); uint8_t i; uint32_t ctl = 0; data->channels = channels; data->sample_cnt = 0; /* enable lradc clk */ acts_clock_peripheral_enable(CLOCK_ID_LRADC); LOG_DBG("Active channels:0x%x", data->channels); //#ifdef CONFIG_ADC_ACTS_ALWAYS_ON if(data->b_always_on) { /* If enable new channels, need to disable PMUADC and then enable */ if (pmuadc_reg->ctl & PMUADC_CTL_PMUADC_EN) { pmuadc_reg->ctl &= ~PMUADC_CTL_PMUADC_EN; //k_busy_wait(300); } } //#endif /* enable PMU ADC channles */ for (i = 0; i <= PMUADC_MAX_CHANNEL_ID; i++) { if (channels & BIT(i)) ctl |= BIT(i); } pmuadc_reg->ctl |= ctl; /* enable PMU ADC function */ pmuadc_reg->ctl |= PMUADC_CTL_PMUADC_EN; pmuadc_reg->pending = pmuadc_reg->pending; //#ifndef CONFIG_ADC_ACTS_ALWAYS_ON if(!data->b_always_on) { uint16_t intmask = 0; /* enable channels interrupts */ for (i = 0; i <= PMUADC_MAX_CHANNEL_ID; i++) { if (channels & BIT(i)) { intmask |= BIT(i); } } pmuadc_reg->intmask = intmask; } //#endif return 0; } /* @brief disable all PMU ADC channels */ static int pmuadc_disable_channels(const struct device *dev) { struct acts_pmu_adc *pmuadc_reg = get_pmuadc_reg_base(dev); /* disable all ADC channels */ pmuadc_reg->intmask = 0; pmuadc_reg->pending = pmuadc_reg->pending; pmuadc_reg->ctl &= ~(PMUADC_CTL_PMUADC_EN | 0x7FF); //k_busy_wait(300); /* disable lradc clk */ acts_clock_peripheral_disable(CLOCK_ID_LRADC); return 0; } //#ifdef CONFIG_ADC_ACTS_ALWAYS_ON static int pmuadc_wait_sample_complete(const struct device *dev, uint8_t ch_index) { uint32_t timestamp = k_cycle_get_32(); struct acts_pmu_adc *pmuadc_reg = get_pmuadc_reg_base(dev); while(!(ch_index & pmuadc_reg->pending)) { if (k_cyc_to_us_floor32(k_cycle_get_32() - timestamp) > PMUADC_WAIT_OVER_SAMPLE_US) { LOG_ERR("failed to get ADC channel:%d PD:0x%x CTL:0x%x", ch_index, pmuadc_reg->pending, pmuadc_reg->ctl); return -ETIMEDOUT; } } LOG_DBG("pmuadc wait channel:%d use %dus", ch_index, k_cyc_to_us_floor32(k_cycle_get_32() - timestamp)); return 0; } //#endif /* @brief Implementation of the ADC driver API function: adc_channel_setup. */ static int pmuadc_channel_setup(const struct device *dev, const struct adc_channel_cfg *channel_cfg) { struct pmuadc_drv_data *data = dev->data; uint8_t channel_id = channel_cfg->channel_id; if (channel_id > PMUADC_MAX_CHANNEL_ID) { LOG_ERR("Invalid channel id %d", channel_id); return -EINVAL; } pmuadc_lock(dev); if (!(data->channel_bitmap & BIT(channel_id))) { data->channel_bitmap |= BIT(channel_id); LOG_INF("Enable PMU ADC channel@%d", channel_id); //#ifdef CONFIG_ADC_ACTS_ALWAYS_ON if(data->b_always_on) { pmuadc_enable_channels(dev, BIT(channel_id)); #if (CONFIG_PMUADC_BAT_WAIT_AVG_COMPLETE == 0) && (CONFIG_PMUADC_BAT_AVG_CNT == 2) /* Ignore the first BAT Voltage sample data */ if (channel_id == PMUADC_ID_BATV) pmuadc_wait_sample_complete(dev, BIT(PMUADC_ID_BATV)); #endif //k_busy_wait(300); } //#endif } pmuadc_unlock(dev); return 0; } static u16_t pmu_adc_cal(int32_t adcval, int32_t offset) { adcval = adcval + offset; return (u16_t)adcval; } /* @brief start to read the PMU ADC measure data */ static int pmuadc_start_read(const struct device *dev, const struct adc_sequence *sequence) { struct pmuadc_drv_data *data = dev->data; int ret = 0; uint32_t channels = sequence->channels; uint8_t i; //#ifdef CONFIG_ADC_ACTS_ALWAYS_ON if(data->b_always_on) { struct acts_pmu_adc *pmuadc_reg = get_pmuadc_reg_base(dev); /* clear all channels pending */ pmuadc_reg->pending = pmuadc_reg->pending; if (channels & PMUADC_PD_BATV_PD) { #if (CONFIG_PMUADC_BAT_WAIT_AVG_COMPLETE == 1) && (CONFIG_PMUADC_BAT_AVG_CNT == 2) ret = pmuadc_wait_sample_complete(dev, BIT(PMUADC_ID_BATV)); if (ret) return ret; #endif data->mdata.v.battery_voltage = pmuadc_reg->bat_data & BATADC_DATA_BATV_MASK; data->mdata.v.battery_voltage = pmu_adc_cal(data->mdata.v.battery_voltage, adc_bat_offset); } if (channels & PMUADC_PD_CHARGI_PD) data->mdata.v.charging_current = pmuadc_reg->charge_data & CHARGI_DATA_CHARGI_MASK; if (channels & PMUADC_PD_DC5V_PD){ data->mdata.v.dc5v_voltage = pmuadc_reg->dc5v_data & DC5VADC_DATA_DC5V_MASK; data->mdata.v.dc5v_voltage = pmu_adc_cal(data->mdata.v.dc5v_voltage, adc_dc5v_offset); } if (channels & PMUADC_PD_SENSOR_PD){ data->mdata.v.sensor_temperature = pmuadc_reg->sensor_data & SENSADC_DATA_SENSOR_MASK; data->mdata.v.sensor_temperature = pmu_adc_cal(data->mdata.v.sensor_temperature, adc_sensor_offset); } if (channels & PMUADC_PD_SVCC_PD) data->mdata.v.svcc_voltage = pmuadc_reg->svcc_data & SVCCADC_DATA_SVCC_MASK; if (channels & PMUADC_PD_VCCI_PD) data->mdata.v.vcci_voltage = pmuadc_reg->vcci_data & VCCIADC_DATA_VCCI_MASK; LRADCx_PENDING_ALL(channels); }else{ //#else //uint32_t timestamp = k_cycle_get_32(); ret = pmuadc_enable_channels(dev, channels); if (ret) return ret; ret = pmuadc_wait_for_completion(dev); /*LOG_DBG("pmuadc wait channel:%d use %dus", channels, k_cyc_to_us_floor32(k_cycle_get_32() - timestamp));*/ } //#endif /* CONFIG_ADC_ACTS_ALWAYS_ON */ if (!ret) { for (i = 0; i <= PMUADC_MAX_CHANNEL_ID; i++) { if (channels & BIT(i)) { sys_put_le16(data->mdata.data[i], sequence->buffer); } } } //#ifndef CONFIG_ADC_ACTS_ALWAYS_ON if(!data->b_always_on){ pmuadc_disable_channels(dev); } //#endif return ret; } /* @brief Implementation of the ADC driver API function: adc_read. */ static int pmuadc_read(const struct device *dev, const struct adc_sequence *sequence) { int ret; ret = pmuadc_check_channels(dev, sequence); if (ret) return ret; //#ifndef CONFIG_ADC_ACTS_ALWAYS_ON pmuadc_lock(dev); //#endif ret = pmuadc_start_read(dev, sequence); //#ifndef CONFIG_ADC_ACTS_ALWAYS_ON pmuadc_unlock(dev); //#endif return ret; } #ifdef CONFIG_ADC_ASYNC /* @brief Implementation of the ADC driver API function: adc_read_sync. */ static int pmuadc_read_async(const struct device *dev, const struct adc_sequence *sequence, struct k_poll_signal *async) { return -ENOTSUP; } #endif /* CONFIG_ADC_ASYNC */ static const struct adc_driver_api pmuadc_driver_api = { .channel_setup = pmuadc_channel_setup, .read = pmuadc_read, #ifdef CONFIG_ADC_ASYNC .read_async = pmuadc_read_async, #endif }; //#ifndef CONFIG_ADC_ACTS_ALWAYS_ON static void pmuadc_isr(void *arg) { struct device *dev = (struct device *)arg; struct pmuadc_drv_data *data = dev->data; const struct pmuadc_config_data *cfg = dev->config; struct acts_pmu_adc *pmuadc_reg = get_pmuadc_reg_base(dev); uint32_t pending = pmuadc_reg->pending; uint32_t val; LOG_DBG("ctl:0x%x mask:0x%x pending:0x%x channels:0x%x", pmuadc_reg->ctl, pmuadc_reg->intmask, pending, data->channels); if (pending & PMUADC_PD_CHARGI_PD) { val = pmuadc_reg->charge_data & CHARGI_DATA_CHARGI_MASK; data->mdata.v.charging_current = val; data->channels &= ~BIT(0); LOG_DBG("New charging current: 0x%x", val); } if (pending & PMUADC_PD_BATV_PD) { val = pmuadc_reg->bat_data & BATADC_DATA_BATV_MASK; data->mdata.v.battery_voltage = pmu_adc_cal(val, adc_bat_offset); data->channels &= ~BIT(1); LOG_DBG("New battery voltage: 0x%x", val); } if (pending & PMUADC_PD_DC5V_PD) { val = pmuadc_reg->dc5v_data & DC5VADC_DATA_DC5V_MASK; data->mdata.v.dc5v_voltage = pmu_adc_cal(val, adc_dc5v_offset); data->channels &= ~BIT(2); LOG_DBG("New DC5V voltage: 0x%x", val); } if (pending & PMUADC_PD_SENSOR_PD) { val = pmuadc_reg->sensor_data & SENSADC_DATA_SENSOR_MASK; data->mdata.v.sensor_temperature = pmu_adc_cal(val, adc_sensor_offset);; data->channels &= ~BIT(3); LOG_DBG("New sensor temperature: 0x%x", val); } if (pending & PMUADC_PD_SVCC_PD) { val = pmuadc_reg->svcc_data & SVCCADC_DATA_SVCC_MASK; data->mdata.v.svcc_voltage = val; data->channels &= ~BIT(4); LOG_DBG("New SVCC voltage: 0x%x", val); } if (pending & PMUADC_PD_VCCI_PD) { val = pmuadc_reg->vcci_data & VCCIADC_DATA_VCCI_MASK; data->mdata.v.vcci_voltage = val; data->channels &= ~BIT(6); LOG_DBG("New SVCC voltage: 0x%x", val); } LRADCx_PENDING_ALL(pending); pmuadc_reg->pending = pending; if ((!data->channels) && (++data->sample_cnt > cfg->debounce)) { LOG_DBG("complete"); pmuadc_reg->intmask = 0; pmuadc_complete(dev); } } //#endif /* @brief set the LRADC clock source and divisor */ static int pmuadc_clk_source_set(const struct device *dev) { const struct pmuadc_config_data *cfg = dev->config; sys_write32(((cfg->clk_src & 0x7) << 4) | ((cfg->clk_div & 0x3)), CMU_LRADCCLK); LOG_DBG("LRADCCLK:0x%08x", sys_read32(CMU_LRADCCLK)); return 0; } /* @brief ADC core current bias setting */ static int pmuadc_bias_setting(const struct device *dev) { struct acts_pmu_adc *pmuadc_reg = get_pmuadc_reg_base(dev); uint32_t reg = pmuadc_reg->ctl; reg &= ~PMUADC_CTL_REG_IBIAS_BUF_MASK; reg |= PMUADC_CTL_REG_IBIAS_BUF(CONFIG_PMUADC_IBIAS_BUF_SEL); reg &= ~PMUADC_CTL_REG_IBIAS_ADC_MASK; reg |= PMUADC_CTL_REG_IBIAS_ADC(CONFIG_PMUADC_IBIAS_ADC_SEL); pmuadc_reg->ctl = reg; return 0; } /* @brief ADC channels over sampling times setting */ static int pmuadc_digital_setting(const struct device *dev) { struct acts_pmu_adc *pmuadc_reg = get_pmuadc_reg_base(dev); uint32_t reg = pmuadc_reg->dig_ctl; reg &= ~PMUADCDIG_CTL_BAT_AVG_MASK; reg |= PMUADCDIG_CTL_BAT_AVG(CONFIG_PMUADC_BAT_AVG_CNT); reg &= ~PMUADCDIG_CTL_LRADC1_AVG_MASK; reg |= PMUADCDIG_CTL_LRADC1_AVG(CONFIG_PMUADC_LRADC1_AVG); pmuadc_reg->dig_ctl = reg; return 0; } static void adc_cal_init(void) { unsigned int offset; adc_bat_offset = 0; adc_sensor_offset = 0; adc_dc5v_offset = 0; if (!soc_atp_get_pmu_calib(4, &offset)){ LOG_DBG("get batadc cal=0x%x\n", offset); if(offset & 0x10) //转换成adc 校准值 adc_bat_offset = -(0x20-offset)*16; else adc_bat_offset = offset*16; } /* if(sensor_efuse & 0x10) efuse_temp = - (0x20-sensor_efuse)*10;//offset校准按摄氏度,补码 0.1度 else efuse_temp = sensor_efuse*10; adc_temp = 4532 - ((adc_val*7004)/4096);//单位0.1度 sd 算法 -> 4532-((adc_val*1751)/1024) sensor_temp = adc_temp + efuse_temp = 4532-((adc_val*1751)/1024) + efuse_temp = 4532 -((adc_val*1751)/1024 - efuse_temp) = 4532 -(adc_val - efuse_temp*1024/1751)*1751/1024 */ if (!soc_atp_get_pmu_calib(5, &offset)){ //offset校准按摄氏度,补码 单位0.1度 LOG_DBG("get sensoradc cal=0x%x\n", offset); if(offset & 0x10) adc_sensor_offset = -(0x20-offset)*10; else adc_sensor_offset = offset*10; /*把校准值转成adc 值*/ adc_sensor_offset = -adc_sensor_offset*1024/1751; } if (!soc_atp_get_pmu_calib(7, &offset)){ LOG_DBG("get dv5v cal=0x%x\n", offset); if(offset & 0x10) adc_dc5v_offset = -(0x20-offset)*4; else adc_dc5v_offset = offset*4; } LOG_DBG("adc:bat=%d,sensor=%d,dc5v=%d\n", adc_bat_offset, adc_sensor_offset, adc_dc5v_offset); } /* @brief PMU ADC initialization */ static int pmuadc_init(const struct device *dev) { const struct pmuadc_config_data *cfg = dev->config; struct pmuadc_drv_data *data = dev->data; struct acts_pmu_adc *pmuadc_reg = get_pmuadc_reg_base(dev); adc_cal_init(); /* configure the LRADC clock source */ pmuadc_clk_source_set(dev); acts_clock_peripheral_enable(cfg->clk_id); /* disable all ADC channels */ pmuadc_reg->intmask = 0; pmuadc_reg->pending = pmuadc_reg->pending; pmuadc_reg->ctl &= ~0x3FFFFFF; /* set scan mode, scan one channel each time */ pmuadc_reg->ctl |= PMUADC_CTL_SCAN_MODE; pmuadc_bias_setting(dev); pmuadc_digital_setting(dev); k_busy_wait(300); //#ifndef CONFIG_ADC_ACTS_ALWAYS_ON k_sem_init(&data->completion, 0, 1); //#endif k_sem_init(&data->lock, 1, 1); if (cfg->irq_config) cfg->irq_config(); #ifdef CONFIG_ADC_ACTS_ALWAYS_ON data->b_always_on = true; irq_disable(IRQ_ID_LRADC); #else data->b_always_on = false; #endif return 0; } int pmuadc_mode_switch(bool always_on) { struct pmuadc_drv_data *data; const struct device *dev = device_get_binding(CONFIG_PMUADC_NAME); if (dev == NULL) { printk("ADC device not found\n"); return -ENODEV; } data = dev->data; pmuadc_lock(dev); if(always_on){ if(!data->b_always_on){ printk("adc setto alwayson=0x%x\n", data->channel_bitmap); data->b_always_on = always_on; irq_disable(IRQ_ID_LRADC); pmuadc_enable_channels(dev, data->channel_bitmap); #if (CONFIG_PMUADC_BAT_AVG_CNT == 2) pmuadc_wait_sample_complete(dev, BIT(PMUADC_ID_BATV)); #endif } }else{ if(data->b_always_on){ printk("adc setto not alwayson=0x%x\n", data->channel_bitmap); data->b_always_on = always_on; pmuadc_disable_channels(dev); irq_enable(IRQ_ID_LRADC); } } pmuadc_unlock(dev); return 0; } #ifdef CONFIG_PM_DEVICE int adc_pm_control(const struct device *device, enum pm_device_action action) { //int ret; //const struct pmuadc_config_data *cfg = device->config; struct pmuadc_drv_data *data = device->data; //#ifdef CONFIG_ADC_ACTS_ALWAYS_ON // static uint32_t pmuadc_ctl_bak; // struct acts_pmu_adc *pmuadc_reg = get_pmuadc_reg_base(device); //#endif switch (action) { case PM_DEVICE_ACTION_RESUME: sl_dbg("adc wakeup\n"); if(data->b_always_on){ //acts_clock_peripheral_enable(cfg->clk_id); //#ifdef CONFIG_ADC_ACTS_ALWAYS_ON //pmuadc_reg->ctl = (pmuadc_ctl_bak & ~PMUADC_CTL_PMUADC_EN); //LOG_DBG("%d PMUADC_CTL:0x%x", __LINE__, pmuadc_reg->ctl); //k_busy_wait(300); //pmuadc_reg->ctl |= PMUADC_CTL_PMUADC_EN; //LOG_DBG("%d PMUADC_CTL:0x%x", __LINE__, pmuadc_reg->ctl); //k_busy_wait(300); pmuadc_enable_channels(device, data->channel_bitmap); }else{ //#else irq_enable(IRQ_ID_LRADC); //#endif } break; case PM_DEVICE_ACTION_SUSPEND: //#ifndef CONFIG_ADC_ACTS_ALWAYS_ON if (!k_sem_count_get(&data->lock)) { printk("adc busy, not suspend\n"); return -EINVAL; } //#endif sl_dbg("adc sleep\n"); //#ifdef CONFIG_ADC_ACTS_ALWAYS_ON if(data->b_always_on){ //pmuadc_ctl_bak = pmuadc_reg->ctl; pmuadc_disable_channels(device); //acts_clock_peripheral_disable(cfg->clk_id); }else{ //#else irq_disable(IRQ_ID_LRADC); } //#endif break; case PM_DEVICE_ACTION_EARLY_SUSPEND: break; case PM_DEVICE_ACTION_LATE_RESUME: break; default: break; //ret = -EINVAL; } return 0; } #else #define adc_pm_control NULL #endif //#ifndef CONFIG_ADC_ACTS_ALWAYS_ON static void pmuadc_irq_config(void); //#endif /* pmu adc driver data */ static struct pmuadc_drv_data pmuadc_drv_data0; /* pmu adc config data */ static const struct pmuadc_config_data pmuadc_config_data0 = { .reg_base = PMUADC_CTL, .clk_id = CLOCK_ID_LRADC, .clk_src = CONFIG_PMUADC_CLOCK_SOURCE, .clk_div = CONFIG_PMUADC_CLOCK_DIV, //#ifndef CONFIG_ADC_ACTS_ALWAYS_ON .debounce = CONFIG_PMUADC_DEBOUNCE, .irq_config = pmuadc_irq_config, //#endif }; DEVICE_DEFINE(pmuadc0, CONFIG_PMUADC_NAME, pmuadc_init, adc_pm_control, &pmuadc_drv_data0, &pmuadc_config_data0, PRE_KERNEL_2, CONFIG_KERNEL_INIT_PRIORITY_OBJECTS, &pmuadc_driver_api); //#ifndef CONFIG_ADC_ACTS_ALWAYS_ON static void pmuadc_irq_config(void) { IRQ_CONNECT(IRQ_ID_LRADC, CONFIG_PMUADC_IRQ_PRI, pmuadc_isr, DEVICE_GET(pmuadc0), 0); irq_enable(IRQ_ID_LRADC); } //#endif