19952f691SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
26609dbe4SPeter De Schrijver /*
36609dbe4SPeter De Schrijver  * Copyright (c) 2012, 2013, NVIDIA CORPORATION.  All rights reserved.
46609dbe4SPeter De Schrijver  */
56609dbe4SPeter De Schrijver 
66609dbe4SPeter De Schrijver #include <linux/io.h>
76609dbe4SPeter De Schrijver #include <linux/clk-provider.h>
86609dbe4SPeter De Schrijver #include <linux/of.h>
96609dbe4SPeter De Schrijver #include <linux/of_address.h>
106609dbe4SPeter De Schrijver #include <linux/delay.h>
116609dbe4SPeter De Schrijver #include <linux/export.h>
126609dbe4SPeter De Schrijver #include <linux/clk/tegra.h>
136609dbe4SPeter De Schrijver 
146609dbe4SPeter De Schrijver #include "clk.h"
156609dbe4SPeter De Schrijver #include "clk-id.h"
166609dbe4SPeter De Schrijver 
176609dbe4SPeter De Schrijver #define AUDIO_SYNC_CLK_I2S0 0x4a0
186609dbe4SPeter De Schrijver #define AUDIO_SYNC_CLK_I2S1 0x4a4
196609dbe4SPeter De Schrijver #define AUDIO_SYNC_CLK_I2S2 0x4a8
206609dbe4SPeter De Schrijver #define AUDIO_SYNC_CLK_I2S3 0x4ac
216609dbe4SPeter De Schrijver #define AUDIO_SYNC_CLK_I2S4 0x4b0
226609dbe4SPeter De Schrijver #define AUDIO_SYNC_CLK_SPDIF 0x4b4
23319af797SPeter De Schrijver #define AUDIO_SYNC_CLK_DMIC1 0x560
24319af797SPeter De Schrijver #define AUDIO_SYNC_CLK_DMIC2 0x564
25319af797SPeter De Schrijver #define AUDIO_SYNC_CLK_DMIC3 0x6b8
266609dbe4SPeter De Schrijver 
276609dbe4SPeter De Schrijver #define AUDIO_SYNC_DOUBLER 0x49c
286609dbe4SPeter De Schrijver 
296609dbe4SPeter De Schrijver #define PLLA_OUT 0xb4
306609dbe4SPeter De Schrijver 
316609dbe4SPeter De Schrijver struct tegra_sync_source_initdata {
326609dbe4SPeter De Schrijver 	char		*name;
336609dbe4SPeter De Schrijver 	unsigned long	rate;
346609dbe4SPeter De Schrijver 	unsigned long	max_rate;
356609dbe4SPeter De Schrijver 	int		clk_id;
366609dbe4SPeter De Schrijver };
376609dbe4SPeter De Schrijver 
386609dbe4SPeter De Schrijver #define SYNC(_name) \
396609dbe4SPeter De Schrijver 	{\
406609dbe4SPeter De Schrijver 		.name		= #_name,\
416609dbe4SPeter De Schrijver 		.clk_id		= tegra_clk_ ## _name,\
426609dbe4SPeter De Schrijver 	}
436609dbe4SPeter De Schrijver 
446609dbe4SPeter De Schrijver struct tegra_audio_clk_initdata {
456609dbe4SPeter De Schrijver 	char		*gate_name;
466609dbe4SPeter De Schrijver 	char		*mux_name;
476609dbe4SPeter De Schrijver 	u32		offset;
486609dbe4SPeter De Schrijver 	int		gate_clk_id;
496609dbe4SPeter De Schrijver 	int		mux_clk_id;
506609dbe4SPeter De Schrijver };
516609dbe4SPeter De Schrijver 
526609dbe4SPeter De Schrijver #define AUDIO(_name, _offset) \
536609dbe4SPeter De Schrijver 	{\
546609dbe4SPeter De Schrijver 		.gate_name	= #_name,\
556609dbe4SPeter De Schrijver 		.mux_name	= #_name"_mux",\
566609dbe4SPeter De Schrijver 		.offset		= _offset,\
576609dbe4SPeter De Schrijver 		.gate_clk_id	= tegra_clk_ ## _name,\
586609dbe4SPeter De Schrijver 		.mux_clk_id	= tegra_clk_ ## _name ## _mux,\
596609dbe4SPeter De Schrijver 	}
606609dbe4SPeter De Schrijver 
616609dbe4SPeter De Schrijver struct tegra_audio2x_clk_initdata {
626609dbe4SPeter De Schrijver 	char		*parent;
636609dbe4SPeter De Schrijver 	char		*gate_name;
646609dbe4SPeter De Schrijver 	char		*name_2x;
656609dbe4SPeter De Schrijver 	char		*div_name;
666609dbe4SPeter De Schrijver 	int		clk_id;
676609dbe4SPeter De Schrijver 	int		clk_num;
686609dbe4SPeter De Schrijver 	u8		div_offset;
696609dbe4SPeter De Schrijver };
706609dbe4SPeter De Schrijver 
716609dbe4SPeter De Schrijver #define AUDIO2X(_name, _num, _offset) \
726609dbe4SPeter De Schrijver 	{\
736609dbe4SPeter De Schrijver 		.parent		= #_name,\
746609dbe4SPeter De Schrijver 		.gate_name	= #_name"_2x",\
756609dbe4SPeter De Schrijver 		.name_2x	= #_name"_doubler",\
766609dbe4SPeter De Schrijver 		.div_name	= #_name"_div",\
776609dbe4SPeter De Schrijver 		.clk_id		= tegra_clk_ ## _name ## _2x,\
786609dbe4SPeter De Schrijver 		.clk_num	= _num,\
796609dbe4SPeter De Schrijver 		.div_offset	= _offset,\
806609dbe4SPeter De Schrijver 	}
816609dbe4SPeter De Schrijver 
826609dbe4SPeter De Schrijver static DEFINE_SPINLOCK(clk_doubler_lock);
836609dbe4SPeter De Schrijver 
84319af797SPeter De Schrijver static const char * const mux_audio_sync_clk[] = { "spdif_in_sync",
85319af797SPeter De Schrijver 	"i2s0_sync", "i2s1_sync", "i2s2_sync", "i2s3_sync", "i2s4_sync",
86319af797SPeter De Schrijver 	"pll_a_out0", "vimclk_sync",
87319af797SPeter De Schrijver };
88319af797SPeter De Schrijver 
89319af797SPeter De Schrijver static const char * const mux_dmic_sync_clk[] = { "unused", "i2s0_sync",
90319af797SPeter De Schrijver 	"i2s1_sync", "i2s2_sync", "i2s3_sync", "i2s4_sync", "pll_a_out0",
91319af797SPeter De Schrijver 	"vimclk_sync",
926609dbe4SPeter De Schrijver };
936609dbe4SPeter De Schrijver 
946609dbe4SPeter De Schrijver static struct tegra_sync_source_initdata sync_source_clks[] __initdata = {
956609dbe4SPeter De Schrijver 	SYNC(spdif_in_sync),
966609dbe4SPeter De Schrijver 	SYNC(i2s0_sync),
976609dbe4SPeter De Schrijver 	SYNC(i2s1_sync),
986609dbe4SPeter De Schrijver 	SYNC(i2s2_sync),
996609dbe4SPeter De Schrijver 	SYNC(i2s3_sync),
1006609dbe4SPeter De Schrijver 	SYNC(i2s4_sync),
1016609dbe4SPeter De Schrijver 	SYNC(vimclk_sync),
1026609dbe4SPeter De Schrijver };
1036609dbe4SPeter De Schrijver 
1046609dbe4SPeter De Schrijver static struct tegra_audio_clk_initdata audio_clks[] = {
1056609dbe4SPeter De Schrijver 	AUDIO(audio0, AUDIO_SYNC_CLK_I2S0),
1066609dbe4SPeter De Schrijver 	AUDIO(audio1, AUDIO_SYNC_CLK_I2S1),
1076609dbe4SPeter De Schrijver 	AUDIO(audio2, AUDIO_SYNC_CLK_I2S2),
1086609dbe4SPeter De Schrijver 	AUDIO(audio3, AUDIO_SYNC_CLK_I2S3),
1096609dbe4SPeter De Schrijver 	AUDIO(audio4, AUDIO_SYNC_CLK_I2S4),
1106609dbe4SPeter De Schrijver 	AUDIO(spdif, AUDIO_SYNC_CLK_SPDIF),
1116609dbe4SPeter De Schrijver };
1126609dbe4SPeter De Schrijver 
113319af797SPeter De Schrijver static struct tegra_audio_clk_initdata dmic_clks[] = {
114319af797SPeter De Schrijver 	AUDIO(dmic1_sync_clk, AUDIO_SYNC_CLK_DMIC1),
115319af797SPeter De Schrijver 	AUDIO(dmic2_sync_clk, AUDIO_SYNC_CLK_DMIC2),
116319af797SPeter De Schrijver 	AUDIO(dmic3_sync_clk, AUDIO_SYNC_CLK_DMIC3),
117319af797SPeter De Schrijver };
118319af797SPeter De Schrijver 
1196609dbe4SPeter De Schrijver static struct tegra_audio2x_clk_initdata audio2x_clks[] = {
1206609dbe4SPeter De Schrijver 	AUDIO2X(audio0, 113, 24),
1216609dbe4SPeter De Schrijver 	AUDIO2X(audio1, 114, 25),
1226609dbe4SPeter De Schrijver 	AUDIO2X(audio2, 115, 26),
1236609dbe4SPeter De Schrijver 	AUDIO2X(audio3, 116, 27),
1246609dbe4SPeter De Schrijver 	AUDIO2X(audio4, 117, 28),
1256609dbe4SPeter De Schrijver 	AUDIO2X(spdif, 118, 29),
1266609dbe4SPeter De Schrijver };
1276609dbe4SPeter De Schrijver 
tegra_audio_sync_clk_init(void __iomem * clk_base,struct tegra_clk * tegra_clks,struct tegra_audio_clk_initdata * sync,int num_sync_clks,const char * const * mux_names,int num_mux_inputs)128319af797SPeter De Schrijver static void __init tegra_audio_sync_clk_init(void __iomem *clk_base,
129319af797SPeter De Schrijver 				      struct tegra_clk *tegra_clks,
130319af797SPeter De Schrijver 				      struct tegra_audio_clk_initdata *sync,
131319af797SPeter De Schrijver 				      int num_sync_clks,
132319af797SPeter De Schrijver 				      const char * const *mux_names,
133319af797SPeter De Schrijver 				      int num_mux_inputs)
134319af797SPeter De Schrijver {
135319af797SPeter De Schrijver 	struct clk *clk;
136319af797SPeter De Schrijver 	struct clk **dt_clk;
137319af797SPeter De Schrijver 	struct tegra_audio_clk_initdata *data;
138319af797SPeter De Schrijver 	int i;
139319af797SPeter De Schrijver 
140319af797SPeter De Schrijver 	for (i = 0, data = sync; i < num_sync_clks; i++, data++) {
141319af797SPeter De Schrijver 		dt_clk = tegra_lookup_dt_id(data->mux_clk_id, tegra_clks);
142319af797SPeter De Schrijver 		if (!dt_clk)
143319af797SPeter De Schrijver 			continue;
144319af797SPeter De Schrijver 
145319af797SPeter De Schrijver 		clk = clk_register_mux(NULL, data->mux_name, mux_names,
146319af797SPeter De Schrijver 					num_mux_inputs,
147319af797SPeter De Schrijver 					CLK_SET_RATE_NO_REPARENT,
148319af797SPeter De Schrijver 					clk_base + data->offset, 0, 3, 0,
149319af797SPeter De Schrijver 					NULL);
150319af797SPeter De Schrijver 		*dt_clk = clk;
151319af797SPeter De Schrijver 
152319af797SPeter De Schrijver 		dt_clk = tegra_lookup_dt_id(data->gate_clk_id, tegra_clks);
153319af797SPeter De Schrijver 		if (!dt_clk)
154319af797SPeter De Schrijver 			continue;
155319af797SPeter De Schrijver 
156319af797SPeter De Schrijver 		clk = clk_register_gate(NULL, data->gate_name, data->mux_name,
157319af797SPeter De Schrijver 					0, clk_base + data->offset, 4,
158319af797SPeter De Schrijver 					CLK_GATE_SET_TO_DISABLE, NULL);
159319af797SPeter De Schrijver 		*dt_clk = clk;
160319af797SPeter De Schrijver 	}
161319af797SPeter De Schrijver }
162319af797SPeter De Schrijver 
tegra_audio_clk_init(void __iomem * clk_base,void __iomem * pmc_base,struct tegra_clk * tegra_clks,struct tegra_audio_clk_info * audio_info,unsigned int num_plls,unsigned long sync_max_rate)1636609dbe4SPeter De Schrijver void __init tegra_audio_clk_init(void __iomem *clk_base,
1646609dbe4SPeter De Schrijver 			void __iomem *pmc_base, struct tegra_clk *tegra_clks,
16588d909beSRhyland Klein 			struct tegra_audio_clk_info *audio_info,
166845d782dSJon Hunter 			unsigned int num_plls, unsigned long sync_max_rate)
1676609dbe4SPeter De Schrijver {
1686609dbe4SPeter De Schrijver 	struct clk *clk;
1696609dbe4SPeter De Schrijver 	struct clk **dt_clk;
1706609dbe4SPeter De Schrijver 	int i;
1716609dbe4SPeter De Schrijver 
17288d909beSRhyland Klein 	if (!audio_info || num_plls < 1) {
17388d909beSRhyland Klein 		pr_err("No audio data passed to tegra_audio_clk_init\n");
17488d909beSRhyland Klein 		WARN_ON(1);
17588d909beSRhyland Klein 		return;
17688d909beSRhyland Klein 	}
17788d909beSRhyland Klein 
17888d909beSRhyland Klein 	for (i = 0; i < num_plls; i++) {
17988d909beSRhyland Klein 		struct tegra_audio_clk_info *info = &audio_info[i];
18088d909beSRhyland Klein 
18188d909beSRhyland Klein 		dt_clk = tegra_lookup_dt_id(info->clk_id, tegra_clks);
1826609dbe4SPeter De Schrijver 		if (dt_clk) {
18388d909beSRhyland Klein 			clk = tegra_clk_register_pll(info->name, info->parent,
18488d909beSRhyland Klein 					clk_base, pmc_base, 0, info->pll_params,
18588d909beSRhyland Klein 					NULL);
1866609dbe4SPeter De Schrijver 			*dt_clk = clk;
1876609dbe4SPeter De Schrijver 		}
18888d909beSRhyland Klein 	}
1896609dbe4SPeter De Schrijver 
1906609dbe4SPeter De Schrijver 	/* PLLA_OUT0 */
1916609dbe4SPeter De Schrijver 	dt_clk = tegra_lookup_dt_id(tegra_clk_pll_a_out0, tegra_clks);
1926609dbe4SPeter De Schrijver 	if (dt_clk) {
1936609dbe4SPeter De Schrijver 		clk = tegra_clk_register_divider("pll_a_out0_div", "pll_a",
1946609dbe4SPeter De Schrijver 				clk_base + PLLA_OUT, 0, TEGRA_DIVIDER_ROUND_UP,
1956609dbe4SPeter De Schrijver 				8, 8, 1, NULL);
1966609dbe4SPeter De Schrijver 		clk = tegra_clk_register_pll_out("pll_a_out0", "pll_a_out0_div",
1976609dbe4SPeter De Schrijver 				clk_base + PLLA_OUT, 1, 0, CLK_IGNORE_UNUSED |
1986609dbe4SPeter De Schrijver 				CLK_SET_RATE_PARENT, 0, NULL);
1996609dbe4SPeter De Schrijver 		*dt_clk = clk;
2006609dbe4SPeter De Schrijver 	}
2016609dbe4SPeter De Schrijver 
2026609dbe4SPeter De Schrijver 	for (i = 0; i < ARRAY_SIZE(sync_source_clks); i++) {
2036609dbe4SPeter De Schrijver 		struct tegra_sync_source_initdata *data;
2046609dbe4SPeter De Schrijver 
2056609dbe4SPeter De Schrijver 		data = &sync_source_clks[i];
2066609dbe4SPeter De Schrijver 
2076609dbe4SPeter De Schrijver 		dt_clk = tegra_lookup_dt_id(data->clk_id, tegra_clks);
2086609dbe4SPeter De Schrijver 		if (!dt_clk)
2096609dbe4SPeter De Schrijver 			continue;
2106609dbe4SPeter De Schrijver 
211845d782dSJon Hunter 		clk = tegra_clk_register_sync_source(data->name, sync_max_rate);
2126609dbe4SPeter De Schrijver 		*dt_clk = clk;
2136609dbe4SPeter De Schrijver 	}
2146609dbe4SPeter De Schrijver 
215319af797SPeter De Schrijver 	tegra_audio_sync_clk_init(clk_base, tegra_clks, audio_clks,
216319af797SPeter De Schrijver 				  ARRAY_SIZE(audio_clks), mux_audio_sync_clk,
217319af797SPeter De Schrijver 				  ARRAY_SIZE(mux_audio_sync_clk));
2186609dbe4SPeter De Schrijver 
219319af797SPeter De Schrijver 	/* make sure the DMIC sync clocks have a valid parent */
220319af797SPeter De Schrijver 	for (i = 0; i < ARRAY_SIZE(dmic_clks); i++)
221319af797SPeter De Schrijver 		writel_relaxed(1, clk_base + dmic_clks[i].offset);
2226609dbe4SPeter De Schrijver 
223319af797SPeter De Schrijver 	tegra_audio_sync_clk_init(clk_base, tegra_clks, dmic_clks,
224319af797SPeter De Schrijver 				  ARRAY_SIZE(dmic_clks), mux_dmic_sync_clk,
225319af797SPeter De Schrijver 				  ARRAY_SIZE(mux_dmic_sync_clk));
2266609dbe4SPeter De Schrijver 
2276609dbe4SPeter De Schrijver 	for (i = 0; i < ARRAY_SIZE(audio2x_clks); i++) {
2286609dbe4SPeter De Schrijver 		struct tegra_audio2x_clk_initdata *data;
2296609dbe4SPeter De Schrijver 
2306609dbe4SPeter De Schrijver 		data = &audio2x_clks[i];
2316609dbe4SPeter De Schrijver 		dt_clk = tegra_lookup_dt_id(data->clk_id, tegra_clks);
2326609dbe4SPeter De Schrijver 		if (!dt_clk)
2336609dbe4SPeter De Schrijver 			continue;
2346609dbe4SPeter De Schrijver 
2356609dbe4SPeter De Schrijver 		clk = clk_register_fixed_factor(NULL, data->name_2x,
2366609dbe4SPeter De Schrijver 				data->parent, CLK_SET_RATE_PARENT, 2, 1);
2376609dbe4SPeter De Schrijver 		clk = tegra_clk_register_divider(data->div_name,
2386609dbe4SPeter De Schrijver 				data->name_2x, clk_base + AUDIO_SYNC_DOUBLER,
2396609dbe4SPeter De Schrijver 				0, 0, data->div_offset, 1, 0,
2406609dbe4SPeter De Schrijver 				&clk_doubler_lock);
2416609dbe4SPeter De Schrijver 		clk = tegra_clk_register_periph_gate(data->gate_name,
2426609dbe4SPeter De Schrijver 				data->div_name, TEGRA_PERIPH_NO_RESET,
2436609dbe4SPeter De Schrijver 				clk_base, CLK_SET_RATE_PARENT, data->clk_num,
2446609dbe4SPeter De Schrijver 				periph_clk_enb_refcnt);
2456609dbe4SPeter De Schrijver 		*dt_clk = clk;
2466609dbe4SPeter De Schrijver 	}
2476609dbe4SPeter De Schrijver }
2486609dbe4SPeter De Schrijver 
249