summaryrefslogtreecommitdiff
path: root/drivers/clk/sunxi
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2016-03-23 16:06:45 +0300
committerLinus Torvalds <torvalds@linux-foundation.org>2016-03-23 16:06:45 +0300
commit33c1f638a0feda92ffcb507c302482a5e6158a87 (patch)
tree57d70d2e0b56d38bd295fe439294c3c8739f4130 /drivers/clk/sunxi
parenta24e3d414e59ac76566dedcad1ed1d319a93ec14 (diff)
parent4d3ac6662452060721599a3392bc2f524af984cb (diff)
downloadlinux-33c1f638a0feda92ffcb507c302482a5e6158a87.tar.xz
Merge tag 'clk-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/clk/linux
Pull clk updates from Stephen Boyd: "The clk changes for this release cycle are mostly dominated by new device support in terms of LoC, but there has been some cleanup in the core as well as the usual minor clk additions to various drivers. Core: - parent tracking has been simplified - CLK_IS_ROOT is now a no-op flag, cleaning up drivers has started - of_clk_init() doesn't consider disabled DT nodes anymore - clk_unregister() had an error path bug squashed - of_clk_get_parent_count() has been fixed to only return unsigned ints - HAVE_MACH_CLKDEV is removed now that the last arch user (ARM) is gone New Drivers: - NXP LPC18xx creg - QCOM IPQ4019 GCC - TI dm814x ADPLL - i.MX6QP Updates: - Cyngus audio clks found on Broadcom iProc devices - Non-critical fixes for BCM2385 PLLs - Samsung exynos5433 updates for clk id errors, HDMI support, suspend/resume simplifications - USB, CAN, LVDS, and FCP clks on shmobile devices - sunxi got support for more clks on new SoCs and went through a minor refactoring/rewrite to use a simpler factor clk construct - rockchip added some more clk ids and added suport for fraction dividers - QCOM GDSCs in msm8996 - A new devm helper to make adding custom actions simpler (acked by Greg)" * tag 'clk-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/clk/linux: (197 commits) clk: bcm2835: fix check of error code returned by devm_ioremap_resource() clk: renesas: div6: use RENESAS for #define clk: renesas: Rename header file renesas.h clk: max77{686,802}: Remove CLK_IS_ROOT clk: versatile: Remove CLK_IS_ROOT clk: sunxi: Remove use of variable length array clk: fixed-rate: Remove CLK_IS_ROOT clk: qcom: Remove CLK_IS_ROOT doc: dt: add documentation for lpc1850-creg-clk driver clk: add lpc18xx creg clk driver clk: lpc32xx: fix compilation warning clk: xgene: Add missing parenthesis when clearing divider value clk: mb86s7x: Remove CLK_IS_ROOT clk: x86: Remove clkdev.h and clk.h includes clk: x86: Remove CLK_IS_ROOT clk: mvebu: Remove CLK_IS_ROOT clk: renesas: move drivers to renesas directory clk: si5{14,351,70}: Remove CLK_IS_ROOT clk: scpi: Remove CLK_IS_ROOT clk: s2mps11: Remove CLK_IS_ROOT ...
Diffstat (limited to 'drivers/clk/sunxi')
-rw-r--r--drivers/clk/sunxi/clk-a10-hosc.c3
-rw-r--r--drivers/clk/sunxi/clk-a20-gmac.c2
-rw-r--r--drivers/clk/sunxi/clk-factors.c123
-rw-r--r--drivers/clk/sunxi/clk-factors.h26
-rw-r--r--drivers/clk/sunxi/clk-mod0.c23
-rw-r--r--drivers/clk/sunxi/clk-simple-gates.c4
-rw-r--r--drivers/clk/sunxi/clk-sun6i-apb0-gates.c2
-rw-r--r--drivers/clk/sunxi/clk-sun6i-ar100.c235
-rw-r--r--drivers/clk/sunxi/clk-sun8i-apb0.c2
-rw-r--r--drivers/clk/sunxi/clk-sun8i-bus-gates.c3
-rw-r--r--drivers/clk/sunxi/clk-sun8i-mbus.c132
-rw-r--r--drivers/clk/sunxi/clk-sun9i-core.c86
-rw-r--r--drivers/clk/sunxi/clk-sunxi.c863
-rw-r--r--drivers/clk/sunxi/clk-usb.c24
14 files changed, 693 insertions, 835 deletions
diff --git a/drivers/clk/sunxi/clk-a10-hosc.c b/drivers/clk/sunxi/clk-a10-hosc.c
index 0481d5d673d6..6b598c6a0213 100644
--- a/drivers/clk/sunxi/clk-a10-hosc.c
+++ b/drivers/clk/sunxi/clk-a10-hosc.c
@@ -15,9 +15,9 @@
*/
#include <linux/clk-provider.h>
-#include <linux/clkdev.h>
#include <linux/of.h>
#include <linux/of_address.h>
+#include <linux/slab.h>
#define SUNXI_OSC24M_GATE 0
@@ -61,7 +61,6 @@ static void __init sun4i_osc_clk_setup(struct device_node *node)
goto err_free_gate;
of_clk_add_provider(node, of_clk_src_simple_get, clk);
- clk_register_clkdev(clk, clk_name, NULL);
return;
diff --git a/drivers/clk/sunxi/clk-a20-gmac.c b/drivers/clk/sunxi/clk-a20-gmac.c
index 1611b036421c..3437f734c9bf 100644
--- a/drivers/clk/sunxi/clk-a20-gmac.c
+++ b/drivers/clk/sunxi/clk-a20-gmac.c
@@ -17,7 +17,6 @@
*/
#include <linux/clk-provider.h>
-#include <linux/clkdev.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/slab.h>
@@ -107,7 +106,6 @@ static void __init sun7i_a20_gmac_clk_setup(struct device_node *node)
goto iounmap_reg;
of_clk_add_provider(node, of_clk_src_simple_get, clk);
- clk_register_clkdev(clk, clk_name, NULL);
return;
diff --git a/drivers/clk/sunxi/clk-factors.c b/drivers/clk/sunxi/clk-factors.c
index 59428dbd607a..ddefe9668863 100644
--- a/drivers/clk/sunxi/clk-factors.c
+++ b/drivers/clk/sunxi/clk-factors.c
@@ -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);
}
diff --git a/drivers/clk/sunxi/clk-factors.h b/drivers/clk/sunxi/clk-factors.h
index 171085ab5513..1e63c5b2d5f4 100644
--- a/drivers/clk/sunxi/clk-factors.h
+++ b/drivers/clk/sunxi/clk-factors.h
@@ -2,7 +2,6 @@
#define __MACH_SUNXI_CLK_FACTORS_H
#include <linux/clk-provider.h>
-#include <linux/clkdev.h>
#include <linux/spinlock.h>
#define SUNXI_FACTORS_NOT_APPLICABLE (0)
@@ -19,21 +18,36 @@ struct clk_factors_config {
u8 n_start;
};
+struct factors_request {
+ unsigned long rate;
+ unsigned long parent_rate;
+ u8 parent_index;
+ u8 n;
+ u8 k;
+ u8 m;
+ u8 p;
+};
+
struct factors_data {
int enable;
int mux;
int muxmask;
- struct clk_factors_config *table;
- void (*getter) (u32 *rate, u32 parent_rate, u8 *n, u8 *k, u8 *m, u8 *p);
+ const struct clk_factors_config *table;
+ void (*getter)(struct factors_request *req);
+ void (*recalc)(struct factors_request *req);
const char *name;
};
struct clk_factors {
struct clk_hw hw;
void __iomem *reg;
- struct clk_factors_config *config;
- void (*get_factors) (u32 *rate, u32 parent, u8 *n, u8 *k, u8 *m, u8 *p);
+ 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;
+ struct clk_gate *gate;
};
struct clk *sunxi_factors_register(struct device_node *node,
@@ -41,4 +55,6 @@ struct clk *sunxi_factors_register(struct device_node *node,
spinlock_t *lock,
void __iomem *reg);
+void sunxi_factors_unregister(struct device_node *node, struct clk *clk);
+
#endif
diff --git a/drivers/clk/sunxi/clk-mod0.c b/drivers/clk/sunxi/clk-mod0.c
index d167e1efb927..b38d71cec74c 100644
--- a/drivers/clk/sunxi/clk-mod0.c
+++ b/drivers/clk/sunxi/clk-mod0.c
@@ -15,6 +15,7 @@
*/
#include <linux/clk.h>
+#include <linux/clkdev.h>
#include <linux/clk-provider.h>
#include <linux/of_address.h>
#include <linux/platform_device.h>
@@ -28,17 +29,16 @@
* rate = (parent_rate >> p) / (m + 1);
*/
-static void sun4i_a10_get_mod0_factors(u32 *freq, u32 parent_rate,
- u8 *n, u8 *k, u8 *m, u8 *p)
+static void sun4i_a10_get_mod0_factors(struct factors_request *req)
{
u8 div, calcm, calcp;
/* These clocks can only divide, so we will never be able to achieve
* frequencies higher than the parent frequency */
- if (*freq > parent_rate)
- *freq = parent_rate;
+ if (req->rate > req->parent_rate)
+ req->rate = req->parent_rate;
- div = DIV_ROUND_UP(parent_rate, *freq);
+ div = DIV_ROUND_UP(req->parent_rate, req->rate);
if (div < 16)
calcp = 0;
@@ -51,18 +51,13 @@ static void sun4i_a10_get_mod0_factors(u32 *freq, u32 parent_rate,
calcm = DIV_ROUND_UP(div, 1 << calcp);
- *freq = (parent_rate >> calcp) / calcm;
-
- /* we were called to round the frequency, we can now return */
- if (n == NULL)
- return;
-
- *m = calcm - 1;
- *p = calcp;
+ req->rate = (req->parent_rate >> calcp) / calcm;
+ req->m = calcm - 1;
+ req->p = calcp;
}
/* user manual says "n" but it's really "p" */
-static struct clk_factors_config sun4i_a10_mod0_config = {
+static const struct clk_factors_config sun4i_a10_mod0_config = {
.mshift = 0,
.mwidth = 4,
.pshift = 16,
diff --git a/drivers/clk/sunxi/clk-simple-gates.c b/drivers/clk/sunxi/clk-simple-gates.c
index f4da52b5ca0e..a085c3bc127c 100644
--- a/drivers/clk/sunxi/clk-simple-gates.c
+++ b/drivers/clk/sunxi/clk-simple-gates.c
@@ -98,6 +98,8 @@ static void __init sunxi_simple_gates_init(struct device_node *node)
sunxi_simple_gates_setup(node, NULL, 0);
}
+CLK_OF_DECLARE(sun4i_a10_gates, "allwinner,sun4i-a10-gates-clk",
+ sunxi_simple_gates_init);
CLK_OF_DECLARE(sun4i_a10_apb0, "allwinner,sun4i-a10-apb0-gates-clk",
sunxi_simple_gates_init);
CLK_OF_DECLARE(sun4i_a10_apb1, "allwinner,sun4i-a10-apb1-gates-clk",
@@ -130,6 +132,8 @@ CLK_OF_DECLARE(sun8i_a23_apb2, "allwinner,sun8i-a23-apb2-gates-clk",
sunxi_simple_gates_init);
CLK_OF_DECLARE(sun8i_a33_ahb1, "allwinner,sun8i-a33-ahb1-gates-clk",
sunxi_simple_gates_init);
+CLK_OF_DECLARE(sun8i_a83t_apb0, "allwinner,sun8i-a83t-apb0-gates-clk",
+ sunxi_simple_gates_init);
CLK_OF_DECLARE(sun9i_a80_ahb0, "allwinner,sun9i-a80-ahb0-gates-clk",
sunxi_simple_gates_init);
CLK_OF_DECLARE(sun9i_a80_ahb1, "allwinner,sun9i-a80-ahb1-gates-clk",
diff --git a/drivers/clk/sunxi/clk-sun6i-apb0-gates.c b/drivers/clk/sunxi/clk-sun6i-apb0-gates.c
index 23d042aabb4f..68021fa5ecd9 100644
--- a/drivers/clk/sunxi/clk-sun6i-apb0-gates.c
+++ b/drivers/clk/sunxi/clk-sun6i-apb0-gates.c
@@ -9,7 +9,6 @@
*/
#include <linux/clk-provider.h>
-#include <linux/clkdev.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_device.h>
@@ -87,7 +86,6 @@ static int sun6i_a31_apb0_gates_clk_probe(struct platform_device *pdev)
clk_parent, 0, reg, i,
0, NULL);
WARN_ON(IS_ERR(clk_data->clks[i]));
- clk_register_clkdev(clk_data->clks[i], clk_name, NULL);
j++;
}
diff --git a/drivers/clk/sunxi/clk-sun6i-ar100.c b/drivers/clk/sunxi/clk-sun6i-ar100.c
index 20887686bdbe..84a187e55360 100644
--- a/drivers/clk/sunxi/clk-sun6i-ar100.c
+++ b/drivers/clk/sunxi/clk-sun6i-ar100.c
@@ -8,211 +8,97 @@
*
*/
+#include <linux/bitops.h>
#include <linux/clk-provider.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/platform_device.h>
+#include <linux/spinlock.h>
-#define SUN6I_AR100_MAX_PARENTS 4
-#define SUN6I_AR100_SHIFT_MASK 0x3
-#define SUN6I_AR100_SHIFT_MAX SUN6I_AR100_SHIFT_MASK
-#define SUN6I_AR100_SHIFT_SHIFT 4
-#define SUN6I_AR100_DIV_MASK 0x1f
-#define SUN6I_AR100_DIV_MAX (SUN6I_AR100_DIV_MASK + 1)
-#define SUN6I_AR100_DIV_SHIFT 8
-#define SUN6I_AR100_MUX_MASK 0x3
-#define SUN6I_AR100_MUX_SHIFT 16
-
-struct ar100_clk {
- struct clk_hw hw;
- void __iomem *reg;
-};
-
-static inline struct ar100_clk *to_ar100_clk(struct clk_hw *hw)
-{
- return container_of(hw, struct ar100_clk, hw);
-}
-
-static unsigned long ar100_recalc_rate(struct clk_hw *hw,
- unsigned long parent_rate)
-{
- struct ar100_clk *clk = to_ar100_clk(hw);
- u32 val = readl(clk->reg);
- int shift = (val >> SUN6I_AR100_SHIFT_SHIFT) & SUN6I_AR100_SHIFT_MASK;
- int div = (val >> SUN6I_AR100_DIV_SHIFT) & SUN6I_AR100_DIV_MASK;
-
- return (parent_rate >> shift) / (div + 1);
-}
-
-static int ar100_determine_rate(struct clk_hw *hw,
- struct clk_rate_request *req)
-{
- int nparents = clk_hw_get_num_parents(hw);
- long best_rate = -EINVAL;
- int i;
-
- req->best_parent_hw = NULL;
-
- for (i = 0; i < nparents; i++) {
- unsigned long parent_rate;
- unsigned long tmp_rate;
- struct clk_hw *parent;
- unsigned long div;
- int shift;
-
- parent = clk_hw_get_parent_by_index(hw, i);
- parent_rate = clk_hw_get_rate(parent);
- div = DIV_ROUND_UP(parent_rate, req->rate);
-
- /*
- * The AR100 clk contains 2 divisors:
- * - one power of 2 divisor
- * - one regular divisor
- *
- * First check if we can safely shift (or divide by a power
- * of 2) without losing precision on the requested rate.
- */
- shift = ffs(div) - 1;
- if (shift > SUN6I_AR100_SHIFT_MAX)
- shift = SUN6I_AR100_SHIFT_MAX;
-
- div >>= shift;
-
- /*
- * Then if the divisor is still bigger than what the HW
- * actually supports, use a bigger shift (or power of 2
- * divider) value and accept to lose some precision.
- */
- while (div > SUN6I_AR100_DIV_MAX) {
- shift++;
- div >>= 1;
- if (shift > SUN6I_AR100_SHIFT_MAX)
- break;
- }
-
- /*
- * If the shift value (or power of 2 divider) is bigger
- * than what the HW actually support, skip this parent.
- */
- if (shift > SUN6I_AR100_SHIFT_MAX)
- continue;
-
- tmp_rate = (parent_rate >> shift) / div;
- if (!req->best_parent_hw || tmp_rate > best_rate) {
- req->best_parent_hw = parent;
- req->best_parent_rate = parent_rate;
- best_rate = tmp_rate;
- }
- }
-
- if (best_rate < 0)
- return best_rate;
-
- req->rate = best_rate;
-
- return 0;
-}
-
-static int ar100_set_parent(struct clk_hw *hw, u8 index)
-{
- struct ar100_clk *clk = to_ar100_clk(hw);
- u32 val = readl(clk->reg);
-
- if (index >= SUN6I_AR100_MAX_PARENTS)
- return -EINVAL;
-
- val &= ~(SUN6I_AR100_MUX_MASK << SUN6I_AR100_MUX_SHIFT);
- val |= (index << SUN6I_AR100_MUX_SHIFT);
- writel(val, clk->reg);
-
- return 0;
-}
+#include "clk-factors.h"
-static u8 ar100_get_parent(struct clk_hw *hw)
-{
- struct ar100_clk *clk = to_ar100_clk(hw);
- return (readl(clk->reg) >> SUN6I_AR100_MUX_SHIFT) &
- SUN6I_AR100_MUX_MASK;
-}
-
-static int ar100_set_rate(struct clk_hw *hw, unsigned long rate,
- unsigned long parent_rate)
+/**
+ * sun6i_get_ar100_factors - Calculates factors p, m for AR100
+ *
+ * AR100 rate is calculated as follows
+ * rate = (parent_rate >> p) / (m + 1);
+ */
+static void sun6i_get_ar100_factors(struct factors_request *req)
{
- unsigned long div = parent_rate / rate;
- struct ar100_clk *clk = to_ar100_clk(hw);
- u32 val = readl(clk->reg);
+ unsigned long div;
int shift;
- if (parent_rate % rate)
- return -EINVAL;
+ /* clock only divides */
+ if (req->rate > req->parent_rate)
+ req->rate = req->parent_rate;
- shift = ffs(div) - 1;
- if (shift > SUN6I_AR100_SHIFT_MAX)
- shift = SUN6I_AR100_SHIFT_MAX;
+ div = DIV_ROUND_UP(req->parent_rate, req->rate);
- div >>= shift;
+ if (div < 32)
+ shift = 0;
+ else if (div >> 1 < 32)
+ shift = 1;
+ else if (div >> 2 < 32)
+ shift = 2;
+ else
+ shift = 3;
- if (div > SUN6I_AR100_DIV_MAX)
- return -EINVAL;
+ div >>= shift;
- val &= ~((SUN6I_AR100_SHIFT_MASK << SUN6I_AR100_SHIFT_SHIFT) |
- (SUN6I_AR100_DIV_MASK << SUN6I_AR100_DIV_SHIFT));
- val |= (shift << SUN6I_AR100_SHIFT_SHIFT) |
- (div << SUN6I_AR100_DIV_SHIFT);
- writel(val, clk->reg);
+ if (div > 32)
+ div = 32;
- return 0;
+ req->rate = (req->parent_rate >> shift) / div;
+ req->m = div - 1;
+ req->p = shift;
}
-static struct clk_ops ar100_ops = {
- .recalc_rate = ar100_recalc_rate,
- .determine_rate = ar100_determine_rate,
- .set_parent = ar100_set_parent,
- .get_parent = ar100_get_parent,
- .set_rate = ar100_set_rate,
+static const struct clk_factors_config sun6i_ar100_config = {
+ .mwidth = 5,
+ .mshift = 8,
+ .pwidth = 2,
+ .pshift = 4,
};
+static const struct factors_data sun6i_ar100_data = {
+ .mux = 16,
+ .muxmask = GENMASK(1, 0),
+ .table = &sun6i_ar100_config,
+ .getter = sun6i_get_ar100_factors,
+};
+
+static DEFINE_SPINLOCK(sun6i_ar100_lock);
+
static int sun6i_a31_ar100_clk_probe(struct platform_device *pdev)
{
- const char *parents[SUN6I_AR100_MAX_PARENTS];
struct device_node *np = pdev->dev.of_node;
- const char *clk_name = np->name;
- struct clk_init_data init;
- struct ar100_clk *ar100;
struct resource *r;
+ void __iomem *reg;
struct clk *clk;
- int nparents;
-
- ar100 = devm_kzalloc(&pdev->dev, sizeof(*ar100), GFP_KERNEL);
- if (!ar100)
- return -ENOMEM;
r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- ar100->reg = devm_ioremap_resource(&pdev->dev, r);
- if (IS_ERR(ar100->reg))
- return PTR_ERR(ar100->reg);
+ reg = devm_ioremap_resource(&pdev->dev, r);
+ if (IS_ERR(reg))
+ return PTR_ERR(reg);
- nparents = of_clk_get_parent_count(np);
- if (nparents > SUN6I_AR100_MAX_PARENTS)
- nparents = SUN6I_AR100_MAX_PARENTS;
-
- of_clk_parent_fill(np, parents, nparents);
+ clk = sunxi_factors_register(np, &sun6i_ar100_data, &sun6i_ar100_lock,
+ reg);
+ if (!clk)
+ return -ENOMEM;
- of_property_read_string(np, "clock-output-names", &clk_name);
+ platform_set_drvdata(pdev, clk);
- init.name = clk_name;
- init.ops = &ar100_ops;
- init.parent_names = parents;
- init.num_parents = nparents;
- init.flags = 0;
+ return 0;
+}
- ar100->hw.init = &init;
+static int sun6i_a31_ar100_clk_remove(struct platform_device *pdev)
+{
+ struct device_node *np = pdev->dev.of_node;
+ struct clk *clk = platform_get_drvdata(pdev);
- clk = clk_register(&pdev->dev, &ar100->hw);
- if (IS_ERR(clk))
- return PTR_ERR(clk);
+ sunxi_factors_unregister(np, clk);
- return of_clk_add_provider(np, of_clk_src_simple_get, clk);
+ return 0;
}
static const struct of_device_id sun6i_a31_ar100_clk_dt_ids[] = {
@@ -227,6 +113,7 @@ static struct platform_driver sun6i_a31_ar100_clk_driver = {
.of_match_table = sun6i_a31_ar100_clk_dt_ids,
},
.probe = sun6i_a31_ar100_clk_probe,
+ .remove = sun6i_a31_ar100_clk_remove,
};
module_platform_driver(sun6i_a31_ar100_clk_driver);
diff --git a/drivers/clk/sunxi/clk-sun8i-apb0.c b/drivers/clk/sunxi/clk-sun8i-apb0.c
index 7ba61103a6f5..2ea61debffc1 100644
--- a/drivers/clk/sunxi/clk-sun8i-apb0.c
+++ b/drivers/clk/sunxi/clk-sun8i-apb0.c
@@ -36,7 +36,7 @@ static struct clk *sun8i_a23_apb0_register(struct device_node *node,
/* The A23 APB0 clock is a standard 2 bit wide divider clock */
clk = clk_register_divider(NULL, clk_name, clk_parent, 0, reg,
- 0, 2, CLK_DIVIDER_POWER_OF_TWO, NULL);
+ 0, 2, 0, NULL);
if (IS_ERR(clk))
return clk;
diff --git a/drivers/clk/sunxi/clk-sun8i-bus-gates.c b/drivers/clk/sunxi/clk-sun8i-bus-gates.c
index e32d18ba252b..63fdb790df29 100644
--- a/drivers/clk/sunxi/clk-sun8i-bus-gates.c
+++ b/drivers/clk/sunxi/clk-sun8i-bus-gates.c
@@ -17,7 +17,6 @@
* GNU General Public License for more details.
*/
-#include <linux/clk.h>
#include <linux/clk-provider.h>
#include <linux/of.h>
#include <linux/of_address.h>
@@ -110,3 +109,5 @@ err_unmap:
CLK_OF_DECLARE(sun8i_h3_bus_gates, "allwinner,sun8i-h3-bus-gates-clk",
sun8i_h3_bus_gates_init);
+CLK_OF_DECLARE(sun8i_a83t_bus_gates, "allwinner,sun8i-a83t-bus-gates-clk",
+ sun8i_h3_bus_gates_init);
diff --git a/drivers/clk/sunxi/clk-sun8i-mbus.c b/drivers/clk/sunxi/clk-sun8i-mbus.c
index bf117a636d23..411d3033a96e 100644
--- a/drivers/clk/sunxi/clk-sun8i-mbus.c
+++ b/drivers/clk/sunxi/clk-sun8i-mbus.c
@@ -15,74 +15,106 @@
*/
#include <linux/clk.h>
+#include <linux/clkdev.h>
#include <linux/clk-provider.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
#include <linux/of_address.h>
-#include "clk-factors.h"
+#define SUN8I_MBUS_ENABLE 31
+#define SUN8I_MBUS_MUX_SHIFT 24
+#define SUN8I_MBUS_MUX_MASK 0x3
+#define SUN8I_MBUS_DIV_SHIFT 0
+#define SUN8I_MBUS_DIV_WIDTH 3
+#define SUN8I_MBUS_MAX_PARENTS 4
-/**
- * sun8i_a23_get_mbus_factors() - calculates m factor for MBUS clocks
- * MBUS rate is calculated as follows
- * rate = parent_rate / (m + 1);
- */
+static DEFINE_SPINLOCK(sun8i_a23_mbus_lock);
-static void sun8i_a23_get_mbus_factors(u32 *freq, u32 parent_rate,
- u8 *n, u8 *k, u8 *m, u8 *p)
+static void __init sun8i_a23_mbus_setup(struct device_node *node)
{
- u8 div;
+ int num_parents = of_clk_get_parent_count(node);
+ const char **parents;
+ const char *clk_name = node->name;
+ struct resource res;
+ struct clk_divider *div;
+ struct clk_gate *gate;
+ struct clk_mux *mux;
+ struct clk *clk;
+ void __iomem *reg;
+ int err;
- /*
- * These clocks can only divide, so we will never be able to
- * achieve frequencies higher than the parent frequency
- */
- if (*freq > parent_rate)
- *freq = parent_rate;
+ parents = kcalloc(num_parents, sizeof(*parents), GFP_KERNEL);
+ if (!parents)
+ return;
- div = DIV_ROUND_UP(parent_rate, *freq);
+ reg = of_io_request_and_map(node, 0, of_node_full_name(node));
+ if (!reg) {
+ pr_err("Could not get registers for sun8i-mbus-clk\n");
+ goto err_free_parents;
+ }
- if (div > 8)
- div = 8;
+ div = kzalloc(sizeof(*div), GFP_KERNEL);
+ if (!div)
+ goto err_unmap;
- *freq = parent_rate / div;
+ mux = kzalloc(sizeof(*mux), GFP_KERNEL);
+ if (!mux)
+ goto err_free_div;
- /* we were called to round the frequency, we can now return */
- if (m == NULL)
- return;
+ gate = kzalloc(sizeof(*gate), GFP_KERNEL);
+ if (!gate)
+ goto err_free_mux;
- *m = div - 1;
-}
+ of_property_read_string(node, "clock-output-names", &clk_name);
+ of_clk_parent_fill(node, parents, num_parents);
-static struct clk_factors_config sun8i_a23_mbus_config = {
- .mshift = 0,
- .mwidth = 3,
-};
+ gate->reg = reg;
+ gate->bit_idx = SUN8I_MBUS_ENABLE;
+ gate->lock = &sun8i_a23_mbus_lock;
-static const struct factors_data sun8i_a23_mbus_data __initconst = {
- .enable = 31,
- .mux = 24,
- .muxmask = BIT(1) | BIT(0),
- .table = &sun8i_a23_mbus_config,
- .getter = sun8i_a23_get_mbus_factors,
-};
+ div->reg = reg;
+ div->shift = SUN8I_MBUS_DIV_SHIFT;
+ div->width = SUN8I_MBUS_DIV_WIDTH;
+ div->lock = &sun8i_a23_mbus_lock;
-static DEFINE_SPINLOCK(sun8i_a23_mbus_lock);
-
-static void __init sun8i_a23_mbus_setup(struct device_node *node)
-{
- struct clk *mbus;
- void __iomem *reg;
+ mux->reg = reg;
+ mux->shift = SUN8I_MBUS_MUX_SHIFT;
+ mux->mask = SUN8I_MBUS_MUX_MASK;
+ mux->lock = &sun8i_a23_mbus_lock;
- reg = of_iomap(node, 0);
- if (!reg) {
- pr_err("Could not get registers for a23-mbus-clk\n");
- return;
- }
+ clk = clk_register_composite(NULL, clk_name, parents, num_parents,
+ &mux->hw, &clk_mux_ops,
+ &div->hw, &clk_divider_ops,
+ &gate->hw, &clk_gate_ops,
+ 0);
+ if (IS_ERR(clk))
+ goto err_free_gate;
- mbus = sunxi_factors_register(node, &sun8i_a23_mbus_data,
- &sun8i_a23_mbus_lock, reg);
+ err = of_clk_add_provider(node, of_clk_src_simple_get, clk);
+ if (err)
+ goto err_unregister_clk;
+ kfree(parents); /* parents is deep copied */
/* The MBUS clocks needs to be always enabled */
- __clk_get(mbus);
- clk_prepare_enable(mbus);
+ __clk_get(clk);
+ clk_prepare_enable(clk);
+
+ return;
+
+err_unregister_clk:
+ /* TODO: The composite clock stuff will leak a bit here. */
+ clk_unregister(clk);
+err_free_gate:
+ kfree(gate);
+err_free_mux:
+ kfree(mux);
+err_free_div:
+ kfree(div);
+err_unmap:
+ iounmap(reg);
+ of_address_to_resource(node, 0, &res);
+ release_mem_region(res.start, resource_size(&res));
+err_free_parents:
+ kfree(parents);
}
CLK_OF_DECLARE(sun8i_a23_mbus, "allwinner,sun8i-a23-mbus-clk", sun8i_a23_mbus_setup);
diff --git a/drivers/clk/sunxi/clk-sun9i-core.c b/drivers/clk/sunxi/clk-sun9i-core.c
index 6c4c98324d3c..43f014f85803 100644
--- a/drivers/clk/sunxi/clk-sun9i-core.c
+++ b/drivers/clk/sunxi/clk-sun9i-core.c
@@ -15,6 +15,7 @@
*/
#include <linux/clk.h>
+#include <linux/clkdev.h>
#include <linux/clk-provider.h>
#include <linux/of.h>
#include <linux/of_address.h>
@@ -32,15 +33,14 @@
* p and m are named div1 and div2 in Allwinner's SDK
*/
-static void sun9i_a80_get_pll4_factors(u32 *freq, u32 parent_rate,
- u8 *n_ret, u8 *k, u8 *m_ret, u8 *p_ret)
+static void sun9i_a80_get_pll4_factors(struct factors_request *req)
{
int n;
int m = 1;
int p = 1;
/* Normalize value to a 6 MHz multiple (24 MHz / 4) */
- n = DIV_ROUND_UP(*freq, 6000000);
+ n = DIV_ROUND_UP(req->rate, 6000000);
/* If n is too large switch to steps of 12 MHz */
if (n > 255) {
@@ -60,18 +60,13 @@ static void sun9i_a80_get_pll4_factors(u32 *freq, u32 parent_rate,
else if (n < 12)
n = 12;
- *freq = ((24000000 * n) >> p) / (m + 1);
-
- /* we were called to round the frequency, we can now return */
- if (n_ret == NULL)
- return;
-
- *n_ret = n;
- *m_ret = m;
- *p_ret = p;
+ req->rate = ((24000000 * n) >> p) / (m + 1);
+ req->n = n;
+ req->m = m;
+ req->p = p;
}
-static struct clk_factors_config sun9i_a80_pll4_config = {
+static const struct clk_factors_config sun9i_a80_pll4_config = {
.mshift = 18,
.mwidth = 1,
.nshift = 8,
@@ -111,30 +106,24 @@ CLK_OF_DECLARE(sun9i_a80_pll4, "allwinner,sun9i-a80-pll4-clk", sun9i_a80_pll4_se
* rate = parent_rate / (m + 1);
*/
-static void sun9i_a80_get_gt_factors(u32 *freq, u32 parent_rate,
- u8 *n, u8 *k, u8 *m, u8 *p)
+static void sun9i_a80_get_gt_factors(struct factors_request *req)
{
u32 div;
- if (parent_rate < *freq)
- *freq = parent_rate;
+ if (req->parent_rate < req->rate)
+ req->rate = req->parent_rate;
- div = DIV_ROUND_UP(parent_rate, *freq);
+ div = DIV_ROUND_UP(req->parent_rate, req->rate);
/* maximum divider is 4 */
if (div > 4)
div = 4;
- *freq = parent_rate / div;
-
- /* we were called to round the frequency, we can now return */
- if (!m)
- return;
-
- *m = div;
+ req->rate = req->parent_rate / div;
+ req->m = div;
}
-static struct clk_factors_config sun9i_a80_gt_config = {
+static const struct clk_factors_config sun9i_a80_gt_config = {
.mshift = 0,
.mwidth = 2,
};
@@ -176,30 +165,24 @@ CLK_OF_DECLARE(sun9i_a80_gt, "allwinner,sun9i-a80-gt-clk", sun9i_a80_gt_setup);
* rate = parent_rate >> p;
*/
-static void sun9i_a80_get_ahb_factors(u32 *freq, u32 parent_rate,
- u8 *n, u8 *k, u8 *m, u8 *p)
+static void sun9i_a80_get_ahb_factors(struct factors_request *req)
{
u32 _p;
- if (parent_rate < *freq)
- *freq = parent_rate;
+ if (req->parent_rate < req->rate)
+ req->rate = req->parent_rate;
- _p = order_base_2(DIV_ROUND_UP(parent_rate, *freq));
+ _p = order_base_2(DIV_ROUND_UP(req->parent_rate, req->rate));
/* maximum p is 3 */
if (_p > 3)
_p = 3;
- *freq = parent_rate >> _p;
-
- /* we were called to round the frequency, we can now return */
- if (!p)
- return;
-
- *p = _p;
+ req->rate = req->parent_rate >> _p;
+ req->p = _p;
}
-static struct clk_factors_config sun9i_a80_ahb_config = {
+static const struct clk_factors_config sun9i_a80_ahb_config = {
.pshift = 0,
.pwidth = 2,
};
@@ -262,34 +245,25 @@ CLK_OF_DECLARE(sun9i_a80_apb0, "allwinner,sun9i-a80-apb0-clk", sun9i_a80_apb0_se
* rate = (parent_rate >> p) / (m + 1);
*/
-static void sun9i_a80_get_apb1_factors(u32 *freq, u32 parent_rate,
- u8 *n, u8 *k, u8 *m, u8 *p)
+static void sun9i_a80_get_apb1_factors(struct factors_request *req)
{
u32 div;
- u8 calcm, calcp;
- if (parent_rate < *freq)
- *freq = parent_rate;
+ if (req->parent_rate < req->rate)
+ req->rate = req->parent_rate;
- div = DIV_ROUND_UP(parent_rate, *freq);
+ div = DIV_ROUND_UP(req->parent_rate, req->rate);
/* Highest possible divider is 256 (p = 3, m = 31) */
if (div > 256)
div = 256;
- calcp = order_base_2(div);
- calcm = (parent_rate >> calcp) - 1;
- *freq = (parent_rate >> calcp) / (calcm + 1);
-
- /* we were called to round the frequency, we can now return */
- if (n == NULL)
- return;
-
- *m = calcm;
- *p = calcp;
+ req->p = order_base_2(div);
+ req->m = (req->parent_rate >> req->p) - 1;
+ req->rate = (req->parent_rate >> req->p) / (req->m + 1);
}
-static struct clk_factors_config sun9i_a80_apb1_config = {
+static const struct clk_factors_config sun9i_a80_apb1_config = {
.mshift = 0,
.mwidth = 5,
.pshift = 16,
diff --git a/drivers/clk/sunxi/clk-sunxi.c b/drivers/clk/sunxi/clk-sunxi.c
index 5ba2188ee99c..91de0a006773 100644
--- a/drivers/clk/sunxi/clk-sunxi.c
+++ b/drivers/clk/sunxi/clk-sunxi.c
@@ -28,214 +28,6 @@
static DEFINE_SPINLOCK(clk_lock);
-/**
- * sun6i_a31_ahb1_clk_setup() - Setup function for a31 ahb1 composite clk
- */
-
-#define SUN6I_AHB1_MAX_PARENTS 4
-#define SUN6I_AHB1_MUX_PARENT_PLL6 3
-#define SUN6I_AHB1_MUX_SHIFT 12
-/* un-shifted mask is what mux_clk expects */
-#define SUN6I_AHB1_MUX_MASK 0x3
-#define SUN6I_AHB1_MUX_GET_PARENT(reg) ((reg >> SUN6I_AHB1_MUX_SHIFT) & \
- SUN6I_AHB1_MUX_MASK)
-
-#define SUN6I_AHB1_DIV_SHIFT 4
-#define SUN6I_AHB1_DIV_MASK (0x3 << SUN6I_AHB1_DIV_SHIFT)
-#define SUN6I_AHB1_DIV_GET(reg) ((reg & SUN6I_AHB1_DIV_MASK) >> \
- SUN6I_AHB1_DIV_SHIFT)
-#define SUN6I_AHB1_DIV_SET(reg, div) ((reg & ~SUN6I_AHB1_DIV_MASK) | \
- (div << SUN6I_AHB1_DIV_SHIFT))
-#define SUN6I_AHB1_PLL6_DIV_SHIFT 6
-#define SUN6I_AHB1_PLL6_DIV_MASK (0x3 << SUN6I_AHB1_PLL6_DIV_SHIFT)
-#define SUN6I_AHB1_PLL6_DIV_GET(reg) ((reg & SUN6I_AHB1_PLL6_DIV_MASK) >> \
- SUN6I_AHB1_PLL6_DIV_SHIFT)
-#define SUN6I_AHB1_PLL6_DIV_SET(reg, div) ((reg & ~SUN6I_AHB1_PLL6_DIV_MASK) | \
- (div << SUN6I_AHB1_PLL6_DIV_SHIFT))
-
-struct sun6i_ahb1_clk {
- struct clk_hw hw;
- void __iomem *reg;
-};
-
-#define to_sun6i_ahb1_clk(_hw) container_of(_hw, struct sun6i_ahb1_clk, hw)
-
-static unsigned long sun6i_ahb1_clk_recalc_rate(struct clk_hw *hw,
- unsigned long parent_rate)
-{
- struct sun6i_ahb1_clk *ahb1 = to_sun6i_ahb1_clk(hw);
- unsigned long rate;
- u32 reg;
-
- /* Fetch the register value */
- reg = readl(ahb1->reg);
-
- /* apply pre-divider first if parent is pll6 */
- if (SUN6I_AHB1_MUX_GET_PARENT(reg) == SUN6I_AHB1_MUX_PARENT_PLL6)
- parent_rate /= SUN6I_AHB1_PLL6_DIV_GET(reg) + 1;
-
- /* clk divider */
- rate = parent_rate >> SUN6I_AHB1_DIV_GET(reg);
-
- return rate;
-}
-
-static long sun6i_ahb1_clk_round(unsigned long rate, u8 *divp, u8 *pre_divp,
- u8 parent, unsigned long parent_rate)
-{
- u8 div, calcp, calcm = 1;
-
- /*
- * clock can only divide, so we will never be able to achieve
- * frequencies higher than the parent frequency
- */
- if (parent_rate && rate > parent_rate)
- rate = parent_rate;
-
- div = DIV_ROUND_UP(parent_rate, rate);
-
- /* calculate pre-divider if parent is pll6 */
- if (parent == SUN6I_AHB1_MUX_PARENT_PLL6) {
- if (div < 4)
- calcp = 0;
- else if (div / 2 < 4)
- calcp = 1;
- else if (div / 4 < 4)
- calcp = 2;
- else
- calcp = 3;
-
- calcm = DIV_ROUND_UP(div, 1 << calcp);
- } else {
- calcp = __roundup_pow_of_two(div);
- calcp = calcp > 3 ? 3 : calcp;
- }
-
- /* we were asked to pass back divider values */
- if (divp) {
- *divp = calcp;
- *pre_divp = calcm - 1;
- }
-
- return (parent_rate / calcm) >> calcp;
-}
-
-static int sun6i_ahb1_clk_determine_rate(struct clk_hw *hw,
- struct clk_rate_request *req)
-{
- struct clk_hw *parent, *best_parent = NULL;
- int i, num_parents;
- unsigned long parent_rate, best = 0, child_rate, best_child_rate = 0;
-
- /* 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++) {
- parent = clk_hw_get_parent_by_index(hw, i);
- if (!parent)
- continue;
- if (clk_hw_get_flags(hw) & CLK_SET_RATE_PARENT)
- parent_rate = clk_hw_round_rate(parent, req->rate);
- else
- parent_rate = clk_hw_get_rate(parent);
-
- child_rate = sun6i_ahb1_clk_round(req->rate, NULL, NULL, i,
- parent_rate);
-
- if (child_rate <= req->rate && child_rate > best_child_rate) {
- best_parent = parent;
- best = parent_rate;
- best_child_rate = child_rate;
- }
- }
-
- if (!best_parent)
- return -EINVAL;
-
- req->best_parent_hw = best_parent;
- req->best_parent_rate = best;
- req->rate = best_child_rate;
-
- return 0;
-}
-
-static int sun6i_ahb1_clk_set_rate(struct clk_hw *hw, unsigned long rate,
- unsigned long parent_rate)
-{
- struct sun6i_ahb1_clk *ahb1 = to_sun6i_ahb1_clk(hw);
- unsigned long flags;
- u8 div, pre_div, parent;
- u32 reg;
-
- spin_lock_irqsave(&clk_lock, flags);
-
- reg = readl(ahb1->reg);
-
- /* need to know which parent is used to apply pre-divider */
- parent = SUN6I_AHB1_MUX_GET_PARENT(reg);
- sun6i_ahb1_clk_round(rate, &div, &pre_div, parent, parent_rate);
-
- reg = SUN6I_AHB1_DIV_SET(reg, div);
- reg = SUN6I_AHB1_PLL6_DIV_SET(reg, pre_div);
- writel(reg, ahb1->reg);
-
- spin_unlock_irqrestore(&clk_lock, flags);
-
- return 0;
-}
-
-static const struct clk_ops sun6i_ahb1_clk_ops = {
- .determine_rate = sun6i_ahb1_clk_determine_rate,
- .recalc_rate = sun6i_ahb1_clk_recalc_rate,
- .set_rate = sun6i_ahb1_clk_set_rate,
-};
-
-static void __init sun6i_ahb1_clk_setup(struct device_node *node)
-{
- struct clk *clk;
- struct sun6i_ahb1_clk *ahb1;
- struct clk_mux *mux;
- const char *clk_name = node->name;
- const char *parents[SUN6I_AHB1_MAX_PARENTS];
- void __iomem *reg;
- int i;
-
- reg = of_io_request_and_map(node, 0, of_node_full_name(node));
- if (IS_ERR(reg))
- return;
-
- /* we have a mux, we will have >1 parents */
- i = of_clk_parent_fill(node, parents, SUN6I_AHB1_MAX_PARENTS);
- of_property_read_string(node, "clock-output-names", &clk_name);
-
- ahb1 = kzalloc(sizeof(struct sun6i_ahb1_clk), GFP_KERNEL);
- if (!ahb1)
- return;
-
- mux = kzalloc(sizeof(struct clk_mux), GFP_KERNEL);
- if (!mux) {
- kfree(ahb1);
- return;
- }
-
- /* set up clock properties */
- mux->reg = reg;
- mux->shift = SUN6I_AHB1_MUX_SHIFT;
- mux->mask = SUN6I_AHB1_MUX_MASK;
- mux->lock = &clk_lock;
- ahb1->reg = reg;
-
- clk = clk_register_composite(NULL, clk_name, parents, i,
- &mux->hw, &clk_mux_ops,
- &ahb1->hw, &sun6i_ahb1_clk_ops,
- NULL, NULL, 0);
-
- if (!IS_ERR(clk)) {
- of_clk_add_provider(node, of_clk_src_simple_get, clk);
- clk_register_clkdev(clk, clk_name, NULL);
- }
-}
-CLK_OF_DECLARE(sun6i_a31_ahb1, "allwinner,sun6i-a31-ahb1-clk", sun6i_ahb1_clk_setup);
-
/* Maximum number of parents our clocks have */
#define SUNXI_MAX_PARENTS 5
@@ -246,49 +38,45 @@ CLK_OF_DECLARE(sun6i_a31_ahb1, "allwinner,sun6i-a31-ahb1-clk", sun6i_ahb1_clk_se
* parent_rate is always 24Mhz
*/
-static void sun4i_get_pll1_factors(u32 *freq, u32 parent_rate,
- u8 *n, u8 *k, u8 *m, u8 *p)
+static void sun4i_get_pll1_factors(struct factors_request *req)
{
u8 div;
/* Normalize value to a 6M multiple */
- div = *freq / 6000000;
- *freq = 6000000 * div;
-
- /* we were called to round the frequency, we can now return */
- if (n == NULL)
- return;
+ div = req->rate / 6000000;
+ req->rate = 6000000 * div;
/* m is always zero for pll1 */
- *m = 0;
+ req->m = 0;
/* k is 1 only on these cases */
- if (*freq >= 768000000 || *freq == 42000000 || *freq == 54000000)
- *k = 1;
+ if (req->rate >= 768000000 || req->rate == 42000000 ||
+ req->rate == 54000000)
+ req->k = 1;
else
- *k = 0;
+ req->k = 0;
/* p will be 3 for divs under 10 */
if (div < 10)
- *p = 3;
+ req->p = 3;
/* p will be 2 for divs between 10 - 20 and odd divs under 32 */
else if (div < 20 || (div < 32 && (div & 1)))
- *p = 2;
+ req->p = 2;
/* p will be 1 for even divs under 32, divs under 40 and odd pairs
* of divs between 40-62 */
else if (div < 40 || (div < 64 && (div & 2)))
- *p = 1;
+ req->p = 1;
/* any other entries have p = 0 */
else
- *p = 0;
+ req->p = 0;
/* calculate a suitable n based on k and p */
- div <<= *p;
- div /= (*k + 1);
- *n = div / 4;
+ div <<= req->p;
+ div /= (req->k + 1);
+ req->n = div / 4;
}
/**
@@ -297,15 +85,14 @@ static void sun4i_get_pll1_factors(u32 *freq, u32 parent_rate,
* rate = parent_rate * (n + 1) * (k + 1) / (m + 1);
* parent_rate should always be 24MHz
*/
-static void sun6i_a31_get_pll1_factors(u32 *freq, u32 parent_rate,
- u8 *n, u8 *k, u8 *m, u8 *p)
+static void sun6i_a31_get_pll1_factors(struct factors_request *req)
{
/*
* We can operate only on MHz, this will make our life easier
* later.
*/
- u32 freq_mhz = *freq / 1000000;
- u32 parent_freq_mhz = parent_rate / 1000000;
+ u32 freq_mhz = req->rate / 1000000;
+ u32 parent_freq_mhz = req->parent_rate / 1000000;
/*
* Round down the frequency to the closest multiple of either
@@ -319,28 +106,20 @@ static void sun6i_a31_get_pll1_factors(u32 *freq, u32 parent_rate,
else
freq_mhz = round_freq_16;
- *freq = freq_mhz * 1000000;
-
- /*
- * If the factors pointer are null, we were just called to
- * round down the frequency.
- * Exit.
- */
- if (n == NULL)
- return;
+ req->rate = freq_mhz * 1000000;
/* If the frequency is a multiple of 32 MHz, k is always 3 */
if (!(freq_mhz % 32))
- *k = 3;
+ req->k = 3;
/* If the frequency is a multiple of 9 MHz, k is always 2 */
else if (!(freq_mhz % 9))
- *k = 2;
+ req->k = 2;
/* If the frequency is a multiple of 8 MHz, k is always 1 */
else if (!(freq_mhz % 8))
- *k = 1;
+ req->k = 1;
/* Otherwise, we don't use the k factor */
else
- *k = 0;
+ req->k = 0;
/*
* If the frequency is a multiple of 2 but not a multiple of
@@ -351,27 +130,28 @@ static void sun6i_a31_get_pll1_factors(u32 *freq, u32 parent_rate,
* somehow relates to this frequency.
*/
if ((freq_mhz % 6) == 2 || (freq_mhz % 6) == 4)
- *m = 2;
+ req->m = 2;
/*
* If the frequency is a multiple of 6MHz, but the factor is
* odd, m will be 3
*/
else if ((freq_mhz / 6) & 1)
- *m = 3;
+ req->m = 3;
/* Otherwise, we end up with m = 1 */
else
- *m = 1;
+ req->m = 1;
/* Calculate n thanks to the above factors we already got */
- *n = freq_mhz * (*m + 1) / ((*k + 1) * parent_freq_mhz) - 1;
+ req->n = freq_mhz * (req->m + 1) / ((req->k + 1) * parent_freq_mhz)
+ - 1;
/*
* If n end up being outbound, and that we can still decrease
* m, do it.
*/
- if ((*n + 1) > 31 && (*m + 1) > 1) {
- *n = (*n + 1) / 2 - 1;
- *m = (*m + 1) / 2 - 1;
+ if ((req->n + 1) > 31 && (req->m + 1) > 1) {
+ req->n = (req->n + 1) / 2 - 1;
+ req->m = (req->m + 1) / 2 - 1;
}
}
@@ -382,45 +162,41 @@ static void sun6i_a31_get_pll1_factors(u32 *freq, u32 parent_rate,
* parent_rate is always 24Mhz
*/
-static void sun8i_a23_get_pll1_factors(u32 *freq, u32 parent_rate,
- u8 *n, u8 *k, u8 *m, u8 *p)
+static void sun8i_a23_get_pll1_factors(struct factors_request *req)
{
u8 div;
/* Normalize value to a 6M multiple */
- div = *freq / 6000000;
- *freq = 6000000 * div;
-
- /* we were called to round the frequency, we can now return */
- if (n == NULL)
- return;
+ div = req->rate / 6000000;
+ req->rate = 6000000 * div;
/* m is always zero for pll1 */
- *m = 0;
+ req->m = 0;
/* k is 1 only on these cases */
- if (*freq >= 768000000 || *freq == 42000000 || *freq == 54000000)
- *k = 1;
+ if (req->rate >= 768000000 || req->rate == 42000000 ||
+ req->rate == 54000000)
+ req->k = 1;
else
- *k = 0;
+ req->k = 0;
/* p will be 2 for divs under 20 and odd divs under 32 */
if (div < 20 || (div < 32 && (div & 1)))
- *p = 2;
+ req->p = 2;
/* p will be 1 for even divs under 32, divs under 40 and odd pairs
* of divs between 40-62 */
else if (div < 40 || (div < 64 && (div & 2)))
- *p = 1;
+ req->p = 1;
/* any other entries have p = 0 */
else
- *p = 0;
+ req->p = 0;
/* calculate a suitable n based on k and p */
- div <<= *p;
- div /= (*k + 1);
- *n = div / 4 - 1;
+ div <<= req->p;
+ div /= (req->k + 1);
+ req->n = div / 4 - 1;
}
/**
@@ -430,29 +206,24 @@ static void sun8i_a23_get_pll1_factors(u32 *freq, u32 parent_rate,
* parent_rate is always 24Mhz
*/
-static void sun4i_get_pll5_factors(u32 *freq, u32 parent_rate,
- u8 *n, u8 *k, u8 *m, u8 *p)
+static void sun4i_get_pll5_factors(struct factors_request *req)
{
u8 div;
/* Normalize value to a parent_rate multiple (24M) */
- div = *freq / parent_rate;
- *freq = parent_rate * div;
-
- /* we were called to round the frequency, we can now return */
- if (n == NULL)
- return;
+ div = req->rate / req->parent_rate;
+ req->rate = req->parent_rate * div;
if (div < 31)
- *k = 0;
+ req->k = 0;
else if (div / 2 < 31)
- *k = 1;
+ req->k = 1;
else if (div / 3 < 31)
- *k = 2;
+ req->k = 2;
else
- *k = 3;
+ req->k = 3;
- *n = DIV_ROUND_UP(div, (*k+1));
+ req->n = DIV_ROUND_UP(div, (req->k + 1));
}
/**
@@ -462,24 +233,19 @@ static void sun4i_get_pll5_factors(u32 *freq, u32 parent_rate,
* parent_rate is always 24Mhz
*/
-static void sun6i_a31_get_pll6_factors(u32 *freq, u32 parent_rate,
- u8 *n, u8 *k, u8 *m, u8 *p)
+static void sun6i_a31_get_pll6_factors(struct factors_request *req)
{
u8 div;
/* Normalize value to a parent_rate multiple (24M) */
- div = *freq / parent_rate;
- *freq = parent_rate * div;
-
- /* we were called to round the frequency, we can now return */
- if (n == NULL)
- return;
+ div = req->rate / req->parent_rate;
+ req->rate = req->parent_rate * div;
- *k = div / 32;
- if (*k > 3)
- *k = 3;
+ req->k = div / 32;
+ if (req->k > 3)
+ req->k = 3;
- *n = DIV_ROUND_UP(div, (*k+1)) - 1;
+ req->n = DIV_ROUND_UP(div, (req->k + 1)) - 1;
}
/**
@@ -488,37 +254,94 @@ static void sun6i_a31_get_pll6_factors(u32 *freq, u32 parent_rate,
* rate = parent_rate >> p
*/
-static void sun5i_a13_get_ahb_factors(u32 *freq, u32 parent_rate,
- u8 *n, u8 *k, u8 *m, u8 *p)
+static void sun5i_a13_get_ahb_factors(struct factors_request *req)
{
u32 div;
/* divide only */
- if (parent_rate < *freq)
- *freq = parent_rate;
+ if (req->parent_rate < req->rate)
+ req->rate = req->parent_rate;
/*
* user manual says valid speed is 8k ~ 276M, but tests show it
* can work at speeds up to 300M, just after reparenting to pll6
*/
- if (*freq < 8000)
- *freq = 8000;
- if (*freq > 300000000)
- *freq = 300000000;
+ if (req->rate < 8000)
+ req->rate = 8000;
+ if (req->rate > 300000000)
+ req->rate = 300000000;
- div = order_base_2(DIV_ROUND_UP(parent_rate, *freq));
+ div = order_base_2(DIV_ROUND_UP(req->parent_rate, req->rate));
/* p = 0 ~ 3 */
if (div > 3)
div = 3;
- *freq = parent_rate >> div;
+ req->rate = req->parent_rate >> div;
- /* we were called to round the frequency, we can now return */
- if (p == NULL)
- return;
+ req->p = div;
+}
+
+#define SUN6I_AHB1_PARENT_PLL6 3
+
+/**
+ * sun6i_a31_get_ahb_factors() - calculates m, p factors for AHB
+ * AHB rate is calculated as follows
+ * rate = parent_rate >> p
+ *
+ * if parent is pll6, then
+ * parent_rate = pll6 rate / (m + 1)
+ */
+
+static void sun6i_get_ahb1_factors(struct factors_request *req)
+{
+ u8 div, calcp, calcm = 1;
+
+ /*
+ * clock can only divide, so we will never be able to achieve
+ * frequencies higher than the parent frequency
+ */
+ if (req->parent_rate && req->rate > req->parent_rate)
+ req->rate = req->parent_rate;
+
+ div = DIV_ROUND_UP(req->parent_rate, req->rate);
+
+ /* calculate pre-divider if parent is pll6 */
+ if (req->parent_index == SUN6I_AHB1_PARENT_PLL6) {
+ if (div < 4)
+ calcp = 0;
+ else if (div / 2 < 4)
+ calcp = 1;
+ else if (div / 4 < 4)
+ calcp = 2;
+ else
+ calcp = 3;
+
+ calcm = DIV_ROUND_UP(div, 1 << calcp);
+ } else {
+ calcp = __roundup_pow_of_two(div);
+ calcp = calcp > 3 ? 3 : calcp;
+ }
- *p = div;
+ req->rate = (req->parent_rate / calcm) >> calcp;
+ req->p = calcp;
+ req->m = calcm - 1;
+}
+
+/**
+ * sun6i_ahb1_recalc() - calculates AHB clock rate from m, p factors and
+ * parent index
+ */
+static void sun6i_ahb1_recalc(struct factors_request *req)
+{
+ req->rate = req->parent_rate;
+
+ /* apply pre-divider first if parent is pll6 */
+ if (req->parent_index == SUN6I_AHB1_PARENT_PLL6)
+ req->rate /= req->m + 1;
+
+ /* clk divider */
+ req->rate >>= req->p;
}
/**
@@ -527,39 +350,34 @@ static void sun5i_a13_get_ahb_factors(u32 *freq, u32 parent_rate,
* rate = (parent_rate >> p) / (m + 1);
*/
-static void sun4i_get_apb1_factors(u32 *freq, u32 parent_rate,
- u8 *n, u8 *k, u8 *m, u8 *p)
+static void sun4i_get_apb1_factors(struct factors_request *req)
{
u8 calcm, calcp;
+ int div;
- if (parent_rate < *freq)
- *freq = parent_rate;
+ if (req->parent_rate < req->rate)
+ req->rate = req->parent_rate;
- parent_rate = DIV_ROUND_UP(parent_rate, *freq);
+ div = DIV_ROUND_UP(req->parent_rate, req->rate);
/* Invalid rate! */
- if (parent_rate > 32)
+ if (div > 32)
return;
- if (parent_rate <= 4)
+ if (div <= 4)
calcp = 0;
- else if (parent_rate <= 8)
+ else if (div <= 8)
calcp = 1;
- else if (parent_rate <= 16)
+ else if (div <= 16)
calcp = 2;
else
calcp = 3;
- calcm = (parent_rate >> calcp) - 1;
+ calcm = (req->parent_rate >> calcp) - 1;
- *freq = (parent_rate >> calcp) / (calcm + 1);
-
- /* we were called to round the frequency, we can now return */
- if (n == NULL)
- return;
-
- *m = calcm;
- *p = calcp;
+ req->rate = (req->parent_rate >> calcp) / (calcm + 1);
+ req->m = calcm;
+ req->p = calcp;
}
@@ -571,17 +389,16 @@ static void sun4i_get_apb1_factors(u32 *freq, u32 parent_rate,
* rate = (parent_rate >> p) / (m + 1);
*/
-static void sun7i_a20_get_out_factors(u32 *freq, u32 parent_rate,
- u8 *n, u8 *k, u8 *m, u8 *p)
+static void sun7i_a20_get_out_factors(struct factors_request *req)
{
u8 div, calcm, calcp;
/* These clocks can only divide, so we will never be able to achieve
* frequencies higher than the parent frequency */
- if (*freq > parent_rate)
- *freq = parent_rate;
+ if (req->rate > req->parent_rate)
+ req->rate = req->parent_rate;
- div = DIV_ROUND_UP(parent_rate, *freq);
+ div = DIV_ROUND_UP(req->parent_rate, req->rate);
if (div < 32)
calcp = 0;
@@ -594,21 +411,16 @@ static void sun7i_a20_get_out_factors(u32 *freq, u32 parent_rate,
calcm = DIV_ROUND_UP(div, 1 << calcp);
- *freq = (parent_rate >> calcp) / calcm;
-
- /* we were called to round the frequency, we can now return */
- if (n == NULL)
- return;
-
- *m = calcm - 1;
- *p = calcp;
+ req->rate = (req->parent_rate >> calcp) / calcm;
+ req->m = calcm - 1;
+ req->p = calcp;
}
/**
* sunxi_factors_clk_setup() - Setup function for factor clocks
*/
-static struct clk_factors_config sun4i_pll1_config = {
+static const struct clk_factors_config sun4i_pll1_config = {
.nshift = 8,
.nwidth = 5,
.kshift = 4,
@@ -619,7 +431,7 @@ static struct clk_factors_config sun4i_pll1_config = {
.pwidth = 2,
};
-static struct clk_factors_config sun6i_a31_pll1_config = {
+static const struct clk_factors_config sun6i_a31_pll1_config = {
.nshift = 8,
.nwidth = 5,
.kshift = 4,
@@ -629,7 +441,7 @@ static struct clk_factors_config sun6i_a31_pll1_config = {
.n_start = 1,
};
-static struct clk_factors_config sun8i_a23_pll1_config = {
+static const struct clk_factors_config sun8i_a23_pll1_config = {
.nshift = 8,
.nwidth = 5,
.kshift = 4,
@@ -641,14 +453,14 @@ static struct clk_factors_config sun8i_a23_pll1_config = {
.n_start = 1,
};
-static struct clk_factors_config sun4i_pll5_config = {
+static const struct clk_factors_config sun4i_pll5_config = {
.nshift = 8,
.nwidth = 5,
.kshift = 4,
.kwidth = 2,
};
-static struct clk_factors_config sun6i_a31_pll6_config = {
+static const struct clk_factors_config sun6i_a31_pll6_config = {
.nshift = 8,
.nwidth = 5,
.kshift = 4,
@@ -656,12 +468,19 @@ static struct clk_factors_config sun6i_a31_pll6_config = {
.n_start = 1,
};
-static struct clk_factors_config sun5i_a13_ahb_config = {
+static const struct clk_factors_config sun5i_a13_ahb_config = {
.pshift = 4,
.pwidth = 2,
};
-static struct clk_factors_config sun4i_apb1_config = {
+static const struct clk_factors_config sun6i_ahb1_config = {
+ .mshift = 6,
+ .mwidth = 2,
+ .pshift = 4,
+ .pwidth = 2,
+};
+
+static const struct clk_factors_config sun4i_apb1_config = {
.mshift = 0,
.mwidth = 5,
.pshift = 16,
@@ -669,7 +488,7 @@ static struct clk_factors_config sun4i_apb1_config = {
};
/* user manual says "n" but it's really "p" */
-static struct clk_factors_config sun7i_a20_out_config = {
+static const struct clk_factors_config sun7i_a20_out_config = {
.mshift = 8,
.mwidth = 5,
.pshift = 20,
@@ -728,6 +547,14 @@ static const struct factors_data sun5i_a13_ahb_data __initconst = {
.getter = sun5i_a13_get_ahb_factors,
};
+static const struct factors_data sun6i_ahb1_data __initconst = {
+ .mux = 12,
+ .muxmask = BIT(1) | BIT(0),
+ .table = &sun6i_ahb1_config,
+ .getter = sun6i_get_ahb1_factors,
+ .recalc = sun6i_ahb1_recalc,
+};
+
static const struct factors_data sun4i_apb1_data __initconst = {
.mux = 24,
.muxmask = BIT(1) | BIT(0),
@@ -758,6 +585,61 @@ static struct clk * __init sunxi_factors_clk_setup(struct device_node *node,
return sunxi_factors_register(node, data, &clk_lock, reg);
}
+static void __init sun4i_pll1_clk_setup(struct device_node *node)
+{
+ sunxi_factors_clk_setup(node, &sun4i_pll1_data);
+}
+CLK_OF_DECLARE(sun4i_pll1, "allwinner,sun4i-a10-pll1-clk",
+ sun4i_pll1_clk_setup);
+
+static void __init sun6i_pll1_clk_setup(struct device_node *node)
+{
+ sunxi_factors_clk_setup(node, &sun6i_a31_pll1_data);
+}
+CLK_OF_DECLARE(sun6i_pll1, "allwinner,sun6i-a31-pll1-clk",
+ sun6i_pll1_clk_setup);
+
+static void __init sun8i_pll1_clk_setup(struct device_node *node)
+{
+ sunxi_factors_clk_setup(node, &sun8i_a23_pll1_data);
+}
+CLK_OF_DECLARE(sun8i_pll1, "allwinner,sun8i-a23-pll1-clk",
+ sun8i_pll1_clk_setup);
+
+static void __init sun7i_pll4_clk_setup(struct device_node *node)
+{
+ sunxi_factors_clk_setup(node, &sun7i_a20_pll4_data);
+}
+CLK_OF_DECLARE(sun7i_pll4, "allwinner,sun7i-a20-pll4-clk",
+ sun7i_pll4_clk_setup);
+
+static void __init sun5i_ahb_clk_setup(struct device_node *node)
+{
+ sunxi_factors_clk_setup(node, &sun5i_a13_ahb_data);
+}
+CLK_OF_DECLARE(sun5i_ahb, "allwinner,sun5i-a13-ahb-clk",
+ sun5i_ahb_clk_setup);
+
+static void __init sun6i_ahb1_clk_setup(struct device_node *node)
+{
+ sunxi_factors_clk_setup(node, &sun6i_ahb1_data);
+}
+CLK_OF_DECLARE(sun6i_a31_ahb1, "allwinner,sun6i-a31-ahb1-clk",
+ sun6i_ahb1_clk_setup);
+
+static void __init sun4i_apb1_clk_setup(struct device_node *node)
+{
+ sunxi_factors_clk_setup(node, &sun4i_apb1_data);
+}
+CLK_OF_DECLARE(sun4i_apb1, "allwinner,sun4i-a10-apb1-clk",
+ sun4i_apb1_clk_setup);
+
+static void __init sun7i_out_clk_setup(struct device_node *node)
+{
+ sunxi_factors_clk_setup(node, &sun7i_a20_out_data);
+}
+CLK_OF_DECLARE(sun7i_out, "allwinner,sun7i-a20-out-clk",
+ sun7i_out_clk_setup);
/**
@@ -782,8 +664,8 @@ static const struct mux_data sun8i_h3_ahb2_mux_data __initconst = {
.shift = 0,
};
-static void __init sunxi_mux_clk_setup(struct device_node *node,
- struct mux_data *data)
+static struct clk * __init sunxi_mux_clk_setup(struct device_node *node,
+ const struct mux_data *data)
{
struct clk *clk;
const char *clk_name = node->name;
@@ -792,21 +674,71 @@ static void __init sunxi_mux_clk_setup(struct device_node *node,
int i;
reg = of_iomap(node, 0);
+ if (!reg) {
+ pr_err("Could not map registers for mux-clk: %s\n",
+ of_node_full_name(node));
+ return NULL;
+ }
i = of_clk_parent_fill(node, parents, SUNXI_MAX_PARENTS);
- of_property_read_string(node, "clock-output-names", &clk_name);
+ if (of_property_read_string(node, "clock-output-names", &clk_name)) {
+ pr_err("%s: could not read clock-output-names from \"%s\"\n",
+ __func__, of_node_full_name(node));
+ goto out_unmap;
+ }
clk = clk_register_mux(NULL, clk_name, parents, i,
CLK_SET_RATE_PARENT, reg,
data->shift, SUNXI_MUX_GATE_WIDTH,
0, &clk_lock);
- if (clk) {
- of_clk_add_provider(node, of_clk_src_simple_get, clk);
- clk_register_clkdev(clk, clk_name, NULL);
+ if (IS_ERR(clk)) {
+ pr_err("%s: failed to register mux clock %s: %ld\n", __func__,
+ clk_name, PTR_ERR(clk));
+ goto out_unmap;
+ }
+
+ if (of_clk_add_provider(node, of_clk_src_simple_get, clk)) {
+ pr_err("%s: failed to add clock provider for %s\n",
+ __func__, clk_name);
+ clk_unregister_divider(clk);
+ goto out_unmap;
}
+
+ return clk;
+out_unmap:
+ iounmap(reg);
+ return NULL;
+}
+
+static void __init sun4i_cpu_clk_setup(struct device_node *node)
+{
+ struct clk *clk;
+
+ clk = sunxi_mux_clk_setup(node, &sun4i_cpu_mux_data);
+ if (!clk)
+ return;
+
+ /* Protect CPU clock */
+ __clk_get(clk);
+ clk_prepare_enable(clk);
+}
+CLK_OF_DECLARE(sun4i_cpu, "allwinner,sun4i-a10-cpu-clk",
+ sun4i_cpu_clk_setup);
+
+static void __init sun6i_ahb1_mux_clk_setup(struct device_node *node)
+{
+ sunxi_mux_clk_setup(node, &sun6i_a31_ahb1_mux_data);
}
+CLK_OF_DECLARE(sun6i_ahb1_mux, "allwinner,sun6i-a31-ahb1-mux-clk",
+ sun6i_ahb1_mux_clk_setup);
+static void __init sun8i_ahb2_clk_setup(struct device_node *node)
+{
+ sunxi_mux_clk_setup(node, &sun8i_h3_ahb2_mux_data);
+}
+CLK_OF_DECLARE(sun8i_ahb2, "allwinner,sun8i-h3-ahb2-clk",
+ sun8i_ahb2_clk_setup);
/**
@@ -865,7 +797,7 @@ static const struct div_data sun4i_apb0_data __initconst = {
};
static void __init sunxi_divider_clk_setup(struct device_node *node,
- struct div_data *data)
+ const struct div_data *data)
{
struct clk *clk;
const char *clk_name = node->name;
@@ -873,21 +805,77 @@ static void __init sunxi_divider_clk_setup(struct device_node *node,
void __iomem *reg;
reg = of_iomap(node, 0);
+ if (!reg) {
+ pr_err("Could not map registers for mux-clk: %s\n",
+ of_node_full_name(node));
+ return;
+ }
clk_parent = of_clk_get_parent_name(node, 0);
- of_property_read_string(node, "clock-output-names", &clk_name);
+ if (of_property_read_string(node, "clock-output-names", &clk_name)) {
+ pr_err("%s: could not read clock-output-names from \"%s\"\n",
+ __func__, of_node_full_name(node));
+ goto out_unmap;
+ }
clk = clk_register_divider_table(NULL, clk_name, clk_parent, 0,
reg, data->shift, data->width,
data->pow ? CLK_DIVIDER_POWER_OF_TWO : 0,
data->table, &clk_lock);
- if (clk) {
- of_clk_add_provider(node, of_clk_src_simple_get, clk);
- clk_register_clkdev(clk, clk_name, NULL);
+ if (IS_ERR(clk)) {
+ pr_err("%s: failed to register divider clock %s: %ld\n",
+ __func__, clk_name, PTR_ERR(clk));
+ goto out_unmap;
}
+
+ if (of_clk_add_provider(node, of_clk_src_simple_get, clk)) {
+ pr_err("%s: failed to add clock provider for %s\n",
+ __func__, clk_name);
+ goto out_unregister;
+ }
+
+ if (clk_register_clkdev(clk, clk_name, NULL)) {
+ of_clk_del_provider(node);
+ goto out_unregister;
+ }
+
+ return;
+out_unregister:
+ clk_unregister_divider(clk);
+
+out_unmap:
+ iounmap(reg);
}
+static void __init sun4i_ahb_clk_setup(struct device_node *node)
+{
+ sunxi_divider_clk_setup(node, &sun4i_ahb_data);
+}
+CLK_OF_DECLARE(sun4i_ahb, "allwinner,sun4i-a10-ahb-clk",
+ sun4i_ahb_clk_setup);
+
+static void __init sun4i_apb0_clk_setup(struct device_node *node)
+{
+ sunxi_divider_clk_setup(node, &sun4i_apb0_data);
+}
+CLK_OF_DECLARE(sun4i_apb0, "allwinner,sun4i-a10-apb0-clk",
+ sun4i_apb0_clk_setup);
+
+static void __init sun4i_axi_clk_setup(struct device_node *node)
+{
+ sunxi_divider_clk_setup(node, &sun4i_axi_data);
+}
+CLK_OF_DECLARE(sun4i_axi, "allwinner,sun4i-a10-axi-clk",
+ sun4i_axi_clk_setup);
+
+static void __init sun8i_axi_clk_setup(struct device_node *node)
+{
+ sunxi_divider_clk_setup(node, &sun8i_a23_axi_data);
+}
+CLK_OF_DECLARE(sun8i_axi, "allwinner,sun8i-a23-axi-clk",
+ sun8i_axi_clk_setup);
+
/**
@@ -975,8 +963,8 @@ static const struct divs_data sun6i_a31_pll6_divs_data __initconst = {
* |________________________|
*/
-static void __init sunxi_divs_clk_setup(struct device_node *node,
- struct divs_data *data)
+static struct clk ** __init sunxi_divs_clk_setup(struct device_node *node,
+ const struct divs_data *data)
{
struct clk_onecell_data *clk_data;
const char *parent;
@@ -997,13 +985,20 @@ static void __init sunxi_divs_clk_setup(struct device_node *node,
/* Set up factor clock that we will be dividing */
pclk = sunxi_factors_clk_setup(node, data->factors);
+ if (!pclk)
+ return NULL;
parent = __clk_get_name(pclk);
reg = of_iomap(node, 0);
+ if (!reg) {
+ pr_err("Could not map registers for divs-clk: %s\n",
+ of_node_full_name(node));
+ return NULL;
+ }
clk_data = kmalloc(sizeof(struct clk_onecell_data), GFP_KERNEL);
if (!clk_data)
- return;
+ goto out_unmap;
clks = kcalloc(ndivs, sizeof(*clks), GFP_KERNEL);
if (!clks)
@@ -1081,146 +1076,54 @@ static void __init sunxi_divs_clk_setup(struct device_node *node,
clkflags);
WARN_ON(IS_ERR(clk_data->clks[i]));
- clk_register_clkdev(clks[i], clk_name, NULL);
}
/* Adjust to the real max */
clk_data->clk_num = i;
- of_clk_add_provider(node, of_clk_src_onecell_get, clk_data);
-
- return;
+ if (of_clk_add_provider(node, of_clk_src_onecell_get, clk_data)) {
+ pr_err("%s: failed to add clock provider for %s\n",
+ __func__, clk_name);
+ goto free_gate;
+ }
+ return clks;
free_gate:
kfree(gate);
free_clks:
kfree(clks);
free_clkdata:
kfree(clk_data);
+out_unmap:
+ iounmap(reg);
+ return NULL;
}
-
-
-/* Matches for factors clocks */
-static const struct of_device_id clk_factors_match[] __initconst = {
- {.compatible = "allwinner,sun4i-a10-pll1-clk", .data = &sun4i_pll1_data,},
- {.compatible = "allwinner,sun6i-a31-pll1-clk", .data = &sun6i_a31_pll1_data,},
- {.compatible = "allwinner,sun8i-a23-pll1-clk", .data = &sun8i_a23_pll1_data,},
- {.compatible = "allwinner,sun7i-a20-pll4-clk", .data = &sun7i_a20_pll4_data,},
- {.compatible = "allwinner,sun5i-a13-ahb-clk", .data = &sun5i_a13_ahb_data,},
- {.compatible = "allwinner,sun4i-a10-apb1-clk", .data = &sun4i_apb1_data,},
- {.compatible = "allwinner,sun7i-a20-out-clk", .data = &sun7i_a20_out_data,},
- {}
-};
-
-/* Matches for divider clocks */
-static const struct of_device_id clk_div_match[] __initconst = {
- {.compatible = "allwinner,sun4i-a10-axi-clk", .data = &sun4i_axi_data,},
- {.compatible = "allwinner,sun8i-a23-axi-clk", .data = &sun8i_a23_axi_data,},
- {.compatible = "allwinner,sun4i-a10-ahb-clk", .data = &sun4i_ahb_data,},
- {.compatible = "allwinner,sun4i-a10-apb0-clk", .data = &sun4i_apb0_data,},
- {}
-};
-
-/* Matches for divided outputs */
-static const struct of_device_id clk_divs_match[] __initconst = {
- {.compatible = "allwinner,sun4i-a10-pll5-clk", .data = &pll5_divs_data,},
- {.compatible = "allwinner,sun4i-a10-pll6-clk", .data = &pll6_divs_data,},
- {.compatible = "allwinner,sun6i-a31-pll6-clk", .data = &sun6i_a31_pll6_divs_data,},
- {}
-};
-
-/* Matches for mux clocks */
-static const struct of_device_id clk_mux_match[] __initconst = {
- {.compatible = "allwinner,sun4i-a10-cpu-clk", .data = &sun4i_cpu_mux_data,},
- {.compatible = "allwinner,sun6i-a31-ahb1-mux-clk", .data = &sun6i_a31_ahb1_mux_data,},
- {.compatible = "allwinner,sun8i-h3-ahb2-clk", .data = &sun8i_h3_ahb2_mux_data,},
- {}
-};
-
-
-static void __init of_sunxi_table_clock_setup(const struct of_device_id *clk_match,
- void *function)
-{
- struct device_node *np;
- const struct div_data *data;
- const struct of_device_id *match;
- void (*setup_function)(struct device_node *, const void *) = function;
-
- for_each_matching_node_and_match(np, clk_match, &match) {
- data = match->data;
- setup_function(np, data);
- }
-}
-
-static void __init sunxi_init_clocks(const char *clocks[], int nclocks)
-{
- unsigned int i;
-
- /* Register divided output clocks */
- of_sunxi_table_clock_setup(clk_divs_match, sunxi_divs_clk_setup);
-
- /* Register factor clocks */
- of_sunxi_table_clock_setup(clk_factors_match, sunxi_factors_clk_setup);
-
- /* Register divider clocks */
- of_sunxi_table_clock_setup(clk_div_match, sunxi_divider_clk_setup);
-
- /* Register mux clocks */
- of_sunxi_table_clock_setup(clk_mux_match, sunxi_mux_clk_setup);
-
- /* Protect the clocks that needs to stay on */
- for (i = 0; i < nclocks; i++) {
- struct clk *clk = clk_get(NULL, clocks[i]);
-
- if (!IS_ERR(clk))
- clk_prepare_enable(clk);
- }
-}
-
-static const char *sun4i_a10_critical_clocks[] __initdata = {
- "pll5_ddr",
-};
-
-static void __init sun4i_a10_init_clocks(struct device_node *node)
+static void __init sun4i_pll5_clk_setup(struct device_node *node)
{
- sunxi_init_clocks(sun4i_a10_critical_clocks,
- ARRAY_SIZE(sun4i_a10_critical_clocks));
-}
-CLK_OF_DECLARE(sun4i_a10_clk_init, "allwinner,sun4i-a10", sun4i_a10_init_clocks);
+ struct clk **clks;
-static const char *sun5i_critical_clocks[] __initdata = {
- "cpu",
- "pll5_ddr",
-};
+ clks = sunxi_divs_clk_setup(node, &pll5_divs_data);
+ if (!clks)
+ return;
-static void __init sun5i_init_clocks(struct device_node *node)
-{
- sunxi_init_clocks(sun5i_critical_clocks,
- ARRAY_SIZE(sun5i_critical_clocks));
+ /* Protect PLL5_DDR */
+ __clk_get(clks[0]);
+ clk_prepare_enable(clks[0]);
}
-CLK_OF_DECLARE(sun5i_a10s_clk_init, "allwinner,sun5i-a10s", sun5i_init_clocks);
-CLK_OF_DECLARE(sun5i_a13_clk_init, "allwinner,sun5i-a13", sun5i_init_clocks);
-CLK_OF_DECLARE(sun5i_r8_clk_init, "allwinner,sun5i-r8", sun5i_init_clocks);
-CLK_OF_DECLARE(sun7i_a20_clk_init, "allwinner,sun7i-a20", sun5i_init_clocks);
-
-static const char *sun6i_critical_clocks[] __initdata = {
- "cpu",
-};
+CLK_OF_DECLARE(sun4i_pll5, "allwinner,sun4i-a10-pll5-clk",
+ sun4i_pll5_clk_setup);
-static void __init sun6i_init_clocks(struct device_node *node)
+static void __init sun4i_pll6_clk_setup(struct device_node *node)
{
- sunxi_init_clocks(sun6i_critical_clocks,
- ARRAY_SIZE(sun6i_critical_clocks));
+ sunxi_divs_clk_setup(node, &pll6_divs_data);
}
-CLK_OF_DECLARE(sun6i_a31_clk_init, "allwinner,sun6i-a31", sun6i_init_clocks);
-CLK_OF_DECLARE(sun6i_a31s_clk_init, "allwinner,sun6i-a31s", sun6i_init_clocks);
-CLK_OF_DECLARE(sun8i_a23_clk_init, "allwinner,sun8i-a23", sun6i_init_clocks);
-CLK_OF_DECLARE(sun8i_a33_clk_init, "allwinner,sun8i-a33", sun6i_init_clocks);
-CLK_OF_DECLARE(sun8i_h3_clk_init, "allwinner,sun8i-h3", sun6i_init_clocks);
+CLK_OF_DECLARE(sun4i_pll6, "allwinner,sun4i-a10-pll6-clk",
+ sun4i_pll6_clk_setup);
-static void __init sun9i_init_clocks(struct device_node *node)
+static void __init sun6i_pll6_clk_setup(struct device_node *node)
{
- sunxi_init_clocks(NULL, 0);
+ sunxi_divs_clk_setup(node, &sun6i_a31_pll6_divs_data);
}
-CLK_OF_DECLARE(sun9i_a80_clk_init, "allwinner,sun9i-a80", sun9i_init_clocks);
+CLK_OF_DECLARE(sun6i_pll6, "allwinner,sun6i-a31-pll6-clk",
+ sun6i_pll6_clk_setup);
diff --git a/drivers/clk/sunxi/clk-usb.c b/drivers/clk/sunxi/clk-usb.c
index 67b8e38f4ee9..5432b1c198a4 100644
--- a/drivers/clk/sunxi/clk-usb.c
+++ b/drivers/clk/sunxi/clk-usb.c
@@ -216,6 +216,18 @@ static void __init sun8i_a23_usb_setup(struct device_node *node)
}
CLK_OF_DECLARE(sun8i_a23_usb, "allwinner,sun8i-a23-usb-clk", sun8i_a23_usb_setup);
+static const struct usb_clk_data sun8i_h3_usb_clk_data __initconst = {
+ .clk_mask = BIT(19) | BIT(18) | BIT(17) | BIT(16) |
+ BIT(11) | BIT(10) | BIT(9) | BIT(8),
+ .reset_mask = BIT(3) | BIT(2) | BIT(1) | BIT(0),
+};
+
+static void __init sun8i_h3_usb_setup(struct device_node *node)
+{
+ sunxi_usb_clk_setup(node, &sun8i_h3_usb_clk_data, &sun4i_a10_usb_lock);
+}
+CLK_OF_DECLARE(sun8i_h3_usb, "allwinner,sun8i-h3-usb-clk", sun8i_h3_usb_setup);
+
static const struct usb_clk_data sun9i_a80_usb_mod_data __initconst = {
.clk_mask = BIT(6) | BIT(5) | BIT(4) | BIT(3) | BIT(2) | BIT(1),
.reset_mask = BIT(19) | BIT(18) | BIT(17),
@@ -243,15 +255,3 @@ static void __init sun9i_a80_usb_phy_setup(struct device_node *node)
sunxi_usb_clk_setup(node, &sun9i_a80_usb_phy_data, &a80_usb_phy_lock);
}
CLK_OF_DECLARE(sun9i_a80_usb_phy, "allwinner,sun9i-a80-usb-phy-clk", sun9i_a80_usb_phy_setup);
-
-static const struct usb_clk_data sun8i_h3_usb_clk_data __initconst = {
- .clk_mask = BIT(19) | BIT(18) | BIT(17) | BIT(16) |
- BIT(11) | BIT(10) | BIT(9) | BIT(8),
- .reset_mask = BIT(3) | BIT(2) | BIT(1) | BIT(0),
-};
-
-static void __init sun8i_h3_usb_setup(struct device_node *node)
-{
- sunxi_usb_clk_setup(node, &sun8i_h3_usb_clk_data, &sun4i_a10_usb_lock);
-}
-CLK_OF_DECLARE(sun8i_h3_usb, "allwinner,sun8i-h3-usb-clk", sun8i_h3_usb_setup);