drivers/usb/gadget/udc/bdc/bdc_udc.c

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

File Facts

System
Linux kernel
Corpus path
drivers/usb/gadget/udc/bdc/bdc_udc.c
Extension
.c
Size
15671 bytes
Lines
591
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 (bdc->devstatus & REMOTE_WAKEUP_ISSUED) {
			bdc->devstatus &= ~REMOTE_WAKEUP_ISSUED;
			if (bdc->gadget.speed == USB_SPEED_SUPER) {
				bdc_function_wake_fh(bdc, 0);
				bdc->devstatus |= FUNC_WAKE_ISSUED;
				/*
				 * Start a Notification timer and check if the
				 * Host transferred anything on any of the EPs,
				 * if not then send function wake again every
				 * TNotification secs until host initiates
				 * transfer to BDC, USB3 spec Table 8.13
				 */
				schedule_delayed_work(
						&bdc->func_wake_notify,
						msecs_to_jiffies(BDC_TNOTIFY));
				dev_dbg(bdc->dev, "sched func_wake_notify\n");
			}
		}
		break;

	case BDC_LINK_STATE_RESUME:
		dev_dbg(bdc->dev, "Resumed from Suspend\n");
		if (bdc->devstatus & DEVICE_SUSPENDED) {
			bdc->gadget_driver->resume(&bdc->gadget);
			bdc->devstatus &= ~DEVICE_SUSPENDED;
		}
		break;
	default:
		dev_dbg(bdc->dev, "link state:%d\n", link_state);
	}
}

/* something changes on upstream port, handle it here */
void bdc_sr_uspc(struct bdc *bdc, struct bdc_sr *sreport)
{
	u32 clear_flags = 0;
	u32 uspc;
	bool connected = false;
	bool disconn = false;

	uspc = bdc_readl(bdc->regs, BDC_USPC);
	dev_dbg(bdc->dev, "%s uspc=0x%08x\n", __func__, uspc);

	/* Port connect changed */
	if (uspc & BDC_PCC) {
		/* Vbus not present, and not connected to Downstream port */
		if ((uspc & BDC_VBC) && !(uspc & BDC_VBS) && !(uspc & BDC_PCS))
			disconn = true;
		else if ((uspc & BDC_PCS) && !BDC_PST(uspc))
			connected = true;
		clear_flags |= BDC_PCC;
	}

	/* Change in VBus and VBus is present */
	if ((uspc & BDC_VBC) && (uspc & BDC_VBS)) {
		if (bdc->pullup) {
			dev_dbg(bdc->dev, "Do a softconnect\n");
			/* Attached state, do a softconnect */
			bdc_softconn(bdc);
			usb_gadget_set_state(&bdc->gadget, USB_STATE_POWERED);
		}
		clear_flags |= BDC_VBC;
	} else if ((uspc & BDC_PRS) || (uspc & BDC_PRC) || disconn) {
		/* Hot reset, warm reset, 2.0 bus reset or disconn */
		dev_dbg(bdc->dev, "Port reset or disconn\n");
		bdc_uspc_disconnected(bdc, disconn);
		clear_flags |= BDC_PRC;
	} else if ((uspc & BDC_PSC) && (uspc & BDC_PCS)) {
		/* Change in Link state */
		handle_link_state_change(bdc, uspc);
		clear_flags |= BDC_PSC;
	}

	/*
	 * In SS we might not have PRC bit set before connection, but in 2.0
	 * the PRC bit is set before connection, so moving this condition out
	 * of bus reset to handle both SS/2.0 speeds.
	 */
	if (connected) {
		/* This is the connect event for U0/L0 */
		dev_dbg(bdc->dev, "Connected\n");
		bdc_uspc_connected(bdc);
		bdc->devstatus &= ~(DEVICE_SUSPENDED);
	}
	uspc = bdc_readl(bdc->regs, BDC_USPC);
	uspc &= (~BDC_USPSC_RW);
	dev_dbg(bdc->dev, "uspc=%x\n", uspc);
	bdc_writel(bdc->regs, BDC_USPC, clear_flags);
}

Annotation

Implementation Notes