[PATCH v3 1/2] clk: divider: Add CLK_DIVIDER_EVEN flag support

Shawn Lin shawn.lin at rock-chips.com
Mon May 7 19:19:26 PDT 2018


Hi Michael and Stephen

Any chance to take a look at this patch? :)

On 2018/4/8 15:28, Shawn Lin wrote:
> CLK_DIVIDER_EVEN is used for clock divders that should only
> use even number in the div field.
> 
> Two clock divder should consider to use this flag
> (1) The divder is physically only support even div number to
> generate 50% duty cycle clock rate.
> (2) The divder's clock consumer request it to use even div number
> to generate the most closest requested rate for whatever reason.
> 
> In some platforms, for instance Rockchip, the eMMC/SDIO/SDMMC should
> request divder to use even number when working at a high throughput
> speed mode reliably. However, that wasn't guaranteed by clock framework.
> So the previous tricky is to carefully assign magic clock rate to their
> parents as well as consumer's clock rate when requesting. That works
> bad in practice if folks change the parents clock rate or the clock
> hierarchy randomly. That also work bad if the consumer's clock rate
> came from the DT, which is changed so fraquent for different boards.
> 
> To make it's less prone to make mistake and to make it really respect
> the fact that the divder should use even number to the div field, we
> need the clock framework's help. Now we have CLK_DIVIDER_POWER_OF_TWO,
> which could guarantee the div field is even number, however, obviously
> it skips the even numbers which isn't the power of 2, but maybe which is
> the best div to generate closest clock rate for consumer.
> 
> Look at the examples here when doing some random test on Rockchip's
> platforms, by changing the requested clock rate from DT, namely
> assigning different max-frquency for MMC node,
> 
> when the mmc host driver requests 80MHz with CLK_DIVIDER_POWER_OF_TWO
> flag for the clock divder, it shows the final clock rate is 61.44Mhz
> 
> pll_vpll0		1		1 983039999     0   0
>     vpll0		4		4 983039999     0   0
>       clk_emmc_div       1		1 122880000     0   0
> 	clk_emmc	1		1 122880000     0   0
> 	  emmc_sample	0		0  61440000	0 155
> 	  emmc_drv	0		0  61440000	0 180
> 
> With this patch and add CLK_DIVIDER_EVEN flag for clk_emmc_div, we get
> the final clock rate, 67.7376MHz.
> 
> pll_vpll1               1		1 812851199	0   0
>     vpll1                2		2 812851199     0   0
>       clk_emmc_div       1		1 135475200     0   0
> 	clk_emmc	1		1 135475200     0   0
> 	  emmc_sample   0		0  67737600     0 113
> 	  emmc_drv	0		0  67737600	0 180
> 
> Apprently 67737600 is better than 61440000 when requesting 80MHz.
> Of course, we could have more case that worsen the gap between
> the desired rate and the actual rate if using CLK_DIVIDER_POWER_OF_TWO.
> 
> Alternatively, clk_div_table could be resorted to handle different kinds
> of div limilation, and it seems to be applied to irregular div calculation
> in practice by current clock provider drivers, but even number for div
> field is a rather common, regular and symmetrical requirement. So it is
> worth to introduces CLK_DIVIDER_EVEN flag for clock framework to make
> the best in this process.
> 
> Signed-off-by: Shawn Lin <shawn.lin at rock-chips.com>
> 
> ---
> 
> Changes in v3:
> - Fix a wrong if condition check
> 
> Changes in v2:
> - Avoid to use div 1 if (CLK_DIVIDER_EVEN | CLK_DIVIDER_POWER_OF_TWO)
>    suggested by Heiko. And seems actually no other flags should be bothered
>    by this newly added CLK_DIVIDER_EVEN.
> 
>   drivers/clk/clk-divider.c    | 26 ++++++++++++++++++++++++--
>   include/linux/clk-provider.h |  3 +++
>   2 files changed, 27 insertions(+), 2 deletions(-)
> 
> diff --git a/drivers/clk/clk-divider.c b/drivers/clk/clk-divider.c
> index b6234a5..8f305f1 100644
> --- a/drivers/clk/clk-divider.c
> +++ b/drivers/clk/clk-divider.c
> @@ -159,8 +159,17 @@ static bool _is_valid_table_div(const struct clk_div_table *table,
>   static bool _is_valid_div(const struct clk_div_table *table, unsigned int div,
>   			  unsigned long flags)
>   {
> -	if (flags & CLK_DIVIDER_POWER_OF_TWO)
> -		return is_power_of_2(div);
> +	bool is_valid;
> +
> +	if (flags & CLK_DIVIDER_POWER_OF_TWO) {
> +		is_valid = is_power_of_2(div);
> +		if (flags & CLK_DIVIDER_EVEN)
> +			return is_valid && (div != 1);
> +		return is_valid;
> +	}
> +
> +	if (flags & CLK_DIVIDER_EVEN)
> +		return !(div % 2);
>   	if (table)
>   		return _is_valid_table_div(table, div);
>   	return true;
> @@ -208,6 +217,12 @@ static int _div_round_up(const struct clk_div_table *table,
>   {
>   	int div = DIV_ROUND_UP_ULL((u64)parent_rate, rate);
>   
> +	/*
> +	 * Check even before power of two to avoid div 1 if combination
> +	 * happens, which applies to all the following similarities.
> +	 */
> +	if (flags & CLK_DIVIDER_EVEN)
> +		div = !(div % 2) ? div : (div + 1);
>   	if (flags & CLK_DIVIDER_POWER_OF_TWO)
>   		div = __roundup_pow_of_two(div);
>   	if (table)
> @@ -226,6 +241,11 @@ static int _div_round_closest(const struct clk_div_table *table,
>   	up = DIV_ROUND_UP_ULL((u64)parent_rate, rate);
>   	down = parent_rate / rate;
>   
> +	if (flags & CLK_DIVIDER_EVEN) {
> +		up = !(up % 2) ? up : (up + 1);
> +		down = !(down % 2) ? down : (down - 1);
> +	}
> +
>   	if (flags & CLK_DIVIDER_POWER_OF_TWO) {
>   		up = __roundup_pow_of_two(up);
>   		down = __rounddown_pow_of_two(down);
> @@ -264,6 +284,8 @@ static int _next_div(const struct clk_div_table *table, int div,
>   {
>   	div++;
>   
> +	if (flags & CLK_DIVIDER_EVEN)
> +		div = !(div % 2) ? div : (div + 1);
>   	if (flags & CLK_DIVIDER_POWER_OF_TWO)
>   		return __roundup_pow_of_two(div);
>   	if (table)
> diff --git a/include/linux/clk-provider.h b/include/linux/clk-provider.h
> index 210a890..7c59611 100644
> --- a/include/linux/clk-provider.h
> +++ b/include/linux/clk-provider.h
> @@ -388,6 +388,8 @@ struct clk_div_table {
>    * CLK_DIVIDER_MAX_AT_ZERO - For dividers which are like CLK_DIVIDER_ONE_BASED
>    *	except when the value read from the register is zero, the divisor is
>    *	2^width of the field.
> + * CLK_DIVIDER_EVEN - For the dividers which could only use even number in the
> + *	div field.
>    */
>   struct clk_divider {
>   	struct clk_hw	hw;
> @@ -409,6 +411,7 @@ struct clk_divider {
>   #define CLK_DIVIDER_ROUND_CLOSEST	BIT(4)
>   #define CLK_DIVIDER_READ_ONLY		BIT(5)
>   #define CLK_DIVIDER_MAX_AT_ZERO		BIT(6)
> +#define CLK_DIVIDER_EVEN		BIT(7)
>   
>   extern const struct clk_ops clk_divider_ops;
>   extern const struct clk_ops clk_divider_ro_ops;
> 




More information about the Linux-rockchip mailing list