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 return -EINVAL; 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 return -EINVAL; 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 return -EINVAL; 227 } 228 } 229 } 230 231 if (pdata->usb_valid) { 232 if (pdata->uok && gpio_is_valid(pdata->uok)) { 233 gpio = pdata->uok; 234 usb_in = gpio_get_value(gpio) ? 0 : 1; 235 } else { 236 dev_err(dev, "When USB is wired, UOK should be wired." 237 "as well.\n"); 238 return -EINVAL; 239 } 240 } 241 242 if (pdata->cen) { 243 if (gpio_is_valid(pdata->cen)) { 244 gpio_set_value(pdata->cen, (ta_in || usb_in) ? 0 : 1); 245 } else { 246 dev_err(dev, "Invalid pin: cen.\n"); 247 return -EINVAL; 248 } 249 } 250 251 if (pdata->chg) { 252 if (!gpio_is_valid(pdata->chg)) { 253 dev_err(dev, "Invalid pin: chg.\n"); 254 return -EINVAL; 255 } 256 } 257 258 if (pdata->flt) { 259 if (!gpio_is_valid(pdata->flt)) { 260 dev_err(dev, "Invalid pin: flt.\n"); 261 return -EINVAL; 262 } 263 } 264 265 if (pdata->usus) { 266 if (!gpio_is_valid(pdata->usus)) { 267 dev_err(dev, "Invalid pin: usus.\n"); 268 return -EINVAL; 269 } 270 } 271 272 data->fault = false; 273 data->ta_in = ta_in; 274 data->usb_in = usb_in; 275 276 data->psy_desc.name = "max8903_charger"; 277 data->psy_desc.type = (ta_in) ? POWER_SUPPLY_TYPE_MAINS : 278 ((usb_in) ? POWER_SUPPLY_TYPE_USB : 279 POWER_SUPPLY_TYPE_BATTERY); 280 data->psy_desc.get_property = max8903_get_property; 281 data->psy_desc.properties = max8903_charger_props; 282 data->psy_desc.num_properties = ARRAY_SIZE(max8903_charger_props); 283 284 psy_cfg.drv_data = data; 285 286 data->psy = devm_power_supply_register(dev, &data->psy_desc, &psy_cfg); 287 if (IS_ERR(data->psy)) { 288 dev_err(dev, "failed: power supply register.\n"); 289 return PTR_ERR(data->psy); 290 } 291 292 if (pdata->dc_valid) { 293 ret = devm_request_threaded_irq(dev, gpio_to_irq(pdata->dok), 294 NULL, max8903_dcin, 295 IRQF_TRIGGER_FALLING | 296 IRQF_TRIGGER_RISING, 297 "MAX8903 DC IN", data); 298 if (ret) { 299 dev_err(dev, "Cannot request irq %d for DC (%d)\n", 300 gpio_to_irq(pdata->dok), ret); 301 return ret; 302 } 303 } 304 305 if (pdata->usb_valid) { 306 ret = devm_request_threaded_irq(dev, gpio_to_irq(pdata->uok), 307 NULL, max8903_usbin, 308 IRQF_TRIGGER_FALLING | 309 IRQF_TRIGGER_RISING, 310 "MAX8903 USB IN", data); 311 if (ret) { 312 dev_err(dev, "Cannot request irq %d for USB (%d)\n", 313 gpio_to_irq(pdata->uok), ret); 314 return ret; 315 } 316 } 317 318 if (pdata->flt) { 319 ret = devm_request_threaded_irq(dev, gpio_to_irq(pdata->flt), 320 NULL, max8903_fault, 321 IRQF_TRIGGER_FALLING | 322 IRQF_TRIGGER_RISING, 323 "MAX8903 Fault", data); 324 if (ret) { 325 dev_err(dev, "Cannot request irq %d for Fault (%d)\n", 326 gpio_to_irq(pdata->flt), ret); 327 return ret; 328 } 329 } 330 331 return 0; 332} 333 334static struct platform_driver max8903_driver = { 335 .probe = max8903_probe, 336 .driver = { 337 .name = "max8903-charger", 338 }, 339}; 340 341module_platform_driver(max8903_driver); 342 343MODULE_LICENSE("GPL"); 344MODULE_DESCRIPTION("MAX8903 Charger Driver"); 345MODULE_AUTHOR("MyungJoo Ham <myungjoo.ham@samsung.com>"); 346MODULE_ALIAS("platform:max8903-charger"); 347