diff options
Diffstat (limited to 'drivers/extcon/extcon-intel-int3496.c')
| -rw-r--r-- | drivers/extcon/extcon-intel-int3496.c | 100 |
1 files changed, 62 insertions, 38 deletions
diff --git a/drivers/extcon/extcon-intel-int3496.c b/drivers/extcon/extcon-intel-int3496.c index d9f9afe45961..ded1a85a5549 100644 --- a/drivers/extcon/extcon-intel-int3496.c +++ b/drivers/extcon/extcon-intel-int3496.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * Intel INT3496 ACPI device extcon driver * @@ -7,23 +8,16 @@ * * Copyright (c) 2014, Intel Corporation. * Author: David Cohen <david.a.cohen@linux.intel.com> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. */ #include <linux/acpi.h> -#include <linux/extcon.h> -#include <linux/gpio.h> +#include <linux/devm-helpers.h> +#include <linux/extcon-provider.h> +#include <linux/gpio/consumer.h> #include <linux/interrupt.h> #include <linux/module.h> #include <linux/platform_device.h> +#include <linux/regulator/consumer.h> #define INT3496_GPIO_USB_ID 0 #define INT3496_GPIO_VBUS_EN 1 @@ -37,7 +31,9 @@ struct int3496_data { 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[] = { @@ -50,12 +46,37 @@ static const struct acpi_gpio_params vbus_gpios = { INT3496_GPIO_VBUS_EN, 0, fal static const struct acpi_gpio_params mux_gpios = { INT3496_GPIO_USB_MUX, 0, false }; static const struct acpi_gpio_mapping acpi_int3496_default_gpios[] = { - { "id-gpios", &id_gpios, 1 }, + /* + * 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 = @@ -74,6 +95,8 @@ static void int3496_do_usb_id(struct work_struct *work) 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); } @@ -94,10 +117,12 @@ static int int3496_probe(struct platform_device *pdev) struct int3496_data *data; int ret; - ret = devm_acpi_dev_add_driver_gpios(dev, acpi_int3496_default_gpios); - if (ret) { - dev_err(dev, "can't add GPIO ACPI mapping\n"); - return ret; + if (has_acpi_companion(dev)) { + ret = devm_acpi_dev_add_driver_gpios(dev, acpi_int3496_default_gpios); + if (ret) { + dev_err(dev, "can't add GPIO ACPI mapping\n"); + return ret; + } } data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL); @@ -105,16 +130,16 @@ static int int3496_probe(struct platform_device *pdev) return -ENOMEM; data->dev = dev; - INIT_DELAYED_WORK(&data->work, int3496_do_usb_id); + ret = devm_delayed_work_autocancel(dev, &data->work, int3496_do_usb_id); + if (ret) + return ret; - data->gpio_usb_id = devm_gpiod_get(dev, "id", GPIOD_IN); + data->gpio_usb_id = + devm_gpiod_get(dev, "id", GPIOD_IN | GPIOD_FLAGS_BIT_NONEXCLUSIVE); if (IS_ERR(data->gpio_usb_id)) { ret = PTR_ERR(data->gpio_usb_id); dev_err(dev, "can't request USB ID GPIO: %d\n", ret); return ret; - } else if (gpiod_get_direction(data->gpio_usb_id) != GPIOF_DIR_IN) { - dev_warn(dev, FW_BUG "USB ID GPIO not in input mode, fixing\n"); - gpiod_direction_input(data->gpio_usb_id); } data->usb_id_irq = gpiod_to_irq(data->gpio_usb_id); @@ -124,12 +149,14 @@ static int int3496_probe(struct platform_device *pdev) } data->gpio_vbus_en = devm_gpiod_get(dev, "vbus", GPIOD_ASIS); - if (IS_ERR(data->gpio_vbus_en)) - dev_info(dev, "can't request VBUS EN GPIO\n"); + if (IS_ERR(data->gpio_vbus_en)) { + dev_dbg(dev, "can't request VBUS EN GPIO\n"); + data->vbus_boost = devm_regulator_get_optional(dev, "vbus"); + } data->gpio_usb_mux = devm_gpiod_get(dev, "mux", GPIOD_ASIS); if (IS_ERR(data->gpio_usb_mux)) - dev_info(dev, "can't request USB MUX GPIO\n"); + dev_dbg(dev, "can't request USB MUX GPIO\n"); /* register extcon device */ data->edev = devm_extcon_dev_allocate(dev, int3496_cable); @@ -153,41 +180,38 @@ static int int3496_probe(struct platform_device *pdev) return ret; } - /* queue initial processing of id-pin */ + /* process id-pin so that we start with the right status */ queue_delayed_work(system_wq, &data->work, 0); + flush_delayed_work(&data->work); platform_set_drvdata(pdev, data); return 0; } -static int int3496_remove(struct platform_device *pdev) -{ - struct int3496_data *data = platform_get_drvdata(pdev); - - devm_free_irq(&pdev->dev, data->usb_id_irq, data); - cancel_delayed_work_sync(&data->work); - - return 0; -} - -static struct acpi_device_id int3496_acpi_match[] = { +static const struct acpi_device_id int3496_acpi_match[] = { { "INT3496" }, { } }; MODULE_DEVICE_TABLE(acpi, int3496_acpi_match); +static const struct platform_device_id int3496_ids[] = { + { .name = "intel-int3496" }, + {}, +}; +MODULE_DEVICE_TABLE(platform, int3496_ids); + static struct platform_driver int3496_driver = { .driver = { .name = "intel-int3496", .acpi_match_table = int3496_acpi_match, }, .probe = int3496_probe, - .remove = int3496_remove, + .id_table = int3496_ids, }; module_platform_driver(int3496_driver); MODULE_AUTHOR("Hans de Goede <hdegoede@redhat.com>"); MODULE_DESCRIPTION("Intel INT3496 ACPI device extcon driver"); -MODULE_LICENSE("GPL"); +MODULE_LICENSE("GPL v2"); |
