drivers/usb/gadget/udc/pxa25x_udc.c

Source file repositories/reference/linux-study-clean/drivers/usb/gadget/udc/pxa25x_udc.c

File Facts

System
Linux kernel
Corpus path
drivers/usb/gadget/udc/pxa25x_udc.c
Extension
.c
Size
68264 bytes
Lines
2490
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 (is_last) {
			done (ep, req, 0);
			if (list_empty(&ep->queue))
				pio_irq_disable(ep);
			return 1;
		}

		// TODO experiment: how robust can fifo mode tweaking be?
		// double buffering is off in the default fifo mode, which
		// prevents TFS from being set here.

	} while (udc_ep_get_UDCCS(ep) & UDCCS_BI_TFS);
	return 0;
}

/* caller asserts req->pending (ep0 irq status nyet cleared); starts
 * ep0 data stage.  these chips want very simple state transitions.
 */
static inline
void ep0start(struct pxa25x_udc *dev, u32 flags, const char *tag)
{
	udc_ep0_set_UDCCS(dev, flags|UDCCS0_SA|UDCCS0_OPR);
	udc_set_reg(dev, USIR0, USIR0_IR0);
	dev->req_pending = 0;
	DBG(DBG_VERY_NOISY, "%s %s, %02x/%02x\n",
		__func__, tag, udc_ep0_get_UDCCS(dev), flags);
}

static int
write_ep0_fifo (struct pxa25x_ep *ep, struct pxa25x_request *req)
{
	struct pxa25x_udc *dev = ep->dev;
	unsigned	count;
	int		is_short;

	count = write_packet(&dev->ep[0], req, EP0_FIFO_SIZE);
	ep->dev->stats.write.bytes += count;

	/* last packet "must be" short (or a zlp) */
	is_short = (count != EP0_FIFO_SIZE);

	DBG(DBG_VERY_NOISY, "ep0in %d bytes %d left %p\n", count,
		req->req.length - req->req.actual, req);

	if (unlikely (is_short)) {
		if (ep->dev->req_pending)
			ep0start(ep->dev, UDCCS0_IPR, "short IN");
		else
			udc_ep0_set_UDCCS(dev, UDCCS0_IPR);

		count = req->req.length;
		done (ep, req, 0);
		ep0_idle(ep->dev);
#ifndef CONFIG_ARCH_IXP4XX
#if 1
		/* This seems to get rid of lost status irqs in some cases:
		 * host responds quickly, or next request involves config
		 * change automagic, or should have been hidden, or ...
		 *
		 * FIXME get rid of all udelays possible...
		 */
		if (count >= EP0_FIFO_SIZE) {
			count = 100;
			do {
				if ((udc_ep0_get_UDCCS(dev) & UDCCS0_OPR) != 0) {
					/* clear OPR, generate ack */
					udc_ep0_set_UDCCS(dev, UDCCS0_OPR);
					break;
				}
				count--;
				udelay(1);
			} while (count);
		}
#endif
#endif
	} else if (ep->dev->req_pending)
		ep0start(ep->dev, 0, "IN");
	return is_short;
}


/*
 * read_fifo -  unload packet(s) from the fifo we use for usb OUT
 * transfers and put them into the request.  caller should have made
 * sure there's at least one packet ready.
 *
 * returns true if the request completed because of short packet or the
 * request buffer having filled (and maybe overran till end-of-packet).
 */
static int

Annotation

Implementation Notes