162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * OMAP3/4 - specific DPLL control functions 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2009-2010 Texas Instruments, Inc. 662306a36Sopenharmony_ci * Copyright (C) 2009-2010 Nokia Corporation 762306a36Sopenharmony_ci * 862306a36Sopenharmony_ci * Written by Paul Walmsley 962306a36Sopenharmony_ci * Testing and integration fixes by Jouni Högander 1062306a36Sopenharmony_ci * 1162306a36Sopenharmony_ci * 36xx support added by Vishwanath BS, Richard Woodruff, and Nishanth 1262306a36Sopenharmony_ci * Menon 1362306a36Sopenharmony_ci * 1462306a36Sopenharmony_ci * Parts of this code are based on code written by 1562306a36Sopenharmony_ci * Richard Woodruff, Tony Lindgren, Tuukka Tikkanen, Karthik Dasu 1662306a36Sopenharmony_ci */ 1762306a36Sopenharmony_ci 1862306a36Sopenharmony_ci#include <linux/kernel.h> 1962306a36Sopenharmony_ci#include <linux/device.h> 2062306a36Sopenharmony_ci#include <linux/list.h> 2162306a36Sopenharmony_ci#include <linux/errno.h> 2262306a36Sopenharmony_ci#include <linux/delay.h> 2362306a36Sopenharmony_ci#include <linux/clk.h> 2462306a36Sopenharmony_ci#include <linux/io.h> 2562306a36Sopenharmony_ci#include <linux/bitops.h> 2662306a36Sopenharmony_ci#include <linux/clkdev.h> 2762306a36Sopenharmony_ci#include <linux/clk/ti.h> 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_ci#include "clock.h" 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_ci/* CM_AUTOIDLE_PLL*.AUTO_* bit values */ 3262306a36Sopenharmony_ci#define DPLL_AUTOIDLE_DISABLE 0x0 3362306a36Sopenharmony_ci#define DPLL_AUTOIDLE_LOW_POWER_STOP 0x1 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_ci#define MAX_DPLL_WAIT_TRIES 1000000 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_ci#define OMAP3XXX_EN_DPLL_LOCKED 0x7 3862306a36Sopenharmony_ci 3962306a36Sopenharmony_ci/* Forward declarations */ 4062306a36Sopenharmony_cistatic u32 omap3_dpll_autoidle_read(struct clk_hw_omap *clk); 4162306a36Sopenharmony_cistatic void omap3_dpll_deny_idle(struct clk_hw_omap *clk); 4262306a36Sopenharmony_cistatic void omap3_dpll_allow_idle(struct clk_hw_omap *clk); 4362306a36Sopenharmony_ci 4462306a36Sopenharmony_ci/* Private functions */ 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_ci/* _omap3_dpll_write_clken - write clken_bits arg to a DPLL's enable bits */ 4762306a36Sopenharmony_cistatic void _omap3_dpll_write_clken(struct clk_hw_omap *clk, u8 clken_bits) 4862306a36Sopenharmony_ci{ 4962306a36Sopenharmony_ci const struct dpll_data *dd; 5062306a36Sopenharmony_ci u32 v; 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_ci dd = clk->dpll_data; 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_ci v = ti_clk_ll_ops->clk_readl(&dd->control_reg); 5562306a36Sopenharmony_ci v &= ~dd->enable_mask; 5662306a36Sopenharmony_ci v |= clken_bits << __ffs(dd->enable_mask); 5762306a36Sopenharmony_ci ti_clk_ll_ops->clk_writel(v, &dd->control_reg); 5862306a36Sopenharmony_ci} 5962306a36Sopenharmony_ci 6062306a36Sopenharmony_ci/* _omap3_wait_dpll_status: wait for a DPLL to enter a specific state */ 6162306a36Sopenharmony_cistatic int _omap3_wait_dpll_status(struct clk_hw_omap *clk, u8 state) 6262306a36Sopenharmony_ci{ 6362306a36Sopenharmony_ci const struct dpll_data *dd; 6462306a36Sopenharmony_ci int i = 0; 6562306a36Sopenharmony_ci int ret = -EINVAL; 6662306a36Sopenharmony_ci const char *clk_name; 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_ci dd = clk->dpll_data; 6962306a36Sopenharmony_ci clk_name = clk_hw_get_name(&clk->hw); 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_ci state <<= __ffs(dd->idlest_mask); 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_ci while (((ti_clk_ll_ops->clk_readl(&dd->idlest_reg) & dd->idlest_mask) 7462306a36Sopenharmony_ci != state) && i < MAX_DPLL_WAIT_TRIES) { 7562306a36Sopenharmony_ci i++; 7662306a36Sopenharmony_ci udelay(1); 7762306a36Sopenharmony_ci } 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_ci if (i == MAX_DPLL_WAIT_TRIES) { 8062306a36Sopenharmony_ci pr_err("clock: %s failed transition to '%s'\n", 8162306a36Sopenharmony_ci clk_name, (state) ? "locked" : "bypassed"); 8262306a36Sopenharmony_ci } else { 8362306a36Sopenharmony_ci pr_debug("clock: %s transition to '%s' in %d loops\n", 8462306a36Sopenharmony_ci clk_name, (state) ? "locked" : "bypassed", i); 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_ci ret = 0; 8762306a36Sopenharmony_ci } 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_ci return ret; 9062306a36Sopenharmony_ci} 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_ci/* From 3430 TRM ES2 4.7.6.2 */ 9362306a36Sopenharmony_cistatic u16 _omap3_dpll_compute_freqsel(struct clk_hw_omap *clk, u8 n) 9462306a36Sopenharmony_ci{ 9562306a36Sopenharmony_ci unsigned long fint; 9662306a36Sopenharmony_ci u16 f = 0; 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_ci fint = clk_hw_get_rate(clk->dpll_data->clk_ref) / n; 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_ci pr_debug("clock: fint is %lu\n", fint); 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_ci if (fint >= 750000 && fint <= 1000000) 10362306a36Sopenharmony_ci f = 0x3; 10462306a36Sopenharmony_ci else if (fint > 1000000 && fint <= 1250000) 10562306a36Sopenharmony_ci f = 0x4; 10662306a36Sopenharmony_ci else if (fint > 1250000 && fint <= 1500000) 10762306a36Sopenharmony_ci f = 0x5; 10862306a36Sopenharmony_ci else if (fint > 1500000 && fint <= 1750000) 10962306a36Sopenharmony_ci f = 0x6; 11062306a36Sopenharmony_ci else if (fint > 1750000 && fint <= 2100000) 11162306a36Sopenharmony_ci f = 0x7; 11262306a36Sopenharmony_ci else if (fint > 7500000 && fint <= 10000000) 11362306a36Sopenharmony_ci f = 0xB; 11462306a36Sopenharmony_ci else if (fint > 10000000 && fint <= 12500000) 11562306a36Sopenharmony_ci f = 0xC; 11662306a36Sopenharmony_ci else if (fint > 12500000 && fint <= 15000000) 11762306a36Sopenharmony_ci f = 0xD; 11862306a36Sopenharmony_ci else if (fint > 15000000 && fint <= 17500000) 11962306a36Sopenharmony_ci f = 0xE; 12062306a36Sopenharmony_ci else if (fint > 17500000 && fint <= 21000000) 12162306a36Sopenharmony_ci f = 0xF; 12262306a36Sopenharmony_ci else 12362306a36Sopenharmony_ci pr_debug("clock: unknown freqsel setting for %d\n", n); 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_ci return f; 12662306a36Sopenharmony_ci} 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_ci/** 12962306a36Sopenharmony_ci * _omap3_noncore_dpll_lock - instruct a DPLL to lock and wait for readiness 13062306a36Sopenharmony_ci * @clk: pointer to a DPLL struct clk 13162306a36Sopenharmony_ci * 13262306a36Sopenharmony_ci * Instructs a non-CORE DPLL to lock. Waits for the DPLL to report 13362306a36Sopenharmony_ci * readiness before returning. Will save and restore the DPLL's 13462306a36Sopenharmony_ci * autoidle state across the enable, per the CDP code. If the DPLL 13562306a36Sopenharmony_ci * locked successfully, return 0; if the DPLL did not lock in the time 13662306a36Sopenharmony_ci * allotted, or DPLL3 was passed in, return -EINVAL. 13762306a36Sopenharmony_ci */ 13862306a36Sopenharmony_cistatic int _omap3_noncore_dpll_lock(struct clk_hw_omap *clk) 13962306a36Sopenharmony_ci{ 14062306a36Sopenharmony_ci const struct dpll_data *dd; 14162306a36Sopenharmony_ci u8 ai; 14262306a36Sopenharmony_ci u8 state = 1; 14362306a36Sopenharmony_ci int r = 0; 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_ci pr_debug("clock: locking DPLL %s\n", clk_hw_get_name(&clk->hw)); 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_ci dd = clk->dpll_data; 14862306a36Sopenharmony_ci state <<= __ffs(dd->idlest_mask); 14962306a36Sopenharmony_ci 15062306a36Sopenharmony_ci /* Check if already locked */ 15162306a36Sopenharmony_ci if ((ti_clk_ll_ops->clk_readl(&dd->idlest_reg) & dd->idlest_mask) == 15262306a36Sopenharmony_ci state) 15362306a36Sopenharmony_ci goto done; 15462306a36Sopenharmony_ci 15562306a36Sopenharmony_ci ai = omap3_dpll_autoidle_read(clk); 15662306a36Sopenharmony_ci 15762306a36Sopenharmony_ci if (ai) 15862306a36Sopenharmony_ci omap3_dpll_deny_idle(clk); 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_ci _omap3_dpll_write_clken(clk, DPLL_LOCKED); 16162306a36Sopenharmony_ci 16262306a36Sopenharmony_ci r = _omap3_wait_dpll_status(clk, 1); 16362306a36Sopenharmony_ci 16462306a36Sopenharmony_ci if (ai) 16562306a36Sopenharmony_ci omap3_dpll_allow_idle(clk); 16662306a36Sopenharmony_ci 16762306a36Sopenharmony_cidone: 16862306a36Sopenharmony_ci return r; 16962306a36Sopenharmony_ci} 17062306a36Sopenharmony_ci 17162306a36Sopenharmony_ci/** 17262306a36Sopenharmony_ci * _omap3_noncore_dpll_bypass - instruct a DPLL to bypass and wait for readiness 17362306a36Sopenharmony_ci * @clk: pointer to a DPLL struct clk 17462306a36Sopenharmony_ci * 17562306a36Sopenharmony_ci * Instructs a non-CORE DPLL to enter low-power bypass mode. In 17662306a36Sopenharmony_ci * bypass mode, the DPLL's rate is set equal to its parent clock's 17762306a36Sopenharmony_ci * rate. Waits for the DPLL to report readiness before returning. 17862306a36Sopenharmony_ci * Will save and restore the DPLL's autoidle state across the enable, 17962306a36Sopenharmony_ci * per the CDP code. If the DPLL entered bypass mode successfully, 18062306a36Sopenharmony_ci * return 0; if the DPLL did not enter bypass in the time allotted, or 18162306a36Sopenharmony_ci * DPLL3 was passed in, or the DPLL does not support low-power bypass, 18262306a36Sopenharmony_ci * return -EINVAL. 18362306a36Sopenharmony_ci */ 18462306a36Sopenharmony_cistatic int _omap3_noncore_dpll_bypass(struct clk_hw_omap *clk) 18562306a36Sopenharmony_ci{ 18662306a36Sopenharmony_ci int r; 18762306a36Sopenharmony_ci u8 ai; 18862306a36Sopenharmony_ci 18962306a36Sopenharmony_ci if (!(clk->dpll_data->modes & (1 << DPLL_LOW_POWER_BYPASS))) 19062306a36Sopenharmony_ci return -EINVAL; 19162306a36Sopenharmony_ci 19262306a36Sopenharmony_ci pr_debug("clock: configuring DPLL %s for low-power bypass\n", 19362306a36Sopenharmony_ci clk_hw_get_name(&clk->hw)); 19462306a36Sopenharmony_ci 19562306a36Sopenharmony_ci ai = omap3_dpll_autoidle_read(clk); 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_ci _omap3_dpll_write_clken(clk, DPLL_LOW_POWER_BYPASS); 19862306a36Sopenharmony_ci 19962306a36Sopenharmony_ci r = _omap3_wait_dpll_status(clk, 0); 20062306a36Sopenharmony_ci 20162306a36Sopenharmony_ci if (ai) 20262306a36Sopenharmony_ci omap3_dpll_allow_idle(clk); 20362306a36Sopenharmony_ci 20462306a36Sopenharmony_ci return r; 20562306a36Sopenharmony_ci} 20662306a36Sopenharmony_ci 20762306a36Sopenharmony_ci/** 20862306a36Sopenharmony_ci * _omap3_noncore_dpll_stop - instruct a DPLL to stop 20962306a36Sopenharmony_ci * @clk: pointer to a DPLL struct clk 21062306a36Sopenharmony_ci * 21162306a36Sopenharmony_ci * Instructs a non-CORE DPLL to enter low-power stop. Will save and 21262306a36Sopenharmony_ci * restore the DPLL's autoidle state across the stop, per the CDP 21362306a36Sopenharmony_ci * code. If DPLL3 was passed in, or the DPLL does not support 21462306a36Sopenharmony_ci * low-power stop, return -EINVAL; otherwise, return 0. 21562306a36Sopenharmony_ci */ 21662306a36Sopenharmony_cistatic int _omap3_noncore_dpll_stop(struct clk_hw_omap *clk) 21762306a36Sopenharmony_ci{ 21862306a36Sopenharmony_ci u8 ai; 21962306a36Sopenharmony_ci 22062306a36Sopenharmony_ci if (!(clk->dpll_data->modes & (1 << DPLL_LOW_POWER_STOP))) 22162306a36Sopenharmony_ci return -EINVAL; 22262306a36Sopenharmony_ci 22362306a36Sopenharmony_ci pr_debug("clock: stopping DPLL %s\n", clk_hw_get_name(&clk->hw)); 22462306a36Sopenharmony_ci 22562306a36Sopenharmony_ci ai = omap3_dpll_autoidle_read(clk); 22662306a36Sopenharmony_ci 22762306a36Sopenharmony_ci _omap3_dpll_write_clken(clk, DPLL_LOW_POWER_STOP); 22862306a36Sopenharmony_ci 22962306a36Sopenharmony_ci if (ai) 23062306a36Sopenharmony_ci omap3_dpll_allow_idle(clk); 23162306a36Sopenharmony_ci 23262306a36Sopenharmony_ci return 0; 23362306a36Sopenharmony_ci} 23462306a36Sopenharmony_ci 23562306a36Sopenharmony_ci/** 23662306a36Sopenharmony_ci * _lookup_dco - Lookup DCO used by j-type DPLL 23762306a36Sopenharmony_ci * @clk: pointer to a DPLL struct clk 23862306a36Sopenharmony_ci * @dco: digital control oscillator selector 23962306a36Sopenharmony_ci * @m: DPLL multiplier to set 24062306a36Sopenharmony_ci * @n: DPLL divider to set 24162306a36Sopenharmony_ci * 24262306a36Sopenharmony_ci * See 36xx TRM section 3.5.3.3.3.2 "Type B DPLL (Low-Jitter)" 24362306a36Sopenharmony_ci * 24462306a36Sopenharmony_ci * XXX This code is not needed for 3430/AM35xx; can it be optimized 24562306a36Sopenharmony_ci * out in non-multi-OMAP builds for those chips? 24662306a36Sopenharmony_ci */ 24762306a36Sopenharmony_cistatic void _lookup_dco(struct clk_hw_omap *clk, u8 *dco, u16 m, u8 n) 24862306a36Sopenharmony_ci{ 24962306a36Sopenharmony_ci unsigned long fint, clkinp; /* watch out for overflow */ 25062306a36Sopenharmony_ci 25162306a36Sopenharmony_ci clkinp = clk_hw_get_rate(clk_hw_get_parent(&clk->hw)); 25262306a36Sopenharmony_ci fint = (clkinp / n) * m; 25362306a36Sopenharmony_ci 25462306a36Sopenharmony_ci if (fint < 1000000000) 25562306a36Sopenharmony_ci *dco = 2; 25662306a36Sopenharmony_ci else 25762306a36Sopenharmony_ci *dco = 4; 25862306a36Sopenharmony_ci} 25962306a36Sopenharmony_ci 26062306a36Sopenharmony_ci/** 26162306a36Sopenharmony_ci * _lookup_sddiv - Calculate sigma delta divider for j-type DPLL 26262306a36Sopenharmony_ci * @clk: pointer to a DPLL struct clk 26362306a36Sopenharmony_ci * @sd_div: target sigma-delta divider 26462306a36Sopenharmony_ci * @m: DPLL multiplier to set 26562306a36Sopenharmony_ci * @n: DPLL divider to set 26662306a36Sopenharmony_ci * 26762306a36Sopenharmony_ci * See 36xx TRM section 3.5.3.3.3.2 "Type B DPLL (Low-Jitter)" 26862306a36Sopenharmony_ci * 26962306a36Sopenharmony_ci * XXX This code is not needed for 3430/AM35xx; can it be optimized 27062306a36Sopenharmony_ci * out in non-multi-OMAP builds for those chips? 27162306a36Sopenharmony_ci */ 27262306a36Sopenharmony_cistatic void _lookup_sddiv(struct clk_hw_omap *clk, u8 *sd_div, u16 m, u8 n) 27362306a36Sopenharmony_ci{ 27462306a36Sopenharmony_ci unsigned long clkinp, sd; /* watch out for overflow */ 27562306a36Sopenharmony_ci int mod1, mod2; 27662306a36Sopenharmony_ci 27762306a36Sopenharmony_ci clkinp = clk_hw_get_rate(clk_hw_get_parent(&clk->hw)); 27862306a36Sopenharmony_ci 27962306a36Sopenharmony_ci /* 28062306a36Sopenharmony_ci * target sigma-delta to near 250MHz 28162306a36Sopenharmony_ci * sd = ceil[(m/(n+1)) * (clkinp_MHz / 250)] 28262306a36Sopenharmony_ci */ 28362306a36Sopenharmony_ci clkinp /= 100000; /* shift from MHz to 10*Hz for 38.4 and 19.2 */ 28462306a36Sopenharmony_ci mod1 = (clkinp * m) % (250 * n); 28562306a36Sopenharmony_ci sd = (clkinp * m) / (250 * n); 28662306a36Sopenharmony_ci mod2 = sd % 10; 28762306a36Sopenharmony_ci sd /= 10; 28862306a36Sopenharmony_ci 28962306a36Sopenharmony_ci if (mod1 || mod2) 29062306a36Sopenharmony_ci sd++; 29162306a36Sopenharmony_ci *sd_div = sd; 29262306a36Sopenharmony_ci} 29362306a36Sopenharmony_ci 29462306a36Sopenharmony_ci/** 29562306a36Sopenharmony_ci * omap3_noncore_dpll_ssc_program - set spread-spectrum clocking registers 29662306a36Sopenharmony_ci * @clk: struct clk * of DPLL to set 29762306a36Sopenharmony_ci * 29862306a36Sopenharmony_ci * Enable the DPLL spread spectrum clocking if frequency modulation and 29962306a36Sopenharmony_ci * frequency spreading have been set, otherwise disable it. 30062306a36Sopenharmony_ci */ 30162306a36Sopenharmony_cistatic void omap3_noncore_dpll_ssc_program(struct clk_hw_omap *clk) 30262306a36Sopenharmony_ci{ 30362306a36Sopenharmony_ci struct dpll_data *dd = clk->dpll_data; 30462306a36Sopenharmony_ci unsigned long ref_rate; 30562306a36Sopenharmony_ci u32 v, ctrl, mod_freq_divider, exponent, mantissa; 30662306a36Sopenharmony_ci u32 deltam_step, deltam_ceil; 30762306a36Sopenharmony_ci 30862306a36Sopenharmony_ci ctrl = ti_clk_ll_ops->clk_readl(&dd->control_reg); 30962306a36Sopenharmony_ci 31062306a36Sopenharmony_ci if (dd->ssc_modfreq && dd->ssc_deltam) { 31162306a36Sopenharmony_ci ctrl |= dd->ssc_enable_mask; 31262306a36Sopenharmony_ci 31362306a36Sopenharmony_ci if (dd->ssc_downspread) 31462306a36Sopenharmony_ci ctrl |= dd->ssc_downspread_mask; 31562306a36Sopenharmony_ci else 31662306a36Sopenharmony_ci ctrl &= ~dd->ssc_downspread_mask; 31762306a36Sopenharmony_ci 31862306a36Sopenharmony_ci ref_rate = clk_hw_get_rate(dd->clk_ref); 31962306a36Sopenharmony_ci mod_freq_divider = 32062306a36Sopenharmony_ci (ref_rate / dd->last_rounded_n) / (4 * dd->ssc_modfreq); 32162306a36Sopenharmony_ci if (dd->ssc_modfreq > (ref_rate / 70)) 32262306a36Sopenharmony_ci pr_warn("clock: SSC modulation frequency of DPLL %s greater than %ld\n", 32362306a36Sopenharmony_ci __clk_get_name(clk->hw.clk), ref_rate / 70); 32462306a36Sopenharmony_ci 32562306a36Sopenharmony_ci exponent = 0; 32662306a36Sopenharmony_ci mantissa = mod_freq_divider; 32762306a36Sopenharmony_ci while ((mantissa > 127) && (exponent < 7)) { 32862306a36Sopenharmony_ci exponent++; 32962306a36Sopenharmony_ci mantissa /= 2; 33062306a36Sopenharmony_ci } 33162306a36Sopenharmony_ci if (mantissa > 127) 33262306a36Sopenharmony_ci mantissa = 127; 33362306a36Sopenharmony_ci 33462306a36Sopenharmony_ci v = ti_clk_ll_ops->clk_readl(&dd->ssc_modfreq_reg); 33562306a36Sopenharmony_ci v &= ~(dd->ssc_modfreq_mant_mask | dd->ssc_modfreq_exp_mask); 33662306a36Sopenharmony_ci v |= mantissa << __ffs(dd->ssc_modfreq_mant_mask); 33762306a36Sopenharmony_ci v |= exponent << __ffs(dd->ssc_modfreq_exp_mask); 33862306a36Sopenharmony_ci ti_clk_ll_ops->clk_writel(v, &dd->ssc_modfreq_reg); 33962306a36Sopenharmony_ci 34062306a36Sopenharmony_ci deltam_step = dd->last_rounded_m * dd->ssc_deltam; 34162306a36Sopenharmony_ci deltam_step /= 10; 34262306a36Sopenharmony_ci if (dd->ssc_downspread) 34362306a36Sopenharmony_ci deltam_step /= 2; 34462306a36Sopenharmony_ci 34562306a36Sopenharmony_ci deltam_step <<= __ffs(dd->ssc_deltam_int_mask); 34662306a36Sopenharmony_ci deltam_step /= 100; 34762306a36Sopenharmony_ci deltam_step /= mod_freq_divider; 34862306a36Sopenharmony_ci if (deltam_step > 0xFFFFF) 34962306a36Sopenharmony_ci deltam_step = 0xFFFFF; 35062306a36Sopenharmony_ci 35162306a36Sopenharmony_ci deltam_ceil = (deltam_step & dd->ssc_deltam_int_mask) >> 35262306a36Sopenharmony_ci __ffs(dd->ssc_deltam_int_mask); 35362306a36Sopenharmony_ci if (deltam_step & dd->ssc_deltam_frac_mask) 35462306a36Sopenharmony_ci deltam_ceil++; 35562306a36Sopenharmony_ci 35662306a36Sopenharmony_ci if ((dd->ssc_downspread && 35762306a36Sopenharmony_ci ((dd->last_rounded_m - (2 * deltam_ceil)) < 20 || 35862306a36Sopenharmony_ci dd->last_rounded_m > 2045)) || 35962306a36Sopenharmony_ci ((dd->last_rounded_m - deltam_ceil) < 20 || 36062306a36Sopenharmony_ci (dd->last_rounded_m + deltam_ceil) > 2045)) 36162306a36Sopenharmony_ci pr_warn("clock: SSC multiplier of DPLL %s is out of range\n", 36262306a36Sopenharmony_ci __clk_get_name(clk->hw.clk)); 36362306a36Sopenharmony_ci 36462306a36Sopenharmony_ci v = ti_clk_ll_ops->clk_readl(&dd->ssc_deltam_reg); 36562306a36Sopenharmony_ci v &= ~(dd->ssc_deltam_int_mask | dd->ssc_deltam_frac_mask); 36662306a36Sopenharmony_ci v |= deltam_step << __ffs(dd->ssc_deltam_int_mask | 36762306a36Sopenharmony_ci dd->ssc_deltam_frac_mask); 36862306a36Sopenharmony_ci ti_clk_ll_ops->clk_writel(v, &dd->ssc_deltam_reg); 36962306a36Sopenharmony_ci } else { 37062306a36Sopenharmony_ci ctrl &= ~dd->ssc_enable_mask; 37162306a36Sopenharmony_ci } 37262306a36Sopenharmony_ci 37362306a36Sopenharmony_ci ti_clk_ll_ops->clk_writel(ctrl, &dd->control_reg); 37462306a36Sopenharmony_ci} 37562306a36Sopenharmony_ci 37662306a36Sopenharmony_ci/** 37762306a36Sopenharmony_ci * omap3_noncore_dpll_program - set non-core DPLL M,N values directly 37862306a36Sopenharmony_ci * @clk: struct clk * of DPLL to set 37962306a36Sopenharmony_ci * @freqsel: FREQSEL value to set 38062306a36Sopenharmony_ci * 38162306a36Sopenharmony_ci * Program the DPLL with the last M, N values calculated, and wait for 38262306a36Sopenharmony_ci * the DPLL to lock. Returns -EINVAL upon error, or 0 upon success. 38362306a36Sopenharmony_ci */ 38462306a36Sopenharmony_cistatic int omap3_noncore_dpll_program(struct clk_hw_omap *clk, u16 freqsel) 38562306a36Sopenharmony_ci{ 38662306a36Sopenharmony_ci struct dpll_data *dd = clk->dpll_data; 38762306a36Sopenharmony_ci u8 dco, sd_div, ai = 0; 38862306a36Sopenharmony_ci u32 v; 38962306a36Sopenharmony_ci bool errata_i810; 39062306a36Sopenharmony_ci 39162306a36Sopenharmony_ci /* 3430 ES2 TRM: 4.7.6.9 DPLL Programming Sequence */ 39262306a36Sopenharmony_ci _omap3_noncore_dpll_bypass(clk); 39362306a36Sopenharmony_ci 39462306a36Sopenharmony_ci /* 39562306a36Sopenharmony_ci * Set jitter correction. Jitter correction applicable for OMAP343X 39662306a36Sopenharmony_ci * only since freqsel field is no longer present on other devices. 39762306a36Sopenharmony_ci */ 39862306a36Sopenharmony_ci if (ti_clk_get_features()->flags & TI_CLK_DPLL_HAS_FREQSEL) { 39962306a36Sopenharmony_ci v = ti_clk_ll_ops->clk_readl(&dd->control_reg); 40062306a36Sopenharmony_ci v &= ~dd->freqsel_mask; 40162306a36Sopenharmony_ci v |= freqsel << __ffs(dd->freqsel_mask); 40262306a36Sopenharmony_ci ti_clk_ll_ops->clk_writel(v, &dd->control_reg); 40362306a36Sopenharmony_ci } 40462306a36Sopenharmony_ci 40562306a36Sopenharmony_ci /* Set DPLL multiplier, divider */ 40662306a36Sopenharmony_ci v = ti_clk_ll_ops->clk_readl(&dd->mult_div1_reg); 40762306a36Sopenharmony_ci 40862306a36Sopenharmony_ci /* Handle Duty Cycle Correction */ 40962306a36Sopenharmony_ci if (dd->dcc_mask) { 41062306a36Sopenharmony_ci if (dd->last_rounded_rate >= dd->dcc_rate) 41162306a36Sopenharmony_ci v |= dd->dcc_mask; /* Enable DCC */ 41262306a36Sopenharmony_ci else 41362306a36Sopenharmony_ci v &= ~dd->dcc_mask; /* Disable DCC */ 41462306a36Sopenharmony_ci } 41562306a36Sopenharmony_ci 41662306a36Sopenharmony_ci v &= ~(dd->mult_mask | dd->div1_mask); 41762306a36Sopenharmony_ci v |= dd->last_rounded_m << __ffs(dd->mult_mask); 41862306a36Sopenharmony_ci v |= (dd->last_rounded_n - 1) << __ffs(dd->div1_mask); 41962306a36Sopenharmony_ci 42062306a36Sopenharmony_ci /* Configure dco and sd_div for dplls that have these fields */ 42162306a36Sopenharmony_ci if (dd->dco_mask) { 42262306a36Sopenharmony_ci _lookup_dco(clk, &dco, dd->last_rounded_m, dd->last_rounded_n); 42362306a36Sopenharmony_ci v &= ~(dd->dco_mask); 42462306a36Sopenharmony_ci v |= dco << __ffs(dd->dco_mask); 42562306a36Sopenharmony_ci } 42662306a36Sopenharmony_ci if (dd->sddiv_mask) { 42762306a36Sopenharmony_ci _lookup_sddiv(clk, &sd_div, dd->last_rounded_m, 42862306a36Sopenharmony_ci dd->last_rounded_n); 42962306a36Sopenharmony_ci v &= ~(dd->sddiv_mask); 43062306a36Sopenharmony_ci v |= sd_div << __ffs(dd->sddiv_mask); 43162306a36Sopenharmony_ci } 43262306a36Sopenharmony_ci 43362306a36Sopenharmony_ci /* 43462306a36Sopenharmony_ci * Errata i810 - DPLL controller can get stuck while transitioning 43562306a36Sopenharmony_ci * to a power saving state. Software must ensure the DPLL can not 43662306a36Sopenharmony_ci * transition to a low power state while changing M/N values. 43762306a36Sopenharmony_ci * Easiest way to accomplish this is to prevent DPLL autoidle 43862306a36Sopenharmony_ci * before doing the M/N re-program. 43962306a36Sopenharmony_ci */ 44062306a36Sopenharmony_ci errata_i810 = ti_clk_get_features()->flags & TI_CLK_ERRATA_I810; 44162306a36Sopenharmony_ci 44262306a36Sopenharmony_ci if (errata_i810) { 44362306a36Sopenharmony_ci ai = omap3_dpll_autoidle_read(clk); 44462306a36Sopenharmony_ci if (ai) { 44562306a36Sopenharmony_ci omap3_dpll_deny_idle(clk); 44662306a36Sopenharmony_ci 44762306a36Sopenharmony_ci /* OCP barrier */ 44862306a36Sopenharmony_ci omap3_dpll_autoidle_read(clk); 44962306a36Sopenharmony_ci } 45062306a36Sopenharmony_ci } 45162306a36Sopenharmony_ci 45262306a36Sopenharmony_ci ti_clk_ll_ops->clk_writel(v, &dd->mult_div1_reg); 45362306a36Sopenharmony_ci 45462306a36Sopenharmony_ci /* Set 4X multiplier and low-power mode */ 45562306a36Sopenharmony_ci if (dd->m4xen_mask || dd->lpmode_mask) { 45662306a36Sopenharmony_ci v = ti_clk_ll_ops->clk_readl(&dd->control_reg); 45762306a36Sopenharmony_ci 45862306a36Sopenharmony_ci if (dd->m4xen_mask) { 45962306a36Sopenharmony_ci if (dd->last_rounded_m4xen) 46062306a36Sopenharmony_ci v |= dd->m4xen_mask; 46162306a36Sopenharmony_ci else 46262306a36Sopenharmony_ci v &= ~dd->m4xen_mask; 46362306a36Sopenharmony_ci } 46462306a36Sopenharmony_ci 46562306a36Sopenharmony_ci if (dd->lpmode_mask) { 46662306a36Sopenharmony_ci if (dd->last_rounded_lpmode) 46762306a36Sopenharmony_ci v |= dd->lpmode_mask; 46862306a36Sopenharmony_ci else 46962306a36Sopenharmony_ci v &= ~dd->lpmode_mask; 47062306a36Sopenharmony_ci } 47162306a36Sopenharmony_ci 47262306a36Sopenharmony_ci ti_clk_ll_ops->clk_writel(v, &dd->control_reg); 47362306a36Sopenharmony_ci } 47462306a36Sopenharmony_ci 47562306a36Sopenharmony_ci if (dd->ssc_enable_mask) 47662306a36Sopenharmony_ci omap3_noncore_dpll_ssc_program(clk); 47762306a36Sopenharmony_ci 47862306a36Sopenharmony_ci /* We let the clock framework set the other output dividers later */ 47962306a36Sopenharmony_ci 48062306a36Sopenharmony_ci /* REVISIT: Set ramp-up delay? */ 48162306a36Sopenharmony_ci 48262306a36Sopenharmony_ci _omap3_noncore_dpll_lock(clk); 48362306a36Sopenharmony_ci 48462306a36Sopenharmony_ci if (errata_i810 && ai) 48562306a36Sopenharmony_ci omap3_dpll_allow_idle(clk); 48662306a36Sopenharmony_ci 48762306a36Sopenharmony_ci return 0; 48862306a36Sopenharmony_ci} 48962306a36Sopenharmony_ci 49062306a36Sopenharmony_ci/* Public functions */ 49162306a36Sopenharmony_ci 49262306a36Sopenharmony_ci/** 49362306a36Sopenharmony_ci * omap3_dpll_recalc - recalculate DPLL rate 49462306a36Sopenharmony_ci * @hw: struct clk_hw containing the DPLL struct clk 49562306a36Sopenharmony_ci * @parent_rate: clock rate of the DPLL parent 49662306a36Sopenharmony_ci * 49762306a36Sopenharmony_ci * Recalculate and propagate the DPLL rate. 49862306a36Sopenharmony_ci */ 49962306a36Sopenharmony_ciunsigned long omap3_dpll_recalc(struct clk_hw *hw, unsigned long parent_rate) 50062306a36Sopenharmony_ci{ 50162306a36Sopenharmony_ci struct clk_hw_omap *clk = to_clk_hw_omap(hw); 50262306a36Sopenharmony_ci 50362306a36Sopenharmony_ci return omap2_get_dpll_rate(clk); 50462306a36Sopenharmony_ci} 50562306a36Sopenharmony_ci 50662306a36Sopenharmony_ci/* Non-CORE DPLL (e.g., DPLLs that do not control SDRC) clock functions */ 50762306a36Sopenharmony_ci 50862306a36Sopenharmony_ci/** 50962306a36Sopenharmony_ci * omap3_noncore_dpll_enable - instruct a DPLL to enter bypass or lock mode 51062306a36Sopenharmony_ci * @hw: struct clk_hw containing then pointer to a DPLL struct clk 51162306a36Sopenharmony_ci * 51262306a36Sopenharmony_ci * Instructs a non-CORE DPLL to enable, e.g., to enter bypass or lock. 51362306a36Sopenharmony_ci * The choice of modes depends on the DPLL's programmed rate: if it is 51462306a36Sopenharmony_ci * the same as the DPLL's parent clock, it will enter bypass; 51562306a36Sopenharmony_ci * otherwise, it will enter lock. This code will wait for the DPLL to 51662306a36Sopenharmony_ci * indicate readiness before returning, unless the DPLL takes too long 51762306a36Sopenharmony_ci * to enter the target state. Intended to be used as the struct clk's 51862306a36Sopenharmony_ci * enable function. If DPLL3 was passed in, or the DPLL does not 51962306a36Sopenharmony_ci * support low-power stop, or if the DPLL took too long to enter 52062306a36Sopenharmony_ci * bypass or lock, return -EINVAL; otherwise, return 0. 52162306a36Sopenharmony_ci */ 52262306a36Sopenharmony_ciint omap3_noncore_dpll_enable(struct clk_hw *hw) 52362306a36Sopenharmony_ci{ 52462306a36Sopenharmony_ci struct clk_hw_omap *clk = to_clk_hw_omap(hw); 52562306a36Sopenharmony_ci int r; 52662306a36Sopenharmony_ci struct dpll_data *dd; 52762306a36Sopenharmony_ci struct clk_hw *parent; 52862306a36Sopenharmony_ci 52962306a36Sopenharmony_ci dd = clk->dpll_data; 53062306a36Sopenharmony_ci if (!dd) 53162306a36Sopenharmony_ci return -EINVAL; 53262306a36Sopenharmony_ci 53362306a36Sopenharmony_ci if (clk->clkdm) { 53462306a36Sopenharmony_ci r = ti_clk_ll_ops->clkdm_clk_enable(clk->clkdm, hw->clk); 53562306a36Sopenharmony_ci if (r) { 53662306a36Sopenharmony_ci WARN(1, 53762306a36Sopenharmony_ci "%s: could not enable %s's clockdomain %s: %d\n", 53862306a36Sopenharmony_ci __func__, clk_hw_get_name(hw), 53962306a36Sopenharmony_ci clk->clkdm_name, r); 54062306a36Sopenharmony_ci return r; 54162306a36Sopenharmony_ci } 54262306a36Sopenharmony_ci } 54362306a36Sopenharmony_ci 54462306a36Sopenharmony_ci parent = clk_hw_get_parent(hw); 54562306a36Sopenharmony_ci 54662306a36Sopenharmony_ci if (clk_hw_get_rate(hw) == clk_hw_get_rate(dd->clk_bypass)) { 54762306a36Sopenharmony_ci WARN_ON(parent != dd->clk_bypass); 54862306a36Sopenharmony_ci r = _omap3_noncore_dpll_bypass(clk); 54962306a36Sopenharmony_ci } else { 55062306a36Sopenharmony_ci WARN_ON(parent != dd->clk_ref); 55162306a36Sopenharmony_ci r = _omap3_noncore_dpll_lock(clk); 55262306a36Sopenharmony_ci } 55362306a36Sopenharmony_ci 55462306a36Sopenharmony_ci return r; 55562306a36Sopenharmony_ci} 55662306a36Sopenharmony_ci 55762306a36Sopenharmony_ci/** 55862306a36Sopenharmony_ci * omap3_noncore_dpll_disable - instruct a DPLL to enter low-power stop 55962306a36Sopenharmony_ci * @hw: struct clk_hw containing then pointer to a DPLL struct clk 56062306a36Sopenharmony_ci * 56162306a36Sopenharmony_ci * Instructs a non-CORE DPLL to enter low-power stop. This function is 56262306a36Sopenharmony_ci * intended for use in struct clkops. No return value. 56362306a36Sopenharmony_ci */ 56462306a36Sopenharmony_civoid omap3_noncore_dpll_disable(struct clk_hw *hw) 56562306a36Sopenharmony_ci{ 56662306a36Sopenharmony_ci struct clk_hw_omap *clk = to_clk_hw_omap(hw); 56762306a36Sopenharmony_ci 56862306a36Sopenharmony_ci _omap3_noncore_dpll_stop(clk); 56962306a36Sopenharmony_ci if (clk->clkdm) 57062306a36Sopenharmony_ci ti_clk_ll_ops->clkdm_clk_disable(clk->clkdm, hw->clk); 57162306a36Sopenharmony_ci} 57262306a36Sopenharmony_ci 57362306a36Sopenharmony_ci/* Non-CORE DPLL rate set code */ 57462306a36Sopenharmony_ci 57562306a36Sopenharmony_ci/** 57662306a36Sopenharmony_ci * omap3_noncore_dpll_determine_rate - determine rate for a DPLL 57762306a36Sopenharmony_ci * @hw: pointer to the clock to determine rate for 57862306a36Sopenharmony_ci * @req: target rate request 57962306a36Sopenharmony_ci * 58062306a36Sopenharmony_ci * Determines which DPLL mode to use for reaching a desired target rate. 58162306a36Sopenharmony_ci * Checks whether the DPLL shall be in bypass or locked mode, and if 58262306a36Sopenharmony_ci * locked, calculates the M,N values for the DPLL via round-rate. 58362306a36Sopenharmony_ci * Returns a 0 on success, negative error value in failure. 58462306a36Sopenharmony_ci */ 58562306a36Sopenharmony_ciint omap3_noncore_dpll_determine_rate(struct clk_hw *hw, 58662306a36Sopenharmony_ci struct clk_rate_request *req) 58762306a36Sopenharmony_ci{ 58862306a36Sopenharmony_ci struct clk_hw_omap *clk = to_clk_hw_omap(hw); 58962306a36Sopenharmony_ci struct dpll_data *dd; 59062306a36Sopenharmony_ci 59162306a36Sopenharmony_ci if (!req->rate) 59262306a36Sopenharmony_ci return -EINVAL; 59362306a36Sopenharmony_ci 59462306a36Sopenharmony_ci dd = clk->dpll_data; 59562306a36Sopenharmony_ci if (!dd) 59662306a36Sopenharmony_ci return -EINVAL; 59762306a36Sopenharmony_ci 59862306a36Sopenharmony_ci if (clk_hw_get_rate(dd->clk_bypass) == req->rate && 59962306a36Sopenharmony_ci (dd->modes & (1 << DPLL_LOW_POWER_BYPASS))) { 60062306a36Sopenharmony_ci req->best_parent_hw = dd->clk_bypass; 60162306a36Sopenharmony_ci } else { 60262306a36Sopenharmony_ci req->rate = omap2_dpll_round_rate(hw, req->rate, 60362306a36Sopenharmony_ci &req->best_parent_rate); 60462306a36Sopenharmony_ci req->best_parent_hw = dd->clk_ref; 60562306a36Sopenharmony_ci } 60662306a36Sopenharmony_ci 60762306a36Sopenharmony_ci req->best_parent_rate = req->rate; 60862306a36Sopenharmony_ci 60962306a36Sopenharmony_ci return 0; 61062306a36Sopenharmony_ci} 61162306a36Sopenharmony_ci 61262306a36Sopenharmony_ci/** 61362306a36Sopenharmony_ci * omap3_noncore_dpll_set_parent - set parent for a DPLL clock 61462306a36Sopenharmony_ci * @hw: pointer to the clock to set parent for 61562306a36Sopenharmony_ci * @index: parent index to select 61662306a36Sopenharmony_ci * 61762306a36Sopenharmony_ci * Sets parent for a DPLL clock. This sets the DPLL into bypass or 61862306a36Sopenharmony_ci * locked mode. Returns 0 with success, negative error value otherwise. 61962306a36Sopenharmony_ci */ 62062306a36Sopenharmony_ciint omap3_noncore_dpll_set_parent(struct clk_hw *hw, u8 index) 62162306a36Sopenharmony_ci{ 62262306a36Sopenharmony_ci struct clk_hw_omap *clk = to_clk_hw_omap(hw); 62362306a36Sopenharmony_ci int ret; 62462306a36Sopenharmony_ci 62562306a36Sopenharmony_ci if (!hw) 62662306a36Sopenharmony_ci return -EINVAL; 62762306a36Sopenharmony_ci 62862306a36Sopenharmony_ci if (index) 62962306a36Sopenharmony_ci ret = _omap3_noncore_dpll_bypass(clk); 63062306a36Sopenharmony_ci else 63162306a36Sopenharmony_ci ret = _omap3_noncore_dpll_lock(clk); 63262306a36Sopenharmony_ci 63362306a36Sopenharmony_ci return ret; 63462306a36Sopenharmony_ci} 63562306a36Sopenharmony_ci 63662306a36Sopenharmony_ci/** 63762306a36Sopenharmony_ci * omap3_noncore_dpll_set_rate - set rate for a DPLL clock 63862306a36Sopenharmony_ci * @hw: pointer to the clock to set parent for 63962306a36Sopenharmony_ci * @rate: target rate for the clock 64062306a36Sopenharmony_ci * @parent_rate: rate of the parent clock 64162306a36Sopenharmony_ci * 64262306a36Sopenharmony_ci * Sets rate for a DPLL clock. First checks if the clock parent is 64362306a36Sopenharmony_ci * reference clock (in bypass mode, the rate of the clock can't be 64462306a36Sopenharmony_ci * changed) and proceeds with the rate change operation. Returns 0 64562306a36Sopenharmony_ci * with success, negative error value otherwise. 64662306a36Sopenharmony_ci */ 64762306a36Sopenharmony_ciint omap3_noncore_dpll_set_rate(struct clk_hw *hw, unsigned long rate, 64862306a36Sopenharmony_ci unsigned long parent_rate) 64962306a36Sopenharmony_ci{ 65062306a36Sopenharmony_ci struct clk_hw_omap *clk = to_clk_hw_omap(hw); 65162306a36Sopenharmony_ci struct dpll_data *dd; 65262306a36Sopenharmony_ci u16 freqsel = 0; 65362306a36Sopenharmony_ci int ret; 65462306a36Sopenharmony_ci 65562306a36Sopenharmony_ci if (!hw || !rate) 65662306a36Sopenharmony_ci return -EINVAL; 65762306a36Sopenharmony_ci 65862306a36Sopenharmony_ci dd = clk->dpll_data; 65962306a36Sopenharmony_ci if (!dd) 66062306a36Sopenharmony_ci return -EINVAL; 66162306a36Sopenharmony_ci 66262306a36Sopenharmony_ci if (clk_hw_get_parent(hw) != dd->clk_ref) 66362306a36Sopenharmony_ci return -EINVAL; 66462306a36Sopenharmony_ci 66562306a36Sopenharmony_ci if (dd->last_rounded_rate == 0) 66662306a36Sopenharmony_ci return -EINVAL; 66762306a36Sopenharmony_ci 66862306a36Sopenharmony_ci /* Freqsel is available only on OMAP343X devices */ 66962306a36Sopenharmony_ci if (ti_clk_get_features()->flags & TI_CLK_DPLL_HAS_FREQSEL) { 67062306a36Sopenharmony_ci freqsel = _omap3_dpll_compute_freqsel(clk, dd->last_rounded_n); 67162306a36Sopenharmony_ci WARN_ON(!freqsel); 67262306a36Sopenharmony_ci } 67362306a36Sopenharmony_ci 67462306a36Sopenharmony_ci pr_debug("%s: %s: set rate: locking rate to %lu.\n", __func__, 67562306a36Sopenharmony_ci clk_hw_get_name(hw), rate); 67662306a36Sopenharmony_ci 67762306a36Sopenharmony_ci ret = omap3_noncore_dpll_program(clk, freqsel); 67862306a36Sopenharmony_ci 67962306a36Sopenharmony_ci return ret; 68062306a36Sopenharmony_ci} 68162306a36Sopenharmony_ci 68262306a36Sopenharmony_ci/** 68362306a36Sopenharmony_ci * omap3_noncore_dpll_set_rate_and_parent - set rate and parent for a DPLL clock 68462306a36Sopenharmony_ci * @hw: pointer to the clock to set rate and parent for 68562306a36Sopenharmony_ci * @rate: target rate for the DPLL 68662306a36Sopenharmony_ci * @parent_rate: clock rate of the DPLL parent 68762306a36Sopenharmony_ci * @index: new parent index for the DPLL, 0 - reference, 1 - bypass 68862306a36Sopenharmony_ci * 68962306a36Sopenharmony_ci * Sets rate and parent for a DPLL clock. If new parent is the bypass 69062306a36Sopenharmony_ci * clock, only selects the parent. Otherwise proceeds with a rate 69162306a36Sopenharmony_ci * change, as this will effectively also change the parent as the 69262306a36Sopenharmony_ci * DPLL is put into locked mode. Returns 0 with success, negative error 69362306a36Sopenharmony_ci * value otherwise. 69462306a36Sopenharmony_ci */ 69562306a36Sopenharmony_ciint omap3_noncore_dpll_set_rate_and_parent(struct clk_hw *hw, 69662306a36Sopenharmony_ci unsigned long rate, 69762306a36Sopenharmony_ci unsigned long parent_rate, 69862306a36Sopenharmony_ci u8 index) 69962306a36Sopenharmony_ci{ 70062306a36Sopenharmony_ci int ret; 70162306a36Sopenharmony_ci 70262306a36Sopenharmony_ci if (!hw || !rate) 70362306a36Sopenharmony_ci return -EINVAL; 70462306a36Sopenharmony_ci 70562306a36Sopenharmony_ci /* 70662306a36Sopenharmony_ci * clk-ref at index[0], in which case we only need to set rate, 70762306a36Sopenharmony_ci * the parent will be changed automatically with the lock sequence. 70862306a36Sopenharmony_ci * With clk-bypass case we only need to change parent. 70962306a36Sopenharmony_ci */ 71062306a36Sopenharmony_ci if (index) 71162306a36Sopenharmony_ci ret = omap3_noncore_dpll_set_parent(hw, index); 71262306a36Sopenharmony_ci else 71362306a36Sopenharmony_ci ret = omap3_noncore_dpll_set_rate(hw, rate, parent_rate); 71462306a36Sopenharmony_ci 71562306a36Sopenharmony_ci return ret; 71662306a36Sopenharmony_ci} 71762306a36Sopenharmony_ci 71862306a36Sopenharmony_ci/* DPLL autoidle read/set code */ 71962306a36Sopenharmony_ci 72062306a36Sopenharmony_ci/** 72162306a36Sopenharmony_ci * omap3_dpll_autoidle_read - read a DPLL's autoidle bits 72262306a36Sopenharmony_ci * @clk: struct clk * of the DPLL to read 72362306a36Sopenharmony_ci * 72462306a36Sopenharmony_ci * Return the DPLL's autoidle bits, shifted down to bit 0. Returns 72562306a36Sopenharmony_ci * -EINVAL if passed a null pointer or if the struct clk does not 72662306a36Sopenharmony_ci * appear to refer to a DPLL. 72762306a36Sopenharmony_ci */ 72862306a36Sopenharmony_cistatic u32 omap3_dpll_autoidle_read(struct clk_hw_omap *clk) 72962306a36Sopenharmony_ci{ 73062306a36Sopenharmony_ci const struct dpll_data *dd; 73162306a36Sopenharmony_ci u32 v; 73262306a36Sopenharmony_ci 73362306a36Sopenharmony_ci if (!clk || !clk->dpll_data) 73462306a36Sopenharmony_ci return -EINVAL; 73562306a36Sopenharmony_ci 73662306a36Sopenharmony_ci dd = clk->dpll_data; 73762306a36Sopenharmony_ci 73862306a36Sopenharmony_ci if (!dd->autoidle_mask) 73962306a36Sopenharmony_ci return -EINVAL; 74062306a36Sopenharmony_ci 74162306a36Sopenharmony_ci v = ti_clk_ll_ops->clk_readl(&dd->autoidle_reg); 74262306a36Sopenharmony_ci v &= dd->autoidle_mask; 74362306a36Sopenharmony_ci v >>= __ffs(dd->autoidle_mask); 74462306a36Sopenharmony_ci 74562306a36Sopenharmony_ci return v; 74662306a36Sopenharmony_ci} 74762306a36Sopenharmony_ci 74862306a36Sopenharmony_ci/** 74962306a36Sopenharmony_ci * omap3_dpll_allow_idle - enable DPLL autoidle bits 75062306a36Sopenharmony_ci * @clk: struct clk * of the DPLL to operate on 75162306a36Sopenharmony_ci * 75262306a36Sopenharmony_ci * Enable DPLL automatic idle control. This automatic idle mode 75362306a36Sopenharmony_ci * switching takes effect only when the DPLL is locked, at least on 75462306a36Sopenharmony_ci * OMAP3430. The DPLL will enter low-power stop when its downstream 75562306a36Sopenharmony_ci * clocks are gated. No return value. 75662306a36Sopenharmony_ci */ 75762306a36Sopenharmony_cistatic void omap3_dpll_allow_idle(struct clk_hw_omap *clk) 75862306a36Sopenharmony_ci{ 75962306a36Sopenharmony_ci const struct dpll_data *dd; 76062306a36Sopenharmony_ci u32 v; 76162306a36Sopenharmony_ci 76262306a36Sopenharmony_ci if (!clk || !clk->dpll_data) 76362306a36Sopenharmony_ci return; 76462306a36Sopenharmony_ci 76562306a36Sopenharmony_ci dd = clk->dpll_data; 76662306a36Sopenharmony_ci 76762306a36Sopenharmony_ci if (!dd->autoidle_mask) 76862306a36Sopenharmony_ci return; 76962306a36Sopenharmony_ci 77062306a36Sopenharmony_ci /* 77162306a36Sopenharmony_ci * REVISIT: CORE DPLL can optionally enter low-power bypass 77262306a36Sopenharmony_ci * by writing 0x5 instead of 0x1. Add some mechanism to 77362306a36Sopenharmony_ci * optionally enter this mode. 77462306a36Sopenharmony_ci */ 77562306a36Sopenharmony_ci v = ti_clk_ll_ops->clk_readl(&dd->autoidle_reg); 77662306a36Sopenharmony_ci v &= ~dd->autoidle_mask; 77762306a36Sopenharmony_ci v |= DPLL_AUTOIDLE_LOW_POWER_STOP << __ffs(dd->autoidle_mask); 77862306a36Sopenharmony_ci ti_clk_ll_ops->clk_writel(v, &dd->autoidle_reg); 77962306a36Sopenharmony_ci} 78062306a36Sopenharmony_ci 78162306a36Sopenharmony_ci/** 78262306a36Sopenharmony_ci * omap3_dpll_deny_idle - prevent DPLL from automatically idling 78362306a36Sopenharmony_ci * @clk: struct clk * of the DPLL to operate on 78462306a36Sopenharmony_ci * 78562306a36Sopenharmony_ci * Disable DPLL automatic idle control. No return value. 78662306a36Sopenharmony_ci */ 78762306a36Sopenharmony_cistatic void omap3_dpll_deny_idle(struct clk_hw_omap *clk) 78862306a36Sopenharmony_ci{ 78962306a36Sopenharmony_ci const struct dpll_data *dd; 79062306a36Sopenharmony_ci u32 v; 79162306a36Sopenharmony_ci 79262306a36Sopenharmony_ci if (!clk || !clk->dpll_data) 79362306a36Sopenharmony_ci return; 79462306a36Sopenharmony_ci 79562306a36Sopenharmony_ci dd = clk->dpll_data; 79662306a36Sopenharmony_ci 79762306a36Sopenharmony_ci if (!dd->autoidle_mask) 79862306a36Sopenharmony_ci return; 79962306a36Sopenharmony_ci 80062306a36Sopenharmony_ci v = ti_clk_ll_ops->clk_readl(&dd->autoidle_reg); 80162306a36Sopenharmony_ci v &= ~dd->autoidle_mask; 80262306a36Sopenharmony_ci v |= DPLL_AUTOIDLE_DISABLE << __ffs(dd->autoidle_mask); 80362306a36Sopenharmony_ci ti_clk_ll_ops->clk_writel(v, &dd->autoidle_reg); 80462306a36Sopenharmony_ci} 80562306a36Sopenharmony_ci 80662306a36Sopenharmony_ci/* Clock control for DPLL outputs */ 80762306a36Sopenharmony_ci 80862306a36Sopenharmony_ci/* Find the parent DPLL for the given clkoutx2 clock */ 80962306a36Sopenharmony_cistatic struct clk_hw_omap *omap3_find_clkoutx2_dpll(struct clk_hw *hw) 81062306a36Sopenharmony_ci{ 81162306a36Sopenharmony_ci struct clk_hw_omap *pclk = NULL; 81262306a36Sopenharmony_ci 81362306a36Sopenharmony_ci /* Walk up the parents of clk, looking for a DPLL */ 81462306a36Sopenharmony_ci do { 81562306a36Sopenharmony_ci do { 81662306a36Sopenharmony_ci hw = clk_hw_get_parent(hw); 81762306a36Sopenharmony_ci } while (hw && (!omap2_clk_is_hw_omap(hw))); 81862306a36Sopenharmony_ci if (!hw) 81962306a36Sopenharmony_ci break; 82062306a36Sopenharmony_ci pclk = to_clk_hw_omap(hw); 82162306a36Sopenharmony_ci } while (pclk && !pclk->dpll_data); 82262306a36Sopenharmony_ci 82362306a36Sopenharmony_ci /* clk does not have a DPLL as a parent? error in the clock data */ 82462306a36Sopenharmony_ci if (!pclk) { 82562306a36Sopenharmony_ci WARN_ON(1); 82662306a36Sopenharmony_ci return NULL; 82762306a36Sopenharmony_ci } 82862306a36Sopenharmony_ci 82962306a36Sopenharmony_ci return pclk; 83062306a36Sopenharmony_ci} 83162306a36Sopenharmony_ci 83262306a36Sopenharmony_ci/** 83362306a36Sopenharmony_ci * omap3_clkoutx2_recalc - recalculate DPLL X2 output virtual clock rate 83462306a36Sopenharmony_ci * @hw: pointer struct clk_hw 83562306a36Sopenharmony_ci * @parent_rate: clock rate of the DPLL parent 83662306a36Sopenharmony_ci * 83762306a36Sopenharmony_ci * Using parent clock DPLL data, look up DPLL state. If locked, set our 83862306a36Sopenharmony_ci * rate to the dpll_clk * 2; otherwise, just use dpll_clk. 83962306a36Sopenharmony_ci */ 84062306a36Sopenharmony_ciunsigned long omap3_clkoutx2_recalc(struct clk_hw *hw, 84162306a36Sopenharmony_ci unsigned long parent_rate) 84262306a36Sopenharmony_ci{ 84362306a36Sopenharmony_ci const struct dpll_data *dd; 84462306a36Sopenharmony_ci unsigned long rate; 84562306a36Sopenharmony_ci u32 v; 84662306a36Sopenharmony_ci struct clk_hw_omap *pclk = NULL; 84762306a36Sopenharmony_ci 84862306a36Sopenharmony_ci if (!parent_rate) 84962306a36Sopenharmony_ci return 0; 85062306a36Sopenharmony_ci 85162306a36Sopenharmony_ci pclk = omap3_find_clkoutx2_dpll(hw); 85262306a36Sopenharmony_ci 85362306a36Sopenharmony_ci if (!pclk) 85462306a36Sopenharmony_ci return 0; 85562306a36Sopenharmony_ci 85662306a36Sopenharmony_ci dd = pclk->dpll_data; 85762306a36Sopenharmony_ci 85862306a36Sopenharmony_ci WARN_ON(!dd->enable_mask); 85962306a36Sopenharmony_ci 86062306a36Sopenharmony_ci v = ti_clk_ll_ops->clk_readl(&dd->control_reg) & dd->enable_mask; 86162306a36Sopenharmony_ci v >>= __ffs(dd->enable_mask); 86262306a36Sopenharmony_ci if ((v != OMAP3XXX_EN_DPLL_LOCKED) || (dd->flags & DPLL_J_TYPE)) 86362306a36Sopenharmony_ci rate = parent_rate; 86462306a36Sopenharmony_ci else 86562306a36Sopenharmony_ci rate = parent_rate * 2; 86662306a36Sopenharmony_ci return rate; 86762306a36Sopenharmony_ci} 86862306a36Sopenharmony_ci 86962306a36Sopenharmony_ci/** 87062306a36Sopenharmony_ci * omap3_core_dpll_save_context - Save the m and n values of the divider 87162306a36Sopenharmony_ci * @hw: pointer struct clk_hw 87262306a36Sopenharmony_ci * 87362306a36Sopenharmony_ci * Before the dpll registers are lost save the last rounded rate m and n 87462306a36Sopenharmony_ci * and the enable mask. 87562306a36Sopenharmony_ci */ 87662306a36Sopenharmony_ciint omap3_core_dpll_save_context(struct clk_hw *hw) 87762306a36Sopenharmony_ci{ 87862306a36Sopenharmony_ci struct clk_hw_omap *clk = to_clk_hw_omap(hw); 87962306a36Sopenharmony_ci struct dpll_data *dd; 88062306a36Sopenharmony_ci u32 v; 88162306a36Sopenharmony_ci 88262306a36Sopenharmony_ci dd = clk->dpll_data; 88362306a36Sopenharmony_ci 88462306a36Sopenharmony_ci v = ti_clk_ll_ops->clk_readl(&dd->control_reg); 88562306a36Sopenharmony_ci clk->context = (v & dd->enable_mask) >> __ffs(dd->enable_mask); 88662306a36Sopenharmony_ci 88762306a36Sopenharmony_ci if (clk->context == DPLL_LOCKED) { 88862306a36Sopenharmony_ci v = ti_clk_ll_ops->clk_readl(&dd->mult_div1_reg); 88962306a36Sopenharmony_ci dd->last_rounded_m = (v & dd->mult_mask) >> 89062306a36Sopenharmony_ci __ffs(dd->mult_mask); 89162306a36Sopenharmony_ci dd->last_rounded_n = ((v & dd->div1_mask) >> 89262306a36Sopenharmony_ci __ffs(dd->div1_mask)) + 1; 89362306a36Sopenharmony_ci } 89462306a36Sopenharmony_ci 89562306a36Sopenharmony_ci return 0; 89662306a36Sopenharmony_ci} 89762306a36Sopenharmony_ci 89862306a36Sopenharmony_ci/** 89962306a36Sopenharmony_ci * omap3_core_dpll_restore_context - restore the m and n values of the divider 90062306a36Sopenharmony_ci * @hw: pointer struct clk_hw 90162306a36Sopenharmony_ci * 90262306a36Sopenharmony_ci * Restore the last rounded rate m and n 90362306a36Sopenharmony_ci * and the enable mask. 90462306a36Sopenharmony_ci */ 90562306a36Sopenharmony_civoid omap3_core_dpll_restore_context(struct clk_hw *hw) 90662306a36Sopenharmony_ci{ 90762306a36Sopenharmony_ci struct clk_hw_omap *clk = to_clk_hw_omap(hw); 90862306a36Sopenharmony_ci const struct dpll_data *dd; 90962306a36Sopenharmony_ci u32 v; 91062306a36Sopenharmony_ci 91162306a36Sopenharmony_ci dd = clk->dpll_data; 91262306a36Sopenharmony_ci 91362306a36Sopenharmony_ci if (clk->context == DPLL_LOCKED) { 91462306a36Sopenharmony_ci _omap3_dpll_write_clken(clk, 0x4); 91562306a36Sopenharmony_ci _omap3_wait_dpll_status(clk, 0); 91662306a36Sopenharmony_ci 91762306a36Sopenharmony_ci v = ti_clk_ll_ops->clk_readl(&dd->mult_div1_reg); 91862306a36Sopenharmony_ci v &= ~(dd->mult_mask | dd->div1_mask); 91962306a36Sopenharmony_ci v |= dd->last_rounded_m << __ffs(dd->mult_mask); 92062306a36Sopenharmony_ci v |= (dd->last_rounded_n - 1) << __ffs(dd->div1_mask); 92162306a36Sopenharmony_ci ti_clk_ll_ops->clk_writel(v, &dd->mult_div1_reg); 92262306a36Sopenharmony_ci 92362306a36Sopenharmony_ci _omap3_dpll_write_clken(clk, DPLL_LOCKED); 92462306a36Sopenharmony_ci _omap3_wait_dpll_status(clk, 1); 92562306a36Sopenharmony_ci } else { 92662306a36Sopenharmony_ci _omap3_dpll_write_clken(clk, clk->context); 92762306a36Sopenharmony_ci } 92862306a36Sopenharmony_ci} 92962306a36Sopenharmony_ci 93062306a36Sopenharmony_ci/** 93162306a36Sopenharmony_ci * omap3_non_core_dpll_save_context - Save the m and n values of the divider 93262306a36Sopenharmony_ci * @hw: pointer struct clk_hw 93362306a36Sopenharmony_ci * 93462306a36Sopenharmony_ci * Before the dpll registers are lost save the last rounded rate m and n 93562306a36Sopenharmony_ci * and the enable mask. 93662306a36Sopenharmony_ci */ 93762306a36Sopenharmony_ciint omap3_noncore_dpll_save_context(struct clk_hw *hw) 93862306a36Sopenharmony_ci{ 93962306a36Sopenharmony_ci struct clk_hw_omap *clk = to_clk_hw_omap(hw); 94062306a36Sopenharmony_ci struct dpll_data *dd; 94162306a36Sopenharmony_ci u32 v; 94262306a36Sopenharmony_ci 94362306a36Sopenharmony_ci dd = clk->dpll_data; 94462306a36Sopenharmony_ci 94562306a36Sopenharmony_ci v = ti_clk_ll_ops->clk_readl(&dd->control_reg); 94662306a36Sopenharmony_ci clk->context = (v & dd->enable_mask) >> __ffs(dd->enable_mask); 94762306a36Sopenharmony_ci 94862306a36Sopenharmony_ci if (clk->context == DPLL_LOCKED) { 94962306a36Sopenharmony_ci v = ti_clk_ll_ops->clk_readl(&dd->mult_div1_reg); 95062306a36Sopenharmony_ci dd->last_rounded_m = (v & dd->mult_mask) >> 95162306a36Sopenharmony_ci __ffs(dd->mult_mask); 95262306a36Sopenharmony_ci dd->last_rounded_n = ((v & dd->div1_mask) >> 95362306a36Sopenharmony_ci __ffs(dd->div1_mask)) + 1; 95462306a36Sopenharmony_ci } 95562306a36Sopenharmony_ci 95662306a36Sopenharmony_ci return 0; 95762306a36Sopenharmony_ci} 95862306a36Sopenharmony_ci 95962306a36Sopenharmony_ci/** 96062306a36Sopenharmony_ci * omap3_core_dpll_restore_context - restore the m and n values of the divider 96162306a36Sopenharmony_ci * @hw: pointer struct clk_hw 96262306a36Sopenharmony_ci * 96362306a36Sopenharmony_ci * Restore the last rounded rate m and n 96462306a36Sopenharmony_ci * and the enable mask. 96562306a36Sopenharmony_ci */ 96662306a36Sopenharmony_civoid omap3_noncore_dpll_restore_context(struct clk_hw *hw) 96762306a36Sopenharmony_ci{ 96862306a36Sopenharmony_ci struct clk_hw_omap *clk = to_clk_hw_omap(hw); 96962306a36Sopenharmony_ci const struct dpll_data *dd; 97062306a36Sopenharmony_ci u32 ctrl, mult_div1; 97162306a36Sopenharmony_ci 97262306a36Sopenharmony_ci dd = clk->dpll_data; 97362306a36Sopenharmony_ci 97462306a36Sopenharmony_ci ctrl = ti_clk_ll_ops->clk_readl(&dd->control_reg); 97562306a36Sopenharmony_ci mult_div1 = ti_clk_ll_ops->clk_readl(&dd->mult_div1_reg); 97662306a36Sopenharmony_ci 97762306a36Sopenharmony_ci if (clk->context == ((ctrl & dd->enable_mask) >> 97862306a36Sopenharmony_ci __ffs(dd->enable_mask)) && 97962306a36Sopenharmony_ci dd->last_rounded_m == ((mult_div1 & dd->mult_mask) >> 98062306a36Sopenharmony_ci __ffs(dd->mult_mask)) && 98162306a36Sopenharmony_ci dd->last_rounded_n == ((mult_div1 & dd->div1_mask) >> 98262306a36Sopenharmony_ci __ffs(dd->div1_mask)) + 1) { 98362306a36Sopenharmony_ci /* nothing to be done */ 98462306a36Sopenharmony_ci return; 98562306a36Sopenharmony_ci } 98662306a36Sopenharmony_ci 98762306a36Sopenharmony_ci if (clk->context == DPLL_LOCKED) 98862306a36Sopenharmony_ci omap3_noncore_dpll_program(clk, 0); 98962306a36Sopenharmony_ci else 99062306a36Sopenharmony_ci _omap3_dpll_write_clken(clk, clk->context); 99162306a36Sopenharmony_ci} 99262306a36Sopenharmony_ci 99362306a36Sopenharmony_ci/* OMAP3/4 non-CORE DPLL clkops */ 99462306a36Sopenharmony_ciconst struct clk_hw_omap_ops clkhwops_omap3_dpll = { 99562306a36Sopenharmony_ci .allow_idle = omap3_dpll_allow_idle, 99662306a36Sopenharmony_ci .deny_idle = omap3_dpll_deny_idle, 99762306a36Sopenharmony_ci}; 99862306a36Sopenharmony_ci 99962306a36Sopenharmony_ci/** 100062306a36Sopenharmony_ci * omap3_dpll4_set_rate - set rate for omap3 per-dpll 100162306a36Sopenharmony_ci * @hw: clock to change 100262306a36Sopenharmony_ci * @rate: target rate for clock 100362306a36Sopenharmony_ci * @parent_rate: clock rate of the DPLL parent 100462306a36Sopenharmony_ci * 100562306a36Sopenharmony_ci * Check if the current SoC supports the per-dpll reprogram operation 100662306a36Sopenharmony_ci * or not, and then do the rate change if supported. Returns -EINVAL 100762306a36Sopenharmony_ci * if not supported, 0 for success, and potential error codes from the 100862306a36Sopenharmony_ci * clock rate change. 100962306a36Sopenharmony_ci */ 101062306a36Sopenharmony_ciint omap3_dpll4_set_rate(struct clk_hw *hw, unsigned long rate, 101162306a36Sopenharmony_ci unsigned long parent_rate) 101262306a36Sopenharmony_ci{ 101362306a36Sopenharmony_ci /* 101462306a36Sopenharmony_ci * According to the 12-5 CDP code from TI, "Limitation 2.5" 101562306a36Sopenharmony_ci * on 3430ES1 prevents us from changing DPLL multipliers or dividers 101662306a36Sopenharmony_ci * on DPLL4. 101762306a36Sopenharmony_ci */ 101862306a36Sopenharmony_ci if (ti_clk_get_features()->flags & TI_CLK_DPLL4_DENY_REPROGRAM) { 101962306a36Sopenharmony_ci pr_err("clock: DPLL4 cannot change rate due to silicon 'Limitation 2.5' on 3430ES1.\n"); 102062306a36Sopenharmony_ci return -EINVAL; 102162306a36Sopenharmony_ci } 102262306a36Sopenharmony_ci 102362306a36Sopenharmony_ci return omap3_noncore_dpll_set_rate(hw, rate, parent_rate); 102462306a36Sopenharmony_ci} 102562306a36Sopenharmony_ci 102662306a36Sopenharmony_ci/** 102762306a36Sopenharmony_ci * omap3_dpll4_set_rate_and_parent - set rate and parent for omap3 per-dpll 102862306a36Sopenharmony_ci * @hw: clock to change 102962306a36Sopenharmony_ci * @rate: target rate for clock 103062306a36Sopenharmony_ci * @parent_rate: rate of the parent clock 103162306a36Sopenharmony_ci * @index: parent index, 0 - reference clock, 1 - bypass clock 103262306a36Sopenharmony_ci * 103362306a36Sopenharmony_ci * Check if the current SoC support the per-dpll reprogram operation 103462306a36Sopenharmony_ci * or not, and then do the rate + parent change if supported. Returns 103562306a36Sopenharmony_ci * -EINVAL if not supported, 0 for success, and potential error codes 103662306a36Sopenharmony_ci * from the clock rate change. 103762306a36Sopenharmony_ci */ 103862306a36Sopenharmony_ciint omap3_dpll4_set_rate_and_parent(struct clk_hw *hw, unsigned long rate, 103962306a36Sopenharmony_ci unsigned long parent_rate, u8 index) 104062306a36Sopenharmony_ci{ 104162306a36Sopenharmony_ci if (ti_clk_get_features()->flags & TI_CLK_DPLL4_DENY_REPROGRAM) { 104262306a36Sopenharmony_ci pr_err("clock: DPLL4 cannot change rate due to silicon 'Limitation 2.5' on 3430ES1.\n"); 104362306a36Sopenharmony_ci return -EINVAL; 104462306a36Sopenharmony_ci } 104562306a36Sopenharmony_ci 104662306a36Sopenharmony_ci return omap3_noncore_dpll_set_rate_and_parent(hw, rate, parent_rate, 104762306a36Sopenharmony_ci index); 104862306a36Sopenharmony_ci} 104962306a36Sopenharmony_ci 105062306a36Sopenharmony_ci/* Apply DM3730 errata sprz319 advisory 2.1. */ 105162306a36Sopenharmony_cistatic bool omap3_dpll5_apply_errata(struct clk_hw *hw, 105262306a36Sopenharmony_ci unsigned long parent_rate) 105362306a36Sopenharmony_ci{ 105462306a36Sopenharmony_ci struct omap3_dpll5_settings { 105562306a36Sopenharmony_ci unsigned int rate, m, n; 105662306a36Sopenharmony_ci }; 105762306a36Sopenharmony_ci 105862306a36Sopenharmony_ci static const struct omap3_dpll5_settings precomputed[] = { 105962306a36Sopenharmony_ci /* 106062306a36Sopenharmony_ci * From DM3730 errata advisory 2.1, table 35 and 36. 106162306a36Sopenharmony_ci * The N value is increased by 1 compared to the tables as the 106262306a36Sopenharmony_ci * errata lists register values while last_rounded_field is the 106362306a36Sopenharmony_ci * real divider value. 106462306a36Sopenharmony_ci */ 106562306a36Sopenharmony_ci { 12000000, 80, 0 + 1 }, 106662306a36Sopenharmony_ci { 13000000, 443, 5 + 1 }, 106762306a36Sopenharmony_ci { 19200000, 50, 0 + 1 }, 106862306a36Sopenharmony_ci { 26000000, 443, 11 + 1 }, 106962306a36Sopenharmony_ci { 38400000, 25, 0 + 1 } 107062306a36Sopenharmony_ci }; 107162306a36Sopenharmony_ci 107262306a36Sopenharmony_ci const struct omap3_dpll5_settings *d; 107362306a36Sopenharmony_ci struct clk_hw_omap *clk = to_clk_hw_omap(hw); 107462306a36Sopenharmony_ci struct dpll_data *dd; 107562306a36Sopenharmony_ci unsigned int i; 107662306a36Sopenharmony_ci 107762306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(precomputed); ++i) { 107862306a36Sopenharmony_ci if (parent_rate == precomputed[i].rate) 107962306a36Sopenharmony_ci break; 108062306a36Sopenharmony_ci } 108162306a36Sopenharmony_ci 108262306a36Sopenharmony_ci if (i == ARRAY_SIZE(precomputed)) 108362306a36Sopenharmony_ci return false; 108462306a36Sopenharmony_ci 108562306a36Sopenharmony_ci d = &precomputed[i]; 108662306a36Sopenharmony_ci 108762306a36Sopenharmony_ci /* Update the M, N and rounded rate values and program the DPLL. */ 108862306a36Sopenharmony_ci dd = clk->dpll_data; 108962306a36Sopenharmony_ci dd->last_rounded_m = d->m; 109062306a36Sopenharmony_ci dd->last_rounded_n = d->n; 109162306a36Sopenharmony_ci dd->last_rounded_rate = div_u64((u64)parent_rate * d->m, d->n); 109262306a36Sopenharmony_ci omap3_noncore_dpll_program(clk, 0); 109362306a36Sopenharmony_ci 109462306a36Sopenharmony_ci return true; 109562306a36Sopenharmony_ci} 109662306a36Sopenharmony_ci 109762306a36Sopenharmony_ci/** 109862306a36Sopenharmony_ci * omap3_dpll5_set_rate - set rate for omap3 dpll5 109962306a36Sopenharmony_ci * @hw: clock to change 110062306a36Sopenharmony_ci * @rate: target rate for clock 110162306a36Sopenharmony_ci * @parent_rate: rate of the parent clock 110262306a36Sopenharmony_ci * 110362306a36Sopenharmony_ci * Set rate for the DPLL5 clock. Apply the sprz319 advisory 2.1 on OMAP36xx if 110462306a36Sopenharmony_ci * the DPLL is used for USB host (detected through the requested rate). 110562306a36Sopenharmony_ci */ 110662306a36Sopenharmony_ciint omap3_dpll5_set_rate(struct clk_hw *hw, unsigned long rate, 110762306a36Sopenharmony_ci unsigned long parent_rate) 110862306a36Sopenharmony_ci{ 110962306a36Sopenharmony_ci if (rate == OMAP3_DPLL5_FREQ_FOR_USBHOST * 8) { 111062306a36Sopenharmony_ci if (omap3_dpll5_apply_errata(hw, parent_rate)) 111162306a36Sopenharmony_ci return 0; 111262306a36Sopenharmony_ci } 111362306a36Sopenharmony_ci 111462306a36Sopenharmony_ci return omap3_noncore_dpll_set_rate(hw, rate, parent_rate); 111562306a36Sopenharmony_ci} 1116