[PATCH v2 12/13] regulator: pwm: Support extra continuous mode cases

2016-06-08 Thread Boris Brezillon
The continuous mode allows one to declare a PWM regulator without having
to declare the voltage <-> dutycycle association table. It works fine as
long as your voltage(dutycycle) function is linear, but also has the
following constraints:

- dutycycle for min_uV = 0%
- dutycycle for max_uV = 100%
- dutycycle for min_uV < dutycycle for max_uV

While the linearity constraint is acceptable for now, we sometimes need to
restrict of the PWM range (to limit the maximum/minimum voltage for
example) or have a min_uV_dutycycle > max_uV_dutycycle (this could be
tweaked with PWM polarity, but not all PWMs support inverted polarity).

Add the pwm-dutycycle-range and pwm-dutycycle-unit DT properties to define
such constraints. If those properties are not defined, the PWM regulator
use the default pwm-dutycycle-range = <0 100> and
pwm-dutycycle-unit = <100> values (existing behavior).

Signed-off-by: Boris Brezillon 
Reviewed-by: Brian Norris 
Tested-by: Brian Norris 
---
 drivers/regulator/pwm-regulator.c | 92 +++
 1 file changed, 83 insertions(+), 9 deletions(-)

diff --git a/drivers/regulator/pwm-regulator.c 
b/drivers/regulator/pwm-regulator.c
index c39ecd1..3e8680a 100644
--- a/drivers/regulator/pwm-regulator.c
+++ b/drivers/regulator/pwm-regulator.c
@@ -21,6 +21,12 @@
 #include 
 #include 
 
+struct pwm_continuous_reg_data {
+   unsigned int min_uV_dutycycle;
+   unsigned int max_uV_dutycycle;
+   unsigned int dutycycle_unit;
+};
+
 struct pwm_regulator_data {
/*  Shared */
struct pwm_device *pwm;
@@ -28,6 +34,9 @@ struct pwm_regulator_data {
/* Voltage table */
struct pwm_voltages *duty_cycle_table;
 
+   /* Continuous mode info */
+   struct pwm_continuous_reg_data continuous;
+
/* regulator descriptor */
struct regulator_desc desc;
 
@@ -132,31 +141,79 @@ static int pwm_regulator_is_enabled(struct regulator_dev 
*dev)
 static int pwm_regulator_get_voltage(struct regulator_dev *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 diff = rdev->constraints->max_uV - 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;
 
pwm_get_state(drvdata->pwm, );
 
-   return min_uV + pwm_get_relative_duty_cycle(, diff);
+   voltage = pwm_get_relative_duty_cycle(, duty_unit);
+
+   /*
+* The dutycycle for the min_uV voltage might be greater than the
+* one for the 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,
-   int min_uV, int max_uV,
-   unsigned *selector)
+int req_min_uV, int req_max_uV,
+unsigned int *selector)
 {
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 req_diff = min_uV - rdev->constraints->min_uV;
+   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;
+   unsigned int diff_duty;
+   unsigned int dutycycle;
int ret;
 
pwm_prepare_new_state(drvdata->pwm, );
-   diff = rdev->constraints->max_uV - rdev->constraints->min_uV;
 
-   /* We pass diff as the scale to get a uV precision. */
-   pwm_set_relative_duty_cycle(, req_diff, diff);
+   /*
+* The dutycycle for the min_uV voltage might be greater than the
+* one for the max_uV. This is happening when the user needs an
+* inversed polarity, but the PWM device does not support inversing
+

[PATCH v2 12/13] regulator: pwm: Support extra continuous mode cases

2016-06-08 Thread Boris Brezillon
The continuous mode allows one to declare a PWM regulator without having
to declare the voltage <-> dutycycle association table. It works fine as
long as your voltage(dutycycle) function is linear, but also has the
following constraints:

- dutycycle for min_uV = 0%
- dutycycle for max_uV = 100%
- dutycycle for min_uV < dutycycle for max_uV

While the linearity constraint is acceptable for now, we sometimes need to
restrict of the PWM range (to limit the maximum/minimum voltage for
example) or have a min_uV_dutycycle > max_uV_dutycycle (this could be
tweaked with PWM polarity, but not all PWMs support inverted polarity).

Add the pwm-dutycycle-range and pwm-dutycycle-unit DT properties to define
such constraints. If those properties are not defined, the PWM regulator
use the default pwm-dutycycle-range = <0 100> and
pwm-dutycycle-unit = <100> values (existing behavior).

Signed-off-by: Boris Brezillon 
Reviewed-by: Brian Norris 
Tested-by: Brian Norris 
---
 drivers/regulator/pwm-regulator.c | 92 +++
 1 file changed, 83 insertions(+), 9 deletions(-)

diff --git a/drivers/regulator/pwm-regulator.c 
b/drivers/regulator/pwm-regulator.c
index c39ecd1..3e8680a 100644
--- a/drivers/regulator/pwm-regulator.c
+++ b/drivers/regulator/pwm-regulator.c
@@ -21,6 +21,12 @@
 #include 
 #include 
 
+struct pwm_continuous_reg_data {
+   unsigned int min_uV_dutycycle;
+   unsigned int max_uV_dutycycle;
+   unsigned int dutycycle_unit;
+};
+
 struct pwm_regulator_data {
/*  Shared */
struct pwm_device *pwm;
@@ -28,6 +34,9 @@ struct pwm_regulator_data {
/* Voltage table */
struct pwm_voltages *duty_cycle_table;
 
+   /* Continuous mode info */
+   struct pwm_continuous_reg_data continuous;
+
/* regulator descriptor */
struct regulator_desc desc;
 
@@ -132,31 +141,79 @@ static int pwm_regulator_is_enabled(struct regulator_dev 
*dev)
 static int pwm_regulator_get_voltage(struct regulator_dev *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 diff = rdev->constraints->max_uV - 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;
 
pwm_get_state(drvdata->pwm, );
 
-   return min_uV + pwm_get_relative_duty_cycle(, diff);
+   voltage = pwm_get_relative_duty_cycle(, duty_unit);
+
+   /*
+* The dutycycle for the min_uV voltage might be greater than the
+* one for the 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,
-   int min_uV, int max_uV,
-   unsigned *selector)
+int req_min_uV, int req_max_uV,
+unsigned int *selector)
 {
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 req_diff = min_uV - rdev->constraints->min_uV;
+   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;
+   unsigned int diff_duty;
+   unsigned int dutycycle;
int ret;
 
pwm_prepare_new_state(drvdata->pwm, );
-   diff = rdev->constraints->max_uV - rdev->constraints->min_uV;
 
-   /* We pass diff as the scale to get a uV precision. */
-   pwm_set_relative_duty_cycle(, req_diff, diff);
+   /*
+* The dutycycle for the min_uV voltage might be greater than the
+* one for the 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)
+