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-lpcg-scu.c (3510B)


      1// SPDX-License-Identifier: GPL-2.0+
      2/*
      3 * Copyright 2018 NXP
      4 *	Dong Aisheng <aisheng.dong@nxp.com>
      5 */
      6
      7#include <linux/bits.h>
      8#include <linux/clk-provider.h>
      9#include <linux/err.h>
     10#include <linux/io.h>
     11#include <linux/slab.h>
     12#include <linux/spinlock.h>
     13
     14#include "clk-scu.h"
     15
     16static DEFINE_SPINLOCK(imx_lpcg_scu_lock);
     17
     18#define CLK_GATE_SCU_LPCG_MASK		0x3
     19#define CLK_GATE_SCU_LPCG_HW_SEL	BIT(0)
     20#define CLK_GATE_SCU_LPCG_SW_SEL	BIT(1)
     21
     22/*
     23 * struct clk_lpcg_scu - Description of LPCG clock
     24 *
     25 * @hw: clk_hw of this LPCG
     26 * @reg: register of this LPCG clock
     27 * @bit_idx: bit index of this LPCG clock
     28 * @hw_gate: HW auto gate enable
     29 *
     30 * This structure describes one LPCG clock
     31 */
     32struct clk_lpcg_scu {
     33	struct clk_hw hw;
     34	void __iomem *reg;
     35	u8 bit_idx;
     36	bool hw_gate;
     37
     38	/* for state save&restore */
     39	u32 state;
     40};
     41
     42#define to_clk_lpcg_scu(_hw) container_of(_hw, struct clk_lpcg_scu, hw)
     43
     44static int clk_lpcg_scu_enable(struct clk_hw *hw)
     45{
     46	struct clk_lpcg_scu *clk = to_clk_lpcg_scu(hw);
     47	unsigned long flags;
     48	u32 reg, val;
     49
     50	spin_lock_irqsave(&imx_lpcg_scu_lock, flags);
     51
     52	reg = readl_relaxed(clk->reg);
     53	reg &= ~(CLK_GATE_SCU_LPCG_MASK << clk->bit_idx);
     54
     55	val = CLK_GATE_SCU_LPCG_SW_SEL;
     56	if (clk->hw_gate)
     57		val |= CLK_GATE_SCU_LPCG_HW_SEL;
     58
     59	reg |= val << clk->bit_idx;
     60	writel(reg, clk->reg);
     61
     62	spin_unlock_irqrestore(&imx_lpcg_scu_lock, flags);
     63
     64	return 0;
     65}
     66
     67static void clk_lpcg_scu_disable(struct clk_hw *hw)
     68{
     69	struct clk_lpcg_scu *clk = to_clk_lpcg_scu(hw);
     70	unsigned long flags;
     71	u32 reg;
     72
     73	spin_lock_irqsave(&imx_lpcg_scu_lock, flags);
     74
     75	reg = readl_relaxed(clk->reg);
     76	reg &= ~(CLK_GATE_SCU_LPCG_MASK << clk->bit_idx);
     77	writel(reg, clk->reg);
     78
     79	spin_unlock_irqrestore(&imx_lpcg_scu_lock, flags);
     80}
     81
     82static const struct clk_ops clk_lpcg_scu_ops = {
     83	.enable = clk_lpcg_scu_enable,
     84	.disable = clk_lpcg_scu_disable,
     85};
     86
     87struct clk_hw *__imx_clk_lpcg_scu(struct device *dev, const char *name,
     88				  const char *parent_name, unsigned long flags,
     89				  void __iomem *reg, u8 bit_idx, bool hw_gate)
     90{
     91	struct clk_lpcg_scu *clk;
     92	struct clk_init_data init;
     93	struct clk_hw *hw;
     94	int ret;
     95
     96	clk = kzalloc(sizeof(*clk), GFP_KERNEL);
     97	if (!clk)
     98		return ERR_PTR(-ENOMEM);
     99
    100	clk->reg = reg;
    101	clk->bit_idx = bit_idx;
    102	clk->hw_gate = hw_gate;
    103
    104	init.name = name;
    105	init.ops = &clk_lpcg_scu_ops;
    106	init.flags = CLK_SET_RATE_PARENT | flags;
    107	init.parent_names = parent_name ? &parent_name : NULL;
    108	init.num_parents = parent_name ? 1 : 0;
    109
    110	clk->hw.init = &init;
    111
    112	hw = &clk->hw;
    113	ret = clk_hw_register(dev, hw);
    114	if (ret) {
    115		kfree(clk);
    116		hw = ERR_PTR(ret);
    117		return hw;
    118	}
    119
    120	if (dev)
    121		dev_set_drvdata(dev, clk);
    122
    123	return hw;
    124}
    125
    126void imx_clk_lpcg_scu_unregister(struct clk_hw *hw)
    127{
    128	struct clk_lpcg_scu *clk = to_clk_lpcg_scu(hw);
    129
    130	clk_hw_unregister(&clk->hw);
    131	kfree(clk);
    132}
    133
    134static int __maybe_unused imx_clk_lpcg_scu_suspend(struct device *dev)
    135{
    136	struct clk_lpcg_scu *clk = dev_get_drvdata(dev);
    137
    138	clk->state = readl_relaxed(clk->reg);
    139	dev_dbg(dev, "save lpcg state 0x%x\n", clk->state);
    140
    141	return 0;
    142}
    143
    144static int __maybe_unused imx_clk_lpcg_scu_resume(struct device *dev)
    145{
    146	struct clk_lpcg_scu *clk = dev_get_drvdata(dev);
    147
    148	/*
    149	 * FIXME: Sometimes writes don't work unless the CPU issues
    150	 * them twice
    151	 */
    152
    153	writel(clk->state, clk->reg);
    154	writel(clk->state, clk->reg);
    155	dev_dbg(dev, "restore lpcg state 0x%x\n", clk->state);
    156
    157	return 0;
    158}
    159
    160const struct dev_pm_ops imx_clk_lpcg_scu_pm_ops = {
    161	SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(imx_clk_lpcg_scu_suspend,
    162				      imx_clk_lpcg_scu_resume)
    163};