| 
					
				 | 
			
			
				@@ -156,3 +156,175 @@ static inline void s3c_cpufreq_updateclk(struct clk *clk, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 	clk_set_rate(clk, freq); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+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) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		printk(KERN_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) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+			printk(KERN_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.cpu = 0; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	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 */ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	if (policy) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		cpufreq_notify_transition(&freqs.freqs, CPUFREQ_PRECHANGE); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	/* 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 */ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	if (policy) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		cpufreq_notify_transition(&freqs.freqs, CPUFREQ_POSTCHANGE); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	s3c_freq_dbg("%s: finished\n", __func__); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	return 0; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ err_notpossible: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	printk(KERN_ERR "no compatible settings for %d\n", target_freq); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	return -EINVAL; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+/* s3c_cpufreq_target 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ * 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ * called by the cpufreq core to adjust the frequency that the CPU 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ * is currently running at. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ */ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+static int s3c_cpufreq_target(struct cpufreq_policy *policy, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+			      unsigned int target_freq, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+			      unsigned int relation) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+{ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	struct cpufreq_frequency_table *pll; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	unsigned int index; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	/* avoid repeated calls which cause a needless amout of duplicated 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	 * logging output (and CPU time as the calculation process is 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	 * done) */ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	if (target_freq == last_target) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		return 0; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	last_target = target_freq; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	s3c_freq_dbg("%s: policy %p, target %u, relation %u\n", 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		     __func__, policy, target_freq, relation); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	if (ftab) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		if (cpufreq_frequency_table_target(policy, ftab, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+						   target_freq, relation, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+						   &index)) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+			s3c_freq_dbg("%s: table failed\n", __func__); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+			return -EINVAL; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		s3c_freq_dbg("%s: adjust %d to entry %d (%u)\n", __func__, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+			     target_freq, index, ftab[index].frequency); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		target_freq = ftab[index].frequency; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	target_freq *= 1000;  /* convert target to Hz */ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	/* find the settings for our new frequency */ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	if (!pll_reg || cpu_cur.lock_pll) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		/* either we've not got any PLL values, or we've locked 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		 * to the current one. */ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		pll = NULL; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	} else { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		struct cpufreq_policy tmp_policy; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		int ret; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		/* we keep the cpu pll table in Hz, to ensure we get an 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		 * accurate value for the PLL output. */ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 |