/** * omap36xx_gate_clk_enable_with_hsdiv_restore - enable clocks suffering * from HSDivider PWRDN problem Implements Errata ID: i556. * @clk: DPLL output struct clk * * 3630 only: dpll3_m3_ck, dpll4_m2_ck, dpll4_m3_ck, dpll4_m4_ck, * dpll4_m5_ck & dpll4_m6_ck dividers gets loaded with reset * valueafter their respective PWRDN bits are set. Any dummy write * (Any other value different from the Read value) to the * corresponding CM_CLKSEL register will refresh the dividers. */ static int omap36xx_gate_clk_enable_with_hsdiv_restore(struct clk_hw *hw) { struct clk_divider *parent; struct clk_hw *parent_hw; u32 dummy_v, orig_v; int ret; /* Clear PWRDN bit of HSDIVIDER */ ret = omap2_dflt_clk_enable(hw); /* Parent is the x2 node, get parent of parent for the m2 div */ parent_hw = clk_hw_get_parent(clk_hw_get_parent(hw)); parent = to_clk_divider(parent_hw); /* Restore the dividers */ if (!ret) { orig_v = ti_clk_ll_ops->clk_readl(parent->reg); dummy_v = orig_v; /* Write any other value different from the Read value */ dummy_v ^= (1 << parent->shift); ti_clk_ll_ops->clk_writel(dummy_v, parent->reg); /* Write the original divider */ ti_clk_ll_ops->clk_writel(orig_v, parent->reg); } return ret; }
static int clk_i2s_fracdiv_set_rate(struct clk_hw *hw, unsigned long rate, unsigned long parent_rate) { u32 numerator, denominator; struct clk_divider *div = to_clk_divider(hw); int i = 10; if(clk_fracdiv_get_config(rate, parent_rate, &numerator, &denominator) == 0) { while (i--) { writel((numerator - 1) << 16 | denominator, div->reg); mdelay(1); writel(numerator << 16 | denominator, div->reg); mdelay(1); } clk_debug("%s set rate=%lu,is ok\n", hw->clk->name, rate); } else { clk_err("clk_frac_div name=%s can't get rate=%lu\n", hw->clk->name, rate); return -EINVAL; } return 0; }
/** * omap36xx_pwrdn_clk_enable_with_hsdiv_restore - enable clocks suffering * from HSDivider PWRDN problem Implements Errata ID: i556. * @clk: DPLL output struct clk * * 3630 only: dpll3_m3_ck, dpll4_m2_ck, dpll4_m3_ck, dpll4_m4_ck, * dpll4_m5_ck & dpll4_m6_ck dividers gets loaded with reset * valueafter their respective PWRDN bits are set. Any dummy write * (Any other value different from the Read value) to the * corresponding CM_CLKSEL register will refresh the dividers. */ int omap36xx_pwrdn_clk_enable_with_hsdiv_restore(struct clk_hw *clk) { struct clk_divider *parent; struct clk_hw *parent_hw; u32 dummy_v, orig_v; struct clk_hw_omap *omap_clk = to_clk_hw_omap(clk); int ret; /* Clear PWRDN bit of HSDIVIDER */ ret = omap2_dflt_clk_enable(clk); parent_hw = __clk_get_hw(__clk_get_parent(clk->clk)); parent = to_clk_divider(parent_hw); /* Restore the dividers */ if (!ret) { orig_v = omap2_clk_readl(omap_clk, parent->reg); dummy_v = orig_v; /* Write any other value different from the Read value */ dummy_v ^= (1 << parent->shift); omap2_clk_writel(dummy_v, omap_clk, parent->reg); /* Write the original divider */ omap2_clk_writel(orig_v, omap_clk, parent->reg); } return ret; }
static long clk_div_round_rate_even(struct clk_hw *hw, unsigned long rate, unsigned long *prate) { int i = 0; struct clk_divider *divider =to_clk_divider(hw); int max_div = 1 << divider->width; for (i = 1; i <= max_div; i++) { if (i > 1 && (i % 2 != 0)) continue; if (rate >= (*prate / i)) return *prate / i; } return (*prate / max_div); }
static unsigned long clk_fracdiv_recalc(struct clk_hw *hw, unsigned long parent_rate) { unsigned long rate; u64 rate64; struct clk_divider *div = to_clk_divider(hw); u32 numerator, denominator, reg_val; reg_val = readl(div->reg); if (reg_val == 0) return parent_rate; numerator = reg_val >> 16; denominator = reg_val & 0xFFFF; rate64 = (u64)parent_rate * numerator; do_div(rate64, denominator); rate = rate64; clk_debug("%s: %s new clock rate is %lu, prate %lu (frac %u/%u)\n", __func__, hw->clk->name, rate, parent_rate, numerator, denominator); return rate; }