[PATCH] clk: lpc32xx: fix pwm clock divider computation
Vladimir Zapolskiy
vz at mleia.com
Tue Oct 4 19:38:37 PDT 2016
Hi Sylvain,
On 26.09.2016 21:44, Sylvain Lemieux wrote:
> From: Sylvain Lemieux <slemieux at tycoint.com>
>
> A zero value in the PWM clock divider register
> (PWM1_FREQ/PWM2_FREQ) turn off the PWM clock.
>
> The "CLK_DIVIDER_ALLOW_ZERO" option is used for hardware that handle
> the zero divider by not modifying their clock input (i.e. bypass).
> See "/include/linux/clk-provider.h" for details.
the problem is that the divider value is not set to some non-zero
value when the clock is enabled, right?
I think it does not matter if the clock rate value is set to parent
clock rate or any other value when divider is 0 *and* clock is gated.
Enabling or disabling a clock is a gate control, so I suggest two
alternative options at your choice (my preference is option 2):
1) add a custom clk_pwm_gate_enable(), clk_pwm_gate_disable() and
clk_pwm_gate_is_enabled() functions combined under lpc32xx_clk_pwm_gate_ops.
Next instead of adding one more define for a single exception
please reassign .ops for two PWM clocks in runtime in
lpc32xx_clk_init() function before calling lpc32xx_clk_register()
in a loop.
But this option is too invasive, a simpler solution is below.
2) in lpc32xx_clk_init() before clock registrations check for zero
dividers of PWM clocks, then if a divider is 0 and clock is gated
set divider to 1, if the divider is 0 and clock is not gated then
gate the clock and set divider to 1, in other cases do nothing.
> Remove the CLK_DIVIDER_ALLOW_ZERO option and add support to handle
> the clock rate computation of the PWM clock divider 0 value.
>
> Signed-off-by: Sylvain Lemieux <slemieux at tycoint.com>
> ---
> Note:
> * Should we include a new CLK_DIVIDER option for this case
> (i.e. clock off when zero ) in "clk-provider.h"?
>
> drivers/clk/nxp/clk-lpc32xx.c | 52 +++++++++++++++++++++++++++++++++++++++----
> 1 file changed, 48 insertions(+), 4 deletions(-)
>
> diff --git a/drivers/clk/nxp/clk-lpc32xx.c b/drivers/clk/nxp/clk-lpc32xx.c
> index 34c9735..3ca3a14 100644
> --- a/drivers/clk/nxp/clk-lpc32xx.c
> +++ b/drivers/clk/nxp/clk-lpc32xx.c
> @@ -959,6 +959,25 @@ static unsigned long clk_divider_recalc_rate(struct clk_hw *hw,
> divider->flags);
> }
>
> +static unsigned long clk_divider_pwm_recalc_rate(struct clk_hw *hw,
> + unsigned long parent_rate)
> +{
> + struct lpc32xx_clk_div *divider = to_lpc32xx_div(hw);
> + unsigned int val;
> +
> + regmap_read(clk_regmap, divider->reg, &val);
> +
> + val >>= divider->shift;
> + val &= div_mask(divider->width);
> +
> + /* Handle 0 divider -> PWM clock is off. */
> + if(val == 0)
No space in front of the open parenthesis.
> + return 0;
> +
> + return divider_recalc_rate(hw, parent_rate, val, divider->table,
> + divider->flags);
> +}
> +
> static long clk_divider_round_rate(struct clk_hw *hw, unsigned long rate,
> unsigned long *prate)
> {
> @@ -999,6 +1018,12 @@ static const struct clk_ops lpc32xx_clk_divider_ops = {
> .set_rate = clk_divider_set_rate,
> };
>
> +static const struct clk_ops lpc32xx_clk_pwm_divider_ops = {
> + .recalc_rate = clk_divider_pwm_recalc_rate,
> + .round_rate = clk_divider_round_rate,
> + .set_rate = clk_divider_set_rate,
> +};
> +
> static u8 clk_mux_get_parent(struct clk_hw *hw)
> {
> struct lpc32xx_clk_mux *mux = to_lpc32xx_mux(hw);
> @@ -1151,6 +1176,25 @@ struct clk_hw_proto {
> }, \
> }
>
> +#define LPC32XX_DEFINE_PWM_DIV(_idx, _reg, _shift, _width, _tab, _fl) \
> +[CLK_PREFIX(_idx)] = { \
> + .type = CLK_DIV, \
> + { \
> + .hw0 = { \
> + .ops = &lpc32xx_clk_pwm_divider_ops, \
> + { \
> + .div = { \
> + .reg = LPC32XX_CLKPWR_ ## _reg, \
> + .shift = (_shift), \
> + .width = (_width), \
> + .table = (_tab), \
> + .flags = (_fl), \
> + }, \
> + }, \
> + }, \
> + }, \
> +}
> +
> #define LPC32XX_DEFINE_GATE(_idx, _reg, _bit, _flags) \
> [CLK_PREFIX(_idx)] = { \
> .type = CLK_GATE, \
> @@ -1281,14 +1325,14 @@ static struct clk_hw_proto clk_hw_proto[LPC32XX_CLK_HW_MAX] = {
> LPC32XX_DEFINE_GATE(MCPWM, TIMCLK_CTRL1, 6, 0),
>
> LPC32XX_DEFINE_MUX(PWM1_MUX, PWMCLK_CTRL, 1, 0x1, NULL, 0),
> - LPC32XX_DEFINE_DIV(PWM1_DIV, PWMCLK_CTRL, 4, 4, NULL,
> - CLK_DIVIDER_ONE_BASED | CLK_DIVIDER_ALLOW_ZERO),
> + LPC32XX_DEFINE_PWM_DIV(PWM1_DIV, PWMCLK_CTRL, 4, 4, NULL,
> + CLK_DIVIDER_ONE_BASED),
> LPC32XX_DEFINE_GATE(PWM1_GATE, PWMCLK_CTRL, 0, 0),
> LPC32XX_DEFINE_COMPOSITE(PWM1, PWM1_MUX, PWM1_DIV, PWM1_GATE),
>
> LPC32XX_DEFINE_MUX(PWM2_MUX, PWMCLK_CTRL, 3, 0x1, NULL, 0),
> - LPC32XX_DEFINE_DIV(PWM2_DIV, PWMCLK_CTRL, 8, 4, NULL,
> - CLK_DIVIDER_ONE_BASED | CLK_DIVIDER_ALLOW_ZERO),
> + LPC32XX_DEFINE_PWM_DIV(PWM2_DIV, PWMCLK_CTRL, 8, 4, NULL,
> + CLK_DIVIDER_ONE_BASED),
> LPC32XX_DEFINE_GATE(PWM2_GATE, PWMCLK_CTRL, 2, 0),
> LPC32XX_DEFINE_COMPOSITE(PWM2, PWM2_MUX, PWM2_DIV, PWM2_GATE),
>
>
--
With best wishes,
Vladimir
More information about the linux-arm-kernel
mailing list