|
@@ -97,3 +97,149 @@ static int _dpll_test_fint(struct clk_hw_omap *clk, u8 n)
|
|
|
} else if (dd->flags & DPLL_J_TYPE) {
|
|
|
fint_min = OMAP3PLUS_DPLL_FINT_JTYPE_MIN;
|
|
|
fint_max = OMAP3PLUS_DPLL_FINT_JTYPE_MAX;
|
|
|
+ } else {
|
|
|
+ fint_min = OMAP3PLUS_DPLL_FINT_MIN;
|
|
|
+ fint_max = OMAP3PLUS_DPLL_FINT_MAX;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (fint < fint_min) {
|
|
|
+ pr_debug("rejecting n=%d due to Fint failure, lowering max_divider\n",
|
|
|
+ n);
|
|
|
+ dd->max_divider = n;
|
|
|
+ ret = DPLL_FINT_UNDERFLOW;
|
|
|
+ } else if (fint > fint_max) {
|
|
|
+ pr_debug("rejecting n=%d due to Fint failure, boosting min_divider\n",
|
|
|
+ n);
|
|
|
+ dd->min_divider = n;
|
|
|
+ ret = DPLL_FINT_INVALID;
|
|
|
+ } else if (cpu_is_omap3430() && fint > OMAP3430_DPLL_FINT_BAND1_MAX &&
|
|
|
+ fint < OMAP3430_DPLL_FINT_BAND2_MIN) {
|
|
|
+ pr_debug("rejecting n=%d due to Fint failure\n", n);
|
|
|
+ ret = DPLL_FINT_INVALID;
|
|
|
+ }
|
|
|
+
|
|
|
+ return ret;
|
|
|
+}
|
|
|
+
|
|
|
+static unsigned long _dpll_compute_new_rate(unsigned long parent_rate,
|
|
|
+ unsigned int m, unsigned int n)
|
|
|
+{
|
|
|
+ unsigned long long num;
|
|
|
+
|
|
|
+ num = (unsigned long long)parent_rate * m;
|
|
|
+ do_div(num, n);
|
|
|
+ return num;
|
|
|
+}
|
|
|
+
|
|
|
+/*
|
|
|
+ * _dpll_test_mult - test a DPLL multiplier value
|
|
|
+ * @m: pointer to the DPLL m (multiplier) value under test
|
|
|
+ * @n: current DPLL n (divider) value under test
|
|
|
+ * @new_rate: pointer to storage for the resulting rounded rate
|
|
|
+ * @target_rate: the desired DPLL rate
|
|
|
+ * @parent_rate: the DPLL's parent clock rate
|
|
|
+ *
|
|
|
+ * This code tests a DPLL multiplier value, ensuring that the
|
|
|
+ * resulting rate will not be higher than the target_rate, and that
|
|
|
+ * the multiplier value itself is valid for the DPLL. Initially, the
|
|
|
+ * integer pointed to by the m argument should be prescaled by
|
|
|
+ * multiplying by DPLL_SCALE_FACTOR. The code will replace this with
|
|
|
+ * a non-scaled m upon return. This non-scaled m will result in a
|
|
|
+ * new_rate as close as possible to target_rate (but not greater than
|
|
|
+ * target_rate) given the current (parent_rate, n, prescaled m)
|
|
|
+ * triple. Returns DPLL_MULT_UNDERFLOW in the event that the
|
|
|
+ * non-scaled m attempted to underflow, which can allow the calling
|
|
|
+ * function to bail out early; or 0 upon success.
|
|
|
+ */
|
|
|
+static int _dpll_test_mult(int *m, int n, unsigned long *new_rate,
|
|
|
+ unsigned long target_rate,
|
|
|
+ unsigned long parent_rate)
|
|
|
+{
|
|
|
+ int r = 0, carry = 0;
|
|
|
+
|
|
|
+ /* Unscale m and round if necessary */
|
|
|
+ if (*m % DPLL_SCALE_FACTOR >= DPLL_ROUNDING_VAL)
|
|
|
+ carry = 1;
|
|
|
+ *m = (*m / DPLL_SCALE_FACTOR) + carry;
|
|
|
+
|
|
|
+ /*
|
|
|
+ * The new rate must be <= the target rate to avoid programming
|
|
|
+ * a rate that is impossible for the hardware to handle
|
|
|
+ */
|
|
|
+ *new_rate = _dpll_compute_new_rate(parent_rate, *m, n);
|
|
|
+ if (*new_rate > target_rate) {
|
|
|
+ (*m)--;
|
|
|
+ *new_rate = 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ /* Guard against m underflow */
|
|
|
+ if (*m < DPLL_MIN_MULTIPLIER) {
|
|
|
+ *m = DPLL_MIN_MULTIPLIER;
|
|
|
+ *new_rate = 0;
|
|
|
+ r = DPLL_MULT_UNDERFLOW;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (*new_rate == 0)
|
|
|
+ *new_rate = _dpll_compute_new_rate(parent_rate, *m, n);
|
|
|
+
|
|
|
+ return r;
|
|
|
+}
|
|
|
+
|
|
|
+/* Public functions */
|
|
|
+u8 omap2_init_dpll_parent(struct clk_hw *hw)
|
|
|
+{
|
|
|
+ struct clk_hw_omap *clk = to_clk_hw_omap(hw);
|
|
|
+ u32 v;
|
|
|
+ struct dpll_data *dd;
|
|
|
+
|
|
|
+ dd = clk->dpll_data;
|
|
|
+ if (!dd)
|
|
|
+ return -EINVAL;
|
|
|
+
|
|
|
+ v = __raw_readl(dd->control_reg);
|
|
|
+ v &= dd->enable_mask;
|
|
|
+ v >>= __ffs(dd->enable_mask);
|
|
|
+
|
|
|
+ /* Reparent the struct clk in case the dpll is in bypass */
|
|
|
+ if (cpu_is_omap24xx()) {
|
|
|
+ if (v == OMAP2XXX_EN_DPLL_LPBYPASS ||
|
|
|
+ v == OMAP2XXX_EN_DPLL_FRBYPASS)
|
|
|
+ return 1;
|
|
|
+ } else if (cpu_is_omap34xx()) {
|
|
|
+ if (v == OMAP3XXX_EN_DPLL_LPBYPASS ||
|
|
|
+ v == OMAP3XXX_EN_DPLL_FRBYPASS)
|
|
|
+ return 1;
|
|
|
+ } else if (soc_is_am33xx() || cpu_is_omap44xx()) {
|
|
|
+ if (v == OMAP4XXX_EN_DPLL_LPBYPASS ||
|
|
|
+ v == OMAP4XXX_EN_DPLL_FRBYPASS ||
|
|
|
+ v == OMAP4XXX_EN_DPLL_MNBYPASS)
|
|
|
+ return 1;
|
|
|
+ }
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * omap2_get_dpll_rate - returns the current DPLL CLKOUT rate
|
|
|
+ * @clk: struct clk * of a DPLL
|
|
|
+ *
|
|
|
+ * DPLLs can be locked or bypassed - basically, enabled or disabled.
|
|
|
+ * When locked, the DPLL output depends on the M and N values. When
|
|
|
+ * bypassed, on OMAP2xxx, the output rate is either the 32KiHz clock
|
|
|
+ * or sys_clk. Bypass rates on OMAP3 depend on the DPLL: DPLLs 1 and
|
|
|
+ * 2 are bypassed with dpll1_fclk and dpll2_fclk respectively
|
|
|
+ * (generated by DPLL3), while DPLL 3, 4, and 5 bypass rates are sys_clk.
|
|
|
+ * Returns the current DPLL CLKOUT rate (*not* CLKOUTX2) if the DPLL is
|
|
|
+ * locked, or the appropriate bypass rate if the DPLL is bypassed, or 0
|
|
|
+ * if the clock @clk is not a DPLL.
|
|
|
+ */
|
|
|
+unsigned long omap2_get_dpll_rate(struct clk_hw_omap *clk)
|
|
|
+{
|
|
|
+ long long dpll_clk;
|
|
|
+ u32 dpll_mult, dpll_div, v;
|
|
|
+ struct dpll_data *dd;
|
|
|
+
|
|
|
+ dd = clk->dpll_data;
|
|
|
+ if (!dd)
|
|
|
+ return 0;
|
|
|
+
|
|
|
+ /* Return bypass rate if DPLL is bypassed */
|