Blame view

drivers/mfd/tps65010.c 27.2 KB
74ba9207e   Thomas Gleixner   treewide: Replace...
1
  // SPDX-License-Identifier: GPL-2.0-or-later
72cd79954   David Brownell   [PATCH] I2C: add ...
2
3
4
5
6
  /*
   * tps65010 - driver for tps6501x power management chips
   *
   * Copyright (C) 2004 Texas Instruments
   * Copyright (C) 2004-2005 David Brownell
72cd79954   David Brownell   [PATCH] I2C: add ...
7
   */
72cd79954   David Brownell   [PATCH] I2C: add ...
8

72cd79954   David Brownell   [PATCH] I2C: add ...
9
10
11
12
13
  #include <linux/kernel.h>
  #include <linux/module.h>
  #include <linux/init.h>
  #include <linux/slab.h>
  #include <linux/interrupt.h>
72cd79954   David Brownell   [PATCH] I2C: add ...
14
15
16
  #include <linux/i2c.h>
  #include <linux/delay.h>
  #include <linux/workqueue.h>
72cd79954   David Brownell   [PATCH] I2C: add ...
17
18
  #include <linux/debugfs.h>
  #include <linux/seq_file.h>
5c085d369   Ingo Molnar   [PATCH] i2c: Sema...
19
  #include <linux/mutex.h>
79966fd9b   David Brownell   ARM: OMAP: I2C: t...
20
  #include <linux/platform_device.h>
72cd79954   David Brownell   [PATCH] I2C: add ...
21

9787076c4   Wolfram Sang   mfd: tps65010: Mo...
22
  #include <linux/mfd/tps65010.h>
72cd79954   David Brownell   [PATCH] I2C: add ...
23

22e5e747e   Linus Walleij   mfd: tps65010: Us...
24
  #include <linux/gpio/driver.h>
79966fd9b   David Brownell   ARM: OMAP: I2C: t...
25

72cd79954   David Brownell   [PATCH] I2C: add ...
26
27
28
  /*-------------------------------------------------------------------------*/
  
  #define	DRIVER_VERSION	"2 May 2005"
4801bc25f   David Brownell   [PATCH] i2c: tps6...
29
  #define	DRIVER_NAME	(tps65010_driver.driver.name)
72cd79954   David Brownell   [PATCH] I2C: add ...
30
31
32
  
  MODULE_DESCRIPTION("TPS6501x Power Management Driver");
  MODULE_LICENSE("GPL");
72cd79954   David Brownell   [PATCH] I2C: add ...
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
  static struct i2c_driver tps65010_driver;
  
  /*-------------------------------------------------------------------------*/
  
  /* This driver handles a family of multipurpose chips, which incorporate
   * voltage regulators, lithium ion/polymer battery charging, GPIOs, LEDs,
   * and other features often needed in portable devices like cell phones
   * or digital cameras.
   *
   * The tps65011 and tps65013 have different voltage settings compared
   * to tps65010 and tps65012.  The tps65013 has a NO_CHG status/irq.
   * All except tps65010 have "wait" mode, possibly defaulted so that
   * battery-insert != device-on.
   *
   * We could distinguish between some models by checking VDCDC1.UVLO or
   * other registers, unless they've been changed already after powerup
   * as part of board setup by a bootloader.
   */
  enum tps_model {
72cd79954   David Brownell   [PATCH] I2C: add ...
52
53
54
55
56
57
58
  	TPS65010,
  	TPS65011,
  	TPS65012,
  	TPS65013,
  };
  
  struct tps65010 {
b5067f8ff   David Brownell   i2c/tps65010: New...
59
  	struct i2c_client	*client;
5c085d369   Ingo Molnar   [PATCH] i2c: Sema...
60
  	struct mutex		lock;
8c1bc04e7   David Brownell   [PATCH] fix more ...
61
  	struct delayed_work	work;
72cd79954   David Brownell   [PATCH] I2C: add ...
62
63
64
65
66
67
68
69
70
71
72
73
  	struct dentry		*file;
  	unsigned		charging:1;
  	unsigned		por:1;
  	unsigned		model:8;
  	u16			vbus;
  	unsigned long		flags;
  #define	FLAG_VBUS_CHANGED	0
  #define	FLAG_IRQ_ENABLE		1
  
  	/* copies of last register state */
  	u8			chgstatus, regstatus, chgconf;
  	u8			nmask1, nmask2;
79966fd9b   David Brownell   ARM: OMAP: I2C: t...
74
75
76
  	u8			outmask;
  	struct gpio_chip	chip;
  	struct platform_device	*leds;
72cd79954   David Brownell   [PATCH] I2C: add ...
77
  };
4801bc25f   David Brownell   [PATCH] i2c: tps6...
78
  #define	POWER_POLL_DELAY	msecs_to_jiffies(5000)
72cd79954   David Brownell   [PATCH] I2C: add ...
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
  
  /*-------------------------------------------------------------------------*/
  
  #if	defined(DEBUG) || defined(CONFIG_DEBUG_FS)
  
  static void dbg_chgstat(char *buf, size_t len, u8 chgstatus)
  {
  	snprintf(buf, len, "%02x%s%s%s%s%s%s%s%s
  ",
  		chgstatus,
  		(chgstatus & TPS_CHG_USB) ? " USB" : "",
  		(chgstatus & TPS_CHG_AC) ? " AC" : "",
  		(chgstatus & TPS_CHG_THERM) ? " therm" : "",
  		(chgstatus & TPS_CHG_TERM) ? " done" :
  			((chgstatus & (TPS_CHG_USB|TPS_CHG_AC))
  				? " (charging)" : ""),
  		(chgstatus & TPS_CHG_TAPER_TMO) ? " taper_tmo" : "",
  		(chgstatus & TPS_CHG_CHG_TMO) ? " charge_tmo" : "",
  		(chgstatus & TPS_CHG_PRECHG_TMO) ? " prechg_tmo" : "",
  		(chgstatus & TPS_CHG_TEMP_ERR) ? " temp_err" : "");
  }
  
  static void dbg_regstat(char *buf, size_t len, u8 regstatus)
  {
  	snprintf(buf, len, "%02x %s%s%s%s%s%s%s%s
  ",
  		regstatus,
  		(regstatus & TPS_REG_ONOFF) ? "off" : "(on)",
  		(regstatus & TPS_REG_COVER) ? " uncover" : "",
  		(regstatus & TPS_REG_UVLO) ? " UVLO" : "",
  		(regstatus & TPS_REG_NO_CHG) ? " NO_CHG" : "",
65fc50e50   David Brownell   [PATCH] I2C: mino...
110
  		(regstatus & TPS_REG_PG_LD02) ? " ld02_bad" : "",
72cd79954   David Brownell   [PATCH] I2C: add ...
111
112
113
114
115
116
117
  		(regstatus & TPS_REG_PG_LD01) ? " ld01_bad" : "",
  		(regstatus & TPS_REG_PG_MAIN) ? " main_bad" : "",
  		(regstatus & TPS_REG_PG_CORE) ? " core_bad" : "");
  }
  
  static void dbg_chgconf(int por, char *buf, size_t len, u8 chgconfig)
  {
65fc50e50   David Brownell   [PATCH] I2C: mino...
118
  	const char *hibit;
72cd79954   David Brownell   [PATCH] I2C: add ...
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
  
  	if (por)
  		hibit = (chgconfig & TPS_CHARGE_POR)
  				? "POR=69ms" : "POR=1sec";
  	else
  		hibit = (chgconfig & TPS65013_AUA) ? "AUA" : "";
  
  	snprintf(buf, len, "%02x %s%s%s AC=%d%% USB=%dmA %sCharge
  ",
  		chgconfig, hibit,
  		(chgconfig & TPS_CHARGE_RESET) ? " reset" : "",
  		(chgconfig & TPS_CHARGE_FAST) ? " fast" : "",
  		({int p; switch ((chgconfig >> 3) & 3) {
  		case 3:		p = 100; break;
  		case 2:		p = 75; break;
  		case 1:		p = 50; break;
  		default:	p = 25; break;
  		}; p; }),
  		(chgconfig & TPS_VBUS_CHARGING)
  			? ((chgconfig & TPS_VBUS_500MA) ? 500 : 100)
  			: 0,
  		(chgconfig & TPS_CHARGE_ENABLE) ? "" : "No");
  }
  
  #endif
  
  #ifdef	DEBUG
  
  static void show_chgstatus(const char *label, u8 chgstatus)
  {
  	char buf [100];
  
  	dbg_chgstat(buf, sizeof buf, chgstatus);
  	pr_debug("%s: %s %s", DRIVER_NAME, label, buf);
  }
  
  static void show_regstatus(const char *label, u8 regstatus)
  {
  	char buf [100];
  
  	dbg_regstat(buf, sizeof buf, regstatus);
  	pr_debug("%s: %s %s", DRIVER_NAME, label, buf);
  }
  
  static void show_chgconfig(int por, const char *label, u8 chgconfig)
  {
  	char buf [100];
  
  	dbg_chgconf(por, buf, sizeof buf, chgconfig);
  	pr_debug("%s: %s %s", DRIVER_NAME, label, buf);
  }
  
  #else
  
  static inline void show_chgstatus(const char *label, u8 chgstatus) { }
  static inline void show_regstatus(const char *label, u8 chgstatus) { }
  static inline void show_chgconfig(int por, const char *label, u8 chgconfig) { }
  
  #endif
  
  #ifdef	CONFIG_DEBUG_FS
  
  static int dbg_show(struct seq_file *s, void *_)
  {
  	struct tps65010	*tps = s->private;
  	u8		value, v2;
  	unsigned	i;
  	char		buf[100];
  	const char	*chip;
  
  	switch (tps->model) {
  	case TPS65010:	chip = "tps65010"; break;
  	case TPS65011:	chip = "tps65011"; break;
  	case TPS65012:	chip = "tps65012"; break;
  	case TPS65013:	chip = "tps65013"; break;
  	default:	chip = NULL; break;
  	}
  	seq_printf(s, "driver  %s
  version %s
  chip    %s
  
  ",
  			DRIVER_NAME, DRIVER_VERSION, chip);
5c085d369   Ingo Molnar   [PATCH] i2c: Sema...
202
  	mutex_lock(&tps->lock);
72cd79954   David Brownell   [PATCH] I2C: add ...
203
204
205
206
207
208
209
210
211
212
213
214
215
  
  	/* FIXME how can we tell whether a battery is present?
  	 * likely involves a charge gauging chip (like BQ26501).
  	 */
  
  	seq_printf(s, "%scharging
  
  ", tps->charging ? "" : "(not) ");
  
  
  	/* registers for monitoring battery charging and status; note
  	 * that reading chgstat and regstat may ack IRQs...
  	 */
b5067f8ff   David Brownell   i2c/tps65010: New...
216
  	value = i2c_smbus_read_byte_data(tps->client, TPS_CHGCONFIG);
72cd79954   David Brownell   [PATCH] I2C: add ...
217
218
  	dbg_chgconf(tps->por, buf, sizeof buf, value);
  	seq_printf(s, "chgconfig %s", buf);
b5067f8ff   David Brownell   i2c/tps65010: New...
219
  	value = i2c_smbus_read_byte_data(tps->client, TPS_CHGSTATUS);
72cd79954   David Brownell   [PATCH] I2C: add ...
220
221
  	dbg_chgstat(buf, sizeof buf, value);
  	seq_printf(s, "chgstat   %s", buf);
b5067f8ff   David Brownell   i2c/tps65010: New...
222
  	value = i2c_smbus_read_byte_data(tps->client, TPS_MASK1);
72cd79954   David Brownell   [PATCH] I2C: add ...
223
224
225
  	dbg_chgstat(buf, sizeof buf, value);
  	seq_printf(s, "mask1     %s", buf);
  	/* ignore ackint1 */
b5067f8ff   David Brownell   i2c/tps65010: New...
226
  	value = i2c_smbus_read_byte_data(tps->client, TPS_REGSTATUS);
72cd79954   David Brownell   [PATCH] I2C: add ...
227
228
  	dbg_regstat(buf, sizeof buf, value);
  	seq_printf(s, "regstat   %s", buf);
b5067f8ff   David Brownell   i2c/tps65010: New...
229
  	value = i2c_smbus_read_byte_data(tps->client, TPS_MASK2);
72cd79954   David Brownell   [PATCH] I2C: add ...
230
231
232
233
  	dbg_regstat(buf, sizeof buf, value);
  	seq_printf(s, "mask2     %s
  ", buf);
  	/* ignore ackint2 */
bb3d59342   Mark Brown   mfd: tps65010: Us...
234
235
  	queue_delayed_work(system_power_efficient_wq, &tps->work,
  			   POWER_POLL_DELAY);
72cd79954   David Brownell   [PATCH] I2C: add ...
236
237
  
  	/* VMAIN voltage, enable lowpower, etc */
b5067f8ff   David Brownell   i2c/tps65010: New...
238
  	value = i2c_smbus_read_byte_data(tps->client, TPS_VDCDC1);
72cd79954   David Brownell   [PATCH] I2C: add ...
239
240
241
242
  	seq_printf(s, "vdcdc1    %02x
  ", value);
  
  	/* VCORE voltage, vibrator on/off */
b5067f8ff   David Brownell   i2c/tps65010: New...
243
  	value = i2c_smbus_read_byte_data(tps->client, TPS_VDCDC2);
72cd79954   David Brownell   [PATCH] I2C: add ...
244
245
246
247
  	seq_printf(s, "vdcdc2    %02x
  ", value);
  
  	/* both LD0s, and their lowpower behavior */
b5067f8ff   David Brownell   i2c/tps65010: New...
248
  	value = i2c_smbus_read_byte_data(tps->client, TPS_VREGS1);
72cd79954   David Brownell   [PATCH] I2C: add ...
249
250
251
252
253
254
  	seq_printf(s, "vregs1    %02x
  
  ", value);
  
  
  	/* LEDs and GPIOs */
b5067f8ff   David Brownell   i2c/tps65010: New...
255
256
  	value = i2c_smbus_read_byte_data(tps->client, TPS_LED1_ON);
  	v2 = i2c_smbus_read_byte_data(tps->client, TPS_LED1_PER);
72cd79954   David Brownell   [PATCH] I2C: add ...
257
258
259
260
261
262
263
  	seq_printf(s, "led1 %s, on=%02x, per=%02x, %d/%d msec
  ",
  		(value & 0x80)
  			? ((v2 & 0x80) ? "on" : "off")
  			: ((v2 & 0x80) ? "blink" : "(nPG)"),
  		value, v2,
  		(value & 0x7f) * 10, (v2 & 0x7f) * 100);
b5067f8ff   David Brownell   i2c/tps65010: New...
264
265
  	value = i2c_smbus_read_byte_data(tps->client, TPS_LED2_ON);
  	v2 = i2c_smbus_read_byte_data(tps->client, TPS_LED2_PER);
72cd79954   David Brownell   [PATCH] I2C: add ...
266
267
268
269
270
271
272
  	seq_printf(s, "led2 %s, on=%02x, per=%02x, %d/%d msec
  ",
  		(value & 0x80)
  			? ((v2 & 0x80) ? "on" : "off")
  			: ((v2 & 0x80) ? "blink" : "off"),
  		value, v2,
  		(value & 0x7f) * 10, (v2 & 0x7f) * 100);
b5067f8ff   David Brownell   i2c/tps65010: New...
273
274
  	value = i2c_smbus_read_byte_data(tps->client, TPS_DEFGPIO);
  	v2 = i2c_smbus_read_byte_data(tps->client, TPS_MASK3);
72cd79954   David Brownell   [PATCH] I2C: add ...
275
276
277
278
  	seq_printf(s, "defgpio %02x mask3 %02x
  ", value, v2);
  
  	for (i = 0; i < 4; i++) {
65fc50e50   David Brownell   [PATCH] I2C: mino...
279
  		if (value & (1 << (4 + i)))
72cd79954   David Brownell   [PATCH] I2C: add ...
280
281
282
283
284
285
286
287
288
289
  			seq_printf(s, "  gpio%d-out %s
  ", i + 1,
  				(value & (1 << i)) ? "low" : "hi ");
  		else
  			seq_printf(s, "  gpio%d-in  %s %s %s
  ", i + 1,
  				(value & (1 << i)) ? "hi " : "low",
  				(v2 & (1 << i)) ? "no-irq" : "irq",
  				(v2 & (1 << (4 + i))) ? "rising" : "falling");
  	}
5c085d369   Ingo Molnar   [PATCH] i2c: Sema...
290
  	mutex_unlock(&tps->lock);
72cd79954   David Brownell   [PATCH] I2C: add ...
291
292
293
294
295
  	return 0;
  }
  
  static int dbg_tps_open(struct inode *inode, struct file *file)
  {
8e18e2941   Theodore Ts'o   [PATCH] inode_die...
296
  	return single_open(file, dbg_show, inode->i_private);
72cd79954   David Brownell   [PATCH] I2C: add ...
297
  }
2b8693c06   Arjan van de Ven   [PATCH] mark stru...
298
  static const struct file_operations debug_fops = {
72cd79954   David Brownell   [PATCH] I2C: add ...
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
  	.open		= dbg_tps_open,
  	.read		= seq_read,
  	.llseek		= seq_lseek,
  	.release	= single_release,
  };
  
  #define	DEBUG_FOPS	&debug_fops
  
  #else
  #define	DEBUG_FOPS	NULL
  #endif
  
  /*-------------------------------------------------------------------------*/
  
  /* handle IRQS in a task context, so we can use I2C calls */
  static void tps65010_interrupt(struct tps65010 *tps)
  {
  	u8 tmp = 0, mask, poll;
8c1bc04e7   David Brownell   [PATCH] fix more ...
317
  	/* IRQs won't trigger for certain events, but we can get
72cd79954   David Brownell   [PATCH] I2C: add ...
318
319
320
321
322
323
  	 * others by polling (normally, with external power applied).
  	 */
  	poll = 0;
  
  	/* regstatus irqs */
  	if (tps->nmask2) {
b5067f8ff   David Brownell   i2c/tps65010: New...
324
  		tmp = i2c_smbus_read_byte_data(tps->client, TPS_REGSTATUS);
72cd79954   David Brownell   [PATCH] I2C: add ...
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
  		mask = tmp ^ tps->regstatus;
  		tps->regstatus = tmp;
  		mask &= tps->nmask2;
  	} else
  		mask = 0;
  	if (mask) {
  		tps->regstatus =  tmp;
  		/* may need to shut something down ... */
  
  		/* "off" usually means deep sleep */
  		if (tmp & TPS_REG_ONOFF) {
  			pr_info("%s: power off button
  ", DRIVER_NAME);
  #if 0
  			/* REVISIT:  this might need its own workqueue
  			 * plus tweaks including deadlock avoidance ...
ab3bfca7a   Johannes Berg   remove software_s...
341
  			 * also needs to get error handling and probably
b0cb1a19d   Rafael J. Wysocki   Replace CONFIG_SO...
342
  			 * an #ifdef CONFIG_HIBERNATION
72cd79954   David Brownell   [PATCH] I2C: add ...
343
  			 */
a3d25c275   Rafael J. Wysocki   PM: Separate hibe...
344
  			hibernate();
72cd79954   David Brownell   [PATCH] I2C: add ...
345
346
347
348
349
350
351
  #endif
  			poll = 1;
  		}
  	}
  
  	/* chgstatus irqs */
  	if (tps->nmask1) {
b5067f8ff   David Brownell   i2c/tps65010: New...
352
  		tmp = i2c_smbus_read_byte_data(tps->client, TPS_CHGSTATUS);
72cd79954   David Brownell   [PATCH] I2C: add ...
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
  		mask = tmp ^ tps->chgstatus;
  		tps->chgstatus = tmp;
  		mask &= tps->nmask1;
  	} else
  		mask = 0;
  	if (mask) {
  		unsigned	charging = 0;
  
  		show_chgstatus("chg/irq", tmp);
  		if (tmp & (TPS_CHG_USB|TPS_CHG_AC))
  			show_chgconfig(tps->por, "conf", tps->chgconf);
  
  		/* Unless it was turned off or disabled, we charge any
  		 * battery whenever there's power available for it
  		 * and the charger hasn't been disabled.
  		 */
  		if (!(tps->chgstatus & ~(TPS_CHG_USB|TPS_CHG_AC))
  				&& (tps->chgstatus & (TPS_CHG_USB|TPS_CHG_AC))
  				&& (tps->chgconf & TPS_CHARGE_ENABLE)
  				) {
  			if (tps->chgstatus & TPS_CHG_USB) {
  				/* VBUS options are readonly until reconnect */
  				if (mask & TPS_CHG_USB)
  					set_bit(FLAG_VBUS_CHANGED, &tps->flags);
  				charging = 1;
  			} else if (tps->chgstatus & TPS_CHG_AC)
  				charging = 1;
  		}
  		if (charging != tps->charging) {
  			tps->charging = charging;
  			pr_info("%s: battery %scharging
  ",
  				DRIVER_NAME, charging ? "" :
  				((tps->chgstatus & (TPS_CHG_USB|TPS_CHG_AC))
  					? "NOT " : "dis"));
  		}
  	}
  
  	/* always poll to detect (a) power removal, without tps65013
  	 * NO_CHG IRQ; or (b) restart of charging after stop.
  	 */
  	if ((tps->model != TPS65013 || !tps->charging)
  			&& (tps->chgstatus & (TPS_CHG_USB|TPS_CHG_AC)))
  		poll = 1;
  	if (poll)
bb3d59342   Mark Brown   mfd: tps65010: Us...
398
399
  		queue_delayed_work(system_power_efficient_wq, &tps->work,
  				   POWER_POLL_DELAY);
72cd79954   David Brownell   [PATCH] I2C: add ...
400
401
402
403
404
  
  	/* also potentially gpio-in rise or fall */
  }
  
  /* handle IRQs and polling using keventd for now */
8c1bc04e7   David Brownell   [PATCH] fix more ...
405
  static void tps65010_work(struct work_struct *work)
72cd79954   David Brownell   [PATCH] I2C: add ...
406
  {
8c1bc04e7   David Brownell   [PATCH] fix more ...
407
  	struct tps65010		*tps;
72cd79954   David Brownell   [PATCH] I2C: add ...
408

afdb32f2e   Tejun Heo   mfd: update workq...
409
  	tps = container_of(to_delayed_work(work), struct tps65010, work);
5c085d369   Ingo Molnar   [PATCH] i2c: Sema...
410
  	mutex_lock(&tps->lock);
72cd79954   David Brownell   [PATCH] I2C: add ...
411
412
413
414
  
  	tps65010_interrupt(tps);
  
  	if (test_and_clear_bit(FLAG_VBUS_CHANGED, &tps->flags)) {
72cd79954   David Brownell   [PATCH] I2C: add ...
415
  		u8	chgconfig, tmp;
b5067f8ff   David Brownell   i2c/tps65010: New...
416
  		chgconfig = i2c_smbus_read_byte_data(tps->client,
72cd79954   David Brownell   [PATCH] I2C: add ...
417
418
419
420
421
422
  					TPS_CHGCONFIG);
  		chgconfig &= ~(TPS_VBUS_500MA | TPS_VBUS_CHARGING);
  		if (tps->vbus == 500)
  			chgconfig |= TPS_VBUS_500MA | TPS_VBUS_CHARGING;
  		else if (tps->vbus >= 100)
  			chgconfig |= TPS_VBUS_CHARGING;
2fbd58344   Lee Jones   mfd: tps65010: Re...
423
424
  		i2c_smbus_write_byte_data(tps->client,
  					  TPS_CHGCONFIG, chgconfig);
72cd79954   David Brownell   [PATCH] I2C: add ...
425
426
  
  		/* vbus update fails unless VBUS is connected! */
b5067f8ff   David Brownell   i2c/tps65010: New...
427
  		tmp = i2c_smbus_read_byte_data(tps->client, TPS_CHGCONFIG);
72cd79954   David Brownell   [PATCH] I2C: add ...
428
429
430
431
432
  		tps->chgconf = tmp;
  		show_chgconfig(tps->por, "update vbus", tmp);
  	}
  
  	if (test_and_clear_bit(FLAG_IRQ_ENABLE, &tps->flags))
8056c6cb2   David Brownell   i2c/tps65010: New...
433
  		enable_irq(tps->client->irq);
72cd79954   David Brownell   [PATCH] I2C: add ...
434

5c085d369   Ingo Molnar   [PATCH] i2c: Sema...
435
  	mutex_unlock(&tps->lock);
72cd79954   David Brownell   [PATCH] I2C: add ...
436
  }
7d12e780e   David Howells   IRQ: Maintain reg...
437
  static irqreturn_t tps65010_irq(int irq, void *_tps)
72cd79954   David Brownell   [PATCH] I2C: add ...
438
439
440
441
442
  {
  	struct tps65010		*tps = _tps;
  
  	disable_irq_nosync(irq);
  	set_bit(FLAG_IRQ_ENABLE, &tps->flags);
bb3d59342   Mark Brown   mfd: tps65010: Us...
443
  	queue_delayed_work(system_power_efficient_wq, &tps->work, 0);
72cd79954   David Brownell   [PATCH] I2C: add ...
444
445
446
447
  	return IRQ_HANDLED;
  }
  
  /*-------------------------------------------------------------------------*/
79966fd9b   David Brownell   ARM: OMAP: I2C: t...
448
449
  /* offsets 0..3 == GPIO1..GPIO4
   * offsets 4..5 == LED1/nPG, LED2 (we set one of the non-BLINK modes)
b84ee0b0c   Marek Vasut   i2c/tps65010: Vib...
450
   * offset 6 == vibrator motor driver
79966fd9b   David Brownell   ARM: OMAP: I2C: t...
451
452
453
454
455
456
   */
  static void
  tps65010_gpio_set(struct gpio_chip *chip, unsigned offset, int value)
  {
  	if (offset < 4)
  		tps65010_set_gpio_out_value(offset + 1, value);
b84ee0b0c   Marek Vasut   i2c/tps65010: Vib...
457
  	else if (offset < 6)
79966fd9b   David Brownell   ARM: OMAP: I2C: t...
458
  		tps65010_set_led(offset - 3, value ? ON : OFF);
b84ee0b0c   Marek Vasut   i2c/tps65010: Vib...
459
460
  	else
  		tps65010_set_vib(value);
79966fd9b   David Brownell   ARM: OMAP: I2C: t...
461
462
463
464
465
466
467
468
  }
  
  static int
  tps65010_output(struct gpio_chip *chip, unsigned offset, int value)
  {
  	/* GPIOs may be input-only */
  	if (offset < 4) {
  		struct tps65010		*tps;
22e5e747e   Linus Walleij   mfd: tps65010: Us...
469
  		tps = gpiochip_get_data(chip);
79966fd9b   David Brownell   ARM: OMAP: I2C: t...
470
471
472
  		if (!(tps->outmask & (1 << offset)))
  			return -EINVAL;
  		tps65010_set_gpio_out_value(offset + 1, value);
b84ee0b0c   Marek Vasut   i2c/tps65010: Vib...
473
  	} else if (offset < 6)
79966fd9b   David Brownell   ARM: OMAP: I2C: t...
474
  		tps65010_set_led(offset - 3, value ? ON : OFF);
b84ee0b0c   Marek Vasut   i2c/tps65010: Vib...
475
476
  	else
  		tps65010_set_vib(value);
79966fd9b   David Brownell   ARM: OMAP: I2C: t...
477
478
479
480
481
482
483
484
  
  	return 0;
  }
  
  static int tps65010_gpio_get(struct gpio_chip *chip, unsigned offset)
  {
  	int			value;
  	struct tps65010		*tps;
22e5e747e   Linus Walleij   mfd: tps65010: Us...
485
  	tps = gpiochip_get_data(chip);
79966fd9b   David Brownell   ARM: OMAP: I2C: t...
486
487
488
489
  
  	if (offset < 4) {
  		value = i2c_smbus_read_byte_data(tps->client, TPS_DEFGPIO);
  		if (value < 0)
bf3de47f1   Linus Walleij   mfd: tps65010: Be...
490
  			return value;
79966fd9b   David Brownell   ARM: OMAP: I2C: t...
491
492
493
  		if (value & (1 << (offset + 4)))	/* output */
  			return !(value & (1 << offset));
  		else					/* input */
bf3de47f1   Linus Walleij   mfd: tps65010: Be...
494
  			return !!(value & (1 << offset));
79966fd9b   David Brownell   ARM: OMAP: I2C: t...
495
496
497
498
499
500
501
502
  	}
  
  	/* REVISIT we *could* report LED1/nPG and LED2 state ... */
  	return 0;
  }
  
  
  /*-------------------------------------------------------------------------*/
72cd79954   David Brownell   [PATCH] I2C: add ...
503
  static struct tps65010 *the_tps;
bb7337079   Dmitry Torokhov   mfd: tps65010: Re...
504
  static int tps65010_remove(struct i2c_client *client)
72cd79954   David Brownell   [PATCH] I2C: add ...
505
  {
8056c6cb2   David Brownell   i2c/tps65010: New...
506
  	struct tps65010		*tps = i2c_get_clientdata(client);
334a41ce9   Jingoo Han   mfd: Use dev_get_...
507
  	struct tps65010_board	*board = dev_get_platdata(&client->dev);
72cd79954   David Brownell   [PATCH] I2C: add ...
508

79966fd9b   David Brownell   ARM: OMAP: I2C: t...
509
510
511
512
513
514
515
  	if (board && board->teardown) {
  		int status = board->teardown(client, board->context);
  		if (status < 0)
  			dev_dbg(&client->dev, "board %s %s err %d
  ",
  				"teardown", client->name, status);
  	}
8056c6cb2   David Brownell   i2c/tps65010: New...
516
517
  	if (client->irq > 0)
  		free_irq(client->irq, tps);
afdb32f2e   Tejun Heo   mfd: update workq...
518
  	cancel_delayed_work_sync(&tps->work);
72cd79954   David Brownell   [PATCH] I2C: add ...
519
  	debugfs_remove(tps->file);
65fc50e50   David Brownell   [PATCH] I2C: mino...
520
  	the_tps = NULL;
72cd79954   David Brownell   [PATCH] I2C: add ...
521
522
  	return 0;
  }
d2653e927   Jean Delvare   i2c: Add support ...
523
524
  static int tps65010_probe(struct i2c_client *client,
  			  const struct i2c_device_id *id)
72cd79954   David Brownell   [PATCH] I2C: add ...
525
526
527
  {
  	struct tps65010		*tps;
  	int			status;
334a41ce9   Jingoo Han   mfd: Use dev_get_...
528
  	struct tps65010_board	*board = dev_get_platdata(&client->dev);
72cd79954   David Brownell   [PATCH] I2C: add ...
529
530
  
  	if (the_tps) {
8056c6cb2   David Brownell   i2c/tps65010: New...
531
532
533
  		dev_dbg(&client->dev, "only one tps6501x chip allowed
  ");
  		return -ENODEV;
72cd79954   David Brownell   [PATCH] I2C: add ...
534
  	}
8056c6cb2   David Brownell   i2c/tps65010: New...
535
536
  	if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA))
  		return -EINVAL;
eac1dcbd3   Jingoo Han   mfd: tps65010: Us...
537
  	tps = devm_kzalloc(&client->dev, sizeof(*tps), GFP_KERNEL);
72cd79954   David Brownell   [PATCH] I2C: add ...
538
  	if (!tps)
8056c6cb2   David Brownell   i2c/tps65010: New...
539
  		return -ENOMEM;
72cd79954   David Brownell   [PATCH] I2C: add ...
540

5c085d369   Ingo Molnar   [PATCH] i2c: Sema...
541
  	mutex_init(&tps->lock);
8c1bc04e7   David Brownell   [PATCH] fix more ...
542
  	INIT_DELAYED_WORK(&tps->work, tps65010_work);
8056c6cb2   David Brownell   i2c/tps65010: New...
543
  	tps->client = client;
3760f7367   Jean Delvare   i2c: Convert most...
544
  	tps->model = id->driver_data;
72cd79954   David Brownell   [PATCH] I2C: add ...
545

8056c6cb2   David Brownell   i2c/tps65010: New...
546
547
548
549
550
  	/* the IRQ is active low, but many gpio lines can't support that
  	 * so this driver uses falling-edge triggers instead.
  	 */
  	if (client->irq > 0) {
  		status = request_irq(client->irq, tps65010_irq,
212436c2a   Theodore Ts'o   mfd: remove IRQF_...
551
  				     IRQF_TRIGGER_FALLING, DRIVER_NAME, tps);
72cd79954   David Brownell   [PATCH] I2C: add ...
552
  		if (status < 0) {
b5067f8ff   David Brownell   i2c/tps65010: New...
553
554
  			dev_dbg(&client->dev, "can't get IRQ %d, err %d
  ",
8056c6cb2   David Brownell   i2c/tps65010: New...
555
  					client->irq, status);
eac1dcbd3   Jingoo Han   mfd: tps65010: Us...
556
  			return status;
72cd79954   David Brownell   [PATCH] I2C: add ...
557
  		}
72cd79954   David Brownell   [PATCH] I2C: add ...
558
559
  		/* annoying race here, ideally we'd have an option
  		 * to claim the irq now and enable it later.
8056c6cb2   David Brownell   i2c/tps65010: New...
560
  		 * FIXME genirq IRQF_NOAUTOEN now solves that ...
72cd79954   David Brownell   [PATCH] I2C: add ...
561
  		 */
8056c6cb2   David Brownell   i2c/tps65010: New...
562
  		disable_irq(client->irq);
72cd79954   David Brownell   [PATCH] I2C: add ...
563
  		set_bit(FLAG_IRQ_ENABLE, &tps->flags);
72cd79954   David Brownell   [PATCH] I2C: add ...
564
  	} else
8056c6cb2   David Brownell   i2c/tps65010: New...
565
566
  		dev_warn(&client->dev, "IRQ not configured!
  ");
72cd79954   David Brownell   [PATCH] I2C: add ...
567
568
569
570
571
572
573
  
  
  	switch (tps->model) {
  	case TPS65010:
  	case TPS65012:
  		tps->por = 1;
  		break;
72cd79954   David Brownell   [PATCH] I2C: add ...
574
575
  	/* else CHGCONFIG.POR is replaced by AUA, enabling a WAIT mode */
  	}
b5067f8ff   David Brownell   i2c/tps65010: New...
576
  	tps->chgconf = i2c_smbus_read_byte_data(client, TPS_CHGCONFIG);
72cd79954   David Brownell   [PATCH] I2C: add ...
577
578
579
  	show_chgconfig(tps->por, "conf/init", tps->chgconf);
  
  	show_chgstatus("chg/init",
b5067f8ff   David Brownell   i2c/tps65010: New...
580
  		i2c_smbus_read_byte_data(client, TPS_CHGSTATUS));
72cd79954   David Brownell   [PATCH] I2C: add ...
581
  	show_regstatus("reg/init",
b5067f8ff   David Brownell   i2c/tps65010: New...
582
  		i2c_smbus_read_byte_data(client, TPS_REGSTATUS));
72cd79954   David Brownell   [PATCH] I2C: add ...
583
584
585
  
  	pr_debug("%s: vdcdc1 0x%02x, vdcdc2 %02x, vregs1 %02x
  ", DRIVER_NAME,
b5067f8ff   David Brownell   i2c/tps65010: New...
586
587
588
  		i2c_smbus_read_byte_data(client, TPS_VDCDC1),
  		i2c_smbus_read_byte_data(client, TPS_VDCDC2),
  		i2c_smbus_read_byte_data(client, TPS_VREGS1));
72cd79954   David Brownell   [PATCH] I2C: add ...
589
590
  	pr_debug("%s: defgpio 0x%02x, mask3 0x%02x
  ", DRIVER_NAME,
b5067f8ff   David Brownell   i2c/tps65010: New...
591
592
  		i2c_smbus_read_byte_data(client, TPS_DEFGPIO),
  		i2c_smbus_read_byte_data(client, TPS_MASK3));
72cd79954   David Brownell   [PATCH] I2C: add ...
593

6d072d78f   Jean Delvare   i2c/tps65010: Add...
594
  	i2c_set_clientdata(client, tps);
72cd79954   David Brownell   [PATCH] I2C: add ...
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
  	the_tps = tps;
  
  #if	defined(CONFIG_USB_GADGET) && !defined(CONFIG_USB_OTG)
  	/* USB hosts can't draw VBUS.  OTG devices could, later
  	 * when OTG infrastructure enables it.  USB peripherals
  	 * could be relying on VBUS while booting, though.
  	 */
  	tps->vbus = 100;
  #endif
  
  	/* unmask the "interesting" irqs, then poll once to
  	 * kickstart monitoring, initialize shadowed status
  	 * registers, and maybe disable VBUS draw.
  	 */
  	tps->nmask1 = ~0;
b5067f8ff   David Brownell   i2c/tps65010: New...
610
  	(void) i2c_smbus_write_byte_data(client, TPS_MASK1, ~tps->nmask1);
72cd79954   David Brownell   [PATCH] I2C: add ...
611
612
613
614
  
  	tps->nmask2 = TPS_REG_ONOFF;
  	if (tps->model == TPS65013)
  		tps->nmask2 |= TPS_REG_NO_CHG;
b5067f8ff   David Brownell   i2c/tps65010: New...
615
  	(void) i2c_smbus_write_byte_data(client, TPS_MASK2, ~tps->nmask2);
72cd79954   David Brownell   [PATCH] I2C: add ...
616

b5067f8ff   David Brownell   i2c/tps65010: New...
617
618
  	(void) i2c_smbus_write_byte_data(client, TPS_MASK3, 0x0f
  		| i2c_smbus_read_byte_data(client, TPS_MASK3));
72cd79954   David Brownell   [PATCH] I2C: add ...
619

8c1bc04e7   David Brownell   [PATCH] fix more ...
620
  	tps65010_work(&tps->work.work);
72cd79954   David Brownell   [PATCH] I2C: add ...
621
622
623
  
  	tps->file = debugfs_create_file(DRIVER_NAME, S_IRUGO, NULL,
  				tps, DEBUG_FOPS);
79966fd9b   David Brownell   ARM: OMAP: I2C: t...
624
625
  
  	/* optionally register GPIOs */
848369926   Ben Dooks   mfd: Allow the bo...
626
  	if (board && board->base != 0) {
79966fd9b   David Brownell   ARM: OMAP: I2C: t...
627
628
629
  		tps->outmask = board->outmask;
  
  		tps->chip.label = client->name;
58383c784   Linus Walleij   gpio: change memb...
630
  		tps->chip.parent = &client->dev;
d8f388d8d   David Brownell   gpio: sysfs inter...
631
  		tps->chip.owner = THIS_MODULE;
79966fd9b   David Brownell   ARM: OMAP: I2C: t...
632
633
634
635
636
637
638
639
  
  		tps->chip.set = tps65010_gpio_set;
  		tps->chip.direction_output = tps65010_output;
  
  		/* NOTE:  only partial support for inputs; nyet IRQs */
  		tps->chip.get = tps65010_gpio_get;
  
  		tps->chip.base = board->base;
b84ee0b0c   Marek Vasut   i2c/tps65010: Vib...
640
  		tps->chip.ngpio = 7;
79966fd9b   David Brownell   ARM: OMAP: I2C: t...
641
  		tps->chip.can_sleep = 1;
22e5e747e   Linus Walleij   mfd: tps65010: Us...
642
  		status = gpiochip_add_data(&tps->chip, tps);
79966fd9b   David Brownell   ARM: OMAP: I2C: t...
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
  		if (status < 0)
  			dev_err(&client->dev, "can't add gpiochip, err %d
  ",
  					status);
  		else if (board->setup) {
  			status = board->setup(client, board->context);
  			if (status < 0) {
  				dev_dbg(&client->dev,
  					"board %s %s err %d
  ",
  					"setup", client->name, status);
  				status = 0;
  			}
  		}
  	}
72cd79954   David Brownell   [PATCH] I2C: add ...
658
  	return 0;
72cd79954   David Brownell   [PATCH] I2C: add ...
659
  }
3760f7367   Jean Delvare   i2c: Convert most...
660
661
662
663
664
  static const struct i2c_device_id tps65010_id[] = {
  	{ "tps65010", TPS65010 },
  	{ "tps65011", TPS65011 },
  	{ "tps65012", TPS65012 },
  	{ "tps65013", TPS65013 },
b84ee0b0c   Marek Vasut   i2c/tps65010: Vib...
665
  	{ "tps65014", TPS65011 },	/* tps65011 charging at 6.5V max */
3760f7367   Jean Delvare   i2c: Convert most...
666
667
668
  	{ }
  };
  MODULE_DEVICE_TABLE(i2c, tps65010_id);
72cd79954   David Brownell   [PATCH] I2C: add ...
669
  static struct i2c_driver tps65010_driver = {
a9718b0c1   Laurent Riffard   [PATCH] i2c: Drop...
670
  	.driver = {
a9718b0c1   Laurent Riffard   [PATCH] i2c: Drop...
671
672
  		.name	= "tps65010",
  	},
8056c6cb2   David Brownell   i2c/tps65010: New...
673
  	.probe	= tps65010_probe,
bb7337079   Dmitry Torokhov   mfd: tps65010: Re...
674
  	.remove	= tps65010_remove,
3760f7367   Jean Delvare   i2c: Convert most...
675
  	.id_table = tps65010_id,
72cd79954   David Brownell   [PATCH] I2C: add ...
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
  };
  
  /*-------------------------------------------------------------------------*/
  
  /* Draw from VBUS:
   *   0 mA -- DON'T DRAW (might supply power instead)
   * 100 mA -- usb unit load (slowest charge rate)
   * 500 mA -- usb high power (fast battery charge)
   */
  int tps65010_set_vbus_draw(unsigned mA)
  {
  	unsigned long	flags;
  
  	if (!the_tps)
  		return -ENODEV;
  
  	/* assumes non-SMP */
  	local_irq_save(flags);
  	if (mA >= 500)
  		mA = 500;
  	else if (mA >= 100)
  		mA = 100;
  	else
  		mA = 0;
  	the_tps->vbus = mA;
  	if ((the_tps->chgstatus & TPS_CHG_USB)
  			&& test_and_set_bit(
  				FLAG_VBUS_CHANGED, &the_tps->flags)) {
  		/* gadget drivers call this in_irq() */
bb3d59342   Mark Brown   mfd: tps65010: Us...
705
706
  		queue_delayed_work(system_power_efficient_wq, &the_tps->work,
  				   0);
72cd79954   David Brownell   [PATCH] I2C: add ...
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
  	}
  	local_irq_restore(flags);
  
  	return 0;
  }
  EXPORT_SYMBOL(tps65010_set_vbus_draw);
  
  /*-------------------------------------------------------------------------*/
  /* tps65010_set_gpio_out_value parameter:
   * gpio:  GPIO1, GPIO2, GPIO3 or GPIO4
   * value: LOW or HIGH
   */
  int tps65010_set_gpio_out_value(unsigned gpio, unsigned value)
  {
  	int	 status;
  	unsigned defgpio;
  
  	if (!the_tps)
  		return -ENODEV;
  	if ((gpio < GPIO1) || (gpio > GPIO4))
  		return -EINVAL;
5c085d369   Ingo Molnar   [PATCH] i2c: Sema...
728
  	mutex_lock(&the_tps->lock);
72cd79954   David Brownell   [PATCH] I2C: add ...
729

b5067f8ff   David Brownell   i2c/tps65010: New...
730
  	defgpio = i2c_smbus_read_byte_data(the_tps->client, TPS_DEFGPIO);
72cd79954   David Brownell   [PATCH] I2C: add ...
731
732
733
734
735
736
737
738
739
740
741
742
743
744
  
  	/* Configure GPIO for output */
  	defgpio |= 1 << (gpio + 3);
  
  	/* Writing 1 forces a logic 0 on that GPIO and vice versa */
  	switch (value) {
  	case LOW:
  		defgpio |= 1 << (gpio - 1);    /* set GPIO low by writing 1 */
  		break;
  	/* case HIGH: */
  	default:
  		defgpio &= ~(1 << (gpio - 1)); /* set GPIO high by writing 0 */
  		break;
  	}
b5067f8ff   David Brownell   i2c/tps65010: New...
745
  	status = i2c_smbus_write_byte_data(the_tps->client,
72cd79954   David Brownell   [PATCH] I2C: add ...
746
747
748
749
750
  		TPS_DEFGPIO, defgpio);
  
  	pr_debug("%s: gpio%dout = %s, defgpio 0x%02x
  ", DRIVER_NAME,
  		gpio, value ? "high" : "low",
b5067f8ff   David Brownell   i2c/tps65010: New...
751
  		i2c_smbus_read_byte_data(the_tps->client, TPS_DEFGPIO));
72cd79954   David Brownell   [PATCH] I2C: add ...
752

5c085d369   Ingo Molnar   [PATCH] i2c: Sema...
753
  	mutex_unlock(&the_tps->lock);
72cd79954   David Brownell   [PATCH] I2C: add ...
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
  	return status;
  }
  EXPORT_SYMBOL(tps65010_set_gpio_out_value);
  
  /*-------------------------------------------------------------------------*/
  /* tps65010_set_led parameter:
   * led:  LED1 or LED2
   * mode: ON, OFF or BLINK
   */
  int tps65010_set_led(unsigned led, unsigned mode)
  {
  	int	 status;
  	unsigned led_on, led_per, offs;
  
  	if (!the_tps)
  		return -ENODEV;
65fc50e50   David Brownell   [PATCH] I2C: mino...
770
  	if (led == LED1)
72cd79954   David Brownell   [PATCH] I2C: add ...
771
772
773
774
775
  		offs = 0;
  	else {
  		offs = 2;
  		led = LED2;
  	}
5c085d369   Ingo Molnar   [PATCH] i2c: Sema...
776
  	mutex_lock(&the_tps->lock);
72cd79954   David Brownell   [PATCH] I2C: add ...
777

65fc50e50   David Brownell   [PATCH] I2C: mino...
778
779
  	pr_debug("%s: led%i_on   0x%02x
  ", DRIVER_NAME, led,
b5067f8ff   David Brownell   i2c/tps65010: New...
780
  		i2c_smbus_read_byte_data(the_tps->client,
65fc50e50   David Brownell   [PATCH] I2C: mino...
781
  				TPS_LED1_ON + offs));
72cd79954   David Brownell   [PATCH] I2C: add ...
782

65fc50e50   David Brownell   [PATCH] I2C: mino...
783
784
  	pr_debug("%s: led%i_per  0x%02x
  ", DRIVER_NAME, led,
b5067f8ff   David Brownell   i2c/tps65010: New...
785
  		i2c_smbus_read_byte_data(the_tps->client,
65fc50e50   David Brownell   [PATCH] I2C: mino...
786
  				TPS_LED1_PER + offs));
72cd79954   David Brownell   [PATCH] I2C: add ...
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
  
  	switch (mode) {
  	case OFF:
  		led_on  = 1 << 7;
  		led_per = 0 << 7;
  		break;
  	case ON:
  		led_on  = 1 << 7;
  		led_per = 1 << 7;
  		break;
  	case BLINK:
  		led_on  = 0x30 | (0 << 7);
  		led_per = 0x08 | (1 << 7);
  		break;
  	default:
65fc50e50   David Brownell   [PATCH] I2C: mino...
802
803
  		printk(KERN_ERR "%s: Wrong mode parameter for set_led()
  ",
72cd79954   David Brownell   [PATCH] I2C: add ...
804
  		       DRIVER_NAME);
5c085d369   Ingo Molnar   [PATCH] i2c: Sema...
805
  		mutex_unlock(&the_tps->lock);
72cd79954   David Brownell   [PATCH] I2C: add ...
806
807
  		return -EINVAL;
  	}
b5067f8ff   David Brownell   i2c/tps65010: New...
808
  	status = i2c_smbus_write_byte_data(the_tps->client,
72cd79954   David Brownell   [PATCH] I2C: add ...
809
810
811
812
813
814
  			TPS_LED1_ON + offs, led_on);
  
  	if (status != 0) {
  		printk(KERN_ERR "%s: Failed to write led%i_on register
  ",
  		       DRIVER_NAME, led);
5c085d369   Ingo Molnar   [PATCH] i2c: Sema...
815
  		mutex_unlock(&the_tps->lock);
72cd79954   David Brownell   [PATCH] I2C: add ...
816
817
  		return status;
  	}
65fc50e50   David Brownell   [PATCH] I2C: mino...
818
819
  	pr_debug("%s: led%i_on   0x%02x
  ", DRIVER_NAME, led,
b5067f8ff   David Brownell   i2c/tps65010: New...
820
  		i2c_smbus_read_byte_data(the_tps->client, TPS_LED1_ON + offs));
72cd79954   David Brownell   [PATCH] I2C: add ...
821

b5067f8ff   David Brownell   i2c/tps65010: New...
822
  	status = i2c_smbus_write_byte_data(the_tps->client,
72cd79954   David Brownell   [PATCH] I2C: add ...
823
824
825
826
827
828
  			TPS_LED1_PER + offs, led_per);
  
  	if (status != 0) {
  		printk(KERN_ERR "%s: Failed to write led%i_per register
  ",
  		       DRIVER_NAME, led);
5c085d369   Ingo Molnar   [PATCH] i2c: Sema...
829
  		mutex_unlock(&the_tps->lock);
72cd79954   David Brownell   [PATCH] I2C: add ...
830
831
  		return status;
  	}
65fc50e50   David Brownell   [PATCH] I2C: mino...
832
833
  	pr_debug("%s: led%i_per  0x%02x
  ", DRIVER_NAME, led,
b5067f8ff   David Brownell   i2c/tps65010: New...
834
  		i2c_smbus_read_byte_data(the_tps->client,
65fc50e50   David Brownell   [PATCH] I2C: mino...
835
  				TPS_LED1_PER + offs));
72cd79954   David Brownell   [PATCH] I2C: add ...
836

5c085d369   Ingo Molnar   [PATCH] i2c: Sema...
837
  	mutex_unlock(&the_tps->lock);
72cd79954   David Brownell   [PATCH] I2C: add ...
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
  
  	return status;
  }
  EXPORT_SYMBOL(tps65010_set_led);
  
  /*-------------------------------------------------------------------------*/
  /* tps65010_set_vib parameter:
   * value: ON or OFF
   */
  int tps65010_set_vib(unsigned value)
  {
  	int	 status;
  	unsigned vdcdc2;
  
  	if (!the_tps)
  		return -ENODEV;
5c085d369   Ingo Molnar   [PATCH] i2c: Sema...
854
  	mutex_lock(&the_tps->lock);
72cd79954   David Brownell   [PATCH] I2C: add ...
855

b5067f8ff   David Brownell   i2c/tps65010: New...
856
  	vdcdc2 = i2c_smbus_read_byte_data(the_tps->client, TPS_VDCDC2);
72cd79954   David Brownell   [PATCH] I2C: add ...
857
858
859
  	vdcdc2 &= ~(1 << 1);
  	if (value)
  		vdcdc2 |= (1 << 1);
b5067f8ff   David Brownell   i2c/tps65010: New...
860
  	status = i2c_smbus_write_byte_data(the_tps->client,
72cd79954   David Brownell   [PATCH] I2C: add ...
861
862
863
864
  		TPS_VDCDC2, vdcdc2);
  
  	pr_debug("%s: vibrator %s
  ", DRIVER_NAME, value ? "on" : "off");
5c085d369   Ingo Molnar   [PATCH] i2c: Sema...
865
  	mutex_unlock(&the_tps->lock);
72cd79954   David Brownell   [PATCH] I2C: add ...
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
  	return status;
  }
  EXPORT_SYMBOL(tps65010_set_vib);
  
  /*-------------------------------------------------------------------------*/
  /* tps65010_set_low_pwr parameter:
   * mode: ON or OFF
   */
  int tps65010_set_low_pwr(unsigned mode)
  {
  	int	 status;
  	unsigned vdcdc1;
  
  	if (!the_tps)
  		return -ENODEV;
5c085d369   Ingo Molnar   [PATCH] i2c: Sema...
881
  	mutex_lock(&the_tps->lock);
72cd79954   David Brownell   [PATCH] I2C: add ...
882
883
884
885
  
  	pr_debug("%s: %s low_pwr, vdcdc1 0x%02x
  ", DRIVER_NAME,
  		mode ? "enable" : "disable",
b5067f8ff   David Brownell   i2c/tps65010: New...
886
  		i2c_smbus_read_byte_data(the_tps->client, TPS_VDCDC1));
72cd79954   David Brownell   [PATCH] I2C: add ...
887

b5067f8ff   David Brownell   i2c/tps65010: New...
888
  	vdcdc1 = i2c_smbus_read_byte_data(the_tps->client, TPS_VDCDC1);
72cd79954   David Brownell   [PATCH] I2C: add ...
889
890
891
892
893
894
895
896
897
898
  
  	switch (mode) {
  	case OFF:
  		vdcdc1 &= ~TPS_ENABLE_LP; /* disable ENABLE_LP bit */
  		break;
  	/* case ON: */
  	default:
  		vdcdc1 |= TPS_ENABLE_LP;  /* enable ENABLE_LP bit */
  		break;
  	}
b5067f8ff   David Brownell   i2c/tps65010: New...
899
  	status = i2c_smbus_write_byte_data(the_tps->client,
72cd79954   David Brownell   [PATCH] I2C: add ...
900
901
902
903
904
  			TPS_VDCDC1, vdcdc1);
  
  	if (status != 0)
  		printk(KERN_ERR "%s: Failed to write vdcdc1 register
  ",
65fc50e50   David Brownell   [PATCH] I2C: mino...
905
  			DRIVER_NAME);
72cd79954   David Brownell   [PATCH] I2C: add ...
906
907
908
  	else
  		pr_debug("%s: vdcdc1 0x%02x
  ", DRIVER_NAME,
b5067f8ff   David Brownell   i2c/tps65010: New...
909
  			i2c_smbus_read_byte_data(the_tps->client, TPS_VDCDC1));
72cd79954   David Brownell   [PATCH] I2C: add ...
910

5c085d369   Ingo Molnar   [PATCH] i2c: Sema...
911
  	mutex_unlock(&the_tps->lock);
72cd79954   David Brownell   [PATCH] I2C: add ...
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
  
  	return status;
  }
  EXPORT_SYMBOL(tps65010_set_low_pwr);
  
  /*-------------------------------------------------------------------------*/
  /* tps65010_config_vregs1 parameter:
   * value to be written to VREGS1 register
   * Note: The complete register is written, set all bits you need
   */
  int tps65010_config_vregs1(unsigned value)
  {
  	int	 status;
  
  	if (!the_tps)
  		return -ENODEV;
5c085d369   Ingo Molnar   [PATCH] i2c: Sema...
928
  	mutex_lock(&the_tps->lock);
72cd79954   David Brownell   [PATCH] I2C: add ...
929
930
931
  
  	pr_debug("%s: vregs1 0x%02x
  ", DRIVER_NAME,
b5067f8ff   David Brownell   i2c/tps65010: New...
932
  			i2c_smbus_read_byte_data(the_tps->client, TPS_VREGS1));
72cd79954   David Brownell   [PATCH] I2C: add ...
933

b5067f8ff   David Brownell   i2c/tps65010: New...
934
  	status = i2c_smbus_write_byte_data(the_tps->client,
72cd79954   David Brownell   [PATCH] I2C: add ...
935
936
937
938
939
  			TPS_VREGS1, value);
  
  	if (status != 0)
  		printk(KERN_ERR "%s: Failed to write vregs1 register
  ",
65fc50e50   David Brownell   [PATCH] I2C: mino...
940
  			DRIVER_NAME);
72cd79954   David Brownell   [PATCH] I2C: add ...
941
942
943
  	else
  		pr_debug("%s: vregs1 0x%02x
  ", DRIVER_NAME,
b5067f8ff   David Brownell   i2c/tps65010: New...
944
  			i2c_smbus_read_byte_data(the_tps->client, TPS_VREGS1));
72cd79954   David Brownell   [PATCH] I2C: add ...
945

5c085d369   Ingo Molnar   [PATCH] i2c: Sema...
946
  	mutex_unlock(&the_tps->lock);
72cd79954   David Brownell   [PATCH] I2C: add ...
947
948
949
950
  
  	return status;
  }
  EXPORT_SYMBOL(tps65010_config_vregs1);
b45440c33   Ben Dooks   mfd: Allow config...
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
  int tps65010_config_vdcdc2(unsigned value)
  {
  	struct i2c_client *c;
  	int	 status;
  
  	if (!the_tps)
  		return -ENODEV;
  
  	c = the_tps->client;
  	mutex_lock(&the_tps->lock);
  
  	pr_debug("%s: vdcdc2 0x%02x
  ", DRIVER_NAME,
  		 i2c_smbus_read_byte_data(c, TPS_VDCDC2));
  
  	status = i2c_smbus_write_byte_data(c, TPS_VDCDC2, value);
  
  	if (status != 0)
  		printk(KERN_ERR "%s: Failed to write vdcdc2 register
  ",
  			DRIVER_NAME);
  	else
  		pr_debug("%s: vregs1 0x%02x
  ", DRIVER_NAME,
  			 i2c_smbus_read_byte_data(c, TPS_VDCDC2));
  
  	mutex_unlock(&the_tps->lock);
  	return status;
  }
  EXPORT_SYMBOL(tps65010_config_vdcdc2);
72cd79954   David Brownell   [PATCH] I2C: add ...
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
  /*-------------------------------------------------------------------------*/
  /* tps65013_set_low_pwr parameter:
   * mode: ON or OFF
   */
  
  /* FIXME: Assumes AC or USB power is present. Setting AUA bit is not
  	required if power supply is through a battery */
  
  int tps65013_set_low_pwr(unsigned mode)
  {
  	int	 status;
  	unsigned vdcdc1, chgconfig;
  
  	if (!the_tps || the_tps->por)
  		return -ENODEV;
5c085d369   Ingo Molnar   [PATCH] i2c: Sema...
996
  	mutex_lock(&the_tps->lock);
72cd79954   David Brownell   [PATCH] I2C: add ...
997
998
999
1000
1001
  
  	pr_debug("%s: %s low_pwr, chgconfig 0x%02x vdcdc1 0x%02x
  ",
  		DRIVER_NAME,
  		mode ? "enable" : "disable",
b5067f8ff   David Brownell   i2c/tps65010: New...
1002
1003
  		i2c_smbus_read_byte_data(the_tps->client, TPS_CHGCONFIG),
  		i2c_smbus_read_byte_data(the_tps->client, TPS_VDCDC1));
72cd79954   David Brownell   [PATCH] I2C: add ...
1004

b5067f8ff   David Brownell   i2c/tps65010: New...
1005
1006
  	chgconfig = i2c_smbus_read_byte_data(the_tps->client, TPS_CHGCONFIG);
  	vdcdc1 = i2c_smbus_read_byte_data(the_tps->client, TPS_VDCDC1);
72cd79954   David Brownell   [PATCH] I2C: add ...
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
  
  	switch (mode) {
  	case OFF:
  		chgconfig &= ~TPS65013_AUA; /* disable AUA bit */
  		vdcdc1 &= ~TPS_ENABLE_LP; /* disable ENABLE_LP bit */
  		break;
  	/* case ON: */
  	default:
  		chgconfig |= TPS65013_AUA;  /* enable AUA bit */
  		vdcdc1 |= TPS_ENABLE_LP;  /* enable ENABLE_LP bit */
  		break;
  	}
b5067f8ff   David Brownell   i2c/tps65010: New...
1019
  	status = i2c_smbus_write_byte_data(the_tps->client,
72cd79954   David Brownell   [PATCH] I2C: add ...
1020
1021
1022
1023
1024
  			TPS_CHGCONFIG, chgconfig);
  	if (status != 0) {
  		printk(KERN_ERR "%s: Failed to write chconfig register
  ",
  	 DRIVER_NAME);
5c085d369   Ingo Molnar   [PATCH] i2c: Sema...
1025
  		mutex_unlock(&the_tps->lock);
72cd79954   David Brownell   [PATCH] I2C: add ...
1026
1027
  		return status;
  	}
b5067f8ff   David Brownell   i2c/tps65010: New...
1028
  	chgconfig = i2c_smbus_read_byte_data(the_tps->client, TPS_CHGCONFIG);
72cd79954   David Brownell   [PATCH] I2C: add ...
1029
1030
  	the_tps->chgconf = chgconfig;
  	show_chgconfig(0, "chgconf", chgconfig);
b5067f8ff   David Brownell   i2c/tps65010: New...
1031
  	status = i2c_smbus_write_byte_data(the_tps->client,
72cd79954   David Brownell   [PATCH] I2C: add ...
1032
1033
1034
1035
1036
1037
1038
1039
1040
  			TPS_VDCDC1, vdcdc1);
  
  	if (status != 0)
  		printk(KERN_ERR "%s: Failed to write vdcdc1 register
  ",
  	 DRIVER_NAME);
  	else
  		pr_debug("%s: vdcdc1 0x%02x
  ", DRIVER_NAME,
b5067f8ff   David Brownell   i2c/tps65010: New...
1041
  			i2c_smbus_read_byte_data(the_tps->client, TPS_VDCDC1));
72cd79954   David Brownell   [PATCH] I2C: add ...
1042

5c085d369   Ingo Molnar   [PATCH] i2c: Sema...
1043
  	mutex_unlock(&the_tps->lock);
72cd79954   David Brownell   [PATCH] I2C: add ...
1044
1045
1046
1047
1048
1049
1050
1051
1052
  
  	return status;
  }
  EXPORT_SYMBOL(tps65013_set_low_pwr);
  
  /*-------------------------------------------------------------------------*/
  
  static int __init tps_init(void)
  {
dbc352b9f   Aaro Koskinen   mfd: tps65010: Fi...
1053
  	return i2c_add_driver(&tps65010_driver);
72cd79954   David Brownell   [PATCH] I2C: add ...
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
  }
  /* NOTE:  this MUST be initialized before the other parts of the system
   * that rely on it ... but after the i2c bus on which this relies.
   * That is, much earlier than on PC-type systems, which don't often use
   * I2C as a core system bus.
   */
  subsys_initcall(tps_init);
  
  static void __exit tps_exit(void)
  {
  	i2c_del_driver(&tps65010_driver);
  }
  module_exit(tps_exit);