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