Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

at v5.9-rc7 252 lines 5.9 kB view raw
1// SPDX-License-Identifier: GPL-2.0-only 2/* 3 * Copyright 2008 Simtec Electronics 4 * http://armlinux.simtec.co.uk/ 5 * Ben Dooks <ben@simtec.co.uk> 6 * 7 * S3C2412 CPU Frequency scalling 8*/ 9 10#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 11 12#include <linux/init.h> 13#include <linux/module.h> 14#include <linux/interrupt.h> 15#include <linux/ioport.h> 16#include <linux/cpufreq.h> 17#include <linux/device.h> 18#include <linux/delay.h> 19#include <linux/clk.h> 20#include <linux/err.h> 21#include <linux/io.h> 22 23#include <asm/mach/arch.h> 24#include <asm/mach/map.h> 25 26#include <mach/regs-clock.h> 27#include <mach/s3c2412.h> 28 29#include <plat/cpu.h> 30#include <plat/cpu-freq-core.h> 31 32/* our clock resources. */ 33static struct clk *xtal; 34static struct clk *fclk; 35static struct clk *hclk; 36static struct clk *armclk; 37 38/* HDIV: 1, 2, 3, 4, 6, 8 */ 39 40static int s3c2412_cpufreq_calcdivs(struct s3c_cpufreq_config *cfg) 41{ 42 unsigned int hdiv, pdiv, armdiv, dvs; 43 unsigned long hclk, fclk, armclk, armdiv_clk; 44 unsigned long hclk_max; 45 46 fclk = cfg->freq.fclk; 47 armclk = cfg->freq.armclk; 48 hclk_max = cfg->max.hclk; 49 50 /* We can't run hclk above armclk as at the best we have to 51 * have armclk and hclk in dvs mode. */ 52 53 if (hclk_max > armclk) 54 hclk_max = armclk; 55 56 s3c_freq_dbg("%s: fclk=%lu, armclk=%lu, hclk_max=%lu\n", 57 __func__, fclk, armclk, hclk_max); 58 s3c_freq_dbg("%s: want f=%lu, arm=%lu, h=%lu, p=%lu\n", 59 __func__, cfg->freq.fclk, cfg->freq.armclk, 60 cfg->freq.hclk, cfg->freq.pclk); 61 62 armdiv = fclk / armclk; 63 64 if (armdiv < 1) 65 armdiv = 1; 66 if (armdiv > 2) 67 armdiv = 2; 68 69 cfg->divs.arm_divisor = armdiv; 70 armdiv_clk = fclk / armdiv; 71 72 hdiv = armdiv_clk / hclk_max; 73 if (hdiv < 1) 74 hdiv = 1; 75 76 cfg->freq.hclk = hclk = armdiv_clk / hdiv; 77 78 /* set dvs depending on whether we reached armclk or not. */ 79 cfg->divs.dvs = dvs = armclk < armdiv_clk; 80 81 /* update the actual armclk we achieved. */ 82 cfg->freq.armclk = dvs ? hclk : armdiv_clk; 83 84 s3c_freq_dbg("%s: armclk %lu, hclk %lu, armdiv %d, hdiv %d, dvs %d\n", 85 __func__, armclk, hclk, armdiv, hdiv, cfg->divs.dvs); 86 87 if (hdiv > 4) 88 goto invalid; 89 90 pdiv = (hclk > cfg->max.pclk) ? 2 : 1; 91 92 if ((hclk / pdiv) > cfg->max.pclk) 93 pdiv++; 94 95 cfg->freq.pclk = hclk / pdiv; 96 97 s3c_freq_dbg("%s: pdiv %d\n", __func__, pdiv); 98 99 if (pdiv > 2) 100 goto invalid; 101 102 pdiv *= hdiv; 103 104 /* store the result, and then return */ 105 106 cfg->divs.h_divisor = hdiv * armdiv; 107 cfg->divs.p_divisor = pdiv * armdiv; 108 109 return 0; 110 111invalid: 112 return -EINVAL; 113} 114 115static void s3c2412_cpufreq_setdivs(struct s3c_cpufreq_config *cfg) 116{ 117 unsigned long clkdiv; 118 unsigned long olddiv; 119 120 olddiv = clkdiv = __raw_readl(S3C2410_CLKDIVN); 121 122 /* clear off current clock info */ 123 124 clkdiv &= ~S3C2412_CLKDIVN_ARMDIVN; 125 clkdiv &= ~S3C2412_CLKDIVN_HDIVN_MASK; 126 clkdiv &= ~S3C2412_CLKDIVN_PDIVN; 127 128 if (cfg->divs.arm_divisor == 2) 129 clkdiv |= S3C2412_CLKDIVN_ARMDIVN; 130 131 clkdiv |= ((cfg->divs.h_divisor / cfg->divs.arm_divisor) - 1); 132 133 if (cfg->divs.p_divisor != cfg->divs.h_divisor) 134 clkdiv |= S3C2412_CLKDIVN_PDIVN; 135 136 s3c_freq_dbg("%s: div %08lx => %08lx\n", __func__, olddiv, clkdiv); 137 __raw_writel(clkdiv, S3C2410_CLKDIVN); 138 139 clk_set_parent(armclk, cfg->divs.dvs ? hclk : fclk); 140} 141 142static void s3c2412_cpufreq_setrefresh(struct s3c_cpufreq_config *cfg) 143{ 144 struct s3c_cpufreq_board *board = cfg->board; 145 unsigned long refresh; 146 147 s3c_freq_dbg("%s: refresh %u ns, hclk %lu\n", __func__, 148 board->refresh, cfg->freq.hclk); 149 150 /* Reduce both the refresh time (in ns) and the frequency (in MHz) 151 * by 10 each to ensure that we do not overflow 32 bit numbers. This 152 * should work for HCLK up to 133MHz and refresh period up to 30usec. 153 */ 154 155 refresh = (board->refresh / 10); 156 refresh *= (cfg->freq.hclk / 100); 157 refresh /= (1 * 1000 * 1000); /* 10^6 */ 158 159 s3c_freq_dbg("%s: setting refresh 0x%08lx\n", __func__, refresh); 160 __raw_writel(refresh, S3C2412_REFRESH); 161} 162 163/* set the default cpu frequency information, based on an 200MHz part 164 * as we have no other way of detecting the speed rating in software. 165 */ 166 167static struct s3c_cpufreq_info s3c2412_cpufreq_info = { 168 .max = { 169 .fclk = 200000000, 170 .hclk = 100000000, 171 .pclk = 50000000, 172 }, 173 174 .latency = 5000000, /* 5ms */ 175 176 .locktime_m = 150, 177 .locktime_u = 150, 178 .locktime_bits = 16, 179 180 .name = "s3c2412", 181 .set_refresh = s3c2412_cpufreq_setrefresh, 182 .set_divs = s3c2412_cpufreq_setdivs, 183 .calc_divs = s3c2412_cpufreq_calcdivs, 184 185 .calc_iotiming = s3c2412_iotiming_calc, 186 .set_iotiming = s3c2412_iotiming_set, 187 .get_iotiming = s3c2412_iotiming_get, 188 189 .debug_io_show = s3c_cpufreq_debugfs_call(s3c2412_iotiming_debugfs), 190}; 191 192static int s3c2412_cpufreq_add(struct device *dev, 193 struct subsys_interface *sif) 194{ 195 unsigned long fclk_rate; 196 197 hclk = clk_get(NULL, "hclk"); 198 if (IS_ERR(hclk)) { 199 pr_err("cannot find hclk clock\n"); 200 return -ENOENT; 201 } 202 203 fclk = clk_get(NULL, "fclk"); 204 if (IS_ERR(fclk)) { 205 pr_err("cannot find fclk clock\n"); 206 goto err_fclk; 207 } 208 209 fclk_rate = clk_get_rate(fclk); 210 if (fclk_rate > 200000000) { 211 pr_info("fclk %ld MHz, assuming 266MHz capable part\n", 212 fclk_rate / 1000000); 213 s3c2412_cpufreq_info.max.fclk = 266000000; 214 s3c2412_cpufreq_info.max.hclk = 133000000; 215 s3c2412_cpufreq_info.max.pclk = 66000000; 216 } 217 218 armclk = clk_get(NULL, "armclk"); 219 if (IS_ERR(armclk)) { 220 pr_err("cannot find arm clock\n"); 221 goto err_armclk; 222 } 223 224 xtal = clk_get(NULL, "xtal"); 225 if (IS_ERR(xtal)) { 226 pr_err("cannot find xtal clock\n"); 227 goto err_xtal; 228 } 229 230 return s3c_cpufreq_register(&s3c2412_cpufreq_info); 231 232err_xtal: 233 clk_put(armclk); 234err_armclk: 235 clk_put(fclk); 236err_fclk: 237 clk_put(hclk); 238 239 return -ENOENT; 240} 241 242static struct subsys_interface s3c2412_cpufreq_interface = { 243 .name = "s3c2412_cpufreq", 244 .subsys = &s3c2412_subsys, 245 .add_dev = s3c2412_cpufreq_add, 246}; 247 248static int s3c2412_cpufreq_init(void) 249{ 250 return subsys_interface_register(&s3c2412_cpufreq_interface); 251} 252arch_initcall(s3c2412_cpufreq_init);