162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (c) 2014 MundoReader S.L. 462306a36Sopenharmony_ci * Author: Heiko Stuebner <heiko@sntech.de> 562306a36Sopenharmony_ci * 662306a36Sopenharmony_ci * Copyright (c) 2016 Rockchip Electronics Co. Ltd. 762306a36Sopenharmony_ci * Author: Xing Zheng <zhengxing@rock-chips.com> 862306a36Sopenharmony_ci * 962306a36Sopenharmony_ci * based on 1062306a36Sopenharmony_ci * 1162306a36Sopenharmony_ci * samsung/clk.c 1262306a36Sopenharmony_ci * Copyright (c) 2013 Samsung Electronics Co., Ltd. 1362306a36Sopenharmony_ci * Copyright (c) 2013 Linaro Ltd. 1462306a36Sopenharmony_ci * Author: Thomas Abraham <thomas.ab@samsung.com> 1562306a36Sopenharmony_ci */ 1662306a36Sopenharmony_ci 1762306a36Sopenharmony_ci#include <linux/slab.h> 1862306a36Sopenharmony_ci#include <linux/clk.h> 1962306a36Sopenharmony_ci#include <linux/clk-provider.h> 2062306a36Sopenharmony_ci#include <linux/io.h> 2162306a36Sopenharmony_ci#include <linux/mfd/syscon.h> 2262306a36Sopenharmony_ci#include <linux/regmap.h> 2362306a36Sopenharmony_ci#include <linux/reboot.h> 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_ci#include "../clk-fractional-divider.h" 2662306a36Sopenharmony_ci#include "clk.h" 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_ci/* 2962306a36Sopenharmony_ci * Register a clock branch. 3062306a36Sopenharmony_ci * Most clock branches have a form like 3162306a36Sopenharmony_ci * 3262306a36Sopenharmony_ci * src1 --|--\ 3362306a36Sopenharmony_ci * |M |--[GATE]-[DIV]- 3462306a36Sopenharmony_ci * src2 --|--/ 3562306a36Sopenharmony_ci * 3662306a36Sopenharmony_ci * sometimes without one of those components. 3762306a36Sopenharmony_ci */ 3862306a36Sopenharmony_cistatic struct clk *rockchip_clk_register_branch(const char *name, 3962306a36Sopenharmony_ci const char *const *parent_names, u8 num_parents, 4062306a36Sopenharmony_ci void __iomem *base, 4162306a36Sopenharmony_ci int muxdiv_offset, u8 mux_shift, u8 mux_width, u8 mux_flags, 4262306a36Sopenharmony_ci u32 *mux_table, 4362306a36Sopenharmony_ci int div_offset, u8 div_shift, u8 div_width, u8 div_flags, 4462306a36Sopenharmony_ci struct clk_div_table *div_table, int gate_offset, 4562306a36Sopenharmony_ci u8 gate_shift, u8 gate_flags, unsigned long flags, 4662306a36Sopenharmony_ci spinlock_t *lock) 4762306a36Sopenharmony_ci{ 4862306a36Sopenharmony_ci struct clk_hw *hw; 4962306a36Sopenharmony_ci struct clk_mux *mux = NULL; 5062306a36Sopenharmony_ci struct clk_gate *gate = NULL; 5162306a36Sopenharmony_ci struct clk_divider *div = NULL; 5262306a36Sopenharmony_ci const struct clk_ops *mux_ops = NULL, *div_ops = NULL, 5362306a36Sopenharmony_ci *gate_ops = NULL; 5462306a36Sopenharmony_ci int ret; 5562306a36Sopenharmony_ci 5662306a36Sopenharmony_ci if (num_parents > 1) { 5762306a36Sopenharmony_ci mux = kzalloc(sizeof(*mux), GFP_KERNEL); 5862306a36Sopenharmony_ci if (!mux) 5962306a36Sopenharmony_ci return ERR_PTR(-ENOMEM); 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_ci mux->reg = base + muxdiv_offset; 6262306a36Sopenharmony_ci mux->shift = mux_shift; 6362306a36Sopenharmony_ci mux->mask = BIT(mux_width) - 1; 6462306a36Sopenharmony_ci mux->flags = mux_flags; 6562306a36Sopenharmony_ci mux->table = mux_table; 6662306a36Sopenharmony_ci mux->lock = lock; 6762306a36Sopenharmony_ci mux_ops = (mux_flags & CLK_MUX_READ_ONLY) ? &clk_mux_ro_ops 6862306a36Sopenharmony_ci : &clk_mux_ops; 6962306a36Sopenharmony_ci } 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_ci if (gate_offset >= 0) { 7262306a36Sopenharmony_ci gate = kzalloc(sizeof(*gate), GFP_KERNEL); 7362306a36Sopenharmony_ci if (!gate) { 7462306a36Sopenharmony_ci ret = -ENOMEM; 7562306a36Sopenharmony_ci goto err_gate; 7662306a36Sopenharmony_ci } 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_ci gate->flags = gate_flags; 7962306a36Sopenharmony_ci gate->reg = base + gate_offset; 8062306a36Sopenharmony_ci gate->bit_idx = gate_shift; 8162306a36Sopenharmony_ci gate->lock = lock; 8262306a36Sopenharmony_ci gate_ops = &clk_gate_ops; 8362306a36Sopenharmony_ci } 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_ci if (div_width > 0) { 8662306a36Sopenharmony_ci div = kzalloc(sizeof(*div), GFP_KERNEL); 8762306a36Sopenharmony_ci if (!div) { 8862306a36Sopenharmony_ci ret = -ENOMEM; 8962306a36Sopenharmony_ci goto err_div; 9062306a36Sopenharmony_ci } 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_ci div->flags = div_flags; 9362306a36Sopenharmony_ci if (div_offset) 9462306a36Sopenharmony_ci div->reg = base + div_offset; 9562306a36Sopenharmony_ci else 9662306a36Sopenharmony_ci div->reg = base + muxdiv_offset; 9762306a36Sopenharmony_ci div->shift = div_shift; 9862306a36Sopenharmony_ci div->width = div_width; 9962306a36Sopenharmony_ci div->lock = lock; 10062306a36Sopenharmony_ci div->table = div_table; 10162306a36Sopenharmony_ci div_ops = (div_flags & CLK_DIVIDER_READ_ONLY) 10262306a36Sopenharmony_ci ? &clk_divider_ro_ops 10362306a36Sopenharmony_ci : &clk_divider_ops; 10462306a36Sopenharmony_ci } 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_ci hw = clk_hw_register_composite(NULL, name, parent_names, num_parents, 10762306a36Sopenharmony_ci mux ? &mux->hw : NULL, mux_ops, 10862306a36Sopenharmony_ci div ? &div->hw : NULL, div_ops, 10962306a36Sopenharmony_ci gate ? &gate->hw : NULL, gate_ops, 11062306a36Sopenharmony_ci flags); 11162306a36Sopenharmony_ci if (IS_ERR(hw)) { 11262306a36Sopenharmony_ci kfree(div); 11362306a36Sopenharmony_ci kfree(gate); 11462306a36Sopenharmony_ci return ERR_CAST(hw); 11562306a36Sopenharmony_ci } 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_ci return hw->clk; 11862306a36Sopenharmony_cierr_div: 11962306a36Sopenharmony_ci kfree(gate); 12062306a36Sopenharmony_cierr_gate: 12162306a36Sopenharmony_ci kfree(mux); 12262306a36Sopenharmony_ci return ERR_PTR(ret); 12362306a36Sopenharmony_ci} 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_cistruct rockchip_clk_frac { 12662306a36Sopenharmony_ci struct notifier_block clk_nb; 12762306a36Sopenharmony_ci struct clk_fractional_divider div; 12862306a36Sopenharmony_ci struct clk_gate gate; 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_ci struct clk_mux mux; 13162306a36Sopenharmony_ci const struct clk_ops *mux_ops; 13262306a36Sopenharmony_ci int mux_frac_idx; 13362306a36Sopenharmony_ci 13462306a36Sopenharmony_ci bool rate_change_remuxed; 13562306a36Sopenharmony_ci int rate_change_idx; 13662306a36Sopenharmony_ci}; 13762306a36Sopenharmony_ci 13862306a36Sopenharmony_ci#define to_rockchip_clk_frac_nb(nb) \ 13962306a36Sopenharmony_ci container_of(nb, struct rockchip_clk_frac, clk_nb) 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_cistatic int rockchip_clk_frac_notifier_cb(struct notifier_block *nb, 14262306a36Sopenharmony_ci unsigned long event, void *data) 14362306a36Sopenharmony_ci{ 14462306a36Sopenharmony_ci struct clk_notifier_data *ndata = data; 14562306a36Sopenharmony_ci struct rockchip_clk_frac *frac = to_rockchip_clk_frac_nb(nb); 14662306a36Sopenharmony_ci struct clk_mux *frac_mux = &frac->mux; 14762306a36Sopenharmony_ci int ret = 0; 14862306a36Sopenharmony_ci 14962306a36Sopenharmony_ci pr_debug("%s: event %lu, old_rate %lu, new_rate: %lu\n", 15062306a36Sopenharmony_ci __func__, event, ndata->old_rate, ndata->new_rate); 15162306a36Sopenharmony_ci if (event == PRE_RATE_CHANGE) { 15262306a36Sopenharmony_ci frac->rate_change_idx = 15362306a36Sopenharmony_ci frac->mux_ops->get_parent(&frac_mux->hw); 15462306a36Sopenharmony_ci if (frac->rate_change_idx != frac->mux_frac_idx) { 15562306a36Sopenharmony_ci frac->mux_ops->set_parent(&frac_mux->hw, 15662306a36Sopenharmony_ci frac->mux_frac_idx); 15762306a36Sopenharmony_ci frac->rate_change_remuxed = 1; 15862306a36Sopenharmony_ci } 15962306a36Sopenharmony_ci } else if (event == POST_RATE_CHANGE) { 16062306a36Sopenharmony_ci /* 16162306a36Sopenharmony_ci * The POST_RATE_CHANGE notifier runs directly after the 16262306a36Sopenharmony_ci * divider clock is set in clk_change_rate, so we'll have 16362306a36Sopenharmony_ci * remuxed back to the original parent before clk_change_rate 16462306a36Sopenharmony_ci * reaches the mux itself. 16562306a36Sopenharmony_ci */ 16662306a36Sopenharmony_ci if (frac->rate_change_remuxed) { 16762306a36Sopenharmony_ci frac->mux_ops->set_parent(&frac_mux->hw, 16862306a36Sopenharmony_ci frac->rate_change_idx); 16962306a36Sopenharmony_ci frac->rate_change_remuxed = 0; 17062306a36Sopenharmony_ci } 17162306a36Sopenharmony_ci } 17262306a36Sopenharmony_ci 17362306a36Sopenharmony_ci return notifier_from_errno(ret); 17462306a36Sopenharmony_ci} 17562306a36Sopenharmony_ci 17662306a36Sopenharmony_ci/* 17762306a36Sopenharmony_ci * fractional divider must set that denominator is 20 times larger than 17862306a36Sopenharmony_ci * numerator to generate precise clock frequency. 17962306a36Sopenharmony_ci */ 18062306a36Sopenharmony_cistatic void rockchip_fractional_approximation(struct clk_hw *hw, 18162306a36Sopenharmony_ci unsigned long rate, unsigned long *parent_rate, 18262306a36Sopenharmony_ci unsigned long *m, unsigned long *n) 18362306a36Sopenharmony_ci{ 18462306a36Sopenharmony_ci struct clk_fractional_divider *fd = to_clk_fd(hw); 18562306a36Sopenharmony_ci unsigned long p_rate, p_parent_rate; 18662306a36Sopenharmony_ci struct clk_hw *p_parent; 18762306a36Sopenharmony_ci 18862306a36Sopenharmony_ci p_rate = clk_hw_get_rate(clk_hw_get_parent(hw)); 18962306a36Sopenharmony_ci if ((rate * 20 > p_rate) && (p_rate % rate != 0)) { 19062306a36Sopenharmony_ci p_parent = clk_hw_get_parent(clk_hw_get_parent(hw)); 19162306a36Sopenharmony_ci p_parent_rate = clk_hw_get_rate(p_parent); 19262306a36Sopenharmony_ci *parent_rate = p_parent_rate; 19362306a36Sopenharmony_ci } 19462306a36Sopenharmony_ci 19562306a36Sopenharmony_ci fd->flags |= CLK_FRAC_DIVIDER_POWER_OF_TWO_PS; 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_ci clk_fractional_divider_general_approximation(hw, rate, parent_rate, m, n); 19862306a36Sopenharmony_ci} 19962306a36Sopenharmony_ci 20062306a36Sopenharmony_cistatic void rockchip_clk_add_lookup(struct rockchip_clk_provider *ctx, 20162306a36Sopenharmony_ci struct clk *clk, unsigned int id) 20262306a36Sopenharmony_ci{ 20362306a36Sopenharmony_ci ctx->clk_data.clks[id] = clk; 20462306a36Sopenharmony_ci} 20562306a36Sopenharmony_ci 20662306a36Sopenharmony_cistatic struct clk *rockchip_clk_register_frac_branch( 20762306a36Sopenharmony_ci struct rockchip_clk_provider *ctx, const char *name, 20862306a36Sopenharmony_ci const char *const *parent_names, u8 num_parents, 20962306a36Sopenharmony_ci void __iomem *base, int muxdiv_offset, u8 div_flags, 21062306a36Sopenharmony_ci int gate_offset, u8 gate_shift, u8 gate_flags, 21162306a36Sopenharmony_ci unsigned long flags, struct rockchip_clk_branch *child, 21262306a36Sopenharmony_ci spinlock_t *lock) 21362306a36Sopenharmony_ci{ 21462306a36Sopenharmony_ci struct clk_hw *hw; 21562306a36Sopenharmony_ci struct rockchip_clk_frac *frac; 21662306a36Sopenharmony_ci struct clk_gate *gate = NULL; 21762306a36Sopenharmony_ci struct clk_fractional_divider *div = NULL; 21862306a36Sopenharmony_ci const struct clk_ops *div_ops = NULL, *gate_ops = NULL; 21962306a36Sopenharmony_ci 22062306a36Sopenharmony_ci if (muxdiv_offset < 0) 22162306a36Sopenharmony_ci return ERR_PTR(-EINVAL); 22262306a36Sopenharmony_ci 22362306a36Sopenharmony_ci if (child && child->branch_type != branch_mux) { 22462306a36Sopenharmony_ci pr_err("%s: fractional child clock for %s can only be a mux\n", 22562306a36Sopenharmony_ci __func__, name); 22662306a36Sopenharmony_ci return ERR_PTR(-EINVAL); 22762306a36Sopenharmony_ci } 22862306a36Sopenharmony_ci 22962306a36Sopenharmony_ci frac = kzalloc(sizeof(*frac), GFP_KERNEL); 23062306a36Sopenharmony_ci if (!frac) 23162306a36Sopenharmony_ci return ERR_PTR(-ENOMEM); 23262306a36Sopenharmony_ci 23362306a36Sopenharmony_ci if (gate_offset >= 0) { 23462306a36Sopenharmony_ci gate = &frac->gate; 23562306a36Sopenharmony_ci gate->flags = gate_flags; 23662306a36Sopenharmony_ci gate->reg = base + gate_offset; 23762306a36Sopenharmony_ci gate->bit_idx = gate_shift; 23862306a36Sopenharmony_ci gate->lock = lock; 23962306a36Sopenharmony_ci gate_ops = &clk_gate_ops; 24062306a36Sopenharmony_ci } 24162306a36Sopenharmony_ci 24262306a36Sopenharmony_ci div = &frac->div; 24362306a36Sopenharmony_ci div->flags = div_flags; 24462306a36Sopenharmony_ci div->reg = base + muxdiv_offset; 24562306a36Sopenharmony_ci div->mshift = 16; 24662306a36Sopenharmony_ci div->mwidth = 16; 24762306a36Sopenharmony_ci div->nshift = 0; 24862306a36Sopenharmony_ci div->nwidth = 16; 24962306a36Sopenharmony_ci div->lock = lock; 25062306a36Sopenharmony_ci div->approximation = rockchip_fractional_approximation; 25162306a36Sopenharmony_ci div_ops = &clk_fractional_divider_ops; 25262306a36Sopenharmony_ci 25362306a36Sopenharmony_ci hw = clk_hw_register_composite(NULL, name, parent_names, num_parents, 25462306a36Sopenharmony_ci NULL, NULL, 25562306a36Sopenharmony_ci &div->hw, div_ops, 25662306a36Sopenharmony_ci gate ? &gate->hw : NULL, gate_ops, 25762306a36Sopenharmony_ci flags | CLK_SET_RATE_UNGATE); 25862306a36Sopenharmony_ci if (IS_ERR(hw)) { 25962306a36Sopenharmony_ci kfree(frac); 26062306a36Sopenharmony_ci return ERR_CAST(hw); 26162306a36Sopenharmony_ci } 26262306a36Sopenharmony_ci 26362306a36Sopenharmony_ci if (child) { 26462306a36Sopenharmony_ci struct clk_mux *frac_mux = &frac->mux; 26562306a36Sopenharmony_ci struct clk_init_data init; 26662306a36Sopenharmony_ci struct clk *mux_clk; 26762306a36Sopenharmony_ci int ret; 26862306a36Sopenharmony_ci 26962306a36Sopenharmony_ci frac->mux_frac_idx = match_string(child->parent_names, 27062306a36Sopenharmony_ci child->num_parents, name); 27162306a36Sopenharmony_ci frac->mux_ops = &clk_mux_ops; 27262306a36Sopenharmony_ci frac->clk_nb.notifier_call = rockchip_clk_frac_notifier_cb; 27362306a36Sopenharmony_ci 27462306a36Sopenharmony_ci frac_mux->reg = base + child->muxdiv_offset; 27562306a36Sopenharmony_ci frac_mux->shift = child->mux_shift; 27662306a36Sopenharmony_ci frac_mux->mask = BIT(child->mux_width) - 1; 27762306a36Sopenharmony_ci frac_mux->flags = child->mux_flags; 27862306a36Sopenharmony_ci if (child->mux_table) 27962306a36Sopenharmony_ci frac_mux->table = child->mux_table; 28062306a36Sopenharmony_ci frac_mux->lock = lock; 28162306a36Sopenharmony_ci frac_mux->hw.init = &init; 28262306a36Sopenharmony_ci 28362306a36Sopenharmony_ci init.name = child->name; 28462306a36Sopenharmony_ci init.flags = child->flags | CLK_SET_RATE_PARENT; 28562306a36Sopenharmony_ci init.ops = frac->mux_ops; 28662306a36Sopenharmony_ci init.parent_names = child->parent_names; 28762306a36Sopenharmony_ci init.num_parents = child->num_parents; 28862306a36Sopenharmony_ci 28962306a36Sopenharmony_ci mux_clk = clk_register(NULL, &frac_mux->hw); 29062306a36Sopenharmony_ci if (IS_ERR(mux_clk)) { 29162306a36Sopenharmony_ci kfree(frac); 29262306a36Sopenharmony_ci return mux_clk; 29362306a36Sopenharmony_ci } 29462306a36Sopenharmony_ci 29562306a36Sopenharmony_ci rockchip_clk_add_lookup(ctx, mux_clk, child->id); 29662306a36Sopenharmony_ci 29762306a36Sopenharmony_ci /* notifier on the fraction divider to catch rate changes */ 29862306a36Sopenharmony_ci if (frac->mux_frac_idx >= 0) { 29962306a36Sopenharmony_ci pr_debug("%s: found fractional parent in mux at pos %d\n", 30062306a36Sopenharmony_ci __func__, frac->mux_frac_idx); 30162306a36Sopenharmony_ci ret = clk_notifier_register(hw->clk, &frac->clk_nb); 30262306a36Sopenharmony_ci if (ret) 30362306a36Sopenharmony_ci pr_err("%s: failed to register clock notifier for %s\n", 30462306a36Sopenharmony_ci __func__, name); 30562306a36Sopenharmony_ci } else { 30662306a36Sopenharmony_ci pr_warn("%s: could not find %s as parent of %s, rate changes may not work\n", 30762306a36Sopenharmony_ci __func__, name, child->name); 30862306a36Sopenharmony_ci } 30962306a36Sopenharmony_ci } 31062306a36Sopenharmony_ci 31162306a36Sopenharmony_ci return hw->clk; 31262306a36Sopenharmony_ci} 31362306a36Sopenharmony_ci 31462306a36Sopenharmony_cistatic struct clk *rockchip_clk_register_factor_branch(const char *name, 31562306a36Sopenharmony_ci const char *const *parent_names, u8 num_parents, 31662306a36Sopenharmony_ci void __iomem *base, unsigned int mult, unsigned int div, 31762306a36Sopenharmony_ci int gate_offset, u8 gate_shift, u8 gate_flags, 31862306a36Sopenharmony_ci unsigned long flags, spinlock_t *lock) 31962306a36Sopenharmony_ci{ 32062306a36Sopenharmony_ci struct clk_hw *hw; 32162306a36Sopenharmony_ci struct clk_gate *gate = NULL; 32262306a36Sopenharmony_ci struct clk_fixed_factor *fix = NULL; 32362306a36Sopenharmony_ci 32462306a36Sopenharmony_ci /* without gate, register a simple factor clock */ 32562306a36Sopenharmony_ci if (gate_offset == 0) { 32662306a36Sopenharmony_ci return clk_register_fixed_factor(NULL, name, 32762306a36Sopenharmony_ci parent_names[0], flags, mult, 32862306a36Sopenharmony_ci div); 32962306a36Sopenharmony_ci } 33062306a36Sopenharmony_ci 33162306a36Sopenharmony_ci gate = kzalloc(sizeof(*gate), GFP_KERNEL); 33262306a36Sopenharmony_ci if (!gate) 33362306a36Sopenharmony_ci return ERR_PTR(-ENOMEM); 33462306a36Sopenharmony_ci 33562306a36Sopenharmony_ci gate->flags = gate_flags; 33662306a36Sopenharmony_ci gate->reg = base + gate_offset; 33762306a36Sopenharmony_ci gate->bit_idx = gate_shift; 33862306a36Sopenharmony_ci gate->lock = lock; 33962306a36Sopenharmony_ci 34062306a36Sopenharmony_ci fix = kzalloc(sizeof(*fix), GFP_KERNEL); 34162306a36Sopenharmony_ci if (!fix) { 34262306a36Sopenharmony_ci kfree(gate); 34362306a36Sopenharmony_ci return ERR_PTR(-ENOMEM); 34462306a36Sopenharmony_ci } 34562306a36Sopenharmony_ci 34662306a36Sopenharmony_ci fix->mult = mult; 34762306a36Sopenharmony_ci fix->div = div; 34862306a36Sopenharmony_ci 34962306a36Sopenharmony_ci hw = clk_hw_register_composite(NULL, name, parent_names, num_parents, 35062306a36Sopenharmony_ci NULL, NULL, 35162306a36Sopenharmony_ci &fix->hw, &clk_fixed_factor_ops, 35262306a36Sopenharmony_ci &gate->hw, &clk_gate_ops, flags); 35362306a36Sopenharmony_ci if (IS_ERR(hw)) { 35462306a36Sopenharmony_ci kfree(fix); 35562306a36Sopenharmony_ci kfree(gate); 35662306a36Sopenharmony_ci return ERR_CAST(hw); 35762306a36Sopenharmony_ci } 35862306a36Sopenharmony_ci 35962306a36Sopenharmony_ci return hw->clk; 36062306a36Sopenharmony_ci} 36162306a36Sopenharmony_ci 36262306a36Sopenharmony_cistruct rockchip_clk_provider *rockchip_clk_init(struct device_node *np, 36362306a36Sopenharmony_ci void __iomem *base, 36462306a36Sopenharmony_ci unsigned long nr_clks) 36562306a36Sopenharmony_ci{ 36662306a36Sopenharmony_ci struct rockchip_clk_provider *ctx; 36762306a36Sopenharmony_ci struct clk **clk_table; 36862306a36Sopenharmony_ci int i; 36962306a36Sopenharmony_ci 37062306a36Sopenharmony_ci ctx = kzalloc(sizeof(struct rockchip_clk_provider), GFP_KERNEL); 37162306a36Sopenharmony_ci if (!ctx) 37262306a36Sopenharmony_ci return ERR_PTR(-ENOMEM); 37362306a36Sopenharmony_ci 37462306a36Sopenharmony_ci clk_table = kcalloc(nr_clks, sizeof(struct clk *), GFP_KERNEL); 37562306a36Sopenharmony_ci if (!clk_table) 37662306a36Sopenharmony_ci goto err_free; 37762306a36Sopenharmony_ci 37862306a36Sopenharmony_ci for (i = 0; i < nr_clks; ++i) 37962306a36Sopenharmony_ci clk_table[i] = ERR_PTR(-ENOENT); 38062306a36Sopenharmony_ci 38162306a36Sopenharmony_ci ctx->reg_base = base; 38262306a36Sopenharmony_ci ctx->clk_data.clks = clk_table; 38362306a36Sopenharmony_ci ctx->clk_data.clk_num = nr_clks; 38462306a36Sopenharmony_ci ctx->cru_node = np; 38562306a36Sopenharmony_ci spin_lock_init(&ctx->lock); 38662306a36Sopenharmony_ci 38762306a36Sopenharmony_ci ctx->grf = syscon_regmap_lookup_by_phandle(ctx->cru_node, 38862306a36Sopenharmony_ci "rockchip,grf"); 38962306a36Sopenharmony_ci 39062306a36Sopenharmony_ci return ctx; 39162306a36Sopenharmony_ci 39262306a36Sopenharmony_cierr_free: 39362306a36Sopenharmony_ci kfree(ctx); 39462306a36Sopenharmony_ci return ERR_PTR(-ENOMEM); 39562306a36Sopenharmony_ci} 39662306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(rockchip_clk_init); 39762306a36Sopenharmony_ci 39862306a36Sopenharmony_civoid rockchip_clk_of_add_provider(struct device_node *np, 39962306a36Sopenharmony_ci struct rockchip_clk_provider *ctx) 40062306a36Sopenharmony_ci{ 40162306a36Sopenharmony_ci if (of_clk_add_provider(np, of_clk_src_onecell_get, 40262306a36Sopenharmony_ci &ctx->clk_data)) 40362306a36Sopenharmony_ci pr_err("%s: could not register clk provider\n", __func__); 40462306a36Sopenharmony_ci} 40562306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(rockchip_clk_of_add_provider); 40662306a36Sopenharmony_ci 40762306a36Sopenharmony_civoid rockchip_clk_register_plls(struct rockchip_clk_provider *ctx, 40862306a36Sopenharmony_ci struct rockchip_pll_clock *list, 40962306a36Sopenharmony_ci unsigned int nr_pll, int grf_lock_offset) 41062306a36Sopenharmony_ci{ 41162306a36Sopenharmony_ci struct clk *clk; 41262306a36Sopenharmony_ci int idx; 41362306a36Sopenharmony_ci 41462306a36Sopenharmony_ci for (idx = 0; idx < nr_pll; idx++, list++) { 41562306a36Sopenharmony_ci clk = rockchip_clk_register_pll(ctx, list->type, list->name, 41662306a36Sopenharmony_ci list->parent_names, list->num_parents, 41762306a36Sopenharmony_ci list->con_offset, grf_lock_offset, 41862306a36Sopenharmony_ci list->lock_shift, list->mode_offset, 41962306a36Sopenharmony_ci list->mode_shift, list->rate_table, 42062306a36Sopenharmony_ci list->flags, list->pll_flags); 42162306a36Sopenharmony_ci if (IS_ERR(clk)) { 42262306a36Sopenharmony_ci pr_err("%s: failed to register clock %s\n", __func__, 42362306a36Sopenharmony_ci list->name); 42462306a36Sopenharmony_ci continue; 42562306a36Sopenharmony_ci } 42662306a36Sopenharmony_ci 42762306a36Sopenharmony_ci rockchip_clk_add_lookup(ctx, clk, list->id); 42862306a36Sopenharmony_ci } 42962306a36Sopenharmony_ci} 43062306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(rockchip_clk_register_plls); 43162306a36Sopenharmony_ci 43262306a36Sopenharmony_civoid rockchip_clk_register_branches(struct rockchip_clk_provider *ctx, 43362306a36Sopenharmony_ci struct rockchip_clk_branch *list, 43462306a36Sopenharmony_ci unsigned int nr_clk) 43562306a36Sopenharmony_ci{ 43662306a36Sopenharmony_ci struct clk *clk = NULL; 43762306a36Sopenharmony_ci unsigned int idx; 43862306a36Sopenharmony_ci unsigned long flags; 43962306a36Sopenharmony_ci 44062306a36Sopenharmony_ci for (idx = 0; idx < nr_clk; idx++, list++) { 44162306a36Sopenharmony_ci flags = list->flags; 44262306a36Sopenharmony_ci 44362306a36Sopenharmony_ci /* catch simple muxes */ 44462306a36Sopenharmony_ci switch (list->branch_type) { 44562306a36Sopenharmony_ci case branch_mux: 44662306a36Sopenharmony_ci if (list->mux_table) 44762306a36Sopenharmony_ci clk = clk_register_mux_table(NULL, list->name, 44862306a36Sopenharmony_ci list->parent_names, list->num_parents, 44962306a36Sopenharmony_ci flags, 45062306a36Sopenharmony_ci ctx->reg_base + list->muxdiv_offset, 45162306a36Sopenharmony_ci list->mux_shift, list->mux_width, 45262306a36Sopenharmony_ci list->mux_flags, list->mux_table, 45362306a36Sopenharmony_ci &ctx->lock); 45462306a36Sopenharmony_ci else 45562306a36Sopenharmony_ci clk = clk_register_mux(NULL, list->name, 45662306a36Sopenharmony_ci list->parent_names, list->num_parents, 45762306a36Sopenharmony_ci flags, 45862306a36Sopenharmony_ci ctx->reg_base + list->muxdiv_offset, 45962306a36Sopenharmony_ci list->mux_shift, list->mux_width, 46062306a36Sopenharmony_ci list->mux_flags, &ctx->lock); 46162306a36Sopenharmony_ci break; 46262306a36Sopenharmony_ci case branch_muxgrf: 46362306a36Sopenharmony_ci clk = rockchip_clk_register_muxgrf(list->name, 46462306a36Sopenharmony_ci list->parent_names, list->num_parents, 46562306a36Sopenharmony_ci flags, ctx->grf, list->muxdiv_offset, 46662306a36Sopenharmony_ci list->mux_shift, list->mux_width, 46762306a36Sopenharmony_ci list->mux_flags); 46862306a36Sopenharmony_ci break; 46962306a36Sopenharmony_ci case branch_divider: 47062306a36Sopenharmony_ci if (list->div_table) 47162306a36Sopenharmony_ci clk = clk_register_divider_table(NULL, 47262306a36Sopenharmony_ci list->name, list->parent_names[0], 47362306a36Sopenharmony_ci flags, 47462306a36Sopenharmony_ci ctx->reg_base + list->muxdiv_offset, 47562306a36Sopenharmony_ci list->div_shift, list->div_width, 47662306a36Sopenharmony_ci list->div_flags, list->div_table, 47762306a36Sopenharmony_ci &ctx->lock); 47862306a36Sopenharmony_ci else 47962306a36Sopenharmony_ci clk = clk_register_divider(NULL, list->name, 48062306a36Sopenharmony_ci list->parent_names[0], flags, 48162306a36Sopenharmony_ci ctx->reg_base + list->muxdiv_offset, 48262306a36Sopenharmony_ci list->div_shift, list->div_width, 48362306a36Sopenharmony_ci list->div_flags, &ctx->lock); 48462306a36Sopenharmony_ci break; 48562306a36Sopenharmony_ci case branch_fraction_divider: 48662306a36Sopenharmony_ci clk = rockchip_clk_register_frac_branch(ctx, list->name, 48762306a36Sopenharmony_ci list->parent_names, list->num_parents, 48862306a36Sopenharmony_ci ctx->reg_base, list->muxdiv_offset, 48962306a36Sopenharmony_ci list->div_flags, 49062306a36Sopenharmony_ci list->gate_offset, list->gate_shift, 49162306a36Sopenharmony_ci list->gate_flags, flags, list->child, 49262306a36Sopenharmony_ci &ctx->lock); 49362306a36Sopenharmony_ci break; 49462306a36Sopenharmony_ci case branch_half_divider: 49562306a36Sopenharmony_ci clk = rockchip_clk_register_halfdiv(list->name, 49662306a36Sopenharmony_ci list->parent_names, list->num_parents, 49762306a36Sopenharmony_ci ctx->reg_base, list->muxdiv_offset, 49862306a36Sopenharmony_ci list->mux_shift, list->mux_width, 49962306a36Sopenharmony_ci list->mux_flags, list->div_shift, 50062306a36Sopenharmony_ci list->div_width, list->div_flags, 50162306a36Sopenharmony_ci list->gate_offset, list->gate_shift, 50262306a36Sopenharmony_ci list->gate_flags, flags, &ctx->lock); 50362306a36Sopenharmony_ci break; 50462306a36Sopenharmony_ci case branch_gate: 50562306a36Sopenharmony_ci flags |= CLK_SET_RATE_PARENT; 50662306a36Sopenharmony_ci 50762306a36Sopenharmony_ci clk = clk_register_gate(NULL, list->name, 50862306a36Sopenharmony_ci list->parent_names[0], flags, 50962306a36Sopenharmony_ci ctx->reg_base + list->gate_offset, 51062306a36Sopenharmony_ci list->gate_shift, list->gate_flags, &ctx->lock); 51162306a36Sopenharmony_ci break; 51262306a36Sopenharmony_ci case branch_composite: 51362306a36Sopenharmony_ci clk = rockchip_clk_register_branch(list->name, 51462306a36Sopenharmony_ci list->parent_names, list->num_parents, 51562306a36Sopenharmony_ci ctx->reg_base, list->muxdiv_offset, 51662306a36Sopenharmony_ci list->mux_shift, 51762306a36Sopenharmony_ci list->mux_width, list->mux_flags, 51862306a36Sopenharmony_ci list->mux_table, list->div_offset, 51962306a36Sopenharmony_ci list->div_shift, list->div_width, 52062306a36Sopenharmony_ci list->div_flags, list->div_table, 52162306a36Sopenharmony_ci list->gate_offset, list->gate_shift, 52262306a36Sopenharmony_ci list->gate_flags, flags, &ctx->lock); 52362306a36Sopenharmony_ci break; 52462306a36Sopenharmony_ci case branch_mmc: 52562306a36Sopenharmony_ci clk = rockchip_clk_register_mmc( 52662306a36Sopenharmony_ci list->name, 52762306a36Sopenharmony_ci list->parent_names, list->num_parents, 52862306a36Sopenharmony_ci ctx->reg_base + list->muxdiv_offset, 52962306a36Sopenharmony_ci list->div_shift 53062306a36Sopenharmony_ci ); 53162306a36Sopenharmony_ci break; 53262306a36Sopenharmony_ci case branch_inverter: 53362306a36Sopenharmony_ci clk = rockchip_clk_register_inverter( 53462306a36Sopenharmony_ci list->name, list->parent_names, 53562306a36Sopenharmony_ci list->num_parents, 53662306a36Sopenharmony_ci ctx->reg_base + list->muxdiv_offset, 53762306a36Sopenharmony_ci list->div_shift, list->div_flags, &ctx->lock); 53862306a36Sopenharmony_ci break; 53962306a36Sopenharmony_ci case branch_factor: 54062306a36Sopenharmony_ci clk = rockchip_clk_register_factor_branch( 54162306a36Sopenharmony_ci list->name, list->parent_names, 54262306a36Sopenharmony_ci list->num_parents, ctx->reg_base, 54362306a36Sopenharmony_ci list->div_shift, list->div_width, 54462306a36Sopenharmony_ci list->gate_offset, list->gate_shift, 54562306a36Sopenharmony_ci list->gate_flags, flags, &ctx->lock); 54662306a36Sopenharmony_ci break; 54762306a36Sopenharmony_ci case branch_ddrclk: 54862306a36Sopenharmony_ci clk = rockchip_clk_register_ddrclk( 54962306a36Sopenharmony_ci list->name, list->flags, 55062306a36Sopenharmony_ci list->parent_names, list->num_parents, 55162306a36Sopenharmony_ci list->muxdiv_offset, list->mux_shift, 55262306a36Sopenharmony_ci list->mux_width, list->div_shift, 55362306a36Sopenharmony_ci list->div_width, list->div_flags, 55462306a36Sopenharmony_ci ctx->reg_base, &ctx->lock); 55562306a36Sopenharmony_ci break; 55662306a36Sopenharmony_ci } 55762306a36Sopenharmony_ci 55862306a36Sopenharmony_ci /* none of the cases above matched */ 55962306a36Sopenharmony_ci if (!clk) { 56062306a36Sopenharmony_ci pr_err("%s: unknown clock type %d\n", 56162306a36Sopenharmony_ci __func__, list->branch_type); 56262306a36Sopenharmony_ci continue; 56362306a36Sopenharmony_ci } 56462306a36Sopenharmony_ci 56562306a36Sopenharmony_ci if (IS_ERR(clk)) { 56662306a36Sopenharmony_ci pr_err("%s: failed to register clock %s: %ld\n", 56762306a36Sopenharmony_ci __func__, list->name, PTR_ERR(clk)); 56862306a36Sopenharmony_ci continue; 56962306a36Sopenharmony_ci } 57062306a36Sopenharmony_ci 57162306a36Sopenharmony_ci rockchip_clk_add_lookup(ctx, clk, list->id); 57262306a36Sopenharmony_ci } 57362306a36Sopenharmony_ci} 57462306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(rockchip_clk_register_branches); 57562306a36Sopenharmony_ci 57662306a36Sopenharmony_civoid rockchip_clk_register_armclk(struct rockchip_clk_provider *ctx, 57762306a36Sopenharmony_ci unsigned int lookup_id, 57862306a36Sopenharmony_ci const char *name, const char *const *parent_names, 57962306a36Sopenharmony_ci u8 num_parents, 58062306a36Sopenharmony_ci const struct rockchip_cpuclk_reg_data *reg_data, 58162306a36Sopenharmony_ci const struct rockchip_cpuclk_rate_table *rates, 58262306a36Sopenharmony_ci int nrates) 58362306a36Sopenharmony_ci{ 58462306a36Sopenharmony_ci struct clk *clk; 58562306a36Sopenharmony_ci 58662306a36Sopenharmony_ci clk = rockchip_clk_register_cpuclk(name, parent_names, num_parents, 58762306a36Sopenharmony_ci reg_data, rates, nrates, 58862306a36Sopenharmony_ci ctx->reg_base, &ctx->lock); 58962306a36Sopenharmony_ci if (IS_ERR(clk)) { 59062306a36Sopenharmony_ci pr_err("%s: failed to register clock %s: %ld\n", 59162306a36Sopenharmony_ci __func__, name, PTR_ERR(clk)); 59262306a36Sopenharmony_ci return; 59362306a36Sopenharmony_ci } 59462306a36Sopenharmony_ci 59562306a36Sopenharmony_ci rockchip_clk_add_lookup(ctx, clk, lookup_id); 59662306a36Sopenharmony_ci} 59762306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(rockchip_clk_register_armclk); 59862306a36Sopenharmony_ci 59962306a36Sopenharmony_civoid rockchip_clk_protect_critical(const char *const clocks[], 60062306a36Sopenharmony_ci int nclocks) 60162306a36Sopenharmony_ci{ 60262306a36Sopenharmony_ci int i; 60362306a36Sopenharmony_ci 60462306a36Sopenharmony_ci /* Protect the clocks that needs to stay on */ 60562306a36Sopenharmony_ci for (i = 0; i < nclocks; i++) { 60662306a36Sopenharmony_ci struct clk *clk = __clk_lookup(clocks[i]); 60762306a36Sopenharmony_ci 60862306a36Sopenharmony_ci clk_prepare_enable(clk); 60962306a36Sopenharmony_ci } 61062306a36Sopenharmony_ci} 61162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(rockchip_clk_protect_critical); 61262306a36Sopenharmony_ci 61362306a36Sopenharmony_cistatic void __iomem *rst_base; 61462306a36Sopenharmony_cistatic unsigned int reg_restart; 61562306a36Sopenharmony_cistatic void (*cb_restart)(void); 61662306a36Sopenharmony_cistatic int rockchip_restart_notify(struct notifier_block *this, 61762306a36Sopenharmony_ci unsigned long mode, void *cmd) 61862306a36Sopenharmony_ci{ 61962306a36Sopenharmony_ci if (cb_restart) 62062306a36Sopenharmony_ci cb_restart(); 62162306a36Sopenharmony_ci 62262306a36Sopenharmony_ci writel(0xfdb9, rst_base + reg_restart); 62362306a36Sopenharmony_ci return NOTIFY_DONE; 62462306a36Sopenharmony_ci} 62562306a36Sopenharmony_ci 62662306a36Sopenharmony_cistatic struct notifier_block rockchip_restart_handler = { 62762306a36Sopenharmony_ci .notifier_call = rockchip_restart_notify, 62862306a36Sopenharmony_ci .priority = 128, 62962306a36Sopenharmony_ci}; 63062306a36Sopenharmony_ci 63162306a36Sopenharmony_civoid 63262306a36Sopenharmony_cirockchip_register_restart_notifier(struct rockchip_clk_provider *ctx, 63362306a36Sopenharmony_ci unsigned int reg, 63462306a36Sopenharmony_ci void (*cb)(void)) 63562306a36Sopenharmony_ci{ 63662306a36Sopenharmony_ci int ret; 63762306a36Sopenharmony_ci 63862306a36Sopenharmony_ci rst_base = ctx->reg_base; 63962306a36Sopenharmony_ci reg_restart = reg; 64062306a36Sopenharmony_ci cb_restart = cb; 64162306a36Sopenharmony_ci ret = register_restart_handler(&rockchip_restart_handler); 64262306a36Sopenharmony_ci if (ret) 64362306a36Sopenharmony_ci pr_err("%s: cannot register restart handler, %d\n", 64462306a36Sopenharmony_ci __func__, ret); 64562306a36Sopenharmony_ci} 64662306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(rockchip_register_restart_notifier); 647