gpio-charger.c revision 5070437cd99511f69ae561f2ab417142a47a85ec
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 32 struct power_supply charger; 33}; 34 35static irqreturn_t gpio_charger_irq(int irq, void *devid) 36{ 37 struct power_supply *charger = devid; 38 39 power_supply_changed(charger); 40 41 return IRQ_HANDLED; 42} 43 44static inline struct gpio_charger *psy_to_gpio_charger(struct power_supply *psy) 45{ 46 return container_of(psy, struct gpio_charger, charger); 47} 48 49static int gpio_charger_get_property(struct power_supply *psy, 50 enum power_supply_property psp, union power_supply_propval *val) 51{ 52 struct gpio_charger *gpio_charger = psy_to_gpio_charger(psy); 53 const struct gpio_charger_platform_data *pdata = gpio_charger->pdata; 54 55 switch (psp) { 56 case POWER_SUPPLY_PROP_ONLINE: 57 val->intval = gpio_get_value(pdata->gpio); 58 val->intval ^= pdata->gpio_active_low; 59 break; 60 default: 61 return -EINVAL; 62 } 63 64 return 0; 65} 66 67static enum power_supply_property gpio_charger_properties[] = { 68 POWER_SUPPLY_PROP_ONLINE, 69}; 70 71static int __devinit gpio_charger_probe(struct platform_device *pdev) 72{ 73 const struct gpio_charger_platform_data *pdata = pdev->dev.platform_data; 74 struct gpio_charger *gpio_charger; 75 struct power_supply *charger; 76 int ret; 77 int irq; 78 79 if (!pdata) { 80 dev_err(&pdev->dev, "No platform data\n"); 81 return -EINVAL; 82 } 83 84 if (!gpio_is_valid(pdata->gpio)) { 85 dev_err(&pdev->dev, "Invalid gpio pin\n"); 86 return -EINVAL; 87 } 88 89 gpio_charger = kzalloc(sizeof(*gpio_charger), GFP_KERNEL); 90 91 charger = &gpio_charger->charger; 92 93 charger->name = pdata->name; 94 charger->type = pdata->type; 95 charger->properties = gpio_charger_properties; 96 charger->num_properties = ARRAY_SIZE(gpio_charger_properties); 97 charger->get_property = gpio_charger_get_property; 98 charger->supplied_to = pdata->supplied_to; 99 charger->num_supplicants = pdata->num_supplicants; 100 101 ret = gpio_request(pdata->gpio, dev_name(&pdev->dev)); 102 if (ret) { 103 dev_err(&pdev->dev, "Failed to request gpio pin: %d\n", ret); 104 goto err_free; 105 } 106 ret = gpio_direction_input(pdata->gpio); 107 if (ret) { 108 dev_err(&pdev->dev, "Failed to set gpio to input: %d\n", ret); 109 goto err_gpio_free; 110 } 111 112 irq = gpio_to_irq(pdata->gpio); 113 if (irq > 0) { 114 ret = request_irq(irq, gpio_charger_irq, 115 IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, 116 dev_name(&pdev->dev), charger); 117 if (ret) 118 dev_warn(&pdev->dev, "Failed to request irq: %d\n", ret); 119 else 120 gpio_charger->irq = irq; 121 } 122 123 gpio_charger->pdata = pdata; 124 125 ret = power_supply_register(&pdev->dev, charger); 126 if (ret < 0) { 127 dev_err(&pdev->dev, "Failed to register power supply: %d\n", ret); 128 goto err_irq_free; 129 } 130 131 platform_set_drvdata(pdev, gpio_charger); 132 133 return 0; 134 135err_irq_free: 136 if (gpio_charger->irq) 137 free_irq(gpio_charger->irq, charger); 138err_gpio_free: 139 gpio_free(pdata->gpio); 140err_free: 141 kfree(gpio_charger); 142 return ret; 143} 144 145static int __devexit gpio_charger_remove(struct platform_device *pdev) 146{ 147 struct gpio_charger *gpio_charger = platform_get_drvdata(pdev); 148 149 power_supply_unregister(&gpio_charger->charger); 150 151 if (gpio_charger->irq) 152 free_irq(gpio_charger->irq, &gpio_charger->charger); 153 gpio_free(gpio_charger->pdata->gpio); 154 155 platform_set_drvdata(pdev, NULL); 156 kfree(gpio_charger); 157 158 return 0; 159} 160 161static struct platform_driver gpio_charger_driver = { 162 .probe = gpio_charger_probe, 163 .remove = __devexit_p(gpio_charger_remove), 164 .driver = { 165 .name = "gpio-charger", 166 .owner = THIS_MODULE, 167 }, 168}; 169 170static int __init gpio_charger_init(void) 171{ 172 return platform_driver_register(&gpio_charger_driver); 173} 174module_init(gpio_charger_init); 175 176static void __exit gpio_charger_exit(void) 177{ 178 platform_driver_unregister(&gpio_charger_driver); 179} 180module_exit(gpio_charger_exit); 181 182MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>"); 183MODULE_DESCRIPTION("Driver for chargers which report their online status through a GPIO"); 184MODULE_LICENSE("GPL"); 185MODULE_ALIAS("platform:gpio-charger"); 186