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-composite-8m.c (6089B)


      1// SPDX-License-Identifier: GPL-2.0
      2/*
      3 * Copyright 2018 NXP
      4 */
      5
      6#include <linux/clk-provider.h>
      7#include <linux/errno.h>
      8#include <linux/export.h>
      9#include <linux/io.h>
     10#include <linux/slab.h>
     11
     12#include "clk.h"
     13
     14#define PCG_PREDIV_SHIFT	16
     15#define PCG_PREDIV_WIDTH	3
     16#define PCG_PREDIV_MAX		8
     17
     18#define PCG_DIV_SHIFT		0
     19#define PCG_CORE_DIV_WIDTH	3
     20#define PCG_DIV_WIDTH		6
     21#define PCG_DIV_MAX		64
     22
     23#define PCG_PCS_SHIFT		24
     24#define PCG_PCS_MASK		0x7
     25
     26#define PCG_CGC_SHIFT		28
     27
     28static unsigned long imx8m_clk_composite_divider_recalc_rate(struct clk_hw *hw,
     29						unsigned long parent_rate)
     30{
     31	struct clk_divider *divider = to_clk_divider(hw);
     32	unsigned long prediv_rate;
     33	unsigned int prediv_value;
     34	unsigned int div_value;
     35
     36	prediv_value = readl(divider->reg) >> divider->shift;
     37	prediv_value &= clk_div_mask(divider->width);
     38
     39	prediv_rate = divider_recalc_rate(hw, parent_rate, prediv_value,
     40						NULL, divider->flags,
     41						divider->width);
     42
     43	div_value = readl(divider->reg) >> PCG_DIV_SHIFT;
     44	div_value &= clk_div_mask(PCG_DIV_WIDTH);
     45
     46	return divider_recalc_rate(hw, prediv_rate, div_value, NULL,
     47				   divider->flags, PCG_DIV_WIDTH);
     48}
     49
     50static int imx8m_clk_composite_compute_dividers(unsigned long rate,
     51						unsigned long parent_rate,
     52						int *prediv, int *postdiv)
     53{
     54	int div1, div2;
     55	int error = INT_MAX;
     56	int ret = -EINVAL;
     57
     58	*prediv = 1;
     59	*postdiv = 1;
     60
     61	for (div1 = 1; div1 <= PCG_PREDIV_MAX; div1++) {
     62		for (div2 = 1; div2 <= PCG_DIV_MAX; div2++) {
     63			int new_error = ((parent_rate / div1) / div2) - rate;
     64
     65			if (abs(new_error) < abs(error)) {
     66				*prediv = div1;
     67				*postdiv = div2;
     68				error = new_error;
     69				ret = 0;
     70			}
     71		}
     72	}
     73	return ret;
     74}
     75
     76static long imx8m_clk_composite_divider_round_rate(struct clk_hw *hw,
     77						unsigned long rate,
     78						unsigned long *prate)
     79{
     80	int prediv_value;
     81	int div_value;
     82
     83	imx8m_clk_composite_compute_dividers(rate, *prate,
     84						&prediv_value, &div_value);
     85	rate = DIV_ROUND_UP(*prate, prediv_value);
     86
     87	return DIV_ROUND_UP(rate, div_value);
     88
     89}
     90
     91static int imx8m_clk_composite_divider_set_rate(struct clk_hw *hw,
     92					unsigned long rate,
     93					unsigned long parent_rate)
     94{
     95	struct clk_divider *divider = to_clk_divider(hw);
     96	unsigned long flags;
     97	int prediv_value;
     98	int div_value;
     99	int ret;
    100	u32 val;
    101
    102	ret = imx8m_clk_composite_compute_dividers(rate, parent_rate,
    103						&prediv_value, &div_value);
    104	if (ret)
    105		return -EINVAL;
    106
    107	spin_lock_irqsave(divider->lock, flags);
    108
    109	val = readl(divider->reg);
    110	val &= ~((clk_div_mask(divider->width) << divider->shift) |
    111			(clk_div_mask(PCG_DIV_WIDTH) << PCG_DIV_SHIFT));
    112
    113	val |= (u32)(prediv_value  - 1) << divider->shift;
    114	val |= (u32)(div_value - 1) << PCG_DIV_SHIFT;
    115	writel(val, divider->reg);
    116
    117	spin_unlock_irqrestore(divider->lock, flags);
    118
    119	return ret;
    120}
    121
    122static const struct clk_ops imx8m_clk_composite_divider_ops = {
    123	.recalc_rate = imx8m_clk_composite_divider_recalc_rate,
    124	.round_rate = imx8m_clk_composite_divider_round_rate,
    125	.set_rate = imx8m_clk_composite_divider_set_rate,
    126};
    127
    128static u8 imx8m_clk_composite_mux_get_parent(struct clk_hw *hw)
    129{
    130	return clk_mux_ops.get_parent(hw);
    131}
    132
    133static int imx8m_clk_composite_mux_set_parent(struct clk_hw *hw, u8 index)
    134{
    135	struct clk_mux *mux = to_clk_mux(hw);
    136	u32 val = clk_mux_index_to_val(mux->table, mux->flags, index);
    137	unsigned long flags = 0;
    138	u32 reg;
    139
    140	if (mux->lock)
    141		spin_lock_irqsave(mux->lock, flags);
    142
    143	reg = readl(mux->reg);
    144	reg &= ~(mux->mask << mux->shift);
    145	val = val << mux->shift;
    146	reg |= val;
    147	/*
    148	 * write twice to make sure non-target interface
    149	 * SEL_A/B point the same clk input.
    150	 */
    151	writel(reg, mux->reg);
    152	writel(reg, mux->reg);
    153
    154	if (mux->lock)
    155		spin_unlock_irqrestore(mux->lock, flags);
    156
    157	return 0;
    158}
    159
    160static int
    161imx8m_clk_composite_mux_determine_rate(struct clk_hw *hw,
    162				       struct clk_rate_request *req)
    163{
    164	return clk_mux_ops.determine_rate(hw, req);
    165}
    166
    167
    168static const struct clk_ops imx8m_clk_composite_mux_ops = {
    169	.get_parent = imx8m_clk_composite_mux_get_parent,
    170	.set_parent = imx8m_clk_composite_mux_set_parent,
    171	.determine_rate = imx8m_clk_composite_mux_determine_rate,
    172};
    173
    174struct clk_hw *__imx8m_clk_hw_composite(const char *name,
    175					const char * const *parent_names,
    176					int num_parents, void __iomem *reg,
    177					u32 composite_flags,
    178					unsigned long flags)
    179{
    180	struct clk_hw *hw = ERR_PTR(-ENOMEM), *mux_hw;
    181	struct clk_hw *div_hw, *gate_hw = NULL;
    182	struct clk_divider *div = NULL;
    183	struct clk_gate *gate = NULL;
    184	struct clk_mux *mux = NULL;
    185	const struct clk_ops *divider_ops;
    186	const struct clk_ops *mux_ops;
    187
    188	mux = kzalloc(sizeof(*mux), GFP_KERNEL);
    189	if (!mux)
    190		goto fail;
    191
    192	mux_hw = &mux->hw;
    193	mux->reg = reg;
    194	mux->shift = PCG_PCS_SHIFT;
    195	mux->mask = PCG_PCS_MASK;
    196	mux->lock = &imx_ccm_lock;
    197
    198	div = kzalloc(sizeof(*div), GFP_KERNEL);
    199	if (!div)
    200		goto fail;
    201
    202	div_hw = &div->hw;
    203	div->reg = reg;
    204	if (composite_flags & IMX_COMPOSITE_CORE) {
    205		div->shift = PCG_DIV_SHIFT;
    206		div->width = PCG_CORE_DIV_WIDTH;
    207		divider_ops = &clk_divider_ops;
    208		mux_ops = &imx8m_clk_composite_mux_ops;
    209	} else if (composite_flags & IMX_COMPOSITE_BUS) {
    210		div->shift = PCG_PREDIV_SHIFT;
    211		div->width = PCG_PREDIV_WIDTH;
    212		divider_ops = &imx8m_clk_composite_divider_ops;
    213		mux_ops = &imx8m_clk_composite_mux_ops;
    214	} else {
    215		div->shift = PCG_PREDIV_SHIFT;
    216		div->width = PCG_PREDIV_WIDTH;
    217		divider_ops = &imx8m_clk_composite_divider_ops;
    218		mux_ops = &clk_mux_ops;
    219		if (!(composite_flags & IMX_COMPOSITE_FW_MANAGED))
    220			flags |= CLK_SET_PARENT_GATE;
    221	}
    222
    223	div->lock = &imx_ccm_lock;
    224	div->flags = CLK_DIVIDER_ROUND_CLOSEST;
    225
    226	/* skip registering the gate ops if M4 is enabled */
    227	if (!mcore_booted) {
    228		gate = kzalloc(sizeof(*gate), GFP_KERNEL);
    229		if (!gate)
    230			goto fail;
    231
    232		gate_hw = &gate->hw;
    233		gate->reg = reg;
    234		gate->bit_idx = PCG_CGC_SHIFT;
    235		gate->lock = &imx_ccm_lock;
    236	}
    237
    238	hw = clk_hw_register_composite(NULL, name, parent_names, num_parents,
    239			mux_hw, mux_ops, div_hw,
    240			divider_ops, gate_hw, &clk_gate_ops, flags);
    241	if (IS_ERR(hw))
    242		goto fail;
    243
    244	return hw;
    245
    246fail:
    247	kfree(gate);
    248	kfree(div);
    249	kfree(mux);
    250	return ERR_CAST(hw);
    251}
    252EXPORT_SYMBOL_GPL(__imx8m_clk_hw_composite);