|
@@ -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;
|
|
|
|
|
@@ -68,6 +69,19 @@ struct regulator_map {
|
|
|
struct regulator_dev *regulator;
|
|
|
};
|
|
|
|
|
|
+/*
|
|
|
+ * 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
|
|
|
*
|
|
@@ -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);
|
|
|
}
|