1*9952f691SThomas 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