mfd: 88pm860x: Avoid to check resource for preg regulator
[deliverable/linux.git] / drivers / mfd / 88pm860x-core.c
CommitLineData
bbd51b1f
HZ
1/*
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>
5c42e8c4 14#include <linux/i2c.h>
2afa62ea 15#include <linux/irq.h>
bbd51b1f
HZ
16#include <linux/interrupt.h>
17#include <linux/platform_device.h>
18#include <linux/mfd/core.h>
53dbab7a 19#include <linux/mfd/88pm860x.h>
22aad001 20#include <linux/regulator/machine.h>
bbd51b1f 21
2afa62ea
HZ
22#define INT_STATUS_NUM 3
23
a6ccdcd9
HZ
24static 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, },
a16122bc 38};
adb70483 39
894fc8f2
HZ
40static 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, },
3154c344
HZ
69};
70
a70abacb
HZ
71static struct resource buck1_resources[] __devinitdata = {
72 {0x24, 0x24, "buck set", IORESOURCE_REG, },
73};
74static struct resource buck2_resources[] __devinitdata = {
75 {0x25, 0x25, "buck set", IORESOURCE_REG, },
76};
77static struct resource buck3_resources[] __devinitdata = {
78 {0x26, 0x26, "buck set", IORESOURCE_REG, },
79};
80static struct resource ldo1_resources[] __devinitdata = {
81 {0x10, 0x10, "ldo set", IORESOURCE_REG, },
82};
83static struct resource ldo2_resources[] __devinitdata = {
84 {0x11, 0x11, "ldo set", IORESOURCE_REG, },
85};
86static struct resource ldo3_resources[] __devinitdata = {
87 {0x12, 0x12, "ldo set", IORESOURCE_REG, },
88};
89static struct resource ldo4_resources[] __devinitdata = {
90 {0x13, 0x13, "ldo set", IORESOURCE_REG, },
91};
92static struct resource ldo5_resources[] __devinitdata = {
93 {0x14, 0x14, "ldo set", IORESOURCE_REG, },
94};
95static struct resource ldo6_resources[] __devinitdata = {
96 {0x15, 0x15, "ldo set", IORESOURCE_REG, },
97};
98static struct resource ldo7_resources[] __devinitdata = {
99 {0x16, 0x16, "ldo set", IORESOURCE_REG, },
100};
101static struct resource ldo8_resources[] __devinitdata = {
102 {0x17, 0x17, "ldo set", IORESOURCE_REG, },
103};
104static struct resource ldo9_resources[] __devinitdata = {
105 {0x18, 0x18, "ldo set", IORESOURCE_REG, },
106};
107static struct resource ldo10_resources[] __devinitdata = {
108 {0x19, 0x19, "ldo set", IORESOURCE_REG, },
109};
110static struct resource ldo12_resources[] __devinitdata = {
111 {0x1a, 0x1a, "ldo set", IORESOURCE_REG, },
112};
113static struct resource ldo_vibrator_resources[] __devinitdata = {
114 {0x28, 0x28, "ldo set", IORESOURCE_REG, },
115};
116static struct resource ldo14_resources[] __devinitdata = {
117 {0x1b, 0x1b, "ldo set", IORESOURCE_REG, },
22aad001
HZ
118};
119
a5156f1a 120static struct resource touch_resources[] __devinitdata = {
c9f560b3
HZ
121 {PM8607_IRQ_PEN, PM8607_IRQ_PEN, "touch", IORESOURCE_IRQ,},
122};
123
a5156f1a 124static struct resource onkey_resources[] __devinitdata = {
c9f560b3
HZ
125 {PM8607_IRQ_ONKEY, PM8607_IRQ_ONKEY, "onkey", IORESOURCE_IRQ,},
126};
127
a5156f1a 128static struct resource codec_resources[] __devinitdata = {
c9f560b3
HZ
129 /* Headset microphone insertion or removal */
130 {PM8607_IRQ_MICIN, PM8607_IRQ_MICIN, "micin", IORESOURCE_IRQ,},
131 /* Hook-switch press or release */
132 {PM8607_IRQ_HOOK, PM8607_IRQ_HOOK, "hook", IORESOURCE_IRQ,},
133 /* Headset insertion or removal */
134 {PM8607_IRQ_HEADSET, PM8607_IRQ_HEADSET, "headset", IORESOURCE_IRQ,},
135 /* Audio short */
136 {PM8607_IRQ_AUDIO_SHORT, PM8607_IRQ_AUDIO_SHORT, "audio-short", IORESOURCE_IRQ,},
137};
138
a5156f1a 139static struct resource battery_resources[] __devinitdata = {
c9f560b3
HZ
140 {PM8607_IRQ_CC, PM8607_IRQ_CC, "columb counter", IORESOURCE_IRQ,},
141 {PM8607_IRQ_BAT, PM8607_IRQ_BAT, "battery", IORESOURCE_IRQ,},
142};
143
a5156f1a 144static struct resource charger_resources[] __devinitdata = {
c9f560b3
HZ
145 {PM8607_IRQ_CHG, PM8607_IRQ_CHG, "charger detect", IORESOURCE_IRQ,},
146 {PM8607_IRQ_CHG_DONE, PM8607_IRQ_CHG_DONE, "charging done", IORESOURCE_IRQ,},
147 {PM8607_IRQ_CHG_FAULT, PM8607_IRQ_CHG_FAULT, "charging timeout", IORESOURCE_IRQ,},
148 {PM8607_IRQ_GPADC1, PM8607_IRQ_GPADC1, "battery temperature", IORESOURCE_IRQ,},
149 {PM8607_IRQ_VBAT, PM8607_IRQ_VBAT, "battery voltage", IORESOURCE_IRQ,},
150 {PM8607_IRQ_VCHG, PM8607_IRQ_VCHG, "vchg voltage", IORESOURCE_IRQ,},
151};
152
008b3040 153static struct resource rtc_resources[] __devinitdata = {
02367029 154 {PM8607_IRQ_RTC, PM8607_IRQ_RTC, "rtc", IORESOURCE_IRQ,},
008b3040
HZ
155};
156
50b381b7 157static struct mfd_cell bk_devs[] __devinitdata = {
a6ccdcd9
HZ
158 {
159 .name = "88pm860x-backlight",
160 .id = 0,
161 .num_resources = ARRAY_SIZE(bk0_resources),
162 .resources = bk0_resources,
163 }, {
164 .name = "88pm860x-backlight",
165 .id = 1,
166 .num_resources = ARRAY_SIZE(bk1_resources),
167 .resources = bk1_resources,
168 }, {
169 .name = "88pm860x-backlight",
170 .id = 2,
171 .num_resources = ARRAY_SIZE(bk2_resources),
172 .resources = bk2_resources,
173 },
adb70483
HZ
174};
175
50b381b7 176static struct mfd_cell led_devs[] __devinitdata = {
894fc8f2
HZ
177 {
178 .name = "88pm860x-led",
179 .id = 0,
180 .num_resources = ARRAY_SIZE(led0_resources),
181 .resources = led0_resources,
182 }, {
183 .name = "88pm860x-led",
184 .id = 1,
185 .num_resources = ARRAY_SIZE(led1_resources),
186 .resources = led1_resources,
187 }, {
188 .name = "88pm860x-led",
189 .id = 2,
190 .num_resources = ARRAY_SIZE(led2_resources),
191 .resources = led2_resources,
192 }, {
193 .name = "88pm860x-led",
194 .id = 3,
195 .num_resources = ARRAY_SIZE(led3_resources),
196 .resources = led3_resources,
197 }, {
198 .name = "88pm860x-led",
199 .id = 4,
200 .num_resources = ARRAY_SIZE(led4_resources),
201 .resources = led4_resources,
202 }, {
203 .name = "88pm860x-led",
204 .id = 5,
205 .num_resources = ARRAY_SIZE(led5_resources),
206 .resources = led5_resources,
207 },
a16122bc
HZ
208};
209
50b381b7 210static struct mfd_cell reg_devs[] __devinitdata = {
a70abacb
HZ
211 {
212 .name = "88pm860x-regulator",
213 .id = 0,
214 .num_resources = ARRAY_SIZE(buck1_resources),
215 .resources = buck1_resources,
216 }, {
217 .name = "88pm860x-regulator",
218 .id = 1,
219 .num_resources = ARRAY_SIZE(buck2_resources),
220 .resources = buck2_resources,
221 }, {
222 .name = "88pm860x-regulator",
223 .id = 2,
224 .num_resources = ARRAY_SIZE(buck3_resources),
225 .resources = buck3_resources,
226 }, {
227 .name = "88pm860x-regulator",
228 .id = 3,
229 .num_resources = ARRAY_SIZE(ldo1_resources),
230 .resources = ldo1_resources,
231 }, {
232 .name = "88pm860x-regulator",
233 .id = 4,
234 .num_resources = ARRAY_SIZE(ldo2_resources),
235 .resources = ldo2_resources,
236 }, {
237 .name = "88pm860x-regulator",
238 .id = 5,
239 .num_resources = ARRAY_SIZE(ldo3_resources),
240 .resources = ldo3_resources,
241 }, {
242 .name = "88pm860x-regulator",
243 .id = 6,
244 .num_resources = ARRAY_SIZE(ldo4_resources),
245 .resources = ldo4_resources,
246 }, {
247 .name = "88pm860x-regulator",
248 .id = 7,
249 .num_resources = ARRAY_SIZE(ldo5_resources),
250 .resources = ldo5_resources,
251 }, {
252 .name = "88pm860x-regulator",
253 .id = 8,
254 .num_resources = ARRAY_SIZE(ldo6_resources),
255 .resources = ldo6_resources,
256 }, {
257 .name = "88pm860x-regulator",
258 .id = 9,
259 .num_resources = ARRAY_SIZE(ldo7_resources),
260 .resources = ldo7_resources,
261 }, {
262 .name = "88pm860x-regulator",
263 .id = 10,
264 .num_resources = ARRAY_SIZE(ldo8_resources),
265 .resources = ldo8_resources,
266 }, {
267 .name = "88pm860x-regulator",
268 .id = 11,
269 .num_resources = ARRAY_SIZE(ldo9_resources),
270 .resources = ldo9_resources,
271 }, {
272 .name = "88pm860x-regulator",
273 .id = 12,
274 .num_resources = ARRAY_SIZE(ldo10_resources),
275 .resources = ldo10_resources,
276 }, {
277 .name = "88pm860x-regulator",
278 .id = 13,
279 .num_resources = ARRAY_SIZE(ldo12_resources),
280 .resources = ldo12_resources,
281 }, {
282 .name = "88pm860x-regulator",
283 .id = 14,
284 .num_resources = ARRAY_SIZE(ldo_vibrator_resources),
285 .resources = ldo_vibrator_resources,
286 }, {
287 .name = "88pm860x-regulator",
288 .id = 15,
289 .num_resources = ARRAY_SIZE(ldo14_resources),
290 .resources = ldo14_resources,
291 },
22aad001
HZ
292};
293
a5156f1a 294static struct mfd_cell touch_devs[] = {
c9f560b3 295 {"88pm860x-touch", -1,},
a16122bc
HZ
296};
297
a5156f1a 298static struct mfd_cell onkey_devs[] = {
c9f560b3 299 {"88pm860x-onkey", -1,},
a16122bc 300};
bbd51b1f 301
a5156f1a 302static struct mfd_cell codec_devs[] = {
c9f560b3 303 {"88pm860x-codec", -1,},
2afa62ea
HZ
304};
305
2573f6d3
JZ
306static struct regulator_consumer_supply preg_supply[] = {
307 REGULATOR_SUPPLY("preg", "charger-manager"),
308};
309
310static struct regulator_init_data preg_init_data = {
311 .num_consumer_supplies = ARRAY_SIZE(preg_supply),
312 .consumer_supplies = &preg_supply[0],
313};
314
2afa62ea 315static struct mfd_cell power_devs[] = {
c9f560b3
HZ
316 {"88pm860x-battery", -1,},
317 {"88pm860x-charger", -1,},
2573f6d3 318 {"88pm860x-preg", -1,},
2c36af7b
HZ
319};
320
008b3040
HZ
321static struct mfd_cell rtc_devs[] = {
322 {"88pm860x-rtc", -1,},
323};
324
2c36af7b 325
2afa62ea
HZ
326struct pm860x_irq_data {
327 int reg;
328 int mask_reg;
329 int enable; /* enable or not */
330 int offs; /* bit offset in mask register */
331};
5c42e8c4 332
2afa62ea
HZ
333static struct pm860x_irq_data pm860x_irqs[] = {
334 [PM8607_IRQ_ONKEY] = {
335 .reg = PM8607_INT_STATUS1,
336 .mask_reg = PM8607_INT_MASK_1,
337 .offs = 1 << 0,
338 },
339 [PM8607_IRQ_EXTON] = {
340 .reg = PM8607_INT_STATUS1,
341 .mask_reg = PM8607_INT_MASK_1,
342 .offs = 1 << 1,
343 },
344 [PM8607_IRQ_CHG] = {
345 .reg = PM8607_INT_STATUS1,
346 .mask_reg = PM8607_INT_MASK_1,
347 .offs = 1 << 2,
348 },
349 [PM8607_IRQ_BAT] = {
350 .reg = PM8607_INT_STATUS1,
351 .mask_reg = PM8607_INT_MASK_1,
352 .offs = 1 << 3,
353 },
354 [PM8607_IRQ_RTC] = {
355 .reg = PM8607_INT_STATUS1,
356 .mask_reg = PM8607_INT_MASK_1,
357 .offs = 1 << 4,
358 },
359 [PM8607_IRQ_CC] = {
360 .reg = PM8607_INT_STATUS1,
361 .mask_reg = PM8607_INT_MASK_1,
362 .offs = 1 << 5,
363 },
364 [PM8607_IRQ_VBAT] = {
365 .reg = PM8607_INT_STATUS2,
366 .mask_reg = PM8607_INT_MASK_2,
367 .offs = 1 << 0,
368 },
369 [PM8607_IRQ_VCHG] = {
370 .reg = PM8607_INT_STATUS2,
371 .mask_reg = PM8607_INT_MASK_2,
372 .offs = 1 << 1,
373 },
374 [PM8607_IRQ_VSYS] = {
375 .reg = PM8607_INT_STATUS2,
376 .mask_reg = PM8607_INT_MASK_2,
377 .offs = 1 << 2,
378 },
379 [PM8607_IRQ_TINT] = {
380 .reg = PM8607_INT_STATUS2,
381 .mask_reg = PM8607_INT_MASK_2,
382 .offs = 1 << 3,
383 },
384 [PM8607_IRQ_GPADC0] = {
385 .reg = PM8607_INT_STATUS2,
386 .mask_reg = PM8607_INT_MASK_2,
387 .offs = 1 << 4,
388 },
389 [PM8607_IRQ_GPADC1] = {
390 .reg = PM8607_INT_STATUS2,
391 .mask_reg = PM8607_INT_MASK_2,
392 .offs = 1 << 5,
393 },
394 [PM8607_IRQ_GPADC2] = {
395 .reg = PM8607_INT_STATUS2,
396 .mask_reg = PM8607_INT_MASK_2,
397 .offs = 1 << 6,
398 },
399 [PM8607_IRQ_GPADC3] = {
400 .reg = PM8607_INT_STATUS2,
401 .mask_reg = PM8607_INT_MASK_2,
402 .offs = 1 << 7,
403 },
404 [PM8607_IRQ_AUDIO_SHORT] = {
405 .reg = PM8607_INT_STATUS3,
406 .mask_reg = PM8607_INT_MASK_3,
407 .offs = 1 << 0,
408 },
409 [PM8607_IRQ_PEN] = {
410 .reg = PM8607_INT_STATUS3,
411 .mask_reg = PM8607_INT_MASK_3,
412 .offs = 1 << 1,
413 },
414 [PM8607_IRQ_HEADSET] = {
415 .reg = PM8607_INT_STATUS3,
416 .mask_reg = PM8607_INT_MASK_3,
417 .offs = 1 << 2,
418 },
419 [PM8607_IRQ_HOOK] = {
420 .reg = PM8607_INT_STATUS3,
421 .mask_reg = PM8607_INT_MASK_3,
422 .offs = 1 << 3,
423 },
424 [PM8607_IRQ_MICIN] = {
425 .reg = PM8607_INT_STATUS3,
426 .mask_reg = PM8607_INT_MASK_3,
427 .offs = 1 << 4,
428 },
429 [PM8607_IRQ_CHG_FAIL] = {
430 .reg = PM8607_INT_STATUS3,
431 .mask_reg = PM8607_INT_MASK_3,
432 .offs = 1 << 5,
433 },
434 [PM8607_IRQ_CHG_DONE] = {
435 .reg = PM8607_INT_STATUS3,
436 .mask_reg = PM8607_INT_MASK_3,
437 .offs = 1 << 6,
438 },
439 [PM8607_IRQ_CHG_FAULT] = {
440 .reg = PM8607_INT_STATUS3,
441 .mask_reg = PM8607_INT_MASK_3,
442 .offs = 1 << 7,
443 },
444};
5c42e8c4 445
2afa62ea 446static irqreturn_t pm860x_irq(int irq, void *data)
5c42e8c4 447{
5c42e8c4 448 struct pm860x_chip *chip = data;
2afa62ea
HZ
449 struct pm860x_irq_data *irq_data;
450 struct i2c_client *i2c;
451 int read_reg = -1, value = 0;
452 int i;
453
454 i2c = (chip->id == CHIP_PM8607) ? chip->client : chip->companion;
455 for (i = 0; i < ARRAY_SIZE(pm860x_irqs); i++) {
456 irq_data = &pm860x_irqs[i];
457 if (read_reg != irq_data->reg) {
458 read_reg = irq_data->reg;
459 value = pm860x_reg_read(i2c, irq_data->reg);
5c42e8c4 460 }
2afa62ea
HZ
461 if (value & irq_data->enable)
462 handle_nested_irq(chip->irq_base + i);
5c42e8c4 463 }
5c42e8c4
HZ
464 return IRQ_HANDLED;
465}
466
49f89d9a 467static void pm860x_irq_lock(struct irq_data *data)
53dbab7a 468{
49f89d9a 469 struct pm860x_chip *chip = irq_data_get_irq_chip_data(data);
5c42e8c4
HZ
470
471 mutex_lock(&chip->irq_lock);
53dbab7a
HZ
472}
473
49f89d9a 474static void pm860x_irq_sync_unlock(struct irq_data *data)
bbd51b1f 475{
49f89d9a 476 struct pm860x_chip *chip = irq_data_get_irq_chip_data(data);
2afa62ea
HZ
477 struct pm860x_irq_data *irq_data;
478 struct i2c_client *i2c;
479 static unsigned char cached[3] = {0x0, 0x0, 0x0};
480 unsigned char mask[3];
481 int i;
482
483 i2c = (chip->id == CHIP_PM8607) ? chip->client : chip->companion;
484 /* Load cached value. In initial, all IRQs are masked */
485 for (i = 0; i < 3; i++)
486 mask[i] = cached[i];
487 for (i = 0; i < ARRAY_SIZE(pm860x_irqs); i++) {
488 irq_data = &pm860x_irqs[i];
489 switch (irq_data->mask_reg) {
490 case PM8607_INT_MASK_1:
491 mask[0] &= ~irq_data->offs;
492 mask[0] |= irq_data->enable;
493 break;
494 case PM8607_INT_MASK_2:
495 mask[1] &= ~irq_data->offs;
496 mask[1] |= irq_data->enable;
497 break;
498 case PM8607_INT_MASK_3:
499 mask[2] &= ~irq_data->offs;
500 mask[2] |= irq_data->enable;
501 break;
502 default:
503 dev_err(chip->dev, "wrong IRQ\n");
504 break;
505 }
506 }
507 /* update mask into registers */
508 for (i = 0; i < 3; i++) {
509 if (mask[i] != cached[i]) {
510 cached[i] = mask[i];
511 pm860x_reg_write(i2c, PM8607_INT_MASK_1 + i, mask[i]);
512 }
513 }
5c42e8c4 514
5c42e8c4 515 mutex_unlock(&chip->irq_lock);
2afa62ea 516}
5c42e8c4 517
49f89d9a 518static void pm860x_irq_enable(struct irq_data *data)
2afa62ea 519{
49f89d9a
MB
520 struct pm860x_chip *chip = irq_data_get_irq_chip_data(data);
521 pm860x_irqs[data->irq - chip->irq_base].enable
522 = pm860x_irqs[data->irq - chip->irq_base].offs;
5c42e8c4 523}
2afa62ea 524
49f89d9a 525static void pm860x_irq_disable(struct irq_data *data)
2afa62ea 526{
49f89d9a
MB
527 struct pm860x_chip *chip = irq_data_get_irq_chip_data(data);
528 pm860x_irqs[data->irq - chip->irq_base].enable = 0;
2afa62ea
HZ
529}
530
531static struct irq_chip pm860x_irq_chip = {
532 .name = "88pm860x",
49f89d9a
MB
533 .irq_bus_lock = pm860x_irq_lock,
534 .irq_bus_sync_unlock = pm860x_irq_sync_unlock,
535 .irq_enable = pm860x_irq_enable,
536 .irq_disable = pm860x_irq_disable,
2afa62ea 537};
5c42e8c4 538
a16122bc
HZ
539static int __devinit device_gpadc_init(struct pm860x_chip *chip,
540 struct pm860x_platform_data *pdata)
541{
542 struct i2c_client *i2c = (chip->id == CHIP_PM8607) ? chip->client \
543 : chip->companion;
eb6e8ddf
DC
544 int data;
545 int ret;
a16122bc
HZ
546
547 /* initialize GPADC without activating it */
548
eb6e8ddf
DC
549 if (!pdata || !pdata->touch)
550 return -EINVAL;
551
552 /* set GPADC MISC1 register */
553 data = 0;
554 data |= (pdata->touch->gpadc_prebias << 1) & PM8607_GPADC_PREBIAS_MASK;
555 data |= (pdata->touch->slot_cycle << 3) & PM8607_GPADC_SLOT_CYCLE_MASK;
556 data |= (pdata->touch->off_scale << 5) & PM8607_GPADC_OFF_SCALE_MASK;
557 data |= (pdata->touch->sw_cal << 7) & PM8607_GPADC_SW_CAL_MASK;
558 if (data) {
559 ret = pm860x_reg_write(i2c, PM8607_GPADC_MISC1, data);
560 if (ret < 0)
561 goto out;
a16122bc 562 }
eb6e8ddf
DC
563 /* set tsi prebias time */
564 if (pdata->touch->tsi_prebias) {
565 data = pdata->touch->tsi_prebias;
566 ret = pm860x_reg_write(i2c, PM8607_TSI_PREBIAS, data);
567 if (ret < 0)
568 goto out;
569 }
570 /* set prebias & prechg time of pen detect */
571 data = 0;
572 data |= pdata->touch->pen_prebias & PM8607_PD_PREBIAS_MASK;
573 data |= (pdata->touch->pen_prechg << 5) & PM8607_PD_PRECHG_MASK;
574 if (data) {
575 ret = pm860x_reg_write(i2c, PM8607_PD_PREBIAS, data);
576 if (ret < 0)
577 goto out;
a16122bc 578 }
eb6e8ddf
DC
579
580 ret = pm860x_set_bits(i2c, PM8607_GPADC_MISC1,
581 PM8607_GPADC_EN, PM8607_GPADC_EN);
a16122bc
HZ
582out:
583 return ret;
584}
585
5c42e8c4
HZ
586static int __devinit device_irq_init(struct pm860x_chip *chip,
587 struct pm860x_platform_data *pdata)
588{
589 struct i2c_client *i2c = (chip->id == CHIP_PM8607) ? chip->client \
590 : chip->companion;
591 unsigned char status_buf[INT_STATUS_NUM];
2afa62ea 592 unsigned long flags = IRQF_TRIGGER_FALLING | IRQF_ONESHOT;
2afa62ea
HZ
593 int i, data, mask, ret = -EINVAL;
594 int __irq;
5c42e8c4 595
2afa62ea
HZ
596 if (!pdata || !pdata->irq_base) {
597 dev_warn(chip->dev, "No interrupt support on IRQ base\n");
598 return -EINVAL;
599 }
5c42e8c4
HZ
600
601 mask = PM8607_B0_MISC1_INV_INT | PM8607_B0_MISC1_INT_CLEAR
602 | PM8607_B0_MISC1_INT_MASK;
603 data = 0;
604 chip->irq_mode = 0;
605 if (pdata && pdata->irq_mode) {
606 /*
607 * irq_mode defines the way of clearing interrupt. If it's 1,
608 * clear IRQ by write. Otherwise, clear it by read.
609 * This control bit is valid from 88PM8607 B0 steping.
610 */
611 data |= PM8607_B0_MISC1_INT_CLEAR;
612 chip->irq_mode = 1;
613 }
614 ret = pm860x_set_bits(i2c, PM8607_B0_MISC1, mask, data);
615 if (ret < 0)
616 goto out;
617
618 /* mask all IRQs */
619 memset(status_buf, 0, INT_STATUS_NUM);
620 ret = pm860x_bulk_write(i2c, PM8607_INT_MASK_1,
621 INT_STATUS_NUM, status_buf);
622 if (ret < 0)
623 goto out;
624
625 if (chip->irq_mode) {
626 /* clear interrupt status by write */
627 memset(status_buf, 0xFF, INT_STATUS_NUM);
628 ret = pm860x_bulk_write(i2c, PM8607_INT_STATUS1,
629 INT_STATUS_NUM, status_buf);
630 } else {
631 /* clear interrupt status by read */
632 ret = pm860x_bulk_read(i2c, PM8607_INT_STATUS1,
633 INT_STATUS_NUM, status_buf);
634 }
635 if (ret < 0)
636 goto out;
637
2afa62ea
HZ
638 mutex_init(&chip->irq_lock);
639 chip->irq_base = pdata->irq_base;
640 chip->core_irq = i2c->irq;
641 if (!chip->core_irq)
5c42e8c4 642 goto out;
2afa62ea 643
2afa62ea
HZ
644 /* register IRQ by genirq */
645 for (i = 0; i < ARRAY_SIZE(pm860x_irqs); i++) {
646 __irq = i + chip->irq_base;
d5bb1221
TG
647 irq_set_chip_data(__irq, chip);
648 irq_set_chip_and_handler(__irq, &pm860x_irq_chip,
2afa62ea 649 handle_edge_irq);
d5bb1221 650 irq_set_nested_thread(__irq, 1);
2afa62ea
HZ
651#ifdef CONFIG_ARM
652 set_irq_flags(__irq, IRQF_VALID);
653#else
d5bb1221 654 irq_set_noprobe(__irq);
2afa62ea 655#endif
5c42e8c4 656 }
2afa62ea 657
50164984 658 ret = request_threaded_irq(chip->core_irq, NULL, pm860x_irq, flags | IRQF_ONESHOT,
2afa62ea
HZ
659 "88pm860x", chip);
660 if (ret) {
661 dev_err(chip->dev, "Failed to request IRQ: %d\n", ret);
662 chip->core_irq = 0;
663 }
664
5c42e8c4
HZ
665 return 0;
666out:
2afa62ea 667 chip->core_irq = 0;
5c42e8c4
HZ
668 return ret;
669}
670
872c1b14 671static void device_irq_exit(struct pm860x_chip *chip)
5c42e8c4 672{
2afa62ea
HZ
673 if (chip->core_irq)
674 free_irq(chip->core_irq, chip);
5c42e8c4
HZ
675}
676
23de435a
JZ
677int pm8606_osc_enable(struct pm860x_chip *chip, unsigned short client)
678{
679 int ret = -EIO;
680 struct i2c_client *i2c = (chip->id == CHIP_PM8606) ?
681 chip->client : chip->companion;
682
683 dev_dbg(chip->dev, "%s(B): client=0x%x\n", __func__, client);
684 dev_dbg(chip->dev, "%s(B): vote=0x%x status=%d\n",
685 __func__, chip->osc_vote,
686 chip->osc_status);
687
688 mutex_lock(&chip->osc_lock);
689 /* Update voting status */
690 chip->osc_vote |= client;
691 /* If reference group is off - turn on*/
692 if (chip->osc_status != PM8606_REF_GP_OSC_ON) {
693 chip->osc_status = PM8606_REF_GP_OSC_UNKNOWN;
694 /* Enable Reference group Vsys */
695 if (pm860x_set_bits(i2c, PM8606_VSYS,
696 PM8606_VSYS_EN, PM8606_VSYS_EN))
697 goto out;
698
699 /*Enable Internal Oscillator */
700 if (pm860x_set_bits(i2c, PM8606_MISC,
701 PM8606_MISC_OSC_EN, PM8606_MISC_OSC_EN))
702 goto out;
703 /* Update status (only if writes succeed) */
704 chip->osc_status = PM8606_REF_GP_OSC_ON;
705 }
706 mutex_unlock(&chip->osc_lock);
707
708 dev_dbg(chip->dev, "%s(A): vote=0x%x status=%d ret=%d\n",
709 __func__, chip->osc_vote,
710 chip->osc_status, ret);
711 return 0;
712out:
713 mutex_unlock(&chip->osc_lock);
714 return ret;
715}
2f5f89be 716EXPORT_SYMBOL(pm8606_osc_enable);
23de435a
JZ
717
718int pm8606_osc_disable(struct pm860x_chip *chip, unsigned short client)
719{
720 int ret = -EIO;
721 struct i2c_client *i2c = (chip->id == CHIP_PM8606) ?
722 chip->client : chip->companion;
723
724 dev_dbg(chip->dev, "%s(B): client=0x%x\n", __func__, client);
725 dev_dbg(chip->dev, "%s(B): vote=0x%x status=%d\n",
726 __func__, chip->osc_vote,
727 chip->osc_status);
728
729 mutex_lock(&chip->osc_lock);
730 /*Update voting status */
731 chip->osc_vote &= ~(client);
732 /* If reference group is off and this is the last client to release
733 * - turn off */
734 if ((chip->osc_status != PM8606_REF_GP_OSC_OFF) &&
735 (chip->osc_vote == REF_GP_NO_CLIENTS)) {
736 chip->osc_status = PM8606_REF_GP_OSC_UNKNOWN;
737 /* Disable Reference group Vsys */
738 if (pm860x_set_bits(i2c, PM8606_VSYS, PM8606_VSYS_EN, 0))
739 goto out;
740 /* Disable Internal Oscillator */
741 if (pm860x_set_bits(i2c, PM8606_MISC, PM8606_MISC_OSC_EN, 0))
742 goto out;
743 chip->osc_status = PM8606_REF_GP_OSC_OFF;
744 }
745 mutex_unlock(&chip->osc_lock);
746
747 dev_dbg(chip->dev, "%s(A): vote=0x%x status=%d ret=%d\n",
748 __func__, chip->osc_vote,
749 chip->osc_status, ret);
750 return 0;
751out:
752 mutex_unlock(&chip->osc_lock);
753 return ret;
754}
2f5f89be 755EXPORT_SYMBOL(pm8606_osc_disable);
23de435a
JZ
756
757static void __devinit device_osc_init(struct i2c_client *i2c)
758{
759 struct pm860x_chip *chip = i2c_get_clientdata(i2c);
760
761 mutex_init(&chip->osc_lock);
762 /* init portofino reference group voting and status */
763 /* Disable Reference group Vsys */
764 pm860x_set_bits(i2c, PM8606_VSYS, PM8606_VSYS_EN, 0);
765 /* Disable Internal Oscillator */
766 pm860x_set_bits(i2c, PM8606_MISC, PM8606_MISC_OSC_EN, 0);
767
768 chip->osc_vote = REF_GP_NO_CLIENTS;
769 chip->osc_status = PM8606_REF_GP_OSC_OFF;
770}
771
adb70483 772static void __devinit device_bk_init(struct pm860x_chip *chip,
adb70483
HZ
773 struct pm860x_platform_data *pdata)
774{
a6ccdcd9
HZ
775 int ret, i;
776
777 if (pdata && pdata->backlight) {
778 if (pdata->num_backlights > ARRAY_SIZE(bk_devs))
779 pdata->num_backlights = ARRAY_SIZE(bk_devs);
780 for (i = 0; i < pdata->num_backlights; i++) {
781 bk_devs[i].platform_data = &pdata->backlight[i];
782 bk_devs[i].pdata_size =
783 sizeof(struct pm860x_backlight_pdata);
adb70483
HZ
784 }
785 }
a6ccdcd9 786 ret = mfd_add_devices(chip->dev, 0, bk_devs,
55692af5 787 ARRAY_SIZE(bk_devs), NULL, 0, NULL);
a6ccdcd9
HZ
788 if (ret < 0)
789 dev_err(chip->dev, "Failed to add backlight subdev\n");
adb70483
HZ
790}
791
3154c344 792static void __devinit device_led_init(struct pm860x_chip *chip,
3154c344 793 struct pm860x_platform_data *pdata)
5c42e8c4 794{
894fc8f2 795 int ret, i;
3154c344 796
894fc8f2
HZ
797 if (pdata && pdata->led) {
798 if (pdata->num_leds > ARRAY_SIZE(led_devs))
799 pdata->num_leds = ARRAY_SIZE(led_devs);
800 for (i = 0; i < pdata->num_leds; i++) {
801 led_devs[i].platform_data = &pdata->led[i];
802 led_devs[i].pdata_size =
803 sizeof(struct pm860x_led_pdata);
a16122bc
HZ
804 }
805 }
894fc8f2 806 ret = mfd_add_devices(chip->dev, 0, led_devs,
55692af5 807 ARRAY_SIZE(led_devs), NULL, 0, NULL);
894fc8f2
HZ
808 if (ret < 0) {
809 dev_err(chip->dev, "Failed to add led subdev\n");
810 return;
811 }
5c42e8c4
HZ
812}
813
22aad001 814static void __devinit device_regulator_init(struct pm860x_chip *chip,
22aad001
HZ
815 struct pm860x_platform_data *pdata)
816{
22aad001 817 int ret;
22aad001 818
a70abacb
HZ
819 if (pdata == NULL)
820 return;
821 if (pdata->buck1) {
822 reg_devs[0].platform_data = pdata->buck1;
823 reg_devs[0].pdata_size = sizeof(struct regulator_init_data);
824 }
825 if (pdata->buck2) {
826 reg_devs[1].platform_data = pdata->buck2;
827 reg_devs[1].pdata_size = sizeof(struct regulator_init_data);
828 }
829 if (pdata->buck3) {
830 reg_devs[2].platform_data = pdata->buck3;
831 reg_devs[2].pdata_size = sizeof(struct regulator_init_data);
832 }
833 if (pdata->ldo1) {
834 reg_devs[3].platform_data = pdata->ldo1;
835 reg_devs[3].pdata_size = sizeof(struct regulator_init_data);
836 }
837 if (pdata->ldo2) {
838 reg_devs[4].platform_data = pdata->ldo2;
839 reg_devs[4].pdata_size = sizeof(struct regulator_init_data);
840 }
841 if (pdata->ldo3) {
842 reg_devs[5].platform_data = pdata->ldo3;
843 reg_devs[5].pdata_size = sizeof(struct regulator_init_data);
844 }
845 if (pdata->ldo4) {
846 reg_devs[6].platform_data = pdata->ldo4;
847 reg_devs[6].pdata_size = sizeof(struct regulator_init_data);
848 }
849 if (pdata->ldo5) {
850 reg_devs[7].platform_data = pdata->ldo5;
851 reg_devs[7].pdata_size = sizeof(struct regulator_init_data);
852 }
853 if (pdata->ldo6) {
854 reg_devs[8].platform_data = pdata->ldo6;
855 reg_devs[8].pdata_size = sizeof(struct regulator_init_data);
856 }
857 if (pdata->ldo7) {
858 reg_devs[9].platform_data = pdata->ldo7;
859 reg_devs[9].pdata_size = sizeof(struct regulator_init_data);
860 }
861 if (pdata->ldo8) {
862 reg_devs[10].platform_data = pdata->ldo8;
863 reg_devs[10].pdata_size = sizeof(struct regulator_init_data);
864 }
865 if (pdata->ldo9) {
866 reg_devs[11].platform_data = pdata->ldo9;
867 reg_devs[11].pdata_size = sizeof(struct regulator_init_data);
868 }
869 if (pdata->ldo10) {
870 reg_devs[12].platform_data = pdata->ldo10;
871 reg_devs[12].pdata_size = sizeof(struct regulator_init_data);
872 }
873 if (pdata->ldo12) {
874 reg_devs[13].platform_data = pdata->ldo12;
875 reg_devs[13].pdata_size = sizeof(struct regulator_init_data);
876 }
877 if (pdata->ldo_vibrator) {
878 reg_devs[14].platform_data = pdata->ldo_vibrator;
879 reg_devs[14].pdata_size = sizeof(struct regulator_init_data);
880 }
881 if (pdata->ldo14) {
882 reg_devs[15].platform_data = pdata->ldo14;
883 reg_devs[15].pdata_size = sizeof(struct regulator_init_data);
884 }
885 ret = mfd_add_devices(chip->dev, 0, reg_devs,
55692af5 886 ARRAY_SIZE(reg_devs), NULL, 0, NULL);
a70abacb
HZ
887 if (ret < 0) {
888 dev_err(chip->dev, "Failed to add regulator subdev\n");
22aad001 889 return;
22aad001 890 }
22aad001
HZ
891}
892
008b3040 893static void __devinit device_rtc_init(struct pm860x_chip *chip,
008b3040
HZ
894 struct pm860x_platform_data *pdata)
895{
896 int ret;
897
898 if ((pdata == NULL))
899 return;
900
901 rtc_devs[0].platform_data = pdata->rtc;
902 rtc_devs[0].pdata_size = sizeof(struct pm860x_rtc_pdata);
903 rtc_devs[0].num_resources = ARRAY_SIZE(rtc_resources);
904 rtc_devs[0].resources = &rtc_resources[0];
905 ret = mfd_add_devices(chip->dev, 0, &rtc_devs[0],
906 ARRAY_SIZE(rtc_devs), &rtc_resources[0],
55692af5 907 chip->irq_base, NULL);
008b3040
HZ
908 if (ret < 0)
909 dev_err(chip->dev, "Failed to add rtc subdev\n");
910}
911
c9f560b3 912static void __devinit device_touch_init(struct pm860x_chip *chip,
c9f560b3
HZ
913 struct pm860x_platform_data *pdata)
914{
915 int ret;
916
f5fb758d 917 if (pdata == NULL)
c9f560b3
HZ
918 return;
919
f5fb758d
HZ
920 touch_devs[0].platform_data = pdata->touch;
921 touch_devs[0].pdata_size = sizeof(struct pm860x_touch_pdata);
c9f560b3
HZ
922 touch_devs[0].num_resources = ARRAY_SIZE(touch_resources);
923 touch_devs[0].resources = &touch_resources[0];
924 ret = mfd_add_devices(chip->dev, 0, &touch_devs[0],
925 ARRAY_SIZE(touch_devs), &touch_resources[0],
55692af5 926 chip->irq_base, NULL);
c9f560b3
HZ
927 if (ret < 0)
928 dev_err(chip->dev, "Failed to add touch subdev\n");
929}
930
931static void __devinit device_power_init(struct pm860x_chip *chip,
c9f560b3
HZ
932 struct pm860x_platform_data *pdata)
933{
934 int ret;
935
f5fb758d 936 if (pdata == NULL)
c9f560b3
HZ
937 return;
938
f5fb758d
HZ
939 power_devs[0].platform_data = pdata->power;
940 power_devs[0].pdata_size = sizeof(struct pm860x_power_pdata);
c9f560b3
HZ
941 power_devs[0].num_resources = ARRAY_SIZE(battery_resources);
942 power_devs[0].resources = &battery_resources[0],
943 ret = mfd_add_devices(chip->dev, 0, &power_devs[0], 1,
55692af5 944 &battery_resources[0], chip->irq_base, NULL);
c9f560b3
HZ
945 if (ret < 0)
946 dev_err(chip->dev, "Failed to add battery subdev\n");
947
f5fb758d
HZ
948 power_devs[1].platform_data = pdata->power;
949 power_devs[1].pdata_size = sizeof(struct pm860x_power_pdata);
c9f560b3
HZ
950 power_devs[1].num_resources = ARRAY_SIZE(charger_resources);
951 power_devs[1].resources = &charger_resources[0],
952 ret = mfd_add_devices(chip->dev, 0, &power_devs[1], 1,
55692af5 953 &charger_resources[0], chip->irq_base, NULL);
c9f560b3
HZ
954 if (ret < 0)
955 dev_err(chip->dev, "Failed to add charger subdev\n");
2573f6d3
JZ
956
957 power_devs[2].platform_data = &preg_init_data;
958 power_devs[2].pdata_size = sizeof(struct regulator_init_data);
2573f6d3 959 ret = mfd_add_devices(chip->dev, 0, &power_devs[2], 1,
ff13e9e2 960 NULL, chip->irq_base, NULL);
2573f6d3
JZ
961 if (ret < 0)
962 dev_err(chip->dev, "Failed to add preg subdev\n");
c9f560b3
HZ
963}
964
965static void __devinit device_onkey_init(struct pm860x_chip *chip,
c9f560b3
HZ
966 struct pm860x_platform_data *pdata)
967{
968 int ret;
969
970 onkey_devs[0].num_resources = ARRAY_SIZE(onkey_resources);
971 onkey_devs[0].resources = &onkey_resources[0],
972 ret = mfd_add_devices(chip->dev, 0, &onkey_devs[0],
973 ARRAY_SIZE(onkey_devs), &onkey_resources[0],
55692af5 974 chip->irq_base, NULL);
c9f560b3
HZ
975 if (ret < 0)
976 dev_err(chip->dev, "Failed to add onkey subdev\n");
977}
978
979static void __devinit device_codec_init(struct pm860x_chip *chip,
c9f560b3
HZ
980 struct pm860x_platform_data *pdata)
981{
982 int ret;
983
984 codec_devs[0].num_resources = ARRAY_SIZE(codec_resources);
985 codec_devs[0].resources = &codec_resources[0],
986 ret = mfd_add_devices(chip->dev, 0, &codec_devs[0],
55692af5
MB
987 ARRAY_SIZE(codec_devs), &codec_resources[0], 0,
988 NULL);
c9f560b3
HZ
989 if (ret < 0)
990 dev_err(chip->dev, "Failed to add codec subdev\n");
991}
992
5c42e8c4
HZ
993static void __devinit device_8607_init(struct pm860x_chip *chip,
994 struct i2c_client *i2c,
995 struct pm860x_platform_data *pdata)
996{
a16122bc 997 int data, ret;
bbd51b1f 998
53dbab7a 999 ret = pm860x_reg_read(i2c, PM8607_CHIP_ID);
bbd51b1f
HZ
1000 if (ret < 0) {
1001 dev_err(chip->dev, "Failed to read CHIP ID: %d\n", ret);
1002 goto out;
1003 }
38b34052
HZ
1004 switch (ret & PM8607_VERSION_MASK) {
1005 case 0x40:
1006 case 0x50:
bbd51b1f
HZ
1007 dev_info(chip->dev, "Marvell 88PM8607 (ID: %02x) detected\n",
1008 ret);
38b34052
HZ
1009 break;
1010 default:
bbd51b1f
HZ
1011 dev_err(chip->dev, "Failed to detect Marvell 88PM8607. "
1012 "Chip ID: %02x\n", ret);
1013 goto out;
1014 }
bbd51b1f 1015
53dbab7a 1016 ret = pm860x_reg_read(i2c, PM8607_BUCK3);
bbd51b1f
HZ
1017 if (ret < 0) {
1018 dev_err(chip->dev, "Failed to read BUCK3 register: %d\n", ret);
1019 goto out;
1020 }
1021 if (ret & PM8607_BUCK3_DOUBLE)
1022 chip->buck3_double = 1;
1023
5c42e8c4 1024 ret = pm860x_reg_read(i2c, PM8607_B0_MISC1);
bbd51b1f
HZ
1025 if (ret < 0) {
1026 dev_err(chip->dev, "Failed to read MISC1 register: %d\n", ret);
1027 goto out;
1028 }
bbd51b1f 1029
5c42e8c4
HZ
1030 if (pdata && (pdata->i2c_port == PI2C_PORT))
1031 data = PM8607_B0_MISC1_PI2C;
1032 else
1033 data = 0;
1034 ret = pm860x_set_bits(i2c, PM8607_B0_MISC1, PM8607_B0_MISC1_PI2C, data);
1035 if (ret < 0) {
1036 dev_err(chip->dev, "Failed to access MISC1:%d\n", ret);
1037 goto out;
1038 }
1039
a16122bc
HZ
1040 ret = device_gpadc_init(chip, pdata);
1041 if (ret < 0)
1042 goto out;
1043
5c42e8c4
HZ
1044 ret = device_irq_init(chip, pdata);
1045 if (ret < 0)
1046 goto out;
1047
cea438dd
HZ
1048 device_regulator_init(chip, pdata);
1049 device_rtc_init(chip, pdata);
1050 device_onkey_init(chip, pdata);
1051 device_touch_init(chip, pdata);
1052 device_power_init(chip, pdata);
1053 device_codec_init(chip, pdata);
bbd51b1f 1054out:
53dbab7a
HZ
1055 return;
1056}
1057
78258064
JZ
1058static void __devinit device_8606_init(struct pm860x_chip *chip,
1059 struct i2c_client *i2c,
1060 struct pm860x_platform_data *pdata)
1061{
1062 device_osc_init(i2c);
1063 device_bk_init(chip, pdata);
1064 device_led_init(chip, pdata);
1065}
1066
872c1b14 1067int __devinit pm860x_device_init(struct pm860x_chip *chip,
53dbab7a
HZ
1068 struct pm860x_platform_data *pdata)
1069{
2afa62ea 1070 chip->core_irq = 0;
5c42e8c4 1071
53dbab7a
HZ
1072 switch (chip->id) {
1073 case CHIP_PM8606:
78258064 1074 device_8606_init(chip, chip->client, pdata);
53dbab7a
HZ
1075 break;
1076 case CHIP_PM8607:
1077 device_8607_init(chip, chip->client, pdata);
1078 break;
1079 }
1080
1081 if (chip->companion) {
1082 switch (chip->id) {
1083 case CHIP_PM8607:
78258064 1084 device_8606_init(chip, chip->companion, pdata);
53dbab7a
HZ
1085 break;
1086 case CHIP_PM8606:
1087 device_8607_init(chip, chip->companion, pdata);
1088 break;
1089 }
1090 }
5c42e8c4 1091
53dbab7a 1092 return 0;
bbd51b1f
HZ
1093}
1094
872c1b14 1095void __devexit pm860x_device_exit(struct pm860x_chip *chip)
bbd51b1f 1096{
5c42e8c4 1097 device_irq_exit(chip);
bbd51b1f
HZ
1098 mfd_remove_devices(chip->dev);
1099}
1100
53dbab7a 1101MODULE_DESCRIPTION("PMIC Driver for Marvell 88PM860x");
bbd51b1f
HZ
1102MODULE_AUTHOR("Haojian Zhuang <haojian.zhuang@marvell.com>");
1103MODULE_LICENSE("GPL");
This page took 0.353034 seconds and 5 git commands to generate.