diff options
Diffstat (limited to 'drivers/usb/chipidea/udc.c')
| -rw-r--r-- | drivers/usb/chipidea/udc.c | 50 | 
1 files changed, 42 insertions, 8 deletions
diff --git a/drivers/usb/chipidea/udc.c b/drivers/usb/chipidea/udc.c index ff451048c1ac..41914a55055d 100644 --- a/drivers/usb/chipidea/udc.c +++ b/drivers/usb/chipidea/udc.c @@ -86,10 +86,8 @@ static int hw_device_state(struct ci_hdrc *ci, u32 dma)  		/* interrupt, error, port change, reset, sleep/suspend */  		hw_write(ci, OP_USBINTR, ~0,  			     USBi_UI|USBi_UEI|USBi_PCI|USBi_URI|USBi_SLI); -		hw_write(ci, OP_USBCMD, USBCMD_RS, USBCMD_RS);  	} else {  		hw_write(ci, OP_USBINTR, ~0, 0); -		hw_write(ci, OP_USBCMD, USBCMD_RS, 0);  	}  	return 0;  } @@ -522,6 +520,20 @@ static void free_pending_td(struct ci_hw_ep *hwep)  	kfree(pending);  } +static int reprime_dtd(struct ci_hdrc *ci, struct ci_hw_ep *hwep, +					   struct td_node *node) +{ +	hwep->qh.ptr->td.next = node->dma; +	hwep->qh.ptr->td.token &= +		cpu_to_le32(~(TD_STATUS_HALTED | TD_STATUS_ACTIVE)); + +	/* Synchronize before ep prime */ +	wmb(); + +	return hw_ep_prime(ci, hwep->num, hwep->dir, +				hwep->type == USB_ENDPOINT_XFER_CONTROL); +} +  /**   * _hardware_dequeue: handles a request at hardware level   * @gadget: gadget @@ -535,6 +547,7 @@ static int _hardware_dequeue(struct ci_hw_ep *hwep, struct ci_hw_req *hwreq)  	struct td_node *node, *tmpnode;  	unsigned remaining_length;  	unsigned actual = hwreq->req.length; +	struct ci_hdrc *ci = hwep->ci;  	if (hwreq->req.status != -EALREADY)  		return -EINVAL; @@ -544,6 +557,11 @@ static int _hardware_dequeue(struct ci_hw_ep *hwep, struct ci_hw_req *hwreq)  	list_for_each_entry_safe(node, tmpnode, &hwreq->tds, td) {  		tmptoken = le32_to_cpu(node->ptr->token);  		if ((TD_STATUS_ACTIVE & tmptoken) != 0) { +			int n = hw_ep_bit(hwep->num, hwep->dir); + +			if (ci->rev == CI_REVISION_24) +				if (!hw_read(ci, OP_ENDPTSTAT, BIT(n))) +					reprime_dtd(ci, hwep, node);  			hwreq->req.status = -EALREADY;  			return -EBUSY;  		} @@ -929,6 +947,13 @@ __acquires(hwep->lock)  	return retval;  } +static int otg_a_alt_hnp_support(struct ci_hdrc *ci) +{ +	dev_warn(&ci->gadget.dev, +		"connect the device to an alternate port if you want HNP\n"); +	return isr_setup_status_phase(ci); +} +  /**   * isr_setup_packet_handler: setup packet handler   * @ci: UDC descriptor @@ -1061,6 +1086,10 @@ __acquires(ci->lock)  							ci);  				}  				break; +			case USB_DEVICE_A_ALT_HNP_SUPPORT: +				if (ci_otg_is_fsm_mode(ci)) +					err = otg_a_alt_hnp_support(ci); +				break;  			default:  				goto delegate;  			} @@ -1151,10 +1180,13 @@ static int ep_enable(struct usb_ep *ep,  	/* only internal SW should enable ctrl endpts */ -	hwep->ep.desc = desc; - -	if (!list_empty(&hwep->qh.queue)) +	if (!list_empty(&hwep->qh.queue)) {  		dev_warn(hwep->ci->dev, "enabling a non-empty endpoint!\n"); +		spin_unlock_irqrestore(hwep->lock, flags); +		return -EBUSY; +	} + +	hwep->ep.desc = desc;  	hwep->dir  = usb_endpoint_dir_in(desc) ? TX : RX;  	hwep->num  = usb_endpoint_num(desc); @@ -1474,7 +1506,9 @@ static int ci_udc_vbus_session(struct usb_gadget *_gadget, int is_active)  			hw_device_reset(ci);  			hw_device_state(ci, ci->ep0out->qh.dma);  			usb_gadget_set_state(_gadget, USB_STATE_POWERED); +			usb_udc_vbus_handler(_gadget, true);  		} else { +			usb_udc_vbus_handler(_gadget, false);  			if (ci->driver)  				ci->driver->disconnect(&ci->gadget);  			hw_device_state(ci, 0); @@ -1540,13 +1574,12 @@ static int ci_udc_pullup(struct usb_gadget *_gadget, int is_on)  {  	struct ci_hdrc *ci = container_of(_gadget, struct ci_hdrc, gadget); -	if (!ci->vbus_active) -		return -EOPNOTSUPP; - +	pm_runtime_get_sync(&ci->gadget.dev);  	if (is_on)  		hw_write(ci, OP_USBCMD, USBCMD_RS, USBCMD_RS);  	else  		hw_write(ci, OP_USBCMD, USBCMD_RS, 0); +	pm_runtime_put_sync(&ci->gadget.dev);  	return 0;  } @@ -1676,6 +1709,7 @@ static int ci_udc_start(struct usb_gadget *gadget,  		spin_lock_irqsave(&ci->lock, flags);  		hw_device_reset(ci);  	} else { +		usb_udc_vbus_handler(&ci->gadget, false);  		pm_runtime_put_sync(&ci->gadget.dev);  		return retval;  	}  |