static int dwc3_omap_extcon_register(struct dwc3_omap *omap) { int ret; struct device_node *node = omap->dev->of_node; struct extcon_dev *edev; if (of_property_read_bool(node, "extcon")) { edev = extcon_get_edev_by_phandle(omap->dev, 0); if (IS_ERR(edev)) { dev_vdbg(omap->dev, "couldn't get extcon device\n"); return -EPROBE_DEFER; } omap->vbus_nb.notifier_call = dwc3_omap_vbus_notifier; ret = extcon_register_notifier(edev, EXTCON_USB, &omap->vbus_nb); if (ret < 0) dev_vdbg(omap->dev, "failed to register notifier for USB\n"); omap->id_nb.notifier_call = dwc3_omap_id_notifier; ret = extcon_register_notifier(edev, EXTCON_USB_HOST, &omap->id_nb); if (ret < 0) dev_vdbg(omap->dev, "failed to register notifier for USB-HOST\n"); if (extcon_get_cable_state_(edev, EXTCON_USB) == true) dwc3_omap_set_mailbox(omap, OMAP_DWC3_VBUS_VALID); if (extcon_get_cable_state_(edev, EXTCON_USB_HOST) == true) dwc3_omap_set_mailbox(omap, OMAP_DWC3_ID_GROUND); omap->edev = edev; } return 0; }
static int omap_otg_probe(struct platform_device *pdev) { const struct omap_usb_config *config = pdev->dev.platform_data; struct otg_device *otg_dev; struct extcon_dev *extcon; int ret; u32 rev; if (!config || !config->extcon) return -ENODEV; extcon = extcon_get_extcon_dev(config->extcon); if (!extcon) return -EPROBE_DEFER; otg_dev = devm_kzalloc(&pdev->dev, sizeof(*otg_dev), GFP_KERNEL); if (!otg_dev) return -ENOMEM; otg_dev->base = devm_ioremap_resource(&pdev->dev, &pdev->resource[0]); if (IS_ERR(otg_dev->base)) return PTR_ERR(otg_dev->base); otg_dev->extcon = extcon; otg_dev->id_nb.notifier_call = omap_otg_id_notifier; otg_dev->vbus_nb.notifier_call = omap_otg_vbus_notifier; ret = extcon_register_notifier(extcon, EXTCON_USB_HOST, &otg_dev->id_nb); if (ret) return ret; ret = extcon_register_notifier(extcon, EXTCON_USB, &otg_dev->vbus_nb); if (ret) { extcon_unregister_notifier(extcon, EXTCON_USB_HOST, &otg_dev->id_nb); return ret; } otg_dev->id = extcon_get_cable_state_(extcon, EXTCON_USB_HOST); otg_dev->vbus = extcon_get_cable_state_(extcon, EXTCON_USB); omap_otg_set_mode(otg_dev); rev = readl(otg_dev->base); dev_info(&pdev->dev, "OMAP USB OTG controller rev %d.%d (%s, id=%d, vbus=%d)\n", (rev >> 4) & 0xf, rev & 0xf, config->extcon, otg_dev->id, otg_dev->vbus); return 0; }
static int axp288_charger_handle_otg_evt(struct notifier_block *nb, unsigned long event, void *param) { struct axp288_chrg_info *info = container_of(nb, struct axp288_chrg_info, otg.id_nb); struct extcon_dev *edev = info->otg.cable; int usb_host = extcon_get_cable_state_(edev, EXTCON_USB_HOST); dev_dbg(&info->pdev->dev, "external connector USB-Host is %s\n", usb_host ? "attached" : "detached"); /* * Set usb_id_short flag to avoid running charger detection logic * in case usb host. */ info->otg.id_short = usb_host; schedule_work(&info->otg.work); return NOTIFY_OK; }
static int msm_otg_read_dt(struct platform_device *pdev, struct msm_otg *motg) { struct msm_otg_platform_data *pdata; struct extcon_dev *ext_id, *ext_vbus; struct device_node *node = pdev->dev.of_node; struct property *prop; int len, ret, words; u32 val, tmp[3]; pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL); if (!pdata) return -ENOMEM; motg->pdata = pdata; pdata->phy_type = (enum msm_usb_phy_type)of_device_get_match_data(&pdev->dev); if (!pdata->phy_type) return 1; motg->link_rst = devm_reset_control_get(&pdev->dev, "link"); if (IS_ERR(motg->link_rst)) return PTR_ERR(motg->link_rst); motg->phy_rst = devm_reset_control_get(&pdev->dev, "phy"); if (IS_ERR(motg->phy_rst)) motg->phy_rst = NULL; pdata->mode = usb_get_dr_mode(&pdev->dev); if (pdata->mode == USB_DR_MODE_UNKNOWN) pdata->mode = USB_DR_MODE_OTG; pdata->otg_control = OTG_PHY_CONTROL; if (!of_property_read_u32(node, "qcom,otg-control", &val)) if (val == OTG_PMIC_CONTROL) pdata->otg_control = val; if (!of_property_read_u32(node, "qcom,phy-num", &val) && val < 2) motg->phy_number = val; motg->vdd_levels[VDD_LEVEL_NONE] = USB_PHY_SUSP_DIG_VOL; motg->vdd_levels[VDD_LEVEL_MIN] = USB_PHY_VDD_DIG_VOL_MIN; motg->vdd_levels[VDD_LEVEL_MAX] = USB_PHY_VDD_DIG_VOL_MAX; if (of_get_property(node, "qcom,vdd-levels", &len) && len == sizeof(tmp)) { of_property_read_u32_array(node, "qcom,vdd-levels", tmp, len / sizeof(*tmp)); motg->vdd_levels[VDD_LEVEL_NONE] = tmp[VDD_LEVEL_NONE]; motg->vdd_levels[VDD_LEVEL_MIN] = tmp[VDD_LEVEL_MIN]; motg->vdd_levels[VDD_LEVEL_MAX] = tmp[VDD_LEVEL_MAX]; } motg->manual_pullup = of_property_read_bool(node, "qcom,manual-pullup"); motg->switch_gpio = devm_gpiod_get_optional(&pdev->dev, "switch", GPIOD_OUT_LOW); if (IS_ERR(motg->switch_gpio)) return PTR_ERR(motg->switch_gpio); ext_id = ERR_PTR(-ENODEV); ext_vbus = ERR_PTR(-ENODEV); if (of_property_read_bool(node, "extcon")) { /* Each one of them is not mandatory */ ext_vbus = extcon_get_edev_by_phandle(&pdev->dev, 0); if (IS_ERR(ext_vbus) && PTR_ERR(ext_vbus) != -ENODEV) return PTR_ERR(ext_vbus); ext_id = extcon_get_edev_by_phandle(&pdev->dev, 1); if (IS_ERR(ext_id) && PTR_ERR(ext_id) != -ENODEV) return PTR_ERR(ext_id); } if (!IS_ERR(ext_vbus)) { motg->vbus.extcon = ext_vbus; motg->vbus.nb.notifier_call = msm_otg_vbus_notifier; ret = extcon_register_notifier(ext_vbus, EXTCON_USB, &motg->vbus.nb); if (ret < 0) { dev_err(&pdev->dev, "register VBUS notifier failed\n"); return ret; } ret = extcon_get_cable_state_(ext_vbus, EXTCON_USB); if (ret) set_bit(B_SESS_VLD, &motg->inputs); else clear_bit(B_SESS_VLD, &motg->inputs); } if (!IS_ERR(ext_id)) { motg->id.extcon = ext_id; motg->id.nb.notifier_call = msm_otg_id_notifier; ret = extcon_register_notifier(ext_id, EXTCON_USB_HOST, &motg->id.nb); if (ret < 0) { dev_err(&pdev->dev, "register ID notifier failed\n"); extcon_unregister_notifier(motg->vbus.extcon, EXTCON_USB, &motg->vbus.nb); return ret; } ret = extcon_get_cable_state_(ext_id, EXTCON_USB_HOST); if (ret) clear_bit(ID, &motg->inputs); else set_bit(ID, &motg->inputs); } prop = of_find_property(node, "qcom,phy-init-sequence", &len); if (!prop || !len) return 0; words = len / sizeof(u32); if (words >= ULPI_EXT_VENDOR_SPECIFIC) { dev_warn(&pdev->dev, "Too big PHY init sequence %d\n", words); return 0; } pdata->phy_init_seq = devm_kzalloc(&pdev->dev, len, GFP_KERNEL); if (!pdata->phy_init_seq) return 0; ret = of_property_read_u32_array(node, "qcom,phy-init-sequence", pdata->phy_init_seq, words); if (!ret) pdata->phy_init_sz = words; return 0; }
/* * hotplug */ static void usbhsc_hotplug(struct usbhs_priv *priv) { struct platform_device *pdev = usbhs_priv_to_pdev(priv); struct usbhs_mod *mod = usbhs_mod_get_current(priv); int id; int enable; int cable; int ret; /* * get vbus status from platform */ enable = usbhs_platform_call(priv, get_vbus, pdev); /* * get id from platform */ id = usbhs_platform_call(priv, get_id, pdev); if (enable && !mod) { if (priv->edev) { cable = extcon_get_cable_state_(priv->edev, EXTCON_USB_HOST); if ((cable > 0 && id != USBHS_HOST) || (!cable && id != USBHS_GADGET)) { dev_info(&pdev->dev, "USB cable plugged in doesn't match the selected role!\n"); return; } } ret = usbhs_mod_change(priv, id); if (ret < 0) return; dev_dbg(&pdev->dev, "%s enable\n", __func__); /* power on */ if (usbhsc_flags_has(priv, USBHSF_RUNTIME_PWCTRL)) usbhsc_power_ctrl(priv, enable); /* bus init */ usbhsc_set_buswait(priv); usbhsc_bus_init(priv); /* module start */ usbhs_mod_call(priv, start, priv); } else if (!enable && mod) { dev_dbg(&pdev->dev, "%s disable\n", __func__); /* module stop */ usbhs_mod_call(priv, stop, priv); /* bus init */ usbhsc_bus_init(priv); /* power off */ if (usbhsc_flags_has(priv, USBHSF_RUNTIME_PWCTRL)) usbhsc_power_ctrl(priv, enable); usbhs_mod_change(priv, -1); /* reset phy for next connection */ usbhs_platform_call(priv, phy_reset, pdev); } }
static int axp288_charger_probe(struct platform_device *pdev) { int ret, i, pirq; struct axp288_chrg_info *info; struct axp20x_dev *axp20x = dev_get_drvdata(pdev->dev.parent); struct power_supply_config charger_cfg = {}; info = devm_kzalloc(&pdev->dev, sizeof(*info), GFP_KERNEL); if (!info) return -ENOMEM; info->pdev = pdev; info->regmap = axp20x->regmap; info->regmap_irqc = axp20x->regmap_irqc; info->pdata = pdev->dev.platform_data; if (!info->pdata) { /* Try ACPI provided pdata via device properties */ if (!device_property_present(&pdev->dev, "axp288_charger_data\n")) dev_err(&pdev->dev, "failed to get platform data\n"); return -ENODEV; } info->cable.edev = extcon_get_extcon_dev(AXP288_EXTCON_DEV_NAME); if (info->cable.edev == NULL) { dev_dbg(&pdev->dev, "%s is not ready, probe deferred\n", AXP288_EXTCON_DEV_NAME); return -EPROBE_DEFER; } /* Register for extcon notification */ INIT_WORK(&info->cable.work, axp288_charger_extcon_evt_worker); info->cable.nb.notifier_call = axp288_charger_handle_cable_evt; ret = extcon_register_notifier(info->cable.edev, EXTCON_CHG_USB_SDP, &info->cable.nb); if (ret) { dev_err(&info->pdev->dev, "failed to register extcon notifier for SDP %d\n", ret); return ret; } ret = extcon_register_notifier(info->cable.edev, EXTCON_CHG_USB_CDP, &info->cable.nb); if (ret) { dev_err(&info->pdev->dev, "failed to register extcon notifier for CDP %d\n", ret); extcon_unregister_notifier(info->cable.edev, EXTCON_CHG_USB_SDP, &info->cable.nb); return ret; } ret = extcon_register_notifier(info->cable.edev, EXTCON_CHG_USB_DCP, &info->cable.nb); if (ret) { dev_err(&info->pdev->dev, "failed to register extcon notifier for DCP %d\n", ret); extcon_unregister_notifier(info->cable.edev, EXTCON_CHG_USB_SDP, &info->cable.nb); extcon_unregister_notifier(info->cable.edev, EXTCON_CHG_USB_CDP, &info->cable.nb); return ret; } platform_set_drvdata(pdev, info); mutex_init(&info->lock); /* Register with power supply class */ charger_cfg.drv_data = info; info->psy_usb = power_supply_register(&pdev->dev, &axp288_charger_desc, &charger_cfg); if (IS_ERR(info->psy_usb)) { dev_err(&pdev->dev, "failed to register power supply charger\n"); ret = PTR_ERR(info->psy_usb); goto psy_reg_failed; } /* Register for OTG notification */ INIT_WORK(&info->otg.work, axp288_charger_otg_evt_worker); info->otg.id_nb.notifier_call = axp288_charger_handle_otg_evt; ret = extcon_register_notifier(info->otg.cable, EXTCON_USB_HOST, &info->otg.id_nb); if (ret) dev_warn(&pdev->dev, "failed to register otg notifier\n"); if (info->otg.cable) info->otg.id_short = extcon_get_cable_state_( info->otg.cable, EXTCON_USB_HOST); /* Register charger interrupts */ for (i = 0; i < CHRG_INTR_END; i++) { pirq = platform_get_irq(info->pdev, i); info->irq[i] = regmap_irq_get_virq(info->regmap_irqc, pirq); if (info->irq[i] < 0) { dev_warn(&info->pdev->dev, "failed to get virtual interrupt=%d\n", pirq); ret = info->irq[i]; goto intr_reg_failed; } ret = devm_request_threaded_irq(&info->pdev->dev, info->irq[i], NULL, axp288_charger_irq_thread_handler, IRQF_ONESHOT, info->pdev->name, info); if (ret) { dev_err(&pdev->dev, "failed to request interrupt=%d\n", info->irq[i]); goto intr_reg_failed; } } charger_init_hw_regs(info); return 0; intr_reg_failed: if (info->otg.cable) extcon_unregister_notifier(info->otg.cable, EXTCON_USB_HOST, &info->otg.id_nb); power_supply_unregister(info->psy_usb); psy_reg_failed: extcon_unregister_notifier(info->cable.edev, EXTCON_CHG_USB_SDP, &info->cable.nb); extcon_unregister_notifier(info->cable.edev, EXTCON_CHG_USB_CDP, &info->cable.nb); extcon_unregister_notifier(info->cable.edev, EXTCON_CHG_USB_DCP, &info->cable.nb); return ret; }
static void axp288_charger_extcon_evt_worker(struct work_struct *work) { struct axp288_chrg_info *info = container_of(work, struct axp288_chrg_info, cable.work); int ret, current_limit; bool changed = false; struct extcon_dev *edev = info->cable.edev; bool old_connected = info->cable.connected; /* Determine cable/charger type */ if (extcon_get_cable_state_(edev, EXTCON_CHG_USB_SDP) > 0) { dev_dbg(&info->pdev->dev, "USB SDP charger is connected"); info->cable.connected = true; info->cable.chg_type = POWER_SUPPLY_TYPE_USB; } else if (extcon_get_cable_state_(edev, EXTCON_CHG_USB_CDP) > 0) { dev_dbg(&info->pdev->dev, "USB CDP charger is connected"); info->cable.connected = true; info->cable.chg_type = POWER_SUPPLY_TYPE_USB_CDP; } else if (extcon_get_cable_state_(edev, EXTCON_CHG_USB_DCP) > 0) { dev_dbg(&info->pdev->dev, "USB DCP charger is connected"); info->cable.connected = true; info->cable.chg_type = POWER_SUPPLY_TYPE_USB_DCP; } else { if (old_connected) dev_dbg(&info->pdev->dev, "USB charger disconnected"); info->cable.connected = false; info->cable.chg_type = POWER_SUPPLY_TYPE_USB; } /* Cable status changed */ if (old_connected != info->cable.connected) changed = true; if (!changed) return; mutex_lock(&info->lock); if (info->is_charger_enabled && !info->cable.connected) { info->enable_charger = false; ret = axp288_charger_enable_charger(info, info->enable_charger); if (ret < 0) dev_err(&info->pdev->dev, "cannot disable charger (%d)", ret); } else if (!info->is_charger_enabled && info->cable.connected) { switch (info->cable.chg_type) { case POWER_SUPPLY_TYPE_USB: current_limit = ILIM_500MA; break; case POWER_SUPPLY_TYPE_USB_CDP: current_limit = ILIM_1500MA; break; case POWER_SUPPLY_TYPE_USB_DCP: current_limit = ILIM_2000MA; break; default: /* Unknown */ current_limit = 0; break; } /* Set vbus current limit first, then enable charger */ ret = axp288_charger_set_vbus_inlmt(info, current_limit); if (ret < 0) { dev_err(&info->pdev->dev, "error setting current limit (%d)", ret); } else { info->enable_charger = (current_limit > 0); ret = axp288_charger_enable_charger(info, info->enable_charger); if (ret < 0) dev_err(&info->pdev->dev, "cannot enable charger (%d)", ret); } } if (changed) info->health = axp288_get_charger_health(info); mutex_unlock(&info->lock); if (changed) power_supply_changed(info->psy_usb); }