1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * Copyright (c) 2012, NVIDIA CORPORATION. All rights reserved. |
4 | */ |
5 | |
6 | #include <linux/kernel.h> |
7 | #include <linux/io.h> |
8 | #include <linux/err.h> |
9 | #include <linux/delay.h> |
10 | #include <linux/slab.h> |
11 | #include <linux/clk-provider.h> |
12 | |
13 | #include "clk.h" |
14 | |
15 | #define pll_out_enb(p) (BIT(p->enb_bit_idx)) |
16 | #define pll_out_rst(p) (BIT(p->rst_bit_idx)) |
17 | |
18 | static int clk_pll_out_is_enabled(struct clk_hw *hw) |
19 | { |
20 | struct tegra_clk_pll_out *pll_out = to_clk_pll_out(hw); |
21 | u32 val = readl_relaxed(pll_out->reg); |
22 | int state; |
23 | |
24 | state = (val & pll_out_enb(pll_out)) ? 1 : 0; |
25 | if (!(val & (pll_out_rst(pll_out)))) |
26 | state = 0; |
27 | return state; |
28 | } |
29 | |
30 | static int clk_pll_out_enable(struct clk_hw *hw) |
31 | { |
32 | struct tegra_clk_pll_out *pll_out = to_clk_pll_out(hw); |
33 | unsigned long flags = 0; |
34 | u32 val; |
35 | |
36 | if (pll_out->lock) |
37 | spin_lock_irqsave(pll_out->lock, flags); |
38 | |
39 | val = readl_relaxed(pll_out->reg); |
40 | |
41 | val |= (pll_out_enb(pll_out) | pll_out_rst(pll_out)); |
42 | |
43 | writel_relaxed(val, pll_out->reg); |
44 | udelay(2); |
45 | |
46 | if (pll_out->lock) |
47 | spin_unlock_irqrestore(lock: pll_out->lock, flags); |
48 | |
49 | return 0; |
50 | } |
51 | |
52 | static void clk_pll_out_disable(struct clk_hw *hw) |
53 | { |
54 | struct tegra_clk_pll_out *pll_out = to_clk_pll_out(hw); |
55 | unsigned long flags = 0; |
56 | u32 val; |
57 | |
58 | if (pll_out->lock) |
59 | spin_lock_irqsave(pll_out->lock, flags); |
60 | |
61 | val = readl_relaxed(pll_out->reg); |
62 | |
63 | val &= ~(pll_out_enb(pll_out) | pll_out_rst(pll_out)); |
64 | |
65 | writel_relaxed(val, pll_out->reg); |
66 | udelay(2); |
67 | |
68 | if (pll_out->lock) |
69 | spin_unlock_irqrestore(lock: pll_out->lock, flags); |
70 | } |
71 | |
72 | static void tegra_clk_pll_out_restore_context(struct clk_hw *hw) |
73 | { |
74 | if (!__clk_get_enable_count(clk: hw->clk)) |
75 | clk_pll_out_disable(hw); |
76 | else |
77 | clk_pll_out_enable(hw); |
78 | } |
79 | |
80 | const struct clk_ops tegra_clk_pll_out_ops = { |
81 | .is_enabled = clk_pll_out_is_enabled, |
82 | .enable = clk_pll_out_enable, |
83 | .disable = clk_pll_out_disable, |
84 | .restore_context = tegra_clk_pll_out_restore_context, |
85 | }; |
86 | |
87 | struct clk *tegra_clk_register_pll_out(const char *name, |
88 | const char *parent_name, void __iomem *reg, u8 enb_bit_idx, |
89 | u8 rst_bit_idx, unsigned long flags, u8 pll_out_flags, |
90 | spinlock_t *lock) |
91 | { |
92 | struct tegra_clk_pll_out *pll_out; |
93 | struct clk *clk; |
94 | struct clk_init_data init; |
95 | |
96 | pll_out = kzalloc(size: sizeof(*pll_out), GFP_KERNEL); |
97 | if (!pll_out) |
98 | return ERR_PTR(error: -ENOMEM); |
99 | |
100 | init.name = name; |
101 | init.ops = &tegra_clk_pll_out_ops; |
102 | init.parent_names = (parent_name ? &parent_name : NULL); |
103 | init.num_parents = (parent_name ? 1 : 0); |
104 | init.flags = flags; |
105 | |
106 | pll_out->reg = reg; |
107 | pll_out->enb_bit_idx = enb_bit_idx; |
108 | pll_out->rst_bit_idx = rst_bit_idx; |
109 | pll_out->flags = pll_out_flags; |
110 | pll_out->lock = lock; |
111 | |
112 | /* Data in .init is copied by clk_register(), so stack variable OK */ |
113 | pll_out->hw.init = &init; |
114 | |
115 | clk = clk_register(NULL, hw: &pll_out->hw); |
116 | if (IS_ERR(ptr: clk)) |
117 | kfree(objp: pll_out); |
118 | |
119 | return clk; |
120 | } |
121 | |