Author | Tokens | Token Proportion | Commits | Commit Proportion |
---|---|---|---|---|
Ben Dooks | 825 | 89.38% | 3 | 27.27% |
Arnd Bergmann | 55 | 5.96% | 3 | 27.27% |
Kay Sievers | 19 | 2.06% | 1 | 9.09% |
Joe Perches | 17 | 1.84% | 2 | 18.18% |
Heiko Stübner | 5 | 0.54% | 1 | 9.09% |
Thomas Gleixner | 2 | 0.22% | 1 | 9.09% |
Total | 923 | 11 |
// SPDX-License-Identifier: GPL-2.0-only /* * Copyright 2008 Simtec Electronics * http://armlinux.simtec.co.uk/ * Ben Dooks <ben@simtec.co.uk> * * S3C2412 CPU Frequency scalling */ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #include <linux/init.h> #include <linux/module.h> #include <linux/interrupt.h> #include <linux/ioport.h> #include <linux/cpufreq.h> #include <linux/device.h> #include <linux/delay.h> #include <linux/clk.h> #include <linux/err.h> #include <linux/io.h> #include <linux/soc/samsung/s3c-cpufreq-core.h> #include <linux/soc/samsung/s3c-pm.h> #include <asm/mach/arch.h> #include <asm/mach/map.h> #define S3C2412_CLKDIVN_PDIVN (1<<2) #define S3C2412_CLKDIVN_HDIVN_MASK (3<<0) #define S3C2412_CLKDIVN_ARMDIVN (1<<3) #define S3C2412_CLKDIVN_DVSEN (1<<4) #define S3C2412_CLKDIVN_HALFHCLK (1<<5) #define S3C2412_CLKDIVN_USB48DIV (1<<6) #define S3C2412_CLKDIVN_UARTDIV_MASK (15<<8) #define S3C2412_CLKDIVN_UARTDIV_SHIFT (8) #define S3C2412_CLKDIVN_I2SDIV_MASK (15<<12) #define S3C2412_CLKDIVN_I2SDIV_SHIFT (12) #define S3C2412_CLKDIVN_CAMDIV_MASK (15<<16) #define S3C2412_CLKDIVN_CAMDIV_SHIFT (16) /* our clock resources. */ static struct clk *xtal; static struct clk *fclk; static struct clk *hclk; static struct clk *armclk; /* HDIV: 1, 2, 3, 4, 6, 8 */ static int s3c2412_cpufreq_calcdivs(struct s3c_cpufreq_config *cfg) { unsigned int hdiv, pdiv, armdiv, dvs; unsigned long hclk, fclk, armclk, armdiv_clk; unsigned long hclk_max; fclk = cfg->freq.fclk; armclk = cfg->freq.armclk; hclk_max = cfg->max.hclk; /* We can't run hclk above armclk as at the best we have to * have armclk and hclk in dvs mode. */ if (hclk_max > armclk) hclk_max = armclk; s3c_freq_dbg("%s: fclk=%lu, armclk=%lu, hclk_max=%lu\n", __func__, fclk, armclk, hclk_max); s3c_freq_dbg("%s: want f=%lu, arm=%lu, h=%lu, p=%lu\n", __func__, cfg->freq.fclk, cfg->freq.armclk, cfg->freq.hclk, cfg->freq.pclk); armdiv = fclk / armclk; if (armdiv < 1) armdiv = 1; if (armdiv > 2) armdiv = 2; cfg->divs.arm_divisor = armdiv; armdiv_clk = fclk / armdiv; hdiv = armdiv_clk / hclk_max; if (hdiv < 1) hdiv = 1; cfg->freq.hclk = hclk = armdiv_clk / hdiv; /* set dvs depending on whether we reached armclk or not. */ cfg->divs.dvs = dvs = armclk < armdiv_clk; /* update the actual armclk we achieved. */ cfg->freq.armclk = dvs ? hclk : armdiv_clk; s3c_freq_dbg("%s: armclk %lu, hclk %lu, armdiv %d, hdiv %d, dvs %d\n", __func__, armclk, hclk, armdiv, hdiv, cfg->divs.dvs); if (hdiv > 4) goto invalid; pdiv = (hclk > cfg->max.pclk) ? 2 : 1; if ((hclk / pdiv) > cfg->max.pclk) pdiv++; cfg->freq.pclk = hclk / pdiv; s3c_freq_dbg("%s: pdiv %d\n", __func__, pdiv); if (pdiv > 2) goto invalid; pdiv *= hdiv; /* store the result, and then return */ cfg->divs.h_divisor = hdiv * armdiv; cfg->divs.p_divisor = pdiv * armdiv; return 0; invalid: return -EINVAL; } static void s3c2412_cpufreq_setdivs(struct s3c_cpufreq_config *cfg) { unsigned long clkdiv; unsigned long olddiv; olddiv = clkdiv = s3c24xx_read_clkdivn(); /* clear off current clock info */ clkdiv &= ~S3C2412_CLKDIVN_ARMDIVN; clkdiv &= ~S3C2412_CLKDIVN_HDIVN_MASK; clkdiv &= ~S3C2412_CLKDIVN_PDIVN; if (cfg->divs.arm_divisor == 2) clkdiv |= S3C2412_CLKDIVN_ARMDIVN; clkdiv |= ((cfg->divs.h_divisor / cfg->divs.arm_divisor) - 1); if (cfg->divs.p_divisor != cfg->divs.h_divisor) clkdiv |= S3C2412_CLKDIVN_PDIVN; s3c_freq_dbg("%s: div %08lx => %08lx\n", __func__, olddiv, clkdiv); s3c24xx_write_clkdivn(clkdiv); clk_set_parent(armclk, cfg->divs.dvs ? hclk : fclk); } /* set the default cpu frequency information, based on an 200MHz part * as we have no other way of detecting the speed rating in software. */ static struct s3c_cpufreq_info s3c2412_cpufreq_info = { .max = { .fclk = 200000000, .hclk = 100000000, .pclk = 50000000, }, .latency = 5000000, /* 5ms */ .locktime_m = 150, .locktime_u = 150, .locktime_bits = 16, .name = "s3c2412", .set_refresh = s3c2412_cpufreq_setrefresh, .set_divs = s3c2412_cpufreq_setdivs, .calc_divs = s3c2412_cpufreq_calcdivs, .calc_iotiming = s3c2412_iotiming_calc, .set_iotiming = s3c2412_iotiming_set, .get_iotiming = s3c2412_iotiming_get, .debug_io_show = s3c_cpufreq_debugfs_call(s3c2412_iotiming_debugfs), }; static int s3c2412_cpufreq_add(struct device *dev, struct subsys_interface *sif) { unsigned long fclk_rate; hclk = clk_get(NULL, "hclk"); if (IS_ERR(hclk)) { pr_err("cannot find hclk clock\n"); return -ENOENT; } fclk = clk_get(NULL, "fclk"); if (IS_ERR(fclk)) { pr_err("cannot find fclk clock\n"); goto err_fclk; } fclk_rate = clk_get_rate(fclk); if (fclk_rate > 200000000) { pr_info("fclk %ld MHz, assuming 266MHz capable part\n", fclk_rate / 1000000); s3c2412_cpufreq_info.max.fclk = 266000000; s3c2412_cpufreq_info.max.hclk = 133000000; s3c2412_cpufreq_info.max.pclk = 66000000; } armclk = clk_get(NULL, "armclk"); if (IS_ERR(armclk)) { pr_err("cannot find arm clock\n"); goto err_armclk; } xtal = clk_get(NULL, "xtal"); if (IS_ERR(xtal)) { pr_err("cannot find xtal clock\n"); goto err_xtal; } return s3c_cpufreq_register(&s3c2412_cpufreq_info); err_xtal: clk_put(armclk); err_armclk: clk_put(fclk); err_fclk: clk_put(hclk); return -ENOENT; } static struct subsys_interface s3c2412_cpufreq_interface = { .name = "s3c2412_cpufreq", .subsys = &s3c2412_subsys, .add_dev = s3c2412_cpufreq_add, }; static int s3c2412_cpufreq_init(void) { return subsys_interface_register(&s3c2412_cpufreq_interface); } arch_initcall(s3c2412_cpufreq_init);
Information contained on this website is for historical information purposes only and does not indicate or represent copyright ownership.
Created with Cregit http://github.com/cregit/cregit
Version 2.0-RC1