18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * clk-flexgen.c 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) ST-Microelectronics SA 2013 68c2ecf20Sopenharmony_ci * Author: Maxime Coquelin <maxime.coquelin@st.com> for ST-Microelectronics. 78c2ecf20Sopenharmony_ci */ 88c2ecf20Sopenharmony_ci 98c2ecf20Sopenharmony_ci#include <linux/clk.h> 108c2ecf20Sopenharmony_ci#include <linux/clk-provider.h> 118c2ecf20Sopenharmony_ci#include <linux/module.h> 128c2ecf20Sopenharmony_ci#include <linux/slab.h> 138c2ecf20Sopenharmony_ci#include <linux/io.h> 148c2ecf20Sopenharmony_ci#include <linux/err.h> 158c2ecf20Sopenharmony_ci#include <linux/string.h> 168c2ecf20Sopenharmony_ci#include <linux/of.h> 178c2ecf20Sopenharmony_ci#include <linux/of_address.h> 188c2ecf20Sopenharmony_ci 198c2ecf20Sopenharmony_cistruct clkgen_data { 208c2ecf20Sopenharmony_ci unsigned long flags; 218c2ecf20Sopenharmony_ci bool mode; 228c2ecf20Sopenharmony_ci}; 238c2ecf20Sopenharmony_ci 248c2ecf20Sopenharmony_cistruct flexgen { 258c2ecf20Sopenharmony_ci struct clk_hw hw; 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_ci /* Crossbar */ 288c2ecf20Sopenharmony_ci struct clk_mux mux; 298c2ecf20Sopenharmony_ci /* Pre-divisor's gate */ 308c2ecf20Sopenharmony_ci struct clk_gate pgate; 318c2ecf20Sopenharmony_ci /* Pre-divisor */ 328c2ecf20Sopenharmony_ci struct clk_divider pdiv; 338c2ecf20Sopenharmony_ci /* Final divisor's gate */ 348c2ecf20Sopenharmony_ci struct clk_gate fgate; 358c2ecf20Sopenharmony_ci /* Final divisor */ 368c2ecf20Sopenharmony_ci struct clk_divider fdiv; 378c2ecf20Sopenharmony_ci /* Asynchronous mode control */ 388c2ecf20Sopenharmony_ci struct clk_gate sync; 398c2ecf20Sopenharmony_ci /* hw control flags */ 408c2ecf20Sopenharmony_ci bool control_mode; 418c2ecf20Sopenharmony_ci}; 428c2ecf20Sopenharmony_ci 438c2ecf20Sopenharmony_ci#define to_flexgen(_hw) container_of(_hw, struct flexgen, hw) 448c2ecf20Sopenharmony_ci#define to_clk_gate(_hw) container_of(_hw, struct clk_gate, hw) 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_cistatic int flexgen_enable(struct clk_hw *hw) 478c2ecf20Sopenharmony_ci{ 488c2ecf20Sopenharmony_ci struct flexgen *flexgen = to_flexgen(hw); 498c2ecf20Sopenharmony_ci struct clk_hw *pgate_hw = &flexgen->pgate.hw; 508c2ecf20Sopenharmony_ci struct clk_hw *fgate_hw = &flexgen->fgate.hw; 518c2ecf20Sopenharmony_ci 528c2ecf20Sopenharmony_ci __clk_hw_set_clk(pgate_hw, hw); 538c2ecf20Sopenharmony_ci __clk_hw_set_clk(fgate_hw, hw); 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_ci clk_gate_ops.enable(pgate_hw); 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_ci clk_gate_ops.enable(fgate_hw); 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_ci pr_debug("%s: flexgen output enabled\n", clk_hw_get_name(hw)); 608c2ecf20Sopenharmony_ci return 0; 618c2ecf20Sopenharmony_ci} 628c2ecf20Sopenharmony_ci 638c2ecf20Sopenharmony_cistatic void flexgen_disable(struct clk_hw *hw) 648c2ecf20Sopenharmony_ci{ 658c2ecf20Sopenharmony_ci struct flexgen *flexgen = to_flexgen(hw); 668c2ecf20Sopenharmony_ci struct clk_hw *fgate_hw = &flexgen->fgate.hw; 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_ci /* disable only the final gate */ 698c2ecf20Sopenharmony_ci __clk_hw_set_clk(fgate_hw, hw); 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_ci clk_gate_ops.disable(fgate_hw); 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_ci pr_debug("%s: flexgen output disabled\n", clk_hw_get_name(hw)); 748c2ecf20Sopenharmony_ci} 758c2ecf20Sopenharmony_ci 768c2ecf20Sopenharmony_cistatic int flexgen_is_enabled(struct clk_hw *hw) 778c2ecf20Sopenharmony_ci{ 788c2ecf20Sopenharmony_ci struct flexgen *flexgen = to_flexgen(hw); 798c2ecf20Sopenharmony_ci struct clk_hw *fgate_hw = &flexgen->fgate.hw; 808c2ecf20Sopenharmony_ci 818c2ecf20Sopenharmony_ci __clk_hw_set_clk(fgate_hw, hw); 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_ci if (!clk_gate_ops.is_enabled(fgate_hw)) 848c2ecf20Sopenharmony_ci return 0; 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_ci return 1; 878c2ecf20Sopenharmony_ci} 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_cistatic u8 flexgen_get_parent(struct clk_hw *hw) 908c2ecf20Sopenharmony_ci{ 918c2ecf20Sopenharmony_ci struct flexgen *flexgen = to_flexgen(hw); 928c2ecf20Sopenharmony_ci struct clk_hw *mux_hw = &flexgen->mux.hw; 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_ci __clk_hw_set_clk(mux_hw, hw); 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_ci return clk_mux_ops.get_parent(mux_hw); 978c2ecf20Sopenharmony_ci} 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_cistatic int flexgen_set_parent(struct clk_hw *hw, u8 index) 1008c2ecf20Sopenharmony_ci{ 1018c2ecf20Sopenharmony_ci struct flexgen *flexgen = to_flexgen(hw); 1028c2ecf20Sopenharmony_ci struct clk_hw *mux_hw = &flexgen->mux.hw; 1038c2ecf20Sopenharmony_ci 1048c2ecf20Sopenharmony_ci __clk_hw_set_clk(mux_hw, hw); 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_ci return clk_mux_ops.set_parent(mux_hw, index); 1078c2ecf20Sopenharmony_ci} 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_cistatic inline unsigned long 1108c2ecf20Sopenharmony_ciclk_best_div(unsigned long parent_rate, unsigned long rate) 1118c2ecf20Sopenharmony_ci{ 1128c2ecf20Sopenharmony_ci return parent_rate / rate + ((rate > (2*(parent_rate % rate))) ? 0 : 1); 1138c2ecf20Sopenharmony_ci} 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_cistatic long flexgen_round_rate(struct clk_hw *hw, unsigned long rate, 1168c2ecf20Sopenharmony_ci unsigned long *prate) 1178c2ecf20Sopenharmony_ci{ 1188c2ecf20Sopenharmony_ci unsigned long div; 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_ci /* Round div according to exact prate and wished rate */ 1218c2ecf20Sopenharmony_ci div = clk_best_div(*prate, rate); 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_ci if (clk_hw_get_flags(hw) & CLK_SET_RATE_PARENT) { 1248c2ecf20Sopenharmony_ci *prate = rate * div; 1258c2ecf20Sopenharmony_ci return rate; 1268c2ecf20Sopenharmony_ci } 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_ci return *prate / div; 1298c2ecf20Sopenharmony_ci} 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_cistatic unsigned long flexgen_recalc_rate(struct clk_hw *hw, 1328c2ecf20Sopenharmony_ci unsigned long parent_rate) 1338c2ecf20Sopenharmony_ci{ 1348c2ecf20Sopenharmony_ci struct flexgen *flexgen = to_flexgen(hw); 1358c2ecf20Sopenharmony_ci struct clk_hw *pdiv_hw = &flexgen->pdiv.hw; 1368c2ecf20Sopenharmony_ci struct clk_hw *fdiv_hw = &flexgen->fdiv.hw; 1378c2ecf20Sopenharmony_ci unsigned long mid_rate; 1388c2ecf20Sopenharmony_ci 1398c2ecf20Sopenharmony_ci __clk_hw_set_clk(pdiv_hw, hw); 1408c2ecf20Sopenharmony_ci __clk_hw_set_clk(fdiv_hw, hw); 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_ci mid_rate = clk_divider_ops.recalc_rate(pdiv_hw, parent_rate); 1438c2ecf20Sopenharmony_ci 1448c2ecf20Sopenharmony_ci return clk_divider_ops.recalc_rate(fdiv_hw, mid_rate); 1458c2ecf20Sopenharmony_ci} 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_cistatic int flexgen_set_rate(struct clk_hw *hw, unsigned long rate, 1488c2ecf20Sopenharmony_ci unsigned long parent_rate) 1498c2ecf20Sopenharmony_ci{ 1508c2ecf20Sopenharmony_ci struct flexgen *flexgen = to_flexgen(hw); 1518c2ecf20Sopenharmony_ci struct clk_hw *pdiv_hw = &flexgen->pdiv.hw; 1528c2ecf20Sopenharmony_ci struct clk_hw *fdiv_hw = &flexgen->fdiv.hw; 1538c2ecf20Sopenharmony_ci struct clk_hw *sync_hw = &flexgen->sync.hw; 1548c2ecf20Sopenharmony_ci struct clk_gate *config = to_clk_gate(sync_hw); 1558c2ecf20Sopenharmony_ci unsigned long div = 0; 1568c2ecf20Sopenharmony_ci int ret = 0; 1578c2ecf20Sopenharmony_ci u32 reg; 1588c2ecf20Sopenharmony_ci 1598c2ecf20Sopenharmony_ci __clk_hw_set_clk(pdiv_hw, hw); 1608c2ecf20Sopenharmony_ci __clk_hw_set_clk(fdiv_hw, hw); 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_ci if (flexgen->control_mode) { 1638c2ecf20Sopenharmony_ci reg = readl(config->reg); 1648c2ecf20Sopenharmony_ci reg &= ~BIT(config->bit_idx); 1658c2ecf20Sopenharmony_ci writel(reg, config->reg); 1668c2ecf20Sopenharmony_ci } 1678c2ecf20Sopenharmony_ci 1688c2ecf20Sopenharmony_ci div = clk_best_div(parent_rate, rate); 1698c2ecf20Sopenharmony_ci 1708c2ecf20Sopenharmony_ci /* 1718c2ecf20Sopenharmony_ci * pdiv is mainly targeted for low freq results, while fdiv 1728c2ecf20Sopenharmony_ci * should be used for div <= 64. The other way round can 1738c2ecf20Sopenharmony_ci * lead to 'duty cycle' issues. 1748c2ecf20Sopenharmony_ci */ 1758c2ecf20Sopenharmony_ci 1768c2ecf20Sopenharmony_ci if (div <= 64) { 1778c2ecf20Sopenharmony_ci clk_divider_ops.set_rate(pdiv_hw, parent_rate, parent_rate); 1788c2ecf20Sopenharmony_ci ret = clk_divider_ops.set_rate(fdiv_hw, rate, rate * div); 1798c2ecf20Sopenharmony_ci } else { 1808c2ecf20Sopenharmony_ci clk_divider_ops.set_rate(fdiv_hw, parent_rate, parent_rate); 1818c2ecf20Sopenharmony_ci ret = clk_divider_ops.set_rate(pdiv_hw, rate, rate * div); 1828c2ecf20Sopenharmony_ci } 1838c2ecf20Sopenharmony_ci 1848c2ecf20Sopenharmony_ci return ret; 1858c2ecf20Sopenharmony_ci} 1868c2ecf20Sopenharmony_ci 1878c2ecf20Sopenharmony_cistatic const struct clk_ops flexgen_ops = { 1888c2ecf20Sopenharmony_ci .enable = flexgen_enable, 1898c2ecf20Sopenharmony_ci .disable = flexgen_disable, 1908c2ecf20Sopenharmony_ci .is_enabled = flexgen_is_enabled, 1918c2ecf20Sopenharmony_ci .get_parent = flexgen_get_parent, 1928c2ecf20Sopenharmony_ci .set_parent = flexgen_set_parent, 1938c2ecf20Sopenharmony_ci .round_rate = flexgen_round_rate, 1948c2ecf20Sopenharmony_ci .recalc_rate = flexgen_recalc_rate, 1958c2ecf20Sopenharmony_ci .set_rate = flexgen_set_rate, 1968c2ecf20Sopenharmony_ci}; 1978c2ecf20Sopenharmony_ci 1988c2ecf20Sopenharmony_cistatic struct clk *clk_register_flexgen(const char *name, 1998c2ecf20Sopenharmony_ci const char **parent_names, u8 num_parents, 2008c2ecf20Sopenharmony_ci void __iomem *reg, spinlock_t *lock, u32 idx, 2018c2ecf20Sopenharmony_ci unsigned long flexgen_flags, bool mode) { 2028c2ecf20Sopenharmony_ci struct flexgen *fgxbar; 2038c2ecf20Sopenharmony_ci struct clk *clk; 2048c2ecf20Sopenharmony_ci struct clk_init_data init; 2058c2ecf20Sopenharmony_ci u32 xbar_shift; 2068c2ecf20Sopenharmony_ci void __iomem *xbar_reg, *fdiv_reg; 2078c2ecf20Sopenharmony_ci 2088c2ecf20Sopenharmony_ci fgxbar = kzalloc(sizeof(struct flexgen), GFP_KERNEL); 2098c2ecf20Sopenharmony_ci if (!fgxbar) 2108c2ecf20Sopenharmony_ci return ERR_PTR(-ENOMEM); 2118c2ecf20Sopenharmony_ci 2128c2ecf20Sopenharmony_ci init.name = name; 2138c2ecf20Sopenharmony_ci init.ops = &flexgen_ops; 2148c2ecf20Sopenharmony_ci init.flags = CLK_GET_RATE_NOCACHE | flexgen_flags; 2158c2ecf20Sopenharmony_ci init.parent_names = parent_names; 2168c2ecf20Sopenharmony_ci init.num_parents = num_parents; 2178c2ecf20Sopenharmony_ci 2188c2ecf20Sopenharmony_ci xbar_reg = reg + 0x18 + (idx & ~0x3); 2198c2ecf20Sopenharmony_ci xbar_shift = (idx % 4) * 0x8; 2208c2ecf20Sopenharmony_ci fdiv_reg = reg + 0x164 + idx * 4; 2218c2ecf20Sopenharmony_ci 2228c2ecf20Sopenharmony_ci /* Crossbar element config */ 2238c2ecf20Sopenharmony_ci fgxbar->mux.lock = lock; 2248c2ecf20Sopenharmony_ci fgxbar->mux.mask = BIT(6) - 1; 2258c2ecf20Sopenharmony_ci fgxbar->mux.reg = xbar_reg; 2268c2ecf20Sopenharmony_ci fgxbar->mux.shift = xbar_shift; 2278c2ecf20Sopenharmony_ci fgxbar->mux.table = NULL; 2288c2ecf20Sopenharmony_ci 2298c2ecf20Sopenharmony_ci 2308c2ecf20Sopenharmony_ci /* Pre-divider's gate config (in xbar register)*/ 2318c2ecf20Sopenharmony_ci fgxbar->pgate.lock = lock; 2328c2ecf20Sopenharmony_ci fgxbar->pgate.reg = xbar_reg; 2338c2ecf20Sopenharmony_ci fgxbar->pgate.bit_idx = xbar_shift + 6; 2348c2ecf20Sopenharmony_ci 2358c2ecf20Sopenharmony_ci /* Pre-divider config */ 2368c2ecf20Sopenharmony_ci fgxbar->pdiv.lock = lock; 2378c2ecf20Sopenharmony_ci fgxbar->pdiv.reg = reg + 0x58 + idx * 4; 2388c2ecf20Sopenharmony_ci fgxbar->pdiv.width = 10; 2398c2ecf20Sopenharmony_ci 2408c2ecf20Sopenharmony_ci /* Final divider's gate config */ 2418c2ecf20Sopenharmony_ci fgxbar->fgate.lock = lock; 2428c2ecf20Sopenharmony_ci fgxbar->fgate.reg = fdiv_reg; 2438c2ecf20Sopenharmony_ci fgxbar->fgate.bit_idx = 6; 2448c2ecf20Sopenharmony_ci 2458c2ecf20Sopenharmony_ci /* Final divider config */ 2468c2ecf20Sopenharmony_ci fgxbar->fdiv.lock = lock; 2478c2ecf20Sopenharmony_ci fgxbar->fdiv.reg = fdiv_reg; 2488c2ecf20Sopenharmony_ci fgxbar->fdiv.width = 6; 2498c2ecf20Sopenharmony_ci 2508c2ecf20Sopenharmony_ci /* Final divider sync config */ 2518c2ecf20Sopenharmony_ci fgxbar->sync.lock = lock; 2528c2ecf20Sopenharmony_ci fgxbar->sync.reg = fdiv_reg; 2538c2ecf20Sopenharmony_ci fgxbar->sync.bit_idx = 7; 2548c2ecf20Sopenharmony_ci 2558c2ecf20Sopenharmony_ci fgxbar->control_mode = mode; 2568c2ecf20Sopenharmony_ci 2578c2ecf20Sopenharmony_ci fgxbar->hw.init = &init; 2588c2ecf20Sopenharmony_ci 2598c2ecf20Sopenharmony_ci clk = clk_register(NULL, &fgxbar->hw); 2608c2ecf20Sopenharmony_ci if (IS_ERR(clk)) 2618c2ecf20Sopenharmony_ci kfree(fgxbar); 2628c2ecf20Sopenharmony_ci else 2638c2ecf20Sopenharmony_ci pr_debug("%s: parent %s rate %u\n", 2648c2ecf20Sopenharmony_ci __clk_get_name(clk), 2658c2ecf20Sopenharmony_ci __clk_get_name(clk_get_parent(clk)), 2668c2ecf20Sopenharmony_ci (unsigned int)clk_get_rate(clk)); 2678c2ecf20Sopenharmony_ci return clk; 2688c2ecf20Sopenharmony_ci} 2698c2ecf20Sopenharmony_ci 2708c2ecf20Sopenharmony_cistatic const char ** __init flexgen_get_parents(struct device_node *np, 2718c2ecf20Sopenharmony_ci int *num_parents) 2728c2ecf20Sopenharmony_ci{ 2738c2ecf20Sopenharmony_ci const char **parents; 2748c2ecf20Sopenharmony_ci unsigned int nparents; 2758c2ecf20Sopenharmony_ci 2768c2ecf20Sopenharmony_ci nparents = of_clk_get_parent_count(np); 2778c2ecf20Sopenharmony_ci if (WARN_ON(!nparents)) 2788c2ecf20Sopenharmony_ci return NULL; 2798c2ecf20Sopenharmony_ci 2808c2ecf20Sopenharmony_ci parents = kcalloc(nparents, sizeof(const char *), GFP_KERNEL); 2818c2ecf20Sopenharmony_ci if (!parents) 2828c2ecf20Sopenharmony_ci return NULL; 2838c2ecf20Sopenharmony_ci 2848c2ecf20Sopenharmony_ci *num_parents = of_clk_parent_fill(np, parents, nparents); 2858c2ecf20Sopenharmony_ci 2868c2ecf20Sopenharmony_ci return parents; 2878c2ecf20Sopenharmony_ci} 2888c2ecf20Sopenharmony_ci 2898c2ecf20Sopenharmony_cistatic const struct clkgen_data clkgen_audio = { 2908c2ecf20Sopenharmony_ci .flags = CLK_SET_RATE_PARENT, 2918c2ecf20Sopenharmony_ci}; 2928c2ecf20Sopenharmony_ci 2938c2ecf20Sopenharmony_cistatic const struct clkgen_data clkgen_video = { 2948c2ecf20Sopenharmony_ci .flags = CLK_SET_RATE_PARENT, 2958c2ecf20Sopenharmony_ci .mode = 1, 2968c2ecf20Sopenharmony_ci}; 2978c2ecf20Sopenharmony_ci 2988c2ecf20Sopenharmony_cistatic const struct of_device_id flexgen_of_match[] = { 2998c2ecf20Sopenharmony_ci { 3008c2ecf20Sopenharmony_ci .compatible = "st,flexgen-audio", 3018c2ecf20Sopenharmony_ci .data = &clkgen_audio, 3028c2ecf20Sopenharmony_ci }, 3038c2ecf20Sopenharmony_ci { 3048c2ecf20Sopenharmony_ci .compatible = "st,flexgen-video", 3058c2ecf20Sopenharmony_ci .data = &clkgen_video, 3068c2ecf20Sopenharmony_ci }, 3078c2ecf20Sopenharmony_ci {} 3088c2ecf20Sopenharmony_ci}; 3098c2ecf20Sopenharmony_ci 3108c2ecf20Sopenharmony_cistatic void __init st_of_flexgen_setup(struct device_node *np) 3118c2ecf20Sopenharmony_ci{ 3128c2ecf20Sopenharmony_ci struct device_node *pnode; 3138c2ecf20Sopenharmony_ci void __iomem *reg; 3148c2ecf20Sopenharmony_ci struct clk_onecell_data *clk_data; 3158c2ecf20Sopenharmony_ci const char **parents; 3168c2ecf20Sopenharmony_ci int num_parents, i; 3178c2ecf20Sopenharmony_ci spinlock_t *rlock = NULL; 3188c2ecf20Sopenharmony_ci const struct of_device_id *match; 3198c2ecf20Sopenharmony_ci struct clkgen_data *data = NULL; 3208c2ecf20Sopenharmony_ci unsigned long flex_flags = 0; 3218c2ecf20Sopenharmony_ci int ret; 3228c2ecf20Sopenharmony_ci bool clk_mode = 0; 3238c2ecf20Sopenharmony_ci 3248c2ecf20Sopenharmony_ci pnode = of_get_parent(np); 3258c2ecf20Sopenharmony_ci if (!pnode) 3268c2ecf20Sopenharmony_ci return; 3278c2ecf20Sopenharmony_ci 3288c2ecf20Sopenharmony_ci reg = of_iomap(pnode, 0); 3298c2ecf20Sopenharmony_ci of_node_put(pnode); 3308c2ecf20Sopenharmony_ci if (!reg) 3318c2ecf20Sopenharmony_ci return; 3328c2ecf20Sopenharmony_ci 3338c2ecf20Sopenharmony_ci parents = flexgen_get_parents(np, &num_parents); 3348c2ecf20Sopenharmony_ci if (!parents) { 3358c2ecf20Sopenharmony_ci iounmap(reg); 3368c2ecf20Sopenharmony_ci return; 3378c2ecf20Sopenharmony_ci } 3388c2ecf20Sopenharmony_ci 3398c2ecf20Sopenharmony_ci match = of_match_node(flexgen_of_match, np); 3408c2ecf20Sopenharmony_ci if (match) { 3418c2ecf20Sopenharmony_ci data = (struct clkgen_data *)match->data; 3428c2ecf20Sopenharmony_ci flex_flags = data->flags; 3438c2ecf20Sopenharmony_ci clk_mode = data->mode; 3448c2ecf20Sopenharmony_ci } 3458c2ecf20Sopenharmony_ci 3468c2ecf20Sopenharmony_ci clk_data = kzalloc(sizeof(*clk_data), GFP_KERNEL); 3478c2ecf20Sopenharmony_ci if (!clk_data) 3488c2ecf20Sopenharmony_ci goto err; 3498c2ecf20Sopenharmony_ci 3508c2ecf20Sopenharmony_ci ret = of_property_count_strings(np, "clock-output-names"); 3518c2ecf20Sopenharmony_ci if (ret <= 0) { 3528c2ecf20Sopenharmony_ci pr_err("%s: Failed to get number of output clocks (%d)", 3538c2ecf20Sopenharmony_ci __func__, clk_data->clk_num); 3548c2ecf20Sopenharmony_ci goto err; 3558c2ecf20Sopenharmony_ci } 3568c2ecf20Sopenharmony_ci clk_data->clk_num = ret; 3578c2ecf20Sopenharmony_ci 3588c2ecf20Sopenharmony_ci clk_data->clks = kcalloc(clk_data->clk_num, sizeof(struct clk *), 3598c2ecf20Sopenharmony_ci GFP_KERNEL); 3608c2ecf20Sopenharmony_ci if (!clk_data->clks) 3618c2ecf20Sopenharmony_ci goto err; 3628c2ecf20Sopenharmony_ci 3638c2ecf20Sopenharmony_ci rlock = kzalloc(sizeof(spinlock_t), GFP_KERNEL); 3648c2ecf20Sopenharmony_ci if (!rlock) 3658c2ecf20Sopenharmony_ci goto err; 3668c2ecf20Sopenharmony_ci 3678c2ecf20Sopenharmony_ci spin_lock_init(rlock); 3688c2ecf20Sopenharmony_ci 3698c2ecf20Sopenharmony_ci for (i = 0; i < clk_data->clk_num; i++) { 3708c2ecf20Sopenharmony_ci struct clk *clk; 3718c2ecf20Sopenharmony_ci const char *clk_name; 3728c2ecf20Sopenharmony_ci 3738c2ecf20Sopenharmony_ci if (of_property_read_string_index(np, "clock-output-names", 3748c2ecf20Sopenharmony_ci i, &clk_name)) { 3758c2ecf20Sopenharmony_ci break; 3768c2ecf20Sopenharmony_ci } 3778c2ecf20Sopenharmony_ci 3788c2ecf20Sopenharmony_ci flex_flags &= ~CLK_IS_CRITICAL; 3798c2ecf20Sopenharmony_ci of_clk_detect_critical(np, i, &flex_flags); 3808c2ecf20Sopenharmony_ci 3818c2ecf20Sopenharmony_ci /* 3828c2ecf20Sopenharmony_ci * If we read an empty clock name then the output is unused 3838c2ecf20Sopenharmony_ci */ 3848c2ecf20Sopenharmony_ci if (*clk_name == '\0') 3858c2ecf20Sopenharmony_ci continue; 3868c2ecf20Sopenharmony_ci 3878c2ecf20Sopenharmony_ci clk = clk_register_flexgen(clk_name, parents, num_parents, 3888c2ecf20Sopenharmony_ci reg, rlock, i, flex_flags, clk_mode); 3898c2ecf20Sopenharmony_ci 3908c2ecf20Sopenharmony_ci if (IS_ERR(clk)) 3918c2ecf20Sopenharmony_ci goto err; 3928c2ecf20Sopenharmony_ci 3938c2ecf20Sopenharmony_ci clk_data->clks[i] = clk; 3948c2ecf20Sopenharmony_ci } 3958c2ecf20Sopenharmony_ci 3968c2ecf20Sopenharmony_ci kfree(parents); 3978c2ecf20Sopenharmony_ci of_clk_add_provider(np, of_clk_src_onecell_get, clk_data); 3988c2ecf20Sopenharmony_ci 3998c2ecf20Sopenharmony_ci return; 4008c2ecf20Sopenharmony_ci 4018c2ecf20Sopenharmony_cierr: 4028c2ecf20Sopenharmony_ci iounmap(reg); 4038c2ecf20Sopenharmony_ci if (clk_data) 4048c2ecf20Sopenharmony_ci kfree(clk_data->clks); 4058c2ecf20Sopenharmony_ci kfree(clk_data); 4068c2ecf20Sopenharmony_ci kfree(parents); 4078c2ecf20Sopenharmony_ci kfree(rlock); 4088c2ecf20Sopenharmony_ci} 4098c2ecf20Sopenharmony_ciCLK_OF_DECLARE(flexgen, "st,flexgen", st_of_flexgen_setup); 410