18c2ecf20Sopenharmony_ci/* 28c2ecf20Sopenharmony_ci * Copyright (C) 2014 Broadcom Corporation 38c2ecf20Sopenharmony_ci * 48c2ecf20Sopenharmony_ci * This program is free software; you can redistribute it and/or 58c2ecf20Sopenharmony_ci * modify it under the terms of the GNU General Public License as 68c2ecf20Sopenharmony_ci * published by the Free Software Foundation version 2. 78c2ecf20Sopenharmony_ci * 88c2ecf20Sopenharmony_ci * This program is distributed "as is" WITHOUT ANY WARRANTY of any 98c2ecf20Sopenharmony_ci * kind, whether express or implied; without even the implied warranty 108c2ecf20Sopenharmony_ci * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 118c2ecf20Sopenharmony_ci * GNU General Public License for more details. 128c2ecf20Sopenharmony_ci */ 138c2ecf20Sopenharmony_ci 148c2ecf20Sopenharmony_ci#include <linux/kernel.h> 158c2ecf20Sopenharmony_ci#include <linux/err.h> 168c2ecf20Sopenharmony_ci#include <linux/clk-provider.h> 178c2ecf20Sopenharmony_ci#include <linux/io.h> 188c2ecf20Sopenharmony_ci#include <linux/of.h> 198c2ecf20Sopenharmony_ci#include <linux/clkdev.h> 208c2ecf20Sopenharmony_ci#include <linux/of_address.h> 218c2ecf20Sopenharmony_ci#include <linux/delay.h> 228c2ecf20Sopenharmony_ci 238c2ecf20Sopenharmony_ci#include "clk-iproc.h" 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_ci#define PLL_VCO_HIGH_SHIFT 19 268c2ecf20Sopenharmony_ci#define PLL_VCO_LOW_SHIFT 30 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_ci/* 298c2ecf20Sopenharmony_ci * PLL MACRO_SELECT modes 0 to 5 choose pre-calculated PLL output frequencies 308c2ecf20Sopenharmony_ci * from a look-up table. Mode 7 allows user to manipulate PLL clock dividers 318c2ecf20Sopenharmony_ci */ 328c2ecf20Sopenharmony_ci#define PLL_USER_MODE 7 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_ci/* number of delay loops waiting for PLL to lock */ 358c2ecf20Sopenharmony_ci#define LOCK_DELAY 100 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_ci/* number of VCO frequency bands */ 388c2ecf20Sopenharmony_ci#define NUM_FREQ_BANDS 8 398c2ecf20Sopenharmony_ci 408c2ecf20Sopenharmony_ci#define NUM_KP_BANDS 3 418c2ecf20Sopenharmony_cienum kp_band { 428c2ecf20Sopenharmony_ci KP_BAND_MID = 0, 438c2ecf20Sopenharmony_ci KP_BAND_HIGH, 448c2ecf20Sopenharmony_ci KP_BAND_HIGH_HIGH 458c2ecf20Sopenharmony_ci}; 468c2ecf20Sopenharmony_ci 478c2ecf20Sopenharmony_cistatic const unsigned int kp_table[NUM_KP_BANDS][NUM_FREQ_BANDS] = { 488c2ecf20Sopenharmony_ci { 5, 6, 6, 7, 7, 8, 9, 10 }, 498c2ecf20Sopenharmony_ci { 4, 4, 5, 5, 6, 7, 8, 9 }, 508c2ecf20Sopenharmony_ci { 4, 5, 5, 6, 7, 8, 9, 10 }, 518c2ecf20Sopenharmony_ci}; 528c2ecf20Sopenharmony_ci 538c2ecf20Sopenharmony_cistatic const unsigned long ref_freq_table[NUM_FREQ_BANDS][2] = { 548c2ecf20Sopenharmony_ci { 10000000, 12500000 }, 558c2ecf20Sopenharmony_ci { 12500000, 15000000 }, 568c2ecf20Sopenharmony_ci { 15000000, 20000000 }, 578c2ecf20Sopenharmony_ci { 20000000, 25000000 }, 588c2ecf20Sopenharmony_ci { 25000000, 50000000 }, 598c2ecf20Sopenharmony_ci { 50000000, 75000000 }, 608c2ecf20Sopenharmony_ci { 75000000, 100000000 }, 618c2ecf20Sopenharmony_ci { 100000000, 125000000 }, 628c2ecf20Sopenharmony_ci}; 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_cienum vco_freq_range { 658c2ecf20Sopenharmony_ci VCO_LOW = 700000000U, 668c2ecf20Sopenharmony_ci VCO_MID = 1200000000U, 678c2ecf20Sopenharmony_ci VCO_HIGH = 2200000000U, 688c2ecf20Sopenharmony_ci VCO_HIGH_HIGH = 3100000000U, 698c2ecf20Sopenharmony_ci VCO_MAX = 4000000000U, 708c2ecf20Sopenharmony_ci}; 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_cistruct iproc_pll { 738c2ecf20Sopenharmony_ci void __iomem *status_base; 748c2ecf20Sopenharmony_ci void __iomem *control_base; 758c2ecf20Sopenharmony_ci void __iomem *pwr_base; 768c2ecf20Sopenharmony_ci void __iomem *asiu_base; 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_ci const struct iproc_pll_ctrl *ctrl; 798c2ecf20Sopenharmony_ci const struct iproc_pll_vco_param *vco_param; 808c2ecf20Sopenharmony_ci unsigned int num_vco_entries; 818c2ecf20Sopenharmony_ci}; 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_cistruct iproc_clk { 848c2ecf20Sopenharmony_ci struct clk_hw hw; 858c2ecf20Sopenharmony_ci struct iproc_pll *pll; 868c2ecf20Sopenharmony_ci const struct iproc_clk_ctrl *ctrl; 878c2ecf20Sopenharmony_ci}; 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_ci#define to_iproc_clk(hw) container_of(hw, struct iproc_clk, hw) 908c2ecf20Sopenharmony_ci 918c2ecf20Sopenharmony_cistatic int pll_calc_param(unsigned long target_rate, 928c2ecf20Sopenharmony_ci unsigned long parent_rate, 938c2ecf20Sopenharmony_ci struct iproc_pll_vco_param *vco_out) 948c2ecf20Sopenharmony_ci{ 958c2ecf20Sopenharmony_ci u64 ndiv_int, ndiv_frac, residual; 968c2ecf20Sopenharmony_ci 978c2ecf20Sopenharmony_ci ndiv_int = target_rate / parent_rate; 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_ci if (!ndiv_int || (ndiv_int > 255)) 1008c2ecf20Sopenharmony_ci return -EINVAL; 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_ci residual = target_rate - (ndiv_int * parent_rate); 1038c2ecf20Sopenharmony_ci residual <<= 20; 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_ci /* 1068c2ecf20Sopenharmony_ci * Add half of the divisor so the result will be rounded to closest 1078c2ecf20Sopenharmony_ci * instead of rounded down. 1088c2ecf20Sopenharmony_ci */ 1098c2ecf20Sopenharmony_ci residual += (parent_rate / 2); 1108c2ecf20Sopenharmony_ci ndiv_frac = div64_u64((u64)residual, (u64)parent_rate); 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_ci vco_out->ndiv_int = ndiv_int; 1138c2ecf20Sopenharmony_ci vco_out->ndiv_frac = ndiv_frac; 1148c2ecf20Sopenharmony_ci vco_out->pdiv = 1; 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_ci vco_out->rate = vco_out->ndiv_int * parent_rate; 1178c2ecf20Sopenharmony_ci residual = (u64)vco_out->ndiv_frac * (u64)parent_rate; 1188c2ecf20Sopenharmony_ci residual >>= 20; 1198c2ecf20Sopenharmony_ci vco_out->rate += residual; 1208c2ecf20Sopenharmony_ci 1218c2ecf20Sopenharmony_ci return 0; 1228c2ecf20Sopenharmony_ci} 1238c2ecf20Sopenharmony_ci 1248c2ecf20Sopenharmony_ci/* 1258c2ecf20Sopenharmony_ci * Based on the target frequency, find a match from the VCO frequency parameter 1268c2ecf20Sopenharmony_ci * table and return its index 1278c2ecf20Sopenharmony_ci */ 1288c2ecf20Sopenharmony_cistatic int pll_get_rate_index(struct iproc_pll *pll, unsigned int target_rate) 1298c2ecf20Sopenharmony_ci{ 1308c2ecf20Sopenharmony_ci int i; 1318c2ecf20Sopenharmony_ci 1328c2ecf20Sopenharmony_ci for (i = 0; i < pll->num_vco_entries; i++) 1338c2ecf20Sopenharmony_ci if (target_rate == pll->vco_param[i].rate) 1348c2ecf20Sopenharmony_ci break; 1358c2ecf20Sopenharmony_ci 1368c2ecf20Sopenharmony_ci if (i >= pll->num_vco_entries) 1378c2ecf20Sopenharmony_ci return -EINVAL; 1388c2ecf20Sopenharmony_ci 1398c2ecf20Sopenharmony_ci return i; 1408c2ecf20Sopenharmony_ci} 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_cistatic int get_kp(unsigned long ref_freq, enum kp_band kp_index) 1438c2ecf20Sopenharmony_ci{ 1448c2ecf20Sopenharmony_ci int i; 1458c2ecf20Sopenharmony_ci 1468c2ecf20Sopenharmony_ci if (ref_freq < ref_freq_table[0][0]) 1478c2ecf20Sopenharmony_ci return -EINVAL; 1488c2ecf20Sopenharmony_ci 1498c2ecf20Sopenharmony_ci for (i = 0; i < NUM_FREQ_BANDS; i++) { 1508c2ecf20Sopenharmony_ci if (ref_freq >= ref_freq_table[i][0] && 1518c2ecf20Sopenharmony_ci ref_freq < ref_freq_table[i][1]) 1528c2ecf20Sopenharmony_ci return kp_table[kp_index][i]; 1538c2ecf20Sopenharmony_ci } 1548c2ecf20Sopenharmony_ci return -EINVAL; 1558c2ecf20Sopenharmony_ci} 1568c2ecf20Sopenharmony_ci 1578c2ecf20Sopenharmony_cistatic int pll_wait_for_lock(struct iproc_pll *pll) 1588c2ecf20Sopenharmony_ci{ 1598c2ecf20Sopenharmony_ci int i; 1608c2ecf20Sopenharmony_ci const struct iproc_pll_ctrl *ctrl = pll->ctrl; 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_ci for (i = 0; i < LOCK_DELAY; i++) { 1638c2ecf20Sopenharmony_ci u32 val = readl(pll->status_base + ctrl->status.offset); 1648c2ecf20Sopenharmony_ci 1658c2ecf20Sopenharmony_ci if (val & (1 << ctrl->status.shift)) 1668c2ecf20Sopenharmony_ci return 0; 1678c2ecf20Sopenharmony_ci udelay(10); 1688c2ecf20Sopenharmony_ci } 1698c2ecf20Sopenharmony_ci 1708c2ecf20Sopenharmony_ci return -EIO; 1718c2ecf20Sopenharmony_ci} 1728c2ecf20Sopenharmony_ci 1738c2ecf20Sopenharmony_cistatic void iproc_pll_write(const struct iproc_pll *pll, void __iomem *base, 1748c2ecf20Sopenharmony_ci const u32 offset, u32 val) 1758c2ecf20Sopenharmony_ci{ 1768c2ecf20Sopenharmony_ci const struct iproc_pll_ctrl *ctrl = pll->ctrl; 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_ci writel(val, base + offset); 1798c2ecf20Sopenharmony_ci 1808c2ecf20Sopenharmony_ci if (unlikely(ctrl->flags & IPROC_CLK_NEEDS_READ_BACK && 1818c2ecf20Sopenharmony_ci (base == pll->status_base || base == pll->control_base))) 1828c2ecf20Sopenharmony_ci val = readl(base + offset); 1838c2ecf20Sopenharmony_ci} 1848c2ecf20Sopenharmony_ci 1858c2ecf20Sopenharmony_cistatic void __pll_disable(struct iproc_pll *pll) 1868c2ecf20Sopenharmony_ci{ 1878c2ecf20Sopenharmony_ci const struct iproc_pll_ctrl *ctrl = pll->ctrl; 1888c2ecf20Sopenharmony_ci u32 val; 1898c2ecf20Sopenharmony_ci 1908c2ecf20Sopenharmony_ci if (ctrl->flags & IPROC_CLK_PLL_ASIU) { 1918c2ecf20Sopenharmony_ci val = readl(pll->asiu_base + ctrl->asiu.offset); 1928c2ecf20Sopenharmony_ci val &= ~(1 << ctrl->asiu.en_shift); 1938c2ecf20Sopenharmony_ci iproc_pll_write(pll, pll->asiu_base, ctrl->asiu.offset, val); 1948c2ecf20Sopenharmony_ci } 1958c2ecf20Sopenharmony_ci 1968c2ecf20Sopenharmony_ci if (ctrl->flags & IPROC_CLK_EMBED_PWRCTRL) { 1978c2ecf20Sopenharmony_ci val = readl(pll->control_base + ctrl->aon.offset); 1988c2ecf20Sopenharmony_ci val |= bit_mask(ctrl->aon.pwr_width) << ctrl->aon.pwr_shift; 1998c2ecf20Sopenharmony_ci iproc_pll_write(pll, pll->control_base, ctrl->aon.offset, val); 2008c2ecf20Sopenharmony_ci } 2018c2ecf20Sopenharmony_ci 2028c2ecf20Sopenharmony_ci if (pll->pwr_base) { 2038c2ecf20Sopenharmony_ci /* latch input value so core power can be shut down */ 2048c2ecf20Sopenharmony_ci val = readl(pll->pwr_base + ctrl->aon.offset); 2058c2ecf20Sopenharmony_ci val |= 1 << ctrl->aon.iso_shift; 2068c2ecf20Sopenharmony_ci iproc_pll_write(pll, pll->pwr_base, ctrl->aon.offset, val); 2078c2ecf20Sopenharmony_ci 2088c2ecf20Sopenharmony_ci /* power down the core */ 2098c2ecf20Sopenharmony_ci val &= ~(bit_mask(ctrl->aon.pwr_width) << ctrl->aon.pwr_shift); 2108c2ecf20Sopenharmony_ci iproc_pll_write(pll, pll->pwr_base, ctrl->aon.offset, val); 2118c2ecf20Sopenharmony_ci } 2128c2ecf20Sopenharmony_ci} 2138c2ecf20Sopenharmony_ci 2148c2ecf20Sopenharmony_cistatic int __pll_enable(struct iproc_pll *pll) 2158c2ecf20Sopenharmony_ci{ 2168c2ecf20Sopenharmony_ci const struct iproc_pll_ctrl *ctrl = pll->ctrl; 2178c2ecf20Sopenharmony_ci u32 val; 2188c2ecf20Sopenharmony_ci 2198c2ecf20Sopenharmony_ci if (ctrl->flags & IPROC_CLK_EMBED_PWRCTRL) { 2208c2ecf20Sopenharmony_ci val = readl(pll->control_base + ctrl->aon.offset); 2218c2ecf20Sopenharmony_ci val &= ~(bit_mask(ctrl->aon.pwr_width) << ctrl->aon.pwr_shift); 2228c2ecf20Sopenharmony_ci iproc_pll_write(pll, pll->control_base, ctrl->aon.offset, val); 2238c2ecf20Sopenharmony_ci } 2248c2ecf20Sopenharmony_ci 2258c2ecf20Sopenharmony_ci if (pll->pwr_base) { 2268c2ecf20Sopenharmony_ci /* power up the PLL and make sure it's not latched */ 2278c2ecf20Sopenharmony_ci val = readl(pll->pwr_base + ctrl->aon.offset); 2288c2ecf20Sopenharmony_ci val |= bit_mask(ctrl->aon.pwr_width) << ctrl->aon.pwr_shift; 2298c2ecf20Sopenharmony_ci val &= ~(1 << ctrl->aon.iso_shift); 2308c2ecf20Sopenharmony_ci iproc_pll_write(pll, pll->pwr_base, ctrl->aon.offset, val); 2318c2ecf20Sopenharmony_ci } 2328c2ecf20Sopenharmony_ci 2338c2ecf20Sopenharmony_ci /* certain PLLs also need to be ungated from the ASIU top level */ 2348c2ecf20Sopenharmony_ci if (ctrl->flags & IPROC_CLK_PLL_ASIU) { 2358c2ecf20Sopenharmony_ci val = readl(pll->asiu_base + ctrl->asiu.offset); 2368c2ecf20Sopenharmony_ci val |= (1 << ctrl->asiu.en_shift); 2378c2ecf20Sopenharmony_ci iproc_pll_write(pll, pll->asiu_base, ctrl->asiu.offset, val); 2388c2ecf20Sopenharmony_ci } 2398c2ecf20Sopenharmony_ci 2408c2ecf20Sopenharmony_ci return 0; 2418c2ecf20Sopenharmony_ci} 2428c2ecf20Sopenharmony_ci 2438c2ecf20Sopenharmony_cistatic void __pll_put_in_reset(struct iproc_pll *pll) 2448c2ecf20Sopenharmony_ci{ 2458c2ecf20Sopenharmony_ci u32 val; 2468c2ecf20Sopenharmony_ci const struct iproc_pll_ctrl *ctrl = pll->ctrl; 2478c2ecf20Sopenharmony_ci const struct iproc_pll_reset_ctrl *reset = &ctrl->reset; 2488c2ecf20Sopenharmony_ci 2498c2ecf20Sopenharmony_ci val = readl(pll->control_base + reset->offset); 2508c2ecf20Sopenharmony_ci if (ctrl->flags & IPROC_CLK_PLL_RESET_ACTIVE_LOW) 2518c2ecf20Sopenharmony_ci val |= BIT(reset->reset_shift) | BIT(reset->p_reset_shift); 2528c2ecf20Sopenharmony_ci else 2538c2ecf20Sopenharmony_ci val &= ~(BIT(reset->reset_shift) | BIT(reset->p_reset_shift)); 2548c2ecf20Sopenharmony_ci iproc_pll_write(pll, pll->control_base, reset->offset, val); 2558c2ecf20Sopenharmony_ci} 2568c2ecf20Sopenharmony_ci 2578c2ecf20Sopenharmony_cistatic void __pll_bring_out_reset(struct iproc_pll *pll, unsigned int kp, 2588c2ecf20Sopenharmony_ci unsigned int ka, unsigned int ki) 2598c2ecf20Sopenharmony_ci{ 2608c2ecf20Sopenharmony_ci u32 val; 2618c2ecf20Sopenharmony_ci const struct iproc_pll_ctrl *ctrl = pll->ctrl; 2628c2ecf20Sopenharmony_ci const struct iproc_pll_reset_ctrl *reset = &ctrl->reset; 2638c2ecf20Sopenharmony_ci const struct iproc_pll_dig_filter_ctrl *dig_filter = &ctrl->dig_filter; 2648c2ecf20Sopenharmony_ci 2658c2ecf20Sopenharmony_ci val = readl(pll->control_base + dig_filter->offset); 2668c2ecf20Sopenharmony_ci val &= ~(bit_mask(dig_filter->ki_width) << dig_filter->ki_shift | 2678c2ecf20Sopenharmony_ci bit_mask(dig_filter->kp_width) << dig_filter->kp_shift | 2688c2ecf20Sopenharmony_ci bit_mask(dig_filter->ka_width) << dig_filter->ka_shift); 2698c2ecf20Sopenharmony_ci val |= ki << dig_filter->ki_shift | kp << dig_filter->kp_shift | 2708c2ecf20Sopenharmony_ci ka << dig_filter->ka_shift; 2718c2ecf20Sopenharmony_ci iproc_pll_write(pll, pll->control_base, dig_filter->offset, val); 2728c2ecf20Sopenharmony_ci 2738c2ecf20Sopenharmony_ci val = readl(pll->control_base + reset->offset); 2748c2ecf20Sopenharmony_ci if (ctrl->flags & IPROC_CLK_PLL_RESET_ACTIVE_LOW) 2758c2ecf20Sopenharmony_ci val &= ~(BIT(reset->reset_shift) | BIT(reset->p_reset_shift)); 2768c2ecf20Sopenharmony_ci else 2778c2ecf20Sopenharmony_ci val |= BIT(reset->reset_shift) | BIT(reset->p_reset_shift); 2788c2ecf20Sopenharmony_ci iproc_pll_write(pll, pll->control_base, reset->offset, val); 2798c2ecf20Sopenharmony_ci} 2808c2ecf20Sopenharmony_ci 2818c2ecf20Sopenharmony_ci/* 2828c2ecf20Sopenharmony_ci * Determines if the change to be applied to the PLL is minor (just an update 2838c2ecf20Sopenharmony_ci * or the fractional divider). If so, then we can avoid going through a 2848c2ecf20Sopenharmony_ci * disruptive reset and lock sequence. 2858c2ecf20Sopenharmony_ci */ 2868c2ecf20Sopenharmony_cistatic bool pll_fractional_change_only(struct iproc_pll *pll, 2878c2ecf20Sopenharmony_ci struct iproc_pll_vco_param *vco) 2888c2ecf20Sopenharmony_ci{ 2898c2ecf20Sopenharmony_ci const struct iproc_pll_ctrl *ctrl = pll->ctrl; 2908c2ecf20Sopenharmony_ci u32 val; 2918c2ecf20Sopenharmony_ci u32 ndiv_int; 2928c2ecf20Sopenharmony_ci unsigned int pdiv; 2938c2ecf20Sopenharmony_ci 2948c2ecf20Sopenharmony_ci /* PLL needs to be locked */ 2958c2ecf20Sopenharmony_ci val = readl(pll->status_base + ctrl->status.offset); 2968c2ecf20Sopenharmony_ci if ((val & (1 << ctrl->status.shift)) == 0) 2978c2ecf20Sopenharmony_ci return false; 2988c2ecf20Sopenharmony_ci 2998c2ecf20Sopenharmony_ci val = readl(pll->control_base + ctrl->ndiv_int.offset); 3008c2ecf20Sopenharmony_ci ndiv_int = (val >> ctrl->ndiv_int.shift) & 3018c2ecf20Sopenharmony_ci bit_mask(ctrl->ndiv_int.width); 3028c2ecf20Sopenharmony_ci 3038c2ecf20Sopenharmony_ci if (ndiv_int != vco->ndiv_int) 3048c2ecf20Sopenharmony_ci return false; 3058c2ecf20Sopenharmony_ci 3068c2ecf20Sopenharmony_ci val = readl(pll->control_base + ctrl->pdiv.offset); 3078c2ecf20Sopenharmony_ci pdiv = (val >> ctrl->pdiv.shift) & bit_mask(ctrl->pdiv.width); 3088c2ecf20Sopenharmony_ci 3098c2ecf20Sopenharmony_ci if (pdiv != vco->pdiv) 3108c2ecf20Sopenharmony_ci return false; 3118c2ecf20Sopenharmony_ci 3128c2ecf20Sopenharmony_ci return true; 3138c2ecf20Sopenharmony_ci} 3148c2ecf20Sopenharmony_ci 3158c2ecf20Sopenharmony_cistatic int pll_set_rate(struct iproc_clk *clk, struct iproc_pll_vco_param *vco, 3168c2ecf20Sopenharmony_ci unsigned long parent_rate) 3178c2ecf20Sopenharmony_ci{ 3188c2ecf20Sopenharmony_ci struct iproc_pll *pll = clk->pll; 3198c2ecf20Sopenharmony_ci const struct iproc_pll_ctrl *ctrl = pll->ctrl; 3208c2ecf20Sopenharmony_ci int ka = 0, ki, kp, ret; 3218c2ecf20Sopenharmony_ci unsigned long rate = vco->rate; 3228c2ecf20Sopenharmony_ci u32 val; 3238c2ecf20Sopenharmony_ci enum kp_band kp_index; 3248c2ecf20Sopenharmony_ci unsigned long ref_freq; 3258c2ecf20Sopenharmony_ci const char *clk_name = clk_hw_get_name(&clk->hw); 3268c2ecf20Sopenharmony_ci 3278c2ecf20Sopenharmony_ci /* 3288c2ecf20Sopenharmony_ci * reference frequency = parent frequency / PDIV 3298c2ecf20Sopenharmony_ci * If PDIV = 0, then it becomes a multiplier (x2) 3308c2ecf20Sopenharmony_ci */ 3318c2ecf20Sopenharmony_ci if (vco->pdiv == 0) 3328c2ecf20Sopenharmony_ci ref_freq = parent_rate * 2; 3338c2ecf20Sopenharmony_ci else 3348c2ecf20Sopenharmony_ci ref_freq = parent_rate / vco->pdiv; 3358c2ecf20Sopenharmony_ci 3368c2ecf20Sopenharmony_ci /* determine Ki and Kp index based on target VCO frequency */ 3378c2ecf20Sopenharmony_ci if (rate >= VCO_LOW && rate < VCO_HIGH) { 3388c2ecf20Sopenharmony_ci ki = 4; 3398c2ecf20Sopenharmony_ci kp_index = KP_BAND_MID; 3408c2ecf20Sopenharmony_ci } else if (rate >= VCO_HIGH && rate < VCO_HIGH_HIGH) { 3418c2ecf20Sopenharmony_ci ki = 3; 3428c2ecf20Sopenharmony_ci kp_index = KP_BAND_HIGH; 3438c2ecf20Sopenharmony_ci } else if (rate >= VCO_HIGH_HIGH && rate < VCO_MAX) { 3448c2ecf20Sopenharmony_ci ki = 3; 3458c2ecf20Sopenharmony_ci kp_index = KP_BAND_HIGH_HIGH; 3468c2ecf20Sopenharmony_ci } else { 3478c2ecf20Sopenharmony_ci pr_err("%s: pll: %s has invalid rate: %lu\n", __func__, 3488c2ecf20Sopenharmony_ci clk_name, rate); 3498c2ecf20Sopenharmony_ci return -EINVAL; 3508c2ecf20Sopenharmony_ci } 3518c2ecf20Sopenharmony_ci 3528c2ecf20Sopenharmony_ci kp = get_kp(ref_freq, kp_index); 3538c2ecf20Sopenharmony_ci if (kp < 0) { 3548c2ecf20Sopenharmony_ci pr_err("%s: pll: %s has invalid kp\n", __func__, clk_name); 3558c2ecf20Sopenharmony_ci return kp; 3568c2ecf20Sopenharmony_ci } 3578c2ecf20Sopenharmony_ci 3588c2ecf20Sopenharmony_ci ret = __pll_enable(pll); 3598c2ecf20Sopenharmony_ci if (ret) { 3608c2ecf20Sopenharmony_ci pr_err("%s: pll: %s fails to enable\n", __func__, clk_name); 3618c2ecf20Sopenharmony_ci return ret; 3628c2ecf20Sopenharmony_ci } 3638c2ecf20Sopenharmony_ci 3648c2ecf20Sopenharmony_ci if (pll_fractional_change_only(clk->pll, vco)) { 3658c2ecf20Sopenharmony_ci /* program fractional part of NDIV */ 3668c2ecf20Sopenharmony_ci if (ctrl->flags & IPROC_CLK_PLL_HAS_NDIV_FRAC) { 3678c2ecf20Sopenharmony_ci val = readl(pll->control_base + ctrl->ndiv_frac.offset); 3688c2ecf20Sopenharmony_ci val &= ~(bit_mask(ctrl->ndiv_frac.width) << 3698c2ecf20Sopenharmony_ci ctrl->ndiv_frac.shift); 3708c2ecf20Sopenharmony_ci val |= vco->ndiv_frac << ctrl->ndiv_frac.shift; 3718c2ecf20Sopenharmony_ci iproc_pll_write(pll, pll->control_base, 3728c2ecf20Sopenharmony_ci ctrl->ndiv_frac.offset, val); 3738c2ecf20Sopenharmony_ci return 0; 3748c2ecf20Sopenharmony_ci } 3758c2ecf20Sopenharmony_ci } 3768c2ecf20Sopenharmony_ci 3778c2ecf20Sopenharmony_ci /* put PLL in reset */ 3788c2ecf20Sopenharmony_ci __pll_put_in_reset(pll); 3798c2ecf20Sopenharmony_ci 3808c2ecf20Sopenharmony_ci /* set PLL in user mode before modifying PLL controls */ 3818c2ecf20Sopenharmony_ci if (ctrl->flags & IPROC_CLK_PLL_USER_MODE_ON) { 3828c2ecf20Sopenharmony_ci val = readl(pll->control_base + ctrl->macro_mode.offset); 3838c2ecf20Sopenharmony_ci val &= ~(bit_mask(ctrl->macro_mode.width) << 3848c2ecf20Sopenharmony_ci ctrl->macro_mode.shift); 3858c2ecf20Sopenharmony_ci val |= PLL_USER_MODE << ctrl->macro_mode.shift; 3868c2ecf20Sopenharmony_ci iproc_pll_write(pll, pll->control_base, 3878c2ecf20Sopenharmony_ci ctrl->macro_mode.offset, val); 3888c2ecf20Sopenharmony_ci } 3898c2ecf20Sopenharmony_ci 3908c2ecf20Sopenharmony_ci iproc_pll_write(pll, pll->control_base, ctrl->vco_ctrl.u_offset, 0); 3918c2ecf20Sopenharmony_ci 3928c2ecf20Sopenharmony_ci val = readl(pll->control_base + ctrl->vco_ctrl.l_offset); 3938c2ecf20Sopenharmony_ci 3948c2ecf20Sopenharmony_ci if (rate >= VCO_LOW && rate < VCO_MID) 3958c2ecf20Sopenharmony_ci val |= (1 << PLL_VCO_LOW_SHIFT); 3968c2ecf20Sopenharmony_ci 3978c2ecf20Sopenharmony_ci if (rate < VCO_HIGH) 3988c2ecf20Sopenharmony_ci val &= ~(1 << PLL_VCO_HIGH_SHIFT); 3998c2ecf20Sopenharmony_ci else 4008c2ecf20Sopenharmony_ci val |= (1 << PLL_VCO_HIGH_SHIFT); 4018c2ecf20Sopenharmony_ci 4028c2ecf20Sopenharmony_ci iproc_pll_write(pll, pll->control_base, ctrl->vco_ctrl.l_offset, val); 4038c2ecf20Sopenharmony_ci 4048c2ecf20Sopenharmony_ci /* program integer part of NDIV */ 4058c2ecf20Sopenharmony_ci val = readl(pll->control_base + ctrl->ndiv_int.offset); 4068c2ecf20Sopenharmony_ci val &= ~(bit_mask(ctrl->ndiv_int.width) << ctrl->ndiv_int.shift); 4078c2ecf20Sopenharmony_ci val |= vco->ndiv_int << ctrl->ndiv_int.shift; 4088c2ecf20Sopenharmony_ci iproc_pll_write(pll, pll->control_base, ctrl->ndiv_int.offset, val); 4098c2ecf20Sopenharmony_ci 4108c2ecf20Sopenharmony_ci /* program fractional part of NDIV */ 4118c2ecf20Sopenharmony_ci if (ctrl->flags & IPROC_CLK_PLL_HAS_NDIV_FRAC) { 4128c2ecf20Sopenharmony_ci val = readl(pll->control_base + ctrl->ndiv_frac.offset); 4138c2ecf20Sopenharmony_ci val &= ~(bit_mask(ctrl->ndiv_frac.width) << 4148c2ecf20Sopenharmony_ci ctrl->ndiv_frac.shift); 4158c2ecf20Sopenharmony_ci val |= vco->ndiv_frac << ctrl->ndiv_frac.shift; 4168c2ecf20Sopenharmony_ci iproc_pll_write(pll, pll->control_base, ctrl->ndiv_frac.offset, 4178c2ecf20Sopenharmony_ci val); 4188c2ecf20Sopenharmony_ci } 4198c2ecf20Sopenharmony_ci 4208c2ecf20Sopenharmony_ci /* program PDIV */ 4218c2ecf20Sopenharmony_ci val = readl(pll->control_base + ctrl->pdiv.offset); 4228c2ecf20Sopenharmony_ci val &= ~(bit_mask(ctrl->pdiv.width) << ctrl->pdiv.shift); 4238c2ecf20Sopenharmony_ci val |= vco->pdiv << ctrl->pdiv.shift; 4248c2ecf20Sopenharmony_ci iproc_pll_write(pll, pll->control_base, ctrl->pdiv.offset, val); 4258c2ecf20Sopenharmony_ci 4268c2ecf20Sopenharmony_ci __pll_bring_out_reset(pll, kp, ka, ki); 4278c2ecf20Sopenharmony_ci 4288c2ecf20Sopenharmony_ci ret = pll_wait_for_lock(pll); 4298c2ecf20Sopenharmony_ci if (ret < 0) { 4308c2ecf20Sopenharmony_ci pr_err("%s: pll: %s failed to lock\n", __func__, clk_name); 4318c2ecf20Sopenharmony_ci return ret; 4328c2ecf20Sopenharmony_ci } 4338c2ecf20Sopenharmony_ci 4348c2ecf20Sopenharmony_ci return 0; 4358c2ecf20Sopenharmony_ci} 4368c2ecf20Sopenharmony_ci 4378c2ecf20Sopenharmony_cistatic int iproc_pll_enable(struct clk_hw *hw) 4388c2ecf20Sopenharmony_ci{ 4398c2ecf20Sopenharmony_ci struct iproc_clk *clk = to_iproc_clk(hw); 4408c2ecf20Sopenharmony_ci struct iproc_pll *pll = clk->pll; 4418c2ecf20Sopenharmony_ci 4428c2ecf20Sopenharmony_ci return __pll_enable(pll); 4438c2ecf20Sopenharmony_ci} 4448c2ecf20Sopenharmony_ci 4458c2ecf20Sopenharmony_cistatic void iproc_pll_disable(struct clk_hw *hw) 4468c2ecf20Sopenharmony_ci{ 4478c2ecf20Sopenharmony_ci struct iproc_clk *clk = to_iproc_clk(hw); 4488c2ecf20Sopenharmony_ci struct iproc_pll *pll = clk->pll; 4498c2ecf20Sopenharmony_ci const struct iproc_pll_ctrl *ctrl = pll->ctrl; 4508c2ecf20Sopenharmony_ci 4518c2ecf20Sopenharmony_ci if (ctrl->flags & IPROC_CLK_AON) 4528c2ecf20Sopenharmony_ci return; 4538c2ecf20Sopenharmony_ci 4548c2ecf20Sopenharmony_ci __pll_disable(pll); 4558c2ecf20Sopenharmony_ci} 4568c2ecf20Sopenharmony_ci 4578c2ecf20Sopenharmony_cistatic unsigned long iproc_pll_recalc_rate(struct clk_hw *hw, 4588c2ecf20Sopenharmony_ci unsigned long parent_rate) 4598c2ecf20Sopenharmony_ci{ 4608c2ecf20Sopenharmony_ci struct iproc_clk *clk = to_iproc_clk(hw); 4618c2ecf20Sopenharmony_ci struct iproc_pll *pll = clk->pll; 4628c2ecf20Sopenharmony_ci const struct iproc_pll_ctrl *ctrl = pll->ctrl; 4638c2ecf20Sopenharmony_ci u32 val; 4648c2ecf20Sopenharmony_ci u64 ndiv, ndiv_int, ndiv_frac; 4658c2ecf20Sopenharmony_ci unsigned int pdiv; 4668c2ecf20Sopenharmony_ci unsigned long rate; 4678c2ecf20Sopenharmony_ci 4688c2ecf20Sopenharmony_ci if (parent_rate == 0) 4698c2ecf20Sopenharmony_ci return 0; 4708c2ecf20Sopenharmony_ci 4718c2ecf20Sopenharmony_ci /* PLL needs to be locked */ 4728c2ecf20Sopenharmony_ci val = readl(pll->status_base + ctrl->status.offset); 4738c2ecf20Sopenharmony_ci if ((val & (1 << ctrl->status.shift)) == 0) 4748c2ecf20Sopenharmony_ci return 0; 4758c2ecf20Sopenharmony_ci 4768c2ecf20Sopenharmony_ci /* 4778c2ecf20Sopenharmony_ci * PLL output frequency = 4788c2ecf20Sopenharmony_ci * 4798c2ecf20Sopenharmony_ci * ((ndiv_int + ndiv_frac / 2^20) * (parent clock rate / pdiv) 4808c2ecf20Sopenharmony_ci */ 4818c2ecf20Sopenharmony_ci val = readl(pll->control_base + ctrl->ndiv_int.offset); 4828c2ecf20Sopenharmony_ci ndiv_int = (val >> ctrl->ndiv_int.shift) & 4838c2ecf20Sopenharmony_ci bit_mask(ctrl->ndiv_int.width); 4848c2ecf20Sopenharmony_ci ndiv = ndiv_int << 20; 4858c2ecf20Sopenharmony_ci 4868c2ecf20Sopenharmony_ci if (ctrl->flags & IPROC_CLK_PLL_HAS_NDIV_FRAC) { 4878c2ecf20Sopenharmony_ci val = readl(pll->control_base + ctrl->ndiv_frac.offset); 4888c2ecf20Sopenharmony_ci ndiv_frac = (val >> ctrl->ndiv_frac.shift) & 4898c2ecf20Sopenharmony_ci bit_mask(ctrl->ndiv_frac.width); 4908c2ecf20Sopenharmony_ci ndiv += ndiv_frac; 4918c2ecf20Sopenharmony_ci } 4928c2ecf20Sopenharmony_ci 4938c2ecf20Sopenharmony_ci val = readl(pll->control_base + ctrl->pdiv.offset); 4948c2ecf20Sopenharmony_ci pdiv = (val >> ctrl->pdiv.shift) & bit_mask(ctrl->pdiv.width); 4958c2ecf20Sopenharmony_ci 4968c2ecf20Sopenharmony_ci rate = (ndiv * parent_rate) >> 20; 4978c2ecf20Sopenharmony_ci 4988c2ecf20Sopenharmony_ci if (pdiv == 0) 4998c2ecf20Sopenharmony_ci rate *= 2; 5008c2ecf20Sopenharmony_ci else 5018c2ecf20Sopenharmony_ci rate /= pdiv; 5028c2ecf20Sopenharmony_ci 5038c2ecf20Sopenharmony_ci return rate; 5048c2ecf20Sopenharmony_ci} 5058c2ecf20Sopenharmony_ci 5068c2ecf20Sopenharmony_cistatic int iproc_pll_determine_rate(struct clk_hw *hw, 5078c2ecf20Sopenharmony_ci struct clk_rate_request *req) 5088c2ecf20Sopenharmony_ci{ 5098c2ecf20Sopenharmony_ci unsigned int i; 5108c2ecf20Sopenharmony_ci struct iproc_clk *clk = to_iproc_clk(hw); 5118c2ecf20Sopenharmony_ci struct iproc_pll *pll = clk->pll; 5128c2ecf20Sopenharmony_ci const struct iproc_pll_ctrl *ctrl = pll->ctrl; 5138c2ecf20Sopenharmony_ci unsigned long diff, best_diff; 5148c2ecf20Sopenharmony_ci unsigned int best_idx = 0; 5158c2ecf20Sopenharmony_ci int ret; 5168c2ecf20Sopenharmony_ci 5178c2ecf20Sopenharmony_ci if (req->rate == 0 || req->best_parent_rate == 0) 5188c2ecf20Sopenharmony_ci return -EINVAL; 5198c2ecf20Sopenharmony_ci 5208c2ecf20Sopenharmony_ci if (ctrl->flags & IPROC_CLK_PLL_CALC_PARAM) { 5218c2ecf20Sopenharmony_ci struct iproc_pll_vco_param vco_param; 5228c2ecf20Sopenharmony_ci 5238c2ecf20Sopenharmony_ci ret = pll_calc_param(req->rate, req->best_parent_rate, 5248c2ecf20Sopenharmony_ci &vco_param); 5258c2ecf20Sopenharmony_ci if (ret) 5268c2ecf20Sopenharmony_ci return ret; 5278c2ecf20Sopenharmony_ci 5288c2ecf20Sopenharmony_ci req->rate = vco_param.rate; 5298c2ecf20Sopenharmony_ci return 0; 5308c2ecf20Sopenharmony_ci } 5318c2ecf20Sopenharmony_ci 5328c2ecf20Sopenharmony_ci if (!pll->vco_param) 5338c2ecf20Sopenharmony_ci return -EINVAL; 5348c2ecf20Sopenharmony_ci 5358c2ecf20Sopenharmony_ci best_diff = ULONG_MAX; 5368c2ecf20Sopenharmony_ci for (i = 0; i < pll->num_vco_entries; i++) { 5378c2ecf20Sopenharmony_ci diff = abs(req->rate - pll->vco_param[i].rate); 5388c2ecf20Sopenharmony_ci if (diff <= best_diff) { 5398c2ecf20Sopenharmony_ci best_diff = diff; 5408c2ecf20Sopenharmony_ci best_idx = i; 5418c2ecf20Sopenharmony_ci } 5428c2ecf20Sopenharmony_ci /* break now if perfect match */ 5438c2ecf20Sopenharmony_ci if (diff == 0) 5448c2ecf20Sopenharmony_ci break; 5458c2ecf20Sopenharmony_ci } 5468c2ecf20Sopenharmony_ci 5478c2ecf20Sopenharmony_ci req->rate = pll->vco_param[best_idx].rate; 5488c2ecf20Sopenharmony_ci 5498c2ecf20Sopenharmony_ci return 0; 5508c2ecf20Sopenharmony_ci} 5518c2ecf20Sopenharmony_ci 5528c2ecf20Sopenharmony_cistatic int iproc_pll_set_rate(struct clk_hw *hw, unsigned long rate, 5538c2ecf20Sopenharmony_ci unsigned long parent_rate) 5548c2ecf20Sopenharmony_ci{ 5558c2ecf20Sopenharmony_ci struct iproc_clk *clk = to_iproc_clk(hw); 5568c2ecf20Sopenharmony_ci struct iproc_pll *pll = clk->pll; 5578c2ecf20Sopenharmony_ci const struct iproc_pll_ctrl *ctrl = pll->ctrl; 5588c2ecf20Sopenharmony_ci struct iproc_pll_vco_param vco_param; 5598c2ecf20Sopenharmony_ci int rate_index, ret; 5608c2ecf20Sopenharmony_ci 5618c2ecf20Sopenharmony_ci if (ctrl->flags & IPROC_CLK_PLL_CALC_PARAM) { 5628c2ecf20Sopenharmony_ci ret = pll_calc_param(rate, parent_rate, &vco_param); 5638c2ecf20Sopenharmony_ci if (ret) 5648c2ecf20Sopenharmony_ci return ret; 5658c2ecf20Sopenharmony_ci } else { 5668c2ecf20Sopenharmony_ci rate_index = pll_get_rate_index(pll, rate); 5678c2ecf20Sopenharmony_ci if (rate_index < 0) 5688c2ecf20Sopenharmony_ci return rate_index; 5698c2ecf20Sopenharmony_ci 5708c2ecf20Sopenharmony_ci vco_param = pll->vco_param[rate_index]; 5718c2ecf20Sopenharmony_ci } 5728c2ecf20Sopenharmony_ci 5738c2ecf20Sopenharmony_ci ret = pll_set_rate(clk, &vco_param, parent_rate); 5748c2ecf20Sopenharmony_ci return ret; 5758c2ecf20Sopenharmony_ci} 5768c2ecf20Sopenharmony_ci 5778c2ecf20Sopenharmony_cistatic const struct clk_ops iproc_pll_ops = { 5788c2ecf20Sopenharmony_ci .enable = iproc_pll_enable, 5798c2ecf20Sopenharmony_ci .disable = iproc_pll_disable, 5808c2ecf20Sopenharmony_ci .recalc_rate = iproc_pll_recalc_rate, 5818c2ecf20Sopenharmony_ci .determine_rate = iproc_pll_determine_rate, 5828c2ecf20Sopenharmony_ci .set_rate = iproc_pll_set_rate, 5838c2ecf20Sopenharmony_ci}; 5848c2ecf20Sopenharmony_ci 5858c2ecf20Sopenharmony_cistatic int iproc_clk_enable(struct clk_hw *hw) 5868c2ecf20Sopenharmony_ci{ 5878c2ecf20Sopenharmony_ci struct iproc_clk *clk = to_iproc_clk(hw); 5888c2ecf20Sopenharmony_ci const struct iproc_clk_ctrl *ctrl = clk->ctrl; 5898c2ecf20Sopenharmony_ci struct iproc_pll *pll = clk->pll; 5908c2ecf20Sopenharmony_ci u32 val; 5918c2ecf20Sopenharmony_ci 5928c2ecf20Sopenharmony_ci /* channel enable is active low */ 5938c2ecf20Sopenharmony_ci val = readl(pll->control_base + ctrl->enable.offset); 5948c2ecf20Sopenharmony_ci val &= ~(1 << ctrl->enable.enable_shift); 5958c2ecf20Sopenharmony_ci iproc_pll_write(pll, pll->control_base, ctrl->enable.offset, val); 5968c2ecf20Sopenharmony_ci 5978c2ecf20Sopenharmony_ci /* also make sure channel is not held */ 5988c2ecf20Sopenharmony_ci val = readl(pll->control_base + ctrl->enable.offset); 5998c2ecf20Sopenharmony_ci val &= ~(1 << ctrl->enable.hold_shift); 6008c2ecf20Sopenharmony_ci iproc_pll_write(pll, pll->control_base, ctrl->enable.offset, val); 6018c2ecf20Sopenharmony_ci 6028c2ecf20Sopenharmony_ci return 0; 6038c2ecf20Sopenharmony_ci} 6048c2ecf20Sopenharmony_ci 6058c2ecf20Sopenharmony_cistatic void iproc_clk_disable(struct clk_hw *hw) 6068c2ecf20Sopenharmony_ci{ 6078c2ecf20Sopenharmony_ci struct iproc_clk *clk = to_iproc_clk(hw); 6088c2ecf20Sopenharmony_ci const struct iproc_clk_ctrl *ctrl = clk->ctrl; 6098c2ecf20Sopenharmony_ci struct iproc_pll *pll = clk->pll; 6108c2ecf20Sopenharmony_ci u32 val; 6118c2ecf20Sopenharmony_ci 6128c2ecf20Sopenharmony_ci if (ctrl->flags & IPROC_CLK_AON) 6138c2ecf20Sopenharmony_ci return; 6148c2ecf20Sopenharmony_ci 6158c2ecf20Sopenharmony_ci val = readl(pll->control_base + ctrl->enable.offset); 6168c2ecf20Sopenharmony_ci val |= 1 << ctrl->enable.enable_shift; 6178c2ecf20Sopenharmony_ci iproc_pll_write(pll, pll->control_base, ctrl->enable.offset, val); 6188c2ecf20Sopenharmony_ci} 6198c2ecf20Sopenharmony_ci 6208c2ecf20Sopenharmony_cistatic unsigned long iproc_clk_recalc_rate(struct clk_hw *hw, 6218c2ecf20Sopenharmony_ci unsigned long parent_rate) 6228c2ecf20Sopenharmony_ci{ 6238c2ecf20Sopenharmony_ci struct iproc_clk *clk = to_iproc_clk(hw); 6248c2ecf20Sopenharmony_ci const struct iproc_clk_ctrl *ctrl = clk->ctrl; 6258c2ecf20Sopenharmony_ci struct iproc_pll *pll = clk->pll; 6268c2ecf20Sopenharmony_ci u32 val; 6278c2ecf20Sopenharmony_ci unsigned int mdiv; 6288c2ecf20Sopenharmony_ci unsigned long rate; 6298c2ecf20Sopenharmony_ci 6308c2ecf20Sopenharmony_ci if (parent_rate == 0) 6318c2ecf20Sopenharmony_ci return 0; 6328c2ecf20Sopenharmony_ci 6338c2ecf20Sopenharmony_ci val = readl(pll->control_base + ctrl->mdiv.offset); 6348c2ecf20Sopenharmony_ci mdiv = (val >> ctrl->mdiv.shift) & bit_mask(ctrl->mdiv.width); 6358c2ecf20Sopenharmony_ci if (mdiv == 0) 6368c2ecf20Sopenharmony_ci mdiv = 256; 6378c2ecf20Sopenharmony_ci 6388c2ecf20Sopenharmony_ci if (ctrl->flags & IPROC_CLK_MCLK_DIV_BY_2) 6398c2ecf20Sopenharmony_ci rate = parent_rate / (mdiv * 2); 6408c2ecf20Sopenharmony_ci else 6418c2ecf20Sopenharmony_ci rate = parent_rate / mdiv; 6428c2ecf20Sopenharmony_ci 6438c2ecf20Sopenharmony_ci return rate; 6448c2ecf20Sopenharmony_ci} 6458c2ecf20Sopenharmony_ci 6468c2ecf20Sopenharmony_cistatic int iproc_clk_determine_rate(struct clk_hw *hw, 6478c2ecf20Sopenharmony_ci struct clk_rate_request *req) 6488c2ecf20Sopenharmony_ci{ 6498c2ecf20Sopenharmony_ci unsigned int bestdiv; 6508c2ecf20Sopenharmony_ci 6518c2ecf20Sopenharmony_ci if (req->rate == 0) 6528c2ecf20Sopenharmony_ci return -EINVAL; 6538c2ecf20Sopenharmony_ci if (req->rate == req->best_parent_rate) 6548c2ecf20Sopenharmony_ci return 0; 6558c2ecf20Sopenharmony_ci 6568c2ecf20Sopenharmony_ci bestdiv = DIV_ROUND_CLOSEST(req->best_parent_rate, req->rate); 6578c2ecf20Sopenharmony_ci if (bestdiv < 2) 6588c2ecf20Sopenharmony_ci req->rate = req->best_parent_rate; 6598c2ecf20Sopenharmony_ci 6608c2ecf20Sopenharmony_ci if (bestdiv > 256) 6618c2ecf20Sopenharmony_ci bestdiv = 256; 6628c2ecf20Sopenharmony_ci 6638c2ecf20Sopenharmony_ci req->rate = req->best_parent_rate / bestdiv; 6648c2ecf20Sopenharmony_ci 6658c2ecf20Sopenharmony_ci return 0; 6668c2ecf20Sopenharmony_ci} 6678c2ecf20Sopenharmony_ci 6688c2ecf20Sopenharmony_cistatic int iproc_clk_set_rate(struct clk_hw *hw, unsigned long rate, 6698c2ecf20Sopenharmony_ci unsigned long parent_rate) 6708c2ecf20Sopenharmony_ci{ 6718c2ecf20Sopenharmony_ci struct iproc_clk *clk = to_iproc_clk(hw); 6728c2ecf20Sopenharmony_ci const struct iproc_clk_ctrl *ctrl = clk->ctrl; 6738c2ecf20Sopenharmony_ci struct iproc_pll *pll = clk->pll; 6748c2ecf20Sopenharmony_ci u32 val; 6758c2ecf20Sopenharmony_ci unsigned int div; 6768c2ecf20Sopenharmony_ci 6778c2ecf20Sopenharmony_ci if (rate == 0 || parent_rate == 0) 6788c2ecf20Sopenharmony_ci return -EINVAL; 6798c2ecf20Sopenharmony_ci 6808c2ecf20Sopenharmony_ci div = DIV_ROUND_CLOSEST(parent_rate, rate); 6818c2ecf20Sopenharmony_ci if (ctrl->flags & IPROC_CLK_MCLK_DIV_BY_2) 6828c2ecf20Sopenharmony_ci div /= 2; 6838c2ecf20Sopenharmony_ci 6848c2ecf20Sopenharmony_ci if (div > 256) 6858c2ecf20Sopenharmony_ci return -EINVAL; 6868c2ecf20Sopenharmony_ci 6878c2ecf20Sopenharmony_ci val = readl(pll->control_base + ctrl->mdiv.offset); 6888c2ecf20Sopenharmony_ci if (div == 256) { 6898c2ecf20Sopenharmony_ci val &= ~(bit_mask(ctrl->mdiv.width) << ctrl->mdiv.shift); 6908c2ecf20Sopenharmony_ci } else { 6918c2ecf20Sopenharmony_ci val &= ~(bit_mask(ctrl->mdiv.width) << ctrl->mdiv.shift); 6928c2ecf20Sopenharmony_ci val |= div << ctrl->mdiv.shift; 6938c2ecf20Sopenharmony_ci } 6948c2ecf20Sopenharmony_ci iproc_pll_write(pll, pll->control_base, ctrl->mdiv.offset, val); 6958c2ecf20Sopenharmony_ci 6968c2ecf20Sopenharmony_ci return 0; 6978c2ecf20Sopenharmony_ci} 6988c2ecf20Sopenharmony_ci 6998c2ecf20Sopenharmony_cistatic const struct clk_ops iproc_clk_ops = { 7008c2ecf20Sopenharmony_ci .enable = iproc_clk_enable, 7018c2ecf20Sopenharmony_ci .disable = iproc_clk_disable, 7028c2ecf20Sopenharmony_ci .recalc_rate = iproc_clk_recalc_rate, 7038c2ecf20Sopenharmony_ci .determine_rate = iproc_clk_determine_rate, 7048c2ecf20Sopenharmony_ci .set_rate = iproc_clk_set_rate, 7058c2ecf20Sopenharmony_ci}; 7068c2ecf20Sopenharmony_ci 7078c2ecf20Sopenharmony_ci/** 7088c2ecf20Sopenharmony_ci * Some PLLs require the PLL SW override bit to be set before changes can be 7098c2ecf20Sopenharmony_ci * applied to the PLL 7108c2ecf20Sopenharmony_ci */ 7118c2ecf20Sopenharmony_cistatic void iproc_pll_sw_cfg(struct iproc_pll *pll) 7128c2ecf20Sopenharmony_ci{ 7138c2ecf20Sopenharmony_ci const struct iproc_pll_ctrl *ctrl = pll->ctrl; 7148c2ecf20Sopenharmony_ci 7158c2ecf20Sopenharmony_ci if (ctrl->flags & IPROC_CLK_PLL_NEEDS_SW_CFG) { 7168c2ecf20Sopenharmony_ci u32 val; 7178c2ecf20Sopenharmony_ci 7188c2ecf20Sopenharmony_ci val = readl(pll->control_base + ctrl->sw_ctrl.offset); 7198c2ecf20Sopenharmony_ci val |= BIT(ctrl->sw_ctrl.shift); 7208c2ecf20Sopenharmony_ci iproc_pll_write(pll, pll->control_base, ctrl->sw_ctrl.offset, 7218c2ecf20Sopenharmony_ci val); 7228c2ecf20Sopenharmony_ci } 7238c2ecf20Sopenharmony_ci} 7248c2ecf20Sopenharmony_ci 7258c2ecf20Sopenharmony_civoid iproc_pll_clk_setup(struct device_node *node, 7268c2ecf20Sopenharmony_ci const struct iproc_pll_ctrl *pll_ctrl, 7278c2ecf20Sopenharmony_ci const struct iproc_pll_vco_param *vco, 7288c2ecf20Sopenharmony_ci unsigned int num_vco_entries, 7298c2ecf20Sopenharmony_ci const struct iproc_clk_ctrl *clk_ctrl, 7308c2ecf20Sopenharmony_ci unsigned int num_clks) 7318c2ecf20Sopenharmony_ci{ 7328c2ecf20Sopenharmony_ci int i, ret; 7338c2ecf20Sopenharmony_ci struct iproc_pll *pll; 7348c2ecf20Sopenharmony_ci struct iproc_clk *iclk; 7358c2ecf20Sopenharmony_ci struct clk_init_data init; 7368c2ecf20Sopenharmony_ci const char *parent_name; 7378c2ecf20Sopenharmony_ci struct iproc_clk *iclk_array; 7388c2ecf20Sopenharmony_ci struct clk_hw_onecell_data *clk_data; 7398c2ecf20Sopenharmony_ci const char *clk_name; 7408c2ecf20Sopenharmony_ci 7418c2ecf20Sopenharmony_ci if (WARN_ON(!pll_ctrl) || WARN_ON(!clk_ctrl)) 7428c2ecf20Sopenharmony_ci return; 7438c2ecf20Sopenharmony_ci 7448c2ecf20Sopenharmony_ci pll = kzalloc(sizeof(*pll), GFP_KERNEL); 7458c2ecf20Sopenharmony_ci if (WARN_ON(!pll)) 7468c2ecf20Sopenharmony_ci return; 7478c2ecf20Sopenharmony_ci 7488c2ecf20Sopenharmony_ci clk_data = kzalloc(struct_size(clk_data, hws, num_clks), GFP_KERNEL); 7498c2ecf20Sopenharmony_ci if (WARN_ON(!clk_data)) 7508c2ecf20Sopenharmony_ci goto err_clk_data; 7518c2ecf20Sopenharmony_ci clk_data->num = num_clks; 7528c2ecf20Sopenharmony_ci 7538c2ecf20Sopenharmony_ci iclk_array = kcalloc(num_clks, sizeof(struct iproc_clk), GFP_KERNEL); 7548c2ecf20Sopenharmony_ci if (WARN_ON(!iclk_array)) 7558c2ecf20Sopenharmony_ci goto err_clks; 7568c2ecf20Sopenharmony_ci 7578c2ecf20Sopenharmony_ci pll->control_base = of_iomap(node, 0); 7588c2ecf20Sopenharmony_ci if (WARN_ON(!pll->control_base)) 7598c2ecf20Sopenharmony_ci goto err_pll_iomap; 7608c2ecf20Sopenharmony_ci 7618c2ecf20Sopenharmony_ci /* Some SoCs do not require the pwr_base, thus failing is not fatal */ 7628c2ecf20Sopenharmony_ci pll->pwr_base = of_iomap(node, 1); 7638c2ecf20Sopenharmony_ci 7648c2ecf20Sopenharmony_ci /* some PLLs require gating control at the top ASIU level */ 7658c2ecf20Sopenharmony_ci if (pll_ctrl->flags & IPROC_CLK_PLL_ASIU) { 7668c2ecf20Sopenharmony_ci pll->asiu_base = of_iomap(node, 2); 7678c2ecf20Sopenharmony_ci if (WARN_ON(!pll->asiu_base)) 7688c2ecf20Sopenharmony_ci goto err_asiu_iomap; 7698c2ecf20Sopenharmony_ci } 7708c2ecf20Sopenharmony_ci 7718c2ecf20Sopenharmony_ci if (pll_ctrl->flags & IPROC_CLK_PLL_SPLIT_STAT_CTRL) { 7728c2ecf20Sopenharmony_ci /* Some SoCs have a split status/control. If this does not 7738c2ecf20Sopenharmony_ci * exist, assume they are unified. 7748c2ecf20Sopenharmony_ci */ 7758c2ecf20Sopenharmony_ci pll->status_base = of_iomap(node, 2); 7768c2ecf20Sopenharmony_ci if (!pll->status_base) 7778c2ecf20Sopenharmony_ci goto err_status_iomap; 7788c2ecf20Sopenharmony_ci } else 7798c2ecf20Sopenharmony_ci pll->status_base = pll->control_base; 7808c2ecf20Sopenharmony_ci 7818c2ecf20Sopenharmony_ci /* initialize and register the PLL itself */ 7828c2ecf20Sopenharmony_ci pll->ctrl = pll_ctrl; 7838c2ecf20Sopenharmony_ci 7848c2ecf20Sopenharmony_ci iclk = &iclk_array[0]; 7858c2ecf20Sopenharmony_ci iclk->pll = pll; 7868c2ecf20Sopenharmony_ci 7878c2ecf20Sopenharmony_ci ret = of_property_read_string_index(node, "clock-output-names", 7888c2ecf20Sopenharmony_ci 0, &clk_name); 7898c2ecf20Sopenharmony_ci if (WARN_ON(ret)) 7908c2ecf20Sopenharmony_ci goto err_pll_register; 7918c2ecf20Sopenharmony_ci 7928c2ecf20Sopenharmony_ci init.name = clk_name; 7938c2ecf20Sopenharmony_ci init.ops = &iproc_pll_ops; 7948c2ecf20Sopenharmony_ci init.flags = 0; 7958c2ecf20Sopenharmony_ci parent_name = of_clk_get_parent_name(node, 0); 7968c2ecf20Sopenharmony_ci init.parent_names = (parent_name ? &parent_name : NULL); 7978c2ecf20Sopenharmony_ci init.num_parents = (parent_name ? 1 : 0); 7988c2ecf20Sopenharmony_ci iclk->hw.init = &init; 7998c2ecf20Sopenharmony_ci 8008c2ecf20Sopenharmony_ci if (vco) { 8018c2ecf20Sopenharmony_ci pll->num_vco_entries = num_vco_entries; 8028c2ecf20Sopenharmony_ci pll->vco_param = vco; 8038c2ecf20Sopenharmony_ci } 8048c2ecf20Sopenharmony_ci 8058c2ecf20Sopenharmony_ci iproc_pll_sw_cfg(pll); 8068c2ecf20Sopenharmony_ci 8078c2ecf20Sopenharmony_ci ret = clk_hw_register(NULL, &iclk->hw); 8088c2ecf20Sopenharmony_ci if (WARN_ON(ret)) 8098c2ecf20Sopenharmony_ci goto err_pll_register; 8108c2ecf20Sopenharmony_ci 8118c2ecf20Sopenharmony_ci clk_data->hws[0] = &iclk->hw; 8128c2ecf20Sopenharmony_ci parent_name = clk_name; 8138c2ecf20Sopenharmony_ci 8148c2ecf20Sopenharmony_ci /* now initialize and register all leaf clocks */ 8158c2ecf20Sopenharmony_ci for (i = 1; i < num_clks; i++) { 8168c2ecf20Sopenharmony_ci memset(&init, 0, sizeof(init)); 8178c2ecf20Sopenharmony_ci 8188c2ecf20Sopenharmony_ci ret = of_property_read_string_index(node, "clock-output-names", 8198c2ecf20Sopenharmony_ci i, &clk_name); 8208c2ecf20Sopenharmony_ci if (WARN_ON(ret)) 8218c2ecf20Sopenharmony_ci goto err_clk_register; 8228c2ecf20Sopenharmony_ci 8238c2ecf20Sopenharmony_ci iclk = &iclk_array[i]; 8248c2ecf20Sopenharmony_ci iclk->pll = pll; 8258c2ecf20Sopenharmony_ci iclk->ctrl = &clk_ctrl[i]; 8268c2ecf20Sopenharmony_ci 8278c2ecf20Sopenharmony_ci init.name = clk_name; 8288c2ecf20Sopenharmony_ci init.ops = &iproc_clk_ops; 8298c2ecf20Sopenharmony_ci init.flags = 0; 8308c2ecf20Sopenharmony_ci init.parent_names = (parent_name ? &parent_name : NULL); 8318c2ecf20Sopenharmony_ci init.num_parents = (parent_name ? 1 : 0); 8328c2ecf20Sopenharmony_ci iclk->hw.init = &init; 8338c2ecf20Sopenharmony_ci 8348c2ecf20Sopenharmony_ci ret = clk_hw_register(NULL, &iclk->hw); 8358c2ecf20Sopenharmony_ci if (WARN_ON(ret)) 8368c2ecf20Sopenharmony_ci goto err_clk_register; 8378c2ecf20Sopenharmony_ci 8388c2ecf20Sopenharmony_ci clk_data->hws[i] = &iclk->hw; 8398c2ecf20Sopenharmony_ci } 8408c2ecf20Sopenharmony_ci 8418c2ecf20Sopenharmony_ci ret = of_clk_add_hw_provider(node, of_clk_hw_onecell_get, clk_data); 8428c2ecf20Sopenharmony_ci if (WARN_ON(ret)) 8438c2ecf20Sopenharmony_ci goto err_clk_register; 8448c2ecf20Sopenharmony_ci 8458c2ecf20Sopenharmony_ci return; 8468c2ecf20Sopenharmony_ci 8478c2ecf20Sopenharmony_cierr_clk_register: 8488c2ecf20Sopenharmony_ci while (--i >= 0) 8498c2ecf20Sopenharmony_ci clk_hw_unregister(clk_data->hws[i]); 8508c2ecf20Sopenharmony_ci 8518c2ecf20Sopenharmony_cierr_pll_register: 8528c2ecf20Sopenharmony_ci if (pll->status_base != pll->control_base) 8538c2ecf20Sopenharmony_ci iounmap(pll->status_base); 8548c2ecf20Sopenharmony_ci 8558c2ecf20Sopenharmony_cierr_status_iomap: 8568c2ecf20Sopenharmony_ci if (pll->asiu_base) 8578c2ecf20Sopenharmony_ci iounmap(pll->asiu_base); 8588c2ecf20Sopenharmony_ci 8598c2ecf20Sopenharmony_cierr_asiu_iomap: 8608c2ecf20Sopenharmony_ci if (pll->pwr_base) 8618c2ecf20Sopenharmony_ci iounmap(pll->pwr_base); 8628c2ecf20Sopenharmony_ci 8638c2ecf20Sopenharmony_ci iounmap(pll->control_base); 8648c2ecf20Sopenharmony_ci 8658c2ecf20Sopenharmony_cierr_pll_iomap: 8668c2ecf20Sopenharmony_ci kfree(iclk_array); 8678c2ecf20Sopenharmony_ci 8688c2ecf20Sopenharmony_cierr_clks: 8698c2ecf20Sopenharmony_ci kfree(clk_data); 8708c2ecf20Sopenharmony_ci 8718c2ecf20Sopenharmony_cierr_clk_data: 8728c2ecf20Sopenharmony_ci kfree(pll); 8738c2ecf20Sopenharmony_ci} 874