clk: rockchip: implement the fraction divider branch type
authorHeiko Stübner <heiko@sntech.de>
Tue, 26 Aug 2014 22:54:21 +0000 (00:54 +0200)
committerMike Turquette <mturquette@linaro.org>
Tue, 2 Sep 2014 22:03:17 +0000 (15:03 -0700)
Rockchip SoCs may provide fraction dividers for some clocks, mostly for
i2s and uarts. In contrast to the other registers, these do not use
the hiword-mask paradigm, but instead split the register into the upper
16 bit for the nominator and the lower 16 bit for the denominator.

The common clock framework got a generic fractional divider clock type
recently that can accomodate this setting easily. All currently known
fraction dividers have a separate gate too, therefore implement the
divider as composite using the ops-struct from fractional_divider clock
and add the gate if necessary.

Signed-off-by: Heiko Stuebner <heiko@sntech.de>
Signed-off-by: Mike Turquette <mturquette@linaro.org>
drivers/clk/rockchip/clk.c

index 278cf9dd1e237ecb3bb56048f321bb2ff6970549..5db1ecf95eca069f735e5e26314dd651c19b6197 100644 (file)
@@ -103,6 +103,54 @@ struct clk *rockchip_clk_register_branch(const char *name,
        return clk;
 }
 
+static struct clk *rockchip_clk_register_frac_branch(const char *name,
+               const char **parent_names, u8 num_parents, void __iomem *base,
+               int muxdiv_offset, u8 div_flags,
+               int gate_offset, u8 gate_shift, u8 gate_flags,
+               unsigned long flags, spinlock_t *lock)
+{
+       struct clk *clk;
+       struct clk_gate *gate = NULL;
+       struct clk_fractional_divider *div = NULL;
+       const struct clk_ops *div_ops = NULL, *gate_ops = NULL;
+
+       if (gate_offset >= 0) {
+               gate = kzalloc(sizeof(*gate), GFP_KERNEL);
+               if (!gate)
+                       return ERR_PTR(-ENOMEM);
+
+               gate->flags = gate_flags;
+               gate->reg = base + gate_offset;
+               gate->bit_idx = gate_shift;
+               gate->lock = lock;
+               gate_ops = &clk_gate_ops;
+       }
+
+       if (muxdiv_offset < 0)
+               return ERR_PTR(-EINVAL);
+
+       div = kzalloc(sizeof(*div), GFP_KERNEL);
+       if (!div)
+               return ERR_PTR(-ENOMEM);
+
+       div->flags = div_flags;
+       div->reg = base + muxdiv_offset;
+       div->mshift = 16;
+       div->mmask = 0xffff0000;
+       div->nshift = 0;
+       div->nmask = 0xffff;
+       div->lock = lock;
+       div_ops = &clk_fractional_divider_ops;
+
+       clk = clk_register_composite(NULL, name, parent_names, num_parents,
+                                    NULL, NULL,
+                                    &div->hw, div_ops,
+                                    gate ? &gate->hw : NULL, gate_ops,
+                                    flags);
+
+       return clk;
+}
+
 static DEFINE_SPINLOCK(clk_lock);
 static struct clk **clk_table;
 static void __iomem *reg_base;
@@ -197,8 +245,14 @@ void __init rockchip_clk_register_branches(
                                        list->div_flags, &clk_lock);
                        break;
                case branch_fraction_divider:
-                       /* unimplemented */
-                       continue;
+                       /* keep all gates untouched for now */
+                       flags |= CLK_IGNORE_UNUSED;
+
+                       clk = rockchip_clk_register_frac_branch(list->name,
+                               list->parent_names, list->num_parents,
+                               reg_base, list->muxdiv_offset, list->div_flags,
+                               list->gate_offset, list->gate_shift,
+                               list->gate_flags, flags, &clk_lock);
                        break;
                case branch_gate:
                        flags |= CLK_SET_RATE_PARENT;