|  | /* | 
|  | * Copyright (c) 2015 Linaro Ltd. | 
|  | * Author: Pi-Cheng Chen <pi-cheng.chen@linaro.org> | 
|  | * | 
|  | * This program is free software; you can redistribute it and/or modify | 
|  | * it under the terms of the GNU General Public License version 2 as | 
|  | * published by the Free Software Foundation. | 
|  | * | 
|  | * This program is distributed in the hope that it will be useful, | 
|  | * but WITHOUT ANY WARRANTY; without even the implied warranty of | 
|  | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | 
|  | * GNU General Public License for more details. | 
|  | */ | 
|  |  | 
|  | #include <linux/clk-provider.h> | 
|  | #include <linux/mfd/syscon.h> | 
|  | #include <linux/slab.h> | 
|  |  | 
|  | #include "clk-mtk.h" | 
|  | #include "clk-cpumux.h" | 
|  |  | 
|  | static inline struct mtk_clk_cpumux *to_mtk_clk_cpumux(struct clk_hw *_hw) | 
|  | { | 
|  | return container_of(_hw, struct mtk_clk_cpumux, hw); | 
|  | } | 
|  |  | 
|  | static u8 clk_cpumux_get_parent(struct clk_hw *hw) | 
|  | { | 
|  | struct mtk_clk_cpumux *mux = to_mtk_clk_cpumux(hw); | 
|  | unsigned int val; | 
|  |  | 
|  | regmap_read(mux->regmap, mux->reg, &val); | 
|  |  | 
|  | val >>= mux->shift; | 
|  | val &= mux->mask; | 
|  |  | 
|  | return val; | 
|  | } | 
|  |  | 
|  | static int clk_cpumux_set_parent(struct clk_hw *hw, u8 index) | 
|  | { | 
|  | struct mtk_clk_cpumux *mux = to_mtk_clk_cpumux(hw); | 
|  | u32 mask, val; | 
|  |  | 
|  | val = index << mux->shift; | 
|  | mask = mux->mask << mux->shift; | 
|  |  | 
|  | return regmap_update_bits(mux->regmap, mux->reg, mask, val); | 
|  | } | 
|  |  | 
|  | static const struct clk_ops clk_cpumux_ops = { | 
|  | .get_parent = clk_cpumux_get_parent, | 
|  | .set_parent = clk_cpumux_set_parent, | 
|  | }; | 
|  |  | 
|  | static struct clk * | 
|  | mtk_clk_register_cpumux(const struct mtk_composite *mux, | 
|  | struct regmap *regmap) | 
|  | { | 
|  | struct mtk_clk_cpumux *cpumux; | 
|  | struct clk *clk; | 
|  | struct clk_init_data init; | 
|  |  | 
|  | cpumux = kzalloc(sizeof(*cpumux), GFP_KERNEL); | 
|  | if (!cpumux) | 
|  | return ERR_PTR(-ENOMEM); | 
|  |  | 
|  | init.name = mux->name; | 
|  | init.ops = &clk_cpumux_ops; | 
|  | init.parent_names = mux->parent_names; | 
|  | init.num_parents = mux->num_parents; | 
|  | init.flags = mux->flags; | 
|  |  | 
|  | cpumux->reg = mux->mux_reg; | 
|  | cpumux->shift = mux->mux_shift; | 
|  | cpumux->mask = BIT(mux->mux_width) - 1; | 
|  | cpumux->regmap = regmap; | 
|  | cpumux->hw.init = &init; | 
|  |  | 
|  | clk = clk_register(NULL, &cpumux->hw); | 
|  | if (IS_ERR(clk)) | 
|  | kfree(cpumux); | 
|  |  | 
|  | return clk; | 
|  | } | 
|  |  | 
|  | int mtk_clk_register_cpumuxes(struct device_node *node, | 
|  | const struct mtk_composite *clks, int num, | 
|  | struct clk_onecell_data *clk_data) | 
|  | { | 
|  | int i; | 
|  | struct clk *clk; | 
|  | struct regmap *regmap; | 
|  |  | 
|  | regmap = syscon_node_to_regmap(node); | 
|  | if (IS_ERR(regmap)) { | 
|  | pr_err("Cannot find regmap for %pOF: %ld\n", node, | 
|  | PTR_ERR(regmap)); | 
|  | return PTR_ERR(regmap); | 
|  | } | 
|  |  | 
|  | for (i = 0; i < num; i++) { | 
|  | const struct mtk_composite *mux = &clks[i]; | 
|  |  | 
|  | clk = mtk_clk_register_cpumux(mux, regmap); | 
|  | if (IS_ERR(clk)) { | 
|  | pr_err("Failed to register clk %s: %ld\n", | 
|  | mux->name, PTR_ERR(clk)); | 
|  | continue; | 
|  | } | 
|  |  | 
|  | clk_data->clks[mux->id] = clk; | 
|  | } | 
|  |  | 
|  | return 0; | 
|  | } |