brintos

brintos / linux-shallow public Read only

0
0
Text · 7.5 KiB · 6099029 Raw
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