]> git.karo-electronics.de Git - karo-tx-linux.git/blobdiff - drivers/clk/sunxi/clk-factors.c
Merge remote-tracking branch 'nfs/linux-next'
[karo-tx-linux.git] / drivers / clk / sunxi / clk-factors.c
index 59428dbd607a85d3ff15e3b648f529e1a575abf9..ddefe9668863b0ffbaebfc5e33c709c49c2486e2 100644 (file)
@@ -48,7 +48,7 @@ static unsigned long clk_factors_recalc_rate(struct clk_hw *hw,
        u32 reg;
        unsigned long rate;
        struct clk_factors *factors = to_clk_factors(hw);
-       struct clk_factors_config *config = factors->config;
+       const struct clk_factors_config *config = factors->config;
 
        /* Fetch the register value */
        reg = readl(factors->reg);
@@ -63,18 +63,28 @@ 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);
 
-       /* Calculate the rate */
-       rate = (parent_rate * (n + config->n_start) * (k + 1) >> p) / (m + 1);
+       if (factors->recalc) {
+               struct factors_request factors_req = {
+                       .parent_rate = parent_rate,
+                       .n = n,
+                       .k = k,
+                       .m = m,
+                       .p = p,
+               };
 
-       return rate;
-}
+               /* get mux details from mux clk structure */
+               if (factors->mux)
+                       factors_req.parent_index =
+                               (reg >> factors->mux->shift) &
+                               factors->mux->mask;
 
-static long clk_factors_round_rate(struct clk_hw *hw, unsigned long rate,
-                                  unsigned long *parent_rate)
-{
-       struct clk_factors *factors = to_clk_factors(hw);
-       factors->get_factors((u32 *)&rate, (u32)*parent_rate,
-                            NULL, NULL, NULL, NULL);
+               factors->recalc(&factors_req);
+
+               return factors_req.rate;
+       }
+
+       /* Calculate the rate */
+       rate = (parent_rate * (n + config->n_start) * (k + 1) >> p) / (m + 1);
 
        return rate;
 }
@@ -82,6 +92,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;
@@ -89,6 +100,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;
@@ -97,8 +112,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;
@@ -120,13 +136,16 @@ static int clk_factors_determine_rate(struct clk_hw *hw,
 static int clk_factors_set_rate(struct clk_hw *hw, unsigned long rate,
                                unsigned long parent_rate)
 {
-       u8 n = 0, k = 0, m = 0, p = 0;
+       struct factors_request req = {
+               .rate = rate,
+               .parent_rate = parent_rate,
+       };
        u32 reg;
        struct clk_factors *factors = to_clk_factors(hw);
-       struct clk_factors_config *config = factors->config;
+       const struct clk_factors_config *config = factors->config;
        unsigned long flags = 0;
 
-       factors->get_factors((u32 *)&rate, (u32)parent_rate, &n, &k, &m, &p);
+       factors->get_factors(&req);
 
        if (factors->lock)
                spin_lock_irqsave(factors->lock, flags);
@@ -135,10 +154,10 @@ static int clk_factors_set_rate(struct clk_hw *hw, unsigned long rate,
        reg = readl(factors->reg);
 
        /* Set up the new factors - macros do not do anything if width is 0 */
-       reg = FACTOR_SET(config->nshift, config->nwidth, reg, n);
-       reg = FACTOR_SET(config->kshift, config->kwidth, reg, k);
-       reg = FACTOR_SET(config->mshift, config->mwidth, reg, m);
-       reg = FACTOR_SET(config->pshift, config->pwidth, reg, p);
+       reg = FACTOR_SET(config->nshift, config->nwidth, reg, req.n);
+       reg = FACTOR_SET(config->kshift, config->kwidth, reg, req.k);
+       reg = FACTOR_SET(config->mshift, config->mwidth, reg, req.m);
+       reg = FACTOR_SET(config->pshift, config->pwidth, reg, req.p);
 
        /* Apply them now */
        writel(reg, factors->reg);
@@ -155,7 +174,6 @@ static int clk_factors_set_rate(struct clk_hw *hw, unsigned long rate,
 static const struct clk_ops clk_factors_ops = {
        .determine_rate = clk_factors_determine_rate,
        .recalc_rate = clk_factors_recalc_rate,
-       .round_rate = clk_factors_round_rate,
        .set_rate = clk_factors_set_rate,
 };
 
@@ -172,7 +190,7 @@ struct clk *sunxi_factors_register(struct device_node *node,
        struct clk_hw *mux_hw = NULL;
        const char *clk_name = node->name;
        const char *parents[FACTORS_MAX_PARENTS];
-       int i = 0;
+       int ret, i = 0;
 
        /* if we have a mux, we will have >1 parents */
        i = of_clk_parent_fill(node, parents, FACTORS_MAX_PARENTS);
@@ -188,21 +206,22 @@ struct clk *sunxi_factors_register(struct device_node *node,
 
        factors = kzalloc(sizeof(struct clk_factors), GFP_KERNEL);
        if (!factors)
-               return NULL;
+               goto err_factors;
 
        /* set up factors properties */
        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 */
        if (data->enable) {
                gate = kzalloc(sizeof(struct clk_gate), GFP_KERNEL);
-               if (!gate) {
-                       kfree(factors);
-                       return NULL;
-               }
+               if (!gate)
+                       goto err_gate;
+
+               factors->gate = gate;
 
                /* set up gate properties */
                gate->reg = reg;
@@ -214,11 +233,10 @@ struct clk *sunxi_factors_register(struct device_node *node,
        /* Add a mux if this factor clock can be muxed */
        if (data->mux) {
                mux = kzalloc(sizeof(struct clk_mux), GFP_KERNEL);
-               if (!mux) {
-                       kfree(factors);
-                       kfree(gate);
-                       return NULL;
-               }
+               if (!mux)
+                       goto err_mux;
+
+               factors->mux = mux;
 
                /* set up gate properties */
                mux->reg = reg;
@@ -233,11 +251,44 @@ struct clk *sunxi_factors_register(struct device_node *node,
                        mux_hw, &clk_mux_ops,
                        &factors->hw, &clk_factors_ops,
                        gate_hw, &clk_gate_ops, 0);
+       if (IS_ERR(clk))
+               goto err_register;
 
-       if (!IS_ERR(clk)) {
-               of_clk_add_provider(node, of_clk_src_simple_get, clk);
-               clk_register_clkdev(clk, clk_name, NULL);
-       }
+       ret = of_clk_add_provider(node, of_clk_src_simple_get, clk);
+       if (ret)
+               goto err_provider;
 
        return clk;
+
+err_provider:
+       /* TODO: The composite clock stuff will leak a bit here. */
+       clk_unregister(clk);
+err_register:
+       kfree(mux);
+err_mux:
+       kfree(gate);
+err_gate:
+       kfree(factors);
+err_factors:
+       return NULL;
+}
+
+void sunxi_factors_unregister(struct device_node *node, struct clk *clk)
+{
+       struct clk_hw *hw = __clk_get_hw(clk);
+       struct clk_factors *factors;
+       const char *name;
+
+       if (!hw)
+               return;
+
+       factors = to_clk_factors(hw);
+       name = clk_hw_get_name(hw);
+
+       of_clk_del_provider(node);
+       /* TODO: The composite clock stuff will leak a bit here. */
+       clk_unregister(clk);
+       kfree(factors->mux);
+       kfree(factors->gate);
+       kfree(factors);
 }