The four power source types that are detected are:
1. Standard downstream port (SDP)
– This is a computer USB port capable of USB 1.1 (100 mA), USB 2.0 (100/500 mA), or USB 3.0 (150/900 mA). D+ and Dare independently pulled down in the host with a 14.25–24.8 kΩ resistance.
2. Charging downstream port (CDP)
– This is typically a powered USB hub capable of 1.5 A.
3. Dedicated charging port (DCP) 标充
– This is a (standard wall charger) capable of at least 500 mA. D+ and D- are shorted in the wall adapter with a maximum
resistance of 200 Ω. SCHG defaults to high current mode and runs automatic input current limiting (AICL).
4. Other charging port (not covered by USB charging specification 1.2)
– This is a non-standard charger with a proprietary D+/D- configuration. Typically, these chargers have similar current
capability to normal DCPs. D+ and D- are connected to Vbus via a resistor divider, which causes them to be at specific fixed
voltage levels
from <80-p2536-5a_e_pm8953_+_pmi8952_power_management_ic_design_guidelines_training_slides.pdf>
*/
//
"usbin-src-det",
static irqreturn_t src_detect_handler(int irq, void *_chip)
{
....
if (src_detect) {
update_usb_status(chip, usb_present, 0);
} else {
update_usb_status(chip, 0, false);
....
}
void update_usb_status(struct smbchg_chip *chip, bool usb_present, bool force)
{
...
if (!chip->usb_present && usb_present) {
chip->usb_present = usb_present;
handle_usb_insertion(chip);
} else if (chip->usb_present && !usb_present) {
chip->usb_present = usb_present;
handle_usb_removal(chip);
}
...
}
// usb插入开启快充检测
static void handle_usb_insertion(struct smbchg_chip *chip)
{
...
read_usb_type(chip, &usb_type_name, &usb_supply_type); -> smbchg_read(chip, ®, chip->misc_base + IDEV_STS, 1); // 读取1608寄存器获取充电类型
smbchg_change_usb_supply_type(chip, usb_supply_type);
// 快充检测
if (!chip->hvdcp_not_supported &&(usb_supply_type == POWER_SUPPLY_TYPE_USB_DCP))
{
cancel_delayed_work_sync(&chip->hvdcp_det_work);
smbchg_stay_awake(chip, PM_DETECT_HVDCP);
schedule_delayed_work(&chip->hvdcp_det_work,msecs_to_jiffies(HVDCP_NOTIFY_MS));
}
}
pmi8950_fg: qcom,fg {
spmi-dev-container;
compatible = "qcom,qpnp-fg";
#address-cells = <1>;
#size-cells = <1>;
qcom,resume-soc = <95>; // 复充电量
status = "okay";
qcom,bcl-lm-threshold-ma = <127>;
qcom,bcl-mh-threshold-ma = <405>;
qcom,fg-iterm-ma = <150>; // 截止电流
qcom,fg-chg-iterm-ma = <100>; // 截止电流
qcom,fg-cutoff-voltage-mv = <3400>; // 关机电压
// msm-pmi8950.dtsi
pmi8950_charger: qcom,qpnp-smbcharger {
spmi-dev-container;
compatible = "qcom,qpnp-smbcharger";
qcom,iterm-ma = <256>; // 充电截止电流
qcom,resume-delta-mv = <100>; // 复充电压
qcom,chg-inhibit-fg; // 如果有这个定义,则复充条件都使用上边的fg设置
qcom,float-voltage-mv = <4400>; // Maximum voltage up to which the battery is charged; supported range is 3.6 V to 4.5 V.
qcom,thermal-mitigation = <1900 1900 1900 0>; // 不同温度的电流限制
qcom,fg-cc-cv-threshold-mv = <4400>; // cc - cv的电压条件
qcom,autoadjust-vfloat; // A Boolean property that when set, makes the driver automatically readjust vfloat using the fuel gauge ADC readings to make chargingmore accurate.
}
//
chip->chg_inhibit_source_fg = of_property_read_bool(node,"qcom,chg-inhibit-fg"); // 如果有这个定义,则截止电流复充条件都使用上边的fg设置
static int smbchg_hw_init(struct smbchg_chip *chip)
{
....
// 复充条件使用fg adc还是
/*
* Based on the configuration, use the analog sensors or the fuelgauge
* adc for recharge threshold source.
*/
if (chip->chg_inhibit_source_fg)
rc = smbchg_sec_masked_write(chip, chip->chgr_base + CHGR_CFG1,
TERM_I_SRC_BIT | RECHG_THRESHOLD_SRC_BIT,
TERM_SRC_FG | RECHG_THRESHOLD_SRC_BIT);
else
rc = smbchg_sec_masked_write(chip, chip->chgr_base + CHGR_CFG1,
TERM_I_SRC_BIT | RECHG_THRESHOLD_SRC_BIT, 0);
....
}
get_prop_batt_status
static int smbchg_change_usb_supply_type(struct smbchg_chip *chip,enum power_supply_type type)
{
if (type == POWER_SUPPLY_TYPE_USB)
current_limit_ma = DEFAULT_SDP_MA;
else if (type == POWER_SUPPLY_TYPE_USB)
current_limit_ma = DEFAULT_SDP_MA;
else if (type == POWER_SUPPLY_TYPE_USB_CDP)
current_limit_ma = DEFAULT_CDP_MA;
else if (type == POWER_SUPPLY_TYPE_USB_HVDCP)
current_limit_ma = smbchg_default_hvdcp_icl_ma; // 快充充电电流限制
else if (type == POWER_SUPPLY_TYPE_USB_HVDCP_3)
current_limit_ma = smbchg_default_hvdcp3_icl_ma;
else
current_limit_ma = smbchg_default_dcp_icl_ma;
rc = vote(chip->usb_icl_votable, PSY_ICL_VOTER, true,
current_limit_ma);
.....
}
static int smbchg_probe(struct spmi_device *spmi)
{
...
// 创建充电电流限制的vote
chip->usb_icl_votable = create_votable(&spmi->dev,
"SMBCHG: usb_icl",
VOTE_MIN, NUM_ICL_VOTER, 3000,
set_usb_current_limit_vote_cb); // 回调函数
....
}
static int get_prop_batt_status(struct smbchg_chip *chip)
{
....
charger_present = is_usb_present(chip) | is_dc_present(chip) |
chip->hvdcp_3_det_ignore_uv;
if (!charger_present)
return POWER_SUPPLY_STATUS_DISCHARGING;
...
POWER_SUPPLY_STATUS_FULL
POWER_SUPPLY_STATUS_NOT_CHARGING
POWER_SUPPLY_STATUS_CHARGING
}
static int smbchg_charging_en(struct smbchg_chip *chip, bool en)
{
/* The en bit is configured active low */
return smbchg_masked_write(chip, chip->bat_if_base + CMD_CHG_REG,
EN_BAT_CHG_BIT, en ? 0 : EN_BAT_CHG_BIT);
}
static int smbchg_float_voltage_set(struct smbchg_chip *chip, int vfloat_mv)
static int smbchg_set_fastchg_current_raw(struct smbchg_chip *chip,int current_ma)
cat sys/class/power_supply/battery/system_temp_level // 正常值为0
qcom,thermal-mitigation = <1900 1900 1900 0>; // 不同温度的电流限制
// 超过限制temp level会导致usb charger suspend
static int smbchg_system_temp_level_set(struct smbchg_chip *chip,int lvl_sel)
{
...
if (chip->therm_lvl_sel == (chip->thermal_levels - 1)) {
// disable charger
rc = vote(chip->usb_suspend_votable, THERMAL_EN_VOTER, true, 0);
}
....
//限制充电电流
thermal_icl_ma =(int)chip->thermal_mitigation[chip->therm_lvl_sel];
vote(chip->usb_icl_votable, THERMAL_ICL_VOTER, true,thermal_icl_ma);
....
if (prev_therm_lvl == chip->thermal_levels - 1) //恢复回充温度
// enable charger
rc = vote(chip->usb_suspend_votable, THERMAL_EN_VOTER, false, 0);
...
}
chip->hvdcp_not_supported
usb_otg: usb@78db000 {
compatible = "qcom,hsusb-otg"; // phy-msm-usb.c
...
qcom,floated-charger-enable = <1>; //enable float charger 需添加高通patch
...
}
POWER_SUPPLY_PROP_VOLTAGE_MAX
smbchg_set_fastchg_current_raw(chip, val->intval);
// msm8953-qrd.dtsi
&pmi8950_fg {
compatible = "qcom,qpnp-fg";
qcom,battery-data = <&qrd_batterydata>;
// 通过fg内部adc读取温度值
qcom,thermal-coefficients = [DA 86 F0 50 08 3C]; // ntc热敏电阻的系数查表得出 (80-nv610-41_c_linux_android_pmic_fuel_gauge_user_guide.pdf page 11 80-vt310-123_c_pmi8994_fuel_gauge_hw_sw_control.pdf page 14)
...
}
/ {
qrd_batterydata: qcom,battery-data {
qcom,batt-id-range-pct = <15>; // battey_id 电阻精确范围
#include "xxxxx_7000mAh_averaged_MasterSlave.dtsi"
#include "xxxxx_7000mAh_averaged_SecondSlave.dtsi"
};
};
// xxxx_7000mAh_averaged_MasterSlave.dtsi
qcom,xxxxxxx6000atl_7000mah_averaged_masterslave_feb15th2017 {
...
qcom,batt-id-kohm = <51>; // battery id
qcom,max-voltage-uv = <4400000>; // 满电电压
qcom,nom-batt-capacity-mah = <7000>; // 电池容量
qcom,fastchg-current-ma = <2500>; // charger current
...
static int fg_power_get_property(struct power_supply *psy,
enum power_supply_property psp,
union power_supply_propval *val)
{
case POWER_SUPPLY_PROP_RESISTANCE_ID:
//val->intval = get_sram_prop_now(chip, FG_DATA_BATT_ID);
val->intval = cm_get_ID1();
if(val->intval == 0x21)
val->intval = 100000;
else
val->intval =1000000;
break;
}
static int fg_batt_profile_init(struct fg_chip *chip)
{
/*
(of_batterydata.c)
获取最佳node by "qcom,batt-id-kohm"
内阻值在 "qcom,batt-id-range-pct" 范围内则使用这个node
fg_batt_type 为指定电池name,如果指定就只会从指定name的 dts中查找,一般不指定
*/
profile_node = of_batterydata_get_best_profile(batt_node, "bms",fg_batt_type);
}
PMI8952 charger bringup without battery profile To allow charging to proceed with an unknown battery profile (for example, for bringup purposes before
battery characterization), include the following device tree property in your charger device tree node
(qcom,qpnp-smbcharger):
qcom,charge-unknown-battery;
//3. msm-pmi8950.dtsi warm charge cold charger
--- a/arch/arm/boot/dts/qcom/msm-pmi8950.dtsi
+++ b/arch/arm/boot/dts/qcom/msm-pmi8950.dtsi
@@ -217,7 +217,7 @@
qcom,aicl-rerun-period-s = <180>;
qcom,autoadjust-vfloat;
qcom,fastchg-current-comp = <700>;// 高低温下的充电电流 Supported values are 250, 700,900 and 1200mA. Set normal current is 2A.\u9ad8\u4f4e\u6e29\u8bbe\u7f6e\u7ea6\u4e3a0.3*2A
- qcom,float-voltage-comp =<11>;//warm或者cool状态的限制电压为4.2V,4.4V对应的值0x2E.4.1V is 0x23, so 0x2E-0x23 = 11(10进制)( 80-nt391-2x_e_pmi8952_hardware_register_description.pdf SMBCHGL_CHGR_FV_CFG)
+ qcom,float-voltage-comp =<9>;//warm或者cool状态的限制电压为4.2V,4.4V对应的值0x2E.4.1V is 0x23, so 0x2E-0x23 = 11(10进制)
qcom,chgr@1000 {
reg = <0x1000 0x100>;
diff --git a/drivers/power/qpnp-smbcharger.c b/drivers/power/qpnp-smbcharger.c
index bad46e2..36c1dd8 100644
--- a/drivers/power/qpnp-smbcharger.c
+++ b/drivers/power/qpnp-smbcharger.c
@@ -6207,7 +6203,16 @@ static irqreturn_t batt_warm_handler(int irq, void *_chip)
{
struct smbchg_chip *chip = _chip;
u8 reg = 0;
-
+ int rc;
+ /* 高温设置满电电压为 4.24V */
+ if (chip->float_voltage_comp != -EINVAL) {
+ rc = smbchg_float_voltage_comp_set(chip,
+ chip->float_voltage_comp);
+ if (rc < 0)
+ dev_err(chip->dev, "Couldn't set float voltage comp rc = %d\n",rc);
+ pr_smb(PR_STATUS, "set float voltage comp to %d\n",
+ chip->float_voltage_comp);
+ }
smbchg_read(chip, ®, chip->bat_if_base + RT_STS, 1);
chip->batt_warm = !!(reg & HOT_BAT_SOFT_BIT);
pr_smb(PR_INTERRUPT, "triggered: 0x%02x\n", reg);
@@ -6223,7 +6228,11 @@ static irqreturn_t batt_cool_handler(int irq, void *_chip)
{
struct smbchg_chip *chip = _chip;
u8 reg = 0;
-
+ int rc;
+ /* 低温设置满电电压为 4.4V*/
+ rc = smbchg_float_voltage_comp_set(chip,0);
+ if (rc < 0)
+ dev_err(chip->dev, "Couldn't set float voltage comp rc = %d\n",rc);
smbchg_read(chip, ®, chip->bat_if_base + RT_STS, 1);
chip->batt_cool = !!(reg & COLD_BAT_SOFT_BIT);
pr_smb(PR_INTERRUPT, "triggered: 0x%02x\n", reg);
// 4.
// 修改内部电量计的更新频率
--- a/drivers/power/qpnp-fg.c
+++ b/drivers/power/qpnp-fg.c
@@ -1973,12 +1973,12 @@ static void fg_handle_battery_insertion(struct fg_chip *chip)
schedule_delayed_work(&chip->update_sram_data, msecs_to_jiffies(0));
}
-
+#if 0 //this function is not used for a while. it used in the "fg_common_hw_init" function originally
static int soc_to_setpoint(int soc)
{
return DIV_ROUND_CLOSEST(soc * 255, 100);
}
-
+#endif
static void batt_to_setpoint_adc(int vbatt_mv, u8 *data)
{
int val;
@@ -7931,7 +7931,7 @@ static int fg_common_hw_init(struct fg_chip *chip)
}
rc = fg_mem_masked_write(chip, settings[FG_MEM_DELTA_SOC].address, 0xFF,
- soc_to_setpoint(settings[FG_MEM_DELTA_SOC].value),
+ settings[FG_MEM_DELTA_SOC].value,
settings[FG_MEM_DELTA_SOC].offset);
if (rc) {
pr_err("failed to write delta soc rc=%d\n", rc);
static int update_sram_data(struct fg_chip *chip, int *resched_ms)
// fg_data[i] 的数据含义
enum fg_mem_data_index {
FG_DATA_BATT_TEMP = 0,
FG_DATA_OCV,
FG_DATA_VOLTAGE,
FG_DATA_CURRENT,
FG_DATA_BATT_ESR,
FG_DATA_BATT_ESR_COUNT,
FG_DATA_BATT_SOC,
FG_DATA_CC_CHARGE,
FG_DATA_VINT_ERR,
FG_DATA_CPRED_VOLTAGE,
/* values below this only gets read once per profile reload */
FG_DATA_BATT_ID,
FG_DATA_BATT_ID_INFO,
FG_DATA_MAX,
};
qpnp-adc-voltage.c
//4.1 mpp2设置为vac输入模式
pm8937_mpps: mpps {
...
mpp@a100 {
/* MPP2 - VBUS CHARGE THERMAL config */
reg = <0xa100 0x100>;
qcom,pin-num = <2>;
qcom,mode = <4>; /* AIN input */
qcom,invert = <1>; /* Enable MPP */
qcom,ain-route = <1>; /* AMUX 6 */
qcom,master-en = <1>;
qcom,src-sel = <0>; /* Function constant */
};
...
}
// 4.2 mmp2对应chanel 11 ( form 80-nv610-45_b_pmic_adc_software_user_guide.pdf PM8952 AMUX channels )
pm8937_vadc: vadc@3100 {
compatible = "qcom,qpnp-vadc";
...
chan@11 {
label = "vbus_therm";
reg = <0x11>;
qcom,decimation = <0>;
qcom,pre-div-channel-scaling = <0>;
qcom,calibration-type = "ratiometric";
qcom,scale-function = <11>;
qcom,hw-settle-time = <2>;
qcom,fast-avg-setup = <0>;
qcom,vadc-thermal-node;
};
...
}
// 4.3 哪边使用此vadc,及标明channel值
pmi8937_charger: qcom,qpnp-smbcharger {
...
qcom,vbus_therm-vadc = <&pm8937_vadc>;
qcom,board_temp-vadc = <&pm8937_vadc>;
qcom,usbchg_temp-channel-id = <17>;
qcom,board_temp-channel-id = <19>;
....
}
// 4.4 .c文件中使用adc
// 根据名字获取对应的adc ( qcom,vbus_therm-vadc - snprintf(prop_name, QPNP_MAX_PROP_NAME_LEN, "qcom,%s-vadc", name);)
if (of_find_property(spmi->dev.of_node, "qcom,vbus_therm-vadc", NULL))
qpnp_get_vadc(&spmi->dev, "vbus_therm");
// 4.5获取adc数据
static inline int32_t qpnp_vadc_read(struct qpnp_vadc_chip *dev,uint32_t channel,struct qpnp_vadc_result *result)
/*
*/
pm8937_mpps: mpps {
compatible = "qcom,qpnp-pin";
mpp@a100 {
/* MPP2 - PA_THERM config */
reg = <0xa100 0x100>;
qcom,pin-num = <2>;
qcom,mode = <4>; /* AIN input */
qcom,invert = <1>; /* Enable MPP */
qcom,ain-route = <1>; /* AMUX 6 */
qcom,master-en = <1>;
qcom,src-sel = <0>; /* Function constant */
};
pm8937_vadc: vadc@3100 {
compatible = "qcom,qpnp-vadc";
reg = <0x3100 0x100>;
#address-cells = <1>;
#size-cells = <0>;
interrupts = <0x0 0x31 0x0>;
interrupt-names = "eoc-int-en-set";
qcom,adc-bit-resolution = <15>;
qcom,adc-vdd-reference = <1800>;
qcom,vadc-poll-eoc;
chan@11 {
label = "pa_therm1";
reg = <0x11>;
qcom,decimation = <0>;
qcom,pre-div-channel-scaling = <0>;
qcom,calibration-type = "ratiometric";
qcom,scale-function = <2>;
qcom,hw-settle-time = <2>;
qcom,fast-avg-setup = <0>;
qcom,vadc-thermal-node;
};
struct thermal_zone_device *usb_connector_device;
long temperature;
usb_connector_device = thermal_zone_get_zone_by_name("pa_therm1");
ret = thermal_zone_get_temp(usb_connector_device, &temperature);
struct charger_manager->desc->thermal_zone = "thermal name"
// drivers\usb\dwc3\dwc3-msm.c
usb3: ssusb@7000000{
compatible = "qcom,dwc-usb3-msm";
}
//drivers\usb\phy\phy-msm-usb.c
usb_otg: usb@78db000 {
compatible = "qcom,hsusb-otg";
power_supply_set_usb_otg //设置otg是否在线
POWER_SUPPLY_PROP_USB_OTG
pm8950_get_pon_reason
target_pause_for_battery_charge // 判断是否为关机充电
&pmi8950_charger {
...
qcom,chg-led-sw-controls;
qcom,chg-led-support; // charger led 使用pmi芯片默认的 CHG_LED pin脚
..
}
// qpnp-smbcharger.c
static int smbchg_probe(struct spmi_device *spmi)
{
....
if (chip->cfg_chg_led_support &&chip->schg_version == QPNP_SCHG_LITE) {
rc = smbchg_register_chg_led(chip);
}
....
}