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
346609dbe4SPeter De Schrijver 
356609dbe4SPeter De Schrijver #define AUDIO_SYNC_DOUBLER 0x49c
366609dbe4SPeter De Schrijver 
376609dbe4SPeter De Schrijver #define PLLA_OUT 0xb4
386609dbe4SPeter De Schrijver 
396609dbe4SPeter De Schrijver struct tegra_sync_source_initdata {
406609dbe4SPeter De Schrijver 	char		*name;
416609dbe4SPeter De Schrijver 	unsigned long	rate;
426609dbe4SPeter De Schrijver 	unsigned long	max_rate;
436609dbe4SPeter De Schrijver 	int		clk_id;
446609dbe4SPeter De Schrijver };
456609dbe4SPeter De Schrijver 
466609dbe4SPeter De Schrijver #define SYNC(_name) \
476609dbe4SPeter De Schrijver 	{\
486609dbe4SPeter De Schrijver 		.name		= #_name,\
496609dbe4SPeter De Schrijver 		.rate		= 24000000,\
506609dbe4SPeter De Schrijver 		.max_rate	= 24000000,\
516609dbe4SPeter De Schrijver 		.clk_id		= tegra_clk_ ## _name,\
526609dbe4SPeter De Schrijver 	}
536609dbe4SPeter De Schrijver 
546609dbe4SPeter De Schrijver struct tegra_audio_clk_initdata {
556609dbe4SPeter De Schrijver 	char		*gate_name;
566609dbe4SPeter De Schrijver 	char		*mux_name;
576609dbe4SPeter De Schrijver 	u32		offset;
586609dbe4SPeter De Schrijver 	int		gate_clk_id;
596609dbe4SPeter De Schrijver 	int		mux_clk_id;
606609dbe4SPeter De Schrijver };
616609dbe4SPeter De Schrijver 
626609dbe4SPeter De Schrijver #define AUDIO(_name, _offset) \
636609dbe4SPeter De Schrijver 	{\
646609dbe4SPeter De Schrijver 		.gate_name	= #_name,\
656609dbe4SPeter De Schrijver 		.mux_name	= #_name"_mux",\
666609dbe4SPeter De Schrijver 		.offset		= _offset,\
676609dbe4SPeter De Schrijver 		.gate_clk_id	= tegra_clk_ ## _name,\
686609dbe4SPeter De Schrijver 		.mux_clk_id	= tegra_clk_ ## _name ## _mux,\
696609dbe4SPeter De Schrijver 	}
706609dbe4SPeter De Schrijver 
716609dbe4SPeter De Schrijver struct tegra_audio2x_clk_initdata {
726609dbe4SPeter De Schrijver 	char		*parent;
736609dbe4SPeter De Schrijver 	char		*gate_name;
746609dbe4SPeter De Schrijver 	char		*name_2x;
756609dbe4SPeter De Schrijver 	char		*div_name;
766609dbe4SPeter De Schrijver 	int		clk_id;
776609dbe4SPeter De Schrijver 	int		clk_num;
786609dbe4SPeter De Schrijver 	u8		div_offset;
796609dbe4SPeter De Schrijver };
806609dbe4SPeter De Schrijver 
816609dbe4SPeter De Schrijver #define AUDIO2X(_name, _num, _offset) \
826609dbe4SPeter De Schrijver 	{\
836609dbe4SPeter De Schrijver 		.parent		= #_name,\
846609dbe4SPeter De Schrijver 		.gate_name	= #_name"_2x",\
856609dbe4SPeter De Schrijver 		.name_2x	= #_name"_doubler",\
866609dbe4SPeter De Schrijver 		.div_name	= #_name"_div",\
876609dbe4SPeter De Schrijver 		.clk_id		= tegra_clk_ ## _name ## _2x,\
886609dbe4SPeter De Schrijver 		.clk_num	= _num,\
896609dbe4SPeter De Schrijver 		.div_offset	= _offset,\
906609dbe4SPeter De Schrijver 	}
916609dbe4SPeter De Schrijver 
926609dbe4SPeter De Schrijver static DEFINE_SPINLOCK(clk_doubler_lock);
936609dbe4SPeter De Schrijver 
946609dbe4SPeter De Schrijver static const char *mux_audio_sync_clk[] = { "spdif_in_sync", "i2s0_sync",
956609dbe4SPeter De Schrijver 	"i2s1_sync", "i2s2_sync", "i2s3_sync", "i2s4_sync", "vimclk_sync",
966609dbe4SPeter De Schrijver };
976609dbe4SPeter De Schrijver 
986609dbe4SPeter De Schrijver static struct tegra_sync_source_initdata sync_source_clks[] __initdata = {
996609dbe4SPeter De Schrijver 	SYNC(spdif_in_sync),
1006609dbe4SPeter De Schrijver 	SYNC(i2s0_sync),
1016609dbe4SPeter De Schrijver 	SYNC(i2s1_sync),
1026609dbe4SPeter De Schrijver 	SYNC(i2s2_sync),
1036609dbe4SPeter De Schrijver 	SYNC(i2s3_sync),
1046609dbe4SPeter De Schrijver 	SYNC(i2s4_sync),
1056609dbe4SPeter De Schrijver 	SYNC(vimclk_sync),
1066609dbe4SPeter De Schrijver };
1076609dbe4SPeter De Schrijver 
1086609dbe4SPeter De Schrijver static struct tegra_audio_clk_initdata audio_clks[] = {
1096609dbe4SPeter De Schrijver 	AUDIO(audio0, AUDIO_SYNC_CLK_I2S0),
1106609dbe4SPeter De Schrijver 	AUDIO(audio1, AUDIO_SYNC_CLK_I2S1),
1116609dbe4SPeter De Schrijver 	AUDIO(audio2, AUDIO_SYNC_CLK_I2S2),
1126609dbe4SPeter De Schrijver 	AUDIO(audio3, AUDIO_SYNC_CLK_I2S3),
1136609dbe4SPeter De Schrijver 	AUDIO(audio4, AUDIO_SYNC_CLK_I2S4),
1146609dbe4SPeter De Schrijver 	AUDIO(spdif, AUDIO_SYNC_CLK_SPDIF),
1156609dbe4SPeter De Schrijver };
1166609dbe4SPeter De Schrijver 
1176609dbe4SPeter De Schrijver static struct tegra_audio2x_clk_initdata audio2x_clks[] = {
1186609dbe4SPeter De Schrijver 	AUDIO2X(audio0, 113, 24),
1196609dbe4SPeter De Schrijver 	AUDIO2X(audio1, 114, 25),
1206609dbe4SPeter De Schrijver 	AUDIO2X(audio2, 115, 26),
1216609dbe4SPeter De Schrijver 	AUDIO2X(audio3, 116, 27),
1226609dbe4SPeter De Schrijver 	AUDIO2X(audio4, 117, 28),
1236609dbe4SPeter De Schrijver 	AUDIO2X(spdif, 118, 29),
1246609dbe4SPeter De Schrijver };
1256609dbe4SPeter De Schrijver 
1266609dbe4SPeter De Schrijver void __init tegra_audio_clk_init(void __iomem *clk_base,
1276609dbe4SPeter De Schrijver 			void __iomem *pmc_base, struct tegra_clk *tegra_clks,
12888d909beSRhyland Klein 			struct tegra_audio_clk_info *audio_info,
12988d909beSRhyland Klein 			unsigned int num_plls)
1306609dbe4SPeter De Schrijver {
1316609dbe4SPeter De Schrijver 	struct clk *clk;
1326609dbe4SPeter De Schrijver 	struct clk **dt_clk;
1336609dbe4SPeter De Schrijver 	int i;
1346609dbe4SPeter De Schrijver 
13588d909beSRhyland Klein 	if (!audio_info || num_plls < 1) {
13688d909beSRhyland Klein 		pr_err("No audio data passed to tegra_audio_clk_init\n");
13788d909beSRhyland Klein 		WARN_ON(1);
13888d909beSRhyland Klein 		return;
13988d909beSRhyland Klein 	}
14088d909beSRhyland Klein 
14188d909beSRhyland Klein 	for (i = 0; i < num_plls; i++) {
14288d909beSRhyland Klein 		struct tegra_audio_clk_info *info = &audio_info[i];
14388d909beSRhyland Klein 
14488d909beSRhyland Klein 		dt_clk = tegra_lookup_dt_id(info->clk_id, tegra_clks);
1456609dbe4SPeter De Schrijver 		if (dt_clk) {
14688d909beSRhyland Klein 			clk = tegra_clk_register_pll(info->name, info->parent,
14788d909beSRhyland Klein 					clk_base, pmc_base, 0, info->pll_params,
14888d909beSRhyland Klein 					NULL);
1496609dbe4SPeter De Schrijver 			*dt_clk = clk;
1506609dbe4SPeter De Schrijver 		}
15188d909beSRhyland Klein 	}
1526609dbe4SPeter De Schrijver 
1536609dbe4SPeter De Schrijver 	/* PLLA_OUT0 */
1546609dbe4SPeter De Schrijver 	dt_clk = tegra_lookup_dt_id(tegra_clk_pll_a_out0, tegra_clks);
1556609dbe4SPeter De Schrijver 	if (dt_clk) {
1566609dbe4SPeter De Schrijver 		clk = tegra_clk_register_divider("pll_a_out0_div", "pll_a",
1576609dbe4SPeter De Schrijver 				clk_base + PLLA_OUT, 0, TEGRA_DIVIDER_ROUND_UP,
1586609dbe4SPeter De Schrijver 				8, 8, 1, NULL);
1596609dbe4SPeter De Schrijver 		clk = tegra_clk_register_pll_out("pll_a_out0", "pll_a_out0_div",
1606609dbe4SPeter De Schrijver 				clk_base + PLLA_OUT, 1, 0, CLK_IGNORE_UNUSED |
1616609dbe4SPeter De Schrijver 				CLK_SET_RATE_PARENT, 0, NULL);
1626609dbe4SPeter De Schrijver 		*dt_clk = clk;
1636609dbe4SPeter De Schrijver 	}
1646609dbe4SPeter De Schrijver 
1656609dbe4SPeter De Schrijver 	for (i = 0; i < ARRAY_SIZE(sync_source_clks); i++) {
1666609dbe4SPeter De Schrijver 		struct tegra_sync_source_initdata *data;
1676609dbe4SPeter De Schrijver 
1686609dbe4SPeter De Schrijver 		data = &sync_source_clks[i];
1696609dbe4SPeter De Schrijver 
1706609dbe4SPeter De Schrijver 		dt_clk = tegra_lookup_dt_id(data->clk_id, tegra_clks);
1716609dbe4SPeter De Schrijver 		if (!dt_clk)
1726609dbe4SPeter De Schrijver 			continue;
1736609dbe4SPeter De Schrijver 
1746609dbe4SPeter De Schrijver 		clk = tegra_clk_register_sync_source(data->name,
1756609dbe4SPeter De Schrijver 					data->rate, data->max_rate);
1766609dbe4SPeter De Schrijver 		*dt_clk = clk;
1776609dbe4SPeter De Schrijver 	}
1786609dbe4SPeter De Schrijver 
1796609dbe4SPeter De Schrijver 	for (i = 0; i < ARRAY_SIZE(audio_clks); i++) {
1806609dbe4SPeter De Schrijver 		struct tegra_audio_clk_initdata *data;
1816609dbe4SPeter De Schrijver 
1826609dbe4SPeter De Schrijver 		data = &audio_clks[i];
1836609dbe4SPeter De Schrijver 		dt_clk = tegra_lookup_dt_id(data->mux_clk_id, tegra_clks);
1846609dbe4SPeter De Schrijver 
1856609dbe4SPeter De Schrijver 		if (!dt_clk)
1866609dbe4SPeter De Schrijver 			continue;
1876609dbe4SPeter De Schrijver 		clk = clk_register_mux(NULL, data->mux_name, mux_audio_sync_clk,
1886609dbe4SPeter De Schrijver 					ARRAY_SIZE(mux_audio_sync_clk),
1896609dbe4SPeter De Schrijver 					CLK_SET_RATE_NO_REPARENT,
1906609dbe4SPeter De Schrijver 					clk_base + data->offset, 0, 3, 0,
1916609dbe4SPeter De Schrijver 					NULL);
1926609dbe4SPeter De Schrijver 		*dt_clk = clk;
1936609dbe4SPeter De Schrijver 
1946609dbe4SPeter De Schrijver 		dt_clk = tegra_lookup_dt_id(data->gate_clk_id, tegra_clks);
1956609dbe4SPeter De Schrijver 		if (!dt_clk)
1966609dbe4SPeter De Schrijver 			continue;
1976609dbe4SPeter De Schrijver 
1986609dbe4SPeter De Schrijver 		clk = clk_register_gate(NULL, data->gate_name, data->mux_name,
1996609dbe4SPeter De Schrijver 					0, clk_base + data->offset, 4,
2006609dbe4SPeter De Schrijver 					CLK_GATE_SET_TO_DISABLE, NULL);
2016609dbe4SPeter De Schrijver 		*dt_clk = clk;
2026609dbe4SPeter De Schrijver 	}
2036609dbe4SPeter De Schrijver 
2046609dbe4SPeter De Schrijver 	for (i = 0; i < ARRAY_SIZE(audio2x_clks); i++) {
2056609dbe4SPeter De Schrijver 		struct tegra_audio2x_clk_initdata *data;
2066609dbe4SPeter De Schrijver 
2076609dbe4SPeter De Schrijver 		data = &audio2x_clks[i];
2086609dbe4SPeter De Schrijver 		dt_clk = tegra_lookup_dt_id(data->clk_id, tegra_clks);
2096609dbe4SPeter De Schrijver 		if (!dt_clk)
2106609dbe4SPeter De Schrijver 			continue;
2116609dbe4SPeter De Schrijver 
2126609dbe4SPeter De Schrijver 		clk = clk_register_fixed_factor(NULL, data->name_2x,
2136609dbe4SPeter De Schrijver 				data->parent, CLK_SET_RATE_PARENT, 2, 1);
2146609dbe4SPeter De Schrijver 		clk = tegra_clk_register_divider(data->div_name,
2156609dbe4SPeter De Schrijver 				data->name_2x, clk_base + AUDIO_SYNC_DOUBLER,
2166609dbe4SPeter De Schrijver 				0, 0, data->div_offset, 1, 0,
2176609dbe4SPeter De Schrijver 				&clk_doubler_lock);
2186609dbe4SPeter De Schrijver 		clk = tegra_clk_register_periph_gate(data->gate_name,
2196609dbe4SPeter De Schrijver 				data->div_name, TEGRA_PERIPH_NO_RESET,
2206609dbe4SPeter De Schrijver 				clk_base, CLK_SET_RATE_PARENT, data->clk_num,
2216609dbe4SPeter De Schrijver 				periph_clk_enb_refcnt);
2226609dbe4SPeter De Schrijver 		*dt_clk = clk;
2236609dbe4SPeter De Schrijver 	}
2246609dbe4SPeter De Schrijver }
2256609dbe4SPeter De Schrijver 
226