root/drivers/cpufreq/s3c2412-cpufreq.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. s3c2412_cpufreq_calcdivs
  2. s3c2412_cpufreq_setdivs
  3. s3c2412_cpufreq_setrefresh
  4. s3c2412_cpufreq_add
  5. s3c2412_cpufreq_init

   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. */
  33 static struct clk *xtal;
  34 static struct clk *fclk;
  35 static struct clk *hclk;
  36 static struct clk *armclk;
  37 
  38 /* HDIV: 1, 2, 3, 4, 6, 8 */
  39 
  40 static 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 
 111 invalid:
 112         return -EINVAL;
 113 }
 114 
 115 static 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 
 142 static 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 
 167 static 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 
 192 static 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 
 232 err_xtal:
 233         clk_put(armclk);
 234 err_armclk:
 235         clk_put(fclk);
 236 err_fclk:
 237         clk_put(hclk);
 238 
 239         return -ENOENT;
 240 }
 241 
 242 static struct subsys_interface s3c2412_cpufreq_interface = {
 243         .name           = "s3c2412_cpufreq",
 244         .subsys         = &s3c2412_subsys,
 245         .add_dev        = s3c2412_cpufreq_add,
 246 };
 247 
 248 static int s3c2412_cpufreq_init(void)
 249 {
 250         return subsys_interface_register(&s3c2412_cpufreq_interface);
 251 }
 252 arch_initcall(s3c2412_cpufreq_init);

/* [<][>][^][v][top][bottom][index][help] */