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 #include <linux/of.h> 26 #include <linux/of_gpio.h> 27 28 #include <linux/power/gpio-charger.h> 29 30 struct gpio_charger { 31 const struct gpio_charger_platform_data *pdata; 32 unsigned int irq; 33 bool wakeup_enabled; 34 35 struct power_supply *charger; 36 struct power_supply_desc charger_desc; 37 }; 38 39 static irqreturn_t gpio_charger_irq(int irq, void *devid) 40 { 41 struct power_supply *charger = devid; 42 43 power_supply_changed(charger); 44 45 return IRQ_HANDLED; 46 } 47 48 static inline struct gpio_charger *psy_to_gpio_charger(struct power_supply *psy) 49 { 50 return power_supply_get_drvdata(psy); 51 } 52 53 static int gpio_charger_get_property(struct power_supply *psy, 54 enum power_supply_property psp, union power_supply_propval *val) 55 { 56 struct gpio_charger *gpio_charger = psy_to_gpio_charger(psy); 57 const struct gpio_charger_platform_data *pdata = gpio_charger->pdata; 58 59 switch (psp) { 60 case POWER_SUPPLY_PROP_ONLINE: 61 val->intval = !!gpio_get_value_cansleep(pdata->gpio); 62 val->intval ^= pdata->gpio_active_low; 63 break; 64 default: 65 return -EINVAL; 66 } 67 68 return 0; 69 } 70 71 static enum power_supply_property gpio_charger_properties[] = { 72 POWER_SUPPLY_PROP_ONLINE, 73 }; 74 75 static 76 struct gpio_charger_platform_data *gpio_charger_parse_dt(struct device *dev) 77 { 78 struct device_node *np = dev->of_node; 79 struct gpio_charger_platform_data *pdata; 80 const char *chargetype; 81 enum of_gpio_flags flags; 82 int ret; 83 84 if (!np) 85 return ERR_PTR(-ENOENT); 86 87 pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL); 88 if (!pdata) 89 return ERR_PTR(-ENOMEM); 90 91 pdata->name = np->name; 92 93 pdata->gpio = of_get_gpio_flags(np, 0, &flags); 94 if (pdata->gpio < 0) { 95 if (pdata->gpio != -EPROBE_DEFER) 96 dev_err(dev, "could not get charger gpio\n"); 97 return ERR_PTR(pdata->gpio); 98 } 99 100 pdata->gpio_active_low = !!(flags & OF_GPIO_ACTIVE_LOW); 101 102 pdata->type = POWER_SUPPLY_TYPE_UNKNOWN; 103 ret = of_property_read_string(np, "charger-type", &chargetype); 104 if (ret >= 0) { 105 if (!strncmp("unknown", chargetype, 7)) 106 pdata->type = POWER_SUPPLY_TYPE_UNKNOWN; 107 else if (!strncmp("battery", chargetype, 7)) 108 pdata->type = POWER_SUPPLY_TYPE_BATTERY; 109 else if (!strncmp("ups", chargetype, 3)) 110 pdata->type = POWER_SUPPLY_TYPE_UPS; 111 else if (!strncmp("mains", chargetype, 5)) 112 pdata->type = POWER_SUPPLY_TYPE_MAINS; 113 else if (!strncmp("usb-sdp", chargetype, 7)) 114 pdata->type = POWER_SUPPLY_TYPE_USB; 115 else if (!strncmp("usb-dcp", chargetype, 7)) 116 pdata->type = POWER_SUPPLY_TYPE_USB_DCP; 117 else if (!strncmp("usb-cdp", chargetype, 7)) 118 pdata->type = POWER_SUPPLY_TYPE_USB_CDP; 119 else if (!strncmp("usb-aca", chargetype, 7)) 120 pdata->type = POWER_SUPPLY_TYPE_USB_ACA; 121 else 122 dev_warn(dev, "unknown charger type %s\n", chargetype); 123 } 124 125 return pdata; 126 } 127 128 static int gpio_charger_probe(struct platform_device *pdev) 129 { 130 const struct gpio_charger_platform_data *pdata = pdev->dev.platform_data; 131 struct power_supply_config psy_cfg = {}; 132 struct gpio_charger *gpio_charger; 133 struct power_supply_desc *charger_desc; 134 int ret; 135 int irq; 136 137 if (!pdata) { 138 pdata = gpio_charger_parse_dt(&pdev->dev); 139 if (IS_ERR(pdata)) { 140 ret = PTR_ERR(pdata); 141 if (ret != -EPROBE_DEFER) 142 dev_err(&pdev->dev, "No platform data\n"); 143 return ret; 144 } 145 } 146 147 if (!gpio_is_valid(pdata->gpio)) { 148 dev_err(&pdev->dev, "Invalid gpio pin\n"); 149 return -EINVAL; 150 } 151 152 gpio_charger = devm_kzalloc(&pdev->dev, sizeof(*gpio_charger), 153 GFP_KERNEL); 154 if (!gpio_charger) { 155 dev_err(&pdev->dev, "Failed to alloc driver structure\n"); 156 return -ENOMEM; 157 } 158 159 charger_desc = &gpio_charger->charger_desc; 160 161 charger_desc->name = pdata->name ? pdata->name : "gpio-charger"; 162 charger_desc->type = pdata->type; 163 charger_desc->properties = gpio_charger_properties; 164 charger_desc->num_properties = ARRAY_SIZE(gpio_charger_properties); 165 charger_desc->get_property = gpio_charger_get_property; 166 167 psy_cfg.supplied_to = pdata->supplied_to; 168 psy_cfg.num_supplicants = pdata->num_supplicants; 169 psy_cfg.of_node = pdev->dev.of_node; 170 psy_cfg.drv_data = gpio_charger; 171 172 ret = gpio_request(pdata->gpio, dev_name(&pdev->dev)); 173 if (ret) { 174 dev_err(&pdev->dev, "Failed to request gpio pin: %d\n", ret); 175 goto err_free; 176 } 177 ret = gpio_direction_input(pdata->gpio); 178 if (ret) { 179 dev_err(&pdev->dev, "Failed to set gpio to input: %d\n", ret); 180 goto err_gpio_free; 181 } 182 183 gpio_charger->pdata = pdata; 184 185 gpio_charger->charger = power_supply_register(&pdev->dev, 186 charger_desc, &psy_cfg); 187 if (IS_ERR(gpio_charger->charger)) { 188 ret = PTR_ERR(gpio_charger->charger); 189 dev_err(&pdev->dev, "Failed to register power supply: %d\n", 190 ret); 191 goto err_gpio_free; 192 } 193 194 irq = gpio_to_irq(pdata->gpio); 195 if (irq > 0) { 196 ret = request_any_context_irq(irq, gpio_charger_irq, 197 IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, 198 dev_name(&pdev->dev), gpio_charger->charger); 199 if (ret < 0) 200 dev_warn(&pdev->dev, "Failed to request irq: %d\n", ret); 201 else 202 gpio_charger->irq = irq; 203 } 204 205 platform_set_drvdata(pdev, gpio_charger); 206 207 device_init_wakeup(&pdev->dev, 1); 208 209 return 0; 210 211 err_gpio_free: 212 gpio_free(pdata->gpio); 213 err_free: 214 return ret; 215 } 216 217 static int gpio_charger_remove(struct platform_device *pdev) 218 { 219 struct gpio_charger *gpio_charger = platform_get_drvdata(pdev); 220 221 if (gpio_charger->irq) 222 free_irq(gpio_charger->irq, gpio_charger->charger); 223 224 power_supply_unregister(gpio_charger->charger); 225 226 gpio_free(gpio_charger->pdata->gpio); 227 228 return 0; 229 } 230 231 #ifdef CONFIG_PM_SLEEP 232 static int gpio_charger_suspend(struct device *dev) 233 { 234 struct gpio_charger *gpio_charger = dev_get_drvdata(dev); 235 236 if (device_may_wakeup(dev)) 237 gpio_charger->wakeup_enabled = 238 !enable_irq_wake(gpio_charger->irq); 239 240 return 0; 241 } 242 243 static int gpio_charger_resume(struct device *dev) 244 { 245 struct platform_device *pdev = to_platform_device(dev); 246 struct gpio_charger *gpio_charger = platform_get_drvdata(pdev); 247 248 if (device_may_wakeup(dev) && gpio_charger->wakeup_enabled) 249 disable_irq_wake(gpio_charger->irq); 250 power_supply_changed(gpio_charger->charger); 251 252 return 0; 253 } 254 #endif 255 256 static SIMPLE_DEV_PM_OPS(gpio_charger_pm_ops, 257 gpio_charger_suspend, gpio_charger_resume); 258 259 static const struct of_device_id gpio_charger_match[] = { 260 { .compatible = "gpio-charger" }, 261 { } 262 }; 263 MODULE_DEVICE_TABLE(of, gpio_charger_match); 264 265 static struct platform_driver gpio_charger_driver = { 266 .probe = gpio_charger_probe, 267 .remove = gpio_charger_remove, 268 .driver = { 269 .name = "gpio-charger", 270 .pm = &gpio_charger_pm_ops, 271 .of_match_table = gpio_charger_match, 272 }, 273 }; 274 275 module_platform_driver(gpio_charger_driver); 276 277 MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>"); 278 MODULE_DESCRIPTION("Driver for chargers which report their online status through a GPIO"); 279 MODULE_LICENSE("GPL"); 280 MODULE_ALIAS("platform:gpio-charger"); 281