diff options
author | Mark Brown <broonie@sirena.org.uk> | 2013-04-28 02:13:40 +0100 |
---|---|---|
committer | Mark Brown <broonie@sirena.org.uk> | 2013-04-28 02:13:40 +0100 |
commit | 3dc06c1baf2b28e5365a1159755eac2e95142601 (patch) | |
tree | 771e1dcfd1ac5f0d2e14bec7973bcd58c9f7dfea /drivers/regulator/core.c | |
parent | 5f19a85ba728de8a1eca8b8ae239cc27f9449985 (diff) | |
parent | 407945fd78c3fddef83ba17bf2250112c07dc7c1 (diff) |
Merge remote-tracking branch 'regulator/topic/gpio' into v3.9-rc8
Diffstat (limited to 'drivers/regulator/core.c')
-rw-r--r-- | drivers/regulator/core.c | 142 |
1 files changed, 123 insertions, 19 deletions
diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c index 2434e2e1afc..6e501784158 100644 --- a/drivers/regulator/core.c +++ b/drivers/regulator/core.c @@ -51,6 +51,7 @@ static DEFINE_MUTEX(regulator_list_mutex); static LIST_HEAD(regulator_list); static LIST_HEAD(regulator_map_list); +static LIST_HEAD(regulator_ena_gpio_list); static bool has_full_constraints; static bool board_wants_dummy_regulator; @@ -69,6 +70,19 @@ struct regulator_map { }; /* + * struct regulator_enable_gpio + * + * Management for shared enable GPIO pin + */ +struct regulator_enable_gpio { + struct list_head list; + int gpio; + u32 enable_count; /* a number of enabled shared GPIO */ + u32 request_count; /* a number of requested shared GPIO */ + unsigned int ena_gpio_invert:1; +}; + +/* * struct regulator * * One for each consumer device. @@ -1465,6 +1479,101 @@ void devm_regulator_put(struct regulator *regulator) } EXPORT_SYMBOL_GPL(devm_regulator_put); +/* Manage enable GPIO list. Same GPIO pin can be shared among regulators */ +static int regulator_ena_gpio_request(struct regulator_dev *rdev, + const struct regulator_config *config) +{ + struct regulator_enable_gpio *pin; + int ret; + + list_for_each_entry(pin, ®ulator_ena_gpio_list, list) { + if (pin->gpio == config->ena_gpio) { + rdev_dbg(rdev, "GPIO %d is already used\n", + config->ena_gpio); + goto update_ena_gpio_to_rdev; + } + } + + ret = gpio_request_one(config->ena_gpio, + GPIOF_DIR_OUT | config->ena_gpio_flags, + rdev_get_name(rdev)); + if (ret) + return ret; + + pin = kzalloc(sizeof(struct regulator_enable_gpio), GFP_KERNEL); + if (pin == NULL) { + gpio_free(config->ena_gpio); + return -ENOMEM; + } + + pin->gpio = config->ena_gpio; + pin->ena_gpio_invert = config->ena_gpio_invert; + list_add(&pin->list, ®ulator_ena_gpio_list); + +update_ena_gpio_to_rdev: + pin->request_count++; + rdev->ena_pin = pin; + return 0; +} + +static void regulator_ena_gpio_free(struct regulator_dev *rdev) +{ + struct regulator_enable_gpio *pin, *n; + + if (!rdev->ena_pin) + return; + + /* Free the GPIO only in case of no use */ + list_for_each_entry_safe(pin, n, ®ulator_ena_gpio_list, list) { + if (pin->gpio == rdev->ena_pin->gpio) { + if (pin->request_count <= 1) { + pin->request_count = 0; + gpio_free(pin->gpio); + list_del(&pin->list); + kfree(pin); + } else { + pin->request_count--; + } + } + } +} + +/** + * Balance enable_count of each GPIO and actual GPIO pin control. + * GPIO is enabled in case of initial use. (enable_count is 0) + * GPIO is disabled when it is not shared any more. (enable_count <= 1) + */ +static int regulator_ena_gpio_ctrl(struct regulator_dev *rdev, bool enable) +{ + struct regulator_enable_gpio *pin = rdev->ena_pin; + + if (!pin) + return -EINVAL; + + if (enable) { + /* Enable GPIO at initial use */ + if (pin->enable_count == 0) + gpio_set_value_cansleep(pin->gpio, + !pin->ena_gpio_invert); + + pin->enable_count++; + } else { + if (pin->enable_count > 1) { + pin->enable_count--; + return 0; + } + + /* Disable GPIO if not used */ + if (pin->enable_count <= 1) { + gpio_set_value_cansleep(pin->gpio, + pin->ena_gpio_invert); + pin->enable_count = 0; + } + } + + return 0; +} + static int _regulator_do_enable(struct regulator_dev *rdev) { int ret, delay; @@ -1480,9 +1589,10 @@ static int _regulator_do_enable(struct regulator_dev *rdev) trace_regulator_enable(rdev_get_name(rdev)); - if (rdev->ena_gpio) { - gpio_set_value_cansleep(rdev->ena_gpio, - !rdev->ena_gpio_invert); + if (rdev->ena_pin) { + ret = regulator_ena_gpio_ctrl(rdev, true); + if (ret < 0) + return ret; rdev->ena_gpio_state = 1; } else if (rdev->desc->ops->enable) { ret = rdev->desc->ops->enable(rdev); @@ -1584,9 +1694,10 @@ static int _regulator_do_disable(struct regulator_dev *rdev) trace_regulator_disable(rdev_get_name(rdev)); - if (rdev->ena_gpio) { - gpio_set_value_cansleep(rdev->ena_gpio, - rdev->ena_gpio_invert); + if (rdev->ena_pin) { + ret = regulator_ena_gpio_ctrl(rdev, false); + if (ret < 0) + return ret; rdev->ena_gpio_state = 0; } else if (rdev->desc->ops->disable) { @@ -1859,7 +1970,7 @@ EXPORT_SYMBOL_GPL(regulator_disable_regmap); static int _regulator_is_enabled(struct regulator_dev *rdev) { /* A GPIO control always takes precedence */ - if (rdev->ena_gpio) + if (rdev->ena_pin) return rdev->ena_gpio_state; /* If we don't know then assume that the regulator is always on */ @@ -3293,7 +3404,7 @@ static int add_regulator_attributes(struct regulator_dev *rdev) if (status < 0) return status; } - if (rdev->ena_gpio || ops->is_enabled) { + if (rdev->ena_pin || ops->is_enabled) { status = device_create_file(dev, &dev_attr_state); if (status < 0) return status; @@ -3495,22 +3606,17 @@ regulator_register(const struct regulator_desc *regulator_desc, dev_set_drvdata(&rdev->dev, rdev); if (config->ena_gpio && gpio_is_valid(config->ena_gpio)) { - ret = gpio_request_one(config->ena_gpio, - GPIOF_DIR_OUT | config->ena_gpio_flags, - rdev_get_name(rdev)); + ret = regulator_ena_gpio_request(rdev, config); if (ret != 0) { rdev_err(rdev, "Failed to request enable GPIO%d: %d\n", config->ena_gpio, ret); goto wash; } - rdev->ena_gpio = config->ena_gpio; - rdev->ena_gpio_invert = config->ena_gpio_invert; - if (config->ena_gpio_flags & GPIOF_OUT_INIT_HIGH) rdev->ena_gpio_state = 1; - if (rdev->ena_gpio_invert) + if (config->ena_gpio_invert) rdev->ena_gpio_state = !rdev->ena_gpio_state; } @@ -3590,8 +3696,7 @@ unset_supplies: scrub: if (rdev->supply) _regulator_put(rdev->supply); - if (rdev->ena_gpio) - gpio_free(rdev->ena_gpio); + regulator_ena_gpio_free(rdev); kfree(rdev->constraints); wash: device_unregister(&rdev->dev); @@ -3626,8 +3731,7 @@ void regulator_unregister(struct regulator_dev *rdev) unset_regulator_supplies(rdev); list_del(&rdev->list); kfree(rdev->constraints); - if (rdev->ena_gpio) - gpio_free(rdev->ena_gpio); + regulator_ena_gpio_free(rdev); device_unregister(&rdev->dev); mutex_unlock(®ulator_list_mutex); } |