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-fracn-gppll.c (7895B)


      1// SPDX-License-Identifier: GPL-2.0
      2/*
      3 * Copyright 2021 NXP
      4 */
      5
      6#include <linux/bitfield.h>
      7#include <linux/clk-provider.h>
      8#include <linux/err.h>
      9#include <linux/export.h>
     10#include <linux/io.h>
     11#include <linux/iopoll.h>
     12#include <linux/slab.h>
     13#include <asm/div64.h>
     14
     15#include "clk.h"
     16
     17#define PLL_CTRL		0x0
     18#define CLKMUX_BYPASS		BIT(2)
     19#define CLKMUX_EN		BIT(1)
     20#define POWERUP_MASK		BIT(0)
     21
     22#define PLL_ANA_PRG		0x10
     23#define PLL_SPREAD_SPECTRUM	0x30
     24
     25#define PLL_NUMERATOR		0x40
     26#define PLL_MFN_MASK		GENMASK(31, 2)
     27
     28#define PLL_DENOMINATOR		0x50
     29#define PLL_MFD_MASK		GENMASK(29, 0)
     30
     31#define PLL_DIV			0x60
     32#define PLL_MFI_MASK		GENMASK(24, 16)
     33#define PLL_RDIV_MASK		GENMASK(15, 13)
     34#define PLL_ODIV_MASK		GENMASK(7, 0)
     35
     36#define PLL_DFS_CTRL(x)		(0x70 + (x) * 0x10)
     37
     38#define PLL_STATUS		0xF0
     39#define LOCK_STATUS		BIT(0)
     40
     41#define DFS_STATUS		0xF4
     42
     43#define LOCK_TIMEOUT_US		200
     44
     45#define PLL_FRACN_GP(_rate, _mfi, _mfn, _mfd, _rdiv, _odiv)	\
     46	{							\
     47		.rate	=	(_rate),			\
     48		.mfi	=	(_mfi),				\
     49		.mfn	=	(_mfn),				\
     50		.mfd	=	(_mfd),				\
     51		.rdiv	=	(_rdiv),			\
     52		.odiv	=	(_odiv),			\
     53	}
     54
     55struct clk_fracn_gppll {
     56	struct clk_hw			hw;
     57	void __iomem			*base;
     58	const struct imx_fracn_gppll_rate_table *rate_table;
     59	int rate_count;
     60};
     61
     62/*
     63 * Fvco = Fref * (MFI + MFN / MFD)
     64 * Fout = Fvco / (rdiv * odiv)
     65 */
     66static const struct imx_fracn_gppll_rate_table fracn_tbl[] = {
     67	PLL_FRACN_GP(650000000U, 81, 0, 0, 0, 3),
     68	PLL_FRACN_GP(594000000U, 198, 0, 0, 0, 8),
     69	PLL_FRACN_GP(560000000U, 70, 0, 0, 0, 3),
     70	PLL_FRACN_GP(400000000U, 50, 0, 0, 0, 3),
     71	PLL_FRACN_GP(393216000U, 81, 92, 100, 0, 5)
     72};
     73
     74struct imx_fracn_gppll_clk imx_fracn_gppll = {
     75	.rate_table = fracn_tbl,
     76	.rate_count = ARRAY_SIZE(fracn_tbl),
     77};
     78EXPORT_SYMBOL_GPL(imx_fracn_gppll);
     79
     80static inline struct clk_fracn_gppll *to_clk_fracn_gppll(struct clk_hw *hw)
     81{
     82	return container_of(hw, struct clk_fracn_gppll, hw);
     83}
     84
     85static const struct imx_fracn_gppll_rate_table *
     86imx_get_pll_settings(struct clk_fracn_gppll *pll, unsigned long rate)
     87{
     88	const struct imx_fracn_gppll_rate_table *rate_table = pll->rate_table;
     89	int i;
     90
     91	for (i = 0; i < pll->rate_count; i++)
     92		if (rate == rate_table[i].rate)
     93			return &rate_table[i];
     94
     95	return NULL;
     96}
     97
     98static long clk_fracn_gppll_round_rate(struct clk_hw *hw, unsigned long rate,
     99				       unsigned long *prate)
    100{
    101	struct clk_fracn_gppll *pll = to_clk_fracn_gppll(hw);
    102	const struct imx_fracn_gppll_rate_table *rate_table = pll->rate_table;
    103	int i;
    104
    105	/* Assuming rate_table is in descending order */
    106	for (i = 0; i < pll->rate_count; i++)
    107		if (rate >= rate_table[i].rate)
    108			return rate_table[i].rate;
    109
    110	/* return minimum supported value */
    111	return rate_table[pll->rate_count - 1].rate;
    112}
    113
    114static unsigned long clk_fracn_gppll_recalc_rate(struct clk_hw *hw, unsigned long parent_rate)
    115{
    116	struct clk_fracn_gppll *pll = to_clk_fracn_gppll(hw);
    117	const struct imx_fracn_gppll_rate_table *rate_table = pll->rate_table;
    118	u32 pll_numerator, pll_denominator, pll_div;
    119	u32 mfi, mfn, mfd, rdiv, odiv;
    120	u64 fvco = parent_rate;
    121	long rate = 0;
    122	int i;
    123
    124	pll_numerator = readl_relaxed(pll->base + PLL_NUMERATOR);
    125	mfn = FIELD_GET(PLL_MFN_MASK, pll_numerator);
    126
    127	pll_denominator = readl_relaxed(pll->base + PLL_DENOMINATOR);
    128	mfd = FIELD_GET(PLL_MFD_MASK, pll_denominator);
    129
    130	pll_div = readl_relaxed(pll->base + PLL_DIV);
    131	mfi = FIELD_GET(PLL_MFI_MASK, pll_div);
    132
    133	rdiv = FIELD_GET(PLL_RDIV_MASK, pll_div);
    134	rdiv = rdiv + 1;
    135	odiv = FIELD_GET(PLL_ODIV_MASK, pll_div);
    136	switch (odiv) {
    137	case 0:
    138		odiv = 2;
    139		break;
    140	case 1:
    141		odiv = 3;
    142		break;
    143	default:
    144		break;
    145	}
    146
    147	/*
    148	 * Sometimes, the recalculated rate has deviation due to
    149	 * the frac part. So find the accurate pll rate from the table
    150	 * first, if no match rate in the table, use the rate calculated
    151	 * from the equation below.
    152	 */
    153	for (i = 0; i < pll->rate_count; i++) {
    154		if (rate_table[i].mfn == mfn && rate_table[i].mfi == mfi &&
    155		    rate_table[i].mfd == mfd && rate_table[i].rdiv == rdiv &&
    156		    rate_table[i].odiv == odiv)
    157			rate = rate_table[i].rate;
    158	}
    159
    160	if (rate)
    161		return (unsigned long)rate;
    162
    163	/* Fvco = Fref * (MFI + MFN / MFD) */
    164	fvco = fvco * mfi * mfd + fvco * mfn;
    165	do_div(fvco, mfd * rdiv * odiv);
    166
    167	return (unsigned long)fvco;
    168}
    169
    170static int clk_fracn_gppll_wait_lock(struct clk_fracn_gppll *pll)
    171{
    172	u32 val;
    173
    174	return readl_poll_timeout(pll->base + PLL_STATUS, val,
    175				  val & LOCK_STATUS, 0, LOCK_TIMEOUT_US);
    176}
    177
    178static int clk_fracn_gppll_set_rate(struct clk_hw *hw, unsigned long drate,
    179				    unsigned long prate)
    180{
    181	struct clk_fracn_gppll *pll = to_clk_fracn_gppll(hw);
    182	const struct imx_fracn_gppll_rate_table *rate;
    183	u32 tmp, pll_div, ana_mfn;
    184	int ret;
    185
    186	rate = imx_get_pll_settings(pll, drate);
    187
    188	/* Disable output */
    189	tmp = readl_relaxed(pll->base + PLL_CTRL);
    190	tmp &= ~CLKMUX_EN;
    191	writel_relaxed(tmp, pll->base + PLL_CTRL);
    192
    193	/* Power Down */
    194	tmp &= ~POWERUP_MASK;
    195	writel_relaxed(tmp, pll->base + PLL_CTRL);
    196
    197	/* Disable BYPASS */
    198	tmp &= ~CLKMUX_BYPASS;
    199	writel_relaxed(tmp, pll->base + PLL_CTRL);
    200
    201	pll_div = FIELD_PREP(PLL_RDIV_MASK, rate->rdiv) | rate->odiv |
    202		FIELD_PREP(PLL_MFI_MASK, rate->mfi);
    203	writel_relaxed(pll_div, pll->base + PLL_DIV);
    204	writel_relaxed(rate->mfd, pll->base + PLL_DENOMINATOR);
    205	writel_relaxed(FIELD_PREP(PLL_MFN_MASK, rate->mfn), pll->base + PLL_NUMERATOR);
    206
    207	/* Wait for 5us according to fracn mode pll doc */
    208	udelay(5);
    209
    210	/* Enable Powerup */
    211	tmp |= POWERUP_MASK;
    212	writel_relaxed(tmp, pll->base + PLL_CTRL);
    213
    214	/* Wait Lock */
    215	ret = clk_fracn_gppll_wait_lock(pll);
    216	if (ret)
    217		return ret;
    218
    219	/* Enable output */
    220	tmp |= CLKMUX_EN;
    221	writel_relaxed(tmp, pll->base + PLL_CTRL);
    222
    223	ana_mfn = readl_relaxed(pll->base + PLL_STATUS);
    224	ana_mfn = FIELD_GET(PLL_MFN_MASK, ana_mfn);
    225
    226	WARN(ana_mfn != rate->mfn, "ana_mfn != rate->mfn\n");
    227
    228	return 0;
    229}
    230
    231static int clk_fracn_gppll_prepare(struct clk_hw *hw)
    232{
    233	struct clk_fracn_gppll *pll = to_clk_fracn_gppll(hw);
    234	u32 val;
    235	int ret;
    236
    237	val = readl_relaxed(pll->base + PLL_CTRL);
    238	if (val & POWERUP_MASK)
    239		return 0;
    240
    241	val |= CLKMUX_BYPASS;
    242	writel_relaxed(val, pll->base + PLL_CTRL);
    243
    244	val |= POWERUP_MASK;
    245	writel_relaxed(val, pll->base + PLL_CTRL);
    246
    247	val |= CLKMUX_EN;
    248	writel_relaxed(val, pll->base + PLL_CTRL);
    249
    250	ret = clk_fracn_gppll_wait_lock(pll);
    251	if (ret)
    252		return ret;
    253
    254	val &= ~CLKMUX_BYPASS;
    255	writel_relaxed(val, pll->base + PLL_CTRL);
    256
    257	return 0;
    258}
    259
    260static int clk_fracn_gppll_is_prepared(struct clk_hw *hw)
    261{
    262	struct clk_fracn_gppll *pll = to_clk_fracn_gppll(hw);
    263	u32 val;
    264
    265	val = readl_relaxed(pll->base + PLL_CTRL);
    266
    267	return (val & POWERUP_MASK) ? 1 : 0;
    268}
    269
    270static void clk_fracn_gppll_unprepare(struct clk_hw *hw)
    271{
    272	struct clk_fracn_gppll *pll = to_clk_fracn_gppll(hw);
    273	u32 val;
    274
    275	val = readl_relaxed(pll->base + PLL_CTRL);
    276	val &= ~POWERUP_MASK;
    277	writel_relaxed(val, pll->base + PLL_CTRL);
    278}
    279
    280static const struct clk_ops clk_fracn_gppll_ops = {
    281	.prepare	= clk_fracn_gppll_prepare,
    282	.unprepare	= clk_fracn_gppll_unprepare,
    283	.is_prepared	= clk_fracn_gppll_is_prepared,
    284	.recalc_rate	= clk_fracn_gppll_recalc_rate,
    285	.round_rate	= clk_fracn_gppll_round_rate,
    286	.set_rate	= clk_fracn_gppll_set_rate,
    287};
    288
    289struct clk_hw *imx_clk_fracn_gppll(const char *name, const char *parent_name, void __iomem *base,
    290				   const struct imx_fracn_gppll_clk *pll_clk)
    291{
    292	struct clk_fracn_gppll *pll;
    293	struct clk_hw *hw;
    294	struct clk_init_data init;
    295	int ret;
    296
    297	pll = kzalloc(sizeof(*pll), GFP_KERNEL);
    298	if (!pll)
    299		return ERR_PTR(-ENOMEM);
    300
    301	init.name = name;
    302	init.flags = pll_clk->flags;
    303	init.parent_names = &parent_name;
    304	init.num_parents = 1;
    305	init.ops = &clk_fracn_gppll_ops;
    306
    307	pll->base = base;
    308	pll->hw.init = &init;
    309	pll->rate_table = pll_clk->rate_table;
    310	pll->rate_count = pll_clk->rate_count;
    311
    312	hw = &pll->hw;
    313
    314	ret = clk_hw_register(NULL, hw);
    315	if (ret) {
    316		pr_err("%s: failed to register pll %s %d\n", __func__, name, ret);
    317		kfree(pll);
    318		return ERR_PTR(ret);
    319	}
    320
    321	return hw;
    322}
    323EXPORT_SYMBOL_GPL(imx_clk_fracn_gppll);