Path: blob/master/drivers/extcon/extcon-intel-int3496.c
26378 views
// SPDX-License-Identifier: GPL-2.01/*2* Intel INT3496 ACPI device extcon driver3*4* Copyright (c) 2016 Hans de Goede <[email protected]>5*6* Based on android x86 kernel code which is:7*8* Copyright (c) 2014, Intel Corporation.9* Author: David Cohen <[email protected]>10*/1112#include <linux/acpi.h>13#include <linux/devm-helpers.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#include <linux/regulator/consumer.h>2021#define INT3496_GPIO_USB_ID 022#define INT3496_GPIO_VBUS_EN 123#define INT3496_GPIO_USB_MUX 224#define DEBOUNCE_TIME msecs_to_jiffies(50)2526struct int3496_data {27struct device *dev;28struct extcon_dev *edev;29struct delayed_work work;30struct gpio_desc *gpio_usb_id;31struct gpio_desc *gpio_vbus_en;32struct gpio_desc *gpio_usb_mux;33struct regulator *vbus_boost;34int usb_id_irq;35bool vbus_boost_enabled;36};3738static const unsigned int int3496_cable[] = {39EXTCON_USB_HOST,40EXTCON_NONE,41};4243static const struct acpi_gpio_params id_gpios = { INT3496_GPIO_USB_ID, 0, false };44static const struct acpi_gpio_params vbus_gpios = { INT3496_GPIO_VBUS_EN, 0, false };45static const struct acpi_gpio_params mux_gpios = { INT3496_GPIO_USB_MUX, 0, false };4647static const struct acpi_gpio_mapping acpi_int3496_default_gpios[] = {48/*49* Some platforms have a bug in ACPI GPIO description making IRQ50* GPIO to be output only. Ask the GPIO core to ignore this limit.51*/52{ "id-gpios", &id_gpios, 1, ACPI_GPIO_QUIRK_NO_IO_RESTRICTION },53{ "vbus-gpios", &vbus_gpios, 1 },54{ "mux-gpios", &mux_gpios, 1 },55{ },56};5758static void int3496_set_vbus_boost(struct int3496_data *data, bool enable)59{60int ret;6162if (IS_ERR_OR_NULL(data->vbus_boost))63return;6465if (data->vbus_boost_enabled == enable)66return;6768if (enable)69ret = regulator_enable(data->vbus_boost);70else71ret = regulator_disable(data->vbus_boost);7273if (ret == 0)74data->vbus_boost_enabled = enable;75else76dev_err(data->dev, "Error updating Vbus boost regulator: %d\n", ret);77}7879static void int3496_do_usb_id(struct work_struct *work)80{81struct int3496_data *data =82container_of(work, struct int3496_data, work.work);83int id = gpiod_get_value_cansleep(data->gpio_usb_id);8485/* id == 1: PERIPHERAL, id == 0: HOST */86dev_dbg(data->dev, "Connected %s cable\n", id ? "PERIPHERAL" : "HOST");8788/*89* Peripheral: set USB mux to peripheral and disable VBUS90* Host: set USB mux to host and enable VBUS91*/92if (!IS_ERR(data->gpio_usb_mux))93gpiod_direction_output(data->gpio_usb_mux, id);9495if (!IS_ERR(data->gpio_vbus_en))96gpiod_direction_output(data->gpio_vbus_en, !id);97else98int3496_set_vbus_boost(data, !id);99100extcon_set_state_sync(data->edev, EXTCON_USB_HOST, !id);101}102103static irqreturn_t int3496_thread_isr(int irq, void *priv)104{105struct int3496_data *data = priv;106107/* Let the pin settle before processing it */108mod_delayed_work(system_wq, &data->work, DEBOUNCE_TIME);109110return IRQ_HANDLED;111}112113static int int3496_probe(struct platform_device *pdev)114{115struct device *dev = &pdev->dev;116struct int3496_data *data;117int ret;118119if (has_acpi_companion(dev)) {120ret = devm_acpi_dev_add_driver_gpios(dev, acpi_int3496_default_gpios);121if (ret) {122dev_err(dev, "can't add GPIO ACPI mapping\n");123return ret;124}125}126127data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);128if (!data)129return -ENOMEM;130131data->dev = dev;132ret = devm_delayed_work_autocancel(dev, &data->work, int3496_do_usb_id);133if (ret)134return ret;135136data->gpio_usb_id =137devm_gpiod_get(dev, "id", GPIOD_IN | GPIOD_FLAGS_BIT_NONEXCLUSIVE);138if (IS_ERR(data->gpio_usb_id)) {139ret = PTR_ERR(data->gpio_usb_id);140dev_err(dev, "can't request USB ID GPIO: %d\n", ret);141return ret;142}143144data->usb_id_irq = gpiod_to_irq(data->gpio_usb_id);145if (data->usb_id_irq < 0) {146dev_err(dev, "can't get USB ID IRQ: %d\n", data->usb_id_irq);147return data->usb_id_irq;148}149150data->gpio_vbus_en = devm_gpiod_get(dev, "vbus", GPIOD_ASIS);151if (IS_ERR(data->gpio_vbus_en)) {152dev_dbg(dev, "can't request VBUS EN GPIO\n");153data->vbus_boost = devm_regulator_get_optional(dev, "vbus");154}155156data->gpio_usb_mux = devm_gpiod_get(dev, "mux", GPIOD_ASIS);157if (IS_ERR(data->gpio_usb_mux))158dev_dbg(dev, "can't request USB MUX GPIO\n");159160/* register extcon device */161data->edev = devm_extcon_dev_allocate(dev, int3496_cable);162if (IS_ERR(data->edev))163return -ENOMEM;164165ret = devm_extcon_dev_register(dev, data->edev);166if (ret < 0) {167dev_err(dev, "can't register extcon device: %d\n", ret);168return ret;169}170171ret = devm_request_threaded_irq(dev, data->usb_id_irq,172NULL, int3496_thread_isr,173IRQF_SHARED | IRQF_ONESHOT |174IRQF_TRIGGER_RISING |175IRQF_TRIGGER_FALLING,176dev_name(dev), data);177if (ret < 0) {178dev_err(dev, "can't request IRQ for USB ID GPIO: %d\n", ret);179return ret;180}181182/* process id-pin so that we start with the right status */183queue_delayed_work(system_wq, &data->work, 0);184flush_delayed_work(&data->work);185186platform_set_drvdata(pdev, data);187188return 0;189}190191static const struct acpi_device_id int3496_acpi_match[] = {192{ "INT3496" },193{ }194};195MODULE_DEVICE_TABLE(acpi, int3496_acpi_match);196197static const struct platform_device_id int3496_ids[] = {198{ .name = "intel-int3496" },199{},200};201MODULE_DEVICE_TABLE(platform, int3496_ids);202203static struct platform_driver int3496_driver = {204.driver = {205.name = "intel-int3496",206.acpi_match_table = int3496_acpi_match,207},208.probe = int3496_probe,209.id_table = int3496_ids,210};211212module_platform_driver(int3496_driver);213214MODULE_AUTHOR("Hans de Goede <[email protected]>");215MODULE_DESCRIPTION("Intel INT3496 ACPI device extcon driver");216MODULE_LICENSE("GPL v2");217218219