return 0;
 }
 
+#ifdef CONFIG_CPU_FREQ
+/*
+ * Walk PRCM rate table and fillout cpufreq freq_table
+ */
+static struct cpufreq_frequency_table freq_table[ARRAY_SIZE(rate_table)];
+
+void omap2_clk_init_cpufreq_table(struct cpufreq_frequency_table **table)
+{
+       struct prcm_config *prcm;
+       int i = 0;
+
+       for (prcm = rate_table; prcm->mpu_speed; prcm++) {
+               if (!(prcm->flags & cpu_mask))
+                       continue;
+               if (prcm->xtal_speed != sys_ck.rate)
+                       continue;
+
+               /* don't put bypass rates in table */
+               if (prcm->dpll_speed == prcm->xtal_speed)
+                       continue;
+
+               freq_table[i].index = i;
+               freq_table[i].frequency = prcm->mpu_speed / 1000;
+               i++;
+       }
+
+       if (i == 0) {
+               printk(KERN_WARNING "%s: failed to initialize frequency "
+                      "table\n", __func__);
+               return;
+       }
+
+       freq_table[i].index = i;
+       freq_table[i].frequency = CPUFREQ_TABLE_END;
+
+       *table = &freq_table[0];
+}
+#endif
+
 static struct clk_functions omap2_clk_functions = {
        .clk_enable             = omap2_clk_enable,
        .clk_disable            = omap2_clk_disable,
        .clk_set_rate           = omap2_clk_set_rate,
        .clk_set_parent         = omap2_clk_set_parent,
        .clk_disable_unused     = omap2_clk_disable_unused,
+#ifdef CONFIG_CPU_FREQ
+       .clk_init_cpufreq_table = omap2_clk_init_cpufreq_table,
+#endif
 };
 
 static u32 omap2_get_apll_clkin(void)
 
 #include <linux/io.h>
 
 #include <mach/hardware.h>
+#include <mach/clock.h>
 #include <asm/system.h>
 
 #define VERY_HI_RATE   900000000
 
+static struct cpufreq_frequency_table *freq_table;
+
 #ifdef CONFIG_ARCH_OMAP1
 #define MPU_CLK                "mpu"
 #else
 
 int omap_verify_speed(struct cpufreq_policy *policy)
 {
+       if (freq_table)
+               return cpufreq_frequency_table_verify(policy, freq_table);
+
        if (policy->cpu)
                return -EINVAL;
 
        struct cpufreq_freqs freqs;
        int ret = 0;
 
+       /* Ensure desired rate is within allowed range.  Some govenors
+        * (ondemand) will just pass target_freq=0 to get the minimum. */
+       if (target_freq < policy->cpuinfo.min_freq)
+               target_freq = policy->cpuinfo.min_freq;
+       if (target_freq > policy->cpuinfo.max_freq)
+               target_freq = policy->cpuinfo.max_freq;
+
        freqs.old = omap_getspeed(0);
        freqs.new = clk_round_rate(mpu_clk, target_freq * 1000) / 1000;
        freqs.cpu = 0;
 
+       if (freqs.old == freqs.new)
+               return ret;
+
        cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE);
-       ret = clk_set_rate(mpu_clk, target_freq * 1000);
+#ifdef CONFIG_CPU_FREQ_DEBUG
+       printk(KERN_DEBUG "cpufreq-omap: transition: %u --> %u\n",
+              freqs.old, freqs.new);
+#endif
+       ret = clk_set_rate(mpu_clk, freqs.new * 1000);
        cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE);
 
        return ret;
 
 static int __init omap_cpu_init(struct cpufreq_policy *policy)
 {
+       int result = 0;
+
        mpu_clk = clk_get(NULL, MPU_CLK);
        if (IS_ERR(mpu_clk))
                return PTR_ERR(mpu_clk);
 
        if (policy->cpu != 0)
                return -EINVAL;
+
        policy->cur = policy->min = policy->max = omap_getspeed(0);
-       policy->cpuinfo.min_freq = clk_round_rate(mpu_clk, 0) / 1000;
-       policy->cpuinfo.max_freq = clk_round_rate(mpu_clk, VERY_HI_RATE) / 1000;
-       policy->cpuinfo.transition_latency = CPUFREQ_ETERNAL;
+
+       clk_init_cpufreq_table(&freq_table);
+       if (freq_table) {
+               result = cpufreq_frequency_table_cpuinfo(policy, freq_table);
+               if (!result)
+                       cpufreq_frequency_table_get_attr(freq_table,
+                                                       policy->cpu);
+       } else {
+               policy->cpuinfo.min_freq = clk_round_rate(mpu_clk, 0) / 1000;
+               policy->cpuinfo.max_freq = clk_round_rate(mpu_clk,
+                                                       VERY_HI_RATE) / 1000;
+       }
+
+       /* FIXME: what's the actual transition time? */
+       policy->cpuinfo.transition_latency = 10 * 1000 * 1000;
 
        return 0;
 }
        return 0;
 }
 
+static struct freq_attr *omap_cpufreq_attr[] = {
+       &cpufreq_freq_attr_scaling_available_freqs,
+       NULL,
+};
+
 static struct cpufreq_driver omap_driver = {
        .flags          = CPUFREQ_STICKY,
        .verify         = omap_verify_speed,
        .init           = omap_cpu_init,
        .exit           = omap_cpu_exit,
        .name           = "omap",
+       .attr           = omap_cpufreq_attr,
 };
 
 static int __init omap_cpufreq_init(void)
 }
 
 arch_initcall(omap_cpufreq_init);
+
+/*
+ * if ever we want to remove this, upon cleanup call:
+ *
+ * cpufreq_unregister_driver()
+ * cpufreq_frequency_table_put_attr()
+ */
+