clk: sunxi: factors: Support custom formulas
authorChen-Yu Tsai <wens@csie.org>
Mon, 25 Jan 2016 13:15:43 +0000 (21:15 +0800)
committerMaxime Ripard <maxime.ripard@free-electrons.com>
Fri, 29 Jan 2016 10:30:27 +0000 (11:30 +0100)
Some clocks cannot be modelled using the standard factors clk formula,
such as clocks with special pre-dividers on one parent, or clocks
with all power-of-two dividers.

Add support for a custom .recalc callback for factors clk. Also pass
the current parent index to the .get_factor and .recalc callbacks.

Signed-off-by: Chen-Yu Tsai <wens@csie.org>
Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com>
drivers/clk/sunxi/clk-factors.c
drivers/clk/sunxi/clk-factors.h

index 4a8e36a..2927b09 100644 (file)
@@ -63,6 +63,26 @@ static unsigned long clk_factors_recalc_rate(struct clk_hw *hw,
        if (config->pwidth != SUNXI_FACTORS_NOT_APPLICABLE)
                p = FACTOR_GET(config->pshift, config->pwidth, reg);
 
+       if (factors->recalc) {
+               struct factors_request factors_req = {
+                       .parent_rate = parent_rate,
+                       .n = n,
+                       .k = k,
+                       .m = m,
+                       .p = p,
+               };
+
+               /* get mux details from mux clk structure */
+               if (factors->mux)
+                       factors_req.parent_index =
+                               (reg >> factors->mux->shift) &
+                               factors->mux->mask;
+
+               factors->recalc(&factors_req);
+
+               return factors_req.rate;
+       }
+
        /* Calculate the rate */
        rate = (parent_rate * (n + config->n_start) * (k + 1) >> p) / (m + 1);
 
@@ -87,6 +107,7 @@ static long clk_factors_round_rate(struct clk_hw *hw, unsigned long rate,
 static int clk_factors_determine_rate(struct clk_hw *hw,
                                      struct clk_rate_request *req)
 {
+       struct clk_factors *factors = to_clk_factors(hw);
        struct clk_hw *parent, *best_parent = NULL;
        int i, num_parents;
        unsigned long parent_rate, best = 0, child_rate, best_child_rate = 0;
@@ -94,6 +115,10 @@ static int clk_factors_determine_rate(struct clk_hw *hw,
        /* find the parent that can help provide the fastest rate <= rate */
        num_parents = clk_hw_get_num_parents(hw);
        for (i = 0; i < num_parents; i++) {
+               struct factors_request factors_req = {
+                       .rate = req->rate,
+                       .parent_index = i,
+               };
                parent = clk_hw_get_parent_by_index(hw, i);
                if (!parent)
                        continue;
@@ -102,8 +127,9 @@ static int clk_factors_determine_rate(struct clk_hw *hw,
                else
                        parent_rate = clk_hw_get_rate(parent);
 
-               child_rate = clk_factors_round_rate(hw, req->rate,
-                                                   &parent_rate);
+               factors_req.parent_rate = parent_rate;
+               factors->get_factors(&factors_req);
+               child_rate = factors_req.rate;
 
                if (child_rate <= req->rate && child_rate > best_child_rate) {
                        best_parent = parent;
@@ -202,6 +228,7 @@ struct clk *sunxi_factors_register(struct device_node *node,
        factors->reg = reg;
        factors->config = data->table;
        factors->get_factors = data->getter;
+       factors->recalc = data->recalc;
        factors->lock = lock;
 
        /* Add a gate if this factor clock can be gated */
index f09d7c2..a44a865 100644 (file)
@@ -22,6 +22,7 @@ struct clk_factors_config {
 struct factors_request {
        unsigned long rate;
        unsigned long parent_rate;
+       u8 parent_index;
        u8 n;
        u8 k;
        u8 m;
@@ -34,6 +35,7 @@ struct factors_data {
        int muxmask;
        const struct clk_factors_config *table;
        void (*getter)(struct factors_request *req);
+       void (*recalc)(struct factors_request *req);
        const char *name;
 };
 
@@ -42,6 +44,7 @@ struct clk_factors {
        void __iomem *reg;
        const struct clk_factors_config *config;
        void (*get_factors)(struct factors_request *req);
+       void (*recalc)(struct factors_request *req);
        spinlock_t *lock;
        /* for cleanup */
        struct clk_mux *mux;