blob: fdc73d68615344e045a1cd70de86b3adf4447507 [file] [log] [blame]
MyungJoo Hamb14a9cc2011-03-29 10:10:16 +09001/*
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>
Paul Gortmaker7e6d62d2011-07-03 15:28:29 -040025#include <linux/module.h>
Chris Lapac5ed3302016-06-24 12:26:12 +100026#include <linux/of.h>
27#include <linux/of_device.h>
28#include <linux/of_gpio.h>
MyungJoo Hamb14a9cc2011-03-29 10:10:16 +090029#include <linux/slab.h>
30#include <linux/power_supply.h>
31#include <linux/platform_device.h>
32#include <linux/power/max8903_charger.h>
33
34struct max8903_data {
Chris Lapa0c3ae042016-06-24 12:26:07 +100035 struct max8903_pdata *pdata;
MyungJoo Hamb14a9cc2011-03-29 10:10:16 +090036 struct device *dev;
Krzysztof Kozlowski297d7162015-03-12 08:44:11 +010037 struct power_supply *psy;
38 struct power_supply_desc psy_desc;
MyungJoo Hamb14a9cc2011-03-29 10:10:16 +090039 bool fault;
40 bool usb_in;
41 bool ta_in;
42};
43
44static enum power_supply_property max8903_charger_props[] = {
45 POWER_SUPPLY_PROP_STATUS, /* Charger status output */
46 POWER_SUPPLY_PROP_ONLINE, /* External power source */
47 POWER_SUPPLY_PROP_HEALTH, /* Fault or OK */
48};
49
50static int max8903_get_property(struct power_supply *psy,
51 enum power_supply_property psp,
52 union power_supply_propval *val)
53{
Krzysztof Kozlowski297d7162015-03-12 08:44:11 +010054 struct max8903_data *data = power_supply_get_drvdata(psy);
MyungJoo Hamb14a9cc2011-03-29 10:10:16 +090055
56 switch (psp) {
57 case POWER_SUPPLY_PROP_STATUS:
58 val->intval = POWER_SUPPLY_STATUS_UNKNOWN;
Chris Lapa3525e5c2016-06-24 12:26:10 +100059 if (gpio_is_valid(data->pdata->chg)) {
Chris Lapa0c3ae042016-06-24 12:26:07 +100060 if (gpio_get_value(data->pdata->chg) == 0)
MyungJoo Hamb14a9cc2011-03-29 10:10:16 +090061 val->intval = POWER_SUPPLY_STATUS_CHARGING;
62 else if (data->usb_in || data->ta_in)
63 val->intval = POWER_SUPPLY_STATUS_NOT_CHARGING;
64 else
65 val->intval = POWER_SUPPLY_STATUS_DISCHARGING;
66 }
67 break;
68 case POWER_SUPPLY_PROP_ONLINE:
69 val->intval = 0;
70 if (data->usb_in || data->ta_in)
71 val->intval = 1;
72 break;
73 case POWER_SUPPLY_PROP_HEALTH:
74 val->intval = POWER_SUPPLY_HEALTH_GOOD;
75 if (data->fault)
76 val->intval = POWER_SUPPLY_HEALTH_UNSPEC_FAILURE;
77 break;
78 default:
79 return -EINVAL;
80 }
Chris Lapac5ed3302016-06-24 12:26:12 +100081
MyungJoo Hamb14a9cc2011-03-29 10:10:16 +090082 return 0;
83}
84
85static irqreturn_t max8903_dcin(int irq, void *_data)
86{
87 struct max8903_data *data = _data;
Chris Lapa0c3ae042016-06-24 12:26:07 +100088 struct max8903_pdata *pdata = data->pdata;
MyungJoo Hamb14a9cc2011-03-29 10:10:16 +090089 bool ta_in;
90 enum power_supply_type old_type;
91
92 ta_in = gpio_get_value(pdata->dok) ? false : true;
93
94 if (ta_in == data->ta_in)
95 return IRQ_HANDLED;
96
97 data->ta_in = ta_in;
98
99 /* Set Current-Limit-Mode 1:DC 0:USB */
Chris Lapa3525e5c2016-06-24 12:26:10 +1000100 if (gpio_is_valid(pdata->dcm))
MyungJoo Hamb14a9cc2011-03-29 10:10:16 +0900101 gpio_set_value(pdata->dcm, ta_in ? 1 : 0);
102
103 /* Charger Enable / Disable (cen is negated) */
Chris Lapa3525e5c2016-06-24 12:26:10 +1000104 if (gpio_is_valid(pdata->cen))
MyungJoo Hamb14a9cc2011-03-29 10:10:16 +0900105 gpio_set_value(pdata->cen, ta_in ? 0 :
106 (data->usb_in ? 0 : 1));
107
108 dev_dbg(data->dev, "TA(DC-IN) Charger %s.\n", ta_in ?
109 "Connected" : "Disconnected");
110
Krzysztof Kozlowski297d7162015-03-12 08:44:11 +0100111 old_type = data->psy_desc.type;
MyungJoo Hamb14a9cc2011-03-29 10:10:16 +0900112
113 if (data->ta_in)
Krzysztof Kozlowski297d7162015-03-12 08:44:11 +0100114 data->psy_desc.type = POWER_SUPPLY_TYPE_MAINS;
MyungJoo Hamb14a9cc2011-03-29 10:10:16 +0900115 else if (data->usb_in)
Krzysztof Kozlowski297d7162015-03-12 08:44:11 +0100116 data->psy_desc.type = POWER_SUPPLY_TYPE_USB;
MyungJoo Hamb14a9cc2011-03-29 10:10:16 +0900117 else
Krzysztof Kozlowski297d7162015-03-12 08:44:11 +0100118 data->psy_desc.type = POWER_SUPPLY_TYPE_BATTERY;
MyungJoo Hamb14a9cc2011-03-29 10:10:16 +0900119
Krzysztof Kozlowski297d7162015-03-12 08:44:11 +0100120 if (old_type != data->psy_desc.type)
121 power_supply_changed(data->psy);
MyungJoo Hamb14a9cc2011-03-29 10:10:16 +0900122
123 return IRQ_HANDLED;
124}
125
126static irqreturn_t max8903_usbin(int irq, void *_data)
127{
128 struct max8903_data *data = _data;
Chris Lapa0c3ae042016-06-24 12:26:07 +1000129 struct max8903_pdata *pdata = data->pdata;
MyungJoo Hamb14a9cc2011-03-29 10:10:16 +0900130 bool usb_in;
131 enum power_supply_type old_type;
132
133 usb_in = gpio_get_value(pdata->uok) ? false : true;
134
135 if (usb_in == data->usb_in)
136 return IRQ_HANDLED;
137
138 data->usb_in = usb_in;
139
140 /* Do not touch Current-Limit-Mode */
141
142 /* Charger Enable / Disable (cen is negated) */
Chris Lapa3525e5c2016-06-24 12:26:10 +1000143 if (gpio_is_valid(pdata->cen))
MyungJoo Hamb14a9cc2011-03-29 10:10:16 +0900144 gpio_set_value(pdata->cen, usb_in ? 0 :
145 (data->ta_in ? 0 : 1));
146
147 dev_dbg(data->dev, "USB Charger %s.\n", usb_in ?
148 "Connected" : "Disconnected");
149
Krzysztof Kozlowski297d7162015-03-12 08:44:11 +0100150 old_type = data->psy_desc.type;
MyungJoo Hamb14a9cc2011-03-29 10:10:16 +0900151
152 if (data->ta_in)
Krzysztof Kozlowski297d7162015-03-12 08:44:11 +0100153 data->psy_desc.type = POWER_SUPPLY_TYPE_MAINS;
MyungJoo Hamb14a9cc2011-03-29 10:10:16 +0900154 else if (data->usb_in)
Krzysztof Kozlowski297d7162015-03-12 08:44:11 +0100155 data->psy_desc.type = POWER_SUPPLY_TYPE_USB;
MyungJoo Hamb14a9cc2011-03-29 10:10:16 +0900156 else
Krzysztof Kozlowski297d7162015-03-12 08:44:11 +0100157 data->psy_desc.type = POWER_SUPPLY_TYPE_BATTERY;
MyungJoo Hamb14a9cc2011-03-29 10:10:16 +0900158
Krzysztof Kozlowski297d7162015-03-12 08:44:11 +0100159 if (old_type != data->psy_desc.type)
160 power_supply_changed(data->psy);
MyungJoo Hamb14a9cc2011-03-29 10:10:16 +0900161
162 return IRQ_HANDLED;
163}
164
165static irqreturn_t max8903_fault(int irq, void *_data)
166{
167 struct max8903_data *data = _data;
Chris Lapa0c3ae042016-06-24 12:26:07 +1000168 struct max8903_pdata *pdata = data->pdata;
MyungJoo Hamb14a9cc2011-03-29 10:10:16 +0900169 bool fault;
170
171 fault = gpio_get_value(pdata->flt) ? false : true;
172
173 if (fault == data->fault)
174 return IRQ_HANDLED;
175
176 data->fault = fault;
177
178 if (fault)
179 dev_err(data->dev, "Charger suffers a fault and stops.\n");
180 else
181 dev_err(data->dev, "Charger recovered from a fault.\n");
182
183 return IRQ_HANDLED;
184}
185
Chris Lapac5ed3302016-06-24 12:26:12 +1000186static struct max8903_pdata *max8903_parse_dt_data(struct device *dev)
187{
188 struct device_node *np = dev->of_node;
189 struct max8903_pdata *pdata = NULL;
190
191 if (!np)
192 return NULL;
193
194 pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL);
195 if (!pdata)
196 return NULL;
197
198 pdata->dc_valid = false;
199 pdata->usb_valid = false;
200
201 pdata->cen = of_get_named_gpio(np, "cen-gpios", 0);
202 if (!gpio_is_valid(pdata->cen))
203 pdata->cen = -EINVAL;
204
205 pdata->chg = of_get_named_gpio(np, "chg-gpios", 0);
206 if (!gpio_is_valid(pdata->chg))
207 pdata->chg = -EINVAL;
208
209 pdata->flt = of_get_named_gpio(np, "flt-gpios", 0);
210 if (!gpio_is_valid(pdata->flt))
211 pdata->flt = -EINVAL;
212
213 pdata->usus = of_get_named_gpio(np, "usus-gpios", 0);
214 if (!gpio_is_valid(pdata->usus))
215 pdata->usus = -EINVAL;
216
217 pdata->dcm = of_get_named_gpio(np, "dcm-gpios", 0);
218 if (!gpio_is_valid(pdata->dcm))
219 pdata->dcm = -EINVAL;
220
221 pdata->dok = of_get_named_gpio(np, "dok-gpios", 0);
222 if (!gpio_is_valid(pdata->dok))
223 pdata->dok = -EINVAL;
224 else
225 pdata->dc_valid = true;
226
227 pdata->uok = of_get_named_gpio(np, "uok-gpios", 0);
228 if (!gpio_is_valid(pdata->uok))
229 pdata->uok = -EINVAL;
230 else
231 pdata->usb_valid = true;
232
233 return pdata;
234}
235
Chris Lapa88a469b2016-06-24 12:26:09 +1000236static int max8903_setup_gpios(struct platform_device *pdev)
237{
238 struct max8903_data *data = platform_get_drvdata(pdev);
239 struct device *dev = &pdev->dev;
240 struct max8903_pdata *pdata = pdev->dev.platform_data;
241 int ret = 0;
242 int gpio;
243 int ta_in = 0;
244 int usb_in = 0;
245
246 if (pdata->dc_valid) {
Chris Lapa3525e5c2016-06-24 12:26:10 +1000247 if (gpio_is_valid(pdata->dok)) {
Chris Lapa88a469b2016-06-24 12:26:09 +1000248 ret = devm_gpio_request(dev, pdata->dok,
249 data->psy_desc.name);
250 if (ret) {
251 dev_err(dev,
252 "Failed GPIO request for dok: %d err %d\n",
253 pdata->dok, ret);
254 return ret;
255 }
256
257 gpio = pdata->dok; /* PULL_UPed Interrupt */
258 ta_in = gpio_get_value(gpio) ? 0 : 1;
259 } else {
260 dev_err(dev, "When DC is wired, DOK should be wired as well.\n");
261 return -EINVAL;
262 }
263 }
264
Chris Lapa3525e5c2016-06-24 12:26:10 +1000265 if (gpio_is_valid(pdata->dcm)) {
266 ret = devm_gpio_request(dev, pdata->dcm, data->psy_desc.name);
267 if (ret) {
268 dev_err(dev,
269 "Failed GPIO request for dcm: %d err %d\n",
270 pdata->dcm, ret);
271 return ret;
Chris Lapa88a469b2016-06-24 12:26:09 +1000272 }
Chris Lapa3525e5c2016-06-24 12:26:10 +1000273
274 gpio = pdata->dcm; /* Output */
275 gpio_set_value(gpio, ta_in);
Chris Lapa88a469b2016-06-24 12:26:09 +1000276 }
277
278 if (pdata->usb_valid) {
Chris Lapa3525e5c2016-06-24 12:26:10 +1000279 if (gpio_is_valid(pdata->uok)) {
Chris Lapa88a469b2016-06-24 12:26:09 +1000280 ret = devm_gpio_request(dev, pdata->uok,
281 data->psy_desc.name);
282 if (ret) {
283 dev_err(dev,
284 "Failed GPIO request for uok: %d err %d\n",
285 pdata->uok, ret);
286 return ret;
287 }
288
289 gpio = pdata->uok;
290 usb_in = gpio_get_value(gpio) ? 0 : 1;
291 } else {
292 dev_err(dev, "When USB is wired, UOK should be wired."
293 "as well.\n");
294 return -EINVAL;
295 }
296 }
297
Chris Lapa3525e5c2016-06-24 12:26:10 +1000298 if (gpio_is_valid(pdata->cen)) {
299 ret = devm_gpio_request(dev, pdata->cen, data->psy_desc.name);
300 if (ret) {
301 dev_err(dev,
302 "Failed GPIO request for cen: %d err %d\n",
303 pdata->cen, ret);
304 return ret;
305 }
Chris Lapa88a469b2016-06-24 12:26:09 +1000306
Chris Lapa3525e5c2016-06-24 12:26:10 +1000307 gpio_set_value(pdata->cen, (ta_in || usb_in) ? 0 : 1);
308 }
309
310 if (gpio_is_valid(pdata->chg)) {
311 ret = devm_gpio_request(dev, pdata->chg, data->psy_desc.name);
312 if (ret) {
313 dev_err(dev,
314 "Failed GPIO request for chg: %d err %d\n",
315 pdata->chg, ret);
316 return ret;
Chris Lapa88a469b2016-06-24 12:26:09 +1000317 }
318 }
319
Chris Lapa3525e5c2016-06-24 12:26:10 +1000320 if (gpio_is_valid(pdata->flt)) {
321 ret = devm_gpio_request(dev, pdata->flt, data->psy_desc.name);
322 if (ret) {
323 dev_err(dev,
324 "Failed GPIO request for flt: %d err %d\n",
325 pdata->flt, ret);
326 return ret;
Chris Lapa88a469b2016-06-24 12:26:09 +1000327 }
328 }
329
Chris Lapa3525e5c2016-06-24 12:26:10 +1000330 if (gpio_is_valid(pdata->usus)) {
331 ret = devm_gpio_request(dev, pdata->usus, data->psy_desc.name);
332 if (ret) {
333 dev_err(dev,
334 "Failed GPIO request for usus: %d err %d\n",
335 pdata->usus, ret);
336 return ret;
Chris Lapa88a469b2016-06-24 12:26:09 +1000337 }
338 }
339
340 data->fault = false;
341 data->ta_in = ta_in;
342 data->usb_in = usb_in;
343
344 return 0;
345}
346
Bill Pembertonc8afa642012-11-19 13:22:23 -0500347static int max8903_probe(struct platform_device *pdev)
MyungJoo Hamb14a9cc2011-03-29 10:10:16 +0900348{
349 struct max8903_data *data;
350 struct device *dev = &pdev->dev;
351 struct max8903_pdata *pdata = pdev->dev.platform_data;
Krzysztof Kozlowski297d7162015-03-12 08:44:11 +0100352 struct power_supply_config psy_cfg = {};
MyungJoo Hamb14a9cc2011-03-29 10:10:16 +0900353 int ret = 0;
MyungJoo Hamb14a9cc2011-03-29 10:10:16 +0900354
Jingoo Hanf3f66b32013-03-11 15:34:35 +0900355 data = devm_kzalloc(dev, sizeof(struct max8903_data), GFP_KERNEL);
Chris Lapae6518a42016-06-24 12:26:11 +1000356 if (!data)
MyungJoo Hamb14a9cc2011-03-29 10:10:16 +0900357 return -ENOMEM;
Chris Lapa0c3ae042016-06-24 12:26:07 +1000358
Chris Lapac5ed3302016-06-24 12:26:12 +1000359 if (IS_ENABLED(CONFIG_OF) && !pdata && dev->of_node)
360 pdata = max8903_parse_dt_data(dev);
361
362 if (!pdata) {
363 dev_err(dev, "No platform data.\n");
364 return -EINVAL;
365 }
366
367 pdev->dev.platform_data = pdata;
368 data->pdata = pdata;
MyungJoo Hamb14a9cc2011-03-29 10:10:16 +0900369 data->dev = dev;
370 platform_set_drvdata(pdev, data);
371
372 if (pdata->dc_valid == false && pdata->usb_valid == false) {
373 dev_err(dev, "No valid power sources.\n");
Vaishali Thakkar0df2dee2015-09-17 18:55:00 +0530374 return -EINVAL;
MyungJoo Hamb14a9cc2011-03-29 10:10:16 +0900375 }
376
Chris Lapa88a469b2016-06-24 12:26:09 +1000377 ret = max8903_setup_gpios(pdev);
378 if (ret)
379 return ret;
MyungJoo Hamb14a9cc2011-03-29 10:10:16 +0900380
Krzysztof Kozlowski297d7162015-03-12 08:44:11 +0100381 data->psy_desc.name = "max8903_charger";
Chris Lapa88a469b2016-06-24 12:26:09 +1000382 data->psy_desc.type = (data->ta_in) ? POWER_SUPPLY_TYPE_MAINS :
383 ((data->usb_in) ? POWER_SUPPLY_TYPE_USB :
MyungJoo Hamb14a9cc2011-03-29 10:10:16 +0900384 POWER_SUPPLY_TYPE_BATTERY);
Krzysztof Kozlowski297d7162015-03-12 08:44:11 +0100385 data->psy_desc.get_property = max8903_get_property;
386 data->psy_desc.properties = max8903_charger_props;
387 data->psy_desc.num_properties = ARRAY_SIZE(max8903_charger_props);
MyungJoo Hamb14a9cc2011-03-29 10:10:16 +0900388
Chris Lapac5ed3302016-06-24 12:26:12 +1000389 psy_cfg.of_node = dev->of_node;
Krzysztof Kozlowski297d7162015-03-12 08:44:11 +0100390 psy_cfg.drv_data = data;
391
Vaishali Thakkar0df2dee2015-09-17 18:55:00 +0530392 data->psy = devm_power_supply_register(dev, &data->psy_desc, &psy_cfg);
Krzysztof Kozlowski297d7162015-03-12 08:44:11 +0100393 if (IS_ERR(data->psy)) {
MyungJoo Hamb14a9cc2011-03-29 10:10:16 +0900394 dev_err(dev, "failed: power supply register.\n");
Vaishali Thakkar0df2dee2015-09-17 18:55:00 +0530395 return PTR_ERR(data->psy);
MyungJoo Hamb14a9cc2011-03-29 10:10:16 +0900396 }
397
398 if (pdata->dc_valid) {
Vaishali Thakkar0df2dee2015-09-17 18:55:00 +0530399 ret = devm_request_threaded_irq(dev, gpio_to_irq(pdata->dok),
Saurabh Sengard5fdfed2015-11-19 12:42:59 +0530400 NULL, max8903_dcin,
401 IRQF_TRIGGER_FALLING |
402 IRQF_TRIGGER_RISING | IRQF_ONESHOT,
403 "MAX8903 DC IN", data);
MyungJoo Hamb14a9cc2011-03-29 10:10:16 +0900404 if (ret) {
405 dev_err(dev, "Cannot request irq %d for DC (%d)\n",
406 gpio_to_irq(pdata->dok), ret);
Vaishali Thakkar0df2dee2015-09-17 18:55:00 +0530407 return ret;
MyungJoo Hamb14a9cc2011-03-29 10:10:16 +0900408 }
409 }
410
411 if (pdata->usb_valid) {
Vaishali Thakkar0df2dee2015-09-17 18:55:00 +0530412 ret = devm_request_threaded_irq(dev, gpio_to_irq(pdata->uok),
Saurabh Sengard5fdfed2015-11-19 12:42:59 +0530413 NULL, max8903_usbin,
414 IRQF_TRIGGER_FALLING |
415 IRQF_TRIGGER_RISING | IRQF_ONESHOT,
416 "MAX8903 USB IN", data);
MyungJoo Hamb14a9cc2011-03-29 10:10:16 +0900417 if (ret) {
418 dev_err(dev, "Cannot request irq %d for USB (%d)\n",
419 gpio_to_irq(pdata->uok), ret);
Vaishali Thakkar0df2dee2015-09-17 18:55:00 +0530420 return ret;
MyungJoo Hamb14a9cc2011-03-29 10:10:16 +0900421 }
422 }
423
Chris Lapa3525e5c2016-06-24 12:26:10 +1000424 if (gpio_is_valid(pdata->flt)) {
Vaishali Thakkar0df2dee2015-09-17 18:55:00 +0530425 ret = devm_request_threaded_irq(dev, gpio_to_irq(pdata->flt),
Saurabh Sengard5fdfed2015-11-19 12:42:59 +0530426 NULL, max8903_fault,
427 IRQF_TRIGGER_FALLING |
428 IRQF_TRIGGER_RISING | IRQF_ONESHOT,
429 "MAX8903 Fault", data);
MyungJoo Hamb14a9cc2011-03-29 10:10:16 +0900430 if (ret) {
431 dev_err(dev, "Cannot request irq %d for Fault (%d)\n",
432 gpio_to_irq(pdata->flt), ret);
Vaishali Thakkar0df2dee2015-09-17 18:55:00 +0530433 return ret;
MyungJoo Hamb14a9cc2011-03-29 10:10:16 +0900434 }
435 }
436
437 return 0;
MyungJoo Hamb14a9cc2011-03-29 10:10:16 +0900438}
439
Chris Lapac5ed3302016-06-24 12:26:12 +1000440static const struct of_device_id max8903_match_ids[] = {
441 { .compatible = "maxim,max8903", },
442 { /* sentinel */ }
443};
444MODULE_DEVICE_TABLE(of, max8903_match_ids);
445
MyungJoo Hamb14a9cc2011-03-29 10:10:16 +0900446static struct platform_driver max8903_driver = {
447 .probe = max8903_probe,
MyungJoo Hamb14a9cc2011-03-29 10:10:16 +0900448 .driver = {
449 .name = "max8903-charger",
Chris Lapac5ed3302016-06-24 12:26:12 +1000450 .of_match_table = max8903_match_ids
MyungJoo Hamb14a9cc2011-03-29 10:10:16 +0900451 },
452};
453
Axel Lin300bac72011-11-26 12:01:10 +0800454module_platform_driver(max8903_driver);
MyungJoo Hamb14a9cc2011-03-29 10:10:16 +0900455
456MODULE_LICENSE("GPL");
457MODULE_DESCRIPTION("MAX8903 Charger Driver");
458MODULE_AUTHOR("MyungJoo Ham <myungjoo.ham@samsung.com>");
Axel Linbd19c752011-06-24 22:26:36 +0800459MODULE_ALIAS("platform:max8903-charger");