max8903_charger.c revision 7e6d62db5efecded2dd4606ff2ff3726b9c52505
1/* 2 * max8903_charger.c - Maxim 8903 USB/Adapter Charger Driver 3 * 4 * Copyright (C) 2011 Samsung Electronics 5 * MyungJoo Ham <myungjoo.ham@samsung.com> 6 * 7 * This program is free software; you can redistribute it and/or modify 8 * it under the terms of the GNU General Public License as published by 9 * the Free Software Foundation; either version 2 of the License, or 10 * (at your option) any later version. 11 * 12 * This program is distributed in the hope that it will be useful, 13 * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 * GNU General Public License for more details. 16 * 17 * You should have received a copy of the GNU General Public License 18 * along with this program; if not, write to the Free Software 19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 20 * 21 */ 22 23#include <linux/gpio.h> 24#include <linux/interrupt.h> 25#include <linux/module.h> 26#include <linux/slab.h> 27#include <linux/power_supply.h> 28#include <linux/platform_device.h> 29#include <linux/power/max8903_charger.h> 30 31struct max8903_data { 32 struct max8903_pdata pdata; 33 struct device *dev; 34 struct power_supply psy; 35 bool fault; 36 bool usb_in; 37 bool ta_in; 38}; 39 40static enum power_supply_property max8903_charger_props[] = { 41 POWER_SUPPLY_PROP_STATUS, /* Charger status output */ 42 POWER_SUPPLY_PROP_ONLINE, /* External power source */ 43 POWER_SUPPLY_PROP_HEALTH, /* Fault or OK */ 44}; 45 46static int max8903_get_property(struct power_supply *psy, 47 enum power_supply_property psp, 48 union power_supply_propval *val) 49{ 50 struct max8903_data *data = container_of(psy, 51 struct max8903_data, psy); 52 53 switch (psp) { 54 case POWER_SUPPLY_PROP_STATUS: 55 val->intval = POWER_SUPPLY_STATUS_UNKNOWN; 56 if (data->pdata.chg) { 57 if (gpio_get_value(data->pdata.chg) == 0) 58 val->intval = POWER_SUPPLY_STATUS_CHARGING; 59 else if (data->usb_in || data->ta_in) 60 val->intval = POWER_SUPPLY_STATUS_NOT_CHARGING; 61 else 62 val->intval = POWER_SUPPLY_STATUS_DISCHARGING; 63 } 64 break; 65 case POWER_SUPPLY_PROP_ONLINE: 66 val->intval = 0; 67 if (data->usb_in || data->ta_in) 68 val->intval = 1; 69 break; 70 case POWER_SUPPLY_PROP_HEALTH: 71 val->intval = POWER_SUPPLY_HEALTH_GOOD; 72 if (data->fault) 73 val->intval = POWER_SUPPLY_HEALTH_UNSPEC_FAILURE; 74 break; 75 default: 76 return -EINVAL; 77 } 78 return 0; 79} 80 81static irqreturn_t max8903_dcin(int irq, void *_data) 82{ 83 struct max8903_data *data = _data; 84 struct max8903_pdata *pdata = &data->pdata; 85 bool ta_in; 86 enum power_supply_type old_type; 87 88 ta_in = gpio_get_value(pdata->dok) ? false : true; 89 90 if (ta_in == data->ta_in) 91 return IRQ_HANDLED; 92 93 data->ta_in = ta_in; 94 95 /* Set Current-Limit-Mode 1:DC 0:USB */ 96 if (pdata->dcm) 97 gpio_set_value(pdata->dcm, ta_in ? 1 : 0); 98 99 /* Charger Enable / Disable (cen is negated) */ 100 if (pdata->cen) 101 gpio_set_value(pdata->cen, ta_in ? 0 : 102 (data->usb_in ? 0 : 1)); 103 104 dev_dbg(data->dev, "TA(DC-IN) Charger %s.\n", ta_in ? 105 "Connected" : "Disconnected"); 106 107 old_type = data->psy.type; 108 109 if (data->ta_in) 110 data->psy.type = POWER_SUPPLY_TYPE_MAINS; 111 else if (data->usb_in) 112 data->psy.type = POWER_SUPPLY_TYPE_USB; 113 else 114 data->psy.type = POWER_SUPPLY_TYPE_BATTERY; 115 116 if (old_type != data->psy.type) 117 power_supply_changed(&data->psy); 118 119 return IRQ_HANDLED; 120} 121 122static irqreturn_t max8903_usbin(int irq, void *_data) 123{ 124 struct max8903_data *data = _data; 125 struct max8903_pdata *pdata = &data->pdata; 126 bool usb_in; 127 enum power_supply_type old_type; 128 129 usb_in = gpio_get_value(pdata->uok) ? false : true; 130 131 if (usb_in == data->usb_in) 132 return IRQ_HANDLED; 133 134 data->usb_in = usb_in; 135 136 /* Do not touch Current-Limit-Mode */ 137 138 /* Charger Enable / Disable (cen is negated) */ 139 if (pdata->cen) 140 gpio_set_value(pdata->cen, usb_in ? 0 : 141 (data->ta_in ? 0 : 1)); 142 143 dev_dbg(data->dev, "USB Charger %s.\n", usb_in ? 144 "Connected" : "Disconnected"); 145 146 old_type = data->psy.type; 147 148 if (data->ta_in) 149 data->psy.type = POWER_SUPPLY_TYPE_MAINS; 150 else if (data->usb_in) 151 data->psy.type = POWER_SUPPLY_TYPE_USB; 152 else 153 data->psy.type = POWER_SUPPLY_TYPE_BATTERY; 154 155 if (old_type != data->psy.type) 156 power_supply_changed(&data->psy); 157 158 return IRQ_HANDLED; 159} 160 161static irqreturn_t max8903_fault(int irq, void *_data) 162{ 163 struct max8903_data *data = _data; 164 struct max8903_pdata *pdata = &data->pdata; 165 bool fault; 166 167 fault = gpio_get_value(pdata->flt) ? false : true; 168 169 if (fault == data->fault) 170 return IRQ_HANDLED; 171 172 data->fault = fault; 173 174 if (fault) 175 dev_err(data->dev, "Charger suffers a fault and stops.\n"); 176 else 177 dev_err(data->dev, "Charger recovered from a fault.\n"); 178 179 return IRQ_HANDLED; 180} 181 182static __devinit int max8903_probe(struct platform_device *pdev) 183{ 184 struct max8903_data *data; 185 struct device *dev = &pdev->dev; 186 struct max8903_pdata *pdata = pdev->dev.platform_data; 187 int ret = 0; 188 int gpio; 189 int ta_in = 0; 190 int usb_in = 0; 191 192 data = kzalloc(sizeof(struct max8903_data), GFP_KERNEL); 193 if (data == NULL) { 194 dev_err(dev, "Cannot allocate memory.\n"); 195 return -ENOMEM; 196 } 197 memcpy(&data->pdata, pdata, sizeof(struct max8903_pdata)); 198 data->dev = dev; 199 platform_set_drvdata(pdev, data); 200 201 if (pdata->dc_valid == false && pdata->usb_valid == false) { 202 dev_err(dev, "No valid power sources.\n"); 203 ret = -EINVAL; 204 goto err; 205 } 206 207 if (pdata->dc_valid) { 208 if (pdata->dok && gpio_is_valid(pdata->dok) && 209 pdata->dcm && gpio_is_valid(pdata->dcm)) { 210 gpio = pdata->dok; /* PULL_UPed Interrupt */ 211 ta_in = gpio_get_value(gpio) ? 0 : 1; 212 213 gpio = pdata->dcm; /* Output */ 214 gpio_set_value(gpio, ta_in); 215 } else { 216 dev_err(dev, "When DC is wired, DOK and DCM should" 217 " be wired as well.\n"); 218 ret = -EINVAL; 219 goto err; 220 } 221 } else { 222 if (pdata->dcm) { 223 if (gpio_is_valid(pdata->dcm)) 224 gpio_set_value(pdata->dcm, 0); 225 else { 226 dev_err(dev, "Invalid pin: dcm.\n"); 227 ret = -EINVAL; 228 goto err; 229 } 230 } 231 } 232 233 if (pdata->usb_valid) { 234 if (pdata->uok && gpio_is_valid(pdata->uok)) { 235 gpio = pdata->uok; 236 usb_in = gpio_get_value(gpio) ? 0 : 1; 237 } else { 238 dev_err(dev, "When USB is wired, UOK should be wired." 239 "as well.\n"); 240 ret = -EINVAL; 241 goto err; 242 } 243 } 244 245 if (pdata->cen) { 246 if (gpio_is_valid(pdata->cen)) { 247 gpio_set_value(pdata->cen, (ta_in || usb_in) ? 0 : 1); 248 } else { 249 dev_err(dev, "Invalid pin: cen.\n"); 250 ret = -EINVAL; 251 goto err; 252 } 253 } 254 255 if (pdata->chg) { 256 if (!gpio_is_valid(pdata->chg)) { 257 dev_err(dev, "Invalid pin: chg.\n"); 258 ret = -EINVAL; 259 goto err; 260 } 261 } 262 263 if (pdata->flt) { 264 if (!gpio_is_valid(pdata->flt)) { 265 dev_err(dev, "Invalid pin: flt.\n"); 266 ret = -EINVAL; 267 goto err; 268 } 269 } 270 271 if (pdata->usus) { 272 if (!gpio_is_valid(pdata->usus)) { 273 dev_err(dev, "Invalid pin: usus.\n"); 274 ret = -EINVAL; 275 goto err; 276 } 277 } 278 279 data->fault = false; 280 data->ta_in = ta_in; 281 data->usb_in = usb_in; 282 283 data->psy.name = "max8903_charger"; 284 data->psy.type = (ta_in) ? POWER_SUPPLY_TYPE_MAINS : 285 ((usb_in) ? POWER_SUPPLY_TYPE_USB : 286 POWER_SUPPLY_TYPE_BATTERY); 287 data->psy.get_property = max8903_get_property; 288 data->psy.properties = max8903_charger_props; 289 data->psy.num_properties = ARRAY_SIZE(max8903_charger_props); 290 291 ret = power_supply_register(dev, &data->psy); 292 if (ret) { 293 dev_err(dev, "failed: power supply register.\n"); 294 goto err; 295 } 296 297 if (pdata->dc_valid) { 298 ret = request_threaded_irq(gpio_to_irq(pdata->dok), 299 NULL, max8903_dcin, 300 IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING, 301 "MAX8903 DC IN", data); 302 if (ret) { 303 dev_err(dev, "Cannot request irq %d for DC (%d)\n", 304 gpio_to_irq(pdata->dok), ret); 305 goto err_psy; 306 } 307 } 308 309 if (pdata->usb_valid) { 310 ret = request_threaded_irq(gpio_to_irq(pdata->uok), 311 NULL, max8903_usbin, 312 IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING, 313 "MAX8903 USB IN", data); 314 if (ret) { 315 dev_err(dev, "Cannot request irq %d for USB (%d)\n", 316 gpio_to_irq(pdata->uok), ret); 317 goto err_dc_irq; 318 } 319 } 320 321 if (pdata->flt) { 322 ret = request_threaded_irq(gpio_to_irq(pdata->flt), 323 NULL, max8903_fault, 324 IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING, 325 "MAX8903 Fault", data); 326 if (ret) { 327 dev_err(dev, "Cannot request irq %d for Fault (%d)\n", 328 gpio_to_irq(pdata->flt), ret); 329 goto err_usb_irq; 330 } 331 } 332 333 return 0; 334 335err_usb_irq: 336 if (pdata->usb_valid) 337 free_irq(gpio_to_irq(pdata->uok), data); 338err_dc_irq: 339 if (pdata->dc_valid) 340 free_irq(gpio_to_irq(pdata->dok), data); 341err_psy: 342 power_supply_unregister(&data->psy); 343err: 344 kfree(data); 345 return ret; 346} 347 348static __devexit int max8903_remove(struct platform_device *pdev) 349{ 350 struct max8903_data *data = platform_get_drvdata(pdev); 351 352 if (data) { 353 struct max8903_pdata *pdata = &data->pdata; 354 355 if (pdata->flt) 356 free_irq(gpio_to_irq(pdata->flt), data); 357 if (pdata->usb_valid) 358 free_irq(gpio_to_irq(pdata->uok), data); 359 if (pdata->dc_valid) 360 free_irq(gpio_to_irq(pdata->dok), data); 361 power_supply_unregister(&data->psy); 362 kfree(data); 363 } 364 365 return 0; 366} 367 368static struct platform_driver max8903_driver = { 369 .probe = max8903_probe, 370 .remove = __devexit_p(max8903_remove), 371 .driver = { 372 .name = "max8903-charger", 373 .owner = THIS_MODULE, 374 }, 375}; 376 377static int __init max8903_init(void) 378{ 379 return platform_driver_register(&max8903_driver); 380} 381module_init(max8903_init); 382 383static void __exit max8903_exit(void) 384{ 385 platform_driver_unregister(&max8903_driver); 386} 387module_exit(max8903_exit); 388 389MODULE_LICENSE("GPL"); 390MODULE_DESCRIPTION("MAX8903 Charger Driver"); 391MODULE_AUTHOR("MyungJoo Ham <myungjoo.ham@samsung.com>"); 392MODULE_ALIAS("max8903-charger"); 393