162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (c) 2015 Linaro Ltd. 462306a36Sopenharmony_ci * Author: Pi-Cheng Chen <pi-cheng.chen@linaro.org> 562306a36Sopenharmony_ci */ 662306a36Sopenharmony_ci 762306a36Sopenharmony_ci#include <linux/clk-provider.h> 862306a36Sopenharmony_ci#include <linux/container_of.h> 962306a36Sopenharmony_ci#include <linux/err.h> 1062306a36Sopenharmony_ci#include <linux/mfd/syscon.h> 1162306a36Sopenharmony_ci#include <linux/module.h> 1262306a36Sopenharmony_ci#include <linux/regmap.h> 1362306a36Sopenharmony_ci#include <linux/slab.h> 1462306a36Sopenharmony_ci 1562306a36Sopenharmony_ci#include "clk-mtk.h" 1662306a36Sopenharmony_ci#include "clk-cpumux.h" 1762306a36Sopenharmony_ci 1862306a36Sopenharmony_cistruct mtk_clk_cpumux { 1962306a36Sopenharmony_ci struct clk_hw hw; 2062306a36Sopenharmony_ci struct regmap *regmap; 2162306a36Sopenharmony_ci u32 reg; 2262306a36Sopenharmony_ci u32 mask; 2362306a36Sopenharmony_ci u8 shift; 2462306a36Sopenharmony_ci}; 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_cistatic inline struct mtk_clk_cpumux *to_mtk_clk_cpumux(struct clk_hw *_hw) 2762306a36Sopenharmony_ci{ 2862306a36Sopenharmony_ci return container_of(_hw, struct mtk_clk_cpumux, hw); 2962306a36Sopenharmony_ci} 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_cistatic u8 clk_cpumux_get_parent(struct clk_hw *hw) 3262306a36Sopenharmony_ci{ 3362306a36Sopenharmony_ci struct mtk_clk_cpumux *mux = to_mtk_clk_cpumux(hw); 3462306a36Sopenharmony_ci unsigned int val; 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_ci regmap_read(mux->regmap, mux->reg, &val); 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_ci val >>= mux->shift; 3962306a36Sopenharmony_ci val &= mux->mask; 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_ci return val; 4262306a36Sopenharmony_ci} 4362306a36Sopenharmony_ci 4462306a36Sopenharmony_cistatic int clk_cpumux_set_parent(struct clk_hw *hw, u8 index) 4562306a36Sopenharmony_ci{ 4662306a36Sopenharmony_ci struct mtk_clk_cpumux *mux = to_mtk_clk_cpumux(hw); 4762306a36Sopenharmony_ci u32 mask, val; 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_ci val = index << mux->shift; 5062306a36Sopenharmony_ci mask = mux->mask << mux->shift; 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_ci return regmap_update_bits(mux->regmap, mux->reg, mask, val); 5362306a36Sopenharmony_ci} 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_cistatic const struct clk_ops clk_cpumux_ops = { 5662306a36Sopenharmony_ci .determine_rate = clk_hw_determine_rate_no_reparent, 5762306a36Sopenharmony_ci .get_parent = clk_cpumux_get_parent, 5862306a36Sopenharmony_ci .set_parent = clk_cpumux_set_parent, 5962306a36Sopenharmony_ci}; 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_cistatic struct clk_hw * 6262306a36Sopenharmony_cimtk_clk_register_cpumux(struct device *dev, const struct mtk_composite *mux, 6362306a36Sopenharmony_ci struct regmap *regmap) 6462306a36Sopenharmony_ci{ 6562306a36Sopenharmony_ci struct mtk_clk_cpumux *cpumux; 6662306a36Sopenharmony_ci int ret; 6762306a36Sopenharmony_ci struct clk_init_data init; 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_ci cpumux = kzalloc(sizeof(*cpumux), GFP_KERNEL); 7062306a36Sopenharmony_ci if (!cpumux) 7162306a36Sopenharmony_ci return ERR_PTR(-ENOMEM); 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_ci init.name = mux->name; 7462306a36Sopenharmony_ci init.ops = &clk_cpumux_ops; 7562306a36Sopenharmony_ci init.parent_names = mux->parent_names; 7662306a36Sopenharmony_ci init.num_parents = mux->num_parents; 7762306a36Sopenharmony_ci init.flags = mux->flags; 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_ci cpumux->reg = mux->mux_reg; 8062306a36Sopenharmony_ci cpumux->shift = mux->mux_shift; 8162306a36Sopenharmony_ci cpumux->mask = BIT(mux->mux_width) - 1; 8262306a36Sopenharmony_ci cpumux->regmap = regmap; 8362306a36Sopenharmony_ci cpumux->hw.init = &init; 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_ci ret = clk_hw_register(dev, &cpumux->hw); 8662306a36Sopenharmony_ci if (ret) { 8762306a36Sopenharmony_ci kfree(cpumux); 8862306a36Sopenharmony_ci return ERR_PTR(ret); 8962306a36Sopenharmony_ci } 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_ci return &cpumux->hw; 9262306a36Sopenharmony_ci} 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_cistatic void mtk_clk_unregister_cpumux(struct clk_hw *hw) 9562306a36Sopenharmony_ci{ 9662306a36Sopenharmony_ci struct mtk_clk_cpumux *cpumux; 9762306a36Sopenharmony_ci if (!hw) 9862306a36Sopenharmony_ci return; 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_ci cpumux = to_mtk_clk_cpumux(hw); 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_ci clk_hw_unregister(hw); 10362306a36Sopenharmony_ci kfree(cpumux); 10462306a36Sopenharmony_ci} 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_ciint mtk_clk_register_cpumuxes(struct device *dev, struct device_node *node, 10762306a36Sopenharmony_ci const struct mtk_composite *clks, int num, 10862306a36Sopenharmony_ci struct clk_hw_onecell_data *clk_data) 10962306a36Sopenharmony_ci{ 11062306a36Sopenharmony_ci int i; 11162306a36Sopenharmony_ci struct clk_hw *hw; 11262306a36Sopenharmony_ci struct regmap *regmap; 11362306a36Sopenharmony_ci 11462306a36Sopenharmony_ci regmap = device_node_to_regmap(node); 11562306a36Sopenharmony_ci if (IS_ERR(regmap)) { 11662306a36Sopenharmony_ci pr_err("Cannot find regmap for %pOF: %pe\n", node, regmap); 11762306a36Sopenharmony_ci return PTR_ERR(regmap); 11862306a36Sopenharmony_ci } 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_ci for (i = 0; i < num; i++) { 12162306a36Sopenharmony_ci const struct mtk_composite *mux = &clks[i]; 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_ci if (!IS_ERR_OR_NULL(clk_data->hws[mux->id])) { 12462306a36Sopenharmony_ci pr_warn("%pOF: Trying to register duplicate clock ID: %d\n", 12562306a36Sopenharmony_ci node, mux->id); 12662306a36Sopenharmony_ci continue; 12762306a36Sopenharmony_ci } 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_ci hw = mtk_clk_register_cpumux(dev, mux, regmap); 13062306a36Sopenharmony_ci if (IS_ERR(hw)) { 13162306a36Sopenharmony_ci pr_err("Failed to register clk %s: %pe\n", mux->name, 13262306a36Sopenharmony_ci hw); 13362306a36Sopenharmony_ci goto err; 13462306a36Sopenharmony_ci } 13562306a36Sopenharmony_ci 13662306a36Sopenharmony_ci clk_data->hws[mux->id] = hw; 13762306a36Sopenharmony_ci } 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_ci return 0; 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_cierr: 14262306a36Sopenharmony_ci while (--i >= 0) { 14362306a36Sopenharmony_ci const struct mtk_composite *mux = &clks[i]; 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_ci if (IS_ERR_OR_NULL(clk_data->hws[mux->id])) 14662306a36Sopenharmony_ci continue; 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_ci mtk_clk_unregister_cpumux(clk_data->hws[mux->id]); 14962306a36Sopenharmony_ci clk_data->hws[mux->id] = ERR_PTR(-ENOENT); 15062306a36Sopenharmony_ci } 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_ci return PTR_ERR(hw); 15362306a36Sopenharmony_ci} 15462306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(mtk_clk_register_cpumuxes); 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_civoid mtk_clk_unregister_cpumuxes(const struct mtk_composite *clks, int num, 15762306a36Sopenharmony_ci struct clk_hw_onecell_data *clk_data) 15862306a36Sopenharmony_ci{ 15962306a36Sopenharmony_ci int i; 16062306a36Sopenharmony_ci 16162306a36Sopenharmony_ci for (i = num; i > 0; i--) { 16262306a36Sopenharmony_ci const struct mtk_composite *mux = &clks[i - 1]; 16362306a36Sopenharmony_ci 16462306a36Sopenharmony_ci if (IS_ERR_OR_NULL(clk_data->hws[mux->id])) 16562306a36Sopenharmony_ci continue; 16662306a36Sopenharmony_ci 16762306a36Sopenharmony_ci mtk_clk_unregister_cpumux(clk_data->hws[mux->id]); 16862306a36Sopenharmony_ci clk_data->hws[mux->id] = ERR_PTR(-ENOENT); 16962306a36Sopenharmony_ci } 17062306a36Sopenharmony_ci} 17162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(mtk_clk_unregister_cpumuxes); 17262306a36Sopenharmony_ci 17362306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 174