drivers/usb/phy/phy-gpio-vbus-usb.c

Source file repositories/reference/linux-study-clean/drivers/usb/phy/phy-gpio-vbus-usb.c

File Facts

System
Linux kernel
Corpus path
drivers/usb/phy/phy-gpio-vbus-usb.c
Extension
.c
Size
10624 bytes
Lines
396
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

struct gpio_vbus_data {
	struct gpio_desc	*vbus_gpiod;
	struct gpio_desc	*pullup_gpiod;
	struct usb_phy		phy;
	struct device          *dev;
	struct regulator       *vbus_draw;
	int			vbus_draw_enabled;
	unsigned		mA;
	struct delayed_work	work;
	int			vbus;
	int			irq;
};


/*
 * This driver relies on "both edges" triggering.  VBUS has 100 msec to
 * stabilize, so the peripheral controller driver may need to cope with
 * some bouncing due to current surges (e.g. charging local capacitance)
 * and contact chatter.
 *
 * REVISIT in desperate straits, toggling between rising and falling
 * edges might be workable.
 */
#define VBUS_IRQ_FLAGS \
	(IRQF_SHARED | IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING)


/* interface to regulator framework */
static void set_vbus_draw(struct gpio_vbus_data *gpio_vbus, unsigned mA)
{
	struct regulator *vbus_draw = gpio_vbus->vbus_draw;
	int enabled;
	int ret;

	if (!vbus_draw)
		return;

	enabled = gpio_vbus->vbus_draw_enabled;
	if (mA) {
		regulator_set_current_limit(vbus_draw, 0, 1000 * mA);
		if (!enabled) {
			ret = regulator_enable(vbus_draw);
			if (ret < 0)
				return;
			gpio_vbus->vbus_draw_enabled = 1;
		}
	} else {
		if (enabled) {
			ret = regulator_disable(vbus_draw);
			if (ret < 0)
				return;
			gpio_vbus->vbus_draw_enabled = 0;
		}
	}
	gpio_vbus->mA = mA;
}

static int is_vbus_powered(struct gpio_vbus_data *gpio_vbus)
{
	return gpiod_get_value(gpio_vbus->vbus_gpiod);
}

static void gpio_vbus_work(struct work_struct *work)
{
	struct gpio_vbus_data *gpio_vbus =
		container_of(work, struct gpio_vbus_data, work.work);
	int status, vbus;

	if (!gpio_vbus->phy.otg->gadget)
		return;

	vbus = is_vbus_powered(gpio_vbus);
	if ((vbus ^ gpio_vbus->vbus) == 0)
		return;
	gpio_vbus->vbus = vbus;

	/* Peripheral controllers which manage the pullup themselves won't have
	 * a pullup GPIO configured here.  If it's configured here, we'll do
	 * what isp1301_omap::b_peripheral() does and enable the pullup here...
	 * although that may complicate usb_gadget_{,dis}connect() support.
	 */

	if (vbus) {
		status = USB_EVENT_VBUS;
		gpio_vbus->phy.otg->state = OTG_STATE_B_PERIPHERAL;
		gpio_vbus->phy.last_event = status;
		usb_gadget_vbus_connect(gpio_vbus->phy.otg->gadget);

		/* drawing a "unit load" is *always* OK, except for OTG */
		set_vbus_draw(gpio_vbus, 100);

Annotation

Implementation Notes