15070437cd99511f69ae561f2ab417142a47a85ecLars-Peter Clausen/* 25070437cd99511f69ae561f2ab417142a47a85ecLars-Peter Clausen * Copyright (C) 2010, Lars-Peter Clausen <lars@metafoo.de> 35070437cd99511f69ae561f2ab417142a47a85ecLars-Peter Clausen * Driver for chargers which report their online status through a GPIO pin 45070437cd99511f69ae561f2ab417142a47a85ecLars-Peter Clausen * 55070437cd99511f69ae561f2ab417142a47a85ecLars-Peter Clausen * This program is free software; you can redistribute it and/or modify it 65070437cd99511f69ae561f2ab417142a47a85ecLars-Peter Clausen * under the terms of the GNU General Public License as published by the 75070437cd99511f69ae561f2ab417142a47a85ecLars-Peter Clausen * Free Software Foundation; either version 2 of the License, or (at your 85070437cd99511f69ae561f2ab417142a47a85ecLars-Peter Clausen * option) any later version. 95070437cd99511f69ae561f2ab417142a47a85ecLars-Peter Clausen * 105070437cd99511f69ae561f2ab417142a47a85ecLars-Peter Clausen * You should have received a copy of the GNU General Public License along 115070437cd99511f69ae561f2ab417142a47a85ecLars-Peter Clausen * with this program; if not, write to the Free Software Foundation, Inc., 125070437cd99511f69ae561f2ab417142a47a85ecLars-Peter Clausen * 675 Mass Ave, Cambridge, MA 02139, USA. 135070437cd99511f69ae561f2ab417142a47a85ecLars-Peter Clausen * 145070437cd99511f69ae561f2ab417142a47a85ecLars-Peter Clausen */ 155070437cd99511f69ae561f2ab417142a47a85ecLars-Peter Clausen 165070437cd99511f69ae561f2ab417142a47a85ecLars-Peter Clausen#include <linux/device.h> 175070437cd99511f69ae561f2ab417142a47a85ecLars-Peter Clausen#include <linux/gpio.h> 185070437cd99511f69ae561f2ab417142a47a85ecLars-Peter Clausen#include <linux/init.h> 195070437cd99511f69ae561f2ab417142a47a85ecLars-Peter Clausen#include <linux/interrupt.h> 205070437cd99511f69ae561f2ab417142a47a85ecLars-Peter Clausen#include <linux/kernel.h> 215070437cd99511f69ae561f2ab417142a47a85ecLars-Peter Clausen#include <linux/module.h> 225070437cd99511f69ae561f2ab417142a47a85ecLars-Peter Clausen#include <linux/platform_device.h> 235070437cd99511f69ae561f2ab417142a47a85ecLars-Peter Clausen#include <linux/power_supply.h> 245070437cd99511f69ae561f2ab417142a47a85ecLars-Peter Clausen#include <linux/slab.h> 255070437cd99511f69ae561f2ab417142a47a85ecLars-Peter Clausen 265070437cd99511f69ae561f2ab417142a47a85ecLars-Peter Clausen#include <linux/power/gpio-charger.h> 275070437cd99511f69ae561f2ab417142a47a85ecLars-Peter Clausen 285070437cd99511f69ae561f2ab417142a47a85ecLars-Peter Clausenstruct gpio_charger { 295070437cd99511f69ae561f2ab417142a47a85ecLars-Peter Clausen const struct gpio_charger_platform_data *pdata; 305070437cd99511f69ae561f2ab417142a47a85ecLars-Peter Clausen unsigned int irq; 314ea81266010ae41a48c7ce393335dfe241931209Dmitry Eremin-Solenikov bool wakeup_enabled; 325070437cd99511f69ae561f2ab417142a47a85ecLars-Peter Clausen 335070437cd99511f69ae561f2ab417142a47a85ecLars-Peter Clausen struct power_supply charger; 345070437cd99511f69ae561f2ab417142a47a85ecLars-Peter Clausen}; 355070437cd99511f69ae561f2ab417142a47a85ecLars-Peter Clausen 365070437cd99511f69ae561f2ab417142a47a85ecLars-Peter Clausenstatic irqreturn_t gpio_charger_irq(int irq, void *devid) 375070437cd99511f69ae561f2ab417142a47a85ecLars-Peter Clausen{ 385070437cd99511f69ae561f2ab417142a47a85ecLars-Peter Clausen struct power_supply *charger = devid; 395070437cd99511f69ae561f2ab417142a47a85ecLars-Peter Clausen 405070437cd99511f69ae561f2ab417142a47a85ecLars-Peter Clausen power_supply_changed(charger); 415070437cd99511f69ae561f2ab417142a47a85ecLars-Peter Clausen 425070437cd99511f69ae561f2ab417142a47a85ecLars-Peter Clausen return IRQ_HANDLED; 435070437cd99511f69ae561f2ab417142a47a85ecLars-Peter Clausen} 445070437cd99511f69ae561f2ab417142a47a85ecLars-Peter Clausen 455070437cd99511f69ae561f2ab417142a47a85ecLars-Peter Clausenstatic inline struct gpio_charger *psy_to_gpio_charger(struct power_supply *psy) 465070437cd99511f69ae561f2ab417142a47a85ecLars-Peter Clausen{ 475070437cd99511f69ae561f2ab417142a47a85ecLars-Peter Clausen return container_of(psy, struct gpio_charger, charger); 485070437cd99511f69ae561f2ab417142a47a85ecLars-Peter Clausen} 495070437cd99511f69ae561f2ab417142a47a85ecLars-Peter Clausen 505070437cd99511f69ae561f2ab417142a47a85ecLars-Peter Clausenstatic int gpio_charger_get_property(struct power_supply *psy, 515070437cd99511f69ae561f2ab417142a47a85ecLars-Peter Clausen enum power_supply_property psp, union power_supply_propval *val) 525070437cd99511f69ae561f2ab417142a47a85ecLars-Peter Clausen{ 535070437cd99511f69ae561f2ab417142a47a85ecLars-Peter Clausen struct gpio_charger *gpio_charger = psy_to_gpio_charger(psy); 545070437cd99511f69ae561f2ab417142a47a85ecLars-Peter Clausen const struct gpio_charger_platform_data *pdata = gpio_charger->pdata; 555070437cd99511f69ae561f2ab417142a47a85ecLars-Peter Clausen 565070437cd99511f69ae561f2ab417142a47a85ecLars-Peter Clausen switch (psp) { 575070437cd99511f69ae561f2ab417142a47a85ecLars-Peter Clausen case POWER_SUPPLY_PROP_ONLINE: 584d96fb1ec81118c6406fe6d3670f172b2faaedf3Heiko Stuebner val->intval = !!gpio_get_value_cansleep(pdata->gpio); 595070437cd99511f69ae561f2ab417142a47a85ecLars-Peter Clausen val->intval ^= pdata->gpio_active_low; 605070437cd99511f69ae561f2ab417142a47a85ecLars-Peter Clausen break; 615070437cd99511f69ae561f2ab417142a47a85ecLars-Peter Clausen default: 625070437cd99511f69ae561f2ab417142a47a85ecLars-Peter Clausen return -EINVAL; 635070437cd99511f69ae561f2ab417142a47a85ecLars-Peter Clausen } 645070437cd99511f69ae561f2ab417142a47a85ecLars-Peter Clausen 655070437cd99511f69ae561f2ab417142a47a85ecLars-Peter Clausen return 0; 665070437cd99511f69ae561f2ab417142a47a85ecLars-Peter Clausen} 675070437cd99511f69ae561f2ab417142a47a85ecLars-Peter Clausen 685070437cd99511f69ae561f2ab417142a47a85ecLars-Peter Clausenstatic enum power_supply_property gpio_charger_properties[] = { 695070437cd99511f69ae561f2ab417142a47a85ecLars-Peter Clausen POWER_SUPPLY_PROP_ONLINE, 705070437cd99511f69ae561f2ab417142a47a85ecLars-Peter Clausen}; 715070437cd99511f69ae561f2ab417142a47a85ecLars-Peter Clausen 72c8afa6406e60aec6ff90033e5ffe41a206609296Bill Pembertonstatic int gpio_charger_probe(struct platform_device *pdev) 735070437cd99511f69ae561f2ab417142a47a85ecLars-Peter Clausen{ 745070437cd99511f69ae561f2ab417142a47a85ecLars-Peter Clausen const struct gpio_charger_platform_data *pdata = pdev->dev.platform_data; 755070437cd99511f69ae561f2ab417142a47a85ecLars-Peter Clausen struct gpio_charger *gpio_charger; 765070437cd99511f69ae561f2ab417142a47a85ecLars-Peter Clausen struct power_supply *charger; 775070437cd99511f69ae561f2ab417142a47a85ecLars-Peter Clausen int ret; 785070437cd99511f69ae561f2ab417142a47a85ecLars-Peter Clausen int irq; 795070437cd99511f69ae561f2ab417142a47a85ecLars-Peter Clausen 805070437cd99511f69ae561f2ab417142a47a85ecLars-Peter Clausen if (!pdata) { 815070437cd99511f69ae561f2ab417142a47a85ecLars-Peter Clausen dev_err(&pdev->dev, "No platform data\n"); 825070437cd99511f69ae561f2ab417142a47a85ecLars-Peter Clausen return -EINVAL; 835070437cd99511f69ae561f2ab417142a47a85ecLars-Peter Clausen } 845070437cd99511f69ae561f2ab417142a47a85ecLars-Peter Clausen 855070437cd99511f69ae561f2ab417142a47a85ecLars-Peter Clausen if (!gpio_is_valid(pdata->gpio)) { 865070437cd99511f69ae561f2ab417142a47a85ecLars-Peter Clausen dev_err(&pdev->dev, "Invalid gpio pin\n"); 875070437cd99511f69ae561f2ab417142a47a85ecLars-Peter Clausen return -EINVAL; 885070437cd99511f69ae561f2ab417142a47a85ecLars-Peter Clausen } 895070437cd99511f69ae561f2ab417142a47a85ecLars-Peter Clausen 90b92ff855b97795d8be3115c0aff04f6efb5a53aaJingoo Han gpio_charger = devm_kzalloc(&pdev->dev, sizeof(*gpio_charger), 91b92ff855b97795d8be3115c0aff04f6efb5a53aaJingoo Han GFP_KERNEL); 922e9ff5f5e4c6b034554f3539f29529265279102cLars-Peter Clausen if (!gpio_charger) { 932e9ff5f5e4c6b034554f3539f29529265279102cLars-Peter Clausen dev_err(&pdev->dev, "Failed to alloc driver structure\n"); 942e9ff5f5e4c6b034554f3539f29529265279102cLars-Peter Clausen return -ENOMEM; 952e9ff5f5e4c6b034554f3539f29529265279102cLars-Peter Clausen } 965070437cd99511f69ae561f2ab417142a47a85ecLars-Peter Clausen 975070437cd99511f69ae561f2ab417142a47a85ecLars-Peter Clausen charger = &gpio_charger->charger; 985070437cd99511f69ae561f2ab417142a47a85ecLars-Peter Clausen 9980577b8a478f3386d106464f2a2241b2d43571ceLars-Peter Clausen charger->name = pdata->name ? pdata->name : "gpio-charger"; 1005070437cd99511f69ae561f2ab417142a47a85ecLars-Peter Clausen charger->type = pdata->type; 1015070437cd99511f69ae561f2ab417142a47a85ecLars-Peter Clausen charger->properties = gpio_charger_properties; 1025070437cd99511f69ae561f2ab417142a47a85ecLars-Peter Clausen charger->num_properties = ARRAY_SIZE(gpio_charger_properties); 10326eb387265872b59566ddeed5e9bf142a6b9ff5bLars-Peter Clausen charger->get_property = gpio_charger_get_property; 1045070437cd99511f69ae561f2ab417142a47a85ecLars-Peter Clausen charger->supplied_to = pdata->supplied_to; 1055070437cd99511f69ae561f2ab417142a47a85ecLars-Peter Clausen charger->num_supplicants = pdata->num_supplicants; 1065070437cd99511f69ae561f2ab417142a47a85ecLars-Peter Clausen 1075070437cd99511f69ae561f2ab417142a47a85ecLars-Peter Clausen ret = gpio_request(pdata->gpio, dev_name(&pdev->dev)); 1085070437cd99511f69ae561f2ab417142a47a85ecLars-Peter Clausen if (ret) { 1095070437cd99511f69ae561f2ab417142a47a85ecLars-Peter Clausen dev_err(&pdev->dev, "Failed to request gpio pin: %d\n", ret); 1105070437cd99511f69ae561f2ab417142a47a85ecLars-Peter Clausen goto err_free; 1115070437cd99511f69ae561f2ab417142a47a85ecLars-Peter Clausen } 1125070437cd99511f69ae561f2ab417142a47a85ecLars-Peter Clausen ret = gpio_direction_input(pdata->gpio); 1135070437cd99511f69ae561f2ab417142a47a85ecLars-Peter Clausen if (ret) { 1145070437cd99511f69ae561f2ab417142a47a85ecLars-Peter Clausen dev_err(&pdev->dev, "Failed to set gpio to input: %d\n", ret); 1155070437cd99511f69ae561f2ab417142a47a85ecLars-Peter Clausen goto err_gpio_free; 1165070437cd99511f69ae561f2ab417142a47a85ecLars-Peter Clausen } 1175070437cd99511f69ae561f2ab417142a47a85ecLars-Peter Clausen 11826eb387265872b59566ddeed5e9bf142a6b9ff5bLars-Peter Clausen gpio_charger->pdata = pdata; 11926eb387265872b59566ddeed5e9bf142a6b9ff5bLars-Peter Clausen 12026eb387265872b59566ddeed5e9bf142a6b9ff5bLars-Peter Clausen ret = power_supply_register(&pdev->dev, charger); 12126eb387265872b59566ddeed5e9bf142a6b9ff5bLars-Peter Clausen if (ret < 0) { 12226eb387265872b59566ddeed5e9bf142a6b9ff5bLars-Peter Clausen dev_err(&pdev->dev, "Failed to register power supply: %d\n", 12326eb387265872b59566ddeed5e9bf142a6b9ff5bLars-Peter Clausen ret); 12426eb387265872b59566ddeed5e9bf142a6b9ff5bLars-Peter Clausen goto err_gpio_free; 12526eb387265872b59566ddeed5e9bf142a6b9ff5bLars-Peter Clausen } 12626eb387265872b59566ddeed5e9bf142a6b9ff5bLars-Peter Clausen 1275070437cd99511f69ae561f2ab417142a47a85ecLars-Peter Clausen irq = gpio_to_irq(pdata->gpio); 1285070437cd99511f69ae561f2ab417142a47a85ecLars-Peter Clausen if (irq > 0) { 12926eb387265872b59566ddeed5e9bf142a6b9ff5bLars-Peter Clausen ret = request_any_context_irq(irq, gpio_charger_irq, 1305070437cd99511f69ae561f2ab417142a47a85ecLars-Peter Clausen IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, 1315070437cd99511f69ae561f2ab417142a47a85ecLars-Peter Clausen dev_name(&pdev->dev), charger); 132f8c63918c9a3004bd052fa4af08aa1cefbcd79acAxel Lin if (ret < 0) 1335070437cd99511f69ae561f2ab417142a47a85ecLars-Peter Clausen dev_warn(&pdev->dev, "Failed to request irq: %d\n", ret); 1345070437cd99511f69ae561f2ab417142a47a85ecLars-Peter Clausen else 1355070437cd99511f69ae561f2ab417142a47a85ecLars-Peter Clausen gpio_charger->irq = irq; 1365070437cd99511f69ae561f2ab417142a47a85ecLars-Peter Clausen } 1375070437cd99511f69ae561f2ab417142a47a85ecLars-Peter Clausen 1385070437cd99511f69ae561f2ab417142a47a85ecLars-Peter Clausen platform_set_drvdata(pdev, gpio_charger); 1395070437cd99511f69ae561f2ab417142a47a85ecLars-Peter Clausen 1404ea81266010ae41a48c7ce393335dfe241931209Dmitry Eremin-Solenikov device_init_wakeup(&pdev->dev, 1); 1414ea81266010ae41a48c7ce393335dfe241931209Dmitry Eremin-Solenikov 1425070437cd99511f69ae561f2ab417142a47a85ecLars-Peter Clausen return 0; 1435070437cd99511f69ae561f2ab417142a47a85ecLars-Peter Clausen 1445070437cd99511f69ae561f2ab417142a47a85ecLars-Peter Clausenerr_gpio_free: 1455070437cd99511f69ae561f2ab417142a47a85ecLars-Peter Clausen gpio_free(pdata->gpio); 1465070437cd99511f69ae561f2ab417142a47a85ecLars-Peter Clausenerr_free: 1475070437cd99511f69ae561f2ab417142a47a85ecLars-Peter Clausen return ret; 1485070437cd99511f69ae561f2ab417142a47a85ecLars-Peter Clausen} 1495070437cd99511f69ae561f2ab417142a47a85ecLars-Peter Clausen 150415ec69fb1861fc377c65cb30ddc76999891b8e1Bill Pembertonstatic int gpio_charger_remove(struct platform_device *pdev) 1515070437cd99511f69ae561f2ab417142a47a85ecLars-Peter Clausen{ 1525070437cd99511f69ae561f2ab417142a47a85ecLars-Peter Clausen struct gpio_charger *gpio_charger = platform_get_drvdata(pdev); 1535070437cd99511f69ae561f2ab417142a47a85ecLars-Peter Clausen 1545070437cd99511f69ae561f2ab417142a47a85ecLars-Peter Clausen if (gpio_charger->irq) 1555070437cd99511f69ae561f2ab417142a47a85ecLars-Peter Clausen free_irq(gpio_charger->irq, &gpio_charger->charger); 15626eb387265872b59566ddeed5e9bf142a6b9ff5bLars-Peter Clausen 15726eb387265872b59566ddeed5e9bf142a6b9ff5bLars-Peter Clausen power_supply_unregister(&gpio_charger->charger); 15826eb387265872b59566ddeed5e9bf142a6b9ff5bLars-Peter Clausen 1595070437cd99511f69ae561f2ab417142a47a85ecLars-Peter Clausen gpio_free(gpio_charger->pdata->gpio); 1605070437cd99511f69ae561f2ab417142a47a85ecLars-Peter Clausen 1615070437cd99511f69ae561f2ab417142a47a85ecLars-Peter Clausen return 0; 1625070437cd99511f69ae561f2ab417142a47a85ecLars-Peter Clausen} 1635070437cd99511f69ae561f2ab417142a47a85ecLars-Peter Clausen 164f10513de2a6036742b55e23278f0322f2e5c3772Benson Leung#ifdef CONFIG_PM_SLEEP 1654ea81266010ae41a48c7ce393335dfe241931209Dmitry Eremin-Solenikovstatic int gpio_charger_suspend(struct device *dev) 1664ea81266010ae41a48c7ce393335dfe241931209Dmitry Eremin-Solenikov{ 1674ea81266010ae41a48c7ce393335dfe241931209Dmitry Eremin-Solenikov struct gpio_charger *gpio_charger = dev_get_drvdata(dev); 1684ea81266010ae41a48c7ce393335dfe241931209Dmitry Eremin-Solenikov 1694ea81266010ae41a48c7ce393335dfe241931209Dmitry Eremin-Solenikov if (device_may_wakeup(dev)) 1704ea81266010ae41a48c7ce393335dfe241931209Dmitry Eremin-Solenikov gpio_charger->wakeup_enabled = 1714ea81266010ae41a48c7ce393335dfe241931209Dmitry Eremin-Solenikov enable_irq_wake(gpio_charger->irq); 1724ea81266010ae41a48c7ce393335dfe241931209Dmitry Eremin-Solenikov 1734ea81266010ae41a48c7ce393335dfe241931209Dmitry Eremin-Solenikov return 0; 1744ea81266010ae41a48c7ce393335dfe241931209Dmitry Eremin-Solenikov} 1754ea81266010ae41a48c7ce393335dfe241931209Dmitry Eremin-Solenikov 176f10513de2a6036742b55e23278f0322f2e5c3772Benson Leungstatic int gpio_charger_resume(struct device *dev) 177f10513de2a6036742b55e23278f0322f2e5c3772Benson Leung{ 178f10513de2a6036742b55e23278f0322f2e5c3772Benson Leung struct platform_device *pdev = to_platform_device(dev); 179f10513de2a6036742b55e23278f0322f2e5c3772Benson Leung struct gpio_charger *gpio_charger = platform_get_drvdata(pdev); 180f10513de2a6036742b55e23278f0322f2e5c3772Benson Leung 1814ea81266010ae41a48c7ce393335dfe241931209Dmitry Eremin-Solenikov if (gpio_charger->wakeup_enabled) 1824ea81266010ae41a48c7ce393335dfe241931209Dmitry Eremin-Solenikov disable_irq_wake(gpio_charger->irq); 183f10513de2a6036742b55e23278f0322f2e5c3772Benson Leung power_supply_changed(&gpio_charger->charger); 184f10513de2a6036742b55e23278f0322f2e5c3772Benson Leung 185f10513de2a6036742b55e23278f0322f2e5c3772Benson Leung return 0; 186f10513de2a6036742b55e23278f0322f2e5c3772Benson Leung} 187f10513de2a6036742b55e23278f0322f2e5c3772Benson Leung#endif 188f10513de2a6036742b55e23278f0322f2e5c3772Benson Leung 1894ea81266010ae41a48c7ce393335dfe241931209Dmitry Eremin-Solenikovstatic SIMPLE_DEV_PM_OPS(gpio_charger_pm_ops, 1904ea81266010ae41a48c7ce393335dfe241931209Dmitry Eremin-Solenikov gpio_charger_suspend, gpio_charger_resume); 191f10513de2a6036742b55e23278f0322f2e5c3772Benson Leung 1925070437cd99511f69ae561f2ab417142a47a85ecLars-Peter Clausenstatic struct platform_driver gpio_charger_driver = { 1935070437cd99511f69ae561f2ab417142a47a85ecLars-Peter Clausen .probe = gpio_charger_probe, 19428ea73f4c67cb3dd8c972b21d9fdf84ea78d6daaBill Pemberton .remove = gpio_charger_remove, 1955070437cd99511f69ae561f2ab417142a47a85ecLars-Peter Clausen .driver = { 1965070437cd99511f69ae561f2ab417142a47a85ecLars-Peter Clausen .name = "gpio-charger", 1975070437cd99511f69ae561f2ab417142a47a85ecLars-Peter Clausen .owner = THIS_MODULE, 198f10513de2a6036742b55e23278f0322f2e5c3772Benson Leung .pm = &gpio_charger_pm_ops, 1995070437cd99511f69ae561f2ab417142a47a85ecLars-Peter Clausen }, 2005070437cd99511f69ae561f2ab417142a47a85ecLars-Peter Clausen}; 2015070437cd99511f69ae561f2ab417142a47a85ecLars-Peter Clausen 202300bac7fb85a20b2704dc3645419057992f78565Axel Linmodule_platform_driver(gpio_charger_driver); 2035070437cd99511f69ae561f2ab417142a47a85ecLars-Peter Clausen 2045070437cd99511f69ae561f2ab417142a47a85ecLars-Peter ClausenMODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>"); 2055070437cd99511f69ae561f2ab417142a47a85ecLars-Peter ClausenMODULE_DESCRIPTION("Driver for chargers which report their online status through a GPIO"); 2065070437cd99511f69ae561f2ab417142a47a85ecLars-Peter ClausenMODULE_LICENSE("GPL"); 2075070437cd99511f69ae561f2ab417142a47a85ecLars-Peter ClausenMODULE_ALIAS("platform:gpio-charger"); 208