diff options
Diffstat (limited to 'drivers/extcon')
-rw-r--r-- | drivers/extcon/Kconfig | 10 | ||||
-rw-r--r-- | drivers/extcon/Makefile | 1 | ||||
-rw-r--r-- | drivers/extcon/devres.c | 2 | ||||
-rw-r--r-- | drivers/extcon/extcon-adc-jack.c | 2 | ||||
-rw-r--r-- | drivers/extcon/extcon-arizona.c | 20 | ||||
-rw-r--r-- | drivers/extcon/extcon-axp288.c | 110 | ||||
-rw-r--r-- | drivers/extcon/extcon-intel-int3496.c | 179 | ||||
-rw-r--r-- | drivers/extcon/extcon-max14577.c | 6 | ||||
-rw-r--r-- | drivers/extcon/extcon-max77693.c | 12 | ||||
-rw-r--r-- | drivers/extcon/extcon-max77843.c | 24 | ||||
-rw-r--r-- | drivers/extcon/extcon-palmas.c | 21 | ||||
-rw-r--r-- | drivers/extcon/extcon-rt8973a.c | 6 | ||||
-rw-r--r-- | drivers/extcon/extcon-sm5502.c | 6 | ||||
-rw-r--r-- | drivers/extcon/extcon-usb-gpio.c | 7 | ||||
-rw-r--r-- | drivers/extcon/extcon.c | 45 | ||||
-rw-r--r-- | drivers/extcon/extcon.h | 62 |
16 files changed, 378 insertions, 135 deletions
diff --git a/drivers/extcon/Kconfig b/drivers/extcon/Kconfig index 04788d92ea52..96bbae579c0b 100644 --- a/drivers/extcon/Kconfig +++ b/drivers/extcon/Kconfig @@ -42,6 +42,16 @@ config EXTCON_GPIO Say Y here to enable GPIO based extcon support. Note that GPIO extcon supports single state per extcon instance. +config EXTCON_INTEL_INT3496 + tristate "Intel INT3496 ACPI device extcon driver" + depends on GPIOLIB && ACPI + help + Say Y here to enable extcon support for USB OTG ports controlled by + an Intel INT3496 ACPI device. + + This ACPI device is typically found on Intel Baytrail or Cherrytrail + based tablets, or other Baytrail / Cherrytrail devices. + config EXTCON_MAX14577 tristate "Maxim MAX14577/77836 EXTCON Support" depends on MFD_MAX14577 diff --git a/drivers/extcon/Makefile b/drivers/extcon/Makefile index 31a0a999c4fb..237ac3f953c2 100644 --- a/drivers/extcon/Makefile +++ b/drivers/extcon/Makefile @@ -8,6 +8,7 @@ obj-$(CONFIG_EXTCON_ADC_JACK) += extcon-adc-jack.o obj-$(CONFIG_EXTCON_ARIZONA) += extcon-arizona.o obj-$(CONFIG_EXTCON_AXP288) += extcon-axp288.o obj-$(CONFIG_EXTCON_GPIO) += extcon-gpio.o +obj-$(CONFIG_EXTCON_INTEL_INT3496) += extcon-intel-int3496.o obj-$(CONFIG_EXTCON_MAX14577) += extcon-max14577.o obj-$(CONFIG_EXTCON_MAX3355) += extcon-max3355.o obj-$(CONFIG_EXTCON_MAX77693) += extcon-max77693.o diff --git a/drivers/extcon/devres.c b/drivers/extcon/devres.c index e686acd1c459..b40eb1805927 100644 --- a/drivers/extcon/devres.c +++ b/drivers/extcon/devres.c @@ -14,7 +14,7 @@ * GNU General Public License for more details. */ -#include <linux/extcon.h> +#include "extcon.h" static int devm_extcon_dev_match(struct device *dev, void *res, void *data) { diff --git a/drivers/extcon/extcon-adc-jack.c b/drivers/extcon/extcon-adc-jack.c index bc538708c753..6f6537ab0a79 100644 --- a/drivers/extcon/extcon-adc-jack.c +++ b/drivers/extcon/extcon-adc-jack.c @@ -67,7 +67,7 @@ static void adc_jack_handler(struct work_struct *work) ret = iio_read_channel_raw(data->chan, &adc_val); if (ret < 0) { - dev_err(&data->edev->dev, "read channel() error: %d\n", ret); + dev_err(data->dev, "read channel() error: %d\n", ret); return; } diff --git a/drivers/extcon/extcon-arizona.c b/drivers/extcon/extcon-arizona.c index d836d4ce5ee4..ed78b7c26627 100644 --- a/drivers/extcon/extcon-arizona.c +++ b/drivers/extcon/extcon-arizona.c @@ -236,12 +236,8 @@ static void arizona_extcon_set_mode(struct arizona_extcon_info *info, int mode) mode %= info->micd_num_modes; - if (arizona->pdata.micd_pol_gpio > 0) - gpio_set_value_cansleep(arizona->pdata.micd_pol_gpio, - info->micd_modes[mode].gpio); - else - gpiod_set_value_cansleep(info->micd_pol_gpio, - info->micd_modes[mode].gpio); + gpiod_set_value_cansleep(info->micd_pol_gpio, + info->micd_modes[mode].gpio); regmap_update_bits(arizona->regmap, ARIZONA_MIC_DETECT_1, ARIZONA_MICD_BIAS_SRC_MASK, @@ -1412,21 +1408,21 @@ static int arizona_extcon_probe(struct platform_device *pdev) regmap_update_bits(arizona->regmap, ARIZONA_GP_SWITCH_1, ARIZONA_SW1_MODE_MASK, arizona->pdata.gpsw); - if (arizona->pdata.micd_pol_gpio > 0) { + if (pdata->micd_pol_gpio > 0) { if (info->micd_modes[0].gpio) mode = GPIOF_OUT_INIT_HIGH; else mode = GPIOF_OUT_INIT_LOW; - ret = devm_gpio_request_one(&pdev->dev, - arizona->pdata.micd_pol_gpio, - mode, - "MICD polarity"); + ret = devm_gpio_request_one(&pdev->dev, pdata->micd_pol_gpio, + mode, "MICD polarity"); if (ret != 0) { dev_err(arizona->dev, "Failed to request GPIO%d: %d\n", - arizona->pdata.micd_pol_gpio, ret); + pdata->micd_pol_gpio, ret); goto err_register; } + + info->micd_pol_gpio = gpio_to_desc(pdata->micd_pol_gpio); } else { if (info->micd_modes[0].gpio) mode = GPIOD_OUT_HIGH; diff --git a/drivers/extcon/extcon-axp288.c b/drivers/extcon/extcon-axp288.c index 42f41e808292..f4fd03e58e37 100644 --- a/drivers/extcon/extcon-axp288.c +++ b/drivers/extcon/extcon-axp288.c @@ -21,7 +21,6 @@ #include <linux/interrupt.h> #include <linux/platform_device.h> #include <linux/property.h> -#include <linux/usb/phy.h> #include <linux/notifier.h> #include <linux/extcon.h> #include <linux/regmap.h> @@ -71,12 +70,6 @@ #define DET_STAT_CDP 2 #define DET_STAT_DCP 3 -/* IRQ enable-1 register */ -#define PWRSRC_IRQ_CFG_MASK (BIT(4)|BIT(3)|BIT(2)) - -/* IRQ enable-6 register */ -#define BC12_IRQ_CFG_MASK BIT(1) - enum axp288_extcon_reg { AXP288_PS_STAT_REG = 0x00, AXP288_PS_BOOT_REASON_REG = 0x02, @@ -84,8 +77,6 @@ enum axp288_extcon_reg { AXP288_BC_VBUS_CNTL_REG = 0x2d, AXP288_BC_USB_STAT_REG = 0x2e, AXP288_BC_DET_STAT_REG = 0x2f, - AXP288_PWRSRC_IRQ_CFG_REG = 0x40, - AXP288_BC12_IRQ_CFG_REG = 0x45, }; enum axp288_mux_select { @@ -105,6 +96,7 @@ static const unsigned int axp288_extcon_cables[] = { EXTCON_CHG_USB_SDP, EXTCON_CHG_USB_CDP, EXTCON_CHG_USB_DCP, + EXTCON_USB, EXTCON_NONE, }; @@ -112,11 +104,11 @@ struct axp288_extcon_info { struct device *dev; struct regmap *regmap; struct regmap_irq_chip_data *regmap_irqc; - struct axp288_extcon_pdata *pdata; + struct gpio_desc *gpio_mux_cntl; int irq[EXTCON_IRQ_END]; struct extcon_dev *edev; struct notifier_block extcon_nb; - struct usb_phy *otg; + unsigned int previous_cable; }; /* Power up/down reason string array */ @@ -156,10 +148,9 @@ static void axp288_extcon_log_rsi(struct axp288_extcon_info *info) static int axp288_handle_chrg_det_event(struct axp288_extcon_info *info) { - static bool notify_otg, notify_charger; - static unsigned int cable; int ret, stat, cfg, pwr_stat; u8 chrg_type; + unsigned int cable = info->previous_cable; bool vbus_attach = false; ret = regmap_read(info->regmap, AXP288_PS_STAT_REG, &pwr_stat); @@ -168,9 +159,9 @@ static int axp288_handle_chrg_det_event(struct axp288_extcon_info *info) return ret; } - vbus_attach = (pwr_stat & PS_STAT_VBUS_PRESENT); + vbus_attach = (pwr_stat & PS_STAT_VBUS_VALID); if (!vbus_attach) - goto notify_otg; + goto no_vbus; /* Check charger detection completion status */ ret = regmap_read(info->regmap, AXP288_BC_GLOBAL_REG, &cfg); @@ -190,19 +181,14 @@ static int axp288_handle_chrg_det_event(struct axp288_extcon_info *info) switch (chrg_type) { case DET_STAT_SDP: dev_dbg(info->dev, "sdp cable is connected\n"); - notify_otg = true; - notify_charger = true; cable = EXTCON_CHG_USB_SDP; break; case DET_STAT_CDP: dev_dbg(info->dev, "cdp cable is connected\n"); - notify_otg = true; - notify_charger = true; cable = EXTCON_CHG_USB_CDP; break; case DET_STAT_DCP: dev_dbg(info->dev, "dcp cable is connected\n"); - notify_charger = true; cable = EXTCON_CHG_USB_DCP; break; default: @@ -210,27 +196,28 @@ static int axp288_handle_chrg_det_event(struct axp288_extcon_info *info) "disconnect or unknown or ID event\n"); } -notify_otg: - if (notify_otg) { - /* - * If VBUS is absent Connect D+/D- lines to PMIC for BC - * detection. Else connect them to SOC for USB communication. - */ - if (info->pdata->gpio_mux_cntl) - gpiod_set_value(info->pdata->gpio_mux_cntl, - vbus_attach ? EXTCON_GPIO_MUX_SEL_SOC - : EXTCON_GPIO_MUX_SEL_PMIC); - - atomic_notifier_call_chain(&info->otg->notifier, - vbus_attach ? USB_EVENT_VBUS : USB_EVENT_NONE, NULL); - } - - if (notify_charger) +no_vbus: + /* + * If VBUS is absent Connect D+/D- lines to PMIC for BC + * detection. Else connect them to SOC for USB communication. + */ + if (info->gpio_mux_cntl) + gpiod_set_value(info->gpio_mux_cntl, + vbus_attach ? EXTCON_GPIO_MUX_SEL_SOC + : EXTCON_GPIO_MUX_SEL_PMIC); + + extcon_set_state_sync(info->edev, info->previous_cable, false); + if (info->previous_cable == EXTCON_CHG_USB_SDP) + extcon_set_state_sync(info->edev, EXTCON_USB, false); + + if (vbus_attach) { extcon_set_state_sync(info->edev, cable, vbus_attach); + if (cable == EXTCON_CHG_USB_SDP) + extcon_set_state_sync(info->edev, EXTCON_USB, + vbus_attach); - /* Clear the flags on disconnect event */ - if (!vbus_attach) - notify_otg = notify_charger = false; + info->previous_cable = cable; + } return 0; @@ -253,15 +240,10 @@ static irqreturn_t axp288_extcon_isr(int irq, void *data) return IRQ_HANDLED; } -static void axp288_extcon_enable_irq(struct axp288_extcon_info *info) +static void axp288_extcon_enable(struct axp288_extcon_info *info) { - /* Unmask VBUS interrupt */ - regmap_write(info->regmap, AXP288_PWRSRC_IRQ_CFG_REG, - PWRSRC_IRQ_CFG_MASK); regmap_update_bits(info->regmap, AXP288_BC_GLOBAL_REG, BC_GLOBAL_RUN, 0); - /* Unmask the BC1.2 complete interrupts */ - regmap_write(info->regmap, AXP288_BC12_IRQ_CFG_REG, BC12_IRQ_CFG_MASK); /* Enable the charger detection logic */ regmap_update_bits(info->regmap, AXP288_BC_GLOBAL_REG, BC_GLOBAL_RUN, BC_GLOBAL_RUN); @@ -271,6 +253,7 @@ static int axp288_extcon_probe(struct platform_device *pdev) { struct axp288_extcon_info *info; struct axp20x_dev *axp20x = dev_get_drvdata(pdev->dev.parent); + struct axp288_extcon_pdata *pdata = pdev->dev.platform_data; int ret, i, pirq, gpio; info = devm_kzalloc(&pdev->dev, sizeof(*info), GFP_KERNEL); @@ -280,15 +263,10 @@ static int axp288_extcon_probe(struct platform_device *pdev) info->dev = &pdev->dev; info->regmap = axp20x->regmap; info->regmap_irqc = axp20x->regmap_irqc; - info->pdata = pdev->dev.platform_data; - - if (!info->pdata) { - /* Try ACPI provided pdata via device properties */ - if (!device_property_present(&pdev->dev, - "axp288_extcon_data\n")) - dev_err(&pdev->dev, "failed to get platform data\n"); - return -ENODEV; - } + info->previous_cable = EXTCON_NONE; + if (pdata) + info->gpio_mux_cntl = pdata->gpio_mux_cntl; + platform_set_drvdata(pdev, info); axp288_extcon_log_rsi(info); @@ -308,23 +286,16 @@ static int axp288_extcon_probe(struct platform_device *pdev) return ret; } - /* Get otg transceiver phy */ - info->otg = devm_usb_get_phy(&pdev->dev, USB_PHY_TYPE_USB2); - if (IS_ERR(info->otg)) { - dev_err(&pdev->dev, "failed to get otg transceiver\n"); - return PTR_ERR(info->otg); - } - /* Set up gpio control for USB Mux */ - if (info->pdata->gpio_mux_cntl) { - gpio = desc_to_gpio(info->pdata->gpio_mux_cntl); + if (info->gpio_mux_cntl) { + gpio = desc_to_gpio(info->gpio_mux_cntl); ret = devm_gpio_request(&pdev->dev, gpio, "USB_MUX"); if (ret < 0) { dev_err(&pdev->dev, "failed to request the gpio=%d\n", gpio); return ret; } - gpiod_direction_output(info->pdata->gpio_mux_cntl, + gpiod_direction_output(info->gpio_mux_cntl, EXTCON_GPIO_MUX_SEL_PMIC); } @@ -349,14 +320,21 @@ static int axp288_extcon_probe(struct platform_device *pdev) } } - /* Enable interrupts */ - axp288_extcon_enable_irq(info); + /* Start charger cable type detection */ + axp288_extcon_enable(info); return 0; } +static const struct platform_device_id axp288_extcon_table[] = { + { .name = "axp288_extcon" }, + {}, +}; +MODULE_DEVICE_TABLE(platform, axp288_extcon_table); + static struct platform_driver axp288_extcon_driver = { .probe = axp288_extcon_probe, + .id_table = axp288_extcon_table, .driver = { .name = "axp288_extcon", }, diff --git a/drivers/extcon/extcon-intel-int3496.c b/drivers/extcon/extcon-intel-int3496.c new file mode 100644 index 000000000000..a3131b036de6 --- /dev/null +++ b/drivers/extcon/extcon-intel-int3496.c @@ -0,0 +1,179 @@ +/* + * Intel INT3496 ACPI device extcon driver + * + * Copyright (c) 2016 Hans de Goede <hdegoede@redhat.com> + * + * Based on android x86 kernel code which is: + * + * 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/interrupt.h> +#include <linux/module.h> +#include <linux/platform_device.h> + +#define INT3496_GPIO_USB_ID 0 +#define INT3496_GPIO_VBUS_EN 1 +#define INT3496_GPIO_USB_MUX 2 +#define DEBOUNCE_TIME msecs_to_jiffies(50) + +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; + int usb_id_irq; +}; + +static const unsigned int int3496_cable[] = { + EXTCON_USB_HOST, + EXTCON_NONE, +}; + +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); + + 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_wq, &data->work, DEBOUNCE_TIME); + + return IRQ_HANDLED; +} + +static int int3496_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct int3496_data *data; + int ret; + + data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + data->dev = dev; + INIT_DELAYED_WORK(&data->work, int3496_do_usb_id); + + data->gpio_usb_id = devm_gpiod_get_index(dev, "id", + INT3496_GPIO_USB_ID, + GPIOD_IN); + 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; + } + + data->usb_id_irq = gpiod_to_irq(data->gpio_usb_id); + if (data->usb_id_irq <= 0) { + dev_err(dev, "can't get USB ID IRQ: %d\n", data->usb_id_irq); + return -EINVAL; + } + + data->gpio_vbus_en = devm_gpiod_get_index(dev, "vbus en", + INT3496_GPIO_VBUS_EN, + GPIOD_ASIS); + if (IS_ERR(data->gpio_vbus_en)) + dev_info(dev, "can't request VBUS EN GPIO\n"); + + data->gpio_usb_mux = devm_gpiod_get_index(dev, "usb mux", + INT3496_GPIO_USB_MUX, + GPIOD_ASIS); + if (IS_ERR(data->gpio_usb_mux)) + dev_info(dev, "can't request USB MUX GPIO\n"); + + /* register extcon device */ + data->edev = devm_extcon_dev_allocate(dev, int3496_cable); + if (IS_ERR(data->edev)) + return -ENOMEM; + + ret = devm_extcon_dev_register(dev, data->edev); + if (ret < 0) { + dev_err(dev, "can't register extcon device: %d\n", ret); + return ret; + } + + ret = devm_request_threaded_irq(dev, data->usb_id_irq, + NULL, int3496_thread_isr, + IRQF_SHARED | IRQF_ONESHOT | + IRQF_TRIGGER_RISING | + IRQF_TRIGGER_FALLING, + dev_name(dev), data); + if (ret < 0) { + dev_err(dev, "can't request IRQ for USB ID GPIO: %d\n", ret); + return ret; + } + + /* queue initial processing of id-pin */ + queue_delayed_work(system_wq, &data->work, 0); + + 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[] = { + { "INT3496" }, + { } +}; +MODULE_DEVICE_TABLE(acpi, int3496_acpi_match); + +static struct platform_driver int3496_driver = { + .driver = { + .name = "intel-int3496", + .acpi_match_table = int3496_acpi_match, + }, + .probe = int3496_probe, + .remove = int3496_remove, +}; + +module_platform_driver(int3496_driver); + +MODULE_AUTHOR("Hans de Goede <hdegoede@redhat.com>"); +MODULE_DESCRIPTION("Intel INT3496 ACPI device extcon driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/extcon/extcon-max14577.c b/drivers/extcon/extcon-max14577.c index 12e26c4e7763..f6414b7fa5bc 100644 --- a/drivers/extcon/extcon-max14577.c +++ b/drivers/extcon/extcon-max14577.c @@ -531,8 +531,10 @@ static int max14577_parse_irq(struct max14577_muic_info *info, int irq_type) case MAX14577_IRQ_INT1_ADC: case MAX14577_IRQ_INT1_ADCLOW: case MAX14577_IRQ_INT1_ADCERR: - /* Handle all of accessory except for - type of charger accessory */ + /* + * Handle all of accessory except for + * type of charger accessory. + */ info->irq_adc = true; return 1; case MAX14577_IRQ_INT2_CHGTYP: diff --git a/drivers/extcon/extcon-max77693.c b/drivers/extcon/extcon-max77693.c index 68dbcb814b2f..62163468f205 100644 --- a/drivers/extcon/extcon-max77693.c +++ b/drivers/extcon/extcon-max77693.c @@ -188,8 +188,10 @@ enum max77693_muic_acc_type { MAX77693_MUIC_ADC_AUDIO_MODE_REMOTE, MAX77693_MUIC_ADC_OPEN, - /* The below accessories have same ADC value so ADCLow and - ADC1K bit is used to separate specific accessory */ + /* + * The below accessories have same ADC value so ADCLow and + * ADC1K bit is used to separate specific accessory. + */ /* ADC|VBVolot|ADCLow|ADC1K| */ MAX77693_MUIC_GND_USB_HOST = 0x100, /* 0x0| 0| 0| 0| */ MAX77693_MUIC_GND_USB_HOST_VB = 0x104, /* 0x0| 1| 0| 0| */ @@ -970,8 +972,10 @@ static void max77693_muic_irq_work(struct work_struct *work) case MAX77693_MUIC_IRQ_INT1_ADC_LOW: case MAX77693_MUIC_IRQ_INT1_ADC_ERR: case MAX77693_MUIC_IRQ_INT1_ADC1K: - /* Handle all of accessory except for - type of charger accessory */ + /* + * Handle all of accessory except for + * type of charger accessory. + */ ret = max77693_muic_adc_handler(info); break; case MAX77693_MUIC_IRQ_INT2_CHGTYP: diff --git a/drivers/extcon/extcon-max77843.c b/drivers/extcon/extcon-max77843.c index 5d11fdf36e94..6e722d552cf1 100644 --- a/drivers/extcon/extcon-max77843.c +++ b/drivers/extcon/extcon-max77843.c @@ -97,8 +97,10 @@ enum max77843_muic_accessory_type { MAX77843_MUIC_ADC_AUDIO_DEVICE_TYPE1, MAX77843_MUIC_ADC_OPEN, - /* The blow accessories should check - not only ADC value but also ADC1K and VBVolt value. */ + /* + * The below accessories should check + * not only ADC value but also ADC1K and VBVolt value. + */ /* Offset|ADC1K|VBVolt| */ MAX77843_MUIC_GND_USB_HOST = 0x100, /* 0x1| 0| 0| */ MAX77843_MUIC_GND_USB_HOST_VB = 0x101, /* 0x1| 0| 1| */ @@ -265,16 +267,20 @@ static int max77843_muic_get_cable_type(struct max77843_muic_info *info, /* Check GROUND accessory with charger cable */ if (adc == MAX77843_MUIC_ADC_GROUND) { if (chg_type == MAX77843_MUIC_CHG_NONE) { - /* The following state when charger cable is + /* + * The following state when charger cable is * disconnected but the GROUND accessory still - * connected */ + * connected. + */ *attached = false; cable_type = info->prev_chg_type; info->prev_chg_type = MAX77843_MUIC_CHG_NONE; } else { - /* The following state when charger cable is - * connected on the GROUND accessory */ + /* + * The following state when charger cable is + * connected on the GROUND accessory. + */ *attached = true; cable_type = MAX77843_MUIC_CHG_GND; info->prev_chg_type = MAX77843_MUIC_CHG_GND; @@ -299,11 +305,13 @@ static int max77843_muic_get_cable_type(struct max77843_muic_info *info, } else { *attached = true; - /* Offset|ADC1K|VBVolt| + /* + * Offset|ADC1K|VBVolt| * 0x1| 0| 0| USB-HOST * 0x1| 0| 1| USB-HOST with VB * 0x1| 1| 0| MHL - * 0x1| 1| 1| MHL with VB */ + * 0x1| 1| 1| MHL with VB + */ /* Get ADC1K register bit */ gnd_type = (info->status[MAX77843_MUIC_STATUS1] & MAX77843_MUIC_STATUS1_ADC1K_MASK); diff --git a/drivers/extcon/extcon-palmas.c b/drivers/extcon/extcon-palmas.c index 634ba70782de..ca904e8b3235 100644 --- a/drivers/extcon/extcon-palmas.c +++ b/drivers/extcon/extcon-palmas.c @@ -62,7 +62,7 @@ static irqreturn_t palmas_vbus_irq_handler(int irq, void *_palmas_usb) if (palmas_usb->linkstat != PALMAS_USB_STATE_VBUS) { palmas_usb->linkstat = PALMAS_USB_STATE_VBUS; extcon_set_state_sync(edev, EXTCON_USB, true); - dev_info(palmas_usb->dev, "USB cable is attached\n"); + dev_dbg(palmas_usb->dev, "USB cable is attached\n"); } else { dev_dbg(palmas_usb->dev, "Spurious connect event detected\n"); @@ -71,7 +71,7 @@ static irqreturn_t palmas_vbus_irq_handler(int irq, void *_palmas_usb) if (palmas_usb->linkstat == PALMAS_USB_STATE_VBUS) { palmas_usb->linkstat = PALMAS_USB_STATE_DISCONNECT; extcon_set_state_sync(edev, EXTCON_USB, false); - dev_info(palmas_usb->dev, "USB cable is detached\n"); + dev_dbg(palmas_usb->dev, "USB cable is detached\n"); } else { dev_dbg(palmas_usb->dev, "Spurious disconnect event detected\n"); @@ -99,7 +99,7 @@ static irqreturn_t palmas_id_irq_handler(int irq, void *_palmas_usb) PALMAS_USB_ID_INT_EN_HI_CLR_ID_GND); palmas_usb->linkstat = PALMAS_USB_STATE_ID; extcon_set_state_sync(edev, EXTCON_USB_HOST, true); - dev_info(palmas_usb->dev, "USB-HOST cable is attached\n"); + dev_dbg(palmas_usb->dev, "USB-HOST cable is attached\n"); } else if ((set & PALMAS_USB_ID_INT_SRC_ID_FLOAT) && (id_src & PALMAS_USB_ID_INT_SRC_ID_FLOAT)) { palmas_write(palmas_usb->palmas, PALMAS_USB_OTG_BASE, @@ -107,17 +107,17 @@ static irqreturn_t palmas_id_irq_handler(int irq, void *_palmas_usb) PALMAS_USB_ID_INT_EN_HI_CLR_ID_FLOAT); palmas_usb->linkstat = PALMAS_USB_STATE_DISCONNECT; extcon_set_state_sync(edev, EXTCON_USB_HOST, false); - dev_info(palmas_usb->dev, "USB-HOST cable is detached\n"); + dev_dbg(palmas_usb->dev, "USB-HOST cable is detached\n"); } else if ((palmas_usb->linkstat == PALMAS_USB_STATE_ID) && (!(set & PALMAS_USB_ID_INT_SRC_ID_GND))) { palmas_usb->linkstat = PALMAS_USB_STATE_DISCONNECT; extcon_set_state_sync(edev, EXTCON_USB_HOST, false); - dev_info(palmas_usb->dev, "USB-HOST cable is detached\n"); + dev_dbg(palmas_usb->dev, "USB-HOST cable is detached\n"); } else if ((palmas_usb->linkstat == PALMAS_USB_STATE_DISCONNECT) && (id_src & PALMAS_USB_ID_INT_SRC_ID_GND)) { palmas_usb->linkstat = PALMAS_USB_STATE_ID; extcon_set_state_sync(edev, EXTCON_USB_HOST, true); - dev_info(palmas_usb->dev, " USB-HOST cable is attached\n"); + dev_dbg(palmas_usb->dev, " USB-HOST cable is attached\n"); } return IRQ_HANDLED; @@ -138,10 +138,10 @@ static void palmas_gpio_id_detect(struct work_struct *work) if (id) { extcon_set_state_sync(edev, EXTCON_USB_HOST, false); - dev_info(palmas_usb->dev, "USB-HOST cable is detached\n"); + dev_dbg(palmas_usb->dev, "USB-HOST cable is detached\n"); } else { extcon_set_state_sync(edev, EXTCON_USB_HOST, true); - dev_info(palmas_usb->dev, "USB-HOST cable is attached\n"); + dev_dbg(palmas_usb->dev, "USB-HOST cable is attached\n"); } } @@ -190,6 +190,11 @@ static int palmas_usb_probe(struct platform_device *pdev) struct palmas_usb *palmas_usb; int status; + if (!palmas) { + dev_err(&pdev->dev, "failed to get valid parent\n"); + return -EINVAL; + } + palmas_usb = devm_kzalloc(&pdev->dev, sizeof(*palmas_usb), GFP_KERNEL); if (!palmas_usb) return -ENOMEM; diff --git a/drivers/extcon/extcon-rt8973a.c b/drivers/extcon/extcon-rt8973a.c index 174c388739ea..3e882aa107e8 100644 --- a/drivers/extcon/extcon-rt8973a.c +++ b/drivers/extcon/extcon-rt8973a.c @@ -142,8 +142,10 @@ enum rt8973a_muic_acc_type { RT8973A_MUIC_ADC_UNKNOWN_ACC_5, RT8973A_MUIC_ADC_OPEN = 0x1f, - /* The below accessories has same ADC value (0x1f). - So, Device type1 is used to separate specific accessory. */ + /* + * The below accessories has same ADC value (0x1f). + * So, Device type1 is used to separate specific accessory. + */ /* |---------|--ADC| */ /* | [7:5]|[4:0]| */ RT8973A_MUIC_ADC_USB = 0x3f, /* | 001|11111| */ diff --git a/drivers/extcon/extcon-sm5502.c b/drivers/extcon/extcon-sm5502.c index b22325688503..106ef0297b53 100644 --- a/drivers/extcon/extcon-sm5502.c +++ b/drivers/extcon/extcon-sm5502.c @@ -135,8 +135,10 @@ enum sm5502_muic_acc_type { SM5502_MUIC_ADC_AUDIO_TYPE1, SM5502_MUIC_ADC_OPEN = 0x1f, - /* The below accessories have same ADC value (0x1f or 0x1e). - So, Device type1 is used to separate specific accessory. */ + /* + * The below accessories have same ADC value (0x1f or 0x1e). + * So, Device type1 is used to separate specific accessory. + */ /* |---------|--ADC| */ /* | [7:5]|[4:0]| */ SM5502_MUIC_ADC_AUDIO_TYPE1_FULL_REMOTE = 0x3e, /* | 001|11110| */ diff --git a/drivers/extcon/extcon-usb-gpio.c b/drivers/extcon/extcon-usb-gpio.c index d589c5feff3d..a5e1882b4ca6 100644 --- a/drivers/extcon/extcon-usb-gpio.c +++ b/drivers/extcon/extcon-usb-gpio.c @@ -27,6 +27,7 @@ #include <linux/slab.h> #include <linux/workqueue.h> #include <linux/acpi.h> +#include <linux/pinctrl/consumer.h> #define USB_GPIO_DEBOUNCE_MS 20 /* ms */ @@ -245,6 +246,9 @@ static int usb_extcon_suspend(struct device *dev) if (info->vbus_gpiod) disable_irq(info->vbus_irq); + if (!device_may_wakeup(dev)) + pinctrl_pm_select_sleep_state(dev); + return ret; } @@ -253,6 +257,9 @@ static int usb_extcon_resume(struct device *dev) struct usb_extcon_info *info = dev_get_drvdata(dev); int ret = 0; + if (!device_may_wakeup(dev)) + pinctrl_pm_select_default_state(dev); + if (device_may_wakeup(dev)) { if (info->id_gpiod) { ret = disable_irq_wake(info->id_irq); diff --git a/drivers/extcon/extcon.c b/drivers/extcon/extcon.c index 78298460d168..09ac5e70c2f3 100644 --- a/drivers/extcon/extcon.c +++ b/drivers/extcon/extcon.c @@ -30,11 +30,12 @@ #include <linux/device.h> #include <linux/fs.h> #include <linux/err.h> -#include <linux/extcon.h> #include <linux/of.h> #include <linux/slab.h> #include <linux/sysfs.h> +#include "extcon.h" + #define SUPPORTED_CABLE_MAX 32 #define CABLE_NAME_MAX 30 @@ -59,7 +60,7 @@ struct __extcon_info { [EXTCON_USB_HOST] = { .type = EXTCON_TYPE_USB, .id = EXTCON_USB_HOST, - .name = "USB_HOST", + .name = "USB-HOST", }, /* Charging external connector */ @@ -98,6 +99,11 @@ struct __extcon_info { .id = EXTCON_CHG_WPT, .name = "WPT", }, + [EXTCON_CHG_USB_PD] = { + .type = EXTCON_TYPE_CHG | EXTCON_TYPE_USB, + .id = EXTCON_CHG_USB_PD, + .name = "PD", + }, /* Jack external connector */ [EXTCON_JACK_MICROPHONE] = { @@ -453,7 +459,7 @@ int extcon_sync(struct extcon_dev *edev, unsigned int id) dev_err(&edev->dev, "out of memory in extcon_set_state\n"); kobject_uevent(&edev->dev.kobj, KOBJ_CHANGE); - return 0; + return -ENOMEM; } length = name_show(&edev->dev, NULL, prop_buf); @@ -906,35 +912,16 @@ int extcon_register_notifier(struct extcon_dev *edev, unsigned int id, unsigned long flags; int ret, idx = -EINVAL; - if (!nb) + if (!edev || !nb) return -EINVAL; - if (edev) { - idx = find_cable_index_by_id(edev, id); - if (idx < 0) - return idx; - - spin_lock_irqsave(&edev->lock, flags); - ret = raw_notifier_chain_register(&edev->nh[idx], nb); - spin_unlock_irqrestore(&edev->lock, flags); - } else { - struct extcon_dev *extd; - - mutex_lock(&extcon_dev_list_lock); - list_for_each_entry(extd, &extcon_dev_list, entry) { - idx = find_cable_index_by_id(extd, id); - if (idx >= 0) - break; - } - mutex_unlock(&extcon_dev_list_lock); + idx = find_cable_index_by_id(edev, id); + if (idx < 0) + return idx; - if (idx >= 0) { - edev = extd; - return extcon_register_notifier(extd, id, nb); - } else { - ret = -ENODEV; - } - } + spin_lock_irqsave(&edev->lock, flags); + ret = raw_notifier_chain_register(&edev->nh[idx], nb); + spin_unlock_irqrestore(&edev->lock, flags); return ret; } diff --git a/drivers/extcon/extcon.h b/drivers/extcon/extcon.h new file mode 100644 index 000000000000..993ddccafe11 --- /dev/null +++ b/drivers/extcon/extcon.h @@ -0,0 +1,62 @@ +#ifndef __LINUX_EXTCON_INTERNAL_H__ +#define __LINUX_EXTCON_INTERNAL_H__ + +#include <linux/extcon.h> + +/** + * struct extcon_dev - An extcon device represents one external connector. + * @name: The name of this extcon device. Parent device name is + * used if NULL. + * @supported_cable: Array of supported cable names ending with EXTCON_NONE. + * If supported_cable is NULL, cable name related APIs + * are disabled. + * @mutually_exclusive: Array of mutually exclusive set of cables that cannot + * be attached simultaneously. The array should be + * ending with NULL or be NULL (no mutually exclusive + * cables). For example, if it is { 0x7, 0x30, 0}, then, + * {0, 1}, {0, 1, 2}, {0, 2}, {1, 2}, or {4, 5} cannot + * be attached simulataneously. {0x7, 0} is equivalent to + * {0x3, 0x6, 0x5, 0}. If it is {0xFFFFFFFF, 0}, there + * can be no simultaneous connections. + * @dev: Device of this extcon. + * @state: Attach/detach state of this extcon. Do not provide at + * register-time. + * @nh: Notifier for the state change events from this extcon + * @entry: To support list of extcon devices so that users can + * search for extcon devices based on the extcon name. + * @lock: + * @max_supported: Internal value to store the number of cables. + * @extcon_dev_type: Device_type struct to provide attribute_groups + * customized for each extcon device. + * @cables: Sysfs subdirectories. Each represents one cable. + * + * In most cases, users only need to provide "User initializing data" of + * this struct when registering an extcon. In some exceptional cases, + * optional callbacks may be needed. However, the values in "internal data" + * are overwritten by register function. + */ +struct extcon_dev { + /* Optional user initializing data */ + const char *name; + const unsigned int *supported_cable; + const u32 *mutually_exclusive; + + /* Internal data. Please do not set. */ + struct device dev; + struct raw_notifier_head *nh; + struct list_head entry; + int max_supported; + spinlock_t lock; /* could be called by irq handler */ + u32 state; + + /* /sys/class/extcon/.../cable.n/... */ + struct device_type extcon_dev_type; + struct extcon_cable *cables; + + /* /sys/class/extcon/.../mutually_exclusive/... */ + struct attribute_group attr_g_muex; + struct attribute **attrs_muex; + struct device_attribute *d_attrs_muex; +}; + +#endif /* __LINUX_EXTCON_INTERNAL_H__ */ |