brintos

brintos / linux-shallow public Read only

0
0
Text · 6.4 KiB · fa817c3 Raw
286 lines · c
1// SPDX-License-Identifier: GPL-2.02/*3 * Copyright (C) 2011 Sascha Hauer, Pengutronix <s.hauer@pengutronix.de>4 * Copyright (C) 2011 Richard Zhao, Linaro <richard.zhao@linaro.org>5 * Copyright (C) 2011-2012 Mike Turquette, Linaro Ltd <mturquette@linaro.org>6 *7 * Simple multiplexer clock implementation8 */9 10#include <linux/clk-provider.h>11#include <linux/device.h>12#include <linux/module.h>13#include <linux/slab.h>14#include <linux/io.h>15#include <linux/err.h>16 17/*18 * DOC: basic adjustable multiplexer clock that cannot gate19 *20 * Traits of this clock:21 * prepare - clk_prepare only ensures that parents are prepared22 * enable - clk_enable only ensures that parents are enabled23 * rate - rate is only affected by parent switching.  No clk_set_rate support24 * parent - parent is adjustable through clk_set_parent25 */26 27static inline u32 clk_mux_readl(struct clk_mux *mux)28{29	if (mux->flags & CLK_MUX_BIG_ENDIAN)30		return ioread32be(mux->reg);31 32	return readl(mux->reg);33}34 35static inline void clk_mux_writel(struct clk_mux *mux, u32 val)36{37	if (mux->flags & CLK_MUX_BIG_ENDIAN)38		iowrite32be(val, mux->reg);39	else40		writel(val, mux->reg);41}42 43int clk_mux_val_to_index(struct clk_hw *hw, const u32 *table, unsigned int flags,44			 unsigned int val)45{46	int num_parents = clk_hw_get_num_parents(hw);47 48	if (table) {49		int i;50 51		for (i = 0; i < num_parents; i++)52			if (table[i] == val)53				return i;54		return -EINVAL;55	}56 57	if (val && (flags & CLK_MUX_INDEX_BIT))58		val = ffs(val) - 1;59 60	if (val && (flags & CLK_MUX_INDEX_ONE))61		val--;62 63	if (val >= num_parents)64		return -EINVAL;65 66	return val;67}68EXPORT_SYMBOL_GPL(clk_mux_val_to_index);69 70unsigned int clk_mux_index_to_val(const u32 *table, unsigned int flags, u8 index)71{72	unsigned int val = index;73 74	if (table) {75		val = table[index];76	} else {77		if (flags & CLK_MUX_INDEX_BIT)78			val = 1 << index;79 80		if (flags & CLK_MUX_INDEX_ONE)81			val++;82	}83 84	return val;85}86EXPORT_SYMBOL_GPL(clk_mux_index_to_val);87 88static u8 clk_mux_get_parent(struct clk_hw *hw)89{90	struct clk_mux *mux = to_clk_mux(hw);91	u32 val;92 93	val = clk_mux_readl(mux) >> mux->shift;94	val &= mux->mask;95 96	return clk_mux_val_to_index(hw, mux->table, mux->flags, val);97}98 99static int clk_mux_set_parent(struct clk_hw *hw, u8 index)100{101	struct clk_mux *mux = to_clk_mux(hw);102	u32 val = clk_mux_index_to_val(mux->table, mux->flags, index);103	unsigned long flags = 0;104	u32 reg;105 106	if (mux->lock)107		spin_lock_irqsave(mux->lock, flags);108	else109		__acquire(mux->lock);110 111	if (mux->flags & CLK_MUX_HIWORD_MASK) {112		reg = mux->mask << (mux->shift + 16);113	} else {114		reg = clk_mux_readl(mux);115		reg &= ~(mux->mask << mux->shift);116	}117	val = val << mux->shift;118	reg |= val;119	clk_mux_writel(mux, reg);120 121	if (mux->lock)122		spin_unlock_irqrestore(mux->lock, flags);123	else124		__release(mux->lock);125 126	return 0;127}128 129static int clk_mux_determine_rate(struct clk_hw *hw,130				  struct clk_rate_request *req)131{132	struct clk_mux *mux = to_clk_mux(hw);133 134	return clk_mux_determine_rate_flags(hw, req, mux->flags);135}136 137const struct clk_ops clk_mux_ops = {138	.get_parent = clk_mux_get_parent,139	.set_parent = clk_mux_set_parent,140	.determine_rate = clk_mux_determine_rate,141};142EXPORT_SYMBOL_GPL(clk_mux_ops);143 144const struct clk_ops clk_mux_ro_ops = {145	.get_parent = clk_mux_get_parent,146};147EXPORT_SYMBOL_GPL(clk_mux_ro_ops);148 149struct clk_hw *__clk_hw_register_mux(struct device *dev, struct device_node *np,150		const char *name, u8 num_parents,151		const char * const *parent_names,152		const struct clk_hw **parent_hws,153		const struct clk_parent_data *parent_data,154		unsigned long flags, void __iomem *reg, u8 shift, u32 mask,155		u8 clk_mux_flags, const u32 *table, spinlock_t *lock)156{157	struct clk_mux *mux;158	struct clk_hw *hw;159	struct clk_init_data init = {};160	int ret = -EINVAL;161 162	if (clk_mux_flags & CLK_MUX_HIWORD_MASK) {163		u8 width = fls(mask) - ffs(mask) + 1;164 165		if (width + shift > 16) {166			pr_err("mux value exceeds LOWORD field\n");167			return ERR_PTR(-EINVAL);168		}169	}170 171	/* allocate the mux */172	mux = kzalloc(sizeof(*mux), GFP_KERNEL);173	if (!mux)174		return ERR_PTR(-ENOMEM);175 176	init.name = name;177	if (clk_mux_flags & CLK_MUX_READ_ONLY)178		init.ops = &clk_mux_ro_ops;179	else180		init.ops = &clk_mux_ops;181	init.flags = flags;182	init.parent_names = parent_names;183	init.parent_data = parent_data;184	init.parent_hws = parent_hws;185	init.num_parents = num_parents;186 187	/* struct clk_mux assignments */188	mux->reg = reg;189	mux->shift = shift;190	mux->mask = mask;191	mux->flags = clk_mux_flags;192	mux->lock = lock;193	mux->table = table;194	mux->hw.init = &init;195 196	hw = &mux->hw;197	if (dev || !np)198		ret = clk_hw_register(dev, hw);199	else if (np)200		ret = of_clk_hw_register(np, hw);201	if (ret) {202		kfree(mux);203		hw = ERR_PTR(ret);204	}205 206	return hw;207}208EXPORT_SYMBOL_GPL(__clk_hw_register_mux);209 210static void devm_clk_hw_release_mux(struct device *dev, void *res)211{212	clk_hw_unregister_mux(*(struct clk_hw **)res);213}214 215struct clk_hw *__devm_clk_hw_register_mux(struct device *dev, struct device_node *np,216		const char *name, u8 num_parents,217		const char * const *parent_names,218		const struct clk_hw **parent_hws,219		const struct clk_parent_data *parent_data,220		unsigned long flags, void __iomem *reg, u8 shift, u32 mask,221		u8 clk_mux_flags, const u32 *table, spinlock_t *lock)222{223	struct clk_hw **ptr, *hw;224 225	ptr = devres_alloc(devm_clk_hw_release_mux, sizeof(*ptr), GFP_KERNEL);226	if (!ptr)227		return ERR_PTR(-ENOMEM);228 229	hw = __clk_hw_register_mux(dev, np, name, num_parents, parent_names, parent_hws,230				       parent_data, flags, reg, shift, mask,231				       clk_mux_flags, table, lock);232 233	if (!IS_ERR(hw)) {234		*ptr = hw;235		devres_add(dev, ptr);236	} else {237		devres_free(ptr);238	}239 240	return hw;241}242EXPORT_SYMBOL_GPL(__devm_clk_hw_register_mux);243 244struct clk *clk_register_mux_table(struct device *dev, const char *name,245		const char * const *parent_names, u8 num_parents,246		unsigned long flags, void __iomem *reg, u8 shift, u32 mask,247		u8 clk_mux_flags, const u32 *table, spinlock_t *lock)248{249	struct clk_hw *hw;250 251	hw = clk_hw_register_mux_table(dev, name, parent_names,252				       num_parents, flags, reg, shift, mask,253				       clk_mux_flags, table, lock);254	if (IS_ERR(hw))255		return ERR_CAST(hw);256	return hw->clk;257}258EXPORT_SYMBOL_GPL(clk_register_mux_table);259 260void clk_unregister_mux(struct clk *clk)261{262	struct clk_mux *mux;263	struct clk_hw *hw;264 265	hw = __clk_get_hw(clk);266	if (!hw)267		return;268 269	mux = to_clk_mux(hw);270 271	clk_unregister(clk);272	kfree(mux);273}274EXPORT_SYMBOL_GPL(clk_unregister_mux);275 276void clk_hw_unregister_mux(struct clk_hw *hw)277{278	struct clk_mux *mux;279 280	mux = to_clk_mux(hw);281 282	clk_hw_unregister(hw);283	kfree(mux);284}285EXPORT_SYMBOL_GPL(clk_hw_unregister_mux);286