static void dwc3_host_quirks(struct device *dev, struct xhci_hcd *xhci) { struct dwc_otg2 *otg = dwc3_get_otg(); struct intel_dwc_otg_pdata *data = NULL; data = (struct intel_dwc_otg_pdata *)otg->otg_data; if (otg && otg->otg_data) data = (struct intel_dwc_otg_pdata *)otg->otg_data; if (data && data->utmi_fs_det_wa) xhci->quirks |= XHCI_PORT_RESET; /* * As of now platform drivers don't provide MSI support so we ensure * here that the generic code does not try to make a pci_dev from our * dev struct in order to setup MSI */ xhci->quirks |= XHCI_PLAT; /* * Due to some fatal silicon errors, the controller have to do reset * for make driver continue work. */ xhci->quirks |= XHCI_RESET; }
static void dwc3_host_quirks(struct device *dev, struct xhci_hcd *xhci) { struct dwc_otg2 *otg = dwc3_get_otg(); struct intel_dwc_otg_pdata *data = NULL; data = (struct intel_dwc_otg_pdata *)otg->otg_data; if (otg && otg->otg_data) data = (struct intel_dwc_otg_pdata *)otg->otg_data; if (data && data->utmi_fs_det_wa) xhci->quirks |= XHCI_PORT_RESET; /* * As of now platform drivers don't provide MSI support so we ensure * here that the generic code does not try to make a pci_dev from our * dev struct in order to setup MSI */ xhci->quirks |= XHCI_PLAT; /* * Due to some fatal silicon errors, the controller have to do reset * for make driver continue work. */ xhci->quirks |= XHCI_RESET; /* * Change SS port host reset to warm reset, due to individual USB3.0 * UMS address fail caused by link state unstable afer hot reset. */ xhci->quirks |= XHCI_FORCE_WR; }
static int dwc_otg_charger_hwdet(bool enable) { int retval; struct usb_phy *phy; struct dwc_otg2 *otg = dwc3_get_otg(); /* Just return if charger detection is not enabled */ if (!charger_detect_enable(otg)) return 0; phy = usb_get_phy(USB_PHY_TYPE_USB2); if (!phy) return -ENODEV; if (enable) { retval = usb_phy_io_write(phy, PWCTRL_HWDETECT, TUSB1211_POWER_CONTROL_SET); if (retval) return retval; otg_dbg(otg, "set HWDETECT\n"); } else { retval = usb_phy_io_write(phy, PWCTRL_HWDETECT, TUSB1211_POWER_CONTROL_CLR); if (retval) return retval; otg_dbg(otg, "clear HWDETECT\n"); } usb_put_phy(phy); return 0; }
static int dwc3_intel_byt_handle_notification(struct notifier_block *nb, unsigned long event, void *data) { struct dwc_otg2 *otg = dwc3_get_otg(); int state, val; unsigned long flags; if (!otg) return NOTIFY_BAD; val = *(int *)data; spin_lock_irqsave(&otg->lock, flags); switch (event) { case USB_EVENT_VBUS: if (val) { 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; default: otg_dbg(otg, "DWC OTG Notify unknow notify message\n"); state = NOTIFY_DONE; } dwc3_wakeup_otg_thread(otg); spin_unlock_irqrestore(&otg->lock, flags); return state; }
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; }
static void dwc_a_bus_drop(struct usb_phy *x) { struct dwc_otg2 *otg = dwc3_get_otg(); unsigned long flags; if (otg->usb2_phy.vbus_state == VBUS_DISABLED) { spin_lock_irqsave(&otg->lock, flags); otg->user_events |= USER_A_BUS_DROP; dwc3_wakeup_otg_thread(otg); spin_unlock_irqrestore(&otg->lock, flags); } }
static void dwc_otg_suspend_discon_work(struct work_struct *work) { struct dwc_otg2 *otg = dwc3_get_otg(); unsigned long flags; otg_dbg(otg, "start suspend_disconn work\n"); spin_lock_irqsave(&otg->lock, flags); otg->otg_events |= OEVT_A_DEV_SESS_END_DET_EVNT; otg->otg_events &= ~OEVT_B_DEV_SES_VLD_DET_EVNT; dwc3_wakeup_otg_thread(otg); spin_unlock_irqrestore(&otg->lock, flags); }
static int dwc3_check_gpio_id(struct dwc_otg2 *otg2) { struct dwc_otg2 *otg = dwc3_get_otg(); struct intel_dwc_otg_pdata *data; int id = 0; int next = 0; int count = 0; unsigned long timeout; otg_dbg(otg, "start check gpio id\n"); data = (struct intel_dwc_otg_pdata *)otg->otg_data; /* Polling ID GPIO PIN value for SW debounce as HW debouce chip * is not connected on BYT CR board */ if (data && data->gpio_id) { id = gpio_get_value(data->gpio_id); /* If get 20 of the same value in a row by GPIO read, * then end SW debouce and return the ID value. * the total length of debouce time is 80ms~100ms for * 20 times GPIO read on BYT CR, which is longer than * normal debounce time done by HW chip. * Also set 200ms timeout value to avoid impact from * pin unstable cases */ timeout = jiffies + msecs_to_jiffies(200); while ((count < 20) && (!time_after(jiffies, timeout))) { next = gpio_get_value(data->gpio_id); otg_dbg(otg, "id value pin %d = %d\n", data->gpio_id, next); if (next < 0) return -EINVAL; else if (id == next) count++; else { id = next; count = 0; } } if (count >= 20) { otg_dbg(otg, "id debounce done = %d\n", id); return id; } } return -ENODEV; }
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 irqreturn_t dwc3_gpio_id_irq(int irq, void *dev) { struct dwc_otg2 *otg = dwc3_get_otg(); struct intel_dwc_otg_pdata *data; int id; data = (struct intel_dwc_otg_pdata *)otg->otg_data; id = dwc3_check_gpio_id(otg); if (id == 0 || id == 1) { if (data->id != id) { data->id = id; dev_info(otg->dev, "ID notification (id = %d)\n", data->id); atomic_notifier_call_chain(&otg->usb2_phy.notifier, USB_EVENT_ID, &id); } } return IRQ_HANDLED; }
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_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_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; }