diff options
author | Krzysztof Kozlowski <k.kozlowski@samsung.com> | 2014-01-24 08:37:57 -0500 |
---|---|---|
committer | Mark Brown <broonie@linaro.org> | 2014-01-27 15:24:17 -0500 |
commit | ee1e0994ab1bd302fd03432de79c07751df47ffa (patch) | |
tree | 04ecbe1f9b036da25496727fd2d4ced15f8f22dd /drivers/regulator/s5m8767.c | |
parent | 07b19808486054f356dbf3495a277f51af062b35 (diff) |
regulator: s5m8767: Use GPIO for controlling Buck9/eMMC
Add support for GPIO control (enable/disable) over Buck9. The Buck9
Converter is used as a supply for eMMC Host Controller.
BUCK9EN GPIO of S5M8767 chip may be used by application processor to
enable or disable the Buck9. This 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; Without the patch the regulator supplying
eMMC must be defined as fixed-regulator.
Signed-off-by: Krzysztof Kozlowski <k.kozlowski@samsung.com>
Acked-by: Lee Jones <lee.jones@linaro.org>
Signed-off-by: Mark Brown <broonie@linaro.org>
Diffstat (limited to 'drivers/regulator/s5m8767.c')
-rw-r--r-- | drivers/regulator/s5m8767.c | 87 |
1 files changed, 84 insertions, 3 deletions
diff --git a/drivers/regulator/s5m8767.c b/drivers/regulator/s5m8767.c index d7164bb75d3e..6850a25a41c4 100644 --- a/drivers/regulator/s5m8767.c +++ b/drivers/regulator/s5m8767.c | |||
@@ -11,11 +11,8 @@ | |||
11 | * | 11 | * |
12 | */ | 12 | */ |
13 | 13 | ||
14 | #include <linux/bug.h> | ||
15 | #include <linux/err.h> | 14 | #include <linux/err.h> |
16 | #include <linux/gpio.h> | ||
17 | #include <linux/of_gpio.h> | 15 | #include <linux/of_gpio.h> |
18 | #include <linux/slab.h> | ||
19 | #include <linux/module.h> | 16 | #include <linux/module.h> |
20 | #include <linux/platform_device.h> | 17 | #include <linux/platform_device.h> |
21 | #include <linux/regulator/driver.h> | 18 | #include <linux/regulator/driver.h> |
@@ -483,6 +480,65 @@ static struct regulator_desc regulators[] = { | |||
483 | s5m8767_regulator_desc(BUCK9), | 480 | s5m8767_regulator_desc(BUCK9), |
484 | }; | 481 | }; |
485 | 482 | ||
483 | /* | ||
484 | * Enable GPIO control over BUCK9 in regulator_config for that regulator. | ||
485 | */ | ||
486 | static void s5m8767_regulator_config_ext_control(struct s5m8767_info *s5m8767, | ||
487 | struct sec_regulator_data *rdata, | ||
488 | struct regulator_config *config) | ||
489 | { | ||
490 | int i, mode = 0; | ||
491 | |||
492 | if (rdata->id != S5M8767_BUCK9) | ||
493 | return; | ||
494 | |||
495 | /* Check if opmode for regulator matches S5M8767_ENCTRL_USE_GPIO */ | ||
496 | for (i = 0; i < s5m8767->num_regulators; i++) { | ||
497 | const struct sec_opmode_data *opmode = &s5m8767->opmode[i]; | ||
498 | if (opmode->id == rdata->id) { | ||
499 | mode = s5m8767_opmode_reg[rdata->id][opmode->mode]; | ||
500 | break; | ||
501 | } | ||
502 | } | ||
503 | if (mode != S5M8767_ENCTRL_USE_GPIO) { | ||
504 | dev_warn(s5m8767->dev, | ||
505 | "ext-control for %s: mismatched op_mode (%x), ignoring\n", | ||
506 | rdata->reg_node->name, mode); | ||
507 | return; | ||
508 | } | ||
509 | |||
510 | if (!gpio_is_valid(rdata->ext_control_gpio)) { | ||
511 | dev_warn(s5m8767->dev, | ||
512 | "ext-control for %s: GPIO not valid, ignoring\n", | ||
513 | rdata->reg_node->name); | ||
514 | return; | ||
515 | } | ||
516 | |||
517 | config->ena_gpio = rdata->ext_control_gpio; | ||
518 | config->ena_gpio_flags = GPIOF_OUT_INIT_HIGH; | ||
519 | } | ||
520 | |||
521 | /* | ||
522 | * Turn on GPIO control over BUCK9. | ||
523 | */ | ||
524 | static int s5m8767_enable_ext_control(struct s5m8767_info *s5m8767, | ||
525 | struct regulator_dev *rdev) | ||
526 | { | ||
527 | int ret, reg, enable_ctrl; | ||
528 | |||
529 | if (rdev_get_id(rdev) != S5M8767_BUCK9) | ||
530 | return -EINVAL; | ||
531 | |||
532 | ret = s5m8767_get_register(rdev, ®, &enable_ctrl); | ||
533 | if (ret) | ||
534 | return ret; | ||
535 | |||
536 | return regmap_update_bits(s5m8767->iodev->regmap_pmic, | ||
537 | reg, S5M8767_ENCTRL_MASK, | ||
538 | S5M8767_ENCTRL_USE_GPIO << S5M8767_ENCTRL_SHIFT); | ||
539 | } | ||
540 | |||
541 | |||
486 | #ifdef CONFIG_OF | 542 | #ifdef CONFIG_OF |
487 | static int s5m8767_pmic_dt_parse_dvs_gpio(struct sec_pmic_dev *iodev, | 543 | static int s5m8767_pmic_dt_parse_dvs_gpio(struct sec_pmic_dev *iodev, |
488 | struct sec_platform_data *pdata, | 544 | struct sec_platform_data *pdata, |
@@ -520,6 +576,16 @@ static int s5m8767_pmic_dt_parse_ds_gpio(struct sec_pmic_dev *iodev, | |||
520 | return 0; | 576 | return 0; |
521 | } | 577 | } |
522 | 578 | ||
579 | static void s5m8767_pmic_dt_parse_ext_control_gpio(struct sec_pmic_dev *iodev, | ||
580 | struct sec_regulator_data *rdata, | ||
581 | struct device_node *reg_np) | ||
582 | { | ||
583 | rdata->ext_control_gpio = of_get_named_gpio(reg_np, | ||
584 | "s5m8767,pmic-ext-control-gpios", 0); | ||
585 | if (!gpio_is_valid(rdata->ext_control_gpio)) | ||
586 | rdata->ext_control_gpio = 0; | ||
587 | } | ||
588 | |||
523 | static int s5m8767_pmic_dt_parse_pdata(struct platform_device *pdev, | 589 | static int s5m8767_pmic_dt_parse_pdata(struct platform_device *pdev, |
524 | struct sec_platform_data *pdata) | 590 | struct sec_platform_data *pdata) |
525 | { | 591 | { |
@@ -574,6 +640,8 @@ static int s5m8767_pmic_dt_parse_pdata(struct platform_device *pdev, | |||
574 | continue; | 640 | continue; |
575 | } | 641 | } |
576 | 642 | ||
643 | s5m8767_pmic_dt_parse_ext_control_gpio(iodev, rdata, reg_np); | ||
644 | |||
577 | rdata->id = i; | 645 | rdata->id = i; |
578 | rdata->initdata = of_get_regulator_init_data( | 646 | rdata->initdata = of_get_regulator_init_data( |
579 | &pdev->dev, reg_np); | 647 | &pdev->dev, reg_np); |
@@ -940,6 +1008,9 @@ static int s5m8767_pmic_probe(struct platform_device *pdev) | |||
940 | config.driver_data = s5m8767; | 1008 | config.driver_data = s5m8767; |
941 | config.regmap = iodev->regmap_pmic; | 1009 | config.regmap = iodev->regmap_pmic; |
942 | config.of_node = pdata->regulators[i].reg_node; | 1010 | config.of_node = pdata->regulators[i].reg_node; |
1011 | if (pdata->regulators[i].ext_control_gpio) | ||
1012 | s5m8767_regulator_config_ext_control(s5m8767, | ||
1013 | &pdata->regulators[i], &config); | ||
943 | 1014 | ||
944 | rdev[i] = devm_regulator_register(&pdev->dev, ®ulators[id], | 1015 | rdev[i] = devm_regulator_register(&pdev->dev, ®ulators[id], |
945 | &config); | 1016 | &config); |
@@ -949,6 +1020,16 @@ static int s5m8767_pmic_probe(struct platform_device *pdev) | |||
949 | id); | 1020 | id); |
950 | return ret; | 1021 | return ret; |
951 | } | 1022 | } |
1023 | |||
1024 | if (pdata->regulators[i].ext_control_gpio) { | ||
1025 | ret = s5m8767_enable_ext_control(s5m8767, rdev[i]); | ||
1026 | if (ret < 0) { | ||
1027 | dev_err(s5m8767->dev, | ||
1028 | "failed to enable gpio control over %s: %d\n", | ||
1029 | rdev[i]->desc->name, ret); | ||
1030 | return ret; | ||
1031 | } | ||
1032 | } | ||
952 | } | 1033 | } |
953 | 1034 | ||
954 | return 0; | 1035 | return 0; |