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

ccu_common.c (5064B)


      1// SPDX-License-Identifier: GPL-2.0-or-later
      2/*
      3 * Copyright 2016 Maxime Ripard
      4 *
      5 * Maxime Ripard <maxime.ripard@free-electrons.com>
      6 */
      7
      8#include <linux/clk.h>
      9#include <linux/clk-provider.h>
     10#include <linux/device.h>
     11#include <linux/iopoll.h>
     12#include <linux/module.h>
     13#include <linux/slab.h>
     14
     15#include "ccu_common.h"
     16#include "ccu_gate.h"
     17#include "ccu_reset.h"
     18
     19struct sunxi_ccu {
     20	const struct sunxi_ccu_desc	*desc;
     21	spinlock_t			lock;
     22	struct ccu_reset		reset;
     23};
     24
     25void ccu_helper_wait_for_lock(struct ccu_common *common, u32 lock)
     26{
     27	void __iomem *addr;
     28	u32 reg;
     29
     30	if (!lock)
     31		return;
     32
     33	if (common->features & CCU_FEATURE_LOCK_REG)
     34		addr = common->base + common->lock_reg;
     35	else
     36		addr = common->base + common->reg;
     37
     38	WARN_ON(readl_relaxed_poll_timeout(addr, reg, reg & lock, 100, 70000));
     39}
     40EXPORT_SYMBOL_NS_GPL(ccu_helper_wait_for_lock, SUNXI_CCU);
     41
     42/*
     43 * This clock notifier is called when the frequency of a PLL clock is
     44 * changed. In common PLL designs, changes to the dividers take effect
     45 * almost immediately, while changes to the multipliers (implemented
     46 * as dividers in the feedback loop) take a few cycles to work into
     47 * the feedback loop for the PLL to stablize.
     48 *
     49 * Sometimes when the PLL clock rate is changed, the decrease in the
     50 * divider is too much for the decrease in the multiplier to catch up.
     51 * The PLL clock rate will spike, and in some cases, might lock up
     52 * completely.
     53 *
     54 * This notifier callback will gate and then ungate the clock,
     55 * effectively resetting it, so it proceeds to work. Care must be
     56 * taken to reparent consumers to other temporary clocks during the
     57 * rate change, and that this notifier callback must be the first
     58 * to be registered.
     59 */
     60static int ccu_pll_notifier_cb(struct notifier_block *nb,
     61			       unsigned long event, void *data)
     62{
     63	struct ccu_pll_nb *pll = to_ccu_pll_nb(nb);
     64	int ret = 0;
     65
     66	if (event != POST_RATE_CHANGE)
     67		goto out;
     68
     69	ccu_gate_helper_disable(pll->common, pll->enable);
     70
     71	ret = ccu_gate_helper_enable(pll->common, pll->enable);
     72	if (ret)
     73		goto out;
     74
     75	ccu_helper_wait_for_lock(pll->common, pll->lock);
     76
     77out:
     78	return notifier_from_errno(ret);
     79}
     80
     81int ccu_pll_notifier_register(struct ccu_pll_nb *pll_nb)
     82{
     83	pll_nb->clk_nb.notifier_call = ccu_pll_notifier_cb;
     84
     85	return clk_notifier_register(pll_nb->common->hw.clk,
     86				     &pll_nb->clk_nb);
     87}
     88EXPORT_SYMBOL_NS_GPL(ccu_pll_notifier_register, SUNXI_CCU);
     89
     90static int sunxi_ccu_probe(struct sunxi_ccu *ccu, struct device *dev,
     91			   struct device_node *node, void __iomem *reg,
     92			   const struct sunxi_ccu_desc *desc)
     93{
     94	struct ccu_reset *reset;
     95	int i, ret;
     96
     97	ccu->desc = desc;
     98
     99	spin_lock_init(&ccu->lock);
    100
    101	for (i = 0; i < desc->num_ccu_clks; i++) {
    102		struct ccu_common *cclk = desc->ccu_clks[i];
    103
    104		if (!cclk)
    105			continue;
    106
    107		cclk->base = reg;
    108		cclk->lock = &ccu->lock;
    109	}
    110
    111	for (i = 0; i < desc->hw_clks->num ; i++) {
    112		struct clk_hw *hw = desc->hw_clks->hws[i];
    113		const char *name;
    114
    115		if (!hw)
    116			continue;
    117
    118		name = hw->init->name;
    119		if (dev)
    120			ret = clk_hw_register(dev, hw);
    121		else
    122			ret = of_clk_hw_register(node, hw);
    123		if (ret) {
    124			pr_err("Couldn't register clock %d - %s\n", i, name);
    125			goto err_clk_unreg;
    126		}
    127	}
    128
    129	ret = of_clk_add_hw_provider(node, of_clk_hw_onecell_get,
    130				     desc->hw_clks);
    131	if (ret)
    132		goto err_clk_unreg;
    133
    134	reset = &ccu->reset;
    135	reset->rcdev.of_node = node;
    136	reset->rcdev.ops = &ccu_reset_ops;
    137	reset->rcdev.owner = dev ? dev->driver->owner : THIS_MODULE;
    138	reset->rcdev.nr_resets = desc->num_resets;
    139	reset->base = reg;
    140	reset->lock = &ccu->lock;
    141	reset->reset_map = desc->resets;
    142
    143	ret = reset_controller_register(&reset->rcdev);
    144	if (ret)
    145		goto err_del_provider;
    146
    147	return 0;
    148
    149err_del_provider:
    150	of_clk_del_provider(node);
    151err_clk_unreg:
    152	while (--i >= 0) {
    153		struct clk_hw *hw = desc->hw_clks->hws[i];
    154
    155		if (!hw)
    156			continue;
    157		clk_hw_unregister(hw);
    158	}
    159	return ret;
    160}
    161
    162static void devm_sunxi_ccu_release(struct device *dev, void *res)
    163{
    164	struct sunxi_ccu *ccu = res;
    165	const struct sunxi_ccu_desc *desc = ccu->desc;
    166	int i;
    167
    168	reset_controller_unregister(&ccu->reset.rcdev);
    169	of_clk_del_provider(dev->of_node);
    170
    171	for (i = 0; i < desc->hw_clks->num; i++) {
    172		struct clk_hw *hw = desc->hw_clks->hws[i];
    173
    174		if (!hw)
    175			continue;
    176		clk_hw_unregister(hw);
    177	}
    178}
    179
    180int devm_sunxi_ccu_probe(struct device *dev, void __iomem *reg,
    181			 const struct sunxi_ccu_desc *desc)
    182{
    183	struct sunxi_ccu *ccu;
    184	int ret;
    185
    186	ccu = devres_alloc(devm_sunxi_ccu_release, sizeof(*ccu), GFP_KERNEL);
    187	if (!ccu)
    188		return -ENOMEM;
    189
    190	ret = sunxi_ccu_probe(ccu, dev, dev->of_node, reg, desc);
    191	if (ret) {
    192		devres_free(ccu);
    193		return ret;
    194	}
    195
    196	devres_add(dev, ccu);
    197
    198	return 0;
    199}
    200EXPORT_SYMBOL_NS_GPL(devm_sunxi_ccu_probe, SUNXI_CCU);
    201
    202void of_sunxi_ccu_probe(struct device_node *node, void __iomem *reg,
    203			const struct sunxi_ccu_desc *desc)
    204{
    205	struct sunxi_ccu *ccu;
    206	int ret;
    207
    208	ccu = kzalloc(sizeof(*ccu), GFP_KERNEL);
    209	if (!ccu)
    210		return;
    211
    212	ret = sunxi_ccu_probe(ccu, NULL, node, reg, desc);
    213	if (ret) {
    214		pr_err("%pOF: probing clocks failed: %d\n", node, ret);
    215		kfree(ccu);
    216	}
    217}
    218
    219MODULE_LICENSE("GPL");