From a918093cee4c24603d799b9a4ecf6d51c9e91e9f Mon Sep 17 00:00:00 2001 From: Laxman Dewangan Date: Sun, 24 Feb 2013 21:02:38 +0530 Subject: extcon: palma: add vbus detection through extcon notification Add VBUS detection on palmas and notify the state through extcon framework. Bug 1238096 Change-Id: Ic5bb577aa66330e93b909df7dfc297aaf55fa0e6 Signed-off-by: Mallikarjun Kasoju Reviewed-on: http://git-master/r/203655 (cherry picked from commit b5e45af19fd91e8c54c17c1f21deee0afb8d6fd4) Reviewed-on: http://git-master/r/204692 (cherry picked from commit 222d6e830ab6bb3253d18066ba5dc6f820d7a040) Reviewed-on: http://git-master/r/206549 Reviewed-by: Automatic_Commit_Validation_User Tested-by: Seema Khowala Reviewed-by: Bitan Biswas --- drivers/extcon/Kconfig | 7 ++ drivers/extcon/Makefile | 1 + drivers/extcon/extcon-palmas.c | 204 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 212 insertions(+) create mode 100644 drivers/extcon/extcon-palmas.c (limited to 'drivers/extcon') diff --git a/drivers/extcon/Kconfig b/drivers/extcon/Kconfig index da6ff5767207..1482d27e55a9 100644 --- a/drivers/extcon/Kconfig +++ b/drivers/extcon/Kconfig @@ -52,6 +52,13 @@ config EXTCON_MAX8997 Maxim MAX8997 PMIC. The MAX8997 MUIC is a USB port accessory detector and switch. +config EXTCON_PALMAS + tristate "PALMAS EXTCON Support" + depends on MFD_PALMAS + help + If you say yes here you get support for the VBUS detection and notification + through extcon framework from Palmas PMIC. + config EXTCON_ARIZONA tristate "Wolfson Arizona EXTCON support" depends on MFD_ARIZONA && INPUT && SND_SOC diff --git a/drivers/extcon/Makefile b/drivers/extcon/Makefile index 064584bdf986..fd9b31e93548 100644 --- a/drivers/extcon/Makefile +++ b/drivers/extcon/Makefile @@ -8,4 +8,5 @@ obj-$(CONFIG_EXTCON_ADC_JACK) += extcon-adc-jack.o obj-$(CONFIG_EXTCON_MAX77665) += extcon-max77665.o obj-$(CONFIG_EXTCON_MAX77693) += extcon-max77693.o obj-$(CONFIG_EXTCON_MAX8997) += extcon-max8997.o +obj-$(CONFIG_EXTCON_PALMAS) += extcon-palmas.o obj-$(CONFIG_EXTCON_ARIZONA) += extcon-arizona.o diff --git a/drivers/extcon/extcon-palmas.c b/drivers/extcon/extcon-palmas.c new file mode 100644 index 000000000000..5977836b32de --- /dev/null +++ b/drivers/extcon/extcon-palmas.c @@ -0,0 +1,204 @@ +/* + * palmas-extcon.c -- Palmas VBUS detection in extcon framework. + * + * Copyright (c) 2013, NVIDIA Corporation. + * + * Author: Laxman Dewangan + * + * 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 version 2. + * + * This program is distributed "as is" WITHOUT ANY WARRANTY of any kind, + * whether express or implied; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + * 02111-1307, USA + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct palmas_extcon { + struct device *dev; + struct palmas *palmas; + struct extcon_dev *edev; + int vbus_irq; + int id_irq; +}; + +const char *palmas_excon_cable[] = { + [0] = "USB", + NULL, +}; + +static int palmas_extcon_cable_update( + struct palmas_extcon *palma_econ) +{ + int ret; + unsigned int status; + + ret = palmas_read(palma_econ->palmas, PALMAS_INTERRUPT_BASE, + PALMAS_INT3_LINE_STATE, &status); + if (ret < 0) { + dev_err(palma_econ->dev, + "INT3_LINE_STATE read failed: %d\n", ret); + return ret; + } + + if (status & PALMAS_INT3_LINE_STATE_VBUS) + extcon_set_cable_state(palma_econ->edev, "USB", true); + else + extcon_set_cable_state(palma_econ->edev, "USB", false); + + dev_info(palma_econ->dev, "VBUS %s status: 0x%02x\n", + (status & PALMAS_INT3_LINE_STATE_VBUS) ? "Valid" : "Invalid", + status); + + return 0; +} + +static irqreturn_t palmas_extcon_irq(int irq, void *data) +{ + struct palmas_extcon *palma_econ = data; + + if (irq == palma_econ->vbus_irq) + palmas_extcon_cable_update(palma_econ); + else + dev_err(palma_econ->dev, "Unknown interrupt %d\n", irq); + + return IRQ_HANDLED; +} + +static int __devinit palmas_extcon_probe(struct platform_device *pdev) +{ + struct palmas_extcon *palma_econ; + struct extcon_dev *edev; + int ret; + + palma_econ = devm_kzalloc(&pdev->dev, sizeof(*palma_econ), GFP_KERNEL); + if (!palma_econ) { + dev_err(&pdev->dev, "Memory allocation failed for palma_econ\n"); + return -ENOMEM; + } + + edev = devm_kzalloc(&pdev->dev, sizeof(*edev), GFP_KERNEL); + if (!edev) { + dev_err(&pdev->dev, "Memory allocation failed for edev\n"); + return -ENOMEM; + } + palma_econ->edev = edev; + palma_econ->edev->name = dev_name(&pdev->dev); + palma_econ->edev->supported_cable = palmas_excon_cable; + + palma_econ->dev = &pdev->dev; + palma_econ->palmas = dev_get_drvdata(pdev->dev.parent); + dev_set_drvdata(&pdev->dev, palma_econ); + + palma_econ->vbus_irq = platform_get_irq(pdev, 0); + palma_econ->id_irq = platform_get_irq(pdev, 1); + + ret = extcon_dev_register(palma_econ->edev, NULL); + if (ret < 0) { + dev_err(&pdev->dev, "failed to register extcon device\n"); + return ret; + } + + /* Set initial state */ + ret = palmas_extcon_cable_update(palma_econ); + if (ret < 0) { + dev_err(&pdev->dev, "Cable init failed: %d\n", ret); + goto out; + } + + ret = request_threaded_irq(palma_econ->vbus_irq, NULL, + palmas_extcon_irq, IRQF_ONESHOT | IRQF_EARLY_RESUME, + dev_name(palma_econ->dev), palma_econ); + if (ret < 0) { + dev_err(palma_econ->dev, "request irq %d failed: %dn", + palma_econ->vbus_irq, ret); + goto out; + } + + device_set_wakeup_capable(&pdev->dev, 1); + return 0; + +out: + extcon_dev_unregister(palma_econ->edev); + return ret; +} + +static int __devexit palmas_extcon_remove(struct platform_device *pdev) +{ + struct palmas_extcon *palma_econ = dev_get_drvdata(&pdev->dev); + + extcon_dev_unregister(palma_econ->edev); + free_irq(palma_econ->vbus_irq, palma_econ); + return 0; +} + +#ifdef CONFIG_PM_SLEEP +static int palmas_extcon_suspend(struct device *dev) +{ + struct palmas_extcon *palma_econ = dev_get_drvdata(dev); + + if (device_may_wakeup(dev)) + enable_irq_wake(palma_econ->vbus_irq); + return 0; +} + +static int palmas_extcon_resume(struct device *dev) +{ + struct palmas_extcon *palma_econ = dev_get_drvdata(dev); + + if (device_may_wakeup(dev)) + disable_irq_wake(palma_econ->vbus_irq); + return 0; +}; +#endif + +static const struct dev_pm_ops palmas_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(palmas_extcon_suspend, + palmas_extcon_resume) +}; + +static struct platform_driver palmas_extcon_driver = { + .probe = palmas_extcon_probe, + .remove = __devexit_p(palmas_extcon_remove), + .driver = { + .name = "palmas-extcon", + .owner = THIS_MODULE, + .pm = &palmas_pm_ops, + }, +}; + +static int __init palmas_extcon_driver_init(void) +{ + return platform_driver_register(&palmas_extcon_driver); +} +subsys_initcall_sync(palmas_extcon_driver_init); + +static void __exit palmas_extcon_driver_exit(void) +{ + platform_driver_unregister(&palmas_extcon_driver); +} +module_exit(palmas_extcon_driver_exit); + +MODULE_DESCRIPTION("palmas extcon driver"); +MODULE_AUTHOR("Laxman Dewangan"); +MODULE_ALIAS("platform:palmas-extcon"); +MODULE_LICENSE("GPL v2"); -- cgit v1.2.3