drivers/usb/mtu3/mtu3_qmu.c

Source file repositories/reference/linux-study-clean/drivers/usb/mtu3/mtu3_qmu.c

File Facts

System
Linux kernel
Corpus path
drivers/usb/mtu3/mtu3_qmu.c
Extension
.c
Size
18475 bytes
Lines
687
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 (mtu3_readl(mbase, USB_QMU_TQCSR(epnum)) & QMU_Q_ACTIVE) {
			dev_warn(mtu->dev, "Tx %d Active Now!\n", epnum);
			return 0;
		}
		mtu3_writel(mbase, USB_QMU_TQCSR(epnum), QMU_Q_START);

	} else {
		write_rxq_start_addr(mbase, epnum, ring->dma);
		mtu3_setbits(mbase, MU3D_EP_RXCR0(epnum), RX_DMAREQEN);
		/* don't expect ZLP */
		mtu3_clrbits(mbase, U3D_QCR3, QMU_RX_ZLP(epnum));
		/* move to next GPD when receive ZLP */
		mtu3_setbits(mbase, U3D_QCR3, QMU_RX_COZ(epnum));
		mtu3_writel(mbase, U3D_RQERRIESR0,
				QMU_RX_LEN_ERR(epnum) | QMU_RX_CS_ERR(epnum));
		mtu3_writel(mbase, U3D_RQERRIESR1, QMU_RX_ZLP_ERR(epnum));

		if (mtu3_readl(mbase, USB_QMU_RQCSR(epnum)) & QMU_Q_ACTIVE) {
			dev_warn(mtu->dev, "Rx %d Active Now!\n", epnum);
			return 0;
		}
		mtu3_writel(mbase, USB_QMU_RQCSR(epnum), QMU_Q_START);
	}

	return 0;
}

/* may called in atomic context */
void mtu3_qmu_stop(struct mtu3_ep *mep)
{
	struct mtu3 *mtu = mep->mtu;
	void __iomem *mbase = mtu->mac_base;
	int epnum = mep->epnum;
	u32 value = 0;
	u32 qcsr;
	int ret;

	qcsr = mep->is_in ? USB_QMU_TQCSR(epnum) : USB_QMU_RQCSR(epnum);

	if (!(mtu3_readl(mbase, qcsr) & QMU_Q_ACTIVE)) {
		dev_dbg(mtu->dev, "%s's qmu is inactive now!\n", mep->name);
		return;
	}
	mtu3_writel(mbase, qcsr, QMU_Q_STOP);

	if (mep->is_in)
		mtu3_setbits(mbase, MU3D_EP_TXCR0(epnum), TX_FLUSHFIFO);

	ret = readl_poll_timeout_atomic(mbase + qcsr, value,
			!(value & QMU_Q_ACTIVE), 1, 1000);
	if (ret) {
		dev_err(mtu->dev, "stop %s's qmu failed\n", mep->name);
		return;
	}

	/* flush fifo again to make sure the fifo is empty */
	if (mep->is_in)
		mtu3_setbits(mbase, MU3D_EP_TXCR0(epnum), TX_FLUSHFIFO);

	dev_dbg(mtu->dev, "%s's qmu stop now!\n", mep->name);
}

void mtu3_qmu_flush(struct mtu3_ep *mep)
{

	dev_dbg(mep->mtu->dev, "%s flush QMU %s\n", __func__,
		((mep->is_in) ? "TX" : "RX"));

	/*Stop QMU */
	mtu3_qmu_stop(mep);
	reset_gpd_list(mep);
}

/*
 * QMU can't transfer zero length packet directly (a hardware limit
 * on old SoCs), so when needs to send ZLP, we intentionally trigger
 * a length error interrupt, and in the ISR sends a ZLP by BMU.
 */
static void qmu_tx_zlp_error_handler(struct mtu3 *mtu, u8 epnum)
{
	struct mtu3_ep *mep = mtu->in_eps + epnum;
	struct mtu3_gpd_ring *ring = &mep->gpd_ring;
	void __iomem *mbase = mtu->mac_base;
	struct qmu_gpd *gpd_current = NULL;
	struct mtu3_request *mreq;
	dma_addr_t cur_gpd_dma;
	u32 txcsr = 0;
	int ret;

	mreq = next_request(mep);

Annotation

Implementation Notes