展锐USB 插拔更新充电状态分析

介绍

在charger-manager.c 中有注册usb 状态改变的通知,当我们插拔USB时会触发policy_usb_change_callback() 方法

bsp/kernel/kernel4.14/drivers/power/supply/charger-manager.c

static int charger_manager_policy_init(struct charger_manager *cm)
{
    ...
    cm->policy.usb_notify.notifier_call = policy_usb_change_callback;
    ret = usb_register_notifier(cm->policy.usb_phy, &cm->policy.usb_notify);
    ...
}

在调查USB充电流程时,发现给FW层上报充电状态时,是通过这个回调方法通知FW充电状态发生了变化,然后SystemUI 的Status Bar可以及时更新充电图标。通过这篇文章记录下插拔USB时调用到此回调的一个流程。

USB 插拔中断事件

在我当前的展锐平台上,加载了extcon-usb-gpio 驱动,这个驱动可以检测USB 的插拔动作,当我们插入USB 会触发系统GPIO中断

1. 加载extcon-usb-gpio 驱动

bsp/kernel/kernel4.14/drivers/extcon/Makefile

...
# 开机默认加载此驱动
obj-$(CONFIG_EXTCON_USB_GPIO)    += extcon-usb-gpio.o
...

2. extcon-usb-gpio驱动初始化

bsp/kernel/kernel4.14/drivers/extcon/extcon-usb-gpio.c

static const struct of_device_id usb_extcon_dt_match[] = {
    { .compatible = "linux,extcon-usb-gpio", },
    { /* sentinel */ }
};

static struct platform_driver usb_extcon_driver = {
    .probe        = usb_extcon_probe,  // probe 函数,驱动匹配到了对应的设备后会调用此函数
    .remove        = usb_extcon_remove, // 设备移除后会调用此函数
    .driver        = {
        .name    = "extcon-usb-gpio",  // 该驱动的name
        .pm    = &usb_extcon_pm_ops,  // 电源相关
        .of_match_table = usb_extcon_dt_match, // 用于匹配dts文件里的设备,如果有compatible 相同的设备后才会调用probe 函数
    },
    .id_table = usb_extcon_platform_ids,
};

module_platform_driver(usb_extcon_driver); // 该驱动的入口函数

首先会在dts 文件中匹配compatible为linux,extcon-usb-gpio的文件,需要分析硬件设计图,查看USB插拔的中断口,如下

&extcon_gpio {
	compatible = "linux,extcon-usb-gpio";
	vbus-gpio = <&pmic_eic 0 GPIO_ACTIVE_HIGH>;
	id-gpio = <&eic_debounce 4 GPIO_ACTIVE_HIGH>;
};

probe 函数为驱动的初始化函数,当我们加载此驱动后,会在bus 总线上寻找对应的device,如果有匹配的device,则系统会默认调用此驱动的probe 函数,在如下目录可以发现我们的设备已经成功加载了此驱动

sys/bus/platform/drivers/extcon-usb-gpio

接着查看probe 函数,这里面会获取dts中配置的gpio 口,然后设置中断

bsp/kernel/kernel4.14/drivers/extcon/extcon-usb-gpio.c

static int usb_extcon_probe(struct platform_device *pdev)
{
    ...
    info->id_gpiod = devm_gpiod_get_optional(&pdev->dev, "id", GPIOD_IN);
    info->vbus_gpiod = devm_gpiod_get_optional(&pdev->dev, "vbus", GPIOD_IN);
    ...
    if (info->id_gpiod)
        // 用于防抖的
        ret = gpiod_set_debounce(info->id_gpiod, USB_GPIO_DEBOUNCE_MS * 1000);
    if (!ret && info->vbus_gpiod)
        ret = gpiod_set_debounce(info->vbus_gpiod, USB_GPIO_DEBOUNCE_MS * 1000);
    ...
    if (info->id_gpiod) {
        info->id_irq = gpiod_to_irq(info->id_gpiod); // 将id-gpio 口设置为中断模式
        ...
        // 设置中断条件,发生中断是调用usb_irq_handler() 方法,当GPIO 上拉或者下拉时触发中断
        ret = devm_request_threaded_irq(dev, info->id_irq, NULL,
                        usb_irq_handler,
                        IRQF_TRIGGER_RISING |
                        IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
                        pdev->name, info);
        ...
    }

    if (info->vbus_gpiod) {
        info->vbus_irq = gpiod_to_irq(info->vbus_gpiod); // 将vbus-gpio 口设置为中断模式
        ...
        // 设置中断条件,发生中断是调用usb_irq_handler() 方法,当GPIO 上拉或者下拉时触发中断
        ret = devm_request_threaded_irq(dev, info->vbus_irq, NULL,
                        usb_irq_handler,
                        IRQF_TRIGGER_RISING |
                        IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
                        pdev->name, info);
        ...
    }
    ...
}

3. 中断回调函数

当id-gpio 和 vbus-gpio 口上拉或者下拉时触发中断,调用usb_irq_handler() 中断,上拉或者下拉代表USB 的插入和拔出。

bsp/kernel/kernel4.14/drivers/extcon/extcon-usb-gpio.c

static irqreturn_t usb_irq_handler(int irq, void *dev_id)
{
    struct usb_extcon_info *info = dev_id;
    queue_delayed_work(system_power_efficient_wq, &info->wq_detcable,
               info->debounce_jiffies);
    return IRQ_HANDLED;
}

static int usb_extcon_probe(struct platform_device *pdev)
{
    ...
    // 初始化工作队列wq_detcable 为usb_extcon_detect_cable() 方法
    INIT_DELAYED_WORK(&info->wq_detcable, usb_extcon_detect_cable);
    ...
}

static void usb_extcon_detect_cable(struct work_struct *work)
{
    int id, vbus;
    struct usb_extcon_info *info = container_of(to_delayed_work(work),
                            struct usb_extcon_info,
                            wq_detcable);

    // 读取id 和vbus gpio口,用于判断当前是上拉中断还是下拉产生的中断
    id = info->id_gpiod ?
        gpiod_get_value_cansleep(info->id_gpiod) : 1;
    vbus = info->vbus_gpiod ?
        gpiod_get_value_cansleep(info->vbus_gpiod) : id;

    // 拔出USB 触发的中断
    if (id)
        extcon_set_state_sync(info->edev, EXTCON_USB_HOST, false);
    if (!vbus)
        extcon_set_state_sync(info->edev, EXTCON_USB, false);

    // 插入USB 触发的中断
    if (!id) {
        extcon_set_state_sync(info->edev, EXTCON_USB_HOST, true);
    } else {
        if (vbus)
            extcon_set_state_sync(info->edev, EXTCON_USB, true);
    }
}

在USB插拔触发中断时,会调用info->wq_detcable 工,即作队列会调用usb_extcon_detect_cable() 方法,之后会通过extcon_set_state_sync() 方法设置USB 的状态。

bsp/kernel/kernel4.14/drivers/extcon/extcon.c

int extcon_set_state_sync(struct extcon_dev *edev, unsigned int id, bool state)
{
    int ret, index;
    unsigned long flags;

    index = find_cable_index_by_id(edev, id);
    if (index < 0)
        return index;

    /* Check whether the external connector's state is changed. */
    spin_lock_irqsave(&edev->lock, flags);
    ret = is_extcon_changed(edev, index, state); // 检查USB状态是否发生了变化
    spin_unlock_irqrestore(&edev->lock, flags);
    if (!ret)
        return 0;

    ret = extcon_set_state(edev, id, state); // 设置USB 的连接状态,但是不会通知USB 状态的改变
    if (ret < 0)
        return ret;

    return extcon_sync(edev, id); // 设置USB 的连接状态并通知其他模块
}
EXPORT_SYMBOL_GPL(extcon_set_state_sync);


int extcon_sync(struct extcon_dev *edev, unsigned int id)
{
    ...
    // 通过这个方法通知某个注册了USB 状态改变的模块
    raw_notifier_call_chain(&edev->nh[index], state, edev);

    // 通过这个方法通知所有注册了USB 状态改变的模块
    raw_notifier_call_chain(&edev->nh_all, state, edev);
    ...
    kobject_uevent_env(&edev->dev.kobj, KOBJ_CHANGE, envp); // 通过UEvent 通知用户空间USB发生了改变
    free_page((unsigned long)prop_buf);

    return 0;
}
EXPORT_SYMBOL_GPL(extcon_sync);
bsp/kernel/kernel4.14/kernel/notifier.c

int raw_notifier_call_chain(struct raw_notifier_head *nh,
        unsigned long val, void *v)
{
    return __raw_notifier_call_chain(nh, val, v, -1, NULL);
}
EXPORT_SYMBOL_GPL(raw_notifier_call_chain);

int __raw_notifier_call_chain(struct raw_notifier_head *nh,
                  unsigned long val, void *v,
                  int nr_to_call, int *nr_calls)
{
    return notifier_call_chain(&nh->head, val, v, nr_to_call, nr_calls);
}
EXPORT_SYMBOL_GPL(__raw_notifier_call_chain);

static int notifier_call_chain(struct notifier_block **nl,
                   unsigned long val, void *v,
                   int nr_to_call, int *nr_calls)
{
    int ret = NOTIFY_DONE;
    struct notifier_block *nb, *next_nb;

    nb = rcu_dereference_raw(*nl);

    // 循环读取所有的notifier_block, 然后调用它的notifier_call() 方法
    while (nb && nr_to_call) {
        next_nb = rcu_dereference_raw(nb->next);
        ...
        ret = nb->notifier_call(nb, val, v);

        if (nr_calls)
            (*nr_calls)++;

        if (ret & NOTIFY_STOP_MASK)
            break;
        nb = next_nb;
        nr_to_call--;
    }
    return ret;
}
NOKPROBE_SYMBOL(notifier_call_chain);

从log分析可以看到,nb->notifier_call() 方法会调用sprd_hsphy_vbus_notify() 方法

[14:53:40:749] [ 51.173699] c2 [] (unwind_backtrace) from [] (show_stack+0x20/0x24)␍␊

[14:53:40:757] [ 51.181677] c2 [] (show_stack) from [] (dump_stack+0xa4/0xd8)␍␊

[14:53:40:761] [ 51.189129] c2 [] (dump_stack) from [] (sprd_hsphy_vbus_notify+0x48/0x30c)␍␊

[14:53:40:774] [ 51.197722] c2 [] (sprd_hsphy_vbus_notify) from [] (notifier_call_chain+0x74/0xac)␍␊

[14:53:40:779] [ 51.207006] c2 [] (notifier_call_chain) from [] (raw_notifier_call_chain+0x28/0x30)␍␊

[14:53:40:792] [ 51.216371] c2 [] (raw_notifier_call_chain) from [] (extcon_sync+0x78/0x1e4)␍␊

[14:53:40:801] [ 51.225137] c2 [] (extcon_sync) from [] (extcon_set_state_sync+0x7c/0x90)␍␊

[14:53:40:806] [ 51.233639] c2 [] (extcon_set_state_sync) from [] (usb_extcon_detect_cable+0xa8/0xac)␍␊

[14:53:40:815] [ 51.243185] c2 [] (usb_extcon_detect_cable) from [] (process_one_work+0x290/0x4a8)␍␊

[14:53:40:825] [ 51.252466] c2 [] (process_one_work) from [] (worker_thread+0x2f8/0x478)␍␊

[14:53:40:833] [ 51.260887] c2 [] (worker_thread) from [] (kthread+0x148/0x164)␍␊

[14:53:40:844] [ 51.268519] c2 [] (kthread) from [] (ret_from_fork+0x14/0x20)

接着查看sprd_hsphy_vbus_notify() 方法的流程

bsp/kernel/kernel4.14/drivers/usb/phy/phy-sprd-sharkle.c

static int sprd_hsphy_vbus_notify(struct notifier_block *nb,
                  unsigned long event, void *data)
{
    struct usb_phy *usb_phy = container_of(nb, struct usb_phy, vbus_nb);
    struct sprd_hsphy *phy = container_of(usb_phy, struct sprd_hsphy, phy);
    ...
    // event 的值是我们传入的USB 状态(state),state = 1 表示USB 插入,否则没有插入
    if (event) {
        /* usb vbus valid */
        reg = readl_relaxed(phy->base + REG_AP_AHB_OTG_PHY_TEST);
        reg |= (MASK_AP_AHB_OTG_VBUS_VALID_EXT |
             MASK_AP_AHB_OTG_VBUS_VALID_PHYREG);
        writel_relaxed(reg, phy->base + REG_AP_AHB_OTG_PHY_TEST);
        usb_phy_set_charger_state(usb_phy, USB_CHARGER_PRESENT);
    } else {
        /* usb vbus invalid */
        reg = readl_relaxed(phy->base + REG_AP_AHB_OTG_PHY_TEST);
        reg &= ~(MASK_AP_AHB_OTG_VBUS_VALID_PHYREG |
            MASK_AP_AHB_OTG_VBUS_VALID_EXT);
        writel_relaxed(reg, phy->base + REG_AP_AHB_OTG_PHY_TEST);
        usb_phy_set_charger_state(usb_phy, USB_CHARGER_ABSENT);
    }

    return 0;
}
bsp/kernel/kernel4.14/drivers/usb/phy/phy.c

void usb_phy_set_charger_state(struct usb_phy *usb_phy,
                   enum usb_charger_state state)
{
    ...
    usb_phy->chg_state = state;
    if (usb_phy->chg_state != USB_CHARGER_PRESENT)
        usb_phy->chg_type = UNKNOWN_TYPE;

    schedule_work(&usb_phy->chg_work);
}
EXPORT_SYMBOL_GPL(usb_phy_set_charger_state);

static void usb_charger_init(struct usb_phy *usb_phy)
{
    usb_phy->chg_type = UNKNOWN_TYPE;
    usb_phy->chg_state = USB_CHARGER_DEFAULT;
    usb_phy_set_default_current(usb_phy);
    INIT_WORK(&usb_phy->chg_work, usb_phy_notify_charger_work); // 初始化usb_phy->chg_work 工作队列
}

调用usb_phy_set_charger_state() 方法后,会调用usb_phy_notify_charger_work() 方法

bsp/kernel/kernel4.14/drivers/usb/phy/phy.c

static void usb_phy_notify_charger_work(struct work_struct *work)
{
    ...
    // 判断USB的插入状态,如果是插入状态,则走USB_CHARGER_PRESENT 分支
    switch (usb_phy->chg_state) {
    case USB_CHARGER_PRESENT:
        if (usb_phy->chg_type == UNKNOWN_TYPE)
            usb_phy->chg_type = usb_phy->charger_detect(usb_phy);

        usb_phy_get_charger_current(usb_phy, &min, &max);

        atomic_notifier_call_chain(&usb_phy->notifier, max, usb_phy); //调用usb_phy中注册的回调方法
        snprintf(uchger_state, ARRAY_SIZE(uchger_state),
             "USB_CHARGER_STATE=%s", "USB_CHARGER_PRESENT");
        break;
    case USB_CHARGER_ABSENT:
        usb_phy_set_default_current(usb_phy);

        atomic_notifier_call_chain(&usb_phy->notifier, 0, usb_phy); //调用usb_phy中注册的回调方法
        snprintf(uchger_state, ARRAY_SIZE(uchger_state),
             "USB_CHARGER_STATE=%s", "USB_CHARGER_ABSENT");
        break;
    default:
        ...

    // 使用ueven 通知方法通知用户
    kobject_uevent_env(&usb_phy->dev->kobj, KOBJ_CHANGE, envp);
}

atomic_notifier_call_chain() 方法定义在notifier.c 中

bsp/kernel/kernel4.14/kernel/notifier.c

int atomic_notifier_call_chain(struct atomic_notifier_head *nh,
                   unsigned long val, void *v)
{
    return __atomic_notifier_call_chain(nh, val, v, -1, NULL);
}

int __atomic_notifier_call_chain(struct atomic_notifier_head *nh,
                 unsigned long val, void *v,
                 int nr_to_call, int *nr_calls)
{
    ...
    ret = notifier_call_chain(&nh->head, val, v, nr_to_call, nr_calls);
    ...
    return ret;
}

static int notifier_call_chain(struct notifier_block **nl,
                   unsigned long val, void *v,
                   int nr_to_call, int *nr_calls)
{
    
    ...
    nb = rcu_dereference_raw(*nl); // 这个nb 是usb_phy 通知块的头部块
    // 回调所有注册的notifier_call 函数
    while (nb && nr_to_call) {
        next_nb = rcu_dereference_raw(nb->next);

        ...
        ret = nb->notifier_call(nb, val, v);

        if (nr_calls)
            (*nr_calls)++;

        if (ret & NOTIFY_STOP_MASK)
            break;
        nb = next_nb;
        nr_to_call--;
    }
    return ret;
}

从usb_phy_notify_charger_work() 方法传入的nh是usb_phy。

以charger-manager 为例,在charger-manager.c 的charger_manager_policy_init() 方法中注册了usb_phy.notifier_call 方法

4. charger-manager 注册USB 状态改变通知

bsp/kernel/kernel4.14/drivers/power/supply/charger-manager.c

static int charger_manager_policy_init(struct charger_manager *cm)
{
    ...
    cm->policy.usb_notify.notifier_call = policy_usb_change_callback;
    ret = usb_register_notifier(cm->policy.usb_phy, &cm->policy.usb_notify);
    ...
}

 通过usb_register_notifier() 方法注册回调方法

bsp/kernel/kernel4.14/include/linux/usb/phy.h

static inline int
usb_register_notifier(struct usb_phy *x, struct notifier_block *nb)
{
    return atomic_notifier_chain_register(&x->notifier, nb);
}

将回调添加到usb_phy 通知块的头部,如果USB 发生了中断,则会调用到这个回调

bsp/kernel/kernel4.14/kernel/notifier.c

int atomic_notifier_chain_register(struct atomic_notifier_head *nh,
        struct notifier_block *n)
{
    ...
    ret = notifier_chain_register(&nh->head, n);
    ...
}

// 将usb_notify 通知块添加到usb_phy 里通知块的头部
static int notifier_chain_register(struct notifier_block **nl,
        struct notifier_block *n)
{
    while ((*nl) != NULL) {
        if (n->priority > (*nl)->priority)
            break;
        nl = &((*nl)->next);
    }
    n->next = *nl;
    rcu_assign_pointer(*nl, n);
    return 0;
}

所以在我们插入USB时,会调用到charger-manager.c::policy_usb_change_callback() 方法

你可能感兴趣的:(Android,电源,android,power_supply流程,USB插拔事件分发,USB充电状态)