首页 > 其他分享 >extcon驱动及其在USB驱动中的应用

extcon驱动及其在USB驱动中的应用

时间:2024-05-29 17:34:36浏览次数:27  
标签:dwc3 USB vbus extcon 驱动 mdwc edev

一、简介

extcon 是External Connector的简称,用于抽象外部连接器,比如说Audio Jack、USB MicroB/TypeC接口等。它的原型是Android的switch-class驱动,经过修改后在kernel 3.4.0版本时被引入内核中。

高通(Qualcomm)平台的USB相关的EXTCON(External Connector)功能主要涉及到了对外部连接器状态的检测和响应机制,特别是当USB端口上有设备连接或断开时。EXTCON框架是Linux内核的一部分,用于统一管理所有类型的外部连接器,包括USB、耳机插孔、Docking接口等。在高通平台中,EXTCON常用于实现USB OTG(On-The-Go)模式的自动切换、USB设备接入的快速响应以及电源管理等功能。

二、功能介绍

extcon驱动的主要功能是识别外部连接器状态变化,并将状态变化通知到与外部连接器相关的其他驱动。

使用extcon驱动,有什么好处呢?之前的内核都没有extcon驱动,又是怎么处理这些外部连接器的?不妨以USB驱动为例,看看使用extcon驱动前后的变化。

USB常见的外部接口有TypeA/B/C三种,其中TypeA/B又有标准A/B、Mini A/B和Micro A/B三种,直接上图:

这三种不同的接口,TypeA/B只是物理信号上的连接,主控芯片内部没有针对TypeA/B的专用控制器,可通过VBUS和ID两个脚的状态来识别是否接入了USB主机或USB外设。接入主机前,VBUS脚上没有电压,接入主机后,主机端会在VBUS脚上提供5V电压;接入外设前,ID脚为高电平,接入外设后,ID脚被拉低。于是软件可以通过主动读取这两个脚的电平或者异步响应这两个脚的中断来获知状态的变化。

TypeC就有点特别,从TypeC规范可以看到,TypeC是有一个状态机的,从Unattached状态走到Attached Sink状态(做从设备)或者Attached Source状态(做主机),主控芯片内部是有相应的控制器的,控制器会通过寄存器汇报状态变化,并产生中断通知主控。TypeC控制器需要软件进行相应的编程来配置和使能它。

 

以上就是USB针对不同外部接口所面临的状况。在extcon驱动出现之前,同一份USB控制器驱动代码,比较常见的做法就是在设备树(dts)中指明是哪种接口,USB控制器驱动代码中会解析设备树中的定义,通过if...else...来走不同的代码逻辑。如果是MicroB接口,就注册VBUS和ID脚的中断、查询IO脚的电平状态;如果是TypeC接口,就注册TypeC的中断,查询TypeC的状态。假设后续又有新的接口出现,工作原理不同于已有的接口,那就又需要在USB控制器驱动中去增加相关代码。

在extcon驱动出现后,USB控制器驱动就能和外部接口驱动解耦。在USB控制器驱动看来,不管外部接口是什么,我只需知道外部接口状态的变化就好了,比如是否接入主机了、是否有设备接入了。使用extcon驱动提供的函数接口来注册notifier,当外部接口状态变化时,extcon驱动负责回调notifier,USB控制器驱动代码无需再针对不同的外部接口改来改去。不同的外部接口,都用extcon来抽象自己的行为。

三、高通QCM6490平台extcon通知机制实例解析

1、dts说明

qcom,pmic_glink {
                compatible = "qcom,pmic-glink";
                qcom,pmic-glink-channel = "PMIC_RTR_ADSP_APPS";
                qcom,subsys-name = "adsp";
                qcom,protection-domain = "tms/servreg", "msm/adsp/charger_pd";

                battery_charger: qcom,battery_charger {
                        compatible = "qcom,battery-charger";
                };

                qcom,ucsi {
                        compatible = "qcom,ucsi-glink";
                        port {
                                usb_port0_connector: endpoint {
                                        remote-endpoint = <&usb_port0>;
                                };
                        };
                };

                altmode: qcom,altmode {
                        compatible = "qcom,altmode-glink";
                        #altmode-cells = <1>;
                };
        };

//usb0的外部连接器为battery charger

&usb0 {
        fibo,ss_switch_power = <&tlmm 106 GPIO_ACTIVE_HIGH>;
        extcon = <&battery_charger>;
};

2、驱动的部分主要涉及两个部分

dwc3-msm.c和qti_battery_charger.c

(1)extcon设备的注册

  • dwc3-msm.c中extcon注册的过程:
if (of_property_read_bool(node, "extcon") ) {    //从dts中读取extcon,查看是否有extcon设备,如果有会进行注册extcon设备
                ret = dwc3_msm_extcon_register(mdwc);
.............
        }
  • dwc3_msm_extcon_register注册设备:
static int dwc3_msm_extcon_register(struct dwc3_msm *mdwc)
{
        struct device_node *node = mdwc->dev->of_node;
        struct extcon_dev *edev;
        int idx, extcon_cnt, ret = 0;
        bool check_vbus_state, check_id_state, phandle_found = false;

        extcon_cnt = of_count_phandle_with_args(node, "extcon", NULL);
        if (extcon_cnt < 0) {
                dev_err(mdwc->dev, "of_count_phandle_with_args failed\n");
                return -ENODEV;
        }

        mdwc->extcon = devm_kcalloc(mdwc->dev, extcon_cnt,
                                        sizeof(*mdwc->extcon), GFP_KERNEL);
        if (!mdwc->extcon)
                return -ENOMEM;

        for (idx = 0; idx < extcon_cnt; idx++) {
                edev = extcon_get_edev_by_phandle(mdwc->dev, idx); //读取具体的extcon设备,目前我们dts中的extcon设备为battery_charger
                if (IS_ERR(edev) && PTR_ERR(edev) != -ENODEV)
                        return PTR_ERR(edev);

                if (IS_ERR_OR_NULL(edev))
                        continue;

                check_vbus_state = check_id_state = true;
                phandle_found = true;
                mdwc->extcon[idx].mdwc = mdwc;
                mdwc->extcon[idx].edev = edev;
                mdwc->extcon[idx].idx = idx;
               //注册id和vbus的通知
                mdwc->extcon[idx].vbus_nb.notifier_call =
                                                dwc3_msm_vbus_notifier;
                ret = extcon_register_notifier(edev, EXTCON_USB,
                                                &mdwc->extcon[idx].vbus_nb);
                if (ret < 0)
                        check_vbus_state = false;

                mdwc->extcon[idx].id_nb.notifier_call = dwc3_msm_id_notifier;
                ret = extcon_register_notifier(edev, EXTCON_USB_HOST,
                                                &mdwc->extcon[idx].id_nb);
                if (ret < 0)
                        check_id_state = false;

                /* Update initial VBUS/ID state */
                if (check_vbus_state && extcon_get_state(edev, EXTCON_USB))
  • dwc3_msm_vbus_notifier通知函数:
static int dwc3_msm_vbus_notifier(struct notifier_block *nb,
        unsigned long event, void *ptr)
{
        struct dwc3 *dwc;
        struct extcon_dev *edev = ptr;
        struct extcon_nb *enb = container_of(nb, struct extcon_nb, vbus_nb);
        struct dwc3_msm *mdwc = enb->mdwc;
        char *eud_str;
        const char *edev_name;
        bool is_cdp;
    printk(KERN_ERR"***dwc3_msm_vbus_notifier*******");
        if (!edev || !mdwc)
                return NOTIFY_DONE;

        dwc = platform_get_drvdata(mdwc->dwc3);

        printk(KERN_ERR"***dwc3_msm_vbus_notifier****extcon idx=%d********", enb->idx);
        dev_err(mdwc->dev, "vbus:%ld event received\n", event);
        edev_name = extcon_get_edev_name(edev);
        dbg_log_string("edev:%s\n", edev_name);

        /* detect USB spoof disconnect/connect notification with EUD device */
        eud_str = strnstr(edev_name, "eud", strlen(edev_name));
        printk(KERN_ERR"***dwc3_msm_vbus_notifier****eud_str=%s********",eud_str);
        if (eud_str) {
                if (mdwc->eud_active == event)
                        return NOTIFY_DONE;
                mdwc->eud_active = event;
                mdwc->check_eud_state = true;
        } else {
                if (mdwc->vbus_active == event)
                        return NOTIFY_DONE;
                mdwc->vbus_active = event;
        }

        /*
         * In case of ADSP based charger detection driving a pulse on
         * DP to ensure proper CDP detection will be taken care by
         * ADSP.
         */
        is_cdp = ((mdwc->apsd_source == IIO) &&
                (get_chg_type(mdwc) == POWER_SUPPLY_TYPE_USB_CDP)) ||
                ((mdwc->apsd_source == PSY) &&
                (get_chg_type(mdwc) == POWER_SUPPLY_USB_TYPE_CDP));

        /*
         * Drive a pulse on DP to ensure proper CDP detection
         * and only when the vbus connect event is a valid one.
         */
        if (is_cdp && mdwc->vbus_active && !mdwc->check_eud_state) {
                dev_err(mdwc->dev, "Connected to CDP, pull DP up\n");
                mdwc->hs_phy->charger_detect(mdwc->hs_phy);
        }

        mdwc->ext_idx = enb->idx;
        if (dwc->dr_mode == USB_DR_MODE_OTG && !mdwc->in_restart)
                queue_work(mdwc->dwc3_wq, &mdwc->resume_work);
        return NOTIFY_DONE;
}
  • dwc3_msm_id_notifier通知函数 
static int dwc3_msm_id_notifier(struct notifier_block *nb,
        unsigned long event, void *ptr)
{
        struct dwc3 *dwc;
        struct extcon_dev *edev = ptr;
        struct extcon_nb *enb = container_of(nb, struct extcon_nb, id_nb);
        struct dwc3_msm *mdwc = enb->mdwc;
        enum dwc3_id_state id;
    printk(KERN_ERR"\n*****dwc3_msm_id_notifier****\n");
        if (!edev || !mdwc)
                return NOTIFY_DONE;

        dwc = platform_get_drvdata(mdwc->dwc3);

        dbg_event(0xFF, "extcon idx", enb->idx);

        id = event ? DWC3_ID_GROUND : DWC3_ID_FLOAT;

        if (mdwc->id_state == id)
                return NOTIFY_DONE;

        mdwc->ext_idx = enb->idx;

        dev_err(mdwc->dev, "host:%ld (id:%d) event received\n", event, id);

        mdwc->id_state = id;
        dbg_event(0xFF, "id_state", mdwc->id_state);
        queue_work(mdwc->dwc3_wq, &mdwc->resume_work);

        return NOTIFY_DONE;
}

static void check_for_sdp_connection(struct work_struct *w)
{
        struct dwc3_msm *mdwc =
                container_of(w, struct dwc3_msm, sdp_check.work);
        struct dwc3 *dwc = platform_get_drvdata(mdwc->dwc3);

        if (!mdwc->vbus_active)
                return;

        /* floating D+/D- lines detected */
        if (dwc->gadget.state < USB_STATE_DEFAULT &&
                dwc3_gadget_get_link_state(dwc) != DWC3_LINK_STATE_CMPLY) {
                mdwc->vbus_active = false;
                dbg_event(0xFF, "Q RW SPD CHK", mdwc->vbus_active);
                queue_work(mdwc->dwc3_wq, &mdwc->resume_work);
        }
}

(2)qti_battery_charger.c中通过extcon_set_state_sync接口去发通知触发dwc3_msm_vbus_notifier和dwc3_msm_id_notifier

 

 

 

标签:dwc3,USB,vbus,extcon,驱动,mdwc,edev
From: https://www.cnblogs.com/yuanqiangfei/p/18220743

相关文章

  • Linux块设备驱动
    1块设备驱动简介块设备是针对存储设备的,比如SD卡、EMMC、NANDFlash、NorFlash、SPIFlash、机械硬盘、固态硬盘等。因此块设备驱动其实就是这些存储设备驱动。块设备驱动比字符设备复杂,不同类型的存储设备又对应不同的驱动子系统,如MTD(memorytechnologydevice内存技术设备......
  • 最简单/最常见的行情驱动
    Onbar,Onbarclose,OnbaropenOnbar其实就是将k线数据遍历,一个bar会上传tick数据,包括单位时间内的最高成交价格,最低成交价格,开盘价和收盘价。通过对价格的遍历,绘制指标和进行行情变化的判断是否执行行情数据策略。 macd指标/*macdmacd其实一根线a是对k线进行加权平均另一根线b是......
  • 分清策略的驱动模式:事件驱动概念
    如何理解事件驱动概念?比方说我是一家理发店的店长,我设计好了一套从顾客进店开始的流程我把这套流程交给门口的迎宾人员,请问什么时候这个迎宾人员会去执行这套流程?答案是:客人来了事件驱动其实就是,当某一个事情发生以后,我执行对应的操作。也就是if条件:执行事件判断一下驱动方......
  • AP9180内置 MOS 管升压型恒流驱动芯片
    概述 AP9180是一款高效率、高精度的升压型大功率LED灯恒流驱动芯片。AP9180内置高精度误差放大器,固定关断时间控制电路,恒流驱动电路等,特别适合大功率、多个高亮度LED灯串的恒流驱动。AP9180采用固定关断时间的控制方式,其工作频率最高可达350KHz,可使外部电感和滤波电容体积......
  • 【数据驱动】【航空航天结构的高效损伤检测技术】一种数据驱动的结构健康监测(SHM)方法,
     ......
  • AP9193 LED大功率升压恒流驱动芯片
    概述AP9193是一款高效率、高精度的升压型大功率LED灯恒流驱动控制芯片。AP9193内置高精度误差放大器,固定关断时间控制电路,恒流驱动电路等,特别适合大功率、多个高亮度LED灯的串恒流驱动。AP9193采用固定关断时间的控制方式,其工作频率最高可达1MHz,可使外部电感和滤波电容体积减......
  • AP9195 LED大功率升压恒流驱动芯片
    概述 AP9195是一款高效率、高精度的升压型大功率LED灯恒流驱动控制芯片。AP9195内置高精度误差放大器,固定关断时间控制电路,恒流驱动电路等,特别适合大功率、多个高亮度LED灯串的恒流驱动。AP9195通过调节外置的电流采样电阻,能控制高亮度LED灯的驱动电流,使LED灯亮度达到预期恒定......
  • [IMX6ULL驱动开发]-Linux对中断的处理(二)
    上一篇文章中,引入了Linux对于中断的一些简略流程以及中断抽象为具体实际形象。此文章主要是继续加深对Linux对中断的处理流程以及一些相应的数据结构。目录Linux对中断的扩展:硬件中断、软件中断多中断处理中断上下部处理流程发生中断A,并被中断A打断发生中断A,并被中断B打......
  • Dolphinscheduler不重启加载Oracle驱动
    转载自刘茫茫看山问题背景某天我们的租户反馈数据库连接缺少必要的驱动,我们通过日志查看确实是缺少部分数据库的驱动,因为DolphinScheduler默认只带了Oracle和MySQL的驱动,并且需要将pom文件中的test模式去掉才可以在打包的时候引入。我们的任务量比较大,在3.0存在容错机制的情况下......
  • SD8002D单声道功率放大器输入1KHZ5V电压驱动功率SOP8封装2.0V-5.5V
    SD8002D是一款AB类,单声道带关断模式,桥式音频功率放大器。在输入1KHz,5V工作电压时,最大驱动功率为:3W,(4Ω负载,总谐波失真<10%),2W,(4Ω负载,总谐波失真<1%);音频范围内总谐波失真噪音小于1%(20赫兹·20KHz);SD8002D应用电路简单,只需要极少数外围器件,就能提供高品质的输出功率。......