18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * OMAP3/4 - specific DPLL control functions 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2009-2010 Texas Instruments, Inc. 68c2ecf20Sopenharmony_ci * Copyright (C) 2009-2010 Nokia Corporation 78c2ecf20Sopenharmony_ci * 88c2ecf20Sopenharmony_ci * Written by Paul Walmsley 98c2ecf20Sopenharmony_ci * Testing and integration fixes by Jouni Högander 108c2ecf20Sopenharmony_ci * 118c2ecf20Sopenharmony_ci * 36xx support added by Vishwanath BS, Richard Woodruff, and Nishanth 128c2ecf20Sopenharmony_ci * Menon 138c2ecf20Sopenharmony_ci * 148c2ecf20Sopenharmony_ci * Parts of this code are based on code written by 158c2ecf20Sopenharmony_ci * Richard Woodruff, Tony Lindgren, Tuukka Tikkanen, Karthik Dasu 168c2ecf20Sopenharmony_ci */ 178c2ecf20Sopenharmony_ci 188c2ecf20Sopenharmony_ci#include <linux/kernel.h> 198c2ecf20Sopenharmony_ci#include <linux/device.h> 208c2ecf20Sopenharmony_ci#include <linux/list.h> 218c2ecf20Sopenharmony_ci#include <linux/errno.h> 228c2ecf20Sopenharmony_ci#include <linux/delay.h> 238c2ecf20Sopenharmony_ci#include <linux/clk.h> 248c2ecf20Sopenharmony_ci#include <linux/io.h> 258c2ecf20Sopenharmony_ci#include <linux/bitops.h> 268c2ecf20Sopenharmony_ci#include <linux/clkdev.h> 278c2ecf20Sopenharmony_ci#include <linux/clk/ti.h> 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_ci#include "clock.h" 308c2ecf20Sopenharmony_ci 318c2ecf20Sopenharmony_ci/* CM_AUTOIDLE_PLL*.AUTO_* bit values */ 328c2ecf20Sopenharmony_ci#define DPLL_AUTOIDLE_DISABLE 0x0 338c2ecf20Sopenharmony_ci#define DPLL_AUTOIDLE_LOW_POWER_STOP 0x1 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_ci#define MAX_DPLL_WAIT_TRIES 1000000 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_ci#define OMAP3XXX_EN_DPLL_LOCKED 0x7 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_ci/* Forward declarations */ 408c2ecf20Sopenharmony_cistatic u32 omap3_dpll_autoidle_read(struct clk_hw_omap *clk); 418c2ecf20Sopenharmony_cistatic void omap3_dpll_deny_idle(struct clk_hw_omap *clk); 428c2ecf20Sopenharmony_cistatic void omap3_dpll_allow_idle(struct clk_hw_omap *clk); 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_ci/* Private functions */ 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_ci/* _omap3_dpll_write_clken - write clken_bits arg to a DPLL's enable bits */ 478c2ecf20Sopenharmony_cistatic void _omap3_dpll_write_clken(struct clk_hw_omap *clk, u8 clken_bits) 488c2ecf20Sopenharmony_ci{ 498c2ecf20Sopenharmony_ci const struct dpll_data *dd; 508c2ecf20Sopenharmony_ci u32 v; 518c2ecf20Sopenharmony_ci 528c2ecf20Sopenharmony_ci dd = clk->dpll_data; 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_ci v = ti_clk_ll_ops->clk_readl(&dd->control_reg); 558c2ecf20Sopenharmony_ci v &= ~dd->enable_mask; 568c2ecf20Sopenharmony_ci v |= clken_bits << __ffs(dd->enable_mask); 578c2ecf20Sopenharmony_ci ti_clk_ll_ops->clk_writel(v, &dd->control_reg); 588c2ecf20Sopenharmony_ci} 598c2ecf20Sopenharmony_ci 608c2ecf20Sopenharmony_ci/* _omap3_wait_dpll_status: wait for a DPLL to enter a specific state */ 618c2ecf20Sopenharmony_cistatic int _omap3_wait_dpll_status(struct clk_hw_omap *clk, u8 state) 628c2ecf20Sopenharmony_ci{ 638c2ecf20Sopenharmony_ci const struct dpll_data *dd; 648c2ecf20Sopenharmony_ci int i = 0; 658c2ecf20Sopenharmony_ci int ret = -EINVAL; 668c2ecf20Sopenharmony_ci const char *clk_name; 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_ci dd = clk->dpll_data; 698c2ecf20Sopenharmony_ci clk_name = clk_hw_get_name(&clk->hw); 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_ci state <<= __ffs(dd->idlest_mask); 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_ci while (((ti_clk_ll_ops->clk_readl(&dd->idlest_reg) & dd->idlest_mask) 748c2ecf20Sopenharmony_ci != state) && i < MAX_DPLL_WAIT_TRIES) { 758c2ecf20Sopenharmony_ci i++; 768c2ecf20Sopenharmony_ci udelay(1); 778c2ecf20Sopenharmony_ci } 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_ci if (i == MAX_DPLL_WAIT_TRIES) { 808c2ecf20Sopenharmony_ci pr_err("clock: %s failed transition to '%s'\n", 818c2ecf20Sopenharmony_ci clk_name, (state) ? "locked" : "bypassed"); 828c2ecf20Sopenharmony_ci } else { 838c2ecf20Sopenharmony_ci pr_debug("clock: %s transition to '%s' in %d loops\n", 848c2ecf20Sopenharmony_ci clk_name, (state) ? "locked" : "bypassed", i); 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_ci ret = 0; 878c2ecf20Sopenharmony_ci } 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_ci return ret; 908c2ecf20Sopenharmony_ci} 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_ci/* From 3430 TRM ES2 4.7.6.2 */ 938c2ecf20Sopenharmony_cistatic u16 _omap3_dpll_compute_freqsel(struct clk_hw_omap *clk, u8 n) 948c2ecf20Sopenharmony_ci{ 958c2ecf20Sopenharmony_ci unsigned long fint; 968c2ecf20Sopenharmony_ci u16 f = 0; 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_ci fint = clk_hw_get_rate(clk->dpll_data->clk_ref) / n; 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_ci pr_debug("clock: fint is %lu\n", fint); 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_ci if (fint >= 750000 && fint <= 1000000) 1038c2ecf20Sopenharmony_ci f = 0x3; 1048c2ecf20Sopenharmony_ci else if (fint > 1000000 && fint <= 1250000) 1058c2ecf20Sopenharmony_ci f = 0x4; 1068c2ecf20Sopenharmony_ci else if (fint > 1250000 && fint <= 1500000) 1078c2ecf20Sopenharmony_ci f = 0x5; 1088c2ecf20Sopenharmony_ci else if (fint > 1500000 && fint <= 1750000) 1098c2ecf20Sopenharmony_ci f = 0x6; 1108c2ecf20Sopenharmony_ci else if (fint > 1750000 && fint <= 2100000) 1118c2ecf20Sopenharmony_ci f = 0x7; 1128c2ecf20Sopenharmony_ci else if (fint > 7500000 && fint <= 10000000) 1138c2ecf20Sopenharmony_ci f = 0xB; 1148c2ecf20Sopenharmony_ci else if (fint > 10000000 && fint <= 12500000) 1158c2ecf20Sopenharmony_ci f = 0xC; 1168c2ecf20Sopenharmony_ci else if (fint > 12500000 && fint <= 15000000) 1178c2ecf20Sopenharmony_ci f = 0xD; 1188c2ecf20Sopenharmony_ci else if (fint > 15000000 && fint <= 17500000) 1198c2ecf20Sopenharmony_ci f = 0xE; 1208c2ecf20Sopenharmony_ci else if (fint > 17500000 && fint <= 21000000) 1218c2ecf20Sopenharmony_ci f = 0xF; 1228c2ecf20Sopenharmony_ci else 1238c2ecf20Sopenharmony_ci pr_debug("clock: unknown freqsel setting for %d\n", n); 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_ci return f; 1268c2ecf20Sopenharmony_ci} 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_ci/* 1298c2ecf20Sopenharmony_ci * _omap3_noncore_dpll_lock - instruct a DPLL to lock and wait for readiness 1308c2ecf20Sopenharmony_ci * @clk: pointer to a DPLL struct clk 1318c2ecf20Sopenharmony_ci * 1328c2ecf20Sopenharmony_ci * Instructs a non-CORE DPLL to lock. Waits for the DPLL to report 1338c2ecf20Sopenharmony_ci * readiness before returning. Will save and restore the DPLL's 1348c2ecf20Sopenharmony_ci * autoidle state across the enable, per the CDP code. If the DPLL 1358c2ecf20Sopenharmony_ci * locked successfully, return 0; if the DPLL did not lock in the time 1368c2ecf20Sopenharmony_ci * allotted, or DPLL3 was passed in, return -EINVAL. 1378c2ecf20Sopenharmony_ci */ 1388c2ecf20Sopenharmony_cistatic int _omap3_noncore_dpll_lock(struct clk_hw_omap *clk) 1398c2ecf20Sopenharmony_ci{ 1408c2ecf20Sopenharmony_ci const struct dpll_data *dd; 1418c2ecf20Sopenharmony_ci u8 ai; 1428c2ecf20Sopenharmony_ci u8 state = 1; 1438c2ecf20Sopenharmony_ci int r = 0; 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_ci pr_debug("clock: locking DPLL %s\n", clk_hw_get_name(&clk->hw)); 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_ci dd = clk->dpll_data; 1488c2ecf20Sopenharmony_ci state <<= __ffs(dd->idlest_mask); 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_ci /* Check if already locked */ 1518c2ecf20Sopenharmony_ci if ((ti_clk_ll_ops->clk_readl(&dd->idlest_reg) & dd->idlest_mask) == 1528c2ecf20Sopenharmony_ci state) 1538c2ecf20Sopenharmony_ci goto done; 1548c2ecf20Sopenharmony_ci 1558c2ecf20Sopenharmony_ci ai = omap3_dpll_autoidle_read(clk); 1568c2ecf20Sopenharmony_ci 1578c2ecf20Sopenharmony_ci if (ai) 1588c2ecf20Sopenharmony_ci omap3_dpll_deny_idle(clk); 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_ci _omap3_dpll_write_clken(clk, DPLL_LOCKED); 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_ci r = _omap3_wait_dpll_status(clk, 1); 1638c2ecf20Sopenharmony_ci 1648c2ecf20Sopenharmony_ci if (ai) 1658c2ecf20Sopenharmony_ci omap3_dpll_allow_idle(clk); 1668c2ecf20Sopenharmony_ci 1678c2ecf20Sopenharmony_cidone: 1688c2ecf20Sopenharmony_ci return r; 1698c2ecf20Sopenharmony_ci} 1708c2ecf20Sopenharmony_ci 1718c2ecf20Sopenharmony_ci/* 1728c2ecf20Sopenharmony_ci * _omap3_noncore_dpll_bypass - instruct a DPLL to bypass and wait for readiness 1738c2ecf20Sopenharmony_ci * @clk: pointer to a DPLL struct clk 1748c2ecf20Sopenharmony_ci * 1758c2ecf20Sopenharmony_ci * Instructs a non-CORE DPLL to enter low-power bypass mode. In 1768c2ecf20Sopenharmony_ci * bypass mode, the DPLL's rate is set equal to its parent clock's 1778c2ecf20Sopenharmony_ci * rate. Waits for the DPLL to report readiness before returning. 1788c2ecf20Sopenharmony_ci * Will save and restore the DPLL's autoidle state across the enable, 1798c2ecf20Sopenharmony_ci * per the CDP code. If the DPLL entered bypass mode successfully, 1808c2ecf20Sopenharmony_ci * return 0; if the DPLL did not enter bypass in the time allotted, or 1818c2ecf20Sopenharmony_ci * DPLL3 was passed in, or the DPLL does not support low-power bypass, 1828c2ecf20Sopenharmony_ci * return -EINVAL. 1838c2ecf20Sopenharmony_ci */ 1848c2ecf20Sopenharmony_cistatic int _omap3_noncore_dpll_bypass(struct clk_hw_omap *clk) 1858c2ecf20Sopenharmony_ci{ 1868c2ecf20Sopenharmony_ci int r; 1878c2ecf20Sopenharmony_ci u8 ai; 1888c2ecf20Sopenharmony_ci 1898c2ecf20Sopenharmony_ci if (!(clk->dpll_data->modes & (1 << DPLL_LOW_POWER_BYPASS))) 1908c2ecf20Sopenharmony_ci return -EINVAL; 1918c2ecf20Sopenharmony_ci 1928c2ecf20Sopenharmony_ci pr_debug("clock: configuring DPLL %s for low-power bypass\n", 1938c2ecf20Sopenharmony_ci clk_hw_get_name(&clk->hw)); 1948c2ecf20Sopenharmony_ci 1958c2ecf20Sopenharmony_ci ai = omap3_dpll_autoidle_read(clk); 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_ci _omap3_dpll_write_clken(clk, DPLL_LOW_POWER_BYPASS); 1988c2ecf20Sopenharmony_ci 1998c2ecf20Sopenharmony_ci r = _omap3_wait_dpll_status(clk, 0); 2008c2ecf20Sopenharmony_ci 2018c2ecf20Sopenharmony_ci if (ai) 2028c2ecf20Sopenharmony_ci omap3_dpll_allow_idle(clk); 2038c2ecf20Sopenharmony_ci 2048c2ecf20Sopenharmony_ci return r; 2058c2ecf20Sopenharmony_ci} 2068c2ecf20Sopenharmony_ci 2078c2ecf20Sopenharmony_ci/* 2088c2ecf20Sopenharmony_ci * _omap3_noncore_dpll_stop - instruct a DPLL to stop 2098c2ecf20Sopenharmony_ci * @clk: pointer to a DPLL struct clk 2108c2ecf20Sopenharmony_ci * 2118c2ecf20Sopenharmony_ci * Instructs a non-CORE DPLL to enter low-power stop. Will save and 2128c2ecf20Sopenharmony_ci * restore the DPLL's autoidle state across the stop, per the CDP 2138c2ecf20Sopenharmony_ci * code. If DPLL3 was passed in, or the DPLL does not support 2148c2ecf20Sopenharmony_ci * low-power stop, return -EINVAL; otherwise, return 0. 2158c2ecf20Sopenharmony_ci */ 2168c2ecf20Sopenharmony_cistatic int _omap3_noncore_dpll_stop(struct clk_hw_omap *clk) 2178c2ecf20Sopenharmony_ci{ 2188c2ecf20Sopenharmony_ci u8 ai; 2198c2ecf20Sopenharmony_ci 2208c2ecf20Sopenharmony_ci if (!(clk->dpll_data->modes & (1 << DPLL_LOW_POWER_STOP))) 2218c2ecf20Sopenharmony_ci return -EINVAL; 2228c2ecf20Sopenharmony_ci 2238c2ecf20Sopenharmony_ci pr_debug("clock: stopping DPLL %s\n", clk_hw_get_name(&clk->hw)); 2248c2ecf20Sopenharmony_ci 2258c2ecf20Sopenharmony_ci ai = omap3_dpll_autoidle_read(clk); 2268c2ecf20Sopenharmony_ci 2278c2ecf20Sopenharmony_ci _omap3_dpll_write_clken(clk, DPLL_LOW_POWER_STOP); 2288c2ecf20Sopenharmony_ci 2298c2ecf20Sopenharmony_ci if (ai) 2308c2ecf20Sopenharmony_ci omap3_dpll_allow_idle(clk); 2318c2ecf20Sopenharmony_ci 2328c2ecf20Sopenharmony_ci return 0; 2338c2ecf20Sopenharmony_ci} 2348c2ecf20Sopenharmony_ci 2358c2ecf20Sopenharmony_ci/** 2368c2ecf20Sopenharmony_ci * _lookup_dco - Lookup DCO used by j-type DPLL 2378c2ecf20Sopenharmony_ci * @clk: pointer to a DPLL struct clk 2388c2ecf20Sopenharmony_ci * @dco: digital control oscillator selector 2398c2ecf20Sopenharmony_ci * @m: DPLL multiplier to set 2408c2ecf20Sopenharmony_ci * @n: DPLL divider to set 2418c2ecf20Sopenharmony_ci * 2428c2ecf20Sopenharmony_ci * See 36xx TRM section 3.5.3.3.3.2 "Type B DPLL (Low-Jitter)" 2438c2ecf20Sopenharmony_ci * 2448c2ecf20Sopenharmony_ci * XXX This code is not needed for 3430/AM35xx; can it be optimized 2458c2ecf20Sopenharmony_ci * out in non-multi-OMAP builds for those chips? 2468c2ecf20Sopenharmony_ci */ 2478c2ecf20Sopenharmony_cistatic void _lookup_dco(struct clk_hw_omap *clk, u8 *dco, u16 m, u8 n) 2488c2ecf20Sopenharmony_ci{ 2498c2ecf20Sopenharmony_ci unsigned long fint, clkinp; /* watch out for overflow */ 2508c2ecf20Sopenharmony_ci 2518c2ecf20Sopenharmony_ci clkinp = clk_hw_get_rate(clk_hw_get_parent(&clk->hw)); 2528c2ecf20Sopenharmony_ci fint = (clkinp / n) * m; 2538c2ecf20Sopenharmony_ci 2548c2ecf20Sopenharmony_ci if (fint < 1000000000) 2558c2ecf20Sopenharmony_ci *dco = 2; 2568c2ecf20Sopenharmony_ci else 2578c2ecf20Sopenharmony_ci *dco = 4; 2588c2ecf20Sopenharmony_ci} 2598c2ecf20Sopenharmony_ci 2608c2ecf20Sopenharmony_ci/** 2618c2ecf20Sopenharmony_ci * _lookup_sddiv - Calculate sigma delta divider for j-type DPLL 2628c2ecf20Sopenharmony_ci * @clk: pointer to a DPLL struct clk 2638c2ecf20Sopenharmony_ci * @sd_div: target sigma-delta divider 2648c2ecf20Sopenharmony_ci * @m: DPLL multiplier to set 2658c2ecf20Sopenharmony_ci * @n: DPLL divider to set 2668c2ecf20Sopenharmony_ci * 2678c2ecf20Sopenharmony_ci * See 36xx TRM section 3.5.3.3.3.2 "Type B DPLL (Low-Jitter)" 2688c2ecf20Sopenharmony_ci * 2698c2ecf20Sopenharmony_ci * XXX This code is not needed for 3430/AM35xx; can it be optimized 2708c2ecf20Sopenharmony_ci * out in non-multi-OMAP builds for those chips? 2718c2ecf20Sopenharmony_ci */ 2728c2ecf20Sopenharmony_cistatic void _lookup_sddiv(struct clk_hw_omap *clk, u8 *sd_div, u16 m, u8 n) 2738c2ecf20Sopenharmony_ci{ 2748c2ecf20Sopenharmony_ci unsigned long clkinp, sd; /* watch out for overflow */ 2758c2ecf20Sopenharmony_ci int mod1, mod2; 2768c2ecf20Sopenharmony_ci 2778c2ecf20Sopenharmony_ci clkinp = clk_hw_get_rate(clk_hw_get_parent(&clk->hw)); 2788c2ecf20Sopenharmony_ci 2798c2ecf20Sopenharmony_ci /* 2808c2ecf20Sopenharmony_ci * target sigma-delta to near 250MHz 2818c2ecf20Sopenharmony_ci * sd = ceil[(m/(n+1)) * (clkinp_MHz / 250)] 2828c2ecf20Sopenharmony_ci */ 2838c2ecf20Sopenharmony_ci clkinp /= 100000; /* shift from MHz to 10*Hz for 38.4 and 19.2 */ 2848c2ecf20Sopenharmony_ci mod1 = (clkinp * m) % (250 * n); 2858c2ecf20Sopenharmony_ci sd = (clkinp * m) / (250 * n); 2868c2ecf20Sopenharmony_ci mod2 = sd % 10; 2878c2ecf20Sopenharmony_ci sd /= 10; 2888c2ecf20Sopenharmony_ci 2898c2ecf20Sopenharmony_ci if (mod1 || mod2) 2908c2ecf20Sopenharmony_ci sd++; 2918c2ecf20Sopenharmony_ci *sd_div = sd; 2928c2ecf20Sopenharmony_ci} 2938c2ecf20Sopenharmony_ci 2948c2ecf20Sopenharmony_ci/* 2958c2ecf20Sopenharmony_ci * _omap3_noncore_dpll_program - set non-core DPLL M,N values directly 2968c2ecf20Sopenharmony_ci * @clk: struct clk * of DPLL to set 2978c2ecf20Sopenharmony_ci * @freqsel: FREQSEL value to set 2988c2ecf20Sopenharmony_ci * 2998c2ecf20Sopenharmony_ci * Program the DPLL with the last M, N values calculated, and wait for 3008c2ecf20Sopenharmony_ci * the DPLL to lock. Returns -EINVAL upon error, or 0 upon success. 3018c2ecf20Sopenharmony_ci */ 3028c2ecf20Sopenharmony_cistatic int omap3_noncore_dpll_program(struct clk_hw_omap *clk, u16 freqsel) 3038c2ecf20Sopenharmony_ci{ 3048c2ecf20Sopenharmony_ci struct dpll_data *dd = clk->dpll_data; 3058c2ecf20Sopenharmony_ci u8 dco, sd_div, ai = 0; 3068c2ecf20Sopenharmony_ci u32 v; 3078c2ecf20Sopenharmony_ci bool errata_i810; 3088c2ecf20Sopenharmony_ci 3098c2ecf20Sopenharmony_ci /* 3430 ES2 TRM: 4.7.6.9 DPLL Programming Sequence */ 3108c2ecf20Sopenharmony_ci _omap3_noncore_dpll_bypass(clk); 3118c2ecf20Sopenharmony_ci 3128c2ecf20Sopenharmony_ci /* 3138c2ecf20Sopenharmony_ci * Set jitter correction. Jitter correction applicable for OMAP343X 3148c2ecf20Sopenharmony_ci * only since freqsel field is no longer present on other devices. 3158c2ecf20Sopenharmony_ci */ 3168c2ecf20Sopenharmony_ci if (ti_clk_get_features()->flags & TI_CLK_DPLL_HAS_FREQSEL) { 3178c2ecf20Sopenharmony_ci v = ti_clk_ll_ops->clk_readl(&dd->control_reg); 3188c2ecf20Sopenharmony_ci v &= ~dd->freqsel_mask; 3198c2ecf20Sopenharmony_ci v |= freqsel << __ffs(dd->freqsel_mask); 3208c2ecf20Sopenharmony_ci ti_clk_ll_ops->clk_writel(v, &dd->control_reg); 3218c2ecf20Sopenharmony_ci } 3228c2ecf20Sopenharmony_ci 3238c2ecf20Sopenharmony_ci /* Set DPLL multiplier, divider */ 3248c2ecf20Sopenharmony_ci v = ti_clk_ll_ops->clk_readl(&dd->mult_div1_reg); 3258c2ecf20Sopenharmony_ci 3268c2ecf20Sopenharmony_ci /* Handle Duty Cycle Correction */ 3278c2ecf20Sopenharmony_ci if (dd->dcc_mask) { 3288c2ecf20Sopenharmony_ci if (dd->last_rounded_rate >= dd->dcc_rate) 3298c2ecf20Sopenharmony_ci v |= dd->dcc_mask; /* Enable DCC */ 3308c2ecf20Sopenharmony_ci else 3318c2ecf20Sopenharmony_ci v &= ~dd->dcc_mask; /* Disable DCC */ 3328c2ecf20Sopenharmony_ci } 3338c2ecf20Sopenharmony_ci 3348c2ecf20Sopenharmony_ci v &= ~(dd->mult_mask | dd->div1_mask); 3358c2ecf20Sopenharmony_ci v |= dd->last_rounded_m << __ffs(dd->mult_mask); 3368c2ecf20Sopenharmony_ci v |= (dd->last_rounded_n - 1) << __ffs(dd->div1_mask); 3378c2ecf20Sopenharmony_ci 3388c2ecf20Sopenharmony_ci /* Configure dco and sd_div for dplls that have these fields */ 3398c2ecf20Sopenharmony_ci if (dd->dco_mask) { 3408c2ecf20Sopenharmony_ci _lookup_dco(clk, &dco, dd->last_rounded_m, dd->last_rounded_n); 3418c2ecf20Sopenharmony_ci v &= ~(dd->dco_mask); 3428c2ecf20Sopenharmony_ci v |= dco << __ffs(dd->dco_mask); 3438c2ecf20Sopenharmony_ci } 3448c2ecf20Sopenharmony_ci if (dd->sddiv_mask) { 3458c2ecf20Sopenharmony_ci _lookup_sddiv(clk, &sd_div, dd->last_rounded_m, 3468c2ecf20Sopenharmony_ci dd->last_rounded_n); 3478c2ecf20Sopenharmony_ci v &= ~(dd->sddiv_mask); 3488c2ecf20Sopenharmony_ci v |= sd_div << __ffs(dd->sddiv_mask); 3498c2ecf20Sopenharmony_ci } 3508c2ecf20Sopenharmony_ci 3518c2ecf20Sopenharmony_ci /* 3528c2ecf20Sopenharmony_ci * Errata i810 - DPLL controller can get stuck while transitioning 3538c2ecf20Sopenharmony_ci * to a power saving state. Software must ensure the DPLL can not 3548c2ecf20Sopenharmony_ci * transition to a low power state while changing M/N values. 3558c2ecf20Sopenharmony_ci * Easiest way to accomplish this is to prevent DPLL autoidle 3568c2ecf20Sopenharmony_ci * before doing the M/N re-program. 3578c2ecf20Sopenharmony_ci */ 3588c2ecf20Sopenharmony_ci errata_i810 = ti_clk_get_features()->flags & TI_CLK_ERRATA_I810; 3598c2ecf20Sopenharmony_ci 3608c2ecf20Sopenharmony_ci if (errata_i810) { 3618c2ecf20Sopenharmony_ci ai = omap3_dpll_autoidle_read(clk); 3628c2ecf20Sopenharmony_ci if (ai) { 3638c2ecf20Sopenharmony_ci omap3_dpll_deny_idle(clk); 3648c2ecf20Sopenharmony_ci 3658c2ecf20Sopenharmony_ci /* OCP barrier */ 3668c2ecf20Sopenharmony_ci omap3_dpll_autoidle_read(clk); 3678c2ecf20Sopenharmony_ci } 3688c2ecf20Sopenharmony_ci } 3698c2ecf20Sopenharmony_ci 3708c2ecf20Sopenharmony_ci ti_clk_ll_ops->clk_writel(v, &dd->mult_div1_reg); 3718c2ecf20Sopenharmony_ci 3728c2ecf20Sopenharmony_ci /* Set 4X multiplier and low-power mode */ 3738c2ecf20Sopenharmony_ci if (dd->m4xen_mask || dd->lpmode_mask) { 3748c2ecf20Sopenharmony_ci v = ti_clk_ll_ops->clk_readl(&dd->control_reg); 3758c2ecf20Sopenharmony_ci 3768c2ecf20Sopenharmony_ci if (dd->m4xen_mask) { 3778c2ecf20Sopenharmony_ci if (dd->last_rounded_m4xen) 3788c2ecf20Sopenharmony_ci v |= dd->m4xen_mask; 3798c2ecf20Sopenharmony_ci else 3808c2ecf20Sopenharmony_ci v &= ~dd->m4xen_mask; 3818c2ecf20Sopenharmony_ci } 3828c2ecf20Sopenharmony_ci 3838c2ecf20Sopenharmony_ci if (dd->lpmode_mask) { 3848c2ecf20Sopenharmony_ci if (dd->last_rounded_lpmode) 3858c2ecf20Sopenharmony_ci v |= dd->lpmode_mask; 3868c2ecf20Sopenharmony_ci else 3878c2ecf20Sopenharmony_ci v &= ~dd->lpmode_mask; 3888c2ecf20Sopenharmony_ci } 3898c2ecf20Sopenharmony_ci 3908c2ecf20Sopenharmony_ci ti_clk_ll_ops->clk_writel(v, &dd->control_reg); 3918c2ecf20Sopenharmony_ci } 3928c2ecf20Sopenharmony_ci 3938c2ecf20Sopenharmony_ci /* We let the clock framework set the other output dividers later */ 3948c2ecf20Sopenharmony_ci 3958c2ecf20Sopenharmony_ci /* REVISIT: Set ramp-up delay? */ 3968c2ecf20Sopenharmony_ci 3978c2ecf20Sopenharmony_ci _omap3_noncore_dpll_lock(clk); 3988c2ecf20Sopenharmony_ci 3998c2ecf20Sopenharmony_ci if (errata_i810 && ai) 4008c2ecf20Sopenharmony_ci omap3_dpll_allow_idle(clk); 4018c2ecf20Sopenharmony_ci 4028c2ecf20Sopenharmony_ci return 0; 4038c2ecf20Sopenharmony_ci} 4048c2ecf20Sopenharmony_ci 4058c2ecf20Sopenharmony_ci/* Public functions */ 4068c2ecf20Sopenharmony_ci 4078c2ecf20Sopenharmony_ci/** 4088c2ecf20Sopenharmony_ci * omap3_dpll_recalc - recalculate DPLL rate 4098c2ecf20Sopenharmony_ci * @clk: DPLL struct clk 4108c2ecf20Sopenharmony_ci * 4118c2ecf20Sopenharmony_ci * Recalculate and propagate the DPLL rate. 4128c2ecf20Sopenharmony_ci */ 4138c2ecf20Sopenharmony_ciunsigned long omap3_dpll_recalc(struct clk_hw *hw, unsigned long parent_rate) 4148c2ecf20Sopenharmony_ci{ 4158c2ecf20Sopenharmony_ci struct clk_hw_omap *clk = to_clk_hw_omap(hw); 4168c2ecf20Sopenharmony_ci 4178c2ecf20Sopenharmony_ci return omap2_get_dpll_rate(clk); 4188c2ecf20Sopenharmony_ci} 4198c2ecf20Sopenharmony_ci 4208c2ecf20Sopenharmony_ci/* Non-CORE DPLL (e.g., DPLLs that do not control SDRC) clock functions */ 4218c2ecf20Sopenharmony_ci 4228c2ecf20Sopenharmony_ci/** 4238c2ecf20Sopenharmony_ci * omap3_noncore_dpll_enable - instruct a DPLL to enter bypass or lock mode 4248c2ecf20Sopenharmony_ci * @clk: pointer to a DPLL struct clk 4258c2ecf20Sopenharmony_ci * 4268c2ecf20Sopenharmony_ci * Instructs a non-CORE DPLL to enable, e.g., to enter bypass or lock. 4278c2ecf20Sopenharmony_ci * The choice of modes depends on the DPLL's programmed rate: if it is 4288c2ecf20Sopenharmony_ci * the same as the DPLL's parent clock, it will enter bypass; 4298c2ecf20Sopenharmony_ci * otherwise, it will enter lock. This code will wait for the DPLL to 4308c2ecf20Sopenharmony_ci * indicate readiness before returning, unless the DPLL takes too long 4318c2ecf20Sopenharmony_ci * to enter the target state. Intended to be used as the struct clk's 4328c2ecf20Sopenharmony_ci * enable function. If DPLL3 was passed in, or the DPLL does not 4338c2ecf20Sopenharmony_ci * support low-power stop, or if the DPLL took too long to enter 4348c2ecf20Sopenharmony_ci * bypass or lock, return -EINVAL; otherwise, return 0. 4358c2ecf20Sopenharmony_ci */ 4368c2ecf20Sopenharmony_ciint omap3_noncore_dpll_enable(struct clk_hw *hw) 4378c2ecf20Sopenharmony_ci{ 4388c2ecf20Sopenharmony_ci struct clk_hw_omap *clk = to_clk_hw_omap(hw); 4398c2ecf20Sopenharmony_ci int r; 4408c2ecf20Sopenharmony_ci struct dpll_data *dd; 4418c2ecf20Sopenharmony_ci struct clk_hw *parent; 4428c2ecf20Sopenharmony_ci 4438c2ecf20Sopenharmony_ci dd = clk->dpll_data; 4448c2ecf20Sopenharmony_ci if (!dd) 4458c2ecf20Sopenharmony_ci return -EINVAL; 4468c2ecf20Sopenharmony_ci 4478c2ecf20Sopenharmony_ci if (clk->clkdm) { 4488c2ecf20Sopenharmony_ci r = ti_clk_ll_ops->clkdm_clk_enable(clk->clkdm, hw->clk); 4498c2ecf20Sopenharmony_ci if (r) { 4508c2ecf20Sopenharmony_ci WARN(1, 4518c2ecf20Sopenharmony_ci "%s: could not enable %s's clockdomain %s: %d\n", 4528c2ecf20Sopenharmony_ci __func__, clk_hw_get_name(hw), 4538c2ecf20Sopenharmony_ci clk->clkdm_name, r); 4548c2ecf20Sopenharmony_ci return r; 4558c2ecf20Sopenharmony_ci } 4568c2ecf20Sopenharmony_ci } 4578c2ecf20Sopenharmony_ci 4588c2ecf20Sopenharmony_ci parent = clk_hw_get_parent(hw); 4598c2ecf20Sopenharmony_ci 4608c2ecf20Sopenharmony_ci if (clk_hw_get_rate(hw) == clk_hw_get_rate(dd->clk_bypass)) { 4618c2ecf20Sopenharmony_ci WARN_ON(parent != dd->clk_bypass); 4628c2ecf20Sopenharmony_ci r = _omap3_noncore_dpll_bypass(clk); 4638c2ecf20Sopenharmony_ci } else { 4648c2ecf20Sopenharmony_ci WARN_ON(parent != dd->clk_ref); 4658c2ecf20Sopenharmony_ci r = _omap3_noncore_dpll_lock(clk); 4668c2ecf20Sopenharmony_ci } 4678c2ecf20Sopenharmony_ci 4688c2ecf20Sopenharmony_ci return r; 4698c2ecf20Sopenharmony_ci} 4708c2ecf20Sopenharmony_ci 4718c2ecf20Sopenharmony_ci/** 4728c2ecf20Sopenharmony_ci * omap3_noncore_dpll_disable - instruct a DPLL to enter low-power stop 4738c2ecf20Sopenharmony_ci * @clk: pointer to a DPLL struct clk 4748c2ecf20Sopenharmony_ci * 4758c2ecf20Sopenharmony_ci * Instructs a non-CORE DPLL to enter low-power stop. This function is 4768c2ecf20Sopenharmony_ci * intended for use in struct clkops. No return value. 4778c2ecf20Sopenharmony_ci */ 4788c2ecf20Sopenharmony_civoid omap3_noncore_dpll_disable(struct clk_hw *hw) 4798c2ecf20Sopenharmony_ci{ 4808c2ecf20Sopenharmony_ci struct clk_hw_omap *clk = to_clk_hw_omap(hw); 4818c2ecf20Sopenharmony_ci 4828c2ecf20Sopenharmony_ci _omap3_noncore_dpll_stop(clk); 4838c2ecf20Sopenharmony_ci if (clk->clkdm) 4848c2ecf20Sopenharmony_ci ti_clk_ll_ops->clkdm_clk_disable(clk->clkdm, hw->clk); 4858c2ecf20Sopenharmony_ci} 4868c2ecf20Sopenharmony_ci 4878c2ecf20Sopenharmony_ci/* Non-CORE DPLL rate set code */ 4888c2ecf20Sopenharmony_ci 4898c2ecf20Sopenharmony_ci/** 4908c2ecf20Sopenharmony_ci * omap3_noncore_dpll_determine_rate - determine rate for a DPLL 4918c2ecf20Sopenharmony_ci * @hw: pointer to the clock to determine rate for 4928c2ecf20Sopenharmony_ci * @req: target rate request 4938c2ecf20Sopenharmony_ci * 4948c2ecf20Sopenharmony_ci * Determines which DPLL mode to use for reaching a desired target rate. 4958c2ecf20Sopenharmony_ci * Checks whether the DPLL shall be in bypass or locked mode, and if 4968c2ecf20Sopenharmony_ci * locked, calculates the M,N values for the DPLL via round-rate. 4978c2ecf20Sopenharmony_ci * Returns a 0 on success, negative error value in failure. 4988c2ecf20Sopenharmony_ci */ 4998c2ecf20Sopenharmony_ciint omap3_noncore_dpll_determine_rate(struct clk_hw *hw, 5008c2ecf20Sopenharmony_ci struct clk_rate_request *req) 5018c2ecf20Sopenharmony_ci{ 5028c2ecf20Sopenharmony_ci struct clk_hw_omap *clk = to_clk_hw_omap(hw); 5038c2ecf20Sopenharmony_ci struct dpll_data *dd; 5048c2ecf20Sopenharmony_ci 5058c2ecf20Sopenharmony_ci if (!req->rate) 5068c2ecf20Sopenharmony_ci return -EINVAL; 5078c2ecf20Sopenharmony_ci 5088c2ecf20Sopenharmony_ci dd = clk->dpll_data; 5098c2ecf20Sopenharmony_ci if (!dd) 5108c2ecf20Sopenharmony_ci return -EINVAL; 5118c2ecf20Sopenharmony_ci 5128c2ecf20Sopenharmony_ci if (clk_hw_get_rate(dd->clk_bypass) == req->rate && 5138c2ecf20Sopenharmony_ci (dd->modes & (1 << DPLL_LOW_POWER_BYPASS))) { 5148c2ecf20Sopenharmony_ci req->best_parent_hw = dd->clk_bypass; 5158c2ecf20Sopenharmony_ci } else { 5168c2ecf20Sopenharmony_ci req->rate = omap2_dpll_round_rate(hw, req->rate, 5178c2ecf20Sopenharmony_ci &req->best_parent_rate); 5188c2ecf20Sopenharmony_ci req->best_parent_hw = dd->clk_ref; 5198c2ecf20Sopenharmony_ci } 5208c2ecf20Sopenharmony_ci 5218c2ecf20Sopenharmony_ci req->best_parent_rate = req->rate; 5228c2ecf20Sopenharmony_ci 5238c2ecf20Sopenharmony_ci return 0; 5248c2ecf20Sopenharmony_ci} 5258c2ecf20Sopenharmony_ci 5268c2ecf20Sopenharmony_ci/** 5278c2ecf20Sopenharmony_ci * omap3_noncore_dpll_set_parent - set parent for a DPLL clock 5288c2ecf20Sopenharmony_ci * @hw: pointer to the clock to set parent for 5298c2ecf20Sopenharmony_ci * @index: parent index to select 5308c2ecf20Sopenharmony_ci * 5318c2ecf20Sopenharmony_ci * Sets parent for a DPLL clock. This sets the DPLL into bypass or 5328c2ecf20Sopenharmony_ci * locked mode. Returns 0 with success, negative error value otherwise. 5338c2ecf20Sopenharmony_ci */ 5348c2ecf20Sopenharmony_ciint omap3_noncore_dpll_set_parent(struct clk_hw *hw, u8 index) 5358c2ecf20Sopenharmony_ci{ 5368c2ecf20Sopenharmony_ci struct clk_hw_omap *clk = to_clk_hw_omap(hw); 5378c2ecf20Sopenharmony_ci int ret; 5388c2ecf20Sopenharmony_ci 5398c2ecf20Sopenharmony_ci if (!hw) 5408c2ecf20Sopenharmony_ci return -EINVAL; 5418c2ecf20Sopenharmony_ci 5428c2ecf20Sopenharmony_ci if (index) 5438c2ecf20Sopenharmony_ci ret = _omap3_noncore_dpll_bypass(clk); 5448c2ecf20Sopenharmony_ci else 5458c2ecf20Sopenharmony_ci ret = _omap3_noncore_dpll_lock(clk); 5468c2ecf20Sopenharmony_ci 5478c2ecf20Sopenharmony_ci return ret; 5488c2ecf20Sopenharmony_ci} 5498c2ecf20Sopenharmony_ci 5508c2ecf20Sopenharmony_ci/** 5518c2ecf20Sopenharmony_ci * omap3_noncore_dpll_set_rate - set rate for a DPLL clock 5528c2ecf20Sopenharmony_ci * @hw: pointer to the clock to set parent for 5538c2ecf20Sopenharmony_ci * @rate: target rate for the clock 5548c2ecf20Sopenharmony_ci * @parent_rate: rate of the parent clock 5558c2ecf20Sopenharmony_ci * 5568c2ecf20Sopenharmony_ci * Sets rate for a DPLL clock. First checks if the clock parent is 5578c2ecf20Sopenharmony_ci * reference clock (in bypass mode, the rate of the clock can't be 5588c2ecf20Sopenharmony_ci * changed) and proceeds with the rate change operation. Returns 0 5598c2ecf20Sopenharmony_ci * with success, negative error value otherwise. 5608c2ecf20Sopenharmony_ci */ 5618c2ecf20Sopenharmony_ciint omap3_noncore_dpll_set_rate(struct clk_hw *hw, unsigned long rate, 5628c2ecf20Sopenharmony_ci unsigned long parent_rate) 5638c2ecf20Sopenharmony_ci{ 5648c2ecf20Sopenharmony_ci struct clk_hw_omap *clk = to_clk_hw_omap(hw); 5658c2ecf20Sopenharmony_ci struct dpll_data *dd; 5668c2ecf20Sopenharmony_ci u16 freqsel = 0; 5678c2ecf20Sopenharmony_ci int ret; 5688c2ecf20Sopenharmony_ci 5698c2ecf20Sopenharmony_ci if (!hw || !rate) 5708c2ecf20Sopenharmony_ci return -EINVAL; 5718c2ecf20Sopenharmony_ci 5728c2ecf20Sopenharmony_ci dd = clk->dpll_data; 5738c2ecf20Sopenharmony_ci if (!dd) 5748c2ecf20Sopenharmony_ci return -EINVAL; 5758c2ecf20Sopenharmony_ci 5768c2ecf20Sopenharmony_ci if (clk_hw_get_parent(hw) != dd->clk_ref) 5778c2ecf20Sopenharmony_ci return -EINVAL; 5788c2ecf20Sopenharmony_ci 5798c2ecf20Sopenharmony_ci if (dd->last_rounded_rate == 0) 5808c2ecf20Sopenharmony_ci return -EINVAL; 5818c2ecf20Sopenharmony_ci 5828c2ecf20Sopenharmony_ci /* Freqsel is available only on OMAP343X devices */ 5838c2ecf20Sopenharmony_ci if (ti_clk_get_features()->flags & TI_CLK_DPLL_HAS_FREQSEL) { 5848c2ecf20Sopenharmony_ci freqsel = _omap3_dpll_compute_freqsel(clk, dd->last_rounded_n); 5858c2ecf20Sopenharmony_ci WARN_ON(!freqsel); 5868c2ecf20Sopenharmony_ci } 5878c2ecf20Sopenharmony_ci 5888c2ecf20Sopenharmony_ci pr_debug("%s: %s: set rate: locking rate to %lu.\n", __func__, 5898c2ecf20Sopenharmony_ci clk_hw_get_name(hw), rate); 5908c2ecf20Sopenharmony_ci 5918c2ecf20Sopenharmony_ci ret = omap3_noncore_dpll_program(clk, freqsel); 5928c2ecf20Sopenharmony_ci 5938c2ecf20Sopenharmony_ci return ret; 5948c2ecf20Sopenharmony_ci} 5958c2ecf20Sopenharmony_ci 5968c2ecf20Sopenharmony_ci/** 5978c2ecf20Sopenharmony_ci * omap3_noncore_dpll_set_rate_and_parent - set rate and parent for a DPLL clock 5988c2ecf20Sopenharmony_ci * @hw: pointer to the clock to set rate and parent for 5998c2ecf20Sopenharmony_ci * @rate: target rate for the DPLL 6008c2ecf20Sopenharmony_ci * @parent_rate: clock rate of the DPLL parent 6018c2ecf20Sopenharmony_ci * @index: new parent index for the DPLL, 0 - reference, 1 - bypass 6028c2ecf20Sopenharmony_ci * 6038c2ecf20Sopenharmony_ci * Sets rate and parent for a DPLL clock. If new parent is the bypass 6048c2ecf20Sopenharmony_ci * clock, only selects the parent. Otherwise proceeds with a rate 6058c2ecf20Sopenharmony_ci * change, as this will effectively also change the parent as the 6068c2ecf20Sopenharmony_ci * DPLL is put into locked mode. Returns 0 with success, negative error 6078c2ecf20Sopenharmony_ci * value otherwise. 6088c2ecf20Sopenharmony_ci */ 6098c2ecf20Sopenharmony_ciint omap3_noncore_dpll_set_rate_and_parent(struct clk_hw *hw, 6108c2ecf20Sopenharmony_ci unsigned long rate, 6118c2ecf20Sopenharmony_ci unsigned long parent_rate, 6128c2ecf20Sopenharmony_ci u8 index) 6138c2ecf20Sopenharmony_ci{ 6148c2ecf20Sopenharmony_ci int ret; 6158c2ecf20Sopenharmony_ci 6168c2ecf20Sopenharmony_ci if (!hw || !rate) 6178c2ecf20Sopenharmony_ci return -EINVAL; 6188c2ecf20Sopenharmony_ci 6198c2ecf20Sopenharmony_ci /* 6208c2ecf20Sopenharmony_ci * clk-ref at index[0], in which case we only need to set rate, 6218c2ecf20Sopenharmony_ci * the parent will be changed automatically with the lock sequence. 6228c2ecf20Sopenharmony_ci * With clk-bypass case we only need to change parent. 6238c2ecf20Sopenharmony_ci */ 6248c2ecf20Sopenharmony_ci if (index) 6258c2ecf20Sopenharmony_ci ret = omap3_noncore_dpll_set_parent(hw, index); 6268c2ecf20Sopenharmony_ci else 6278c2ecf20Sopenharmony_ci ret = omap3_noncore_dpll_set_rate(hw, rate, parent_rate); 6288c2ecf20Sopenharmony_ci 6298c2ecf20Sopenharmony_ci return ret; 6308c2ecf20Sopenharmony_ci} 6318c2ecf20Sopenharmony_ci 6328c2ecf20Sopenharmony_ci/* DPLL autoidle read/set code */ 6338c2ecf20Sopenharmony_ci 6348c2ecf20Sopenharmony_ci/** 6358c2ecf20Sopenharmony_ci * omap3_dpll_autoidle_read - read a DPLL's autoidle bits 6368c2ecf20Sopenharmony_ci * @clk: struct clk * of the DPLL to read 6378c2ecf20Sopenharmony_ci * 6388c2ecf20Sopenharmony_ci * Return the DPLL's autoidle bits, shifted down to bit 0. Returns 6398c2ecf20Sopenharmony_ci * -EINVAL if passed a null pointer or if the struct clk does not 6408c2ecf20Sopenharmony_ci * appear to refer to a DPLL. 6418c2ecf20Sopenharmony_ci */ 6428c2ecf20Sopenharmony_cistatic u32 omap3_dpll_autoidle_read(struct clk_hw_omap *clk) 6438c2ecf20Sopenharmony_ci{ 6448c2ecf20Sopenharmony_ci const struct dpll_data *dd; 6458c2ecf20Sopenharmony_ci u32 v; 6468c2ecf20Sopenharmony_ci 6478c2ecf20Sopenharmony_ci if (!clk || !clk->dpll_data) 6488c2ecf20Sopenharmony_ci return -EINVAL; 6498c2ecf20Sopenharmony_ci 6508c2ecf20Sopenharmony_ci dd = clk->dpll_data; 6518c2ecf20Sopenharmony_ci 6528c2ecf20Sopenharmony_ci if (!dd->autoidle_mask) 6538c2ecf20Sopenharmony_ci return -EINVAL; 6548c2ecf20Sopenharmony_ci 6558c2ecf20Sopenharmony_ci v = ti_clk_ll_ops->clk_readl(&dd->autoidle_reg); 6568c2ecf20Sopenharmony_ci v &= dd->autoidle_mask; 6578c2ecf20Sopenharmony_ci v >>= __ffs(dd->autoidle_mask); 6588c2ecf20Sopenharmony_ci 6598c2ecf20Sopenharmony_ci return v; 6608c2ecf20Sopenharmony_ci} 6618c2ecf20Sopenharmony_ci 6628c2ecf20Sopenharmony_ci/** 6638c2ecf20Sopenharmony_ci * omap3_dpll_allow_idle - enable DPLL autoidle bits 6648c2ecf20Sopenharmony_ci * @clk: struct clk * of the DPLL to operate on 6658c2ecf20Sopenharmony_ci * 6668c2ecf20Sopenharmony_ci * Enable DPLL automatic idle control. This automatic idle mode 6678c2ecf20Sopenharmony_ci * switching takes effect only when the DPLL is locked, at least on 6688c2ecf20Sopenharmony_ci * OMAP3430. The DPLL will enter low-power stop when its downstream 6698c2ecf20Sopenharmony_ci * clocks are gated. No return value. 6708c2ecf20Sopenharmony_ci */ 6718c2ecf20Sopenharmony_cistatic void omap3_dpll_allow_idle(struct clk_hw_omap *clk) 6728c2ecf20Sopenharmony_ci{ 6738c2ecf20Sopenharmony_ci const struct dpll_data *dd; 6748c2ecf20Sopenharmony_ci u32 v; 6758c2ecf20Sopenharmony_ci 6768c2ecf20Sopenharmony_ci if (!clk || !clk->dpll_data) 6778c2ecf20Sopenharmony_ci return; 6788c2ecf20Sopenharmony_ci 6798c2ecf20Sopenharmony_ci dd = clk->dpll_data; 6808c2ecf20Sopenharmony_ci 6818c2ecf20Sopenharmony_ci if (!dd->autoidle_mask) 6828c2ecf20Sopenharmony_ci return; 6838c2ecf20Sopenharmony_ci 6848c2ecf20Sopenharmony_ci /* 6858c2ecf20Sopenharmony_ci * REVISIT: CORE DPLL can optionally enter low-power bypass 6868c2ecf20Sopenharmony_ci * by writing 0x5 instead of 0x1. Add some mechanism to 6878c2ecf20Sopenharmony_ci * optionally enter this mode. 6888c2ecf20Sopenharmony_ci */ 6898c2ecf20Sopenharmony_ci v = ti_clk_ll_ops->clk_readl(&dd->autoidle_reg); 6908c2ecf20Sopenharmony_ci v &= ~dd->autoidle_mask; 6918c2ecf20Sopenharmony_ci v |= DPLL_AUTOIDLE_LOW_POWER_STOP << __ffs(dd->autoidle_mask); 6928c2ecf20Sopenharmony_ci ti_clk_ll_ops->clk_writel(v, &dd->autoidle_reg); 6938c2ecf20Sopenharmony_ci} 6948c2ecf20Sopenharmony_ci 6958c2ecf20Sopenharmony_ci/** 6968c2ecf20Sopenharmony_ci * omap3_dpll_deny_idle - prevent DPLL from automatically idling 6978c2ecf20Sopenharmony_ci * @clk: struct clk * of the DPLL to operate on 6988c2ecf20Sopenharmony_ci * 6998c2ecf20Sopenharmony_ci * Disable DPLL automatic idle control. No return value. 7008c2ecf20Sopenharmony_ci */ 7018c2ecf20Sopenharmony_cistatic void omap3_dpll_deny_idle(struct clk_hw_omap *clk) 7028c2ecf20Sopenharmony_ci{ 7038c2ecf20Sopenharmony_ci const struct dpll_data *dd; 7048c2ecf20Sopenharmony_ci u32 v; 7058c2ecf20Sopenharmony_ci 7068c2ecf20Sopenharmony_ci if (!clk || !clk->dpll_data) 7078c2ecf20Sopenharmony_ci return; 7088c2ecf20Sopenharmony_ci 7098c2ecf20Sopenharmony_ci dd = clk->dpll_data; 7108c2ecf20Sopenharmony_ci 7118c2ecf20Sopenharmony_ci if (!dd->autoidle_mask) 7128c2ecf20Sopenharmony_ci return; 7138c2ecf20Sopenharmony_ci 7148c2ecf20Sopenharmony_ci v = ti_clk_ll_ops->clk_readl(&dd->autoidle_reg); 7158c2ecf20Sopenharmony_ci v &= ~dd->autoidle_mask; 7168c2ecf20Sopenharmony_ci v |= DPLL_AUTOIDLE_DISABLE << __ffs(dd->autoidle_mask); 7178c2ecf20Sopenharmony_ci ti_clk_ll_ops->clk_writel(v, &dd->autoidle_reg); 7188c2ecf20Sopenharmony_ci} 7198c2ecf20Sopenharmony_ci 7208c2ecf20Sopenharmony_ci/* Clock control for DPLL outputs */ 7218c2ecf20Sopenharmony_ci 7228c2ecf20Sopenharmony_ci/* Find the parent DPLL for the given clkoutx2 clock */ 7238c2ecf20Sopenharmony_cistatic struct clk_hw_omap *omap3_find_clkoutx2_dpll(struct clk_hw *hw) 7248c2ecf20Sopenharmony_ci{ 7258c2ecf20Sopenharmony_ci struct clk_hw_omap *pclk = NULL; 7268c2ecf20Sopenharmony_ci 7278c2ecf20Sopenharmony_ci /* Walk up the parents of clk, looking for a DPLL */ 7288c2ecf20Sopenharmony_ci do { 7298c2ecf20Sopenharmony_ci do { 7308c2ecf20Sopenharmony_ci hw = clk_hw_get_parent(hw); 7318c2ecf20Sopenharmony_ci } while (hw && (!omap2_clk_is_hw_omap(hw))); 7328c2ecf20Sopenharmony_ci if (!hw) 7338c2ecf20Sopenharmony_ci break; 7348c2ecf20Sopenharmony_ci pclk = to_clk_hw_omap(hw); 7358c2ecf20Sopenharmony_ci } while (pclk && !pclk->dpll_data); 7368c2ecf20Sopenharmony_ci 7378c2ecf20Sopenharmony_ci /* clk does not have a DPLL as a parent? error in the clock data */ 7388c2ecf20Sopenharmony_ci if (!pclk) { 7398c2ecf20Sopenharmony_ci WARN_ON(1); 7408c2ecf20Sopenharmony_ci return NULL; 7418c2ecf20Sopenharmony_ci } 7428c2ecf20Sopenharmony_ci 7438c2ecf20Sopenharmony_ci return pclk; 7448c2ecf20Sopenharmony_ci} 7458c2ecf20Sopenharmony_ci 7468c2ecf20Sopenharmony_ci/** 7478c2ecf20Sopenharmony_ci * omap3_clkoutx2_recalc - recalculate DPLL X2 output virtual clock rate 7488c2ecf20Sopenharmony_ci * @clk: DPLL output struct clk 7498c2ecf20Sopenharmony_ci * 7508c2ecf20Sopenharmony_ci * Using parent clock DPLL data, look up DPLL state. If locked, set our 7518c2ecf20Sopenharmony_ci * rate to the dpll_clk * 2; otherwise, just use dpll_clk. 7528c2ecf20Sopenharmony_ci */ 7538c2ecf20Sopenharmony_ciunsigned long omap3_clkoutx2_recalc(struct clk_hw *hw, 7548c2ecf20Sopenharmony_ci unsigned long parent_rate) 7558c2ecf20Sopenharmony_ci{ 7568c2ecf20Sopenharmony_ci const struct dpll_data *dd; 7578c2ecf20Sopenharmony_ci unsigned long rate; 7588c2ecf20Sopenharmony_ci u32 v; 7598c2ecf20Sopenharmony_ci struct clk_hw_omap *pclk = NULL; 7608c2ecf20Sopenharmony_ci 7618c2ecf20Sopenharmony_ci if (!parent_rate) 7628c2ecf20Sopenharmony_ci return 0; 7638c2ecf20Sopenharmony_ci 7648c2ecf20Sopenharmony_ci pclk = omap3_find_clkoutx2_dpll(hw); 7658c2ecf20Sopenharmony_ci 7668c2ecf20Sopenharmony_ci if (!pclk) 7678c2ecf20Sopenharmony_ci return 0; 7688c2ecf20Sopenharmony_ci 7698c2ecf20Sopenharmony_ci dd = pclk->dpll_data; 7708c2ecf20Sopenharmony_ci 7718c2ecf20Sopenharmony_ci WARN_ON(!dd->enable_mask); 7728c2ecf20Sopenharmony_ci 7738c2ecf20Sopenharmony_ci v = ti_clk_ll_ops->clk_readl(&dd->control_reg) & dd->enable_mask; 7748c2ecf20Sopenharmony_ci v >>= __ffs(dd->enable_mask); 7758c2ecf20Sopenharmony_ci if ((v != OMAP3XXX_EN_DPLL_LOCKED) || (dd->flags & DPLL_J_TYPE)) 7768c2ecf20Sopenharmony_ci rate = parent_rate; 7778c2ecf20Sopenharmony_ci else 7788c2ecf20Sopenharmony_ci rate = parent_rate * 2; 7798c2ecf20Sopenharmony_ci return rate; 7808c2ecf20Sopenharmony_ci} 7818c2ecf20Sopenharmony_ci 7828c2ecf20Sopenharmony_ci/** 7838c2ecf20Sopenharmony_ci * omap3_core_dpll_save_context - Save the m and n values of the divider 7848c2ecf20Sopenharmony_ci * @hw: pointer struct clk_hw 7858c2ecf20Sopenharmony_ci * 7868c2ecf20Sopenharmony_ci * Before the dpll registers are lost save the last rounded rate m and n 7878c2ecf20Sopenharmony_ci * and the enable mask. 7888c2ecf20Sopenharmony_ci */ 7898c2ecf20Sopenharmony_ciint omap3_core_dpll_save_context(struct clk_hw *hw) 7908c2ecf20Sopenharmony_ci{ 7918c2ecf20Sopenharmony_ci struct clk_hw_omap *clk = to_clk_hw_omap(hw); 7928c2ecf20Sopenharmony_ci struct dpll_data *dd; 7938c2ecf20Sopenharmony_ci u32 v; 7948c2ecf20Sopenharmony_ci 7958c2ecf20Sopenharmony_ci dd = clk->dpll_data; 7968c2ecf20Sopenharmony_ci 7978c2ecf20Sopenharmony_ci v = ti_clk_ll_ops->clk_readl(&dd->control_reg); 7988c2ecf20Sopenharmony_ci clk->context = (v & dd->enable_mask) >> __ffs(dd->enable_mask); 7998c2ecf20Sopenharmony_ci 8008c2ecf20Sopenharmony_ci if (clk->context == DPLL_LOCKED) { 8018c2ecf20Sopenharmony_ci v = ti_clk_ll_ops->clk_readl(&dd->mult_div1_reg); 8028c2ecf20Sopenharmony_ci dd->last_rounded_m = (v & dd->mult_mask) >> 8038c2ecf20Sopenharmony_ci __ffs(dd->mult_mask); 8048c2ecf20Sopenharmony_ci dd->last_rounded_n = ((v & dd->div1_mask) >> 8058c2ecf20Sopenharmony_ci __ffs(dd->div1_mask)) + 1; 8068c2ecf20Sopenharmony_ci } 8078c2ecf20Sopenharmony_ci 8088c2ecf20Sopenharmony_ci return 0; 8098c2ecf20Sopenharmony_ci} 8108c2ecf20Sopenharmony_ci 8118c2ecf20Sopenharmony_ci/** 8128c2ecf20Sopenharmony_ci * omap3_core_dpll_restore_context - restore the m and n values of the divider 8138c2ecf20Sopenharmony_ci * @hw: pointer struct clk_hw 8148c2ecf20Sopenharmony_ci * 8158c2ecf20Sopenharmony_ci * Restore the last rounded rate m and n 8168c2ecf20Sopenharmony_ci * and the enable mask. 8178c2ecf20Sopenharmony_ci */ 8188c2ecf20Sopenharmony_civoid omap3_core_dpll_restore_context(struct clk_hw *hw) 8198c2ecf20Sopenharmony_ci{ 8208c2ecf20Sopenharmony_ci struct clk_hw_omap *clk = to_clk_hw_omap(hw); 8218c2ecf20Sopenharmony_ci const struct dpll_data *dd; 8228c2ecf20Sopenharmony_ci u32 v; 8238c2ecf20Sopenharmony_ci 8248c2ecf20Sopenharmony_ci dd = clk->dpll_data; 8258c2ecf20Sopenharmony_ci 8268c2ecf20Sopenharmony_ci if (clk->context == DPLL_LOCKED) { 8278c2ecf20Sopenharmony_ci _omap3_dpll_write_clken(clk, 0x4); 8288c2ecf20Sopenharmony_ci _omap3_wait_dpll_status(clk, 0); 8298c2ecf20Sopenharmony_ci 8308c2ecf20Sopenharmony_ci v = ti_clk_ll_ops->clk_readl(&dd->mult_div1_reg); 8318c2ecf20Sopenharmony_ci v &= ~(dd->mult_mask | dd->div1_mask); 8328c2ecf20Sopenharmony_ci v |= dd->last_rounded_m << __ffs(dd->mult_mask); 8338c2ecf20Sopenharmony_ci v |= (dd->last_rounded_n - 1) << __ffs(dd->div1_mask); 8348c2ecf20Sopenharmony_ci ti_clk_ll_ops->clk_writel(v, &dd->mult_div1_reg); 8358c2ecf20Sopenharmony_ci 8368c2ecf20Sopenharmony_ci _omap3_dpll_write_clken(clk, DPLL_LOCKED); 8378c2ecf20Sopenharmony_ci _omap3_wait_dpll_status(clk, 1); 8388c2ecf20Sopenharmony_ci } else { 8398c2ecf20Sopenharmony_ci _omap3_dpll_write_clken(clk, clk->context); 8408c2ecf20Sopenharmony_ci } 8418c2ecf20Sopenharmony_ci} 8428c2ecf20Sopenharmony_ci 8438c2ecf20Sopenharmony_ci/** 8448c2ecf20Sopenharmony_ci * omap3_non_core_dpll_save_context - Save the m and n values of the divider 8458c2ecf20Sopenharmony_ci * @hw: pointer struct clk_hw 8468c2ecf20Sopenharmony_ci * 8478c2ecf20Sopenharmony_ci * Before the dpll registers are lost save the last rounded rate m and n 8488c2ecf20Sopenharmony_ci * and the enable mask. 8498c2ecf20Sopenharmony_ci */ 8508c2ecf20Sopenharmony_ciint omap3_noncore_dpll_save_context(struct clk_hw *hw) 8518c2ecf20Sopenharmony_ci{ 8528c2ecf20Sopenharmony_ci struct clk_hw_omap *clk = to_clk_hw_omap(hw); 8538c2ecf20Sopenharmony_ci struct dpll_data *dd; 8548c2ecf20Sopenharmony_ci u32 v; 8558c2ecf20Sopenharmony_ci 8568c2ecf20Sopenharmony_ci dd = clk->dpll_data; 8578c2ecf20Sopenharmony_ci 8588c2ecf20Sopenharmony_ci v = ti_clk_ll_ops->clk_readl(&dd->control_reg); 8598c2ecf20Sopenharmony_ci clk->context = (v & dd->enable_mask) >> __ffs(dd->enable_mask); 8608c2ecf20Sopenharmony_ci 8618c2ecf20Sopenharmony_ci if (clk->context == DPLL_LOCKED) { 8628c2ecf20Sopenharmony_ci v = ti_clk_ll_ops->clk_readl(&dd->mult_div1_reg); 8638c2ecf20Sopenharmony_ci dd->last_rounded_m = (v & dd->mult_mask) >> 8648c2ecf20Sopenharmony_ci __ffs(dd->mult_mask); 8658c2ecf20Sopenharmony_ci dd->last_rounded_n = ((v & dd->div1_mask) >> 8668c2ecf20Sopenharmony_ci __ffs(dd->div1_mask)) + 1; 8678c2ecf20Sopenharmony_ci } 8688c2ecf20Sopenharmony_ci 8698c2ecf20Sopenharmony_ci return 0; 8708c2ecf20Sopenharmony_ci} 8718c2ecf20Sopenharmony_ci 8728c2ecf20Sopenharmony_ci/** 8738c2ecf20Sopenharmony_ci * omap3_core_dpll_restore_context - restore the m and n values of the divider 8748c2ecf20Sopenharmony_ci * @hw: pointer struct clk_hw 8758c2ecf20Sopenharmony_ci * 8768c2ecf20Sopenharmony_ci * Restore the last rounded rate m and n 8778c2ecf20Sopenharmony_ci * and the enable mask. 8788c2ecf20Sopenharmony_ci */ 8798c2ecf20Sopenharmony_civoid omap3_noncore_dpll_restore_context(struct clk_hw *hw) 8808c2ecf20Sopenharmony_ci{ 8818c2ecf20Sopenharmony_ci struct clk_hw_omap *clk = to_clk_hw_omap(hw); 8828c2ecf20Sopenharmony_ci const struct dpll_data *dd; 8838c2ecf20Sopenharmony_ci u32 ctrl, mult_div1; 8848c2ecf20Sopenharmony_ci 8858c2ecf20Sopenharmony_ci dd = clk->dpll_data; 8868c2ecf20Sopenharmony_ci 8878c2ecf20Sopenharmony_ci ctrl = ti_clk_ll_ops->clk_readl(&dd->control_reg); 8888c2ecf20Sopenharmony_ci mult_div1 = ti_clk_ll_ops->clk_readl(&dd->mult_div1_reg); 8898c2ecf20Sopenharmony_ci 8908c2ecf20Sopenharmony_ci if (clk->context == ((ctrl & dd->enable_mask) >> 8918c2ecf20Sopenharmony_ci __ffs(dd->enable_mask)) && 8928c2ecf20Sopenharmony_ci dd->last_rounded_m == ((mult_div1 & dd->mult_mask) >> 8938c2ecf20Sopenharmony_ci __ffs(dd->mult_mask)) && 8948c2ecf20Sopenharmony_ci dd->last_rounded_n == ((mult_div1 & dd->div1_mask) >> 8958c2ecf20Sopenharmony_ci __ffs(dd->div1_mask)) + 1) { 8968c2ecf20Sopenharmony_ci /* nothing to be done */ 8978c2ecf20Sopenharmony_ci return; 8988c2ecf20Sopenharmony_ci } 8998c2ecf20Sopenharmony_ci 9008c2ecf20Sopenharmony_ci if (clk->context == DPLL_LOCKED) 9018c2ecf20Sopenharmony_ci omap3_noncore_dpll_program(clk, 0); 9028c2ecf20Sopenharmony_ci else 9038c2ecf20Sopenharmony_ci _omap3_dpll_write_clken(clk, clk->context); 9048c2ecf20Sopenharmony_ci} 9058c2ecf20Sopenharmony_ci 9068c2ecf20Sopenharmony_ci/* OMAP3/4 non-CORE DPLL clkops */ 9078c2ecf20Sopenharmony_ciconst struct clk_hw_omap_ops clkhwops_omap3_dpll = { 9088c2ecf20Sopenharmony_ci .allow_idle = omap3_dpll_allow_idle, 9098c2ecf20Sopenharmony_ci .deny_idle = omap3_dpll_deny_idle, 9108c2ecf20Sopenharmony_ci}; 9118c2ecf20Sopenharmony_ci 9128c2ecf20Sopenharmony_ci/** 9138c2ecf20Sopenharmony_ci * omap3_dpll4_set_rate - set rate for omap3 per-dpll 9148c2ecf20Sopenharmony_ci * @hw: clock to change 9158c2ecf20Sopenharmony_ci * @rate: target rate for clock 9168c2ecf20Sopenharmony_ci * @parent_rate: rate of the parent clock 9178c2ecf20Sopenharmony_ci * 9188c2ecf20Sopenharmony_ci * Check if the current SoC supports the per-dpll reprogram operation 9198c2ecf20Sopenharmony_ci * or not, and then do the rate change if supported. Returns -EINVAL 9208c2ecf20Sopenharmony_ci * if not supported, 0 for success, and potential error codes from the 9218c2ecf20Sopenharmony_ci * clock rate change. 9228c2ecf20Sopenharmony_ci */ 9238c2ecf20Sopenharmony_ciint omap3_dpll4_set_rate(struct clk_hw *hw, unsigned long rate, 9248c2ecf20Sopenharmony_ci unsigned long parent_rate) 9258c2ecf20Sopenharmony_ci{ 9268c2ecf20Sopenharmony_ci /* 9278c2ecf20Sopenharmony_ci * According to the 12-5 CDP code from TI, "Limitation 2.5" 9288c2ecf20Sopenharmony_ci * on 3430ES1 prevents us from changing DPLL multipliers or dividers 9298c2ecf20Sopenharmony_ci * on DPLL4. 9308c2ecf20Sopenharmony_ci */ 9318c2ecf20Sopenharmony_ci if (ti_clk_get_features()->flags & TI_CLK_DPLL4_DENY_REPROGRAM) { 9328c2ecf20Sopenharmony_ci pr_err("clock: DPLL4 cannot change rate due to silicon 'Limitation 2.5' on 3430ES1.\n"); 9338c2ecf20Sopenharmony_ci return -EINVAL; 9348c2ecf20Sopenharmony_ci } 9358c2ecf20Sopenharmony_ci 9368c2ecf20Sopenharmony_ci return omap3_noncore_dpll_set_rate(hw, rate, parent_rate); 9378c2ecf20Sopenharmony_ci} 9388c2ecf20Sopenharmony_ci 9398c2ecf20Sopenharmony_ci/** 9408c2ecf20Sopenharmony_ci * omap3_dpll4_set_rate_and_parent - set rate and parent for omap3 per-dpll 9418c2ecf20Sopenharmony_ci * @hw: clock to change 9428c2ecf20Sopenharmony_ci * @rate: target rate for clock 9438c2ecf20Sopenharmony_ci * @parent_rate: rate of the parent clock 9448c2ecf20Sopenharmony_ci * @index: parent index, 0 - reference clock, 1 - bypass clock 9458c2ecf20Sopenharmony_ci * 9468c2ecf20Sopenharmony_ci * Check if the current SoC support the per-dpll reprogram operation 9478c2ecf20Sopenharmony_ci * or not, and then do the rate + parent change if supported. Returns 9488c2ecf20Sopenharmony_ci * -EINVAL if not supported, 0 for success, and potential error codes 9498c2ecf20Sopenharmony_ci * from the clock rate change. 9508c2ecf20Sopenharmony_ci */ 9518c2ecf20Sopenharmony_ciint omap3_dpll4_set_rate_and_parent(struct clk_hw *hw, unsigned long rate, 9528c2ecf20Sopenharmony_ci unsigned long parent_rate, u8 index) 9538c2ecf20Sopenharmony_ci{ 9548c2ecf20Sopenharmony_ci if (ti_clk_get_features()->flags & TI_CLK_DPLL4_DENY_REPROGRAM) { 9558c2ecf20Sopenharmony_ci pr_err("clock: DPLL4 cannot change rate due to silicon 'Limitation 2.5' on 3430ES1.\n"); 9568c2ecf20Sopenharmony_ci return -EINVAL; 9578c2ecf20Sopenharmony_ci } 9588c2ecf20Sopenharmony_ci 9598c2ecf20Sopenharmony_ci return omap3_noncore_dpll_set_rate_and_parent(hw, rate, parent_rate, 9608c2ecf20Sopenharmony_ci index); 9618c2ecf20Sopenharmony_ci} 9628c2ecf20Sopenharmony_ci 9638c2ecf20Sopenharmony_ci/* Apply DM3730 errata sprz319 advisory 2.1. */ 9648c2ecf20Sopenharmony_cistatic bool omap3_dpll5_apply_errata(struct clk_hw *hw, 9658c2ecf20Sopenharmony_ci unsigned long parent_rate) 9668c2ecf20Sopenharmony_ci{ 9678c2ecf20Sopenharmony_ci struct omap3_dpll5_settings { 9688c2ecf20Sopenharmony_ci unsigned int rate, m, n; 9698c2ecf20Sopenharmony_ci }; 9708c2ecf20Sopenharmony_ci 9718c2ecf20Sopenharmony_ci static const struct omap3_dpll5_settings precomputed[] = { 9728c2ecf20Sopenharmony_ci /* 9738c2ecf20Sopenharmony_ci * From DM3730 errata advisory 2.1, table 35 and 36. 9748c2ecf20Sopenharmony_ci * The N value is increased by 1 compared to the tables as the 9758c2ecf20Sopenharmony_ci * errata lists register values while last_rounded_field is the 9768c2ecf20Sopenharmony_ci * real divider value. 9778c2ecf20Sopenharmony_ci */ 9788c2ecf20Sopenharmony_ci { 12000000, 80, 0 + 1 }, 9798c2ecf20Sopenharmony_ci { 13000000, 443, 5 + 1 }, 9808c2ecf20Sopenharmony_ci { 19200000, 50, 0 + 1 }, 9818c2ecf20Sopenharmony_ci { 26000000, 443, 11 + 1 }, 9828c2ecf20Sopenharmony_ci { 38400000, 25, 0 + 1 } 9838c2ecf20Sopenharmony_ci }; 9848c2ecf20Sopenharmony_ci 9858c2ecf20Sopenharmony_ci const struct omap3_dpll5_settings *d; 9868c2ecf20Sopenharmony_ci struct clk_hw_omap *clk = to_clk_hw_omap(hw); 9878c2ecf20Sopenharmony_ci struct dpll_data *dd; 9888c2ecf20Sopenharmony_ci unsigned int i; 9898c2ecf20Sopenharmony_ci 9908c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(precomputed); ++i) { 9918c2ecf20Sopenharmony_ci if (parent_rate == precomputed[i].rate) 9928c2ecf20Sopenharmony_ci break; 9938c2ecf20Sopenharmony_ci } 9948c2ecf20Sopenharmony_ci 9958c2ecf20Sopenharmony_ci if (i == ARRAY_SIZE(precomputed)) 9968c2ecf20Sopenharmony_ci return false; 9978c2ecf20Sopenharmony_ci 9988c2ecf20Sopenharmony_ci d = &precomputed[i]; 9998c2ecf20Sopenharmony_ci 10008c2ecf20Sopenharmony_ci /* Update the M, N and rounded rate values and program the DPLL. */ 10018c2ecf20Sopenharmony_ci dd = clk->dpll_data; 10028c2ecf20Sopenharmony_ci dd->last_rounded_m = d->m; 10038c2ecf20Sopenharmony_ci dd->last_rounded_n = d->n; 10048c2ecf20Sopenharmony_ci dd->last_rounded_rate = div_u64((u64)parent_rate * d->m, d->n); 10058c2ecf20Sopenharmony_ci omap3_noncore_dpll_program(clk, 0); 10068c2ecf20Sopenharmony_ci 10078c2ecf20Sopenharmony_ci return true; 10088c2ecf20Sopenharmony_ci} 10098c2ecf20Sopenharmony_ci 10108c2ecf20Sopenharmony_ci/** 10118c2ecf20Sopenharmony_ci * omap3_dpll5_set_rate - set rate for omap3 dpll5 10128c2ecf20Sopenharmony_ci * @hw: clock to change 10138c2ecf20Sopenharmony_ci * @rate: target rate for clock 10148c2ecf20Sopenharmony_ci * @parent_rate: rate of the parent clock 10158c2ecf20Sopenharmony_ci * 10168c2ecf20Sopenharmony_ci * Set rate for the DPLL5 clock. Apply the sprz319 advisory 2.1 on OMAP36xx if 10178c2ecf20Sopenharmony_ci * the DPLL is used for USB host (detected through the requested rate). 10188c2ecf20Sopenharmony_ci */ 10198c2ecf20Sopenharmony_ciint omap3_dpll5_set_rate(struct clk_hw *hw, unsigned long rate, 10208c2ecf20Sopenharmony_ci unsigned long parent_rate) 10218c2ecf20Sopenharmony_ci{ 10228c2ecf20Sopenharmony_ci if (rate == OMAP3_DPLL5_FREQ_FOR_USBHOST * 8) { 10238c2ecf20Sopenharmony_ci if (omap3_dpll5_apply_errata(hw, parent_rate)) 10248c2ecf20Sopenharmony_ci return 0; 10258c2ecf20Sopenharmony_ci } 10268c2ecf20Sopenharmony_ci 10278c2ecf20Sopenharmony_ci return omap3_noncore_dpll_set_rate(hw, rate, parent_rate); 10288c2ecf20Sopenharmony_ci} 1029