diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c index da9782bd27d0..71d6adc4eeab 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; @@ -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 * @@ -1456,6 +1470,65 @@ 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--; + } + } + } +} + static int _regulator_do_enable(struct regulator_dev *rdev) { int ret, delay; @@ -3435,18 +3508,13 @@ 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; @@ -3522,8 +3590,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); @@ -3558,8 +3625,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); } diff --git a/include/linux/regulator/driver.h b/include/linux/regulator/driver.h index 23070fd83872..a467d11dd67d 100644 --- a/include/linux/regulator/driver.h +++ b/include/linux/regulator/driver.h @@ -22,6 +22,7 @@ struct regmap; struct regulator_dev; struct regulator_init_data; +struct regulator_enable_gpio; enum regulator_status { REGULATOR_STATUS_OFF, @@ -300,6 +301,7 @@ struct regulator_dev { struct dentry *debugfs; + struct regulator_enable_gpio *ena_pin; int ena_gpio; unsigned int ena_gpio_invert:1; unsigned int ena_gpio_state:1;