Commit 7f96a82f authored by Greg Kroah-Hartman's avatar Greg Kroah-Hartman
Browse files

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

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

Chanwoo writes:

Update
extcot://git.kernel.org/pub/scm/linux/kernel/git/chanwoo/extcon.git
tags/extcon-next-for-4.8
n for 4.8

Detailed description for patchset:
1. Update the extcon-gpio.c driver
- Use PM wakeirq APIs and support to check the state of external connector
  when wake-up from suspend state if the interrupt of external connector is
  not used as wakeup source.
- Support for ACPI gpio interface

2. Remove deprecated extcon APIs using the legacy cable name
- The extcon framework handle the external connector only by unique id
  instead of legacy cable name to prevent the problem.
- Removed functions
  : extcon_get_cable_state()
  : extcon_set_cable_state()
  : extcon_register_interest()
  : extcon_unregister_interest()
- It has the dependency on the axp288_charger.c driver.
  So, this pull request includes the 'ib-extcon-powersupply-4.8'
  immutable branch to protect the merge conflict.

3. Support the resource-managed function for extcon_register_notifier
- Add the devm_extcon_register/unregister_notifier() funticon to handle
  the resource automatically by resource managed functions and split out
  the resource-managed function from extcon core to seprate file(devres.c).

4. Supprot the suspend/resume for extcon-adc-jack.c driver
- Add the support the suspend/resume function to use extcon-adc-jack.c
  as wakeup source.

5. Fix the minor issue
- Check the return value of find_cable_index_by_id()
- Move the struct extcon_cable to extcon core from header file
  because it should be only handled on extcon core.
- Add the missing of_node_put() after calling of_parse_phandle()
  to decrement the reference count.
parents 2a7fbcec 1b6cf310
Loading
Loading
Loading
Loading
+2 −1
Original line number Diff line number Diff line
@@ -46,7 +46,8 @@ Optional properties:
    The second cell represents the MICBIAS to be used.
    The third cell represents the value of the micd-pol-gpio pin.

  - wlf,gpsw : Settings for the general purpose switch
  - wlf,gpsw : Settings for the general purpose switch, set as one of the
    ARIZONA_GPSW_XXX defines.

Example:

+2 −1
Original line number Diff line number Diff line
@@ -2,7 +2,8 @@
# Makefile for external connector class (extcon) devices
#

obj-$(CONFIG_EXTCON)		+= extcon.o
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
+216 −0
Original line number Diff line number Diff line
/*
 *  drivers/extcon/devres.c - EXTCON device's resource management
 *
 * Copyright (C) 2016 Samsung Electronics
 * Author: Chanwoo Choi <cw00.choi@samsung.com>
 *
 * This software is licensed under the terms of the GNU General Public
 * License version 2, as published by the Free Software Foundation, and
 * may be copied, distributed, and modified under those terms.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 */

#include <linux/extcon.h>

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);
}


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

struct extcon_dev_notifier_devres {
	struct extcon_dev *edev;
	unsigned int id;
	struct notifier_block *nb;
};

static void devm_extcon_dev_notifier_unreg(struct device *dev, void *res)
{
	struct extcon_dev_notifier_devres *this = res;

	extcon_unregister_notifier(this->edev, this->id, this->nb);
}

/**
 * devm_extcon_dev_allocate - Allocate managed extcon device
 * @dev:		device owning the extcon device being created
 * @supported_cable:	Array of supported extcon ending with EXTCON_NONE.
 *			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 unsigned int *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;
	}

	edev->dev.parent = dev;

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

	return edev;
}
EXPORT_SYMBOL_GPL(devm_extcon_dev_allocate);

/**
 * devm_extcon_dev_free() - Resource-managed extcon_dev_unregister()
 * @dev:	device the extcon belongs to
 * @edev:	the extcon device to unregister
 *
 * Free the memory that is allocated with devm_extcon_dev_allocate()
 * function.
 */
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);

/**
 * 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);

/**
 * devm_extcon_register_notifier() - Resource-managed extcon_register_notifier()
 * @dev:	device to allocate extcon device
 * @edev:	the extcon device that has the external connecotr.
 * @id:		the unique id of each external connector in extcon enumeration.
 * @nb:		a notifier block to be registered.
 *
 * This function manages automatically the notifier of extcon device using
 * device resource management and simplify the control of unregistering
 * the notifier of extcon device.
 *
 * Note that the second parameter given to the callback of nb (val) is
 * "old_state", not the current state. The current state can be retrieved
 * by looking at the third pameter (edev pointer)'s state value.
 *
 * Returns 0 if success or negaive error number if failure.
 */
int devm_extcon_register_notifier(struct device *dev, struct extcon_dev *edev,
				unsigned int id, struct notifier_block *nb)
{
	struct extcon_dev_notifier_devres *ptr;
	int ret;

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

	ret = extcon_register_notifier(edev, id, nb);
	if (ret) {
		devres_free(ptr);
		return ret;
	}

	ptr->edev = edev;
	ptr->id = id;
	ptr->nb = nb;
	devres_add(dev, ptr);

	return 0;
}
EXPORT_SYMBOL(devm_extcon_register_notifier);

/**
 * devm_extcon_unregister_notifier()
			- Resource-managed extcon_unregister_notifier()
 * @dev:	device to allocate extcon device
 * @edev:	the extcon device that has the external connecotr.
 * @id:		the unique id of each external connector in extcon enumeration.
 * @nb:		a notifier block to be registered.
 */
void devm_extcon_unregister_notifier(struct device *dev,
				struct extcon_dev *edev, unsigned int id,
				struct notifier_block *nb)
{
	WARN_ON(devres_release(dev, devm_extcon_dev_notifier_unreg,
			       devm_extcon_dev_match, edev));
}
EXPORT_SYMBOL(devm_extcon_unregister_notifier);
+34 −0
Original line number Diff line number Diff line
@@ -38,6 +38,7 @@
 * @chan:		iio channel being queried.
 */
struct adc_jack_data {
	struct device *dev;
	struct extcon_dev *edev;

	const unsigned int **cable_names;
@@ -49,6 +50,7 @@ struct adc_jack_data {
	struct delayed_work handler;

	struct iio_channel *chan;
	bool wakeup_source;
};

static void adc_jack_handler(struct work_struct *work)
@@ -105,6 +107,7 @@ static int adc_jack_probe(struct platform_device *pdev)
		return -EINVAL;
	}

	data->dev = &pdev->dev;
	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");
@@ -128,6 +131,7 @@ static int adc_jack_probe(struct platform_device *pdev)
		return PTR_ERR(data->chan);

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

	INIT_DEFERRABLE_WORK(&data->handler, adc_jack_handler);

@@ -151,6 +155,9 @@ static int adc_jack_probe(struct platform_device *pdev)
		return err;
	}

	if (data->wakeup_source)
		device_init_wakeup(&pdev->dev, 1);

	return 0;
}

@@ -165,11 +172,38 @@ static int adc_jack_remove(struct platform_device *pdev)
	return 0;
}

#ifdef CONFIG_PM_SLEEP
static int adc_jack_suspend(struct device *dev)
{
	struct adc_jack_data *data = dev_get_drvdata(dev);

	cancel_delayed_work_sync(&data->handler);
	if (device_may_wakeup(data->dev))
		enable_irq_wake(data->irq);

	return 0;
}

static int adc_jack_resume(struct device *dev)
{
	struct adc_jack_data *data = dev_get_drvdata(dev);

	if (device_may_wakeup(data->dev))
		disable_irq_wake(data->irq);

	return 0;
}
#endif /* CONFIG_PM_SLEEP */

static SIMPLE_DEV_PM_OPS(adc_jack_pm_ops,
		adc_jack_suspend, adc_jack_resume);

static struct platform_driver adc_jack_driver = {
	.probe          = adc_jack_probe,
	.remove         = adc_jack_remove,
	.driver         = {
		.name   = "adc-jack",
		.pm = &adc_jack_pm_ops,
	},
};

+18 −14
Original line number Diff line number Diff line
@@ -24,8 +24,10 @@
#include <linux/module.h>
#include <linux/of_gpio.h>
#include <linux/platform_device.h>
#include <linux/pm_wakeirq.h>
#include <linux/slab.h>
#include <linux/workqueue.h>
#include <linux/acpi.h>

#define USB_GPIO_DEBOUNCE_MS	20	/* ms */

@@ -91,7 +93,7 @@ static int usb_extcon_probe(struct platform_device *pdev)
	struct usb_extcon_info *info;
	int ret;

	if (!np)
	if (!np && !ACPI_HANDLE(dev))
		return -EINVAL;

	info = devm_kzalloc(&pdev->dev, sizeof(*info), GFP_KERNEL);
@@ -141,7 +143,8 @@ static int usb_extcon_probe(struct platform_device *pdev)
	}

	platform_set_drvdata(pdev, info);
	device_init_wakeup(dev, 1);
	device_init_wakeup(dev, true);
	dev_pm_set_wake_irq(dev, info->id_irq);

	/* Perform initial detection */
	usb_extcon_detect_cable(&info->wq_detcable.work);
@@ -155,6 +158,9 @@ static int usb_extcon_remove(struct platform_device *pdev)

	cancel_delayed_work_sync(&info->wq_detcable);

	dev_pm_clear_wake_irq(&pdev->dev);
	device_init_wakeup(&pdev->dev, false);

	return 0;
}

@@ -164,12 +170,6 @@ static int usb_extcon_suspend(struct device *dev)
	struct usb_extcon_info *info = dev_get_drvdata(dev);
	int ret = 0;

	if (device_may_wakeup(dev)) {
		ret = enable_irq_wake(info->id_irq);
		if (ret)
			return ret;
	}

	/*
	 * We don't want to process any IRQs after this point
	 * as GPIOs used behind I2C subsystem might not be
@@ -185,13 +185,10 @@ static int usb_extcon_resume(struct device *dev)
	struct usb_extcon_info *info = dev_get_drvdata(dev);
	int ret = 0;

	if (device_may_wakeup(dev)) {
		ret = disable_irq_wake(info->id_irq);
		if (ret)
			return ret;
	}

	enable_irq(info->id_irq);
	if (!device_may_wakeup(dev))
		queue_delayed_work(system_power_efficient_wq,
				   &info->wq_detcable, 0);

	return ret;
}
@@ -206,6 +203,12 @@ static const struct of_device_id usb_extcon_dt_match[] = {
};
MODULE_DEVICE_TABLE(of, usb_extcon_dt_match);

static const struct platform_device_id usb_extcon_platform_ids[] = {
	{ .name = "extcon-usb-gpio", },
	{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(platform, usb_extcon_platform_ids);

static struct platform_driver usb_extcon_driver = {
	.probe		= usb_extcon_probe,
	.remove		= usb_extcon_remove,
@@ -214,6 +217,7 @@ static struct platform_driver usb_extcon_driver = {
		.pm	= &usb_extcon_pm_ops,
		.of_match_table = usb_extcon_dt_match,
	},
	.id_table = usb_extcon_platform_ids,
};

module_platform_driver(usb_extcon_driver);
Loading