static int s3c_cpufreq_settarget()

in s3c24xx-cpufreq.c [148:262]


static int s3c_cpufreq_settarget(struct cpufreq_policy *policy,
				 unsigned int target_freq,
				 struct cpufreq_frequency_table *pll)
{
	struct s3c_cpufreq_freqs freqs;
	struct s3c_cpufreq_config cpu_new;
	unsigned long flags;

	cpu_new = cpu_cur;  /* copy new from current */

	s3c_cpufreq_show("cur", &cpu_cur);

	/* TODO - check for DMA currently outstanding */

	cpu_new.pll = pll ? *pll : cpu_cur.pll;

	if (pll)
		freqs.pll_changing = 1;

	/* update our frequencies */

	cpu_new.freq.armclk = target_freq;
	cpu_new.freq.fclk = cpu_new.pll.frequency;

	if (s3c_cpufreq_calcdivs(&cpu_new) < 0) {
		pr_err("no divisors for %d\n", target_freq);
		goto err_notpossible;
	}

	s3c_freq_dbg("%s: got divs\n", __func__);

	s3c_cpufreq_calc(&cpu_new);

	s3c_freq_dbg("%s: calculated frequencies for new\n", __func__);

	if (cpu_new.freq.hclk != cpu_cur.freq.hclk) {
		if (s3c_cpufreq_calcio(&cpu_new) < 0) {
			pr_err("%s: no IO timings\n", __func__);
			goto err_notpossible;
		}
	}

	s3c_cpufreq_show("new", &cpu_new);

	/* setup our cpufreq parameters */

	freqs.old = cpu_cur.freq;
	freqs.new = cpu_new.freq;

	freqs.freqs.old = cpu_cur.freq.armclk / 1000;
	freqs.freqs.new = cpu_new.freq.armclk / 1000;

	/* update f/h/p clock settings before we issue the change
	 * notification, so that drivers do not need to do anything
	 * special if they want to recalculate on CPUFREQ_PRECHANGE. */

	s3c_cpufreq_updateclk(_clk_mpll, cpu_new.pll.frequency);
	s3c_cpufreq_updateclk(clk_fclk, cpu_new.freq.fclk);
	s3c_cpufreq_updateclk(clk_hclk, cpu_new.freq.hclk);
	s3c_cpufreq_updateclk(clk_pclk, cpu_new.freq.pclk);

	/* start the frequency change */
	cpufreq_freq_transition_begin(policy, &freqs.freqs);

	/* If hclk is staying the same, then we do not need to
	 * re-write the IO or the refresh timings whilst we are changing
	 * speed. */

	local_irq_save(flags);

	/* is our memory clock slowing down? */
	if (cpu_new.freq.hclk < cpu_cur.freq.hclk) {
		s3c_cpufreq_setrefresh(&cpu_new);
		s3c_cpufreq_setio(&cpu_new);
	}

	if (cpu_new.freq.fclk == cpu_cur.freq.fclk) {
		/* not changing PLL, just set the divisors */

		s3c_cpufreq_setdivs(&cpu_new);
	} else {
		if (cpu_new.freq.fclk < cpu_cur.freq.fclk) {
			/* slow the cpu down, then set divisors */

			s3c_cpufreq_setfvco(&cpu_new);
			s3c_cpufreq_setdivs(&cpu_new);
		} else {
			/* set the divisors, then speed up */

			s3c_cpufreq_setdivs(&cpu_new);
			s3c_cpufreq_setfvco(&cpu_new);
		}
	}

	/* did our memory clock speed up */
	if (cpu_new.freq.hclk > cpu_cur.freq.hclk) {
		s3c_cpufreq_setrefresh(&cpu_new);
		s3c_cpufreq_setio(&cpu_new);
	}

	/* update our current settings */
	cpu_cur = cpu_new;

	local_irq_restore(flags);

	/* notify everyone we've done this */
	cpufreq_freq_transition_end(policy, &freqs.freqs, 0);

	s3c_freq_dbg("%s: finished\n", __func__);
	return 0;

 err_notpossible:
	pr_err("no compatible settings for %d\n", target_freq);
	return -EINVAL;
}