xref: /openbmc/linux/drivers/clk/tegra/clk.c (revision c900529f3d9161bfde5cca0754f83b4d3c3e0220)
19952f691SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
28f8f484bSPrashant Gaikwad /*
38f8f484bSPrashant Gaikwad  * Copyright (c) 2012, NVIDIA CORPORATION.  All rights reserved.
48f8f484bSPrashant Gaikwad  */
58f8f484bSPrashant Gaikwad 
6584ac4e9SStephen Boyd #include <linux/clkdev.h>
78f8f484bSPrashant Gaikwad #include <linux/clk.h>
88f8f484bSPrashant Gaikwad #include <linux/clk-provider.h>
94236e752SMikko Perttunen #include <linux/delay.h>
1062e59c4eSStephen Boyd #include <linux/io.h>
1161fd290dSPrashant Gaikwad #include <linux/of.h>
12a96cbb14SRob Herring #include <linux/of_platform.h>
1361fd290dSPrashant Gaikwad #include <linux/clk/tegra.h>
14b1bc04a2SDmitry Osipenko #include <linux/platform_device.h>
15b1bc04a2SDmitry Osipenko #include <linux/pm_runtime.h>
166d5b988eSStephen Warren #include <linux/reset-controller.h>
17*28df1500SAndy Shevchenko #include <linux/string_helpers.h>
18306a7f91SThierry Reding 
19306a7f91SThierry Reding #include <soc/tegra/fuse.h>
208f8f484bSPrashant Gaikwad 
218f8f484bSPrashant Gaikwad #include "clk.h"
228f8f484bSPrashant Gaikwad 
2361fd290dSPrashant Gaikwad /* Global data of Tegra CPU CAR ops */
24b1bc04a2SDmitry Osipenko static struct device_node *tegra_car_np;
256a676fa0SPeter De Schrijver static struct tegra_cpu_car_ops dummy_car_ops;
266a676fa0SPeter De Schrijver struct tegra_cpu_car_ops *tegra_cpu_car_ops = &dummy_car_ops;
2761fd290dSPrashant Gaikwad 
28343a607cSPeter De Schrijver int *periph_clk_enb_refcnt;
29d5ff89a8SPeter De Schrijver static int periph_banks;
30535f296dSSowjanya Komatineni static u32 *periph_state_ctx;
31343a607cSPeter De Schrijver static struct clk **clks;
32343a607cSPeter De Schrijver static int clk_num;
33343a607cSPeter De Schrijver static struct clk_onecell_data clk_data;
34d5ff89a8SPeter De Schrijver 
3566b6f3d0SMikko Perttunen /* Handlers for SoC-specific reset lines */
3666b6f3d0SMikko Perttunen static int (*special_reset_assert)(unsigned long);
3766b6f3d0SMikko Perttunen static int (*special_reset_deassert)(unsigned long);
3866b6f3d0SMikko Perttunen static unsigned int num_special_reset;
3966b6f3d0SMikko Perttunen 
407e14f223SThierry Reding static const struct tegra_clk_periph_regs periph_regs[] = {
41d5ff89a8SPeter De Schrijver 	[0] = {
42d5ff89a8SPeter De Schrijver 		.enb_reg = CLK_OUT_ENB_L,
43d5ff89a8SPeter De Schrijver 		.enb_set_reg = CLK_OUT_ENB_SET_L,
44d5ff89a8SPeter De Schrijver 		.enb_clr_reg = CLK_OUT_ENB_CLR_L,
45d5ff89a8SPeter De Schrijver 		.rst_reg = RST_DEVICES_L,
46d5ff89a8SPeter De Schrijver 		.rst_set_reg = RST_DEVICES_SET_L,
47d5ff89a8SPeter De Schrijver 		.rst_clr_reg = RST_DEVICES_CLR_L,
48d5ff89a8SPeter De Schrijver 	},
49d5ff89a8SPeter De Schrijver 	[1] = {
50d5ff89a8SPeter De Schrijver 		.enb_reg = CLK_OUT_ENB_H,
51d5ff89a8SPeter De Schrijver 		.enb_set_reg = CLK_OUT_ENB_SET_H,
52d5ff89a8SPeter De Schrijver 		.enb_clr_reg = CLK_OUT_ENB_CLR_H,
53d5ff89a8SPeter De Schrijver 		.rst_reg = RST_DEVICES_H,
54d5ff89a8SPeter De Schrijver 		.rst_set_reg = RST_DEVICES_SET_H,
55d5ff89a8SPeter De Schrijver 		.rst_clr_reg = RST_DEVICES_CLR_H,
56d5ff89a8SPeter De Schrijver 	},
57d5ff89a8SPeter De Schrijver 	[2] = {
58d5ff89a8SPeter De Schrijver 		.enb_reg = CLK_OUT_ENB_U,
59d5ff89a8SPeter De Schrijver 		.enb_set_reg = CLK_OUT_ENB_SET_U,
60d5ff89a8SPeter De Schrijver 		.enb_clr_reg = CLK_OUT_ENB_CLR_U,
61d5ff89a8SPeter De Schrijver 		.rst_reg = RST_DEVICES_U,
62d5ff89a8SPeter De Schrijver 		.rst_set_reg = RST_DEVICES_SET_U,
63d5ff89a8SPeter De Schrijver 		.rst_clr_reg = RST_DEVICES_CLR_U,
64d5ff89a8SPeter De Schrijver 	},
65d5ff89a8SPeter De Schrijver 	[3] = {
66d5ff89a8SPeter De Schrijver 		.enb_reg = CLK_OUT_ENB_V,
67d5ff89a8SPeter De Schrijver 		.enb_set_reg = CLK_OUT_ENB_SET_V,
68d5ff89a8SPeter De Schrijver 		.enb_clr_reg = CLK_OUT_ENB_CLR_V,
69d5ff89a8SPeter De Schrijver 		.rst_reg = RST_DEVICES_V,
70d5ff89a8SPeter De Schrijver 		.rst_set_reg = RST_DEVICES_SET_V,
71d5ff89a8SPeter De Schrijver 		.rst_clr_reg = RST_DEVICES_CLR_V,
72d5ff89a8SPeter De Schrijver 	},
73d5ff89a8SPeter De Schrijver 	[4] = {
74d5ff89a8SPeter De Schrijver 		.enb_reg = CLK_OUT_ENB_W,
75d5ff89a8SPeter De Schrijver 		.enb_set_reg = CLK_OUT_ENB_SET_W,
76d5ff89a8SPeter De Schrijver 		.enb_clr_reg = CLK_OUT_ENB_CLR_W,
77d5ff89a8SPeter De Schrijver 		.rst_reg = RST_DEVICES_W,
78d5ff89a8SPeter De Schrijver 		.rst_set_reg = RST_DEVICES_SET_W,
79d5ff89a8SPeter De Schrijver 		.rst_clr_reg = RST_DEVICES_CLR_W,
80d5ff89a8SPeter De Schrijver 	},
812b239077SPeter De Schrijver 	[5] = {
822b239077SPeter De Schrijver 		.enb_reg = CLK_OUT_ENB_X,
832b239077SPeter De Schrijver 		.enb_set_reg = CLK_OUT_ENB_SET_X,
842b239077SPeter De Schrijver 		.enb_clr_reg = CLK_OUT_ENB_CLR_X,
852b239077SPeter De Schrijver 		.rst_reg = RST_DEVICES_X,
862b239077SPeter De Schrijver 		.rst_set_reg = RST_DEVICES_SET_X,
872b239077SPeter De Schrijver 		.rst_clr_reg = RST_DEVICES_CLR_X,
882b239077SPeter De Schrijver 	},
89699b477aSThierry Reding 	[6] = {
90699b477aSThierry Reding 		.enb_reg = CLK_OUT_ENB_Y,
91699b477aSThierry Reding 		.enb_set_reg = CLK_OUT_ENB_SET_Y,
92699b477aSThierry Reding 		.enb_clr_reg = CLK_OUT_ENB_CLR_Y,
93699b477aSThierry Reding 		.rst_reg = RST_DEVICES_Y,
94699b477aSThierry Reding 		.rst_set_reg = RST_DEVICES_SET_Y,
95699b477aSThierry Reding 		.rst_clr_reg = RST_DEVICES_CLR_Y,
96699b477aSThierry Reding 	},
97d5ff89a8SPeter De Schrijver };
98d5ff89a8SPeter De Schrijver 
996d5b988eSStephen Warren static void __iomem *clk_base;
1006d5b988eSStephen Warren 
tegra_clk_rst_assert(struct reset_controller_dev * rcdev,unsigned long id)1016d5b988eSStephen Warren static int tegra_clk_rst_assert(struct reset_controller_dev *rcdev,
1026d5b988eSStephen Warren 		unsigned long id)
1036d5b988eSStephen Warren {
1046d5b988eSStephen Warren 	/*
1056d5b988eSStephen Warren 	 * If peripheral is on the APB bus then we must read the APB bus to
1066d5b988eSStephen Warren 	 * flush the write operation in apb bus. This will avoid peripheral
1076d5b988eSStephen Warren 	 * access after disabling clock. Since the reset driver has no
1086d5b988eSStephen Warren 	 * knowledge of which reset IDs represent which devices, simply do
1096d5b988eSStephen Warren 	 * this all the time.
1106d5b988eSStephen Warren 	 */
1116d5b988eSStephen Warren 	tegra_read_chipid();
1126d5b988eSStephen Warren 
11366b6f3d0SMikko Perttunen 	if (id < periph_banks * 32) {
1146d5b988eSStephen Warren 		writel_relaxed(BIT(id % 32),
1156d5b988eSStephen Warren 			       clk_base + periph_regs[id / 32].rst_set_reg);
1166d5b988eSStephen Warren 		return 0;
11766b6f3d0SMikko Perttunen 	} else if (id < periph_banks * 32 + num_special_reset) {
11866b6f3d0SMikko Perttunen 		return special_reset_assert(id);
11966b6f3d0SMikko Perttunen 	}
12066b6f3d0SMikko Perttunen 
12166b6f3d0SMikko Perttunen 	return -EINVAL;
1226d5b988eSStephen Warren }
1236d5b988eSStephen Warren 
tegra_clk_rst_deassert(struct reset_controller_dev * rcdev,unsigned long id)1246d5b988eSStephen Warren static int tegra_clk_rst_deassert(struct reset_controller_dev *rcdev,
1256d5b988eSStephen Warren 		unsigned long id)
1266d5b988eSStephen Warren {
12766b6f3d0SMikko Perttunen 	if (id < periph_banks * 32) {
1286d5b988eSStephen Warren 		writel_relaxed(BIT(id % 32),
1296d5b988eSStephen Warren 			       clk_base + periph_regs[id / 32].rst_clr_reg);
1306d5b988eSStephen Warren 		return 0;
13166b6f3d0SMikko Perttunen 	} else if (id < periph_banks * 32 + num_special_reset) {
13266b6f3d0SMikko Perttunen 		return special_reset_deassert(id);
13366b6f3d0SMikko Perttunen 	}
13466b6f3d0SMikko Perttunen 
13566b6f3d0SMikko Perttunen 	return -EINVAL;
1366d5b988eSStephen Warren }
1376d5b988eSStephen Warren 
tegra_clk_rst_reset(struct reset_controller_dev * rcdev,unsigned long id)1384236e752SMikko Perttunen static int tegra_clk_rst_reset(struct reset_controller_dev *rcdev,
1394236e752SMikko Perttunen 		unsigned long id)
1404236e752SMikko Perttunen {
1414236e752SMikko Perttunen 	int err;
1424236e752SMikko Perttunen 
1434236e752SMikko Perttunen 	err = tegra_clk_rst_assert(rcdev, id);
1444236e752SMikko Perttunen 	if (err)
1454236e752SMikko Perttunen 		return err;
1464236e752SMikko Perttunen 
1474236e752SMikko Perttunen 	udelay(1);
1484236e752SMikko Perttunen 
1494236e752SMikko Perttunen 	return tegra_clk_rst_deassert(rcdev, id);
1504236e752SMikko Perttunen }
1514236e752SMikko Perttunen 
get_reg_bank(int clkid)1527e14f223SThierry Reding const struct tegra_clk_periph_regs *get_reg_bank(int clkid)
153d5ff89a8SPeter De Schrijver {
154d5ff89a8SPeter De Schrijver 	int reg_bank = clkid / 32;
155d5ff89a8SPeter De Schrijver 
156d5ff89a8SPeter De Schrijver 	if (reg_bank < periph_banks)
157d5ff89a8SPeter De Schrijver 		return &periph_regs[reg_bank];
158d5ff89a8SPeter De Schrijver 	else {
159d5ff89a8SPeter De Schrijver 		WARN_ON(1);
160d5ff89a8SPeter De Schrijver 		return NULL;
161d5ff89a8SPeter De Schrijver 	}
162d5ff89a8SPeter De Schrijver }
163d5ff89a8SPeter De Schrijver 
tegra_clk_set_pllp_out_cpu(bool enable)16468a14a56SSowjanya Komatineni void tegra_clk_set_pllp_out_cpu(bool enable)
16568a14a56SSowjanya Komatineni {
16668a14a56SSowjanya Komatineni 	u32 val;
16768a14a56SSowjanya Komatineni 
16868a14a56SSowjanya Komatineni 	val = readl_relaxed(clk_base + CLK_OUT_ENB_Y);
16968a14a56SSowjanya Komatineni 	if (enable)
17068a14a56SSowjanya Komatineni 		val |= CLK_ENB_PLLP_OUT_CPU;
17168a14a56SSowjanya Komatineni 	else
17268a14a56SSowjanya Komatineni 		val &= ~CLK_ENB_PLLP_OUT_CPU;
17368a14a56SSowjanya Komatineni 
17468a14a56SSowjanya Komatineni 	writel_relaxed(val, clk_base + CLK_OUT_ENB_Y);
17568a14a56SSowjanya Komatineni }
17668a14a56SSowjanya Komatineni 
tegra_clk_periph_suspend(void)177535f296dSSowjanya Komatineni void tegra_clk_periph_suspend(void)
178535f296dSSowjanya Komatineni {
179535f296dSSowjanya Komatineni 	unsigned int i, idx;
180535f296dSSowjanya Komatineni 
181535f296dSSowjanya Komatineni 	idx = 0;
182535f296dSSowjanya Komatineni 	for (i = 0; i < periph_banks; i++, idx++)
183535f296dSSowjanya Komatineni 		periph_state_ctx[idx] =
184535f296dSSowjanya Komatineni 			readl_relaxed(clk_base + periph_regs[i].enb_reg);
185535f296dSSowjanya Komatineni 
186535f296dSSowjanya Komatineni 	for (i = 0; i < periph_banks; i++, idx++)
187535f296dSSowjanya Komatineni 		periph_state_ctx[idx] =
188535f296dSSowjanya Komatineni 			readl_relaxed(clk_base + periph_regs[i].rst_reg);
189535f296dSSowjanya Komatineni }
190535f296dSSowjanya Komatineni 
tegra_clk_periph_resume(void)191535f296dSSowjanya Komatineni void tegra_clk_periph_resume(void)
192535f296dSSowjanya Komatineni {
193535f296dSSowjanya Komatineni 	unsigned int i, idx;
194535f296dSSowjanya Komatineni 
195535f296dSSowjanya Komatineni 	idx = 0;
196535f296dSSowjanya Komatineni 	for (i = 0; i < periph_banks; i++, idx++)
197535f296dSSowjanya Komatineni 		writel_relaxed(periph_state_ctx[idx],
198535f296dSSowjanya Komatineni 			       clk_base + periph_regs[i].enb_reg);
199535f296dSSowjanya Komatineni 	/*
200535f296dSSowjanya Komatineni 	 * All non-boot peripherals will be in reset state on resume.
201535f296dSSowjanya Komatineni 	 * Wait for 5us of reset propagation delay before de-asserting
202535f296dSSowjanya Komatineni 	 * the peripherals based on the saved context.
203535f296dSSowjanya Komatineni 	 */
204535f296dSSowjanya Komatineni 	fence_udelay(5, clk_base);
205535f296dSSowjanya Komatineni 
206535f296dSSowjanya Komatineni 	for (i = 0; i < periph_banks; i++, idx++)
207535f296dSSowjanya Komatineni 		writel_relaxed(periph_state_ctx[idx],
208535f296dSSowjanya Komatineni 			       clk_base + periph_regs[i].rst_reg);
209535f296dSSowjanya Komatineni 
210535f296dSSowjanya Komatineni 	fence_udelay(2, clk_base);
211535f296dSSowjanya Komatineni }
212535f296dSSowjanya Komatineni 
tegra_clk_periph_ctx_init(int banks)213535f296dSSowjanya Komatineni static int tegra_clk_periph_ctx_init(int banks)
214535f296dSSowjanya Komatineni {
215535f296dSSowjanya Komatineni 	periph_state_ctx = kcalloc(2 * banks, sizeof(*periph_state_ctx),
216535f296dSSowjanya Komatineni 				   GFP_KERNEL);
217535f296dSSowjanya Komatineni 	if (!periph_state_ctx)
218535f296dSSowjanya Komatineni 		return -ENOMEM;
219535f296dSSowjanya Komatineni 
220535f296dSSowjanya Komatineni 	return 0;
221535f296dSSowjanya Komatineni }
222535f296dSSowjanya Komatineni 
tegra_clk_init(void __iomem * regs,int num,int banks)2236d5b988eSStephen Warren struct clk ** __init tegra_clk_init(void __iomem *regs, int num, int banks)
224d5ff89a8SPeter De Schrijver {
2256d5b988eSStephen Warren 	clk_base = regs;
2266d5b988eSStephen Warren 
227343a607cSPeter De Schrijver 	if (WARN_ON(banks > ARRAY_SIZE(periph_regs)))
228343a607cSPeter De Schrijver 		return NULL;
229d5ff89a8SPeter De Schrijver 
2306396bb22SKees Cook 	periph_clk_enb_refcnt = kcalloc(32 * banks,
2316396bb22SKees Cook 					sizeof(*periph_clk_enb_refcnt),
2326396bb22SKees Cook 					GFP_KERNEL);
233343a607cSPeter De Schrijver 	if (!periph_clk_enb_refcnt)
234343a607cSPeter De Schrijver 		return NULL;
235d5ff89a8SPeter De Schrijver 
236343a607cSPeter De Schrijver 	periph_banks = banks;
237343a607cSPeter De Schrijver 
2386396bb22SKees Cook 	clks = kcalloc(num, sizeof(struct clk *), GFP_KERNEL);
239fc666936SDmitry Osipenko 	if (!clks) {
240343a607cSPeter De Schrijver 		kfree(periph_clk_enb_refcnt);
241fc666936SDmitry Osipenko 		return NULL;
242fc666936SDmitry Osipenko 	}
243343a607cSPeter De Schrijver 
244343a607cSPeter De Schrijver 	clk_num = num;
245343a607cSPeter De Schrijver 
246535f296dSSowjanya Komatineni 	if (IS_ENABLED(CONFIG_PM_SLEEP)) {
247535f296dSSowjanya Komatineni 		if (tegra_clk_periph_ctx_init(banks)) {
248535f296dSSowjanya Komatineni 			kfree(periph_clk_enb_refcnt);
249535f296dSSowjanya Komatineni 			kfree(clks);
250535f296dSSowjanya Komatineni 			return NULL;
251535f296dSSowjanya Komatineni 		}
252535f296dSSowjanya Komatineni 	}
253535f296dSSowjanya Komatineni 
254343a607cSPeter De Schrijver 	return clks;
255d5ff89a8SPeter De Schrijver }
256d5ff89a8SPeter De Schrijver 
tegra_init_dup_clks(struct tegra_clk_duplicate * dup_list,struct clk * clks[],int clk_max)2578f8f484bSPrashant Gaikwad void __init tegra_init_dup_clks(struct tegra_clk_duplicate *dup_list,
2588f8f484bSPrashant Gaikwad 				struct clk *clks[], int clk_max)
2598f8f484bSPrashant Gaikwad {
2608f8f484bSPrashant Gaikwad 	struct clk *clk;
2618f8f484bSPrashant Gaikwad 
2628f8f484bSPrashant Gaikwad 	for (; dup_list->clk_id < clk_max; dup_list++) {
2638f8f484bSPrashant Gaikwad 		clk = clks[dup_list->clk_id];
2648f8f484bSPrashant Gaikwad 		dup_list->lookup.clk = clk;
2658f8f484bSPrashant Gaikwad 		clkdev_add(&dup_list->lookup);
2668f8f484bSPrashant Gaikwad 	}
2678f8f484bSPrashant Gaikwad }
2688f8f484bSPrashant Gaikwad 
tegra_init_from_table(struct tegra_clk_init_table * tbl,struct clk * clks[],int clk_max)269b1bc04a2SDmitry Osipenko void tegra_init_from_table(struct tegra_clk_init_table *tbl,
2708f8f484bSPrashant Gaikwad 			   struct clk *clks[], int clk_max)
2718f8f484bSPrashant Gaikwad {
2728f8f484bSPrashant Gaikwad 	struct clk *clk;
2738f8f484bSPrashant Gaikwad 
2748f8f484bSPrashant Gaikwad 	for (; tbl->clk_id < clk_max; tbl++) {
2758f8f484bSPrashant Gaikwad 		clk = clks[tbl->clk_id];
276b9e742c3STomeu Vizoso 		if (IS_ERR_OR_NULL(clk)) {
277b9e742c3STomeu Vizoso 			pr_err("%s: invalid entry %ld in clks array for id %d\n",
278b9e742c3STomeu Vizoso 			       __func__, PTR_ERR(clk), tbl->clk_id);
279b9e742c3STomeu Vizoso 			WARN_ON(1);
280b9e742c3STomeu Vizoso 
281b9e742c3STomeu Vizoso 			continue;
282b9e742c3STomeu Vizoso 		}
2838f8f484bSPrashant Gaikwad 
2848f8f484bSPrashant Gaikwad 		if (tbl->parent_id < clk_max) {
2858f8f484bSPrashant Gaikwad 			struct clk *parent = clks[tbl->parent_id];
2868f8f484bSPrashant Gaikwad 			if (clk_set_parent(clk, parent)) {
2878f8f484bSPrashant Gaikwad 				pr_err("%s: Failed to set parent %s of %s\n",
2888f8f484bSPrashant Gaikwad 				       __func__, __clk_get_name(parent),
2898f8f484bSPrashant Gaikwad 				       __clk_get_name(clk));
2908f8f484bSPrashant Gaikwad 				WARN_ON(1);
2918f8f484bSPrashant Gaikwad 			}
2928f8f484bSPrashant Gaikwad 		}
2938f8f484bSPrashant Gaikwad 
2948f8f484bSPrashant Gaikwad 		if (tbl->rate)
2958f8f484bSPrashant Gaikwad 			if (clk_set_rate(clk, tbl->rate)) {
2968f8f484bSPrashant Gaikwad 				pr_err("%s: Failed to set rate %lu of %s\n",
2978f8f484bSPrashant Gaikwad 				       __func__, tbl->rate,
2988f8f484bSPrashant Gaikwad 				       __clk_get_name(clk));
2998f8f484bSPrashant Gaikwad 				WARN_ON(1);
3008f8f484bSPrashant Gaikwad 			}
3018f8f484bSPrashant Gaikwad 
3028f8f484bSPrashant Gaikwad 		if (tbl->state)
3038f8f484bSPrashant Gaikwad 			if (clk_prepare_enable(clk)) {
3048f8f484bSPrashant Gaikwad 				pr_err("%s: Failed to enable %s\n", __func__,
3058f8f484bSPrashant Gaikwad 				       __clk_get_name(clk));
3068f8f484bSPrashant Gaikwad 				WARN_ON(1);
3078f8f484bSPrashant Gaikwad 			}
3088f8f484bSPrashant Gaikwad 	}
3098f8f484bSPrashant Gaikwad }
31061fd290dSPrashant Gaikwad 
3117ba256d2SPhilipp Zabel static const struct reset_control_ops rst_ops = {
3126d5b988eSStephen Warren 	.assert = tegra_clk_rst_assert,
3136d5b988eSStephen Warren 	.deassert = tegra_clk_rst_deassert,
3144236e752SMikko Perttunen 	.reset = tegra_clk_rst_reset,
3156d5b988eSStephen Warren };
3166d5b988eSStephen Warren 
3176d5b988eSStephen Warren static struct reset_controller_dev rst_ctlr = {
3186d5b988eSStephen Warren 	.ops = &rst_ops,
3196d5b988eSStephen Warren 	.owner = THIS_MODULE,
3206d5b988eSStephen Warren 	.of_reset_n_cells = 1,
3216d5b988eSStephen Warren };
3226d5b988eSStephen Warren 
tegra_add_of_provider(struct device_node * np,void * clk_src_onecell_get)3235d797111SDmitry Osipenko void __init tegra_add_of_provider(struct device_node *np,
3245d797111SDmitry Osipenko 				  void *clk_src_onecell_get)
325343a607cSPeter De Schrijver {
326343a607cSPeter De Schrijver 	int i;
327343a607cSPeter De Schrijver 
328b1bc04a2SDmitry Osipenko 	tegra_car_np = np;
329b1bc04a2SDmitry Osipenko 
330343a607cSPeter De Schrijver 	for (i = 0; i < clk_num; i++) {
331343a607cSPeter De Schrijver 		if (IS_ERR(clks[i])) {
332343a607cSPeter De Schrijver 			pr_err
333343a607cSPeter De Schrijver 			    ("Tegra clk %d: register failed with %ld\n",
334343a607cSPeter De Schrijver 			     i, PTR_ERR(clks[i]));
335343a607cSPeter De Schrijver 		}
336343a607cSPeter De Schrijver 		if (!clks[i])
337343a607cSPeter De Schrijver 			clks[i] = ERR_PTR(-EINVAL);
338343a607cSPeter De Schrijver 	}
339343a607cSPeter De Schrijver 
340343a607cSPeter De Schrijver 	clk_data.clks = clks;
341343a607cSPeter De Schrijver 	clk_data.clk_num = clk_num;
3425d797111SDmitry Osipenko 	of_clk_add_provider(np, clk_src_onecell_get, &clk_data);
3436d5b988eSStephen Warren 
3446d5b988eSStephen Warren 	rst_ctlr.of_node = np;
34566b6f3d0SMikko Perttunen 	rst_ctlr.nr_resets = periph_banks * 32 + num_special_reset;
3466d5b988eSStephen Warren 	reset_controller_register(&rst_ctlr);
347343a607cSPeter De Schrijver }
348343a607cSPeter De Schrijver 
tegra_init_special_resets(unsigned int num,int (* assert)(unsigned long),int (* deassert)(unsigned long))34966b6f3d0SMikko Perttunen void __init tegra_init_special_resets(unsigned int num,
35066b6f3d0SMikko Perttunen 				      int (*assert)(unsigned long),
35166b6f3d0SMikko Perttunen 				      int (*deassert)(unsigned long))
35266b6f3d0SMikko Perttunen {
35366b6f3d0SMikko Perttunen 	num_special_reset = num;
35466b6f3d0SMikko Perttunen 	special_reset_assert = assert;
35566b6f3d0SMikko Perttunen 	special_reset_deassert = deassert;
35666b6f3d0SMikko Perttunen }
35766b6f3d0SMikko Perttunen 
tegra_register_devclks(struct tegra_devclk * dev_clks,int num)358b1bc04a2SDmitry Osipenko void tegra_register_devclks(struct tegra_devclk *dev_clks, int num)
35973d37e4cSPeter De Schrijver {
36073d37e4cSPeter De Schrijver 	int i;
36173d37e4cSPeter De Schrijver 
36273d37e4cSPeter De Schrijver 	for (i = 0; i < num; i++, dev_clks++)
36373d37e4cSPeter De Schrijver 		clk_register_clkdev(clks[dev_clks->dt_id], dev_clks->con_id,
36473d37e4cSPeter De Schrijver 				dev_clks->dev_id);
3659f0030c8SPeter De Schrijver 
3669f0030c8SPeter De Schrijver 	for (i = 0; i < clk_num; i++) {
3679f0030c8SPeter De Schrijver 		if (!IS_ERR_OR_NULL(clks[i]))
3689f0030c8SPeter De Schrijver 			clk_register_clkdev(clks[i], __clk_get_name(clks[i]),
3699f0030c8SPeter De Schrijver 				"tegra-clk-debug");
3709f0030c8SPeter De Schrijver 	}
37173d37e4cSPeter De Schrijver }
37273d37e4cSPeter De Schrijver 
tegra_lookup_dt_id(int clk_id,struct tegra_clk * tegra_clk)373b8700d50SPeter De Schrijver struct clk ** __init tegra_lookup_dt_id(int clk_id,
374b8700d50SPeter De Schrijver 					struct tegra_clk *tegra_clk)
375b8700d50SPeter De Schrijver {
376b8700d50SPeter De Schrijver 	if (tegra_clk[clk_id].present)
377b8700d50SPeter De Schrijver 		return &clks[tegra_clk[clk_id].dt_id];
378b8700d50SPeter De Schrijver 	else
379b8700d50SPeter De Schrijver 		return NULL;
380b8700d50SPeter De Schrijver }
381b8700d50SPeter De Schrijver 
tegra_clk_get_of_node(struct clk_hw * hw)382b1bc04a2SDmitry Osipenko static struct device_node *tegra_clk_get_of_node(struct clk_hw *hw)
383b1bc04a2SDmitry Osipenko {
384b1bc04a2SDmitry Osipenko 	struct device_node *np;
385b1bc04a2SDmitry Osipenko 	char *node_name;
386b1bc04a2SDmitry Osipenko 
387*28df1500SAndy Shevchenko 	node_name = kstrdup_and_replace(hw->init->name, '_', '-', GFP_KERNEL);
388b1bc04a2SDmitry Osipenko 	if (!node_name)
389b1bc04a2SDmitry Osipenko 		return NULL;
390b1bc04a2SDmitry Osipenko 
391b1bc04a2SDmitry Osipenko 	for_each_child_of_node(tegra_car_np, np) {
392b1bc04a2SDmitry Osipenko 		if (!strcmp(np->name, node_name))
393b1bc04a2SDmitry Osipenko 			break;
394b1bc04a2SDmitry Osipenko 	}
395b1bc04a2SDmitry Osipenko 
396b1bc04a2SDmitry Osipenko 	kfree(node_name);
397b1bc04a2SDmitry Osipenko 
398b1bc04a2SDmitry Osipenko 	return np;
399b1bc04a2SDmitry Osipenko }
400b1bc04a2SDmitry Osipenko 
tegra_clk_dev_register(struct clk_hw * hw)401b1bc04a2SDmitry Osipenko struct clk *tegra_clk_dev_register(struct clk_hw *hw)
402b1bc04a2SDmitry Osipenko {
403b1bc04a2SDmitry Osipenko 	struct platform_device *pdev, *parent;
404b1bc04a2SDmitry Osipenko 	const char *dev_name = NULL;
405b1bc04a2SDmitry Osipenko 	struct device *dev = NULL;
406b1bc04a2SDmitry Osipenko 	struct device_node *np;
407b1bc04a2SDmitry Osipenko 
408b1bc04a2SDmitry Osipenko 	np = tegra_clk_get_of_node(hw);
409b1bc04a2SDmitry Osipenko 
410b1bc04a2SDmitry Osipenko 	if (!of_device_is_available(np))
411b1bc04a2SDmitry Osipenko 		goto put_node;
412b1bc04a2SDmitry Osipenko 
413b1bc04a2SDmitry Osipenko 	dev_name = kasprintf(GFP_KERNEL, "tegra_clk_%s", hw->init->name);
414b1bc04a2SDmitry Osipenko 	if (!dev_name)
415b1bc04a2SDmitry Osipenko 		goto put_node;
416b1bc04a2SDmitry Osipenko 
417b1bc04a2SDmitry Osipenko 	parent = of_find_device_by_node(tegra_car_np);
418b1bc04a2SDmitry Osipenko 	if (parent) {
419b1bc04a2SDmitry Osipenko 		pdev = of_platform_device_create(np, dev_name, &parent->dev);
420b1bc04a2SDmitry Osipenko 		put_device(&parent->dev);
421b1bc04a2SDmitry Osipenko 
422b1bc04a2SDmitry Osipenko 		if (!pdev) {
423b1bc04a2SDmitry Osipenko 			pr_err("%s: failed to create device for %pOF\n",
424b1bc04a2SDmitry Osipenko 			       __func__, np);
425b1bc04a2SDmitry Osipenko 			goto free_name;
426b1bc04a2SDmitry Osipenko 		}
427b1bc04a2SDmitry Osipenko 
428b1bc04a2SDmitry Osipenko 		dev = &pdev->dev;
429b1bc04a2SDmitry Osipenko 		pm_runtime_enable(dev);
430b1bc04a2SDmitry Osipenko 	} else {
431b1bc04a2SDmitry Osipenko 		WARN(1, "failed to find device for %pOF\n", tegra_car_np);
432b1bc04a2SDmitry Osipenko 	}
433b1bc04a2SDmitry Osipenko 
434b1bc04a2SDmitry Osipenko free_name:
435b1bc04a2SDmitry Osipenko 	kfree(dev_name);
436b1bc04a2SDmitry Osipenko put_node:
437b1bc04a2SDmitry Osipenko 	of_node_put(np);
438b1bc04a2SDmitry Osipenko 
439b1bc04a2SDmitry Osipenko 	return clk_register(dev, hw);
440b1bc04a2SDmitry Osipenko }
441b1bc04a2SDmitry Osipenko 
442441f199aSStephen Warren tegra_clk_apply_init_table_func tegra_clk_apply_init_table;
443441f199aSStephen Warren 
tegra_clocks_apply_init_table(void)444d0a57bd5SPeter De Schrijver static int __init tegra_clocks_apply_init_table(void)
445441f199aSStephen Warren {
446441f199aSStephen Warren 	if (!tegra_clk_apply_init_table)
447d0a57bd5SPeter De Schrijver 		return 0;
448441f199aSStephen Warren 
449441f199aSStephen Warren 	tegra_clk_apply_init_table();
450d0a57bd5SPeter De Schrijver 
451d0a57bd5SPeter De Schrijver 	return 0;
452441f199aSStephen Warren }
453d0a57bd5SPeter De Schrijver arch_initcall(tegra_clocks_apply_init_table);
454