コード例 #1
0
/**
 * omap3_noncore_dpll_enable - instruct a DPLL to enter bypass or lock mode
 * @clk: pointer to a DPLL struct clk
 *
 * Instructs a non-CORE DPLL to enable, e.g., to enter bypass or lock.
 * The choice of modes depends on the DPLL's programmed rate: if it is
 * the same as the DPLL's parent clock, it will enter bypass;
 * otherwise, it will enter lock.  This code will wait for the DPLL to
 * indicate readiness before returning, unless the DPLL takes too long
 * to enter the target state.  Intended to be used as the struct clk's
 * enable function.  If DPLL3 was passed in, or the DPLL does not
 * support low-power stop, or if the DPLL took too long to enter
 * bypass or lock, return -EINVAL; otherwise, return 0.
 */
static int omap3_noncore_dpll_enable(struct clk *clk)
{
	int r;
	struct dpll_data *dd;

	if (clk == &dpll3_ck)
		return -EINVAL;

	dd = clk->dpll_data;
	if (!dd)
		return -EINVAL;

	if (clk->rate == dd->bypass_clk->rate)
		r = _omap3_noncore_dpll_bypass(clk);
	else
		r = _omap3_noncore_dpll_lock(clk);

	return r;
}
コード例 #2
0
/*
 * omap3_noncore_dpll_program - set non-core DPLL M,N values directly
 * @clk: struct clk * of DPLL to set
 * @m: DPLL multiplier to set
 * @n: DPLL divider to set
 * @freqsel: FREQSEL value to set
 *
 * Program the DPLL with the supplied M, N values, and wait for the DPLL to
 * lock..  Returns -EINVAL upon error, or 0 upon success.
 */
static int omap3_noncore_dpll_program(struct clk *clk, u16 m, u8 n, u16 freqsel)
{
	struct dpll_data *dd = clk->dpll_data;
	u32 v;

	/* 3430 ES2 TRM: 4.7.6.9 DPLL Programming Sequence */
	_omap3_noncore_dpll_bypass(clk);

	/* Set jitter correction */
	v = __raw_readl(dd->control_reg);
	v &= ~dd->freqsel_mask;
	v |= freqsel << __ffs(dd->freqsel_mask);
	__raw_writel(v, dd->control_reg);

	/* Set DPLL multiplier, divider */
	v = __raw_readl(dd->mult_div1_reg);
	v &= ~(dd->mult_mask | dd->div1_mask);
	v |= m << __ffs(dd->mult_mask);
	v |= (n - 1) << __ffs(dd->div1_mask);
	if (dd->jtype) {
		u8 dco, sd_div;
		lookup_dco_sddiv(clk, &dco, &sd_div, m, n);
		v &= ~(dd->dco_sel_mask | dd->sd_div_mask);
		v |=  dco << __ffs(dd->dco_sel_mask);
		v |=  sd_div << __ffs(dd->sd_div_mask);
	}
	__raw_writel(v, dd->mult_div1_reg);

	/* We let the clock framework set the other output dividers later */

	/* REVISIT: Set ramp-up delay? */

	_omap3_noncore_dpll_lock(clk);

	return 0;
}
コード例 #3
0
ファイル: dpll3xxx.c プロジェクト: travistabbal/kindle-kernel
/**
 * omap3_noncore_dpll_set_rate - set non-core DPLL rate
 * @clk: struct clk * of DPLL to set
 * @rate: rounded target rate
 *
 * Set the DPLL CLKOUT to the target rate.  If the DPLL can enter
 * low-power bypass, and the target rate is the bypass source clock
 * rate, then configure the DPLL for bypass.  Otherwise, round the
 * target rate if it hasn't been done already, then program and lock
 * the DPLL.  Returns -EINVAL upon error, or 0 upon success.
 */
int omap3_noncore_dpll_set_rate(struct clk *clk, unsigned long rate)
{
	struct clk *new_parent = NULL;
	u16 freqsel = 0;
	struct dpll_data *dd;
	int ret;
	unsigned long orig_rate = 0;

	if (!clk || !rate)
		return -EINVAL;

	dd = clk->dpll_data;
	if (!dd)
		return -EINVAL;

	if (rate == omap2_get_dpll_rate(clk))
		return 0;

	/*
	 * Ensure both the bypass and ref clocks are enabled prior to
	 * doing anything; we need the bypass clock running to reprogram
	 * the DPLL.
	 */
	omap2_clk_enable(dd->clk_bypass);
	omap2_clk_enable(dd->clk_ref);

	if (dd->clk_bypass->rate == rate &&
	    (clk->dpll_data->modes & (1 << DPLL_LOW_POWER_BYPASS))) {
		pr_debug("clock: %s: set rate: entering bypass.\n", clk->name);

		ret = _omap3_noncore_dpll_bypass(clk);
		if (!ret)
			new_parent = dd->clk_bypass;
	} else {
		/*
		 * On 4460, the MPU clk for frequencies higher than 1Ghz
		 * is sourced from CLKOUTX2_M3, instead of CLKOUT_M2, while
		 * value of M3 is fixed to 1. Hence for frequencies higher
		 * than 1 Ghz, lock the DPLL at half the rate so the
		 * CLKOUTX2_M3 then matches the requested rate.
		 */
		if (cpu_is_omap4460() && !strcmp(clk->name, "dpll_mpu_ck")
					&& (rate > 1000000000)) {
			orig_rate = rate;
			rate = rate/2;
		}
		if (dd->last_rounded_rate != rate)
			rate = clk->round_rate(clk, rate);

		if (dd->last_rounded_rate == 0)
			return -EINVAL;

		/* No freqsel on OMAP4 and OMAP3630 */
		if (!cpu_is_omap44xx() && !cpu_is_omap3630()) {
			freqsel = _omap3_dpll_compute_freqsel(clk,
						dd->last_rounded_n);
			if (!freqsel)
				WARN_ON(1);
		}

		/* Set the rate back to original for book keeping*/
		if (orig_rate)
			rate = orig_rate;
		pr_debug("clock: %s: set rate: locking rate to %lu.\n",
			 clk->name, rate);

		ret = omap3_noncore_dpll_program(clk, dd->last_rounded_m,
				 dd->last_rounded_n, freqsel, orig_rate);
		if (!ret)
			new_parent = dd->clk_ref;
	}
	if (!ret) {
		/*
		 * Switch the parent clock in the heirarchy, and make sure
		 * that the new parent's usecount is correct.  Note: we
		 * enable the new parent before disabling the old to avoid
		 * any unnecessary hardware disable->enable transitions.
		 */
		if (clk->usecount) {
			omap2_clk_enable(new_parent);
			omap2_clk_disable(clk->parent);
		}
		clk_reparent(clk, new_parent);
		clk->rate = rate;
	}
	omap2_clk_disable(dd->clk_ref);
	omap2_clk_disable(dd->clk_bypass);

	return 0;
}
コード例 #4
0
ファイル: dpll3xxx.c プロジェクト: travistabbal/kindle-kernel
/*
 * _omap3_noncore_dpll_program - set non-core DPLL M,N values directly
 * @clk: struct clk * of DPLL to set
 * @m: DPLL multiplier to set
 * @n: DPLL divider to set
 * @freqsel: FREQSEL value to set
 *
 * Program the DPLL with the supplied M, N values, and wait for the DPLL to
 * lock..  Returns -EINVAL upon error, or 0 upon success.
 */
static int omap3_noncore_dpll_program(struct clk *clk, u16 m, u8 n, u16 freqsel,
						unsigned long orig_rate)
{
	struct dpll_data *dd = clk->dpll_data;
	u8 dco, sd_div;
	u32 v;

	/*
	 * On OMAP4460, to obtain MPU DPLL frequency higher
	 * than 1GHz, DCC (Duty Cycle Correction) needs to
	 * be enabled.
	 * Also the interconnect frequency to EMIF should
	 * be switched between MPU clk divide by 4 (for
	 * frequencies higher than 920Mhz) and MPU clk divide
	 * by 2 (for frequencies lower than or equal to 920Mhz)
	 * Lastly the async bridge to ABE must be MPU clk divide
	 * by 8 for MPU clk > 748Mhz and MPU clk divide by 4
	 * for lower frequencies.
	 * TODO: For now use a strcmp, but need to find a
	 * better way to identify the MPU dpll.
	 */
	if (cpu_is_omap4460() && !strcmp(clk->name, "dpll_mpu_ck")) {
		/* DCC control */
		v = __raw_readl(dd->mult_div1_reg);
		if ((orig_rate <= 1000000000) && (v & OMAP4460_DCC_EN_MASK)) {
			v &= ~OMAP4460_DCC_EN_MASK; /* Disable DCC */
			__raw_writel(v, dd->mult_div1_reg);
		}
	}

	/* 3430 ES2 TRM: 4.7.6.9 DPLL Programming Sequence */
	_omap3_noncore_dpll_bypass(clk);

	/*
	 * Set jitter correction. No jitter correction for OMAP4 and 3630
	 * since freqsel field is no longer present
	 */
	if (!cpu_is_omap44xx() && !cpu_is_omap3630()) {
		v = __raw_readl(dd->control_reg);
		v &= ~dd->freqsel_mask;
		v |= freqsel << __ffs(dd->freqsel_mask);
		__raw_writel(v, dd->control_reg);
	}

	/* Set DPLL multiplier, divider */
	v = __raw_readl(dd->mult_div1_reg);
	v &= ~(dd->mult_mask | dd->div1_mask);
	v |= m << __ffs(dd->mult_mask);
	v |= (n - 1) << __ffs(dd->div1_mask);

	/* Configure dco and sd_div for dplls that have these fields */
	if (dd->dco_mask) {
		_lookup_dco(clk, &dco, m, n);
		v &= ~(dd->dco_mask);
		v |= dco << __ffs(dd->dco_mask);
	}
	if (dd->sddiv_mask) {
		_lookup_sddiv(clk, &sd_div, m, n);
		v &= ~(dd->sddiv_mask);
		v |= sd_div << __ffs(dd->sddiv_mask);
	}

	__raw_writel(v, dd->mult_div1_reg);

	/* We let the clock framework set the other output dividers later */

	/* REVISIT: Set ramp-up delay? */

	_omap3_noncore_dpll_lock(clk);

	if (cpu_is_omap4460() && !strcmp(clk->name, "dpll_mpu_ck")) {
		/* DCC control */
		if (orig_rate > 1000000000) {
			v &= ~OMAP4460_DCC_COUNT_MAX_MASK;
			v |= (5 << OMAP4460_DCC_COUNT_MAX_SHIFT);
			__raw_writel(v, dd->mult_div1_reg);
			v |= OMAP4460_DCC_EN_MASK; /* Enable DCC */
			__raw_writel(v, dd->mult_div1_reg);
		}

		/* EMIF/ABE clock rate control */
		v = __raw_readl(OMAP4430_CM_MPU_MPU_CLKCTRL);
		if (orig_rate > 920000000)
			v |= OMAP4460_CLKSEL_EMIF_DIV_MODE_MASK;
		else
			v &= ~OMAP4460_CLKSEL_EMIF_DIV_MODE_MASK;

		if (orig_rate > 748000000)
			v |= OMAP4460_CLKSEL_ABE_DIV_MODE_MASK;
		else
			v &= ~OMAP4460_CLKSEL_ABE_DIV_MODE_MASK;
		__raw_writel(v, OMAP4430_CM_MPU_MPU_CLKCTRL);
	}

	return 0;
}
コード例 #5
0
ファイル: dpll3xxx.c プロジェクト: suiye223/p920-ICS-kernel
/**
 * omap3_noncore_dpll_set_rate - set non-core DPLL rate
 * @clk: struct clk * of DPLL to set
 * @rate: rounded target rate
 *
 * Set the DPLL CLKOUT to the target rate.  If the DPLL can enter
 * low-power bypass, and the target rate is the bypass source clock
 * rate, then configure the DPLL for bypass.  Otherwise, round the
 * target rate if it hasn't been done already, then program and lock
 * the DPLL.  Returns -EINVAL upon error, or 0 upon success.
 */
int omap3_noncore_dpll_set_rate(struct clk *clk, unsigned long rate)
{
    struct clk *new_parent = NULL;
    u16 freqsel = 0;
    struct dpll_data *dd;
    int ret;

    if (!clk || !rate)
        return -EINVAL;

    dd = clk->dpll_data;
    if (!dd)
        return -EINVAL;

    if (rate == omap2_get_dpll_rate(clk))
        return 0;

    /*
     * Ensure both the bypass and ref clocks are enabled prior to
     * doing anything; we need the bypass clock running to reprogram
     * the DPLL.
     */
    omap2_clk_enable(dd->clk_bypass);
    omap2_clk_enable(dd->clk_ref);

    if (dd->clk_bypass->rate == rate &&
            (clk->dpll_data->modes & (1 << DPLL_LOW_POWER_BYPASS))) {
        pr_debug("clock: %s: set rate: entering bypass.\n", clk->name);

        ret = _omap3_noncore_dpll_bypass(clk);
        if (!ret)
            new_parent = dd->clk_bypass;
    } else {
        if (dd->last_rounded_rate != rate)
            rate = clk->round_rate(clk, rate);

        if (dd->last_rounded_rate == 0)
            return -EINVAL;

        /* No freqsel on OMAP4 and OMAP3630 */
        if (!cpu_is_omap44xx() && !cpu_is_omap3630()) {
            freqsel = _omap3_dpll_compute_freqsel(clk,
                                                  dd->last_rounded_n);
            if (!freqsel)
                WARN_ON(1);
        }

        pr_debug("clock: %s: set rate: locking rate to %lu.\n",
                 clk->name, rate);

        ret = omap3_noncore_dpll_program(clk, dd->last_rounded_m,
                                         dd->last_rounded_n, freqsel);
        if (!ret)
            new_parent = dd->clk_ref;
    }
    if (!ret) {
        /*
         * Switch the parent clock in the hierarchy, and make sure
         * that the new parent's usecount is correct.  Note: we
         * enable the new parent before disabling the old to avoid
         * any unnecessary hardware disable->enable transitions.
         */
        if (clk->usecount) {
            omap2_clk_enable(new_parent);
            omap2_clk_disable(clk->parent);
        }
        clk_reparent(clk, new_parent);
        clk->rate = rate;
    }
    omap2_clk_disable(dd->clk_ref);
    omap2_clk_disable(dd->clk_bypass);

    return 0;
}
コード例 #6
0
/**
 * omap3_noncore_dpll_set_rate - set non-core DPLL rate
 * @clk: struct clk * of DPLL to set
 * @rate: rounded target rate
 *
 * Set the DPLL CLKOUT to the target rate.  If the DPLL can enter
 * low-power bypass, and the target rate is the bypass source clock
 * rate, then configure the DPLL for bypass.  Otherwise, round the
 * target rate if it hasn't been done already, then program and lock
 * the DPLL.  Returns -EINVAL upon error, or 0 upon success.
 */
int omap3_noncore_dpll_set_rate(struct clk_hw *hw, unsigned long rate,
					unsigned long parent_rate)
{
	struct clk_hw_omap *clk = to_clk_hw_omap(hw);
	struct clk *new_parent = NULL;
	unsigned long rrate;
	u16 freqsel = 0;
	struct dpll_data *dd;
	int ret;

	if (!hw || !rate)
		return -EINVAL;

	dd = clk->dpll_data;
	if (!dd)
		return -EINVAL;

	if (__clk_get_rate(dd->clk_bypass) == rate &&
	    (dd->modes & (1 << DPLL_LOW_POWER_BYPASS))) {
		pr_debug("%s: %s: set rate: entering bypass.\n",
			 __func__, __clk_get_name(hw->clk));

		__clk_prepare(dd->clk_bypass);
		clk_enable(dd->clk_bypass);
		ret = _omap3_noncore_dpll_bypass(clk);
		if (!ret)
			new_parent = dd->clk_bypass;
		clk_disable(dd->clk_bypass);
		__clk_unprepare(dd->clk_bypass);
	} else {
		__clk_prepare(dd->clk_ref);
		clk_enable(dd->clk_ref);

		if (dd->last_rounded_rate != rate) {
			rrate = __clk_round_rate(hw->clk, rate);
			if (rrate != rate) {
				pr_warn("%s: %s: final rate %lu does not match desired rate %lu\n",
					__func__, __clk_get_name(hw->clk),
					rrate, rate);
				rate = rrate;
			}
		}

		if (dd->last_rounded_rate == 0)
			return -EINVAL;

		/* Freqsel is available only on OMAP343X devices */
		if (cpu_is_omap343x()) {
			freqsel = _omap3_dpll_compute_freqsel(clk,
						dd->last_rounded_n);
			WARN_ON(!freqsel);
		}

		pr_debug("%s: %s: set rate: locking rate to %lu.\n",
			 __func__, __clk_get_name(hw->clk), rate);

		ret = omap3_noncore_dpll_program(clk, freqsel);
		if (!ret)
			new_parent = dd->clk_ref;
		clk_disable(dd->clk_ref);
		__clk_unprepare(dd->clk_ref);
	}
	/*
	* FIXME - this is all wrong.  common code handles reparenting and
	* migrating prepare/enable counts.  dplls should be a multiplexer
	* clock and this should be a set_parent operation so that all of that
	* stuff is inherited for free
	*/

	if (!ret && clk_get_parent(hw->clk) != new_parent)
		__clk_reparent(hw->clk, new_parent);

	return 0;
}
コード例 #7
0
/*
 * _omap3_noncore_dpll_program - set non-core DPLL M,N values directly
 * @clk:	struct clk * of DPLL to set
 * @freqsel:	FREQSEL value to set
 *
 * Program the DPLL with the last M, N values calculated, and wait for
 * the DPLL to lock. Returns -EINVAL upon error, or 0 upon success.
 */
static int omap3_noncore_dpll_program(struct clk_hw_omap *clk, u16 freqsel)
{
	struct dpll_data *dd = clk->dpll_data;
	u8 dco, sd_div;
	u32 v;

	/* 3430 ES2 TRM: 4.7.6.9 DPLL Programming Sequence */
	_omap3_noncore_dpll_bypass(clk);

	if (dd->sink_clkdm)
		clkdm_clk_enable(dd->sink_clkdm, clk->hw.clk);

	/*
	 * Set jitter correction. Jitter correction applicable for OMAP343X
	 * only since freqsel field is no longer present on other devices.
	 */
	if (cpu_is_omap343x()) {
		v = omap2_clk_readl(clk, dd->control_reg);
		v &= ~dd->freqsel_mask;
		v |= freqsel << __ffs(dd->freqsel_mask);
		omap2_clk_writel(v, clk, dd->control_reg);
	}

	/* Set DPLL multiplier, divider */
	v = omap2_clk_readl(clk, dd->mult_div1_reg);

	/* Handle Duty Cycle Correction */
	if (dd->dcc_mask) {
		if (dd->last_rounded_rate >= dd->dcc_rate)
			v |= dd->dcc_mask; /* Enable DCC */
		else
			v &= ~dd->dcc_mask; /* Disable DCC */
	}

	v &= ~(dd->mult_mask | dd->div1_mask);
	v |= dd->last_rounded_m << __ffs(dd->mult_mask);
	v |= (dd->last_rounded_n - 1) << __ffs(dd->div1_mask);

	/* Configure dco and sd_div for dplls that have these fields */
	if (dd->dco_mask) {
		_lookup_dco(clk, &dco, dd->last_rounded_m, dd->last_rounded_n);
		v &= ~(dd->dco_mask);
		v |= dco << __ffs(dd->dco_mask);
	}
	if (dd->sddiv_mask) {
		_lookup_sddiv(clk, &sd_div, dd->last_rounded_m,
			      dd->last_rounded_n);
		v &= ~(dd->sddiv_mask);
		v |= sd_div << __ffs(dd->sddiv_mask);
	}

	omap2_clk_writel(v, clk, dd->mult_div1_reg);

	/* Set 4X multiplier and low-power mode */
	if (dd->m4xen_mask || dd->lpmode_mask) {
		v = omap2_clk_readl(clk, dd->control_reg);

		if (dd->m4xen_mask) {
			if (dd->last_rounded_m4xen)
				v |= dd->m4xen_mask;
			else
				v &= ~dd->m4xen_mask;
		}

		if (dd->lpmode_mask) {
			if (dd->last_rounded_lpmode)
				v |= dd->lpmode_mask;
			else
				v &= ~dd->lpmode_mask;
		}

		omap2_clk_writel(v, clk, dd->control_reg);
	}

	/* We let the clock framework set the other output dividers later */

	/* REVISIT: Set ramp-up delay? */

	_omap3_noncore_dpll_lock(clk);

	if (dd->sink_clkdm)
		clkdm_clk_disable(dd->sink_clkdm, clk->hw.clk);

	return 0;
}
コード例 #8
0
ファイル: dpll3xxx.c プロジェクト: AlexShiLucky/linux
/*
 * _omap3_noncore_dpll_program - set non-core DPLL M,N values directly
 * @clk:	struct clk * of DPLL to set
 * @freqsel:	FREQSEL value to set
 *
 * Program the DPLL with the last M, N values calculated, and wait for
 * the DPLL to lock. Returns -EINVAL upon error, or 0 upon success.
 */
static int omap3_noncore_dpll_program(struct clk_hw_omap *clk, u16 freqsel)
{
	struct dpll_data *dd = clk->dpll_data;
	u8 dco, sd_div, ai = 0;
	u32 v;
	bool errata_i810;

	/* 3430 ES2 TRM: 4.7.6.9 DPLL Programming Sequence */
	_omap3_noncore_dpll_bypass(clk);

	/*
	 * Set jitter correction. Jitter correction applicable for OMAP343X
	 * only since freqsel field is no longer present on other devices.
	 */
	if (ti_clk_get_features()->flags & TI_CLK_DPLL_HAS_FREQSEL) {
		v = ti_clk_ll_ops->clk_readl(&dd->control_reg);
		v &= ~dd->freqsel_mask;
		v |= freqsel << __ffs(dd->freqsel_mask);
		ti_clk_ll_ops->clk_writel(v, &dd->control_reg);
	}

	/* Set DPLL multiplier, divider */
	v = ti_clk_ll_ops->clk_readl(&dd->mult_div1_reg);

	/* Handle Duty Cycle Correction */
	if (dd->dcc_mask) {
		if (dd->last_rounded_rate >= dd->dcc_rate)
			v |= dd->dcc_mask; /* Enable DCC */
		else
			v &= ~dd->dcc_mask; /* Disable DCC */
	}

	v &= ~(dd->mult_mask | dd->div1_mask);
	v |= dd->last_rounded_m << __ffs(dd->mult_mask);
	v |= (dd->last_rounded_n - 1) << __ffs(dd->div1_mask);

	/* Configure dco and sd_div for dplls that have these fields */
	if (dd->dco_mask) {
		_lookup_dco(clk, &dco, dd->last_rounded_m, dd->last_rounded_n);
		v &= ~(dd->dco_mask);
		v |= dco << __ffs(dd->dco_mask);
	}
	if (dd->sddiv_mask) {
		_lookup_sddiv(clk, &sd_div, dd->last_rounded_m,
			      dd->last_rounded_n);
		v &= ~(dd->sddiv_mask);
		v |= sd_div << __ffs(dd->sddiv_mask);
	}

	/*
	 * Errata i810 - DPLL controller can get stuck while transitioning
	 * to a power saving state. Software must ensure the DPLL can not
	 * transition to a low power state while changing M/N values.
	 * Easiest way to accomplish this is to prevent DPLL autoidle
	 * before doing the M/N re-program.
	 */
	errata_i810 = ti_clk_get_features()->flags & TI_CLK_ERRATA_I810;

	if (errata_i810) {
		ai = omap3_dpll_autoidle_read(clk);
		if (ai) {
			omap3_dpll_deny_idle(clk);

			/* OCP barrier */
			omap3_dpll_autoidle_read(clk);
		}
	}

	ti_clk_ll_ops->clk_writel(v, &dd->mult_div1_reg);

	/* Set 4X multiplier and low-power mode */
	if (dd->m4xen_mask || dd->lpmode_mask) {
		v = ti_clk_ll_ops->clk_readl(&dd->control_reg);

		if (dd->m4xen_mask) {
			if (dd->last_rounded_m4xen)
				v |= dd->m4xen_mask;
			else
				v &= ~dd->m4xen_mask;
		}

		if (dd->lpmode_mask) {
			if (dd->last_rounded_lpmode)
				v |= dd->lpmode_mask;
			else
				v &= ~dd->lpmode_mask;
		}

		ti_clk_ll_ops->clk_writel(v, &dd->control_reg);
	}

	/* We let the clock framework set the other output dividers later */

	/* REVISIT: Set ramp-up delay? */

	_omap3_noncore_dpll_lock(clk);

	if (errata_i810 && ai)
		omap3_dpll_allow_idle(clk);

	return 0;
}
コード例 #9
0
static int omap3_noncore_dpll_set_rate(struct clk *clk, unsigned long rate)
{
	struct clk *new_parent = NULL;
	u16 freqsel;
	struct dpll_data *dd;
	int ret;

	if (!clk || !rate)
		return -EINVAL;

	dd = clk->dpll_data;
	if (!dd)
		return -EINVAL;

	if (rate == omap2_get_dpll_rate(clk))
		return 0;

	
	omap2_clk_enable(dd->clk_bypass);
	omap2_clk_enable(dd->clk_ref);

	if (dd->clk_bypass->rate == rate &&
	    (clk->dpll_data->modes & (1 << DPLL_LOW_POWER_BYPASS))) {
		pr_debug("clock: %s: set rate: entering bypass.\n", clk->name);

		ret = _omap3_noncore_dpll_bypass(clk);
		if (!ret)
			new_parent = dd->clk_bypass;
	} else {
		if (dd->last_rounded_rate != rate)
			omap2_dpll_round_rate(clk, rate);

		if (dd->last_rounded_rate == 0)
			return -EINVAL;

		freqsel = _omap3_dpll_compute_freqsel(clk, dd->last_rounded_n);
		if (!freqsel)
			WARN_ON(1);

		pr_debug("clock: %s: set rate: locking rate to %lu.\n",
			 clk->name, rate);

		ret = omap3_noncore_dpll_program(clk, dd->last_rounded_m,
						 dd->last_rounded_n, freqsel);
		if (!ret)
			new_parent = dd->clk_ref;
	}
	if (!ret) {
		
		if (clk->usecount) {
			omap2_clk_enable(new_parent);
			omap2_clk_disable(clk->parent);
		}
		clk_reparent(clk, new_parent);
		clk->rate = rate;
	}
	omap2_clk_disable(dd->clk_ref);
	omap2_clk_disable(dd->clk_bypass);

	return 0;
}
コード例 #10
0
ファイル: dpll3xxx.c プロジェクト: Forzaferrarileo/linux
/**
 * omap3_noncore_dpll_set_rate - set non-core DPLL rate
 * @clk: struct clk * of DPLL to set
 * @rate: rounded target rate
 *
 * Set the DPLL CLKOUT to the target rate.  If the DPLL can enter
 * low-power bypass, and the target rate is the bypass source clock
 * rate, then configure the DPLL for bypass.  Otherwise, round the
 * target rate if it hasn't been done already, then program and lock
 * the DPLL.  Returns -EINVAL upon error, or 0 upon success.
 */
int omap3_noncore_dpll_set_rate(struct clk_hw *hw, unsigned long rate,
					unsigned long parent_rate)
{
	struct clk_hw_omap *clk = to_clk_hw_omap(hw);
	struct clk *new_parent = NULL;
	u16 freqsel = 0;
	struct dpll_data *dd;
	int ret;

	if (!hw || !rate)
		return -EINVAL;

	dd = clk->dpll_data;
	if (!dd)
		return -EINVAL;

	__clk_prepare(dd->clk_bypass);
	clk_enable(dd->clk_bypass);
	__clk_prepare(dd->clk_ref);
	clk_enable(dd->clk_ref);

	if (__clk_get_rate(dd->clk_bypass) == rate &&
	    (dd->modes & (1 << DPLL_LOW_POWER_BYPASS))) {
		pr_debug("%s: %s: set rate: entering bypass.\n",
			 __func__, __clk_get_name(hw->clk));

		ret = _omap3_noncore_dpll_bypass(clk);
		if (!ret)
			new_parent = dd->clk_bypass;
	} else {
		if (dd->last_rounded_rate != rate)
			rate = __clk_round_rate(hw->clk, rate);

		if (dd->last_rounded_rate == 0)
			return -EINVAL;

		/* No freqsel on OMAP4 and OMAP3630 */
		if (!cpu_is_omap44xx() && !cpu_is_omap3630()) {
			freqsel = _omap3_dpll_compute_freqsel(clk,
						dd->last_rounded_n);
			if (!freqsel)
				WARN_ON(1);
		}

		pr_debug("%s: %s: set rate: locking rate to %lu.\n",
			 __func__, __clk_get_name(hw->clk), rate);

		ret = omap3_noncore_dpll_program(clk, dd->last_rounded_m,
						dd->last_rounded_n, freqsel);
		if (!ret)
			new_parent = dd->clk_ref;
	}
	/*
	* FIXME - this is all wrong.  common code handles reparenting and
	* migrating prepare/enable counts.  dplls should be a multiplexer
	* clock and this should be a set_parent operation so that all of that
	* stuff is inherited for free
	*/

	if (!ret)
		__clk_reparent(hw->clk, new_parent);

	clk_disable(dd->clk_ref);
	__clk_unprepare(dd->clk_ref);
	clk_disable(dd->clk_bypass);
	__clk_unprepare(dd->clk_bypass);

	return 0;
}