int dwc3_intel_platform_init(struct dwc_otg2 *otg) { int retval; struct intel_dwc_otg_pdata *data; data = (struct intel_dwc_otg_pdata *)otg->otg_data; /* Init a_bus_drop callback */ otg->usb2_phy.a_bus_drop = dwc_a_bus_drop; otg->usb2_phy.vbus_state = VBUS_ENABLED; /* Get usb2 phy type */ otg->usb2_phy.intf = data->usb2_phy_type; /* Turn off VUSBPHY if it haven't used by USB2 PHY. * Otherwise, it will consume ~2.6mA(on VSYS) on MOFD. */ if (!data->using_vusbphy) { retval = control_usb_phy_power(PMIC_VLDOCNT, false); if (retval) otg_err(otg, "Fail to turn off VUSBPHY\n"); } else if (!is_utmi_phy(otg)) { /* If the current USB2 PHY low power controlled by VUSBPHY. Then * we need to de-assert USBRST pin to make USB2 PHY always stay * in active state. */ retval = control_usb_phy_power(PMIC_USBPHYCTRL, true); if (retval) otg_err(otg, "Fail to de-assert USBRST#\n"); } else { /* If we are using utmi phy, and through VUSBPHY to do power * control. Then we need to assert USBRST# for external ULPI phy * to ask it under inactive state saving power. */ retval = control_usb_phy_power(PMIC_USBPHYCTRL, false); if (retval) otg_err(otg, "Fail to de-assert USBRST#\n"); } /* Don't let phy go to suspend mode, which * will cause FS/LS devices enum failed in host mode. */ set_sus_phy(otg, 0); retval = device_create_file(otg->dev, &dev_attr_otg_id); if (retval < 0) { otg_dbg(otg, "Can't register sysfs attribute: %d\n", retval); return -ENOMEM; } otg_dbg(otg, "\n"); otg_write(otg, OEVTEN, 0); otg_write(otg, OCTL, 0); dwc3_switch_mode(otg, GCTL_PRT_CAP_DIR_OTG); return 0; }
int basin_cove_get_id(struct dwc_otg2 *otg) { int ret, id = RID_UNKNOWN; u8 idsts, pmic_id; ret = intel_scu_ipc_update_register(PMIC_USBIDCTRL, USBIDCTRL_ACA_DETEN_D1 | PMIC_USBPHYCTRL_D0, USBIDCTRL_ACA_DETEN_D1 | PMIC_USBPHYCTRL_D0); if (ret) otg_err(otg, "Fail to enable ACA&ID detection logic\n"); ret = intel_scu_ipc_ioread8(PMIC_USBIDSTS, &idsts); if (ret) { otg_err(otg, "Fail to read id\n"); return id; } if (idsts & USBIDSTS_ID_FLOAT_STS) id = RID_FLOAT; else if (idsts & USBIDSTS_ID_RARBRC_STS(1)) id = RID_A; else if (idsts & USBIDSTS_ID_RARBRC_STS(2)) id = RID_B; else if (idsts & USBIDSTS_ID_RARBRC_STS(3)) id = RID_C; else { /* PMIC A0 reports ID_GND = 0 for RID_GND but PMIC B0 reports * ID_GND = 1 for RID_GND */ ret = intel_scu_ipc_ioread8(0x00, &pmic_id); if (ret) { otg_err(otg, "Fail to read PMIC ID register\n"); } else if (((pmic_id & VENDOR_ID_MASK) == BASIN_COVE_PMIC_ID) && ((pmic_id & PMIC_MAJOR_REV) == PMIC_A0_MAJOR_REV)) { if (idsts & USBIDSTS_ID_GND) id = RID_GND; } else { if (!(idsts & USBIDSTS_ID_GND)) id = RID_GND; } } ret = intel_scu_ipc_update_register(PMIC_USBIDCTRL, USBIDCTRL_ACA_DETEN_D1 | PMIC_USBPHYCTRL_D0, 0); if (ret) otg_err(otg, "Fail to enable ACA&ID detection logic\n"); return id; }
static int dwc3_intel_byt_notify_charger_type(struct dwc_otg2 *otg, enum power_supply_charger_event event) { struct power_supply_cable_props cap; unsigned long flags; /* Just return if charger detection is not enabled */ if (!charger_detect_enable(otg) && !sdp_charging(otg)) return 0; /* If OTG driver doesn't do charger detection, then no need * to do notification on charger removal events */ if (!charger_detect_enable(otg) && (event == POWER_SUPPLY_CHARGER_EVENT_DISCONNECT)) { otg_err(otg, "%s: disconnect ignore!\n", __func__); return -EINVAL; } if (event > POWER_SUPPLY_CHARGER_EVENT_DISCONNECT) { otg_err(otg, "%s: Invalid power_supply_charger_event!\n", __func__); return -EINVAL; } if ((otg->charging_cap.chrg_type == POWER_SUPPLY_CHARGER_TYPE_USB_SDP) && ((otg->charging_cap.ma != 100) && (otg->charging_cap.ma != 150) && (otg->charging_cap.ma != 500) && (otg->charging_cap.ma != 900))) { otg_err(otg, "%s: invalid SDP current!\n", __func__); return -EINVAL; } spin_lock_irqsave(&otg->lock, flags); cap.chrg_type = otg->charging_cap.chrg_type; cap.ma = otg->charging_cap.ma; cap.chrg_evt = event; spin_unlock_irqrestore(&otg->lock, flags); if (sdp_charging(otg)) atomic_notifier_call_chain(&otg->usb2_phy.notifier, USB_EVENT_ENUMERATED, &cap.ma); else atomic_notifier_call_chain(&otg->usb2_phy.notifier, USB_EVENT_CHARGER, &cap); return 0; }
static ssize_t store_vbus_evt(struct device *_dev, struct device_attribute *attr, const char *buf, size_t count) { unsigned long flags; struct dwc_otg2 *otg = dwc3_get_otg(); if (count != 2) { otg_err(otg, "return EINVAL\n"); return -EINVAL; } if (count > 0 && buf[count-1] == '\n') ((char *) buf)[count-1] = 0; switch (buf[0]) { case '1': otg_dbg(otg, "Change the VBUS to High\n"); otg->otg_events |= OEVT_B_DEV_SES_VLD_DET_EVNT; spin_lock_irqsave(&otg->lock, flags); dwc3_wakeup_otg_thread(otg); spin_unlock_irqrestore(&otg->lock, flags); return count; case '0': otg_dbg(otg, "Change the VBUS to Low\n"); otg->otg_events |= OEVT_A_DEV_SESS_END_DET_EVNT; spin_lock_irqsave(&otg->lock, flags); dwc3_wakeup_otg_thread(otg); spin_unlock_irqrestore(&otg->lock, flags); return count; default: return -EINVAL; } return count; }
int dwc3_intel_byt_resume(struct dwc_otg2 *otg) { struct pci_dev *pci_dev; if (!otg) return 0; pci_dev = to_pci_dev(otg->dev); /* From synopsys spec 12.2.11. * Software cannot access memory-mapped I/O space * for 10ms. */ mdelay(10); pci_restore_state(pci_dev); if (pci_enable_device(pci_dev) < 0) { otg_err(otg, "pci_enable_device failed.\n"); return -EIO; } set_sus_phy(otg, 0); return 0; }
static enum dwc_otg_state do_wait_vbus_fall(struct dwc_otg2 *otg) { int ret; u32 otg_events = 0; u32 user_events = 0; u32 otg_mask = 0; u32 user_mask = 0; otg_mask = OEVT_A_DEV_SESS_END_DET_EVNT; ret = sleep_until_event(otg, otg_mask, user_mask, &otg_events, &user_events, VBUS_TIMEOUT); if (ret < 0) return DWC_STATE_EXIT; if (otg_events & OEVT_A_DEV_SESS_END_DET_EVNT) { otg_dbg(otg, "OEVT_A_DEV_SESS_END_DET_EVNT\n"); if (otg->charging_cap.chrg_type == POWER_SUPPLY_CHARGER_TYPE_ACA_DOCK) dwc_otg_notify_charger_type(otg, POWER_SUPPLY_CHARGER_EVENT_DISCONNECT); return DWC_STATE_B_IDLE; } /* timeout*/ if (!ret) { otg_err(otg, "Haven't get VBus drop event! Maybe something wrong\n"); return DWC_STATE_B_IDLE; } return DWC_STATE_INVALID; }
int shady_cove_get_id(struct dwc_otg2 *otg) { u8 schgrirq1; struct iio_channel *chan; int ret, rid, id = RID_UNKNOWN; ret = intel_scu_ipc_ioread8(PMIC_SCHGRIRQ1, &schgrirq1); if (ret) { otg_err(otg, "Fail to read id\n"); return id; } /* PMIC_SCHGRIRQ1_SUSBIDDET bit definition: * 0 = RID_A/B/C ; 1 = RID_GND ; 2 = RID_FLOAT */ if (schgrirq1 & PMIC_SCHGRIRQ1_SUSBIDDET(2)) return RID_FLOAT; else if (schgrirq1 & PMIC_SCHGRIRQ1_SUSBIDDET(1)) return RID_GND; chan = iio_channel_get(NULL, "USBID"); if (IS_ERR_OR_NULL(chan)) { otg_err(otg, "%s: Fail to get USBID channel\n", __func__); return id; } ret = iio_read_channel_raw(chan, &rid); if (ret) { otg_err(otg, "%s: Fail to read USBID channel", __func__); goto done; } if ((rid > 11150) && (rid < 13640)) id = RID_A; else if ((rid > 6120) && (rid < 7480)) id = RID_B; else if ((rid > 3285) && (rid < 4015)) id = RID_C; done: iio_channel_release(chan); return id; }
static enum power_supply_charger_cable_type basin_cove_aca_check(struct dwc_otg2 *otg) { u8 rarbrc; int ret; enum power_supply_charger_cable_type type = POWER_SUPPLY_CHARGER_TYPE_NONE; ret = intel_scu_ipc_update_register(PMIC_USBIDCTRL, USBIDCTRL_ACA_DETEN_D1, USBIDCTRL_ACA_DETEN_D1); if (ret) otg_err(otg, "Fail to enable ACA&ID detection logic\n"); /* Wait >66.1ms (for TCHGD_SERX_DEB) */ msleep(66); /* Read decoded RID value */ ret = intel_scu_ipc_ioread8(PMIC_USBIDSTS, &rarbrc); if (ret) otg_err(otg, "Fail to read decoded RID value\n"); rarbrc &= USBIDSTS_ID_RARBRC_STS(3); rarbrc >>= 1; /* If ID_RARBRC_STS==01: ACA-Dock detected * If ID_RARBRC_STS==00: MHL detected */ if (rarbrc == 1) { /* ACA-Dock */ type = POWER_SUPPLY_CHARGER_TYPE_ACA_DOCK; } else if (!rarbrc) { /* MHL */ type = POWER_SUPPLY_CHARGER_TYPE_MHL; } ret = intel_scu_ipc_update_register(PMIC_USBIDCTRL, USBIDCTRL_ACA_DETEN_D1, 0); if (ret) otg_err(otg, "Fail to enable ACA&ID detection logic\n"); return type; }
static ssize_t store_otg_id(struct device *_dev, struct device_attribute *attr, const char *buf, size_t count) { unsigned long flags; struct dwc_otg2 *otg = dwc3_get_otg(); if (!otg) return 0; if (count != 2) { otg_err(otg, "return EINVAL\n"); return -EINVAL; } if (count > 0 && buf[count-1] == '\n') ((char *) buf)[count-1] = 0; switch (buf[0]) { case 'a': case 'A': otg_dbg(otg, "Change ID to A\n"); otg->user_events |= USER_ID_A_CHANGE_EVENT; spin_lock_irqsave(&otg->lock, flags); dwc3_wakeup_otg_thread(otg); otg_id = 0; spin_unlock_irqrestore(&otg->lock, flags); return count; case 'b': case 'B': otg_dbg(otg, "Change ID to B\n"); otg->user_events |= USER_ID_B_CHANGE_EVENT; spin_lock_irqsave(&otg->lock, flags); dwc3_wakeup_otg_thread(otg); otg_id = 1; spin_unlock_irqrestore(&otg->lock, flags); return count; default: otg_err(otg, "Just support change ID to A!\n"); return -EINVAL; } return count; }
static int dwc3_intel_notify_charger_type(struct dwc_otg2 *otg, enum power_supply_charger_event event) { struct power_supply_cable_props cap; int ret = 0; unsigned long flags; if (!charger_detect_enable(otg) && ((otg->charging_cap.chrg_type != POWER_SUPPLY_CHARGER_TYPE_USB_SDP) || event == POWER_SUPPLY_CHARGER_EVENT_DISCONNECT)) return 0; if (event > POWER_SUPPLY_CHARGER_EVENT_DISCONNECT) { otg_err(otg, "%s: Invalid power_supply_charger_event!\n", __func__); return -EINVAL; } if ((otg->charging_cap.chrg_type == POWER_SUPPLY_CHARGER_TYPE_USB_SDP) && ((otg->charging_cap.ma != 0) && (otg->charging_cap.ma != 100) && (otg->charging_cap.ma != 150) && (otg->charging_cap.ma != 500) && (otg->charging_cap.ma != 900))) { otg_err(otg, "%s: invalid SDP current!\n", __func__); return -EINVAL; } spin_lock_irqsave(&otg->lock, flags); cap.chrg_type = otg->charging_cap.chrg_type; cap.ma = otg->charging_cap.ma; cap.chrg_evt = event; spin_unlock_irqrestore(&otg->lock, flags); atomic_notifier_call_chain(&otg->usb2_phy.notifier, USB_EVENT_CHARGER, &cap); return ret; }
/** * static int __init s3c6410_otg_module_init(void) * * @brief module_init function * * @return it returns result of platform_driver_register * @remark * This function is called when the s3c6410_otg_driver is installed with the * insmod command. It registers the s3c6410_otg_driver structure with the * appropriate bus driver. This will cause the s3c6410_otg_driver_probe function * to be called. In addition, the bus driver will automatically expose * attributes defined for the device and driver in the special sysfs file * system. */ static int __init s3c6410_otg_module_init(void) { int ret_val = 0; otg_dbg(OTG_DBG_OTGHCDI_DRIVER, "s3c_otg_module_init \n"); ret_val = platform_driver_register(&s3c6410_otg_driver); if (ret_val < 0) { otg_err(OTG_DBG_OTGHCDI_DRIVER, "platform_driver_register \n"); } return ret_val; }
static void start_peripheral(struct dwc_otg2 *otg) { struct usb_gadget *gadget; int ret; if (dwc3_otg_pdata->prepare_start_peripheral) ret = dwc3_otg_pdata->prepare_start_peripheral(otg); gadget = otg->otg.gadget; if (!gadget) { otg_err(otg, "Haven't set gadget yet!\n"); return; } otg->start_device(gadget); }
static void otg_power_work(struct work_struct *work) { struct sec_otghost *otghost = container_of(work, struct sec_otghost, work); struct sec_otghost_data *hdata = otghost->otg_data; if (hdata && hdata->set_pwr_cb) { pr_info("otg power off - don't turn off the power\n"); hdata->set_pwr_cb(0); #ifdef CONFIG_USB_HOST_NOTIFY if (g_pUsbHcd) host_state_notify(&g_pUsbHcd->ndev, NOTIFY_HOST_OVERCURRENT); #endif } else { otg_err(true, "invalid otghost data\n"); } }
/* This function will control VUSBPHY to power gate/ungate USBPHY. * If current platform haven't using VUSBPHY, then assert/deassert * USBRST_N pin to make PHY enter reset state. */ static int enable_usb_phy(struct dwc_otg2 *otg, bool on_off) { struct intel_dwc_otg_pdata *data; int ret; if (!otg || !otg->otg_data) return -EINVAL; data = (struct intel_dwc_otg_pdata *)otg->otg_data; if (data->using_vusbphy) ret = control_usb_phy_power(PMIC_VLDOCNT, on_off); else ret = control_usb_phy_power(PMIC_USBPHYCTRL, on_off); if (ret) otg_err(otg, "dwc3 %s usb phy failed\n", on_off ? "enable" : "disable"); return ret; }
static int start_host(struct dwc_otg2 *otg) { int ret = 0; struct usb_hcd *hcd = NULL; otg_dbg(otg, "\n"); if (!otg->otg.host) { otg_err(otg, "Haven't set host yet!\n"); return -ENODEV; } if (dwc3_otg_pdata->prepare_start_host) ret = dwc3_otg_pdata->prepare_start_host(otg); /* Start host driver */ hcd = container_of(otg->otg.host, struct usb_hcd, self); ret = otg->start_host(hcd); return ret; }
int dwc3_intel_byt_suspend(struct dwc_otg2 *otg) { struct pci_dev *pci_dev; pci_power_t state = PCI_D3hot; if (!otg) return 0; pci_dev = to_pci_dev(otg->dev); set_sus_phy(otg, 1); if (pci_save_state(pci_dev)) { otg_err(otg, "pci_save_state failed!\n"); return -EIO; } pci_disable_device(pci_dev); pci_set_power_state(pci_dev, state); return 0; }
static int dwc_otg2_set_peripheral(struct usb_otg *x, struct usb_gadget *gadget) { struct dwc_otg2 *otg; if (!x) { otg_err(otg, "otg is NULL!\n"); return -ENODEV; } otg = xceiv_to_dwc_otg2(x); otg_dbg(otg, "\n"); if (!gadget) { otg->otg.gadget = NULL; stop_main_thread(otg); return -ENODEV; } otg->otg.gadget = gadget; otg->usb2_phy.state = OTG_STATE_B_IDLE; start_main_thread(otg); return 0; }
static int ulpi_read(struct usb_phy *phy, u32 reg) { struct dwc_otg2 *otg = container_of(phy, struct dwc_otg2, usb2_phy); u32 val32 = 0, count = 10000; u8 val, tmp; if (phy->intf != USB2_PHY_ULPI) return -ENODEV; reg &= 0xFF; while (count) { if (otg_read(otg, GUSB2PHYACC0) & GUSB2PHYACC0_VSTSBSY) udelay(1); else break; count--; } if (!count) { otg_err(otg, "USB2 PHY always busy!!\n"); return -EBUSY; } count = 10000; /* Determine if use extend registers access */ if (reg & EXTEND_ULPI_REGISTER_ACCESS_MASK) { otg_dbg(otg, "Access extend registers 0x%x\n", reg); val32 = GUSB2PHYACC0_NEWREGREQ | GUSB2PHYACC0_REGADDR(ULPI_ACCESS_EXTENDED) | GUSB2PHYACC0_VCTRL(reg); } else { otg_dbg(otg, "Access normal registers 0x%x\n", reg); val32 = GUSB2PHYACC0_NEWREGREQ | GUSB2PHYACC0_REGADDR(reg) | GUSB2PHYACC0_VCTRL(0x00); } otg_write(otg, GUSB2PHYACC0, val32); while (count) { if (otg_read(otg, GUSB2PHYACC0) & GUSB2PHYACC0_VSTSDONE) { val = otg_read(otg, GUSB2PHYACC0) & GUSB2PHYACC0_REGDATA_MASK; otg_dbg(otg, "%s - reg 0x%x data 0x%x\n", __func__, reg, val); goto cleanup; } udelay(1); count--; } otg_err(otg, "%s read PHY data failed.\n", __func__); return -ETIMEDOUT; cleanup: /* Clear GUSB2PHYACC0[16:21] before return. * Otherwise, it will cause PHY can't in workable * state. This is one dwc3 controller silicon bug. */ tmp = otg_read(otg, GUSB2PHYACC0); otg_write(otg, GUSB2PHYACC0, tmp & ~GUSB2PHYACC0_REGADDR(0x3F)); return val; }
static enum dwc_otg_state do_a_host(struct dwc_otg2 *otg) { int rc = 0; u32 otg_events, user_events, otg_mask, user_mask; int id = RID_UNKNOWN; unsigned long flags; /* If Battery low and connected charger is not ACA-DOCK. * Then stop trying to start host mode. */ if ((otg->usb2_phy.vbus_state == VBUS_DISABLED) && (otg->charging_cap.chrg_type != POWER_SUPPLY_CHARGER_TYPE_ACA_DOCK)) { otg_uevent_trigger(&otg->usb2_phy); return DWC_STATE_B_IDLE; } if (otg->charging_cap.chrg_type != POWER_SUPPLY_CHARGER_TYPE_ACA_DOCK) { dwc_otg_enable_vbus(otg, 1); /* meant receive vbus valid event*/ if (do_wait_vbus_raise(otg) == DWC_STATE_A_HOST) otg_err(otg, "Drive VBUS maybe fail!\n"); } rc = start_host(otg); if (rc < 0) { stop_host(otg); otg_err(otg, "start_host failed!"); return DWC_STATE_INVALID; } stay_host: otg_events = 0; user_events = 0; user_mask = USER_A_BUS_DROP | USER_ID_B_CHANGE_EVENT; otg_mask = OEVT_CONN_ID_STS_CHNG_EVNT | OEVT_A_DEV_SESS_END_DET_EVNT; rc = sleep_until_event(otg, otg_mask, user_mask, &otg_events, &user_events, 0); if (rc < 0) { stop_host(otg); return DWC_STATE_EXIT; } /* Higher priority first */ if (otg_events & OEVT_A_DEV_SESS_END_DET_EVNT) { otg_dbg(otg, "OEVT_A_DEV_SESS_END_DET_EVNT\n"); /* ACA-Dock plug out */ if (otg->charging_cap.chrg_type == POWER_SUPPLY_CHARGER_TYPE_ACA_DOCK) dwc_otg_notify_charger_type(otg, POWER_SUPPLY_CHARGER_EVENT_DISCONNECT); else dwc_otg_enable_vbus(otg, 0); stop_host(otg); return DWC_STATE_B_IDLE; } if (user_events & USER_A_BUS_DROP) { /* Due to big consume by DUT, even ACA-Dock connected, * the battery capability still maybe decrease. For this * case, still save host mode. Because DUT haven't drive VBus.*/ if (otg->charging_cap.chrg_type == POWER_SUPPLY_CHARGER_TYPE_ACA_DOCK) goto stay_host; dwc_otg_enable_vbus(otg, 0); stop_host(otg); return DWC_STATE_B_IDLE; } if (otg_events & OEVT_CONN_ID_STS_CHNG_EVNT) { otg_dbg(otg, "OEVT_CONN_ID_STS_CHNG_EVNT\n"); id = get_id(otg); /* Plug out ACA_DOCK/USB device */ if (id == RID_FLOAT) { if (otg->charging_cap.chrg_type == POWER_SUPPLY_CHARGER_TYPE_ACA_DOCK) { /* ACA_DOCK plug out, receive * id change prior to vBus change */ dwc_otg_notify_charger_type(otg, POWER_SUPPLY_CHARGER_EVENT_DISCONNECT); stop_host(otg); } else { /* Normal USB device plug out */ spin_lock_irqsave(&otg->lock, flags); otg->charging_cap.chrg_type = POWER_SUPPLY_CHARGER_TYPE_NONE; spin_unlock_irqrestore(&otg->lock, flags); stop_host(otg); dwc_otg_enable_vbus(otg, 0); } } else if (id == RID_GND || id == RID_A) { otg_dbg(otg, "Stay DWC_STATE_A_HOST!!\n"); /* Prevent user fast plug in after plug out. * It will cause the first ID change event lost. * So need to check real ID currently. */ goto stay_host; } else { otg_err(otg, "Meet invalid charger cases!"); spin_lock_irqsave(&otg->lock, flags); otg->charging_cap.chrg_type = POWER_SUPPLY_CHARGER_TYPE_NONE; spin_unlock_irqrestore(&otg->lock, flags); stop_host(otg); } return DWC_STATE_WAIT_VBUS_FALL; } /* Higher priority first */ if (user_events & USER_ID_B_CHANGE_EVENT) { otg_dbg(otg, "USER_ID_B_CHANGE_EVENT\n"); stop_host(otg); otg->user_events |= USER_ID_B_CHANGE_EVENT; return DWC_STATE_B_IDLE; } /* Invalid state */ return DWC_STATE_INVALID; }
static enum dwc_otg_state do_charger_detection(struct dwc_otg2 *otg) { enum dwc_otg_state state = DWC_STATE_INVALID; enum power_supply_charger_cable_type charger = POWER_SUPPLY_CHARGER_TYPE_NONE; unsigned long flags, ma = 0; charger = get_charger_type(otg); switch (charger) { case POWER_SUPPLY_CHARGER_TYPE_ACA_A: case POWER_SUPPLY_CHARGER_TYPE_ACA_B: case POWER_SUPPLY_CHARGER_TYPE_ACA_C: otg_err(otg, "Ignore micro ACA charger.\n"); charger = POWER_SUPPLY_CHARGER_TYPE_NONE; break; case POWER_SUPPLY_CHARGER_TYPE_USB_SDP: case POWER_SUPPLY_CHARGER_TYPE_USB_CDP: state = DWC_STATE_B_PERIPHERAL; break; case POWER_SUPPLY_CHARGER_TYPE_ACA_DOCK: state = DWC_STATE_A_HOST; break; case POWER_SUPPLY_CHARGER_TYPE_USB_DCP: case POWER_SUPPLY_CHARGER_TYPE_SE1: state = DWC_STATE_CHARGING; break; case POWER_SUPPLY_CHARGER_TYPE_NONE: default: if (is_self_powered_b_device(otg)) { state = DWC_STATE_A_HOST; charger = POWER_SUPPLY_CHARGER_TYPE_B_DEVICE; break; } }; switch (charger) { case POWER_SUPPLY_CHARGER_TYPE_ACA_DOCK: case POWER_SUPPLY_CHARGER_TYPE_ACA_A: case POWER_SUPPLY_CHARGER_TYPE_ACA_B: case POWER_SUPPLY_CHARGER_TYPE_ACA_C: case POWER_SUPPLY_CHARGER_TYPE_USB_DCP: case POWER_SUPPLY_CHARGER_TYPE_USB_CDP: case POWER_SUPPLY_CHARGER_TYPE_SE1: ma = 1500; break; case POWER_SUPPLY_CHARGER_TYPE_USB_SDP: break; default: otg_err(otg, "Charger type is not valid to notify battery\n"); return -EINVAL; } spin_lock_irqsave(&otg->lock, flags); otg->charging_cap.chrg_type = charger; otg->charging_cap.ma = ma; spin_unlock_irqrestore(&otg->lock, flags); switch (charger) { case POWER_SUPPLY_CHARGER_TYPE_ACA_DOCK: case POWER_SUPPLY_CHARGER_TYPE_USB_DCP: case POWER_SUPPLY_CHARGER_TYPE_USB_CDP: case POWER_SUPPLY_CHARGER_TYPE_SE1: if (dwc_otg_notify_charger_type(otg, POWER_SUPPLY_CHARGER_EVENT_CONNECT) < 0) otg_err(otg, "Notify battery type failed!\n"); break; case POWER_SUPPLY_CHARGER_TYPE_USB_SDP: /* SDP is complicate, it will be handle in set_power */ default: break; } return state; }
static int dwc3_intel_set_power(struct usb_phy *_otg, unsigned ma) { unsigned long flags; struct dwc_otg2 *otg = dwc3_get_otg(); struct power_supply_cable_props cap; struct intel_dwc_otg_pdata *data; data = (struct intel_dwc_otg_pdata *)otg->otg_data; /* On ANN, due the VBUS haven't connect to internal USB PHY. So * controller can't get disconnect interrupt which depend on vbus drop * detection. * So controller will receive early suspend and suspend interrupt. But * we can detect vbus status to determine current scenario is real * suspend or vbus drop. */ if (data->detect_vbus_drop && ma == OTG_DEVICE_SUSPEND) { if (!check_vbus_status(otg)) { cap.chrg_type = otg->charging_cap.chrg_type; cap.ma = otg->charging_cap.ma; cap.chrg_evt = POWER_SUPPLY_CHARGER_EVENT_DISCONNECT; atomic_notifier_call_chain(&otg->usb2_phy.notifier, USB_EVENT_CHARGER, &cap); return 0; } } if (otg->charging_cap.chrg_type == POWER_SUPPLY_CHARGER_TYPE_USB_CDP) return 0; else if (otg->charging_cap.chrg_type != POWER_SUPPLY_CHARGER_TYPE_USB_SDP) { otg_err(otg, "%s: currently, chrg type is not SDP!\n", __func__); return -EINVAL; } if (ma == OTG_DEVICE_SUSPEND) { spin_lock_irqsave(&otg->lock, flags); cap.chrg_type = otg->charging_cap.chrg_type; cap.ma = otg->charging_cap.ma; cap.chrg_evt = POWER_SUPPLY_CHARGER_EVENT_SUSPEND; spin_unlock_irqrestore(&otg->lock, flags); /* mA is zero mean D+/D- opened cable. * If SMIP set, then notify 500mA. * Otherwise, notify 0mA. */ if (!cap.ma) { if (data->charging_compliance) { cap.ma = 500; cap.chrg_evt = POWER_SUPPLY_CHARGER_EVENT_CONNECT; } /* For standard SDP, if SMIP set, then ignore suspend */ } else if (data->charging_compliance) return 0; /* Stander SDP(cap.mA != 0) and SMIP not set. * Should send 0mA with SUSPEND event */ else cap.ma = 0; atomic_notifier_call_chain(&otg->usb2_phy.notifier, USB_EVENT_CHARGER, &cap); otg_dbg(otg, "Notify EM"); otg_dbg(otg, "POWER_SUPPLY_CHARGER_EVENT_SUSPEND\n"); return 0; } else if (ma == OTG_DEVICE_RESUME) { otg_dbg(otg, "Notify EM"); otg_dbg(otg, "POWER_SUPPLY_CHARGER_EVENT_CONNECT\n"); dwc3_intel_notify_charger_type(otg, POWER_SUPPLY_CHARGER_EVENT_CONNECT); return 0; } /* For SMIP set case, only need to report 500/900mA */ if (data->charging_compliance) { if ((ma != OTG_USB2_500MA) && (ma != OTG_USB3_900MA)) return 0; } /* Covert macro to integer number*/ switch (ma) { case OTG_USB2_0MA: ma = 0; break; case OTG_USB2_100MA: ma = 100; break; case OTG_USB3_150MA: ma = 150; break; case OTG_USB2_500MA: ma = 500; break; case OTG_USB3_900MA: ma = 900; break; default: otg_err(otg, "Device driver set invalid SDP current value!\n"); return -EINVAL; } spin_lock_irqsave(&otg->lock, flags); otg->charging_cap.ma = ma; spin_unlock_irqrestore(&otg->lock, flags); dwc3_intel_notify_charger_type(otg, POWER_SUPPLY_CHARGER_EVENT_CONNECT); return 0; }
static int dwc3_intel_handle_notification(struct notifier_block *nb, unsigned long event, void *data) { int state; unsigned long flags, valid_chrg_type; struct dwc_otg2 *otg = dwc3_get_otg(); struct power_supply_cable_props *cap; if (!otg) return NOTIFY_BAD; valid_chrg_type = POWER_SUPPLY_CHARGER_TYPE_USB_SDP | POWER_SUPPLY_CHARGER_TYPE_USB_CDP | POWER_SUPPLY_CHARGER_TYPE_ACA_DOCK; spin_lock_irqsave(&otg->lock, flags); switch (event) { case USB_EVENT_ID: otg->otg_events |= OEVT_CONN_ID_STS_CHNG_EVNT; state = NOTIFY_OK; break; case USB_EVENT_VBUS: /* WA for EM driver which should not sent VBUS event * if UTMI PHY selected. */ if (!charger_detect_enable(otg)) { state = NOTIFY_OK; goto done; } if (*(int *)data) { otg->otg_events |= OEVT_B_DEV_SES_VLD_DET_EVNT; otg->otg_events &= ~OEVT_A_DEV_SESS_END_DET_EVNT; } else { otg->otg_events |= OEVT_A_DEV_SESS_END_DET_EVNT; otg->otg_events &= ~OEVT_B_DEV_SES_VLD_DET_EVNT; } state = NOTIFY_OK; break; case USB_EVENT_CHARGER: if (charger_detect_enable(otg)) { state = NOTIFY_DONE; goto done; } cap = (struct power_supply_cable_props *)data; if (!(cap->chrg_type & valid_chrg_type)) { otg_err(otg, "Ignore invalid charger type!\n"); state = NOTIFY_DONE; goto done; } /* Ignore the events which send by USB driver itself. */ if (cap->chrg_evt == POWER_SUPPLY_CHARGER_EVENT_CONNECT) if (cap_record.chrg_type == POWER_SUPPLY_CHARGER_TYPE_USB_SDP) { state = NOTIFY_DONE; goto done; } if (cap->chrg_evt == POWER_SUPPLY_CHARGER_EVENT_CONNECT) { otg->otg_events |= OEVT_B_DEV_SES_VLD_DET_EVNT; otg->otg_events &= ~OEVT_A_DEV_SESS_END_DET_EVNT; cap_record.chrg_type = cap->chrg_type; cap_record.ma = cap->ma; cap_record.chrg_evt = cap->chrg_evt; } else if (cap->chrg_evt == POWER_SUPPLY_CHARGER_EVENT_DISCONNECT) { otg->otg_events |= OEVT_A_DEV_SESS_END_DET_EVNT; otg->otg_events &= ~OEVT_B_DEV_SES_VLD_DET_EVNT; cap_record.chrg_type = POWER_SUPPLY_CHARGER_TYPE_NONE; cap_record.ma = 0; cap_record.chrg_evt = POWER_SUPPLY_CHARGER_EVENT_DISCONNECT; } if (cap->chrg_type == POWER_SUPPLY_CHARGER_TYPE_ACA_DOCK) otg->otg_events |= OEVT_CONN_ID_STS_CHNG_EVNT; state = NOTIFY_OK; break; default: otg_dbg(otg, "DWC OTG Notify unknow notify message\n"); state = NOTIFY_DONE; } dwc3_wakeup_otg_thread(otg); done: spin_unlock_irqrestore(&otg->lock, flags); return state; }
static int dwc3_intel_byt_set_power(struct usb_phy *_otg, unsigned ma) { unsigned long flags; struct dwc_otg2 *otg = dwc3_get_otg(); struct power_supply_cable_props cap; struct intel_dwc_otg_pdata *data; data = (struct intel_dwc_otg_pdata *)otg->otg_data; if (!data) return -EINVAL; if (ma == OTG_USB2_100MA || ma == OTG_USB3_150MA || ma == OTG_USB2_500MA || ma == OTG_USB3_900MA || ma == OTG_DEVICE_RESUME) { otg_dbg(otg, "cancel discon work\n"); __cancel_delayed_work(&data->suspend_discon_work); } else if (ma == OTG_DEVICE_SUSPEND) { otg_dbg(otg, "schedule discon work\n"); schedule_delayed_work(&data->suspend_discon_work, SUSPEND_DISCONNECT_TIMEOUT); } /* Needn't notify charger capability if charger_detection disable */ if (!charger_detect_enable(otg) && !sdp_charging(otg)) return 0; if (ma == OTG_DEVICE_SUSPEND) { spin_lock_irqsave(&otg->lock, flags); cap.chrg_type = otg->charging_cap.chrg_type; cap.ma = otg->charging_cap.ma; cap.chrg_evt = POWER_SUPPLY_CHARGER_EVENT_SUSPEND; spin_unlock_irqrestore(&otg->lock, flags); /* ma is zero mean D+/D- opened cable. * If SMIP set, then notify 500ma. * Otherwise, notify 0ma. */ if (!cap.ma) { if (data->charging_compliance) { cap.ma = 500; cap.chrg_evt = POWER_SUPPLY_CHARGER_EVENT_CONNECT; } /* For standard SDP, if SMIP set, then ignore suspend */ } else if (data->charging_compliance) return 0; /* Stander SDP(cap.ma != 0) and SMIP not set. * Should send 0ma with SUSPEND event */ else cap.ma = 2; if (sdp_charging(otg)) atomic_notifier_call_chain(&otg->usb2_phy.notifier, USB_EVENT_ENUMERATED, &cap.ma); else atomic_notifier_call_chain(&otg->usb2_phy.notifier, USB_EVENT_CHARGER, &cap); otg_dbg(otg, "Notify EM CHARGER_EVENT_SUSPEND\n"); return 0; } else if (ma == OTG_DEVICE_RESUME) { otg_dbg(otg, "Notify EM CHARGER_EVENT_CONNECT\n"); dwc3_intel_byt_notify_charger_type(otg, POWER_SUPPLY_CHARGER_EVENT_CONNECT); return 0; } /* For SMIP set case, only need to report 500/900ma */ if (data->charging_compliance) { if ((ma != OTG_USB2_500MA) && (ma != OTG_USB3_900MA)) return 0; } /* Covert macro to integer number*/ switch (ma) { case OTG_USB2_100MA: ma = 100; break; case OTG_USB3_150MA: ma = 150; break; case OTG_USB2_500MA: ma = 500; break; case OTG_USB3_900MA: ma = 900; break; default: otg_err(otg, "Device driver set invalid SDP current value!\n"); return -EINVAL; } spin_lock_irqsave(&otg->lock, flags); otg->charging_cap.ma = ma; spin_unlock_irqrestore(&otg->lock, flags); dwc3_intel_byt_notify_charger_type(otg, POWER_SUPPLY_CHARGER_EVENT_CONNECT); return 0; }
static int dwc3_otg_create_children(struct dwc_otg2 *otg, struct resource *res, int num) { struct platform_device *dwc_host, *dwc_gadget; enum dwc3_otg_mode mode = dwc3_otg_pdata->mode; int retval = 0, i; if (!otg || !res) return -EINVAL; if (num != 2) return -EINVAL; dwc_host = dwc_gadget = NULL; for (i = 0; i < 2; i++) { if (res[i].flags == IORESOURCE_MEM) { otg->usb2_phy.io_priv = ioremap_nocache( res[i].start, res[i].end - res[i].start); if (!otg->usb2_phy.io_priv) { otg_err(otg, "dwc3 otg ioremap failed\n"); return -ENOMEM; } break; } } /* resource have no mem io resource */ if (!otg->usb2_phy.io_priv) return -EINVAL; platform_par = kzalloc(sizeof(*platform_par), GFP_KERNEL); if (!platform_par) { otg_err(otg, "alloc dwc_device_par failed\n"); goto err1; } platform_par->io_addr = otg->usb2_phy.io_priv; platform_par->len = res[i].end - res[i].start; if (mode == DWC3_DEVICE_ONLY) goto device_only; dwc_host = platform_device_alloc(DWC3_HOST_NAME, HOST_DEVID); if (!dwc_host) { otg_err(otg, "couldn't allocate dwc3 host device\n"); goto err2; } retval = platform_device_add_resources(dwc_host, res, num); if (retval) { otg_err(otg, "couldn't add resources to dwc3 device\n"); goto err3; } platform_device_add_data(dwc_host, platform_par, sizeof(struct dwc_device_par)); dwc_host->dev.dma_mask = otg->dev->dma_mask; dwc_host->dev.dma_parms = otg->dev->dma_parms; dwc_host->dev.parent = otg->dev; retval = platform_device_add(dwc_host); if (retval) { otg_err(otg, "failed to register dwc3 host\n"); goto err1; } otg->host = dwc_host; if (mode != DWC3_DRD) return 0; device_only: dwc_gadget = platform_device_alloc(DWC3_DEVICE_NAME, GADGET_DEVID); if (!dwc_gadget) { otg_err(otg, "couldn't allocate dwc3 device\n"); goto err3; } retval = platform_device_add_resources(dwc_gadget, res, num); if (retval) { otg_err(otg, "couldn't add resources to dwc3 device\n"); goto err3; } dwc_gadget->dev.dma_mask = otg->dev->dma_mask; dwc_gadget->dev.dma_parms = otg->dev->dma_parms; dwc_gadget->dev.parent = otg->dev; platform_device_add_data(dwc_gadget, platform_par, sizeof(struct dwc_device_par)); retval = platform_device_add(dwc_gadget); if (retval) { otg_err(otg, "failed to register dwc3 gadget\n"); goto err3; } otg->gadget = dwc_gadget; return 0; err3: if (mode == DWC3_DRD) platform_device_unregister(dwc_host); err2: kfree(platform_par); err1: iounmap(otg->usb2_phy.io_priv); return retval; }
static struct dwc_otg2 *dwc3_otg_alloc(struct device *dev) { struct dwc_otg2 *otg = NULL; struct usb_phy *usb_phy; int retval; otg = kzalloc(sizeof(*otg), GFP_KERNEL); if (!otg) { dev_err(dev, "Alloc otg failed\n"); return NULL; } the_transceiver = otg; otg->otg_data = dev->platform_data; usb_phy = &otg->usb2_phy; otg->otg.phy = usb_phy; otg->usb2_phy.otg = &otg->otg; otg->dev = dev; otg->usb3_phy.dev = otg->dev; otg->usb3_phy.label = "dwc-usb3-phy"; otg->usb3_phy.state = OTG_STATE_UNDEFINED; otg->usb3_phy.otg = &otg->otg; otg->usb2_phy.dev = otg->dev; otg->usb2_phy.label = "dwc-usb2-phy"; otg->usb2_phy.state = OTG_STATE_UNDEFINED; otg->usb2_phy.set_power = dwc3_otg_pdata->set_power; otg->usb2_phy.get_chrg_status = dwc_otg_get_chrg_status; otg->usb2_phy.io_ops = &dwc_otg_io_ops; otg->usb2_phy.otg = &otg->otg; otg->otg.set_host = dwc_otg2_set_host; otg->otg.set_peripheral = dwc_otg2_set_peripheral; ATOMIC_INIT_NOTIFIER_HEAD(&otg->usb2_phy.notifier); ATOMIC_INIT_NOTIFIER_HEAD(&otg->usb3_phy.notifier); otg->state = DWC_STATE_B_IDLE; spin_lock_init(&otg->lock); init_waitqueue_head(&otg->main_wq); /* Register otg notifier to monitor ID and VBus change events */ otg->nb.notifier_call = dwc_otg_handle_notification; usb_register_notifier(&otg->usb2_phy, &otg->nb); otg_dbg(otg, "Version: %s\n", VERSION); retval = usb_add_phy(&otg->usb2_phy, USB_PHY_TYPE_USB2); if (retval) { otg_err(otg, "can't register transceiver, err: %d\n", retval); goto err1; } retval = usb_add_phy(&otg->usb3_phy, USB_PHY_TYPE_USB3); if (retval) { otg_err(otg, "can't register transceiver, err: %d\n", retval); goto err2; } return otg; err2: usb_remove_phy(&otg->usb2_phy); err1: kfree(otg); otg = NULL; return otg; }
static enum power_supply_charger_cable_type dwc3_intel_byt_get_charger_type(struct dwc_otg2 *otg) { struct usb_phy *phy; u8 val, vdat_det, chgd_serx_dm; unsigned long timeout, interval; enum power_supply_charger_cable_type type = POWER_SUPPLY_CHARGER_TYPE_NONE; /* No need to do charger detection if not enabled */ if (!charger_detect_enable(otg)) return POWER_SUPPLY_CHARGER_TYPE_USB_SDP; phy = usb_get_phy(USB_PHY_TYPE_USB2); if (!phy) { otg_err(otg, "Get USB2 PHY failed\n"); return POWER_SUPPLY_CHARGER_TYPE_NONE; } /* PHY Enable: * Power on PHY */ enable_usb_phy(otg, true); /* Wait 10ms (~5ms before PHY de-asserts DIR, * XXus for initial Link reg sync-up).*/ msleep(20); /* DCD Enable: Change OPMODE to 01 (Non-driving), * TermSel to 0, & * XcvrSel to 01 (enable FS xcvr) */ usb_phy_io_write(phy, FUNCCTRL_OPMODE(1) | FUNCCTRL_XCVRSELECT(1), TUSB1211_FUNC_CTRL_SET); usb_phy_io_write(phy, FUNCCTRL_OPMODE(2) | FUNCCTRL_XCVRSELECT(2) | FUNCCTRL_TERMSELECT, TUSB1211_FUNC_CTRL_CLR); /*Enable SW control*/ usb_phy_io_write(phy, PWCTRL_SW_CONTROL, TUSB1211_POWER_CONTROL_SET); /* Enable IDPSRC */ usb_phy_io_write(phy, VS3_CHGD_IDP_SRC_EN, TUSB1211_VENDOR_SPECIFIC3_SET); /* Check DCD result, use same polling parameter */ timeout = jiffies + msecs_to_jiffies(DATACON_TIMEOUT); interval = DATACON_INTERVAL * 1000; /* us */ /* DCD Check: * Delay 66.5 ms. (Note: * TIDP_SRC_ON + TCHGD_SERX_DEB = * 347.8us + 66.1ms). */ usleep_range(66500, 67000); while (!time_after(jiffies, timeout)) { /* Read DP logic level. */ val = usb_phy_io_read(phy, TUSB1211_VENDOR_SPECIFIC4); if (val < 0) { otg_err(otg, "ULPI read error! try again\n"); continue; } if (!(val & VS4_CHGD_SERX_DP)) { otg_info(otg, "Data contact detected!\n"); break; } /* Polling interval */ usleep_range(interval, interval + 2000); } /* Disable DP pullup (Idp_src) */ usb_phy_io_write(phy, VS3_CHGD_IDP_SRC_EN, TUSB1211_VENDOR_SPECIFIC3_CLR); /* SE1 Det Enable: * Read DP/DM logic level. Note: use DEBUG * because VS4 isn’t enabled in this situation. */ val = usb_phy_io_read(phy, TUSB1211_DEBUG); if (val < 0) otg_err(otg, "ULPI read error!\n"); val &= DEBUG_LINESTATE; /* If '11': SE1 detected; goto 'Cleanup'. * Else: goto 'Pri Det Enable'. */ if (val == 3) { type = POWER_SUPPLY_CHARGER_TYPE_SE1; goto cleanup; } /* Pri Det Enable: * Enable VDPSRC. */ usb_phy_io_write(phy, PWCTRL_DP_VSRC_EN, TUSB1211_POWER_CONTROL_SET); /* Wait >106.1ms (40ms for BC * Tvdpsrc_on, 66.1ms for TI CHGD_SERX_DEB). */ msleep(107); /* Pri Det Check: * Check if DM > VDATREF. */ vdat_det = usb_phy_io_read(phy, TUSB1211_POWER_CONTROL); if (vdat_det < 0) otg_err(otg, "ULPI read error!\n"); vdat_det &= PWCTRL_VDAT_DET; /* Check if DM<VLGC */ chgd_serx_dm = usb_phy_io_read(phy, TUSB1211_VENDOR_SPECIFIC4); if (chgd_serx_dm < 0) otg_err(otg, "ULPI read error!\n"); chgd_serx_dm &= VS4_CHGD_SERX_DM; /* If VDAT_DET==0 || CHGD_SERX_DM==1: SDP detected * If VDAT_DET==1 && CHGD_SERX_DM==0: CDP/DCP */ if (vdat_det == 0 || chgd_serx_dm == 1) type = POWER_SUPPLY_CHARGER_TYPE_USB_SDP; /* Disable VDPSRC. */ usb_phy_io_write(phy, PWCTRL_DP_VSRC_EN, TUSB1211_POWER_CONTROL_CLR); /* If SDP, goto “Cleanup”. * Else, goto “Sec Det Enable” */ if (type == POWER_SUPPLY_CHARGER_TYPE_USB_SDP) goto cleanup; /* Sec Det Enable: * delay 1ms. */ usleep_range(1000, 1500); /* Swap DP & DM */ usb_phy_io_write(phy, VS1_DATAPOLARITY, TUSB1211_VENDOR_SPECIFIC1_CLR); /* Enable 'VDMSRC'. */ usb_phy_io_write(phy, PWCTRL_DP_VSRC_EN, TUSB1211_POWER_CONTROL_SET); /* Wait >73ms (40ms for BC Tvdmsrc_on, 33ms for TI TVDPSRC_DEB) */ msleep(80); /* Sec Det Check: * Check if DP>VDATREF. */ val = usb_phy_io_read(phy, TUSB1211_POWER_CONTROL); if (val < 0) otg_err(otg, "ULPI read error!\n"); val &= PWCTRL_VDAT_DET; /* If VDAT_DET==0: CDP detected. * If VDAT_DET==1: DCP detected. */ if (!val) type = POWER_SUPPLY_CHARGER_TYPE_USB_CDP; else type = POWER_SUPPLY_CHARGER_TYPE_USB_DCP; /* Disable VDMSRC. */ usb_phy_io_write(phy, PWCTRL_DP_VSRC_EN, TUSB1211_POWER_CONTROL_CLR); /* Swap DP & DM. */ usb_phy_io_write(phy, VS1_DATAPOLARITY, TUSB1211_VENDOR_SPECIFIC1_SET); cleanup: /* If DCP detected, assert VDPSRC. */ if (type == POWER_SUPPLY_CHARGER_TYPE_USB_DCP) usb_phy_io_write(phy, PWCTRL_SW_CONTROL | PWCTRL_DP_VSRC_EN, TUSB1211_POWER_CONTROL_SET); usb_put_phy(phy); switch (type) { case POWER_SUPPLY_CHARGER_TYPE_ACA_DOCK: case POWER_SUPPLY_CHARGER_TYPE_ACA_A: case POWER_SUPPLY_CHARGER_TYPE_ACA_B: case POWER_SUPPLY_CHARGER_TYPE_ACA_C: case POWER_SUPPLY_CHARGER_TYPE_USB_DCP: case POWER_SUPPLY_CHARGER_TYPE_USB_CDP: case POWER_SUPPLY_CHARGER_TYPE_SE1: dwc_otg_charger_hwdet(true); break; default: break; }; return type; }
int dwc3_intel_resume(struct dwc_otg2 *otg) { struct pci_dev *pci_dev; struct usb_hcd *hcd = NULL; u32 data; int ret; if (!otg) return 0; hcd = container_of(otg->otg.host, struct usb_hcd, self); /* After resume from D0i3cold. The UTMI PHY D+ drive issue * reproduced due to all setting be reseted. So switch to OTG * mode avoid D+ drive too early. */ if ((otg->state == DWC_STATE_B_IDLE || otg->state == DWC_STATE_CHARGING || otg->state == DWC_STATE_WAIT_VBUS_FALL || otg->state == DWC_STATE_WAIT_VBUS_RAISE) && is_utmi_phy(otg)) { /* Reconnect DP/DM between Pmic and SOC for support host * and device mode. */ ret = intel_scu_ipc_update_register(PMIC_USBPHYCTRL, USBPHYRSTB, USBPHYRSTB); if (ret) otg_err(otg, "%s: ipc update failed\n", __func__); otg_write(otg, OEVTEN, 0); otg_write(otg, OCTL, 0); dwc3_switch_mode(otg, GCTL_PRT_CAP_DIR_OTG); } /* This is one SCU WA. SCU should set GUSB2PHYCFG0 * bit 4 for ULPI setting. But SCU haven't do that. * So do WA first until SCU fix. */ data = otg_read(otg, GUSB2PHYCFG0); if (is_utmi_phy(otg)) data &= ~(1 << 4); else data |= (1 << 4); otg_write(otg, GUSB2PHYCFG0, data); pci_dev = to_pci_dev(otg->dev); /* From synopsys spec 12.2.11. * Software cannot access memory-mapped I/O space * for 10ms. Delay 5 ms here should be enough. Too * long a delay causes hibernation exit failure. */ mdelay(5); pci_restore_state(pci_dev); if (pci_enable_device(pci_dev) < 0) { otg_err(otg, "pci_enable_device failed.\n"); return -EIO; } set_sus_phy(otg, 0); /* Delay 1ms waiting PHY clock debounce. * Without this debounce, will met fabric error randomly. **/ mdelay(1); if (otg->state == DWC_STATE_A_HOST && otg->resume_host) otg->resume_host(hcd); return 0; }
static int dwc_otg_probe(struct pci_dev *pdev, const struct pci_device_id *id) { int retval = 0; struct resource res[2]; struct dwc_otg2 *otg = NULL; unsigned long resource, len; if (!dwc3_otg_pdata) return -ENODEV; if (pci_enable_device(pdev) < 0) { dev_err(&pdev->dev, "pci device enable failed\n"); return -ENODEV; } pci_set_power_state(pdev, PCI_D0); pci_set_master(pdev); otg = dwc3_otg_alloc(&pdev->dev); if (!otg) { dev_err(&pdev->dev, "dwc3 otg init failed\n"); goto err; } /* control register: BAR 0 */ resource = pci_resource_start(pdev, 0); len = pci_resource_len(pdev, 0); if (!request_mem_region(resource, len, driver_name)) { otg_err(otg, "Request memory region failed\n"); retval = -EBUSY; goto err; } otg_dbg(otg, "dwc otg pci resouce: 0x%lu, len: 0x%lu\n", resource, len); otg_dbg(otg, "vendor: 0x%x, device: 0x%x\n", pdev->vendor, pdev->device); memset(res, 0x00, sizeof(struct resource) * ARRAY_SIZE(res)); res[0].start = pci_resource_start(pdev, 0); res[0].end = pci_resource_end(pdev, 0); res[0].name = "dwc_usb3_io"; res[0].flags = IORESOURCE_MEM; res[1].start = pdev->irq; res[1].name = "dwc_usb3_irq"; res[1].flags = IORESOURCE_IRQ; retval = dwc3_otg_create_children(otg, res, ARRAY_SIZE(res)); if (retval) { otg_err(otg, "dwc3 otg create alloc children failed\n"); goto err; } otg->irqnum = pdev->irq; wake_lock_init(&wakelock, WAKE_LOCK_SUSPEND, "dwc_otg_wakelock"); if (dwc3_otg_pdata->platform_init) { retval = dwc3_otg_pdata->platform_init(otg); if (retval) goto err; } pm_runtime_set_autosuspend_delay(&pdev->dev, 100); pm_runtime_use_autosuspend(&pdev->dev); pm_runtime_allow(&pdev->dev); pm_runtime_mark_last_busy(otg->dev); pm_runtime_put_autosuspend(&pdev->dev); return 0; err: if (the_transceiver) dwc_otg_remove(pdev); return retval; }
static int s3c6410_otg_drv_probe (struct platform_device *pdev) { int ret_val = 0; u32 reg_val = 0; otg_dbg(OTG_DBG_OTGHCDI_DRIVER, "s3c_otg_drv_probe \n"); otg_clock = clk_get(&pdev->dev, "otg"); if (otg_clock == NULL) { printk(KERN_INFO "failed to find otg clock source\n"); return -ENOENT; } clk_enable(otg_clock); ///init for host mode /** Allocate memory for the base HCD & Initialize the base HCD. */ g_pUsbHcd = usb_create_hcd(&s3c6410_otg_hc_driver, &pdev->dev, dev_name(&pdev->dev)); if (g_pUsbHcd == NULL) { ret_val = -ENOMEM; otg_err(OTG_DBG_OTGHCDI_DRIVER, "failed to usb_create_hcd\n"); goto err_out_clk; } // mapping hcd resource & device resource g_pUsbHcd->rsrc_start = pdev->resource[0].start; g_pUsbHcd->rsrc_len = pdev->resource[0].end - pdev->resource[0].start + 1; if (!request_mem_region(g_pUsbHcd->rsrc_start, g_pUsbHcd->rsrc_len, gHcdName)) { otg_err(OTG_DBG_OTGHCDI_DRIVER, "failed to request_mem_region\n"); reg_val = -EBUSY; goto err_out_create_hcd; } //Physical address => Virtual address g_pUsbHcd->regs = S3C_VA_OTG; g_pUsbHcd->self.otg_port = 1; g_pUDCBase = (u8 *)g_pUsbHcd->regs; /// call others' init() reg_val = otg_hcd_init_modules(); if( reg_val != USB_ERR_SUCCESS) { otg_err(OTG_DBG_OTGHCDI_DRIVER, "failed to otg_hcd_init_modules\n"); reg_val = USB_ERR_FAIL; goto err_out_create_hcd; } /** * Attempt to ensure this device is really a s3c6410 USB-OTG Controller. * Read and verify the SNPSID register contents. The value should be * 0x45F42XXX, which corresponds to "OT2", as in "OTG version 2.XX". */ //reg_val = read_reg_32((unsigned int *)((u8 *)g_pUsbHcd->regs + 0x40)); reg_val = read_reg_32(0x40); if ((reg_val & 0xFFFFF000) != 0x4F542000) { otg_err(OTG_DBG_OTGHCDI_DRIVER, "Bad value for SNPSID: 0x%x\n", reg_val); ret_val = -EINVAL; goto err_out_create_hcd_init; } /* * Finish generic HCD initialization and start the HCD. This function * allocates the DMA buffer pool, registers the USB bus, requests the * IRQ line, and calls s3c6410_otghcd_start method. */ ret_val = usb_add_hcd(g_pUsbHcd, pdev->resource[1].start, IRQF_DISABLED); if (ret_val < 0) { goto err_out_create_hcd_init; } otg_dbg(OTG_DBG_OTGHCDI_DRIVER,"OTG HCD Initialized HCD, bus=%s, usbbus=%d\n", "EMSP OTG Controller", g_pUsbHcd->self.busnum); return USB_ERR_SUCCESS; err_out_create_hcd_init: otg_hcd_deinit_modules(); release_mem_region(g_pUsbHcd->rsrc_start, g_pUsbHcd->rsrc_len); err_out_create_hcd: usb_put_hcd(g_pUsbHcd); err_out_clk: return ret_val; }
int dwc3_intel_byt_platform_init(struct dwc_otg2 *otg) { struct intel_dwc_otg_pdata *data; u32 gctl; int id_value; int retval; data = (struct intel_dwc_otg_pdata *)otg->otg_data; if (data) INIT_DELAYED_WORK(&data->suspend_discon_work, dwc_otg_suspend_discon_work); if (data && data->gpio_cs && data->gpio_reset) { retval = gpio_request(data->gpio_cs, "phy_cs"); if (retval < 0) { otg_err(otg, "failed to request CS pin %d\n", data->gpio_cs); return retval; } retval = gpio_request(data->gpio_reset, "phy_reset"); if (retval < 0) { otg_err(otg, "failed to request RESET pin %d\n", data->gpio_reset); return retval; } } if (data && data->gpio_id) { dev_info(otg->dev, "USB ID detection - Enabled - GPIO\n"); /* Set ID default value to 1 Floating */ data->id = 1; retval = gpio_request(data->gpio_id, "gpio_id"); if (retval < 0) { otg_err(otg, "failed to request ID pin %d\n", data->gpio_id); return retval; } retval = gpio_direction_input(data->gpio_id); if (retval < 0) { otg_err(otg, "failed to request ID pin %d\n", data->gpio_id); return retval; } retval = request_threaded_irq(gpio_to_irq(data->gpio_id), NULL, dwc3_gpio_id_irq, IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING | IRQF_ONESHOT, "dwc-gpio-id", otg->dev); if (retval < 0) { otg_err(otg, "failed to request interrupt gpio ID\n"); return retval; } otg_dbg(otg, "GPIO ID request/Interrupt reuqest Done\n"); id_value = dwc3_check_gpio_id(otg); if ((id_value == 0 || id_value == 1) && (data->id != id_value)) { data->id = id_value; dev_info(otg->dev, "ID notification (id = %d)\n", data->id); atomic_notifier_call_chain(&otg->usb2_phy.notifier, USB_EVENT_ID, &id_value); } else otg_dbg(otg, "Get incorrect ID value %d\n", id_value); } /* Don't let phy go to suspend mode, which * will cause FS/LS devices enum failed in host mode. */ set_sus_phy(otg, 0); retval = device_create_file(otg->dev, &dev_attr_otg_id); if (retval < 0) { otg_dbg(otg, "Can't register sysfs attribute: %d\n", retval); return -ENOMEM; } retval = device_create_file(otg->dev, &dev_attr_vbus_evt); if (retval < 0) { otg_dbg(otg, "Can't register sysfs attribute: %d\n", retval); return -ENOMEM; } otg_dbg(otg, "\n"); otg_write(otg, OEVTEN, 0); otg_write(otg, OCTL, 0); gctl = otg_read(otg, GCTL); gctl |= GCTL_PRT_CAP_DIR_OTG << GCTL_PRT_CAP_DIR_SHIFT; otg_write(otg, GCTL, gctl); return 0; }