aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/extcon
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/extcon')
-rw-r--r--drivers/extcon/Kconfig23
-rw-r--r--drivers/extcon/Makefile2
-rw-r--r--drivers/extcon/extcon-adc-jack.c3
-rw-r--r--drivers/extcon/extcon-arizona.c1815
-rw-r--r--drivers/extcon/extcon-axp288.c82
-rw-r--r--drivers/extcon/extcon-fsa9480.c7
-rw-r--r--drivers/extcon/extcon-gpio.c15
-rw-r--r--drivers/extcon/extcon-intel-cht-wc.c240
-rw-r--r--drivers/extcon/extcon-intel-int3496.c70
-rw-r--r--drivers/extcon/extcon-intel-mrfld.c9
-rw-r--r--drivers/extcon/extcon-max14577.c28
-rw-r--r--drivers/extcon/extcon-max3355.c1
-rw-r--r--drivers/extcon/extcon-max77693.c21
-rw-r--r--drivers/extcon/extcon-max77843.c2
-rw-r--r--drivers/extcon/extcon-max8997.c50
-rw-r--r--drivers/extcon/extcon-palmas.c35
-rw-r--r--drivers/extcon/extcon-ptn5150.c262
-rw-r--r--drivers/extcon/extcon-qcom-spmi-misc.c116
-rw-r--r--drivers/extcon/extcon-rt8973a.c5
-rw-r--r--drivers/extcon/extcon-sm5502.c228
-rw-r--r--drivers/extcon/extcon-sm5502.h82
-rw-r--r--drivers/extcon/extcon-usb-gpio.c22
-rw-r--r--drivers/extcon/extcon-usbc-cros-ec.c2
-rw-r--r--drivers/extcon/extcon-usbc-tusb320.c513
-rw-r--r--drivers/extcon/extcon.c65
25 files changed, 1463 insertions, 2235 deletions
diff --git a/drivers/extcon/Kconfig b/drivers/extcon/Kconfig
index aac507bff135..290186e44e6b 100644
--- a/drivers/extcon/Kconfig
+++ b/drivers/extcon/Kconfig
@@ -21,17 +21,9 @@ config EXTCON_ADC_JACK
help
Say Y here to enable extcon device driver based on ADC values.
-config EXTCON_ARIZONA
- tristate "Wolfson Arizona EXTCON support"
- depends on MFD_ARIZONA && INPUT && SND_SOC
- help
- Say Y here to enable support for external accessory detection
- with Wolfson Arizona devices. These are audio CODECs with
- advanced audio accessory detection support.
-
config EXTCON_AXP288
tristate "X-Power AXP288 EXTCON support"
- depends on MFD_AXP20X && USB_SUPPORT && X86 && ACPI
+ depends on MFD_AXP20X && USB_SUPPORT && X86 && ACPI && IOSF_MBI
select USB_ROLE_SWITCH
help
Say Y here to enable support for USB peripheral detection
@@ -69,6 +61,8 @@ config EXTCON_INTEL_INT3496
config EXTCON_INTEL_CHT_WC
tristate "Intel Cherrytrail Whiskey Cove PMIC extcon driver"
depends on INTEL_SOC_PMIC_CHTWC
+ depends on USB_SUPPORT
+ select USB_ROLE_SWITCH
help
Say Y here to enable extcon support for charger detection / control
on the Intel Cherrytrail Whiskey Cove PMIC.
@@ -137,6 +131,7 @@ config EXTCON_PALMAS
config EXTCON_PTN5150
tristate "NXP PTN5150 CC LOGIC USB EXTCON support"
depends on I2C && (GPIOLIB || COMPILE_TEST)
+ depends on USB_ROLE_SWITCH || !USB_ROLE_SWITCH
select REGMAP_I2C
help
Say Y here to enable support for USB peripheral and USB host
@@ -162,7 +157,7 @@ config EXTCON_RT8973A
from abnormal high input voltage (up to 28V).
config EXTCON_SM5502
- tristate "Silicon Mitus SM5502 EXTCON support"
+ tristate "Silicon Mitus SM5502/SM5504/SM5703 EXTCON support"
depends on I2C
select IRQ_DOMAIN
select REGMAP_I2C
@@ -186,4 +181,12 @@ config EXTCON_USBC_CROS_EC
Say Y here to enable USB Type C cable detection extcon support when
using Chrome OS EC based USB Type-C ports.
+config EXTCON_USBC_TUSB320
+ tristate "TI TUSB320 USB-C extcon support"
+ depends on I2C && TYPEC
+ select REGMAP_I2C
+ help
+ Say Y here to enable support for USB Type C cable detection extcon
+ support using a TUSB320.
+
endif
diff --git a/drivers/extcon/Makefile b/drivers/extcon/Makefile
index 52096fd8a216..1b390d934ca9 100644
--- a/drivers/extcon/Makefile
+++ b/drivers/extcon/Makefile
@@ -6,7 +6,6 @@
obj-$(CONFIG_EXTCON) += extcon-core.o
extcon-core-objs += extcon.o devres.o
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_FSA9480) += extcon-fsa9480.o
obj-$(CONFIG_EXTCON_GPIO) += extcon-gpio.o
@@ -25,3 +24,4 @@ obj-$(CONFIG_EXTCON_RT8973A) += extcon-rt8973a.o
obj-$(CONFIG_EXTCON_SM5502) += extcon-sm5502.o
obj-$(CONFIG_EXTCON_USB_GPIO) += extcon-usb-gpio.o
obj-$(CONFIG_EXTCON_USBC_CROS_EC) += extcon-usbc-cros-ec.o
+obj-$(CONFIG_EXTCON_USBC_TUSB320) += extcon-usbc-tusb320.o
diff --git a/drivers/extcon/extcon-adc-jack.c b/drivers/extcon/extcon-adc-jack.c
index ad02dc6747a4..0317b614b680 100644
--- a/drivers/extcon/extcon-adc-jack.c
+++ b/drivers/extcon/extcon-adc-jack.c
@@ -124,7 +124,7 @@ static int adc_jack_probe(struct platform_device *pdev)
for (i = 0; data->adc_conditions[i].id != EXTCON_NONE; i++);
data->num_conditions = i;
- data->chan = iio_channel_get(&pdev->dev, pdata->consumer_channel);
+ data->chan = devm_iio_channel_get(&pdev->dev, pdata->consumer_channel);
if (IS_ERR(data->chan))
return PTR_ERR(data->chan);
@@ -164,7 +164,6 @@ static int adc_jack_remove(struct platform_device *pdev)
free_irq(data->irq, data);
cancel_work_sync(&data->handler.work);
- iio_channel_release(data->chan);
return 0;
}
diff --git a/drivers/extcon/extcon-arizona.c b/drivers/extcon/extcon-arizona.c
deleted file mode 100644
index 7401733db08b..000000000000
--- a/drivers/extcon/extcon-arizona.c
+++ /dev/null
@@ -1,1815 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-or-later
-/*
- * extcon-arizona.c - Extcon driver Wolfson Arizona devices
- *
- * Copyright (C) 2012-2014 Wolfson Microelectronics plc
- */
-
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/i2c.h>
-#include <linux/slab.h>
-#include <linux/interrupt.h>
-#include <linux/err.h>
-#include <linux/gpio/consumer.h>
-#include <linux/gpio.h>
-#include <linux/input.h>
-#include <linux/platform_device.h>
-#include <linux/pm_runtime.h>
-#include <linux/property.h>
-#include <linux/regulator/consumer.h>
-#include <linux/extcon-provider.h>
-
-#include <sound/soc.h>
-
-#include <linux/mfd/arizona/core.h>
-#include <linux/mfd/arizona/pdata.h>
-#include <linux/mfd/arizona/registers.h>
-#include <dt-bindings/mfd/arizona.h>
-
-#define ARIZONA_MAX_MICD_RANGE 8
-
-#define ARIZONA_MICD_CLAMP_MODE_JDL 0x4
-#define ARIZONA_MICD_CLAMP_MODE_JDH 0x5
-#define ARIZONA_MICD_CLAMP_MODE_JDL_GP5H 0x9
-#define ARIZONA_MICD_CLAMP_MODE_JDH_GP5H 0xb
-
-#define ARIZONA_TST_CAP_DEFAULT 0x3
-#define ARIZONA_TST_CAP_CLAMP 0x1
-
-#define ARIZONA_HPDET_MAX 10000
-
-#define HPDET_DEBOUNCE 500
-#define DEFAULT_MICD_TIMEOUT 2000
-
-#define ARIZONA_HPDET_WAIT_COUNT 15
-#define ARIZONA_HPDET_WAIT_DELAY_MS 20
-
-#define QUICK_HEADPHONE_MAX_OHM 3
-#define MICROPHONE_MIN_OHM 1257
-#define MICROPHONE_MAX_OHM 30000
-
-#define MICD_DBTIME_TWO_READINGS 2
-#define MICD_DBTIME_FOUR_READINGS 4
-
-#define MICD_LVL_1_TO_7 (ARIZONA_MICD_LVL_1 | ARIZONA_MICD_LVL_2 | \
- ARIZONA_MICD_LVL_3 | ARIZONA_MICD_LVL_4 | \
- ARIZONA_MICD_LVL_5 | ARIZONA_MICD_LVL_6 | \
- ARIZONA_MICD_LVL_7)
-
-#define MICD_LVL_0_TO_7 (ARIZONA_MICD_LVL_0 | MICD_LVL_1_TO_7)
-
-#define MICD_LVL_0_TO_8 (MICD_LVL_0_TO_7 | ARIZONA_MICD_LVL_8)
-
-struct arizona_extcon_info {
- struct device *dev;
- struct arizona *arizona;
- struct mutex lock;
- struct regulator *micvdd;
- struct input_dev *input;
-
- u16 last_jackdet;
-
- int micd_mode;
- const struct arizona_micd_config *micd_modes;
- int micd_num_modes;
-
- const struct arizona_micd_range *micd_ranges;
- int num_micd_ranges;
-
- bool micd_reva;
- bool micd_clamp;
-
- struct delayed_work hpdet_work;
- struct delayed_work micd_detect_work;
- struct delayed_work micd_timeout_work;
-
- bool hpdet_active;
- bool hpdet_done;
- bool hpdet_retried;
-
- int num_hpdet_res;
- unsigned int hpdet_res[3];
-
- bool mic;
- bool detecting;
- int jack_flips;
-
- int hpdet_ip_version;
-
- struct extcon_dev *edev;
-
- struct gpio_desc *micd_pol_gpio;
-};
-
-static const struct arizona_micd_config micd_default_modes[] = {
- { ARIZONA_ACCDET_SRC, 1, 0 },
- { 0, 2, 1 },
-};
-
-static const struct arizona_micd_range micd_default_ranges[] = {
- { .max = 11, .key = BTN_0 },
- { .max = 28, .key = BTN_1 },
- { .max = 54, .key = BTN_2 },
- { .max = 100, .key = BTN_3 },
- { .max = 186, .key = BTN_4 },
- { .max = 430, .key = BTN_5 },
-};
-
-/* The number of levels in arizona_micd_levels valid for button thresholds */
-#define ARIZONA_NUM_MICD_BUTTON_LEVELS 64
-
-static const int arizona_micd_levels[] = {
- 3, 6, 8, 11, 13, 16, 18, 21, 23, 26, 28, 31, 34, 36, 39, 41, 44, 46,
- 49, 52, 54, 57, 60, 62, 65, 67, 70, 73, 75, 78, 81, 83, 89, 94, 100,
- 105, 111, 116, 122, 127, 139, 150, 161, 173, 186, 196, 209, 220, 245,
- 270, 295, 321, 348, 375, 402, 430, 489, 550, 614, 681, 752, 903, 1071,
- 1257, 30000,
-};
-
-static const unsigned int arizona_cable[] = {
- EXTCON_MECHANICAL,
- EXTCON_JACK_MICROPHONE,
- EXTCON_JACK_HEADPHONE,
- EXTCON_JACK_LINE_OUT,
- EXTCON_NONE,
-};
-
-static void arizona_start_hpdet_acc_id(struct arizona_extcon_info *info);
-
-static void arizona_extcon_hp_clamp(struct arizona_extcon_info *info,
- bool clamp)
-{
- struct arizona *arizona = info->arizona;
- unsigned int mask = 0, val = 0;
- unsigned int cap_sel = 0;
- int ret;
-
- switch (arizona->type) {
- case WM8998:
- case WM1814:
- mask = 0;
- break;
- case WM5110:
- case WM8280:
- mask = ARIZONA_HP1L_SHRTO | ARIZONA_HP1L_FLWR |
- ARIZONA_HP1L_SHRTI;
- if (clamp) {
- val = ARIZONA_HP1L_SHRTO;
- cap_sel = ARIZONA_TST_CAP_CLAMP;
- } else {
- val = ARIZONA_HP1L_FLWR | ARIZONA_HP1L_SHRTI;
- cap_sel = ARIZONA_TST_CAP_DEFAULT;
- }
-
- ret = regmap_update_bits(arizona->regmap,
- ARIZONA_HP_TEST_CTRL_1,
- ARIZONA_HP1_TST_CAP_SEL_MASK,
- cap_sel);
- if (ret != 0)
- dev_warn(arizona->dev,
- "Failed to set TST_CAP_SEL: %d\n", ret);
- break;
- default:
- mask = ARIZONA_RMV_SHRT_HP1L;
- if (clamp)
- val = ARIZONA_RMV_SHRT_HP1L;
- break;
- }
-
- snd_soc_dapm_mutex_lock(arizona->dapm);
-
- arizona->hpdet_clamp = clamp;
-
- /* Keep the HP output stages disabled while doing the clamp */
- if (clamp) {
- ret = regmap_update_bits(arizona->regmap,
- ARIZONA_OUTPUT_ENABLES_1,
- ARIZONA_OUT1L_ENA |
- ARIZONA_OUT1R_ENA, 0);
- if (ret != 0)
- dev_warn(arizona->dev,
- "Failed to disable headphone outputs: %d\n",
- ret);
- }
-
- if (mask) {
- ret = regmap_update_bits(arizona->regmap, ARIZONA_HP_CTRL_1L,
- mask, val);
- if (ret != 0)
- dev_warn(arizona->dev, "Failed to do clamp: %d\n",
- ret);
-
- ret = regmap_update_bits(arizona->regmap, ARIZONA_HP_CTRL_1R,
- mask, val);
- if (ret != 0)
- dev_warn(arizona->dev, "Failed to do clamp: %d\n",
- ret);
- }
-
- /* Restore the desired state while not doing the clamp */
- if (!clamp) {
- ret = regmap_update_bits(arizona->regmap,
- ARIZONA_OUTPUT_ENABLES_1,
- ARIZONA_OUT1L_ENA |
- ARIZONA_OUT1R_ENA, arizona->hp_ena);
- if (ret != 0)
- dev_warn(arizona->dev,
- "Failed to restore headphone outputs: %d\n",
- ret);
- }
-
- snd_soc_dapm_mutex_unlock(arizona->dapm);
-}
-
-static void arizona_extcon_set_mode(struct arizona_extcon_info *info, int mode)
-{
- struct arizona *arizona = info->arizona;
-
- mode %= info->micd_num_modes;
-
- 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,
- info->micd_modes[mode].bias <<
- ARIZONA_MICD_BIAS_SRC_SHIFT);
- regmap_update_bits(arizona->regmap, ARIZONA_ACCESSORY_DETECT_MODE_1,
- ARIZONA_ACCDET_SRC, info->micd_modes[mode].src);
-
- info->micd_mode = mode;
-
- dev_dbg(arizona->dev, "Set jack polarity to %d\n", mode);
-}
-
-static const char *arizona_extcon_get_micbias(struct arizona_extcon_info *info)
-{
- switch (info->micd_modes[0].bias) {
- case 1:
- return "MICBIAS1";
- case 2:
- return "MICBIAS2";
- case 3:
- return "MICBIAS3";
- default:
- return "MICVDD";
- }
-}
-
-static void arizona_extcon_pulse_micbias(struct arizona_extcon_info *info)
-{
- struct arizona *arizona = info->arizona;
- const char *widget = arizona_extcon_get_micbias(info);
- struct snd_soc_dapm_context *dapm = arizona->dapm;
- struct snd_soc_component *component = snd_soc_dapm_to_component(dapm);
- int ret;
-
- ret = snd_soc_component_force_enable_pin(component, widget);
- if (ret != 0)
- dev_warn(arizona->dev, "Failed to enable %s: %d\n",
- widget, ret);
-
- snd_soc_dapm_sync(dapm);
-
- if (!arizona->pdata.micd_force_micbias) {
- ret = snd_soc_component_disable_pin(component, widget);
- if (ret != 0)
- dev_warn(arizona->dev, "Failed to disable %s: %d\n",
- widget, ret);
-
- snd_soc_dapm_sync(dapm);
- }
-}
-
-static void arizona_start_mic(struct arizona_extcon_info *info)
-{
- struct arizona *arizona = info->arizona;
- bool change;
- int ret;
- unsigned int mode;
-
- /* Microphone detection can't use idle mode */
- pm_runtime_get(info->dev);
-
- if (info->detecting) {
- ret = regulator_allow_bypass(info->micvdd, false);
- if (ret != 0) {
- dev_err(arizona->dev,
- "Failed to regulate MICVDD: %d\n",
- ret);
- }
- }
-
- ret = regulator_enable(info->micvdd);
- if (ret != 0) {
- dev_err(arizona->dev, "Failed to enable MICVDD: %d\n",
- ret);
- }
-
- if (info->micd_reva) {
- const struct reg_sequence reva[] = {
- { 0x80, 0x3 },
- { 0x294, 0x0 },
- { 0x80, 0x0 },
- };
-
- regmap_multi_reg_write(arizona->regmap, reva, ARRAY_SIZE(reva));
- }
-
- if (info->detecting && arizona->pdata.micd_software_compare)
- mode = ARIZONA_ACCDET_MODE_ADC;
- else
- mode = ARIZONA_ACCDET_MODE_MIC;
-
- regmap_update_bits(arizona->regmap,
- ARIZONA_ACCESSORY_DETECT_MODE_1,
- ARIZONA_ACCDET_MODE_MASK, mode);
-
- arizona_extcon_pulse_micbias(info);
-
- ret = regmap_update_bits_check(arizona->regmap, ARIZONA_MIC_DETECT_1,
- ARIZONA_MICD_ENA, ARIZONA_MICD_ENA,
- &change);
- if (ret < 0) {
- dev_err(arizona->dev, "Failed to enable micd: %d\n", ret);
- } else if (!change) {
- regulator_disable(info->micvdd);
- pm_runtime_put_autosuspend(info->dev);
- }
-}
-
-static void arizona_stop_mic(struct arizona_extcon_info *info)
-{
- struct arizona *arizona = info->arizona;
- const char *widget = arizona_extcon_get_micbias(info);
- struct snd_soc_dapm_context *dapm = arizona->dapm;
- struct snd_soc_component *component = snd_soc_dapm_to_component(dapm);
- bool change = false;
- int ret;
-
- ret = regmap_update_bits_check(arizona->regmap, ARIZONA_MIC_DETECT_1,
- ARIZONA_MICD_ENA, 0,
- &change);
- if (ret < 0)
- dev_err(arizona->dev, "Failed to disable micd: %d\n", ret);
-
- ret = snd_soc_component_disable_pin(component, widget);
- if (ret != 0)
- dev_warn(arizona->dev,
- "Failed to disable %s: %d\n",
- widget, ret);
-
- snd_soc_dapm_sync(dapm);
-
- if (info->micd_reva) {
- const struct reg_sequence reva[] = {
- { 0x80, 0x3 },
- { 0x294, 0x2 },
- { 0x80, 0x0 },
- };
-
- regmap_multi_reg_write(arizona->regmap, reva, ARRAY_SIZE(reva));
- }
-
- ret = regulator_allow_bypass(info->micvdd, true);
- if (ret != 0) {
- dev_err(arizona->dev, "Failed to bypass MICVDD: %d\n",
- ret);
- }
-
- if (change) {
- regulator_disable(info->micvdd);
- pm_runtime_mark_last_busy(info->dev);
- pm_runtime_put_autosuspend(info->dev);
- }
-}
-
-static struct {
- unsigned int threshold;
- unsigned int factor_a;
- unsigned int factor_b;
-} arizona_hpdet_b_ranges[] = {
- { 100, 5528, 362464 },
- { 169, 11084, 6186851 },
- { 169, 11065, 65460395 },
-};
-
-#define ARIZONA_HPDET_B_RANGE_MAX 0x3fb
-
-static struct {
- int min;
- int max;
-} arizona_hpdet_c_ranges[] = {
- { 0, 30 },
- { 8, 100 },
- { 100, 1000 },
- { 1000, 10000 },
-};
-
-static int arizona_hpdet_read(struct arizona_extcon_info *info)
-{
- struct arizona *arizona = info->arizona;
- unsigned int val, range;
- int ret;
-
- ret = regmap_read(arizona->regmap, ARIZONA_HEADPHONE_DETECT_2, &val);
- if (ret != 0) {
- dev_err(arizona->dev, "Failed to read HPDET status: %d\n",
- ret);
- return ret;
- }
-
- switch (info->hpdet_ip_version) {
- case 0:
- if (!(val & ARIZONA_HP_DONE)) {
- dev_err(arizona->dev, "HPDET did not complete: %x\n",
- val);
- return -EAGAIN;
- }
-
- val &= ARIZONA_HP_LVL_MASK;
- break;
-
- case 1:
- if (!(val & ARIZONA_HP_DONE_B)) {
- dev_err(arizona->dev, "HPDET did not complete: %x\n",
- val);
- return -EAGAIN;
- }
-
- ret = regmap_read(arizona->regmap, ARIZONA_HP_DACVAL, &val);
- if (ret != 0) {
- dev_err(arizona->dev, "Failed to read HP value: %d\n",
- ret);
- return -EAGAIN;
- }
-
- regmap_read(arizona->regmap, ARIZONA_HEADPHONE_DETECT_1,
- &range);
- range = (range & ARIZONA_HP_IMPEDANCE_RANGE_MASK)
- >> ARIZONA_HP_IMPEDANCE_RANGE_SHIFT;
-
- if (range < ARRAY_SIZE(arizona_hpdet_b_ranges) - 1 &&
- (val < arizona_hpdet_b_ranges[range].threshold ||
- val >= ARIZONA_HPDET_B_RANGE_MAX)) {
- range++;
- dev_dbg(arizona->dev, "Moving to HPDET range %d\n",
- range);
- regmap_update_bits(arizona->regmap,
- ARIZONA_HEADPHONE_DETECT_1,
- ARIZONA_HP_IMPEDANCE_RANGE_MASK,
- range <<
- ARIZONA_HP_IMPEDANCE_RANGE_SHIFT);
- return -EAGAIN;
- }
-
- /* If we go out of range report top of range */
- if (val < arizona_hpdet_b_ranges[range].threshold ||
- val >= ARIZONA_HPDET_B_RANGE_MAX) {
- dev_dbg(arizona->dev, "Measurement out of range\n");
- return ARIZONA_HPDET_MAX;
- }
-
- dev_dbg(arizona->dev, "HPDET read %d in range %d\n",
- val, range);
-
- val = arizona_hpdet_b_ranges[range].factor_b
- / ((val * 100) -
- arizona_hpdet_b_ranges[range].factor_a);
- break;
-
- case 2:
- if (!(val & ARIZONA_HP_DONE_B)) {
- dev_err(arizona->dev, "HPDET did not complete: %x\n",
- val);
- return -EAGAIN;
- }
-
- val &= ARIZONA_HP_LVL_B_MASK;
- /* Convert to ohms, the value is in 0.5 ohm increments */
- val /= 2;
-
- regmap_read(arizona->regmap, ARIZONA_HEADPHONE_DETECT_1,
- &range);
- range = (range & ARIZONA_HP_IMPEDANCE_RANGE_MASK)
- >> ARIZONA_HP_IMPEDANCE_RANGE_SHIFT;
-
- /* Skip up a range, or report? */
- if (range < ARRAY_SIZE(arizona_hpdet_c_ranges) - 1 &&
- (val >= arizona_hpdet_c_ranges[range].max)) {
- range++;
- dev_dbg(arizona->dev, "Moving to HPDET range %d-%d\n",
- arizona_hpdet_c_ranges[range].min,
- arizona_hpdet_c_ranges[range].max);
- regmap_update_bits(arizona->regmap,
- ARIZONA_HEADPHONE_DETECT_1,
- ARIZONA_HP_IMPEDANCE_RANGE_MASK,
- range <<
- ARIZONA_HP_IMPEDANCE_RANGE_SHIFT);
- return -EAGAIN;
- }
-
- if (range && (val < arizona_hpdet_c_ranges[range].min)) {
- dev_dbg(arizona->dev, "Reporting range boundary %d\n",
- arizona_hpdet_c_ranges[range].min);
- val = arizona_hpdet_c_ranges[range].min;
- }
- break;
-
- default:
- dev_warn(arizona->dev, "Unknown HPDET IP revision %d\n",
- info->hpdet_ip_version);
- return -EINVAL;
- }
-
- dev_dbg(arizona->dev, "HP impedance %d ohms\n", val);
- return val;
-}
-
-static int arizona_hpdet_do_id(struct arizona_extcon_info *info, int *reading,
- bool *mic)
-{
- struct arizona *arizona = info->arizona;
- int id_gpio = arizona->pdata.hpdet_id_gpio;
-
- if (!arizona->pdata.hpdet_acc_id)
- return 0;
-
- /*
- * If we're using HPDET for accessory identification we need
- * to take multiple measurements, step through them in sequence.
- */
- info->hpdet_res[info->num_hpdet_res++] = *reading;
-
- /* Only check the mic directly if we didn't already ID it */
- if (id_gpio && info->num_hpdet_res == 1) {
- dev_dbg(arizona->dev, "Measuring mic\n");
-
- regmap_update_bits(arizona->regmap,
- ARIZONA_ACCESSORY_DETECT_MODE_1,
- ARIZONA_ACCDET_MODE_MASK |
- ARIZONA_ACCDET_SRC,
- ARIZONA_ACCDET_MODE_HPR |
- info->micd_modes[0].src);
-
- gpio_set_value_cansleep(id_gpio, 1);
-
- regmap_update_bits(arizona->regmap, ARIZONA_HEADPHONE_DETECT_1,
- ARIZONA_HP_POLL, ARIZONA_HP_POLL);
- return -EAGAIN;
- }
-
- /* OK, got both. Now, compare... */
- dev_dbg(arizona->dev, "HPDET measured %d %d\n",
- info->hpdet_res[0], info->hpdet_res[1]);
-
- /* Take the headphone impedance for the main report */
- *reading = info->hpdet_res[0];
-
- /* Sometimes we get false readings due to slow insert */
- if (*reading >= ARIZONA_HPDET_MAX && !info->hpdet_retried) {
- dev_dbg(arizona->dev, "Retrying high impedance\n");
- info->num_hpdet_res = 0;
- info->hpdet_retried = true;
- arizona_start_hpdet_acc_id(info);
- pm_runtime_put(info->dev);
- return -EAGAIN;
- }
-
- /*
- * If we measure the mic as high impedance
- */
- if (!id_gpio || info->hpdet_res[1] > 50) {
- dev_dbg(arizona->dev, "Detected mic\n");
- *mic = true;
- info->detecting = true;
- } else {
- dev_dbg(arizona->dev, "Detected headphone\n");
- }
-
- /* Make sure everything is reset back to the real polarity */
- regmap_update_bits(arizona->regmap, ARIZONA_ACCESSORY_DETECT_MODE_1,
- ARIZONA_ACCDET_SRC, info->micd_modes[0].src);
-
- return 0;
-}
-
-static irqreturn_t arizona_hpdet_irq(int irq, void *data)
-{
- struct arizona_extcon_info *info = data;
- struct arizona *arizona = info->arizona;
- int id_gpio = arizona->pdata.hpdet_id_gpio;
- unsigned int report = EXTCON_JACK_HEADPHONE;
- int ret, reading;
- bool mic = false;
-
- mutex_lock(&info->lock);
-
- /* If we got a spurious IRQ for some reason then ignore it */
- if (!info->hpdet_active) {
- dev_warn(arizona->dev, "Spurious HPDET IRQ\n");
- mutex_unlock(&info->lock);
- return IRQ_NONE;
- }
-
- /* If the cable was removed while measuring ignore the result */
- ret = extcon_get_state(info->edev, EXTCON_MECHANICAL);
- if (ret < 0) {
- dev_err(arizona->dev, "Failed to check cable state: %d\n",
- ret);
- goto out;
- } else if (!ret) {
- dev_dbg(arizona->dev, "Ignoring HPDET for removed cable\n");
- goto done;
- }
-
- ret = arizona_hpdet_read(info);
- if (ret == -EAGAIN)
- goto out;
- else if (ret < 0)
- goto done;
- reading = ret;
-
- /* Reset back to starting range */
- regmap_update_bits(arizona->regmap,
- ARIZONA_HEADPHONE_DETECT_1,
- ARIZONA_HP_IMPEDANCE_RANGE_MASK | ARIZONA_HP_POLL,
- 0);
-
- ret = arizona_hpdet_do_id(info, &reading, &mic);
- if (ret == -EAGAIN)
- goto out;
- else if (ret < 0)
- goto done;
-
- /* Report high impedence cables as line outputs */
- if (reading >= 5000)
- report = EXTCON_JACK_LINE_OUT;
- else
- report = EXTCON_JACK_HEADPHONE;
-
- ret = extcon_set_state_sync(info->edev, report, true);
- if (ret != 0)
- dev_err(arizona->dev, "Failed to report HP/line: %d\n",
- ret);
-
-done:
- /* Reset back to starting range */
- regmap_update_bits(arizona->regmap,
- ARIZONA_HEADPHONE_DETECT_1,
- ARIZONA_HP_IMPEDANCE_RANGE_MASK | ARIZONA_HP_POLL,
- 0);
-
- arizona_extcon_hp_clamp(info, false);
-
- if (id_gpio)
- gpio_set_value_cansleep(id_gpio, 0);
-
- /* If we have a mic then reenable MICDET */
- if (mic || info->mic)
- arizona_start_mic(info);
-
- if (info->hpdet_active) {
- pm_runtime_put_autosuspend(info->dev);
- info->hpdet_active = false;
- }
-
- info->hpdet_done = true;
-
-out:
- mutex_unlock(&info->lock);
-
- return IRQ_HANDLED;
-}
-
-static void arizona_identify_headphone(struct arizona_extcon_info *info)
-{
- struct arizona *arizona = info->arizona;
- int ret;
-
- if (info->hpdet_done)
- return;
-
- dev_dbg(arizona->dev, "Starting HPDET\n");
-
- /* Make sure we keep the device enabled during the measurement */
- pm_runtime_get(info->dev);
-
- info->hpdet_active = true;
-
- arizona_stop_mic(info);
-
- arizona_extcon_hp_clamp(info, true);
-
- ret = regmap_update_bits(arizona->regmap,
- ARIZONA_ACCESSORY_DETECT_MODE_1,
- ARIZONA_ACCDET_MODE_MASK,
- arizona->pdata.hpdet_channel);
- if (ret != 0) {
- dev_err(arizona->dev, "Failed to set HPDET mode: %d\n", ret);
- goto err;
- }
-
- ret = regmap_update_bits(arizona->regmap, ARIZONA_HEADPHONE_DETECT_1,
- ARIZONA_HP_POLL, ARIZONA_HP_POLL);
- if (ret != 0) {
- dev_err(arizona->dev, "Can't start HPDETL measurement: %d\n",
- ret);
- goto err;
- }
-
- return;
-
-err:
- arizona_extcon_hp_clamp(info, false);
- pm_runtime_put_autosuspend(info->dev);
-
- /* Just report headphone */
- ret = extcon_set_state_sync(info->edev, EXTCON_JACK_HEADPHONE, true);
- if (ret != 0)
- dev_err(arizona->dev, "Failed to report headphone: %d\n", ret);
-
- if (info->mic)
- arizona_start_mic(info);
-
- info->hpdet_active = false;
-}
-
-static void arizona_start_hpdet_acc_id(struct arizona_extcon_info *info)
-{
- struct arizona *arizona = info->arizona;
- int hp_reading = 32;
- bool mic;
- int ret;
-
- dev_dbg(arizona->dev, "Starting identification via HPDET\n");
-
- /* Make sure we keep the device enabled during the measurement */
- pm_runtime_get_sync(info->dev);
-
- info->hpdet_active = true;
-
- arizona_extcon_hp_clamp(info, true);
-
- ret = regmap_update_bits(arizona->regmap,
- ARIZONA_ACCESSORY_DETECT_MODE_1,
- ARIZONA_ACCDET_SRC | ARIZONA_ACCDET_MODE_MASK,
- info->micd_modes[0].src |
- arizona->pdata.hpdet_channel);
- if (ret != 0) {
- dev_err(arizona->dev, "Failed to set HPDET mode: %d\n", ret);
- goto err;
- }
-
- if (arizona->pdata.hpdet_acc_id_line) {
- ret = regmap_update_bits(arizona->regmap,
- ARIZONA_HEADPHONE_DETECT_1,
- ARIZONA_HP_POLL, ARIZONA_HP_POLL);
- if (ret != 0) {
- dev_err(arizona->dev,
- "Can't start HPDETL measurement: %d\n",
- ret);
- goto err;
- }
- } else {
- arizona_hpdet_do_id(info, &hp_reading, &mic);
- }
-
- return;
-
-err:
- /* Just report headphone */
- ret = extcon_set_state_sync(info->edev, EXTCON_JACK_HEADPHONE, true);
- if (ret != 0)
- dev_err(arizona->dev, "Failed to report headphone: %d\n", ret);
-
- info->hpdet_active = false;
-}
-
-static void arizona_micd_timeout_work(struct work_struct *work)
-{
- struct arizona_extcon_info *info = container_of(work,
- struct arizona_extcon_info,
- micd_timeout_work.work);
-
- mutex_lock(&info->lock);
-
- dev_dbg(info->arizona->dev, "MICD timed out, reporting HP\n");
-
- info->detecting = false;
-
- arizona_identify_headphone(info);
-
- mutex_unlock(&info->lock);
-}
-
-static int arizona_micd_adc_read(struct arizona_extcon_info *info)
-{
- struct arizona *arizona = info->arizona;
- unsigned int val;
- int ret;
-
- /* Must disable MICD before we read the ADCVAL */
- regmap_update_bits(arizona->regmap, ARIZONA_MIC_DETECT_1,
- ARIZONA_MICD_ENA, 0);
-
- ret = regmap_read(arizona->regmap, ARIZONA_MIC_DETECT_4, &val);
- if (ret != 0) {
- dev_err(arizona->dev,
- "Failed to read MICDET_ADCVAL: %d\n", ret);
- return ret;
- }
-
- dev_dbg(arizona->dev, "MICDET_ADCVAL: %x\n", val);
-
- val &= ARIZONA_MICDET_ADCVAL_MASK;
- if (val < ARRAY_SIZE(arizona_micd_levels))
- val = arizona_micd_levels[val];
- else
- val = INT_MAX;
-
- if (val <= QUICK_HEADPHONE_MAX_OHM)
- val = ARIZONA_MICD_STS | ARIZONA_MICD_LVL_0;
- else if (val <= MICROPHONE_MIN_OHM)
- val = ARIZONA_MICD_STS | ARIZONA_MICD_LVL_1;
- else if (val <= MICROPHONE_MAX_OHM)
- val = ARIZONA_MICD_STS | ARIZONA_MICD_LVL_8;
- else
- val = ARIZONA_MICD_LVL_8;
-
- return val;
-}
-
-static int arizona_micd_read(struct arizona_extcon_info *info)
-{
- struct arizona *arizona = info->arizona;
- unsigned int val = 0;
- int ret, i;
-
- for (i = 0; i < 10 && !(val & MICD_LVL_0_TO_8); i++) {
- ret = regmap_read(arizona->regmap, ARIZONA_MIC_DETECT_3, &val);
- if (ret != 0) {
- dev_err(arizona->dev,
- "Failed to read MICDET: %d\n", ret);
- return ret;
- }
-
- dev_dbg(arizona->dev, "MICDET: %x\n", val);
-
- if (!(val & ARIZONA_MICD_VALID)) {
- dev_warn(arizona->dev,
- "Microphone detection state invalid\n");
- return -EINVAL;
- }
- }
-
- if (i == 10 && !(val & MICD_LVL_0_TO_8)) {
- dev_err(arizona->dev, "Failed to get valid MICDET value\n");
- return -EINVAL;
- }
-
- return val;
-}
-
-static int arizona_micdet_reading(void *priv)
-{
- struct arizona_extcon_info *info = priv;
- struct arizona *arizona = info->arizona;
- int ret, val;
-
- if (info->detecting && arizona->pdata.micd_software_compare)
- ret = arizona_micd_adc_read(info);
- else
- ret = arizona_micd_read(info);
- if (ret < 0)
- return ret;
-
- val = ret;
-
- /* Due to jack detect this should never happen */
- if (!(val & ARIZONA_MICD_STS)) {
- dev_warn(arizona->dev, "Detected open circuit\n");
- info->mic = false;
- info->detecting = false;
- arizona_identify_headphone(info);
- return 0;
- }
-
- /* If we got a high impedence we should have a headset, report it. */
- if (val & ARIZONA_MICD_LVL_8) {
- info->mic = true;
- info->detecting = false;
-
- arizona_identify_headphone(info);
-
- ret = extcon_set_state_sync(info->edev,
- EXTCON_JACK_MICROPHONE, true);
- if (ret != 0)
- dev_err(arizona->dev, "Headset report failed: %d\n",
- ret);
-
- /* Don't need to regulate for button detection */
- ret = regulator_allow_bypass(info->micvdd, true);
- if (ret != 0) {
- dev_err(arizona->dev, "Failed to bypass MICVDD: %d\n",
- ret);
- }
-
- return 0;
- }
-
- /* If we detected a lower impedence during initial startup
- * then we probably have the wrong polarity, flip it. Don't
- * do this for the lowest impedences to speed up detection of
- * plain headphones. If both polarities report a low
- * impedence then give up and report headphones.
- */
- if (val & MICD_LVL_1_TO_7) {
- if (info->jack_flips >= info->micd_num_modes * 10) {
- dev_dbg(arizona->dev, "Detected HP/line\n");
-
- info->detecting = false;
-
- arizona_identify_headphone(info);
- } else {
- info->micd_mode++;
- if (info->micd_mode == info->micd_num_modes)
- info->micd_mode = 0;
- arizona_extcon_set_mode(info, info->micd_mode);
-
- info->jack_flips++;
-
- if (arizona->pdata.micd_software_compare)
- regmap_update_bits(arizona->regmap,
- ARIZONA_MIC_DETECT_1,
- ARIZONA_MICD_ENA,
- ARIZONA_MICD_ENA);
-
- queue_delayed_work(system_power_efficient_wq,
- &info->micd_timeout_work,
- msecs_to_jiffies(arizona->pdata.micd_timeout));
- }
-
- return 0;
- }
-
- /*
- * If we're still detecting and we detect a short then we've
- * got a headphone.
- */
- dev_dbg(arizona->dev, "Headphone detected\n");
- info->detecting = false;
-
- arizona_identify_headphone(info);
-
- return 0;
-}
-
-static int arizona_button_reading(void *priv)
-{
- struct arizona_extcon_info *info = priv;
- struct arizona *arizona = info->arizona;
- int val, key, lvl, i;
-
- val = arizona_micd_read(info);
- if (val < 0)
- return val;
-
- /*
- * If we're still detecting and we detect a short then we've
- * got a headphone. Otherwise it's a button press.
- */
- if (val & MICD_LVL_0_TO_7) {
- if (info->mic) {
- dev_dbg(arizona->dev, "Mic button detected\n");
-
- lvl = val & ARIZONA_MICD_LVL_MASK;
- lvl >>= ARIZONA_MICD_LVL_SHIFT;
-
- for (i = 0; i < info->num_micd_ranges; i++)
- input_report_key(info->input,
- info->micd_ranges[i].key, 0);
-
- if (lvl && ffs(lvl) - 1 < info->num_micd_ranges) {
- key = info->micd_ranges[ffs(lvl) - 1].key;
- input_report_key(info->input, key, 1);
- input_sync(info->input);
- } else {
- dev_err(arizona->dev, "Button out of range\n");
- }
- } else {
- dev_warn(arizona->dev, "Button with no mic: %x\n",
- val);
- }
- } else {
- dev_dbg(arizona->dev, "Mic button released\n");
- for (i = 0; i < info->num_micd_ranges; i++)
- input_report_key(info->input,
- info->micd_ranges[i].key, 0);
- input_sync(info->input);
- arizona_extcon_pulse_micbias(info);
- }
-
- return 0;
-}
-
-static void arizona_micd_detect(struct work_struct *work)
-{
- struct arizona_extcon_info *info = container_of(work,
- struct arizona_extcon_info,
- micd_detect_work.work);
- struct arizona *arizona = info->arizona;
- int ret;
-
- cancel_delayed_work_sync(&info->micd_timeout_work);
-
- mutex_lock(&info->lock);
-
- /* If the cable was removed while measuring ignore the result */
- ret = extcon_get_state(info->edev, EXTCON_MECHANICAL);
- if (ret < 0) {
- dev_err(arizona->dev, "Failed to check cable state: %d\n",
- ret);
- mutex_unlock(&info->lock);
- return;
- } else if (!ret) {
- dev_dbg(arizona->dev, "Ignoring MICDET for removed cable\n");
- mutex_unlock(&info->lock);
- return;
- }
-
- if (info->detecting)
- arizona_micdet_reading(info);
- else
- arizona_button_reading(info);
-
- pm_runtime_mark_last_busy(info->dev);
- mutex_unlock(&info->lock);
-}
-
-static irqreturn_t arizona_micdet(int irq, void *data)
-{
- struct arizona_extcon_info *info = data;
- struct arizona *arizona = info->arizona;
- int debounce = arizona->pdata.micd_detect_debounce;
-
- cancel_delayed_work_sync(&info->micd_detect_work);
- cancel_delayed_work_sync(&info->micd_timeout_work);
-
- mutex_lock(&info->lock);
- if (!info->detecting)
- debounce = 0;
- mutex_unlock(&info->lock);
-
- if (debounce)
- queue_delayed_work(system_power_efficient_wq,
- &info->micd_detect_work,
- msecs_to_jiffies(debounce));
- else
- arizona_micd_detect(&info->micd_detect_work.work);
-
- return IRQ_HANDLED;
-}
-
-static void arizona_hpdet_work(struct work_struct *work)
-{
- struct arizona_extcon_info *info = container_of(work,
- struct arizona_extcon_info,
- hpdet_work.work);
-
- mutex_lock(&info->lock);
- arizona_start_hpdet_acc_id(info);
- mutex_unlock(&info->lock);
-}
-
-static int arizona_hpdet_wait(struct arizona_extcon_info *info)
-{
- struct arizona *arizona = info->arizona;
- unsigned int val;
- int i, ret;
-
- for (i = 0; i < ARIZONA_HPDET_WAIT_COUNT; i++) {
- ret = regmap_read(arizona->regmap, ARIZONA_HEADPHONE_DETECT_2,
- &val);
- if (ret) {
- dev_err(arizona->dev,
- "Failed to read HPDET state: %d\n", ret);
- return ret;
- }
-
- switch (info->hpdet_ip_version) {
- case 0:
- if (val & ARIZONA_HP_DONE)
- return 0;
- break;
- default:
- if (val & ARIZONA_HP_DONE_B)
- return 0;
- break;
- }
-
- msleep(ARIZONA_HPDET_WAIT_DELAY_MS);
- }
-
- dev_warn(arizona->dev, "HPDET did not appear to complete\n");
-
- return -ETIMEDOUT;
-}
-
-static irqreturn_t arizona_jackdet(int irq, void *data)
-{
- struct arizona_extcon_info *info = data;
- struct arizona *arizona = info->arizona;
- unsigned int val, present, mask;
- bool cancelled_hp, cancelled_mic;
- int ret, i;
-
- cancelled_hp = cancel_delayed_work_sync(&info->hpdet_work);
- cancelled_mic = cancel_delayed_work_sync(&info->micd_timeout_work);
-
- pm_runtime_get_sync(info->dev);
-
- mutex_lock(&info->lock);
-
- if (info->micd_clamp) {
- mask = ARIZONA_MICD_CLAMP_STS;
- present = 0;
- } else {
- mask = ARIZONA_JD1_STS;
- if (arizona->pdata.jd_invert)
- present = 0;
- else
- present = ARIZONA_JD1_STS;
- }
-
- ret = regmap_read(arizona->regmap, ARIZONA_AOD_IRQ_RAW_STATUS, &val);
- if (ret != 0) {
- dev_err(arizona->dev, "Failed to read jackdet status: %d\n",
- ret);
- mutex_unlock(&info->lock);
- pm_runtime_put_autosuspend(info->dev);
- return IRQ_NONE;
- }
-
- val &= mask;
- if (val == info->last_jackdet) {
- dev_dbg(arizona->dev, "Suppressing duplicate JACKDET\n");
- if (cancelled_hp)
- queue_delayed_work(system_power_efficient_wq,
- &info->hpdet_work,
- msecs_to_jiffies(HPDET_DEBOUNCE));
-
- if (cancelled_mic) {
- int micd_timeout = arizona->pdata.micd_timeout;
-
- queue_delayed_work(system_power_efficient_wq,
- &info->micd_timeout_work,
- msecs_to_jiffies(micd_timeout));
- }
-
- goto out;
- }
- info->last_jackdet = val;
-
- if (info->last_jackdet == present) {
- dev_dbg(arizona->dev, "Detected jack\n");
- ret = extcon_set_state_sync(info->edev,
- EXTCON_MECHANICAL, true);
-
- if (ret != 0)
- dev_err(arizona->dev, "Mechanical report failed: %d\n",
- ret);
-
- info->detecting = true;
- info->mic = false;
- info->jack_flips = 0;
-
- if (!arizona->pdata.hpdet_acc_id) {
- arizona_start_mic(info);
- } else {
- queue_delayed_work(system_power_efficient_wq,
- &info->hpdet_work,
- msecs_to_jiffies(HPDET_DEBOUNCE));
- }
-
- if (info->micd_clamp || !arizona->pdata.jd_invert)
- regmap_update_bits(arizona->regmap,
- ARIZONA_JACK_DETECT_DEBOUNCE,
- ARIZONA_MICD_CLAMP_DB |
- ARIZONA_JD1_DB, 0);
- } else {
- dev_dbg(arizona->dev, "Detected jack removal\n");
-
- arizona_stop_mic(info);
-
- info->num_hpdet_res = 0;
- for (i = 0; i < ARRAY_SIZE(info->hpdet_res); i++)
- info->hpdet_res[i] = 0;
- info->mic = false;
- info->hpdet_done = false;
- info->hpdet_retried = false;
-
- for (i = 0; i < info->num_micd_ranges; i++)
- input_report_key(info->input,
- info->micd_ranges[i].key, 0);
- input_sync(info->input);
-
- for (i = 0; i < ARRAY_SIZE(arizona_cable) - 1; i++) {
- ret = extcon_set_state_sync(info->edev,
- arizona_cable[i], false);
- if (ret != 0)
- dev_err(arizona->dev,
- "Removal report failed: %d\n", ret);
- }
-
- /*
- * If the jack was removed during a headphone detection we
- * need to wait for the headphone detection to finish, as
- * it can not be aborted. We don't want to be able to start
- * a new headphone detection from a fresh insert until this
- * one is finished.
- */
- arizona_hpdet_wait(info);
-
- regmap_update_bits(arizona->regmap,
- ARIZONA_JACK_DETECT_DEBOUNCE,
- ARIZONA_MICD_CLAMP_DB | ARIZONA_JD1_DB,
- ARIZONA_MICD_CLAMP_DB | ARIZONA_JD1_DB);
- }
-
-out:
- /* Clear trig_sts to make sure DCVDD is not forced up */
- regmap_write(arizona->regmap, ARIZONA_AOD_WKUP_AND_TRIG,
- ARIZONA_MICD_CLAMP_FALL_TRIG_STS |
- ARIZONA_MICD_CLAMP_RISE_TRIG_STS |
- ARIZONA_JD1_FALL_TRIG_STS |
- ARIZONA_JD1_RISE_TRIG_STS);
-
- mutex_unlock(&info->lock);
-
- pm_runtime_mark_last_busy(info->dev);
- pm_runtime_put_autosuspend(info->dev);
-
- return IRQ_HANDLED;
-}
-
-/* Map a level onto a slot in the register bank */
-static void arizona_micd_set_level(struct arizona *arizona, int index,
- unsigned int level)
-{
- int reg;
- unsigned int mask;
-
- reg = ARIZONA_MIC_DETECT_LEVEL_4 - (index / 2);
-
- if (!(index % 2)) {
- mask = 0x3f00;
- level <<= 8;
- } else {
- mask = 0x3f;
- }
-
- /* Program the level itself */
- regmap_update_bits(arizona->regmap, reg, mask, level);
-}
-
-static int arizona_extcon_get_micd_configs(struct device *dev,
- struct arizona *arizona)
-{
- const char * const prop = "wlf,micd-configs";
- const int entries_per_config = 3;
- struct arizona_micd_config *micd_configs;
- int nconfs, ret;
- int i, j;
- u32 *vals;
-
- nconfs = device_property_count_u32(arizona->dev, prop);
- if (nconfs <= 0)
- return 0;
-
- vals = kcalloc(nconfs, sizeof(u32), GFP_KERNEL);
- if (!vals)
- return -ENOMEM;
-
- ret = device_property_read_u32_array(arizona->dev, prop, vals, nconfs);
- if (ret < 0)
- goto out;
-
- nconfs /= entries_per_config;
- micd_configs = devm_kcalloc(dev, nconfs, sizeof(*micd_configs),
- GFP_KERNEL);
- if (!micd_configs) {
- ret = -ENOMEM;
- goto out;
- }
-
- for (i = 0, j = 0; i < nconfs; ++i) {
- micd_configs[i].src = vals[j++] ? ARIZONA_ACCDET_SRC : 0;
- micd_configs[i].bias = vals[j++];
- micd_configs[i].gpio = vals[j++];
- }
-
- arizona->pdata.micd_configs = micd_configs;
- arizona->pdata.num_micd_configs = nconfs;
-
-out:
- kfree(vals);
- return ret;
-}
-
-static int arizona_extcon_device_get_pdata(struct device *dev,
- struct arizona *arizona)
-{
- struct arizona_pdata *pdata = &arizona->pdata;
- unsigned int val = ARIZONA_ACCDET_MODE_HPL;
- int ret;
-
- device_property_read_u32(arizona->dev, "wlf,hpdet-channel", &val);
- switch (val) {
- case ARIZONA_ACCDET_MODE_HPL:
- case ARIZONA_ACCDET_MODE_HPR:
- pdata->hpdet_channel = val;
- break;
- default:
- dev_err(arizona->dev,
- "Wrong wlf,hpdet-channel DT value %d\n", val);
- pdata->hpdet_channel = ARIZONA_ACCDET_MODE_HPL;
- }
-
- device_property_read_u32(arizona->dev, "wlf,micd-detect-debounce",
- &pdata->micd_detect_debounce);
-
- device_property_read_u32(arizona->dev, "wlf,micd-bias-start-time",
- &pdata->micd_bias_start_time);
-
- device_property_read_u32(arizona->dev, "wlf,micd-rate",
- &pdata->micd_rate);
-
- device_property_read_u32(arizona->dev, "wlf,micd-dbtime",
- &pdata->micd_dbtime);
-
- device_property_read_u32(arizona->dev, "wlf,micd-timeout-ms",
- &pdata->micd_timeout);
-
- pdata->micd_force_micbias = device_property_read_bool(arizona->dev,
- "wlf,micd-force-micbias");
-
- pdata->micd_software_compare = device_property_read_bool(arizona->dev,
- "wlf,micd-software-compare");
-
- pdata->jd_invert = device_property_read_bool(arizona->dev,
- "wlf,jd-invert");
-
- device_property_read_u32(arizona->dev, "wlf,gpsw", &pdata->gpsw);
-
- pdata->jd_gpio5 = device_property_read_bool(arizona->dev,
- "wlf,use-jd2");
- pdata->jd_gpio5_nopull = device_property_read_bool(arizona->dev,
- "wlf,use-jd2-nopull");
-
- ret = arizona_extcon_get_micd_configs(dev, arizona);
- if (ret < 0)
- dev_err(arizona->dev, "Failed to read micd configs: %d\n", ret);
-
- return 0;
-}
-
-static int arizona_extcon_probe(struct platform_device *pdev)
-{
- struct arizona *arizona = dev_get_drvdata(pdev->dev.parent);
- struct arizona_pdata *pdata = &arizona->pdata;
- struct arizona_extcon_info *info;
- unsigned int val;
- unsigned int clamp_mode;
- int jack_irq_fall, jack_irq_rise;
- int ret, mode, i, j;
-
- if (!arizona->dapm || !arizona->dapm->card)
- return -EPROBE_DEFER;
-
- info = devm_kzalloc(&pdev->dev, sizeof(*info), GFP_KERNEL);
- if (!info)
- return -ENOMEM;
-
- if (!dev_get_platdata(arizona->dev))
- arizona_extcon_device_get_pdata(&pdev->dev, arizona);
-
- info->micvdd = devm_regulator_get(&pdev->dev, "MICVDD");
- if (IS_ERR(info->micvdd)) {
- ret = PTR_ERR(info->micvdd);
- dev_err(arizona->dev, "Failed to get MICVDD: %d\n", ret);
- return ret;
- }
-
- mutex_init(&info->lock);
- info->arizona = arizona;
- info->dev = &pdev->dev;
- info->last_jackdet = ~(ARIZONA_MICD_CLAMP_STS | ARIZONA_JD1_STS);
- INIT_DELAYED_WORK(&info->hpdet_work, arizona_hpdet_work);
- INIT_DELAYED_WORK(&info->micd_detect_work, arizona_micd_detect);
- INIT_DELAYED_WORK(&info->micd_timeout_work, arizona_micd_timeout_work);
- platform_set_drvdata(pdev, info);
-
- switch (arizona->type) {
- case WM5102:
- switch (arizona->rev) {
- case 0:
- info->micd_reva = true;
- break;
- default:
- info->micd_clamp = true;
- info->hpdet_ip_version = 1;
- break;
- }
- break;
- case WM5110:
- case WM8280:
- switch (arizona->rev) {
- case 0 ... 2:
- break;
- default:
- info->micd_clamp = true;
- info->hpdet_ip_version = 2;
- break;
- }
- break;
- case WM8998:
- case WM1814:
- info->micd_clamp = true;
- info->hpdet_ip_version = 2;
- break;
- default:
- break;
- }
-
- info->edev = devm_extcon_dev_allocate(&pdev->dev, arizona_cable);
- if (IS_ERR(info->edev)) {
- dev_err(&pdev->dev, "failed to allocate extcon device\n");
- return -ENOMEM;
- }
-
- ret = devm_extcon_dev_register(&pdev->dev, info->edev);
- if (ret < 0) {
- dev_err(arizona->dev, "extcon_dev_register() failed: %d\n",
- ret);
- return ret;
- }
-
- info->input = devm_input_allocate_device(&pdev->dev);
- if (!info->input) {
- dev_err(arizona->dev, "Can't allocate input dev\n");
- ret = -ENOMEM;
- goto err_register;
- }
-
- info->input->name = "Headset";
- info->input->phys = "arizona/extcon";
-
- if (!pdata->micd_timeout)
- pdata->micd_timeout = DEFAULT_MICD_TIMEOUT;
-
- if (pdata->num_micd_configs) {
- info->micd_modes = pdata->micd_configs;
- info->micd_num_modes = pdata->num_micd_configs;
- } else {
- info->micd_modes = micd_default_modes;
- info->micd_num_modes = ARRAY_SIZE(micd_default_modes);
- }
-
- if (arizona->pdata.gpsw > 0)
- regmap_update_bits(arizona->regmap, ARIZONA_GP_SWITCH_1,
- ARIZONA_SW1_MODE_MASK, arizona->pdata.gpsw);
-
- 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, pdata->micd_pol_gpio,
- mode, "MICD polarity");
- if (ret != 0) {
- dev_err(arizona->dev, "Failed to request GPIO%d: %d\n",
- 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;
- else
- mode = GPIOD_OUT_LOW;
-
- /* We can't use devm here because we need to do the get
- * against the MFD device, as that is where the of_node
- * will reside, but if we devm against that the GPIO
- * will not be freed if the extcon driver is unloaded.
- */
- info->micd_pol_gpio = gpiod_get_optional(arizona->dev,
- "wlf,micd-pol",
- GPIOD_OUT_LOW);
- if (IS_ERR(info->micd_pol_gpio)) {
- ret = PTR_ERR(info->micd_pol_gpio);
- dev_err(arizona->dev,
- "Failed to get microphone polarity GPIO: %d\n",
- ret);
- goto err_register;
- }
- }
-
- if (arizona->pdata.hpdet_id_gpio > 0) {
- ret = devm_gpio_request_one(&pdev->dev,
- arizona->pdata.hpdet_id_gpio,
- GPIOF_OUT_INIT_LOW,
- "HPDET");
- if (ret != 0) {
- dev_err(arizona->dev, "Failed to request GPIO%d: %d\n",
- arizona->pdata.hpdet_id_gpio, ret);
- goto err_gpio;
- }
- }
-
- if (arizona->pdata.micd_bias_start_time)
- regmap_update_bits(arizona->regmap, ARIZONA_MIC_DETECT_1,
- ARIZONA_MICD_BIAS_STARTTIME_MASK,
- arizona->pdata.micd_bias_start_time
- << ARIZONA_MICD_BIAS_STARTTIME_SHIFT);
-
- if (arizona->pdata.micd_rate)
- regmap_update_bits(arizona->regmap, ARIZONA_MIC_DETECT_1,
- ARIZONA_MICD_RATE_MASK,
- arizona->pdata.micd_rate
- << ARIZONA_MICD_RATE_SHIFT);
-
- switch (arizona->pdata.micd_dbtime) {
- case MICD_DBTIME_FOUR_READINGS:
- regmap_update_bits(arizona->regmap, ARIZONA_MIC_DETECT_1,
- ARIZONA_MICD_DBTIME_MASK,
- ARIZONA_MICD_DBTIME);
- break;
- case MICD_DBTIME_TWO_READINGS:
- regmap_update_bits(arizona->regmap, ARIZONA_MIC_DETECT_1,
- ARIZONA_MICD_DBTIME_MASK, 0);
- break;
- default:
- break;
- }
-
- BUILD_BUG_ON(ARRAY_SIZE(arizona_micd_levels) <
- ARIZONA_NUM_MICD_BUTTON_LEVELS);
-
- if (arizona->pdata.num_micd_ranges) {
- info->micd_ranges = pdata->micd_ranges;
- info->num_micd_ranges = pdata->num_micd_ranges;
- } else {
- info->micd_ranges = micd_default_ranges;
- info->num_micd_ranges = ARRAY_SIZE(micd_default_ranges);
- }
-
- if (arizona->pdata.num_micd_ranges > ARIZONA_MAX_MICD_RANGE) {
- dev_err(arizona->dev, "Too many MICD ranges: %d\n",
- arizona->pdata.num_micd_ranges);
- }
-
- if (info->num_micd_ranges > 1) {
- for (i = 1; i < info->num_micd_ranges; i++) {
- if (info->micd_ranges[i - 1].max >
- info->micd_ranges[i].max) {
- dev_err(arizona->dev,
- "MICD ranges must be sorted\n");
- ret = -EINVAL;
- goto err_gpio;
- }
- }
- }
-
- /* Disable all buttons by default */
- regmap_update_bits(arizona->regmap, ARIZONA_MIC_DETECT_2,
- ARIZONA_MICD_LVL_SEL_MASK, 0x81);
-
- /* Set up all the buttons the user specified */
- for (i = 0; i < info->num_micd_ranges; i++) {
- for (j = 0; j < ARIZONA_NUM_MICD_BUTTON_LEVELS; j++)
- if (arizona_micd_levels[j] >= info->micd_ranges[i].max)
- break;
-
- if (j == ARIZONA_NUM_MICD_BUTTON_LEVELS) {
- dev_err(arizona->dev, "Unsupported MICD level %d\n",
- info->micd_ranges[i].max);
- ret = -EINVAL;
- goto err_gpio;
- }
-
- dev_dbg(arizona->dev, "%d ohms for MICD threshold %d\n",
- arizona_micd_levels[j], i);
-
- arizona_micd_set_level(arizona, i, j);
- input_set_capability(info->input, EV_KEY,
- info->micd_ranges[i].key);
-
- /* Enable reporting of that range */
- regmap_update_bits(arizona->regmap, ARIZONA_MIC_DETECT_2,
- 1 << i, 1 << i);
- }
-
- /* Set all the remaining keys to a maximum */
- for (; i < ARIZONA_MAX_MICD_RANGE; i++)
- arizona_micd_set_level(arizona, i, 0x3f);
-
- /*
- * If we have a clamp use it, activating in conjunction with
- * GPIO5 if that is connected for jack detect operation.
- */
- if (info->micd_clamp) {
- if (arizona->pdata.jd_gpio5) {
- /* Put the GPIO into input mode with optional pull */
- val = 0xc101;
- if (arizona->pdata.jd_gpio5_nopull)
- val &= ~ARIZONA_GPN_PU;
-
- regmap_write(arizona->regmap, ARIZONA_GPIO5_CTRL,
- val);
-
- if (arizona->pdata.jd_invert)
- clamp_mode = ARIZONA_MICD_CLAMP_MODE_JDH_GP5H;
- else
- clamp_mode = ARIZONA_MICD_CLAMP_MODE_JDL_GP5H;
- } else {
- if (arizona->pdata.jd_invert)
- clamp_mode = ARIZONA_MICD_CLAMP_MODE_JDH;
- else
- clamp_mode = ARIZONA_MICD_CLAMP_MODE_JDL;
- }
-
- regmap_update_bits(arizona->regmap,
- ARIZONA_MICD_CLAMP_CONTROL,
- ARIZONA_MICD_CLAMP_MODE_MASK, clamp_mode);
-
- regmap_update_bits(arizona->regmap,
- ARIZONA_JACK_DETECT_DEBOUNCE,
- ARIZONA_MICD_CLAMP_DB,
- ARIZONA_MICD_CLAMP_DB);
- }
-
- arizona_extcon_set_mode(info, 0);
-
- pm_runtime_enable(&pdev->dev);
- pm_runtime_idle(&pdev->dev);
- pm_runtime_get_sync(&pdev->dev);
-
- if (info->micd_clamp) {
- jack_irq_rise = ARIZONA_IRQ_MICD_CLAMP_RISE;
- jack_irq_fall = ARIZONA_IRQ_MICD_CLAMP_FALL;
- } else {
- jack_irq_rise = ARIZONA_IRQ_JD_RISE;
- jack_irq_fall = ARIZONA_IRQ_JD_FALL;
- }
-
- ret = arizona_request_irq(arizona, jack_irq_rise,
- "JACKDET rise", arizona_jackdet, info);
- if (ret != 0) {
- dev_err(&pdev->dev, "Failed to get JACKDET rise IRQ: %d\n",
- ret);
- goto err_gpio;
- }
-
- ret = arizona_set_irq_wake(arizona, jack_irq_rise, 1);
- if (ret != 0) {
- dev_err(&pdev->dev, "Failed to set JD rise IRQ wake: %d\n",
- ret);
- goto err_rise;
- }
-
- ret = arizona_request_irq(arizona, jack_irq_fall,
- "JACKDET fall", arizona_jackdet, info);
- if (ret != 0) {
- dev_err(&pdev->dev, "Failed to get JD fall IRQ: %d\n", ret);
- goto err_rise_wake;
- }
-
- ret = arizona_set_irq_wake(arizona, jack_irq_fall, 1);
- if (ret != 0) {
- dev_err(&pdev->dev, "Failed to set JD fall IRQ wake: %d\n",
- ret);
- goto err_fall;
- }
-
- ret = arizona_request_irq(arizona, ARIZONA_IRQ_MICDET,
- "MICDET", arizona_micdet, info);
- if (ret != 0) {
- dev_err(&pdev->dev, "Failed to get MICDET IRQ: %d\n", ret);
- goto err_fall_wake;
- }
-
- ret = arizona_request_irq(arizona, ARIZONA_IRQ_HPDET,
- "HPDET", arizona_hpdet_irq, info);
- if (ret != 0) {
- dev_err(&pdev->dev, "Failed to get HPDET IRQ: %d\n", ret);
- goto err_micdet;
- }
-
- arizona_clk32k_enable(arizona);
- regmap_update_bits(arizona->regmap, ARIZONA_JACK_DETECT_DEBOUNCE,
- ARIZONA_JD1_DB, ARIZONA_JD1_DB);
- regmap_update_bits(arizona->regmap, ARIZONA_JACK_DETECT_ANALOGUE,
- ARIZONA_JD1_ENA, ARIZONA_JD1_ENA);
-
- ret = regulator_allow_bypass(info->micvdd, true);
- if (ret != 0)
- dev_warn(arizona->dev, "Failed to set MICVDD to bypass: %d\n",
- ret);
-
- pm_runtime_put(&pdev->dev);
-
- ret = input_register_device(info->input);
- if (ret) {
- dev_err(&pdev->dev, "Can't register input device: %d\n", ret);
- goto err_hpdet;
- }
-
- return 0;
-
-err_hpdet:
- arizona_free_irq(arizona, ARIZONA_IRQ_HPDET, info);
-err_micdet:
- arizona_free_irq(arizona, ARIZONA_IRQ_MICDET, info);
-err_fall_wake:
- arizona_set_irq_wake(arizona, jack_irq_fall, 0);
-err_fall:
- arizona_free_irq(arizona, jack_irq_fall, info);
-err_rise_wake:
- arizona_set_irq_wake(arizona, jack_irq_rise, 0);
-err_rise:
- arizona_free_irq(arizona, jack_irq_rise, info);
-err_gpio:
- gpiod_put(info->micd_pol_gpio);
-err_register:
- pm_runtime_disable(&pdev->dev);
- return ret;
-}
-
-static int arizona_extcon_remove(struct platform_device *pdev)
-{
- struct arizona_extcon_info *info = platform_get_drvdata(pdev);
- struct arizona *arizona = info->arizona;
- int jack_irq_rise, jack_irq_fall;
- bool change;
- int ret;
-
- ret = regmap_update_bits_check(arizona->regmap, ARIZONA_MIC_DETECT_1,
- ARIZONA_MICD_ENA, 0,
- &change);
- if (ret < 0) {
- dev_err(&pdev->dev, "Failed to disable micd on remove: %d\n",
- ret);
- } else if (change) {
- regulator_disable(info->micvdd);
- pm_runtime_put(info->dev);
- }
-
- gpiod_put(info->micd_pol_gpio);
-
- pm_runtime_disable(&pdev->dev);
-
- regmap_update_bits(arizona->regmap,
- ARIZONA_MICD_CLAMP_CONTROL,
- ARIZONA_MICD_CLAMP_MODE_MASK, 0);
-
- if (info->micd_clamp) {
- jack_irq_rise = ARIZONA_IRQ_MICD_CLAMP_RISE;
- jack_irq_fall = ARIZONA_IRQ_MICD_CLAMP_FALL;
- } else {
- jack_irq_rise = ARIZONA_IRQ_JD_RISE;
- jack_irq_fall = ARIZONA_IRQ_JD_FALL;
- }
-
- arizona_set_irq_wake(arizona, jack_irq_rise, 0);
- arizona_set_irq_wake(arizona, jack_irq_fall, 0);
- arizona_free_irq(arizona, ARIZONA_IRQ_HPDET, info);
- arizona_free_irq(arizona, ARIZONA_IRQ_MICDET, info);
- arizona_free_irq(arizona, jack_irq_rise, info);
- arizona_free_irq(arizona, jack_irq_fall, info);
- cancel_delayed_work_sync(&info->hpdet_work);
- regmap_update_bits(arizona->regmap, ARIZONA_JACK_DETECT_ANALOGUE,
- ARIZONA_JD1_ENA, 0);
- arizona_clk32k_disable(arizona);
-
- return 0;
-}
-
-static struct platform_driver arizona_extcon_driver = {
- .driver = {
- .name = "arizona-extcon",
- },
- .probe = arizona_extcon_probe,
- .remove = arizona_extcon_remove,
-};
-
-module_platform_driver(arizona_extcon_driver);
-
-MODULE_DESCRIPTION("Arizona Extcon driver");
-MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>");
-MODULE_LICENSE("GPL");
-MODULE_ALIAS("platform:extcon-arizona");
diff --git a/drivers/extcon/extcon-axp288.c b/drivers/extcon/extcon-axp288.c
index a7f216191493..180be768c215 100644
--- a/drivers/extcon/extcon-axp288.c
+++ b/drivers/extcon/extcon-axp288.c
@@ -24,6 +24,7 @@
#include <asm/cpu_device_id.h>
#include <asm/intel-family.h>
+#include <asm/iosf_mbi.h>
/* Power source status register */
#define PS_STAT_VBUS_TRIGGER BIT(0)
@@ -107,7 +108,7 @@ struct axp288_extcon_info {
};
static const struct x86_cpu_id cherry_trail_cpu_ids[] = {
- { X86_VENDOR_INTEL, 6, INTEL_FAM6_ATOM_AIRMONT, X86_FEATURE_ANY },
+ X86_MATCH_INTEL_FAM6_MODEL(ATOM_AIRMONT, NULL),
{}
};
@@ -215,6 +216,10 @@ static int axp288_handle_chrg_det_event(struct axp288_extcon_info *info)
unsigned int cable = info->previous_cable;
bool vbus_attach = false;
+ ret = iosf_mbi_block_punit_i2c_access();
+ if (ret < 0)
+ return ret;
+
vbus_attach = axp288_get_vbus_attach(info);
if (!vbus_attach)
goto no_vbus;
@@ -253,6 +258,8 @@ static int axp288_handle_chrg_det_event(struct axp288_extcon_info *info)
}
no_vbus:
+ iosf_mbi_unblock_punit_i2c_access();
+
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);
@@ -275,6 +282,8 @@ no_vbus:
return 0;
dev_det_ret:
+ iosf_mbi_unblock_punit_i2c_access();
+
if (ret < 0)
dev_err(info->dev, "failed to detect BC Mod\n");
@@ -305,13 +314,23 @@ static irqreturn_t axp288_extcon_isr(int irq, void *data)
return IRQ_HANDLED;
}
-static void axp288_extcon_enable(struct axp288_extcon_info *info)
+static int axp288_extcon_enable(struct axp288_extcon_info *info)
{
+ int ret = 0;
+
+ ret = iosf_mbi_block_punit_i2c_access();
+ if (ret < 0)
+ return ret;
+
regmap_update_bits(info->regmap, AXP288_BC_GLOBAL_REG,
BC_GLOBAL_RUN, 0);
/* Enable the charger detection logic */
regmap_update_bits(info->regmap, AXP288_BC_GLOBAL_REG,
BC_GLOBAL_RUN, BC_GLOBAL_RUN);
+
+ iosf_mbi_unblock_punit_i2c_access();
+
+ return ret;
}
static void axp288_put_role_sw(void *data)
@@ -375,8 +394,8 @@ static int axp288_extcon_probe(struct platform_device *pdev)
if (adev) {
info->id_extcon = extcon_get_extcon_dev(acpi_dev_name(adev));
put_device(&adev->dev);
- if (!info->id_extcon)
- return -EPROBE_DEFER;
+ if (IS_ERR(info->id_extcon))
+ return PTR_ERR(info->id_extcon);
dev_info(dev, "controlling USB role\n");
} else {
@@ -384,10 +403,16 @@ static int axp288_extcon_probe(struct platform_device *pdev)
}
}
+ ret = iosf_mbi_block_punit_i2c_access();
+ if (ret < 0)
+ return ret;
+
info->vbus_attach = axp288_get_vbus_attach(info);
axp288_extcon_log_rsi(info);
+ iosf_mbi_unblock_punit_i2c_access();
+
/* Initialize extcon device */
info->edev = devm_extcon_dev_allocate(&pdev->dev,
axp288_extcon_cables);
@@ -441,11 +466,44 @@ static int axp288_extcon_probe(struct platform_device *pdev)
}
/* Start charger cable type detection */
- axp288_extcon_enable(info);
+ ret = axp288_extcon_enable(info);
+ if (ret < 0)
+ return ret;
+
+ device_init_wakeup(dev, true);
+ platform_set_drvdata(pdev, info);
return 0;
}
+static int __maybe_unused axp288_extcon_suspend(struct device *dev)
+{
+ struct axp288_extcon_info *info = dev_get_drvdata(dev);
+
+ if (device_may_wakeup(dev))
+ enable_irq_wake(info->irq[VBUS_RISING_IRQ]);
+
+ return 0;
+}
+
+static int __maybe_unused axp288_extcon_resume(struct device *dev)
+{
+ struct axp288_extcon_info *info = dev_get_drvdata(dev);
+
+ /*
+ * Wakeup when a charger is connected to do charger-type
+ * connection and generate an extcon event which makes the
+ * axp288 charger driver set the input current limit.
+ */
+ if (device_may_wakeup(dev))
+ disable_irq_wake(info->irq[VBUS_RISING_IRQ]);
+
+ return 0;
+}
+
+static SIMPLE_DEV_PM_OPS(axp288_extcon_pm_ops, axp288_extcon_suspend,
+ axp288_extcon_resume);
+
static const struct platform_device_id axp288_extcon_table[] = {
{ .name = "axp288_extcon" },
{},
@@ -457,20 +515,10 @@ static struct platform_driver axp288_extcon_driver = {
.id_table = axp288_extcon_table,
.driver = {
.name = "axp288_extcon",
+ .pm = &axp288_extcon_pm_ops,
},
};
-
-static int __init axp288_extcon_init(void)
-{
- return platform_driver_register(&axp288_extcon_driver);
-}
-module_init(axp288_extcon_init);
-
-static void __exit axp288_extcon_exit(void)
-{
- platform_driver_unregister(&axp288_extcon_driver);
-}
-module_exit(axp288_extcon_exit);
+module_platform_driver(axp288_extcon_driver);
MODULE_AUTHOR("Ramakrishna Pallala <ramakrishna.pallala@intel.com>");
MODULE_AUTHOR("Hans de Goede <hdegoede@redhat.com>");
diff --git a/drivers/extcon/extcon-fsa9480.c b/drivers/extcon/extcon-fsa9480.c
index 8405512f5199..7cff66c29907 100644
--- a/drivers/extcon/extcon-fsa9480.c
+++ b/drivers/extcon/extcon-fsa9480.c
@@ -324,11 +324,6 @@ static int fsa9480_probe(struct i2c_client *client,
return 0;
}
-static int fsa9480_remove(struct i2c_client *client)
-{
- return 0;
-}
-
#ifdef CONFIG_PM_SLEEP
static int fsa9480_suspend(struct device *dev)
{
@@ -364,6 +359,7 @@ MODULE_DEVICE_TABLE(i2c, fsa9480_id);
static const struct of_device_id fsa9480_of_match[] = {
{ .compatible = "fcs,fsa9480", },
{ .compatible = "fcs,fsa880", },
+ { .compatible = "ti,tsu6111", },
{ },
};
MODULE_DEVICE_TABLE(of, fsa9480_of_match);
@@ -375,7 +371,6 @@ static struct i2c_driver fsa9480_i2c_driver = {
.of_match_table = fsa9480_of_match,
},
.probe = fsa9480_probe,
- .remove = fsa9480_remove,
.id_table = fsa9480_id,
};
diff --git a/drivers/extcon/extcon-gpio.c b/drivers/extcon/extcon-gpio.c
index c211222f5d0c..4105df74f2b0 100644
--- a/drivers/extcon/extcon-gpio.c
+++ b/drivers/extcon/extcon-gpio.c
@@ -9,6 +9,7 @@
* (originally switch class is supported)
*/
+#include <linux/devm-helpers.h>
#include <linux/extcon-provider.h>
#include <linux/gpio/consumer.h>
#include <linux/init.h>
@@ -112,7 +113,9 @@ static int gpio_extcon_probe(struct platform_device *pdev)
if (ret < 0)
return ret;
- INIT_DELAYED_WORK(&data->work, gpio_extcon_work);
+ ret = devm_delayed_work_autocancel(dev, &data->work, gpio_extcon_work);
+ if (ret)
+ return ret;
/*
* Request the interrupt of gpio to detect whether external connector
@@ -131,15 +134,6 @@ static int gpio_extcon_probe(struct platform_device *pdev)
return 0;
}
-static int gpio_extcon_remove(struct platform_device *pdev)
-{
- struct gpio_extcon_data *data = platform_get_drvdata(pdev);
-
- cancel_delayed_work_sync(&data->work);
-
- return 0;
-}
-
#ifdef CONFIG_PM_SLEEP
static int gpio_extcon_resume(struct device *dev)
{
@@ -158,7 +152,6 @@ static SIMPLE_DEV_PM_OPS(gpio_extcon_pm_ops, NULL, gpio_extcon_resume);
static struct platform_driver gpio_extcon_driver = {
.probe = gpio_extcon_probe,
- .remove = gpio_extcon_remove,
.driver = {
.name = "extcon-gpio",
.pm = &gpio_extcon_pm_ops,
diff --git a/drivers/extcon/extcon-intel-cht-wc.c b/drivers/extcon/extcon-intel-cht-wc.c
index 771f6f4cf92e..89a6449e3f4a 100644
--- a/drivers/extcon/extcon-intel-cht-wc.c
+++ b/drivers/extcon/extcon-intel-cht-wc.c
@@ -14,8 +14,12 @@
#include <linux/module.h>
#include <linux/mod_devicetable.h>
#include <linux/platform_device.h>
+#include <linux/power_supply.h>
+#include <linux/property.h>
#include <linux/regmap.h>
+#include <linux/regulator/consumer.h>
#include <linux/slab.h>
+#include <linux/usb/role.h>
#include "extcon-intel.h"
@@ -101,8 +105,13 @@ struct cht_wc_extcon_data {
struct device *dev;
struct regmap *regmap;
struct extcon_dev *edev;
+ struct usb_role_switch *role_sw;
+ struct regulator *vbus_boost;
+ struct power_supply *psy;
+ enum power_supply_usb_type usb_type;
unsigned int previous_cable;
bool usb_host;
+ bool vbus_boost_enabled;
};
static int cht_wc_extcon_get_id(struct cht_wc_extcon_data *ext, int pwrsrc_sts)
@@ -112,13 +121,21 @@ static int cht_wc_extcon_get_id(struct cht_wc_extcon_data *ext, int pwrsrc_sts)
return INTEL_USB_ID_GND;
case CHT_WC_PWRSRC_RID_FLOAT:
return INTEL_USB_ID_FLOAT;
+ /*
+ * According to the spec. we should read the USB-ID pin ADC value here
+ * to determine the resistance of the used pull-down resister and then
+ * return RID_A / RID_B / RID_C based on this. But all "Accessory
+ * Charger Adapter"s (ACAs) which users can actually buy always use
+ * a combination of a charging port with one or more USB-A ports, so
+ * they should always use a resistor indicating RID_A. But the spec
+ * is hard to read / badly-worded so some of them actually indicate
+ * they are a RID_B ACA evnen though they clearly are a RID_A ACA.
+ * To workaround this simply always return INTEL_USB_RID_A, which
+ * matches all the ACAs which users can actually buy.
+ */
case CHT_WC_PWRSRC_RID_ACA:
+ return INTEL_USB_RID_A;
default:
- /*
- * Once we have IIO support for the GPADC we should read
- * the USBID GPADC channel here and determine ACA role
- * based on that.
- */
return INTEL_USB_ID_FLOAT;
}
}
@@ -147,14 +164,15 @@ static int cht_wc_extcon_get_charger(struct cht_wc_extcon_data *ext,
} while (time_before(jiffies, timeout));
if (status != CHT_WC_USBSRC_STS_SUCCESS) {
- if (ignore_errors)
- return EXTCON_CHG_USB_SDP; /* Save fallback */
+ if (!ignore_errors) {
+ if (status == CHT_WC_USBSRC_STS_FAIL)
+ dev_warn(ext->dev, "Could not detect charger type\n");
+ else
+ dev_warn(ext->dev, "Timeout detecting charger type\n");
+ }
- if (status == CHT_WC_USBSRC_STS_FAIL)
- dev_warn(ext->dev, "Could not detect charger type\n");
- else
- dev_warn(ext->dev, "Timeout detecting charger type\n");
- return EXTCON_CHG_USB_SDP; /* Save fallback */
+ /* Safe fallback */
+ usbsrc = CHT_WC_USBSRC_TYPE_SDP << CHT_WC_USBSRC_TYPE_SHIFT;
}
usbsrc = (usbsrc & CHT_WC_USBSRC_TYPE_MASK) >> CHT_WC_USBSRC_TYPE_SHIFT;
@@ -163,18 +181,23 @@ static int cht_wc_extcon_get_charger(struct cht_wc_extcon_data *ext,
dev_warn(ext->dev,
"Unhandled charger type %d, defaulting to SDP\n",
ret);
+ ext->usb_type = POWER_SUPPLY_USB_TYPE_SDP;
return EXTCON_CHG_USB_SDP;
case CHT_WC_USBSRC_TYPE_SDP:
case CHT_WC_USBSRC_TYPE_FLOATING:
case CHT_WC_USBSRC_TYPE_OTHER:
+ ext->usb_type = POWER_SUPPLY_USB_TYPE_SDP;
return EXTCON_CHG_USB_SDP;
case CHT_WC_USBSRC_TYPE_CDP:
+ ext->usb_type = POWER_SUPPLY_USB_TYPE_CDP;
return EXTCON_CHG_USB_CDP;
case CHT_WC_USBSRC_TYPE_DCP:
case CHT_WC_USBSRC_TYPE_DCP_EXTPHY:
case CHT_WC_USBSRC_TYPE_MHL: /* MHL2+ delivers upto 2A, treat as DCP */
+ ext->usb_type = POWER_SUPPLY_USB_TYPE_DCP;
return EXTCON_CHG_USB_DCP;
case CHT_WC_USBSRC_TYPE_ACA:
+ ext->usb_type = POWER_SUPPLY_USB_TYPE_ACA;
return EXTCON_CHG_USB_ACA;
}
}
@@ -216,6 +239,18 @@ static void cht_wc_extcon_set_otgmode(struct cht_wc_extcon_data *ext,
CHT_WC_CHGRCTRL1_OTGMODE, val);
if (ret)
dev_err(ext->dev, "Error updating CHGRCTRL1 reg: %d\n", ret);
+
+ if (ext->vbus_boost && ext->vbus_boost_enabled != enable) {
+ if (enable)
+ ret = regulator_enable(ext->vbus_boost);
+ else
+ ret = regulator_disable(ext->vbus_boost);
+
+ if (ret)
+ dev_err(ext->dev, "Error updating Vbus boost regulator: %d\n", ret);
+ else
+ ext->vbus_boost_enabled = enable;
+ }
}
static void cht_wc_extcon_enable_charging(struct cht_wc_extcon_data *ext,
@@ -245,6 +280,9 @@ static void cht_wc_extcon_pwrsrc_event(struct cht_wc_extcon_data *ext)
unsigned int cable = EXTCON_NONE;
/* Ignore errors in host mode, as the 5v boost converter is on then */
bool ignore_get_charger_errors = ext->usb_host;
+ enum usb_role role;
+
+ ext->usb_type = POWER_SUPPLY_USB_TYPE_UNKNOWN;
ret = regmap_read(ext->regmap, CHT_WC_PWRSRC_STS, &pwrsrc_sts);
if (ret) {
@@ -288,6 +326,21 @@ set_state:
ext->usb_host = ((id == INTEL_USB_ID_GND) || (id == INTEL_USB_RID_A));
extcon_set_state_sync(ext->edev, EXTCON_USB_HOST, ext->usb_host);
+
+ if (ext->usb_host)
+ role = USB_ROLE_HOST;
+ else if (pwrsrc_sts & CHT_WC_PWRSRC_VBUS)
+ role = USB_ROLE_DEVICE;
+ else
+ role = USB_ROLE_NONE;
+
+ /* Note: this is a no-op when ext->role_sw is NULL */
+ ret = usb_role_switch_set_role(ext->role_sw, role);
+ if (ret)
+ dev_err(ext->dev, "Error setting USB-role: %d\n", ret);
+
+ if (ext->psy)
+ power_supply_changed(ext->psy);
}
static irqreturn_t cht_wc_extcon_isr(int irq, void *data)
@@ -333,6 +386,114 @@ static int cht_wc_extcon_sw_control(struct cht_wc_extcon_data *ext, bool enable)
return ret;
}
+static int cht_wc_extcon_find_role_sw(struct cht_wc_extcon_data *ext)
+{
+ const struct software_node *swnode;
+ struct fwnode_handle *fwnode;
+
+ swnode = software_node_find_by_name(NULL, "intel-xhci-usb-sw");
+ if (!swnode)
+ return -EPROBE_DEFER;
+
+ fwnode = software_node_fwnode(swnode);
+ ext->role_sw = usb_role_switch_find_by_fwnode(fwnode);
+ fwnode_handle_put(fwnode);
+
+ return ext->role_sw ? 0 : -EPROBE_DEFER;
+}
+
+static void cht_wc_extcon_put_role_sw(void *data)
+{
+ struct cht_wc_extcon_data *ext = data;
+
+ usb_role_switch_put(ext->role_sw);
+}
+
+/* Some boards require controlling the role-sw and Vbus based on the id-pin */
+static int cht_wc_extcon_get_role_sw_and_regulator(struct cht_wc_extcon_data *ext)
+{
+ int ret;
+
+ ret = cht_wc_extcon_find_role_sw(ext);
+ if (ret)
+ return ret;
+
+ ret = devm_add_action_or_reset(ext->dev, cht_wc_extcon_put_role_sw, ext);
+ if (ret)
+ return ret;
+
+ /*
+ * On x86/ACPI platforms the regulator <-> consumer link is provided
+ * by platform_data passed to the regulator driver. This means that
+ * this info is not available before the regulator driver has bound.
+ * Use devm_regulator_get_optional() to avoid getting a dummy
+ * regulator and wait for the regulator to show up if necessary.
+ */
+ ext->vbus_boost = devm_regulator_get_optional(ext->dev, "vbus");
+ if (IS_ERR(ext->vbus_boost)) {
+ ret = PTR_ERR(ext->vbus_boost);
+ if (ret == -ENODEV)
+ ret = -EPROBE_DEFER;
+
+ return dev_err_probe(ext->dev, ret, "getting Vbus regulator");
+ }
+
+ return 0;
+}
+
+static int cht_wc_extcon_psy_get_prop(struct power_supply *psy,
+ enum power_supply_property psp,
+ union power_supply_propval *val)
+{
+ struct cht_wc_extcon_data *ext = power_supply_get_drvdata(psy);
+
+ switch (psp) {
+ case POWER_SUPPLY_PROP_USB_TYPE:
+ val->intval = ext->usb_type;
+ break;
+ case POWER_SUPPLY_PROP_ONLINE:
+ val->intval = ext->usb_type ? 1 : 0;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static const enum power_supply_usb_type cht_wc_extcon_psy_usb_types[] = {
+ POWER_SUPPLY_USB_TYPE_SDP,
+ POWER_SUPPLY_USB_TYPE_CDP,
+ POWER_SUPPLY_USB_TYPE_DCP,
+ POWER_SUPPLY_USB_TYPE_ACA,
+ POWER_SUPPLY_USB_TYPE_UNKNOWN,
+};
+
+static const enum power_supply_property cht_wc_extcon_psy_props[] = {
+ POWER_SUPPLY_PROP_USB_TYPE,
+ POWER_SUPPLY_PROP_ONLINE,
+};
+
+static const struct power_supply_desc cht_wc_extcon_psy_desc = {
+ .name = "cht_wcove_pwrsrc",
+ .type = POWER_SUPPLY_TYPE_USB,
+ .usb_types = cht_wc_extcon_psy_usb_types,
+ .num_usb_types = ARRAY_SIZE(cht_wc_extcon_psy_usb_types),
+ .properties = cht_wc_extcon_psy_props,
+ .num_properties = ARRAY_SIZE(cht_wc_extcon_psy_props),
+ .get_property = cht_wc_extcon_psy_get_prop,
+};
+
+static int cht_wc_extcon_register_psy(struct cht_wc_extcon_data *ext)
+{
+ struct power_supply_config psy_cfg = { .drv_data = ext };
+
+ ext->psy = devm_power_supply_register(ext->dev,
+ &cht_wc_extcon_psy_desc,
+ &psy_cfg);
+ return PTR_ERR_OR_ZERO(ext->psy);
+}
+
static int cht_wc_extcon_probe(struct platform_device *pdev)
{
struct intel_soc_pmic *pmic = dev_get_drvdata(pdev->dev.parent);
@@ -358,20 +519,47 @@ static int cht_wc_extcon_probe(struct platform_device *pdev)
if (IS_ERR(ext->edev))
return PTR_ERR(ext->edev);
- /*
- * When a host-cable is detected the BIOS enables an external 5v boost
- * converter to power connected devices there are 2 problems with this:
- * 1) This gets seen by the external battery charger as a valid Vbus
- * supply and it then tries to feed Vsys from this creating a
- * feedback loop which causes aprox. 300 mA extra battery drain
- * (and unless we drive the external-charger-disable pin high it
- * also tries to charge the battery causing even more feedback).
- * 2) This gets seen by the pwrsrc block as a SDP USB Vbus supply
- * Since the external battery charger has its own 5v boost converter
- * which does not have these issues, we simply turn the separate
- * external 5v boost converter off and leave it off entirely.
- */
- cht_wc_extcon_set_5v_boost(ext, false);
+ switch (pmic->cht_wc_model) {
+ case INTEL_CHT_WC_GPD_WIN_POCKET:
+ /*
+ * When a host-cable is detected the BIOS enables an external 5v boost
+ * converter to power connected devices there are 2 problems with this:
+ * 1) This gets seen by the external battery charger as a valid Vbus
+ * supply and it then tries to feed Vsys from this creating a
+ * feedback loop which causes aprox. 300 mA extra battery drain
+ * (and unless we drive the external-charger-disable pin high it
+ * also tries to charge the battery causing even more feedback).
+ * 2) This gets seen by the pwrsrc block as a SDP USB Vbus supply
+ * Since the external battery charger has its own 5v boost converter
+ * which does not have these issues, we simply turn the separate
+ * external 5v boost converter off and leave it off entirely.
+ */
+ cht_wc_extcon_set_5v_boost(ext, false);
+ break;
+ case INTEL_CHT_WC_LENOVO_YOGABOOK1:
+ /* Do this first, as it may very well return -EPROBE_DEFER. */
+ ret = cht_wc_extcon_get_role_sw_and_regulator(ext);
+ if (ret)
+ return ret;
+ /*
+ * The bq25890 used here relies on this driver's BC-1.2 charger
+ * detection, and the bq25890 driver expect this info to be
+ * available through a parent power_supply class device which
+ * models the detected charger (idem to how the Type-C TCPM code
+ * registers a power_supply classdev for the connected charger).
+ */
+ ret = cht_wc_extcon_register_psy(ext);
+ if (ret)
+ return ret;
+ break;
+ case INTEL_CHT_WC_XIAOMI_MIPAD2:
+ ret = cht_wc_extcon_get_role_sw_and_regulator(ext);
+ if (ret)
+ return ret;
+ break;
+ default:
+ break;
+ }
/* Enable sw control */
ret = cht_wc_extcon_sw_control(ext, true);
diff --git a/drivers/extcon/extcon-intel-int3496.c b/drivers/extcon/extcon-intel-int3496.c
index 80c9abcc3f97..ded1a85a5549 100644
--- a/drivers/extcon/extcon-intel-int3496.c
+++ b/drivers/extcon/extcon-intel-int3496.c
@@ -11,11 +11,13 @@
*/
#include <linux/acpi.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
@@ -29,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[] = {
@@ -52,6 +56,27 @@ static const struct acpi_gpio_mapping acpi_int3496_default_gpios[] = {
{ },
};
+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 =
@@ -70,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);
}
@@ -90,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);
@@ -101,9 +130,12 @@ 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);
@@ -117,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);
@@ -155,29 +189,25 @@ static int int3496_probe(struct platform_device *pdev)
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 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);
diff --git a/drivers/extcon/extcon-intel-mrfld.c b/drivers/extcon/extcon-intel-mrfld.c
index f47016fb28a8..cd1a5f230077 100644
--- a/drivers/extcon/extcon-intel-mrfld.c
+++ b/drivers/extcon/extcon-intel-mrfld.c
@@ -197,6 +197,7 @@ static int mrfld_extcon_probe(struct platform_device *pdev)
struct intel_soc_pmic *pmic = dev_get_drvdata(dev->parent);
struct regmap *regmap = pmic->regmap;
struct mrfld_extcon_data *data;
+ unsigned int status;
unsigned int id;
int irq, ret;
@@ -244,6 +245,14 @@ static int mrfld_extcon_probe(struct platform_device *pdev)
/* Get initial state */
mrfld_extcon_role_detect(data);
+ /*
+ * Cached status value is used for cable detection, see comments
+ * in mrfld_extcon_cable_detect(), we need to sync cached value
+ * with a real state of the hardware.
+ */
+ regmap_read(regmap, BCOVE_SCHGRIRQ1, &status);
+ data->status = status;
+
mrfld_extcon_clear(data, BCOVE_MIRQLVL1, BCOVE_LVL1_CHGR);
mrfld_extcon_clear(data, BCOVE_MCHGRIRQ1, BCOVE_CHGRIRQ_ALL);
diff --git a/drivers/extcon/extcon-max14577.c b/drivers/extcon/extcon-max14577.c
index 32f663436e6e..5476f48ed74b 100644
--- a/drivers/extcon/extcon-max14577.c
+++ b/drivers/extcon/extcon-max14577.c
@@ -6,6 +6,7 @@
// Chanwoo Choi <cw00.choi@samsung.com>
// Krzysztof Kozlowski <krzk@kernel.org>
+#include <linux/devm-helpers.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/i2c.h>
@@ -673,7 +674,10 @@ static int max14577_muic_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, info);
mutex_init(&info->mutex);
- INIT_WORK(&info->irq_work, max14577_muic_irq_work);
+ ret = devm_work_autocancel(&pdev->dev, &info->irq_work,
+ max14577_muic_irq_work);
+ if (ret)
+ return ret;
switch (max14577->dev_type) {
case MAXIM_DEVICE_TYPE_MAX77836:
@@ -713,7 +717,7 @@ static int max14577_muic_probe(struct platform_device *pdev)
max14577_extcon_cable);
if (IS_ERR(info->edev)) {
dev_err(&pdev->dev, "failed to allocate memory for extcon\n");
- return -ENOMEM;
+ return PTR_ERR(info->edev);
}
ret = devm_extcon_dev_register(&pdev->dev, info->edev);
@@ -766,15 +770,6 @@ static int max14577_muic_probe(struct platform_device *pdev)
return ret;
}
-static int max14577_muic_remove(struct platform_device *pdev)
-{
- struct max14577_muic_info *info = platform_get_drvdata(pdev);
-
- cancel_work_sync(&info->irq_work);
-
- return 0;
-}
-
static const struct platform_device_id max14577_muic_id[] = {
{ "max14577-muic", MAXIM_DEVICE_TYPE_MAX14577, },
{ "max77836-muic", MAXIM_DEVICE_TYPE_MAX77836, },
@@ -782,12 +777,21 @@ static const struct platform_device_id max14577_muic_id[] = {
};
MODULE_DEVICE_TABLE(platform, max14577_muic_id);
+static const struct of_device_id of_max14577_muic_dt_match[] = {
+ { .compatible = "maxim,max14577-muic",
+ .data = (void *)MAXIM_DEVICE_TYPE_MAX14577, },
+ { .compatible = "maxim,max77836-muic",
+ .data = (void *)MAXIM_DEVICE_TYPE_MAX77836, },
+ { },
+};
+MODULE_DEVICE_TABLE(of, of_max14577_muic_dt_match);
+
static struct platform_driver max14577_muic_driver = {
.driver = {
.name = "max14577-muic",
+ .of_match_table = of_max14577_muic_dt_match,
},
.probe = max14577_muic_probe,
- .remove = max14577_muic_remove,
.id_table = max14577_muic_id,
};
diff --git a/drivers/extcon/extcon-max3355.c b/drivers/extcon/extcon-max3355.c
index fa01926c09f1..d7795607f693 100644
--- a/drivers/extcon/extcon-max3355.c
+++ b/drivers/extcon/extcon-max3355.c
@@ -7,7 +7,6 @@
*/
#include <linux/extcon-provider.h>
-#include <linux/gpio.h>
#include <linux/gpio/consumer.h>
#include <linux/interrupt.h>
#include <linux/module.h>
diff --git a/drivers/extcon/extcon-max77693.c b/drivers/extcon/extcon-max77693.c
index 32fc5a66ffa9..1f1d9ab0c5c7 100644
--- a/drivers/extcon/extcon-max77693.c
+++ b/drivers/extcon/extcon-max77693.c
@@ -5,6 +5,7 @@
// Copyright (C) 2012 Samsung Electrnoics
// Chanwoo Choi <cw00.choi@samsung.com>
+#include <linux/devm-helpers.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/i2c.h>
@@ -1127,7 +1128,10 @@ static int max77693_muic_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, info);
mutex_init(&info->mutex);
- INIT_WORK(&info->irq_work, max77693_muic_irq_work);
+ ret = devm_work_autocancel(&pdev->dev, &info->irq_work,
+ max77693_muic_irq_work);
+ if (ret)
+ return ret;
/* Support irq domain for MAX77693 MUIC device */
for (i = 0; i < ARRAY_SIZE(muic_irqs); i++) {
@@ -1157,7 +1161,7 @@ static int max77693_muic_probe(struct platform_device *pdev)
max77693_extcon_cable);
if (IS_ERR(info->edev)) {
dev_err(&pdev->dev, "failed to allocate memory for extcon\n");
- return -ENOMEM;
+ return PTR_ERR(info->edev);
}
ret = devm_extcon_dev_register(&pdev->dev, info->edev);
@@ -1254,22 +1258,11 @@ static int max77693_muic_probe(struct platform_device *pdev)
return ret;
}
-static int max77693_muic_remove(struct platform_device *pdev)
-{
- struct max77693_muic_info *info = platform_get_drvdata(pdev);
-
- cancel_work_sync(&info->irq_work);
- input_unregister_device(info->dock);
-
- return 0;
-}
-
static struct platform_driver max77693_muic_driver = {
.driver = {
.name = DEV_NAME,
},
.probe = max77693_muic_probe,
- .remove = max77693_muic_remove,
};
module_platform_driver(max77693_muic_driver);
@@ -1277,4 +1270,4 @@ module_platform_driver(max77693_muic_driver);
MODULE_DESCRIPTION("Maxim MAX77693 Extcon driver");
MODULE_AUTHOR("Chanwoo Choi <cw00.choi@samsung.com>");
MODULE_LICENSE("GPL");
-MODULE_ALIAS("platform:extcon-max77693");
+MODULE_ALIAS("platform:max77693-muic");
diff --git a/drivers/extcon/extcon-max77843.c b/drivers/extcon/extcon-max77843.c
index e6b50ca83008..8e6e97ec65a8 100644
--- a/drivers/extcon/extcon-max77843.c
+++ b/drivers/extcon/extcon-max77843.c
@@ -845,7 +845,7 @@ static int max77843_muic_probe(struct platform_device *pdev)
max77843_extcon_cable);
if (IS_ERR(info->edev)) {
dev_err(&pdev->dev, "Failed to allocate memory for extcon\n");
- ret = -ENODEV;
+ ret = PTR_ERR(info->edev);
goto err_muic_irq;
}
diff --git a/drivers/extcon/extcon-max8997.c b/drivers/extcon/extcon-max8997.c
index 172e116ac1ce..9cddf08e0696 100644
--- a/drivers/extcon/extcon-max8997.c
+++ b/drivers/extcon/extcon-max8997.c
@@ -5,6 +5,7 @@
// Copyright (C) 2012 Samsung Electronics
// Donggeun Kim <dg77.kim@samsung.com>
+#include <linux/devm-helpers.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/i2c.h>
@@ -44,6 +45,8 @@ static struct max8997_muic_irq muic_irqs[] = {
{ MAX8997_MUICIRQ_ChgDetRun, "muic-CHGDETRUN" },
{ MAX8997_MUICIRQ_ChgTyp, "muic-CHGTYP" },
{ MAX8997_MUICIRQ_OVP, "muic-OVP" },
+ { MAX8997_PMICIRQ_CHGINS, "pmic-CHGINS" },
+ { MAX8997_PMICIRQ_CHGRM, "pmic-CHGRM" },
};
/* Define supported cable type */
@@ -538,6 +541,8 @@ static void max8997_muic_irq_work(struct work_struct *work)
case MAX8997_MUICIRQ_DCDTmr:
case MAX8997_MUICIRQ_ChgDetRun:
case MAX8997_MUICIRQ_ChgTyp:
+ case MAX8997_PMICIRQ_CHGINS:
+ case MAX8997_PMICIRQ_CHGRM:
/* Handle charger cable */
ret = max8997_muic_chg_handler(info);
break;
@@ -646,27 +651,30 @@ static int max8997_muic_probe(struct platform_device *pdev)
mutex_init(&info->mutex);
INIT_WORK(&info->irq_work, max8997_muic_irq_work);
+ ret = devm_work_autocancel(&pdev->dev, &info->irq_work,
+ max8997_muic_irq_work);
+ if (ret)
+ return ret;
for (i = 0; i < ARRAY_SIZE(muic_irqs); i++) {
struct max8997_muic_irq *muic_irq = &muic_irqs[i];
unsigned int virq = 0;
virq = irq_create_mapping(max8997->irq_domain, muic_irq->irq);
- if (!virq) {
- ret = -EINVAL;
- goto err_irq;
- }
+ if (!virq)
+ return -EINVAL;
+
muic_irq->virq = virq;
- ret = request_threaded_irq(virq, NULL,
- max8997_muic_irq_handler,
- IRQF_NO_SUSPEND,
- muic_irq->name, info);
+ ret = devm_request_threaded_irq(&pdev->dev, virq, NULL,
+ max8997_muic_irq_handler,
+ IRQF_NO_SUSPEND,
+ muic_irq->name, info);
if (ret) {
dev_err(&pdev->dev,
"failed: irq request (IRQ: %d, error :%d)\n",
muic_irq->irq, ret);
- goto err_irq;
+ return ret;
}
}
@@ -674,14 +682,13 @@ static int max8997_muic_probe(struct platform_device *pdev)
info->edev = devm_extcon_dev_allocate(&pdev->dev, max8997_extcon_cable);
if (IS_ERR(info->edev)) {
dev_err(&pdev->dev, "failed to allocate memory for extcon\n");
- ret = -ENOMEM;
- goto err_irq;
+ return PTR_ERR(info->edev);
}
ret = devm_extcon_dev_register(&pdev->dev, info->edev);
if (ret) {
dev_err(&pdev->dev, "failed to register extcon device\n");
- goto err_irq;
+ return ret;
}
if (pdata && pdata->muic_pdata) {
@@ -752,23 +759,6 @@ static int max8997_muic_probe(struct platform_device *pdev)
delay_jiffies);
return 0;
-
-err_irq:
- while (--i >= 0)
- free_irq(muic_irqs[i].virq, info);
- return ret;
-}
-
-static int max8997_muic_remove(struct platform_device *pdev)
-{
- struct max8997_muic_info *info = platform_get_drvdata(pdev);
- int i;
-
- for (i = 0; i < ARRAY_SIZE(muic_irqs); i++)
- free_irq(muic_irqs[i].virq, info);
- cancel_work_sync(&info->irq_work);
-
- return 0;
}
static struct platform_driver max8997_muic_driver = {
@@ -776,7 +766,6 @@ static struct platform_driver max8997_muic_driver = {
.name = DEV_NAME,
},
.probe = max8997_muic_probe,
- .remove = max8997_muic_remove,
};
module_platform_driver(max8997_muic_driver);
@@ -784,3 +773,4 @@ module_platform_driver(max8997_muic_driver);
MODULE_DESCRIPTION("Maxim MAX8997 Extcon driver");
MODULE_AUTHOR("Donggeun Kim <dg77.kim@samsung.com>");
MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:max8997-muic");
diff --git a/drivers/extcon/extcon-palmas.c b/drivers/extcon/extcon-palmas.c
index edc5016f46f1..32f8b541770b 100644
--- a/drivers/extcon/extcon-palmas.c
+++ b/drivers/extcon/extcon-palmas.c
@@ -2,13 +2,14 @@
/*
* Palmas USB transceiver driver
*
- * Copyright (C) 2013 Texas Instruments Incorporated - http://www.ti.com
+ * Copyright (C) 2013 Texas Instruments Incorporated - https://www.ti.com
* Author: Graeme Gregory <gg@slimlogic.co.uk>
* Author: Kishon Vijay Abraham I <kishon@ti.com>
* Based on twl6030_usb.c
* Author: Hema HK <hemahk@ti.com>
*/
+#include <linux/devm-helpers.h>
#include <linux/module.h>
#include <linux/interrupt.h>
#include <linux/platform_device.h>
@@ -106,7 +107,7 @@ static irqreturn_t palmas_id_irq_handler(int irq, void *_palmas_usb)
(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_dbg(palmas_usb->dev, " USB-HOST cable is attached\n");
+ dev_dbg(palmas_usb->dev, "USB-HOST cable is attached\n");
}
return IRQ_HANDLED;
@@ -205,17 +206,15 @@ static int palmas_usb_probe(struct platform_device *pdev)
palmas_usb->id_gpiod = devm_gpiod_get_optional(&pdev->dev, "id",
GPIOD_IN);
- if (IS_ERR(palmas_usb->id_gpiod)) {
- dev_err(&pdev->dev, "failed to get id gpio\n");
- return PTR_ERR(palmas_usb->id_gpiod);
- }
+ if (IS_ERR(palmas_usb->id_gpiod))
+ return dev_err_probe(&pdev->dev, PTR_ERR(palmas_usb->id_gpiod),
+ "failed to get id gpio\n");
palmas_usb->vbus_gpiod = devm_gpiod_get_optional(&pdev->dev, "vbus",
GPIOD_IN);
- if (IS_ERR(palmas_usb->vbus_gpiod)) {
- dev_err(&pdev->dev, "failed to get vbus gpio\n");
- return PTR_ERR(palmas_usb->vbus_gpiod);
- }
+ if (IS_ERR(palmas_usb->vbus_gpiod))
+ return dev_err_probe(&pdev->dev, PTR_ERR(palmas_usb->vbus_gpiod),
+ "failed to get id gpio\n");
if (palmas_usb->enable_id_detection && palmas_usb->id_gpiod) {
palmas_usb->enable_id_detection = false;
@@ -239,7 +238,11 @@ static int palmas_usb_probe(struct platform_device *pdev)
palmas_usb->sw_debounce_jiffies = msecs_to_jiffies(debounce);
}
- INIT_DELAYED_WORK(&palmas_usb->wq_detectid, palmas_gpio_id_detect);
+ status = devm_delayed_work_autocancel(&pdev->dev,
+ &palmas_usb->wq_detectid,
+ palmas_gpio_id_detect);
+ if (status)
+ return status;
palmas->usb = palmas_usb;
palmas_usb->palmas = palmas;
@@ -361,15 +364,6 @@ static int palmas_usb_probe(struct platform_device *pdev)
return 0;
}
-static int palmas_usb_remove(struct platform_device *pdev)
-{
- struct palmas_usb *palmas_usb = platform_get_drvdata(pdev);
-
- cancel_delayed_work_sync(&palmas_usb->wq_detectid);
-
- return 0;
-}
-
#ifdef CONFIG_PM_SLEEP
static int palmas_usb_suspend(struct device *dev)
{
@@ -424,7 +418,6 @@ static const struct of_device_id of_palmas_match_tbl[] = {
static struct platform_driver palmas_usb_driver = {
.probe = palmas_usb_probe,
- .remove = palmas_usb_remove,
.driver = {
.name = "palmas-usb",
.of_match_table = of_palmas_match_tbl,
diff --git a/drivers/extcon/extcon-ptn5150.c b/drivers/extcon/extcon-ptn5150.c
index d1c997599390..017a07197f38 100644
--- a/drivers/extcon/extcon-ptn5150.c
+++ b/drivers/extcon/extcon-ptn5150.c
@@ -5,7 +5,9 @@
// Based on extcon-sm5502.c driver
// Copyright (c) 2018-2019 by Vijai Kumar K
// Author: Vijai Kumar K <vijaikumar.kanagarajan@gmail.com>
+// Copyright (c) 2020 Krzysztof Kozlowski <krzk@kernel.org>
+#include <linux/bitfield.h>
#include <linux/err.h>
#include <linux/i2c.h>
#include <linux/interrupt.h>
@@ -15,48 +17,31 @@
#include <linux/slab.h>
#include <linux/extcon-provider.h>
#include <linux/gpio/consumer.h>
+#include <linux/usb/role.h>
/* PTN5150 registers */
-enum ptn5150_reg {
- PTN5150_REG_DEVICE_ID = 0x01,
- PTN5150_REG_CONTROL,
- PTN5150_REG_INT_STATUS,
- PTN5150_REG_CC_STATUS,
- PTN5150_REG_CON_DET = 0x09,
- PTN5150_REG_VCONN_STATUS,
- PTN5150_REG_RESET,
- PTN5150_REG_INT_MASK = 0x18,
- PTN5150_REG_INT_REG_STATUS,
- PTN5150_REG_END,
-};
+#define PTN5150_REG_DEVICE_ID 0x01
+#define PTN5150_REG_CONTROL 0x02
+#define PTN5150_REG_INT_STATUS 0x03
+#define PTN5150_REG_CC_STATUS 0x04
+#define PTN5150_REG_CON_DET 0x09
+#define PTN5150_REG_VCONN_STATUS 0x0a
+#define PTN5150_REG_RESET 0x0b
+#define PTN5150_REG_INT_MASK 0x18
+#define PTN5150_REG_INT_REG_STATUS 0x19
+#define PTN5150_REG_END PTN5150_REG_INT_REG_STATUS
#define PTN5150_DFP_ATTACHED 0x1
#define PTN5150_UFP_ATTACHED 0x2
/* Define PTN5150 MASK/SHIFT constant */
-#define PTN5150_REG_DEVICE_ID_VENDOR_SHIFT 0
-#define PTN5150_REG_DEVICE_ID_VENDOR_MASK \
- (0x3 << PTN5150_REG_DEVICE_ID_VENDOR_SHIFT)
-
-#define PTN5150_REG_DEVICE_ID_VERSION_SHIFT 3
-#define PTN5150_REG_DEVICE_ID_VERSION_MASK \
- (0x1f << PTN5150_REG_DEVICE_ID_VERSION_SHIFT)
-
-#define PTN5150_REG_CC_PORT_ATTACHMENT_SHIFT 2
-#define PTN5150_REG_CC_PORT_ATTACHMENT_MASK \
- (0x7 << PTN5150_REG_CC_PORT_ATTACHMENT_SHIFT)
-
-#define PTN5150_REG_CC_VBUS_DETECTION_SHIFT 7
-#define PTN5150_REG_CC_VBUS_DETECTION_MASK \
- (0x1 << PTN5150_REG_CC_VBUS_DETECTION_SHIFT)
+#define PTN5150_REG_DEVICE_ID_VERSION GENMASK(7, 3)
+#define PTN5150_REG_DEVICE_ID_VENDOR GENMASK(2, 0)
-#define PTN5150_REG_INT_CABLE_ATTACH_SHIFT 0
-#define PTN5150_REG_INT_CABLE_ATTACH_MASK \
- (0x1 << PTN5150_REG_INT_CABLE_ATTACH_SHIFT)
-
-#define PTN5150_REG_INT_CABLE_DETACH_SHIFT 1
-#define PTN5150_REG_INT_CABLE_DETACH_MASK \
- (0x1 << PTN5150_REG_CC_CABLE_DETACH_SHIFT)
+#define PTN5150_REG_CC_PORT_ATTACHMENT GENMASK(4, 2)
+#define PTN5150_REG_CC_VBUS_DETECTION BIT(7)
+#define PTN5150_REG_INT_CABLE_ATTACH_MASK BIT(0)
+#define PTN5150_REG_INT_CABLE_DETACH_MASK BIT(1)
struct ptn5150_info {
struct device *dev;
@@ -68,6 +53,7 @@ struct ptn5150_info {
int irq;
struct work_struct irq_work;
struct mutex mutex;
+ struct usb_role_switch *role_sw;
};
/* List of detectable cables */
@@ -83,12 +69,55 @@ static const struct regmap_config ptn5150_regmap_config = {
.max_register = PTN5150_REG_END,
};
+static void ptn5150_check_state(struct ptn5150_info *info)
+{
+ unsigned int port_status, reg_data, vbus;
+ enum usb_role usb_role = USB_ROLE_NONE;
+ int ret;
+
+ ret = regmap_read(info->regmap, PTN5150_REG_CC_STATUS, &reg_data);
+ if (ret) {
+ dev_err(info->dev, "failed to read CC STATUS %d\n", ret);
+ return;
+ }
+
+ port_status = FIELD_GET(PTN5150_REG_CC_PORT_ATTACHMENT, reg_data);
+
+ switch (port_status) {
+ case PTN5150_DFP_ATTACHED:
+ extcon_set_state_sync(info->edev, EXTCON_USB_HOST, false);
+ gpiod_set_value_cansleep(info->vbus_gpiod, 0);
+ extcon_set_state_sync(info->edev, EXTCON_USB, true);
+ usb_role = USB_ROLE_DEVICE;
+ break;
+ case PTN5150_UFP_ATTACHED:
+ extcon_set_state_sync(info->edev, EXTCON_USB, false);
+ vbus = FIELD_GET(PTN5150_REG_CC_VBUS_DETECTION, reg_data);
+ if (vbus)
+ gpiod_set_value_cansleep(info->vbus_gpiod, 0);
+ else
+ gpiod_set_value_cansleep(info->vbus_gpiod, 1);
+
+ extcon_set_state_sync(info->edev, EXTCON_USB_HOST, true);
+ usb_role = USB_ROLE_HOST;
+ break;
+ default:
+ break;
+ }
+
+ if (usb_role) {
+ ret = usb_role_switch_set_role(info->role_sw, usb_role);
+ if (ret)
+ dev_err(info->dev, "failed to set %s role: %d\n",
+ usb_role_string(usb_role), ret);
+ }
+}
+
static void ptn5150_irq_work(struct work_struct *work)
{
struct ptn5150_info *info = container_of(work,
struct ptn5150_info, irq_work);
int ret = 0;
- unsigned int reg_data;
unsigned int int_status;
if (!info->edev)
@@ -96,13 +125,6 @@ static void ptn5150_irq_work(struct work_struct *work)
mutex_lock(&info->mutex);
- ret = regmap_read(info->regmap, PTN5150_REG_CC_STATUS, &reg_data);
- if (ret) {
- dev_err(info->dev, "failed to read CC STATUS %d\n", ret);
- mutex_unlock(&info->mutex);
- return;
- }
-
/* Clear interrupt. Read would clear the register */
ret = regmap_read(info->regmap, PTN5150_REG_INT_STATUS, &int_status);
if (ret) {
@@ -116,47 +138,20 @@ static void ptn5150_irq_work(struct work_struct *work)
cable_attach = int_status & PTN5150_REG_INT_CABLE_ATTACH_MASK;
if (cable_attach) {
- unsigned int port_status;
- unsigned int vbus;
-
- port_status = ((reg_data &
- PTN5150_REG_CC_PORT_ATTACHMENT_MASK) >>
- PTN5150_REG_CC_PORT_ATTACHMENT_SHIFT);
-
- switch (port_status) {
- case PTN5150_DFP_ATTACHED:
- extcon_set_state_sync(info->edev,
- EXTCON_USB_HOST, false);
- gpiod_set_value(info->vbus_gpiod, 0);
- extcon_set_state_sync(info->edev, EXTCON_USB,
- true);
- break;
- case PTN5150_UFP_ATTACHED:
- extcon_set_state_sync(info->edev, EXTCON_USB,
- false);
- vbus = ((reg_data &
- PTN5150_REG_CC_VBUS_DETECTION_MASK) >>
- PTN5150_REG_CC_VBUS_DETECTION_SHIFT);
- if (vbus)
- gpiod_set_value(info->vbus_gpiod, 0);
- else
- gpiod_set_value(info->vbus_gpiod, 1);
-
- extcon_set_state_sync(info->edev,
- EXTCON_USB_HOST, true);
- break;
- default:
- dev_err(info->dev,
- "Unknown Port status : %x\n",
- port_status);
- break;
- }
+ ptn5150_check_state(info);
} else {
extcon_set_state_sync(info->edev,
EXTCON_USB_HOST, false);
extcon_set_state_sync(info->edev,
EXTCON_USB, false);
- gpiod_set_value(info->vbus_gpiod, 0);
+ gpiod_set_value_cansleep(info->vbus_gpiod, 0);
+
+ ret = usb_role_switch_set_role(info->role_sw,
+ USB_ROLE_NONE);
+ if (ret)
+ dev_err(info->dev,
+ "failed to set none role: %d\n",
+ ret);
}
}
@@ -194,13 +189,10 @@ static int ptn5150_init_dev_type(struct ptn5150_info *info)
return -EINVAL;
}
- vendor_id = ((reg_data & PTN5150_REG_DEVICE_ID_VENDOR_MASK) >>
- PTN5150_REG_DEVICE_ID_VENDOR_SHIFT);
- version_id = ((reg_data & PTN5150_REG_DEVICE_ID_VERSION_MASK) >>
- PTN5150_REG_DEVICE_ID_VERSION_SHIFT);
-
- dev_info(info->dev, "Device type: version: 0x%x, vendor: 0x%x\n",
- version_id, vendor_id);
+ vendor_id = FIELD_GET(PTN5150_REG_DEVICE_ID_VENDOR, reg_data);
+ version_id = FIELD_GET(PTN5150_REG_DEVICE_ID_VERSION, reg_data);
+ dev_dbg(info->dev, "Device type: version: 0x%x, vendor: 0x%x\n",
+ version_id, vendor_id);
/* Clear any existing interrupts */
ret = regmap_read(info->regmap, PTN5150_REG_INT_STATUS, &reg_data);
@@ -221,8 +213,15 @@ static int ptn5150_init_dev_type(struct ptn5150_info *info)
return 0;
}
-static int ptn5150_i2c_probe(struct i2c_client *i2c,
- const struct i2c_device_id *id)
+static void ptn5150_work_sync_and_put(void *data)
+{
+ struct ptn5150_info *info = data;
+
+ cancel_work_sync(&info->irq_work);
+ usb_role_switch_put(info->role_sw);
+}
+
+static int ptn5150_i2c_probe(struct i2c_client *i2c)
{
struct device *dev = &i2c->dev;
struct device_node *np = i2c->dev.of_node;
@@ -239,20 +238,15 @@ static int ptn5150_i2c_probe(struct i2c_client *i2c,
info->dev = &i2c->dev;
info->i2c = i2c;
- info->int_gpiod = devm_gpiod_get(&i2c->dev, "int", GPIOD_IN);
- if (IS_ERR(info->int_gpiod)) {
- dev_err(dev, "failed to get INT GPIO\n");
- return PTR_ERR(info->int_gpiod);
- }
- info->vbus_gpiod = devm_gpiod_get(&i2c->dev, "vbus", GPIOD_IN);
+ info->vbus_gpiod = devm_gpiod_get(&i2c->dev, "vbus", GPIOD_OUT_LOW);
if (IS_ERR(info->vbus_gpiod)) {
- dev_err(dev, "failed to get VBUS GPIO\n");
- return PTR_ERR(info->vbus_gpiod);
- }
- ret = gpiod_direction_output(info->vbus_gpiod, 0);
- if (ret) {
- dev_err(dev, "failed to set VBUS GPIO direction\n");
- return -EINVAL;
+ ret = PTR_ERR(info->vbus_gpiod);
+ if (ret == -ENOENT) {
+ dev_info(dev, "No VBUS GPIO, ignoring VBUS control\n");
+ info->vbus_gpiod = NULL;
+ } else {
+ return dev_err_probe(dev, ret, "failed to get VBUS GPIO\n");
+ }
}
mutex_init(&info->mutex);
@@ -261,28 +255,34 @@ static int ptn5150_i2c_probe(struct i2c_client *i2c,
info->regmap = devm_regmap_init_i2c(i2c, &ptn5150_regmap_config);
if (IS_ERR(info->regmap)) {
- ret = PTR_ERR(info->regmap);
- dev_err(info->dev, "failed to allocate register map: %d\n",
- ret);
- return ret;
+ return dev_err_probe(info->dev, PTR_ERR(info->regmap),
+ "failed to allocate register map\n");
}
- if (info->int_gpiod) {
+ if (i2c->irq > 0) {
+ info->irq = i2c->irq;
+ } else {
+ info->int_gpiod = devm_gpiod_get(&i2c->dev, "int", GPIOD_IN);
+ if (IS_ERR(info->int_gpiod)) {
+ return dev_err_probe(dev, PTR_ERR(info->int_gpiod),
+ "failed to get INT GPIO\n");
+ }
+
info->irq = gpiod_to_irq(info->int_gpiod);
if (info->irq < 0) {
dev_err(dev, "failed to get INTB IRQ\n");
return info->irq;
}
+ }
- ret = devm_request_threaded_irq(dev, info->irq, NULL,
- ptn5150_irq_handler,
- IRQF_TRIGGER_FALLING |
- IRQF_ONESHOT,
- i2c->name, info);
- if (ret < 0) {
- dev_err(dev, "failed to request handler for INTB IRQ\n");
- return ret;
- }
+ ret = devm_request_threaded_irq(dev, info->irq, NULL,
+ ptn5150_irq_handler,
+ IRQF_TRIGGER_FALLING |
+ IRQF_ONESHOT,
+ i2c->name, info);
+ if (ret < 0) {
+ dev_err(dev, "failed to request handler for INTB IRQ\n");
+ return ret;
}
/* Allocate extcon device */
@@ -299,11 +299,35 @@ static int ptn5150_i2c_probe(struct i2c_client *i2c,
return ret;
}
+ extcon_set_property_capability(info->edev, EXTCON_USB,
+ EXTCON_PROP_USB_VBUS);
+ extcon_set_property_capability(info->edev, EXTCON_USB_HOST,
+ EXTCON_PROP_USB_VBUS);
+ extcon_set_property_capability(info->edev, EXTCON_USB_HOST,
+ EXTCON_PROP_USB_TYPEC_POLARITY);
+
/* Initialize PTN5150 device and print vendor id and version id */
ret = ptn5150_init_dev_type(info);
if (ret)
return -EINVAL;
+ info->role_sw = usb_role_switch_get(info->dev);
+ if (IS_ERR(info->role_sw))
+ return dev_err_probe(info->dev, PTR_ERR(info->role_sw),
+ "failed to get role switch\n");
+
+ ret = devm_add_action_or_reset(dev, ptn5150_work_sync_and_put, info);
+ if (ret)
+ return ret;
+
+ /*
+ * Update current extcon state if for example OTG connection was there
+ * before the probe
+ */
+ mutex_lock(&info->mutex);
+ ptn5150_check_state(info);
+ mutex_unlock(&info->mutex);
+
return 0;
}
@@ -324,16 +348,12 @@ static struct i2c_driver ptn5150_i2c_driver = {
.name = "ptn5150",
.of_match_table = ptn5150_dt_match,
},
- .probe = ptn5150_i2c_probe,
+ .probe_new = ptn5150_i2c_probe,
.id_table = ptn5150_i2c_id,
};
-
-static int __init ptn5150_i2c_init(void)
-{
- return i2c_add_driver(&ptn5150_i2c_driver);
-}
-subsys_initcall(ptn5150_i2c_init);
+module_i2c_driver(ptn5150_i2c_driver);
MODULE_DESCRIPTION("NXP PTN5150 CC logic Extcon driver");
MODULE_AUTHOR("Vijai Kumar K <vijaikumar.kanagarajan@gmail.com>");
+MODULE_AUTHOR("Krzysztof Kozlowski <krzk@kernel.org>");
MODULE_LICENSE("GPL v2");
diff --git a/drivers/extcon/extcon-qcom-spmi-misc.c b/drivers/extcon/extcon-qcom-spmi-misc.c
index 6b836ae62176..eb02cb962b5e 100644
--- a/drivers/extcon/extcon-qcom-spmi-misc.c
+++ b/drivers/extcon/extcon-qcom-spmi-misc.c
@@ -1,12 +1,13 @@
// SPDX-License-Identifier: GPL-2.0-only
/**
* extcon-qcom-spmi-misc.c - Qualcomm USB extcon driver to support USB ID
- * detection based on extcon-usb-gpio.c.
+ * and VBUS detection based on extcon-usb-gpio.c.
*
* Copyright (C) 2016 Linaro, Ltd.
* Stephen Boyd <stephen.boyd@linaro.org>
*/
+#include <linux/devm-helpers.h>
#include <linux/extcon-provider.h>
#include <linux/init.h>
#include <linux/interrupt.h>
@@ -21,30 +22,56 @@
struct qcom_usb_extcon_info {
struct extcon_dev *edev;
- int irq;
+ int id_irq;
+ int vbus_irq;
struct delayed_work wq_detcable;
unsigned long debounce_jiffies;
};
static const unsigned int qcom_usb_extcon_cable[] = {
+ EXTCON_USB,
EXTCON_USB_HOST,
EXTCON_NONE,
};
static void qcom_usb_extcon_detect_cable(struct work_struct *work)
{
- bool id;
+ bool state = false;
int ret;
+ union extcon_property_value val;
struct qcom_usb_extcon_info *info = container_of(to_delayed_work(work),
struct qcom_usb_extcon_info,
wq_detcable);
- /* check ID and update cable state */
- ret = irq_get_irqchip_state(info->irq, IRQCHIP_STATE_LINE_LEVEL, &id);
- if (ret)
- return;
+ if (info->id_irq > 0) {
+ /* check ID and update cable state */
+ ret = irq_get_irqchip_state(info->id_irq,
+ IRQCHIP_STATE_LINE_LEVEL, &state);
+ if (ret)
+ return;
+
+ if (!state) {
+ val.intval = true;
+ extcon_set_property(info->edev, EXTCON_USB_HOST,
+ EXTCON_PROP_USB_SS, val);
+ }
+ extcon_set_state_sync(info->edev, EXTCON_USB_HOST, !state);
+ }
- extcon_set_state_sync(info->edev, EXTCON_USB_HOST, !id);
+ if (info->vbus_irq > 0) {
+ /* check VBUS and update cable state */
+ ret = irq_get_irqchip_state(info->vbus_irq,
+ IRQCHIP_STATE_LINE_LEVEL, &state);
+ if (ret)
+ return;
+
+ if (state) {
+ val.intval = true;
+ extcon_set_property(info->edev, EXTCON_USB,
+ EXTCON_PROP_USB_SS, val);
+ }
+ extcon_set_state_sync(info->edev, EXTCON_USB, state);
+ }
}
static irqreturn_t qcom_usb_irq_handler(int irq, void *dev_id)
@@ -79,21 +106,52 @@ static int qcom_usb_extcon_probe(struct platform_device *pdev)
return ret;
}
+ ret = extcon_set_property_capability(info->edev,
+ EXTCON_USB, EXTCON_PROP_USB_SS);
+ ret |= extcon_set_property_capability(info->edev,
+ EXTCON_USB_HOST, EXTCON_PROP_USB_SS);
+ if (ret) {
+ dev_err(dev, "failed to register extcon props rc=%d\n",
+ ret);
+ return ret;
+ }
+
info->debounce_jiffies = msecs_to_jiffies(USB_ID_DEBOUNCE_MS);
- INIT_DELAYED_WORK(&info->wq_detcable, qcom_usb_extcon_detect_cable);
- info->irq = platform_get_irq_byname(pdev, "usb_id");
- if (info->irq < 0)
- return info->irq;
+ ret = devm_delayed_work_autocancel(dev, &info->wq_detcable,
+ qcom_usb_extcon_detect_cable);
+ if (ret)
+ return ret;
- ret = devm_request_threaded_irq(dev, info->irq, NULL,
+ info->id_irq = platform_get_irq_byname(pdev, "usb_id");
+ if (info->id_irq > 0) {
+ ret = devm_request_threaded_irq(dev, info->id_irq, NULL,
qcom_usb_irq_handler,
IRQF_TRIGGER_RISING |
IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
pdev->name, info);
- if (ret < 0) {
- dev_err(dev, "failed to request handler for ID IRQ\n");
- return ret;
+ if (ret < 0) {
+ dev_err(dev, "failed to request handler for ID IRQ\n");
+ return ret;
+ }
+ }
+
+ info->vbus_irq = platform_get_irq_byname(pdev, "usb_vbus");
+ if (info->vbus_irq > 0) {
+ ret = devm_request_threaded_irq(dev, info->vbus_irq, NULL,
+ qcom_usb_irq_handler,
+ IRQF_TRIGGER_RISING |
+ IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
+ pdev->name, info);
+ if (ret < 0) {
+ dev_err(dev, "failed to request handler for VBUS IRQ\n");
+ return ret;
+ }
+ }
+
+ if (info->id_irq < 0 && info->vbus_irq < 0) {
+ dev_err(dev, "ID and VBUS IRQ not found\n");
+ return -EINVAL;
}
platform_set_drvdata(pdev, info);
@@ -105,23 +163,18 @@ static int qcom_usb_extcon_probe(struct platform_device *pdev)
return 0;
}
-static int qcom_usb_extcon_remove(struct platform_device *pdev)
-{
- struct qcom_usb_extcon_info *info = platform_get_drvdata(pdev);
-
- cancel_delayed_work_sync(&info->wq_detcable);
-
- return 0;
-}
-
#ifdef CONFIG_PM_SLEEP
static int qcom_usb_extcon_suspend(struct device *dev)
{
struct qcom_usb_extcon_info *info = dev_get_drvdata(dev);
int ret = 0;
- if (device_may_wakeup(dev))
- ret = enable_irq_wake(info->irq);
+ if (device_may_wakeup(dev)) {
+ if (info->id_irq > 0)
+ ret = enable_irq_wake(info->id_irq);
+ if (info->vbus_irq > 0)
+ ret = enable_irq_wake(info->vbus_irq);
+ }
return ret;
}
@@ -131,8 +184,12 @@ static int qcom_usb_extcon_resume(struct device *dev)
struct qcom_usb_extcon_info *info = dev_get_drvdata(dev);
int ret = 0;
- if (device_may_wakeup(dev))
- ret = disable_irq_wake(info->irq);
+ if (device_may_wakeup(dev)) {
+ if (info->id_irq > 0)
+ ret = disable_irq_wake(info->id_irq);
+ if (info->vbus_irq > 0)
+ ret = disable_irq_wake(info->vbus_irq);
+ }
return ret;
}
@@ -149,7 +206,6 @@ MODULE_DEVICE_TABLE(of, qcom_usb_extcon_dt_match);
static struct platform_driver qcom_usb_extcon_driver = {
.probe = qcom_usb_extcon_probe,
- .remove = qcom_usb_extcon_remove,
.driver = {
.name = "extcon-pm8941-misc",
.pm = &qcom_usb_extcon_pm_ops,
diff --git a/drivers/extcon/extcon-rt8973a.c b/drivers/extcon/extcon-rt8973a.c
index 40c07f4d656e..e6e448f6ea2f 100644
--- a/drivers/extcon/extcon-rt8973a.c
+++ b/drivers/extcon/extcon-rt8973a.c
@@ -192,7 +192,6 @@ static const struct regmap_irq_chip rt8973a_muic_irq_chip = {
.name = "rt8973a",
.status_base = RT8973A_REG_INT1,
.mask_base = RT8973A_REG_INTM1,
- .mask_invert = false,
.num_regs = 2,
.irqs = rt8973a_irqs,
.num_irqs = ARRAY_SIZE(rt8973a_irqs),
@@ -647,13 +646,11 @@ static int rt8973a_muic_i2c_probe(struct i2c_client *i2c,
return 0;
}
-static int rt8973a_muic_i2c_remove(struct i2c_client *i2c)
+static void rt8973a_muic_i2c_remove(struct i2c_client *i2c)
{
struct rt8973a_muic_info *info = i2c_get_clientdata(i2c);
regmap_del_irq_chip(info->irq, info->irq_data);
-
- return 0;
}
static const struct of_device_id rt8973a_dt_match[] = {
diff --git a/drivers/extcon/extcon-sm5502.c b/drivers/extcon/extcon-sm5502.c
index 106d4da647bd..8401e8b27788 100644
--- a/drivers/extcon/extcon-sm5502.c
+++ b/drivers/extcon/extcon-sm5502.c
@@ -40,17 +40,13 @@ struct sm5502_muic_info {
struct i2c_client *i2c;
struct regmap *regmap;
+ const struct sm5502_type *type;
struct regmap_irq_chip_data *irq_data;
- struct muic_irq *muic_irqs;
- unsigned int num_muic_irqs;
int irq;
bool irq_attach;
bool irq_detach;
struct work_struct irq_work;
- struct reg_data *reg_data;
- unsigned int num_reg_data;
-
struct mutex mutex;
/*
@@ -62,6 +58,18 @@ struct sm5502_muic_info {
struct delayed_work wq_detcable;
};
+struct sm5502_type {
+ struct muic_irq *muic_irqs;
+ unsigned int num_muic_irqs;
+ const struct regmap_irq_chip *irq_chip;
+
+ struct reg_data *reg_data;
+ unsigned int num_reg_data;
+
+ unsigned int otg_dev_type1;
+ int (*parse_irq)(struct sm5502_muic_info *info, int irq_type);
+};
+
/* Default value of SM5502 register to bring up MUIC device. */
static struct reg_data sm5502_reg_data[] = {
{
@@ -88,7 +96,33 @@ static struct reg_data sm5502_reg_data[] = {
| SM5502_REG_INTM2_MHL_MASK,
.invert = true,
},
- { }
+};
+
+/* Default value of SM5504 register to bring up MUIC device. */
+static struct reg_data sm5504_reg_data[] = {
+ {
+ .reg = SM5502_REG_RESET,
+ .val = SM5502_REG_RESET_MASK,
+ .invert = true,
+ }, {
+ .reg = SM5502_REG_INTMASK1,
+ .val = SM5504_REG_INTM1_ATTACH_MASK
+ | SM5504_REG_INTM1_DETACH_MASK,
+ .invert = false,
+ }, {
+ .reg = SM5502_REG_INTMASK2,
+ .val = SM5504_REG_INTM2_RID_CHG_MASK
+ | SM5504_REG_INTM2_UVLO_MASK
+ | SM5504_REG_INTM2_POR_MASK,
+ .invert = true,
+ }, {
+ .reg = SM5502_REG_CONTROL,
+ .val = SM5502_REG_CONTROL_MANUAL_SW_MASK
+ | SM5504_REG_CONTROL_CHGTYP_MASK
+ | SM5504_REG_CONTROL_USBCHDEN_MASK
+ | SM5504_REG_CONTROL_ADC_EN_MASK,
+ .invert = true,
+ },
};
/* List of detectable cables */
@@ -144,6 +178,7 @@ enum sm5502_muic_acc_type {
SM5502_MUIC_ADC_AUDIO_TYPE1_FULL_REMOTE = 0x3e, /* | 001|11110| */
SM5502_MUIC_ADC_AUDIO_TYPE1_SEND_END = 0x5e, /* | 010|11110| */
/* |Dev Type1|--ADC| */
+ SM5502_MUIC_ADC_GROUND_USB_OTG = 0x80, /* | 100|00000| */
SM5502_MUIC_ADC_OPEN_USB = 0x5f, /* | 010|11111| */
SM5502_MUIC_ADC_OPEN_TA = 0xdf, /* | 110|11111| */
SM5502_MUIC_ADC_OPEN_USB_OTG = 0xff, /* | 111|11111| */
@@ -192,12 +227,59 @@ static const struct regmap_irq_chip sm5502_muic_irq_chip = {
.name = "sm5502",
.status_base = SM5502_REG_INT1,
.mask_base = SM5502_REG_INTMASK1,
- .mask_invert = false,
.num_regs = 2,
.irqs = sm5502_irqs,
.num_irqs = ARRAY_SIZE(sm5502_irqs),
};
+/* List of supported interrupt for SM5504 */
+static struct muic_irq sm5504_muic_irqs[] = {
+ { SM5504_IRQ_INT1_ATTACH, "muic-attach" },
+ { SM5504_IRQ_INT1_DETACH, "muic-detach" },
+ { SM5504_IRQ_INT1_CHG_DET, "muic-chg-det" },
+ { SM5504_IRQ_INT1_DCD_OUT, "muic-dcd-out" },
+ { SM5504_IRQ_INT1_OVP_EVENT, "muic-ovp-event" },
+ { SM5504_IRQ_INT1_CONNECT, "muic-connect" },
+ { SM5504_IRQ_INT1_ADC_CHG, "muic-adc-chg" },
+ { SM5504_IRQ_INT2_RID_CHG, "muic-rid-chg" },
+ { SM5504_IRQ_INT2_UVLO, "muic-uvlo" },
+ { SM5504_IRQ_INT2_POR, "muic-por" },
+ { SM5504_IRQ_INT2_OVP_FET, "muic-ovp-fet" },
+ { SM5504_IRQ_INT2_OCP_LATCH, "muic-ocp-latch" },
+ { SM5504_IRQ_INT2_OCP_EVENT, "muic-ocp-event" },
+ { SM5504_IRQ_INT2_OVP_OCP_EVENT, "muic-ovp-ocp-event" },
+};
+
+/* Define interrupt list of SM5504 to register regmap_irq */
+static const struct regmap_irq sm5504_irqs[] = {
+ /* INT1 interrupts */
+ { .reg_offset = 0, .mask = SM5504_IRQ_INT1_ATTACH_MASK, },
+ { .reg_offset = 0, .mask = SM5504_IRQ_INT1_DETACH_MASK, },
+ { .reg_offset = 0, .mask = SM5504_IRQ_INT1_CHG_DET_MASK, },
+ { .reg_offset = 0, .mask = SM5504_IRQ_INT1_DCD_OUT_MASK, },
+ { .reg_offset = 0, .mask = SM5504_IRQ_INT1_OVP_MASK, },
+ { .reg_offset = 0, .mask = SM5504_IRQ_INT1_CONNECT_MASK, },
+ { .reg_offset = 0, .mask = SM5504_IRQ_INT1_ADC_CHG_MASK, },
+
+ /* INT2 interrupts */
+ { .reg_offset = 1, .mask = SM5504_IRQ_INT2_RID_CHG_MASK,},
+ { .reg_offset = 1, .mask = SM5504_IRQ_INT2_UVLO_MASK, },
+ { .reg_offset = 1, .mask = SM5504_IRQ_INT2_POR_MASK, },
+ { .reg_offset = 1, .mask = SM5504_IRQ_INT2_OVP_FET_MASK, },
+ { .reg_offset = 1, .mask = SM5504_IRQ_INT2_OCP_LATCH_MASK, },
+ { .reg_offset = 1, .mask = SM5504_IRQ_INT2_OCP_EVENT_MASK, },
+ { .reg_offset = 1, .mask = SM5504_IRQ_INT2_OVP_OCP_EVENT_MASK, },
+};
+
+static const struct regmap_irq_chip sm5504_muic_irq_chip = {
+ .name = "sm5504",
+ .status_base = SM5502_REG_INT1,
+ .mask_base = SM5502_REG_INTMASK1,
+ .num_regs = 2,
+ .irqs = sm5504_irqs,
+ .num_irqs = ARRAY_SIZE(sm5504_irqs),
+};
+
/* Define regmap configuration of SM5502 for I2C communication */
static bool sm5502_muic_volatile_reg(struct device *dev, unsigned int reg)
{
@@ -291,11 +373,25 @@ static unsigned int sm5502_muic_get_cable_type(struct sm5502_muic_info *info)
* connected with to MUIC device.
*/
cable_type = adc & SM5502_REG_ADC_MASK;
- if (cable_type == SM5502_MUIC_ADC_GROUND)
- return SM5502_MUIC_ADC_GROUND;
switch (cable_type) {
case SM5502_MUIC_ADC_GROUND:
+ ret = regmap_read(info->regmap, SM5502_REG_DEV_TYPE1,
+ &dev_type1);
+ if (ret) {
+ dev_err(info->dev, "failed to read DEV_TYPE1 reg\n");
+ return ret;
+ }
+
+ if (dev_type1 == info->type->otg_dev_type1) {
+ cable_type = SM5502_MUIC_ADC_GROUND_USB_OTG;
+ } else {
+ dev_dbg(info->dev,
+ "cannot identify the cable type: adc(0x%x), dev_type1(0x%x)\n",
+ adc, dev_type1);
+ return -EINVAL;
+ }
+ break;
case SM5502_MUIC_ADC_SEND_END_BUTTON:
case SM5502_MUIC_ADC_REMOTE_S1_BUTTON:
case SM5502_MUIC_ADC_REMOTE_S2_BUTTON:
@@ -342,6 +438,11 @@ static unsigned int sm5502_muic_get_cable_type(struct sm5502_muic_info *info)
return ret;
}
+ if (dev_type1 == info->type->otg_dev_type1) {
+ cable_type = SM5502_MUIC_ADC_OPEN_USB_OTG;
+ break;
+ }
+
switch (dev_type1) {
case SM5502_REG_DEV_TYPE1_USB_SDP_MASK:
cable_type = SM5502_MUIC_ADC_OPEN_USB;
@@ -349,9 +450,6 @@ static unsigned int sm5502_muic_get_cable_type(struct sm5502_muic_info *info)
case SM5502_REG_DEV_TYPE1_DEDICATED_CHG_MASK:
cable_type = SM5502_MUIC_ADC_OPEN_TA;
break;
- case SM5502_REG_DEV_TYPE1_USB_OTG_MASK:
- cable_type = SM5502_MUIC_ADC_OPEN_USB_OTG;
- break;
default:
dev_dbg(info->dev,
"cannot identify the cable type: adc(0x%x)\n",
@@ -396,6 +494,7 @@ static int sm5502_muic_cable_handler(struct sm5502_muic_info *info,
con_sw = DM_DP_SWITCH_OPEN;
vbus_sw = VBUSIN_SWITCH_VBUSOUT;
break;
+ case SM5502_MUIC_ADC_GROUND_USB_OTG:
case SM5502_MUIC_ADC_OPEN_USB_OTG:
id = EXTCON_USB_HOST;
con_sw = DM_DP_SWITCH_USB;
@@ -480,16 +579,44 @@ static int sm5502_parse_irq(struct sm5502_muic_info *info, int irq_type)
return 0;
}
+static int sm5504_parse_irq(struct sm5502_muic_info *info, int irq_type)
+{
+ switch (irq_type) {
+ case SM5504_IRQ_INT1_ATTACH:
+ info->irq_attach = true;
+ break;
+ case SM5504_IRQ_INT1_DETACH:
+ info->irq_detach = true;
+ break;
+ case SM5504_IRQ_INT1_CHG_DET:
+ case SM5504_IRQ_INT1_DCD_OUT:
+ case SM5504_IRQ_INT1_OVP_EVENT:
+ case SM5504_IRQ_INT1_CONNECT:
+ case SM5504_IRQ_INT1_ADC_CHG:
+ case SM5504_IRQ_INT2_RID_CHG:
+ case SM5504_IRQ_INT2_UVLO:
+ case SM5504_IRQ_INT2_POR:
+ case SM5504_IRQ_INT2_OVP_FET:
+ case SM5504_IRQ_INT2_OCP_LATCH:
+ case SM5504_IRQ_INT2_OCP_EVENT:
+ case SM5504_IRQ_INT2_OVP_OCP_EVENT:
+ default:
+ break;
+ }
+
+ return 0;
+}
+
static irqreturn_t sm5502_muic_irq_handler(int irq, void *data)
{
struct sm5502_muic_info *info = data;
int i, irq_type = -1, ret;
- for (i = 0; i < info->num_muic_irqs; i++)
- if (irq == info->muic_irqs[i].virq)
- irq_type = info->muic_irqs[i].irq;
+ for (i = 0; i < info->type->num_muic_irqs; i++)
+ if (irq == info->type->muic_irqs[i].virq)
+ irq_type = info->type->muic_irqs[i].irq;
- ret = sm5502_parse_irq(info, irq_type);
+ ret = info->type->parse_irq(info, irq_type);
if (ret < 0) {
dev_warn(info->dev, "cannot handle is interrupt:%d\n",
irq_type);
@@ -534,19 +661,18 @@ static void sm5502_init_dev_type(struct sm5502_muic_info *info)
version_id, vendor_id);
/* Initiazle the register of SM5502 device to bring-up */
- for (i = 0; i < info->num_reg_data; i++) {
+ for (i = 0; i < info->type->num_reg_data; i++) {
unsigned int val = 0;
- if (!info->reg_data[i].invert)
- val |= ~info->reg_data[i].val;
+ if (!info->type->reg_data[i].invert)
+ val |= ~info->type->reg_data[i].val;
else
- val = info->reg_data[i].val;
- regmap_write(info->regmap, info->reg_data[i].reg, val);
+ val = info->type->reg_data[i].val;
+ regmap_write(info->regmap, info->type->reg_data[i].reg, val);
}
}
-static int sm5022_muic_i2c_probe(struct i2c_client *i2c,
- const struct i2c_device_id *id)
+static int sm5022_muic_i2c_probe(struct i2c_client *i2c)
{
struct device_node *np = i2c->dev.of_node;
struct sm5502_muic_info *info;
@@ -563,10 +689,13 @@ static int sm5022_muic_i2c_probe(struct i2c_client *i2c,
info->dev = &i2c->dev;
info->i2c = i2c;
info->irq = i2c->irq;
- info->muic_irqs = sm5502_muic_irqs;
- info->num_muic_irqs = ARRAY_SIZE(sm5502_muic_irqs);
- info->reg_data = sm5502_reg_data;
- info->num_reg_data = ARRAY_SIZE(sm5502_reg_data);
+ info->type = device_get_match_data(info->dev);
+ if (!info->type)
+ return -EINVAL;
+ if (!info->type->parse_irq) {
+ dev_err(info->dev, "parse_irq missing in struct sm5502_type\n");
+ return -EINVAL;
+ }
mutex_init(&info->mutex);
@@ -582,16 +711,17 @@ static int sm5022_muic_i2c_probe(struct i2c_client *i2c,
/* Support irq domain for SM5502 MUIC device */
irq_flags = IRQF_TRIGGER_FALLING | IRQF_ONESHOT | IRQF_SHARED;
- ret = regmap_add_irq_chip(info->regmap, info->irq, irq_flags, 0,
- &sm5502_muic_irq_chip, &info->irq_data);
+ ret = devm_regmap_add_irq_chip(info->dev, info->regmap, info->irq,
+ irq_flags, 0, info->type->irq_chip,
+ &info->irq_data);
if (ret != 0) {
dev_err(info->dev, "failed to request IRQ %d: %d\n",
info->irq, ret);
return ret;
}
- for (i = 0; i < info->num_muic_irqs; i++) {
- struct muic_irq *muic_irq = &info->muic_irqs[i];
+ for (i = 0; i < info->type->num_muic_irqs; i++) {
+ struct muic_irq *muic_irq = &info->type->muic_irqs[i];
int virq = 0;
virq = regmap_irq_get_virq(info->irq_data, muic_irq->irq);
@@ -643,17 +773,30 @@ static int sm5022_muic_i2c_probe(struct i2c_client *i2c,
return 0;
}
-static int sm5502_muic_i2c_remove(struct i2c_client *i2c)
-{
- struct sm5502_muic_info *info = i2c_get_clientdata(i2c);
-
- regmap_del_irq_chip(info->irq, info->irq_data);
+static const struct sm5502_type sm5502_data = {
+ .muic_irqs = sm5502_muic_irqs,
+ .num_muic_irqs = ARRAY_SIZE(sm5502_muic_irqs),
+ .irq_chip = &sm5502_muic_irq_chip,
+ .reg_data = sm5502_reg_data,
+ .num_reg_data = ARRAY_SIZE(sm5502_reg_data),
+ .otg_dev_type1 = SM5502_REG_DEV_TYPE1_USB_OTG_MASK,
+ .parse_irq = sm5502_parse_irq,
+};
- return 0;
-}
+static const struct sm5502_type sm5504_data = {
+ .muic_irqs = sm5504_muic_irqs,
+ .num_muic_irqs = ARRAY_SIZE(sm5504_muic_irqs),
+ .irq_chip = &sm5504_muic_irq_chip,
+ .reg_data = sm5504_reg_data,
+ .num_reg_data = ARRAY_SIZE(sm5504_reg_data),
+ .otg_dev_type1 = SM5504_REG_DEV_TYPE1_USB_OTG_MASK,
+ .parse_irq = sm5504_parse_irq,
+};
static const struct of_device_id sm5502_dt_match[] = {
- { .compatible = "siliconmitus,sm5502-muic" },
+ { .compatible = "siliconmitus,sm5502-muic", .data = &sm5502_data },
+ { .compatible = "siliconmitus,sm5504-muic", .data = &sm5504_data },
+ { .compatible = "siliconmitus,sm5703-muic", .data = &sm5502_data },
{ },
};
MODULE_DEVICE_TABLE(of, sm5502_dt_match);
@@ -684,7 +827,9 @@ static SIMPLE_DEV_PM_OPS(sm5502_muic_pm_ops,
sm5502_muic_suspend, sm5502_muic_resume);
static const struct i2c_device_id sm5502_i2c_id[] = {
- { "sm5502", TYPE_SM5502 },
+ { "sm5502", (kernel_ulong_t)&sm5502_data },
+ { "sm5504", (kernel_ulong_t)&sm5504_data },
+ { "sm5703-muic", (kernel_ulong_t)&sm5502_data },
{ }
};
MODULE_DEVICE_TABLE(i2c, sm5502_i2c_id);
@@ -695,8 +840,7 @@ static struct i2c_driver sm5502_muic_i2c_driver = {
.pm = &sm5502_muic_pm_ops,
.of_match_table = sm5502_dt_match,
},
- .probe = sm5022_muic_i2c_probe,
- .remove = sm5502_muic_i2c_remove,
+ .probe_new = sm5022_muic_i2c_probe,
.id_table = sm5502_i2c_id,
};
diff --git a/drivers/extcon/extcon-sm5502.h b/drivers/extcon/extcon-sm5502.h
index ce1f1ec310c4..9c04315d48e2 100644
--- a/drivers/extcon/extcon-sm5502.h
+++ b/drivers/extcon/extcon-sm5502.h
@@ -8,10 +8,6 @@
#ifndef __LINUX_EXTCON_SM5502_H
#define __LINUX_EXTCON_SM5502_H
-enum sm5502_types {
- TYPE_SM5502,
-};
-
/* SM5502 registers */
enum sm5502_reg {
SM5502_REG_DEVICE_ID = 0x01,
@@ -93,6 +89,13 @@ enum sm5502_reg {
#define SM5502_REG_CONTROL_RAW_DATA_MASK (0x1 << SM5502_REG_CONTROL_RAW_DATA_SHIFT)
#define SM5502_REG_CONTROL_SW_OPEN_MASK (0x1 << SM5502_REG_CONTROL_SW_OPEN_SHIFT)
+#define SM5504_REG_CONTROL_CHGTYP_SHIFT 5
+#define SM5504_REG_CONTROL_USBCHDEN_SHIFT 6
+#define SM5504_REG_CONTROL_ADC_EN_SHIFT 7
+#define SM5504_REG_CONTROL_CHGTYP_MASK (0x1 << SM5504_REG_CONTROL_CHGTYP_SHIFT)
+#define SM5504_REG_CONTROL_USBCHDEN_MASK (0x1 << SM5504_REG_CONTROL_USBCHDEN_SHIFT)
+#define SM5504_REG_CONTROL_ADC_EN_MASK (0x1 << SM5504_REG_CONTROL_ADC_EN_SHIFT)
+
#define SM5502_REG_INTM1_ATTACH_SHIFT 0
#define SM5502_REG_INTM1_DETACH_SHIFT 1
#define SM5502_REG_INTM1_KP_SHIFT 2
@@ -123,6 +126,36 @@ enum sm5502_reg {
#define SM5502_REG_INTM2_STUCK_KEY_RCV_MASK (0x1 << SM5502_REG_INTM2_STUCK_KEY_RCV_SHIFT)
#define SM5502_REG_INTM2_MHL_MASK (0x1 << SM5502_REG_INTM2_MHL_SHIFT)
+#define SM5504_REG_INTM1_ATTACH_SHIFT 0
+#define SM5504_REG_INTM1_DETACH_SHIFT 1
+#define SM5504_REG_INTM1_CHG_DET_SHIFT 2
+#define SM5504_REG_INTM1_DCD_OUT_SHIFT 3
+#define SM5504_REG_INTM1_OVP_EVENT_SHIFT 4
+#define SM5504_REG_INTM1_CONNECT_SHIFT 5
+#define SM5504_REG_INTM1_ADC_CHG_SHIFT 6
+#define SM5504_REG_INTM1_ATTACH_MASK (0x1 << SM5504_REG_INTM1_ATTACH_SHIFT)
+#define SM5504_REG_INTM1_DETACH_MASK (0x1 << SM5504_REG_INTM1_DETACH_SHIFT)
+#define SM5504_REG_INTM1_CHG_DET_MASK (0x1 << SM5504_REG_INTM1_CHG_DET_SHIFT)
+#define SM5504_REG_INTM1_DCD_OUT_MASK (0x1 << SM5504_REG_INTM1_DCD_OUT_SHIFT)
+#define SM5504_REG_INTM1_OVP_EVENT_MASK (0x1 << SM5504_REG_INTM1_OVP_EVENT_SHIFT)
+#define SM5504_REG_INTM1_CONNECT_MASK (0x1 << SM5504_REG_INTM1_CONNECT_SHIFT)
+#define SM5504_REG_INTM1_ADC_CHG_MASK (0x1 << SM5504_REG_INTM1_ADC_CHG_SHIFT)
+
+#define SM5504_REG_INTM2_RID_CHG_SHIFT 0
+#define SM5504_REG_INTM2_UVLO_SHIFT 1
+#define SM5504_REG_INTM2_POR_SHIFT 2
+#define SM5504_REG_INTM2_OVP_FET_SHIFT 4
+#define SM5504_REG_INTM2_OCP_LATCH_SHIFT 5
+#define SM5504_REG_INTM2_OCP_EVENT_SHIFT 6
+#define SM5504_REG_INTM2_OVP_OCP_EVENT_SHIFT 7
+#define SM5504_REG_INTM2_RID_CHG_MASK (0x1 << SM5504_REG_INTM2_RID_CHG_SHIFT)
+#define SM5504_REG_INTM2_UVLO_MASK (0x1 << SM5504_REG_INTM2_UVLO_SHIFT)
+#define SM5504_REG_INTM2_POR_MASK (0x1 << SM5504_REG_INTM2_POR_SHIFT)
+#define SM5504_REG_INTM2_OVP_FET_MASK (0x1 << SM5504_REG_INTM2_OVP_FET_SHIFT)
+#define SM5504_REG_INTM2_OCP_LATCH_MASK (0x1 << SM5504_REG_INTM2_OCP_LATCH_SHIFT)
+#define SM5504_REG_INTM2_OCP_EVENT_MASK (0x1 << SM5504_REG_INTM2_OCP_EVENT_SHIFT)
+#define SM5504_REG_INTM2_OVP_OCP_EVENT_MASK (0x1 << SM5504_REG_INTM2_OVP_OCP_EVENT_SHIFT)
+
#define SM5502_REG_ADC_SHIFT 0
#define SM5502_REG_ADC_MASK (0x1f << SM5502_REG_ADC_SHIFT)
@@ -199,6 +232,9 @@ enum sm5502_reg {
#define SM5502_REG_DEV_TYPE1_DEDICATED_CHG_MASK (0x1 << SM5502_REG_DEV_TYPE1_DEDICATED_CHG_SHIFT)
#define SM5502_REG_DEV_TYPE1_USB_OTG_MASK (0x1 << SM5502_REG_DEV_TYPE1_USB_OTG_SHIFT)
+#define SM5504_REG_DEV_TYPE1_USB_OTG_SHIFT 0
+#define SM5504_REG_DEV_TYPE1_USB_OTG_MASK (0x1 << SM5504_REG_DEV_TYPE1_USB_OTG_SHIFT)
+
#define SM5502_REG_DEV_TYPE2_JIG_USB_ON_SHIFT 0
#define SM5502_REG_DEV_TYPE2_JIG_USB_OFF_SHIFT 1
#define SM5502_REG_DEV_TYPE2_JIG_UART_ON_SHIFT 2
@@ -277,4 +313,42 @@ enum sm5502_irq {
#define SM5502_IRQ_INT2_STUCK_KEY_RCV_MASK BIT(4)
#define SM5502_IRQ_INT2_MHL_MASK BIT(5)
+/* SM5504 Interrupts */
+enum sm5504_irq {
+ /* INT1 */
+ SM5504_IRQ_INT1_ATTACH,
+ SM5504_IRQ_INT1_DETACH,
+ SM5504_IRQ_INT1_CHG_DET,
+ SM5504_IRQ_INT1_DCD_OUT,
+ SM5504_IRQ_INT1_OVP_EVENT,
+ SM5504_IRQ_INT1_CONNECT,
+ SM5504_IRQ_INT1_ADC_CHG,
+
+ /* INT2 */
+ SM5504_IRQ_INT2_RID_CHG,
+ SM5504_IRQ_INT2_UVLO,
+ SM5504_IRQ_INT2_POR,
+ SM5504_IRQ_INT2_OVP_FET,
+ SM5504_IRQ_INT2_OCP_LATCH,
+ SM5504_IRQ_INT2_OCP_EVENT,
+ SM5504_IRQ_INT2_OVP_OCP_EVENT,
+
+ SM5504_IRQ_NUM,
+};
+
+#define SM5504_IRQ_INT1_ATTACH_MASK BIT(0)
+#define SM5504_IRQ_INT1_DETACH_MASK BIT(1)
+#define SM5504_IRQ_INT1_CHG_DET_MASK BIT(2)
+#define SM5504_IRQ_INT1_DCD_OUT_MASK BIT(3)
+#define SM5504_IRQ_INT1_OVP_MASK BIT(4)
+#define SM5504_IRQ_INT1_CONNECT_MASK BIT(5)
+#define SM5504_IRQ_INT1_ADC_CHG_MASK BIT(6)
+#define SM5504_IRQ_INT2_RID_CHG_MASK BIT(0)
+#define SM5504_IRQ_INT2_UVLO_MASK BIT(1)
+#define SM5504_IRQ_INT2_POR_MASK BIT(2)
+#define SM5504_IRQ_INT2_OVP_FET_MASK BIT(4)
+#define SM5504_IRQ_INT2_OCP_LATCH_MASK BIT(5)
+#define SM5504_IRQ_INT2_OCP_EVENT_MASK BIT(6)
+#define SM5504_IRQ_INT2_OVP_OCP_EVENT_MASK BIT(7)
+
#endif /* __LINUX_EXTCON_SM5502_H */
diff --git a/drivers/extcon/extcon-usb-gpio.c b/drivers/extcon/extcon-usb-gpio.c
index 98b5afa5b615..40d967a11e87 100644
--- a/drivers/extcon/extcon-usb-gpio.c
+++ b/drivers/extcon/extcon-usb-gpio.c
@@ -1,24 +1,23 @@
// SPDX-License-Identifier: GPL-2.0-only
-/**
+/*
* drivers/extcon/extcon-usb-gpio.c - USB GPIO extcon driver
*
- * Copyright (C) 2015 Texas Instruments Incorporated - http://www.ti.com
+ * Copyright (C) 2015 Texas Instruments Incorporated - https://www.ti.com
* Author: Roger Quadros <rogerq@ti.com>
*/
#include <linux/extcon-provider.h>
-#include <linux/gpio.h>
#include <linux/gpio/consumer.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/kernel.h>
#include <linux/module.h>
-#include <linux/of_gpio.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
#include <linux/workqueue.h>
#include <linux/pinctrl/consumer.h>
+#include <linux/mod_devicetable.h>
#define USB_GPIO_DEBOUNCE_MS 20 /* ms */
@@ -227,16 +226,6 @@ static int usb_extcon_suspend(struct device *dev)
}
}
- /*
- * We don't want to process any IRQs after this point
- * as GPIOs used behind I2C subsystem might not be
- * accessible until resume completes. So disable IRQ.
- */
- if (info->id_gpiod)
- disable_irq(info->id_irq);
- if (info->vbus_gpiod)
- disable_irq(info->vbus_irq);
-
if (!device_may_wakeup(dev))
pinctrl_pm_select_sleep_state(dev);
@@ -268,11 +257,6 @@ static int usb_extcon_resume(struct device *dev)
}
}
- if (info->id_gpiod)
- enable_irq(info->id_irq);
- if (info->vbus_gpiod)
- enable_irq(info->vbus_irq);
-
queue_delayed_work(system_power_efficient_wq,
&info->wq_detcable, 0);
diff --git a/drivers/extcon/extcon-usbc-cros-ec.c b/drivers/extcon/extcon-usbc-cros-ec.c
index 5290cc2d19d9..fde1db62be0d 100644
--- a/drivers/extcon/extcon-usbc-cros-ec.c
+++ b/drivers/extcon/extcon-usbc-cros-ec.c
@@ -68,7 +68,7 @@ static int cros_ec_pd_command(struct cros_ec_extcon_info *info,
struct cros_ec_command *msg;
int ret;
- msg = kzalloc(sizeof(*msg) + max(outsize, insize), GFP_KERNEL);
+ msg = kzalloc(struct_size(msg, data, max(outsize, insize)), GFP_KERNEL);
if (!msg)
return -ENOMEM;
diff --git a/drivers/extcon/extcon-usbc-tusb320.c b/drivers/extcon/extcon-usbc-tusb320.c
new file mode 100644
index 000000000000..41041ff0fadb
--- /dev/null
+++ b/drivers/extcon/extcon-usbc-tusb320.c
@@ -0,0 +1,513 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * drivers/extcon/extcon-tusb320.c - TUSB320 extcon driver
+ *
+ * Copyright (C) 2020 National Instruments Corporation
+ * Author: Michael Auchter <michael.auchter@ni.com>
+ */
+
+#include <linux/bitfield.h>
+#include <linux/extcon-provider.h>
+#include <linux/i2c.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/regmap.h>
+#include <linux/usb/typec.h>
+
+#define TUSB320_REG8 0x8
+#define TUSB320_REG8_CURRENT_MODE_ADVERTISE GENMASK(7, 6)
+#define TUSB320_REG8_CURRENT_MODE_ADVERTISE_USB 0x0
+#define TUSB320_REG8_CURRENT_MODE_ADVERTISE_15A 0x1
+#define TUSB320_REG8_CURRENT_MODE_ADVERTISE_30A 0x2
+#define TUSB320_REG8_CURRENT_MODE_DETECT GENMASK(5, 4)
+#define TUSB320_REG8_CURRENT_MODE_DETECT_DEF 0x0
+#define TUSB320_REG8_CURRENT_MODE_DETECT_MED 0x1
+#define TUSB320_REG8_CURRENT_MODE_DETECT_ACC 0x2
+#define TUSB320_REG8_CURRENT_MODE_DETECT_HI 0x3
+#define TUSB320_REG8_ACCESSORY_CONNECTED GENMASK(3, 2)
+#define TUSB320_REG8_ACCESSORY_CONNECTED_NONE 0x0
+#define TUSB320_REG8_ACCESSORY_CONNECTED_AUDIO 0x4
+#define TUSB320_REG8_ACCESSORY_CONNECTED_ACC 0x5
+#define TUSB320_REG8_ACCESSORY_CONNECTED_DEBUG 0x6
+#define TUSB320_REG8_ACTIVE_CABLE_DETECTION BIT(0)
+
+#define TUSB320_REG9 0x9
+#define TUSB320_REG9_ATTACHED_STATE_SHIFT 6
+#define TUSB320_REG9_ATTACHED_STATE_MASK 0x3
+#define TUSB320_REG9_CABLE_DIRECTION BIT(5)
+#define TUSB320_REG9_INTERRUPT_STATUS BIT(4)
+
+#define TUSB320_REGA 0xa
+#define TUSB320L_REGA_DISABLE_TERM BIT(0)
+#define TUSB320_REGA_I2C_SOFT_RESET BIT(3)
+#define TUSB320_REGA_MODE_SELECT_SHIFT 4
+#define TUSB320_REGA_MODE_SELECT_MASK 0x3
+
+#define TUSB320L_REGA0_REVISION 0xa0
+
+enum tusb320_attached_state {
+ TUSB320_ATTACHED_STATE_NONE,
+ TUSB320_ATTACHED_STATE_DFP,
+ TUSB320_ATTACHED_STATE_UFP,
+ TUSB320_ATTACHED_STATE_ACC,
+};
+
+enum tusb320_mode {
+ TUSB320_MODE_PORT,
+ TUSB320_MODE_UFP,
+ TUSB320_MODE_DFP,
+ TUSB320_MODE_DRP,
+};
+
+struct tusb320_priv;
+
+struct tusb320_ops {
+ int (*set_mode)(struct tusb320_priv *priv, enum tusb320_mode mode);
+ int (*get_revision)(struct tusb320_priv *priv, unsigned int *revision);
+};
+
+struct tusb320_priv {
+ struct device *dev;
+ struct regmap *regmap;
+ struct extcon_dev *edev;
+ struct tusb320_ops *ops;
+ enum tusb320_attached_state state;
+ struct typec_port *port;
+ struct typec_capability cap;
+ enum typec_port_type port_type;
+ enum typec_pwr_opmode pwr_opmode;
+};
+
+static const char * const tusb_attached_states[] = {
+ [TUSB320_ATTACHED_STATE_NONE] = "not attached",
+ [TUSB320_ATTACHED_STATE_DFP] = "downstream facing port",
+ [TUSB320_ATTACHED_STATE_UFP] = "upstream facing port",
+ [TUSB320_ATTACHED_STATE_ACC] = "accessory",
+};
+
+static const unsigned int tusb320_extcon_cable[] = {
+ EXTCON_USB,
+ EXTCON_USB_HOST,
+ EXTCON_NONE,
+};
+
+static int tusb320_check_signature(struct tusb320_priv *priv)
+{
+ static const char sig[] = { '\0', 'T', 'U', 'S', 'B', '3', '2', '0' };
+ unsigned val;
+ int i, ret;
+
+ for (i = 0; i < sizeof(sig); i++) {
+ ret = regmap_read(priv->regmap, sizeof(sig) - 1 - i, &val);
+ if (ret < 0)
+ return ret;
+ if (val != sig[i]) {
+ dev_err(priv->dev, "signature mismatch!\n");
+ return -ENODEV;
+ }
+ }
+
+ return 0;
+}
+
+static int tusb320_set_mode(struct tusb320_priv *priv, enum tusb320_mode mode)
+{
+ int ret;
+
+ /* Mode cannot be changed while cable is attached */
+ if (priv->state != TUSB320_ATTACHED_STATE_NONE)
+ return -EBUSY;
+
+ /* Write mode */
+ ret = regmap_write_bits(priv->regmap, TUSB320_REGA,
+ TUSB320_REGA_MODE_SELECT_MASK << TUSB320_REGA_MODE_SELECT_SHIFT,
+ mode << TUSB320_REGA_MODE_SELECT_SHIFT);
+ if (ret) {
+ dev_err(priv->dev, "failed to write mode: %d\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int tusb320l_set_mode(struct tusb320_priv *priv, enum tusb320_mode mode)
+{
+ int ret;
+
+ /* Disable CC state machine */
+ ret = regmap_write_bits(priv->regmap, TUSB320_REGA,
+ TUSB320L_REGA_DISABLE_TERM, 1);
+ if (ret) {
+ dev_err(priv->dev,
+ "failed to disable CC state machine: %d\n", ret);
+ return ret;
+ }
+
+ /* Write mode */
+ ret = regmap_write_bits(priv->regmap, TUSB320_REGA,
+ TUSB320_REGA_MODE_SELECT_MASK << TUSB320_REGA_MODE_SELECT_SHIFT,
+ mode << TUSB320_REGA_MODE_SELECT_SHIFT);
+ if (ret) {
+ dev_err(priv->dev, "failed to write mode: %d\n", ret);
+ goto err;
+ }
+
+ msleep(5);
+err:
+ /* Re-enable CC state machine */
+ ret = regmap_write_bits(priv->regmap, TUSB320_REGA,
+ TUSB320L_REGA_DISABLE_TERM, 0);
+ if (ret)
+ dev_err(priv->dev,
+ "failed to re-enable CC state machine: %d\n", ret);
+
+ return ret;
+}
+
+static int tusb320_reset(struct tusb320_priv *priv)
+{
+ int ret;
+
+ /* Set mode to default (follow PORT pin) */
+ ret = priv->ops->set_mode(priv, TUSB320_MODE_PORT);
+ if (ret && ret != -EBUSY) {
+ dev_err(priv->dev,
+ "failed to set mode to PORT: %d\n", ret);
+ return ret;
+ }
+
+ /* Perform soft reset */
+ ret = regmap_write_bits(priv->regmap, TUSB320_REGA,
+ TUSB320_REGA_I2C_SOFT_RESET, 1);
+ if (ret) {
+ dev_err(priv->dev,
+ "failed to write soft reset bit: %d\n", ret);
+ return ret;
+ }
+
+ /* Wait for chip to go through reset */
+ msleep(95);
+
+ return 0;
+}
+
+static int tusb320l_get_revision(struct tusb320_priv *priv, unsigned int *revision)
+{
+ return regmap_read(priv->regmap, TUSB320L_REGA0_REVISION, revision);
+}
+
+static struct tusb320_ops tusb320_ops = {
+ .set_mode = tusb320_set_mode,
+};
+
+static struct tusb320_ops tusb320l_ops = {
+ .set_mode = tusb320l_set_mode,
+ .get_revision = tusb320l_get_revision,
+};
+
+static int tusb320_set_adv_pwr_mode(struct tusb320_priv *priv)
+{
+ u8 mode;
+
+ if (priv->pwr_opmode == TYPEC_PWR_MODE_USB)
+ mode = TUSB320_REG8_CURRENT_MODE_ADVERTISE_USB;
+ else if (priv->pwr_opmode == TYPEC_PWR_MODE_1_5A)
+ mode = TUSB320_REG8_CURRENT_MODE_ADVERTISE_15A;
+ else if (priv->pwr_opmode == TYPEC_PWR_MODE_3_0A)
+ mode = TUSB320_REG8_CURRENT_MODE_ADVERTISE_30A;
+ else /* No other mode is supported. */
+ return -EINVAL;
+
+ return regmap_write_bits(priv->regmap, TUSB320_REG8,
+ TUSB320_REG8_CURRENT_MODE_ADVERTISE,
+ FIELD_PREP(TUSB320_REG8_CURRENT_MODE_ADVERTISE,
+ mode));
+}
+
+static int tusb320_port_type_set(struct typec_port *port,
+ enum typec_port_type type)
+{
+ struct tusb320_priv *priv = typec_get_drvdata(port);
+
+ if (type == TYPEC_PORT_SRC)
+ return priv->ops->set_mode(priv, TUSB320_MODE_DFP);
+ else if (type == TYPEC_PORT_SNK)
+ return priv->ops->set_mode(priv, TUSB320_MODE_UFP);
+ else if (type == TYPEC_PORT_DRP)
+ return priv->ops->set_mode(priv, TUSB320_MODE_DRP);
+ else
+ return priv->ops->set_mode(priv, TUSB320_MODE_PORT);
+}
+
+static const struct typec_operations tusb320_typec_ops = {
+ .port_type_set = tusb320_port_type_set,
+};
+
+static void tusb320_extcon_irq_handler(struct tusb320_priv *priv, u8 reg)
+{
+ int state, polarity;
+
+ state = (reg >> TUSB320_REG9_ATTACHED_STATE_SHIFT) &
+ TUSB320_REG9_ATTACHED_STATE_MASK;
+ polarity = !!(reg & TUSB320_REG9_CABLE_DIRECTION);
+
+ dev_dbg(priv->dev, "attached state: %s, polarity: %d\n",
+ tusb_attached_states[state], polarity);
+
+ extcon_set_state(priv->edev, EXTCON_USB,
+ state == TUSB320_ATTACHED_STATE_UFP);
+ extcon_set_state(priv->edev, EXTCON_USB_HOST,
+ state == TUSB320_ATTACHED_STATE_DFP);
+ extcon_set_property(priv->edev, EXTCON_USB,
+ EXTCON_PROP_USB_TYPEC_POLARITY,
+ (union extcon_property_value)polarity);
+ extcon_set_property(priv->edev, EXTCON_USB_HOST,
+ EXTCON_PROP_USB_TYPEC_POLARITY,
+ (union extcon_property_value)polarity);
+ extcon_sync(priv->edev, EXTCON_USB);
+ extcon_sync(priv->edev, EXTCON_USB_HOST);
+
+ priv->state = state;
+}
+
+static void tusb320_typec_irq_handler(struct tusb320_priv *priv, u8 reg9)
+{
+ struct typec_port *port = priv->port;
+ struct device *dev = priv->dev;
+ u8 mode, role, state;
+ int ret, reg8;
+ bool ori;
+
+ ori = reg9 & TUSB320_REG9_CABLE_DIRECTION;
+ typec_set_orientation(port, ori ? TYPEC_ORIENTATION_REVERSE :
+ TYPEC_ORIENTATION_NORMAL);
+
+ state = (reg9 >> TUSB320_REG9_ATTACHED_STATE_SHIFT) &
+ TUSB320_REG9_ATTACHED_STATE_MASK;
+ if (state == TUSB320_ATTACHED_STATE_DFP)
+ role = TYPEC_SOURCE;
+ else
+ role = TYPEC_SINK;
+
+ typec_set_vconn_role(port, role);
+ typec_set_pwr_role(port, role);
+ typec_set_data_role(port, role == TYPEC_SOURCE ?
+ TYPEC_HOST : TYPEC_DEVICE);
+
+ ret = regmap_read(priv->regmap, TUSB320_REG8, &reg8);
+ if (ret) {
+ dev_err(dev, "error during reg8 i2c read, ret=%d!\n", ret);
+ return;
+ }
+
+ mode = FIELD_GET(TUSB320_REG8_CURRENT_MODE_DETECT, reg8);
+ if (mode == TUSB320_REG8_CURRENT_MODE_DETECT_DEF)
+ typec_set_pwr_opmode(port, TYPEC_PWR_MODE_USB);
+ else if (mode == TUSB320_REG8_CURRENT_MODE_DETECT_MED)
+ typec_set_pwr_opmode(port, TYPEC_PWR_MODE_1_5A);
+ else if (mode == TUSB320_REG8_CURRENT_MODE_DETECT_HI)
+ typec_set_pwr_opmode(port, TYPEC_PWR_MODE_3_0A);
+ else /* Charge through accessory */
+ typec_set_pwr_opmode(port, TYPEC_PWR_MODE_USB);
+}
+
+static irqreturn_t tusb320_irq_handler(int irq, void *dev_id)
+{
+ struct tusb320_priv *priv = dev_id;
+ unsigned int reg;
+
+ if (regmap_read(priv->regmap, TUSB320_REG9, &reg)) {
+ dev_err(priv->dev, "error during i2c read!\n");
+ return IRQ_NONE;
+ }
+
+ if (!(reg & TUSB320_REG9_INTERRUPT_STATUS))
+ return IRQ_NONE;
+
+ tusb320_extcon_irq_handler(priv, reg);
+ tusb320_typec_irq_handler(priv, reg);
+
+ regmap_write(priv->regmap, TUSB320_REG9, reg);
+
+ return IRQ_HANDLED;
+}
+
+static const struct regmap_config tusb320_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+};
+
+static int tusb320_extcon_probe(struct tusb320_priv *priv)
+{
+ int ret;
+
+ priv->edev = devm_extcon_dev_allocate(priv->dev, tusb320_extcon_cable);
+ if (IS_ERR(priv->edev)) {
+ dev_err(priv->dev, "failed to allocate extcon device\n");
+ return PTR_ERR(priv->edev);
+ }
+
+ ret = devm_extcon_dev_register(priv->dev, priv->edev);
+ if (ret < 0) {
+ dev_err(priv->dev, "failed to register extcon device\n");
+ return ret;
+ }
+
+ extcon_set_property_capability(priv->edev, EXTCON_USB,
+ EXTCON_PROP_USB_TYPEC_POLARITY);
+ extcon_set_property_capability(priv->edev, EXTCON_USB_HOST,
+ EXTCON_PROP_USB_TYPEC_POLARITY);
+
+ return 0;
+}
+
+static int tusb320_typec_probe(struct i2c_client *client,
+ struct tusb320_priv *priv)
+{
+ struct fwnode_handle *connector;
+ const char *cap_str;
+ int ret;
+
+ /* The Type-C connector is optional, for backward compatibility. */
+ connector = device_get_named_child_node(&client->dev, "connector");
+ if (!connector)
+ return 0;
+
+ /* Type-C connector found. */
+ ret = typec_get_fw_cap(&priv->cap, connector);
+ if (ret)
+ return ret;
+
+ priv->port_type = priv->cap.type;
+
+ /* This goes into register 0x8 field CURRENT_MODE_ADVERTISE */
+ ret = fwnode_property_read_string(connector, "typec-power-opmode", &cap_str);
+ if (ret)
+ return ret;
+
+ ret = typec_find_pwr_opmode(cap_str);
+ if (ret < 0)
+ return ret;
+ if (ret == TYPEC_PWR_MODE_PD)
+ return -EINVAL;
+
+ priv->pwr_opmode = ret;
+
+ /* Initialize the hardware with the devicetree settings. */
+ ret = tusb320_set_adv_pwr_mode(priv);
+ if (ret)
+ return ret;
+
+ priv->cap.revision = USB_TYPEC_REV_1_1;
+ priv->cap.accessory[0] = TYPEC_ACCESSORY_AUDIO;
+ priv->cap.accessory[1] = TYPEC_ACCESSORY_DEBUG;
+ priv->cap.orientation_aware = true;
+ priv->cap.driver_data = priv;
+ priv->cap.ops = &tusb320_typec_ops;
+ priv->cap.fwnode = connector;
+
+ priv->port = typec_register_port(&client->dev, &priv->cap);
+ if (IS_ERR(priv->port))
+ return PTR_ERR(priv->port);
+
+ return 0;
+}
+
+static int tusb320_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct tusb320_priv *priv;
+ const void *match_data;
+ unsigned int revision;
+ int ret;
+
+ priv = devm_kzalloc(&client->dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+ priv->dev = &client->dev;
+
+ priv->regmap = devm_regmap_init_i2c(client, &tusb320_regmap_config);
+ if (IS_ERR(priv->regmap))
+ return PTR_ERR(priv->regmap);
+
+ ret = tusb320_check_signature(priv);
+ if (ret)
+ return ret;
+
+ match_data = device_get_match_data(&client->dev);
+ if (!match_data)
+ return -EINVAL;
+
+ priv->ops = (struct tusb320_ops*)match_data;
+
+ if (priv->ops->get_revision) {
+ ret = priv->ops->get_revision(priv, &revision);
+ if (ret)
+ dev_warn(priv->dev,
+ "failed to read revision register: %d\n", ret);
+ else
+ dev_info(priv->dev, "chip revision %d\n", revision);
+ }
+
+ ret = tusb320_extcon_probe(priv);
+ if (ret)
+ return ret;
+
+ ret = tusb320_typec_probe(client, priv);
+ if (ret)
+ return ret;
+
+ /* update initial state */
+ tusb320_irq_handler(client->irq, priv);
+
+ /* Reset chip to its default state */
+ ret = tusb320_reset(priv);
+ if (ret)
+ dev_warn(priv->dev, "failed to reset chip: %d\n", ret);
+ else
+ /*
+ * State and polarity might change after a reset, so update
+ * them again and make sure the interrupt status bit is cleared.
+ */
+ tusb320_irq_handler(client->irq, priv);
+
+ ret = devm_request_threaded_irq(priv->dev, client->irq, NULL,
+ tusb320_irq_handler,
+ IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
+ client->name, priv);
+
+ return ret;
+}
+
+static const struct of_device_id tusb320_extcon_dt_match[] = {
+ { .compatible = "ti,tusb320", .data = &tusb320_ops, },
+ { .compatible = "ti,tusb320l", .data = &tusb320l_ops, },
+ { }
+};
+MODULE_DEVICE_TABLE(of, tusb320_extcon_dt_match);
+
+static struct i2c_driver tusb320_extcon_driver = {
+ .probe = tusb320_probe,
+ .driver = {
+ .name = "extcon-tusb320",
+ .of_match_table = tusb320_extcon_dt_match,
+ },
+};
+
+static int __init tusb320_init(void)
+{
+ return i2c_add_driver(&tusb320_extcon_driver);
+}
+subsys_initcall(tusb320_init);
+
+static void __exit tusb320_exit(void)
+{
+ i2c_del_driver(&tusb320_extcon_driver);
+}
+module_exit(tusb320_exit);
+
+MODULE_AUTHOR("Michael Auchter <michael.auchter@ni.com>");
+MODULE_DESCRIPTION("TI TUSB320 extcon driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/extcon/extcon.c b/drivers/extcon/extcon.c
index e055893fd5c3..e1c71359b605 100644
--- a/drivers/extcon/extcon.c
+++ b/drivers/extcon/extcon.c
@@ -167,6 +167,16 @@ static const struct __extcon_info {
.id = EXTCON_DISP_HMD,
.name = "HMD",
},
+ [EXTCON_DISP_CVBS] = {
+ .type = EXTCON_TYPE_DISP,
+ .id = EXTCON_DISP_CVBS,
+ .name = "CVBS",
+ },
+ [EXTCON_DISP_EDP] = {
+ .type = EXTCON_TYPE_DISP,
+ .id = EXTCON_DISP_EDP,
+ .name = "EDP",
+ },
/* Miscellaneous external connector */
[EXTCON_DOCK] = {
@@ -247,7 +257,7 @@ static int find_cable_index_by_id(struct extcon_dev *edev, const unsigned int id
{
int i;
- /* Find the the index of extcon cable in edev->supported_cable */
+ /* Find the index of extcon cable in edev->supported_cable */
for (i = 0; i < edev->max_supported; i++) {
if (edev->supported_cable[i] == id)
return i;
@@ -399,6 +409,7 @@ static ssize_t cable_state_show(struct device *dev,
/**
* extcon_sync() - Synchronize the state for an external connector.
* @edev: the extcon device
+ * @id: the unique id indicating an external connector
*
* Note that this function send a notification in order to synchronize
* the state and property of an external connector.
@@ -576,19 +587,7 @@ EXPORT_SYMBOL_GPL(extcon_set_state);
*/
int extcon_set_state_sync(struct extcon_dev *edev, unsigned int id, bool state)
{
- int ret, index;
- unsigned long flags;
-
- index = find_cable_index_by_id(edev, id);
- if (index < 0)
- return index;
-
- /* Check whether the external connector's state is changed. */
- spin_lock_irqsave(&edev->lock, flags);
- ret = is_extcon_changed(edev, index, state);
- spin_unlock_irqrestore(&edev->lock, flags);
- if (!ret)
- return 0;
+ int ret;
ret = extcon_set_state(edev, id, state);
if (ret < 0)
@@ -748,6 +747,9 @@ EXPORT_SYMBOL_GPL(extcon_set_property);
/**
* extcon_set_property_sync() - Set property of an external connector with sync.
+ * @edev: the extcon device
+ * @id: the unique id indicating an external connector
+ * @prop: the property id indicating an extcon property
* @prop_val: the pointer including the new value of extcon property
*
* Note that when setting the property value of external connector,
@@ -863,6 +865,8 @@ EXPORT_SYMBOL_GPL(extcon_set_property_capability);
* @extcon_name: the extcon name provided with extcon_dev_register()
*
* Return the pointer of extcon device if success or ERR_PTR(err) if fail.
+ * NOTE: This function returns -EPROBE_DEFER so it may only be called from
+ * probe() functions.
*/
struct extcon_dev *extcon_get_extcon_dev(const char *extcon_name)
{
@@ -876,7 +880,7 @@ struct extcon_dev *extcon_get_extcon_dev(const char *extcon_name)
if (!strcmp(sd->name, extcon_name))
goto out;
}
- sd = NULL;
+ sd = ERR_PTR(-EPROBE_DEFER);
out:
mutex_unlock(&extcon_dev_list_lock);
return sd;
@@ -900,7 +904,7 @@ int extcon_register_notifier(struct extcon_dev *edev, unsigned int id,
struct notifier_block *nb)
{
unsigned long flags;
- int ret, idx = -EINVAL;
+ int ret, idx;
if (!edev || !nb)
return -EINVAL;
@@ -1230,18 +1234,14 @@ int extcon_dev_register(struct extcon_dev *edev)
edev->dev.type = &edev->extcon_dev_type;
}
- ret = device_register(&edev->dev);
- if (ret) {
- put_device(&edev->dev);
- goto err_dev;
- }
-
spin_lock_init(&edev->lock);
- edev->nh = devm_kcalloc(&edev->dev, edev->max_supported,
- sizeof(*edev->nh), GFP_KERNEL);
- if (!edev->nh) {
- ret = -ENOMEM;
- goto err_dev;
+ if (edev->max_supported) {
+ edev->nh = kcalloc(edev->max_supported, sizeof(*edev->nh),
+ GFP_KERNEL);
+ if (!edev->nh) {
+ ret = -ENOMEM;
+ goto err_alloc_nh;
+ }
}
for (index = 0; index < edev->max_supported; index++)
@@ -1252,6 +1252,12 @@ int extcon_dev_register(struct extcon_dev *edev)
dev_set_drvdata(&edev->dev, edev);
edev->state = 0;
+ ret = device_register(&edev->dev);
+ if (ret) {
+ put_device(&edev->dev);
+ goto err_dev;
+ }
+
mutex_lock(&extcon_dev_list_lock);
list_add(&edev->entry, &extcon_dev_list);
mutex_unlock(&extcon_dev_list_lock);
@@ -1260,6 +1266,9 @@ int extcon_dev_register(struct extcon_dev *edev)
err_dev:
if (edev->max_supported)
+ kfree(edev->nh);
+err_alloc_nh:
+ if (edev->max_supported)
kfree(edev->extcon_dev_type.groups);
err_alloc_groups:
if (edev->max_supported && edev->mutually_exclusive) {
@@ -1319,6 +1328,7 @@ void extcon_dev_unregister(struct extcon_dev *edev)
if (edev->max_supported) {
kfree(edev->extcon_dev_type.groups);
kfree(edev->cables);
+ kfree(edev->nh);
}
put_device(&edev->dev);
@@ -1406,6 +1416,7 @@ const char *extcon_get_edev_name(struct extcon_dev *edev)
{
return !edev ? NULL : edev->name;
}
+EXPORT_SYMBOL_GPL(extcon_get_edev_name);
static int __init extcon_class_init(void)
{