赞
踩
最近在chg驱动和usb驱动中经常看见extcon的踪影,打算整理汇总一下extcon相关的知识。
extcon:External Connector framework
从名字看主要表征外部连接器的,通过gpio状态去识别外部连接器的类型,并通知关心外部连接器状态的驱动。
主要驱动代码路径:
kernel/msm-4.19/include/linux/extcon.h
kernel/msm-4.19/drivers/extcon
从下图中可以看到常见的usb 充电 显示接口 耳机等等都可以使用extcon来做状态通知和同步
主要的API接口如下:
//获取id当前的连接状态 extern int extcon_get_state(struct extcon_dev *edev, unsigned int id); /* * Following APIs get the property of each external connector. * The 'id' argument indicates the defined external connector * and the 'prop' indicates the extcon property. * * And extcon_get_property_capability() get the capability of the property * for each external connector. They are used to get the capability of the * property of each external connector based on the id and property. */ extern int extcon_get_property(struct extcon_dev *edev, unsigned int id, unsigned int prop, union extcon_property_value *prop_val); extern int extcon_get_property_capability(struct extcon_dev *edev, unsigned int id, unsigned int prop); /* * Following APIs set array of mutually exclusive. * The 'exclusive' argument indicates the array of mutually exclusive set * of cables that cannot be attached simultaneously. */ extern int extcon_set_mutually_exclusive(struct extcon_dev *edev, const u32 *exclusive); /* * Following APIs register the notifier block in order to detect * the change of both state and property value for each external connector. * * extcon_register_notifier(*edev, id, *nb) : Register a notifier block * for specific external connector of the extcon. * extcon_register_notifier_all(*edev, *nb) : Register a notifier block * for all supported external connectors of the extcon. */ extern int extcon_register_notifier(struct extcon_dev *edev, unsigned int id, struct notifier_block *nb); extern int extcon_unregister_notifier(struct extcon_dev *edev, unsigned int id, struct notifier_block *nb); extern int extcon_register_blocking_notifier(struct extcon_dev *edev, unsigned int id, struct notifier_block *nb); extern int extcon_unregister_blocking_notifier(struct extcon_dev *edev, unsigned int id, struct notifier_block *nb); extern int devm_extcon_register_notifier(struct device *dev, struct extcon_dev *edev, unsigned int id, struct notifier_block *nb); extern void devm_extcon_unregister_notifier(struct device *dev, struct extcon_dev *edev, unsigned int id, struct notifier_block *nb); extern int extcon_register_notifier_all(struct extcon_dev *edev, struct notifier_block *nb); extern int extcon_unregister_notifier_all(struct extcon_dev *edev, struct notifier_block *nb); extern int devm_extcon_register_notifier_all(struct device *dev, struct extcon_dev *edev, struct notifier_block *nb); extern void devm_extcon_unregister_notifier_all(struct device *dev, struct extcon_dev *edev, struct notifier_block *nb); /* * Following APIs get the extcon_dev from devicetree or by through extcon name. */ extern struct extcon_dev *extcon_get_extcon_dev(const char *extcon_name); extern struct extcon_dev *extcon_find_edev_by_node(struct device_node *node); extern struct extcon_dev *extcon_get_edev_by_phandle(struct device *dev, int index); /* Following API get the name of extcon device. */ extern const char *extcon_get_edev_name(struct extcon_dev *edev); extern int extcon_blocking_sync(struct extcon_dev *edev, unsigned int id, u8 val);
用法实例:
static const unsigned int smblib_extcon_cable[] = { EXTCON_USB, EXTCON_USB_HOST, EXTCON_NONE, }; /* extcon alloc*/ chg->extcon = devm_extcon_dev_allocate(chg->dev, smblib_extcon_cable); /* extcon registration */ rc = devm_extcon_dev_register(chg->dev, chg->extcon); /* Support reporting polarity and speed via properties */ /*EXTCON_USB/EXTCON_USB_HOST 支持设置typec的方向还有usb的速度 */ rc = extcon_set_property_capability(chg->extcon, EXTCON_USB, EXTCON_PROP_USB_TYPEC_POLARITY); rc |= extcon_set_property_capability(chg->extcon, EXTCON_USB, EXTCON_PROP_USB_SS); rc |= extcon_set_property_capability(chg->extcon, EXTCON_USB_HOST, EXTCON_PROP_USB_TYPEC_POLARITY); rc |= extcon_set_property_capability(chg->extcon, EXTCON_USB_HOST, EXTCON_PROP_USB_SS); /*设置EXTCON_USB 的EXTCON_PROP_USB_TYPEC_POLARITY 属性为 val*/ extcon_set_property(chg->extcon, EXTCON_USB, EXTCON_PROP_USB_TYPEC_POLARITY, val); /*读取EXTCON_USB 的EXTCON_PROP_USB_TYPEC_POLARITY 属性*/ ret = extcon_get_property(edev, EXTCON_USB, EXTCON_PROP_USB_SS, &val);
下面完整看下手机里面的extcon部分驱动:
dts配置:
driver:
usb_extcon_probe关键函数如下:解析dts的gpio配置并注册中断usb_irq_handler
static const unsigned int usb_extcon_cable[] = { EXTCON_USB, EXTCON_USB_HOST, EXTCON_NONE, }; static int usb_extcon_probe(struct platform_device *pdev) { ...... info = devm_kzalloc(&pdev->dev, sizeof(*info), GFP_KERNEL); info->id_gpiod = devm_gpiod_get_optional(&pdev->dev, "id", GPIOD_IN); info->vbus_gpiod = devm_gpiod_get_optional(&pdev->dev, "vbus", GPIOD_IN); info->vbus_out_gpiod = devm_gpiod_get_optional(&pdev->dev, "vbus-out", GPIOD_OUT_HIGH); ..... info->edev = devm_extcon_dev_allocate(dev, usb_extcon_cable); ret = devm_extcon_dev_register(dev, info->edev); INIT_DELAYED_WORK(&info->wq_detcable, usb_extcon_detect_cable); if (info->id_gpiod) { info->id_irq = gpiod_to_irq(info->id_gpiod); 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); ret = devm_request_threaded_irq(dev, info->vbus_irq, NULL, usb_irq_handler, IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING | IRQF_ONESHOT, pdev->name, info); } /* Perform initial detection */ usb_extcon_detect_cable(&info->wq_detcable.work); return 0; }
在中断函数usb_irq_handler里面根据id pin和vbus状态区分设置usb DRD 模式的切换(作为host或者device)
static irqreturn_t usb_irq_handler(int irq, void *dev_id) { ....... queue_delayed_work(system_power_efficient_wq, &info->wq_detcable, info->debounce_jiffies); return IRQ_HANDLED; } static void usb_extcon_detect_cable(struct work_struct *work) { /* check ID and VBUS and update cable state */ id = info->id_gpiod ? gpiod_get_value_cansleep(info->id_gpiod) : 1; vbus = info->vbus_gpiod ? gpiod_get_value_cansleep(info->vbus_gpiod) : id; /* at first we clean states which are no longer active */ if (id) { if (info->vbus_out_gpiod) gpiod_set_value_cansleep(info->vbus_out_gpiod, 0); extcon_set_state_sync(info->edev, EXTCON_USB_HOST, false); } if (!vbus) extcon_set_state_sync(info->edev, EXTCON_USB, false); if (!id) { if (info->vbus_out_gpiod) gpiod_set_value_cansleep(info->vbus_out_gpiod, 1); extcon_set_state_sync(info->edev, EXTCON_USB_HOST, true); } else { if (vbus) extcon_set_state_sync(info->edev, EXTCON_USB, true); } }
再其他驱动(dwc3-msm.c)里面也可以注册特定extcon的notifier回调函数:
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); 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; 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; mdwc->extcon[idx].blocking_sync_nb.notifier_call = dwc3_usb_blocking_sync; extcon_register_blocking_notifier(edev, EXTCON_USB_HOST, &mdwc->extcon[idx].blocking_sync_nb); /* Update initial VBUS/ID state */ if (check_vbus_state && extcon_get_state(edev, EXTCON_USB)) dwc3_msm_vbus_notifier(&mdwc->extcon[idx].vbus_nb, true, edev); else if (check_id_state && extcon_get_state(edev, EXTCON_USB_HOST)) dwc3_msm_id_notifier(&mdwc->extcon[idx].id_nb, true, edev); } if (!phandle_found) { dev_err(mdwc->dev, "no extcon device found\n"); return -ENODEV; } return 0; }
赞
踩
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。