From d14e47056c726de566aa1fa8c3e7a4fd59141091 Mon Sep 17 00:00:00 2001 From: Chen-Yu Tsai Date: Thu, 26 Jun 2014 23:55:39 +0800 Subject: clk: sunxi: register clock gates with clkdev The new important clock protect code requires the clocks be registered with clkdev. This was missing for sunxi_gates type clocks. Signed-off-by: Chen-Yu Tsai Acked-by: Maxime Ripard Signed-off-by: Maxime Ripard --- drivers/clk/sunxi/clk-sunxi.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/clk/sunxi') diff --git a/drivers/clk/sunxi/clk-sunxi.c b/drivers/clk/sunxi/clk-sunxi.c index fb2ce8440f0e..e43ed6a6b660 100644 --- a/drivers/clk/sunxi/clk-sunxi.c +++ b/drivers/clk/sunxi/clk-sunxi.c @@ -899,6 +899,7 @@ static void __init sunxi_gates_clk_setup(struct device_node *node, reg + 4 * (i/32), i % 32, 0, &clk_lock); WARN_ON(IS_ERR(clk_data->clks[i])); + clk_register_clkdev(clk_data->clks[i], clk_name, NULL); j++; } -- cgit v1.2.3 From 70eab199fa39cb78e13d369db55f24a3839b8f9e Mon Sep 17 00:00:00 2001 From: Chen-Yu Tsai Date: Thu, 26 Jun 2014 23:55:40 +0800 Subject: clk: sunxi: move "ahb_sdram" to protected clock list With sunxi_gates clocks registered with clkdev, we can use the protected clocks list to enable the "ahb_sdram" clock, instead of looking for it and adding CLK_IGNORE_UNUSED inline in the clock setup code. Signed-off-by: Chen-Yu Tsai Acked-by: Maxime Ripard Signed-off-by: Maxime Ripard --- drivers/clk/sunxi/clk-sunxi.c | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) (limited to 'drivers/clk/sunxi') diff --git a/drivers/clk/sunxi/clk-sunxi.c b/drivers/clk/sunxi/clk-sunxi.c index e43ed6a6b660..a38c799ddc62 100644 --- a/drivers/clk/sunxi/clk-sunxi.c +++ b/drivers/clk/sunxi/clk-sunxi.c @@ -870,7 +870,6 @@ static void __init sunxi_gates_clk_setup(struct device_node *node, int qty; int i = 0; int j = 0; - int ignore; reg = of_iomap(node, 0); @@ -891,11 +890,8 @@ static void __init sunxi_gates_clk_setup(struct device_node *node, of_property_read_string_index(node, "clock-output-names", j, &clk_name); - /* No driver claims this clock, but it should remain gated */ - ignore = !strcmp("ahb_sdram", clk_name) ? CLK_IGNORE_UNUSED : 0; - clk_data->clks[i] = clk_register_gate(NULL, clk_name, - clk_parent, ignore, + clk_parent, 0, reg + 4 * (i/32), i % 32, 0, &clk_lock); WARN_ON(IS_ERR(clk_data->clks[i])); @@ -1203,6 +1199,7 @@ static void __init sunxi_init_clocks(const char *clocks[], int nclocks) static const char *sun4i_a10_critical_clocks[] __initdata = { "pll5_ddr", + "ahb_sdram", }; static void __init sun4i_a10_init_clocks(struct device_node *node) @@ -1215,6 +1212,7 @@ CLK_OF_DECLARE(sun4i_a10_clk_init, "allwinner,sun4i-a10", sun4i_a10_init_clocks) static const char *sun5i_critical_clocks[] __initdata = { "mbus", "pll5_ddr", + "ahb_sdram", }; static void __init sun5i_init_clocks(struct device_node *node) -- cgit v1.2.3 From 9a5e6c7eb5ccbb5f0d3a1dffce135f0a727f40e1 Mon Sep 17 00:00:00 2001 From: Chen-Yu Tsai Date: Thu, 26 Jun 2014 23:55:41 +0800 Subject: clk: sunxi: Support factor clocks with N factor starting not from 0 The PLLs on newer Allwinner SoC's, such as the A31 and A23, have a N multiplier factor that starts from 1, not 0. This patch adds an option to the factor clk driver's config data structures to specify the base value of N. Signed-off-by: Chen-Yu Tsai Acked-by: Maxime Ripard Signed-off-by: Maxime Ripard --- drivers/clk/sunxi/clk-factors.c | 2 +- drivers/clk/sunxi/clk-factors.h | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers/clk/sunxi') diff --git a/drivers/clk/sunxi/clk-factors.c b/drivers/clk/sunxi/clk-factors.c index 3806d97e529b..2057c8ac648f 100644 --- a/drivers/clk/sunxi/clk-factors.c +++ b/drivers/clk/sunxi/clk-factors.c @@ -62,7 +62,7 @@ static unsigned long clk_factors_recalc_rate(struct clk_hw *hw, p = FACTOR_GET(config->pshift, config->pwidth, reg); /* Calculate the rate */ - rate = (parent_rate * n * (k + 1) >> p) / (m + 1); + rate = (parent_rate * (n + config->n_start) * (k + 1) >> p) / (m + 1); return rate; } diff --git a/drivers/clk/sunxi/clk-factors.h b/drivers/clk/sunxi/clk-factors.h index 02e1a43ebac7..d2d0efa39379 100644 --- a/drivers/clk/sunxi/clk-factors.h +++ b/drivers/clk/sunxi/clk-factors.h @@ -15,6 +15,7 @@ struct clk_factors_config { u8 mwidth; u8 pshift; u8 pwidth; + u8 n_start; }; struct clk_factors { -- cgit v1.2.3 From ea5671bffbb2b6eefdce7e467a162ae2eef032ac Mon Sep 17 00:00:00 2001 From: Chen-Yu Tsai Date: Thu, 26 Jun 2014 23:55:42 +0800 Subject: clk: sunxi: Add support for table-based divider clocks A few of the clock modules have odd dividers, such as the 2 lowest dividers being the same (2), or have the same divider when the highest bit is set. This patch adds support for optional divider tables, so the clock framework will know about the odd values. Signed-off-by: Chen-Yu Tsai Acked-by: Maxime Ripard Signed-off-by: Maxime Ripard --- drivers/clk/sunxi/clk-sunxi.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) (limited to 'drivers/clk/sunxi') diff --git a/drivers/clk/sunxi/clk-sunxi.c b/drivers/clk/sunxi/clk-sunxi.c index a38c799ddc62..6fe9492f84ad 100644 --- a/drivers/clk/sunxi/clk-sunxi.c +++ b/drivers/clk/sunxi/clk-sunxi.c @@ -664,6 +664,7 @@ struct div_data { u8 shift; u8 pow; u8 width; + const struct clk_div_table *table; }; static const struct div_data sun4i_axi_data __initconst = { @@ -704,10 +705,10 @@ static void __init sunxi_divider_clk_setup(struct device_node *node, of_property_read_string(node, "clock-output-names", &clk_name); - clk = clk_register_divider(NULL, clk_name, clk_parent, 0, - reg, data->shift, data->width, - data->pow ? CLK_DIVIDER_POWER_OF_TWO : 0, - &clk_lock); + 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); -- cgit v1.2.3 From 515c1a4bdcd9b55e2c21e897a9ca276bd708d145 Mon Sep 17 00:00:00 2001 From: Chen-Yu Tsai Date: Thu, 26 Jun 2014 23:55:43 +0800 Subject: 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 Acked-by: Maxime Ripard Signed-off-by: Maxime Ripard --- drivers/clk/sunxi/clk-sunxi.c | 101 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 101 insertions(+) (limited to 'drivers/clk/sunxi') 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 @@ -163,6 +163,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 @@ -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); -- cgit v1.2.3 From 57a1fbf28424561a080b34fbdd04661282aea40e Mon Sep 17 00:00:00 2001 From: Chen-Yu Tsai Date: Thu, 3 Jul 2014 22:55:41 +0800 Subject: clk: sunxi: Add A23 APB0 divider clock support The A23 has an almost identical PRCM clock tree. The difference in the APB0 clock is the smallest divisor is 1, instead of 2. This patch adds a separate sun8i-a23-apb0-clk driver to support it. Signed-off-by: Chen-Yu Tsai Signed-off-by: Maxime Ripard --- drivers/clk/sunxi/Makefile | 4 ++- drivers/clk/sunxi/clk-sun8i-apb0.c | 68 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 71 insertions(+), 1 deletion(-) create mode 100644 drivers/clk/sunxi/clk-sun8i-apb0.c (limited to 'drivers/clk/sunxi') diff --git a/drivers/clk/sunxi/Makefile b/drivers/clk/sunxi/Makefile index 762fd64dbd1f..6850cba35871 100644 --- a/drivers/clk/sunxi/Makefile +++ b/drivers/clk/sunxi/Makefile @@ -6,4 +6,6 @@ obj-y += clk-sunxi.o clk-factors.o obj-y += clk-a10-hosc.o obj-y += clk-a20-gmac.o -obj-$(CONFIG_MFD_SUN6I_PRCM) += clk-sun6i-ar100.o clk-sun6i-apb0.o clk-sun6i-apb0-gates.o +obj-$(CONFIG_MFD_SUN6I_PRCM) += \ + clk-sun6i-ar100.o clk-sun6i-apb0.o clk-sun6i-apb0-gates.o \ + clk-sun8i-apb0.o diff --git a/drivers/clk/sunxi/clk-sun8i-apb0.c b/drivers/clk/sunxi/clk-sun8i-apb0.c new file mode 100644 index 000000000000..196a4997f9d9 --- /dev/null +++ b/drivers/clk/sunxi/clk-sun8i-apb0.c @@ -0,0 +1,68 @@ +/* + * Copyright (C) 2014 Chen-Yu Tsai + * Author: Chen-Yu Tsai + * + * Allwinner A23 APB0 clock driver + * + * License Terms: GNU General Public License v2 + * + * Based on clk-sun6i-apb0.c + * Allwinner A31 APB0 clock driver + * + * Copyright (C) 2014 Free Electrons + * Author: Boris BREZILLON + * + */ + +#include +#include +#include +#include + +static int sun8i_a23_apb0_clk_probe(struct platform_device *pdev) +{ + struct device_node *np = pdev->dev.of_node; + const char *clk_name = np->name; + const char *clk_parent; + struct resource *r; + void __iomem *reg; + struct clk *clk; + + r = platform_get_resource(pdev, IORESOURCE_MEM, 0); + reg = devm_ioremap_resource(&pdev->dev, r); + if (IS_ERR(reg)) + return PTR_ERR(reg); + + clk_parent = of_clk_get_parent_name(np, 0); + if (!clk_parent) + return -EINVAL; + + of_property_read_string(np, "clock-output-names", &clk_name); + + /* The A23 APB0 clock is a standard 2 bit wide divider clock */ + clk = clk_register_divider(&pdev->dev, clk_name, clk_parent, 0, reg, + 0, 2, CLK_DIVIDER_POWER_OF_TWO, NULL); + if (IS_ERR(clk)) + return PTR_ERR(clk); + + return of_clk_add_provider(np, of_clk_src_simple_get, clk); +} + +const struct of_device_id sun8i_a23_apb0_clk_dt_ids[] = { + { .compatible = "allwinner,sun8i-a23-apb0-clk" }, + { /* sentinel */ } +}; + +static struct platform_driver sun8i_a23_apb0_clk_driver = { + .driver = { + .name = "sun8i-a23-apb0-clk", + .owner = THIS_MODULE, + .of_match_table = sun8i_a23_apb0_clk_dt_ids, + }, + .probe = sun8i_a23_apb0_clk_probe, +}; +module_platform_driver(sun8i_a23_apb0_clk_driver); + +MODULE_AUTHOR("Chen-Yu Tsai "); +MODULE_DESCRIPTION("Allwinner A23 APB0 clock Driver"); +MODULE_LICENSE("GPL v2"); -- cgit v1.2.3 From cd6eb534fbb8c9c52e6900f6b086d8c95f966449 Mon Sep 17 00:00:00 2001 From: Chen-Yu Tsai Date: Thu, 3 Jul 2014 22:55:42 +0800 Subject: clk: sunxi: Fix gate indexing for sun6i-a31-apb0-gates sun6i-a31-apb0-gates supports using clock-indices for holes between individual gates. However, the driver passes the number of gates registered in clk_data->clk_num, which of_clk_src_onecell_get uses to recognize the range of valid indices a consumer can use. This patch makes the driver pass the maximum gate index + 1, so of_clk_src_onecell_get does not complain about indices greater than gates registered. This was tested on the A23 SoC, which has a similar APB0 clock, but has holes for gates to removed IP blocks. Signed-off-by: Chen-Yu Tsai Acked-by: Maxime Ripard Signed-off-by: Maxime Ripard --- drivers/clk/sunxi/clk-sun6i-apb0-gates.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'drivers/clk/sunxi') diff --git a/drivers/clk/sunxi/clk-sun6i-apb0-gates.c b/drivers/clk/sunxi/clk-sun6i-apb0-gates.c index 44cd27c5c401..b342f2a2d4a7 100644 --- a/drivers/clk/sunxi/clk-sun6i-apb0-gates.c +++ b/drivers/clk/sunxi/clk-sun6i-apb0-gates.c @@ -25,6 +25,7 @@ static int sun6i_a31_apb0_gates_clk_probe(struct platform_device *pdev) void __iomem *reg; int gate_id; int ngates; + int gate_max = 0; int i; r = platform_get_resource(pdev, IORESOURCE_MEM, 0); @@ -72,9 +73,12 @@ static int sun6i_a31_apb0_gates_clk_probe(struct platform_device *pdev) reg, gate_id, 0, NULL); WARN_ON(IS_ERR(clk_data->clks[gate_id])); + + if (gate_id > gate_max) + gate_max = gate_id; } - clk_data->clk_num = ngates; + clk_data->clk_num = gate_max + 1; return of_clk_add_provider(np, of_clk_src_onecell_get, clk_data); } -- cgit v1.2.3 From b72efd0f651271ee05835ab848eee7c4e6851c99 Mon Sep 17 00:00:00 2001 From: Chen-Yu Tsai Date: Wed, 9 Jul 2014 15:54:34 +0800 Subject: clk: sunxi: sun6i-apb0-gates: use bitmaps for valid gate indices sun6i-apb0-gates uses the "clock-indices" DT property to indicate valid gate bits or holes in between. However, the rest of sunxi clock drivers use bitmaps for this purpose. This patch modifies sun6i-apb0-gates to use bitmaps as well, to be consistent with the sunxi platform. Also add the missing call to clk_register_clkdev, so system clock lookups will work. Signed-off-by: Chen-Yu Tsai Signed-off-by: Maxime Ripard --- drivers/clk/sunxi/clk-sun6i-apb0-gates.c | 75 +++++++++++++++++--------------- 1 file changed, 39 insertions(+), 36 deletions(-) (limited to 'drivers/clk/sunxi') diff --git a/drivers/clk/sunxi/clk-sun6i-apb0-gates.c b/drivers/clk/sunxi/clk-sun6i-apb0-gates.c index b342f2a2d4a7..3bd8357f912e 100644 --- a/drivers/clk/sunxi/clk-sun6i-apb0-gates.c +++ b/drivers/clk/sunxi/clk-sun6i-apb0-gates.c @@ -9,24 +9,48 @@ */ #include +#include #include #include +#include #include #define SUN6I_APB0_GATES_MAX_SIZE 32 +struct gates_data { + DECLARE_BITMAP(mask, SUN6I_APB0_GATES_MAX_SIZE); +}; + +static const struct gates_data sun6i_a31_apb0_gates __initconst = { + .mask = {0x7F}, +}; + +const struct of_device_id sun6i_a31_apb0_gates_clk_dt_ids[] = { + { .compatible = "allwinner,sun6i-a31-apb0-gates-clk", .data = &sun6i_a31_apb0_gates }, + { /* sentinel */ } +}; + static int sun6i_a31_apb0_gates_clk_probe(struct platform_device *pdev) { struct device_node *np = pdev->dev.of_node; struct clk_onecell_data *clk_data; + const struct of_device_id *device; + const struct gates_data *data; const char *clk_parent; const char *clk_name; struct resource *r; void __iomem *reg; - int gate_id; int ngates; - int gate_max = 0; int i; + int j = 0; + + if (!np) + return -ENODEV; + + device = of_match_device(sun6i_a31_apb0_gates_clk_dt_ids, &pdev->dev); + if (!device) + return -ENODEV; + data = device->data; r = platform_get_resource(pdev, IORESOURCE_MEM, 0); reg = devm_ioremap_resource(&pdev->dev, r); @@ -37,57 +61,36 @@ static int sun6i_a31_apb0_gates_clk_probe(struct platform_device *pdev) if (!clk_parent) return -EINVAL; - ngates = of_property_count_strings(np, "clock-output-names"); - if (ngates < 0) - return ngates; - - if (!ngates || ngates > SUN6I_APB0_GATES_MAX_SIZE) - return -EINVAL; - clk_data = devm_kzalloc(&pdev->dev, sizeof(struct clk_onecell_data), GFP_KERNEL); if (!clk_data) return -ENOMEM; - clk_data->clks = devm_kzalloc(&pdev->dev, - SUN6I_APB0_GATES_MAX_SIZE * - sizeof(struct clk *), - GFP_KERNEL); + /* Worst-case size approximation and memory allocation */ + ngates = find_last_bit(data->mask, SUN6I_APB0_GATES_MAX_SIZE); + clk_data->clks = devm_kcalloc(&pdev->dev, (ngates + 1), + sizeof(struct clk *), GFP_KERNEL); if (!clk_data->clks) return -ENOMEM; - for (i = 0; i < ngates; i++) { + for_each_set_bit(i, data->mask, SUN6I_APB0_GATES_MAX_SIZE) { of_property_read_string_index(np, "clock-output-names", - i, &clk_name); - - gate_id = i; - of_property_read_u32_index(np, "clock-indices", i, &gate_id); - - WARN_ON(gate_id >= SUN6I_APB0_GATES_MAX_SIZE); - if (gate_id >= SUN6I_APB0_GATES_MAX_SIZE) - continue; + j, &clk_name); - clk_data->clks[gate_id] = clk_register_gate(&pdev->dev, - clk_name, - clk_parent, 0, - reg, gate_id, - 0, NULL); - WARN_ON(IS_ERR(clk_data->clks[gate_id])); + clk_data->clks[i] = clk_register_gate(&pdev->dev, clk_name, + 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); - if (gate_id > gate_max) - gate_max = gate_id; + j++; } - clk_data->clk_num = gate_max + 1; + clk_data->clk_num = ngates + 1; return of_clk_add_provider(np, of_clk_src_onecell_get, clk_data); } -const struct of_device_id sun6i_a31_apb0_gates_clk_dt_ids[] = { - { .compatible = "allwinner,sun6i-a31-apb0-gates-clk" }, - { /* sentinel */ } -}; - static struct platform_driver sun6i_a31_apb0_gates_clk_driver = { .driver = { .name = "sun6i-a31-apb0-gates-clk", -- cgit v1.2.3 From 6c1d66f0da59362cb33ce37d436cd28c77c2b2cb Mon Sep 17 00:00:00 2001 From: Chen-Yu Tsai Date: Wed, 9 Jul 2014 15:54:35 +0800 Subject: clk: sunxi: sun6i-a31-apb0-gates: Add A23 APB0 support This patch adds "allwinner,sun8i-a23-apb0-gates-clk", a A23 specific compatible to the sun6i-a31-apb0-gates clock driver, along with the gate bitmap. Signed-off-by: Chen-Yu Tsai Signed-off-by: Maxime Ripard --- drivers/clk/sunxi/clk-sun6i-apb0-gates.c | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'drivers/clk/sunxi') diff --git a/drivers/clk/sunxi/clk-sun6i-apb0-gates.c b/drivers/clk/sunxi/clk-sun6i-apb0-gates.c index 3bd8357f912e..551f220ade91 100644 --- a/drivers/clk/sunxi/clk-sun6i-apb0-gates.c +++ b/drivers/clk/sunxi/clk-sun6i-apb0-gates.c @@ -25,8 +25,13 @@ static const struct gates_data sun6i_a31_apb0_gates __initconst = { .mask = {0x7F}, }; +static const struct gates_data sun8i_a23_apb0_gates __initconst = { + .mask = {0x5D}, +}; + const struct of_device_id sun6i_a31_apb0_gates_clk_dt_ids[] = { { .compatible = "allwinner,sun6i-a31-apb0-gates-clk", .data = &sun6i_a31_apb0_gates }, + { .compatible = "allwinner,sun8i-a23-apb0-gates-clk", .data = &sun8i_a23_apb0_gates }, { /* sentinel */ } }; -- cgit v1.2.3