111cb8da0SChris Morgan // SPDX-License-Identifier: GPL-2.0+ 211cb8da0SChris Morgan /* 311cb8da0SChris Morgan * Charger Driver for Rockchip rk817 411cb8da0SChris Morgan * 511cb8da0SChris Morgan * Copyright (c) 2021 Maya Matuszczyk <maccraft123mc@gmail.com> 611cb8da0SChris Morgan * 711cb8da0SChris Morgan * Authors: Maya Matuszczyk <maccraft123mc@gmail.com> 811cb8da0SChris Morgan * Chris Morgan <macromorgan@hotmail.com> 911cb8da0SChris Morgan */ 1011cb8da0SChris Morgan 1111cb8da0SChris Morgan #include <asm/unaligned.h> 1211cb8da0SChris Morgan #include <linux/devm-helpers.h> 1311cb8da0SChris Morgan #include <linux/mfd/rk808.h> 1411cb8da0SChris Morgan #include <linux/irq.h> 1511cb8da0SChris Morgan #include <linux/of.h> 1611cb8da0SChris Morgan #include <linux/platform_device.h> 1711cb8da0SChris Morgan #include <linux/power_supply.h> 1811cb8da0SChris Morgan #include <linux/regmap.h> 1911cb8da0SChris Morgan 2011cb8da0SChris Morgan /* Charging statuses reported by hardware register */ 2111cb8da0SChris Morgan enum rk817_charge_status { 2211cb8da0SChris Morgan CHRG_OFF, 2311cb8da0SChris Morgan DEAD_CHRG, 2411cb8da0SChris Morgan TRICKLE_CHRG, 2511cb8da0SChris Morgan CC_OR_CV_CHRG, 2611cb8da0SChris Morgan CHARGE_FINISH, 2711cb8da0SChris Morgan USB_OVER_VOL, 2811cb8da0SChris Morgan BAT_TMP_ERR, 2911cb8da0SChris Morgan BAT_TIM_ERR, 3011cb8da0SChris Morgan }; 3111cb8da0SChris Morgan 3211cb8da0SChris Morgan /* 3311cb8da0SChris Morgan * Max charging current read to/written from hardware register. 3411cb8da0SChris Morgan * Note how highest value corresponding to 0x7 is the lowest 3511cb8da0SChris Morgan * current, this is per the datasheet. 3611cb8da0SChris Morgan */ 3711cb8da0SChris Morgan enum rk817_chg_cur { 3811cb8da0SChris Morgan CHG_1A, 3911cb8da0SChris Morgan CHG_1_5A, 4011cb8da0SChris Morgan CHG_2A, 4111cb8da0SChris Morgan CHG_2_5A, 4211cb8da0SChris Morgan CHG_2_75A, 4311cb8da0SChris Morgan CHG_3A, 4411cb8da0SChris Morgan CHG_3_5A, 4511cb8da0SChris Morgan CHG_0_5A, 4611cb8da0SChris Morgan }; 4711cb8da0SChris Morgan 4811cb8da0SChris Morgan struct rk817_charger { 4911cb8da0SChris Morgan struct device *dev; 5011cb8da0SChris Morgan struct rk808 *rk808; 5111cb8da0SChris Morgan 5211cb8da0SChris Morgan struct power_supply *bat_ps; 5311cb8da0SChris Morgan struct power_supply *chg_ps; 5411cb8da0SChris Morgan bool plugged_in; 5511cb8da0SChris Morgan bool battery_present; 5611cb8da0SChris Morgan 5711cb8da0SChris Morgan /* 5811cb8da0SChris Morgan * voltage_k and voltage_b values are used to calibrate the ADC 5911cb8da0SChris Morgan * voltage readings. While they are documented in the BSP kernel and 6011cb8da0SChris Morgan * datasheet as voltage_k and voltage_b, there is no further 6111cb8da0SChris Morgan * information explaining them in more detail. 6211cb8da0SChris Morgan */ 6311cb8da0SChris Morgan 6411cb8da0SChris Morgan uint32_t voltage_k; 6511cb8da0SChris Morgan uint32_t voltage_b; 6611cb8da0SChris Morgan 6711cb8da0SChris Morgan /* 6811cb8da0SChris Morgan * soc - state of charge - like the BSP this is stored as a percentage, 6911cb8da0SChris Morgan * to the thousandth. BSP has a display state of charge (dsoc) and a 7011cb8da0SChris Morgan * remaining state of charge (rsoc). This value will be used for both 7111cb8da0SChris Morgan * purposes here so we don't do any fancy math to try and "smooth" the 7211cb8da0SChris Morgan * charge and just report it as it is. Note for example an soc of 100 7311cb8da0SChris Morgan * is stored as 100000, an soc of 50 is stored as 50000, etc. 7411cb8da0SChris Morgan */ 7511cb8da0SChris Morgan int soc; 7611cb8da0SChris Morgan 7711cb8da0SChris Morgan /* 7811cb8da0SChris Morgan * Capacity of battery when fully charged, equal or less than design 7911cb8da0SChris Morgan * capacity depending upon wear. BSP kernel saves to nvram in mAh, 8011cb8da0SChris Morgan * so this value is in mAh not the standard uAh. 8111cb8da0SChris Morgan */ 8211cb8da0SChris Morgan int fcc_mah; 8311cb8da0SChris Morgan 8411cb8da0SChris Morgan /* 8511cb8da0SChris Morgan * Calibrate the SOC on a fully charged battery, this way we can use 8611cb8da0SChris Morgan * the calibrated SOC value to correct for columb counter drift. 8711cb8da0SChris Morgan */ 8811cb8da0SChris Morgan bool soc_cal; 8911cb8da0SChris Morgan 9011cb8da0SChris Morgan /* Implementation specific immutable properties from device tree */ 9111cb8da0SChris Morgan int res_div; 9211cb8da0SChris Morgan int sleep_enter_current_ua; 9311cb8da0SChris Morgan int sleep_filter_current_ua; 9411cb8da0SChris Morgan int bat_charge_full_design_uah; 9511cb8da0SChris Morgan int bat_voltage_min_design_uv; 9611cb8da0SChris Morgan int bat_voltage_max_design_uv; 9711cb8da0SChris Morgan 9811cb8da0SChris Morgan /* Values updated periodically by driver for display. */ 9911cb8da0SChris Morgan int charge_now_uah; 10011cb8da0SChris Morgan int volt_avg_uv; 10111cb8da0SChris Morgan int cur_avg_ua; 10211cb8da0SChris Morgan int max_chg_cur_ua; 10311cb8da0SChris Morgan int max_chg_volt_uv; 10411cb8da0SChris Morgan int charge_status; 10511cb8da0SChris Morgan int charger_input_volt_avg_uv; 10611cb8da0SChris Morgan 10711cb8da0SChris Morgan /* Work queue to periodically update values. */ 10811cb8da0SChris Morgan struct delayed_work work; 10911cb8da0SChris Morgan }; 11011cb8da0SChris Morgan 11111cb8da0SChris Morgan /* ADC coefficients extracted from BSP kernel */ 11211cb8da0SChris Morgan #define ADC_TO_CURRENT(adc_value, res_div) \ 11311cb8da0SChris Morgan (adc_value * 172 / res_div) 11411cb8da0SChris Morgan 11511cb8da0SChris Morgan #define CURRENT_TO_ADC(current, samp_res) \ 11611cb8da0SChris Morgan (current * samp_res / 172) 11711cb8da0SChris Morgan 11811cb8da0SChris Morgan #define CHARGE_TO_ADC(capacity, res_div) \ 11911cb8da0SChris Morgan (capacity * res_div * 3600 / 172 * 1000) 12011cb8da0SChris Morgan 12111cb8da0SChris Morgan #define ADC_TO_CHARGE_UAH(adc_value, res_div) \ 12211cb8da0SChris Morgan (adc_value / 3600 * 172 / res_div) 12311cb8da0SChris Morgan 124883babd4SChris Morgan static int rk817_chg_cur_to_reg(u32 chg_cur_ma) 12511cb8da0SChris Morgan { 12611cb8da0SChris Morgan if (chg_cur_ma >= 3500) 12711cb8da0SChris Morgan return CHG_3_5A; 12811cb8da0SChris Morgan else if (chg_cur_ma >= 3000) 12911cb8da0SChris Morgan return CHG_3A; 13011cb8da0SChris Morgan else if (chg_cur_ma >= 2750) 13111cb8da0SChris Morgan return CHG_2_75A; 13211cb8da0SChris Morgan else if (chg_cur_ma >= 2500) 13311cb8da0SChris Morgan return CHG_2_5A; 13411cb8da0SChris Morgan else if (chg_cur_ma >= 2000) 13511cb8da0SChris Morgan return CHG_2A; 13611cb8da0SChris Morgan else if (chg_cur_ma >= 1500) 13711cb8da0SChris Morgan return CHG_1_5A; 13811cb8da0SChris Morgan else if (chg_cur_ma >= 1000) 13911cb8da0SChris Morgan return CHG_1A; 14011cb8da0SChris Morgan else if (chg_cur_ma >= 500) 14111cb8da0SChris Morgan return CHG_0_5A; 14211cb8da0SChris Morgan else 14311cb8da0SChris Morgan return -EINVAL; 14411cb8da0SChris Morgan } 14511cb8da0SChris Morgan 14611cb8da0SChris Morgan static int rk817_chg_cur_from_reg(u8 reg) 14711cb8da0SChris Morgan { 14811cb8da0SChris Morgan switch (reg) { 14911cb8da0SChris Morgan case CHG_0_5A: 15011cb8da0SChris Morgan return 500000; 15111cb8da0SChris Morgan case CHG_1A: 15211cb8da0SChris Morgan return 1000000; 15311cb8da0SChris Morgan case CHG_1_5A: 15411cb8da0SChris Morgan return 1500000; 15511cb8da0SChris Morgan case CHG_2A: 15611cb8da0SChris Morgan return 2000000; 15711cb8da0SChris Morgan case CHG_2_5A: 15811cb8da0SChris Morgan return 2500000; 15911cb8da0SChris Morgan case CHG_2_75A: 16011cb8da0SChris Morgan return 2750000; 16111cb8da0SChris Morgan case CHG_3A: 16211cb8da0SChris Morgan return 3000000; 16311cb8da0SChris Morgan case CHG_3_5A: 16411cb8da0SChris Morgan return 3500000; 16511cb8da0SChris Morgan default: 16611cb8da0SChris Morgan return -EINVAL; 16711cb8da0SChris Morgan } 16811cb8da0SChris Morgan } 16911cb8da0SChris Morgan 17011cb8da0SChris Morgan static void rk817_bat_calib_vol(struct rk817_charger *charger) 17111cb8da0SChris Morgan { 17211cb8da0SChris Morgan uint32_t vcalib0 = 0; 17311cb8da0SChris Morgan uint32_t vcalib1 = 0; 17411cb8da0SChris Morgan u8 bulk_reg[2]; 17511cb8da0SChris Morgan 17611cb8da0SChris Morgan /* calibrate voltage */ 17711cb8da0SChris Morgan regmap_bulk_read(charger->rk808->regmap, RK817_GAS_GAUGE_VCALIB0_H, 17811cb8da0SChris Morgan bulk_reg, 2); 17911cb8da0SChris Morgan vcalib0 = get_unaligned_be16(bulk_reg); 18011cb8da0SChris Morgan 18111cb8da0SChris Morgan regmap_bulk_read(charger->rk808->regmap, RK817_GAS_GAUGE_VCALIB1_H, 18211cb8da0SChris Morgan bulk_reg, 2); 18311cb8da0SChris Morgan vcalib1 = get_unaligned_be16(bulk_reg); 18411cb8da0SChris Morgan 18511cb8da0SChris Morgan /* values were taken from BSP kernel */ 18611cb8da0SChris Morgan charger->voltage_k = (4025 - 2300) * 1000 / 18711cb8da0SChris Morgan ((vcalib1 - vcalib0) ? (vcalib1 - vcalib0) : 1); 18811cb8da0SChris Morgan charger->voltage_b = 4025 - (charger->voltage_k * vcalib1) / 1000; 18911cb8da0SChris Morgan } 19011cb8da0SChris Morgan 19111cb8da0SChris Morgan static void rk817_bat_calib_cur(struct rk817_charger *charger) 19211cb8da0SChris Morgan { 19311cb8da0SChris Morgan u8 bulk_reg[2]; 19411cb8da0SChris Morgan 19511cb8da0SChris Morgan /* calibrate current */ 19611cb8da0SChris Morgan regmap_bulk_read(charger->rk808->regmap, RK817_GAS_GAUGE_IOFFSET_H, 19711cb8da0SChris Morgan bulk_reg, 2); 19811cb8da0SChris Morgan regmap_bulk_write(charger->rk808->regmap, RK817_GAS_GAUGE_CAL_OFFSET_H, 19911cb8da0SChris Morgan bulk_reg, 2); 20011cb8da0SChris Morgan } 20111cb8da0SChris Morgan 20211cb8da0SChris Morgan /* 20311cb8da0SChris Morgan * note that only the fcc_mah is really used by this driver, the other values 20411cb8da0SChris Morgan * are to ensure we can remain backwards compatible with the BSP kernel. 20511cb8da0SChris Morgan */ 20611cb8da0SChris Morgan static int rk817_record_battery_nvram_values(struct rk817_charger *charger) 20711cb8da0SChris Morgan { 20811cb8da0SChris Morgan u8 bulk_reg[3]; 20911cb8da0SChris Morgan int ret, rsoc; 21011cb8da0SChris Morgan 21111cb8da0SChris Morgan /* 21211cb8da0SChris Morgan * write the soc value to the nvram location used by the BSP kernel 21311cb8da0SChris Morgan * for the dsoc value. 21411cb8da0SChris Morgan */ 21511cb8da0SChris Morgan put_unaligned_le24(charger->soc, bulk_reg); 21611cb8da0SChris Morgan ret = regmap_bulk_write(charger->rk808->regmap, RK817_GAS_GAUGE_BAT_R1, 21711cb8da0SChris Morgan bulk_reg, 3); 21811cb8da0SChris Morgan if (ret < 0) 21911cb8da0SChris Morgan return ret; 22011cb8da0SChris Morgan /* 22111cb8da0SChris Morgan * write the remaining capacity in mah to the nvram location used by 22211cb8da0SChris Morgan * the BSP kernel for the rsoc value. 22311cb8da0SChris Morgan */ 22411cb8da0SChris Morgan rsoc = (charger->soc * charger->fcc_mah) / 100000; 22511cb8da0SChris Morgan put_unaligned_le24(rsoc, bulk_reg); 22611cb8da0SChris Morgan ret = regmap_bulk_write(charger->rk808->regmap, RK817_GAS_GAUGE_DATA0, 22711cb8da0SChris Morgan bulk_reg, 3); 22811cb8da0SChris Morgan if (ret < 0) 22911cb8da0SChris Morgan return ret; 23011cb8da0SChris Morgan /* write the fcc_mah in mAh, just as the BSP kernel does. */ 23111cb8da0SChris Morgan put_unaligned_le24(charger->fcc_mah, bulk_reg); 23211cb8da0SChris Morgan ret = regmap_bulk_write(charger->rk808->regmap, RK817_GAS_GAUGE_DATA3, 23311cb8da0SChris Morgan bulk_reg, 3); 23411cb8da0SChris Morgan if (ret < 0) 23511cb8da0SChris Morgan return ret; 23611cb8da0SChris Morgan 23711cb8da0SChris Morgan return 0; 23811cb8da0SChris Morgan } 23911cb8da0SChris Morgan 24011cb8da0SChris Morgan static int rk817_bat_calib_cap(struct rk817_charger *charger) 24111cb8da0SChris Morgan { 24211cb8da0SChris Morgan struct rk808 *rk808 = charger->rk808; 24311cb8da0SChris Morgan int tmp, charge_now, charge_now_adc, volt_avg; 24411cb8da0SChris Morgan u8 bulk_reg[4]; 24511cb8da0SChris Morgan 24611cb8da0SChris Morgan /* Calibrate the soc and fcc on a fully charged battery */ 24711cb8da0SChris Morgan 24811cb8da0SChris Morgan if (charger->charge_status == CHARGE_FINISH && (!charger->soc_cal)) { 24911cb8da0SChris Morgan /* 25011cb8da0SChris Morgan * soc should be 100000 and columb counter should show the full 25111cb8da0SChris Morgan * charge capacity. Note that if the device is unplugged for a 25211cb8da0SChris Morgan * period of several days the columb counter will have a large 25311cb8da0SChris Morgan * margin of error, so setting it back to the full charge on 25411cb8da0SChris Morgan * a completed charge cycle should correct this (my device was 25511cb8da0SChris Morgan * showing 33% battery after 3 days unplugged when it should 25611cb8da0SChris Morgan * have been closer to 95% based on voltage and charge 25711cb8da0SChris Morgan * current). 25811cb8da0SChris Morgan */ 25911cb8da0SChris Morgan 26011cb8da0SChris Morgan charger->soc = 100000; 26111cb8da0SChris Morgan charge_now_adc = CHARGE_TO_ADC(charger->fcc_mah, 26211cb8da0SChris Morgan charger->res_div); 26311cb8da0SChris Morgan put_unaligned_be32(charge_now_adc, bulk_reg); 26411cb8da0SChris Morgan regmap_bulk_write(rk808->regmap, RK817_GAS_GAUGE_Q_INIT_H3, 26511cb8da0SChris Morgan bulk_reg, 4); 26611cb8da0SChris Morgan 26711cb8da0SChris Morgan charger->soc_cal = 1; 26811cb8da0SChris Morgan dev_dbg(charger->dev, 26911cb8da0SChris Morgan "Fully charged. SOC is %d, full capacity is %d\n", 27011cb8da0SChris Morgan charger->soc, charger->fcc_mah * 1000); 27111cb8da0SChris Morgan } 27211cb8da0SChris Morgan 27311cb8da0SChris Morgan /* 27411cb8da0SChris Morgan * The columb counter can drift up slightly, so we should correct for 27511cb8da0SChris Morgan * it. But don't correct it until we're at 100% soc. 27611cb8da0SChris Morgan */ 27711cb8da0SChris Morgan if (charger->charge_status == CHARGE_FINISH && charger->soc_cal) { 27811cb8da0SChris Morgan regmap_bulk_read(rk808->regmap, RK817_GAS_GAUGE_Q_PRES_H3, 27911cb8da0SChris Morgan bulk_reg, 4); 28011cb8da0SChris Morgan charge_now_adc = get_unaligned_be32(bulk_reg); 28111cb8da0SChris Morgan if (charge_now_adc < 0) 28211cb8da0SChris Morgan return charge_now_adc; 28311cb8da0SChris Morgan charge_now = ADC_TO_CHARGE_UAH(charge_now_adc, 28411cb8da0SChris Morgan charger->res_div); 28511cb8da0SChris Morgan 28611cb8da0SChris Morgan /* 28711cb8da0SChris Morgan * Re-init columb counter with updated values to correct drift. 28811cb8da0SChris Morgan */ 28911cb8da0SChris Morgan if (charge_now / 1000 > charger->fcc_mah) { 29011cb8da0SChris Morgan dev_dbg(charger->dev, 29111cb8da0SChris Morgan "Recalibrating columb counter to %d uah\n", 29211cb8da0SChris Morgan charge_now); 29311cb8da0SChris Morgan /* 29411cb8da0SChris Morgan * Order of operations matters here to ensure we keep 29511cb8da0SChris Morgan * enough precision until the last step to keep from 29611cb8da0SChris Morgan * making needless updates to columb counter. 29711cb8da0SChris Morgan */ 29811cb8da0SChris Morgan charge_now_adc = CHARGE_TO_ADC(charger->fcc_mah, 29911cb8da0SChris Morgan charger->res_div); 30011cb8da0SChris Morgan put_unaligned_be32(charge_now_adc, bulk_reg); 30111cb8da0SChris Morgan regmap_bulk_write(rk808->regmap, 30211cb8da0SChris Morgan RK817_GAS_GAUGE_Q_INIT_H3, 30311cb8da0SChris Morgan bulk_reg, 4); 30411cb8da0SChris Morgan } 30511cb8da0SChris Morgan } 30611cb8da0SChris Morgan 30711cb8da0SChris Morgan /* 30811cb8da0SChris Morgan * Calibrate the fully charged capacity when we previously had a full 30911cb8da0SChris Morgan * battery (soc_cal = 1) and are now empty (at or below minimum design 31011cb8da0SChris Morgan * voltage). If our columb counter is still positive, subtract that 31111cb8da0SChris Morgan * from our fcc value to get a calibrated fcc, and if our columb 31211cb8da0SChris Morgan * counter is negative add that to our fcc (but not to exceed our 31311cb8da0SChris Morgan * design capacity). 31411cb8da0SChris Morgan */ 31511cb8da0SChris Morgan regmap_bulk_read(charger->rk808->regmap, RK817_GAS_GAUGE_BAT_VOL_H, 31611cb8da0SChris Morgan bulk_reg, 2); 31711cb8da0SChris Morgan tmp = get_unaligned_be16(bulk_reg); 31811cb8da0SChris Morgan volt_avg = (charger->voltage_k * tmp) + 1000 * charger->voltage_b; 31911cb8da0SChris Morgan if (volt_avg <= charger->bat_voltage_min_design_uv && 32011cb8da0SChris Morgan charger->soc_cal) { 32111cb8da0SChris Morgan regmap_bulk_read(rk808->regmap, RK817_GAS_GAUGE_Q_PRES_H3, 32211cb8da0SChris Morgan bulk_reg, 4); 32311cb8da0SChris Morgan charge_now_adc = get_unaligned_be32(bulk_reg); 32411cb8da0SChris Morgan charge_now = ADC_TO_CHARGE_UAH(charge_now_adc, 32511cb8da0SChris Morgan charger->res_div); 32611cb8da0SChris Morgan /* 32711cb8da0SChris Morgan * Note, if charge_now is negative this will add it (what we 32811cb8da0SChris Morgan * want) and if it's positive this will subtract (also what 32911cb8da0SChris Morgan * we want). 33011cb8da0SChris Morgan */ 33111cb8da0SChris Morgan charger->fcc_mah = charger->fcc_mah - (charge_now / 1000); 33211cb8da0SChris Morgan 33311cb8da0SChris Morgan dev_dbg(charger->dev, 33411cb8da0SChris Morgan "Recalibrating full charge capacity to %d uah\n", 33511cb8da0SChris Morgan charger->fcc_mah * 1000); 33611cb8da0SChris Morgan } 33711cb8da0SChris Morgan 338baba1315SChris Morgan /* 339baba1315SChris Morgan * Set the SOC to 0 if we are below the minimum system voltage. 340baba1315SChris Morgan */ 341baba1315SChris Morgan if (volt_avg <= charger->bat_voltage_min_design_uv) { 342baba1315SChris Morgan charger->soc = 0; 343baba1315SChris Morgan charge_now_adc = CHARGE_TO_ADC(0, charger->res_div); 344baba1315SChris Morgan put_unaligned_be32(charge_now_adc, bulk_reg); 345baba1315SChris Morgan regmap_bulk_write(rk808->regmap, 346baba1315SChris Morgan RK817_GAS_GAUGE_Q_INIT_H3, bulk_reg, 4); 347baba1315SChris Morgan dev_warn(charger->dev, 348baba1315SChris Morgan "Battery voltage %d below minimum voltage %d\n", 349baba1315SChris Morgan volt_avg, charger->bat_voltage_min_design_uv); 350baba1315SChris Morgan } 351baba1315SChris Morgan 35211cb8da0SChris Morgan rk817_record_battery_nvram_values(charger); 35311cb8da0SChris Morgan 35411cb8da0SChris Morgan return 0; 35511cb8da0SChris Morgan } 35611cb8da0SChris Morgan 35711cb8da0SChris Morgan static void rk817_read_props(struct rk817_charger *charger) 35811cb8da0SChris Morgan { 35911cb8da0SChris Morgan int tmp, reg; 36011cb8da0SChris Morgan u8 bulk_reg[4]; 36111cb8da0SChris Morgan 36211cb8da0SChris Morgan /* 36311cb8da0SChris Morgan * Recalibrate voltage and current readings if we need to BSP does both 36411cb8da0SChris Morgan * on CUR_CALIB_UPD, ignoring VOL_CALIB_UPD. Curiously enough, both 36511cb8da0SChris Morgan * documentation and the BSP show that you perform an update if bit 7 36611cb8da0SChris Morgan * is 1, but you clear the status by writing a 1 to bit 7. 36711cb8da0SChris Morgan */ 36811cb8da0SChris Morgan regmap_read(charger->rk808->regmap, RK817_GAS_GAUGE_ADC_CONFIG1, ®); 36911cb8da0SChris Morgan if (reg & RK817_VOL_CUR_CALIB_UPD) { 37011cb8da0SChris Morgan rk817_bat_calib_cur(charger); 37111cb8da0SChris Morgan rk817_bat_calib_vol(charger); 37211cb8da0SChris Morgan regmap_write_bits(charger->rk808->regmap, 37311cb8da0SChris Morgan RK817_GAS_GAUGE_ADC_CONFIG1, 37411cb8da0SChris Morgan RK817_VOL_CUR_CALIB_UPD, 37511cb8da0SChris Morgan RK817_VOL_CUR_CALIB_UPD); 37611cb8da0SChris Morgan } 37711cb8da0SChris Morgan 37811cb8da0SChris Morgan /* Update reported charge. */ 37911cb8da0SChris Morgan regmap_bulk_read(charger->rk808->regmap, RK817_GAS_GAUGE_Q_PRES_H3, 38011cb8da0SChris Morgan bulk_reg, 4); 38111cb8da0SChris Morgan tmp = get_unaligned_be32(bulk_reg); 38211cb8da0SChris Morgan charger->charge_now_uah = ADC_TO_CHARGE_UAH(tmp, charger->res_div); 38311cb8da0SChris Morgan if (charger->charge_now_uah < 0) 38411cb8da0SChris Morgan charger->charge_now_uah = 0; 38511cb8da0SChris Morgan if (charger->charge_now_uah > charger->fcc_mah * 1000) 38611cb8da0SChris Morgan charger->charge_now_uah = charger->fcc_mah * 1000; 38711cb8da0SChris Morgan 38811cb8da0SChris Morgan /* Update soc based on reported charge. */ 38911cb8da0SChris Morgan charger->soc = charger->charge_now_uah * 100 / charger->fcc_mah; 39011cb8da0SChris Morgan 39111cb8da0SChris Morgan /* Update reported voltage. */ 39211cb8da0SChris Morgan regmap_bulk_read(charger->rk808->regmap, RK817_GAS_GAUGE_BAT_VOL_H, 39311cb8da0SChris Morgan bulk_reg, 2); 39411cb8da0SChris Morgan tmp = get_unaligned_be16(bulk_reg); 39511cb8da0SChris Morgan charger->volt_avg_uv = (charger->voltage_k * tmp) + 1000 * 39611cb8da0SChris Morgan charger->voltage_b; 39711cb8da0SChris Morgan 39811cb8da0SChris Morgan /* 39911cb8da0SChris Morgan * Update reported current. Note value from registers is a signed 16 40011cb8da0SChris Morgan * bit int. 40111cb8da0SChris Morgan */ 40211cb8da0SChris Morgan regmap_bulk_read(charger->rk808->regmap, RK817_GAS_GAUGE_BAT_CUR_H, 40311cb8da0SChris Morgan bulk_reg, 2); 40411cb8da0SChris Morgan tmp = (short int)get_unaligned_be16(bulk_reg); 40511cb8da0SChris Morgan charger->cur_avg_ua = ADC_TO_CURRENT(tmp, charger->res_div); 40611cb8da0SChris Morgan 40711cb8da0SChris Morgan /* 40811cb8da0SChris Morgan * Update the max charge current. This value shouldn't change, but we 40911cb8da0SChris Morgan * can read it to report what the PMIC says it is instead of simply 41011cb8da0SChris Morgan * returning the default value. 41111cb8da0SChris Morgan */ 41211cb8da0SChris Morgan regmap_read(charger->rk808->regmap, RK817_PMIC_CHRG_OUT, ®); 41311cb8da0SChris Morgan charger->max_chg_cur_ua = 41411cb8da0SChris Morgan rk817_chg_cur_from_reg(reg & RK817_CHRG_CUR_SEL); 41511cb8da0SChris Morgan 41611cb8da0SChris Morgan /* 41711cb8da0SChris Morgan * Update max charge voltage. Like the max charge current this value 41811cb8da0SChris Morgan * shouldn't change, but we can report what the PMIC says. 41911cb8da0SChris Morgan */ 42011cb8da0SChris Morgan regmap_read(charger->rk808->regmap, RK817_PMIC_CHRG_OUT, ®); 42111cb8da0SChris Morgan charger->max_chg_volt_uv = ((((reg & RK817_CHRG_VOL_SEL) >> 4) * 42211cb8da0SChris Morgan 50000) + 4100000); 42311cb8da0SChris Morgan 42411cb8da0SChris Morgan /* Check if battery still present. */ 42511cb8da0SChris Morgan regmap_read(charger->rk808->regmap, RK817_PMIC_CHRG_STS, ®); 42611cb8da0SChris Morgan charger->battery_present = (reg & RK817_BAT_EXS); 42711cb8da0SChris Morgan 42811cb8da0SChris Morgan /* Get which type of charge we are using (if any). */ 42911cb8da0SChris Morgan regmap_read(charger->rk808->regmap, RK817_PMIC_CHRG_STS, ®); 43011cb8da0SChris Morgan charger->charge_status = (reg >> 4) & 0x07; 43111cb8da0SChris Morgan 43211cb8da0SChris Morgan /* 43311cb8da0SChris Morgan * Get charger input voltage. Note that on my example hardware (an 43411cb8da0SChris Morgan * Odroid Go Advance) the voltage of the power connector is measured 43511cb8da0SChris Morgan * on the register labelled USB in the datasheet; I don't know if this 43611cb8da0SChris Morgan * is how it is designed or just a quirk of the implementation. I 43711cb8da0SChris Morgan * believe this will also measure the voltage of the USB output when in 43811cb8da0SChris Morgan * OTG mode, if that is the case we may need to change this in the 43911cb8da0SChris Morgan * future to return 0 if the power supply status is offline (I can't 44011cb8da0SChris Morgan * test this with my current implementation. Also, when the voltage 44111cb8da0SChris Morgan * should be zero sometimes the ADC still shows a single bit (which 44211cb8da0SChris Morgan * would register as 20000uv). When this happens set it to 0. 44311cb8da0SChris Morgan */ 44411cb8da0SChris Morgan regmap_bulk_read(charger->rk808->regmap, RK817_GAS_GAUGE_USB_VOL_H, 44511cb8da0SChris Morgan bulk_reg, 2); 44611cb8da0SChris Morgan reg = get_unaligned_be16(bulk_reg); 44711cb8da0SChris Morgan if (reg > 1) { 44811cb8da0SChris Morgan tmp = ((charger->voltage_k * reg / 1000 + charger->voltage_b) * 44911cb8da0SChris Morgan 60 / 46); 45011cb8da0SChris Morgan charger->charger_input_volt_avg_uv = tmp * 1000; 45111cb8da0SChris Morgan } else { 45211cb8da0SChris Morgan charger->charger_input_volt_avg_uv = 0; 45311cb8da0SChris Morgan } 45411cb8da0SChris Morgan 45511cb8da0SChris Morgan /* Calibrate battery capacity and soc. */ 45611cb8da0SChris Morgan rk817_bat_calib_cap(charger); 45711cb8da0SChris Morgan } 45811cb8da0SChris Morgan 45911cb8da0SChris Morgan static int rk817_bat_get_prop(struct power_supply *ps, 46011cb8da0SChris Morgan enum power_supply_property prop, 46111cb8da0SChris Morgan union power_supply_propval *val) 46211cb8da0SChris Morgan { 46311cb8da0SChris Morgan struct rk817_charger *charger = power_supply_get_drvdata(ps); 46411cb8da0SChris Morgan 46511cb8da0SChris Morgan switch (prop) { 46611cb8da0SChris Morgan case POWER_SUPPLY_PROP_PRESENT: 46711cb8da0SChris Morgan val->intval = charger->battery_present; 46811cb8da0SChris Morgan break; 46911cb8da0SChris Morgan case POWER_SUPPLY_PROP_STATUS: 47011cb8da0SChris Morgan if (charger->cur_avg_ua < 0) { 47111cb8da0SChris Morgan val->intval = POWER_SUPPLY_STATUS_DISCHARGING; 47211cb8da0SChris Morgan break; 47311cb8da0SChris Morgan } 47411cb8da0SChris Morgan switch (charger->charge_status) { 47511cb8da0SChris Morgan case CHRG_OFF: 47611cb8da0SChris Morgan val->intval = POWER_SUPPLY_STATUS_NOT_CHARGING; 47711cb8da0SChris Morgan break; 47811cb8da0SChris Morgan /* 47911cb8da0SChris Morgan * Dead charge is documented, but not explained. I never 48011cb8da0SChris Morgan * observed it but assume it's a pre-charge for a dead 48111cb8da0SChris Morgan * battery. 48211cb8da0SChris Morgan */ 48311cb8da0SChris Morgan case DEAD_CHRG: 48411cb8da0SChris Morgan case TRICKLE_CHRG: 48511cb8da0SChris Morgan case CC_OR_CV_CHRG: 48611cb8da0SChris Morgan val->intval = POWER_SUPPLY_STATUS_CHARGING; 48711cb8da0SChris Morgan break; 48811cb8da0SChris Morgan case CHARGE_FINISH: 48911cb8da0SChris Morgan val->intval = POWER_SUPPLY_STATUS_FULL; 49011cb8da0SChris Morgan break; 49111cb8da0SChris Morgan default: 49211cb8da0SChris Morgan val->intval = POWER_SUPPLY_STATUS_UNKNOWN; 49311cb8da0SChris Morgan return -EINVAL; 49411cb8da0SChris Morgan 49511cb8da0SChris Morgan } 49611cb8da0SChris Morgan break; 49711cb8da0SChris Morgan case POWER_SUPPLY_PROP_CHARGE_TYPE: 49811cb8da0SChris Morgan switch (charger->charge_status) { 49911cb8da0SChris Morgan case CHRG_OFF: 50011cb8da0SChris Morgan case CHARGE_FINISH: 50111cb8da0SChris Morgan val->intval = POWER_SUPPLY_CHARGE_TYPE_NONE; 50211cb8da0SChris Morgan break; 50311cb8da0SChris Morgan case TRICKLE_CHRG: 50411cb8da0SChris Morgan val->intval = POWER_SUPPLY_CHARGE_TYPE_TRICKLE; 50511cb8da0SChris Morgan break; 50611cb8da0SChris Morgan case DEAD_CHRG: 50711cb8da0SChris Morgan case CC_OR_CV_CHRG: 50811cb8da0SChris Morgan val->intval = POWER_SUPPLY_CHARGE_TYPE_STANDARD; 50911cb8da0SChris Morgan break; 51011cb8da0SChris Morgan default: 51111cb8da0SChris Morgan val->intval = POWER_SUPPLY_CHARGE_TYPE_UNKNOWN; 51211cb8da0SChris Morgan break; 51311cb8da0SChris Morgan } 51411cb8da0SChris Morgan break; 51511cb8da0SChris Morgan case POWER_SUPPLY_PROP_CHARGE_FULL: 51611cb8da0SChris Morgan val->intval = charger->fcc_mah * 1000; 51711cb8da0SChris Morgan break; 51811cb8da0SChris Morgan case POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN: 51911cb8da0SChris Morgan val->intval = charger->bat_charge_full_design_uah; 52011cb8da0SChris Morgan break; 52111cb8da0SChris Morgan case POWER_SUPPLY_PROP_CHARGE_EMPTY_DESIGN: 52211cb8da0SChris Morgan val->intval = 0; 52311cb8da0SChris Morgan break; 52411cb8da0SChris Morgan case POWER_SUPPLY_PROP_CHARGE_NOW: 52511cb8da0SChris Morgan val->intval = charger->charge_now_uah; 52611cb8da0SChris Morgan break; 52711cb8da0SChris Morgan case POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN: 52811cb8da0SChris Morgan val->intval = charger->bat_voltage_min_design_uv; 52911cb8da0SChris Morgan break; 53011cb8da0SChris Morgan case POWER_SUPPLY_PROP_CAPACITY: 53111cb8da0SChris Morgan /* Add 500 so that values like 99999 are 100% not 99%. */ 53211cb8da0SChris Morgan val->intval = (charger->soc + 500) / 1000; 53311cb8da0SChris Morgan if (val->intval > 100) 53411cb8da0SChris Morgan val->intval = 100; 53511cb8da0SChris Morgan if (val->intval < 0) 53611cb8da0SChris Morgan val->intval = 0; 53711cb8da0SChris Morgan break; 53811cb8da0SChris Morgan case POWER_SUPPLY_PROP_VOLTAGE_AVG: 53911cb8da0SChris Morgan val->intval = charger->volt_avg_uv; 54011cb8da0SChris Morgan break; 54111cb8da0SChris Morgan case POWER_SUPPLY_PROP_CURRENT_AVG: 54211cb8da0SChris Morgan val->intval = charger->cur_avg_ua; 54311cb8da0SChris Morgan break; 54411cb8da0SChris Morgan case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX: 54511cb8da0SChris Morgan val->intval = charger->max_chg_cur_ua; 54611cb8da0SChris Morgan break; 54711cb8da0SChris Morgan case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE_MAX: 54811cb8da0SChris Morgan val->intval = charger->max_chg_volt_uv; 54911cb8da0SChris Morgan break; 55011cb8da0SChris Morgan case POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN: 55111cb8da0SChris Morgan val->intval = charger->bat_voltage_max_design_uv; 55211cb8da0SChris Morgan break; 55311cb8da0SChris Morgan default: 55411cb8da0SChris Morgan return -EINVAL; 55511cb8da0SChris Morgan } 55611cb8da0SChris Morgan return 0; 55711cb8da0SChris Morgan } 55811cb8da0SChris Morgan 55911cb8da0SChris Morgan static int rk817_chg_get_prop(struct power_supply *ps, 56011cb8da0SChris Morgan enum power_supply_property prop, 56111cb8da0SChris Morgan union power_supply_propval *val) 56211cb8da0SChris Morgan { 56311cb8da0SChris Morgan struct rk817_charger *charger = power_supply_get_drvdata(ps); 56411cb8da0SChris Morgan 56511cb8da0SChris Morgan switch (prop) { 56611cb8da0SChris Morgan case POWER_SUPPLY_PROP_ONLINE: 56711cb8da0SChris Morgan val->intval = charger->plugged_in; 56811cb8da0SChris Morgan break; 56911cb8da0SChris Morgan case POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN: 57011cb8da0SChris Morgan /* max voltage from datasheet at 5.5v (default 5.0v) */ 57111cb8da0SChris Morgan val->intval = 5500000; 57211cb8da0SChris Morgan break; 57311cb8da0SChris Morgan case POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN: 57411cb8da0SChris Morgan /* min voltage from datasheet at 3.8v (default 5.0v) */ 57511cb8da0SChris Morgan val->intval = 3800000; 57611cb8da0SChris Morgan break; 57711cb8da0SChris Morgan case POWER_SUPPLY_PROP_VOLTAGE_AVG: 57811cb8da0SChris Morgan val->intval = charger->charger_input_volt_avg_uv; 57911cb8da0SChris Morgan break; 58011cb8da0SChris Morgan /* 58111cb8da0SChris Morgan * While it's possible that other implementations could use different 58211cb8da0SChris Morgan * USB types, the current implementation for this PMIC (the Odroid Go 58311cb8da0SChris Morgan * Advance) only uses a dedicated charging port with no rx/tx lines. 58411cb8da0SChris Morgan */ 58511cb8da0SChris Morgan case POWER_SUPPLY_PROP_USB_TYPE: 58611cb8da0SChris Morgan val->intval = POWER_SUPPLY_USB_TYPE_DCP; 58711cb8da0SChris Morgan break; 58811cb8da0SChris Morgan default: 58911cb8da0SChris Morgan return -EINVAL; 59011cb8da0SChris Morgan } 59111cb8da0SChris Morgan return 0; 59211cb8da0SChris Morgan 59311cb8da0SChris Morgan } 59411cb8da0SChris Morgan 59511cb8da0SChris Morgan static irqreturn_t rk817_plug_in_isr(int irq, void *cg) 59611cb8da0SChris Morgan { 59711cb8da0SChris Morgan struct rk817_charger *charger; 59811cb8da0SChris Morgan 59911cb8da0SChris Morgan charger = (struct rk817_charger *)cg; 60011cb8da0SChris Morgan charger->plugged_in = 1; 60111cb8da0SChris Morgan power_supply_changed(charger->chg_ps); 60211cb8da0SChris Morgan power_supply_changed(charger->bat_ps); 60311cb8da0SChris Morgan /* try to recalibrate capacity if we hit full charge. */ 60411cb8da0SChris Morgan charger->soc_cal = 0; 60511cb8da0SChris Morgan 60611cb8da0SChris Morgan rk817_read_props(charger); 60711cb8da0SChris Morgan 60811cb8da0SChris Morgan dev_dbg(charger->dev, "Power Cord Inserted\n"); 60911cb8da0SChris Morgan 61011cb8da0SChris Morgan return IRQ_HANDLED; 61111cb8da0SChris Morgan } 61211cb8da0SChris Morgan 61311cb8da0SChris Morgan static irqreturn_t rk817_plug_out_isr(int irq, void *cg) 61411cb8da0SChris Morgan { 61511cb8da0SChris Morgan struct rk817_charger *charger; 61611cb8da0SChris Morgan struct rk808 *rk808; 61711cb8da0SChris Morgan 61811cb8da0SChris Morgan charger = (struct rk817_charger *)cg; 61911cb8da0SChris Morgan rk808 = charger->rk808; 62011cb8da0SChris Morgan charger->plugged_in = 0; 62111cb8da0SChris Morgan power_supply_changed(charger->bat_ps); 62211cb8da0SChris Morgan power_supply_changed(charger->chg_ps); 62311cb8da0SChris Morgan 62411cb8da0SChris Morgan /* 62511cb8da0SChris Morgan * For some reason the bits of RK817_PMIC_CHRG_IN reset whenever the 62611cb8da0SChris Morgan * power cord is unplugged. This was not documented in the BSP kernel 62711cb8da0SChris Morgan * or the datasheet and only discovered by trial and error. Set minimum 62811cb8da0SChris Morgan * USB input voltage to 4.5v and enable USB voltage input limit. 62911cb8da0SChris Morgan */ 63011cb8da0SChris Morgan regmap_write_bits(rk808->regmap, RK817_PMIC_CHRG_IN, 63111cb8da0SChris Morgan RK817_USB_VLIM_SEL, (0x05 << 4)); 63211cb8da0SChris Morgan regmap_write_bits(rk808->regmap, RK817_PMIC_CHRG_IN, RK817_USB_VLIM_EN, 63311cb8da0SChris Morgan (0x01 << 7)); 63411cb8da0SChris Morgan 63511cb8da0SChris Morgan /* 63611cb8da0SChris Morgan * Set average USB input current limit to 1.5A and enable USB current 63711cb8da0SChris Morgan * input limit. 63811cb8da0SChris Morgan */ 63911cb8da0SChris Morgan regmap_write_bits(rk808->regmap, RK817_PMIC_CHRG_IN, 64011cb8da0SChris Morgan RK817_USB_ILIM_SEL, 0x03); 64111cb8da0SChris Morgan regmap_write_bits(rk808->regmap, RK817_PMIC_CHRG_IN, RK817_USB_ILIM_EN, 64211cb8da0SChris Morgan (0x01 << 3)); 64311cb8da0SChris Morgan 64411cb8da0SChris Morgan rk817_read_props(charger); 64511cb8da0SChris Morgan 64611cb8da0SChris Morgan dev_dbg(charger->dev, "Power Cord Removed\n"); 64711cb8da0SChris Morgan 64811cb8da0SChris Morgan return IRQ_HANDLED; 64911cb8da0SChris Morgan } 65011cb8da0SChris Morgan 65111cb8da0SChris Morgan static enum power_supply_property rk817_bat_props[] = { 65211cb8da0SChris Morgan POWER_SUPPLY_PROP_PRESENT, 65311cb8da0SChris Morgan POWER_SUPPLY_PROP_STATUS, 65411cb8da0SChris Morgan POWER_SUPPLY_PROP_CHARGE_TYPE, 65511cb8da0SChris Morgan POWER_SUPPLY_PROP_CHARGE_FULL, 65611cb8da0SChris Morgan POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN, 65711cb8da0SChris Morgan POWER_SUPPLY_PROP_CHARGE_EMPTY_DESIGN, 65811cb8da0SChris Morgan POWER_SUPPLY_PROP_CHARGE_NOW, 65911cb8da0SChris Morgan POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE_MAX, 66011cb8da0SChris Morgan POWER_SUPPLY_PROP_VOLTAGE_AVG, 66111cb8da0SChris Morgan POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX, 66211cb8da0SChris Morgan POWER_SUPPLY_PROP_CURRENT_AVG, 66311cb8da0SChris Morgan POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN, 66411cb8da0SChris Morgan POWER_SUPPLY_PROP_CAPACITY, 66511cb8da0SChris Morgan POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN, 66611cb8da0SChris Morgan }; 66711cb8da0SChris Morgan 66811cb8da0SChris Morgan static enum power_supply_property rk817_chg_props[] = { 66911cb8da0SChris Morgan POWER_SUPPLY_PROP_ONLINE, 67011cb8da0SChris Morgan POWER_SUPPLY_PROP_USB_TYPE, 67111cb8da0SChris Morgan POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN, 67211cb8da0SChris Morgan POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN, 67311cb8da0SChris Morgan POWER_SUPPLY_PROP_VOLTAGE_AVG, 67411cb8da0SChris Morgan }; 67511cb8da0SChris Morgan 67611cb8da0SChris Morgan static enum power_supply_usb_type rk817_usb_type[] = { 67711cb8da0SChris Morgan POWER_SUPPLY_USB_TYPE_DCP, 67811cb8da0SChris Morgan POWER_SUPPLY_USB_TYPE_UNKNOWN, 67911cb8da0SChris Morgan }; 68011cb8da0SChris Morgan 68111cb8da0SChris Morgan static const struct power_supply_desc rk817_bat_desc = { 68211cb8da0SChris Morgan .name = "rk817-battery", 68311cb8da0SChris Morgan .type = POWER_SUPPLY_TYPE_BATTERY, 68411cb8da0SChris Morgan .properties = rk817_bat_props, 68511cb8da0SChris Morgan .num_properties = ARRAY_SIZE(rk817_bat_props), 68611cb8da0SChris Morgan .get_property = rk817_bat_get_prop, 68711cb8da0SChris Morgan }; 68811cb8da0SChris Morgan 68911cb8da0SChris Morgan static const struct power_supply_desc rk817_chg_desc = { 69011cb8da0SChris Morgan .name = "rk817-charger", 69111cb8da0SChris Morgan .type = POWER_SUPPLY_TYPE_USB, 69211cb8da0SChris Morgan .usb_types = rk817_usb_type, 69311cb8da0SChris Morgan .num_usb_types = ARRAY_SIZE(rk817_usb_type), 69411cb8da0SChris Morgan .properties = rk817_chg_props, 69511cb8da0SChris Morgan .num_properties = ARRAY_SIZE(rk817_chg_props), 69611cb8da0SChris Morgan .get_property = rk817_chg_get_prop, 69711cb8da0SChris Morgan }; 69811cb8da0SChris Morgan 69911cb8da0SChris Morgan static int rk817_read_battery_nvram_values(struct rk817_charger *charger) 70011cb8da0SChris Morgan { 70111cb8da0SChris Morgan u8 bulk_reg[3]; 70211cb8da0SChris Morgan int ret; 70311cb8da0SChris Morgan 70411cb8da0SChris Morgan /* Read the nvram data for full charge capacity. */ 70511cb8da0SChris Morgan ret = regmap_bulk_read(charger->rk808->regmap, 70611cb8da0SChris Morgan RK817_GAS_GAUGE_DATA3, bulk_reg, 3); 70711cb8da0SChris Morgan if (ret < 0) 70811cb8da0SChris Morgan return ret; 70911cb8da0SChris Morgan charger->fcc_mah = get_unaligned_le24(bulk_reg); 71011cb8da0SChris Morgan 71111cb8da0SChris Morgan /* 71211cb8da0SChris Morgan * Sanity checking for values equal to zero or less than would be 71311cb8da0SChris Morgan * practical for this device (BSP Kernel assumes 500mAH or less) for 71411cb8da0SChris Morgan * practicality purposes. Also check if the value is too large and 71511cb8da0SChris Morgan * correct it. 71611cb8da0SChris Morgan */ 71711cb8da0SChris Morgan if ((charger->fcc_mah < 500) || 71811cb8da0SChris Morgan ((charger->fcc_mah * 1000) > charger->bat_charge_full_design_uah)) { 71911cb8da0SChris Morgan dev_info(charger->dev, 72011cb8da0SChris Morgan "Invalid NVRAM max charge, setting to %u uAH\n", 72111cb8da0SChris Morgan charger->bat_charge_full_design_uah); 72211cb8da0SChris Morgan charger->fcc_mah = charger->bat_charge_full_design_uah / 1000; 72311cb8da0SChris Morgan } 72411cb8da0SChris Morgan 72511cb8da0SChris Morgan /* 72611cb8da0SChris Morgan * Read the nvram for state of charge. Sanity check for values greater 727baba1315SChris Morgan * than 100 (10000) or less than 0, because other things (BSP kernels, 728baba1315SChris Morgan * U-Boot, or even i2cset) can write to this register. If the value is 729baba1315SChris Morgan * off it should get corrected automatically when the voltage drops to 730baba1315SChris Morgan * the min (soc is 0) or when the battery is full (soc is 100). 73111cb8da0SChris Morgan */ 73211cb8da0SChris Morgan ret = regmap_bulk_read(charger->rk808->regmap, 73311cb8da0SChris Morgan RK817_GAS_GAUGE_BAT_R1, bulk_reg, 3); 73411cb8da0SChris Morgan if (ret < 0) 73511cb8da0SChris Morgan return ret; 73611cb8da0SChris Morgan charger->soc = get_unaligned_le24(bulk_reg); 73711cb8da0SChris Morgan if (charger->soc > 10000) 73811cb8da0SChris Morgan charger->soc = 10000; 739baba1315SChris Morgan if (charger->soc < 0) 740baba1315SChris Morgan charger->soc = 0; 74111cb8da0SChris Morgan 74211cb8da0SChris Morgan return 0; 74311cb8da0SChris Morgan } 74411cb8da0SChris Morgan 74511cb8da0SChris Morgan static int 74611cb8da0SChris Morgan rk817_read_or_set_full_charge_on_boot(struct rk817_charger *charger, 74711cb8da0SChris Morgan struct power_supply_battery_info *bat_info) 74811cb8da0SChris Morgan { 74911cb8da0SChris Morgan struct rk808 *rk808 = charger->rk808; 75011cb8da0SChris Morgan u8 bulk_reg[4]; 751baba1315SChris Morgan u32 boot_voltage, boot_charge_mah; 752baba1315SChris Morgan int ret, reg, off_time, tmp; 75311cb8da0SChris Morgan bool first_boot; 75411cb8da0SChris Morgan 75511cb8da0SChris Morgan /* 75611cb8da0SChris Morgan * Check if the battery is uninitalized. If it is, the columb counter 75711cb8da0SChris Morgan * needs to be set up. 75811cb8da0SChris Morgan */ 75911cb8da0SChris Morgan ret = regmap_read(rk808->regmap, RK817_GAS_GAUGE_GG_STS, ®); 76011cb8da0SChris Morgan if (ret < 0) 76111cb8da0SChris Morgan return ret; 76211cb8da0SChris Morgan first_boot = reg & RK817_BAT_CON; 76311cb8da0SChris Morgan /* 76411cb8da0SChris Morgan * If the battery is uninitialized, use the poweron voltage and an ocv 76511cb8da0SChris Morgan * lookup to guess our charge. The number won't be very accurate until 76611cb8da0SChris Morgan * we hit either our minimum voltage (0%) or full charge (100%). 76711cb8da0SChris Morgan */ 76811cb8da0SChris Morgan if (first_boot) { 76911cb8da0SChris Morgan regmap_bulk_read(rk808->regmap, RK817_GAS_GAUGE_PWRON_VOL_H, 77011cb8da0SChris Morgan bulk_reg, 2); 77111cb8da0SChris Morgan tmp = get_unaligned_be16(bulk_reg); 77211cb8da0SChris Morgan boot_voltage = (charger->voltage_k * tmp) + 77311cb8da0SChris Morgan 1000 * charger->voltage_b; 77411cb8da0SChris Morgan /* 77511cb8da0SChris Morgan * Since only implementation has no working thermistor, assume 77611cb8da0SChris Morgan * 20C for OCV lookup. If lookup fails, report error with OCV 77711cb8da0SChris Morgan * table. 77811cb8da0SChris Morgan */ 77911cb8da0SChris Morgan charger->soc = power_supply_batinfo_ocv2cap(bat_info, 78011cb8da0SChris Morgan boot_voltage, 78111cb8da0SChris Morgan 20) * 1000; 78211cb8da0SChris Morgan if (charger->soc < 0) 78311cb8da0SChris Morgan charger->soc = 0; 78411cb8da0SChris Morgan 78511cb8da0SChris Morgan /* Guess that full charge capacity is the design capacity */ 78611cb8da0SChris Morgan charger->fcc_mah = charger->bat_charge_full_design_uah / 1000; 78711cb8da0SChris Morgan /* 78811cb8da0SChris Morgan * Set battery as "set up". BSP driver uses this value even 78911cb8da0SChris Morgan * though datasheet claims it's a read-only value. 79011cb8da0SChris Morgan */ 79111cb8da0SChris Morgan regmap_write_bits(rk808->regmap, RK817_GAS_GAUGE_GG_STS, 79211cb8da0SChris Morgan RK817_BAT_CON, 0); 79311cb8da0SChris Morgan /* Save nvram values */ 79411cb8da0SChris Morgan ret = rk817_record_battery_nvram_values(charger); 79511cb8da0SChris Morgan if (ret < 0) 79611cb8da0SChris Morgan return ret; 79711cb8da0SChris Morgan } else { 79811cb8da0SChris Morgan ret = rk817_read_battery_nvram_values(charger); 79911cb8da0SChris Morgan if (ret < 0) 80011cb8da0SChris Morgan return ret; 80111cb8da0SChris Morgan 80211cb8da0SChris Morgan regmap_bulk_read(rk808->regmap, RK817_GAS_GAUGE_Q_PRES_H3, 80311cb8da0SChris Morgan bulk_reg, 4); 80411cb8da0SChris Morgan tmp = get_unaligned_be32(bulk_reg); 805baba1315SChris Morgan if (tmp < 0) 806baba1315SChris Morgan tmp = 0; 80711cb8da0SChris Morgan boot_charge_mah = ADC_TO_CHARGE_UAH(tmp, 80811cb8da0SChris Morgan charger->res_div) / 1000; 80911cb8da0SChris Morgan /* 810baba1315SChris Morgan * Check if the columb counter has been off for more than 30 81111cb8da0SChris Morgan * minutes as it tends to drift downward. If so, re-init soc 81211cb8da0SChris Morgan * with the boot voltage instead. Note the unit values for the 81311cb8da0SChris Morgan * OFF_CNT register appear to be in decaminutes and stops 81411cb8da0SChris Morgan * counting at 2550 (0xFF) minutes. BSP kernel used OCV, but 81511cb8da0SChris Morgan * for me occasionally that would show invalid values. Boot 81611cb8da0SChris Morgan * voltage is only accurate for me on first poweron (not 81711cb8da0SChris Morgan * reboots), but we shouldn't ever encounter an OFF_CNT more 81811cb8da0SChris Morgan * than 0 on a reboot anyway. 81911cb8da0SChris Morgan */ 82011cb8da0SChris Morgan regmap_read(rk808->regmap, RK817_GAS_GAUGE_OFF_CNT, &off_time); 821baba1315SChris Morgan if (off_time >= 3) { 82211cb8da0SChris Morgan regmap_bulk_read(rk808->regmap, 82311cb8da0SChris Morgan RK817_GAS_GAUGE_PWRON_VOL_H, 82411cb8da0SChris Morgan bulk_reg, 2); 82511cb8da0SChris Morgan tmp = get_unaligned_be16(bulk_reg); 82611cb8da0SChris Morgan boot_voltage = (charger->voltage_k * tmp) + 82711cb8da0SChris Morgan 1000 * charger->voltage_b; 82811cb8da0SChris Morgan charger->soc = 82911cb8da0SChris Morgan power_supply_batinfo_ocv2cap(bat_info, 83011cb8da0SChris Morgan boot_voltage, 83111cb8da0SChris Morgan 20) * 1000; 83211cb8da0SChris Morgan } else { 83311cb8da0SChris Morgan charger->soc = (boot_charge_mah * 1000 * 100 / 83411cb8da0SChris Morgan charger->fcc_mah); 83511cb8da0SChris Morgan } 83611cb8da0SChris Morgan } 83711cb8da0SChris Morgan 83811cb8da0SChris Morgan /* 83911cb8da0SChris Morgan * Now we have our full charge capacity and soc, init the columb 84011cb8da0SChris Morgan * counter. 84111cb8da0SChris Morgan */ 84211cb8da0SChris Morgan boot_charge_mah = charger->soc * charger->fcc_mah / 100 / 1000; 84311cb8da0SChris Morgan if (boot_charge_mah > charger->fcc_mah) 84411cb8da0SChris Morgan boot_charge_mah = charger->fcc_mah; 84511cb8da0SChris Morgan tmp = CHARGE_TO_ADC(boot_charge_mah, charger->res_div); 84611cb8da0SChris Morgan put_unaligned_be32(tmp, bulk_reg); 84711cb8da0SChris Morgan ret = regmap_bulk_write(rk808->regmap, RK817_GAS_GAUGE_Q_INIT_H3, 84811cb8da0SChris Morgan bulk_reg, 4); 84911cb8da0SChris Morgan if (ret < 0) 85011cb8da0SChris Morgan return ret; 85111cb8da0SChris Morgan 85211cb8da0SChris Morgan /* Set QMAX value to max design capacity. */ 85311cb8da0SChris Morgan tmp = CHARGE_TO_ADC((charger->bat_charge_full_design_uah / 1000), 85411cb8da0SChris Morgan charger->res_div); 85511cb8da0SChris Morgan put_unaligned_be32(tmp, bulk_reg); 85611cb8da0SChris Morgan ret = regmap_bulk_write(rk808->regmap, RK817_GAS_GAUGE_Q_MAX_H3, 85711cb8da0SChris Morgan bulk_reg, 4); 85811cb8da0SChris Morgan if (ret < 0) 85911cb8da0SChris Morgan return ret; 86011cb8da0SChris Morgan 86111cb8da0SChris Morgan return 0; 86211cb8da0SChris Morgan } 86311cb8da0SChris Morgan 86411cb8da0SChris Morgan static int rk817_battery_init(struct rk817_charger *charger, 86511cb8da0SChris Morgan struct power_supply_battery_info *bat_info) 86611cb8da0SChris Morgan { 86711cb8da0SChris Morgan struct rk808 *rk808 = charger->rk808; 86811cb8da0SChris Morgan u32 tmp, max_chg_vol_mv, max_chg_cur_ma; 869883babd4SChris Morgan u8 max_chg_vol_reg, chg_term_i_reg; 870883babd4SChris Morgan int ret, chg_term_ma, max_chg_cur_reg; 87111cb8da0SChris Morgan u8 bulk_reg[2]; 87211cb8da0SChris Morgan 87311cb8da0SChris Morgan /* Get initial plug state */ 87411cb8da0SChris Morgan regmap_read(rk808->regmap, RK817_SYS_STS, &tmp); 87511cb8da0SChris Morgan charger->plugged_in = (tmp & RK817_PLUG_IN_STS); 87611cb8da0SChris Morgan 87711cb8da0SChris Morgan /* 87811cb8da0SChris Morgan * Turn on all ADC functions to measure battery, USB, and sys voltage, 87911cb8da0SChris Morgan * as well as batt temp. Note only tested implementation so far does 88011cb8da0SChris Morgan * not use a battery with a thermistor. 88111cb8da0SChris Morgan */ 88211cb8da0SChris Morgan regmap_write(rk808->regmap, RK817_GAS_GAUGE_ADC_CONFIG0, 0xfc); 88311cb8da0SChris Morgan 88411cb8da0SChris Morgan /* 88511cb8da0SChris Morgan * Set relax mode voltage sampling interval and ADC offset calibration 88611cb8da0SChris Morgan * interval to 8 minutes to mirror BSP kernel. Set voltage and current 88711cb8da0SChris Morgan * modes to average to mirror BSP kernel. 88811cb8da0SChris Morgan */ 88911cb8da0SChris Morgan regmap_write(rk808->regmap, RK817_GAS_GAUGE_GG_CON, 0x04); 89011cb8da0SChris Morgan 89111cb8da0SChris Morgan /* Calibrate voltage like the BSP does here. */ 89211cb8da0SChris Morgan rk817_bat_calib_vol(charger); 89311cb8da0SChris Morgan 89411cb8da0SChris Morgan /* Write relax threshold, derived from sleep enter current. */ 89511cb8da0SChris Morgan tmp = CURRENT_TO_ADC(charger->sleep_enter_current_ua, 89611cb8da0SChris Morgan charger->res_div); 89711cb8da0SChris Morgan put_unaligned_be16(tmp, bulk_reg); 89811cb8da0SChris Morgan regmap_bulk_write(rk808->regmap, RK817_GAS_GAUGE_RELAX_THRE_H, 89911cb8da0SChris Morgan bulk_reg, 2); 90011cb8da0SChris Morgan 90111cb8da0SChris Morgan /* Write sleep sample current, derived from sleep filter current. */ 90211cb8da0SChris Morgan tmp = CURRENT_TO_ADC(charger->sleep_filter_current_ua, 90311cb8da0SChris Morgan charger->res_div); 90411cb8da0SChris Morgan put_unaligned_be16(tmp, bulk_reg); 90511cb8da0SChris Morgan regmap_bulk_write(rk808->regmap, RK817_GAS_GAUGE_SLEEP_CON_SAMP_CUR_H, 90611cb8da0SChris Morgan bulk_reg, 2); 90711cb8da0SChris Morgan 90811cb8da0SChris Morgan /* Restart battery relax voltage */ 90911cb8da0SChris Morgan regmap_write_bits(rk808->regmap, RK817_GAS_GAUGE_GG_STS, 91011cb8da0SChris Morgan RK817_RELAX_VOL_UPD, (0x0 << 2)); 91111cb8da0SChris Morgan 91211cb8da0SChris Morgan /* 91311cb8da0SChris Morgan * Set OCV Threshold Voltage to 127.5mV. This was hard coded like this 91411cb8da0SChris Morgan * in the BSP. 91511cb8da0SChris Morgan */ 91611cb8da0SChris Morgan regmap_write(rk808->regmap, RK817_GAS_GAUGE_OCV_THRE_VOL, 0xff); 91711cb8da0SChris Morgan 91811cb8da0SChris Morgan /* 91911cb8da0SChris Morgan * Set maximum charging voltage to battery max voltage. Trying to be 92011cb8da0SChris Morgan * incredibly safe with these value, as setting them wrong could 92111cb8da0SChris Morgan * overcharge the battery, which would be very bad. 92211cb8da0SChris Morgan */ 92311cb8da0SChris Morgan max_chg_vol_mv = bat_info->constant_charge_voltage_max_uv / 1000; 92411cb8da0SChris Morgan max_chg_cur_ma = bat_info->constant_charge_current_max_ua / 1000; 92511cb8da0SChris Morgan 92611cb8da0SChris Morgan if (max_chg_vol_mv < 4100) { 92711cb8da0SChris Morgan return dev_err_probe(charger->dev, -EINVAL, 92811cb8da0SChris Morgan "invalid max charger voltage, value %u unsupported\n", 92911cb8da0SChris Morgan max_chg_vol_mv * 1000); 93011cb8da0SChris Morgan } 93111cb8da0SChris Morgan if (max_chg_vol_mv > 4450) { 93211cb8da0SChris Morgan dev_info(charger->dev, 93311cb8da0SChris Morgan "Setting max charge voltage to 4450000uv\n"); 93411cb8da0SChris Morgan max_chg_vol_mv = 4450; 93511cb8da0SChris Morgan } 93611cb8da0SChris Morgan 93711cb8da0SChris Morgan if (max_chg_cur_ma < 500) { 93811cb8da0SChris Morgan return dev_err_probe(charger->dev, -EINVAL, 93911cb8da0SChris Morgan "invalid max charger current, value %u unsupported\n", 94011cb8da0SChris Morgan max_chg_cur_ma * 1000); 94111cb8da0SChris Morgan } 94211cb8da0SChris Morgan if (max_chg_cur_ma > 3500) 94311cb8da0SChris Morgan dev_info(charger->dev, 94411cb8da0SChris Morgan "Setting max charge current to 3500000ua\n"); 94511cb8da0SChris Morgan 94611cb8da0SChris Morgan /* 94711cb8da0SChris Morgan * Now that the values are sanity checked, if we subtract 4100 from the 94811cb8da0SChris Morgan * max voltage and divide by 50, we conviently get the exact value for 94911cb8da0SChris Morgan * the registers, which are 4.1v, 4.15v, 4.2v, 4.25v, 4.3v, 4.35v, 95011cb8da0SChris Morgan * 4.4v, and 4.45v; these correspond to values 0x00 through 0x07. 95111cb8da0SChris Morgan */ 95211cb8da0SChris Morgan max_chg_vol_reg = (max_chg_vol_mv - 4100) / 50; 95311cb8da0SChris Morgan 95411cb8da0SChris Morgan max_chg_cur_reg = rk817_chg_cur_to_reg(max_chg_cur_ma); 95511cb8da0SChris Morgan 95611cb8da0SChris Morgan if (max_chg_vol_reg < 0 || max_chg_vol_reg > 7) { 95711cb8da0SChris Morgan return dev_err_probe(charger->dev, -EINVAL, 95811cb8da0SChris Morgan "invalid max charger voltage, value %u unsupported\n", 95911cb8da0SChris Morgan max_chg_vol_mv * 1000); 96011cb8da0SChris Morgan } 96111cb8da0SChris Morgan if (max_chg_cur_reg < 0 || max_chg_cur_reg > 7) { 96211cb8da0SChris Morgan return dev_err_probe(charger->dev, -EINVAL, 96311cb8da0SChris Morgan "invalid max charger current, value %u unsupported\n", 96411cb8da0SChris Morgan max_chg_cur_ma * 1000); 96511cb8da0SChris Morgan } 96611cb8da0SChris Morgan 96711cb8da0SChris Morgan /* 96811cb8da0SChris Morgan * Write the values to the registers, and deliver an emergency warning 96911cb8da0SChris Morgan * in the event they are not written correctly. 97011cb8da0SChris Morgan */ 97111cb8da0SChris Morgan ret = regmap_write_bits(rk808->regmap, RK817_PMIC_CHRG_OUT, 97211cb8da0SChris Morgan RK817_CHRG_VOL_SEL, (max_chg_vol_reg << 4)); 97311cb8da0SChris Morgan if (ret) { 97411cb8da0SChris Morgan dev_emerg(charger->dev, 97511cb8da0SChris Morgan "Danger, unable to set max charger voltage: %u\n", 97611cb8da0SChris Morgan ret); 97711cb8da0SChris Morgan } 97811cb8da0SChris Morgan 97911cb8da0SChris Morgan ret = regmap_write_bits(rk808->regmap, RK817_PMIC_CHRG_OUT, 98011cb8da0SChris Morgan RK817_CHRG_CUR_SEL, max_chg_cur_reg); 98111cb8da0SChris Morgan if (ret) { 98211cb8da0SChris Morgan dev_emerg(charger->dev, 98311cb8da0SChris Morgan "Danger, unable to set max charger current: %u\n", 98411cb8da0SChris Morgan ret); 98511cb8da0SChris Morgan } 98611cb8da0SChris Morgan 98711cb8da0SChris Morgan /* Set charge finishing mode to analog */ 98811cb8da0SChris Morgan regmap_write_bits(rk808->regmap, RK817_PMIC_CHRG_TERM, 98911cb8da0SChris Morgan RK817_CHRG_TERM_ANA_DIG, (0x0 << 2)); 99011cb8da0SChris Morgan 99111cb8da0SChris Morgan /* 99211cb8da0SChris Morgan * Set charge finish current, warn if value not in range and keep 99311cb8da0SChris Morgan * default. 99411cb8da0SChris Morgan */ 99511cb8da0SChris Morgan chg_term_ma = bat_info->charge_term_current_ua / 1000; 99611cb8da0SChris Morgan if (chg_term_ma < 150 || chg_term_ma > 400) { 99711cb8da0SChris Morgan dev_warn(charger->dev, 99811cb8da0SChris Morgan "Invalid charge termination %u, keeping default\n", 99911cb8da0SChris Morgan chg_term_ma * 1000); 100011cb8da0SChris Morgan chg_term_ma = 200; 100111cb8da0SChris Morgan } 100211cb8da0SChris Morgan 100311cb8da0SChris Morgan /* 100411cb8da0SChris Morgan * Values of 150ma, 200ma, 300ma, and 400ma correspond to 00, 01, 10, 100511cb8da0SChris Morgan * and 11. 100611cb8da0SChris Morgan */ 100711cb8da0SChris Morgan chg_term_i_reg = (chg_term_ma - 100) / 100; 100811cb8da0SChris Morgan regmap_write_bits(rk808->regmap, RK817_PMIC_CHRG_TERM, 100911cb8da0SChris Morgan RK817_CHRG_TERM_ANA_SEL, chg_term_i_reg); 101011cb8da0SChris Morgan 101111cb8da0SChris Morgan ret = rk817_read_or_set_full_charge_on_boot(charger, bat_info); 101211cb8da0SChris Morgan if (ret < 0) 101311cb8da0SChris Morgan return ret; 101411cb8da0SChris Morgan 101511cb8da0SChris Morgan /* 101611cb8da0SChris Morgan * Set minimum USB input voltage to 4.5v and enable USB voltage input 101711cb8da0SChris Morgan * limit. 101811cb8da0SChris Morgan */ 101911cb8da0SChris Morgan regmap_write_bits(rk808->regmap, RK817_PMIC_CHRG_IN, 102011cb8da0SChris Morgan RK817_USB_VLIM_SEL, (0x05 << 4)); 102111cb8da0SChris Morgan regmap_write_bits(rk808->regmap, RK817_PMIC_CHRG_IN, RK817_USB_VLIM_EN, 102211cb8da0SChris Morgan (0x01 << 7)); 102311cb8da0SChris Morgan 102411cb8da0SChris Morgan /* 102511cb8da0SChris Morgan * Set average USB input current limit to 1.5A and enable USB current 102611cb8da0SChris Morgan * input limit. 102711cb8da0SChris Morgan */ 102811cb8da0SChris Morgan regmap_write_bits(rk808->regmap, RK817_PMIC_CHRG_IN, 102911cb8da0SChris Morgan RK817_USB_ILIM_SEL, 0x03); 103011cb8da0SChris Morgan regmap_write_bits(rk808->regmap, RK817_PMIC_CHRG_IN, RK817_USB_ILIM_EN, 103111cb8da0SChris Morgan (0x01 << 3)); 103211cb8da0SChris Morgan 103311cb8da0SChris Morgan return 0; 103411cb8da0SChris Morgan } 103511cb8da0SChris Morgan 103611cb8da0SChris Morgan static void rk817_charging_monitor(struct work_struct *work) 103711cb8da0SChris Morgan { 103811cb8da0SChris Morgan struct rk817_charger *charger; 103911cb8da0SChris Morgan 104011cb8da0SChris Morgan charger = container_of(work, struct rk817_charger, work.work); 104111cb8da0SChris Morgan 104211cb8da0SChris Morgan rk817_read_props(charger); 104311cb8da0SChris Morgan 104411cb8da0SChris Morgan /* Run every 8 seconds like the BSP driver did. */ 104511cb8da0SChris Morgan queue_delayed_work(system_wq, &charger->work, msecs_to_jiffies(8000)); 104611cb8da0SChris Morgan } 104711cb8da0SChris Morgan 104811cb8da0SChris Morgan static int rk817_charger_probe(struct platform_device *pdev) 104911cb8da0SChris Morgan { 105011cb8da0SChris Morgan struct rk808 *rk808 = dev_get_drvdata(pdev->dev.parent); 105111cb8da0SChris Morgan struct rk817_charger *charger; 105211cb8da0SChris Morgan struct device_node *node; 105311cb8da0SChris Morgan struct power_supply_battery_info *bat_info; 105411cb8da0SChris Morgan struct device *dev = &pdev->dev; 105511cb8da0SChris Morgan struct power_supply_config pscfg = {}; 105611cb8da0SChris Morgan int plugin_irq, plugout_irq; 105711cb8da0SChris Morgan int of_value; 105811cb8da0SChris Morgan int ret; 105911cb8da0SChris Morgan 106011cb8da0SChris Morgan node = of_get_child_by_name(dev->parent->of_node, "charger"); 106111cb8da0SChris Morgan if (!node) 106211cb8da0SChris Morgan return -ENODEV; 106311cb8da0SChris Morgan 106411cb8da0SChris Morgan charger = devm_kzalloc(&pdev->dev, sizeof(*charger), GFP_KERNEL); 106554c03bfdSQiheng Lin if (!charger) { 106654c03bfdSQiheng Lin of_node_put(node); 106711cb8da0SChris Morgan return -ENOMEM; 106854c03bfdSQiheng Lin } 106911cb8da0SChris Morgan 107011cb8da0SChris Morgan charger->rk808 = rk808; 107111cb8da0SChris Morgan 107211cb8da0SChris Morgan charger->dev = &pdev->dev; 107311cb8da0SChris Morgan platform_set_drvdata(pdev, charger); 107411cb8da0SChris Morgan 107511cb8da0SChris Morgan rk817_bat_calib_vol(charger); 107611cb8da0SChris Morgan 107711cb8da0SChris Morgan pscfg.drv_data = charger; 107811cb8da0SChris Morgan pscfg.of_node = node; 107911cb8da0SChris Morgan 108011cb8da0SChris Morgan /* 108111cb8da0SChris Morgan * Get sample resistor value. Note only values of 10000 or 20000 108211cb8da0SChris Morgan * microohms are allowed. Schematic for my test implementation (an 108311cb8da0SChris Morgan * Odroid Go Advance) shows a 10 milliohm resistor for reference. 108411cb8da0SChris Morgan */ 108511cb8da0SChris Morgan ret = of_property_read_u32(node, "rockchip,resistor-sense-micro-ohms", 108611cb8da0SChris Morgan &of_value); 108711cb8da0SChris Morgan if (ret < 0) { 108811cb8da0SChris Morgan return dev_err_probe(dev, ret, 108911cb8da0SChris Morgan "Error reading sample resistor value\n"); 109011cb8da0SChris Morgan } 109111cb8da0SChris Morgan /* 109211cb8da0SChris Morgan * Store as a 1 or a 2, since all we really use the value for is as a 109311cb8da0SChris Morgan * divisor in some calculations. 109411cb8da0SChris Morgan */ 109511cb8da0SChris Morgan charger->res_div = (of_value == 20000) ? 2 : 1; 109611cb8da0SChris Morgan 109711cb8da0SChris Morgan /* 109811cb8da0SChris Morgan * Get sleep enter current value. Not sure what this value is for 109911cb8da0SChris Morgan * other than to help calibrate the relax threshold. 110011cb8da0SChris Morgan */ 110111cb8da0SChris Morgan ret = of_property_read_u32(node, 110211cb8da0SChris Morgan "rockchip,sleep-enter-current-microamp", 110311cb8da0SChris Morgan &of_value); 110411cb8da0SChris Morgan if (ret < 0) { 110511cb8da0SChris Morgan return dev_err_probe(dev, ret, 110611cb8da0SChris Morgan "Error reading sleep enter cur value\n"); 110711cb8da0SChris Morgan } 110811cb8da0SChris Morgan charger->sleep_enter_current_ua = of_value; 110911cb8da0SChris Morgan 111011cb8da0SChris Morgan /* Get sleep filter current value */ 111111cb8da0SChris Morgan ret = of_property_read_u32(node, 111211cb8da0SChris Morgan "rockchip,sleep-filter-current-microamp", 111311cb8da0SChris Morgan &of_value); 111411cb8da0SChris Morgan if (ret < 0) { 111511cb8da0SChris Morgan return dev_err_probe(dev, ret, 111611cb8da0SChris Morgan "Error reading sleep filter cur value\n"); 111711cb8da0SChris Morgan } 111811cb8da0SChris Morgan 111911cb8da0SChris Morgan charger->sleep_filter_current_ua = of_value; 112011cb8da0SChris Morgan 112111cb8da0SChris Morgan charger->bat_ps = devm_power_supply_register(&pdev->dev, 112211cb8da0SChris Morgan &rk817_bat_desc, &pscfg); 1123172c65e6SDan Carpenter if (IS_ERR(charger->bat_ps)) 112411cb8da0SChris Morgan return dev_err_probe(dev, -EINVAL, 112511cb8da0SChris Morgan "Battery failed to probe\n"); 112611cb8da0SChris Morgan 1127172c65e6SDan Carpenter charger->chg_ps = devm_power_supply_register(&pdev->dev, 1128172c65e6SDan Carpenter &rk817_chg_desc, &pscfg); 112911cb8da0SChris Morgan if (IS_ERR(charger->chg_ps)) 113011cb8da0SChris Morgan return dev_err_probe(dev, -EINVAL, 113111cb8da0SChris Morgan "Charger failed to probe\n"); 113211cb8da0SChris Morgan 113311cb8da0SChris Morgan ret = power_supply_get_battery_info(charger->bat_ps, 113411cb8da0SChris Morgan &bat_info); 113511cb8da0SChris Morgan if (ret) { 113611cb8da0SChris Morgan return dev_err_probe(dev, ret, 11373cc52437SChristophe JAILLET "Unable to get battery info\n"); 113811cb8da0SChris Morgan } 113911cb8da0SChris Morgan 114011cb8da0SChris Morgan if ((bat_info->charge_full_design_uah <= 0) || 114111cb8da0SChris Morgan (bat_info->voltage_min_design_uv <= 0) || 114211cb8da0SChris Morgan (bat_info->voltage_max_design_uv <= 0) || 114311cb8da0SChris Morgan (bat_info->constant_charge_voltage_max_uv <= 0) || 114411cb8da0SChris Morgan (bat_info->constant_charge_current_max_ua <= 0) || 114511cb8da0SChris Morgan (bat_info->charge_term_current_ua <= 0)) { 114611cb8da0SChris Morgan return dev_err_probe(dev, -EINVAL, 114711cb8da0SChris Morgan "Required bat info missing or invalid\n"); 114811cb8da0SChris Morgan } 114911cb8da0SChris Morgan 115011cb8da0SChris Morgan charger->bat_charge_full_design_uah = bat_info->charge_full_design_uah; 115111cb8da0SChris Morgan charger->bat_voltage_min_design_uv = bat_info->voltage_min_design_uv; 115211cb8da0SChris Morgan charger->bat_voltage_max_design_uv = bat_info->voltage_max_design_uv; 115311cb8da0SChris Morgan 115411cb8da0SChris Morgan /* 115511cb8da0SChris Morgan * Has to run after power_supply_get_battery_info as it depends on some 115611cb8da0SChris Morgan * values discovered from that routine. 115711cb8da0SChris Morgan */ 115811cb8da0SChris Morgan ret = rk817_battery_init(charger, bat_info); 115911cb8da0SChris Morgan if (ret) 116011cb8da0SChris Morgan return ret; 116111cb8da0SChris Morgan 116211cb8da0SChris Morgan power_supply_put_battery_info(charger->bat_ps, bat_info); 116311cb8da0SChris Morgan 116411cb8da0SChris Morgan plugin_irq = platform_get_irq(pdev, 0); 116511cb8da0SChris Morgan if (plugin_irq < 0) 116611cb8da0SChris Morgan return plugin_irq; 116711cb8da0SChris Morgan 116811cb8da0SChris Morgan plugout_irq = platform_get_irq(pdev, 1); 116911cb8da0SChris Morgan if (plugout_irq < 0) 117011cb8da0SChris Morgan return plugout_irq; 117111cb8da0SChris Morgan 117211cb8da0SChris Morgan ret = devm_request_threaded_irq(charger->dev, plugin_irq, NULL, 117311cb8da0SChris Morgan rk817_plug_in_isr, 117411cb8da0SChris Morgan IRQF_TRIGGER_RISING | IRQF_ONESHOT, 117511cb8da0SChris Morgan "rk817_plug_in", charger); 117611cb8da0SChris Morgan if (ret) { 117711cb8da0SChris Morgan return dev_err_probe(&pdev->dev, ret, 117811cb8da0SChris Morgan "plug_in_irq request failed!\n"); 117911cb8da0SChris Morgan } 118011cb8da0SChris Morgan 118111cb8da0SChris Morgan ret = devm_request_threaded_irq(charger->dev, plugout_irq, NULL, 118211cb8da0SChris Morgan rk817_plug_out_isr, 118311cb8da0SChris Morgan IRQF_TRIGGER_RISING | IRQF_ONESHOT, 118411cb8da0SChris Morgan "rk817_plug_out", charger); 118511cb8da0SChris Morgan if (ret) { 118611cb8da0SChris Morgan return dev_err_probe(&pdev->dev, ret, 118711cb8da0SChris Morgan "plug_out_irq request failed!\n"); 118811cb8da0SChris Morgan } 118911cb8da0SChris Morgan 119011cb8da0SChris Morgan ret = devm_delayed_work_autocancel(&pdev->dev, &charger->work, 119111cb8da0SChris Morgan rk817_charging_monitor); 119211cb8da0SChris Morgan if (ret) 119311cb8da0SChris Morgan return ret; 119411cb8da0SChris Morgan 119511cb8da0SChris Morgan /* Force the first update immediately. */ 119611cb8da0SChris Morgan mod_delayed_work(system_wq, &charger->work, 0); 119711cb8da0SChris Morgan 119811cb8da0SChris Morgan return 0; 119911cb8da0SChris Morgan } 120011cb8da0SChris Morgan 120111cb8da0SChris Morgan 120211cb8da0SChris Morgan static struct platform_driver rk817_charger_driver = { 120311cb8da0SChris Morgan .probe = rk817_charger_probe, 120411cb8da0SChris Morgan .driver = { 120511cb8da0SChris Morgan .name = "rk817-charger", 120611cb8da0SChris Morgan }, 120711cb8da0SChris Morgan }; 120811cb8da0SChris Morgan module_platform_driver(rk817_charger_driver); 120911cb8da0SChris Morgan 121011cb8da0SChris Morgan MODULE_DESCRIPTION("Battery power supply driver for RK817 PMIC"); 121111cb8da0SChris Morgan MODULE_AUTHOR("Maya Matuszczyk <maccraft123mc@gmail.com>"); 121211cb8da0SChris Morgan MODULE_AUTHOR("Chris Morgan <macromorgan@hotmail.com>"); 121311cb8da0SChris Morgan MODULE_LICENSE("GPL"); 1214