1 // SPDX-License-Identifier: GPL-2.0 2 /* 3 * Intel INT3496 ACPI device extcon driver 4 * 5 * Copyright (c) 2016 Hans de Goede <hdegoede@redhat.com> 6 * 7 * Based on android x86 kernel code which is: 8 * 9 * Copyright (c) 2014, Intel Corporation. 10 * Author: David Cohen <david.a.cohen@linux.intel.com> 11 */ 12 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> 19 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) 24 25 struct int3496_data { 26 struct device *dev; 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; 32 int usb_id_irq; 33 }; 34 35 static const unsigned int int3496_cable[] = { 36 EXTCON_USB_HOST, 37 EXTCON_NONE, 38 }; 39 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 }; 43 44 static const struct acpi_gpio_mapping acpi_int3496_default_gpios[] = { 45 /* 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. 48 */ 49 { "id-gpios", &id_gpios, 1, ACPI_GPIO_QUIRK_NO_IO_RESTRICTION }, 50 { "vbus-gpios", &vbus_gpios, 1 }, 51 { "mux-gpios", &mux_gpios, 1 }, 52 { }, 53 }; 54 55 static void int3496_do_usb_id(struct work_struct *work) 56 { 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); 60 61 /* id == 1: PERIPHERAL, id == 0: HOST */ 62 dev_dbg(data->dev, "Connected %s cable\n", id ? "PERIPHERAL" : "HOST"); 63 64 /* 65 * Peripheral: set USB mux to peripheral and disable VBUS 66 * Host: set USB mux to host and enable VBUS 67 */ 68 if (!IS_ERR(data->gpio_usb_mux)) 69 gpiod_direction_output(data->gpio_usb_mux, id); 70 71 if (!IS_ERR(data->gpio_vbus_en)) 72 gpiod_direction_output(data->gpio_vbus_en, !id); 73 74 extcon_set_state_sync(data->edev, EXTCON_USB_HOST, !id); 75 } 76 77 static irqreturn_t int3496_thread_isr(int irq, void *priv) 78 { 79 struct int3496_data *data = priv; 80 81 /* Let the pin settle before processing it */ 82 mod_delayed_work(system_wq, &data->work, DEBOUNCE_TIME); 83 84 return IRQ_HANDLED; 85 } 86 87 static int int3496_probe(struct platform_device *pdev) 88 { 89 struct device *dev = &pdev->dev; 90 struct int3496_data *data; 91 int ret; 92 93 ret = devm_acpi_dev_add_driver_gpios(dev, acpi_int3496_default_gpios); 94 if (ret) { 95 dev_err(dev, "can't add GPIO ACPI mapping\n"); 96 return ret; 97 } 98 99 data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL); 100 if (!data) 101 return -ENOMEM; 102 103 data->dev = dev; 104 INIT_DELAYED_WORK(&data->work, int3496_do_usb_id); 105 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); 110 return ret; 111 } 112 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; 117 } 118 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"); 122 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"); 126 127 /* register extcon device */ 128 data->edev = devm_extcon_dev_allocate(dev, int3496_cable); 129 if (IS_ERR(data->edev)) 130 return -ENOMEM; 131 132 ret = devm_extcon_dev_register(dev, data->edev); 133 if (ret < 0) { 134 dev_err(dev, "can't register extcon device: %d\n", ret); 135 return ret; 136 } 137 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); 144 if (ret < 0) { 145 dev_err(dev, "can't request IRQ for USB ID GPIO: %d\n", ret); 146 return ret; 147 } 148 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); 152 153 platform_set_drvdata(pdev, data); 154 155 return 0; 156 } 157 158 static int int3496_remove(struct platform_device *pdev) 159 { 160 struct int3496_data *data = platform_get_drvdata(pdev); 161 162 devm_free_irq(&pdev->dev, data->usb_id_irq, data); 163 cancel_delayed_work_sync(&data->work); 164 165 return 0; 166 } 167 168 static const struct acpi_device_id int3496_acpi_match[] = { 169 { "INT3496" }, 170 { } 171 }; 172 MODULE_DEVICE_TABLE(acpi, int3496_acpi_match); 173 174 static struct platform_driver int3496_driver = { 175 .driver = { 176 .name = "intel-int3496", 177 .acpi_match_table = int3496_acpi_match, 178 }, 179 .probe = int3496_probe, 180 .remove = int3496_remove, 181 }; 182 183 module_platform_driver(int3496_driver); 184 185 MODULE_AUTHOR("Hans de Goede <hdegoede@redhat.com>"); 186 MODULE_DESCRIPTION("Intel INT3496 ACPI device extcon driver"); 187 MODULE_LICENSE("GPL v2"); 188