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-cpu.c (15512B)


      1// SPDX-License-Identifier: GPL-2.0-only
      2/*
      3 * Copyright (c) 2014 Samsung Electronics Co., Ltd.
      4 * Author: Thomas Abraham <thomas.ab@samsung.com>
      5 *
      6 * Copyright (c) 2015 Samsung Electronics Co., Ltd.
      7 * Bartlomiej Zolnierkiewicz <b.zolnierkie@samsung.com>
      8 *
      9 * This file contains the utility function to register CPU clock for Samsung
     10 * Exynos platforms. A CPU clock is defined as a clock supplied to a CPU or a
     11 * group of CPUs. The CPU clock is typically derived from a hierarchy of clock
     12 * blocks which includes mux and divider blocks. There are a number of other
     13 * auxiliary clocks supplied to the CPU domain such as the debug blocks and AXI
     14 * clock for CPU domain. The rates of these auxiliary clocks are related to the
     15 * CPU clock rate and this relation is usually specified in the hardware manual
     16 * of the SoC or supplied after the SoC characterization.
     17 *
     18 * The below implementation of the CPU clock allows the rate changes of the CPU
     19 * clock and the corresponding rate changes of the auxillary clocks of the CPU
     20 * domain. The platform clock driver provides a clock register configuration
     21 * for each configurable rate which is then used to program the clock hardware
     22 * registers to acheive a fast co-oridinated rate change for all the CPU domain
     23 * clocks.
     24 *
     25 * On a rate change request for the CPU clock, the rate change is propagated
     26 * upto the PLL supplying the clock to the CPU domain clock blocks. While the
     27 * CPU domain PLL is reconfigured, the CPU domain clocks are driven using an
     28 * alternate clock source. If required, the alternate clock source is divided
     29 * down in order to keep the output clock rate within the previous OPP limits.
     30*/
     31
     32#include <linux/errno.h>
     33#include <linux/io.h>
     34#include <linux/slab.h>
     35#include <linux/clk.h>
     36#include <linux/clk-provider.h>
     37#include "clk-cpu.h"
     38
     39#define E4210_SRC_CPU		0x0
     40#define E4210_STAT_CPU		0x200
     41#define E4210_DIV_CPU0		0x300
     42#define E4210_DIV_CPU1		0x304
     43#define E4210_DIV_STAT_CPU0	0x400
     44#define E4210_DIV_STAT_CPU1	0x404
     45
     46#define E5433_MUX_SEL2		0x008
     47#define E5433_MUX_STAT2		0x208
     48#define E5433_DIV_CPU0		0x400
     49#define E5433_DIV_CPU1		0x404
     50#define E5433_DIV_STAT_CPU0	0x500
     51#define E5433_DIV_STAT_CPU1	0x504
     52
     53#define E4210_DIV0_RATIO0_MASK	0x7
     54#define E4210_DIV1_HPM_MASK	(0x7 << 4)
     55#define E4210_DIV1_COPY_MASK	(0x7 << 0)
     56#define E4210_MUX_HPM_MASK	(1 << 20)
     57#define E4210_DIV0_ATB_SHIFT	16
     58#define E4210_DIV0_ATB_MASK	(DIV_MASK << E4210_DIV0_ATB_SHIFT)
     59
     60#define MAX_DIV			8
     61#define DIV_MASK		7
     62#define DIV_MASK_ALL		0xffffffff
     63#define MUX_MASK		7
     64
     65/*
     66 * Helper function to wait until divider(s) have stabilized after the divider
     67 * value has changed.
     68 */
     69static void wait_until_divider_stable(void __iomem *div_reg, unsigned long mask)
     70{
     71	unsigned long timeout = jiffies + msecs_to_jiffies(10);
     72
     73	do {
     74		if (!(readl(div_reg) & mask))
     75			return;
     76	} while (time_before(jiffies, timeout));
     77
     78	if (!(readl(div_reg) & mask))
     79		return;
     80
     81	pr_err("%s: timeout in divider stablization\n", __func__);
     82}
     83
     84/*
     85 * Helper function to wait until mux has stabilized after the mux selection
     86 * value was changed.
     87 */
     88static void wait_until_mux_stable(void __iomem *mux_reg, u32 mux_pos,
     89					unsigned long mux_value)
     90{
     91	unsigned long timeout = jiffies + msecs_to_jiffies(10);
     92
     93	do {
     94		if (((readl(mux_reg) >> mux_pos) & MUX_MASK) == mux_value)
     95			return;
     96	} while (time_before(jiffies, timeout));
     97
     98	if (((readl(mux_reg) >> mux_pos) & MUX_MASK) == mux_value)
     99		return;
    100
    101	pr_err("%s: re-parenting mux timed-out\n", __func__);
    102}
    103
    104/* common round rate callback useable for all types of CPU clocks */
    105static long exynos_cpuclk_round_rate(struct clk_hw *hw,
    106			unsigned long drate, unsigned long *prate)
    107{
    108	struct clk_hw *parent = clk_hw_get_parent(hw);
    109	*prate = clk_hw_round_rate(parent, drate);
    110	return *prate;
    111}
    112
    113/* common recalc rate callback useable for all types of CPU clocks */
    114static unsigned long exynos_cpuclk_recalc_rate(struct clk_hw *hw,
    115			unsigned long parent_rate)
    116{
    117	/*
    118	 * The CPU clock output (armclk) rate is the same as its parent
    119	 * rate. Although there exist certain dividers inside the CPU
    120	 * clock block that could be used to divide the parent clock,
    121	 * the driver does not make use of them currently, except during
    122	 * frequency transitions.
    123	 */
    124	return parent_rate;
    125}
    126
    127static const struct clk_ops exynos_cpuclk_clk_ops = {
    128	.recalc_rate = exynos_cpuclk_recalc_rate,
    129	.round_rate = exynos_cpuclk_round_rate,
    130};
    131
    132/*
    133 * Helper function to set the 'safe' dividers for the CPU clock. The parameters
    134 * div and mask contain the divider value and the register bit mask of the
    135 * dividers to be programmed.
    136 */
    137static void exynos_set_safe_div(void __iomem *base, unsigned long div,
    138					unsigned long mask)
    139{
    140	unsigned long div0;
    141
    142	div0 = readl(base + E4210_DIV_CPU0);
    143	div0 = (div0 & ~mask) | (div & mask);
    144	writel(div0, base + E4210_DIV_CPU0);
    145	wait_until_divider_stable(base + E4210_DIV_STAT_CPU0, mask);
    146}
    147
    148/* handler for pre-rate change notification from parent clock */
    149static int exynos_cpuclk_pre_rate_change(struct clk_notifier_data *ndata,
    150			struct exynos_cpuclk *cpuclk, void __iomem *base)
    151{
    152	const struct exynos_cpuclk_cfg_data *cfg_data = cpuclk->cfg;
    153	unsigned long alt_prate = clk_hw_get_rate(cpuclk->alt_parent);
    154	unsigned long alt_div = 0, alt_div_mask = DIV_MASK;
    155	unsigned long div0, div1 = 0, mux_reg;
    156	unsigned long flags;
    157
    158	/* find out the divider values to use for clock data */
    159	while ((cfg_data->prate * 1000) != ndata->new_rate) {
    160		if (cfg_data->prate == 0)
    161			return -EINVAL;
    162		cfg_data++;
    163	}
    164
    165	spin_lock_irqsave(cpuclk->lock, flags);
    166
    167	/*
    168	 * For the selected PLL clock frequency, get the pre-defined divider
    169	 * values. If the clock for sclk_hpm is not sourced from apll, then
    170	 * the values for DIV_COPY and DIV_HPM dividers need not be set.
    171	 */
    172	div0 = cfg_data->div0;
    173	if (cpuclk->flags & CLK_CPU_HAS_DIV1) {
    174		div1 = cfg_data->div1;
    175		if (readl(base + E4210_SRC_CPU) & E4210_MUX_HPM_MASK)
    176			div1 = readl(base + E4210_DIV_CPU1) &
    177				(E4210_DIV1_HPM_MASK | E4210_DIV1_COPY_MASK);
    178	}
    179
    180	/*
    181	 * If the old parent clock speed is less than the clock speed of
    182	 * the alternate parent, then it should be ensured that at no point
    183	 * the armclk speed is more than the old_prate until the dividers are
    184	 * set.  Also workaround the issue of the dividers being set to lower
    185	 * values before the parent clock speed is set to new lower speed
    186	 * (this can result in too high speed of armclk output clocks).
    187	 */
    188	if (alt_prate > ndata->old_rate || ndata->old_rate > ndata->new_rate) {
    189		unsigned long tmp_rate = min(ndata->old_rate, ndata->new_rate);
    190
    191		alt_div = DIV_ROUND_UP(alt_prate, tmp_rate) - 1;
    192		WARN_ON(alt_div >= MAX_DIV);
    193
    194		if (cpuclk->flags & CLK_CPU_NEEDS_DEBUG_ALT_DIV) {
    195			/*
    196			 * In Exynos4210, ATB clock parent is also mout_core. So
    197			 * ATB clock also needs to be mantained at safe speed.
    198			 */
    199			alt_div |= E4210_DIV0_ATB_MASK;
    200			alt_div_mask |= E4210_DIV0_ATB_MASK;
    201		}
    202		exynos_set_safe_div(base, alt_div, alt_div_mask);
    203		div0 |= alt_div;
    204	}
    205
    206	/* select sclk_mpll as the alternate parent */
    207	mux_reg = readl(base + E4210_SRC_CPU);
    208	writel(mux_reg | (1 << 16), base + E4210_SRC_CPU);
    209	wait_until_mux_stable(base + E4210_STAT_CPU, 16, 2);
    210
    211	/* alternate parent is active now. set the dividers */
    212	writel(div0, base + E4210_DIV_CPU0);
    213	wait_until_divider_stable(base + E4210_DIV_STAT_CPU0, DIV_MASK_ALL);
    214
    215	if (cpuclk->flags & CLK_CPU_HAS_DIV1) {
    216		writel(div1, base + E4210_DIV_CPU1);
    217		wait_until_divider_stable(base + E4210_DIV_STAT_CPU1,
    218				DIV_MASK_ALL);
    219	}
    220
    221	spin_unlock_irqrestore(cpuclk->lock, flags);
    222	return 0;
    223}
    224
    225/* handler for post-rate change notification from parent clock */
    226static int exynos_cpuclk_post_rate_change(struct clk_notifier_data *ndata,
    227			struct exynos_cpuclk *cpuclk, void __iomem *base)
    228{
    229	const struct exynos_cpuclk_cfg_data *cfg_data = cpuclk->cfg;
    230	unsigned long div = 0, div_mask = DIV_MASK;
    231	unsigned long mux_reg;
    232	unsigned long flags;
    233
    234	/* find out the divider values to use for clock data */
    235	if (cpuclk->flags & CLK_CPU_NEEDS_DEBUG_ALT_DIV) {
    236		while ((cfg_data->prate * 1000) != ndata->new_rate) {
    237			if (cfg_data->prate == 0)
    238				return -EINVAL;
    239			cfg_data++;
    240		}
    241	}
    242
    243	spin_lock_irqsave(cpuclk->lock, flags);
    244
    245	/* select mout_apll as the alternate parent */
    246	mux_reg = readl(base + E4210_SRC_CPU);
    247	writel(mux_reg & ~(1 << 16), base + E4210_SRC_CPU);
    248	wait_until_mux_stable(base + E4210_STAT_CPU, 16, 1);
    249
    250	if (cpuclk->flags & CLK_CPU_NEEDS_DEBUG_ALT_DIV) {
    251		div |= (cfg_data->div0 & E4210_DIV0_ATB_MASK);
    252		div_mask |= E4210_DIV0_ATB_MASK;
    253	}
    254
    255	exynos_set_safe_div(base, div, div_mask);
    256	spin_unlock_irqrestore(cpuclk->lock, flags);
    257	return 0;
    258}
    259
    260/*
    261 * Helper function to set the 'safe' dividers for the CPU clock. The parameters
    262 * div and mask contain the divider value and the register bit mask of the
    263 * dividers to be programmed.
    264 */
    265static void exynos5433_set_safe_div(void __iomem *base, unsigned long div,
    266					unsigned long mask)
    267{
    268	unsigned long div0;
    269
    270	div0 = readl(base + E5433_DIV_CPU0);
    271	div0 = (div0 & ~mask) | (div & mask);
    272	writel(div0, base + E5433_DIV_CPU0);
    273	wait_until_divider_stable(base + E5433_DIV_STAT_CPU0, mask);
    274}
    275
    276/* handler for pre-rate change notification from parent clock */
    277static int exynos5433_cpuclk_pre_rate_change(struct clk_notifier_data *ndata,
    278			struct exynos_cpuclk *cpuclk, void __iomem *base)
    279{
    280	const struct exynos_cpuclk_cfg_data *cfg_data = cpuclk->cfg;
    281	unsigned long alt_prate = clk_hw_get_rate(cpuclk->alt_parent);
    282	unsigned long alt_div = 0, alt_div_mask = DIV_MASK;
    283	unsigned long div0, div1 = 0, mux_reg;
    284	unsigned long flags;
    285
    286	/* find out the divider values to use for clock data */
    287	while ((cfg_data->prate * 1000) != ndata->new_rate) {
    288		if (cfg_data->prate == 0)
    289			return -EINVAL;
    290		cfg_data++;
    291	}
    292
    293	spin_lock_irqsave(cpuclk->lock, flags);
    294
    295	/*
    296	 * For the selected PLL clock frequency, get the pre-defined divider
    297	 * values.
    298	 */
    299	div0 = cfg_data->div0;
    300	div1 = cfg_data->div1;
    301
    302	/*
    303	 * If the old parent clock speed is less than the clock speed of
    304	 * the alternate parent, then it should be ensured that at no point
    305	 * the armclk speed is more than the old_prate until the dividers are
    306	 * set.  Also workaround the issue of the dividers being set to lower
    307	 * values before the parent clock speed is set to new lower speed
    308	 * (this can result in too high speed of armclk output clocks).
    309	 */
    310	if (alt_prate > ndata->old_rate || ndata->old_rate > ndata->new_rate) {
    311		unsigned long tmp_rate = min(ndata->old_rate, ndata->new_rate);
    312
    313		alt_div = DIV_ROUND_UP(alt_prate, tmp_rate) - 1;
    314		WARN_ON(alt_div >= MAX_DIV);
    315
    316		exynos5433_set_safe_div(base, alt_div, alt_div_mask);
    317		div0 |= alt_div;
    318	}
    319
    320	/* select the alternate parent */
    321	mux_reg = readl(base + E5433_MUX_SEL2);
    322	writel(mux_reg | 1, base + E5433_MUX_SEL2);
    323	wait_until_mux_stable(base + E5433_MUX_STAT2, 0, 2);
    324
    325	/* alternate parent is active now. set the dividers */
    326	writel(div0, base + E5433_DIV_CPU0);
    327	wait_until_divider_stable(base + E5433_DIV_STAT_CPU0, DIV_MASK_ALL);
    328
    329	writel(div1, base + E5433_DIV_CPU1);
    330	wait_until_divider_stable(base + E5433_DIV_STAT_CPU1, DIV_MASK_ALL);
    331
    332	spin_unlock_irqrestore(cpuclk->lock, flags);
    333	return 0;
    334}
    335
    336/* handler for post-rate change notification from parent clock */
    337static int exynos5433_cpuclk_post_rate_change(struct clk_notifier_data *ndata,
    338			struct exynos_cpuclk *cpuclk, void __iomem *base)
    339{
    340	unsigned long div = 0, div_mask = DIV_MASK;
    341	unsigned long mux_reg;
    342	unsigned long flags;
    343
    344	spin_lock_irqsave(cpuclk->lock, flags);
    345
    346	/* select apll as the alternate parent */
    347	mux_reg = readl(base + E5433_MUX_SEL2);
    348	writel(mux_reg & ~1, base + E5433_MUX_SEL2);
    349	wait_until_mux_stable(base + E5433_MUX_STAT2, 0, 1);
    350
    351	exynos5433_set_safe_div(base, div, div_mask);
    352	spin_unlock_irqrestore(cpuclk->lock, flags);
    353	return 0;
    354}
    355
    356/*
    357 * This notifier function is called for the pre-rate and post-rate change
    358 * notifications of the parent clock of cpuclk.
    359 */
    360static int exynos_cpuclk_notifier_cb(struct notifier_block *nb,
    361				unsigned long event, void *data)
    362{
    363	struct clk_notifier_data *ndata = data;
    364	struct exynos_cpuclk *cpuclk;
    365	void __iomem *base;
    366	int err = 0;
    367
    368	cpuclk = container_of(nb, struct exynos_cpuclk, clk_nb);
    369	base = cpuclk->ctrl_base;
    370
    371	if (event == PRE_RATE_CHANGE)
    372		err = exynos_cpuclk_pre_rate_change(ndata, cpuclk, base);
    373	else if (event == POST_RATE_CHANGE)
    374		err = exynos_cpuclk_post_rate_change(ndata, cpuclk, base);
    375
    376	return notifier_from_errno(err);
    377}
    378
    379/*
    380 * This notifier function is called for the pre-rate and post-rate change
    381 * notifications of the parent clock of cpuclk.
    382 */
    383static int exynos5433_cpuclk_notifier_cb(struct notifier_block *nb,
    384				unsigned long event, void *data)
    385{
    386	struct clk_notifier_data *ndata = data;
    387	struct exynos_cpuclk *cpuclk;
    388	void __iomem *base;
    389	int err = 0;
    390
    391	cpuclk = container_of(nb, struct exynos_cpuclk, clk_nb);
    392	base = cpuclk->ctrl_base;
    393
    394	if (event == PRE_RATE_CHANGE)
    395		err = exynos5433_cpuclk_pre_rate_change(ndata, cpuclk, base);
    396	else if (event == POST_RATE_CHANGE)
    397		err = exynos5433_cpuclk_post_rate_change(ndata, cpuclk, base);
    398
    399	return notifier_from_errno(err);
    400}
    401
    402/* helper function to register a CPU clock */
    403static int __init exynos_register_cpu_clock(struct samsung_clk_provider *ctx,
    404		unsigned int lookup_id, const char *name,
    405		const struct clk_hw *parent, const struct clk_hw *alt_parent,
    406		unsigned long offset, const struct exynos_cpuclk_cfg_data *cfg,
    407		unsigned long num_cfgs, unsigned long flags)
    408{
    409	struct exynos_cpuclk *cpuclk;
    410	struct clk_init_data init;
    411	const char *parent_name;
    412	int ret = 0;
    413
    414	if (IS_ERR(parent) || IS_ERR(alt_parent)) {
    415		pr_err("%s: invalid parent clock(s)\n", __func__);
    416		return -EINVAL;
    417	}
    418
    419	cpuclk = kzalloc(sizeof(*cpuclk), GFP_KERNEL);
    420	if (!cpuclk)
    421		return -ENOMEM;
    422
    423	parent_name = clk_hw_get_name(parent);
    424
    425	init.name = name;
    426	init.flags = CLK_SET_RATE_PARENT;
    427	init.parent_names = &parent_name;
    428	init.num_parents = 1;
    429	init.ops = &exynos_cpuclk_clk_ops;
    430
    431	cpuclk->alt_parent = alt_parent;
    432	cpuclk->hw.init = &init;
    433	cpuclk->ctrl_base = ctx->reg_base + offset;
    434	cpuclk->lock = &ctx->lock;
    435	cpuclk->flags = flags;
    436	if (flags & CLK_CPU_HAS_E5433_REGS_LAYOUT)
    437		cpuclk->clk_nb.notifier_call = exynos5433_cpuclk_notifier_cb;
    438	else
    439		cpuclk->clk_nb.notifier_call = exynos_cpuclk_notifier_cb;
    440
    441
    442	ret = clk_notifier_register(parent->clk, &cpuclk->clk_nb);
    443	if (ret) {
    444		pr_err("%s: failed to register clock notifier for %s\n",
    445				__func__, name);
    446		goto free_cpuclk;
    447	}
    448
    449	cpuclk->cfg = kmemdup(cfg, sizeof(*cfg) * num_cfgs, GFP_KERNEL);
    450	if (!cpuclk->cfg) {
    451		ret = -ENOMEM;
    452		goto unregister_clk_nb;
    453	}
    454
    455	ret = clk_hw_register(NULL, &cpuclk->hw);
    456	if (ret) {
    457		pr_err("%s: could not register cpuclk %s\n", __func__,	name);
    458		goto free_cpuclk_data;
    459	}
    460
    461	samsung_clk_add_lookup(ctx, &cpuclk->hw, lookup_id);
    462	return 0;
    463
    464free_cpuclk_data:
    465	kfree(cpuclk->cfg);
    466unregister_clk_nb:
    467	clk_notifier_unregister(parent->clk, &cpuclk->clk_nb);
    468free_cpuclk:
    469	kfree(cpuclk);
    470	return ret;
    471}
    472
    473void __init samsung_clk_register_cpu(struct samsung_clk_provider *ctx,
    474		const struct samsung_cpu_clock *list, unsigned int nr_clk)
    475{
    476	unsigned int idx;
    477	unsigned int num_cfgs;
    478	struct clk_hw **hws = ctx->clk_data.hws;
    479
    480	for (idx = 0; idx < nr_clk; idx++, list++) {
    481		/* find count of configuration rates in cfg */
    482		for (num_cfgs = 0; list->cfg[num_cfgs].prate != 0; )
    483			num_cfgs++;
    484
    485		exynos_register_cpu_clock(ctx, list->id, list->name, hws[list->parent_id],
    486				hws[list->alt_parent_id], list->offset, list->cfg, num_cfgs,
    487				list->flags);
    488	}
    489}