Commit 073a457d authored by Greg Kroah-Hartman's avatar Greg Kroah-Hartman

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

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

Update extcon for 4.12

Detailed description for this pull request:
1. Add new 'extcon-intel-cht-wc.c' driver
- Intel Cherrytrail Whiskey Cove PMIC extcon driver supports
the detection of the charger connectors and the control.

2. Add new extcon API to monitor the all external connectors.
- The extcon consumer might need to monitor the all supported external
connectors from the extcon device. Before, the extcon consumer
should have each notifier_block structure for each external connector.

In order to support the requirement, the extcon adds new
extcon_register_notifier_all() API. The extcon consumer is able
to monitor the state change of all supported external connectors
from the extcon device by using only one notifier_block.

- extcon_(register|unregister)_notifier_all(struct extcon_dev *edev
				struct notifier_block *nb)
- devm_extcon_(register|unregister)_notifier_all(struct device *dev,
				struct extcon_dev *edev
				struct notifier_block *nb)

3. Remove porting compatibility of old switch class
- The extcon removes the porting compatibility of old switch class
because there are no any use-case and requirement of switch class.

4. Update the extcon drivers and Fix the minor issues
- Revert the ACPI gpio interface on the extcon-usb-gpioc.c.
- Fix the issues related to the suspend-to-ram for both extcon-usb-gpio.c
  and extcon-palmas.c.
- Add warning message for extcon-arizona.c when headphone detection is not
  finished.
parents 615cdd7c 70641a0a
Staging/Android Switch Class Porting Guide
(linux/drivers/staging/android/switch)
(c) Copyright 2012 Samsung Electronics
AUTHORS
MyungJoo Ham <myungjoo.ham@samsung.com>
/*****************************************************************
* CHAPTER 1. *
* PORTING SWITCH CLASS DEVICE DRIVERS *
*****************************************************************/
****** STEP 1. Basic Functionality
No extcon extended feature, but switch features only.
- struct switch_dev (fed to switch_dev_register/unregister)
@name: no change
@dev: no change
@index: drop (not used in switch device driver side anyway)
@state: no change
If you have used @state with magic numbers, keep it
at this step.
@print_name: no change but type change (switch_dev->extcon_dev)
@print_state: no change but type change (switch_dev->extcon_dev)
- switch_dev_register(sdev, dev)
=> extcon_dev_register(edev)
: type change (sdev->edev)
: remove second param('dev'). if edev has parent device, should store
'dev' to 'edev.dev.parent' before registering extcon device
- switch_dev_unregister(sdev)
=> extcon_dev_unregister(edev)
: no change but type change (sdev->edev)
- switch_get_state(sdev)
=> extcon_get_state(edev)
: no change but type change (sdev->edev) and (return: int->u32)
- switch_set_state(sdev, state)
=> extcon_set_state(edev, state)
: no change but type change (sdev->edev) and (state: int->u32)
With this changes, the ex-switch extcon class device works as it once
worked as switch class device. However, it will now have additional
interfaces (both ABI and in-kernel API) and different ABI locations.
However, if CONFIG_ANDROID is enabled without CONFIG_ANDROID_SWITCH,
/sys/class/switch/* will be symbolically linked to /sys/class/extcon/
so that they are still compatible with legacy userspace processes.
****** STEP 2. Multistate (no more magic numbers in state value)
Extcon's extended features for switch device drivers with
complex features usually required magic numbers in state
value of switch_dev. With extcon, such magic numbers that
support multiple cables are no more required or supported.
1. Define cable names at edev->supported_cable.
2. (Recommended) remove print_state callback.
3. Use extcon_get_cable_state_(edev, index) or
extcon_get_cable_state(edev, cable_name) instead of
extcon_get_state(edev) if you intend to get a state of a specific
cable. Same for set_state. This way, you can remove the usage of
magic numbers in state value.
4. Use extcon_update_state() if you are updating specific bits of
the state value.
Example: a switch device driver w/ magic numbers for two cables.
"0x00": no cables connected.
"0x01": cable 1 connected
"0x02": cable 2 connected
"0x03": cable 1 and 2 connected
1. edev->supported_cable = {"1", "2", NULL};
2. edev->print_state = NULL;
3. extcon_get_cable_state_(edev, 0) shows cable 1's state.
extcon_get_cable_state(edev, "1") shows cable 1's state.
extcon_set_cable_state_(edev, 1) sets cable 2's state.
extcon_set_cable_state(edev, "2") sets cable 2's state
4. extcon_update_state(edev, 0x01, 0) sets the least bit's 0.
****** STEP 3. Notify other device drivers
You can notify others of the cable attach/detach events with
notifier chains.
At the side of other device drivers (the extcon device itself
does not need to get notified of its own events), there are two
methods to register notifier_block for cable events:
(a) for a specific cable or (b) for every cable.
(a) extcon_register_interest(obj, extcon_name, cable_name, nb)
Example: want to get news of "MAX8997_MUIC"'s "USB" cable
obj = kzalloc(sizeof(struct extcon_specific_cable_nb),
GFP_KERNEL);
nb->notifier_call = the_callback_to_handle_usb;
extcon_register_intereset(obj, "MAX8997_MUIC", "USB", nb);
(b) extcon_register_notifier(edev, nb)
Call nb for any changes in edev.
Please note that in order to properly behave with method (a),
the extcon device driver should support multistate feature (STEP 2).
****** STEP 4. Inter-cable relation (mutually exclusive)
You can provide inter-cable mutually exclusiveness information
for an extcon device. When cables A and B are declared to be mutually
exclusive, the two cables cannot be in ATTACHED state simulteneously.
/*****************************************************************
* CHAPTER 2. *
* PORTING USERSPACE w/ SWITCH CLASS DEVICE SUPPORT *
*****************************************************************/
****** ABI Location
If "CONFIG_ANDROID" is enabled, /sys/class/switch/* are created
as symbolic links to /sys/class/extcon/*.
The two files of switch class, name and state, are provided with
extcon, too. When the multistate support (STEP 2 of CHAPTER 1.) is
not enabled or print_state callback is supplied, the output of
state ABI is same with switch class.
......@@ -52,6 +52,13 @@ config EXTCON_INTEL_INT3496
This ACPI device is typically found on Intel Baytrail or Cherrytrail
based tablets, or other Baytrail / Cherrytrail devices.
config EXTCON_INTEL_CHT_WC
tristate "Intel Cherrytrail Whiskey Cove PMIC extcon driver"
depends on INTEL_SOC_PMIC_CHTWC
help
Say Y here to enable extcon support for charger detection / control
on the Intel Cherrytrail Whiskey Cove PMIC.
config EXTCON_MAX14577
tristate "Maxim MAX14577/77836 EXTCON Support"
depends on MFD_MAX14577
......
......@@ -9,6 +9,7 @@ obj-$(CONFIG_EXTCON_ARIZONA) += extcon-arizona.o
obj-$(CONFIG_EXTCON_AXP288) += extcon-axp288.o
obj-$(CONFIG_EXTCON_GPIO) += extcon-gpio.o
obj-$(CONFIG_EXTCON_INTEL_INT3496) += extcon-intel-int3496.o
obj-$(CONFIG_EXTCON_INTEL_CHT_WC) += extcon-intel-cht-wc.o
obj-$(CONFIG_EXTCON_MAX14577) += extcon-max14577.o
obj-$(CONFIG_EXTCON_MAX3355) += extcon-max3355.o
obj-$(CONFIG_EXTCON_MAX77693) += extcon-max77693.o
......
......@@ -50,6 +50,13 @@ static void devm_extcon_dev_notifier_unreg(struct device *dev, void *res)
extcon_unregister_notifier(this->edev, this->id, this->nb);
}
static void devm_extcon_dev_notifier_all_unreg(struct device *dev, void *res)
{
struct extcon_dev_notifier_devres *this = res;
extcon_unregister_notifier_all(this->edev, this->nb);
}
/**
* devm_extcon_dev_allocate - Allocate managed extcon device
* @dev: device owning the extcon device being created
......@@ -214,3 +221,57 @@ void devm_extcon_unregister_notifier(struct device *dev,
devm_extcon_dev_match, edev));
}
EXPORT_SYMBOL(devm_extcon_unregister_notifier);
/**
* devm_extcon_register_notifier_all()
* - Resource-managed extcon_register_notifier_all()
* @dev: device to allocate extcon device
* @edev: the extcon device that has the external connecotr.
* @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. To get more information, refer that function.
*
* Returns 0 if success or negaive error number if failure.
*/
int devm_extcon_register_notifier_all(struct device *dev, struct extcon_dev *edev,
struct notifier_block *nb)
{
struct extcon_dev_notifier_devres *ptr;
int ret;
ptr = devres_alloc(devm_extcon_dev_notifier_all_unreg, sizeof(*ptr),
GFP_KERNEL);
if (!ptr)
return -ENOMEM;
ret = extcon_register_notifier_all(edev, nb);
if (ret) {
devres_free(ptr);
return ret;
}
ptr->edev = edev;
ptr->nb = nb;
devres_add(dev, ptr);
return 0;
}
EXPORT_SYMBOL(devm_extcon_register_notifier_all);
/**
* devm_extcon_unregister_notifier_all()
* - Resource-managed extcon_unregister_notifier_all()
* @dev: device to allocate extcon device
* @edev: the extcon device that has the external connecotr.
* @nb: a notifier block to be registered.
*/
void devm_extcon_unregister_notifier_all(struct device *dev,
struct extcon_dev *edev,
struct notifier_block *nb)
{
WARN_ON(devres_release(dev, devm_extcon_dev_notifier_all_unreg,
devm_extcon_dev_match, edev));
}
EXPORT_SYMBOL(devm_extcon_unregister_notifier_all);
......@@ -51,6 +51,9 @@
#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
......@@ -1049,6 +1052,40 @@ static void arizona_hpdet_work(struct work_struct *work)
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;
......@@ -1155,6 +1192,15 @@ static irqreturn_t arizona_jackdet(int irq, void *data)
"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,
......
This diff is collapsed.
......@@ -413,6 +413,12 @@ static int palmas_usb_resume(struct device *dev)
if (palmas_usb->enable_gpio_id_detection)
disable_irq_wake(palmas_usb->gpio_id_irq);
}
/* check if GPIO states changed while suspend/resume */
if (palmas_usb->enable_gpio_vbus_detection)
palmas_vbus_irq_handler(palmas_usb->gpio_vbus_irq, palmas_usb);
palmas_gpio_id_detect(&palmas_usb->wq_detectid.work);
return 0;
};
#endif
......
......@@ -26,7 +26,6 @@
#include <linux/platform_device.h>
#include <linux/slab.h>
#include <linux/workqueue.h>
#include <linux/acpi.h>
#include <linux/pinctrl/consumer.h>
#define USB_GPIO_DEBOUNCE_MS 20 /* ms */
......@@ -111,7 +110,7 @@ static int usb_extcon_probe(struct platform_device *pdev)
struct usb_extcon_info *info;
int ret;
if (!np && !ACPI_HANDLE(dev))
if (!np)
return -EINVAL;
info = devm_kzalloc(&pdev->dev, sizeof(*info), GFP_KERNEL);
......@@ -195,7 +194,7 @@ static int usb_extcon_probe(struct platform_device *pdev)
}
platform_set_drvdata(pdev, info);
device_init_wakeup(dev, true);
device_set_wakeup_capable(&pdev->dev, true);
/* Perform initial detection */
usb_extcon_detect_cable(&info->wq_detcable.work);
......@@ -282,9 +281,8 @@ static int usb_extcon_resume(struct device *dev)
if (info->vbus_gpiod)
enable_irq(info->vbus_irq);
if (!device_may_wakeup(dev))
queue_delayed_work(system_power_efficient_wq,
&info->wq_detcable, 0);
queue_delayed_work(system_power_efficient_wq,
&info->wq_detcable, 0);
return ret;
}
......
......@@ -230,9 +230,6 @@ struct extcon_cable {
};
static struct class *extcon_class;
#if defined(CONFIG_ANDROID)
static struct class_compat *switch_class;
#endif /* CONFIG_ANDROID */
static LIST_HEAD(extcon_dev_list);
static DEFINE_MUTEX(extcon_dev_list_lock);
......@@ -380,7 +377,7 @@ static ssize_t state_show(struct device *dev, struct device_attribute *attr,
for (i = 0; i < edev->max_supported; i++) {
count += sprintf(buf + count, "%s=%d\n",
extcon_info[edev->supported_cable[i]].name,
!!(edev->state & (1 << i)));
!!(edev->state & BIT(i)));
}
return count;
......@@ -448,8 +445,19 @@ int extcon_sync(struct extcon_dev *edev, unsigned int id)
spin_lock_irqsave(&edev->lock, flags);
state = !!(edev->state & BIT(index));
/*
* Call functions in a raw notifier chain for the specific one
* external connector.
*/
raw_notifier_call_chain(&edev->nh[index], state, edev);
/*
* Call functions in a raw notifier chain for the all supported
* external connectors.
*/
raw_notifier_call_chain(&edev->nh_all, state, edev);
/* This could be in interrupt handler */
prop_buf = (char *)get_zeroed_page(GFP_ATOMIC);
if (!prop_buf) {
......@@ -954,6 +962,59 @@ int extcon_unregister_notifier(struct extcon_dev *edev, unsigned int id,
}
EXPORT_SYMBOL_GPL(extcon_unregister_notifier);
/**
* extcon_register_notifier_all() - Register a notifier block for all connectors
* @edev: the extcon device that has the external connecotr.
* @nb: a notifier block to be registered.
*
* This fucntion registers a notifier block in order to receive the state
* change of all supported external connectors from extcon device.
* And The second parameter given to the callback of nb (val) is
* the current state and third parameter is the edev pointer.
*
* Returns 0 if success or error number if fail
*/
int extcon_register_notifier_all(struct extcon_dev *edev,
struct notifier_block *nb)
{
unsigned long flags;
int ret;
if (!edev || !nb)
return -EINVAL;
spin_lock_irqsave(&edev->lock, flags);
ret = raw_notifier_chain_register(&edev->nh_all, nb);
spin_unlock_irqrestore(&edev->lock, flags);
return ret;
}
EXPORT_SYMBOL_GPL(extcon_register_notifier_all);
/**
* extcon_unregister_notifier_all() - Unregister a notifier block from extcon.
* @edev: the extcon device that has the external connecotr.
* @nb: a notifier block to be registered.
*
* Returns 0 if success or error number if fail
*/
int extcon_unregister_notifier_all(struct extcon_dev *edev,
struct notifier_block *nb)
{
unsigned long flags;
int ret;
if (!edev || !nb)
return -EINVAL;
spin_lock_irqsave(&edev->lock, flags);
ret = raw_notifier_chain_unregister(&edev->nh_all, nb);
spin_unlock_irqrestore(&edev->lock, flags);
return ret;
}
EXPORT_SYMBOL_GPL(extcon_unregister_notifier_all);
static struct attribute *extcon_attrs[] = {
&dev_attr_state.attr,
&dev_attr_name.attr,
......@@ -968,12 +1029,6 @@ static int create_extcon_class(void)
if (IS_ERR(extcon_class))
return PTR_ERR(extcon_class);
extcon_class->dev_groups = extcon_groups;
#if defined(CONFIG_ANDROID)
switch_class = class_compat_register("switch");
if (WARN(!switch_class, "cannot allocate"))
return -ENOMEM;
#endif /* CONFIG_ANDROID */
}
return 0;
......@@ -1195,10 +1250,6 @@ int extcon_dev_register(struct extcon_dev *edev)
put_device(&edev->dev);
goto err_dev;
}
#if defined(CONFIG_ANDROID)
if (switch_class)
ret = class_compat_create_link(switch_class, &edev->dev, NULL);
#endif /* CONFIG_ANDROID */
spin_lock_init(&edev->lock);
......@@ -1212,6 +1263,8 @@ int extcon_dev_register(struct extcon_dev *edev)
for (index = 0; index < edev->max_supported; index++)
RAW_INIT_NOTIFIER_HEAD(&edev->nh[index]);
RAW_INIT_NOTIFIER_HEAD(&edev->nh_all);
dev_set_drvdata(&edev->dev, edev);
edev->state = 0;
......@@ -1284,10 +1337,6 @@ void extcon_dev_unregister(struct extcon_dev *edev)
kfree(edev->cables);
}
#if defined(CONFIG_ANDROID)
if (switch_class)
class_compat_remove_link(switch_class, &edev->dev, NULL);
#endif
put_device(&edev->dev);
}
EXPORT_SYMBOL_GPL(extcon_dev_unregister);
......@@ -1358,9 +1407,6 @@ module_init(extcon_class_init);
static void __exit extcon_class_exit(void)
{
#if defined(CONFIG_ANDROID)
class_compat_unregister(switch_class);
#endif
class_destroy(extcon_class);
}
module_exit(extcon_class_exit);
......
......@@ -21,6 +21,8 @@
* @dev: Device of this extcon.
* @state: Attach/detach state of this extcon. Do not provide at
* register-time.
* @nh_all: Notifier for the state change events for all supported
* external connectors from this extcon.
* @nh: Notifier for the state change events from this extcon
* @entry: To support list of extcon devices so that users can
* search for extcon devices based on the extcon name.
......@@ -43,6 +45,7 @@ struct extcon_dev {
/* Internal data. Please do not set. */
struct device dev;
struct raw_notifier_head nh_all;
struct raw_notifier_head *nh;
struct list_head entry;
int max_supported;
......
......@@ -236,11 +236,11 @@ extern int extcon_set_property_capability(struct extcon_dev *edev,
unsigned int id, unsigned int prop);
/*
* Following APIs are to monitor every action of a notifier.
* Registrar gets notified for every external port of a connection device.
* Probably this could be used to debug an action of notifier; however,
* we do not recommend to use this for normal 'notifiee' device drivers who
* want to be notified by a specific external port of the notifier.
* Following APIs are to monitor the status change of the external connectors.
* extcon_register_notifier(*edev, id, *nb) : Register a notifier block
* for specific external connector of the extcon.
* extcon_register_notifier_all(*edev, *nb) : Register a notifier block
* for all supported external connectors of the extcon.
*/
extern int extcon_register_notifier(struct extcon_dev *edev, unsigned int id,
struct notifier_block *nb);
......@@ -253,6 +253,17 @@ extern void devm_extcon_unregister_notifier(struct device *dev,
struct extcon_dev *edev, unsigned int id,
struct notifier_block *nb);
extern int extcon_register_notifier_all(struct extcon_dev *edev,
struct notifier_block *nb);
extern int extcon_unregister_notifier_all(struct extcon_dev *edev,
struct notifier_block *nb);
extern int devm_extcon_register_notifier_all(struct device *dev,
struct extcon_dev *edev,
struct notifier_block *nb);
extern void devm_extcon_unregister_notifier_all(struct device *dev,
struct extcon_dev *edev,
struct notifier_block *nb);
/*
* Following API get the extcon device from devicetree.
* This function use phandle of devicetree to get extcon device directly.
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment