diff options
author | David Brownell <dbrownell@users.sourceforge.net> | 2008-11-30 18:43:58 -0500 |
---|---|---|
committer | Samuel Ortiz <samuel@sortiz.org> | 2009-01-04 06:17:39 -0500 |
commit | dad759ff8ba79927766e3f0159bfc5fb6de0f982 (patch) | |
tree | 06849276db93d8893d763175b8d3acb4b2b5e2f8 | |
parent | 67460a7c26271fd7a32e5d51b2c806a84ce78a62 (diff) |
mfd: twl4030: create some regulator devices
Initial code to create twl4030 voltage regulator devices, using
the new regulator framework. Note that this now starts to care
what name is used to declare the TWL chip:
- TWL4030 is the "old" chip; newer ones have a bigger variety
of VAUX2 voltages.
- TWL5030 is the core "new" chip; TPS65950 is its catalog version.
- The TPS65930 and TPS65920 are cost-reduced catalog versions of
TWL5030 parts ... fewer regulators, no battery charger, etc.
Board-specific regulator configuration should be provided, listing
which regulators are used and their constraints (e.g. 1.8V only).
Code that could ("should"?) leverage the regulator stuff includes
TWL4030 USB transceiver support and MMC glue, LCD support for the
3430SDP and Labrador boards, and S-Video output.
Signed-off-by: David Brownell <dbrownell@users.sourceforge.net>
Signed-off-by: Tony Lindgren <tony@atomide.com>
Signed-off-by: Samuel Ortiz <sameo@openedhand.com>
-rw-r--r-- | drivers/mfd/twl4030-core.c | 174 | ||||
-rw-r--r-- | include/linux/i2c/twl4030.h | 47 |
2 files changed, 211 insertions, 10 deletions
diff --git a/drivers/mfd/twl4030-core.c b/drivers/mfd/twl4030-core.c index f5486cce86f4..8ab9ee8543a6 100644 --- a/drivers/mfd/twl4030-core.c +++ b/drivers/mfd/twl4030-core.c | |||
@@ -33,6 +33,8 @@ | |||
33 | #include <linux/clk.h> | 33 | #include <linux/clk.h> |
34 | #include <linux/err.h> | 34 | #include <linux/err.h> |
35 | 35 | ||
36 | #include <linux/regulator/machine.h> | ||
37 | |||
36 | #include <linux/i2c.h> | 38 | #include <linux/i2c.h> |
37 | #include <linux/i2c/twl4030.h> | 39 | #include <linux/i2c/twl4030.h> |
38 | 40 | ||
@@ -71,6 +73,13 @@ | |||
71 | #define twl_has_gpio() false | 73 | #define twl_has_gpio() false |
72 | #endif | 74 | #endif |
73 | 75 | ||
76 | #if defined(CONFIG_REGULATOR_TWL4030) \ | ||
77 | || defined(CONFIG_REGULATOR_TWL4030_MODULE) | ||
78 | #define twl_has_regulator() true | ||
79 | #else | ||
80 | #define twl_has_regulator() false | ||
81 | #endif | ||
82 | |||
74 | #if defined(CONFIG_TWL4030_MADC) || defined(CONFIG_TWL4030_MADC_MODULE) | 83 | #if defined(CONFIG_TWL4030_MADC) || defined(CONFIG_TWL4030_MADC_MODULE) |
75 | #define twl_has_madc() true | 84 | #define twl_has_madc() true |
76 | #else | 85 | #else |
@@ -149,6 +158,10 @@ | |||
149 | #define HIGH_PERF_SQ (1 << 3) | 158 | #define HIGH_PERF_SQ (1 << 3) |
150 | 159 | ||
151 | 160 | ||
161 | /* chip-specific feature flags, for i2c_device_id.driver_data */ | ||
162 | #define TWL4030_VAUX2 BIT(0) /* pre-5030 voltage ranges */ | ||
163 | #define TPS_SUBSET BIT(1) /* tps659[23]0 have fewer LDOs */ | ||
164 | |||
152 | /*----------------------------------------------------------------------*/ | 165 | /*----------------------------------------------------------------------*/ |
153 | 166 | ||
154 | /* is driver active, bound to a chip? */ | 167 | /* is driver active, bound to a chip? */ |
@@ -352,7 +365,8 @@ EXPORT_SYMBOL(twl4030_i2c_read_u8); | |||
352 | 365 | ||
353 | /*----------------------------------------------------------------------*/ | 366 | /*----------------------------------------------------------------------*/ |
354 | 367 | ||
355 | static struct device *add_child(unsigned chip, const char *name, | 368 | static struct device * |
369 | add_numbered_child(unsigned chip, const char *name, int num, | ||
356 | void *pdata, unsigned pdata_len, | 370 | void *pdata, unsigned pdata_len, |
357 | bool can_wakeup, int irq0, int irq1) | 371 | bool can_wakeup, int irq0, int irq1) |
358 | { | 372 | { |
@@ -360,7 +374,7 @@ static struct device *add_child(unsigned chip, const char *name, | |||
360 | struct twl4030_client *twl = &twl4030_modules[chip]; | 374 | struct twl4030_client *twl = &twl4030_modules[chip]; |
361 | int status; | 375 | int status; |
362 | 376 | ||
363 | pdev = platform_device_alloc(name, -1); | 377 | pdev = platform_device_alloc(name, num); |
364 | if (!pdev) { | 378 | if (!pdev) { |
365 | dev_dbg(&twl->client->dev, "can't alloc dev\n"); | 379 | dev_dbg(&twl->client->dev, "can't alloc dev\n"); |
366 | status = -ENOMEM; | 380 | status = -ENOMEM; |
@@ -402,17 +416,52 @@ err: | |||
402 | return &pdev->dev; | 416 | return &pdev->dev; |
403 | } | 417 | } |
404 | 418 | ||
419 | static inline struct device *add_child(unsigned chip, const char *name, | ||
420 | void *pdata, unsigned pdata_len, | ||
421 | bool can_wakeup, int irq0, int irq1) | ||
422 | { | ||
423 | return add_numbered_child(chip, name, -1, pdata, pdata_len, | ||
424 | can_wakeup, irq0, irq1); | ||
425 | } | ||
426 | |||
427 | static struct device * | ||
428 | add_regulator_linked(int num, struct regulator_init_data *pdata, | ||
429 | struct regulator_consumer_supply *consumers, | ||
430 | unsigned num_consumers) | ||
431 | { | ||
432 | /* regulator framework demands init_data ... */ | ||
433 | if (!pdata) | ||
434 | return NULL; | ||
435 | |||
436 | if (consumers && !pdata->consumer_supplies) { | ||
437 | pdata->consumer_supplies = consumers; | ||
438 | pdata->num_consumer_supplies = num_consumers; | ||
439 | } | ||
440 | |||
441 | /* NOTE: we currently ignore regulator IRQs, e.g. for short circuits */ | ||
442 | return add_numbered_child(3, "twl4030_reg", num, | ||
443 | pdata, sizeof(*pdata), false, 0, 0); | ||
444 | } | ||
445 | |||
446 | static struct device * | ||
447 | add_regulator(int num, struct regulator_init_data *pdata) | ||
448 | { | ||
449 | return add_regulator_linked(num, pdata, NULL, 0); | ||
450 | } | ||
451 | |||
405 | /* | 452 | /* |
406 | * NOTE: We know the first 8 IRQs after pdata->base_irq are | 453 | * NOTE: We know the first 8 IRQs after pdata->base_irq are |
407 | * for the PIH, and the next are for the PWR_INT SIH, since | 454 | * for the PIH, and the next are for the PWR_INT SIH, since |
408 | * that's how twl_init_irq() sets things up. | 455 | * that's how twl_init_irq() sets things up. |
409 | */ | 456 | */ |
410 | 457 | ||
411 | static int add_children(struct twl4030_platform_data *pdata) | 458 | static int |
459 | add_children(struct twl4030_platform_data *pdata, unsigned long features) | ||
412 | { | 460 | { |
413 | struct device *child; | 461 | struct device *child; |
462 | struct device *usb_transceiver = NULL; | ||
414 | 463 | ||
415 | if (twl_has_bci() && pdata->bci) { | 464 | if (twl_has_bci() && pdata->bci && !(features & TPS_SUBSET)) { |
416 | child = add_child(3, "twl4030_bci", | 465 | child = add_child(3, "twl4030_bci", |
417 | pdata->bci, sizeof(*pdata->bci), | 466 | pdata->bci, sizeof(*pdata->bci), |
418 | false, | 467 | false, |
@@ -469,6 +518,111 @@ static int add_children(struct twl4030_platform_data *pdata) | |||
469 | pdata->irq_base + 8 + 2, pdata->irq_base + 4); | 518 | pdata->irq_base + 8 + 2, pdata->irq_base + 4); |
470 | if (IS_ERR(child)) | 519 | if (IS_ERR(child)) |
471 | return PTR_ERR(child); | 520 | return PTR_ERR(child); |
521 | |||
522 | /* we need to connect regulators to this transceiver */ | ||
523 | usb_transceiver = child; | ||
524 | } | ||
525 | |||
526 | if (twl_has_regulator()) { | ||
527 | /* | ||
528 | child = add_regulator(TWL4030_REG_VPLL1, pdata->vpll1); | ||
529 | if (IS_ERR(child)) | ||
530 | return PTR_ERR(child); | ||
531 | */ | ||
532 | |||
533 | child = add_regulator(TWL4030_REG_VMMC1, pdata->vmmc1); | ||
534 | if (IS_ERR(child)) | ||
535 | return PTR_ERR(child); | ||
536 | |||
537 | child = add_regulator(TWL4030_REG_VDAC, pdata->vdac); | ||
538 | if (IS_ERR(child)) | ||
539 | return PTR_ERR(child); | ||
540 | |||
541 | child = add_regulator((features & TWL4030_VAUX2) | ||
542 | ? TWL4030_REG_VAUX2_4030 | ||
543 | : TWL4030_REG_VAUX2, | ||
544 | pdata->vaux2); | ||
545 | if (IS_ERR(child)) | ||
546 | return PTR_ERR(child); | ||
547 | } | ||
548 | |||
549 | if (twl_has_regulator() && usb_transceiver) { | ||
550 | static struct regulator_consumer_supply usb1v5 = { | ||
551 | .supply = "usb1v5", | ||
552 | }; | ||
553 | static struct regulator_consumer_supply usb1v8 = { | ||
554 | .supply = "usb1v8", | ||
555 | }; | ||
556 | static struct regulator_consumer_supply usb3v1 = { | ||
557 | .supply = "usb3v1", | ||
558 | }; | ||
559 | static struct regulator_consumer_supply usbcp = { | ||
560 | .supply = "usbcp", | ||
561 | }; | ||
562 | |||
563 | /* this is a template that gets copied */ | ||
564 | struct regulator_init_data usb_fixed = { | ||
565 | .constraints.valid_modes_mask = | ||
566 | REGULATOR_MODE_NORMAL | ||
567 | | REGULATOR_MODE_STANDBY, | ||
568 | .constraints.valid_ops_mask = | ||
569 | REGULATOR_CHANGE_MODE | ||
570 | | REGULATOR_CHANGE_STATUS, | ||
571 | }; | ||
572 | |||
573 | usb1v5.dev = usb_transceiver; | ||
574 | usb1v8.dev = usb_transceiver; | ||
575 | usb3v1.dev = usb_transceiver; | ||
576 | usbcp.dev = usb_transceiver; | ||
577 | |||
578 | child = add_regulator_linked(TWL4030_REG_VUSB1V5, &usb_fixed, | ||
579 | &usb1v5, 1); | ||
580 | if (IS_ERR(child)) | ||
581 | return PTR_ERR(child); | ||
582 | |||
583 | child = add_regulator_linked(TWL4030_REG_VUSB1V8, &usb_fixed, | ||
584 | &usb1v8, 1); | ||
585 | if (IS_ERR(child)) | ||
586 | return PTR_ERR(child); | ||
587 | |||
588 | child = add_regulator_linked(TWL4030_REG_VUSB3V1, &usb_fixed, | ||
589 | &usb3v1, 1); | ||
590 | if (IS_ERR(child)) | ||
591 | return PTR_ERR(child); | ||
592 | |||
593 | child = add_regulator_linked(TWL4030_REG_VUSBCP, &usb_fixed, | ||
594 | &usbcp, 1); | ||
595 | if (IS_ERR(child)) | ||
596 | return PTR_ERR(child); | ||
597 | } | ||
598 | |||
599 | /* maybe add LDOs that are omitted on cost-reduced parts */ | ||
600 | if (twl_has_regulator() && !(features & TPS_SUBSET)) { | ||
601 | /* | ||
602 | child = add_regulator(TWL4030_REG_VPLL2, pdata->vpll2); | ||
603 | if (IS_ERR(child)) | ||
604 | return PTR_ERR(child); | ||
605 | */ | ||
606 | |||
607 | child = add_regulator(TWL4030_REG_VMMC2, pdata->vmmc2); | ||
608 | if (IS_ERR(child)) | ||
609 | return PTR_ERR(child); | ||
610 | |||
611 | child = add_regulator(TWL4030_REG_VSIM, pdata->vsim); | ||
612 | if (IS_ERR(child)) | ||
613 | return PTR_ERR(child); | ||
614 | |||
615 | child = add_regulator(TWL4030_REG_VAUX1, pdata->vaux1); | ||
616 | if (IS_ERR(child)) | ||
617 | return PTR_ERR(child); | ||
618 | |||
619 | child = add_regulator(TWL4030_REG_VAUX3, pdata->vaux3); | ||
620 | if (IS_ERR(child)) | ||
621 | return PTR_ERR(child); | ||
622 | |||
623 | child = add_regulator(TWL4030_REG_VAUX4, pdata->vaux4); | ||
624 | if (IS_ERR(child)) | ||
625 | return PTR_ERR(child); | ||
472 | } | 626 | } |
473 | 627 | ||
474 | return 0; | 628 | return 0; |
@@ -632,7 +786,7 @@ twl4030_probe(struct i2c_client *client, const struct i2c_device_id *id) | |||
632 | goto fail; | 786 | goto fail; |
633 | } | 787 | } |
634 | 788 | ||
635 | status = add_children(pdata); | 789 | status = add_children(pdata, id->driver_data); |
636 | fail: | 790 | fail: |
637 | if (status < 0) | 791 | if (status < 0) |
638 | twl4030_remove(client); | 792 | twl4030_remove(client); |
@@ -640,11 +794,11 @@ fail: | |||
640 | } | 794 | } |
641 | 795 | ||
642 | static const struct i2c_device_id twl4030_ids[] = { | 796 | static const struct i2c_device_id twl4030_ids[] = { |
643 | { "twl4030", 0 }, /* "Triton 2" */ | 797 | { "twl4030", TWL4030_VAUX2 }, /* "Triton 2" */ |
644 | { "tps65950", 0 }, /* catalog version of twl4030 */ | 798 | { "twl5030", 0 }, /* T2 updated */ |
645 | { "tps65930", 0 }, /* fewer LDOs and DACs; no charger */ | 799 | { "tps65950", 0 }, /* catalog version of twl5030 */ |
646 | { "tps65920", 0 }, /* fewer LDOs; no codec or charger */ | 800 | { "tps65930", TPS_SUBSET }, /* fewer LDOs and DACs; no charger */ |
647 | { "twl5030", 0 }, /* T2 updated */ | 801 | { "tps65920", TPS_SUBSET }, /* fewer LDOs; no codec or charger */ |
648 | { /* end of list */ }, | 802 | { /* end of list */ }, |
649 | }; | 803 | }; |
650 | MODULE_DEVICE_TABLE(i2c, twl4030_ids); | 804 | MODULE_DEVICE_TABLE(i2c, twl4030_ids); |
diff --git a/include/linux/i2c/twl4030.h b/include/linux/i2c/twl4030.h index d4846695bcd0..e06555d40d35 100644 --- a/include/linux/i2c/twl4030.h +++ b/include/linux/i2c/twl4030.h | |||
@@ -278,6 +278,18 @@ struct twl4030_platform_data { | |||
278 | struct twl4030_keypad_data *keypad; | 278 | struct twl4030_keypad_data *keypad; |
279 | struct twl4030_usb_data *usb; | 279 | struct twl4030_usb_data *usb; |
280 | 280 | ||
281 | /* LDO regulators */ | ||
282 | struct regulator_init_data *vdac; | ||
283 | struct regulator_init_data *vpll1; | ||
284 | struct regulator_init_data *vpll2; | ||
285 | struct regulator_init_data *vmmc1; | ||
286 | struct regulator_init_data *vmmc2; | ||
287 | struct regulator_init_data *vsim; | ||
288 | struct regulator_init_data *vaux1; | ||
289 | struct regulator_init_data *vaux2; | ||
290 | struct regulator_init_data *vaux3; | ||
291 | struct regulator_init_data *vaux4; | ||
292 | |||
281 | /* REVISIT more to come ... _nothing_ should be hard-wired */ | 293 | /* REVISIT more to come ... _nothing_ should be hard-wired */ |
282 | }; | 294 | }; |
283 | 295 | ||
@@ -309,4 +321,39 @@ int twl4030_set_gpio_debounce(int gpio, int enable); | |||
309 | static inline int twl4030charger_usb_en(int enable) { return 0; } | 321 | static inline int twl4030charger_usb_en(int enable) { return 0; } |
310 | #endif | 322 | #endif |
311 | 323 | ||
324 | /*----------------------------------------------------------------------*/ | ||
325 | |||
326 | /* Linux-specific regulator identifiers ... for now, we only support | ||
327 | * the LDOs, and leave the three buck converters alone. VDD1 and VDD2 | ||
328 | * need to tie into hardware based voltage scaling (cpufreq etc), while | ||
329 | * VIO is generally fixed. | ||
330 | */ | ||
331 | |||
332 | /* EXTERNAL dc-to-dc buck converters */ | ||
333 | #define TWL4030_REG_VDD1 0 | ||
334 | #define TWL4030_REG_VDD2 1 | ||
335 | #define TWL4030_REG_VIO 2 | ||
336 | |||
337 | /* EXTERNAL LDOs */ | ||
338 | #define TWL4030_REG_VDAC 3 | ||
339 | #define TWL4030_REG_VPLL1 4 | ||
340 | #define TWL4030_REG_VPLL2 5 /* not on all chips */ | ||
341 | #define TWL4030_REG_VMMC1 6 | ||
342 | #define TWL4030_REG_VMMC2 7 /* not on all chips */ | ||
343 | #define TWL4030_REG_VSIM 8 /* not on all chips */ | ||
344 | #define TWL4030_REG_VAUX1 9 /* not on all chips */ | ||
345 | #define TWL4030_REG_VAUX2_4030 10 /* (twl4030-specific) */ | ||
346 | #define TWL4030_REG_VAUX2 11 /* (twl5030 and newer) */ | ||
347 | #define TWL4030_REG_VAUX3 12 /* not on all chips */ | ||
348 | #define TWL4030_REG_VAUX4 13 /* not on all chips */ | ||
349 | |||
350 | /* INTERNAL LDOs */ | ||
351 | #define TWL4030_REG_VINTANA1 14 | ||
352 | #define TWL4030_REG_VINTANA2 15 | ||
353 | #define TWL4030_REG_VINTDIG 16 | ||
354 | #define TWL4030_REG_VUSB1V5 17 | ||
355 | #define TWL4030_REG_VUSB1V8 18 | ||
356 | #define TWL4030_REG_VUSB3V1 19 | ||
357 | #define TWL4030_REG_VUSBCP 20 | ||
358 | |||
312 | #endif /* End of __TWL4030_H */ | 359 | #endif /* End of __TWL4030_H */ |