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

mcde_clk_div.c (4658B)


      1// SPDX-License-Identifier: GPL-2.0
      2#include <linux/clk-provider.h>
      3#include <linux/regulator/consumer.h>
      4
      5#include "mcde_drm.h"
      6#include "mcde_display_regs.h"
      7
      8/* The MCDE internal clock dividers for FIFO A and B */
      9struct mcde_clk_div {
     10	struct clk_hw hw;
     11	struct mcde *mcde;
     12	u32 cr;
     13	u32 cr_div;
     14};
     15
     16static int mcde_clk_div_enable(struct clk_hw *hw)
     17{
     18	struct mcde_clk_div *cdiv = container_of(hw, struct mcde_clk_div, hw);
     19	struct mcde *mcde = cdiv->mcde;
     20	u32 val;
     21
     22	spin_lock(&mcde->fifo_crx1_lock);
     23	val = readl(mcde->regs + cdiv->cr);
     24	/*
     25	 * Select the PLL72 (LCD) clock as parent
     26	 * FIXME: implement other parents.
     27	 */
     28	val &= ~MCDE_CRX1_CLKSEL_MASK;
     29	val |= MCDE_CRX1_CLKSEL_CLKPLL72 << MCDE_CRX1_CLKSEL_SHIFT;
     30	/* Internal clock */
     31	val |= MCDE_CRA1_CLKTYPE_TVXCLKSEL1;
     32
     33	/* Clear then set the divider */
     34	val &= ~(MCDE_CRX1_BCD | MCDE_CRX1_PCD_MASK);
     35	val |= cdiv->cr_div;
     36
     37	writel(val, mcde->regs + cdiv->cr);
     38	spin_unlock(&mcde->fifo_crx1_lock);
     39
     40	return 0;
     41}
     42
     43static int mcde_clk_div_choose_div(struct clk_hw *hw, unsigned long rate,
     44				   unsigned long *prate, bool set_parent)
     45{
     46	int best_div = 1, div;
     47	struct clk_hw *parent = clk_hw_get_parent(hw);
     48	unsigned long best_prate = 0;
     49	unsigned long best_diff = ~0ul;
     50	int max_div = (1 << MCDE_CRX1_PCD_BITS) - 1;
     51
     52	for (div = 1; div < max_div; div++) {
     53		unsigned long this_prate, div_rate, diff;
     54
     55		if (set_parent)
     56			this_prate = clk_hw_round_rate(parent, rate * div);
     57		else
     58			this_prate = *prate;
     59		div_rate = DIV_ROUND_UP_ULL(this_prate, div);
     60		diff = abs(rate - div_rate);
     61
     62		if (diff < best_diff) {
     63			best_div = div;
     64			best_diff = diff;
     65			best_prate = this_prate;
     66		}
     67	}
     68
     69	*prate = best_prate;
     70	return best_div;
     71}
     72
     73static long mcde_clk_div_round_rate(struct clk_hw *hw, unsigned long rate,
     74				     unsigned long *prate)
     75{
     76	int div = mcde_clk_div_choose_div(hw, rate, prate, true);
     77
     78	return DIV_ROUND_UP_ULL(*prate, div);
     79}
     80
     81static unsigned long mcde_clk_div_recalc_rate(struct clk_hw *hw,
     82					       unsigned long prate)
     83{
     84	struct mcde_clk_div *cdiv = container_of(hw, struct mcde_clk_div, hw);
     85	struct mcde *mcde = cdiv->mcde;
     86	u32 cr;
     87	int div;
     88
     89	/*
     90	 * If the MCDE is not powered we can't access registers.
     91	 * It will come up with 0 in the divider register bits, which
     92	 * means "divide by 2".
     93	 */
     94	if (!regulator_is_enabled(mcde->epod))
     95		return DIV_ROUND_UP_ULL(prate, 2);
     96
     97	cr = readl(mcde->regs + cdiv->cr);
     98	if (cr & MCDE_CRX1_BCD)
     99		return prate;
    100
    101	/* 0 in the PCD means "divide by 2", 1 means "divide by 3" etc */
    102	div = cr & MCDE_CRX1_PCD_MASK;
    103	div += 2;
    104
    105	return DIV_ROUND_UP_ULL(prate, div);
    106}
    107
    108static int mcde_clk_div_set_rate(struct clk_hw *hw, unsigned long rate,
    109				  unsigned long prate)
    110{
    111	struct mcde_clk_div *cdiv = container_of(hw, struct mcde_clk_div, hw);
    112	int div = mcde_clk_div_choose_div(hw, rate, &prate, false);
    113	u32 cr = 0;
    114
    115	/*
    116	 * We cache the CR bits to set the divide in the state so that
    117	 * we can call this before we can even write to the hardware.
    118	 */
    119	if (div == 1) {
    120		/* Bypass clock divider */
    121		cr |= MCDE_CRX1_BCD;
    122	} else {
    123		div -= 2;
    124		cr |= div & MCDE_CRX1_PCD_MASK;
    125	}
    126	cdiv->cr_div = cr;
    127
    128	return 0;
    129}
    130
    131static const struct clk_ops mcde_clk_div_ops = {
    132	.enable = mcde_clk_div_enable,
    133	.recalc_rate = mcde_clk_div_recalc_rate,
    134	.round_rate = mcde_clk_div_round_rate,
    135	.set_rate = mcde_clk_div_set_rate,
    136};
    137
    138int mcde_init_clock_divider(struct mcde *mcde)
    139{
    140	struct device *dev = mcde->dev;
    141	struct mcde_clk_div *fifoa;
    142	struct mcde_clk_div *fifob;
    143	const char *parent_name;
    144	struct clk_init_data fifoa_init = {
    145		.name = "fifoa",
    146		.ops = &mcde_clk_div_ops,
    147		.parent_names = &parent_name,
    148		.num_parents = 1,
    149		.flags = CLK_SET_RATE_PARENT,
    150	};
    151	struct clk_init_data fifob_init = {
    152		.name = "fifob",
    153		.ops = &mcde_clk_div_ops,
    154		.parent_names = &parent_name,
    155		.num_parents = 1,
    156		.flags = CLK_SET_RATE_PARENT,
    157	};
    158	int ret;
    159
    160	spin_lock_init(&mcde->fifo_crx1_lock);
    161	parent_name = __clk_get_name(mcde->lcd_clk);
    162
    163	/* Allocate 2 clocks */
    164	fifoa = devm_kzalloc(dev, sizeof(*fifoa), GFP_KERNEL);
    165	if (!fifoa)
    166		return -ENOMEM;
    167	fifob = devm_kzalloc(dev, sizeof(*fifob), GFP_KERNEL);
    168	if (!fifob)
    169		return -ENOMEM;
    170
    171	fifoa->mcde = mcde;
    172	fifoa->cr = MCDE_CRA1;
    173	fifoa->hw.init = &fifoa_init;
    174	ret = devm_clk_hw_register(dev, &fifoa->hw);
    175	if (ret) {
    176		dev_err(dev, "error registering FIFO A clock divider\n");
    177		return ret;
    178	}
    179	mcde->fifoa_clk = fifoa->hw.clk;
    180
    181	fifob->mcde = mcde;
    182	fifob->cr = MCDE_CRB1;
    183	fifob->hw.init = &fifob_init;
    184	ret = devm_clk_hw_register(dev, &fifob->hw);
    185	if (ret) {
    186		dev_err(dev, "error registering FIFO B clock divider\n");
    187		return ret;
    188	}
    189	mcde->fifob_clk = fifob->hw.clk;
    190
    191	return 0;
    192}