|
@@ -536,3 +536,181 @@ int __init s3c_cpufreq_setboard(struct s3c_cpufreq_board *board)
|
|
*ours = *board;
|
|
*ours = *board;
|
|
cpu_cur.board = ours;
|
|
cpu_cur.board = ours;
|
|
|
|
|
|
|
|
+ return 0;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+int __init s3c_cpufreq_auto_io(void)
|
|
|
|
+{
|
|
|
|
+ int ret;
|
|
|
|
+
|
|
|
|
+ if (!cpu_cur.info->get_iotiming) {
|
|
|
|
+ printk(KERN_ERR "%s: get_iotiming undefined\n", __func__);
|
|
|
|
+ return -ENOENT;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ printk(KERN_INFO "%s: working out IO settings\n", __func__);
|
|
|
|
+
|
|
|
|
+ ret = (cpu_cur.info->get_iotiming)(&cpu_cur, &s3c24xx_iotiming);
|
|
|
|
+ if (ret)
|
|
|
|
+ printk(KERN_ERR "%s: failed to get timings\n", __func__);
|
|
|
|
+
|
|
|
|
+ return ret;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+/* if one or is zero, then return the other, otherwise return the min */
|
|
|
|
+#define do_min(_a, _b) ((_a) == 0 ? (_b) : (_b) == 0 ? (_a) : min(_a, _b))
|
|
|
|
+
|
|
|
|
+/**
|
|
|
|
+ * s3c_cpufreq_freq_min - find the minimum settings for the given freq.
|
|
|
|
+ * @dst: The destination structure
|
|
|
|
+ * @a: One argument.
|
|
|
|
+ * @b: The other argument.
|
|
|
|
+ *
|
|
|
|
+ * Create a minimum of each frequency entry in the 'struct s3c_freq',
|
|
|
|
+ * unless the entry is zero when it is ignored and the non-zero argument
|
|
|
|
+ * used.
|
|
|
|
+ */
|
|
|
|
+static void s3c_cpufreq_freq_min(struct s3c_freq *dst,
|
|
|
|
+ struct s3c_freq *a, struct s3c_freq *b)
|
|
|
|
+{
|
|
|
|
+ dst->fclk = do_min(a->fclk, b->fclk);
|
|
|
|
+ dst->hclk = do_min(a->hclk, b->hclk);
|
|
|
|
+ dst->pclk = do_min(a->pclk, b->pclk);
|
|
|
|
+ dst->armclk = do_min(a->armclk, b->armclk);
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static inline u32 calc_locktime(u32 freq, u32 time_us)
|
|
|
|
+{
|
|
|
|
+ u32 result;
|
|
|
|
+
|
|
|
|
+ result = freq * time_us;
|
|
|
|
+ result = DIV_ROUND_UP(result, 1000 * 1000);
|
|
|
|
+
|
|
|
|
+ return result;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static void s3c_cpufreq_update_loctkime(void)
|
|
|
|
+{
|
|
|
|
+ unsigned int bits = cpu_cur.info->locktime_bits;
|
|
|
|
+ u32 rate = (u32)clk_get_rate(_clk_xtal);
|
|
|
|
+ u32 val;
|
|
|
|
+
|
|
|
|
+ if (bits == 0) {
|
|
|
|
+ WARN_ON(1);
|
|
|
|
+ return;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ val = calc_locktime(rate, cpu_cur.info->locktime_u) << bits;
|
|
|
|
+ val |= calc_locktime(rate, cpu_cur.info->locktime_m);
|
|
|
|
+
|
|
|
|
+ printk(KERN_INFO "%s: new locktime is 0x%08x\n", __func__, val);
|
|
|
|
+ __raw_writel(val, S3C2410_LOCKTIME);
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static int s3c_cpufreq_build_freq(void)
|
|
|
|
+{
|
|
|
|
+ int size, ret;
|
|
|
|
+
|
|
|
|
+ if (!cpu_cur.info->calc_freqtable)
|
|
|
|
+ return -EINVAL;
|
|
|
|
+
|
|
|
|
+ kfree(ftab);
|
|
|
|
+ ftab = NULL;
|
|
|
|
+
|
|
|
|
+ size = cpu_cur.info->calc_freqtable(&cpu_cur, NULL, 0);
|
|
|
|
+ size++;
|
|
|
|
+
|
|
|
|
+ ftab = kmalloc(sizeof(struct cpufreq_frequency_table) * size, GFP_KERNEL);
|
|
|
|
+ if (!ftab) {
|
|
|
|
+ printk(KERN_ERR "%s: no memory for tables\n", __func__);
|
|
|
|
+ return -ENOMEM;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ ftab_size = size;
|
|
|
|
+
|
|
|
|
+ ret = cpu_cur.info->calc_freqtable(&cpu_cur, ftab, size);
|
|
|
|
+ s3c_cpufreq_addfreq(ftab, ret, size, CPUFREQ_TABLE_END);
|
|
|
|
+
|
|
|
|
+ return 0;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static int __init s3c_cpufreq_initcall(void)
|
|
|
|
+{
|
|
|
|
+ int ret = 0;
|
|
|
|
+
|
|
|
|
+ if (cpu_cur.info && cpu_cur.board) {
|
|
|
|
+ ret = s3c_cpufreq_initclks();
|
|
|
|
+ if (ret)
|
|
|
|
+ goto out;
|
|
|
|
+
|
|
|
|
+ /* get current settings */
|
|
|
|
+ s3c_cpufreq_getcur(&cpu_cur);
|
|
|
|
+ s3c_cpufreq_show("cur", &cpu_cur);
|
|
|
|
+
|
|
|
|
+ if (cpu_cur.board->auto_io) {
|
|
|
|
+ ret = s3c_cpufreq_auto_io();
|
|
|
|
+ if (ret) {
|
|
|
|
+ printk(KERN_ERR "%s: failed to get io timing\n",
|
|
|
|
+ __func__);
|
|
|
|
+ goto out;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (cpu_cur.board->need_io && !cpu_cur.info->set_iotiming) {
|
|
|
|
+ printk(KERN_ERR "%s: no IO support registered\n",
|
|
|
|
+ __func__);
|
|
|
|
+ ret = -EINVAL;
|
|
|
|
+ goto out;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (!cpu_cur.info->need_pll)
|
|
|
|
+ cpu_cur.lock_pll = 1;
|
|
|
|
+
|
|
|
|
+ s3c_cpufreq_update_loctkime();
|
|
|
|
+
|
|
|
|
+ s3c_cpufreq_freq_min(&cpu_cur.max, &cpu_cur.board->max,
|
|
|
|
+ &cpu_cur.info->max);
|
|
|
|
+
|
|
|
|
+ if (cpu_cur.info->calc_freqtable)
|
|
|
|
+ s3c_cpufreq_build_freq();
|
|
|
|
+
|
|
|
|
+ ret = cpufreq_register_driver(&s3c24xx_driver);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ out:
|
|
|
|
+ return ret;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+late_initcall(s3c_cpufreq_initcall);
|
|
|
|
+
|
|
|
|
+/**
|
|
|
|
+ * s3c_plltab_register - register CPU PLL table.
|
|
|
|
+ * @plls: The list of PLL entries.
|
|
|
|
+ * @plls_no: The size of the PLL entries @plls.
|
|
|
|
+ *
|
|
|
|
+ * Register the given set of PLLs with the system.
|
|
|
|
+ */
|
|
|
|
+int __init s3c_plltab_register(struct cpufreq_frequency_table *plls,
|
|
|
|
+ unsigned int plls_no)
|
|
|
|
+{
|
|
|
|
+ struct cpufreq_frequency_table *vals;
|
|
|
|
+ unsigned int size;
|
|
|
|
+
|
|
|
|
+ size = sizeof(struct cpufreq_frequency_table) * (plls_no + 1);
|
|
|
|
+
|
|
|
|
+ vals = kmalloc(size, GFP_KERNEL);
|
|
|
|
+ if (vals) {
|
|
|
|
+ memcpy(vals, plls, size);
|
|
|
|
+ pll_reg = vals;
|
|
|
|
+
|
|
|
|
+ /* write a terminating entry, we don't store it in the
|
|
|
|
+ * table that is stored in the kernel */
|
|
|
|
+ vals += plls_no;
|
|
|
|
+ vals->frequency = CPUFREQ_TABLE_END;
|
|
|
|
+
|
|
|
|
+ printk(KERN_INFO "cpufreq: %d PLL entries\n", plls_no);
|
|
|
|
+ } else
|
|
|
|
+ printk(KERN_ERR "cpufreq: no memory for PLL tables\n");
|
|
|
|
+
|
|
|
|
+ return vals ? 0 : -ENOMEM;
|
|
|
|
+}
|