cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

clk-mux.c (5879B)


      1// SPDX-License-Identifier: GPL-2.0
      2/*
      3 * Copyright (c) 2018 MediaTek Inc.
      4 * Author: Owen Chen <owen.chen@mediatek.com>
      5 */
      6
      7#include <linux/clk-provider.h>
      8#include <linux/compiler_types.h>
      9#include <linux/container_of.h>
     10#include <linux/err.h>
     11#include <linux/mfd/syscon.h>
     12#include <linux/module.h>
     13#include <linux/regmap.h>
     14#include <linux/spinlock.h>
     15#include <linux/slab.h>
     16
     17#include "clk-mux.h"
     18
     19struct mtk_clk_mux {
     20	struct clk_hw hw;
     21	struct regmap *regmap;
     22	const struct mtk_mux *data;
     23	spinlock_t *lock;
     24	bool reparent;
     25};
     26
     27static inline struct mtk_clk_mux *to_mtk_clk_mux(struct clk_hw *hw)
     28{
     29	return container_of(hw, struct mtk_clk_mux, hw);
     30}
     31
     32static int mtk_clk_mux_enable_setclr(struct clk_hw *hw)
     33{
     34	struct mtk_clk_mux *mux = to_mtk_clk_mux(hw);
     35	unsigned long flags = 0;
     36
     37	if (mux->lock)
     38		spin_lock_irqsave(mux->lock, flags);
     39	else
     40		__acquire(mux->lock);
     41
     42	regmap_write(mux->regmap, mux->data->clr_ofs,
     43		     BIT(mux->data->gate_shift));
     44
     45	/*
     46	 * If the parent has been changed when the clock was disabled, it will
     47	 * not be effective yet. Set the update bit to ensure the mux gets
     48	 * updated.
     49	 */
     50	if (mux->reparent && mux->data->upd_shift >= 0) {
     51		regmap_write(mux->regmap, mux->data->upd_ofs,
     52			     BIT(mux->data->upd_shift));
     53		mux->reparent = false;
     54	}
     55
     56	if (mux->lock)
     57		spin_unlock_irqrestore(mux->lock, flags);
     58	else
     59		__release(mux->lock);
     60
     61	return 0;
     62}
     63
     64static void mtk_clk_mux_disable_setclr(struct clk_hw *hw)
     65{
     66	struct mtk_clk_mux *mux = to_mtk_clk_mux(hw);
     67
     68	regmap_write(mux->regmap, mux->data->set_ofs,
     69			BIT(mux->data->gate_shift));
     70}
     71
     72static int mtk_clk_mux_is_enabled(struct clk_hw *hw)
     73{
     74	struct mtk_clk_mux *mux = to_mtk_clk_mux(hw);
     75	u32 val;
     76
     77	regmap_read(mux->regmap, mux->data->mux_ofs, &val);
     78
     79	return (val & BIT(mux->data->gate_shift)) == 0;
     80}
     81
     82static u8 mtk_clk_mux_get_parent(struct clk_hw *hw)
     83{
     84	struct mtk_clk_mux *mux = to_mtk_clk_mux(hw);
     85	u32 mask = GENMASK(mux->data->mux_width - 1, 0);
     86	u32 val;
     87
     88	regmap_read(mux->regmap, mux->data->mux_ofs, &val);
     89	val = (val >> mux->data->mux_shift) & mask;
     90
     91	return val;
     92}
     93
     94static int mtk_clk_mux_set_parent_setclr_lock(struct clk_hw *hw, u8 index)
     95{
     96	struct mtk_clk_mux *mux = to_mtk_clk_mux(hw);
     97	u32 mask = GENMASK(mux->data->mux_width - 1, 0);
     98	u32 val, orig;
     99	unsigned long flags = 0;
    100
    101	if (mux->lock)
    102		spin_lock_irqsave(mux->lock, flags);
    103	else
    104		__acquire(mux->lock);
    105
    106	regmap_read(mux->regmap, mux->data->mux_ofs, &orig);
    107	val = (orig & ~(mask << mux->data->mux_shift))
    108			| (index << mux->data->mux_shift);
    109
    110	if (val != orig) {
    111		regmap_write(mux->regmap, mux->data->clr_ofs,
    112				mask << mux->data->mux_shift);
    113		regmap_write(mux->regmap, mux->data->set_ofs,
    114				index << mux->data->mux_shift);
    115
    116		if (mux->data->upd_shift >= 0) {
    117			regmap_write(mux->regmap, mux->data->upd_ofs,
    118					BIT(mux->data->upd_shift));
    119			mux->reparent = true;
    120		}
    121	}
    122
    123	if (mux->lock)
    124		spin_unlock_irqrestore(mux->lock, flags);
    125	else
    126		__release(mux->lock);
    127
    128	return 0;
    129}
    130
    131const struct clk_ops mtk_mux_clr_set_upd_ops = {
    132	.get_parent = mtk_clk_mux_get_parent,
    133	.set_parent = mtk_clk_mux_set_parent_setclr_lock,
    134};
    135EXPORT_SYMBOL_GPL(mtk_mux_clr_set_upd_ops);
    136
    137const struct clk_ops mtk_mux_gate_clr_set_upd_ops  = {
    138	.enable = mtk_clk_mux_enable_setclr,
    139	.disable = mtk_clk_mux_disable_setclr,
    140	.is_enabled = mtk_clk_mux_is_enabled,
    141	.get_parent = mtk_clk_mux_get_parent,
    142	.set_parent = mtk_clk_mux_set_parent_setclr_lock,
    143};
    144EXPORT_SYMBOL_GPL(mtk_mux_gate_clr_set_upd_ops);
    145
    146static struct clk_hw *mtk_clk_register_mux(const struct mtk_mux *mux,
    147				 struct regmap *regmap,
    148				 spinlock_t *lock)
    149{
    150	struct mtk_clk_mux *clk_mux;
    151	struct clk_init_data init = {};
    152	int ret;
    153
    154	clk_mux = kzalloc(sizeof(*clk_mux), GFP_KERNEL);
    155	if (!clk_mux)
    156		return ERR_PTR(-ENOMEM);
    157
    158	init.name = mux->name;
    159	init.flags = mux->flags | CLK_SET_RATE_PARENT;
    160	init.parent_names = mux->parent_names;
    161	init.num_parents = mux->num_parents;
    162	init.ops = mux->ops;
    163
    164	clk_mux->regmap = regmap;
    165	clk_mux->data = mux;
    166	clk_mux->lock = lock;
    167	clk_mux->hw.init = &init;
    168
    169	ret = clk_hw_register(NULL, &clk_mux->hw);
    170	if (ret) {
    171		kfree(clk_mux);
    172		return ERR_PTR(ret);
    173	}
    174
    175	return &clk_mux->hw;
    176}
    177
    178static void mtk_clk_unregister_mux(struct clk_hw *hw)
    179{
    180	struct mtk_clk_mux *mux;
    181	if (!hw)
    182		return;
    183
    184	mux = to_mtk_clk_mux(hw);
    185
    186	clk_hw_unregister(hw);
    187	kfree(mux);
    188}
    189
    190int mtk_clk_register_muxes(const struct mtk_mux *muxes,
    191			   int num, struct device_node *node,
    192			   spinlock_t *lock,
    193			   struct clk_hw_onecell_data *clk_data)
    194{
    195	struct regmap *regmap;
    196	struct clk_hw *hw;
    197	int i;
    198
    199	regmap = device_node_to_regmap(node);
    200	if (IS_ERR(regmap)) {
    201		pr_err("Cannot find regmap for %pOF: %pe\n", node, regmap);
    202		return PTR_ERR(regmap);
    203	}
    204
    205	for (i = 0; i < num; i++) {
    206		const struct mtk_mux *mux = &muxes[i];
    207
    208		if (!IS_ERR_OR_NULL(clk_data->hws[mux->id])) {
    209			pr_warn("%pOF: Trying to register duplicate clock ID: %d\n",
    210				node, mux->id);
    211			continue;
    212		}
    213
    214		hw = mtk_clk_register_mux(mux, regmap, lock);
    215
    216		if (IS_ERR(hw)) {
    217			pr_err("Failed to register clk %s: %pe\n", mux->name,
    218			       hw);
    219			goto err;
    220		}
    221
    222		clk_data->hws[mux->id] = hw;
    223	}
    224
    225	return 0;
    226
    227err:
    228	while (--i >= 0) {
    229		const struct mtk_mux *mux = &muxes[i];
    230
    231		if (IS_ERR_OR_NULL(clk_data->hws[mux->id]))
    232			continue;
    233
    234		mtk_clk_unregister_mux(clk_data->hws[mux->id]);
    235		clk_data->hws[mux->id] = ERR_PTR(-ENOENT);
    236	}
    237
    238	return PTR_ERR(hw);
    239}
    240EXPORT_SYMBOL_GPL(mtk_clk_register_muxes);
    241
    242void mtk_clk_unregister_muxes(const struct mtk_mux *muxes, int num,
    243			      struct clk_hw_onecell_data *clk_data)
    244{
    245	int i;
    246
    247	if (!clk_data)
    248		return;
    249
    250	for (i = num; i > 0; i--) {
    251		const struct mtk_mux *mux = &muxes[i - 1];
    252
    253		if (IS_ERR_OR_NULL(clk_data->hws[mux->id]))
    254			continue;
    255
    256		mtk_clk_unregister_mux(clk_data->hws[mux->id]);
    257		clk_data->hws[mux->id] = ERR_PTR(-ENOENT);
    258	}
    259}
    260EXPORT_SYMBOL_GPL(mtk_clk_unregister_muxes);
    261
    262MODULE_LICENSE("GPL");