162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * MMP Audio Clock Controller driver
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright (C) 2020 Lubomir Rintel <lkundrak@v3.sk>
662306a36Sopenharmony_ci */
762306a36Sopenharmony_ci
862306a36Sopenharmony_ci#include <linux/clk-provider.h>
962306a36Sopenharmony_ci#include <linux/io.h>
1062306a36Sopenharmony_ci#include <linux/module.h>
1162306a36Sopenharmony_ci#include <linux/platform_device.h>
1262306a36Sopenharmony_ci#include <linux/pm_clock.h>
1362306a36Sopenharmony_ci#include <linux/pm_runtime.h>
1462306a36Sopenharmony_ci#include <linux/slab.h>
1562306a36Sopenharmony_ci#include <dt-bindings/clock/marvell,mmp2-audio.h>
1662306a36Sopenharmony_ci
1762306a36Sopenharmony_ci/* Audio Controller Registers */
1862306a36Sopenharmony_ci#define SSPA_AUD_CTRL				0x04
1962306a36Sopenharmony_ci#define SSPA_AUD_PLL_CTRL0			0x08
2062306a36Sopenharmony_ci#define SSPA_AUD_PLL_CTRL1			0x0c
2162306a36Sopenharmony_ci
2262306a36Sopenharmony_ci/* SSPA Audio Control Register */
2362306a36Sopenharmony_ci#define SSPA_AUD_CTRL_SYSCLK_SHIFT		0
2462306a36Sopenharmony_ci#define SSPA_AUD_CTRL_SYSCLK_DIV_SHIFT		1
2562306a36Sopenharmony_ci#define SSPA_AUD_CTRL_SSPA0_MUX_SHIFT		7
2662306a36Sopenharmony_ci#define SSPA_AUD_CTRL_SSPA0_SHIFT		8
2762306a36Sopenharmony_ci#define SSPA_AUD_CTRL_SSPA0_DIV_SHIFT		9
2862306a36Sopenharmony_ci#define SSPA_AUD_CTRL_SSPA1_SHIFT		16
2962306a36Sopenharmony_ci#define SSPA_AUD_CTRL_SSPA1_DIV_SHIFT		17
3062306a36Sopenharmony_ci#define SSPA_AUD_CTRL_SSPA1_MUX_SHIFT		23
3162306a36Sopenharmony_ci#define SSPA_AUD_CTRL_DIV_MASK			0x7e
3262306a36Sopenharmony_ci
3362306a36Sopenharmony_ci/* SSPA Audio PLL Control 0 Register */
3462306a36Sopenharmony_ci#define SSPA_AUD_PLL_CTRL0_DIV_OCLK_MODULO_MASK (0x7 << 28)
3562306a36Sopenharmony_ci#define SSPA_AUD_PLL_CTRL0_DIV_OCLK_MODULO(x)	((x) << 28)
3662306a36Sopenharmony_ci#define SSPA_AUD_PLL_CTRL0_FRACT_MASK		(0xfffff << 8)
3762306a36Sopenharmony_ci#define SSPA_AUD_PLL_CTRL0_FRACT(x)		((x) << 8)
3862306a36Sopenharmony_ci#define SSPA_AUD_PLL_CTRL0_ENA_DITHER		(1 << 7)
3962306a36Sopenharmony_ci#define SSPA_AUD_PLL_CTRL0_ICP_2UA		(0 << 5)
4062306a36Sopenharmony_ci#define SSPA_AUD_PLL_CTRL0_ICP_5UA		(1 << 5)
4162306a36Sopenharmony_ci#define SSPA_AUD_PLL_CTRL0_ICP_7UA		(2 << 5)
4262306a36Sopenharmony_ci#define SSPA_AUD_PLL_CTRL0_ICP_10UA		(3 << 5)
4362306a36Sopenharmony_ci#define SSPA_AUD_PLL_CTRL0_DIV_FBCCLK_MASK	(0x3 << 3)
4462306a36Sopenharmony_ci#define SSPA_AUD_PLL_CTRL0_DIV_FBCCLK(x)	((x) << 3)
4562306a36Sopenharmony_ci#define SSPA_AUD_PLL_CTRL0_DIV_MCLK_MASK	(0x1 << 2)
4662306a36Sopenharmony_ci#define SSPA_AUD_PLL_CTRL0_DIV_MCLK(x)		((x) << 2)
4762306a36Sopenharmony_ci#define SSPA_AUD_PLL_CTRL0_PD_OVPROT_DIS	(1 << 1)
4862306a36Sopenharmony_ci#define SSPA_AUD_PLL_CTRL0_PU			(1 << 0)
4962306a36Sopenharmony_ci
5062306a36Sopenharmony_ci/* SSPA Audio PLL Control 1 Register */
5162306a36Sopenharmony_ci#define SSPA_AUD_PLL_CTRL1_SEL_FAST_CLK		(1 << 24)
5262306a36Sopenharmony_ci#define SSPA_AUD_PLL_CTRL1_CLK_SEL_MASK		(1 << 11)
5362306a36Sopenharmony_ci#define SSPA_AUD_PLL_CTRL1_CLK_SEL_AUDIO_PLL	(1 << 11)
5462306a36Sopenharmony_ci#define SSPA_AUD_PLL_CTRL1_CLK_SEL_VCXO		(0 << 11)
5562306a36Sopenharmony_ci#define SSPA_AUD_PLL_CTRL1_DIV_OCLK_PATTERN_MASK (0x7ff << 0)
5662306a36Sopenharmony_ci#define SSPA_AUD_PLL_CTRL1_DIV_OCLK_PATTERN(x)	((x) << 0)
5762306a36Sopenharmony_ci
5862306a36Sopenharmony_ci#define CLK_AUDIO_NR_CLKS			3
5962306a36Sopenharmony_ci
6062306a36Sopenharmony_cistruct mmp2_audio_clk {
6162306a36Sopenharmony_ci	void __iomem *mmio_base;
6262306a36Sopenharmony_ci
6362306a36Sopenharmony_ci	struct clk_hw audio_pll_hw;
6462306a36Sopenharmony_ci	struct clk_mux sspa_mux;
6562306a36Sopenharmony_ci	struct clk_mux sspa1_mux;
6662306a36Sopenharmony_ci	struct clk_divider sysclk_div;
6762306a36Sopenharmony_ci	struct clk_divider sspa0_div;
6862306a36Sopenharmony_ci	struct clk_divider sspa1_div;
6962306a36Sopenharmony_ci	struct clk_gate sysclk_gate;
7062306a36Sopenharmony_ci	struct clk_gate sspa0_gate;
7162306a36Sopenharmony_ci	struct clk_gate sspa1_gate;
7262306a36Sopenharmony_ci
7362306a36Sopenharmony_ci	u32 aud_ctrl;
7462306a36Sopenharmony_ci	u32 aud_pll_ctrl0;
7562306a36Sopenharmony_ci	u32 aud_pll_ctrl1;
7662306a36Sopenharmony_ci
7762306a36Sopenharmony_ci	spinlock_t lock;
7862306a36Sopenharmony_ci
7962306a36Sopenharmony_ci	/* Must be last */
8062306a36Sopenharmony_ci	struct clk_hw_onecell_data clk_data;
8162306a36Sopenharmony_ci};
8262306a36Sopenharmony_ci
8362306a36Sopenharmony_cistatic const struct {
8462306a36Sopenharmony_ci	unsigned long parent_rate;
8562306a36Sopenharmony_ci	unsigned long freq_vco;
8662306a36Sopenharmony_ci	unsigned char mclk;
8762306a36Sopenharmony_ci	unsigned char fbcclk;
8862306a36Sopenharmony_ci	unsigned short fract;
8962306a36Sopenharmony_ci} predivs[] = {
9062306a36Sopenharmony_ci	{ 26000000, 135475200, 0, 0, 0x8a18 },
9162306a36Sopenharmony_ci	{ 26000000, 147456000, 0, 1, 0x0da1 },
9262306a36Sopenharmony_ci	{ 38400000, 135475200, 1, 2, 0x8208 },
9362306a36Sopenharmony_ci	{ 38400000, 147456000, 1, 3, 0xaaaa },
9462306a36Sopenharmony_ci};
9562306a36Sopenharmony_ci
9662306a36Sopenharmony_cistatic const struct {
9762306a36Sopenharmony_ci	unsigned char divisor;
9862306a36Sopenharmony_ci	unsigned char modulo;
9962306a36Sopenharmony_ci	unsigned char pattern;
10062306a36Sopenharmony_ci} postdivs[] = {
10162306a36Sopenharmony_ci	{   1,	3,  0, },
10262306a36Sopenharmony_ci	{   2,	5,  0, },
10362306a36Sopenharmony_ci	{   4,	0,  0, },
10462306a36Sopenharmony_ci	{   6,	1,  1, },
10562306a36Sopenharmony_ci	{   8,	1,  0, },
10662306a36Sopenharmony_ci	{   9,	1,  2, },
10762306a36Sopenharmony_ci	{  12,	2,  1, },
10862306a36Sopenharmony_ci	{  16,	2,  0, },
10962306a36Sopenharmony_ci	{  18,	2,  2, },
11062306a36Sopenharmony_ci	{  24,	4,  1, },
11162306a36Sopenharmony_ci	{  36,	4,  2, },
11262306a36Sopenharmony_ci	{  48,	6,  1, },
11362306a36Sopenharmony_ci	{  72,	6,  2, },
11462306a36Sopenharmony_ci};
11562306a36Sopenharmony_ci
11662306a36Sopenharmony_cistatic unsigned long audio_pll_recalc_rate(struct clk_hw *hw,
11762306a36Sopenharmony_ci					   unsigned long parent_rate)
11862306a36Sopenharmony_ci{
11962306a36Sopenharmony_ci	struct mmp2_audio_clk *priv = container_of(hw, struct mmp2_audio_clk, audio_pll_hw);
12062306a36Sopenharmony_ci	unsigned int prediv;
12162306a36Sopenharmony_ci	unsigned int postdiv;
12262306a36Sopenharmony_ci	u32 aud_pll_ctrl0;
12362306a36Sopenharmony_ci	u32 aud_pll_ctrl1;
12462306a36Sopenharmony_ci
12562306a36Sopenharmony_ci	aud_pll_ctrl0 = readl(priv->mmio_base + SSPA_AUD_PLL_CTRL0);
12662306a36Sopenharmony_ci	aud_pll_ctrl0 &= SSPA_AUD_PLL_CTRL0_DIV_OCLK_MODULO_MASK |
12762306a36Sopenharmony_ci			 SSPA_AUD_PLL_CTRL0_FRACT_MASK |
12862306a36Sopenharmony_ci			 SSPA_AUD_PLL_CTRL0_ENA_DITHER |
12962306a36Sopenharmony_ci			 SSPA_AUD_PLL_CTRL0_DIV_FBCCLK_MASK |
13062306a36Sopenharmony_ci			 SSPA_AUD_PLL_CTRL0_DIV_MCLK_MASK |
13162306a36Sopenharmony_ci			 SSPA_AUD_PLL_CTRL0_PU;
13262306a36Sopenharmony_ci
13362306a36Sopenharmony_ci	aud_pll_ctrl1 = readl(priv->mmio_base + SSPA_AUD_PLL_CTRL1);
13462306a36Sopenharmony_ci	aud_pll_ctrl1 &= SSPA_AUD_PLL_CTRL1_CLK_SEL_MASK |
13562306a36Sopenharmony_ci			 SSPA_AUD_PLL_CTRL1_DIV_OCLK_PATTERN_MASK;
13662306a36Sopenharmony_ci
13762306a36Sopenharmony_ci	for (prediv = 0; prediv < ARRAY_SIZE(predivs); prediv++) {
13862306a36Sopenharmony_ci		if (predivs[prediv].parent_rate != parent_rate)
13962306a36Sopenharmony_ci			continue;
14062306a36Sopenharmony_ci		for (postdiv = 0; postdiv < ARRAY_SIZE(postdivs); postdiv++) {
14162306a36Sopenharmony_ci			unsigned long freq;
14262306a36Sopenharmony_ci			u32 val;
14362306a36Sopenharmony_ci
14462306a36Sopenharmony_ci			val = SSPA_AUD_PLL_CTRL0_ENA_DITHER;
14562306a36Sopenharmony_ci			val |= SSPA_AUD_PLL_CTRL0_PU;
14662306a36Sopenharmony_ci			val |= SSPA_AUD_PLL_CTRL0_DIV_OCLK_MODULO(postdivs[postdiv].modulo);
14762306a36Sopenharmony_ci			val |= SSPA_AUD_PLL_CTRL0_FRACT(predivs[prediv].fract);
14862306a36Sopenharmony_ci			val |= SSPA_AUD_PLL_CTRL0_DIV_FBCCLK(predivs[prediv].fbcclk);
14962306a36Sopenharmony_ci			val |= SSPA_AUD_PLL_CTRL0_DIV_MCLK(predivs[prediv].mclk);
15062306a36Sopenharmony_ci			if (val != aud_pll_ctrl0)
15162306a36Sopenharmony_ci				continue;
15262306a36Sopenharmony_ci
15362306a36Sopenharmony_ci			val = SSPA_AUD_PLL_CTRL1_CLK_SEL_AUDIO_PLL;
15462306a36Sopenharmony_ci			val |= SSPA_AUD_PLL_CTRL1_DIV_OCLK_PATTERN(postdivs[postdiv].pattern);
15562306a36Sopenharmony_ci			if (val != aud_pll_ctrl1)
15662306a36Sopenharmony_ci				continue;
15762306a36Sopenharmony_ci
15862306a36Sopenharmony_ci			freq = predivs[prediv].freq_vco;
15962306a36Sopenharmony_ci			freq /= postdivs[postdiv].divisor;
16062306a36Sopenharmony_ci			return freq;
16162306a36Sopenharmony_ci		}
16262306a36Sopenharmony_ci	}
16362306a36Sopenharmony_ci
16462306a36Sopenharmony_ci	return 0;
16562306a36Sopenharmony_ci}
16662306a36Sopenharmony_ci
16762306a36Sopenharmony_cistatic long audio_pll_round_rate(struct clk_hw *hw, unsigned long rate,
16862306a36Sopenharmony_ci				 unsigned long *parent_rate)
16962306a36Sopenharmony_ci{
17062306a36Sopenharmony_ci	unsigned int prediv;
17162306a36Sopenharmony_ci	unsigned int postdiv;
17262306a36Sopenharmony_ci	long rounded = 0;
17362306a36Sopenharmony_ci
17462306a36Sopenharmony_ci	for (prediv = 0; prediv < ARRAY_SIZE(predivs); prediv++) {
17562306a36Sopenharmony_ci		if (predivs[prediv].parent_rate != *parent_rate)
17662306a36Sopenharmony_ci			continue;
17762306a36Sopenharmony_ci		for (postdiv = 0; postdiv < ARRAY_SIZE(postdivs); postdiv++) {
17862306a36Sopenharmony_ci			long freq = predivs[prediv].freq_vco;
17962306a36Sopenharmony_ci
18062306a36Sopenharmony_ci			freq /= postdivs[postdiv].divisor;
18162306a36Sopenharmony_ci			if (freq == rate)
18262306a36Sopenharmony_ci				return rate;
18362306a36Sopenharmony_ci			if (freq < rate)
18462306a36Sopenharmony_ci				continue;
18562306a36Sopenharmony_ci			if (rounded && freq > rounded)
18662306a36Sopenharmony_ci				continue;
18762306a36Sopenharmony_ci			rounded = freq;
18862306a36Sopenharmony_ci		}
18962306a36Sopenharmony_ci	}
19062306a36Sopenharmony_ci
19162306a36Sopenharmony_ci	return rounded;
19262306a36Sopenharmony_ci}
19362306a36Sopenharmony_ci
19462306a36Sopenharmony_cistatic int audio_pll_set_rate(struct clk_hw *hw, unsigned long rate,
19562306a36Sopenharmony_ci			      unsigned long parent_rate)
19662306a36Sopenharmony_ci{
19762306a36Sopenharmony_ci	struct mmp2_audio_clk *priv = container_of(hw, struct mmp2_audio_clk, audio_pll_hw);
19862306a36Sopenharmony_ci	unsigned int prediv;
19962306a36Sopenharmony_ci	unsigned int postdiv;
20062306a36Sopenharmony_ci	unsigned long val;
20162306a36Sopenharmony_ci
20262306a36Sopenharmony_ci	for (prediv = 0; prediv < ARRAY_SIZE(predivs); prediv++) {
20362306a36Sopenharmony_ci		if (predivs[prediv].parent_rate != parent_rate)
20462306a36Sopenharmony_ci			continue;
20562306a36Sopenharmony_ci
20662306a36Sopenharmony_ci		for (postdiv = 0; postdiv < ARRAY_SIZE(postdivs); postdiv++) {
20762306a36Sopenharmony_ci			if (rate * postdivs[postdiv].divisor != predivs[prediv].freq_vco)
20862306a36Sopenharmony_ci				continue;
20962306a36Sopenharmony_ci
21062306a36Sopenharmony_ci			val = SSPA_AUD_PLL_CTRL0_ENA_DITHER;
21162306a36Sopenharmony_ci			val |= SSPA_AUD_PLL_CTRL0_PU;
21262306a36Sopenharmony_ci			val |= SSPA_AUD_PLL_CTRL0_DIV_OCLK_MODULO(postdivs[postdiv].modulo);
21362306a36Sopenharmony_ci			val |= SSPA_AUD_PLL_CTRL0_FRACT(predivs[prediv].fract);
21462306a36Sopenharmony_ci			val |= SSPA_AUD_PLL_CTRL0_DIV_FBCCLK(predivs[prediv].fbcclk);
21562306a36Sopenharmony_ci			val |= SSPA_AUD_PLL_CTRL0_DIV_MCLK(predivs[prediv].mclk);
21662306a36Sopenharmony_ci			writel(val, priv->mmio_base + SSPA_AUD_PLL_CTRL0);
21762306a36Sopenharmony_ci
21862306a36Sopenharmony_ci			val = SSPA_AUD_PLL_CTRL1_CLK_SEL_AUDIO_PLL;
21962306a36Sopenharmony_ci			val |= SSPA_AUD_PLL_CTRL1_DIV_OCLK_PATTERN(postdivs[postdiv].pattern);
22062306a36Sopenharmony_ci			writel(val, priv->mmio_base + SSPA_AUD_PLL_CTRL1);
22162306a36Sopenharmony_ci
22262306a36Sopenharmony_ci			return 0;
22362306a36Sopenharmony_ci		}
22462306a36Sopenharmony_ci	}
22562306a36Sopenharmony_ci
22662306a36Sopenharmony_ci	return -ERANGE;
22762306a36Sopenharmony_ci}
22862306a36Sopenharmony_ci
22962306a36Sopenharmony_cistatic const struct clk_ops audio_pll_ops = {
23062306a36Sopenharmony_ci	.recalc_rate = audio_pll_recalc_rate,
23162306a36Sopenharmony_ci	.round_rate = audio_pll_round_rate,
23262306a36Sopenharmony_ci	.set_rate = audio_pll_set_rate,
23362306a36Sopenharmony_ci};
23462306a36Sopenharmony_ci
23562306a36Sopenharmony_cistatic int register_clocks(struct mmp2_audio_clk *priv, struct device *dev)
23662306a36Sopenharmony_ci{
23762306a36Sopenharmony_ci	const struct clk_parent_data sspa_mux_parents[] = {
23862306a36Sopenharmony_ci		{ .hw = &priv->audio_pll_hw },
23962306a36Sopenharmony_ci		{ .fw_name = "i2s0" },
24062306a36Sopenharmony_ci	};
24162306a36Sopenharmony_ci	const struct clk_parent_data sspa1_mux_parents[] = {
24262306a36Sopenharmony_ci		{ .hw = &priv->audio_pll_hw },
24362306a36Sopenharmony_ci		{ .fw_name = "i2s1" },
24462306a36Sopenharmony_ci	};
24562306a36Sopenharmony_ci	int ret;
24662306a36Sopenharmony_ci
24762306a36Sopenharmony_ci	priv->audio_pll_hw.init = CLK_HW_INIT_FW_NAME("audio_pll",
24862306a36Sopenharmony_ci				"vctcxo", &audio_pll_ops,
24962306a36Sopenharmony_ci				CLK_SET_RATE_PARENT);
25062306a36Sopenharmony_ci	ret = devm_clk_hw_register(dev, &priv->audio_pll_hw);
25162306a36Sopenharmony_ci	if (ret)
25262306a36Sopenharmony_ci		return ret;
25362306a36Sopenharmony_ci
25462306a36Sopenharmony_ci	priv->sspa_mux.hw.init = CLK_HW_INIT_PARENTS_DATA("sspa_mux",
25562306a36Sopenharmony_ci				sspa_mux_parents, &clk_mux_ops,
25662306a36Sopenharmony_ci				CLK_SET_RATE_PARENT);
25762306a36Sopenharmony_ci	priv->sspa_mux.reg = priv->mmio_base + SSPA_AUD_CTRL;
25862306a36Sopenharmony_ci	priv->sspa_mux.mask = 1;
25962306a36Sopenharmony_ci	priv->sspa_mux.shift = SSPA_AUD_CTRL_SSPA0_MUX_SHIFT;
26062306a36Sopenharmony_ci	ret = devm_clk_hw_register(dev, &priv->sspa_mux.hw);
26162306a36Sopenharmony_ci	if (ret)
26262306a36Sopenharmony_ci		return ret;
26362306a36Sopenharmony_ci
26462306a36Sopenharmony_ci	priv->sysclk_div.hw.init = CLK_HW_INIT_HW("sys_div",
26562306a36Sopenharmony_ci				&priv->sspa_mux.hw, &clk_divider_ops,
26662306a36Sopenharmony_ci				CLK_SET_RATE_PARENT);
26762306a36Sopenharmony_ci	priv->sysclk_div.reg = priv->mmio_base + SSPA_AUD_CTRL;
26862306a36Sopenharmony_ci	priv->sysclk_div.shift = SSPA_AUD_CTRL_SYSCLK_DIV_SHIFT;
26962306a36Sopenharmony_ci	priv->sysclk_div.width = 6;
27062306a36Sopenharmony_ci	priv->sysclk_div.flags = CLK_DIVIDER_ONE_BASED;
27162306a36Sopenharmony_ci	priv->sysclk_div.flags |= CLK_DIVIDER_ROUND_CLOSEST;
27262306a36Sopenharmony_ci	priv->sysclk_div.flags |= CLK_DIVIDER_ALLOW_ZERO;
27362306a36Sopenharmony_ci	ret = devm_clk_hw_register(dev, &priv->sysclk_div.hw);
27462306a36Sopenharmony_ci	if (ret)
27562306a36Sopenharmony_ci		return ret;
27662306a36Sopenharmony_ci
27762306a36Sopenharmony_ci	priv->sysclk_gate.hw.init = CLK_HW_INIT_HW("sys_clk",
27862306a36Sopenharmony_ci				&priv->sysclk_div.hw, &clk_gate_ops,
27962306a36Sopenharmony_ci				CLK_SET_RATE_PARENT);
28062306a36Sopenharmony_ci	priv->sysclk_gate.reg = priv->mmio_base + SSPA_AUD_CTRL;
28162306a36Sopenharmony_ci	priv->sysclk_gate.bit_idx = SSPA_AUD_CTRL_SYSCLK_SHIFT;
28262306a36Sopenharmony_ci	ret = devm_clk_hw_register(dev, &priv->sysclk_gate.hw);
28362306a36Sopenharmony_ci	if (ret)
28462306a36Sopenharmony_ci		return ret;
28562306a36Sopenharmony_ci
28662306a36Sopenharmony_ci	priv->sspa0_div.hw.init = CLK_HW_INIT_HW("sspa0_div",
28762306a36Sopenharmony_ci				&priv->sspa_mux.hw, &clk_divider_ops, 0);
28862306a36Sopenharmony_ci	priv->sspa0_div.reg = priv->mmio_base + SSPA_AUD_CTRL;
28962306a36Sopenharmony_ci	priv->sspa0_div.shift = SSPA_AUD_CTRL_SSPA0_DIV_SHIFT;
29062306a36Sopenharmony_ci	priv->sspa0_div.width = 6;
29162306a36Sopenharmony_ci	priv->sspa0_div.flags = CLK_DIVIDER_ONE_BASED;
29262306a36Sopenharmony_ci	priv->sspa0_div.flags |= CLK_DIVIDER_ROUND_CLOSEST;
29362306a36Sopenharmony_ci	priv->sspa0_div.flags |= CLK_DIVIDER_ALLOW_ZERO;
29462306a36Sopenharmony_ci	ret = devm_clk_hw_register(dev, &priv->sspa0_div.hw);
29562306a36Sopenharmony_ci	if (ret)
29662306a36Sopenharmony_ci		return ret;
29762306a36Sopenharmony_ci
29862306a36Sopenharmony_ci	priv->sspa0_gate.hw.init = CLK_HW_INIT_HW("sspa0_clk",
29962306a36Sopenharmony_ci				&priv->sspa0_div.hw, &clk_gate_ops,
30062306a36Sopenharmony_ci				CLK_SET_RATE_PARENT);
30162306a36Sopenharmony_ci	priv->sspa0_gate.reg = priv->mmio_base + SSPA_AUD_CTRL;
30262306a36Sopenharmony_ci	priv->sspa0_gate.bit_idx = SSPA_AUD_CTRL_SSPA0_SHIFT;
30362306a36Sopenharmony_ci	ret = devm_clk_hw_register(dev, &priv->sspa0_gate.hw);
30462306a36Sopenharmony_ci	if (ret)
30562306a36Sopenharmony_ci		return ret;
30662306a36Sopenharmony_ci
30762306a36Sopenharmony_ci	priv->sspa1_mux.hw.init = CLK_HW_INIT_PARENTS_DATA("sspa1_mux",
30862306a36Sopenharmony_ci				sspa1_mux_parents, &clk_mux_ops,
30962306a36Sopenharmony_ci				CLK_SET_RATE_PARENT);
31062306a36Sopenharmony_ci	priv->sspa1_mux.reg = priv->mmio_base + SSPA_AUD_CTRL;
31162306a36Sopenharmony_ci	priv->sspa1_mux.mask = 1;
31262306a36Sopenharmony_ci	priv->sspa1_mux.shift = SSPA_AUD_CTRL_SSPA1_MUX_SHIFT;
31362306a36Sopenharmony_ci	ret = devm_clk_hw_register(dev, &priv->sspa1_mux.hw);
31462306a36Sopenharmony_ci	if (ret)
31562306a36Sopenharmony_ci		return ret;
31662306a36Sopenharmony_ci
31762306a36Sopenharmony_ci	priv->sspa1_div.hw.init = CLK_HW_INIT_HW("sspa1_div",
31862306a36Sopenharmony_ci				&priv->sspa1_mux.hw, &clk_divider_ops, 0);
31962306a36Sopenharmony_ci	priv->sspa1_div.reg = priv->mmio_base + SSPA_AUD_CTRL;
32062306a36Sopenharmony_ci	priv->sspa1_div.shift = SSPA_AUD_CTRL_SSPA1_DIV_SHIFT;
32162306a36Sopenharmony_ci	priv->sspa1_div.width = 6;
32262306a36Sopenharmony_ci	priv->sspa1_div.flags = CLK_DIVIDER_ONE_BASED;
32362306a36Sopenharmony_ci	priv->sspa1_div.flags |= CLK_DIVIDER_ROUND_CLOSEST;
32462306a36Sopenharmony_ci	priv->sspa1_div.flags |= CLK_DIVIDER_ALLOW_ZERO;
32562306a36Sopenharmony_ci	ret = devm_clk_hw_register(dev, &priv->sspa1_div.hw);
32662306a36Sopenharmony_ci	if (ret)
32762306a36Sopenharmony_ci		return ret;
32862306a36Sopenharmony_ci
32962306a36Sopenharmony_ci	priv->sspa1_gate.hw.init = CLK_HW_INIT_HW("sspa1_clk",
33062306a36Sopenharmony_ci				&priv->sspa1_div.hw, &clk_gate_ops,
33162306a36Sopenharmony_ci				CLK_SET_RATE_PARENT);
33262306a36Sopenharmony_ci	priv->sspa1_gate.reg = priv->mmio_base + SSPA_AUD_CTRL;
33362306a36Sopenharmony_ci	priv->sspa1_gate.bit_idx = SSPA_AUD_CTRL_SSPA1_SHIFT;
33462306a36Sopenharmony_ci	ret = devm_clk_hw_register(dev, &priv->sspa1_gate.hw);
33562306a36Sopenharmony_ci	if (ret)
33662306a36Sopenharmony_ci		return ret;
33762306a36Sopenharmony_ci
33862306a36Sopenharmony_ci	priv->clk_data.hws[MMP2_CLK_AUDIO_SYSCLK] = &priv->sysclk_gate.hw;
33962306a36Sopenharmony_ci	priv->clk_data.hws[MMP2_CLK_AUDIO_SSPA0] = &priv->sspa0_gate.hw;
34062306a36Sopenharmony_ci	priv->clk_data.hws[MMP2_CLK_AUDIO_SSPA1] = &priv->sspa1_gate.hw;
34162306a36Sopenharmony_ci	priv->clk_data.num = CLK_AUDIO_NR_CLKS;
34262306a36Sopenharmony_ci
34362306a36Sopenharmony_ci	return of_clk_add_hw_provider(dev->of_node, of_clk_hw_onecell_get,
34462306a36Sopenharmony_ci				      &priv->clk_data);
34562306a36Sopenharmony_ci}
34662306a36Sopenharmony_ci
34762306a36Sopenharmony_cistatic int mmp2_audio_clk_probe(struct platform_device *pdev)
34862306a36Sopenharmony_ci{
34962306a36Sopenharmony_ci	struct mmp2_audio_clk *priv;
35062306a36Sopenharmony_ci	int ret;
35162306a36Sopenharmony_ci
35262306a36Sopenharmony_ci	priv = devm_kzalloc(&pdev->dev,
35362306a36Sopenharmony_ci			    struct_size(priv, clk_data.hws,
35462306a36Sopenharmony_ci					CLK_AUDIO_NR_CLKS),
35562306a36Sopenharmony_ci			    GFP_KERNEL);
35662306a36Sopenharmony_ci	if (!priv)
35762306a36Sopenharmony_ci		return -ENOMEM;
35862306a36Sopenharmony_ci
35962306a36Sopenharmony_ci	spin_lock_init(&priv->lock);
36062306a36Sopenharmony_ci	platform_set_drvdata(pdev, priv);
36162306a36Sopenharmony_ci
36262306a36Sopenharmony_ci	priv->mmio_base = devm_platform_ioremap_resource(pdev, 0);
36362306a36Sopenharmony_ci	if (IS_ERR(priv->mmio_base))
36462306a36Sopenharmony_ci		return PTR_ERR(priv->mmio_base);
36562306a36Sopenharmony_ci
36662306a36Sopenharmony_ci	pm_runtime_enable(&pdev->dev);
36762306a36Sopenharmony_ci	ret = pm_clk_create(&pdev->dev);
36862306a36Sopenharmony_ci	if (ret)
36962306a36Sopenharmony_ci		goto disable_pm_runtime;
37062306a36Sopenharmony_ci
37162306a36Sopenharmony_ci	ret = pm_clk_add(&pdev->dev, "audio");
37262306a36Sopenharmony_ci	if (ret)
37362306a36Sopenharmony_ci		goto destroy_pm_clk;
37462306a36Sopenharmony_ci
37562306a36Sopenharmony_ci	ret = register_clocks(priv, &pdev->dev);
37662306a36Sopenharmony_ci	if (ret)
37762306a36Sopenharmony_ci		goto destroy_pm_clk;
37862306a36Sopenharmony_ci
37962306a36Sopenharmony_ci	return 0;
38062306a36Sopenharmony_ci
38162306a36Sopenharmony_cidestroy_pm_clk:
38262306a36Sopenharmony_ci	pm_clk_destroy(&pdev->dev);
38362306a36Sopenharmony_cidisable_pm_runtime:
38462306a36Sopenharmony_ci	pm_runtime_disable(&pdev->dev);
38562306a36Sopenharmony_ci
38662306a36Sopenharmony_ci	return ret;
38762306a36Sopenharmony_ci}
38862306a36Sopenharmony_ci
38962306a36Sopenharmony_cistatic void mmp2_audio_clk_remove(struct platform_device *pdev)
39062306a36Sopenharmony_ci{
39162306a36Sopenharmony_ci	pm_clk_destroy(&pdev->dev);
39262306a36Sopenharmony_ci	pm_runtime_disable(&pdev->dev);
39362306a36Sopenharmony_ci}
39462306a36Sopenharmony_ci
39562306a36Sopenharmony_ci#ifdef CONFIG_PM
39662306a36Sopenharmony_cistatic int mmp2_audio_clk_suspend(struct device *dev)
39762306a36Sopenharmony_ci{
39862306a36Sopenharmony_ci	struct mmp2_audio_clk *priv = dev_get_drvdata(dev);
39962306a36Sopenharmony_ci
40062306a36Sopenharmony_ci	priv->aud_ctrl = readl(priv->mmio_base + SSPA_AUD_CTRL);
40162306a36Sopenharmony_ci	priv->aud_pll_ctrl0 = readl(priv->mmio_base + SSPA_AUD_PLL_CTRL0);
40262306a36Sopenharmony_ci	priv->aud_pll_ctrl1 = readl(priv->mmio_base + SSPA_AUD_PLL_CTRL1);
40362306a36Sopenharmony_ci	pm_clk_suspend(dev);
40462306a36Sopenharmony_ci
40562306a36Sopenharmony_ci	return 0;
40662306a36Sopenharmony_ci}
40762306a36Sopenharmony_ci
40862306a36Sopenharmony_cistatic int mmp2_audio_clk_resume(struct device *dev)
40962306a36Sopenharmony_ci{
41062306a36Sopenharmony_ci	struct mmp2_audio_clk *priv = dev_get_drvdata(dev);
41162306a36Sopenharmony_ci
41262306a36Sopenharmony_ci	pm_clk_resume(dev);
41362306a36Sopenharmony_ci	writel(priv->aud_ctrl, priv->mmio_base + SSPA_AUD_CTRL);
41462306a36Sopenharmony_ci	writel(priv->aud_pll_ctrl0, priv->mmio_base + SSPA_AUD_PLL_CTRL0);
41562306a36Sopenharmony_ci	writel(priv->aud_pll_ctrl1, priv->mmio_base + SSPA_AUD_PLL_CTRL1);
41662306a36Sopenharmony_ci
41762306a36Sopenharmony_ci	return 0;
41862306a36Sopenharmony_ci}
41962306a36Sopenharmony_ci#endif
42062306a36Sopenharmony_ci
42162306a36Sopenharmony_cistatic const struct dev_pm_ops mmp2_audio_clk_pm_ops = {
42262306a36Sopenharmony_ci	SET_RUNTIME_PM_OPS(mmp2_audio_clk_suspend, mmp2_audio_clk_resume, NULL)
42362306a36Sopenharmony_ci};
42462306a36Sopenharmony_ci
42562306a36Sopenharmony_cistatic const struct of_device_id mmp2_audio_clk_of_match[] = {
42662306a36Sopenharmony_ci	{ .compatible = "marvell,mmp2-audio-clock" },
42762306a36Sopenharmony_ci	{}
42862306a36Sopenharmony_ci};
42962306a36Sopenharmony_ci
43062306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, mmp2_audio_clk_of_match);
43162306a36Sopenharmony_ci
43262306a36Sopenharmony_cistatic struct platform_driver mmp2_audio_clk_driver = {
43362306a36Sopenharmony_ci	.driver = {
43462306a36Sopenharmony_ci		.name = "mmp2-audio-clock",
43562306a36Sopenharmony_ci		.of_match_table = of_match_ptr(mmp2_audio_clk_of_match),
43662306a36Sopenharmony_ci		.pm = &mmp2_audio_clk_pm_ops,
43762306a36Sopenharmony_ci	},
43862306a36Sopenharmony_ci	.probe = mmp2_audio_clk_probe,
43962306a36Sopenharmony_ci	.remove_new = mmp2_audio_clk_remove,
44062306a36Sopenharmony_ci};
44162306a36Sopenharmony_cimodule_platform_driver(mmp2_audio_clk_driver);
44262306a36Sopenharmony_ci
44362306a36Sopenharmony_ciMODULE_AUTHOR("Lubomir Rintel <lkundrak@v3.sk>");
44462306a36Sopenharmony_ciMODULE_DESCRIPTION("Clock driver for MMP2 Audio subsystem");
44562306a36Sopenharmony_ciMODULE_LICENSE("GPL");
446