Re: [PATCH v3 06/11] clk: tegra: dfll: support PWM regulator control

2018-03-09 Thread Peter De Schrijver
On Thu, Mar 08, 2018 at 11:15:17PM +, Jon Hunter wrote:
> 
> On 06/02/18 16:34, Peter De Schrijver wrote:
> > The DFLL can directly generate a PWM signal to control the regulator IC
> > rather then sending i2c messages. This patch adds support for this mode.
> > In this mode the hardware LUT is not used and also there is no regulator
> > object involved because there is no way to control the regulator voltage
> > without also changing the DFLL output frequency. Also the register debugfs
> > file is slightly reworked to only show the i2c registers when i2c mode is
> > in use. As there is no regulator object for the PWM regulator, its step and
> > offset values are retrieved from DT instead.
> 
> It is unclear to me why we bother creating the LUT for PWM if it is not
> used? Is this for debug to get an approximation? Why do we do this?
> 

lut_uv certainly is used. This makes it easier to abstract PWM vs i2c. It
would also be possible to replace every reference to lut_uv with a function
which calculates the corresponding voltage by either querying the regulator
framework in case of i2c or doing alignment.offset_uv + x * alignment.step_uv
in case of PWM. Doing this once seems a bit easier to me.

> > Signed-off-by: Peter De Schrijver 
> > ---
> >  drivers/clk/tegra/clk-dfll.c   | 398 
> > ++---
> >  drivers/clk/tegra/clk-tegra124-dfll-fcpu.c |  23 +-
> >  2 files changed, 382 insertions(+), 39 deletions(-)
> > 
> > diff --git a/drivers/clk/tegra/clk-dfll.c b/drivers/clk/tegra/clk-dfll.c
> > index fa97763..228edb4 100644
> > --- a/drivers/clk/tegra/clk-dfll.c
> > +++ b/drivers/clk/tegra/clk-dfll.c
> > @@ -243,6 +243,12 @@ enum dfll_tune_range {
> > DFLL_TUNE_LOW = 1,
> >  };
> >  
> > +
> > +enum tegra_dfll_pmu_if {
> > +   TEGRA_DFLL_PMU_I2C = 0,
> > +   TEGRA_DFLL_PMU_PWM = 1,
> > +};
> > +
> >  /**
> >   * struct dfll_rate_req - target DFLL rate request data
> >   * @rate: target frequency, after the postscaling
> > @@ -294,17 +300,25 @@ struct tegra_dfll {
> > u32 ci;
> > u32 cg;
> > boolcg_scale;
> > +   u32 reg_init_uV;
> >  
> > /* I2C interface parameters */
> > u32 i2c_fs_rate;
> > u32 i2c_reg;
> > u32 i2c_slave_addr;
> >  
> > -   /* i2c_lut array entries are regulator framework selectors */
> > +   /* lut array entries are regulator framework selectors or PWM values*/
> > unsigned inti2c_lut[MAX_DFLL_VOLTAGES];
> > unsigned intlut_uv[MAX_DFLL_VOLTAGES];
> > int lut_size;
> > u8  lut_bottom, lut_min, lut_max, lut_safe;
> > +
> > +   /* PWM interface */
> > +   enum tegra_dfll_pmu_if  pmu_if;
> > +   unsigned long   pwm_rate;
> > +   struct pinctrl  *pwm_pin;
> > +   struct pinctrl_state*pwm_enable_state;
> > +   struct pinctrl_state*pwm_disable_state;
> >  };
> >  
> >  #define clk_hw_to_dfll(_hw) container_of(_hw, struct tegra_dfll, 
> > dfll_clk_hw)
> > @@ -491,6 +505,36 @@ static void dfll_set_mode(struct tegra_dfll *td,
> >  }
> >  
> >  /*
> > + * DVCO rate control
> > + */
> > +
> > +static unsigned long get_dvco_rate_below(struct tegra_dfll *td, u8 out_min)
> > +{
> > +   struct dev_pm_opp *opp;
> > +   unsigned long rate, prev_rate;
> > +   int uv, min_uv;
> > +
> > +   min_uv = td->lut_uv[out_min];
> > +   for (rate = 0, prev_rate = 0; ; rate++) {
> > +   rcu_read_lock();
> > +   opp = dev_pm_opp_find_freq_ceil(td->soc->dev, );
> > +   if (IS_ERR(opp)) {
> > +   rcu_read_unlock();
> > +   break;
> > +   }
> > +   uv = dev_pm_opp_get_voltage(opp);
> > +   rcu_read_unlock();
> > +
> > +   if (uv && uv > min_uv)
> > +   return prev_rate;
> > +
> > +   prev_rate = rate;
> > +   }
> > +
> > +   return prev_rate;
> > +}
> > +
> > +/*
> >   * DFLL-to-I2C controller interface
> >   */
> >  
> > @@ -519,6 +563,119 @@ static int dfll_i2c_set_output_enabled(struct 
> > tegra_dfll *td, bool enable)
> > return 0;
> >  }
> >  
> > +
> > +/*
> > + * DFLL-to-PWM controller interface
> > + */
> > +
> > +/**
> > + * dfll_pwm_set_output_enabled - enable/disable PWM voltage requests
> > + * @td: DFLL instance
> > + * @enable: whether to enable or disable the PWM voltage requests
> > + *
> > + * Set the master enable control for PWM control value updates. If 
> > disabled,
> > + * then the PWM signal is not driven. Also configure the PWM output pad
> > + * to the appropriate state.
> > + */
> > +static int dfll_pwm_set_output_enabled(struct tegra_dfll *td, bool enable)
> > +{
> > +   int ret;
> > +   u32 val, div;
> > +
> > +   if 

Re: [PATCH v3 06/11] clk: tegra: dfll: support PWM regulator control

2018-03-09 Thread Peter De Schrijver
On Thu, Mar 08, 2018 at 11:15:17PM +, Jon Hunter wrote:
> 
> On 06/02/18 16:34, Peter De Schrijver wrote:
> > The DFLL can directly generate a PWM signal to control the regulator IC
> > rather then sending i2c messages. This patch adds support for this mode.
> > In this mode the hardware LUT is not used and also there is no regulator
> > object involved because there is no way to control the regulator voltage
> > without also changing the DFLL output frequency. Also the register debugfs
> > file is slightly reworked to only show the i2c registers when i2c mode is
> > in use. As there is no regulator object for the PWM regulator, its step and
> > offset values are retrieved from DT instead.
> 
> It is unclear to me why we bother creating the LUT for PWM if it is not
> used? Is this for debug to get an approximation? Why do we do this?
> 

lut_uv certainly is used. This makes it easier to abstract PWM vs i2c. It
would also be possible to replace every reference to lut_uv with a function
which calculates the corresponding voltage by either querying the regulator
framework in case of i2c or doing alignment.offset_uv + x * alignment.step_uv
in case of PWM. Doing this once seems a bit easier to me.

> > Signed-off-by: Peter De Schrijver 
> > ---
> >  drivers/clk/tegra/clk-dfll.c   | 398 
> > ++---
> >  drivers/clk/tegra/clk-tegra124-dfll-fcpu.c |  23 +-
> >  2 files changed, 382 insertions(+), 39 deletions(-)
> > 
> > diff --git a/drivers/clk/tegra/clk-dfll.c b/drivers/clk/tegra/clk-dfll.c
> > index fa97763..228edb4 100644
> > --- a/drivers/clk/tegra/clk-dfll.c
> > +++ b/drivers/clk/tegra/clk-dfll.c
> > @@ -243,6 +243,12 @@ enum dfll_tune_range {
> > DFLL_TUNE_LOW = 1,
> >  };
> >  
> > +
> > +enum tegra_dfll_pmu_if {
> > +   TEGRA_DFLL_PMU_I2C = 0,
> > +   TEGRA_DFLL_PMU_PWM = 1,
> > +};
> > +
> >  /**
> >   * struct dfll_rate_req - target DFLL rate request data
> >   * @rate: target frequency, after the postscaling
> > @@ -294,17 +300,25 @@ struct tegra_dfll {
> > u32 ci;
> > u32 cg;
> > boolcg_scale;
> > +   u32 reg_init_uV;
> >  
> > /* I2C interface parameters */
> > u32 i2c_fs_rate;
> > u32 i2c_reg;
> > u32 i2c_slave_addr;
> >  
> > -   /* i2c_lut array entries are regulator framework selectors */
> > +   /* lut array entries are regulator framework selectors or PWM values*/
> > unsigned inti2c_lut[MAX_DFLL_VOLTAGES];
> > unsigned intlut_uv[MAX_DFLL_VOLTAGES];
> > int lut_size;
> > u8  lut_bottom, lut_min, lut_max, lut_safe;
> > +
> > +   /* PWM interface */
> > +   enum tegra_dfll_pmu_if  pmu_if;
> > +   unsigned long   pwm_rate;
> > +   struct pinctrl  *pwm_pin;
> > +   struct pinctrl_state*pwm_enable_state;
> > +   struct pinctrl_state*pwm_disable_state;
> >  };
> >  
> >  #define clk_hw_to_dfll(_hw) container_of(_hw, struct tegra_dfll, 
> > dfll_clk_hw)
> > @@ -491,6 +505,36 @@ static void dfll_set_mode(struct tegra_dfll *td,
> >  }
> >  
> >  /*
> > + * DVCO rate control
> > + */
> > +
> > +static unsigned long get_dvco_rate_below(struct tegra_dfll *td, u8 out_min)
> > +{
> > +   struct dev_pm_opp *opp;
> > +   unsigned long rate, prev_rate;
> > +   int uv, min_uv;
> > +
> > +   min_uv = td->lut_uv[out_min];
> > +   for (rate = 0, prev_rate = 0; ; rate++) {
> > +   rcu_read_lock();
> > +   opp = dev_pm_opp_find_freq_ceil(td->soc->dev, );
> > +   if (IS_ERR(opp)) {
> > +   rcu_read_unlock();
> > +   break;
> > +   }
> > +   uv = dev_pm_opp_get_voltage(opp);
> > +   rcu_read_unlock();
> > +
> > +   if (uv && uv > min_uv)
> > +   return prev_rate;
> > +
> > +   prev_rate = rate;
> > +   }
> > +
> > +   return prev_rate;
> > +}
> > +
> > +/*
> >   * DFLL-to-I2C controller interface
> >   */
> >  
> > @@ -519,6 +563,119 @@ static int dfll_i2c_set_output_enabled(struct 
> > tegra_dfll *td, bool enable)
> > return 0;
> >  }
> >  
> > +
> > +/*
> > + * DFLL-to-PWM controller interface
> > + */
> > +
> > +/**
> > + * dfll_pwm_set_output_enabled - enable/disable PWM voltage requests
> > + * @td: DFLL instance
> > + * @enable: whether to enable or disable the PWM voltage requests
> > + *
> > + * Set the master enable control for PWM control value updates. If 
> > disabled,
> > + * then the PWM signal is not driven. Also configure the PWM output pad
> > + * to the appropriate state.
> > + */
> > +static int dfll_pwm_set_output_enabled(struct tegra_dfll *td, bool enable)
> > +{
> > +   int ret;
> > +   u32 val, div;
> > +
> > +   if (enable) {
> > +   ret 

Re: [PATCH v3 06/11] clk: tegra: dfll: support PWM regulator control

2018-03-08 Thread Jon Hunter

On 06/02/18 16:34, Peter De Schrijver wrote:
> The DFLL can directly generate a PWM signal to control the regulator IC
> rather then sending i2c messages. This patch adds support for this mode.
> In this mode the hardware LUT is not used and also there is no regulator
> object involved because there is no way to control the regulator voltage
> without also changing the DFLL output frequency. Also the register debugfs
> file is slightly reworked to only show the i2c registers when i2c mode is
> in use. As there is no regulator object for the PWM regulator, its step and
> offset values are retrieved from DT instead.

It is unclear to me why we bother creating the LUT for PWM if it is not
used? Is this for debug to get an approximation? Why do we do this?

> Signed-off-by: Peter De Schrijver 
> ---
>  drivers/clk/tegra/clk-dfll.c   | 398 
> ++---
>  drivers/clk/tegra/clk-tegra124-dfll-fcpu.c |  23 +-
>  2 files changed, 382 insertions(+), 39 deletions(-)
> 
> diff --git a/drivers/clk/tegra/clk-dfll.c b/drivers/clk/tegra/clk-dfll.c
> index fa97763..228edb4 100644
> --- a/drivers/clk/tegra/clk-dfll.c
> +++ b/drivers/clk/tegra/clk-dfll.c
> @@ -243,6 +243,12 @@ enum dfll_tune_range {
>   DFLL_TUNE_LOW = 1,
>  };
>  
> +
> +enum tegra_dfll_pmu_if {
> + TEGRA_DFLL_PMU_I2C = 0,
> + TEGRA_DFLL_PMU_PWM = 1,
> +};
> +
>  /**
>   * struct dfll_rate_req - target DFLL rate request data
>   * @rate: target frequency, after the postscaling
> @@ -294,17 +300,25 @@ struct tegra_dfll {
>   u32 ci;
>   u32 cg;
>   boolcg_scale;
> + u32 reg_init_uV;
>  
>   /* I2C interface parameters */
>   u32 i2c_fs_rate;
>   u32 i2c_reg;
>   u32 i2c_slave_addr;
>  
> - /* i2c_lut array entries are regulator framework selectors */
> + /* lut array entries are regulator framework selectors or PWM values*/
>   unsigned inti2c_lut[MAX_DFLL_VOLTAGES];
>   unsigned intlut_uv[MAX_DFLL_VOLTAGES];
>   int lut_size;
>   u8  lut_bottom, lut_min, lut_max, lut_safe;
> +
> + /* PWM interface */
> + enum tegra_dfll_pmu_if  pmu_if;
> + unsigned long   pwm_rate;
> + struct pinctrl  *pwm_pin;
> + struct pinctrl_state*pwm_enable_state;
> + struct pinctrl_state*pwm_disable_state;
>  };
>  
>  #define clk_hw_to_dfll(_hw) container_of(_hw, struct tegra_dfll, dfll_clk_hw)
> @@ -491,6 +505,36 @@ static void dfll_set_mode(struct tegra_dfll *td,
>  }
>  
>  /*
> + * DVCO rate control
> + */
> +
> +static unsigned long get_dvco_rate_below(struct tegra_dfll *td, u8 out_min)
> +{
> + struct dev_pm_opp *opp;
> + unsigned long rate, prev_rate;
> + int uv, min_uv;
> +
> + min_uv = td->lut_uv[out_min];
> + for (rate = 0, prev_rate = 0; ; rate++) {
> + rcu_read_lock();
> + opp = dev_pm_opp_find_freq_ceil(td->soc->dev, );
> + if (IS_ERR(opp)) {
> + rcu_read_unlock();
> + break;
> + }
> + uv = dev_pm_opp_get_voltage(opp);
> + rcu_read_unlock();
> +
> + if (uv && uv > min_uv)
> + return prev_rate;
> +
> + prev_rate = rate;
> + }
> +
> + return prev_rate;
> +}
> +
> +/*
>   * DFLL-to-I2C controller interface
>   */
>  
> @@ -519,6 +563,119 @@ static int dfll_i2c_set_output_enabled(struct 
> tegra_dfll *td, bool enable)
>   return 0;
>  }
>  
> +
> +/*
> + * DFLL-to-PWM controller interface
> + */
> +
> +/**
> + * dfll_pwm_set_output_enabled - enable/disable PWM voltage requests
> + * @td: DFLL instance
> + * @enable: whether to enable or disable the PWM voltage requests
> + *
> + * Set the master enable control for PWM control value updates. If disabled,
> + * then the PWM signal is not driven. Also configure the PWM output pad
> + * to the appropriate state.
> + */
> +static int dfll_pwm_set_output_enabled(struct tegra_dfll *td, bool enable)
> +{
> + int ret;
> + u32 val, div;
> +
> + if (enable) {
> + ret = pinctrl_select_state(td->pwm_pin, td->pwm_enable_state);
> + if (ret < 0) {
> + dev_err(td->dev, "setting enable state failed\n");
> + return ret;
> + }
> + val = dfll_readl(td, DFLL_OUTPUT_CFG);
> + val &= ~DFLL_OUTPUT_CFG_PWM_DIV_MASK;
> + div = DIV_ROUND_UP(td->ref_rate, td->pwm_rate);
> + val |= (div << DFLL_OUTPUT_CFG_PWM_DIV_SHIFT)
> + & DFLL_OUTPUT_CFG_PWM_DIV_MASK;
> + dfll_writel(td, val, DFLL_OUTPUT_CFG);

Re: [PATCH v3 06/11] clk: tegra: dfll: support PWM regulator control

2018-03-08 Thread Jon Hunter

On 06/02/18 16:34, Peter De Schrijver wrote:
> The DFLL can directly generate a PWM signal to control the regulator IC
> rather then sending i2c messages. This patch adds support for this mode.
> In this mode the hardware LUT is not used and also there is no regulator
> object involved because there is no way to control the regulator voltage
> without also changing the DFLL output frequency. Also the register debugfs
> file is slightly reworked to only show the i2c registers when i2c mode is
> in use. As there is no regulator object for the PWM regulator, its step and
> offset values are retrieved from DT instead.

It is unclear to me why we bother creating the LUT for PWM if it is not
used? Is this for debug to get an approximation? Why do we do this?

> Signed-off-by: Peter De Schrijver 
> ---
>  drivers/clk/tegra/clk-dfll.c   | 398 
> ++---
>  drivers/clk/tegra/clk-tegra124-dfll-fcpu.c |  23 +-
>  2 files changed, 382 insertions(+), 39 deletions(-)
> 
> diff --git a/drivers/clk/tegra/clk-dfll.c b/drivers/clk/tegra/clk-dfll.c
> index fa97763..228edb4 100644
> --- a/drivers/clk/tegra/clk-dfll.c
> +++ b/drivers/clk/tegra/clk-dfll.c
> @@ -243,6 +243,12 @@ enum dfll_tune_range {
>   DFLL_TUNE_LOW = 1,
>  };
>  
> +
> +enum tegra_dfll_pmu_if {
> + TEGRA_DFLL_PMU_I2C = 0,
> + TEGRA_DFLL_PMU_PWM = 1,
> +};
> +
>  /**
>   * struct dfll_rate_req - target DFLL rate request data
>   * @rate: target frequency, after the postscaling
> @@ -294,17 +300,25 @@ struct tegra_dfll {
>   u32 ci;
>   u32 cg;
>   boolcg_scale;
> + u32 reg_init_uV;
>  
>   /* I2C interface parameters */
>   u32 i2c_fs_rate;
>   u32 i2c_reg;
>   u32 i2c_slave_addr;
>  
> - /* i2c_lut array entries are regulator framework selectors */
> + /* lut array entries are regulator framework selectors or PWM values*/
>   unsigned inti2c_lut[MAX_DFLL_VOLTAGES];
>   unsigned intlut_uv[MAX_DFLL_VOLTAGES];
>   int lut_size;
>   u8  lut_bottom, lut_min, lut_max, lut_safe;
> +
> + /* PWM interface */
> + enum tegra_dfll_pmu_if  pmu_if;
> + unsigned long   pwm_rate;
> + struct pinctrl  *pwm_pin;
> + struct pinctrl_state*pwm_enable_state;
> + struct pinctrl_state*pwm_disable_state;
>  };
>  
>  #define clk_hw_to_dfll(_hw) container_of(_hw, struct tegra_dfll, dfll_clk_hw)
> @@ -491,6 +505,36 @@ static void dfll_set_mode(struct tegra_dfll *td,
>  }
>  
>  /*
> + * DVCO rate control
> + */
> +
> +static unsigned long get_dvco_rate_below(struct tegra_dfll *td, u8 out_min)
> +{
> + struct dev_pm_opp *opp;
> + unsigned long rate, prev_rate;
> + int uv, min_uv;
> +
> + min_uv = td->lut_uv[out_min];
> + for (rate = 0, prev_rate = 0; ; rate++) {
> + rcu_read_lock();
> + opp = dev_pm_opp_find_freq_ceil(td->soc->dev, );
> + if (IS_ERR(opp)) {
> + rcu_read_unlock();
> + break;
> + }
> + uv = dev_pm_opp_get_voltage(opp);
> + rcu_read_unlock();
> +
> + if (uv && uv > min_uv)
> + return prev_rate;
> +
> + prev_rate = rate;
> + }
> +
> + return prev_rate;
> +}
> +
> +/*
>   * DFLL-to-I2C controller interface
>   */
>  
> @@ -519,6 +563,119 @@ static int dfll_i2c_set_output_enabled(struct 
> tegra_dfll *td, bool enable)
>   return 0;
>  }
>  
> +
> +/*
> + * DFLL-to-PWM controller interface
> + */
> +
> +/**
> + * dfll_pwm_set_output_enabled - enable/disable PWM voltage requests
> + * @td: DFLL instance
> + * @enable: whether to enable or disable the PWM voltage requests
> + *
> + * Set the master enable control for PWM control value updates. If disabled,
> + * then the PWM signal is not driven. Also configure the PWM output pad
> + * to the appropriate state.
> + */
> +static int dfll_pwm_set_output_enabled(struct tegra_dfll *td, bool enable)
> +{
> + int ret;
> + u32 val, div;
> +
> + if (enable) {
> + ret = pinctrl_select_state(td->pwm_pin, td->pwm_enable_state);
> + if (ret < 0) {
> + dev_err(td->dev, "setting enable state failed\n");
> + return ret;
> + }
> + val = dfll_readl(td, DFLL_OUTPUT_CFG);
> + val &= ~DFLL_OUTPUT_CFG_PWM_DIV_MASK;
> + div = DIV_ROUND_UP(td->ref_rate, td->pwm_rate);
> + val |= (div << DFLL_OUTPUT_CFG_PWM_DIV_SHIFT)
> + & DFLL_OUTPUT_CFG_PWM_DIV_MASK;
> + dfll_writel(td, val, DFLL_OUTPUT_CFG);
> + 

[PATCH v3 06/11] clk: tegra: dfll: support PWM regulator control

2018-02-06 Thread Peter De Schrijver
The DFLL can directly generate a PWM signal to control the regulator IC
rather then sending i2c messages. This patch adds support for this mode.
In this mode the hardware LUT is not used and also there is no regulator
object involved because there is no way to control the regulator voltage
without also changing the DFLL output frequency. Also the register debugfs
file is slightly reworked to only show the i2c registers when i2c mode is
in use. As there is no regulator object for the PWM regulator, its step and
offset values are retrieved from DT instead.

Signed-off-by: Peter De Schrijver 
---
 drivers/clk/tegra/clk-dfll.c   | 398 ++---
 drivers/clk/tegra/clk-tegra124-dfll-fcpu.c |  23 +-
 2 files changed, 382 insertions(+), 39 deletions(-)

diff --git a/drivers/clk/tegra/clk-dfll.c b/drivers/clk/tegra/clk-dfll.c
index fa97763..228edb4 100644
--- a/drivers/clk/tegra/clk-dfll.c
+++ b/drivers/clk/tegra/clk-dfll.c
@@ -243,6 +243,12 @@ enum dfll_tune_range {
DFLL_TUNE_LOW = 1,
 };
 
+
+enum tegra_dfll_pmu_if {
+   TEGRA_DFLL_PMU_I2C = 0,
+   TEGRA_DFLL_PMU_PWM = 1,
+};
+
 /**
  * struct dfll_rate_req - target DFLL rate request data
  * @rate: target frequency, after the postscaling
@@ -294,17 +300,25 @@ struct tegra_dfll {
u32 ci;
u32 cg;
boolcg_scale;
+   u32 reg_init_uV;
 
/* I2C interface parameters */
u32 i2c_fs_rate;
u32 i2c_reg;
u32 i2c_slave_addr;
 
-   /* i2c_lut array entries are regulator framework selectors */
+   /* lut array entries are regulator framework selectors or PWM values*/
unsigned inti2c_lut[MAX_DFLL_VOLTAGES];
unsigned intlut_uv[MAX_DFLL_VOLTAGES];
int lut_size;
u8  lut_bottom, lut_min, lut_max, lut_safe;
+
+   /* PWM interface */
+   enum tegra_dfll_pmu_if  pmu_if;
+   unsigned long   pwm_rate;
+   struct pinctrl  *pwm_pin;
+   struct pinctrl_state*pwm_enable_state;
+   struct pinctrl_state*pwm_disable_state;
 };
 
 #define clk_hw_to_dfll(_hw) container_of(_hw, struct tegra_dfll, dfll_clk_hw)
@@ -491,6 +505,36 @@ static void dfll_set_mode(struct tegra_dfll *td,
 }
 
 /*
+ * DVCO rate control
+ */
+
+static unsigned long get_dvco_rate_below(struct tegra_dfll *td, u8 out_min)
+{
+   struct dev_pm_opp *opp;
+   unsigned long rate, prev_rate;
+   int uv, min_uv;
+
+   min_uv = td->lut_uv[out_min];
+   for (rate = 0, prev_rate = 0; ; rate++) {
+   rcu_read_lock();
+   opp = dev_pm_opp_find_freq_ceil(td->soc->dev, );
+   if (IS_ERR(opp)) {
+   rcu_read_unlock();
+   break;
+   }
+   uv = dev_pm_opp_get_voltage(opp);
+   rcu_read_unlock();
+
+   if (uv && uv > min_uv)
+   return prev_rate;
+
+   prev_rate = rate;
+   }
+
+   return prev_rate;
+}
+
+/*
  * DFLL-to-I2C controller interface
  */
 
@@ -519,6 +563,119 @@ static int dfll_i2c_set_output_enabled(struct tegra_dfll 
*td, bool enable)
return 0;
 }
 
+
+/*
+ * DFLL-to-PWM controller interface
+ */
+
+/**
+ * dfll_pwm_set_output_enabled - enable/disable PWM voltage requests
+ * @td: DFLL instance
+ * @enable: whether to enable or disable the PWM voltage requests
+ *
+ * Set the master enable control for PWM control value updates. If disabled,
+ * then the PWM signal is not driven. Also configure the PWM output pad
+ * to the appropriate state.
+ */
+static int dfll_pwm_set_output_enabled(struct tegra_dfll *td, bool enable)
+{
+   int ret;
+   u32 val, div;
+
+   if (enable) {
+   ret = pinctrl_select_state(td->pwm_pin, td->pwm_enable_state);
+   if (ret < 0) {
+   dev_err(td->dev, "setting enable state failed\n");
+   return ret;
+   }
+   val = dfll_readl(td, DFLL_OUTPUT_CFG);
+   val &= ~DFLL_OUTPUT_CFG_PWM_DIV_MASK;
+   div = DIV_ROUND_UP(td->ref_rate, td->pwm_rate);
+   val |= (div << DFLL_OUTPUT_CFG_PWM_DIV_SHIFT)
+   & DFLL_OUTPUT_CFG_PWM_DIV_MASK;
+   dfll_writel(td, val, DFLL_OUTPUT_CFG);
+   dfll_wmb(td);
+
+   val |= DFLL_OUTPUT_CFG_PWM_ENABLE;
+   dfll_writel(td, val, DFLL_OUTPUT_CFG);
+   dfll_wmb(td);
+   } else {
+   ret = pinctrl_select_state(td->pwm_pin, td->pwm_disable_state);
+   if (ret < 0)
+   dev_warn(td->dev, 

[PATCH v3 06/11] clk: tegra: dfll: support PWM regulator control

2018-02-06 Thread Peter De Schrijver
The DFLL can directly generate a PWM signal to control the regulator IC
rather then sending i2c messages. This patch adds support for this mode.
In this mode the hardware LUT is not used and also there is no regulator
object involved because there is no way to control the regulator voltage
without also changing the DFLL output frequency. Also the register debugfs
file is slightly reworked to only show the i2c registers when i2c mode is
in use. As there is no regulator object for the PWM regulator, its step and
offset values are retrieved from DT instead.

Signed-off-by: Peter De Schrijver 
---
 drivers/clk/tegra/clk-dfll.c   | 398 ++---
 drivers/clk/tegra/clk-tegra124-dfll-fcpu.c |  23 +-
 2 files changed, 382 insertions(+), 39 deletions(-)

diff --git a/drivers/clk/tegra/clk-dfll.c b/drivers/clk/tegra/clk-dfll.c
index fa97763..228edb4 100644
--- a/drivers/clk/tegra/clk-dfll.c
+++ b/drivers/clk/tegra/clk-dfll.c
@@ -243,6 +243,12 @@ enum dfll_tune_range {
DFLL_TUNE_LOW = 1,
 };
 
+
+enum tegra_dfll_pmu_if {
+   TEGRA_DFLL_PMU_I2C = 0,
+   TEGRA_DFLL_PMU_PWM = 1,
+};
+
 /**
  * struct dfll_rate_req - target DFLL rate request data
  * @rate: target frequency, after the postscaling
@@ -294,17 +300,25 @@ struct tegra_dfll {
u32 ci;
u32 cg;
boolcg_scale;
+   u32 reg_init_uV;
 
/* I2C interface parameters */
u32 i2c_fs_rate;
u32 i2c_reg;
u32 i2c_slave_addr;
 
-   /* i2c_lut array entries are regulator framework selectors */
+   /* lut array entries are regulator framework selectors or PWM values*/
unsigned inti2c_lut[MAX_DFLL_VOLTAGES];
unsigned intlut_uv[MAX_DFLL_VOLTAGES];
int lut_size;
u8  lut_bottom, lut_min, lut_max, lut_safe;
+
+   /* PWM interface */
+   enum tegra_dfll_pmu_if  pmu_if;
+   unsigned long   pwm_rate;
+   struct pinctrl  *pwm_pin;
+   struct pinctrl_state*pwm_enable_state;
+   struct pinctrl_state*pwm_disable_state;
 };
 
 #define clk_hw_to_dfll(_hw) container_of(_hw, struct tegra_dfll, dfll_clk_hw)
@@ -491,6 +505,36 @@ static void dfll_set_mode(struct tegra_dfll *td,
 }
 
 /*
+ * DVCO rate control
+ */
+
+static unsigned long get_dvco_rate_below(struct tegra_dfll *td, u8 out_min)
+{
+   struct dev_pm_opp *opp;
+   unsigned long rate, prev_rate;
+   int uv, min_uv;
+
+   min_uv = td->lut_uv[out_min];
+   for (rate = 0, prev_rate = 0; ; rate++) {
+   rcu_read_lock();
+   opp = dev_pm_opp_find_freq_ceil(td->soc->dev, );
+   if (IS_ERR(opp)) {
+   rcu_read_unlock();
+   break;
+   }
+   uv = dev_pm_opp_get_voltage(opp);
+   rcu_read_unlock();
+
+   if (uv && uv > min_uv)
+   return prev_rate;
+
+   prev_rate = rate;
+   }
+
+   return prev_rate;
+}
+
+/*
  * DFLL-to-I2C controller interface
  */
 
@@ -519,6 +563,119 @@ static int dfll_i2c_set_output_enabled(struct tegra_dfll 
*td, bool enable)
return 0;
 }
 
+
+/*
+ * DFLL-to-PWM controller interface
+ */
+
+/**
+ * dfll_pwm_set_output_enabled - enable/disable PWM voltage requests
+ * @td: DFLL instance
+ * @enable: whether to enable or disable the PWM voltage requests
+ *
+ * Set the master enable control for PWM control value updates. If disabled,
+ * then the PWM signal is not driven. Also configure the PWM output pad
+ * to the appropriate state.
+ */
+static int dfll_pwm_set_output_enabled(struct tegra_dfll *td, bool enable)
+{
+   int ret;
+   u32 val, div;
+
+   if (enable) {
+   ret = pinctrl_select_state(td->pwm_pin, td->pwm_enable_state);
+   if (ret < 0) {
+   dev_err(td->dev, "setting enable state failed\n");
+   return ret;
+   }
+   val = dfll_readl(td, DFLL_OUTPUT_CFG);
+   val &= ~DFLL_OUTPUT_CFG_PWM_DIV_MASK;
+   div = DIV_ROUND_UP(td->ref_rate, td->pwm_rate);
+   val |= (div << DFLL_OUTPUT_CFG_PWM_DIV_SHIFT)
+   & DFLL_OUTPUT_CFG_PWM_DIV_MASK;
+   dfll_writel(td, val, DFLL_OUTPUT_CFG);
+   dfll_wmb(td);
+
+   val |= DFLL_OUTPUT_CFG_PWM_ENABLE;
+   dfll_writel(td, val, DFLL_OUTPUT_CFG);
+   dfll_wmb(td);
+   } else {
+   ret = pinctrl_select_state(td->pwm_pin, td->pwm_disable_state);
+   if (ret < 0)
+   dev_warn(td->dev, "setting disable state