18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright (C) 2020-2022 MaxLinear, Inc. 48c2ecf20Sopenharmony_ci * Copyright (C) 2020 Intel Corporation. 58c2ecf20Sopenharmony_ci * Zhu Yixin <yzhu@maxlinear.com> 68c2ecf20Sopenharmony_ci * Rahul Tanwar <rtanwar@maxlinear.com> 78c2ecf20Sopenharmony_ci */ 88c2ecf20Sopenharmony_ci 98c2ecf20Sopenharmony_ci#include <linux/clk-provider.h> 108c2ecf20Sopenharmony_ci#include <linux/delay.h> 118c2ecf20Sopenharmony_ci#include <linux/device.h> 128c2ecf20Sopenharmony_ci#include <linux/iopoll.h> 138c2ecf20Sopenharmony_ci#include <linux/of.h> 148c2ecf20Sopenharmony_ci 158c2ecf20Sopenharmony_ci#include "clk-cgu.h" 168c2ecf20Sopenharmony_ci 178c2ecf20Sopenharmony_ci#define to_lgm_clk_pll(_hw) container_of(_hw, struct lgm_clk_pll, hw) 188c2ecf20Sopenharmony_ci#define PLL_REF_DIV(x) ((x) + 0x08) 198c2ecf20Sopenharmony_ci 208c2ecf20Sopenharmony_ci/* 218c2ecf20Sopenharmony_ci * Calculate formula: 228c2ecf20Sopenharmony_ci * rate = (prate * mult + (prate * frac) / frac_div) / div 238c2ecf20Sopenharmony_ci */ 248c2ecf20Sopenharmony_cistatic unsigned long 258c2ecf20Sopenharmony_cilgm_pll_calc_rate(unsigned long prate, unsigned int mult, 268c2ecf20Sopenharmony_ci unsigned int div, unsigned int frac, unsigned int frac_div) 278c2ecf20Sopenharmony_ci{ 288c2ecf20Sopenharmony_ci u64 crate, frate, rate64; 298c2ecf20Sopenharmony_ci 308c2ecf20Sopenharmony_ci rate64 = prate; 318c2ecf20Sopenharmony_ci crate = rate64 * mult; 328c2ecf20Sopenharmony_ci frate = rate64 * frac; 338c2ecf20Sopenharmony_ci do_div(frate, frac_div); 348c2ecf20Sopenharmony_ci crate += frate; 358c2ecf20Sopenharmony_ci do_div(crate, div); 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_ci return crate; 388c2ecf20Sopenharmony_ci} 398c2ecf20Sopenharmony_ci 408c2ecf20Sopenharmony_cistatic unsigned long lgm_pll_recalc_rate(struct clk_hw *hw, unsigned long prate) 418c2ecf20Sopenharmony_ci{ 428c2ecf20Sopenharmony_ci struct lgm_clk_pll *pll = to_lgm_clk_pll(hw); 438c2ecf20Sopenharmony_ci unsigned int div, mult, frac; 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_ci mult = lgm_get_clk_val(pll->membase, PLL_REF_DIV(pll->reg), 0, 12); 468c2ecf20Sopenharmony_ci div = lgm_get_clk_val(pll->membase, PLL_REF_DIV(pll->reg), 18, 6); 478c2ecf20Sopenharmony_ci frac = lgm_get_clk_val(pll->membase, pll->reg, 2, 24); 488c2ecf20Sopenharmony_ci 498c2ecf20Sopenharmony_ci if (pll->type == TYPE_LJPLL) 508c2ecf20Sopenharmony_ci div *= 4; 518c2ecf20Sopenharmony_ci 528c2ecf20Sopenharmony_ci return lgm_pll_calc_rate(prate, mult, div, frac, BIT(24)); 538c2ecf20Sopenharmony_ci} 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_cistatic int lgm_pll_is_enabled(struct clk_hw *hw) 568c2ecf20Sopenharmony_ci{ 578c2ecf20Sopenharmony_ci struct lgm_clk_pll *pll = to_lgm_clk_pll(hw); 588c2ecf20Sopenharmony_ci unsigned int ret; 598c2ecf20Sopenharmony_ci 608c2ecf20Sopenharmony_ci ret = lgm_get_clk_val(pll->membase, pll->reg, 0, 1); 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_ci return ret; 638c2ecf20Sopenharmony_ci} 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_cistatic int lgm_pll_enable(struct clk_hw *hw) 668c2ecf20Sopenharmony_ci{ 678c2ecf20Sopenharmony_ci struct lgm_clk_pll *pll = to_lgm_clk_pll(hw); 688c2ecf20Sopenharmony_ci u32 val; 698c2ecf20Sopenharmony_ci int ret; 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_ci lgm_set_clk_val(pll->membase, pll->reg, 0, 1, 1); 728c2ecf20Sopenharmony_ci ret = regmap_read_poll_timeout_atomic(pll->membase, pll->reg, 738c2ecf20Sopenharmony_ci val, (val & 0x1), 1, 100); 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_ci 768c2ecf20Sopenharmony_ci return ret; 778c2ecf20Sopenharmony_ci} 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_cistatic void lgm_pll_disable(struct clk_hw *hw) 808c2ecf20Sopenharmony_ci{ 818c2ecf20Sopenharmony_ci struct lgm_clk_pll *pll = to_lgm_clk_pll(hw); 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_ci lgm_set_clk_val(pll->membase, pll->reg, 0, 1, 0); 848c2ecf20Sopenharmony_ci} 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_cistatic const struct clk_ops lgm_pll_ops = { 878c2ecf20Sopenharmony_ci .recalc_rate = lgm_pll_recalc_rate, 888c2ecf20Sopenharmony_ci .is_enabled = lgm_pll_is_enabled, 898c2ecf20Sopenharmony_ci .enable = lgm_pll_enable, 908c2ecf20Sopenharmony_ci .disable = lgm_pll_disable, 918c2ecf20Sopenharmony_ci}; 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_cistatic struct clk_hw * 948c2ecf20Sopenharmony_cilgm_clk_register_pll(struct lgm_clk_provider *ctx, 958c2ecf20Sopenharmony_ci const struct lgm_pll_clk_data *list) 968c2ecf20Sopenharmony_ci{ 978c2ecf20Sopenharmony_ci struct clk_init_data init = {}; 988c2ecf20Sopenharmony_ci struct lgm_clk_pll *pll; 998c2ecf20Sopenharmony_ci struct device *dev = ctx->dev; 1008c2ecf20Sopenharmony_ci struct clk_hw *hw; 1018c2ecf20Sopenharmony_ci int ret; 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_ci init.ops = &lgm_pll_ops; 1048c2ecf20Sopenharmony_ci init.name = list->name; 1058c2ecf20Sopenharmony_ci init.flags = list->flags; 1068c2ecf20Sopenharmony_ci init.parent_data = list->parent_data; 1078c2ecf20Sopenharmony_ci init.num_parents = list->num_parents; 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_ci pll = devm_kzalloc(dev, sizeof(*pll), GFP_KERNEL); 1108c2ecf20Sopenharmony_ci if (!pll) 1118c2ecf20Sopenharmony_ci return ERR_PTR(-ENOMEM); 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_ci pll->membase = ctx->membase; 1148c2ecf20Sopenharmony_ci pll->reg = list->reg; 1158c2ecf20Sopenharmony_ci pll->flags = list->flags; 1168c2ecf20Sopenharmony_ci pll->type = list->type; 1178c2ecf20Sopenharmony_ci pll->hw.init = &init; 1188c2ecf20Sopenharmony_ci 1198c2ecf20Sopenharmony_ci hw = &pll->hw; 1208c2ecf20Sopenharmony_ci ret = devm_clk_hw_register(dev, hw); 1218c2ecf20Sopenharmony_ci if (ret) 1228c2ecf20Sopenharmony_ci return ERR_PTR(ret); 1238c2ecf20Sopenharmony_ci 1248c2ecf20Sopenharmony_ci return hw; 1258c2ecf20Sopenharmony_ci} 1268c2ecf20Sopenharmony_ci 1278c2ecf20Sopenharmony_ciint lgm_clk_register_plls(struct lgm_clk_provider *ctx, 1288c2ecf20Sopenharmony_ci const struct lgm_pll_clk_data *list, 1298c2ecf20Sopenharmony_ci unsigned int nr_clk) 1308c2ecf20Sopenharmony_ci{ 1318c2ecf20Sopenharmony_ci struct clk_hw *hw; 1328c2ecf20Sopenharmony_ci int i; 1338c2ecf20Sopenharmony_ci 1348c2ecf20Sopenharmony_ci for (i = 0; i < nr_clk; i++, list++) { 1358c2ecf20Sopenharmony_ci hw = lgm_clk_register_pll(ctx, list); 1368c2ecf20Sopenharmony_ci if (IS_ERR(hw)) { 1378c2ecf20Sopenharmony_ci dev_err(ctx->dev, "failed to register pll: %s\n", 1388c2ecf20Sopenharmony_ci list->name); 1398c2ecf20Sopenharmony_ci return PTR_ERR(hw); 1408c2ecf20Sopenharmony_ci } 1418c2ecf20Sopenharmony_ci ctx->clk_data.hws[list->id] = hw; 1428c2ecf20Sopenharmony_ci } 1438c2ecf20Sopenharmony_ci 1448c2ecf20Sopenharmony_ci return 0; 1458c2ecf20Sopenharmony_ci} 146