blob: a88967a466cb3a4e4f7c9dacba3cd2e2a2e54310 [file] [log] [blame]
Haojian Zhuangbbd51b12010-01-06 17:04:18 -05001/*
2 * Base driver for Marvell 88PM8607
3 *
4 * Copyright (C) 2009 Marvell International Ltd.
5 * Haojian Zhuang <haojian.zhuang@marvell.com>
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License version 2 as
9 * published by the Free Software Foundation.
10 */
11
12#include <linux/kernel.h>
13#include <linux/module.h>
Haojian Zhuang5c42e8c2009-12-15 16:01:47 -050014#include <linux/i2c.h>
Haojian Zhuang2afa62e2010-02-08 05:02:00 -050015#include <linux/irq.h>
Haojian Zhuangbbd51b12010-01-06 17:04:18 -050016#include <linux/interrupt.h>
17#include <linux/platform_device.h>
18#include <linux/mfd/core.h>
Haojian Zhuang53dbab72010-01-08 06:01:24 -050019#include <linux/mfd/88pm860x.h>
Haojian Zhuangbbd51b12010-01-06 17:04:18 -050020
Haojian Zhuang2afa62e2010-02-08 05:02:00 -050021#define INT_STATUS_NUM 3
22
Haojian Zhuangadb70482011-03-07 23:43:09 +080023static struct resource bk_resources[] __initdata = {
24 {PM8606_BACKLIGHT1, PM8606_BACKLIGHT1, "backlight-0", IORESOURCE_IO,},
25 {PM8606_BACKLIGHT2, PM8606_BACKLIGHT2, "backlight-1", IORESOURCE_IO,},
26 {PM8606_BACKLIGHT3, PM8606_BACKLIGHT3, "backlight-2", IORESOURCE_IO,},
Haojian Zhuanga16122b2009-12-15 16:04:36 -050027};
Haojian Zhuangadb70482011-03-07 23:43:09 +080028
29static struct mfd_cell bk_devs[] __initdata = {
30 {"88pm860x-backlight", 0,},
31 {"88pm860x-backlight", 1,},
32 {"88pm860x-backlight", 2,},
33};
34
35static struct pm860x_backlight_pdata bk_pdata[ARRAY_SIZE(bk_devs)];
Haojian Zhuanga16122b2009-12-15 16:04:36 -050036
37char pm860x_led_name[][MFD_NAME_SIZE] = {
38 "led0-red",
39 "led0-green",
40 "led0-blue",
41 "led1-red",
42 "led1-green",
43 "led1-blue",
44};
45EXPORT_SYMBOL(pm860x_led_name);
46
Haojian Zhuanga16122b2009-12-15 16:04:36 -050047#define PM8606_LED_RESOURCE(_i, _x) \
48{ \
49 .name = pm860x_led_name[_i], \
50 .start = PM8606_##_x, \
51 .end = PM8606_##_x, \
52 .flags = IORESOURCE_IO, \
53}
54
55static struct resource led_resources[] = {
Haojian Zhuang21f1fc32010-05-19 13:06:59 +080056 PM8606_LED_RESOURCE(PM8606_LED1_RED, RGB1B),
57 PM8606_LED_RESOURCE(PM8606_LED1_GREEN, RGB1C),
58 PM8606_LED_RESOURCE(PM8606_LED1_BLUE, RGB1D),
59 PM8606_LED_RESOURCE(PM8606_LED2_RED, RGB2B),
60 PM8606_LED_RESOURCE(PM8606_LED2_GREEN, RGB2C),
61 PM8606_LED_RESOURCE(PM8606_LED2_BLUE, RGB2D),
Haojian Zhuanga16122b2009-12-15 16:04:36 -050062};
63
64#define PM8606_LED_DEVS(_i) \
65{ \
66 .name = "88pm860x-led", \
67 .num_resources = 1, \
68 .resources = &led_resources[_i], \
69 .id = _i, \
70}
71
72static struct mfd_cell led_devs[] = {
73 PM8606_LED_DEVS(PM8606_LED1_RED),
74 PM8606_LED_DEVS(PM8606_LED1_GREEN),
75 PM8606_LED_DEVS(PM8606_LED1_BLUE),
76 PM8606_LED_DEVS(PM8606_LED2_RED),
77 PM8606_LED_DEVS(PM8606_LED2_GREEN),
78 PM8606_LED_DEVS(PM8606_LED2_BLUE),
79};
80
81static struct resource touch_resources[] = {
82 {
83 .start = PM8607_IRQ_PEN,
84 .end = PM8607_IRQ_PEN,
85 .flags = IORESOURCE_IRQ,
86 },
87};
88
89static struct mfd_cell touch_devs[] = {
90 {
91 .name = "88pm860x-touch",
92 .num_resources = 1,
93 .resources = &touch_resources[0],
94 },
95};
Haojian Zhuangbbd51b12010-01-06 17:04:18 -050096
97#define PM8607_REG_RESOURCE(_start, _end) \
98{ \
99 .start = PM8607_##_start, \
100 .end = PM8607_##_end, \
101 .flags = IORESOURCE_IO, \
102}
103
Haojian Zhuang2afa62e2010-02-08 05:02:00 -0500104static struct resource power_supply_resources[] = {
105 {
106 .name = "88pm860x-power",
107 .start = PM8607_IRQ_CHG,
108 .end = PM8607_IRQ_CHG,
109 .flags = IORESOURCE_IRQ,
110 },
111};
112
113static struct mfd_cell power_devs[] = {
114 {
115 .name = "88pm860x-power",
116 .num_resources = 1,
117 .resources = &power_supply_resources[0],
118 .id = -1,
119 },
120};
121
122static struct resource onkey_resources[] = {
123 {
124 .name = "88pm860x-onkey",
125 .start = PM8607_IRQ_ONKEY,
126 .end = PM8607_IRQ_ONKEY,
127 .flags = IORESOURCE_IRQ,
128 },
129};
130
131static struct mfd_cell onkey_devs[] = {
132 {
133 .name = "88pm860x-onkey",
134 .num_resources = 1,
135 .resources = &onkey_resources[0],
136 .id = -1,
137 },
138};
139
Haojian Zhuang2c36af72010-08-12 11:59:33 +0800140static struct resource codec_resources[] = {
141 {
142 /* Headset microphone insertion or removal */
143 .name = "micin",
144 .start = PM8607_IRQ_MICIN,
145 .end = PM8607_IRQ_MICIN,
146 .flags = IORESOURCE_IRQ,
147 }, {
148 /* Hook-switch press or release */
149 .name = "hook",
150 .start = PM8607_IRQ_HOOK,
151 .end = PM8607_IRQ_HOOK,
152 .flags = IORESOURCE_IRQ,
153 }, {
154 /* Headset insertion or removal */
155 .name = "headset",
156 .start = PM8607_IRQ_HEADSET,
157 .end = PM8607_IRQ_HEADSET,
158 .flags = IORESOURCE_IRQ,
159 }, {
160 /* Audio short */
161 .name = "audio-short",
162 .start = PM8607_IRQ_AUDIO_SHORT,
163 .end = PM8607_IRQ_AUDIO_SHORT,
164 .flags = IORESOURCE_IRQ,
165 },
166};
167
168static struct mfd_cell codec_devs[] = {
169 {
170 .name = "88pm860x-codec",
171 .num_resources = ARRAY_SIZE(codec_resources),
172 .resources = &codec_resources[0],
173 .id = -1,
174 },
175};
176
Haojian Zhuanga16122b2009-12-15 16:04:36 -0500177static struct resource regulator_resources[] = {
Haojian Zhuangbbd51b12010-01-06 17:04:18 -0500178 PM8607_REG_RESOURCE(BUCK1, BUCK1),
179 PM8607_REG_RESOURCE(BUCK2, BUCK2),
180 PM8607_REG_RESOURCE(BUCK3, BUCK3),
181 PM8607_REG_RESOURCE(LDO1, LDO1),
182 PM8607_REG_RESOURCE(LDO2, LDO2),
183 PM8607_REG_RESOURCE(LDO3, LDO3),
184 PM8607_REG_RESOURCE(LDO4, LDO4),
185 PM8607_REG_RESOURCE(LDO5, LDO5),
186 PM8607_REG_RESOURCE(LDO6, LDO6),
187 PM8607_REG_RESOURCE(LDO7, LDO7),
188 PM8607_REG_RESOURCE(LDO8, LDO8),
189 PM8607_REG_RESOURCE(LDO9, LDO9),
190 PM8607_REG_RESOURCE(LDO10, LDO10),
191 PM8607_REG_RESOURCE(LDO12, LDO12),
Haojian Zhuang9f79e9d2010-05-04 09:54:51 -0400192 PM8607_REG_RESOURCE(VIBRATOR_SET, VIBRATOR_SET),
Haojian Zhuangbbd51b12010-01-06 17:04:18 -0500193 PM8607_REG_RESOURCE(LDO14, LDO14),
194};
195
Haojian Zhuang192bbb92010-04-29 13:33:50 -0400196#define PM8607_REG_DEVS(_id) \
Haojian Zhuangbbd51b12010-01-06 17:04:18 -0500197{ \
Haojian Zhuang192bbb92010-04-29 13:33:50 -0400198 .name = "88pm860x-regulator", \
Haojian Zhuangbbd51b12010-01-06 17:04:18 -0500199 .num_resources = 1, \
Haojian Zhuanga16122b2009-12-15 16:04:36 -0500200 .resources = &regulator_resources[PM8607_ID_##_id], \
201 .id = PM8607_ID_##_id, \
Haojian Zhuangbbd51b12010-01-06 17:04:18 -0500202}
203
Haojian Zhuanga16122b2009-12-15 16:04:36 -0500204static struct mfd_cell regulator_devs[] = {
Haojian Zhuang192bbb92010-04-29 13:33:50 -0400205 PM8607_REG_DEVS(BUCK1),
206 PM8607_REG_DEVS(BUCK2),
207 PM8607_REG_DEVS(BUCK3),
208 PM8607_REG_DEVS(LDO1),
209 PM8607_REG_DEVS(LDO2),
210 PM8607_REG_DEVS(LDO3),
211 PM8607_REG_DEVS(LDO4),
212 PM8607_REG_DEVS(LDO5),
213 PM8607_REG_DEVS(LDO6),
214 PM8607_REG_DEVS(LDO7),
215 PM8607_REG_DEVS(LDO8),
216 PM8607_REG_DEVS(LDO9),
217 PM8607_REG_DEVS(LDO10),
218 PM8607_REG_DEVS(LDO12),
Haojian Zhuang9f79e9d2010-05-04 09:54:51 -0400219 PM8607_REG_DEVS(LDO13),
Haojian Zhuang192bbb92010-04-29 13:33:50 -0400220 PM8607_REG_DEVS(LDO14),
Haojian Zhuangbbd51b12010-01-06 17:04:18 -0500221};
222
Haojian Zhuang2afa62e2010-02-08 05:02:00 -0500223struct pm860x_irq_data {
224 int reg;
225 int mask_reg;
226 int enable; /* enable or not */
227 int offs; /* bit offset in mask register */
228};
Haojian Zhuang5c42e8c2009-12-15 16:01:47 -0500229
Haojian Zhuang2afa62e2010-02-08 05:02:00 -0500230static struct pm860x_irq_data pm860x_irqs[] = {
231 [PM8607_IRQ_ONKEY] = {
232 .reg = PM8607_INT_STATUS1,
233 .mask_reg = PM8607_INT_MASK_1,
234 .offs = 1 << 0,
235 },
236 [PM8607_IRQ_EXTON] = {
237 .reg = PM8607_INT_STATUS1,
238 .mask_reg = PM8607_INT_MASK_1,
239 .offs = 1 << 1,
240 },
241 [PM8607_IRQ_CHG] = {
242 .reg = PM8607_INT_STATUS1,
243 .mask_reg = PM8607_INT_MASK_1,
244 .offs = 1 << 2,
245 },
246 [PM8607_IRQ_BAT] = {
247 .reg = PM8607_INT_STATUS1,
248 .mask_reg = PM8607_INT_MASK_1,
249 .offs = 1 << 3,
250 },
251 [PM8607_IRQ_RTC] = {
252 .reg = PM8607_INT_STATUS1,
253 .mask_reg = PM8607_INT_MASK_1,
254 .offs = 1 << 4,
255 },
256 [PM8607_IRQ_CC] = {
257 .reg = PM8607_INT_STATUS1,
258 .mask_reg = PM8607_INT_MASK_1,
259 .offs = 1 << 5,
260 },
261 [PM8607_IRQ_VBAT] = {
262 .reg = PM8607_INT_STATUS2,
263 .mask_reg = PM8607_INT_MASK_2,
264 .offs = 1 << 0,
265 },
266 [PM8607_IRQ_VCHG] = {
267 .reg = PM8607_INT_STATUS2,
268 .mask_reg = PM8607_INT_MASK_2,
269 .offs = 1 << 1,
270 },
271 [PM8607_IRQ_VSYS] = {
272 .reg = PM8607_INT_STATUS2,
273 .mask_reg = PM8607_INT_MASK_2,
274 .offs = 1 << 2,
275 },
276 [PM8607_IRQ_TINT] = {
277 .reg = PM8607_INT_STATUS2,
278 .mask_reg = PM8607_INT_MASK_2,
279 .offs = 1 << 3,
280 },
281 [PM8607_IRQ_GPADC0] = {
282 .reg = PM8607_INT_STATUS2,
283 .mask_reg = PM8607_INT_MASK_2,
284 .offs = 1 << 4,
285 },
286 [PM8607_IRQ_GPADC1] = {
287 .reg = PM8607_INT_STATUS2,
288 .mask_reg = PM8607_INT_MASK_2,
289 .offs = 1 << 5,
290 },
291 [PM8607_IRQ_GPADC2] = {
292 .reg = PM8607_INT_STATUS2,
293 .mask_reg = PM8607_INT_MASK_2,
294 .offs = 1 << 6,
295 },
296 [PM8607_IRQ_GPADC3] = {
297 .reg = PM8607_INT_STATUS2,
298 .mask_reg = PM8607_INT_MASK_2,
299 .offs = 1 << 7,
300 },
301 [PM8607_IRQ_AUDIO_SHORT] = {
302 .reg = PM8607_INT_STATUS3,
303 .mask_reg = PM8607_INT_MASK_3,
304 .offs = 1 << 0,
305 },
306 [PM8607_IRQ_PEN] = {
307 .reg = PM8607_INT_STATUS3,
308 .mask_reg = PM8607_INT_MASK_3,
309 .offs = 1 << 1,
310 },
311 [PM8607_IRQ_HEADSET] = {
312 .reg = PM8607_INT_STATUS3,
313 .mask_reg = PM8607_INT_MASK_3,
314 .offs = 1 << 2,
315 },
316 [PM8607_IRQ_HOOK] = {
317 .reg = PM8607_INT_STATUS3,
318 .mask_reg = PM8607_INT_MASK_3,
319 .offs = 1 << 3,
320 },
321 [PM8607_IRQ_MICIN] = {
322 .reg = PM8607_INT_STATUS3,
323 .mask_reg = PM8607_INT_MASK_3,
324 .offs = 1 << 4,
325 },
326 [PM8607_IRQ_CHG_FAIL] = {
327 .reg = PM8607_INT_STATUS3,
328 .mask_reg = PM8607_INT_MASK_3,
329 .offs = 1 << 5,
330 },
331 [PM8607_IRQ_CHG_DONE] = {
332 .reg = PM8607_INT_STATUS3,
333 .mask_reg = PM8607_INT_MASK_3,
334 .offs = 1 << 6,
335 },
336 [PM8607_IRQ_CHG_FAULT] = {
337 .reg = PM8607_INT_STATUS3,
338 .mask_reg = PM8607_INT_MASK_3,
339 .offs = 1 << 7,
340 },
341};
342
Haojian Zhuang2afa62e2010-02-08 05:02:00 -0500343static irqreturn_t pm860x_irq(int irq, void *data)
Haojian Zhuang5c42e8c2009-12-15 16:01:47 -0500344{
Haojian Zhuang5c42e8c2009-12-15 16:01:47 -0500345 struct pm860x_chip *chip = data;
Haojian Zhuang2afa62e2010-02-08 05:02:00 -0500346 struct pm860x_irq_data *irq_data;
347 struct i2c_client *i2c;
348 int read_reg = -1, value = 0;
349 int i;
Haojian Zhuang5c42e8c2009-12-15 16:01:47 -0500350
Haojian Zhuang2afa62e2010-02-08 05:02:00 -0500351 i2c = (chip->id == CHIP_PM8607) ? chip->client : chip->companion;
352 for (i = 0; i < ARRAY_SIZE(pm860x_irqs); i++) {
353 irq_data = &pm860x_irqs[i];
354 if (read_reg != irq_data->reg) {
355 read_reg = irq_data->reg;
356 value = pm860x_reg_read(i2c, irq_data->reg);
Haojian Zhuang5c42e8c2009-12-15 16:01:47 -0500357 }
Haojian Zhuang2afa62e2010-02-08 05:02:00 -0500358 if (value & irq_data->enable)
359 handle_nested_irq(chip->irq_base + i);
Haojian Zhuang5c42e8c2009-12-15 16:01:47 -0500360 }
Haojian Zhuang5c42e8c2009-12-15 16:01:47 -0500361 return IRQ_HANDLED;
362}
363
Mark Brown49f89d92010-12-11 12:31:31 +0000364static void pm860x_irq_lock(struct irq_data *data)
Haojian Zhuang5c42e8c2009-12-15 16:01:47 -0500365{
Mark Brown49f89d92010-12-11 12:31:31 +0000366 struct pm860x_chip *chip = irq_data_get_irq_chip_data(data);
Haojian Zhuang5c42e8c2009-12-15 16:01:47 -0500367
368 mutex_lock(&chip->irq_lock);
Haojian Zhuang5c42e8c2009-12-15 16:01:47 -0500369}
Haojian Zhuang5c42e8c2009-12-15 16:01:47 -0500370
Mark Brown49f89d92010-12-11 12:31:31 +0000371static void pm860x_irq_sync_unlock(struct irq_data *data)
Haojian Zhuang5c42e8c2009-12-15 16:01:47 -0500372{
Mark Brown49f89d92010-12-11 12:31:31 +0000373 struct pm860x_chip *chip = irq_data_get_irq_chip_data(data);
Haojian Zhuang2afa62e2010-02-08 05:02:00 -0500374 struct pm860x_irq_data *irq_data;
375 struct i2c_client *i2c;
376 static unsigned char cached[3] = {0x0, 0x0, 0x0};
377 unsigned char mask[3];
378 int i;
Haojian Zhuang5c42e8c2009-12-15 16:01:47 -0500379
Haojian Zhuang2afa62e2010-02-08 05:02:00 -0500380 i2c = (chip->id == CHIP_PM8607) ? chip->client : chip->companion;
381 /* Load cached value. In initial, all IRQs are masked */
382 for (i = 0; i < 3; i++)
383 mask[i] = cached[i];
384 for (i = 0; i < ARRAY_SIZE(pm860x_irqs); i++) {
385 irq_data = &pm860x_irqs[i];
386 switch (irq_data->mask_reg) {
387 case PM8607_INT_MASK_1:
388 mask[0] &= ~irq_data->offs;
389 mask[0] |= irq_data->enable;
390 break;
391 case PM8607_INT_MASK_2:
392 mask[1] &= ~irq_data->offs;
393 mask[1] |= irq_data->enable;
394 break;
395 case PM8607_INT_MASK_3:
396 mask[2] &= ~irq_data->offs;
397 mask[2] |= irq_data->enable;
398 break;
399 default:
400 dev_err(chip->dev, "wrong IRQ\n");
401 break;
402 }
403 }
404 /* update mask into registers */
405 for (i = 0; i < 3; i++) {
406 if (mask[i] != cached[i]) {
407 cached[i] = mask[i];
408 pm860x_reg_write(i2c, PM8607_INT_MASK_1 + i, mask[i]);
409 }
410 }
411
Haojian Zhuang5c42e8c2009-12-15 16:01:47 -0500412 mutex_unlock(&chip->irq_lock);
Haojian Zhuang5c42e8c2009-12-15 16:01:47 -0500413}
Haojian Zhuang2afa62e2010-02-08 05:02:00 -0500414
Mark Brown49f89d92010-12-11 12:31:31 +0000415static void pm860x_irq_enable(struct irq_data *data)
Haojian Zhuang2afa62e2010-02-08 05:02:00 -0500416{
Mark Brown49f89d92010-12-11 12:31:31 +0000417 struct pm860x_chip *chip = irq_data_get_irq_chip_data(data);
418 pm860x_irqs[data->irq - chip->irq_base].enable
419 = pm860x_irqs[data->irq - chip->irq_base].offs;
Haojian Zhuang2afa62e2010-02-08 05:02:00 -0500420}
421
Mark Brown49f89d92010-12-11 12:31:31 +0000422static void pm860x_irq_disable(struct irq_data *data)
Haojian Zhuang2afa62e2010-02-08 05:02:00 -0500423{
Mark Brown49f89d92010-12-11 12:31:31 +0000424 struct pm860x_chip *chip = irq_data_get_irq_chip_data(data);
425 pm860x_irqs[data->irq - chip->irq_base].enable = 0;
Haojian Zhuang2afa62e2010-02-08 05:02:00 -0500426}
427
428static struct irq_chip pm860x_irq_chip = {
429 .name = "88pm860x",
Mark Brown49f89d92010-12-11 12:31:31 +0000430 .irq_bus_lock = pm860x_irq_lock,
431 .irq_bus_sync_unlock = pm860x_irq_sync_unlock,
432 .irq_enable = pm860x_irq_enable,
433 .irq_disable = pm860x_irq_disable,
Haojian Zhuang2afa62e2010-02-08 05:02:00 -0500434};
Haojian Zhuang5c42e8c2009-12-15 16:01:47 -0500435
Haojian Zhuanga16122b2009-12-15 16:04:36 -0500436static int __devinit device_gpadc_init(struct pm860x_chip *chip,
437 struct pm860x_platform_data *pdata)
438{
439 struct i2c_client *i2c = (chip->id == CHIP_PM8607) ? chip->client \
440 : chip->companion;
Dan Carpentereb6e8dd2010-05-27 00:54:09 +0200441 int data;
442 int ret;
Haojian Zhuanga16122b2009-12-15 16:04:36 -0500443
444 /* initialize GPADC without activating it */
445
Dan Carpentereb6e8dd2010-05-27 00:54:09 +0200446 if (!pdata || !pdata->touch)
447 return -EINVAL;
Haojian Zhuanga16122b2009-12-15 16:04:36 -0500448
Dan Carpentereb6e8dd2010-05-27 00:54:09 +0200449 /* set GPADC MISC1 register */
450 data = 0;
451 data |= (pdata->touch->gpadc_prebias << 1) & PM8607_GPADC_PREBIAS_MASK;
452 data |= (pdata->touch->slot_cycle << 3) & PM8607_GPADC_SLOT_CYCLE_MASK;
453 data |= (pdata->touch->off_scale << 5) & PM8607_GPADC_OFF_SCALE_MASK;
454 data |= (pdata->touch->sw_cal << 7) & PM8607_GPADC_SW_CAL_MASK;
455 if (data) {
456 ret = pm860x_reg_write(i2c, PM8607_GPADC_MISC1, data);
457 if (ret < 0)
458 goto out;
459 }
460 /* set tsi prebias time */
461 if (pdata->touch->tsi_prebias) {
462 data = pdata->touch->tsi_prebias;
463 ret = pm860x_reg_write(i2c, PM8607_TSI_PREBIAS, data);
464 if (ret < 0)
465 goto out;
466 }
467 /* set prebias & prechg time of pen detect */
468 data = 0;
469 data |= pdata->touch->pen_prebias & PM8607_PD_PREBIAS_MASK;
470 data |= (pdata->touch->pen_prechg << 5) & PM8607_PD_PRECHG_MASK;
471 if (data) {
472 ret = pm860x_reg_write(i2c, PM8607_PD_PREBIAS, data);
473 if (ret < 0)
474 goto out;
Haojian Zhuanga16122b2009-12-15 16:04:36 -0500475 }
476
Dan Carpentereb6e8dd2010-05-27 00:54:09 +0200477 ret = pm860x_set_bits(i2c, PM8607_GPADC_MISC1,
478 PM8607_GPADC_EN, PM8607_GPADC_EN);
Haojian Zhuanga16122b2009-12-15 16:04:36 -0500479out:
480 return ret;
481}
482
Haojian Zhuang5c42e8c2009-12-15 16:01:47 -0500483static int __devinit device_irq_init(struct pm860x_chip *chip,
484 struct pm860x_platform_data *pdata)
485{
486 struct i2c_client *i2c = (chip->id == CHIP_PM8607) ? chip->client \
487 : chip->companion;
488 unsigned char status_buf[INT_STATUS_NUM];
Haojian Zhuang2afa62e2010-02-08 05:02:00 -0500489 unsigned long flags = IRQF_TRIGGER_FALLING | IRQF_ONESHOT;
490 struct irq_desc *desc;
491 int i, data, mask, ret = -EINVAL;
492 int __irq;
Haojian Zhuang5c42e8c2009-12-15 16:01:47 -0500493
Haojian Zhuang2afa62e2010-02-08 05:02:00 -0500494 if (!pdata || !pdata->irq_base) {
495 dev_warn(chip->dev, "No interrupt support on IRQ base\n");
496 return -EINVAL;
497 }
Haojian Zhuang5c42e8c2009-12-15 16:01:47 -0500498
499 mask = PM8607_B0_MISC1_INV_INT | PM8607_B0_MISC1_INT_CLEAR
500 | PM8607_B0_MISC1_INT_MASK;
501 data = 0;
502 chip->irq_mode = 0;
503 if (pdata && pdata->irq_mode) {
504 /*
505 * irq_mode defines the way of clearing interrupt. If it's 1,
506 * clear IRQ by write. Otherwise, clear it by read.
507 * This control bit is valid from 88PM8607 B0 steping.
508 */
509 data |= PM8607_B0_MISC1_INT_CLEAR;
510 chip->irq_mode = 1;
511 }
512 ret = pm860x_set_bits(i2c, PM8607_B0_MISC1, mask, data);
513 if (ret < 0)
514 goto out;
515
516 /* mask all IRQs */
517 memset(status_buf, 0, INT_STATUS_NUM);
518 ret = pm860x_bulk_write(i2c, PM8607_INT_MASK_1,
519 INT_STATUS_NUM, status_buf);
520 if (ret < 0)
521 goto out;
522
523 if (chip->irq_mode) {
524 /* clear interrupt status by write */
525 memset(status_buf, 0xFF, INT_STATUS_NUM);
526 ret = pm860x_bulk_write(i2c, PM8607_INT_STATUS1,
527 INT_STATUS_NUM, status_buf);
528 } else {
529 /* clear interrupt status by read */
530 ret = pm860x_bulk_read(i2c, PM8607_INT_STATUS1,
531 INT_STATUS_NUM, status_buf);
532 }
533 if (ret < 0)
534 goto out;
535
Haojian Zhuang2afa62e2010-02-08 05:02:00 -0500536 mutex_init(&chip->irq_lock);
537 chip->irq_base = pdata->irq_base;
538 chip->core_irq = i2c->irq;
539 if (!chip->core_irq)
Haojian Zhuang5c42e8c2009-12-15 16:01:47 -0500540 goto out;
Haojian Zhuang2afa62e2010-02-08 05:02:00 -0500541
542 desc = irq_to_desc(chip->core_irq);
543
544 /* register IRQ by genirq */
545 for (i = 0; i < ARRAY_SIZE(pm860x_irqs); i++) {
546 __irq = i + chip->irq_base;
547 set_irq_chip_data(__irq, chip);
548 set_irq_chip_and_handler(__irq, &pm860x_irq_chip,
549 handle_edge_irq);
550 set_irq_nested_thread(__irq, 1);
551#ifdef CONFIG_ARM
552 set_irq_flags(__irq, IRQF_VALID);
553#else
554 set_irq_noprobe(__irq);
555#endif
Haojian Zhuang5c42e8c2009-12-15 16:01:47 -0500556 }
Haojian Zhuang2afa62e2010-02-08 05:02:00 -0500557
558 ret = request_threaded_irq(chip->core_irq, NULL, pm860x_irq, flags,
559 "88pm860x", chip);
560 if (ret) {
561 dev_err(chip->dev, "Failed to request IRQ: %d\n", ret);
562 chip->core_irq = 0;
563 }
564
Haojian Zhuang5c42e8c2009-12-15 16:01:47 -0500565 return 0;
566out:
Haojian Zhuang2afa62e2010-02-08 05:02:00 -0500567 chip->core_irq = 0;
Haojian Zhuang5c42e8c2009-12-15 16:01:47 -0500568 return ret;
569}
570
Henrik Kretzschmar872c1b12010-03-26 02:40:13 +0100571static void device_irq_exit(struct pm860x_chip *chip)
Haojian Zhuang5c42e8c2009-12-15 16:01:47 -0500572{
Haojian Zhuang2afa62e2010-02-08 05:02:00 -0500573 if (chip->core_irq)
574 free_irq(chip->core_irq, chip);
Haojian Zhuang5c42e8c2009-12-15 16:01:47 -0500575}
576
Haojian Zhuangadb70482011-03-07 23:43:09 +0800577static void __devinit device_bk_init(struct pm860x_chip *chip,
578 struct i2c_client *i2c,
579 struct pm860x_platform_data *pdata)
580{
581 int ret;
582 int i, j, id;
583
584 if ((pdata == NULL) || (pdata->backlight == NULL))
585 return;
586
587 if (pdata->num_backlights > ARRAY_SIZE(bk_devs))
588 pdata->num_backlights = ARRAY_SIZE(bk_devs);
589
590 for (i = 0; i < pdata->num_backlights; i++) {
591 memcpy(&bk_pdata[i], &pdata->backlight[i],
592 sizeof(struct pm860x_backlight_pdata));
593 bk_devs[i].mfd_data = &bk_pdata[i];
594
595 for (j = 0; j < ARRAY_SIZE(bk_devs); j++) {
596 id = bk_resources[j].start;
597 if (bk_pdata[i].flags != id)
598 continue;
599
600 bk_devs[i].num_resources = 1;
601 bk_devs[i].resources = &bk_resources[j];
602 ret = mfd_add_devices(chip->dev, 0,
603 &bk_devs[i], 1,
604 &bk_resources[j], 0);
605 if (ret < 0) {
606 dev_err(chip->dev, "Failed to add "
607 "backlight subdev\n");
608 return;
609 }
610 }
611 }
612}
613
Haojian Zhuang5c42e8c2009-12-15 16:01:47 -0500614static void __devinit device_8606_init(struct pm860x_chip *chip,
615 struct i2c_client *i2c,
616 struct pm860x_platform_data *pdata)
Haojian Zhuang53dbab72010-01-08 06:01:24 -0500617{
Haojian Zhuanga16122b2009-12-15 16:04:36 -0500618 int ret;
619
Haojian Zhuanga16122b2009-12-15 16:04:36 -0500620 if (pdata && pdata->led) {
621 ret = mfd_add_devices(chip->dev, 0, &led_devs[0],
622 ARRAY_SIZE(led_devs),
623 &led_resources[0], 0);
624 if (ret < 0) {
625 dev_err(chip->dev, "Failed to add led "
626 "subdev\n");
627 goto out_dev;
628 }
629 }
630 return;
631out_dev:
Haojian Zhuanga16122b2009-12-15 16:04:36 -0500632 device_irq_exit(chip);
Haojian Zhuang53dbab72010-01-08 06:01:24 -0500633}
634
Haojian Zhuang5c42e8c2009-12-15 16:01:47 -0500635static void __devinit device_8607_init(struct pm860x_chip *chip,
636 struct i2c_client *i2c,
637 struct pm860x_platform_data *pdata)
Haojian Zhuangbbd51b12010-01-06 17:04:18 -0500638{
Haojian Zhuanga16122b2009-12-15 16:04:36 -0500639 int data, ret;
Haojian Zhuangbbd51b12010-01-06 17:04:18 -0500640
Haojian Zhuang53dbab72010-01-08 06:01:24 -0500641 ret = pm860x_reg_read(i2c, PM8607_CHIP_ID);
Haojian Zhuangbbd51b12010-01-06 17:04:18 -0500642 if (ret < 0) {
643 dev_err(chip->dev, "Failed to read CHIP ID: %d\n", ret);
644 goto out;
645 }
Haojian Zhuang38b340522010-09-08 09:44:34 -0400646 switch (ret & PM8607_VERSION_MASK) {
647 case 0x40:
648 case 0x50:
Haojian Zhuangbbd51b12010-01-06 17:04:18 -0500649 dev_info(chip->dev, "Marvell 88PM8607 (ID: %02x) detected\n",
650 ret);
Haojian Zhuang38b340522010-09-08 09:44:34 -0400651 break;
652 default:
Haojian Zhuangbbd51b12010-01-06 17:04:18 -0500653 dev_err(chip->dev, "Failed to detect Marvell 88PM8607. "
654 "Chip ID: %02x\n", ret);
655 goto out;
656 }
Haojian Zhuangbbd51b12010-01-06 17:04:18 -0500657
Haojian Zhuang53dbab72010-01-08 06:01:24 -0500658 ret = pm860x_reg_read(i2c, PM8607_BUCK3);
Haojian Zhuangbbd51b12010-01-06 17:04:18 -0500659 if (ret < 0) {
660 dev_err(chip->dev, "Failed to read BUCK3 register: %d\n", ret);
661 goto out;
662 }
663 if (ret & PM8607_BUCK3_DOUBLE)
664 chip->buck3_double = 1;
665
Haojian Zhuang5c42e8c2009-12-15 16:01:47 -0500666 ret = pm860x_reg_read(i2c, PM8607_B0_MISC1);
Haojian Zhuangbbd51b12010-01-06 17:04:18 -0500667 if (ret < 0) {
668 dev_err(chip->dev, "Failed to read MISC1 register: %d\n", ret);
669 goto out;
670 }
Haojian Zhuangbbd51b12010-01-06 17:04:18 -0500671
Haojian Zhuang5c42e8c2009-12-15 16:01:47 -0500672 if (pdata && (pdata->i2c_port == PI2C_PORT))
673 data = PM8607_B0_MISC1_PI2C;
674 else
675 data = 0;
676 ret = pm860x_set_bits(i2c, PM8607_B0_MISC1, PM8607_B0_MISC1_PI2C, data);
677 if (ret < 0) {
678 dev_err(chip->dev, "Failed to access MISC1:%d\n", ret);
679 goto out;
680 }
681
Haojian Zhuanga16122b2009-12-15 16:04:36 -0500682 ret = device_gpadc_init(chip, pdata);
683 if (ret < 0)
684 goto out;
685
Haojian Zhuang5c42e8c2009-12-15 16:01:47 -0500686 ret = device_irq_init(chip, pdata);
687 if (ret < 0)
688 goto out;
689
Haojian Zhuanga16122b2009-12-15 16:04:36 -0500690 ret = mfd_add_devices(chip->dev, 0, &regulator_devs[0],
691 ARRAY_SIZE(regulator_devs),
692 &regulator_resources[0], 0);
693 if (ret < 0) {
694 dev_err(chip->dev, "Failed to add regulator subdev\n");
695 goto out_dev;
696 }
697
698 if (pdata && pdata->touch) {
699 ret = mfd_add_devices(chip->dev, 0, &touch_devs[0],
700 ARRAY_SIZE(touch_devs),
701 &touch_resources[0], 0);
702 if (ret < 0) {
703 dev_err(chip->dev, "Failed to add touch "
704 "subdev\n");
705 goto out_dev;
Haojian Zhuangbbd51b12010-01-06 17:04:18 -0500706 }
707 }
Haojian Zhuang2afa62e2010-02-08 05:02:00 -0500708
709 if (pdata && pdata->power) {
710 ret = mfd_add_devices(chip->dev, 0, &power_devs[0],
711 ARRAY_SIZE(power_devs),
712 &power_supply_resources[0], 0);
713 if (ret < 0) {
714 dev_err(chip->dev, "Failed to add power supply "
715 "subdev\n");
716 goto out_dev;
717 }
718 }
719
720 ret = mfd_add_devices(chip->dev, 0, &onkey_devs[0],
721 ARRAY_SIZE(onkey_devs),
722 &onkey_resources[0], 0);
723 if (ret < 0) {
724 dev_err(chip->dev, "Failed to add onkey subdev\n");
725 goto out_dev;
726 }
727
Haojian Zhuang2c36af72010-08-12 11:59:33 +0800728 ret = mfd_add_devices(chip->dev, 0, &codec_devs[0],
729 ARRAY_SIZE(codec_devs),
730 &codec_resources[0], 0);
731 if (ret < 0) {
732 dev_err(chip->dev, "Failed to add codec subdev\n");
733 goto out_dev;
734 }
Haojian Zhuanga16122b2009-12-15 16:04:36 -0500735 return;
736out_dev:
737 mfd_remove_devices(chip->dev);
738 device_irq_exit(chip);
Haojian Zhuangbbd51b12010-01-06 17:04:18 -0500739out:
Haojian Zhuang53dbab72010-01-08 06:01:24 -0500740 return;
Haojian Zhuangbbd51b12010-01-06 17:04:18 -0500741}
742
Henrik Kretzschmar872c1b12010-03-26 02:40:13 +0100743int __devinit pm860x_device_init(struct pm860x_chip *chip,
Haojian Zhuang53dbab72010-01-08 06:01:24 -0500744 struct pm860x_platform_data *pdata)
745{
Haojian Zhuang2afa62e2010-02-08 05:02:00 -0500746 chip->core_irq = 0;
Haojian Zhuang5c42e8c2009-12-15 16:01:47 -0500747
Haojian Zhuang53dbab72010-01-08 06:01:24 -0500748 switch (chip->id) {
749 case CHIP_PM8606:
Haojian Zhuangadb70482011-03-07 23:43:09 +0800750 device_bk_init(chip, chip->client, pdata);
Haojian Zhuang53dbab72010-01-08 06:01:24 -0500751 device_8606_init(chip, chip->client, pdata);
752 break;
753 case CHIP_PM8607:
754 device_8607_init(chip, chip->client, pdata);
755 break;
756 }
757
758 if (chip->companion) {
759 switch (chip->id) {
760 case CHIP_PM8607:
Haojian Zhuangadb70482011-03-07 23:43:09 +0800761 device_bk_init(chip, chip->companion, pdata);
Haojian Zhuang53dbab72010-01-08 06:01:24 -0500762 device_8606_init(chip, chip->companion, pdata);
763 break;
764 case CHIP_PM8606:
765 device_8607_init(chip, chip->companion, pdata);
766 break;
767 }
768 }
Haojian Zhuang5c42e8c2009-12-15 16:01:47 -0500769
Haojian Zhuang53dbab72010-01-08 06:01:24 -0500770 return 0;
771}
772
Henrik Kretzschmar872c1b12010-03-26 02:40:13 +0100773void __devexit pm860x_device_exit(struct pm860x_chip *chip)
Haojian Zhuangbbd51b12010-01-06 17:04:18 -0500774{
Haojian Zhuang5c42e8c2009-12-15 16:01:47 -0500775 device_irq_exit(chip);
Haojian Zhuangbbd51b12010-01-06 17:04:18 -0500776 mfd_remove_devices(chip->dev);
777}
778
Haojian Zhuang53dbab72010-01-08 06:01:24 -0500779MODULE_DESCRIPTION("PMIC Driver for Marvell 88PM860x");
Haojian Zhuangbbd51b12010-01-06 17:04:18 -0500780MODULE_AUTHOR("Haojian Zhuang <haojian.zhuang@marvell.com>");
781MODULE_LICENSE("GPL");