|
@@ -19,6 +19,7 @@
|
|
#include <linux/of.h>
|
|
#include <linux/of.h>
|
|
#include <linux/of_address.h>
|
|
#include <linux/of_address.h>
|
|
#include <linux/reset-controller.h>
|
|
#include <linux/reset-controller.h>
|
|
|
|
+#include <linux/spinlock.h>
|
|
|
|
|
|
#include "clk-factors.h"
|
|
#include "clk-factors.h"
|
|
|
|
|
|
@@ -319,46 +320,6 @@ static void sun4i_get_apb1_factors(u32 *freq, u32 parent_rate,
|
|
|
|
|
|
|
|
|
|
|
|
|
|
-/**
|
|
|
|
- * sun4i_get_mod0_factors() - calculates m, n factors for MOD0-style clocks
|
|
|
|
- * MOD0 rate is calculated as follows
|
|
|
|
- * rate = (parent_rate >> p) / (m + 1);
|
|
|
|
- */
|
|
|
|
-
|
|
|
|
-static void sun4i_get_mod0_factors(u32 *freq, u32 parent_rate,
|
|
|
|
- u8 *n, u8 *k, u8 *m, u8 *p)
|
|
|
|
-{
|
|
|
|
- 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;
|
|
|
|
-
|
|
|
|
- div = DIV_ROUND_UP(parent_rate, *freq);
|
|
|
|
-
|
|
|
|
- if (div < 16)
|
|
|
|
- calcp = 0;
|
|
|
|
- else if (div / 2 < 16)
|
|
|
|
- calcp = 1;
|
|
|
|
- else if (div / 4 < 16)
|
|
|
|
- calcp = 2;
|
|
|
|
- else
|
|
|
|
- calcp = 3;
|
|
|
|
-
|
|
|
|
- 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;
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-
|
|
|
|
|
|
|
|
/**
|
|
/**
|
|
* sun7i_a20_get_out_factors() - calculates m, p factors for CLK_OUT_A/B
|
|
* sun7i_a20_get_out_factors() - calculates m, p factors for CLK_OUT_A/B
|
|
@@ -440,16 +401,6 @@ EXPORT_SYMBOL(clk_sunxi_mmc_phase_control);
|
|
* sunxi_factors_clk_setup() - Setup function for factor clocks
|
|
* sunxi_factors_clk_setup() - Setup function for factor clocks
|
|
*/
|
|
*/
|
|
|
|
|
|
-#define SUNXI_FACTORS_MUX_MASK 0x3
|
|
|
|
-
|
|
|
|
-struct factors_data {
|
|
|
|
- int enable;
|
|
|
|
- int mux;
|
|
|
|
- struct clk_factors_config *table;
|
|
|
|
- void (*getter) (u32 *rate, u32 parent_rate, u8 *n, u8 *k, u8 *m, u8 *p);
|
|
|
|
- const char *name;
|
|
|
|
-};
|
|
|
|
-
|
|
|
|
static struct clk_factors_config sun4i_pll1_config = {
|
|
static struct clk_factors_config sun4i_pll1_config = {
|
|
.nshift = 8,
|
|
.nshift = 8,
|
|
.nwidth = 5,
|
|
.nwidth = 5,
|
|
@@ -503,14 +454,6 @@ static struct clk_factors_config sun4i_apb1_config = {
|
|
.pwidth = 2,
|
|
.pwidth = 2,
|
|
};
|
|
};
|
|
|
|
|
|
-/* user manual says "n" but it's really "p" */
|
|
|
|
-static struct clk_factors_config sun4i_mod0_config = {
|
|
|
|
- .mshift = 0,
|
|
|
|
- .mwidth = 4,
|
|
|
|
- .pshift = 16,
|
|
|
|
- .pwidth = 2,
|
|
|
|
-};
|
|
|
|
-
|
|
|
|
/* user manual says "n" but it's really "p" */
|
|
/* user manual says "n" but it's really "p" */
|
|
static struct clk_factors_config sun7i_a20_out_config = {
|
|
static struct clk_factors_config sun7i_a20_out_config = {
|
|
.mshift = 8,
|
|
.mshift = 8,
|
|
@@ -568,13 +511,6 @@ static const struct factors_data sun4i_apb1_data __initconst = {
|
|
.getter = sun4i_get_apb1_factors,
|
|
.getter = sun4i_get_apb1_factors,
|
|
};
|
|
};
|
|
|
|
|
|
-static const struct factors_data sun4i_mod0_data __initconst = {
|
|
|
|
- .enable = 31,
|
|
|
|
- .mux = 24,
|
|
|
|
- .table = &sun4i_mod0_config,
|
|
|
|
- .getter = sun4i_get_mod0_factors,
|
|
|
|
-};
|
|
|
|
-
|
|
|
|
static const struct factors_data sun7i_a20_out_data __initconst = {
|
|
static const struct factors_data sun7i_a20_out_data __initconst = {
|
|
.enable = 31,
|
|
.enable = 31,
|
|
.mux = 24,
|
|
.mux = 24,
|
|
@@ -583,89 +519,9 @@ static const struct factors_data sun7i_a20_out_data __initconst = {
|
|
};
|
|
};
|
|
|
|
|
|
static struct clk * __init sunxi_factors_clk_setup(struct device_node *node,
|
|
static struct clk * __init sunxi_factors_clk_setup(struct device_node *node,
|
|
- const struct factors_data *data)
|
|
|
|
|
|
+ const struct factors_data *data)
|
|
{
|
|
{
|
|
- struct clk *clk;
|
|
|
|
- struct clk_factors *factors;
|
|
|
|
- struct clk_gate *gate = NULL;
|
|
|
|
- struct clk_mux *mux = NULL;
|
|
|
|
- struct clk_hw *gate_hw = NULL;
|
|
|
|
- struct clk_hw *mux_hw = NULL;
|
|
|
|
- const char *clk_name = node->name;
|
|
|
|
- const char *parents[SUNXI_MAX_PARENTS];
|
|
|
|
- void __iomem *reg;
|
|
|
|
- int i = 0;
|
|
|
|
-
|
|
|
|
- reg = of_iomap(node, 0);
|
|
|
|
-
|
|
|
|
- /* if we have a mux, we will have >1 parents */
|
|
|
|
- while (i < SUNXI_MAX_PARENTS &&
|
|
|
|
- (parents[i] = of_clk_get_parent_name(node, i)) != NULL)
|
|
|
|
- i++;
|
|
|
|
-
|
|
|
|
- /*
|
|
|
|
- * some factor clocks, such as pll5 and pll6, may have multiple
|
|
|
|
- * outputs, and have their name designated in factors_data
|
|
|
|
- */
|
|
|
|
- if (data->name)
|
|
|
|
- clk_name = data->name;
|
|
|
|
- else
|
|
|
|
- of_property_read_string(node, "clock-output-names", &clk_name);
|
|
|
|
-
|
|
|
|
- factors = kzalloc(sizeof(struct clk_factors), GFP_KERNEL);
|
|
|
|
- if (!factors)
|
|
|
|
- return NULL;
|
|
|
|
-
|
|
|
|
- /* 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;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- /* set up gate properties */
|
|
|
|
- gate->reg = reg;
|
|
|
|
- gate->bit_idx = data->enable;
|
|
|
|
- gate->lock = &clk_lock;
|
|
|
|
- gate_hw = &gate->hw;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- /* 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;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- /* set up gate properties */
|
|
|
|
- mux->reg = reg;
|
|
|
|
- mux->shift = data->mux;
|
|
|
|
- mux->mask = SUNXI_FACTORS_MUX_MASK;
|
|
|
|
- mux->lock = &clk_lock;
|
|
|
|
- mux_hw = &mux->hw;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- /* set up factors properties */
|
|
|
|
- factors->reg = reg;
|
|
|
|
- factors->config = data->table;
|
|
|
|
- factors->get_factors = data->getter;
|
|
|
|
- factors->lock = &clk_lock;
|
|
|
|
-
|
|
|
|
- clk = clk_register_composite(NULL, clk_name,
|
|
|
|
- parents, i,
|
|
|
|
- mux_hw, &clk_mux_ops,
|
|
|
|
- &factors->hw, &clk_factors_ops,
|
|
|
|
- gate_hw, &clk_gate_ops, 0);
|
|
|
|
-
|
|
|
|
- if (!IS_ERR(clk)) {
|
|
|
|
- of_clk_add_provider(node, of_clk_src_simple_get, clk);
|
|
|
|
- clk_register_clkdev(clk, clk_name, NULL);
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- return clk;
|
|
|
|
|
|
+ return sunxi_factors_register(node, data, &clk_lock);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
@@ -762,10 +618,19 @@ static const struct div_data sun4i_ahb_data __initconst = {
|
|
.width = 2,
|
|
.width = 2,
|
|
};
|
|
};
|
|
|
|
|
|
|
|
+static const struct clk_div_table sun4i_apb0_table[] __initconst = {
|
|
|
|
+ { .val = 0, .div = 2 },
|
|
|
|
+ { .val = 1, .div = 2 },
|
|
|
|
+ { .val = 2, .div = 4 },
|
|
|
|
+ { .val = 3, .div = 8 },
|
|
|
|
+ { } /* sentinel */
|
|
|
|
+};
|
|
|
|
+
|
|
static const struct div_data sun4i_apb0_data __initconst = {
|
|
static const struct div_data sun4i_apb0_data __initconst = {
|
|
.shift = 8,
|
|
.shift = 8,
|
|
.pow = 1,
|
|
.pow = 1,
|
|
.width = 2,
|
|
.width = 2,
|
|
|
|
+ .table = sun4i_apb0_table,
|
|
};
|
|
};
|
|
|
|
|
|
static const struct div_data sun6i_a31_apb2_div_data __initconst = {
|
|
static const struct div_data sun6i_a31_apb2_div_data __initconst = {
|
|
@@ -1199,7 +1064,6 @@ static const struct of_device_id clk_factors_match[] __initconst = {
|
|
{.compatible = "allwinner,sun7i-a20-pll4-clk", .data = &sun7i_a20_pll4_data,},
|
|
{.compatible = "allwinner,sun7i-a20-pll4-clk", .data = &sun7i_a20_pll4_data,},
|
|
{.compatible = "allwinner,sun6i-a31-pll6-clk", .data = &sun6i_a31_pll6_data,},
|
|
{.compatible = "allwinner,sun6i-a31-pll6-clk", .data = &sun6i_a31_pll6_data,},
|
|
{.compatible = "allwinner,sun4i-a10-apb1-clk", .data = &sun4i_apb1_data,},
|
|
{.compatible = "allwinner,sun4i-a10-apb1-clk", .data = &sun4i_apb1_data,},
|
|
- {.compatible = "allwinner,sun4i-a10-mod0-clk", .data = &sun4i_mod0_data,},
|
|
|
|
{.compatible = "allwinner,sun7i-a20-out-clk", .data = &sun7i_a20_out_data,},
|
|
{.compatible = "allwinner,sun7i-a20-out-clk", .data = &sun7i_a20_out_data,},
|
|
{}
|
|
{}
|
|
};
|
|
};
|
|
@@ -1311,7 +1175,6 @@ static void __init sun4i_a10_init_clocks(struct device_node *node)
|
|
CLK_OF_DECLARE(sun4i_a10_clk_init, "allwinner,sun4i-a10", sun4i_a10_init_clocks);
|
|
CLK_OF_DECLARE(sun4i_a10_clk_init, "allwinner,sun4i-a10", sun4i_a10_init_clocks);
|
|
|
|
|
|
static const char *sun5i_critical_clocks[] __initdata = {
|
|
static const char *sun5i_critical_clocks[] __initdata = {
|
|
- "mbus",
|
|
|
|
"pll5_ddr",
|
|
"pll5_ddr",
|
|
"ahb_sdram",
|
|
"ahb_sdram",
|
|
};
|
|
};
|