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.h> 196609dbe4SPeter De Schrijver #include <linux/clk-provider.h> 206609dbe4SPeter De Schrijver #include <linux/of.h> 216609dbe4SPeter De Schrijver #include <linux/of_address.h> 226609dbe4SPeter De Schrijver #include <linux/delay.h> 236609dbe4SPeter De Schrijver #include <linux/export.h> 246609dbe4SPeter De Schrijver #include <linux/clk/tegra.h> 256609dbe4SPeter De Schrijver 266609dbe4SPeter De Schrijver #include "clk.h" 276609dbe4SPeter De Schrijver #include "clk-id.h" 286609dbe4SPeter De Schrijver 296609dbe4SPeter De Schrijver #define AUDIO_SYNC_CLK_I2S0 0x4a0 306609dbe4SPeter De Schrijver #define AUDIO_SYNC_CLK_I2S1 0x4a4 316609dbe4SPeter De Schrijver #define AUDIO_SYNC_CLK_I2S2 0x4a8 326609dbe4SPeter De Schrijver #define AUDIO_SYNC_CLK_I2S3 0x4ac 336609dbe4SPeter De Schrijver #define AUDIO_SYNC_CLK_I2S4 0x4b0 346609dbe4SPeter De Schrijver #define AUDIO_SYNC_CLK_SPDIF 0x4b4 356609dbe4SPeter De Schrijver 366609dbe4SPeter De Schrijver #define AUDIO_SYNC_DOUBLER 0x49c 376609dbe4SPeter De Schrijver 386609dbe4SPeter De Schrijver #define PLLA_OUT 0xb4 396609dbe4SPeter De Schrijver 406609dbe4SPeter De Schrijver struct tegra_sync_source_initdata { 416609dbe4SPeter De Schrijver char *name; 426609dbe4SPeter De Schrijver unsigned long rate; 436609dbe4SPeter De Schrijver unsigned long max_rate; 446609dbe4SPeter De Schrijver int clk_id; 456609dbe4SPeter De Schrijver }; 466609dbe4SPeter De Schrijver 476609dbe4SPeter De Schrijver #define SYNC(_name) \ 486609dbe4SPeter De Schrijver {\ 496609dbe4SPeter De Schrijver .name = #_name,\ 506609dbe4SPeter De Schrijver .rate = 24000000,\ 516609dbe4SPeter De Schrijver .max_rate = 24000000,\ 526609dbe4SPeter De Schrijver .clk_id = tegra_clk_ ## _name,\ 536609dbe4SPeter De Schrijver } 546609dbe4SPeter De Schrijver 556609dbe4SPeter De Schrijver struct tegra_audio_clk_initdata { 566609dbe4SPeter De Schrijver char *gate_name; 576609dbe4SPeter De Schrijver char *mux_name; 586609dbe4SPeter De Schrijver u32 offset; 596609dbe4SPeter De Schrijver int gate_clk_id; 606609dbe4SPeter De Schrijver int mux_clk_id; 616609dbe4SPeter De Schrijver }; 626609dbe4SPeter De Schrijver 636609dbe4SPeter De Schrijver #define AUDIO(_name, _offset) \ 646609dbe4SPeter De Schrijver {\ 656609dbe4SPeter De Schrijver .gate_name = #_name,\ 666609dbe4SPeter De Schrijver .mux_name = #_name"_mux",\ 676609dbe4SPeter De Schrijver .offset = _offset,\ 686609dbe4SPeter De Schrijver .gate_clk_id = tegra_clk_ ## _name,\ 696609dbe4SPeter De Schrijver .mux_clk_id = tegra_clk_ ## _name ## _mux,\ 706609dbe4SPeter De Schrijver } 716609dbe4SPeter De Schrijver 726609dbe4SPeter De Schrijver struct tegra_audio2x_clk_initdata { 736609dbe4SPeter De Schrijver char *parent; 746609dbe4SPeter De Schrijver char *gate_name; 756609dbe4SPeter De Schrijver char *name_2x; 766609dbe4SPeter De Schrijver char *div_name; 776609dbe4SPeter De Schrijver int clk_id; 786609dbe4SPeter De Schrijver int clk_num; 796609dbe4SPeter De Schrijver u8 div_offset; 806609dbe4SPeter De Schrijver }; 816609dbe4SPeter De Schrijver 826609dbe4SPeter De Schrijver #define AUDIO2X(_name, _num, _offset) \ 836609dbe4SPeter De Schrijver {\ 846609dbe4SPeter De Schrijver .parent = #_name,\ 856609dbe4SPeter De Schrijver .gate_name = #_name"_2x",\ 866609dbe4SPeter De Schrijver .name_2x = #_name"_doubler",\ 876609dbe4SPeter De Schrijver .div_name = #_name"_div",\ 886609dbe4SPeter De Schrijver .clk_id = tegra_clk_ ## _name ## _2x,\ 896609dbe4SPeter De Schrijver .clk_num = _num,\ 906609dbe4SPeter De Schrijver .div_offset = _offset,\ 916609dbe4SPeter De Schrijver } 926609dbe4SPeter De Schrijver 936609dbe4SPeter De Schrijver static DEFINE_SPINLOCK(clk_doubler_lock); 946609dbe4SPeter De Schrijver 956609dbe4SPeter De Schrijver static const char *mux_audio_sync_clk[] = { "spdif_in_sync", "i2s0_sync", 966609dbe4SPeter De Schrijver "i2s1_sync", "i2s2_sync", "i2s3_sync", "i2s4_sync", "vimclk_sync", 976609dbe4SPeter De Schrijver }; 986609dbe4SPeter De Schrijver 996609dbe4SPeter De Schrijver static struct tegra_sync_source_initdata sync_source_clks[] __initdata = { 1006609dbe4SPeter De Schrijver SYNC(spdif_in_sync), 1016609dbe4SPeter De Schrijver SYNC(i2s0_sync), 1026609dbe4SPeter De Schrijver SYNC(i2s1_sync), 1036609dbe4SPeter De Schrijver SYNC(i2s2_sync), 1046609dbe4SPeter De Schrijver SYNC(i2s3_sync), 1056609dbe4SPeter De Schrijver SYNC(i2s4_sync), 1066609dbe4SPeter De Schrijver SYNC(vimclk_sync), 1076609dbe4SPeter De Schrijver }; 1086609dbe4SPeter De Schrijver 1096609dbe4SPeter De Schrijver static struct tegra_audio_clk_initdata audio_clks[] = { 1106609dbe4SPeter De Schrijver AUDIO(audio0, AUDIO_SYNC_CLK_I2S0), 1116609dbe4SPeter De Schrijver AUDIO(audio1, AUDIO_SYNC_CLK_I2S1), 1126609dbe4SPeter De Schrijver AUDIO(audio2, AUDIO_SYNC_CLK_I2S2), 1136609dbe4SPeter De Schrijver AUDIO(audio3, AUDIO_SYNC_CLK_I2S3), 1146609dbe4SPeter De Schrijver AUDIO(audio4, AUDIO_SYNC_CLK_I2S4), 1156609dbe4SPeter De Schrijver AUDIO(spdif, AUDIO_SYNC_CLK_SPDIF), 1166609dbe4SPeter De Schrijver }; 1176609dbe4SPeter De Schrijver 1186609dbe4SPeter De Schrijver static struct tegra_audio2x_clk_initdata audio2x_clks[] = { 1196609dbe4SPeter De Schrijver AUDIO2X(audio0, 113, 24), 1206609dbe4SPeter De Schrijver AUDIO2X(audio1, 114, 25), 1216609dbe4SPeter De Schrijver AUDIO2X(audio2, 115, 26), 1226609dbe4SPeter De Schrijver AUDIO2X(audio3, 116, 27), 1236609dbe4SPeter De Schrijver AUDIO2X(audio4, 117, 28), 1246609dbe4SPeter De Schrijver AUDIO2X(spdif, 118, 29), 1256609dbe4SPeter De Schrijver }; 1266609dbe4SPeter De Schrijver 1276609dbe4SPeter De Schrijver void __init tegra_audio_clk_init(void __iomem *clk_base, 1286609dbe4SPeter De Schrijver void __iomem *pmc_base, struct tegra_clk *tegra_clks, 1296609dbe4SPeter De Schrijver struct tegra_clk_pll_params *pll_a_params) 1306609dbe4SPeter De Schrijver { 1316609dbe4SPeter De Schrijver struct clk *clk; 1326609dbe4SPeter De Schrijver struct clk **dt_clk; 1336609dbe4SPeter De Schrijver int i; 1346609dbe4SPeter De Schrijver 1356609dbe4SPeter De Schrijver /* PLLA */ 1366609dbe4SPeter De Schrijver dt_clk = tegra_lookup_dt_id(tegra_clk_pll_a, tegra_clks); 1376609dbe4SPeter De Schrijver if (dt_clk) { 1386609dbe4SPeter De Schrijver clk = tegra_clk_register_pll("pll_a", "pll_p_out1", clk_base, 1396609dbe4SPeter De Schrijver pmc_base, 0, pll_a_params, NULL); 1406609dbe4SPeter De Schrijver *dt_clk = clk; 1416609dbe4SPeter De Schrijver } 1426609dbe4SPeter De Schrijver 1436609dbe4SPeter De Schrijver /* PLLA_OUT0 */ 1446609dbe4SPeter De Schrijver dt_clk = tegra_lookup_dt_id(tegra_clk_pll_a_out0, tegra_clks); 1456609dbe4SPeter De Schrijver if (dt_clk) { 1466609dbe4SPeter De Schrijver clk = tegra_clk_register_divider("pll_a_out0_div", "pll_a", 1476609dbe4SPeter De Schrijver clk_base + PLLA_OUT, 0, TEGRA_DIVIDER_ROUND_UP, 1486609dbe4SPeter De Schrijver 8, 8, 1, NULL); 1496609dbe4SPeter De Schrijver clk = tegra_clk_register_pll_out("pll_a_out0", "pll_a_out0_div", 1506609dbe4SPeter De Schrijver clk_base + PLLA_OUT, 1, 0, CLK_IGNORE_UNUSED | 1516609dbe4SPeter De Schrijver CLK_SET_RATE_PARENT, 0, NULL); 1526609dbe4SPeter De Schrijver *dt_clk = clk; 1536609dbe4SPeter De Schrijver } 1546609dbe4SPeter De Schrijver 1556609dbe4SPeter De Schrijver for (i = 0; i < ARRAY_SIZE(sync_source_clks); i++) { 1566609dbe4SPeter De Schrijver struct tegra_sync_source_initdata *data; 1576609dbe4SPeter De Schrijver 1586609dbe4SPeter De Schrijver data = &sync_source_clks[i]; 1596609dbe4SPeter De Schrijver 1606609dbe4SPeter De Schrijver dt_clk = tegra_lookup_dt_id(data->clk_id, tegra_clks); 1616609dbe4SPeter De Schrijver if (!dt_clk) 1626609dbe4SPeter De Schrijver continue; 1636609dbe4SPeter De Schrijver 1646609dbe4SPeter De Schrijver clk = tegra_clk_register_sync_source(data->name, 1656609dbe4SPeter De Schrijver data->rate, data->max_rate); 1666609dbe4SPeter De Schrijver *dt_clk = clk; 1676609dbe4SPeter De Schrijver } 1686609dbe4SPeter De Schrijver 1696609dbe4SPeter De Schrijver for (i = 0; i < ARRAY_SIZE(audio_clks); i++) { 1706609dbe4SPeter De Schrijver struct tegra_audio_clk_initdata *data; 1716609dbe4SPeter De Schrijver 1726609dbe4SPeter De Schrijver data = &audio_clks[i]; 1736609dbe4SPeter De Schrijver dt_clk = tegra_lookup_dt_id(data->mux_clk_id, tegra_clks); 1746609dbe4SPeter De Schrijver 1756609dbe4SPeter De Schrijver if (!dt_clk) 1766609dbe4SPeter De Schrijver continue; 1776609dbe4SPeter De Schrijver clk = clk_register_mux(NULL, data->mux_name, mux_audio_sync_clk, 1786609dbe4SPeter De Schrijver ARRAY_SIZE(mux_audio_sync_clk), 1796609dbe4SPeter De Schrijver CLK_SET_RATE_NO_REPARENT, 1806609dbe4SPeter De Schrijver clk_base + data->offset, 0, 3, 0, 1816609dbe4SPeter De Schrijver NULL); 1826609dbe4SPeter De Schrijver *dt_clk = clk; 1836609dbe4SPeter De Schrijver 1846609dbe4SPeter De Schrijver dt_clk = tegra_lookup_dt_id(data->gate_clk_id, tegra_clks); 1856609dbe4SPeter De Schrijver if (!dt_clk) 1866609dbe4SPeter De Schrijver continue; 1876609dbe4SPeter De Schrijver 1886609dbe4SPeter De Schrijver clk = clk_register_gate(NULL, data->gate_name, data->mux_name, 1896609dbe4SPeter De Schrijver 0, clk_base + data->offset, 4, 1906609dbe4SPeter De Schrijver CLK_GATE_SET_TO_DISABLE, NULL); 1916609dbe4SPeter De Schrijver *dt_clk = clk; 1926609dbe4SPeter De Schrijver } 1936609dbe4SPeter De Schrijver 1946609dbe4SPeter De Schrijver for (i = 0; i < ARRAY_SIZE(audio2x_clks); i++) { 1956609dbe4SPeter De Schrijver struct tegra_audio2x_clk_initdata *data; 1966609dbe4SPeter De Schrijver 1976609dbe4SPeter De Schrijver data = &audio2x_clks[i]; 1986609dbe4SPeter De Schrijver dt_clk = tegra_lookup_dt_id(data->clk_id, tegra_clks); 1996609dbe4SPeter De Schrijver if (!dt_clk) 2006609dbe4SPeter De Schrijver continue; 2016609dbe4SPeter De Schrijver 2026609dbe4SPeter De Schrijver clk = clk_register_fixed_factor(NULL, data->name_2x, 2036609dbe4SPeter De Schrijver data->parent, CLK_SET_RATE_PARENT, 2, 1); 2046609dbe4SPeter De Schrijver clk = tegra_clk_register_divider(data->div_name, 2056609dbe4SPeter De Schrijver data->name_2x, clk_base + AUDIO_SYNC_DOUBLER, 2066609dbe4SPeter De Schrijver 0, 0, data->div_offset, 1, 0, 2076609dbe4SPeter De Schrijver &clk_doubler_lock); 2086609dbe4SPeter De Schrijver clk = tegra_clk_register_periph_gate(data->gate_name, 2096609dbe4SPeter De Schrijver data->div_name, TEGRA_PERIPH_NO_RESET, 2106609dbe4SPeter De Schrijver clk_base, CLK_SET_RATE_PARENT, data->clk_num, 2116609dbe4SPeter De Schrijver periph_clk_enb_refcnt); 2126609dbe4SPeter De Schrijver *dt_clk = clk; 2136609dbe4SPeter De Schrijver } 2146609dbe4SPeter De Schrijver } 2156609dbe4SPeter De Schrijver 216