18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * MMP PLL clock rate calculation 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2020 Lubomir Rintel <lkundrak@v3.sk> 68c2ecf20Sopenharmony_ci */ 78c2ecf20Sopenharmony_ci 88c2ecf20Sopenharmony_ci#include <linux/clk-provider.h> 98c2ecf20Sopenharmony_ci#include <linux/slab.h> 108c2ecf20Sopenharmony_ci#include <linux/io.h> 118c2ecf20Sopenharmony_ci 128c2ecf20Sopenharmony_ci#include "clk.h" 138c2ecf20Sopenharmony_ci 148c2ecf20Sopenharmony_ci#define to_clk_mmp_pll(hw) container_of(hw, struct mmp_clk_pll, hw) 158c2ecf20Sopenharmony_ci 168c2ecf20Sopenharmony_cistruct mmp_clk_pll { 178c2ecf20Sopenharmony_ci struct clk_hw hw; 188c2ecf20Sopenharmony_ci unsigned long default_rate; 198c2ecf20Sopenharmony_ci void __iomem *enable_reg; 208c2ecf20Sopenharmony_ci u32 enable; 218c2ecf20Sopenharmony_ci void __iomem *reg; 228c2ecf20Sopenharmony_ci u8 shift; 238c2ecf20Sopenharmony_ci 248c2ecf20Sopenharmony_ci unsigned long input_rate; 258c2ecf20Sopenharmony_ci void __iomem *postdiv_reg; 268c2ecf20Sopenharmony_ci u8 postdiv_shift; 278c2ecf20Sopenharmony_ci}; 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_cistatic int mmp_clk_pll_is_enabled(struct clk_hw *hw) 308c2ecf20Sopenharmony_ci{ 318c2ecf20Sopenharmony_ci struct mmp_clk_pll *pll = to_clk_mmp_pll(hw); 328c2ecf20Sopenharmony_ci u32 val; 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_ci val = readl_relaxed(pll->enable_reg); 358c2ecf20Sopenharmony_ci if ((val & pll->enable) == pll->enable) 368c2ecf20Sopenharmony_ci return 1; 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_ci /* Some PLLs, if not software controlled, output default clock. */ 398c2ecf20Sopenharmony_ci if (pll->default_rate > 0) 408c2ecf20Sopenharmony_ci return 1; 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_ci return 0; 438c2ecf20Sopenharmony_ci} 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_cistatic unsigned long mmp_clk_pll_recalc_rate(struct clk_hw *hw, 468c2ecf20Sopenharmony_ci unsigned long parent_rate) 478c2ecf20Sopenharmony_ci{ 488c2ecf20Sopenharmony_ci struct mmp_clk_pll *pll = to_clk_mmp_pll(hw); 498c2ecf20Sopenharmony_ci u32 fbdiv, refdiv, postdiv; 508c2ecf20Sopenharmony_ci u64 rate; 518c2ecf20Sopenharmony_ci u32 val; 528c2ecf20Sopenharmony_ci 538c2ecf20Sopenharmony_ci val = readl_relaxed(pll->enable_reg); 548c2ecf20Sopenharmony_ci if ((val & pll->enable) != pll->enable) 558c2ecf20Sopenharmony_ci return pll->default_rate; 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_ci if (pll->reg) { 588c2ecf20Sopenharmony_ci val = readl_relaxed(pll->reg); 598c2ecf20Sopenharmony_ci fbdiv = (val >> pll->shift) & 0x1ff; 608c2ecf20Sopenharmony_ci refdiv = (val >> (pll->shift + 9)) & 0x1f; 618c2ecf20Sopenharmony_ci } else { 628c2ecf20Sopenharmony_ci fbdiv = 2; 638c2ecf20Sopenharmony_ci refdiv = 1; 648c2ecf20Sopenharmony_ci } 658c2ecf20Sopenharmony_ci 668c2ecf20Sopenharmony_ci if (pll->postdiv_reg) { 678c2ecf20Sopenharmony_ci /* MMP3 clock rate calculation */ 688c2ecf20Sopenharmony_ci static const u8 postdivs[] = {2, 3, 4, 5, 6, 8, 10, 12, 16}; 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_ci val = readl_relaxed(pll->postdiv_reg); 718c2ecf20Sopenharmony_ci postdiv = (val >> pll->postdiv_shift) & 0x7; 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_ci rate = pll->input_rate; 748c2ecf20Sopenharmony_ci rate *= 2 * fbdiv; 758c2ecf20Sopenharmony_ci do_div(rate, refdiv); 768c2ecf20Sopenharmony_ci do_div(rate, postdivs[postdiv]); 778c2ecf20Sopenharmony_ci } else { 788c2ecf20Sopenharmony_ci /* MMP2 clock rate calculation */ 798c2ecf20Sopenharmony_ci if (refdiv == 3) { 808c2ecf20Sopenharmony_ci rate = 19200000; 818c2ecf20Sopenharmony_ci } else if (refdiv == 4) { 828c2ecf20Sopenharmony_ci rate = 26000000; 838c2ecf20Sopenharmony_ci } else { 848c2ecf20Sopenharmony_ci pr_err("bad refdiv: %d (0x%08x)\n", refdiv, val); 858c2ecf20Sopenharmony_ci return 0; 868c2ecf20Sopenharmony_ci } 878c2ecf20Sopenharmony_ci 888c2ecf20Sopenharmony_ci rate *= fbdiv + 2; 898c2ecf20Sopenharmony_ci do_div(rate, refdiv + 2); 908c2ecf20Sopenharmony_ci } 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_ci return (unsigned long)rate; 938c2ecf20Sopenharmony_ci} 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_cistatic const struct clk_ops mmp_clk_pll_ops = { 968c2ecf20Sopenharmony_ci .is_enabled = mmp_clk_pll_is_enabled, 978c2ecf20Sopenharmony_ci .recalc_rate = mmp_clk_pll_recalc_rate, 988c2ecf20Sopenharmony_ci}; 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_cistatic struct clk *mmp_clk_register_pll(char *name, 1018c2ecf20Sopenharmony_ci unsigned long default_rate, 1028c2ecf20Sopenharmony_ci void __iomem *enable_reg, u32 enable, 1038c2ecf20Sopenharmony_ci void __iomem *reg, u8 shift, 1048c2ecf20Sopenharmony_ci unsigned long input_rate, 1058c2ecf20Sopenharmony_ci void __iomem *postdiv_reg, u8 postdiv_shift) 1068c2ecf20Sopenharmony_ci{ 1078c2ecf20Sopenharmony_ci struct mmp_clk_pll *pll; 1088c2ecf20Sopenharmony_ci struct clk *clk; 1098c2ecf20Sopenharmony_ci struct clk_init_data init; 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_ci pll = kzalloc(sizeof(*pll), GFP_KERNEL); 1128c2ecf20Sopenharmony_ci if (!pll) 1138c2ecf20Sopenharmony_ci return ERR_PTR(-ENOMEM); 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_ci init.name = name; 1168c2ecf20Sopenharmony_ci init.ops = &mmp_clk_pll_ops; 1178c2ecf20Sopenharmony_ci init.flags = 0; 1188c2ecf20Sopenharmony_ci init.parent_names = NULL; 1198c2ecf20Sopenharmony_ci init.num_parents = 0; 1208c2ecf20Sopenharmony_ci 1218c2ecf20Sopenharmony_ci pll->default_rate = default_rate; 1228c2ecf20Sopenharmony_ci pll->enable_reg = enable_reg; 1238c2ecf20Sopenharmony_ci pll->enable = enable; 1248c2ecf20Sopenharmony_ci pll->reg = reg; 1258c2ecf20Sopenharmony_ci pll->shift = shift; 1268c2ecf20Sopenharmony_ci 1278c2ecf20Sopenharmony_ci pll->input_rate = input_rate; 1288c2ecf20Sopenharmony_ci pll->postdiv_reg = postdiv_reg; 1298c2ecf20Sopenharmony_ci pll->postdiv_shift = postdiv_shift; 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_ci pll->hw.init = &init; 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_ci clk = clk_register(NULL, &pll->hw); 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_ci if (IS_ERR(clk)) 1368c2ecf20Sopenharmony_ci kfree(pll); 1378c2ecf20Sopenharmony_ci 1388c2ecf20Sopenharmony_ci return clk; 1398c2ecf20Sopenharmony_ci} 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_civoid mmp_register_pll_clks(struct mmp_clk_unit *unit, 1428c2ecf20Sopenharmony_ci struct mmp_param_pll_clk *clks, 1438c2ecf20Sopenharmony_ci void __iomem *base, int size) 1448c2ecf20Sopenharmony_ci{ 1458c2ecf20Sopenharmony_ci struct clk *clk; 1468c2ecf20Sopenharmony_ci int i; 1478c2ecf20Sopenharmony_ci 1488c2ecf20Sopenharmony_ci for (i = 0; i < size; i++) { 1498c2ecf20Sopenharmony_ci void __iomem *reg = NULL; 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_ci if (clks[i].offset) 1528c2ecf20Sopenharmony_ci reg = base + clks[i].offset; 1538c2ecf20Sopenharmony_ci 1548c2ecf20Sopenharmony_ci clk = mmp_clk_register_pll(clks[i].name, 1558c2ecf20Sopenharmony_ci clks[i].default_rate, 1568c2ecf20Sopenharmony_ci base + clks[i].enable_offset, 1578c2ecf20Sopenharmony_ci clks[i].enable, 1588c2ecf20Sopenharmony_ci reg, clks[i].shift, 1598c2ecf20Sopenharmony_ci clks[i].input_rate, 1608c2ecf20Sopenharmony_ci base + clks[i].postdiv_offset, 1618c2ecf20Sopenharmony_ci clks[i].postdiv_shift); 1628c2ecf20Sopenharmony_ci if (IS_ERR(clk)) { 1638c2ecf20Sopenharmony_ci pr_err("%s: failed to register clock %s\n", 1648c2ecf20Sopenharmony_ci __func__, clks[i].name); 1658c2ecf20Sopenharmony_ci continue; 1668c2ecf20Sopenharmony_ci } 1678c2ecf20Sopenharmony_ci if (clks[i].id) 1688c2ecf20Sopenharmony_ci unit->clk_table[clks[i].id] = clk; 1698c2ecf20Sopenharmony_ci } 1708c2ecf20Sopenharmony_ci} 171