1/* 2 * Copyright (C) 2010, Lars-Peter Clausen <lars@metafoo.de> 3 * Driver for chargers which report their online status through a GPIO pin 4 * 5 * This program is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License as published by the 7 * Free Software Foundation; either version 2 of the License, or (at your 8 * option) any later version. 9 * 10 * You should have received a copy of the GNU General Public License along 11 * with this program; if not, write to the Free Software Foundation, Inc., 12 * 675 Mass Ave, Cambridge, MA 02139, USA. 13 * 14 */ 15 16#include <linux/device.h> 17#include <linux/gpio.h> 18#include <linux/init.h> 19#include <linux/interrupt.h> 20#include <linux/kernel.h> 21#include <linux/module.h> 22#include <linux/platform_device.h> 23#include <linux/power_supply.h> 24#include <linux/slab.h> 25 26#include <linux/power/gpio-charger.h> 27 28struct gpio_charger { 29 const struct gpio_charger_platform_data *pdata; 30 unsigned int irq; 31 bool wakeup_enabled; 32 33 struct power_supply charger; 34}; 35 36static irqreturn_t gpio_charger_irq(int irq, void *devid) 37{ 38 struct power_supply *charger = devid; 39 40 power_supply_changed(charger); 41 42 return IRQ_HANDLED; 43} 44 45static inline struct gpio_charger *psy_to_gpio_charger(struct power_supply *psy) 46{ 47 return container_of(psy, struct gpio_charger, charger); 48} 49 50static int gpio_charger_get_property(struct power_supply *psy, 51 enum power_supply_property psp, union power_supply_propval *val) 52{ 53 struct gpio_charger *gpio_charger = psy_to_gpio_charger(psy); 54 const struct gpio_charger_platform_data *pdata = gpio_charger->pdata; 55 56 switch (psp) { 57 case POWER_SUPPLY_PROP_ONLINE: 58 val->intval = !!gpio_get_value_cansleep(pdata->gpio); 59 val->intval ^= pdata->gpio_active_low; 60 break; 61 default: 62 return -EINVAL; 63 } 64 65 return 0; 66} 67 68static enum power_supply_property gpio_charger_properties[] = { 69 POWER_SUPPLY_PROP_ONLINE, 70}; 71 72static int gpio_charger_probe(struct platform_device *pdev) 73{ 74 const struct gpio_charger_platform_data *pdata = pdev->dev.platform_data; 75 struct gpio_charger *gpio_charger; 76 struct power_supply *charger; 77 int ret; 78 int irq; 79 80 if (!pdata) { 81 dev_err(&pdev->dev, "No platform data\n"); 82 return -EINVAL; 83 } 84 85 if (!gpio_is_valid(pdata->gpio)) { 86 dev_err(&pdev->dev, "Invalid gpio pin\n"); 87 return -EINVAL; 88 } 89 90 gpio_charger = devm_kzalloc(&pdev->dev, sizeof(*gpio_charger), 91 GFP_KERNEL); 92 if (!gpio_charger) { 93 dev_err(&pdev->dev, "Failed to alloc driver structure\n"); 94 return -ENOMEM; 95 } 96 97 charger = &gpio_charger->charger; 98 99 charger->name = pdata->name ? pdata->name : "gpio-charger"; 100 charger->type = pdata->type; 101 charger->properties = gpio_charger_properties; 102 charger->num_properties = ARRAY_SIZE(gpio_charger_properties); 103 charger->get_property = gpio_charger_get_property; 104 charger->supplied_to = pdata->supplied_to; 105 charger->num_supplicants = pdata->num_supplicants; 106 107 ret = gpio_request(pdata->gpio, dev_name(&pdev->dev)); 108 if (ret) { 109 dev_err(&pdev->dev, "Failed to request gpio pin: %d\n", ret); 110 goto err_free; 111 } 112 ret = gpio_direction_input(pdata->gpio); 113 if (ret) { 114 dev_err(&pdev->dev, "Failed to set gpio to input: %d\n", ret); 115 goto err_gpio_free; 116 } 117 118 gpio_charger->pdata = pdata; 119 120 ret = power_supply_register(&pdev->dev, charger); 121 if (ret < 0) { 122 dev_err(&pdev->dev, "Failed to register power supply: %d\n", 123 ret); 124 goto err_gpio_free; 125 } 126 127 irq = gpio_to_irq(pdata->gpio); 128 if (irq > 0) { 129 ret = request_any_context_irq(irq, gpio_charger_irq, 130 IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, 131 dev_name(&pdev->dev), charger); 132 if (ret < 0) 133 dev_warn(&pdev->dev, "Failed to request irq: %d\n", ret); 134 else 135 gpio_charger->irq = irq; 136 } 137 138 platform_set_drvdata(pdev, gpio_charger); 139 140 device_init_wakeup(&pdev->dev, 1); 141 142 return 0; 143 144err_gpio_free: 145 gpio_free(pdata->gpio); 146err_free: 147 return ret; 148} 149 150static int gpio_charger_remove(struct platform_device *pdev) 151{ 152 struct gpio_charger *gpio_charger = platform_get_drvdata(pdev); 153 154 if (gpio_charger->irq) 155 free_irq(gpio_charger->irq, &gpio_charger->charger); 156 157 power_supply_unregister(&gpio_charger->charger); 158 159 gpio_free(gpio_charger->pdata->gpio); 160 161 return 0; 162} 163 164#ifdef CONFIG_PM_SLEEP 165static int gpio_charger_suspend(struct device *dev) 166{ 167 struct gpio_charger *gpio_charger = dev_get_drvdata(dev); 168 169 if (device_may_wakeup(dev)) 170 gpio_charger->wakeup_enabled = 171 enable_irq_wake(gpio_charger->irq); 172 173 return 0; 174} 175 176static int gpio_charger_resume(struct device *dev) 177{ 178 struct platform_device *pdev = to_platform_device(dev); 179 struct gpio_charger *gpio_charger = platform_get_drvdata(pdev); 180 181 if (gpio_charger->wakeup_enabled) 182 disable_irq_wake(gpio_charger->irq); 183 power_supply_changed(&gpio_charger->charger); 184 185 return 0; 186} 187#endif 188 189static SIMPLE_DEV_PM_OPS(gpio_charger_pm_ops, 190 gpio_charger_suspend, gpio_charger_resume); 191 192static struct platform_driver gpio_charger_driver = { 193 .probe = gpio_charger_probe, 194 .remove = gpio_charger_remove, 195 .driver = { 196 .name = "gpio-charger", 197 .owner = THIS_MODULE, 198 .pm = &gpio_charger_pm_ops, 199 }, 200}; 201 202module_platform_driver(gpio_charger_driver); 203 204MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>"); 205MODULE_DESCRIPTION("Driver for chargers which report their online status through a GPIO"); 206MODULE_LICENSE("GPL"); 207MODULE_ALIAS("platform:gpio-charger"); 208