blob: e364b22f957d70336df1ab1dfa12980520f991a3 [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 Zhuanga6ccdcd2012-08-08 23:17:26 +080024static struct resource bk0_resources[] __devinitdata = {
25 {2, 2, "duty cycle", IORESOURCE_REG, },
26 {3, 3, "always on", IORESOURCE_REG, },
27 {3, 3, "current", IORESOURCE_REG, },
28};
29static struct resource bk1_resources[] __devinitdata = {
30 {4, 4, "duty cycle", IORESOURCE_REG, },
31 {5, 5, "always on", IORESOURCE_REG, },
32 {5, 5, "current", IORESOURCE_REG, },
33};
34static struct resource bk2_resources[] __devinitdata = {
35 {6, 6, "duty cycle", IORESOURCE_REG, },
36 {7, 7, "always on", IORESOURCE_REG, },
37 {5, 5, "current", IORESOURCE_REG, },
Haojian Zhuanga16122b2009-12-15 16:04:36 -050038};
Haojian Zhuangadb70482011-03-07 23:43:09 +080039
Haojian Zhuang894fc8f2012-08-08 23:17:27 +080040static struct resource led0_resources[] __devinitdata = {
41 /* RGB1 Red LED */
42 {0xd, 0xd, "control", IORESOURCE_REG, },
43 {0xc, 0xc, "blink", IORESOURCE_REG, },
44};
45static struct resource led1_resources[] __devinitdata = {
46 /* RGB1 Green LED */
47 {0xe, 0xe, "control", IORESOURCE_REG, },
48 {0xc, 0xc, "blink", IORESOURCE_REG, },
49};
50static struct resource led2_resources[] __devinitdata = {
51 /* RGB1 Blue LED */
52 {0xf, 0xf, "control", IORESOURCE_REG, },
53 {0xc, 0xc, "blink", IORESOURCE_REG, },
54};
55static struct resource led3_resources[] __devinitdata = {
56 /* RGB2 Red LED */
57 {0x9, 0x9, "control", IORESOURCE_REG, },
58 {0x8, 0x8, "blink", IORESOURCE_REG, },
59};
60static struct resource led4_resources[] __devinitdata = {
61 /* RGB2 Green LED */
62 {0xa, 0xa, "control", IORESOURCE_REG, },
63 {0x8, 0x8, "blink", IORESOURCE_REG, },
64};
65static struct resource led5_resources[] __devinitdata = {
66 /* RGB2 Blue LED */
67 {0xb, 0xb, "control", IORESOURCE_REG, },
68 {0x8, 0x8, "blink", IORESOURCE_REG, },
Haojian Zhuang3154c342011-03-07 23:43:10 +080069};
70
Haojian Zhuanga5156f12011-04-26 11:06:22 +020071static struct resource regulator_resources[] __devinitdata = {
Mark Brown02367022012-08-07 19:42:48 +010072 {PM8607_ID_BUCK1, PM8607_ID_BUCK1, "buck-1", IORESOURCE_REG,},
73 {PM8607_ID_BUCK2, PM8607_ID_BUCK2, "buck-2", IORESOURCE_REG,},
74 {PM8607_ID_BUCK3, PM8607_ID_BUCK3, "buck-3", IORESOURCE_REG,},
75 {PM8607_ID_LDO1, PM8607_ID_LDO1, "ldo-01", IORESOURCE_REG,},
76 {PM8607_ID_LDO2, PM8607_ID_LDO2, "ldo-02", IORESOURCE_REG,},
77 {PM8607_ID_LDO3, PM8607_ID_LDO3, "ldo-03", IORESOURCE_REG,},
78 {PM8607_ID_LDO4, PM8607_ID_LDO4, "ldo-04", IORESOURCE_REG,},
79 {PM8607_ID_LDO5, PM8607_ID_LDO5, "ldo-05", IORESOURCE_REG,},
80 {PM8607_ID_LDO6, PM8607_ID_LDO6, "ldo-06", IORESOURCE_REG,},
81 {PM8607_ID_LDO7, PM8607_ID_LDO7, "ldo-07", IORESOURCE_REG,},
82 {PM8607_ID_LDO8, PM8607_ID_LDO8, "ldo-08", IORESOURCE_REG,},
83 {PM8607_ID_LDO9, PM8607_ID_LDO9, "ldo-09", IORESOURCE_REG,},
84 {PM8607_ID_LDO10, PM8607_ID_LDO10, "ldo-10", IORESOURCE_REG,},
85 {PM8607_ID_LDO11, PM8607_ID_LDO11, "ldo-11", IORESOURCE_REG,},
86 {PM8607_ID_LDO12, PM8607_ID_LDO12, "ldo-12", IORESOURCE_REG,},
87 {PM8607_ID_LDO13, PM8607_ID_LDO13, "ldo-13", IORESOURCE_REG,},
88 {PM8607_ID_LDO14, PM8607_ID_LDO14, "ldo-14", IORESOURCE_REG,},
89 {PM8607_ID_LDO15, PM8607_ID_LDO15, "ldo-15", IORESOURCE_REG,},
Haojian Zhuang22aad002011-03-07 23:43:11 +080090};
91
Haojian Zhuanga5156f12011-04-26 11:06:22 +020092static struct resource touch_resources[] __devinitdata = {
Haojian Zhuangc9f560b2011-03-07 23:43:12 +080093 {PM8607_IRQ_PEN, PM8607_IRQ_PEN, "touch", IORESOURCE_IRQ,},
94};
95
Haojian Zhuanga5156f12011-04-26 11:06:22 +020096static struct resource onkey_resources[] __devinitdata = {
Haojian Zhuangc9f560b2011-03-07 23:43:12 +080097 {PM8607_IRQ_ONKEY, PM8607_IRQ_ONKEY, "onkey", IORESOURCE_IRQ,},
98};
99
Haojian Zhuanga5156f12011-04-26 11:06:22 +0200100static struct resource codec_resources[] __devinitdata = {
Haojian Zhuangc9f560b2011-03-07 23:43:12 +0800101 /* Headset microphone insertion or removal */
102 {PM8607_IRQ_MICIN, PM8607_IRQ_MICIN, "micin", IORESOURCE_IRQ,},
103 /* Hook-switch press or release */
104 {PM8607_IRQ_HOOK, PM8607_IRQ_HOOK, "hook", IORESOURCE_IRQ,},
105 /* Headset insertion or removal */
106 {PM8607_IRQ_HEADSET, PM8607_IRQ_HEADSET, "headset", IORESOURCE_IRQ,},
107 /* Audio short */
108 {PM8607_IRQ_AUDIO_SHORT, PM8607_IRQ_AUDIO_SHORT, "audio-short", IORESOURCE_IRQ,},
109};
110
Haojian Zhuanga5156f12011-04-26 11:06:22 +0200111static struct resource battery_resources[] __devinitdata = {
Haojian Zhuangc9f560b2011-03-07 23:43:12 +0800112 {PM8607_IRQ_CC, PM8607_IRQ_CC, "columb counter", IORESOURCE_IRQ,},
113 {PM8607_IRQ_BAT, PM8607_IRQ_BAT, "battery", IORESOURCE_IRQ,},
114};
115
Haojian Zhuanga5156f12011-04-26 11:06:22 +0200116static struct resource charger_resources[] __devinitdata = {
Haojian Zhuangc9f560b2011-03-07 23:43:12 +0800117 {PM8607_IRQ_CHG, PM8607_IRQ_CHG, "charger detect", IORESOURCE_IRQ,},
118 {PM8607_IRQ_CHG_DONE, PM8607_IRQ_CHG_DONE, "charging done", IORESOURCE_IRQ,},
119 {PM8607_IRQ_CHG_FAULT, PM8607_IRQ_CHG_FAULT, "charging timeout", IORESOURCE_IRQ,},
120 {PM8607_IRQ_GPADC1, PM8607_IRQ_GPADC1, "battery temperature", IORESOURCE_IRQ,},
121 {PM8607_IRQ_VBAT, PM8607_IRQ_VBAT, "battery voltage", IORESOURCE_IRQ,},
122 {PM8607_IRQ_VCHG, PM8607_IRQ_VCHG, "vchg voltage", IORESOURCE_IRQ,},
123};
124
Jett.Zhou2573f6d2012-07-06 10:59:58 +0800125static struct resource preg_resources[] __devinitdata = {
Mark Brown02367022012-08-07 19:42:48 +0100126 {PM8606_ID_PREG, PM8606_ID_PREG, "preg", IORESOURCE_REG,},
Jett.Zhou2573f6d2012-07-06 10:59:58 +0800127};
128
Haojian Zhuang008b3042011-05-06 17:21:20 +0800129static struct resource rtc_resources[] __devinitdata = {
Mark Brown02367022012-08-07 19:42:48 +0100130 {PM8607_IRQ_RTC, PM8607_IRQ_RTC, "rtc", IORESOURCE_IRQ,},
Haojian Zhuang008b3042011-05-06 17:21:20 +0800131};
132
Haojian Zhuanga5156f12011-04-26 11:06:22 +0200133static struct mfd_cell bk_devs[] = {
Haojian Zhuanga6ccdcd2012-08-08 23:17:26 +0800134 {
135 .name = "88pm860x-backlight",
136 .id = 0,
137 .num_resources = ARRAY_SIZE(bk0_resources),
138 .resources = bk0_resources,
139 }, {
140 .name = "88pm860x-backlight",
141 .id = 1,
142 .num_resources = ARRAY_SIZE(bk1_resources),
143 .resources = bk1_resources,
144 }, {
145 .name = "88pm860x-backlight",
146 .id = 2,
147 .num_resources = ARRAY_SIZE(bk2_resources),
148 .resources = bk2_resources,
149 },
Haojian Zhuangadb70482011-03-07 23:43:09 +0800150};
151
Haojian Zhuanga5156f12011-04-26 11:06:22 +0200152static struct mfd_cell led_devs[] = {
Haojian Zhuang894fc8f2012-08-08 23:17:27 +0800153 {
154 .name = "88pm860x-led",
155 .id = 0,
156 .num_resources = ARRAY_SIZE(led0_resources),
157 .resources = led0_resources,
158 }, {
159 .name = "88pm860x-led",
160 .id = 1,
161 .num_resources = ARRAY_SIZE(led1_resources),
162 .resources = led1_resources,
163 }, {
164 .name = "88pm860x-led",
165 .id = 2,
166 .num_resources = ARRAY_SIZE(led2_resources),
167 .resources = led2_resources,
168 }, {
169 .name = "88pm860x-led",
170 .id = 3,
171 .num_resources = ARRAY_SIZE(led3_resources),
172 .resources = led3_resources,
173 }, {
174 .name = "88pm860x-led",
175 .id = 4,
176 .num_resources = ARRAY_SIZE(led4_resources),
177 .resources = led4_resources,
178 }, {
179 .name = "88pm860x-led",
180 .id = 5,
181 .num_resources = ARRAY_SIZE(led5_resources),
182 .resources = led5_resources,
183 },
Haojian Zhuang3154c342011-03-07 23:43:10 +0800184};
185
Haojian Zhuanga5156f12011-04-26 11:06:22 +0200186static struct mfd_cell regulator_devs[] = {
Haojian Zhuang22aad002011-03-07 23:43:11 +0800187 {"88pm860x-regulator", 0,},
188 {"88pm860x-regulator", 1,},
189 {"88pm860x-regulator", 2,},
190 {"88pm860x-regulator", 3,},
191 {"88pm860x-regulator", 4,},
192 {"88pm860x-regulator", 5,},
193 {"88pm860x-regulator", 6,},
194 {"88pm860x-regulator", 7,},
195 {"88pm860x-regulator", 8,},
196 {"88pm860x-regulator", 9,},
197 {"88pm860x-regulator", 10,},
198 {"88pm860x-regulator", 11,},
199 {"88pm860x-regulator", 12,},
200 {"88pm860x-regulator", 13,},
201 {"88pm860x-regulator", 14,},
202 {"88pm860x-regulator", 15,},
203 {"88pm860x-regulator", 16,},
204 {"88pm860x-regulator", 17,},
205};
206
Haojian Zhuanga5156f12011-04-26 11:06:22 +0200207static struct mfd_cell touch_devs[] = {
Haojian Zhuangc9f560b2011-03-07 23:43:12 +0800208 {"88pm860x-touch", -1,},
Haojian Zhuanga16122b2009-12-15 16:04:36 -0500209};
210
Haojian Zhuanga5156f12011-04-26 11:06:22 +0200211static struct mfd_cell onkey_devs[] = {
Haojian Zhuangc9f560b2011-03-07 23:43:12 +0800212 {"88pm860x-onkey", -1,},
Haojian Zhuanga16122b2009-12-15 16:04:36 -0500213};
Haojian Zhuangbbd51b12010-01-06 17:04:18 -0500214
Haojian Zhuanga5156f12011-04-26 11:06:22 +0200215static struct mfd_cell codec_devs[] = {
Haojian Zhuangc9f560b2011-03-07 23:43:12 +0800216 {"88pm860x-codec", -1,},
Haojian Zhuang2afa62e2010-02-08 05:02:00 -0500217};
218
Jett.Zhou2573f6d2012-07-06 10:59:58 +0800219static struct regulator_consumer_supply preg_supply[] = {
220 REGULATOR_SUPPLY("preg", "charger-manager"),
221};
222
223static struct regulator_init_data preg_init_data = {
224 .num_consumer_supplies = ARRAY_SIZE(preg_supply),
225 .consumer_supplies = &preg_supply[0],
226};
227
Haojian Zhuang2afa62e2010-02-08 05:02:00 -0500228static struct mfd_cell power_devs[] = {
Haojian Zhuangc9f560b2011-03-07 23:43:12 +0800229 {"88pm860x-battery", -1,},
230 {"88pm860x-charger", -1,},
Jett.Zhou2573f6d2012-07-06 10:59:58 +0800231 {"88pm860x-preg", -1,},
Haojian Zhuang2afa62e2010-02-08 05:02:00 -0500232};
233
Haojian Zhuang008b3042011-05-06 17:21:20 +0800234static struct mfd_cell rtc_devs[] = {
235 {"88pm860x-rtc", -1,},
236};
237
Haojian Zhuang2c36af72010-08-12 11:59:33 +0800238
Haojian Zhuang2afa62e2010-02-08 05:02:00 -0500239struct pm860x_irq_data {
240 int reg;
241 int mask_reg;
242 int enable; /* enable or not */
243 int offs; /* bit offset in mask register */
244};
Haojian Zhuang5c42e8c2009-12-15 16:01:47 -0500245
Haojian Zhuang2afa62e2010-02-08 05:02:00 -0500246static struct pm860x_irq_data pm860x_irqs[] = {
247 [PM8607_IRQ_ONKEY] = {
248 .reg = PM8607_INT_STATUS1,
249 .mask_reg = PM8607_INT_MASK_1,
250 .offs = 1 << 0,
251 },
252 [PM8607_IRQ_EXTON] = {
253 .reg = PM8607_INT_STATUS1,
254 .mask_reg = PM8607_INT_MASK_1,
255 .offs = 1 << 1,
256 },
257 [PM8607_IRQ_CHG] = {
258 .reg = PM8607_INT_STATUS1,
259 .mask_reg = PM8607_INT_MASK_1,
260 .offs = 1 << 2,
261 },
262 [PM8607_IRQ_BAT] = {
263 .reg = PM8607_INT_STATUS1,
264 .mask_reg = PM8607_INT_MASK_1,
265 .offs = 1 << 3,
266 },
267 [PM8607_IRQ_RTC] = {
268 .reg = PM8607_INT_STATUS1,
269 .mask_reg = PM8607_INT_MASK_1,
270 .offs = 1 << 4,
271 },
272 [PM8607_IRQ_CC] = {
273 .reg = PM8607_INT_STATUS1,
274 .mask_reg = PM8607_INT_MASK_1,
275 .offs = 1 << 5,
276 },
277 [PM8607_IRQ_VBAT] = {
278 .reg = PM8607_INT_STATUS2,
279 .mask_reg = PM8607_INT_MASK_2,
280 .offs = 1 << 0,
281 },
282 [PM8607_IRQ_VCHG] = {
283 .reg = PM8607_INT_STATUS2,
284 .mask_reg = PM8607_INT_MASK_2,
285 .offs = 1 << 1,
286 },
287 [PM8607_IRQ_VSYS] = {
288 .reg = PM8607_INT_STATUS2,
289 .mask_reg = PM8607_INT_MASK_2,
290 .offs = 1 << 2,
291 },
292 [PM8607_IRQ_TINT] = {
293 .reg = PM8607_INT_STATUS2,
294 .mask_reg = PM8607_INT_MASK_2,
295 .offs = 1 << 3,
296 },
297 [PM8607_IRQ_GPADC0] = {
298 .reg = PM8607_INT_STATUS2,
299 .mask_reg = PM8607_INT_MASK_2,
300 .offs = 1 << 4,
301 },
302 [PM8607_IRQ_GPADC1] = {
303 .reg = PM8607_INT_STATUS2,
304 .mask_reg = PM8607_INT_MASK_2,
305 .offs = 1 << 5,
306 },
307 [PM8607_IRQ_GPADC2] = {
308 .reg = PM8607_INT_STATUS2,
309 .mask_reg = PM8607_INT_MASK_2,
310 .offs = 1 << 6,
311 },
312 [PM8607_IRQ_GPADC3] = {
313 .reg = PM8607_INT_STATUS2,
314 .mask_reg = PM8607_INT_MASK_2,
315 .offs = 1 << 7,
316 },
317 [PM8607_IRQ_AUDIO_SHORT] = {
318 .reg = PM8607_INT_STATUS3,
319 .mask_reg = PM8607_INT_MASK_3,
320 .offs = 1 << 0,
321 },
322 [PM8607_IRQ_PEN] = {
323 .reg = PM8607_INT_STATUS3,
324 .mask_reg = PM8607_INT_MASK_3,
325 .offs = 1 << 1,
326 },
327 [PM8607_IRQ_HEADSET] = {
328 .reg = PM8607_INT_STATUS3,
329 .mask_reg = PM8607_INT_MASK_3,
330 .offs = 1 << 2,
331 },
332 [PM8607_IRQ_HOOK] = {
333 .reg = PM8607_INT_STATUS3,
334 .mask_reg = PM8607_INT_MASK_3,
335 .offs = 1 << 3,
336 },
337 [PM8607_IRQ_MICIN] = {
338 .reg = PM8607_INT_STATUS3,
339 .mask_reg = PM8607_INT_MASK_3,
340 .offs = 1 << 4,
341 },
342 [PM8607_IRQ_CHG_FAIL] = {
343 .reg = PM8607_INT_STATUS3,
344 .mask_reg = PM8607_INT_MASK_3,
345 .offs = 1 << 5,
346 },
347 [PM8607_IRQ_CHG_DONE] = {
348 .reg = PM8607_INT_STATUS3,
349 .mask_reg = PM8607_INT_MASK_3,
350 .offs = 1 << 6,
351 },
352 [PM8607_IRQ_CHG_FAULT] = {
353 .reg = PM8607_INT_STATUS3,
354 .mask_reg = PM8607_INT_MASK_3,
355 .offs = 1 << 7,
356 },
357};
358
Haojian Zhuang2afa62e2010-02-08 05:02:00 -0500359static irqreturn_t pm860x_irq(int irq, void *data)
Haojian Zhuang5c42e8c2009-12-15 16:01:47 -0500360{
Haojian Zhuang5c42e8c2009-12-15 16:01:47 -0500361 struct pm860x_chip *chip = data;
Haojian Zhuang2afa62e2010-02-08 05:02:00 -0500362 struct pm860x_irq_data *irq_data;
363 struct i2c_client *i2c;
364 int read_reg = -1, value = 0;
365 int i;
Haojian Zhuang5c42e8c2009-12-15 16:01:47 -0500366
Haojian Zhuang2afa62e2010-02-08 05:02:00 -0500367 i2c = (chip->id == CHIP_PM8607) ? chip->client : chip->companion;
368 for (i = 0; i < ARRAY_SIZE(pm860x_irqs); i++) {
369 irq_data = &pm860x_irqs[i];
370 if (read_reg != irq_data->reg) {
371 read_reg = irq_data->reg;
372 value = pm860x_reg_read(i2c, irq_data->reg);
Haojian Zhuang5c42e8c2009-12-15 16:01:47 -0500373 }
Haojian Zhuang2afa62e2010-02-08 05:02:00 -0500374 if (value & irq_data->enable)
375 handle_nested_irq(chip->irq_base + i);
Haojian Zhuang5c42e8c2009-12-15 16:01:47 -0500376 }
Haojian Zhuang5c42e8c2009-12-15 16:01:47 -0500377 return IRQ_HANDLED;
378}
379
Mark Brown49f89d92010-12-11 12:31:31 +0000380static void pm860x_irq_lock(struct irq_data *data)
Haojian Zhuang5c42e8c2009-12-15 16:01:47 -0500381{
Mark Brown49f89d92010-12-11 12:31:31 +0000382 struct pm860x_chip *chip = irq_data_get_irq_chip_data(data);
Haojian Zhuang5c42e8c2009-12-15 16:01:47 -0500383
384 mutex_lock(&chip->irq_lock);
Haojian Zhuang5c42e8c2009-12-15 16:01:47 -0500385}
Haojian Zhuang5c42e8c2009-12-15 16:01:47 -0500386
Mark Brown49f89d92010-12-11 12:31:31 +0000387static void pm860x_irq_sync_unlock(struct irq_data *data)
Haojian Zhuang5c42e8c2009-12-15 16:01:47 -0500388{
Mark Brown49f89d92010-12-11 12:31:31 +0000389 struct pm860x_chip *chip = irq_data_get_irq_chip_data(data);
Haojian Zhuang2afa62e2010-02-08 05:02:00 -0500390 struct pm860x_irq_data *irq_data;
391 struct i2c_client *i2c;
392 static unsigned char cached[3] = {0x0, 0x0, 0x0};
393 unsigned char mask[3];
394 int i;
Haojian Zhuang5c42e8c2009-12-15 16:01:47 -0500395
Haojian Zhuang2afa62e2010-02-08 05:02:00 -0500396 i2c = (chip->id == CHIP_PM8607) ? chip->client : chip->companion;
397 /* Load cached value. In initial, all IRQs are masked */
398 for (i = 0; i < 3; i++)
399 mask[i] = cached[i];
400 for (i = 0; i < ARRAY_SIZE(pm860x_irqs); i++) {
401 irq_data = &pm860x_irqs[i];
402 switch (irq_data->mask_reg) {
403 case PM8607_INT_MASK_1:
404 mask[0] &= ~irq_data->offs;
405 mask[0] |= irq_data->enable;
406 break;
407 case PM8607_INT_MASK_2:
408 mask[1] &= ~irq_data->offs;
409 mask[1] |= irq_data->enable;
410 break;
411 case PM8607_INT_MASK_3:
412 mask[2] &= ~irq_data->offs;
413 mask[2] |= irq_data->enable;
414 break;
415 default:
416 dev_err(chip->dev, "wrong IRQ\n");
417 break;
418 }
419 }
420 /* update mask into registers */
421 for (i = 0; i < 3; i++) {
422 if (mask[i] != cached[i]) {
423 cached[i] = mask[i];
424 pm860x_reg_write(i2c, PM8607_INT_MASK_1 + i, mask[i]);
425 }
426 }
427
Haojian Zhuang5c42e8c2009-12-15 16:01:47 -0500428 mutex_unlock(&chip->irq_lock);
Haojian Zhuang5c42e8c2009-12-15 16:01:47 -0500429}
Haojian Zhuang2afa62e2010-02-08 05:02:00 -0500430
Mark Brown49f89d92010-12-11 12:31:31 +0000431static void pm860x_irq_enable(struct irq_data *data)
Haojian Zhuang2afa62e2010-02-08 05:02:00 -0500432{
Mark Brown49f89d92010-12-11 12:31:31 +0000433 struct pm860x_chip *chip = irq_data_get_irq_chip_data(data);
434 pm860x_irqs[data->irq - chip->irq_base].enable
435 = pm860x_irqs[data->irq - chip->irq_base].offs;
Haojian Zhuang2afa62e2010-02-08 05:02:00 -0500436}
437
Mark Brown49f89d92010-12-11 12:31:31 +0000438static void pm860x_irq_disable(struct irq_data *data)
Haojian Zhuang2afa62e2010-02-08 05:02:00 -0500439{
Mark Brown49f89d92010-12-11 12:31:31 +0000440 struct pm860x_chip *chip = irq_data_get_irq_chip_data(data);
441 pm860x_irqs[data->irq - chip->irq_base].enable = 0;
Haojian Zhuang2afa62e2010-02-08 05:02:00 -0500442}
443
444static struct irq_chip pm860x_irq_chip = {
445 .name = "88pm860x",
Mark Brown49f89d92010-12-11 12:31:31 +0000446 .irq_bus_lock = pm860x_irq_lock,
447 .irq_bus_sync_unlock = pm860x_irq_sync_unlock,
448 .irq_enable = pm860x_irq_enable,
449 .irq_disable = pm860x_irq_disable,
Haojian Zhuang2afa62e2010-02-08 05:02:00 -0500450};
Haojian Zhuang5c42e8c2009-12-15 16:01:47 -0500451
Haojian Zhuanga16122b2009-12-15 16:04:36 -0500452static int __devinit device_gpadc_init(struct pm860x_chip *chip,
453 struct pm860x_platform_data *pdata)
454{
455 struct i2c_client *i2c = (chip->id == CHIP_PM8607) ? chip->client \
456 : chip->companion;
Dan Carpentereb6e8dd2010-05-27 00:54:09 +0200457 int data;
458 int ret;
Haojian Zhuanga16122b2009-12-15 16:04:36 -0500459
460 /* initialize GPADC without activating it */
461
Dan Carpentereb6e8dd2010-05-27 00:54:09 +0200462 if (!pdata || !pdata->touch)
463 return -EINVAL;
Haojian Zhuanga16122b2009-12-15 16:04:36 -0500464
Dan Carpentereb6e8dd2010-05-27 00:54:09 +0200465 /* set GPADC MISC1 register */
466 data = 0;
467 data |= (pdata->touch->gpadc_prebias << 1) & PM8607_GPADC_PREBIAS_MASK;
468 data |= (pdata->touch->slot_cycle << 3) & PM8607_GPADC_SLOT_CYCLE_MASK;
469 data |= (pdata->touch->off_scale << 5) & PM8607_GPADC_OFF_SCALE_MASK;
470 data |= (pdata->touch->sw_cal << 7) & PM8607_GPADC_SW_CAL_MASK;
471 if (data) {
472 ret = pm860x_reg_write(i2c, PM8607_GPADC_MISC1, data);
473 if (ret < 0)
474 goto out;
475 }
476 /* set tsi prebias time */
477 if (pdata->touch->tsi_prebias) {
478 data = pdata->touch->tsi_prebias;
479 ret = pm860x_reg_write(i2c, PM8607_TSI_PREBIAS, data);
480 if (ret < 0)
481 goto out;
482 }
483 /* set prebias & prechg time of pen detect */
484 data = 0;
485 data |= pdata->touch->pen_prebias & PM8607_PD_PREBIAS_MASK;
486 data |= (pdata->touch->pen_prechg << 5) & PM8607_PD_PRECHG_MASK;
487 if (data) {
488 ret = pm860x_reg_write(i2c, PM8607_PD_PREBIAS, data);
489 if (ret < 0)
490 goto out;
Haojian Zhuanga16122b2009-12-15 16:04:36 -0500491 }
492
Dan Carpentereb6e8dd2010-05-27 00:54:09 +0200493 ret = pm860x_set_bits(i2c, PM8607_GPADC_MISC1,
494 PM8607_GPADC_EN, PM8607_GPADC_EN);
Haojian Zhuanga16122b2009-12-15 16:04:36 -0500495out:
496 return ret;
497}
498
Haojian Zhuang5c42e8c2009-12-15 16:01:47 -0500499static int __devinit device_irq_init(struct pm860x_chip *chip,
500 struct pm860x_platform_data *pdata)
501{
502 struct i2c_client *i2c = (chip->id == CHIP_PM8607) ? chip->client \
503 : chip->companion;
504 unsigned char status_buf[INT_STATUS_NUM];
Haojian Zhuang2afa62e2010-02-08 05:02:00 -0500505 unsigned long flags = IRQF_TRIGGER_FALLING | IRQF_ONESHOT;
Haojian Zhuang2afa62e2010-02-08 05:02:00 -0500506 int i, data, mask, ret = -EINVAL;
507 int __irq;
Haojian Zhuang5c42e8c2009-12-15 16:01:47 -0500508
Haojian Zhuang2afa62e2010-02-08 05:02:00 -0500509 if (!pdata || !pdata->irq_base) {
510 dev_warn(chip->dev, "No interrupt support on IRQ base\n");
511 return -EINVAL;
512 }
Haojian Zhuang5c42e8c2009-12-15 16:01:47 -0500513
514 mask = PM8607_B0_MISC1_INV_INT | PM8607_B0_MISC1_INT_CLEAR
515 | PM8607_B0_MISC1_INT_MASK;
516 data = 0;
517 chip->irq_mode = 0;
518 if (pdata && pdata->irq_mode) {
519 /*
520 * irq_mode defines the way of clearing interrupt. If it's 1,
521 * clear IRQ by write. Otherwise, clear it by read.
522 * This control bit is valid from 88PM8607 B0 steping.
523 */
524 data |= PM8607_B0_MISC1_INT_CLEAR;
525 chip->irq_mode = 1;
526 }
527 ret = pm860x_set_bits(i2c, PM8607_B0_MISC1, mask, data);
528 if (ret < 0)
529 goto out;
530
531 /* mask all IRQs */
532 memset(status_buf, 0, INT_STATUS_NUM);
533 ret = pm860x_bulk_write(i2c, PM8607_INT_MASK_1,
534 INT_STATUS_NUM, status_buf);
535 if (ret < 0)
536 goto out;
537
538 if (chip->irq_mode) {
539 /* clear interrupt status by write */
540 memset(status_buf, 0xFF, INT_STATUS_NUM);
541 ret = pm860x_bulk_write(i2c, PM8607_INT_STATUS1,
542 INT_STATUS_NUM, status_buf);
543 } else {
544 /* clear interrupt status by read */
545 ret = pm860x_bulk_read(i2c, PM8607_INT_STATUS1,
546 INT_STATUS_NUM, status_buf);
547 }
548 if (ret < 0)
549 goto out;
550
Haojian Zhuang2afa62e2010-02-08 05:02:00 -0500551 mutex_init(&chip->irq_lock);
552 chip->irq_base = pdata->irq_base;
553 chip->core_irq = i2c->irq;
554 if (!chip->core_irq)
Haojian Zhuang5c42e8c2009-12-15 16:01:47 -0500555 goto out;
Haojian Zhuang2afa62e2010-02-08 05:02:00 -0500556
Haojian Zhuang2afa62e2010-02-08 05:02:00 -0500557 /* register IRQ by genirq */
558 for (i = 0; i < ARRAY_SIZE(pm860x_irqs); i++) {
559 __irq = i + chip->irq_base;
Thomas Gleixnerd5bb1222011-03-25 11:12:32 +0000560 irq_set_chip_data(__irq, chip);
561 irq_set_chip_and_handler(__irq, &pm860x_irq_chip,
Haojian Zhuang2afa62e2010-02-08 05:02:00 -0500562 handle_edge_irq);
Thomas Gleixnerd5bb1222011-03-25 11:12:32 +0000563 irq_set_nested_thread(__irq, 1);
Haojian Zhuang2afa62e2010-02-08 05:02:00 -0500564#ifdef CONFIG_ARM
565 set_irq_flags(__irq, IRQF_VALID);
566#else
Thomas Gleixnerd5bb1222011-03-25 11:12:32 +0000567 irq_set_noprobe(__irq);
Haojian Zhuang2afa62e2010-02-08 05:02:00 -0500568#endif
Haojian Zhuang5c42e8c2009-12-15 16:01:47 -0500569 }
Haojian Zhuang2afa62e2010-02-08 05:02:00 -0500570
571 ret = request_threaded_irq(chip->core_irq, NULL, pm860x_irq, flags,
572 "88pm860x", chip);
573 if (ret) {
574 dev_err(chip->dev, "Failed to request IRQ: %d\n", ret);
575 chip->core_irq = 0;
576 }
577
Haojian Zhuang5c42e8c2009-12-15 16:01:47 -0500578 return 0;
579out:
Haojian Zhuang2afa62e2010-02-08 05:02:00 -0500580 chip->core_irq = 0;
Haojian Zhuang5c42e8c2009-12-15 16:01:47 -0500581 return ret;
582}
583
Henrik Kretzschmar872c1b12010-03-26 02:40:13 +0100584static void device_irq_exit(struct pm860x_chip *chip)
Haojian Zhuang5c42e8c2009-12-15 16:01:47 -0500585{
Haojian Zhuang2afa62e2010-02-08 05:02:00 -0500586 if (chip->core_irq)
587 free_irq(chip->core_irq, chip);
Haojian Zhuang5c42e8c2009-12-15 16:01:47 -0500588}
589
Jett.Zhou23de4352012-03-01 11:59:19 +0100590int pm8606_osc_enable(struct pm860x_chip *chip, unsigned short client)
591{
592 int ret = -EIO;
593 struct i2c_client *i2c = (chip->id == CHIP_PM8606) ?
594 chip->client : chip->companion;
595
596 dev_dbg(chip->dev, "%s(B): client=0x%x\n", __func__, client);
597 dev_dbg(chip->dev, "%s(B): vote=0x%x status=%d\n",
598 __func__, chip->osc_vote,
599 chip->osc_status);
600
601 mutex_lock(&chip->osc_lock);
602 /* Update voting status */
603 chip->osc_vote |= client;
604 /* If reference group is off - turn on*/
605 if (chip->osc_status != PM8606_REF_GP_OSC_ON) {
606 chip->osc_status = PM8606_REF_GP_OSC_UNKNOWN;
607 /* Enable Reference group Vsys */
608 if (pm860x_set_bits(i2c, PM8606_VSYS,
609 PM8606_VSYS_EN, PM8606_VSYS_EN))
610 goto out;
611
612 /*Enable Internal Oscillator */
613 if (pm860x_set_bits(i2c, PM8606_MISC,
614 PM8606_MISC_OSC_EN, PM8606_MISC_OSC_EN))
615 goto out;
616 /* Update status (only if writes succeed) */
617 chip->osc_status = PM8606_REF_GP_OSC_ON;
618 }
619 mutex_unlock(&chip->osc_lock);
620
621 dev_dbg(chip->dev, "%s(A): vote=0x%x status=%d ret=%d\n",
622 __func__, chip->osc_vote,
623 chip->osc_status, ret);
624 return 0;
625out:
626 mutex_unlock(&chip->osc_lock);
627 return ret;
628}
Samuel Ortiz2f5f89b2012-03-06 08:06:14 +0100629EXPORT_SYMBOL(pm8606_osc_enable);
Jett.Zhou23de4352012-03-01 11:59:19 +0100630
631int pm8606_osc_disable(struct pm860x_chip *chip, unsigned short client)
632{
633 int ret = -EIO;
634 struct i2c_client *i2c = (chip->id == CHIP_PM8606) ?
635 chip->client : chip->companion;
636
637 dev_dbg(chip->dev, "%s(B): client=0x%x\n", __func__, client);
638 dev_dbg(chip->dev, "%s(B): vote=0x%x status=%d\n",
639 __func__, chip->osc_vote,
640 chip->osc_status);
641
642 mutex_lock(&chip->osc_lock);
643 /*Update voting status */
644 chip->osc_vote &= ~(client);
645 /* If reference group is off and this is the last client to release
646 * - turn off */
647 if ((chip->osc_status != PM8606_REF_GP_OSC_OFF) &&
648 (chip->osc_vote == REF_GP_NO_CLIENTS)) {
649 chip->osc_status = PM8606_REF_GP_OSC_UNKNOWN;
650 /* Disable Reference group Vsys */
651 if (pm860x_set_bits(i2c, PM8606_VSYS, PM8606_VSYS_EN, 0))
652 goto out;
653 /* Disable Internal Oscillator */
654 if (pm860x_set_bits(i2c, PM8606_MISC, PM8606_MISC_OSC_EN, 0))
655 goto out;
656 chip->osc_status = PM8606_REF_GP_OSC_OFF;
657 }
658 mutex_unlock(&chip->osc_lock);
659
660 dev_dbg(chip->dev, "%s(A): vote=0x%x status=%d ret=%d\n",
661 __func__, chip->osc_vote,
662 chip->osc_status, ret);
663 return 0;
664out:
665 mutex_unlock(&chip->osc_lock);
666 return ret;
667}
Samuel Ortiz2f5f89b2012-03-06 08:06:14 +0100668EXPORT_SYMBOL(pm8606_osc_disable);
Jett.Zhou23de4352012-03-01 11:59:19 +0100669
670static void __devinit device_osc_init(struct i2c_client *i2c)
671{
672 struct pm860x_chip *chip = i2c_get_clientdata(i2c);
673
674 mutex_init(&chip->osc_lock);
675 /* init portofino reference group voting and status */
676 /* Disable Reference group Vsys */
677 pm860x_set_bits(i2c, PM8606_VSYS, PM8606_VSYS_EN, 0);
678 /* Disable Internal Oscillator */
679 pm860x_set_bits(i2c, PM8606_MISC, PM8606_MISC_OSC_EN, 0);
680
681 chip->osc_vote = REF_GP_NO_CLIENTS;
682 chip->osc_status = PM8606_REF_GP_OSC_OFF;
683}
684
Haojian Zhuangadb70482011-03-07 23:43:09 +0800685static void __devinit device_bk_init(struct pm860x_chip *chip,
Haojian Zhuangadb70482011-03-07 23:43:09 +0800686 struct pm860x_platform_data *pdata)
687{
Haojian Zhuanga6ccdcd2012-08-08 23:17:26 +0800688 int ret, i;
Haojian Zhuangadb70482011-03-07 23:43:09 +0800689
Haojian Zhuanga6ccdcd2012-08-08 23:17:26 +0800690 if (pdata && pdata->backlight) {
691 if (pdata->num_backlights > ARRAY_SIZE(bk_devs))
692 pdata->num_backlights = ARRAY_SIZE(bk_devs);
693 for (i = 0; i < pdata->num_backlights; i++) {
694 bk_devs[i].platform_data = &pdata->backlight[i];
695 bk_devs[i].pdata_size =
696 sizeof(struct pm860x_backlight_pdata);
Haojian Zhuangadb70482011-03-07 23:43:09 +0800697 }
698 }
Haojian Zhuanga6ccdcd2012-08-08 23:17:26 +0800699 ret = mfd_add_devices(chip->dev, 0, bk_devs,
700 ARRAY_SIZE(bk_devs), NULL, 0);
701 if (ret < 0)
702 dev_err(chip->dev, "Failed to add backlight subdev\n");
Haojian Zhuangadb70482011-03-07 23:43:09 +0800703}
704
Haojian Zhuang3154c342011-03-07 23:43:10 +0800705static void __devinit device_led_init(struct pm860x_chip *chip,
Haojian Zhuang3154c342011-03-07 23:43:10 +0800706 struct pm860x_platform_data *pdata)
Haojian Zhuang53dbab72010-01-08 06:01:24 -0500707{
Haojian Zhuang894fc8f2012-08-08 23:17:27 +0800708 int ret, i;
Haojian Zhuanga16122b2009-12-15 16:04:36 -0500709
Haojian Zhuang894fc8f2012-08-08 23:17:27 +0800710 if (pdata && pdata->led) {
711 if (pdata->num_leds > ARRAY_SIZE(led_devs))
712 pdata->num_leds = ARRAY_SIZE(led_devs);
713 for (i = 0; i < pdata->num_leds; i++) {
714 led_devs[i].platform_data = &pdata->led[i];
715 led_devs[i].pdata_size =
716 sizeof(struct pm860x_led_pdata);
Haojian Zhuanga16122b2009-12-15 16:04:36 -0500717 }
718 }
Haojian Zhuang894fc8f2012-08-08 23:17:27 +0800719 ret = mfd_add_devices(chip->dev, 0, led_devs,
720 ARRAY_SIZE(led_devs), NULL, 0);
721 if (ret < 0) {
722 dev_err(chip->dev, "Failed to add led subdev\n");
723 return;
724 }
Haojian Zhuang53dbab72010-01-08 06:01:24 -0500725}
726
Haojian Zhuang22aad002011-03-07 23:43:11 +0800727static void __devinit device_regulator_init(struct pm860x_chip *chip,
Haojian Zhuang22aad002011-03-07 23:43:11 +0800728 struct pm860x_platform_data *pdata)
729{
730 struct regulator_init_data *initdata;
731 int ret;
Haojian Zhuang586e1a12011-05-06 17:21:23 +0800732 int i, seq;
Haojian Zhuang22aad002011-03-07 23:43:11 +0800733
734 if ((pdata == NULL) || (pdata->regulator == NULL))
735 return;
736
737 if (pdata->num_regulators > ARRAY_SIZE(regulator_devs))
738 pdata->num_regulators = ARRAY_SIZE(regulator_devs);
739
Haojian Zhuang586e1a12011-05-06 17:21:23 +0800740 for (i = 0, seq = -1; i < pdata->num_regulators; i++) {
Haojian Zhuang22aad002011-03-07 23:43:11 +0800741 initdata = &pdata->regulator[i];
Haojian Zhuang586e1a12011-05-06 17:21:23 +0800742 seq = *(unsigned int *)initdata->driver_data;
743 if ((seq < 0) || (seq > PM8607_ID_RG_MAX)) {
744 dev_err(chip->dev, "Wrong ID(%d) on regulator(%s)\n",
745 seq, initdata->constraints.name);
Haojian Zhuang22aad002011-03-07 23:43:11 +0800746 goto out;
747 }
Haojian Zhuangf5fb7582011-05-06 17:21:21 +0800748 regulator_devs[i].platform_data = &pdata->regulator[i];
749 regulator_devs[i].pdata_size = sizeof(struct regulator_init_data);
Haojian Zhuang22aad002011-03-07 23:43:11 +0800750 regulator_devs[i].num_resources = 1;
Haojian Zhuang586e1a12011-05-06 17:21:23 +0800751 regulator_devs[i].resources = &regulator_resources[seq];
Haojian Zhuang22aad002011-03-07 23:43:11 +0800752
753 ret = mfd_add_devices(chip->dev, 0, &regulator_devs[i], 1,
Haojian Zhuang586e1a12011-05-06 17:21:23 +0800754 &regulator_resources[seq], 0);
Haojian Zhuang22aad002011-03-07 23:43:11 +0800755 if (ret < 0) {
756 dev_err(chip->dev, "Failed to add regulator subdev\n");
757 goto out;
758 }
759 }
760out:
761 return;
762}
763
Haojian Zhuang008b3042011-05-06 17:21:20 +0800764static void __devinit device_rtc_init(struct pm860x_chip *chip,
Haojian Zhuang008b3042011-05-06 17:21:20 +0800765 struct pm860x_platform_data *pdata)
766{
767 int ret;
768
769 if ((pdata == NULL))
770 return;
771
772 rtc_devs[0].platform_data = pdata->rtc;
773 rtc_devs[0].pdata_size = sizeof(struct pm860x_rtc_pdata);
774 rtc_devs[0].num_resources = ARRAY_SIZE(rtc_resources);
775 rtc_devs[0].resources = &rtc_resources[0];
776 ret = mfd_add_devices(chip->dev, 0, &rtc_devs[0],
777 ARRAY_SIZE(rtc_devs), &rtc_resources[0],
778 chip->irq_base);
779 if (ret < 0)
780 dev_err(chip->dev, "Failed to add rtc subdev\n");
781}
782
Haojian Zhuangc9f560b2011-03-07 23:43:12 +0800783static void __devinit device_touch_init(struct pm860x_chip *chip,
Haojian Zhuangc9f560b2011-03-07 23:43:12 +0800784 struct pm860x_platform_data *pdata)
785{
786 int ret;
787
Haojian Zhuangf5fb7582011-05-06 17:21:21 +0800788 if (pdata == NULL)
Haojian Zhuangc9f560b2011-03-07 23:43:12 +0800789 return;
790
Haojian Zhuangf5fb7582011-05-06 17:21:21 +0800791 touch_devs[0].platform_data = pdata->touch;
792 touch_devs[0].pdata_size = sizeof(struct pm860x_touch_pdata);
Haojian Zhuangc9f560b2011-03-07 23:43:12 +0800793 touch_devs[0].num_resources = ARRAY_SIZE(touch_resources);
794 touch_devs[0].resources = &touch_resources[0];
795 ret = mfd_add_devices(chip->dev, 0, &touch_devs[0],
796 ARRAY_SIZE(touch_devs), &touch_resources[0],
797 chip->irq_base);
798 if (ret < 0)
799 dev_err(chip->dev, "Failed to add touch subdev\n");
800}
801
802static void __devinit device_power_init(struct pm860x_chip *chip,
Haojian Zhuangc9f560b2011-03-07 23:43:12 +0800803 struct pm860x_platform_data *pdata)
804{
805 int ret;
806
Haojian Zhuangf5fb7582011-05-06 17:21:21 +0800807 if (pdata == NULL)
Haojian Zhuangc9f560b2011-03-07 23:43:12 +0800808 return;
809
Haojian Zhuangf5fb7582011-05-06 17:21:21 +0800810 power_devs[0].platform_data = pdata->power;
811 power_devs[0].pdata_size = sizeof(struct pm860x_power_pdata);
Haojian Zhuangc9f560b2011-03-07 23:43:12 +0800812 power_devs[0].num_resources = ARRAY_SIZE(battery_resources);
813 power_devs[0].resources = &battery_resources[0],
814 ret = mfd_add_devices(chip->dev, 0, &power_devs[0], 1,
815 &battery_resources[0], chip->irq_base);
816 if (ret < 0)
817 dev_err(chip->dev, "Failed to add battery subdev\n");
818
Haojian Zhuangf5fb7582011-05-06 17:21:21 +0800819 power_devs[1].platform_data = pdata->power;
820 power_devs[1].pdata_size = sizeof(struct pm860x_power_pdata);
Haojian Zhuangc9f560b2011-03-07 23:43:12 +0800821 power_devs[1].num_resources = ARRAY_SIZE(charger_resources);
822 power_devs[1].resources = &charger_resources[0],
823 ret = mfd_add_devices(chip->dev, 0, &power_devs[1], 1,
824 &charger_resources[0], chip->irq_base);
825 if (ret < 0)
826 dev_err(chip->dev, "Failed to add charger subdev\n");
Jett.Zhou2573f6d2012-07-06 10:59:58 +0800827
828 power_devs[2].platform_data = &preg_init_data;
829 power_devs[2].pdata_size = sizeof(struct regulator_init_data);
830 power_devs[2].num_resources = ARRAY_SIZE(preg_resources);
831 power_devs[2].resources = &preg_resources[0],
832 ret = mfd_add_devices(chip->dev, 0, &power_devs[2], 1,
833 &preg_resources[0], chip->irq_base);
834 if (ret < 0)
835 dev_err(chip->dev, "Failed to add preg subdev\n");
Haojian Zhuangc9f560b2011-03-07 23:43:12 +0800836}
837
838static void __devinit device_onkey_init(struct pm860x_chip *chip,
Haojian Zhuangc9f560b2011-03-07 23:43:12 +0800839 struct pm860x_platform_data *pdata)
840{
841 int ret;
842
843 onkey_devs[0].num_resources = ARRAY_SIZE(onkey_resources);
844 onkey_devs[0].resources = &onkey_resources[0],
845 ret = mfd_add_devices(chip->dev, 0, &onkey_devs[0],
846 ARRAY_SIZE(onkey_devs), &onkey_resources[0],
847 chip->irq_base);
848 if (ret < 0)
849 dev_err(chip->dev, "Failed to add onkey subdev\n");
850}
851
852static void __devinit device_codec_init(struct pm860x_chip *chip,
Haojian Zhuangc9f560b2011-03-07 23:43:12 +0800853 struct pm860x_platform_data *pdata)
854{
855 int ret;
856
857 codec_devs[0].num_resources = ARRAY_SIZE(codec_resources);
858 codec_devs[0].resources = &codec_resources[0],
859 ret = mfd_add_devices(chip->dev, 0, &codec_devs[0],
860 ARRAY_SIZE(codec_devs), &codec_resources[0], 0);
861 if (ret < 0)
862 dev_err(chip->dev, "Failed to add codec subdev\n");
863}
864
Haojian Zhuang5c42e8c2009-12-15 16:01:47 -0500865static void __devinit device_8607_init(struct pm860x_chip *chip,
866 struct i2c_client *i2c,
867 struct pm860x_platform_data *pdata)
Haojian Zhuangbbd51b12010-01-06 17:04:18 -0500868{
Haojian Zhuanga16122b2009-12-15 16:04:36 -0500869 int data, ret;
Haojian Zhuangbbd51b12010-01-06 17:04:18 -0500870
Haojian Zhuang53dbab72010-01-08 06:01:24 -0500871 ret = pm860x_reg_read(i2c, PM8607_CHIP_ID);
Haojian Zhuangbbd51b12010-01-06 17:04:18 -0500872 if (ret < 0) {
873 dev_err(chip->dev, "Failed to read CHIP ID: %d\n", ret);
874 goto out;
875 }
Haojian Zhuang38b340522010-09-08 09:44:34 -0400876 switch (ret & PM8607_VERSION_MASK) {
877 case 0x40:
878 case 0x50:
Haojian Zhuangbbd51b12010-01-06 17:04:18 -0500879 dev_info(chip->dev, "Marvell 88PM8607 (ID: %02x) detected\n",
880 ret);
Haojian Zhuang38b340522010-09-08 09:44:34 -0400881 break;
882 default:
Haojian Zhuangbbd51b12010-01-06 17:04:18 -0500883 dev_err(chip->dev, "Failed to detect Marvell 88PM8607. "
884 "Chip ID: %02x\n", ret);
885 goto out;
886 }
Haojian Zhuangbbd51b12010-01-06 17:04:18 -0500887
Haojian Zhuang53dbab72010-01-08 06:01:24 -0500888 ret = pm860x_reg_read(i2c, PM8607_BUCK3);
Haojian Zhuangbbd51b12010-01-06 17:04:18 -0500889 if (ret < 0) {
890 dev_err(chip->dev, "Failed to read BUCK3 register: %d\n", ret);
891 goto out;
892 }
893 if (ret & PM8607_BUCK3_DOUBLE)
894 chip->buck3_double = 1;
895
Haojian Zhuang5c42e8c2009-12-15 16:01:47 -0500896 ret = pm860x_reg_read(i2c, PM8607_B0_MISC1);
Haojian Zhuangbbd51b12010-01-06 17:04:18 -0500897 if (ret < 0) {
898 dev_err(chip->dev, "Failed to read MISC1 register: %d\n", ret);
899 goto out;
900 }
Haojian Zhuangbbd51b12010-01-06 17:04:18 -0500901
Haojian Zhuang5c42e8c2009-12-15 16:01:47 -0500902 if (pdata && (pdata->i2c_port == PI2C_PORT))
903 data = PM8607_B0_MISC1_PI2C;
904 else
905 data = 0;
906 ret = pm860x_set_bits(i2c, PM8607_B0_MISC1, PM8607_B0_MISC1_PI2C, data);
907 if (ret < 0) {
908 dev_err(chip->dev, "Failed to access MISC1:%d\n", ret);
909 goto out;
910 }
911
Haojian Zhuanga16122b2009-12-15 16:04:36 -0500912 ret = device_gpadc_init(chip, pdata);
913 if (ret < 0)
914 goto out;
915
Haojian Zhuang5c42e8c2009-12-15 16:01:47 -0500916 ret = device_irq_init(chip, pdata);
917 if (ret < 0)
918 goto out;
919
Haojian Zhuangcea438d2011-05-06 17:21:24 +0800920 device_regulator_init(chip, pdata);
921 device_rtc_init(chip, pdata);
922 device_onkey_init(chip, pdata);
923 device_touch_init(chip, pdata);
924 device_power_init(chip, pdata);
925 device_codec_init(chip, pdata);
Haojian Zhuangbbd51b12010-01-06 17:04:18 -0500926out:
Haojian Zhuang53dbab72010-01-08 06:01:24 -0500927 return;
Haojian Zhuangbbd51b12010-01-06 17:04:18 -0500928}
929
Jett.Zhou78258062012-02-28 10:58:42 +0800930static void __devinit device_8606_init(struct pm860x_chip *chip,
931 struct i2c_client *i2c,
932 struct pm860x_platform_data *pdata)
933{
934 device_osc_init(i2c);
935 device_bk_init(chip, pdata);
936 device_led_init(chip, pdata);
937}
938
Henrik Kretzschmar872c1b12010-03-26 02:40:13 +0100939int __devinit pm860x_device_init(struct pm860x_chip *chip,
Haojian Zhuang53dbab72010-01-08 06:01:24 -0500940 struct pm860x_platform_data *pdata)
941{
Haojian Zhuang2afa62e2010-02-08 05:02:00 -0500942 chip->core_irq = 0;
Haojian Zhuang5c42e8c2009-12-15 16:01:47 -0500943
Haojian Zhuang53dbab72010-01-08 06:01:24 -0500944 switch (chip->id) {
945 case CHIP_PM8606:
Jett.Zhou78258062012-02-28 10:58:42 +0800946 device_8606_init(chip, chip->client, pdata);
Haojian Zhuang53dbab72010-01-08 06:01:24 -0500947 break;
948 case CHIP_PM8607:
949 device_8607_init(chip, chip->client, pdata);
950 break;
951 }
952
953 if (chip->companion) {
954 switch (chip->id) {
955 case CHIP_PM8607:
Jett.Zhou78258062012-02-28 10:58:42 +0800956 device_8606_init(chip, chip->companion, pdata);
Haojian Zhuang53dbab72010-01-08 06:01:24 -0500957 break;
958 case CHIP_PM8606:
959 device_8607_init(chip, chip->companion, pdata);
960 break;
961 }
962 }
Haojian Zhuang5c42e8c2009-12-15 16:01:47 -0500963
Haojian Zhuang53dbab72010-01-08 06:01:24 -0500964 return 0;
965}
966
Henrik Kretzschmar872c1b12010-03-26 02:40:13 +0100967void __devexit pm860x_device_exit(struct pm860x_chip *chip)
Haojian Zhuangbbd51b12010-01-06 17:04:18 -0500968{
Haojian Zhuang5c42e8c2009-12-15 16:01:47 -0500969 device_irq_exit(chip);
Haojian Zhuangbbd51b12010-01-06 17:04:18 -0500970 mfd_remove_devices(chip->dev);
971}
972
Haojian Zhuang53dbab72010-01-08 06:01:24 -0500973MODULE_DESCRIPTION("PMIC Driver for Marvell 88PM860x");
Haojian Zhuangbbd51b12010-01-06 17:04:18 -0500974MODULE_AUTHOR("Haojian Zhuang <haojian.zhuang@marvell.com>");
975MODULE_LICENSE("GPL");