1 // SPDX-License-Identifier: GPL-2.0
3 * Intel INT3496 ACPI device extcon driver
5 * Copyright (c) 2016 Hans de Goede <hdegoede@redhat.com>
7 * Based on android x86 kernel code which is:
9 * Copyright (c) 2014, Intel Corporation.
10 * Author: David Cohen <david.a.cohen@linux.intel.com>
13 #include <linux/acpi.h>
14 #include <linux/extcon-provider.h>
15 #include <linux/gpio/consumer.h>
16 #include <linux/interrupt.h>
17 #include <linux/module.h>
18 #include <linux/platform_device.h>
20 #define INT3496_GPIO_USB_ID 0
21 #define INT3496_GPIO_VBUS_EN 1
22 #define INT3496_GPIO_USB_MUX 2
23 #define DEBOUNCE_TIME msecs_to_jiffies(50)
27 struct extcon_dev
*edev
;
28 struct delayed_work work
;
29 struct gpio_desc
*gpio_usb_id
;
30 struct gpio_desc
*gpio_vbus_en
;
31 struct gpio_desc
*gpio_usb_mux
;
35 static const unsigned int int3496_cable
[] = {
40 static const struct acpi_gpio_params id_gpios
= { INT3496_GPIO_USB_ID
, 0, false };
41 static const struct acpi_gpio_params vbus_gpios
= { INT3496_GPIO_VBUS_EN
, 0, false };
42 static const struct acpi_gpio_params mux_gpios
= { INT3496_GPIO_USB_MUX
, 0, false };
44 static const struct acpi_gpio_mapping acpi_int3496_default_gpios
[] = {
46 * Some platforms have a bug in ACPI GPIO description making IRQ
47 * GPIO to be output only. Ask the GPIO core to ignore this limit.
49 { "id-gpios", &id_gpios
, 1, ACPI_GPIO_QUIRK_NO_IO_RESTRICTION
},
50 { "vbus-gpios", &vbus_gpios
, 1 },
51 { "mux-gpios", &mux_gpios
, 1 },
55 static void int3496_do_usb_id(struct work_struct
*work
)
57 struct int3496_data
*data
=
58 container_of(work
, struct int3496_data
, work
.work
);
59 int id
= gpiod_get_value_cansleep(data
->gpio_usb_id
);
61 /* id == 1: PERIPHERAL, id == 0: HOST */
62 dev_dbg(data
->dev
, "Connected %s cable\n", id
? "PERIPHERAL" : "HOST");
65 * Peripheral: set USB mux to peripheral and disable VBUS
66 * Host: set USB mux to host and enable VBUS
68 if (!IS_ERR(data
->gpio_usb_mux
))
69 gpiod_direction_output(data
->gpio_usb_mux
, id
);
71 if (!IS_ERR(data
->gpio_vbus_en
))
72 gpiod_direction_output(data
->gpio_vbus_en
, !id
);
74 extcon_set_state_sync(data
->edev
, EXTCON_USB_HOST
, !id
);
77 static irqreturn_t
int3496_thread_isr(int irq
, void *priv
)
79 struct int3496_data
*data
= priv
;
81 /* Let the pin settle before processing it */
82 mod_delayed_work(system_wq
, &data
->work
, DEBOUNCE_TIME
);
87 static int int3496_probe(struct platform_device
*pdev
)
89 struct device
*dev
= &pdev
->dev
;
90 struct int3496_data
*data
;
93 ret
= devm_acpi_dev_add_driver_gpios(dev
, acpi_int3496_default_gpios
);
95 dev_err(dev
, "can't add GPIO ACPI mapping\n");
99 data
= devm_kzalloc(dev
, sizeof(*data
), GFP_KERNEL
);
104 INIT_DELAYED_WORK(&data
->work
, int3496_do_usb_id
);
106 data
->gpio_usb_id
= devm_gpiod_get(dev
, "id", GPIOD_IN
);
107 if (IS_ERR(data
->gpio_usb_id
)) {
108 ret
= PTR_ERR(data
->gpio_usb_id
);
109 dev_err(dev
, "can't request USB ID GPIO: %d\n", ret
);
113 data
->usb_id_irq
= gpiod_to_irq(data
->gpio_usb_id
);
114 if (data
->usb_id_irq
< 0) {
115 dev_err(dev
, "can't get USB ID IRQ: %d\n", data
->usb_id_irq
);
116 return data
->usb_id_irq
;
119 data
->gpio_vbus_en
= devm_gpiod_get(dev
, "vbus", GPIOD_ASIS
);
120 if (IS_ERR(data
->gpio_vbus_en
))
121 dev_info(dev
, "can't request VBUS EN GPIO\n");
123 data
->gpio_usb_mux
= devm_gpiod_get(dev
, "mux", GPIOD_ASIS
);
124 if (IS_ERR(data
->gpio_usb_mux
))
125 dev_info(dev
, "can't request USB MUX GPIO\n");
127 /* register extcon device */
128 data
->edev
= devm_extcon_dev_allocate(dev
, int3496_cable
);
129 if (IS_ERR(data
->edev
))
132 ret
= devm_extcon_dev_register(dev
, data
->edev
);
134 dev_err(dev
, "can't register extcon device: %d\n", ret
);
138 ret
= devm_request_threaded_irq(dev
, data
->usb_id_irq
,
139 NULL
, int3496_thread_isr
,
140 IRQF_SHARED
| IRQF_ONESHOT
|
141 IRQF_TRIGGER_RISING
|
142 IRQF_TRIGGER_FALLING
,
143 dev_name(dev
), data
);
145 dev_err(dev
, "can't request IRQ for USB ID GPIO: %d\n", ret
);
149 /* process id-pin so that we start with the right status */
150 queue_delayed_work(system_wq
, &data
->work
, 0);
151 flush_delayed_work(&data
->work
);
153 platform_set_drvdata(pdev
, data
);
158 static int int3496_remove(struct platform_device
*pdev
)
160 struct int3496_data
*data
= platform_get_drvdata(pdev
);
162 devm_free_irq(&pdev
->dev
, data
->usb_id_irq
, data
);
163 cancel_delayed_work_sync(&data
->work
);
168 static const struct acpi_device_id int3496_acpi_match
[] = {
172 MODULE_DEVICE_TABLE(acpi
, int3496_acpi_match
);
174 static struct platform_driver int3496_driver
= {
176 .name
= "intel-int3496",
177 .acpi_match_table
= int3496_acpi_match
,
179 .probe
= int3496_probe
,
180 .remove
= int3496_remove
,
183 module_platform_driver(int3496_driver
);
185 MODULE_AUTHOR("Hans de Goede <hdegoede@redhat.com>");
186 MODULE_DESCRIPTION("Intel INT3496 ACPI device extcon driver");
187 MODULE_LICENSE("GPL v2");