162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * OMAP4-specific DPLL control functions 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2011 Texas Instruments, Inc. 662306a36Sopenharmony_ci * Rajendra Nayak 762306a36Sopenharmony_ci */ 862306a36Sopenharmony_ci 962306a36Sopenharmony_ci#include <linux/kernel.h> 1062306a36Sopenharmony_ci#include <linux/errno.h> 1162306a36Sopenharmony_ci#include <linux/clk.h> 1262306a36Sopenharmony_ci#include <linux/io.h> 1362306a36Sopenharmony_ci#include <linux/bitops.h> 1462306a36Sopenharmony_ci#include <linux/clk/ti.h> 1562306a36Sopenharmony_ci 1662306a36Sopenharmony_ci#include "clock.h" 1762306a36Sopenharmony_ci 1862306a36Sopenharmony_ci/* 1962306a36Sopenharmony_ci * Maximum DPLL input frequency (FINT) and output frequency (FOUT) that 2062306a36Sopenharmony_ci * can supported when using the DPLL low-power mode. Frequencies are 2162306a36Sopenharmony_ci * defined in OMAP4430/60 Public TRM section 3.6.3.3.2 "Enable Control, 2262306a36Sopenharmony_ci * Status, and Low-Power Operation Mode". 2362306a36Sopenharmony_ci */ 2462306a36Sopenharmony_ci#define OMAP4_DPLL_LP_FINT_MAX 1000000 2562306a36Sopenharmony_ci#define OMAP4_DPLL_LP_FOUT_MAX 100000000 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_ci/* 2862306a36Sopenharmony_ci * Bitfield declarations 2962306a36Sopenharmony_ci */ 3062306a36Sopenharmony_ci#define OMAP4430_DPLL_CLKOUT_GATE_CTRL_MASK BIT(8) 3162306a36Sopenharmony_ci#define OMAP4430_DPLL_CLKOUTX2_GATE_CTRL_MASK BIT(10) 3262306a36Sopenharmony_ci#define OMAP4430_DPLL_REGM4XEN_MASK BIT(11) 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_ci/* Static rate multiplier for OMAP4 REGM4XEN clocks */ 3562306a36Sopenharmony_ci#define OMAP4430_REGM4XEN_MULT 4 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_cistatic void omap4_dpllmx_allow_gatectrl(struct clk_hw_omap *clk) 3862306a36Sopenharmony_ci{ 3962306a36Sopenharmony_ci u32 v; 4062306a36Sopenharmony_ci u32 mask; 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_ci if (!clk) 4362306a36Sopenharmony_ci return; 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_ci mask = clk->flags & CLOCK_CLKOUTX2 ? 4662306a36Sopenharmony_ci OMAP4430_DPLL_CLKOUTX2_GATE_CTRL_MASK : 4762306a36Sopenharmony_ci OMAP4430_DPLL_CLKOUT_GATE_CTRL_MASK; 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_ci v = ti_clk_ll_ops->clk_readl(&clk->clksel_reg); 5062306a36Sopenharmony_ci /* Clear the bit to allow gatectrl */ 5162306a36Sopenharmony_ci v &= ~mask; 5262306a36Sopenharmony_ci ti_clk_ll_ops->clk_writel(v, &clk->clksel_reg); 5362306a36Sopenharmony_ci} 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_cistatic void omap4_dpllmx_deny_gatectrl(struct clk_hw_omap *clk) 5662306a36Sopenharmony_ci{ 5762306a36Sopenharmony_ci u32 v; 5862306a36Sopenharmony_ci u32 mask; 5962306a36Sopenharmony_ci 6062306a36Sopenharmony_ci if (!clk) 6162306a36Sopenharmony_ci return; 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_ci mask = clk->flags & CLOCK_CLKOUTX2 ? 6462306a36Sopenharmony_ci OMAP4430_DPLL_CLKOUTX2_GATE_CTRL_MASK : 6562306a36Sopenharmony_ci OMAP4430_DPLL_CLKOUT_GATE_CTRL_MASK; 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_ci v = ti_clk_ll_ops->clk_readl(&clk->clksel_reg); 6862306a36Sopenharmony_ci /* Set the bit to deny gatectrl */ 6962306a36Sopenharmony_ci v |= mask; 7062306a36Sopenharmony_ci ti_clk_ll_ops->clk_writel(v, &clk->clksel_reg); 7162306a36Sopenharmony_ci} 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_ciconst struct clk_hw_omap_ops clkhwops_omap4_dpllmx = { 7462306a36Sopenharmony_ci .allow_idle = omap4_dpllmx_allow_gatectrl, 7562306a36Sopenharmony_ci .deny_idle = omap4_dpllmx_deny_gatectrl, 7662306a36Sopenharmony_ci}; 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_ci/** 7962306a36Sopenharmony_ci * omap4_dpll_lpmode_recalc - compute DPLL low-power setting 8062306a36Sopenharmony_ci * @dd: pointer to the dpll data structure 8162306a36Sopenharmony_ci * 8262306a36Sopenharmony_ci * Calculates if low-power mode can be enabled based upon the last 8362306a36Sopenharmony_ci * multiplier and divider values calculated. If low-power mode can be 8462306a36Sopenharmony_ci * enabled, then the bit to enable low-power mode is stored in the 8562306a36Sopenharmony_ci * last_rounded_lpmode variable. This implementation is based upon the 8662306a36Sopenharmony_ci * criteria for enabling low-power mode as described in the OMAP4430/60 8762306a36Sopenharmony_ci * Public TRM section 3.6.3.3.2 "Enable Control, Status, and Low-Power 8862306a36Sopenharmony_ci * Operation Mode". 8962306a36Sopenharmony_ci */ 9062306a36Sopenharmony_cistatic void omap4_dpll_lpmode_recalc(struct dpll_data *dd) 9162306a36Sopenharmony_ci{ 9262306a36Sopenharmony_ci long fint, fout; 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_ci fint = clk_hw_get_rate(dd->clk_ref) / (dd->last_rounded_n + 1); 9562306a36Sopenharmony_ci fout = fint * dd->last_rounded_m; 9662306a36Sopenharmony_ci 9762306a36Sopenharmony_ci if ((fint < OMAP4_DPLL_LP_FINT_MAX) && (fout < OMAP4_DPLL_LP_FOUT_MAX)) 9862306a36Sopenharmony_ci dd->last_rounded_lpmode = 1; 9962306a36Sopenharmony_ci else 10062306a36Sopenharmony_ci dd->last_rounded_lpmode = 0; 10162306a36Sopenharmony_ci} 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_ci/** 10462306a36Sopenharmony_ci * omap4_dpll_regm4xen_recalc - compute DPLL rate, considering REGM4XEN bit 10562306a36Sopenharmony_ci * @hw: pointer to the clock to compute the rate for 10662306a36Sopenharmony_ci * @parent_rate: clock rate of the DPLL parent 10762306a36Sopenharmony_ci * 10862306a36Sopenharmony_ci * Compute the output rate for the OMAP4 DPLL represented by @clk. 10962306a36Sopenharmony_ci * Takes the REGM4XEN bit into consideration, which is needed for the 11062306a36Sopenharmony_ci * OMAP4 ABE DPLL. Returns the DPLL's output rate (before M-dividers) 11162306a36Sopenharmony_ci * upon success, or 0 upon error. 11262306a36Sopenharmony_ci */ 11362306a36Sopenharmony_ciunsigned long omap4_dpll_regm4xen_recalc(struct clk_hw *hw, 11462306a36Sopenharmony_ci unsigned long parent_rate) 11562306a36Sopenharmony_ci{ 11662306a36Sopenharmony_ci struct clk_hw_omap *clk = to_clk_hw_omap(hw); 11762306a36Sopenharmony_ci u32 v; 11862306a36Sopenharmony_ci unsigned long rate; 11962306a36Sopenharmony_ci struct dpll_data *dd; 12062306a36Sopenharmony_ci 12162306a36Sopenharmony_ci if (!clk || !clk->dpll_data) 12262306a36Sopenharmony_ci return 0; 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_ci dd = clk->dpll_data; 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_ci rate = omap2_get_dpll_rate(clk); 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_ci /* regm4xen adds a multiplier of 4 to DPLL calculations */ 12962306a36Sopenharmony_ci v = ti_clk_ll_ops->clk_readl(&dd->control_reg); 13062306a36Sopenharmony_ci if (v & OMAP4430_DPLL_REGM4XEN_MASK) 13162306a36Sopenharmony_ci rate *= OMAP4430_REGM4XEN_MULT; 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_ci return rate; 13462306a36Sopenharmony_ci} 13562306a36Sopenharmony_ci 13662306a36Sopenharmony_ci/** 13762306a36Sopenharmony_ci * omap4_dpll_regm4xen_round_rate - round DPLL rate, considering REGM4XEN bit 13862306a36Sopenharmony_ci * @hw: struct hw_clk containing the struct clk * of the DPLL to round a rate for 13962306a36Sopenharmony_ci * @target_rate: the desired rate of the DPLL 14062306a36Sopenharmony_ci * @parent_rate: clock rate of the DPLL parent 14162306a36Sopenharmony_ci * 14262306a36Sopenharmony_ci * Compute the rate that would be programmed into the DPLL hardware 14362306a36Sopenharmony_ci * for @clk if set_rate() were to be provided with the rate 14462306a36Sopenharmony_ci * @target_rate. Takes the REGM4XEN bit into consideration, which is 14562306a36Sopenharmony_ci * needed for the OMAP4 ABE DPLL. Returns the rounded rate (before 14662306a36Sopenharmony_ci * M-dividers) upon success, -EINVAL if @clk is null or not a DPLL, or 14762306a36Sopenharmony_ci * ~0 if an error occurred in omap2_dpll_round_rate(). 14862306a36Sopenharmony_ci */ 14962306a36Sopenharmony_cilong omap4_dpll_regm4xen_round_rate(struct clk_hw *hw, 15062306a36Sopenharmony_ci unsigned long target_rate, 15162306a36Sopenharmony_ci unsigned long *parent_rate) 15262306a36Sopenharmony_ci{ 15362306a36Sopenharmony_ci struct clk_hw_omap *clk = to_clk_hw_omap(hw); 15462306a36Sopenharmony_ci struct dpll_data *dd; 15562306a36Sopenharmony_ci long r; 15662306a36Sopenharmony_ci 15762306a36Sopenharmony_ci if (!clk || !clk->dpll_data) 15862306a36Sopenharmony_ci return -EINVAL; 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_ci dd = clk->dpll_data; 16162306a36Sopenharmony_ci 16262306a36Sopenharmony_ci dd->last_rounded_m4xen = 0; 16362306a36Sopenharmony_ci 16462306a36Sopenharmony_ci /* 16562306a36Sopenharmony_ci * First try to compute the DPLL configuration for 16662306a36Sopenharmony_ci * target rate without using the 4X multiplier. 16762306a36Sopenharmony_ci */ 16862306a36Sopenharmony_ci r = omap2_dpll_round_rate(hw, target_rate, NULL); 16962306a36Sopenharmony_ci if (r != ~0) 17062306a36Sopenharmony_ci goto out; 17162306a36Sopenharmony_ci 17262306a36Sopenharmony_ci /* 17362306a36Sopenharmony_ci * If we did not find a valid DPLL configuration, try again, but 17462306a36Sopenharmony_ci * this time see if using the 4X multiplier can help. Enabling the 17562306a36Sopenharmony_ci * 4X multiplier is equivalent to dividing the target rate by 4. 17662306a36Sopenharmony_ci */ 17762306a36Sopenharmony_ci r = omap2_dpll_round_rate(hw, target_rate / OMAP4430_REGM4XEN_MULT, 17862306a36Sopenharmony_ci NULL); 17962306a36Sopenharmony_ci if (r == ~0) 18062306a36Sopenharmony_ci return r; 18162306a36Sopenharmony_ci 18262306a36Sopenharmony_ci dd->last_rounded_rate *= OMAP4430_REGM4XEN_MULT; 18362306a36Sopenharmony_ci dd->last_rounded_m4xen = 1; 18462306a36Sopenharmony_ci 18562306a36Sopenharmony_ciout: 18662306a36Sopenharmony_ci omap4_dpll_lpmode_recalc(dd); 18762306a36Sopenharmony_ci 18862306a36Sopenharmony_ci return dd->last_rounded_rate; 18962306a36Sopenharmony_ci} 19062306a36Sopenharmony_ci 19162306a36Sopenharmony_ci/** 19262306a36Sopenharmony_ci * omap4_dpll_regm4xen_determine_rate - determine rate for a DPLL 19362306a36Sopenharmony_ci * @hw: pointer to the clock to determine rate for 19462306a36Sopenharmony_ci * @req: target rate request 19562306a36Sopenharmony_ci * 19662306a36Sopenharmony_ci * Determines which DPLL mode to use for reaching a desired rate. 19762306a36Sopenharmony_ci * Checks whether the DPLL shall be in bypass or locked mode, and if 19862306a36Sopenharmony_ci * locked, calculates the M,N values for the DPLL via round-rate. 19962306a36Sopenharmony_ci * Returns 0 on success and a negative error value otherwise. 20062306a36Sopenharmony_ci */ 20162306a36Sopenharmony_ciint omap4_dpll_regm4xen_determine_rate(struct clk_hw *hw, 20262306a36Sopenharmony_ci struct clk_rate_request *req) 20362306a36Sopenharmony_ci{ 20462306a36Sopenharmony_ci struct clk_hw_omap *clk = to_clk_hw_omap(hw); 20562306a36Sopenharmony_ci struct dpll_data *dd; 20662306a36Sopenharmony_ci 20762306a36Sopenharmony_ci if (!req->rate) 20862306a36Sopenharmony_ci return -EINVAL; 20962306a36Sopenharmony_ci 21062306a36Sopenharmony_ci dd = clk->dpll_data; 21162306a36Sopenharmony_ci if (!dd) 21262306a36Sopenharmony_ci return -EINVAL; 21362306a36Sopenharmony_ci 21462306a36Sopenharmony_ci if (clk_hw_get_rate(dd->clk_bypass) == req->rate && 21562306a36Sopenharmony_ci (dd->modes & (1 << DPLL_LOW_POWER_BYPASS))) { 21662306a36Sopenharmony_ci req->best_parent_hw = dd->clk_bypass; 21762306a36Sopenharmony_ci } else { 21862306a36Sopenharmony_ci req->rate = omap4_dpll_regm4xen_round_rate(hw, req->rate, 21962306a36Sopenharmony_ci &req->best_parent_rate); 22062306a36Sopenharmony_ci req->best_parent_hw = dd->clk_ref; 22162306a36Sopenharmony_ci } 22262306a36Sopenharmony_ci 22362306a36Sopenharmony_ci req->best_parent_rate = req->rate; 22462306a36Sopenharmony_ci 22562306a36Sopenharmony_ci return 0; 22662306a36Sopenharmony_ci} 227