Commit 53de7c26 authored by Thierry Reding's avatar Thierry Reding
Browse files

Merge branch 'for-4.8/regulator' into for-next

parents 070d9a93 58fd822b
Loading
Loading
Loading
Loading
+25 −1
Original line number Original line Diff line number Diff line
@@ -34,20 +34,44 @@ Only required for Voltage Table Mode:
			    First cell is voltage in microvolts (uV)
			    First cell is voltage in microvolts (uV)
			    Second cell is duty-cycle in percent (%)
			    Second cell is duty-cycle in percent (%)


Optional properties for Continuous mode:
- pwm-dutycycle-unit:	Integer value encoding the duty cycle unit. If not
			defined, <100> is assumed, meaning that
			pwm-dutycycle-range contains values expressed in
			percent.

- pwm-dutycycle-range:	Should contain 2 entries. The first entry is encoding
			the dutycycle for regulator-min-microvolt and the
			second one the dutycycle for regulator-max-microvolt.
			Duty cycle values are expressed in pwm-dutycycle-unit.
			If not defined, <0 100> is assumed.

NB: To be clear, if voltage-table is provided, then the device will be used
NB: To be clear, if voltage-table is provided, then the device will be used
in Voltage Table Mode.  If no voltage-table is provided, then the device will
in Voltage Table Mode.  If no voltage-table is provided, then the device will
be used in Continuous Voltage Mode.
be used in Continuous Voltage Mode.


Optional properties:
--------------------
- enable-gpios:		GPIO to use to enable/disable the regulator

Any property defined as part of the core regulator binding can also be used.
Any property defined as part of the core regulator binding can also be used.
(See: ../regulator/regulator.txt)
(See: ../regulator/regulator.txt)


Continuous Voltage Example:
Continuous Voltage With Enable GPIO Example:
	pwm_regulator {
	pwm_regulator {
		compatible = "pwm-regulator;
		compatible = "pwm-regulator;
		pwms = <&pwm1 0 8448 0>;
		pwms = <&pwm1 0 8448 0>;
		enable-gpios = <&gpio0 23 GPIO_ACTIVE_HIGH>;
		regulator-min-microvolt = <1016000>;
		regulator-min-microvolt = <1016000>;
		regulator-max-microvolt = <1114000>;
		regulator-max-microvolt = <1114000>;
		regulator-name = "vdd_logic";
		regulator-name = "vdd_logic";
		/* unit == per-mille */
		pwm-dutycycle-unit = <1000>;
		/*
		 * Inverted PWM logic, and the duty cycle range is limited
		 * to 30%-70%.
		 */
		pwm-dutycycle-range <700 300>; /* */
	};
	};


Voltage Table Example:
Voltage Table Example:
+148 −48
Original line number Original line Diff line number Diff line
@@ -20,6 +20,13 @@
#include <linux/of.h>
#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/of_device.h>
#include <linux/pwm.h>
#include <linux/pwm.h>
#include <linux/gpio/consumer.h>

struct pwm_continuous_reg_data {
	unsigned int min_uV_dutycycle;
	unsigned int max_uV_dutycycle;
	unsigned int dutycycle_unit;
};


struct pwm_regulator_data {
struct pwm_regulator_data {
	/*  Shared */
	/*  Shared */
@@ -28,6 +35,9 @@ struct pwm_regulator_data {
	/* Voltage table */
	/* Voltage table */
	struct pwm_voltages *duty_cycle_table;
	struct pwm_voltages *duty_cycle_table;


	/* Continuous mode info */
	struct pwm_continuous_reg_data continuous;

	/* regulator descriptor */
	/* regulator descriptor */
	struct regulator_desc desc;
	struct regulator_desc desc;


@@ -36,8 +46,8 @@ struct pwm_regulator_data {


	int state;
	int state;


	/* Continuous voltage */
	/* Enable GPIO */
	int volt_uV;
	struct gpio_desc *enb_gpio;
};
};


struct pwm_voltages {
struct pwm_voltages {
@@ -48,10 +58,31 @@ struct pwm_voltages {
/**
/**
 * Voltage table call-backs
 * Voltage table call-backs
 */
 */
static void pwm_regulator_init_state(struct regulator_dev *rdev)
{
	struct pwm_regulator_data *drvdata = rdev_get_drvdata(rdev);
	struct pwm_state pwm_state;
	unsigned int dutycycle;
	int i;

	pwm_get_state(drvdata->pwm, &pwm_state);
	dutycycle = pwm_get_relative_duty_cycle(&pwm_state, 100);

	for (i = 0; i < rdev->desc->n_voltages; i++) {
		if (dutycycle == drvdata->duty_cycle_table[i].dutycycle) {
			drvdata->state = i;
			return;
		}
	}
}

static int pwm_regulator_get_voltage_sel(struct regulator_dev *rdev)
static int pwm_regulator_get_voltage_sel(struct regulator_dev *rdev)
{
{
	struct pwm_regulator_data *drvdata = rdev_get_drvdata(rdev);
	struct pwm_regulator_data *drvdata = rdev_get_drvdata(rdev);


	if (drvdata->state < 0)
		pwm_regulator_init_state(rdev);

	return drvdata->state;
	return drvdata->state;
}
}


@@ -59,16 +90,14 @@ static int pwm_regulator_set_voltage_sel(struct regulator_dev *rdev,
					 unsigned selector)
					 unsigned selector)
{
{
	struct pwm_regulator_data *drvdata = rdev_get_drvdata(rdev);
	struct pwm_regulator_data *drvdata = rdev_get_drvdata(rdev);
	struct pwm_args pargs;
	struct pwm_state pstate;
	int dutycycle;
	int ret;
	int ret;


	pwm_get_args(drvdata->pwm, &pargs);
	pwm_init_state(drvdata->pwm, &pstate);
	pwm_set_relative_duty_cycle(&pstate,
			drvdata->duty_cycle_table[selector].dutycycle, 100);


	dutycycle = (pargs.period *
	ret = pwm_apply_state(drvdata->pwm, &pstate);
		    drvdata->duty_cycle_table[selector].dutycycle) / 100;

	ret = pwm_config(drvdata->pwm, dutycycle, pargs.period);
	if (ret) {
	if (ret) {
		dev_err(&rdev->dev, "Failed to configure PWM: %d\n", ret);
		dev_err(&rdev->dev, "Failed to configure PWM: %d\n", ret);
		return ret;
		return ret;
@@ -94,6 +123,9 @@ static int pwm_regulator_enable(struct regulator_dev *dev)
{
{
	struct pwm_regulator_data *drvdata = rdev_get_drvdata(dev);
	struct pwm_regulator_data *drvdata = rdev_get_drvdata(dev);


	if (drvdata->enb_gpio)
		gpiod_set_value_cansleep(drvdata->enb_gpio, 1);

	return pwm_enable(drvdata->pwm);
	return pwm_enable(drvdata->pwm);
}
}


@@ -103,6 +135,9 @@ static int pwm_regulator_disable(struct regulator_dev *dev)


	pwm_disable(drvdata->pwm);
	pwm_disable(drvdata->pwm);


	if (drvdata->enb_gpio)
		gpiod_set_value_cansleep(drvdata->enb_gpio, 0);

	return 0;
	return 0;
}
}


@@ -110,64 +145,100 @@ static int pwm_regulator_is_enabled(struct regulator_dev *dev)
{
{
	struct pwm_regulator_data *drvdata = rdev_get_drvdata(dev);
	struct pwm_regulator_data *drvdata = rdev_get_drvdata(dev);


	if (drvdata->enb_gpio && !gpiod_get_value_cansleep(drvdata->enb_gpio))
		return false;

	return pwm_is_enabled(drvdata->pwm);
	return pwm_is_enabled(drvdata->pwm);
}
}


static int pwm_regulator_get_voltage(struct regulator_dev *rdev)
static int pwm_regulator_get_voltage(struct regulator_dev *rdev)
{
{
	struct pwm_regulator_data *drvdata = rdev_get_drvdata(rdev);
	struct pwm_regulator_data *drvdata = rdev_get_drvdata(rdev);
	unsigned int min_uV_duty = drvdata->continuous.min_uV_dutycycle;
	unsigned int max_uV_duty = drvdata->continuous.max_uV_dutycycle;
	unsigned int duty_unit = drvdata->continuous.dutycycle_unit;
	int min_uV = rdev->constraints->min_uV;
	int max_uV = rdev->constraints->max_uV;
	int diff_uV = max_uV - min_uV;
	struct pwm_state pstate;
	unsigned int diff_duty;
	unsigned int voltage;


	return drvdata->volt_uV;
	pwm_get_state(drvdata->pwm, &pstate);

	voltage = pwm_get_relative_duty_cycle(&pstate, duty_unit);

	/*
	 * The dutycycle for min_uV might be greater than the one for max_uV.
	 * This is happening when the user needs an inversed polarity, but the
	 * PWM device does not support inversing it in hardware.
	 */
	if (max_uV_duty < min_uV_duty) {
		voltage = min_uV_duty - voltage;
		diff_duty = min_uV_duty - max_uV_duty;
	} else {
		voltage = voltage - min_uV_duty;
		diff_duty = max_uV_duty - min_uV_duty;
	}

	voltage = DIV_ROUND_CLOSEST_ULL((u64)voltage * diff_uV, diff_duty);

	return voltage + min_uV;
}
}


static int pwm_regulator_set_voltage(struct regulator_dev *rdev,
static int pwm_regulator_set_voltage(struct regulator_dev *rdev,
					int min_uV, int max_uV,
				     int req_min_uV, int req_max_uV,
					unsigned *selector)
				     unsigned int *selector)
{
{
	struct pwm_regulator_data *drvdata = rdev_get_drvdata(rdev);
	struct pwm_regulator_data *drvdata = rdev_get_drvdata(rdev);
	unsigned int min_uV_duty = drvdata->continuous.min_uV_dutycycle;
	unsigned int max_uV_duty = drvdata->continuous.max_uV_dutycycle;
	unsigned int duty_unit = drvdata->continuous.dutycycle_unit;
	unsigned int ramp_delay = rdev->constraints->ramp_delay;
	unsigned int ramp_delay = rdev->constraints->ramp_delay;
	struct pwm_args pargs;
	int min_uV = rdev->constraints->min_uV;
	unsigned int req_diff = min_uV - rdev->constraints->min_uV;
	int max_uV = rdev->constraints->max_uV;
	unsigned int diff;
	int diff_uV = max_uV - min_uV;
	unsigned int duty_pulse;
	struct pwm_state pstate;
	u64 req_period;
	int old_uV = pwm_regulator_get_voltage(rdev);
	u32 rem;
	unsigned int diff_duty;
	unsigned int dutycycle;
	int ret;
	int ret;


	pwm_get_args(drvdata->pwm, &pargs);
	pwm_init_state(drvdata->pwm, &pstate);
	diff = rdev->constraints->max_uV - rdev->constraints->min_uV;


	/* First try to find out if we get the iduty cycle time which is
	/*
	 * factor of PWM period time. If (request_diff_to_min * pwm_period)
	 * The dutycycle for min_uV might be greater than the one for max_uV.
	 * is perfect divided by voltage_range_diff then it is possible to
	 * This is happening when the user needs an inversed polarity, but the
	 * get duty cycle time which is factor of PWM period. This will help
	 * PWM device does not support inversing it in hardware.
	 * to get output voltage nearer to requested value as there is no
	 * calculation loss.
	 */
	 */
	req_period = req_diff * pargs.period;
	if (max_uV_duty < min_uV_duty)
	div_u64_rem(req_period, diff, &rem);
		diff_duty = min_uV_duty - max_uV_duty;
	if (!rem) {
	else
		do_div(req_period, diff);
		diff_duty = max_uV_duty - min_uV_duty;
		duty_pulse = (unsigned int)req_period;

	} else {
	dutycycle = DIV_ROUND_CLOSEST_ULL((u64)(req_min_uV - min_uV) *
		duty_pulse = (pargs.period / 100) * ((req_diff * 100) / diff);
					  diff_duty,
	}
					  diff_uV);

	if (max_uV_duty < min_uV_duty)
		dutycycle = min_uV_duty - dutycycle;
	else
		dutycycle = min_uV_duty + dutycycle;


	ret = pwm_config(drvdata->pwm, duty_pulse, pargs.period);
	pwm_set_relative_duty_cycle(&pstate, dutycycle, duty_unit);

	ret = pwm_apply_state(drvdata->pwm, &pstate);
	if (ret) {
	if (ret) {
		dev_err(&rdev->dev, "Failed to configure PWM: %d\n", ret);
		dev_err(&rdev->dev, "Failed to configure PWM: %d\n", ret);
		return ret;
		return ret;
	}
	}


	ret = pwm_enable(drvdata->pwm);
	if ((ramp_delay == 0) || !pwm_regulator_is_enabled(rdev))
	if (ret) {
		return 0;
		dev_err(&rdev->dev, "Failed to enable PWM: %d\n", ret);
		return ret;
	}
	drvdata->volt_uV = min_uV;


	/* Delay required by PWM regulator to settle to the new voltage */
	/* Ramp delay is in uV/uS. Adjust to uS and delay */
	usleep_range(ramp_delay, ramp_delay + 1000);
	ramp_delay = DIV_ROUND_UP(abs(req_min_uV - old_uV), ramp_delay);
	usleep_range(ramp_delay, ramp_delay + DIV_ROUND_UP(ramp_delay, 10));


	return 0;
	return 0;
}
}
@@ -226,6 +297,7 @@ static int pwm_regulator_init_table(struct platform_device *pdev,
		return ret;
		return ret;
	}
	}


	drvdata->state			= -EINVAL;
	drvdata->duty_cycle_table	= duty_cycle_table;
	drvdata->duty_cycle_table	= duty_cycle_table;
	memcpy(&drvdata->ops, &pwm_regulator_voltage_table_ops,
	memcpy(&drvdata->ops, &pwm_regulator_voltage_table_ops,
	       sizeof(drvdata->ops));
	       sizeof(drvdata->ops));
@@ -238,11 +310,28 @@ static int pwm_regulator_init_table(struct platform_device *pdev,
static int pwm_regulator_init_continuous(struct platform_device *pdev,
static int pwm_regulator_init_continuous(struct platform_device *pdev,
					 struct pwm_regulator_data *drvdata)
					 struct pwm_regulator_data *drvdata)
{
{
	u32 dutycycle_range[2] = { 0, 100 };
	u32 dutycycle_unit = 100;

	memcpy(&drvdata->ops, &pwm_regulator_voltage_continuous_ops,
	memcpy(&drvdata->ops, &pwm_regulator_voltage_continuous_ops,
	       sizeof(drvdata->ops));
	       sizeof(drvdata->ops));
	drvdata->desc.ops = &drvdata->ops;
	drvdata->desc.ops = &drvdata->ops;
	drvdata->desc.continuous_voltage_range = true;
	drvdata->desc.continuous_voltage_range = true;


	of_property_read_u32_array(pdev->dev.of_node,
				   "pwm-dutycycle-range",
				   dutycycle_range, 2);
	of_property_read_u32(pdev->dev.of_node, "pwm-dutycycle-unit",
			     &dutycycle_unit);

	if (dutycycle_range[0] > dutycycle_unit ||
	    dutycycle_range[1] > dutycycle_unit)
		return -EINVAL;

	drvdata->continuous.dutycycle_unit = dutycycle_unit;
	drvdata->continuous.min_uV_dutycycle = dutycycle_range[0];
	drvdata->continuous.max_uV_dutycycle = dutycycle_range[1];

	return 0;
	return 0;
}
}


@@ -253,6 +342,7 @@ static int pwm_regulator_probe(struct platform_device *pdev)
	struct regulator_dev *regulator;
	struct regulator_dev *regulator;
	struct regulator_config config = { };
	struct regulator_config config = { };
	struct device_node *np = pdev->dev.of_node;
	struct device_node *np = pdev->dev.of_node;
	enum gpiod_flags gpio_flags;
	int ret;
	int ret;


	if (!np) {
	if (!np) {
@@ -290,11 +380,21 @@ static int pwm_regulator_probe(struct platform_device *pdev)
		return ret;
		return ret;
	}
	}


	/*
	if (init_data->constraints.boot_on || init_data->constraints.always_on)
	 * FIXME: pwm_apply_args() should be removed when switching to the
		gpio_flags = GPIOD_OUT_HIGH;
	 * atomic PWM API.
	else
	 */
		gpio_flags = GPIOD_OUT_LOW;
	pwm_apply_args(drvdata->pwm);
	drvdata->enb_gpio = devm_gpiod_get_optional(&pdev->dev, "enable",
						    gpio_flags);
	if (IS_ERR(drvdata->enb_gpio)) {
		ret = PTR_ERR(drvdata->enb_gpio);
		dev_err(&pdev->dev, "Failed to get enable GPIO: %d\n", ret);
		return ret;
	}

	ret = pwm_adjust_config(drvdata->pwm);
	if (ret)
		return ret;


	regulator = devm_regulator_register(&pdev->dev,
	regulator = devm_regulator_register(&pdev->dev,
					    &drvdata->desc, &config);
					    &drvdata->desc, &config);