| /* Copyright (c) 2013, The Linux Foundation. All rights reserved. |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions are |
| * met: |
| * * Redistributions of source code must retain the above copyright |
| * notice, this list of conditions and the following disclaimer. |
| * * Redistributions in binary form must reproduce the above |
| * copyright notice, this list of conditions and the following |
| * disclaimer in the documentation and/or other materials provided |
| * with the distribution. |
| * * Neither the name of The Linux Foundation, Inc. nor the names of its |
| * contributors may be used to endorse or promote products derived |
| * from this software without specific prior written permission. |
| * |
| * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED |
| * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF |
| * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT |
| * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS |
| * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
| * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
| * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR |
| * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, |
| * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE |
| * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN |
| * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| */ |
| |
| #include <bits.h> |
| #include <debug.h> |
| #include <reg.h> |
| #include <spmi.h> |
| #include <platform/timer.h> |
| #include <pm8x41_adc.h> |
| #include <pm8x41_hw.h> |
| |
| /* |
| * This is the predefined adc configuration values for the supported |
| * channels |
| */ |
| static struct adc_conf adc_data[] = { |
| CHAN_INIT(VADC_USR1_BASE, VADC_BAT_CHAN_ID, VADC_MODE_NORMAL, VADC_DECIM_RATIO_VAL, HW_SET_DELAY_100US, FAST_AVG_SAMP_1, CALIB_RATIO), |
| CHAN_INIT(VADC_USR1_BASE, VADC_BAT_VOL_CHAN_ID, VADC_MODE_NORMAL, VADC_DECIM_RATIO_VAL, HW_SET_DELAY_100US, FAST_AVG_SAMP_1, CALIB_ABS), |
| }; |
| |
| |
| static struct adc_conf* get_channel_prop(uint16_t ch_num) |
| { |
| struct adc_conf *chan_data = NULL; |
| uint8_t i; |
| |
| for(i = 0; i < ARRAY_SIZE(adc_data) ; i++) { |
| chan_data = &adc_data[i]; |
| if (chan_data->chan == ch_num) |
| break; |
| } |
| |
| return chan_data; |
| } |
| |
| static void adc_limit_result_range(uint16_t *result) |
| { |
| if (*result < VADC_MIN_VAL) |
| *result = VADC_MIN_VAL; |
| else if(*result > VADC_MAX_VAL) |
| *result = VADC_MAX_VAL; |
| } |
| |
| static void adc_read_conv_result(struct adc_conf *adc, uint16_t *result) |
| { |
| uint8_t val = 0; |
| |
| /* Read the MSB part */ |
| val = REG_READ(adc->base + VADC_REG_DATA_MSB); |
| *result = val; |
| |
| /* Read the LSB part */ |
| val = REG_READ(adc->base + VADC_REG_DATA_LSB); |
| *result = ((*result) << 8) | val; |
| |
| adc_limit_result_range(result); |
| } |
| |
| static void adc_enable(struct adc_conf *adc, uint8_t enable) |
| { |
| if (enable) |
| REG_WRITE((adc->base + VADC_EN_CTL), VADC_CTL_EN_BIT); |
| else |
| REG_WRITE((adc->base + VADC_EN_CTL), (uint8_t) (~VADC_CTL_EN_BIT)); |
| } |
| |
| static void adc_measure(struct adc_conf *adc, uint16_t *result) |
| { |
| uint8_t status; |
| |
| /* Request conversion */ |
| REG_WRITE((adc->base + VADC_CONV_REQ), VADC_CON_REQ_BIT); |
| |
| /* Poll for the conversion to complete */ |
| do { |
| status = REG_READ(adc->base + VADC_STATUS); |
| status &= VADC_STATUS_MASK; |
| if (status == VADC_STATUS_EOC) { |
| dprintf(SPEW, "ADC conversion is complete\n"); |
| break; |
| } |
| /* Wait for sometime before polling for the status again */ |
| udelay(10); |
| } while(1); |
| |
| /* Now read the conversion result */ |
| adc_read_conv_result(adc, result); |
| } |
| |
| /* |
| * This function configures adc & requests for conversion |
| */ |
| static uint16_t adc_configure(struct adc_conf *adc) |
| { |
| uint8_t mode; |
| uint8_t adc_p; |
| uint16_t result; |
| |
| /* Mode Selection */ |
| mode = (adc->mode << VADC_MODE_BIT_NORMAL); |
| REG_WRITE((adc->base + VADC_MODE_CTRL), mode); |
| |
| /* Select Channel */ |
| REG_WRITE((adc->base + VADC_CHAN_SEL), adc->chan); |
| |
| /* ADC digital param setup */ |
| adc_p = (adc->adc_param << VADC_DECIM_RATIO_SEL); |
| REG_WRITE((adc->base + VADC_DIG_ADC_PARAM), adc_p); |
| |
| /* hardware settling time */ |
| REG_WRITE((adc->base + VADC_HW_SETTLE_TIME), adc->hw_set_time); |
| |
| /* For normal mode set the fast avg */ |
| REG_WRITE((adc->base + VADC_FAST_AVG), adc->fast_avg); |
| |
| /* Enable Vadc */ |
| adc_enable(adc, true); |
| |
| /* Measure the result */ |
| adc_measure(adc, &result); |
| |
| /* Disable vadc */ |
| adc_enable(adc, false); |
| |
| return result; |
| } |
| |
| static uint32_t vadc_calibrate(uint16_t result, uint8_t calib_type) |
| { |
| struct adc_conf calib; |
| uint16_t calib1; |
| uint16_t calib2; |
| uint32_t calib_result = 0; |
| uint32_t mul; |
| |
| if(calib_type == CALIB_ABS) { |
| /* |
| * Measure the calib data for 1.25 V ref |
| */ |
| calib.base = VADC_USR1_BASE; |
| calib.chan = VREF_125_CHAN_ID; |
| calib.mode = VADC_MODE_NORMAL; |
| calib.adc_param = VADC_DECIM_RATIO_VAL; |
| calib.hw_set_time = 0x0; |
| calib.fast_avg = 0x0; |
| |
| calib1 = adc_configure(&calib); |
| |
| /* |
| * Measure the calib data for 0.625 V ref |
| */ |
| calib.base = VADC_USR1_BASE; |
| calib.chan = VREF_625_CHAN_ID; |
| calib.mode = VADC_MODE_NORMAL; |
| calib.adc_param = VADC_DECIM_RATIO_VAL; |
| calib.hw_set_time = 0x0; |
| calib.fast_avg = 0x0; |
| |
| calib2 = adc_configure(&calib); |
| |
| mul = VREF_625_MV / (calib1 - calib2); |
| calib_result = (result - calib2) * mul; |
| calib_result += VREF_625_MV; |
| calib_result *= OFFSET_GAIN_DNOM; |
| calib_result /= OFFSET_GAIN_NUME; |
| } else if(calib_type == CALIB_RATIO) { |
| /* |
| * Measure the calib data for VDD_ADC ref |
| */ |
| calib.base = VADC_USR1_BASE; |
| calib.chan = VDD_VADC_CHAN_ID; |
| calib.mode = VADC_MODE_NORMAL; |
| calib.adc_param = VADC_DECIM_RATIO_VAL; |
| calib.hw_set_time = 0; |
| calib.fast_avg = 0; |
| |
| calib1 = adc_configure(&calib); |
| |
| /* |
| * Measure the calib data for ADC_GND |
| */ |
| calib.base = VADC_USR1_BASE; |
| calib.chan = GND_REF_CHAN_ID; |
| calib.mode = VADC_MODE_NORMAL; |
| calib.adc_param = VADC_DECIM_RATIO_VAL; |
| calib.hw_set_time = 0; |
| calib.fast_avg = 0; |
| |
| calib2 = adc_configure(&calib); |
| |
| mul = VREF_18_V / (calib1 - calib2); |
| calib_result = (result - calib2) * mul; |
| } |
| |
| return calib_result; |
| } |
| |
| /* |
| * This API takes channel number as input |
| * & returns the calibrated voltage as output |
| * The calibrated result is the voltage in uVs |
| */ |
| uint32_t pm8x41_adc_channel_read(uint16_t ch_num) |
| { |
| struct adc_conf *adc; |
| uint16_t result; |
| uint32_t calib_result; |
| |
| adc = get_channel_prop(ch_num); |
| if (!adc) { |
| dprintf(CRITICAL, "Error: requested channel is not supported: %u\n", ch_num); |
| return -1; |
| } |
| |
| result = adc_configure(adc); |
| |
| calib_result = vadc_calibrate(result, adc->calib_type); |
| |
| dprintf(SPEW, "Result: Raw %u\tCalibrated:%llu\n", result, calib_result); |
| |
| return calib_result; |
| } |
| |
| /* |
| * This function configures the maximum |
| * current for USB in uA |
| */ |
| int pm8x41_iusb_max_config(uint32_t current) |
| { |
| uint32_t mul; |
| |
| if(current < IUSB_MIN_UA || current > IUSB_MAX_UA) { |
| dprintf(CRITICAL, "Error: Current value for USB are not in permissible range\n"); |
| return -1; |
| } |
| |
| if (current == USB_CUR_100UA) |
| mul = 0x0; |
| else if (current == USB_CUR_150UA) |
| mul = 0x1; |
| else |
| mul = current / USB_CUR_100UA; |
| |
| REG_WRITE(IUSB_MAX_REG, mul); |
| |
| return 0; |
| } |
| |
| /* |
| * This function configures the maximum |
| * current for battery in uA |
| */ |
| int pm8x41_ibat_max_config(uint32_t current) |
| { |
| uint32_t mul; |
| |
| if(current < IBAT_MIN_UA || current > IBAT_MAX_UA) { |
| dprintf(CRITICAL, "Error: Current value for BAT are not in permissible range\n"); |
| return -1; |
| } |
| |
| mul = (current - BAT_CUR_100UA) / BAT_CUR_STEP; |
| |
| REG_WRITE(IBAT_MAX_REG, mul); |
| |
| return 0; |
| } |