|
@@ -406,3 +406,133 @@ static __init int s3c_cpufreq_initclks(void)
|
|
|
clk_pclk = s3c_cpufreq_clk_get(NULL, "pclk");
|
|
|
clk_arm = s3c_cpufreq_clk_get(NULL, "armclk");
|
|
|
|
|
|
+ if (IS_ERR(clk_fclk) || IS_ERR(clk_hclk) || IS_ERR(clk_pclk) ||
|
|
|
+ IS_ERR(_clk_mpll) || IS_ERR(clk_arm) || IS_ERR(_clk_xtal)) {
|
|
|
+ printk(KERN_ERR "%s: could not get clock(s)\n", __func__);
|
|
|
+ return -ENOENT;
|
|
|
+ }
|
|
|
+
|
|
|
+ printk(KERN_INFO "%s: clocks f=%lu,h=%lu,p=%lu,a=%lu\n", __func__,
|
|
|
+ clk_get_rate(clk_fclk) / 1000,
|
|
|
+ clk_get_rate(clk_hclk) / 1000,
|
|
|
+ clk_get_rate(clk_pclk) / 1000,
|
|
|
+ clk_get_rate(clk_arm) / 1000);
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+static int s3c_cpufreq_verify(struct cpufreq_policy *policy)
|
|
|
+{
|
|
|
+ if (policy->cpu != 0)
|
|
|
+ return -EINVAL;
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+#ifdef CONFIG_PM
|
|
|
+static struct cpufreq_frequency_table suspend_pll;
|
|
|
+static unsigned int suspend_freq;
|
|
|
+
|
|
|
+static int s3c_cpufreq_suspend(struct cpufreq_policy *policy)
|
|
|
+{
|
|
|
+ suspend_pll.frequency = clk_get_rate(_clk_mpll);
|
|
|
+ suspend_pll.index = __raw_readl(S3C2410_MPLLCON);
|
|
|
+ suspend_freq = s3c_cpufreq_get(0) * 1000;
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+static int s3c_cpufreq_resume(struct cpufreq_policy *policy)
|
|
|
+{
|
|
|
+ int ret;
|
|
|
+
|
|
|
+ s3c_freq_dbg("%s: resuming with policy %p\n", __func__, policy);
|
|
|
+
|
|
|
+ last_target = ~0; /* invalidate last_target setting */
|
|
|
+
|
|
|
+ /* first, find out what speed we resumed at. */
|
|
|
+ s3c_cpufreq_resume_clocks();
|
|
|
+
|
|
|
+ /* whilst we will be called later on, we try and re-set the
|
|
|
+ * cpu frequencies as soon as possible so that we do not end
|
|
|
+ * up resuming devices and then immediately having to re-set
|
|
|
+ * a number of settings once these devices have restarted.
|
|
|
+ *
|
|
|
+ * as a note, it is expected devices are not used until they
|
|
|
+ * have been un-suspended and at that time they should have
|
|
|
+ * used the updated clock settings.
|
|
|
+ */
|
|
|
+
|
|
|
+ ret = s3c_cpufreq_settarget(NULL, suspend_freq, &suspend_pll);
|
|
|
+ if (ret) {
|
|
|
+ printk(KERN_ERR "%s: failed to reset pll/freq\n", __func__);
|
|
|
+ return ret;
|
|
|
+ }
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+#else
|
|
|
+#define s3c_cpufreq_resume NULL
|
|
|
+#define s3c_cpufreq_suspend NULL
|
|
|
+#endif
|
|
|
+
|
|
|
+static struct cpufreq_driver s3c24xx_driver = {
|
|
|
+ .flags = CPUFREQ_STICKY,
|
|
|
+ .verify = s3c_cpufreq_verify,
|
|
|
+ .target = s3c_cpufreq_target,
|
|
|
+ .get = s3c_cpufreq_get,
|
|
|
+ .init = s3c_cpufreq_init,
|
|
|
+ .suspend = s3c_cpufreq_suspend,
|
|
|
+ .resume = s3c_cpufreq_resume,
|
|
|
+ .name = "s3c24xx",
|
|
|
+};
|
|
|
+
|
|
|
+
|
|
|
+int __init s3c_cpufreq_register(struct s3c_cpufreq_info *info)
|
|
|
+{
|
|
|
+ if (!info || !info->name) {
|
|
|
+ printk(KERN_ERR "%s: failed to pass valid information\n",
|
|
|
+ __func__);
|
|
|
+ return -EINVAL;
|
|
|
+ }
|
|
|
+
|
|
|
+ printk(KERN_INFO "S3C24XX CPU Frequency driver, %s cpu support\n",
|
|
|
+ info->name);
|
|
|
+
|
|
|
+ /* check our driver info has valid data */
|
|
|
+
|
|
|
+ BUG_ON(info->set_refresh == NULL);
|
|
|
+ BUG_ON(info->set_divs == NULL);
|
|
|
+ BUG_ON(info->calc_divs == NULL);
|
|
|
+
|
|
|
+ /* info->set_fvco is optional, depending on whether there
|
|
|
+ * is a need to set the clock code. */
|
|
|
+
|
|
|
+ cpu_cur.info = info;
|
|
|
+
|
|
|
+ /* Note, driver registering should probably update locktime */
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+int __init s3c_cpufreq_setboard(struct s3c_cpufreq_board *board)
|
|
|
+{
|
|
|
+ struct s3c_cpufreq_board *ours;
|
|
|
+
|
|
|
+ if (!board) {
|
|
|
+ printk(KERN_INFO "%s: no board data\n", __func__);
|
|
|
+ return -EINVAL;
|
|
|
+ }
|
|
|
+
|
|
|
+ /* Copy the board information so that each board can make this
|
|
|
+ * initdata. */
|
|
|
+
|
|
|
+ ours = kzalloc(sizeof(struct s3c_cpufreq_board), GFP_KERNEL);
|
|
|
+ if (ours == NULL) {
|
|
|
+ printk(KERN_ERR "%s: no memory\n", __func__);
|
|
|
+ return -ENOMEM;
|
|
|
+ }
|
|
|
+
|
|
|
+ *ours = *board;
|
|
|
+ cpu_cur.board = ours;
|
|
|
+
|