diff -drupN a/drivers/clk/sunxi/clk-sunxi.c b/drivers/clk/sunxi/clk-sunxi.c --- a/drivers/clk/sunxi/clk-sunxi.c 2018-08-06 17:23:04.000000000 +0300 +++ b/drivers/clk/sunxi/clk-sunxi.c 2022-06-12 05:28:14.000000000 +0300 @@ -1,12 +1,10 @@ /* - * Copyright 2013 Emilio López - * - * Emilio López + * Copyright (C) 2016-2020 Allwinnertech + * Wim Hwang * * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of @@ -17,1170 +15,320 @@ #include #include #include +#include #include #include -#include -#include -#include -#include - +#include +#include "clk-sunxi.h" #include "clk-factors.h" +#include "clk-periph.h" +#include "clk-cpu.h" -static DEFINE_SPINLOCK(clk_lock); - -/* Maximum number of parents our clocks have */ -#define SUNXI_MAX_PARENTS 5 +#ifdef CONFIG_PM_SLEEP +/*list head use for standby*/ +LIST_HEAD(clk_periph_reg_cache_list); +LIST_HEAD(clk_factor_reg_cache_list); +#endif -/** - * sun4i_get_pll1_factors() - calculates n, k, m, p factors for PLL1 - * PLL1 rate is calculated as follows - * rate = (parent_rate * n * (k + 1) >> p) / (m + 1); - * parent_rate is always 24Mhz +/* + * of_sunxi_clocks_init() - Clocks initialize */ - -static void sun4i_get_pll1_factors(struct factors_request *req) +static void __init of_sunxi_clocks_init(struct device_node *node) { - u8 div; - - /* Normalize value to a 6M multiple */ - div = req->rate / 6000000; - req->rate = 6000000 * div; - - /* m is always zero for pll1 */ - req->m = 0; - - /* k is 1 only on these cases */ - if (req->rate >= 768000000 || req->rate == 42000000 || - req->rate == 54000000) - req->k = 1; - else - req->k = 0; - - /* p will be 3 for divs under 10 */ - if (div < 10) - 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))) - 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))) - req->p = 1; - - /* any other entries have p = 0 */ - else - req->p = 0; - - /* calculate a suitable n based on k and p */ - div <<= req->p; - div /= (req->k + 1); - req->n = div / 4; + /* do some soc special init here */ + sunxi_clocks_init(node); } -/** - * sun6i_a31_get_pll1_factors() - calculates n, k and m factors for PLL1 - * PLL1 rate is calculated as follows - * rate = parent_rate * (n + 1) * (k + 1) / (m + 1); - * parent_rate should always be 24MHz - */ -static void sun6i_a31_get_pll1_factors(struct factors_request *req) +static void __init of_sunxi_fixed_clk_setup(struct device_node *node) { - /* - * We can operate only on MHz, this will make our life easier - * later. - */ - u32 freq_mhz = req->rate / 1000000; - u32 parent_freq_mhz = req->parent_rate / 1000000; - - /* - * Round down the frequency to the closest multiple of either - * 6 or 16 - */ - u32 round_freq_6 = round_down(freq_mhz, 6); - u32 round_freq_16 = round_down(freq_mhz, 16); - - if (round_freq_6 > round_freq_16) - freq_mhz = round_freq_6; - else - freq_mhz = round_freq_16; - - req->rate = freq_mhz * 1000000; - - /* If the frequency is a multiple of 32 MHz, k is always 3 */ - if (!(freq_mhz % 32)) - req->k = 3; - /* If the frequency is a multiple of 9 MHz, k is always 2 */ - else if (!(freq_mhz % 9)) - req->k = 2; - /* If the frequency is a multiple of 8 MHz, k is always 1 */ - else if (!(freq_mhz % 8)) - req->k = 1; - /* Otherwise, we don't use the k factor */ - else - req->k = 0; - - /* - * If the frequency is a multiple of 2 but not a multiple of - * 3, m is 3. This is the first time we use 6 here, yet we - * will use it on several other places. - * We use this number because it's the lowest frequency we can - * generate (with n = 0, k = 0, m = 3), so every other frequency - * somehow relates to this frequency. - */ - if ((freq_mhz % 6) == 2 || (freq_mhz % 6) == 4) - 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) - req->m = 3; - /* Otherwise, we end up with m = 1 */ - else - req->m = 1; + struct clk *clk; + const char *clk_name = node->name; + u32 rate; - /* Calculate n thanks to the above factors we already got */ - req->n = freq_mhz * (req->m + 1) / ((req->k + 1) * parent_freq_mhz) - - 1; + if (of_property_read_u32(node, "clock-frequency", &rate)) + return; - /* - * If n end up being outbound, and that we can still decrease - * m, do it. - */ - if ((req->n + 1) > 31 && (req->m + 1) > 1) { - req->n = (req->n + 1) / 2 - 1; - req->m = (req->m + 1) / 2 - 1; + if (of_property_read_string(node, "clock-output-names", &clk_name)) { + pr_err("%s:get clock-output-names failed in %s node\n", + __func__, node->full_name); + return; } -} -/** - * 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(struct factors_request *req) -{ - u8 div; - - /* Normalize value to a 6M multiple */ - div = req->rate / 6000000; - req->rate = 6000000 * div; - - /* m is always zero for pll1 */ - req->m = 0; - - /* k is 1 only on these cases */ - if (req->rate >= 768000000 || req->rate == 42000000 || - req->rate == 54000000) - req->k = 1; - else - req->k = 0; - - /* p will be 2 for divs under 20 and odd divs under 32 */ - if (div < 20 || (div < 32 && (div & 1))) - 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))) - req->p = 1; - - /* any other entries have p = 0 */ - else - req->p = 0; - - /* calculate a suitable n based on k and p */ - div <<= req->p; - div /= (req->k + 1); - req->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) - * parent_rate is always 24Mhz - */ - -static void sun4i_get_pll5_factors(struct factors_request *req) -{ - u8 div; - - /* Normalize value to a parent_rate multiple (24M) */ - div = req->rate / req->parent_rate; - req->rate = req->parent_rate * div; - - if (div < 31) - req->k = 0; - else if (div / 2 < 31) - req->k = 1; - else if (div / 3 < 31) - req->k = 2; - else - req->k = 3; - - req->n = DIV_ROUND_UP(div, (req->k + 1)); -} - -/** - * sun6i_a31_get_pll6_factors() - calculates n, k factors for A31 PLL6x2 - * PLL6x2 rate is calculated as follows - * rate = parent_rate * (n + 1) * (k + 1) - * parent_rate is always 24Mhz - */ - -static void sun6i_a31_get_pll6_factors(struct factors_request *req) -{ - u8 div; - - /* Normalize value to a parent_rate multiple (24M) */ - div = req->rate / req->parent_rate; - req->rate = req->parent_rate * div; - - req->k = div / 32; - if (req->k > 3) - req->k = 3; - - req->n = DIV_ROUND_UP(div, (req->k + 1)) - 1; -} - -/** - * sun5i_a13_get_ahb_factors() - calculates m, p factors for AHB - * AHB rate is calculated as follows - * rate = parent_rate >> p - */ - -static void sun5i_a13_get_ahb_factors(struct factors_request *req) -{ - u32 div; - - /* divide only */ - 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 (req->rate < 8000) - req->rate = 8000; - if (req->rate > 300000000) - req->rate = 300000000; - - div = order_base_2(DIV_ROUND_UP(req->parent_rate, req->rate)); - - /* p = 0 ~ 3 */ - if (div > 3) - div = 3; - - req->rate = req->parent_rate >> div; - - 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; + clk = clk_register_fixed_rate(NULL, clk_name, NULL, CLK_IS_ROOT, rate); + if (!IS_ERR(clk)) { + clk_register_clkdev(clk, clk_name, NULL); + of_clk_add_provider(node, of_clk_src_simple_get, clk); } - - 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; -} - -/** - * sun4i_get_apb1_factors() - calculates m, p factors for APB1 - * APB1 rate is calculated as follows - * rate = (parent_rate >> p) / (m + 1); - */ - -static void sun4i_get_apb1_factors(struct factors_request *req) +static void __init of_sunxi_fixed_factor_clk_setup(struct device_node *node) { - u8 calcm, calcp; - int div; - - if (req->parent_rate < req->rate) - req->rate = req->parent_rate; - - div = DIV_ROUND_UP(req->parent_rate, req->rate); + struct clk *clk; + const char *clk_name = node->name; + const char *parent_name; + u32 div, mult; - /* Invalid rate! */ - if (div > 32) + if (of_property_read_u32(node, "clock-div", &div)) { + pr_err("%s Fixed factor clock <%s> must have a clock-div property\n", + __func__, node->name); return; + } - if (div <= 4) - calcp = 0; - else if (div <= 8) - calcp = 1; - else if (div <= 16) - calcp = 2; - else - calcp = 3; - - calcm = (div >> calcp) - 1; - - req->rate = (req->parent_rate >> calcp) / (calcm + 1); - req->m = calcm; - req->p = calcp; -} - - - - -/** - * 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(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 (req->rate > req->parent_rate) - req->rate = req->parent_rate; - - div = DIV_ROUND_UP(req->parent_rate, req->rate); - - if (div < 32) - calcp = 0; - else if (div / 2 < 32) - calcp = 1; - else if (div / 4 < 32) - calcp = 2; - else - calcp = 3; + if (of_property_read_u32(node, "clock-mult", &mult)) { + pr_err("%s Fixed factor clock <%s> must have a clokc-mult property\n", + __func__, node->name); + return; + } - calcm = DIV_ROUND_UP(div, 1 << calcp); + if (of_property_read_string(node, "clock-output-names", &clk_name)) { + pr_err("%s:get clock-output-names failed in %s node\n", + __func__, node->full_name); + return; + } + parent_name = of_clk_get_parent_name(node, 0); - req->rate = (req->parent_rate >> calcp) / calcm; - req->m = calcm - 1; - req->p = calcp; + clk = clk_register_fixed_factor(NULL, clk_name, parent_name, 0, + mult, div); + if (!IS_ERR(clk)) { + clk_register_clkdev(clk, clk_name, NULL); + of_clk_add_provider(node, of_clk_src_simple_get, clk); + } } -/** - * sunxi_factors_clk_setup() - Setup function for factor clocks +/* + * of_sunxi_pll_clk_setup() - Setup function for pll factors clk */ - -static const struct clk_factors_config sun4i_pll1_config = { - .nshift = 8, - .nwidth = 5, - .kshift = 4, - .kwidth = 2, - .mshift = 0, - .mwidth = 2, - .pshift = 16, - .pwidth = 2, -}; - -static const struct clk_factors_config sun6i_a31_pll1_config = { - .nshift = 8, - .nwidth = 5, - .kshift = 4, - .kwidth = 2, - .mshift = 0, - .mwidth = 2, - .n_start = 1, -}; - -static const 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 const struct clk_factors_config sun4i_pll5_config = { - .nshift = 8, - .nwidth = 5, - .kshift = 4, - .kwidth = 2, -}; - -static const struct clk_factors_config sun6i_a31_pll6_config = { - .nshift = 8, - .nwidth = 5, - .kshift = 4, - .kwidth = 2, - .n_start = 1, -}; - -static const struct clk_factors_config sun5i_a13_ahb_config = { - .pshift = 4, - .pwidth = 2, -}; - -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, - .pwidth = 2, -}; - -/* user manual says "n" but it's really "p" */ -static const 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, - .getter = sun4i_get_pll1_factors, -}; - -static const struct factors_data sun6i_a31_pll1_data __initconst = { - .enable = 31, - .table = &sun6i_a31_pll1_config, - .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, - .getter = sun4i_get_pll5_factors, -}; - -static const struct factors_data sun4i_pll5_data __initconst = { - .enable = 31, - .table = &sun4i_pll5_config, - .getter = sun4i_get_pll5_factors, -}; - -static const struct factors_data sun6i_a31_pll6_data __initconst = { - .enable = 31, - .table = &sun6i_a31_pll6_config, - .getter = sun6i_a31_get_pll6_factors, -}; - -static const struct factors_data sun5i_a13_ahb_data __initconst = { - .mux = 6, - .muxmask = BIT(1) | BIT(0), - .table = &sun5i_a13_ahb_config, - .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), - .table = &sun4i_apb1_config, - .getter = sun4i_get_apb1_factors, -}; - -static const struct factors_data sun7i_a20_out_data __initconst = { - .enable = 31, - .mux = 24, - .muxmask = BIT(1) | BIT(0), - .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) +static void __init of_sunxi_pll_clk_setup(struct device_node *node) { - void __iomem *reg; + struct clk *clk; + const char *clk_name = node->name; + const char *lock_mode = NULL; + struct factor_init_data *factor; - reg = of_iomap(node, 0); - if (!reg) { - pr_err("Could not get registers for factors-clk: %s\n", - node->name); - return NULL; + if (of_property_read_string(node, "clock-output-names", &clk_name)) { + pr_err("%s:get clock-output-names failed in %s node\n", + __func__, node->full_name); + return; + } + factor = sunxi_clk_get_factor_by_name(clk_name); + if (!factor) { + pr_err("clk %s not found in %s\n", clk_name, __func__); + return; } - 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); + if (!of_property_read_string(node, "lock-mode", &lock_mode)) + sunxi_clk_set_factor_lock_mode(factor, lock_mode); -static void __init sun7i_out_clk_setup(struct device_node *node) -{ - sunxi_factors_clk_setup(node, &sun7i_a20_out_data); + clk = sunxi_clk_register_factors(NULL, sunxi_clk_base, + &clk_lock, factor); + if (!IS_ERR(clk)) { + clk_register_clkdev(clk, clk_name, NULL); + of_clk_add_provider(node, of_clk_src_simple_get, clk); + } } -CLK_OF_DECLARE(sun7i_out, "allwinner,sun7i-a20-out-clk", - sun7i_out_clk_setup); - -/** - * sunxi_mux_clk_setup() - Setup function for muxes +/* + * of_sunxi_cpus_pll_clk_setup() - Setup function for prcm pll factors clk */ - -#define SUNXI_MUX_GATE_WIDTH 2 - -struct mux_data { - u8 shift; -}; - -static const struct mux_data sun4i_cpu_mux_data __initconst = { - .shift = 16, -}; - -static const struct mux_data sun6i_a31_ahb1_mux_data __initconst = { - .shift = 12, -}; - -static const struct mux_data sun8i_h3_ahb2_mux_data __initconst = { - .shift = 0, -}; - -static struct clk * __init sunxi_mux_clk_setup(struct device_node *node, - const struct mux_data *data) +static void __init of_sunxi_cpus_pll_clk_setup(struct device_node *node) { struct clk *clk; const char *clk_name = node->name; - const char *parents[SUNXI_MAX_PARENTS]; - void __iomem *reg; - 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; - } + const char *lock_mode = NULL; + struct factor_init_data *factor; - i = of_clk_parent_fill(node, parents, SUNXI_MAX_PARENTS); 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; + pr_err("%s:get clock-output-names failed in %s node\n", + __func__, node->full_name); + return; } - - clk = clk_register_mux(NULL, clk_name, parents, i, - CLK_SET_RATE_PARENT, reg, - data->shift, SUNXI_MUX_GATE_WIDTH, - 0, &clk_lock); - - if (IS_ERR(clk)) { - pr_err("%s: failed to register mux clock %s: %ld\n", __func__, - clk_name, PTR_ERR(clk)); - goto out_unmap; + factor = sunxi_clk_get_factor_by_name(clk_name); + if (!factor) { + pr_err("clk %s not found in %s\n", clk_name, __func__); + return; } - 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; - } + if (!of_property_read_string(node, "lock-mode", &lock_mode)) + sunxi_clk_set_factor_lock_mode(factor, lock_mode); - return clk; -out_unmap: - iounmap(reg); - return NULL; + clk = sunxi_clk_register_factors(NULL, sunxi_clk_cpus_base, + &clk_lock, factor); + if (!IS_ERR(clk)) { + clk_register_clkdev(clk, clk_name, NULL); + of_clk_add_provider(node, of_clk_src_simple_get, clk); + } } -static void __init sun4i_cpu_clk_setup(struct device_node *node) +/* + * of_sunxi_periph_clk_setup() - Setup function for periph clk + */ +static void __init of_sunxi_periph_clk_setup(struct device_node *node) { struct clk *clk; + const char *clk_name = node->name; + struct periph_init_data *periph; - clk = sunxi_mux_clk_setup(node, &sun4i_cpu_mux_data); - if (!clk) + if (of_property_read_string(node, "clock-output-names", &clk_name)) { + pr_err("%s:get clock-output-names failed in %s node\n", + __func__, node->full_name); 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); + periph = sunxi_clk_get_periph_by_name(clk_name); + if (!periph) { + pr_err("clk %s not found in %s\n", clk_name, __func__); + return; + } -static void __init sun6i_ahb1_mux_clk_setup(struct device_node *node) -{ - sunxi_mux_clk_setup(node, &sun6i_a31_ahb1_mux_data); + if (!strcmp(clk_name, "losc_out")) { + clk = sunxi_clk_register_periph(periph, 0); + } else + clk = sunxi_clk_register_periph(periph, + sunxi_clk_base); + if (!IS_ERR(clk)) { + clk_register_clkdev(clk, clk_name, NULL); + of_clk_add_provider(node, of_clk_src_simple_get, clk); + } } -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) +/* + * of_sunxi_cpu_clk_setup() - Setup function for cpu clk + */ +static void __init of_sunxi_cpu_clk_setup(struct device_node *node) { - sunxi_mux_clk_setup(node, &sun8i_h3_ahb2_mux_data); + sunxi_cpu_clocks_init(node); } -CLK_OF_DECLARE(sun8i_ahb2, "allwinner,sun8i-h3-ahb2-clk", - sun8i_ahb2_clk_setup); - /** - * sunxi_divider_clk_setup() - Setup function for simple divider clocks + * of_periph_cpus_clk_setup() - Setup function for periph cpus clk */ - -struct div_data { - u8 shift; - u8 pow; - u8 width; - const struct clk_div_table *table; -}; - -static const struct div_data sun4i_axi_data __initconst = { - .shift = 0, - .pow = 0, - .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, - .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 = { - .shift = 8, - .pow = 1, - .width = 2, - .table = sun4i_apb0_table, -}; - -static void __init sunxi_divider_clk_setup(struct device_node *node, - const struct div_data *data) +void of_sunxi_periph_cpus_clk_setup(struct device_node *node) { struct clk *clk; const char *clk_name = node->name; - const char *clk_parent; - 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); + struct periph_init_data *periph; 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; + pr_err("%s:get clock-output-names failed in %s node\n", + __func__, node->full_name); + return; } - 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 (IS_ERR(clk)) { - pr_err("%s: failed to register divider clock %s: %ld\n", - __func__, clk_name, PTR_ERR(clk)); - goto out_unmap; + periph = sunxi_clk_get_periph_cpus_by_name(clk_name); + if (!periph) { + pr_err("clk %s not found in %s\n", clk_name, __func__); + return; } - 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; - } + /* register clk */ + if (!strcmp(clk_name, "losc_out") || + !strcmp(clk_name, "dcxo_out") || + !strcmp(clk_name, "r_dma") || + !strcmp(clk_name, "hosc32k")) { + clk = sunxi_clk_register_periph(periph, 0); + } else + clk = sunxi_clk_register_periph(periph, + sunxi_clk_cpus_base); - if (clk_register_clkdev(clk, clk_name, NULL)) { - of_clk_del_provider(node); - goto out_unregister; + if (!IS_ERR(clk)) { + clk_register_clkdev(clk, clk_name, NULL); + of_clk_add_provider(node, of_clk_src_simple_get, clk); + return; } - 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); + pr_err("clk %s not found in %s\n", clk_name, __func__); } -CLK_OF_DECLARE(sun8i_axi, "allwinner,sun8i-a23-axi-clk", - sun8i_axi_clk_setup); - - - -/** - * sunxi_gates_clk_setup() - Setup function for leaf gates on clocks - */ - -#define SUNXI_GATES_MAX_SIZE 64 - -struct gates_data { - DECLARE_BITMAP(mask, SUNXI_GATES_MAX_SIZE); -}; - -/** - * sunxi_divs_clk_setup() helper data - */ - -#define SUNXI_DIVS_MAX_QTY 4 -#define SUNXI_DIVISOR_WIDTH 2 - -struct divs_data { - const struct factors_data *factors; /* data for the factor clock */ - int ndivs; /* number of outputs */ - /* - * List of outputs. Refer to the diagram for sunxi_divs_clk_setup(): - * self or base factor clock refers to the output from the pll - * itself. The remaining refer to fixed or configurable divider - * outputs. - */ - struct { - u8 self; /* is it the base factor clock? (only one) */ - u8 fixed; /* is it a fixed divisor? if not... */ - struct clk_div_table *table; /* is it a table based divisor? */ - u8 shift; /* otherwise it's a normal divisor with this shift */ - u8 pow; /* is it power-of-two based? */ - u8 gate; /* is it independently gateable? */ - } div[SUNXI_DIVS_MAX_QTY]; -}; - -static struct clk_div_table pll6_sata_tbl[] = { - { .val = 0, .div = 6, }, - { .val = 1, .div = 12, }, - { .val = 2, .div = 18, }, - { .val = 3, .div = 24, }, - { } /* sentinel */ -}; - -static const struct divs_data pll5_divs_data __initconst = { - .factors = &sun4i_pll5_data, - .ndivs = 2, - .div = { - { .shift = 0, .pow = 0, }, /* M, DDR */ - { .shift = 16, .pow = 1, }, /* P, other */ - /* No output for the base factor clock */ - } -}; - -static const struct divs_data pll6_divs_data __initconst = { - .factors = &sun4i_pll5_data, - .ndivs = 4, - .div = { - { .shift = 0, .table = pll6_sata_tbl, .gate = 14 }, /* M, SATA */ - { .fixed = 2 }, /* P, other */ - { .self = 1 }, /* base factor clock, 2x */ - { .fixed = 4 }, /* pll6 / 4, used as ahb input */ - } -}; - -static const struct divs_data sun6i_a31_pll6_divs_data __initconst = { - .factors = &sun6i_a31_pll6_data, - .ndivs = 2, - .div = { - { .fixed = 2 }, /* normal output */ - { .self = 1 }, /* base factor clock, 2x */ - } -}; /** - * sunxi_divs_clk_setup() - Setup function for leaf divisors on clocks - * - * These clocks look something like this - * ________________________ - * | ___divisor 1---|----> to consumer - * parent >--| pll___/___divisor 2---|----> to consumer - * | \_______________|____> to consumer - * |________________________| + * of_periph_rtc_clk_setup() - Setup function for periph rtc clk */ - -static struct clk ** __init sunxi_divs_clk_setup(struct device_node *node, - const struct divs_data *data) +void of_sunxi_periph_rtc_clk_setup(struct device_node *node) { - struct clk_onecell_data *clk_data; - const char *parent; - const char *clk_name; - struct clk **clks, *pclk; - struct clk_hw *gate_hw, *rate_hw; - const struct clk_ops *rate_ops; - struct clk_gate *gate = NULL; - struct clk_fixed_factor *fix_factor; - struct clk_divider *divider; - struct factors_data factors = *data->factors; - char *derived_name = NULL; - void __iomem *reg; - int ndivs = SUNXI_DIVS_MAX_QTY, i = 0; - int flags, clkflags; - - /* if number of children known, use it */ - if (data->ndivs) - ndivs = data->ndivs; - - /* Try to find a name for base factor clock */ - for (i = 0; i < ndivs; i++) { - if (data->div[i].self) { - of_property_read_string_index(node, "clock-output-names", - i, &factors.name); - break; - } - } - /* If we don't have a .self clk use the first output-name up to '_' */ - if (factors.name == NULL) { - char *endp; - - of_property_read_string_index(node, "clock-output-names", - 0, &clk_name); - endp = strchr(clk_name, '_'); - if (endp) { - derived_name = kstrndup(clk_name, endp - clk_name, - GFP_KERNEL); - factors.name = derived_name; - } else { - factors.name = clk_name; - } - } - - /* Set up factor clock that we will be dividing */ - pclk = sunxi_factors_clk_setup(node, &factors); - if (!pclk) - return NULL; - - parent = __clk_get_name(pclk); - kfree(derived_name); + struct clk *clk; + const char *clk_name = node->name; + struct periph_init_data *periph; - 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; + if (of_property_read_string(node, "clock-output-names", &clk_name)) { + pr_err("%s:get clock-output-names failed in %s node\n", + __func__, node->full_name); + return; } - clk_data = kmalloc(sizeof(struct clk_onecell_data), GFP_KERNEL); - if (!clk_data) - goto out_unmap; - - clks = kcalloc(ndivs, sizeof(*clks), GFP_KERNEL); - if (!clks) - goto free_clkdata; - - clk_data->clks = clks; - - /* It's not a good idea to have automatic reparenting changing - * our RAM clock! */ - clkflags = !strcmp("pll5", parent) ? 0 : CLK_SET_RATE_PARENT; - - for (i = 0; i < ndivs; i++) { - if (of_property_read_string_index(node, "clock-output-names", - i, &clk_name) != 0) - break; - - /* If this is the base factor clock, only update clks */ - if (data->div[i].self) { - clk_data->clks[i] = pclk; - continue; - } - - gate_hw = NULL; - rate_hw = NULL; - rate_ops = NULL; - - /* If this leaf clock can be gated, create a gate */ - if (data->div[i].gate) { - gate = kzalloc(sizeof(*gate), GFP_KERNEL); - if (!gate) - goto free_clks; - - gate->reg = reg; - gate->bit_idx = data->div[i].gate; - gate->lock = &clk_lock; - - gate_hw = &gate->hw; - } - - /* Leaves can be fixed or configurable divisors */ - if (data->div[i].fixed) { - fix_factor = kzalloc(sizeof(*fix_factor), GFP_KERNEL); - if (!fix_factor) - goto free_gate; - - fix_factor->mult = 1; - fix_factor->div = data->div[i].fixed; - - rate_hw = &fix_factor->hw; - rate_ops = &clk_fixed_factor_ops; - } else { - divider = kzalloc(sizeof(*divider), GFP_KERNEL); - if (!divider) - goto free_gate; - - flags = data->div[i].pow ? CLK_DIVIDER_POWER_OF_TWO : 0; - - divider->reg = reg; - divider->shift = data->div[i].shift; - divider->width = SUNXI_DIVISOR_WIDTH; - divider->flags = flags; - divider->lock = &clk_lock; - divider->table = data->div[i].table; - - rate_hw = ÷r->hw; - rate_ops = &clk_divider_ops; - } - - /* Wrap the (potential) gate and the divisor on a composite - * clock to unify them */ - clks[i] = clk_register_composite(NULL, clk_name, &parent, 1, - NULL, NULL, - rate_hw, rate_ops, - gate_hw, &clk_gate_ops, - clkflags); - - WARN_ON(IS_ERR(clk_data->clks[i])); + periph = sunxi_clk_get_periph_rtc_by_name(clk_name); + if (!periph) { + pr_err("clk %s not found in %s\n", clk_name, __func__); + return; } - /* Adjust to the real max */ - clk_data->clk_num = i; + /* register clk */ + clk = sunxi_clk_register_periph(periph, + sunxi_clk_rtc_base); - 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; + if (!IS_ERR(clk)) { + clk_register_clkdev(clk, clk_name, NULL); + of_clk_add_provider(node, of_clk_src_simple_get, clk); + return; } - return clks; -free_gate: - kfree(gate); -free_clks: - kfree(clks); -free_clkdata: - kfree(clk_data); -out_unmap: - iounmap(reg); - return NULL; + pr_err("clk %s not found in %s\n", clk_name, __func__); } -static void __init sun4i_pll5_clk_setup(struct device_node *node) +#ifdef CONFIG_PM_SLEEP +void sunxi_factor_clk_save(struct sunxi_factor_clk_reg_cache *factor_clk_reg) { - struct clk **clks; - - clks = sunxi_divs_clk_setup(node, &pll5_divs_data); - if (!clks) - return; - - /* Protect PLL5_DDR */ - __clk_get(clks[0]); - clk_prepare_enable(clks[0]); + factor_clk_reg->config_value = readl(factor_clk_reg->config_reg); + if (factor_clk_reg->sdmpat_reg) + factor_clk_reg->sdmpat_value = readl(factor_clk_reg->sdmpat_reg); } -CLK_OF_DECLARE(sun4i_pll5, "allwinner,sun4i-a10-pll5-clk", - sun4i_pll5_clk_setup); -static void __init sun4i_pll6_clk_setup(struct device_node *node) +void sunxi_factor_clk_restore(struct sunxi_factor_clk_reg_cache *factor_clk_reg) { - sunxi_divs_clk_setup(node, &pll6_divs_data); -} -CLK_OF_DECLARE(sun4i_pll6, "allwinner,sun4i-a10-pll6-clk", - sun4i_pll6_clk_setup); + if (factor_clk_reg->sdmpat_reg) + writel(factor_clk_reg->sdmpat_value, factor_clk_reg->sdmpat_reg); + writel(factor_clk_reg->config_value, factor_clk_reg->config_reg); -static void __init sun6i_pll6_clk_setup(struct device_node *node) -{ - sunxi_divs_clk_setup(node, &sun6i_a31_pll6_divs_data); } -CLK_OF_DECLARE(sun6i_pll6, "allwinner,sun6i-a31-pll6-clk", - sun6i_pll6_clk_setup); -/* - * sun6i display - * - * rate = parent_rate / (m + 1); - */ -static void sun6i_display_factors(struct factors_request *req) +void sunxi_periph_clk_save(struct sunxi_periph_clk_reg_cache *periph_clk_reg) { - u8 m; - - if (req->rate > req->parent_rate) - req->rate = req->parent_rate; - - m = DIV_ROUND_UP(req->parent_rate, req->rate); - - req->rate = req->parent_rate / m; - req->m = m - 1; + if (periph_clk_reg->gate_dram_reg) + periph_clk_reg->gate_dram_value = readl(periph_clk_reg->gate_dram_reg); + if (periph_clk_reg->gate_reset_reg) + periph_clk_reg->gate_reset_value = readl(periph_clk_reg->gate_reset_reg); + if (periph_clk_reg->gate_enable_reg) + periph_clk_reg->gate_enable_value = readl(periph_clk_reg->gate_enable_reg); + if (periph_clk_reg->divider_reg) + periph_clk_reg->divider_value = readl(periph_clk_reg->divider_reg); + if (periph_clk_reg->mux_reg) + periph_clk_reg->mux_value = readl(periph_clk_reg->mux_reg); + if (periph_clk_reg->gate_bus_reg) + periph_clk_reg->gate_bus_value = readl(periph_clk_reg->gate_bus_reg); } -static const struct clk_factors_config sun6i_display_config = { - .mshift = 0, - .mwidth = 4, -}; - -static const struct factors_data sun6i_display_data __initconst = { - .enable = 31, - .mux = 24, - .muxmask = BIT(2) | BIT(1) | BIT(0), - .table = &sun6i_display_config, - .getter = sun6i_display_factors, -}; - -static void __init sun6i_display_setup(struct device_node *node) +void sunxi_periph_clk_restore(struct sunxi_periph_clk_reg_cache *periph_clk_reg) { - sunxi_factors_clk_setup(node, &sun6i_display_data); + /* we should take care of the order, fix me */ + if (periph_clk_reg->gate_dram_reg) + writel(periph_clk_reg->gate_dram_value, periph_clk_reg->gate_dram_reg); + if (periph_clk_reg->gate_reset_reg) + writel(periph_clk_reg->gate_reset_value, periph_clk_reg->gate_reset_reg); + if (periph_clk_reg->gate_enable_reg) + writel(periph_clk_reg->gate_enable_value, periph_clk_reg->gate_enable_reg); + if (periph_clk_reg->divider_reg) + writel(periph_clk_reg->divider_value, periph_clk_reg->divider_reg); + if (periph_clk_reg->mux_reg) + writel(periph_clk_reg->mux_value, periph_clk_reg->mux_reg); + if (periph_clk_reg->gate_bus_reg) + writel(periph_clk_reg->gate_bus_value, periph_clk_reg->gate_bus_reg); } -CLK_OF_DECLARE(sun6i_display, "allwinner,sun6i-a31-display-clk", - sun6i_display_setup); +#endif + +CLK_OF_DECLARE(sunxi_clocks_init, "allwinner,clk-init", of_sunxi_clocks_init); +CLK_OF_DECLARE(sunxi_fixed_clk, "allwinner,fixed-clock", of_sunxi_fixed_clk_setup); +CLK_OF_DECLARE(sunxi_pll_clk, "allwinner,pll-clock", of_sunxi_pll_clk_setup); +CLK_OF_DECLARE(sunxi_cpus_pll_clk, "allwinner,cpus-pll-clock", of_sunxi_cpus_pll_clk_setup); +CLK_OF_DECLARE(sunxi_fixed_factor_clk, "allwinner,fixed-factor-clock", of_sunxi_fixed_factor_clk_setup); +CLK_OF_DECLARE(sunxi_cpu_clk, "allwinner,cpu-clock", of_sunxi_cpu_clk_setup); +CLK_OF_DECLARE(sunxi_periph_clk, "allwinner,periph-clock", of_sunxi_periph_clk_setup); +CLK_OF_DECLARE(periph_cpus_clk, "allwinner,periph-cpus-clock", of_sunxi_periph_cpus_clk_setup); +CLK_OF_DECLARE(periph_rtc_clk, "allwinner,periph-rtc-clock", of_sunxi_periph_rtc_clk_setup);