ARM: tegra: Port tegra to generic clock framework
[deliverable/linux.git] / arch / arm / mach-tegra / tegra30_clocks.c
index 645ef6900a0e885fd53af2c71cfb329552f23133..a9fa8ea0b6e0aeef236f19b35610d3d216332820 100644 (file)
@@ -381,14 +381,16 @@ static int tegra_periph_clk_enable_refcount[CLK_OUT_ENB_NUM * 32];
                udelay(2);                                              \
        } while (0)
 
-
-static inline int clk_set_div(struct clk *c, u32 n)
+static inline int clk_set_div(struct clk_tegra *c, u32 n)
 {
-       return clk_set_rate(c, (clk_get_rate(c->parent) + n-1) / n);
+       struct clk *clk = c->hw.clk;
+
+       return clk_set_rate(clk,
+                       (__clk_get_rate(__clk_get_parent(clk)) + n - 1) / n);
 }
 
 static inline u32 periph_clk_to_reg(
-       struct clk *c, u32 reg_L, u32 reg_V, int offs)
+       struct clk_tegra *c, u32 reg_L, u32 reg_V, int offs)
 {
        u32 reg = c->u.periph.clk_num / 32;
        BUG_ON(reg >= RST_DEVICES_NUM);
@@ -470,15 +472,32 @@ static int clk_div16_get_divider(unsigned long parent_rate, unsigned long rate)
        return divider_u16 - 1;
 }
 
+static unsigned long tegra30_clk_fixed_recalc_rate(struct clk_hw *hw,
+               unsigned long parent_rate)
+{
+       return to_clk_tegra(hw)->fixed_rate;
+}
+
+struct clk_ops tegra_clk_32k_ops = {
+       .recalc_rate = tegra30_clk_fixed_recalc_rate,
+};
+
 /* clk_m functions */
-static unsigned long tegra30_clk_m_autodetect_rate(struct clk *c)
+static unsigned long tegra30_clk_m_recalc_rate(struct clk_hw *hw,
+               unsigned long parent_rate)
+{
+       if (!to_clk_tegra(hw)->fixed_rate)
+               to_clk_tegra(hw)->fixed_rate = clk_measure_input_freq();
+       return to_clk_tegra(hw)->fixed_rate;
+}
+
+static void tegra30_clk_m_init(struct clk_hw *hw)
 {
        u32 osc_ctrl = clk_readl(OSC_CTRL);
        u32 auto_clock_control = osc_ctrl & ~OSC_CTRL_OSC_FREQ_MASK;
        u32 pll_ref_div = osc_ctrl & OSC_CTRL_PLL_REF_DIV_MASK;
 
-       c->rate = clk_measure_input_freq();
-       switch (c->rate) {
+       switch (to_clk_tegra(hw)->fixed_rate) {
        case 12000000:
                auto_clock_control |= OSC_CTRL_OSC_FREQ_12MHZ;
                BUG_ON(pll_ref_div != OSC_CTRL_PLL_REF_DIV_1);
@@ -508,46 +527,44 @@ static unsigned long tegra30_clk_m_autodetect_rate(struct clk *c)
                BUG_ON(pll_ref_div != OSC_CTRL_PLL_REF_DIV_4);
                break;
        default:
-               pr_err("%s: Unexpected clock rate %ld", __func__, c->rate);
+               pr_err("%s: Unexpected clock rate %ld", __func__,
+                               to_clk_tegra(hw)->fixed_rate);
                BUG();
        }
        clk_writel(auto_clock_control, OSC_CTRL);
-       return c->rate;
 }
 
-static void tegra30_clk_m_init(struct clk *c)
-{
-       pr_debug("%s on clock %s\n", __func__, c->name);
-       tegra30_clk_m_autodetect_rate(c);
-}
+struct clk_ops tegra30_clk_m_ops = {
+       .init = tegra30_clk_m_init,
+       .recalc_rate = tegra30_clk_m_recalc_rate,
+};
 
-static int tegra30_clk_m_enable(struct clk *c)
+static unsigned long tegra30_clk_m_div_recalc_rate(struct clk_hw *hw,
+               unsigned long parent_rate)
 {
-       pr_debug("%s on clock %s\n", __func__, c->name);
-       return 0;
-}
+       struct clk_tegra *c = to_clk_tegra(hw);
+       u64 rate = parent_rate;
 
-static void tegra30_clk_m_disable(struct clk *c)
-{
-       pr_debug("%s on clock %s\n", __func__, c->name);
-       WARN(1, "Attempting to disable main SoC clock\n");
-}
+       if (c->mul != 0 && c->div != 0) {
+               rate *= c->mul;
+               rate += c->div - 1; /* round up */
+               do_div(rate, c->div);
+       }
 
-struct clk_ops tegra30_clk_m_ops = {
-       .init           = tegra30_clk_m_init,
-       .enable         = tegra30_clk_m_enable,
-       .disable        = tegra30_clk_m_disable,
-};
+       return rate;
+}
 
 struct clk_ops tegra_clk_m_div_ops = {
-       .enable         = tegra30_clk_m_enable,
+       .recalc_rate = tegra30_clk_m_div_recalc_rate,
 };
 
 /* PLL reference divider functions */
-static void tegra30_pll_ref_init(struct clk *c)
+static unsigned long tegra30_pll_ref_recalc_rate(struct clk_hw *hw,
+                       unsigned long parent_rate)
 {
+       struct clk_tegra *c = to_clk_tegra(hw);
+       unsigned long rate = parent_rate;
        u32 pll_ref_div = clk_readl(OSC_CTRL) & OSC_CTRL_PLL_REF_DIV_MASK;
-       pr_debug("%s on clock %s\n", __func__, c->name);
 
        switch (pll_ref_div) {
        case OSC_CTRL_PLL_REF_DIV_1:
@@ -564,13 +581,18 @@ static void tegra30_pll_ref_init(struct clk *c)
                BUG();
        }
        c->mul = 1;
-       c->state = ON;
+
+       if (c->mul != 0 && c->div != 0) {
+               rate *= c->mul;
+               rate += c->div - 1; /* round up */
+               do_div(rate, c->div);
+       }
+
+       return rate;
 }
 
 struct clk_ops tegra_pll_ref_ops = {
-       .init           = tegra30_pll_ref_init,
-       .enable         = tegra30_clk_m_enable,
-       .disable        = tegra30_clk_m_disable,
+       .recalc_rate = tegra30_pll_ref_recalc_rate,
 };
 
 /* super clock functions */
@@ -581,56 +603,50 @@ struct clk_ops tegra_pll_ref_ops = {
  * only when its parent is a fixed rate PLL, since we can't change PLL rate
  * in this case.
  */
-static void tegra30_super_clk_init(struct clk *c)
+static void tegra30_super_clk_init(struct clk_hw *hw)
 {
-       u32 val;
-       int source;
-       int shift;
-       const struct clk_mux_sel *sel;
-       val = clk_readl(c->reg + SUPER_CLK_MUX);
-       c->state = ON;
-       BUG_ON(((val & SUPER_STATE_MASK) != SUPER_STATE_RUN) &&
-               ((val & SUPER_STATE_MASK) != SUPER_STATE_IDLE));
-       shift = ((val & SUPER_STATE_MASK) == SUPER_STATE_IDLE) ?
-               SUPER_IDLE_SOURCE_SHIFT : SUPER_RUN_SOURCE_SHIFT;
-       source = (val >> shift) & SUPER_SOURCE_MASK;
-       if (c->flags & DIV_2)
-               source |= val & SUPER_LP_DIV2_BYPASS;
-       for (sel = c->inputs; sel->input != NULL; sel++) {
-               if (sel->value == source)
-                       break;
-       }
-       BUG_ON(sel->input == NULL);
-       c->parent = sel->input;
+       struct clk_tegra *c = to_clk_tegra(hw);
+       struct clk_tegra *p =
+                       to_clk_tegra(__clk_get_hw(__clk_get_parent(hw->clk)));
 
+       c->state = ON;
        if (c->flags & DIV_U71) {
                /* Init safe 7.1 divider value (does not affect PLLX path) */
                clk_writel(SUPER_CLOCK_DIV_U71_MIN << SUPER_CLOCK_DIV_U71_SHIFT,
                           c->reg + SUPER_CLK_DIVIDER);
                c->mul = 2;
                c->div = 2;
-               if (!(c->parent->flags & PLLX))
+               if (!(p->flags & PLLX))
                        c->div += SUPER_CLOCK_DIV_U71_MIN;
        } else
                clk_writel(0, c->reg + SUPER_CLK_DIVIDER);
 }
 
-static int tegra30_super_clk_enable(struct clk *c)
+static u8 tegra30_super_clk_get_parent(struct clk_hw *hw)
 {
-       return 0;
-}
+       struct clk_tegra *c = to_clk_tegra(hw);
+       u32 val;
+       int source;
+       int shift;
 
-static void tegra30_super_clk_disable(struct clk *c)
-{
-       /* since tegra 3 has 2 CPU super clocks - low power lp-mode clock and
-          geared up g-mode super clock - mode switch may request to disable
-          either of them; accept request with no affect on h/w */
+       val = clk_readl(c->reg + SUPER_CLK_MUX);
+       BUG_ON(((val & SUPER_STATE_MASK) != SUPER_STATE_RUN) &&
+               ((val & SUPER_STATE_MASK) != SUPER_STATE_IDLE));
+       shift = ((val & SUPER_STATE_MASK) == SUPER_STATE_IDLE) ?
+               SUPER_IDLE_SOURCE_SHIFT : SUPER_RUN_SOURCE_SHIFT;
+       source = (val >> shift) & SUPER_SOURCE_MASK;
+       if (c->flags & DIV_2)
+               source |= val & SUPER_LP_DIV2_BYPASS;
+
+       return source;
 }
 
-static int tegra30_super_clk_set_parent(struct clk *c, struct clk *p)
+static int tegra30_super_clk_set_parent(struct clk_hw *hw, u8 index)
 {
+       struct clk_tegra *c = to_clk_tegra(hw);
+       struct clk_tegra *p =
+                       to_clk_tegra(__clk_get_hw(clk_get_parent(hw->clk)));
        u32 val;
-       const struct clk_mux_sel *sel;
        int shift;
 
        val = clk_readl(c->reg + SUPER_CLK_MUX);
@@ -638,48 +654,36 @@ static int tegra30_super_clk_set_parent(struct clk *c, struct clk *p)
                ((val & SUPER_STATE_MASK) != SUPER_STATE_IDLE));
        shift = ((val & SUPER_STATE_MASK) == SUPER_STATE_IDLE) ?
                SUPER_IDLE_SOURCE_SHIFT : SUPER_RUN_SOURCE_SHIFT;
-       for (sel = c->inputs; sel->input != NULL; sel++) {
-               if (sel->input == p) {
-                       /* For LP mode super-clock switch between PLLX direct
-                          and divided-by-2 outputs is allowed only when other
-                          than PLLX clock source is current parent */
-                       if ((c->flags & DIV_2) && (p->flags & PLLX) &&
-                           ((sel->value ^ val) & SUPER_LP_DIV2_BYPASS)) {
-                               if (c->parent->flags & PLLX)
-                                       return -EINVAL;
-                               val ^= SUPER_LP_DIV2_BYPASS;
-                               clk_writel_delay(val, c->reg);
-                       }
-                       val &= ~(SUPER_SOURCE_MASK << shift);
-                       val |= (sel->value & SUPER_SOURCE_MASK) << shift;
-
-                       /* 7.1 divider for CPU super-clock does not affect
-                          PLLX path */
-                       if (c->flags & DIV_U71) {
-                               u32 div = 0;
-                               if (!(p->flags & PLLX)) {
-                                       div = clk_readl(c->reg +
-                                                       SUPER_CLK_DIVIDER);
-                                       div &= SUPER_CLOCK_DIV_U71_MASK;
-                                       div >>= SUPER_CLOCK_DIV_U71_SHIFT;
-                               }
-                               c->div = div + 2;
-                               c->mul = 2;
-                       }
-
-                       if (c->refcnt)
-                               clk_enable(p);
-
-                       clk_writel_delay(val, c->reg);
 
-                       if (c->refcnt && c->parent)
-                               clk_disable(c->parent);
+       /* For LP mode super-clock switch between PLLX direct
+          and divided-by-2 outputs is allowed only when other
+          than PLLX clock source is current parent */
+       if ((c->flags & DIV_2) && (p->flags & PLLX) &&
+           ((index ^ val) & SUPER_LP_DIV2_BYPASS)) {
+               if (p->flags & PLLX)
+                       return -EINVAL;
+               val ^= SUPER_LP_DIV2_BYPASS;
+               clk_writel_delay(val, c->reg);
+       }
+       val &= ~(SUPER_SOURCE_MASK << shift);
+       val |= (index & SUPER_SOURCE_MASK) << shift;
 
-                       clk_reparent(c, p);
-                       return 0;
+       /* 7.1 divider for CPU super-clock does not affect
+          PLLX path */
+       if (c->flags & DIV_U71) {
+               u32 div = 0;
+               if (!(p->flags & PLLX)) {
+                       div = clk_readl(c->reg +
+                                       SUPER_CLK_DIVIDER);
+                       div &= SUPER_CLOCK_DIV_U71_MASK;
+                       div >>= SUPER_CLOCK_DIV_U71_SHIFT;
                }
+               c->div = div + 2;
+               c->mul = 2;
        }
-       return -EINVAL;
+       clk_writel_delay(val, c->reg);
+
+       return 0;
 }
 
 /*
@@ -691,10 +695,15 @@ static int tegra30_super_clk_set_parent(struct clk *c, struct clk *p)
  * rate of this PLL can't be changed, and it has many other children. In
  * this case use 7.1 fractional divider to adjust the super clock rate.
  */
-static int tegra30_super_clk_set_rate(struct clk *c, unsigned long rate)
+static int tegra30_super_clk_set_rate(struct clk_hw *hw, unsigned long rate,
+               unsigned long parent_rate)
 {
-       if ((c->flags & DIV_U71) && (c->parent->flags & PLL_FIXED)) {
-               int div = clk_div71_get_divider(c->parent->u.pll.fixed_rate,
+       struct clk_tegra *c = to_clk_tegra(hw);
+       struct clk *parent = __clk_get_parent(hw->clk);
+       struct clk_tegra *cparent = to_clk_tegra(__clk_get_hw(parent));
+
+       if ((c->flags & DIV_U71) && (cparent->flags & PLL_FIXED)) {
+               int div = clk_div71_get_divider(parent_rate,
                                        rate, c->flags, ROUND_DIVIDER_DOWN);
                div = max(div, SUPER_CLOCK_DIV_U71_MIN);
 
@@ -704,55 +713,86 @@ static int tegra30_super_clk_set_rate(struct clk *c, unsigned long rate)
                c->mul = 2;
                return 0;
        }
-       return clk_set_rate(c->parent, rate);
+       return 0;
+}
+
+static unsigned long tegra30_super_clk_recalc_rate(struct clk_hw *hw,
+               unsigned long parent_rate)
+{
+       struct clk_tegra *c = to_clk_tegra(hw);
+       u64 rate = parent_rate;
+
+       if (c->mul != 0 && c->div != 0) {
+               rate *= c->mul;
+               rate += c->div - 1; /* round up */
+               do_div(rate, c->div);
+       }
+
+       return rate;
+}
+
+static long tegra30_super_clk_round_rate(struct clk_hw *hw, unsigned long rate,
+                               unsigned long *prate)
+{
+       struct clk_tegra *c = to_clk_tegra(hw);
+       struct clk *parent = __clk_get_parent(hw->clk);
+       struct clk_tegra *cparent = to_clk_tegra(__clk_get_hw(parent));
+       int mul = 2;
+       int div;
+
+       if ((c->flags & DIV_U71) && (cparent->flags & PLL_FIXED)) {
+               div = clk_div71_get_divider(*prate,
+                               rate, c->flags, ROUND_DIVIDER_DOWN);
+               div = max(div, SUPER_CLOCK_DIV_U71_MIN) + 2;
+               rate = *prate * mul;
+               rate += div - 1; /* round up */
+               do_div(rate, c->div);
+
+               return rate;
+       }
+       return *prate;
 }
 
 struct clk_ops tegra30_super_ops = {
-       .init                   = tegra30_super_clk_init,
-       .enable                 = tegra30_super_clk_enable,
-       .disable                = tegra30_super_clk_disable,
-       .set_parent             = tegra30_super_clk_set_parent,
-       .set_rate               = tegra30_super_clk_set_rate,
+       .init = tegra30_super_clk_init,
+       .set_parent = tegra30_super_clk_set_parent,
+       .get_parent = tegra30_super_clk_get_parent,
+       .recalc_rate = tegra30_super_clk_recalc_rate,
+       .round_rate = tegra30_super_clk_round_rate,
+       .set_rate = tegra30_super_clk_set_rate,
 };
 
-static int tegra30_twd_clk_set_rate(struct clk *c, unsigned long rate)
+static unsigned long tegra30_twd_clk_recalc_rate(struct clk_hw *hw,
+               unsigned long parent_rate)
 {
-       /* The input value 'rate' is the clock rate of the CPU complex. */
-       c->rate = (rate * c->mul) / c->div;
-       return 0;
+       struct clk_tegra *c = to_clk_tegra(hw);
+       u64 rate = parent_rate;
+
+       if (c->mul != 0 && c->div != 0) {
+               rate *= c->mul;
+               rate += c->div - 1; /* round up */
+               do_div(rate, c->div);
+       }
+
+       return rate;
 }
 
 struct clk_ops tegra30_twd_ops = {
-       .set_rate       = tegra30_twd_clk_set_rate,
+       .recalc_rate = tegra30_twd_clk_recalc_rate,
 };
 
 /* Blink output functions */
-
-static void tegra30_blink_clk_init(struct clk *c)
+static int tegra30_blink_clk_is_enabled(struct clk_hw *hw)
 {
+       struct clk_tegra *c = to_clk_tegra(hw);
        u32 val;
 
        val = pmc_readl(PMC_CTRL);
        c->state = (val & PMC_CTRL_BLINK_ENB) ? ON : OFF;
-       c->mul = 1;
-       val = pmc_readl(c->reg);
-
-       if (val & PMC_BLINK_TIMER_ENB) {
-               unsigned int on_off;
-
-               on_off = (val >> PMC_BLINK_TIMER_DATA_ON_SHIFT) &
-                       PMC_BLINK_TIMER_DATA_ON_MASK;
-               val >>= PMC_BLINK_TIMER_DATA_OFF_SHIFT;
-               val &= PMC_BLINK_TIMER_DATA_OFF_MASK;
-               on_off += val;
-               /* each tick in the blink timer is 4 32KHz clocks */
-               c->div = on_off * 4;
-       } else {
-               c->div = 1;
-       }
+       return c->state;
 }
 
-static int tegra30_blink_clk_enable(struct clk *c)
+static int tegra30_blink_clk_enable(struct clk_hw *hw)
 {
        u32 val;
 
@@ -765,7 +805,7 @@ static int tegra30_blink_clk_enable(struct clk *c)
        return 0;
 }
 
-static void tegra30_blink_clk_disable(struct clk *c)
+static void tegra30_blink_clk_disable(struct clk_hw *hw)
 {
        u32 val;
 
@@ -776,9 +816,11 @@ static void tegra30_blink_clk_disable(struct clk *c)
        pmc_writel(val & ~PMC_DPD_PADS_ORIDE_BLINK_ENB, PMC_DPD_PADS_ORIDE);
 }
 
-static int tegra30_blink_clk_set_rate(struct clk *c, unsigned long rate)
+static int tegra30_blink_clk_set_rate(struct clk_hw *hw, unsigned long rate,
+               unsigned long parent_rate)
 {
-       unsigned long parent_rate = clk_get_rate(c->parent);
+       struct clk_tegra *c = to_clk_tegra(hw);
+
        if (rate >= parent_rate) {
                c->div = 1;
                pmc_writel(0, c->reg);
@@ -801,40 +843,77 @@ static int tegra30_blink_clk_set_rate(struct clk *c, unsigned long rate)
        return 0;
 }
 
-struct clk_ops tegra30_blink_clk_ops = {
-       .init                   = &tegra30_blink_clk_init,
-       .enable                 = &tegra30_blink_clk_enable,
-       .disable                = &tegra30_blink_clk_disable,
-       .set_rate               = &tegra30_blink_clk_set_rate,
-};
+static unsigned long tegra30_blink_clk_recalc_rate(struct clk_hw *hw,
+               unsigned long parent_rate)
+{
+       struct clk_tegra *c = to_clk_tegra(hw);
+       u64 rate = parent_rate;
+       u32 val;
+       u32 mul;
+       u32 div;
+       u32 on_off;
 
-/* PLL Functions */
-static int tegra30_pll_clk_wait_for_lock(struct clk *c, u32 lock_reg,
-                                        u32 lock_bit)
+       mul = 1;
+       val = pmc_readl(c->reg);
+
+       if (val & PMC_BLINK_TIMER_ENB) {
+               on_off = (val >> PMC_BLINK_TIMER_DATA_ON_SHIFT) &
+                       PMC_BLINK_TIMER_DATA_ON_MASK;
+               val >>= PMC_BLINK_TIMER_DATA_OFF_SHIFT;
+               val &= PMC_BLINK_TIMER_DATA_OFF_MASK;
+               on_off += val;
+               /* each tick in the blink timer is 4 32KHz clocks */
+               div = on_off * 4;
+       } else {
+               div = 1;
+       }
+
+       if (mul != 0 && div != 0) {
+               rate *= mul;
+               rate += div - 1; /* round up */
+               do_div(rate, div);
+       }
+       return rate;
+}
+
+static long tegra30_blink_clk_round_rate(struct clk_hw *hw, unsigned long rate,
+                               unsigned long *prate)
 {
-#if USE_PLL_LOCK_BITS
-       int i;
-       for (i = 0; i < c->u.pll.lock_delay; i++) {
-               if (clk_readl(lock_reg) & lock_bit) {
-                       udelay(PLL_POST_LOCK_DELAY);
-                       return 0;
-               }
-               udelay(2);              /* timeout = 2 * lock time */
+       int div;
+       int mul;
+       long round_rate = *prate;
+
+       mul = 1;
+
+       if (rate >= *prate) {
+               div = 1;
+       } else {
+               div = DIV_ROUND_UP(*prate / 8, rate);
+               div *= 8;
        }
-       pr_err("Timed out waiting for lock bit on pll %s", c->name);
-       return -1;
-#endif
-       udelay(c->u.pll.lock_delay);
 
-       return 0;
+       round_rate *= mul;
+       round_rate += div - 1;
+       do_div(round_rate, div);
+
+       return round_rate;
 }
 
-static void tegra30_utmi_param_configure(struct clk *c)
+struct clk_ops tegra30_blink_clk_ops = {
+       .is_enabled = tegra30_blink_clk_is_enabled,
+       .enable = tegra30_blink_clk_enable,
+       .disable = tegra30_blink_clk_disable,
+       .recalc_rate = tegra30_blink_clk_recalc_rate,
+       .round_rate = tegra30_blink_clk_round_rate,
+       .set_rate = tegra30_blink_clk_set_rate,
+};
+
+static void tegra30_utmi_param_configure(struct clk_hw *hw)
 {
+       unsigned long main_rate =
+               __clk_get_rate(__clk_get_parent(__clk_get_parent(hw->clk)));
        u32 reg;
        int i;
-       unsigned long main_rate =
-               clk_get_rate(c->parent->parent);
 
        for (i = 0; i < ARRAY_SIZE(utmi_parameters); i++) {
                if (main_rate == utmi_parameters[i].osc_frequency)
@@ -885,50 +964,52 @@ static void tegra30_utmi_param_configure(struct clk *c)
        clk_writel(reg, UTMIP_PLL_CFG1);
 }
 
-static void tegra30_pll_clk_init(struct clk *c)
+/* PLL Functions */
+static int tegra30_pll_clk_wait_for_lock(struct clk_tegra *c, u32 lock_reg,
+                                        u32 lock_bit)
+{
+       int ret = 0;
+
+#if USE_PLL_LOCK_BITS
+       int i;
+       for (i = 0; i < c->u.pll.lock_delay; i++) {
+               if (clk_readl(lock_reg) & lock_bit) {
+                       udelay(PLL_POST_LOCK_DELAY);
+                       return 0;
+               }
+               udelay(2);      /* timeout = 2 * lock time */
+       }
+       pr_err("Timed out waiting for lock bit on pll %s",
+                                       __clk_get_name(hw->clk));
+       ret = -1;
+#else
+       udelay(c->u.pll.lock_delay);
+#endif
+       return ret;
+}
+
+static int tegra30_pll_clk_is_enabled(struct clk_hw *hw)
 {
+       struct clk_tegra *c = to_clk_tegra(hw);
        u32 val = clk_readl(c->reg + PLL_BASE);
 
        c->state = (val & PLL_BASE_ENABLE) ? ON : OFF;
+       return c->state;
+}
 
-       if (c->flags & PLL_FIXED && !(val & PLL_BASE_OVERRIDE)) {
-               const struct clk_pll_freq_table *sel;
-               unsigned long input_rate = clk_get_rate(c->parent);
-               for (sel = c->u.pll.freq_table; sel->input_rate != 0; sel++) {
-                       if (sel->input_rate == input_rate &&
-                               sel->output_rate == c->u.pll.fixed_rate) {
-                               c->mul = sel->n;
-                               c->div = sel->m * sel->p;
-                               return;
-                       }
-               }
-               pr_err("Clock %s has unknown fixed frequency\n", c->name);
-               BUG();
-       } else if (val & PLL_BASE_BYPASS) {
-               c->mul = 1;
-               c->div = 1;
-       } else {
-               c->mul = (val & PLL_BASE_DIVN_MASK) >> PLL_BASE_DIVN_SHIFT;
-               c->div = (val & PLL_BASE_DIVM_MASK) >> PLL_BASE_DIVM_SHIFT;
-               if (c->flags & PLLU)
-                       c->div *= (val & PLLU_BASE_POST_DIV) ? 1 : 2;
-               else
-                       c->div *= (0x1 << ((val & PLL_BASE_DIVP_MASK) >>
-                                       PLL_BASE_DIVP_SHIFT));
-               if (c->flags & PLL_FIXED) {
-                       unsigned long rate = clk_get_rate_locked(c);
-                       BUG_ON(rate != c->u.pll.fixed_rate);
-               }
-       }
+static void tegra30_pll_clk_init(struct clk_hw *hw)
+{
+       struct clk_tegra *c = to_clk_tegra(hw);
 
        if (c->flags & PLLU)
-               tegra30_utmi_param_configure(c);
+               tegra30_utmi_param_configure(hw);
 }
 
-static int tegra30_pll_clk_enable(struct clk *c)
+static int tegra30_pll_clk_enable(struct clk_hw *hw)
 {
+       struct clk_tegra *c = to_clk_tegra(hw);
        u32 val;
-       pr_debug("%s on clock %s\n", __func__, c->name);
+       pr_debug("%s on clock %s\n", __func__, __clk_get_name(hw->clk));
 
 #if USE_PLL_LOCK_BITS
        val = clk_readl(c->reg + PLL_MISC(c));
@@ -951,10 +1032,11 @@ static int tegra30_pll_clk_enable(struct clk *c)
        return 0;
 }
 
-static void tegra30_pll_clk_disable(struct clk *c)
+static void tegra30_pll_clk_disable(struct clk_hw *hw)
 {
+       struct clk_tegra *c = to_clk_tegra(hw);
        u32 val;
-       pr_debug("%s on clock %s\n", __func__, c->name);
+       pr_debug("%s on clock %s\n", __func__, __clk_get_name(hw->clk));
 
        val = clk_readl(c->reg);
        val &= ~(PLL_BASE_BYPASS | PLL_BASE_ENABLE);
@@ -967,36 +1049,36 @@ static void tegra30_pll_clk_disable(struct clk *c)
        }
 }
 
-static int tegra30_pll_clk_set_rate(struct clk *c, unsigned long rate)
+static int tegra30_pll_clk_set_rate(struct clk_hw *hw, unsigned long rate,
+               unsigned long parent_rate)
 {
+       struct clk_tegra *c = to_clk_tegra(hw);
        u32 val, p_div, old_base;
        unsigned long input_rate;
        const struct clk_pll_freq_table *sel;
        struct clk_pll_freq_table cfg;
 
-       pr_debug("%s: %s %lu\n", __func__, c->name, rate);
-
        if (c->flags & PLL_FIXED) {
                int ret = 0;
                if (rate != c->u.pll.fixed_rate) {
                        pr_err("%s: Can not change %s fixed rate %lu to %lu\n",
-                              __func__, c->name, c->u.pll.fixed_rate, rate);
+                              __func__, __clk_get_name(hw->clk),
+                               c->u.pll.fixed_rate, rate);
                        ret = -EINVAL;
                }
                return ret;
        }
 
        if (c->flags & PLLM) {
-               if (rate != clk_get_rate_locked(c)) {
+               if (rate != __clk_get_rate(hw->clk)) {
                        pr_err("%s: Can not change memory %s rate in flight\n",
-                              __func__, c->name);
+                               __func__, __clk_get_name(hw->clk));
                        return -EINVAL;
                }
-               return 0;
        }
 
        p_div = 0;
-       input_rate = clk_get_rate(c->parent);
+       input_rate = parent_rate;
 
        /* Check if the target rate is tabulated */
        for (sel = c->u.pll.freq_table; sel->input_rate != 0; sel++) {
@@ -1054,7 +1136,7 @@ static int tegra30_pll_clk_set_rate(struct clk *c, unsigned long rate)
                    (p_div > (PLL_BASE_DIVP_MASK >> PLL_BASE_DIVP_SHIFT)) ||
                    (cfg.output_rate > c->u.pll.vco_max)) {
                        pr_err("%s: Failed to set %s out-of-table rate %lu\n",
-                              __func__, c->name, rate);
+                              __func__, __clk_get_name(hw->clk), rate);
                        return -EINVAL;
                }
                p_div <<= PLL_BASE_DIVP_SHIFT;
@@ -1072,7 +1154,7 @@ static int tegra30_pll_clk_set_rate(struct clk *c, unsigned long rate)
                return 0;
 
        if (c->state == ON) {
-               tegra30_pll_clk_disable(c);
+               tegra30_pll_clk_disable(hw);
                val &= ~(PLL_BASE_BYPASS | PLL_BASE_ENABLE);
        }
        clk_writel(val, c->reg + PLL_BASE);
@@ -1094,87 +1176,201 @@ static int tegra30_pll_clk_set_rate(struct clk *c, unsigned long rate)
        }
 
        if (c->state == ON)
-               tegra30_pll_clk_enable(c);
-
-       return 0;
-}
+               tegra30_pll_clk_enable(hw);
 
-struct clk_ops tegra30_pll_ops = {
-       .init                   = tegra30_pll_clk_init,
-       .enable                 = tegra30_pll_clk_enable,
-       .disable                = tegra30_pll_clk_disable,
-       .set_rate               = tegra30_pll_clk_set_rate,
-};
-
-static int
-tegra30_plld_clk_cfg_ex(struct clk *c, enum tegra_clk_ex_param p, u32 setting)
-{
-       u32 val, mask, reg;
-
-       switch (p) {
-       case TEGRA_CLK_PLLD_CSI_OUT_ENB:
-               mask = PLLD_BASE_CSI_CLKENABLE;
-               reg = c->reg + PLL_BASE;
-               break;
-       case TEGRA_CLK_PLLD_DSI_OUT_ENB:
-               mask = PLLD_MISC_DSI_CLKENABLE;
-               reg = c->reg + PLL_MISC(c);
-               break;
-       case TEGRA_CLK_PLLD_MIPI_MUX_SEL:
-               if (!(c->flags & PLL_ALT_MISC_REG)) {
-                       mask = PLLD_BASE_DSIB_MUX_MASK;
-                       reg = c->reg + PLL_BASE;
-                       break;
-               }
-       /* fall through - error since PLLD2 does not have MUX_SEL control */
-       default:
-               return -EINVAL;
-       }
+       c->u.pll.fixed_rate = rate;
 
-       val = clk_readl(reg);
-       if (setting)
-               val |= mask;
-       else
-               val &= ~mask;
-       clk_writel(val, reg);
        return 0;
 }
 
-struct clk_ops tegra_plld_ops = {
-       .init                   = tegra30_pll_clk_init,
-       .enable                 = tegra30_pll_clk_enable,
-       .disable                = tegra30_pll_clk_disable,
-       .set_rate               = tegra30_pll_clk_set_rate,
-       .clk_cfg_ex             = tegra30_plld_clk_cfg_ex,
-};
-
-static void tegra30_plle_clk_init(struct clk *c)
+static long tegra30_pll_round_rate(struct clk_hw *hw, unsigned long rate,
+                               unsigned long *prate)
 {
+       struct clk_tegra *c = to_clk_tegra(hw);
+       unsigned long input_rate = *prate;
+       unsigned long output_rate = *prate;
+       const struct clk_pll_freq_table *sel;
+       struct clk_pll_freq_table cfg;
+       int mul;
+       int div;
+       u32 p_div;
        u32 val;
 
-       val = clk_readl(PLLE_AUX);
-       c->parent = (val & PLLE_AUX_PLLP_SEL) ?
-               tegra_get_clock_by_name("pll_p") :
-               tegra_get_clock_by_name("pll_ref");
+       if (c->flags & PLL_FIXED)
+               return c->u.pll.fixed_rate;
 
-       val = clk_readl(c->reg + PLL_BASE);
-       c->state = (val & PLLE_BASE_ENABLE) ? ON : OFF;
-       c->mul = (val & PLLE_BASE_DIVN_MASK) >> PLLE_BASE_DIVN_SHIFT;
-       c->div = (val & PLLE_BASE_DIVM_MASK) >> PLLE_BASE_DIVM_SHIFT;
-       c->div *= (val & PLLE_BASE_DIVP_MASK) >> PLLE_BASE_DIVP_SHIFT;
-}
+       if (c->flags & PLLM)
+               return __clk_get_rate(hw->clk);
 
-static void tegra30_plle_clk_disable(struct clk *c)
-{
-       u32 val;
-       pr_debug("%s on clock %s\n", __func__, c->name);
+       p_div = 0;
+       /* Check if the target rate is tabulated */
+       for (sel = c->u.pll.freq_table; sel->input_rate != 0; sel++) {
+               if (sel->input_rate == input_rate && sel->output_rate == rate) {
+                       if (c->flags & PLLU) {
+                               BUG_ON(sel->p < 1 || sel->p > 2);
+                               if (sel->p == 1)
+                                       p_div = PLLU_BASE_POST_DIV;
+                       } else {
+                               BUG_ON(sel->p < 1);
+                               for (val = sel->p; val > 1; val >>= 1)
+                                       p_div++;
+                               p_div <<= PLL_BASE_DIVP_SHIFT;
+                       }
+                       break;
+               }
+       }
 
-       val = clk_readl(c->reg + PLL_BASE);
-       val &= ~(PLLE_BASE_CML_ENABLE | PLLE_BASE_ENABLE);
-       clk_writel(val, c->reg + PLL_BASE);
-}
+       if (sel->input_rate == 0) {
+               unsigned long cfreq;
+               BUG_ON(c->flags & PLLU);
+               sel = &cfg;
+
+               switch (input_rate) {
+               case 12000000:
+               case 26000000:
+                       cfreq = (rate <= 1000000 * 1000) ? 1000000 : 2000000;
+                       break;
+               case 13000000:
+                       cfreq = (rate <= 1000000 * 1000) ? 1000000 : 2600000;
+                       break;
+               case 16800000:
+               case 19200000:
+                       cfreq = (rate <= 1200000 * 1000) ? 1200000 : 2400000;
+                       break;
+               default:
+                       pr_err("%s: Unexpected reference rate %lu\n",
+                              __func__, input_rate);
+                       BUG();
+               }
+
+               /* Raise VCO to guarantee 0.5% accuracy */
+               for (cfg.output_rate = rate; cfg.output_rate < 200 * cfreq;
+                     cfg.output_rate <<= 1)
+                       p_div++;
+
+               cfg.p = 0x1 << p_div;
+               cfg.m = input_rate / cfreq;
+               cfg.n = cfg.output_rate / cfreq;
+       }
+
+       mul = sel->n;
+       div = sel->m * sel->p;
+
+       output_rate *= mul;
+       output_rate += div - 1; /* round up */
+       do_div(output_rate, div);
+
+       return output_rate;
+}
+
+static unsigned long tegra30_pll_recalc_rate(struct clk_hw *hw,
+               unsigned long parent_rate)
+{
+       struct clk_tegra *c = to_clk_tegra(hw);
+       u64 rate = parent_rate;
+       u32 val = clk_readl(c->reg + PLL_BASE);
+
+       if (c->flags & PLL_FIXED && !(val & PLL_BASE_OVERRIDE)) {
+               const struct clk_pll_freq_table *sel;
+               for (sel = c->u.pll.freq_table; sel->input_rate != 0; sel++) {
+                       if (sel->input_rate == parent_rate &&
+                               sel->output_rate == c->u.pll.fixed_rate) {
+                               c->mul = sel->n;
+                               c->div = sel->m * sel->p;
+                               break;
+                       }
+               }
+               pr_err("Clock %s has unknown fixed frequency\n",
+                                               __clk_get_name(hw->clk));
+               BUG();
+       } else if (val & PLL_BASE_BYPASS) {
+               c->mul = 1;
+               c->div = 1;
+       } else {
+               c->mul = (val & PLL_BASE_DIVN_MASK) >> PLL_BASE_DIVN_SHIFT;
+               c->div = (val & PLL_BASE_DIVM_MASK) >> PLL_BASE_DIVM_SHIFT;
+               if (c->flags & PLLU)
+                       c->div *= (val & PLLU_BASE_POST_DIV) ? 1 : 2;
+               else
+                       c->div *= (0x1 << ((val & PLL_BASE_DIVP_MASK) >>
+                                       PLL_BASE_DIVP_SHIFT));
+       }
+
+       if (c->mul != 0 && c->div != 0) {
+               rate *= c->mul;
+               rate += c->div - 1; /* round up */
+               do_div(rate, c->div);
+       }
+
+       return rate;
+}
+
+struct clk_ops tegra30_pll_ops = {
+       .is_enabled = tegra30_pll_clk_is_enabled,
+       .init = tegra30_pll_clk_init,
+       .enable = tegra30_pll_clk_enable,
+       .disable = tegra30_pll_clk_disable,
+       .recalc_rate = tegra30_pll_recalc_rate,
+       .round_rate = tegra30_pll_round_rate,
+       .set_rate = tegra30_pll_clk_set_rate,
+};
+
+int tegra30_plld_clk_cfg_ex(struct clk_hw *hw,
+                               enum tegra_clk_ex_param p, u32 setting)
+{
+       struct clk_tegra *c = to_clk_tegra(hw);
+       u32 val, mask, reg;
+
+       switch (p) {
+       case TEGRA_CLK_PLLD_CSI_OUT_ENB:
+               mask = PLLD_BASE_CSI_CLKENABLE;
+               reg = c->reg + PLL_BASE;
+               break;
+       case TEGRA_CLK_PLLD_DSI_OUT_ENB:
+               mask = PLLD_MISC_DSI_CLKENABLE;
+               reg = c->reg + PLL_MISC(c);
+               break;
+       case TEGRA_CLK_PLLD_MIPI_MUX_SEL:
+               if (!(c->flags & PLL_ALT_MISC_REG)) {
+                       mask = PLLD_BASE_DSIB_MUX_MASK;
+                       reg = c->reg + PLL_BASE;
+                       break;
+               }
+       /* fall through - error since PLLD2 does not have MUX_SEL control */
+       default:
+               return -EINVAL;
+       }
+
+       val = clk_readl(reg);
+       if (setting)
+               val |= mask;
+       else
+               val &= ~mask;
+       clk_writel(val, reg);
+       return 0;
+}
+
+static int tegra30_plle_clk_is_enabled(struct clk_hw *hw)
+{
+       struct clk_tegra *c = to_clk_tegra(hw);
+       u32 val;
+
+       val = clk_readl(c->reg + PLL_BASE);
+       c->state = (val & PLLE_BASE_ENABLE) ? ON : OFF;
+       return c->state;
+}
+
+static void tegra30_plle_clk_disable(struct clk_hw *hw)
+{
+       struct clk_tegra *c = to_clk_tegra(hw);
+       u32 val;
+
+       val = clk_readl(c->reg + PLL_BASE);
+       val &= ~(PLLE_BASE_CML_ENABLE | PLLE_BASE_ENABLE);
+       clk_writel(val, c->reg + PLL_BASE);
+}
 
-static void tegra30_plle_training(struct clk *c)
+static void tegra30_plle_training(struct clk_tegra *c)
 {
        u32 val;
 
@@ -1197,12 +1393,15 @@ static void tegra30_plle_training(struct clk *c)
        } while (!(val & PLLE_MISC_READY));
 }
 
-static int tegra30_plle_configure(struct clk *c, bool force_training)
+static int tegra30_plle_configure(struct clk_hw *hw, bool force_training)
 {
-       u32 val;
+       struct clk_tegra *c = to_clk_tegra(hw);
+       struct clk *parent = __clk_get_parent(hw->clk);
        const struct clk_pll_freq_table *sel;
+       u32 val;
+
        unsigned long rate = c->u.pll.fixed_rate;
-       unsigned long input_rate = clk_get_rate(c->parent);
+       unsigned long input_rate = __clk_get_rate(parent);
 
        for (sel = c->u.pll.freq_table; sel->input_rate != 0; sel++) {
                if (sel->input_rate == input_rate && sel->output_rate == rate)
@@ -1213,7 +1412,7 @@ static int tegra30_plle_configure(struct clk *c, bool force_training)
                return -ENOSYS;
 
        /* disable PLLE, clear setup fiels */
-       tegra30_plle_clk_disable(c);
+       tegra30_plle_clk_disable(hw);
 
        val = clk_readl(c->reg + PLL_MISC(c));
        val &= ~(PLLE_MISC_LOCK_ENABLE | PLLE_MISC_SETUP_MASK);
@@ -1251,52 +1450,64 @@ static int tegra30_plle_configure(struct clk *c, bool force_training)
        return 0;
 }
 
-static int tegra30_plle_clk_enable(struct clk *c)
+static int tegra30_plle_clk_enable(struct clk_hw *hw)
 {
-       pr_debug("%s on clock %s\n", __func__, c->name);
-       return tegra30_plle_configure(c, !c->set);
+       struct clk_tegra *c = to_clk_tegra(hw);
+
+       return tegra30_plle_configure(hw, !c->set);
+}
+
+static unsigned long tegra30_plle_clk_recalc_rate(struct clk_hw *hw,
+                       unsigned long parent_rate)
+{
+       struct clk_tegra *c = to_clk_tegra(hw);
+       unsigned long rate = parent_rate;
+       u32 val;
+
+       val = clk_readl(c->reg + PLL_BASE);
+       c->mul = (val & PLLE_BASE_DIVN_MASK) >> PLLE_BASE_DIVN_SHIFT;
+       c->div = (val & PLLE_BASE_DIVM_MASK) >> PLLE_BASE_DIVM_SHIFT;
+       c->div *= (val & PLLE_BASE_DIVP_MASK) >> PLLE_BASE_DIVP_SHIFT;
+
+       if (c->mul != 0 && c->div != 0) {
+               rate *= c->mul;
+               rate += c->div - 1; /* round up */
+               do_div(rate, c->div);
+       }
+       return rate;
 }
 
 struct clk_ops tegra30_plle_ops = {
-       .init                   = tegra30_plle_clk_init,
-       .enable                 = tegra30_plle_clk_enable,
-       .disable                = tegra30_plle_clk_disable,
+       .is_enabled = tegra30_plle_clk_is_enabled,
+       .enable = tegra30_plle_clk_enable,
+       .disable = tegra30_plle_clk_disable,
+       .recalc_rate = tegra30_plle_clk_recalc_rate,
 };
 
 /* Clock divider ops */
-static void tegra30_pll_div_clk_init(struct clk *c)
+static int tegra30_pll_div_clk_is_enabled(struct clk_hw *hw)
 {
+       struct clk_tegra *c = to_clk_tegra(hw);
+
        if (c->flags & DIV_U71) {
-               u32 divu71;
                u32 val = clk_readl(c->reg);
                val >>= c->reg_shift;
                c->state = (val & PLL_OUT_CLKEN) ? ON : OFF;
                if (!(val & PLL_OUT_RESET_DISABLE))
                        c->state = OFF;
-
-               divu71 = (val & PLL_OUT_RATIO_MASK) >> PLL_OUT_RATIO_SHIFT;
-               c->div = (divu71 + 2);
-               c->mul = 2;
-       } else if (c->flags & DIV_2) {
-               c->state = ON;
-               if (c->flags & (PLLD | PLLX)) {
-                       c->div = 2;
-                       c->mul = 1;
-               } else
-                       BUG();
        } else {
                c->state = ON;
-               c->div = 1;
-               c->mul = 1;
        }
+       return c->state;
 }
 
-static int tegra30_pll_div_clk_enable(struct clk *c)
+static int tegra30_pll_div_clk_enable(struct clk_hw *hw)
 {
+       struct clk_tegra *c = to_clk_tegra(hw);
        u32 val;
        u32 new_val;
 
-       pr_debug("%s: %s\n", __func__, c->name);
+       pr_debug("%s: %s\n", __func__, __clk_get_name(hw->clk));
        if (c->flags & DIV_U71) {
                val = clk_readl(c->reg);
                new_val = val >> c->reg_shift;
@@ -1314,12 +1525,13 @@ static int tegra30_pll_div_clk_enable(struct clk *c)
        return -EINVAL;
 }
 
-static void tegra30_pll_div_clk_disable(struct clk *c)
+static void tegra30_pll_div_clk_disable(struct clk_hw *hw)
 {
+       struct clk_tegra *c = to_clk_tegra(hw);
        u32 val;
        u32 new_val;
 
-       pr_debug("%s: %s\n", __func__, c->name);
+       pr_debug("%s: %s\n", __func__, __clk_get_name(hw->clk));
        if (c->flags & DIV_U71) {
                val = clk_readl(c->reg);
                new_val = val >> c->reg_shift;
@@ -1333,14 +1545,14 @@ static void tegra30_pll_div_clk_disable(struct clk *c)
        }
 }
 
-static int tegra30_pll_div_clk_set_rate(struct clk *c, unsigned long rate)
+static int tegra30_pll_div_clk_set_rate(struct clk_hw *hw, unsigned long rate,
+               unsigned long parent_rate)
 {
+       struct clk_tegra *c = to_clk_tegra(hw);
        u32 val;
        u32 new_val;
        int divider_u71;
-       unsigned long parent_rate = clk_get_rate(c->parent);
 
-       pr_debug("%s: %s %lu\n", __func__, c->name, rate);
        if (c->flags & DIV_U71) {
                divider_u71 = clk_div71_get_divider(
                        parent_rate, rate, c->flags, ROUND_DIVIDER_UP);
@@ -1358,19 +1570,59 @@ static int tegra30_pll_div_clk_set_rate(struct clk *c, unsigned long rate)
                        clk_writel_delay(val, c->reg);
                        c->div = divider_u71 + 2;
                        c->mul = 2;
+                       c->fixed_rate = rate;
                        return 0;
                }
-       } else if (c->flags & DIV_2)
-               return clk_set_rate(c->parent, rate * 2);
+       } else if (c->flags & DIV_2) {
+               c->fixed_rate = rate;
+               return 0;
+       }
 
        return -EINVAL;
 }
 
-static long tegra30_pll_div_clk_round_rate(struct clk *c, unsigned long rate)
+static unsigned long tegra30_pll_div_clk_recalc_rate(struct clk_hw *hw,
+               unsigned long parent_rate)
+{
+       struct clk_tegra *c = to_clk_tegra(hw);
+       u64 rate = parent_rate;
+
+       if (c->flags & DIV_U71) {
+               u32 divu71;
+               u32 val = clk_readl(c->reg);
+               val >>= c->reg_shift;
+
+               divu71 = (val & PLL_OUT_RATIO_MASK) >> PLL_OUT_RATIO_SHIFT;
+               c->div = (divu71 + 2);
+               c->mul = 2;
+       } else if (c->flags & DIV_2) {
+               if (c->flags & (PLLD | PLLX)) {
+                       c->div = 2;
+                       c->mul = 1;
+               } else
+                       BUG();
+       } else {
+               c->div = 1;
+               c->mul = 1;
+       }
+       if (c->mul != 0 && c->div != 0) {
+               rate *= c->mul;
+               rate += c->div - 1; /* round up */
+               do_div(rate, c->div);
+       }
+
+       return rate;
+}
+
+static long tegra30_pll_div_clk_round_rate(struct clk_hw *hw,
+                               unsigned long rate, unsigned long *prate)
 {
+       struct clk_tegra *c = to_clk_tegra(hw);
+       unsigned long parent_rate = __clk_get_rate(__clk_get_parent(hw->clk));
        int divider;
-       unsigned long parent_rate = clk_get_rate(c->parent);
-       pr_debug("%s: %s %lu\n", __func__, c->name, rate);
+
+       if (prate)
+               parent_rate = *prate;
 
        if (c->flags & DIV_U71) {
                divider = clk_div71_get_divider(
@@ -1378,23 +1630,25 @@ static long tegra30_pll_div_clk_round_rate(struct clk *c, unsigned long rate)
                if (divider < 0)
                        return divider;
                return DIV_ROUND_UP(parent_rate * 2, divider + 2);
-       } else if (c->flags & DIV_2)
-               /* no rounding - fixed DIV_2 dividers pass rate to parent PLL */
+       } else if (c->flags & DIV_2) {
+               *prate = rate * 2;
                return rate;
+       }
 
        return -EINVAL;
 }
 
 struct clk_ops tegra30_pll_div_ops = {
-       .init                   = tegra30_pll_div_clk_init,
-       .enable                 = tegra30_pll_div_clk_enable,
-       .disable                = tegra30_pll_div_clk_disable,
-       .set_rate               = tegra30_pll_div_clk_set_rate,
-       .round_rate             = tegra30_pll_div_clk_round_rate,
+       .is_enabled = tegra30_pll_div_clk_is_enabled,
+       .enable = tegra30_pll_div_clk_enable,
+       .disable = tegra30_pll_div_clk_disable,
+       .set_rate = tegra30_pll_div_clk_set_rate,
+       .recalc_rate = tegra30_pll_div_clk_recalc_rate,
+       .round_rate = tegra30_pll_div_clk_round_rate,
 };
 
 /* Periph clk ops */
-static inline u32 periph_clk_source_mask(struct clk *c)
+static inline u32 periph_clk_source_mask(struct clk_tegra *c)
 {
        if (c->flags & MUX8)
                return 7 << 29;
@@ -1408,7 +1662,7 @@ static inline u32 periph_clk_source_mask(struct clk *c)
                return 3 << 30;
 }
 
-static inline u32 periph_clk_source_shift(struct clk *c)
+static inline u32 periph_clk_source_shift(struct clk_tegra *c)
 {
        if (c->flags & MUX8)
                return 29;
@@ -1422,47 +1676,9 @@ static inline u32 periph_clk_source_shift(struct clk *c)
                return 30;
 }
 
-static void tegra30_periph_clk_init(struct clk *c)
+static int tegra30_periph_clk_is_enabled(struct clk_hw *hw)
 {
-       u32 val = clk_readl(c->reg);
-       const struct clk_mux_sel *mux = 0;
-       const struct clk_mux_sel *sel;
-       if (c->flags & MUX) {
-               for (sel = c->inputs; sel->input != NULL; sel++) {
-                       if (((val & periph_clk_source_mask(c)) >>
-                           periph_clk_source_shift(c)) == sel->value)
-                               mux = sel;
-               }
-               BUG_ON(!mux);
-
-               c->parent = mux->input;
-       } else {
-               c->parent = c->inputs[0].input;
-       }
-
-       if (c->flags & DIV_U71) {
-               u32 divu71 = val & PERIPH_CLK_SOURCE_DIVU71_MASK;
-               if ((c->flags & DIV_U71_UART) &&
-                   (!(val & PERIPH_CLK_UART_DIV_ENB))) {
-                       divu71 = 0;
-               }
-               if (c->flags & DIV_U71_IDLE) {
-                       val &= ~(PERIPH_CLK_SOURCE_DIVU71_MASK <<
-                               PERIPH_CLK_SOURCE_DIVIDLE_SHIFT);
-                       val |= (PERIPH_CLK_SOURCE_DIVIDLE_VAL <<
-                               PERIPH_CLK_SOURCE_DIVIDLE_SHIFT);
-                       clk_writel(val, c->reg);
-               }
-               c->div = divu71 + 2;
-               c->mul = 2;
-       } else if (c->flags & DIV_U16) {
-               u32 divu16 = val & PERIPH_CLK_SOURCE_DIVU16_MASK;
-               c->div = divu16 + 1;
-               c->mul = 1;
-       } else {
-               c->div = 1;
-               c->mul = 1;
-       }
+       struct clk_tegra *c = to_clk_tegra(hw);
 
        c->state = ON;
        if (!(clk_readl(PERIPH_CLK_TO_ENB_REG(c)) & PERIPH_CLK_TO_BIT(c)))
@@ -1470,11 +1686,12 @@ static void tegra30_periph_clk_init(struct clk *c)
        if (!(c->flags & PERIPH_NO_RESET))
                if (clk_readl(PERIPH_CLK_TO_RST_REG(c)) & PERIPH_CLK_TO_BIT(c))
                        c->state = OFF;
+       return c->state;
 }
 
-static int tegra30_periph_clk_enable(struct clk *c)
+static int tegra30_periph_clk_enable(struct clk_hw *hw)
 {
-       pr_debug("%s on clock %s\n", __func__, c->name);
+       struct clk_tegra *c = to_clk_tegra(hw);
 
        tegra_periph_clk_enable_refcount[c->u.periph.clk_num]++;
        if (tegra_periph_clk_enable_refcount[c->u.periph.clk_num] > 1)
@@ -1493,31 +1710,29 @@ static int tegra30_periph_clk_enable(struct clk *c)
        return 0;
 }
 
-static void tegra30_periph_clk_disable(struct clk *c)
+static void tegra30_periph_clk_disable(struct clk_hw *hw)
 {
+       struct clk_tegra *c = to_clk_tegra(hw);
        unsigned long val;
-       pr_debug("%s on clock %s\n", __func__, c->name);
 
-       if (c->refcnt)
-               tegra_periph_clk_enable_refcount[c->u.periph.clk_num]--;
+       tegra_periph_clk_enable_refcount[c->u.periph.clk_num]--;
 
-       if (tegra_periph_clk_enable_refcount[c->u.periph.clk_num] == 0) {
-               /* If peripheral is in the APB bus then read the APB bus to
-                * flush the write operation in apb bus. This will avoid the
-                * peripheral access after disabling clock*/
-               if (c->flags & PERIPH_ON_APB)
-                       val = chipid_readl();
+       if (tegra_periph_clk_enable_refcount[c->u.periph.clk_num] > 0)
+               return;
 
-               clk_writel_delay(
-                       PERIPH_CLK_TO_BIT(c), PERIPH_CLK_TO_ENB_CLR_REG(c));
-       }
+       /* If peripheral is in the APB bus then read the APB bus to
+        * flush the write operation in apb bus. This will avoid the
+        * peripheral access after disabling clock*/
+       if (c->flags & PERIPH_ON_APB)
+               val = chipid_readl();
+
+       clk_writel_delay(PERIPH_CLK_TO_BIT(c), PERIPH_CLK_TO_ENB_CLR_REG(c));
 }
 
-static void tegra30_periph_clk_reset(struct clk *c, bool assert)
+void tegra30_periph_clk_reset(struct clk_hw *hw, bool assert)
 {
+       struct clk_tegra *c = to_clk_tegra(hw);
        unsigned long val;
-       pr_debug("%s %s on clock %s\n", __func__,
-                assert ? "assert" : "deassert", c->name);
 
        if (!(c->flags & PERIPH_NO_RESET)) {
                if (assert) {
@@ -1536,42 +1751,40 @@ static void tegra30_periph_clk_reset(struct clk *c, bool assert)
        }
 }
 
-static int tegra30_periph_clk_set_parent(struct clk *c, struct clk *p)
+static int tegra30_periph_clk_set_parent(struct clk_hw *hw, u8 index)
 {
+       struct clk_tegra *c = to_clk_tegra(hw);
        u32 val;
-       const struct clk_mux_sel *sel;
-       pr_debug("%s: %s %s\n", __func__, c->name, p->name);
 
        if (!(c->flags & MUX))
-               return (p == c->parent) ? 0 : (-EINVAL);
-
-       for (sel = c->inputs; sel->input != NULL; sel++) {
-               if (sel->input == p) {
-                       val = clk_readl(c->reg);
-                       val &= ~periph_clk_source_mask(c);
-                       val |= (sel->value << periph_clk_source_shift(c));
-
-                       if (c->refcnt)
-                               clk_enable(p);
+               return (index == 0) ? 0 : (-EINVAL);
 
-                       clk_writel_delay(val, c->reg);
+       val = clk_readl(c->reg);
+       val &= ~periph_clk_source_mask(c);
+       val |= (index << periph_clk_source_shift(c));
+       clk_writel_delay(val, c->reg);
+       return 0;
+}
 
-                       if (c->refcnt && c->parent)
-                               clk_disable(c->parent);
+static u8 tegra30_periph_clk_get_parent(struct clk_hw *hw)
+{
+       struct clk_tegra *c = to_clk_tegra(hw);
+       u32 val = clk_readl(c->reg);
+       int source  = (val & periph_clk_source_mask(c)) >>
+                                       periph_clk_source_shift(c);
 
-                       clk_reparent(c, p);
-                       return 0;
-               }
-       }
+       if (!(c->flags & MUX))
+               return 0;
 
-       return -EINVAL;
+       return source;
 }
 
-static int tegra30_periph_clk_set_rate(struct clk *c, unsigned long rate)
+static int tegra30_periph_clk_set_rate(struct clk_hw *hw, unsigned long rate,
+               unsigned long parent_rate)
 {
+       struct clk_tegra *c = to_clk_tegra(hw);
        u32 val;
        int divider;
-       unsigned long parent_rate = clk_get_rate(c->parent);
 
        if (c->flags & DIV_U71) {
                divider = clk_div71_get_divider(
@@ -1610,12 +1823,15 @@ static int tegra30_periph_clk_set_rate(struct clk *c, unsigned long rate)
        return -EINVAL;
 }
 
-static long tegra30_periph_clk_round_rate(struct clk *c,
-       unsigned long rate)
+static long tegra30_periph_clk_round_rate(struct clk_hw *hw, unsigned long rate,
+                               unsigned long *prate)
 {
+       struct clk_tegra *c = to_clk_tegra(hw);
+       unsigned long parent_rate = __clk_get_rate(__clk_get_parent(hw->clk));
        int divider;
-       unsigned long parent_rate = clk_get_rate(c->parent);
-       pr_debug("%s: %s %lu\n", __func__, c->name, rate);
+
+       if (prate)
+               parent_rate = *prate;
 
        if (c->flags & DIV_U71) {
                divider = clk_div71_get_divider(
@@ -1633,20 +1849,85 @@ static long tegra30_periph_clk_round_rate(struct clk *c,
        return -EINVAL;
 }
 
+static unsigned long tegra30_periph_clk_recalc_rate(struct clk_hw *hw,
+               unsigned long parent_rate)
+{
+       struct clk_tegra *c = to_clk_tegra(hw);
+       u64 rate = parent_rate;
+       u32 val = clk_readl(c->reg);
+
+       if (c->flags & DIV_U71) {
+               u32 divu71 = val & PERIPH_CLK_SOURCE_DIVU71_MASK;
+               if ((c->flags & DIV_U71_UART) &&
+                   (!(val & PERIPH_CLK_UART_DIV_ENB))) {
+                       divu71 = 0;
+               }
+               if (c->flags & DIV_U71_IDLE) {
+                       val &= ~(PERIPH_CLK_SOURCE_DIVU71_MASK <<
+                               PERIPH_CLK_SOURCE_DIVIDLE_SHIFT);
+                       val |= (PERIPH_CLK_SOURCE_DIVIDLE_VAL <<
+                               PERIPH_CLK_SOURCE_DIVIDLE_SHIFT);
+                       clk_writel(val, c->reg);
+               }
+               c->div = divu71 + 2;
+               c->mul = 2;
+       } else if (c->flags & DIV_U16) {
+               u32 divu16 = val & PERIPH_CLK_SOURCE_DIVU16_MASK;
+               c->div = divu16 + 1;
+               c->mul = 1;
+       } else {
+               c->div = 1;
+               c->mul = 1;
+       }
+
+       if (c->mul != 0 && c->div != 0) {
+               rate *= c->mul;
+               rate += c->div - 1; /* round up */
+               do_div(rate, c->div);
+       }
+       return rate;
+}
+
 struct clk_ops tegra30_periph_clk_ops = {
-       .init                   = &tegra30_periph_clk_init,
+       .is_enabled = tegra30_periph_clk_is_enabled,
+       .enable = tegra30_periph_clk_enable,
+       .disable = tegra30_periph_clk_disable,
+       .set_parent = tegra30_periph_clk_set_parent,
+       .get_parent = tegra30_periph_clk_get_parent,
+       .set_rate = tegra30_periph_clk_set_rate,
+       .round_rate = tegra30_periph_clk_round_rate,
+       .recalc_rate = tegra30_periph_clk_recalc_rate,
+};
+
+static int tegra30_dsib_clk_set_parent(struct clk_hw *hw, u8 index)
+{
+       struct clk *d = clk_get_sys(NULL, "pll_d");
+       /* The DSIB parent selection bit is in PLLD base
+          register - can not do direct r-m-w, must be
+          protected by PLLD lock */
+       tegra_clk_cfg_ex(
+               d, TEGRA_CLK_PLLD_MIPI_MUX_SEL, index);
+
+       return 0;
+}
+
+struct clk_ops tegra30_dsib_clk_ops = {
+       .is_enabled = tegra30_periph_clk_is_enabled,
        .enable                 = &tegra30_periph_clk_enable,
        .disable                = &tegra30_periph_clk_disable,
-       .set_parent             = &tegra30_periph_clk_set_parent,
+       .set_parent             = &tegra30_dsib_clk_set_parent,
+       .get_parent             = &tegra30_periph_clk_get_parent,
        .set_rate               = &tegra30_periph_clk_set_rate,
        .round_rate             = &tegra30_periph_clk_round_rate,
-       .reset                  = &tegra30_periph_clk_reset,
+       .recalc_rate            = &tegra30_periph_clk_recalc_rate,
 };
 
 /* Periph extended clock configuration ops */
-static int
-tegra30_vi_clk_cfg_ex(struct clk *c, enum tegra_clk_ex_param p, u32 setting)
+int tegra30_vi_clk_cfg_ex(struct clk_hw *hw,
+                               enum tegra_clk_ex_param p, u32 setting)
 {
+       struct clk_tegra *c = to_clk_tegra(hw);
+
        if (p == TEGRA_CLK_VI_INP_SEL) {
                u32 val = clk_readl(c->reg);
                val &= ~PERIPH_CLK_VI_SEL_EX_MASK;
@@ -1658,20 +1939,11 @@ tegra30_vi_clk_cfg_ex(struct clk *c, enum tegra_clk_ex_param p, u32 setting)
        return -EINVAL;
 }
 
-struct clk_ops tegra_vi_clk_ops = {
-       .init                   = &tegra30_periph_clk_init,
-       .enable                 = &tegra30_periph_clk_enable,
-       .disable                = &tegra30_periph_clk_disable,
-       .set_parent             = &tegra30_periph_clk_set_parent,
-       .set_rate               = &tegra30_periph_clk_set_rate,
-       .round_rate             = &tegra30_periph_clk_round_rate,
-       .clk_cfg_ex             = &tegra30_vi_clk_cfg_ex,
-       .reset                  = &tegra30_periph_clk_reset,
-};
-
-static int
-tegra30_nand_clk_cfg_ex(struct clk *c, enum tegra_clk_ex_param p, u32 setting)
+int tegra30_nand_clk_cfg_ex(struct clk_hw *hw,
+                               enum tegra_clk_ex_param p, u32 setting)
 {
+       struct clk_tegra *c = to_clk_tegra(hw);
+
        if (p == TEGRA_CLK_NAND_PAD_DIV2_ENB) {
                u32 val = clk_readl(c->reg);
                if (setting)
@@ -1684,20 +1956,11 @@ tegra30_nand_clk_cfg_ex(struct clk *c, enum tegra_clk_ex_param p, u32 setting)
        return -EINVAL;
 }
 
-struct clk_ops tegra_nand_clk_ops = {
-       .init                   = &tegra30_periph_clk_init,
-       .enable                 = &tegra30_periph_clk_enable,
-       .disable                = &tegra30_periph_clk_disable,
-       .set_parent             = &tegra30_periph_clk_set_parent,
-       .set_rate               = &tegra30_periph_clk_set_rate,
-       .round_rate             = &tegra30_periph_clk_round_rate,
-       .clk_cfg_ex             = &tegra30_nand_clk_cfg_ex,
-       .reset                  = &tegra30_periph_clk_reset,
-};
-
-static int
-tegra30_dtv_clk_cfg_ex(struct clk *c, enum tegra_clk_ex_param p, u32 setting)
+int tegra30_dtv_clk_cfg_ex(struct clk_hw *hw,
+                               enum tegra_clk_ex_param p, u32 setting)
 {
+       struct clk_tegra *c = to_clk_tegra(hw);
+
        if (p == TEGRA_CLK_DTV_INVERT) {
                u32 val = clk_readl(c->reg);
                if (setting)
@@ -1710,91 +1973,27 @@ tegra30_dtv_clk_cfg_ex(struct clk *c, enum tegra_clk_ex_param p, u32 setting)
        return -EINVAL;
 }
 
-struct clk_ops tegra_dtv_clk_ops = {
-       .init                   = &tegra30_periph_clk_init,
-       .enable                 = &tegra30_periph_clk_enable,
-       .disable                = &tegra30_periph_clk_disable,
-       .set_parent             = &tegra30_periph_clk_set_parent,
-       .set_rate               = &tegra30_periph_clk_set_rate,
-       .round_rate             = &tegra30_periph_clk_round_rate,
-       .clk_cfg_ex             = &tegra30_dtv_clk_cfg_ex,
-       .reset                  = &tegra30_periph_clk_reset,
-};
-
-static int tegra30_dsib_clk_set_parent(struct clk *c, struct clk *p)
-{
-       const struct clk_mux_sel *sel;
-       struct clk *d = tegra_get_clock_by_name("pll_d");
-
-       pr_debug("%s: %s %s\n", __func__, c->name, p->name);
-
-       for (sel = c->inputs; sel->input != NULL; sel++) {
-               if (sel->input == p) {
-                       if (c->refcnt)
-                               clk_enable(p);
-
-                       /* The DSIB parent selection bit is in PLLD base
-                          register - can not do direct r-m-w, must be
-                          protected by PLLD lock */
-                       tegra_clk_cfg_ex(
-                               d, TEGRA_CLK_PLLD_MIPI_MUX_SEL, sel->value);
-
-                       if (c->refcnt && c->parent)
-                               clk_disable(c->parent);
-
-                       clk_reparent(c, p);
-                       return 0;
-               }
-       }
-
-       return -EINVAL;
-}
-
-struct clk_ops tegra_dsib_clk_ops = {
-       .init                   = &tegra30_periph_clk_init,
-       .enable                 = &tegra30_periph_clk_enable,
-       .disable                = &tegra30_periph_clk_disable,
-       .set_parent             = &tegra30_dsib_clk_set_parent,
-       .set_rate               = &tegra30_periph_clk_set_rate,
-       .round_rate             = &tegra30_periph_clk_round_rate,
-       .reset                  = &tegra30_periph_clk_reset,
-};
-
-/* pciex clock support only reset function */
-struct clk_ops tegra_pciex_clk_ops = {
-       .reset    = tegra30_periph_clk_reset,
-};
-
 /* Output clock ops */
 
 static DEFINE_SPINLOCK(clk_out_lock);
 
-static void tegra30_clk_out_init(struct clk *c)
+static int tegra30_clk_out_is_enabled(struct clk_hw *hw)
 {
-       const struct clk_mux_sel *mux = 0;
-       const struct clk_mux_sel *sel;
+       struct clk_tegra *c = to_clk_tegra(hw);
        u32 val = pmc_readl(c->reg);
 
        c->state = (val & (0x1 << c->u.periph.clk_num)) ? ON : OFF;
        c->mul = 1;
        c->div = 1;
-
-       for (sel = c->inputs; sel->input != NULL; sel++) {
-               if (((val & periph_clk_source_mask(c)) >>
-                    periph_clk_source_shift(c)) == sel->value)
-                       mux = sel;
-       }
-       BUG_ON(!mux);
-       c->parent = mux->input;
+       return c->state;
 }
 
-static int tegra30_clk_out_enable(struct clk *c)
+static int tegra30_clk_out_enable(struct clk_hw *hw)
 {
+       struct clk_tegra *c = to_clk_tegra(hw);
        u32 val;
        unsigned long flags;
 
-       pr_debug("%s on clock %s\n", __func__, c->name);
-
        spin_lock_irqsave(&clk_out_lock, flags);
        val = pmc_readl(c->reg);
        val |= (0x1 << c->u.periph.clk_num);
@@ -1804,13 +2003,12 @@ static int tegra30_clk_out_enable(struct clk *c)
        return 0;
 }
 
-static void tegra30_clk_out_disable(struct clk *c)
+static void tegra30_clk_out_disable(struct clk_hw *hw)
 {
+       struct clk_tegra *c = to_clk_tegra(hw);
        u32 val;
        unsigned long flags;
 
-       pr_debug("%s on clock %s\n", __func__, c->name);
-
        spin_lock_irqsave(&clk_out_lock, flags);
        val = pmc_readl(c->reg);
        val &= ~(0x1 << c->u.periph.clk_num);
@@ -1818,58 +2016,59 @@ static void tegra30_clk_out_disable(struct clk *c)
        spin_unlock_irqrestore(&clk_out_lock, flags);
 }
 
-static int tegra30_clk_out_set_parent(struct clk *c, struct clk *p)
+static int tegra30_clk_out_set_parent(struct clk_hw *hw, u8 index)
 {
+       struct clk_tegra *c = to_clk_tegra(hw);
        u32 val;
        unsigned long flags;
-       const struct clk_mux_sel *sel;
 
-       pr_debug("%s: %s %s\n", __func__, c->name, p->name);
-
-       for (sel = c->inputs; sel->input != NULL; sel++) {
-               if (sel->input == p) {
-                       if (c->refcnt)
-                               clk_enable(p);
+       spin_lock_irqsave(&clk_out_lock, flags);
+       val = pmc_readl(c->reg);
+       val &= ~periph_clk_source_mask(c);
+       val |= (index << periph_clk_source_shift(c));
+       pmc_writel(val, c->reg);
+       spin_unlock_irqrestore(&clk_out_lock, flags);
 
-                       spin_lock_irqsave(&clk_out_lock, flags);
-                       val = pmc_readl(c->reg);
-                       val &= ~periph_clk_source_mask(c);
-                       val |= (sel->value << periph_clk_source_shift(c));
-                       pmc_writel(val, c->reg);
-                       spin_unlock_irqrestore(&clk_out_lock, flags);
+       return 0;
+}
 
-                       if (c->refcnt && c->parent)
-                               clk_disable(c->parent);
+static u8 tegra30_clk_out_get_parent(struct clk_hw *hw)
+{
+       struct clk_tegra *c = to_clk_tegra(hw);
+       u32 val = pmc_readl(c->reg);
+       int source;
 
-                       clk_reparent(c, p);
-                       return 0;
-               }
-       }
-       return -EINVAL;
+       source = (val & periph_clk_source_mask(c)) >>
+                               periph_clk_source_shift(c);
+       return source;
 }
 
 struct clk_ops tegra_clk_out_ops = {
-       .init                   = &tegra30_clk_out_init,
-       .enable                 = &tegra30_clk_out_enable,
-       .disable                = &tegra30_clk_out_disable,
-       .set_parent             = &tegra30_clk_out_set_parent,
+       .is_enabled = tegra30_clk_out_is_enabled,
+       .enable = tegra30_clk_out_enable,
+       .disable = tegra30_clk_out_disable,
+       .set_parent = tegra30_clk_out_set_parent,
+       .get_parent = tegra30_clk_out_get_parent,
+       .recalc_rate = tegra30_clk_fixed_recalc_rate,
 };
 
 /* Clock doubler ops */
-static void tegra30_clk_double_init(struct clk *c)
+static int tegra30_clk_double_is_enabled(struct clk_hw *hw)
 {
-       u32 val = clk_readl(c->reg);
-       c->mul = val & (0x1 << c->reg_shift) ? 1 : 2;
-       c->div = 1;
+       struct clk_tegra *c = to_clk_tegra(hw);
+
        c->state = ON;
        if (!(clk_readl(PERIPH_CLK_TO_ENB_REG(c)) & PERIPH_CLK_TO_BIT(c)))
                c->state = OFF;
+       return c->state;
 };
 
-static int tegra30_clk_double_set_rate(struct clk *c, unsigned long rate)
+static int tegra30_clk_double_set_rate(struct clk_hw *hw, unsigned long rate,
+               unsigned long parent_rate)
 {
+       struct clk_tegra *c = to_clk_tegra(hw);
        u32 val;
-       unsigned long parent_rate = clk_get_rate(c->parent);
+
        if (rate == parent_rate) {
                val = clk_readl(c->reg) | (0x1 << c->reg_shift);
                clk_writel(val, c->reg);
@@ -1886,108 +2085,139 @@ static int tegra30_clk_double_set_rate(struct clk *c, unsigned long rate)
        return -EINVAL;
 }
 
-struct clk_ops tegra30_clk_double_ops = {
-       .init                   = &tegra30_clk_double_init,
-       .enable                 = &tegra30_periph_clk_enable,
-       .disable                = &tegra30_periph_clk_disable,
-       .set_rate               = &tegra30_clk_double_set_rate,
-};
+static unsigned long tegra30_clk_double_recalc_rate(struct clk_hw *hw,
+               unsigned long parent_rate)
+{
+       struct clk_tegra *c = to_clk_tegra(hw);
+       u64 rate = parent_rate;
 
-/* Audio sync clock ops */
-static int tegra30_sync_source_set_rate(struct clk *c, unsigned long rate)
+       u32 val = clk_readl(c->reg);
+       c->mul = val & (0x1 << c->reg_shift) ? 1 : 2;
+       c->div = 1;
+
+       if (c->mul != 0 && c->div != 0) {
+               rate *= c->mul;
+               rate += c->div - 1; /* round up */
+               do_div(rate, c->div);
+       }
+
+       return rate;
+}
+
+static long tegra30_clk_double_round_rate(struct clk_hw *hw, unsigned long rate,
+                               unsigned long *prate)
 {
-       c->rate = rate;
-       return 0;
+       unsigned long output_rate = *prate;
+
+       do_div(output_rate, 2);
+       return output_rate;
 }
 
+struct clk_ops tegra30_clk_double_ops = {
+       .is_enabled = tegra30_clk_double_is_enabled,
+       .enable = tegra30_periph_clk_enable,
+       .disable = tegra30_periph_clk_disable,
+       .recalc_rate = tegra30_clk_double_recalc_rate,
+       .round_rate = tegra30_clk_double_round_rate,
+       .set_rate = tegra30_clk_double_set_rate,
+};
+
+/* Audio sync clock ops */
 struct clk_ops tegra_sync_source_ops = {
-       .set_rate               = &tegra30_sync_source_set_rate,
+       .recalc_rate = tegra30_clk_fixed_recalc_rate,
 };
 
-static void tegra30_audio_sync_clk_init(struct clk *c)
+static int tegra30_audio_sync_clk_is_enabled(struct clk_hw *hw)
 {
-       int source;
-       const struct clk_mux_sel *sel;
+       struct clk_tegra *c = to_clk_tegra(hw);
        u32 val = clk_readl(c->reg);
        c->state = (val & AUDIO_SYNC_DISABLE_BIT) ? OFF : ON;
-       source = val & AUDIO_SYNC_SOURCE_MASK;
-       for (sel = c->inputs; sel->input != NULL; sel++)
-               if (sel->value == source)
-                       break;
-       BUG_ON(sel->input == NULL);
-       c->parent = sel->input;
+       return c->state;
 }
 
-static int tegra30_audio_sync_clk_enable(struct clk *c)
+static int tegra30_audio_sync_clk_enable(struct clk_hw *hw)
 {
+       struct clk_tegra *c = to_clk_tegra(hw);
        u32 val = clk_readl(c->reg);
        clk_writel((val & (~AUDIO_SYNC_DISABLE_BIT)), c->reg);
        return 0;
 }
 
-static void tegra30_audio_sync_clk_disable(struct clk *c)
+static void tegra30_audio_sync_clk_disable(struct clk_hw *hw)
 {
+       struct clk_tegra *c = to_clk_tegra(hw);
        u32 val = clk_readl(c->reg);
        clk_writel((val | AUDIO_SYNC_DISABLE_BIT), c->reg);
 }
 
-static int tegra30_audio_sync_clk_set_parent(struct clk *c, struct clk *p)
+static int tegra30_audio_sync_clk_set_parent(struct clk_hw *hw, u8 index)
 {
+       struct clk_tegra *c = to_clk_tegra(hw);
        u32 val;
-       const struct clk_mux_sel *sel;
-       for (sel = c->inputs; sel->input != NULL; sel++) {
-               if (sel->input == p) {
-                       val = clk_readl(c->reg);
-                       val &= ~AUDIO_SYNC_SOURCE_MASK;
-                       val |= sel->value;
 
-                       if (c->refcnt)
-                               clk_enable(p);
-
-                       clk_writel(val, c->reg);
+       val = clk_readl(c->reg);
+       val &= ~AUDIO_SYNC_SOURCE_MASK;
+       val |= index;
 
-                       if (c->refcnt && c->parent)
-                               clk_disable(c->parent);
+       clk_writel(val, c->reg);
+       return 0;
+}
 
-                       clk_reparent(c, p);
-                       return 0;
-               }
-       }
+static u8 tegra30_audio_sync_clk_get_parent(struct clk_hw *hw)
+{
+       struct clk_tegra *c = to_clk_tegra(hw);
+       u32 val = clk_readl(c->reg);
+       int source;
 
-       return -EINVAL;
+       source = val & AUDIO_SYNC_SOURCE_MASK;
+       return source;
 }
 
 struct clk_ops tegra30_audio_sync_clk_ops = {
-       .init       = tegra30_audio_sync_clk_init,
-       .enable     = tegra30_audio_sync_clk_enable,
-       .disable    = tegra30_audio_sync_clk_disable,
+       .is_enabled = tegra30_audio_sync_clk_is_enabled,
+       .enable = tegra30_audio_sync_clk_enable,
+       .disable = tegra30_audio_sync_clk_disable,
        .set_parent = tegra30_audio_sync_clk_set_parent,
+       .get_parent = tegra30_audio_sync_clk_get_parent,
+       .recalc_rate = tegra30_clk_fixed_recalc_rate,
 };
 
 /* cml0 (pcie), and cml1 (sata) clock ops */
-static void tegra30_cml_clk_init(struct clk *c)
+static int tegra30_cml_clk_is_enabled(struct clk_hw *hw)
 {
+       struct clk_tegra *c = to_clk_tegra(hw);
        u32 val = clk_readl(c->reg);
        c->state = val & (0x1 << c->u.periph.clk_num) ? ON : OFF;
+       return c->state;
 }
 
-static int tegra30_cml_clk_enable(struct clk *c)
+static int tegra30_cml_clk_enable(struct clk_hw *hw)
 {
+       struct clk_tegra *c = to_clk_tegra(hw);
+
        u32 val = clk_readl(c->reg);
        val |= (0x1 << c->u.periph.clk_num);
        clk_writel(val, c->reg);
+
        return 0;
 }
 
-static void tegra30_cml_clk_disable(struct clk *c)
+static void tegra30_cml_clk_disable(struct clk_hw *hw)
 {
+       struct clk_tegra *c = to_clk_tegra(hw);
+
        u32 val = clk_readl(c->reg);
        val &= ~(0x1 << c->u.periph.clk_num);
        clk_writel(val, c->reg);
 }
 
 struct clk_ops tegra_cml_clk_ops = {
-       .init                   = &tegra30_cml_clk_init,
-       .enable                 = &tegra30_cml_clk_enable,
-       .disable                = &tegra30_cml_clk_disable,
+       .is_enabled = tegra30_cml_clk_is_enabled,
+       .enable = tegra30_cml_clk_enable,
+       .disable = tegra30_cml_clk_disable,
+       .recalc_rate = tegra30_clk_fixed_recalc_rate,
+};
+
+struct clk_ops tegra_pciex_clk_ops = {
+       .recalc_rate = tegra30_clk_fixed_recalc_rate,
 };
This page took 0.04862 seconds and 5 git commands to generate.