Blame view

drivers/power/pda_power.c 11.6 KB
b2998049c   Anton Vorontsov   [BATTERY] pda_pow...
1
2
3
4
5
6
7
8
9
10
11
12
13
14
  /*
   * Common power driver for PDAs and phones with one or two external
   * power supplies (AC/USB) connected to main and backup batteries,
   * and optional builtin charger.
   *
   * Copyright © 2007 Anton Vorontsov <cbou@mail.ru>
   *
   * 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.
   */
  
  #include <linux/module.h>
  #include <linux/platform_device.h>
5bf2b994b   Philipp Zabel   pda_power: Add op...
15
  #include <linux/err.h>
b2998049c   Anton Vorontsov   [BATTERY] pda_pow...
16
  #include <linux/interrupt.h>
9ad63986c   Dima Zavin   pda_power: Add su...
17
  #include <linux/notifier.h>
b2998049c   Anton Vorontsov   [BATTERY] pda_pow...
18
19
  #include <linux/power_supply.h>
  #include <linux/pda_power.h>
5bf2b994b   Philipp Zabel   pda_power: Add op...
20
  #include <linux/regulator/consumer.h>
b2998049c   Anton Vorontsov   [BATTERY] pda_pow...
21
22
  #include <linux/timer.h>
  #include <linux/jiffies.h>
5bf2b994b   Philipp Zabel   pda_power: Add op...
23
  #include <linux/usb/otg.h>
b2998049c   Anton Vorontsov   [BATTERY] pda_pow...
24
25
26
  
  static inline unsigned int get_irq_flags(struct resource *res)
  {
d554a3f9d   Theodore Ts'o   pda_power: remove...
27
  	return IRQF_SHARED | (res->flags & IRQF_TRIGGER_MASK);
b2998049c   Anton Vorontsov   [BATTERY] pda_pow...
28
29
30
31
32
33
34
  }
  
  static struct device *dev;
  static struct pda_power_pdata *pdata;
  static struct resource *ac_irq, *usb_irq;
  static struct timer_list charger_timer;
  static struct timer_list supply_timer;
c3caebad7   Anton Vorontsov   pda_power: implem...
35
36
  static struct timer_list polling_timer;
  static int polling;
297d716f6   Krzysztof Kozlowski   power_supply: Cha...
37
  static struct power_supply *pda_psy_ac, *pda_psy_usb;
b2998049c   Anton Vorontsov   [BATTERY] pda_pow...
38

820d08835   Felipe Balbi   usb: power: pda_p...
39
  #if IS_ENABLED(CONFIG_USB_PHY)
867538110   Heikki Krogerus   usb: otg: Rename ...
40
  static struct usb_phy *transceiver;
9ad63986c   Dima Zavin   pda_power: Add su...
41
  static struct notifier_block otg_nb;
1c74529db   Axel Lin   pda_power: Fix bu...
42
  #endif
5bf2b994b   Philipp Zabel   pda_power: Add op...
43
  static struct regulator *ac_draw;
bfde2662a   Anton Vorontsov   pda_power: variou...
44
45
46
47
48
49
50
51
52
  enum {
  	PDA_PSY_OFFLINE = 0,
  	PDA_PSY_ONLINE = 1,
  	PDA_PSY_TO_CHANGE,
  };
  static int new_ac_status = -1;
  static int new_usb_status = -1;
  static int ac_status = -1;
  static int usb_status = -1;
b2998049c   Anton Vorontsov   [BATTERY] pda_pow...
53
54
55
56
57
58
  static int pda_power_get_property(struct power_supply *psy,
  				  enum power_supply_property psp,
  				  union power_supply_propval *val)
  {
  	switch (psp) {
  	case POWER_SUPPLY_PROP_ONLINE:
297d716f6   Krzysztof Kozlowski   power_supply: Cha...
59
  		if (psy->desc->type == POWER_SUPPLY_TYPE_MAINS)
b2998049c   Anton Vorontsov   [BATTERY] pda_pow...
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
  			val->intval = pdata->is_ac_online ?
  				      pdata->is_ac_online() : 0;
  		else
  			val->intval = pdata->is_usb_online ?
  				      pdata->is_usb_online() : 0;
  		break;
  	default:
  		return -EINVAL;
  	}
  	return 0;
  }
  
  static enum power_supply_property pda_power_props[] = {
  	POWER_SUPPLY_PROP_ONLINE,
  };
  
  static char *pda_power_supplied_to[] = {
  	"main-battery",
  	"backup-battery",
  };
297d716f6   Krzysztof Kozlowski   power_supply: Cha...
80
  static const struct power_supply_desc pda_psy_ac_desc = {
bfde2662a   Anton Vorontsov   pda_power: variou...
81
82
  	.name = "ac",
  	.type = POWER_SUPPLY_TYPE_MAINS,
bfde2662a   Anton Vorontsov   pda_power: variou...
83
84
85
  	.properties = pda_power_props,
  	.num_properties = ARRAY_SIZE(pda_power_props),
  	.get_property = pda_power_get_property,
b2998049c   Anton Vorontsov   [BATTERY] pda_pow...
86
  };
297d716f6   Krzysztof Kozlowski   power_supply: Cha...
87
  static const struct power_supply_desc pda_psy_usb_desc = {
bfde2662a   Anton Vorontsov   pda_power: variou...
88
89
  	.name = "usb",
  	.type = POWER_SUPPLY_TYPE_USB,
bfde2662a   Anton Vorontsov   pda_power: variou...
90
91
92
93
94
95
96
97
98
99
100
101
102
  	.properties = pda_power_props,
  	.num_properties = ARRAY_SIZE(pda_power_props),
  	.get_property = pda_power_get_property,
  };
  
  static void update_status(void)
  {
  	if (pdata->is_ac_online)
  		new_ac_status = !!pdata->is_ac_online();
  
  	if (pdata->is_usb_online)
  		new_usb_status = !!pdata->is_usb_online();
  }
b2998049c   Anton Vorontsov   [BATTERY] pda_pow...
103
104
  static void update_charger(void)
  {
5bf2b994b   Philipp Zabel   pda_power: Add op...
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
  	static int regulator_enabled;
  	int max_uA = pdata->ac_max_uA;
  
  	if (pdata->set_charge) {
  		if (new_ac_status > 0) {
  			dev_dbg(dev, "charger on (AC)
  ");
  			pdata->set_charge(PDA_POWER_CHARGE_AC);
  		} else if (new_usb_status > 0) {
  			dev_dbg(dev, "charger on (USB)
  ");
  			pdata->set_charge(PDA_POWER_CHARGE_USB);
  		} else {
  			dev_dbg(dev, "charger off
  ");
  			pdata->set_charge(0);
  		}
  	} else if (ac_draw) {
  		if (new_ac_status > 0) {
  			regulator_set_current_limit(ac_draw, max_uA, max_uA);
  			if (!regulator_enabled) {
  				dev_dbg(dev, "charger on (AC)
  ");
92311c3c7   Mark Brown   pda_power: Compla...
128
  				WARN_ON(regulator_enable(ac_draw));
5bf2b994b   Philipp Zabel   pda_power: Add op...
129
130
131
132
133
134
  				regulator_enabled = 1;
  			}
  		} else {
  			if (regulator_enabled) {
  				dev_dbg(dev, "charger off
  ");
92311c3c7   Mark Brown   pda_power: Compla...
135
  				WARN_ON(regulator_disable(ac_draw));
5bf2b994b   Philipp Zabel   pda_power: Add op...
136
137
138
  				regulator_enabled = 0;
  			}
  		}
b2998049c   Anton Vorontsov   [BATTERY] pda_pow...
139
  	}
b2998049c   Anton Vorontsov   [BATTERY] pda_pow...
140
  }
bfde2662a   Anton Vorontsov   pda_power: variou...
141
  static void supply_timer_func(unsigned long unused)
b2998049c   Anton Vorontsov   [BATTERY] pda_pow...
142
  {
bfde2662a   Anton Vorontsov   pda_power: variou...
143
144
  	if (ac_status == PDA_PSY_TO_CHANGE) {
  		ac_status = new_ac_status;
297d716f6   Krzysztof Kozlowski   power_supply: Cha...
145
  		power_supply_changed(pda_psy_ac);
bfde2662a   Anton Vorontsov   pda_power: variou...
146
  	}
5ebf6e6a9   Jeff Garzik   pda_power: clean ...
147

bfde2662a   Anton Vorontsov   pda_power: variou...
148
149
  	if (usb_status == PDA_PSY_TO_CHANGE) {
  		usb_status = new_usb_status;
297d716f6   Krzysztof Kozlowski   power_supply: Cha...
150
  		power_supply_changed(pda_psy_usb);
bfde2662a   Anton Vorontsov   pda_power: variou...
151
  	}
b2998049c   Anton Vorontsov   [BATTERY] pda_pow...
152
  }
bfde2662a   Anton Vorontsov   pda_power: variou...
153
  static void psy_changed(void)
b2998049c   Anton Vorontsov   [BATTERY] pda_pow...
154
155
  {
  	update_charger();
bfde2662a   Anton Vorontsov   pda_power: variou...
156
157
158
159
  	/*
  	 * Okay, charger set. Now wait a bit before notifying supplicants,
  	 * charge power should stabilize.
  	 */
b2998049c   Anton Vorontsov   [BATTERY] pda_pow...
160
161
  	mod_timer(&supply_timer,
  		  jiffies + msecs_to_jiffies(pdata->wait_for_charger));
b2998049c   Anton Vorontsov   [BATTERY] pda_pow...
162
  }
bfde2662a   Anton Vorontsov   pda_power: variou...
163
164
165
166
167
  static void charger_timer_func(unsigned long unused)
  {
  	update_status();
  	psy_changed();
  }
5ebf6e6a9   Jeff Garzik   pda_power: clean ...
168
  static irqreturn_t power_changed_isr(int irq, void *power_supply)
b2998049c   Anton Vorontsov   [BATTERY] pda_pow...
169
  {
297d716f6   Krzysztof Kozlowski   power_supply: Cha...
170
  	if (power_supply == pda_psy_ac)
bfde2662a   Anton Vorontsov   pda_power: variou...
171
  		ac_status = PDA_PSY_TO_CHANGE;
297d716f6   Krzysztof Kozlowski   power_supply: Cha...
172
  	else if (power_supply == pda_psy_usb)
bfde2662a   Anton Vorontsov   pda_power: variou...
173
174
175
176
177
178
179
180
  		usb_status = PDA_PSY_TO_CHANGE;
  	else
  		return IRQ_NONE;
  
  	/*
  	 * Wait a bit before reading ac/usb line status and setting charger,
  	 * because ac/usb status readings may lag from irq.
  	 */
b2998049c   Anton Vorontsov   [BATTERY] pda_pow...
181
182
  	mod_timer(&charger_timer,
  		  jiffies + msecs_to_jiffies(pdata->wait_for_status));
bfde2662a   Anton Vorontsov   pda_power: variou...
183

b2998049c   Anton Vorontsov   [BATTERY] pda_pow...
184
185
  	return IRQ_HANDLED;
  }
c3caebad7   Anton Vorontsov   pda_power: implem...
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
  static void polling_timer_func(unsigned long unused)
  {
  	int changed = 0;
  
  	dev_dbg(dev, "polling...
  ");
  
  	update_status();
  
  	if (!ac_irq && new_ac_status != ac_status) {
  		ac_status = PDA_PSY_TO_CHANGE;
  		changed = 1;
  	}
  
  	if (!usb_irq && new_usb_status != usb_status) {
  		usb_status = PDA_PSY_TO_CHANGE;
  		changed = 1;
  	}
  
  	if (changed)
  		psy_changed();
  
  	mod_timer(&polling_timer,
  		  jiffies + msecs_to_jiffies(pdata->polling_interval));
  }
820d08835   Felipe Balbi   usb: power: pda_p...
211
  #if IS_ENABLED(CONFIG_USB_PHY)
5bf2b994b   Philipp Zabel   pda_power: Add op...
212
213
  static int otg_is_usb_online(void)
  {
9ad63986c   Dima Zavin   pda_power: Add su...
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
  	return (transceiver->last_event == USB_EVENT_VBUS ||
  		transceiver->last_event == USB_EVENT_ENUMERATED);
  }
  
  static int otg_is_ac_online(void)
  {
  	return (transceiver->last_event == USB_EVENT_CHARGER);
  }
  
  static int otg_handle_notification(struct notifier_block *nb,
  		unsigned long event, void *unused)
  {
  	switch (event) {
  	case USB_EVENT_CHARGER:
  		ac_status = PDA_PSY_TO_CHANGE;
  		break;
  	case USB_EVENT_VBUS:
  	case USB_EVENT_ENUMERATED:
  		usb_status = PDA_PSY_TO_CHANGE;
  		break;
  	case USB_EVENT_NONE:
  		ac_status = PDA_PSY_TO_CHANGE;
  		usb_status = PDA_PSY_TO_CHANGE;
  		break;
  	default:
  		return NOTIFY_OK;
  	}
  
  	/*
  	 * Wait a bit before reading ac/usb line status and setting charger,
  	 * because ac/usb status readings may lag from irq.
  	 */
  	mod_timer(&charger_timer,
  		  jiffies + msecs_to_jiffies(pdata->wait_for_status));
  
  	return NOTIFY_OK;
5bf2b994b   Philipp Zabel   pda_power: Add op...
250
251
  }
  #endif
b2998049c   Anton Vorontsov   [BATTERY] pda_pow...
252
253
  static int pda_power_probe(struct platform_device *pdev)
  {
2dc9215d7   Krzysztof Kozlowski   power_supply: Mov...
254
  	struct power_supply_config psy_cfg = {};
b2998049c   Anton Vorontsov   [BATTERY] pda_pow...
255
256
257
258
259
260
261
262
263
264
265
266
267
  	int ret = 0;
  
  	dev = &pdev->dev;
  
  	if (pdev->id != -1) {
  		dev_err(dev, "it's meaningless to register several "
  			"pda_powers; use id = -1
  ");
  		ret = -EINVAL;
  		goto wrongid;
  	}
  
  	pdata = pdev->dev.platform_data;
f6b6b180b   Philipp Zabel   pda_power: add in...
268
269
270
271
272
  	if (pdata->init) {
  		ret = pdata->init(dev);
  		if (ret < 0)
  			goto init_failed;
  	}
ec60ea5cd   Paul Parsons   pda_power: Fix ac...
273
274
275
276
277
  	ac_draw = regulator_get(dev, "ac_draw");
  	if (IS_ERR(ac_draw)) {
  		dev_dbg(dev, "couldn't get ac_draw regulator
  ");
  		ac_draw = NULL;
ec60ea5cd   Paul Parsons   pda_power: Fix ac...
278
  	}
bfde2662a   Anton Vorontsov   pda_power: variou...
279
  	update_status();
b2998049c   Anton Vorontsov   [BATTERY] pda_pow...
280
281
282
283
284
285
286
  	update_charger();
  
  	if (!pdata->wait_for_status)
  		pdata->wait_for_status = 500;
  
  	if (!pdata->wait_for_charger)
  		pdata->wait_for_charger = 500;
c3caebad7   Anton Vorontsov   pda_power: implem...
287
288
  	if (!pdata->polling_interval)
  		pdata->polling_interval = 2000;
5bf2b994b   Philipp Zabel   pda_power: Add op...
289
290
  	if (!pdata->ac_max_uA)
  		pdata->ac_max_uA = 500000;
b2998049c   Anton Vorontsov   [BATTERY] pda_pow...
291
292
293
294
295
  	setup_timer(&charger_timer, charger_timer_func, 0);
  	setup_timer(&supply_timer, supply_timer_func, 0);
  
  	ac_irq = platform_get_resource_byname(pdev, IORESOURCE_IRQ, "ac");
  	usb_irq = platform_get_resource_byname(pdev, IORESOURCE_IRQ, "usb");
b2998049c   Anton Vorontsov   [BATTERY] pda_pow...
296
297
  
  	if (pdata->supplied_to) {
2dc9215d7   Krzysztof Kozlowski   power_supply: Mov...
298
299
300
301
302
  		psy_cfg.supplied_to = pdata->supplied_to;
  		psy_cfg.num_supplicants = pdata->num_supplicants;
  	} else {
  		psy_cfg.supplied_to = pda_power_supplied_to;
  		psy_cfg.num_supplicants = ARRAY_SIZE(pda_power_supplied_to);
b2998049c   Anton Vorontsov   [BATTERY] pda_pow...
303
  	}
820d08835   Felipe Balbi   usb: power: pda_p...
304
  #if IS_ENABLED(CONFIG_USB_PHY)
662dca54c   Kishon Vijay Abraham I   usb: otg: support...
305
  	transceiver = usb_get_phy(USB_PHY_TYPE_USB2);
ded017ee6   Kishon Vijay Abraham I   usb: phy: fix ret...
306
307
308
309
310
  	if (!IS_ERR_OR_NULL(transceiver)) {
  		if (!pdata->is_usb_online)
  			pdata->is_usb_online = otg_is_usb_online;
  		if (!pdata->is_ac_online)
  			pdata->is_ac_online = otg_is_ac_online;
9ad63986c   Dima Zavin   pda_power: Add su...
311
  	}
1c74529db   Axel Lin   pda_power: Fix bu...
312
  #endif
9ad63986c   Dima Zavin   pda_power: Add su...
313

9ef451062   Dmitry Eremin-Solenikov   pda_power: only r...
314
  	if (pdata->is_ac_online) {
297d716f6   Krzysztof Kozlowski   power_supply: Cha...
315
316
317
  		pda_psy_ac = power_supply_register(&pdev->dev,
  						   &pda_psy_ac_desc, &psy_cfg);
  		if (IS_ERR(pda_psy_ac)) {
9ef451062   Dmitry Eremin-Solenikov   pda_power: only r...
318
319
  			dev_err(dev, "failed to register %s power supply
  ",
297d716f6   Krzysztof Kozlowski   power_supply: Cha...
320
321
  				pda_psy_ac_desc.name);
  			ret = PTR_ERR(pda_psy_ac);
9ef451062   Dmitry Eremin-Solenikov   pda_power: only r...
322
323
  			goto ac_supply_failed;
  		}
b2998049c   Anton Vorontsov   [BATTERY] pda_pow...
324

9ef451062   Dmitry Eremin-Solenikov   pda_power: only r...
325
326
327
  		if (ac_irq) {
  			ret = request_irq(ac_irq->start, power_changed_isr,
  					  get_irq_flags(ac_irq), ac_irq->name,
297d716f6   Krzysztof Kozlowski   power_supply: Cha...
328
  					  pda_psy_ac);
9ef451062   Dmitry Eremin-Solenikov   pda_power: only r...
329
330
331
332
333
  			if (ret) {
  				dev_err(dev, "request ac irq failed
  ");
  				goto ac_irq_failed;
  			}
c3caebad7   Anton Vorontsov   pda_power: implem...
334
335
  		} else {
  			polling = 1;
9ef451062   Dmitry Eremin-Solenikov   pda_power: only r...
336
  		}
b2998049c   Anton Vorontsov   [BATTERY] pda_pow...
337
  	}
9ef451062   Dmitry Eremin-Solenikov   pda_power: only r...
338
  	if (pdata->is_usb_online) {
297d716f6   Krzysztof Kozlowski   power_supply: Cha...
339
340
341
342
  		pda_psy_usb = power_supply_register(&pdev->dev,
  						    &pda_psy_usb_desc,
  						    &psy_cfg);
  		if (IS_ERR(pda_psy_usb)) {
9ef451062   Dmitry Eremin-Solenikov   pda_power: only r...
343
344
  			dev_err(dev, "failed to register %s power supply
  ",
297d716f6   Krzysztof Kozlowski   power_supply: Cha...
345
346
  				pda_psy_usb_desc.name);
  			ret = PTR_ERR(pda_psy_usb);
9ef451062   Dmitry Eremin-Solenikov   pda_power: only r...
347
  			goto usb_supply_failed;
b2998049c   Anton Vorontsov   [BATTERY] pda_pow...
348
  		}
b2998049c   Anton Vorontsov   [BATTERY] pda_pow...
349

9ef451062   Dmitry Eremin-Solenikov   pda_power: only r...
350
351
352
  		if (usb_irq) {
  			ret = request_irq(usb_irq->start, power_changed_isr,
  					  get_irq_flags(usb_irq),
297d716f6   Krzysztof Kozlowski   power_supply: Cha...
353
  					  usb_irq->name, pda_psy_usb);
9ef451062   Dmitry Eremin-Solenikov   pda_power: only r...
354
355
356
357
358
  			if (ret) {
  				dev_err(dev, "request usb irq failed
  ");
  				goto usb_irq_failed;
  			}
c3caebad7   Anton Vorontsov   pda_power: implem...
359
360
  		} else {
  			polling = 1;
b2998049c   Anton Vorontsov   [BATTERY] pda_pow...
361
362
  		}
  	}
820d08835   Felipe Balbi   usb: power: pda_p...
363
  #if IS_ENABLED(CONFIG_USB_PHY)
ded017ee6   Kishon Vijay Abraham I   usb: phy: fix ret...
364
  	if (!IS_ERR_OR_NULL(transceiver) && pdata->use_otg_notifier) {
9ad63986c   Dima Zavin   pda_power: Add su...
365
  		otg_nb.notifier_call = otg_handle_notification;
fcc8ebc99   Heikki Krogerus   power_supply: Con...
366
  		ret = usb_register_notifier(transceiver, &otg_nb);
9ad63986c   Dima Zavin   pda_power: Add su...
367
368
369
370
371
372
373
  		if (ret) {
  			dev_err(dev, "failure to register otg notifier
  ");
  			goto otg_reg_notifier_failed;
  		}
  		polling = 0;
  	}
1c74529db   Axel Lin   pda_power: Fix bu...
374
  #endif
9ad63986c   Dima Zavin   pda_power: Add su...
375

c3caebad7   Anton Vorontsov   pda_power: implem...
376
377
378
379
380
381
382
383
384
385
  	if (polling) {
  		dev_dbg(dev, "will poll for status
  ");
  		setup_timer(&polling_timer, polling_timer_func, 0);
  		mod_timer(&polling_timer,
  			  jiffies + msecs_to_jiffies(pdata->polling_interval));
  	}
  
  	if (ac_irq || usb_irq)
  		device_init_wakeup(&pdev->dev, 1);
8f8e9b387   Dmitry Eremin-Solenikov   pda_power: add su...
386

9ef451062   Dmitry Eremin-Solenikov   pda_power: only r...
387
  	return 0;
b2998049c   Anton Vorontsov   [BATTERY] pda_pow...
388

820d08835   Felipe Balbi   usb: power: pda_p...
389
  #if IS_ENABLED(CONFIG_USB_PHY)
9ad63986c   Dima Zavin   pda_power: Add su...
390
391
  otg_reg_notifier_failed:
  	if (pdata->is_usb_online && usb_irq)
297d716f6   Krzysztof Kozlowski   power_supply: Cha...
392
  		free_irq(usb_irq->start, pda_psy_usb);
1c74529db   Axel Lin   pda_power: Fix bu...
393
  #endif
b2998049c   Anton Vorontsov   [BATTERY] pda_pow...
394
  usb_irq_failed:
9ef451062   Dmitry Eremin-Solenikov   pda_power: only r...
395
  	if (pdata->is_usb_online)
297d716f6   Krzysztof Kozlowski   power_supply: Cha...
396
  		power_supply_unregister(pda_psy_usb);
9ef451062   Dmitry Eremin-Solenikov   pda_power: only r...
397
398
  usb_supply_failed:
  	if (pdata->is_ac_online && ac_irq)
297d716f6   Krzysztof Kozlowski   power_supply: Cha...
399
  		free_irq(ac_irq->start, pda_psy_ac);
820d08835   Felipe Balbi   usb: power: pda_p...
400
  #if IS_ENABLED(CONFIG_USB_PHY)
ded017ee6   Kishon Vijay Abraham I   usb: phy: fix ret...
401
  	if (!IS_ERR_OR_NULL(transceiver))
721002ec1   Kishon Vijay Abraham I   usb: otg: utils: ...
402
  		usb_put_phy(transceiver);
1c74529db   Axel Lin   pda_power: Fix bu...
403
  #endif
b2998049c   Anton Vorontsov   [BATTERY] pda_pow...
404
  ac_irq_failed:
9ef451062   Dmitry Eremin-Solenikov   pda_power: only r...
405
  	if (pdata->is_ac_online)
297d716f6   Krzysztof Kozlowski   power_supply: Cha...
406
  		power_supply_unregister(pda_psy_ac);
9ef451062   Dmitry Eremin-Solenikov   pda_power: only r...
407
  ac_supply_failed:
5bf2b994b   Philipp Zabel   pda_power: Add op...
408
409
410
411
  	if (ac_draw) {
  		regulator_put(ac_draw);
  		ac_draw = NULL;
  	}
f6b6b180b   Philipp Zabel   pda_power: add in...
412
413
414
  	if (pdata->exit)
  		pdata->exit(dev);
  init_failed:
b2998049c   Anton Vorontsov   [BATTERY] pda_pow...
415
  wrongid:
b2998049c   Anton Vorontsov   [BATTERY] pda_pow...
416
417
418
419
420
  	return ret;
  }
  
  static int pda_power_remove(struct platform_device *pdev)
  {
9ef451062   Dmitry Eremin-Solenikov   pda_power: only r...
421
  	if (pdata->is_usb_online && usb_irq)
297d716f6   Krzysztof Kozlowski   power_supply: Cha...
422
  		free_irq(usb_irq->start, pda_psy_usb);
9ef451062   Dmitry Eremin-Solenikov   pda_power: only r...
423
  	if (pdata->is_ac_online && ac_irq)
297d716f6   Krzysztof Kozlowski   power_supply: Cha...
424
  		free_irq(ac_irq->start, pda_psy_ac);
bfde2662a   Anton Vorontsov   pda_power: variou...
425

c3caebad7   Anton Vorontsov   pda_power: implem...
426
427
  	if (polling)
  		del_timer_sync(&polling_timer);
b2998049c   Anton Vorontsov   [BATTERY] pda_pow...
428
429
  	del_timer_sync(&charger_timer);
  	del_timer_sync(&supply_timer);
bfde2662a   Anton Vorontsov   pda_power: variou...
430

9ef451062   Dmitry Eremin-Solenikov   pda_power: only r...
431
  	if (pdata->is_usb_online)
297d716f6   Krzysztof Kozlowski   power_supply: Cha...
432
  		power_supply_unregister(pda_psy_usb);
9ef451062   Dmitry Eremin-Solenikov   pda_power: only r...
433
  	if (pdata->is_ac_online)
297d716f6   Krzysztof Kozlowski   power_supply: Cha...
434
  		power_supply_unregister(pda_psy_ac);
820d08835   Felipe Balbi   usb: power: pda_p...
435
  #if IS_ENABLED(CONFIG_USB_PHY)
ded017ee6   Kishon Vijay Abraham I   usb: phy: fix ret...
436
  	if (!IS_ERR_OR_NULL(transceiver))
721002ec1   Kishon Vijay Abraham I   usb: otg: utils: ...
437
  		usb_put_phy(transceiver);
5bf2b994b   Philipp Zabel   pda_power: Add op...
438
439
440
441
442
  #endif
  	if (ac_draw) {
  		regulator_put(ac_draw);
  		ac_draw = NULL;
  	}
f6b6b180b   Philipp Zabel   pda_power: add in...
443
444
  	if (pdata->exit)
  		pdata->exit(dev);
bfde2662a   Anton Vorontsov   pda_power: variou...
445

b2998049c   Anton Vorontsov   [BATTERY] pda_pow...
446
447
  	return 0;
  }
8f8e9b387   Dmitry Eremin-Solenikov   pda_power: add su...
448
  #ifdef CONFIG_PM
e82374fd1   Robert Jarzmik   pda_power: Check ...
449
450
  static int ac_wakeup_enabled;
  static int usb_wakeup_enabled;
8f8e9b387   Dmitry Eremin-Solenikov   pda_power: add su...
451
452
  static int pda_power_suspend(struct platform_device *pdev, pm_message_t state)
  {
a3bcbbee8   Daniel Mack   pda_power: Add fu...
453
454
455
456
457
458
  	if (pdata->suspend) {
  		int ret = pdata->suspend(state);
  
  		if (ret)
  			return ret;
  	}
8f8e9b387   Dmitry Eremin-Solenikov   pda_power: add su...
459
460
  	if (device_may_wakeup(&pdev->dev)) {
  		if (ac_irq)
e82374fd1   Robert Jarzmik   pda_power: Check ...
461
  			ac_wakeup_enabled = !enable_irq_wake(ac_irq->start);
8f8e9b387   Dmitry Eremin-Solenikov   pda_power: add su...
462
  		if (usb_irq)
e82374fd1   Robert Jarzmik   pda_power: Check ...
463
  			usb_wakeup_enabled = !enable_irq_wake(usb_irq->start);
8f8e9b387   Dmitry Eremin-Solenikov   pda_power: add su...
464
465
466
467
468
469
470
471
  	}
  
  	return 0;
  }
  
  static int pda_power_resume(struct platform_device *pdev)
  {
  	if (device_may_wakeup(&pdev->dev)) {
e82374fd1   Robert Jarzmik   pda_power: Check ...
472
  		if (usb_irq && usb_wakeup_enabled)
8f8e9b387   Dmitry Eremin-Solenikov   pda_power: add su...
473
  			disable_irq_wake(usb_irq->start);
e82374fd1   Robert Jarzmik   pda_power: Check ...
474
  		if (ac_irq && ac_wakeup_enabled)
8f8e9b387   Dmitry Eremin-Solenikov   pda_power: add su...
475
476
  			disable_irq_wake(ac_irq->start);
  	}
a3bcbbee8   Daniel Mack   pda_power: Add fu...
477
478
  	if (pdata->resume)
  		return pdata->resume();
8f8e9b387   Dmitry Eremin-Solenikov   pda_power: add su...
479
480
481
482
483
484
  	return 0;
  }
  #else
  #define pda_power_suspend NULL
  #define pda_power_resume NULL
  #endif /* CONFIG_PM */
b2998049c   Anton Vorontsov   [BATTERY] pda_pow...
485
486
487
488
489
490
  static struct platform_driver pda_power_pdrv = {
  	.driver = {
  		.name = "pda-power",
  	},
  	.probe = pda_power_probe,
  	.remove = pda_power_remove,
8f8e9b387   Dmitry Eremin-Solenikov   pda_power: add su...
491
492
  	.suspend = pda_power_suspend,
  	.resume = pda_power_resume,
b2998049c   Anton Vorontsov   [BATTERY] pda_pow...
493
  };
300bac7fb   Axel Lin   power_supply: Con...
494
  module_platform_driver(pda_power_pdrv);
b2998049c   Anton Vorontsov   [BATTERY] pda_pow...
495

b2998049c   Anton Vorontsov   [BATTERY] pda_pow...
496
497
  MODULE_LICENSE("GPL");
  MODULE_AUTHOR("Anton Vorontsov <cbou@mail.ru>");
300bac7fb   Axel Lin   power_supply: Con...
498
  MODULE_ALIAS("platform:pda-power");