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