327 lines · c
1// SPDX-License-Identifier: GPL-2.02/*3 * Copyright (c) 2018 MediaTek Inc.4 * Author: Owen Chen <owen.chen@mediatek.com>5 */6 7#include <linux/clk.h>8#include <linux/clk-provider.h>9#include <linux/compiler_types.h>10#include <linux/container_of.h>11#include <linux/err.h>12#include <linux/mfd/syscon.h>13#include <linux/module.h>14#include <linux/regmap.h>15#include <linux/spinlock.h>16#include <linux/slab.h>17 18#include "clk-mux.h"19 20struct mtk_clk_mux {21 struct clk_hw hw;22 struct regmap *regmap;23 const struct mtk_mux *data;24 spinlock_t *lock;25 bool reparent;26};27 28static inline struct mtk_clk_mux *to_mtk_clk_mux(struct clk_hw *hw)29{30 return container_of(hw, struct mtk_clk_mux, hw);31}32 33static int mtk_clk_mux_enable_setclr(struct clk_hw *hw)34{35 struct mtk_clk_mux *mux = to_mtk_clk_mux(hw);36 unsigned long flags = 0;37 38 if (mux->lock)39 spin_lock_irqsave(mux->lock, flags);40 else41 __acquire(mux->lock);42 43 regmap_write(mux->regmap, mux->data->clr_ofs,44 BIT(mux->data->gate_shift));45 46 /*47 * If the parent has been changed when the clock was disabled, it will48 * not be effective yet. Set the update bit to ensure the mux gets49 * updated.50 */51 if (mux->reparent && mux->data->upd_shift >= 0) {52 regmap_write(mux->regmap, mux->data->upd_ofs,53 BIT(mux->data->upd_shift));54 mux->reparent = false;55 }56 57 if (mux->lock)58 spin_unlock_irqrestore(mux->lock, flags);59 else60 __release(mux->lock);61 62 return 0;63}64 65static void mtk_clk_mux_disable_setclr(struct clk_hw *hw)66{67 struct mtk_clk_mux *mux = to_mtk_clk_mux(hw);68 69 regmap_write(mux->regmap, mux->data->set_ofs,70 BIT(mux->data->gate_shift));71}72 73static int mtk_clk_mux_is_enabled(struct clk_hw *hw)74{75 struct mtk_clk_mux *mux = to_mtk_clk_mux(hw);76 u32 val;77 78 regmap_read(mux->regmap, mux->data->mux_ofs, &val);79 80 return (val & BIT(mux->data->gate_shift)) == 0;81}82 83static u8 mtk_clk_mux_get_parent(struct clk_hw *hw)84{85 struct mtk_clk_mux *mux = to_mtk_clk_mux(hw);86 u32 mask = GENMASK(mux->data->mux_width - 1, 0);87 u32 val;88 89 regmap_read(mux->regmap, mux->data->mux_ofs, &val);90 val = (val >> mux->data->mux_shift) & mask;91 92 if (mux->data->parent_index) {93 int i;94 95 for (i = 0; i < mux->data->num_parents; i++)96 if (mux->data->parent_index[i] == val)97 return i;98 99 /* Not found: return an impossible index to generate error */100 return mux->data->num_parents + 1;101 }102 103 return val;104}105 106static int mtk_clk_mux_set_parent_setclr_lock(struct clk_hw *hw, u8 index)107{108 struct mtk_clk_mux *mux = to_mtk_clk_mux(hw);109 u32 mask = GENMASK(mux->data->mux_width - 1, 0);110 u32 val, orig;111 unsigned long flags = 0;112 113 if (mux->lock)114 spin_lock_irqsave(mux->lock, flags);115 else116 __acquire(mux->lock);117 118 if (mux->data->parent_index)119 index = mux->data->parent_index[index];120 121 regmap_read(mux->regmap, mux->data->mux_ofs, &orig);122 val = (orig & ~(mask << mux->data->mux_shift))123 | (index << mux->data->mux_shift);124 125 if (val != orig) {126 regmap_write(mux->regmap, mux->data->clr_ofs,127 mask << mux->data->mux_shift);128 regmap_write(mux->regmap, mux->data->set_ofs,129 index << mux->data->mux_shift);130 131 if (mux->data->upd_shift >= 0) {132 regmap_write(mux->regmap, mux->data->upd_ofs,133 BIT(mux->data->upd_shift));134 mux->reparent = true;135 }136 }137 138 if (mux->lock)139 spin_unlock_irqrestore(mux->lock, flags);140 else141 __release(mux->lock);142 143 return 0;144}145 146static int mtk_clk_mux_determine_rate(struct clk_hw *hw,147 struct clk_rate_request *req)148{149 struct mtk_clk_mux *mux = to_mtk_clk_mux(hw);150 151 return clk_mux_determine_rate_flags(hw, req, mux->data->flags);152}153 154const struct clk_ops mtk_mux_clr_set_upd_ops = {155 .get_parent = mtk_clk_mux_get_parent,156 .set_parent = mtk_clk_mux_set_parent_setclr_lock,157 .determine_rate = mtk_clk_mux_determine_rate,158};159EXPORT_SYMBOL_GPL(mtk_mux_clr_set_upd_ops);160 161const struct clk_ops mtk_mux_gate_clr_set_upd_ops = {162 .enable = mtk_clk_mux_enable_setclr,163 .disable = mtk_clk_mux_disable_setclr,164 .is_enabled = mtk_clk_mux_is_enabled,165 .get_parent = mtk_clk_mux_get_parent,166 .set_parent = mtk_clk_mux_set_parent_setclr_lock,167 .determine_rate = mtk_clk_mux_determine_rate,168};169EXPORT_SYMBOL_GPL(mtk_mux_gate_clr_set_upd_ops);170 171static struct clk_hw *mtk_clk_register_mux(struct device *dev,172 const struct mtk_mux *mux,173 struct regmap *regmap,174 spinlock_t *lock)175{176 struct mtk_clk_mux *clk_mux;177 struct clk_init_data init = {};178 int ret;179 180 clk_mux = kzalloc(sizeof(*clk_mux), GFP_KERNEL);181 if (!clk_mux)182 return ERR_PTR(-ENOMEM);183 184 init.name = mux->name;185 init.flags = mux->flags;186 init.parent_names = mux->parent_names;187 init.num_parents = mux->num_parents;188 init.ops = mux->ops;189 190 clk_mux->regmap = regmap;191 clk_mux->data = mux;192 clk_mux->lock = lock;193 clk_mux->hw.init = &init;194 195 ret = clk_hw_register(dev, &clk_mux->hw);196 if (ret) {197 kfree(clk_mux);198 return ERR_PTR(ret);199 }200 201 return &clk_mux->hw;202}203 204static void mtk_clk_unregister_mux(struct clk_hw *hw)205{206 struct mtk_clk_mux *mux;207 if (!hw)208 return;209 210 mux = to_mtk_clk_mux(hw);211 212 clk_hw_unregister(hw);213 kfree(mux);214}215 216int mtk_clk_register_muxes(struct device *dev,217 const struct mtk_mux *muxes,218 int num, struct device_node *node,219 spinlock_t *lock,220 struct clk_hw_onecell_data *clk_data)221{222 struct regmap *regmap;223 struct clk_hw *hw;224 int i;225 226 regmap = device_node_to_regmap(node);227 if (IS_ERR(regmap)) {228 pr_err("Cannot find regmap for %pOF: %pe\n", node, regmap);229 return PTR_ERR(regmap);230 }231 232 for (i = 0; i < num; i++) {233 const struct mtk_mux *mux = &muxes[i];234 235 if (!IS_ERR_OR_NULL(clk_data->hws[mux->id])) {236 pr_warn("%pOF: Trying to register duplicate clock ID: %d\n",237 node, mux->id);238 continue;239 }240 241 hw = mtk_clk_register_mux(dev, mux, regmap, lock);242 243 if (IS_ERR(hw)) {244 pr_err("Failed to register clk %s: %pe\n", mux->name,245 hw);246 goto err;247 }248 249 clk_data->hws[mux->id] = hw;250 }251 252 return 0;253 254err:255 while (--i >= 0) {256 const struct mtk_mux *mux = &muxes[i];257 258 if (IS_ERR_OR_NULL(clk_data->hws[mux->id]))259 continue;260 261 mtk_clk_unregister_mux(clk_data->hws[mux->id]);262 clk_data->hws[mux->id] = ERR_PTR(-ENOENT);263 }264 265 return PTR_ERR(hw);266}267EXPORT_SYMBOL_GPL(mtk_clk_register_muxes);268 269void mtk_clk_unregister_muxes(const struct mtk_mux *muxes, int num,270 struct clk_hw_onecell_data *clk_data)271{272 int i;273 274 if (!clk_data)275 return;276 277 for (i = num; i > 0; i--) {278 const struct mtk_mux *mux = &muxes[i - 1];279 280 if (IS_ERR_OR_NULL(clk_data->hws[mux->id]))281 continue;282 283 mtk_clk_unregister_mux(clk_data->hws[mux->id]);284 clk_data->hws[mux->id] = ERR_PTR(-ENOENT);285 }286}287EXPORT_SYMBOL_GPL(mtk_clk_unregister_muxes);288 289/*290 * This clock notifier is called when the frequency of the parent291 * PLL clock is to be changed. The idea is to switch the parent to a292 * stable clock, such as the main oscillator, while the PLL frequency293 * stabilizes.294 */295static int mtk_clk_mux_notifier_cb(struct notifier_block *nb,296 unsigned long event, void *_data)297{298 struct clk_notifier_data *data = _data;299 struct clk_hw *hw = __clk_get_hw(data->clk);300 struct mtk_mux_nb *mux_nb = to_mtk_mux_nb(nb);301 int ret = 0;302 303 switch (event) {304 case PRE_RATE_CHANGE:305 mux_nb->original_index = mux_nb->ops->get_parent(hw);306 ret = mux_nb->ops->set_parent(hw, mux_nb->bypass_index);307 break;308 case POST_RATE_CHANGE:309 case ABORT_RATE_CHANGE:310 ret = mux_nb->ops->set_parent(hw, mux_nb->original_index);311 break;312 }313 314 return notifier_from_errno(ret);315}316 317int devm_mtk_clk_mux_notifier_register(struct device *dev, struct clk *clk,318 struct mtk_mux_nb *mux_nb)319{320 mux_nb->nb.notifier_call = mtk_clk_mux_notifier_cb;321 322 return devm_clk_notifier_register(dev, clk, &mux_nb->nb);323}324EXPORT_SYMBOL_GPL(devm_mtk_clk_mux_notifier_register);325 326MODULE_LICENSE("GPL");327