Qualcomm高压充电方案 一、 构建系统与内核配置修改(Patch 部分)在补丁的头部,修改了三个关键的内核配置文件,用于将新驱动整合到编译系统中:pineapple_GKI.config:增加了CONFIG_SGM41575_BATTERY = m。这表明该驱动在基于高通 Pineapple 平台(通常是骁龙 8 Gen 3 架构)的GKI(通用内核镜像)中,被配置为编译成内核模块(Module),而不是直接编译进内核核心(built-in)。Kconfig:定义了配置项SGM41575_BATTERY,其类型为tristate(支持内建、模块或关闭)。当用户在编译内核时看到 "SGM41575 Battery" 选项,选中后即可激活此驱动。Makefile:增加了obj-$(CONFIG_SGM41575_BATTERY) += sgm41575_charger.o。当上述配置项被激活时,编译系统会自动将sgm41575_charger.c编译为目标文件。二、 驱动核心架构与基础宏定义进入sgm41575_charger.c源文件,代码展现了典型 Linux Power Supply(供电子系统)驱动的结构:1. 头文件与调试宏代码引入了linux/power_supply.h(电源子系统标准接口)、linux/regmap.h(寄存器映射抽象层)和linux/i2c.h(I2C 总线接口)。另外,通过SGM41575_KERNEL_DEBUG宏定制了一套标准化的日志打印函数(SGM_INFO、SGM_ERR等),并在打印时自动附带函数名(__func__)和行号(__LINE__),极大地方便了内核调试。2. 寄存器默认值初始化(sgm41575_reg_defs)驱动使用了一个结构体数组来存储芯片复位或初始化时的寄存器默认值:包含了输入电压限制(SGM41575_INPUT_V_LIM)、输入电流限制(SGM41575_INPUT_I_LIM_MSB/LSB)等。高压快充配置:在SGM41575_CHRG_CTRL_2寄存器中,代码通过#else分支将值设为0x40。注释明确指出,这里启用了 HVDCP(高通高压快充)检测,并支持 9V 和 12V 的高压快充方案。三、 核心功能函数深度解析代码中包含大量对芯片寄存器进行读写、物理量转换的函数,主要可以分为以下几类:1. ADC 数据采集与物理量转换(电量监测)芯片内置了 ADC(模数转换器)来监测电池状态。驱动实现了一系列get_*_adc函数:sgm41575_get_vbat_adc(电池电压)/sgm41575_get_vbus_adc(输入电压):先向SGM41575_ADC_CTRL写入SGM41575_ADC_EN以开启 ADC 转换。随后分别读取高 8 位(MSB)和低 8 位(LSB)寄存器,拼接成 16 位原始数据。最后乘以步长微伏(SGM41575_ADC_VOLT_STEP_uV),将原始寄存器值转换为标准的微伏($\mu V$)单位。sgm41575_get_ibat_adc(电池电流)/sgm41575_get_ibus_adc(输入电流):逻辑与电压类似,但在处理电池电流ibat时,代码将拼接后的数据强制转换为了short(有符号短整型)。这是因为电池电流有正负之分(充电为正,放电为负)。最后乘以SGM41575_ADC_CURR_STEP_uA转换为微安($\mu A$)。2. 充电参数限制与安全设置预充与截止电流设置:sgm41575_set_prechrg_curr和sgm41575_set_term_curr用于设定充电安全边界。函数内部做了严格的边界检查(Clamping),如果传入的值低于最小值或高于最大值,会被强制限制在芯片规格范围内,防止写入非法值导致硬件损坏。充电电压与输入电流限制:sgm41575_set_chrg_volt负责设置当前的充电目标电压。它会比对平台初始化数据(sgm-init_data.max_vreg),确保不会发生电池过充。3. 状态机获取(sgm41575_get_state)这是一个非常核心的函数。驱动通过连续读取CHRG_STAT和FAULT_STAT系列寄存器,全面捕捉芯片当前的物理状态:vbus_statusonline:判断 USB 线是否插入,电源是否就绪。chrg_statuschrg_type:获取当前的充电阶段(如未充电、恒流、恒压)以及识别到的充电源类型(SDP 标准下游端口、DCP 专用充电端口、CDP 充电下游端口)。healthfault:获取 NTC 温度状态以及芯片是否有报错(如过压、超时等)。函数最后还会调用前面提到的所有 ADC 采集函数,刷新电压电流数据并打印。四、 与 Linux 电源子系统的交互(Power Supply Framework)驱动的高层逻辑通过标准的 Linux 供电框架与系统上层(如 Android 的 Health HAL)交互:充电开关控制:sgm41575_enable_charger和sgm41575_disable_charger通过修改SGM41575_CHRG_CTRL_0寄存器的SGM41575_CHRG_EN位,实现对充电使能脚的软件控制,并同步更新全局标志位s_sgm41575_charging_flag。属性设置回调(sgm41575_charger_set_property):当上层系统想要改变充电状态时(例如 Android 触发了电池保护要停止充电),会调用此函数:若接收到POWER_SUPPLY_PROP_STATUS且值为 0,则调用disable_charger。若接收到POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT,则动态调整芯片的输入限流,以适应不同的充电器。属性获取回调(sgm41575_charger_get_property):从代码截断处可以看到,系统读取充电状态时,驱动会加锁(mutex_lock)以保证线程安全,直接返回缓存的state结构体数据。若未检测到 VBUS 输入或处于 OTG 模式,则向系统上层报告POWER_SUPPLY_STATUS_DISCHARGING(放电状态)。diff --git a/kernel_platform/msm-kernel/drivers/power/supply/sgm41575_charger.c b/kernel_platform/msm-kernel/drivers/power/supply/sgm41575_charger.c new file mode 100755 index 0000000..5c441aa --- /dev/null +++ b/kernel_platform/msm-kernel/drivers/power/supply/sgm41575_charger.c @@ -0,0 +1,1706 @@ +// SPDX-License-Identifier: GPL-2.0 +// SGM41575 driver +// Copyright (C) 2020 Texas Instruments Incorporated - http://www.ti.com/ + +#include linux/module.h +#include linux/i2c.h +#include linux/power_supply.h +#include linux/regmap.h +#include linux/types.h +#include linux/gpio/consumer.h +#include linux/interrupt.h +#include linux/delay.h +#include linux/usb/phy.h +#include linux/regulator/driver.h +#include linux/regulator/of_regulator.h +#include linux/regulator/machine.h + +#include linux/acpi.h +#include linux/gpio.h +#include linux/of_gpio.h + +#include "sgm41575_charger.h" + +#define SGM41575_NUM_WD_VAL 8 +#define CONFIG_INTERRUPTS_IN_DTS +#define SGM41575_INFO 0x1 +#define SGM41575_RETRY_TIMES 2 + + +static struct power_supply_desc sgm41575_power_supply_desc; + +#define SGM41575_KERNEL_DEBUG +#ifdef SGM41575_KERNEL_DEBUG +#define SGM_INFO(fmt, args...) printk("[sgm41575_charger.c INFO] %s() called: line=%d " fmt, __func__, __LINE__, ##args) +#define SGM_DBG(fmt, args...) printk("[sgm41575_charger.c DBG] %s() called: line=%d " fmt, __func__, __LINE__, ##args) +#define SGM_ERR(fmt, args...) printk("[sgm41575_charger.c ERR] %s() called: line=%d " fmt, __func__, __LINE__, ##args) +#define SGM_LOG(fmt, args...) printk("[sgm41575_charger.c LOG] %s() called: line=%d " fmt, __func__, __LINE__, ##args) +#else +#define SGM_INFO +#define SGM_DBG +#define SGM_ERR +#define SGM_LOG +#endif + + +static struct reg_default sgm41575_reg_defs[] = { + {SGM41575_INPUT_V_LIM, 0x24}, + {SGM41575_INPUT_I_LIM_MSB, 0x00}, + {SGM41575_INPUT_I_LIM_LSB, 0xc8}, + {SGM41575_PRECHRG_CTRL, 0xc3}, + {SGM41575_TERM_CTRL, 0x5}, + {SGM41575_VOTG_LSB_REG, 0xdc}, + {SGM41575_IOTG_REG, 0x4b}, + {SGM41575_TIMER_CTRL, 0x3d}, + {SGM41575_CHRG_CTRL_0, 0xa2}, + {SGM41575_CHRG_CTRL_1, 0x85}, + #if 0 + {SGM41575_CHRG_CTRL_2, 0x50},//disable hvdcp detect and hvdcp 9v + #else + {SGM41575_CHRG_CTRL_2, 0x40},//enable hvdcp detect and hvdcp 9v 12v + #endif + {SGM41575_CHRG_CTRL_3, 0x12}, + {SGM41575_CHRG_CTRL_5, 0x16}, + {SGM41575_MPPT_CTRL, 0xaa}, + {SGM41575_TEMP_CTRL, 0xc0}, + {SGM41575_NTC_CTRL_0, 0x7a}, + {SGM41575_NTC_CTRL_1, 0x55}, + {SGM41575_ICO_I_LIM, 0x0}, + {SGM41575_CHRG_STAT_0, 0x0}, + {SGM41575_CHRG_STAT_1, 0x0}, + {SGM41575_CHRG_STAT_2, 0x0}, + {SGM41575_CHRG_STAT_3, 0x0}, + {SGM41575_CHRG_STAT_4, 0x0}, + {SGM41575_FAULT_STAT_0, 0x0}, + {SGM41575_FAULT_STAT_1, 0x0}, + {SGM41575_CHRG_FLAG_0, 0x0}, + {SGM41575_CHRG_FLAG_1, 0x0}, + {SGM41575_CHRG_FLAG_2, 0x0}, + {SGM41575_CHRG_FLAG_3, 0x0}, + {SGM41575_FAULT_FLAG_0, 0x0}, + {SGM41575_FAULT_FLAG_1, 0x0}, + {SGM41575_CHRG_MSK_0, 0x0}, + {SGM41575_CHRG_MSK_1, 0x0}, + {SGM41575_CHRG_MSK_2, 0x0}, + {SGM41575_CHRG_MSK_3, 0x0}, + {SGM41575_FAULT_MSK_0, 0x0}, + {SGM41575_FAULT_MSK_1, 0x0}, + {SGM41575_ADC_CTRL, 0x30}, + {SGM41575_FN_DISABE_0, 0x0}, + {SGM41575_FN_DISABE_1, 0x0}, + {SGM41575_ADC_IBUS_MSB, 0x0}, + {SGM41575_ADC_IBUS_LSB, 0x0}, + {SGM41575_ADC_IBAT_MSB, 0x0}, + {SGM41575_ADC_IBAT_LSB, 0x0}, + {SGM41575_ADC_VAC1, 0x0}, + {SGM41575_ADC_VAC2, 0x0}, + {SGM41575_ADC_VBAT_MSB, 0x0}, + {SGM41575_ADC_VBAT_LSB, 0x0}, + {SGM41575_ADC_VBUS_MSB, 0x0}, + {SGM41575_ADC_VBUS_LSB, 0x0}, + {SGM41575_ADC_TS, 0x0}, + {SGM41575_ADC_TDIE, 0x0}, + {SGM41575_ADC_DP, 0x0}, + {SGM41575_ADC_DM, 0x0}, + #if 1 //for sgm41575 + {SGM41575_DPDM_DRV, 0x0}, + #else + {SGM41575_DPDM_DRV, 0x08}, + #endif + {SGM41575_PART_INFO, 0x0}, +}; +static bool s_sgm41575_charging_flag = 0; +static bool s_temp_warn_flag = 0; + +static enum power_supply_usb_type sgm41575_usb_type[] = { + POWER_SUPPLY_USB_TYPE_UNKNOWN, + POWER_SUPPLY_USB_TYPE_SDP, + POWER_SUPPLY_USB_TYPE_DCP, + POWER_SUPPLY_USB_TYPE_CDP, + POWER_SUPPLY_USB_TYPE_ACA, + //POWER_SUPPLY_USB_TYPE_HVDCP, +}; + + +static int sgm41575_get_vbat_adc(struct sgm41575_device *sgm) +{ + int ret; + int vbat_adc_lsb, vbat_adc_msb; + int vbat_adc; + + ret = regmap_update_bits(sgm-regmap, SGM41575_ADC_CTRL, + SGM41575_ADC_EN, SGM41575_ADC_EN); + if (ret) + return ret; + + ret = regmap_read(sgm-regmap, SGM41575_ADC_VBAT_MSB, vbat_adc_msb); + if (ret) + return ret; + + ret = regmap_read(sgm-regmap, SGM41575_ADC_VBAT_LSB, vbat_adc_lsb); + if (ret) + return ret; + + vbat_adc = (vbat_adc_msb 8) | vbat_adc_lsb; + + return vbat_adc * SGM41575_ADC_VOLT_STEP_uV; +} + +static int sgm41575_get_vbus_adc(struct sgm41575_device *sgm) +{ + int ret; + int vbus_adc_lsb, vbus_adc_msb; + int vbus_adc; + + ret = regmap_update_bits(sgm-regmap, SGM41575_ADC_CTRL, + SGM41575_ADC_EN, SGM41575_ADC_EN); + if (ret) + return ret; + + ret = regmap_read(sgm-regmap, SGM41575_ADC_VBUS_MSB, vbus_adc_msb); + if (ret) + return ret; + + ret = regmap_read(sgm-regmap, SGM41575_ADC_VBUS_LSB, vbus_adc_lsb); + if (ret) + return ret; + + vbus_adc = (vbus_adc_msb 8) | vbus_adc_lsb; + + return vbus_adc * SGM41575_ADC_VOLT_STEP_uV; +} + +static int sgm41575_get_ibus_adc(struct sgm41575_device *sgm) +{ + int ret; + int ibus_adc_lsb, ibus_adc_msb; + int ibus_adc; + + ret = regmap_update_bits(sgm-regmap, SGM41575_ADC_CTRL, + SGM41575_ADC_EN, SGM41575_ADC_EN); + if (ret) + return ret; + + ret = regmap_read(sgm-regmap, SGM41575_ADC_IBUS_MSB, ibus_adc_msb); + if (ret) + return ret; + + ret = regmap_read(sgm-regmap, SGM41575_ADC_IBUS_LSB, ibus_adc_lsb); + if (ret) + return ret; + + ibus_adc = (ibus_adc_msb 8) | ibus_adc_lsb; + + return ibus_adc * SGM41575_ADC_CURR_STEP_uA; +} + + +static int sgm41575_get_ibat_adc(struct sgm41575_device *sgm) +{ + int ret; + unsigned int ibat_adc_lsb, ibat_adc_msb; + short ibat_adc; + + ret = regmap_update_bits(sgm-regmap, SGM41575_ADC_CTRL, + SGM41575_ADC_EN, SGM41575_ADC_EN); + if (ret) + return ret