s3c2412-cpufreq.c (5658B)
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#include <linux/soc/samsung/s3c-cpufreq-core.h> 23#include <linux/soc/samsung/s3c-pm.h> 24 25#include <asm/mach/arch.h> 26#include <asm/mach/map.h> 27 28#define S3C2412_CLKDIVN_PDIVN (1<<2) 29#define S3C2412_CLKDIVN_HDIVN_MASK (3<<0) 30#define S3C2412_CLKDIVN_ARMDIVN (1<<3) 31#define S3C2412_CLKDIVN_DVSEN (1<<4) 32#define S3C2412_CLKDIVN_HALFHCLK (1<<5) 33#define S3C2412_CLKDIVN_USB48DIV (1<<6) 34#define S3C2412_CLKDIVN_UARTDIV_MASK (15<<8) 35#define S3C2412_CLKDIVN_UARTDIV_SHIFT (8) 36#define S3C2412_CLKDIVN_I2SDIV_MASK (15<<12) 37#define S3C2412_CLKDIVN_I2SDIV_SHIFT (12) 38#define S3C2412_CLKDIVN_CAMDIV_MASK (15<<16) 39#define S3C2412_CLKDIVN_CAMDIV_SHIFT (16) 40 41/* our clock resources. */ 42static struct clk *xtal; 43static struct clk *fclk; 44static struct clk *hclk; 45static struct clk *armclk; 46 47/* HDIV: 1, 2, 3, 4, 6, 8 */ 48 49static int s3c2412_cpufreq_calcdivs(struct s3c_cpufreq_config *cfg) 50{ 51 unsigned int hdiv, pdiv, armdiv, dvs; 52 unsigned long hclk, fclk, armclk, armdiv_clk; 53 unsigned long hclk_max; 54 55 fclk = cfg->freq.fclk; 56 armclk = cfg->freq.armclk; 57 hclk_max = cfg->max.hclk; 58 59 /* We can't run hclk above armclk as at the best we have to 60 * have armclk and hclk in dvs mode. */ 61 62 if (hclk_max > armclk) 63 hclk_max = armclk; 64 65 s3c_freq_dbg("%s: fclk=%lu, armclk=%lu, hclk_max=%lu\n", 66 __func__, fclk, armclk, hclk_max); 67 s3c_freq_dbg("%s: want f=%lu, arm=%lu, h=%lu, p=%lu\n", 68 __func__, cfg->freq.fclk, cfg->freq.armclk, 69 cfg->freq.hclk, cfg->freq.pclk); 70 71 armdiv = fclk / armclk; 72 73 if (armdiv < 1) 74 armdiv = 1; 75 if (armdiv > 2) 76 armdiv = 2; 77 78 cfg->divs.arm_divisor = armdiv; 79 armdiv_clk = fclk / armdiv; 80 81 hdiv = armdiv_clk / hclk_max; 82 if (hdiv < 1) 83 hdiv = 1; 84 85 cfg->freq.hclk = hclk = armdiv_clk / hdiv; 86 87 /* set dvs depending on whether we reached armclk or not. */ 88 cfg->divs.dvs = dvs = armclk < armdiv_clk; 89 90 /* update the actual armclk we achieved. */ 91 cfg->freq.armclk = dvs ? hclk : armdiv_clk; 92 93 s3c_freq_dbg("%s: armclk %lu, hclk %lu, armdiv %d, hdiv %d, dvs %d\n", 94 __func__, armclk, hclk, armdiv, hdiv, cfg->divs.dvs); 95 96 if (hdiv > 4) 97 goto invalid; 98 99 pdiv = (hclk > cfg->max.pclk) ? 2 : 1; 100 101 if ((hclk / pdiv) > cfg->max.pclk) 102 pdiv++; 103 104 cfg->freq.pclk = hclk / pdiv; 105 106 s3c_freq_dbg("%s: pdiv %d\n", __func__, pdiv); 107 108 if (pdiv > 2) 109 goto invalid; 110 111 pdiv *= hdiv; 112 113 /* store the result, and then return */ 114 115 cfg->divs.h_divisor = hdiv * armdiv; 116 cfg->divs.p_divisor = pdiv * armdiv; 117 118 return 0; 119 120invalid: 121 return -EINVAL; 122} 123 124static void s3c2412_cpufreq_setdivs(struct s3c_cpufreq_config *cfg) 125{ 126 unsigned long clkdiv; 127 unsigned long olddiv; 128 129 olddiv = clkdiv = s3c24xx_read_clkdivn(); 130 131 /* clear off current clock info */ 132 133 clkdiv &= ~S3C2412_CLKDIVN_ARMDIVN; 134 clkdiv &= ~S3C2412_CLKDIVN_HDIVN_MASK; 135 clkdiv &= ~S3C2412_CLKDIVN_PDIVN; 136 137 if (cfg->divs.arm_divisor == 2) 138 clkdiv |= S3C2412_CLKDIVN_ARMDIVN; 139 140 clkdiv |= ((cfg->divs.h_divisor / cfg->divs.arm_divisor) - 1); 141 142 if (cfg->divs.p_divisor != cfg->divs.h_divisor) 143 clkdiv |= S3C2412_CLKDIVN_PDIVN; 144 145 s3c_freq_dbg("%s: div %08lx => %08lx\n", __func__, olddiv, clkdiv); 146 s3c24xx_write_clkdivn(clkdiv); 147 148 clk_set_parent(armclk, cfg->divs.dvs ? hclk : fclk); 149} 150 151/* set the default cpu frequency information, based on an 200MHz part 152 * as we have no other way of detecting the speed rating in software. 153 */ 154 155static struct s3c_cpufreq_info s3c2412_cpufreq_info = { 156 .max = { 157 .fclk = 200000000, 158 .hclk = 100000000, 159 .pclk = 50000000, 160 }, 161 162 .latency = 5000000, /* 5ms */ 163 164 .locktime_m = 150, 165 .locktime_u = 150, 166 .locktime_bits = 16, 167 168 .name = "s3c2412", 169 .set_refresh = s3c2412_cpufreq_setrefresh, 170 .set_divs = s3c2412_cpufreq_setdivs, 171 .calc_divs = s3c2412_cpufreq_calcdivs, 172 173 .calc_iotiming = s3c2412_iotiming_calc, 174 .set_iotiming = s3c2412_iotiming_set, 175 .get_iotiming = s3c2412_iotiming_get, 176 177 .debug_io_show = s3c_cpufreq_debugfs_call(s3c2412_iotiming_debugfs), 178}; 179 180static int s3c2412_cpufreq_add(struct device *dev, 181 struct subsys_interface *sif) 182{ 183 unsigned long fclk_rate; 184 185 hclk = clk_get(NULL, "hclk"); 186 if (IS_ERR(hclk)) { 187 pr_err("cannot find hclk clock\n"); 188 return -ENOENT; 189 } 190 191 fclk = clk_get(NULL, "fclk"); 192 if (IS_ERR(fclk)) { 193 pr_err("cannot find fclk clock\n"); 194 goto err_fclk; 195 } 196 197 fclk_rate = clk_get_rate(fclk); 198 if (fclk_rate > 200000000) { 199 pr_info("fclk %ld MHz, assuming 266MHz capable part\n", 200 fclk_rate / 1000000); 201 s3c2412_cpufreq_info.max.fclk = 266000000; 202 s3c2412_cpufreq_info.max.hclk = 133000000; 203 s3c2412_cpufreq_info.max.pclk = 66000000; 204 } 205 206 armclk = clk_get(NULL, "armclk"); 207 if (IS_ERR(armclk)) { 208 pr_err("cannot find arm clock\n"); 209 goto err_armclk; 210 } 211 212 xtal = clk_get(NULL, "xtal"); 213 if (IS_ERR(xtal)) { 214 pr_err("cannot find xtal clock\n"); 215 goto err_xtal; 216 } 217 218 return s3c_cpufreq_register(&s3c2412_cpufreq_info); 219 220err_xtal: 221 clk_put(armclk); 222err_armclk: 223 clk_put(fclk); 224err_fclk: 225 clk_put(hclk); 226 227 return -ENOENT; 228} 229 230static struct subsys_interface s3c2412_cpufreq_interface = { 231 .name = "s3c2412_cpufreq", 232 .subsys = &s3c2412_subsys, 233 .add_dev = s3c2412_cpufreq_add, 234}; 235 236static int s3c2412_cpufreq_init(void) 237{ 238 return subsys_interface_register(&s3c2412_cpufreq_interface); 239} 240arch_initcall(s3c2412_cpufreq_init);