18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci// Copyright (c) 2018, The Linux Foundation. All rights reserved. 38c2ecf20Sopenharmony_ci 48c2ecf20Sopenharmony_ci#include <linux/kernel.h> 58c2ecf20Sopenharmony_ci#include <linux/export.h> 68c2ecf20Sopenharmony_ci#include <linux/regmap.h> 78c2ecf20Sopenharmony_ci#include <linux/delay.h> 88c2ecf20Sopenharmony_ci#include <linux/err.h> 98c2ecf20Sopenharmony_ci#include <linux/clk-provider.h> 108c2ecf20Sopenharmony_ci#include <linux/spinlock.h> 118c2ecf20Sopenharmony_ci 128c2ecf20Sopenharmony_ci#include "clk-regmap.h" 138c2ecf20Sopenharmony_ci#include "clk-hfpll.h" 148c2ecf20Sopenharmony_ci 158c2ecf20Sopenharmony_ci#define PLL_OUTCTRL BIT(0) 168c2ecf20Sopenharmony_ci#define PLL_BYPASSNL BIT(1) 178c2ecf20Sopenharmony_ci#define PLL_RESET_N BIT(2) 188c2ecf20Sopenharmony_ci 198c2ecf20Sopenharmony_ci/* Initialize a HFPLL at a given rate and enable it. */ 208c2ecf20Sopenharmony_cistatic void __clk_hfpll_init_once(struct clk_hw *hw) 218c2ecf20Sopenharmony_ci{ 228c2ecf20Sopenharmony_ci struct clk_hfpll *h = to_clk_hfpll(hw); 238c2ecf20Sopenharmony_ci struct hfpll_data const *hd = h->d; 248c2ecf20Sopenharmony_ci struct regmap *regmap = h->clkr.regmap; 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_ci if (likely(h->init_done)) 278c2ecf20Sopenharmony_ci return; 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_ci /* Configure PLL parameters for integer mode. */ 308c2ecf20Sopenharmony_ci if (hd->config_val) 318c2ecf20Sopenharmony_ci regmap_write(regmap, hd->config_reg, hd->config_val); 328c2ecf20Sopenharmony_ci regmap_write(regmap, hd->m_reg, 0); 338c2ecf20Sopenharmony_ci regmap_write(regmap, hd->n_reg, 1); 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_ci if (hd->user_reg) { 368c2ecf20Sopenharmony_ci u32 regval = hd->user_val; 378c2ecf20Sopenharmony_ci unsigned long rate; 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_ci rate = clk_hw_get_rate(hw); 408c2ecf20Sopenharmony_ci 418c2ecf20Sopenharmony_ci /* Pick the right VCO. */ 428c2ecf20Sopenharmony_ci if (hd->user_vco_mask && rate > hd->low_vco_max_rate) 438c2ecf20Sopenharmony_ci regval |= hd->user_vco_mask; 448c2ecf20Sopenharmony_ci regmap_write(regmap, hd->user_reg, regval); 458c2ecf20Sopenharmony_ci } 468c2ecf20Sopenharmony_ci 478c2ecf20Sopenharmony_ci if (hd->droop_reg) 488c2ecf20Sopenharmony_ci regmap_write(regmap, hd->droop_reg, hd->droop_val); 498c2ecf20Sopenharmony_ci 508c2ecf20Sopenharmony_ci h->init_done = true; 518c2ecf20Sopenharmony_ci} 528c2ecf20Sopenharmony_ci 538c2ecf20Sopenharmony_cistatic void __clk_hfpll_enable(struct clk_hw *hw) 548c2ecf20Sopenharmony_ci{ 558c2ecf20Sopenharmony_ci struct clk_hfpll *h = to_clk_hfpll(hw); 568c2ecf20Sopenharmony_ci struct hfpll_data const *hd = h->d; 578c2ecf20Sopenharmony_ci struct regmap *regmap = h->clkr.regmap; 588c2ecf20Sopenharmony_ci u32 val; 598c2ecf20Sopenharmony_ci 608c2ecf20Sopenharmony_ci __clk_hfpll_init_once(hw); 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_ci /* Disable PLL bypass mode. */ 638c2ecf20Sopenharmony_ci regmap_update_bits(regmap, hd->mode_reg, PLL_BYPASSNL, PLL_BYPASSNL); 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_ci /* 668c2ecf20Sopenharmony_ci * H/W requires a 5us delay between disabling the bypass and 678c2ecf20Sopenharmony_ci * de-asserting the reset. Delay 10us just to be safe. 688c2ecf20Sopenharmony_ci */ 698c2ecf20Sopenharmony_ci udelay(10); 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_ci /* De-assert active-low PLL reset. */ 728c2ecf20Sopenharmony_ci regmap_update_bits(regmap, hd->mode_reg, PLL_RESET_N, PLL_RESET_N); 738c2ecf20Sopenharmony_ci 748c2ecf20Sopenharmony_ci /* Wait for PLL to lock. */ 758c2ecf20Sopenharmony_ci if (hd->status_reg) { 768c2ecf20Sopenharmony_ci do { 778c2ecf20Sopenharmony_ci regmap_read(regmap, hd->status_reg, &val); 788c2ecf20Sopenharmony_ci } while (!(val & BIT(hd->lock_bit))); 798c2ecf20Sopenharmony_ci } else { 808c2ecf20Sopenharmony_ci udelay(60); 818c2ecf20Sopenharmony_ci } 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_ci /* Enable PLL output. */ 848c2ecf20Sopenharmony_ci regmap_update_bits(regmap, hd->mode_reg, PLL_OUTCTRL, PLL_OUTCTRL); 858c2ecf20Sopenharmony_ci} 868c2ecf20Sopenharmony_ci 878c2ecf20Sopenharmony_ci/* Enable an already-configured HFPLL. */ 888c2ecf20Sopenharmony_cistatic int clk_hfpll_enable(struct clk_hw *hw) 898c2ecf20Sopenharmony_ci{ 908c2ecf20Sopenharmony_ci unsigned long flags; 918c2ecf20Sopenharmony_ci struct clk_hfpll *h = to_clk_hfpll(hw); 928c2ecf20Sopenharmony_ci struct hfpll_data const *hd = h->d; 938c2ecf20Sopenharmony_ci struct regmap *regmap = h->clkr.regmap; 948c2ecf20Sopenharmony_ci u32 mode; 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_ci spin_lock_irqsave(&h->lock, flags); 978c2ecf20Sopenharmony_ci regmap_read(regmap, hd->mode_reg, &mode); 988c2ecf20Sopenharmony_ci if (!(mode & (PLL_BYPASSNL | PLL_RESET_N | PLL_OUTCTRL))) 998c2ecf20Sopenharmony_ci __clk_hfpll_enable(hw); 1008c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&h->lock, flags); 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_ci return 0; 1038c2ecf20Sopenharmony_ci} 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_cistatic void __clk_hfpll_disable(struct clk_hfpll *h) 1068c2ecf20Sopenharmony_ci{ 1078c2ecf20Sopenharmony_ci struct hfpll_data const *hd = h->d; 1088c2ecf20Sopenharmony_ci struct regmap *regmap = h->clkr.regmap; 1098c2ecf20Sopenharmony_ci 1108c2ecf20Sopenharmony_ci /* 1118c2ecf20Sopenharmony_ci * Disable the PLL output, disable test mode, enable the bypass mode, 1128c2ecf20Sopenharmony_ci * and assert the reset. 1138c2ecf20Sopenharmony_ci */ 1148c2ecf20Sopenharmony_ci regmap_update_bits(regmap, hd->mode_reg, 1158c2ecf20Sopenharmony_ci PLL_BYPASSNL | PLL_RESET_N | PLL_OUTCTRL, 0); 1168c2ecf20Sopenharmony_ci} 1178c2ecf20Sopenharmony_ci 1188c2ecf20Sopenharmony_cistatic void clk_hfpll_disable(struct clk_hw *hw) 1198c2ecf20Sopenharmony_ci{ 1208c2ecf20Sopenharmony_ci struct clk_hfpll *h = to_clk_hfpll(hw); 1218c2ecf20Sopenharmony_ci unsigned long flags; 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_ci spin_lock_irqsave(&h->lock, flags); 1248c2ecf20Sopenharmony_ci __clk_hfpll_disable(h); 1258c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&h->lock, flags); 1268c2ecf20Sopenharmony_ci} 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_cistatic long clk_hfpll_round_rate(struct clk_hw *hw, unsigned long rate, 1298c2ecf20Sopenharmony_ci unsigned long *parent_rate) 1308c2ecf20Sopenharmony_ci{ 1318c2ecf20Sopenharmony_ci struct clk_hfpll *h = to_clk_hfpll(hw); 1328c2ecf20Sopenharmony_ci struct hfpll_data const *hd = h->d; 1338c2ecf20Sopenharmony_ci unsigned long rrate; 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_ci rate = clamp(rate, hd->min_rate, hd->max_rate); 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_ci rrate = DIV_ROUND_UP(rate, *parent_rate) * *parent_rate; 1388c2ecf20Sopenharmony_ci if (rrate > hd->max_rate) 1398c2ecf20Sopenharmony_ci rrate -= *parent_rate; 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_ci return rrate; 1428c2ecf20Sopenharmony_ci} 1438c2ecf20Sopenharmony_ci 1448c2ecf20Sopenharmony_ci/* 1458c2ecf20Sopenharmony_ci * For optimization reasons, assumes no downstream clocks are actively using 1468c2ecf20Sopenharmony_ci * it. 1478c2ecf20Sopenharmony_ci */ 1488c2ecf20Sopenharmony_cistatic int clk_hfpll_set_rate(struct clk_hw *hw, unsigned long rate, 1498c2ecf20Sopenharmony_ci unsigned long parent_rate) 1508c2ecf20Sopenharmony_ci{ 1518c2ecf20Sopenharmony_ci struct clk_hfpll *h = to_clk_hfpll(hw); 1528c2ecf20Sopenharmony_ci struct hfpll_data const *hd = h->d; 1538c2ecf20Sopenharmony_ci struct regmap *regmap = h->clkr.regmap; 1548c2ecf20Sopenharmony_ci unsigned long flags; 1558c2ecf20Sopenharmony_ci u32 l_val, val; 1568c2ecf20Sopenharmony_ci bool enabled; 1578c2ecf20Sopenharmony_ci 1588c2ecf20Sopenharmony_ci l_val = rate / parent_rate; 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_ci spin_lock_irqsave(&h->lock, flags); 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_ci enabled = __clk_is_enabled(hw->clk); 1638c2ecf20Sopenharmony_ci if (enabled) 1648c2ecf20Sopenharmony_ci __clk_hfpll_disable(h); 1658c2ecf20Sopenharmony_ci 1668c2ecf20Sopenharmony_ci /* Pick the right VCO. */ 1678c2ecf20Sopenharmony_ci if (hd->user_reg && hd->user_vco_mask) { 1688c2ecf20Sopenharmony_ci regmap_read(regmap, hd->user_reg, &val); 1698c2ecf20Sopenharmony_ci if (rate <= hd->low_vco_max_rate) 1708c2ecf20Sopenharmony_ci val &= ~hd->user_vco_mask; 1718c2ecf20Sopenharmony_ci else 1728c2ecf20Sopenharmony_ci val |= hd->user_vco_mask; 1738c2ecf20Sopenharmony_ci regmap_write(regmap, hd->user_reg, val); 1748c2ecf20Sopenharmony_ci } 1758c2ecf20Sopenharmony_ci 1768c2ecf20Sopenharmony_ci regmap_write(regmap, hd->l_reg, l_val); 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_ci if (enabled) 1798c2ecf20Sopenharmony_ci __clk_hfpll_enable(hw); 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&h->lock, flags); 1828c2ecf20Sopenharmony_ci 1838c2ecf20Sopenharmony_ci return 0; 1848c2ecf20Sopenharmony_ci} 1858c2ecf20Sopenharmony_ci 1868c2ecf20Sopenharmony_cistatic unsigned long clk_hfpll_recalc_rate(struct clk_hw *hw, 1878c2ecf20Sopenharmony_ci unsigned long parent_rate) 1888c2ecf20Sopenharmony_ci{ 1898c2ecf20Sopenharmony_ci struct clk_hfpll *h = to_clk_hfpll(hw); 1908c2ecf20Sopenharmony_ci struct hfpll_data const *hd = h->d; 1918c2ecf20Sopenharmony_ci struct regmap *regmap = h->clkr.regmap; 1928c2ecf20Sopenharmony_ci u32 l_val; 1938c2ecf20Sopenharmony_ci 1948c2ecf20Sopenharmony_ci regmap_read(regmap, hd->l_reg, &l_val); 1958c2ecf20Sopenharmony_ci 1968c2ecf20Sopenharmony_ci return l_val * parent_rate; 1978c2ecf20Sopenharmony_ci} 1988c2ecf20Sopenharmony_ci 1998c2ecf20Sopenharmony_cistatic int clk_hfpll_init(struct clk_hw *hw) 2008c2ecf20Sopenharmony_ci{ 2018c2ecf20Sopenharmony_ci struct clk_hfpll *h = to_clk_hfpll(hw); 2028c2ecf20Sopenharmony_ci struct hfpll_data const *hd = h->d; 2038c2ecf20Sopenharmony_ci struct regmap *regmap = h->clkr.regmap; 2048c2ecf20Sopenharmony_ci u32 mode, status; 2058c2ecf20Sopenharmony_ci 2068c2ecf20Sopenharmony_ci regmap_read(regmap, hd->mode_reg, &mode); 2078c2ecf20Sopenharmony_ci if (mode != (PLL_BYPASSNL | PLL_RESET_N | PLL_OUTCTRL)) { 2088c2ecf20Sopenharmony_ci __clk_hfpll_init_once(hw); 2098c2ecf20Sopenharmony_ci return 0; 2108c2ecf20Sopenharmony_ci } 2118c2ecf20Sopenharmony_ci 2128c2ecf20Sopenharmony_ci if (hd->status_reg) { 2138c2ecf20Sopenharmony_ci regmap_read(regmap, hd->status_reg, &status); 2148c2ecf20Sopenharmony_ci if (!(status & BIT(hd->lock_bit))) { 2158c2ecf20Sopenharmony_ci WARN(1, "HFPLL %s is ON, but not locked!\n", 2168c2ecf20Sopenharmony_ci __clk_get_name(hw->clk)); 2178c2ecf20Sopenharmony_ci clk_hfpll_disable(hw); 2188c2ecf20Sopenharmony_ci __clk_hfpll_init_once(hw); 2198c2ecf20Sopenharmony_ci } 2208c2ecf20Sopenharmony_ci } 2218c2ecf20Sopenharmony_ci 2228c2ecf20Sopenharmony_ci return 0; 2238c2ecf20Sopenharmony_ci} 2248c2ecf20Sopenharmony_ci 2258c2ecf20Sopenharmony_cistatic int hfpll_is_enabled(struct clk_hw *hw) 2268c2ecf20Sopenharmony_ci{ 2278c2ecf20Sopenharmony_ci struct clk_hfpll *h = to_clk_hfpll(hw); 2288c2ecf20Sopenharmony_ci struct hfpll_data const *hd = h->d; 2298c2ecf20Sopenharmony_ci struct regmap *regmap = h->clkr.regmap; 2308c2ecf20Sopenharmony_ci u32 mode; 2318c2ecf20Sopenharmony_ci 2328c2ecf20Sopenharmony_ci regmap_read(regmap, hd->mode_reg, &mode); 2338c2ecf20Sopenharmony_ci mode &= 0x7; 2348c2ecf20Sopenharmony_ci return mode == (PLL_BYPASSNL | PLL_RESET_N | PLL_OUTCTRL); 2358c2ecf20Sopenharmony_ci} 2368c2ecf20Sopenharmony_ci 2378c2ecf20Sopenharmony_ciconst struct clk_ops clk_ops_hfpll = { 2388c2ecf20Sopenharmony_ci .enable = clk_hfpll_enable, 2398c2ecf20Sopenharmony_ci .disable = clk_hfpll_disable, 2408c2ecf20Sopenharmony_ci .is_enabled = hfpll_is_enabled, 2418c2ecf20Sopenharmony_ci .round_rate = clk_hfpll_round_rate, 2428c2ecf20Sopenharmony_ci .set_rate = clk_hfpll_set_rate, 2438c2ecf20Sopenharmony_ci .recalc_rate = clk_hfpll_recalc_rate, 2448c2ecf20Sopenharmony_ci .init = clk_hfpll_init, 2458c2ecf20Sopenharmony_ci}; 2468c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(clk_ops_hfpll); 247