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