18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright (c) 2014 MundoReader S.L. 48c2ecf20Sopenharmony_ci * Author: Heiko Stuebner <heiko@sntech.de> 58c2ecf20Sopenharmony_ci * 68c2ecf20Sopenharmony_ci * Copyright (c) 2016 Rockchip Electronics Co. Ltd. 78c2ecf20Sopenharmony_ci * Author: Xing Zheng <zhengxing@rock-chips.com> 88c2ecf20Sopenharmony_ci * 98c2ecf20Sopenharmony_ci * based on 108c2ecf20Sopenharmony_ci * 118c2ecf20Sopenharmony_ci * samsung/clk.c 128c2ecf20Sopenharmony_ci * Copyright (c) 2013 Samsung Electronics Co., Ltd. 138c2ecf20Sopenharmony_ci * Copyright (c) 2013 Linaro Ltd. 148c2ecf20Sopenharmony_ci * Author: Thomas Abraham <thomas.ab@samsung.com> 158c2ecf20Sopenharmony_ci */ 168c2ecf20Sopenharmony_ci 178c2ecf20Sopenharmony_ci#include <linux/slab.h> 188c2ecf20Sopenharmony_ci#include <linux/clk.h> 198c2ecf20Sopenharmony_ci#include <linux/clk-provider.h> 208c2ecf20Sopenharmony_ci#include <linux/io.h> 218c2ecf20Sopenharmony_ci#include <linux/mfd/syscon.h> 228c2ecf20Sopenharmony_ci#include <linux/regmap.h> 238c2ecf20Sopenharmony_ci#include <linux/reboot.h> 248c2ecf20Sopenharmony_ci#include <linux/rational.h> 258c2ecf20Sopenharmony_ci#include "clk.h" 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_ci/** 288c2ecf20Sopenharmony_ci * Register a clock branch. 298c2ecf20Sopenharmony_ci * Most clock branches have a form like 308c2ecf20Sopenharmony_ci * 318c2ecf20Sopenharmony_ci * src1 --|--\ 328c2ecf20Sopenharmony_ci * |M |--[GATE]-[DIV]- 338c2ecf20Sopenharmony_ci * src2 --|--/ 348c2ecf20Sopenharmony_ci * 358c2ecf20Sopenharmony_ci * sometimes without one of those components. 368c2ecf20Sopenharmony_ci */ 378c2ecf20Sopenharmony_cistatic struct clk *rockchip_clk_register_branch(const char *name, 388c2ecf20Sopenharmony_ci const char *const *parent_names, u8 num_parents, 398c2ecf20Sopenharmony_ci void __iomem *base, 408c2ecf20Sopenharmony_ci int muxdiv_offset, u8 mux_shift, u8 mux_width, u8 mux_flags, 418c2ecf20Sopenharmony_ci int div_offset, u8 div_shift, u8 div_width, u8 div_flags, 428c2ecf20Sopenharmony_ci struct clk_div_table *div_table, int gate_offset, 438c2ecf20Sopenharmony_ci u8 gate_shift, u8 gate_flags, unsigned long flags, 448c2ecf20Sopenharmony_ci spinlock_t *lock) 458c2ecf20Sopenharmony_ci{ 468c2ecf20Sopenharmony_ci struct clk_hw *hw; 478c2ecf20Sopenharmony_ci struct clk_mux *mux = NULL; 488c2ecf20Sopenharmony_ci struct clk_gate *gate = NULL; 498c2ecf20Sopenharmony_ci struct clk_divider *div = NULL; 508c2ecf20Sopenharmony_ci const struct clk_ops *mux_ops = NULL, *div_ops = NULL, 518c2ecf20Sopenharmony_ci *gate_ops = NULL; 528c2ecf20Sopenharmony_ci int ret; 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_ci if (num_parents > 1) { 558c2ecf20Sopenharmony_ci mux = kzalloc(sizeof(*mux), GFP_KERNEL); 568c2ecf20Sopenharmony_ci if (!mux) 578c2ecf20Sopenharmony_ci return ERR_PTR(-ENOMEM); 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_ci mux->reg = base + muxdiv_offset; 608c2ecf20Sopenharmony_ci mux->shift = mux_shift; 618c2ecf20Sopenharmony_ci mux->mask = BIT(mux_width) - 1; 628c2ecf20Sopenharmony_ci mux->flags = mux_flags; 638c2ecf20Sopenharmony_ci mux->lock = lock; 648c2ecf20Sopenharmony_ci mux_ops = (mux_flags & CLK_MUX_READ_ONLY) ? &clk_mux_ro_ops 658c2ecf20Sopenharmony_ci : &clk_mux_ops; 668c2ecf20Sopenharmony_ci } 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_ci if (gate_offset >= 0) { 698c2ecf20Sopenharmony_ci gate = kzalloc(sizeof(*gate), GFP_KERNEL); 708c2ecf20Sopenharmony_ci if (!gate) { 718c2ecf20Sopenharmony_ci ret = -ENOMEM; 728c2ecf20Sopenharmony_ci goto err_gate; 738c2ecf20Sopenharmony_ci } 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_ci gate->flags = gate_flags; 768c2ecf20Sopenharmony_ci gate->reg = base + gate_offset; 778c2ecf20Sopenharmony_ci gate->bit_idx = gate_shift; 788c2ecf20Sopenharmony_ci gate->lock = lock; 798c2ecf20Sopenharmony_ci gate_ops = &clk_gate_ops; 808c2ecf20Sopenharmony_ci } 818c2ecf20Sopenharmony_ci 828c2ecf20Sopenharmony_ci if (div_width > 0) { 838c2ecf20Sopenharmony_ci div = kzalloc(sizeof(*div), GFP_KERNEL); 848c2ecf20Sopenharmony_ci if (!div) { 858c2ecf20Sopenharmony_ci ret = -ENOMEM; 868c2ecf20Sopenharmony_ci goto err_div; 878c2ecf20Sopenharmony_ci } 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_ci div->flags = div_flags; 908c2ecf20Sopenharmony_ci if (div_offset) 918c2ecf20Sopenharmony_ci div->reg = base + div_offset; 928c2ecf20Sopenharmony_ci else 938c2ecf20Sopenharmony_ci div->reg = base + muxdiv_offset; 948c2ecf20Sopenharmony_ci div->shift = div_shift; 958c2ecf20Sopenharmony_ci div->width = div_width; 968c2ecf20Sopenharmony_ci div->lock = lock; 978c2ecf20Sopenharmony_ci div->table = div_table; 988c2ecf20Sopenharmony_ci div_ops = (div_flags & CLK_DIVIDER_READ_ONLY) 998c2ecf20Sopenharmony_ci ? &clk_divider_ro_ops 1008c2ecf20Sopenharmony_ci : &clk_divider_ops; 1018c2ecf20Sopenharmony_ci } 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_ci hw = clk_hw_register_composite(NULL, name, parent_names, num_parents, 1048c2ecf20Sopenharmony_ci mux ? &mux->hw : NULL, mux_ops, 1058c2ecf20Sopenharmony_ci div ? &div->hw : NULL, div_ops, 1068c2ecf20Sopenharmony_ci gate ? &gate->hw : NULL, gate_ops, 1078c2ecf20Sopenharmony_ci flags); 1088c2ecf20Sopenharmony_ci if (IS_ERR(hw)) { 1098c2ecf20Sopenharmony_ci kfree(div); 1108c2ecf20Sopenharmony_ci kfree(gate); 1118c2ecf20Sopenharmony_ci return ERR_CAST(hw); 1128c2ecf20Sopenharmony_ci } 1138c2ecf20Sopenharmony_ci 1148c2ecf20Sopenharmony_ci return hw->clk; 1158c2ecf20Sopenharmony_cierr_div: 1168c2ecf20Sopenharmony_ci kfree(gate); 1178c2ecf20Sopenharmony_cierr_gate: 1188c2ecf20Sopenharmony_ci kfree(mux); 1198c2ecf20Sopenharmony_ci return ERR_PTR(ret); 1208c2ecf20Sopenharmony_ci} 1218c2ecf20Sopenharmony_ci 1228c2ecf20Sopenharmony_cistruct rockchip_clk_frac { 1238c2ecf20Sopenharmony_ci struct notifier_block clk_nb; 1248c2ecf20Sopenharmony_ci struct clk_fractional_divider div; 1258c2ecf20Sopenharmony_ci struct clk_gate gate; 1268c2ecf20Sopenharmony_ci 1278c2ecf20Sopenharmony_ci struct clk_mux mux; 1288c2ecf20Sopenharmony_ci const struct clk_ops *mux_ops; 1298c2ecf20Sopenharmony_ci int mux_frac_idx; 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_ci bool rate_change_remuxed; 1328c2ecf20Sopenharmony_ci int rate_change_idx; 1338c2ecf20Sopenharmony_ci}; 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_ci#define to_rockchip_clk_frac_nb(nb) \ 1368c2ecf20Sopenharmony_ci container_of(nb, struct rockchip_clk_frac, clk_nb) 1378c2ecf20Sopenharmony_ci 1388c2ecf20Sopenharmony_cistatic int rockchip_clk_frac_notifier_cb(struct notifier_block *nb, 1398c2ecf20Sopenharmony_ci unsigned long event, void *data) 1408c2ecf20Sopenharmony_ci{ 1418c2ecf20Sopenharmony_ci struct clk_notifier_data *ndata = data; 1428c2ecf20Sopenharmony_ci struct rockchip_clk_frac *frac = to_rockchip_clk_frac_nb(nb); 1438c2ecf20Sopenharmony_ci struct clk_mux *frac_mux = &frac->mux; 1448c2ecf20Sopenharmony_ci int ret = 0; 1458c2ecf20Sopenharmony_ci 1468c2ecf20Sopenharmony_ci pr_debug("%s: event %lu, old_rate %lu, new_rate: %lu\n", 1478c2ecf20Sopenharmony_ci __func__, event, ndata->old_rate, ndata->new_rate); 1488c2ecf20Sopenharmony_ci if (event == PRE_RATE_CHANGE) { 1498c2ecf20Sopenharmony_ci frac->rate_change_idx = 1508c2ecf20Sopenharmony_ci frac->mux_ops->get_parent(&frac_mux->hw); 1518c2ecf20Sopenharmony_ci if (frac->rate_change_idx != frac->mux_frac_idx) { 1528c2ecf20Sopenharmony_ci frac->mux_ops->set_parent(&frac_mux->hw, 1538c2ecf20Sopenharmony_ci frac->mux_frac_idx); 1548c2ecf20Sopenharmony_ci frac->rate_change_remuxed = 1; 1558c2ecf20Sopenharmony_ci } 1568c2ecf20Sopenharmony_ci } else if (event == POST_RATE_CHANGE) { 1578c2ecf20Sopenharmony_ci /* 1588c2ecf20Sopenharmony_ci * The POST_RATE_CHANGE notifier runs directly after the 1598c2ecf20Sopenharmony_ci * divider clock is set in clk_change_rate, so we'll have 1608c2ecf20Sopenharmony_ci * remuxed back to the original parent before clk_change_rate 1618c2ecf20Sopenharmony_ci * reaches the mux itself. 1628c2ecf20Sopenharmony_ci */ 1638c2ecf20Sopenharmony_ci if (frac->rate_change_remuxed) { 1648c2ecf20Sopenharmony_ci frac->mux_ops->set_parent(&frac_mux->hw, 1658c2ecf20Sopenharmony_ci frac->rate_change_idx); 1668c2ecf20Sopenharmony_ci frac->rate_change_remuxed = 0; 1678c2ecf20Sopenharmony_ci } 1688c2ecf20Sopenharmony_ci } 1698c2ecf20Sopenharmony_ci 1708c2ecf20Sopenharmony_ci return notifier_from_errno(ret); 1718c2ecf20Sopenharmony_ci} 1728c2ecf20Sopenharmony_ci 1738c2ecf20Sopenharmony_ci/** 1748c2ecf20Sopenharmony_ci * fractional divider must set that denominator is 20 times larger than 1758c2ecf20Sopenharmony_ci * numerator to generate precise clock frequency. 1768c2ecf20Sopenharmony_ci */ 1778c2ecf20Sopenharmony_cistatic void rockchip_fractional_approximation(struct clk_hw *hw, 1788c2ecf20Sopenharmony_ci unsigned long rate, unsigned long *parent_rate, 1798c2ecf20Sopenharmony_ci unsigned long *m, unsigned long *n) 1808c2ecf20Sopenharmony_ci{ 1818c2ecf20Sopenharmony_ci struct clk_fractional_divider *fd = to_clk_fd(hw); 1828c2ecf20Sopenharmony_ci unsigned long p_rate, p_parent_rate; 1838c2ecf20Sopenharmony_ci struct clk_hw *p_parent; 1848c2ecf20Sopenharmony_ci unsigned long scale; 1858c2ecf20Sopenharmony_ci 1868c2ecf20Sopenharmony_ci p_rate = clk_hw_get_rate(clk_hw_get_parent(hw)); 1878c2ecf20Sopenharmony_ci if ((rate * 20 > p_rate) && (p_rate % rate != 0)) { 1888c2ecf20Sopenharmony_ci p_parent = clk_hw_get_parent(clk_hw_get_parent(hw)); 1898c2ecf20Sopenharmony_ci p_parent_rate = clk_hw_get_rate(p_parent); 1908c2ecf20Sopenharmony_ci *parent_rate = p_parent_rate; 1918c2ecf20Sopenharmony_ci } 1928c2ecf20Sopenharmony_ci 1938c2ecf20Sopenharmony_ci /* 1948c2ecf20Sopenharmony_ci * Get rate closer to *parent_rate to guarantee there is no overflow 1958c2ecf20Sopenharmony_ci * for m and n. In the result it will be the nearest rate left shifted 1968c2ecf20Sopenharmony_ci * by (scale - fd->nwidth) bits. 1978c2ecf20Sopenharmony_ci */ 1988c2ecf20Sopenharmony_ci scale = fls_long(*parent_rate / rate - 1); 1998c2ecf20Sopenharmony_ci if (scale > fd->nwidth) 2008c2ecf20Sopenharmony_ci rate <<= scale - fd->nwidth; 2018c2ecf20Sopenharmony_ci 2028c2ecf20Sopenharmony_ci rational_best_approximation(rate, *parent_rate, 2038c2ecf20Sopenharmony_ci GENMASK(fd->mwidth - 1, 0), GENMASK(fd->nwidth - 1, 0), 2048c2ecf20Sopenharmony_ci m, n); 2058c2ecf20Sopenharmony_ci} 2068c2ecf20Sopenharmony_ci 2078c2ecf20Sopenharmony_cistatic struct clk *rockchip_clk_register_frac_branch( 2088c2ecf20Sopenharmony_ci struct rockchip_clk_provider *ctx, const char *name, 2098c2ecf20Sopenharmony_ci const char *const *parent_names, u8 num_parents, 2108c2ecf20Sopenharmony_ci void __iomem *base, int muxdiv_offset, u8 div_flags, 2118c2ecf20Sopenharmony_ci int gate_offset, u8 gate_shift, u8 gate_flags, 2128c2ecf20Sopenharmony_ci unsigned long flags, struct rockchip_clk_branch *child, 2138c2ecf20Sopenharmony_ci spinlock_t *lock) 2148c2ecf20Sopenharmony_ci{ 2158c2ecf20Sopenharmony_ci struct clk_hw *hw; 2168c2ecf20Sopenharmony_ci struct rockchip_clk_frac *frac; 2178c2ecf20Sopenharmony_ci struct clk_gate *gate = NULL; 2188c2ecf20Sopenharmony_ci struct clk_fractional_divider *div = NULL; 2198c2ecf20Sopenharmony_ci const struct clk_ops *div_ops = NULL, *gate_ops = NULL; 2208c2ecf20Sopenharmony_ci 2218c2ecf20Sopenharmony_ci if (muxdiv_offset < 0) 2228c2ecf20Sopenharmony_ci return ERR_PTR(-EINVAL); 2238c2ecf20Sopenharmony_ci 2248c2ecf20Sopenharmony_ci if (child && child->branch_type != branch_mux) { 2258c2ecf20Sopenharmony_ci pr_err("%s: fractional child clock for %s can only be a mux\n", 2268c2ecf20Sopenharmony_ci __func__, name); 2278c2ecf20Sopenharmony_ci return ERR_PTR(-EINVAL); 2288c2ecf20Sopenharmony_ci } 2298c2ecf20Sopenharmony_ci 2308c2ecf20Sopenharmony_ci frac = kzalloc(sizeof(*frac), GFP_KERNEL); 2318c2ecf20Sopenharmony_ci if (!frac) 2328c2ecf20Sopenharmony_ci return ERR_PTR(-ENOMEM); 2338c2ecf20Sopenharmony_ci 2348c2ecf20Sopenharmony_ci if (gate_offset >= 0) { 2358c2ecf20Sopenharmony_ci gate = &frac->gate; 2368c2ecf20Sopenharmony_ci gate->flags = gate_flags; 2378c2ecf20Sopenharmony_ci gate->reg = base + gate_offset; 2388c2ecf20Sopenharmony_ci gate->bit_idx = gate_shift; 2398c2ecf20Sopenharmony_ci gate->lock = lock; 2408c2ecf20Sopenharmony_ci gate_ops = &clk_gate_ops; 2418c2ecf20Sopenharmony_ci } 2428c2ecf20Sopenharmony_ci 2438c2ecf20Sopenharmony_ci div = &frac->div; 2448c2ecf20Sopenharmony_ci div->flags = div_flags; 2458c2ecf20Sopenharmony_ci div->reg = base + muxdiv_offset; 2468c2ecf20Sopenharmony_ci div->mshift = 16; 2478c2ecf20Sopenharmony_ci div->mwidth = 16; 2488c2ecf20Sopenharmony_ci div->mmask = GENMASK(div->mwidth - 1, 0) << div->mshift; 2498c2ecf20Sopenharmony_ci div->nshift = 0; 2508c2ecf20Sopenharmony_ci div->nwidth = 16; 2518c2ecf20Sopenharmony_ci div->nmask = GENMASK(div->nwidth - 1, 0) << div->nshift; 2528c2ecf20Sopenharmony_ci div->lock = lock; 2538c2ecf20Sopenharmony_ci div->approximation = rockchip_fractional_approximation; 2548c2ecf20Sopenharmony_ci div_ops = &clk_fractional_divider_ops; 2558c2ecf20Sopenharmony_ci 2568c2ecf20Sopenharmony_ci hw = clk_hw_register_composite(NULL, name, parent_names, num_parents, 2578c2ecf20Sopenharmony_ci NULL, NULL, 2588c2ecf20Sopenharmony_ci &div->hw, div_ops, 2598c2ecf20Sopenharmony_ci gate ? &gate->hw : NULL, gate_ops, 2608c2ecf20Sopenharmony_ci flags | CLK_SET_RATE_UNGATE); 2618c2ecf20Sopenharmony_ci if (IS_ERR(hw)) { 2628c2ecf20Sopenharmony_ci kfree(frac); 2638c2ecf20Sopenharmony_ci return ERR_CAST(hw); 2648c2ecf20Sopenharmony_ci } 2658c2ecf20Sopenharmony_ci 2668c2ecf20Sopenharmony_ci if (child) { 2678c2ecf20Sopenharmony_ci struct clk_mux *frac_mux = &frac->mux; 2688c2ecf20Sopenharmony_ci struct clk_init_data init; 2698c2ecf20Sopenharmony_ci struct clk *mux_clk; 2708c2ecf20Sopenharmony_ci int ret; 2718c2ecf20Sopenharmony_ci 2728c2ecf20Sopenharmony_ci frac->mux_frac_idx = match_string(child->parent_names, 2738c2ecf20Sopenharmony_ci child->num_parents, name); 2748c2ecf20Sopenharmony_ci frac->mux_ops = &clk_mux_ops; 2758c2ecf20Sopenharmony_ci frac->clk_nb.notifier_call = rockchip_clk_frac_notifier_cb; 2768c2ecf20Sopenharmony_ci 2778c2ecf20Sopenharmony_ci frac_mux->reg = base + child->muxdiv_offset; 2788c2ecf20Sopenharmony_ci frac_mux->shift = child->mux_shift; 2798c2ecf20Sopenharmony_ci frac_mux->mask = BIT(child->mux_width) - 1; 2808c2ecf20Sopenharmony_ci frac_mux->flags = child->mux_flags; 2818c2ecf20Sopenharmony_ci frac_mux->lock = lock; 2828c2ecf20Sopenharmony_ci frac_mux->hw.init = &init; 2838c2ecf20Sopenharmony_ci 2848c2ecf20Sopenharmony_ci init.name = child->name; 2858c2ecf20Sopenharmony_ci init.flags = child->flags | CLK_SET_RATE_PARENT; 2868c2ecf20Sopenharmony_ci init.ops = frac->mux_ops; 2878c2ecf20Sopenharmony_ci init.parent_names = child->parent_names; 2888c2ecf20Sopenharmony_ci init.num_parents = child->num_parents; 2898c2ecf20Sopenharmony_ci 2908c2ecf20Sopenharmony_ci mux_clk = clk_register(NULL, &frac_mux->hw); 2918c2ecf20Sopenharmony_ci if (IS_ERR(mux_clk)) { 2928c2ecf20Sopenharmony_ci kfree(frac); 2938c2ecf20Sopenharmony_ci return mux_clk; 2948c2ecf20Sopenharmony_ci } 2958c2ecf20Sopenharmony_ci 2968c2ecf20Sopenharmony_ci rockchip_clk_add_lookup(ctx, mux_clk, child->id); 2978c2ecf20Sopenharmony_ci 2988c2ecf20Sopenharmony_ci /* notifier on the fraction divider to catch rate changes */ 2998c2ecf20Sopenharmony_ci if (frac->mux_frac_idx >= 0) { 3008c2ecf20Sopenharmony_ci pr_debug("%s: found fractional parent in mux at pos %d\n", 3018c2ecf20Sopenharmony_ci __func__, frac->mux_frac_idx); 3028c2ecf20Sopenharmony_ci ret = clk_notifier_register(hw->clk, &frac->clk_nb); 3038c2ecf20Sopenharmony_ci if (ret) 3048c2ecf20Sopenharmony_ci pr_err("%s: failed to register clock notifier for %s\n", 3058c2ecf20Sopenharmony_ci __func__, name); 3068c2ecf20Sopenharmony_ci } else { 3078c2ecf20Sopenharmony_ci pr_warn("%s: could not find %s as parent of %s, rate changes may not work\n", 3088c2ecf20Sopenharmony_ci __func__, name, child->name); 3098c2ecf20Sopenharmony_ci } 3108c2ecf20Sopenharmony_ci } 3118c2ecf20Sopenharmony_ci 3128c2ecf20Sopenharmony_ci return hw->clk; 3138c2ecf20Sopenharmony_ci} 3148c2ecf20Sopenharmony_ci 3158c2ecf20Sopenharmony_cistatic struct clk *rockchip_clk_register_factor_branch(const char *name, 3168c2ecf20Sopenharmony_ci const char *const *parent_names, u8 num_parents, 3178c2ecf20Sopenharmony_ci void __iomem *base, unsigned int mult, unsigned int div, 3188c2ecf20Sopenharmony_ci int gate_offset, u8 gate_shift, u8 gate_flags, 3198c2ecf20Sopenharmony_ci unsigned long flags, spinlock_t *lock) 3208c2ecf20Sopenharmony_ci{ 3218c2ecf20Sopenharmony_ci struct clk_hw *hw; 3228c2ecf20Sopenharmony_ci struct clk_gate *gate = NULL; 3238c2ecf20Sopenharmony_ci struct clk_fixed_factor *fix = NULL; 3248c2ecf20Sopenharmony_ci 3258c2ecf20Sopenharmony_ci /* without gate, register a simple factor clock */ 3268c2ecf20Sopenharmony_ci if (gate_offset == 0) { 3278c2ecf20Sopenharmony_ci return clk_register_fixed_factor(NULL, name, 3288c2ecf20Sopenharmony_ci parent_names[0], flags, mult, 3298c2ecf20Sopenharmony_ci div); 3308c2ecf20Sopenharmony_ci } 3318c2ecf20Sopenharmony_ci 3328c2ecf20Sopenharmony_ci gate = kzalloc(sizeof(*gate), GFP_KERNEL); 3338c2ecf20Sopenharmony_ci if (!gate) 3348c2ecf20Sopenharmony_ci return ERR_PTR(-ENOMEM); 3358c2ecf20Sopenharmony_ci 3368c2ecf20Sopenharmony_ci gate->flags = gate_flags; 3378c2ecf20Sopenharmony_ci gate->reg = base + gate_offset; 3388c2ecf20Sopenharmony_ci gate->bit_idx = gate_shift; 3398c2ecf20Sopenharmony_ci gate->lock = lock; 3408c2ecf20Sopenharmony_ci 3418c2ecf20Sopenharmony_ci fix = kzalloc(sizeof(*fix), GFP_KERNEL); 3428c2ecf20Sopenharmony_ci if (!fix) { 3438c2ecf20Sopenharmony_ci kfree(gate); 3448c2ecf20Sopenharmony_ci return ERR_PTR(-ENOMEM); 3458c2ecf20Sopenharmony_ci } 3468c2ecf20Sopenharmony_ci 3478c2ecf20Sopenharmony_ci fix->mult = mult; 3488c2ecf20Sopenharmony_ci fix->div = div; 3498c2ecf20Sopenharmony_ci 3508c2ecf20Sopenharmony_ci hw = clk_hw_register_composite(NULL, name, parent_names, num_parents, 3518c2ecf20Sopenharmony_ci NULL, NULL, 3528c2ecf20Sopenharmony_ci &fix->hw, &clk_fixed_factor_ops, 3538c2ecf20Sopenharmony_ci &gate->hw, &clk_gate_ops, flags); 3548c2ecf20Sopenharmony_ci if (IS_ERR(hw)) { 3558c2ecf20Sopenharmony_ci kfree(fix); 3568c2ecf20Sopenharmony_ci kfree(gate); 3578c2ecf20Sopenharmony_ci return ERR_CAST(hw); 3588c2ecf20Sopenharmony_ci } 3598c2ecf20Sopenharmony_ci 3608c2ecf20Sopenharmony_ci return hw->clk; 3618c2ecf20Sopenharmony_ci} 3628c2ecf20Sopenharmony_ci 3638c2ecf20Sopenharmony_cistruct rockchip_clk_provider *rockchip_clk_init(struct device_node *np, 3648c2ecf20Sopenharmony_ci void __iomem *base, 3658c2ecf20Sopenharmony_ci unsigned long nr_clks) 3668c2ecf20Sopenharmony_ci{ 3678c2ecf20Sopenharmony_ci struct rockchip_clk_provider *ctx; 3688c2ecf20Sopenharmony_ci struct clk **clk_table; 3698c2ecf20Sopenharmony_ci int i; 3708c2ecf20Sopenharmony_ci 3718c2ecf20Sopenharmony_ci ctx = kzalloc(sizeof(struct rockchip_clk_provider), GFP_KERNEL); 3728c2ecf20Sopenharmony_ci if (!ctx) 3738c2ecf20Sopenharmony_ci return ERR_PTR(-ENOMEM); 3748c2ecf20Sopenharmony_ci 3758c2ecf20Sopenharmony_ci clk_table = kcalloc(nr_clks, sizeof(struct clk *), GFP_KERNEL); 3768c2ecf20Sopenharmony_ci if (!clk_table) 3778c2ecf20Sopenharmony_ci goto err_free; 3788c2ecf20Sopenharmony_ci 3798c2ecf20Sopenharmony_ci for (i = 0; i < nr_clks; ++i) 3808c2ecf20Sopenharmony_ci clk_table[i] = ERR_PTR(-ENOENT); 3818c2ecf20Sopenharmony_ci 3828c2ecf20Sopenharmony_ci ctx->reg_base = base; 3838c2ecf20Sopenharmony_ci ctx->clk_data.clks = clk_table; 3848c2ecf20Sopenharmony_ci ctx->clk_data.clk_num = nr_clks; 3858c2ecf20Sopenharmony_ci ctx->cru_node = np; 3868c2ecf20Sopenharmony_ci spin_lock_init(&ctx->lock); 3878c2ecf20Sopenharmony_ci 3888c2ecf20Sopenharmony_ci ctx->grf = syscon_regmap_lookup_by_phandle(ctx->cru_node, 3898c2ecf20Sopenharmony_ci "rockchip,grf"); 3908c2ecf20Sopenharmony_ci 3918c2ecf20Sopenharmony_ci return ctx; 3928c2ecf20Sopenharmony_ci 3938c2ecf20Sopenharmony_cierr_free: 3948c2ecf20Sopenharmony_ci kfree(ctx); 3958c2ecf20Sopenharmony_ci return ERR_PTR(-ENOMEM); 3968c2ecf20Sopenharmony_ci} 3978c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(rockchip_clk_init); 3988c2ecf20Sopenharmony_ci 3998c2ecf20Sopenharmony_civoid rockchip_clk_of_add_provider(struct device_node *np, 4008c2ecf20Sopenharmony_ci struct rockchip_clk_provider *ctx) 4018c2ecf20Sopenharmony_ci{ 4028c2ecf20Sopenharmony_ci if (of_clk_add_provider(np, of_clk_src_onecell_get, 4038c2ecf20Sopenharmony_ci &ctx->clk_data)) 4048c2ecf20Sopenharmony_ci pr_err("%s: could not register clk provider\n", __func__); 4058c2ecf20Sopenharmony_ci} 4068c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(rockchip_clk_of_add_provider); 4078c2ecf20Sopenharmony_ci 4088c2ecf20Sopenharmony_civoid rockchip_clk_add_lookup(struct rockchip_clk_provider *ctx, 4098c2ecf20Sopenharmony_ci struct clk *clk, unsigned int id) 4108c2ecf20Sopenharmony_ci{ 4118c2ecf20Sopenharmony_ci if (ctx->clk_data.clks && id) 4128c2ecf20Sopenharmony_ci ctx->clk_data.clks[id] = clk; 4138c2ecf20Sopenharmony_ci} 4148c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(rockchip_clk_add_lookup); 4158c2ecf20Sopenharmony_ci 4168c2ecf20Sopenharmony_civoid rockchip_clk_register_plls(struct rockchip_clk_provider *ctx, 4178c2ecf20Sopenharmony_ci struct rockchip_pll_clock *list, 4188c2ecf20Sopenharmony_ci unsigned int nr_pll, int grf_lock_offset) 4198c2ecf20Sopenharmony_ci{ 4208c2ecf20Sopenharmony_ci struct clk *clk; 4218c2ecf20Sopenharmony_ci int idx; 4228c2ecf20Sopenharmony_ci 4238c2ecf20Sopenharmony_ci for (idx = 0; idx < nr_pll; idx++, list++) { 4248c2ecf20Sopenharmony_ci clk = rockchip_clk_register_pll(ctx, list->type, list->name, 4258c2ecf20Sopenharmony_ci list->parent_names, list->num_parents, 4268c2ecf20Sopenharmony_ci list->con_offset, grf_lock_offset, 4278c2ecf20Sopenharmony_ci list->lock_shift, list->mode_offset, 4288c2ecf20Sopenharmony_ci list->mode_shift, list->rate_table, 4298c2ecf20Sopenharmony_ci list->flags, list->pll_flags); 4308c2ecf20Sopenharmony_ci if (IS_ERR(clk)) { 4318c2ecf20Sopenharmony_ci pr_err("%s: failed to register clock %s\n", __func__, 4328c2ecf20Sopenharmony_ci list->name); 4338c2ecf20Sopenharmony_ci continue; 4348c2ecf20Sopenharmony_ci } 4358c2ecf20Sopenharmony_ci 4368c2ecf20Sopenharmony_ci rockchip_clk_add_lookup(ctx, clk, list->id); 4378c2ecf20Sopenharmony_ci } 4388c2ecf20Sopenharmony_ci} 4398c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(rockchip_clk_register_plls); 4408c2ecf20Sopenharmony_ci 4418c2ecf20Sopenharmony_civoid rockchip_clk_register_branches(struct rockchip_clk_provider *ctx, 4428c2ecf20Sopenharmony_ci struct rockchip_clk_branch *list, 4438c2ecf20Sopenharmony_ci unsigned int nr_clk) 4448c2ecf20Sopenharmony_ci{ 4458c2ecf20Sopenharmony_ci struct clk *clk = NULL; 4468c2ecf20Sopenharmony_ci unsigned int idx; 4478c2ecf20Sopenharmony_ci unsigned long flags; 4488c2ecf20Sopenharmony_ci 4498c2ecf20Sopenharmony_ci for (idx = 0; idx < nr_clk; idx++, list++) { 4508c2ecf20Sopenharmony_ci flags = list->flags; 4518c2ecf20Sopenharmony_ci 4528c2ecf20Sopenharmony_ci /* catch simple muxes */ 4538c2ecf20Sopenharmony_ci switch (list->branch_type) { 4548c2ecf20Sopenharmony_ci case branch_mux: 4558c2ecf20Sopenharmony_ci clk = clk_register_mux(NULL, list->name, 4568c2ecf20Sopenharmony_ci list->parent_names, list->num_parents, 4578c2ecf20Sopenharmony_ci flags, ctx->reg_base + list->muxdiv_offset, 4588c2ecf20Sopenharmony_ci list->mux_shift, list->mux_width, 4598c2ecf20Sopenharmony_ci list->mux_flags, &ctx->lock); 4608c2ecf20Sopenharmony_ci break; 4618c2ecf20Sopenharmony_ci case branch_muxgrf: 4628c2ecf20Sopenharmony_ci clk = rockchip_clk_register_muxgrf(list->name, 4638c2ecf20Sopenharmony_ci list->parent_names, list->num_parents, 4648c2ecf20Sopenharmony_ci flags, ctx->grf, list->muxdiv_offset, 4658c2ecf20Sopenharmony_ci list->mux_shift, list->mux_width, 4668c2ecf20Sopenharmony_ci list->mux_flags); 4678c2ecf20Sopenharmony_ci break; 4688c2ecf20Sopenharmony_ci case branch_divider: 4698c2ecf20Sopenharmony_ci if (list->div_table) 4708c2ecf20Sopenharmony_ci clk = clk_register_divider_table(NULL, 4718c2ecf20Sopenharmony_ci list->name, list->parent_names[0], 4728c2ecf20Sopenharmony_ci flags, 4738c2ecf20Sopenharmony_ci ctx->reg_base + list->muxdiv_offset, 4748c2ecf20Sopenharmony_ci list->div_shift, list->div_width, 4758c2ecf20Sopenharmony_ci list->div_flags, list->div_table, 4768c2ecf20Sopenharmony_ci &ctx->lock); 4778c2ecf20Sopenharmony_ci else 4788c2ecf20Sopenharmony_ci clk = clk_register_divider(NULL, list->name, 4798c2ecf20Sopenharmony_ci list->parent_names[0], flags, 4808c2ecf20Sopenharmony_ci ctx->reg_base + list->muxdiv_offset, 4818c2ecf20Sopenharmony_ci list->div_shift, list->div_width, 4828c2ecf20Sopenharmony_ci list->div_flags, &ctx->lock); 4838c2ecf20Sopenharmony_ci break; 4848c2ecf20Sopenharmony_ci case branch_fraction_divider: 4858c2ecf20Sopenharmony_ci clk = rockchip_clk_register_frac_branch(ctx, list->name, 4868c2ecf20Sopenharmony_ci list->parent_names, list->num_parents, 4878c2ecf20Sopenharmony_ci ctx->reg_base, list->muxdiv_offset, 4888c2ecf20Sopenharmony_ci list->div_flags, 4898c2ecf20Sopenharmony_ci list->gate_offset, list->gate_shift, 4908c2ecf20Sopenharmony_ci list->gate_flags, flags, list->child, 4918c2ecf20Sopenharmony_ci &ctx->lock); 4928c2ecf20Sopenharmony_ci break; 4938c2ecf20Sopenharmony_ci case branch_half_divider: 4948c2ecf20Sopenharmony_ci clk = rockchip_clk_register_halfdiv(list->name, 4958c2ecf20Sopenharmony_ci list->parent_names, list->num_parents, 4968c2ecf20Sopenharmony_ci ctx->reg_base, list->muxdiv_offset, 4978c2ecf20Sopenharmony_ci list->mux_shift, list->mux_width, 4988c2ecf20Sopenharmony_ci list->mux_flags, list->div_shift, 4998c2ecf20Sopenharmony_ci list->div_width, list->div_flags, 5008c2ecf20Sopenharmony_ci list->gate_offset, list->gate_shift, 5018c2ecf20Sopenharmony_ci list->gate_flags, flags, &ctx->lock); 5028c2ecf20Sopenharmony_ci break; 5038c2ecf20Sopenharmony_ci case branch_gate: 5048c2ecf20Sopenharmony_ci flags |= CLK_SET_RATE_PARENT; 5058c2ecf20Sopenharmony_ci 5068c2ecf20Sopenharmony_ci clk = clk_register_gate(NULL, list->name, 5078c2ecf20Sopenharmony_ci list->parent_names[0], flags, 5088c2ecf20Sopenharmony_ci ctx->reg_base + list->gate_offset, 5098c2ecf20Sopenharmony_ci list->gate_shift, list->gate_flags, &ctx->lock); 5108c2ecf20Sopenharmony_ci break; 5118c2ecf20Sopenharmony_ci case branch_composite: 5128c2ecf20Sopenharmony_ci clk = rockchip_clk_register_branch(list->name, 5138c2ecf20Sopenharmony_ci list->parent_names, list->num_parents, 5148c2ecf20Sopenharmony_ci ctx->reg_base, list->muxdiv_offset, 5158c2ecf20Sopenharmony_ci list->mux_shift, 5168c2ecf20Sopenharmony_ci list->mux_width, list->mux_flags, 5178c2ecf20Sopenharmony_ci list->div_offset, list->div_shift, list->div_width, 5188c2ecf20Sopenharmony_ci list->div_flags, list->div_table, 5198c2ecf20Sopenharmony_ci list->gate_offset, list->gate_shift, 5208c2ecf20Sopenharmony_ci list->gate_flags, flags, &ctx->lock); 5218c2ecf20Sopenharmony_ci break; 5228c2ecf20Sopenharmony_ci case branch_mmc: 5238c2ecf20Sopenharmony_ci clk = rockchip_clk_register_mmc( 5248c2ecf20Sopenharmony_ci list->name, 5258c2ecf20Sopenharmony_ci list->parent_names, list->num_parents, 5268c2ecf20Sopenharmony_ci ctx->reg_base + list->muxdiv_offset, 5278c2ecf20Sopenharmony_ci list->div_shift 5288c2ecf20Sopenharmony_ci ); 5298c2ecf20Sopenharmony_ci break; 5308c2ecf20Sopenharmony_ci case branch_inverter: 5318c2ecf20Sopenharmony_ci clk = rockchip_clk_register_inverter( 5328c2ecf20Sopenharmony_ci list->name, list->parent_names, 5338c2ecf20Sopenharmony_ci list->num_parents, 5348c2ecf20Sopenharmony_ci ctx->reg_base + list->muxdiv_offset, 5358c2ecf20Sopenharmony_ci list->div_shift, list->div_flags, &ctx->lock); 5368c2ecf20Sopenharmony_ci break; 5378c2ecf20Sopenharmony_ci case branch_factor: 5388c2ecf20Sopenharmony_ci clk = rockchip_clk_register_factor_branch( 5398c2ecf20Sopenharmony_ci list->name, list->parent_names, 5408c2ecf20Sopenharmony_ci list->num_parents, ctx->reg_base, 5418c2ecf20Sopenharmony_ci list->div_shift, list->div_width, 5428c2ecf20Sopenharmony_ci list->gate_offset, list->gate_shift, 5438c2ecf20Sopenharmony_ci list->gate_flags, flags, &ctx->lock); 5448c2ecf20Sopenharmony_ci break; 5458c2ecf20Sopenharmony_ci case branch_ddrclk: 5468c2ecf20Sopenharmony_ci clk = rockchip_clk_register_ddrclk( 5478c2ecf20Sopenharmony_ci list->name, list->flags, 5488c2ecf20Sopenharmony_ci list->parent_names, list->num_parents, 5498c2ecf20Sopenharmony_ci list->muxdiv_offset, list->mux_shift, 5508c2ecf20Sopenharmony_ci list->mux_width, list->div_shift, 5518c2ecf20Sopenharmony_ci list->div_width, list->div_flags, 5528c2ecf20Sopenharmony_ci ctx->reg_base, &ctx->lock); 5538c2ecf20Sopenharmony_ci break; 5548c2ecf20Sopenharmony_ci } 5558c2ecf20Sopenharmony_ci 5568c2ecf20Sopenharmony_ci /* none of the cases above matched */ 5578c2ecf20Sopenharmony_ci if (!clk) { 5588c2ecf20Sopenharmony_ci pr_err("%s: unknown clock type %d\n", 5598c2ecf20Sopenharmony_ci __func__, list->branch_type); 5608c2ecf20Sopenharmony_ci continue; 5618c2ecf20Sopenharmony_ci } 5628c2ecf20Sopenharmony_ci 5638c2ecf20Sopenharmony_ci if (IS_ERR(clk)) { 5648c2ecf20Sopenharmony_ci pr_err("%s: failed to register clock %s: %ld\n", 5658c2ecf20Sopenharmony_ci __func__, list->name, PTR_ERR(clk)); 5668c2ecf20Sopenharmony_ci continue; 5678c2ecf20Sopenharmony_ci } 5688c2ecf20Sopenharmony_ci 5698c2ecf20Sopenharmony_ci rockchip_clk_add_lookup(ctx, clk, list->id); 5708c2ecf20Sopenharmony_ci } 5718c2ecf20Sopenharmony_ci} 5728c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(rockchip_clk_register_branches); 5738c2ecf20Sopenharmony_ci 5748c2ecf20Sopenharmony_civoid rockchip_clk_register_armclk(struct rockchip_clk_provider *ctx, 5758c2ecf20Sopenharmony_ci unsigned int lookup_id, 5768c2ecf20Sopenharmony_ci const char *name, const char *const *parent_names, 5778c2ecf20Sopenharmony_ci u8 num_parents, 5788c2ecf20Sopenharmony_ci const struct rockchip_cpuclk_reg_data *reg_data, 5798c2ecf20Sopenharmony_ci const struct rockchip_cpuclk_rate_table *rates, 5808c2ecf20Sopenharmony_ci int nrates) 5818c2ecf20Sopenharmony_ci{ 5828c2ecf20Sopenharmony_ci struct clk *clk; 5838c2ecf20Sopenharmony_ci 5848c2ecf20Sopenharmony_ci clk = rockchip_clk_register_cpuclk(name, parent_names, num_parents, 5858c2ecf20Sopenharmony_ci reg_data, rates, nrates, 5868c2ecf20Sopenharmony_ci ctx->reg_base, &ctx->lock); 5878c2ecf20Sopenharmony_ci if (IS_ERR(clk)) { 5888c2ecf20Sopenharmony_ci pr_err("%s: failed to register clock %s: %ld\n", 5898c2ecf20Sopenharmony_ci __func__, name, PTR_ERR(clk)); 5908c2ecf20Sopenharmony_ci return; 5918c2ecf20Sopenharmony_ci } 5928c2ecf20Sopenharmony_ci 5938c2ecf20Sopenharmony_ci rockchip_clk_add_lookup(ctx, clk, lookup_id); 5948c2ecf20Sopenharmony_ci} 5958c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(rockchip_clk_register_armclk); 5968c2ecf20Sopenharmony_ci 5978c2ecf20Sopenharmony_civoid rockchip_clk_protect_critical(const char *const clocks[], 5988c2ecf20Sopenharmony_ci int nclocks) 5998c2ecf20Sopenharmony_ci{ 6008c2ecf20Sopenharmony_ci int i; 6018c2ecf20Sopenharmony_ci 6028c2ecf20Sopenharmony_ci /* Protect the clocks that needs to stay on */ 6038c2ecf20Sopenharmony_ci for (i = 0; i < nclocks; i++) { 6048c2ecf20Sopenharmony_ci struct clk *clk = __clk_lookup(clocks[i]); 6058c2ecf20Sopenharmony_ci 6068c2ecf20Sopenharmony_ci if (clk) 6078c2ecf20Sopenharmony_ci clk_prepare_enable(clk); 6088c2ecf20Sopenharmony_ci } 6098c2ecf20Sopenharmony_ci} 6108c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(rockchip_clk_protect_critical); 6118c2ecf20Sopenharmony_ci 6128c2ecf20Sopenharmony_cistatic void __iomem *rst_base; 6138c2ecf20Sopenharmony_cistatic unsigned int reg_restart; 6148c2ecf20Sopenharmony_cistatic void (*cb_restart)(void); 6158c2ecf20Sopenharmony_cistatic int rockchip_restart_notify(struct notifier_block *this, 6168c2ecf20Sopenharmony_ci unsigned long mode, void *cmd) 6178c2ecf20Sopenharmony_ci{ 6188c2ecf20Sopenharmony_ci if (cb_restart) 6198c2ecf20Sopenharmony_ci cb_restart(); 6208c2ecf20Sopenharmony_ci 6218c2ecf20Sopenharmony_ci writel(0xfdb9, rst_base + reg_restart); 6228c2ecf20Sopenharmony_ci return NOTIFY_DONE; 6238c2ecf20Sopenharmony_ci} 6248c2ecf20Sopenharmony_ci 6258c2ecf20Sopenharmony_cistatic struct notifier_block rockchip_restart_handler = { 6268c2ecf20Sopenharmony_ci .notifier_call = rockchip_restart_notify, 6278c2ecf20Sopenharmony_ci .priority = 128, 6288c2ecf20Sopenharmony_ci}; 6298c2ecf20Sopenharmony_ci 6308c2ecf20Sopenharmony_civoid 6318c2ecf20Sopenharmony_cirockchip_register_restart_notifier(struct rockchip_clk_provider *ctx, 6328c2ecf20Sopenharmony_ci unsigned int reg, 6338c2ecf20Sopenharmony_ci void (*cb)(void)) 6348c2ecf20Sopenharmony_ci{ 6358c2ecf20Sopenharmony_ci int ret; 6368c2ecf20Sopenharmony_ci 6378c2ecf20Sopenharmony_ci rst_base = ctx->reg_base; 6388c2ecf20Sopenharmony_ci reg_restart = reg; 6398c2ecf20Sopenharmony_ci cb_restart = cb; 6408c2ecf20Sopenharmony_ci ret = register_restart_handler(&rockchip_restart_handler); 6418c2ecf20Sopenharmony_ci if (ret) 6428c2ecf20Sopenharmony_ci pr_err("%s: cannot register restart handler, %d\n", 6438c2ecf20Sopenharmony_ci __func__, ret); 6448c2ecf20Sopenharmony_ci} 6458c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(rockchip_register_restart_notifier); 646