162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (c) 2014 Marvell Technology Group Ltd. 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Sebastian Hesselbarth <sebastian.hesselbarth@gmail.com> 662306a36Sopenharmony_ci * Alexandre Belloni <alexandre.belloni@free-electrons.com> 762306a36Sopenharmony_ci */ 862306a36Sopenharmony_ci#include <linux/clk-provider.h> 962306a36Sopenharmony_ci#include <linux/io.h> 1062306a36Sopenharmony_ci#include <linux/kernel.h> 1162306a36Sopenharmony_ci#include <linux/of.h> 1262306a36Sopenharmony_ci#include <linux/of_address.h> 1362306a36Sopenharmony_ci#include <linux/slab.h> 1462306a36Sopenharmony_ci 1562306a36Sopenharmony_ci#include "berlin2-avpll.h" 1662306a36Sopenharmony_ci 1762306a36Sopenharmony_ci/* 1862306a36Sopenharmony_ci * Berlin2 SoCs comprise up to two PLLs called AVPLL built upon a 1962306a36Sopenharmony_ci * VCO with 8 channels each, channel 8 is the odd-one-out and does 2062306a36Sopenharmony_ci * not provide mul/div. 2162306a36Sopenharmony_ci * 2262306a36Sopenharmony_ci * Unfortunately, its registers are not named but just numbered. To 2362306a36Sopenharmony_ci * get in at least some kind of structure, we split each AVPLL into 2462306a36Sopenharmony_ci * the VCOs and each channel into separate clock drivers. 2562306a36Sopenharmony_ci * 2662306a36Sopenharmony_ci * Also, here and there the VCO registers are a bit different with 2762306a36Sopenharmony_ci * respect to bit shifts. Make sure to add a comment for those. 2862306a36Sopenharmony_ci */ 2962306a36Sopenharmony_ci#define NUM_CHANNELS 8 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_ci#define AVPLL_CTRL(x) ((x) * 0x4) 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_ci#define VCO_CTRL0 AVPLL_CTRL(0) 3462306a36Sopenharmony_ci/* BG2/BG2CDs VCO_B has an additional shift of 4 for its VCO_CTRL0 reg */ 3562306a36Sopenharmony_ci#define VCO_RESET BIT(0) 3662306a36Sopenharmony_ci#define VCO_POWERUP BIT(1) 3762306a36Sopenharmony_ci#define VCO_INTERPOL_SHIFT 2 3862306a36Sopenharmony_ci#define VCO_INTERPOL_MASK (0xf << VCO_INTERPOL_SHIFT) 3962306a36Sopenharmony_ci#define VCO_REG1V45_SEL_SHIFT 6 4062306a36Sopenharmony_ci#define VCO_REG1V45_SEL(x) ((x) << VCO_REG1V45_SEL_SHIFT) 4162306a36Sopenharmony_ci#define VCO_REG1V45_SEL_1V40 VCO_REG1V45_SEL(0) 4262306a36Sopenharmony_ci#define VCO_REG1V45_SEL_1V45 VCO_REG1V45_SEL(1) 4362306a36Sopenharmony_ci#define VCO_REG1V45_SEL_1V50 VCO_REG1V45_SEL(2) 4462306a36Sopenharmony_ci#define VCO_REG1V45_SEL_1V55 VCO_REG1V45_SEL(3) 4562306a36Sopenharmony_ci#define VCO_REG1V45_SEL_MASK VCO_REG1V45_SEL(3) 4662306a36Sopenharmony_ci#define VCO_REG0V9_SEL_SHIFT 8 4762306a36Sopenharmony_ci#define VCO_REG0V9_SEL_MASK (0xf << VCO_REG0V9_SEL_SHIFT) 4862306a36Sopenharmony_ci#define VCO_VTHCAL_SHIFT 12 4962306a36Sopenharmony_ci#define VCO_VTHCAL(x) ((x) << VCO_VTHCAL_SHIFT) 5062306a36Sopenharmony_ci#define VCO_VTHCAL_0V90 VCO_VTHCAL(0) 5162306a36Sopenharmony_ci#define VCO_VTHCAL_0V95 VCO_VTHCAL(1) 5262306a36Sopenharmony_ci#define VCO_VTHCAL_1V00 VCO_VTHCAL(2) 5362306a36Sopenharmony_ci#define VCO_VTHCAL_1V05 VCO_VTHCAL(3) 5462306a36Sopenharmony_ci#define VCO_VTHCAL_MASK VCO_VTHCAL(3) 5562306a36Sopenharmony_ci#define VCO_KVCOEXT_SHIFT 14 5662306a36Sopenharmony_ci#define VCO_KVCOEXT_MASK (0x3 << VCO_KVCOEXT_SHIFT) 5762306a36Sopenharmony_ci#define VCO_KVCOEXT_ENABLE BIT(17) 5862306a36Sopenharmony_ci#define VCO_V2IEXT_SHIFT 18 5962306a36Sopenharmony_ci#define VCO_V2IEXT_MASK (0xf << VCO_V2IEXT_SHIFT) 6062306a36Sopenharmony_ci#define VCO_V2IEXT_ENABLE BIT(22) 6162306a36Sopenharmony_ci#define VCO_SPEED_SHIFT 23 6262306a36Sopenharmony_ci#define VCO_SPEED(x) ((x) << VCO_SPEED_SHIFT) 6362306a36Sopenharmony_ci#define VCO_SPEED_1G08_1G21 VCO_SPEED(0) 6462306a36Sopenharmony_ci#define VCO_SPEED_1G21_1G40 VCO_SPEED(1) 6562306a36Sopenharmony_ci#define VCO_SPEED_1G40_1G61 VCO_SPEED(2) 6662306a36Sopenharmony_ci#define VCO_SPEED_1G61_1G86 VCO_SPEED(3) 6762306a36Sopenharmony_ci#define VCO_SPEED_1G86_2G00 VCO_SPEED(4) 6862306a36Sopenharmony_ci#define VCO_SPEED_2G00_2G22 VCO_SPEED(5) 6962306a36Sopenharmony_ci#define VCO_SPEED_2G22 VCO_SPEED(6) 7062306a36Sopenharmony_ci#define VCO_SPEED_MASK VCO_SPEED(0x7) 7162306a36Sopenharmony_ci#define VCO_CLKDET_ENABLE BIT(26) 7262306a36Sopenharmony_ci#define VCO_CTRL1 AVPLL_CTRL(1) 7362306a36Sopenharmony_ci#define VCO_REFDIV_SHIFT 0 7462306a36Sopenharmony_ci#define VCO_REFDIV(x) ((x) << VCO_REFDIV_SHIFT) 7562306a36Sopenharmony_ci#define VCO_REFDIV_1 VCO_REFDIV(0) 7662306a36Sopenharmony_ci#define VCO_REFDIV_2 VCO_REFDIV(1) 7762306a36Sopenharmony_ci#define VCO_REFDIV_4 VCO_REFDIV(2) 7862306a36Sopenharmony_ci#define VCO_REFDIV_3 VCO_REFDIV(3) 7962306a36Sopenharmony_ci#define VCO_REFDIV_MASK VCO_REFDIV(0x3f) 8062306a36Sopenharmony_ci#define VCO_FBDIV_SHIFT 6 8162306a36Sopenharmony_ci#define VCO_FBDIV(x) ((x) << VCO_FBDIV_SHIFT) 8262306a36Sopenharmony_ci#define VCO_FBDIV_MASK VCO_FBDIV(0xff) 8362306a36Sopenharmony_ci#define VCO_ICP_SHIFT 14 8462306a36Sopenharmony_ci/* PLL Charge Pump Current = 10uA * (x + 1) */ 8562306a36Sopenharmony_ci#define VCO_ICP(x) ((x) << VCO_ICP_SHIFT) 8662306a36Sopenharmony_ci#define VCO_ICP_MASK VCO_ICP(0xf) 8762306a36Sopenharmony_ci#define VCO_LOAD_CAP BIT(18) 8862306a36Sopenharmony_ci#define VCO_CALIBRATION_START BIT(19) 8962306a36Sopenharmony_ci#define VCO_FREQOFFSETn(x) AVPLL_CTRL(3 + (x)) 9062306a36Sopenharmony_ci#define VCO_FREQOFFSET_MASK 0x7ffff 9162306a36Sopenharmony_ci#define VCO_CTRL10 AVPLL_CTRL(10) 9262306a36Sopenharmony_ci#define VCO_POWERUP_CH1 BIT(20) 9362306a36Sopenharmony_ci#define VCO_CTRL11 AVPLL_CTRL(11) 9462306a36Sopenharmony_ci#define VCO_CTRL12 AVPLL_CTRL(12) 9562306a36Sopenharmony_ci#define VCO_CTRL13 AVPLL_CTRL(13) 9662306a36Sopenharmony_ci#define VCO_CTRL14 AVPLL_CTRL(14) 9762306a36Sopenharmony_ci#define VCO_CTRL15 AVPLL_CTRL(15) 9862306a36Sopenharmony_ci#define VCO_SYNC1n(x) AVPLL_CTRL(15 + (x)) 9962306a36Sopenharmony_ci#define VCO_SYNC1_MASK 0x1ffff 10062306a36Sopenharmony_ci#define VCO_SYNC2n(x) AVPLL_CTRL(23 + (x)) 10162306a36Sopenharmony_ci#define VCO_SYNC2_MASK 0x1ffff 10262306a36Sopenharmony_ci#define VCO_CTRL30 AVPLL_CTRL(30) 10362306a36Sopenharmony_ci#define VCO_DPLL_CH1_ENABLE BIT(17) 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_cistruct berlin2_avpll_vco { 10662306a36Sopenharmony_ci struct clk_hw hw; 10762306a36Sopenharmony_ci void __iomem *base; 10862306a36Sopenharmony_ci u8 flags; 10962306a36Sopenharmony_ci}; 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_ci#define to_avpll_vco(hw) container_of(hw, struct berlin2_avpll_vco, hw) 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_cistatic int berlin2_avpll_vco_is_enabled(struct clk_hw *hw) 11462306a36Sopenharmony_ci{ 11562306a36Sopenharmony_ci struct berlin2_avpll_vco *vco = to_avpll_vco(hw); 11662306a36Sopenharmony_ci u32 reg; 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_ci reg = readl_relaxed(vco->base + VCO_CTRL0); 11962306a36Sopenharmony_ci if (vco->flags & BERLIN2_AVPLL_BIT_QUIRK) 12062306a36Sopenharmony_ci reg >>= 4; 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_ci return !!(reg & VCO_POWERUP); 12362306a36Sopenharmony_ci} 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_cistatic int berlin2_avpll_vco_enable(struct clk_hw *hw) 12662306a36Sopenharmony_ci{ 12762306a36Sopenharmony_ci struct berlin2_avpll_vco *vco = to_avpll_vco(hw); 12862306a36Sopenharmony_ci u32 reg; 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_ci reg = readl_relaxed(vco->base + VCO_CTRL0); 13162306a36Sopenharmony_ci if (vco->flags & BERLIN2_AVPLL_BIT_QUIRK) 13262306a36Sopenharmony_ci reg |= VCO_POWERUP << 4; 13362306a36Sopenharmony_ci else 13462306a36Sopenharmony_ci reg |= VCO_POWERUP; 13562306a36Sopenharmony_ci writel_relaxed(reg, vco->base + VCO_CTRL0); 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_ci return 0; 13862306a36Sopenharmony_ci} 13962306a36Sopenharmony_ci 14062306a36Sopenharmony_cistatic void berlin2_avpll_vco_disable(struct clk_hw *hw) 14162306a36Sopenharmony_ci{ 14262306a36Sopenharmony_ci struct berlin2_avpll_vco *vco = to_avpll_vco(hw); 14362306a36Sopenharmony_ci u32 reg; 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_ci reg = readl_relaxed(vco->base + VCO_CTRL0); 14662306a36Sopenharmony_ci if (vco->flags & BERLIN2_AVPLL_BIT_QUIRK) 14762306a36Sopenharmony_ci reg &= ~(VCO_POWERUP << 4); 14862306a36Sopenharmony_ci else 14962306a36Sopenharmony_ci reg &= ~VCO_POWERUP; 15062306a36Sopenharmony_ci writel_relaxed(reg, vco->base + VCO_CTRL0); 15162306a36Sopenharmony_ci} 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_cistatic u8 vco_refdiv[] = { 1, 2, 4, 3 }; 15462306a36Sopenharmony_ci 15562306a36Sopenharmony_cistatic unsigned long 15662306a36Sopenharmony_ciberlin2_avpll_vco_recalc_rate(struct clk_hw *hw, unsigned long parent_rate) 15762306a36Sopenharmony_ci{ 15862306a36Sopenharmony_ci struct berlin2_avpll_vco *vco = to_avpll_vco(hw); 15962306a36Sopenharmony_ci u32 reg, refdiv, fbdiv; 16062306a36Sopenharmony_ci u64 freq = parent_rate; 16162306a36Sopenharmony_ci 16262306a36Sopenharmony_ci /* AVPLL VCO frequency: Fvco = (Fref / refdiv) * fbdiv */ 16362306a36Sopenharmony_ci reg = readl_relaxed(vco->base + VCO_CTRL1); 16462306a36Sopenharmony_ci refdiv = (reg & VCO_REFDIV_MASK) >> VCO_REFDIV_SHIFT; 16562306a36Sopenharmony_ci refdiv = vco_refdiv[refdiv]; 16662306a36Sopenharmony_ci fbdiv = (reg & VCO_FBDIV_MASK) >> VCO_FBDIV_SHIFT; 16762306a36Sopenharmony_ci freq *= fbdiv; 16862306a36Sopenharmony_ci do_div(freq, refdiv); 16962306a36Sopenharmony_ci 17062306a36Sopenharmony_ci return (unsigned long)freq; 17162306a36Sopenharmony_ci} 17262306a36Sopenharmony_ci 17362306a36Sopenharmony_cistatic const struct clk_ops berlin2_avpll_vco_ops = { 17462306a36Sopenharmony_ci .is_enabled = berlin2_avpll_vco_is_enabled, 17562306a36Sopenharmony_ci .enable = berlin2_avpll_vco_enable, 17662306a36Sopenharmony_ci .disable = berlin2_avpll_vco_disable, 17762306a36Sopenharmony_ci .recalc_rate = berlin2_avpll_vco_recalc_rate, 17862306a36Sopenharmony_ci}; 17962306a36Sopenharmony_ci 18062306a36Sopenharmony_ciint __init berlin2_avpll_vco_register(void __iomem *base, 18162306a36Sopenharmony_ci const char *name, const char *parent_name, 18262306a36Sopenharmony_ci u8 vco_flags, unsigned long flags) 18362306a36Sopenharmony_ci{ 18462306a36Sopenharmony_ci struct berlin2_avpll_vco *vco; 18562306a36Sopenharmony_ci struct clk_init_data init; 18662306a36Sopenharmony_ci 18762306a36Sopenharmony_ci vco = kzalloc(sizeof(*vco), GFP_KERNEL); 18862306a36Sopenharmony_ci if (!vco) 18962306a36Sopenharmony_ci return -ENOMEM; 19062306a36Sopenharmony_ci 19162306a36Sopenharmony_ci vco->base = base; 19262306a36Sopenharmony_ci vco->flags = vco_flags; 19362306a36Sopenharmony_ci vco->hw.init = &init; 19462306a36Sopenharmony_ci init.name = name; 19562306a36Sopenharmony_ci init.ops = &berlin2_avpll_vco_ops; 19662306a36Sopenharmony_ci init.parent_names = &parent_name; 19762306a36Sopenharmony_ci init.num_parents = 1; 19862306a36Sopenharmony_ci init.flags = flags; 19962306a36Sopenharmony_ci 20062306a36Sopenharmony_ci return clk_hw_register(NULL, &vco->hw); 20162306a36Sopenharmony_ci} 20262306a36Sopenharmony_ci 20362306a36Sopenharmony_cistruct berlin2_avpll_channel { 20462306a36Sopenharmony_ci struct clk_hw hw; 20562306a36Sopenharmony_ci void __iomem *base; 20662306a36Sopenharmony_ci u8 flags; 20762306a36Sopenharmony_ci u8 index; 20862306a36Sopenharmony_ci}; 20962306a36Sopenharmony_ci 21062306a36Sopenharmony_ci#define to_avpll_channel(hw) container_of(hw, struct berlin2_avpll_channel, hw) 21162306a36Sopenharmony_ci 21262306a36Sopenharmony_cistatic int berlin2_avpll_channel_is_enabled(struct clk_hw *hw) 21362306a36Sopenharmony_ci{ 21462306a36Sopenharmony_ci struct berlin2_avpll_channel *ch = to_avpll_channel(hw); 21562306a36Sopenharmony_ci u32 reg; 21662306a36Sopenharmony_ci 21762306a36Sopenharmony_ci if (ch->index == 7) 21862306a36Sopenharmony_ci return 1; 21962306a36Sopenharmony_ci 22062306a36Sopenharmony_ci reg = readl_relaxed(ch->base + VCO_CTRL10); 22162306a36Sopenharmony_ci reg &= VCO_POWERUP_CH1 << ch->index; 22262306a36Sopenharmony_ci 22362306a36Sopenharmony_ci return !!reg; 22462306a36Sopenharmony_ci} 22562306a36Sopenharmony_ci 22662306a36Sopenharmony_cistatic int berlin2_avpll_channel_enable(struct clk_hw *hw) 22762306a36Sopenharmony_ci{ 22862306a36Sopenharmony_ci struct berlin2_avpll_channel *ch = to_avpll_channel(hw); 22962306a36Sopenharmony_ci u32 reg; 23062306a36Sopenharmony_ci 23162306a36Sopenharmony_ci reg = readl_relaxed(ch->base + VCO_CTRL10); 23262306a36Sopenharmony_ci reg |= VCO_POWERUP_CH1 << ch->index; 23362306a36Sopenharmony_ci writel_relaxed(reg, ch->base + VCO_CTRL10); 23462306a36Sopenharmony_ci 23562306a36Sopenharmony_ci return 0; 23662306a36Sopenharmony_ci} 23762306a36Sopenharmony_ci 23862306a36Sopenharmony_cistatic void berlin2_avpll_channel_disable(struct clk_hw *hw) 23962306a36Sopenharmony_ci{ 24062306a36Sopenharmony_ci struct berlin2_avpll_channel *ch = to_avpll_channel(hw); 24162306a36Sopenharmony_ci u32 reg; 24262306a36Sopenharmony_ci 24362306a36Sopenharmony_ci reg = readl_relaxed(ch->base + VCO_CTRL10); 24462306a36Sopenharmony_ci reg &= ~(VCO_POWERUP_CH1 << ch->index); 24562306a36Sopenharmony_ci writel_relaxed(reg, ch->base + VCO_CTRL10); 24662306a36Sopenharmony_ci} 24762306a36Sopenharmony_ci 24862306a36Sopenharmony_cistatic const u8 div_hdmi[] = { 1, 2, 4, 6 }; 24962306a36Sopenharmony_cistatic const u8 div_av1[] = { 1, 2, 5, 5 }; 25062306a36Sopenharmony_ci 25162306a36Sopenharmony_cistatic unsigned long 25262306a36Sopenharmony_ciberlin2_avpll_channel_recalc_rate(struct clk_hw *hw, unsigned long parent_rate) 25362306a36Sopenharmony_ci{ 25462306a36Sopenharmony_ci struct berlin2_avpll_channel *ch = to_avpll_channel(hw); 25562306a36Sopenharmony_ci u32 reg, div_av2, div_av3, divider = 1; 25662306a36Sopenharmony_ci u64 freq = parent_rate; 25762306a36Sopenharmony_ci 25862306a36Sopenharmony_ci reg = readl_relaxed(ch->base + VCO_CTRL30); 25962306a36Sopenharmony_ci if ((reg & (VCO_DPLL_CH1_ENABLE << ch->index)) == 0) 26062306a36Sopenharmony_ci goto skip_div; 26162306a36Sopenharmony_ci 26262306a36Sopenharmony_ci /* 26362306a36Sopenharmony_ci * Fch = (Fref * sync2) / 26462306a36Sopenharmony_ci * (sync1 * div_hdmi * div_av1 * div_av2 * div_av3) 26562306a36Sopenharmony_ci */ 26662306a36Sopenharmony_ci 26762306a36Sopenharmony_ci reg = readl_relaxed(ch->base + VCO_SYNC1n(ch->index)); 26862306a36Sopenharmony_ci /* BG2/BG2CDs SYNC1 reg on AVPLL_B channel 1 is shifted by 4 */ 26962306a36Sopenharmony_ci if (ch->flags & BERLIN2_AVPLL_BIT_QUIRK && ch->index == 0) 27062306a36Sopenharmony_ci reg >>= 4; 27162306a36Sopenharmony_ci divider = reg & VCO_SYNC1_MASK; 27262306a36Sopenharmony_ci 27362306a36Sopenharmony_ci reg = readl_relaxed(ch->base + VCO_SYNC2n(ch->index)); 27462306a36Sopenharmony_ci freq *= reg & VCO_SYNC2_MASK; 27562306a36Sopenharmony_ci 27662306a36Sopenharmony_ci /* Channel 8 has no dividers */ 27762306a36Sopenharmony_ci if (ch->index == 7) 27862306a36Sopenharmony_ci goto skip_div; 27962306a36Sopenharmony_ci 28062306a36Sopenharmony_ci /* 28162306a36Sopenharmony_ci * HDMI divider start at VCO_CTRL11, bit 7; MSB is enable, lower 2 bit 28262306a36Sopenharmony_ci * determine divider. 28362306a36Sopenharmony_ci */ 28462306a36Sopenharmony_ci reg = readl_relaxed(ch->base + VCO_CTRL11) >> 7; 28562306a36Sopenharmony_ci reg = (reg >> (ch->index * 3)); 28662306a36Sopenharmony_ci if (reg & BIT(2)) 28762306a36Sopenharmony_ci divider *= div_hdmi[reg & 0x3]; 28862306a36Sopenharmony_ci 28962306a36Sopenharmony_ci /* 29062306a36Sopenharmony_ci * AV1 divider start at VCO_CTRL11, bit 28; MSB is enable, lower 2 bit 29162306a36Sopenharmony_ci * determine divider. 29262306a36Sopenharmony_ci */ 29362306a36Sopenharmony_ci if (ch->index == 0) { 29462306a36Sopenharmony_ci reg = readl_relaxed(ch->base + VCO_CTRL11); 29562306a36Sopenharmony_ci reg >>= 28; 29662306a36Sopenharmony_ci } else { 29762306a36Sopenharmony_ci reg = readl_relaxed(ch->base + VCO_CTRL12); 29862306a36Sopenharmony_ci reg >>= (ch->index-1) * 3; 29962306a36Sopenharmony_ci } 30062306a36Sopenharmony_ci if (reg & BIT(2)) 30162306a36Sopenharmony_ci divider *= div_av1[reg & 0x3]; 30262306a36Sopenharmony_ci 30362306a36Sopenharmony_ci /* 30462306a36Sopenharmony_ci * AV2 divider start at VCO_CTRL12, bit 18; each 7 bits wide, 30562306a36Sopenharmony_ci * zero is not a valid value. 30662306a36Sopenharmony_ci */ 30762306a36Sopenharmony_ci if (ch->index < 2) { 30862306a36Sopenharmony_ci reg = readl_relaxed(ch->base + VCO_CTRL12); 30962306a36Sopenharmony_ci reg >>= 18 + (ch->index * 7); 31062306a36Sopenharmony_ci } else if (ch->index < 7) { 31162306a36Sopenharmony_ci reg = readl_relaxed(ch->base + VCO_CTRL13); 31262306a36Sopenharmony_ci reg >>= (ch->index - 2) * 7; 31362306a36Sopenharmony_ci } else { 31462306a36Sopenharmony_ci reg = readl_relaxed(ch->base + VCO_CTRL14); 31562306a36Sopenharmony_ci } 31662306a36Sopenharmony_ci div_av2 = reg & 0x7f; 31762306a36Sopenharmony_ci if (div_av2) 31862306a36Sopenharmony_ci divider *= div_av2; 31962306a36Sopenharmony_ci 32062306a36Sopenharmony_ci /* 32162306a36Sopenharmony_ci * AV3 divider start at VCO_CTRL14, bit 7; each 4 bits wide. 32262306a36Sopenharmony_ci * AV2/AV3 form a fractional divider, where only specfic values for AV3 32362306a36Sopenharmony_ci * are allowed. AV3 != 0 divides by AV2/2, AV3=0 is bypass. 32462306a36Sopenharmony_ci */ 32562306a36Sopenharmony_ci if (ch->index < 6) { 32662306a36Sopenharmony_ci reg = readl_relaxed(ch->base + VCO_CTRL14); 32762306a36Sopenharmony_ci reg >>= 7 + (ch->index * 4); 32862306a36Sopenharmony_ci } else { 32962306a36Sopenharmony_ci reg = readl_relaxed(ch->base + VCO_CTRL15); 33062306a36Sopenharmony_ci } 33162306a36Sopenharmony_ci div_av3 = reg & 0xf; 33262306a36Sopenharmony_ci if (div_av2 && div_av3) 33362306a36Sopenharmony_ci freq *= 2; 33462306a36Sopenharmony_ci 33562306a36Sopenharmony_ciskip_div: 33662306a36Sopenharmony_ci do_div(freq, divider); 33762306a36Sopenharmony_ci return (unsigned long)freq; 33862306a36Sopenharmony_ci} 33962306a36Sopenharmony_ci 34062306a36Sopenharmony_cistatic const struct clk_ops berlin2_avpll_channel_ops = { 34162306a36Sopenharmony_ci .is_enabled = berlin2_avpll_channel_is_enabled, 34262306a36Sopenharmony_ci .enable = berlin2_avpll_channel_enable, 34362306a36Sopenharmony_ci .disable = berlin2_avpll_channel_disable, 34462306a36Sopenharmony_ci .recalc_rate = berlin2_avpll_channel_recalc_rate, 34562306a36Sopenharmony_ci}; 34662306a36Sopenharmony_ci 34762306a36Sopenharmony_ci/* 34862306a36Sopenharmony_ci * Another nice quirk: 34962306a36Sopenharmony_ci * On some production SoCs, AVPLL channels are scrambled with respect 35062306a36Sopenharmony_ci * to the channel numbering in the registers but still referenced by 35162306a36Sopenharmony_ci * their original channel numbers. We deal with it by having a flag 35262306a36Sopenharmony_ci * and a translation table for the index. 35362306a36Sopenharmony_ci */ 35462306a36Sopenharmony_cistatic const u8 quirk_index[] __initconst = { 0, 6, 5, 4, 3, 2, 1, 7 }; 35562306a36Sopenharmony_ci 35662306a36Sopenharmony_ciint __init berlin2_avpll_channel_register(void __iomem *base, 35762306a36Sopenharmony_ci const char *name, u8 index, const char *parent_name, 35862306a36Sopenharmony_ci u8 ch_flags, unsigned long flags) 35962306a36Sopenharmony_ci{ 36062306a36Sopenharmony_ci struct berlin2_avpll_channel *ch; 36162306a36Sopenharmony_ci struct clk_init_data init; 36262306a36Sopenharmony_ci 36362306a36Sopenharmony_ci ch = kzalloc(sizeof(*ch), GFP_KERNEL); 36462306a36Sopenharmony_ci if (!ch) 36562306a36Sopenharmony_ci return -ENOMEM; 36662306a36Sopenharmony_ci 36762306a36Sopenharmony_ci ch->base = base; 36862306a36Sopenharmony_ci if (ch_flags & BERLIN2_AVPLL_SCRAMBLE_QUIRK) 36962306a36Sopenharmony_ci ch->index = quirk_index[index]; 37062306a36Sopenharmony_ci else 37162306a36Sopenharmony_ci ch->index = index; 37262306a36Sopenharmony_ci 37362306a36Sopenharmony_ci ch->flags = ch_flags; 37462306a36Sopenharmony_ci ch->hw.init = &init; 37562306a36Sopenharmony_ci init.name = name; 37662306a36Sopenharmony_ci init.ops = &berlin2_avpll_channel_ops; 37762306a36Sopenharmony_ci init.parent_names = &parent_name; 37862306a36Sopenharmony_ci init.num_parents = 1; 37962306a36Sopenharmony_ci init.flags = flags; 38062306a36Sopenharmony_ci 38162306a36Sopenharmony_ci return clk_hw_register(NULL, &ch->hw); 38262306a36Sopenharmony_ci} 383