diff options
| -rw-r--r-- | drivers/usb/dwc2/hcd_queue.c | 578 | 
1 files changed, 289 insertions, 289 deletions
| diff --git a/drivers/usb/dwc2/hcd_queue.c b/drivers/usb/dwc2/hcd_queue.c index 39f4de6279f8..8a2067bc1e62 100644 --- a/drivers/usb/dwc2/hcd_queue.c +++ b/drivers/usb/dwc2/hcd_queue.c @@ -57,295 +57,6 @@  #define DWC2_UNRESERVE_DELAY (msecs_to_jiffies(5))  /** - * dwc2_do_unreserve() - Actually release the periodic reservation - * - * This function actually releases the periodic bandwidth that was reserved - * by the given qh. - * - * @hsotg: The HCD state structure for the DWC OTG controller - * @qh:    QH for the periodic transfer. - */ -static void dwc2_do_unreserve(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh) -{ -	assert_spin_locked(&hsotg->lock); - -	WARN_ON(!qh->unreserve_pending); - -	/* No more unreserve pending--we're doing it */ -	qh->unreserve_pending = false; - -	if (WARN_ON(!list_empty(&qh->qh_list_entry))) -		list_del_init(&qh->qh_list_entry); - -	/* Update claimed usecs per (micro)frame */ -	hsotg->periodic_usecs -= qh->host_us; - -	if (hsotg->core_params->uframe_sched > 0) { -		int i; - -		for (i = 0; i < 8; i++) { -			hsotg->frame_usecs[i] += qh->frame_usecs[i]; -			qh->frame_usecs[i] = 0; -		} -	} else { -		/* Release periodic channel reservation */ -		hsotg->periodic_channels--; -	} -} - -/** - * dwc2_unreserve_timer_fn() - Timer function to release periodic reservation - * - * According to the kernel doc for usb_submit_urb() (specifically the part about - * "Reserved Bandwidth Transfers"), we need to keep a reservation active as - * long as a device driver keeps submitting.  Since we're using HCD_BH to give - * back the URB we need to give the driver a little bit of time before we - * release the reservation.  This worker is called after the appropriate - * delay. - * - * @work: Pointer to a qh unreserve_work. - */ -static void dwc2_unreserve_timer_fn(unsigned long data) -{ -	struct dwc2_qh *qh = (struct dwc2_qh *)data; -	struct dwc2_hsotg *hsotg = qh->hsotg; -	unsigned long flags; - -	/* -	 * Wait for the lock, or for us to be scheduled again.  We -	 * could be scheduled again if: -	 * - We started executing but didn't get the lock yet. -	 * - A new reservation came in, but cancel didn't take effect -	 *   because we already started executing. -	 * - The timer has been kicked again. -	 * In that case cancel and wait for the next call. -	 */ -	while (!spin_trylock_irqsave(&hsotg->lock, flags)) { -		if (timer_pending(&qh->unreserve_timer)) -			return; -	} - -	/* -	 * Might be no more unreserve pending if: -	 * - We started executing but didn't get the lock yet. -	 * - A new reservation came in, but cancel didn't take effect -	 *   because we already started executing. -	 * -	 * We can't put this in the loop above because unreserve_pending needs -	 * to be accessed under lock, so we can only check it once we got the -	 * lock. -	 */ -	if (qh->unreserve_pending) -		dwc2_do_unreserve(hsotg, qh); - -	spin_unlock_irqrestore(&hsotg->lock, flags); -} - -/** - * dwc2_qh_init() - Initializes a QH structure - * - * @hsotg: The HCD state structure for the DWC OTG controller - * @qh:    The QH to init - * @urb:   Holds the information about the device/endpoint needed to initialize - *         the QH - */ -#define SCHEDULE_SLOP 10 -static void dwc2_qh_init(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh, -			 struct dwc2_hcd_urb *urb) -{ -	int dev_speed, hub_addr, hub_port; -	char *speed, *type; - -	dev_vdbg(hsotg->dev, "%s()\n", __func__); - -	/* Initialize QH */ -	qh->hsotg = hsotg; -	setup_timer(&qh->unreserve_timer, dwc2_unreserve_timer_fn, -		    (unsigned long)qh); -	qh->ep_type = dwc2_hcd_get_pipe_type(&urb->pipe_info); -	qh->ep_is_in = dwc2_hcd_is_pipe_in(&urb->pipe_info) ? 1 : 0; - -	qh->data_toggle = DWC2_HC_PID_DATA0; -	qh->maxp = dwc2_hcd_get_mps(&urb->pipe_info); -	INIT_LIST_HEAD(&qh->qtd_list); -	INIT_LIST_HEAD(&qh->qh_list_entry); - -	/* FS/LS Endpoint on HS Hub, NOT virtual root hub */ -	dev_speed = dwc2_host_get_speed(hsotg, urb->priv); - -	dwc2_host_hub_info(hsotg, urb->priv, &hub_addr, &hub_port); - -	if ((dev_speed == USB_SPEED_LOW || dev_speed == USB_SPEED_FULL) && -	    hub_addr != 0 && hub_addr != 1) { -		dev_vdbg(hsotg->dev, -			 "QH init: EP %d: TT found at hub addr %d, for port %d\n", -			 dwc2_hcd_get_ep_num(&urb->pipe_info), hub_addr, -			 hub_port); -		qh->do_split = 1; -	} - -	if (qh->ep_type == USB_ENDPOINT_XFER_INT || -	    qh->ep_type == USB_ENDPOINT_XFER_ISOC) { -		/* Compute scheduling parameters once and save them */ -		u32 hprt, prtspd; - -		/* Todo: Account for split transfers in the bus time */ -		int bytecount = -			dwc2_hb_mult(qh->maxp) * dwc2_max_packet(qh->maxp); - -		qh->host_us = NS_TO_US(usb_calc_bus_time(qh->do_split ? -			      USB_SPEED_HIGH : dev_speed, qh->ep_is_in, -			      qh->ep_type == USB_ENDPOINT_XFER_ISOC, -			      bytecount)); - -		/* Ensure frame_number corresponds to the reality */ -		hsotg->frame_number = dwc2_hcd_get_frame_number(hsotg); -		/* Start in a slightly future (micro)frame */ -		qh->next_active_frame = dwc2_frame_num_inc(hsotg->frame_number, -						     SCHEDULE_SLOP); -		qh->host_interval = urb->interval; -		dwc2_sch_dbg(hsotg, "QH=%p init nxt=%04x, fn=%04x, int=%#x\n", -			     qh, qh->next_active_frame, hsotg->frame_number, -			     qh->host_interval); -#if 0 -		/* Increase interrupt polling rate for debugging */ -		if (qh->ep_type == USB_ENDPOINT_XFER_INT) -			qh->host_interval = 8; -#endif -		hprt = dwc2_readl(hsotg->regs + HPRT0); -		prtspd = (hprt & HPRT0_SPD_MASK) >> HPRT0_SPD_SHIFT; -		if (prtspd == HPRT0_SPD_HIGH_SPEED && -		    (dev_speed == USB_SPEED_LOW || -		     dev_speed == USB_SPEED_FULL)) { -			qh->host_interval *= 8; -			qh->next_active_frame |= 0x7; -			qh->start_split_frame = qh->next_active_frame; -			dwc2_sch_dbg(hsotg, -				     "QH=%p init*8 nxt=%04x, fn=%04x, int=%#x\n", -				     qh, qh->next_active_frame, -				     hsotg->frame_number, qh->host_interval); - -		} -		dev_dbg(hsotg->dev, "interval=%d\n", qh->host_interval); -	} - -	dev_vdbg(hsotg->dev, "DWC OTG HCD QH Initialized\n"); -	dev_vdbg(hsotg->dev, "DWC OTG HCD QH - qh = %p\n", qh); -	dev_vdbg(hsotg->dev, "DWC OTG HCD QH - Device Address = %d\n", -		 dwc2_hcd_get_dev_addr(&urb->pipe_info)); -	dev_vdbg(hsotg->dev, "DWC OTG HCD QH - Endpoint %d, %s\n", -		 dwc2_hcd_get_ep_num(&urb->pipe_info), -		 dwc2_hcd_is_pipe_in(&urb->pipe_info) ? "IN" : "OUT"); - -	qh->dev_speed = dev_speed; - -	switch (dev_speed) { -	case USB_SPEED_LOW: -		speed = "low"; -		break; -	case USB_SPEED_FULL: -		speed = "full"; -		break; -	case USB_SPEED_HIGH: -		speed = "high"; -		break; -	default: -		speed = "?"; -		break; -	} -	dev_vdbg(hsotg->dev, "DWC OTG HCD QH - Speed = %s\n", speed); - -	switch (qh->ep_type) { -	case USB_ENDPOINT_XFER_ISOC: -		type = "isochronous"; -		break; -	case USB_ENDPOINT_XFER_INT: -		type = "interrupt"; -		break; -	case USB_ENDPOINT_XFER_CONTROL: -		type = "control"; -		break; -	case USB_ENDPOINT_XFER_BULK: -		type = "bulk"; -		break; -	default: -		type = "?"; -		break; -	} - -	dev_vdbg(hsotg->dev, "DWC OTG HCD QH - Type = %s\n", type); - -	if (qh->ep_type == USB_ENDPOINT_XFER_INT) { -		dev_vdbg(hsotg->dev, "DWC OTG HCD QH - usecs = %d\n", -			 qh->host_us); -		dev_vdbg(hsotg->dev, "DWC OTG HCD QH - interval = %d\n", -			 qh->host_interval); -	} -} - -/** - * dwc2_hcd_qh_create() - Allocates and initializes a QH - * - * @hsotg:        The HCD state structure for the DWC OTG controller - * @urb:          Holds the information about the device/endpoint needed - *                to initialize the QH - * @atomic_alloc: Flag to do atomic allocation if needed - * - * Return: Pointer to the newly allocated QH, or NULL on error - */ -struct dwc2_qh *dwc2_hcd_qh_create(struct dwc2_hsotg *hsotg, -					  struct dwc2_hcd_urb *urb, -					  gfp_t mem_flags) -{ -	struct dwc2_qh *qh; - -	if (!urb->priv) -		return NULL; - -	/* Allocate memory */ -	qh = kzalloc(sizeof(*qh), mem_flags); -	if (!qh) -		return NULL; - -	dwc2_qh_init(hsotg, qh, urb); - -	if (hsotg->core_params->dma_desc_enable > 0 && -	    dwc2_hcd_qh_init_ddma(hsotg, qh, mem_flags) < 0) { -		dwc2_hcd_qh_free(hsotg, qh); -		return NULL; -	} - -	return qh; -} - -/** - * dwc2_hcd_qh_free() - Frees the QH - * - * @hsotg: HCD instance - * @qh:    The QH to free - * - * QH should already be removed from the list. QTD list should already be empty - * if called from URB Dequeue. - * - * Must NOT be called with interrupt disabled or spinlock held - */ -void dwc2_hcd_qh_free(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh) -{ -	/* Make sure any unreserve work is finished. */ -	if (del_timer_sync(&qh->unreserve_timer)) { -		unsigned long flags; - -		spin_lock_irqsave(&hsotg->lock, flags); -		dwc2_do_unreserve(hsotg, qh); -		spin_unlock_irqrestore(&hsotg->lock, flags); -	} - -	if (qh->desc_list) -		dwc2_hcd_qh_free_ddma(hsotg, qh); -	kfree(qh); -} - -/**   * dwc2_periodic_channel_available() - Checks that a channel is available for a   * periodic transfer   * @@ -534,6 +245,91 @@ static int dwc2_find_uframe(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh)  }  /** + * dwc2_do_unreserve() - Actually release the periodic reservation + * + * This function actually releases the periodic bandwidth that was reserved + * by the given qh. + * + * @hsotg: The HCD state structure for the DWC OTG controller + * @qh:    QH for the periodic transfer. + */ +static void dwc2_do_unreserve(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh) +{ +	assert_spin_locked(&hsotg->lock); + +	WARN_ON(!qh->unreserve_pending); + +	/* No more unreserve pending--we're doing it */ +	qh->unreserve_pending = false; + +	if (WARN_ON(!list_empty(&qh->qh_list_entry))) +		list_del_init(&qh->qh_list_entry); + +	/* Update claimed usecs per (micro)frame */ +	hsotg->periodic_usecs -= qh->host_us; + +	if (hsotg->core_params->uframe_sched > 0) { +		int i; + +		for (i = 0; i < 8; i++) { +			hsotg->frame_usecs[i] += qh->frame_usecs[i]; +			qh->frame_usecs[i] = 0; +		} +	} else { +		/* Release periodic channel reservation */ +		hsotg->periodic_channels--; +	} +} + +/** + * dwc2_unreserve_timer_fn() - Timer function to release periodic reservation + * + * According to the kernel doc for usb_submit_urb() (specifically the part about + * "Reserved Bandwidth Transfers"), we need to keep a reservation active as + * long as a device driver keeps submitting.  Since we're using HCD_BH to give + * back the URB we need to give the driver a little bit of time before we + * release the reservation.  This worker is called after the appropriate + * delay. + * + * @work: Pointer to a qh unreserve_work. + */ +static void dwc2_unreserve_timer_fn(unsigned long data) +{ +	struct dwc2_qh *qh = (struct dwc2_qh *)data; +	struct dwc2_hsotg *hsotg = qh->hsotg; +	unsigned long flags; + +	/* +	 * Wait for the lock, or for us to be scheduled again.  We +	 * could be scheduled again if: +	 * - We started executing but didn't get the lock yet. +	 * - A new reservation came in, but cancel didn't take effect +	 *   because we already started executing. +	 * - The timer has been kicked again. +	 * In that case cancel and wait for the next call. +	 */ +	while (!spin_trylock_irqsave(&hsotg->lock, flags)) { +		if (timer_pending(&qh->unreserve_timer)) +			return; +	} + +	/* +	 * Might be no more unreserve pending if: +	 * - We started executing but didn't get the lock yet. +	 * - A new reservation came in, but cancel didn't take effect +	 *   because we already started executing. +	 * +	 * We can't put this in the loop above because unreserve_pending needs +	 * to be accessed under lock, so we can only check it once we got the +	 * lock. +	 */ +	if (qh->unreserve_pending) +		dwc2_do_unreserve(hsotg, qh); + +	spin_unlock_irqrestore(&hsotg->lock, flags); +} + +/**   * dwc2_check_max_xfer_size() - Checks that the max transfer size allowed in a   * host channel is large enough to handle the maximum data transfer in a single   * (micro)frame for a periodic transfer @@ -695,6 +491,210 @@ static void dwc2_deschedule_periodic(struct dwc2_hsotg *hsotg,  }  /** + * dwc2_qh_init() - Initializes a QH structure + * + * @hsotg: The HCD state structure for the DWC OTG controller + * @qh:    The QH to init + * @urb:   Holds the information about the device/endpoint needed to initialize + *         the QH + */ +#define SCHEDULE_SLOP 10 +static void dwc2_qh_init(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh, +			 struct dwc2_hcd_urb *urb) +{ +	int dev_speed, hub_addr, hub_port; +	char *speed, *type; + +	dev_vdbg(hsotg->dev, "%s()\n", __func__); + +	/* Initialize QH */ +	qh->hsotg = hsotg; +	setup_timer(&qh->unreserve_timer, dwc2_unreserve_timer_fn, +		    (unsigned long)qh); +	qh->ep_type = dwc2_hcd_get_pipe_type(&urb->pipe_info); +	qh->ep_is_in = dwc2_hcd_is_pipe_in(&urb->pipe_info) ? 1 : 0; + +	qh->data_toggle = DWC2_HC_PID_DATA0; +	qh->maxp = dwc2_hcd_get_mps(&urb->pipe_info); +	INIT_LIST_HEAD(&qh->qtd_list); +	INIT_LIST_HEAD(&qh->qh_list_entry); + +	/* FS/LS Endpoint on HS Hub, NOT virtual root hub */ +	dev_speed = dwc2_host_get_speed(hsotg, urb->priv); + +	dwc2_host_hub_info(hsotg, urb->priv, &hub_addr, &hub_port); + +	if ((dev_speed == USB_SPEED_LOW || dev_speed == USB_SPEED_FULL) && +	    hub_addr != 0 && hub_addr != 1) { +		dev_vdbg(hsotg->dev, +			 "QH init: EP %d: TT found at hub addr %d, for port %d\n", +			 dwc2_hcd_get_ep_num(&urb->pipe_info), hub_addr, +			 hub_port); +		qh->do_split = 1; +	} + +	if (qh->ep_type == USB_ENDPOINT_XFER_INT || +	    qh->ep_type == USB_ENDPOINT_XFER_ISOC) { +		/* Compute scheduling parameters once and save them */ +		u32 hprt, prtspd; + +		/* Todo: Account for split transfers in the bus time */ +		int bytecount = +			dwc2_hb_mult(qh->maxp) * dwc2_max_packet(qh->maxp); + +		qh->host_us = NS_TO_US(usb_calc_bus_time(qh->do_split ? +			      USB_SPEED_HIGH : dev_speed, qh->ep_is_in, +			      qh->ep_type == USB_ENDPOINT_XFER_ISOC, +			      bytecount)); + +		/* Ensure frame_number corresponds to the reality */ +		hsotg->frame_number = dwc2_hcd_get_frame_number(hsotg); +		/* Start in a slightly future (micro)frame */ +		qh->next_active_frame = dwc2_frame_num_inc(hsotg->frame_number, +						     SCHEDULE_SLOP); +		qh->host_interval = urb->interval; +		dwc2_sch_dbg(hsotg, "QH=%p init nxt=%04x, fn=%04x, int=%#x\n", +			     qh, qh->next_active_frame, hsotg->frame_number, +			     qh->host_interval); +#if 0 +		/* Increase interrupt polling rate for debugging */ +		if (qh->ep_type == USB_ENDPOINT_XFER_INT) +			qh->host_interval = 8; +#endif +		hprt = dwc2_readl(hsotg->regs + HPRT0); +		prtspd = (hprt & HPRT0_SPD_MASK) >> HPRT0_SPD_SHIFT; +		if (prtspd == HPRT0_SPD_HIGH_SPEED && +		    (dev_speed == USB_SPEED_LOW || +		     dev_speed == USB_SPEED_FULL)) { +			qh->host_interval *= 8; +			qh->next_active_frame |= 0x7; +			qh->start_split_frame = qh->next_active_frame; +			dwc2_sch_dbg(hsotg, +				     "QH=%p init*8 nxt=%04x, fn=%04x, int=%#x\n", +				     qh, qh->next_active_frame, +				     hsotg->frame_number, qh->host_interval); + +		} +		dev_dbg(hsotg->dev, "interval=%d\n", qh->host_interval); +	} + +	dev_vdbg(hsotg->dev, "DWC OTG HCD QH Initialized\n"); +	dev_vdbg(hsotg->dev, "DWC OTG HCD QH - qh = %p\n", qh); +	dev_vdbg(hsotg->dev, "DWC OTG HCD QH - Device Address = %d\n", +		 dwc2_hcd_get_dev_addr(&urb->pipe_info)); +	dev_vdbg(hsotg->dev, "DWC OTG HCD QH - Endpoint %d, %s\n", +		 dwc2_hcd_get_ep_num(&urb->pipe_info), +		 dwc2_hcd_is_pipe_in(&urb->pipe_info) ? "IN" : "OUT"); + +	qh->dev_speed = dev_speed; + +	switch (dev_speed) { +	case USB_SPEED_LOW: +		speed = "low"; +		break; +	case USB_SPEED_FULL: +		speed = "full"; +		break; +	case USB_SPEED_HIGH: +		speed = "high"; +		break; +	default: +		speed = "?"; +		break; +	} +	dev_vdbg(hsotg->dev, "DWC OTG HCD QH - Speed = %s\n", speed); + +	switch (qh->ep_type) { +	case USB_ENDPOINT_XFER_ISOC: +		type = "isochronous"; +		break; +	case USB_ENDPOINT_XFER_INT: +		type = "interrupt"; +		break; +	case USB_ENDPOINT_XFER_CONTROL: +		type = "control"; +		break; +	case USB_ENDPOINT_XFER_BULK: +		type = "bulk"; +		break; +	default: +		type = "?"; +		break; +	} + +	dev_vdbg(hsotg->dev, "DWC OTG HCD QH - Type = %s\n", type); + +	if (qh->ep_type == USB_ENDPOINT_XFER_INT) { +		dev_vdbg(hsotg->dev, "DWC OTG HCD QH - usecs = %d\n", +			 qh->host_us); +		dev_vdbg(hsotg->dev, "DWC OTG HCD QH - interval = %d\n", +			 qh->host_interval); +	} +} + +/** + * dwc2_hcd_qh_create() - Allocates and initializes a QH + * + * @hsotg:        The HCD state structure for the DWC OTG controller + * @urb:          Holds the information about the device/endpoint needed + *                to initialize the QH + * @atomic_alloc: Flag to do atomic allocation if needed + * + * Return: Pointer to the newly allocated QH, or NULL on error + */ +struct dwc2_qh *dwc2_hcd_qh_create(struct dwc2_hsotg *hsotg, +					  struct dwc2_hcd_urb *urb, +					  gfp_t mem_flags) +{ +	struct dwc2_qh *qh; + +	if (!urb->priv) +		return NULL; + +	/* Allocate memory */ +	qh = kzalloc(sizeof(*qh), mem_flags); +	if (!qh) +		return NULL; + +	dwc2_qh_init(hsotg, qh, urb); + +	if (hsotg->core_params->dma_desc_enable > 0 && +	    dwc2_hcd_qh_init_ddma(hsotg, qh, mem_flags) < 0) { +		dwc2_hcd_qh_free(hsotg, qh); +		return NULL; +	} + +	return qh; +} + +/** + * dwc2_hcd_qh_free() - Frees the QH + * + * @hsotg: HCD instance + * @qh:    The QH to free + * + * QH should already be removed from the list. QTD list should already be empty + * if called from URB Dequeue. + * + * Must NOT be called with interrupt disabled or spinlock held + */ +void dwc2_hcd_qh_free(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh) +{ +	/* Make sure any unreserve work is finished. */ +	if (del_timer_sync(&qh->unreserve_timer)) { +		unsigned long flags; + +		spin_lock_irqsave(&hsotg->lock, flags); +		dwc2_do_unreserve(hsotg, qh); +		spin_unlock_irqrestore(&hsotg->lock, flags); +	} + +	if (qh->desc_list) +		dwc2_hcd_qh_free_ddma(hsotg, qh); +	kfree(qh); +} + +/**   * dwc2_hcd_qh_add() - Adds a QH to either the non periodic or periodic   * schedule if it is not already in the schedule. If the QH is already in   * the schedule, no action is taken. | 
