16609dbe4SPeter De Schrijver /*
26609dbe4SPeter De Schrijver  * Copyright (c) 2012, 2013, NVIDIA CORPORATION.  All rights reserved.
36609dbe4SPeter De Schrijver  *
46609dbe4SPeter De Schrijver  * This program is free software; you can redistribute it and/or modify it
56609dbe4SPeter De Schrijver  * under the terms and conditions of the GNU General Public License,
66609dbe4SPeter De Schrijver  * version 2, as published by the Free Software Foundation.
76609dbe4SPeter De Schrijver  *
86609dbe4SPeter De Schrijver  * This program is distributed in the hope it will be useful, but WITHOUT
96609dbe4SPeter De Schrijver  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
106609dbe4SPeter De Schrijver  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
116609dbe4SPeter De Schrijver  * more details.
126609dbe4SPeter De Schrijver  *
136609dbe4SPeter De Schrijver  * You should have received a copy of the GNU General Public License
146609dbe4SPeter De Schrijver  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
156609dbe4SPeter De Schrijver  */
166609dbe4SPeter De Schrijver 
176609dbe4SPeter De Schrijver #include <linux/io.h>
186609dbe4SPeter De Schrijver #include <linux/clk-provider.h>
196609dbe4SPeter De Schrijver #include <linux/of.h>
206609dbe4SPeter De Schrijver #include <linux/of_address.h>
216609dbe4SPeter De Schrijver #include <linux/delay.h>
226609dbe4SPeter De Schrijver #include <linux/export.h>
236609dbe4SPeter De Schrijver #include <linux/clk/tegra.h>
246609dbe4SPeter De Schrijver 
256609dbe4SPeter De Schrijver #include "clk.h"
266609dbe4SPeter De Schrijver #include "clk-id.h"
276609dbe4SPeter De Schrijver 
286609dbe4SPeter De Schrijver #define AUDIO_SYNC_CLK_I2S0 0x4a0
296609dbe4SPeter De Schrijver #define AUDIO_SYNC_CLK_I2S1 0x4a4
306609dbe4SPeter De Schrijver #define AUDIO_SYNC_CLK_I2S2 0x4a8
316609dbe4SPeter De Schrijver #define AUDIO_SYNC_CLK_I2S3 0x4ac
326609dbe4SPeter De Schrijver #define AUDIO_SYNC_CLK_I2S4 0x4b0
336609dbe4SPeter De Schrijver #define AUDIO_SYNC_CLK_SPDIF 0x4b4
34319af797SPeter De Schrijver #define AUDIO_SYNC_CLK_DMIC1 0x560
35319af797SPeter De Schrijver #define AUDIO_SYNC_CLK_DMIC2 0x564
36319af797SPeter De Schrijver #define AUDIO_SYNC_CLK_DMIC3 0x6b8
376609dbe4SPeter De Schrijver 
386609dbe4SPeter De Schrijver #define AUDIO_SYNC_DOUBLER 0x49c
396609dbe4SPeter De Schrijver 
406609dbe4SPeter De Schrijver #define PLLA_OUT 0xb4
416609dbe4SPeter De Schrijver 
426609dbe4SPeter De Schrijver struct tegra_sync_source_initdata {
436609dbe4SPeter De Schrijver 	char		*name;
446609dbe4SPeter De Schrijver 	unsigned long	rate;
456609dbe4SPeter De Schrijver 	unsigned long	max_rate;
466609dbe4SPeter De Schrijver 	int		clk_id;
476609dbe4SPeter De Schrijver };
486609dbe4SPeter De Schrijver 
496609dbe4SPeter De Schrijver #define SYNC(_name) \
506609dbe4SPeter De Schrijver 	{\
516609dbe4SPeter De Schrijver 		.name		= #_name,\
526609dbe4SPeter De Schrijver 		.rate		= 24000000,\
536609dbe4SPeter De Schrijver 		.max_rate	= 24000000,\
546609dbe4SPeter De Schrijver 		.clk_id		= tegra_clk_ ## _name,\
556609dbe4SPeter De Schrijver 	}
566609dbe4SPeter De Schrijver 
576609dbe4SPeter De Schrijver struct tegra_audio_clk_initdata {
586609dbe4SPeter De Schrijver 	char		*gate_name;
596609dbe4SPeter De Schrijver 	char		*mux_name;
606609dbe4SPeter De Schrijver 	u32		offset;
616609dbe4SPeter De Schrijver 	int		gate_clk_id;
626609dbe4SPeter De Schrijver 	int		mux_clk_id;
636609dbe4SPeter De Schrijver };
646609dbe4SPeter De Schrijver 
656609dbe4SPeter De Schrijver #define AUDIO(_name, _offset) \
666609dbe4SPeter De Schrijver 	{\
676609dbe4SPeter De Schrijver 		.gate_name	= #_name,\
686609dbe4SPeter De Schrijver 		.mux_name	= #_name"_mux",\
696609dbe4SPeter De Schrijver 		.offset		= _offset,\
706609dbe4SPeter De Schrijver 		.gate_clk_id	= tegra_clk_ ## _name,\
716609dbe4SPeter De Schrijver 		.mux_clk_id	= tegra_clk_ ## _name ## _mux,\
726609dbe4SPeter De Schrijver 	}
736609dbe4SPeter De Schrijver 
746609dbe4SPeter De Schrijver struct tegra_audio2x_clk_initdata {
756609dbe4SPeter De Schrijver 	char		*parent;
766609dbe4SPeter De Schrijver 	char		*gate_name;
776609dbe4SPeter De Schrijver 	char		*name_2x;
786609dbe4SPeter De Schrijver 	char		*div_name;
796609dbe4SPeter De Schrijver 	int		clk_id;
806609dbe4SPeter De Schrijver 	int		clk_num;
816609dbe4SPeter De Schrijver 	u8		div_offset;
826609dbe4SPeter De Schrijver };
836609dbe4SPeter De Schrijver 
846609dbe4SPeter De Schrijver #define AUDIO2X(_name, _num, _offset) \
856609dbe4SPeter De Schrijver 	{\
866609dbe4SPeter De Schrijver 		.parent		= #_name,\
876609dbe4SPeter De Schrijver 		.gate_name	= #_name"_2x",\
886609dbe4SPeter De Schrijver 		.name_2x	= #_name"_doubler",\
896609dbe4SPeter De Schrijver 		.div_name	= #_name"_div",\
906609dbe4SPeter De Schrijver 		.clk_id		= tegra_clk_ ## _name ## _2x,\
916609dbe4SPeter De Schrijver 		.clk_num	= _num,\
926609dbe4SPeter De Schrijver 		.div_offset	= _offset,\
936609dbe4SPeter De Schrijver 	}
946609dbe4SPeter De Schrijver 
956609dbe4SPeter De Schrijver static DEFINE_SPINLOCK(clk_doubler_lock);
966609dbe4SPeter De Schrijver 
97319af797SPeter De Schrijver static const char * const mux_audio_sync_clk[] = { "spdif_in_sync",
98319af797SPeter De Schrijver 	"i2s0_sync", "i2s1_sync", "i2s2_sync", "i2s3_sync", "i2s4_sync",
99319af797SPeter De Schrijver 	"pll_a_out0", "vimclk_sync",
100319af797SPeter De Schrijver };
101319af797SPeter De Schrijver 
102319af797SPeter De Schrijver static const char * const mux_dmic_sync_clk[] = { "unused", "i2s0_sync",
103319af797SPeter De Schrijver 	"i2s1_sync", "i2s2_sync", "i2s3_sync", "i2s4_sync", "pll_a_out0",
104319af797SPeter De Schrijver 	"vimclk_sync",
1056609dbe4SPeter De Schrijver };
1066609dbe4SPeter De Schrijver 
1076609dbe4SPeter De Schrijver static struct tegra_sync_source_initdata sync_source_clks[] __initdata = {
1086609dbe4SPeter De Schrijver 	SYNC(spdif_in_sync),
1096609dbe4SPeter De Schrijver 	SYNC(i2s0_sync),
1106609dbe4SPeter De Schrijver 	SYNC(i2s1_sync),
1116609dbe4SPeter De Schrijver 	SYNC(i2s2_sync),
1126609dbe4SPeter De Schrijver 	SYNC(i2s3_sync),
1136609dbe4SPeter De Schrijver 	SYNC(i2s4_sync),
1146609dbe4SPeter De Schrijver 	SYNC(vimclk_sync),
1156609dbe4SPeter De Schrijver };
1166609dbe4SPeter De Schrijver 
1176609dbe4SPeter De Schrijver static struct tegra_audio_clk_initdata audio_clks[] = {
1186609dbe4SPeter De Schrijver 	AUDIO(audio0, AUDIO_SYNC_CLK_I2S0),
1196609dbe4SPeter De Schrijver 	AUDIO(audio1, AUDIO_SYNC_CLK_I2S1),
1206609dbe4SPeter De Schrijver 	AUDIO(audio2, AUDIO_SYNC_CLK_I2S2),
1216609dbe4SPeter De Schrijver 	AUDIO(audio3, AUDIO_SYNC_CLK_I2S3),
1226609dbe4SPeter De Schrijver 	AUDIO(audio4, AUDIO_SYNC_CLK_I2S4),
1236609dbe4SPeter De Schrijver 	AUDIO(spdif, AUDIO_SYNC_CLK_SPDIF),
1246609dbe4SPeter De Schrijver };
1256609dbe4SPeter De Schrijver 
126319af797SPeter De Schrijver static struct tegra_audio_clk_initdata dmic_clks[] = {
127319af797SPeter De Schrijver 	AUDIO(dmic1_sync_clk, AUDIO_SYNC_CLK_DMIC1),
128319af797SPeter De Schrijver 	AUDIO(dmic2_sync_clk, AUDIO_SYNC_CLK_DMIC2),
129319af797SPeter De Schrijver 	AUDIO(dmic3_sync_clk, AUDIO_SYNC_CLK_DMIC3),
130319af797SPeter De Schrijver };
131319af797SPeter De Schrijver 
1326609dbe4SPeter De Schrijver static struct tegra_audio2x_clk_initdata audio2x_clks[] = {
1336609dbe4SPeter De Schrijver 	AUDIO2X(audio0, 113, 24),
1346609dbe4SPeter De Schrijver 	AUDIO2X(audio1, 114, 25),
1356609dbe4SPeter De Schrijver 	AUDIO2X(audio2, 115, 26),
1366609dbe4SPeter De Schrijver 	AUDIO2X(audio3, 116, 27),
1376609dbe4SPeter De Schrijver 	AUDIO2X(audio4, 117, 28),
1386609dbe4SPeter De Schrijver 	AUDIO2X(spdif, 118, 29),
1396609dbe4SPeter De Schrijver };
1406609dbe4SPeter De Schrijver 
141319af797SPeter De Schrijver static void __init tegra_audio_sync_clk_init(void __iomem *clk_base,
142319af797SPeter De Schrijver 				      struct tegra_clk *tegra_clks,
143319af797SPeter De Schrijver 				      struct tegra_audio_clk_initdata *sync,
144319af797SPeter De Schrijver 				      int num_sync_clks,
145319af797SPeter De Schrijver 				      const char * const *mux_names,
146319af797SPeter De Schrijver 				      int num_mux_inputs)
147319af797SPeter De Schrijver {
148319af797SPeter De Schrijver 	struct clk *clk;
149319af797SPeter De Schrijver 	struct clk **dt_clk;
150319af797SPeter De Schrijver 	struct tegra_audio_clk_initdata *data;
151319af797SPeter De Schrijver 	int i;
152319af797SPeter De Schrijver 
153319af797SPeter De Schrijver 	for (i = 0, data = sync; i < num_sync_clks; i++, data++) {
154319af797SPeter De Schrijver 		dt_clk = tegra_lookup_dt_id(data->mux_clk_id, tegra_clks);
155319af797SPeter De Schrijver 		if (!dt_clk)
156319af797SPeter De Schrijver 			continue;
157319af797SPeter De Schrijver 
158319af797SPeter De Schrijver 		clk = clk_register_mux(NULL, data->mux_name, mux_names,
159319af797SPeter De Schrijver 					num_mux_inputs,
160319af797SPeter De Schrijver 					CLK_SET_RATE_NO_REPARENT,
161319af797SPeter De Schrijver 					clk_base + data->offset, 0, 3, 0,
162319af797SPeter De Schrijver 					NULL);
163319af797SPeter De Schrijver 		*dt_clk = clk;
164319af797SPeter De Schrijver 
165319af797SPeter De Schrijver 		dt_clk = tegra_lookup_dt_id(data->gate_clk_id, tegra_clks);
166319af797SPeter De Schrijver 		if (!dt_clk)
167319af797SPeter De Schrijver 			continue;
168319af797SPeter De Schrijver 
169319af797SPeter De Schrijver 		clk = clk_register_gate(NULL, data->gate_name, data->mux_name,
170319af797SPeter De Schrijver 					0, clk_base + data->offset, 4,
171319af797SPeter De Schrijver 					CLK_GATE_SET_TO_DISABLE, NULL);
172319af797SPeter De Schrijver 		*dt_clk = clk;
173319af797SPeter De Schrijver 	}
174319af797SPeter De Schrijver }
175319af797SPeter De Schrijver 
1766609dbe4SPeter De Schrijver void __init tegra_audio_clk_init(void __iomem *clk_base,
1776609dbe4SPeter De Schrijver 			void __iomem *pmc_base, struct tegra_clk *tegra_clks,
17888d909beSRhyland Klein 			struct tegra_audio_clk_info *audio_info,
17988d909beSRhyland Klein 			unsigned int num_plls)
1806609dbe4SPeter De Schrijver {
1816609dbe4SPeter De Schrijver 	struct clk *clk;
1826609dbe4SPeter De Schrijver 	struct clk **dt_clk;
1836609dbe4SPeter De Schrijver 	int i;
1846609dbe4SPeter De Schrijver 
18588d909beSRhyland Klein 	if (!audio_info || num_plls < 1) {
18688d909beSRhyland Klein 		pr_err("No audio data passed to tegra_audio_clk_init\n");
18788d909beSRhyland Klein 		WARN_ON(1);
18888d909beSRhyland Klein 		return;
18988d909beSRhyland Klein 	}
19088d909beSRhyland Klein 
19188d909beSRhyland Klein 	for (i = 0; i < num_plls; i++) {
19288d909beSRhyland Klein 		struct tegra_audio_clk_info *info = &audio_info[i];
19388d909beSRhyland Klein 
19488d909beSRhyland Klein 		dt_clk = tegra_lookup_dt_id(info->clk_id, tegra_clks);
1956609dbe4SPeter De Schrijver 		if (dt_clk) {
19688d909beSRhyland Klein 			clk = tegra_clk_register_pll(info->name, info->parent,
19788d909beSRhyland Klein 					clk_base, pmc_base, 0, info->pll_params,
19888d909beSRhyland Klein 					NULL);
1996609dbe4SPeter De Schrijver 			*dt_clk = clk;
2006609dbe4SPeter De Schrijver 		}
20188d909beSRhyland Klein 	}
2026609dbe4SPeter De Schrijver 
2036609dbe4SPeter De Schrijver 	/* PLLA_OUT0 */
2046609dbe4SPeter De Schrijver 	dt_clk = tegra_lookup_dt_id(tegra_clk_pll_a_out0, tegra_clks);
2056609dbe4SPeter De Schrijver 	if (dt_clk) {
2066609dbe4SPeter De Schrijver 		clk = tegra_clk_register_divider("pll_a_out0_div", "pll_a",
2076609dbe4SPeter De Schrijver 				clk_base + PLLA_OUT, 0, TEGRA_DIVIDER_ROUND_UP,
2086609dbe4SPeter De Schrijver 				8, 8, 1, NULL);
2096609dbe4SPeter De Schrijver 		clk = tegra_clk_register_pll_out("pll_a_out0", "pll_a_out0_div",
2106609dbe4SPeter De Schrijver 				clk_base + PLLA_OUT, 1, 0, CLK_IGNORE_UNUSED |
2116609dbe4SPeter De Schrijver 				CLK_SET_RATE_PARENT, 0, NULL);
2126609dbe4SPeter De Schrijver 		*dt_clk = clk;
2136609dbe4SPeter De Schrijver 	}
2146609dbe4SPeter De Schrijver 
2156609dbe4SPeter De Schrijver 	for (i = 0; i < ARRAY_SIZE(sync_source_clks); i++) {
2166609dbe4SPeter De Schrijver 		struct tegra_sync_source_initdata *data;
2176609dbe4SPeter De Schrijver 
2186609dbe4SPeter De Schrijver 		data = &sync_source_clks[i];
2196609dbe4SPeter De Schrijver 
2206609dbe4SPeter De Schrijver 		dt_clk = tegra_lookup_dt_id(data->clk_id, tegra_clks);
2216609dbe4SPeter De Schrijver 		if (!dt_clk)
2226609dbe4SPeter De Schrijver 			continue;
2236609dbe4SPeter De Schrijver 
2246609dbe4SPeter De Schrijver 		clk = tegra_clk_register_sync_source(data->name,
2256609dbe4SPeter De Schrijver 					data->rate, data->max_rate);
2266609dbe4SPeter De Schrijver 		*dt_clk = clk;
2276609dbe4SPeter De Schrijver 	}
2286609dbe4SPeter De Schrijver 
229319af797SPeter De Schrijver 	tegra_audio_sync_clk_init(clk_base, tegra_clks, audio_clks,
230319af797SPeter De Schrijver 				  ARRAY_SIZE(audio_clks), mux_audio_sync_clk,
231319af797SPeter De Schrijver 				  ARRAY_SIZE(mux_audio_sync_clk));
2326609dbe4SPeter De Schrijver 
233319af797SPeter De Schrijver 	/* make sure the DMIC sync clocks have a valid parent */
234319af797SPeter De Schrijver 	for (i = 0; i < ARRAY_SIZE(dmic_clks); i++)
235319af797SPeter De Schrijver 		writel_relaxed(1, clk_base + dmic_clks[i].offset);
2366609dbe4SPeter De Schrijver 
237319af797SPeter De Schrijver 	tegra_audio_sync_clk_init(clk_base, tegra_clks, dmic_clks,
238319af797SPeter De Schrijver 				  ARRAY_SIZE(dmic_clks), mux_dmic_sync_clk,
239319af797SPeter De Schrijver 				  ARRAY_SIZE(mux_dmic_sync_clk));
2406609dbe4SPeter De Schrijver 
2416609dbe4SPeter De Schrijver 	for (i = 0; i < ARRAY_SIZE(audio2x_clks); i++) {
2426609dbe4SPeter De Schrijver 		struct tegra_audio2x_clk_initdata *data;
2436609dbe4SPeter De Schrijver 
2446609dbe4SPeter De Schrijver 		data = &audio2x_clks[i];
2456609dbe4SPeter De Schrijver 		dt_clk = tegra_lookup_dt_id(data->clk_id, tegra_clks);
2466609dbe4SPeter De Schrijver 		if (!dt_clk)
2476609dbe4SPeter De Schrijver 			continue;
2486609dbe4SPeter De Schrijver 
2496609dbe4SPeter De Schrijver 		clk = clk_register_fixed_factor(NULL, data->name_2x,
2506609dbe4SPeter De Schrijver 				data->parent, CLK_SET_RATE_PARENT, 2, 1);
2516609dbe4SPeter De Schrijver 		clk = tegra_clk_register_divider(data->div_name,
2526609dbe4SPeter De Schrijver 				data->name_2x, clk_base + AUDIO_SYNC_DOUBLER,
2536609dbe4SPeter De Schrijver 				0, 0, data->div_offset, 1, 0,
2546609dbe4SPeter De Schrijver 				&clk_doubler_lock);
2556609dbe4SPeter De Schrijver 		clk = tegra_clk_register_periph_gate(data->gate_name,
2566609dbe4SPeter De Schrijver 				data->div_name, TEGRA_PERIPH_NO_RESET,
2576609dbe4SPeter De Schrijver 				clk_base, CLK_SET_RATE_PARENT, data->clk_num,
2586609dbe4SPeter De Schrijver 				periph_clk_enb_refcnt);
2596609dbe4SPeter De Schrijver 		*dt_clk = clk;
2606609dbe4SPeter De Schrijver 	}
2616609dbe4SPeter De Schrijver }
2626609dbe4SPeter De Schrijver 
263