return DIV_ROUND_UP_ULL(parent_rate, value);
}
+static void zynqmp_get_divider2_val(struct clk_hw *hw,
+ unsigned long rate,
+ struct zynqmp_clk_divider *divider,
+ u32 *bestdiv)
+{
+ int div1;
+ int div2;
+ long error = LONG_MAX;
+ unsigned long div1_prate;
+ struct clk_hw *div1_parent_hw;
+ struct zynqmp_clk_divider *pdivider;
+ struct clk_hw *div2_parent_hw = clk_hw_get_parent(hw);
+
+ if (!div2_parent_hw)
+ return;
+
+ pdivider = to_zynqmp_clk_divider(div2_parent_hw);
+ if (!pdivider)
+ return;
+
+ div1_parent_hw = clk_hw_get_parent(div2_parent_hw);
+ if (!div1_parent_hw)
+ return;
+
+ div1_prate = clk_hw_get_rate(div1_parent_hw);
+ *bestdiv = 1;
+ for (div1 = 1; div1 <= pdivider->max_div;) {
+ for (div2 = 1; div2 <= divider->max_div;) {
+ long new_error = ((div1_prate / div1) / div2) - rate;
+
+ if (abs(new_error) < abs(error)) {
+ *bestdiv = div2;
+ error = new_error;
+ }
+ if (divider->flags & CLK_DIVIDER_POWER_OF_TWO)
+ div2 = div2 << 1;
+ else
+ div2++;
+ }
+ if (pdivider->flags & CLK_DIVIDER_POWER_OF_TWO)
+ div1 = div1 << 1;
+ else
+ div1++;
+ }
+}
+
/**
* zynqmp_clk_divider_round_rate() - Round rate of divider clock
* @hw: handle between common and hardware-specific interfaces
u32 div_type = divider->div_type;
u32 bestdiv;
int ret;
- u8 width;
/* if read only, just return current value */
if (divider->flags & CLK_DIVIDER_READ_ONLY) {
return DIV_ROUND_UP_ULL((u64)*prate, bestdiv);
}
- width = fls(divider->max_div);
+ bestdiv = zynqmp_divider_get_val(*prate, rate, divider->flags);
+
+ /*
+ * In case of two divisors, compute best divider values and return
+ * divider2 value based on compute value. div1 will be automatically
+ * set to optimum based on required total divider value.
+ */
+ if (div_type == TYPE_DIV2 &&
+ (clk_hw_get_flags(hw) & CLK_SET_RATE_PARENT)) {
+ zynqmp_get_divider2_val(hw, rate, divider, &bestdiv);
+ }
- rate = divider_round_rate(hw, rate, prate, NULL, width, divider->flags);
+ if ((clk_hw_get_flags(hw) & CLK_SET_RATE_PARENT) && divider->is_frac)
+ bestdiv = rate % *prate ? 1 : bestdiv;
- if (divider->is_frac && (clk_hw_get_flags(hw) & CLK_SET_RATE_PARENT) && (rate % *prate))
- *prate = rate;
+ bestdiv = min_t(u32, bestdiv, divider->max_div);
+ *prate = rate * bestdiv;
return rate;
}