blob: 96ea0c64bbbcd4776e0ab45720f5a757cfe8d2fb [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 Zhuang22aad002011-03-07 23:43:11 +080020#include <linux/regulator/machine.h>
Haojian Zhuangbbd51b12010-01-06 17:04:18 -050021
Haojian Zhuang2afa62e2010-02-08 05:02:00 -050022#define INT_STATUS_NUM 3
23
Haojian Zhuangadb70482011-03-07 23:43:09 +080024static struct resource bk_resources[] __initdata = {
25 {PM8606_BACKLIGHT1, PM8606_BACKLIGHT1, "backlight-0", IORESOURCE_IO,},
26 {PM8606_BACKLIGHT2, PM8606_BACKLIGHT2, "backlight-1", IORESOURCE_IO,},
27 {PM8606_BACKLIGHT3, PM8606_BACKLIGHT3, "backlight-2", IORESOURCE_IO,},
Haojian Zhuanga16122b2009-12-15 16:04:36 -050028};
Haojian Zhuangadb70482011-03-07 23:43:09 +080029
Haojian Zhuang3154c342011-03-07 23:43:10 +080030static struct resource led_resources[] __initdata = {
31 {PM8606_LED1_RED, PM8606_LED1_RED, "led0-red", IORESOURCE_IO,},
32 {PM8606_LED1_GREEN, PM8606_LED1_GREEN, "led0-green", IORESOURCE_IO,},
33 {PM8606_LED1_BLUE, PM8606_LED1_BLUE, "led0-blue", IORESOURCE_IO,},
34 {PM8606_LED2_RED, PM8606_LED2_RED, "led1-red", IORESOURCE_IO,},
35 {PM8606_LED2_GREEN, PM8606_LED2_GREEN, "led1-green", IORESOURCE_IO,},
36 {PM8606_LED2_BLUE, PM8606_LED2_BLUE, "led1-blue", IORESOURCE_IO,},
37};
38
Haojian Zhuang22aad002011-03-07 23:43:11 +080039static struct resource regulator_resources[] __initdata = {
40 {PM8607_ID_BUCK1, PM8607_ID_BUCK1, "buck-1", IORESOURCE_IO,},
41 {PM8607_ID_BUCK2, PM8607_ID_BUCK2, "buck-2", IORESOURCE_IO,},
42 {PM8607_ID_BUCK3, PM8607_ID_BUCK3, "buck-3", IORESOURCE_IO,},
43 {PM8607_ID_LDO1, PM8607_ID_LDO1, "ldo-01", IORESOURCE_IO,},
44 {PM8607_ID_LDO2, PM8607_ID_LDO2, "ldo-02", IORESOURCE_IO,},
45 {PM8607_ID_LDO3, PM8607_ID_LDO3, "ldo-03", IORESOURCE_IO,},
46 {PM8607_ID_LDO4, PM8607_ID_LDO4, "ldo-04", IORESOURCE_IO,},
47 {PM8607_ID_LDO5, PM8607_ID_LDO5, "ldo-05", IORESOURCE_IO,},
48 {PM8607_ID_LDO6, PM8607_ID_LDO6, "ldo-06", IORESOURCE_IO,},
49 {PM8607_ID_LDO7, PM8607_ID_LDO7, "ldo-07", IORESOURCE_IO,},
50 {PM8607_ID_LDO8, PM8607_ID_LDO8, "ldo-08", IORESOURCE_IO,},
51 {PM8607_ID_LDO9, PM8607_ID_LDO9, "ldo-09", IORESOURCE_IO,},
52 {PM8607_ID_LDO10, PM8607_ID_LDO10, "ldo-10", IORESOURCE_IO,},
53 {PM8607_ID_LDO11, PM8607_ID_LDO11, "ldo-11", IORESOURCE_IO,},
54 {PM8607_ID_LDO12, PM8607_ID_LDO12, "ldo-12", IORESOURCE_IO,},
55 {PM8607_ID_LDO13, PM8607_ID_LDO13, "ldo-13", IORESOURCE_IO,},
56 {PM8607_ID_LDO14, PM8607_ID_LDO14, "ldo-14", IORESOURCE_IO,},
57 {PM8607_ID_LDO15, PM8607_ID_LDO15, "ldo-15", IORESOURCE_IO,},
58};
59
Haojian Zhuangadb70482011-03-07 23:43:09 +080060static struct mfd_cell bk_devs[] __initdata = {
61 {"88pm860x-backlight", 0,},
62 {"88pm860x-backlight", 1,},
63 {"88pm860x-backlight", 2,},
64};
65
Haojian Zhuang3154c342011-03-07 23:43:10 +080066static struct mfd_cell led_devs[] __initdata = {
67 {"88pm860x-led", 0,},
68 {"88pm860x-led", 1,},
69 {"88pm860x-led", 2,},
70 {"88pm860x-led", 3,},
71 {"88pm860x-led", 4,},
72 {"88pm860x-led", 5,},
73};
74
Haojian Zhuang22aad002011-03-07 23:43:11 +080075static struct mfd_cell regulator_devs[] __initdata = {
76 {"88pm860x-regulator", 0,},
77 {"88pm860x-regulator", 1,},
78 {"88pm860x-regulator", 2,},
79 {"88pm860x-regulator", 3,},
80 {"88pm860x-regulator", 4,},
81 {"88pm860x-regulator", 5,},
82 {"88pm860x-regulator", 6,},
83 {"88pm860x-regulator", 7,},
84 {"88pm860x-regulator", 8,},
85 {"88pm860x-regulator", 9,},
86 {"88pm860x-regulator", 10,},
87 {"88pm860x-regulator", 11,},
88 {"88pm860x-regulator", 12,},
89 {"88pm860x-regulator", 13,},
90 {"88pm860x-regulator", 14,},
91 {"88pm860x-regulator", 15,},
92 {"88pm860x-regulator", 16,},
93 {"88pm860x-regulator", 17,},
94};
95
Haojian Zhuangadb70482011-03-07 23:43:09 +080096static struct pm860x_backlight_pdata bk_pdata[ARRAY_SIZE(bk_devs)];
Haojian Zhuang3154c342011-03-07 23:43:10 +080097static struct pm860x_led_pdata led_pdata[ARRAY_SIZE(led_devs)];
Haojian Zhuang22aad002011-03-07 23:43:11 +080098static struct regulator_init_data regulator_pdata[ARRAY_SIZE(regulator_devs)];
Haojian Zhuanga16122b2009-12-15 16:04:36 -050099
100static struct resource touch_resources[] = {
101 {
102 .start = PM8607_IRQ_PEN,
103 .end = PM8607_IRQ_PEN,
104 .flags = IORESOURCE_IRQ,
105 },
106};
107
108static struct mfd_cell touch_devs[] = {
109 {
110 .name = "88pm860x-touch",
111 .num_resources = 1,
112 .resources = &touch_resources[0],
113 },
114};
Haojian Zhuangbbd51b12010-01-06 17:04:18 -0500115
Haojian Zhuang2afa62e2010-02-08 05:02:00 -0500116static struct resource power_supply_resources[] = {
117 {
118 .name = "88pm860x-power",
119 .start = PM8607_IRQ_CHG,
120 .end = PM8607_IRQ_CHG,
121 .flags = IORESOURCE_IRQ,
122 },
123};
124
125static struct mfd_cell power_devs[] = {
126 {
127 .name = "88pm860x-power",
128 .num_resources = 1,
129 .resources = &power_supply_resources[0],
130 .id = -1,
131 },
132};
133
134static struct resource onkey_resources[] = {
135 {
136 .name = "88pm860x-onkey",
137 .start = PM8607_IRQ_ONKEY,
138 .end = PM8607_IRQ_ONKEY,
139 .flags = IORESOURCE_IRQ,
140 },
141};
142
143static struct mfd_cell onkey_devs[] = {
144 {
145 .name = "88pm860x-onkey",
146 .num_resources = 1,
147 .resources = &onkey_resources[0],
148 .id = -1,
149 },
150};
151
Haojian Zhuang2c36af72010-08-12 11:59:33 +0800152static struct resource codec_resources[] = {
153 {
154 /* Headset microphone insertion or removal */
155 .name = "micin",
156 .start = PM8607_IRQ_MICIN,
157 .end = PM8607_IRQ_MICIN,
158 .flags = IORESOURCE_IRQ,
159 }, {
160 /* Hook-switch press or release */
161 .name = "hook",
162 .start = PM8607_IRQ_HOOK,
163 .end = PM8607_IRQ_HOOK,
164 .flags = IORESOURCE_IRQ,
165 }, {
166 /* Headset insertion or removal */
167 .name = "headset",
168 .start = PM8607_IRQ_HEADSET,
169 .end = PM8607_IRQ_HEADSET,
170 .flags = IORESOURCE_IRQ,
171 }, {
172 /* Audio short */
173 .name = "audio-short",
174 .start = PM8607_IRQ_AUDIO_SHORT,
175 .end = PM8607_IRQ_AUDIO_SHORT,
176 .flags = IORESOURCE_IRQ,
177 },
178};
179
180static struct mfd_cell codec_devs[] = {
181 {
182 .name = "88pm860x-codec",
183 .num_resources = ARRAY_SIZE(codec_resources),
184 .resources = &codec_resources[0],
185 .id = -1,
186 },
187};
188
Haojian Zhuang2afa62e2010-02-08 05:02:00 -0500189struct pm860x_irq_data {
190 int reg;
191 int mask_reg;
192 int enable; /* enable or not */
193 int offs; /* bit offset in mask register */
194};
Haojian Zhuang5c42e8c2009-12-15 16:01:47 -0500195
Haojian Zhuang2afa62e2010-02-08 05:02:00 -0500196static struct pm860x_irq_data pm860x_irqs[] = {
197 [PM8607_IRQ_ONKEY] = {
198 .reg = PM8607_INT_STATUS1,
199 .mask_reg = PM8607_INT_MASK_1,
200 .offs = 1 << 0,
201 },
202 [PM8607_IRQ_EXTON] = {
203 .reg = PM8607_INT_STATUS1,
204 .mask_reg = PM8607_INT_MASK_1,
205 .offs = 1 << 1,
206 },
207 [PM8607_IRQ_CHG] = {
208 .reg = PM8607_INT_STATUS1,
209 .mask_reg = PM8607_INT_MASK_1,
210 .offs = 1 << 2,
211 },
212 [PM8607_IRQ_BAT] = {
213 .reg = PM8607_INT_STATUS1,
214 .mask_reg = PM8607_INT_MASK_1,
215 .offs = 1 << 3,
216 },
217 [PM8607_IRQ_RTC] = {
218 .reg = PM8607_INT_STATUS1,
219 .mask_reg = PM8607_INT_MASK_1,
220 .offs = 1 << 4,
221 },
222 [PM8607_IRQ_CC] = {
223 .reg = PM8607_INT_STATUS1,
224 .mask_reg = PM8607_INT_MASK_1,
225 .offs = 1 << 5,
226 },
227 [PM8607_IRQ_VBAT] = {
228 .reg = PM8607_INT_STATUS2,
229 .mask_reg = PM8607_INT_MASK_2,
230 .offs = 1 << 0,
231 },
232 [PM8607_IRQ_VCHG] = {
233 .reg = PM8607_INT_STATUS2,
234 .mask_reg = PM8607_INT_MASK_2,
235 .offs = 1 << 1,
236 },
237 [PM8607_IRQ_VSYS] = {
238 .reg = PM8607_INT_STATUS2,
239 .mask_reg = PM8607_INT_MASK_2,
240 .offs = 1 << 2,
241 },
242 [PM8607_IRQ_TINT] = {
243 .reg = PM8607_INT_STATUS2,
244 .mask_reg = PM8607_INT_MASK_2,
245 .offs = 1 << 3,
246 },
247 [PM8607_IRQ_GPADC0] = {
248 .reg = PM8607_INT_STATUS2,
249 .mask_reg = PM8607_INT_MASK_2,
250 .offs = 1 << 4,
251 },
252 [PM8607_IRQ_GPADC1] = {
253 .reg = PM8607_INT_STATUS2,
254 .mask_reg = PM8607_INT_MASK_2,
255 .offs = 1 << 5,
256 },
257 [PM8607_IRQ_GPADC2] = {
258 .reg = PM8607_INT_STATUS2,
259 .mask_reg = PM8607_INT_MASK_2,
260 .offs = 1 << 6,
261 },
262 [PM8607_IRQ_GPADC3] = {
263 .reg = PM8607_INT_STATUS2,
264 .mask_reg = PM8607_INT_MASK_2,
265 .offs = 1 << 7,
266 },
267 [PM8607_IRQ_AUDIO_SHORT] = {
268 .reg = PM8607_INT_STATUS3,
269 .mask_reg = PM8607_INT_MASK_3,
270 .offs = 1 << 0,
271 },
272 [PM8607_IRQ_PEN] = {
273 .reg = PM8607_INT_STATUS3,
274 .mask_reg = PM8607_INT_MASK_3,
275 .offs = 1 << 1,
276 },
277 [PM8607_IRQ_HEADSET] = {
278 .reg = PM8607_INT_STATUS3,
279 .mask_reg = PM8607_INT_MASK_3,
280 .offs = 1 << 2,
281 },
282 [PM8607_IRQ_HOOK] = {
283 .reg = PM8607_INT_STATUS3,
284 .mask_reg = PM8607_INT_MASK_3,
285 .offs = 1 << 3,
286 },
287 [PM8607_IRQ_MICIN] = {
288 .reg = PM8607_INT_STATUS3,
289 .mask_reg = PM8607_INT_MASK_3,
290 .offs = 1 << 4,
291 },
292 [PM8607_IRQ_CHG_FAIL] = {
293 .reg = PM8607_INT_STATUS3,
294 .mask_reg = PM8607_INT_MASK_3,
295 .offs = 1 << 5,
296 },
297 [PM8607_IRQ_CHG_DONE] = {
298 .reg = PM8607_INT_STATUS3,
299 .mask_reg = PM8607_INT_MASK_3,
300 .offs = 1 << 6,
301 },
302 [PM8607_IRQ_CHG_FAULT] = {
303 .reg = PM8607_INT_STATUS3,
304 .mask_reg = PM8607_INT_MASK_3,
305 .offs = 1 << 7,
306 },
307};
308
Haojian Zhuang2afa62e2010-02-08 05:02:00 -0500309static irqreturn_t pm860x_irq(int irq, void *data)
Haojian Zhuang5c42e8c2009-12-15 16:01:47 -0500310{
Haojian Zhuang5c42e8c2009-12-15 16:01:47 -0500311 struct pm860x_chip *chip = data;
Haojian Zhuang2afa62e2010-02-08 05:02:00 -0500312 struct pm860x_irq_data *irq_data;
313 struct i2c_client *i2c;
314 int read_reg = -1, value = 0;
315 int i;
Haojian Zhuang5c42e8c2009-12-15 16:01:47 -0500316
Haojian Zhuang2afa62e2010-02-08 05:02:00 -0500317 i2c = (chip->id == CHIP_PM8607) ? chip->client : chip->companion;
318 for (i = 0; i < ARRAY_SIZE(pm860x_irqs); i++) {
319 irq_data = &pm860x_irqs[i];
320 if (read_reg != irq_data->reg) {
321 read_reg = irq_data->reg;
322 value = pm860x_reg_read(i2c, irq_data->reg);
Haojian Zhuang5c42e8c2009-12-15 16:01:47 -0500323 }
Haojian Zhuang2afa62e2010-02-08 05:02:00 -0500324 if (value & irq_data->enable)
325 handle_nested_irq(chip->irq_base + i);
Haojian Zhuang5c42e8c2009-12-15 16:01:47 -0500326 }
Haojian Zhuang5c42e8c2009-12-15 16:01:47 -0500327 return IRQ_HANDLED;
328}
329
Mark Brown49f89d92010-12-11 12:31:31 +0000330static void pm860x_irq_lock(struct irq_data *data)
Haojian Zhuang5c42e8c2009-12-15 16:01:47 -0500331{
Mark Brown49f89d92010-12-11 12:31:31 +0000332 struct pm860x_chip *chip = irq_data_get_irq_chip_data(data);
Haojian Zhuang5c42e8c2009-12-15 16:01:47 -0500333
334 mutex_lock(&chip->irq_lock);
Haojian Zhuang5c42e8c2009-12-15 16:01:47 -0500335}
Haojian Zhuang5c42e8c2009-12-15 16:01:47 -0500336
Mark Brown49f89d92010-12-11 12:31:31 +0000337static void pm860x_irq_sync_unlock(struct irq_data *data)
Haojian Zhuang5c42e8c2009-12-15 16:01:47 -0500338{
Mark Brown49f89d92010-12-11 12:31:31 +0000339 struct pm860x_chip *chip = irq_data_get_irq_chip_data(data);
Haojian Zhuang2afa62e2010-02-08 05:02:00 -0500340 struct pm860x_irq_data *irq_data;
341 struct i2c_client *i2c;
342 static unsigned char cached[3] = {0x0, 0x0, 0x0};
343 unsigned char mask[3];
344 int i;
Haojian Zhuang5c42e8c2009-12-15 16:01:47 -0500345
Haojian Zhuang2afa62e2010-02-08 05:02:00 -0500346 i2c = (chip->id == CHIP_PM8607) ? chip->client : chip->companion;
347 /* Load cached value. In initial, all IRQs are masked */
348 for (i = 0; i < 3; i++)
349 mask[i] = cached[i];
350 for (i = 0; i < ARRAY_SIZE(pm860x_irqs); i++) {
351 irq_data = &pm860x_irqs[i];
352 switch (irq_data->mask_reg) {
353 case PM8607_INT_MASK_1:
354 mask[0] &= ~irq_data->offs;
355 mask[0] |= irq_data->enable;
356 break;
357 case PM8607_INT_MASK_2:
358 mask[1] &= ~irq_data->offs;
359 mask[1] |= irq_data->enable;
360 break;
361 case PM8607_INT_MASK_3:
362 mask[2] &= ~irq_data->offs;
363 mask[2] |= irq_data->enable;
364 break;
365 default:
366 dev_err(chip->dev, "wrong IRQ\n");
367 break;
368 }
369 }
370 /* update mask into registers */
371 for (i = 0; i < 3; i++) {
372 if (mask[i] != cached[i]) {
373 cached[i] = mask[i];
374 pm860x_reg_write(i2c, PM8607_INT_MASK_1 + i, mask[i]);
375 }
376 }
377
Haojian Zhuang5c42e8c2009-12-15 16:01:47 -0500378 mutex_unlock(&chip->irq_lock);
Haojian Zhuang5c42e8c2009-12-15 16:01:47 -0500379}
Haojian Zhuang2afa62e2010-02-08 05:02:00 -0500380
Mark Brown49f89d92010-12-11 12:31:31 +0000381static void pm860x_irq_enable(struct irq_data *data)
Haojian Zhuang2afa62e2010-02-08 05:02:00 -0500382{
Mark Brown49f89d92010-12-11 12:31:31 +0000383 struct pm860x_chip *chip = irq_data_get_irq_chip_data(data);
384 pm860x_irqs[data->irq - chip->irq_base].enable
385 = pm860x_irqs[data->irq - chip->irq_base].offs;
Haojian Zhuang2afa62e2010-02-08 05:02:00 -0500386}
387
Mark Brown49f89d92010-12-11 12:31:31 +0000388static void pm860x_irq_disable(struct irq_data *data)
Haojian Zhuang2afa62e2010-02-08 05:02:00 -0500389{
Mark Brown49f89d92010-12-11 12:31:31 +0000390 struct pm860x_chip *chip = irq_data_get_irq_chip_data(data);
391 pm860x_irqs[data->irq - chip->irq_base].enable = 0;
Haojian Zhuang2afa62e2010-02-08 05:02:00 -0500392}
393
394static struct irq_chip pm860x_irq_chip = {
395 .name = "88pm860x",
Mark Brown49f89d92010-12-11 12:31:31 +0000396 .irq_bus_lock = pm860x_irq_lock,
397 .irq_bus_sync_unlock = pm860x_irq_sync_unlock,
398 .irq_enable = pm860x_irq_enable,
399 .irq_disable = pm860x_irq_disable,
Haojian Zhuang2afa62e2010-02-08 05:02:00 -0500400};
Haojian Zhuang5c42e8c2009-12-15 16:01:47 -0500401
Haojian Zhuanga16122b2009-12-15 16:04:36 -0500402static int __devinit device_gpadc_init(struct pm860x_chip *chip,
403 struct pm860x_platform_data *pdata)
404{
405 struct i2c_client *i2c = (chip->id == CHIP_PM8607) ? chip->client \
406 : chip->companion;
Dan Carpentereb6e8dd2010-05-27 00:54:09 +0200407 int data;
408 int ret;
Haojian Zhuanga16122b2009-12-15 16:04:36 -0500409
410 /* initialize GPADC without activating it */
411
Dan Carpentereb6e8dd2010-05-27 00:54:09 +0200412 if (!pdata || !pdata->touch)
413 return -EINVAL;
Haojian Zhuanga16122b2009-12-15 16:04:36 -0500414
Dan Carpentereb6e8dd2010-05-27 00:54:09 +0200415 /* set GPADC MISC1 register */
416 data = 0;
417 data |= (pdata->touch->gpadc_prebias << 1) & PM8607_GPADC_PREBIAS_MASK;
418 data |= (pdata->touch->slot_cycle << 3) & PM8607_GPADC_SLOT_CYCLE_MASK;
419 data |= (pdata->touch->off_scale << 5) & PM8607_GPADC_OFF_SCALE_MASK;
420 data |= (pdata->touch->sw_cal << 7) & PM8607_GPADC_SW_CAL_MASK;
421 if (data) {
422 ret = pm860x_reg_write(i2c, PM8607_GPADC_MISC1, data);
423 if (ret < 0)
424 goto out;
425 }
426 /* set tsi prebias time */
427 if (pdata->touch->tsi_prebias) {
428 data = pdata->touch->tsi_prebias;
429 ret = pm860x_reg_write(i2c, PM8607_TSI_PREBIAS, data);
430 if (ret < 0)
431 goto out;
432 }
433 /* set prebias & prechg time of pen detect */
434 data = 0;
435 data |= pdata->touch->pen_prebias & PM8607_PD_PREBIAS_MASK;
436 data |= (pdata->touch->pen_prechg << 5) & PM8607_PD_PRECHG_MASK;
437 if (data) {
438 ret = pm860x_reg_write(i2c, PM8607_PD_PREBIAS, data);
439 if (ret < 0)
440 goto out;
Haojian Zhuanga16122b2009-12-15 16:04:36 -0500441 }
442
Dan Carpentereb6e8dd2010-05-27 00:54:09 +0200443 ret = pm860x_set_bits(i2c, PM8607_GPADC_MISC1,
444 PM8607_GPADC_EN, PM8607_GPADC_EN);
Haojian Zhuanga16122b2009-12-15 16:04:36 -0500445out:
446 return ret;
447}
448
Haojian Zhuang5c42e8c2009-12-15 16:01:47 -0500449static int __devinit device_irq_init(struct pm860x_chip *chip,
450 struct pm860x_platform_data *pdata)
451{
452 struct i2c_client *i2c = (chip->id == CHIP_PM8607) ? chip->client \
453 : chip->companion;
454 unsigned char status_buf[INT_STATUS_NUM];
Haojian Zhuang2afa62e2010-02-08 05:02:00 -0500455 unsigned long flags = IRQF_TRIGGER_FALLING | IRQF_ONESHOT;
456 struct irq_desc *desc;
457 int i, data, mask, ret = -EINVAL;
458 int __irq;
Haojian Zhuang5c42e8c2009-12-15 16:01:47 -0500459
Haojian Zhuang2afa62e2010-02-08 05:02:00 -0500460 if (!pdata || !pdata->irq_base) {
461 dev_warn(chip->dev, "No interrupt support on IRQ base\n");
462 return -EINVAL;
463 }
Haojian Zhuang5c42e8c2009-12-15 16:01:47 -0500464
465 mask = PM8607_B0_MISC1_INV_INT | PM8607_B0_MISC1_INT_CLEAR
466 | PM8607_B0_MISC1_INT_MASK;
467 data = 0;
468 chip->irq_mode = 0;
469 if (pdata && pdata->irq_mode) {
470 /*
471 * irq_mode defines the way of clearing interrupt. If it's 1,
472 * clear IRQ by write. Otherwise, clear it by read.
473 * This control bit is valid from 88PM8607 B0 steping.
474 */
475 data |= PM8607_B0_MISC1_INT_CLEAR;
476 chip->irq_mode = 1;
477 }
478 ret = pm860x_set_bits(i2c, PM8607_B0_MISC1, mask, data);
479 if (ret < 0)
480 goto out;
481
482 /* mask all IRQs */
483 memset(status_buf, 0, INT_STATUS_NUM);
484 ret = pm860x_bulk_write(i2c, PM8607_INT_MASK_1,
485 INT_STATUS_NUM, status_buf);
486 if (ret < 0)
487 goto out;
488
489 if (chip->irq_mode) {
490 /* clear interrupt status by write */
491 memset(status_buf, 0xFF, INT_STATUS_NUM);
492 ret = pm860x_bulk_write(i2c, PM8607_INT_STATUS1,
493 INT_STATUS_NUM, status_buf);
494 } else {
495 /* clear interrupt status by read */
496 ret = pm860x_bulk_read(i2c, PM8607_INT_STATUS1,
497 INT_STATUS_NUM, status_buf);
498 }
499 if (ret < 0)
500 goto out;
501
Haojian Zhuang2afa62e2010-02-08 05:02:00 -0500502 mutex_init(&chip->irq_lock);
503 chip->irq_base = pdata->irq_base;
504 chip->core_irq = i2c->irq;
505 if (!chip->core_irq)
Haojian Zhuang5c42e8c2009-12-15 16:01:47 -0500506 goto out;
Haojian Zhuang2afa62e2010-02-08 05:02:00 -0500507
508 desc = irq_to_desc(chip->core_irq);
509
510 /* register IRQ by genirq */
511 for (i = 0; i < ARRAY_SIZE(pm860x_irqs); i++) {
512 __irq = i + chip->irq_base;
513 set_irq_chip_data(__irq, chip);
514 set_irq_chip_and_handler(__irq, &pm860x_irq_chip,
515 handle_edge_irq);
516 set_irq_nested_thread(__irq, 1);
517#ifdef CONFIG_ARM
518 set_irq_flags(__irq, IRQF_VALID);
519#else
520 set_irq_noprobe(__irq);
521#endif
Haojian Zhuang5c42e8c2009-12-15 16:01:47 -0500522 }
Haojian Zhuang2afa62e2010-02-08 05:02:00 -0500523
524 ret = request_threaded_irq(chip->core_irq, NULL, pm860x_irq, flags,
525 "88pm860x", chip);
526 if (ret) {
527 dev_err(chip->dev, "Failed to request IRQ: %d\n", ret);
528 chip->core_irq = 0;
529 }
530
Haojian Zhuang5c42e8c2009-12-15 16:01:47 -0500531 return 0;
532out:
Haojian Zhuang2afa62e2010-02-08 05:02:00 -0500533 chip->core_irq = 0;
Haojian Zhuang5c42e8c2009-12-15 16:01:47 -0500534 return ret;
535}
536
Henrik Kretzschmar872c1b12010-03-26 02:40:13 +0100537static void device_irq_exit(struct pm860x_chip *chip)
Haojian Zhuang5c42e8c2009-12-15 16:01:47 -0500538{
Haojian Zhuang2afa62e2010-02-08 05:02:00 -0500539 if (chip->core_irq)
540 free_irq(chip->core_irq, chip);
Haojian Zhuang5c42e8c2009-12-15 16:01:47 -0500541}
542
Haojian Zhuangadb70482011-03-07 23:43:09 +0800543static void __devinit device_bk_init(struct pm860x_chip *chip,
544 struct i2c_client *i2c,
545 struct pm860x_platform_data *pdata)
546{
547 int ret;
548 int i, j, id;
549
550 if ((pdata == NULL) || (pdata->backlight == NULL))
551 return;
552
553 if (pdata->num_backlights > ARRAY_SIZE(bk_devs))
554 pdata->num_backlights = ARRAY_SIZE(bk_devs);
555
556 for (i = 0; i < pdata->num_backlights; i++) {
557 memcpy(&bk_pdata[i], &pdata->backlight[i],
558 sizeof(struct pm860x_backlight_pdata));
559 bk_devs[i].mfd_data = &bk_pdata[i];
560
561 for (j = 0; j < ARRAY_SIZE(bk_devs); j++) {
562 id = bk_resources[j].start;
563 if (bk_pdata[i].flags != id)
564 continue;
565
566 bk_devs[i].num_resources = 1;
567 bk_devs[i].resources = &bk_resources[j];
568 ret = mfd_add_devices(chip->dev, 0,
569 &bk_devs[i], 1,
570 &bk_resources[j], 0);
571 if (ret < 0) {
572 dev_err(chip->dev, "Failed to add "
573 "backlight subdev\n");
574 return;
575 }
576 }
577 }
578}
579
Haojian Zhuang3154c342011-03-07 23:43:10 +0800580static void __devinit device_led_init(struct pm860x_chip *chip,
581 struct i2c_client *i2c,
582 struct pm860x_platform_data *pdata)
Haojian Zhuang53dbab72010-01-08 06:01:24 -0500583{
Haojian Zhuanga16122b2009-12-15 16:04:36 -0500584 int ret;
Haojian Zhuang3154c342011-03-07 23:43:10 +0800585 int i, j, id;
Haojian Zhuanga16122b2009-12-15 16:04:36 -0500586
Haojian Zhuang3154c342011-03-07 23:43:10 +0800587 if ((pdata == NULL) || (pdata->led == NULL))
588 return;
589
590 if (pdata->num_leds > ARRAY_SIZE(led_devs))
591 pdata->num_leds = ARRAY_SIZE(led_devs);
592
593 for (i = 0; i < pdata->num_leds; i++) {
594 memcpy(&led_pdata[i], &pdata->led[i],
595 sizeof(struct pm860x_led_pdata));
596 led_devs[i].mfd_data = &led_pdata[i];
597
598 for (j = 0; j < ARRAY_SIZE(led_devs); j++) {
599 id = led_resources[j].start;
600 if (led_pdata[i].flags != id)
601 continue;
602
603 led_devs[i].num_resources = 1;
604 led_devs[i].resources = &led_resources[j],
605 ret = mfd_add_devices(chip->dev, 0,
606 &led_devs[i], 1,
607 &led_resources[j], 0);
608 if (ret < 0) {
609 dev_err(chip->dev, "Failed to add "
610 "led subdev\n");
611 return;
612 }
Haojian Zhuanga16122b2009-12-15 16:04:36 -0500613 }
614 }
Haojian Zhuang53dbab72010-01-08 06:01:24 -0500615}
616
Haojian Zhuang22aad002011-03-07 23:43:11 +0800617static void __devinit device_regulator_init(struct pm860x_chip *chip,
618 struct i2c_client *i2c,
619 struct pm860x_platform_data *pdata)
620{
621 struct regulator_init_data *initdata;
622 int ret;
623 int i, j;
624
625 if ((pdata == NULL) || (pdata->regulator == NULL))
626 return;
627
628 if (pdata->num_regulators > ARRAY_SIZE(regulator_devs))
629 pdata->num_regulators = ARRAY_SIZE(regulator_devs);
630
631 for (i = 0, j = -1; i < pdata->num_regulators; i++) {
632 initdata = &pdata->regulator[i];
633 if (strstr(initdata->constraints.name, "BUCK")) {
634 sscanf(initdata->constraints.name, "BUCK%d", &j);
635 /* BUCK1 ~ BUCK3 */
636 if ((j < 1) || (j > 3)) {
637 dev_err(chip->dev, "Failed to add constraint "
638 "(%s)\n", initdata->constraints.name);
639 goto out;
640 }
641 j = (j - 1) + PM8607_ID_BUCK1;
642 }
643 if (strstr(initdata->constraints.name, "LDO")) {
644 sscanf(initdata->constraints.name, "LDO%d", &j);
645 /* LDO1 ~ LDO15 */
646 if ((j < 1) || (j > 15)) {
647 dev_err(chip->dev, "Failed to add constraint "
648 "(%s)\n", initdata->constraints.name);
649 goto out;
650 }
651 j = (j - 1) + PM8607_ID_LDO1;
652 }
653 if (j == -1) {
654 dev_err(chip->dev, "Failed to add constraint (%s)\n",
655 initdata->constraints.name);
656 goto out;
657 }
658 memcpy(&regulator_pdata[i], &pdata->regulator[i],
659 sizeof(struct regulator_init_data));
660 regulator_devs[i].mfd_data = &regulator_pdata[i];
661 regulator_devs[i].num_resources = 1;
662 regulator_devs[i].resources = &regulator_resources[j];
663
664 ret = mfd_add_devices(chip->dev, 0, &regulator_devs[i], 1,
665 &regulator_resources[j], 0);
666 if (ret < 0) {
667 dev_err(chip->dev, "Failed to add regulator subdev\n");
668 goto out;
669 }
670 }
671out:
672 return;
673}
674
Haojian Zhuang5c42e8c2009-12-15 16:01:47 -0500675static void __devinit device_8607_init(struct pm860x_chip *chip,
676 struct i2c_client *i2c,
677 struct pm860x_platform_data *pdata)
Haojian Zhuangbbd51b12010-01-06 17:04:18 -0500678{
Haojian Zhuanga16122b2009-12-15 16:04:36 -0500679 int data, ret;
Haojian Zhuangbbd51b12010-01-06 17:04:18 -0500680
Haojian Zhuang53dbab72010-01-08 06:01:24 -0500681 ret = pm860x_reg_read(i2c, PM8607_CHIP_ID);
Haojian Zhuangbbd51b12010-01-06 17:04:18 -0500682 if (ret < 0) {
683 dev_err(chip->dev, "Failed to read CHIP ID: %d\n", ret);
684 goto out;
685 }
Haojian Zhuang38b340522010-09-08 09:44:34 -0400686 switch (ret & PM8607_VERSION_MASK) {
687 case 0x40:
688 case 0x50:
Haojian Zhuangbbd51b12010-01-06 17:04:18 -0500689 dev_info(chip->dev, "Marvell 88PM8607 (ID: %02x) detected\n",
690 ret);
Haojian Zhuang38b340522010-09-08 09:44:34 -0400691 break;
692 default:
Haojian Zhuangbbd51b12010-01-06 17:04:18 -0500693 dev_err(chip->dev, "Failed to detect Marvell 88PM8607. "
694 "Chip ID: %02x\n", ret);
695 goto out;
696 }
Haojian Zhuangbbd51b12010-01-06 17:04:18 -0500697
Haojian Zhuang53dbab72010-01-08 06:01:24 -0500698 ret = pm860x_reg_read(i2c, PM8607_BUCK3);
Haojian Zhuangbbd51b12010-01-06 17:04:18 -0500699 if (ret < 0) {
700 dev_err(chip->dev, "Failed to read BUCK3 register: %d\n", ret);
701 goto out;
702 }
703 if (ret & PM8607_BUCK3_DOUBLE)
704 chip->buck3_double = 1;
705
Haojian Zhuang5c42e8c2009-12-15 16:01:47 -0500706 ret = pm860x_reg_read(i2c, PM8607_B0_MISC1);
Haojian Zhuangbbd51b12010-01-06 17:04:18 -0500707 if (ret < 0) {
708 dev_err(chip->dev, "Failed to read MISC1 register: %d\n", ret);
709 goto out;
710 }
Haojian Zhuangbbd51b12010-01-06 17:04:18 -0500711
Haojian Zhuang5c42e8c2009-12-15 16:01:47 -0500712 if (pdata && (pdata->i2c_port == PI2C_PORT))
713 data = PM8607_B0_MISC1_PI2C;
714 else
715 data = 0;
716 ret = pm860x_set_bits(i2c, PM8607_B0_MISC1, PM8607_B0_MISC1_PI2C, data);
717 if (ret < 0) {
718 dev_err(chip->dev, "Failed to access MISC1:%d\n", ret);
719 goto out;
720 }
721
Haojian Zhuanga16122b2009-12-15 16:04:36 -0500722 ret = device_gpadc_init(chip, pdata);
723 if (ret < 0)
724 goto out;
725
Haojian Zhuang5c42e8c2009-12-15 16:01:47 -0500726 ret = device_irq_init(chip, pdata);
727 if (ret < 0)
728 goto out;
729
Haojian Zhuanga16122b2009-12-15 16:04:36 -0500730 if (pdata && pdata->touch) {
731 ret = mfd_add_devices(chip->dev, 0, &touch_devs[0],
732 ARRAY_SIZE(touch_devs),
733 &touch_resources[0], 0);
734 if (ret < 0) {
735 dev_err(chip->dev, "Failed to add touch "
736 "subdev\n");
737 goto out_dev;
Haojian Zhuangbbd51b12010-01-06 17:04:18 -0500738 }
739 }
Haojian Zhuang2afa62e2010-02-08 05:02:00 -0500740
741 if (pdata && pdata->power) {
742 ret = mfd_add_devices(chip->dev, 0, &power_devs[0],
743 ARRAY_SIZE(power_devs),
744 &power_supply_resources[0], 0);
745 if (ret < 0) {
746 dev_err(chip->dev, "Failed to add power supply "
747 "subdev\n");
748 goto out_dev;
749 }
750 }
751
752 ret = mfd_add_devices(chip->dev, 0, &onkey_devs[0],
753 ARRAY_SIZE(onkey_devs),
754 &onkey_resources[0], 0);
755 if (ret < 0) {
756 dev_err(chip->dev, "Failed to add onkey subdev\n");
757 goto out_dev;
758 }
759
Haojian Zhuang2c36af72010-08-12 11:59:33 +0800760 ret = mfd_add_devices(chip->dev, 0, &codec_devs[0],
761 ARRAY_SIZE(codec_devs),
762 &codec_resources[0], 0);
763 if (ret < 0) {
764 dev_err(chip->dev, "Failed to add codec subdev\n");
765 goto out_dev;
766 }
Haojian Zhuang22aad002011-03-07 23:43:11 +0800767
768 device_regulator_init(chip, i2c, pdata);
Haojian Zhuanga16122b2009-12-15 16:04:36 -0500769 return;
770out_dev:
771 mfd_remove_devices(chip->dev);
772 device_irq_exit(chip);
Haojian Zhuangbbd51b12010-01-06 17:04:18 -0500773out:
Haojian Zhuang53dbab72010-01-08 06:01:24 -0500774 return;
Haojian Zhuangbbd51b12010-01-06 17:04:18 -0500775}
776
Henrik Kretzschmar872c1b12010-03-26 02:40:13 +0100777int __devinit pm860x_device_init(struct pm860x_chip *chip,
Haojian Zhuang53dbab72010-01-08 06:01:24 -0500778 struct pm860x_platform_data *pdata)
779{
Haojian Zhuang2afa62e2010-02-08 05:02:00 -0500780 chip->core_irq = 0;
Haojian Zhuang5c42e8c2009-12-15 16:01:47 -0500781
Haojian Zhuang53dbab72010-01-08 06:01:24 -0500782 switch (chip->id) {
783 case CHIP_PM8606:
Haojian Zhuangadb70482011-03-07 23:43:09 +0800784 device_bk_init(chip, chip->client, pdata);
Haojian Zhuang3154c342011-03-07 23:43:10 +0800785 device_led_init(chip, chip->client, pdata);
Haojian Zhuang53dbab72010-01-08 06:01:24 -0500786 break;
787 case CHIP_PM8607:
788 device_8607_init(chip, chip->client, pdata);
789 break;
790 }
791
792 if (chip->companion) {
793 switch (chip->id) {
794 case CHIP_PM8607:
Haojian Zhuangadb70482011-03-07 23:43:09 +0800795 device_bk_init(chip, chip->companion, pdata);
Haojian Zhuang3154c342011-03-07 23:43:10 +0800796 device_led_init(chip, chip->companion, pdata);
Haojian Zhuang53dbab72010-01-08 06:01:24 -0500797 break;
798 case CHIP_PM8606:
799 device_8607_init(chip, chip->companion, pdata);
800 break;
801 }
802 }
Haojian Zhuang5c42e8c2009-12-15 16:01:47 -0500803
Haojian Zhuang53dbab72010-01-08 06:01:24 -0500804 return 0;
805}
806
Henrik Kretzschmar872c1b12010-03-26 02:40:13 +0100807void __devexit pm860x_device_exit(struct pm860x_chip *chip)
Haojian Zhuangbbd51b12010-01-06 17:04:18 -0500808{
Haojian Zhuang5c42e8c2009-12-15 16:01:47 -0500809 device_irq_exit(chip);
Haojian Zhuangbbd51b12010-01-06 17:04:18 -0500810 mfd_remove_devices(chip->dev);
811}
812
Haojian Zhuang53dbab72010-01-08 06:01:24 -0500813MODULE_DESCRIPTION("PMIC Driver for Marvell 88PM860x");
Haojian Zhuangbbd51b12010-01-06 17:04:18 -0500814MODULE_AUTHOR("Haojian Zhuang <haojian.zhuang@marvell.com>");
815MODULE_LICENSE("GPL");