Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 3dc06c1b authored by Mark Brown's avatar Mark Brown
Browse files

Merge remote-tracking branch 'regulator/topic/gpio' into v3.9-rc8

parents 5f19a85b 407945fd
Loading
Loading
Loading
Loading
+123 −19
Original line number Diff line number Diff line
@@ -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, &regulator_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, &regulator_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, &regulator_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 @@ regulator_register(const struct regulator_desc *regulator_desc,
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(&regulator_list_mutex);
}
+17 −81
Original line number Diff line number Diff line
@@ -184,40 +184,6 @@ static enum lp8788_ldo_id lp8788_aldo_id[] = {
	ALDO10,
};

static int lp8788_ldo_enable(struct regulator_dev *rdev)
{
	struct lp8788_ldo *ldo = rdev_get_drvdata(rdev);

	if (ldo->en_pin) {
		gpio_set_value(ldo->en_pin->gpio, ENABLE);
		return 0;
	} else {
		return regulator_enable_regmap(rdev);
	}
}

static int lp8788_ldo_disable(struct regulator_dev *rdev)
{
	struct lp8788_ldo *ldo = rdev_get_drvdata(rdev);

	if (ldo->en_pin) {
		gpio_set_value(ldo->en_pin->gpio, DISABLE);
		return 0;
	} else {
		return regulator_disable_regmap(rdev);
	}
}

static int lp8788_ldo_is_enabled(struct regulator_dev *rdev)
{
	struct lp8788_ldo *ldo = rdev_get_drvdata(rdev);

	if (ldo->en_pin)
		return gpio_get_value(ldo->en_pin->gpio) ? 1 : 0;
	else
		return regulator_is_enabled_regmap(rdev);
}

static int lp8788_ldo_enable_time(struct regulator_dev *rdev)
{
	struct lp8788_ldo *ldo = rdev_get_drvdata(rdev);
@@ -253,17 +219,17 @@ static struct regulator_ops lp8788_ldo_voltage_table_ops = {
	.list_voltage = regulator_list_voltage_table,
	.set_voltage_sel = regulator_set_voltage_sel_regmap,
	.get_voltage_sel = regulator_get_voltage_sel_regmap,
	.enable = lp8788_ldo_enable,
	.disable = lp8788_ldo_disable,
	.is_enabled = lp8788_ldo_is_enabled,
	.enable = regulator_enable_regmap,
	.disable = regulator_disable_regmap,
	.is_enabled = regulator_is_enabled_regmap,
	.enable_time = lp8788_ldo_enable_time,
};

static struct regulator_ops lp8788_ldo_voltage_fixed_ops = {
	.get_voltage = lp8788_ldo_fixed_get_voltage,
	.enable = lp8788_ldo_enable,
	.disable = lp8788_ldo_disable,
	.is_enabled = lp8788_ldo_is_enabled,
	.enable = regulator_enable_regmap,
	.disable = regulator_disable_regmap,
	.is_enabled = regulator_is_enabled_regmap,
	.enable_time = lp8788_ldo_enable_time,
};

@@ -535,43 +501,10 @@ static struct regulator_desc lp8788_aldo_desc[] = {
	},
};

static int lp8788_gpio_request_ldo_en(struct platform_device *pdev,
				struct lp8788_ldo *ldo,
				enum lp8788_ext_ldo_en_id id)
{
	struct device *dev = &pdev->dev;
	struct lp8788_ldo_enable_pin *pin = ldo->en_pin;
	int ret, gpio, pinstate;
	char *name[] = {
		[EN_ALDO1]   = "LP8788_EN_ALDO1",
		[EN_ALDO234] = "LP8788_EN_ALDO234",
		[EN_ALDO5]   = "LP8788_EN_ALDO5",
		[EN_ALDO7]   = "LP8788_EN_ALDO7",
		[EN_DLDO7]   = "LP8788_EN_DLDO7",
		[EN_DLDO911] = "LP8788_EN_DLDO911",
	};

	gpio = pin->gpio;
	if (!gpio_is_valid(gpio)) {
		dev_err(dev, "invalid gpio: %d\n", gpio);
		return -EINVAL;
	}

	pinstate = pin->init_state;
	ret = devm_gpio_request_one(dev, gpio, pinstate, name[id]);
	if (ret == -EBUSY) {
		dev_warn(dev, "gpio%d already used\n", gpio);
		return 0;
	}

	return ret;
}

static int lp8788_config_ldo_enable_mode(struct platform_device *pdev,
					struct lp8788_ldo *ldo,
					enum lp8788_ldo_id id)
{
	int ret;
	struct lp8788 *lp = ldo->lp;
	struct lp8788_platform_data *pdata = lp->pdata;
	enum lp8788_ext_ldo_en_id enable_id;
@@ -613,14 +546,7 @@ static int lp8788_config_ldo_enable_mode(struct platform_device *pdev,
		goto set_default_ldo_enable_mode;

	ldo->en_pin = pdata->ldo_pin[enable_id];

	ret = lp8788_gpio_request_ldo_en(pdev, ldo, enable_id);
	if (ret) {
		ldo->en_pin = NULL;
		goto set_default_ldo_enable_mode;
	}

	return ret;
	return 0;

set_default_ldo_enable_mode:
	return lp8788_update_bits(lp, LP8788_EN_SEL, en_mask[enable_id], 0);
@@ -644,6 +570,11 @@ static int lp8788_dldo_probe(struct platform_device *pdev)
	if (ret)
		return ret;

	if (ldo->en_pin) {
		cfg.ena_gpio = ldo->en_pin->gpio;
		cfg.ena_gpio_flags = ldo->en_pin->init_state;
	}

	cfg.dev = pdev->dev.parent;
	cfg.init_data = lp->pdata ? lp->pdata->dldo_data[id] : NULL;
	cfg.driver_data = ldo;
@@ -700,6 +631,11 @@ static int lp8788_aldo_probe(struct platform_device *pdev)
	if (ret)
		return ret;

	if (ldo->en_pin) {
		cfg.ena_gpio = ldo->en_pin->gpio;
		cfg.ena_gpio_flags = ldo->en_pin->init_state;
	}

	cfg.dev = pdev->dev.parent;
	cfg.init_data = lp->pdata ? lp->pdata->aldo_data[id] : NULL;
	cfg.driver_data = ldo;
+2 −2
Original line number Diff line number Diff line
@@ -22,6 +22,7 @@
struct regmap;
struct regulator_dev;
struct regulator_init_data;
struct regulator_enable_gpio;

enum regulator_status {
	REGULATOR_STATUS_OFF,
@@ -305,8 +306,7 @@ struct regulator_dev {

	struct dentry *debugfs;

	int ena_gpio;
	unsigned int ena_gpio_invert:1;
	struct regulator_enable_gpio *ena_pin;
	unsigned int ena_gpio_state:1;
};