max8903_charger.c 11.2 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
/*
 * max8903_charger.c - Maxim 8903 USB/Adapter Charger Driver
 *
 * Copyright (C) 2011 Samsung Electronics
 * MyungJoo Ham <myungjoo.ham@samsung.com>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 *
 */

#include <linux/gpio.h>
#include <linux/interrupt.h>
25
#include <linux/module.h>
26 27 28
#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/of_gpio.h>
29 30 31 32 33 34
#include <linux/slab.h>
#include <linux/power_supply.h>
#include <linux/platform_device.h>
#include <linux/power/max8903_charger.h>

struct max8903_data {
35
	struct max8903_pdata *pdata;
36
	struct device *dev;
37 38
	struct power_supply *psy;
	struct power_supply_desc psy_desc;
39 40 41 42 43 44 45 46 47 48 49 50 51 52 53
	bool fault;
	bool usb_in;
	bool ta_in;
};

static enum power_supply_property max8903_charger_props[] = {
	POWER_SUPPLY_PROP_STATUS, /* Charger status output */
	POWER_SUPPLY_PROP_ONLINE, /* External power source */
	POWER_SUPPLY_PROP_HEALTH, /* Fault or OK */
};

static int max8903_get_property(struct power_supply *psy,
		enum power_supply_property psp,
		union power_supply_propval *val)
{
54
	struct max8903_data *data = power_supply_get_drvdata(psy);
55 56 57 58

	switch (psp) {
	case POWER_SUPPLY_PROP_STATUS:
		val->intval = POWER_SUPPLY_STATUS_UNKNOWN;
59
		if (gpio_is_valid(data->pdata->chg)) {
60
			if (gpio_get_value(data->pdata->chg) == 0)
61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80
				val->intval = POWER_SUPPLY_STATUS_CHARGING;
			else if (data->usb_in || data->ta_in)
				val->intval = POWER_SUPPLY_STATUS_NOT_CHARGING;
			else
				val->intval = POWER_SUPPLY_STATUS_DISCHARGING;
		}
		break;
	case POWER_SUPPLY_PROP_ONLINE:
		val->intval = 0;
		if (data->usb_in || data->ta_in)
			val->intval = 1;
		break;
	case POWER_SUPPLY_PROP_HEALTH:
		val->intval = POWER_SUPPLY_HEALTH_GOOD;
		if (data->fault)
			val->intval = POWER_SUPPLY_HEALTH_UNSPEC_FAILURE;
		break;
	default:
		return -EINVAL;
	}
81

82 83 84 85 86 87
	return 0;
}

static irqreturn_t max8903_dcin(int irq, void *_data)
{
	struct max8903_data *data = _data;
88
	struct max8903_pdata *pdata = data->pdata;
89 90 91 92 93 94 95 96 97 98 99
	bool ta_in;
	enum power_supply_type old_type;

	ta_in = gpio_get_value(pdata->dok) ? false : true;

	if (ta_in == data->ta_in)
		return IRQ_HANDLED;

	data->ta_in = ta_in;

	/* Set Current-Limit-Mode 1:DC 0:USB */
100
	if (gpio_is_valid(pdata->dcm))
101 102 103
		gpio_set_value(pdata->dcm, ta_in ? 1 : 0);

	/* Charger Enable / Disable (cen is negated) */
104
	if (gpio_is_valid(pdata->cen))
105 106 107 108 109 110
		gpio_set_value(pdata->cen, ta_in ? 0 :
				(data->usb_in ? 0 : 1));

	dev_dbg(data->dev, "TA(DC-IN) Charger %s.\n", ta_in ?
			"Connected" : "Disconnected");

111
	old_type = data->psy_desc.type;
112 113

	if (data->ta_in)
114
		data->psy_desc.type = POWER_SUPPLY_TYPE_MAINS;
115
	else if (data->usb_in)
116
		data->psy_desc.type = POWER_SUPPLY_TYPE_USB;
117
	else
118
		data->psy_desc.type = POWER_SUPPLY_TYPE_BATTERY;
119

120 121
	if (old_type != data->psy_desc.type)
		power_supply_changed(data->psy);
122 123 124 125 126 127 128

	return IRQ_HANDLED;
}

static irqreturn_t max8903_usbin(int irq, void *_data)
{
	struct max8903_data *data = _data;
129
	struct max8903_pdata *pdata = data->pdata;
130 131 132 133 134 135 136 137 138 139 140 141 142
	bool usb_in;
	enum power_supply_type old_type;

	usb_in = gpio_get_value(pdata->uok) ? false : true;

	if (usb_in == data->usb_in)
		return IRQ_HANDLED;

	data->usb_in = usb_in;

	/* Do not touch Current-Limit-Mode */

	/* Charger Enable / Disable (cen is negated) */
143
	if (gpio_is_valid(pdata->cen))
144 145 146 147 148 149
		gpio_set_value(pdata->cen, usb_in ? 0 :
				(data->ta_in ? 0 : 1));

	dev_dbg(data->dev, "USB Charger %s.\n", usb_in ?
			"Connected" : "Disconnected");

150
	old_type = data->psy_desc.type;
151 152

	if (data->ta_in)
153
		data->psy_desc.type = POWER_SUPPLY_TYPE_MAINS;
154
	else if (data->usb_in)
155
		data->psy_desc.type = POWER_SUPPLY_TYPE_USB;
156
	else
157
		data->psy_desc.type = POWER_SUPPLY_TYPE_BATTERY;
158

159 160
	if (old_type != data->psy_desc.type)
		power_supply_changed(data->psy);
161 162 163 164 165 166 167

	return IRQ_HANDLED;
}

static irqreturn_t max8903_fault(int irq, void *_data)
{
	struct max8903_data *data = _data;
168
	struct max8903_pdata *pdata = data->pdata;
169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185
	bool fault;

	fault = gpio_get_value(pdata->flt) ? false : true;

	if (fault == data->fault)
		return IRQ_HANDLED;

	data->fault = fault;

	if (fault)
		dev_err(data->dev, "Charger suffers a fault and stops.\n");
	else
		dev_err(data->dev, "Charger recovered from a fault.\n");

	return IRQ_HANDLED;
}

186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235
static struct max8903_pdata *max8903_parse_dt_data(struct device *dev)
{
	struct device_node *np = dev->of_node;
	struct max8903_pdata *pdata = NULL;

	if (!np)
		return NULL;

	pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL);
	if (!pdata)
		return NULL;

	pdata->dc_valid = false;
	pdata->usb_valid = false;

	pdata->cen = of_get_named_gpio(np, "cen-gpios", 0);
	if (!gpio_is_valid(pdata->cen))
		pdata->cen = -EINVAL;

	pdata->chg = of_get_named_gpio(np, "chg-gpios", 0);
	if (!gpio_is_valid(pdata->chg))
		pdata->chg = -EINVAL;

	pdata->flt = of_get_named_gpio(np, "flt-gpios", 0);
	if (!gpio_is_valid(pdata->flt))
		pdata->flt = -EINVAL;

	pdata->usus = of_get_named_gpio(np, "usus-gpios", 0);
	if (!gpio_is_valid(pdata->usus))
		pdata->usus = -EINVAL;

	pdata->dcm = of_get_named_gpio(np, "dcm-gpios", 0);
	if (!gpio_is_valid(pdata->dcm))
		pdata->dcm = -EINVAL;

	pdata->dok = of_get_named_gpio(np, "dok-gpios", 0);
	if (!gpio_is_valid(pdata->dok))
		pdata->dok = -EINVAL;
	else
		pdata->dc_valid = true;

	pdata->uok = of_get_named_gpio(np, "uok-gpios", 0);
	if (!gpio_is_valid(pdata->uok))
		pdata->uok = -EINVAL;
	else
		pdata->usb_valid = true;

	return pdata;
}

C
Chris Lapa 已提交
236
static int max8903_setup_gpios(struct platform_device *pdev)
237
{
C
Chris Lapa 已提交
238
	struct max8903_data *data = platform_get_drvdata(pdev);
239 240 241 242 243 244 245 246
	struct device *dev = &pdev->dev;
	struct max8903_pdata *pdata = pdev->dev.platform_data;
	int ret = 0;
	int gpio;
	int ta_in = 0;
	int usb_in = 0;

	if (pdata->dc_valid) {
247
		if (gpio_is_valid(pdata->dok)) {
C
Chris Lapa 已提交
248 249 250 251 252 253 254 255 256
			ret = devm_gpio_request(dev, pdata->dok,
						data->psy_desc.name);
			if (ret) {
				dev_err(dev,
					"Failed GPIO request for dok: %d err %d\n",
					pdata->dok, ret);
				return ret;
			}

257 258
			gpio = pdata->dok; /* PULL_UPed Interrupt */
			ta_in = gpio_get_value(gpio) ? 0 : 1;
259 260 261 262 263
		} else {
			dev_err(dev, "When DC is wired, DOK should be wired as well.\n");
			return -EINVAL;
		}
	}
264

265 266 267 268 269 270 271
	if (gpio_is_valid(pdata->dcm)) {
		ret = devm_gpio_request(dev, pdata->dcm, data->psy_desc.name);
		if (ret) {
			dev_err(dev,
				"Failed GPIO request for dcm: %d err %d\n",
				pdata->dcm, ret);
			return ret;
272
		}
273 274 275

		gpio = pdata->dcm; /* Output */
		gpio_set_value(gpio, ta_in);
276 277 278
	}

	if (pdata->usb_valid) {
279
		if (gpio_is_valid(pdata->uok)) {
C
Chris Lapa 已提交
280 281 282 283 284 285 286 287 288
			ret = devm_gpio_request(dev, pdata->uok,
						data->psy_desc.name);
			if (ret) {
				dev_err(dev,
					"Failed GPIO request for uok: %d err %d\n",
					pdata->uok, ret);
				return ret;
			}

289 290 291 292 293
			gpio = pdata->uok;
			usb_in = gpio_get_value(gpio) ? 0 : 1;
		} else {
			dev_err(dev, "When USB is wired, UOK should be wired."
					"as well.\n");
294
			return -EINVAL;
295 296 297
		}
	}

298 299 300 301 302 303 304
	if (gpio_is_valid(pdata->cen)) {
		ret = devm_gpio_request(dev, pdata->cen, data->psy_desc.name);
		if (ret) {
			dev_err(dev,
				"Failed GPIO request for cen: %d err %d\n",
				pdata->cen, ret);
			return ret;
305
		}
306 307

		gpio_set_value(pdata->cen, (ta_in || usb_in) ? 0 : 1);
308 309
	}

310 311 312 313 314 315 316
	if (gpio_is_valid(pdata->chg)) {
		ret = devm_gpio_request(dev, pdata->chg, data->psy_desc.name);
		if (ret) {
			dev_err(dev,
				"Failed GPIO request for chg: %d err %d\n",
				pdata->chg, ret);
			return ret;
317 318 319
		}
	}

320 321 322 323 324 325 326
	if (gpio_is_valid(pdata->flt)) {
		ret = devm_gpio_request(dev, pdata->flt, data->psy_desc.name);
		if (ret) {
			dev_err(dev,
				"Failed GPIO request for flt: %d err %d\n",
				pdata->flt, ret);
			return ret;
327 328 329
		}
	}

330 331 332 333 334 335 336
	if (gpio_is_valid(pdata->usus)) {
		ret = devm_gpio_request(dev, pdata->usus, data->psy_desc.name);
		if (ret) {
			dev_err(dev,
				"Failed GPIO request for usus: %d err %d\n",
				pdata->usus, ret);
			return ret;
337 338 339 340 341 342 343
		}
	}

	data->fault = false;
	data->ta_in = ta_in;
	data->usb_in = usb_in;

C
Chris Lapa 已提交
344 345 346 347 348 349 350 351 352 353 354 355
	return 0;
}

static int max8903_probe(struct platform_device *pdev)
{
	struct max8903_data *data;
	struct device *dev = &pdev->dev;
	struct max8903_pdata *pdata = pdev->dev.platform_data;
	struct power_supply_config psy_cfg = {};
	int ret = 0;

	data = devm_kzalloc(dev, sizeof(struct max8903_data), GFP_KERNEL);
356
	if (!data)
C
Chris Lapa 已提交
357 358
		return -ENOMEM;

359 360 361 362 363 364 365 366 367 368
	if (IS_ENABLED(CONFIG_OF) && !pdata && dev->of_node)
		pdata = max8903_parse_dt_data(dev);

	if (!pdata) {
		dev_err(dev, "No platform data.\n");
		return -EINVAL;
	}

	pdev->dev.platform_data = pdata;
	data->pdata = pdata;
C
Chris Lapa 已提交
369 370 371 372 373 374 375 376 377 378 379 380
	data->dev = dev;
	platform_set_drvdata(pdev, data);

	if (pdata->dc_valid == false && pdata->usb_valid == false) {
		dev_err(dev, "No valid power sources.\n");
		return -EINVAL;
	}

	ret = max8903_setup_gpios(pdev);
	if (ret)
		return ret;

381
	data->psy_desc.name = "max8903_charger";
C
Chris Lapa 已提交
382 383
	data->psy_desc.type = (data->ta_in) ? POWER_SUPPLY_TYPE_MAINS :
			((data->usb_in) ? POWER_SUPPLY_TYPE_USB :
384
			 POWER_SUPPLY_TYPE_BATTERY);
385 386 387
	data->psy_desc.get_property = max8903_get_property;
	data->psy_desc.properties = max8903_charger_props;
	data->psy_desc.num_properties = ARRAY_SIZE(max8903_charger_props);
388

389
	psy_cfg.of_node = dev->of_node;
390 391
	psy_cfg.drv_data = data;

392
	data->psy = devm_power_supply_register(dev, &data->psy_desc, &psy_cfg);
393
	if (IS_ERR(data->psy)) {
394
		dev_err(dev, "failed: power supply register.\n");
395
		return PTR_ERR(data->psy);
396 397 398
	}

	if (pdata->dc_valid) {
399
		ret = devm_request_threaded_irq(dev, gpio_to_irq(pdata->dok),
400 401 402 403
					NULL, max8903_dcin,
					IRQF_TRIGGER_FALLING |
					IRQF_TRIGGER_RISING | IRQF_ONESHOT,
					"MAX8903 DC IN", data);
404 405 406
		if (ret) {
			dev_err(dev, "Cannot request irq %d for DC (%d)\n",
					gpio_to_irq(pdata->dok), ret);
407
			return ret;
408 409 410 411
		}
	}

	if (pdata->usb_valid) {
412
		ret = devm_request_threaded_irq(dev, gpio_to_irq(pdata->uok),
413 414 415 416
					NULL, max8903_usbin,
					IRQF_TRIGGER_FALLING |
					IRQF_TRIGGER_RISING | IRQF_ONESHOT,
					"MAX8903 USB IN", data);
417 418 419
		if (ret) {
			dev_err(dev, "Cannot request irq %d for USB (%d)\n",
					gpio_to_irq(pdata->uok), ret);
420
			return ret;
421 422 423
		}
	}

424
	if (gpio_is_valid(pdata->flt)) {
425
		ret = devm_request_threaded_irq(dev, gpio_to_irq(pdata->flt),
426 427 428 429
					NULL, max8903_fault,
					IRQF_TRIGGER_FALLING |
					IRQF_TRIGGER_RISING | IRQF_ONESHOT,
					"MAX8903 Fault", data);
430 431 432
		if (ret) {
			dev_err(dev, "Cannot request irq %d for Fault (%d)\n",
					gpio_to_irq(pdata->flt), ret);
433
			return ret;
434 435 436 437 438 439
		}
	}

	return 0;
}

440 441 442 443 444 445
static const struct of_device_id max8903_match_ids[] = {
	{ .compatible = "maxim,max8903", },
	{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, max8903_match_ids);

446 447 448 449
static struct platform_driver max8903_driver = {
	.probe	= max8903_probe,
	.driver = {
		.name	= "max8903-charger",
450
		.of_match_table = max8903_match_ids
451 452 453
	},
};

454
module_platform_driver(max8903_driver);
455 456 457 458

MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("MAX8903 Charger Driver");
MODULE_AUTHOR("MyungJoo Ham <myungjoo.ham@samsung.com>");
459
MODULE_ALIAS("platform:max8903-charger");