Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-only
0002 /*
0003  * Copyright 2008 Simtec Electronics
0004  *  http://armlinux.simtec.co.uk/
0005  *  Ben Dooks <ben@simtec.co.uk>
0006  *
0007  * S3C2412 CPU Frequency scalling
0008 */
0009 
0010 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
0011 
0012 #include <linux/init.h>
0013 #include <linux/module.h>
0014 #include <linux/interrupt.h>
0015 #include <linux/ioport.h>
0016 #include <linux/cpufreq.h>
0017 #include <linux/device.h>
0018 #include <linux/delay.h>
0019 #include <linux/clk.h>
0020 #include <linux/err.h>
0021 #include <linux/io.h>
0022 #include <linux/soc/samsung/s3c-cpufreq-core.h>
0023 #include <linux/soc/samsung/s3c-pm.h>
0024 
0025 #include <asm/mach/arch.h>
0026 #include <asm/mach/map.h>
0027 
0028 #define S3C2412_CLKDIVN_PDIVN       (1<<2)
0029 #define S3C2412_CLKDIVN_HDIVN_MASK  (3<<0)
0030 #define S3C2412_CLKDIVN_ARMDIVN     (1<<3)
0031 #define S3C2412_CLKDIVN_DVSEN       (1<<4)
0032 #define S3C2412_CLKDIVN_HALFHCLK    (1<<5)
0033 #define S3C2412_CLKDIVN_USB48DIV    (1<<6)
0034 #define S3C2412_CLKDIVN_UARTDIV_MASK    (15<<8)
0035 #define S3C2412_CLKDIVN_UARTDIV_SHIFT   (8)
0036 #define S3C2412_CLKDIVN_I2SDIV_MASK (15<<12)
0037 #define S3C2412_CLKDIVN_I2SDIV_SHIFT    (12)
0038 #define S3C2412_CLKDIVN_CAMDIV_MASK (15<<16)
0039 #define S3C2412_CLKDIVN_CAMDIV_SHIFT    (16)
0040 
0041 /* our clock resources. */
0042 static struct clk *xtal;
0043 static struct clk *fclk;
0044 static struct clk *hclk;
0045 static struct clk *armclk;
0046 
0047 /* HDIV: 1, 2, 3, 4, 6, 8 */
0048 
0049 static int s3c2412_cpufreq_calcdivs(struct s3c_cpufreq_config *cfg)
0050 {
0051     unsigned int hdiv, pdiv, armdiv, dvs;
0052     unsigned long hclk, fclk, armclk, armdiv_clk;
0053     unsigned long hclk_max;
0054 
0055     fclk = cfg->freq.fclk;
0056     armclk = cfg->freq.armclk;
0057     hclk_max = cfg->max.hclk;
0058 
0059     /* We can't run hclk above armclk as at the best we have to
0060      * have armclk and hclk in dvs mode. */
0061 
0062     if (hclk_max > armclk)
0063         hclk_max = armclk;
0064 
0065     s3c_freq_dbg("%s: fclk=%lu, armclk=%lu, hclk_max=%lu\n",
0066              __func__, fclk, armclk, hclk_max);
0067     s3c_freq_dbg("%s: want f=%lu, arm=%lu, h=%lu, p=%lu\n",
0068              __func__, cfg->freq.fclk, cfg->freq.armclk,
0069              cfg->freq.hclk, cfg->freq.pclk);
0070 
0071     armdiv = fclk / armclk;
0072 
0073     if (armdiv < 1)
0074         armdiv = 1;
0075     if (armdiv > 2)
0076         armdiv = 2;
0077 
0078     cfg->divs.arm_divisor = armdiv;
0079     armdiv_clk = fclk / armdiv;
0080 
0081     hdiv = armdiv_clk / hclk_max;
0082     if (hdiv < 1)
0083         hdiv = 1;
0084 
0085     cfg->freq.hclk = hclk = armdiv_clk / hdiv;
0086 
0087     /* set dvs depending on whether we reached armclk or not. */
0088     cfg->divs.dvs = dvs = armclk < armdiv_clk;
0089 
0090     /* update the actual armclk we achieved. */
0091     cfg->freq.armclk = dvs ? hclk : armdiv_clk;
0092 
0093     s3c_freq_dbg("%s: armclk %lu, hclk %lu, armdiv %d, hdiv %d, dvs %d\n",
0094              __func__, armclk, hclk, armdiv, hdiv, cfg->divs.dvs);
0095 
0096     if (hdiv > 4)
0097         goto invalid;
0098 
0099     pdiv = (hclk > cfg->max.pclk) ? 2 : 1;
0100 
0101     if ((hclk / pdiv) > cfg->max.pclk)
0102         pdiv++;
0103 
0104     cfg->freq.pclk = hclk / pdiv;
0105 
0106     s3c_freq_dbg("%s: pdiv %d\n", __func__, pdiv);
0107 
0108     if (pdiv > 2)
0109         goto invalid;
0110 
0111     pdiv *= hdiv;
0112 
0113     /* store the result, and then return */
0114 
0115     cfg->divs.h_divisor = hdiv * armdiv;
0116     cfg->divs.p_divisor = pdiv * armdiv;
0117 
0118     return 0;
0119 
0120 invalid:
0121     return -EINVAL;
0122 }
0123 
0124 static void s3c2412_cpufreq_setdivs(struct s3c_cpufreq_config *cfg)
0125 {
0126     unsigned long clkdiv;
0127     unsigned long olddiv;
0128 
0129     olddiv = clkdiv = s3c24xx_read_clkdivn();
0130 
0131     /* clear off current clock info */
0132 
0133     clkdiv &= ~S3C2412_CLKDIVN_ARMDIVN;
0134     clkdiv &= ~S3C2412_CLKDIVN_HDIVN_MASK;
0135     clkdiv &= ~S3C2412_CLKDIVN_PDIVN;
0136 
0137     if (cfg->divs.arm_divisor == 2)
0138         clkdiv |= S3C2412_CLKDIVN_ARMDIVN;
0139 
0140     clkdiv |= ((cfg->divs.h_divisor / cfg->divs.arm_divisor) - 1);
0141 
0142     if (cfg->divs.p_divisor != cfg->divs.h_divisor)
0143         clkdiv |= S3C2412_CLKDIVN_PDIVN;
0144 
0145     s3c_freq_dbg("%s: div %08lx => %08lx\n", __func__, olddiv, clkdiv);
0146     s3c24xx_write_clkdivn(clkdiv);
0147 
0148     clk_set_parent(armclk, cfg->divs.dvs ? hclk : fclk);
0149 }
0150 
0151 /* set the default cpu frequency information, based on an 200MHz part
0152  * as we have no other way of detecting the speed rating in software.
0153  */
0154 
0155 static struct s3c_cpufreq_info s3c2412_cpufreq_info = {
0156     .max        = {
0157         .fclk   = 200000000,
0158         .hclk   = 100000000,
0159         .pclk   =  50000000,
0160     },
0161 
0162     .latency    = 5000000, /* 5ms */
0163 
0164     .locktime_m = 150,
0165     .locktime_u = 150,
0166     .locktime_bits  = 16,
0167 
0168     .name       = "s3c2412",
0169     .set_refresh    = s3c2412_cpufreq_setrefresh,
0170     .set_divs   = s3c2412_cpufreq_setdivs,
0171     .calc_divs  = s3c2412_cpufreq_calcdivs,
0172 
0173     .calc_iotiming  = s3c2412_iotiming_calc,
0174     .set_iotiming   = s3c2412_iotiming_set,
0175     .get_iotiming   = s3c2412_iotiming_get,
0176 
0177     .debug_io_show  = s3c_cpufreq_debugfs_call(s3c2412_iotiming_debugfs),
0178 };
0179 
0180 static int s3c2412_cpufreq_add(struct device *dev,
0181                    struct subsys_interface *sif)
0182 {
0183     unsigned long fclk_rate;
0184 
0185     hclk = clk_get(NULL, "hclk");
0186     if (IS_ERR(hclk)) {
0187         pr_err("cannot find hclk clock\n");
0188         return -ENOENT;
0189     }
0190 
0191     fclk = clk_get(NULL, "fclk");
0192     if (IS_ERR(fclk)) {
0193         pr_err("cannot find fclk clock\n");
0194         goto err_fclk;
0195     }
0196 
0197     fclk_rate = clk_get_rate(fclk);
0198     if (fclk_rate > 200000000) {
0199         pr_info("fclk %ld MHz, assuming 266MHz capable part\n",
0200             fclk_rate / 1000000);
0201         s3c2412_cpufreq_info.max.fclk = 266000000;
0202         s3c2412_cpufreq_info.max.hclk = 133000000;
0203         s3c2412_cpufreq_info.max.pclk =  66000000;
0204     }
0205 
0206     armclk = clk_get(NULL, "armclk");
0207     if (IS_ERR(armclk)) {
0208         pr_err("cannot find arm clock\n");
0209         goto err_armclk;
0210     }
0211 
0212     xtal = clk_get(NULL, "xtal");
0213     if (IS_ERR(xtal)) {
0214         pr_err("cannot find xtal clock\n");
0215         goto err_xtal;
0216     }
0217 
0218     return s3c_cpufreq_register(&s3c2412_cpufreq_info);
0219 
0220 err_xtal:
0221     clk_put(armclk);
0222 err_armclk:
0223     clk_put(fclk);
0224 err_fclk:
0225     clk_put(hclk);
0226 
0227     return -ENOENT;
0228 }
0229 
0230 static struct subsys_interface s3c2412_cpufreq_interface = {
0231     .name       = "s3c2412_cpufreq",
0232     .subsys     = &s3c2412_subsys,
0233     .add_dev    = s3c2412_cpufreq_add,
0234 };
0235 
0236 static int s3c2412_cpufreq_init(void)
0237 {
0238     return subsys_interface_register(&s3c2412_cpufreq_interface);
0239 }
0240 arch_initcall(s3c2412_cpufreq_init);