diff options
author | Chen-Yu Tsai <wens@csie.org> | 2014-06-26 19:55:43 +0400 |
---|---|---|
committer | Maxime Ripard <maxime.ripard@free-electrons.com> | 2014-07-04 14:05:17 +0400 |
commit | 515c1a4bdcd9b55e2c21e897a9ca276bd708d145 (patch) | |
tree | 8e6b064a92ac32dd444a94ab49d2bdacc371c609 /drivers/clk/sunxi | |
parent | ea5671bffbb2b6eefdce7e467a162ae2eef032ac (diff) | |
download | linux-515c1a4bdcd9b55e2c21e897a9ca276bd708d145.tar.xz |
clk: sunxi: Add A23 clocks support
The clock control unit on the A23 is similar to the one found on the A31.
The AHB1, APB1, APB2 gates on the A23 are almost identical to the ones
on the A31, but some outputs are missing.
The main CPU PLL (PLL1) however is like that on older Allwinner SoCs,
such as the A10 or A20, but the N factor starts from 1 instead of 0.
This patch adds support for PLL1 and all the basic clock muxes and gates.
Signed-off-by: Chen-Yu Tsai <wens@csie.org>
Acked-by: Maxime Ripard <maxime.ripard@free-electrons.com>
Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com>
Diffstat (limited to 'drivers/clk/sunxi')
-rw-r--r-- | drivers/clk/sunxi/clk-sunxi.c | 101 |
1 files changed, 101 insertions, 0 deletions
diff --git a/drivers/clk/sunxi/clk-sunxi.c b/drivers/clk/sunxi/clk-sunxi.c index 6fe9492f84ad..8975972f6da5 100644 --- a/drivers/clk/sunxi/clk-sunxi.c +++ b/drivers/clk/sunxi/clk-sunxi.c @@ -164,6 +164,54 @@ static void sun6i_a31_get_pll1_factors(u32 *freq, u32 parent_rate, } /** + * sun8i_a23_get_pll1_factors() - calculates n, k, m, p factors for PLL1 + * PLL1 rate is calculated as follows + * rate = (parent_rate * (n + 1) * (k + 1) >> p) / (m + 1); + * 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) +{ + 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; + + /* m is always zero for pll1 */ + *m = 0; + + /* k is 1 only on these cases */ + if (*freq >= 768000000 || *freq == 42000000 || *freq == 54000000) + *k = 1; + else + *k = 0; + + /* p will be 2 for divs under 20 and odd divs under 32 */ + if (div < 20 || (div < 32 && (div & 1))) + *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; + + /* any other entries have p = 0 */ + else + *p = 0; + + /* calculate a suitable n based on k and p */ + div <<= *p; + div /= (*k + 1); + *n = div / 4 - 1; +} + +/** * sun4i_get_pll5_factors() - calculates n, k factors for PLL5 * PLL5 rate is calculated as follows * rate = parent_rate * n * (k + 1) @@ -422,6 +470,18 @@ static struct clk_factors_config sun6i_a31_pll1_config = { .mwidth = 2, }; +static struct clk_factors_config sun8i_a23_pll1_config = { + .nshift = 8, + .nwidth = 5, + .kshift = 4, + .kwidth = 2, + .mshift = 0, + .mwidth = 2, + .pshift = 16, + .pwidth = 2, + .n_start = 1, +}; + static struct clk_factors_config sun4i_pll5_config = { .nshift = 8, .nwidth = 5, @@ -471,6 +531,12 @@ static const struct factors_data sun6i_a31_pll1_data __initconst = { .getter = sun6i_a31_get_pll1_factors, }; +static const struct factors_data sun8i_a23_pll1_data __initconst = { + .enable = 31, + .table = &sun8i_a23_pll1_config, + .getter = sun8i_a23_get_pll1_factors, +}; + static const struct factors_data sun7i_a20_pll4_data __initconst = { .enable = 31, .table = &sun4i_pll5_config, @@ -673,6 +739,23 @@ static const struct div_data sun4i_axi_data __initconst = { .width = 2, }; +static const struct clk_div_table sun8i_a23_axi_table[] __initconst = { + { .val = 0, .div = 1 }, + { .val = 1, .div = 2 }, + { .val = 2, .div = 3 }, + { .val = 3, .div = 4 }, + { .val = 4, .div = 4 }, + { .val = 5, .div = 4 }, + { .val = 6, .div = 4 }, + { .val = 7, .div = 4 }, + { } /* sentinel */ +}; + +static const struct div_data sun8i_a23_axi_data __initconst = { + .width = 3, + .table = sun8i_a23_axi_table, +}; + static const struct div_data sun4i_ahb_data __initconst = { .shift = 4, .pow = 1, @@ -805,6 +888,10 @@ static const struct gates_data sun7i_a20_ahb_gates_data __initconst = { .mask = { 0x12f77fff, 0x16ff3f }, }; +static const struct gates_data sun8i_a23_ahb1_gates_data __initconst = { + .mask = {0x25386742, 0x2505111}, +}; + static const struct gates_data sun4i_apb0_gates_data __initconst = { .mask = {0x4EF}, }; @@ -837,6 +924,10 @@ static const struct gates_data sun6i_a31_apb1_gates_data __initconst = { .mask = {0x3031}, }; +static const struct gates_data sun8i_a23_apb1_gates_data __initconst = { + .mask = {0x3021}, +}; + static const struct gates_data sun6i_a31_apb2_gates_data __initconst = { .mask = {0x3F000F}, }; @@ -845,6 +936,10 @@ static const struct gates_data sun7i_a20_apb1_gates_data __initconst = { .mask = { 0xff80ff }, }; +static const struct gates_data sun8i_a23_apb2_gates_data __initconst = { + .mask = {0x1F0007}, +}; + static const struct gates_data sun4i_a10_usb_gates_data __initconst = { .mask = {0x1C0}, .reset_mask = 0x07, @@ -1100,6 +1195,7 @@ free_clkdata: 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,sun6i-a31-pll6-clk", .data = &sun6i_a31_pll6_data,}, {.compatible = "allwinner,sun4i-a10-apb1-clk", .data = &sun4i_apb1_data,}, @@ -1111,6 +1207,7 @@ static const struct of_device_id clk_factors_match[] __initconst = { /* 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,}, {.compatible = "allwinner,sun6i-a31-apb2-div-clk", .data = &sun6i_a31_apb2_div_data,}, @@ -1140,6 +1237,7 @@ static const struct of_device_id clk_gates_match[] __initconst = { {.compatible = "allwinner,sun5i-a13-ahb-gates-clk", .data = &sun5i_a13_ahb_gates_data,}, {.compatible = "allwinner,sun6i-a31-ahb1-gates-clk", .data = &sun6i_a31_ahb1_gates_data,}, {.compatible = "allwinner,sun7i-a20-ahb-gates-clk", .data = &sun7i_a20_ahb_gates_data,}, + {.compatible = "allwinner,sun8i-a23-ahb1-gates-clk", .data = &sun8i_a23_ahb1_gates_data,}, {.compatible = "allwinner,sun4i-a10-apb0-gates-clk", .data = &sun4i_apb0_gates_data,}, {.compatible = "allwinner,sun5i-a10s-apb0-gates-clk", .data = &sun5i_a10s_apb0_gates_data,}, {.compatible = "allwinner,sun5i-a13-apb0-gates-clk", .data = &sun5i_a13_apb0_gates_data,}, @@ -1149,7 +1247,9 @@ static const struct of_device_id clk_gates_match[] __initconst = { {.compatible = "allwinner,sun5i-a13-apb1-gates-clk", .data = &sun5i_a13_apb1_gates_data,}, {.compatible = "allwinner,sun6i-a31-apb1-gates-clk", .data = &sun6i_a31_apb1_gates_data,}, {.compatible = "allwinner,sun7i-a20-apb1-gates-clk", .data = &sun7i_a20_apb1_gates_data,}, + {.compatible = "allwinner,sun8i-a23-apb1-gates-clk", .data = &sun8i_a23_apb1_gates_data,}, {.compatible = "allwinner,sun6i-a31-apb2-gates-clk", .data = &sun6i_a31_apb2_gates_data,}, + {.compatible = "allwinner,sun8i-a23-apb2-gates-clk", .data = &sun8i_a23_apb2_gates_data,}, {.compatible = "allwinner,sun4i-a10-usb-clk", .data = &sun4i_a10_usb_gates_data,}, {.compatible = "allwinner,sun5i-a13-usb-clk", .data = &sun5i_a13_usb_gates_data,}, {.compatible = "allwinner,sun6i-a31-usb-clk", .data = &sun6i_a31_usb_gates_data,}, @@ -1236,3 +1336,4 @@ static void __init sun6i_init_clocks(struct device_node *node) ARRAY_SIZE(sun6i_critical_clocks)); } CLK_OF_DECLARE(sun6i_a31_clk_init, "allwinner,sun6i-a31", sun6i_init_clocks); +CLK_OF_DECLARE(sun8i_a23_clk_init, "allwinner,sun8i-a23", sun6i_init_clocks); |