Blame view

drivers/power/charger-manager.c 53.9 KB
3bb3dbbd5   Donggeun Kim   power_supply: Add...
1
2
3
4
5
6
7
8
9
10
11
12
13
  /*
   * Copyright (C) 2011 Samsung Electronics Co., Ltd.
   * MyungJoo Ham <myungjoo.ham@samsung.com>
   *
   * This driver enables to monitor battery health and control charger
   * during suspend-to-mem.
   * Charger manager depends on other devices. register this later than
   * the depending devices.
   *
   * This program is free software; you can redistribute it and/or modify
   * it under the terms of the GNU General Public License version 2 as
   * published by the Free Software Foundation.
  **/
e5409cbd8   Joe Perches   charger-manager: ...
14
  #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
3bb3dbbd5   Donggeun Kim   power_supply: Add...
15
16
17
18
19
20
21
22
23
24
  #include <linux/io.h>
  #include <linux/module.h>
  #include <linux/irq.h>
  #include <linux/interrupt.h>
  #include <linux/rtc.h>
  #include <linux/slab.h>
  #include <linux/workqueue.h>
  #include <linux/platform_device.h>
  #include <linux/power/charger-manager.h>
  #include <linux/regulator/consumer.h>
3950c7865   Chanwoo Choi   charger-manager: ...
25
  #include <linux/sysfs.h>
856ee6115   Jonghwa Lee   charger-manager: ...
26
  #include <linux/of.h>
5c49a6256   Jonghwa Lee   charger-manager: ...
27
28
29
30
31
32
33
34
  #include <linux/thermal.h>
  
  /*
   * Default termperature threshold for charging.
   * Every temperature units are in tenth of centigrade.
   */
  #define CM_DEFAULT_RECHARGE_TEMP_DIFF	50
  #define CM_DEFAULT_CHARGE_TEMP_MAX	500
3bb3dbbd5   Donggeun Kim   power_supply: Add...
35

dfeccb12b   Chanwoo Choi   charger-manager: ...
36
37
38
39
40
  static const char * const default_event_names[] = {
  	[CM_EVENT_UNKNOWN] = "Unknown",
  	[CM_EVENT_BATT_FULL] = "Battery Full",
  	[CM_EVENT_BATT_IN] = "Battery Inserted",
  	[CM_EVENT_BATT_OUT] = "Battery Pulled Out",
5c49a6256   Jonghwa Lee   charger-manager: ...
41
42
  	[CM_EVENT_BATT_OVERHEAT] = "Battery Overheat",
  	[CM_EVENT_BATT_COLD] = "Battery Cold",
dfeccb12b   Chanwoo Choi   charger-manager: ...
43
44
45
46
  	[CM_EVENT_EXT_PWR_IN_OUT] = "External Power Attach/Detach",
  	[CM_EVENT_CHG_START_STOP] = "Charging Start/Stop",
  	[CM_EVENT_OTHERS] = "Other battery events"
  };
3bb3dbbd5   Donggeun Kim   power_supply: Add...
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
  /*
   * Regard CM_JIFFIES_SMALL jiffies is small enough to ignore for
   * delayed works so that we can run delayed works with CM_JIFFIES_SMALL
   * without any delays.
   */
  #define	CM_JIFFIES_SMALL	(2)
  
  /* If y is valid (> 0) and smaller than x, do x = y */
  #define CM_MIN_VALID(x, y)	x = (((y > 0) && ((x) > (y))) ? (y) : (x))
  
  /*
   * Regard CM_RTC_SMALL (sec) is small enough to ignore error in invoking
   * rtc alarm. It should be 2 or larger
   */
  #define CM_RTC_SMALL		(2)
  
  #define UEVENT_BUF_SIZE		32
  
  static LIST_HEAD(cm_list);
  static DEFINE_MUTEX(cm_list_mtx);
  
  /* About in-suspend (suspend-again) monitoring */
c1155c64e   Jonghwa Lee   power: charger-ma...
69
  static struct alarm *cm_timer;
3bb3dbbd5   Donggeun Kim   power_supply: Add...
70
  static bool cm_suspended;
c1155c64e   Jonghwa Lee   power: charger-ma...
71
  static bool cm_timer_set;
3bb3dbbd5   Donggeun Kim   power_supply: Add...
72
  static unsigned long cm_suspend_duration_ms;
d829dc75b   Chanwoo Choi   charger-manager: ...
73
74
75
76
77
  /* About normal (not suspended) monitoring */
  static unsigned long polling_jiffy = ULONG_MAX; /* ULONG_MAX: no polling */
  static unsigned long next_polling; /* Next appointed polling time */
  static struct workqueue_struct *cm_wq; /* init at driver add */
  static struct delayed_work cm_monitor_work; /* init at driver add */
3bb3dbbd5   Donggeun Kim   power_supply: Add...
78
79
80
81
82
83
84
  /**
   * is_batt_present - See if the battery presents in place.
   * @cm: the Charger Manager representing the battery.
   */
  static bool is_batt_present(struct charger_manager *cm)
  {
  	union power_supply_propval val;
bdbe81445   Krzysztof Kozlowski   power: charger-ma...
85
  	struct power_supply *psy;
3bb3dbbd5   Donggeun Kim   power_supply: Add...
86
87
88
89
  	bool present = false;
  	int i, ret;
  
  	switch (cm->desc->battery_present) {
d829dc75b   Chanwoo Choi   charger-manager: ...
90
91
92
93
94
  	case CM_BATTERY_PRESENT:
  		present = true;
  		break;
  	case CM_NO_BATTERY:
  		break;
3bb3dbbd5   Donggeun Kim   power_supply: Add...
95
  	case CM_FUEL_GAUGE:
bdbe81445   Krzysztof Kozlowski   power: charger-ma...
96
97
98
  		psy = power_supply_get_by_name(cm->desc->psy_fuel_gauge);
  		if (!psy)
  			break;
b70229bca   Krzysztof Kozlowski   power_supply: cha...
99
100
  		ret = power_supply_get_property(psy, POWER_SUPPLY_PROP_PRESENT,
  				&val);
3bb3dbbd5   Donggeun Kim   power_supply: Add...
101
102
  		if (ret == 0 && val.intval)
  			present = true;
b43eb35ab   Krzysztof Kozlowski   power_supply: cha...
103
  		power_supply_put(psy);
3bb3dbbd5   Donggeun Kim   power_supply: Add...
104
105
  		break;
  	case CM_CHARGER_STAT:
cdaf3e153   Krzysztof Kozlowski   power: charger-ma...
106
107
108
109
110
111
112
113
114
  		for (i = 0; cm->desc->psy_charger_stat[i]; i++) {
  			psy = power_supply_get_by_name(
  					cm->desc->psy_charger_stat[i]);
  			if (!psy) {
  				dev_err(cm->dev, "Cannot find power supply \"%s\"
  ",
  					cm->desc->psy_charger_stat[i]);
  				continue;
  			}
b70229bca   Krzysztof Kozlowski   power_supply: cha...
115
116
  			ret = power_supply_get_property(psy,
  				POWER_SUPPLY_PROP_PRESENT, &val);
b43eb35ab   Krzysztof Kozlowski   power_supply: cha...
117
  			power_supply_put(psy);
3bb3dbbd5   Donggeun Kim   power_supply: Add...
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
  			if (ret == 0 && val.intval) {
  				present = true;
  				break;
  			}
  		}
  		break;
  	}
  
  	return present;
  }
  
  /**
   * is_ext_pwr_online - See if an external power source is attached to charge
   * @cm: the Charger Manager representing the battery.
   *
   * Returns true if at least one of the chargers of the battery has an external
   * power source attached to charge the battery regardless of whether it is
   * actually charging or not.
   */
  static bool is_ext_pwr_online(struct charger_manager *cm)
  {
  	union power_supply_propval val;
cdaf3e153   Krzysztof Kozlowski   power: charger-ma...
140
  	struct power_supply *psy;
3bb3dbbd5   Donggeun Kim   power_supply: Add...
141
142
143
144
  	bool online = false;
  	int i, ret;
  
  	/* If at least one of them has one, it's yes. */
cdaf3e153   Krzysztof Kozlowski   power: charger-ma...
145
146
147
148
149
150
151
152
  	for (i = 0; cm->desc->psy_charger_stat[i]; i++) {
  		psy = power_supply_get_by_name(cm->desc->psy_charger_stat[i]);
  		if (!psy) {
  			dev_err(cm->dev, "Cannot find power supply \"%s\"
  ",
  					cm->desc->psy_charger_stat[i]);
  			continue;
  		}
b70229bca   Krzysztof Kozlowski   power_supply: cha...
153
154
  		ret = power_supply_get_property(psy, POWER_SUPPLY_PROP_ONLINE,
  				&val);
b43eb35ab   Krzysztof Kozlowski   power_supply: cha...
155
  		power_supply_put(psy);
3bb3dbbd5   Donggeun Kim   power_supply: Add...
156
157
158
159
160
161
162
163
164
165
  		if (ret == 0 && val.intval) {
  			online = true;
  			break;
  		}
  	}
  
  	return online;
  }
  
  /**
ad3d13eee   Donggeun Kim   power_supply: Cha...
166
167
168
169
170
171
172
173
174
175
   * get_batt_uV - Get the voltage level of the battery
   * @cm: the Charger Manager representing the battery.
   * @uV: the voltage level returned.
   *
   * Returns 0 if there is no error.
   * Returns a negative value on error.
   */
  static int get_batt_uV(struct charger_manager *cm, int *uV)
  {
  	union power_supply_propval val;
bdbe81445   Krzysztof Kozlowski   power: charger-ma...
176
  	struct power_supply *fuel_gauge;
ad3d13eee   Donggeun Kim   power_supply: Cha...
177
  	int ret;
bdbe81445   Krzysztof Kozlowski   power: charger-ma...
178
179
  	fuel_gauge = power_supply_get_by_name(cm->desc->psy_fuel_gauge);
  	if (!fuel_gauge)
ad3d13eee   Donggeun Kim   power_supply: Cha...
180
  		return -ENODEV;
b70229bca   Krzysztof Kozlowski   power_supply: cha...
181
  	ret = power_supply_get_property(fuel_gauge,
bb2a95c2d   Axel Lin   charger-manager: ...
182
  				POWER_SUPPLY_PROP_VOLTAGE_NOW, &val);
b43eb35ab   Krzysztof Kozlowski   power_supply: cha...
183
  	power_supply_put(fuel_gauge);
ad3d13eee   Donggeun Kim   power_supply: Cha...
184
185
186
187
188
189
190
191
  	if (ret)
  		return ret;
  
  	*uV = val.intval;
  	return 0;
  }
  
  /**
3bb3dbbd5   Donggeun Kim   power_supply: Add...
192
193
194
195
196
197
198
   * is_charging - Returns true if the battery is being charged.
   * @cm: the Charger Manager representing the battery.
   */
  static bool is_charging(struct charger_manager *cm)
  {
  	int i, ret;
  	bool charging = false;
cdaf3e153   Krzysztof Kozlowski   power: charger-ma...
199
  	struct power_supply *psy;
3bb3dbbd5   Donggeun Kim   power_supply: Add...
200
201
202
203
204
205
206
  	union power_supply_propval val;
  
  	/* If there is no battery, it cannot be charged */
  	if (!is_batt_present(cm))
  		return false;
  
  	/* If at least one of the charger is charging, return yes */
cdaf3e153   Krzysztof Kozlowski   power: charger-ma...
207
  	for (i = 0; cm->desc->psy_charger_stat[i]; i++) {
3bb3dbbd5   Donggeun Kim   power_supply: Add...
208
209
210
211
212
  		/* 1. The charger sholuld not be DISABLED */
  		if (cm->emergency_stop)
  			continue;
  		if (!cm->charger_enabled)
  			continue;
cdaf3e153   Krzysztof Kozlowski   power: charger-ma...
213
214
215
216
217
218
219
  		psy = power_supply_get_by_name(cm->desc->psy_charger_stat[i]);
  		if (!psy) {
  			dev_err(cm->dev, "Cannot find power supply \"%s\"
  ",
  					cm->desc->psy_charger_stat[i]);
  			continue;
  		}
3bb3dbbd5   Donggeun Kim   power_supply: Add...
220
  		/* 2. The charger should be online (ext-power) */
b70229bca   Krzysztof Kozlowski   power_supply: cha...
221
222
  		ret = power_supply_get_property(psy, POWER_SUPPLY_PROP_ONLINE,
  				&val);
3bb3dbbd5   Donggeun Kim   power_supply: Add...
223
  		if (ret) {
e5409cbd8   Joe Perches   charger-manager: ...
224
225
226
  			dev_warn(cm->dev, "Cannot read ONLINE value from %s
  ",
  				 cm->desc->psy_charger_stat[i]);
b43eb35ab   Krzysztof Kozlowski   power_supply: cha...
227
  			power_supply_put(psy);
3bb3dbbd5   Donggeun Kim   power_supply: Add...
228
229
  			continue;
  		}
b43eb35ab   Krzysztof Kozlowski   power_supply: cha...
230
231
  		if (val.intval == 0) {
  			power_supply_put(psy);
3bb3dbbd5   Donggeun Kim   power_supply: Add...
232
  			continue;
b43eb35ab   Krzysztof Kozlowski   power_supply: cha...
233
  		}
3bb3dbbd5   Donggeun Kim   power_supply: Add...
234
235
236
237
238
  
  		/*
  		 * 3. The charger should not be FULL, DISCHARGING,
  		 * or NOT_CHARGING.
  		 */
b70229bca   Krzysztof Kozlowski   power_supply: cha...
239
240
  		ret = power_supply_get_property(psy, POWER_SUPPLY_PROP_STATUS,
  				&val);
b43eb35ab   Krzysztof Kozlowski   power_supply: cha...
241
  		power_supply_put(psy);
3bb3dbbd5   Donggeun Kim   power_supply: Add...
242
  		if (ret) {
e5409cbd8   Joe Perches   charger-manager: ...
243
244
245
  			dev_warn(cm->dev, "Cannot read STATUS value from %s
  ",
  				 cm->desc->psy_charger_stat[i]);
3bb3dbbd5   Donggeun Kim   power_supply: Add...
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
  			continue;
  		}
  		if (val.intval == POWER_SUPPLY_STATUS_FULL ||
  				val.intval == POWER_SUPPLY_STATUS_DISCHARGING ||
  				val.intval == POWER_SUPPLY_STATUS_NOT_CHARGING)
  			continue;
  
  		/* Then, this is charging. */
  		charging = true;
  		break;
  	}
  
  	return charging;
  }
  
  /**
2ed9e9b65   Chanwoo Choi   charger-manager: ...
262
263
264
265
266
267
268
   * is_full_charged - Returns true if the battery is fully charged.
   * @cm: the Charger Manager representing the battery.
   */
  static bool is_full_charged(struct charger_manager *cm)
  {
  	struct charger_desc *desc = cm->desc;
  	union power_supply_propval val;
bdbe81445   Krzysztof Kozlowski   power: charger-ma...
269
  	struct power_supply *fuel_gauge;
b43eb35ab   Krzysztof Kozlowski   power_supply: cha...
270
  	bool is_full = false;
2ed9e9b65   Chanwoo Choi   charger-manager: ...
271
272
273
274
  	int ret = 0;
  	int uV;
  
  	/* If there is no battery, it cannot be charged */
0fa11dbc2   Chanwoo Choi   charger-manager: ...
275
276
  	if (!is_batt_present(cm))
  		return false;
2ed9e9b65   Chanwoo Choi   charger-manager: ...
277

bdbe81445   Krzysztof Kozlowski   power: charger-ma...
278
279
280
281
282
  	fuel_gauge = power_supply_get_by_name(cm->desc->psy_fuel_gauge);
  	if (!fuel_gauge)
  		return false;
  
  	if (desc->fullbatt_full_capacity > 0) {
0fa11dbc2   Chanwoo Choi   charger-manager: ...
283
  		val.intval = 0;
2ed9e9b65   Chanwoo Choi   charger-manager: ...
284
  		/* Not full if capacity of fuel gauge isn't full */
b70229bca   Krzysztof Kozlowski   power_supply: cha...
285
  		ret = power_supply_get_property(fuel_gauge,
2ed9e9b65   Chanwoo Choi   charger-manager: ...
286
  				POWER_SUPPLY_PROP_CHARGE_FULL, &val);
b43eb35ab   Krzysztof Kozlowski   power_supply: cha...
287
288
289
290
  		if (!ret && val.intval > desc->fullbatt_full_capacity) {
  			is_full = true;
  			goto out;
  		}
2ed9e9b65   Chanwoo Choi   charger-manager: ...
291
292
293
294
295
  	}
  
  	/* Full, if it's over the fullbatt voltage */
  	if (desc->fullbatt_uV > 0) {
  		ret = get_batt_uV(cm, &uV);
b43eb35ab   Krzysztof Kozlowski   power_supply: cha...
296
297
298
299
  		if (!ret && uV >= desc->fullbatt_uV) {
  			is_full = true;
  			goto out;
  		}
2ed9e9b65   Chanwoo Choi   charger-manager: ...
300
301
302
  	}
  
  	/* Full, if the capacity is more than fullbatt_soc */
bdbe81445   Krzysztof Kozlowski   power: charger-ma...
303
  	if (desc->fullbatt_soc > 0) {
0fa11dbc2   Chanwoo Choi   charger-manager: ...
304
  		val.intval = 0;
b70229bca   Krzysztof Kozlowski   power_supply: cha...
305
  		ret = power_supply_get_property(fuel_gauge,
2ed9e9b65   Chanwoo Choi   charger-manager: ...
306
  				POWER_SUPPLY_PROP_CAPACITY, &val);
b43eb35ab   Krzysztof Kozlowski   power_supply: cha...
307
308
309
310
  		if (!ret && val.intval >= desc->fullbatt_soc) {
  			is_full = true;
  			goto out;
  		}
2ed9e9b65   Chanwoo Choi   charger-manager: ...
311
  	}
b43eb35ab   Krzysztof Kozlowski   power_supply: cha...
312
313
314
  out:
  	power_supply_put(fuel_gauge);
  	return is_full;
2ed9e9b65   Chanwoo Choi   charger-manager: ...
315
316
317
  }
  
  /**
3bb3dbbd5   Donggeun Kim   power_supply: Add...
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
   * is_polling_required - Return true if need to continue polling for this CM.
   * @cm: the Charger Manager representing the battery.
   */
  static bool is_polling_required(struct charger_manager *cm)
  {
  	switch (cm->desc->polling_mode) {
  	case CM_POLL_DISABLE:
  		return false;
  	case CM_POLL_ALWAYS:
  		return true;
  	case CM_POLL_EXTERNAL_POWER_ONLY:
  		return is_ext_pwr_online(cm);
  	case CM_POLL_CHARGING_ONLY:
  		return is_charging(cm);
  	default:
  		dev_warn(cm->dev, "Incorrect polling_mode (%d)
  ",
e5409cbd8   Joe Perches   charger-manager: ...
335
  			 cm->desc->polling_mode);
3bb3dbbd5   Donggeun Kim   power_supply: Add...
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
  	}
  
  	return false;
  }
  
  /**
   * try_charger_enable - Enable/Disable chargers altogether
   * @cm: the Charger Manager representing the battery.
   * @enable: true: enable / false: disable
   *
   * Note that Charger Manager keeps the charger enabled regardless whether
   * the charger is charging or not (because battery is full or no external
   * power source exists) except when CM needs to disable chargers forcibly
   * bacause of emergency causes; when the battery is overheated or too cold.
   */
  static int try_charger_enable(struct charger_manager *cm, bool enable)
  {
  	int err = 0, i;
  	struct charger_desc *desc = cm->desc;
  
  	/* Ignore if it's redundent command */
bb2a95c2d   Axel Lin   charger-manager: ...
357
  	if (enable == cm->charger_enabled)
3bb3dbbd5   Donggeun Kim   power_supply: Add...
358
359
360
361
362
  		return 0;
  
  	if (enable) {
  		if (cm->emergency_stop)
  			return -EAGAIN;
8fcfe088e   Chanwoo Choi   charger-manager: ...
363
364
365
366
367
368
369
  
  		/*
  		 * Save start time of charging to limit
  		 * maximum possible charging time.
  		 */
  		cm->charging_start_time = ktime_to_ms(ktime_get());
  		cm->charging_end_time = 0;
dbb61fc74   Chanwoo Choi   charger-manager: ...
370
  		for (i = 0 ; i < desc->num_charger_regulators ; i++) {
3950c7865   Chanwoo Choi   charger-manager: ...
371
372
  			if (desc->charger_regulators[i].externally_control)
  				continue;
dbb61fc74   Chanwoo Choi   charger-manager: ...
373
374
  			err = regulator_enable(desc->charger_regulators[i].consumer);
  			if (err < 0) {
e5409cbd8   Joe Perches   charger-manager: ...
375
376
377
  				dev_warn(cm->dev, "Cannot enable %s regulator
  ",
  					 desc->charger_regulators[i].regulator_name);
dbb61fc74   Chanwoo Choi   charger-manager: ...
378
379
  			}
  		}
3bb3dbbd5   Donggeun Kim   power_supply: Add...
380
381
  	} else {
  		/*
8fcfe088e   Chanwoo Choi   charger-manager: ...
382
383
384
385
386
  		 * Save end time of charging to maintain fully charged state
  		 * of battery after full-batt.
  		 */
  		cm->charging_start_time = 0;
  		cm->charging_end_time = ktime_to_ms(ktime_get());
dbb61fc74   Chanwoo Choi   charger-manager: ...
387
  		for (i = 0 ; i < desc->num_charger_regulators ; i++) {
3950c7865   Chanwoo Choi   charger-manager: ...
388
389
  			if (desc->charger_regulators[i].externally_control)
  				continue;
dbb61fc74   Chanwoo Choi   charger-manager: ...
390
391
  			err = regulator_disable(desc->charger_regulators[i].consumer);
  			if (err < 0) {
e5409cbd8   Joe Perches   charger-manager: ...
392
393
394
  				dev_warn(cm->dev, "Cannot disable %s regulator
  ",
  					 desc->charger_regulators[i].regulator_name);
dbb61fc74   Chanwoo Choi   charger-manager: ...
395
396
  			}
  		}
3bb3dbbd5   Donggeun Kim   power_supply: Add...
397
398
399
400
  		/*
  		 * Abnormal battery state - Stop charging forcibly,
  		 * even if charger was enabled at the other places
  		 */
3bb3dbbd5   Donggeun Kim   power_supply: Add...
401
402
403
404
405
  		for (i = 0; i < desc->num_charger_regulators; i++) {
  			if (regulator_is_enabled(
  				    desc->charger_regulators[i].consumer)) {
  				regulator_force_disable(
  					desc->charger_regulators[i].consumer);
e5409cbd8   Joe Perches   charger-manager: ...
406
407
408
  				dev_warn(cm->dev, "Disable regulator(%s) forcibly
  ",
  					 desc->charger_regulators[i].regulator_name);
3bb3dbbd5   Donggeun Kim   power_supply: Add...
409
410
411
412
413
414
415
416
417
418
419
  			}
  		}
  	}
  
  	if (!err)
  		cm->charger_enabled = enable;
  
  	return err;
  }
  
  /**
d829dc75b   Chanwoo Choi   charger-manager: ...
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
   * try_charger_restart - Restart charging.
   * @cm: the Charger Manager representing the battery.
   *
   * Restart charging by turning off and on the charger.
   */
  static int try_charger_restart(struct charger_manager *cm)
  {
  	int err;
  
  	if (cm->emergency_stop)
  		return -EAGAIN;
  
  	err = try_charger_enable(cm, false);
  	if (err)
  		return err;
  
  	return try_charger_enable(cm, true);
  }
  
  /**
3bb3dbbd5   Donggeun Kim   power_supply: Add...
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
   * uevent_notify - Let users know something has changed.
   * @cm: the Charger Manager representing the battery.
   * @event: the event string.
   *
   * If @event is null, it implies that uevent_notify is called
   * by resume function. When called in the resume function, cm_suspended
   * should be already reset to false in order to let uevent_notify
   * notify the recent event during the suspend to users. While
   * suspended, uevent_notify does not notify users, but tracks
   * events so that uevent_notify can notify users later after resumed.
   */
  static void uevent_notify(struct charger_manager *cm, const char *event)
  {
  	static char env_str[UEVENT_BUF_SIZE + 1] = "";
  	static char env_str_save[UEVENT_BUF_SIZE + 1] = "";
  
  	if (cm_suspended) {
  		/* Nothing in suspended-event buffer */
  		if (env_str_save[0] == 0) {
  			if (!strncmp(env_str, event, UEVENT_BUF_SIZE))
  				return; /* status not changed */
  			strncpy(env_str_save, event, UEVENT_BUF_SIZE);
  			return;
  		}
  
  		if (!strncmp(env_str_save, event, UEVENT_BUF_SIZE))
  			return; /* Duplicated. */
bb2a95c2d   Axel Lin   charger-manager: ...
467
  		strncpy(env_str_save, event, UEVENT_BUF_SIZE);
3bb3dbbd5   Donggeun Kim   power_supply: Add...
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
  		return;
  	}
  
  	if (event == NULL) {
  		/* No messages pending */
  		if (!env_str_save[0])
  			return;
  
  		strncpy(env_str, env_str_save, UEVENT_BUF_SIZE);
  		kobject_uevent(&cm->dev->kobj, KOBJ_CHANGE);
  		env_str_save[0] = 0;
  
  		return;
  	}
  
  	/* status not changed */
  	if (!strncmp(env_str, event, UEVENT_BUF_SIZE))
  		return;
  
  	/* save the status and notify the update */
  	strncpy(env_str, event, UEVENT_BUF_SIZE);
  	kobject_uevent(&cm->dev->kobj, KOBJ_CHANGE);
e5409cbd8   Joe Perches   charger-manager: ...
490
491
  	dev_info(cm->dev, "%s
  ", event);
3bb3dbbd5   Donggeun Kim   power_supply: Add...
492
493
494
  }
  
  /**
d829dc75b   Chanwoo Choi   charger-manager: ...
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
   * fullbatt_vchk - Check voltage drop some times after "FULL" event.
   * @work: the work_struct appointing the function
   *
   * If a user has designated "fullbatt_vchkdrop_ms/uV" values with
   * charger_desc, Charger Manager checks voltage drop after the battery
   * "FULL" event. It checks whether the voltage has dropped more than
   * fullbatt_vchkdrop_uV by calling this function after fullbatt_vchkrop_ms.
   */
  static void fullbatt_vchk(struct work_struct *work)
  {
  	struct delayed_work *dwork = to_delayed_work(work);
  	struct charger_manager *cm = container_of(dwork,
  			struct charger_manager, fullbatt_vchk_work);
  	struct charger_desc *desc = cm->desc;
  	int batt_uV, err, diff;
  
  	/* remove the appointment for fullbatt_vchk */
  	cm->fullbatt_vchk_jiffies_at = 0;
  
  	if (!desc->fullbatt_vchkdrop_uV || !desc->fullbatt_vchkdrop_ms)
  		return;
  
  	err = get_batt_uV(cm, &batt_uV);
  	if (err) {
e5409cbd8   Joe Perches   charger-manager: ...
519
520
  		dev_err(cm->dev, "%s: get_batt_uV error(%d)
  ", __func__, err);
d829dc75b   Chanwoo Choi   charger-manager: ...
521
522
  		return;
  	}
f36b9ddba   Chanwoo Choi   charger-manager: ...
523
524
525
  	diff = desc->fullbatt_uV - batt_uV;
  	if (diff < 0)
  		return;
d829dc75b   Chanwoo Choi   charger-manager: ...
526

e5409cbd8   Joe Perches   charger-manager: ...
527
528
  	dev_info(cm->dev, "VBATT dropped %duV after full-batt
  ", diff);
d829dc75b   Chanwoo Choi   charger-manager: ...
529
530
531
  
  	if (diff > desc->fullbatt_vchkdrop_uV) {
  		try_charger_restart(cm);
8fcfe088e   Chanwoo Choi   charger-manager: ...
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
  		uevent_notify(cm, "Recharging");
  	}
  }
  
  /**
   * check_charging_duration - Monitor charging/discharging duration
   * @cm: the Charger Manager representing the battery.
   *
   * If whole charging duration exceed 'charging_max_duration_ms',
   * cm stop charging to prevent overcharge/overheat. If discharging
   * duration exceed 'discharging _max_duration_ms', charger cable is
   * attached, after full-batt, cm start charging to maintain fully
   * charged state for battery.
   */
  static int check_charging_duration(struct charger_manager *cm)
  {
  	struct charger_desc *desc = cm->desc;
  	u64 curr = ktime_to_ms(ktime_get());
  	u64 duration;
  	int ret = false;
  
  	if (!desc->charging_max_duration_ms &&
  			!desc->discharging_max_duration_ms)
  		return ret;
  
  	if (cm->charger_enabled) {
  		duration = curr - cm->charging_start_time;
  
  		if (duration > desc->charging_max_duration_ms) {
856ee6115   Jonghwa Lee   charger-manager: ...
561
562
  			dev_info(cm->dev, "Charging duration exceed %ums
  ",
8fcfe088e   Chanwoo Choi   charger-manager: ...
563
564
565
566
567
568
569
570
571
572
  				 desc->charging_max_duration_ms);
  			uevent_notify(cm, "Discharging");
  			try_charger_enable(cm, false);
  			ret = true;
  		}
  	} else if (is_ext_pwr_online(cm) && !cm->charger_enabled) {
  		duration = curr - cm->charging_end_time;
  
  		if (duration > desc->charging_max_duration_ms &&
  				is_ext_pwr_online(cm)) {
856ee6115   Jonghwa Lee   charger-manager: ...
573
574
  			dev_info(cm->dev, "Discharging duration exceed %ums
  ",
8fcfe088e   Chanwoo Choi   charger-manager: ...
575
  				 desc->discharging_max_duration_ms);
e5409cbd8   Joe Perches   charger-manager: ...
576
  			uevent_notify(cm, "Recharging");
8fcfe088e   Chanwoo Choi   charger-manager: ...
577
578
579
  			try_charger_enable(cm, true);
  			ret = true;
  		}
d829dc75b   Chanwoo Choi   charger-manager: ...
580
  	}
8fcfe088e   Chanwoo Choi   charger-manager: ...
581
582
  
  	return ret;
d829dc75b   Chanwoo Choi   charger-manager: ...
583
  }
bdbe81445   Krzysztof Kozlowski   power: charger-ma...
584
585
586
587
  static int cm_get_battery_temperature_by_psy(struct charger_manager *cm,
  					int *temp)
  {
  	struct power_supply *fuel_gauge;
b43eb35ab   Krzysztof Kozlowski   power_supply: cha...
588
  	int ret;
bdbe81445   Krzysztof Kozlowski   power: charger-ma...
589
590
591
592
  
  	fuel_gauge = power_supply_get_by_name(cm->desc->psy_fuel_gauge);
  	if (!fuel_gauge)
  		return -ENODEV;
b43eb35ab   Krzysztof Kozlowski   power_supply: cha...
593
  	ret = power_supply_get_property(fuel_gauge,
bdbe81445   Krzysztof Kozlowski   power: charger-ma...
594
595
  				POWER_SUPPLY_PROP_TEMP,
  				(union power_supply_propval *)temp);
b43eb35ab   Krzysztof Kozlowski   power_supply: cha...
596
597
598
  	power_supply_put(fuel_gauge);
  
  	return ret;
bdbe81445   Krzysztof Kozlowski   power: charger-ma...
599
  }
5c49a6256   Jonghwa Lee   charger-manager: ...
600
601
602
603
604
605
606
607
608
  static int cm_get_battery_temperature(struct charger_manager *cm,
  					int *temp)
  {
  	int ret;
  
  	if (!cm->desc->measure_battery_temp)
  		return -ENODEV;
  
  #ifdef CONFIG_THERMAL
bdbe81445   Krzysztof Kozlowski   power: charger-ma...
609
  	if (cm->tzd_batt) {
17e8351a7   Sascha Hauer   thermal: consiste...
610
  		ret = thermal_zone_get_temp(cm->tzd_batt, temp);
bdbe81445   Krzysztof Kozlowski   power: charger-ma...
611
612
613
614
  		if (!ret)
  			/* Calibrate temperature unit */
  			*temp /= 100;
  	} else
5c49a6256   Jonghwa Lee   charger-manager: ...
615
  #endif
bdbe81445   Krzysztof Kozlowski   power: charger-ma...
616
617
618
619
  	{
  		/* if-else continued from CONFIG_THERMAL */
  		ret = cm_get_battery_temperature_by_psy(cm, temp);
  	}
5c49a6256   Jonghwa Lee   charger-manager: ...
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
  	return ret;
  }
  
  static int cm_check_thermal_status(struct charger_manager *cm)
  {
  	struct charger_desc *desc = cm->desc;
  	int temp, upper_limit, lower_limit;
  	int ret = 0;
  
  	ret = cm_get_battery_temperature(cm, &temp);
  	if (ret) {
  		/* FIXME:
  		 * No information of battery temperature might
  		 * occur hazadous result. We have to handle it
  		 * depending on battery type.
  		 */
  		dev_err(cm->dev, "Failed to get battery temperature
  ");
  		return 0;
  	}
  
  	upper_limit = desc->temp_max;
  	lower_limit = desc->temp_min;
  
  	if (cm->emergency_stop) {
  		upper_limit -= desc->temp_diff;
  		lower_limit += desc->temp_diff;
  	}
  
  	if (temp > upper_limit)
  		ret = CM_EVENT_BATT_OVERHEAT;
  	else if (temp < lower_limit)
  		ret = CM_EVENT_BATT_COLD;
  
  	return ret;
  }
d829dc75b   Chanwoo Choi   charger-manager: ...
656
  /**
3bb3dbbd5   Donggeun Kim   power_supply: Add...
657
658
659
660
661
662
663
664
   * _cm_monitor - Monitor the temperature and return true for exceptions.
   * @cm: the Charger Manager representing the battery.
   *
   * Returns true if there is an event to notify for the battery.
   * (True if the status of "emergency_stop" changes)
   */
  static bool _cm_monitor(struct charger_manager *cm)
  {
5c49a6256   Jonghwa Lee   charger-manager: ...
665
  	int temp_alrt;
3bb3dbbd5   Donggeun Kim   power_supply: Add...
666

5c49a6256   Jonghwa Lee   charger-manager: ...
667
  	temp_alrt = cm_check_thermal_status(cm);
3bb3dbbd5   Donggeun Kim   power_supply: Add...
668

2ed9e9b65   Chanwoo Choi   charger-manager: ...
669
  	/* It has been stopped already */
5c49a6256   Jonghwa Lee   charger-manager: ...
670
  	if (temp_alrt && cm->emergency_stop)
3bb3dbbd5   Donggeun Kim   power_supply: Add...
671
  		return false;
2ed9e9b65   Chanwoo Choi   charger-manager: ...
672
673
674
675
  	/*
  	 * Check temperature whether overheat or cold.
  	 * If temperature is out of range normal state, stop charging.
  	 */
5c49a6256   Jonghwa Lee   charger-manager: ...
676
677
678
679
  	if (temp_alrt) {
  		cm->emergency_stop = temp_alrt;
  		if (!try_charger_enable(cm, false))
  			uevent_notify(cm, default_event_names[temp_alrt]);
2ed9e9b65   Chanwoo Choi   charger-manager: ...
680
681
  
  	/*
8fcfe088e   Chanwoo Choi   charger-manager: ...
682
683
684
685
686
  	 * Check whole charging duration and discharing duration
  	 * after full-batt.
  	 */
  	} else if (!cm->emergency_stop && check_charging_duration(cm)) {
  		dev_dbg(cm->dev,
e5409cbd8   Joe Perches   charger-manager: ...
687
688
  			"Charging/Discharging duration is out of range
  ");
8fcfe088e   Chanwoo Choi   charger-manager: ...
689
  	/*
2ed9e9b65   Chanwoo Choi   charger-manager: ...
690
691
692
693
694
695
696
697
698
699
700
701
702
703
  	 * Check dropped voltage of battery. If battery voltage is more
  	 * dropped than fullbatt_vchkdrop_uV after fully charged state,
  	 * charger-manager have to recharge battery.
  	 */
  	} else if (!cm->emergency_stop && is_ext_pwr_online(cm) &&
  			!cm->charger_enabled) {
  		fullbatt_vchk(&cm->fullbatt_vchk_work.work);
  
  	/*
  	 * Check whether fully charged state to protect overcharge
  	 * if charger-manager is charging for battery.
  	 */
  	} else if (!cm->emergency_stop && is_full_charged(cm) &&
  			cm->charger_enabled) {
e5409cbd8   Joe Perches   charger-manager: ...
704
705
  		dev_info(cm->dev, "EVENT_HANDLE: Battery Fully Charged
  ");
2ed9e9b65   Chanwoo Choi   charger-manager: ...
706
707
708
709
710
  		uevent_notify(cm, default_event_names[CM_EVENT_BATT_FULL]);
  
  		try_charger_enable(cm, false);
  
  		fullbatt_vchk(&cm->fullbatt_vchk_work.work);
3bb3dbbd5   Donggeun Kim   power_supply: Add...
711
712
  	} else {
  		cm->emergency_stop = 0;
2ed9e9b65   Chanwoo Choi   charger-manager: ...
713
714
715
716
  		if (is_ext_pwr_online(cm)) {
  			if (!try_charger_enable(cm, true))
  				uevent_notify(cm, "CHARGING");
  		}
3bb3dbbd5   Donggeun Kim   power_supply: Add...
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
  	}
  
  	return true;
  }
  
  /**
   * cm_monitor - Monitor every battery.
   *
   * Returns true if there is an event to notify from any of the batteries.
   * (True if the status of "emergency_stop" changes)
   */
  static bool cm_monitor(void)
  {
  	bool stop = false;
  	struct charger_manager *cm;
  
  	mutex_lock(&cm_list_mtx);
bb2a95c2d   Axel Lin   charger-manager: ...
734
735
736
737
  	list_for_each_entry(cm, &cm_list, entry) {
  		if (_cm_monitor(cm))
  			stop = true;
  	}
3bb3dbbd5   Donggeun Kim   power_supply: Add...
738
739
740
741
742
  
  	mutex_unlock(&cm_list_mtx);
  
  	return stop;
  }
d829dc75b   Chanwoo Choi   charger-manager: ...
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
  /**
   * _setup_polling - Setup the next instance of polling.
   * @work: work_struct of the function _setup_polling.
   */
  static void _setup_polling(struct work_struct *work)
  {
  	unsigned long min = ULONG_MAX;
  	struct charger_manager *cm;
  	bool keep_polling = false;
  	unsigned long _next_polling;
  
  	mutex_lock(&cm_list_mtx);
  
  	list_for_each_entry(cm, &cm_list, entry) {
  		if (is_polling_required(cm) && cm->desc->polling_interval_ms) {
  			keep_polling = true;
  
  			if (min > cm->desc->polling_interval_ms)
  				min = cm->desc->polling_interval_ms;
  		}
  	}
  
  	polling_jiffy = msecs_to_jiffies(min);
  	if (polling_jiffy <= CM_JIFFIES_SMALL)
  		polling_jiffy = CM_JIFFIES_SMALL + 1;
  
  	if (!keep_polling)
  		polling_jiffy = ULONG_MAX;
  	if (polling_jiffy == ULONG_MAX)
  		goto out;
  
  	WARN(cm_wq == NULL, "charger-manager: workqueue not initialized"
  			    ". try it later. %s
  ", __func__);
2fbb520d2   Tejun Heo   charger_manager: ...
777
778
779
780
781
782
  	/*
  	 * Use mod_delayed_work() iff the next polling interval should
  	 * occur before the currently scheduled one.  If @cm_monitor_work
  	 * isn't active, the end result is the same, so no need to worry
  	 * about stale @next_polling.
  	 */
d829dc75b   Chanwoo Choi   charger-manager: ...
783
  	_next_polling = jiffies + polling_jiffy;
2fbb520d2   Tejun Heo   charger_manager: ...
784
  	if (time_before(_next_polling, next_polling)) {
41f63c535   Tejun Heo   workqueue: use mo...
785
  		mod_delayed_work(cm_wq, &cm_monitor_work, polling_jiffy);
2fbb520d2   Tejun Heo   charger_manager: ...
786
787
788
789
  		next_polling = _next_polling;
  	} else {
  		if (queue_delayed_work(cm_wq, &cm_monitor_work, polling_jiffy))
  			next_polling = _next_polling;
d829dc75b   Chanwoo Choi   charger-manager: ...
790
  	}
d829dc75b   Chanwoo Choi   charger-manager: ...
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
  out:
  	mutex_unlock(&cm_list_mtx);
  }
  static DECLARE_WORK(setup_polling, _setup_polling);
  
  /**
   * cm_monitor_poller - The Monitor / Poller.
   * @work: work_struct of the function cm_monitor_poller
   *
   * During non-suspended state, cm_monitor_poller is used to poll and monitor
   * the batteries.
   */
  static void cm_monitor_poller(struct work_struct *work)
  {
  	cm_monitor();
  	schedule_work(&setup_polling);
  }
dfeccb12b   Chanwoo Choi   charger-manager: ...
808
809
810
811
812
813
814
815
816
817
818
819
820
  /**
   * fullbatt_handler - Event handler for CM_EVENT_BATT_FULL
   * @cm: the Charger Manager representing the battery.
   */
  static void fullbatt_handler(struct charger_manager *cm)
  {
  	struct charger_desc *desc = cm->desc;
  
  	if (!desc->fullbatt_vchkdrop_uV || !desc->fullbatt_vchkdrop_ms)
  		goto out;
  
  	if (cm_suspended)
  		device_set_wakeup_capable(cm->dev, true);
41f63c535   Tejun Heo   workqueue: use mo...
821
822
  	mod_delayed_work(cm_wq, &cm->fullbatt_vchk_work,
  			 msecs_to_jiffies(desc->fullbatt_vchkdrop_ms));
dfeccb12b   Chanwoo Choi   charger-manager: ...
823
824
825
826
827
828
829
  	cm->fullbatt_vchk_jiffies_at = jiffies + msecs_to_jiffies(
  				       desc->fullbatt_vchkdrop_ms);
  
  	if (cm->fullbatt_vchk_jiffies_at == 0)
  		cm->fullbatt_vchk_jiffies_at = 1;
  
  out:
e5409cbd8   Joe Perches   charger-manager: ...
830
831
  	dev_info(cm->dev, "EVENT_HANDLE: Battery Fully Charged
  ");
dfeccb12b   Chanwoo Choi   charger-manager: ...
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
  	uevent_notify(cm, default_event_names[CM_EVENT_BATT_FULL]);
  }
  
  /**
   * battout_handler - Event handler for CM_EVENT_BATT_OUT
   * @cm: the Charger Manager representing the battery.
   */
  static void battout_handler(struct charger_manager *cm)
  {
  	if (cm_suspended)
  		device_set_wakeup_capable(cm->dev, true);
  
  	if (!is_batt_present(cm)) {
  		dev_emerg(cm->dev, "Battery Pulled Out!
  ");
  		uevent_notify(cm, default_event_names[CM_EVENT_BATT_OUT]);
  	} else {
  		uevent_notify(cm, "Battery Reinserted?");
  	}
  }
  
  /**
   * misc_event_handler - Handler for other evnets
   * @cm: the Charger Manager representing the battery.
   * @type: the Charger Manager representing the battery.
   */
  static void misc_event_handler(struct charger_manager *cm,
  			enum cm_event_types type)
  {
  	if (cm_suspended)
  		device_set_wakeup_capable(cm->dev, true);
2fbb520d2   Tejun Heo   charger_manager: ...
863
  	if (is_polling_required(cm) && cm->desc->polling_interval_ms)
dfeccb12b   Chanwoo Choi   charger-manager: ...
864
865
866
  		schedule_work(&setup_polling);
  	uevent_notify(cm, default_event_names[type]);
  }
ad3d13eee   Donggeun Kim   power_supply: Cha...
867
868
869
870
  static int charger_get_property(struct power_supply *psy,
  		enum power_supply_property psp,
  		union power_supply_propval *val)
  {
297d716f6   Krzysztof Kozlowski   power_supply: Cha...
871
  	struct charger_manager *cm = power_supply_get_drvdata(psy);
ad3d13eee   Donggeun Kim   power_supply: Cha...
872
  	struct charger_desc *desc = cm->desc;
b43eb35ab   Krzysztof Kozlowski   power_supply: cha...
873
  	struct power_supply *fuel_gauge = NULL;
df58c04c9   Anton Vorontsov   charger-manager: ...
874
875
  	int ret = 0;
  	int uV;
ad3d13eee   Donggeun Kim   power_supply: Cha...
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
  
  	switch (psp) {
  	case POWER_SUPPLY_PROP_STATUS:
  		if (is_charging(cm))
  			val->intval = POWER_SUPPLY_STATUS_CHARGING;
  		else if (is_ext_pwr_online(cm))
  			val->intval = POWER_SUPPLY_STATUS_NOT_CHARGING;
  		else
  			val->intval = POWER_SUPPLY_STATUS_DISCHARGING;
  		break;
  	case POWER_SUPPLY_PROP_HEALTH:
  		if (cm->emergency_stop > 0)
  			val->intval = POWER_SUPPLY_HEALTH_OVERHEAT;
  		else if (cm->emergency_stop < 0)
  			val->intval = POWER_SUPPLY_HEALTH_COLD;
  		else
  			val->intval = POWER_SUPPLY_HEALTH_GOOD;
  		break;
  	case POWER_SUPPLY_PROP_PRESENT:
  		if (is_batt_present(cm))
  			val->intval = 1;
  		else
  			val->intval = 0;
  		break;
  	case POWER_SUPPLY_PROP_VOLTAGE_NOW:
df58c04c9   Anton Vorontsov   charger-manager: ...
901
  		ret = get_batt_uV(cm, &val->intval);
ad3d13eee   Donggeun Kim   power_supply: Cha...
902
903
  		break;
  	case POWER_SUPPLY_PROP_CURRENT_NOW:
bdbe81445   Krzysztof Kozlowski   power: charger-ma...
904
905
906
907
908
  		fuel_gauge = power_supply_get_by_name(cm->desc->psy_fuel_gauge);
  		if (!fuel_gauge) {
  			ret = -ENODEV;
  			break;
  		}
b70229bca   Krzysztof Kozlowski   power_supply: cha...
909
  		ret = power_supply_get_property(fuel_gauge,
ad3d13eee   Donggeun Kim   power_supply: Cha...
910
911
912
  				POWER_SUPPLY_PROP_CURRENT_NOW, val);
  		break;
  	case POWER_SUPPLY_PROP_TEMP:
ad3d13eee   Donggeun Kim   power_supply: Cha...
913
  	case POWER_SUPPLY_PROP_TEMP_AMBIENT:
5c49a6256   Jonghwa Lee   charger-manager: ...
914
  		return cm_get_battery_temperature(cm, &val->intval);
ad3d13eee   Donggeun Kim   power_supply: Cha...
915
  	case POWER_SUPPLY_PROP_CAPACITY:
ad3d13eee   Donggeun Kim   power_supply: Cha...
916
917
918
919
920
  		if (!is_batt_present(cm)) {
  			/* There is no battery. Assume 100% */
  			val->intval = 100;
  			break;
  		}
b43eb35ab   Krzysztof Kozlowski   power_supply: cha...
921
922
923
924
925
  		fuel_gauge = power_supply_get_by_name(cm->desc->psy_fuel_gauge);
  		if (!fuel_gauge) {
  			ret = -ENODEV;
  			break;
  		}
b70229bca   Krzysztof Kozlowski   power_supply: cha...
926
  		ret = power_supply_get_property(fuel_gauge,
ad3d13eee   Donggeun Kim   power_supply: Cha...
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
  					POWER_SUPPLY_PROP_CAPACITY, val);
  		if (ret)
  			break;
  
  		if (val->intval > 100) {
  			val->intval = 100;
  			break;
  		}
  		if (val->intval < 0)
  			val->intval = 0;
  
  		/* Do not adjust SOC when charging: voltage is overrated */
  		if (is_charging(cm))
  			break;
  
  		/*
  		 * If the capacity value is inconsistent, calibrate it base on
  		 * the battery voltage values and the thresholds given as desc
  		 */
  		ret = get_batt_uV(cm, &uV);
  		if (ret) {
  			/* Voltage information not available. No calibration */
  			ret = 0;
  			break;
  		}
  
  		if (desc->fullbatt_uV > 0 && uV >= desc->fullbatt_uV &&
  		    !is_charging(cm)) {
  			val->intval = 100;
  			break;
  		}
  
  		break;
  	case POWER_SUPPLY_PROP_ONLINE:
  		if (is_ext_pwr_online(cm))
  			val->intval = 1;
  		else
  			val->intval = 0;
  		break;
  	case POWER_SUPPLY_PROP_CHARGE_FULL:
2ed9e9b65   Chanwoo Choi   charger-manager: ...
967
  		if (is_full_charged(cm))
ad3d13eee   Donggeun Kim   power_supply: Cha...
968
  			val->intval = 1;
2ed9e9b65   Chanwoo Choi   charger-manager: ...
969
970
  		else
  			val->intval = 0;
ad3d13eee   Donggeun Kim   power_supply: Cha...
971
972
973
974
  		ret = 0;
  		break;
  	case POWER_SUPPLY_PROP_CHARGE_NOW:
  		if (is_charging(cm)) {
bdbe81445   Krzysztof Kozlowski   power: charger-ma...
975
976
977
978
979
980
  			fuel_gauge = power_supply_get_by_name(
  					cm->desc->psy_fuel_gauge);
  			if (!fuel_gauge) {
  				ret = -ENODEV;
  				break;
  			}
b70229bca   Krzysztof Kozlowski   power_supply: cha...
981
  			ret = power_supply_get_property(fuel_gauge,
ad3d13eee   Donggeun Kim   power_supply: Cha...
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
  						POWER_SUPPLY_PROP_CHARGE_NOW,
  						val);
  			if (ret) {
  				val->intval = 1;
  				ret = 0;
  			} else {
  				/* If CHARGE_NOW is supplied, use it */
  				val->intval = (val->intval > 0) ?
  						val->intval : 1;
  			}
  		} else {
  			val->intval = 0;
  		}
  		break;
  	default:
  		return -EINVAL;
  	}
b43eb35ab   Krzysztof Kozlowski   power_supply: cha...
999
1000
  	if (fuel_gauge)
  		power_supply_put(fuel_gauge);
ad3d13eee   Donggeun Kim   power_supply: Cha...
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
  	return ret;
  }
  
  #define NUM_CHARGER_PSY_OPTIONAL	(4)
  static enum power_supply_property default_charger_props[] = {
  	/* Guaranteed to provide */
  	POWER_SUPPLY_PROP_STATUS,
  	POWER_SUPPLY_PROP_HEALTH,
  	POWER_SUPPLY_PROP_PRESENT,
  	POWER_SUPPLY_PROP_VOLTAGE_NOW,
  	POWER_SUPPLY_PROP_CAPACITY,
  	POWER_SUPPLY_PROP_ONLINE,
  	POWER_SUPPLY_PROP_CHARGE_FULL,
  	/*
  	 * Optional properties are:
  	 * POWER_SUPPLY_PROP_CHARGE_NOW,
  	 * POWER_SUPPLY_PROP_CURRENT_NOW,
  	 * POWER_SUPPLY_PROP_TEMP, and
  	 * POWER_SUPPLY_PROP_TEMP_AMBIENT,
  	 */
  };
297d716f6   Krzysztof Kozlowski   power_supply: Cha...
1022
  static const struct power_supply_desc psy_default = {
ad3d13eee   Donggeun Kim   power_supply: Cha...
1023
1024
1025
1026
1027
  	.name = "battery",
  	.type = POWER_SUPPLY_TYPE_BATTERY,
  	.properties = default_charger_props,
  	.num_properties = ARRAY_SIZE(default_charger_props),
  	.get_property = charger_get_property,
ba9c91825   Krzysztof Kozlowski   power: charger-ma...
1028
  	.no_thermal = true,
ad3d13eee   Donggeun Kim   power_supply: Cha...
1029
  };
3bb3dbbd5   Donggeun Kim   power_supply: Add...
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
  /**
   * cm_setup_timer - For in-suspend monitoring setup wakeup alarm
   *		    for suspend_again.
   *
   * Returns true if the alarm is set for Charger Manager to use.
   * Returns false if
   *	cm_setup_timer fails to set an alarm,
   *	cm_setup_timer does not need to set an alarm for Charger Manager,
   *	or an alarm previously configured is to be used.
   */
  static bool cm_setup_timer(void)
  {
  	struct charger_manager *cm;
  	unsigned int wakeup_ms = UINT_MAX;
c1155c64e   Jonghwa Lee   power: charger-ma...
1044
  	int timer_req = 0;
3bb3dbbd5   Donggeun Kim   power_supply: Add...
1045

c1155c64e   Jonghwa Lee   power: charger-ma...
1046
1047
1048
  	if (time_after(next_polling, jiffies))
  		CM_MIN_VALID(wakeup_ms,
  			jiffies_to_msecs(next_polling - jiffies));
3bb3dbbd5   Donggeun Kim   power_supply: Add...
1049

c1155c64e   Jonghwa Lee   power: charger-ma...
1050
  	mutex_lock(&cm_list_mtx);
3bb3dbbd5   Donggeun Kim   power_supply: Add...
1051
  	list_for_each_entry(cm, &cm_list, entry) {
d829dc75b   Chanwoo Choi   charger-manager: ...
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
  		unsigned int fbchk_ms = 0;
  
  		/* fullbatt_vchk is required. setup timer for that */
  		if (cm->fullbatt_vchk_jiffies_at) {
  			fbchk_ms = jiffies_to_msecs(cm->fullbatt_vchk_jiffies_at
  						    - jiffies);
  			if (time_is_before_eq_jiffies(
  				cm->fullbatt_vchk_jiffies_at) ||
  				msecs_to_jiffies(fbchk_ms) < CM_JIFFIES_SMALL) {
  				fullbatt_vchk(&cm->fullbatt_vchk_work.work);
  				fbchk_ms = 0;
  			}
  		}
  		CM_MIN_VALID(wakeup_ms, fbchk_ms);
3bb3dbbd5   Donggeun Kim   power_supply: Add...
1066
1067
1068
  		/* Skip if polling is not required for this CM */
  		if (!is_polling_required(cm) && !cm->emergency_stop)
  			continue;
c1155c64e   Jonghwa Lee   power: charger-ma...
1069
  		timer_req++;
3bb3dbbd5   Donggeun Kim   power_supply: Add...
1070
1071
1072
1073
  		if (cm->desc->polling_interval_ms == 0)
  			continue;
  		CM_MIN_VALID(wakeup_ms, cm->desc->polling_interval_ms);
  	}
3bb3dbbd5   Donggeun Kim   power_supply: Add...
1074
  	mutex_unlock(&cm_list_mtx);
c1155c64e   Jonghwa Lee   power: charger-ma...
1075
1076
  	if (timer_req && cm_timer) {
  		ktime_t now, add;
3bb3dbbd5   Donggeun Kim   power_supply: Add...
1077

c1155c64e   Jonghwa Lee   power: charger-ma...
1078
1079
1080
1081
1082
1083
1084
  		/*
  		 * Set alarm with the polling interval (wakeup_ms)
  		 * The alarm time should be NOW + CM_RTC_SMALL or later.
  		 */
  		if (wakeup_ms == UINT_MAX ||
  			wakeup_ms < CM_RTC_SMALL * MSEC_PER_SEC)
  			wakeup_ms = 2 * CM_RTC_SMALL * MSEC_PER_SEC;
3bb3dbbd5   Donggeun Kim   power_supply: Add...
1085

c1155c64e   Jonghwa Lee   power: charger-ma...
1086
1087
  		pr_info("Charger Manager wakeup timer: %u ms
  ", wakeup_ms);
3bb3dbbd5   Donggeun Kim   power_supply: Add...
1088

c1155c64e   Jonghwa Lee   power: charger-ma...
1089
1090
1091
1092
  		now = ktime_get_boottime();
  		add = ktime_set(wakeup_ms / MSEC_PER_SEC,
  				(wakeup_ms % MSEC_PER_SEC) * NSEC_PER_MSEC);
  		alarm_start(cm_timer, ktime_add(now, add));
3bb3dbbd5   Donggeun Kim   power_supply: Add...
1093

c1155c64e   Jonghwa Lee   power: charger-ma...
1094
  		cm_suspend_duration_ms = wakeup_ms;
3bb3dbbd5   Donggeun Kim   power_supply: Add...
1095

c1155c64e   Jonghwa Lee   power: charger-ma...
1096
  		return true;
3bb3dbbd5   Donggeun Kim   power_supply: Add...
1097
  	}
3bb3dbbd5   Donggeun Kim   power_supply: Add...
1098
1099
  	return false;
  }
bee737bcc   Chanwoo Choi   charger-manager: ...
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
  /**
   * charger_extcon_work - enable/diable charger according to the state
   *			of charger cable
   *
   * @work: work_struct of the function charger_extcon_work.
   */
  static void charger_extcon_work(struct work_struct *work)
  {
  	struct charger_cable *cable =
  			container_of(work, struct charger_cable, wq);
45cd4fb28   Chanwoo Choi   charger-manager: ...
1110
1111
1112
1113
1114
1115
1116
1117
  	int ret;
  
  	if (cable->attached && cable->min_uA != 0 && cable->max_uA != 0) {
  		ret = regulator_set_current_limit(cable->charger->consumer,
  					cable->min_uA, cable->max_uA);
  		if (ret < 0) {
  			pr_err("Cannot set current limit of %s (%s)
  ",
e5409cbd8   Joe Perches   charger-manager: ...
1118
  			       cable->charger->regulator_name, cable->name);
45cd4fb28   Chanwoo Choi   charger-manager: ...
1119
1120
1121
1122
1123
  			return;
  		}
  
  		pr_info("Set current limit of %s : %duA ~ %duA
  ",
e5409cbd8   Joe Perches   charger-manager: ...
1124
1125
  			cable->charger->regulator_name,
  			cable->min_uA, cable->max_uA);
45cd4fb28   Chanwoo Choi   charger-manager: ...
1126
  	}
bee737bcc   Chanwoo Choi   charger-manager: ...
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
  
  	try_charger_enable(cable->cm, cable->attached);
  }
  
  /**
   * charger_extcon_notifier - receive the state of charger cable
   *			when registered cable is attached or detached.
   *
   * @self: the notifier block of the charger_extcon_notifier.
   * @event: the cable state.
   * @ptr: the data pointer of notifier block.
   */
  static int charger_extcon_notifier(struct notifier_block *self,
  			unsigned long event, void *ptr)
  {
  	struct charger_cable *cable =
  		container_of(self, struct charger_cable, nb);
2ed9e9b65   Chanwoo Choi   charger-manager: ...
1144
1145
1146
1147
  	/*
  	 * The newly state of charger cable.
  	 * If cable is attached, cable->attached is true.
  	 */
bee737bcc   Chanwoo Choi   charger-manager: ...
1148
  	cable->attached = event;
2ed9e9b65   Chanwoo Choi   charger-manager: ...
1149
1150
1151
1152
1153
1154
  
  	/*
  	 * Setup monitoring to check battery state
  	 * when charger cable is attached.
  	 */
  	if (cable->attached && is_polling_required(cable->cm)) {
2fbb520d2   Tejun Heo   charger_manager: ...
1155
  		cancel_work_sync(&setup_polling);
2ed9e9b65   Chanwoo Choi   charger-manager: ...
1156
1157
1158
1159
1160
1161
1162
  		schedule_work(&setup_polling);
  	}
  
  	/*
  	 * Setup work for controlling charger(regulator)
  	 * according to charger cable.
  	 */
bee737bcc   Chanwoo Choi   charger-manager: ...
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
  	schedule_work(&cable->wq);
  
  	return NOTIFY_DONE;
  }
  
  /**
   * charger_extcon_init - register external connector to use it
   *			as the charger cable
   *
   * @cm: the Charger Manager representing the battery.
   * @cable: the Charger cable representing the external connector.
   */
  static int charger_extcon_init(struct charger_manager *cm,
  		struct charger_cable *cable)
  {
  	int ret = 0;
  
  	/*
  	 * Charger manager use Extcon framework to identify
  	 * the charger cable among various external connector
  	 * cable (e.g., TA, USB, MHL, Dock).
  	 */
  	INIT_WORK(&cable->wq, charger_extcon_work);
  	cable->nb.notifier_call = charger_extcon_notifier;
  	ret = extcon_register_interest(&cable->extcon_dev,
  			cable->extcon_name, cable->name, &cable->nb);
  	if (ret < 0) {
e5409cbd8   Joe Perches   charger-manager: ...
1190
1191
1192
  		pr_info("Cannot register extcon_dev for %s(cable: %s)
  ",
  			cable->extcon_name, cable->name);
bee737bcc   Chanwoo Choi   charger-manager: ...
1193
1194
1195
1196
1197
  		ret = -EINVAL;
  	}
  
  	return ret;
  }
41468a111   Chanwoo Choi   charger-manager: ...
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
  /**
   * charger_manager_register_extcon - Register extcon device to recevie state
   *				     of charger cable.
   * @cm: the Charger Manager representing the battery.
   *
   * This function support EXTCON(External Connector) subsystem to detect the
   * state of charger cables for enabling or disabling charger(regulator) and
   * select the charger cable for charging among a number of external cable
   * according to policy of H/W board.
   */
  static int charger_manager_register_extcon(struct charger_manager *cm)
  {
  	struct charger_desc *desc = cm->desc;
  	struct charger_regulator *charger;
  	int ret = 0;
  	int i;
  	int j;
  
  	for (i = 0; i < desc->num_charger_regulators; i++) {
  		charger = &desc->charger_regulators[i];
  
  		charger->consumer = regulator_get(cm->dev,
  					charger->regulator_name);
5a6c22084   Jonghwa Lee   charger-manager: ...
1221
  		if (IS_ERR(charger->consumer)) {
e5409cbd8   Joe Perches   charger-manager: ...
1222
1223
1224
  			dev_err(cm->dev, "Cannot find charger(%s)
  ",
  				charger->regulator_name);
5a6c22084   Jonghwa Lee   charger-manager: ...
1225
  			return PTR_ERR(charger->consumer);
41468a111   Chanwoo Choi   charger-manager: ...
1226
1227
1228
1229
1230
1231
1232
1233
  		}
  		charger->cm = cm;
  
  		for (j = 0; j < charger->num_cables; j++) {
  			struct charger_cable *cable = &charger->cables[j];
  
  			ret = charger_extcon_init(cm, cable);
  			if (ret < 0) {
e5409cbd8   Joe Perches   charger-manager: ...
1234
1235
1236
  				dev_err(cm->dev, "Cannot initialize charger(%s)
  ",
  					charger->regulator_name);
41468a111   Chanwoo Choi   charger-manager: ...
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
  				goto err;
  			}
  			cable->charger = charger;
  			cable->cm = cm;
  		}
  	}
  
  err:
  	return ret;
  }
3950c7865   Chanwoo Choi   charger-manager: ...
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
  /* help function of sysfs node to control charger(regulator) */
  static ssize_t charger_name_show(struct device *dev,
  				struct device_attribute *attr, char *buf)
  {
  	struct charger_regulator *charger
  		= container_of(attr, struct charger_regulator, attr_name);
  
  	return sprintf(buf, "%s
  ", charger->regulator_name);
  }
  
  static ssize_t charger_state_show(struct device *dev,
  				struct device_attribute *attr, char *buf)
  {
  	struct charger_regulator *charger
  		= container_of(attr, struct charger_regulator, attr_state);
  	int state = 0;
  
  	if (!charger->externally_control)
  		state = regulator_is_enabled(charger->consumer);
  
  	return sprintf(buf, "%s
  ", state ? "enabled" : "disabled");
  }
  
  static ssize_t charger_externally_control_show(struct device *dev,
  				struct device_attribute *attr, char *buf)
  {
  	struct charger_regulator *charger = container_of(attr,
  			struct charger_regulator, attr_externally_control);
  
  	return sprintf(buf, "%d
  ", charger->externally_control);
  }
  
  static ssize_t charger_externally_control_store(struct device *dev,
  				struct device_attribute *attr, const char *buf,
  				size_t count)
  {
  	struct charger_regulator *charger
  		= container_of(attr, struct charger_regulator,
  					attr_externally_control);
  	struct charger_manager *cm = charger->cm;
  	struct charger_desc *desc = cm->desc;
  	int i;
  	int ret;
  	int externally_control;
  	int chargers_externally_control = 1;
  
  	ret = sscanf(buf, "%d", &externally_control);
  	if (ret == 0) {
  		ret = -EINVAL;
  		return ret;
  	}
  
  	if (!externally_control) {
  		charger->externally_control = 0;
  		return count;
  	}
  
  	for (i = 0; i < desc->num_charger_regulators; i++) {
  		if (&desc->charger_regulators[i] != charger &&
41468a111   Chanwoo Choi   charger-manager: ...
1309
  			!desc->charger_regulators[i].externally_control) {
3950c7865   Chanwoo Choi   charger-manager: ...
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
  			/*
  			 * At least, one charger is controlled by
  			 * charger-manager
  			 */
  			chargers_externally_control = 0;
  			break;
  		}
  	}
  
  	if (!chargers_externally_control) {
  		if (cm->charger_enabled) {
  			try_charger_enable(charger->cm, false);
  			charger->externally_control = externally_control;
  			try_charger_enable(charger->cm, true);
  		} else {
  			charger->externally_control = externally_control;
  		}
  	} else {
  		dev_warn(cm->dev,
e5409cbd8   Joe Perches   charger-manager: ...
1329
1330
1331
  			 "'%s' regulator should be controlled in charger-manager because charger-manager must need at least one charger for charging
  ",
  			 charger->regulator_name);
3950c7865   Chanwoo Choi   charger-manager: ...
1332
1333
1334
1335
  	}
  
  	return count;
  }
41468a111   Chanwoo Choi   charger-manager: ...
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
  /**
   * charger_manager_register_sysfs - Register sysfs entry for each charger
   * @cm: the Charger Manager representing the battery.
   *
   * This function add sysfs entry for charger(regulator) to control charger from
   * user-space. If some development board use one more chargers for charging
   * but only need one charger on specific case which is dependent on user
   * scenario or hardware restrictions, the user enter 1 or 0(zero) to '/sys/
   * class/power_supply/battery/charger.[index]/externally_control'. For example,
   * if user enter 1 to 'sys/class/power_supply/battery/charger.[index]/
   * externally_control, this charger isn't controlled from charger-manager and
   * always stay off state of regulator.
   */
  static int charger_manager_register_sysfs(struct charger_manager *cm)
  {
  	struct charger_desc *desc = cm->desc;
  	struct charger_regulator *charger;
  	int chargers_externally_control = 1;
  	char buf[11];
  	char *str;
  	int ret = 0;
  	int i;
  
  	/* Create sysfs entry to control charger(regulator) */
  	for (i = 0; i < desc->num_charger_regulators; i++) {
  		charger = &desc->charger_regulators[i];
  
  		snprintf(buf, 10, "charger.%d", i);
883c10a9d   Jonghwa Lee   charger-manager :...
1364
1365
  		str = devm_kzalloc(cm->dev,
  				sizeof(char) * (strlen(buf) + 1), GFP_KERNEL);
41468a111   Chanwoo Choi   charger-manager: ...
1366
  		if (!str) {
41468a111   Chanwoo Choi   charger-manager: ...
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
  			ret = -ENOMEM;
  			goto err;
  		}
  		strcpy(str, buf);
  
  		charger->attrs[0] = &charger->attr_name.attr;
  		charger->attrs[1] = &charger->attr_state.attr;
  		charger->attrs[2] = &charger->attr_externally_control.attr;
  		charger->attrs[3] = NULL;
  		charger->attr_g.name = str;
  		charger->attr_g.attrs = charger->attrs;
  
  		sysfs_attr_init(&charger->attr_name.attr);
  		charger->attr_name.attr.name = "name";
  		charger->attr_name.attr.mode = 0444;
  		charger->attr_name.show = charger_name_show;
  
  		sysfs_attr_init(&charger->attr_state.attr);
  		charger->attr_state.attr.name = "state";
  		charger->attr_state.attr.mode = 0444;
  		charger->attr_state.show = charger_state_show;
  
  		sysfs_attr_init(&charger->attr_externally_control.attr);
  		charger->attr_externally_control.attr.name
  				= "externally_control";
  		charger->attr_externally_control.attr.mode = 0644;
  		charger->attr_externally_control.show
  				= charger_externally_control_show;
  		charger->attr_externally_control.store
  				= charger_externally_control_store;
  
  		if (!desc->charger_regulators[i].externally_control ||
  				!chargers_externally_control)
  			chargers_externally_control = 0;
e5409cbd8   Joe Perches   charger-manager: ...
1401
1402
1403
  		dev_info(cm->dev, "'%s' regulator's externally_control is %d
  ",
  			 charger->regulator_name, charger->externally_control);
41468a111   Chanwoo Choi   charger-manager: ...
1404

297d716f6   Krzysztof Kozlowski   power_supply: Cha...
1405
  		ret = sysfs_create_group(&cm->charger_psy->dev.kobj,
41468a111   Chanwoo Choi   charger-manager: ...
1406
1407
  					&charger->attr_g);
  		if (ret < 0) {
e5409cbd8   Joe Perches   charger-manager: ...
1408
1409
1410
  			dev_err(cm->dev, "Cannot create sysfs entry of %s regulator
  ",
  				charger->regulator_name);
41468a111   Chanwoo Choi   charger-manager: ...
1411
1412
1413
1414
1415
1416
  			ret = -EINVAL;
  			goto err;
  		}
  	}
  
  	if (chargers_externally_control) {
e5409cbd8   Joe Perches   charger-manager: ...
1417
1418
  		dev_err(cm->dev, "Cannot register regulator because charger-manager must need at least one charger for charging battery
  ");
41468a111   Chanwoo Choi   charger-manager: ...
1419
1420
1421
1422
1423
1424
1425
  		ret = -EINVAL;
  		goto err;
  	}
  
  err:
  	return ret;
  }
bdbe81445   Krzysztof Kozlowski   power: charger-ma...
1426
1427
  static int cm_init_thermal_data(struct charger_manager *cm,
  		struct power_supply *fuel_gauge)
5c49a6256   Jonghwa Lee   charger-manager: ...
1428
1429
1430
1431
1432
1433
  {
  	struct charger_desc *desc = cm->desc;
  	union power_supply_propval val;
  	int ret;
  
  	/* Verify whether fuel gauge provides battery temperature */
b70229bca   Krzysztof Kozlowski   power_supply: cha...
1434
  	ret = power_supply_get_property(fuel_gauge,
5c49a6256   Jonghwa Lee   charger-manager: ...
1435
1436
1437
  					POWER_SUPPLY_PROP_TEMP, &val);
  
  	if (!ret) {
297d716f6   Krzysztof Kozlowski   power_supply: Cha...
1438
  		cm->charger_psy_desc.properties[cm->charger_psy_desc.num_properties] =
5c49a6256   Jonghwa Lee   charger-manager: ...
1439
  				POWER_SUPPLY_PROP_TEMP;
297d716f6   Krzysztof Kozlowski   power_supply: Cha...
1440
  		cm->charger_psy_desc.num_properties++;
5c49a6256   Jonghwa Lee   charger-manager: ...
1441
1442
1443
  		cm->desc->measure_battery_temp = true;
  	}
  #ifdef CONFIG_THERMAL
5c49a6256   Jonghwa Lee   charger-manager: ...
1444
1445
1446
1447
1448
1449
1450
  	if (ret && desc->thermal_zone) {
  		cm->tzd_batt =
  			thermal_zone_get_zone_by_name(desc->thermal_zone);
  		if (IS_ERR(cm->tzd_batt))
  			return PTR_ERR(cm->tzd_batt);
  
  		/* Use external thermometer */
297d716f6   Krzysztof Kozlowski   power_supply: Cha...
1451
  		cm->charger_psy_desc.properties[cm->charger_psy_desc.num_properties] =
5c49a6256   Jonghwa Lee   charger-manager: ...
1452
  				POWER_SUPPLY_PROP_TEMP_AMBIENT;
297d716f6   Krzysztof Kozlowski   power_supply: Cha...
1453
  		cm->charger_psy_desc.num_properties++;
5c49a6256   Jonghwa Lee   charger-manager: ...
1454
1455
1456
1457
1458
1459
1460
1461
1462
1463
1464
1465
1466
1467
  		cm->desc->measure_battery_temp = true;
  		ret = 0;
  	}
  #endif
  	if (cm->desc->measure_battery_temp) {
  		/* NOTICE : Default allowable minimum charge temperature is 0 */
  		if (!desc->temp_max)
  			desc->temp_max = CM_DEFAULT_CHARGE_TEMP_MAX;
  		if (!desc->temp_diff)
  			desc->temp_diff = CM_DEFAULT_RECHARGE_TEMP_DIFF;
  	}
  
  	return ret;
  }
8fb088550   Fabian Frederick   power: constify o...
1468
  static const struct of_device_id charger_manager_match[] = {
856ee6115   Jonghwa Lee   charger-manager: ...
1469
1470
1471
1472
1473
  	{
  		.compatible = "charger-manager",
  	},
  	{},
  };
434a09f9c   Anton Vorontsov   charger-manager: ...
1474
  static struct charger_desc *of_cm_parse_desc(struct device *dev)
856ee6115   Jonghwa Lee   charger-manager: ...
1475
1476
1477
1478
1479
1480
1481
1482
1483
1484
1485
1486
1487
1488
1489
1490
1491
1492
1493
1494
1495
1496
1497
1498
1499
1500
1501
1502
1503
1504
1505
1506
1507
1508
1509
1510
1511
1512
1513
1514
1515
1516
1517
1518
1519
1520
1521
1522
1523
1524
1525
1526
1527
1528
1529
1530
1531
1532
1533
1534
1535
1536
1537
1538
1539
1540
1541
1542
1543
1544
1545
1546
1547
1548
1549
1550
1551
1552
1553
1554
1555
1556
1557
1558
1559
1560
1561
1562
1563
  {
  	struct charger_desc *desc;
  	struct device_node *np = dev->of_node;
  	u32 poll_mode = CM_POLL_DISABLE;
  	u32 battery_stat = CM_NO_BATTERY;
  	int num_chgs = 0;
  
  	desc = devm_kzalloc(dev, sizeof(*desc), GFP_KERNEL);
  	if (!desc)
  		return ERR_PTR(-ENOMEM);
  
  	of_property_read_string(np, "cm-name", &desc->psy_name);
  
  	of_property_read_u32(np, "cm-poll-mode", &poll_mode);
  	desc->polling_mode = poll_mode;
  
  	of_property_read_u32(np, "cm-poll-interval",
  				&desc->polling_interval_ms);
  
  	of_property_read_u32(np, "cm-fullbatt-vchkdrop-ms",
  					&desc->fullbatt_vchkdrop_ms);
  	of_property_read_u32(np, "cm-fullbatt-vchkdrop-volt",
  					&desc->fullbatt_vchkdrop_uV);
  	of_property_read_u32(np, "cm-fullbatt-voltage", &desc->fullbatt_uV);
  	of_property_read_u32(np, "cm-fullbatt-soc", &desc->fullbatt_soc);
  	of_property_read_u32(np, "cm-fullbatt-capacity",
  					&desc->fullbatt_full_capacity);
  
  	of_property_read_u32(np, "cm-battery-stat", &battery_stat);
  	desc->battery_present = battery_stat;
  
  	/* chargers */
  	of_property_read_u32(np, "cm-num-chargers", &num_chgs);
  	if (num_chgs) {
  		/* Allocate empty bin at the tail of array */
  		desc->psy_charger_stat = devm_kzalloc(dev, sizeof(char *)
  						* (num_chgs + 1), GFP_KERNEL);
  		if (desc->psy_charger_stat) {
  			int i;
  			for (i = 0; i < num_chgs; i++)
  				of_property_read_string_index(np, "cm-chargers",
  						i, &desc->psy_charger_stat[i]);
  		} else {
  			return ERR_PTR(-ENOMEM);
  		}
  	}
  
  	of_property_read_string(np, "cm-fuel-gauge", &desc->psy_fuel_gauge);
  
  	of_property_read_string(np, "cm-thermal-zone", &desc->thermal_zone);
  
  	of_property_read_u32(np, "cm-battery-cold", &desc->temp_min);
  	if (of_get_property(np, "cm-battery-cold-in-minus", NULL))
  		desc->temp_min *= -1;
  	of_property_read_u32(np, "cm-battery-hot", &desc->temp_max);
  	of_property_read_u32(np, "cm-battery-temp-diff", &desc->temp_diff);
  
  	of_property_read_u32(np, "cm-charging-max",
  				&desc->charging_max_duration_ms);
  	of_property_read_u32(np, "cm-discharging-max",
  				&desc->discharging_max_duration_ms);
  
  	/* battery charger regualtors */
  	desc->num_charger_regulators = of_get_child_count(np);
  	if (desc->num_charger_regulators) {
  		struct charger_regulator *chg_regs;
  		struct device_node *child;
  
  		chg_regs = devm_kzalloc(dev, sizeof(*chg_regs)
  					* desc->num_charger_regulators,
  					GFP_KERNEL);
  		if (!chg_regs)
  			return ERR_PTR(-ENOMEM);
  
  		desc->charger_regulators = chg_regs;
  
  		for_each_child_of_node(np, child) {
  			struct charger_cable *cables;
  			struct device_node *_child;
  
  			of_property_read_string(child, "cm-regulator-name",
  					&chg_regs->regulator_name);
  
  			/* charger cables */
  			chg_regs->num_cables = of_get_child_count(child);
  			if (chg_regs->num_cables) {
  				cables = devm_kzalloc(dev, sizeof(*cables)
  						* chg_regs->num_cables,
  						GFP_KERNEL);
8e5cfb74b   Julia Lawall   power_supply: cha...
1564
1565
  				if (!cables) {
  					of_node_put(child);
856ee6115   Jonghwa Lee   charger-manager: ...
1566
  					return ERR_PTR(-ENOMEM);
8e5cfb74b   Julia Lawall   power_supply: cha...
1567
  				}
856ee6115   Jonghwa Lee   charger-manager: ...
1568
1569
1570
1571
1572
1573
1574
1575
1576
1577
1578
1579
1580
1581
1582
1583
1584
1585
1586
1587
1588
1589
1590
1591
1592
1593
1594
1595
  
  				chg_regs->cables = cables;
  
  				for_each_child_of_node(child, _child) {
  					of_property_read_string(_child,
  					"cm-cable-name", &cables->name);
  					of_property_read_string(_child,
  					"cm-cable-extcon",
  					&cables->extcon_name);
  					of_property_read_u32(_child,
  					"cm-cable-min",
  					&cables->min_uA);
  					of_property_read_u32(_child,
  					"cm-cable-max",
  					&cables->max_uA);
  					cables++;
  				}
  			}
  			chg_regs++;
  		}
  	}
  	return desc;
  }
  
  static inline struct charger_desc *cm_get_drv_data(struct platform_device *pdev)
  {
  	if (pdev->dev.of_node)
  		return of_cm_parse_desc(&pdev->dev);
86515b7de   Jingoo Han   power: charger-ma...
1596
  	return dev_get_platdata(&pdev->dev);
856ee6115   Jonghwa Lee   charger-manager: ...
1597
  }
c1155c64e   Jonghwa Lee   power: charger-ma...
1598
1599
1600
1601
1602
  static enum alarmtimer_restart cm_timer_func(struct alarm *alarm, ktime_t now)
  {
  	cm_timer_set = false;
  	return ALARMTIMER_NORESTART;
  }
3bb3dbbd5   Donggeun Kim   power_supply: Add...
1603
1604
  static int charger_manager_probe(struct platform_device *pdev)
  {
856ee6115   Jonghwa Lee   charger-manager: ...
1605
  	struct charger_desc *desc = cm_get_drv_data(pdev);
3bb3dbbd5   Donggeun Kim   power_supply: Add...
1606
1607
  	struct charger_manager *cm;
  	int ret = 0, i = 0;
bee737bcc   Chanwoo Choi   charger-manager: ...
1608
  	int j = 0;
ad3d13eee   Donggeun Kim   power_supply: Cha...
1609
  	union power_supply_propval val;
bdbe81445   Krzysztof Kozlowski   power: charger-ma...
1610
  	struct power_supply *fuel_gauge;
297d716f6   Krzysztof Kozlowski   power_supply: Cha...
1611
  	struct power_supply_config psy_cfg = {};
3bb3dbbd5   Donggeun Kim   power_supply: Add...
1612

c6738d06a   Chanwoo Choi   power: charger-ma...
1613
  	if (IS_ERR(desc)) {
e5409cbd8   Joe Perches   charger-manager: ...
1614
1615
  		dev_err(&pdev->dev, "No platform data (desc) found
  ");
883c10a9d   Jonghwa Lee   charger-manager :...
1616
  		return -ENODEV;
3bb3dbbd5   Donggeun Kim   power_supply: Add...
1617
  	}
883c10a9d   Jonghwa Lee   charger-manager :...
1618
1619
1620
1621
  	cm = devm_kzalloc(&pdev->dev,
  			sizeof(struct charger_manager),	GFP_KERNEL);
  	if (!cm)
  		return -ENOMEM;
3bb3dbbd5   Donggeun Kim   power_supply: Add...
1622
1623
1624
  
  	/* Basic Values. Unspecified are Null or 0 */
  	cm->dev = &pdev->dev;
883c10a9d   Jonghwa Lee   charger-manager :...
1625
  	cm->desc = desc;
297d716f6   Krzysztof Kozlowski   power_supply: Cha...
1626
  	psy_cfg.drv_data = cm;
3bb3dbbd5   Donggeun Kim   power_supply: Add...
1627

c1155c64e   Jonghwa Lee   power: charger-ma...
1628
1629
1630
1631
1632
  	/* Initialize alarm timer */
  	if (alarmtimer_get_rtcdev()) {
  		cm_timer = devm_kzalloc(cm->dev, sizeof(*cm_timer), GFP_KERNEL);
  		alarm_init(cm_timer, ALARM_BOOTTIME, cm_timer_func);
  	}
d829dc75b   Chanwoo Choi   charger-manager: ...
1633
1634
1635
1636
1637
  	/*
  	 * The following two do not need to be errors.
  	 * Users may intentionally ignore those two features.
  	 */
  	if (desc->fullbatt_uV == 0) {
e5409cbd8   Joe Perches   charger-manager: ...
1638
1639
  		dev_info(&pdev->dev, "Ignoring full-battery voltage threshold as it is not supplied
  ");
d829dc75b   Chanwoo Choi   charger-manager: ...
1640
1641
  	}
  	if (!desc->fullbatt_vchkdrop_ms || !desc->fullbatt_vchkdrop_uV) {
e5409cbd8   Joe Perches   charger-manager: ...
1642
1643
  		dev_info(&pdev->dev, "Disabling full-battery voltage drop checking mechanism as it is not supplied
  ");
d829dc75b   Chanwoo Choi   charger-manager: ...
1644
1645
1646
  		desc->fullbatt_vchkdrop_ms = 0;
  		desc->fullbatt_vchkdrop_uV = 0;
  	}
2ed9e9b65   Chanwoo Choi   charger-manager: ...
1647
  	if (desc->fullbatt_soc == 0) {
e5409cbd8   Joe Perches   charger-manager: ...
1648
1649
  		dev_info(&pdev->dev, "Ignoring full-battery soc(state of charge) threshold as it is not supplied
  ");
2ed9e9b65   Chanwoo Choi   charger-manager: ...
1650
1651
  	}
  	if (desc->fullbatt_full_capacity == 0) {
e5409cbd8   Joe Perches   charger-manager: ...
1652
1653
  		dev_info(&pdev->dev, "Ignoring full-battery full capacity threshold as it is not supplied
  ");
2ed9e9b65   Chanwoo Choi   charger-manager: ...
1654
  	}
d829dc75b   Chanwoo Choi   charger-manager: ...
1655

3bb3dbbd5   Donggeun Kim   power_supply: Add...
1656
  	if (!desc->charger_regulators || desc->num_charger_regulators < 1) {
e5409cbd8   Joe Perches   charger-manager: ...
1657
1658
  		dev_err(&pdev->dev, "charger_regulators undefined
  ");
883c10a9d   Jonghwa Lee   charger-manager :...
1659
  		return -EINVAL;
3bb3dbbd5   Donggeun Kim   power_supply: Add...
1660
1661
1662
  	}
  
  	if (!desc->psy_charger_stat || !desc->psy_charger_stat[0]) {
e5409cbd8   Joe Perches   charger-manager: ...
1663
1664
  		dev_err(&pdev->dev, "No power supply defined
  ");
883c10a9d   Jonghwa Lee   charger-manager :...
1665
  		return -EINVAL;
3bb3dbbd5   Donggeun Kim   power_supply: Add...
1666
  	}
661a88860   Krzysztof Kozlowski   power: charger-ma...
1667
1668
1669
1670
1671
  	if (!desc->psy_fuel_gauge) {
  		dev_err(&pdev->dev, "No fuel gauge power supply defined
  ");
  		return -EINVAL;
  	}
3bb3dbbd5   Donggeun Kim   power_supply: Add...
1672
1673
1674
  	/* Counting index only */
  	while (desc->psy_charger_stat[i])
  		i++;
cdaf3e153   Krzysztof Kozlowski   power: charger-ma...
1675
  	/* Check if charger's supplies are present at probe */
3bb3dbbd5   Donggeun Kim   power_supply: Add...
1676
  	for (i = 0; desc->psy_charger_stat[i]; i++) {
cdaf3e153   Krzysztof Kozlowski   power: charger-ma...
1677
1678
1679
1680
  		struct power_supply *psy;
  
  		psy = power_supply_get_by_name(desc->psy_charger_stat[i]);
  		if (!psy) {
e5409cbd8   Joe Perches   charger-manager: ...
1681
1682
1683
  			dev_err(&pdev->dev, "Cannot find power supply \"%s\"
  ",
  				desc->psy_charger_stat[i]);
883c10a9d   Jonghwa Lee   charger-manager :...
1684
  			return -ENODEV;
3bb3dbbd5   Donggeun Kim   power_supply: Add...
1685
  		}
b43eb35ab   Krzysztof Kozlowski   power_supply: cha...
1686
  		power_supply_put(psy);
3bb3dbbd5   Donggeun Kim   power_supply: Add...
1687
1688
1689
1690
1691
1692
  	}
  
  	if (desc->polling_interval_ms == 0 ||
  	    msecs_to_jiffies(desc->polling_interval_ms) <= CM_JIFFIES_SMALL) {
  		dev_err(&pdev->dev, "polling_interval_ms is too small
  ");
883c10a9d   Jonghwa Lee   charger-manager :...
1693
  		return -EINVAL;
3bb3dbbd5   Donggeun Kim   power_supply: Add...
1694
  	}
8fcfe088e   Chanwoo Choi   charger-manager: ...
1695
1696
  	if (!desc->charging_max_duration_ms ||
  			!desc->discharging_max_duration_ms) {
e5409cbd8   Joe Perches   charger-manager: ...
1697
1698
  		dev_info(&pdev->dev, "Cannot limit charging duration checking mechanism to prevent overcharge/overheat and control discharging duration
  ");
8fcfe088e   Chanwoo Choi   charger-manager: ...
1699
1700
1701
  		desc->charging_max_duration_ms = 0;
  		desc->discharging_max_duration_ms = 0;
  	}
3bb3dbbd5   Donggeun Kim   power_supply: Add...
1702
  	platform_set_drvdata(pdev, cm);
297d716f6   Krzysztof Kozlowski   power_supply: Cha...
1703
  	memcpy(&cm->charger_psy_desc, &psy_default, sizeof(psy_default));
bb2a95c2d   Axel Lin   charger-manager: ...
1704

41468a111   Chanwoo Choi   charger-manager: ...
1705
  	if (!desc->psy_name)
bb2a95c2d   Axel Lin   charger-manager: ...
1706
  		strncpy(cm->psy_name_buf, psy_default.name, PSY_NAME_MAX);
41468a111   Chanwoo Choi   charger-manager: ...
1707
  	else
ad3d13eee   Donggeun Kim   power_supply: Cha...
1708
  		strncpy(cm->psy_name_buf, desc->psy_name, PSY_NAME_MAX);
297d716f6   Krzysztof Kozlowski   power_supply: Cha...
1709
  	cm->charger_psy_desc.name = cm->psy_name_buf;
ad3d13eee   Donggeun Kim   power_supply: Cha...
1710
1711
  
  	/* Allocate for psy properties because they may vary */
297d716f6   Krzysztof Kozlowski   power_supply: Cha...
1712
  	cm->charger_psy_desc.properties = devm_kzalloc(&pdev->dev,
883c10a9d   Jonghwa Lee   charger-manager :...
1713
  				sizeof(enum power_supply_property)
ad3d13eee   Donggeun Kim   power_supply: Cha...
1714
  				* (ARRAY_SIZE(default_charger_props) +
883c10a9d   Jonghwa Lee   charger-manager :...
1715
  				NUM_CHARGER_PSY_OPTIONAL), GFP_KERNEL);
297d716f6   Krzysztof Kozlowski   power_supply: Cha...
1716
  	if (!cm->charger_psy_desc.properties)
883c10a9d   Jonghwa Lee   charger-manager :...
1717
  		return -ENOMEM;
297d716f6   Krzysztof Kozlowski   power_supply: Cha...
1718
  	memcpy(cm->charger_psy_desc.properties, default_charger_props,
ad3d13eee   Donggeun Kim   power_supply: Cha...
1719
1720
  		sizeof(enum power_supply_property) *
  		ARRAY_SIZE(default_charger_props));
297d716f6   Krzysztof Kozlowski   power_supply: Cha...
1721
  	cm->charger_psy_desc.num_properties = psy_default.num_properties;
ad3d13eee   Donggeun Kim   power_supply: Cha...
1722
1723
  
  	/* Find which optional psy-properties are available */
b43eb35ab   Krzysztof Kozlowski   power_supply: cha...
1724
1725
1726
1727
1728
1729
1730
  	fuel_gauge = power_supply_get_by_name(desc->psy_fuel_gauge);
  	if (!fuel_gauge) {
  		dev_err(&pdev->dev, "Cannot find power supply \"%s\"
  ",
  			desc->psy_fuel_gauge);
  		return -ENODEV;
  	}
b70229bca   Krzysztof Kozlowski   power_supply: cha...
1731
  	if (!power_supply_get_property(fuel_gauge,
ad3d13eee   Donggeun Kim   power_supply: Cha...
1732
  					  POWER_SUPPLY_PROP_CHARGE_NOW, &val)) {
297d716f6   Krzysztof Kozlowski   power_supply: Cha...
1733
  		cm->charger_psy_desc.properties[cm->charger_psy_desc.num_properties] =
ad3d13eee   Donggeun Kim   power_supply: Cha...
1734
  				POWER_SUPPLY_PROP_CHARGE_NOW;
297d716f6   Krzysztof Kozlowski   power_supply: Cha...
1735
  		cm->charger_psy_desc.num_properties++;
ad3d13eee   Donggeun Kim   power_supply: Cha...
1736
  	}
b70229bca   Krzysztof Kozlowski   power_supply: cha...
1737
  	if (!power_supply_get_property(fuel_gauge,
ad3d13eee   Donggeun Kim   power_supply: Cha...
1738
1739
  					  POWER_SUPPLY_PROP_CURRENT_NOW,
  					  &val)) {
297d716f6   Krzysztof Kozlowski   power_supply: Cha...
1740
  		cm->charger_psy_desc.properties[cm->charger_psy_desc.num_properties] =
ad3d13eee   Donggeun Kim   power_supply: Cha...
1741
  				POWER_SUPPLY_PROP_CURRENT_NOW;
297d716f6   Krzysztof Kozlowski   power_supply: Cha...
1742
  		cm->charger_psy_desc.num_properties++;
ad3d13eee   Donggeun Kim   power_supply: Cha...
1743
  	}
bb2a95c2d   Axel Lin   charger-manager: ...
1744

bdbe81445   Krzysztof Kozlowski   power: charger-ma...
1745
  	ret = cm_init_thermal_data(cm, fuel_gauge);
5c49a6256   Jonghwa Lee   charger-manager: ...
1746
1747
1748
1749
  	if (ret) {
  		dev_err(&pdev->dev, "Failed to initialize thermal data
  ");
  		cm->desc->measure_battery_temp = false;
ad3d13eee   Donggeun Kim   power_supply: Cha...
1750
  	}
b43eb35ab   Krzysztof Kozlowski   power_supply: cha...
1751
  	power_supply_put(fuel_gauge);
ad3d13eee   Donggeun Kim   power_supply: Cha...
1752

d829dc75b   Chanwoo Choi   charger-manager: ...
1753
  	INIT_DELAYED_WORK(&cm->fullbatt_vchk_work, fullbatt_vchk);
4970d839a   Krzysztof Kozlowski   power_supply: cha...
1754
1755
  	cm->charger_psy = power_supply_register(&pdev->dev,
  						&cm->charger_psy_desc,
297d716f6   Krzysztof Kozlowski   power_supply: Cha...
1756
1757
  						&psy_cfg);
  	if (IS_ERR(cm->charger_psy)) {
e5409cbd8   Joe Perches   charger-manager: ...
1758
1759
  		dev_err(&pdev->dev, "Cannot register charger-manager with name \"%s\"
  ",
ecf896b97   Krzysztof Kozlowski   power_supply: cha...
1760
  			cm->charger_psy_desc.name);
297d716f6   Krzysztof Kozlowski   power_supply: Cha...
1761
  		return PTR_ERR(cm->charger_psy);
ad3d13eee   Donggeun Kim   power_supply: Cha...
1762
  	}
41468a111   Chanwoo Choi   charger-manager: ...
1763
1764
1765
1766
1767
1768
  	/* Register extcon device for charger cable */
  	ret = charger_manager_register_extcon(cm);
  	if (ret < 0) {
  		dev_err(&pdev->dev, "Cannot initialize extcon device
  ");
  		goto err_reg_extcon;
3bb3dbbd5   Donggeun Kim   power_supply: Add...
1769
  	}
41468a111   Chanwoo Choi   charger-manager: ...
1770
1771
1772
1773
1774
1775
1776
  	/* Register sysfs entry for charger(regulator) */
  	ret = charger_manager_register_sysfs(cm);
  	if (ret < 0) {
  		dev_err(&pdev->dev,
  			"Cannot initialize sysfs entry of regulator
  ");
  		goto err_reg_sysfs;
3bb3dbbd5   Donggeun Kim   power_supply: Add...
1777
1778
1779
1780
1781
1782
  	}
  
  	/* Add to the list */
  	mutex_lock(&cm_list_mtx);
  	list_add(&cm->entry, &cm_list);
  	mutex_unlock(&cm_list_mtx);
dfeccb12b   Chanwoo Choi   charger-manager: ...
1783
1784
1785
1786
1787
1788
  	/*
  	 * Charger-manager is capable of waking up the systme from sleep
  	 * when event is happend through cm_notify_event()
  	 */
  	device_init_wakeup(&pdev->dev, true);
  	device_set_wakeup_capable(&pdev->dev, false);
b1022e247   Chanwoo Choi   power: charger-ma...
1789
1790
1791
1792
1793
1794
  	/*
  	 * Charger-manager have to check the charging state right after
  	 * tialization of charger-manager and then update current charging
  	 * state.
  	 */
  	cm_monitor();
d829dc75b   Chanwoo Choi   charger-manager: ...
1795
  	schedule_work(&setup_polling);
3bb3dbbd5   Donggeun Kim   power_supply: Add...
1796
  	return 0;
41468a111   Chanwoo Choi   charger-manager: ...
1797
  err_reg_sysfs:
3950c7865   Chanwoo Choi   charger-manager: ...
1798
1799
1800
1801
  	for (i = 0; i < desc->num_charger_regulators; i++) {
  		struct charger_regulator *charger;
  
  		charger = &desc->charger_regulators[i];
297d716f6   Krzysztof Kozlowski   power_supply: Cha...
1802
  		sysfs_remove_group(&cm->charger_psy->dev.kobj,
3950c7865   Chanwoo Choi   charger-manager: ...
1803
  				&charger->attr_g);
3950c7865   Chanwoo Choi   charger-manager: ...
1804
  	}
41468a111   Chanwoo Choi   charger-manager: ...
1805
1806
1807
1808
1809
1810
  err_reg_extcon:
  	for (i = 0; i < desc->num_charger_regulators; i++) {
  		struct charger_regulator *charger;
  
  		charger = &desc->charger_regulators[i];
  		for (j = 0; j < charger->num_cables; j++) {
bee737bcc   Chanwoo Choi   charger-manager: ...
1811
  			struct charger_cable *cable = &charger->cables[j];
3cc9d2696   Jonghwa Lee   charger-manager: ...
1812
1813
1814
  			/* Remove notifier block if only edev exists */
  			if (cable->extcon_dev.edev)
  				extcon_unregister_interest(&cable->extcon_dev);
bee737bcc   Chanwoo Choi   charger-manager: ...
1815
  		}
41468a111   Chanwoo Choi   charger-manager: ...
1816

bee737bcc   Chanwoo Choi   charger-manager: ...
1817
  		regulator_put(desc->charger_regulators[i].consumer);
41468a111   Chanwoo Choi   charger-manager: ...
1818
  	}
bee737bcc   Chanwoo Choi   charger-manager: ...
1819

297d716f6   Krzysztof Kozlowski   power_supply: Cha...
1820
  	power_supply_unregister(cm->charger_psy);
883c10a9d   Jonghwa Lee   charger-manager :...
1821

3bb3dbbd5   Donggeun Kim   power_supply: Add...
1822
1823
  	return ret;
  }
415ec69fb   Bill Pemberton   power: remove use...
1824
  static int charger_manager_remove(struct platform_device *pdev)
3bb3dbbd5   Donggeun Kim   power_supply: Add...
1825
1826
1827
  {
  	struct charger_manager *cm = platform_get_drvdata(pdev);
  	struct charger_desc *desc = cm->desc;
bee737bcc   Chanwoo Choi   charger-manager: ...
1828
1829
  	int i = 0;
  	int j = 0;
3bb3dbbd5   Donggeun Kim   power_supply: Add...
1830
1831
1832
1833
1834
  
  	/* Remove from the list */
  	mutex_lock(&cm_list_mtx);
  	list_del(&cm->entry);
  	mutex_unlock(&cm_list_mtx);
2fbb520d2   Tejun Heo   charger_manager: ...
1835
1836
  	cancel_work_sync(&setup_polling);
  	cancel_delayed_work_sync(&cm_monitor_work);
d829dc75b   Chanwoo Choi   charger-manager: ...
1837

bee737bcc   Chanwoo Choi   charger-manager: ...
1838
1839
1840
1841
1842
1843
1844
1845
1846
1847
1848
  	for (i = 0 ; i < desc->num_charger_regulators ; i++) {
  		struct charger_regulator *charger
  				= &desc->charger_regulators[i];
  		for (j = 0 ; j < charger->num_cables ; j++) {
  			struct charger_cable *cable = &charger->cables[j];
  			extcon_unregister_interest(&cable->extcon_dev);
  		}
  	}
  
  	for (i = 0 ; i < desc->num_charger_regulators ; i++)
  		regulator_put(desc->charger_regulators[i].consumer);
297d716f6   Krzysztof Kozlowski   power_supply: Cha...
1849
  	power_supply_unregister(cm->charger_psy);
d829dc75b   Chanwoo Choi   charger-manager: ...
1850
1851
  
  	try_charger_enable(cm, false);
3bb3dbbd5   Donggeun Kim   power_supply: Add...
1852
1853
  	return 0;
  }
1bbe24d46   Axel Lin   power_supply: Fix...
1854
  static const struct platform_device_id charger_manager_id[] = {
3bb3dbbd5   Donggeun Kim   power_supply: Add...
1855
1856
1857
  	{ "charger-manager", 0 },
  	{ },
  };
1bbe24d46   Axel Lin   power_supply: Fix...
1858
  MODULE_DEVICE_TABLE(platform, charger_manager_id);
3bb3dbbd5   Donggeun Kim   power_supply: Add...
1859

dfeccb12b   Chanwoo Choi   charger-manager: ...
1860
1861
1862
1863
1864
1865
1866
1867
1868
1869
1870
  static int cm_suspend_noirq(struct device *dev)
  {
  	int ret = 0;
  
  	if (device_may_wakeup(dev)) {
  		device_set_wakeup_capable(dev, false);
  		ret = -EAGAIN;
  	}
  
  	return ret;
  }
c1155c64e   Jonghwa Lee   power: charger-ma...
1871
1872
1873
1874
1875
1876
1877
1878
1879
1880
1881
1882
1883
1884
1885
1886
1887
1888
  static bool cm_need_to_awake(void)
  {
  	struct charger_manager *cm;
  
  	if (cm_timer)
  		return false;
  
  	mutex_lock(&cm_list_mtx);
  	list_for_each_entry(cm, &cm_list, entry) {
  		if (is_charging(cm)) {
  			mutex_unlock(&cm_list_mtx);
  			return true;
  		}
  	}
  	mutex_unlock(&cm_list_mtx);
  
  	return false;
  }
3bb3dbbd5   Donggeun Kim   power_supply: Add...
1889
1890
  static int cm_suspend_prepare(struct device *dev)
  {
bb2a95c2d   Axel Lin   charger-manager: ...
1891
  	struct charger_manager *cm = dev_get_drvdata(dev);
3bb3dbbd5   Donggeun Kim   power_supply: Add...
1892

c1155c64e   Jonghwa Lee   power: charger-ma...
1893
1894
  	if (cm_need_to_awake())
  		return -EBUSY;
3bb3dbbd5   Donggeun Kim   power_supply: Add...
1895

c1155c64e   Jonghwa Lee   power: charger-ma...
1896
  	if (!cm_suspended)
3bb3dbbd5   Donggeun Kim   power_supply: Add...
1897
  		cm_suspended = true;
3bb3dbbd5   Donggeun Kim   power_supply: Add...
1898

c1155c64e   Jonghwa Lee   power: charger-ma...
1899
  	cm_timer_set = cm_setup_timer();
3bb3dbbd5   Donggeun Kim   power_supply: Add...
1900

c1155c64e   Jonghwa Lee   power: charger-ma...
1901
1902
1903
1904
  	if (cm_timer_set) {
  		cancel_work_sync(&setup_polling);
  		cancel_delayed_work_sync(&cm_monitor_work);
  		cancel_delayed_work(&cm->fullbatt_vchk_work);
3bb3dbbd5   Donggeun Kim   power_supply: Add...
1905
1906
1907
1908
1909
1910
1911
  	}
  
  	return 0;
  }
  
  static void cm_suspend_complete(struct device *dev)
  {
bb2a95c2d   Axel Lin   charger-manager: ...
1912
  	struct charger_manager *cm = dev_get_drvdata(dev);
3bb3dbbd5   Donggeun Kim   power_supply: Add...
1913

c1155c64e   Jonghwa Lee   power: charger-ma...
1914
  	if (cm_suspended)
3bb3dbbd5   Donggeun Kim   power_supply: Add...
1915
  		cm_suspended = false;
c1155c64e   Jonghwa Lee   power: charger-ma...
1916
1917
1918
1919
1920
1921
1922
1923
1924
  
  	if (cm_timer_set) {
  		ktime_t remain;
  
  		alarm_cancel(cm_timer);
  		cm_timer_set = false;
  		remain = alarm_expires_remaining(cm_timer);
  		cm_suspend_duration_ms -= ktime_to_ms(remain);
  		schedule_work(&setup_polling);
3bb3dbbd5   Donggeun Kim   power_supply: Add...
1925
  	}
c1155c64e   Jonghwa Lee   power: charger-ma...
1926
  	_cm_monitor(cm);
d829dc75b   Chanwoo Choi   charger-manager: ...
1927
1928
1929
1930
1931
1932
1933
1934
1935
1936
1937
1938
1939
1940
  	/* Re-enqueue delayed work (fullbatt_vchk_work) */
  	if (cm->fullbatt_vchk_jiffies_at) {
  		unsigned long delay = 0;
  		unsigned long now = jiffies + CM_JIFFIES_SMALL;
  
  		if (time_after_eq(now, cm->fullbatt_vchk_jiffies_at)) {
  			delay = (unsigned long)((long)now
  				- (long)(cm->fullbatt_vchk_jiffies_at));
  			delay = jiffies_to_msecs(delay);
  		} else {
  			delay = 0;
  		}
  
  		/*
c1155c64e   Jonghwa Lee   power: charger-ma...
1941
1942
  		 * Account for cm_suspend_duration_ms with assuming that
  		 * timer stops in suspend.
d829dc75b   Chanwoo Choi   charger-manager: ...
1943
  		 */
c1155c64e   Jonghwa Lee   power: charger-ma...
1944
1945
1946
1947
  		if (delay > cm_suspend_duration_ms)
  			delay -= cm_suspend_duration_ms;
  		else
  			delay = 0;
d829dc75b   Chanwoo Choi   charger-manager: ...
1948
1949
1950
1951
  
  		queue_delayed_work(cm_wq, &cm->fullbatt_vchk_work,
  				   msecs_to_jiffies(delay));
  	}
dfeccb12b   Chanwoo Choi   charger-manager: ...
1952
  	device_set_wakeup_capable(cm->dev, false);
3bb3dbbd5   Donggeun Kim   power_supply: Add...
1953
1954
1955
1956
  }
  
  static const struct dev_pm_ops charger_manager_pm = {
  	.prepare	= cm_suspend_prepare,
dfeccb12b   Chanwoo Choi   charger-manager: ...
1957
  	.suspend_noirq	= cm_suspend_noirq,
3bb3dbbd5   Donggeun Kim   power_supply: Add...
1958
1959
1960
1961
1962
1963
  	.complete	= cm_suspend_complete,
  };
  
  static struct platform_driver charger_manager_driver = {
  	.driver = {
  		.name = "charger-manager",
3bb3dbbd5   Donggeun Kim   power_supply: Add...
1964
  		.pm = &charger_manager_pm,
856ee6115   Jonghwa Lee   charger-manager: ...
1965
  		.of_match_table = charger_manager_match,
3bb3dbbd5   Donggeun Kim   power_supply: Add...
1966
1967
  	},
  	.probe = charger_manager_probe,
28ea73f4c   Bill Pemberton   power: remove use...
1968
  	.remove = charger_manager_remove,
3bb3dbbd5   Donggeun Kim   power_supply: Add...
1969
1970
1971
1972
1973
  	.id_table = charger_manager_id,
  };
  
  static int __init charger_manager_init(void)
  {
d829dc75b   Chanwoo Choi   charger-manager: ...
1974
1975
  	cm_wq = create_freezable_workqueue("charger_manager");
  	INIT_DELAYED_WORK(&cm_monitor_work, cm_monitor_poller);
3bb3dbbd5   Donggeun Kim   power_supply: Add...
1976
1977
1978
1979
1980
1981
  	return platform_driver_register(&charger_manager_driver);
  }
  late_initcall(charger_manager_init);
  
  static void __exit charger_manager_cleanup(void)
  {
d829dc75b   Chanwoo Choi   charger-manager: ...
1982
1983
  	destroy_workqueue(cm_wq);
  	cm_wq = NULL;
3bb3dbbd5   Donggeun Kim   power_supply: Add...
1984
1985
1986
  	platform_driver_unregister(&charger_manager_driver);
  }
  module_exit(charger_manager_cleanup);
dfeccb12b   Chanwoo Choi   charger-manager: ...
1987
  /**
dfeccb12b   Chanwoo Choi   charger-manager: ...
1988
1989
1990
1991
1992
1993
1994
1995
1996
1997
1998
1999
2000
2001
2002
2003
   * cm_notify_event - charger driver notify Charger Manager of charger event
   * @psy: pointer to instance of charger's power_supply
   * @type: type of charger event
   * @msg: optional message passed to uevent_notify fuction
   */
  void cm_notify_event(struct power_supply *psy, enum cm_event_types type,
  		     char *msg)
  {
  	struct charger_manager *cm;
  	bool found_power_supply = false;
  
  	if (psy == NULL)
  		return;
  
  	mutex_lock(&cm_list_mtx);
  	list_for_each_entry(cm, &cm_list, entry) {
5f4768225   Andy Shevchenko   power: charger_ma...
2004
2005
2006
  		if (match_string(cm->desc->psy_charger_stat, -1,
  				 psy->desc->name) >= 0) {
  			found_power_supply = true;
dfeccb12b   Chanwoo Choi   charger-manager: ...
2007
  			break;
5f4768225   Andy Shevchenko   power: charger_ma...
2008
  		}
dfeccb12b   Chanwoo Choi   charger-manager: ...
2009
2010
2011
2012
2013
2014
2015
2016
2017
2018
2019
2020
2021
2022
2023
2024
2025
2026
2027
2028
2029
2030
  	}
  	mutex_unlock(&cm_list_mtx);
  
  	if (!found_power_supply)
  		return;
  
  	switch (type) {
  	case CM_EVENT_BATT_FULL:
  		fullbatt_handler(cm);
  		break;
  	case CM_EVENT_BATT_OUT:
  		battout_handler(cm);
  		break;
  	case CM_EVENT_BATT_IN:
  	case CM_EVENT_EXT_PWR_IN_OUT ... CM_EVENT_CHG_START_STOP:
  		misc_event_handler(cm, type);
  		break;
  	case CM_EVENT_UNKNOWN:
  	case CM_EVENT_OTHERS:
  		uevent_notify(cm, msg ? msg : default_event_names[type]);
  		break;
  	default:
e5409cbd8   Joe Perches   charger-manager: ...
2031
2032
  		dev_err(cm->dev, "%s: type not specified
  ", __func__);
dfeccb12b   Chanwoo Choi   charger-manager: ...
2033
2034
2035
2036
  		break;
  	}
  }
  EXPORT_SYMBOL_GPL(cm_notify_event);
3bb3dbbd5   Donggeun Kim   power_supply: Add...
2037
2038
2039
  MODULE_AUTHOR("MyungJoo Ham <myungjoo.ham@samsung.com>");
  MODULE_DESCRIPTION("Charger Manager");
  MODULE_LICENSE("GPL");