162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (c) 2013 NVIDIA CORPORATION. All rights reserved. 462306a36Sopenharmony_ci */ 562306a36Sopenharmony_ci 662306a36Sopenharmony_ci#include <linux/clk-provider.h> 762306a36Sopenharmony_ci#include <linux/device.h> 862306a36Sopenharmony_ci#include <linux/err.h> 962306a36Sopenharmony_ci#include <linux/slab.h> 1062306a36Sopenharmony_ci 1162306a36Sopenharmony_cistatic u8 clk_composite_get_parent(struct clk_hw *hw) 1262306a36Sopenharmony_ci{ 1362306a36Sopenharmony_ci struct clk_composite *composite = to_clk_composite(hw); 1462306a36Sopenharmony_ci const struct clk_ops *mux_ops = composite->mux_ops; 1562306a36Sopenharmony_ci struct clk_hw *mux_hw = composite->mux_hw; 1662306a36Sopenharmony_ci 1762306a36Sopenharmony_ci __clk_hw_set_clk(mux_hw, hw); 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_ci return mux_ops->get_parent(mux_hw); 2062306a36Sopenharmony_ci} 2162306a36Sopenharmony_ci 2262306a36Sopenharmony_cistatic int clk_composite_set_parent(struct clk_hw *hw, u8 index) 2362306a36Sopenharmony_ci{ 2462306a36Sopenharmony_ci struct clk_composite *composite = to_clk_composite(hw); 2562306a36Sopenharmony_ci const struct clk_ops *mux_ops = composite->mux_ops; 2662306a36Sopenharmony_ci struct clk_hw *mux_hw = composite->mux_hw; 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_ci __clk_hw_set_clk(mux_hw, hw); 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_ci return mux_ops->set_parent(mux_hw, index); 3162306a36Sopenharmony_ci} 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_cistatic unsigned long clk_composite_recalc_rate(struct clk_hw *hw, 3462306a36Sopenharmony_ci unsigned long parent_rate) 3562306a36Sopenharmony_ci{ 3662306a36Sopenharmony_ci struct clk_composite *composite = to_clk_composite(hw); 3762306a36Sopenharmony_ci const struct clk_ops *rate_ops = composite->rate_ops; 3862306a36Sopenharmony_ci struct clk_hw *rate_hw = composite->rate_hw; 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_ci __clk_hw_set_clk(rate_hw, hw); 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_ci return rate_ops->recalc_rate(rate_hw, parent_rate); 4362306a36Sopenharmony_ci} 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_cistatic int clk_composite_determine_rate_for_parent(struct clk_hw *rate_hw, 4662306a36Sopenharmony_ci struct clk_rate_request *req, 4762306a36Sopenharmony_ci struct clk_hw *parent_hw, 4862306a36Sopenharmony_ci const struct clk_ops *rate_ops) 4962306a36Sopenharmony_ci{ 5062306a36Sopenharmony_ci long rate; 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_ci req->best_parent_hw = parent_hw; 5362306a36Sopenharmony_ci req->best_parent_rate = clk_hw_get_rate(parent_hw); 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_ci if (rate_ops->determine_rate) 5662306a36Sopenharmony_ci return rate_ops->determine_rate(rate_hw, req); 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_ci rate = rate_ops->round_rate(rate_hw, req->rate, 5962306a36Sopenharmony_ci &req->best_parent_rate); 6062306a36Sopenharmony_ci if (rate < 0) 6162306a36Sopenharmony_ci return rate; 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_ci req->rate = rate; 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_ci return 0; 6662306a36Sopenharmony_ci} 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_cistatic int clk_composite_determine_rate(struct clk_hw *hw, 6962306a36Sopenharmony_ci struct clk_rate_request *req) 7062306a36Sopenharmony_ci{ 7162306a36Sopenharmony_ci struct clk_composite *composite = to_clk_composite(hw); 7262306a36Sopenharmony_ci const struct clk_ops *rate_ops = composite->rate_ops; 7362306a36Sopenharmony_ci const struct clk_ops *mux_ops = composite->mux_ops; 7462306a36Sopenharmony_ci struct clk_hw *rate_hw = composite->rate_hw; 7562306a36Sopenharmony_ci struct clk_hw *mux_hw = composite->mux_hw; 7662306a36Sopenharmony_ci struct clk_hw *parent; 7762306a36Sopenharmony_ci unsigned long rate_diff; 7862306a36Sopenharmony_ci unsigned long best_rate_diff = ULONG_MAX; 7962306a36Sopenharmony_ci unsigned long best_rate = 0; 8062306a36Sopenharmony_ci int i, ret; 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_ci if (rate_hw && rate_ops && 8362306a36Sopenharmony_ci (rate_ops->determine_rate || rate_ops->round_rate) && 8462306a36Sopenharmony_ci mux_hw && mux_ops && mux_ops->set_parent) { 8562306a36Sopenharmony_ci req->best_parent_hw = NULL; 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_ci if (clk_hw_get_flags(hw) & CLK_SET_RATE_NO_REPARENT) { 8862306a36Sopenharmony_ci struct clk_rate_request tmp_req; 8962306a36Sopenharmony_ci 9062306a36Sopenharmony_ci parent = clk_hw_get_parent(mux_hw); 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_ci clk_hw_forward_rate_request(hw, req, parent, &tmp_req, req->rate); 9362306a36Sopenharmony_ci ret = clk_composite_determine_rate_for_parent(rate_hw, 9462306a36Sopenharmony_ci &tmp_req, 9562306a36Sopenharmony_ci parent, 9662306a36Sopenharmony_ci rate_ops); 9762306a36Sopenharmony_ci if (ret) 9862306a36Sopenharmony_ci return ret; 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_ci req->rate = tmp_req.rate; 10162306a36Sopenharmony_ci req->best_parent_hw = tmp_req.best_parent_hw; 10262306a36Sopenharmony_ci req->best_parent_rate = tmp_req.best_parent_rate; 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_ci return 0; 10562306a36Sopenharmony_ci } 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_ci for (i = 0; i < clk_hw_get_num_parents(mux_hw); i++) { 10862306a36Sopenharmony_ci struct clk_rate_request tmp_req; 10962306a36Sopenharmony_ci 11062306a36Sopenharmony_ci parent = clk_hw_get_parent_by_index(mux_hw, i); 11162306a36Sopenharmony_ci if (!parent) 11262306a36Sopenharmony_ci continue; 11362306a36Sopenharmony_ci 11462306a36Sopenharmony_ci clk_hw_forward_rate_request(hw, req, parent, &tmp_req, req->rate); 11562306a36Sopenharmony_ci ret = clk_composite_determine_rate_for_parent(rate_hw, 11662306a36Sopenharmony_ci &tmp_req, 11762306a36Sopenharmony_ci parent, 11862306a36Sopenharmony_ci rate_ops); 11962306a36Sopenharmony_ci if (ret) 12062306a36Sopenharmony_ci continue; 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_ci if (req->rate >= tmp_req.rate) 12362306a36Sopenharmony_ci rate_diff = req->rate - tmp_req.rate; 12462306a36Sopenharmony_ci else 12562306a36Sopenharmony_ci rate_diff = tmp_req.rate - req->rate; 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_ci if (!rate_diff || !req->best_parent_hw 12862306a36Sopenharmony_ci || best_rate_diff > rate_diff) { 12962306a36Sopenharmony_ci req->best_parent_hw = parent; 13062306a36Sopenharmony_ci req->best_parent_rate = tmp_req.best_parent_rate; 13162306a36Sopenharmony_ci best_rate_diff = rate_diff; 13262306a36Sopenharmony_ci best_rate = tmp_req.rate; 13362306a36Sopenharmony_ci } 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_ci if (!rate_diff) 13662306a36Sopenharmony_ci return 0; 13762306a36Sopenharmony_ci } 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_ci req->rate = best_rate; 14062306a36Sopenharmony_ci return 0; 14162306a36Sopenharmony_ci } else if (rate_hw && rate_ops && rate_ops->determine_rate) { 14262306a36Sopenharmony_ci __clk_hw_set_clk(rate_hw, hw); 14362306a36Sopenharmony_ci return rate_ops->determine_rate(rate_hw, req); 14462306a36Sopenharmony_ci } else if (mux_hw && mux_ops && mux_ops->determine_rate) { 14562306a36Sopenharmony_ci __clk_hw_set_clk(mux_hw, hw); 14662306a36Sopenharmony_ci return mux_ops->determine_rate(mux_hw, req); 14762306a36Sopenharmony_ci } else { 14862306a36Sopenharmony_ci pr_err("clk: clk_composite_determine_rate function called, but no mux or rate callback set!\n"); 14962306a36Sopenharmony_ci return -EINVAL; 15062306a36Sopenharmony_ci } 15162306a36Sopenharmony_ci} 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_cistatic long clk_composite_round_rate(struct clk_hw *hw, unsigned long rate, 15462306a36Sopenharmony_ci unsigned long *prate) 15562306a36Sopenharmony_ci{ 15662306a36Sopenharmony_ci struct clk_composite *composite = to_clk_composite(hw); 15762306a36Sopenharmony_ci const struct clk_ops *rate_ops = composite->rate_ops; 15862306a36Sopenharmony_ci struct clk_hw *rate_hw = composite->rate_hw; 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_ci __clk_hw_set_clk(rate_hw, hw); 16162306a36Sopenharmony_ci 16262306a36Sopenharmony_ci return rate_ops->round_rate(rate_hw, rate, prate); 16362306a36Sopenharmony_ci} 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_cistatic int clk_composite_set_rate(struct clk_hw *hw, unsigned long rate, 16662306a36Sopenharmony_ci unsigned long parent_rate) 16762306a36Sopenharmony_ci{ 16862306a36Sopenharmony_ci struct clk_composite *composite = to_clk_composite(hw); 16962306a36Sopenharmony_ci const struct clk_ops *rate_ops = composite->rate_ops; 17062306a36Sopenharmony_ci struct clk_hw *rate_hw = composite->rate_hw; 17162306a36Sopenharmony_ci 17262306a36Sopenharmony_ci __clk_hw_set_clk(rate_hw, hw); 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_ci return rate_ops->set_rate(rate_hw, rate, parent_rate); 17562306a36Sopenharmony_ci} 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_cistatic int clk_composite_set_rate_and_parent(struct clk_hw *hw, 17862306a36Sopenharmony_ci unsigned long rate, 17962306a36Sopenharmony_ci unsigned long parent_rate, 18062306a36Sopenharmony_ci u8 index) 18162306a36Sopenharmony_ci{ 18262306a36Sopenharmony_ci struct clk_composite *composite = to_clk_composite(hw); 18362306a36Sopenharmony_ci const struct clk_ops *rate_ops = composite->rate_ops; 18462306a36Sopenharmony_ci const struct clk_ops *mux_ops = composite->mux_ops; 18562306a36Sopenharmony_ci struct clk_hw *rate_hw = composite->rate_hw; 18662306a36Sopenharmony_ci struct clk_hw *mux_hw = composite->mux_hw; 18762306a36Sopenharmony_ci unsigned long temp_rate; 18862306a36Sopenharmony_ci 18962306a36Sopenharmony_ci __clk_hw_set_clk(rate_hw, hw); 19062306a36Sopenharmony_ci __clk_hw_set_clk(mux_hw, hw); 19162306a36Sopenharmony_ci 19262306a36Sopenharmony_ci temp_rate = rate_ops->recalc_rate(rate_hw, parent_rate); 19362306a36Sopenharmony_ci if (temp_rate > rate) { 19462306a36Sopenharmony_ci rate_ops->set_rate(rate_hw, rate, parent_rate); 19562306a36Sopenharmony_ci mux_ops->set_parent(mux_hw, index); 19662306a36Sopenharmony_ci } else { 19762306a36Sopenharmony_ci mux_ops->set_parent(mux_hw, index); 19862306a36Sopenharmony_ci rate_ops->set_rate(rate_hw, rate, parent_rate); 19962306a36Sopenharmony_ci } 20062306a36Sopenharmony_ci 20162306a36Sopenharmony_ci return 0; 20262306a36Sopenharmony_ci} 20362306a36Sopenharmony_ci 20462306a36Sopenharmony_cistatic int clk_composite_is_enabled(struct clk_hw *hw) 20562306a36Sopenharmony_ci{ 20662306a36Sopenharmony_ci struct clk_composite *composite = to_clk_composite(hw); 20762306a36Sopenharmony_ci const struct clk_ops *gate_ops = composite->gate_ops; 20862306a36Sopenharmony_ci struct clk_hw *gate_hw = composite->gate_hw; 20962306a36Sopenharmony_ci 21062306a36Sopenharmony_ci __clk_hw_set_clk(gate_hw, hw); 21162306a36Sopenharmony_ci 21262306a36Sopenharmony_ci return gate_ops->is_enabled(gate_hw); 21362306a36Sopenharmony_ci} 21462306a36Sopenharmony_ci 21562306a36Sopenharmony_cistatic int clk_composite_enable(struct clk_hw *hw) 21662306a36Sopenharmony_ci{ 21762306a36Sopenharmony_ci struct clk_composite *composite = to_clk_composite(hw); 21862306a36Sopenharmony_ci const struct clk_ops *gate_ops = composite->gate_ops; 21962306a36Sopenharmony_ci struct clk_hw *gate_hw = composite->gate_hw; 22062306a36Sopenharmony_ci 22162306a36Sopenharmony_ci __clk_hw_set_clk(gate_hw, hw); 22262306a36Sopenharmony_ci 22362306a36Sopenharmony_ci return gate_ops->enable(gate_hw); 22462306a36Sopenharmony_ci} 22562306a36Sopenharmony_ci 22662306a36Sopenharmony_cistatic void clk_composite_disable(struct clk_hw *hw) 22762306a36Sopenharmony_ci{ 22862306a36Sopenharmony_ci struct clk_composite *composite = to_clk_composite(hw); 22962306a36Sopenharmony_ci const struct clk_ops *gate_ops = composite->gate_ops; 23062306a36Sopenharmony_ci struct clk_hw *gate_hw = composite->gate_hw; 23162306a36Sopenharmony_ci 23262306a36Sopenharmony_ci __clk_hw_set_clk(gate_hw, hw); 23362306a36Sopenharmony_ci 23462306a36Sopenharmony_ci gate_ops->disable(gate_hw); 23562306a36Sopenharmony_ci} 23662306a36Sopenharmony_ci 23762306a36Sopenharmony_cistatic struct clk_hw *__clk_hw_register_composite(struct device *dev, 23862306a36Sopenharmony_ci const char *name, const char * const *parent_names, 23962306a36Sopenharmony_ci const struct clk_parent_data *pdata, int num_parents, 24062306a36Sopenharmony_ci struct clk_hw *mux_hw, const struct clk_ops *mux_ops, 24162306a36Sopenharmony_ci struct clk_hw *rate_hw, const struct clk_ops *rate_ops, 24262306a36Sopenharmony_ci struct clk_hw *gate_hw, const struct clk_ops *gate_ops, 24362306a36Sopenharmony_ci unsigned long flags) 24462306a36Sopenharmony_ci{ 24562306a36Sopenharmony_ci struct clk_hw *hw; 24662306a36Sopenharmony_ci struct clk_init_data init = {}; 24762306a36Sopenharmony_ci struct clk_composite *composite; 24862306a36Sopenharmony_ci struct clk_ops *clk_composite_ops; 24962306a36Sopenharmony_ci int ret; 25062306a36Sopenharmony_ci 25162306a36Sopenharmony_ci composite = kzalloc(sizeof(*composite), GFP_KERNEL); 25262306a36Sopenharmony_ci if (!composite) 25362306a36Sopenharmony_ci return ERR_PTR(-ENOMEM); 25462306a36Sopenharmony_ci 25562306a36Sopenharmony_ci init.name = name; 25662306a36Sopenharmony_ci init.flags = flags; 25762306a36Sopenharmony_ci if (parent_names) 25862306a36Sopenharmony_ci init.parent_names = parent_names; 25962306a36Sopenharmony_ci else 26062306a36Sopenharmony_ci init.parent_data = pdata; 26162306a36Sopenharmony_ci init.num_parents = num_parents; 26262306a36Sopenharmony_ci hw = &composite->hw; 26362306a36Sopenharmony_ci 26462306a36Sopenharmony_ci clk_composite_ops = &composite->ops; 26562306a36Sopenharmony_ci 26662306a36Sopenharmony_ci if (mux_hw && mux_ops) { 26762306a36Sopenharmony_ci if (!mux_ops->get_parent) { 26862306a36Sopenharmony_ci hw = ERR_PTR(-EINVAL); 26962306a36Sopenharmony_ci goto err; 27062306a36Sopenharmony_ci } 27162306a36Sopenharmony_ci 27262306a36Sopenharmony_ci composite->mux_hw = mux_hw; 27362306a36Sopenharmony_ci composite->mux_ops = mux_ops; 27462306a36Sopenharmony_ci clk_composite_ops->get_parent = clk_composite_get_parent; 27562306a36Sopenharmony_ci if (mux_ops->set_parent) 27662306a36Sopenharmony_ci clk_composite_ops->set_parent = clk_composite_set_parent; 27762306a36Sopenharmony_ci if (mux_ops->determine_rate) 27862306a36Sopenharmony_ci clk_composite_ops->determine_rate = clk_composite_determine_rate; 27962306a36Sopenharmony_ci } 28062306a36Sopenharmony_ci 28162306a36Sopenharmony_ci if (rate_hw && rate_ops) { 28262306a36Sopenharmony_ci if (!rate_ops->recalc_rate) { 28362306a36Sopenharmony_ci hw = ERR_PTR(-EINVAL); 28462306a36Sopenharmony_ci goto err; 28562306a36Sopenharmony_ci } 28662306a36Sopenharmony_ci clk_composite_ops->recalc_rate = clk_composite_recalc_rate; 28762306a36Sopenharmony_ci 28862306a36Sopenharmony_ci if (rate_ops->determine_rate) 28962306a36Sopenharmony_ci clk_composite_ops->determine_rate = 29062306a36Sopenharmony_ci clk_composite_determine_rate; 29162306a36Sopenharmony_ci else if (rate_ops->round_rate) 29262306a36Sopenharmony_ci clk_composite_ops->round_rate = 29362306a36Sopenharmony_ci clk_composite_round_rate; 29462306a36Sopenharmony_ci 29562306a36Sopenharmony_ci /* .set_rate requires either .round_rate or .determine_rate */ 29662306a36Sopenharmony_ci if (rate_ops->set_rate) { 29762306a36Sopenharmony_ci if (rate_ops->determine_rate || rate_ops->round_rate) 29862306a36Sopenharmony_ci clk_composite_ops->set_rate = 29962306a36Sopenharmony_ci clk_composite_set_rate; 30062306a36Sopenharmony_ci else 30162306a36Sopenharmony_ci WARN(1, "%s: missing round_rate op is required\n", 30262306a36Sopenharmony_ci __func__); 30362306a36Sopenharmony_ci } 30462306a36Sopenharmony_ci 30562306a36Sopenharmony_ci composite->rate_hw = rate_hw; 30662306a36Sopenharmony_ci composite->rate_ops = rate_ops; 30762306a36Sopenharmony_ci } 30862306a36Sopenharmony_ci 30962306a36Sopenharmony_ci if (mux_hw && mux_ops && rate_hw && rate_ops) { 31062306a36Sopenharmony_ci if (mux_ops->set_parent && rate_ops->set_rate) 31162306a36Sopenharmony_ci clk_composite_ops->set_rate_and_parent = 31262306a36Sopenharmony_ci clk_composite_set_rate_and_parent; 31362306a36Sopenharmony_ci } 31462306a36Sopenharmony_ci 31562306a36Sopenharmony_ci if (gate_hw && gate_ops) { 31662306a36Sopenharmony_ci if (!gate_ops->is_enabled || !gate_ops->enable || 31762306a36Sopenharmony_ci !gate_ops->disable) { 31862306a36Sopenharmony_ci hw = ERR_PTR(-EINVAL); 31962306a36Sopenharmony_ci goto err; 32062306a36Sopenharmony_ci } 32162306a36Sopenharmony_ci 32262306a36Sopenharmony_ci composite->gate_hw = gate_hw; 32362306a36Sopenharmony_ci composite->gate_ops = gate_ops; 32462306a36Sopenharmony_ci clk_composite_ops->is_enabled = clk_composite_is_enabled; 32562306a36Sopenharmony_ci clk_composite_ops->enable = clk_composite_enable; 32662306a36Sopenharmony_ci clk_composite_ops->disable = clk_composite_disable; 32762306a36Sopenharmony_ci } 32862306a36Sopenharmony_ci 32962306a36Sopenharmony_ci init.ops = clk_composite_ops; 33062306a36Sopenharmony_ci composite->hw.init = &init; 33162306a36Sopenharmony_ci 33262306a36Sopenharmony_ci ret = clk_hw_register(dev, hw); 33362306a36Sopenharmony_ci if (ret) { 33462306a36Sopenharmony_ci hw = ERR_PTR(ret); 33562306a36Sopenharmony_ci goto err; 33662306a36Sopenharmony_ci } 33762306a36Sopenharmony_ci 33862306a36Sopenharmony_ci if (composite->mux_hw) 33962306a36Sopenharmony_ci composite->mux_hw->clk = hw->clk; 34062306a36Sopenharmony_ci 34162306a36Sopenharmony_ci if (composite->rate_hw) 34262306a36Sopenharmony_ci composite->rate_hw->clk = hw->clk; 34362306a36Sopenharmony_ci 34462306a36Sopenharmony_ci if (composite->gate_hw) 34562306a36Sopenharmony_ci composite->gate_hw->clk = hw->clk; 34662306a36Sopenharmony_ci 34762306a36Sopenharmony_ci return hw; 34862306a36Sopenharmony_ci 34962306a36Sopenharmony_cierr: 35062306a36Sopenharmony_ci kfree(composite); 35162306a36Sopenharmony_ci return hw; 35262306a36Sopenharmony_ci} 35362306a36Sopenharmony_ci 35462306a36Sopenharmony_cistruct clk_hw *clk_hw_register_composite(struct device *dev, const char *name, 35562306a36Sopenharmony_ci const char * const *parent_names, int num_parents, 35662306a36Sopenharmony_ci struct clk_hw *mux_hw, const struct clk_ops *mux_ops, 35762306a36Sopenharmony_ci struct clk_hw *rate_hw, const struct clk_ops *rate_ops, 35862306a36Sopenharmony_ci struct clk_hw *gate_hw, const struct clk_ops *gate_ops, 35962306a36Sopenharmony_ci unsigned long flags) 36062306a36Sopenharmony_ci{ 36162306a36Sopenharmony_ci return __clk_hw_register_composite(dev, name, parent_names, NULL, 36262306a36Sopenharmony_ci num_parents, mux_hw, mux_ops, 36362306a36Sopenharmony_ci rate_hw, rate_ops, gate_hw, 36462306a36Sopenharmony_ci gate_ops, flags); 36562306a36Sopenharmony_ci} 36662306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(clk_hw_register_composite); 36762306a36Sopenharmony_ci 36862306a36Sopenharmony_cistruct clk_hw *clk_hw_register_composite_pdata(struct device *dev, 36962306a36Sopenharmony_ci const char *name, 37062306a36Sopenharmony_ci const struct clk_parent_data *parent_data, 37162306a36Sopenharmony_ci int num_parents, 37262306a36Sopenharmony_ci struct clk_hw *mux_hw, const struct clk_ops *mux_ops, 37362306a36Sopenharmony_ci struct clk_hw *rate_hw, const struct clk_ops *rate_ops, 37462306a36Sopenharmony_ci struct clk_hw *gate_hw, const struct clk_ops *gate_ops, 37562306a36Sopenharmony_ci unsigned long flags) 37662306a36Sopenharmony_ci{ 37762306a36Sopenharmony_ci return __clk_hw_register_composite(dev, name, NULL, parent_data, 37862306a36Sopenharmony_ci num_parents, mux_hw, mux_ops, 37962306a36Sopenharmony_ci rate_hw, rate_ops, gate_hw, 38062306a36Sopenharmony_ci gate_ops, flags); 38162306a36Sopenharmony_ci} 38262306a36Sopenharmony_ci 38362306a36Sopenharmony_cistruct clk *clk_register_composite(struct device *dev, const char *name, 38462306a36Sopenharmony_ci const char * const *parent_names, int num_parents, 38562306a36Sopenharmony_ci struct clk_hw *mux_hw, const struct clk_ops *mux_ops, 38662306a36Sopenharmony_ci struct clk_hw *rate_hw, const struct clk_ops *rate_ops, 38762306a36Sopenharmony_ci struct clk_hw *gate_hw, const struct clk_ops *gate_ops, 38862306a36Sopenharmony_ci unsigned long flags) 38962306a36Sopenharmony_ci{ 39062306a36Sopenharmony_ci struct clk_hw *hw; 39162306a36Sopenharmony_ci 39262306a36Sopenharmony_ci hw = clk_hw_register_composite(dev, name, parent_names, num_parents, 39362306a36Sopenharmony_ci mux_hw, mux_ops, rate_hw, rate_ops, gate_hw, gate_ops, 39462306a36Sopenharmony_ci flags); 39562306a36Sopenharmony_ci if (IS_ERR(hw)) 39662306a36Sopenharmony_ci return ERR_CAST(hw); 39762306a36Sopenharmony_ci return hw->clk; 39862306a36Sopenharmony_ci} 39962306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(clk_register_composite); 40062306a36Sopenharmony_ci 40162306a36Sopenharmony_cistruct clk *clk_register_composite_pdata(struct device *dev, const char *name, 40262306a36Sopenharmony_ci const struct clk_parent_data *parent_data, 40362306a36Sopenharmony_ci int num_parents, 40462306a36Sopenharmony_ci struct clk_hw *mux_hw, const struct clk_ops *mux_ops, 40562306a36Sopenharmony_ci struct clk_hw *rate_hw, const struct clk_ops *rate_ops, 40662306a36Sopenharmony_ci struct clk_hw *gate_hw, const struct clk_ops *gate_ops, 40762306a36Sopenharmony_ci unsigned long flags) 40862306a36Sopenharmony_ci{ 40962306a36Sopenharmony_ci struct clk_hw *hw; 41062306a36Sopenharmony_ci 41162306a36Sopenharmony_ci hw = clk_hw_register_composite_pdata(dev, name, parent_data, 41262306a36Sopenharmony_ci num_parents, mux_hw, mux_ops, rate_hw, rate_ops, 41362306a36Sopenharmony_ci gate_hw, gate_ops, flags); 41462306a36Sopenharmony_ci if (IS_ERR(hw)) 41562306a36Sopenharmony_ci return ERR_CAST(hw); 41662306a36Sopenharmony_ci return hw->clk; 41762306a36Sopenharmony_ci} 41862306a36Sopenharmony_ci 41962306a36Sopenharmony_civoid clk_unregister_composite(struct clk *clk) 42062306a36Sopenharmony_ci{ 42162306a36Sopenharmony_ci struct clk_composite *composite; 42262306a36Sopenharmony_ci struct clk_hw *hw; 42362306a36Sopenharmony_ci 42462306a36Sopenharmony_ci hw = __clk_get_hw(clk); 42562306a36Sopenharmony_ci if (!hw) 42662306a36Sopenharmony_ci return; 42762306a36Sopenharmony_ci 42862306a36Sopenharmony_ci composite = to_clk_composite(hw); 42962306a36Sopenharmony_ci 43062306a36Sopenharmony_ci clk_unregister(clk); 43162306a36Sopenharmony_ci kfree(composite); 43262306a36Sopenharmony_ci} 43362306a36Sopenharmony_ci 43462306a36Sopenharmony_civoid clk_hw_unregister_composite(struct clk_hw *hw) 43562306a36Sopenharmony_ci{ 43662306a36Sopenharmony_ci struct clk_composite *composite; 43762306a36Sopenharmony_ci 43862306a36Sopenharmony_ci composite = to_clk_composite(hw); 43962306a36Sopenharmony_ci 44062306a36Sopenharmony_ci clk_hw_unregister(hw); 44162306a36Sopenharmony_ci kfree(composite); 44262306a36Sopenharmony_ci} 44362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(clk_hw_unregister_composite); 44462306a36Sopenharmony_ci 44562306a36Sopenharmony_cistatic void devm_clk_hw_release_composite(struct device *dev, void *res) 44662306a36Sopenharmony_ci{ 44762306a36Sopenharmony_ci clk_hw_unregister_composite(*(struct clk_hw **)res); 44862306a36Sopenharmony_ci} 44962306a36Sopenharmony_ci 45062306a36Sopenharmony_cistatic struct clk_hw *__devm_clk_hw_register_composite(struct device *dev, 45162306a36Sopenharmony_ci const char *name, const char * const *parent_names, 45262306a36Sopenharmony_ci const struct clk_parent_data *pdata, int num_parents, 45362306a36Sopenharmony_ci struct clk_hw *mux_hw, const struct clk_ops *mux_ops, 45462306a36Sopenharmony_ci struct clk_hw *rate_hw, const struct clk_ops *rate_ops, 45562306a36Sopenharmony_ci struct clk_hw *gate_hw, const struct clk_ops *gate_ops, 45662306a36Sopenharmony_ci unsigned long flags) 45762306a36Sopenharmony_ci{ 45862306a36Sopenharmony_ci struct clk_hw **ptr, *hw; 45962306a36Sopenharmony_ci 46062306a36Sopenharmony_ci ptr = devres_alloc(devm_clk_hw_release_composite, sizeof(*ptr), 46162306a36Sopenharmony_ci GFP_KERNEL); 46262306a36Sopenharmony_ci if (!ptr) 46362306a36Sopenharmony_ci return ERR_PTR(-ENOMEM); 46462306a36Sopenharmony_ci 46562306a36Sopenharmony_ci hw = __clk_hw_register_composite(dev, name, parent_names, pdata, 46662306a36Sopenharmony_ci num_parents, mux_hw, mux_ops, rate_hw, 46762306a36Sopenharmony_ci rate_ops, gate_hw, gate_ops, flags); 46862306a36Sopenharmony_ci 46962306a36Sopenharmony_ci if (!IS_ERR(hw)) { 47062306a36Sopenharmony_ci *ptr = hw; 47162306a36Sopenharmony_ci devres_add(dev, ptr); 47262306a36Sopenharmony_ci } else { 47362306a36Sopenharmony_ci devres_free(ptr); 47462306a36Sopenharmony_ci } 47562306a36Sopenharmony_ci 47662306a36Sopenharmony_ci return hw; 47762306a36Sopenharmony_ci} 47862306a36Sopenharmony_ci 47962306a36Sopenharmony_cistruct clk_hw *devm_clk_hw_register_composite_pdata(struct device *dev, 48062306a36Sopenharmony_ci const char *name, 48162306a36Sopenharmony_ci const struct clk_parent_data *parent_data, 48262306a36Sopenharmony_ci int num_parents, 48362306a36Sopenharmony_ci struct clk_hw *mux_hw, const struct clk_ops *mux_ops, 48462306a36Sopenharmony_ci struct clk_hw *rate_hw, const struct clk_ops *rate_ops, 48562306a36Sopenharmony_ci struct clk_hw *gate_hw, const struct clk_ops *gate_ops, 48662306a36Sopenharmony_ci unsigned long flags) 48762306a36Sopenharmony_ci{ 48862306a36Sopenharmony_ci return __devm_clk_hw_register_composite(dev, name, NULL, parent_data, 48962306a36Sopenharmony_ci num_parents, mux_hw, mux_ops, 49062306a36Sopenharmony_ci rate_hw, rate_ops, gate_hw, 49162306a36Sopenharmony_ci gate_ops, flags); 49262306a36Sopenharmony_ci} 493