Blame view

drivers/mfd/max8925-core.c 16.7 KB
d50f8f339   Haojian Zhuang   mfd: Initial max8...
1
2
3
  /*
   * Base driver for Maxim MAX8925
   *
1f1cf8f98   Haojian Zhuang   mfd: Update irq h...
4
   * Copyright (C) 2009-2010 Marvell International Ltd.
d50f8f339   Haojian Zhuang   mfd: Initial max8...
5
6
7
8
9
10
11
12
13
14
   *	Haojian Zhuang <haojian.zhuang@marvell.com>
   *
   * 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/kernel.h>
  #include <linux/module.h>
  #include <linux/i2c.h>
1f1cf8f98   Haojian Zhuang   mfd: Update irq h...
15
  #include <linux/irq.h>
d50f8f339   Haojian Zhuang   mfd: Initial max8...
16
17
18
19
  #include <linux/interrupt.h>
  #include <linux/platform_device.h>
  #include <linux/mfd/core.h>
  #include <linux/mfd/max8925.h>
1ad998934   Haojian Zhuang   mfd: Add subdevs ...
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
  static struct resource backlight_resources[] = {
  	{
  		.name	= "max8925-backlight",
  		.start	= MAX8925_WLED_MODE_CNTL,
  		.end	= MAX8925_WLED_CNTL,
  		.flags	= IORESOURCE_IO,
  	},
  };
  
  static struct mfd_cell backlight_devs[] = {
  	{
  		.name		= "max8925-backlight",
  		.num_resources	= 1,
  		.resources	= &backlight_resources[0],
  		.id		= -1,
  	},
  };
  
  static struct resource touch_resources[] = {
  	{
  		.name	= "max8925-tsc",
  		.start	= MAX8925_TSC_IRQ,
  		.end	= MAX8925_ADC_RES_END,
  		.flags	= IORESOURCE_IO,
  	},
  };
  
  static struct mfd_cell touch_devs[] = {
  	{
  		.name		= "max8925-touch",
  		.num_resources	= 1,
  		.resources	= &touch_resources[0],
  		.id		= -1,
  	},
  };
1f1cf8f98   Haojian Zhuang   mfd: Update irq h...
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
  static struct resource power_supply_resources[] = {
  	{
  		.name	= "max8925-power",
  		.start	= MAX8925_CHG_IRQ1,
  		.end	= MAX8925_CHG_IRQ1_MASK,
  		.flags	= IORESOURCE_IO,
  	},
  };
  
  static struct mfd_cell power_devs[] = {
  	{
  		.name		= "max8925-power",
  		.num_resources	= 1,
  		.resources	= &power_supply_resources[0],
  		.id		= -1,
  	},
  };
  
  static struct resource rtc_resources[] = {
  	{
  		.name	= "max8925-rtc",
  		.start	= MAX8925_RTC_IRQ,
  		.end	= MAX8925_RTC_IRQ_MASK,
  		.flags	= IORESOURCE_IO,
  	},
  };
  
  static struct mfd_cell rtc_devs[] = {
  	{
  		.name		= "max8925-rtc",
  		.num_resources	= 1,
  		.resources	= &rtc_resources[0],
  		.id		= -1,
  	},
  };
d0f7a6d6d   Haojian Zhuang   mfd: Enable onkey...
90
91
92
  static struct resource onkey_resources[] = {
  	{
  		.name	= "max8925-onkey",
2d95ae3bb   Haojian Zhuang   input: Monitor bo...
93
94
95
96
97
98
99
  		.start	= MAX8925_IRQ_GPM_SW_R,
  		.end	= MAX8925_IRQ_GPM_SW_R,
  		.flags	= IORESOURCE_IRQ,
  	}, {
  		.name	= "max8925-onkey",
  		.start	= MAX8925_IRQ_GPM_SW_F,
  		.end	= MAX8925_IRQ_GPM_SW_F,
d0f7a6d6d   Haojian Zhuang   mfd: Enable onkey...
100
101
102
103
104
105
106
  		.flags	= IORESOURCE_IRQ,
  	},
  };
  
  static struct mfd_cell onkey_devs[] = {
  	{
  		.name		= "max8925-onkey",
2d95ae3bb   Haojian Zhuang   input: Monitor bo...
107
  		.num_resources	= 2,
d0f7a6d6d   Haojian Zhuang   mfd: Enable onkey...
108
109
110
111
  		.resources	= &onkey_resources[0],
  		.id		= -1,
  	},
  };
1ad998934   Haojian Zhuang   mfd: Add subdevs ...
112
113
114
115
116
117
118
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
  #define MAX8925_REG_RESOURCE(_start, _end)	\
  {						\
  	.start	= MAX8925_##_start,		\
  	.end	= MAX8925_##_end,		\
  	.flags	= IORESOURCE_IO,		\
  }
  
  static struct resource regulator_resources[] = {
  	MAX8925_REG_RESOURCE(SDCTL1, SDCTL1),
  	MAX8925_REG_RESOURCE(SDCTL2, SDCTL2),
  	MAX8925_REG_RESOURCE(SDCTL3, SDCTL3),
  	MAX8925_REG_RESOURCE(LDOCTL1, LDOCTL1),
  	MAX8925_REG_RESOURCE(LDOCTL2, LDOCTL2),
  	MAX8925_REG_RESOURCE(LDOCTL3, LDOCTL3),
  	MAX8925_REG_RESOURCE(LDOCTL4, LDOCTL4),
  	MAX8925_REG_RESOURCE(LDOCTL5, LDOCTL5),
  	MAX8925_REG_RESOURCE(LDOCTL6, LDOCTL6),
  	MAX8925_REG_RESOURCE(LDOCTL7, LDOCTL7),
  	MAX8925_REG_RESOURCE(LDOCTL8, LDOCTL8),
  	MAX8925_REG_RESOURCE(LDOCTL9, LDOCTL9),
  	MAX8925_REG_RESOURCE(LDOCTL10, LDOCTL10),
  	MAX8925_REG_RESOURCE(LDOCTL11, LDOCTL11),
  	MAX8925_REG_RESOURCE(LDOCTL12, LDOCTL12),
  	MAX8925_REG_RESOURCE(LDOCTL13, LDOCTL13),
  	MAX8925_REG_RESOURCE(LDOCTL14, LDOCTL14),
  	MAX8925_REG_RESOURCE(LDOCTL15, LDOCTL15),
  	MAX8925_REG_RESOURCE(LDOCTL16, LDOCTL16),
  	MAX8925_REG_RESOURCE(LDOCTL17, LDOCTL17),
  	MAX8925_REG_RESOURCE(LDOCTL18, LDOCTL18),
  	MAX8925_REG_RESOURCE(LDOCTL19, LDOCTL19),
  	MAX8925_REG_RESOURCE(LDOCTL20, LDOCTL20),
  };
  
  #define MAX8925_REG_DEVS(_id)						\
  {									\
  	.name		= "max8925-regulator",				\
  	.num_resources	= 1,						\
  	.resources	= &regulator_resources[MAX8925_ID_##_id],	\
  	.id		= MAX8925_ID_##_id,				\
  }
  
  static struct mfd_cell regulator_devs[] = {
  	MAX8925_REG_DEVS(SD1),
  	MAX8925_REG_DEVS(SD2),
  	MAX8925_REG_DEVS(SD3),
  	MAX8925_REG_DEVS(LDO1),
  	MAX8925_REG_DEVS(LDO2),
  	MAX8925_REG_DEVS(LDO3),
  	MAX8925_REG_DEVS(LDO4),
  	MAX8925_REG_DEVS(LDO5),
  	MAX8925_REG_DEVS(LDO6),
  	MAX8925_REG_DEVS(LDO7),
  	MAX8925_REG_DEVS(LDO8),
  	MAX8925_REG_DEVS(LDO9),
  	MAX8925_REG_DEVS(LDO10),
  	MAX8925_REG_DEVS(LDO11),
  	MAX8925_REG_DEVS(LDO12),
  	MAX8925_REG_DEVS(LDO13),
  	MAX8925_REG_DEVS(LDO14),
  	MAX8925_REG_DEVS(LDO15),
  	MAX8925_REG_DEVS(LDO16),
  	MAX8925_REG_DEVS(LDO17),
  	MAX8925_REG_DEVS(LDO18),
  	MAX8925_REG_DEVS(LDO19),
  	MAX8925_REG_DEVS(LDO20),
  };
1f1cf8f98   Haojian Zhuang   mfd: Update irq h...
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
  enum {
  	FLAGS_ADC = 1,	/* register in ADC component */
  	FLAGS_RTC,	/* register in RTC component */
  };
  
  struct max8925_irq_data {
  	int	reg;
  	int	mask_reg;
  	int	enable;		/* enable or not */
  	int	offs;		/* bit offset in mask register */
  	int	flags;
  	int	tsc_irq;
  };
  
  static struct max8925_irq_data max8925_irqs[] = {
  	[MAX8925_IRQ_VCHG_DC_OVP] = {
  		.reg		= MAX8925_CHG_IRQ1,
  		.mask_reg	= MAX8925_CHG_IRQ1_MASK,
  		.offs		= 1 << 0,
  	},
  	[MAX8925_IRQ_VCHG_DC_F] = {
  		.reg		= MAX8925_CHG_IRQ1,
  		.mask_reg	= MAX8925_CHG_IRQ1_MASK,
  		.offs		= 1 << 1,
  	},
  	[MAX8925_IRQ_VCHG_DC_R] = {
  		.reg		= MAX8925_CHG_IRQ1,
  		.mask_reg	= MAX8925_CHG_IRQ1_MASK,
  		.offs		= 1 << 2,
  	},
1f1cf8f98   Haojian Zhuang   mfd: Update irq h...
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
  	[MAX8925_IRQ_VCHG_THM_OK_R] = {
  		.reg		= MAX8925_CHG_IRQ2,
  		.mask_reg	= MAX8925_CHG_IRQ2_MASK,
  		.offs		= 1 << 0,
  	},
  	[MAX8925_IRQ_VCHG_THM_OK_F] = {
  		.reg		= MAX8925_CHG_IRQ2,
  		.mask_reg	= MAX8925_CHG_IRQ2_MASK,
  		.offs		= 1 << 1,
  	},
  	[MAX8925_IRQ_VCHG_SYSLOW_F] = {
  		.reg		= MAX8925_CHG_IRQ2,
  		.mask_reg	= MAX8925_CHG_IRQ2_MASK,
  		.offs		= 1 << 2,
  	},
  	[MAX8925_IRQ_VCHG_SYSLOW_R] = {
  		.reg		= MAX8925_CHG_IRQ2,
  		.mask_reg	= MAX8925_CHG_IRQ2_MASK,
  		.offs		= 1 << 3,
  	},
  	[MAX8925_IRQ_VCHG_RST] = {
  		.reg		= MAX8925_CHG_IRQ2,
  		.mask_reg	= MAX8925_CHG_IRQ2_MASK,
  		.offs		= 1 << 4,
  	},
  	[MAX8925_IRQ_VCHG_DONE] = {
  		.reg		= MAX8925_CHG_IRQ2,
  		.mask_reg	= MAX8925_CHG_IRQ2_MASK,
  		.offs		= 1 << 5,
  	},
  	[MAX8925_IRQ_VCHG_TOPOFF] = {
  		.reg		= MAX8925_CHG_IRQ2,
  		.mask_reg	= MAX8925_CHG_IRQ2_MASK,
  		.offs		= 1 << 6,
  	},
  	[MAX8925_IRQ_VCHG_TMR_FAULT] = {
  		.reg		= MAX8925_CHG_IRQ2,
  		.mask_reg	= MAX8925_CHG_IRQ2_MASK,
  		.offs		= 1 << 7,
  	},
  	[MAX8925_IRQ_GPM_RSTIN] = {
  		.reg		= MAX8925_ON_OFF_IRQ1,
  		.mask_reg	= MAX8925_ON_OFF_IRQ1_MASK,
  		.offs		= 1 << 0,
  	},
  	[MAX8925_IRQ_GPM_MPL] = {
  		.reg		= MAX8925_ON_OFF_IRQ1,
  		.mask_reg	= MAX8925_ON_OFF_IRQ1_MASK,
  		.offs		= 1 << 1,
  	},
  	[MAX8925_IRQ_GPM_SW_3SEC] = {
  		.reg		= MAX8925_ON_OFF_IRQ1,
  		.mask_reg	= MAX8925_ON_OFF_IRQ1_MASK,
  		.offs		= 1 << 2,
  	},
  	[MAX8925_IRQ_GPM_EXTON_F] = {
  		.reg		= MAX8925_ON_OFF_IRQ1,
  		.mask_reg	= MAX8925_ON_OFF_IRQ1_MASK,
  		.offs		= 1 << 3,
  	},
  	[MAX8925_IRQ_GPM_EXTON_R] = {
  		.reg		= MAX8925_ON_OFF_IRQ1,
  		.mask_reg	= MAX8925_ON_OFF_IRQ1_MASK,
  		.offs		= 1 << 4,
  	},
  	[MAX8925_IRQ_GPM_SW_1SEC] = {
  		.reg		= MAX8925_ON_OFF_IRQ1,
  		.mask_reg	= MAX8925_ON_OFF_IRQ1_MASK,
  		.offs		= 1 << 5,
  	},
  	[MAX8925_IRQ_GPM_SW_F] = {
  		.reg		= MAX8925_ON_OFF_IRQ1,
  		.mask_reg	= MAX8925_ON_OFF_IRQ1_MASK,
  		.offs		= 1 << 6,
  	},
  	[MAX8925_IRQ_GPM_SW_R] = {
  		.reg		= MAX8925_ON_OFF_IRQ1,
  		.mask_reg	= MAX8925_ON_OFF_IRQ1_MASK,
  		.offs		= 1 << 7,
  	},
  	[MAX8925_IRQ_GPM_SYSCKEN_F] = {
  		.reg		= MAX8925_ON_OFF_IRQ2,
  		.mask_reg	= MAX8925_ON_OFF_IRQ2_MASK,
  		.offs		= 1 << 0,
  	},
  	[MAX8925_IRQ_GPM_SYSCKEN_R] = {
  		.reg		= MAX8925_ON_OFF_IRQ2,
  		.mask_reg	= MAX8925_ON_OFF_IRQ2_MASK,
  		.offs		= 1 << 1,
  	},
  	[MAX8925_IRQ_RTC_ALARM1] = {
  		.reg		= MAX8925_RTC_IRQ,
  		.mask_reg	= MAX8925_RTC_IRQ_MASK,
  		.offs		= 1 << 2,
  		.flags		= FLAGS_RTC,
  	},
  	[MAX8925_IRQ_RTC_ALARM0] = {
  		.reg		= MAX8925_RTC_IRQ,
  		.mask_reg	= MAX8925_RTC_IRQ_MASK,
  		.offs		= 1 << 3,
  		.flags		= FLAGS_RTC,
  	},
  	[MAX8925_IRQ_TSC_STICK] = {
  		.reg		= MAX8925_TSC_IRQ,
  		.mask_reg	= MAX8925_TSC_IRQ_MASK,
  		.offs		= 1 << 0,
  		.flags		= FLAGS_ADC,
  		.tsc_irq	= 1,
  	},
  	[MAX8925_IRQ_TSC_NSTICK] = {
  		.reg		= MAX8925_TSC_IRQ,
  		.mask_reg	= MAX8925_TSC_IRQ_MASK,
  		.offs		= 1 << 1,
  		.flags		= FLAGS_ADC,
  		.tsc_irq	= 1,
  	},
  };
  
  static inline struct max8925_irq_data *irq_to_max8925(struct max8925_chip *chip,
  						      int irq)
d50f8f339   Haojian Zhuang   mfd: Initial max8...
328
  {
1f1cf8f98   Haojian Zhuang   mfd: Update irq h...
329
330
  	return &max8925_irqs[irq - chip->irq_base];
  }
d50f8f339   Haojian Zhuang   mfd: Initial max8...
331

1f1cf8f98   Haojian Zhuang   mfd: Update irq h...
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
  static irqreturn_t max8925_irq(int irq, void *data)
  {
  	struct max8925_chip *chip = data;
  	struct max8925_irq_data *irq_data;
  	struct i2c_client *i2c;
  	int read_reg = -1, value = 0;
  	int i;
  
  	for (i = 0; i < ARRAY_SIZE(max8925_irqs); i++) {
  		irq_data = &max8925_irqs[i];
  		/* TSC IRQ should be serviced in max8925_tsc_irq() */
  		if (irq_data->tsc_irq)
  			continue;
  		if (irq_data->flags == FLAGS_RTC)
  			i2c = chip->rtc;
  		else if (irq_data->flags == FLAGS_ADC)
  			i2c = chip->adc;
  		else
  			i2c = chip->i2c;
  		if (read_reg != irq_data->reg) {
  			read_reg = irq_data->reg;
  			value = max8925_reg_read(i2c, irq_data->reg);
d50f8f339   Haojian Zhuang   mfd: Initial max8...
354
  		}
1f1cf8f98   Haojian Zhuang   mfd: Update irq h...
355
356
  		if (value & irq_data->enable)
  			handle_nested_irq(chip->irq_base + i);
d50f8f339   Haojian Zhuang   mfd: Initial max8...
357
  	}
1f1cf8f98   Haojian Zhuang   mfd: Update irq h...
358
  	return IRQ_HANDLED;
d50f8f339   Haojian Zhuang   mfd: Initial max8...
359
  }
1f1cf8f98   Haojian Zhuang   mfd: Update irq h...
360
  static irqreturn_t max8925_tsc_irq(int irq, void *data)
d50f8f339   Haojian Zhuang   mfd: Initial max8...
361
  {
1f1cf8f98   Haojian Zhuang   mfd: Update irq h...
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
  	struct max8925_chip *chip = data;
  	struct max8925_irq_data *irq_data;
  	struct i2c_client *i2c;
  	int read_reg = -1, value = 0;
  	int i;
  
  	for (i = 0; i < ARRAY_SIZE(max8925_irqs); i++) {
  		irq_data = &max8925_irqs[i];
  		/* non TSC IRQ should be serviced in max8925_irq() */
  		if (!irq_data->tsc_irq)
  			continue;
  		if (irq_data->flags == FLAGS_RTC)
  			i2c = chip->rtc;
  		else if (irq_data->flags == FLAGS_ADC)
  			i2c = chip->adc;
  		else
  			i2c = chip->i2c;
  		if (read_reg != irq_data->reg) {
  			read_reg = irq_data->reg;
  			value = max8925_reg_read(i2c, irq_data->reg);
  		}
  		if (value & irq_data->enable)
  			handle_nested_irq(chip->irq_base + i);
  	}
  	return IRQ_HANDLED;
d50f8f339   Haojian Zhuang   mfd: Initial max8...
387
  }
98d9bc13c   Mark Brown   mfd: Convert max8...
388
  static void max8925_irq_lock(struct irq_data *data)
d50f8f339   Haojian Zhuang   mfd: Initial max8...
389
  {
98d9bc13c   Mark Brown   mfd: Convert max8...
390
  	struct max8925_chip *chip = irq_data_get_irq_chip_data(data);
d50f8f339   Haojian Zhuang   mfd: Initial max8...
391

1f1cf8f98   Haojian Zhuang   mfd: Update irq h...
392
  	mutex_lock(&chip->irq_lock);
d50f8f339   Haojian Zhuang   mfd: Initial max8...
393
  }
98d9bc13c   Mark Brown   mfd: Convert max8...
394
  static void max8925_irq_sync_unlock(struct irq_data *data)
d50f8f339   Haojian Zhuang   mfd: Initial max8...
395
  {
98d9bc13c   Mark Brown   mfd: Convert max8...
396
  	struct max8925_chip *chip = irq_data_get_irq_chip_data(data);
1f1cf8f98   Haojian Zhuang   mfd: Update irq h...
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
  	struct max8925_irq_data *irq_data;
  	static unsigned char cache_chg[2] = {0xff, 0xff};
  	static unsigned char cache_on[2] = {0xff, 0xff};
  	static unsigned char cache_rtc = 0xff, cache_tsc = 0xff;
  	unsigned char irq_chg[2], irq_on[2];
  	unsigned char irq_rtc, irq_tsc;
  	int i;
  
  	/* Load cached value. In initial, all IRQs are masked */
  	irq_chg[0] = cache_chg[0];
  	irq_chg[1] = cache_chg[1];
  	irq_on[0] = cache_on[0];
  	irq_on[1] = cache_on[1];
  	irq_rtc = cache_rtc;
  	irq_tsc = cache_tsc;
  	for (i = 0; i < ARRAY_SIZE(max8925_irqs); i++) {
  		irq_data = &max8925_irqs[i];
90182317a   Kevin Liu   mfd: Fix max8925 ...
414
  		/* 1 -- disable, 0 -- enable */
1f1cf8f98   Haojian Zhuang   mfd: Update irq h...
415
416
  		switch (irq_data->mask_reg) {
  		case MAX8925_CHG_IRQ1_MASK:
90182317a   Kevin Liu   mfd: Fix max8925 ...
417
  			irq_chg[0] &= ~irq_data->enable;
1f1cf8f98   Haojian Zhuang   mfd: Update irq h...
418
419
  			break;
  		case MAX8925_CHG_IRQ2_MASK:
90182317a   Kevin Liu   mfd: Fix max8925 ...
420
  			irq_chg[1] &= ~irq_data->enable;
1f1cf8f98   Haojian Zhuang   mfd: Update irq h...
421
422
  			break;
  		case MAX8925_ON_OFF_IRQ1_MASK:
90182317a   Kevin Liu   mfd: Fix max8925 ...
423
  			irq_on[0] &= ~irq_data->enable;
1f1cf8f98   Haojian Zhuang   mfd: Update irq h...
424
425
  			break;
  		case MAX8925_ON_OFF_IRQ2_MASK:
90182317a   Kevin Liu   mfd: Fix max8925 ...
426
  			irq_on[1] &= ~irq_data->enable;
1f1cf8f98   Haojian Zhuang   mfd: Update irq h...
427
428
  			break;
  		case MAX8925_RTC_IRQ_MASK:
90182317a   Kevin Liu   mfd: Fix max8925 ...
429
  			irq_rtc &= ~irq_data->enable;
1f1cf8f98   Haojian Zhuang   mfd: Update irq h...
430
431
  			break;
  		case MAX8925_TSC_IRQ_MASK:
90182317a   Kevin Liu   mfd: Fix max8925 ...
432
  			irq_tsc &= ~irq_data->enable;
1f1cf8f98   Haojian Zhuang   mfd: Update irq h...
433
434
435
436
437
438
439
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
467
468
  			break;
  		default:
  			dev_err(chip->dev, "wrong IRQ
  ");
  			break;
  		}
  	}
  	/* update mask into registers */
  	if (cache_chg[0] != irq_chg[0]) {
  		cache_chg[0] = irq_chg[0];
  		max8925_reg_write(chip->i2c, MAX8925_CHG_IRQ1_MASK,
  			irq_chg[0]);
  	}
  	if (cache_chg[1] != irq_chg[1]) {
  		cache_chg[1] = irq_chg[1];
  		max8925_reg_write(chip->i2c, MAX8925_CHG_IRQ2_MASK,
  			irq_chg[1]);
  	}
  	if (cache_on[0] != irq_on[0]) {
  		cache_on[0] = irq_on[0];
  		max8925_reg_write(chip->i2c, MAX8925_ON_OFF_IRQ1_MASK,
  				irq_on[0]);
  	}
  	if (cache_on[1] != irq_on[1]) {
  		cache_on[1] = irq_on[1];
  		max8925_reg_write(chip->i2c, MAX8925_ON_OFF_IRQ2_MASK,
  				irq_on[1]);
  	}
  	if (cache_rtc != irq_rtc) {
  		cache_rtc = irq_rtc;
  		max8925_reg_write(chip->rtc, MAX8925_RTC_IRQ_MASK, irq_rtc);
  	}
  	if (cache_tsc != irq_tsc) {
  		cache_tsc = irq_tsc;
  		max8925_reg_write(chip->adc, MAX8925_TSC_IRQ_MASK, irq_tsc);
  	}
d50f8f339   Haojian Zhuang   mfd: Initial max8...
469

1f1cf8f98   Haojian Zhuang   mfd: Update irq h...
470
  	mutex_unlock(&chip->irq_lock);
d50f8f339   Haojian Zhuang   mfd: Initial max8...
471
  }
98d9bc13c   Mark Brown   mfd: Convert max8...
472
  static void max8925_irq_enable(struct irq_data *data)
d50f8f339   Haojian Zhuang   mfd: Initial max8...
473
  {
98d9bc13c   Mark Brown   mfd: Convert max8...
474
475
476
  	struct max8925_chip *chip = irq_data_get_irq_chip_data(data);
  	max8925_irqs[data->irq - chip->irq_base].enable
  		= max8925_irqs[data->irq - chip->irq_base].offs;
d50f8f339   Haojian Zhuang   mfd: Initial max8...
477
  }
98d9bc13c   Mark Brown   mfd: Convert max8...
478
  static void max8925_irq_disable(struct irq_data *data)
d50f8f339   Haojian Zhuang   mfd: Initial max8...
479
  {
98d9bc13c   Mark Brown   mfd: Convert max8...
480
481
  	struct max8925_chip *chip = irq_data_get_irq_chip_data(data);
  	max8925_irqs[data->irq - chip->irq_base].enable = 0;
d50f8f339   Haojian Zhuang   mfd: Initial max8...
482
  }
d50f8f339   Haojian Zhuang   mfd: Initial max8...
483

1f1cf8f98   Haojian Zhuang   mfd: Update irq h...
484
485
  static struct irq_chip max8925_irq_chip = {
  	.name		= "max8925",
98d9bc13c   Mark Brown   mfd: Convert max8...
486
487
488
489
  	.irq_bus_lock	= max8925_irq_lock,
  	.irq_bus_sync_unlock = max8925_irq_sync_unlock,
  	.irq_enable	= max8925_irq_enable,
  	.irq_disable	= max8925_irq_disable,
1f1cf8f98   Haojian Zhuang   mfd: Update irq h...
490
491
492
493
  };
  
  static int max8925_irq_init(struct max8925_chip *chip, int irq,
  			    struct max8925_platform_data *pdata)
d50f8f339   Haojian Zhuang   mfd: Initial max8...
494
  {
1f1cf8f98   Haojian Zhuang   mfd: Update irq h...
495
  	unsigned long flags = IRQF_TRIGGER_FALLING | IRQF_ONESHOT;
1f1cf8f98   Haojian Zhuang   mfd: Update irq h...
496
497
498
499
500
501
  	int i, ret;
  	int __irq;
  
  	if (!pdata || !pdata->irq_base) {
  		dev_warn(chip->dev, "No interrupt support on IRQ base
  ");
d50f8f339   Haojian Zhuang   mfd: Initial max8...
502
  		return -EINVAL;
1f1cf8f98   Haojian Zhuang   mfd: Update irq h...
503
504
505
506
507
508
509
510
  	}
  	/* clear all interrupts */
  	max8925_reg_read(chip->i2c, MAX8925_CHG_IRQ1);
  	max8925_reg_read(chip->i2c, MAX8925_CHG_IRQ2);
  	max8925_reg_read(chip->i2c, MAX8925_ON_OFF_IRQ1);
  	max8925_reg_read(chip->i2c, MAX8925_ON_OFF_IRQ2);
  	max8925_reg_read(chip->rtc, MAX8925_RTC_IRQ);
  	max8925_reg_read(chip->adc, MAX8925_TSC_IRQ);
68e488d96   Haojian Zhuang   mfd: Delay to mas...
511
  	/* mask all interrupts except for TSC */
1f1cf8f98   Haojian Zhuang   mfd: Update irq h...
512
513
514
515
516
517
518
  	max8925_reg_write(chip->rtc, MAX8925_ALARM0_CNTL, 0);
  	max8925_reg_write(chip->rtc, MAX8925_ALARM1_CNTL, 0);
  	max8925_reg_write(chip->i2c, MAX8925_CHG_IRQ1_MASK, 0xff);
  	max8925_reg_write(chip->i2c, MAX8925_CHG_IRQ2_MASK, 0xff);
  	max8925_reg_write(chip->i2c, MAX8925_ON_OFF_IRQ1_MASK, 0xff);
  	max8925_reg_write(chip->i2c, MAX8925_ON_OFF_IRQ2_MASK, 0xff);
  	max8925_reg_write(chip->rtc, MAX8925_RTC_IRQ_MASK, 0xff);
1f1cf8f98   Haojian Zhuang   mfd: Update irq h...
519
520
521
522
  
  	mutex_init(&chip->irq_lock);
  	chip->core_irq = irq;
  	chip->irq_base = pdata->irq_base;
1f1cf8f98   Haojian Zhuang   mfd: Update irq h...
523
524
525
526
  
  	/* register with genirq */
  	for (i = 0; i < ARRAY_SIZE(max8925_irqs); i++) {
  		__irq = i + chip->irq_base;
d5bb12216   Thomas Gleixner   mfd: Cleanup irq ...
527
528
  		irq_set_chip_data(__irq, chip);
  		irq_set_chip_and_handler(__irq, &max8925_irq_chip,
1f1cf8f98   Haojian Zhuang   mfd: Update irq h...
529
  					 handle_edge_irq);
d5bb12216   Thomas Gleixner   mfd: Cleanup irq ...
530
  		irq_set_nested_thread(__irq, 1);
1f1cf8f98   Haojian Zhuang   mfd: Update irq h...
531
532
533
  #ifdef CONFIG_ARM
  		set_irq_flags(__irq, IRQF_VALID);
  #else
d5bb12216   Thomas Gleixner   mfd: Cleanup irq ...
534
  		irq_set_noprobe(__irq);
1f1cf8f98   Haojian Zhuang   mfd: Update irq h...
535
536
537
538
539
540
541
  #endif
  	}
  	if (!irq) {
  		dev_warn(chip->dev, "No interrupt support on core IRQ
  ");
  		goto tsc_irq;
  	}
d50f8f339   Haojian Zhuang   mfd: Initial max8...
542

1f1cf8f98   Haojian Zhuang   mfd: Update irq h...
543
544
545
546
547
548
549
  	ret = request_threaded_irq(irq, NULL, max8925_irq, flags,
  				   "max8925", chip);
  	if (ret) {
  		dev_err(chip->dev, "Failed to request core IRQ: %d
  ", ret);
  		chip->core_irq = 0;
  	}
68e488d96   Haojian Zhuang   mfd: Delay to mas...
550

1f1cf8f98   Haojian Zhuang   mfd: Update irq h...
551
  tsc_irq:
68e488d96   Haojian Zhuang   mfd: Delay to mas...
552
553
  	/* mask TSC interrupt */
  	max8925_reg_write(chip->adc, MAX8925_TSC_IRQ_MASK, 0x0f);
1f1cf8f98   Haojian Zhuang   mfd: Update irq h...
554
555
556
557
558
559
560
561
562
563
564
565
566
567
  	if (!pdata->tsc_irq) {
  		dev_warn(chip->dev, "No interrupt support on TSC IRQ
  ");
  		return 0;
  	}
  	chip->tsc_irq = pdata->tsc_irq;
  
  	ret = request_threaded_irq(chip->tsc_irq, NULL, max8925_tsc_irq,
  				   flags, "max8925-tsc", chip);
  	if (ret) {
  		dev_err(chip->dev, "Failed to request TSC IRQ: %d
  ", ret);
  		chip->tsc_irq = 0;
  	}
d50f8f339   Haojian Zhuang   mfd: Initial max8...
568
569
  	return 0;
  }
d50f8f339   Haojian Zhuang   mfd: Initial max8...
570

1f1cf8f98   Haojian Zhuang   mfd: Update irq h...
571
572
  int __devinit max8925_device_init(struct max8925_chip *chip,
  				  struct max8925_platform_data *pdata)
d50f8f339   Haojian Zhuang   mfd: Initial max8...
573
574
  {
  	int ret;
1f1cf8f98   Haojian Zhuang   mfd: Update irq h...
575
576
577
578
579
580
581
582
583
584
585
586
587
  	max8925_irq_init(chip, chip->i2c->irq, pdata);
  
  	if (pdata && (pdata->power || pdata->touch)) {
  		/* enable ADC to control internal reference */
  		max8925_set_bits(chip->i2c, MAX8925_RESET_CNFG, 1, 1);
  		/* enable internal reference for ADC */
  		max8925_set_bits(chip->adc, MAX8925_TSC_CNFG1, 3, 2);
  		/* check for internal reference IRQ */
  		do {
  			ret = max8925_reg_read(chip->adc, MAX8925_TSC_IRQ);
  		} while (ret & MAX8925_NREF_OK);
  		/* enaable ADC scheduler, interval is 1 second */
  		max8925_set_bits(chip->adc, MAX8925_ADC_SCHED, 3, 2);
d50f8f339   Haojian Zhuang   mfd: Initial max8...
588
  	}
d50f8f339   Haojian Zhuang   mfd: Initial max8...
589

1f1cf8f98   Haojian Zhuang   mfd: Update irq h...
590
591
  	/* enable Momentary Power Loss */
  	max8925_set_bits(chip->rtc, MAX8925_MPL_CNTL, 1 << 4, 1 << 4);
1ad998934   Haojian Zhuang   mfd: Add subdevs ...
592

1f1cf8f98   Haojian Zhuang   mfd: Update irq h...
593
594
595
  	ret = mfd_add_devices(chip->dev, 0, &rtc_devs[0],
  			      ARRAY_SIZE(rtc_devs),
  			      &rtc_resources[0], 0);
1ad998934   Haojian Zhuang   mfd: Add subdevs ...
596
  	if (ret < 0) {
1f1cf8f98   Haojian Zhuang   mfd: Update irq h...
597
598
599
600
  		dev_err(chip->dev, "Failed to add rtc subdev
  ");
  		goto out;
  	}
d0f7a6d6d   Haojian Zhuang   mfd: Enable onkey...
601
602
603
604
605
606
607
608
609
  
  	ret = mfd_add_devices(chip->dev, 0, &onkey_devs[0],
  			      ARRAY_SIZE(onkey_devs),
  			      &onkey_resources[0], 0);
  	if (ret < 0) {
  		dev_err(chip->dev, "Failed to add onkey subdev
  ");
  		goto out_dev;
  	}
ebf9988ec   Haojian Zhuang   mfd: Remove check...
610
  	if (pdata) {
1f1cf8f98   Haojian Zhuang   mfd: Update irq h...
611
612
613
614
615
616
617
618
  		ret = mfd_add_devices(chip->dev, 0, &regulator_devs[0],
  				      ARRAY_SIZE(regulator_devs),
  				      &regulator_resources[0], 0);
  		if (ret < 0) {
  			dev_err(chip->dev, "Failed to add regulator subdev
  ");
  			goto out_dev;
  		}
1ad998934   Haojian Zhuang   mfd: Add subdevs ...
619
620
621
622
623
624
625
626
627
628
629
630
  	}
  
  	if (pdata && pdata->backlight) {
  		ret = mfd_add_devices(chip->dev, 0, &backlight_devs[0],
  				      ARRAY_SIZE(backlight_devs),
  				      &backlight_resources[0], 0);
  		if (ret < 0) {
  			dev_err(chip->dev, "Failed to add backlight subdev
  ");
  			goto out_dev;
  		}
  	}
d50f8f339   Haojian Zhuang   mfd: Initial max8...
631

1f1cf8f98   Haojian Zhuang   mfd: Update irq h...
632
633
634
635
636
637
638
639
640
641
  	if (pdata && pdata->power) {
  		ret = mfd_add_devices(chip->dev, 0, &power_devs[0],
  					ARRAY_SIZE(power_devs),
  					&power_supply_resources[0], 0);
  		if (ret < 0) {
  			dev_err(chip->dev, "Failed to add power supply "
  				"subdev
  ");
  			goto out_dev;
  		}
d50f8f339   Haojian Zhuang   mfd: Initial max8...
642
  	}
1ad998934   Haojian Zhuang   mfd: Add subdevs ...
643
644
645
646
647
648
649
650
  
  	if (pdata && pdata->touch) {
  		ret = mfd_add_devices(chip->dev, 0, &touch_devs[0],
  				      ARRAY_SIZE(touch_devs),
  				      &touch_resources[0], 0);
  		if (ret < 0) {
  			dev_err(chip->dev, "Failed to add touch subdev
  ");
1f1cf8f98   Haojian Zhuang   mfd: Update irq h...
651
  			goto out_dev;
1ad998934   Haojian Zhuang   mfd: Add subdevs ...
652
653
  		}
  	}
1f1cf8f98   Haojian Zhuang   mfd: Update irq h...
654

1ad998934   Haojian Zhuang   mfd: Add subdevs ...
655
  	return 0;
1f1cf8f98   Haojian Zhuang   mfd: Update irq h...
656
657
  out_dev:
  	mfd_remove_devices(chip->dev);
d50f8f339   Haojian Zhuang   mfd: Initial max8...
658
659
660
  out:
  	return ret;
  }
1f1cf8f98   Haojian Zhuang   mfd: Update irq h...
661
  void __devexit max8925_device_exit(struct max8925_chip *chip)
d50f8f339   Haojian Zhuang   mfd: Initial max8...
662
  {
1f1cf8f98   Haojian Zhuang   mfd: Update irq h...
663
664
665
666
  	if (chip->core_irq)
  		free_irq(chip->core_irq, chip);
  	if (chip->tsc_irq)
  		free_irq(chip->tsc_irq, chip);
1ad998934   Haojian Zhuang   mfd: Add subdevs ...
667
  	mfd_remove_devices(chip->dev);
d50f8f339   Haojian Zhuang   mfd: Initial max8...
668
  }
1f1cf8f98   Haojian Zhuang   mfd: Update irq h...
669

d50f8f339   Haojian Zhuang   mfd: Initial max8...
670
671
672
  MODULE_DESCRIPTION("PMIC Driver for Maxim MAX8925");
  MODULE_AUTHOR("Haojian Zhuang <haojian.zhuang@marvell.com");
  MODULE_LICENSE("GPL");