diff options
Diffstat (limited to 'drivers/extcon')
-rw-r--r-- | drivers/extcon/Kconfig | 4 | ||||
-rw-r--r-- | drivers/extcon/Makefile | 2 | ||||
-rw-r--r-- | drivers/extcon/extcon-adc-jack.c | 3 | ||||
-rw-r--r-- | drivers/extcon/extcon-arizona.c | 25 | ||||
-rw-r--r-- | drivers/extcon/extcon-class.c | 3 | ||||
-rw-r--r-- | drivers/extcon/extcon-gpio.c | 2 | ||||
-rw-r--r-- | drivers/extcon/extcon-palmas.c | 134 | ||||
-rw-r--r-- | drivers/extcon/of_extcon.c | 64 |
8 files changed, 180 insertions, 57 deletions
diff --git a/drivers/extcon/Kconfig b/drivers/extcon/Kconfig index 63f454e20576..f1d54a3985bd 100644 --- a/drivers/extcon/Kconfig +++ b/drivers/extcon/Kconfig @@ -14,6 +14,10 @@ if EXTCON comment "Extcon Device Drivers" +config OF_EXTCON + def_tristate y + depends on OF + config EXTCON_GPIO tristate "GPIO extcon support" depends on GPIOLIB diff --git a/drivers/extcon/Makefile b/drivers/extcon/Makefile index 540e2c3a4431..759fdae46f95 100644 --- a/drivers/extcon/Makefile +++ b/drivers/extcon/Makefile @@ -2,6 +2,8 @@ # Makefile for external connector class (extcon) devices # +obj-$(CONFIG_OF_EXTCON) += of_extcon.o + obj-$(CONFIG_EXTCON) += extcon-class.o obj-$(CONFIG_EXTCON_GPIO) += extcon-gpio.o obj-$(CONFIG_EXTCON_ADC_JACK) += extcon-adc-jack.o diff --git a/drivers/extcon/extcon-adc-jack.c b/drivers/extcon/extcon-adc-jack.c index d0233cd18ffa..5985807e52c9 100644 --- a/drivers/extcon/extcon-adc-jack.c +++ b/drivers/extcon/extcon-adc-jack.c @@ -87,7 +87,8 @@ static irqreturn_t adc_jack_irq_thread(int irq, void *_data) { struct adc_jack_data *data = _data; - schedule_delayed_work(&data->handler, data->handling_delay); + queue_delayed_work(system_power_efficient_wq, + &data->handler, data->handling_delay); return IRQ_HANDLED; } diff --git a/drivers/extcon/extcon-arizona.c b/drivers/extcon/extcon-arizona.c index 7a1b4a7791ba..e55713083c78 100644 --- a/drivers/extcon/extcon-arizona.c +++ b/drivers/extcon/extcon-arizona.c @@ -890,8 +890,9 @@ static void arizona_micd_detect(struct work_struct *work) handled: if (info->detecting) - schedule_delayed_work(&info->micd_timeout_work, - msecs_to_jiffies(info->micd_timeout)); + queue_delayed_work(system_power_efficient_wq, + &info->micd_timeout_work, + msecs_to_jiffies(info->micd_timeout)); pm_runtime_mark_last_busy(info->dev); mutex_unlock(&info->lock); @@ -912,8 +913,9 @@ static irqreturn_t arizona_micdet(int irq, void *data) mutex_unlock(&info->lock); if (debounce) - schedule_delayed_work(&info->micd_detect_work, - msecs_to_jiffies(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); @@ -967,12 +969,14 @@ static irqreturn_t arizona_jackdet(int irq, void *data) if (val == info->last_jackdet) { dev_dbg(arizona->dev, "Suppressing duplicate JACKDET\n"); if (cancelled_hp) - schedule_delayed_work(&info->hpdet_work, - msecs_to_jiffies(HPDET_DEBOUNCE)); + queue_delayed_work(system_power_efficient_wq, + &info->hpdet_work, + msecs_to_jiffies(HPDET_DEBOUNCE)); if (cancelled_mic) - schedule_delayed_work(&info->micd_timeout_work, - msecs_to_jiffies(info->micd_timeout)); + queue_delayed_work(system_power_efficient_wq, + &info->micd_timeout_work, + msecs_to_jiffies(info->micd_timeout)); goto out; } @@ -994,8 +998,9 @@ static irqreturn_t arizona_jackdet(int irq, void *data) arizona_start_mic(info); } else { - schedule_delayed_work(&info->hpdet_work, - msecs_to_jiffies(HPDET_DEBOUNCE)); + queue_delayed_work(system_power_efficient_wq, + &info->hpdet_work, + msecs_to_jiffies(HPDET_DEBOUNCE)); } regmap_update_bits(arizona->regmap, diff --git a/drivers/extcon/extcon-class.c b/drivers/extcon/extcon-class.c index 18ccadef43fd..1446152bf2e9 100644 --- a/drivers/extcon/extcon-class.c +++ b/drivers/extcon/extcon-class.c @@ -602,7 +602,8 @@ int extcon_dev_register(struct extcon_dev *edev, struct device *dev) edev->dev->class = extcon_class; edev->dev->release = extcon_dev_release; - dev_set_name(edev->dev, "%s", edev->name ? edev->name : dev_name(dev)); + edev->name = edev->name ? edev->name : dev_name(dev); + dev_set_name(edev->dev, "%s", edev->name); if (edev->max_supported) { char buf[10]; diff --git a/drivers/extcon/extcon-gpio.c b/drivers/extcon/extcon-gpio.c index 02bec32adde4..f874c30ddbff 100644 --- a/drivers/extcon/extcon-gpio.c +++ b/drivers/extcon/extcon-gpio.c @@ -56,7 +56,7 @@ static irqreturn_t gpio_irq_handler(int irq, void *dev_id) { struct gpio_extcon_data *extcon_data = dev_id; - schedule_delayed_work(&extcon_data->work, + queue_delayed_work(system_power_efficient_wq, &extcon_data->work, extcon_data->debounce_jiffies); return IRQ_HANDLED; } diff --git a/drivers/extcon/extcon-palmas.c b/drivers/extcon/extcon-palmas.c index b752a0ad7b63..89fdd05c5fd6 100644 --- a/drivers/extcon/extcon-palmas.c +++ b/drivers/extcon/extcon-palmas.c @@ -57,6 +57,7 @@ static irqreturn_t palmas_vbus_irq_handler(int irq, void *_palmas_usb) if (palmas_usb->linkstat != PALMAS_USB_STATE_VBUS) { palmas_usb->linkstat = PALMAS_USB_STATE_VBUS; extcon_set_cable_state(&palmas_usb->edev, "USB", true); + dev_info(palmas_usb->dev, "USB cable is attached\n"); } else { dev_dbg(palmas_usb->dev, "Spurious connect event detected\n"); @@ -65,6 +66,7 @@ static irqreturn_t palmas_vbus_irq_handler(int irq, void *_palmas_usb) if (palmas_usb->linkstat == PALMAS_USB_STATE_VBUS) { palmas_usb->linkstat = PALMAS_USB_STATE_DISCONNECT; extcon_set_cable_state(&palmas_usb->edev, "USB", false); + dev_info(palmas_usb->dev, "USB cable is detached\n"); } else { dev_dbg(palmas_usb->dev, "Spurious disconnect event detected\n"); @@ -84,28 +86,23 @@ static irqreturn_t palmas_id_irq_handler(int irq, void *_palmas_usb) if (set & PALMAS_USB_ID_INT_SRC_ID_GND) { palmas_write(palmas_usb->palmas, PALMAS_USB_OTG_BASE, - PALMAS_USB_ID_INT_EN_HI_SET, - PALMAS_USB_ID_INT_EN_HI_SET_ID_FLOAT); - palmas_write(palmas_usb->palmas, PALMAS_USB_OTG_BASE, - PALMAS_USB_ID_INT_EN_HI_CLR, - PALMAS_USB_ID_INT_EN_HI_CLR_ID_GND); - palmas_write(palmas_usb->palmas, PALMAS_USB_OTG_BASE, PALMAS_USB_ID_INT_LATCH_CLR, PALMAS_USB_ID_INT_EN_HI_CLR_ID_GND); palmas_usb->linkstat = PALMAS_USB_STATE_ID; extcon_set_cable_state(&palmas_usb->edev, "USB-HOST", true); + dev_info(palmas_usb->dev, "USB-HOST cable is attached\n"); } else if (set & PALMAS_USB_ID_INT_SRC_ID_FLOAT) { palmas_write(palmas_usb->palmas, PALMAS_USB_OTG_BASE, - PALMAS_USB_ID_INT_EN_HI_SET, - PALMAS_USB_ID_INT_EN_HI_SET_ID_GND); - palmas_write(palmas_usb->palmas, PALMAS_USB_OTG_BASE, - PALMAS_USB_ID_INT_EN_HI_CLR, - PALMAS_USB_ID_INT_EN_HI_CLR_ID_FLOAT); - palmas_write(palmas_usb->palmas, PALMAS_USB_OTG_BASE, PALMAS_USB_ID_INT_LATCH_CLR, PALMAS_USB_ID_INT_EN_HI_CLR_ID_FLOAT); palmas_usb->linkstat = PALMAS_USB_STATE_DISCONNECT; extcon_set_cable_state(&palmas_usb->edev, "USB-HOST", false); + dev_info(palmas_usb->dev, "USB-HOST cable is detached\n"); + } else if ((palmas_usb->linkstat == PALMAS_USB_STATE_ID) && + (!(set & PALMAS_USB_ID_INT_SRC_ID_GND))) { + palmas_usb->linkstat = PALMAS_USB_STATE_DISCONNECT; + extcon_set_cable_state(&palmas_usb->edev, "USB-HOST", false); + dev_info(palmas_usb->dev, "USB-HOST cable is detached\n"); } return IRQ_HANDLED; @@ -122,13 +119,17 @@ static void palmas_enable_irq(struct palmas_usb *palmas_usb) palmas_write(palmas_usb->palmas, PALMAS_USB_OTG_BASE, PALMAS_USB_ID_INT_EN_HI_SET, - PALMAS_USB_ID_INT_EN_HI_SET_ID_GND); + PALMAS_USB_ID_INT_EN_HI_SET_ID_GND | + PALMAS_USB_ID_INT_EN_HI_SET_ID_FLOAT); - palmas_vbus_irq_handler(palmas_usb->vbus_irq, palmas_usb); + if (palmas_usb->enable_vbus_detection) + palmas_vbus_irq_handler(palmas_usb->vbus_irq, palmas_usb); /* cold plug for host mode needs this delay */ - msleep(30); - palmas_id_irq_handler(palmas_usb->id_irq, palmas_usb); + if (palmas_usb->enable_id_detection) { + msleep(30); + palmas_id_irq_handler(palmas_usb->id_irq, palmas_usb); + } } static int palmas_usb_probe(struct platform_device *pdev) @@ -139,21 +140,25 @@ static int palmas_usb_probe(struct platform_device *pdev) struct palmas_usb *palmas_usb; int status; - if (node && !pdata) { - pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL); - - if (!pdata) - return -ENOMEM; - - pdata->wakeup = of_property_read_bool(node, "ti,wakeup"); - } else if (!pdata) { - return -EINVAL; - } - palmas_usb = devm_kzalloc(&pdev->dev, sizeof(*palmas_usb), GFP_KERNEL); if (!palmas_usb) return -ENOMEM; + if (node && !pdata) { + palmas_usb->wakeup = of_property_read_bool(node, "ti,wakeup"); + palmas_usb->enable_id_detection = of_property_read_bool(node, + "ti,enable-id-detection"); + palmas_usb->enable_vbus_detection = of_property_read_bool(node, + "ti,enable-vbus-detection"); + } else { + palmas_usb->wakeup = true; + palmas_usb->enable_id_detection = true; + palmas_usb->enable_vbus_detection = true; + + if (pdata) + palmas_usb->wakeup = pdata->wakeup; + } + palmas->usb = palmas_usb; palmas_usb->palmas = palmas; @@ -168,11 +173,10 @@ static int palmas_usb_probe(struct platform_device *pdev) palmas_usb->vbus_irq = regmap_irq_get_virq(palmas->irq_data, PALMAS_VBUS_IRQ); - palmas_usb_wakeup(palmas, pdata->wakeup); + palmas_usb_wakeup(palmas, palmas_usb->wakeup); platform_set_drvdata(pdev, palmas_usb); - palmas_usb->edev.name = "palmas-usb"; palmas_usb->edev.supported_cable = palmas_extcon_cable; palmas_usb->edev.mutually_exclusive = mutually_exclusive; @@ -182,28 +186,36 @@ static int palmas_usb_probe(struct platform_device *pdev) return status; } - status = devm_request_threaded_irq(palmas_usb->dev, palmas_usb->id_irq, - NULL, palmas_id_irq_handler, - IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING, - "palmas_usb_id", palmas_usb); - if (status < 0) { - dev_err(&pdev->dev, "can't get IRQ %d, err %d\n", + if (palmas_usb->enable_id_detection) { + status = devm_request_threaded_irq(palmas_usb->dev, + palmas_usb->id_irq, + NULL, palmas_id_irq_handler, + IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING | + IRQF_ONESHOT | IRQF_EARLY_RESUME, + "palmas_usb_id", palmas_usb); + if (status < 0) { + dev_err(&pdev->dev, "can't get IRQ %d, err %d\n", palmas_usb->id_irq, status); - goto fail_extcon; + goto fail_extcon; + } } - status = devm_request_threaded_irq(palmas_usb->dev, - palmas_usb->vbus_irq, NULL, palmas_vbus_irq_handler, - IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING, - "palmas_usb_vbus", palmas_usb); - if (status < 0) { - dev_err(&pdev->dev, "can't get IRQ %d, err %d\n", + if (palmas_usb->enable_vbus_detection) { + status = devm_request_threaded_irq(palmas_usb->dev, + palmas_usb->vbus_irq, NULL, + palmas_vbus_irq_handler, + IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING | + IRQF_ONESHOT | IRQF_EARLY_RESUME, + "palmas_usb_vbus", palmas_usb); + if (status < 0) { + dev_err(&pdev->dev, "can't get IRQ %d, err %d\n", palmas_usb->vbus_irq, status); - goto fail_extcon; + goto fail_extcon; + } } palmas_enable_irq(palmas_usb); - + device_set_wakeup_capable(&pdev->dev, true); return 0; fail_extcon: @@ -221,6 +233,39 @@ static int palmas_usb_remove(struct platform_device *pdev) return 0; } +#ifdef CONFIG_PM_SLEEP +static int palmas_usb_suspend(struct device *dev) +{ + struct palmas_usb *palmas_usb = dev_get_drvdata(dev); + + if (device_may_wakeup(dev)) { + if (palmas_usb->enable_vbus_detection) + enable_irq_wake(palmas_usb->vbus_irq); + if (palmas_usb->enable_id_detection) + enable_irq_wake(palmas_usb->id_irq); + } + return 0; +} + +static int palmas_usb_resume(struct device *dev) +{ + struct palmas_usb *palmas_usb = dev_get_drvdata(dev); + + if (device_may_wakeup(dev)) { + if (palmas_usb->enable_vbus_detection) + disable_irq_wake(palmas_usb->vbus_irq); + if (palmas_usb->enable_id_detection) + disable_irq_wake(palmas_usb->id_irq); + } + return 0; +}; +#endif + +static const struct dev_pm_ops palmas_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(palmas_usb_suspend, + palmas_usb_resume) +}; + static struct of_device_id of_palmas_match_tbl[] = { { .compatible = "ti,palmas-usb", }, { .compatible = "ti,twl6035-usb", }, @@ -234,6 +279,7 @@ static struct platform_driver palmas_usb_driver = { .name = "palmas-usb", .of_match_table = of_palmas_match_tbl, .owner = THIS_MODULE, + .pm = &palmas_pm_ops, }, }; diff --git a/drivers/extcon/of_extcon.c b/drivers/extcon/of_extcon.c new file mode 100644 index 000000000000..72173ecbb311 --- /dev/null +++ b/drivers/extcon/of_extcon.c @@ -0,0 +1,64 @@ +/* + * OF helpers for External connector (extcon) framework + * + * Copyright (C) 2013 Texas Instruments, Inc. + * Kishon Vijay Abraham I <kishon@ti.com> + * + * Copyright (C) 2013 Samsung Electronics + * Chanwoo Choi <cw00.choi@samsung.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#include <linux/module.h> +#include <linux/slab.h> +#include <linux/err.h> +#include <linux/extcon.h> +#include <linux/of.h> +#include <linux/of_platform.h> +#include <linux/extcon/of_extcon.h> + +/* + * of_extcon_get_extcon_dev - Get the name of extcon device from devicetree + * @dev - instance to the given device + * @index - index into list of extcon_dev + * + * return the instance of extcon device + */ +struct extcon_dev *of_extcon_get_extcon_dev(struct device *dev, int index) +{ + struct device_node *node; + struct extcon_dev *edev; + struct platform_device *extcon_parent_dev; + + if (!dev->of_node) { + dev_dbg(dev, "device does not have a device node entry\n"); + return ERR_PTR(-EINVAL); + } + + node = of_parse_phandle(dev->of_node, "extcon", index); + if (!node) { + dev_dbg(dev, "failed to get phandle in %s node\n", + dev->of_node->full_name); + return ERR_PTR(-ENODEV); + } + + extcon_parent_dev = of_find_device_by_node(node); + if (!extcon_parent_dev) { + dev_dbg(dev, "unable to find device by node\n"); + return ERR_PTR(-EPROBE_DEFER); + } + + edev = extcon_get_extcon_dev(dev_name(&extcon_parent_dev->dev)); + if (!edev) { + dev_dbg(dev, "unable to get extcon device : %s\n", + dev_name(&extcon_parent_dev->dev)); + return ERR_PTR(-ENODEV); + } + + return edev; +} +EXPORT_SYMBOL_GPL(of_extcon_get_extcon_dev); |