drivers/extcon/extcon-intel-int3496.c

Source file repositories/reference/linux-study-clean/drivers/extcon/extcon-intel-int3496.c

File Facts

System
Linux kernel
Corpus path
drivers/extcon/extcon-intel-int3496.c
Extension
.c
Size
5738 bytes
Lines
218
Domain
Driver Families
Bucket
drivers/extcon
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 int3496_data {
	struct device *dev;
	struct extcon_dev *edev;
	struct delayed_work work;
	struct gpio_desc *gpio_usb_id;
	struct gpio_desc *gpio_vbus_en;
	struct gpio_desc *gpio_usb_mux;
	struct regulator *vbus_boost;
	int usb_id_irq;
	bool vbus_boost_enabled;
};

static const unsigned int int3496_cable[] = {
	EXTCON_USB_HOST,
	EXTCON_NONE,
};

static const struct acpi_gpio_params id_gpios = { INT3496_GPIO_USB_ID, 0, false };
static const struct acpi_gpio_params vbus_gpios = { INT3496_GPIO_VBUS_EN, 0, false };
static const struct acpi_gpio_params mux_gpios = { INT3496_GPIO_USB_MUX, 0, false };

static const struct acpi_gpio_mapping acpi_int3496_default_gpios[] = {
	/*
	 * Some platforms have a bug in ACPI GPIO description making IRQ
	 * GPIO to be output only. Ask the GPIO core to ignore this limit.
	 */
	{ "id-gpios", &id_gpios, 1, ACPI_GPIO_QUIRK_NO_IO_RESTRICTION },
	{ "vbus-gpios", &vbus_gpios, 1 },
	{ "mux-gpios", &mux_gpios, 1 },
	{ },
};

static void int3496_set_vbus_boost(struct int3496_data *data, bool enable)
{
	int ret;

	if (IS_ERR_OR_NULL(data->vbus_boost))
		return;

	if (data->vbus_boost_enabled == enable)
		return;

	if (enable)
		ret = regulator_enable(data->vbus_boost);
	else
		ret = regulator_disable(data->vbus_boost);

	if (ret == 0)
		data->vbus_boost_enabled = enable;
	else
		dev_err(data->dev, "Error updating Vbus boost regulator: %d\n", ret);
}

static void int3496_do_usb_id(struct work_struct *work)
{
	struct int3496_data *data =
		container_of(work, struct int3496_data, work.work);
	int id = gpiod_get_value_cansleep(data->gpio_usb_id);

	/* id == 1: PERIPHERAL, id == 0: HOST */
	dev_dbg(data->dev, "Connected %s cable\n", id ? "PERIPHERAL" : "HOST");

	/*
	 * Peripheral: set USB mux to peripheral and disable VBUS
	 * Host: set USB mux to host and enable VBUS
	 */
	if (!IS_ERR(data->gpio_usb_mux))
		gpiod_direction_output(data->gpio_usb_mux, id);

	if (!IS_ERR(data->gpio_vbus_en))
		gpiod_direction_output(data->gpio_vbus_en, !id);
	else
		int3496_set_vbus_boost(data, !id);

	extcon_set_state_sync(data->edev, EXTCON_USB_HOST, !id);
}

static irqreturn_t int3496_thread_isr(int irq, void *priv)
{
	struct int3496_data *data = priv;

	/* Let the pin settle before processing it */
	mod_delayed_work(system_percpu_wq, &data->work, DEBOUNCE_TIME);

	return IRQ_HANDLED;
}

static int int3496_probe(struct platform_device *pdev)
{
	struct device *dev = &pdev->dev;

Annotation

Implementation Notes