Merge branch 'vhost' of git://git.kernel.org/pub/scm/linux/kernel/git/mst/vhost
[pandora-kernel.git] / arch / arm / mach-omap2 / clock.c
index 426d76f..a6d0b34 100644 (file)
@@ -17,6 +17,8 @@
 #include <linux/kernel.h>
 #include <linux/list.h>
 #include <linux/errno.h>
+#include <linux/err.h>
+#include <linux/delay.h>
 #include <linux/clk.h>
 #include <linux/io.h>
 #include <linux/bitops.h>
@@ -35,9 +37,9 @@
 
 u8 cpu_mask;
 
-/*-------------------------------------------------------------------------
- * OMAP2/3/4 specific clock functions
- *-------------------------------------------------------------------------*/
+/*
+ * OMAP2+ specific clock functions
+ */
 
 /* Private functions */
 
@@ -69,20 +71,6 @@ static void _omap2_module_wait_ready(struct clk *clk)
                             clk->name);
 }
 
-/* Enables clock without considering parent dependencies or use count
- * REVISIT: Maybe change this to use clk->enable like on omap1?
- */
-static int _omap2_clk_enable(struct clk *clk)
-{
-       return clk->ops->enable(clk);
-}
-
-/* Disables clock without considering parent dependencies or use count */
-static void _omap2_clk_disable(struct clk *clk)
-{
-       clk->ops->disable(clk);
-}
-
 /* Public functions */
 
 /**
@@ -243,46 +231,106 @@ const struct clkops clkops_omap2_dflt = {
        .disable        = omap2_dflt_clk_disable,
 };
 
+/**
+ * omap2_clk_disable - disable a clock, if the system is not using it
+ * @clk: struct clk * to disable
+ *
+ * Decrements the usecount on struct clk @clk.  If there are no users
+ * left, call the clkops-specific clock disable function to disable it
+ * in hardware.  If the clock is part of a clockdomain (which they all
+ * should be), request that the clockdomain be disabled.  (It too has
+ * a usecount, and so will not be disabled in the hardware until it no
+ * longer has any users.)  If the clock has a parent clock (most of
+ * them do), then call ourselves, recursing on the parent clock.  This
+ * can cause an entire branch of the clock tree to be powered off by
+ * simply disabling one clock.  Intended to be called with the clockfw_lock
+ * spinlock held.  No return value.
+ */
 void omap2_clk_disable(struct clk *clk)
 {
-       if (clk->usecount > 0 && !(--clk->usecount)) {
-               _omap2_clk_disable(clk);
-               if (clk->parent)
-                       omap2_clk_disable(clk->parent);
-               if (clk->clkdm)
-                       omap2_clkdm_clk_disable(clk->clkdm, clk);
-
+       if (clk->usecount == 0) {
+               WARN(1, "clock: %s: omap2_clk_disable() called, but usecount "
+                    "already 0?", clk->name);
+               return;
        }
+
+       pr_debug("clock: %s: decrementing usecount\n", clk->name);
+
+       clk->usecount--;
+
+       if (clk->usecount > 0)
+               return;
+
+       pr_debug("clock: %s: disabling in hardware\n", clk->name);
+
+       clk->ops->disable(clk);
+
+       if (clk->clkdm)
+               omap2_clkdm_clk_disable(clk->clkdm, clk);
+
+       if (clk->parent)
+               omap2_clk_disable(clk->parent);
 }
 
+/**
+ * omap2_clk_enable - request that the system enable a clock
+ * @clk: struct clk * to enable
+ *
+ * Increments the usecount on struct clk @clk.  If there were no users
+ * previously, then recurse up the clock tree, enabling all of the
+ * clock's parents and all of the parent clockdomains, and finally,
+ * enabling @clk's clockdomain, and @clk itself.  Intended to be
+ * called with the clockfw_lock spinlock held.  Returns 0 upon success
+ * or a negative error code upon failure.
+ */
 int omap2_clk_enable(struct clk *clk)
 {
-       int ret = 0;
+       int ret;
 
-       if (clk->usecount++ == 0) {
-               if (clk->clkdm)
-                       omap2_clkdm_clk_enable(clk->clkdm, clk);
+       pr_debug("clock: %s: incrementing usecount\n", clk->name);
 
-               if (clk->parent) {
-                       ret = omap2_clk_enable(clk->parent);
-                       if (ret)
-                               goto err;
-               }
+       clk->usecount++;
+
+       if (clk->usecount > 1)
+               return 0;
 
-               ret = _omap2_clk_enable(clk);
+       pr_debug("clock: %s: enabling in hardware\n", clk->name);
+
+       if (clk->parent) {
+               ret = omap2_clk_enable(clk->parent);
                if (ret) {
-                       if (clk->parent)
-                               omap2_clk_disable(clk->parent);
+                       WARN(1, "clock: %s: could not enable parent %s: %d\n",
+                            clk->name, clk->parent->name, ret);
+                       goto oce_err1;
+               }
+       }
 
-                       goto err;
+       if (clk->clkdm) {
+               ret = omap2_clkdm_clk_enable(clk->clkdm, clk);
+               if (ret) {
+                       WARN(1, "clock: %s: could not enable clockdomain %s: "
+                            "%d\n", clk->name, clk->clkdm->name, ret);
+                       goto oce_err2;
                }
        }
-       return ret;
 
-err:
+       ret = clk->ops->enable(clk);
+       if (ret) {
+               WARN(1, "clock: %s: could not enable: %d\n", clk->name, ret);
+               goto oce_err3;
+       }
+
+       return 0;
+
+oce_err3:
        if (clk->clkdm)
                omap2_clkdm_clk_disable(clk->clkdm, clk);
+oce_err2:
+       if (clk->parent)
+               omap2_clk_disable(clk->parent);
+oce_err1:
        clk->usecount--;
+
        return ret;
 }
 
@@ -323,9 +371,9 @@ const struct clkops clkops_omap3_noncore_dpll_ops = {
 #endif
 
 
-/*-------------------------------------------------------------------------
- * Omap2 clock reset and init functions
- *-------------------------------------------------------------------------*/
+/*
+ * OMAP2+ clock reset and init functions
+ */
 
 #ifdef CONFIG_OMAP_RESET_CLOCKS
 void omap2_clk_disable_unused(struct clk *clk)
@@ -342,13 +390,97 @@ void omap2_clk_disable_unused(struct clk *clk)
        if (cpu_is_omap34xx()) {
                omap2_clk_enable(clk);
                omap2_clk_disable(clk);
-       } else
-               _omap2_clk_disable(clk);
+       } else {
+               clk->ops->disable(clk);
+       }
        if (clk->clkdm != NULL)
                pwrdm_clkdm_state_switch(clk->clkdm);
 }
 #endif
 
+/**
+ * omap2_clk_switch_mpurate_at_boot - switch ARM MPU rate by boot-time argument
+ * @mpurate_ck_name: clk name of the clock to change rate
+ *
+ * Change the ARM MPU clock rate to the rate specified on the command
+ * line, if one was specified.  @mpurate_ck_name should be
+ * "virt_prcm_set" on OMAP2xxx and "dpll1_ck" on OMAP34xx/OMAP36xx.
+ * XXX Does not handle voltage scaling - on OMAP2xxx this is currently
+ * handled by the virt_prcm_set clock, but this should be handled by
+ * the OPP layer.  XXX This is intended to be handled by the OPP layer
+ * code in the near future and should be removed from the clock code.
+ * Returns -EINVAL if 'mpurate' is zero or if clk_set_rate() rejects
+ * the rate, -ENOENT if the struct clk referred to by @mpurate_ck_name
+ * cannot be found, or 0 upon success.
+ */
+int __init omap2_clk_switch_mpurate_at_boot(const char *mpurate_ck_name)
+{
+       struct clk *mpurate_ck;
+       int r;
+
+       if (!mpurate)
+               return -EINVAL;
+
+       mpurate_ck = clk_get(NULL, mpurate_ck_name);
+       if (WARN(IS_ERR(mpurate_ck), "Failed to get %s.\n", mpurate_ck_name))
+               return -ENOENT;
+
+       r = clk_set_rate(mpurate_ck, mpurate);
+       if (IS_ERR_VALUE(r)) {
+               WARN(1, "clock: %s: unable to set MPU rate to %d: %d\n",
+                    mpurate_ck->name, mpurate, r);
+               return -EINVAL;
+       }
+
+       calibrate_delay();
+       recalculate_root_clocks();
+
+       clk_put(mpurate_ck);
+
+       return 0;
+}
+
+/**
+ * omap2_clk_print_new_rates - print summary of current clock tree rates
+ * @hfclkin_ck_name: clk name for the off-chip HF oscillator
+ * @core_ck_name: clk name for the on-chip CORE_CLK
+ * @mpu_ck_name: clk name for the ARM MPU clock
+ *
+ * Prints a short message to the console with the HFCLKIN oscillator
+ * rate, the rate of the CORE clock, and the rate of the ARM MPU clock.
+ * Called by the boot-time MPU rate switching code.   XXX This is intended
+ * to be handled by the OPP layer code in the near future and should be
+ * removed from the clock code.  No return value.
+ */
+void __init omap2_clk_print_new_rates(const char *hfclkin_ck_name,
+                                     const char *core_ck_name,
+                                     const char *mpu_ck_name)
+{
+       struct clk *hfclkin_ck, *core_ck, *mpu_ck;
+       unsigned long hfclkin_rate;
+
+       mpu_ck = clk_get(NULL, mpu_ck_name);
+       if (WARN(IS_ERR(mpu_ck), "clock: failed to get %s.\n", mpu_ck_name))
+               return;
+
+       core_ck = clk_get(NULL, core_ck_name);
+       if (WARN(IS_ERR(core_ck), "clock: failed to get %s.\n", core_ck_name))
+               return;
+
+       hfclkin_ck = clk_get(NULL, hfclkin_ck_name);
+       if (WARN(IS_ERR(hfclkin_ck), "Failed to get %s.\n", hfclkin_ck_name))
+               return;
+
+       hfclkin_rate = clk_get_rate(hfclkin_ck);
+
+       pr_info("Switched to new clocking rate (Crystal/Core/MPU): "
+               "%ld.%01ld/%ld/%ld MHz\n",
+               (hfclkin_rate / 1000000),
+               ((hfclkin_rate / 100000) % 10),
+               (clk_get_rate(core_ck) / 1000000),
+               (clk_get_rate(mpu_ck) / 1000000));
+}
+
 /* Common data */
 
 struct clk_functions omap2_clk_functions = {