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-pllv4.c (5680B)


      1// SPDX-License-Identifier: GPL-2.0+
      2/*
      3 * Copyright (C) 2016 Freescale Semiconductor, Inc.
      4 * Copyright 2017~2018 NXP
      5 *
      6 * Author: Dong Aisheng <aisheng.dong@nxp.com>
      7 *
      8 */
      9
     10#include <linux/bits.h>
     11#include <linux/clk-provider.h>
     12#include <linux/err.h>
     13#include <linux/io.h>
     14#include <linux/iopoll.h>
     15#include <linux/slab.h>
     16
     17#include "clk.h"
     18
     19/* PLL Control Status Register (xPLLCSR) */
     20#define PLL_CSR_OFFSET		0x0
     21#define PLL_VLD			BIT(24)
     22#define PLL_EN			BIT(0)
     23
     24/* PLL Configuration Register (xPLLCFG) */
     25#define PLL_CFG_OFFSET		0x08
     26#define IMX8ULP_PLL_CFG_OFFSET	0x10
     27#define BP_PLL_MULT		16
     28#define BM_PLL_MULT		(0x7f << 16)
     29
     30/* PLL Numerator Register (xPLLNUM) */
     31#define PLL_NUM_OFFSET		0x10
     32#define IMX8ULP_PLL_NUM_OFFSET	0x1c
     33
     34/* PLL Denominator Register (xPLLDENOM) */
     35#define PLL_DENOM_OFFSET	0x14
     36#define IMX8ULP_PLL_DENOM_OFFSET	0x18
     37
     38#define MAX_MFD			0x3fffffff
     39#define DEFAULT_MFD		1000000
     40
     41struct clk_pllv4 {
     42	struct clk_hw	hw;
     43	void __iomem	*base;
     44	u32		cfg_offset;
     45	u32		num_offset;
     46	u32		denom_offset;
     47};
     48
     49/* Valid PLL MULT Table */
     50static const int pllv4_mult_table[] = {33, 27, 22, 20, 17, 16};
     51
     52#define to_clk_pllv4(__hw) container_of(__hw, struct clk_pllv4, hw)
     53
     54#define LOCK_TIMEOUT_US		USEC_PER_MSEC
     55
     56static inline int clk_pllv4_wait_lock(struct clk_pllv4 *pll)
     57{
     58	u32 csr;
     59
     60	return readl_poll_timeout(pll->base  + PLL_CSR_OFFSET,
     61				  csr, csr & PLL_VLD, 0, LOCK_TIMEOUT_US);
     62}
     63
     64static int clk_pllv4_is_prepared(struct clk_hw *hw)
     65{
     66	struct clk_pllv4 *pll = to_clk_pllv4(hw);
     67
     68	if (readl_relaxed(pll->base) & PLL_EN)
     69		return 1;
     70
     71	return 0;
     72}
     73
     74static unsigned long clk_pllv4_recalc_rate(struct clk_hw *hw,
     75					   unsigned long parent_rate)
     76{
     77	struct clk_pllv4 *pll = to_clk_pllv4(hw);
     78	u32 mult, mfn, mfd;
     79	u64 temp64;
     80
     81	mult = readl_relaxed(pll->base + pll->cfg_offset);
     82	mult &= BM_PLL_MULT;
     83	mult >>= BP_PLL_MULT;
     84
     85	mfn = readl_relaxed(pll->base + pll->num_offset);
     86	mfd = readl_relaxed(pll->base + pll->denom_offset);
     87	temp64 = parent_rate;
     88	temp64 *= mfn;
     89	do_div(temp64, mfd);
     90
     91	return (parent_rate * mult) + (u32)temp64;
     92}
     93
     94static long clk_pllv4_round_rate(struct clk_hw *hw, unsigned long rate,
     95				 unsigned long *prate)
     96{
     97	unsigned long parent_rate = *prate;
     98	unsigned long round_rate, i;
     99	u32 mfn, mfd = DEFAULT_MFD;
    100	bool found = false;
    101	u64 temp64;
    102
    103	for (i = 0; i < ARRAY_SIZE(pllv4_mult_table); i++) {
    104		round_rate = parent_rate * pllv4_mult_table[i];
    105		if (rate >= round_rate) {
    106			found = true;
    107			break;
    108		}
    109	}
    110
    111	if (!found) {
    112		pr_warn("%s: unable to round rate %lu, parent rate %lu\n",
    113			clk_hw_get_name(hw), rate, parent_rate);
    114		return 0;
    115	}
    116
    117	if (parent_rate <= MAX_MFD)
    118		mfd = parent_rate;
    119
    120	temp64 = (u64)(rate - round_rate);
    121	temp64 *= mfd;
    122	do_div(temp64, parent_rate);
    123	mfn = temp64;
    124
    125	/*
    126	 * NOTE: The value of numerator must always be configured to be
    127	 * less than the value of the denominator. If we can't get a proper
    128	 * pair of mfn/mfd, we simply return the round_rate without using
    129	 * the frac part.
    130	 */
    131	if (mfn >= mfd)
    132		return round_rate;
    133
    134	temp64 = (u64)parent_rate;
    135	temp64 *= mfn;
    136	do_div(temp64, mfd);
    137
    138	return round_rate + (u32)temp64;
    139}
    140
    141static bool clk_pllv4_is_valid_mult(unsigned int mult)
    142{
    143	int i;
    144
    145	/* check if mult is in valid MULT table */
    146	for (i = 0; i < ARRAY_SIZE(pllv4_mult_table); i++) {
    147		if (pllv4_mult_table[i] == mult)
    148			return true;
    149	}
    150
    151	return false;
    152}
    153
    154static int clk_pllv4_set_rate(struct clk_hw *hw, unsigned long rate,
    155			      unsigned long parent_rate)
    156{
    157	struct clk_pllv4 *pll = to_clk_pllv4(hw);
    158	u32 val, mult, mfn, mfd = DEFAULT_MFD;
    159	u64 temp64;
    160
    161	mult = rate / parent_rate;
    162
    163	if (!clk_pllv4_is_valid_mult(mult))
    164		return -EINVAL;
    165
    166	if (parent_rate <= MAX_MFD)
    167		mfd = parent_rate;
    168
    169	temp64 = (u64)(rate - mult * parent_rate);
    170	temp64 *= mfd;
    171	do_div(temp64, parent_rate);
    172	mfn = temp64;
    173
    174	val = readl_relaxed(pll->base + pll->cfg_offset);
    175	val &= ~BM_PLL_MULT;
    176	val |= mult << BP_PLL_MULT;
    177	writel_relaxed(val, pll->base + pll->cfg_offset);
    178
    179	writel_relaxed(mfn, pll->base + pll->num_offset);
    180	writel_relaxed(mfd, pll->base + pll->denom_offset);
    181
    182	return 0;
    183}
    184
    185static int clk_pllv4_prepare(struct clk_hw *hw)
    186{
    187	u32 val;
    188	struct clk_pllv4 *pll = to_clk_pllv4(hw);
    189
    190	val = readl_relaxed(pll->base);
    191	val |= PLL_EN;
    192	writel_relaxed(val, pll->base);
    193
    194	return clk_pllv4_wait_lock(pll);
    195}
    196
    197static void clk_pllv4_unprepare(struct clk_hw *hw)
    198{
    199	u32 val;
    200	struct clk_pllv4 *pll = to_clk_pllv4(hw);
    201
    202	val = readl_relaxed(pll->base);
    203	val &= ~PLL_EN;
    204	writel_relaxed(val, pll->base);
    205}
    206
    207static const struct clk_ops clk_pllv4_ops = {
    208	.recalc_rate	= clk_pllv4_recalc_rate,
    209	.round_rate	= clk_pllv4_round_rate,
    210	.set_rate	= clk_pllv4_set_rate,
    211	.prepare	= clk_pllv4_prepare,
    212	.unprepare	= clk_pllv4_unprepare,
    213	.is_prepared	= clk_pllv4_is_prepared,
    214};
    215
    216struct clk_hw *imx_clk_hw_pllv4(enum imx_pllv4_type type, const char *name,
    217		 const char *parent_name, void __iomem *base)
    218{
    219	struct clk_pllv4 *pll;
    220	struct clk_hw *hw;
    221	struct clk_init_data init;
    222	int ret;
    223
    224	pll = kzalloc(sizeof(*pll), GFP_KERNEL);
    225	if (!pll)
    226		return ERR_PTR(-ENOMEM);
    227
    228	pll->base = base;
    229
    230	if (type == IMX_PLLV4_IMX8ULP) {
    231		pll->cfg_offset = IMX8ULP_PLL_CFG_OFFSET;
    232		pll->num_offset = IMX8ULP_PLL_NUM_OFFSET;
    233		pll->denom_offset = IMX8ULP_PLL_DENOM_OFFSET;
    234	} else {
    235		pll->cfg_offset = PLL_CFG_OFFSET;
    236		pll->num_offset = PLL_NUM_OFFSET;
    237		pll->denom_offset = PLL_DENOM_OFFSET;
    238	}
    239
    240	init.name = name;
    241	init.ops = &clk_pllv4_ops;
    242	init.parent_names = &parent_name;
    243	init.num_parents = 1;
    244	init.flags = CLK_SET_RATE_GATE;
    245
    246	pll->hw.init = &init;
    247
    248	hw = &pll->hw;
    249	ret = clk_hw_register(NULL, hw);
    250	if (ret) {
    251		kfree(pll);
    252		hw = ERR_PTR(ret);
    253	}
    254
    255	return hw;
    256}
    257EXPORT_SYMBOL_GPL(imx_clk_hw_pllv4);