/* linux/arch/arm/plat-s3c24xx/cpu-freq.c * * Copyright (c) 2006-2008 Simtec Electronics * http://armlinux.simtec.co.uk/ * Ben Dooks * * S3C24XX CPU Frequency scaling * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* note, cpufreq support deals in kHz, no Hz */ static struct cpufreq_driver s3c24xx_driver; static struct s3c_cpufreq_config cpu_cur; static struct s3c_iotimings s3c24xx_iotiming; static struct cpufreq_frequency_table *pll_reg; static unsigned int last_target = ~0; static unsigned int ftab_size; static struct cpufreq_frequency_table *ftab; static struct clk *_clk_mpll; static struct clk *_clk_xtal; static struct clk *clk_fclk; static struct clk *clk_hclk; static struct clk *clk_pclk; static struct clk *clk_arm; #ifdef CONFIG_CPU_FREQ_S3C24XX_DEBUGFS struct s3c_cpufreq_config *s3c_cpufreq_getconfig(void) { return &cpu_cur; } struct s3c_iotimings *s3c_cpufreq_getiotimings(void) { return &s3c24xx_iotiming; } #endif /* CONFIG_CPU_FREQ_S3C24XX_DEBUGFS */ static void s3c_cpufreq_getcur(struct s3c_cpufreq_config *cfg) { unsigned long fclk, pclk, hclk, armclk; cfg->freq.fclk = fclk = clk_get_rate(clk_fclk); cfg->freq.hclk = hclk = clk_get_rate(clk_hclk); cfg->freq.pclk = pclk = clk_get_rate(clk_pclk); cfg->freq.armclk = armclk = clk_get_rate(clk_arm); cfg->pll.index = __raw_readl(S3C2410_MPLLCON); cfg->pll.frequency = fclk; cfg->freq.hclk_tns = 1000000000 / (cfg->freq.hclk / 10); cfg->divs.h_divisor = fclk / hclk; cfg->divs.p_divisor = fclk / pclk; } static inline void s3c_cpufreq_calc(struct s3c_cpufreq_config *cfg) { unsigned long pll = cfg->pll.frequency; cfg->freq.fclk = pll; cfg->freq.hclk = pll / cfg->divs.h_divisor; cfg->freq.pclk = pll / cfg->divs.p_divisor; /* convert hclk into 10ths of nanoseconds for io calcs */ cfg->freq.hclk_tns = 1000000000 / (cfg->freq.hclk / 10); } static inline int closer(unsigned int target, unsigned int n, unsigned int c) { int diff_cur = abs(target - c); int diff_new = abs(target - n); return (diff_new < diff_cur); } static void s3c_cpufreq_show(const char *pfx, struct s3c_cpufreq_config *cfg) { s3c_freq_dbg("%s: Fvco=%u, F=%lu, A=%lu, H=%lu (%u), P=%lu (%u)\n", pfx, cfg->pll.frequency, cfg->freq.fclk, cfg->freq.armclk, cfg->freq.hclk, cfg->divs.h_divisor, cfg->freq.pclk, cfg->divs.p_divisor); } /* functions to wrapper the driver info calls to do the cpu specific work */ static void s3c_cpufreq_setio(struct s3c_cpufreq_config *cfg) { if (cfg->info->set_iotiming) (cfg->info->set_iotiming)(cfg, &s3c24xx_iotiming); } static int s3c_cpufreq_calcio(struct s3c_cpufreq_config *cfg) { if (cfg->info->calc_iotiming) return (cfg->info->calc_iotiming)(cfg, &s3c24xx_iotiming); return 0; } static void s3c_cpufreq_setrefresh(struct s3c_cpufreq_config *cfg) { (cfg->info->set_refresh)(cfg); } static void s3c_cpufreq_setdivs(struct s3c_cpufreq_config *cfg) { (cfg->info->set_divs)(cfg); } static int s3c_cpufreq_calcdivs(struct s3c_cpufreq_config *cfg) { return (cfg->info->calc_divs)(cfg); } static void s3c_cpufreq_setfvco(struct s3c_cpufreq_config *cfg) { (cfg->info->set_fvco)(cfg); } static inline void s3c_cpufreq_resume_clocks(void) { cpu_cur.info->resume_clocks(); } static inline void s3c_cpufreq_updateclk(struct clk *clk, unsigned int freq) { clk_set_rate(clk, freq); }