当前位置: 首页 > news >正文

linux 驱动-power_supply 与 mtk 充电框架

文章目录

  • 一、pd 快充协议
  • 二、power suplly 子系统
    • 1、数据结构
      • 1.1 power_supply_desc
      • 1.2 power_supply_config
      • 1.3 power_supply
      • 1.4 power_supply_type
      • 1.5 power_supply_usb_type
        • 1.5.1 POWER_SUPPLY_USB_TYPE_SDP
        • 1.5.2 POWER_SUPPLY_USB_TYPE_DCP
        • 1.5.3 POWER_SUPPLY_USB_TYPE_CDP
      • 1.6 power_supply_property
        • 1.6.1 POWER_SUPPLY_PROP_STATUS
        • 1.6.2 POWER_SUPPLY_PROP_CHARGE_TYPE
        • 1.6.3 POWER_SUPPLY_PROP_HEALTH
        • 1.6.4 POWER_SUPPLY_PROP_CAPACITY_LEVEL
        • 1.6.5 POWER_SUPPLY_PROP_ONLINE
    • 2、 重要接口
      • 2.1 power_supply_register
      • 2.2 power_supply_get_property
      • 2.3 power_supply_get_property
      • 2.4 power_supply_changed
  • 三、各个模块简单说明
    • 1、rt1711
    • 2、mtk_pd_adapter
    • 3、charge alg
    • 4、charge ic
  • 四、进入快充的条件
  • 五、遇到的问题以及解决方案
    • 1、pd 协议时序问题
      • 1.1 问题现象
      • 1.2 问题分析
      • 1.3 问题解决
    • 2、charge ic 无法及时响应设备插入
      • 2.1 问题现象
      • 2.2 解决方案
    • 3、charge ic 无 ibus 不进快充.
      • 3.1 问题描述
      • 3.2 解决方案
    • 4、系统层监听不到充电器插入
      • 4.1 问题描述
      • 4.2 解决方案
    • 5、插着 usb 开机无法系统识别到充电器
  • 六、添加 lk 驱动

作者: baron
博客: baron-z.cn

    本文分析总结 mtk 平台的充电框架, 平台 mt8786 , 系统 Android12, 内核版本 kernel-5.10. mtk 的充电包含这几个部分,

  • charge ic: 给电池充电, 以及系统供电的芯片, 包含 ichg 充电电流 vbus 充电电压, ocv 截止电压等.
  • battery: 管理电池的基本信息, 电量, 充电电流等.
  • charger alg: 快冲算法相关, 例如 pd 充电的算法实现.
  • mtk pd adapter: 通用适配器驱动, 统一 sink 的接口为 mtk 的标准接口. 主要包括获取 src_cap 信息, request 电压电流等.
  • typec 与 sink 驱动: 例如 1711 等 sink 驱动.

    整体框架如图所示.

在这里插入图片描述

    充电的整个过程就是适配器输入的 vbus * ibus 最后减去损耗之后到电池的 ibat * vbat.

在这里插入图片描述

    我们知道,电流越大,在电阻上损失的功率(P=I²R)也越大,同时还会引起明显的发热问题。因此,在需要提升充电功率的情况下,我们通常选择在前端适配器提高充电电压(VBUS),比如采用 9V/2A、12V/2A 等方案, 通过提升输入电压,设备可以在传输过程中保持较小的电流,从而降低线损和热损. 这就是我们为什么选择升压而不是升电流来提升充电功率的根本原因

    上图展示则是我们常见的 pd 充电的方案, RT1711 等 Sink 端芯片通过 CC(Configuration Channel)线路与适配器(Source)进行通信. 当适配器插入时,RT1711 监测到连接事件并产生中断,通知主控 CPU. CPU 随后通过 I2C 等接口与 RT1711 通讯,获取适配器支持的电压电流档位 (如 9V/2A、9V/3A、12V/2A 等 PDO). 根据设备当前的充电策略和需求(charger alg),CPU 选择一个合适的供电档位,并通过 RT1711 发起 PD 请求,完成协商. 一旦协商成功,VBUS 输出相应电压, cpu 同步通过 i2c 配置 Charge IC 以匹配电池侧的充电电流(Ibat)

  • sink: typec 标准中的受电方设备, RT1711 控制芯片及其所在的终端系统(如手机)
  • source: typec 标准中的供电方, 适配器等

一、pd 快充协议

    标准的 pd 协议其实很简单只有几条命令 Source CapGoodCRCRequestAccept.

  • Source Cap: source 发出的, 描述 source 支持的电压和协议等信息.
  • GoodCRC: 类似于 ack, 无论是 sink 还是 source 收到信息之后必须回复 GoodCRC 表示成功收到信息,需要注意的是这个信号是硬件发出的, 不受软件控制.只要收到信息, 硬件就会立刻回复 goodcrc.
  • Request: 由 sink 发出的, 用于向 source 申请对应的电压和电流
  • Accept: 由 source 发出的, 表示 sink Request 的电压和电流已经准备就绪.

我们可以用 powerz 抓取协议信息, 下图就是用 powerz 抓取的协议信息.

在这里插入图片描述

  1. 插入适配器后适配器(source)往外广播 source cap, rt1711(sink) 收到这个广播后回复 GoodCRC.
  2. 紧接着 rt1711(sink) 需要在 30ms 内立即回复一个 Request , 通过 Request 申请一个适配器支持的电压电流. source 收到这个 Request 后回复 GoodCRC
  3. source 当适配器接受这个 Request 之后会回复 Accept 表示申请成功, 接下来会输出 Request 的电压, sink 收到 Accept 后回复 GoodCRC.

注意: 需要注意的是这里申请的电压电流是上限, 例如 request 9v 3a, 表示适配器最多输出 9v 3a, 不代表它固定输出这么多, 实际输出可能是 8.9V, 1A 这种, 电压在 9V 附近波动, 电流则取决于负载.但是上限是 3A

二、power suplly 子系统

 任何带电池的设备都需要充电的功能, 如上图所示的 mtk 充电结构, 涉及到的模块就有 charger manager, charge icbattery(电量计 ic). charger manager 用来管理充电, 根据电池容量和充电类型让 charge ic 用多大电流多大电压, 以及是否选择快充等. charge ic 用于设置实际的充电电流(ichg), 截止电压(cv 电压), 输入限压(vindpm), 输入限流(iinlim), otg(如果支持otg), 获取 ibat 和 ibus 等功能. battery(电量计 ic) 核心功能就是用于计算返回电池容量, 就是我们系统里面看见的电池百分比, 以及电池的状态信息(充电/停冲)等.

Charger Manager 通常由各个厂商根据自家平台定制, 不同的平台会实现一套自己的 charger maneger.展讯使用的就是 sprd charger manager 它的框架较原生 linux 没有太大打的变化,.mtk 则是在 linux 的基础上自己实现了一套 gm3.0 之类的专有框架. 有些简单的设备不需要显示电量百分比、只需基本充电功能的产品,比如某些红外功能机,甚至可以省略这层管理,直接由 Charge IC 接管所有充电控制.

注意: battery 表示的是电量计 ic, 而不是电池本身,电池的信息由电量计统计, 这里容易搞混.

    我们可以看出硬件相关的有 charge ic 和 battery(电量计 ic). 不同厂家的芯片寄存器有所不同, 但是他们的功能又比较统一, 无论是个厂家生产的 charge ic 的功能都是输入输出限流等功能, 而 batter(电量计 ic) 都是统计电量. 为了统一管理 charge ic 和 battery(电量计 ic) 这类硬件, linux 设计了一个电源管理框架 power supply, 它的核心作用就是解耦合.将 charger ic 和 battery(电量计ic) 的各个功能描述为一个个的 property(属性). 并且将接口统一为两个接口 power_supply_get_property 和 power_supply_set_property. 这样, 不管底层换哪家芯片,charge manager 的逻辑都不用改.只要调这两套接口就行.同时 power supply 将这些 property(属性) 通过属性节点(show/store)开放给用户空间.用户空间可以实时获取充电以及电池的信息的信息. 整体框架如图所示.

在这里插入图片描述

1、数据结构

1.1 power_supply_desc

struct power_supply_desc {const char *name; // 名字, 可以通过名字找到这个设备enum power_supply_type type; // 用于描述这个 psy 的类型 usb/battery等const enum power_supply_usb_type *usb_types; // usb 的类型, SDP, CDP, DCP 等size_t num_usb_types; // usb type 的数量const enum power_supply_property *properties; // 支持的属性列表size_t num_properties;                        // 属性的数量// 获取属性值int (*get_property)(struct power_supply *psy,enum power_supply_property psp,union power_supply_propval *val);// 设置属性值int (*set_property)(struct power_supply *psy,enum power_supply_property psp,const union power_supply_propval *val);// 判断属性值是否可写int (*property_is_writeable)(struct power_supply *psy,enum power_supply_property psp);// 外部电源发生 change 的回调函数void (*external_power_changed)(struct power_supply *psy);void (*set_charged)(struct power_supply *psy);bool no_thermal;int use_for_apm;ANDROID_KABI_RESERVE(1);
};

1.2 power_supply_config

struct power_supply_config {struct device_node *of_node;  // 设备节点struct fwnode_handle *fwnode; // fwnode 节点void *drv_data; // 私有数据const struct attribute_group **attr_grp; // 私有的属性节点char **supplied_to;  // 一个字符串数组, 保存了该 psy 向下一级供电的 psy 列表. 组织 psy 之间的级联方向.size_t num_supplicants;ANDROID_KABI_RESERVE(1);
};

1.3 power_supply

struct power_supply {const struct power_supply_desc *desc; // desc 描述char **supplied_to; // 该 psy 的下一级 psy 列表size_t num_supplicants; // supplied_to 的数量char **supplied_from;  // 向该 psy 供电的 psysize_t num_supplies;   // supplied_from 的数量struct device_node *of_node; // 设备节点void *drv_data; // 所属设备struct device dev; // 私有设备struct work_struct changed_work; // 发生 change 的工作队列struct delayed_work deferred_register_work;// ......ANDROID_KABI_RESERVE(1);
};

1.4 power_supply_type

 用于描述 psy 的类型. 这个结构是用户空间用的, 也就是 /sys/class/power_supply/xxx/type , 如果是 USB 类型设置 POWER_SUPPLY_TYPE_USB 就行了. 注意注释中的 xxx_DCP, xxx_CDP 是历史遗留, 新版本的内核用 power_supply_usb_type 进行细分. 主要是为了兼用一些老的系统应用, 所以保留了.

enum power_supply_type {POWER_SUPPLY_TYPE_UNKNOWN = 0,         // 未知电源类型POWER_SUPPLY_TYPE_BATTERY,             // battery 电池供电POWER_SUPPLY_TYPE_UPS,                 // 不间断电源供电POWER_SUPPLY_TYPE_MAINS,               // ac 充电, 圆圆的充电口POWER_SUPPLY_TYPE_USB,                 // 标准 USB 下行端口/***************** 这些是历史遗留问题为了兼容之前的版本的代码 ***************************/POWER_SUPPLY_TYPE_USB_DCP,             // USB 专用充电端口仅充电电流最高 1.5APOWER_SUPPLY_TYPE_USB_CDP,             // USB 充电下行端口, 支持数据与充电,电流最高 1.5APOWER_SUPPLY_TYPE_USB_ACA,             // USB 附件充电适配器POWER_SUPPLY_TYPE_USB_TYPE_C,          // USB Type-C 端口, 默认 5v3aPOWER_SUPPLY_TYPE_USB_PD,              // USB PD 快充端口POWER_SUPPLY_TYPE_USB_PD_DRP,          // USB PD 双角色端口/***************************** usb end ****************/POWER_SUPPLY_TYPE_APPLE_BRICK_ID,      // Apple 专用充电识别POWER_SUPPLY_TYPE_WIRELESS,            // 无线充电方式
};

例如:

// charge ic 的 type
console:/ # cat /sys/class/power_supply/ba70x-usb/type
USB// battery 的 type
console:/ # cat /sys/class/power_supply/battery/type
Battery

1.5 power_supply_usb_type

    它在新内核中用于细分 usb 类型的接口.

enum power_supply_usb_type {POWER_SUPPLY_USB_TYPE_UNKNOWN = 0,     // 未知类型POWER_SUPPLY_USB_TYPE_SDP,             // 标准下行端口,支持数据传输,最大电流 500mA 或 900mAPOWER_SUPPLY_USB_TYPE_DCP,             // 专用充电端口,不支持数据传输,电流可达 1.5A 或更高POWER_SUPPLY_USB_TYPE_CDP,             // 充电下行端口,支持数据和较大电流(最高 1.5A)POWER_SUPPLY_USB_TYPE_ACA,             // 附件充电适配器,如 OTG 设备带充电功能POWER_SUPPLY_USB_TYPE_C,               // Type-C 接口,默认支持 5V/3APOWER_SUPPLY_USB_TYPE_PD,              // 支持快充的供电类型,可达 20V/5A (最高 100W)POWER_SUPPLY_USB_TYPE_PD_DRP,          // 快充双角色接口,既能供电也能受电POWER_SUPPLY_USB_TYPE_PD_PPS,          // 可编程电源,支持电压电流精细调节(如部分手机快充)POWER_SUPPLY_USB_TYPE_APPLE_BRICK_ID   // 苹果专用充电识别,不同瓦数的充电器(如 5W/10W/20W)
};
1.5.1 POWER_SUPPLY_USB_TYPE_SDP
  • 这种端口的 D+ 和 D- 线上具有 15kΩ 下拉电阻
  • 限流值为:挂起时 2.5mA,连接时为 100mA,连接并配置为较高功率时为 500mA。它其实就是一种普通的 USB 模式。
  • 当 USB 处于这种模式时既可以为外部设备(手机充电、充电宝)充电,也可以起到数据连接的作用(U盘、手机上传/下载)。
1.5.2 POWER_SUPPLY_USB_TYPE_DCP
  • 这种端口不支持任何数据传输, 但能够提供 1.5A 以上的电流。
  • 端口的 D+ 和 D- 线之间短路。
  • 这种类型的端口支持较高充电能力的墙上充电器和车载充电器,无需枚举。它其实就是简单的充电器,当 USB 处于这种模式时只能进行充电而不能进行数据连接。
1.5.3 POWER_SUPPLY_USB_TYPE_CDP
  • 这种端口既支持大电流充电,也支持完全兼容 USB 2.0 的数据传输。
  • 端口具有 D+ 和 D- 通信所必需的 15kΩ 下拉电阻,也具有充电器检测阶段切换的内部电路。内部电路允许便携设备将 CDP 与其它类型端口区分开来。
  • 带有快充功能(1.5A)的 USB 接口,当 USB 处于这种模式时既可以进行快充,也可以起到数据连接的作用。

1.6 power_supply_property

    属性结构, 用于描述支持的属性, 下面列出常见的属性

enum power_supply_property {POWER_SUPPLY_PROP_STATUS = 0,     // 充电状态(如正在充电/未充电/已充满)POWER_SUPPLY_PROP_CHARGE_TYPE,   // 当前充电方式(如恒流/恒压/涓流)POWER_SUPPLY_PROP_HEALTH,        // 电池健康状态(如良好/过热/故障)POWER_SUPPLY_PROP_PRESENT,       // 电源是否存在(例如电池是否插入)POWER_SUPPLY_PROP_ONLINE,        // 电源是否处于在线状态(是否供电中)// ……POWER_SUPPLY_PROP_CAPACITY,      // 电池容量百分比(0~100%)POWER_SUPPLY_PROP_TYPE,          // 充电类型同前面的 power_supply_type POWER_SUPPLY_PROP_USB_TYPE,      // 具体的 usb 类型同前面的 power_supply_usb_type// ……
};
1.6.1 POWER_SUPPLY_PROP_STATUS

 status 属性, 包括:“Unknown”、“Charging”、“Discharging”、“Not charging”、“Full”. 由枚举型变量(POWER_SUPPLY_STATUS_*)定义.

enum {POWER_SUPPLY_STATUS_UNKNOWN = 0,POWER_SUPPLY_STATUS_CHARGING,      // 正在充电POWER_SUPPLY_STATUS_DISCHARGING,   // 正在放电POWER_SUPPLY_STATUS_NOT_CHARGING,  // 未充电POWER_SUPPLY_STATUS_FULL,          // 充满
};
1.6.2 POWER_SUPPLY_PROP_CHARGE_TYPE

 type 充电类型, 包括:“Unknown”、“N/A”、“Trickle”、“Fast”. 由枚举型变量 (POWER_SUPPLY_CHARGE_TYPE_*) 定义.

enum {POWER_SUPPLY_CHARGE_TYPE_UNKNOWN = 0,   // 充电类型:未知POWER_SUPPLY_CHARGE_TYPE_NONE,          // 充电类型:无(没有充电)POWER_SUPPLY_CHARGE_TYPE_TRICKLE,       // 充电类型:缓慢充电POWER_SUPPLY_CHARGE_TYPE_FAST,          // 充电类型:快速充电POWER_SUPPLY_CHARGE_TYPE_TAPER,         // 充电类型:逐渐减少充电
};
1.6.3 POWER_SUPPLY_PROP_HEALTH

 health “健康” 情况,包括:“Unknown”、“Good”、“Overheat”、“Dead”、“Over voltage” 等等. 由枚举型变量 (POWER_SUPPLY_HEALTH_*) 定义。一般用于 battery 类型的 PSY.

enum {POWER_SUPPLY_HEALTH_UNKNOWN = 0,               // 电池健康状态:未知POWER_SUPPLY_HEALTH_GOOD,                      // 电池健康状态:正常POWER_SUPPLY_HEALTH_OVERHEAT,                  // 电池健康状态:过热POWER_SUPPLY_HEALTH_DEAD,                      // 电池健康状态:已损坏POWER_SUPPLY_HEALTH_OVERVOLTAGE,               // 电池健康状态:过电压POWER_SUPPLY_HEALTH_UNSPEC_FAILURE,            // 电池健康状态:未指定的故障POWER_SUPPLY_HEALTH_COLD,                      // 电池健康状态:过冷POWER_SUPPLY_HEALTH_WATCHDOG_TIMER_EXPIRE,     // 电池健康状态:看门狗定时器到期POWER_SUPPLY_HEALTH_SAFETY_TIMER_EXPIRE,       // 电池健康状态:安全定时器到期POWER_SUPPLY_HEALTH_OVERCURRENT,               // 电池健康状态:过电流POWER_SUPPLY_HEALTH_WARM,                      // 电池健康状态:温暖POWER_SUPPLY_HEALTH_COOL,                      // 电池健康状态:凉爽POWER_SUPPLY_HEALTH_HOT,                       // 电池健康状态:过热
};
1.6.4 POWER_SUPPLY_PROP_CAPACITY_LEVEL

 容量,包括:“Unknown”、“Critical”、“Low”, “Normal”、“High”、“Full”.由枚举型变量 ( POWER_SUPPLY_CAPACITY_LEVEL_*) 定义。一般用于 battery 类型的 PSY.

enum {POWER_SUPPLY_CAPACITY_LEVEL_UNKNOWN = 0,      // 电池容量级别:未知POWER_SUPPLY_CAPACITY_LEVEL_CRITICAL,         // 电池容量级别:临界(非常低)POWER_SUPPLY_CAPACITY_LEVEL_LOW,              // 电池容量级别:低POWER_SUPPLY_CAPACITY_LEVEL_NORMAL,           // 电池容量级别:正常POWER_SUPPLY_CAPACITY_LEVEL_HIGH,             // 电池容量级别:高POWER_SUPPLY_CAPACITY_LEVEL_FULL,             // 电池容量级别:充满
};
1.6.5 POWER_SUPPLY_PROP_ONLINE

很重要用来表示是否插入充电器 0 表示没插充电器, 非 0 表示插入了充电器.

2、 重要接口

2.1 power_supply_register

注册 psy 有三个个参数 parent, power_supply_desc 和 power_supply_config, 其中关键点如下.

  1. 第一个参数不能设置为空即不能 power_supply_register(NULL, xxx_desc, xxx_config).
  2. desc->properties 不能设置 POWER_SUPPLY_PROP_USB_TYPE 属性, 因为上层用的是 desc->type 这个属性. 设置这个将导致 psy 注册失败参考下面伪代码.
static enum power_supply_property ba70x_charger_props[] = {//  POWER_SUPPLY_PROP_TYPE, // 不能设置这个属性, 设置这个属性无法注册 psy, 这里设置的属性是开放给用户空间的.// ......
};static int ba70x_usb_get_property(struct power_supply *psy,enum power_supply_property psp,union power_supply_propval *val)
{struct ba70x *ba = power_supply_get_drvdata(psy);switch (psp) {case POWER_SUPPLY_PROP_TYPE: // 可以直接在这里设置, 其他内核模块可以获取这个属性. val->intval = POWER_SUPPLY_TYPE_USB_DCP;default:return -EINVAL;}return 0;
}static const struct power_supply_desc usb_desc = {.name = "ba70x-usb", // 很重要 power_supply_get_by_name 通过这个名字找到这个 psy.type = POWER_SUPPLY_TYPE_USB, // 上层用的是这个获取 POWER_SUPPLY_PROP_TYPE 属性..properties = ba70x_charger_props,.num_properties = ARRAY_SIZE(ba70x_charger_props),.get_property = ba70x_usb_get_property,//.....
};
  1. psy 设备被注册进 power_supply_class 中, 可以通过 power_supply_get_by_name(name) 找到它. 本质就是查找 power_supply_class 中名字为 name 的 psy. 名字通过 psy->desc->name 设置.
  2. 注册会自动创建 power_supply_changed_work 工作队列. 当有事件发生, 例如电池更新了电量, 可以通过这个工作队列通知 supplied_from(向该psy供电的psy) 和 supplied_to(被该psy供电的psy).
power_supply_changed(xxx_psy) -->__power_supply_changed_work() --> // 这里遍历 power_supply_class 上的所有 psy 设备调用 __power_supply_changed_work__power_supply_is_supplied_by() --> // 找到 xxx_psy 的 supplied_from 或者 supplied_topst->desc->external_power_changed() // 调用 supplied_from/supplied_to->desc->external_power_changed 回调通知该 psy

2.2 power_supply_get_property

函数很简单回调 psy->desc->get_property 设置属性值.

// drivers/power/supply/power_supply_core.c
int power_supply_get_property(struct power_supply *psy,enum power_supply_property psp,union power_supply_propval *val)
{if (atomic_read(&psy->use_cnt) <= 0) {if (!psy->initialized)return -EAGAIN;return -ENODEV;}return psy->desc->get_property(psy, psp, val);
}
EXPORT_SYMBOL_GPL(power_supply_get_property);

2.3 power_supply_get_property

回调 psy->desc->get_property 获取属性值.

int power_supply_get_property(struct power_supply *psy,enum power_supply_property psp,union power_supply_propval *val)
{if (atomic_read(&psy->use_cnt) <= 0) {if (!psy->initialized)return -EAGAIN;return -ENODEV;}return psy->desc->get_property(psy, psp, val);
}

2.4 power_supply_changed

当有事情要通知上层或者其他模块的时候调用这个函数. 它主要做了三件事情

  • 回调 supplied_from/supplied_to->desc->external_power_changed 通知 supplied_from/supplied_to
  • 调用内核通知链 power_supply_notifier 上的 call 发出 PSY_EVENT_PROP_CHANGED 事件.
  • 向上层发送 KOBJ_CHANGE 这个 uevent 通知上层系统.
power_supply_changed() -->class_for_each_device(power_supply_class, NULL, psy,__power_supply_changed_work); -->__power_supply_changed_work()-->__power_supply_is_supplied_by(psy, pst)-->pst->desc->external_power_changed() // 通知 supplied_from 和 supplied_toatomic_notifier_call_chain(&power_supply_notifier, PSY_EVENT_PROP_CHANGED, psy); // 调用通知链发起通知kobject_uevent(&psy->dev.kobj, KOBJ_CHANGE); // 向上层发出 KOBJ_CHANGE uevent 事件

三、各个模块简单说明

1、rt1711

    mtk 自己实现了一套 typec 框架. 它的设计思路和前面的 psy 子系统很像, 核心功能就是解耦合, 解除 rt1711 这类充电识别芯片和 mt_pd_adapter 的耦合. 他的源码实现有点绕相关的代码在 drivers/misc/mediatek/typec/ . 整体的框架经过简化后如下图所示.

在这里插入图片描述

总体来说就几个步骤.

  1. rt1711 这类快充芯片通过 tcpc_device_register 将驱动的接口注册进入 tcpc_class.
  2. 然后需要监听快充芯片变化的驱动例如 mt_pd_adapter 会通过 register_tcp_dev_notifier 函数注册一个 call , 当有事件发生时这个 call 会被回调
    info->tcpc = tcpc_dev_get_by_name("type_c_port0");info->pd_nb.notifier_call = pd_tcp_notifier_call;ret = register_tcp_dev_notifier(info->tcpc, &info->pd_nb,TCP_NOTIFY_TYPE_USB | TCP_NOTIFY_TYPE_MISC |TCP_NOTIFY_TYPE_VBUS);// 注册 call 用于监听变化, 当 1711 有对应事件发生会回调这个 call 通知.
static int pd_tcp_notifier_call(struct notifier_block *pnb,unsigned long event, void *data)
{struct tcp_notify *noti = data;struct mtk_pd_adapter_info *pinfo;struct adapter_device *adapter;int ret = 0, sink_mv, sink_ma;pinfo = container_of(pnb, struct mtk_pd_adapter_info, pd_nb);adapter = pinfo->adapter_dev;pr_notice("PD charger event:%d %d\n", (int)event,(int)noti->pd_state.connected);switch (event) {case TCP_NOTIFY_PD_STATE:switch (noti->pd_state.connected) {case  PD_CONNECT_NONE:pinfo->pd_type = MTK_PD_CONNECT_NONE;ret = srcu_notifier_call_chain(&adapter->evt_nh,MTK_PD_CONNECT_NONE, NULL);break;case PD_CONNECT_PE_READY_SNK_PD30: // 识别到支持 pd30 快充pinfo->pd_type = MTK_PD_CONNECT_PE_READY_SNK_PD30;ret = srcu_notifier_call_chain(&adapter->evt_nh,MTK_PD_CONNECT_PE_READY_SNK_PD30, NULL);;break;// ......};break;// ......}
  1. 当插入适配器后 1711 这里充电识别芯片 cc 线收到数据, 或者检测 vbus 会触发中断, 在中断中调用 tcpci_alert 处理, 然后根据寄存器的状态分别处理不同事件, 如需通知其他模块, 则会回调通过 pd_tcp_notifier_call 的 call 函数通知.

如下为触发 pd_tcp_notifier_call 的调用, 又臭又长. 感兴趣可以看看, 除此之外还耦合了其他操作.

rt1711_irq_work_handler() -->tcpci_alert() -->tcpci_alert_handlers[i].handler(tcpc) -->tcpci_alert_recv_msg() -->pd_put_pd_msg_event() -->pd_put_event() -->wake_up(&tcpc->event_wait_que) -->pd_policy_engine_run() -->pd_try_get_next_event() -->pd_try_get_active_event() --> pd_dpm_get_ready_reaction() -->dpm_check_reaction_available() -->reaction->handler(pd_port) -->dpm_reaction_update_pe_ready() -->pd_update_connect_state() -->tcpci_notify_pd_state() -->tcpc_check_notify_time() -->tcp_notify_func() -->srcu_notifier_call_chain(&tcpc->evt_nh[type], state, tcp_noti);-->pd_handle_event() -->pd_process_event()-->

1711 的 dts 配置说明如下

    &rt1711_typec {rt-tcpc,name = "type_c_port0"; // 非常重要设备名, tcpc_dev_get_by_name 的参数.rt-tcpc,role_def = <5>; /* 0: Unknown, 1: SNK, 2: SRC *//* 3: DRP, 4: Try.SRC, 5: Try.SNK */rt-tcpc,rp_level = <0>; /* 0: Default, 1: 1.5, 2: 3.0 */rt-tcpc,vconn_supply  = <1>;  /* 0: Never, 1: Always, *//* 2: EMarkOnly, 3: StartOnly */rt-tcpc,notifier_supply_num = <3>;rt1711pd,intr_gpio = <&pio 5 0x0>; // 中断引脚rt1711pd,intr_gpio_num = <5>;      // 中断引脚pd-data {pd,vid = <0x29cf>; // 芯片 vidpd,pid = <0x1711>; // 芯片 pidpd,source-cap-ext = <0x171129cf 0x00000000 0x000000000x00000000 0x00000000 0x02000000>;pd,mfrs = "RichtekTCPC";/* * ./drivers/misc/mediatek/typec/tcpc/inc/tcpm.h*  VSAFE5V = 0,   // 安全5v供电*  MAX_POWER = 1, // 最大功率*  CUSTOM = 2,    // 自定义策略*  MAX_POWER_LV = 0x21,  // 最大功率 + 偏好低压*   MAX_POWER_LVIC = 0x31 // 最大功率 + 偏低压 + 忽略电流不匹配*  MAX_POWER_HV = 0x41, MAX_POWER_HVIC = 0x51 // 最大功率 + 偏好高压*/pd,charging_policy= <0x31>; // 这里选择 最大功率 + 偏低压 + 忽略电流不匹配/** Fixed 5V, 500 mA <0x00019032>* Fixed 5V, 1A <0x00019064>* Fixed 5V, 2A <0x000190c8>* Fixed 5V, 3A <0x0001912c>* Fixed 9V, 500 mA <0x0002d032>* Fixed 9V, 1A <0x0002d064>* Fixed 9V, 2A <0x0002d0c8>* Fixed 9V, 3A <0x0002d12c>* Variable 5-9V, 1A <0x8642d064>* Variable 5-9V, 2A <0x8642d0c8>* Variable 5-9V, 3A <0x8642d12c>* PPS 3V~5.9V, 3A <0xC0761E3C>*/pd,source-pdo-size = <1>;pd,source-pdo-data = <0x00019032>;// 注意这个要配合前面的 pd,charging_policy 配置// 做 sink 的时候, 插入适配器默认申请的电压// 计算公式: 电压/200 << 12 | 电流/10pd,sink-pdo-size = <1>;pd,sink-pdo-data = <0x0001912c>; // 这里是5v3apd,id-vdo-size = <6>;pd,id-vdo-data = <0xd14029cf 0x0 0x171100000x41800000 0x0 0x21800000>;};};

    rt1711 充电识别芯片的初始化需要关注的就两个, 一个是自己驱动的加载也就是 rt1711.c. 另一个就是 tcpci_late_sync.c 驱动的加载, 这个驱动用来启动初始化流程. 流程如下所示.

在这里插入图片描述

2、mtk_pd_adapter

    mtk_pd_adapter pd 通用适配器驱动同样是为了解耦合的, 同样也是添加一个 adapter_dev 设备到 adapter_class , 然后通过name 获取这个设备, 这个 name 通过 dts 配置.

在这里插入图片描述

其中常用的接口如下

static struct adapter_ops adapter_ops = {.get_status = pd_get_status,.set_cap = pd_set_cap, // 设置 cap 也就是向 1711 申请电流电压.get_output = pd_get_output,.get_property = pd_get_property, // 获取属性, 这里是获取支持的快充类型.get_cap = pd_get_cap, // 获取适配器支持的电压档位. 5v/3a, 9v/2a 等..authentication = pd_authentication,.is_cc = pd_is_cc,.set_wdt = pd_set_wdt,.enable_wdt = pd_enable_wdt,.send_hardreset = pd_send_hardreset,
};

dts 设备树配置如下

// arch/arm64/boot/dts/mediatek/mt6789.dtspd_adapter: pd_adapter {compatible = "mediatek,pd_adapter";boot_mode = <&chosen>;adapter_name = "pd_adapter"; // 名字很重要, 找到这个设备的关键.force_cv;phys = <&u2port0 PHY_TYPE_USB2>;phy-names = "usb2-phy";};

3、charge alg

    这部分是算法模块的集合, mt8786支持 pd, pe, pe2, pe4, pe5 快充, 注意只能选一个, 不能兼容的, 对于不同的快充做了不同的驱动.

drivers/power/supply/mtk_pd.c
drivers/power/supply/mtk_pe.c
drivers/power/supply/mtk_pe2.c
drivers/power/supply/mtk_pe4.c
drivers/power/supply/mtk_pe5.c

本文以 pd 快充为例进行说明, 整体和前面的也没啥太大区别, 通过 chg_alg_device_register 注册一个设备到 charger_algorithm_class 设备类中, 然后 mt_charger 通过 get_chg_alg_by_name 获取这个设备之后通过 algorithm_class 调用 alg_device 的回调函数, 从而实现解耦合.

在这里插入图片描述

static struct chg_alg_ops pd_alg_ops = {.init_algo = _pd_init_algo,  // alg 初始化.is_algo_ready = _pd_is_algo_ready, // pd 快充状态获取, ALG_READY 快充就绪, ALG_NOT_READY 快充没有就绪, PD_TA_NOT_SUPPORT 不支持快充, ALG_RUNNING 正在快充.start_algo = _pd_start_algo,       // 启动快充.is_algo_running = _pd_is_algo_running, // 快充是否已经启动.stop_algo = _pd_stop_algo, // 停止快充.notifier_call = _pd_notifier_call,.get_prop = _pd_get_prop,.set_prop = _pd_set_prop,.set_current_limit = _pd_set_setting,
};

dts 配置如下

    pdc: pdc {compatible = "mediatek,charger,pd";gauge = <&mtk_gauge>;min_charger_voltage = <4600000>;  // 适配器最低的充电电压pd_vbus_low_bound = <5000000>;    // 支持的低电电压pd_vbus_upper_bound = <12000000>; // 支持的最高充电电压, 就是 pd 快充的最高电压vsys_watt = <5000000>;ibus_err = <14>;pd_stop_battery_soc = <90>; // 当电量高于90%之后, 插入适配器不会进入快充/* single charger */sc_input_current = <3200000>;   // 输入电流的最大值(根据代码猜测未实际验证)sc_charger_current = <3000000>; // 充电电流的最大值(根据代码猜测未实际验证)/* dual charger in series*/dcs_input_current = <3200000>;dcs_chg1_charger_current = <1500000>;dcs_chg2_charger_current = <1500000>;/* dual charger */dual_polling_ieoc = <450000>;slave_mivr_diff = <100000>;vbat_threshold = <4150>;
};

4、charge ic

    charge ic 需要实现的有两个部分 charge_device 和 power_supply, mtk 自己实现了一个 charge_device 用于解耦合, 但是实际上也是混着 power_supply 使用的, 从这个方面看其实也没有完全解耦合. 对于 charge_device 的实现也是和前面一样的. 细节在于名字必须使用 primary_chg. 这部分驱动主要用于设置 charge ic 的充电电流(ichg), 输入限流(iinlim)等参数.

// 以下是必须实现的最精简版的 pd 快充需要实现的接口.
static struct charger_ops ba70_chg_ops = {.enable = ba70_enable_charging, // 使能充电.set_charging_current = ba70_set_chg_current, // 设置 ichg.set_input_current = ba70_set_input_current,  // 设置输入限流 iinlim.set_constant_voltage = ba70_set_cv_voltage,  // 设置截止电压 cv.set_mivr = ba70_set_vindpm_voltage,          // 设置输入限压 vindpm.get_ibus_adc = ba70_get_ibus_adc,            // 读取 ibus.get_vbus_adc = ba70_get_vbus_adc,            // 读取 vbus.get_ibat_adc = ba70_get_ibat_adc,            // 读取 ibat
};static int ba70x_charger_probe(struct i2c_client *client, const struct i2c_device_id *id)
{ba->chg_dev_name = "primary_chg"; // 细节在于这个名字必须用 primary_chgba->chg_dev = charger_device_register(ba->chg_dev_name, &client->dev, ba,&ba70_chg_ops, &ba70_chg_props);
}

具体如下图所示.

在这里插入图片描述

    power_supply 这部分驱动则有两个作用一个是提供充电属性 status 和 online 等给上层监听充电状态, 还有就是返回充电类型给到 charge alg. charge alg 需要根据充电类型决定是否进入快充.

static const enum power_supply_usb_type ba70x_usb_types[] = {POWER_SUPPLY_USB_TYPE_SDP,POWER_SUPPLY_USB_TYPE_DCP,POWER_SUPPLY_USB_TYPE_CDP,POWER_SUPPLY_USB_TYPE_UNKNOWN,
};static enum power_supply_property ba70x_charger_props[] = {POWER_SUPPLY_PROP_STATUS,POWER_SUPPLY_PROP_CHARGE_TYPE,POWER_SUPPLY_PROP_ONLINE,POWER_SUPPLY_PROP_VOLTAGE_NOW,POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT,//  POWER_SUPPLY_PROP_TYPE,POWER_SUPPLY_PROP_USB_TYPE,
};static int ba70x_usb_get_property(struct power_supply *psy,enum power_supply_property psp,union power_supply_propval *val)
{struct ba70x *ba = power_supply_get_drvdata(psy);switch (psp) {case POWER_SUPPLY_PROP_STATUS:val->intval = ba70x_get_charging_status(ba);break;case POWER_SUPPLY_PROP_ONLINE:val->intval = ba->online;break;case POWER_SUPPLY_PROP_CHARGE_TYPE:val->intval = ba70x_charge_status(ba);break;case POWER_SUPPLY_PROP_VOLTAGE_NOW:val->intval = ba70x_adc_read_vbus_volt(ba);break;case POWER_SUPPLY_PROP_TYPE:val->intval = POWER_SUPPLY_TYPE_USB_DCP;break;case POWER_SUPPLY_PROP_USB_TYPE:val->intval = POWER_SUPPLY_TYPE_USB;break;case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT:val->intval = 3000000;break;default:return -EINVAL;}return 0;
}static int ba70x_set_property(struct power_supply *psy,enum power_supply_property psp,const union power_supply_propval *val)
{struct ba70x *ba = power_supply_get_drvdata(psy);int ret = 0;switch (psp) {case POWER_SUPPLY_PROP_ONLINE:ba->online = val->intval;schedule_work(&ba->state_work);break;default:return 0;}return ret;
}// USB 电源定义
static const struct power_supply_desc usb_desc = {.name = "ba70x-usb",.type = POWER_SUPPLY_TYPE_USB, // 系统会层监听 usb 类型的设备节点.properties = ba70x_charger_props, // 设置属性, 比较关键的时 online 和 statue 属性.num_properties = ARRAY_SIZE(ba70x_charger_props),.get_property = ba70x_usb_get_property,.set_property = ba70x_set_property, // 设置属性.usb_types = ba70x_usb_types, // 支持的 usb 充电类型.num_usb_types = ARRAY_SIZE(ba70x_usb_types),
};static int ba70x_psy_register(struct ba70x *ba)
{int ret;struct power_supply_config usb_cfg = { .drv_data = ba };ba->usb = devm_power_supply_register(ba->dev, &usb_desc, &usb_cfg);if (IS_ERR(ba->usb)) {ret = PTR_ERR(ba->usb);dev_err(ba->dev, " 1 1Failed to register USB psy: %d\n", ret);}return 0;
}static int ba70x_charger_probe(struct i2c_client *client, const struct i2c_device_id *id)
{ba70x_psy_register(ba);
}

四、进入快充的条件

处于篇幅考虑, 就不一句代码一句代码进行注释说明, 直接给出进入快充的条件.

  1. charge ic 需要实现 POWER_SUPPLY_PROP_TYPE 属性并且返回值只能是 POWER_SUPPLY_TYPE_USB_DCP.
static int ba70x_usb_get_property(struct power_supply *psy,enum power_supply_property psp,union power_supply_propval *val)
{struct ba70x *ba = power_supply_get_drvdata(psy);switch (psp) {case POWER_SUPPLY_PROP_TYPE: // 这个必须返回非 POWER_SUPPLY_TYPE_UNKNOWN, 这个将返回给 info->chr_typeval->intval = POWER_SUPPLY_TYPE_USB_DCP;break;case POWER_SUPPLY_PROP_USB_TYPE: // 这个将返回给 info->usb_typeval->intval = POWER_SUPPLY_TYPE_USB;break;default:return -EINVAL;}return 0;
}
  1. enable_fast_charging_indicator 必须为 true. 这个在 dts 中配置
// arch/arm64/boot/dts/mediatek/mt6768.dts
charger: charger {compatible = "mediatek,charger";// ......enable_fast_charging_indicator;};
  1. info->fast_charging_indicator 设置为需要支持的快充
static int mtk_charger_probe(struct platform_device *pdev)
{info->fast_charging_indicator = 4; // 4 表示 pd 快充return 0;
}

具体参考下表

charge algalg[index]alg_id
pe5info->alg[0]PE5_ID(16)
pe45info->alg[1]PE4_ID(8)
pe4info->alg[2]PE4_ID(8)
pdinfo->alg[3]PDC_ID(4)
pe2info->alg[4]PE2_ID(2)
peinfo->alg[5]PE_ID(1)
  1. 电量低于 90% 则不进快充,这个也是在 dts 中配置
    pdc: pdc {compatible = "mediatek,charger,pd";// .......pd_stop_battery_soc = <90>; // 当电量高于90%之后, 插入适配器不会进入快充
};
  1. 适配器支持 pd 快充, 1711 识别到快充适配器后, 返回 PD_CONNECT_PE_READY_SNK_PD30
static int pd_tcp_notifier_call(struct notifier_block *pnb,unsigned long event, void *data)
{// ......switch (event) {case TCP_NOTIFY_PD_STATE:case PD_CONNECT_PE_READY_SNK_PD30: // 触发这个表示支持适配器支持 pd30pinfo->pd_type = MTK_PD_CONNECT_PE_READY_SNK_PD30;ret = srcu_notifier_call_chain(&adapter->evt_nh,MTK_PD_CONNECT_PE_READY_SNK_PD30, NULL);break;// ......}
  1. 设置好支持的快充最高电压 pd_vbus_upper_bound 它通过 dts 配置
pdc: pdc {compatible = "mediatek,charger,pd";// .......pd_vbus_low_bound = <5000000>;    // 这个是支持的低电电压pd_vbus_upper_bound = <12000000>; // 这个是支持的最高电压 
};

    以上条件都满足就会进入快判断, 注意以上条件是触发快充检测的条件. 真正要进入快充还需要满足升压条件. mtk pd 的升压逻辑还是沿用 gm30 的,就是检测当前需要的功耗是否达到了需要升压的高阈值。如果是则升压,如果低于低阈值则降压,否则使用当前电压, 具体公式如下.

pd_max_watt = 12400000 // 升压功耗的高阈值
pd_min_watt = 7900000  // 升压的功耗低阈值
now_max_watt = cap->max_mv[idx] * ibus + chg2_watt; //当前经过计算需要的功耗

    当 now_max_watt > pd_max_watt 则选择升压到当前电压的高一级电压,当 now_max_watt < pd_min_watt 则降低一级电压,在中间则保持不变.

详细参考 G90(mt6785)-快充配置, 很久之前调试 g90 写过这部分没有变化.

五、遇到的问题以及解决方案

1、pd 协议时序问题

1.1 问题现象

    插入适配器后, 适配器一直复位, 很长时间后进入普通充电无法进入快充.

1.2 问题分析

    使用 powerz 抓波形发现问题是 1711 在收到 source 的 src cap 之后没有及时回复request, 前面有讲过, sin 在收到 source 的 src cap 之后必须在 30ms 内进行回复.

在这里插入图片描述

    由于之前的项目用过这颗 ic 所以排除硬件问题, 我观察发现当前内核版本 kernel-5.10 中断是电平触发, 而之前的项目内核版本是 4.19 驱动用的是跳变沿触发, 于是推断驱动哪里有问题导致的时序异常.

1.3 问题解决

    这个问题尝试过很多修改甚至怀疑过 mtk i2c 控制器有问题, 最终解决方案为移植 4.19 内核的 rt1711 驱动到 kernel-5.10.

2、charge ic 无法及时响应设备插入

2.1 问题现象

    我们用的 charge ic 有问题, 插入后不会立即出发中断, 要等一会才触发, 导致不能及时响应插入这个动作. 因为 mtk 充电驱动会读取 charge ic 的 online 状态.

2.2 解决方案

    巧妙的使用 1711 来解决这个问题, 插入适配器后 1711 会触发中断, 然后 mtk_chg_type_det 会接受到这条消息, 通过它来设置charge ic 的状态为 online.

pd_tcp_notifier_call()-->typec_attach_thread() -->ret = power_supply_set_property(mci->bc12_psy,POWER_SUPPLY_PROP_ONLINE, &val);

所以只需要再 charge ic 驱动中添加下面驱动

static int ba70x_set_property(struct power_supply *psy,enum power_supply_property psp,const union power_supply_propval *val)
{struct ba70x *ba = power_supply_get_drvdata(psy);int ret = 0;printk("%s, %s psp: %d\n", __func__, psy->desc->name , psp);switch (psp) {case POWER_SUPPLY_PROP_ONLINE:ba->online = val->intval; // 添加这个, 这里更新一下 online 的状态break;default:return 0;}return ret;
}

3、charge ic 无 ibus 不进快充.

3.1 问题描述

    无法进快充, 发现跑了快充算法但是没有进快充. 发现是因为这个 charge ic 比较垃圾, 没法读到 ibus.

3.2 解决方案

    造假, 使用一个假的 ibus 让算法强行进入快充.

static int ba70_get_ibus_adc(struct charger_device *dev, u32 *ibus)
{struct ba70x *ba = charger_get_data(dev);u32 vbus = 0;vbus = ba70x_adc_read_vbus_volt(ba);if(vbus >= 4000)*ibus = 2500 * 1000;else if (vbus >= 8000)*ibus = 2333 * 1000;else if(vbus >= 11000)*ibus = 1800 * 1000;printk("%s vbus: %d, ibus: %d\n", __func__, vbus, ibus);return 0;
}static struct charger_ops ba70_chg_ops = {.get_ibus_adc = ba70_get_ibus_adc, // 通过这个接口返回 ibus
}

4、系统层监听不到充电器插入

4.1 问题描述

    插入充电器后系统检测不到充电器插入, 系统监听的油两个属性 status 和 online

console:/ # dumpsys battery
Current Battery Service state:AC powered: falseUSB powered: true // 这个表示插入 usb, 对应的 online 属性Wireless powered: falseMax charging current: 0Max charging voltage: 0Charge counter: 2946000status: 2    // 这个表示正在充电, 对应 status 属性health: 2present: truelevel: 100scale: 100voltage: 4250temperature: 280technology: Li-ion
console:/ #

    系统监听的是 type 为 usb 的节点下的 online 属性 和 status 属性

console:/ # cat /sys/class/power_supply/ba70x-usb/type
USB
console:/ # cat /sys/class/power_supply/ba70x-usb/online
2
console:/ #
console:/ # cat /sys/class/power_supply/ba70x-usb/status
Full

4.2 解决方案

    解决方案就是创建一个工作队列, 然后在插入的时候调用 power_supply_changed 上报 uevent.

diff --git a/device/mediatek/sepolicy/basic/non_plat/genfs_contexts b/device/mediatek/sepolicy/basic/non_plat/genfs_contexts
index d64391e0587..ef75c22bc12 100644
--- a/device/mediatek/sepolicy/basic/non_plat/genfs_contexts
+++ b/device/mediatek/sepolicy/basic/non_plat/genfs_contexts
@@ -897,3 +897,5 @@ genfscon sysfs /devices/platform/11f10000.efuse/mtk-devinfo0/nvmem# For chager enable fast charging algorithmgenfscon sysfs /devices/platform/charger/fast_chg_indicator   u:object_r:sysfs_fs_chg_file:s0
+genfscon sysfs /devices/platform/11004000.i2c/i2c-7/7-006a/power_supply                         u:object_r:sysfs_batteryinfo:s0
+genfscon sysfs /devices/platform/11004000.i2c/i2c-7/7-006a/power_supply/ba70x-usb               u:object_r:sysfs_batteryinfo:s0
diff --git a/kernel-5.10/drivers/power/supply/ba70x_charger.c b/kernel-5.10/drivers/power/supply/ba70x_charger.c
index d3674bc044d..8b2bd512925 100644
--- a/kernel-5.10/drivers/power/supply/ba70x_charger.c
+++ b/kernel-5.10/drivers/power/supply/ba70x_charger.c
@@ -79,7 +79,9 @@ struct ba70x {struct power_supply *usb;struct power_supply *batt_psy;
+       struct power_supply *mt_charger;
+       struct work_struct state_work;struct charger_device* chg_dev;const char *chg_dev_name;char online;
@@ -167,7 +169,6 @@ static int ba70x_enable_otg(struct ba70x *ba)return ba70x_update_bits(ba, BA70X_REG_03,BA70X_OTG_CONFIG_MASK, val);
-}int ba70x_disable_otg(struct ba70x *ba)
@@ -440,7 +441,15 @@ int ba70x_get_charging_status(struct ba70x *ba)}val &= BA70X_CHRG_STAT_MASK;val >>= BA70X_CHRG_STAT_SHIFT;
-       return val;
+
+       if(val == 0x00)
+               return POWER_SUPPLY_STATUS_DISCHARGING;
+       else if(val == 0x01 | val == 0x02)
+               return POWER_SUPPLY_STATUS_CHARGING;
+       else if(val == 0x03)
+               return POWER_SUPPLY_STATUS_FULL;
+
+       return POWER_SUPPLY_STATUS_DISCHARGING;}EXPORT_SYMBOL_GPL(ba70x_get_charging_status);@@ -856,6 +865,7 @@ static int ba70x_charge_status(struct ba70x *ba)}static enum power_supply_property ba70x_charger_props[] = {
+       POWER_SUPPLY_PROP_STATUS,POWER_SUPPLY_PROP_CHARGE_TYPE, /* Charger status output */POWER_SUPPLY_PROP_ONLINE, /* External power source */POWER_SUPPLY_PROP_VOLTAGE_NOW,
@@ -873,6 +883,9 @@ static int ba70x_usb_get_property(struct power_supply *psy,printk("%s, %s psp: %d\n", __func__, psy->desc->name , psp);switch (psp) {
+       case POWER_SUPPLY_PROP_STATUS:
+               val->intval = ba70x_get_charging_status(ba);
+               break;case POWER_SUPPLY_PROP_ONLINE:val->intval = ba->online;break;
@@ -919,6 +932,7 @@ static int ba70x_set_property(struct power_supply *psy,break;case POWER_SUPPLY_PROP_ONLINE:ba->online = val->intval;
+               schedule_work(&ba->state_work);break;case POWER_SUPPLY_PROP_VOLTAGE_NOW:break;
@@ -1077,10 +1091,32 @@ static int ba70x_detect_device(struct ba70x *ba)return ret;}+static void ba70x_charger_state_change(struct work_struct *work)
+{
+       static int old_online = 0;
+       struct ba70x *ba = container_of(work, struct ba70x, state_work);
+
+       printk("%s old_online:%d ba->online:%d\n", __func__, old_online, ba->online);
+
+       if (ba->mt_charger == NULL || IS_ERR(ba->mt_charger)) {
+               ba->mt_charger = power_supply_get_by_name("mtk-master-charger");
+       }
+
+       if (ba->mt_charger == NULL || IS_ERR(ba->mt_charger)) {
+               printk("%s Couldn't get mt_charger\n", __func__);
+       } else {
+               if(old_online != ba->online && (!ba->online || !old_online))
+                       power_supply_changed(ba->mt_charger);
+
+               old_online = ba->online;
+       }
+}
+static irqreturn_t ba70x_charger_interrupt(int irq, void *data){printk("%s\n", __func__);
+return IRQ_HANDLED;}@@ -1350,7 +1386,7 @@ static int ba70x_charger_probe(struct i2c_client *client,ba->status |= BA70X_STATUS_EXIST;}-       ba->batt_psy = power_supply_get_by_name("battery");
+       ba->mt_charger = power_supply_get_by_name("mtk-master-charger");g_ba = ba;@@ -1376,6 +1412,7 @@ static int ba70x_charger_probe(struct i2c_client *client,printk("%s chg_dev register ok\n", __func__);+       INIT_WORK(&ba->state_work, ba70x_charger_state_change);ret = sysfs_create_group(&ba->dev->kobj, &ba70x_attr_group);if (ret) {dev_err(ba->dev, "failed to register sysfs. err: %d\n", ret);

5、插着 usb 开机无法系统识别到充电器

    同样的增加一个 power_supply_changed 上报信息

diff --git a/kernel-5.10/drivers/power/supply/mtk_charger.c b/kernel-5.10/drivers/power/supply/mtk_charger.c
index 293ef7ceb23..a95e139d3bc 100644
--- a/kernel-5.10/drivers/power/supply/mtk_charger.c
+++ b/kernel-5.10/drivers/power/supply/mtk_charger.c
@@ -2710,6 +2710,9 @@ static int charger_routine_thread(void *arg)charger_check_status(info);kpoc_power_off_check(info);+               if(info->psy1 != NULL)
+                       power_supply_changed(info->psy1);
+if (is_disable_charger(info) == false &&is_charger_on == true &&info->can_charging == true) {

六、添加 lk 驱动

    给出关键修改

diff --git a/vendor/mediatek/proprietary/bootable/bootloader/lk/platform/common/power/mtk_charger_intf.c b/vendor/mediatek/proprietary/bootable/bootloader/lk/platform/common/power/mtk_charger_intf.c
index 2a0dee4bbbf..9a706eb8b50 100644
--- a/vendor/mediatek/proprietary/bootable/bootloader/lk/platform/common/power/mtk_charger_intf.c
+++ b/vendor/mediatek/proprietary/bootable/bootloader/lk/platform/common/power/mtk_charger_intf.c
@@ -49,6 +49,7 @@#include "rt9467.h"#include "rt9471.h"#include "bq25890.h"
+#include "ba70x_reg.h"#define MAX_MCHR_INFO_SIZE 2@@ -63,6 +64,7 @@ static int (*mtk_charger_init_list[])(void) = {rt9471_probe,rt9466_probe,rt9467_probe,
+   ba70x_probe,bq25890_probe,#endif};
diff --git a/vendor/mediatek/proprietary/bootable/bootloader/lk/platform/common/power/rules.mk b/vendor/mediatek/proprietary/bootable/bootloader/lk/platform/common/power/rules.mk
index 773f38a5ff6..f134c684f2e 100644
--- a/vendor/mediatek/proprietary/bootable/bootloader/lk/platform/common/power/rules.mk
+++ b/vendor/mediatek/proprietary/bootable/bootloader/lk/platform/common/power/rules.mk
@@ -14,6 +14,12 @@ ifeq ($(MTK_CHARGER_NEW_ARCH),yes)DEFINES += MTK_CHARGER_NEW_ARCHendif+ifeq ($(MTK_BA70X_SUPPORT),yes)
+       OBJS += $(LOCAL_DIR)/ba70x_charger.o
+       DEFINES += MTK_BA70X_SUPPORT
+       DEFINES += SWCHR_POWER_PATH
+endif
+ifeq ($(MTK_MT6370_PMU_CHARGER_SUPPORT),yes)OBJS += $(LOCAL_DIR)/mt6370_pmu_charger.oDEFINES += MTK_MT6370_PMU_CHARGER_SUPPORT
diff --git a/vendor/mediatek/proprietary/bootable/bootloader/lk/platform/mt6768/platform.c b/vendor/mediatek/proprietary/bootable/bootloader/lk/platform/mt6768/platform.c
index ec6a6079ebb..193ee5ebe46 100644
--- a/vendor/mediatek/proprietary/bootable/bootloader/lk/platform/mt6768/platform.c
+++ b/vendor/mediatek/proprietary/bootable/bootloader/lk/platform/mt6768/platform.c
@@ -882,7 +882,8 @@ void platform_init(void)#endif // MTK_BATLOWV_NO_PANEL_ON_EARLYmt_disp_power(TRUE);#ifndef MACH_FPGA_NO_DISPLAY
-           mt_disp_show_low_battery();
+           //mt_disp_show_low_battery();
+           mt_disp_show_boot_logo();#endifmt65xx_leds_brightness_set(6, 110);#ifndef MACH_FPGA_NO_DISPLAY
diff --git a/vendor/mediatek/proprietary/bootable/bootloader/lk/project/tb8786p1_64_k510_wifi.mk b/vendor/mediatek/proprietary/bootable/bootloader/lk/project/tb8786p1_64_k510_wifi.mk
index ee5b00d69bc..e1b0e197dbd 100644
--- a/vendor/mediatek/proprietary/bootable/bootloader/lk/project/tb8786p1_64_k510_wifi.mk
+++ b/vendor/mediatek/proprietary/bootable/bootloader/lk/project/tb8786p1_64_k510_wifi.mk
@@ -10,10 +10,9 @@ MTK_SMI_SUPPORT = yesDEFINES += MTK_NEW_COMBO_EMMC_SUPPORTDEFINES += MTK_GPT_SCHEME_SUPPORTMTK_CHARGER_NEW_ARCH := yes
+MTK_BA70X_SUPPORT := yesMTK_PUMP_EXPRESS_PLUS_SUPPORT := noMTK_CHARGER_INTERFACE := yes
-MTK_MT6370_PMU_CHARGER_SUPPORT := yes
-MTK_MT6370_PMU_BLED_SUPPORT := yesMTK_LCM_PHYSICAL_ROTATION = 90CUSTOM_LK_LCM="hx83102p_wxga_vdo_incell_boe"#nt35595_fhd_dsi_cmd_truly_nt50358 = yes
http://www.dtcms.com/a/293383.html

相关文章:

  • 工业互联网时代,如何通过混合SD-WAN提升煤炭行业智能化网络安全
  • 【Pytorch】数据集的加载和处理(一)
  • 使用ubuntu:20.04和ubuntu:jammy构建secretflow环境
  • ndarray的创建(小白五分钟从入门到精通)
  • 嵌入式开发学习(第三阶段 Linux系统开发)
  • 数据资产——解读数据资产全过程管理手册2025【附全文阅读】
  • [c++11]constexpr
  • 考研数据结构Part1——单链表知识点总结
  • 陷波滤波器设计全解析:原理、传递函数与MATLAB实现
  • Netty中AbstractReferenceCountedByteBuf对AtomicIntegerFieldUpdater的使用
  • 威胁情报:Solana 开源机器人盗币分析
  • Automotive SPICE
  • git的版本冲突
  • 大模型——Data Agent:超越 BI 与 AI 的边界
  • 用ESP32打造全3D打印四驱遥控车:无需APP的Wi-Fi控制方案
  • 从0开始的中后台管理系统-2
  • 课题学习笔记2——中华心法问答系统
  • 汽车行业数字化——解读52页汽车设计制造一体化整车产品生命周期PLM解决方案【附全文阅读】
  • 记录更新时间用java的new date还是数据库的now
  • 深入理解 C 语言数据类型:从内存到应用的全面解析
  • CAN基础知识 - 进阶版
  • 消息推送功能设计指南:精准触达与用户体验的平衡之道
  • Spring Boot 中集成ShardingSphere-JDBC的基本使用
  • Kibana报错[security_exception] current license is non-compliant for [security]
  • HCIA/IP(一二章)笔记
  • TTL+日志的MDC实现简易链路追踪
  • 强化学习理论
  • 计算机是怎么样工作的
  • 在 Ubuntu 22.04 上安装并优化 Nginx nginx入门操作 稍难,需要有一定理论 多理解 多实践
  • Class13预测房价代码