162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+ 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Charger Driver for Rockchip rk817 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (c) 2021 Maya Matuszczyk <maccraft123mc@gmail.com> 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * Authors: Maya Matuszczyk <maccraft123mc@gmail.com> 862306a36Sopenharmony_ci * Chris Morgan <macromorgan@hotmail.com> 962306a36Sopenharmony_ci */ 1062306a36Sopenharmony_ci 1162306a36Sopenharmony_ci#include <asm/unaligned.h> 1262306a36Sopenharmony_ci#include <linux/devm-helpers.h> 1362306a36Sopenharmony_ci#include <linux/mfd/rk808.h> 1462306a36Sopenharmony_ci#include <linux/irq.h> 1562306a36Sopenharmony_ci#include <linux/of.h> 1662306a36Sopenharmony_ci#include <linux/platform_device.h> 1762306a36Sopenharmony_ci#include <linux/power_supply.h> 1862306a36Sopenharmony_ci#include <linux/regmap.h> 1962306a36Sopenharmony_ci 2062306a36Sopenharmony_ci/* Charging statuses reported by hardware register */ 2162306a36Sopenharmony_cienum rk817_charge_status { 2262306a36Sopenharmony_ci CHRG_OFF, 2362306a36Sopenharmony_ci DEAD_CHRG, 2462306a36Sopenharmony_ci TRICKLE_CHRG, 2562306a36Sopenharmony_ci CC_OR_CV_CHRG, 2662306a36Sopenharmony_ci CHARGE_FINISH, 2762306a36Sopenharmony_ci USB_OVER_VOL, 2862306a36Sopenharmony_ci BAT_TMP_ERR, 2962306a36Sopenharmony_ci BAT_TIM_ERR, 3062306a36Sopenharmony_ci}; 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_ci/* 3362306a36Sopenharmony_ci * Max charging current read to/written from hardware register. 3462306a36Sopenharmony_ci * Note how highest value corresponding to 0x7 is the lowest 3562306a36Sopenharmony_ci * current, this is per the datasheet. 3662306a36Sopenharmony_ci */ 3762306a36Sopenharmony_cienum rk817_chg_cur { 3862306a36Sopenharmony_ci CHG_1A, 3962306a36Sopenharmony_ci CHG_1_5A, 4062306a36Sopenharmony_ci CHG_2A, 4162306a36Sopenharmony_ci CHG_2_5A, 4262306a36Sopenharmony_ci CHG_2_75A, 4362306a36Sopenharmony_ci CHG_3A, 4462306a36Sopenharmony_ci CHG_3_5A, 4562306a36Sopenharmony_ci CHG_0_5A, 4662306a36Sopenharmony_ci}; 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_cistruct rk817_charger { 4962306a36Sopenharmony_ci struct device *dev; 5062306a36Sopenharmony_ci struct rk808 *rk808; 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_ci struct power_supply *bat_ps; 5362306a36Sopenharmony_ci struct power_supply *chg_ps; 5462306a36Sopenharmony_ci bool plugged_in; 5562306a36Sopenharmony_ci bool battery_present; 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_ci /* 5862306a36Sopenharmony_ci * voltage_k and voltage_b values are used to calibrate the ADC 5962306a36Sopenharmony_ci * voltage readings. While they are documented in the BSP kernel and 6062306a36Sopenharmony_ci * datasheet as voltage_k and voltage_b, there is no further 6162306a36Sopenharmony_ci * information explaining them in more detail. 6262306a36Sopenharmony_ci */ 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_ci uint32_t voltage_k; 6562306a36Sopenharmony_ci uint32_t voltage_b; 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_ci /* 6862306a36Sopenharmony_ci * soc - state of charge - like the BSP this is stored as a percentage, 6962306a36Sopenharmony_ci * to the thousandth. BSP has a display state of charge (dsoc) and a 7062306a36Sopenharmony_ci * remaining state of charge (rsoc). This value will be used for both 7162306a36Sopenharmony_ci * purposes here so we don't do any fancy math to try and "smooth" the 7262306a36Sopenharmony_ci * charge and just report it as it is. Note for example an soc of 100 7362306a36Sopenharmony_ci * is stored as 100000, an soc of 50 is stored as 50000, etc. 7462306a36Sopenharmony_ci */ 7562306a36Sopenharmony_ci int soc; 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_ci /* 7862306a36Sopenharmony_ci * Capacity of battery when fully charged, equal or less than design 7962306a36Sopenharmony_ci * capacity depending upon wear. BSP kernel saves to nvram in mAh, 8062306a36Sopenharmony_ci * so this value is in mAh not the standard uAh. 8162306a36Sopenharmony_ci */ 8262306a36Sopenharmony_ci int fcc_mah; 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_ci /* 8562306a36Sopenharmony_ci * Calibrate the SOC on a fully charged battery, this way we can use 8662306a36Sopenharmony_ci * the calibrated SOC value to correct for columb counter drift. 8762306a36Sopenharmony_ci */ 8862306a36Sopenharmony_ci bool soc_cal; 8962306a36Sopenharmony_ci 9062306a36Sopenharmony_ci /* Implementation specific immutable properties from device tree */ 9162306a36Sopenharmony_ci int res_div; 9262306a36Sopenharmony_ci int sleep_enter_current_ua; 9362306a36Sopenharmony_ci int sleep_filter_current_ua; 9462306a36Sopenharmony_ci int bat_charge_full_design_uah; 9562306a36Sopenharmony_ci int bat_voltage_min_design_uv; 9662306a36Sopenharmony_ci int bat_voltage_max_design_uv; 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_ci /* Values updated periodically by driver for display. */ 9962306a36Sopenharmony_ci int charge_now_uah; 10062306a36Sopenharmony_ci int volt_avg_uv; 10162306a36Sopenharmony_ci int cur_avg_ua; 10262306a36Sopenharmony_ci int max_chg_cur_ua; 10362306a36Sopenharmony_ci int max_chg_volt_uv; 10462306a36Sopenharmony_ci int charge_status; 10562306a36Sopenharmony_ci int charger_input_volt_avg_uv; 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_ci /* Work queue to periodically update values. */ 10862306a36Sopenharmony_ci struct delayed_work work; 10962306a36Sopenharmony_ci}; 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_ci/* ADC coefficients extracted from BSP kernel */ 11262306a36Sopenharmony_ci#define ADC_TO_CURRENT(adc_value, res_div) \ 11362306a36Sopenharmony_ci (adc_value * 172 / res_div) 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_ci#define CURRENT_TO_ADC(current, samp_res) \ 11662306a36Sopenharmony_ci (current * samp_res / 172) 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_ci#define CHARGE_TO_ADC(capacity, res_div) \ 11962306a36Sopenharmony_ci (capacity * res_div * 3600 / 172 * 1000) 12062306a36Sopenharmony_ci 12162306a36Sopenharmony_ci#define ADC_TO_CHARGE_UAH(adc_value, res_div) \ 12262306a36Sopenharmony_ci (adc_value / 3600 * 172 / res_div) 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_cistatic int rk817_chg_cur_to_reg(u32 chg_cur_ma) 12562306a36Sopenharmony_ci{ 12662306a36Sopenharmony_ci if (chg_cur_ma >= 3500) 12762306a36Sopenharmony_ci return CHG_3_5A; 12862306a36Sopenharmony_ci else if (chg_cur_ma >= 3000) 12962306a36Sopenharmony_ci return CHG_3A; 13062306a36Sopenharmony_ci else if (chg_cur_ma >= 2750) 13162306a36Sopenharmony_ci return CHG_2_75A; 13262306a36Sopenharmony_ci else if (chg_cur_ma >= 2500) 13362306a36Sopenharmony_ci return CHG_2_5A; 13462306a36Sopenharmony_ci else if (chg_cur_ma >= 2000) 13562306a36Sopenharmony_ci return CHG_2A; 13662306a36Sopenharmony_ci else if (chg_cur_ma >= 1500) 13762306a36Sopenharmony_ci return CHG_1_5A; 13862306a36Sopenharmony_ci else if (chg_cur_ma >= 1000) 13962306a36Sopenharmony_ci return CHG_1A; 14062306a36Sopenharmony_ci else if (chg_cur_ma >= 500) 14162306a36Sopenharmony_ci return CHG_0_5A; 14262306a36Sopenharmony_ci else 14362306a36Sopenharmony_ci return -EINVAL; 14462306a36Sopenharmony_ci} 14562306a36Sopenharmony_ci 14662306a36Sopenharmony_cistatic int rk817_chg_cur_from_reg(u8 reg) 14762306a36Sopenharmony_ci{ 14862306a36Sopenharmony_ci switch (reg) { 14962306a36Sopenharmony_ci case CHG_0_5A: 15062306a36Sopenharmony_ci return 500000; 15162306a36Sopenharmony_ci case CHG_1A: 15262306a36Sopenharmony_ci return 1000000; 15362306a36Sopenharmony_ci case CHG_1_5A: 15462306a36Sopenharmony_ci return 1500000; 15562306a36Sopenharmony_ci case CHG_2A: 15662306a36Sopenharmony_ci return 2000000; 15762306a36Sopenharmony_ci case CHG_2_5A: 15862306a36Sopenharmony_ci return 2500000; 15962306a36Sopenharmony_ci case CHG_2_75A: 16062306a36Sopenharmony_ci return 2750000; 16162306a36Sopenharmony_ci case CHG_3A: 16262306a36Sopenharmony_ci return 3000000; 16362306a36Sopenharmony_ci case CHG_3_5A: 16462306a36Sopenharmony_ci return 3500000; 16562306a36Sopenharmony_ci default: 16662306a36Sopenharmony_ci return -EINVAL; 16762306a36Sopenharmony_ci } 16862306a36Sopenharmony_ci} 16962306a36Sopenharmony_ci 17062306a36Sopenharmony_cistatic void rk817_bat_calib_vol(struct rk817_charger *charger) 17162306a36Sopenharmony_ci{ 17262306a36Sopenharmony_ci uint32_t vcalib0 = 0; 17362306a36Sopenharmony_ci uint32_t vcalib1 = 0; 17462306a36Sopenharmony_ci u8 bulk_reg[2]; 17562306a36Sopenharmony_ci 17662306a36Sopenharmony_ci /* calibrate voltage */ 17762306a36Sopenharmony_ci regmap_bulk_read(charger->rk808->regmap, RK817_GAS_GAUGE_VCALIB0_H, 17862306a36Sopenharmony_ci bulk_reg, 2); 17962306a36Sopenharmony_ci vcalib0 = get_unaligned_be16(bulk_reg); 18062306a36Sopenharmony_ci 18162306a36Sopenharmony_ci regmap_bulk_read(charger->rk808->regmap, RK817_GAS_GAUGE_VCALIB1_H, 18262306a36Sopenharmony_ci bulk_reg, 2); 18362306a36Sopenharmony_ci vcalib1 = get_unaligned_be16(bulk_reg); 18462306a36Sopenharmony_ci 18562306a36Sopenharmony_ci /* values were taken from BSP kernel */ 18662306a36Sopenharmony_ci charger->voltage_k = (4025 - 2300) * 1000 / 18762306a36Sopenharmony_ci ((vcalib1 - vcalib0) ? (vcalib1 - vcalib0) : 1); 18862306a36Sopenharmony_ci charger->voltage_b = 4025 - (charger->voltage_k * vcalib1) / 1000; 18962306a36Sopenharmony_ci} 19062306a36Sopenharmony_ci 19162306a36Sopenharmony_cistatic void rk817_bat_calib_cur(struct rk817_charger *charger) 19262306a36Sopenharmony_ci{ 19362306a36Sopenharmony_ci u8 bulk_reg[2]; 19462306a36Sopenharmony_ci 19562306a36Sopenharmony_ci /* calibrate current */ 19662306a36Sopenharmony_ci regmap_bulk_read(charger->rk808->regmap, RK817_GAS_GAUGE_IOFFSET_H, 19762306a36Sopenharmony_ci bulk_reg, 2); 19862306a36Sopenharmony_ci regmap_bulk_write(charger->rk808->regmap, RK817_GAS_GAUGE_CAL_OFFSET_H, 19962306a36Sopenharmony_ci bulk_reg, 2); 20062306a36Sopenharmony_ci} 20162306a36Sopenharmony_ci 20262306a36Sopenharmony_ci/* 20362306a36Sopenharmony_ci * note that only the fcc_mah is really used by this driver, the other values 20462306a36Sopenharmony_ci * are to ensure we can remain backwards compatible with the BSP kernel. 20562306a36Sopenharmony_ci */ 20662306a36Sopenharmony_cistatic int rk817_record_battery_nvram_values(struct rk817_charger *charger) 20762306a36Sopenharmony_ci{ 20862306a36Sopenharmony_ci u8 bulk_reg[3]; 20962306a36Sopenharmony_ci int ret, rsoc; 21062306a36Sopenharmony_ci 21162306a36Sopenharmony_ci /* 21262306a36Sopenharmony_ci * write the soc value to the nvram location used by the BSP kernel 21362306a36Sopenharmony_ci * for the dsoc value. 21462306a36Sopenharmony_ci */ 21562306a36Sopenharmony_ci put_unaligned_le24(charger->soc, bulk_reg); 21662306a36Sopenharmony_ci ret = regmap_bulk_write(charger->rk808->regmap, RK817_GAS_GAUGE_BAT_R1, 21762306a36Sopenharmony_ci bulk_reg, 3); 21862306a36Sopenharmony_ci if (ret < 0) 21962306a36Sopenharmony_ci return ret; 22062306a36Sopenharmony_ci /* 22162306a36Sopenharmony_ci * write the remaining capacity in mah to the nvram location used by 22262306a36Sopenharmony_ci * the BSP kernel for the rsoc value. 22362306a36Sopenharmony_ci */ 22462306a36Sopenharmony_ci rsoc = (charger->soc * charger->fcc_mah) / 100000; 22562306a36Sopenharmony_ci put_unaligned_le24(rsoc, bulk_reg); 22662306a36Sopenharmony_ci ret = regmap_bulk_write(charger->rk808->regmap, RK817_GAS_GAUGE_DATA0, 22762306a36Sopenharmony_ci bulk_reg, 3); 22862306a36Sopenharmony_ci if (ret < 0) 22962306a36Sopenharmony_ci return ret; 23062306a36Sopenharmony_ci /* write the fcc_mah in mAh, just as the BSP kernel does. */ 23162306a36Sopenharmony_ci put_unaligned_le24(charger->fcc_mah, bulk_reg); 23262306a36Sopenharmony_ci ret = regmap_bulk_write(charger->rk808->regmap, RK817_GAS_GAUGE_DATA3, 23362306a36Sopenharmony_ci bulk_reg, 3); 23462306a36Sopenharmony_ci if (ret < 0) 23562306a36Sopenharmony_ci return ret; 23662306a36Sopenharmony_ci 23762306a36Sopenharmony_ci return 0; 23862306a36Sopenharmony_ci} 23962306a36Sopenharmony_ci 24062306a36Sopenharmony_cistatic int rk817_bat_calib_cap(struct rk817_charger *charger) 24162306a36Sopenharmony_ci{ 24262306a36Sopenharmony_ci struct rk808 *rk808 = charger->rk808; 24362306a36Sopenharmony_ci int tmp, charge_now, charge_now_adc, volt_avg; 24462306a36Sopenharmony_ci u8 bulk_reg[4]; 24562306a36Sopenharmony_ci 24662306a36Sopenharmony_ci /* Calibrate the soc and fcc on a fully charged battery */ 24762306a36Sopenharmony_ci 24862306a36Sopenharmony_ci if (charger->charge_status == CHARGE_FINISH && (!charger->soc_cal)) { 24962306a36Sopenharmony_ci /* 25062306a36Sopenharmony_ci * soc should be 100000 and columb counter should show the full 25162306a36Sopenharmony_ci * charge capacity. Note that if the device is unplugged for a 25262306a36Sopenharmony_ci * period of several days the columb counter will have a large 25362306a36Sopenharmony_ci * margin of error, so setting it back to the full charge on 25462306a36Sopenharmony_ci * a completed charge cycle should correct this (my device was 25562306a36Sopenharmony_ci * showing 33% battery after 3 days unplugged when it should 25662306a36Sopenharmony_ci * have been closer to 95% based on voltage and charge 25762306a36Sopenharmony_ci * current). 25862306a36Sopenharmony_ci */ 25962306a36Sopenharmony_ci 26062306a36Sopenharmony_ci charger->soc = 100000; 26162306a36Sopenharmony_ci charge_now_adc = CHARGE_TO_ADC(charger->fcc_mah, 26262306a36Sopenharmony_ci charger->res_div); 26362306a36Sopenharmony_ci put_unaligned_be32(charge_now_adc, bulk_reg); 26462306a36Sopenharmony_ci regmap_bulk_write(rk808->regmap, RK817_GAS_GAUGE_Q_INIT_H3, 26562306a36Sopenharmony_ci bulk_reg, 4); 26662306a36Sopenharmony_ci 26762306a36Sopenharmony_ci charger->soc_cal = 1; 26862306a36Sopenharmony_ci dev_dbg(charger->dev, 26962306a36Sopenharmony_ci "Fully charged. SOC is %d, full capacity is %d\n", 27062306a36Sopenharmony_ci charger->soc, charger->fcc_mah * 1000); 27162306a36Sopenharmony_ci } 27262306a36Sopenharmony_ci 27362306a36Sopenharmony_ci /* 27462306a36Sopenharmony_ci * The columb counter can drift up slightly, so we should correct for 27562306a36Sopenharmony_ci * it. But don't correct it until we're at 100% soc. 27662306a36Sopenharmony_ci */ 27762306a36Sopenharmony_ci if (charger->charge_status == CHARGE_FINISH && charger->soc_cal) { 27862306a36Sopenharmony_ci regmap_bulk_read(rk808->regmap, RK817_GAS_GAUGE_Q_PRES_H3, 27962306a36Sopenharmony_ci bulk_reg, 4); 28062306a36Sopenharmony_ci charge_now_adc = get_unaligned_be32(bulk_reg); 28162306a36Sopenharmony_ci if (charge_now_adc < 0) 28262306a36Sopenharmony_ci return charge_now_adc; 28362306a36Sopenharmony_ci charge_now = ADC_TO_CHARGE_UAH(charge_now_adc, 28462306a36Sopenharmony_ci charger->res_div); 28562306a36Sopenharmony_ci 28662306a36Sopenharmony_ci /* 28762306a36Sopenharmony_ci * Re-init columb counter with updated values to correct drift. 28862306a36Sopenharmony_ci */ 28962306a36Sopenharmony_ci if (charge_now / 1000 > charger->fcc_mah) { 29062306a36Sopenharmony_ci dev_dbg(charger->dev, 29162306a36Sopenharmony_ci "Recalibrating columb counter to %d uah\n", 29262306a36Sopenharmony_ci charge_now); 29362306a36Sopenharmony_ci /* 29462306a36Sopenharmony_ci * Order of operations matters here to ensure we keep 29562306a36Sopenharmony_ci * enough precision until the last step to keep from 29662306a36Sopenharmony_ci * making needless updates to columb counter. 29762306a36Sopenharmony_ci */ 29862306a36Sopenharmony_ci charge_now_adc = CHARGE_TO_ADC(charger->fcc_mah, 29962306a36Sopenharmony_ci charger->res_div); 30062306a36Sopenharmony_ci put_unaligned_be32(charge_now_adc, bulk_reg); 30162306a36Sopenharmony_ci regmap_bulk_write(rk808->regmap, 30262306a36Sopenharmony_ci RK817_GAS_GAUGE_Q_INIT_H3, 30362306a36Sopenharmony_ci bulk_reg, 4); 30462306a36Sopenharmony_ci } 30562306a36Sopenharmony_ci } 30662306a36Sopenharmony_ci 30762306a36Sopenharmony_ci /* 30862306a36Sopenharmony_ci * Calibrate the fully charged capacity when we previously had a full 30962306a36Sopenharmony_ci * battery (soc_cal = 1) and are now empty (at or below minimum design 31062306a36Sopenharmony_ci * voltage). If our columb counter is still positive, subtract that 31162306a36Sopenharmony_ci * from our fcc value to get a calibrated fcc, and if our columb 31262306a36Sopenharmony_ci * counter is negative add that to our fcc (but not to exceed our 31362306a36Sopenharmony_ci * design capacity). 31462306a36Sopenharmony_ci */ 31562306a36Sopenharmony_ci regmap_bulk_read(charger->rk808->regmap, RK817_GAS_GAUGE_BAT_VOL_H, 31662306a36Sopenharmony_ci bulk_reg, 2); 31762306a36Sopenharmony_ci tmp = get_unaligned_be16(bulk_reg); 31862306a36Sopenharmony_ci volt_avg = (charger->voltage_k * tmp) + 1000 * charger->voltage_b; 31962306a36Sopenharmony_ci if (volt_avg <= charger->bat_voltage_min_design_uv && 32062306a36Sopenharmony_ci charger->soc_cal) { 32162306a36Sopenharmony_ci regmap_bulk_read(rk808->regmap, RK817_GAS_GAUGE_Q_PRES_H3, 32262306a36Sopenharmony_ci bulk_reg, 4); 32362306a36Sopenharmony_ci charge_now_adc = get_unaligned_be32(bulk_reg); 32462306a36Sopenharmony_ci charge_now = ADC_TO_CHARGE_UAH(charge_now_adc, 32562306a36Sopenharmony_ci charger->res_div); 32662306a36Sopenharmony_ci /* 32762306a36Sopenharmony_ci * Note, if charge_now is negative this will add it (what we 32862306a36Sopenharmony_ci * want) and if it's positive this will subtract (also what 32962306a36Sopenharmony_ci * we want). 33062306a36Sopenharmony_ci */ 33162306a36Sopenharmony_ci charger->fcc_mah = charger->fcc_mah - (charge_now / 1000); 33262306a36Sopenharmony_ci 33362306a36Sopenharmony_ci dev_dbg(charger->dev, 33462306a36Sopenharmony_ci "Recalibrating full charge capacity to %d uah\n", 33562306a36Sopenharmony_ci charger->fcc_mah * 1000); 33662306a36Sopenharmony_ci } 33762306a36Sopenharmony_ci 33862306a36Sopenharmony_ci /* 33962306a36Sopenharmony_ci * Set the SOC to 0 if we are below the minimum system voltage. 34062306a36Sopenharmony_ci */ 34162306a36Sopenharmony_ci if (volt_avg <= charger->bat_voltage_min_design_uv) { 34262306a36Sopenharmony_ci charger->soc = 0; 34362306a36Sopenharmony_ci charge_now_adc = CHARGE_TO_ADC(0, charger->res_div); 34462306a36Sopenharmony_ci put_unaligned_be32(charge_now_adc, bulk_reg); 34562306a36Sopenharmony_ci regmap_bulk_write(rk808->regmap, 34662306a36Sopenharmony_ci RK817_GAS_GAUGE_Q_INIT_H3, bulk_reg, 4); 34762306a36Sopenharmony_ci dev_warn(charger->dev, 34862306a36Sopenharmony_ci "Battery voltage %d below minimum voltage %d\n", 34962306a36Sopenharmony_ci volt_avg, charger->bat_voltage_min_design_uv); 35062306a36Sopenharmony_ci } 35162306a36Sopenharmony_ci 35262306a36Sopenharmony_ci rk817_record_battery_nvram_values(charger); 35362306a36Sopenharmony_ci 35462306a36Sopenharmony_ci return 0; 35562306a36Sopenharmony_ci} 35662306a36Sopenharmony_ci 35762306a36Sopenharmony_cistatic void rk817_read_props(struct rk817_charger *charger) 35862306a36Sopenharmony_ci{ 35962306a36Sopenharmony_ci int tmp, reg; 36062306a36Sopenharmony_ci u8 bulk_reg[4]; 36162306a36Sopenharmony_ci 36262306a36Sopenharmony_ci /* 36362306a36Sopenharmony_ci * Recalibrate voltage and current readings if we need to BSP does both 36462306a36Sopenharmony_ci * on CUR_CALIB_UPD, ignoring VOL_CALIB_UPD. Curiously enough, both 36562306a36Sopenharmony_ci * documentation and the BSP show that you perform an update if bit 7 36662306a36Sopenharmony_ci * is 1, but you clear the status by writing a 1 to bit 7. 36762306a36Sopenharmony_ci */ 36862306a36Sopenharmony_ci regmap_read(charger->rk808->regmap, RK817_GAS_GAUGE_ADC_CONFIG1, ®); 36962306a36Sopenharmony_ci if (reg & RK817_VOL_CUR_CALIB_UPD) { 37062306a36Sopenharmony_ci rk817_bat_calib_cur(charger); 37162306a36Sopenharmony_ci rk817_bat_calib_vol(charger); 37262306a36Sopenharmony_ci regmap_write_bits(charger->rk808->regmap, 37362306a36Sopenharmony_ci RK817_GAS_GAUGE_ADC_CONFIG1, 37462306a36Sopenharmony_ci RK817_VOL_CUR_CALIB_UPD, 37562306a36Sopenharmony_ci RK817_VOL_CUR_CALIB_UPD); 37662306a36Sopenharmony_ci } 37762306a36Sopenharmony_ci 37862306a36Sopenharmony_ci /* Update reported charge. */ 37962306a36Sopenharmony_ci regmap_bulk_read(charger->rk808->regmap, RK817_GAS_GAUGE_Q_PRES_H3, 38062306a36Sopenharmony_ci bulk_reg, 4); 38162306a36Sopenharmony_ci tmp = get_unaligned_be32(bulk_reg); 38262306a36Sopenharmony_ci charger->charge_now_uah = ADC_TO_CHARGE_UAH(tmp, charger->res_div); 38362306a36Sopenharmony_ci if (charger->charge_now_uah < 0) 38462306a36Sopenharmony_ci charger->charge_now_uah = 0; 38562306a36Sopenharmony_ci if (charger->charge_now_uah > charger->fcc_mah * 1000) 38662306a36Sopenharmony_ci charger->charge_now_uah = charger->fcc_mah * 1000; 38762306a36Sopenharmony_ci 38862306a36Sopenharmony_ci /* Update soc based on reported charge. */ 38962306a36Sopenharmony_ci charger->soc = charger->charge_now_uah * 100 / charger->fcc_mah; 39062306a36Sopenharmony_ci 39162306a36Sopenharmony_ci /* Update reported voltage. */ 39262306a36Sopenharmony_ci regmap_bulk_read(charger->rk808->regmap, RK817_GAS_GAUGE_BAT_VOL_H, 39362306a36Sopenharmony_ci bulk_reg, 2); 39462306a36Sopenharmony_ci tmp = get_unaligned_be16(bulk_reg); 39562306a36Sopenharmony_ci charger->volt_avg_uv = (charger->voltage_k * tmp) + 1000 * 39662306a36Sopenharmony_ci charger->voltage_b; 39762306a36Sopenharmony_ci 39862306a36Sopenharmony_ci /* 39962306a36Sopenharmony_ci * Update reported current. Note value from registers is a signed 16 40062306a36Sopenharmony_ci * bit int. 40162306a36Sopenharmony_ci */ 40262306a36Sopenharmony_ci regmap_bulk_read(charger->rk808->regmap, RK817_GAS_GAUGE_BAT_CUR_H, 40362306a36Sopenharmony_ci bulk_reg, 2); 40462306a36Sopenharmony_ci tmp = (short int)get_unaligned_be16(bulk_reg); 40562306a36Sopenharmony_ci charger->cur_avg_ua = ADC_TO_CURRENT(tmp, charger->res_div); 40662306a36Sopenharmony_ci 40762306a36Sopenharmony_ci /* 40862306a36Sopenharmony_ci * Update the max charge current. This value shouldn't change, but we 40962306a36Sopenharmony_ci * can read it to report what the PMIC says it is instead of simply 41062306a36Sopenharmony_ci * returning the default value. 41162306a36Sopenharmony_ci */ 41262306a36Sopenharmony_ci regmap_read(charger->rk808->regmap, RK817_PMIC_CHRG_OUT, ®); 41362306a36Sopenharmony_ci charger->max_chg_cur_ua = 41462306a36Sopenharmony_ci rk817_chg_cur_from_reg(reg & RK817_CHRG_CUR_SEL); 41562306a36Sopenharmony_ci 41662306a36Sopenharmony_ci /* 41762306a36Sopenharmony_ci * Update max charge voltage. Like the max charge current this value 41862306a36Sopenharmony_ci * shouldn't change, but we can report what the PMIC says. 41962306a36Sopenharmony_ci */ 42062306a36Sopenharmony_ci regmap_read(charger->rk808->regmap, RK817_PMIC_CHRG_OUT, ®); 42162306a36Sopenharmony_ci charger->max_chg_volt_uv = ((((reg & RK817_CHRG_VOL_SEL) >> 4) * 42262306a36Sopenharmony_ci 50000) + 4100000); 42362306a36Sopenharmony_ci 42462306a36Sopenharmony_ci /* Check if battery still present. */ 42562306a36Sopenharmony_ci regmap_read(charger->rk808->regmap, RK817_PMIC_CHRG_STS, ®); 42662306a36Sopenharmony_ci charger->battery_present = (reg & RK817_BAT_EXS); 42762306a36Sopenharmony_ci 42862306a36Sopenharmony_ci /* Get which type of charge we are using (if any). */ 42962306a36Sopenharmony_ci regmap_read(charger->rk808->regmap, RK817_PMIC_CHRG_STS, ®); 43062306a36Sopenharmony_ci charger->charge_status = (reg >> 4) & 0x07; 43162306a36Sopenharmony_ci 43262306a36Sopenharmony_ci /* 43362306a36Sopenharmony_ci * Get charger input voltage. Note that on my example hardware (an 43462306a36Sopenharmony_ci * Odroid Go Advance) the voltage of the power connector is measured 43562306a36Sopenharmony_ci * on the register labelled USB in the datasheet; I don't know if this 43662306a36Sopenharmony_ci * is how it is designed or just a quirk of the implementation. I 43762306a36Sopenharmony_ci * believe this will also measure the voltage of the USB output when in 43862306a36Sopenharmony_ci * OTG mode, if that is the case we may need to change this in the 43962306a36Sopenharmony_ci * future to return 0 if the power supply status is offline (I can't 44062306a36Sopenharmony_ci * test this with my current implementation. Also, when the voltage 44162306a36Sopenharmony_ci * should be zero sometimes the ADC still shows a single bit (which 44262306a36Sopenharmony_ci * would register as 20000uv). When this happens set it to 0. 44362306a36Sopenharmony_ci */ 44462306a36Sopenharmony_ci regmap_bulk_read(charger->rk808->regmap, RK817_GAS_GAUGE_USB_VOL_H, 44562306a36Sopenharmony_ci bulk_reg, 2); 44662306a36Sopenharmony_ci reg = get_unaligned_be16(bulk_reg); 44762306a36Sopenharmony_ci if (reg > 1) { 44862306a36Sopenharmony_ci tmp = ((charger->voltage_k * reg / 1000 + charger->voltage_b) * 44962306a36Sopenharmony_ci 60 / 46); 45062306a36Sopenharmony_ci charger->charger_input_volt_avg_uv = tmp * 1000; 45162306a36Sopenharmony_ci } else { 45262306a36Sopenharmony_ci charger->charger_input_volt_avg_uv = 0; 45362306a36Sopenharmony_ci } 45462306a36Sopenharmony_ci 45562306a36Sopenharmony_ci /* Calibrate battery capacity and soc. */ 45662306a36Sopenharmony_ci rk817_bat_calib_cap(charger); 45762306a36Sopenharmony_ci} 45862306a36Sopenharmony_ci 45962306a36Sopenharmony_cistatic int rk817_bat_get_prop(struct power_supply *ps, 46062306a36Sopenharmony_ci enum power_supply_property prop, 46162306a36Sopenharmony_ci union power_supply_propval *val) 46262306a36Sopenharmony_ci{ 46362306a36Sopenharmony_ci struct rk817_charger *charger = power_supply_get_drvdata(ps); 46462306a36Sopenharmony_ci 46562306a36Sopenharmony_ci switch (prop) { 46662306a36Sopenharmony_ci case POWER_SUPPLY_PROP_PRESENT: 46762306a36Sopenharmony_ci val->intval = charger->battery_present; 46862306a36Sopenharmony_ci break; 46962306a36Sopenharmony_ci case POWER_SUPPLY_PROP_STATUS: 47062306a36Sopenharmony_ci if (charger->cur_avg_ua < 0) { 47162306a36Sopenharmony_ci val->intval = POWER_SUPPLY_STATUS_DISCHARGING; 47262306a36Sopenharmony_ci break; 47362306a36Sopenharmony_ci } 47462306a36Sopenharmony_ci switch (charger->charge_status) { 47562306a36Sopenharmony_ci case CHRG_OFF: 47662306a36Sopenharmony_ci val->intval = POWER_SUPPLY_STATUS_NOT_CHARGING; 47762306a36Sopenharmony_ci break; 47862306a36Sopenharmony_ci /* 47962306a36Sopenharmony_ci * Dead charge is documented, but not explained. I never 48062306a36Sopenharmony_ci * observed it but assume it's a pre-charge for a dead 48162306a36Sopenharmony_ci * battery. 48262306a36Sopenharmony_ci */ 48362306a36Sopenharmony_ci case DEAD_CHRG: 48462306a36Sopenharmony_ci case TRICKLE_CHRG: 48562306a36Sopenharmony_ci case CC_OR_CV_CHRG: 48662306a36Sopenharmony_ci val->intval = POWER_SUPPLY_STATUS_CHARGING; 48762306a36Sopenharmony_ci break; 48862306a36Sopenharmony_ci case CHARGE_FINISH: 48962306a36Sopenharmony_ci val->intval = POWER_SUPPLY_STATUS_FULL; 49062306a36Sopenharmony_ci break; 49162306a36Sopenharmony_ci default: 49262306a36Sopenharmony_ci val->intval = POWER_SUPPLY_STATUS_UNKNOWN; 49362306a36Sopenharmony_ci return -EINVAL; 49462306a36Sopenharmony_ci 49562306a36Sopenharmony_ci } 49662306a36Sopenharmony_ci break; 49762306a36Sopenharmony_ci case POWER_SUPPLY_PROP_CHARGE_TYPE: 49862306a36Sopenharmony_ci switch (charger->charge_status) { 49962306a36Sopenharmony_ci case CHRG_OFF: 50062306a36Sopenharmony_ci case CHARGE_FINISH: 50162306a36Sopenharmony_ci val->intval = POWER_SUPPLY_CHARGE_TYPE_NONE; 50262306a36Sopenharmony_ci break; 50362306a36Sopenharmony_ci case TRICKLE_CHRG: 50462306a36Sopenharmony_ci val->intval = POWER_SUPPLY_CHARGE_TYPE_TRICKLE; 50562306a36Sopenharmony_ci break; 50662306a36Sopenharmony_ci case DEAD_CHRG: 50762306a36Sopenharmony_ci case CC_OR_CV_CHRG: 50862306a36Sopenharmony_ci val->intval = POWER_SUPPLY_CHARGE_TYPE_STANDARD; 50962306a36Sopenharmony_ci break; 51062306a36Sopenharmony_ci default: 51162306a36Sopenharmony_ci val->intval = POWER_SUPPLY_CHARGE_TYPE_UNKNOWN; 51262306a36Sopenharmony_ci break; 51362306a36Sopenharmony_ci } 51462306a36Sopenharmony_ci break; 51562306a36Sopenharmony_ci case POWER_SUPPLY_PROP_CHARGE_FULL: 51662306a36Sopenharmony_ci val->intval = charger->fcc_mah * 1000; 51762306a36Sopenharmony_ci break; 51862306a36Sopenharmony_ci case POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN: 51962306a36Sopenharmony_ci val->intval = charger->bat_charge_full_design_uah; 52062306a36Sopenharmony_ci break; 52162306a36Sopenharmony_ci case POWER_SUPPLY_PROP_CHARGE_EMPTY_DESIGN: 52262306a36Sopenharmony_ci val->intval = 0; 52362306a36Sopenharmony_ci break; 52462306a36Sopenharmony_ci case POWER_SUPPLY_PROP_CHARGE_NOW: 52562306a36Sopenharmony_ci val->intval = charger->charge_now_uah; 52662306a36Sopenharmony_ci break; 52762306a36Sopenharmony_ci case POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN: 52862306a36Sopenharmony_ci val->intval = charger->bat_voltage_min_design_uv; 52962306a36Sopenharmony_ci break; 53062306a36Sopenharmony_ci case POWER_SUPPLY_PROP_CAPACITY: 53162306a36Sopenharmony_ci /* Add 500 so that values like 99999 are 100% not 99%. */ 53262306a36Sopenharmony_ci val->intval = (charger->soc + 500) / 1000; 53362306a36Sopenharmony_ci if (val->intval > 100) 53462306a36Sopenharmony_ci val->intval = 100; 53562306a36Sopenharmony_ci if (val->intval < 0) 53662306a36Sopenharmony_ci val->intval = 0; 53762306a36Sopenharmony_ci break; 53862306a36Sopenharmony_ci case POWER_SUPPLY_PROP_VOLTAGE_AVG: 53962306a36Sopenharmony_ci val->intval = charger->volt_avg_uv; 54062306a36Sopenharmony_ci break; 54162306a36Sopenharmony_ci case POWER_SUPPLY_PROP_CURRENT_AVG: 54262306a36Sopenharmony_ci val->intval = charger->cur_avg_ua; 54362306a36Sopenharmony_ci break; 54462306a36Sopenharmony_ci case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX: 54562306a36Sopenharmony_ci val->intval = charger->max_chg_cur_ua; 54662306a36Sopenharmony_ci break; 54762306a36Sopenharmony_ci case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE_MAX: 54862306a36Sopenharmony_ci val->intval = charger->max_chg_volt_uv; 54962306a36Sopenharmony_ci break; 55062306a36Sopenharmony_ci case POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN: 55162306a36Sopenharmony_ci val->intval = charger->bat_voltage_max_design_uv; 55262306a36Sopenharmony_ci break; 55362306a36Sopenharmony_ci default: 55462306a36Sopenharmony_ci return -EINVAL; 55562306a36Sopenharmony_ci } 55662306a36Sopenharmony_ci return 0; 55762306a36Sopenharmony_ci} 55862306a36Sopenharmony_ci 55962306a36Sopenharmony_cistatic int rk817_chg_get_prop(struct power_supply *ps, 56062306a36Sopenharmony_ci enum power_supply_property prop, 56162306a36Sopenharmony_ci union power_supply_propval *val) 56262306a36Sopenharmony_ci{ 56362306a36Sopenharmony_ci struct rk817_charger *charger = power_supply_get_drvdata(ps); 56462306a36Sopenharmony_ci 56562306a36Sopenharmony_ci switch (prop) { 56662306a36Sopenharmony_ci case POWER_SUPPLY_PROP_ONLINE: 56762306a36Sopenharmony_ci val->intval = charger->plugged_in; 56862306a36Sopenharmony_ci break; 56962306a36Sopenharmony_ci case POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN: 57062306a36Sopenharmony_ci /* max voltage from datasheet at 5.5v (default 5.0v) */ 57162306a36Sopenharmony_ci val->intval = 5500000; 57262306a36Sopenharmony_ci break; 57362306a36Sopenharmony_ci case POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN: 57462306a36Sopenharmony_ci /* min voltage from datasheet at 3.8v (default 5.0v) */ 57562306a36Sopenharmony_ci val->intval = 3800000; 57662306a36Sopenharmony_ci break; 57762306a36Sopenharmony_ci case POWER_SUPPLY_PROP_VOLTAGE_AVG: 57862306a36Sopenharmony_ci val->intval = charger->charger_input_volt_avg_uv; 57962306a36Sopenharmony_ci break; 58062306a36Sopenharmony_ci /* 58162306a36Sopenharmony_ci * While it's possible that other implementations could use different 58262306a36Sopenharmony_ci * USB types, the current implementation for this PMIC (the Odroid Go 58362306a36Sopenharmony_ci * Advance) only uses a dedicated charging port with no rx/tx lines. 58462306a36Sopenharmony_ci */ 58562306a36Sopenharmony_ci case POWER_SUPPLY_PROP_USB_TYPE: 58662306a36Sopenharmony_ci val->intval = POWER_SUPPLY_USB_TYPE_DCP; 58762306a36Sopenharmony_ci break; 58862306a36Sopenharmony_ci default: 58962306a36Sopenharmony_ci return -EINVAL; 59062306a36Sopenharmony_ci } 59162306a36Sopenharmony_ci return 0; 59262306a36Sopenharmony_ci 59362306a36Sopenharmony_ci} 59462306a36Sopenharmony_ci 59562306a36Sopenharmony_cistatic irqreturn_t rk817_plug_in_isr(int irq, void *cg) 59662306a36Sopenharmony_ci{ 59762306a36Sopenharmony_ci struct rk817_charger *charger; 59862306a36Sopenharmony_ci 59962306a36Sopenharmony_ci charger = (struct rk817_charger *)cg; 60062306a36Sopenharmony_ci charger->plugged_in = 1; 60162306a36Sopenharmony_ci power_supply_changed(charger->chg_ps); 60262306a36Sopenharmony_ci power_supply_changed(charger->bat_ps); 60362306a36Sopenharmony_ci /* try to recalibrate capacity if we hit full charge. */ 60462306a36Sopenharmony_ci charger->soc_cal = 0; 60562306a36Sopenharmony_ci 60662306a36Sopenharmony_ci rk817_read_props(charger); 60762306a36Sopenharmony_ci 60862306a36Sopenharmony_ci dev_dbg(charger->dev, "Power Cord Inserted\n"); 60962306a36Sopenharmony_ci 61062306a36Sopenharmony_ci return IRQ_HANDLED; 61162306a36Sopenharmony_ci} 61262306a36Sopenharmony_ci 61362306a36Sopenharmony_cistatic irqreturn_t rk817_plug_out_isr(int irq, void *cg) 61462306a36Sopenharmony_ci{ 61562306a36Sopenharmony_ci struct rk817_charger *charger; 61662306a36Sopenharmony_ci struct rk808 *rk808; 61762306a36Sopenharmony_ci 61862306a36Sopenharmony_ci charger = (struct rk817_charger *)cg; 61962306a36Sopenharmony_ci rk808 = charger->rk808; 62062306a36Sopenharmony_ci charger->plugged_in = 0; 62162306a36Sopenharmony_ci power_supply_changed(charger->bat_ps); 62262306a36Sopenharmony_ci power_supply_changed(charger->chg_ps); 62362306a36Sopenharmony_ci 62462306a36Sopenharmony_ci /* 62562306a36Sopenharmony_ci * For some reason the bits of RK817_PMIC_CHRG_IN reset whenever the 62662306a36Sopenharmony_ci * power cord is unplugged. This was not documented in the BSP kernel 62762306a36Sopenharmony_ci * or the datasheet and only discovered by trial and error. Set minimum 62862306a36Sopenharmony_ci * USB input voltage to 4.5v and enable USB voltage input limit. 62962306a36Sopenharmony_ci */ 63062306a36Sopenharmony_ci regmap_write_bits(rk808->regmap, RK817_PMIC_CHRG_IN, 63162306a36Sopenharmony_ci RK817_USB_VLIM_SEL, (0x05 << 4)); 63262306a36Sopenharmony_ci regmap_write_bits(rk808->regmap, RK817_PMIC_CHRG_IN, RK817_USB_VLIM_EN, 63362306a36Sopenharmony_ci (0x01 << 7)); 63462306a36Sopenharmony_ci 63562306a36Sopenharmony_ci /* 63662306a36Sopenharmony_ci * Set average USB input current limit to 1.5A and enable USB current 63762306a36Sopenharmony_ci * input limit. 63862306a36Sopenharmony_ci */ 63962306a36Sopenharmony_ci regmap_write_bits(rk808->regmap, RK817_PMIC_CHRG_IN, 64062306a36Sopenharmony_ci RK817_USB_ILIM_SEL, 0x03); 64162306a36Sopenharmony_ci regmap_write_bits(rk808->regmap, RK817_PMIC_CHRG_IN, RK817_USB_ILIM_EN, 64262306a36Sopenharmony_ci (0x01 << 3)); 64362306a36Sopenharmony_ci 64462306a36Sopenharmony_ci rk817_read_props(charger); 64562306a36Sopenharmony_ci 64662306a36Sopenharmony_ci dev_dbg(charger->dev, "Power Cord Removed\n"); 64762306a36Sopenharmony_ci 64862306a36Sopenharmony_ci return IRQ_HANDLED; 64962306a36Sopenharmony_ci} 65062306a36Sopenharmony_ci 65162306a36Sopenharmony_cistatic enum power_supply_property rk817_bat_props[] = { 65262306a36Sopenharmony_ci POWER_SUPPLY_PROP_PRESENT, 65362306a36Sopenharmony_ci POWER_SUPPLY_PROP_STATUS, 65462306a36Sopenharmony_ci POWER_SUPPLY_PROP_CHARGE_TYPE, 65562306a36Sopenharmony_ci POWER_SUPPLY_PROP_CHARGE_FULL, 65662306a36Sopenharmony_ci POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN, 65762306a36Sopenharmony_ci POWER_SUPPLY_PROP_CHARGE_EMPTY_DESIGN, 65862306a36Sopenharmony_ci POWER_SUPPLY_PROP_CHARGE_NOW, 65962306a36Sopenharmony_ci POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE_MAX, 66062306a36Sopenharmony_ci POWER_SUPPLY_PROP_VOLTAGE_AVG, 66162306a36Sopenharmony_ci POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX, 66262306a36Sopenharmony_ci POWER_SUPPLY_PROP_CURRENT_AVG, 66362306a36Sopenharmony_ci POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN, 66462306a36Sopenharmony_ci POWER_SUPPLY_PROP_CAPACITY, 66562306a36Sopenharmony_ci POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN, 66662306a36Sopenharmony_ci}; 66762306a36Sopenharmony_ci 66862306a36Sopenharmony_cistatic enum power_supply_property rk817_chg_props[] = { 66962306a36Sopenharmony_ci POWER_SUPPLY_PROP_ONLINE, 67062306a36Sopenharmony_ci POWER_SUPPLY_PROP_USB_TYPE, 67162306a36Sopenharmony_ci POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN, 67262306a36Sopenharmony_ci POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN, 67362306a36Sopenharmony_ci POWER_SUPPLY_PROP_VOLTAGE_AVG, 67462306a36Sopenharmony_ci}; 67562306a36Sopenharmony_ci 67662306a36Sopenharmony_cistatic enum power_supply_usb_type rk817_usb_type[] = { 67762306a36Sopenharmony_ci POWER_SUPPLY_USB_TYPE_DCP, 67862306a36Sopenharmony_ci POWER_SUPPLY_USB_TYPE_UNKNOWN, 67962306a36Sopenharmony_ci}; 68062306a36Sopenharmony_ci 68162306a36Sopenharmony_cistatic const struct power_supply_desc rk817_bat_desc = { 68262306a36Sopenharmony_ci .name = "rk817-battery", 68362306a36Sopenharmony_ci .type = POWER_SUPPLY_TYPE_BATTERY, 68462306a36Sopenharmony_ci .properties = rk817_bat_props, 68562306a36Sopenharmony_ci .num_properties = ARRAY_SIZE(rk817_bat_props), 68662306a36Sopenharmony_ci .get_property = rk817_bat_get_prop, 68762306a36Sopenharmony_ci}; 68862306a36Sopenharmony_ci 68962306a36Sopenharmony_cistatic const struct power_supply_desc rk817_chg_desc = { 69062306a36Sopenharmony_ci .name = "rk817-charger", 69162306a36Sopenharmony_ci .type = POWER_SUPPLY_TYPE_USB, 69262306a36Sopenharmony_ci .usb_types = rk817_usb_type, 69362306a36Sopenharmony_ci .num_usb_types = ARRAY_SIZE(rk817_usb_type), 69462306a36Sopenharmony_ci .properties = rk817_chg_props, 69562306a36Sopenharmony_ci .num_properties = ARRAY_SIZE(rk817_chg_props), 69662306a36Sopenharmony_ci .get_property = rk817_chg_get_prop, 69762306a36Sopenharmony_ci}; 69862306a36Sopenharmony_ci 69962306a36Sopenharmony_cistatic int rk817_read_battery_nvram_values(struct rk817_charger *charger) 70062306a36Sopenharmony_ci{ 70162306a36Sopenharmony_ci u8 bulk_reg[3]; 70262306a36Sopenharmony_ci int ret; 70362306a36Sopenharmony_ci 70462306a36Sopenharmony_ci /* Read the nvram data for full charge capacity. */ 70562306a36Sopenharmony_ci ret = regmap_bulk_read(charger->rk808->regmap, 70662306a36Sopenharmony_ci RK817_GAS_GAUGE_DATA3, bulk_reg, 3); 70762306a36Sopenharmony_ci if (ret < 0) 70862306a36Sopenharmony_ci return ret; 70962306a36Sopenharmony_ci charger->fcc_mah = get_unaligned_le24(bulk_reg); 71062306a36Sopenharmony_ci 71162306a36Sopenharmony_ci /* 71262306a36Sopenharmony_ci * Sanity checking for values equal to zero or less than would be 71362306a36Sopenharmony_ci * practical for this device (BSP Kernel assumes 500mAH or less) for 71462306a36Sopenharmony_ci * practicality purposes. Also check if the value is too large and 71562306a36Sopenharmony_ci * correct it. 71662306a36Sopenharmony_ci */ 71762306a36Sopenharmony_ci if ((charger->fcc_mah < 500) || 71862306a36Sopenharmony_ci ((charger->fcc_mah * 1000) > charger->bat_charge_full_design_uah)) { 71962306a36Sopenharmony_ci dev_info(charger->dev, 72062306a36Sopenharmony_ci "Invalid NVRAM max charge, setting to %u uAH\n", 72162306a36Sopenharmony_ci charger->bat_charge_full_design_uah); 72262306a36Sopenharmony_ci charger->fcc_mah = charger->bat_charge_full_design_uah / 1000; 72362306a36Sopenharmony_ci } 72462306a36Sopenharmony_ci 72562306a36Sopenharmony_ci /* 72662306a36Sopenharmony_ci * Read the nvram for state of charge. Sanity check for values greater 72762306a36Sopenharmony_ci * than 100 (10000) or less than 0, because other things (BSP kernels, 72862306a36Sopenharmony_ci * U-Boot, or even i2cset) can write to this register. If the value is 72962306a36Sopenharmony_ci * off it should get corrected automatically when the voltage drops to 73062306a36Sopenharmony_ci * the min (soc is 0) or when the battery is full (soc is 100). 73162306a36Sopenharmony_ci */ 73262306a36Sopenharmony_ci ret = regmap_bulk_read(charger->rk808->regmap, 73362306a36Sopenharmony_ci RK817_GAS_GAUGE_BAT_R1, bulk_reg, 3); 73462306a36Sopenharmony_ci if (ret < 0) 73562306a36Sopenharmony_ci return ret; 73662306a36Sopenharmony_ci charger->soc = get_unaligned_le24(bulk_reg); 73762306a36Sopenharmony_ci if (charger->soc > 10000) 73862306a36Sopenharmony_ci charger->soc = 10000; 73962306a36Sopenharmony_ci if (charger->soc < 0) 74062306a36Sopenharmony_ci charger->soc = 0; 74162306a36Sopenharmony_ci 74262306a36Sopenharmony_ci return 0; 74362306a36Sopenharmony_ci} 74462306a36Sopenharmony_ci 74562306a36Sopenharmony_cistatic int 74662306a36Sopenharmony_cirk817_read_or_set_full_charge_on_boot(struct rk817_charger *charger, 74762306a36Sopenharmony_ci struct power_supply_battery_info *bat_info) 74862306a36Sopenharmony_ci{ 74962306a36Sopenharmony_ci struct rk808 *rk808 = charger->rk808; 75062306a36Sopenharmony_ci u8 bulk_reg[4]; 75162306a36Sopenharmony_ci u32 boot_voltage, boot_charge_mah; 75262306a36Sopenharmony_ci int ret, reg, off_time, tmp; 75362306a36Sopenharmony_ci bool first_boot; 75462306a36Sopenharmony_ci 75562306a36Sopenharmony_ci /* 75662306a36Sopenharmony_ci * Check if the battery is uninitalized. If it is, the columb counter 75762306a36Sopenharmony_ci * needs to be set up. 75862306a36Sopenharmony_ci */ 75962306a36Sopenharmony_ci ret = regmap_read(rk808->regmap, RK817_GAS_GAUGE_GG_STS, ®); 76062306a36Sopenharmony_ci if (ret < 0) 76162306a36Sopenharmony_ci return ret; 76262306a36Sopenharmony_ci first_boot = reg & RK817_BAT_CON; 76362306a36Sopenharmony_ci /* 76462306a36Sopenharmony_ci * If the battery is uninitialized, use the poweron voltage and an ocv 76562306a36Sopenharmony_ci * lookup to guess our charge. The number won't be very accurate until 76662306a36Sopenharmony_ci * we hit either our minimum voltage (0%) or full charge (100%). 76762306a36Sopenharmony_ci */ 76862306a36Sopenharmony_ci if (first_boot) { 76962306a36Sopenharmony_ci regmap_bulk_read(rk808->regmap, RK817_GAS_GAUGE_PWRON_VOL_H, 77062306a36Sopenharmony_ci bulk_reg, 2); 77162306a36Sopenharmony_ci tmp = get_unaligned_be16(bulk_reg); 77262306a36Sopenharmony_ci boot_voltage = (charger->voltage_k * tmp) + 77362306a36Sopenharmony_ci 1000 * charger->voltage_b; 77462306a36Sopenharmony_ci /* 77562306a36Sopenharmony_ci * Since only implementation has no working thermistor, assume 77662306a36Sopenharmony_ci * 20C for OCV lookup. If lookup fails, report error with OCV 77762306a36Sopenharmony_ci * table. 77862306a36Sopenharmony_ci */ 77962306a36Sopenharmony_ci charger->soc = power_supply_batinfo_ocv2cap(bat_info, 78062306a36Sopenharmony_ci boot_voltage, 78162306a36Sopenharmony_ci 20) * 1000; 78262306a36Sopenharmony_ci if (charger->soc < 0) 78362306a36Sopenharmony_ci charger->soc = 0; 78462306a36Sopenharmony_ci 78562306a36Sopenharmony_ci /* Guess that full charge capacity is the design capacity */ 78662306a36Sopenharmony_ci charger->fcc_mah = charger->bat_charge_full_design_uah / 1000; 78762306a36Sopenharmony_ci /* 78862306a36Sopenharmony_ci * Set battery as "set up". BSP driver uses this value even 78962306a36Sopenharmony_ci * though datasheet claims it's a read-only value. 79062306a36Sopenharmony_ci */ 79162306a36Sopenharmony_ci regmap_write_bits(rk808->regmap, RK817_GAS_GAUGE_GG_STS, 79262306a36Sopenharmony_ci RK817_BAT_CON, 0); 79362306a36Sopenharmony_ci /* Save nvram values */ 79462306a36Sopenharmony_ci ret = rk817_record_battery_nvram_values(charger); 79562306a36Sopenharmony_ci if (ret < 0) 79662306a36Sopenharmony_ci return ret; 79762306a36Sopenharmony_ci } else { 79862306a36Sopenharmony_ci ret = rk817_read_battery_nvram_values(charger); 79962306a36Sopenharmony_ci if (ret < 0) 80062306a36Sopenharmony_ci return ret; 80162306a36Sopenharmony_ci 80262306a36Sopenharmony_ci regmap_bulk_read(rk808->regmap, RK817_GAS_GAUGE_Q_PRES_H3, 80362306a36Sopenharmony_ci bulk_reg, 4); 80462306a36Sopenharmony_ci tmp = get_unaligned_be32(bulk_reg); 80562306a36Sopenharmony_ci if (tmp < 0) 80662306a36Sopenharmony_ci tmp = 0; 80762306a36Sopenharmony_ci boot_charge_mah = ADC_TO_CHARGE_UAH(tmp, 80862306a36Sopenharmony_ci charger->res_div) / 1000; 80962306a36Sopenharmony_ci /* 81062306a36Sopenharmony_ci * Check if the columb counter has been off for more than 30 81162306a36Sopenharmony_ci * minutes as it tends to drift downward. If so, re-init soc 81262306a36Sopenharmony_ci * with the boot voltage instead. Note the unit values for the 81362306a36Sopenharmony_ci * OFF_CNT register appear to be in decaminutes and stops 81462306a36Sopenharmony_ci * counting at 2550 (0xFF) minutes. BSP kernel used OCV, but 81562306a36Sopenharmony_ci * for me occasionally that would show invalid values. Boot 81662306a36Sopenharmony_ci * voltage is only accurate for me on first poweron (not 81762306a36Sopenharmony_ci * reboots), but we shouldn't ever encounter an OFF_CNT more 81862306a36Sopenharmony_ci * than 0 on a reboot anyway. 81962306a36Sopenharmony_ci */ 82062306a36Sopenharmony_ci regmap_read(rk808->regmap, RK817_GAS_GAUGE_OFF_CNT, &off_time); 82162306a36Sopenharmony_ci if (off_time >= 3) { 82262306a36Sopenharmony_ci regmap_bulk_read(rk808->regmap, 82362306a36Sopenharmony_ci RK817_GAS_GAUGE_PWRON_VOL_H, 82462306a36Sopenharmony_ci bulk_reg, 2); 82562306a36Sopenharmony_ci tmp = get_unaligned_be16(bulk_reg); 82662306a36Sopenharmony_ci boot_voltage = (charger->voltage_k * tmp) + 82762306a36Sopenharmony_ci 1000 * charger->voltage_b; 82862306a36Sopenharmony_ci charger->soc = 82962306a36Sopenharmony_ci power_supply_batinfo_ocv2cap(bat_info, 83062306a36Sopenharmony_ci boot_voltage, 83162306a36Sopenharmony_ci 20) * 1000; 83262306a36Sopenharmony_ci } else { 83362306a36Sopenharmony_ci charger->soc = (boot_charge_mah * 1000 * 100 / 83462306a36Sopenharmony_ci charger->fcc_mah); 83562306a36Sopenharmony_ci } 83662306a36Sopenharmony_ci } 83762306a36Sopenharmony_ci 83862306a36Sopenharmony_ci /* 83962306a36Sopenharmony_ci * Now we have our full charge capacity and soc, init the columb 84062306a36Sopenharmony_ci * counter. 84162306a36Sopenharmony_ci */ 84262306a36Sopenharmony_ci boot_charge_mah = charger->soc * charger->fcc_mah / 100 / 1000; 84362306a36Sopenharmony_ci if (boot_charge_mah > charger->fcc_mah) 84462306a36Sopenharmony_ci boot_charge_mah = charger->fcc_mah; 84562306a36Sopenharmony_ci tmp = CHARGE_TO_ADC(boot_charge_mah, charger->res_div); 84662306a36Sopenharmony_ci put_unaligned_be32(tmp, bulk_reg); 84762306a36Sopenharmony_ci ret = regmap_bulk_write(rk808->regmap, RK817_GAS_GAUGE_Q_INIT_H3, 84862306a36Sopenharmony_ci bulk_reg, 4); 84962306a36Sopenharmony_ci if (ret < 0) 85062306a36Sopenharmony_ci return ret; 85162306a36Sopenharmony_ci 85262306a36Sopenharmony_ci /* Set QMAX value to max design capacity. */ 85362306a36Sopenharmony_ci tmp = CHARGE_TO_ADC((charger->bat_charge_full_design_uah / 1000), 85462306a36Sopenharmony_ci charger->res_div); 85562306a36Sopenharmony_ci put_unaligned_be32(tmp, bulk_reg); 85662306a36Sopenharmony_ci ret = regmap_bulk_write(rk808->regmap, RK817_GAS_GAUGE_Q_MAX_H3, 85762306a36Sopenharmony_ci bulk_reg, 4); 85862306a36Sopenharmony_ci if (ret < 0) 85962306a36Sopenharmony_ci return ret; 86062306a36Sopenharmony_ci 86162306a36Sopenharmony_ci return 0; 86262306a36Sopenharmony_ci} 86362306a36Sopenharmony_ci 86462306a36Sopenharmony_cistatic int rk817_battery_init(struct rk817_charger *charger, 86562306a36Sopenharmony_ci struct power_supply_battery_info *bat_info) 86662306a36Sopenharmony_ci{ 86762306a36Sopenharmony_ci struct rk808 *rk808 = charger->rk808; 86862306a36Sopenharmony_ci u32 tmp, max_chg_vol_mv, max_chg_cur_ma; 86962306a36Sopenharmony_ci u8 max_chg_vol_reg, chg_term_i_reg; 87062306a36Sopenharmony_ci int ret, chg_term_ma, max_chg_cur_reg; 87162306a36Sopenharmony_ci u8 bulk_reg[2]; 87262306a36Sopenharmony_ci 87362306a36Sopenharmony_ci /* Get initial plug state */ 87462306a36Sopenharmony_ci regmap_read(rk808->regmap, RK817_SYS_STS, &tmp); 87562306a36Sopenharmony_ci charger->plugged_in = (tmp & RK817_PLUG_IN_STS); 87662306a36Sopenharmony_ci 87762306a36Sopenharmony_ci /* 87862306a36Sopenharmony_ci * Turn on all ADC functions to measure battery, USB, and sys voltage, 87962306a36Sopenharmony_ci * as well as batt temp. Note only tested implementation so far does 88062306a36Sopenharmony_ci * not use a battery with a thermistor. 88162306a36Sopenharmony_ci */ 88262306a36Sopenharmony_ci regmap_write(rk808->regmap, RK817_GAS_GAUGE_ADC_CONFIG0, 0xfc); 88362306a36Sopenharmony_ci 88462306a36Sopenharmony_ci /* 88562306a36Sopenharmony_ci * Set relax mode voltage sampling interval and ADC offset calibration 88662306a36Sopenharmony_ci * interval to 8 minutes to mirror BSP kernel. Set voltage and current 88762306a36Sopenharmony_ci * modes to average to mirror BSP kernel. 88862306a36Sopenharmony_ci */ 88962306a36Sopenharmony_ci regmap_write(rk808->regmap, RK817_GAS_GAUGE_GG_CON, 0x04); 89062306a36Sopenharmony_ci 89162306a36Sopenharmony_ci /* Calibrate voltage like the BSP does here. */ 89262306a36Sopenharmony_ci rk817_bat_calib_vol(charger); 89362306a36Sopenharmony_ci 89462306a36Sopenharmony_ci /* Write relax threshold, derived from sleep enter current. */ 89562306a36Sopenharmony_ci tmp = CURRENT_TO_ADC(charger->sleep_enter_current_ua, 89662306a36Sopenharmony_ci charger->res_div); 89762306a36Sopenharmony_ci put_unaligned_be16(tmp, bulk_reg); 89862306a36Sopenharmony_ci regmap_bulk_write(rk808->regmap, RK817_GAS_GAUGE_RELAX_THRE_H, 89962306a36Sopenharmony_ci bulk_reg, 2); 90062306a36Sopenharmony_ci 90162306a36Sopenharmony_ci /* Write sleep sample current, derived from sleep filter current. */ 90262306a36Sopenharmony_ci tmp = CURRENT_TO_ADC(charger->sleep_filter_current_ua, 90362306a36Sopenharmony_ci charger->res_div); 90462306a36Sopenharmony_ci put_unaligned_be16(tmp, bulk_reg); 90562306a36Sopenharmony_ci regmap_bulk_write(rk808->regmap, RK817_GAS_GAUGE_SLEEP_CON_SAMP_CUR_H, 90662306a36Sopenharmony_ci bulk_reg, 2); 90762306a36Sopenharmony_ci 90862306a36Sopenharmony_ci /* Restart battery relax voltage */ 90962306a36Sopenharmony_ci regmap_write_bits(rk808->regmap, RK817_GAS_GAUGE_GG_STS, 91062306a36Sopenharmony_ci RK817_RELAX_VOL_UPD, (0x0 << 2)); 91162306a36Sopenharmony_ci 91262306a36Sopenharmony_ci /* 91362306a36Sopenharmony_ci * Set OCV Threshold Voltage to 127.5mV. This was hard coded like this 91462306a36Sopenharmony_ci * in the BSP. 91562306a36Sopenharmony_ci */ 91662306a36Sopenharmony_ci regmap_write(rk808->regmap, RK817_GAS_GAUGE_OCV_THRE_VOL, 0xff); 91762306a36Sopenharmony_ci 91862306a36Sopenharmony_ci /* 91962306a36Sopenharmony_ci * Set maximum charging voltage to battery max voltage. Trying to be 92062306a36Sopenharmony_ci * incredibly safe with these value, as setting them wrong could 92162306a36Sopenharmony_ci * overcharge the battery, which would be very bad. 92262306a36Sopenharmony_ci */ 92362306a36Sopenharmony_ci max_chg_vol_mv = bat_info->constant_charge_voltage_max_uv / 1000; 92462306a36Sopenharmony_ci max_chg_cur_ma = bat_info->constant_charge_current_max_ua / 1000; 92562306a36Sopenharmony_ci 92662306a36Sopenharmony_ci if (max_chg_vol_mv < 4100) { 92762306a36Sopenharmony_ci return dev_err_probe(charger->dev, -EINVAL, 92862306a36Sopenharmony_ci "invalid max charger voltage, value %u unsupported\n", 92962306a36Sopenharmony_ci max_chg_vol_mv * 1000); 93062306a36Sopenharmony_ci } 93162306a36Sopenharmony_ci if (max_chg_vol_mv > 4450) { 93262306a36Sopenharmony_ci dev_info(charger->dev, 93362306a36Sopenharmony_ci "Setting max charge voltage to 4450000uv\n"); 93462306a36Sopenharmony_ci max_chg_vol_mv = 4450; 93562306a36Sopenharmony_ci } 93662306a36Sopenharmony_ci 93762306a36Sopenharmony_ci if (max_chg_cur_ma < 500) { 93862306a36Sopenharmony_ci return dev_err_probe(charger->dev, -EINVAL, 93962306a36Sopenharmony_ci "invalid max charger current, value %u unsupported\n", 94062306a36Sopenharmony_ci max_chg_cur_ma * 1000); 94162306a36Sopenharmony_ci } 94262306a36Sopenharmony_ci if (max_chg_cur_ma > 3500) 94362306a36Sopenharmony_ci dev_info(charger->dev, 94462306a36Sopenharmony_ci "Setting max charge current to 3500000ua\n"); 94562306a36Sopenharmony_ci 94662306a36Sopenharmony_ci /* 94762306a36Sopenharmony_ci * Now that the values are sanity checked, if we subtract 4100 from the 94862306a36Sopenharmony_ci * max voltage and divide by 50, we conviently get the exact value for 94962306a36Sopenharmony_ci * the registers, which are 4.1v, 4.15v, 4.2v, 4.25v, 4.3v, 4.35v, 95062306a36Sopenharmony_ci * 4.4v, and 4.45v; these correspond to values 0x00 through 0x07. 95162306a36Sopenharmony_ci */ 95262306a36Sopenharmony_ci max_chg_vol_reg = (max_chg_vol_mv - 4100) / 50; 95362306a36Sopenharmony_ci 95462306a36Sopenharmony_ci max_chg_cur_reg = rk817_chg_cur_to_reg(max_chg_cur_ma); 95562306a36Sopenharmony_ci 95662306a36Sopenharmony_ci if (max_chg_vol_reg < 0 || max_chg_vol_reg > 7) { 95762306a36Sopenharmony_ci return dev_err_probe(charger->dev, -EINVAL, 95862306a36Sopenharmony_ci "invalid max charger voltage, value %u unsupported\n", 95962306a36Sopenharmony_ci max_chg_vol_mv * 1000); 96062306a36Sopenharmony_ci } 96162306a36Sopenharmony_ci if (max_chg_cur_reg < 0 || max_chg_cur_reg > 7) { 96262306a36Sopenharmony_ci return dev_err_probe(charger->dev, -EINVAL, 96362306a36Sopenharmony_ci "invalid max charger current, value %u unsupported\n", 96462306a36Sopenharmony_ci max_chg_cur_ma * 1000); 96562306a36Sopenharmony_ci } 96662306a36Sopenharmony_ci 96762306a36Sopenharmony_ci /* 96862306a36Sopenharmony_ci * Write the values to the registers, and deliver an emergency warning 96962306a36Sopenharmony_ci * in the event they are not written correctly. 97062306a36Sopenharmony_ci */ 97162306a36Sopenharmony_ci ret = regmap_write_bits(rk808->regmap, RK817_PMIC_CHRG_OUT, 97262306a36Sopenharmony_ci RK817_CHRG_VOL_SEL, (max_chg_vol_reg << 4)); 97362306a36Sopenharmony_ci if (ret) { 97462306a36Sopenharmony_ci dev_emerg(charger->dev, 97562306a36Sopenharmony_ci "Danger, unable to set max charger voltage: %u\n", 97662306a36Sopenharmony_ci ret); 97762306a36Sopenharmony_ci } 97862306a36Sopenharmony_ci 97962306a36Sopenharmony_ci ret = regmap_write_bits(rk808->regmap, RK817_PMIC_CHRG_OUT, 98062306a36Sopenharmony_ci RK817_CHRG_CUR_SEL, max_chg_cur_reg); 98162306a36Sopenharmony_ci if (ret) { 98262306a36Sopenharmony_ci dev_emerg(charger->dev, 98362306a36Sopenharmony_ci "Danger, unable to set max charger current: %u\n", 98462306a36Sopenharmony_ci ret); 98562306a36Sopenharmony_ci } 98662306a36Sopenharmony_ci 98762306a36Sopenharmony_ci /* Set charge finishing mode to analog */ 98862306a36Sopenharmony_ci regmap_write_bits(rk808->regmap, RK817_PMIC_CHRG_TERM, 98962306a36Sopenharmony_ci RK817_CHRG_TERM_ANA_DIG, (0x0 << 2)); 99062306a36Sopenharmony_ci 99162306a36Sopenharmony_ci /* 99262306a36Sopenharmony_ci * Set charge finish current, warn if value not in range and keep 99362306a36Sopenharmony_ci * default. 99462306a36Sopenharmony_ci */ 99562306a36Sopenharmony_ci chg_term_ma = bat_info->charge_term_current_ua / 1000; 99662306a36Sopenharmony_ci if (chg_term_ma < 150 || chg_term_ma > 400) { 99762306a36Sopenharmony_ci dev_warn(charger->dev, 99862306a36Sopenharmony_ci "Invalid charge termination %u, keeping default\n", 99962306a36Sopenharmony_ci chg_term_ma * 1000); 100062306a36Sopenharmony_ci chg_term_ma = 200; 100162306a36Sopenharmony_ci } 100262306a36Sopenharmony_ci 100362306a36Sopenharmony_ci /* 100462306a36Sopenharmony_ci * Values of 150ma, 200ma, 300ma, and 400ma correspond to 00, 01, 10, 100562306a36Sopenharmony_ci * and 11. 100662306a36Sopenharmony_ci */ 100762306a36Sopenharmony_ci chg_term_i_reg = (chg_term_ma - 100) / 100; 100862306a36Sopenharmony_ci regmap_write_bits(rk808->regmap, RK817_PMIC_CHRG_TERM, 100962306a36Sopenharmony_ci RK817_CHRG_TERM_ANA_SEL, chg_term_i_reg); 101062306a36Sopenharmony_ci 101162306a36Sopenharmony_ci ret = rk817_read_or_set_full_charge_on_boot(charger, bat_info); 101262306a36Sopenharmony_ci if (ret < 0) 101362306a36Sopenharmony_ci return ret; 101462306a36Sopenharmony_ci 101562306a36Sopenharmony_ci /* 101662306a36Sopenharmony_ci * Set minimum USB input voltage to 4.5v and enable USB voltage input 101762306a36Sopenharmony_ci * limit. 101862306a36Sopenharmony_ci */ 101962306a36Sopenharmony_ci regmap_write_bits(rk808->regmap, RK817_PMIC_CHRG_IN, 102062306a36Sopenharmony_ci RK817_USB_VLIM_SEL, (0x05 << 4)); 102162306a36Sopenharmony_ci regmap_write_bits(rk808->regmap, RK817_PMIC_CHRG_IN, RK817_USB_VLIM_EN, 102262306a36Sopenharmony_ci (0x01 << 7)); 102362306a36Sopenharmony_ci 102462306a36Sopenharmony_ci /* 102562306a36Sopenharmony_ci * Set average USB input current limit to 1.5A and enable USB current 102662306a36Sopenharmony_ci * input limit. 102762306a36Sopenharmony_ci */ 102862306a36Sopenharmony_ci regmap_write_bits(rk808->regmap, RK817_PMIC_CHRG_IN, 102962306a36Sopenharmony_ci RK817_USB_ILIM_SEL, 0x03); 103062306a36Sopenharmony_ci regmap_write_bits(rk808->regmap, RK817_PMIC_CHRG_IN, RK817_USB_ILIM_EN, 103162306a36Sopenharmony_ci (0x01 << 3)); 103262306a36Sopenharmony_ci 103362306a36Sopenharmony_ci return 0; 103462306a36Sopenharmony_ci} 103562306a36Sopenharmony_ci 103662306a36Sopenharmony_cistatic void rk817_charging_monitor(struct work_struct *work) 103762306a36Sopenharmony_ci{ 103862306a36Sopenharmony_ci struct rk817_charger *charger; 103962306a36Sopenharmony_ci 104062306a36Sopenharmony_ci charger = container_of(work, struct rk817_charger, work.work); 104162306a36Sopenharmony_ci 104262306a36Sopenharmony_ci rk817_read_props(charger); 104362306a36Sopenharmony_ci 104462306a36Sopenharmony_ci /* Run every 8 seconds like the BSP driver did. */ 104562306a36Sopenharmony_ci queue_delayed_work(system_wq, &charger->work, msecs_to_jiffies(8000)); 104662306a36Sopenharmony_ci} 104762306a36Sopenharmony_ci 104862306a36Sopenharmony_cistatic void rk817_cleanup_node(void *data) 104962306a36Sopenharmony_ci{ 105062306a36Sopenharmony_ci struct device_node *node = data; 105162306a36Sopenharmony_ci 105262306a36Sopenharmony_ci of_node_put(node); 105362306a36Sopenharmony_ci} 105462306a36Sopenharmony_ci 105562306a36Sopenharmony_cistatic int rk817_charger_probe(struct platform_device *pdev) 105662306a36Sopenharmony_ci{ 105762306a36Sopenharmony_ci struct rk808 *rk808 = dev_get_drvdata(pdev->dev.parent); 105862306a36Sopenharmony_ci struct rk817_charger *charger; 105962306a36Sopenharmony_ci struct device_node *node; 106062306a36Sopenharmony_ci struct power_supply_battery_info *bat_info; 106162306a36Sopenharmony_ci struct device *dev = &pdev->dev; 106262306a36Sopenharmony_ci struct power_supply_config pscfg = {}; 106362306a36Sopenharmony_ci int plugin_irq, plugout_irq; 106462306a36Sopenharmony_ci int of_value; 106562306a36Sopenharmony_ci int ret; 106662306a36Sopenharmony_ci 106762306a36Sopenharmony_ci node = of_get_child_by_name(dev->parent->of_node, "charger"); 106862306a36Sopenharmony_ci if (!node) 106962306a36Sopenharmony_ci return -ENODEV; 107062306a36Sopenharmony_ci 107162306a36Sopenharmony_ci ret = devm_add_action_or_reset(&pdev->dev, rk817_cleanup_node, node); 107262306a36Sopenharmony_ci if (ret) 107362306a36Sopenharmony_ci return ret; 107462306a36Sopenharmony_ci 107562306a36Sopenharmony_ci charger = devm_kzalloc(&pdev->dev, sizeof(*charger), GFP_KERNEL); 107662306a36Sopenharmony_ci if (!charger) 107762306a36Sopenharmony_ci return -ENOMEM; 107862306a36Sopenharmony_ci 107962306a36Sopenharmony_ci charger->rk808 = rk808; 108062306a36Sopenharmony_ci 108162306a36Sopenharmony_ci charger->dev = &pdev->dev; 108262306a36Sopenharmony_ci platform_set_drvdata(pdev, charger); 108362306a36Sopenharmony_ci 108462306a36Sopenharmony_ci rk817_bat_calib_vol(charger); 108562306a36Sopenharmony_ci 108662306a36Sopenharmony_ci pscfg.drv_data = charger; 108762306a36Sopenharmony_ci pscfg.of_node = node; 108862306a36Sopenharmony_ci 108962306a36Sopenharmony_ci /* 109062306a36Sopenharmony_ci * Get sample resistor value. Note only values of 10000 or 20000 109162306a36Sopenharmony_ci * microohms are allowed. Schematic for my test implementation (an 109262306a36Sopenharmony_ci * Odroid Go Advance) shows a 10 milliohm resistor for reference. 109362306a36Sopenharmony_ci */ 109462306a36Sopenharmony_ci ret = of_property_read_u32(node, "rockchip,resistor-sense-micro-ohms", 109562306a36Sopenharmony_ci &of_value); 109662306a36Sopenharmony_ci if (ret < 0) { 109762306a36Sopenharmony_ci return dev_err_probe(dev, ret, 109862306a36Sopenharmony_ci "Error reading sample resistor value\n"); 109962306a36Sopenharmony_ci } 110062306a36Sopenharmony_ci /* 110162306a36Sopenharmony_ci * Store as a 1 or a 2, since all we really use the value for is as a 110262306a36Sopenharmony_ci * divisor in some calculations. 110362306a36Sopenharmony_ci */ 110462306a36Sopenharmony_ci charger->res_div = (of_value == 20000) ? 2 : 1; 110562306a36Sopenharmony_ci 110662306a36Sopenharmony_ci /* 110762306a36Sopenharmony_ci * Get sleep enter current value. Not sure what this value is for 110862306a36Sopenharmony_ci * other than to help calibrate the relax threshold. 110962306a36Sopenharmony_ci */ 111062306a36Sopenharmony_ci ret = of_property_read_u32(node, 111162306a36Sopenharmony_ci "rockchip,sleep-enter-current-microamp", 111262306a36Sopenharmony_ci &of_value); 111362306a36Sopenharmony_ci if (ret < 0) { 111462306a36Sopenharmony_ci return dev_err_probe(dev, ret, 111562306a36Sopenharmony_ci "Error reading sleep enter cur value\n"); 111662306a36Sopenharmony_ci } 111762306a36Sopenharmony_ci charger->sleep_enter_current_ua = of_value; 111862306a36Sopenharmony_ci 111962306a36Sopenharmony_ci /* Get sleep filter current value */ 112062306a36Sopenharmony_ci ret = of_property_read_u32(node, 112162306a36Sopenharmony_ci "rockchip,sleep-filter-current-microamp", 112262306a36Sopenharmony_ci &of_value); 112362306a36Sopenharmony_ci if (ret < 0) { 112462306a36Sopenharmony_ci return dev_err_probe(dev, ret, 112562306a36Sopenharmony_ci "Error reading sleep filter cur value\n"); 112662306a36Sopenharmony_ci } 112762306a36Sopenharmony_ci 112862306a36Sopenharmony_ci charger->sleep_filter_current_ua = of_value; 112962306a36Sopenharmony_ci 113062306a36Sopenharmony_ci charger->bat_ps = devm_power_supply_register(&pdev->dev, 113162306a36Sopenharmony_ci &rk817_bat_desc, &pscfg); 113262306a36Sopenharmony_ci if (IS_ERR(charger->bat_ps)) 113362306a36Sopenharmony_ci return dev_err_probe(dev, -EINVAL, 113462306a36Sopenharmony_ci "Battery failed to probe\n"); 113562306a36Sopenharmony_ci 113662306a36Sopenharmony_ci charger->chg_ps = devm_power_supply_register(&pdev->dev, 113762306a36Sopenharmony_ci &rk817_chg_desc, &pscfg); 113862306a36Sopenharmony_ci if (IS_ERR(charger->chg_ps)) 113962306a36Sopenharmony_ci return dev_err_probe(dev, -EINVAL, 114062306a36Sopenharmony_ci "Charger failed to probe\n"); 114162306a36Sopenharmony_ci 114262306a36Sopenharmony_ci ret = power_supply_get_battery_info(charger->bat_ps, 114362306a36Sopenharmony_ci &bat_info); 114462306a36Sopenharmony_ci if (ret) { 114562306a36Sopenharmony_ci return dev_err_probe(dev, ret, 114662306a36Sopenharmony_ci "Unable to get battery info\n"); 114762306a36Sopenharmony_ci } 114862306a36Sopenharmony_ci 114962306a36Sopenharmony_ci if ((bat_info->charge_full_design_uah <= 0) || 115062306a36Sopenharmony_ci (bat_info->voltage_min_design_uv <= 0) || 115162306a36Sopenharmony_ci (bat_info->voltage_max_design_uv <= 0) || 115262306a36Sopenharmony_ci (bat_info->constant_charge_voltage_max_uv <= 0) || 115362306a36Sopenharmony_ci (bat_info->constant_charge_current_max_ua <= 0) || 115462306a36Sopenharmony_ci (bat_info->charge_term_current_ua <= 0)) { 115562306a36Sopenharmony_ci return dev_err_probe(dev, -EINVAL, 115662306a36Sopenharmony_ci "Required bat info missing or invalid\n"); 115762306a36Sopenharmony_ci } 115862306a36Sopenharmony_ci 115962306a36Sopenharmony_ci charger->bat_charge_full_design_uah = bat_info->charge_full_design_uah; 116062306a36Sopenharmony_ci charger->bat_voltage_min_design_uv = bat_info->voltage_min_design_uv; 116162306a36Sopenharmony_ci charger->bat_voltage_max_design_uv = bat_info->voltage_max_design_uv; 116262306a36Sopenharmony_ci 116362306a36Sopenharmony_ci /* 116462306a36Sopenharmony_ci * Has to run after power_supply_get_battery_info as it depends on some 116562306a36Sopenharmony_ci * values discovered from that routine. 116662306a36Sopenharmony_ci */ 116762306a36Sopenharmony_ci ret = rk817_battery_init(charger, bat_info); 116862306a36Sopenharmony_ci if (ret) 116962306a36Sopenharmony_ci return ret; 117062306a36Sopenharmony_ci 117162306a36Sopenharmony_ci power_supply_put_battery_info(charger->bat_ps, bat_info); 117262306a36Sopenharmony_ci 117362306a36Sopenharmony_ci plugin_irq = platform_get_irq(pdev, 0); 117462306a36Sopenharmony_ci if (plugin_irq < 0) 117562306a36Sopenharmony_ci return plugin_irq; 117662306a36Sopenharmony_ci 117762306a36Sopenharmony_ci plugout_irq = platform_get_irq(pdev, 1); 117862306a36Sopenharmony_ci if (plugout_irq < 0) 117962306a36Sopenharmony_ci return plugout_irq; 118062306a36Sopenharmony_ci 118162306a36Sopenharmony_ci ret = devm_request_threaded_irq(charger->dev, plugin_irq, NULL, 118262306a36Sopenharmony_ci rk817_plug_in_isr, 118362306a36Sopenharmony_ci IRQF_TRIGGER_RISING | IRQF_ONESHOT, 118462306a36Sopenharmony_ci "rk817_plug_in", charger); 118562306a36Sopenharmony_ci if (ret) { 118662306a36Sopenharmony_ci return dev_err_probe(&pdev->dev, ret, 118762306a36Sopenharmony_ci "plug_in_irq request failed!\n"); 118862306a36Sopenharmony_ci } 118962306a36Sopenharmony_ci 119062306a36Sopenharmony_ci ret = devm_request_threaded_irq(charger->dev, plugout_irq, NULL, 119162306a36Sopenharmony_ci rk817_plug_out_isr, 119262306a36Sopenharmony_ci IRQF_TRIGGER_RISING | IRQF_ONESHOT, 119362306a36Sopenharmony_ci "rk817_plug_out", charger); 119462306a36Sopenharmony_ci if (ret) { 119562306a36Sopenharmony_ci return dev_err_probe(&pdev->dev, ret, 119662306a36Sopenharmony_ci "plug_out_irq request failed!\n"); 119762306a36Sopenharmony_ci } 119862306a36Sopenharmony_ci 119962306a36Sopenharmony_ci ret = devm_delayed_work_autocancel(&pdev->dev, &charger->work, 120062306a36Sopenharmony_ci rk817_charging_monitor); 120162306a36Sopenharmony_ci if (ret) 120262306a36Sopenharmony_ci return ret; 120362306a36Sopenharmony_ci 120462306a36Sopenharmony_ci /* Force the first update immediately. */ 120562306a36Sopenharmony_ci mod_delayed_work(system_wq, &charger->work, 0); 120662306a36Sopenharmony_ci 120762306a36Sopenharmony_ci return 0; 120862306a36Sopenharmony_ci} 120962306a36Sopenharmony_ci 121062306a36Sopenharmony_ci 121162306a36Sopenharmony_cistatic struct platform_driver rk817_charger_driver = { 121262306a36Sopenharmony_ci .probe = rk817_charger_probe, 121362306a36Sopenharmony_ci .driver = { 121462306a36Sopenharmony_ci .name = "rk817-charger", 121562306a36Sopenharmony_ci }, 121662306a36Sopenharmony_ci}; 121762306a36Sopenharmony_cimodule_platform_driver(rk817_charger_driver); 121862306a36Sopenharmony_ci 121962306a36Sopenharmony_ciMODULE_DESCRIPTION("Battery power supply driver for RK817 PMIC"); 122062306a36Sopenharmony_ciMODULE_AUTHOR("Maya Matuszczyk <maccraft123mc@gmail.com>"); 122162306a36Sopenharmony_ciMODULE_AUTHOR("Chris Morgan <macromorgan@hotmail.com>"); 122262306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 122362306a36Sopenharmony_ciMODULE_ALIAS("platform:rk817-charger"); 1224