|
@@ -28,3 +28,158 @@
|
|
|
|
|
|
|
|
|
#include <trace/events/power.h>
|
|
|
+
|
|
|
+#include "soc.h"
|
|
|
+#include "clockdomain.h"
|
|
|
+#include "clock.h"
|
|
|
+#include "cm.h"
|
|
|
+#include "cm2xxx.h"
|
|
|
+#include "cm3xxx.h"
|
|
|
+#include "cm-regbits-24xx.h"
|
|
|
+#include "cm-regbits-34xx.h"
|
|
|
+#include "common.h"
|
|
|
+
|
|
|
+/*
|
|
|
+ * MAX_MODULE_ENABLE_WAIT: maximum of number of microseconds to wait
|
|
|
+ * for a module to indicate that it is no longer in idle
|
|
|
+ */
|
|
|
+#define MAX_MODULE_ENABLE_WAIT 100000
|
|
|
+
|
|
|
+u16 cpu_mask;
|
|
|
+
|
|
|
+/*
|
|
|
+ * clkdm_control: if true, then when a clock is enabled in the
|
|
|
+ * hardware, its clockdomain will first be enabled; and when a clock
|
|
|
+ * is disabled in the hardware, its clockdomain will be disabled
|
|
|
+ * afterwards.
|
|
|
+ */
|
|
|
+static bool clkdm_control = true;
|
|
|
+
|
|
|
+static LIST_HEAD(clk_hw_omap_clocks);
|
|
|
+
|
|
|
+/*
|
|
|
+ * Used for clocks that have the same value as the parent clock,
|
|
|
+ * divided by some factor
|
|
|
+ */
|
|
|
+unsigned long omap_fixed_divisor_recalc(struct clk_hw *hw,
|
|
|
+ unsigned long parent_rate)
|
|
|
+{
|
|
|
+ struct clk_hw_omap *oclk;
|
|
|
+
|
|
|
+ if (!hw) {
|
|
|
+ pr_warn("%s: hw is NULL\n", __func__);
|
|
|
+ return -EINVAL;
|
|
|
+ }
|
|
|
+
|
|
|
+ oclk = to_clk_hw_omap(hw);
|
|
|
+
|
|
|
+ WARN_ON(!oclk->fixed_div);
|
|
|
+
|
|
|
+ return parent_rate / oclk->fixed_div;
|
|
|
+}
|
|
|
+
|
|
|
+/*
|
|
|
+ * OMAP2+ specific clock functions
|
|
|
+ */
|
|
|
+
|
|
|
+/* Private functions */
|
|
|
+
|
|
|
+
|
|
|
+/**
|
|
|
+ * _wait_idlest_generic - wait for a module to leave the idle state
|
|
|
+ * @reg: virtual address of module IDLEST register
|
|
|
+ * @mask: value to mask against to determine if the module is active
|
|
|
+ * @idlest: idle state indicator (0 or 1) for the clock
|
|
|
+ * @name: name of the clock (for printk)
|
|
|
+ *
|
|
|
+ * Wait for a module to leave idle, where its idle-status register is
|
|
|
+ * not inside the CM module. Returns 1 if the module left idle
|
|
|
+ * promptly, or 0 if the module did not leave idle before the timeout
|
|
|
+ * elapsed. XXX Deprecated - should be moved into drivers for the
|
|
|
+ * individual IP block that the IDLEST register exists in.
|
|
|
+ */
|
|
|
+static int _wait_idlest_generic(void __iomem *reg, u32 mask, u8 idlest,
|
|
|
+ const char *name)
|
|
|
+{
|
|
|
+ int i = 0, ena = 0;
|
|
|
+
|
|
|
+ ena = (idlest) ? 0 : mask;
|
|
|
+
|
|
|
+ omap_test_timeout(((__raw_readl(reg) & mask) == ena),
|
|
|
+ MAX_MODULE_ENABLE_WAIT, i);
|
|
|
+
|
|
|
+ if (i < MAX_MODULE_ENABLE_WAIT)
|
|
|
+ pr_debug("omap clock: module associated with clock %s ready after %d loops\n",
|
|
|
+ name, i);
|
|
|
+ else
|
|
|
+ pr_err("omap clock: module associated with clock %s didn't enable in %d tries\n",
|
|
|
+ name, MAX_MODULE_ENABLE_WAIT);
|
|
|
+
|
|
|
+ return (i < MAX_MODULE_ENABLE_WAIT) ? 1 : 0;
|
|
|
+};
|
|
|
+
|
|
|
+/**
|
|
|
+ * _omap2_module_wait_ready - wait for an OMAP module to leave IDLE
|
|
|
+ * @clk: struct clk * belonging to the module
|
|
|
+ *
|
|
|
+ * If the necessary clocks for the OMAP hardware IP block that
|
|
|
+ * corresponds to clock @clk are enabled, then wait for the module to
|
|
|
+ * indicate readiness (i.e., to leave IDLE). This code does not
|
|
|
+ * belong in the clock code and will be moved in the medium term to
|
|
|
+ * module-dependent code. No return value.
|
|
|
+ */
|
|
|
+static void _omap2_module_wait_ready(struct clk_hw_omap *clk)
|
|
|
+{
|
|
|
+ void __iomem *companion_reg, *idlest_reg;
|
|
|
+ u8 other_bit, idlest_bit, idlest_val, idlest_reg_id;
|
|
|
+ s16 prcm_mod;
|
|
|
+ int r;
|
|
|
+
|
|
|
+ /* Not all modules have multiple clocks that their IDLEST depends on */
|
|
|
+ if (clk->ops->find_companion) {
|
|
|
+ clk->ops->find_companion(clk, &companion_reg, &other_bit);
|
|
|
+ if (!(__raw_readl(companion_reg) & (1 << other_bit)))
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ clk->ops->find_idlest(clk, &idlest_reg, &idlest_bit, &idlest_val);
|
|
|
+ r = cm_split_idlest_reg(idlest_reg, &prcm_mod, &idlest_reg_id);
|
|
|
+ if (r) {
|
|
|
+ /* IDLEST register not in the CM module */
|
|
|
+ _wait_idlest_generic(idlest_reg, (1 << idlest_bit), idlest_val,
|
|
|
+ __clk_get_name(clk->hw.clk));
|
|
|
+ } else {
|
|
|
+ cm_wait_module_ready(prcm_mod, idlest_reg_id, idlest_bit);
|
|
|
+ };
|
|
|
+}
|
|
|
+
|
|
|
+/* Public functions */
|
|
|
+
|
|
|
+/**
|
|
|
+ * omap2_init_clk_clkdm - look up a clockdomain name, store pointer in clk
|
|
|
+ * @clk: OMAP clock struct ptr to use
|
|
|
+ *
|
|
|
+ * Convert a clockdomain name stored in a struct clk 'clk' into a
|
|
|
+ * clockdomain pointer, and save it into the struct clk. Intended to be
|
|
|
+ * called during clk_register(). No return value.
|
|
|
+ */
|
|
|
+void omap2_init_clk_clkdm(struct clk_hw *hw)
|
|
|
+{
|
|
|
+ struct clk_hw_omap *clk = to_clk_hw_omap(hw);
|
|
|
+ struct clockdomain *clkdm;
|
|
|
+ const char *clk_name;
|
|
|
+
|
|
|
+ if (!clk->clkdm_name)
|
|
|
+ return;
|
|
|
+
|
|
|
+ clk_name = __clk_get_name(hw->clk);
|
|
|
+
|
|
|
+ clkdm = clkdm_lookup(clk->clkdm_name);
|
|
|
+ if (clkdm) {
|
|
|
+ pr_debug("clock: associated clk %s to clkdm %s\n",
|
|
|
+ clk_name, clk->clkdm_name);
|
|
|
+ clk->clkdm = clkdm;
|
|
|
+ } else {
|
|
|
+ pr_debug("clock: could not associate clk %s to clkdm %s\n",
|
|
|
+ clk_name, clk->clkdm_name);
|
|
|
+ }
|