aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorKrzysztof Kozlowski <k.kozlowski@samsung.com>2014-04-14 04:09:07 -0400
committerMark Brown <broonie@linaro.org>2014-04-14 17:12:42 -0400
commit97f53d710b9f63cbef1c86ee39d9ecfdda6e674c (patch)
tree9e856f04c698936a2a0ac777545eb601a9fa1464
parent011703835f83626048ab75d4ada9ab8ed269b193 (diff)
regulator: s2mps11: Add external GPIO control for S2MPS14
Add support for external control over GPIO for LDO10, LDO11 and LDO12 S2MPS14 regulators. External control can be turned on by writing 0x0 to control register which in case of other regulators is used for disabling them. These LDO10-LDO12 regulators can be disabled only by I2C GPIO or PWREN pin so the patch actually allows proper way of disabling them. Additionally the GPIO control has two benefits: - It is faster than toggling it over I2C bus. - It allows disabling the regulator during suspend to RAM; The AP will enable it during resume. Signed-off-by: Krzysztof Kozlowski <k.kozlowski@samsung.com> Signed-off-by: Mark Brown <broonie@linaro.org>
-rw-r--r--drivers/regulator/s2mps11.c67
-rw-r--r--include/linux/mfd/samsung/s2mps14.h2
2 files changed, 67 insertions, 2 deletions
diff --git a/drivers/regulator/s2mps11.c b/drivers/regulator/s2mps11.c
index 3aba0331fb5d..6dad0aa74a47 100644
--- a/drivers/regulator/s2mps11.c
+++ b/drivers/regulator/s2mps11.c
@@ -27,6 +27,7 @@
27#include <linux/regulator/driver.h> 27#include <linux/regulator/driver.h>
28#include <linux/regulator/machine.h> 28#include <linux/regulator/machine.h>
29#include <linux/regulator/of_regulator.h> 29#include <linux/regulator/of_regulator.h>
30#include <linux/of_gpio.h>
30#include <linux/mfd/samsung/core.h> 31#include <linux/mfd/samsung/core.h>
31#include <linux/mfd/samsung/s2mps11.h> 32#include <linux/mfd/samsung/s2mps11.h>
32#include <linux/mfd/samsung/s2mps14.h> 33#include <linux/mfd/samsung/s2mps14.h>
@@ -44,6 +45,8 @@ struct s2mps11_info {
44 * was enabled. 45 * was enabled.
45 */ 46 */
46 unsigned int s2mps14_suspend_state:30; 47 unsigned int s2mps14_suspend_state:30;
48 /* Array of size rdev_num with GPIO-s for external sleep control */
49 int *ext_control_gpio;
47}; 50};
48 51
49static int get_ramp_delay(int ramp_delay) 52static int get_ramp_delay(int ramp_delay)
@@ -409,6 +412,8 @@ static int s2mps14_regulator_enable(struct regulator_dev *rdev)
409 412
410 if (s2mps11->s2mps14_suspend_state & (1 << rdev_get_id(rdev))) 413 if (s2mps11->s2mps14_suspend_state & (1 << rdev_get_id(rdev)))
411 val = S2MPS14_ENABLE_SUSPEND; 414 val = S2MPS14_ENABLE_SUSPEND;
415 else if (s2mps11->ext_control_gpio[rdev_get_id(rdev)])
416 val = S2MPS14_ENABLE_EXT_CONTROL;
412 else 417 else
413 val = rdev->desc->enable_mask; 418 val = rdev->desc->enable_mask;
414 419
@@ -565,9 +570,41 @@ static const struct regulator_desc s2mps14_regulators[] = {
565 regulator_desc_s2mps14_buck1235(5), 570 regulator_desc_s2mps14_buck1235(5),
566}; 571};
567 572
568static int s2mps11_pmic_dt_parse(struct platform_device *pdev, 573static int s2mps14_pmic_enable_ext_control(struct s2mps11_info *s2mps11,
574 struct regulator_dev *rdev)
575{
576 return regmap_update_bits(rdev->regmap, rdev->desc->enable_reg,
577 rdev->desc->enable_mask, S2MPS14_ENABLE_EXT_CONTROL);
578}
579
580static void s2mps14_pmic_dt_parse_ext_control_gpio(struct platform_device *pdev,
569 struct of_regulator_match *rdata, struct s2mps11_info *s2mps11) 581 struct of_regulator_match *rdata, struct s2mps11_info *s2mps11)
570{ 582{
583 int *gpio = s2mps11->ext_control_gpio;
584 unsigned int i;
585 unsigned int valid_regulators[3] = { S2MPS14_LDO10, S2MPS14_LDO11,
586 S2MPS14_LDO12 };
587
588 for (i = 0; i < ARRAY_SIZE(valid_regulators); i++) {
589 unsigned int reg = valid_regulators[i];
590
591 if (!rdata[reg].init_data || !rdata[reg].of_node)
592 continue;
593
594 gpio[reg] = of_get_named_gpio(rdata[reg].of_node,
595 "samsung,ext-control-gpios", 0);
596 if (!gpio_is_valid(gpio[reg]))
597 gpio[reg] = 0;
598 else
599 dev_dbg(&pdev->dev, "Using GPIO %d for ext-control over %d/%s\n",
600 gpio[reg], reg, rdata[reg].name);
601 }
602}
603
604static int s2mps11_pmic_dt_parse(struct platform_device *pdev,
605 struct of_regulator_match *rdata, struct s2mps11_info *s2mps11,
606 enum sec_device_type dev_type)
607{
571 struct device_node *reg_np; 608 struct device_node *reg_np;
572 609
573 reg_np = of_get_child_by_name(pdev->dev.parent->of_node, "regulators"); 610 reg_np = of_get_child_by_name(pdev->dev.parent->of_node, "regulators");
@@ -577,6 +614,9 @@ static int s2mps11_pmic_dt_parse(struct platform_device *pdev,
577 } 614 }
578 615
579 of_regulator_match(&pdev->dev, reg_np, rdata, s2mps11->rdev_num); 616 of_regulator_match(&pdev->dev, reg_np, rdata, s2mps11->rdev_num);
617 if (dev_type == S2MPS14X)
618 s2mps14_pmic_dt_parse_ext_control_gpio(pdev, rdata, s2mps11);
619
580 of_node_put(reg_np); 620 of_node_put(reg_np);
581 621
582 return 0; 622 return 0;
@@ -613,6 +653,12 @@ static int s2mps11_pmic_probe(struct platform_device *pdev)
613 return -EINVAL; 653 return -EINVAL;
614 }; 654 };
615 655
656 s2mps11->ext_control_gpio = devm_kzalloc(&pdev->dev,
657 sizeof(*s2mps11->ext_control_gpio) * s2mps11->rdev_num,
658 GFP_KERNEL);
659 if (!s2mps11->ext_control_gpio)
660 return -ENOMEM;
661
616 if (!iodev->dev->of_node) { 662 if (!iodev->dev->of_node) {
617 if (iodev->pdata) { 663 if (iodev->pdata) {
618 pdata = iodev->pdata; 664 pdata = iodev->pdata;
@@ -631,7 +677,7 @@ static int s2mps11_pmic_probe(struct platform_device *pdev)
631 for (i = 0; i < s2mps11->rdev_num; i++) 677 for (i = 0; i < s2mps11->rdev_num; i++)
632 rdata[i].name = regulators[i].name; 678 rdata[i].name = regulators[i].name;
633 679
634 ret = s2mps11_pmic_dt_parse(pdev, rdata, s2mps11); 680 ret = s2mps11_pmic_dt_parse(pdev, rdata, s2mps11, dev_type);
635 if (ret) 681 if (ret)
636 goto out; 682 goto out;
637 683
@@ -652,6 +698,12 @@ common_reg:
652 config.of_node = rdata[i].of_node; 698 config.of_node = rdata[i].of_node;
653 } 699 }
654 700
701 if (s2mps11->ext_control_gpio[i]) {
702 config.ena_gpio = s2mps11->ext_control_gpio[i];
703 config.ena_gpio_flags = GPIOF_OUT_INIT_HIGH;
704 } else
705 config.ena_gpio = config.ena_gpio_flags = 0;
706
655 regulator = devm_regulator_register(&pdev->dev, 707 regulator = devm_regulator_register(&pdev->dev,
656 &regulators[i], &config); 708 &regulators[i], &config);
657 if (IS_ERR(regulator)) { 709 if (IS_ERR(regulator)) {
@@ -660,6 +712,17 @@ common_reg:
660 i); 712 i);
661 goto out; 713 goto out;
662 } 714 }
715
716 if (s2mps11->ext_control_gpio[i]) {
717 ret = s2mps14_pmic_enable_ext_control(s2mps11,
718 regulator);
719 if (ret < 0) {
720 dev_err(&pdev->dev,
721 "failed to enable GPIO control over %s: %d\n",
722 regulator->desc->name, ret);
723 goto out;
724 }
725 }
663 } 726 }
664 727
665out: 728out:
diff --git a/include/linux/mfd/samsung/s2mps14.h b/include/linux/mfd/samsung/s2mps14.h
index 4b449b8ac548..900cd7a04314 100644
--- a/include/linux/mfd/samsung/s2mps14.h
+++ b/include/linux/mfd/samsung/s2mps14.h
@@ -148,6 +148,8 @@ enum s2mps14_regulators {
148#define S2MPS14_ENABLE_SHIFT 6 148#define S2MPS14_ENABLE_SHIFT 6
149/* On/Off controlled by PWREN */ 149/* On/Off controlled by PWREN */
150#define S2MPS14_ENABLE_SUSPEND (0x01 << S2MPS14_ENABLE_SHIFT) 150#define S2MPS14_ENABLE_SUSPEND (0x01 << S2MPS14_ENABLE_SHIFT)
151/* On/Off controlled by LDO10EN or EMMCEN */
152#define S2MPS14_ENABLE_EXT_CONTROL (0x00 << S2MPS14_ENABLE_SHIFT)
151#define S2MPS14_LDO_N_VOLTAGES (S2MPS14_LDO_VSEL_MASK + 1) 153#define S2MPS14_LDO_N_VOLTAGES (S2MPS14_LDO_VSEL_MASK + 1)
152#define S2MPS14_BUCK_N_VOLTAGES (S2MPS14_BUCK_VSEL_MASK + 1) 154#define S2MPS14_BUCK_N_VOLTAGES (S2MPS14_BUCK_VSEL_MASK + 1)
153 155