示例#1
0
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;
}
示例#6
0
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;
}
示例#12
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;

}
示例#13
0
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;
}