diff options
Diffstat (limited to 'drivers/usb')
| -rw-r--r-- | drivers/usb/gadget/Makefile | 2 | ||||
| -rw-r--r-- | drivers/usb/gadget/atmel_usba_udc.c | 1306 | ||||
| -rw-r--r-- | drivers/usb/gadget/atmel_usba_udc.h | 326 | ||||
| -rw-r--r-- | drivers/usb/gadget/config.c | 6 | ||||
| -rw-r--r-- | drivers/usb/gadget/ether.c | 5 | ||||
| -rw-r--r-- | drivers/usb/gadget/f_dfu.h | 3 | ||||
| -rw-r--r-- | drivers/usb/gadget/g_dnl.c | 5 | ||||
| -rw-r--r-- | drivers/usb/host/ehci-hcd.c | 38 | 
8 files changed, 1681 insertions, 10 deletions
| diff --git a/drivers/usb/gadget/Makefile b/drivers/usb/gadget/Makefile index 4c2a39a21..1590c4a75 100644 --- a/drivers/usb/gadget/Makefile +++ b/drivers/usb/gadget/Makefile @@ -18,10 +18,12 @@ endif  # new USB gadget layer dependencies  ifdef CONFIG_USB_GADGET +COBJS-$(CONFIG_USB_GADGET_ATMEL_USBA) += atmel_usba_udc.o  COBJS-$(CONFIG_USB_GADGET_S3C_UDC_OTG) += s3c_udc_otg.o  COBJS-$(CONFIG_USB_GADGET_FOTG210) += fotg210.o  COBJS-$(CONFIG_USBDOWNLOAD_GADGET) += g_dnl.o  COBJS-$(CONFIG_DFU_FUNCTION) += f_dfu.o +COBJS-$(CONFIG_USB_GADGET_MASS_STORAGE) += f_mass_storage.o  endif  ifdef CONFIG_USB_ETHER  COBJS-y += ether.o diff --git a/drivers/usb/gadget/atmel_usba_udc.c b/drivers/usb/gadget/atmel_usba_udc.c new file mode 100644 index 000000000..c99208d10 --- /dev/null +++ b/drivers/usb/gadget/atmel_usba_udc.c @@ -0,0 +1,1306 @@ +/* + * Driver for the Atmel USBA high speed USB device controller + * [Original from Linux kernel: drivers/usb/gadget/atmel_usba_udc.c] + * + * Copyright (C) 2005-2013 Atmel Corporation + *			   Bo Shen <voice.shen@atmel.com> + * + * SPDX-License-Identifier:     GPL-2.0+ + */ + +#include <common.h> +#include <asm/errno.h> +#include <asm/gpio.h> +#include <asm/hardware.h> +#include <linux/list.h> +#include <linux/usb/ch9.h> +#include <linux/usb/gadget.h> +#include <linux/usb/atmel_usba_udc.h> +#include <malloc.h> +#include <usb/lin_gadget_compat.h> + +#include "atmel_usba_udc.h" + +static int vbus_is_present(struct usba_udc *udc) +{ +	/* No Vbus detection: Assume always present */ +	return 1; +} + +static void next_fifo_transaction(struct usba_ep *ep, struct usba_request *req) +{ +	unsigned int transaction_len; + +	transaction_len = req->req.length - req->req.actual; +	req->last_transaction = 1; +	if (transaction_len > ep->ep.maxpacket) { +		transaction_len = ep->ep.maxpacket; +		req->last_transaction = 0; +	} else if (transaction_len == ep->ep.maxpacket && req->req.zero) { +			req->last_transaction = 0; +	} + +	DBG(DBG_QUEUE, "%s: submit_transaction, req %p (length %d)%s\n", +	    ep->ep.name, req, transaction_len, +	    req->last_transaction ? ", done" : ""); + +	memcpy(ep->fifo, req->req.buf + req->req.actual, transaction_len); +	usba_ep_writel(ep, SET_STA, USBA_TX_PK_RDY); +	req->req.actual += transaction_len; +} + +static void submit_request(struct usba_ep *ep, struct usba_request *req) +{ +	DBG(DBG_QUEUE, "%s: submit_request: req %p (length %d), dma: %d\n", +	    ep->ep.name, req, req->req.length, req->using_dma); + +	req->req.actual = 0; +	req->submitted = 1; + +	next_fifo_transaction(ep, req); +	if (req->last_transaction) { +		usba_ep_writel(ep, CTL_DIS, USBA_TX_PK_RDY); +		usba_ep_writel(ep, CTL_ENB, USBA_TX_COMPLETE); +	} else { +		usba_ep_writel(ep, CTL_DIS, USBA_TX_COMPLETE); +		usba_ep_writel(ep, CTL_ENB, USBA_TX_PK_RDY); +	} +} + +static void submit_next_request(struct usba_ep *ep) +{ +	struct usba_request *req; + +	if (list_empty(&ep->queue)) { +		usba_ep_writel(ep, CTL_DIS, USBA_TX_PK_RDY | USBA_RX_BK_RDY); +		return; +	} + +	req = list_entry(ep->queue.next, struct usba_request, queue); +	if (!req->submitted) +		submit_request(ep, req); +} + +static void send_status(struct usba_udc *udc, struct usba_ep *ep) +{ +	ep->state = STATUS_STAGE_IN; +	usba_ep_writel(ep, SET_STA, USBA_TX_PK_RDY); +	usba_ep_writel(ep, CTL_ENB, USBA_TX_COMPLETE); +} + +static void receive_data(struct usba_ep *ep) +{ +	struct usba_udc *udc = ep->udc; +	struct usba_request *req; +	unsigned long status; +	unsigned int bytecount, nr_busy; +	int is_complete = 0; + +	status = usba_ep_readl(ep, STA); +	nr_busy = USBA_BFEXT(BUSY_BANKS, status); + +	DBG(DBG_QUEUE, "receive data: nr_busy=%u\n", nr_busy); + +	while (nr_busy > 0) { +		if (list_empty(&ep->queue)) { +			usba_ep_writel(ep, CTL_DIS, USBA_RX_BK_RDY); +			break; +		} +		req = list_entry(ep->queue.next, +				 struct usba_request, queue); + +		bytecount = USBA_BFEXT(BYTE_COUNT, status); + +		if (status & USBA_SHORT_PACKET) +			is_complete = 1; +		if (req->req.actual + bytecount >= req->req.length) { +			is_complete = 1; +			bytecount = req->req.length - req->req.actual; +		} + +		memcpy(req->req.buf + req->req.actual, ep->fifo, bytecount); +		req->req.actual += bytecount; + +		usba_ep_writel(ep, CLR_STA, USBA_RX_BK_RDY); + +		if (is_complete) { +			DBG(DBG_QUEUE, "%s: request done\n", ep->ep.name); +			req->req.status = 0; +			list_del_init(&req->queue); +			usba_ep_writel(ep, CTL_DIS, USBA_RX_BK_RDY); +			spin_lock(&udc->lock); +			req->req.complete(&ep->ep, &req->req); +			spin_unlock(&udc->lock); +		} + +		status = usba_ep_readl(ep, STA); +		nr_busy = USBA_BFEXT(BUSY_BANKS, status); + +		if (is_complete && ep_is_control(ep)) { +			send_status(udc, ep); +			break; +		} +	} +} + +static void +request_complete(struct usba_ep *ep, struct usba_request *req, int status) +{ +	if (req->req.status == -EINPROGRESS) +		req->req.status = status; + +	DBG(DBG_GADGET | DBG_REQ, "%s: req %p complete: status %d, actual %u\n", +	    ep->ep.name, req, req->req.status, req->req.actual); + +	req->req.complete(&ep->ep, &req->req); +} + +static void +request_complete_list(struct usba_ep *ep, struct list_head *list, int status) +{ +	struct usba_request *req, *tmp_req; + +	list_for_each_entry_safe(req, tmp_req, list, queue) { +		list_del_init(&req->queue); +		request_complete(ep, req, status); +	} +} + +static int +usba_ep_enable(struct usb_ep *_ep, const struct usb_endpoint_descriptor *desc) +{ +	struct usba_ep *ep = to_usba_ep(_ep); +	struct usba_udc *udc = ep->udc; +	unsigned long flags, ept_cfg, maxpacket; +	unsigned int nr_trans; + +	DBG(DBG_GADGET, "%s: ep_enable: desc=%p\n", ep->ep.name, desc); + +	maxpacket = usb_endpoint_maxp(desc) & 0x7ff; + +	if (((desc->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK) +	      != ep->index) || +	      ep->index == 0 || +	      desc->bDescriptorType != USB_DT_ENDPOINT || +	      maxpacket == 0 || +	      maxpacket > ep->fifo_size) { +		DBG(DBG_ERR, "ep_enable: Invalid argument"); +		return -EINVAL; +	} + +	ep->is_isoc = 0; +	ep->is_in = 0; + +	if (maxpacket <= 8) +		ept_cfg = USBA_BF(EPT_SIZE, USBA_EPT_SIZE_8); +	else +		/* LSB is bit 1, not 0 */ +		ept_cfg = USBA_BF(EPT_SIZE, fls(maxpacket - 1) - 3); + +	DBG(DBG_HW, "%s: EPT_SIZE = %lu (maxpacket = %lu)\n", +	    ep->ep.name, ept_cfg, maxpacket); + +	if (usb_endpoint_dir_in(desc)) { +		ep->is_in = 1; +		ept_cfg |= USBA_EPT_DIR_IN; +	} + +	switch (usb_endpoint_type(desc)) { +	case USB_ENDPOINT_XFER_CONTROL: +		ept_cfg |= USBA_BF(EPT_TYPE, USBA_EPT_TYPE_CONTROL); +		ept_cfg |= USBA_BF(BK_NUMBER, USBA_BK_NUMBER_ONE); +		break; +	case USB_ENDPOINT_XFER_ISOC: +		if (!ep->can_isoc) { +			DBG(DBG_ERR, "ep_enable: %s is not isoc capable\n", +			    ep->ep.name); +			return -EINVAL; +		} + +		/* +		 * Bits 11:12 specify number of _additional_ +		 * transactions per microframe. +		 */ +		nr_trans = ((usb_endpoint_maxp(desc) >> 11) & 3) + 1; +		if (nr_trans > 3) +			return -EINVAL; + +		ep->is_isoc = 1; +		ept_cfg |= USBA_BF(EPT_TYPE, USBA_EPT_TYPE_ISO); + +		/* +		 * Do triple-buffering on high-bandwidth iso endpoints. +		 */ +		if (nr_trans > 1 && ep->nr_banks == 3) +			ept_cfg |= USBA_BF(BK_NUMBER, USBA_BK_NUMBER_TRIPLE); +		else +			ept_cfg |= USBA_BF(BK_NUMBER, USBA_BK_NUMBER_DOUBLE); +		ept_cfg |= USBA_BF(NB_TRANS, nr_trans); +		break; +	case USB_ENDPOINT_XFER_BULK: +		ept_cfg |= USBA_BF(EPT_TYPE, USBA_EPT_TYPE_BULK); +		ept_cfg |= USBA_BF(BK_NUMBER, USBA_BK_NUMBER_ONE); +		break; +	case USB_ENDPOINT_XFER_INT: +		ept_cfg |= USBA_BF(EPT_TYPE, USBA_EPT_TYPE_INT); +		ept_cfg |= USBA_BF(BK_NUMBER, USBA_BK_NUMBER_ONE); +		break; +	} + +	spin_lock_irqsave(&ep->udc->lock, flags); + +	ep->desc = desc; +	ep->ep.maxpacket = maxpacket; + +	usba_ep_writel(ep, CFG, ept_cfg); +	usba_ep_writel(ep, CTL_ENB, USBA_EPT_ENABLE); + +	usba_writel(udc, INT_ENB, +		    (usba_readl(udc, INT_ENB) +		     | USBA_BF(EPT_INT, 1 << ep->index))); + +	spin_unlock_irqrestore(&udc->lock, flags); + +	DBG(DBG_HW, "EPT_CFG%d after init: %#08lx\n", ep->index, +	    (unsigned long)usba_ep_readl(ep, CFG)); +	DBG(DBG_HW, "INT_ENB after init: %#08lx\n", +	    (unsigned long)usba_readl(udc, INT_ENB)); + +	return 0; +} + +static int usba_ep_disable(struct usb_ep *_ep) +{ +	struct usba_ep *ep = to_usba_ep(_ep); +	struct usba_udc *udc = ep->udc; +	LIST_HEAD(req_list); +	unsigned long flags; + +	DBG(DBG_GADGET, "ep_disable: %s\n", ep->ep.name); + +	spin_lock_irqsave(&udc->lock, flags); + +	if (!ep->desc) { +		spin_unlock_irqrestore(&udc->lock, flags); +		/* REVISIT because this driver disables endpoints in +		 * reset_all_endpoints() before calling disconnect(), +		 * most gadget drivers would trigger this non-error ... +		 */ +		if (udc->gadget.speed != USB_SPEED_UNKNOWN) +			DBG(DBG_ERR, "ep_disable: %s not enabled\n", +			    ep->ep.name); +		return -EINVAL; +	} +	ep->desc = NULL; + +	list_splice_init(&ep->queue, &req_list); +	usba_ep_writel(ep, CFG, 0); +	usba_ep_writel(ep, CTL_DIS, USBA_EPT_ENABLE); +	usba_writel(udc, INT_ENB, +		    usba_readl(udc, INT_ENB) & +		    ~USBA_BF(EPT_INT, 1 << ep->index)); + +	request_complete_list(ep, &req_list, -ESHUTDOWN); + +	spin_unlock_irqrestore(&udc->lock, flags); + +	return 0; +} + +static struct usb_request * +usba_ep_alloc_request(struct usb_ep *_ep, gfp_t gfp_flags) +{ +	struct usba_request *req; + +	DBG(DBG_GADGET, "ep_alloc_request: %p, 0x%x\n", _ep, gfp_flags); + +	req = malloc(sizeof(struct usba_request)); +	if (!req) +		return NULL; + +	INIT_LIST_HEAD(&req->queue); + +	return &req->req; +} + +static void +usba_ep_free_request(struct usb_ep *_ep, struct usb_request *_req) +{ +	struct usba_request *req = to_usba_req(_req); + +	DBG(DBG_GADGET, "ep_free_request: %p, %p\n", _ep, _req); + +	free(req); +} + +static int +usba_ep_queue(struct usb_ep *_ep, struct usb_request *_req, gfp_t gfp_flags) +{ +	struct usba_request *req = to_usba_req(_req); +	struct usba_ep *ep = to_usba_ep(_ep); +	struct usba_udc *udc = ep->udc; +	unsigned long flags; +	int ret; + +	DBG(DBG_GADGET | DBG_QUEUE | DBG_REQ, "%s: queue req %p, len %u\n", +	    ep->ep.name, req, _req->length); + +	if (!udc->driver || udc->gadget.speed == USB_SPEED_UNKNOWN || +	    !ep->desc) +		return -ESHUTDOWN; + +	req->submitted = 0; +	req->using_dma = 0; +	req->last_transaction = 0; + +	_req->status = -EINPROGRESS; +	_req->actual = 0; + +	/* May have received a reset since last time we checked */ +	ret = -ESHUTDOWN; +	spin_lock_irqsave(&udc->lock, flags); +	if (ep->desc) { +		list_add_tail(&req->queue, &ep->queue); + +		if ((!ep_is_control(ep) && ep->is_in) || +		    (ep_is_control(ep) && (ep->state == DATA_STAGE_IN || +		    ep->state == STATUS_STAGE_IN))) +			usba_ep_writel(ep, CTL_ENB, USBA_TX_PK_RDY); +		else +			usba_ep_writel(ep, CTL_ENB, USBA_RX_BK_RDY); + +		ret = 0; +	} +	spin_unlock_irqrestore(&udc->lock, flags); + +	return ret; +} + +static int usba_ep_dequeue(struct usb_ep *_ep, struct usb_request *_req) +{ +	struct usba_ep *ep = to_usba_ep(_ep); +	struct usba_request *req = to_usba_req(_req); + +	DBG(DBG_GADGET | DBG_QUEUE, "ep_dequeue: %s, req %p\n", +	    ep->ep.name, req); + +	/* +	 * Errors should stop the queue from advancing until the +	 * completion function returns. +	 */ +	list_del_init(&req->queue); + +	request_complete(ep, req, -ECONNRESET); + +	/* Process the next request if any */ +	submit_next_request(ep); + +	return 0; +} + +static int usba_ep_set_halt(struct usb_ep *_ep, int value) +{ +	struct usba_ep *ep = to_usba_ep(_ep); +	unsigned long flags; +	int ret = 0; + +	DBG(DBG_GADGET, "endpoint %s: %s HALT\n", ep->ep.name, +	    value ? "set" : "clear"); + +	if (!ep->desc) { +		DBG(DBG_ERR, "Attempted to halt uninitialized ep %s\n", +		    ep->ep.name); +		return -ENODEV; +	} + +	if (ep->is_isoc) { +		DBG(DBG_ERR, "Attempted to halt isochronous ep %s\n", +		    ep->ep.name); +		return -ENOTTY; +	} + +	spin_lock_irqsave(&udc->lock, flags); + +	/* +	 * We can't halt IN endpoints while there are still data to be +	 * transferred +	 */ +	if (!list_empty(&ep->queue) || +	    ((value && ep->is_in && (usba_ep_readl(ep, STA) & +	    USBA_BF(BUSY_BANKS, -1L))))) { +		ret = -EAGAIN; +	} else { +		if (value) +			usba_ep_writel(ep, SET_STA, USBA_FORCE_STALL); +		else +			usba_ep_writel(ep, CLR_STA, +				       USBA_FORCE_STALL | USBA_TOGGLE_CLR); +		usba_ep_readl(ep, STA); +	} + +	spin_unlock_irqrestore(&udc->lock, flags); + +	return ret; +} + +static int usba_ep_fifo_status(struct usb_ep *_ep) +{ +	struct usba_ep *ep = to_usba_ep(_ep); + +	return USBA_BFEXT(BYTE_COUNT, usba_ep_readl(ep, STA)); +} + +static void usba_ep_fifo_flush(struct usb_ep *_ep) +{ +	struct usba_ep *ep = to_usba_ep(_ep); +	struct usba_udc *udc = ep->udc; + +	usba_writel(udc, EPT_RST, 1 << ep->index); +} + +static const struct usb_ep_ops usba_ep_ops = { +	.enable		= usba_ep_enable, +	.disable	= usba_ep_disable, +	.alloc_request	= usba_ep_alloc_request, +	.free_request	= usba_ep_free_request, +	.queue		= usba_ep_queue, +	.dequeue	= usba_ep_dequeue, +	.set_halt	= usba_ep_set_halt, +	.fifo_status	= usba_ep_fifo_status, +	.fifo_flush	= usba_ep_fifo_flush, +}; + +static int usba_udc_get_frame(struct usb_gadget *gadget) +{ +	struct usba_udc *udc = to_usba_udc(gadget); + +	return USBA_BFEXT(FRAME_NUMBER, usba_readl(udc, FNUM)); +} + +static int usba_udc_wakeup(struct usb_gadget *gadget) +{ +	struct usba_udc *udc = to_usba_udc(gadget); +	unsigned long flags; +	u32 ctrl; +	int ret = -EINVAL; + +	spin_lock_irqsave(&udc->lock, flags); +	if (udc->devstatus & (1 << USB_DEVICE_REMOTE_WAKEUP)) { +		ctrl = usba_readl(udc, CTRL); +		usba_writel(udc, CTRL, ctrl | USBA_REMOTE_WAKE_UP); +		ret = 0; +	} +	spin_unlock_irqrestore(&udc->lock, flags); + +	return ret; +} + +static int +usba_udc_set_selfpowered(struct usb_gadget *gadget, int is_selfpowered) +{ +	struct usba_udc *udc = to_usba_udc(gadget); +	unsigned long flags; + +	spin_lock_irqsave(&udc->lock, flags); +	if (is_selfpowered) +		udc->devstatus |= 1 << USB_DEVICE_SELF_POWERED; +	else +		udc->devstatus &= ~(1 << USB_DEVICE_SELF_POWERED); +	spin_unlock_irqrestore(&udc->lock, flags); + +	return 0; +} + +static const struct usb_gadget_ops usba_udc_ops = { +	.get_frame		= usba_udc_get_frame, +	.wakeup			= usba_udc_wakeup, +	.set_selfpowered	= usba_udc_set_selfpowered, +}; + +static struct usb_endpoint_descriptor usba_ep0_desc = { +	.bLength = USB_DT_ENDPOINT_SIZE, +	.bDescriptorType = USB_DT_ENDPOINT, +	.bEndpointAddress = 0, +	.bmAttributes = USB_ENDPOINT_XFER_CONTROL, +	.wMaxPacketSize = cpu_to_le16(64), +	/* FIXME: I have no idea what to put here */ +	.bInterval = 1, +}; + +/* + * Called with interrupts disabled and udc->lock held. + */ +static void reset_all_endpoints(struct usba_udc *udc) +{ +	struct usba_ep *ep; +	struct usba_request *req, *tmp_req; + +	usba_writel(udc, EPT_RST, ~0UL); + +	ep = to_usba_ep(udc->gadget.ep0); +	list_for_each_entry_safe(req, tmp_req, &ep->queue, queue) { +		list_del_init(&req->queue); +		request_complete(ep, req, -ECONNRESET); +	} + +	/* NOTE:  normally, the next call to the gadget driver is in +	 * charge of disabling endpoints... usually disconnect(). +	 * The exception would be entering a high speed test mode. +	 * +	 * FIXME remove this code ... and retest thoroughly. +	 */ +	list_for_each_entry(ep, &udc->gadget.ep_list, ep.ep_list) { +		if (ep->desc) { +			spin_unlock(&udc->lock); +			usba_ep_disable(&ep->ep); +			spin_lock(&udc->lock); +		} +	} +} + +static struct usba_ep *get_ep_by_addr(struct usba_udc *udc, u16 wIndex) +{ +	struct usba_ep *ep; + +	if ((wIndex & USB_ENDPOINT_NUMBER_MASK) == 0) +		return to_usba_ep(udc->gadget.ep0); + +	list_for_each_entry(ep, &udc->gadget.ep_list, ep.ep_list) { +		u8 bEndpointAddress; + +		if (!ep->desc) +			continue; +		bEndpointAddress = ep->desc->bEndpointAddress; +		if ((wIndex ^ bEndpointAddress) & USB_DIR_IN) +			continue; +		if ((bEndpointAddress & USB_ENDPOINT_NUMBER_MASK) +				== (wIndex & USB_ENDPOINT_NUMBER_MASK)) +			return ep; +	} + +	return NULL; +} + +/* Called with interrupts disabled and udc->lock held */ +static inline void set_protocol_stall(struct usba_udc *udc, struct usba_ep *ep) +{ +	usba_ep_writel(ep, SET_STA, USBA_FORCE_STALL); +	ep->state = WAIT_FOR_SETUP; +} + +static inline int is_stalled(struct usba_udc *udc, struct usba_ep *ep) +{ +	if (usba_ep_readl(ep, STA) & USBA_FORCE_STALL) +		return 1; +	return 0; +} + +static inline void set_address(struct usba_udc *udc, unsigned int addr) +{ +	u32 regval; + +	DBG(DBG_BUS, "setting address %u...\n", addr); +	regval = usba_readl(udc, CTRL); +	regval = USBA_BFINS(DEV_ADDR, addr, regval); +	usba_writel(udc, CTRL, regval); +} + +static int do_test_mode(struct usba_udc *udc) +{ +	static const char test_packet_buffer[] = { +		/* JKJKJKJK * 9 */ +		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +		/* JJKKJJKK * 8 */ +		0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, +		/* JJKKJJKK * 8 */ +		0xEE, 0xEE, 0xEE, 0xEE, 0xEE, 0xEE, 0xEE, 0xEE, +		/* JJJJJJJKKKKKKK * 8 */ +		0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +		0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +		/* JJJJJJJK * 8 */ +		0x7F, 0xBF, 0xDF, 0xEF, 0xF7, 0xFB, 0xFD, +		/* {JKKKKKKK * 10}, JK */ +		0xFC, 0x7E, 0xBF, 0xDF, 0xEF, 0xF7, 0xFB, 0xFD, 0x7E +	}; +	struct usba_ep *ep; +	int test_mode; + +	test_mode = udc->test_mode; + +	/* Start from a clean slate */ +	reset_all_endpoints(udc); + +	switch (test_mode) { +	case 0x0100: +		/* Test_J */ +		usba_writel(udc, TST, USBA_TST_J_MODE); +		DBG(DBG_ALL, "Entering Test_J mode...\n"); +		break; +	case 0x0200: +		/* Test_K */ +		usba_writel(udc, TST, USBA_TST_K_MODE); +		DBG(DBG_ALL, "Entering Test_K mode...\n"); +		break; +	case 0x0300: +		/* +		 * Test_SE0_NAK: Force high-speed mode and set up ep0 +		 * for Bulk IN transfers +		 */ +		ep = &udc->usba_ep[0]; +		usba_writel(udc, TST, +			    USBA_BF(SPEED_CFG, USBA_SPEED_CFG_FORCE_HIGH)); +		usba_ep_writel(ep, CFG, +			       USBA_BF(EPT_SIZE, USBA_EPT_SIZE_64) +			       | USBA_EPT_DIR_IN +			       | USBA_BF(EPT_TYPE, USBA_EPT_TYPE_BULK) +			       | USBA_BF(BK_NUMBER, 1)); +		if (!(usba_ep_readl(ep, CFG) & USBA_EPT_MAPPED)) { +			set_protocol_stall(udc, ep); +			DBG(DBG_ALL, "Test_SE0_NAK: ep0 not mapped\n"); +		} else { +			usba_ep_writel(ep, CTL_ENB, USBA_EPT_ENABLE); +			DBG(DBG_ALL, "Entering Test_SE0_NAK mode...\n"); +		} +		break; +	case 0x0400: +		/* Test_Packet */ +		ep = &udc->usba_ep[0]; +		usba_ep_writel(ep, CFG, +			       USBA_BF(EPT_SIZE, USBA_EPT_SIZE_64) +			       | USBA_EPT_DIR_IN +			       | USBA_BF(EPT_TYPE, USBA_EPT_TYPE_BULK) +			       | USBA_BF(BK_NUMBER, 1)); +		if (!(usba_ep_readl(ep, CFG) & USBA_EPT_MAPPED)) { +			set_protocol_stall(udc, ep); +			DBG(DBG_ALL, "Test_Packet: ep0 not mapped\n"); +		} else { +			usba_ep_writel(ep, CTL_ENB, USBA_EPT_ENABLE); +			usba_writel(udc, TST, USBA_TST_PKT_MODE); +			memcpy(ep->fifo, test_packet_buffer, +			       sizeof(test_packet_buffer)); +			usba_ep_writel(ep, SET_STA, USBA_TX_PK_RDY); +			DBG(DBG_ALL, "Entering Test_Packet mode...\n"); +		} +		break; +	default: +		DBG(DBG_ERR, "Invalid test mode: 0x%04x\n", test_mode); +		return -EINVAL; +	} + +	return 0; +} + +/* Avoid overly long expressions */ +static inline bool feature_is_dev_remote_wakeup(struct usb_ctrlrequest *crq) +{ +	if (crq->wValue == cpu_to_le16(USB_DEVICE_REMOTE_WAKEUP)) +		return true; +	return false; +} + +static inline bool feature_is_dev_test_mode(struct usb_ctrlrequest *crq) +{ +	if (crq->wValue == cpu_to_le16(USB_DEVICE_TEST_MODE)) +		return true; +	return false; +} + +static inline bool feature_is_ep_halt(struct usb_ctrlrequest *crq) +{ +	if (crq->wValue == cpu_to_le16(USB_ENDPOINT_HALT)) +		return true; +	return false; +} + +static int handle_ep0_setup(struct usba_udc *udc, struct usba_ep *ep, +		struct usb_ctrlrequest *crq) +{ +	int retval = 0; + +	switch (crq->bRequest) { +	case USB_REQ_GET_STATUS: { +		u16 status; + +		if (crq->bRequestType == (USB_DIR_IN | USB_RECIP_DEVICE)) { +			status = cpu_to_le16(udc->devstatus); +		} else if (crq->bRequestType +				== (USB_DIR_IN | USB_RECIP_INTERFACE)) { +			status = cpu_to_le16(0); +		} else if (crq->bRequestType +				== (USB_DIR_IN | USB_RECIP_ENDPOINT)) { +			struct usba_ep *target; + +			target = get_ep_by_addr(udc, le16_to_cpu(crq->wIndex)); +			if (!target) +				goto stall; + +			status = 0; +			if (is_stalled(udc, target)) +				status |= cpu_to_le16(1); +		} else { +			goto delegate; +		} + +		/* Write directly to the FIFO. No queueing is done. */ +		if (crq->wLength != cpu_to_le16(sizeof(status))) +			goto stall; +		ep->state = DATA_STAGE_IN; +		__raw_writew(status, ep->fifo); +		usba_ep_writel(ep, SET_STA, USBA_TX_PK_RDY); +		break; +	} + +	case USB_REQ_CLEAR_FEATURE: { +		if (crq->bRequestType == USB_RECIP_DEVICE) { +			if (feature_is_dev_remote_wakeup(crq)) +				udc->devstatus +					&= ~(1 << USB_DEVICE_REMOTE_WAKEUP); +			else +				/* Can't CLEAR_FEATURE TEST_MODE */ +				goto stall; +		} else if (crq->bRequestType == USB_RECIP_ENDPOINT) { +			struct usba_ep *target; + +			if (crq->wLength != cpu_to_le16(0) || +			    !feature_is_ep_halt(crq)) +				goto stall; +			target = get_ep_by_addr(udc, le16_to_cpu(crq->wIndex)); +			if (!target) +				goto stall; + +			usba_ep_writel(target, CLR_STA, USBA_FORCE_STALL); +			if (target->index != 0) +				usba_ep_writel(target, CLR_STA, +					       USBA_TOGGLE_CLR); +		} else { +			goto delegate; +		} + +		send_status(udc, ep); +		break; +	} + +	case USB_REQ_SET_FEATURE: { +		if (crq->bRequestType == USB_RECIP_DEVICE) { +			if (feature_is_dev_test_mode(crq)) { +				send_status(udc, ep); +				ep->state = STATUS_STAGE_TEST; +				udc->test_mode = le16_to_cpu(crq->wIndex); +				return 0; +			} else if (feature_is_dev_remote_wakeup(crq)) { +				udc->devstatus |= 1 << USB_DEVICE_REMOTE_WAKEUP; +			} else { +				goto stall; +			} +		} else if (crq->bRequestType == USB_RECIP_ENDPOINT) { +			struct usba_ep *target; + +			if (crq->wLength != cpu_to_le16(0) || +			    !feature_is_ep_halt(crq)) +				goto stall; + +			target = get_ep_by_addr(udc, le16_to_cpu(crq->wIndex)); +			if (!target) +				goto stall; + +			usba_ep_writel(target, SET_STA, USBA_FORCE_STALL); +		} else { +			goto delegate; +		} + +		send_status(udc, ep); +		break; +	} + +	case USB_REQ_SET_ADDRESS: +		if (crq->bRequestType != (USB_DIR_OUT | USB_RECIP_DEVICE)) +			goto delegate; + +		set_address(udc, le16_to_cpu(crq->wValue)); +		send_status(udc, ep); +		ep->state = STATUS_STAGE_ADDR; +		break; + +	default: +delegate: +		spin_unlock(&udc->lock); +		retval = udc->driver->setup(&udc->gadget, crq); +		spin_lock(&udc->lock); +	} + +	return retval; + +stall: +	DBG(DBG_ALL, "%s: Invalid setup request: %02x.%02x v%04x i%04x l%d\n", +	    ep->ep.name, crq->bRequestType, crq->bRequest, +	    le16_to_cpu(crq->wValue), le16_to_cpu(crq->wIndex), +	    le16_to_cpu(crq->wLength)); +	set_protocol_stall(udc, ep); + +	return -1; +} + +static void usba_control_irq(struct usba_udc *udc, struct usba_ep *ep) +{ +	struct usba_request *req; +	u32 epstatus; +	u32 epctrl; + +restart: +	epstatus = usba_ep_readl(ep, STA); +	epctrl = usba_ep_readl(ep, CTL); + +	DBG(DBG_INT, "%s [%d]: s/%08x c/%08x\n", +	    ep->ep.name, ep->state, epstatus, epctrl); + +	req = NULL; +	if (!list_empty(&ep->queue)) +		req = list_entry(ep->queue.next, +				 struct usba_request, queue); + +	if ((epctrl & USBA_TX_PK_RDY) && !(epstatus & USBA_TX_PK_RDY)) { +		if (req->submitted) +			next_fifo_transaction(ep, req); +		else +			submit_request(ep, req); + +		if (req->last_transaction) { +			usba_ep_writel(ep, CTL_DIS, USBA_TX_PK_RDY); +			usba_ep_writel(ep, CTL_ENB, USBA_TX_COMPLETE); +		} +		goto restart; +	} +	if ((epstatus & epctrl) & USBA_TX_COMPLETE) { +		usba_ep_writel(ep, CLR_STA, USBA_TX_COMPLETE); + +		switch (ep->state) { +		case DATA_STAGE_IN: +			usba_ep_writel(ep, CTL_ENB, USBA_RX_BK_RDY); +			usba_ep_writel(ep, CTL_DIS, USBA_TX_COMPLETE); +			ep->state = STATUS_STAGE_OUT; +			break; +		case STATUS_STAGE_ADDR: +			/* Activate our new address */ +			usba_writel(udc, CTRL, (usba_readl(udc, CTRL) +						| USBA_FADDR_EN)); +			usba_ep_writel(ep, CTL_DIS, USBA_TX_COMPLETE); +			ep->state = WAIT_FOR_SETUP; +			break; +		case STATUS_STAGE_IN: +			if (req) { +				list_del_init(&req->queue); +				request_complete(ep, req, 0); +				submit_next_request(ep); +			} +			usba_ep_writel(ep, CTL_DIS, USBA_TX_COMPLETE); +			ep->state = WAIT_FOR_SETUP; +			break; +		case STATUS_STAGE_TEST: +			usba_ep_writel(ep, CTL_DIS, USBA_TX_COMPLETE); +			ep->state = WAIT_FOR_SETUP; +			if (do_test_mode(udc)) +				set_protocol_stall(udc, ep); +			break; +		default: +			DBG(DBG_ALL, "%s: TXCOMP: Invalid endpoint state %d\n", +			    ep->ep.name, ep->state); +			set_protocol_stall(udc, ep); +			break; +		} + +		goto restart; +	} +	if ((epstatus & epctrl) & USBA_RX_BK_RDY) { +		switch (ep->state) { +		case STATUS_STAGE_OUT: +			usba_ep_writel(ep, CLR_STA, USBA_RX_BK_RDY); +			usba_ep_writel(ep, CTL_DIS, USBA_RX_BK_RDY); + +			if (req) { +				list_del_init(&req->queue); +				request_complete(ep, req, 0); +			} +			ep->state = WAIT_FOR_SETUP; +			break; + +		case DATA_STAGE_OUT: +			receive_data(ep); +			break; + +		default: +			usba_ep_writel(ep, CLR_STA, USBA_RX_BK_RDY); +			usba_ep_writel(ep, CTL_DIS, USBA_RX_BK_RDY); +			DBG(DBG_ALL, "%s: RXRDY: Invalid endpoint state %d\n", +			    ep->ep.name, ep->state); +			set_protocol_stall(udc, ep); +			break; +		} + +		goto restart; +	} +	if (epstatus & USBA_RX_SETUP) { +		union { +			struct usb_ctrlrequest crq; +			unsigned long data[2]; +		} crq; +		unsigned int pkt_len; +		int ret; + +		if (ep->state != WAIT_FOR_SETUP) { +			/* +			 * Didn't expect a SETUP packet at this +			 * point. Clean up any pending requests (which +			 * may be successful). +			 */ +			int status = -EPROTO; + +			/* +			 * RXRDY and TXCOMP are dropped when SETUP +			 * packets arrive.  Just pretend we received +			 * the status packet. +			 */ +			if (ep->state == STATUS_STAGE_OUT || +			    ep->state == STATUS_STAGE_IN) { +				usba_ep_writel(ep, CTL_DIS, USBA_RX_BK_RDY); +				status = 0; +			} + +			if (req) { +				list_del_init(&req->queue); +				request_complete(ep, req, status); +			} +		} + +		pkt_len = USBA_BFEXT(BYTE_COUNT, usba_ep_readl(ep, STA)); +		DBG(DBG_HW, "Packet length: %u\n", pkt_len); +		if (pkt_len != sizeof(crq)) { +			DBG(DBG_ALL, "udc: Invalid length %u (expected %zu)\n", +			    pkt_len, sizeof(crq)); +			set_protocol_stall(udc, ep); +			return; +		} + +		DBG(DBG_FIFO, "Copying ctrl request from 0x%p:\n", ep->fifo); +		memcpy(crq.data, ep->fifo, sizeof(crq)); + +		/* Free up one bank in the FIFO so that we can +		 * generate or receive a reply right away. */ +		usba_ep_writel(ep, CLR_STA, USBA_RX_SETUP); + +		if (crq.crq.bRequestType & USB_DIR_IN) { +			/* +			 * The USB 2.0 spec states that "if wLength is +			 * zero, there is no data transfer phase." +			 * However, testusb #14 seems to actually +			 * expect a data phase even if wLength = 0... +			 */ +			ep->state = DATA_STAGE_IN; +		} else { +			if (crq.crq.wLength != cpu_to_le16(0)) +				ep->state = DATA_STAGE_OUT; +			else +				ep->state = STATUS_STAGE_IN; +		} + +		ret = -1; +		if (ep->index == 0) { +			ret = handle_ep0_setup(udc, ep, &crq.crq); +		} else { +			spin_unlock(&udc->lock); +			ret = udc->driver->setup(&udc->gadget, &crq.crq); +			spin_lock(&udc->lock); +		} + +		DBG(DBG_BUS, "req %02x.%02x, length %d, state %d, ret %d\n", +		    crq.crq.bRequestType, crq.crq.bRequest, +		    le16_to_cpu(crq.crq.wLength), ep->state, ret); + +		if (ret < 0) { +			/* Let the host know that we failed */ +			set_protocol_stall(udc, ep); +		} +	} +} + +static void usba_ep_irq(struct usba_udc *udc, struct usba_ep *ep) +{ +	struct usba_request *req; +	u32 epstatus; +	u32 epctrl; + +	epstatus = usba_ep_readl(ep, STA); +	epctrl = usba_ep_readl(ep, CTL); + +	DBG(DBG_INT, "%s: interrupt, status: 0x%08x\n", ep->ep.name, epstatus); + +	while ((epctrl & USBA_TX_PK_RDY) && !(epstatus & USBA_TX_PK_RDY)) { +		DBG(DBG_BUS, "%s: TX PK ready\n", ep->ep.name); + +		if (list_empty(&ep->queue)) { +			DBG(DBG_INT, "ep_irq: queue empty\n"); +			usba_ep_writel(ep, CTL_DIS, USBA_TX_PK_RDY); +			return; +		} + +		req = list_entry(ep->queue.next, struct usba_request, queue); + +		if (req->submitted) +			next_fifo_transaction(ep, req); +		else +			submit_request(ep, req); + +		if (req->last_transaction) { +			list_del_init(&req->queue); +			submit_next_request(ep); +			request_complete(ep, req, 0); +		} + +		epstatus = usba_ep_readl(ep, STA); +		epctrl = usba_ep_readl(ep, CTL); +	} + +	if ((epstatus & epctrl) & USBA_RX_BK_RDY) { +		DBG(DBG_BUS, "%s: RX data ready\n", ep->ep.name); +		receive_data(ep); +		usba_ep_writel(ep, CLR_STA, USBA_RX_BK_RDY); +	} +} + +static int usba_udc_irq(struct usba_udc *udc) +{ +	u32 status, ep_status; + +	spin_lock(&udc->lock); + +	status = usba_readl(udc, INT_STA); +	DBG(DBG_INT, "irq, status=%#08x\n", status); + +	if (status & USBA_DET_SUSPEND) { +		usba_writel(udc, INT_CLR, USBA_DET_SUSPEND); +		DBG(DBG_BUS, "Suspend detected\n"); +		if (udc->gadget.speed != USB_SPEED_UNKNOWN && +		    udc->driver && udc->driver->suspend) { +			spin_unlock(&udc->lock); +			udc->driver->suspend(&udc->gadget); +			spin_lock(&udc->lock); +		} +	} + +	if (status & USBA_WAKE_UP) { +		usba_writel(udc, INT_CLR, USBA_WAKE_UP); +		DBG(DBG_BUS, "Wake Up CPU detected\n"); +	} + +	if (status & USBA_END_OF_RESUME) { +		usba_writel(udc, INT_CLR, USBA_END_OF_RESUME); +		DBG(DBG_BUS, "Resume detected\n"); +		if (udc->gadget.speed != USB_SPEED_UNKNOWN && +		    udc->driver && udc->driver->resume) { +			spin_unlock(&udc->lock); +			udc->driver->resume(&udc->gadget); +			spin_lock(&udc->lock); +		} +	} + +	ep_status = USBA_BFEXT(EPT_INT, status); +	if (ep_status) { +		int i; + +		for (i = 0; i < USBA_NR_ENDPOINTS; i++) +			if (ep_status & (1 << i)) { +				if (ep_is_control(&udc->usba_ep[i])) +					usba_control_irq(udc, &udc->usba_ep[i]); +				else +					usba_ep_irq(udc, &udc->usba_ep[i]); +			} +	} + +	if (status & USBA_END_OF_RESET) { +		struct usba_ep *ep0; + +		usba_writel(udc, INT_CLR, USBA_END_OF_RESET); +		reset_all_endpoints(udc); + +		if (udc->gadget.speed != USB_SPEED_UNKNOWN && +		    udc->driver->disconnect) { +			udc->gadget.speed = USB_SPEED_UNKNOWN; +			spin_unlock(&udc->lock); +			udc->driver->disconnect(&udc->gadget); +			spin_lock(&udc->lock); +		} + +		if (status & USBA_HIGH_SPEED) +			udc->gadget.speed = USB_SPEED_HIGH; +		else +			udc->gadget.speed = USB_SPEED_FULL; + +		ep0 = &udc->usba_ep[0]; +		ep0->desc = &usba_ep0_desc; +		ep0->state = WAIT_FOR_SETUP; +		usba_ep_writel(ep0, CFG, +			       (USBA_BF(EPT_SIZE, EP0_EPT_SIZE) +				| USBA_BF(EPT_TYPE, USBA_EPT_TYPE_CONTROL) +				| USBA_BF(BK_NUMBER, USBA_BK_NUMBER_ONE))); +		usba_ep_writel(ep0, CTL_ENB, +			       USBA_EPT_ENABLE | USBA_RX_SETUP); +		usba_writel(udc, INT_ENB, +			    (usba_readl(udc, INT_ENB) +			     | USBA_BF(EPT_INT, 1) +			     | USBA_DET_SUSPEND +			     | USBA_END_OF_RESUME)); + +		/* +		 * Unclear why we hit this irregularly, e.g. in usbtest, +		 * but it's clearly harmless... +		 */ +		if (!(usba_ep_readl(ep0, CFG) & USBA_EPT_MAPPED)) +			DBG(DBG_ALL, "ODD: EP0 configuration is invalid!\n"); +	} + +	spin_unlock(&udc->lock); + +	return 0; +} + +static int atmel_usba_start(struct usba_udc *udc) +{ +	udc->devstatus = 1 << USB_DEVICE_SELF_POWERED; + +	udc->vbus_prev = 0; + +	/* If Vbus is present, enable the controller and wait for reset */ +	if (vbus_is_present(udc) && udc->vbus_prev == 0) { +		usba_writel(udc, CTRL, USBA_ENABLE_MASK); +		usba_writel(udc, INT_ENB, USBA_END_OF_RESET); +	} + +	return 0; +} + +static int atmel_usba_stop(struct usba_udc *udc) +{ +	udc->gadget.speed = USB_SPEED_UNKNOWN; +	reset_all_endpoints(udc); + +	/* This will also disable the DP pullup */ +	usba_writel(udc, CTRL, USBA_DISABLE_MASK); + +	return 0; +} + +static struct usba_udc controller = { +	.regs = (unsigned *)ATMEL_BASE_UDPHS, +	.fifo = (unsigned *)ATMEL_BASE_UDPHS_FIFO, +	.gadget = { +		.ops		= &usba_udc_ops, +		.ep_list	= LIST_HEAD_INIT(controller.gadget.ep_list), +		.speed		= USB_SPEED_HIGH, +		.is_dualspeed	= 1, +		.name		= "atmel_usba_udc", +	}, +}; + +int usb_gadget_handle_interrupts(void) +{ +	struct usba_udc *udc = &controller; + +	return usba_udc_irq(udc); +} + + +int usb_gadget_register_driver(struct usb_gadget_driver *driver) +{ +	struct usba_udc *udc = &controller; +	int ret; + +	if (!driver || !driver->bind || !driver->setup) { +		printf("bad paramter\n"); +		return -EINVAL; +	} + +	if (udc->driver) { +		printf("UDC already has a gadget driver\n"); +		return -EBUSY; +	} + +	atmel_usba_start(udc); + +	udc->driver = driver; + +	ret = driver->bind(&udc->gadget); +	if (ret) { +		error("driver->bind() returned %d\n", ret); +		udc->driver = NULL; +	} + +	return ret; +} + +int usb_gadget_unregister_driver(struct usb_gadget_driver *driver) +{ +	struct usba_udc *udc = &controller; + +	if (!driver || !driver->unbind || !driver->disconnect) { +		error("bad paramter\n"); +		return -EINVAL; +	} + +	driver->disconnect(&udc->gadget); +	driver->unbind(&udc->gadget); +	udc->driver = NULL; + +	atmel_usba_stop(udc); + +	return 0; +} + +static struct usba_ep *usba_udc_pdata(struct usba_platform_data *pdata, +				      struct usba_udc *udc) +{ +	struct usba_ep *eps; +	int i; + +	eps = malloc(sizeof(struct usba_ep) * pdata->num_ep); +	if (!eps) { +		error("failed to alloc eps\n"); +		return NULL; +	} + +	udc->gadget.ep0 = &eps[0].ep; + +	INIT_LIST_HEAD(&udc->gadget.ep_list); +	INIT_LIST_HEAD(&eps[0].ep.ep_list); + +	for (i = 0; i < pdata->num_ep; i++) { +		struct usba_ep *ep = &eps[i]; + +		ep->ep_regs = udc->regs + USBA_EPT_BASE(i); +		ep->dma_regs = udc->regs + USBA_DMA_BASE(i); +		ep->fifo = udc->fifo + USBA_FIFO_BASE(i); +		ep->ep.ops = &usba_ep_ops; +		ep->ep.name = pdata->ep[i].name; +		ep->ep.maxpacket = pdata->ep[i].fifo_size; +		ep->fifo_size = ep->ep.maxpacket; +		ep->udc = udc; +		INIT_LIST_HEAD(&ep->queue); +		ep->nr_banks = pdata->ep[i].nr_banks; +		ep->index = pdata->ep[i].index; +		ep->can_dma = pdata->ep[i].can_dma; +		ep->can_isoc = pdata->ep[i].can_isoc; +		if (i) +			list_add_tail(&ep->ep.ep_list, &udc->gadget.ep_list); +	}; + +	return eps; +} + +int usba_udc_probe(struct usba_platform_data *pdata) +{ +	struct usba_udc *udc; + +	udc = &controller; + +	udc->usba_ep = usba_udc_pdata(pdata, udc); + +	return 0; +} diff --git a/drivers/usb/gadget/atmel_usba_udc.h b/drivers/usb/gadget/atmel_usba_udc.h new file mode 100644 index 000000000..92e462db6 --- /dev/null +++ b/drivers/usb/gadget/atmel_usba_udc.h @@ -0,0 +1,326 @@ +/* + * Register definition for Atmel USBA high speed USB device controller + * [Original from Linux kernel: drivers/usb/gadget/atmel_usba_udc.h] + * + * Copyright (C) 2005-2013 Atmel Corporation + *			   Bo Shen <voice.shen@atmel.com> + * + * SPDX-License-Identifier:     GPL-2.0+ + */ + +#ifndef __LINUX_USB_GADGET_USBA_UDC_H__ +#define __LINUX_USB_GADGET_USBA_UDC_H__ + +/* USB register offsets */ +#define USBA_CTRL				0x0000 +#define USBA_FNUM				0x0004 +#define USBA_INT_ENB				0x0010 +#define USBA_INT_STA				0x0014 +#define USBA_INT_CLR				0x0018 +#define USBA_EPT_RST				0x001c +#define USBA_TST				0x00e0 + +/* USB endpoint register offsets */ +#define USBA_EPT_CFG				0x0000 +#define USBA_EPT_CTL_ENB			0x0004 +#define USBA_EPT_CTL_DIS			0x0008 +#define USBA_EPT_CTL				0x000c +#define USBA_EPT_SET_STA			0x0014 +#define USBA_EPT_CLR_STA			0x0018 +#define USBA_EPT_STA				0x001c + +/* USB DMA register offsets */ +#define USBA_DMA_NXT_DSC			0x0000 +#define USBA_DMA_ADDRESS			0x0004 +#define USBA_DMA_CONTROL			0x0008 +#define USBA_DMA_STATUS				0x000c + +/* Bitfields in CTRL */ +#define USBA_DEV_ADDR_OFFSET			0 +#define USBA_DEV_ADDR_SIZE			7 +#define USBA_FADDR_EN				(1 <<  7) +#define USBA_EN_USBA				(1 <<  8) +#define USBA_DETACH				(1 <<  9) +#define USBA_REMOTE_WAKE_UP			(1 << 10) +#define USBA_PULLD_DIS				(1 << 11) + +#if defined(CONFIG_AVR32) +#define USBA_ENABLE_MASK			USBA_EN_USBA +#define USBA_DISABLE_MASK			0 +#elif defined(CONFIG_AT91FAMILY) +#define USBA_ENABLE_MASK			(USBA_EN_USBA | USBA_PULLD_DIS) +#define USBA_DISABLE_MASK			USBA_DETACH +#endif /* CONFIG_ARCH_AT91 */ + +/* Bitfields in FNUM */ +#define USBA_MICRO_FRAME_NUM_OFFSET		0 +#define USBA_MICRO_FRAME_NUM_SIZE		3 +#define USBA_FRAME_NUMBER_OFFSET		3 +#define USBA_FRAME_NUMBER_SIZE			11 +#define USBA_FRAME_NUM_ERROR			(1 << 31) + +/* Bitfields in INT_ENB/INT_STA/INT_CLR */ +#define USBA_HIGH_SPEED				(1 <<  0) +#define USBA_DET_SUSPEND			(1 <<  1) +#define USBA_MICRO_SOF				(1 <<  2) +#define USBA_SOF				(1 <<  3) +#define USBA_END_OF_RESET			(1 <<  4) +#define USBA_WAKE_UP				(1 <<  5) +#define USBA_END_OF_RESUME			(1 <<  6) +#define USBA_UPSTREAM_RESUME			(1 <<  7) +#define USBA_EPT_INT_OFFSET			8 +#define USBA_EPT_INT_SIZE			16 +#define USBA_DMA_INT_OFFSET			24 +#define USBA_DMA_INT_SIZE			8 + +/* Bitfields in EPT_RST */ +#define USBA_RST_OFFSET				0 +#define USBA_RST_SIZE				16 + +/* Bitfields in USBA_TST */ +#define USBA_SPEED_CFG_OFFSET			0 +#define USBA_SPEED_CFG_SIZE			2 +#define USBA_TST_J_MODE				(1 <<  2) +#define USBA_TST_K_MODE				(1 <<  3) +#define USBA_TST_PKT_MODE			(1 <<  4) +#define USBA_OPMODE2				(1 <<  5) + +/* Bitfields in EPT_CFG */ +#define USBA_EPT_SIZE_OFFSET			0 +#define USBA_EPT_SIZE_SIZE			3 +#define USBA_EPT_DIR_IN				(1 <<  3) +#define USBA_EPT_TYPE_OFFSET			4 +#define USBA_EPT_TYPE_SIZE			2 +#define USBA_BK_NUMBER_OFFSET			6 +#define USBA_BK_NUMBER_SIZE			2 +#define USBA_NB_TRANS_OFFSET			8 +#define USBA_NB_TRANS_SIZE			2 +#define USBA_EPT_MAPPED				(1 << 31) + +/* Bitfields in EPT_CTL/EPT_CTL_ENB/EPT_CTL_DIS */ +#define USBA_EPT_ENABLE				(1 <<  0) +#define USBA_AUTO_VALID				(1 <<  1) +#define USBA_INTDIS_DMA				(1 <<  3) +#define USBA_NYET_DIS				(1 <<  4) +#define USBA_DATAX_RX				(1 <<  6) +#define USBA_MDATA_RX				(1 <<  7) +/* Bits 8-15 and 31 enable interrupts for respective bits in EPT_STA */ +#define USBA_BUSY_BANK_IE			(1 << 18) + +/* Bitfields in EPT_SET_STA/EPT_CLR_STA/EPT_STA */ +#define USBA_FORCE_STALL			(1 <<  5) +#define USBA_TOGGLE_CLR				(1 <<  6) +#define USBA_TOGGLE_SEQ_OFFSET			6 +#define USBA_TOGGLE_SEQ_SIZE			2 +#define USBA_ERR_OVFLW				(1 <<  8) +#define USBA_RX_BK_RDY				(1 <<  9) +#define USBA_KILL_BANK				(1 <<  9) +#define USBA_TX_COMPLETE			(1 << 10) +#define USBA_TX_PK_RDY				(1 << 11) +#define USBA_ISO_ERR_TRANS			(1 << 11) +#define USBA_RX_SETUP				(1 << 12) +#define USBA_ISO_ERR_FLOW			(1 << 12) +#define USBA_STALL_SENT				(1 << 13) +#define USBA_ISO_ERR_CRC			(1 << 13) +#define USBA_ISO_ERR_NBTRANS			(1 << 13) +#define USBA_NAK_IN				(1 << 14) +#define USBA_ISO_ERR_FLUSH			(1 << 14) +#define USBA_NAK_OUT				(1 << 15) +#define USBA_CURRENT_BANK_OFFSET		16 +#define USBA_CURRENT_BANK_SIZE			2 +#define USBA_BUSY_BANKS_OFFSET			18 +#define USBA_BUSY_BANKS_SIZE			2 +#define USBA_BYTE_COUNT_OFFSET			20 +#define USBA_BYTE_COUNT_SIZE			11 +#define USBA_SHORT_PACKET			(1 << 31) + +/* Bitfields in DMA_CONTROL */ +#define USBA_DMA_CH_EN				(1 <<  0) +#define USBA_DMA_LINK				(1 <<  1) +#define USBA_DMA_END_TR_EN			(1 <<  2) +#define USBA_DMA_END_BUF_EN			(1 <<  3) +#define USBA_DMA_END_TR_IE			(1 <<  4) +#define USBA_DMA_END_BUF_IE			(1 <<  5) +#define USBA_DMA_DESC_LOAD_IE			(1 <<  6) +#define USBA_DMA_BURST_LOCK			(1 <<  7) +#define USBA_DMA_BUF_LEN_OFFSET			16 +#define USBA_DMA_BUF_LEN_SIZE			16 + +/* Bitfields in DMA_STATUS */ +#define USBA_DMA_CH_ACTIVE			(1 <<  1) +#define USBA_DMA_END_TR_ST			(1 <<  4) +#define USBA_DMA_END_BUF_ST			(1 <<  5) +#define USBA_DMA_DESC_LOAD_ST			(1 <<  6) + +/* Constants for SPEED_CFG */ +#define USBA_SPEED_CFG_NORMAL			0 +#define USBA_SPEED_CFG_FORCE_HIGH		2 +#define USBA_SPEED_CFG_FORCE_FULL		3 + +/* Constants for EPT_SIZE */ +#define USBA_EPT_SIZE_8				0 +#define USBA_EPT_SIZE_16			1 +#define USBA_EPT_SIZE_32			2 +#define USBA_EPT_SIZE_64			3 +#define USBA_EPT_SIZE_128			4 +#define USBA_EPT_SIZE_256			5 +#define USBA_EPT_SIZE_512			6 +#define USBA_EPT_SIZE_1024			7 + +/* Constants for EPT_TYPE */ +#define USBA_EPT_TYPE_CONTROL			0 +#define USBA_EPT_TYPE_ISO			1 +#define USBA_EPT_TYPE_BULK			2 +#define USBA_EPT_TYPE_INT			3 + +/* Constants for BK_NUMBER */ +#define USBA_BK_NUMBER_ZERO			0 +#define USBA_BK_NUMBER_ONE			1 +#define USBA_BK_NUMBER_DOUBLE			2 +#define USBA_BK_NUMBER_TRIPLE			3 + +/* Bit manipulation macros */ +#define USBA_BF(name, value)					\ +	(((value) & ((1 << USBA_##name##_SIZE) - 1))		\ +	 << USBA_##name##_OFFSET) +#define USBA_BFEXT(name, value)					\ +	(((value) >> USBA_##name##_OFFSET)			\ +	 & ((1 << USBA_##name##_SIZE) - 1)) +#define USBA_BFINS(name, value, old)				\ +	(((old) & ~(((1 << USBA_##name##_SIZE) - 1)		\ +		    << USBA_##name##_OFFSET))			\ +	 | USBA_BF(name, value)) + +/* Register access macros */ +#define usba_readl(udc, reg)					\ +	__raw_readl((udc)->regs + USBA_##reg) +#define usba_writel(udc, reg, value)				\ +	__raw_writel((value), (udc)->regs + USBA_##reg) +#define usba_ep_readl(ep, reg)					\ +	__raw_readl((ep)->ep_regs + USBA_EPT_##reg) +#define usba_ep_writel(ep, reg, value)				\ +	__raw_writel((value), (ep)->ep_regs + USBA_EPT_##reg) +#define usba_dma_readl(ep, reg)					\ +	__raw_readl((ep)->dma_regs + USBA_DMA_##reg) +#define usba_dma_writel(ep, reg, value)				\ +	__raw_writel((value), (ep)->dma_regs + USBA_DMA_##reg) + +/* Calculate base address for a given endpoint or DMA controller */ +#define USBA_EPT_BASE(x)	(0x100 + (x) * 0x20) +#define USBA_DMA_BASE(x)	(0x300 + (x) * 0x10) +#define USBA_FIFO_BASE(x)	((x) << 16) + +/* Synth parameters */ +#define USBA_NR_ENDPOINTS	7 + +#define EP0_FIFO_SIZE		64 +#define EP0_EPT_SIZE		USBA_EPT_SIZE_64 +#define EP0_NR_BANKS		1 + +#define DBG_ERR		0x0001	/* report all error returns */ +#define DBG_HW		0x0002	/* debug hardware initialization */ +#define DBG_GADGET	0x0004	/* calls to/from gadget driver */ +#define DBG_INT		0x0008	/* interrupts */ +#define DBG_BUS		0x0010	/* report changes in bus state */ +#define DBG_QUEUE	0x0020  /* debug request queue processing */ +#define DBG_FIFO	0x0040  /* debug FIFO contents */ +#define DBG_DMA		0x0080  /* debug DMA handling */ +#define DBG_REQ		0x0100	/* print out queued request length */ +#define DBG_ALL		0xffff +#define DBG_NONE	0x0000 + +#define DEBUG_LEVEL	(DBG_ERR) + +#define DBG(level, fmt, ...)					\ +	do {							\ +		if ((level) & DEBUG_LEVEL)			\ +			debug("udc: " fmt, ## __VA_ARGS__);	\ +	} while (0) + +enum usba_ctrl_state { +	WAIT_FOR_SETUP, +	DATA_STAGE_IN, +	DATA_STAGE_OUT, +	STATUS_STAGE_IN, +	STATUS_STAGE_OUT, +	STATUS_STAGE_ADDR, +	STATUS_STAGE_TEST, +}; + +struct usba_dma_desc { +	dma_addr_t next; +	dma_addr_t addr; +	u32 ctrl; +}; + +struct usba_ep { +	int					state; +	void					*ep_regs; +	void					*dma_regs; +	void					*fifo; +	struct usb_ep				ep; +	struct usba_udc				*udc; + +	struct list_head			queue; + +	u16					fifo_size; +	u8					nr_banks; +	u8					index; +	unsigned int				can_dma:1; +	unsigned int				can_isoc:1; +	unsigned int				is_isoc:1; +	unsigned int				is_in:1; + +	const struct usb_endpoint_descriptor	*desc; +}; + +struct usba_request { +	struct usb_request			req; +	struct list_head			queue; + +	u32					ctrl; + +	unsigned int				submitted:1; +	unsigned int				last_transaction:1; +	unsigned int				using_dma:1; +	unsigned int				mapped:1; +}; + +struct usba_udc { +	void *regs; +	void *fifo; + +	struct usb_gadget gadget; +	struct usb_gadget_driver *driver; +	struct platform_device *pdev; +	int irq; +	int vbus_pin; +	int vbus_pin_inverted; +	int num_ep; +	struct usba_ep *usba_ep; + +	u16 devstatus; + +	u16 test_mode; +	int vbus_prev; +}; + +static inline struct usba_ep *to_usba_ep(struct usb_ep *ep) +{ +	return container_of(ep, struct usba_ep, ep); +} + +static inline struct usba_request *to_usba_req(struct usb_request *req) +{ +	return container_of(req, struct usba_request, req); +} + +static inline struct usba_udc *to_usba_udc(struct usb_gadget *gadget) +{ +	return container_of(gadget, struct usba_udc, gadget); +} + +#define ep_is_control(ep)	((ep)->index == 0) +#define ep_is_idle(ep)		((ep)->state == EP_STATE_IDLE) + +#endif /* __LINUX_USB_GADGET_USBA_UDC_H */ diff --git a/drivers/usb/gadget/config.c b/drivers/usb/gadget/config.c index f563afe78..014a6791c 100644 --- a/drivers/usb/gadget/config.c +++ b/drivers/usb/gadget/config.c @@ -10,6 +10,7 @@   */  #include <common.h> +#include <asm/unaligned.h>  #include <asm/errno.h>  #include <linux/list.h>  #include <linux/string.h> @@ -86,7 +87,8 @@ int usb_gadget_config_buf(  	/* config descriptor first */  	if (length < USB_DT_CONFIG_SIZE || !desc)  		return -EINVAL; -	*cp = *config; +	/* config need not be aligned */ +	memcpy(cp, config, sizeof(*cp));  	/* then interface/endpoint/class/vendor/... */  	len = usb_descriptor_fillbuf(USB_DT_CONFIG_SIZE + (u8 *)buf, @@ -100,7 +102,7 @@ int usb_gadget_config_buf(  	/* patch up the config descriptor */  	cp->bLength = USB_DT_CONFIG_SIZE;  	cp->bDescriptorType = USB_DT_CONFIG; -	cp->wTotalLength = cpu_to_le16(len); +	put_unaligned_le16(len, &cp->wTotalLength);  	cp->bmAttributes |= USB_CONFIG_ATT_ONE;  	return len;  } diff --git a/drivers/usb/gadget/ether.c b/drivers/usb/gadget/ether.c index 579893cbf..700d5fbfb 100644 --- a/drivers/usb/gadget/ether.c +++ b/drivers/usb/gadget/ether.c @@ -849,9 +849,10 @@ static struct usb_gadget_strings	stringtab = {  };  /*============================================================================*/ -static u8 control_req[USB_BUFSIZ]; +DEFINE_CACHE_ALIGN_BUFFER(u8, control_req, USB_BUFSIZ); +  #if defined(CONFIG_USB_ETH_CDC) || defined(CONFIG_USB_ETH_RNDIS) -static u8 status_req[STATUS_BYTECOUNT] __attribute__ ((aligned(4))); +DEFINE_CACHE_ALIGN_BUFFER(u8, status_req, STATUS_BYTECOUNT);  #endif diff --git a/drivers/usb/gadget/f_dfu.h b/drivers/usb/gadget/f_dfu.h index 34a4dde5a..cc2c45567 100644 --- a/drivers/usb/gadget/f_dfu.h +++ b/drivers/usb/gadget/f_dfu.h @@ -82,7 +82,4 @@ struct dfu_function_descriptor {  	__le16				wTransferSize;  	__le16				bcdDFUVersion;  } __packed; - -/* configuration-specific linkup */ -int dfu_add(struct usb_configuration *c);  #endif /* __F_DFU_H_ */ diff --git a/drivers/usb/gadget/g_dnl.c b/drivers/usb/gadget/g_dnl.c index a3e05a872..40868c034 100644 --- a/drivers/usb/gadget/g_dnl.c +++ b/drivers/usb/gadget/g_dnl.c @@ -7,7 +7,6 @@   * SPDX-License-Identifier:	GPL-2.0+   */ -#include <errno.h>  #include <common.h>  #include <malloc.h> @@ -15,11 +14,11 @@  #include <part.h>  #include <g_dnl.h> -#include "f_dfu.h" +#include <usb_mass_storage.h> +#include <dfu.h>  #include "gadget_chips.h"  #include "composite.c" -#include "f_mass_storage.c"  /*   * One needs to define the following: diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c index fdad73972..3ae04c025 100644 --- a/drivers/usb/host/ehci-hcd.c +++ b/drivers/usb/host/ehci-hcd.c @@ -36,6 +36,12 @@  #define CONFIG_USB_MAX_CONTROLLER_COUNT 1  #endif +/* + * EHCI spec page 20 says that the HC may take up to 16 uFrames (= 4ms) to halt. + * Let's time out after 8 to have a little safety margin on top of that. + */ +#define HCHALT_TIMEOUT (8 * 1000) +  static struct ehci_ctrl ehcic[CONFIG_USB_MAX_CONTROLLER_COUNT];  #define ALIGN_END_ADDR(type, ptr, size)			\ @@ -190,6 +196,36 @@ out:  	return ret;  } +static int ehci_shutdown(struct ehci_ctrl *ctrl) +{ +	int i, ret = 0; +	uint32_t cmd, reg; + +	cmd = ehci_readl(&ctrl->hcor->or_usbcmd); +	cmd &= ~(CMD_PSE | CMD_ASE); +	ehci_writel(&ctrl->hcor->or_usbcmd, cmd); +	ret = handshake(&ctrl->hcor->or_usbsts, STS_ASS | STS_PSS, 0, +		100 * 1000); + +	if (!ret) { +		for (i = 0; i < CONFIG_SYS_USB_EHCI_MAX_ROOT_PORTS; i++) { +			reg = ehci_readl(&ctrl->hcor->or_portsc[i]); +			reg |= EHCI_PS_SUSP; +			ehci_writel(&ctrl->hcor->or_portsc[i], reg); +		} + +		cmd &= ~CMD_RUN; +		ehci_writel(&ctrl->hcor->or_usbcmd, cmd); +		ret = handshake(&ctrl->hcor->or_usbsts, STS_HALT, STS_HALT, +			HCHALT_TIMEOUT); +	} + +	if (ret) +		puts("EHCI failed to shut down host controller.\n"); + +	return ret; +} +  static int ehci_td_buffer(struct qTD *td, void *buf, size_t sz)  {  	uint32_t delta, next; @@ -808,6 +844,7 @@ ehci_submit_root(struct usb_device *dev, unsigned long pipe, void *buffer,  			}  			break;  		case USB_PORT_FEAT_TEST: +			ehci_shutdown(ctrl);  			reg &= ~(0xf << 16);  			reg |= ((le16_to_cpu(req->index) >> 8) & 0xf) << 16;  			ehci_writel(status_reg, reg); @@ -878,6 +915,7 @@ unknown:  int usb_lowlevel_stop(int index)  { +	ehci_shutdown(&ehcic[index]);  	return ehci_hcd_stop(index);  } |