static int imx6q_set_target()

in imx6q-cpufreq.c [58:188]


static int imx6q_set_target(struct cpufreq_policy *policy, unsigned int index)
{
	struct dev_pm_opp *opp;
	unsigned long freq_hz, volt, volt_old;
	unsigned int old_freq, new_freq;
	bool pll1_sys_temp_enabled = false;
	int ret;

	new_freq = freq_table[index].frequency;
	freq_hz = new_freq * 1000;
	old_freq = clk_get_rate(clks[ARM].clk) / 1000;

	opp = dev_pm_opp_find_freq_ceil(cpu_dev, &freq_hz);
	if (IS_ERR(opp)) {
		dev_err(cpu_dev, "failed to find OPP for %ld\n", freq_hz);
		return PTR_ERR(opp);
	}

	volt = dev_pm_opp_get_voltage(opp);
	dev_pm_opp_put(opp);

	volt_old = regulator_get_voltage(arm_reg);

	dev_dbg(cpu_dev, "%u MHz, %ld mV --> %u MHz, %ld mV\n",
		old_freq / 1000, volt_old / 1000,
		new_freq / 1000, volt / 1000);

	/* scaling up?  scale voltage before frequency */
	if (new_freq > old_freq) {
		if (!IS_ERR(pu_reg)) {
			ret = regulator_set_voltage_tol(pu_reg, imx6_soc_volt[index], 0);
			if (ret) {
				dev_err(cpu_dev, "failed to scale vddpu up: %d\n", ret);
				return ret;
			}
		}
		ret = regulator_set_voltage_tol(soc_reg, imx6_soc_volt[index], 0);
		if (ret) {
			dev_err(cpu_dev, "failed to scale vddsoc up: %d\n", ret);
			return ret;
		}
		ret = regulator_set_voltage_tol(arm_reg, volt, 0);
		if (ret) {
			dev_err(cpu_dev,
				"failed to scale vddarm up: %d\n", ret);
			return ret;
		}
	}

	/*
	 * The setpoints are selected per PLL/PDF frequencies, so we need to
	 * reprogram PLL for frequency scaling.  The procedure of reprogramming
	 * PLL1 is as below.
	 * For i.MX6UL, it has a secondary clk mux, the cpu frequency change
	 * flow is slightly different from other i.MX6 OSC.
	 * The cpu frequeny change flow for i.MX6(except i.MX6UL) is as below:
	 *  - Enable pll2_pfd2_396m_clk and reparent pll1_sw_clk to it
	 *  - Reprogram pll1_sys_clk and reparent pll1_sw_clk back to it
	 *  - Disable pll2_pfd2_396m_clk
	 */
	if (of_machine_is_compatible("fsl,imx6ul") ||
	    of_machine_is_compatible("fsl,imx6ull")) {
		/*
		 * When changing pll1_sw_clk's parent to pll1_sys_clk,
		 * CPU may run at higher than 528MHz, this will lead to
		 * the system unstable if the voltage is lower than the
		 * voltage of 528MHz, so lower the CPU frequency to one
		 * half before changing CPU frequency.
		 */
		clk_set_rate(clks[ARM].clk, (old_freq >> 1) * 1000);
		clk_set_parent(clks[PLL1_SW].clk, clks[PLL1_SYS].clk);
		if (freq_hz > clk_get_rate(clks[PLL2_PFD2_396M].clk))
			clk_set_parent(clks[SECONDARY_SEL].clk,
				       clks[PLL2_BUS].clk);
		else
			clk_set_parent(clks[SECONDARY_SEL].clk,
				       clks[PLL2_PFD2_396M].clk);
		clk_set_parent(clks[STEP].clk, clks[SECONDARY_SEL].clk);
		clk_set_parent(clks[PLL1_SW].clk, clks[STEP].clk);
		if (freq_hz > clk_get_rate(clks[PLL2_BUS].clk)) {
			clk_set_rate(clks[PLL1_SYS].clk, new_freq * 1000);
			clk_set_parent(clks[PLL1_SW].clk, clks[PLL1_SYS].clk);
		}
	} else {
		clk_set_parent(clks[STEP].clk, clks[PLL2_PFD2_396M].clk);
		clk_set_parent(clks[PLL1_SW].clk, clks[STEP].clk);
		if (freq_hz > clk_get_rate(clks[PLL2_PFD2_396M].clk)) {
			clk_set_rate(clks[PLL1_SYS].clk, new_freq * 1000);
			clk_set_parent(clks[PLL1_SW].clk, clks[PLL1_SYS].clk);
		} else {
			/* pll1_sys needs to be enabled for divider rate change to work. */
			pll1_sys_temp_enabled = true;
			clk_prepare_enable(clks[PLL1_SYS].clk);
		}
	}

	/* Ensure the arm clock divider is what we expect */
	ret = clk_set_rate(clks[ARM].clk, new_freq * 1000);
	if (ret) {
		int ret1;

		dev_err(cpu_dev, "failed to set clock rate: %d\n", ret);
		ret1 = regulator_set_voltage_tol(arm_reg, volt_old, 0);
		if (ret1)
			dev_warn(cpu_dev,
				 "failed to restore vddarm voltage: %d\n", ret1);
		return ret;
	}

	/* PLL1 is only needed until after ARM-PODF is set. */
	if (pll1_sys_temp_enabled)
		clk_disable_unprepare(clks[PLL1_SYS].clk);

	/* scaling down?  scale voltage after frequency */
	if (new_freq < old_freq) {
		ret = regulator_set_voltage_tol(arm_reg, volt, 0);
		if (ret)
			dev_warn(cpu_dev,
				 "failed to scale vddarm down: %d\n", ret);
		ret = regulator_set_voltage_tol(soc_reg, imx6_soc_volt[index], 0);
		if (ret)
			dev_warn(cpu_dev, "failed to scale vddsoc down: %d\n", ret);
		if (!IS_ERR(pu_reg)) {
			ret = regulator_set_voltage_tol(pu_reg, imx6_soc_volt[index], 0);
			if (ret)
				dev_warn(cpu_dev, "failed to scale vddpu down: %d\n", ret);
		}
	}

	return 0;
}