/* 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 #include #include #include #include #include #include /* * 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 0; } 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; } /* * API: pm8x41_chgr_vdd_max_config * Configure the VDD max to i/p value */ int pm8x41_chgr_vdd_max_config(uint32_t vol) { uint8_t mul; /* Check for permissible range of i/p */ if (vol < VDD_MIN_UA || vol > VDD_MAX_UA) { dprintf(CRITICAL, "Error: Voltage values are not in permissible range\n"); return -1; } /* Calculate the multiplier */ mul = (vol - VDD_MIN_UA) / VDD_VOL_STEP; /* Write to VDD_MAX register */ REG_WRITE(CHGR_VDD_MAX, mul); return 0; } /* * API: pm8x41_chgr_ctl_enable * Enable FSM-controlled autonomous charging */ int pm8x41_chgr_ctl_enable(uint8_t enable) { /* If charging has to be enabled? * 1. Enable charging in charge control * 2. Enable boot done to enable charging */ if (enable) { REG_WRITE(CHGR_CHG_CTRL, (CHGR_ENABLE << CHGR_EN_BIT)); REG_WRITE(MISC_BOOT_DONE, (BOOT_DONE << BOOT_DONE_BIT)); } else REG_WRITE(CHGR_CHG_CTRL, CHGR_DISABLE); return 0; } /* * API: pm8x41_get_batt_voltage * Get calibrated battery voltage from VADC, in UV */ uint32_t pm8x41_get_batt_voltage() { uint32_t voltage; voltage = pm8x41_adc_channel_read(VADC_BAT_VOL_CHAN_ID); if(!voltage) { dprintf(CRITICAL, "Error getting battery Voltage\n"); return 0; } return voltage; } /* * API: pm8x41_get_voltage_based_soc * Get voltage based State Of Charge, this takes vdd max & battery cutoff * voltage as i/p in uV */ uint32_t pm8x41_get_voltage_based_soc(uint32_t cutoff_vol, uint32_t vdd_max) { uint32_t vol_soc; uint32_t batt_vol; batt_vol = pm8x41_get_batt_voltage(); if(!batt_vol) { dprintf(CRITICAL, "Error: Getting battery voltage based Soc\n"); return 0; } if (cutoff_vol >= vdd_max) { dprintf(CRITICAL, "Cutoff is greater than VDD max, Voltage based soc can't be calculated\n"); return 0; } vol_soc = ((batt_vol - cutoff_vol) * 100) / (vdd_max - cutoff_vol); return vol_soc; }