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 struct power_supply_desc psy_desc; 36 bool fault; 37 bool usb_in; 38 bool ta_in; 39}; 40 41static enum power_supply_property max8903_charger_props[] = { 42 POWER_SUPPLY_PROP_STATUS, /* Charger status output */ 43 POWER_SUPPLY_PROP_ONLINE, /* External power source */ 44 POWER_SUPPLY_PROP_HEALTH, /* Fault or OK */ 45}; 46 47static int max8903_get_property(struct power_supply *psy, 48 enum power_supply_property psp, 49 union power_supply_propval *val) 50{ 51 struct max8903_data *data = power_supply_get_drvdata(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_desc.type; 108 109 if (data->ta_in) 110 data->psy_desc.type = POWER_SUPPLY_TYPE_MAINS; 111 else if (data->usb_in) 112 data->psy_desc.type = POWER_SUPPLY_TYPE_USB; 113 else 114 data->psy_desc.type = POWER_SUPPLY_TYPE_BATTERY; 115 116 if (old_type != data->psy_desc.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_desc.type; 147 148 if (data->ta_in) 149 data->psy_desc.type = POWER_SUPPLY_TYPE_MAINS; 150 else if (data->usb_in) 151 data->psy_desc.type = POWER_SUPPLY_TYPE_USB; 152 else 153 data->psy_desc.type = POWER_SUPPLY_TYPE_BATTERY; 154 155 if (old_type != data->psy_desc.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 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 struct power_supply_config psy_cfg = {}; 188 int ret = 0; 189 int gpio; 190 int ta_in = 0; 191 int usb_in = 0; 192 193 data = devm_kzalloc(dev, sizeof(struct max8903_data), GFP_KERNEL); 194 if (data == NULL) { 195 dev_err(dev, "Cannot allocate memory.\n"); 196 return -ENOMEM; 197 } 198 memcpy(&data->pdata, pdata, sizeof(struct max8903_pdata)); 199 data->dev = dev; 200 platform_set_drvdata(pdev, data); 201 202 if (pdata->dc_valid == false && pdata->usb_valid == false) { 203 dev_err(dev, "No valid power sources.\n"); 204 ret = -EINVAL; 205 goto err; 206 } 207 208 if (pdata->dc_valid) { 209 if (pdata->dok && gpio_is_valid(pdata->dok) && 210 pdata->dcm && gpio_is_valid(pdata->dcm)) { 211 gpio = pdata->dok; /* PULL_UPed Interrupt */ 212 ta_in = gpio_get_value(gpio) ? 0 : 1; 213 214 gpio = pdata->dcm; /* Output */ 215 gpio_set_value(gpio, ta_in); 216 } else { 217 dev_err(dev, "When DC is wired, DOK and DCM should" 218 " be wired as well.\n"); 219 ret = -EINVAL; 220 goto err; 221 } 222 } else { 223 if (pdata->dcm) { 224 if (gpio_is_valid(pdata->dcm)) 225 gpio_set_value(pdata->dcm, 0); 226 else { 227 dev_err(dev, "Invalid pin: dcm.\n"); 228 ret = -EINVAL; 229 goto err; 230 } 231 } 232 } 233 234 if (pdata->usb_valid) { 235 if (pdata->uok && gpio_is_valid(pdata->uok)) { 236 gpio = pdata->uok; 237 usb_in = gpio_get_value(gpio) ? 0 : 1; 238 } else { 239 dev_err(dev, "When USB is wired, UOK should be wired." 240 "as well.\n"); 241 ret = -EINVAL; 242 goto err; 243 } 244 } 245 246 if (pdata->cen) { 247 if (gpio_is_valid(pdata->cen)) { 248 gpio_set_value(pdata->cen, (ta_in || usb_in) ? 0 : 1); 249 } else { 250 dev_err(dev, "Invalid pin: cen.\n"); 251 ret = -EINVAL; 252 goto err; 253 } 254 } 255 256 if (pdata->chg) { 257 if (!gpio_is_valid(pdata->chg)) { 258 dev_err(dev, "Invalid pin: chg.\n"); 259 ret = -EINVAL; 260 goto err; 261 } 262 } 263 264 if (pdata->flt) { 265 if (!gpio_is_valid(pdata->flt)) { 266 dev_err(dev, "Invalid pin: flt.\n"); 267 ret = -EINVAL; 268 goto err; 269 } 270 } 271 272 if (pdata->usus) { 273 if (!gpio_is_valid(pdata->usus)) { 274 dev_err(dev, "Invalid pin: usus.\n"); 275 ret = -EINVAL; 276 goto err; 277 } 278 } 279 280 data->fault = false; 281 data->ta_in = ta_in; 282 data->usb_in = usb_in; 283 284 data->psy_desc.name = "max8903_charger"; 285 data->psy_desc.type = (ta_in) ? POWER_SUPPLY_TYPE_MAINS : 286 ((usb_in) ? POWER_SUPPLY_TYPE_USB : 287 POWER_SUPPLY_TYPE_BATTERY); 288 data->psy_desc.get_property = max8903_get_property; 289 data->psy_desc.properties = max8903_charger_props; 290 data->psy_desc.num_properties = ARRAY_SIZE(max8903_charger_props); 291 292 psy_cfg.drv_data = data; 293 294 data->psy = power_supply_register(dev, &data->psy_desc, &psy_cfg); 295 if (IS_ERR(data->psy)) { 296 dev_err(dev, "failed: power supply register.\n"); 297 ret = PTR_ERR(data->psy); 298 goto err; 299 } 300 301 if (pdata->dc_valid) { 302 ret = request_threaded_irq(gpio_to_irq(pdata->dok), 303 NULL, max8903_dcin, 304 IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING, 305 "MAX8903 DC IN", data); 306 if (ret) { 307 dev_err(dev, "Cannot request irq %d for DC (%d)\n", 308 gpio_to_irq(pdata->dok), ret); 309 goto err_psy; 310 } 311 } 312 313 if (pdata->usb_valid) { 314 ret = request_threaded_irq(gpio_to_irq(pdata->uok), 315 NULL, max8903_usbin, 316 IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING, 317 "MAX8903 USB IN", data); 318 if (ret) { 319 dev_err(dev, "Cannot request irq %d for USB (%d)\n", 320 gpio_to_irq(pdata->uok), ret); 321 goto err_dc_irq; 322 } 323 } 324 325 if (pdata->flt) { 326 ret = request_threaded_irq(gpio_to_irq(pdata->flt), 327 NULL, max8903_fault, 328 IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING, 329 "MAX8903 Fault", data); 330 if (ret) { 331 dev_err(dev, "Cannot request irq %d for Fault (%d)\n", 332 gpio_to_irq(pdata->flt), ret); 333 goto err_usb_irq; 334 } 335 } 336 337 return 0; 338 339err_usb_irq: 340 if (pdata->usb_valid) 341 free_irq(gpio_to_irq(pdata->uok), data); 342err_dc_irq: 343 if (pdata->dc_valid) 344 free_irq(gpio_to_irq(pdata->dok), data); 345err_psy: 346 power_supply_unregister(data->psy); 347err: 348 return ret; 349} 350 351static int max8903_remove(struct platform_device *pdev) 352{ 353 struct max8903_data *data = platform_get_drvdata(pdev); 354 355 if (data) { 356 struct max8903_pdata *pdata = &data->pdata; 357 358 if (pdata->flt) 359 free_irq(gpio_to_irq(pdata->flt), data); 360 if (pdata->usb_valid) 361 free_irq(gpio_to_irq(pdata->uok), data); 362 if (pdata->dc_valid) 363 free_irq(gpio_to_irq(pdata->dok), data); 364 power_supply_unregister(data->psy); 365 } 366 367 return 0; 368} 369 370static struct platform_driver max8903_driver = { 371 .probe = max8903_probe, 372 .remove = max8903_remove, 373 .driver = { 374 .name = "max8903-charger", 375 }, 376}; 377 378module_platform_driver(max8903_driver); 379 380MODULE_LICENSE("GPL"); 381MODULE_DESCRIPTION("MAX8903 Charger Driver"); 382MODULE_AUTHOR("MyungJoo Ham <myungjoo.ham@samsung.com>"); 383MODULE_ALIAS("platform:max8903-charger"); 384