Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 6a57bad6 authored by Greg Kroah-Hartman's avatar Greg Kroah-Hartman
Browse files

Merge tag 'extcon-next-for-3.16' of...

Merge tag 'extcon-next-for-3.16' of git://git.kernel.org/pub/scm/linux/kernel/git/chanwoo/extcon into char-misc-next

Chanwoo writes:

Update extcon for v3.16

This patchset add resource-managed functions to automatically control the memory
and unregistration operation of extcon. Also, This series support new MAX77836
extcon device driver on existing MAX14577 device because existed a little
difference between MAX77836 and MAX14577. Finally, Fix minor issue of extcon
driver.

Detailed description for patchset:
1. Add resource-managed functions
- Add resource-managed functions to automatically free the memory of extcon
structure and to control unregistration behavior as following. This new devm_*
functions applied all of extcon drivers in drivers/extcon/.
: devm_extcon_dev_register/unregister()
: devm_extcon_dev_allocate/free()
: extcon_dev_allocate/free() for devm_extcon_dev_allocate/free()

2. Add new MAX77836 extcon device
- Support MAX77836 device on existing MAX14577 device driver using
different compatible string. This patchset has dependency on MFD/
Regulator/Extcon. So, Lee Jones(MFD Maintainer) created Immutable
branch between MFD and Extcon due for v3.16 merge-window and then
I merged this patchset from MFD git repo[1] to Extcon git repo.
: [1] git://git.kernel.org/pub/scm/linux/kernel/git/lee/mfd
  (branch: ib-mfd-extcon-3.16)

3. Fix minor issue of extcon driver
- extcon-palmas driver
: Fix issue of extcon device name for probe
- extcon-max14577
: Fix probe failure about handling wrong return value.
: Properly Handle return value of regmap_irq_get_virq function.
- extcon-max8997/max77693 driver
: Fix NULL pointer exception on missing pdata

4. Code clean for extcon driver
- extcon-max8997/max77693
: Use power efficient workqueue for delayed cable detection
parents 4f063810 3f79a3fb
Loading
Loading
Loading
Loading
+2 −2
Original line number Diff line number Diff line
@@ -28,13 +28,13 @@ config EXTCON_ADC_JACK
	  Say Y here to enable extcon device driver based on ADC values.

config EXTCON_MAX14577
	tristate "MAX14577 EXTCON Support"
	tristate "MAX14577/77836 EXTCON Support"
	depends on MFD_MAX14577
	select IRQ_DOMAIN
	select REGMAP_I2C
	help
	  If you say yes here you get support for the MUIC device of
	  Maxim MAX14577 PMIC. The MAX14577 MUIC is a USB port accessory
	  Maxim MAX14577/77836. The MAX14577/77836 MUIC is a USB port accessory
	  detector and switch.

config EXTCON_MAX77693
+20 −29
Original line number Diff line number Diff line
@@ -39,7 +39,7 @@
 * @chan:		iio channel being queried.
 */
struct adc_jack_data {
	struct extcon_dev edev;
	struct extcon_dev *edev;

	const char **cable_names;
	int num_cables;
@@ -64,7 +64,7 @@ static void adc_jack_handler(struct work_struct *work)

	ret = iio_read_channel_raw(data->chan, &adc_val);
	if (ret < 0) {
		dev_err(&data->edev.dev, "read channel() error: %d\n", ret);
		dev_err(&data->edev->dev, "read channel() error: %d\n", ret);
		return;
	}

@@ -80,7 +80,7 @@ static void adc_jack_handler(struct work_struct *work)
	}
	/* if no def has met, it means state = 0 (no cables attached) */

	extcon_set_state(&data->edev, state);
	extcon_set_state(data->edev, state);
}

static irqreturn_t adc_jack_irq_thread(int irq, void *_data)
@@ -102,33 +102,33 @@ static int adc_jack_probe(struct platform_device *pdev)
	if (!data)
		return -ENOMEM;

	data->edev.name = pdata->name;

	if (!pdata->cable_names) {
		err = -EINVAL;
		dev_err(&pdev->dev, "error: cable_names not defined.\n");
		goto out;
		return -EINVAL;
	}

	data->edev.dev.parent = &pdev->dev;
	data->edev.supported_cable = pdata->cable_names;
	data->edev = devm_extcon_dev_allocate(&pdev->dev, pdata->cable_names);
	if (IS_ERR(data->edev)) {
		dev_err(&pdev->dev, "failed to allocate extcon device\n");
		return -ENOMEM;
	}
	data->edev->dev.parent = &pdev->dev;
	data->edev->name = pdata->name;

	/* Check the length of array and set num_cables */
	for (i = 0; data->edev.supported_cable[i]; i++)
	for (i = 0; data->edev->supported_cable[i]; i++)
		;
	if (i == 0 || i > SUPPORTED_CABLE_MAX) {
		err = -EINVAL;
		dev_err(&pdev->dev, "error: pdata->cable_names size = %d\n",
				i - 1);
		goto out;
		return -EINVAL;
	}
	data->num_cables = i;

	if (!pdata->adc_conditions ||
			!pdata->adc_conditions[0].state) {
		err = -EINVAL;
		dev_err(&pdev->dev, "error: adc_conditions not defined.\n");
		goto out;
		return -EINVAL;
	}
	data->adc_conditions = pdata->adc_conditions;

@@ -138,10 +138,8 @@ static int adc_jack_probe(struct platform_device *pdev)
	data->num_conditions = i;

	data->chan = iio_channel_get(&pdev->dev, pdata->consumer_channel);
	if (IS_ERR(data->chan)) {
		err = PTR_ERR(data->chan);
		goto out;
	}
	if (IS_ERR(data->chan))
		return PTR_ERR(data->chan);

	data->handling_delay = msecs_to_jiffies(pdata->handling_delay_ms);

@@ -149,15 +147,14 @@ static int adc_jack_probe(struct platform_device *pdev)

	platform_set_drvdata(pdev, data);

	err = extcon_dev_register(&data->edev);
	err = devm_extcon_dev_register(&pdev->dev, data->edev);
	if (err)
		goto out;
		return err;

	data->irq = platform_get_irq(pdev, 0);
	if (!data->irq) {
		dev_err(&pdev->dev, "platform_get_irq failed\n");
		err = -ENODEV;
		goto err_irq;
		return -ENODEV;
	}

	err = request_any_context_irq(data->irq, adc_jack_irq_thread,
@@ -165,15 +162,10 @@ static int adc_jack_probe(struct platform_device *pdev)

	if (err < 0) {
		dev_err(&pdev->dev, "error: irq %d\n", data->irq);
		goto err_irq;
		return err;
	}

	return 0;

err_irq:
	extcon_dev_unregister(&data->edev);
out:
	return err;
}

static int adc_jack_remove(struct platform_device *pdev)
@@ -182,7 +174,6 @@ static int adc_jack_remove(struct platform_device *pdev)

	free_irq(data->irq, data);
	cancel_work_sync(&data->handler.work);
	extcon_dev_unregister(&data->edev);

	return 0;
}
+20 −20
Original line number Diff line number Diff line
@@ -91,7 +91,7 @@ struct arizona_extcon_info {

	int hpdet_ip;

	struct extcon_dev edev;
	struct extcon_dev *edev;
};

static const struct arizona_micd_config micd_default_modes[] = {
@@ -546,7 +546,7 @@ static irqreturn_t arizona_hpdet_irq(int irq, void *data)
	}

	/* If the cable was removed while measuring ignore the result */
	ret = extcon_get_cable_state_(&info->edev, ARIZONA_CABLE_MECHANICAL);
	ret = extcon_get_cable_state_(info->edev, ARIZONA_CABLE_MECHANICAL);
	if (ret < 0) {
		dev_err(arizona->dev, "Failed to check cable state: %d\n",
			ret);
@@ -581,7 +581,7 @@ static irqreturn_t arizona_hpdet_irq(int irq, void *data)
	else
		report = ARIZONA_CABLE_HEADPHONE;

	ret = extcon_set_cable_state_(&info->edev, report, true);
	ret = extcon_set_cable_state_(info->edev, report, true);
	if (ret != 0)
		dev_err(arizona->dev, "Failed to report HP/line: %d\n",
			ret);
@@ -664,7 +664,7 @@ static void arizona_identify_headphone(struct arizona_extcon_info *info)
			   ARIZONA_ACCDET_MODE_MASK, ARIZONA_ACCDET_MODE_MIC);

	/* Just report headphone */
	ret = extcon_update_state(&info->edev,
	ret = extcon_update_state(info->edev,
				  1 << ARIZONA_CABLE_HEADPHONE,
				  1 << ARIZONA_CABLE_HEADPHONE);
	if (ret != 0)
@@ -723,7 +723,7 @@ static void arizona_start_hpdet_acc_id(struct arizona_extcon_info *info)
			   ARIZONA_ACCDET_MODE_MASK, ARIZONA_ACCDET_MODE_MIC);

	/* Just report headphone */
	ret = extcon_update_state(&info->edev,
	ret = extcon_update_state(info->edev,
				  1 << ARIZONA_CABLE_HEADPHONE,
				  1 << ARIZONA_CABLE_HEADPHONE);
	if (ret != 0)
@@ -764,7 +764,7 @@ static void arizona_micd_detect(struct work_struct *work)
	mutex_lock(&info->lock);

	/* If the cable was removed while measuring ignore the result */
	ret = extcon_get_cable_state_(&info->edev, ARIZONA_CABLE_MECHANICAL);
	ret = extcon_get_cable_state_(info->edev, ARIZONA_CABLE_MECHANICAL);
	if (ret < 0) {
		dev_err(arizona->dev, "Failed to check cable state: %d\n",
				ret);
@@ -812,7 +812,7 @@ static void arizona_micd_detect(struct work_struct *work)
	if (info->detecting && (val & ARIZONA_MICD_LVL_8)) {
		arizona_identify_headphone(info);

		ret = extcon_update_state(&info->edev,
		ret = extcon_update_state(info->edev,
					  1 << ARIZONA_CABLE_MICROPHONE,
					  1 << ARIZONA_CABLE_MICROPHONE);

@@ -999,7 +999,7 @@ static irqreturn_t arizona_jackdet(int irq, void *data)

	if (info->last_jackdet == present) {
		dev_dbg(arizona->dev, "Detected jack\n");
		ret = extcon_set_cable_state_(&info->edev,
		ret = extcon_set_cable_state_(info->edev,
					      ARIZONA_CABLE_MECHANICAL, true);

		if (ret != 0)
@@ -1038,7 +1038,7 @@ static irqreturn_t arizona_jackdet(int irq, void *data)
					 info->micd_ranges[i].key, 0);
		input_sync(info->input);

		ret = extcon_update_state(&info->edev, 0xffffffff, 0);
		ret = extcon_update_state(info->edev, 0xffffffff, 0);
		if (ret != 0)
			dev_err(arizona->dev, "Removal report failed: %d\n",
				ret);
@@ -1105,15 +1105,14 @@ static int arizona_extcon_probe(struct platform_device *pdev)
	info = devm_kzalloc(&pdev->dev, sizeof(*info), GFP_KERNEL);
	if (!info) {
		dev_err(&pdev->dev, "Failed to allocate memory\n");
		ret = -ENOMEM;
		goto err;
		return -ENOMEM;
	}

	info->micvdd = devm_regulator_get(arizona->dev, "MICVDD");
	if (IS_ERR(info->micvdd)) {
		ret = PTR_ERR(info->micvdd);
		dev_err(arizona->dev, "Failed to get MICVDD: %d\n", ret);
		goto err;
		return ret;
	}

	mutex_init(&info->lock);
@@ -1151,15 +1150,19 @@ static int arizona_extcon_probe(struct platform_device *pdev)
		break;
	}

	info->edev.name = "Headset Jack";
	info->edev.dev.parent = arizona->dev;
	info->edev.supported_cable = arizona_cable;
	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;
	}
	info->edev->name = "Headset Jack";
	info->edev->dev.parent = arizona->dev;

	ret = extcon_dev_register(&info->edev);
	ret = devm_extcon_dev_register(&pdev->dev, info->edev);
	if (ret < 0) {
		dev_err(arizona->dev, "extcon_dev_register() failed: %d\n",
			ret);
		goto err;
		return ret;
	}

	info->input = devm_input_allocate_device(&pdev->dev);
@@ -1410,8 +1413,6 @@ static int arizona_extcon_probe(struct platform_device *pdev)
err_input:
err_register:
	pm_runtime_disable(&pdev->dev);
	extcon_dev_unregister(&info->edev);
err:
	return ret;
}

@@ -1445,7 +1446,6 @@ static int arizona_extcon_remove(struct platform_device *pdev)
	regmap_update_bits(arizona->regmap, ARIZONA_JACK_DETECT_ANALOGUE,
			   ARIZONA_JD1_ENA, 0);
	arizona_clk32k_disable(arizona);
	extcon_dev_unregister(&info->edev);

	return 0;
}
+151 −0
Original line number Diff line number Diff line
@@ -565,6 +565,100 @@ static void dummy_sysfs_dev_release(struct device *dev)
{
}

/*
 * extcon_dev_allocate() - Allocate the memory of extcon device.
 * @supported_cable:	Array of supported cable names ending with NULL.
 *			If supported_cable is NULL, cable name related APIs
 *			are disabled.
 *
 * This function allocates the memory for extcon device without allocating
 * memory in each extcon provider driver and initialize default setting for
 * extcon device.
 *
 * Return the pointer of extcon device if success or ERR_PTR(err) if fail
 */
struct extcon_dev *extcon_dev_allocate(const char **supported_cable)
{
	struct extcon_dev *edev;

	edev = kzalloc(sizeof(*edev), GFP_KERNEL);
	if (!edev)
		return ERR_PTR(-ENOMEM);

	edev->max_supported = 0;
	edev->supported_cable = supported_cable;

	return edev;
}

/*
 * extcon_dev_free() - Free the memory of extcon device.
 * @edev:	the extcon device to free
 */
void extcon_dev_free(struct extcon_dev *edev)
{
	kfree(edev);
}
EXPORT_SYMBOL_GPL(extcon_dev_free);

static int devm_extcon_dev_match(struct device *dev, void *res, void *data)
{
	struct extcon_dev **r = res;

	if (WARN_ON(!r || !*r))
		return 0;

	return *r == data;
}

static void devm_extcon_dev_release(struct device *dev, void *res)
{
	extcon_dev_free(*(struct extcon_dev **)res);
}

/**
 * devm_extcon_dev_allocate - Allocate managed extcon device
 * @dev:		device owning the extcon device being created
 * @supported_cable:	Array of supported cable names ending with NULL.
 *			If supported_cable is NULL, cable name related APIs
 *			are disabled.
 *
 * This function manages automatically the memory of extcon device using device
 * resource management and simplify the control of freeing the memory of extcon
 * device.
 *
 * Returns the pointer memory of allocated extcon_dev if success
 * or ERR_PTR(err) if fail
 */
struct extcon_dev *devm_extcon_dev_allocate(struct device *dev,
					    const char **supported_cable)
{
	struct extcon_dev **ptr, *edev;

	ptr = devres_alloc(devm_extcon_dev_release, sizeof(*ptr), GFP_KERNEL);
	if (!ptr)
		return ERR_PTR(-ENOMEM);

	edev = extcon_dev_allocate(supported_cable);
	if (IS_ERR(edev)) {
		devres_free(ptr);
		return edev;
	}

	*ptr = edev;
	devres_add(dev, ptr);

	return edev;
}
EXPORT_SYMBOL_GPL(devm_extcon_dev_allocate);

void devm_extcon_dev_free(struct device *dev, struct extcon_dev *edev)
{
	WARN_ON(devres_release(dev, devm_extcon_dev_release,
			       devm_extcon_dev_match, edev));
}
EXPORT_SYMBOL_GPL(devm_extcon_dev_free);

/**
 * extcon_dev_register() - Register a new extcon device
 * @edev	: the new extcon device (should be allocated before calling)
@@ -819,6 +913,63 @@ void extcon_dev_unregister(struct extcon_dev *edev)
}
EXPORT_SYMBOL_GPL(extcon_dev_unregister);

static void devm_extcon_dev_unreg(struct device *dev, void *res)
{
	extcon_dev_unregister(*(struct extcon_dev **)res);
}

/**
 * devm_extcon_dev_register() - Resource-managed extcon_dev_register()
 * @dev:	device to allocate extcon device
 * @edev:	the new extcon device to register
 *
 * Managed extcon_dev_register() function. If extcon device is attached with
 * this function, that extcon device is automatically unregistered on driver
 * detach. Internally this function calls extcon_dev_register() function.
 * To get more information, refer that function.
 *
 * If extcon device is registered with this function and the device needs to be
 * unregistered separately, devm_extcon_dev_unregister() should be used.
 *
 * Returns 0 if success or negaive error number if failure.
 */
int devm_extcon_dev_register(struct device *dev, struct extcon_dev *edev)
{
	struct extcon_dev **ptr;
	int ret;

	ptr = devres_alloc(devm_extcon_dev_unreg, sizeof(*ptr), GFP_KERNEL);
	if (!ptr)
		return -ENOMEM;

	ret = extcon_dev_register(edev);
	if (ret) {
		devres_free(ptr);
		return ret;
	}

	*ptr = edev;
	devres_add(dev, ptr);

	return 0;
}
EXPORT_SYMBOL_GPL(devm_extcon_dev_register);

/**
 * devm_extcon_dev_unregister() - Resource-managed extcon_dev_unregister()
 * @dev:	device the extcon belongs to
 * @edev:	the extcon device to unregister
 *
 * Unregister extcon device that is registered with devm_extcon_dev_register()
 * function.
 */
void devm_extcon_dev_unregister(struct device *dev, struct extcon_dev *edev)
{
	WARN_ON(devres_release(dev, devm_extcon_dev_unreg,
			       devm_extcon_dev_match, edev));
}
EXPORT_SYMBOL_GPL(devm_extcon_dev_unregister);

#ifdef CONFIG_OF
/*
 * extcon_get_edev_by_phandle - Get the extcon device from devicetree
+18 −19
Original line number Diff line number Diff line
@@ -32,7 +32,7 @@
#include <linux/extcon/extcon-gpio.h>

struct gpio_extcon_data {
	struct extcon_dev edev;
	struct extcon_dev *edev;
	unsigned gpio;
	bool gpio_active_low;
	const char *state_on;
@@ -53,7 +53,7 @@ static void gpio_extcon_work(struct work_struct *work)
	state = gpio_get_value(data->gpio);
	if (data->gpio_active_low)
		state = !state;
	extcon_set_state(&data->edev, state);
	extcon_set_state(data->edev, state);
}

static irqreturn_t gpio_irq_handler(int irq, void *dev_id)
@@ -67,9 +67,10 @@ static irqreturn_t gpio_irq_handler(int irq, void *dev_id)

static ssize_t extcon_gpio_print_state(struct extcon_dev *edev, char *buf)
{
	struct gpio_extcon_data	*extcon_data =
		container_of(edev, struct gpio_extcon_data, edev);
	struct device *dev = edev->dev.parent;
	struct gpio_extcon_data *extcon_data = dev_get_drvdata(dev);
	const char *state;

	if (extcon_get_state(edev))
		state = extcon_data->state_on;
	else
@@ -98,15 +99,21 @@ static int gpio_extcon_probe(struct platform_device *pdev)
	if (!extcon_data)
		return -ENOMEM;

	extcon_data->edev.name = pdata->name;
	extcon_data->edev.dev.parent = &pdev->dev;
	extcon_data->edev = devm_extcon_dev_allocate(&pdev->dev, NULL);
	if (IS_ERR(extcon_data->edev)) {
		dev_err(&pdev->dev, "failed to allocate extcon device\n");
		return -ENOMEM;
	}
	extcon_data->edev->name = pdata->name;
	extcon_data->edev->dev.parent = &pdev->dev;

	extcon_data->gpio = pdata->gpio;
	extcon_data->gpio_active_low = pdata->gpio_active_low;
	extcon_data->state_on = pdata->state_on;
	extcon_data->state_off = pdata->state_off;
	extcon_data->check_on_resume = pdata->check_on_resume;
	if (pdata->state_on && pdata->state_off)
		extcon_data->edev.print_state = extcon_gpio_print_state;
		extcon_data->edev->print_state = extcon_gpio_print_state;

	ret = devm_gpio_request_one(&pdev->dev, extcon_data->gpio, GPIOF_DIR_IN,
				    pdev->name);
@@ -121,34 +128,27 @@ static int gpio_extcon_probe(struct platform_device *pdev)
				msecs_to_jiffies(pdata->debounce);
	}

	ret = extcon_dev_register(&extcon_data->edev);
	ret = devm_extcon_dev_register(&pdev->dev, extcon_data->edev);
	if (ret < 0)
		return ret;

	INIT_DELAYED_WORK(&extcon_data->work, gpio_extcon_work);

	extcon_data->irq = gpio_to_irq(extcon_data->gpio);
	if (extcon_data->irq < 0) {
		ret = extcon_data->irq;
		goto err;
	}
	if (extcon_data->irq < 0)
		return extcon_data->irq;

	ret = request_any_context_irq(extcon_data->irq, gpio_irq_handler,
				      pdata->irq_flags, pdev->name,
				      extcon_data);
	if (ret < 0)
		goto err;
		return ret;

	platform_set_drvdata(pdev, extcon_data);
	/* Perform initial detection */
	gpio_extcon_work(&extcon_data->work.work);

	return 0;

err:
	extcon_dev_unregister(&extcon_data->edev);

	return ret;
}

static int gpio_extcon_remove(struct platform_device *pdev)
@@ -157,7 +157,6 @@ static int gpio_extcon_remove(struct platform_device *pdev)

	cancel_delayed_work_sync(&extcon_data->work);
	free_irq(extcon_data->irq, extcon_data);
	extcon_dev_unregister(&extcon_data->edev);

	return 0;
}
Loading