diff --git a/Documentation/devicetree/bindings/clock/sunxi.txt b/Documentation/devicetree/bindings/clock/sunxi.txt index 46d8433b2a8c..c2cb7621ad2d 100644 --- a/Documentation/devicetree/bindings/clock/sunxi.txt +++ b/Documentation/devicetree/bindings/clock/sunxi.txt @@ -36,6 +36,7 @@ Required properties: "allwinner,sun6i-a31-apb2-div-clk" - for the APB2 gates on A31 "allwinner,sun6i-a31-apb2-gates-clk" - for the APB2 gates on A31 "allwinner,sun4i-mod0-clk" - for the module 0 family of clocks + "allwinner,sun7i-a20-out-clk" - for the external output clocks Required properties for all clocks: - reg : shall be the control register address for the clock. diff --git a/drivers/clk/sunxi/clk-sunxi.c b/drivers/clk/sunxi/clk-sunxi.c index 124113e33f06..659e4ea31893 100644 --- a/drivers/clk/sunxi/clk-sunxi.c +++ b/drivers/clk/sunxi/clk-sunxi.c @@ -335,6 +335,47 @@ static void sun4i_get_mod0_factors(u32 *freq, u32 parent_rate, +/** + * sun7i_a20_get_out_factors() - calculates m, p factors for CLK_OUT_A/B + * CLK_OUT rate is calculated as follows + * 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) +{ + 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 = parent_rate / *freq; + + if (div < 32) + calcp = 0; + else if (div / 2 < 32) + calcp = 1; + else if (div / 4 < 32) + 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; +} + + + /** * sunxi_factors_clk_setup() - Setup function for factor clocks */ @@ -390,6 +431,14 @@ static struct clk_factors_config sun4i_mod0_config = { .pwidth = 2, }; +/* user manual says "n" but it's really "p" */ +static struct clk_factors_config sun7i_a20_out_config = { + .mshift = 8, + .mwidth = 5, + .pshift = 20, + .pwidth = 2, +}; + static const struct factors_data sun4i_pll1_data __initconst = { .enable = 31, .table = &sun4i_pll1_config, @@ -420,6 +469,13 @@ static const struct factors_data sun4i_mod0_data __initconst = { .getter = sun4i_get_mod0_factors, }; +static const struct factors_data sun7i_a20_out_data __initconst = { + .enable = 31, + .mux = 24, + .table = &sun7i_a20_out_config, + .getter = sun7i_a20_get_out_factors, +}; + static struct clk * __init sunxi_factors_clk_setup(struct device_node *node, const struct factors_data *data) { @@ -918,6 +974,7 @@ static const struct of_device_id clk_factors_match[] __initconst = { {.compatible = "allwinner,sun6i-a31-pll1-clk", .data = &sun6i_a31_pll1_data,}, {.compatible = "allwinner,sun4i-apb1-clk", .data = &sun4i_apb1_data,}, {.compatible = "allwinner,sun4i-mod0-clk", .data = &sun4i_mod0_data,}, + {.compatible = "allwinner,sun7i-a20-out-clk", .data = &sun7i_a20_out_data,}, {} };