18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright (c) 2014 Marvell Technology Group Ltd. 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Sebastian Hesselbarth <sebastian.hesselbarth@gmail.com> 68c2ecf20Sopenharmony_ci * Alexandre Belloni <alexandre.belloni@free-electrons.com> 78c2ecf20Sopenharmony_ci */ 88c2ecf20Sopenharmony_ci#include <linux/clk-provider.h> 98c2ecf20Sopenharmony_ci#include <linux/io.h> 108c2ecf20Sopenharmony_ci#include <linux/kernel.h> 118c2ecf20Sopenharmony_ci#include <linux/of.h> 128c2ecf20Sopenharmony_ci#include <linux/of_address.h> 138c2ecf20Sopenharmony_ci#include <linux/slab.h> 148c2ecf20Sopenharmony_ci 158c2ecf20Sopenharmony_ci#include "berlin2-avpll.h" 168c2ecf20Sopenharmony_ci 178c2ecf20Sopenharmony_ci/* 188c2ecf20Sopenharmony_ci * Berlin2 SoCs comprise up to two PLLs called AVPLL built upon a 198c2ecf20Sopenharmony_ci * VCO with 8 channels each, channel 8 is the odd-one-out and does 208c2ecf20Sopenharmony_ci * not provide mul/div. 218c2ecf20Sopenharmony_ci * 228c2ecf20Sopenharmony_ci * Unfortunately, its registers are not named but just numbered. To 238c2ecf20Sopenharmony_ci * get in at least some kind of structure, we split each AVPLL into 248c2ecf20Sopenharmony_ci * the VCOs and each channel into separate clock drivers. 258c2ecf20Sopenharmony_ci * 268c2ecf20Sopenharmony_ci * Also, here and there the VCO registers are a bit different with 278c2ecf20Sopenharmony_ci * respect to bit shifts. Make sure to add a comment for those. 288c2ecf20Sopenharmony_ci */ 298c2ecf20Sopenharmony_ci#define NUM_CHANNELS 8 308c2ecf20Sopenharmony_ci 318c2ecf20Sopenharmony_ci#define AVPLL_CTRL(x) ((x) * 0x4) 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_ci#define VCO_CTRL0 AVPLL_CTRL(0) 348c2ecf20Sopenharmony_ci/* BG2/BG2CDs VCO_B has an additional shift of 4 for its VCO_CTRL0 reg */ 358c2ecf20Sopenharmony_ci#define VCO_RESET BIT(0) 368c2ecf20Sopenharmony_ci#define VCO_POWERUP BIT(1) 378c2ecf20Sopenharmony_ci#define VCO_INTERPOL_SHIFT 2 388c2ecf20Sopenharmony_ci#define VCO_INTERPOL_MASK (0xf << VCO_INTERPOL_SHIFT) 398c2ecf20Sopenharmony_ci#define VCO_REG1V45_SEL_SHIFT 6 408c2ecf20Sopenharmony_ci#define VCO_REG1V45_SEL(x) ((x) << VCO_REG1V45_SEL_SHIFT) 418c2ecf20Sopenharmony_ci#define VCO_REG1V45_SEL_1V40 VCO_REG1V45_SEL(0) 428c2ecf20Sopenharmony_ci#define VCO_REG1V45_SEL_1V45 VCO_REG1V45_SEL(1) 438c2ecf20Sopenharmony_ci#define VCO_REG1V45_SEL_1V50 VCO_REG1V45_SEL(2) 448c2ecf20Sopenharmony_ci#define VCO_REG1V45_SEL_1V55 VCO_REG1V45_SEL(3) 458c2ecf20Sopenharmony_ci#define VCO_REG1V45_SEL_MASK VCO_REG1V45_SEL(3) 468c2ecf20Sopenharmony_ci#define VCO_REG0V9_SEL_SHIFT 8 478c2ecf20Sopenharmony_ci#define VCO_REG0V9_SEL_MASK (0xf << VCO_REG0V9_SEL_SHIFT) 488c2ecf20Sopenharmony_ci#define VCO_VTHCAL_SHIFT 12 498c2ecf20Sopenharmony_ci#define VCO_VTHCAL(x) ((x) << VCO_VTHCAL_SHIFT) 508c2ecf20Sopenharmony_ci#define VCO_VTHCAL_0V90 VCO_VTHCAL(0) 518c2ecf20Sopenharmony_ci#define VCO_VTHCAL_0V95 VCO_VTHCAL(1) 528c2ecf20Sopenharmony_ci#define VCO_VTHCAL_1V00 VCO_VTHCAL(2) 538c2ecf20Sopenharmony_ci#define VCO_VTHCAL_1V05 VCO_VTHCAL(3) 548c2ecf20Sopenharmony_ci#define VCO_VTHCAL_MASK VCO_VTHCAL(3) 558c2ecf20Sopenharmony_ci#define VCO_KVCOEXT_SHIFT 14 568c2ecf20Sopenharmony_ci#define VCO_KVCOEXT_MASK (0x3 << VCO_KVCOEXT_SHIFT) 578c2ecf20Sopenharmony_ci#define VCO_KVCOEXT_ENABLE BIT(17) 588c2ecf20Sopenharmony_ci#define VCO_V2IEXT_SHIFT 18 598c2ecf20Sopenharmony_ci#define VCO_V2IEXT_MASK (0xf << VCO_V2IEXT_SHIFT) 608c2ecf20Sopenharmony_ci#define VCO_V2IEXT_ENABLE BIT(22) 618c2ecf20Sopenharmony_ci#define VCO_SPEED_SHIFT 23 628c2ecf20Sopenharmony_ci#define VCO_SPEED(x) ((x) << VCO_SPEED_SHIFT) 638c2ecf20Sopenharmony_ci#define VCO_SPEED_1G08_1G21 VCO_SPEED(0) 648c2ecf20Sopenharmony_ci#define VCO_SPEED_1G21_1G40 VCO_SPEED(1) 658c2ecf20Sopenharmony_ci#define VCO_SPEED_1G40_1G61 VCO_SPEED(2) 668c2ecf20Sopenharmony_ci#define VCO_SPEED_1G61_1G86 VCO_SPEED(3) 678c2ecf20Sopenharmony_ci#define VCO_SPEED_1G86_2G00 VCO_SPEED(4) 688c2ecf20Sopenharmony_ci#define VCO_SPEED_2G00_2G22 VCO_SPEED(5) 698c2ecf20Sopenharmony_ci#define VCO_SPEED_2G22 VCO_SPEED(6) 708c2ecf20Sopenharmony_ci#define VCO_SPEED_MASK VCO_SPEED(0x7) 718c2ecf20Sopenharmony_ci#define VCO_CLKDET_ENABLE BIT(26) 728c2ecf20Sopenharmony_ci#define VCO_CTRL1 AVPLL_CTRL(1) 738c2ecf20Sopenharmony_ci#define VCO_REFDIV_SHIFT 0 748c2ecf20Sopenharmony_ci#define VCO_REFDIV(x) ((x) << VCO_REFDIV_SHIFT) 758c2ecf20Sopenharmony_ci#define VCO_REFDIV_1 VCO_REFDIV(0) 768c2ecf20Sopenharmony_ci#define VCO_REFDIV_2 VCO_REFDIV(1) 778c2ecf20Sopenharmony_ci#define VCO_REFDIV_4 VCO_REFDIV(2) 788c2ecf20Sopenharmony_ci#define VCO_REFDIV_3 VCO_REFDIV(3) 798c2ecf20Sopenharmony_ci#define VCO_REFDIV_MASK VCO_REFDIV(0x3f) 808c2ecf20Sopenharmony_ci#define VCO_FBDIV_SHIFT 6 818c2ecf20Sopenharmony_ci#define VCO_FBDIV(x) ((x) << VCO_FBDIV_SHIFT) 828c2ecf20Sopenharmony_ci#define VCO_FBDIV_MASK VCO_FBDIV(0xff) 838c2ecf20Sopenharmony_ci#define VCO_ICP_SHIFT 14 848c2ecf20Sopenharmony_ci/* PLL Charge Pump Current = 10uA * (x + 1) */ 858c2ecf20Sopenharmony_ci#define VCO_ICP(x) ((x) << VCO_ICP_SHIFT) 868c2ecf20Sopenharmony_ci#define VCO_ICP_MASK VCO_ICP(0xf) 878c2ecf20Sopenharmony_ci#define VCO_LOAD_CAP BIT(18) 888c2ecf20Sopenharmony_ci#define VCO_CALIBRATION_START BIT(19) 898c2ecf20Sopenharmony_ci#define VCO_FREQOFFSETn(x) AVPLL_CTRL(3 + (x)) 908c2ecf20Sopenharmony_ci#define VCO_FREQOFFSET_MASK 0x7ffff 918c2ecf20Sopenharmony_ci#define VCO_CTRL10 AVPLL_CTRL(10) 928c2ecf20Sopenharmony_ci#define VCO_POWERUP_CH1 BIT(20) 938c2ecf20Sopenharmony_ci#define VCO_CTRL11 AVPLL_CTRL(11) 948c2ecf20Sopenharmony_ci#define VCO_CTRL12 AVPLL_CTRL(12) 958c2ecf20Sopenharmony_ci#define VCO_CTRL13 AVPLL_CTRL(13) 968c2ecf20Sopenharmony_ci#define VCO_CTRL14 AVPLL_CTRL(14) 978c2ecf20Sopenharmony_ci#define VCO_CTRL15 AVPLL_CTRL(15) 988c2ecf20Sopenharmony_ci#define VCO_SYNC1n(x) AVPLL_CTRL(15 + (x)) 998c2ecf20Sopenharmony_ci#define VCO_SYNC1_MASK 0x1ffff 1008c2ecf20Sopenharmony_ci#define VCO_SYNC2n(x) AVPLL_CTRL(23 + (x)) 1018c2ecf20Sopenharmony_ci#define VCO_SYNC2_MASK 0x1ffff 1028c2ecf20Sopenharmony_ci#define VCO_CTRL30 AVPLL_CTRL(30) 1038c2ecf20Sopenharmony_ci#define VCO_DPLL_CH1_ENABLE BIT(17) 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_cistruct berlin2_avpll_vco { 1068c2ecf20Sopenharmony_ci struct clk_hw hw; 1078c2ecf20Sopenharmony_ci void __iomem *base; 1088c2ecf20Sopenharmony_ci u8 flags; 1098c2ecf20Sopenharmony_ci}; 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_ci#define to_avpll_vco(hw) container_of(hw, struct berlin2_avpll_vco, hw) 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_cistatic int berlin2_avpll_vco_is_enabled(struct clk_hw *hw) 1148c2ecf20Sopenharmony_ci{ 1158c2ecf20Sopenharmony_ci struct berlin2_avpll_vco *vco = to_avpll_vco(hw); 1168c2ecf20Sopenharmony_ci u32 reg; 1178c2ecf20Sopenharmony_ci 1188c2ecf20Sopenharmony_ci reg = readl_relaxed(vco->base + VCO_CTRL0); 1198c2ecf20Sopenharmony_ci if (vco->flags & BERLIN2_AVPLL_BIT_QUIRK) 1208c2ecf20Sopenharmony_ci reg >>= 4; 1218c2ecf20Sopenharmony_ci 1228c2ecf20Sopenharmony_ci return !!(reg & VCO_POWERUP); 1238c2ecf20Sopenharmony_ci} 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_cistatic int berlin2_avpll_vco_enable(struct clk_hw *hw) 1268c2ecf20Sopenharmony_ci{ 1278c2ecf20Sopenharmony_ci struct berlin2_avpll_vco *vco = to_avpll_vco(hw); 1288c2ecf20Sopenharmony_ci u32 reg; 1298c2ecf20Sopenharmony_ci 1308c2ecf20Sopenharmony_ci reg = readl_relaxed(vco->base + VCO_CTRL0); 1318c2ecf20Sopenharmony_ci if (vco->flags & BERLIN2_AVPLL_BIT_QUIRK) 1328c2ecf20Sopenharmony_ci reg |= VCO_POWERUP << 4; 1338c2ecf20Sopenharmony_ci else 1348c2ecf20Sopenharmony_ci reg |= VCO_POWERUP; 1358c2ecf20Sopenharmony_ci writel_relaxed(reg, vco->base + VCO_CTRL0); 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_ci return 0; 1388c2ecf20Sopenharmony_ci} 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_cistatic void berlin2_avpll_vco_disable(struct clk_hw *hw) 1418c2ecf20Sopenharmony_ci{ 1428c2ecf20Sopenharmony_ci struct berlin2_avpll_vco *vco = to_avpll_vco(hw); 1438c2ecf20Sopenharmony_ci u32 reg; 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_ci reg = readl_relaxed(vco->base + VCO_CTRL0); 1468c2ecf20Sopenharmony_ci if (vco->flags & BERLIN2_AVPLL_BIT_QUIRK) 1478c2ecf20Sopenharmony_ci reg &= ~(VCO_POWERUP << 4); 1488c2ecf20Sopenharmony_ci else 1498c2ecf20Sopenharmony_ci reg &= ~VCO_POWERUP; 1508c2ecf20Sopenharmony_ci writel_relaxed(reg, vco->base + VCO_CTRL0); 1518c2ecf20Sopenharmony_ci} 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_cistatic u8 vco_refdiv[] = { 1, 2, 4, 3 }; 1548c2ecf20Sopenharmony_ci 1558c2ecf20Sopenharmony_cistatic unsigned long 1568c2ecf20Sopenharmony_ciberlin2_avpll_vco_recalc_rate(struct clk_hw *hw, unsigned long parent_rate) 1578c2ecf20Sopenharmony_ci{ 1588c2ecf20Sopenharmony_ci struct berlin2_avpll_vco *vco = to_avpll_vco(hw); 1598c2ecf20Sopenharmony_ci u32 reg, refdiv, fbdiv; 1608c2ecf20Sopenharmony_ci u64 freq = parent_rate; 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_ci /* AVPLL VCO frequency: Fvco = (Fref / refdiv) * fbdiv */ 1638c2ecf20Sopenharmony_ci reg = readl_relaxed(vco->base + VCO_CTRL1); 1648c2ecf20Sopenharmony_ci refdiv = (reg & VCO_REFDIV_MASK) >> VCO_REFDIV_SHIFT; 1658c2ecf20Sopenharmony_ci refdiv = vco_refdiv[refdiv]; 1668c2ecf20Sopenharmony_ci fbdiv = (reg & VCO_FBDIV_MASK) >> VCO_FBDIV_SHIFT; 1678c2ecf20Sopenharmony_ci freq *= fbdiv; 1688c2ecf20Sopenharmony_ci do_div(freq, refdiv); 1698c2ecf20Sopenharmony_ci 1708c2ecf20Sopenharmony_ci return (unsigned long)freq; 1718c2ecf20Sopenharmony_ci} 1728c2ecf20Sopenharmony_ci 1738c2ecf20Sopenharmony_cistatic const struct clk_ops berlin2_avpll_vco_ops = { 1748c2ecf20Sopenharmony_ci .is_enabled = berlin2_avpll_vco_is_enabled, 1758c2ecf20Sopenharmony_ci .enable = berlin2_avpll_vco_enable, 1768c2ecf20Sopenharmony_ci .disable = berlin2_avpll_vco_disable, 1778c2ecf20Sopenharmony_ci .recalc_rate = berlin2_avpll_vco_recalc_rate, 1788c2ecf20Sopenharmony_ci}; 1798c2ecf20Sopenharmony_ci 1808c2ecf20Sopenharmony_ciint __init berlin2_avpll_vco_register(void __iomem *base, 1818c2ecf20Sopenharmony_ci const char *name, const char *parent_name, 1828c2ecf20Sopenharmony_ci u8 vco_flags, unsigned long flags) 1838c2ecf20Sopenharmony_ci{ 1848c2ecf20Sopenharmony_ci struct berlin2_avpll_vco *vco; 1858c2ecf20Sopenharmony_ci struct clk_init_data init; 1868c2ecf20Sopenharmony_ci 1878c2ecf20Sopenharmony_ci vco = kzalloc(sizeof(*vco), GFP_KERNEL); 1888c2ecf20Sopenharmony_ci if (!vco) 1898c2ecf20Sopenharmony_ci return -ENOMEM; 1908c2ecf20Sopenharmony_ci 1918c2ecf20Sopenharmony_ci vco->base = base; 1928c2ecf20Sopenharmony_ci vco->flags = vco_flags; 1938c2ecf20Sopenharmony_ci vco->hw.init = &init; 1948c2ecf20Sopenharmony_ci init.name = name; 1958c2ecf20Sopenharmony_ci init.ops = &berlin2_avpll_vco_ops; 1968c2ecf20Sopenharmony_ci init.parent_names = &parent_name; 1978c2ecf20Sopenharmony_ci init.num_parents = 1; 1988c2ecf20Sopenharmony_ci init.flags = flags; 1998c2ecf20Sopenharmony_ci 2008c2ecf20Sopenharmony_ci return clk_hw_register(NULL, &vco->hw); 2018c2ecf20Sopenharmony_ci} 2028c2ecf20Sopenharmony_ci 2038c2ecf20Sopenharmony_cistruct berlin2_avpll_channel { 2048c2ecf20Sopenharmony_ci struct clk_hw hw; 2058c2ecf20Sopenharmony_ci void __iomem *base; 2068c2ecf20Sopenharmony_ci u8 flags; 2078c2ecf20Sopenharmony_ci u8 index; 2088c2ecf20Sopenharmony_ci}; 2098c2ecf20Sopenharmony_ci 2108c2ecf20Sopenharmony_ci#define to_avpll_channel(hw) container_of(hw, struct berlin2_avpll_channel, hw) 2118c2ecf20Sopenharmony_ci 2128c2ecf20Sopenharmony_cistatic int berlin2_avpll_channel_is_enabled(struct clk_hw *hw) 2138c2ecf20Sopenharmony_ci{ 2148c2ecf20Sopenharmony_ci struct berlin2_avpll_channel *ch = to_avpll_channel(hw); 2158c2ecf20Sopenharmony_ci u32 reg; 2168c2ecf20Sopenharmony_ci 2178c2ecf20Sopenharmony_ci if (ch->index == 7) 2188c2ecf20Sopenharmony_ci return 1; 2198c2ecf20Sopenharmony_ci 2208c2ecf20Sopenharmony_ci reg = readl_relaxed(ch->base + VCO_CTRL10); 2218c2ecf20Sopenharmony_ci reg &= VCO_POWERUP_CH1 << ch->index; 2228c2ecf20Sopenharmony_ci 2238c2ecf20Sopenharmony_ci return !!reg; 2248c2ecf20Sopenharmony_ci} 2258c2ecf20Sopenharmony_ci 2268c2ecf20Sopenharmony_cistatic int berlin2_avpll_channel_enable(struct clk_hw *hw) 2278c2ecf20Sopenharmony_ci{ 2288c2ecf20Sopenharmony_ci struct berlin2_avpll_channel *ch = to_avpll_channel(hw); 2298c2ecf20Sopenharmony_ci u32 reg; 2308c2ecf20Sopenharmony_ci 2318c2ecf20Sopenharmony_ci reg = readl_relaxed(ch->base + VCO_CTRL10); 2328c2ecf20Sopenharmony_ci reg |= VCO_POWERUP_CH1 << ch->index; 2338c2ecf20Sopenharmony_ci writel_relaxed(reg, ch->base + VCO_CTRL10); 2348c2ecf20Sopenharmony_ci 2358c2ecf20Sopenharmony_ci return 0; 2368c2ecf20Sopenharmony_ci} 2378c2ecf20Sopenharmony_ci 2388c2ecf20Sopenharmony_cistatic void berlin2_avpll_channel_disable(struct clk_hw *hw) 2398c2ecf20Sopenharmony_ci{ 2408c2ecf20Sopenharmony_ci struct berlin2_avpll_channel *ch = to_avpll_channel(hw); 2418c2ecf20Sopenharmony_ci u32 reg; 2428c2ecf20Sopenharmony_ci 2438c2ecf20Sopenharmony_ci reg = readl_relaxed(ch->base + VCO_CTRL10); 2448c2ecf20Sopenharmony_ci reg &= ~(VCO_POWERUP_CH1 << ch->index); 2458c2ecf20Sopenharmony_ci writel_relaxed(reg, ch->base + VCO_CTRL10); 2468c2ecf20Sopenharmony_ci} 2478c2ecf20Sopenharmony_ci 2488c2ecf20Sopenharmony_cistatic const u8 div_hdmi[] = { 1, 2, 4, 6 }; 2498c2ecf20Sopenharmony_cistatic const u8 div_av1[] = { 1, 2, 5, 5 }; 2508c2ecf20Sopenharmony_ci 2518c2ecf20Sopenharmony_cistatic unsigned long 2528c2ecf20Sopenharmony_ciberlin2_avpll_channel_recalc_rate(struct clk_hw *hw, unsigned long parent_rate) 2538c2ecf20Sopenharmony_ci{ 2548c2ecf20Sopenharmony_ci struct berlin2_avpll_channel *ch = to_avpll_channel(hw); 2558c2ecf20Sopenharmony_ci u32 reg, div_av2, div_av3, divider = 1; 2568c2ecf20Sopenharmony_ci u64 freq = parent_rate; 2578c2ecf20Sopenharmony_ci 2588c2ecf20Sopenharmony_ci reg = readl_relaxed(ch->base + VCO_CTRL30); 2598c2ecf20Sopenharmony_ci if ((reg & (VCO_DPLL_CH1_ENABLE << ch->index)) == 0) 2608c2ecf20Sopenharmony_ci goto skip_div; 2618c2ecf20Sopenharmony_ci 2628c2ecf20Sopenharmony_ci /* 2638c2ecf20Sopenharmony_ci * Fch = (Fref * sync2) / 2648c2ecf20Sopenharmony_ci * (sync1 * div_hdmi * div_av1 * div_av2 * div_av3) 2658c2ecf20Sopenharmony_ci */ 2668c2ecf20Sopenharmony_ci 2678c2ecf20Sopenharmony_ci reg = readl_relaxed(ch->base + VCO_SYNC1n(ch->index)); 2688c2ecf20Sopenharmony_ci /* BG2/BG2CDs SYNC1 reg on AVPLL_B channel 1 is shifted by 4 */ 2698c2ecf20Sopenharmony_ci if (ch->flags & BERLIN2_AVPLL_BIT_QUIRK && ch->index == 0) 2708c2ecf20Sopenharmony_ci reg >>= 4; 2718c2ecf20Sopenharmony_ci divider = reg & VCO_SYNC1_MASK; 2728c2ecf20Sopenharmony_ci 2738c2ecf20Sopenharmony_ci reg = readl_relaxed(ch->base + VCO_SYNC2n(ch->index)); 2748c2ecf20Sopenharmony_ci freq *= reg & VCO_SYNC2_MASK; 2758c2ecf20Sopenharmony_ci 2768c2ecf20Sopenharmony_ci /* Channel 8 has no dividers */ 2778c2ecf20Sopenharmony_ci if (ch->index == 7) 2788c2ecf20Sopenharmony_ci goto skip_div; 2798c2ecf20Sopenharmony_ci 2808c2ecf20Sopenharmony_ci /* 2818c2ecf20Sopenharmony_ci * HDMI divider start at VCO_CTRL11, bit 7; MSB is enable, lower 2 bit 2828c2ecf20Sopenharmony_ci * determine divider. 2838c2ecf20Sopenharmony_ci */ 2848c2ecf20Sopenharmony_ci reg = readl_relaxed(ch->base + VCO_CTRL11) >> 7; 2858c2ecf20Sopenharmony_ci reg = (reg >> (ch->index * 3)); 2868c2ecf20Sopenharmony_ci if (reg & BIT(2)) 2878c2ecf20Sopenharmony_ci divider *= div_hdmi[reg & 0x3]; 2888c2ecf20Sopenharmony_ci 2898c2ecf20Sopenharmony_ci /* 2908c2ecf20Sopenharmony_ci * AV1 divider start at VCO_CTRL11, bit 28; MSB is enable, lower 2 bit 2918c2ecf20Sopenharmony_ci * determine divider. 2928c2ecf20Sopenharmony_ci */ 2938c2ecf20Sopenharmony_ci if (ch->index == 0) { 2948c2ecf20Sopenharmony_ci reg = readl_relaxed(ch->base + VCO_CTRL11); 2958c2ecf20Sopenharmony_ci reg >>= 28; 2968c2ecf20Sopenharmony_ci } else { 2978c2ecf20Sopenharmony_ci reg = readl_relaxed(ch->base + VCO_CTRL12); 2988c2ecf20Sopenharmony_ci reg >>= (ch->index-1) * 3; 2998c2ecf20Sopenharmony_ci } 3008c2ecf20Sopenharmony_ci if (reg & BIT(2)) 3018c2ecf20Sopenharmony_ci divider *= div_av1[reg & 0x3]; 3028c2ecf20Sopenharmony_ci 3038c2ecf20Sopenharmony_ci /* 3048c2ecf20Sopenharmony_ci * AV2 divider start at VCO_CTRL12, bit 18; each 7 bits wide, 3058c2ecf20Sopenharmony_ci * zero is not a valid value. 3068c2ecf20Sopenharmony_ci */ 3078c2ecf20Sopenharmony_ci if (ch->index < 2) { 3088c2ecf20Sopenharmony_ci reg = readl_relaxed(ch->base + VCO_CTRL12); 3098c2ecf20Sopenharmony_ci reg >>= 18 + (ch->index * 7); 3108c2ecf20Sopenharmony_ci } else if (ch->index < 7) { 3118c2ecf20Sopenharmony_ci reg = readl_relaxed(ch->base + VCO_CTRL13); 3128c2ecf20Sopenharmony_ci reg >>= (ch->index - 2) * 7; 3138c2ecf20Sopenharmony_ci } else { 3148c2ecf20Sopenharmony_ci reg = readl_relaxed(ch->base + VCO_CTRL14); 3158c2ecf20Sopenharmony_ci } 3168c2ecf20Sopenharmony_ci div_av2 = reg & 0x7f; 3178c2ecf20Sopenharmony_ci if (div_av2) 3188c2ecf20Sopenharmony_ci divider *= div_av2; 3198c2ecf20Sopenharmony_ci 3208c2ecf20Sopenharmony_ci /* 3218c2ecf20Sopenharmony_ci * AV3 divider start at VCO_CTRL14, bit 7; each 4 bits wide. 3228c2ecf20Sopenharmony_ci * AV2/AV3 form a fractional divider, where only specfic values for AV3 3238c2ecf20Sopenharmony_ci * are allowed. AV3 != 0 divides by AV2/2, AV3=0 is bypass. 3248c2ecf20Sopenharmony_ci */ 3258c2ecf20Sopenharmony_ci if (ch->index < 6) { 3268c2ecf20Sopenharmony_ci reg = readl_relaxed(ch->base + VCO_CTRL14); 3278c2ecf20Sopenharmony_ci reg >>= 7 + (ch->index * 4); 3288c2ecf20Sopenharmony_ci } else { 3298c2ecf20Sopenharmony_ci reg = readl_relaxed(ch->base + VCO_CTRL15); 3308c2ecf20Sopenharmony_ci } 3318c2ecf20Sopenharmony_ci div_av3 = reg & 0xf; 3328c2ecf20Sopenharmony_ci if (div_av2 && div_av3) 3338c2ecf20Sopenharmony_ci freq *= 2; 3348c2ecf20Sopenharmony_ci 3358c2ecf20Sopenharmony_ciskip_div: 3368c2ecf20Sopenharmony_ci do_div(freq, divider); 3378c2ecf20Sopenharmony_ci return (unsigned long)freq; 3388c2ecf20Sopenharmony_ci} 3398c2ecf20Sopenharmony_ci 3408c2ecf20Sopenharmony_cistatic const struct clk_ops berlin2_avpll_channel_ops = { 3418c2ecf20Sopenharmony_ci .is_enabled = berlin2_avpll_channel_is_enabled, 3428c2ecf20Sopenharmony_ci .enable = berlin2_avpll_channel_enable, 3438c2ecf20Sopenharmony_ci .disable = berlin2_avpll_channel_disable, 3448c2ecf20Sopenharmony_ci .recalc_rate = berlin2_avpll_channel_recalc_rate, 3458c2ecf20Sopenharmony_ci}; 3468c2ecf20Sopenharmony_ci 3478c2ecf20Sopenharmony_ci/* 3488c2ecf20Sopenharmony_ci * Another nice quirk: 3498c2ecf20Sopenharmony_ci * On some production SoCs, AVPLL channels are scrambled with respect 3508c2ecf20Sopenharmony_ci * to the channel numbering in the registers but still referenced by 3518c2ecf20Sopenharmony_ci * their original channel numbers. We deal with it by having a flag 3528c2ecf20Sopenharmony_ci * and a translation table for the index. 3538c2ecf20Sopenharmony_ci */ 3548c2ecf20Sopenharmony_cistatic const u8 quirk_index[] __initconst = { 0, 6, 5, 4, 3, 2, 1, 7 }; 3558c2ecf20Sopenharmony_ci 3568c2ecf20Sopenharmony_ciint __init berlin2_avpll_channel_register(void __iomem *base, 3578c2ecf20Sopenharmony_ci const char *name, u8 index, const char *parent_name, 3588c2ecf20Sopenharmony_ci u8 ch_flags, unsigned long flags) 3598c2ecf20Sopenharmony_ci{ 3608c2ecf20Sopenharmony_ci struct berlin2_avpll_channel *ch; 3618c2ecf20Sopenharmony_ci struct clk_init_data init; 3628c2ecf20Sopenharmony_ci 3638c2ecf20Sopenharmony_ci ch = kzalloc(sizeof(*ch), GFP_KERNEL); 3648c2ecf20Sopenharmony_ci if (!ch) 3658c2ecf20Sopenharmony_ci return -ENOMEM; 3668c2ecf20Sopenharmony_ci 3678c2ecf20Sopenharmony_ci ch->base = base; 3688c2ecf20Sopenharmony_ci if (ch_flags & BERLIN2_AVPLL_SCRAMBLE_QUIRK) 3698c2ecf20Sopenharmony_ci ch->index = quirk_index[index]; 3708c2ecf20Sopenharmony_ci else 3718c2ecf20Sopenharmony_ci ch->index = index; 3728c2ecf20Sopenharmony_ci 3738c2ecf20Sopenharmony_ci ch->flags = ch_flags; 3748c2ecf20Sopenharmony_ci ch->hw.init = &init; 3758c2ecf20Sopenharmony_ci init.name = name; 3768c2ecf20Sopenharmony_ci init.ops = &berlin2_avpll_channel_ops; 3778c2ecf20Sopenharmony_ci init.parent_names = &parent_name; 3788c2ecf20Sopenharmony_ci init.num_parents = 1; 3798c2ecf20Sopenharmony_ci init.flags = flags; 3808c2ecf20Sopenharmony_ci 3818c2ecf20Sopenharmony_ci return clk_hw_register(NULL, &ch->hw); 3828c2ecf20Sopenharmony_ci} 383