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

dpll44xx.c (6300B)


      1// SPDX-License-Identifier: GPL-2.0-only
      2/*
      3 * OMAP4-specific DPLL control functions
      4 *
      5 * Copyright (C) 2011 Texas Instruments, Inc.
      6 * Rajendra Nayak
      7 */
      8
      9#include <linux/kernel.h>
     10#include <linux/errno.h>
     11#include <linux/clk.h>
     12#include <linux/io.h>
     13#include <linux/bitops.h>
     14#include <linux/clk/ti.h>
     15
     16#include "clock.h"
     17
     18/*
     19 * Maximum DPLL input frequency (FINT) and output frequency (FOUT) that
     20 * can supported when using the DPLL low-power mode. Frequencies are
     21 * defined in OMAP4430/60 Public TRM section 3.6.3.3.2 "Enable Control,
     22 * Status, and Low-Power Operation Mode".
     23 */
     24#define OMAP4_DPLL_LP_FINT_MAX	1000000
     25#define OMAP4_DPLL_LP_FOUT_MAX	100000000
     26
     27/*
     28 * Bitfield declarations
     29 */
     30#define OMAP4430_DPLL_CLKOUT_GATE_CTRL_MASK		BIT(8)
     31#define OMAP4430_DPLL_CLKOUTX2_GATE_CTRL_MASK		BIT(10)
     32#define OMAP4430_DPLL_REGM4XEN_MASK			BIT(11)
     33
     34/* Static rate multiplier for OMAP4 REGM4XEN clocks */
     35#define OMAP4430_REGM4XEN_MULT				4
     36
     37static void omap4_dpllmx_allow_gatectrl(struct clk_hw_omap *clk)
     38{
     39	u32 v;
     40	u32 mask;
     41
     42	if (!clk)
     43		return;
     44
     45	mask = clk->flags & CLOCK_CLKOUTX2 ?
     46			OMAP4430_DPLL_CLKOUTX2_GATE_CTRL_MASK :
     47			OMAP4430_DPLL_CLKOUT_GATE_CTRL_MASK;
     48
     49	v = ti_clk_ll_ops->clk_readl(&clk->clksel_reg);
     50	/* Clear the bit to allow gatectrl */
     51	v &= ~mask;
     52	ti_clk_ll_ops->clk_writel(v, &clk->clksel_reg);
     53}
     54
     55static void omap4_dpllmx_deny_gatectrl(struct clk_hw_omap *clk)
     56{
     57	u32 v;
     58	u32 mask;
     59
     60	if (!clk)
     61		return;
     62
     63	mask = clk->flags & CLOCK_CLKOUTX2 ?
     64			OMAP4430_DPLL_CLKOUTX2_GATE_CTRL_MASK :
     65			OMAP4430_DPLL_CLKOUT_GATE_CTRL_MASK;
     66
     67	v = ti_clk_ll_ops->clk_readl(&clk->clksel_reg);
     68	/* Set the bit to deny gatectrl */
     69	v |= mask;
     70	ti_clk_ll_ops->clk_writel(v, &clk->clksel_reg);
     71}
     72
     73const struct clk_hw_omap_ops clkhwops_omap4_dpllmx = {
     74	.allow_idle	= omap4_dpllmx_allow_gatectrl,
     75	.deny_idle      = omap4_dpllmx_deny_gatectrl,
     76};
     77
     78/**
     79 * omap4_dpll_lpmode_recalc - compute DPLL low-power setting
     80 * @dd: pointer to the dpll data structure
     81 *
     82 * Calculates if low-power mode can be enabled based upon the last
     83 * multiplier and divider values calculated. If low-power mode can be
     84 * enabled, then the bit to enable low-power mode is stored in the
     85 * last_rounded_lpmode variable. This implementation is based upon the
     86 * criteria for enabling low-power mode as described in the OMAP4430/60
     87 * Public TRM section 3.6.3.3.2 "Enable Control, Status, and Low-Power
     88 * Operation Mode".
     89 */
     90static void omap4_dpll_lpmode_recalc(struct dpll_data *dd)
     91{
     92	long fint, fout;
     93
     94	fint = clk_hw_get_rate(dd->clk_ref) / (dd->last_rounded_n + 1);
     95	fout = fint * dd->last_rounded_m;
     96
     97	if ((fint < OMAP4_DPLL_LP_FINT_MAX) && (fout < OMAP4_DPLL_LP_FOUT_MAX))
     98		dd->last_rounded_lpmode = 1;
     99	else
    100		dd->last_rounded_lpmode = 0;
    101}
    102
    103/**
    104 * omap4_dpll_regm4xen_recalc - compute DPLL rate, considering REGM4XEN bit
    105 * @hw: pointer to the clock to compute the rate for
    106 * @parent_rate: clock rate of the DPLL parent
    107 *
    108 * Compute the output rate for the OMAP4 DPLL represented by @clk.
    109 * Takes the REGM4XEN bit into consideration, which is needed for the
    110 * OMAP4 ABE DPLL.  Returns the DPLL's output rate (before M-dividers)
    111 * upon success, or 0 upon error.
    112 */
    113unsigned long omap4_dpll_regm4xen_recalc(struct clk_hw *hw,
    114					 unsigned long parent_rate)
    115{
    116	struct clk_hw_omap *clk = to_clk_hw_omap(hw);
    117	u32 v;
    118	unsigned long rate;
    119	struct dpll_data *dd;
    120
    121	if (!clk || !clk->dpll_data)
    122		return 0;
    123
    124	dd = clk->dpll_data;
    125
    126	rate = omap2_get_dpll_rate(clk);
    127
    128	/* regm4xen adds a multiplier of 4 to DPLL calculations */
    129	v = ti_clk_ll_ops->clk_readl(&dd->control_reg);
    130	if (v & OMAP4430_DPLL_REGM4XEN_MASK)
    131		rate *= OMAP4430_REGM4XEN_MULT;
    132
    133	return rate;
    134}
    135
    136/**
    137 * omap4_dpll_regm4xen_round_rate - round DPLL rate, considering REGM4XEN bit
    138 * @hw: struct hw_clk containing the struct clk * of the DPLL to round a rate for
    139 * @target_rate: the desired rate of the DPLL
    140 * @parent_rate: clock rate of the DPLL parent
    141 *
    142 * Compute the rate that would be programmed into the DPLL hardware
    143 * for @clk if set_rate() were to be provided with the rate
    144 * @target_rate.  Takes the REGM4XEN bit into consideration, which is
    145 * needed for the OMAP4 ABE DPLL.  Returns the rounded rate (before
    146 * M-dividers) upon success, -EINVAL if @clk is null or not a DPLL, or
    147 * ~0 if an error occurred in omap2_dpll_round_rate().
    148 */
    149long omap4_dpll_regm4xen_round_rate(struct clk_hw *hw,
    150				    unsigned long target_rate,
    151				    unsigned long *parent_rate)
    152{
    153	struct clk_hw_omap *clk = to_clk_hw_omap(hw);
    154	struct dpll_data *dd;
    155	long r;
    156
    157	if (!clk || !clk->dpll_data)
    158		return -EINVAL;
    159
    160	dd = clk->dpll_data;
    161
    162	dd->last_rounded_m4xen = 0;
    163
    164	/*
    165	 * First try to compute the DPLL configuration for
    166	 * target rate without using the 4X multiplier.
    167	 */
    168	r = omap2_dpll_round_rate(hw, target_rate, NULL);
    169	if (r != ~0)
    170		goto out;
    171
    172	/*
    173	 * If we did not find a valid DPLL configuration, try again, but
    174	 * this time see if using the 4X multiplier can help. Enabling the
    175	 * 4X multiplier is equivalent to dividing the target rate by 4.
    176	 */
    177	r = omap2_dpll_round_rate(hw, target_rate / OMAP4430_REGM4XEN_MULT,
    178				  NULL);
    179	if (r == ~0)
    180		return r;
    181
    182	dd->last_rounded_rate *= OMAP4430_REGM4XEN_MULT;
    183	dd->last_rounded_m4xen = 1;
    184
    185out:
    186	omap4_dpll_lpmode_recalc(dd);
    187
    188	return dd->last_rounded_rate;
    189}
    190
    191/**
    192 * omap4_dpll_regm4xen_determine_rate - determine rate for a DPLL
    193 * @hw: pointer to the clock to determine rate for
    194 * @req: target rate request
    195 *
    196 * Determines which DPLL mode to use for reaching a desired rate.
    197 * Checks whether the DPLL shall be in bypass or locked mode, and if
    198 * locked, calculates the M,N values for the DPLL via round-rate.
    199 * Returns 0 on success and a negative error value otherwise.
    200 */
    201int omap4_dpll_regm4xen_determine_rate(struct clk_hw *hw,
    202				       struct clk_rate_request *req)
    203{
    204	struct clk_hw_omap *clk = to_clk_hw_omap(hw);
    205	struct dpll_data *dd;
    206
    207	if (!req->rate)
    208		return -EINVAL;
    209
    210	dd = clk->dpll_data;
    211	if (!dd)
    212		return -EINVAL;
    213
    214	if (clk_hw_get_rate(dd->clk_bypass) == req->rate &&
    215	    (dd->modes & (1 << DPLL_LOW_POWER_BYPASS))) {
    216		req->best_parent_hw = dd->clk_bypass;
    217	} else {
    218		req->rate = omap4_dpll_regm4xen_round_rate(hw, req->rate,
    219						&req->best_parent_rate);
    220		req->best_parent_hw = dd->clk_ref;
    221	}
    222
    223	req->best_parent_rate = req->rate;
    224
    225	return 0;
    226}