drivers/usb/gadget/udc/aspeed-vhub/epn.c

Source file repositories/reference/linux-study-clean/drivers/usb/gadget/udc/aspeed-vhub/epn.c

File Facts

System
Linux kernel
Corpus path
drivers/usb/gadget/udc/aspeed-vhub/epn.c
Extension
.c
Size
23071 bytes
Lines
855
Domain
Driver Families
Bucket
drivers/usb
Inferred role
Driver Families: implementation source
Status
source implementation candidate

Why This File Exists

Repeatable hardware-adapter layer. Deep compatibility for every driver is out of scope; this atlas records patterns, probe lifecycles, bus glue, IRQ/DMA usage, and links back to core abstractions.

Dependency Surface

Detected Declarations

Annotated Snippet

if (ep->epn.is_in) {
			memcpy(ep->buf, req->req.buf + act, chunk);
			vhub_dma_workaround(ep->buf);
		}
		writel(ep->buf_dma, ep->epn.regs + AST_VHUB_EP_DESC_BASE);
	} else {
		if (ep->epn.is_in)
			vhub_dma_workaround(req->req.buf);
		writel(req->req.dma + act, ep->epn.regs + AST_VHUB_EP_DESC_BASE);
	}

	/* Start DMA */
	req->active = true;
	writel(VHUB_EP_DMA_SET_TX_SIZE(chunk),
	       ep->epn.regs + AST_VHUB_EP_DESC_STATUS);
	writel(VHUB_EP_DMA_SET_TX_SIZE(chunk) | VHUB_EP_DMA_SINGLE_KICK,
	       ep->epn.regs + AST_VHUB_EP_DESC_STATUS);
}

static void ast_vhub_epn_handle_ack(struct ast_vhub_ep *ep)
{
	struct ast_vhub_req *req;
	unsigned int len;
	int status = 0;
	u32 stat;

	/* Read EP status */
	stat = readl(ep->epn.regs + AST_VHUB_EP_DESC_STATUS);

	/* Grab current request if any */
	req = list_first_entry_or_null(&ep->queue, struct ast_vhub_req, queue);

	EPVDBG(ep, "ACK status=%08x is_in=%d, req=%p (active=%d)\n",
	       stat, ep->epn.is_in, req, req ? req->active : 0);

	/* In absence of a request, bail out, must have been dequeued */
	if (!req)
		return;

	/*
	 * Request not active, move on to processing queue, active request
	 * was probably dequeued
	 */
	if (!req->active)
		goto next_chunk;

	/* Check if HW has moved on */
	if (VHUB_EP_DMA_RPTR(stat) != 0) {
		EPDBG(ep, "DMA read pointer not 0 !\n");
		return;
	}

	/* No current DMA ongoing */
	req->active = false;

	/* Grab length out of HW */
	len = VHUB_EP_DMA_TX_SIZE(stat);

	/* If not using DMA, copy data out if needed */
	if (!req->req.dma && !ep->epn.is_in && len) {
		if (req->req.actual + len > req->req.length) {
			req->last_desc = 1;
			status = -EOVERFLOW;
			goto done;
		} else {
			memcpy(req->req.buf + req->req.actual, ep->buf, len);
		}
	}
	/* Adjust size */
	req->req.actual += len;

	/* Check for short packet */
	if (len < ep->ep.maxpacket)
		req->last_desc = 1;

done:
	/* That's it ? complete the request and pick a new one */
	if (req->last_desc >= 0) {
		ast_vhub_done(ep, req, status);
		req = list_first_entry_or_null(&ep->queue, struct ast_vhub_req,
					       queue);

		/*
		 * Due to lock dropping inside "done" the next request could
		 * already be active, so check for that and bail if needed.
		 */
		if (!req || req->active)
			return;
	}

Annotation

Implementation Notes